From 489b66d0b2809ca6f569442fb88d8c96e248daeb Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 05:53:28 -0400 Subject: [PATCH 01/63] Add Ix.Kernel certified checking and set-model consistency proofs Add certified source and claim entry points whose receipts connect authenticated Ixon source to the set model. A closed logical receipt excludes the profile's false proposition under the explicit SetTheory hypothesis. Move Ix.Tc and its callers to Ix.Kernel, internalize the named specification, and import the kernel, theory, and host scaffolding from jcb/monorepo at 7b06b754. Remove the root lean4lean dependency and its replay benchmark and test targets. Preserve source identities, licenses, exact theorem audits, and the frozen adapter corpus. Include the separate Mathlib ZFSet construction: a countable increasing chain of strongly inaccessible cardinals supplies the actual Ix SetTheory interface. The model-existence theorem's full dependency audit permits only propext, Classical.choice, and Quot.sound; the cardinal hypothesis stays explicit. Direct production refinements cover universe comparison, structural expression reading, interning, and sort inference. Full checkEnvAnon refinement remains open. New compiler, circuit, and certificate VM development is deferred; existing compiler proofs receive the necessary namespace and exact hash-axiom audit updates. Run the implementation-proof audits on ordinary PRs, add the unified lake run check-kernel --with-model gate, and document the theorem boundaries and review entry points. Replace milestone labels in docs, comments, and test diagnostics with functional descriptions while retaining frozen inputs. Validation: - lake run check-kernel --with-model - lake test --wfail -- cli - ix codegen --check - bench-measures tests and benchmark CLI help - nix-instantiate --parse flake.nix --- .github/workflows/ci.yml | 27 +- .github/workflows/merge-tests.yml | 23 +- .github/workflows/set-theory-model.yml | 47 + BENCHMARKS.md | 6 +- Benchmarks/Lean4Lean.lean | 541 - Benchmarks/Lean4LeanMain.lean | 11 - Ix.lean | 2 +- Ix/Address.lean | 7 +- Ix/AuxGen/ExprUtils.lean | 2 +- Ix/AuxGen/Kernel.lean | 148 +- Ix/AuxGen/Nested.lean | 2 +- Ix/AuxGen/Recursor.lean | 10 +- Ix/BenchConstants.lean | 4 +- Ix/Certified.lean | 13 + Ix/Certified/Audit.lean | 28 + Ix/Certified/AuditAll.lean | 25 + Ix/Certified/AuditSupport.lean | 162 + Ix/Certified/Bytes.lean | 152 + Ix/Certified/ClaimAccept.lean | 164 + Ix/Certified/ClaimAudit.lean | 49 + Ix/Certified/ClaimCheck.lean | 104 + Ix/Certified/ClaimCommand.lean | 117 + Ix/Certified/ClaimInput.lean | 145 + Ix/Certified/ClaimMain.lean | 8 + Ix/Certified/ClaimMeaning.lean | 151 + Ix/Certified/ClaimSuggest.lean | 75 + Ix/Certified/Command.lean | 96 + Ix/Certified/Corpus.lean | 123 + Ix/Certified/Envelope.lean | 92 + Ix/Certified/Fixtures.lean | 141 + Ix/Certified/Ingress.lean | 315 + Ix/Certified/Ixon.lean | 225 + Ix/Certified/Main.lean | 8 + Ix/Certified/ModelHints.lean | 104 + Ix/Certified/ModeledAudit.lean | 56 + Ix/Certified/NOTICE | 22 + Ix/Certified/Reveal.lean | 155 + Ix/Certified/SourceAudit.lean | 39 + Ix/Certified/SourceExpr.lean | 274 + Ix/Certified/SourceMeaning.lean | 245 + Ix/Certified/SourceStore.lean | 318 + Ix/Certified/Store.lean | 74 + Ix/Certified/Suggest.lean | 31 + Ix/Certified/TcAudit.lean | 42 + Ix/Certified/Trees.lean | 117 + Ix/Cli/BenchCmd.lean | 42 +- Ix/Cli/CheckLeanCmd.lean | 12 +- Ix/Cli/ValidateLeanCmd.lean | 6 +- Ix/Compile/Verify/Audit/SorryFrontier.lean | 2 +- Ix/Compile/Verify/Audit/Statements.lean | 163 +- Ix/Compile/Verify/Catalog.lean | 6 +- Ix/Compile/Verify/CompileExpr.lean | 50 +- Ix/Compile/Verify/CompilePreseed.lean | 6 +- Ix/Compile/Verify/CompileUniv.lean | 4 +- Ix/Compile/Verify/IxonValue.lean | 18 +- Ix/Compile/Verify/Reference.lean | 10 +- Ix/Compile/Verify/SourceValue.lean | 8 +- Ix/Compile/Verify/Statements.lean | 10 +- Ix/Environment.lean | 2 +- Ix/IxVM/ClaimHarness.lean | 6 +- Ix/IxVM/Kernel/DefEq.lean | 4 +- Ix/IxVM/Kernel/Infer.lean | 4 +- Ix/IxVM/Kernel/Whnf.lean | 6 +- Ix/IxonUniv.lean | 22 +- Ix/Kernel.lean | 54 + Ix/{Tc => Kernel}/CanonicalCheck.lean | 8 +- Ix/Kernel/Certified.lean | 338 + Ix/Kernel/CertifiedClaims.lean | 95 + Ix/{Tc => Kernel}/Check.lean | 10 +- Ix/{Tc => Kernel}/Const.lean | 6 +- Ix/{Tc => Kernel}/DefEq.lean | 8 +- Ix/{Tc => Kernel}/Driver.lean | 20 +- Ix/{Tc => Kernel}/Egress.lean | 8 +- Ix/{Tc => Kernel}/EgressLean.lean | 6 +- Ix/{Tc => Kernel}/Env.lean | 14 +- Ix/{Tc => Kernel}/Equiv.lean | 4 +- Ix/{Tc => Kernel}/Error.lean | 6 +- Ix/{Tc => Kernel}/Expr.lean | 10 +- Ix/{Tc => Kernel}/Id.lean | 6 +- Ix/{Tc => Kernel}/Inductive.lean | 12 +- Ix/{Tc => Kernel}/Infer.lean | 12 +- Ix/{Tc => Kernel}/Ingress.lean | 12 +- Ix/{Tc => Kernel}/IngressMeta.lean | 12 +- Ix/{Tc => Kernel}/Knot.lean | 8 +- Ix/{Tc => Kernel}/Lctx.lean | 6 +- Ix/{Tc => Kernel}/Level.lean | 16 +- Ix/{Tc => Kernel}/Mode.lean | 6 +- Ix/{Tc => Kernel}/Monad.lean | 27 +- Ix/{Tc => Kernel}/ParCheck.lean | 10 +- Ix/{Tc => Kernel}/Primitive.lean | 6 +- Ix/{Tc => Kernel}/Subst.lean | 8 +- Ix/{Tc => Kernel}/Validate.lean | 16 +- Ix/{Tc => Kernel}/Verify/Audit/Basic.lean | 106 +- Ix/{Tc => Kernel}/Verify/Audit/Completed.lean | 7267 ++++---- .../Verify/Audit/Conditional.lean | 22 +- .../Verify/Audit/SorryFrontier.lean | 28 +- .../Verify/Audit/Statements.lean | 124 +- Ix/{Tc => Kernel}/Verify/Cache.lean | 18 +- .../Verify/Check/Acceptance.lean | 20 +- .../Verify/Check/BinderRoundTrip.lean | 20 +- .../Verify/Check/BlockAcceptance.lean | 28 +- .../Verify/Check/BlockCache.lean | 6 +- .../Verify/Check/BlockClassification.lean | 6 +- .../Verify/Check/BlockDefinition.lean | 18 +- .../Verify/Check/BlockExecution.lean | 6 +- .../Verify/Check/BlockIdentity.lean | 8 +- .../Verify/Check/BlockNatFixture.lean | 8 +- .../Verify/Check/BlockOracle.lean | 10 +- .../Verify/Check/BlockRouteFrame.lean | 6 +- .../Verify/Check/BlockRouting.lean | 8 +- .../Verify/Check/BlockTransaction.lean | 10 +- .../Verify/Check/BoundedPipelines.lean | 28 +- .../Verify/Check/CheckConstExecution.lean | 8 +- .../Verify/Check/CheckConstTransaction.lean | 6 +- .../Verify/Check/CheckerEvidence.lean | 14 +- .../Verify/Check/DeclarationIngress.lean | 12 +- .../Verify/Check/DeclarationValidation.lean | 10 +- .../Verify/Check/DefEqBasicPolicy.lean | 8 +- .../Verify/Check/DefEqCachePolicy.lean | 6 +- .../Verify/Check/DefEqEtaPolicy.lean | 6 +- .../Verify/Check/DefEqFinalWhnfPolicy.lean | 6 +- .../Verify/Check/DefEqLazyDeltaPolicy.lean | 6 +- .../Verify/Check/DefEqNatPolicy.lean | 6 +- .../Verify/Check/DefEqPipelinePolicy.lean | 6 +- .../Check/DefEqProjectionDeltaPolicy.lean | 6 +- .../Verify/Check/DefEqPropositionPolicy.lean | 6 +- .../Verify/Check/FullInference.lean | 10 +- .../Check/FullInferenceApplications.lean | 12 +- .../Verify/Check/FullInferenceBinders.lean | 22 +- .../Verify/Check/FullInferenceCache.lean | 14 +- .../Verify/Check/FullInferenceDispatcher.lean | 12 +- .../Verify/Check/FullInferenceKnot.lean | 22 +- .../Verify/Check/FullInferenceLeaves.lean | 20 +- .../Check/FullInferenceProjections.lean | 10 +- .../Verify/Check/InferencePolicy.lean | 14 +- .../Verify/Check/MemberEvidence.lean | 16 +- .../Verify/Check/NatAcceptance.lean | 22 +- .../Verify/Check/PositiveFuelSort.lean | 26 +- .../Verify/Check/PreTranslation.lean | 14 +- .../Check/PreTranslationCompatibility.lean | 16 +- .../Verify/Check/PreTranslationIngress.lean | 26 +- .../Verify/Check/PreTranslationOpening.lean | 10 +- .../Verify/Check/PreTranslationScopes.lean | 20 +- .../Check/ProjectionInferencePolicy.lean | 6 +- .../Verify/Check/PublicBlocks.lean | 22 +- .../Verify/Check/PublicStandalone.lean | 44 +- .../Verify/Check/QuotientAdmission.lean | 42 +- .../Verify/Check/QuotientBoundary.lean | 10 +- .../Verify/Check/QuotientBridge.lean | 50 +- .../Verify/Check/RecursiveMethodPolicy.lean | 6 +- .../Verify/Check/ResetFrame.lean | 6 +- .../Verify/Check/SafetyFrame.lean | 6 +- Ix/{Tc => Kernel}/Verify/Check/Scoped.lean | 16 +- .../Verify/Check/ScopedActiveBlock.lean | 14 +- .../Verify/Check/ScopedBoundedPipelines.lean | 22 +- .../Verify/Check/ScopedMemberEvidence.lean | 18 +- .../Verify/Check/ScopedPositiveFuelAxiom.lean | 26 +- .../Check/ScopedPositiveFuelCertificate.lean | 6 +- .../Verify/Check/ScopedStandaloneDriver.lean | 14 +- .../Verify/Check/SingletonInductive.lean | 34 +- .../Verify/Check/StandaloneDriver.lean | 12 +- .../Verify/Check/UncachedInferencePolicy.lean | 8 +- .../Check/UniverseInstantiationPolicy.lean | 6 +- .../Verify/Check/ValidationReach.lean | 8 +- .../Verify/Check/ValidatorFrame.lean | 8 +- .../Verify/Check/ValidatorSoundness.lean | 6 +- .../Verify/Check/WhnfBasicHelperPolicy.lean | 6 +- .../Verify/Check/WhnfBitVecPolicy.lean | 6 +- .../Verify/Check/WhnfDecidablePolicy.lean | 6 +- .../Verify/Check/WhnfDriverPolicy.lean | 6 +- .../Verify/Check/WhnfHelperPolicy.lean | 6 +- .../Verify/Check/WhnfIotaBasePolicy.lean | 6 +- .../Verify/Check/WhnfIotaDispatchPolicy.lean | 6 +- .../Verify/Check/WhnfIotaRecursionPolicy.lean | 6 +- .../Verify/Check/WhnfIotaScopePolicy.lean | 6 +- .../Verify/Check/WhnfIotaSynthesisPolicy.lean | 6 +- .../Verify/Check/WhnfNatArgumentPolicy.lean | 6 +- .../Verify/Check/WhnfNatPolicy.lean | 6 +- .../Verify/Check/WhnfNativePolicy.lean | 6 +- .../Verify/Check/WhnfProjectionPolicy.lean | 6 +- .../Verify/Check/WhnfReductionPolicy.lean | 6 +- Ix/Kernel/Verify/Consistency.lean | 19 + Ix/Kernel/Verify/Consistency/Audit.lean | 51 + Ix/Kernel/Verify/Consistency/Expr.lean | 88 + Ix/Kernel/Verify/Consistency/Infer.lean | 47 + Ix/Kernel/Verify/Consistency/Judgment.lean | 118 + Ix/Kernel/Verify/Consistency/Level.lean | 74 + Ix/{Tc => Kernel}/Verify/Ctx.lean | 58 +- Ix/{Tc => Kernel}/Verify/Decl.lean | 42 +- Ix/{Tc => Kernel}/Verify/DefEq.lean | 12 +- .../Verify/DefEq/AcceleratorGates.lean | 10 +- .../Verify/DefEq/ApplicationSpine.lean | 8 +- Ix/{Tc => Kernel}/Verify/DefEq/BoolTrue.lean | 8 +- .../Verify/DefEq/CacheBranches.lean | 16 +- .../Verify/DefEq/CacheShell.lean | 22 +- .../Verify/DefEq/CheapReduction.lean | 10 +- Ix/{Tc => Kernel}/Verify/DefEq/Closure.lean | 10 +- .../Verify/DefEq/DeltaClassification.lean | 10 +- .../Verify/DefEq/EqualRankCache.lean | 10 +- .../Verify/DefEq/EqualRankPrefix.lean | 8 +- .../Verify/DefEq/EqualRankReduction.lean | 8 +- .../Verify/DefEq/FinalWhnf/Application.lean | 8 +- .../Verify/DefEq/FinalWhnf/Closure.lean | 18 +- .../Verify/DefEq/FinalWhnf/Contracts.lean | 10 +- .../Verify/DefEq/FinalWhnf/EtaExpansion.lean | 12 +- .../DefEq/FinalWhnf/LetDeclaration.lean | 10 +- .../Verify/DefEq/FinalWhnf/NatBridge.lean | 20 +- .../Verify/DefEq/FinalWhnf/ProofTail.lean | 10 +- .../DefEq/FinalWhnf/StringExpansion.lean | 10 +- .../DefEq/FinalWhnf/StructuralPrefix.lean | 16 +- .../Verify/DefEq/FinalWhnf/StructureEta.lean | 14 +- .../DefEq/FinalWhnf/StructureEtaBase.lean | 12 +- .../DefEq/FinalWhnf/StructureEtaFields.lean | 8 +- .../DefEq/FinalWhnf/StructureEtaTail.lean | 8 +- .../Verify/DefEq/FinalWhnf/UnitLike.lean | 16 +- Ix/{Tc => Kernel}/Verify/DefEq/LazyDelta.lean | 8 +- .../Verify/DefEq/LazyDeltaClosure.lean | 8 +- .../Verify/DefEq/LazyDeltaIteration.lean | 8 +- .../Verify/DefEq/LoopFinish.lean | 8 +- Ix/{Tc => Kernel}/Verify/DefEq/NatOffset.lean | 20 +- .../Verify/DefEq/NatOffsetDecomposition.lean | 10 +- .../Verify/DefEq/NatReduction.lean | 8 +- .../Verify/DefEq/OneSidedDelta.lean | 8 +- .../Verify/DefEq/ProjectionDeltaActive.lean | 8 +- .../Verify/DefEq/ProjectionDeltaClosure.lean | 8 +- .../DefEq/ProjectionDeltaEqualRank.lean | 10 +- .../Verify/DefEq/ProjectionDeltaFinish.lean | 8 +- .../Verify/DefEq/ProjectionDeltaLoop.lean | 8 +- .../Verify/DefEq/ProjectionDeltaRank.lean | 8 +- .../Verify/DefEq/ProjectionDeltaStep.lean | 12 +- .../DefEq/ProjectionDeltaUnfolding.lean | 8 +- .../Verify/DefEq/ProjectionProbe.lean | 8 +- .../Verify/DefEq/ProjectionReduction.lean | 10 +- .../Verify/DefEq/ProofIrrelevance.lean | 10 +- .../Verify/DefEq/PropositionClassifier.lean | 10 +- .../Verify/DefEq/RankDispatch.lean | 8 +- .../Verify/DefEq/SameHeadSpine.lean | 12 +- .../Verify/DefEq/SpineArguments.lean | 10 +- .../Verify/DefEq/StoppedContinuation.lean | 10 +- .../DefEq/StoppedContinuationClosure.lean | 8 +- .../Verify/DefEq/StringLiteral.lean | 10 +- .../Verify/DefEq/Structural.lean | 18 +- .../Verify/DefEq/StructuralCongruence.lean | 10 +- .../Verify/Driver/BooleanAcceptance.lean | 8 +- .../Verify/Driver/Dependencies.lean | 6 +- .../Verify/Driver/Enumeration.lean | 6 +- Ix/{Tc => Kernel}/Verify/Driver/Fixtures.lean | 6 +- Ix/{Tc => Kernel}/Verify/Driver/Model.lean | 12 +- Ix/{Tc => Kernel}/Verify/Driver/Serial.lean | 16 +- .../Verify/Driver/SupportedAcceptance.lean | 36 +- .../Driver/SupportedAcceptanceFixtures.lean | 12 +- Ix/{Tc => Kernel}/Verify/Env.lean | 34 +- .../Verify/EquivalenceManager.lean | 8 +- Ix/{Tc => Kernel}/Verify/Execution.lean | 6 +- Ix/{Tc => Kernel}/Verify/Expr.lean | 8 +- Ix/{Tc => Kernel}/Verify/Frame.lean | 14 +- .../Verify/Frontier}/Pending.lean | 31 +- Ix/{Tc => Kernel}/Verify/Inductive.lean | 64 +- .../Inductive/AliasFormerAdmission.lean | 12 +- .../Inductive/AliasFormerCertificate.lean | 22 +- .../Verify/Inductive/AliasFormerFixture.lean | 16 +- .../Verify/Inductive/AliasFormerPattern.lean | 14 +- .../Inductive/AliasFormerRecursorFixture.lean | 12 +- .../Verify/Inductive/AliasRecAdmission.lean | 12 +- .../Verify/Inductive/AliasRecCertificate.lean | 20 +- .../Verify/Inductive/AliasRecFixture.lean | 16 +- .../Verify/Inductive/AliasRecPattern.lean | 14 +- .../Inductive/AliasRecRecursorFixture.lean | 12 +- .../Verify/Inductive/AliasRecSoundness.lean | 28 +- .../Inductive/AnnotatedPiAdmission.lean | 12 +- .../Inductive/AnnotatedPiCertificate.lean | 26 +- .../Verify/Inductive/AnnotatedPiFixture.lean | 16 +- .../Verify/Inductive/AnnotatedPiPattern.lean | 14 +- .../Inductive/AnnotatedPiRecursorFixture.lean | 12 +- .../Inductive/AnnotatedPiSoundness.lean | 28 +- .../Verify/Inductive/BlockCertificate.lean | 16 +- .../Inductive/BlockPatternSoundness.lean | 14 +- .../Verify/Inductive/CandidateSyntax.lean | 26 +- .../Verify/Inductive/Certificate.lean | 14 +- .../Verify/Inductive/ConcreteFixture.lean | 12 +- .../ConstructorPositivityTraversal.lean | 6 +- .../ConstructorValidationTraversal.lean | 6 +- .../Inductive/EliminationBreadthFixture.lean | 24 +- .../Inductive/EnumerationAcceptance.lean | 20 +- .../Verify/Inductive/EnumerationFixture.lean | 20 +- .../Verify/Inductive/ExactLeanSyntax.lean | 6 +- .../GeneratedRecursorAcceptance.lean | 10 +- .../GeneratedRecursorAcceptanceClosure.lean | 18 +- .../Inductive/GeneratedRecursorAdmission.lean | 16 +- .../GeneratedRecursorCheckerFixture.lean | 14 +- .../GeneratedRecursorCommitFixture.lean | 12 +- .../GeneratedRecursorComparison.lean | 6 +- .../GeneratedRecursorInitialInvariant.lean | 54 +- .../GeneratedRecursorMemberCheck.lean | 16 +- .../GeneratedRecursorMemberFixture.lean | 32 +- .../Inductive/GeneratedRecursorMetadata.lean | 8 +- .../GeneratedRecursorRuleFixture.lean | 14 +- .../Inductive/GeneratedRecursorSelection.lean | 12 +- .../Inductive/GeneratedRecursorSemantics.lean | 22 +- .../GeneratedRecursorTypeClosure.lean | 32 +- .../GeneratedRecursorTypeFixture.lean | 18 +- .../Inductive/IndexedBlockValidation.lean | 10 +- .../Inductive/IndexedCandidateOperations.lean | 12 +- .../Inductive/IndexedCandidateSyntax.lean | 40 +- .../IndexedCandidateTransaction.lean | 22 +- .../IndexedConstructorPositivity.lean | 8 +- .../IndexedConstructorValidation.lean | 74 +- .../Inductive/IndexedPositivityTransport.lean | 130 +- .../Inductive/IndexedProducerClosure.lean | 12 +- .../IndexedProductionPositivity.lean | 10 +- .../Inductive/IndexedRecursiveAcceptance.lean | 30 +- .../IndexedRecursiveCertificate.lean | 18 +- .../Inductive/IndexedRecursiveFixture.lean | 16 +- .../Inductive/IndexedRecursiveOracle.lean | 12 +- .../Inductive/IndexedRecursivePattern.lean | 16 +- .../Inductive/IndexedRecursiveSoundness.lean | 80 +- .../Verify/Inductive/IngressExecution.lean | 10 +- .../Verify/Inductive/IotaPattern.lean | 76 +- .../Inductive/MutualBlockCertificate.lean | 22 +- .../Verify/Inductive/MutualBlockFixture.lean | 14 +- .../Inductive/MutualBlockValidation.lean | 8 +- .../Verify/Inductive/MutualFamily.lean | 16 +- .../Inductive/MutualFamilyAdmission.lean | 20 +- .../Verify/Inductive/MutualRecursor.lean | 18 +- .../Inductive/MutualRecursorAdmission.lean | 110 +- .../Verify/Inductive/NestedAdmission.lean | 16 +- .../Inductive/NestedAuxiliaryExpansion.lean | 6 +- .../Inductive/NestedAuxiliaryPositivity.lean | 52 +- .../Inductive/NestedBlockCertificate.lean | 12 +- .../Inductive/NestedCandidateSyntax.lean | 60 +- .../NestedConstructorValidation.lean | 56 +- .../Inductive/NestedPositivityTransport.lean | 54 +- .../Inductive/NestedPositivityTraversal.lean | 8 +- .../Inductive/NestedRecursiveFixture.lean | 10 +- .../Inductive/NestedRecursorAdmission.lean | 12 +- .../Inductive/NestedRecursorFixture.lean | 6 +- .../Inductive/NestedRecursorPattern.lean | 14 +- .../Inductive/NestedRecursorSoundness.lean | 52 +- .../Inductive/NestedSemanticTransaction.lean | 22 +- .../Verify/Inductive/OccurrenceClosure.lean | 8 +- .../Inductive/OccurrenceValidation.lean | 20 +- .../Verify/Inductive/OneFamilyAdmission.lean | 34 +- .../Inductive/PositivityTraceAdapter.lean | 84 +- .../Verify/Inductive/PositivityTraversal.lean | 6 +- .../ProducedGenerationTransaction.lean | 26 +- .../Inductive/RecursivePiAcceptance.lean | 14 +- .../Inductive/RecursivePiAdmission.lean | 12 +- .../Inductive/RecursivePiCertificate.lean | 16 +- .../Verify/Inductive/RecursivePiFixture.lean | 12 +- .../Verify/Inductive/RecursivePiPattern.lean | 16 +- .../Inductive/RecursivePiRecursorFixture.lean | 10 +- .../Inductive/RecursivePiSoundness.lean | 64 +- .../RecursivePositivityTraversal.lean | 6 +- .../Verify/Inductive/ResultSortTelescope.lean | 8 +- .../Verify/Inductive/RuleApplication.lean | 14 +- .../Inductive/SingletonEnumeration.lean | 48 +- .../Verify/Inductive/SingletonFamily.lean | 10 +- .../Verify/Inductive/SingletonIngress.lean | 18 +- .../Verify/Inductive/SingletonOracle.lean | 8 +- .../Verify/Inductive/SingletonRecursor.lean | 14 +- .../Inductive/SpecializationIdentity.lean | 6 +- .../Inductive/StructuralCacheSemantics.lean | 6 +- Ix/{Tc => Kernel}/Verify/Infer.lean | 10 +- .../Verify/Infer/Applications.lean | 16 +- .../Verify/Infer/BinderClosing.lean | 14 +- .../Verify/Infer/BinderOpening.lean | 12 +- .../Verify/Infer/BinderScopes.lean | 12 +- .../Verify/Infer/CacheShell.lean | 6 +- .../Verify/Infer/CacheSoundness.lean | 14 +- Ix/{Tc => Kernel}/Verify/Infer/Callbacks.lean | 10 +- Ix/{Tc => Kernel}/Verify/Infer/CheapBeta.lean | 12 +- Ix/{Tc => Kernel}/Verify/Infer/Constants.lean | 18 +- .../Verify/Infer/Dispatcher.lean | 22 +- .../Verify/Infer/ForallTypes.lean | 16 +- .../Verify/Infer/FunctionTypes.lean | 12 +- .../Verify/Infer/LambdaTypes.lean | 20 +- Ix/{Tc => Kernel}/Verify/Infer/LeafCases.lean | 16 +- Ix/{Tc => Kernel}/Verify/Infer/LetScopes.lean | 8 +- Ix/{Tc => Kernel}/Verify/Infer/LetTypes.lean | 22 +- Ix/{Tc => Kernel}/Verify/Infer/Literals.lean | 32 +- .../Infer/ProjectionClassification.lean | 10 +- .../Verify/Infer/ProjectionTelescope.lean | 62 +- .../Verify/Infer/ProjectionTypes.lean | 18 +- .../Verify/Infer/ScopedLocals.lean | 12 +- Ix/{Tc => Kernel}/Verify/Infer/SortTypes.lean | 18 +- .../Verify/Infer/Substitution.lean | 8 +- .../Verify/InferDefEq/Closure.lean | 8 +- .../Verify/Ingress/AnonStructural.lean | 20 +- .../Verify/Ingress/LiteralBlobs.lean | 8 +- .../Verify/Ingress/Representation.lean | 10 +- .../Verify/Ingress/SerializedBoolean.lean | 10 +- Ix/{Tc => Kernel}/Verify/InstL.lean | 40 +- Ix/{Tc => Kernel}/Verify/InstUniv.lean | 12 +- Ix/{Tc => Kernel}/Verify/Knot.lean | 6 +- Ix/{Tc => Kernel}/Verify/Level.lean | 79 +- Ix/{Tc => Kernel}/Verify/Monad.lean | 8 +- Ix/{Tc => Kernel}/Verify/NatFixture.lean | 116 +- .../Verify/Projection/Concrete.lean | 52 +- .../Verify/Projection/ConcreteFixture.lean | 30 +- .../Verify/RecursiveMethods/CallDomains.lean | 18 +- .../Verify/RecursiveMethods/Closure.lean | 8 +- .../FiniteSupportBoundary.lean | 6 +- .../Verify/RecursiveMethods/Inference.lean | 16 +- .../Verify/RecursiveMethods/Public.lean | 22 +- .../RecursiveMethods/ScopedCallDomains.lean | 20 +- .../RecursiveMethods/ScopedInference.lean | 16 +- .../RecursiveMethods/ScopedSortInference.lean | 18 +- .../RecursiveMethods/SortInference.lean | 16 +- Ix/{Tc => Kernel}/Verify/Run.lean | 22 +- .../Verify/ScopedSuffix/ClosedContext.lean | 8 +- Ix/{Tc => Kernel}/Verify/State.lean | 18 +- Ix/{Tc => Kernel}/Verify/Statements.lean | 12 +- Ix/{Tc => Kernel}/Verify/Subst.lean | 8 +- Ix/{Tc => Kernel}/Verify/Suffix.lean | 6 +- Ix/{Tc => Kernel}/Verify/Support.lean | 6 +- Ix/{Tc => Kernel}/Verify/Totalization.lean | 10 +- Ix/{Tc => Kernel}/Verify/Trans.lean | 204 +- Ix/{Tc => Kernel}/Verify/VLCtx.lean | 16 +- Ix/{Tc => Kernel}/Verify/Whnf.lean | 76 +- .../Verify/Whnf/Beta/ArgumentAlignment.lean | 8 +- .../Verify/Whnf/Beta/ConsumptionBoundary.lean | 26 +- .../Verify/Whnf/Beta/DependentContexts.lean | 14 +- .../Verify/Whnf/Beta/InstantiationChain.lean | 10 +- .../Verify/Whnf/Beta/LambdaInstantiation.lean | 48 +- .../Verify/Whnf/Beta/LambdaPeeling.lean | 6 +- .../Verify/Whnf/Beta/LiftSubstitution.lean | 6 +- .../Verify/Whnf/Beta/Meaning.lean | 8 +- .../Verify/Whnf/Beta/PeelTrace.lean | 30 +- .../Verify/Whnf/Beta/PrefixSemantics.lean | 36 +- .../Verify/Whnf/Beta/SemanticCore.lean | 8 +- .../Whnf/Beta/SimultaneousSubstitution.lean | 6 +- .../Whnf/Beta/SingletonSubstitution.lean | 6 +- .../Verify/Whnf/Beta/Translation.lean | 12 +- Ix/{Tc => Kernel}/Verify/Whnf/Closure.lean | 8 +- .../Verify/Whnf/Delta/CacheExecution.lean | 10 +- .../Verify/Whnf/Delta/CacheSemantics.lean | 6 +- .../Verify/Whnf/Delta/ClosedTranslation.lean | 58 +- .../Verify/Whnf/Delta/Integration.lean | 8 +- .../Verify/Whnf/Delta/OptionalReduction.lean | 8 +- .../Verify/Whnf/Delta/SpineUnfolding.lean | 12 +- .../Verify/Whnf/Delta/StableCache.lean | 16 +- .../Verify/Whnf/Delta/TrustedBody.lean | 10 +- .../Verify/Whnf/Delta/UnfoldingState.lean | 8 +- .../Whnf/Delta/UniverseMonotonicity.lean | 10 +- .../Verify/Whnf/Driver/FullStep.lean | 6 +- .../Verify/Whnf/Driver/PublicReducers.lean | 12 +- .../Verify/Whnf/Iota/ApplicationRequests.lean | 8 +- .../Verify/Whnf/Iota/ArgumentBranches.lean | 18 +- .../Verify/Whnf/Iota/ArgumentExecution.lean | 18 +- .../Verify/Whnf/Iota/ConstructorDispatch.lean | 22 +- .../Whnf/Iota/ConstructorSynthesis.lean | 14 +- .../Iota/ConstructorSynthesisFallback.lean | 6 +- .../Verify/Whnf/Iota/Ingress.lean | 12 +- .../Verify/Whnf/Iota/NatLiteral.lean | 14 +- .../Verify/Whnf/Iota/NatOffset.lean | 10 +- .../Verify/Whnf/Iota/NatPatternMatching.lean | 70 +- .../Verify/Whnf/Iota/NatRecognizer.lean | 6 +- .../Verify/Whnf/Iota/NatReduction.lean | 30 +- .../Verify/Whnf/Iota/NatRuleLayout.lean | 26 +- .../Verify/Whnf/Iota/OptionalReduction.lean | 8 +- .../Verify/Whnf/Iota/RuleInstantiation.lean | 12 +- .../Verify/Whnf/Iota/RuleSuffixTransport.lean | 16 +- .../Verify/Whnf/Iota/SelectedRule.lean | 24 +- .../Verify/Whnf/Iota/StringLiteral.lean | 14 +- .../Verify/Whnf/Iota/StructEtaControl.lean | 6 +- .../Verify/Whnf/Iota/Substitution.lean | 14 +- .../Verify/Whnf/Iota/SynthesisRequests.lean | 16 +- .../Verify/Whnf/NoDelta/BaseReductions.lean | 6 +- .../Whnf/NoDelta/ProjectionApplication.lean | 8 +- .../Whnf/NoDelta/ProjectionDefinition.lean | 8 +- .../Verify/Whnf/NoDelta/Quotient.lean | 12 +- .../Whnf/NoDelta/QuotientReflection.lean | 40 +- .../Verify/Whnf/NoDelta/Reducer.lean | 8 +- .../Verify/Whnf/NoDelta/StringPrimitive.lean | 8 +- .../Verify/Whnf/Projection/NoAccelTail.lean | 6 +- .../Whnf/Projection/StringCallback.lean | 6 +- .../Whnf/Projection/StringExpansion.lean | 6 +- Ix/{Tc => Kernel}/Verify/Whnf/README.md | 2 +- .../Verify/Whnf/Runtime/LazyIngress.lean | 8 +- .../Verify/Whnf/RuntimeContracts.lean | 6 +- .../Verify/Whnf/StructEta/CallbackPrefix.lean | 12 +- .../Verify/Whnf/StructEta/Classifier.lean | 10 +- .../Whnf/StructEta/ExactMajorTelescope.lean | 6 +- .../Verify/Whnf/StructEta/Rebuild.lean | 6 +- .../Whnf/StructEta/RebuildRequests.lean | 10 +- .../Verify/Whnf/StructEta/RebuildTail.lean | 6 +- .../Whnf/StructEta/RecursionClassifier.lean | 10 +- .../Whnf/StructEta/ScopedClassifier.lean | 14 +- .../Whnf/StructEta/ScopedTelescope.lean | 24 +- .../Structural/ApplicationCongruence.lean | 14 +- .../Whnf/Structural/ApplicationRebuild.lean | 8 +- .../Whnf/Structural/ApplicationStep.lean | 6 +- .../Whnf/Structural/ApplicationTails.lean | 10 +- .../Verify/Whnf/Structural/BasicStep.lean | 6 +- .../Verify/Whnf/Structural/BetaBoundary.lean | 10 +- .../Verify/Whnf/Structural/CacheShell.lean | 12 +- .../Whnf/Structural/ProjectionStep.lean | 6 +- .../Whnf/Structural/RecursiveCallbacks.lean | 16 +- .../Verify/Whnf/Structural/Reducer.lean | 8 +- .../Verify/Whnf/Structural/StepAssembly.lean | 6 +- .../Verify/Whnf/Structural/VariableStep.lean | 8 +- .../Verify/Whnf/Structural/VerifiedStep.lean | 6 +- Ix/{Tc => Kernel}/Verify/World.lean | 12 +- Ix/{Tc => Kernel}/Whnf.lean | 24 +- Ix/Tc.lean | 54 - Ix/Theory.lean | 6 + Ix/Theory/Certificate/Build.lean | 211 + Ix/Theory/Certificate/Claims.lean | 50 + Ix/Theory/Certificate/Modeled.lean | 104 + Ix/Theory/Certificate/Ordinary.lean | 98 + Ix/Theory/Certificate/OrdinarySource.lean | 98 + Ix/Theory/Certificate/Quotient.lean | 75 + Ix/Theory/Certificate/Standard.lean | 54 + Ix/Theory/Certificate/Structure.lean | 43 + Ix/Theory/Certificate/Suggest.lean | 417 + Ix/Theory/Certified.lean | 48 + Ix/Theory/Certified/Accept.lean | 139 + Ix/Theory/Certified/Admission.lean | 245 + Ix/Theory/Certified/Basis/Equality.lean | 199 + Ix/Theory/Certified/Basis/Iff.lean | 171 + Ix/Theory/Certified/Basis/Interface.lean | 38 + Ix/Theory/Certified/Basis/Nonempty.lean | 138 + Ix/Theory/Certified/Checker.lean | 316 + Ix/Theory/Certified/ClaimComposition.lean | 245 + Ix/Theory/Certified/Claims.lean | 242 + Ix/Theory/Certified/Frontier.lean | 279 + Ix/Theory/Certified/Level.lean | 121 + Ix/Theory/Certified/LevelEq.lean | 116 + Ix/Theory/Certified/LogicalPolicy.lean | 130 + Ix/Theory/Certified/Modeled/Admission.lean | 59 + Ix/Theory/Certified/Modeled/Equation.lean | 73 + Ix/Theory/Certified/Modeled/Source.lean | 268 + Ix/Theory/Certified/Modeled/Transport.lean | 290 + Ix/Theory/Certified/Natural/Admission.lean | 63 + Ix/Theory/Certified/Natural/Checked.lean | 46 + Ix/Theory/Certified/Natural/Publish.lean | 90 + Ix/Theory/Certified/Natural/Value.lean | 120 + Ix/Theory/Certified/Operations.lean | 103 + Ix/Theory/Certified/Ordinary/Admission.lean | 113 + Ix/Theory/Certified/Ordinary/Checked.lean | 164 + Ix/Theory/Certified/Ordinary/Computation.lean | 133 + .../Certified/Ordinary/ConstructorStage.lean | 154 + .../Certified/Ordinary/Constructors.lean | 237 + Ix/Theory/Certified/Ordinary/Container.lean | 266 + Ix/Theory/Certified/Ordinary/Eliminator.lean | 264 + Ix/Theory/Certified/Ordinary/Family.lean | 139 + Ix/Theory/Certified/Ordinary/LargeElim.lean | 65 + Ix/Theory/Certified/Ordinary/Reading.lean | 309 + .../Certified/Ordinary/RecursorReading.lean | 415 + .../Certified/Ordinary/RecursorStage.lean | 163 + .../Certified/Ordinary/RecursorSyntax.lean | 155 + .../Certified/Ordinary/RecursorValue.lean | 136 + Ix/Theory/Certified/Ordinary/RuleChecks.lean | 90 + .../Certified/Ordinary/RuleEquations.lean | 115 + Ix/Theory/Certified/Ordinary/RuleReading.lean | 270 + Ix/Theory/Certified/Ordinary/Shape.lean | 212 + Ix/Theory/Certified/Policy.lean | 131 + Ix/Theory/Certified/Prelude.lean | 247 + Ix/Theory/Certified/PropWhen.lean | 330 + Ix/Theory/Certified/Quotient/Admission.lean | 57 + Ix/Theory/Certified/Quotient/Checked.lean | 179 + Ix/Theory/Certified/Quotient/Publish.lean | 195 + Ix/Theory/Certified/Quotient/Reading.lean | 184 + Ix/Theory/Certified/Quotient/Syntax.lean | 144 + Ix/Theory/Certified/Quotient/Value.lean | 185 + Ix/Theory/Certified/Signature.lean | 162 + Ix/Theory/Certified/Source.lean | 153 + Ix/Theory/Certified/Standard/Admission.lean | 61 + Ix/Theory/Certified/Standard/Checked.lean | 89 + Ix/Theory/Certified/Standard/Realization.lean | 127 + Ix/Theory/Certified/Store.lean | 69 + Ix/Theory/Certified/Structure/Admission.lean | 62 + Ix/Theory/Certified/Structure/Checked.lean | 117 + .../Certified/Structure/Computation.lean | 84 + Ix/Theory/Certified/Structure/Publish.lean | 161 + Ix/Theory/Certified/Structure/Reading.lean | 149 + Ix/Theory/Certified/Structure/Syntax.lean | 111 + Ix/Theory/Certified/Structure/Value.lean | 141 + Ix/Theory/Certified/Telescope.lean | 172 + Ix/Theory/Const.lean | 165 + Ix/Theory/Expr.lean | 284 + Ix/Theory/Inductive/Levels.lean | 124 + Ix/Theory/LICENSE | 234 + Ix/Theory/LICENSE-APACHE | 201 + Ix/Theory/LICENSE-MIT | 21 + Ix/Theory/Model/Annotated.lean | 221 + Ix/Theory/Model/Context.lean | 56 + Ix/Theory/Model/Environment.lean | 76 + Ix/Theory/Model/Extension.lean | 346 + Ix/Theory/Model/Inductive/Codes.lean | 43 + Ix/Theory/Model/Inductive/Container.lean | 192 + Ix/Theory/Model/Inductive/Recursor.lean | 218 + Ix/Theory/Model/Inductive/Telescope.lean | 585 + Ix/Theory/Model/Instantiation.lean | 96 + Ix/Theory/Model/Interpret.lean | 150 + Ix/Theory/Model/Judgment.lean | 280 + Ix/Theory/Model/PrimitiveValues.lean | 38 + Ix/Theory/Model/ReferenceMap.lean | 168 + Ix/Theory/Model/SetModel/Container.lean | 665 + Ix/Theory/Model/SetModel/Iter.lean | 107 + Ix/Theory/Model/SetModel/Ops.lean | 366 + Ix/Theory/Model/SetModel/RecGraph.lean | 270 + Ix/Theory/Model/SetModel/TaggedSum.lean | 186 + Ix/Theory/Model/SetModel/TupleTower.lean | 424 + Ix/Theory/Model/SetTheory/Core.lean | 166 + Ix/Theory/Model/SetTheory/Derive/Choice.lean | 75 + Ix/Theory/Model/SetTheory/Derive/Empty.lean | 79 + Ix/Theory/Model/SetTheory/Derive/Graphs.lean | 252 + Ix/Theory/Model/SetTheory/Derive/Lfp.lean | 162 + Ix/Theory/Model/SetTheory/Derive/LfpFam.lean | 170 + Ix/Theory/Model/SetTheory/Derive/Omega.lean | 142 + Ix/Theory/Model/SetTheory/Derive/Pair.lean | 128 + Ix/Theory/Model/SetTheory/Derive/Pt.lean | 240 + Ix/Theory/Model/SetTheory/Derive/Quot.lean | 234 + Ix/Theory/Model/SetTheory/Derive/Sep.lean | 74 + Ix/Theory/Model/SetTheory/Derive/Sigma.lean | 158 + Ix/Theory/Model/SetTheory/Derive/Univ.lean | 110 + .../Model/SetTheory/Derive/Universe.lean | 175 + Ix/Theory/Model/Signature.lean | 159 + Ix/Theory/Model/Support.lean | 126 + Ix/Theory/Model/TelescopeSemantics.lean | 81 + Ix/Theory/Model/Value.lean | 90 + Ix/Theory/Model/WellDenoted.lean | 148 + Ix/Theory/NOTICE | 51 + .../Named/ConstructorValidityFixtures.lean | 176 + .../Fixtures/ProjectionExpressibility.lean | 1307 ++ Ix/Theory/Named/Inductive.lean | 3045 ++++ Ix/Theory/Named/InductiveFixtures.lean | 2897 +++ Ix/Theory/Named/LICENSE | 201 + Ix/Theory/Named/Literals.lean | 682 + Ix/Theory/Named/LocalContext.lean | 150 + Ix/Theory/Named/Meta.lean | 120 + Ix/Theory/Named/MutualInductiveFixtures.lean | 199 + Ix/Theory/Named/NOTICE | 116 + Ix/Theory/Named/NestedInductive.lean | 534 + Ix/Theory/Named/NestedInductiveFixtures.lean | 329 + Ix/Theory/Named/Projection.lean | 3518 ++++ Ix/Theory/Named/Quot.lean | 27 + Ix/Theory/Named/Reference/Declaration.lean | 33 + Ix/Theory/Named/Reference/Environment.lean | 125 + .../Named/Reference/Environment/Basic.lean | 226 + Ix/Theory/Named/Reference/Expr.lean | 138 + Ix/Theory/Named/Reference/ForEachExprV.lean | 36 + Ix/Theory/Named/Reference/FuelConfig.lean | 41 + Ix/Theory/Named/Reference/Inductive/Add.lean | 2941 ++++ .../Reference/Inductive/EliminationTrace.lean | 581 + .../Named/Reference/Inductive/Reduce.lean | 120 + .../Reference/Inductive/ValidationTrace.lean | 1600 ++ Ix/Theory/Named/Reference/Instantiate.lean | 39 + Ix/Theory/Named/Reference/Level.lean | 412 + Ix/Theory/Named/Reference/List.lean | 11 + Ix/Theory/Named/Reference/LocalContext.lean | 27 + Ix/Theory/Named/Reference/Primitive.lean | 498 + Ix/Theory/Named/Reference/PtrEq.lean | 28 + Ix/Theory/Named/Reference/Quot.lean | 126 + Ix/Theory/Named/Reference/TypeChecker.lean | 1074 ++ Ix/Theory/Named/SingletonParity.lean | 186 + Ix/Theory/Named/Std/AxiomAudit.lean | 130 + Ix/Theory/Named/Std/Basic.lean | 269 + Ix/Theory/Named/Std/Control.lean | 8 + Ix/Theory/Named/Std/HashMap.lean | 105 + Ix/Theory/Named/Std/NodupKeys.lean | 45 + Ix/Theory/Named/Std/Ord.lean | 58 + Ix/Theory/Named/Std/PersistentHashMap.lean | 48 + Ix/Theory/Named/Std/SMap.lean | 83 + Ix/Theory/Named/Std/ToExpr.lean | 73 + Ix/Theory/Named/Std/VariableBang.lean | 22 + Ix/Theory/Named/Typing/Basic.lean | 141 + Ix/Theory/Named/Typing/Env.lean | 79 + Ix/Theory/Named/Typing/EnvLemmas.lean | 120 + .../Named/Typing/InductiveCertificate.lean | 514 + Ix/Theory/Named/Typing/InductiveLemmas.lean | 14628 ++++++++++++++++ Ix/Theory/Named/Typing/InductivePattern.lean | 761 + .../Named/Typing/InductivePatternWF.lean | 943 + Ix/Theory/Named/Typing/Injectivity.lean | 43 + Ix/Theory/Named/Typing/Lemmas.lean | 1115 ++ Ix/Theory/Named/Typing/Meta.lean | 54 + .../Named/Typing/NestedInductiveLemmas.lean | 190 + Ix/Theory/Named/Typing/Pattern.lean | 466 + Ix/Theory/Named/Typing/QuotLemmas.lean | 39 + Ix/Theory/Named/Typing/Strong.lean | 1031 ++ Ix/Theory/Named/Typing/UniqueTyping.lean | 377 + Ix/Theory/Named/VDecl.lean | 37 + Ix/Theory/Named/VEnv.lean | 214 + Ix/Theory/Named/VExpr.lean | 831 + Ix/Theory/Named/VLevel.lean | 99 + Ix/Theory/Named/Verify/Axioms.lean | 516 + Ix/Theory/Named/Verify/Environment/Basic.lean | 555 + .../Environment/ConstructorValidation.lean | 9028 ++++++++++ .../ConstructorValidityMatrix.lean | 358 + .../Named/Verify/Environment/Elimination.lean | 244 + .../EliminationFixturesCommon.lean | 65 + .../Environment/EliminationFixturesEdges.lean | 249 + .../Environment/EliminationFixturesEq.lean | 65 + .../Environment/EliminationFixturesEqNat.lean | 10 + .../Environment/EliminationFixturesNat.lean | 114 + .../Environment/EliminationFixturesOrAnd.lean | 137 + .../Environment/EliminationFixturesSmall.lean | 100 + .../Environment/IndexedVecCandidate.lean | 1686 ++ .../Environment/IndexedVecConsReplay.lean | 2863 +++ .../Environment/IndexedVecConstructors.lean | 1803 ++ .../Environment/IndexedVecOuterReplay.lean | 1757 ++ .../Environment/IndexedVecSemanticReplay.lean | 3540 ++++ .../Verify/Environment/InductiveFixtures.lean | 12189 +++++++++++++ .../Named/Verify/Environment/Lemmas.lean | 664 + .../Environment/MutualInductiveFixtures.lean | 3150 ++++ .../Verify/Environment/NestedReplay.lean | 3402 ++++ .../Environment/NestedRepresentation.lean | 718 + .../Environment/NestedTransformation.lean | 389 + .../Verify/Environment/Normalization.lean | 6953 ++++++++ .../Environment/NormalizationMatrix.lean | 749 + .../Environment/SingletonParityMatrix.lean | 471 + .../Environment/SingletonParityReplay.lean | 2820 +++ Ix/Theory/Named/Verify/Expr.lean | 1303 ++ Ix/Theory/Named/Verify/Level.lean | 3916 +++++ Ix/Theory/Named/Verify/LevelStd.lean | 549 + Ix/Theory/Named/Verify/LocalContext.lean | 337 + Ix/Theory/Named/Verify/Name.lean | 96 + Ix/Theory/Named/Verify/NameGenerator.lean | 34 + Ix/Theory/Named/Verify/NormLt.lean | 370 + Ix/Theory/Named/Verify/QSort.lean | 398 + Ix/Theory/Named/Verify/TypeChecker.lean | 236 + Ix/Theory/Named/Verify/TypeChecker/Basic.lean | 1176 ++ .../Named/Verify/TypeChecker/InferType.lean | 1082 ++ .../Named/Verify/TypeChecker/IsDefEq.lean | 1228 ++ .../Named/Verify/TypeChecker/Reduce.lean | 221 + Ix/Theory/Named/Verify/TypeChecker/WHNF.lean | 275 + .../Verify/Typing/ConditionallyTyped.lean | 150 + Ix/Theory/Named/Verify/Typing/Expr.lean | 146 + Ix/Theory/Named/Verify/Typing/Lemmas.lean | 2636 +++ Ix/Theory/Named/Verify/VLCtx.lean | 88 + Ix/Theory/PORTING.md | 11 + Ix/Theory/Quot.lean | 239 + Ix/Theory/Ref.lean | 23 + Ix/Theory/Rename.lean | 308 + Ix/Theory/Std/Basic.lean | 31 + Ix/Theory/Store.lean | 116 + Ix/Theory/VLevel.lean | 128 + Models/SetTheory/IxSetTheoryModel.lean | 6 + Models/SetTheory/IxSetTheoryModel/Audit.lean | 52 + .../SetTheory/IxSetTheoryModel/Carneiro.lean | 181 + Models/SetTheory/LICENSE-CON-LECHE | 70 + Models/SetTheory/NOTICE | 11 + Models/SetTheory/README.md | 56 + Models/SetTheory/lake-manifest.json | 123 + Models/SetTheory/lakefile.toml | 16 + Models/SetTheory/lean-toolchain | 1 + README.md | 14 + Tests/Certified/CLI.lean | 264 + Tests/Certified/Check.lean | 144 + Tests/Certified/Claims.lean | 375 + Tests/Certified/ClaimsMain.lean | 13 + Tests/Certified/FeatureCases.lean | 284 + Tests/Certified/Features.lean | 13 + Tests/Certified/Fidelity.lean | 132 + Tests/Certified/FidelityMain.lean | 13 + Tests/Certified/ImportManifest.lean | 82 + Tests/Certified/ModelSerialize.lean | 113 + Tests/Certified/Modeled.lean | 140 + Tests/Certified/ModeledAdversarial.lean | 232 + Tests/Certified/ModeledMain.lean | 11 + Tests/Certified/Ordinary.lean | 236 + Tests/Certified/Serialize.lean | 243 + Tests/Certified/Source.lean | 272 + Tests/Certified/SourceMain.lean | 13 + Tests/Certified/foundation.txt | 4243 +++++ Tests/Fixtures/Certified/README.md | 27 + Tests/Fixtures/Certified/c7-handoff.tar.gz | Bin 0 -> 3510635 bytes Tests/Ix/Catalog.lean | 4 +- Tests/Ix/CatalogDedup.lean | 5 +- Tests/Ix/Compile/LevelSpellings.lean | 2 +- Tests/Ix/ImportIxe.lean | 6 +- Tests/Ix/IxVM/Exploits.lean | 4 +- Tests/Ix/{Tc => Kernel}/AccelDiff.lean | 14 +- Tests/Ix/{Tc => Kernel}/AnonDiff.lean | 10 +- Tests/Ix/Kernel/CheckPrimeGaps.lean | 2 +- Tests/Ix/Kernel/CheckTauCetiReduction.lean | 2 +- Tests/Ix/{Tc => Kernel}/CheckTests.lean | 22 +- .../CheckerRoundtrip.lean} | 48 +- Tests/Ix/Kernel/FocusedLeanCheck.lean | 4 +- Tests/Ix/{Tc => Kernel}/InferDefEq.lean | 22 +- Tests/Ix/{Tc => Kernel}/IngressMetaTests.lean | 42 +- Tests/Ix/{Tc => Kernel}/InitScale.lean | 12 +- Tests/Ix/{Tc => Kernel}/IxonFixtures.lean | 12 +- Tests/Ix/{Tc => Kernel}/ParityEnv.lean | 4 +- Tests/Ix/{Tc => Kernel}/Pins.lean | 16 +- Tests/Ix/Kernel/PrimAddrs.lean | 10 +- Tests/Ix/{Tc => Kernel}/Substrate.lean | 10 +- Tests/Ix/{Tc => Kernel}/TutorialTc.lean | 18 +- Tests/Ix/{Tc => Kernel}/Unit.lean | 14 +- Tests/Ix/{Tc => Kernel}/WhnfTests.lean | 12 +- Tests/Ix/Lean4Lean.lean | 37 - Tests/Main.lean | 56 +- Tests/Theory.lean | 22 + Tests/Theory/Acceptance.lean | 161 + Tests/Theory/Audit/Certified.lean | 484 + Tests/Theory/Certified.lean | 76 + Tests/Theory/Checker.lean | 86 + Tests/Theory/Claims.lean | 130 + Tests/Theory/ImportManifest.lean | 176 + Tests/Theory/Modeled.lean | 165 + Tests/Theory/ModeledEquations.lean | 99 + Tests/Theory/ModeledFixtures.lean | 83 + Tests/Theory/ModeledNested.lean | 177 + Tests/Theory/ModeledPermutation.lean | 105 + Tests/Theory/NamedManifest.lean | 123 + Tests/Theory/Natural.lean | 108 + Tests/Theory/Operations.lean | 49 + Tests/Theory/Ordinary.lean | 203 + Tests/Theory/OrdinaryAcceptance.lean | 162 + Tests/Theory/Provenance.lean | 57 + Tests/Theory/Quotient.lean | 144 + Tests/Theory/RecursorGoldens.lean | 45 + Tests/Theory/Standard.lean | 132 + Tests/Theory/Structure.lean | 159 + Tests/Theory/Suggestions.lean | 55 + Tests/Theory/certified-foundation.txt | 10669 +++++++++++ crates/kernel/src/def_eq.rs | 4 +- crates/kernel/src/inductive.rs | 2 +- docs/benchmarking.md | 6 - docs/certified-checking.md | 160 + docs/ix_canonicity.md | 24 +- docs/kernel-verification.md | 144 + docs/tc-context-digest-collision-boundary.md | 2 +- docs/tc-k0-backedge-audit.md | 18 +- docs/theory.md | 68 + flake.nix | 16 - lake-manifest.json | 12 +- lakefile.lean | 210 +- 829 files changed, 167541 insertions(+), 8517 deletions(-) create mode 100644 .github/workflows/set-theory-model.yml delete mode 100644 Benchmarks/Lean4Lean.lean delete mode 100644 Benchmarks/Lean4LeanMain.lean create mode 100644 Ix/Certified.lean create mode 100644 Ix/Certified/Audit.lean create mode 100644 Ix/Certified/AuditAll.lean create mode 100644 Ix/Certified/AuditSupport.lean create mode 100644 Ix/Certified/Bytes.lean create mode 100644 Ix/Certified/ClaimAccept.lean create mode 100644 Ix/Certified/ClaimAudit.lean create mode 100644 Ix/Certified/ClaimCheck.lean create mode 100644 Ix/Certified/ClaimCommand.lean create mode 100644 Ix/Certified/ClaimInput.lean create mode 100644 Ix/Certified/ClaimMain.lean create mode 100644 Ix/Certified/ClaimMeaning.lean create mode 100644 Ix/Certified/ClaimSuggest.lean create mode 100644 Ix/Certified/Command.lean create mode 100644 Ix/Certified/Corpus.lean create mode 100644 Ix/Certified/Envelope.lean create mode 100644 Ix/Certified/Fixtures.lean create mode 100644 Ix/Certified/Ingress.lean create mode 100644 Ix/Certified/Ixon.lean create mode 100644 Ix/Certified/Main.lean create mode 100644 Ix/Certified/ModelHints.lean create mode 100644 Ix/Certified/ModeledAudit.lean create mode 100644 Ix/Certified/NOTICE create mode 100644 Ix/Certified/Reveal.lean create mode 100644 Ix/Certified/SourceAudit.lean create mode 100644 Ix/Certified/SourceExpr.lean create mode 100644 Ix/Certified/SourceMeaning.lean create mode 100644 Ix/Certified/SourceStore.lean create mode 100644 Ix/Certified/Store.lean create mode 100644 Ix/Certified/Suggest.lean create mode 100644 Ix/Certified/TcAudit.lean create mode 100644 Ix/Certified/Trees.lean create mode 100644 Ix/Kernel.lean rename Ix/{Tc => Kernel}/CanonicalCheck.lean (99%) create mode 100644 Ix/Kernel/Certified.lean create mode 100644 Ix/Kernel/CertifiedClaims.lean rename Ix/{Tc => Kernel}/Check.lean (99%) rename Ix/{Tc => Kernel}/Const.lean (98%) rename Ix/{Tc => Kernel}/DefEq.lean (99%) rename Ix/{Tc => Kernel}/Driver.lean (97%) rename Ix/{Tc => Kernel}/Egress.lean (99%) rename Ix/{Tc => Kernel}/EgressLean.lean (99%) rename Ix/{Tc => Kernel}/Env.lean (98%) rename Ix/{Tc => Kernel}/Equiv.lean (99%) rename Ix/{Tc => Kernel}/Error.lean (98%) rename Ix/{Tc => Kernel}/Expr.lean (99%) rename Ix/{Tc => Kernel}/Id.lean (97%) rename Ix/{Tc => Kernel}/Inductive.lean (99%) rename Ix/{Tc => Kernel}/Infer.lean (97%) rename Ix/{Tc => Kernel}/Ingress.lean (99%) rename Ix/{Tc => Kernel}/IngressMeta.lean (99%) rename Ix/{Tc => Kernel}/Knot.lean (94%) rename Ix/{Tc => Kernel}/Lctx.lean (98%) rename Ix/{Tc => Kernel}/Level.lean (97%) rename Ix/{Tc => Kernel}/Mode.lean (98%) rename Ix/{Tc => Kernel}/Monad.lean (98%) rename Ix/{Tc => Kernel}/ParCheck.lean (98%) rename Ix/{Tc => Kernel}/Primitive.lean (99%) rename Ix/{Tc => Kernel}/Subst.lean (99%) rename Ix/{Tc => Kernel}/Validate.lean (98%) rename Ix/{Tc => Kernel}/Verify/Audit/Basic.lean (64%) rename Ix/{Tc => Kernel}/Verify/Audit/Completed.lean (59%) rename Ix/{Tc => Kernel}/Verify/Audit/Conditional.lean (51%) rename Ix/{Tc => Kernel}/Verify/Audit/SorryFrontier.lean (66%) rename Ix/{Tc => Kernel}/Verify/Audit/Statements.lean (50%) rename Ix/{Tc => Kernel}/Verify/Cache.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/Acceptance.lean (96%) rename Ix/{Tc => Kernel}/Verify/Check/BinderRoundTrip.lean (95%) rename Ix/{Tc => Kernel}/Verify/Check/BlockAcceptance.lean (96%) rename Ix/{Tc => Kernel}/Verify/Check/BlockCache.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/BlockClassification.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/BlockDefinition.lean (94%) rename Ix/{Tc => Kernel}/Verify/Check/BlockExecution.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/BlockIdentity.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/BlockNatFixture.lean (97%) rename Ix/{Tc => Kernel}/Verify/Check/BlockOracle.lean (95%) rename Ix/{Tc => Kernel}/Verify/Check/BlockRouteFrame.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/BlockRouting.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/BlockTransaction.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/BoundedPipelines.lean (96%) rename Ix/{Tc => Kernel}/Verify/Check/CheckConstExecution.lean (95%) rename Ix/{Tc => Kernel}/Verify/Check/CheckConstTransaction.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/CheckerEvidence.lean (95%) rename Ix/{Tc => Kernel}/Verify/Check/DeclarationIngress.lean (95%) rename Ix/{Tc => Kernel}/Verify/Check/DeclarationValidation.lean (96%) rename Ix/{Tc => Kernel}/Verify/Check/DefEqBasicPolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/DefEqCachePolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/DefEqEtaPolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/DefEqFinalWhnfPolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/DefEqLazyDeltaPolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/DefEqNatPolicy.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/DefEqPipelinePolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/DefEqProjectionDeltaPolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/DefEqPropositionPolicy.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/FullInference.lean (95%) rename Ix/{Tc => Kernel}/Verify/Check/FullInferenceApplications.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/FullInferenceBinders.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/FullInferenceCache.lean (96%) rename Ix/{Tc => Kernel}/Verify/Check/FullInferenceDispatcher.lean (96%) rename Ix/{Tc => Kernel}/Verify/Check/FullInferenceKnot.lean (94%) rename Ix/{Tc => Kernel}/Verify/Check/FullInferenceLeaves.lean (95%) rename Ix/{Tc => Kernel}/Verify/Check/FullInferenceProjections.lean (96%) rename Ix/{Tc => Kernel}/Verify/Check/InferencePolicy.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/MemberEvidence.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/NatAcceptance.lean (97%) rename Ix/{Tc => Kernel}/Verify/Check/PositiveFuelSort.lean (94%) rename Ix/{Tc => Kernel}/Verify/Check/PreTranslation.lean (96%) rename Ix/{Tc => Kernel}/Verify/Check/PreTranslationCompatibility.lean (95%) rename Ix/{Tc => Kernel}/Verify/Check/PreTranslationIngress.lean (97%) rename Ix/{Tc => Kernel}/Verify/Check/PreTranslationOpening.lean (97%) rename Ix/{Tc => Kernel}/Verify/Check/PreTranslationScopes.lean (97%) rename Ix/{Tc => Kernel}/Verify/Check/ProjectionInferencePolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/PublicBlocks.lean (80%) rename Ix/{Tc => Kernel}/Verify/Check/PublicStandalone.lean (91%) rename Ix/{Tc => Kernel}/Verify/Check/QuotientAdmission.lean (88%) rename Ix/{Tc => Kernel}/Verify/Check/QuotientBoundary.lean (95%) rename Ix/{Tc => Kernel}/Verify/Check/QuotientBridge.lean (94%) rename Ix/{Tc => Kernel}/Verify/Check/RecursiveMethodPolicy.lean (97%) rename Ix/{Tc => Kernel}/Verify/Check/ResetFrame.lean (97%) rename Ix/{Tc => Kernel}/Verify/Check/SafetyFrame.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/Scoped.lean (89%) rename Ix/{Tc => Kernel}/Verify/Check/ScopedActiveBlock.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/ScopedBoundedPipelines.lean (96%) rename Ix/{Tc => Kernel}/Verify/Check/ScopedMemberEvidence.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/ScopedPositiveFuelAxiom.lean (96%) rename Ix/{Tc => Kernel}/Verify/Check/ScopedPositiveFuelCertificate.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/ScopedStandaloneDriver.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/SingletonInductive.lean (92%) rename Ix/{Tc => Kernel}/Verify/Check/StandaloneDriver.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/UncachedInferencePolicy.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/UniverseInstantiationPolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/ValidationReach.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/ValidatorFrame.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/ValidatorSoundness.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/WhnfBasicHelperPolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/WhnfBitVecPolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/WhnfDecidablePolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/WhnfDriverPolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/WhnfHelperPolicy.lean (95%) rename Ix/{Tc => Kernel}/Verify/Check/WhnfIotaBasePolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/WhnfIotaDispatchPolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/WhnfIotaRecursionPolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/WhnfIotaScopePolicy.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/WhnfIotaSynthesisPolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/WhnfNatArgumentPolicy.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/WhnfNatPolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/WhnfNativePolicy.lean (98%) rename Ix/{Tc => Kernel}/Verify/Check/WhnfProjectionPolicy.lean (99%) rename Ix/{Tc => Kernel}/Verify/Check/WhnfReductionPolicy.lean (99%) create mode 100644 Ix/Kernel/Verify/Consistency.lean create mode 100644 Ix/Kernel/Verify/Consistency/Audit.lean create mode 100644 Ix/Kernel/Verify/Consistency/Expr.lean create mode 100644 Ix/Kernel/Verify/Consistency/Infer.lean create mode 100644 Ix/Kernel/Verify/Consistency/Judgment.lean create mode 100644 Ix/Kernel/Verify/Consistency/Level.lean rename Ix/{Tc => Kernel}/Verify/Ctx.lean (96%) rename Ix/{Tc => Kernel}/Verify/Decl.lean (95%) rename Ix/{Tc => Kernel}/Verify/DefEq.lean (99%) rename Ix/{Tc => Kernel}/Verify/DefEq/AcceleratorGates.lean (97%) rename Ix/{Tc => Kernel}/Verify/DefEq/ApplicationSpine.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/BoolTrue.lean (99%) rename Ix/{Tc => Kernel}/Verify/DefEq/CacheBranches.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/CacheShell.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/CheapReduction.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/Closure.lean (97%) rename Ix/{Tc => Kernel}/Verify/DefEq/DeltaClassification.lean (96%) rename Ix/{Tc => Kernel}/Verify/DefEq/EqualRankCache.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/EqualRankPrefix.lean (97%) rename Ix/{Tc => Kernel}/Verify/DefEq/EqualRankReduction.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/FinalWhnf/Application.lean (97%) rename Ix/{Tc => Kernel}/Verify/DefEq/FinalWhnf/Closure.lean (91%) rename Ix/{Tc => Kernel}/Verify/DefEq/FinalWhnf/Contracts.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/FinalWhnf/EtaExpansion.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/FinalWhnf/LetDeclaration.lean (99%) rename Ix/{Tc => Kernel}/Verify/DefEq/FinalWhnf/NatBridge.lean (97%) rename Ix/{Tc => Kernel}/Verify/DefEq/FinalWhnf/ProofTail.lean (97%) rename Ix/{Tc => Kernel}/Verify/DefEq/FinalWhnf/StringExpansion.lean (97%) rename Ix/{Tc => Kernel}/Verify/DefEq/FinalWhnf/StructuralPrefix.lean (95%) rename Ix/{Tc => Kernel}/Verify/DefEq/FinalWhnf/StructureEta.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/FinalWhnf/StructureEtaBase.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/FinalWhnf/StructureEtaFields.lean (97%) rename Ix/{Tc => Kernel}/Verify/DefEq/FinalWhnf/StructureEtaTail.lean (97%) rename Ix/{Tc => Kernel}/Verify/DefEq/FinalWhnf/UnitLike.lean (96%) rename Ix/{Tc => Kernel}/Verify/DefEq/LazyDelta.lean (99%) rename Ix/{Tc => Kernel}/Verify/DefEq/LazyDeltaClosure.lean (93%) rename Ix/{Tc => Kernel}/Verify/DefEq/LazyDeltaIteration.lean (97%) rename Ix/{Tc => Kernel}/Verify/DefEq/LoopFinish.lean (95%) rename Ix/{Tc => Kernel}/Verify/DefEq/NatOffset.lean (96%) rename Ix/{Tc => Kernel}/Verify/DefEq/NatOffsetDecomposition.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/NatReduction.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/OneSidedDelta.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/ProjectionDeltaActive.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/ProjectionDeltaClosure.lean (97%) rename Ix/{Tc => Kernel}/Verify/DefEq/ProjectionDeltaEqualRank.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/ProjectionDeltaFinish.lean (96%) rename Ix/{Tc => Kernel}/Verify/DefEq/ProjectionDeltaLoop.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/ProjectionDeltaRank.lean (95%) rename Ix/{Tc => Kernel}/Verify/DefEq/ProjectionDeltaStep.lean (96%) rename Ix/{Tc => Kernel}/Verify/DefEq/ProjectionDeltaUnfolding.lean (97%) rename Ix/{Tc => Kernel}/Verify/DefEq/ProjectionProbe.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/ProjectionReduction.lean (96%) rename Ix/{Tc => Kernel}/Verify/DefEq/ProofIrrelevance.lean (97%) rename Ix/{Tc => Kernel}/Verify/DefEq/PropositionClassifier.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/RankDispatch.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/SameHeadSpine.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/SpineArguments.lean (97%) rename Ix/{Tc => Kernel}/Verify/DefEq/StoppedContinuation.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/StoppedContinuationClosure.lean (94%) rename Ix/{Tc => Kernel}/Verify/DefEq/StringLiteral.lean (98%) rename Ix/{Tc => Kernel}/Verify/DefEq/Structural.lean (97%) rename Ix/{Tc => Kernel}/Verify/DefEq/StructuralCongruence.lean (97%) rename Ix/{Tc => Kernel}/Verify/Driver/BooleanAcceptance.lean (99%) rename Ix/{Tc => Kernel}/Verify/Driver/Dependencies.lean (99%) rename Ix/{Tc => Kernel}/Verify/Driver/Enumeration.lean (99%) rename Ix/{Tc => Kernel}/Verify/Driver/Fixtures.lean (98%) rename Ix/{Tc => Kernel}/Verify/Driver/Model.lean (97%) rename Ix/{Tc => Kernel}/Verify/Driver/Serial.lean (95%) rename Ix/{Tc => Kernel}/Verify/Driver/SupportedAcceptance.lean (96%) rename Ix/{Tc => Kernel}/Verify/Driver/SupportedAcceptanceFixtures.lean (95%) rename Ix/{Tc => Kernel}/Verify/Env.lean (98%) rename Ix/{Tc => Kernel}/Verify/EquivalenceManager.lean (99%) rename Ix/{Tc => Kernel}/Verify/Execution.lean (99%) rename Ix/{Tc => Kernel}/Verify/Expr.lean (99%) rename Ix/{Tc => Kernel}/Verify/Frame.lean (98%) rename Ix/{Tc/Verify/Upstream => Kernel/Verify/Frontier}/Pending.lean (69%) rename Ix/{Tc => Kernel}/Verify/Inductive.lean (94%) rename Ix/{Tc => Kernel}/Verify/Inductive/AliasFormerAdmission.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/AliasFormerCertificate.lean (91%) rename Ix/{Tc => Kernel}/Verify/Inductive/AliasFormerFixture.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/AliasFormerPattern.lean (90%) rename Ix/{Tc => Kernel}/Verify/Inductive/AliasFormerRecursorFixture.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/AliasRecAdmission.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/AliasRecCertificate.lean (91%) rename Ix/{Tc => Kernel}/Verify/Inductive/AliasRecFixture.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/AliasRecPattern.lean (96%) rename Ix/{Tc => Kernel}/Verify/Inductive/AliasRecRecursorFixture.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/AliasRecSoundness.lean (95%) rename Ix/{Tc => Kernel}/Verify/Inductive/AnnotatedPiAdmission.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/AnnotatedPiCertificate.lean (91%) rename Ix/{Tc => Kernel}/Verify/Inductive/AnnotatedPiFixture.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/AnnotatedPiPattern.lean (96%) rename Ix/{Tc => Kernel}/Verify/Inductive/AnnotatedPiRecursorFixture.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/AnnotatedPiSoundness.lean (95%) rename Ix/{Tc => Kernel}/Verify/Inductive/BlockCertificate.lean (91%) rename Ix/{Tc => Kernel}/Verify/Inductive/BlockPatternSoundness.lean (90%) rename Ix/{Tc => Kernel}/Verify/Inductive/CandidateSyntax.lean (95%) rename Ix/{Tc => Kernel}/Verify/Inductive/Certificate.lean (94%) rename Ix/{Tc => Kernel}/Verify/Inductive/ConcreteFixture.lean (97%) rename Ix/{Tc => Kernel}/Verify/Inductive/ConstructorPositivityTraversal.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/ConstructorValidationTraversal.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/EliminationBreadthFixture.lean (97%) rename Ix/{Tc => Kernel}/Verify/Inductive/EnumerationAcceptance.lean (97%) rename Ix/{Tc => Kernel}/Verify/Inductive/EnumerationFixture.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/ExactLeanSyntax.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/GeneratedRecursorAcceptance.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/GeneratedRecursorAcceptanceClosure.lean (97%) rename Ix/{Tc => Kernel}/Verify/Inductive/GeneratedRecursorAdmission.lean (95%) rename Ix/{Tc => Kernel}/Verify/Inductive/GeneratedRecursorCheckerFixture.lean (97%) rename Ix/{Tc => Kernel}/Verify/Inductive/GeneratedRecursorCommitFixture.lean (97%) rename Ix/{Tc => Kernel}/Verify/Inductive/GeneratedRecursorComparison.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/GeneratedRecursorInitialInvariant.lean (97%) rename Ix/{Tc => Kernel}/Verify/Inductive/GeneratedRecursorMemberCheck.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/GeneratedRecursorMemberFixture.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/GeneratedRecursorMetadata.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/GeneratedRecursorRuleFixture.lean (94%) rename Ix/{Tc => Kernel}/Verify/Inductive/GeneratedRecursorSelection.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/GeneratedRecursorSemantics.lean (94%) rename Ix/{Tc => Kernel}/Verify/Inductive/GeneratedRecursorTypeClosure.lean (96%) rename Ix/{Tc => Kernel}/Verify/Inductive/GeneratedRecursorTypeFixture.lean (92%) rename Ix/{Tc => Kernel}/Verify/Inductive/IndexedBlockValidation.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/IndexedCandidateOperations.lean (97%) rename Ix/{Tc => Kernel}/Verify/Inductive/IndexedCandidateSyntax.lean (81%) rename Ix/{Tc => Kernel}/Verify/Inductive/IndexedCandidateTransaction.lean (78%) rename Ix/{Tc => Kernel}/Verify/Inductive/IndexedConstructorPositivity.lean (91%) rename Ix/{Tc => Kernel}/Verify/Inductive/IndexedConstructorValidation.lean (85%) rename Ix/{Tc => Kernel}/Verify/Inductive/IndexedPositivityTransport.lean (90%) rename Ix/{Tc => Kernel}/Verify/Inductive/IndexedProducerClosure.lean (84%) rename Ix/{Tc => Kernel}/Verify/Inductive/IndexedProductionPositivity.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/IndexedRecursiveAcceptance.lean (96%) rename Ix/{Tc => Kernel}/Verify/Inductive/IndexedRecursiveCertificate.lean (91%) rename Ix/{Tc => Kernel}/Verify/Inductive/IndexedRecursiveFixture.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/IndexedRecursiveOracle.lean (93%) rename Ix/{Tc => Kernel}/Verify/Inductive/IndexedRecursivePattern.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/IndexedRecursiveSoundness.lean (96%) rename Ix/{Tc => Kernel}/Verify/Inductive/IngressExecution.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/IotaPattern.lean (79%) rename Ix/{Tc => Kernel}/Verify/Inductive/MutualBlockCertificate.lean (85%) rename Ix/{Tc => Kernel}/Verify/Inductive/MutualBlockFixture.lean (97%) rename Ix/{Tc => Kernel}/Verify/Inductive/MutualBlockValidation.lean (97%) rename Ix/{Tc => Kernel}/Verify/Inductive/MutualFamily.lean (94%) rename Ix/{Tc => Kernel}/Verify/Inductive/MutualFamilyAdmission.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/MutualRecursor.lean (94%) rename Ix/{Tc => Kernel}/Verify/Inductive/MutualRecursorAdmission.lean (92%) rename Ix/{Tc => Kernel}/Verify/Inductive/NestedAdmission.lean (96%) rename Ix/{Tc => Kernel}/Verify/Inductive/NestedAuxiliaryExpansion.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/NestedAuxiliaryPositivity.lean (93%) rename Ix/{Tc => Kernel}/Verify/Inductive/NestedBlockCertificate.lean (95%) rename Ix/{Tc => Kernel}/Verify/Inductive/NestedCandidateSyntax.lean (85%) rename Ix/{Tc => Kernel}/Verify/Inductive/NestedConstructorValidation.lean (78%) rename Ix/{Tc => Kernel}/Verify/Inductive/NestedPositivityTransport.lean (84%) rename Ix/{Tc => Kernel}/Verify/Inductive/NestedPositivityTraversal.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/NestedRecursiveFixture.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/NestedRecursorAdmission.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/NestedRecursorFixture.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/NestedRecursorPattern.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/NestedRecursorSoundness.lean (95%) rename Ix/{Tc => Kernel}/Verify/Inductive/NestedSemanticTransaction.lean (97%) rename Ix/{Tc => Kernel}/Verify/Inductive/OccurrenceClosure.lean (97%) rename Ix/{Tc => Kernel}/Verify/Inductive/OccurrenceValidation.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/OneFamilyAdmission.lean (90%) rename Ix/{Tc => Kernel}/Verify/Inductive/PositivityTraceAdapter.lean (85%) rename Ix/{Tc => Kernel}/Verify/Inductive/PositivityTraversal.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/ProducedGenerationTransaction.lean (91%) rename Ix/{Tc => Kernel}/Verify/Inductive/RecursivePiAcceptance.lean (95%) rename Ix/{Tc => Kernel}/Verify/Inductive/RecursivePiAdmission.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/RecursivePiCertificate.lean (89%) rename Ix/{Tc => Kernel}/Verify/Inductive/RecursivePiFixture.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/RecursivePiPattern.lean (96%) rename Ix/{Tc => Kernel}/Verify/Inductive/RecursivePiRecursorFixture.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/RecursivePiSoundness.lean (94%) rename Ix/{Tc => Kernel}/Verify/Inductive/RecursivePositivityTraversal.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/ResultSortTelescope.lean (97%) rename Ix/{Tc => Kernel}/Verify/Inductive/RuleApplication.lean (98%) rename Ix/{Tc => Kernel}/Verify/Inductive/SingletonEnumeration.lean (96%) rename Ix/{Tc => Kernel}/Verify/Inductive/SingletonFamily.lean (99%) rename Ix/{Tc => Kernel}/Verify/Inductive/SingletonIngress.lean (97%) rename Ix/{Tc => Kernel}/Verify/Inductive/SingletonOracle.lean (96%) rename Ix/{Tc => Kernel}/Verify/Inductive/SingletonRecursor.lean (97%) rename Ix/{Tc => Kernel}/Verify/Inductive/SpecializationIdentity.lean (97%) rename Ix/{Tc => Kernel}/Verify/Inductive/StructuralCacheSemantics.lean (99%) rename Ix/{Tc => Kernel}/Verify/Infer.lean (99%) rename Ix/{Tc => Kernel}/Verify/Infer/Applications.lean (97%) rename Ix/{Tc => Kernel}/Verify/Infer/BinderClosing.lean (98%) rename Ix/{Tc => Kernel}/Verify/Infer/BinderOpening.lean (98%) rename Ix/{Tc => Kernel}/Verify/Infer/BinderScopes.lean (98%) rename Ix/{Tc => Kernel}/Verify/Infer/CacheShell.lean (99%) rename Ix/{Tc => Kernel}/Verify/Infer/CacheSoundness.lean (97%) rename Ix/{Tc => Kernel}/Verify/Infer/Callbacks.lean (92%) rename Ix/{Tc => Kernel}/Verify/Infer/CheapBeta.lean (98%) rename Ix/{Tc => Kernel}/Verify/Infer/Constants.lean (96%) rename Ix/{Tc => Kernel}/Verify/Infer/Dispatcher.lean (94%) rename Ix/{Tc => Kernel}/Verify/Infer/ForallTypes.lean (95%) rename Ix/{Tc => Kernel}/Verify/Infer/FunctionTypes.lean (94%) rename Ix/{Tc => Kernel}/Verify/Infer/LambdaTypes.lean (95%) rename Ix/{Tc => Kernel}/Verify/Infer/LeafCases.lean (96%) rename Ix/{Tc => Kernel}/Verify/Infer/LetScopes.lean (99%) rename Ix/{Tc => Kernel}/Verify/Infer/LetTypes.lean (95%) rename Ix/{Tc => Kernel}/Verify/Infer/Literals.lean (88%) rename Ix/{Tc => Kernel}/Verify/Infer/ProjectionClassification.lean (97%) rename Ix/{Tc => Kernel}/Verify/Infer/ProjectionTelescope.lean (94%) rename Ix/{Tc => Kernel}/Verify/Infer/ProjectionTypes.lean (97%) rename Ix/{Tc => Kernel}/Verify/Infer/ScopedLocals.lean (97%) rename Ix/{Tc => Kernel}/Verify/Infer/SortTypes.lean (92%) rename Ix/{Tc => Kernel}/Verify/Infer/Substitution.lean (93%) rename Ix/{Tc => Kernel}/Verify/InferDefEq/Closure.lean (96%) rename Ix/{Tc => Kernel}/Verify/Ingress/AnonStructural.lean (94%) rename Ix/{Tc => Kernel}/Verify/Ingress/LiteralBlobs.lean (99%) rename Ix/{Tc => Kernel}/Verify/Ingress/Representation.lean (98%) rename Ix/{Tc => Kernel}/Verify/Ingress/SerializedBoolean.lean (99%) rename Ix/{Tc => Kernel}/Verify/InstL.lean (95%) rename Ix/{Tc => Kernel}/Verify/InstUniv.lean (99%) rename Ix/{Tc => Kernel}/Verify/Knot.lean (99%) rename Ix/{Tc => Kernel}/Verify/Level.lean (98%) rename Ix/{Tc => Kernel}/Verify/Monad.lean (98%) rename Ix/{Tc => Kernel}/Verify/NatFixture.lean (98%) rename Ix/{Tc => Kernel}/Verify/Projection/Concrete.lean (75%) rename Ix/{Tc => Kernel}/Verify/Projection/ConcreteFixture.lean (80%) rename Ix/{Tc => Kernel}/Verify/RecursiveMethods/CallDomains.lean (98%) rename Ix/{Tc => Kernel}/Verify/RecursiveMethods/Closure.lean (96%) rename Ix/{Tc => Kernel}/Verify/RecursiveMethods/FiniteSupportBoundary.lean (98%) rename Ix/{Tc => Kernel}/Verify/RecursiveMethods/Inference.lean (96%) rename Ix/{Tc => Kernel}/Verify/RecursiveMethods/Public.lean (94%) rename Ix/{Tc => Kernel}/Verify/RecursiveMethods/ScopedCallDomains.lean (96%) rename Ix/{Tc => Kernel}/Verify/RecursiveMethods/ScopedInference.lean (98%) rename Ix/{Tc => Kernel}/Verify/RecursiveMethods/ScopedSortInference.lean (96%) rename Ix/{Tc => Kernel}/Verify/RecursiveMethods/SortInference.lean (96%) rename Ix/{Tc => Kernel}/Verify/Run.lean (97%) rename Ix/{Tc => Kernel}/Verify/ScopedSuffix/ClosedContext.lean (98%) rename Ix/{Tc => Kernel}/Verify/State.lean (98%) rename Ix/{Tc => Kernel}/Verify/Statements.lean (89%) rename Ix/{Tc => Kernel}/Verify/Subst.lean (99%) rename Ix/{Tc => Kernel}/Verify/Suffix.lean (99%) rename Ix/{Tc => Kernel}/Verify/Support.lean (99%) rename Ix/{Tc => Kernel}/Verify/Totalization.lean (99%) rename Ix/{Tc => Kernel}/Verify/Trans.lean (94%) rename Ix/{Tc => Kernel}/Verify/VLCtx.lean (93%) rename Ix/{Tc => Kernel}/Verify/Whnf.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/Beta/ArgumentAlignment.lean (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/Beta/ConsumptionBoundary.lean (87%) rename Ix/{Tc => Kernel}/Verify/Whnf/Beta/DependentContexts.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Beta/InstantiationChain.lean (95%) rename Ix/{Tc => Kernel}/Verify/Whnf/Beta/LambdaInstantiation.lean (83%) rename Ix/{Tc => Kernel}/Verify/Whnf/Beta/LambdaPeeling.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Beta/LiftSubstitution.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/Beta/Meaning.lean (94%) rename Ix/{Tc => Kernel}/Verify/Whnf/Beta/PeelTrace.lean (81%) rename Ix/{Tc => Kernel}/Verify/Whnf/Beta/PrefixSemantics.lean (95%) rename Ix/{Tc => Kernel}/Verify/Whnf/Beta/SemanticCore.lean (95%) rename Ix/{Tc => Kernel}/Verify/Whnf/Beta/SimultaneousSubstitution.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/Beta/SingletonSubstitution.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Beta/Translation.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Closure.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Delta/CacheExecution.lean (96%) rename Ix/{Tc => Kernel}/Verify/Whnf/Delta/CacheSemantics.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Delta/ClosedTranslation.lean (80%) rename Ix/{Tc => Kernel}/Verify/Whnf/Delta/Integration.lean (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/Delta/OptionalReduction.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Delta/SpineUnfolding.lean (96%) rename Ix/{Tc => Kernel}/Verify/Whnf/Delta/StableCache.lean (94%) rename Ix/{Tc => Kernel}/Verify/Whnf/Delta/TrustedBody.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Delta/UnfoldingState.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/Delta/UniverseMonotonicity.lean (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/Driver/FullStep.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/Driver/PublicReducers.lean (94%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/ApplicationRequests.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/ArgumentBranches.lean (95%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/ArgumentExecution.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/ConstructorDispatch.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/ConstructorSynthesis.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/ConstructorSynthesisFallback.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/Ingress.lean (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/NatLiteral.lean (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/NatOffset.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/NatPatternMatching.lean (86%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/NatRecognizer.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/NatReduction.lean (91%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/NatRuleLayout.lean (96%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/OptionalReduction.lean (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/RuleInstantiation.lean (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/RuleSuffixTransport.lean (94%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/SelectedRule.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/StringLiteral.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/StructEtaControl.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/Substitution.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Iota/SynthesisRequests.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/NoDelta/BaseReductions.lean (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/NoDelta/ProjectionApplication.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/NoDelta/ProjectionDefinition.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/NoDelta/Quotient.lean (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/NoDelta/QuotientReflection.lean (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/NoDelta/Reducer.lean (94%) rename Ix/{Tc => Kernel}/Verify/Whnf/NoDelta/StringPrimitive.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Projection/NoAccelTail.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/Projection/StringCallback.lean (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/Projection/StringExpansion.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/README.md (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/Runtime/LazyIngress.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/RuntimeContracts.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/StructEta/CallbackPrefix.lean (96%) rename Ix/{Tc => Kernel}/Verify/Whnf/StructEta/Classifier.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/StructEta/ExactMajorTelescope.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/StructEta/Rebuild.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/StructEta/RebuildRequests.lean (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/StructEta/RebuildTail.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/StructEta/RecursionClassifier.lean (99%) rename Ix/{Tc => Kernel}/Verify/Whnf/StructEta/ScopedClassifier.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/StructEta/ScopedTelescope.lean (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/Structural/ApplicationCongruence.lean (92%) rename Ix/{Tc => Kernel}/Verify/Whnf/Structural/ApplicationRebuild.lean (94%) rename Ix/{Tc => Kernel}/Verify/Whnf/Structural/ApplicationStep.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Structural/ApplicationTails.lean (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/Structural/BasicStep.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Structural/BetaBoundary.lean (96%) rename Ix/{Tc => Kernel}/Verify/Whnf/Structural/CacheShell.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Structural/ProjectionStep.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Structural/RecursiveCallbacks.lean (93%) rename Ix/{Tc => Kernel}/Verify/Whnf/Structural/Reducer.lean (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/Structural/StepAssembly.lean (97%) rename Ix/{Tc => Kernel}/Verify/Whnf/Structural/VariableStep.lean (98%) rename Ix/{Tc => Kernel}/Verify/Whnf/Structural/VerifiedStep.lean (96%) rename Ix/{Tc => Kernel}/Verify/World.lean (98%) rename Ix/{Tc => Kernel}/Whnf.lean (99%) delete mode 100644 Ix/Tc.lean create mode 100644 Ix/Theory.lean create mode 100644 Ix/Theory/Certificate/Build.lean create mode 100644 Ix/Theory/Certificate/Claims.lean create mode 100644 Ix/Theory/Certificate/Modeled.lean create mode 100644 Ix/Theory/Certificate/Ordinary.lean create mode 100644 Ix/Theory/Certificate/OrdinarySource.lean create mode 100644 Ix/Theory/Certificate/Quotient.lean create mode 100644 Ix/Theory/Certificate/Standard.lean create mode 100644 Ix/Theory/Certificate/Structure.lean create mode 100644 Ix/Theory/Certificate/Suggest.lean create mode 100644 Ix/Theory/Certified.lean create mode 100644 Ix/Theory/Certified/Accept.lean create mode 100644 Ix/Theory/Certified/Admission.lean create mode 100644 Ix/Theory/Certified/Basis/Equality.lean create mode 100644 Ix/Theory/Certified/Basis/Iff.lean create mode 100644 Ix/Theory/Certified/Basis/Interface.lean create mode 100644 Ix/Theory/Certified/Basis/Nonempty.lean create mode 100644 Ix/Theory/Certified/Checker.lean create mode 100644 Ix/Theory/Certified/ClaimComposition.lean create mode 100644 Ix/Theory/Certified/Claims.lean create mode 100644 Ix/Theory/Certified/Frontier.lean create mode 100644 Ix/Theory/Certified/Level.lean create mode 100644 Ix/Theory/Certified/LevelEq.lean create mode 100644 Ix/Theory/Certified/LogicalPolicy.lean create mode 100644 Ix/Theory/Certified/Modeled/Admission.lean create mode 100644 Ix/Theory/Certified/Modeled/Equation.lean create mode 100644 Ix/Theory/Certified/Modeled/Source.lean create mode 100644 Ix/Theory/Certified/Modeled/Transport.lean create mode 100644 Ix/Theory/Certified/Natural/Admission.lean create mode 100644 Ix/Theory/Certified/Natural/Checked.lean create mode 100644 Ix/Theory/Certified/Natural/Publish.lean create mode 100644 Ix/Theory/Certified/Natural/Value.lean create mode 100644 Ix/Theory/Certified/Operations.lean create mode 100644 Ix/Theory/Certified/Ordinary/Admission.lean create mode 100644 Ix/Theory/Certified/Ordinary/Checked.lean create mode 100644 Ix/Theory/Certified/Ordinary/Computation.lean create mode 100644 Ix/Theory/Certified/Ordinary/ConstructorStage.lean create mode 100644 Ix/Theory/Certified/Ordinary/Constructors.lean create mode 100644 Ix/Theory/Certified/Ordinary/Container.lean create mode 100644 Ix/Theory/Certified/Ordinary/Eliminator.lean create mode 100644 Ix/Theory/Certified/Ordinary/Family.lean create mode 100644 Ix/Theory/Certified/Ordinary/LargeElim.lean create mode 100644 Ix/Theory/Certified/Ordinary/Reading.lean create mode 100644 Ix/Theory/Certified/Ordinary/RecursorReading.lean create mode 100644 Ix/Theory/Certified/Ordinary/RecursorStage.lean create mode 100644 Ix/Theory/Certified/Ordinary/RecursorSyntax.lean create mode 100644 Ix/Theory/Certified/Ordinary/RecursorValue.lean create mode 100644 Ix/Theory/Certified/Ordinary/RuleChecks.lean create mode 100644 Ix/Theory/Certified/Ordinary/RuleEquations.lean create mode 100644 Ix/Theory/Certified/Ordinary/RuleReading.lean create mode 100644 Ix/Theory/Certified/Ordinary/Shape.lean create mode 100644 Ix/Theory/Certified/Policy.lean create mode 100644 Ix/Theory/Certified/Prelude.lean create mode 100644 Ix/Theory/Certified/PropWhen.lean create mode 100644 Ix/Theory/Certified/Quotient/Admission.lean create mode 100644 Ix/Theory/Certified/Quotient/Checked.lean create mode 100644 Ix/Theory/Certified/Quotient/Publish.lean create mode 100644 Ix/Theory/Certified/Quotient/Reading.lean create mode 100644 Ix/Theory/Certified/Quotient/Syntax.lean create mode 100644 Ix/Theory/Certified/Quotient/Value.lean create mode 100644 Ix/Theory/Certified/Signature.lean create mode 100644 Ix/Theory/Certified/Source.lean create mode 100644 Ix/Theory/Certified/Standard/Admission.lean create mode 100644 Ix/Theory/Certified/Standard/Checked.lean create mode 100644 Ix/Theory/Certified/Standard/Realization.lean create mode 100644 Ix/Theory/Certified/Store.lean create mode 100644 Ix/Theory/Certified/Structure/Admission.lean create mode 100644 Ix/Theory/Certified/Structure/Checked.lean create mode 100644 Ix/Theory/Certified/Structure/Computation.lean create mode 100644 Ix/Theory/Certified/Structure/Publish.lean create mode 100644 Ix/Theory/Certified/Structure/Reading.lean create mode 100644 Ix/Theory/Certified/Structure/Syntax.lean create mode 100644 Ix/Theory/Certified/Structure/Value.lean create mode 100644 Ix/Theory/Certified/Telescope.lean create mode 100644 Ix/Theory/Const.lean create mode 100644 Ix/Theory/Expr.lean create mode 100644 Ix/Theory/Inductive/Levels.lean create mode 100644 Ix/Theory/LICENSE create mode 100644 Ix/Theory/LICENSE-APACHE create mode 100644 Ix/Theory/LICENSE-MIT create mode 100644 Ix/Theory/Model/Annotated.lean create mode 100644 Ix/Theory/Model/Context.lean create mode 100644 Ix/Theory/Model/Environment.lean create mode 100644 Ix/Theory/Model/Extension.lean create mode 100644 Ix/Theory/Model/Inductive/Codes.lean create mode 100644 Ix/Theory/Model/Inductive/Container.lean create mode 100644 Ix/Theory/Model/Inductive/Recursor.lean create mode 100644 Ix/Theory/Model/Inductive/Telescope.lean create mode 100644 Ix/Theory/Model/Instantiation.lean create mode 100644 Ix/Theory/Model/Interpret.lean create mode 100644 Ix/Theory/Model/Judgment.lean create mode 100644 Ix/Theory/Model/PrimitiveValues.lean create mode 100644 Ix/Theory/Model/ReferenceMap.lean create mode 100644 Ix/Theory/Model/SetModel/Container.lean create mode 100644 Ix/Theory/Model/SetModel/Iter.lean create mode 100644 Ix/Theory/Model/SetModel/Ops.lean create mode 100644 Ix/Theory/Model/SetModel/RecGraph.lean create mode 100644 Ix/Theory/Model/SetModel/TaggedSum.lean create mode 100644 Ix/Theory/Model/SetModel/TupleTower.lean create mode 100644 Ix/Theory/Model/SetTheory/Core.lean create mode 100644 Ix/Theory/Model/SetTheory/Derive/Choice.lean create mode 100644 Ix/Theory/Model/SetTheory/Derive/Empty.lean create mode 100644 Ix/Theory/Model/SetTheory/Derive/Graphs.lean create mode 100644 Ix/Theory/Model/SetTheory/Derive/Lfp.lean create mode 100644 Ix/Theory/Model/SetTheory/Derive/LfpFam.lean create mode 100644 Ix/Theory/Model/SetTheory/Derive/Omega.lean create mode 100644 Ix/Theory/Model/SetTheory/Derive/Pair.lean create mode 100644 Ix/Theory/Model/SetTheory/Derive/Pt.lean create mode 100644 Ix/Theory/Model/SetTheory/Derive/Quot.lean create mode 100644 Ix/Theory/Model/SetTheory/Derive/Sep.lean create mode 100644 Ix/Theory/Model/SetTheory/Derive/Sigma.lean create mode 100644 Ix/Theory/Model/SetTheory/Derive/Univ.lean create mode 100644 Ix/Theory/Model/SetTheory/Derive/Universe.lean create mode 100644 Ix/Theory/Model/Signature.lean create mode 100644 Ix/Theory/Model/Support.lean create mode 100644 Ix/Theory/Model/TelescopeSemantics.lean create mode 100644 Ix/Theory/Model/Value.lean create mode 100644 Ix/Theory/Model/WellDenoted.lean create mode 100644 Ix/Theory/NOTICE create mode 100644 Ix/Theory/Named/ConstructorValidityFixtures.lean create mode 100644 Ix/Theory/Named/Fixtures/ProjectionExpressibility.lean create mode 100644 Ix/Theory/Named/Inductive.lean create mode 100644 Ix/Theory/Named/InductiveFixtures.lean create mode 100644 Ix/Theory/Named/LICENSE create mode 100644 Ix/Theory/Named/Literals.lean create mode 100644 Ix/Theory/Named/LocalContext.lean create mode 100644 Ix/Theory/Named/Meta.lean create mode 100644 Ix/Theory/Named/MutualInductiveFixtures.lean create mode 100644 Ix/Theory/Named/NOTICE create mode 100644 Ix/Theory/Named/NestedInductive.lean create mode 100644 Ix/Theory/Named/NestedInductiveFixtures.lean create mode 100644 Ix/Theory/Named/Projection.lean create mode 100644 Ix/Theory/Named/Quot.lean create mode 100644 Ix/Theory/Named/Reference/Declaration.lean create mode 100644 Ix/Theory/Named/Reference/Environment.lean create mode 100644 Ix/Theory/Named/Reference/Environment/Basic.lean create mode 100644 Ix/Theory/Named/Reference/Expr.lean create mode 100644 Ix/Theory/Named/Reference/ForEachExprV.lean create mode 100644 Ix/Theory/Named/Reference/FuelConfig.lean create mode 100644 Ix/Theory/Named/Reference/Inductive/Add.lean create mode 100644 Ix/Theory/Named/Reference/Inductive/EliminationTrace.lean create mode 100644 Ix/Theory/Named/Reference/Inductive/Reduce.lean create mode 100644 Ix/Theory/Named/Reference/Inductive/ValidationTrace.lean create mode 100644 Ix/Theory/Named/Reference/Instantiate.lean create mode 100644 Ix/Theory/Named/Reference/Level.lean create mode 100644 Ix/Theory/Named/Reference/List.lean create mode 100644 Ix/Theory/Named/Reference/LocalContext.lean create mode 100644 Ix/Theory/Named/Reference/Primitive.lean create mode 100644 Ix/Theory/Named/Reference/PtrEq.lean create mode 100644 Ix/Theory/Named/Reference/Quot.lean create mode 100644 Ix/Theory/Named/Reference/TypeChecker.lean create mode 100644 Ix/Theory/Named/SingletonParity.lean create mode 100644 Ix/Theory/Named/Std/AxiomAudit.lean create mode 100644 Ix/Theory/Named/Std/Basic.lean create mode 100644 Ix/Theory/Named/Std/Control.lean create mode 100644 Ix/Theory/Named/Std/HashMap.lean create mode 100644 Ix/Theory/Named/Std/NodupKeys.lean create mode 100644 Ix/Theory/Named/Std/Ord.lean create mode 100644 Ix/Theory/Named/Std/PersistentHashMap.lean create mode 100644 Ix/Theory/Named/Std/SMap.lean create mode 100644 Ix/Theory/Named/Std/ToExpr.lean create mode 100644 Ix/Theory/Named/Std/VariableBang.lean create mode 100644 Ix/Theory/Named/Typing/Basic.lean create mode 100644 Ix/Theory/Named/Typing/Env.lean create mode 100644 Ix/Theory/Named/Typing/EnvLemmas.lean create mode 100644 Ix/Theory/Named/Typing/InductiveCertificate.lean create mode 100644 Ix/Theory/Named/Typing/InductiveLemmas.lean create mode 100644 Ix/Theory/Named/Typing/InductivePattern.lean create mode 100644 Ix/Theory/Named/Typing/InductivePatternWF.lean create mode 100644 Ix/Theory/Named/Typing/Injectivity.lean create mode 100644 Ix/Theory/Named/Typing/Lemmas.lean create mode 100644 Ix/Theory/Named/Typing/Meta.lean create mode 100644 Ix/Theory/Named/Typing/NestedInductiveLemmas.lean create mode 100644 Ix/Theory/Named/Typing/Pattern.lean create mode 100644 Ix/Theory/Named/Typing/QuotLemmas.lean create mode 100644 Ix/Theory/Named/Typing/Strong.lean create mode 100644 Ix/Theory/Named/Typing/UniqueTyping.lean create mode 100644 Ix/Theory/Named/VDecl.lean create mode 100644 Ix/Theory/Named/VEnv.lean create mode 100644 Ix/Theory/Named/VExpr.lean create mode 100644 Ix/Theory/Named/VLevel.lean create mode 100644 Ix/Theory/Named/Verify/Axioms.lean create mode 100644 Ix/Theory/Named/Verify/Environment/Basic.lean create mode 100644 Ix/Theory/Named/Verify/Environment/ConstructorValidation.lean create mode 100644 Ix/Theory/Named/Verify/Environment/ConstructorValidityMatrix.lean create mode 100644 Ix/Theory/Named/Verify/Environment/Elimination.lean create mode 100644 Ix/Theory/Named/Verify/Environment/EliminationFixturesCommon.lean create mode 100644 Ix/Theory/Named/Verify/Environment/EliminationFixturesEdges.lean create mode 100644 Ix/Theory/Named/Verify/Environment/EliminationFixturesEq.lean create mode 100644 Ix/Theory/Named/Verify/Environment/EliminationFixturesEqNat.lean create mode 100644 Ix/Theory/Named/Verify/Environment/EliminationFixturesNat.lean create mode 100644 Ix/Theory/Named/Verify/Environment/EliminationFixturesOrAnd.lean create mode 100644 Ix/Theory/Named/Verify/Environment/EliminationFixturesSmall.lean create mode 100644 Ix/Theory/Named/Verify/Environment/IndexedVecCandidate.lean create mode 100644 Ix/Theory/Named/Verify/Environment/IndexedVecConsReplay.lean create mode 100644 Ix/Theory/Named/Verify/Environment/IndexedVecConstructors.lean create mode 100644 Ix/Theory/Named/Verify/Environment/IndexedVecOuterReplay.lean create mode 100644 Ix/Theory/Named/Verify/Environment/IndexedVecSemanticReplay.lean create mode 100644 Ix/Theory/Named/Verify/Environment/InductiveFixtures.lean create mode 100644 Ix/Theory/Named/Verify/Environment/Lemmas.lean create mode 100644 Ix/Theory/Named/Verify/Environment/MutualInductiveFixtures.lean create mode 100644 Ix/Theory/Named/Verify/Environment/NestedReplay.lean create mode 100644 Ix/Theory/Named/Verify/Environment/NestedRepresentation.lean create mode 100644 Ix/Theory/Named/Verify/Environment/NestedTransformation.lean create mode 100644 Ix/Theory/Named/Verify/Environment/Normalization.lean create mode 100644 Ix/Theory/Named/Verify/Environment/NormalizationMatrix.lean create mode 100644 Ix/Theory/Named/Verify/Environment/SingletonParityMatrix.lean create mode 100644 Ix/Theory/Named/Verify/Environment/SingletonParityReplay.lean create mode 100644 Ix/Theory/Named/Verify/Expr.lean create mode 100644 Ix/Theory/Named/Verify/Level.lean create mode 100644 Ix/Theory/Named/Verify/LevelStd.lean create mode 100644 Ix/Theory/Named/Verify/LocalContext.lean create mode 100644 Ix/Theory/Named/Verify/Name.lean create mode 100644 Ix/Theory/Named/Verify/NameGenerator.lean create mode 100644 Ix/Theory/Named/Verify/NormLt.lean create mode 100644 Ix/Theory/Named/Verify/QSort.lean create mode 100644 Ix/Theory/Named/Verify/TypeChecker.lean create mode 100644 Ix/Theory/Named/Verify/TypeChecker/Basic.lean create mode 100644 Ix/Theory/Named/Verify/TypeChecker/InferType.lean create mode 100644 Ix/Theory/Named/Verify/TypeChecker/IsDefEq.lean create mode 100644 Ix/Theory/Named/Verify/TypeChecker/Reduce.lean create mode 100644 Ix/Theory/Named/Verify/TypeChecker/WHNF.lean create mode 100644 Ix/Theory/Named/Verify/Typing/ConditionallyTyped.lean create mode 100644 Ix/Theory/Named/Verify/Typing/Expr.lean create mode 100644 Ix/Theory/Named/Verify/Typing/Lemmas.lean create mode 100644 Ix/Theory/Named/Verify/VLCtx.lean create mode 100644 Ix/Theory/PORTING.md create mode 100644 Ix/Theory/Quot.lean create mode 100644 Ix/Theory/Ref.lean create mode 100644 Ix/Theory/Rename.lean create mode 100644 Ix/Theory/Std/Basic.lean create mode 100644 Ix/Theory/Store.lean create mode 100644 Ix/Theory/VLevel.lean create mode 100644 Models/SetTheory/IxSetTheoryModel.lean create mode 100644 Models/SetTheory/IxSetTheoryModel/Audit.lean create mode 100644 Models/SetTheory/IxSetTheoryModel/Carneiro.lean create mode 100644 Models/SetTheory/LICENSE-CON-LECHE create mode 100644 Models/SetTheory/NOTICE create mode 100644 Models/SetTheory/README.md create mode 100644 Models/SetTheory/lake-manifest.json create mode 100644 Models/SetTheory/lakefile.toml create mode 100644 Models/SetTheory/lean-toolchain create mode 100644 Tests/Certified/CLI.lean create mode 100644 Tests/Certified/Check.lean create mode 100644 Tests/Certified/Claims.lean create mode 100644 Tests/Certified/ClaimsMain.lean create mode 100644 Tests/Certified/FeatureCases.lean create mode 100644 Tests/Certified/Features.lean create mode 100644 Tests/Certified/Fidelity.lean create mode 100644 Tests/Certified/FidelityMain.lean create mode 100644 Tests/Certified/ImportManifest.lean create mode 100644 Tests/Certified/ModelSerialize.lean create mode 100644 Tests/Certified/Modeled.lean create mode 100644 Tests/Certified/ModeledAdversarial.lean create mode 100644 Tests/Certified/ModeledMain.lean create mode 100644 Tests/Certified/Ordinary.lean create mode 100644 Tests/Certified/Serialize.lean create mode 100644 Tests/Certified/Source.lean create mode 100644 Tests/Certified/SourceMain.lean create mode 100644 Tests/Certified/foundation.txt create mode 100644 Tests/Fixtures/Certified/README.md create mode 100644 Tests/Fixtures/Certified/c7-handoff.tar.gz rename Tests/Ix/{Tc => Kernel}/AccelDiff.lean (93%) rename Tests/Ix/{Tc => Kernel}/AnonDiff.lean (96%) rename Tests/Ix/{Tc => Kernel}/CheckTests.lean (98%) rename Tests/Ix/{Tc/Roundtrip.lean => Kernel/CheckerRoundtrip.lean} (92%) rename Tests/Ix/{Tc => Kernel}/InferDefEq.lean (97%) rename Tests/Ix/{Tc => Kernel}/IngressMetaTests.lean (96%) rename Tests/Ix/{Tc => Kernel}/InitScale.lean (94%) rename Tests/Ix/{Tc => Kernel}/IxonFixtures.lean (98%) rename Tests/Ix/{Tc => Kernel}/ParityEnv.lean (97%) rename Tests/Ix/{Tc => Kernel}/Pins.lean (94%) rename Tests/Ix/{Tc => Kernel}/Substrate.lean (98%) rename Tests/Ix/{Tc => Kernel}/TutorialTc.lean (96%) rename Tests/Ix/{Tc => Kernel}/Unit.lean (98%) rename Tests/Ix/{Tc => Kernel}/WhnfTests.lean (98%) delete mode 100644 Tests/Ix/Lean4Lean.lean create mode 100644 Tests/Theory.lean create mode 100644 Tests/Theory/Acceptance.lean create mode 100644 Tests/Theory/Audit/Certified.lean create mode 100644 Tests/Theory/Certified.lean create mode 100644 Tests/Theory/Checker.lean create mode 100644 Tests/Theory/Claims.lean create mode 100644 Tests/Theory/ImportManifest.lean create mode 100644 Tests/Theory/Modeled.lean create mode 100644 Tests/Theory/ModeledEquations.lean create mode 100644 Tests/Theory/ModeledFixtures.lean create mode 100644 Tests/Theory/ModeledNested.lean create mode 100644 Tests/Theory/ModeledPermutation.lean create mode 100644 Tests/Theory/NamedManifest.lean create mode 100644 Tests/Theory/Natural.lean create mode 100644 Tests/Theory/Operations.lean create mode 100644 Tests/Theory/Ordinary.lean create mode 100644 Tests/Theory/OrdinaryAcceptance.lean create mode 100644 Tests/Theory/Provenance.lean create mode 100644 Tests/Theory/Quotient.lean create mode 100644 Tests/Theory/RecursorGoldens.lean create mode 100644 Tests/Theory/Standard.lean create mode 100644 Tests/Theory/Structure.lean create mode 100644 Tests/Theory/Suggestions.lean create mode 100644 Tests/Theory/certified-foundation.txt create mode 100644 docs/certified-checking.md create mode 100644 docs/kernel-verification.md create mode 100644 docs/theory.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 267f0b62e..db8944612 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,8 @@ jobs: with: build-args: "--wfail -v" test: false - # build-all lint driver compiles every lib/exe target with --wfail, so a + # build-all checks production targets and the consistency foundation + # with --wfail. Named implementation proofs have a separate trust gate. A # warning in any target (exes, benchmarks, Apps) — not just the default lib # lean-action builds above — fails CI. - name: Build all targets @@ -86,6 +87,30 @@ jobs: use-github-cache: false - name: Test Ix CLI run: lake test --wfail -- cli + - name: Check certified source and claim adapters + run: lake run check-certified + + theory: + needs: build + runs-on: warp-ubuntu-latest-x64-16x + steps: + - uses: actions/checkout@v7 + - uses: ./.github/actions/setup-rust-toolchain + - uses: actions/cache/restore@v6 + with: + path: ./.lake + key: lake-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('lean-toolchain') }}-${{ hashFiles('lake-manifest.json') }}-${{ github.sha }} + restore-keys: lake-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('lean-toolchain') }}-${{ hashFiles('lake-manifest.json') }}- + - uses: leanprover/lean-action@v1 + with: + auto-config: false + use-github-cache: false + - name: Check implementation proofs and exact trust manifests + run: lake build IxKernelVerify IxCompileVerify + - name: Check direct kernel consistency refinement + run: lake build --wfail IxKernelConsistency + - name: Check consistency model and exact foundation manifest + run: lake run check-theory rust-test: runs-on: warp-ubuntu-latest-x64-8x diff --git a/.github/workflows/merge-tests.yml b/.github/workflows/merge-tests.yml index dccdf83cd..420798b0b 100644 --- a/.github/workflows/merge-tests.yml +++ b/.github/workflows/merge-tests.yml @@ -47,13 +47,13 @@ jobs: - name: Valgrind FFI kind: valgrind runner: warp-ubuntu-latest-x64-8x - - name: Ix.Tc verification and parity + - name: Ix.Kernel verification and parity kind: tc runner: warp-ubuntu-latest-x64-16x test_args: >- --ignored tc-anon-diff tc-init tc-tutorial tc-roundtrip tc-ingress-meta - tc-pins tc-accel-diff lean4lean + tc-pins tc-accel-diff runs-on: ${{ matrix.runner }} steps: - name: Validate merge-test variant @@ -147,19 +147,22 @@ jobs: --suppressions=.github/valgrind.supp \ .lake/build/bin/IxTests ffi - - name: Check Ix.Tc exported theorem trust manifest + - name: Check Ix.Kernel exported theorem trust manifest if: ${{ matrix.kind == 'tc' }} - run: lake build Ix.Tc.Verify.Audit.Completed Ix.Tc.Verify.Audit.Conditional Ix.Tc.Verify.Audit.Statements - - name: Build Ix.Tc formal verification + run: lake build Ix.Kernel.Verify.Audit.Completed Ix.Kernel.Verify.Audit.Conditional Ix.Kernel.Verify.Audit.Statements + - name: Build Ix.Kernel formal verification if: ${{ matrix.kind == 'tc' }} - run: lake build IxTcVerify - - name: Check Ix.Tc verification sorry frontier + run: lake build IxKernelVerify IxCompileVerify + - name: Check Ix.Kernel consistency refinement if: ${{ matrix.kind == 'tc' }} - run: lake build Ix.Tc.Verify.Audit.SorryFrontier - - name: Test Ix.Tc unit and adversarial fixtures + run: lake build --wfail IxKernelConsistency + - name: Check Ix.Kernel verification sorry frontier + if: ${{ matrix.kind == 'tc' }} + run: lake build Ix.Kernel.Verify.Audit.SorryFrontier + - name: Test Ix.Kernel unit and adversarial fixtures if: ${{ matrix.kind == 'tc' }} run: lake test --wfail -- tc-unit - - name: Run Ix.Tc ignored tests + - name: Run Ix.Kernel ignored tests if: ${{ matrix.kind == 'tc' }} run: lake test --wfail -- ${{ matrix.test_args }} diff --git a/.github/workflows/set-theory-model.yml b/.github/workflows/set-theory-model.yml new file mode 100644 index 000000000..e415b0c2a --- /dev/null +++ b/.github/workflows/set-theory-model.yml @@ -0,0 +1,47 @@ +name: Set-theory model + +on: + pull_request: + paths: + - 'Models/SetTheory/**' + - 'Ix/Theory/Model/SetTheory/Core.lean' + - 'lakefile.lean' + - 'lake-manifest.json' + - 'lean-toolchain' + - '.github/workflows/set-theory-model.yml' + merge_group: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + model: + runs-on: warp-ubuntu-latest-x64-16x + timeout-minutes: 60 + defaults: + run: + working-directory: Models/SetTheory + env: + MATHLIB_NO_CACHE_ON_UPDATE: '1' + MATHLIB_CACHE_DIR: .lake/mathlib-cache + steps: + - uses: actions/checkout@v7 + - uses: leanprover/lean-action@v1 + with: + auto-config: false + use-github-cache: false + - name: Check Lean toolchains match + run: cmp ../../lean-toolchain lean-toolchain + - uses: actions/cache@v6 + with: + path: Models/SetTheory/.lake + key: set-theory-model-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('Models/SetTheory/lean-toolchain', 'Models/SetTheory/lakefile.toml', 'Models/SetTheory/lake-manifest.json') }} + - name: Fetch the imported Mathlib modules and dependencies + run: lake exe cache get Mathlib.SetTheory.Cardinal.Regular Mathlib.SetTheory.ZFC.VonNeumann Mathlib.SetTheory.ZFC.Cardinal + - name: Build model and check axiom guard + run: lake build --wfail diff --git a/BENCHMARKS.md b/BENCHMARKS.md index 6d1740331..5b1b683d0 100644 --- a/BENCHMARKS.md +++ b/BENCHMARKS.md @@ -2,7 +2,7 @@ Head-to-head timings for every stage of the Ix pipeline across three environments, comparing the pure-Lean implementation (`Ix.CompileM` / -`Ix.DecompileM` / `Ix.Tc`) against the Rust implementation +`Ix.DecompileM` / `Ix.Kernel`) against the Rust implementation (`crates/compile` / `crates/kernel`). ## Methodology @@ -119,7 +119,7 @@ Rust: `ix decompile` (the decompile pass over a `.ixe`). Lean: flags → Pass 2 aux regeneration/recovery) *plus* the hash comparison against the canonicalized source (comparison overhead ~5 s at 205k constants). Lean decompilation is dominated by Pass 2's kernel bridge -(regeneration re-infers through `Ix.Tc`); Pass 2 runs on the +(regeneration re-infers through `Ix.Kernel`); Pass 2 runs on the wave-parallel driver (`decompileEnvPass2Parallel`, 16 workers — the count is memory-bound, not core-bound; `IX_DECOMPILE_WORKERS` overrides). The sequential figures from before the parallel driver are @@ -147,7 +147,7 @@ full verdict parity. worker config keeps warm caches; both a 32-worker meta run and a 32-worker anon run without cache clearing were OOM-killed while swap-thrashing). The anon row uses the scale configuration from the -`Ix.Tc` Mathlib-tier validation: 16 workers, `--clear-every 50` +`Ix.Kernel` Mathlib-tier validation: 16 workers, `--clear-every 50` (whole-worker-state renewal every 50 items; RSS plateaus ~42 GB) — **640,658/640,658 passed, zero failures, full verdict parity**. Anon mode dedups alpha-identical constants, hence the smaller count. diff --git a/Benchmarks/Lean4Lean.lean b/Benchmarks/Lean4Lean.lean deleted file mode 100644 index f7437b91c..000000000 --- a/Benchmarks/Lean4Lean.lean +++ /dev/null @@ -1,541 +0,0 @@ -import Cli -import Lean4Lean.Environment -import Ix.Meta -import Ix.TracingTexray -import Ix.Benchmark.Results -import Ix.Cli.ConstsFile - -/-! -# lean4lean typecheck benchmark - -Benchmarks the reference Lean4-in-Lean4 kernel — -[lean4lean](https://github.com/digama0/lean4lean), required by the lakefile -at a pinned rev — over the same library envs the other kernel backends -measure (`Benchmarks/Compile/Compile.lean`). It is the external -yardstick for the Ix kernels: `ix check-rs` (Rust, the `ooc` backend) and -`ix check-lean` (pure-Lean `Ix.Tc`) check the serialized `.ixe` of an env; -this tool has lean4lean check the same library from its `.olean`s. - -``` -lake exe bench-lean4lean [flags] - - the env's Lean source, same input `ix compile` takes - (e.g. `Benchmarks/Compile/CompileInitStd.lean`). Its - lake project supplies the module search path; the - module set is the file's transitive import closure. - --consts per-constant mode: replay each named constant's whole - transitive closure into a fresh kernel environment — - one row per name, the lean4lean counterpart of the ooc - backend's full-closure rows. Same flag/shape as - `ix check-rs --consts`. - --consts-file additionally read names from a file (one per line, - `#` comments ignored). Unions with --consts. - --json write benchmark results rows to (the shared - row contract, `Ix.Benchmark.Results`). - --json-name row key for the whole-library row (default: the - file's stem). The orchestrator passes the env name. - --no-build skip the `lake build` of the env module (for callers - that know the oleans are fresh). - --verbose print each declaration as it is added. -``` - -Without `--consts` the tool measures the **whole library**: every module in -the import closure is replayed through lean4lean — each module's new -constants are re-checked against its imports, one `IO.asTask` per module — -upstream `lake exe lean4lean`'s default mode, i.e. the canonical "how does -lean4lean perform on this library" number. Two driver divergences from -upstream, both required to run at all on this toolchain (the kernel itself -is untouched): duplicated cross-module realizations are skipped instead of -spuriously rejected, and only import regions are freed per task — the -stock binary segfaults freeing a module's own parts (see -`replayFromImports` for both). The row -carries `check-time` (wall over the sweep), `constants` (Σ declarations -added), `throughput` (constants/s), `peak-rss`. Caveats for cross-kernel -reading: lean4lean trusts `.olean` loading for a module's *imports* (every -constant is still checked exactly once, in its home module's task), its -parallelism unit is the module (tune with `LEAN_NUM_THREADS`), the -constant count is Lean declarations rather than Ixon constants — compare -end-to-end library numbers, not per-constant arithmetic — and auto-generated -lemmas that v4.29 multi-part oleans materialize in several modules are -checked once and skipped as duplicates thereafter (see `replayFromImports`; -upstream re-declares and spuriously rejects them). - -A kernel rejection writes the row as `{"status": "rejected"}` (no metrics — -a rejection is a correctness signal, not a benchmark datum) and the run -exits with the reserved code 3; a missing `.olean` is an infrastructure -error (exit 1) detected before any timed window. Rows are flushed after -every result, so a killed run keeps the rows measured so far. - -The replay machinery (`Context`/`State`/`replayConstant`/`replay`, -`replayFromImports`) is adapted from lean4lean's own `Main.lean` -(Apache 2.0, © the lean4lean authors) at the pinned rev — kept in lockstep -with the require so both sides agree on `Lean4Lean.addDecl`'s surface. --/ - -open Lean hiding Environment Exception -open Kernel - -namespace BenchLean4Lean - -/-- Like `Expr.getUsedConstants`, but produce a `NameSet`. -/ -def getUsedConstants' (e : Expr) : NameSet := - e.foldConsts {} fun c cs => cs.insert c - -/-- Return all names appearing in the type or value of a `ConstantInfo`. - -`allowOpaque := true` is load-bearing since v4.33: `ConstantInfo.value?` -now hides theorem proofs (and opaque bodies) by default, and a replay walk -that misses proof references skips their auxiliaries — e.g. the -`._f` structural-recursion helpers (`Nat.add_comm._f`), so the kernel then -rejects the theorem with "unknown constant". Mirrors the fork's -`Lean4Lean.Replay` fix. -/ -def getUsedConstants (c : ConstantInfo) : NameSet := - getUsedConstants' c.type ++ match c.value? (allowOpaque := true) with - | some v => getUsedConstants' v - | none => match c with - | .inductInfo val => .ofList val.ctors - | .ctorInfo val => ({} : NameSet).insert val.name - | .recInfo val => .ofList val.all - | _ => {} - -structure Context where - newConstants : Std.HashMap Name ConstantInfo - verbose := false - checkQuot := true - -structure State where - env : Environment - remaining : NameSet := {} - pending : NameSet := {} - postponedConstructors : NameSet := {} - postponedRecursors : NameSet := {} - numAdded : Nat := 0 - hasStrings := false - -abbrev M := ReaderT Context <| StateRefT State IO - -/-- Check if a `Name` still needs processing. If so, move it from `remaining` to `pending`. -/ -def isTodo (name : Name) : M Bool := do - let r := (← get).remaining - if r.contains name then - modify fun s => { s with remaining := s.remaining.erase name, pending := s.pending.insert name } - return true - else - return false - -def mapEnvM [Monad m] (ex : Exception) (f : Environment → m Environment) : m Exception := do - match ex with - | .unknownConstant env c => return .unknownConstant (← f env) c - | .alreadyDeclared env c => return .alreadyDeclared (← f env) c - | .declTypeMismatch env d t => return .declTypeMismatch env d t - | .declHasMVars env c e => return .declHasMVars (← f env) c e - | .declHasFVars env c e => return .declHasFVars (← f env) c e - | .funExpected env lctx e => return .funExpected (← f env) lctx e - | .typeExpected env lctx e => return .typeExpected (← f env) lctx e - | .letTypeMismatch env lctx n t1 t2 => return .letTypeMismatch (← f env) lctx n t1 t2 - | .exprTypeMismatch env lctx e t => return .exprTypeMismatch (← f env) lctx e t - | .appTypeMismatch env lctx e fn arg => return .appTypeMismatch (← f env) lctx e fn arg - | .invalidProj env lctx e => return .invalidProj (← f env) lctx e - | .thmTypeIsNotProp env c t => return .thmTypeIsNotProp (← f env) c t - | .other _ - | .deterministicTimeout - | .excessiveMemory - | .deepRecursion - | .interrupted => return ex - -/-- Use the current `Environment` to throw a `Kernel.Exception`. -/ -def throwKernelException (ex : Exception) : M α := do - let options := pp.match.set (pp.rawOnError.set {} true) false - -- The replayed environment has no extension state, so it cannot back the - -- pretty printer; a fresh empty environment is good enough for basic - -- printing of the offending declaration. - let env ← mkEmptyEnvironment - let ex ← mapEnvM ex fun _ => return env.toKernelEnv - Prod.fst <$> (Lean.Core.CoreM.toIO · { fileName := "", options, fileMap := default } { env }) do - Lean.throwKernelException ex - -def declName : Declaration → String - | .axiomDecl d => s!"axiomDecl {d.name}" - | .defnDecl d => s!"defnDecl {d.name}" - | .thmDecl d => s!"thmDecl {d.name}" - | .opaqueDecl d => s!"opaqueDecl {d.name}" - | .quotDecl => s!"quotDecl" - | .mutualDefnDecl d => s!"mutualDefnDecl {d.map (·.name)}" - | .inductDecl _ _ d _ => s!"inductDecl {d.map (·.name)}" - -/-- Add a declaration through the lean4lean kernel, possibly throwing a - `KernelException`. -/ -def addDecl (d : Declaration) : M Unit := do - if (← read).verbose then - println! "adding {declName d}" - let t1 ← IO.monoMsNow - match Lean4Lean.addDecl (← get).env d true with - | .ok env => - let t2 ← IO.monoMsNow - if t2 - t1 > 1000 then - println! "{declName d}: lean4lean took {t2 - t1}ms" - modify fun s => { s with env, numAdded := s.numAdded + 1 } - | .error ex => - throwKernelException ex - -def hasStrLit (e : Expr) : Bool := (e.find? (·.isStringLit)).isSome - -def constHasStrLit (ci : ConstantInfo) : Bool := - -- `allowOpaque := true` for the same reason as `getUsedConstants`: a string - -- literal inside a theorem proof must still pre-seed `String.ofList`. - hasStrLit ci.type || (ci.value? (allowOpaque := true)).any hasStrLit - -mutual -/-- -Check if a `Name` still needs to be processed (i.e. is in `remaining`). - -If so, recursively replay any constants it refers to, -to ensure we add declarations in the right order. - -Then construct the `Declaration` from its stored `ConstantInfo`, -and add it to the environment. --/ -partial def replayConstant (name : Name) : M Unit := do - if ← isTodo name then - let some ci := (← read).newConstants[name]? | unreachable! - let mut usedConstants := getUsedConstants ci - -- We want `String.ofList` to be available when encountering string literals. - unless (← get).hasStrings do - if constHasStrLit ci then - usedConstants := usedConstants.insert ``String.ofList - usedConstants := usedConstants.insert ``Char.ofNat - modify ({· with hasStrings := true }) - replayConstants usedConstants - -- Check that this name is still pending: a mutual block may have taken care of it. - if (← get).pending.contains name then - let addDeclAt (d : Declaration) := - try addDecl d catch e => throw <| IO.userError s!"at {name}: {e.toString}" - match ci with - | .defnInfo info => addDeclAt (.defnDecl info) - | .thmInfo info => addDeclAt (.thmDecl info) - | .axiomInfo info => addDeclAt (.axiomDecl info) - | .opaqueInfo info => addDeclAt (.opaqueDecl info) - | .inductInfo info => - let lparams := info.levelParams - let nparams := info.numParams - let all ← info.all.mapM fun n => do pure <| (← read).newConstants[n]! - for o in all do - modify fun s => - { s with remaining := s.remaining.erase o.name, pending := s.pending.erase o.name } - let ctorInfo ← all.mapM fun ci => do - pure (ci, ← ci.inductiveVal!.ctors.mapM fun n => do - pure (← read).newConstants[n]!) - -- Make sure we are really finished with the constructors. - for (_, ctors) in ctorInfo do - for ctor in ctors do - replayConstants (getUsedConstants ctor) - let types : List InductiveType := ctorInfo.map fun ⟨ci, ctors⟩ => - { name := ci.name - type := ci.type - ctors := ctors.map fun ci => { name := ci.name, type := ci.type } } - addDeclAt (.inductDecl lparams nparams types false) - -- We postpone checking constructors, - -- and at the end make sure they are identical - -- to the constructors generated when we replay the inductives. - | .ctorInfo info => - modify fun s => { s with postponedConstructors := s.postponedConstructors.insert info.name } - -- Similarly we postpone checking recursors. - | .recInfo info => - modify fun s => { s with postponedRecursors := s.postponedRecursors.insert info.name } - | .quotInfo _ => addDeclAt .quotDecl - modify fun s => { s with pending := s.pending.erase name } - -/-- Replay a set of constants one at a time. -/ -partial def replayConstants (names : NameSet) : M Unit := do - for n in names do replayConstant n - -end - -end BenchLean4Lean - -deriving instance BEq for ConstantVal -deriving instance BEq for ConstructorVal -deriving instance BEq for RecursorRule -deriving instance BEq for RecursorVal - -namespace BenchLean4Lean - -/-- -Check that all postponed constructors are identical to those generated -when we replayed the inductives. --/ -def checkPostponedConstructors : M Unit := do - for ctor in (← get).postponedConstructors do - match (← get).env.constants.find? ctor, (← read).newConstants[ctor]? with - | some (.ctorInfo info), some (.ctorInfo info') => - unless info == info' do throw <| IO.userError s!"Invalid constructor {ctor}" - | _, _ => throw <| IO.userError s!"No such constructor {ctor}" - -/-- -Check that all postponed recursors are identical to those generated -when we replayed the inductives. --/ -def checkPostponedRecursors : M Unit := do - for ctor in (← get).postponedRecursors do - match (← get).env.constants.find? ctor, (← read).newConstants[ctor]? with - | some (.recInfo info), some (.recInfo info') => - unless info == info' do throw <| IO.userError s!"Invalid recursor {ctor}" - | _, _ => throw <| IO.userError s!"No such recursor {ctor}" - -/-- Check that at the end of (any) file, the quotient module is initialized. -(It will already be initialized at the beginning, unless this is the very -first file, which is responsible for initializing it.) -/ -def checkQuotInit : M Unit := do - unless (← get).env.quotInit do - throw <| IO.userError s!"initial import (Init.Prelude) didn't initialize quotient module" - -/-- "Replay" some constants into an `Environment`, sending them to the - lean4lean kernel for checking. Returns the number of declarations added - and the final environment. -/ -def replay (ctx : Context) (env : Environment) (decl : Option Name := none) : - IO (Nat × Environment) := do - let mut remaining : NameSet := ∅ - for (n, ci) in ctx.newConstants.toList do - -- We skip unsafe constants, and also partial constants. - if !ci.isUnsafe && !ci.isPartial then - remaining := remaining.insert n - let (_, s) ← StateRefT'.run (s := { env, remaining }) do - ReaderT.run (r := ctx) do - match decl with - | some d => replayConstant d - | none => - for n in remaining do - replayConstant n - checkPostponedConstructors - checkPostponedRecursors - if (← read).checkQuot then checkQuotInit - return (s.numAdded, s.env) - -/-- Read a module's olean parts (base + server + private when present), - most complete last — the shape `readModuleDataParts` and the - toolchain's own `LeanChecker` frontend use. Shared by the closure - scanner and the replay tasks so both see the same (private-level) - import set. Throws when the base olean is missing. -/ -def readModuleParts (module : Name) : IO (Array (ModuleData × CompactedRegion)) := do - let mFile ← findOLean module - unless (← mFile.pathExists) do - throw <| IO.userError s!"object file '{mFile}' of module {module} does not exist" - let mut fnames := #[mFile] - let sFile := OLeanLevel.server.adjustFileName mFile - if (← sFile.pathExists) then - fnames := fnames.push sFile - let pFile := OLeanLevel.private.adjustFileName mFile - if (← pFile.pathExists) then - fnames := fnames.push pFile - readModuleDataParts fnames - -open private ImportedModule.mk from Lean.Environment in -/-- Replay one module's new constants against its (trusted-loaded) imports — - upstream `lake exe lean4lean`'s per-module unit of work. -/ -unsafe def replayFromImports (module : Name) (verbose := false) : IO Nat := do - let parts ← readModuleParts module - let some (mod, _) := parts[parts.size - 1]? | unreachable! -- load private module data - let (_, s) ← (importModulesCore mod.imports).run - let env ← match Kernel.Environment.finalizeImport s mod.imports module 0 with - | .ok env => pure env - | .error e => throw <| .userError <| ← (e.toMessageData {}).toString - let mut newConstants := {} - for name in mod.constNames, ci in mod.constants do - -- v4.29 multi-part oleans can materialize the same auto-generated - -- lemma (`*.eq_1`, `*.congr_simp`, …) in several modules' parts; a - -- real import dedups those realizations in `finalizeImport`, so the - -- replay must skip names the imported env already provides instead of - -- re-declaring them (upstream replays them and spuriously rejects, - -- e.g. on v4.29 Std). - if (env.constants.find? name).isNone then - newConstants := newConstants.insert name ci - let (n, env') ← replay { newConstants, verbose } env - -- Free the task's IMPORT regions only (the memory that scales with the - -- sweep: every task maps its whole import closure). Upstream also frees - -- the module's own `parts` regions, which segfaults — reproducibly, in - -- the stock `lean4lean` binary too on this toolchain (decref of - -- persistent region objects after munmap at scope exit) — so the small - -- per-module parts stay mapped: one copy of the library across the - -- sweep, not one closure per task. - (Environment.ofKernelEnv env').freeRegions - pure n - -/-- Replay every module of the library through lean4lean, one `IO.asTask` - per module (parallelism follows the task pool, i.e. `LEAN_NUM_THREADS`). - Callers pre-flight olean existence via `moduleClosure`, so a task - failure here is a kernel rejection, not infrastructure. Returns the - total declarations added plus per-module failures. -/ -unsafe def replayLibrary (modules : Array Name) (verbose : Bool) : - IO (Nat × Array (Name × String)) := do - let mut tasks := #[] - for m in modules do - tasks := tasks.push (m, ← IO.asTask (replayFromImports m verbose)) - let mut added := 0 - let mut failures : Array (Name × String) := #[] - for (m, t) in tasks do - match t.get with - | .error e => failures := failures.push (m, toString e) - | .ok n => added := added + n - return (added, failures) - -/-- Transitive module closure of `roots`, discovered by scanning olean - headers (private-level parts, so `private import`s are covered). No - environment is imported here: the whole-library sweep's tasks free - their compacted regions when done — upstream's memory bound — which is - only sound while nothing else in the process shares mapped regions. - The scanner's own header regions stay mapped (the returned `Name`s - live inside them): one copy of the library's headers, not one per - task. A missing olean throws here, before any timed window. -/ -def moduleClosure (roots : Array Name) : IO (Array Name) := do - let mut seen : NameSet := {} - let mut order : Array Name := #[] - let mut stack : List Name := roots.toList - repeat - match stack with - | [] => break - | m :: rest => - stack := rest - if seen.contains m then - continue - seen := seen.insert m - order := order.push m - let parts ← readModuleParts m - let some (mod, _) := parts[parts.size - 1]? | unreachable! - for imp in mod.imports do - unless seen.contains imp.module do - stack := imp.module :: stack - return order - -/-- Replay `target`'s whole transitive closure into a fresh kernel - environment — the full-closure check semantics of the ooc backend's - per-constant rows. Returns the closure size (declarations added). -/ -def replayClosure (env : Lean.Environment) (newConstants : Std.HashMap Name ConstantInfo) - (target : Name) (verbose : Bool) : IO Nat := do - let _ := env - (·.1) <$> replay { newConstants, verbose, checkQuot := false } (.empty default) (some target) - -/-- Resolve a raw `--consts` string against the env: `toName` first, then a - displayed-form scan (numeric/private components don't round-trip through - `toName`) — the same fallback the other tools' resolvers use. -/ -def resolveName (env : Lean.Environment) (raw : String) : Option Name := - let n := raw.toName - if env.constants.contains n then some n - else env.constants.fold (init := none) fun acc cn _ => - acc <|> if toString cn == raw then some cn else none - -open Ix.Benchmark.Results in -unsafe def runBenchCmd (p : Cli.Parsed) : IO UInt32 := do - let some pathArg := p.positionalArg? "path" - | p.printError "error: must specify to the env's Lean source (e.g. Benchmarks/Compile/CompileInitStd.lean)" - return exitUsage - let path := pathArg.as! String - let jsonOut : Option String := (p.flag? "json").map (·.as! String) - let jsonName := ((p.flag? "json-name").map (·.as! String)).getD - ((System.FilePath.mk path).fileStem.getD path) - let verbose := p.hasFlag "verbose" - let rawNames ← Ix.Cli.ConstsFile.gather p - - -- Build the env module first (outside every timed window). The file's - -- lake project supplies the module search path — the same entry - -- `ix compile` uses, so both backends accept the same registry - -- `module` path. - unless p.hasFlag "no-build" do buildFile path - - if rawNames.isEmpty then - -- Whole-library mode. The import closure is enumerated by scanning - -- olean headers — deliberately WITHOUT importing an environment into - -- this process: the replay tasks free their compacted regions when - -- done (upstream's memory bound, ~steady-state instead of the whole - -- library's fixed-up regions accumulating), and those frees are only - -- sound while no co-resident env shares mapped regions. - initLeanSearchPath (← IO.FS.realPath path).parent - let header ← Lean.parseImports' (← IO.FS.readFile path) path - let modules ← moduleClosure (header.imports.map (·.module)) - IO.println s!"Loaded {path}: {modules.size} modules in the import closure" - TracingTexray.startSampler - -- Whole-library row: module-parallel replay of the import closure. - IO.println s!"replaying {modules.size} modules through lean4lean …" - (← IO.getStdout).flush - TracingTexray.resetPeakTreeRss - let t0 ← IO.monoNanosNow - let (added, failures) ← replayLibrary modules verbose - let t1 ← IO.monoNanosNow - let secs := (t1 - t0).toFloat / 1e9 - let peak ← TracingTexray.peakTreeRssBytes - if failures.isEmpty then - if let some out := jsonOut then - writeRow out jsonName "ok" - [ ("check-time", jsonRound 6 secs) - , ("constants", Lean.toJson added) - , ("throughput", jsonRound 2 (if secs > 0 then added.toFloat / secs else 0)) - , ("peak-rss", Lean.toJson peak) ] - IO.println s!"{jsonName}: checked {added} declarations in {secs}s \ - ({(added.toFloat / secs).toUInt64} consts/s)" - return 0 - else - for (m, e) in failures do - IO.eprintln s!"❌ lean4lean REJECTED module {m}: {e}" - if let some out := jsonOut then - writeRow out jsonName "rejected" [] - return exitRejected - else - -- Per-constant mode: the elaborated file env supplies the constants - -- map (`getFileEnv`, the same entry `ix compile` uses — it also sets - -- the search path). Closure replays never free regions, so the - -- co-resident env is fine here. Each row is the name's whole - -- transitive closure into a fresh kernel env. - let env ← getFileEnv path - TracingTexray.startSampler - let newConstants := env.constants.fold - (init := ({} : Std.HashMap Name ConstantInfo)) fun m n ci => m.insert n ci - let mut anyRejected := false - let mut idx := 0 - for raw in rawNames do - idx := idx + 1 - match resolveName env raw with - | none => IO.eprintln s!"warning: {raw} not found in the env; skipping" - | some target => - -- Announce BEFORE the replay (flushed): a kill mid-replay must - -- leave the in-flight constant's name in the log. - IO.println s!" [{idx}/{rawNames.size}] replaying closure of {raw} …" - (← IO.getStdout).flush - TracingTexray.resetPeakTreeRss - let t0 ← IO.monoNanosNow - let res ← (replayClosure env newConstants target verbose).toBaseIO - let t1 ← IO.monoNanosNow - let secs := (t1 - t0).toFloat / 1e9 - let peak ← TracingTexray.peakTreeRssBytes - match res with - | .ok added => - if let some out := jsonOut then - writeRow out raw "ok" - [ ("check-time", jsonRound 6 secs) - , ("constants", Lean.toJson added) - , ("throughput", jsonRound 2 (if secs > 0 then added.toFloat / secs else 0)) - , ("peak-rss", Lean.toJson peak) ] - IO.println s!" {raw}: constants={added} check={secs}s" - | .error e => - IO.eprintln s!" ❌ {raw} FAILED TO TYPECHECK: {e}" - if let some out := jsonOut then - writeRow out raw "rejected" [] - anyRejected := true - return if anyRejected then exitRejected else 0 - -end BenchLean4Lean - -unsafe def benchLean4LeanCmd : Cli.Cmd := `[Cli| - "bench-lean4lean" VIA BenchLean4Lean.runBenchCmd; - "Benchmark the lean4lean reference kernel over a library env (whole-library module replay, or per-constant closures with --consts)" - - FLAGS: - consts : String; "Per-constant mode: comma-separated fully-qualified names, each replayed as its whole transitive closure into a fresh kernel env (the ooc backend's full-closure row shape). Same flag/shape as `ix check-rs --consts`." - "consts-file" : String; "Additionally read constant names from a file (one per line; `#` comments and blank lines ignored). Unions with --consts." - json : String; "Write benchmark results rows to this path (shared row contract). Off by default." - "json-name" : String; "Row key for the whole-library row (default: the file stem; the orchestrator passes the registry env name)." - "no-build"; "Skip the `lake build` of the env module (callers that know the oleans are fresh)." - verbose; "Print each declaration as it is added." - - ARGS: - path : String; "Path to the env's Lean source, e.g. Benchmarks/Compile/CompileInitStd.lean (same input as `ix compile`)" -] - diff --git a/Benchmarks/Lean4LeanMain.lean b/Benchmarks/Lean4LeanMain.lean deleted file mode 100644 index dbfb3d5b0..000000000 --- a/Benchmarks/Lean4LeanMain.lean +++ /dev/null @@ -1,11 +0,0 @@ -import Benchmarks.Lean4Lean - -/-! -Exe root for `bench-lean4lean`. `main` lives here, in a module nothing -imports, so the machinery in `Benchmarks.Lean4Lean` stays importable -(the `lean4lean` ignored test runner uses it) without a root-level `main` -collision. --/ - -unsafe def main (args : List String) : IO UInt32 := - benchLean4LeanCmd.validate args diff --git a/Ix.lean b/Ix.lean index 9ad43890d..8f0c35502 100644 --- a/Ix.lean +++ b/Ix.lean @@ -17,7 +17,7 @@ public import Ix.Catalog public import Ix.ImportIxe public import Ix.IxEval public import Ix.KernelCheck -public import Ix.Tc +public import Ix.Kernel public import Ix.Claim public import Ix.Merkle public import Ix.AssumptionTree diff --git a/Ix/Address.lean b/Ix/Address.lean index a3a75bf25..a3196baaa 100644 --- a/Ix/Address.lean +++ b/Ix/Address.lean @@ -45,7 +45,12 @@ instance : Hashable Address where ||| ((h.get! 7).toUInt64 <<< 56) /-- Compute the Blake3 hash of a `ByteArray`, returning an `Address`. -/ -def Address.blake3 (x: ByteArray) : Address := ⟨(Blake3.Rust.hash x).val⟩ +def Address.blake3 (x: ByteArray) : Address := + let hasher := Blake3.Rust.hasherUpdate (Blake3.Rust.hasherInit ()) x + -- Supply a kernel-checked size bound: the upstream `hash` helper fills + -- this argument using native evaluation, adding an unnecessary axiom. + ⟨(Blake3.HasherOps.finalizeWithLength hasher 32 (by + rcases System.Platform.numBits_eq with bits | bits <;> rw [bits] <;> decide)).val⟩ /-- Convert a nibble (0--15) to its lowercase hexadecimal character. -/ def hexOfNat : Nat -> Option Char diff --git a/Ix/AuxGen/ExprUtils.lean b/Ix/AuxGen/ExprUtils.lean index 9f2a14ffa..fc6618a79 100644 --- a/Ix/AuxGen/ExprUtils.lean +++ b/Ix/AuxGen/ExprUtils.lean @@ -13,7 +13,7 @@ The kernel-backed half of expr_utils.rs (TcScope, kenv ingress, `decompose_inductive_type`, `kexpr_to_lean`, `to_kexpr_static`, the WHNF source-name restore machinery) is intentionally NOT here — it is a - separate milestone that bridges to `Ix.Tc`. + separate milestone that bridges to `Ix.Kernel`. PARITY RULE: every constructed node goes through the hash-maintaining smart constructors in `Ix.Environment` (`Expr.mkApp`, `Level.mkMax`, ...) diff --git a/Ix/AuxGen/Kernel.lean b/Ix/AuxGen/Kernel.lean index 5de7681e6..10604ae7b 100644 --- a/Ix/AuxGen/Kernel.lean +++ b/Ix/AuxGen/Kernel.lean @@ -6,7 +6,7 @@ compile-side entry points of `crates/kernel/src/ingress.rs` (:2097-2270). aux_gen needs exactly four kernel operations — `whnf`, `infer` + `ensureSort`, `isDefEq`, `isLargeEliminator` — over Meta-mode `KExpr`; - the pure-Lean kernel `Ix.Tc` exposes all four (Knot.lean), so this file + the pure-Lean kernel `Ix.Kernel` exposes all four (Knot.lean), so this file only supplies the VALUE bridge: - `Ix.Expr → KExpr .meta` (`toKexprStatic` for open terms in an FVar @@ -16,13 +16,13 @@ `ensureInKenvOf` family) under PROVISIONAL addresses (`resolveLeanNameAddr`: compiled address if known, else the name hash — mirrors KernelCtx's "addresses may shift" model); - - `TcScope`: a scoped view running `Ix.Tc.TcM` actions against the + - `TcScope`: a scoped view running `Ix.Kernel.TcM` actions against the bridge state, with the fault-in retry loop and WHNF source-name restoration. State model: Rust's `KernelCtx { kenv }` + `KEnv.ingress_cache` become `AuxKernelCtx { tcState, ingressCache }` — the cache lives HERE, not in - `Ix.Tc.KEnv` (`Ix/Tc` is consumed, never modified). Rust's fresh + `Ix.Kernel.KEnv` (`Ix/Kernel` is consumed, never modified). Rust's fresh `TypeChecker::new(&mut kenv)` per scope = fresh `TcState.new` carrying over the persistent `KEnv` (whose whnf/infer caches live inside it, matching the Rust split of TC-transient vs kenv-persistent state). @@ -32,7 +32,7 @@ public import Ix.Common public import Ix.Address public import Ix.Environment public import Ix.CompileM -public import Ix.Tc +public import Ix.Kernel public import Ix.AuxGen.Types public import Ix.AuxGen.ExprUtils public import Ix.AuxGen.Levels @@ -43,16 +43,16 @@ namespace Ix.AuxGen open Ix.CompileM (CompileM CompileError) -abbrev MKExpr := Ix.Tc.KExpr .meta -abbrev MKUniv := Ix.Tc.KUniv .meta -abbrev MKId := Ix.Tc.KId .meta -abbrev MKConst := Ix.Tc.KConst .meta +abbrev MKExpr := Ix.Kernel.KExpr .meta +abbrev MKUniv := Ix.Kernel.KUniv .meta +abbrev MKId := Ix.Kernel.KId .meta +abbrev MKConst := Ix.Kernel.KConst .meta -/-- Inverse of `Ix.Tc.EgressLean.safetyToLean`. -/ +/-- Inverse of `Ix.Kernel.EgressLean.safetyToLean`. -/ def safetyOfLean : Lean.DefinitionSafety → Ix.DefinitionSafety | .unsafe => .unsaf | .safe => .safe | .partial => .part -/-- Inverse of `Ix.Tc.EgressLean.quotKindToLean`. -/ +/-- Inverse of `Ix.Kernel.EgressLean.quotKindToLean`. -/ def quotKindOfLean : Lean.QuotKind → Ix.QuotKind | .type => .type | .ctor => .ctor | .lift => .lift | .ind => .ind @@ -98,17 +98,17 @@ def AddrMaps.ofCompileEnv (cenv : Ix.CompileM.CompileEnv) SMART `mkMax`/`mkIMax` (Rust `KUniv::max/imax` normalize). -/ partial def leanLevelToKuniv (lvl : Level) (paramNames : Array Name) : MKUniv := match lvl with - | .zero _ => Ix.Tc.KUniv.mkZero - | .succ l _ => Ix.Tc.KUniv.mkSucc (leanLevelToKuniv l paramNames) + | .zero _ => Ix.Kernel.KUniv.mkZero + | .succ l _ => Ix.Kernel.KUniv.mkSucc (leanLevelToKuniv l paramNames) | .max a b _ => - Ix.Tc.KUniv.mkMax (leanLevelToKuniv a paramNames) + Ix.Kernel.KUniv.mkMax (leanLevelToKuniv a paramNames) (leanLevelToKuniv b paramNames) | .imax a b _ => - Ix.Tc.KUniv.mkIMax (leanLevelToKuniv a paramNames) + Ix.Kernel.KUniv.mkIMax (leanLevelToKuniv a paramNames) (leanLevelToKuniv b paramNames) | .param name _ => match paramNames.findIdx? (· == name) with - | some idx => Ix.Tc.KUniv.mkParam idx.toUInt64 name + | some idx => Ix.Kernel.KUniv.mkParam idx.toUInt64 name | none => panic! s!"unknown level param `{name.pretty}` not found in param_names \ {paramNames.toList.map (·.pretty)}" @@ -135,7 +135,7 @@ partial def kunivToLevel (u : MKUniv) (paramNames : Array Name) : Level := /-- Rust `KernelCtx` + `KEnv.ingress_cache`. The cache key is `(expr contentHash, paramNamesHash)` — Rust ingress.rs:2216. -/ structure AuxKernelCtx where - tcState : Ix.Tc.TcState .meta + tcState : Ix.Kernel.TcState .meta ingressCache : Std.HashMap (Address × Address) MKExpr := {} /-- Mirrors Rust `KernelCtx.aux_ingress_seen`: ids whose `ingressAuxGenDep` dispatch already ran against this kenv. The @@ -152,16 +152,16 @@ structure AuxKernelCtx where simply never match the provisional-address constants — same shape as the Rust bridge, whose static prim addresses don't match either). -/ def AuxKernelCtx.new : AuxKernelCtx := - { tcState := Ix.Tc.TcState.new {} - (Ix.Tc.Primitives.ofResolve .canonical fun _ => none) } + { tcState := Ix.Kernel.TcState.new {} + (Ix.Kernel.Primitives.ofResolve .canonical fun _ => none) } /-- Bridge monad: aux kernel state over CompileM. -/ abbrev KBridgeM := StateT AuxKernelCtx CompileM -/-- Run an `Ix.Tc.TcM` action against the bridge's kernel state, +/-- Run an `Ix.Kernel.TcM` action against the bridge's kernel state, threading the state back in BOTH outcomes (Rust's `&mut` semantics — caches warmed by a failing call stay warm). -/ -def runTc (act : Ix.Tc.TcM .meta α) : KBridgeM (Except (Ix.Tc.TcError .meta) α) := do +def runTc (act : Ix.Kernel.TcM .meta α) : KBridgeM (Except (Ix.Kernel.TcError .meta) α) := do let kctx ← get match act kctx.tcState with | .ok a st' => @@ -214,7 +214,7 @@ partial def leanExprToKexprCached (e : Expr) (paramNames : Array Name) return hit -- Accumulate consecutive mdata wrappers. - let mut mdataLayers : Array Ix.Tc.MData := #[] + let mut mdataLayers : Array Ix.Kernel.MData := #[] let mut cur := e let mut go := true while go do @@ -230,50 +230,50 @@ partial def leanExprToKexprCached (e : Expr) (paramNames : Array Name) if idx < binderNames.size then return binderNames[binderNames.size - 1 - idx]! return Name.mkAnon - pure (Ix.Tc.KExpr.mkVar (UInt64.ofNat idx) name mdataLayers) + pure (Ix.Kernel.KExpr.mkVar (UInt64.ofNat idx) name mdataLayers) | .sort lvl _ => - pure (Ix.Tc.KExpr.mkSort (leanLevelToKuniv lvl paramNames) mdataLayers) + pure (Ix.Kernel.KExpr.mkSort (leanLevelToKuniv lvl paramNames) mdataLayers) | .const name us _ => let zid : MKId := ⟨maps.resolve name, name⟩ let zus := us.map (leanLevelToKuniv · paramNames) - pure (Ix.Tc.KExpr.mkConst zid zus mdataLayers) + pure (Ix.Kernel.KExpr.mkConst zid zus mdataLayers) | .app f a _ => let fk ← leanExprToKexprCached f paramNames binderNames pnHash maps let ak ← leanExprToKexprCached a paramNames binderNames pnHash maps - pure (Ix.Tc.KExpr.mkApp fk ak mdataLayers) + pure (Ix.Kernel.KExpr.mkApp fk ak mdataLayers) | .forallE binderName dom body bi _ => let dk ← leanExprToKexprCached dom paramNames binderNames pnHash maps let bk ← leanExprToKexprCached body paramNames (binderNames.push binderName) pnHash maps - pure (Ix.Tc.KExpr.mkAll binderName bi dk bk mdataLayers) + pure (Ix.Kernel.KExpr.mkAll binderName bi dk bk mdataLayers) | .lam binderName dom body bi _ => let dk ← leanExprToKexprCached dom paramNames binderNames pnHash maps let bk ← leanExprToKexprCached body paramNames (binderNames.push binderName) pnHash maps - pure (Ix.Tc.KExpr.mkLam binderName bi dk bk mdataLayers) + pure (Ix.Kernel.KExpr.mkLam binderName bi dk bk mdataLayers) | .letE binderName ty val body nd _ => let tk ← leanExprToKexprCached ty paramNames binderNames pnHash maps let vk ← leanExprToKexprCached val paramNames binderNames pnHash maps let bk ← leanExprToKexprCached body paramNames (binderNames.push binderName) pnHash maps - pure (Ix.Tc.KExpr.mkLet binderName tk vk bk nd mdataLayers) + pure (Ix.Kernel.KExpr.mkLet binderName tk vk bk nd mdataLayers) | .proj pname idx s _ => let zid : MKId := ⟨maps.resolve pname, pname⟩ let sk ← leanExprToKexprCached s paramNames binderNames pnHash maps - pure (Ix.Tc.KExpr.mkPrj zid (UInt64.ofNat idx) sk mdataLayers) + pure (Ix.Kernel.KExpr.mkPrj zid (UInt64.ofNat idx) sk mdataLayers) | .lit (.natVal n) _ => -- Compile-side blob convention: 8-byte u64 LE (Rust to_kexpr_static -- / ingress use `nat_to_u64(n).to_le_bytes()`), NOT the kernel's -- trimmed `natBlob`. - pure (Ix.Tc.KExpr.mkNat n (Address.blake3 (UInt64.ofNat n).toLEBytes) mdataLayers) + pure (Ix.Kernel.KExpr.mkNat n (Address.blake3 (UInt64.ofNat n).toLEBytes) mdataLayers) | .lit (.strVal s) _ => - pure (Ix.Tc.KExpr.mkStr s (Address.blake3 s.toUTF8) mdataLayers) + pure (Ix.Kernel.KExpr.mkStr s (Address.blake3 s.toUTF8) mdataLayers) | .fvar _ _ => -- Closed-term converter: fvars have no meaning here (Rust `_raw` -- has no Fvar arm reachable from ensure-in-kenv callers). - pure (Ix.Tc.KExpr.mkSort Ix.Tc.KUniv.mkZero) + pure (Ix.Kernel.KExpr.mkSort Ix.Kernel.KUniv.mkZero) | .mvar _ _ => - pure (Ix.Tc.KExpr.mkSort Ix.Tc.KUniv.mkZero) + pure (Ix.Kernel.KExpr.mkSort Ix.Kernel.KUniv.mkZero) | .mdata .. => unreachable! let result ← internK raw @@ -303,11 +303,11 @@ def ensurePreludeInKenvOf (maps : AddrMaps) : KBridgeM Unit := do let uName := Name.mkStr .mkAnon "u" -- PUnit.{u} : Sort u ; PUnit.unit : PUnit.{u} - let u0 : MKUniv := Ix.Tc.KUniv.mkParam 0 uName - let punitTy := Ix.Tc.KExpr.mkSort u0 + let u0 : MKUniv := Ix.Kernel.KUniv.mkParam 0 uName + let punitTy := Ix.Kernel.KExpr.mkSort u0 let unitName := Name.mkStr punitName "unit" let unitId : MKId := ⟨maps.resolve unitName, unitName⟩ - let unitTy := Ix.Tc.KExpr.mkConst punitId #[Ix.Tc.KUniv.mkParam 0 uName] + let unitTy := Ix.Kernel.KExpr.mkConst punitId #[Ix.Kernel.KUniv.mkParam 0 uName] kenvInsert unitId (.ctor unitName #[uName] false 1 punitId 0 0 0 unitTy) kenvInsert punitId (.indc punitName #[uName] 1 0 0 false punitId 0 punitTy #[unitId] #[]) @@ -320,28 +320,28 @@ def ensurePreludeInKenvOf (maps : AddrMaps) : KBridgeM Unit := do let betaName := Name.mkStr .mkAnon "β" let fstName := Name.mkStr .mkAnon "fst" let sndName := Name.mkStr .mkAnon "snd" - let u0' : MKUniv := Ix.Tc.KUniv.mkParam 0 uName - let u1 : MKUniv := Ix.Tc.KUniv.mkParam 1 vName - let sortU := Ix.Tc.KExpr.mkSort u0' - let sortV := Ix.Tc.KExpr.mkSort u1 + let u0' : MKUniv := Ix.Kernel.KUniv.mkParam 0 uName + let u1 : MKUniv := Ix.Kernel.KUniv.mkParam 1 vName + let sortU := Ix.Kernel.KExpr.mkSort u0' + let sortV := Ix.Kernel.KExpr.mkSort u1 -- Lean stores `max 1 u v` LEFT-associated: max(max(1,u),v). Essential: -- after substitution the normalizing max collapses differently for the -- right-associated form (expr_utils.rs:1813-1821). - let max1uv := Ix.Tc.KUniv.mkMax - (Ix.Tc.KUniv.mkMax (Ix.Tc.KUniv.mkSucc Ix.Tc.KUniv.mkZero) u0') u1 - let pprodTy := Ix.Tc.KExpr.mkAll alphaName Lean.BinderInfo.default sortU - (Ix.Tc.KExpr.mkAll betaName Lean.BinderInfo.default sortV (Ix.Tc.KExpr.mkSort max1uv)) + let max1uv := Ix.Kernel.KUniv.mkMax + (Ix.Kernel.KUniv.mkMax (Ix.Kernel.KUniv.mkSucc Ix.Kernel.KUniv.mkZero) u0') u1 + let pprodTy := Ix.Kernel.KExpr.mkAll alphaName Lean.BinderInfo.default sortU + (Ix.Kernel.KExpr.mkAll betaName Lean.BinderInfo.default sortV (Ix.Kernel.KExpr.mkSort max1uv)) -- PProd.mk : {α : Sort u} → {β : Sort v} → α → β → PProd.{u,v} α β let mkName := Name.mkStr pprodName "mk" let mkId : MKId := ⟨maps.resolve mkName, mkName⟩ - let pprodApp := Ix.Tc.KExpr.mkApp - (Ix.Tc.KExpr.mkApp (Ix.Tc.KExpr.mkConst pprodId #[u0', u1]) - (Ix.Tc.KExpr.mkVar 3 Name.mkAnon)) - (Ix.Tc.KExpr.mkVar 2 Name.mkAnon) - let mkTy := Ix.Tc.KExpr.mkAll alphaName Lean.BinderInfo.implicit sortU - (Ix.Tc.KExpr.mkAll betaName Lean.BinderInfo.implicit sortV - (Ix.Tc.KExpr.mkAll fstName Lean.BinderInfo.default (Ix.Tc.KExpr.mkVar 1 Name.mkAnon) - (Ix.Tc.KExpr.mkAll sndName Lean.BinderInfo.default (Ix.Tc.KExpr.mkVar 1 Name.mkAnon) + let pprodApp := Ix.Kernel.KExpr.mkApp + (Ix.Kernel.KExpr.mkApp (Ix.Kernel.KExpr.mkConst pprodId #[u0', u1]) + (Ix.Kernel.KExpr.mkVar 3 Name.mkAnon)) + (Ix.Kernel.KExpr.mkVar 2 Name.mkAnon) + let mkTy := Ix.Kernel.KExpr.mkAll alphaName Lean.BinderInfo.implicit sortU + (Ix.Kernel.KExpr.mkAll betaName Lean.BinderInfo.implicit sortV + (Ix.Kernel.KExpr.mkAll fstName Lean.BinderInfo.default (Ix.Kernel.KExpr.mkVar 1 Name.mkAnon) + (Ix.Kernel.KExpr.mkAll sndName Lean.BinderInfo.default (Ix.Kernel.KExpr.mkVar 1 Name.mkAnon) pprodApp))) kenvInsert mkId (.ctor mkName #[uName, vName] false 2 pprodId 0 2 2 mkTy) kenvInsert pprodId @@ -451,41 +451,41 @@ partial def toKexprStatic (e : Expr) (fvarLevels : Std.HashMap Name Nat) match e with | .fvar fname _ => match fvarLevels.get? fname with - | some level => Ix.Tc.KExpr.mkVar (UInt64.ofNat (ctxDepth - level - 1)) .mkAnon - | none => Ix.Tc.KExpr.mkSort Ix.Tc.KUniv.mkZero - | .bvar idx _ => Ix.Tc.KExpr.mkVar (UInt64.ofNat idx) .mkAnon - | .sort lvl _ => Ix.Tc.KExpr.mkSort (leanLevelToKuniv lvl paramNames) + | some level => Ix.Kernel.KExpr.mkVar (UInt64.ofNat (ctxDepth - level - 1)) .mkAnon + | none => Ix.Kernel.KExpr.mkSort Ix.Kernel.KUniv.mkZero + | .bvar idx _ => Ix.Kernel.KExpr.mkVar (UInt64.ofNat idx) .mkAnon + | .sort lvl _ => Ix.Kernel.KExpr.mkSort (leanLevelToKuniv lvl paramNames) | .const cname us _ => let zid : MKId := ⟨maps.resolve cname, cname⟩ - Ix.Tc.KExpr.mkConst zid (us.map (leanLevelToKuniv · paramNames)) + Ix.Kernel.KExpr.mkConst zid (us.map (leanLevelToKuniv · paramNames)) | .app f a _ => - Ix.Tc.KExpr.mkApp (toKexprStatic f fvarLevels ctxDepth paramNames maps) + Ix.Kernel.KExpr.mkApp (toKexprStatic f fvarLevels ctxDepth paramNames maps) (toKexprStatic a fvarLevels ctxDepth paramNames maps) | .forallE binderName dom body bi _ => - Ix.Tc.KExpr.mkAll binderName bi + Ix.Kernel.KExpr.mkAll binderName bi (toKexprStatic dom fvarLevels ctxDepth paramNames maps) (toKexprStatic body fvarLevels (ctxDepth + 1) paramNames maps) | .lam binderName dom body bi _ => - Ix.Tc.KExpr.mkLam binderName bi + Ix.Kernel.KExpr.mkLam binderName bi (toKexprStatic dom fvarLevels ctxDepth paramNames maps) (toKexprStatic body fvarLevels (ctxDepth + 1) paramNames maps) | .letE binderName ty val body nd _ => - Ix.Tc.KExpr.mkLet binderName + Ix.Kernel.KExpr.mkLet binderName (toKexprStatic ty fvarLevels ctxDepth paramNames maps) (toKexprStatic val fvarLevels ctxDepth paramNames maps) (toKexprStatic body fvarLevels (ctxDepth + 1) paramNames maps) nd | .proj pname idx s _ => let zid : MKId := ⟨maps.resolve pname, pname⟩ - Ix.Tc.KExpr.mkPrj zid (UInt64.ofNat idx) + Ix.Kernel.KExpr.mkPrj zid (UInt64.ofNat idx) (toKexprStatic s fvarLevels ctxDepth paramNames maps) | .lit (.natVal n) _ => -- 8-byte u64 LE blob convention (see leanExprToKexprCached). - Ix.Tc.KExpr.mkNat n (Address.blake3 (UInt64.ofNat n).toLEBytes) + Ix.Kernel.KExpr.mkNat n (Address.blake3 (UInt64.ofNat n).toLEBytes) | .lit (.strVal s) _ => - Ix.Tc.KExpr.mkStr s (Address.blake3 s.toUTF8) + Ix.Kernel.KExpr.mkStr s (Address.blake3 s.toUTF8) | .mdata _ inner _ => toKexprStatic inner fvarLevels ctxDepth paramNames maps - | .mvar _ _ => Ix.Tc.KExpr.mkSort Ix.Tc.KUniv.mkZero + | .mvar _ _ => Ix.Kernel.KExpr.mkSort Ix.Kernel.KUniv.mkZero /-- `KExpr .meta → Ix.Expr` reconstructing FVars from de-Bruijn `Var`s: indices below `localDepth` stay BVars; above, level = @@ -683,7 +683,7 @@ def new (outerFvarCtx : Array LocalDecl) (paramNames : Array Name) (maps : AddrMaps) : KBridgeM TcScopeSt := do -- Fresh TC portions, persistent env (caches live in KEnv). modify fun kctx => { kctx with tcState := - { Ix.Tc.TcState.new kctx.tcState.env kctx.tcState.prims with + { Ix.Kernel.TcState.new kctx.tcState.env kctx.tcState.prims with inferOnly := true } } let mut fvarLevels : Std.HashMap Name Nat := {} for (decl, i) in outerFvarCtx.zipIdx do @@ -692,7 +692,7 @@ def new (outerFvarCtx : Array LocalDecl) (paramNames : Array Name) { fvarLevels, baseDepth := outerFvarCtx.size, paramNames, maps } for (decl, i) in outerFvarCtx.zipIdx do let kty := toKexprStatic decl.domain fvarLevels i paramNames maps - discard <| runTc (Ix.Tc.TcM.pushLocal kty) + discard <| runTc (Ix.Kernel.TcM.pushLocal kty) return scope /-- Push additional locals (e.g. minor-premise binders); balance with @@ -706,7 +706,7 @@ def pushLocals (scope : TcScopeSt) (decls : Array LocalDecl) fvarLevels := scope.fvarLevels.insert decl.fvarName (depth0 + i) } let kty := toKexprStatic decl.domain scope.fvarLevels (depth0 + i) scope.paramNames scope.maps - discard <| runTc (Ix.Tc.TcM.pushLocal kty) + discard <| runTc (Ix.Kernel.TcM.pushLocal kty) return { scope with extraLocals := scope.extraLocals + decls.size } /-- Mirrors Rust `pop_locals` (expr_utils.rs:2274). -/ @@ -714,7 +714,7 @@ def popLocals (scope : TcScopeSt) (decls : Array LocalDecl) : KBridgeM TcScopeSt := do let mut scope := scope for decl in decls.reverse do - discard <| runTc Ix.Tc.TcM.popLocal + discard <| runTc Ix.Kernel.TcM.popLocal scope := { scope with fvarLevels := scope.fvarLevels.erase decl.fvarName } return { scope with extraLocals := scope.extraLocals - decls.size } @@ -829,7 +829,7 @@ partial def getLevel (scope : TcScopeSt) (ty : Expr) : KBridgeM Level := do let mut faultedAddrs : Std.HashSet Address := {} let mut inferred? : Option MKExpr := none while inferred?.isNone do - match ← runTc (Ix.Tc.TcM.infer kexpr) with + match ← runTc (Ix.Kernel.TcM.infer kexpr) with | .ok e => inferred? := some e | .error (.unknownConst addr) => if !faultedAddrs.contains addr then @@ -847,7 +847,7 @@ partial def getLevel (scope : TcScopeSt) (ty : Expr) : KBridgeM Level := do s!"TcScope::get_level: tc.infer failed: {e}") let inferred := inferred?.get! - let ku ← match ← runTc (Ix.Tc.TcM.ensureSort inferred) with + let ku ← match ← runTc (Ix.Kernel.TcM.ensureSort inferred) with | .ok u => pure u | .error e => throw (.unsupportedExpr s!"TcScope::get_level: ensure_sort failed: {e}") @@ -865,7 +865,7 @@ def whnfLean (scope : TcScopeSt) (ty : Expr) : KBridgeM Expr := do let depth := scope.depth let kexpr := toKexprStatic ty scope.fvarLevels depth scope.paramNames scope.maps - let whnfed ← match ← runTc (Ix.Tc.TcM.whnf kexpr) with + let whnfed ← match ← runTc (Ix.Kernel.TcM.whnf kexpr) with | .ok k => pure k | .error _ => return ty let out := kexprToLean whnfed depth scope.fvarLevels 0 scope.paramNames @@ -883,7 +883,7 @@ def isDefEq (scope : TcScopeSt) (a b : Expr) : KBridgeM Bool := do let depth := scope.depth let ka := toKexprStatic a scope.fvarLevels depth scope.paramNames scope.maps let kb := toKexprStatic b scope.fvarLevels depth scope.paramNames scope.maps - match ← runTc (Ix.Tc.TcM.isDefEq ka kb) with + match ← runTc (Ix.Kernel.TcM.isDefEq ka kb) with | .ok r => return r | .error _ => return false @@ -896,7 +896,7 @@ def isDefEq (scope : TcScopeSt) (a b : Expr) : KBridgeM Bool := do def inferLean (scope : TcScopeSt) (e : Expr) : KBridgeM (Option Expr) := do let depth := scope.depth let ke := toKexprStatic e scope.fvarLevels depth scope.paramNames scope.maps - match ← runTc (Ix.Tc.TcM.infer ke) with + match ← runTc (Ix.Kernel.TcM.infer ke) with | .ok ty => return some (kexprToLean ty depth scope.fvarLevels 0 scope.paramNames) | .error _ => return none diff --git a/Ix/AuxGen/Nested.lean b/Ix/AuxGen/Nested.lean index eeb21a566..b4542ecfb 100644 --- a/Ix/AuxGen/Nested.lean +++ b/Ix/AuxGen/Nested.lean @@ -18,7 +18,7 @@ `AuxLayout {perm, sourceCtorCounts}` metadata. The kernel re-derives this order via blake3 `AUX_INDC_VIEW` / - `AUX_MARKER_VIEW` seed addresses (`Ix/Tc/Inductive.lean:canonicalAuxOrder`, + `AUX_MARKER_VIEW` seed addresses (`Ix/Kernel/Inductive.lean:canonicalAuxOrder`, `crates/kernel/src/inductive.rs:canonical_aux_order`) — those seed strings are the CONSUMER's reconstruction and must not appear here: the compile side orders purely by marker ctor + `sortConsts`. diff --git a/Ix/AuxGen/Recursor.lean b/Ix/AuxGen/Recursor.lean index 72e62ed9f..edff092e3 100644 --- a/Ix/AuxGen/Recursor.lean +++ b/Ix/AuxGen/Recursor.lean @@ -1606,12 +1606,12 @@ def computeIsLargeAndK (classes : Array FlatInfo) (nClasses nParams : Nat) -- Fresh TypeChecker over the persistent kenv (Rust -- `TypeChecker::new(&mut kctx.kenv)`). modify fun kctx => { kctx with - tcState := Ix.Tc.TcState.new kctx.tcState.env kctx.tcState.prims } + tcState := Ix.Kernel.TcState.new kctx.tcState.env kctx.tcState.prims } -- WHNF-reduced result sort level via the kernel. let resultKuniv ← - match ← runTc (Ix.Tc.TcM.runRec - (Ix.Tc.RecM.getResultSortLevel firstTyZ + match ← runTc (Ix.Kernel.TcM.runRec + (Ix.Kernel.RecM.getResultSortLevel firstTyZ (nParams + firstNIndices.toNat))) with | .ok u => pure u | .error e => @@ -1620,8 +1620,8 @@ def computeIsLargeAndK (classes : Array FlatInfo) (nClasses nParams : Nat) {classes[0]!.ind.cnst.name.pretty}: {e}") let isLarge ← - match ← runTc (Ix.Tc.TcM.runRec - (Ix.Tc.RecM.isLargeEliminator resultKuniv indInfos)) with + match ← runTc (Ix.Kernel.TcM.runRec + (Ix.Kernel.RecM.isLargeEliminator resultKuniv indInfos)) with | .ok b => pure b | .error e => throw (.invalidMutualBlock diff --git a/Ix/BenchConstants.lean b/Ix/BenchConstants.lean index cee6bc26a..434f499ad 100644 --- a/Ix/BenchConstants.lean +++ b/Ix/BenchConstants.lean @@ -1,7 +1,7 @@ /- The shared benchmark constant set: the single source of truth for which - constants every per-constant benchmark backend (aiur, zisk, sp1, ooc, - lean4lean) runs. Every backend runs this same set — spanning the cheap → + constants every per-constant benchmark backend (aiur, zisk, sp1, ooc) + runs. Every backend runs this same set — spanning the cheap → heavy cost range across the registry envs — so their numbers stay comparable per constant; the only per-backend carve-outs are the hard feasibility exclusions in `Ix.Cli.BenchCmd.benchExclusions`. diff --git a/Ix/Certified.lean b/Ix/Certified.lean new file mode 100644 index 000000000..05cb4f0ef --- /dev/null +++ b/Ix/Certified.lean @@ -0,0 +1,13 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Command +import Ix.Certified.ClaimCommand + +/-! Source and claim adapters for the explicit certified profile. +Their validation receipts connect authenticated Ixon inputs to the set model. +The production checker's general inference path and Aiur public acceptance +have separate refinement obligations. +-/ diff --git a/Ix/Certified/Audit.lean b/Ix/Certified/Audit.lean new file mode 100644 index 000000000..c5e47e3c9 --- /dev/null +++ b/Ix/Certified/Audit.lean @@ -0,0 +1,28 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.AuditSupport +import Ix.Certified.Bytes + +/-! +The serialized host adapter has its own audit. This is not the pure model's +import boundary or an Aiur compiler/AIR soundness theorem. Foreign hashing and +its output contract remain explicit runtime dependencies. +-/ + +open Lean Lean.Elab Command + +namespace Ix.Certified.Audit + +def roots : Array Lean.Name := #[ + `Ix.Certified.decodeObject?, `Ix.Certified.decodeNatural?, + `Ix.Certified.readSignature?, `Ix.Certified.prepare?, + `Ix.Certified.acceptsSerialized_prepared, + `Ix.Certified.accepted_serialized_has_model, + `Ix.Certified.no_serialized_proof_of_False] + +run_cmd AuditSupport.report "serialized" roots #[] + +end Ix.Certified.Audit diff --git a/Ix/Certified/AuditAll.lean b/Ix/Certified/AuditAll.lean new file mode 100644 index 000000000..a6f8b020c --- /dev/null +++ b/Ix/Certified/AuditAll.lean @@ -0,0 +1,25 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Audit +import Ix.Certified.TcAudit +import Ix.Certified.SourceAudit +import Ix.Certified.ClaimAudit +import Ix.Certified.ModeledAudit + +/-! The union of the five historical adapter boundaries, without duplicate +root, premise or worker reports. Each component remains independently audited. -/ + +namespace Ix.Certified.AuditAll + +def roots : Array Lean.Name := AuditSupport.distinct + (Audit.roots ++ TcAudit.roots ++ SourceAudit.roots ++ ClaimAudit.roots ++ ModeledAudit.roots) + +def premises : Array Lean.Name := AuditSupport.distinct + (TcAudit.premises ++ SourceAudit.premises ++ ClaimAudit.premises ++ ModeledAudit.premises) + +run_cmd AuditSupport.report "Certified host adapters" roots premises + +end Ix.Certified.AuditAll diff --git a/Ix/Certified/AuditSupport.lean b/Ix/Certified/AuditSupport.lean new file mode 100644 index 000000000..f27c6ff67 --- /dev/null +++ b/Ix/Certified/AuditSupport.lean @@ -0,0 +1,162 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Lean + +/-! Checked declaration traversal for the certified host adapters. Runtime +workers and foreign interfaces are inventoried separately from proof axioms. -/ + +open Lean Lean.Elab Command + +namespace Ix.Certified.AuditSupport + +private def constants (info : ConstantInfo) : Array Name := + info.type.getUsedConstants ++ match info with + | .thmInfo value => value.value.getUsedConstants + | .defnInfo value => value.value.getUsedConstants + | .opaqueInfo value => value.value.getUsedConstants + | .inductInfo value => value.ctors.toArray + | _ => #[] + +private partial def closure (env : Environment) (runtime : Bool) (pending : List Name) + (seen : NameSet := {}) : NameSet := + match pending with + | [] => seen + | name :: rest => + if seen.contains name then closure env runtime rest seen + else match env.checked.get.find? name with + | none => closure env runtime rest (seen.insert name) + | some info => + let extras := if runtime then Id.run do + let mut names := #[] + let worker := Lean.Compiler.mkUnsafeRecName name + if (env.checked.get.find? worker).isSome then names := names.push worker + if let some other := Lean.Compiler.getImplementedBy? env name then + names := names.push other + if let some other := (Lean.Compiler.CSimp.ext.getState env).map.find? name then + names := names.push other.toDeclName + return names + else #[] + closure env runtime ((constants info ++ extras).toList ++ rest) (seen.insert name) + +private def projectConstant (env : Environment) (name : Name) : Bool := + match env.getModuleIdxFor? name with + | none => false + | some idx => + let moduleName := env.allImportedModuleNames[idx.toNat]! + (`Ix).isPrefixOf moduleName || (`Blake3).isPrefixOf moduleName + +/-- Include constructor types and full checked bodies instead of relying on +imported axiom summaries. Both added and removed assumptions require review. -/ +def checkAxioms (env : Environment) (root : Name) (expected : Array Name) : + CommandElabM (Array Name) := do + let mut actual := #[] + for name in closure env false [root] do + let some info := env.checked.get.find? name + | throwError "certified adapter has an unavailable checked dependency: {name}" + if info.isAxiom then actual := actual.push name + let sorted := actual.qsort Name.lt + unless sorted == expected.qsort Name.lt do + throwError "certified axiom boundary changed for {root}: expected {expected.qsort Name.lt}, actual {sorted}" + return sorted + +private def expectedAxioms (root : Name) : Array Name := + if #[`Ix.Certified.readLevel_value, `Ix.Certified.treeLeaves_join].contains root then + #[``propext] + else if #[`Ix.Certified.readSignature?, `Ix.Certified.constantBytes?, + `Ix.Certified.resolveReference_iff, `Ix.Certified.readExpr_sound, + `Ix.Certified.ExprReading.unique, `Ix.Certified.readExpr_fuel_independent, + `Ix.Certified.readBlock_sourceHeader, `Ix.Certified.readStore_sourceHeader, + `Ix.Certified.readSignature_sound, `Ix.Theory.Certificate.Modeled.witness?, + `Ix.Theory.Certificate.sourceGroup?, `Ix.Theory.Certificate.proofWitness?, + `Ix.Certified.modelCandidate?, `Ix.Certified.modelCandidates?].contains root then + #[``propext, ``Quot.sound] + else #[``propext, ``Classical.choice, ``Quot.sound] + +def distinct (names : Array Name) : Array Name := + names.foldl (fun result name => if result.contains name then result else result.push name) #[] + +/-- Freeze theorem types, premise definitions, and transitive execution +dependencies. This does not establish correctness of native execution. -/ +def report (label : String) (roots premises : Array Name) : CommandElabM Unit := do + let env ← getEnv + for root in roots do + let some info := env.checked.get.find? root + | throwError "certified audit: missing root {root}" + let axioms ← checkAxioms env root (expectedAxioms root) + liftTermElabM do logInfo m!"ROOT {root}\n{← Meta.ppExpr info.type}\nAXIOMS {axioms}" + for name in premises do + let some info := env.checked.get.find? name + | throwError "certified audit: missing premise {name}" + liftTermElabM do + logInfo m!"PREMISE {name}\n{← Meta.ppExpr info.type}" + if let .defnInfo value := info then logInfo m!"DEFINITION\n{← Meta.ppExpr value.value}" + let logical := closure env false roots.toList + let reachable := (closure env true roots.toList).toList.mergeSort (fun a b => a.toString < b.toString) + let mut workers : Array Name := #[] + let mut externs : Array Name := #[] + let mut replacements : Array (Name × Name) := #[] + for name in reachable do + let some info := env.checked.get.find? name + | throwError "certified adapter has an unavailable checked runtime dependency: {name}" + if projectConstant env name then + if let some parent := Lean.Compiler.isUnsafeRecName? name then + match env.checked.get.find? parent with + | some (.defnInfo original) => + unless original.safety == .safe do + throwError "certified recursion worker source is not safe: {name}" + | _ => throwError "certified recursion worker has no safe source definition: {name}" + workers := workers.push name + if Lean.isExtern env name then externs := externs.push name + if let some other := Lean.Compiler.getImplementedBy? env name then + replacements := replacements.push (name, other) + if let some other := (Lean.Compiler.CSimp.ext.getState env).map.find? name then + replacements := replacements.push (name, other.toDeclName) + if let .defnInfo definition := info then + unless definition.safety == .safe || (Lean.Compiler.isUnsafeRecName? name).isSome do + throwError "certified adapter has an unreviewed unsafe runtime: {name}" + if let .opaqueInfo definition := info then + if definition.isUnsafe then throwError "certified adapter has an unsafe opaque runtime: {name}" + if Lean.Elab.ComputedFields.computedFieldAttr.hasTag env name then + throwError "certified adapter has an unreviewed computed field: {name}" + unless externs == #[`Blake3.Rust.hasherFinalize, `Blake3.Rust.hasherInit, + `Blake3.Rust.hasherInitDeriveKey, `Blake3.Rust.hasherInitKeyed, + `Blake3.Rust.hasherUpdate] do + throwError "certified runtime extern inventory changed: {externs}" + unless replacements.isEmpty do + throwError "certified runtime replacements changed: {replacements}" + for name in externs do + let some info := env.checked.get.find? name + | throwError "certified runtime extern is missing: {name}" + liftTermElabM do logInfo m!"RUNTIME EXTERN {name}\n{← Meta.ppExpr info.type}" + for name in workers.qsort Name.lt do + let some (.defnInfo value) := env.checked.get.find? name + | throwError "certified recursion worker is missing: {name}" + liftTermElabM do + logInfo m!"RECURSION WORKER {name}\n{← Meta.ppExpr value.type}\nIMPLEMENTATION\n{← Meta.ppExpr value.value}" + logInfo m!"{label} ROOTS {roots.size}; LOGICAL DECLARATIONS {logical.size}; WITH RUNTIME {reachable.length}" + logInfo m!"RUNTIME EXTERNS {externs}\nRECURSION WORKERS {workers.size}" + logInfo "All inventoried recursion workers have safe logical sources; no partial opaque source or executable replacement is permitted. Runtime diagnostics cover Ix and Blake3 modules, including private constants. Lean/Std execution and the BLAKE3 foreign interface remain external runtime boundaries." + logInfo "These host-adapter roots establish semantic claims under the enforced profile and explicit model premises. They do not establish full production-checker refinement or Aiur compiler/AIR soundness." + +private inductive ConstructorAuditFixture where + | plain + | withProof (proof : propext (Iff.refl True) = rfl) + +run_cmd do + let _ ← checkAxioms (← getEnv) ``ConstructorAuditFixture.plain #[``propext] + let _ ← checkAxioms (← getEnv) ``Eq.refl #[] + +/-- error: certified axiom boundary changed for Eq.refl: expected [propext], actual [] -/ +#guard_msgs in +run_cmd do + let _ ← checkAxioms (← getEnv) ``Eq.refl #[``propext] + +/-- error: certified axiom boundary changed for propext: expected [], actual [propext] -/ +#guard_msgs in +run_cmd do + let _ ← checkAxioms (← getEnv) ``propext #[] + +end Ix.Certified.AuditSupport diff --git a/Ix/Certified/Bytes.lean b/Ix/Certified/Bytes.lean new file mode 100644 index 000000000..0aeda099c --- /dev/null +++ b/Ix/Certified/Bytes.lean @@ -0,0 +1,152 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Ixon + +namespace Ix.Certified + +open Ix.Theory Ix.Theory.Certified +open Ix.Theory.Model Ix.Theory.Model.SetTheory + +universe v + +/-- These external constant addresses are part of the selected public profile, +not fields supplied by an annotation or typing witness. -/ +structure Profile where + falseType : Address + falseElim : Address + natType : Option Address := none + deriving DecidableEq + +abbrev ConstantBlobs := List (Address × ByteArray) + +abbrev DecodedObject (address : Address) (bytes : ByteArray) := + { value : Ixon.Constant // + address.hash.size = 32 ∧ Address.blake3 bytes = address ∧ + Ixon.runGetExact Ixon.getConstant bytes = .ok value ∧ Ixon.serConstant value = bytes } + +/-- Check canonical re-encoding after a single parse. Keeping the parser's +result as a separate argument makes the cache reflection equation explicit. -/ +def canonicalObject? (address : Address) (bytes : ByteArray) + (auth : address.hash.size = 32 ∧ Address.blake3 bytes = address) + (result : Except String Ixon.Constant) + (parsed : Ixon.runGetExact Ixon.getConstant bytes = result) : Option (DecodedObject address bytes) := + match result, parsed with + | .error _, _ => none + | .ok source, hp => + if hc : Ixon.serConstant source = bytes then some ⟨source, auth.1, auth.2, hp, hc⟩ else none + +/-- The accepted byte domain consists of complete canonical Ixon constants. +The returned proof records authentication, exact consumption, and re-encoding +of the same bytes. It grants no typing or semantic acceptance by itself. -/ +def decodeObject? (address : Address) (bytes : ByteArray) : Option (DecodedObject address bytes) := + if h : address.hash.size = 32 ∧ Address.blake3 bytes = address then + canonicalObject? address bytes h (Ixon.runGetExact Ixon.getConstant bytes) rfl + else none + +def decodeObjects? (blobs : ConstantBlobs) : Option Objects := + blobs.mapM fun (address, bytes) => do + let source ← decodeObject? address bytes + return (address, source.val) + +/-- Natural blobs use Ix's unsigned little-endian encoding, with authentication +and canonical re-encoding checked before any literal is read. -/ +def decodeNatural? (address : Address) (bytes : ByteArray) : + Option { value : Nat // + address.hash.size = 32 ∧ Address.blake3 bytes = address ∧ + Nat.fromBytesLE bytes.data = value ∧ ByteArray.mk value.toBytesLE = bytes } := + if h : address.hash.size = 32 ∧ Address.blake3 bytes = address then + let value := Nat.fromBytesLE bytes.data + if hc : ByteArray.mk value.toBytesLE = bytes then some ⟨value, h.1, h.2, rfl, hc⟩ else none + else none + +def decodeNaturals? (blobs : ConstantBlobs) : Option Naturals := + blobs.mapM fun (address, bytes) => do + let value ← decodeNatural? address bytes + return (address, value.val) + +def readSignature? (profile : Profile) (objects : Objects) : + Option (PrimitiveSignature Address) := do + let falseType ← resolveReference? objects profile.falseType + let falseElim ← resolveReference? objects profile.falseElim + let natType ← match profile.natType with + | none => some none + | some address => (resolveReference? objects address).map some + if h : falseType ≠ falseElim then + return ⟨falseType, falseElim, h, natType⟩ + else none + +structure PreparedInput where + signature : PrimitiveSignature Address + input : ProofInput Address + +def prepare? (fuel : Nat) (profile : Profile) (target : Address) (blobs : ConstantBlobs) + (literalBlobs : ConstantBlobs := []) : Option PreparedInput := do + if !((blobs ++ literalBlobs).map Prod.fst).Nodup then none else do + let naturals ← decodeNaturals? literalBlobs + let objects ← decodeObjects? blobs + let signature ← readSignature? profile objects + let input ← readProofInput? fuel objects target naturals + return ⟨signature, input⟩ + +/-- The actual serialized-input gate invokes the mathematical validator after +authentication and decoding. The byte/statement preservation and compiled +execution theorems remain separate obligations. -/ +def acceptsSerialized (fuel : Nat) (profile : Profile) (target : Address) + (blobs : ConstantBlobs) (witness : ProofWitness Address) + (literalBlobs : ConstantBlobs := []) : Bool := + match prepare? fuel profile target blobs literalBlobs with + | none => false + | some prepared => + acceptsCertified.{0,v} fuel prepared.signature prepared.input witness + +theorem acceptsSerialized_prepared {fuel : Nat} {profile : Profile} {target : Address} + {blobs literalBlobs : ConstantBlobs} {witness : ProofWitness Address} + (h : acceptsSerialized.{v} fuel profile target blobs witness literalBlobs = true) : + ∃ prepared, prepare? fuel profile target blobs literalBlobs = some prepared ∧ + acceptsCertified.{0,v} fuel prepared.signature prepared.input witness = true := by + unfold acceptsSerialized at h + cases hp : prepare? fuel profile target blobs literalBlobs with + | none => simp [hp] at h + | some prepared => exact ⟨prepared, rfl, by simpa only [hp] using h⟩ + +/-- A successful serialized host check constructs the model of its decoded +input. Source meaning, claim binding, and authenticated backend execution +require their respective refinement theorems. -/ +theorem accepted_serialized_has_model {fuel : Nat} {profile : Profile} {target : Address} + {blobs literalBlobs : ConstantBlobs} {witness : ProofWitness Address} + (h : acceptsSerialized.{v} fuel profile target blobs witness literalBlobs = true) + (V : Type v) [SetTheory V] (levels : List Nat) (env : Nat → V) : + ∃ prepared, prepare? fuel profile target blobs literalBlobs = some prepared ∧ + ∃ result : CheckedProof.{0,v} prepared.signature prepared.input, + checkProofCertified fuel prepared.signature prepared.input witness = some result ∧ + ∃ constants : Assignment Address V, + prepared.signature.Compatible result.environment.entries constants ∧ + WellDenoted constants levels env result.proof.val ∧ + WellDenoted constants levels env result.proposition.val ∧ + interp constants levels env result.proof.val ∈ˢ + interp constants levels env result.proposition.val := by + obtain ⟨prepared, hp, ha⟩ := acceptsSerialized_prepared h + exact ⟨prepared, hp, accepted_has_model ha V levels env⟩ + +/-- This checks the decoded target statement against the profile's exact +empty proposition. It performs no theorem or semantic witness search. -/ +def isFalseStatement (fuel : Nat) (profile : Profile) (target : Address) + (blobs : ConstantBlobs) (literalBlobs : ConstantBlobs := []) : Bool := + match prepare? fuel profile target blobs literalBlobs with + | none => false + | some prepared => decide (prepared.input.proposition = prepared.signature.falseExpr) + +theorem no_serialized_proof_of_False {fuel : Nat} {profile : Profile} {target : Address} + {blobs literalBlobs : ConstantBlobs} {witness : ProofWitness Address} + (V : Type v) [SetTheory V] + (hf : isFalseStatement fuel profile target blobs literalBlobs = true) + (h : acceptsSerialized.{v} fuel profile target blobs witness literalBlobs = true) : False := by + obtain ⟨prepared, hp, ha⟩ := acceptsSerialized_prepared h + have he : prepared.input.proposition = prepared.signature.falseExpr := by + simpa only [isFalseStatement, hp, decide_eq_true_eq] using hf + exact no_proof_of_False V he ha + +end Ix.Certified diff --git a/Ix/Certified/ClaimAccept.lean b/Ix/Certified/ClaimAccept.lean new file mode 100644 index 000000000..7e5203f5d --- /dev/null +++ b/Ix/Certified/ClaimAccept.lean @@ -0,0 +1,164 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Reveal + +namespace Ix.Certified + +open Ix.Theory Ix.Theory.Certified Ix.Theory.Model Ix.Theory.Model.SetTheory + +universe v + +def LogicalKind : Ix.Claim → Prop + | .check .. | .checkEnv .. | .catalog .. => True + | _ => False + +theorem LogicalReceipt.kind (receipt : LogicalReceipt.{v} source fuel envelope witness) : + LogicalKind envelope.claim := by + have h := receipt.checked.content.meaning + cases hc : envelope.claim <;> simp_all [LogicalKind] + +/-- Meaning of the exact source declarations selected by the public claim. +The annotated readings remain tied to their original type fields and tables. -/ +def SubjectsInterpreted (receipt : LogicalReceipt.{v} source fuel envelope witness) + (V : Type v) [SetTheory V] (constants : Assignment Address V) : Prop := + ∀ ref ∈ receipt.checked.subjects, ∃ entry, + SourceDeclarationReading source receipt.snapshot.decodedObjects receipt.snapshot.decodedNaturals ref entry ∧ + ∀ levels, levels.length = entry.universes → ∀ env, + WellDenoted constants levels env entry.type ∧ + constants ref levels ∈ˢ interp constants levels env entry.type + +def LogicalMeaning (source : Ixon.Env) (fuel : Nat) (envelope : Envelope) : Prop := + ∃ witness : LogicalWitness, ∃ receipt : LogicalReceipt.{v} source fuel envelope witness, + SignatureReading envelope.profile receipt.snapshot.decodedObjects receipt.checked.signature ∧ + (∀ ref ∈ receipt.checked.frontierRefs, ∃ entry, + receipt.checked.batch.receipt.frontier.interface.entries ref = some entry ∧ + SourceDeclarationReading source receipt.snapshot.decodedObjects receipt.snapshot.decodedNaturals ref entry) ∧ + (∀ ref ∈ receipt.checked.axiomRefs, ∃ entry, + Standard.EntrySource receipt.checked.store ref entry ∨ Quotient.EntrySource receipt.checked.store ref entry) ∧ + (∀ (V : Type v) [SetTheory V] (constants : Assignment Address V), + receipt.checked.signature.Compatible receipt.checked.batch.receipt.frontier.interface.entries constants → + ∃ constants' : Assignment Address V, + receipt.checked.signature.Compatible receipt.checked.batch.receipt.checked.result.entries constants' ∧ + Assignment.AgreesOn receipt.checked.batch.receipt.frontier.interface.entries constants constants' ∧ + SubjectsInterpreted receipt V constants') ∧ + (claimFrontier envelope.claim = none → ∀ (V : Type v) [SetTheory V], + ∃ constants : Assignment Address V, + receipt.checked.signature.Compatible receipt.checked.batch.receipt.checked.result.entries constants ∧ + SubjectsInterpreted receipt V constants) + +theorem LogicalReceipt.meaning (receipt : LogicalReceipt.{v} source fuel envelope witness) : + LogicalMeaning.{v} source fuel envelope := + ⟨witness, receipt, readSignature_sound receipt.checked.signatureReading, + fun _ h => receipt.checked.original_frontier h, + fun _ h => receipt.checked.logical_policy h, + fun V _ constants hM => receipt.subject_meaning V constants hM, + fun closed V _ => receipt.closed_subject_meaning closed V⟩ + +/-- One contract for the versioned claim protocol. Structural membership and +revelation remain structural. Evaluation is excluded until a separate +execution/result interpretation has been proved and bound to the profile. -/ +def SemanticClaimMeaning (source : Ixon.Env) (fuel : Nat) (envelope : Envelope) : Prop := + envelope.protocol = Protocol.current ∧ + match envelope.claim with + | .check .. | .checkEnv .. | .catalog .. => LogicalMeaning.{v} source fuel envelope + | .contains root target => envelope.logicalAxioms = none ∧ TreeMembership root target + | .reveal commitment info => envelope.logicalAxioms = none ∧ RevealMeaning source commitment info + | .eval .. => False + +inductive ClaimWitness where + | logical (witness : LogicalWitness) + | contains (tree : ByteArray) + | reveal (witness : RevealWitness) + +inductive ClaimAction (source : Ixon.Env) (fuel : Nat) (envelope : Envelope) where + | logical {witness : LogicalWitness} (receipt : LogicalReceipt.{v} source fuel envelope witness) + | contains {root target : Address} {bytes : ByteArray} + (claim : envelope.claim = .contains root target) + (noAxioms : envelope.logicalAxioms = none) (opening : TreeOpening fuel root bytes) + (member : target ∈ treeLeaves opening.tree) + | reveal {commitment : Address} {info : Ix.RevealConstantInfo} {witness : RevealWitness} + (claim : envelope.claim = .reveal commitment info) + (noAxioms : envelope.logicalAxioms = none) (receipt : RevealReceipt source commitment info witness) + +structure ClaimReceipt (source : Ixon.Env) (fuel : Nat) (address : Address) (bytes : ByteArray) where + reading : EnvelopeReading address bytes + action : ClaimAction.{v} source fuel reading.envelope + +def checkClaimAction? (fuel : Nat) (source : Ixon.Env) (envelope : Envelope) (witness : ClaimWitness) : + Read source (ClaimAction.{v} source fuel envelope) := + match hc : envelope.claim, witness with + | .check .., .logical witness | .checkEnv .., .logical witness | .catalog .., .logical witness => do + let receipt ← checkLogicalSource?.{v} fuel source envelope witness + return .logical receipt + | .contains root target, .contains bytes => + if ha : envelope.logicalAxioms = none then + match readTree? fuel root bytes with + | none => failure + | some opening => + if hm : (treeLeaves opening.tree).contains target = true then + pure (.contains hc ha opening (List.contains_iff_mem.mp hm)) else failure + else failure + | .reveal commitment info, .reveal witness => + if ha : envelope.logicalAxioms = none then do + let receipt ← checkReveal? source commitment info witness + return .reveal hc ha receipt + else failure + | _, _ => failure + +/-- Public source acceptance starts with exact authenticated claim bytes. +Protocol and policy versions are checked before any dispatcher branch. -/ +def checkClaimBytes? (fuel : Nat) (source : Ixon.Env) (address : Address) (bytes : ByteArray) + (witness : ClaimWitness) : Read source (ClaimReceipt.{v} source fuel address bytes) := + match readEnvelope? fuel address bytes with + | none => failure + | some reading => do + let action ← checkClaimAction?.{v} fuel source reading.envelope witness + return ⟨reading, action⟩ + +def acceptsClaimBytes (fuel : Nat) (source : Ixon.Env) (address : Address) (bytes : ByteArray) + (witness : ClaimWitness) : Bool := + (checkClaimBytes?.{v} fuel source address bytes witness {}).isSome + +theorem ClaimReceipt.meaning (receipt : ClaimReceipt.{v} source fuel address bytes) : + SemanticClaimMeaning.{v} source fuel receipt.reading.envelope := by + refine ⟨receipt.reading.supported, ?_⟩ + cases receipt.action with + | logical logical => + have kind := logical.kind + have meaning := logical.meaning + cases hc : receipt.reading.envelope.claim <;> simp_all [LogicalKind] + | contains claim noAxioms opening member => + rw [claim] + exact ⟨noAxioms, opening.membership member⟩ + | reveal claim noAxioms revealed => + rw [claim] + exact ⟨noAxioms, revealed.meaning⟩ + +/-- Successful source checking yields the meaning of the exact authenticated +public envelope, including profile, statement, frontier and logical-use root. -/ +theorem accepted_claim_meaning {fuel : Nat} {source : Ixon.Env} {address : Address} {bytes : ByteArray} + {witness : ClaimWitness} (h : acceptsClaimBytes.{v} fuel source address bytes witness = true) : + ∃ receipt : ClaimReceipt.{v} source fuel address bytes, ∃ cache, + checkClaimBytes? fuel source address bytes witness {} = some (receipt, cache) ∧ + Address.blake3 bytes = address ∧ envelopeBytes receipt.reading.envelope = bytes ∧ + SemanticClaimMeaning.{v} source fuel receipt.reading.envelope := by + unfold acceptsClaimBytes at h + obtain ⟨⟨receipt, cache⟩, hc⟩ := Option.isSome_iff_exists.mp h + exact ⟨receipt, cache, hc, receipt.reading.authenticated, receipt.reading.canonical, receipt.meaning⟩ + +theorem evaluation_not_semantic {source : Ixon.Env} {fuel : Nat} {envelope : Envelope} + {input output : Address} {frontier : Option Address} + (h : envelope.claim = .eval input output frontier) : ¬ SemanticClaimMeaning.{v} source fuel envelope := by + simp [SemanticClaimMeaning, h] + +theorem membership_not_leaf {fuel : Nat} {root target : Address} {bytes : Option ByteArray} : + readSubjectView? fuel (.contains root target) bytes = none := by cases bytes <;> rfl + +theorem revelation_not_leaf {fuel : Nat} {commitment : Address} {info : Ix.RevealConstantInfo} + {bytes : Option ByteArray} : readSubjectView? fuel (.reveal commitment info) bytes = none := by + cases bytes <;> rfl + +end Ix.Certified diff --git a/Ix/Certified/ClaimAudit.lean b/Ix/Certified/ClaimAudit.lean new file mode 100644 index 000000000..23b9676b7 --- /dev/null +++ b/Ix/Certified/ClaimAudit.lean @@ -0,0 +1,49 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.AuditSupport +import Ix.Certified.ClaimCommand + +/-! Trust, runtime and premise inventory of the versioned semantic claim +checker, source command and actual TcM acceptance boundary. -/ + +open Lean Lean.Elab Command + +namespace Ix.Certified.ClaimAudit + +def roots : Array Lean.Name := #[ + `Ix.Certified.TreeOpening.membership, `Ix.Certified.TreeOpening.unique, + `Ix.Certified.treeLeaves_join, `Ix.Certified.EnvelopeReading.unique, + `Ix.Certified.readEnvelope?, `Ix.Certified.readTree?, + `Ix.Certified.LogicalChecking.original_subject, `Ix.Certified.LogicalChecking.original_frontier, + `Ix.Certified.LogicalChecking.logical_policy, `Ix.Certified.LogicalChecking.closed_frontier, + `Ix.Certified.LogicalReceipt.subject_meaning, `Ix.Certified.LogicalReceipt.closed_subject_meaning, + `Ix.Certified.LogicalReceipt.no_False, + `Ix.Certified.RevealReceipt.meaning, `Ix.Certified.ClaimReceipt.meaning, + `Ix.Certified.accepted_claim_meaning, `Ix.Certified.evaluation_not_semantic, + `Ix.Certified.membership_not_leaf, `Ix.Certified.revelation_not_leaf, + `Ix.Kernel.TcM.checkClaimCertified_success, + `Ix.Kernel.certifiedClaimStep_failure, `Ix.Kernel.certifiedClaimStep_success, + `Ix.Kernel.accepted_tc_claim_meaning, + `Ix.Certified.ClaimCommand.readRequest, + `Ix.Certified.ClaimCommand.run_success, `Ix.Certified.ClaimCommand.run_meaning] + +def premises : Array Lean.Name := #[ + `Ix.Certified.Protocol.current, `Ix.Certified.envelopeMagic, + `Ix.Certified.putEnvelope, `Ix.Certified.getEnvelope, + `Ix.Certified.EnvelopeReading.mk, `Ix.Certified.TreeOpening.mk, + `Ix.Certified.OptionalTreeOpening.mk, `Ix.Certified.SubjectView.mk, + `Ix.Certified.PreparedLeaf.mk, `Ix.Certified.ContentView.mk, + `Ix.Certified.ownedReferences, `Ix.Certified.claimFrontier, + `Ix.Certified.LogicalChecking.mk, `Ix.Certified.LogicalReceipt.mk, + `Ix.Certified.SubjectsInterpreted, `Ix.Certified.LogicalMeaning, + `Ix.Certified.RevealMatches, `Ix.Certified.ConstructorMatches, + `Ix.Certified.ConstructorsMatch, `Ix.Certified.MutMatches, + `Ix.Certified.ComponentsMatch, `Ix.Certified.RulesMatch, + `Ix.Certified.RevealMeaning, `Ix.Certified.SemanticClaimMeaning] + +run_cmd AuditSupport.report "claim meaning" roots premises + +end Ix.Certified.ClaimAudit diff --git a/Ix/Certified/ClaimCheck.lean b/Ix/Certified/ClaimCheck.lean new file mode 100644 index 000000000..7a8b8cb43 --- /dev/null +++ b/Ix/Certified/ClaimCheck.lean @@ -0,0 +1,104 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.ClaimInput + +namespace Ix.Certified + +open Ix.Theory Ix.Theory.Certified Ix.Theory.Model Ix.Theory.Model.SetTheory + +universe v + +structure LogicalWitness where + selection : InputSelection + leaves : List LeafWitness + subjects : Option ByteArray + members : Option ByteArray + frontierTree : Option ByteArray + frontier : List (FrontierWitness Address) + axiomTree : Option ByteArray + +structure LogicalChecking {source : Ixon.Env} (snapshot : SourceSnapshot source) + (fuel : Nat) (envelope : Envelope) (witness : LogicalWitness) where + uniqueSource : ((snapshot.blobs ++ snapshot.literalBlobs).map Prod.fst).Nodup + signature : PrimitiveSignature Address + signatureReading : readSignature? envelope.profile snapshot.decodedObjects = some signature + store : Store Address + storeReading : readStore? fuel snapshot.decodedObjects snapshot.decodedNaturals = some store + leaves : List (LeafInput fuel snapshot.decodedObjects) + leavesReading : witness.leaves.mapM (readLeafInput? fuel snapshot.decodedObjects) = some leaves + content : ContentView fuel envelope.claim witness.subjects witness.members leaves + subjects : List (ConstRef Address) + subjectsReading : content.addresses.flatMapM (subjectReferences? snapshot.decodedObjects) = some subjects + frontier : OptionalTreeOpening fuel (claimFrontier envelope.claim) witness.frontierTree + frontierRefs : List (ConstRef Address) + frontierReading : frontier.leaves.flatMapM (subjectReferences? snapshot.decodedObjects) = some frontierRefs + batch : CheckedBatch.{0,v} fuel signature store (leaves.map (fun leaf => leaf.prepared.node signature)) + batchChecking : checkBatch? fuel signature store witness.frontier + (leaves.map (fun leaf => leaf.prepared.node signature)) = some batch + exactSubjects : ∀ ref, ref ∈ nodeSubjects batch.nodes ↔ ref ∈ ownedReferences signature subjects + exactFrontier : ∀ ref, ref ∈ batch.receipt.frontier.refs ↔ ref ∈ frontierRefs + axioms : OptionalTreeOpening fuel envelope.logicalAxioms witness.axiomTree + axiomRefs : List (ConstRef Address) + axiomsReading : axioms.leaves.flatMapM (subjectReferences? snapshot.decodedObjects) = some axiomRefs + exactAxioms : ∀ ref, ref ∈ batch.logicalUses ↔ ref ∈ axiomRefs + +def checkLogicalSnapshot? {source : Ixon.Env} (snapshot : SourceSnapshot source) + (fuel : Nat) (envelope : Envelope) (witness : LogicalWitness) : + Option (LogicalChecking.{v} snapshot fuel envelope witness) := + if hu : ((snapshot.blobs ++ snapshot.literalBlobs).map Prod.fst).Nodup then + match hs : readSignature? envelope.profile snapshot.decodedObjects with + | none => none + | some signature => + match ht : readStore? fuel snapshot.decodedObjects snapshot.decodedNaturals with + | none => none + | some store => + match hl : witness.leaves.mapM (readLeafInput? fuel snapshot.decodedObjects) with + | none => none + | some leaves => do + let content ← readContentView? fuel envelope.claim witness.subjects witness.members leaves + match htargets : content.addresses.flatMapM (subjectReferences? snapshot.decodedObjects) with + | none => none + | some subjects => do + let frontier ← readOptionalTree? fuel (claimFrontier envelope.claim) witness.frontierTree + match hfrontier : frontier.leaves.flatMapM (subjectReferences? snapshot.decodedObjects) with + | none => none + | some frontierRefs => + match hc : checkBatch?.{0,v} fuel signature store witness.frontier + (leaves.map (fun leaf => leaf.prepared.node signature)) with + | none => none + | some batch => + if hsubjects : sameMembers (nodeSubjects batch.nodes) (ownedReferences signature subjects) = true then + if hf : sameMembers batch.receipt.frontier.refs frontierRefs = true then do + let axioms ← readOptionalTree? fuel envelope.logicalAxioms witness.axiomTree + match haxioms : axioms.leaves.flatMapM (subjectReferences? snapshot.decodedObjects) with + | none => none + | some axiomRefs => + if ha : sameMembers batch.logicalUses axiomRefs = true then + some ⟨hu, signature, hs, store, ht, leaves, hl, content, subjects, htargets, + frontier, frontierRefs, hfrontier, batch, hc, sameMembers_iff.mp hsubjects, + sameMembers_iff.mp hf, axioms, axiomRefs, haxioms, sameMembers_iff.mp ha⟩ + else none + else none + else none + else none + +structure LogicalReceipt (source : Ixon.Env) (fuel : Nat) (envelope : Envelope) (witness : LogicalWitness) where + snapshot : SourceSnapshot source + checked : LogicalChecking.{v} snapshot fuel envelope witness + supported : envelope.protocol = Protocol.current + +/-- Every cache hit revalidates the actual complete claim. Only authenticated +data is reused; claim, policy, frontier and model checks run on every request. -/ +def checkLogicalSource? (fuel : Nat) (source : Ixon.Env) (envelope : Envelope) (witness : LogicalWitness) : + Read source (LogicalReceipt.{v} source fuel envelope witness) := + if hp : envelope.protocol = Protocol.current then do + let snapshot ← readSnapshot? fuel source witness.selection + match checkLogicalSnapshot?.{v} snapshot fuel envelope witness with + | none => failure + | some checked => return ⟨snapshot, checked, hp⟩ + else failure + +end Ix.Certified diff --git a/Ix/Certified/ClaimCommand.lean b/Ix/Certified/ClaimCommand.lean new file mode 100644 index 000000000..659c87de0 --- /dev/null +++ b/Ix/Certified/ClaimCommand.lean @@ -0,0 +1,117 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.ClaimSuggest +import Ix.Kernel.CertifiedClaims + +/-! Generic command for the versioned public claim protocol. Public bytes +and their expected address are inputs; JSON supplies only untrusted search +hints. Each successful command ends in the certified TcM claim checker. -/ + +namespace Ix.Certified.ClaimCommand + +universe v + +inductive Hint where + | logical (hint : LogicalHint) + | contains (tree : ByteArray) + | reveal (witness : RevealWitness) + +structure Request where + address : Address + hint : Hint + +def readAddress (value : String) : Except String Address := + match Address.fromString value with + | none => .error "expected a 32-byte hexadecimal address" + | some address => .ok address + +def readHex (value : String) : Except String ByteArray := + match bytesOfHex value with + | none => .error "expected an even-length hexadecimal byte string" + | some bytes => .ok bytes + +def readOptionalHex (json : Lean.Json) (field : String) : Except String (Option ByteArray) := do + (← json.getObjValAs? (Option String) field).mapM readHex + +def readLeaf (json : Lean.Json) : Except String LeafHint := do + let bytes ← readHex (← json.getObjValAs? String "claim") + let claim ← Ixon.runGetExact Ix.Claim.get bytes + if Ix.Claim.ser claim != bytes then throw "noncanonical leaf claim bytes" + return ⟨claim, ← readOptionalHex json "subjects", ← readOptionalHex json "frontier"⟩ + +def readLogicalHint (json : Lean.Json) : Except String LogicalHint := do + let objects ← (← json.getObjValAs? (List String) "objects").mapM readAddress + let naturals ← (← json.getObjValAs? (List String) "naturals").mapM readAddress + let leaves ← (← json.getObjValAs? (List Lean.Json) "leaves").mapM readLeaf + return ⟨⟨objects, naturals⟩, leaves, ← readOptionalHex json "subjects", + ← readOptionalHex json "members", ← readOptionalHex json "frontier", ← readOptionalHex json "axioms", + ← ModelHint.readOptional json⟩ + +def readRequest (json : Lean.Json) : Except String Request := do + let address ← readAddress (← json.getObjValAs? String "address") + let kind ← json.getObjValAs? String "kind" + let hint : Hint ← if kind = "logical" then do pure (Hint.logical (← readLogicalHint json)) + else if kind = "contains" then do pure (Hint.contains (← readHex (← json.getObjValAs? String "tree"))) + else if kind = "reveal" then do + let secret ← readAddress (← json.getObjValAs? String "secret") + let payload ← readAddress (← json.getObjValAs? String "payload") + pure (Hint.reveal ⟨⟨secret, payload⟩⟩) + else throw "kind must be logical, contains or reveal" + return ⟨address, hint⟩ + +def suggestWitness? (fuel : Nat) (source : Ixon.Env) (bytes : ByteArray) (request : Request) : + Option ClaimWitness := do + let reading ← readEnvelope? fuel request.address bytes + match request.hint with + | .logical hint => return .logical (← suggestLogical? fuel source reading.envelope hint) + | .contains tree => return .contains tree + | .reveal witness => return .reveal witness + +def run (fuel : Nat) (source : Ixon.Env) (bytes : ByteArray) (request : Request) : Except String Unit := do + let some witness := suggestWitness? fuel source bytes request + | throw "certified envelope or witness search declined" + if Kernel.acceptsCertifiedClaim.{v} fuel source request.address bytes witness then return () + else throw "certified claim validation rejected the witness" + +theorem run_success {fuel : Nat} {source : Ixon.Env} {bytes : ByteArray} {request : Request} + (h : run.{v} fuel source bytes request = .ok ()) : + ∃ witness, Kernel.acceptsCertifiedClaim.{v} fuel source request.address bytes witness = true := by + cases hw : suggestWitness? fuel source bytes request with + | none => simp [run, hw] at h + | some witness => + by_cases ha : Kernel.acceptsCertifiedClaim.{v} fuel source request.address bytes witness = true + · exact ⟨witness, ha⟩ + · simp [run, hw, ha] at h + +theorem run_meaning {fuel : Nat} {source : Ixon.Env} {bytes : ByteArray} {request : Request} + (h : run.{v} fuel source bytes request = .ok ()) : + ∃ receipt : ClaimReceipt.{v} source fuel request.address bytes, + Address.blake3 bytes = request.address ∧ envelopeBytes receipt.reading.envelope = bytes ∧ + SemanticClaimMeaning.{v} source fuel receipt.reading.envelope := by + obtain ⟨witness, hw⟩ := run_success h + obtain ⟨receipt, _, ha, hb, hm⟩ := Kernel.accepted_tc_claim_meaning hw + exact ⟨receipt, ha, hb, hm⟩ + +def main (args : List String) : IO UInt32 := do + let [sourcePath, envelopePath, requestPath] := args | do + IO.eprintln "usage: certified-claim-check SOURCE.ixe ENVELOPE.bin REQUEST.json" + return 2 + let sourceBytes ← IO.FS.readBinFile sourcePath + let envelope ← IO.FS.readBinFile envelopePath + let requestText ← IO.FS.readFile requestPath + let result := do + let request ← readRequest (← Lean.Json.parse requestText) + let parts ← Ixon.deEnvVerifiedLazy sourceBytes + run.{0} 6400 parts.env envelope request + return request.address + match result with + | .error error => IO.eprintln error; return 1 + | .ok address => + IO.println <| (Lean.Json.mkObj [("accepted", Lean.toJson true), + ("address", Lean.toJson (hexOfBytes address.hash))]).compress + return 0 + +end Ix.Certified.ClaimCommand diff --git a/Ix/Certified/ClaimInput.lean b/Ix/Certified/ClaimInput.lean new file mode 100644 index 000000000..c5497b8a5 --- /dev/null +++ b/Ix/Certified/ClaimInput.lean @@ -0,0 +1,145 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Envelope +import Ix.Theory.Certified.ClaimComposition + +namespace Ix.Certified + +open Ix.Theory Ix.Theory.Certified Ix.Theory.Model + +def sameMembers {α : Type _} [DecidableEq α] (a b : List α) : Bool := + a.all (b.contains ·) && b.all (a.contains ·) + +theorem sameMembers_iff {α : Type _} [DecidableEq α] {a b : List α} : + sameMembers a b = true ↔ ∀ x, x ∈ a ↔ x ∈ b := by + simp only [sameMembers, Bool.and_eq_true, List.all_eq_true, List.contains_iff_mem] + exact ⟨fun h x => ⟨h.1 x, h.2 x⟩, fun h => ⟨fun x => (h x).mp, fun x => (h x).mpr⟩⟩ + +def claimFrontier : Ix.Claim → Option Address + | .check _ frontier | .checkEnv _ frontier | .catalog _ _ frontier | .eval _ _ frontier => frontier + | _ => none + +structure SubjectView (fuel : Nat) (claim : Ix.Claim) (bytes : Option ByteArray) where + addresses : List Address + meaning : match claim with + | .check address _ => bytes = none ∧ addresses = [address] + | .checkEnv root _ => ∃ raw, bytes = some raw ∧ + ∃ opening : TreeOpening fuel root raw, treeLeaves opening.tree = addresses + | _ => False + +def readSubjectView? (fuel : Nat) (claim : Ix.Claim) (bytes : Option ByteArray) : + Option (SubjectView fuel claim bytes) := + match claim, bytes with + | .check address _, none => some ⟨[address], rfl, rfl⟩ + | .checkEnv root _, some raw => do + let opening ← readTree? fuel root raw + return ⟨treeLeaves opening.tree, raw, rfl, opening, rfl⟩ + | _, _ => none + +def ownedReferences (signature : PrimitiveSignature Address) (refs : List (ConstRef Address)) : + List (ConstRef Address) := + (refs.filter fun ref => ref != signature.falseType && ref != signature.falseElim).eraseDups + +theorem mem_ownedReferences {signature : PrimitiveSignature Address} {refs : List (ConstRef Address)} + {ref : ConstRef Address} : ref ∈ ownedReferences signature refs ↔ + ref ∈ refs ∧ ref ≠ signature.falseType ∧ ref ≠ signature.falseElim := by + simp [ownedReferences] + +structure LeafWitness where + claim : Ix.Claim + subjects : Option ByteArray + frontierTree : Option ByteArray + frontier : List (FrontierWitness Address) + declarations : List (DeclarationWitness Address) + +structure PreparedLeaf (fuel : Nat) (objects : Objects) (witness : LeafWitness) where + subjectView : SubjectView fuel witness.claim witness.subjects + subjectRefs : List (ConstRef Address) + subjectsResolved : subjectView.addresses.flatMapM (subjectReferences? objects) = some subjectRefs + frontierView : OptionalTreeOpening fuel (claimFrontier witness.claim) witness.frontierTree + frontierRefs : List (ConstRef Address) + frontierResolved : frontierView.leaves.flatMapM (subjectReferences? objects) = some frontierRefs + frontierExact : ∀ ref, ref ∈ witness.frontier.map (·.ref) ↔ ref ∈ frontierRefs + +def prepareLeaf? (fuel : Nat) (objects : Objects) (witness : LeafWitness) : + Option (PreparedLeaf fuel objects witness) := do + let subjectView ← readSubjectView? fuel witness.claim witness.subjects + match hs : subjectView.addresses.flatMapM (subjectReferences? objects) with + | none => none + | some subjectRefs => do + let frontierView ← readOptionalTree? fuel (claimFrontier witness.claim) witness.frontierTree + match hf : frontierView.leaves.flatMapM (subjectReferences? objects) with + | none => none + | some frontierRefs => + if he : sameMembers (witness.frontier.map (·.ref)) frontierRefs = true then + some ⟨subjectView, subjectRefs, hs, frontierView, frontierRefs, hf, sameMembers_iff.mp he⟩ + else none + +def PreparedLeaf.node {fuel : Nat} {objects : Objects} {witness : LeafWitness} + (leaf : PreparedLeaf fuel objects witness) (signature : PrimitiveSignature Address) : ClaimNode Address := + ⟨ownedReferences signature leaf.subjectRefs, witness.frontier, witness.declarations⟩ + +structure LeafInput (fuel : Nat) (objects : Objects) where + witness : LeafWitness + prepared : PreparedLeaf fuel objects witness + +def readLeafInput? (fuel : Nat) (objects : Objects) (witness : LeafWitness) : + Option (LeafInput fuel objects) := do + let prepared ← prepareLeaf? fuel objects witness + return ⟨witness, prepared⟩ + +def leafAddresses {fuel : Nat} {objects : Objects} (leaves : List (LeafInput fuel objects)) : List Address := + leaves.flatMap (·.prepared.subjectView.addresses) + +def environmentRoot? : Ix.Claim → Option Address + | .checkEnv root _ => some root + | _ => none + +structure ContentView (fuel : Nat) (claim : Ix.Claim) (subjects members : Option ByteArray) + {objects : Objects} (leaves : List (LeafInput fuel objects)) where + addresses : List Address + exactLeaves : ∀ address, address ∈ addresses ↔ address ∈ leafAddresses leaves + meaning : match claim with + | .check address _ => subjects = none ∧ members = none ∧ addresses = [address] + | .checkEnv root _ => members = none ∧ ∃ raw, subjects = some raw ∧ + ∃ opening : TreeOpening fuel root raw, treeLeaves opening.tree = addresses + | .catalog memberRoot contentRoot _ => ∃ rawContent rawMembers, + subjects = some rawContent ∧ members = some rawMembers ∧ + ∃ content : TreeOpening fuel contentRoot rawContent, + ∃ membership : TreeOpening fuel memberRoot rawMembers, + treeLeaves content.tree = addresses ∧ + ∃ roots, leaves.mapM (fun leaf => environmentRoot? leaf.witness.claim) = some roots ∧ + ∀ root, root ∈ treeLeaves membership.tree ↔ root ∈ roots + | _ => False + +def readContentView? (fuel : Nat) (claim : Ix.Claim) (subjects members : Option ByteArray) + {objects : Objects} (leaves : List (LeafInput fuel objects)) : + Option (ContentView fuel claim subjects members leaves) := + match claim, subjects, members with + | .check address _, none, none => + if he : sameMembers [address] (leafAddresses leaves) = true then + some ⟨[address], sameMembers_iff.mp he, rfl, rfl, rfl⟩ + else none + | .checkEnv root _, some raw, none => do + let opening ← readTree? fuel root raw + if he : sameMembers (treeLeaves opening.tree) (leafAddresses leaves) = true then + some ⟨treeLeaves opening.tree, sameMembers_iff.mp he, rfl, raw, rfl, opening, rfl⟩ + else none + | .catalog memberRoot contentRoot _, some rawContent, some rawMembers => do + let content ← readTree? fuel contentRoot rawContent + let membership ← readTree? fuel memberRoot rawMembers + match hr : leaves.mapM (fun leaf => environmentRoot? leaf.witness.claim) with + | none => none + | some roots => + if he : sameMembers (treeLeaves content.tree) (leafAddresses leaves) = true then + if hm : sameMembers (treeLeaves membership.tree) roots = true then + some ⟨treeLeaves content.tree, sameMembers_iff.mp he, rawContent, rawMembers, rfl, rfl, + content, membership, rfl, roots, hr, sameMembers_iff.mp hm⟩ + else none + else none + | _, _, _ => none + +end Ix.Certified diff --git a/Ix/Certified/ClaimMain.lean b/Ix/Certified/ClaimMain.lean new file mode 100644 index 000000000..2028b1f52 --- /dev/null +++ b/Ix/Certified/ClaimMain.lean @@ -0,0 +1,8 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.ClaimCommand + +def main := Ix.Certified.ClaimCommand.main diff --git a/Ix/Certified/ClaimMeaning.lean b/Ix/Certified/ClaimMeaning.lean new file mode 100644 index 000000000..08158bd8f --- /dev/null +++ b/Ix/Certified/ClaimMeaning.lean @@ -0,0 +1,151 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.ClaimCheck + +namespace Ix.Certified + +open Ix.Theory Ix.Theory.Certified Ix.Theory.Model Ix.Theory.Model.SetTheory + +universe v +variable {source : Ixon.Env} {snapshot : SourceSnapshot source} + {fuel : Nat} {envelope : Envelope} {witness : LogicalWitness} + +theorem OptionalTreeOpening.none_leaves (opening : OptionalTreeOpening fuel none bytes) : + opening.leaves = [] := by + cases bytes with + | none => exact opening.evidence + | some => exact False.elim opening.evidence + +theorem OptionalTreeOpening.closed_leaves (opening : OptionalTreeOpening fuel root bytes) + (closed : root = none) : opening.leaves = [] := by + subst root + exact opening.none_leaves + +theorem LogicalChecking.target_entry + (checked : LogicalChecking.{v} snapshot fuel envelope witness) + {ref : ConstRef Address} (hr : ref ∈ checked.subjects) : + ∃ entry, checked.batch.receipt.checked.result.entries ref = some entry ∧ + EntrySource checked.signature checked.store ref entry := by + have validated := (checked.signature.validate_iff checked.store).mp checked.batch.receipt.frontier.validated + by_cases hF : ref = checked.signature.falseType + · subst ref + exact ⟨PrimitiveSignature.falseEntry, checked.batch.receipt.checked.result.present.1, + Or.inl ⟨rfl, rfl, validated.1⟩⟩ + · by_cases hE : ref = checked.signature.falseElim + · subst ref + exact ⟨checked.signature.falseElimEntry, checked.batch.receipt.checked.result.present.2, + Or.inr (Or.inl ⟨rfl, rfl, validated.2⟩)⟩ + · have owned := (checked.exactSubjects ref).mpr (mem_ownedReferences.mpr ⟨hr, hF, hE⟩) + have hp := checked.batch.receipt.present ref owned + obtain ⟨entry, he⟩ := Option.isSome_iff_exists.mp hp + refine ⟨entry, he, ?_⟩ + rcases checked.batch.receipt.checked.source ref entry he with old | new + · rw [checked.batch.receipt.fresh ref owned] at old + cases old + · exact new + +theorem LogicalChecking.original_subject + (checked : LogicalChecking.{v} snapshot fuel envelope witness) + {ref : ConstRef Address} (hr : ref ∈ checked.subjects) : + ∃ entry, checked.batch.receipt.checked.result.entries ref = some entry ∧ + SourceDeclarationReading source snapshot.decodedObjects snapshot.decodedNaturals ref entry := by + obtain ⟨entry, he, hs⟩ := checked.target_entry hr + exact ⟨entry, he, snapshot.declaration_reading checked.storeReading hs.header⟩ + +theorem LogicalChecking.original_frontier + (checked : LogicalChecking.{v} snapshot fuel envelope witness) + {ref : ConstRef Address} (hr : ref ∈ checked.frontierRefs) : + ∃ entry, checked.batch.receipt.frontier.interface.entries ref = some entry ∧ + SourceDeclarationReading source snapshot.decodedObjects snapshot.decodedNaturals ref entry := by + have hm := (checked.exactFrontier ref).mpr hr + obtain ⟨header, hh, rfl⟩ := List.mem_map.mp hm + exact ⟨header.entry, checked.batch.receipt.frontier.formed.lookup hh, + snapshot.declaration_reading checked.storeReading (checked.batch.receipt.frontier.header_source hh)⟩ + +/-- The claim's public axiom tree contains every use, including uses retained +from leaves. Each address resolves to an exactly realized source schema. -/ +theorem LogicalChecking.logical_policy + (checked : LogicalChecking.{v} snapshot fuel envelope witness) + {ref : ConstRef Address} (hr : ref ∈ checked.axiomRefs) : + ∃ entry, Standard.EntrySource checked.store ref entry ∨ Quotient.EntrySource checked.store ref entry := + checked.batch.logicalUses_authorized ((checked.exactAxioms ref).mpr hr) + +theorem LogicalChecking.closed_frontier + (checked : LogicalChecking.{v} snapshot fuel envelope witness) + (closed : claimFrontier envelope.claim = none) : checked.batch.receipt.frontier.refs = [] := by + have hl : checked.frontier.leaves = [] := by + exact checked.frontier.closed_leaves closed + have hr : checked.frontierRefs = [] := by + have h := checked.frontierReading + rw [hl] at h + simpa using h.symm + apply List.eq_nil_iff_forall_not_mem.mpr + intro ref hm + have h := (checked.exactFrontier ref).mp hm + rw [hr] at h + cases h + +/-- Conditional soundness of every original source member or constructor in +the exact committed subject set. All declared frontier values are preserved +at every universe instance while the shared interpretation is extended. -/ +theorem LogicalReceipt.subject_meaning + (receipt : LogicalReceipt.{v} source fuel envelope witness) + (V : Type v) [SetTheory V] (constants : Assignment Address V) + (hM : receipt.checked.signature.Compatible receipt.checked.batch.receipt.frontier.interface.entries constants) : + ∃ constants' : Assignment Address V, + receipt.checked.signature.Compatible receipt.checked.batch.receipt.checked.result.entries constants' ∧ + Assignment.AgreesOn receipt.checked.batch.receipt.frontier.interface.entries constants constants' ∧ + ∀ ref ∈ receipt.checked.subjects, ∃ entry, + SourceDeclarationReading source receipt.snapshot.decodedObjects receipt.snapshot.decodedNaturals ref entry ∧ + ∀ levels, levels.length = entry.universes → ∀ env, + WellDenoted constants' levels env entry.type ∧ + constants' ref levels ∈ˢ interp constants' levels env entry.type := by + obtain ⟨constants', hm, ha⟩ := receipt.checked.batch.receipt.checked.extension.models V constants hM + refine ⟨constants', hm, ha, ?_⟩ + intro ref hr + obtain ⟨entry, he, hs⟩ := receipt.checked.original_subject hr + refine ⟨entry, hs, ?_⟩ + intro levels hl env + exact ⟨hm.realizes.typeValid ref entry he levels hl env, + hm.realizes.member ref entry he levels hl env⟩ + +/-- A public claim with no structural frontier constructs its model. This +does not assert that its separately recorded logical-axiom set is empty. -/ +theorem LogicalReceipt.closed_subject_meaning + (receipt : LogicalReceipt.{v} source fuel envelope witness) + (closed : claimFrontier envelope.claim = none) (V : Type v) [SetTheory V] : + ∃ constants : Assignment Address V, + receipt.checked.signature.Compatible receipt.checked.batch.receipt.checked.result.entries constants ∧ + ∀ ref ∈ receipt.checked.subjects, ∃ entry, + SourceDeclarationReading source receipt.snapshot.decodedObjects receipt.snapshot.decodedNaturals ref entry ∧ + ∀ levels, levels.length = entry.universes → ∀ env, + WellDenoted constants levels env entry.type ∧ + constants ref levels ∈ˢ interp constants levels env entry.type := by + have hf := receipt.checked.closed_frontier closed + have initial : receipt.checked.signature.Compatible receipt.checked.batch.receipt.frontier.interface.entries + (receipt.checked.signature.assignment (V := V)) := by + rw [receipt.checked.batch.receipt.frontier.empty_interface hf] + exact receipt.checked.signature.compatible_assignment + obtain ⟨constants, hm, _, subjects⟩ := receipt.subject_meaning V receipt.checked.signature.assignment initial + exact ⟨constants, hm, subjects⟩ + +theorem LogicalReceipt.no_False + (receipt : LogicalReceipt.{v} source fuel envelope witness) + (closed : claimFrontier envelope.claim = none) {ref : ConstRef Address} + (subject : ref ∈ receipt.checked.subjects) + (type : receipt.checked.store.type ref = some receipt.checked.signature.falseExpr) + (V : Type v) [SetTheory V] : False := by + have hf := receipt.checked.closed_frontier closed + obtain ⟨constants, hm, _⟩ := receipt.checked.batch.closed_has_model hf V + obtain ⟨entry, he, hs⟩ := receipt.checked.target_entry subject + have hc : entry.type = .const receipt.checked.signature.falseType [] := + AExpr.eq_const_of_erase_eq (Option.some.inj (hs.header.type.symm.trans type)) + have hmem := hm.realizes.member ref entry he (List.replicate entry.universes 0) (by simp) (fun _ => empty) + rw [hc] at hmem + simp only [interp, List.map_nil, hm.falseValue] at hmem + exact not_mem_empty _ hmem + +end Ix.Certified diff --git a/Ix/Certified/ClaimSuggest.lean b/Ix/Certified/ClaimSuggest.lean new file mode 100644 index 000000000..4052efd7c --- /dev/null +++ b/Ix/Certified/ClaimSuggest.lean @@ -0,0 +1,75 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certificate.Claims +import Ix.Certified.ClaimAccept +import Ix.Certified.ModelHints + +/-! Untrusted witness construction. The acceptance path imports none of +these search functions and checks the public envelope and complete returned +witness again. Trees are hints whose public Merkle roots are checked. -/ + +namespace Ix.Certified + +open Ix.Theory Ix.Theory.Certified Ix.Theory.Model + +structure LeafHint where + claim : Ix.Claim + subjects : Option ByteArray + frontierTree : Option ByteArray + +structure LogicalHint where + selection : InputSelection + leaves : List LeafHint + subjects : Option ByteArray + members : Option ByteArray + frontierTree : Option ByteArray + axiomTree : Option ByteArray + models : List ModelHint := [] + +/-- Header dependency order is independent of the Merkle tree's leaf order. -/ +def frontierOrder? (fuel : Nat) (signature : PrimitiveSignature Address) (store : Store Address) + (allowed pending visited active : List (ConstRef Address)) : Option (List (ConstRef Address)) := + match fuel, pending with + | _, [] => some visited + | 0, _ :: _ => none + | fuel + 1, ref :: rest => + if ref = signature.falseType ∨ ref = signature.falseElim ∨ ref ∈ visited then + frontierOrder? fuel signature store allowed rest visited active + else if ref ∈ active ∨ ref ∉ allowed then none + else do + let type ← store.type ref + let visited ← frontierOrder? fuel signature store allowed type.refs visited (ref :: active) + frontierOrder? fuel signature store allowed rest (visited ++ [ref]) active + +def frontierSuggestion? (fuel : Nat) (signature : PrimitiveSignature Address) (store : Store Address) + (objects : Objects) (root : Option Address) (bytes : Option ByteArray) : + Option (List (ConstRef Address) × List (FrontierWitness Address)) := do + let opening ← readOptionalTree? fuel root bytes + let refs ← opening.leaves.flatMapM (subjectReferences? objects) + let ordered ← frontierOrder? fuel signature store refs refs [] [] + let (_, witnesses) ← Certificate.frontierWitnesses? fuel store signature.environment ordered + return (ordered, witnesses) + +def leafSuggestion? (fuel : Nat) (signature : PrimitiveSignature Address) (store : Store Address) + (objects : Objects) (hint : LeafHint) (models : List (Certificate.Modeled.Candidate Address) := []) : Option LeafWitness := do + let subjects ← readSubjectView? fuel hint.claim hint.subjects + let refs ← subjects.addresses.flatMapM (subjectReferences? objects) + let (frontier, _) ← frontierSuggestion? fuel signature store objects (claimFrontier hint.claim) hint.frontierTree + let node ← Certificate.claimNode? fuel signature store frontier (ownedReferences signature refs) models + return ⟨hint.claim, hint.subjects, hint.frontierTree, node.frontier, node.declarations⟩ + +def suggestLogical? (fuel : Nat) (source : Ixon.Env) (envelope : Envelope) (hint : LogicalHint) : + Option LogicalWitness := do + let (snapshot, _) ← readSnapshot? fuel source hint.selection {} + let signature ← readSignature? envelope.profile snapshot.decodedObjects + let store ← readStore? fuel snapshot.decodedObjects snapshot.decodedNaturals + let models ← modelCandidates? snapshot.decodedObjects store hint.models + let leaves ← hint.leaves.mapM (fun leaf => leafSuggestion? fuel signature store snapshot.decodedObjects leaf models) + let (_, frontier) ← frontierSuggestion? fuel signature store snapshot.decodedObjects + (claimFrontier envelope.claim) hint.frontierTree + return ⟨hint.selection, leaves, hint.subjects, hint.members, hint.frontierTree, frontier, hint.axiomTree⟩ + +end Ix.Certified diff --git a/Ix/Certified/Command.lean b/Ix/Certified/Command.lean new file mode 100644 index 000000000..0ad2ba24c --- /dev/null +++ b/Ix/Certified/Command.lean @@ -0,0 +1,96 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Suggest +import Ix.Kernel.Certified + +/-! Generic source-file driver for the certified profile. The JSON request +selects the expected target/subjects, primitive signature and finite source +closure. Witness search is untrusted and always followed by certified TcM +validation. There is no inference-only or unchecked fallback command. -/ + +namespace Ix.Certified.Command + +universe v + +structure Request where + profile : Profile + target : Address + subjects : List Address + selection : InputSelection + models : List ModelHint := [] + +def readAddress (value : String) : Except String Address := + match Address.fromString value with + | none => .error "expected a 32-byte hexadecimal address" + | some address => .ok address + +def readRequest (json : Lean.Json) : Except String Request := do + let falseType ← readAddress (← json.getObjValAs? String "falseType") + let falseElim ← readAddress (← json.getObjValAs? String "falseElim") + let natural ← json.getObjValAs? (Option String) "natType" + let natType ← natural.mapM readAddress + let target ← readAddress (← json.getObjValAs? String "target") + let subjects ← (← json.getObjValAs? (List String) "subjects").mapM readAddress + let objects ← (← json.getObjValAs? (List String) "objects").mapM readAddress + let naturals ← (← json.getObjValAs? (List String) "naturals").mapM readAddress + return ⟨⟨falseType, falseElim, natType⟩, target, subjects, ⟨objects, naturals⟩, ← ModelHint.readOptional json⟩ + +def run (mode : String) (fuel : Nat) (source : Ixon.Env) (request : Request) : Except String Unit := do + if mode = "proof" then + let some witness := suggestSource? fuel source request.profile request.target request.selection request.models + | throw "certified proof witness search declined" + if Kernel.acceptsCertifiedSource.{v} fuel source request.profile request.target request.selection witness then + return () + else throw "certified proof validation rejected the witness" + else if mode = "store" then + let some witness := suggestStore? fuel source request.profile request.subjects request.selection request.models + | throw "certified declaration witness search declined" + if Kernel.acceptsCertifiedStoreSource.{v} fuel source request.profile request.subjects request.selection witness then + return () + else throw "certified declaration validation rejected the witness" + else throw "mode must be proof or store" + +/-- The generic command can report success only after an actual certified +source run. Witness search is not an alternate acceptance path. -/ +theorem run_success {mode : String} {fuel : Nat} {source : Ixon.Env} {request : Request} + (h : run.{v} mode fuel source request = .ok ()) : + (mode = "proof" ∧ ∃ witness, Kernel.acceptsCertifiedSource.{v} fuel source + request.profile request.target request.selection witness = true) ∨ + (mode = "store" ∧ ∃ witness, Kernel.acceptsCertifiedStoreSource.{v} fuel source + request.profile request.subjects request.selection witness = true) := by + by_cases hp : mode = "proof" + · cases hw : suggestSource? fuel source request.profile request.target request.selection request.models with + | none => simp [run, hp, hw] at h + | some witness => + by_cases ha : Kernel.acceptsCertifiedSource.{v} fuel source request.profile request.target request.selection witness = true + · exact .inl ⟨hp, witness, ha⟩ + · simp [run, hp, hw, ha] at h + · by_cases hs : mode = "store" + · cases hw : suggestStore? fuel source request.profile request.subjects request.selection request.models with + | none => simp [run, hs, hw] at h + | some witness => + by_cases ha : Kernel.acceptsCertifiedStoreSource.{v} fuel source request.profile request.subjects request.selection witness = true + · exact .inr ⟨hs, witness, ha⟩ + · simp [run, hs, hw, ha] at h + · simp [run, hp, hs] at h + +def main (args : List String) : IO UInt32 := do + let [mode, sourcePath, requestPath] := args | do + IO.eprintln "usage: certified-check proof|store SOURCE.ixe REQUEST.json" + return 2 + let bytes ← IO.FS.readBinFile sourcePath + let requestText ← IO.FS.readFile requestPath + let result := do + let request ← readRequest (← Lean.Json.parse requestText) + let parts ← Ixon.deEnvVerifiedLazy bytes + run.{0} mode 6400 parts.env request + match result with + | .error error => IO.eprintln error; return 1 + | .ok () => + IO.println <| (Lean.Json.mkObj [("accepted", Lean.toJson true), ("mode", Lean.toJson mode)]).compress + return 0 + +end Ix.Certified.Command diff --git a/Ix/Certified/Corpus.lean b/Ix/Certified/Corpus.lean new file mode 100644 index 000000000..688c899db --- /dev/null +++ b/Ix/Certified/Corpus.lean @@ -0,0 +1,123 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Fixtures + +namespace Ix.Certified.Corpus + +open Fixtures + +def idType (level : UInt64) : Ixon.Expr := + .leanAll (.sort level) (.leanAll (.var 0) (.var 1)) + +def idBody (level : UInt64) : Ixon.Expr := + .leanLam (.sort level) (.leanLam (.var 0) (.var 0)) + +def polyId (kind : Ix.DefKind) : Ixon.Constant := { + info := .defn { + kind, safety := .safe, lvls := 1, typ := idType 0, value := idBody 0 } + sharing := #[], refs := #[], univs := #[.var 0] +} + +def polyUse (kind : Ix.DefKind) (large : Bool) : Ixon.Constant := { + info := .defn { + kind := .thm, safety := .safe, lvls := 0, typ := idType 0, + value := if large then + .app (.leanLam (idType 1) (idBody 0)) (.ref 0 #[1]) + else .ref 0 #[0] } + sharing := #[], refs := #[(encode (polyId kind)).1], + univs := #[.zero, .succ .zero] +} + +def dependent (n : UInt64) (u : Ixon.Univ) : Ixon.Constant := { + info := .defn { + kind := .thm, safety := .safe, lvls := n, + typ := .leanAll (.sort 0) + (.leanAll (.leanAll (.var 0) (.sort 1)) + (.leanAll (.leanAll (.var 1) (.app (.var 1) (.var 0))) + (.leanAll (.var 2) (.app (.var 2) (.var 0))))), + value := .leanLam (.sort 0) + (.leanLam (.leanAll (.var 0) (.sort 1)) + (.leanLam (.leanAll (.var 1) (.app (.var 1) (.var 0))) + (.leanLam (.var 2) (.app (.var 1) (.var 0))))) } + sharing := #[], refs := #[], univs := #[u, .zero] +} + +-- The declared proposition contains a beta redex. The body is checked +-- against that original proposition through a beta conversion certificate. +def betaStatement : Ixon.Constant := { + info := .defn { + kind := .thm, safety := .safe, lvls := 0, + typ := .app (.leanLam (.sort 0) (.var 0)) (idType 0), + value := idBody 0 } + sharing := #[], refs := #[], univs := #[.zero] +} + +-- ∀ A : Prop, ∀ F : (A → A) → Prop, ∀ f, F f → F (fun x => f x). +-- The supplied proof returns its last argument, so admission needs eta +-- beneath a dependent application and a telescope of products. +def etaStatement : Ixon.Constant := { + info := .defn { + kind := .thm, safety := .safe, lvls := 0, + typ := .leanAll (.sort 0) + (.leanAll (.leanAll (.leanAll (.var 0) (.var 1)) (.sort 0)) + (.leanAll (.leanAll (.var 1) (.var 2)) + (.leanAll (.app (.var 1) (.var 0)) + (.app (.var 2) (.leanLam (.var 3) (.app (.var 2) (.var 0))))))), + value := .leanLam (.sort 0) + (.leanLam (.leanAll (.leanAll (.var 0) (.var 1)) (.sort 0)) + (.leanLam (.leanAll (.var 1) (.var 2)) + (.leanLam (.app (.var 1) (.var 0)) (.var 0)))) } + sharing := #[], refs := #[], univs := #[.zero] +} + +def eliminatorStatement : Ixon.Constant := { + info := .defn { + kind := .thm, safety := .safe, lvls := 0, + typ := .leanAll (.leanAll (.ref 0 #[]) (.sort 0)) + (.leanAll (.ref 0 #[]) (.app (.var 1) (.var 0))), + value := .ref 1 #[0] } + sharing := #[], refs := #[falseObject.1, falseElimObject.1], univs := #[.zero] +} + +structure Case where + name : String + target : Address + blobs : ConstantBlobs + +def one (name : String) (source : Ixon.Constant) + (dependencies : List Ixon.Constant := []) : Case := + let object := encode source + ⟨name, object.1, prelude ++ dependencies.map encode ++ [object]⟩ + +def positive : List Case := [ + one "identity" identity, + one "dependent-polymorphic" (dependent 1 (.var 0)), + one "dependent-Prop" (dependent 0 .zero), + one "dependent-Type" (dependent 0 (.succ .zero)), + one "dependent-Type1" (dependent 0 (.succ (.succ .zero))), + one "definition-at-Prop" (polyUse .defn false) [polyId .defn], + one "definition-at-Type" (polyUse .defn true) [polyId .defn], + one "theorem-at-Prop" (polyUse .thm false) [polyId .thm], + one "opaque-at-Type" (polyUse .opaq true) [polyId .opaq], + one "beta-statement" betaStatement, + one "eta-statement" etaStatement, + one "False-eliminator" eliminatorStatement +] + +#guard positive.all fun c => accepted c.blobs c.target + +/-- Syntactic exclusions measured separately from failed witness generation. +The selected profile never enables literals or linear binder flags. -/ +def declined : List Case := [ + one "linear-binder-profile" linearIdentity, + one "literal-profile" { identity with info := .defn { + kind := .thm, safety := .safe, lvls := 0, + typ := idType 0, value := .nat 0 } } +] + +#guard declined.all fun c => (prepare? 300 profile c.target c.blobs).isNone + +end Ix.Certified.Corpus diff --git a/Ix/Certified/Envelope.lean b/Ix/Certified/Envelope.lean new file mode 100644 index 000000000..c88cb93e9 --- /dev/null +++ b/Ix/Certified/Envelope.lean @@ -0,0 +1,92 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Trees +import Ix.Claim + +/-! Versioned public claims for the certified source checker. This envelope +binds the original Ix claim, primitive addresses, modeled logical policy and +axiom-use root. The thin-frontier convention is distinct from the legacy +whole-environment convention. A backend still has to bind its actual program +and permitted keys to this source program version. -/ + +namespace Ix.Certified + +structure Protocol where + format : UInt64 := 1 + codec : UInt64 := 1 + checker : UInt64 := 1 + policy : UInt64 := 1 + aggregation : UInt64 := 1 + deriving DecidableEq + +/-- Checker version 2 includes checked model companions for mutual and +nested declarations. Older checker identities do not select this program. -/ +def Protocol.current : Protocol := { checker := 2 } + +structure Envelope where + protocol : Protocol + profile : Profile + claim : Ix.Claim + logicalAxioms : Option Address + +def envelopeMagic : ByteArray := "IX-CERTIFIED-CLAIM".toUTF8 ++ ⟨#[0]⟩ + +def putEnvelope (envelope : Envelope) : Ixon.PutM Unit := do + Ixon.putBytes envelopeMagic + Ixon.putTag0 ⟨envelope.protocol.format⟩ + Ixon.putTag0 ⟨envelope.protocol.codec⟩ + Ixon.putTag0 ⟨envelope.protocol.checker⟩ + Ixon.putTag0 ⟨envelope.protocol.policy⟩ + Ixon.putTag0 ⟨envelope.protocol.aggregation⟩ + Ixon.Serialize.put envelope.profile.falseType + Ixon.Serialize.put envelope.profile.falseElim + Ix.Claim.putOptAddr envelope.profile.natType + Ix.Claim.put envelope.claim + Ix.Claim.putOptAddr envelope.logicalAxioms + +def envelopeBytes (envelope : Envelope) : ByteArray := Ixon.runPut (putEnvelope envelope) + +def getEnvelope : Ixon.GetM Envelope := do + if (← Ixon.getBytes envelopeMagic.size) != envelopeMagic then throw "invalid certified claim magic" + let format := (← Ixon.getTag0).size + let codec := (← Ixon.getTag0).size + let checker := (← Ixon.getTag0).size + let policy := (← Ixon.getTag0).size + let aggregation := (← Ixon.getTag0).size + let falseType ← Ixon.Serialize.get + let falseElim ← Ixon.Serialize.get + let natType ← Ix.Claim.getOptAddr + let claim ← Ix.Claim.get + let logicalAxioms ← Ix.Claim.getOptAddr + return ⟨⟨format, codec, checker, policy, aggregation⟩, ⟨falseType, falseElim, natType⟩, claim, logicalAxioms⟩ + +structure EnvelopeReading (address : Address) (bytes : ByteArray) where + envelope : Envelope + addressSize : address.hash.size = 32 + authenticated : Address.blake3 bytes = address + parsing : Ixon.runGetExact getEnvelope bytes = .ok envelope + canonical : envelopeBytes envelope = bytes + supported : envelope.protocol = Protocol.current + +def readEnvelope? (fuel : Nat) (address : Address) (bytes : ByteArray) : + Option (EnvelopeReading address bytes) := + if bytes.size > fuel then none else + if ha : address.hash.size = 32 ∧ Address.blake3 bytes = address then + match hp : Ixon.runGetExact getEnvelope bytes with + | .error _ => none + | .ok envelope => + if hc : envelopeBytes envelope = bytes then + if hs : envelope.protocol = Protocol.current then some ⟨envelope, ha.1, ha.2, hp, hc, hs⟩ + else none + else none + else none + +/-- Exact parsing fixes the complete public claim and profile. Semantic or +expression equality is never used to recover the authenticated bytes. -/ +theorem EnvelopeReading.unique {a b : EnvelopeReading address bytes} : a.envelope = b.envelope := + Except.ok.inj (a.parsing.symm.trans b.parsing) + +end Ix.Certified diff --git a/Ix/Certified/Fixtures.lean b/Ix/Certified/Fixtures.lean new file mode 100644 index 000000000..e7306ffd3 --- /dev/null +++ b/Ix/Certified/Fixtures.lean @@ -0,0 +1,141 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Bytes +import Ix.Theory.Certificate.Build + +namespace Ix.Certified.Fixtures + +open Ix.Theory Ix.Theory.Certified + +def encode (source : Ixon.Constant) : Address × ByteArray := + let bytes := Ixon.serConstant source + (Address.blake3 bytes, bytes) + +def falseBlock : Ixon.Constant := { + info := .muts #[.indc { + isUnsafe := false, lvls := 0, params := 0, indices := 0, + typ := .sort 0, ctors := #[] + }] + sharing := #[] + refs := #[] + univs := #[.zero] +} + +def falseBlockObject := encode falseBlock + +def falseProjection : Ixon.Constant := + ⟨.iPrj ⟨0, falseBlockObject.1⟩, #[], #[], #[]⟩ + +def falseObject := encode falseProjection + +def falseElim : Ixon.Constant := { + info := .recr { + k := false, isUnsafe := false, lvls := 1, params := 0, indices := 0, + motives := 1, minors := 0, + typ := .leanAll (.leanAll (.ref 0 #[]) (.sort 0)) + (.leanAll (.ref 0 #[]) (.app (.var 1) (.var 0))), + rules := #[] + } + sharing := #[] + refs := #[falseObject.1] + univs := #[.var 0] +} + +def falseElimObject := encode falseElim + +def profile : Profile := ⟨falseObject.1, falseElimObject.1, none⟩ +def prelude : ConstantBlobs := [falseBlockObject, falseObject, falseElimObject] + +def identity : Ixon.Constant := { + info := .defn { + kind := .thm, safety := .safe, lvls := 0, + typ := .leanAll (.sort 0) (.leanAll (.var 0) (.var 1)), + value := .leanLam (.sort 0) (.leanLam (.var 0) (.var 0)) + } + sharing := #[] + refs := #[] + univs := #[.zero] +} + +def identityObject := encode identity +def identityBlobs : ConstantBlobs := prelude ++ [identityObject] + +def witness? (blobs : ConstantBlobs) (target : Address) : Option (ProofWitness Address) := do + let prepared ← prepare? 300 profile target blobs + Ix.Theory.Certificate.proofWitness? 300 prepared.signature prepared.input + +def accepted (blobs : ConstantBlobs) (target : Address) : Bool := + (witness? blobs target).any (acceptsSerialized.{0} 300 profile target blobs) + +#guard accepted identityBlobs identityObject.1 + +-- A new valid hash cannot license trailing data outside the constant grammar. +def trailingIdentity : Address × ByteArray := + let bytes := identityObject.2.push 0 + (Address.blake3 bytes, bytes) +#guard (prepare? 300 profile trailingIdentity.1 (prelude ++ [trailingIdentity])).isNone + +-- Changed bytes under an old address fail authentication. +#guard (prepare? 300 profile identityObject.1 + (prelude ++ [(identityObject.1, identityObject.2.push 0)])).isNone + +-- Duplicate object addresses are not silently overwritten. +#guard (prepare? 300 profile identityObject.1 (identityBlobs ++ [identityObject])).isNone + +def linearIdentity : Ixon.Constant := + { identity with info := .defn { + kind := .thm, safety := .safe, lvls := 0, + typ := .leanAll (.sort 0) (.leanAll (.var 0) (.var 1)), + value := .lam .linear (.sort 0) (.leanLam (.var 0) (.var 0)) + } } + +def linearIdentityObject := encode linearIdentity +#guard (prepare? 300 profile linearIdentityObject.1 + (prelude ++ [linearIdentityObject])).isNone + +def wrongTable : Ixon.Constant := { identity with univs := #[] } +def wrongTableObject := encode wrongTable +#guard (prepare? 300 profile wrongTableObject.1 (prelude ++ [wrongTableObject])).isNone + +def cyclicSharing : Ixon.Constant := { + identity with + info := .defn { + kind := .thm, safety := .safe, lvls := 0, + typ := .leanAll (.sort 0) (.leanAll (.var 0) (.var 1)), value := .share 0 } + sharing := #[.share 0] +} +def cyclicSharingObject := encode cyclicSharing +#guard (prepare? 30 profile cyclicSharingObject.1 (prelude ++ [cyclicSharingObject])).isNone + +def axiomIdentity : Ixon.Constant := { + identity with + info := .axio { + isUnsafe := false, lvls := 0, + typ := .leanAll (.sort 0) (.leanAll (.var 0) (.var 1)) } +} +def axiomIdentityObject := encode axiomIdentity + +-- Force an untrusted proof witness through the actual acceptance gate. This +-- is a policy rejection, not merely a failure of the suggestion producer. +def axiomRejected : Bool := + match witness? identityBlobs identityObject.1 with + | none => false + | some witness => + let forged := { witness with declarations := witness.declarations.map fun declaration => + match declaration with + | .definition definition => .definition { definition with ref := .member axiomIdentityObject.1 0 } + | .ordinary block => .ordinary block + | .standard witness => .standard witness + | .quotient witness => .quotient witness + | .structure witness => .structure witness + | .natural witness => .natural witness + | .modeled witness => .modeled witness } + !acceptsSerialized.{0} 300 profile axiomIdentityObject.1 + (prelude ++ [axiomIdentityObject]) forged + +#guard axiomRejected + +end Ix.Certified.Fixtures diff --git a/Ix/Certified/Ingress.lean b/Ix/Certified/Ingress.lean new file mode 100644 index 000000000..8e799407f --- /dev/null +++ b/Ix/Certified/Ingress.lean @@ -0,0 +1,315 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Store + +/-! Certified lazy reads from the actual Ixon environment. Only selected +addresses are visited. Materialized source caches and kernel judgment caches +cannot replace authenticated bytes. The reusable cache stores decoded data; +each hit also compares the exact current source bytes. -/ + +namespace Ix.Certified + +open Ix.Theory Ix.Theory.Certified Ix.Theory.Model Ix.Theory.Model.SetTheory + +universe v + +/-- A constant window must be wholly inside its backing buffer. The cached +materialization in `LazyConstant` is intentionally not an input to this read. -/ +def constantBytes? (source : Ixon.Env) (address : Address) : Option ByteArray := do + let entry ← source.consts.get? address + if entry.off ≤ entry.buf.size ∧ entry.len ≤ entry.buf.size - entry.off then + some entry.rawBytes + else none + +abbrev DecodedNatural (address : Address) (bytes : ByteArray) := + { value : Nat // + address.hash.size = 32 ∧ Address.blake3 bytes = address ∧ + Nat.fromBytesLE bytes.data = value ∧ ByteArray.mk value.toBytesLE = bytes } + +theorem decodeObject_complete {address : Address} {bytes : ByteArray} + (value : DecodedObject address bytes) : decodeObject? address bytes = some value := by + rcases value with ⟨value, hs, hh, hp, hc⟩ + unfold decodeObject? + split + · rename_i auth + have he : canonicalObject? address bytes auth (Ixon.runGetExact Ixon.getConstant bytes) rfl = + canonicalObject? address bytes auth (.ok value) hp := by congr 1 + rw [he] + simp [canonicalObject?, hc] + · rename_i h + exact False.elim (h ⟨hs, hh⟩) + +theorem decodeNatural_complete {address : Address} {bytes : ByteArray} + (value : DecodedNatural address bytes) : decodeNatural? address bytes = some value := by + rcases value with ⟨value, hs, hh, hp, hc⟩ + simp [decodeNatural?, hs, hh, hp, hc] + +structure CachedObject where + address : Address + bytes : ByteArray + decoded : DecodedObject address bytes + +structure CachedNatural where + address : Address + bytes : ByteArray + decoded : DecodedNatural address bytes + +/-- The public driver starts with this empty cache. Entries contain only +canonical, authenticated data; no typing, conversion or admission verdicts. -/ +structure InputCache where + objects : List CachedObject := [] + naturals : List CachedNatural := [] + hits : Nat := 0 + misses : Nat := 0 + +namespace InputCache + +def object? (address : Address) (bytes : ByteArray) : + List CachedObject → Option (DecodedObject address bytes) + | [] => none + | entry :: rest => + if ha : entry.address = address then + if hb : entry.bytes = bytes then some (by simpa only [← ha, ← hb] using entry.decoded) + else object? address bytes rest + else object? address bytes rest + +def natural? (address : Address) (bytes : ByteArray) : + List CachedNatural → Option (DecodedNatural address bytes) + | [] => none + | entry :: rest => + if ha : entry.address = address then + if hb : entry.bytes = bytes then some (by simpa only [← ha, ← hb] using entry.decoded) + else natural? address bytes rest + else natural? address bytes rest + +end InputCache + +structure SourceObject (source : Ixon.Env) extends CachedObject where + fromSource : constantBytes? source address = some bytes + +structure SourceNatural (source : Ixon.Env) extends CachedNatural where + fromSource : source.getBlob? address = some bytes + +abbrev Read (_source : Ixon.Env) := StateT InputCache Option + +def readObject? (source : Ixon.Env) (address : Address) : Read source (SourceObject source) := fun cache => + match hs : constantBytes? source address with + | none => none + | some bytes => + match InputCache.object? address bytes cache.objects with + | some decoded => some (⟨⟨address, bytes, decoded⟩, hs⟩, { cache with hits := cache.hits + 1 }) + | none => do + let decoded ← decodeObject? address bytes + let entry : CachedObject := ⟨address, bytes, decoded⟩ + return (⟨entry, hs⟩, { cache with objects := entry :: cache.objects, misses := cache.misses + 1 }) + +def readNatural? (source : Ixon.Env) (address : Address) : Read source (SourceNatural source) := fun cache => + match hs : source.getBlob? address with + | none => none + | some bytes => + match InputCache.natural? address bytes cache.naturals with + | some decoded => some (⟨⟨address, bytes, decoded⟩, hs⟩, { cache with hits := cache.hits + 1 }) + | none => do + let decoded ← decodeNatural? address bytes + let entry : CachedNatural := ⟨address, bytes, decoded⟩ + return (⟨entry, hs⟩, { cache with naturals := entry :: cache.naturals, misses := cache.misses + 1 }) + +/-- An untrusted finite selection of source addresses. It must include all +source groups and raw blobs required by the independently checked witness. -/ +structure InputSelection where + objects : List Address + naturals : List Address := [] + +structure SourceSnapshot (source : Ixon.Env) where + objects : List (SourceObject source) + naturals : List (SourceNatural source) + +namespace SourceSnapshot + +def blobs (snapshot : SourceSnapshot source) : ConstantBlobs := + snapshot.objects.map fun entry => (entry.address, entry.bytes) + +def literalBlobs (snapshot : SourceSnapshot source) : ConstantBlobs := + snapshot.naturals.map fun entry => (entry.address, entry.bytes) + +def decodedObjects (snapshot : SourceSnapshot source) : Objects := + snapshot.objects.map fun entry => (entry.address, entry.decoded.val) + +def decodedNaturals (snapshot : SourceSnapshot source) : Naturals := + snapshot.naturals.map fun entry => (entry.address, entry.decoded.val) + +theorem objects_decode (snapshot : SourceSnapshot source) : + decodeObjects? snapshot.blobs = some snapshot.decodedObjects := by + rcases snapshot with ⟨objects, naturals⟩ + simp only [blobs, decodedObjects, decodeObjects?] + induction objects with + | nil => rfl + | cons entry rest ih => + dsimp only [bind, pure] at ih + simp only [List.map_cons, List.mapM_cons, decodeObject_complete entry.decoded, + bind, Option.bind_some, ih, pure] + +theorem naturals_decode (snapshot : SourceSnapshot source) : + decodeNaturals? snapshot.literalBlobs = some snapshot.decodedNaturals := by + rcases snapshot with ⟨objects, naturals⟩ + simp only [literalBlobs, decodedNaturals, decodeNaturals?] + induction naturals with + | nil => rfl + | cons entry rest ih => + dsimp only [bind, pure] at ih + simp only [List.map_cons, List.mapM_cons, decodeNatural_complete entry.decoded, + bind, Option.bind_some, ih, pure] + +/-- Reusing decoded data saves hashing/parsing. Scope, source metadata, +primitive selection, dependency order and semantic validation still run. -/ +def prepare? (fuel : Nat) (profile : Profile) (target : Address) + (snapshot : SourceSnapshot source) : Option PreparedInput := do + if !((snapshot.blobs ++ snapshot.literalBlobs).map Prod.fst).Nodup then none else do + let signature ← readSignature? profile snapshot.decodedObjects + let input ← readProofInput? fuel snapshot.decodedObjects target snapshot.decodedNaturals + return ⟨signature, input⟩ + +theorem prepare_eq (fuel : Nat) (profile : Profile) (target : Address) + (snapshot : SourceSnapshot source) : + Ix.Certified.prepare? fuel profile target snapshot.blobs snapshot.literalBlobs = + snapshot.prepare? fuel profile target := by + simp only [Ix.Certified.prepare?, prepare?, snapshot.objects_decode, snapshot.naturals_decode, + bind, Option.bind_some] + +/-- Each byte string consumed by the cached checker comes from the current +source environment, even when that address occurred in an earlier request. -/ +theorem objects_from_source (snapshot : SourceSnapshot source) {address : Address} {bytes : ByteArray} + (h : (address, bytes) ∈ snapshot.blobs) : constantBytes? source address = some bytes := by + obtain ⟨entry, _, he⟩ := List.mem_map.mp h + cases he + exact entry.fromSource + +theorem naturals_from_source (snapshot : SourceSnapshot source) {address : Address} {bytes : ByteArray} + (h : (address, bytes) ∈ snapshot.literalBlobs) : source.getBlob? address = some bytes := by + obtain ⟨entry, _, he⟩ := List.mem_map.mp h + cases he + exact entry.fromSource + +end SourceSnapshot + +/-- Failure returns no partially loaded snapshot or semantic verdict. -/ +def readSnapshot? (fuel : Nat) (source : Ixon.Env) (selection : InputSelection) : + Read source (SourceSnapshot source) := do + if selection.objects.length + selection.naturals.length > fuel then failure else do + let objects ← selection.objects.mapM (readObject? source) + let naturals ← selection.naturals.mapM (readNatural? source) + return ⟨objects, naturals⟩ + +/-- A receipt is produced only after complete checking against the snapshot. +All semantic fields are computed by the validator; they are not input data. -/ +structure SourceReceipt (source : Ixon.Env) (fuel : Nat) (profile : Profile) (target : Address) + (witness : ProofWitness Address) where + snapshot : SourceSnapshot source + prepared : PreparedInput + preparation : snapshot.prepare? fuel profile target = some prepared + proof : CheckedProof.{0,v} prepared.signature prepared.input + checked : checkProofCertified fuel prepared.signature prepared.input witness = some proof + +def checkSource? (fuel : Nat) (source : Ixon.Env) (profile : Profile) (target : Address) + (selection : InputSelection) (witness : ProofWitness Address) : + Read source (SourceReceipt.{v} source fuel profile target witness) := do + let snapshot ← readSnapshot? fuel source selection + match hp : snapshot.prepare? fuel profile target with + | none => failure + | some prepared => + match hc : checkProofCertified fuel prepared.signature prepared.input witness with + | none => failure + | some proof => return ⟨snapshot, prepared, hp, proof, hc⟩ + +namespace SourceReceipt + +theorem serialized (receipt : SourceReceipt.{v} source fuel profile target witness) : + acceptsSerialized.{v} fuel profile target receipt.snapshot.blobs witness receipt.snapshot.literalBlobs = true := by + have hp := receipt.snapshot.prepare_eq fuel profile target + rw [receipt.preparation] at hp + simp only [acceptsSerialized, hp, acceptsCertified, receipt.checked, Option.isSome_some] + +theorem has_model (receipt : SourceReceipt.{v} source fuel profile target witness) + (V : Type v) [SetTheory V] (levels : List Nat) (env : Nat → V) : + ∃ constants : Assignment Address V, + receipt.prepared.signature.Compatible receipt.proof.environment.entries constants ∧ + WellDenoted constants levels env receipt.proof.proof.val ∧ + WellDenoted constants levels env receipt.proof.proposition.val ∧ + interp constants levels env receipt.proof.proof.val ∈ˢ + interp constants levels env receipt.proof.proposition.val := by + have ha : acceptsCertified.{0,v} fuel receipt.prepared.signature receipt.prepared.input witness = true := by + simp only [acceptsCertified, receipt.checked, Option.isSome_some] + obtain ⟨result, hr, constants, hM, hW, hP, hm⟩ := accepted_has_model ha V levels env + have he : result = receipt.proof := Option.some.inj (hr.symm.trans receipt.checked) + subst result + exact ⟨constants, hM, hW, hP, hm⟩ + +end SourceReceipt + + +namespace SourceSnapshot + +def prepareStore? (fuel : Nat) (profile : Profile) (subjects : List Address) + (snapshot : SourceSnapshot source) : Option PreparedStore := do + if !((snapshot.blobs ++ snapshot.literalBlobs).map Prod.fst).Nodup then none else + prepareStoreObjects? fuel profile subjects snapshot.decodedObjects snapshot.decodedNaturals + +theorem prepareStore_eq (fuel : Nat) (profile : Profile) (subjects : List Address) + (snapshot : SourceSnapshot source) : + Ix.Certified.prepareStore? fuel profile subjects snapshot.blobs snapshot.literalBlobs = + snapshot.prepareStore? fuel profile subjects := by + simp only [Ix.Certified.prepareStore?, prepareStore?, snapshot.objects_decode, snapshot.naturals_decode, + bind, Option.bind_some] + +end SourceSnapshot + +structure StoreReceipt (source : Ixon.Env) (fuel : Nat) (profile : Profile) (subjects : List Address) + (witness : List (DeclarationWitness Address)) where + snapshot : SourceSnapshot source + prepared : PreparedStore + preparation : snapshot.prepareStore? fuel profile subjects = some prepared + result : CheckedStore.{0,v} prepared.signature prepared.store prepared.targets + checked : checkStoreCertified fuel prepared.signature prepared.store prepared.targets witness = some result + +def checkSourceStore? (fuel : Nat) (source : Ixon.Env) (profile : Profile) (subjects : List Address) + (selection : InputSelection) (witness : List (DeclarationWitness Address)) : + Read source (StoreReceipt.{v} source fuel profile subjects witness) := do + let snapshot ← readSnapshot? fuel source selection + match hp : snapshot.prepareStore? fuel profile subjects with + | none => failure + | some prepared => + match hc : checkStoreCertified fuel prepared.signature prepared.store prepared.targets witness with + | none => failure + | some result => return ⟨snapshot, prepared, hp, result, hc⟩ + +namespace StoreReceipt + +theorem serialized (receipt : StoreReceipt.{v} source fuel profile subjects witness) : + acceptsSerializedStore.{v} fuel profile subjects receipt.snapshot.blobs witness receipt.snapshot.literalBlobs = true := by + have hp := receipt.snapshot.prepareStore_eq fuel profile subjects + rw [receipt.preparation] at hp + simp only [acceptsSerializedStore, hp, acceptsStoreCertified, receipt.checked, Option.isSome_some] + +theorem has_model (receipt : StoreReceipt.{v} source fuel profile subjects witness) + (V : Type v) [SetTheory V] : + ∃ constants : Assignment Address V, + receipt.prepared.signature.Compatible receipt.result.environment.entries constants ∧ + ∀ r ∈ receipt.prepared.targets, ∃ entry, receipt.result.environment.entries r = some entry ∧ + EntrySource receipt.prepared.signature receipt.prepared.store r entry ∧ + ∀ levels, levels.length = entry.universes → ∀ env : Nat → V, + WellDenoted constants levels env entry.type ∧ + constants r levels ∈ˢ interp constants levels env entry.type := by + have ha : acceptsStoreCertified.{0,v} fuel receipt.prepared.signature receipt.prepared.store + receipt.prepared.targets witness = true := by + simp only [acceptsStoreCertified, receipt.checked, Option.isSome_some] + obtain ⟨result, hr, hM⟩ := accepted_store_has_model ha V + have he : result = receipt.result := Option.some.inj (hr.symm.trans receipt.checked) + subst result + exact hM + +end StoreReceipt + +end Ix.Certified diff --git a/Ix/Certified/Ixon.lean b/Ix/Certified/Ixon.lean new file mode 100644 index 000000000..303d1f328 --- /dev/null +++ b/Ix/Certified/Ixon.lean @@ -0,0 +1,225 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Ixon +import Ix.Theory.Certified + +/-! +# Ixon input adapter for the certified profile + +This adapter checks table indices, projection ownership/kinds, ordinary Lean +binder modes, and bounded sharing expansion. It preserves block/member +positions and constructor metadata, including checked constructor positions. +Natural literals use a separately authenticated canonical blob table. +Lets and strings are not enabled here. Semantic admission independently +restricts inductive shapes. Byte authentication and the public claim binding +are separate from this structural adapter. +-/ + +namespace Ix.Certified + +open Ix.Theory +open Ix.Theory.Certified + +instance addressDecidableEq : DecidableEq Address := fun a b => + if h : a.hash = b.hash then + .isTrue (by cases a; cases b; cases h; rfl) + else .isFalse (fun he => h (congrArg Address.hash he)) + +def lookup (entries : List (Address × α)) (address : Address) : Option α := + match entries with + | [] => none + | (key, value) :: rest => if address = key then some value else lookup rest address + +theorem lookup_isSome (entries : List (Address × α)) (address : Address) : + (lookup entries address).isSome ↔ address ∈ entries.map Prod.fst := by + induction entries with + | nil => simp [lookup] + | cons entry rest ih => + obtain ⟨key, value⟩ := entry + by_cases h : address = key <;> simp_all [lookup] + +def readLevel : Ixon.Univ → VLevel + | .zero => .zero + | .succ u => .succ (readLevel u) + | .max u v => .max (readLevel u) (readLevel v) + | .imax u v => .imax (readLevel u) (readLevel v) + | .var i => .param i.toNat + +abbrev Objects := List (Address × Ixon.Constant) +abbrev Naturals := List (Address × Nat) + +def mutMember? (objects : Objects) (block : Address) (index : UInt64) : Option Ixon.MutConst := do + let source ← lookup objects block + let .muts members := source.info | none + members[index.toNat]? + +/-- Projection wrappers must refer to the matching source member kind. -/ +def resolveReference? (objects : Objects) (address : Address) : Option (ConstRef Address) := do + let source ← lookup objects address + match source.info with + | .defn _ | .recr _ | .axio _ | .quot _ => some (.member address 0) + | .iPrj projection => do + let .indc _ ← mutMember? objects projection.block projection.idx | none + return .member projection.block projection.idx.toNat + | .rPrj projection => do + let .recr _ ← mutMember? objects projection.block projection.idx | none + return .member projection.block projection.idx.toNat + | .dPrj projection => do + let .defn _ ← mutMember? objects projection.block projection.idx | none + return .member projection.block projection.idx.toNat + | .cPrj projection => do + let .indc family ← mutMember? objects projection.block projection.idx | none + let _ ← family.ctors[projection.cidx.toNat]? + return .ctor projection.block projection.idx.toNat projection.cidx.toNat + | .muts _ => none + +def readLevels? (source : Ixon.Constant) (indices : Array UInt64) : Option (List VLevel) := + indices.toList.mapM fun index => (source.univs[index.toNat]?).map readLevel + +def readExpr? (fuel : Nat) (objects : Objects) (naturals : Naturals) (block : Address) (source : Ixon.Constant) : + Ixon.Expr → Option (VExpr Address) := + match fuel with + | 0 => fun _ => none + | fuel + 1 => fun expression => + match expression with + | .var index => some (.bvar index.toNat) + | .sort index => (source.univs[index.toNat]?).map (VExpr.sort ∘ readLevel) + | .ref index levels => do + let address ← source.refs[index.toNat]? + let reference ← resolveReference? objects address + let levels ← readLevels? source levels + return .const reference levels + | .recur index levels => do + let levels ← readLevels? source levels + return .const (.member block index.toNat) levels + | .app f a => do + let f ← readExpr? fuel objects naturals block source f + let a ← readExpr? fuel objects naturals block source a + return .app f a + | .lam .many A b => do + let A ← readExpr? fuel objects naturals block source A + let b ← readExpr? fuel objects naturals block source b + return .lam A b + | .all .many .shared A B => do + let A ← readExpr? fuel objects naturals block source A + let B ← readExpr? fuel objects naturals block source B + return .forallE A B + | .prj owner field value => do + let address ← source.refs[owner.toNat]? + let ownerSource ← lookup objects address + let .iPrj _ := ownerSource.info | none + let reference ← resolveReference? objects address + let value ← readExpr? fuel objects naturals block source value + return .proj reference field.toNat value + | .nat index => do + let address ← source.refs[index.toNat]? + return .natLit (← lookup naturals address) + | .share index => do + let shared ← source.sharing[index.toNat]? + readExpr? fuel objects naturals block source shared + | _ => none + +def readSafety : Ix.DefinitionSafety → Safety + | .safe => .safe + | .unsaf => .unsafe + | .part => .partial + +def readDefKind : Ix.DefKind → Ix.Theory.DefKind + | .defn => .definition + | .thm => .theorem + | .opaq => .opaque + +def readQuotKind : Ix.QuotKind → Ix.Theory.QuotKind + | .type => .type + | .ctor => .ctor + | .lift => .lift + | .ind => .ind + +def readDefinition? (fuel : Nat) (objects : Objects) (naturals : Naturals) (block : Address) + (source : Ixon.Constant) (definition : Ixon.Definition) : Option (Const Address) := do + let type ← readExpr? fuel objects naturals block source definition.typ + let body ← readExpr? fuel objects naturals block source definition.value + return .defn definition.lvls.toNat (readDefKind definition.kind) type body + (readSafety definition.safety) + +def readRecursor? (fuel : Nat) (objects : Objects) (naturals : Naturals) (block : Address) + (source : Ixon.Constant) (recursor : Ixon.Recursor) : Option (Const Address) := do + let type ← readExpr? fuel objects naturals block source recursor.typ + let rules ← recursor.rules.toList.mapM fun rule => do + let rhs ← readExpr? fuel objects naturals block source rule.rhs + return (⟨rule.fields.toNat, rhs⟩ : RecRule Address) + return .recursor recursor.lvls.toNat recursor.params.toNat recursor.indices.toNat + recursor.motives.toNat recursor.minors.toNat type rules recursor.k + (if recursor.isUnsafe then .unsafe else .safe) + +def readInductive? (fuel : Nat) (objects : Objects) (naturals : Naturals) (block : Address) + (source : Ixon.Constant) (family : Ixon.Inductive) : Option (Const Address) := do + let type ← readExpr? fuel objects naturals block source family.typ + let constructors ← family.ctors.toList.zipIdx.mapM fun (ctor, index) => do + if ctor.cidx.toNat != index then none else do + let type ← readExpr? fuel objects naturals block source ctor.typ + return (⟨ctor.lvls.toNat, ctor.params.toNat, ctor.fields.toNat, type, + if ctor.isUnsafe then .unsafe else .safe⟩ : Ctor Address) + return .induct family.lvls.toNat family.params.toNat family.indices.toNat + type constructors (if family.isUnsafe then .unsafe else .safe) + +def readMutualMember? (fuel : Nat) (objects : Objects) (naturals : Naturals) (block : Address) + (source : Ixon.Constant) : Ixon.MutConst → Option (Const Address) + | .defn definition => readDefinition? fuel objects naturals block source definition + | .recr recursor => readRecursor? fuel objects naturals block source recursor + | .indc family => readInductive? fuel objects naturals block source family + +def readBlock? (fuel : Nat) (objects : Objects) (naturals : Naturals) (block : Address) + (source : Ixon.Constant) : Option (Block Address) := + match source.info with + | .defn definition => do return ⟨[← readDefinition? fuel objects naturals block source definition]⟩ + | .recr recursor => do return ⟨[← readRecursor? fuel objects naturals block source recursor]⟩ + | .axio declaration => do + let type ← readExpr? fuel objects naturals block source declaration.typ + return ⟨[.axiom declaration.lvls.toNat type (if declaration.isUnsafe then .unsafe else .safe)]⟩ + | .quot quotient => do + let type ← readExpr? fuel objects naturals block source quotient.typ + return ⟨[.quot (readQuotKind quotient.kind) quotient.lvls.toNat type]⟩ + | .muts members => do + return ⟨← members.toList.mapM (readMutualMember? fuel objects naturals block source)⟩ + | _ => none + +def isProjection : Ixon.ConstantInfo → Bool + | .iPrj _ | .rPrj _ | .dPrj _ | .cPrj _ => true + | _ => false + +def readBlocks? (fuel : Nat) (objects : Objects) (naturals : Naturals) : + Objects → Option (List (Address × Block Address)) + | [] => some [] + | (address, source) :: rest => do + if isProjection source.info then + let _ ← resolveReference? objects address + readBlocks? fuel objects naturals rest + else + let block ← readBlock? fuel objects naturals address source + let rest ← readBlocks? fuel objects naturals rest + return (address, block) :: rest + +def readStore? (fuel : Nat) (objects : Objects) (naturals : Naturals := []) : Option (Store Address) := do + if (objects.map Prod.fst).Nodup then + let blocks ← readBlocks? fuel objects naturals objects + if h : (blocks.map Prod.fst).Nodup then + return ⟨blocks.map Prod.fst, h, lookup blocks, lookup_isSome blocks⟩ + else none + else none + +def readProofInput? (fuel : Nat) (objects : Objects) (target : Address) + (naturals : Naturals := []) : + Option (ProofInput Address) := do + let store ← readStore? fuel objects naturals + let reference ← resolveReference? objects target + let declaration ← store.lookup reference + if declaration.uvars ≤ fuel then + return ⟨store, declaration.uvars, .const reference (VLevel.params declaration.uvars), + declaration.type⟩ + else none + +end Ix.Certified diff --git a/Ix/Certified/Main.lean b/Ix/Certified/Main.lean new file mode 100644 index 000000000..0570b7cec --- /dev/null +++ b/Ix/Certified/Main.lean @@ -0,0 +1,8 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Command + +def main := Ix.Certified.Command.main diff --git a/Ix/Certified/ModelHints.lean b/Ix/Certified/ModelHints.lean new file mode 100644 index 000000000..4d65dec82 --- /dev/null +++ b/Ix/Certified/ModelHints.lean @@ -0,0 +1,104 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Bytes +import Ix.Theory.Certificate.Modeled + +/-! Optional untrusted model-package hints refer to actual stored objects. +No mathematical declaration is imported from JSON: model definitions and +equation proofs come from the selected authenticated source store and are +admitted before the original block by the ordinary certified checker. -/ + +namespace Ix.Certified + +open Ix.Theory + +structure ModelProofHint where + equality : Address + reflexivity : Address + eliminator : Address + proof : Address + +structure ModelRuleHints where + owner : Address + proofs : List (Option ModelProofHint) + +structure ModelHint where + source : Address + recursors : List Address + targets : List Address + proofs : List ModelRuleHints := [] + +def modelCandidate? (objects : Objects) (store : Store Address) (hint : ModelHint) : + Option (Certificate.Modeled.Candidate Address) := do + let recursors ← hint.recursors.mapM (resolveReference? objects) + let targets ← hint.targets.mapM (resolveReference? objects) + let proofs ← hint.proofs.mapM fun rules => do + let owner ← resolveReference? objects rules.owner + let universes ← store.uvars owner + let proofs ← rules.proofs.mapM fun proof => proof.mapM fun proof => do + let equality ← resolveReference? objects proof.equality + let reflexivity ← resolveReference? objects proof.reflexivity + let eliminator ← resolveReference? objects proof.eliminator + let ref ← resolveReference? objects proof.proof + return (⟨equality, reflexivity, eliminator, + .const ref ((List.range universes).map VLevel.param)⟩ : Certificate.Modeled.ProofHint Address) + return (owner, proofs) + return ⟨hint.source, recursors, targets, proofs⟩ + +def modelCandidates? (objects : Objects) (store : Store Address) (hints : List ModelHint) : + Option (List (Certificate.Modeled.Candidate Address)) := hints.mapM (modelCandidate? objects store) + +namespace ModelHint + +def address (value : String) : Except String Address := + match Address.fromString value with + | none => .error "expected a 32-byte hexadecimal model address" + | some address => .ok address + +def readProof (json : Lean.Json) : Except String ModelProofHint := do + return ⟨← address (← json.getObjValAs? String "equality"), + ← address (← json.getObjValAs? String "reflexivity"), + ← address (← json.getObjValAs? String "eliminator"), + ← address (← json.getObjValAs? String "proof")⟩ + +def readRules (json : Lean.Json) : Except String ModelRuleHints := do + let owner ← address (← json.getObjValAs? String "owner") + let values ← json.getObjValAs? (List (Option Lean.Json)) "proofs" + let proofs ← values.mapM (Option.mapM readProof) + return ⟨owner, proofs⟩ + +def read (json : Lean.Json) : Except String ModelHint := do + let source ← address (← json.getObjValAs? String "source") + let recursors ← (← json.getObjValAs? (List String) "recursors").mapM address + let targets ← (← json.getObjValAs? (List String) "targets").mapM address + let proofs ← match json.getObjVal? "proofs" with + | .error _ => pure [] + | .ok values => (← values.getArr?).toList.mapM readRules + return ⟨source, recursors, targets, proofs⟩ + +def readOptional (json : Lean.Json) : Except String (List ModelHint) := do + match json.getObjVal? "models" with + | .error _ => pure [] + | .ok models => (← models.getArr?).toList.mapM read + +def proofJson (hint : ModelProofHint) : Lean.Json := Lean.Json.mkObj [ + ("equality", Lean.toJson (hexOfBytes hint.equality.hash)), + ("reflexivity", Lean.toJson (hexOfBytes hint.reflexivity.hash)), + ("eliminator", Lean.toJson (hexOfBytes hint.eliminator.hash)), + ("proof", Lean.toJson (hexOfBytes hint.proof.hash))] + +def rulesJson (hint : ModelRuleHints) : Lean.Json := Lean.Json.mkObj [ + ("owner", Lean.toJson (hexOfBytes hint.owner.hash)), + ("proofs", Lean.toJson (hint.proofs.map (Option.map proofJson)))] + +def json (hint : ModelHint) : Lean.Json := Lean.Json.mkObj [ + ("source", Lean.toJson (hexOfBytes hint.source.hash)), + ("recursors", Lean.toJson (hint.recursors.map (hexOfBytes ·.hash))), + ("targets", Lean.toJson (hint.targets.map (hexOfBytes ·.hash))), + ("proofs", Lean.toJson (hint.proofs.map rulesJson))] + +end ModelHint +end Ix.Certified diff --git a/Ix/Certified/ModeledAudit.lean b/Ix/Certified/ModeledAudit.lean new file mode 100644 index 000000000..738fe740b --- /dev/null +++ b/Ix/Certified/ModeledAudit.lean @@ -0,0 +1,56 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.AuditSupport +import Ix.Certified.ClaimCommand +import Ix.Certified.Command + +/-! Trust, runtime and premise inventory of the versioned semantic claim +checker, source command and actual TcM acceptance boundary. -/ + +open Lean Lean.Elab Command + +namespace Ix.Certified.ModeledAudit + +def roots : Array Lean.Name := #[ + `Ix.Theory.Certified.Modeled.check?_sound, + `Ix.Theory.Certified.Modeled.checkEquation?_sound, + `Ix.Theory.Certified.Modeled.assignment_realizes, + `Ix.Theory.Certified.Modeled.assignment_agrees, + `Ix.Theory.Certified.checkModeledExtension?, + `Ix.Theory.Certificate.Modeled.witness?, + `Ix.Theory.Certificate.sourceGroup?, + `Ix.Theory.Certificate.proofWitness?, + `Ix.Certified.modelCandidate?, `Ix.Certified.modelCandidates?, + `Ix.Certified.ModelHint.readOptional, `Ix.Certified.ModelHint.read, + `Ix.Certified.suggestSource?, `Ix.Certified.suggestStore?, + `Ix.Certified.Command.readRequest, `Ix.Certified.Command.run_success, + `Ix.Certified.suggestLogical?, `Ix.Certified.ClaimCommand.readRequest, + `Ix.Certified.ClaimCommand.run_meaning, + `Ix.Kernel.acceptsCertifiedSource, `Ix.Kernel.acceptsCertifiedStoreSource, + `Ix.Kernel.accepted_tc_claim_meaning] + +def premises : Array Lean.Name := #[ + `Ix.Certified.Protocol.current, + `Ix.Certified.ModelHint.mk, `Ix.Certified.ModelRuleHints.mk, + `Ix.Certified.ModelProofHint.mk, + `Ix.Theory.Certificate.Modeled.Candidate.mk, + `Ix.Theory.Certificate.Modeled.ProofHint.mk, + `Ix.Theory.Certified.Modeled.Witness.mk, + `Ix.Theory.Certified.Modeled.Checked.mk, + `Ix.Theory.Certified.Modeled.SourceMatches, + `Ix.Theory.Certified.Modeled.sourceRefs?, + `Ix.Theory.Certified.Modeled.recursorSource?, + `Ix.Theory.Certified.Modeled.majorSource?, + `Ix.Theory.Certified.Modeled.ruleSource?, + `Ix.Theory.Certified.Modeled.Companion.entry, + `Ix.Theory.Certified.Modeled.CompanionChecked.mk, + `Ix.Theory.Certified.Modeled.CheckedCompanions.mk, + `Ix.Theory.Certified.Modeled.CheckedEquation.mk, + `Ix.Theory.Certified.Modeled.checkEquation?] + +run_cmd AuditSupport.report "modeled source" roots premises + +end Ix.Certified.ModeledAudit diff --git a/Ix/Certified/NOTICE b/Ix/Certified/NOTICE new file mode 100644 index 000000000..41ed404df --- /dev/null +++ b/Ix/Certified/NOTICE @@ -0,0 +1,22 @@ +Certified source and claim adapters + +Copyright (c) 2026 Argument Computer Corporation. +Licensed under MIT OR Apache-2.0; see LICENSE-MIT and LICENSE-APACHE at the +repository root. + +The source and claim adapters were reconstructed from the authenticated +ix-pilot.patch and ix-c3.patch through ix-c7.patch supplied with the former +Lean4Ix consistency work. Original adapter files, patches, source identities, +reports, licenses and differential fixtures are retained in +Tests/Fixtures/Certified/c7-handoff.tar.gz. Individual source and maintained +file identities appear in Tests/Certified/ImportManifest.lean. + +Maintained theory imports use Ix.Theory, and the checker wrappers use +Ix.Kernel. The five historical audits now traverse full checked declaration +graphs and transitive runtime workers. The unused historical native profiling +counter module is preserved in the archive but is not maintained or linked. + +The set model and its separate attribution are maintained under Ix/Theory. +The old checkout is not a build or test dependency. This handoff archive +contains the selected adapter sources and regression evidence, not the +complete historical Ix base checkout. diff --git a/Ix/Certified/Reveal.lean b/Ix/Certified/Reveal.lean new file mode 100644 index 000000000..993a492ca --- /dev/null +++ b/Ix/Certified/Reveal.lean @@ -0,0 +1,155 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.ClaimMeaning + +namespace Ix.Certified + +def Selected {α : Type _} (expected : Option α) (actual : α) : Prop := + match expected with + | none => True + | some value => value = actual + +instance {α : Type _} [DecidableEq α] (expected : Option α) (actual : α) : + Decidable (Selected expected actual) := by + unfold Selected + split <;> infer_instance + +/-- The structural reveal protocol commits to the exact serialized raw +expression, including its original table indices and sharing references. -/ +def expressionAddress (expression : Ixon.Expr) : Address := + Address.blake3 (Ixon.runPut (Ixon.putExpr expression)) + +def ConstructorMatches (expected : Ix.RevealConstructorInfo) (actual : Ixon.Constructor) : Prop := + Selected expected.isUnsafe actual.isUnsafe ∧ Selected expected.lvls actual.lvls ∧ + Selected expected.cidx actual.cidx ∧ Selected expected.params actual.params ∧ + Selected expected.fields actual.fields ∧ Selected expected.typ (expressionAddress actual.typ) + +instance (expected : Ix.RevealConstructorInfo) (actual : Ixon.Constructor) : + Decidable (ConstructorMatches expected actual) := inferInstanceAs (Decidable (_ ∧ _ ∧ _ ∧ _ ∧ _ ∧ _)) + +def ConstructorsMatch (expected : Option (Array (UInt64 × Ix.RevealConstructorInfo))) + (actual : Array Ixon.Constructor) : Prop := + match expected with + | none => True + | some constructors => ∀ pair ∈ constructors.toList, + match actual[pair.1.toNat]? with + | none => False + | some constructor => ConstructorMatches pair.2 constructor + +instance (expected : Option (Array (UInt64 × Ix.RevealConstructorInfo))) (actual : Array Ixon.Constructor) : + Decidable (ConstructorsMatch expected actual) := by + unfold ConstructorsMatch + split + · infer_instance + · apply @List.decidableBAll _ _ (fun pair => by split <;> infer_instance) + +def RulesMatch (expected : Option (Array Ix.RevealRecursorRule)) (actual : Array Ixon.RecursorRule) : Prop := + match expected with + | none => True + | some rules => ∀ rule ∈ rules.toList, + match actual[rule.ruleIdx.toNat]? with + | none => False + | some actual => rule.fields = actual.fields ∧ rule.rhs = expressionAddress actual.rhs + +instance (expected : Option (Array Ix.RevealRecursorRule)) (actual : Array Ixon.RecursorRule) : + Decidable (RulesMatch expected actual) := by + unfold RulesMatch + split + · infer_instance + · apply @List.decidableBAll _ _ (fun rule => by split <;> infer_instance) + +def MutMatches (expected : Ix.RevealMutConstInfo) (actual : Ixon.MutConst) : Prop := + match expected, actual with + | .defn kind safety levels type value, .defn actual => + Selected kind actual.kind ∧ Selected safety actual.safety ∧ Selected levels actual.lvls ∧ + Selected type (expressionAddress actual.typ) ∧ Selected value (expressionAddress actual.value) + | .indc safety levels params indices type ctors, .indc actual => + Selected safety actual.isUnsafe ∧ Selected levels actual.lvls ∧ Selected params actual.params ∧ + Selected indices actual.indices ∧ Selected type (expressionAddress actual.typ) ∧ ConstructorsMatch ctors actual.ctors + | .recr k safety levels params indices motives minors type rules, .recr actual => + Selected k actual.k ∧ Selected safety actual.isUnsafe ∧ Selected levels actual.lvls ∧ + Selected params actual.params ∧ Selected indices actual.indices ∧ Selected motives actual.motives ∧ + Selected minors actual.minors ∧ Selected type (expressionAddress actual.typ) ∧ RulesMatch rules actual.rules + | _, _ => False + +instance (expected : Ix.RevealMutConstInfo) (actual : Ixon.MutConst) : Decidable (MutMatches expected actual) := by + unfold MutMatches + cases expected <;> cases actual <;> infer_instance + +def ComponentsMatch (expected : Array (UInt64 × Ix.RevealMutConstInfo)) (actual : Array Ixon.MutConst) : Prop := + ∀ pair ∈ expected.toList, match actual[pair.1.toNat]? with + | none => False + | some member => MutMatches pair.2 member + +instance (expected : Array (UInt64 × Ix.RevealMutConstInfo)) (actual : Array Ixon.MutConst) : + Decidable (ComponentsMatch expected actual) := by + unfold ComponentsMatch + apply @List.decidableBAll _ _ (fun pair => by split <;> infer_instance) + +def RevealMatches (expected : Ix.RevealConstantInfo) (actual : Ixon.ConstantInfo) : Prop := + match expected, actual with + | .defn kind safety levels type value, .defn actual => + Selected kind actual.kind ∧ Selected safety actual.safety ∧ Selected levels actual.lvls ∧ + Selected type (expressionAddress actual.typ) ∧ Selected value (expressionAddress actual.value) + | .recr k safety levels params indices motives minors type rules, .recr actual => + Selected k actual.k ∧ Selected safety actual.isUnsafe ∧ Selected levels actual.lvls ∧ + Selected params actual.params ∧ Selected indices actual.indices ∧ Selected motives actual.motives ∧ + Selected minors actual.minors ∧ Selected type (expressionAddress actual.typ) ∧ RulesMatch rules actual.rules + | .axio safety levels type, .axio actual => + Selected safety actual.isUnsafe ∧ Selected levels actual.lvls ∧ Selected type (expressionAddress actual.typ) + | .quot kind levels type, .quot actual => + Selected kind actual.kind ∧ Selected levels actual.lvls ∧ Selected type (expressionAddress actual.typ) + | .cPrj index constructor block, .cPrj actual => + Selected index actual.idx ∧ Selected constructor actual.cidx ∧ Selected block actual.block + | .iPrj index block, .iPrj actual | .rPrj index block, .rPrj actual | .dPrj index block, .dPrj actual => + Selected index actual.idx ∧ Selected block actual.block + | .muts components, .muts actual => ComponentsMatch components actual + | _, _ => False + +instance (expected : Ix.RevealConstantInfo) (actual : Ixon.ConstantInfo) : Decidable (RevealMatches expected actual) := by + unfold RevealMatches + cases expected <;> cases actual <;> infer_instance + +structure RevealWitness where + opening : Ixon.Comm + +structure RevealReceipt (source : Ixon.Env) (commitment : Address) (info : Ix.RevealConstantInfo) + (witness : RevealWitness) where + secretSize : witness.opening.secret.hash.size = 32 + payloadSize : witness.opening.payload.hash.size = 32 + bound : witness.opening.commit = commitment + object : SourceObject source + payload : object.address = witness.opening.payload + fields : RevealMatches info object.decoded.val.info + +def checkReveal? (source : Ixon.Env) (commitment : Address) (info : Ix.RevealConstantInfo) + (witness : RevealWitness) : Read source (RevealReceipt source commitment info witness) := + if hs : witness.opening.secret.hash.size = 32 then + if hp : witness.opening.payload.hash.size = 32 then + if hb : witness.opening.commit = commitment then do + let object ← readObject? source witness.opening.payload + if ha : object.address = witness.opening.payload then + if hf : RevealMatches info object.decoded.val.info then + return ⟨hs, hp, hb, object, ha, hf⟩ + else failure + else failure + else failure + else failure + else failure + +def RevealMeaning (source : Ixon.Env) (commitment : Address) (info : Ix.RevealConstantInfo) : Prop := + ∃ opening : Ixon.Comm, opening.secret.hash.size = 32 ∧ opening.payload.hash.size = 32 ∧ + opening.commit = commitment ∧ ∃ bytes object, ObjectBytes source opening.payload bytes object ∧ + RevealMatches info object.info + +theorem RevealReceipt.meaning (receipt : RevealReceipt source commitment info witness) : + RevealMeaning source commitment info := by + refine ⟨witness.opening, receipt.secretSize, receipt.payloadSize, receipt.bound, + receipt.object.bytes, receipt.object.decoded.val, ?_, receipt.fields⟩ + rw [← receipt.payload] + exact ⟨receipt.object.fromSource, receipt.object.decoded.property⟩ + +end Ix.Certified diff --git a/Ix/Certified/SourceAudit.lean b/Ix/Certified/SourceAudit.lean new file mode 100644 index 000000000..96f5b3f33 --- /dev/null +++ b/Ix/Certified/SourceAudit.lean @@ -0,0 +1,39 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.AuditSupport +import Ix.Certified.SourceMeaning + +/-! The source audit freezes original expression/statement meaning and +its connection to the actual canonical source bytes. Claim composition and +backend execution are audited by their separate roots when connected. -/ + +open Lean Lean.Elab Command + +namespace Ix.Certified.SourceAudit + +def roots : Array Lean.Name := #[ + `Ix.Certified.resolveReference_iff, `Ix.Certified.readLevel_value, + `Ix.Certified.readExpr_sound, `Ix.Certified.ExprReading.unique, + `Ix.Certified.readExpr_fuel_independent, + `Ix.Certified.readBlock_sourceHeader, `Ix.Certified.readStore_sourceHeader, + `Ix.Certified.SourceSnapshot.object_bytes, `Ix.Certified.SourceSnapshot.natural_bytes, + `Ix.Certified.readSignature_sound, + `Ix.Certified.StoreReceipt.subject_meaning, `Ix.Certified.SourceReceipt.proposition_meaning, + `Ix.Certified.StoreReceipt.subject_coverage] + +def premises : Array Lean.Name := #[ + `Ix.Certified.MemberMeaning, `Ix.Certified.ReferenceTarget, `Ix.Certified.ReferenceMeaning, + `Ix.Certified.LevelsReading.nil, `Ix.Certified.LevelsReading.cons, + `Ix.Certified.ExprReading.var, `Ix.Certified.ExprReading.sort, `Ix.Certified.ExprReading.ref, + `Ix.Certified.ExprReading.recur, `Ix.Certified.ExprReading.app, `Ix.Certified.ExprReading.lam, + `Ix.Certified.ExprReading.all, `Ix.Certified.ExprReading.prj, `Ix.Certified.ExprReading.nat, + `Ix.Certified.ExprReading.share, `Ix.Certified.rawHeader?, + `Ix.Certified.ObjectBytes, `Ix.Certified.NaturalBytes, + `Ix.Certified.SignatureReading, `Ix.Certified.SourceDeclarationReading, `Ix.Certified.SubjectReading] + +run_cmd AuditSupport.report "source meaning" roots premises + +end Ix.Certified.SourceAudit diff --git a/Ix/Certified/SourceExpr.lean b/Ix/Certified/SourceExpr.lean new file mode 100644 index 000000000..5f29a49e9 --- /dev/null +++ b/Ix/Certified/SourceExpr.lean @@ -0,0 +1,274 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Ixon + +/-! Structural meaning of original Ixon expressions. References retain their +actual table positions and wrapper ownership; sharing expands only through +the original constant's sharing table. This relation has no typing premise. -/ + +namespace Ix.Certified + +open Ix.Theory + +def MemberMeaning (objects : Objects) (block : Address) (index : UInt64) + (member : Ixon.MutConst) : Prop := + ∃ source members, lookup objects block = some source ∧ source.info = .muts members ∧ + members[index.toNat]? = some member + +theorem mutMember_iff {objects : Objects} {block : Address} {index : UInt64} {member : Ixon.MutConst} : + mutMember? objects block index = some member ↔ MemberMeaning objects block index member := by + constructor + · intro h + unfold mutMember? at h + cases hs : lookup objects block with + | none => simp [hs] at h + | some source => + cases hi : source.info <;> simp [hs, hi] at h + exact ⟨source, _, hs, hi, h⟩ + · rintro ⟨source, members, hs, hi, hm⟩ + simp [mutMember?, hs, hi, hm] + +def ReferenceTarget (objects : Objects) (address : Address) (source : Ixon.Constant) + (ref : ConstRef Address) : Prop := + match source.info with + | .defn _ | .recr _ | .axio _ | .quot _ => ref = .member address 0 + | .iPrj projection => + (∃ family, MemberMeaning objects projection.block projection.idx (.indc family)) ∧ + ref = .member projection.block projection.idx.toNat + | .rPrj projection => + (∃ recursor, MemberMeaning objects projection.block projection.idx (.recr recursor)) ∧ + ref = .member projection.block projection.idx.toNat + | .dPrj projection => + (∃ definition, MemberMeaning objects projection.block projection.idx (.defn definition)) ∧ + ref = .member projection.block projection.idx.toNat + | .cPrj projection => + (∃ family ctor, MemberMeaning objects projection.block projection.idx (.indc family) ∧ + family.ctors[projection.cidx.toNat]? = some ctor) ∧ + ref = .ctor projection.block projection.idx.toNat projection.cidx.toNat + | .muts _ => False + +def ReferenceMeaning (objects : Objects) (address : Address) (ref : ConstRef Address) : Prop := + ∃ source, lookup objects address = some source ∧ ReferenceTarget objects address source ref + +theorem resolveReference_iff {objects : Objects} {address : Address} {ref : ConstRef Address} : + resolveReference? objects address = some ref ↔ ReferenceMeaning objects address ref := by + cases hs : lookup objects address with + | none => simp [resolveReference?, ReferenceMeaning, hs] + | some source => + simp only [ReferenceMeaning, hs, Option.some.injEq] + unfold resolveReference? + simp only [hs, bind, Option.bind_some] + unfold ReferenceTarget + rcases source with ⟨info, sharing, references, universes⟩ + cases info + all_goals dsimp only + all_goals try solve | simp [eq_comm] + all_goals simp only [← mutMember_iff] + all_goals rename_i projection + all_goals cases hm : mutMember? objects projection.block projection.idx with + | none => simp [hm] + | some member => + cases member <;> simp [hm, eq_comm, exists_and_left] + + all_goals rename_i family + all_goals cases hf : family.ctors[projection.cidx.toNat]? <;> simp [eq_comm] + +theorem ReferenceMeaning.unique {objects : Objects} {address : Address} {left right : ConstRef Address} + (hl : ReferenceMeaning objects address left) (hr : ReferenceMeaning objects address right) : left = right := + Option.some.inj ((resolveReference_iff.mpr hl).symm.trans (resolveReference_iff.mpr hr)) + +def sourceLevelValue (levels : List Nat) : Ixon.Univ → Nat + | .zero => 0 + | .succ level => sourceLevelValue levels level + 1 + | .max left right => max (sourceLevelValue levels left) (sourceLevelValue levels right) + | .imax left right => + if sourceLevelValue levels right = 0 then 0 else max (sourceLevelValue levels left) (sourceLevelValue levels right) + | .var index => levels.getD index.toNat 0 + +theorem readLevel_value (level : Ixon.Univ) (levels : List Nat) : + (readLevel level).eval levels = sourceLevelValue levels level := by + induction level <;> simp_all [readLevel, VLevel.eval, VLevel.natIMax, sourceLevelValue] + +inductive LevelsReading (source : Ixon.Constant) : List UInt64 → List VLevel → Prop where + | nil : LevelsReading source [] [] + | cons {index indices level levels} : source.univs[index.toNat]? = some level → + LevelsReading source indices levels → + LevelsReading source (index :: indices) (readLevel level :: levels) + +theorem readLevelsList_sound {source : Ixon.Constant} {indices : List UInt64} {levels : List VLevel} + (h : indices.mapM (fun index => (source.univs[index.toNat]?).map readLevel) = some levels) : + LevelsReading source indices levels := by + induction indices generalizing levels with + | nil => + simp at h + subst levels + exact .nil + | cons index indices ih => + cases hi : source.univs[index.toNat]? with + | none => simp [List.mapM_cons, hi] at h + | some level => + cases ht : indices.mapM (fun index => (source.univs[index.toNat]?).map readLevel) with + | none => simp [List.mapM_cons, hi, ht] at h + | some tail => + simp [List.mapM_cons, hi, ht] at h + subst levels + exact .cons hi (ih ht) + +theorem readLevels_sound {source : Ixon.Constant} {indices : Array UInt64} {levels : List VLevel} + (h : readLevels? source indices = some levels) : LevelsReading source indices.toList levels := + readLevelsList_sound h + +theorem LevelsReading.unique {source : Ixon.Constant} {indices : List UInt64} {left right : List VLevel} + (hl : LevelsReading source indices left) (hr : LevelsReading source indices right) : left = right := by + induction hl generalizing right with + | nil => cases hr; rfl + | cons hs _ ih => + cases hr with + | cons ht hr => + cases Option.some.inj (hs.symm.trans ht) + exact congrArg (_ :: ·) (ih hr) + +/-- A reading of the original source syntax, including all reference and +sharing-table lookups. Unsupported binder modes and literal forms have no +constructor in this relation. -/ +inductive ExprReading (objects : Objects) (naturals : Naturals) (block : Address) (source : Ixon.Constant) : + Ixon.Expr → VExpr Address → Prop where + | var (index) : ExprReading objects naturals block source (.var index) (.bvar index.toNat) + | sort {index level} : source.univs[index.toNat]? = some level → + ExprReading objects naturals block source (.sort index) (.sort (readLevel level)) + | ref {index indices address ref levels} : source.refs[index.toNat]? = some address → + ReferenceMeaning objects address ref → LevelsReading source indices.toList levels → + ExprReading objects naturals block source (.ref index indices) (.const ref levels) + | recur {index indices levels} : LevelsReading source indices.toList levels → + ExprReading objects naturals block source (.recur index indices) (.const (.member block index.toNat) levels) + | app {f a fr ar} : ExprReading objects naturals block source f fr → + ExprReading objects naturals block source a ar → + ExprReading objects naturals block source (.app f a) (.app fr ar) + | lam {A b Ar br} : ExprReading objects naturals block source A Ar → + ExprReading objects naturals block source b br → + ExprReading objects naturals block source (.lam .many A b) (.lam Ar br) + | all {A B Ar Br} : ExprReading objects naturals block source A Ar → + ExprReading objects naturals block source B Br → + ExprReading objects naturals block source (.all .many .shared A B) (.forallE Ar Br) + | prj {owner field value address ownerSource projection ref reading} : + source.refs[owner.toNat]? = some address → lookup objects address = some ownerSource → + ownerSource.info = .iPrj projection → ReferenceMeaning objects address ref → + ExprReading objects naturals block source value reading → + ExprReading objects naturals block source (.prj owner field value) (.proj ref field.toNat reading) + | nat {index address value} : source.refs[index.toNat]? = some address → lookup naturals address = some value → + ExprReading objects naturals block source (.nat index) (.natLit value) + | share {index shared reading} : source.sharing[index.toNat]? = some shared → + ExprReading objects naturals block source shared reading → + ExprReading objects naturals block source (.share index) reading + +theorem readExpr_sound {fuel : Nat} {objects : Objects} {naturals : Naturals} {block : Address} + {source : Ixon.Constant} {expression : Ixon.Expr} {reading : VExpr Address} + (h : readExpr? fuel objects naturals block source expression = some reading) : + ExprReading objects naturals block source expression reading := by + induction fuel generalizing expression reading with + | zero => simp [readExpr?] at h + | succ fuel ih => + cases expression with + | var index => + simp [readExpr?] at h + subst reading + exact .var index + | sort index => + simp only [readExpr?, Option.map_eq_some_iff] at h + obtain ⟨level, hl, rfl⟩ := h + exact .sort hl + | ref index indices => + simp only [readExpr?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨address, ha, ref, hr, levels, hl, rfl⟩ := h + exact .ref ha (resolveReference_iff.mp hr) (readLevels_sound hl) + | recur index indices => + simp only [readExpr?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨levels, hl, rfl⟩ := h + exact .recur (readLevels_sound hl) + | app f a => + simp only [readExpr?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨fr, hf, ar, ha, rfl⟩ := h + exact .app (ih hf) (ih ha) + | lam uses A b => + cases uses <;> simp only [readExpr?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + all_goals try contradiction + obtain ⟨Ar, hA, br, hb, rfl⟩ := h + exact .lam (ih hA) (ih hb) + | all uses owned A B => + cases uses <;> cases owned <;> + simp only [readExpr?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + all_goals try contradiction + obtain ⟨Ar, hA, Br, hB, rfl⟩ := h + exact .all (ih hA) (ih hB) + | prj owner field value => + simp only [readExpr?, bind, Option.bind_eq_some_iff] at h + obtain ⟨address, ha, ownerSource, hs, h⟩ := h + cases hp : ownerSource.info <;> simp only [hp] at h + all_goals try contradiction + simp only [Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨ref, hr, reading, hv, rfl⟩ := h + exact .prj ha hs hp (resolveReference_iff.mp hr) (ih hv) + | nat index => + simp only [readExpr?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨address, ha, value, hv, rfl⟩ := h + exact .nat ha hv + | share index => + simp only [readExpr?, bind, Option.bind_eq_some_iff] at h + obtain ⟨shared, hs, hr⟩ := h + exact .share hs (ih hr) + | str | letE => simp [readExpr?] at h + +/-- The original tables determine one expanded expression. Neither fuel nor +a different sharing-expansion derivation can change the statement. -/ +theorem ExprReading.unique {objects : Objects} {naturals : Naturals} {block : Address} + {source : Ixon.Constant} {expression : Ixon.Expr} {left right : VExpr Address} + (hl : ExprReading objects naturals block source expression left) + (hr : ExprReading objects naturals block source expression right) : left = right := by + induction hl generalizing right with + | var => cases hr; rfl + | sort hs => + cases hr with + | sort ht => cases Option.some.inj (hs.symm.trans ht); rfl + | ref ha hm hl => + cases hr with + | ref hb hn hr => + cases Option.some.inj (ha.symm.trans hb) + exact congr (congrArg VExpr.const (hm.unique hn)) (hl.unique hr) + | recur hl => + cases hr with + | recur hr => exact congrArg (VExpr.const _) (hl.unique hr) + | app _ _ ihf iha => + cases hr with + | app hf ha => exact congr (congrArg VExpr.app (ihf hf)) (iha ha) + | lam _ _ ihA ihb => + cases hr with + | lam hA hb => exact congr (congrArg VExpr.lam (ihA hA)) (ihb hb) + | all _ _ ihA ihB => + cases hr with + | all hA hB => exact congr (congrArg VExpr.forallE (ihA hA)) (ihB hB) + | prj ha _ _ hm _ ih => + cases hr with + | prj hb _ _ hn hv => + cases Option.some.inj (ha.symm.trans hb) + exact congr (congrArg (fun r e => VExpr.proj r _ e) (hm.unique hn)) (ih hv) + | nat ha hv => + cases hr with + | nat hb hw => + cases Option.some.inj (ha.symm.trans hb) + exact congrArg VExpr.natLit (Option.some.inj (hv.symm.trans hw)) + | share hs _ ih => + cases hr with + | share ht hr => + cases Option.some.inj (hs.symm.trans ht) + exact ih hr + +theorem readExpr_fuel_independent {fuel₁ fuel₂ : Nat} {objects : Objects} {naturals : Naturals} + {block : Address} {source : Ixon.Constant} {expression : Ixon.Expr} {left right : VExpr Address} + (hl : readExpr? fuel₁ objects naturals block source expression = some left) + (hr : readExpr? fuel₂ objects naturals block source expression = some right) : left = right := + (readExpr_sound hl).unique (readExpr_sound hr) + +end Ix.Certified diff --git a/Ix/Certified/SourceMeaning.lean b/Ix/Certified/SourceMeaning.lean new file mode 100644 index 000000000..4f8aae276 --- /dev/null +++ b/Ix/Certified/SourceMeaning.lean @@ -0,0 +1,245 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.SourceStore +import Ix.Kernel.Certified + +/-! Authenticated source-statement meaning for successful certified source +runs. The original bytes, raw type and table-resolved reading occur together +in the contract; semantic equality never substitutes for byte identity. -/ + +namespace Ix.Certified + +open Ix.Theory Ix.Theory.Certified Ix.Theory.Model Ix.Theory.Model.SetTheory + +universe v + +def ObjectBytes (source : Ixon.Env) (address : Address) (bytes : ByteArray) (value : Ixon.Constant) : Prop := + constantBytes? source address = some bytes ∧ address.hash.size = 32 ∧ + Address.blake3 bytes = address ∧ Ixon.runGetExact Ixon.getConstant bytes = .ok value ∧ + Ixon.serConstant value = bytes + +def NaturalBytes (source : Ixon.Env) (address : Address) (bytes : ByteArray) (value : Nat) : Prop := + source.getBlob? address = some bytes ∧ address.hash.size = 32 ∧ + Address.blake3 bytes = address ∧ Nat.fromBytesLE bytes.data = value ∧ + ByteArray.mk value.toBytesLE = bytes + +theorem SourceSnapshot.object_bytes (snapshot : SourceSnapshot source) {address : Address} {value : Ixon.Constant} + (h : lookup snapshot.decodedObjects address = some value) : ∃ bytes, ObjectBytes source address bytes value := by + obtain ⟨entry, _, he⟩ := List.mem_map.mp (lookup_mem h) + rcases Prod.mk.inj he with ⟨rfl, rfl⟩ + exact ⟨entry.bytes, entry.fromSource, entry.decoded.property⟩ + +theorem SourceSnapshot.natural_bytes (snapshot : SourceSnapshot source) {address : Address} {value : Nat} + (h : lookup snapshot.decodedNaturals address = some value) : ∃ bytes, NaturalBytes source address bytes value := by + obtain ⟨entry, _, he⟩ := List.mem_map.mp (lookup_mem h) + rcases Prod.mk.inj he with ⟨rfl, rfl⟩ + exact ⟨entry.bytes, entry.fromSource, entry.decoded.property⟩ + +def SignatureReading (profile : Profile) (objects : Objects) (signature : PrimitiveSignature Address) : Prop := + ReferenceMeaning objects profile.falseType signature.falseType ∧ + ReferenceMeaning objects profile.falseElim signature.falseElim ∧ + match profile.natType, signature.natType with + | none, none => True + | some address, some ref => ReferenceMeaning objects address ref + | _, _ => False + +theorem readSignature_sound {profile : Profile} {objects : Objects} {signature : PrimitiveSignature Address} + (h : readSignature? profile objects = some signature) : SignatureReading profile objects signature := by + rcases profile with ⟨falseAddress, elimAddress, natural⟩ + cases natural with + | none => + simp only [readSignature?, bind, Option.bind_some, Option.bind_eq_some_iff] at h + obtain ⟨falseType, hF, falseElim, hE, h⟩ := h + split at h + · cases Option.some.inj h + exact ⟨resolveReference_iff.mp hF, resolveReference_iff.mp hE, trivial⟩ + · cases h + | some address => + simp only [readSignature?, bind, Option.bind_eq_some_iff, Option.map_eq_some_iff] at h + obtain ⟨falseType, hF, falseElim, hE, _, ⟨ref, hn, rfl⟩, h⟩ := h + split at h + · cases Option.some.inj h + exact ⟨resolveReference_iff.mp hF, resolveReference_iff.mp hE, resolveReference_iff.mp hn⟩ + · cases h + +theorem SourceSnapshot.prepareStore_parts {source : Ixon.Env} (snapshot : SourceSnapshot source) + {fuel : Nat} {profile : Profile} {subjects : List Address} {prepared : PreparedStore} + (h : snapshot.prepareStore? fuel profile subjects = some prepared) : + readSignature? profile snapshot.decodedObjects = some prepared.signature ∧ + readStore? fuel snapshot.decodedObjects snapshot.decodedNaturals = some prepared.store ∧ + subjects.flatMapM (subjectReferences? snapshot.decodedObjects) = some prepared.targets := by + unfold SourceSnapshot.prepareStore? at h + split at h + · cases h + · simp only [prepareStoreObjects?, bind, Option.bind_eq_some_iff] at h + obtain ⟨signature, hs, store, hstore, targets, ht, h⟩ := h + split at h + · cases h + · cases Option.some.inj h + exact ⟨hs, hstore, ht⟩ + +theorem SourceSnapshot.prepare_parts {source : Ixon.Env} (snapshot : SourceSnapshot source) + {fuel : Nat} {profile : Profile} {target : Address} {prepared : PreparedInput} + (h : snapshot.prepare? fuel profile target = some prepared) : + readSignature? profile snapshot.decodedObjects = some prepared.signature ∧ + readProofInput? fuel snapshot.decodedObjects target snapshot.decodedNaturals = some prepared.input := by + unfold SourceSnapshot.prepare? at h + split at h + · cases h + · simp only [bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨signature, hs, input, hi, rfl⟩ := h + exact ⟨hs, hi⟩ + +def SourceDeclarationReading (source : Ixon.Env) (objects : Objects) (naturals : Naturals) + (ref : ConstRef Address) (entry : ConstantEntry Address) : Prop := + ∃ bytes declaration rawType, + ObjectBytes source ref.block bytes declaration ∧ lookup objects ref.block = some declaration ∧ + rawHeader? declaration ref = some (entry.universes, rawType) ∧ + ExprReading objects naturals ref.block declaration rawType entry.type.erase + +theorem SourceSnapshot.declaration_reading {source : Ixon.Env} (snapshot : SourceSnapshot source) + {fuel : Nat} {store : Store Address} {ref : ConstRef Address} {entry : ConstantEntry Address} + (h : readStore? fuel snapshot.decodedObjects snapshot.decodedNaturals = some store) + (hh : SourceHeader store ref entry) : + SourceDeclarationReading source snapshot.decodedObjects snapshot.decodedNaturals ref entry := by + obtain ⟨declaration, rawType, hs, ht, hr⟩ := readStore_sourceHeader h hh + obtain ⟨bytes, hb⟩ := snapshot.object_bytes hs + exact ⟨bytes, declaration, rawType, hb, hs, ht, hr⟩ + +/-- Every original requested source declaration is realized in every compatible +model, with its complete canonical bytes and its original raw type retained. -/ +theorem StoreReceipt.subject_meaning + (receipt : StoreReceipt.{v} source fuel profile subjects witness) + {ref : ConstRef Address} (hr : ref ∈ receipt.prepared.targets) : + ∃ entry, receipt.result.environment.entries ref = some entry ∧ + SourceDeclarationReading source receipt.snapshot.decodedObjects receipt.snapshot.decodedNaturals ref entry ∧ + ∀ (V : Type v) [SetTheory V] (constants : Assignment Address V), + receipt.prepared.signature.Compatible receipt.result.environment.entries constants → + ∀ levels, levels.length = entry.universes → ∀ env, + WellDenoted constants levels env entry.type ∧ + constants ref levels ∈ˢ interp constants levels env entry.type := by + obtain ⟨entry, he, hh, hm⟩ := receipt.result.subject_sound hr + exact ⟨entry, he, receipt.snapshot.declaration_reading + (receipt.snapshot.prepareStore_parts receipt.preparation).2.1 hh, hm⟩ + +theorem readProofInput_parts {fuel : Nat} {objects : Objects} {naturals : Naturals} {target : Address} + {input : ProofInput Address} (h : readProofInput? fuel objects target naturals = some input) : + readStore? fuel objects naturals = some input.store ∧ + ∃ ref declaration, resolveReference? objects target = some ref ∧ + input.store.lookup ref = some declaration ∧ input.universes = declaration.uvars ∧ + input.proof = .const ref (VLevel.params input.universes) ∧ input.proposition = declaration.type := by + unfold readProofInput? at h + simp only [bind, Option.bind_eq_some_iff] at h + obtain ⟨store, hs, ref, hr, declaration, hd, h⟩ := h + split at h + · cases Option.some.inj h + exact ⟨hs, ref, declaration, hr, hd, rfl, rfl, rfl⟩ + · cases h + +theorem SourceReceipt.target_entry + (receipt : SourceReceipt.{v} source fuel profile target witness) : + ∃ ref entry, ReferenceMeaning receipt.snapshot.decodedObjects target ref ∧ + receipt.proof.environment.entries ref = some entry ∧ + SourceDeclarationReading source receipt.snapshot.decodedObjects receipt.snapshot.decodedNaturals ref entry ∧ + receipt.prepared.input.universes = entry.universes ∧ + receipt.prepared.input.proposition = entry.type.erase := by + have hp := receipt.snapshot.prepare_parts receipt.preparation + obtain ⟨hs, ref, declaration, hr, hd, hu, he, ht⟩ := readProofInput_parts hp.2 + have hproof : receipt.proof.proof.val = .const ref (VLevel.params receipt.prepared.input.universes) := + AExpr.eq_const_of_erase_eq (receipt.proof.proof.property.1.trans he) + have hmem : (receipt.proof.environment.entries ref).isSome = true := by + simpa [hproof, AExpr.ReferencesIn, AExpr.references] using receipt.proof.proofReferences + cases hf : receipt.proof.environment.entries ref with + | none => simp [hf] at hmem + | some entry => + have hh := receipt.proof.environment.sourceHeader hf + have hu' : declaration.uvars = entry.universes := by simpa [Store.uvars, hd] using hh.universes + have ht' : declaration.type = entry.type.erase := by simpa [Store.type, hd] using hh.type + exact ⟨ref, entry, resolveReference_iff.mp hr, hf, + receipt.snapshot.declaration_reading hs hh, hu.trans hu', ht.trans ht'⟩ + +/-- This is the original serialized proposition's type field, with its exact +source bytes and a validated reading of the original tables. The proposition +is inhabited in every compatible interpretation, not just a chosen model. -/ +theorem SourceReceipt.proposition_meaning + (receipt : SourceReceipt.{v} source fuel profile target witness) : + SignatureReading profile receipt.snapshot.decodedObjects receipt.prepared.signature ∧ + ∃ ref bytes declaration rawType, + ReferenceMeaning receipt.snapshot.decodedObjects target ref ∧ + ObjectBytes source ref.block bytes declaration ∧ + rawHeader? declaration ref = some (receipt.prepared.input.universes, rawType) ∧ + ExprReading receipt.snapshot.decodedObjects receipt.snapshot.decodedNaturals ref.block declaration + rawType receipt.proof.proposition.val.erase ∧ + ∀ (V : Type v) [SetTheory V] (constants : Assignment Address V), + receipt.prepared.signature.Compatible receipt.proof.environment.entries constants → ∀ levels env, + WellDenoted constants levels env receipt.proof.proposition.val ∧ + interp constants levels env receipt.proof.proposition.val ∈ˢ univ 0 ∧ + interp constants levels env receipt.proof.proof.val ∈ˢ + interp constants levels env receipt.proof.proposition.val := by + refine ⟨readSignature_sound (receipt.snapshot.prepare_parts receipt.preparation).1, ?_⟩ + obtain ⟨ref, entry, hr, _, ⟨bytes, declaration, rawType, hb, _, ht, hread⟩, hu, hp⟩ := receipt.target_entry + refine ⟨ref, bytes, declaration, rawType, hr, hb, hu ▸ ht, ?_, ?_⟩ + · rw [receipt.proof.proposition.property.1, hp] + exact hread + · intro V _ constants hM levels env + have hs := receipt.proof.isProp V constants hM.realizes levels env (Context.valid_nil constants levels env) + exact ⟨hs.1, hs.2.2, (receipt.proof.typing V constants hM.realizes levels env + (Context.valid_nil constants levels env)).2.2⟩ + +def SubjectReading (objects : Objects) (subject : Address) (refs : List (ConstRef Address)) : Prop := + ∃ source, lookup objects subject = some source ∧ + match source.info with + | .muts members => members.isEmpty = false ∧ refs = + (members.toList.zipIdx.flatMap fun (member, index) => + .member subject index :: match member with + | .indc family => (List.range family.ctors.size).map (.ctor subject index ·) + | _ => []) + | _ => ∃ ref, ReferenceMeaning objects subject ref ∧ refs = [ref] + +theorem subjectReferences_iff {objects : Objects} {subject : Address} {refs : List (ConstRef Address)} : + subjectReferences? objects subject = some refs ↔ SubjectReading objects subject refs := by + cases hs : lookup objects subject with + | none => simp [subjectReferences?, SubjectReading, hs] + | some source => + rcases source with ⟨info, sharing, references, levels⟩ + cases info <;> + simp only [subjectReferences?, SubjectReading, hs, ← resolveReference_iff, bind, + Option.bind_some, Option.bind_eq_some_iff, pure, Option.some.injEq, exists_eq_left'] + all_goals try solve | simp only [eq_comm] + rename_i members + cases he : members.isEmpty <;> simp [eq_comm] + rfl + +theorem flatMapM_mem_iff {α β : Type} {f : α → Option (List β)} {xs : List α} {ys : List β} + (h : xs.flatMapM f = some ys) (value : β) : + value ∈ ys ↔ ∃ source ∈ xs, ∃ values, f source = some values ∧ value ∈ values := by + induction xs generalizing ys with + | nil => simp at h; subst ys; simp + | cons x xs ih => + simp only [List.flatMapM_cons, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨values, hv, tail, ht, rfl⟩ := h + constructor + · intro hm + rcases List.mem_append.mp hm with hm | hm + · exact ⟨x, List.mem_cons_self .., values, hv, hm⟩ + · obtain ⟨source, hs, values, hf, hm⟩ := (ih ht).mp hm + exact ⟨source, List.mem_cons_of_mem _ hs, values, hf, hm⟩ + · rintro ⟨source, hs, result, hf, hm⟩ + rcases List.mem_cons.mp hs with rfl | hs + · cases Option.some.inj (hv.symm.trans hf) + exact List.mem_append_left _ hm + · exact List.mem_append_right _ ((ih ht).mpr ⟨source, hs, result, hf, hm⟩) + +/-- Exact subject coverage: projection owners, every block member and every +constructor are retained. No unrelated declaration is silently discharged. -/ +theorem StoreReceipt.subject_coverage + (receipt : StoreReceipt.{v} source fuel profile subjects witness) (ref : ConstRef Address) : + ref ∈ receipt.prepared.targets ↔ + ∃ subject ∈ subjects, ∃ refs, SubjectReading receipt.snapshot.decodedObjects subject refs ∧ ref ∈ refs := by + have h := flatMapM_mem_iff (receipt.snapshot.prepareStore_parts receipt.preparation).2.2 ref + simpa only [subjectReferences_iff] using h + +end Ix.Certified diff --git a/Ix/Certified/SourceStore.lean b/Ix/Certified/SourceStore.lean new file mode 100644 index 000000000..2555a34fd --- /dev/null +++ b/Ix/Certified/SourceStore.lean @@ -0,0 +1,318 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.SourceExpr +import Ix.Theory.Certified.Source + +/-! Exact original declaration headers through the source block reader. -/ + +namespace Ix.Certified + +open Ix.Theory Ix.Theory.Certified + +theorem mapM_get_right {α β : Type} {f : α → Option β} {xs : List α} {ys : List β} + {index : Nat} {value : β} (h : xs.mapM f = some ys) (hy : ys[index]? = some value) : + ∃ source, xs[index]? = some source ∧ f source = some value := by + induction xs generalizing ys index with + | nil => simp at h; subst ys; simp at hy + | cons x xs ih => + simp only [List.mapM_cons, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨y, hf, tail, ht, rfl⟩ := h + cases index with + | zero => simp at hy; subst value; exact ⟨x, rfl, hf⟩ + | succ index => + obtain ⟨source, hs, hf⟩ := ih ht hy + exact ⟨source, hs, hf⟩ + +def memberRawHeader : Ixon.MutConst → Nat × Ixon.Expr + | .defn value => (value.lvls.toNat, value.typ) + | .recr value => (value.lvls.toNat, value.typ) + | .indc value => (value.lvls.toNat, value.typ) + +def rawHeader? (source : Ixon.Constant) : ConstRef Address → Option (Nat × Ixon.Expr) + | .member _ index => + match source.info with + | .muts members => members[index]?.map memberRawHeader + | .defn value => if index = 0 then some (value.lvls.toNat, value.typ) else none + | .recr value => if index = 0 then some (value.lvls.toNat, value.typ) else none + | .axio value => if index = 0 then some (value.lvls.toNat, value.typ) else none + | .quot value => if index = 0 then some (value.lvls.toNat, value.typ) else none + | _ => none + | .ctor _ index field => do + let .muts members := source.info | none + let .indc family ← members[index]? | none + let ctor ← family.ctors[field]? + return (ctor.lvls.toNat, ctor.typ) + +def blockHeader? (block : Block Address) : ConstRef Address → Option (Nat × VExpr Address) + | .member _ index => block.members[index]?.map fun declaration => (declaration.uvars, declaration.type) + | .ctor _ index field => do + let .induct _ _ _ _ ctors _ ← block.members[index]? | none + let ctor ← ctors[field]? + return (ctor.uvars, ctor.type) + +theorem readMember_header {fuel : Nat} {objects : Objects} {naturals : Naturals} {block : Address} + {source : Ixon.Constant} {member : Ixon.MutConst} {declaration : Const Address} + (h : readMutualMember? fuel objects naturals block source member = some declaration) : + declaration.uvars = (memberRawHeader member).1 ∧ + readExpr? fuel objects naturals block source (memberRawHeader member).2 = some declaration.type := by + cases member with + | defn value => + simp only [readMutualMember?, readDefinition?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨type, ht, body, _, rfl⟩ := h + exact ⟨rfl, ht⟩ + | recr value => + simp only [readMutualMember?, readRecursor?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨type, ht, rules, _, rfl⟩ := h + exact ⟨rfl, ht⟩ + | indc value => + simp only [readMutualMember?, readInductive?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨type, ht, constructors, _, rfl⟩ := h + exact ⟨rfl, ht⟩ + +theorem readInductive_constructor {fuel : Nat} {objects : Objects} {naturals : Naturals} {block : Address} + {source : Ixon.Constant} {family : Ixon.Inductive} {declaration : Const Address} + {index : Nat} {universes : Nat} {type : VExpr Address} + (h : readInductive? fuel objects naturals block source family = some declaration) + (hc : blockHeader? ⟨[declaration]⟩ (.ctor block 0 index) = some (universes, type)) : + ∃ ctor, family.ctors[index]? = some ctor ∧ ctor.lvls.toNat = universes ∧ + readExpr? fuel objects naturals block source ctor.typ = some type := by + simp only [readInductive?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨familyType, _, ctors, hm, rfl⟩ := h + simp only [blockHeader?, List.getElem?_cons_zero, bind, Option.bind_some, + Option.bind_eq_some_iff, pure, Option.some.injEq, Prod.mk.injEq] at hc + obtain ⟨ctor, hc, hn, ht⟩ := hc + obtain ⟨⟨raw, position⟩, hr, hp⟩ := mapM_get_right hm hc + simp only [List.getElem?_zipIdx, Array.getElem?_toList, Nat.zero_add, + Option.map_eq_some_iff, Prod.mk.injEq] at hr + obtain ⟨raw', hs, rfl, rfl⟩ := hr + split at hp + · contradiction + · simp only [Option.bind_eq_some_iff, Option.some.injEq] at hp + obtain ⟨reading, ht', he⟩ := hp + cases he + exact ⟨raw', hs, hn, ht ▸ ht'⟩ + +theorem readBlock_sourceHeader {fuel : Nat} {objects : Objects} {naturals : Naturals} {block : Address} + {source : Ixon.Constant} {result : Block Address} {ref : ConstRef Address} + {universes : Nat} {type : VExpr Address} + (h : readBlock? fuel objects naturals block source = some result) + (hh : blockHeader? result ref = some (universes, type)) : + ∃ raw, rawHeader? source ref = some (universes, raw) ∧ + ExprReading objects naturals block source raw type := by + rcases source with ⟨info, sharing, references, levels⟩ + cases info with + | defn value => + simp only [readBlock?, readDefinition?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨_, ⟨type, ht, body, _, rfl⟩, rfl⟩ := h + cases ref with + | member owner index => + cases index with + | zero => + simp only [blockHeader?, List.getElem?_cons_zero, Option.map_some, + Const.uvars, Const.type, Option.some.injEq, Prod.mk.injEq] at hh + rcases hh with ⟨rfl, rfl⟩ + exact ⟨_, rfl, readExpr_sound ht⟩ + | succ index => simp [blockHeader?] at hh + | ctor owner index field => cases index <;> simp [blockHeader?] at hh + | recr value => + simp only [readBlock?, readRecursor?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨_, ⟨type, ht, rules, _, rfl⟩, rfl⟩ := h + cases ref with + | member owner index => + cases index with + | zero => + simp only [blockHeader?, List.getElem?_cons_zero, Option.map_some, + Const.uvars, Const.type, Option.some.injEq, Prod.mk.injEq] at hh + rcases hh with ⟨rfl, rfl⟩ + exact ⟨_, rfl, readExpr_sound ht⟩ + | succ index => simp [blockHeader?] at hh + | ctor owner index field => cases index <;> simp [blockHeader?] at hh + | axio value => + simp only [readBlock?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨type, ht, rfl⟩ := h + cases ref with + | member owner index => + cases index with + | zero => + simp only [blockHeader?, List.getElem?_cons_zero, Option.map_some, + Const.uvars, Const.type, Option.some.injEq, Prod.mk.injEq] at hh + rcases hh with ⟨rfl, rfl⟩ + exact ⟨_, rfl, readExpr_sound ht⟩ + | succ index => simp [blockHeader?] at hh + | ctor owner index field => cases index <;> simp [blockHeader?] at hh + | quot value => + simp only [readBlock?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨type, ht, rfl⟩ := h + cases ref with + | member owner index => + cases index with + | zero => + simp only [blockHeader?, List.getElem?_cons_zero, Option.map_some, + Const.uvars, Const.type, Option.some.injEq, Prod.mk.injEq] at hh + rcases hh with ⟨rfl, rfl⟩ + exact ⟨_, rfl, readExpr_sound ht⟩ + | succ index => simp [blockHeader?] at hh + | ctor owner index field => cases index <;> simp [blockHeader?] at hh + | muts rawMembers => + simp only [readBlock?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨members, hm, rfl⟩ := h + cases ref with + | member owner index => + simp only [blockHeader?, Option.map_eq_some_iff, Prod.mk.injEq] at hh + obtain ⟨declaration, hd, hn, ht⟩ := hh + obtain ⟨raw, hr, hread⟩ := mapM_get_right hm hd + have ⟨hu, htype⟩ := readMember_header hread + refine ⟨(memberRawHeader raw).2, ?_, readExpr_sound (ht ▸ htype)⟩ + simp only [rawHeader?, ← Array.getElem?_toList, hr, Option.map_some] + exact congrArg some (congrArg (·, (memberRawHeader raw).2) (hu.symm.trans hn)) + | ctor owner index field => + simp only [blockHeader?, bind, Option.bind_eq_some_iff] at hh + obtain ⟨declaration, hd, hc⟩ := hh + obtain ⟨raw, hr, hread⟩ := mapM_get_right hm hd + have hc' : blockHeader? ⟨[declaration]⟩ (.ctor block 0 field) = some (universes, type) := by + simpa only [blockHeader?, List.getElem?_cons_zero, bind, Option.bind_some] using hc + cases raw with + | defn value => + simp only [readMutualMember?, readDefinition?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at hread + obtain ⟨type, _, body, _, rfl⟩ := hread + simp [blockHeader?] at hc' + | recr value => + simp only [readMutualMember?, readRecursor?, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at hread + obtain ⟨type, _, rules, _, rfl⟩ := hread + simp [blockHeader?] at hc' + | indc family => + obtain ⟨ctor, hctor, hn, htype⟩ := readInductive_constructor hread hc' + refine ⟨ctor.typ, ?_, readExpr_sound htype⟩ + have hr' : rawMembers[index]? = some (.indc family) := by simpa using hr + simp [rawHeader?, hr', hctor, hn] + | cPrj | rPrj | iPrj | dPrj => simp [readBlock?] at h + +theorem lookup_mem {α : Type} {entries : List (Address × α)} {address : Address} {value : α} + (h : lookup entries address = some value) : (address, value) ∈ entries := by + induction entries with + | nil => cases h + | cons entry rest ih => + obtain ⟨key, data⟩ := entry + unfold lookup at h + split at h + · rename_i hk + subst address + cases Option.some.inj h + exact List.mem_cons_self .. + · exact List.mem_cons_of_mem _ (ih h) + +theorem lookup_of_mem {α : Type} {entries : List (Address × α)} {address : Address} {value : α} + (hn : (entries.map Prod.fst).Nodup) (h : (address, value) ∈ entries) : + lookup entries address = some value := by + induction entries with + | nil => cases h + | cons entry rest ih => + obtain ⟨key, data⟩ := entry + have hn := List.nodup_cons.mp hn + rcases List.mem_cons.mp h with he | hr + · cases he + simp [lookup] + · have hk : address ≠ key := by + intro he + apply hn.1 + exact List.mem_map.mpr ⟨(address, value), hr, he⟩ + simp only [lookup, hk, ↓reduceIte] + exact ih hn.2 hr + +theorem readBlocks_mem {fuel : Nat} {objects pending : Objects} {naturals : Naturals} + {blocks : List (Address × Block Address)} {address : Address} {block : Block Address} + (h : readBlocks? fuel objects naturals pending = some blocks) (hm : (address, block) ∈ blocks) : + ∃ source, (address, source) ∈ pending ∧ readBlock? fuel objects naturals address source = some block := by + induction pending generalizing blocks with + | nil => simp [readBlocks?] at h; subst blocks; cases hm + | cons entry rest ih => + obtain ⟨key, source⟩ := entry + unfold readBlocks? at h + split at h + · simp only [bind, Option.bind_eq_some_iff] at h + obtain ⟨_, _, h⟩ := h + obtain ⟨source, hs, hb⟩ := ih h hm + exact ⟨source, List.mem_cons_of_mem _ hs, hb⟩ + · simp only [bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨decoded, hd, tail, ht, rfl⟩ := h + rcases List.mem_cons.mp hm with he | hr + · rcases Prod.mk.inj he with ⟨rfl, rfl⟩ + exact ⟨source, List.mem_cons_self .., hd⟩ + · obtain ⟨source, hs, hb⟩ := ih ht hr + exact ⟨source, List.mem_cons_of_mem _ hs, hb⟩ + +theorem readStore_sourceBlock {fuel : Nat} {objects : Objects} {naturals : Naturals} + {store : Store Address} {address : Address} {block : Block Address} + (h : readStore? fuel objects naturals = some store) (hb : store.blocks address = some block) : + ∃ source, lookup objects address = some source ∧ readBlock? fuel objects naturals address source = some block := by + unfold readStore? at h + split at h + · rename_i hn + simp only [bind, Option.bind_eq_some_iff] at h + obtain ⟨blocks, hblocks, h⟩ := h + split at h + · cases Option.some.inj h + obtain ⟨source, hs, hr⟩ := readBlocks_mem hblocks (lookup_mem hb) + exact ⟨source, lookup_of_mem hn hs, hr⟩ + · cases h + · cases h + +def storeHeader? (store : Store Address) (ref : ConstRef Address) : Option (Nat × VExpr Address) := do + let block ← store.blocks ref.block + blockHeader? block ref + +theorem storeHeader_maps (store : Store Address) (ref : ConstRef Address) : + (storeHeader? store ref).map Prod.fst = store.uvars ref ∧ + (storeHeader? store ref).map Prod.snd = store.type ref := by + cases ref with + | member address index => + cases hs : store.blocks address with + | none => simp [storeHeader?, ConstRef.block, Store.type, Store.uvars, Store.lookup, Store.lookupCtor, hs] + | some block => + cases hm : block.members[index]? <;> + simp [storeHeader?, ConstRef.block, blockHeader?, Store.type, Store.uvars, Store.lookup, Store.lookupCtor, hs, hm] + | ctor address index field => + cases hs : store.blocks address with + | none => simp [storeHeader?, ConstRef.block, Store.type, Store.uvars, Store.lookup, Store.lookupCtor, hs] + | some block => + cases hm : block.members[index]? with + | none => simp [storeHeader?, ConstRef.block, blockHeader?, Store.type, Store.uvars, Store.lookup, Store.lookupCtor, hs, hm] + | some declaration => + cases declaration <;> + simp [storeHeader?, ConstRef.block, blockHeader?, Store.type, Store.uvars, Store.lookup, Store.lookupCtor, hs, hm] + rename_i ctors _ + cases hc : ctors[field]? <;> simp + +theorem sourceHeader_pair {store : Store Address} {ref : ConstRef Address} {entry : Ix.Theory.Model.ConstantEntry Address} + (h : SourceHeader store ref entry) : storeHeader? store ref = some (entry.universes, entry.type.erase) := by + have ⟨hu, ht⟩ := storeHeader_maps store ref + rw [h.universes] at hu + rw [h.type] at ht + cases hh : storeHeader? store ref with + | none => simp [hh] at ht + | some header => + rcases header with ⟨universes, type⟩ + simp only [hh, Option.map_some, Option.some.injEq] at hu ht + cases hu + cases ht + rfl + +/-- Exact source type, source universe arity and a unique structural reading +of that original type, for every admitted source member or constructor. -/ +theorem readStore_sourceHeader {fuel : Nat} {objects : Objects} {naturals : Naturals} + {store : Store Address} {ref : ConstRef Address} {entry : Ix.Theory.Model.ConstantEntry Address} + (h : readStore? fuel objects naturals = some store) (hh : SourceHeader store ref entry) : + ∃ source raw, lookup objects ref.block = some source ∧ + rawHeader? source ref = some (entry.universes, raw) ∧ + ExprReading objects naturals ref.block source raw entry.type.erase := by + have hp := sourceHeader_pair hh + simp only [storeHeader?, bind, Option.bind_eq_some_iff] at hp + obtain ⟨block, hb, hp⟩ := hp + obtain ⟨source, hs, hr⟩ := readStore_sourceBlock h hb + obtain ⟨raw, hraw, hread⟩ := readBlock_sourceHeader hr hp + exact ⟨source, raw, hs, hraw, hread⟩ + +end Ix.Certified diff --git a/Ix/Certified/Store.lean b/Ix/Certified/Store.lean new file mode 100644 index 000000000..40d9d5cba --- /dev/null +++ b/Ix/Certified/Store.lean @@ -0,0 +1,74 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Bytes +import Ix.Theory.Certified.Store + +namespace Ix.Certified + +open Ix.Theory Ix.Theory.Certified Ix.Theory.Model Ix.Theory.Model.SetTheory + +universe v + +/-- A block subject requests every member and constructor. A projection +subject must resolve to its actual matching source kind and position. -/ +def subjectReferences? (objects : Objects) (subject : Address) : Option (List (ConstRef Address)) := do + let source ← lookup objects subject + match source.info with + | .muts members => + if members.isEmpty then none else + return members.toList.zipIdx.flatMap fun (member, index) => + .member subject index :: match member with + | .indc family => (List.range family.ctors.size).map (.ctor subject index ·) + | _ => [] + | _ => return [← resolveReference? objects subject] + +structure PreparedStore where + signature : PrimitiveSignature Address + store : Store Address + targets : List (ConstRef Address) + +def prepareStoreObjects? (fuel : Nat) (profile : Profile) (subjects : List Address) + (objects : Objects) (naturals : Naturals) : Option PreparedStore := do + let signature ← readSignature? profile objects + let store ← readStore? fuel objects naturals + let targets ← subjects.flatMapM (subjectReferences? objects) + if targets.length > fuel then none else return ⟨signature, store, targets⟩ + +def prepareStore? (fuel : Nat) (profile : Profile) (subjects : List Address) + (blobs : ConstantBlobs) (literalBlobs : ConstantBlobs := []) : Option PreparedStore := do + if !((blobs ++ literalBlobs).map Prod.fst).Nodup then none else do + let naturals ← decodeNaturals? literalBlobs + let objects ← decodeObjects? blobs + prepareStoreObjects? fuel profile subjects objects naturals + +def acceptsSerializedStore (fuel : Nat) (profile : Profile) (subjects : List Address) + (blobs : ConstantBlobs) (witness : List (DeclarationWitness Address)) + (literalBlobs : ConstantBlobs := []) : Bool := + match prepareStore? fuel profile subjects blobs literalBlobs with + | none => false + | some prepared => acceptsStoreCertified.{0,v} fuel prepared.signature prepared.store prepared.targets witness + +theorem accepted_serialized_store_has_model {fuel : Nat} {profile : Profile} {subjects : List Address} + {blobs literalBlobs : ConstantBlobs} {witness : List (DeclarationWitness Address)} + (h : acceptsSerializedStore.{v} fuel profile subjects blobs witness literalBlobs = true) + (V : Type v) [SetTheory V] : + ∃ prepared, prepareStore? fuel profile subjects blobs literalBlobs = some prepared ∧ + ∃ result : CheckedStore.{0,v} prepared.signature prepared.store prepared.targets, + checkStoreCertified fuel prepared.signature prepared.store prepared.targets witness = some result ∧ + ∃ constants : Assignment Address V, + prepared.signature.Compatible result.environment.entries constants ∧ + ∀ r ∈ prepared.targets, ∃ entry, result.environment.entries r = some entry ∧ + EntrySource prepared.signature prepared.store r entry ∧ + ∀ levels, levels.length = entry.universes → ∀ env : Nat → V, + WellDenoted constants levels env entry.type ∧ + constants r levels ∈ˢ interp constants levels env entry.type := by + unfold acceptsSerializedStore at h + cases hp : prepareStore? fuel profile subjects blobs literalBlobs with + | none => simp [hp] at h + | some prepared => + exact ⟨prepared, rfl, accepted_store_has_model (by simpa only [hp] using h) V⟩ + +end Ix.Certified diff --git a/Ix/Certified/Suggest.lean b/Ix/Certified/Suggest.lean new file mode 100644 index 000000000..6a1340edb --- /dev/null +++ b/Ix/Certified/Suggest.lean @@ -0,0 +1,31 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Ingress +import Ix.Theory.Certificate.Build +import Ix.Certified.ModelHints + +namespace Ix.Certified + +open Ix.Theory.Certified + +/-- Untrusted witness search from selected addresses in the actual source +store. A returned witness still has to pass the certified TcM entry point. -/ +def suggestSource? (fuel : Nat) (source : Ixon.Env) (profile : Profile) (target : Address) + (selection : InputSelection) (hints : List ModelHint := []) : Option (ProofWitness Address) := do + let (snapshot, _) ← readSnapshot? fuel source selection {} + let prepared ← snapshot.prepare? fuel profile target + let models ← modelCandidates? snapshot.decodedObjects prepared.input.store hints + Ix.Theory.Certificate.proofWitness? fuel prepared.signature prepared.input models + +/-- Search for complete declaration groups for every requested source subject. -/ +def suggestStore? (fuel : Nat) (source : Ixon.Env) (profile : Profile) (subjects : List Address) + (selection : InputSelection) (hints : List ModelHint := []) : Option (List (DeclarationWitness Address)) := do + let (snapshot, _) ← readSnapshot? fuel source selection {} + let prepared ← snapshot.prepareStore? fuel profile subjects + let models ← modelCandidates? snapshot.decodedObjects prepared.store hints + Ix.Theory.Certificate.storeWitness? fuel prepared.signature prepared.store prepared.targets models + +end Ix.Certified diff --git a/Ix/Certified/TcAudit.lean b/Ix/Certified/TcAudit.lean new file mode 100644 index 000000000..d91767547 --- /dev/null +++ b/Ix/Certified/TcAudit.lean @@ -0,0 +1,42 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.AuditSupport +import Ix.Kernel.Certified +import Ix.Certified.Command + +/-! This audit covers the actual TcM certified entry points, the decoder +cache and source receipts. It does not promote legacy TcM successes. -/ + +open Lean Lean.Elab Command + +namespace Ix.Certified.TcAudit + +def roots : Array Lean.Name := #[ + `Ix.Certified.constantBytes?, `Ix.Certified.decodeObject_complete, + `Ix.Certified.decodeNatural_complete, + `Ix.Certified.SourceSnapshot.prepare_eq, `Ix.Certified.SourceSnapshot.prepareStore_eq, + `Ix.Certified.SourceSnapshot.objects_from_source, `Ix.Certified.SourceSnapshot.naturals_from_source, + `Ix.Certified.SourceReceipt.serialized, `Ix.Certified.StoreReceipt.serialized, + `Ix.Certified.accepted_serialized_store_has_model, + `Ix.Kernel.TcM.checkCertified_success, `Ix.Kernel.TcM.checkCertified_failure, + `Ix.Kernel.TcM.checkStoreCertified_success, + `Ix.Kernel.certifiedStep_success, `Ix.Kernel.certifiedStep_failure, + `Ix.Kernel.certifiedStoreStep_success, `Ix.Kernel.certifiedStoreStep_failure, + `Ix.Kernel.initialCertifiedState, `Ix.Kernel.accepted_tc_has_model, + `Ix.Kernel.accepted_tc_store_has_model, `Ix.Kernel.no_tc_proof_of_False, + `Ix.Certified.Command.run_success] + +def premises : Array Lean.Name := #[ + `Ix.Certified.InputCache.mk, `Ix.Certified.CachedObject.mk, `Ix.Certified.CachedNatural.mk, + `Ix.Certified.DecodedObject, `Ix.Certified.DecodedNatural, + `Ix.Certified.SourceObject.mk, `Ix.Certified.SourceNatural.mk, + `Ix.Certified.SourceReceipt.mk, `Ix.Certified.StoreReceipt.mk, + `Ix.Certified.InputSelection.mk, `Ix.Certified.subjectReferences?, + `Ix.Kernel.CertifiedState.mk] + +run_cmd AuditSupport.report "certified checker" roots premises + +end Ix.Certified.TcAudit diff --git a/Ix/Certified/Trees.lean b/Ix/Certified/Trees.lean new file mode 100644 index 000000000..59276b734 --- /dev/null +++ b/Ix/Certified/Trees.lean @@ -0,0 +1,117 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.SourceMeaning +import Ix.AssumptionTree + +/-! Total checking of Ix's existing Merkle-tree wire representation. The +public slot determines whether leaves mean constant addresses, environment +roots, or logical-axiom addresses. Padding is never a declaration. -/ + +namespace Ix.Certified + +instance addressLawfulBEq : LawfulBEq Address where + eq_of_beq := by + intro a b h + change (a.hash.data == b.hash.data) = true at h + cases a + cases b + exact congrArg Address.mk (congrArg ByteArray.mk (eq_of_beq h)) + rfl := by + intro a + change (a.hash.data == a.hash.data) = true + exact beq_self_eq_true _ + +def treeRoot : AssumptionTree → Address + | .leaf address => Merkle.leafHash address + | .padding => Merkle.zeroAddress + | .node left right => Merkle.nodeHash (treeRoot left) (treeRoot right) + +def treeLeaves : AssumptionTree → List Address + | .leaf address => [address] + | .padding => [] + | .node left right => treeLeaves left ++ treeLeaves right + +def putTreeBody : AssumptionTree → Ixon.PutM Unit + | .leaf address => do Ixon.putU8 0; Ixon.Serialize.put address + | .padding => Ixon.putU8 1 + | .node left right => do + Ixon.putU8 2 + putTreeBody left + putTreeBody right + +def treeBytes (tree : AssumptionTree) : ByteArray := Ixon.runPut do + Ixon.putTag4 ⟨AssumptionTree.FLAG, AssumptionTree.VARIANT⟩ + putTreeBody tree + +def getTreeBody : Nat → Ixon.GetM AssumptionTree + | 0 => throw "certified tree depth limit" + | fuel + 1 => do + match ← Ixon.getU8 with + | 0 => return .leaf (← Ixon.Serialize.get) + | 1 => return .padding + | 2 => return .node (← getTreeBody fuel) (← getTreeBody fuel) + | _ => throw "invalid certified tree node" + +def getTree (fuel : Nat) : Ixon.GetM AssumptionTree := do + let tag ← Ixon.getTag4 + if tag.flag != AssumptionTree.FLAG || tag.size != AssumptionTree.VARIANT then + throw "invalid certified tree tag" + getTreeBody fuel + +structure TreeOpening (fuel : Nat) (root : Address) (bytes : ByteArray) where + tree : AssumptionTree + parsing : Ixon.runGetExact (getTree fuel) bytes = .ok tree + canonical : treeBytes tree = bytes + rootBound : treeRoot tree = root + addresses : ∀ address ∈ treeLeaves tree, address.hash.size = 32 + +def readTree? (fuel : Nat) (root : Address) (bytes : ByteArray) : + Option (TreeOpening fuel root bytes) := + match hp : Ixon.runGetExact (getTree fuel) bytes with + | .error _ => none + | .ok tree => + if hc : treeBytes tree = bytes then + if hr : treeRoot tree = root then + if ha : (treeLeaves tree).all (fun address => address.hash.size == 32) = true then + some ⟨tree, hp, hc, hr, by simpa using List.all_eq_true.mp ha⟩ + else none + else none + else none + +structure OptionalTreeOpening (fuel : Nat) (root : Option Address) (bytes : Option ByteArray) where + leaves : List Address + evidence : match root, bytes with + | none, none => leaves = [] + | some root, some bytes => ∃ opening : TreeOpening fuel root bytes, treeLeaves opening.tree = leaves + | _, _ => False + +def readOptionalTree? (fuel : Nat) (root : Option Address) (bytes : Option ByteArray) : + Option (OptionalTreeOpening fuel root bytes) := + match root, bytes with + | none, none => some ⟨[], rfl⟩ + | some root, some bytes => do + let opening ← readTree? fuel root bytes + return ⟨treeLeaves opening.tree, ⟨opening, rfl⟩⟩ + | _, _ => none + +def TreeMembership (root target : Address) : Prop := + ∃ tree, treeRoot tree = root ∧ target ∈ treeLeaves tree ∧ + ∀ address ∈ treeLeaves tree, address.hash.size = 32 + +/-- Membership is a structural assertion only. No environment or typing +judgment occurs in its conclusion. -/ +theorem TreeOpening.membership (opening : TreeOpening fuel root bytes) {target : Address} + (h : target ∈ treeLeaves opening.tree) : TreeMembership root target := + ⟨opening.tree, opening.rootBound, h, opening.addresses⟩ + +theorem treeLeaves_join (left right : AssumptionTree) (address : Address) : + address ∈ treeLeaves (.node left right) ↔ address ∈ treeLeaves left ∨ address ∈ treeLeaves right := + List.mem_append + +theorem TreeOpening.unique {a b : TreeOpening fuel root bytes} : a.tree = b.tree := + Except.ok.inj (a.parsing.symm.trans b.parsing) + +end Ix.Certified diff --git a/Ix/Cli/BenchCmd.lean b/Ix/Cli/BenchCmd.lean index 139154378..0fd410ed6 100644 --- a/Ix/Cli/BenchCmd.lean +++ b/Ix/Cli/BenchCmd.lean @@ -12,7 +12,6 @@ the benchmark); 3. spawns the run's measured tool — `bench-typecheck` (aiur), `zisk-host`/`sp1-host` (zkVM execute), `ix check-rs` (ooc), - `bench-lean4lean` (lean4lean; olean-driven, no `.ixe`), `ix compile` (compile) — wrapped in the RAM watchdog (`Ix.Watchdog`: cgroup `memory.max` via a systemd user scope; the kernel OOM-kills at the ceiling). The per-constant backends (aiur, zkVM) spawn @@ -335,22 +334,6 @@ def backendSpecs : List BackendSpec := [ metrics := [("execute", ["check-time", "throughput", "peak-rss"])], thresholds := [("constants", "0", "0"), ("check-time", "0.10", "_"), ("throughput", "_", "0.10"), ("peak-rss", "0.10", "_")] }, - -- lean4lean (github.com/digama0/lean4lean, required by the lakefile at a - -- pinned rev): the reference Lean4-in-Lean4 kernel, the external - -- yardstick for the Ix kernels (`ooc` / `ix check-lean`) on the same - -- libraries. Checks the env's library from its oleans (no `.ixe`): - -- whole-library row (module-parallel replay of the import closure) plus - -- one full-closure row per constant, mirroring ooc's row shape and - -- metric names so cross-kernel tables line up. Disabled in CI until a - -- bencher testbed exists — `ix bench run --backend lean4lean` works - -- locally regardless (`disabled` only gates the CI matrix and - -- `!benchmark` scheduling). - { name := "lean4lean", defaultMode := "execute", - inputs := .perConstantWithEnv, - disabled := some "local-only: no bencher testbed yet", - testbeds := [("execute", "lean4lean-check-x64-32x")], - metrics := [("execute", ["check-time", "throughput", "peak-rss", - "constants"])] }, -- AnthropicFLT remains on-demand: its from-scratch upstream build needs -- substantially more than the per-push workflow's one-hour budget. An -- explicit `--env AnthropicFLT` or `BENCH_ENVS=AnthropicFLT` still runs it. @@ -804,27 +787,6 @@ is not a benchmark run" "--json", out, "--json-name", info.name] if exit != 0 && exit != exitRejected then IO.eprintln s!"[bench] whole-env aiur check failed (exit {exit})" - | "lean4lean" => - -- The reference Lean4-in-Lean4 kernel checks the env's library from - -- its oleans, so no `.ixe` is resolved. The tool takes the same - -- registry `module` path `ix compile` does (its lake project supplies - -- the search path; the tool builds the module itself, outside every - -- timed window). Whole-library row keyed by the env name … - let bl ← resolveBin repo "bench-lean4lean" - let modulePath := s!"{repo}/{info.module}" - let exit ← runGuarded watchdog ceilingGb bl - #[modulePath, "--json", out, "--json-name", info.name] - if exit != 0 && exit != exitRejected then - IO.eprintln s!"[bench] whole-library replay failed (exit {exit})" - -- … plus one full-closure row per constant. ONE process for all names - -- (the ooc pattern): the imported env is shared across the closure - -- replays instead of re-paying the library import per name. - if !names.isEmpty then - IO.FS.writeFile namesFile ("\n".intercalate names.toList ++ "\n") - let exit ← runGuarded watchdog ceilingGb bl - #[modulePath, "--no-build", "--consts-file", namesFile, "--json", out] - if exit != 0 && exit != exitRejected then - IO.eprintln s!"[bench] per-constant closures failed (exit {exit})" | "aiur" => -- prove runs the whole pipeline (`bench-typecheck --recursive`): -- every stage per constant, closed by the pipeline ledger. One process @@ -891,7 +853,7 @@ is not a benchmark run" let expected := match backend with | "compile" => #[info.name] | "decompile" => #[info.name] - | "ooc" | "lean4lean" => #[info.name] ++ names + | "ooc" => #[info.name] ++ names | _ => names let code ← gate out expected if code == 0 || code == exitRejected then @@ -930,7 +892,7 @@ def benchRunCmd : Cli.Cmd := `[Cli| "Execute one benchmark run (backend × env × mode), writing benchmark results JSON. Exits 0 on success (rows saved as the local baseline), 3 when the kernel rejected any constant, 1 when no rows were produced." FLAGS: - backend : String; "aiur | aiur-sharded-env | zisk | sp1 | ooc | lean4lean | compile | decompile" + backend : String; "aiur | aiur-sharded-env | zisk | sp1 | ooc | compile | decompile" env : String; "Benchmark env from the registry (default: InitStd)" mode : String; "prove | execute (default: the backend's defaultMode)" out : String; "Benchmark results JSON output path (default: bench.json)" diff --git a/Ix/Cli/CheckLeanCmd.lean b/Ix/Cli/CheckLeanCmd.lean index af1c420be..2ac5bd962 100644 --- a/Ix/Cli/CheckLeanCmd.lean +++ b/Ix/Cli/CheckLeanCmd.lean @@ -1,6 +1,6 @@ /- `ix check-lean `: typecheck a serialized `.ixe` environment - through the pure-Lean `Ix.Tc` kernel — the reference-kernel counterpart + through the pure-Lean `Ix.Kernel` — the reference-kernel counterpart of `ix check-rs`, with matching mode default, live progress, and exit codes. Correctness-first: expect the Rust kernel to be much faster. @@ -17,7 +17,7 @@ subject-only but still reads dependencies' declared types, so every constant must be present. `--max` therefore bounds the check phase only, never ingress. Checking then runs work-stealing parallel workers - over the shared env (see `Ix.Tc.ParCheck`). + over the shared env (see `Ix.Kernel.ParCheck`). Progress mirrors `check-rs`: a periodic aggregate line (done/total, rate, eta, oldest in-flight) on stderr, persistent lines only for @@ -38,14 +38,14 @@ module public import Cli public import Ix.Common public import Ix.Cli.ConstsFile -public import Ix.Tc +public import Ix.Kernel public import Ix.Benchmark.Results public section namespace Ix.Cli.CheckLeanCmd -open Ix.Tc +open Ix.Kernel /-- First set env var wins; else the default. Zero is a valid setting. -/ def envNat (names : List String) (dflt : Nat) : IO Nat := do @@ -222,7 +222,7 @@ def runCheckLeanCmd (p : Cli.Parsed) : IO UInt32 := do maxRecFuel? := ((← IO.getEnv "IX_MAX_REC_FUEL").bind (·.trimAscii.toString.toNat?)).map (·.toUInt64) } - IO.println s!"Running Ix.Tc kernel check \ + IO.println s!"Running Ix.Kernel check \ ({if anon then "anon" else "meta"} mode) on {envPath}" let t0 ← IO.monoMsNow let bytes ← IO.FS.readBinFile envPath @@ -271,7 +271,7 @@ end Ix.Cli.CheckLeanCmd open Ix.Cli.CheckLeanCmd in def checkLeanCmd : Cli.Cmd := `[Cli| "check-lean" VIA runCheckLeanCmd; - "Typecheck a `.ixe` through the pure-Lean Ix.Tc kernel (meta mode by default; parallel)" + "Typecheck a `.ixe` through the pure-Lean Ix.Kernel (meta mode by default; parallel)" FLAGS: anon; "Run in anon mode (metadata never reaches the kernel; `#hex` labels)" diff --git a/Ix/Cli/ValidateLeanCmd.lean b/Ix/Cli/ValidateLeanCmd.lean index 8441d1711..9f8359a99 100644 --- a/Ix/Cli/ValidateLeanCmd.lean +++ b/Ix/Cli/ValidateLeanCmd.lean @@ -1,6 +1,6 @@ /- `ix validate-lean `: run the pure-Lean Ix pipeline validation - against the Lean environment for any file — the `Ix.Tc` counterpart to + against the Lean environment for any file — the `Ix.Kernel` counterpart to `ix validate` (which drives the Rust implementation's 8-phase pipeline). Phases (all pure-Lean): @@ -44,7 +44,7 @@ public import Ix.DecompileM public import Ix.DecompileDriver public import Ix.DecompileRoundtrip public import Ix.Meta -public import Ix.Tc +public import Ix.Kernel public import Ix.Cli.ValidateCmd public section @@ -54,7 +54,7 @@ open Ix.EnvScope namespace Ix.Cli.ValidateLeanCmd -open Ix.Tc +open Ix.Kernel /-- Phase outcome for the final report. -/ inductive PhaseResult where diff --git a/Ix/Compile/Verify/Audit/SorryFrontier.lean b/Ix/Compile/Verify/Audit/SorryFrontier.lean index db3ee1f65..0595ae8b1 100644 --- a/Ix/Compile/Verify/Audit/SorryFrontier.lean +++ b/Ix/Compile/Verify/Audit/SorryFrontier.lean @@ -4,7 +4,7 @@ import Ix.Compile.Verify.Audit.Statements # Compiler-verification source sorry frontier Fail the build if any declaration emitted from an `Ix.Compile.Verify` source -module directly references `sorryAx`. Upstream Lean4Lean debt is handled by +module directly references `sorryAx`. Named-specification debt is handled by per-root transitive manifests rather than being confused with local source placeholders. -/ diff --git a/Ix/Compile/Verify/Audit/Statements.lean b/Ix/Compile/Verify/Audit/Statements.lean index 4bee84cf0..d30f4c7a4 100644 --- a/Ix/Compile/Verify/Audit/Statements.lean +++ b/Ix/Compile/Verify/Audit/Statements.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Audit.Basic +import Ix.Kernel.Verify.Audit.Basic import Ix.Compile.Verify.Statements /-! @@ -13,25 +13,20 @@ a premise. namespace Ix.Compile.Verify.Audit.Statements -open Ix.Tc.Verify.Audit +open Ix.Kernel.Verify.Audit private def standard : Array Lean.Name := #[``propext, ``Classical.choice, ``Quot.sound] private def noChoice : Array Lean.Name := #[``propext, ``Quot.sound] -private def blake3Native : Array Lean.Name := #[ - nativeAxiom `Blake3 - `Blake3.HasherOps.hash._native.native_decide.ax_1 -] - private def nameNative : Array Lean.Name := #[ nativeAxiom `Ix.Environment `Ix.Name.mkStr._native.native_decide.ax_1 ] private def singletonDriverNative : Array Lean.Name := - blake3Native ++ nameNative + nameNative private def roots : Array RootAllowance := #[ { root := ``Ix.Compile.Verify.IxonExprRel.eraseModes_iff, @@ -71,7 +66,7 @@ private def roots : Array RootAllowance := #[ standardAxioms := standard }, { root := ``Ix.Compile.Verify.ExprTableWF.mono }, { root := ``Ix.Compile.Verify.Catalog.empty_wf, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.Catalog.ofEnv_finite, standardAxioms := noChoice }, { root := ``Ix.Compile.Verify.BlockState.internRef_wf, @@ -91,24 +86,24 @@ private def roots : Array RootAllowance := #[ { root := ``Ix.Compile.Verify.compileAndInternUnivCanon_run_refines, standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileAndInternUnivCanon_array_refines, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileUniv_run_value, standardAxioms := standard }, { root := ``Ix.Compile.Verify.canonPreseedUnivs_run_refines, standardAxioms := standard }, { root := ``Ix.Compile.Verify.collectExprTablesStructural_run_ready, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.collectExprTablesStructural_run_ready_covers, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.collectPreseedExprs_singleton_run_ready_covers, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.collectPreseedExprs_pair_run_ready_covers, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.singletonPreseedCovers_of_ready, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.singletonPreseedCapacity_of_ready, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.internPreseedRefs_run_wf, standardAxioms := standard }, { root := ``Ix.Compile.Verify.internPreseedRefs_run_total, @@ -129,130 +124,130 @@ private def roots : Array RootAllowance := #[ standardAxioms := standard }, { root := ``Ix.Compile.Verify.PreseedCollectionCovers.compileExprRef_of_indexed, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.BlockWireTablesWF.of_preseed, standardAxioms := noChoice }, { root := ``Ix.Compile.Verify.preseedExprTables_singleton_run_ready, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.preseedExprTables_singleton_run_ready_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.preseedExprTables_singleton_run_ready_frozenRef, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.preseedExprTables_of_collect_run_ready_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.preseedExprTables_pair_run_ready_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.preseedExprTables_pair_run_ready_frozenRefs, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.preseedExprTables_roots_run_ready_frozenRefs, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.collectPreseedExprs_inputs_run_ready_covers, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.preseedExprTables_inputs_run_ready_frozenRefs, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.heterogeneousPreseedSeenSafe_of_uniform, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.preseedExprTables_inputs_run_uniform_ready_frozenRefs, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.preseedExprTables_run_univsFinal, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.serializeIxSyntax_run_refines, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.KVMapSupported.all, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileKVMap_run_refines, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.metaCompileSupport_finite, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.BlockState_compileName_strict, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.serializeIxSyntax_run_strictStores, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileDataValue_run_strictStores, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileKVMap_run_strictStores, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.StructuralExprCacheWF.insert, standardAxioms := standard }, { root := ``Ix.Compile.Verify.OrdinaryExprCacheWF.insert, standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_surgeryFree, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExprNoSurgeryFuel_structural_refines, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_structural_refines, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_structural_value, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_sort_value, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_constEmpty_recur_value, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_constEmpty_ref_value, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_lit_value, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExprNoSurgeryFuel_ordinary_refines, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_ordinary_refines, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_ordinary_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_ordinary_codec_roundtrip, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.deConstant_serUnsharedAxiomConstant, standardAxioms := standard }, { root := ``Ix.Compile.Verify.deConstant_serUnsharedDefinitionConstant, standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_ordinary_axiomConstant_roundtrip, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_ordinary_definitionConstant_roundtrip, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.BlockResult.mk'_codec_roundtrip, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.buildConstantWithSharing_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.BlockResult.constantInfo_codec_roundtrip, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.constantInfoRootExprs_toList, standardAxioms := #[``propext] }, { root := ``Ix.Compile.Verify.finishConstantInfoWithSharing_run_codecWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileAxiom_run_ordinary_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.finishQuotientCompilation_run, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileQuotient_run_ordinary_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileRecursorRules_run_ordinary_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.finishRecursorCompilation_run, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileRecursor_run_ordinary_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileConstructor_run_ordinary_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileInductiveConstructors_run_ordinary_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.finishInductiveCompilation_run, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileInductive_run_ordinary_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileInductiveData_run_ordinary_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileMutConsts_run_ordinary_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.buildCompiledMutualBlock_codecWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileMutualBlock_run_of_preseed_ordinary_codecWF, standardAxioms := standard, nativeAxioms := singletonDriverNative }, @@ -281,20 +276,20 @@ private def roots : Array RootAllowance := #[ ``Ix.Compile.Verify.compileConstant_run_mutual_of_lookup_sorted_codecWF, standardAxioms := standard, nativeAxioms := singletonDriverNative }, { root := ``Ix.Compile.Verify.finishInductiveFamilyBlock_run_codecWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.lookupInductiveConstructors_run_of_lookup, standardAxioms := standard, nativeAxioms := nameNative }, { root := ``Ix.Compile.Verify.compileDefinition_run_ordinary_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.finishDefinitionDataCompilation_run, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileDefinitionData_run_ordinary_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileDefinitionDataInfo_run_ready_codecWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.axiomCompileStartState_frozen, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileConstantInfo_axiom_run_ordinary_codecWF, standardAxioms := standard, nativeAxioms := singletonDriverNative }, @@ -325,30 +320,30 @@ private def roots : Array RootAllowance := #[ { root := ``Ix.Compile.Verify.rewriteWithSharing_wireWF, standardAxioms := standard }, { root := ``Ix.Compile.Verify.applySharing_wireWF, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_ordinary_axiomBlock_noSharing_roundtrip, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_ordinary_definitionBlock_noSharing_roundtrip, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_ordinary_axiomBlock_roundtrip, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_ordinary_definitionBlock_roundtrip, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_ordinary_value, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExprNoSurgeryFuel_ordinary_arena_refines, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_ordinary_arena_refines, - standardAxioms := standard, nativeAxioms := blake3Native }, + standardAxioms := standard }, { root := ``Ix.Compile.Verify.compileExpr_run_ordinary_arena_value, - standardAxioms := standard, nativeAxioms := blake3Native } + standardAxioms := standard } ] -run_cmd Ix.Tc.Verify.Audit.check roots +run_cmd Ix.Kernel.Verify.Audit.check roots end Ix.Compile.Verify.Audit.Statements diff --git a/Ix/Compile/Verify/Catalog.lean b/Ix/Compile/Verify/Catalog.lean index 3ff230550..4eafad6c1 100644 --- a/Ix/Compile/Verify/Catalog.lean +++ b/Ix/Compile/Verify/Catalog.lean @@ -5,7 +5,7 @@ import Std.Data.HashMap.Lemmas /-! # Immutable compiler catalog and representation well-formedness -This module states the X1 representation boundary without running Ix.Tc. It +This module states the X1 representation boundary without running Ix.Kernel. It separates content-address integrity, finite immutable lookup support, logical environment views, wire representability, and expression-table resolution. @@ -393,7 +393,7 @@ structure Catalog.Finite (catalog : Catalog) : Prop where memberAddrs : FinitelySupported catalog.memberAddrs /-- X1 in-memory catalog integrity. This is representation -well-formedness, not Lean4Lean `VEnv.WF`. -/ +well-formedness, not Ix.Theory.Named `VEnv.WF`. -/ structure Catalog.WF (catalog : Catalog) : Prop where finite : catalog.Finite constants : ∀ {addr constant}, catalog.constants addr = some constant → @@ -434,7 +434,7 @@ theorem Catalog.empty_wf : Catalog.empty.WF := by /-- Immutable view of a concrete `Ixon.Env`. `nameOf` and mutual member addresses remain explicit semantic inputs because the wire environment stores -Ix names and projection constants, not Lean4Lean names or a redundant member +Ix names and projection constants, not Ix.Theory.Named names or a redundant member array. -/ def Catalog.ofEnv (env : Ixon.Env) (nameOf : Address → Option Lean.Name) diff --git a/Ix/Compile/Verify/CompileExpr.lean b/Ix/Compile/Verify/CompileExpr.lean index 8d2cf06a9..85347ceab 100644 --- a/Ix/Compile/Verify/CompileExpr.lean +++ b/Ix/Compile/Verify/CompileExpr.lean @@ -24,7 +24,7 @@ closes the complete ordinary-expression tree: sorts, arbitrary-universe local and external constants, recursive projections, literals, structural composition, and arbitrary metadata maps including recursive syntax values. The proof covers warm caches, universe spelling patches, blob/name commits, -and independent Lean4Lean values. Its strengthened frontier also relates the +and independent Ix.Theory.Named values. Its strengthened frontier also relates the returned `UInt64` root, including the encoded KV map, to the append-only presentation arena under an explicit no-wrap capacity premise. -/ @@ -1309,11 +1309,11 @@ theorem compileExpr_run_sort_refines exact hrun /-- The production sort result therefore denotes the same independent -Lean4Lean value as the source sort. -/ +Ix.Theory.Named value as the source sort. -/ theorem compileExpr_run_sort_value - {venv : Lean4Lean.VEnv} {sctx : SourceCtx} {catalog : Catalog} + {venv : Ix.Theory.Named.VEnv} {sctx : SourceCtx} {catalog : Catalog} {dctx : DecodeCtx} {trProj : ProjectionRel} - {uvars : Nat} {locals : List Lean4Lean.VExpr} + {uvars : Nat} {locals : List Ix.Theory.Named.VExpr} (compileEnv : Ix.CompileM.CompileEnv) (blockEnv : Ix.CompileM.BlockEnv) (snapshot : Ix.CompileM.BlockState) {levelSupport : Ix.Level → Prop} @@ -1324,7 +1324,7 @@ theorem compileExpr_run_sort_value (hctx : RefCompileCtxRel (frozenRefCompileCtx compileEnv blockEnv snapshot) sctx catalog dctx) {state : Ix.CompileM.BlockState} {level : Ix.Level} {hash : Address} - {raw : Ixon.Univ} {idx : UInt64} {value : Lean4Lean.VExpr} + {raw : Ixon.Univ} {idx : UInt64} {value : Ix.Theory.Named.VExpr} (hlevel : levelSupport level) (hstate : FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state) (hraw : compileUnivRef (univParamIndex blockEnv.univCtx) level = some raw) @@ -2097,9 +2097,9 @@ theorem compileExpr_run_constEmpty_ref_refines Ix.CompileM.exprCompileDepth] using hrun theorem compileExpr_run_constEmpty_recur_value - {venv : Lean4Lean.VEnv} {sctx : SourceCtx} {catalog : Catalog} + {venv : Ix.Theory.Named.VEnv} {sctx : SourceCtx} {catalog : Catalog} {dctx : DecodeCtx} {trProj : ProjectionRel} - {uvars : Nat} {locals : List Lean4Lean.VExpr} + {uvars : Nat} {locals : List Ix.Theory.Named.VExpr} (compileEnv : Ix.CompileM.CompileEnv) (blockEnv : Ix.CompileM.BlockEnv) (snapshot : Ix.CompileM.BlockState) {levelSupport : Ix.Level → Prop} @@ -2108,7 +2108,7 @@ theorem compileExpr_run_constEmpty_recur_value (hctx : RefCompileCtxRel (frozenRefCompileCtx compileEnv blockEnv snapshot) sctx catalog dctx) {state : Ix.CompileM.BlockState} {name : Ix.Name} {hash : Address} - {recIdx : Nat} {value : Lean4Lean.VExpr} + {recIdx : Nat} {value : Ix.Theory.Named.VExpr} (hstate : FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state) (hmut : blockEnv.mutCtx.get? name = some recIdx) (hsource : SourceExprRel (uvars := uvars) venv sctx trProj locals @@ -2138,9 +2138,9 @@ theorem compileExpr_run_constEmpty_recur_value compileExprRef_value hctx hsource href⟩ theorem compileExpr_run_constEmpty_ref_value - {venv : Lean4Lean.VEnv} {sctx : SourceCtx} {catalog : Catalog} + {venv : Ix.Theory.Named.VEnv} {sctx : SourceCtx} {catalog : Catalog} {dctx : DecodeCtx} {trProj : ProjectionRel} - {uvars : Nat} {locals : List Lean4Lean.VExpr} + {uvars : Nat} {locals : List Ix.Theory.Named.VExpr} (compileEnv : Ix.CompileM.CompileEnv) (blockEnv : Ix.CompileM.BlockEnv) (snapshot : Ix.CompileM.BlockState) {levelSupport : Ix.Level → Prop} @@ -2149,7 +2149,7 @@ theorem compileExpr_run_constEmpty_ref_value (hctx : RefCompileCtxRel (frozenRefCompileCtx compileEnv blockEnv snapshot) sctx catalog dctx) {state : Ix.CompileM.BlockState} {name : Ix.Name} {hash addr : Address} - {refIdx : UInt64} {value : Lean4Lean.VExpr} + {refIdx : UInt64} {value : Ix.Theory.Named.VExpr} (hstate : FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state) (hmut : blockEnv.mutCtx.get? name = none) (hresolve : resolveConstAddr? compileEnv snapshot name = some addr) @@ -2317,9 +2317,9 @@ theorem compileExpr_run_lit_refines Ix.CompileM.exprCompileDepth] using hrun theorem compileExpr_run_lit_value - {venv : Lean4Lean.VEnv} {sctx : SourceCtx} {catalog : Catalog} + {venv : Ix.Theory.Named.VEnv} {sctx : SourceCtx} {catalog : Catalog} {dctx : DecodeCtx} {trProj : ProjectionRel} - {uvars : Nat} {locals : List Lean4Lean.VExpr} + {uvars : Nat} {locals : List Ix.Theory.Named.VExpr} (compileEnv : Ix.CompileM.CompileEnv) (blockEnv : Ix.CompileM.BlockEnv) (snapshot : Ix.CompileM.BlockState) {levelSupport : Ix.Level → Prop} @@ -2328,7 +2328,7 @@ theorem compileExpr_run_lit_value (hctx : RefCompileCtxRel (frozenRefCompileCtx compileEnv blockEnv snapshot) sctx catalog dctx) {state : Ix.CompileM.BlockState} {literal : Lean.Literal} {hash : Address} - {refIdx : UInt64} {value : Lean4Lean.VExpr} + {refIdx : UInt64} {value : Ix.Theory.Named.VExpr} (hstate : FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state) (hpreseed : snapshot.refsIndex.get? (literalAddress literal) = some refIdx) (hsource : SourceExprRel (uvars := uvars) venv sctx trProj locals @@ -2460,18 +2460,18 @@ theorem compileExpr_run_structural_refines exact hrun /-- The production result in the structural fragment therefore denotes the -same independent Lean4Lean value as its named Ix source. -/ +same independent Ix.Theory.Named value as its named Ix source. -/ theorem compileExpr_run_structural_value - {venv : Lean4Lean.VEnv} {sctx : SourceCtx} {catalog : Catalog} + {venv : Ix.Theory.Named.VEnv} {sctx : SourceCtx} {catalog : Catalog} {dctx : DecodeCtx} {ctx : RefCompileCtx} {trProj : ProjectionRel} - {uvars : Nat} {locals : List Lean4Lean.VExpr} + {uvars : Nat} {locals : List Ix.Theory.Named.VExpr} (compileEnv : Ix.CompileM.CompileEnv) (blockEnv : Ix.CompileM.BlockEnv) (hfree : compileEnv.surgeryFree = true) (hfaithful : ExprKeyFaithfulOn StructuralExpr) (hctx : RefCompileCtxRel ctx sctx catalog dctx) {state : Ix.CompileM.BlockState} {source : Ix.Expr} - {target : Ixon.Expr} {value : Lean4Lean.VExpr} + {target : Ixon.Expr} {value : Ix.Theory.Named.VExpr} (hstruct : StructuralExpr source) (hstate : StructuralExprCacheWF ctx state) (hsource : SourceExprRel (uvars := uvars) venv sctx trProj locals source value) @@ -4140,12 +4140,12 @@ theorem compileExpr_run_ordinary_wireWF hlevelFaithful hexprFaithful hsource hstate href exact ⟨root, state', hrun, hstate', compileExprRef_wireWF hbound href⟩ -/-- Complete ordinary compilation preserves the independent Lean4Lean value +/-- Complete ordinary compilation preserves the independent Ix.Theory.Named value assigned to the source expression. -/ theorem compileExpr_run_ordinary_value - {venv : Lean4Lean.VEnv} {sctx : SourceCtx} {catalog : Catalog} + {venv : Ix.Theory.Named.VEnv} {sctx : SourceCtx} {catalog : Catalog} {dctx : DecodeCtx} {trProj : ProjectionRel} - {uvars : Nat} {locals : List Lean4Lean.VExpr} + {uvars : Nat} {locals : List Ix.Theory.Named.VExpr} (compileEnv : Ix.CompileM.CompileEnv) (blockEnv : Ix.CompileM.BlockEnv) (snapshot : Ix.CompileM.BlockState) {levelSupport : Ix.Level → Prop} @@ -4156,7 +4156,7 @@ theorem compileExpr_run_ordinary_value (hctx : RefCompileCtxRel (frozenRefCompileCtx compileEnv blockEnv snapshot) sctx catalog dctx) {state : Ix.CompileM.BlockState} {source : Ix.Expr} - {target : Ixon.Expr} {value : Lean4Lean.VExpr} + {target : Ixon.Expr} {value : Ix.Theory.Named.VExpr} (hordinary : SupportedOrdinaryExpr levelSupport source) (hstate : FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state) (hsource : SourceExprRel (uvars := uvars) venv sctx trProj locals source value) @@ -4320,9 +4320,9 @@ theorem compileExpr_run_ordinary_arena_refines /-- The strengthened public theorem exposes canonical value preservation and the faithful presentation sidecar in one result. -/ theorem compileExpr_run_ordinary_arena_value - {venv : Lean4Lean.VEnv} {sctx : SourceCtx} {catalog : Catalog} + {venv : Ix.Theory.Named.VEnv} {sctx : SourceCtx} {catalog : Catalog} {dctx : DecodeCtx} {trProj : ProjectionRel} - {uvars : Nat} {locals : List Lean4Lean.VExpr} + {uvars : Nat} {locals : List Ix.Theory.Named.VExpr} (compileEnv : Ix.CompileM.CompileEnv) (blockEnv : Ix.CompileM.BlockEnv) (snapshot : Ix.CompileM.BlockState) {levelSupport : Ix.Level → Prop} @@ -4333,7 +4333,7 @@ theorem compileExpr_run_ordinary_arena_value (hctx : RefCompileCtxRel (frozenRefCompileCtx compileEnv blockEnv snapshot) sctx catalog dctx) {state : Ix.CompileM.BlockState} {source : Ix.Expr} - {target : Ixon.Expr} {value : Lean4Lean.VExpr} + {target : Ixon.Expr} {value : Ix.Theory.Named.VExpr} (hordinary : SupportedOrdinaryExpr levelSupport source) (hstate : FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state) (harena : ArenaCacheWF state) diff --git a/Ix/Compile/Verify/CompilePreseed.lean b/Ix/Compile/Verify/CompilePreseed.lean index 2611fa215..5fee400be 100644 --- a/Ix/Compile/Verify/CompilePreseed.lean +++ b/Ix/Compile/Verify/CompilePreseed.lean @@ -1,5 +1,5 @@ import Ix.Compile.Verify.CompileConstantCodec -import Lean4Lean.Verify.QSort +import Ix.Theory.Named.Verify.QSort /-! # Production expression-table preseeding @@ -350,7 +350,9 @@ theorem PreseedCollectionWireWF.pushUniv theorem addressBlake3_wire (bytes : ByteArray) : (Address.blake3 bytes).hash.size = 32 := by - exact (Blake3.Rust.hash bytes).property + exact (Blake3.HasherOps.finalizeWithLength + (Blake3.Rust.hasherUpdate (Blake3.Rust.hasherInit ()) bytes) 32 (by + rcases System.Platform.numBits_eq with bits | bits <;> rw [bits] <;> decide)).property /-- Conservative number of reference payloads a source walk can append. Seen-set deduplication can only decrease this cost. -/ diff --git a/Ix/Compile/Verify/CompileUniv.lean b/Ix/Compile/Verify/CompileUniv.lean index 6f5fe1e58..f548ee31b 100644 --- a/Ix/Compile/Verify/CompileUniv.lean +++ b/Ix/Compile/Verify/CompileUniv.lean @@ -2,6 +2,8 @@ import Ix.Compile.Verify.CompileState import Ix.Compile.Verify.Reference import Std.Data.HashMap.Lemmas +open Ix.Theory (VLevel) + /-! # Production universe-compiler refinement @@ -689,7 +691,7 @@ theorem compileAndInternUnivCanon_run_refines rw [run_bind compileEnv blockEnv canonState _ _, horiginalRun] rfl -/-- The production result therefore has the independent Lean4Lean universe +/-- The production result therefore has the independent Ix.Theory.Named universe value assigned to the named source level. -/ theorem compileUniv_run_value (compileEnv : Ix.CompileM.CompileEnv) (blockEnv : Ix.CompileM.BlockEnv) diff --git a/Ix/Compile/Verify/IxonValue.lean b/Ix/Compile/Verify/IxonValue.lean index 84358438e..41f25cd50 100644 --- a/Ix/Compile/Verify/IxonValue.lean +++ b/Ix/Compile/Verify/IxonValue.lean @@ -1,12 +1,14 @@ import Ix.Ixon -import Lean4Lean.Theory.Literals -import Lean4Lean.Theory.Typing.Env +import Ix.Theory.Named.Literals +import Ix.Theory.Named.Typing.Env + +open Ix.Theory (VLevel) /-! -# Ixon v2 expressions and Lean4Lean values +# Ixon v2 expressions and Ix.Theory.Named values This is the first compiler-facing semantic boundary. It interprets an Ixon -expression directly as a Lean4Lean `VExpr`; it does not run Ix.Tc and does not +expression directly as a Ix.Theory.Named `VExpr`; it does not run Ix.Kernel and does not use checker acceptance as a specification. The relation is table-aware. It resolves universe, reference, mutual-member, @@ -19,7 +21,7 @@ available to later substructural passes without changing the Lean meaning. namespace Ix.Compile.Verify -open Lean4Lean (VConstant VEnv VExpr VLevel) +open Ix.Theory.Named (VConstant VEnv VExpr) /-- Immutable semantic views needed to interpret an Ixon expression. -/ structure Catalog where @@ -70,7 +72,7 @@ def DecodeCtx.univArgs? (ctx : DecodeCtx) (idxs : Array UInt64) : /-- Projection interpretation is supplied by the surrounding declaration model. Its universe/local-context indices match the existing raw Theory -boundary, while this module remains independent of Ix.Tc. -/ +boundary, while this module remains independent of Ix.Kernel. -/ abbrev ProjectionRel := Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop @@ -81,7 +83,7 @@ def none : ProjectionRel := fun _ _ _ _ _ _ => False end ProjectionRel -/-- Direct semantic relation from table-indexed Ixon syntax to Lean4Lean +/-- Direct semantic relation from table-indexed Ixon syntax to Ix.Theory.Named syntax. This is a raw representation relation: typing and source-kernel well-formedness are separate obligations. -/ inductive IxonExprRel (venv : VEnv) (catalog : Catalog) (dctx : DecodeCtx) @@ -307,7 +309,7 @@ theorem eraseModes_iff {venv : VEnv} {catalog : Catalog} {dctx : DecodeCtx} end IxonExprRel -/-- Honest boundary for source-kernel meaning while upstream Lean4Lean +/-- Honest boundary for source-kernel meaning while upstream Ix.Theory.Named construction remains incomplete. Compiler theorems consume this explicit witness; no axiom is needed for the structural Ixon conversion itself. -/ structure KernelSourceWitness where diff --git a/Ix/Compile/Verify/Reference.lean b/Ix/Compile/Verify/Reference.lean index 9a5610c02..327e47377 100644 --- a/Ix/Compile/Verify/Reference.lean +++ b/Ix/Compile/Verify/Reference.lean @@ -1,6 +1,8 @@ import Ix.Compile.Verify.Catalog import Ix.Environment -import Lean4Lean.Std.Basic +import Ix.Theory.Named.Std.Basic + +open Ix.Theory (VLevel) /-! # Total ordinary-fragment compiler specification @@ -35,7 +37,7 @@ def compileUnivRef (paramIndex : Ix.Name → Option UInt64) : /-- Independent Theory reading of a named Ix universe under the same positional parameter assignment. -/ def sourceUnivValue (paramIndex : Ix.Name → Option UInt64) : - Ix.Level → Option Lean4Lean.VLevel + Ix.Level → Option Ix.Theory.VLevel | .zero _ => some .zero | .succ level _ => return .succ (← sourceUnivValue paramIndex level) | .max left right _ => @@ -161,8 +163,8 @@ private theorem array_mapM_size_of_eq_some {f : α → Option β} have hmapped := congrArg (Option.map Array.toList) h change Array.toList <$> xs.mapM f = Option.map Array.toList (some ys) at hmapped rw [Array.toList_mapM] at hmapped - have hlength := Lean4Lean.List.Forall₂.length_eq - (Lean4Lean.List.mapM_eq_some.mp hmapped) + have hlength := Ix.Theory.Named.List.Forall₂.length_eq + (Ix.Theory.Named.List.mapM_eq_some.mp hmapped) simpa using hlength.symm /-- Reference compilation preserves the three root-spine lengths used by the diff --git a/Ix/Compile/Verify/SourceValue.lean b/Ix/Compile/Verify/SourceValue.lean index 04fe77053..54051328a 100644 --- a/Ix/Compile/Verify/SourceValue.lean +++ b/Ix/Compile/Verify/SourceValue.lean @@ -1,10 +1,12 @@ import Ix.Compile.Verify.Reference +open Ix.Theory (VLevel) + /-! # Source-to-Ixon value preservation This module closes the first expression-level compiler square. `SourceExprRel` -gives a named `Ix.Expr` an independent Lean4Lean meaning. `RefCompileCtxRel` +gives a named `Ix.Expr` an independent Ix.Theory.Named meaning. `RefCompileCtxRel` states that the finite indices chosen by `compileExprRef` point at the same universes, names, and literal bytes in the target tables. The preservation theorem then constructs `IxonExprRel` for the exact compiler result. @@ -12,7 +14,7 @@ theorem then constructs `IxonExprRel` for the exact compiler result. namespace Ix.Compile.Verify -open Lean4Lean (VConstant VEnv VExpr VLevel) +open Ix.Theory.Named (VConstant VEnv VExpr) /-- Independent semantic interpretation choices for named source syntax. -/ structure SourceCtx where @@ -114,7 +116,7 @@ structure RefCompileCtxRel (compile : RefCompileCtx) (source : SourceCtx) String.fromUTF8? bytes = some value /-- Ordinary reference compilation preserves the independently stated -Lean4Lean value. -/ +Ix.Theory.Named value. -/ theorem compileExprRef_value {venv : VEnv} {sctx : SourceCtx} {catalog : Catalog} {dctx : DecodeCtx} {compile : RefCompileCtx} {trProj : ProjectionRel} {uvars : Nat} {locals : List VExpr} diff --git a/Ix/Compile/Verify/Statements.lean b/Ix/Compile/Verify/Statements.lean index 83c9d5cbe..5e53765a5 100644 --- a/Ix/Compile/Verify/Statements.lean +++ b/Ix/Compile/Verify/Statements.lean @@ -27,7 +27,7 @@ import Ix.Compile.Verify.SourceValue /-! # Public compiler-verification frontier -The first slice exports a direct, table-aware Ixon-to-Lean4Lean relation, the +The first slice exports a direct, table-aware Ixon-to-Ix.Theory.Named relation, the constructive theorem that v2 binder modes do not change the related Theory value, a total ordinary-fragment reference compiler, and proofs that its universe values are preserved and its expression outputs inhabit the @@ -158,14 +158,14 @@ digest-key faithfulness, well-addressed v2 expression tables and constants, and refinement proofs for the production reference/universe interning operations through `CompileM.run`. Production `compileUniv` is structurally total and refines the reference compiler while preserving both memo-cache -soundness and the independent Lean4Lean universe value. In surgery-free +soundness and the independent Ix.Theory.Named universe value. In surgery-free environments, production `compileExpr` now selects a kernel-visible total path; its recursive structural fragment refines `compileExprRef`, preserves a sound collision-disciplined expression cache, retains flattened App-spine -semantics, and composes with the independent Lean4Lean expression value. A +semantics, and composes with the independent Ix.Theory.Named expression value. A frozen-preseed state relation now closes the complete ordinary-expression tree through the actual production dispatcher, including arbitrary-universe -local and external constants, recursive projections, and their Lean4Lean +local and external constants, recursive projections, and their Ix.Theory.Named value corollary. The strengthened theorem also exposes a structural `ArenaRel` for the returned metadata root, preserves every warm-cache root under append-only growth, and makes the `UInt64` arena-capacity boundary @@ -211,6 +211,6 @@ for every variant in the explicit wire domain, with arbitrary canonical application, lambda, and forall spines in every expression payload. `KernelSourceWitness` is the sole upstream source-semantics boundary; later compiler-preservation slices take it -as an explicit hypothesis until Lean4Lean can construct it for a replayed Lean +as an explicit hypothesis until Ix.Theory.Named can construct it for a replayed Lean environment. -/ diff --git a/Ix/Environment.lean b/Ix/Environment.lean index 4bb2c6c3f..6726d69c6 100644 --- a/Ix/Environment.lean +++ b/Ix/Environment.lean @@ -114,7 +114,7 @@ instance : ToString Name where /-- Dot-separated bare rendering — byte-for-byte mirror of the Rust `Name::pretty` (str components verbatim, num components as plain digits, NO `«»` escaping). The kernel's canonical aux ordering seeds - on this exact string (`Ix.Tc.canonicalAuxOrder` ↔ Rust + on this exact string (`Ix.Kernel.canonicalAuxOrder` ↔ Rust `canonical_aux_order`), so `toString` (which escapes nums as `«n»`) must not be substituted there. -/ partial def pretty : Name → String diff --git a/Ix/IxVM/ClaimHarness.lean b/Ix/IxVM/ClaimHarness.lean index 45446a614..6a7539321 100644 --- a/Ix/IxVM/ClaimHarness.lean +++ b/Ix/IxVM/ClaimHarness.lean @@ -5,7 +5,7 @@ public import Ix.Claim public import Ix.AssumptionTree public import Ix.CompileM public import Ix.Common -public import Ix.Tc.Primitive +public import Ix.Kernel.Primitive public section @@ -357,7 +357,7 @@ private def seedTreeAt (root : Address) key`. -/ def closureFrom (env : Ixon.Env) (target : Address) : Std.HashSet Address := closureFromRoots env - (#[target] ++ Ix.Tc.primAddrSet.toArray.filter env.consts.contains) + (#[target] ++ Ix.Kernel.primAddrSet.toArray.filter env.consts.contains) /-- Serializes `claim`, seeds its bytes at `key = blake3(claim)`, and populates the IOBuffer with the `closureFrom` byte scope of every @@ -468,7 +468,7 @@ def shardCheckEnvClaimTrees (env : Ixon.Env) (owned : Array Address) : def shardCheckEnvClaim (env : Ixon.Env) (owned : Array Address) : Except String (Ix.Claim × Std.HashSet Address × Std.HashMap Address Ix.AssumptionTree) := do let (claim, trees) ← shardCheckEnvClaimTrees env owned - let primitiveRoots := Ix.Tc.primAddrSet.toArray.filter env.consts.contains + let primitiveRoots := Ix.Kernel.primAddrSet.toArray.filter env.consts.contains let closure := closureFromRoots env (owned ++ primitiveRoots) pure (claim, closure, trees) diff --git a/Ix/IxVM/Kernel/DefEq.lean b/Ix/IxVM/Kernel/DefEq.lean index 4c5567354..92e87b083 100644 --- a/Ix/IxVM/Kernel/DefEq.lean +++ b/Ix/IxVM/Kernel/DefEq.lean @@ -851,7 +851,7 @@ def defEq := ⟦ -- binder type, not out of `ty_a`, and the ensuing Lam/Lam comparison -- is what checks the two domains agree. Rust `try_eta_expansion` -- (`def_eq.rs:1179-1211`) infers `s`, whnfs to `All`, and takes that - -- `ty`; `Ix/Tc` `tryEtaExpansion` (`DefEq.lean:946-951`) does the same + -- `ty`; `Ix/Kernel` `tryEtaExpansion` (`DefEq.lean:946-951`) does the same -- via `inferOnlyCall`; lean4lean `tryEtaExpansionCore` -- (`TypeChecker.lean:507-511`) is `let .forallE name ty _ bi ← whnf -- (← inferType s)`. @@ -891,7 +891,7 @@ def defEq := ⟦ -- SEMANTIC on purpose — `level_equal`, matching `is_inductive_prop` -- (`Infer.lean:260`) and all three references: Rust gates both struct- -- eta (`whnf.rs:1884`) and proof irrelevance (`def_eq.rs:878`) on - -- `KUniv::is_semantic_zero`, `Ix/Tc` on `isSemanticZero` + -- `KUniv::is_semantic_zero`, `Ix/Kernel` on `isSemanticZero` -- (`Whnf.lean:1139`, `DefEq.lean:817`), and lean4lean's `isProp` on -- `Level.isAlwaysZero` (`TypeChecker.lean:230`), all of which normalize. -- diff --git a/Ix/IxVM/Kernel/Infer.lean b/Ix/IxVM/Kernel/Infer.lean index 3f7762dc4..1edfccc20 100644 --- a/Ix/IxVM/Kernel/Infer.lean +++ b/Ix/IxVM/Kernel/Infer.lean @@ -428,7 +428,7 @@ def infer := ⟦ -- Sort is malformed, and both references that implement this ERROR -- rather than answering — Rust `inductive_app_is_prop` -- (`infer.rs:485-517`, "projection: expected forall in inductive - -- type", then `ensure_sort`) and `Ix/Tc` `inductiveAppIsProp` + -- type", then `ensure_sort`) and `Ix/Kernel` `inductiveAppIsProp` -- (`Infer.lean:296-324`), with the same message. lean4lean has no -- counterpart: `inferProj` infers the type of the APPLIED struct -- expression instead and never peels the declaration's telescope, so @@ -568,7 +568,7 @@ def infer := ⟦ -- No fallthrough arm: a value whose type does not whnf to a Sort is not -- a type, and the references error there too (Rust `ensure_sort`, - -- `tc.rs:648-658`, returns `TcError::TypeExpected`; `Ix/Tc` + -- `tc.rs:648-658`, returns `TcError::TypeExpected`; `Ix/Kernel` -- `ensureSortWhnf` throws `.typeExpected`). Do not give it a default -- level — that would make an ill-typed `Lam` usable as a type. fn k_ensure_sort(e: KExpr, types: List‹KExpr›) -> KLevel { diff --git a/Ix/IxVM/Kernel/Whnf.lean b/Ix/IxVM/Kernel/Whnf.lean index bd89e681c..259d23b77 100644 --- a/Ix/IxVM/Kernel/Whnf.lean +++ b/Ix/IxVM/Kernel/Whnf.lean @@ -364,13 +364,13 @@ def whnf := ⟦ -- Dispatch is on the ADDRESS alone, and the stored `QuotKind` is not -- consulted. All three references decide this way — Rust -- `try_quot_reduce` (`whnf.rs:3073-3120`) compares against - -- `self.prims.quot_lift.addr` / `quot_ind.addr`, `Ix/Tc` + -- `self.prims.quot_lift.addr` / `quot_ind.addr`, `Ix/Kernel` -- `tryQuotReduce` (`Whnf.lean:1674-1698`) against `p.quotLift.addr` / -- `p.quotInd.addr`, and lean4lean `quotReduceRec` (`Quot.lean:85-100`) -- against the names `Quot.lift` / `Quot.ind`. -- -- The kind field is a checked REDUNDANCY, not a dispatch input: Rust's - -- `check_quot` and `Ix/Tc`'s `checkQuot` both DERIVE the expected kind + -- `check_quot` and `Ix/Kernel`'s `checkQuot` both DERIVE the expected kind -- from the address and assert the stored one matches. Reduction runs -- before any of that — `get_ci` typechecks nothing — so keying on the -- kind would trust a field no one has validated yet. @@ -424,7 +424,7 @@ def whnf := ⟦ -- The head is recognised by ADDRESS and the arity must be exactly 3, -- which is all the references look at: Rust -- `if *mk_addr != self.prims.quot_ctor.addr { return Ok(None) }` then - -- `if mk_args.len() != 3` (`whnf.rs:3108`), `Ix/Tc` the same against + -- `if mk_args.len() != 3` (`whnf.rs:3108`), `Ix/Kernel` the same against -- `p.quotCtor.addr` (`Whnf.lean:1690-1698`), lean4lean -- `if !mk.isAppOfArity ``Quot.mk 3` (`Quot.lean:88`). No `get_ci`, no -- stored kind — the constant need not even be resolvable for this to diff --git a/Ix/IxonUniv.lean b/Ix/IxonUniv.lean index 922372c41..94d96a03a 100644 --- a/Ix/IxonUniv.lean +++ b/Ix/IxonUniv.lean @@ -12,7 +12,7 @@ Mirror: crates/ixon/src/canon_univ.rs deterministic canonical representative of a stored level's Géran semantic-equality class, kernel-free and next to the wire type so `Ix.CompileM`, the Tc egress, and probes can all use it. The Géran -machinery is the transliteration of `Ix/Tc/Level.lean:227-472` onto +machinery is the transliteration of `Ix/Kernel/Level.lean:227-472` onto `Ixon.Univ` (positional `var` indices; no metadata); the three kernel `NormLevel` implementations stay untouched and serve as the P4 oracle. @@ -20,10 +20,10 @@ The frozen kernel `mk*` rule set (M1–M8 / I1–I6) is kept with its kernel-rebuild role: `reduceUniv` is the stage-1 decoration-presence test and the P6 oracle, and P3 (canonical forms are `mk*` fixpoints) is what makes kernel ingress the identity on canonical content. -(`Ix.Tc.reduceIxonUniv` computes the same closure by rounding through +(`Ix.Kernel.reduceIxonUniv` computes the same closure by rounding through the actual kernel constructors; `tc-unit` pins their agreement.) -Property set (tested in `Tests/Ix/Tc/Unit.lean` and the Rust twin; +Property set (tested in `Tests/Ix/Kernel/Unit.lean` and the Rust twin; Verify-layer proofs are the D7 follow-up): P1 idempotence; P2 roundtrip-fixpoint (`normalize (linearize L) = L`, exact on non-empty entries — `subsumption` can leave EMPTY entries which `linearize` @@ -42,7 +42,7 @@ namespace Ixon namespace Univ /-- Constructor count — termination measure for the normalization - family (mirrors `Ix.Tc.KUniv.size`). -/ + family (mirrors `Ix.Kernel.KUniv.size`). -/ def size : Univ → Nat | .zero => 1 | .succ u => u.size + 1 @@ -81,7 +81,7 @@ end Univ /-- `mkMax` of the frozen kernel rule set (M1–M8), on `Ixon.Univ`: numerals → the larger (ties → `a`); `max a a = a`; zero sides; - absorption; same-base offsets; raw. Mirrors `Ix.Tc.KUniv.mkMax` / + absorption; same-base offsets; raw. Mirrors `Ix.Kernel.KUniv.mkMax` / Rust `canon_univ::n_max`. -/ def nMax (a b : Univ) : Univ := if a.isExplicit && b.isExplicit then @@ -126,7 +126,7 @@ def nIMax (a b : Univ) : Univ := A non-fixpoint entry reaches the kernel changed (the stage-1 decoration-presence test); P6 pins that this rebuild refines into the Géran classes. `tc-unit` pins agreement with - `Ix.Tc.reduceIxonUniv` (the same closure via the kernel's own + `Ix.Kernel.reduceIxonUniv` (the same closure via the kernel's own constructors). -/ def reduceUniv : Univ → Univ | .zero => .zero @@ -157,7 +157,7 @@ abbrev CNorm := RBTree.RBMap CPath CNode compare instance : Inhabited CNorm := ⟨.empty⟩ /-- Insert `(idx, k)` into the sorted var list, max-merging offsets. - `k` must be the current succ-accumulator (`Ix/Tc/Level.lean:249-252` + `k` must be the current succ-accumulator (`Ix/Kernel/Level.lean:249-252` — dropping it is the classic port bug). -/ def CNode.addVar (n : CNode) (idx k : UInt64) : CNode := match n.vars.findIdx? (fun v => idx ≤ v.1) with @@ -187,13 +187,13 @@ def orderedInsert (a : UInt64) : CPath → Option CPath else (x :: ·) <$> orderedInsert a xs /-! -Termination mirrors `Ix/Tc/Level.lean:289-296`: the measure is +Termination mirrors `Ix/Kernel/Level.lean:289-296`: the measure is `3·Σ Univ.size + {0,1,2}` ordering the equal-size hops between the mutual members. -/ mutual -/-- Flatten a level into canonical form (`Ix.Tc.Level.normalizeAux` on +/-- Flatten a level into canonical form (`Ix.Kernel.Level.normalizeAux` on `Ixon.Univ`). `path` is the imax-conditioning chain, `k` the accumulated succ offset. -/ def normalizeAux (l : Univ) (path : CPath) (k : UInt64) (acc : CNorm) : @@ -276,7 +276,7 @@ def isSubset : CPath → CPath → Bool else false /-- Keep only the `xs` entries not dominated by a `ys` entry - (merge-walk over sorted var lists — `Ix.Tc.Level.subsumeVars`). -/ + (merge-walk over sorted var lists — `Ix.Kernel.Level.subsumeVars`). -/ def subsumeVars (xs ys : Array (UInt64 × UInt64)) : Array (UInt64 × UInt64) := go 0 0 #[] @@ -302,7 +302,7 @@ where decreasing_by all_goals omega /-- Drop contributions dominated by entries at sub-paths - (`Ix.Tc.Level.subsumption` — the in-loop `n1` mutations are + (`Ix.Kernel.Level.subsumption` — the in-loop `n1` mutations are order-sensitive). -/ def subsumption (acc : CNorm) : CNorm := Id.run do let snapshot := acc.toList diff --git a/Ix/Kernel.lean b/Ix/Kernel.lean new file mode 100644 index 000000000..91b1327b1 --- /dev/null +++ b/Ix/Kernel.lean @@ -0,0 +1,54 @@ +module + +public import Ix.Kernel.Mode +public import Ix.Kernel.Id +public import Ix.Kernel.Level +public import Ix.Kernel.Expr +public import Ix.Kernel.Error +public import Ix.Kernel.Const +public import Ix.Kernel.Equiv +public import Ix.Kernel.Env +public import Ix.Kernel.Primitive +public import Ix.Kernel.Subst +public import Ix.Kernel.Lctx +public import Ix.Kernel.Monad +public import Ix.Kernel.Ingress +public import Ix.Kernel.IngressMeta +public import Ix.Kernel.Egress +public import Ix.Kernel.EgressLean +public import Ix.Kernel.Driver +public import Ix.Kernel.ParCheck +public import Ix.Kernel.Validate +public import Ix.Kernel.Whnf +public import Ix.Kernel.Infer +public import Ix.Kernel.DefEq +public import Ix.Kernel.Knot +public import Ix.Kernel.CanonicalCheck +public import Ix.Kernel.Inductive +public import Ix.Kernel.Check + +/-! +# Ix.Kernel — pure-Lean Ix kernel over Ixon + +A correctness-first, formalizable port of the Rust Ix kernel +(`crates/kernel`), operating over the Ixon content-addressed format with +separated anon and meta modes. Designed for correctness and formalization; +for performance, use the Rust kernel. + +Module map (mirrors `crates/kernel/src/` file-for-file): + +| Lean module | Rust source | +|-------------------|----------------| +| `Ix.Kernel.Mode` | `mode.rs` | +| `Ix.Kernel.Id` | `id.rs` | +| `Ix.Kernel.Error` | `error.rs` | +| `Ix.Kernel.Level` | `level.rs` | +| `Ix.Kernel.Expr` | `expr.rs` | +| `Ix.Kernel.Const` | `constant.rs` | +| `Ix.Kernel.Equiv` | `equiv.rs` | +| `Ix.Kernel.Env` | `env.rs` | +| `Ix.Kernel.Primitive` | `primitive.rs` | +| `Ix.Kernel.Subst` | `subst.rs` | +| `Ix.Kernel.Lctx` | `lctx.rs` | +| `Ix.Kernel.Monad` | `tc.rs` | +-/ diff --git a/Ix/Tc/CanonicalCheck.lean b/Ix/Kernel/CanonicalCheck.lean similarity index 99% rename from Ix/Tc/CanonicalCheck.lean rename to Ix/Kernel/CanonicalCheck.lean index 0f6c82976..515fedc2c 100644 --- a/Ix/Tc/CanonicalCheck.lean +++ b/Ix/Kernel/CanonicalCheck.lean @@ -1,7 +1,7 @@ module -public import Ix.Tc.Const -public import Ix.Tc.Error +public import Ix.Kernel.Const +public import Ix.Kernel.Error /-! Mirror: crates/kernel/src/canonical_check.rs @@ -29,7 +29,7 @@ ctx hits compare positionally (weak), misses fall back to address order public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel open Std (HashMap) @@ -465,7 +465,7 @@ def validateCanonicalBlockSinglePass (blockAddr : Address) | .eq, _ => throw (.nonCanonicalBlock blockAddr i .eq) | .gt, _ => throw (.nonCanonicalBlock blockAddr i .gt) -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Kernel/Certified.lean b/Ix/Kernel/Certified.lean new file mode 100644 index 000000000..6d37c9ee1 --- /dev/null +++ b/Ix/Kernel/Certified.lean @@ -0,0 +1,338 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Monad +import Ix.Certified.Ingress + +/-! The witness-assisted certified Ix.Kernel entry point. It reads the selected +closure from the actual lazy Ixon store and checks it against the modeled +policy. Its only reusable cache is the checked byte/decoder cache. Existing +inference flags, equivalence classes and judgment caches have no effect on +this entry point. Successful legacy checking does not create a receipt. -/ + +namespace Ix.Kernel + +open Ix.Certified Ix.Theory Ix.Theory.Certified Ix.Theory.Model Ix.Theory.Model.SetTheory + +universe v + +namespace TcM + +def checkCertified (fuel : Nat) (source : Ixon.Env) (profile : Profile) (target : Address) + (selection : InputSelection) (witness : ProofWitness Address) (cache : InputCache) : + TcM m (SourceReceipt.{v} source fuel profile target witness × InputCache) := fun state => + match checkSource? fuel source profile target selection witness cache with + | none => .error (.other "certified source, profile, resource or witness check failed") state + | some result => .ok result state + +/-- A successful TcM execution reflects the independent full validator, and +cannot be influenced by pre-existing kernel judgment caches or flags. -/ +theorem checkCertified_success {fuel : Nat} {source : Ixon.Env} {profile : Profile} {target : Address} + {selection : InputSelection} {witness : ProofWitness Address} {cache nextCache : InputCache} + {before after : TcState m} {receipt : SourceReceipt.{v} source fuel profile target witness} + (h : (checkCertified fuel source profile target selection witness cache).run before = + .ok (receipt, nextCache) after) : + checkSource? fuel source profile target selection witness cache = some (receipt, nextCache) ∧ + after = before := by + unfold checkCertified EStateM.run at h + cases hc : checkSource? fuel source profile target selection witness cache with + | none => simp [hc] at h + | some result => + simp only [hc, EStateM.Result.ok.injEq] at h + rcases h with ⟨rfl, rfl⟩ + exact ⟨rfl, rfl⟩ + +/-- On failure the entire caller state is unchanged. No admission receipt or +partially populated cache escapes the failed source transaction. -/ +theorem checkCertified_failure {fuel : Nat} {source : Ixon.Env} {profile : Profile} {target : Address} + {selection : InputSelection} {witness : ProofWitness Address} {cache : InputCache} + {before after : TcState m} {error : TcError m} + (h : (checkCertified.{v} fuel source profile target selection witness cache).run before = + .error error after) : + checkSource?.{v} fuel source profile target selection witness cache = none ∧ after = before := by + unfold checkCertified EStateM.run at h + cases hc : checkSource?.{v} fuel source profile target selection witness cache with + | none => + simp only [hc, EStateM.Result.error.injEq] at h + exact ⟨rfl, h.2.symm⟩ + | some result => simp [hc] at h + +end TcM + +/-- Anonymous initialization constructs IDs directly. It does not evaluate +meta-mode display-name hashing, whose legacy implementation carries native +proof leaves. The certified primitive signature is checked separately. -/ +def initialCertifiedPrimitives : Primitives .anon where + nat := ⟨PrimAddrs.canonical.nat, ()⟩ + natZero := ⟨PrimAddrs.canonical.natZero, ()⟩ + natSucc := ⟨PrimAddrs.canonical.natSucc, ()⟩ + natAdd := ⟨PrimAddrs.canonical.natAdd, ()⟩ + natPred := ⟨PrimAddrs.canonical.natPred, ()⟩ + natSub := ⟨PrimAddrs.canonical.natSub, ()⟩ + natMul := ⟨PrimAddrs.canonical.natMul, ()⟩ + natPow := ⟨PrimAddrs.canonical.natPow, ()⟩ + natGcd := ⟨PrimAddrs.canonical.natGcd, ()⟩ + natMod := ⟨PrimAddrs.canonical.natMod, ()⟩ + natDiv := ⟨PrimAddrs.canonical.natDiv, ()⟩ + natBitwise := ⟨PrimAddrs.canonical.natBitwise, ()⟩ + natBeq := ⟨PrimAddrs.canonical.natBeq, ()⟩ + natBle := ⟨PrimAddrs.canonical.natBle, ()⟩ + natLand := ⟨PrimAddrs.canonical.natLand, ()⟩ + natLor := ⟨PrimAddrs.canonical.natLor, ()⟩ + natXor := ⟨PrimAddrs.canonical.natXor, ()⟩ + natShiftLeft := ⟨PrimAddrs.canonical.natShiftLeft, ()⟩ + natShiftRight := ⟨PrimAddrs.canonical.natShiftRight, ()⟩ + boolType := ⟨PrimAddrs.canonical.boolType, ()⟩ + boolTrue := ⟨PrimAddrs.canonical.boolTrue, ()⟩ + boolFalse := ⟨PrimAddrs.canonical.boolFalse, ()⟩ + string := ⟨PrimAddrs.canonical.string, ()⟩ + stringMk := ⟨PrimAddrs.canonical.stringMk, ()⟩ + charType := ⟨PrimAddrs.canonical.charType, ()⟩ + charMk := ⟨PrimAddrs.canonical.charMk, ()⟩ + charOfNat := ⟨PrimAddrs.canonical.charOfNat, ()⟩ + stringOfList := ⟨PrimAddrs.canonical.stringOfList, ()⟩ + stringToByteArray := ⟨PrimAddrs.canonical.stringToByteArray, ()⟩ + byteArrayEmpty := ⟨PrimAddrs.canonical.byteArrayEmpty, ()⟩ + list := ⟨PrimAddrs.canonical.list, ()⟩ + listNil := ⟨PrimAddrs.canonical.listNil, ()⟩ + listCons := ⟨PrimAddrs.canonical.listCons, ()⟩ + eq := ⟨PrimAddrs.canonical.eq, ()⟩ + eqRefl := ⟨PrimAddrs.canonical.eqRefl, ()⟩ + quotType := ⟨PrimAddrs.canonical.quotType, ()⟩ + quotCtor := ⟨PrimAddrs.canonical.quotCtor, ()⟩ + quotLift := ⟨PrimAddrs.canonical.quotLift, ()⟩ + quotInd := ⟨PrimAddrs.canonical.quotInd, ()⟩ + reduceBool := ⟨PrimAddrs.canonical.reduceBool, ()⟩ + reduceNat := ⟨PrimAddrs.canonical.reduceNat, ()⟩ + eagerReduce := ⟨PrimAddrs.canonical.eagerReduce, ()⟩ + systemPlatformNumBits := ⟨PrimAddrs.canonical.systemPlatformNumBits, ()⟩ + systemPlatformGetNumBits := ⟨PrimAddrs.canonical.systemPlatformGetNumBits, ()⟩ + subtypeVal := ⟨PrimAddrs.canonical.subtypeVal, ()⟩ + natDecLe := ⟨PrimAddrs.canonical.natDecLe, ()⟩ + natDecEq := ⟨PrimAddrs.canonical.natDecEq, ()⟩ + natDecLt := ⟨PrimAddrs.canonical.natDecLt, ()⟩ + decidableRec := ⟨PrimAddrs.canonical.decidableRec, ()⟩ + decidableIsTrue := ⟨PrimAddrs.canonical.decidableIsTrue, ()⟩ + decidableIsFalse := ⟨PrimAddrs.canonical.decidableIsFalse, ()⟩ + natLeOfBleEqTrue := ⟨PrimAddrs.canonical.natLeOfBleEqTrue, ()⟩ + natNotLeOfNotBleEqTrue := ⟨PrimAddrs.canonical.natNotLeOfNotBleEqTrue, ()⟩ + natEqOfBeqEqTrue := ⟨PrimAddrs.canonical.natEqOfBeqEqTrue, ()⟩ + natNeOfBeqEqFalse := ⟨PrimAddrs.canonical.natNeOfBeqEqFalse, ()⟩ + fin := ⟨PrimAddrs.canonical.fin, ()⟩ + boolNoConfusion := ⟨PrimAddrs.canonical.boolNoConfusion, ()⟩ + int := ⟨PrimAddrs.canonical.int, ()⟩ + intOfNat := ⟨PrimAddrs.canonical.intOfNat, ()⟩ + intNegSucc := ⟨PrimAddrs.canonical.intNegSucc, ()⟩ + intAdd := ⟨PrimAddrs.canonical.intAdd, ()⟩ + intSub := ⟨PrimAddrs.canonical.intSub, ()⟩ + intMul := ⟨PrimAddrs.canonical.intMul, ()⟩ + intNeg := ⟨PrimAddrs.canonical.intNeg, ()⟩ + intEmod := ⟨PrimAddrs.canonical.intEmod, ()⟩ + intEdiv := ⟨PrimAddrs.canonical.intEdiv, ()⟩ + intBmod := ⟨PrimAddrs.canonical.intBmod, ()⟩ + intBdiv := ⟨PrimAddrs.canonical.intBdiv, ()⟩ + intNatAbs := ⟨PrimAddrs.canonical.intNatAbs, ()⟩ + intPow := ⟨PrimAddrs.canonical.intPow, ()⟩ + intDecEq := ⟨PrimAddrs.canonical.intDecEq, ()⟩ + intDecLe := ⟨PrimAddrs.canonical.intDecLe, ()⟩ + intDecLt := ⟨PrimAddrs.canonical.intDecLt, ()⟩ + punit := ⟨PrimAddrs.canonical.punit, ()⟩ + natRec := ⟨PrimAddrs.canonical.natRec, ()⟩ + natCasesOn := ⟨PrimAddrs.canonical.natCasesOn, ()⟩ + bitVec := ⟨PrimAddrs.canonical.bitVec, ()⟩ + bitVecToNat := ⟨PrimAddrs.canonical.bitVecToNat, ()⟩ + bitVecOfNat := ⟨PrimAddrs.canonical.bitVecOfNat, ()⟩ + bitVecUlt := ⟨PrimAddrs.canonical.bitVecUlt, ()⟩ + decidableDecide := ⟨PrimAddrs.canonical.decidableDecide, ()⟩ + ltLt := ⟨PrimAddrs.canonical.ltLt, ()⟩ + ofNatOfNat := ⟨PrimAddrs.canonical.ofNatOfNat, ()⟩ + unit := ⟨PrimAddrs.canonical.unit, ()⟩ + punitSizeOf1 := ⟨PrimAddrs.canonical.punitSizeOf1, ()⟩ + sizeOfSizeOf := ⟨PrimAddrs.canonical.sizeOfSizeOf, ()⟩ + stringBack := ⟨PrimAddrs.canonical.stringBack, ()⟩ + stringLegacyBack := ⟨PrimAddrs.canonical.stringLegacyBack, ()⟩ + stringUtf8ByteSize := ⟨PrimAddrs.canonical.stringUtf8ByteSize, ()⟩ + stringAppend := ⟨PrimAddrs.canonical.stringAppend, ()⟩ + stringDecEq := ⟨PrimAddrs.canonical.stringDecEq, ()⟩ + +/-- State of the certified driver. Semantic judgments are never cached. -/ +structure CertifiedState where + checker : TcState .anon := TcState.new {} initialCertifiedPrimitives + inputCache : InputCache := {} + +/-- The public source program starts with empty kernel and byte caches. -/ +def initialCertifiedState : CertifiedState := {} + +def certifiedStep (fuel : Nat) (source : Ixon.Env) (profile : Profile) (target : Address) + (selection : InputSelection) (witness : ProofWitness Address) : + EStateM (TcError .anon) CertifiedState (SourceReceipt.{v} source fuel profile target witness) := fun state => + match (TcM.checkCertified fuel source profile target selection witness state.inputCache).run state.checker with + | .error error _ => .error error state + | .ok (receipt, cache) checker => .ok receipt ⟨checker, cache⟩ + +theorem certifiedStep_failure {fuel : Nat} {source : Ixon.Env} {profile : Profile} {target : Address} + {selection : InputSelection} {witness : ProofWitness Address} + {before after : CertifiedState} {error : TcError .anon} + (h : (certifiedStep.{v} fuel source profile target selection witness).run before = .error error after) : + after = before := by + unfold certifiedStep EStateM.run at h + dsimp only at h + split at h + · exact (EStateM.Result.error.inj h).2.symm + · cases h + +/-- All reused cache entries still match current bytes, and every request +reruns full scope, policy, dependency, formation and proof validation. -/ +theorem certifiedStep_success {fuel : Nat} {source : Ixon.Env} {profile : Profile} {target : Address} + {selection : InputSelection} {witness : ProofWitness Address} + {before after : CertifiedState} {receipt : SourceReceipt.{v} source fuel profile target witness} + (h : (certifiedStep fuel source profile target selection witness).run before = .ok receipt after) : + checkSource? fuel source profile target selection witness before.inputCache = + some (receipt, after.inputCache) ∧ after.checker = before.checker := by + unfold certifiedStep EStateM.run at h + dsimp only at h + split at h + · cases h + · rename_i pair checker he + rcases pair with ⟨result, cache⟩ + cases h + exact TcM.checkCertified_success he + +def runCertified (fuel : Nat) (source : Ixon.Env) (profile : Profile) (target : Address) + (selection : InputSelection) (witness : ProofWitness Address) : + Option (SourceReceipt.{v} source fuel profile target witness) := + match (certifiedStep fuel source profile target selection witness).run initialCertifiedState with + | .error _ _ => none + | .ok receipt _ => some receipt + +def acceptsCertifiedSource (fuel : Nat) (source : Ixon.Env) (profile : Profile) (target : Address) + (selection : InputSelection) (witness : ProofWitness Address) : Bool := + (runCertified.{v} fuel source profile target selection witness).isSome + +/-- Source success constructs every model premise from actual byte reads and +validator execution. There is no initial semantic model or cache oracle. -/ +theorem accepted_tc_has_model {fuel : Nat} {source : Ixon.Env} {profile : Profile} {target : Address} + {selection : InputSelection} {witness : ProofWitness Address} + (h : acceptsCertifiedSource.{v} fuel source profile target selection witness = true) + (V : Type v) [SetTheory V] (levels : List Nat) (env : Nat → V) : + ∃ receipt : SourceReceipt.{v} source fuel profile target witness, + runCertified fuel source profile target selection witness = some receipt ∧ + (∀ address bytes, (address, bytes) ∈ receipt.snapshot.blobs → constantBytes? source address = some bytes) ∧ + (∀ address bytes, (address, bytes) ∈ receipt.snapshot.literalBlobs → source.getBlob? address = some bytes) ∧ + ∃ constants : Assignment Address V, + receipt.prepared.signature.Compatible receipt.proof.environment.entries constants ∧ + WellDenoted constants levels env receipt.proof.proof.val ∧ + WellDenoted constants levels env receipt.proof.proposition.val ∧ + interp constants levels env receipt.proof.proof.val ∈ˢ + interp constants levels env receipt.proof.proposition.val := by + unfold acceptsCertifiedSource at h + cases hr : runCertified.{v} fuel source profile target selection witness with + | none => simp [hr] at h + | some receipt => + exact ⟨receipt, rfl, fun _ _ he => receipt.snapshot.objects_from_source he, + fun _ _ he => receipt.snapshot.naturals_from_source he, receipt.has_model V levels env⟩ + +theorem no_tc_proof_of_False (receipt : SourceReceipt.{v} source fuel profile target witness) + (V : Type v) [SetTheory V] + (h : receipt.prepared.input.proposition = receipt.prepared.signature.falseExpr) : False := by + apply no_proof_of_False (fuel := fuel) (witness := witness) V h + simp only [acceptsCertified, receipt.checked, Option.isSome_some] + + +namespace TcM + +def checkStoreCertified (fuel : Nat) (source : Ixon.Env) (profile : Profile) (subjects : List Address) + (selection : InputSelection) (witness : List (DeclarationWitness Address)) (cache : InputCache) : + TcM m (StoreReceipt.{v} source fuel profile subjects witness × InputCache) := fun state => + match checkSourceStore? fuel source profile subjects selection witness cache with + | none => .error (.other "certified declaration, profile, resource or witness check failed") state + | some result => .ok result state + +theorem checkStoreCertified_success {fuel : Nat} {source : Ixon.Env} {profile : Profile} {subjects : List Address} + {selection : InputSelection} {witness : List (DeclarationWitness Address)} {cache nextCache : InputCache} + {before after : TcState m} {receipt : StoreReceipt.{v} source fuel profile subjects witness} + (h : (checkStoreCertified fuel source profile subjects selection witness cache).run before = + .ok (receipt, nextCache) after) : + checkSourceStore? fuel source profile subjects selection witness cache = some (receipt, nextCache) ∧ + after = before := by + unfold checkStoreCertified EStateM.run at h + cases hc : checkSourceStore? fuel source profile subjects selection witness cache with + | none => simp [hc] at h + | some result => + simp only [hc, EStateM.Result.ok.injEq] at h + rcases h with ⟨rfl, rfl⟩ + exact ⟨rfl, rfl⟩ + +end TcM + +def certifiedStoreStep (fuel : Nat) (source : Ixon.Env) (profile : Profile) (subjects : List Address) + (selection : InputSelection) (witness : List (DeclarationWitness Address)) : + EStateM (TcError .anon) CertifiedState (StoreReceipt.{v} source fuel profile subjects witness) := fun state => + match (TcM.checkStoreCertified fuel source profile subjects selection witness state.inputCache).run state.checker with + | .error error _ => .error error state + | .ok (receipt, cache) checker => .ok receipt ⟨checker, cache⟩ + +theorem certifiedStoreStep_failure {fuel : Nat} {source : Ixon.Env} {profile : Profile} {subjects : List Address} + {selection : InputSelection} {witness : List (DeclarationWitness Address)} + {before after : CertifiedState} {error : TcError .anon} + (h : (certifiedStoreStep.{v} fuel source profile subjects selection witness).run before = .error error after) : + after = before := by + unfold certifiedStoreStep EStateM.run at h + dsimp only at h + split at h + · exact (EStateM.Result.error.inj h).2.symm + · cases h + +theorem certifiedStoreStep_success {fuel : Nat} {source : Ixon.Env} {profile : Profile} {subjects : List Address} + {selection : InputSelection} {witness : List (DeclarationWitness Address)} + {before after : CertifiedState} {receipt : StoreReceipt.{v} source fuel profile subjects witness} + (h : (certifiedStoreStep fuel source profile subjects selection witness).run before = .ok receipt after) : + checkSourceStore? fuel source profile subjects selection witness before.inputCache = + some (receipt, after.inputCache) ∧ after.checker = before.checker := by + unfold certifiedStoreStep EStateM.run at h + dsimp only at h + split at h + · cases h + · rename_i pair checker he + rcases pair with ⟨result, cache⟩ + cases h + exact TcM.checkStoreCertified_success he + +def runCertifiedStore (fuel : Nat) (source : Ixon.Env) (profile : Profile) (subjects : List Address) + (selection : InputSelection) (witness : List (DeclarationWitness Address)) : + Option (StoreReceipt.{v} source fuel profile subjects witness) := + match (certifiedStoreStep fuel source profile subjects selection witness).run initialCertifiedState with + | .error _ _ => none + | .ok receipt _ => some receipt + +def acceptsCertifiedStoreSource (fuel : Nat) (source : Ixon.Env) (profile : Profile) (subjects : List Address) + (selection : InputSelection) (witness : List (DeclarationWitness Address)) : Bool := + (runCertifiedStore.{v} fuel source profile subjects selection witness).isSome + +/-- Complete declaration requests, including source blocks, construct a +single model realizing every requested member and constructor. -/ +theorem accepted_tc_store_has_model {fuel : Nat} {source : Ixon.Env} {profile : Profile} {subjects : List Address} + {selection : InputSelection} {witness : List (DeclarationWitness Address)} + (h : acceptsCertifiedStoreSource.{v} fuel source profile subjects selection witness = true) + (V : Type v) [SetTheory V] : + ∃ receipt : StoreReceipt.{v} source fuel profile subjects witness, + runCertifiedStore fuel source profile subjects selection witness = some receipt ∧ + (∀ address bytes, (address, bytes) ∈ receipt.snapshot.blobs → constantBytes? source address = some bytes) ∧ + (∀ address bytes, (address, bytes) ∈ receipt.snapshot.literalBlobs → source.getBlob? address = some bytes) ∧ + ∃ constants : Assignment Address V, + receipt.prepared.signature.Compatible receipt.result.environment.entries constants ∧ + ∀ r ∈ receipt.prepared.targets, ∃ entry, receipt.result.environment.entries r = some entry ∧ + EntrySource receipt.prepared.signature receipt.prepared.store r entry ∧ + ∀ levels, levels.length = entry.universes → ∀ env : Nat → V, + WellDenoted constants levels env entry.type ∧ + constants r levels ∈ˢ interp constants levels env entry.type := by + unfold acceptsCertifiedStoreSource at h + cases hr : runCertifiedStore.{v} fuel source profile subjects selection witness with + | none => simp [hr] at h + | some receipt => + exact ⟨receipt, rfl, fun _ _ he => receipt.snapshot.objects_from_source he, + fun _ _ he => receipt.snapshot.naturals_from_source he, receipt.has_model V⟩ + +end Ix.Kernel diff --git a/Ix/Kernel/CertifiedClaims.lean b/Ix/Kernel/CertifiedClaims.lean new file mode 100644 index 000000000..581360880 --- /dev/null +++ b/Ix/Kernel/CertifiedClaims.lean @@ -0,0 +1,95 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Certified +import Ix.Certified.ClaimAccept + +namespace Ix.Kernel + +open Ix.Certified + +universe v + +namespace TcM + +def checkClaimCertified (fuel : Nat) (source : Ixon.Env) (address : Address) (bytes : ByteArray) + (witness : ClaimWitness) (cache : InputCache) : + TcM m (ClaimReceipt.{v} source fuel address bytes × InputCache) := fun state => + match checkClaimBytes? fuel source address bytes witness cache with + | none => .error (.other "certified claim, source, policy or witness check failed") state + | some result => .ok result state + +theorem checkClaimCertified_success {fuel : Nat} {source : Ixon.Env} {address : Address} {bytes : ByteArray} + {witness : ClaimWitness} {cache nextCache : InputCache} + {before after : TcState m} {receipt : ClaimReceipt.{v} source fuel address bytes} + (h : (checkClaimCertified fuel source address bytes witness cache).run before = + .ok (receipt, nextCache) after) : + checkClaimBytes? fuel source address bytes witness cache = some (receipt, nextCache) ∧ + after = before := by + unfold checkClaimCertified EStateM.run at h + cases hc : checkClaimBytes? fuel source address bytes witness cache with + | none => simp [hc] at h + | some result => + simp only [hc, EStateM.Result.ok.injEq] at h + rcases h with ⟨rfl, rfl⟩ + exact ⟨rfl, rfl⟩ + +end TcM + +def certifiedClaimStep (fuel : Nat) (source : Ixon.Env) (address : Address) (bytes : ByteArray) + (witness : ClaimWitness) : + EStateM (TcError .anon) CertifiedState (ClaimReceipt.{v} source fuel address bytes) := fun state => + match (TcM.checkClaimCertified fuel source address bytes witness state.inputCache).run state.checker with + | .error error _ => .error error state + | .ok (receipt, cache) checker => .ok receipt ⟨checker, cache⟩ + +theorem certifiedClaimStep_failure {fuel : Nat} {source : Ixon.Env} {address : Address} {bytes : ByteArray} + {witness : ClaimWitness} {before after : CertifiedState} {error : TcError .anon} + (h : (certifiedClaimStep.{v} fuel source address bytes witness).run before = .error error after) : + after = before := by + unfold certifiedClaimStep EStateM.run at h + dsimp only at h + split at h + · exact (EStateM.Result.error.inj h).2.symm + · cases h + +theorem certifiedClaimStep_success {fuel : Nat} {source : Ixon.Env} {address : Address} {bytes : ByteArray} + {witness : ClaimWitness} {before after : CertifiedState} {receipt : ClaimReceipt.{v} source fuel address bytes} + (h : (certifiedClaimStep fuel source address bytes witness).run before = .ok receipt after) : + checkClaimBytes? fuel source address bytes witness before.inputCache = some (receipt, after.inputCache) ∧ + after.checker = before.checker := by + unfold certifiedClaimStep EStateM.run at h + dsimp only at h + split at h + · cases h + · rename_i pair checker he + rcases pair with ⟨result, cache⟩ + cases h + exact TcM.checkClaimCertified_success he + +def runCertifiedClaim (fuel : Nat) (source : Ixon.Env) (address : Address) (bytes : ByteArray) + (witness : ClaimWitness) : Option (ClaimReceipt.{v} source fuel address bytes) := + match (certifiedClaimStep fuel source address bytes witness).run initialCertifiedState with + | .error _ _ => none + | .ok receipt _ => some receipt + +def acceptsCertifiedClaim (fuel : Nat) (source : Ixon.Env) (address : Address) (bytes : ByteArray) + (witness : ClaimWitness) : Bool := (runCertifiedClaim.{v} fuel source address bytes witness).isSome + +/-- The actual TcM entry point constructs the meaning of the original claim +from empty initialization. Warm callers have the same receipt theorem and +full rollback; neither logical judgments nor policy decisions are cached. -/ +theorem accepted_tc_claim_meaning {fuel : Nat} {source : Ixon.Env} {address : Address} {bytes : ByteArray} + {witness : ClaimWitness} (h : acceptsCertifiedClaim.{v} fuel source address bytes witness = true) : + ∃ receipt : ClaimReceipt.{v} source fuel address bytes, + runCertifiedClaim fuel source address bytes witness = some receipt ∧ + Address.blake3 bytes = address ∧ envelopeBytes receipt.reading.envelope = bytes ∧ + SemanticClaimMeaning.{v} source fuel receipt.reading.envelope := by + unfold acceptsCertifiedClaim at h + cases hr : runCertifiedClaim.{v} fuel source address bytes witness with + | none => simp [hr] at h + | some receipt => exact ⟨receipt, rfl, receipt.reading.authenticated, receipt.reading.canonical, receipt.meaning⟩ + +end Ix.Kernel diff --git a/Ix/Tc/Check.lean b/Ix/Kernel/Check.lean similarity index 99% rename from Ix/Tc/Check.lean rename to Ix/Kernel/Check.lean index c2f176d4f..f2d2e14e6 100644 --- a/Ix/Tc/Check.lean +++ b/Ix/Kernel/Check.lean @@ -1,6 +1,6 @@ module -public import Ix.Tc.Inductive +public import Ix.Kernel.Inductive /-! Mirror: crates/kernel/src/check.rs @@ -18,13 +18,13 @@ Constant checking dispatch: inductive machinery. The inductive and recursor member/block validators live in -`Ix.Tc.Inductive`. +`Ix.Kernel.Inductive`. -/ public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel open Std (HashSet) @@ -478,7 +478,7 @@ def checkBlockBody (block : KId m) (requested : KId m) : let kind ← classifyBlock members checkClassifiedBlock kind block members --- ### Inductive machinery (validation and recursor generation in Ix.Tc.Inductive) +-- ### Inductive machinery (validation and recursor generation in Ix.Kernel.Inductive) def checkInductiveMember (id : KId m) : RecM m Unit := checkInductiveMemberImpl id @@ -516,7 +516,7 @@ def checkConst (id : KId m) : TcM m Unit := end TcM -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Const.lean b/Ix/Kernel/Const.lean similarity index 98% rename from Ix/Tc/Const.lean rename to Ix/Kernel/Const.lean index 26b0fb7db..2690a4f35 100644 --- a/Ix/Tc/Const.lean +++ b/Ix/Kernel/Const.lean @@ -1,6 +1,6 @@ module -public import Ix.Tc.Expr +public import Ix.Kernel.Expr /-! Mirror: crates/kernel/src/constant.rs @@ -13,7 +13,7 @@ fidelity in meta mode. public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel /-- A recursor computation rule. `ctor` carries the Lean name of the constructor this rule dispatches on; the kernel dispatches on the @@ -94,7 +94,7 @@ def kindName : KConst m → String end KConst -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/DefEq.lean b/Ix/Kernel/DefEq.lean similarity index 99% rename from Ix/Tc/DefEq.lean rename to Ix/Kernel/DefEq.lean index 234606b4a..c015446b1 100644 --- a/Ix/Tc/DefEq.lean +++ b/Ix/Kernel/DefEq.lean @@ -1,11 +1,11 @@ module -public import Ix.Tc.Infer +public import Ix.Kernel.Infer /-! Mirror: crates/kernel/src/def_eq.rs -Multi-tier definitional equality (lean4lean shape): +Multi-tier definitional equality (the named specification shape): 1. quick structural (hash / binder common-fvar opening) 1b. eager Bool.true reduction; 1c. string-literal expansion **before any whnf**; 1d. cheap whnf-core then cheap whnf-no-delta passes @@ -28,7 +28,7 @@ cheap cache; cheap `true` promotes to the full cache and the EquivManager public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel /-- Lazy-delta single-step outcome. -/ inductive LazyDeltaStep where @@ -1277,7 +1277,7 @@ end end RecM -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Driver.lean b/Ix/Kernel/Driver.lean similarity index 97% rename from Ix/Tc/Driver.lean rename to Ix/Kernel/Driver.lean index 9cb37894b..be0310eeb 100644 --- a/Ix/Tc/Driver.lean +++ b/Ix/Kernel/Driver.lean @@ -1,12 +1,12 @@ module -public import Ix.Tc.Ingress -public import Ix.Tc.Egress -public import Ix.Tc.Check +public import Ix.Kernel.Ingress +public import Ix.Kernel.Egress +public import Ix.Kernel.Check /-! Mirror: crates/kernel/src/anon_work.rs (work enumeration) plus the driver -skeleton (the check loop itself lands with `Ix.Tc.Check`). +skeleton (the check loop itself lands with `Ix.Kernel.Check`). `buildAnonWork` identifies the kernel-checkable target set from `env.consts` without consulting metadata sections. For each entry: @@ -26,7 +26,7 @@ are visited in ascending byte-lexicographic address order (Rust public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel /-- A single anon-mode work item — one `checkConst primary` per item suffices to typecheck every address in `targets`. -/ @@ -178,7 +178,7 @@ def ingressAll (ixonEnv : Ixon.Env) (verify : Bool := true) : then merge in chunk order with `KEnv.union` (deterministic conversion makes duplicate keys benign). The shared shape behind the eager whole-env parallel ingress drivers (anon here, meta in - `Ix.Tc.ParCheck`). Chunks schedule on the task pool (bounded by + `Ix.Kernel.ParCheck`). Chunks schedule on the task pool (bounded by `LEAN_NUM_THREADS`). -/ def ingressEnvParallelWith (work : Array W) (runItem : W → EStateM IngressErr (KEnv m) Unit) @@ -214,7 +214,7 @@ def ingressAnonEnvParallel (ixonEnv : Ixon.Env) (work : Array AnonWorkItem) /-- Driver configuration. -/ structure CheckCfg where /-- Verify `blake3(bytes) == addr` at every constant materialization - (the soundness-critical integrity check; see `Ix.Tc.Ingress`). -/ + (the soundness-critical integrity check; see `Ix.Kernel.Ingress`). -/ verifyHashes : Bool := true /-- Clear the reduction-memo caches every N work items (structural caches, constants, and the faulted set are preserved). `0` disables. -/ @@ -295,10 +295,10 @@ def checkEnvAnon (ixonEnv : Ixon.Env) (cfg : CheckCfg := {}) : /-! ### Kernel ↔ Ixon roundtrip driver -`Lean env → (Rust) compile → ixe → Ix.Tc ingress → Ix.Tc egress → canonical +`Lean env → (Rust) compile → ixe → Ix.Kernel ingress → Ix.Kernel egress → canonical compare` — the anon analog of the Rust `kernel-ixon-roundtrip` test. Certifies ingress loses no information: every constant reconstructs -structurally from kernel data alone (see `Ix.Tc.Egress` for the comparison +structurally from kernel data alone (see `Ix.Kernel.Egress` for the comparison contract). Each item runs against a **fresh** kernel env — egress needs no cross-item state, so memory stays bounded per item at any corpus size. -/ @@ -419,7 +419,7 @@ def checkIxeAnon (path : System.FilePath) (cfg : CheckCfg := {}) : let bytes ← IO.FS.readBinFile path return checkIxeBytesAnon bytes cfg -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Egress.lean b/Ix/Kernel/Egress.lean similarity index 99% rename from Ix/Tc/Egress.lean rename to Ix/Kernel/Egress.lean index c8f0ca312..691654350 100644 --- a/Ix/Tc/Egress.lean +++ b/Ix/Kernel/Egress.lean @@ -1,6 +1,6 @@ module -public import Ix.Tc.Ingress +public import Ix.Kernel.Ingress /-! Mirror: crates/compile/src/kernel_egress.rs (the kernel → Ixon half, @@ -50,13 +50,13 @@ leg) becomes possible once meta-mode ingress lands. public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel open Std (HashMap) /-! ### Universe conversion and reduction -`ixonUnivToK` / `kUnivToIxon` / `reduceIxonUniv` live in `Ix.Tc.Ingress` +`ixonUnivToK` / `kUnivToIxon` / `reduceIxonUniv` live in `Ix.Kernel.Ingress` (shared with meta ingress's decoration-presence test). -/ /-! ### Expression egress -/ @@ -893,7 +893,7 @@ def roundtripCompare (original egressed : Ixon.Constant) : return none return some ((describeDiff ca cb).getD "canonical forms differ (no diff located)") -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/EgressLean.lean b/Ix/Kernel/EgressLean.lean similarity index 99% rename from Ix/Tc/EgressLean.lean rename to Ix/Kernel/EgressLean.lean index e2516cb89..92f07208c 100644 --- a/Ix/Tc/EgressLean.lean +++ b/Ix/Kernel/EgressLean.lean @@ -1,6 +1,6 @@ module -public import Ix.Tc.Ingress +public import Ix.Kernel.Ingress public import Ix.Environment /-! @@ -33,7 +33,7 @@ recursors — and walk to the first structural divergence for messages. public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel open Std (HashMap) @@ -365,7 +365,7 @@ def compareLeanCI (original egressed : Ix.ConstantInfo) : Option String := Id.ru | _, _ => pure () return none -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Env.lean b/Ix/Kernel/Env.lean similarity index 98% rename from Ix/Tc/Env.lean rename to Ix/Kernel/Env.lean index 3df2be1c1..8c2ecfc41 100644 --- a/Ix/Tc/Env.lean +++ b/Ix/Kernel/Env.lean @@ -1,7 +1,7 @@ module -public import Ix.Tc.Const -public import Ix.Tc.Error +public import Ix.Kernel.Const +public import Ix.Kernel.Error /-! Mirror: crates/kernel/src/env.rs @@ -13,19 +13,19 @@ Divergences from Rust (documented, semantics-preserving): - The `prims: OnceCell>` field is not stored here (Rust has an env ↔ primitive module cycle Lean can't express); the resolved `Primitives` lives on `TcState` instead, which is where the Rust `TypeChecker` copies it - anyway. `Primitives.fromEnv` performs the resolution (see `Ix.Tc.Primitive`). + anyway. `Primitives.fromEnv` performs the resolution (see `Ix.Kernel.Primitive`). - The reserved-marker guard in Rust's `KEnv::insert` (a panic) is enforced at - ingress time instead (`Ix.Tc.Ingress`), which is the only path that feeds + ingress time instead (`Ix.Kernel.Ingress`), which is the only path that feeds untrusted constants into the env. - `subst_scratch`/`lift_scratch` buffer *reuse* is not ported (allocation - churn is a Rust concern); per-call memoization itself is (see `Ix.Tc.Subst`). + churn is a Rust concern); per-call memoization itself is (see `Ix.Kernel.Subst`). - Perf counters and the sharding profile sink are not ported. -/ public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel open Std (HashMap HashSet) @@ -383,7 +383,7 @@ def cacheSizes (env : KEnv m) : KEnvCacheSizes where end KEnv -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Equiv.lean b/Ix/Kernel/Equiv.lean similarity index 99% rename from Ix/Tc/Equiv.lean rename to Ix/Kernel/Equiv.lean index d8f2841f0..ac83a1ec1 100644 --- a/Ix/Tc/Equiv.lean +++ b/Ix/Kernel/Equiv.lean @@ -17,7 +17,7 @@ reads). Do not reuse the `IO.Ref`-based `Ix.UnionFind`. public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel /-- Composite key for one expression in one context-suffix interpretation. @@ -147,7 +147,7 @@ def addEquiv (em : EquivManager) (k1 k2 : EqKey) : EquivManager := end EquivManager -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Error.lean b/Ix/Kernel/Error.lean similarity index 98% rename from Ix/Tc/Error.lean rename to Ix/Kernel/Error.lean index be3513dfa..901f23182 100644 --- a/Ix/Tc/Error.lean +++ b/Ix/Kernel/Error.lean @@ -1,6 +1,6 @@ module -public import Ix.Tc.Expr +public import Ix.Kernel.Expr /-! Mirror: crates/kernel/src/error.rs @@ -12,7 +12,7 @@ Type checker error types. (Rust's `u64_to_usize` helper is unneeded: Lean public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel inductive TcError (m : Mode) where | typeExpected @@ -80,7 +80,7 @@ instance : Inhabited (TcError m) := ⟨.typeExpected⟩ end TcError -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Expr.lean b/Ix/Kernel/Expr.lean similarity index 99% rename from Ix/Tc/Expr.lean rename to Ix/Kernel/Expr.lean index 69159a4b2..2d094a950 100644 --- a/Ix/Tc/Expr.lean +++ b/Ix/Kernel/Expr.lean @@ -1,8 +1,8 @@ module -public import Ix.Tc.Mode -public import Ix.Tc.Id -public import Ix.Tc.Level +public import Ix.Kernel.Mode +public import Ix.Kernel.Id +public import Ix.Kernel.Level public import Ix.Address public import Ix.Unsigned public import Ix.Ixon @@ -38,7 +38,7 @@ and mdata are never hashed — hash equality is alpha-invariant in meta mode. public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel open Blake3.Rust (Hasher) @@ -531,7 +531,7 @@ instance : Repr (KExpr m) := ⟨fun e _ => .text e.render⟩ end KExpr -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Id.lean b/Ix/Kernel/Id.lean similarity index 97% rename from Ix/Tc/Id.lean rename to Ix/Kernel/Id.lean index b0e6969f7..721ec09d9 100644 --- a/Ix/Tc/Id.lean +++ b/Ix/Kernel/Id.lean @@ -1,6 +1,6 @@ module -public import Ix.Tc.Mode +public import Ix.Kernel.Mode public import Ix.Address /-! @@ -11,7 +11,7 @@ definition ordering). public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel /-- Kernel identifier: bundles a content address with a metadata name. In meta mode both fields participate in equality; in anon mode the name @@ -78,7 +78,7 @@ structure FVarId where instance : ToString FVarId := ⟨fun f => s!"fv${f.id}"⟩ -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Inductive.lean b/Ix/Kernel/Inductive.lean similarity index 99% rename from Ix/Tc/Inductive.lean rename to Ix/Kernel/Inductive.lean index dea08a8e9..18b78eb1e 100644 --- a/Ix/Tc/Inductive.lean +++ b/Ix/Kernel/Inductive.lean @@ -1,7 +1,7 @@ module -public import Ix.Tc.Knot -public import Ix.Tc.CanonicalCheck +public import Ix.Kernel.Knot +public import Ix.Kernel.CanonicalCheck /-! Mirror: crates/kernel/src/inductive.rs @@ -34,7 +34,7 @@ triggering recursor generation for the block public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel open Std (HashSet) @@ -394,7 +394,7 @@ def getResultSortLevel (ty : KExpr m) (n : Nat) : /-- Large eliminator (can target any universe): non-Prop, or Empty-like (0 ctors), or single-ctor Prop whose non-Prop fields all appear among - the return-type args (lean4lean `isLargeEliminator`). -/ + the return-type args (the named specification `isLargeEliminator`). -/ def isLargeEliminator (resultLevel : KUniv m) (indInfos : Array (KId m × UInt64 × UInt64 × Array (KId m) × KExpr m)) : RecM m Bool := do @@ -2945,7 +2945,7 @@ def checkGeneratedRecursorFromCache (recBlock id : KId m) (ty : KExpr m) checkGeneratedRecursorCandidate ty declaredLvls declaredIsUnsafe params motives minors indices storedRules selected | none => - -- C2: no generated recursor — MUST NOT silently pass. + -- Reject a recursor with no generated candidate for its major type. throw (.other "check_recursor: no generated recursor for major") /-- Freeze the complete stored recursor declaration and validate the exact @@ -3120,7 +3120,7 @@ end end RecM -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Infer.lean b/Ix/Kernel/Infer.lean similarity index 97% rename from Ix/Tc/Infer.lean rename to Ix/Kernel/Infer.lean index f48831ef3..abfedbfcc 100644 --- a/Ix/Tc/Infer.lean +++ b/Ix/Kernel/Infer.lean @@ -1,6 +1,6 @@ module -public import Ix.Tc.Whnf +public import Ix.Kernel.Whnf /-! Mirror: crates/kernel/src/infer.rs @@ -22,7 +22,7 @@ Let inference eagerly substitutes the value into the abstracted body type public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -84,7 +84,7 @@ def inferUncached (inferRec : KExpr m → RecM m (KExpr m)) let t ← inferRec ty let _ ← ensureSortDirect t withLctxScope do - -- Open the binder with a fresh fvar (lean4lean inferLambda). + -- Open the binder with a fresh fvar (the named specification inferLambda). let (bodyOpen, fvId) ← TcM.openBinder name bi ty body let bodyTy ← inferRec bodyOpen -- Peephole-reduce App(λ…, …) shapes before wrapping in the Pi. @@ -107,7 +107,7 @@ def inferUncached (inferRec : KExpr m → RecM m (KExpr m)) let valTy ← inferRec val if !(← isDefEqCall valTy ty) then throw .declTypeMismatch - -- Open with a let-bound fvar (lean4lean inferLet); eagerly substitute + -- Open with a let-bound fvar (the named specification inferLet); eagerly substitute -- the value into the abstracted body type, then cheap-beta. withLctxScope do let (bodyOpen, fvId) ← TcM.openLet name ty val body @@ -179,7 +179,7 @@ def ensureForallDirect (e : KExpr m) : RecM m (KExpr m × KExpr m) := do return (a, b) ensureForallWhnf e -/-- The isDefEq back-edge (tied in `Ix.Tc.Knot`). -/ +/-- The isDefEq back-edge (tied in `Ix.Kernel.Knot`). -/ def isDefEqCall (a b : KExpr m) : RecM m Bool := do (← read).isDefEq a b @@ -327,7 +327,7 @@ end end RecM -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Ingress.lean b/Ix/Kernel/Ingress.lean similarity index 99% rename from Ix/Tc/Ingress.lean rename to Ix/Kernel/Ingress.lean index 33999c741..f459ddbd4 100644 --- a/Ix/Tc/Ingress.lean +++ b/Ix/Kernel/Ingress.lean @@ -1,6 +1,6 @@ module -public import Ix.Tc.Monad +public import Ix.Kernel.Monad public import Ix.Ixon /-! @@ -22,7 +22,7 @@ universe-table index for the same reason. Every constructed node is interned. Integrity: `getConstVerified` checks `blake3(rawBytes) == addr` at materialization. Rust's `Env::get` does this on its main path; the -`rs_de_env_lazy`-style loaders and pure-Lean `getEnv` do not, and Ix.Tc is +`rs_de_env_lazy`-style loaders and pure-Lean `getEnv` do not, and Ix.Kernel is the soundness-critical consumer — so it verifies here (`verify := true` by default; the driver's `CheckCfg` can disable it for benchmarking). @@ -31,13 +31,13 @@ here in `insertEntries`, the only path feeding untrusted constants into the kernel env. Meta-mode ingress (arena parallel walk, name resolution, mdata layers) -lives in `Ix.Tc.IngressMeta`. +lives in `Ix.Kernel.IngressMeta`. -/ public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel open Std (HashMap) @@ -100,7 +100,7 @@ def anonCtorAddrs (blockAddr : Address) (indcIdx : UInt64) UNCONDITIONALLY — `--no-verify` must not be able to bind them to other content, because acceleration substitutes native semantics for exactly these declarations (soundness argument in - `Ix/Tc/Primitive.lean` at `primAddrSet`). -/ + `Ix/Kernel/Primitive.lean` at `primAddrSet`). -/ def getConstVerified (ixonEnv : Ixon.Env) (addr : Address) (verify : Bool := true) : Except IngressErr (Option Ixon.Constant) := do match ixonEnv.consts[addr]? with @@ -597,7 +597,7 @@ def ingressAnonAddrShallow (ixonEnv : Ixon.Env) (addr : Address) let _ ← ingressAnonStandalone ixonEnv addr constant return true -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/IngressMeta.lean b/Ix/Kernel/IngressMeta.lean similarity index 99% rename from Ix/Tc/IngressMeta.lean rename to Ix/Kernel/IngressMeta.lean index 1e9533ba6..9f2971f59 100644 --- a/Ix/Tc/IngressMeta.lean +++ b/Ix/Kernel/IngressMeta.lean @@ -1,13 +1,13 @@ module -public import Ix.Tc.Ingress -public import Ix.Tc.CanonicalCheck +public import Ix.Kernel.Ingress +public import Ix.Kernel.CanonicalCheck /-! Mirror: crates/kernel/src/ingress.rs (the Ixon → kernel, meta-mode half) Meta ingress converts the same `Ixon.Constant` structure as the anon path -(`Ix.Tc.Ingress`) but additionally threads the per-constant metadata +(`Ix.Kernel.Ingress`) but additionally threads the per-constant metadata (`Ixon.ConstantMeta`) — binder names/infos, mdata layers, level-param names, constant names — into the kernel types' `Mode.F` fields. Semantic Blake3 addresses never include metadata, so every KExpr/KUniv/KConst @@ -62,13 +62,13 @@ tables the fallback never fires (P3: canonical forms are mk*-fixed), but it keeps raw-table fixtures exercised. Either way the `UnivDecor` on the KExpr node (`ExprInfo.univDecor`) folds into `metaAddr` only, never `addr`, so `tc-meta-addr` and all semantic checking are -untouched; meta egress replays the spelling (`Ix.Tc.egressExpr`). +untouched; meta egress replays the spelling (`Ix.Kernel.egressExpr`). -/ public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel open Std (HashMap) @@ -954,7 +954,7 @@ def ingressMetaWorkItem (ixonEnv : Ixon.Env) (item : MetaWorkItem) catch e => throw s!"{name}: {e}" insertMutsEntriesMeta entries -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Knot.lean b/Ix/Kernel/Knot.lean similarity index 94% rename from Ix/Tc/Knot.lean rename to Ix/Kernel/Knot.lean index 5479bb563..b67bdbcc4 100644 --- a/Ix/Tc/Knot.lean +++ b/Ix/Kernel/Knot.lean @@ -1,9 +1,9 @@ module -public import Ix.Tc.DefEq +public import Ix.Kernel.DefEq /-! -The recursion knot. `Ix.Tc.Whnf`/`Infer`/`DefEq` define the kernel algorithms +The recursion knot. `Ix.Kernel.Whnf`/`Infer`/`DefEq` define the kernel algorithms in `RecM` (a `Methods`-reader over `TcM`); this module ties the back-edges with a total, depth-indexed method table and exports `TcM`-level entry points. Only the back-edges route through the record (whnf reads @@ -18,7 +18,7 @@ has already been consumed. Exhausting method-table depth throws the same public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel /-- Fuel-exhausted end of the recursive method table. Every back-edge has the same error and error-state behavior as an exhausted `TcM.tick`. -/ @@ -81,7 +81,7 @@ def ensureForall (e : KExpr m) : TcM m (KExpr m × KExpr m) := end TcM -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Lctx.lean b/Ix/Kernel/Lctx.lean similarity index 98% rename from Ix/Tc/Lctx.lean rename to Ix/Kernel/Lctx.lean index 31c2d9a08..9fdd0581d 100644 --- a/Ix/Tc/Lctx.lean +++ b/Ix/Kernel/Lctx.lean @@ -1,6 +1,6 @@ module -public import Ix.Tc.Subst +public import Ix.Kernel.Subst /-! Mirror: crates/kernel/src/lctx.rs @@ -19,7 +19,7 @@ Rust's legacy `NameGenerator` is not ported: fvar ids are minted from public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel /-- A single local-context entry: a regular binder (`cdecl`, from lambda or forall) or a let-binding (`ldecl`, with an associated value that WHNF @@ -137,7 +137,7 @@ def isFVar (e : KExpr m) : Bool := | .fvar .. => true | _ => false -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Level.lean b/Ix/Kernel/Level.lean similarity index 97% rename from Ix/Tc/Level.lean rename to Ix/Kernel/Level.lean index a57d19314..62b99ecdb 100644 --- a/Ix/Tc/Level.lean +++ b/Ix/Kernel/Level.lean @@ -1,6 +1,6 @@ module -public import Ix.Tc.Mode +public import Ix.Kernel.Mode public import Ix.Address public import Ix.Unsigned public import Batteries.Recycling.RBTree.Basic @@ -15,21 +15,21 @@ carries `m.F Name` — the parameter name in meta mode, erased in anon mode. Parameter *identity* is the positional index; names are display-only and never hashed, so anon and meta levels of the same structure share addresses. -`normalizeLevel` is a port of the Rust port of Lean4Lean's `Level.Normalize` +`normalizeLevel` is a port of the Rust port of Ix.Theory.Named's `Level.Normalize` (Yoan Géran, "A Canonical Form for Universe Levels in Impredicative Type Theory"). `NormLevel` maps sorted param-index paths (the imax conditioning chain) to nodes tracking constant offsets and variable contributions. Divergence inherited from Rust (see level.rs `norm_level_le` doc): the `≤` check splits constant and variable coverage into independent searches -(`coversConst` / `coversVar`), strictly more complete than Lean4Lean's -single-witness `NormLevel.le`. `univEq` matches Lean4Lean's `isEquiv'`. +(`coversConst` / `coversVar`), strictly more complete than Ix.Theory.Named's +single-witness `NormLevel.le`. `univEq` matches Ix.Theory.Named's `isEquiv'`. -/ public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel open Blake3.Rust (Hasher) @@ -320,7 +320,7 @@ def normalizeAux (l : KUniv m) (path : Path) (k : UInt64) normalizeAux u newPath k acc | none => -- idx already in path (fixed > 0 along this chain); the outer k succs - -- still contribute. Matches Lean4Lean's `acc.addVar v k path`. + -- still contribute. Matches Ix.Theory.Named's `acc.addVar v k path`. let acc := if k != 0 then acc.addVar idx k path else acc normalizeAux u path k acc | .param idx _ _ => @@ -450,7 +450,7 @@ def coversVar (l2 : NormLevel) (p1 : Path) (w off : UInt64) : Bool := /-- Semantic `l1 ≤ l2` on canonical forms. Each ingredient of every `l1` entry must be covered by *some* `l2` entry at a sub-path — constant and variable coverage searched independently (see module doc: intentionally - stronger than Lean4Lean's single-witness `NormLevel.le`). -/ + stronger than Ix.Theory.Named's single-witness `NormLevel.le`). -/ def normLevelLe (l1 l2 : NormLevel) : Bool := l1.toList.all fun (p1, n1) => if n1.constant == 0 && n1.vars.isEmpty then true @@ -505,7 +505,7 @@ def univGeq (u v : KUniv m) : Bool := def KUniv.isSemanticZero (u : KUniv m) : Bool := u.isZero || (Level.normalizeLevel u).toList.all (!Level.entryNonEmpty ·) -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Mode.lean b/Ix/Kernel/Mode.lean similarity index 98% rename from Ix/Tc/Mode.lean rename to Ix/Kernel/Mode.lean index 078493dd6..bae9ea9c5 100644 --- a/Ix/Tc/Mode.lean +++ b/Ix/Kernel/Mode.lean @@ -18,14 +18,14 @@ in either mode, so anon and meta terms of the same underlying structure carry identical Blake3 addresses. Rust's `MetaHash` trait is not ported: its only load-bearing uses are the -`Ord` on `KId` (ported directly in `Ix.Tc.Id` via stored name hashes) and +`Ord` on `KId` (ported directly in `Ix.Kernel.Id` via stored name hashes) and duplicate-level-param detection (`Mode.F.hasDups` below). -/ public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel /-- Kernel mode. `anon` erases metadata fields to `Unit`; `«meta»` keeps them. Mirrors Rust `ZMode` (`Anon = ZMode`, `Meta = ZMode`). -/ @@ -173,7 +173,7 @@ instance {m : Mode} [MetaDisplay α] : MetaDisplay (m.F α) where end Mode.F -end Ix.Tc +end Ix.Kernel /-- Saturating decrement: `0 - 1 = 0`. Lean `UInt64` subtraction wraps where Rust's `saturating_sub` saturates; loose-bound-range (`lbr`) computations diff --git a/Ix/Tc/Monad.lean b/Ix/Kernel/Monad.lean similarity index 98% rename from Ix/Tc/Monad.lean rename to Ix/Kernel/Monad.lean index 45c06591d..f797e47c6 100644 --- a/Ix/Tc/Monad.lean +++ b/Ix/Kernel/Monad.lean @@ -1,10 +1,10 @@ module -public import Ix.Tc.Env -public import Ix.Tc.Equiv -public import Ix.Tc.Primitive -public import Ix.Tc.Subst -public import Ix.Tc.Lctx +public import Ix.Kernel.Env +public import Ix.Kernel.Equiv +public import Ix.Kernel.Primitive +public import Ix.Kernel.Subst +public import Ix.Kernel.Lctx /-! Mirror: crates/kernel/src/tc.rs @@ -26,7 +26,7 @@ Fuels are data-level and placed exactly where Rust places them (post-cache-miss). The `IX_MAX_REC_FUEL` env override is not ported. Cross-file recursion (whnf ↔ infer ↔ def_eq) is tied through a `Methods m` -record + `RecM m := ReaderT (Methods m) (TcM m)`; `Ix.Tc.Knot` ties a total +record + `RecM m := ReaderT (Methods m) (TcM m)`; `Ix.Kernel.Knot` ties a total method table indexed by the current `recFuel`. Only back-edges go through the record. @@ -37,7 +37,7 @@ Not ported (diagnostics/profiling only): perf counters, hot-miss sampler, public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel open Std (HashMap HashSet) @@ -145,8 +145,8 @@ structure TcState (m : Mode) where constant's block into `env` by address. Installed by the driver (`TcState.newLazyAnon`); `none` for eagerly-ingressed envs. The closure runs against `env` and returns whether the address was found in the - backing Ixon env. (The error type is `Ix.Tc.IngressErr`, spelled - `String` here because `Ix.Tc.Ingress` imports this module.) -/ + backing Ixon env. (The error type is `Ix.Kernel.IngressErr`, spelled + `String` here because `Ix.Kernel.Ingress` imports this module.) -/ lazyFault : Option (Address → EStateM String (KEnv m) Bool) := none /-- Addresses already faulted (hit or miss) — never re-ingress. Lives on the state (not reset per constant), mirroring Rust @@ -257,7 +257,7 @@ def addr8 (a : Address) : String := ((toString a).take 8).toString `[tag] ` mirrors the Rust kernel's `IX_STEP_TRACE` journal; diffing the two sequences localizes a behavioral divergence at the first fork (workflow in - `Ix.Tc.ParCheck`). -/ + `Ix.Kernel.ParCheck`). -/ @[inline] def stepTrace (tag : String) (payload : Unit → String) : TcM m Unit := do let s ← get @@ -383,7 +383,8 @@ def ctxAddrForLbrUncached (s : TcState m) (lbr : UInt64) : Address := | none => h := h.update "local".toUTF8 h := h.update s.ctx[i]!.addr.hash - return ⟨(h.finalizeWithLength 32).val⟩ + return ⟨(h.finalizeWithLength 32 (by + rcases System.Platform.numBits_eq with bits | bits <;> rw [bits] <;> decide)).val⟩ /-- Memoized wrapper around `ctxAddrForLbrUncached`, mirroring tc.rs `ctx_addr_for_lbr`. The pure helper is a verification seam as well as an @@ -602,7 +603,7 @@ def instUnivInner (e : KExpr m) (us : Array (KUniv m)) : -- `pure`, not `return`, in every rebuilding arm: `return` exits the -- whole function, skipping the intern + memo-insert tail below, and -- an interior-node-less memo turns shared-DAG walks exponential - -- (see the note in `Ix/Tc/Subst.lean` `liftCached`). + -- (see the note in `Ix/Kernel/Subst.lean` `liftCached`). | .sort u _ => pure (KExpr.mkSort (← TcM.ofExcept (substUniv u us))) | .const id curUs _ => do @@ -871,7 +872,7 @@ def ensureForall (e : KExpr m) : RecM m (KExpr m × KExpr m) := do end RecM -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/ParCheck.lean b/Ix/Kernel/ParCheck.lean similarity index 98% rename from Ix/Tc/ParCheck.lean rename to Ix/Kernel/ParCheck.lean index 606eddc29..b214fa29a 100644 --- a/Ix/Tc/ParCheck.lean +++ b/Ix/Kernel/ParCheck.lean @@ -1,12 +1,12 @@ module -public import Ix.Tc.Driver -public import Ix.Tc.IngressMeta +public import Ix.Kernel.Driver +public import Ix.Kernel.IngressMeta public import Std.Sync.Mutex /-! Parallel whole-env kernel check driver (`ix check-lean`'s engine) plus the -eager parallel meta-ingress driver it needs (shared with `Ix.Tc.Validate`). +eager parallel meta-ingress driver it needs (shared with `Ix.Kernel.Validate`). Mirrors the Rust parallel checker's coordination design (`crates/ffi/src/kernel.rs`: work-stealing over an atomic next-index, @@ -47,7 +47,7 @@ per-constant fuel budget on both sides. public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel /-- Set the stack size, in bytes, for subsequently spawned Lean runtime threads (`.dedicated` task threads and not-yet-spawned pool workers) — @@ -392,7 +392,7 @@ def checkEnvParallel (kenv : KEnv m) (prims : Primitives m) failures := failures.qsort fun a b => a.1 < b.1 elapsedMs := endMs - startMs } -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Primitive.lean b/Ix/Kernel/Primitive.lean similarity index 99% rename from Ix/Tc/Primitive.lean rename to Ix/Kernel/Primitive.lean index 026479be2..71e71a3b2 100644 --- a/Ix/Tc/Primitive.lean +++ b/Ix/Kernel/Primitive.lean @@ -1,6 +1,6 @@ module -public import Ix.Tc.Env +public import Ix.Kernel.Env /-! Mirror: crates/kernel/src/primitive.rs @@ -23,7 +23,7 @@ nested-inductive recursor generation), mirroring Rust. public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel /-- Hardcoded canonical primitive addresses (for lookup in the env). -/ structure PrimAddrs where @@ -593,7 +593,7 @@ def ofAnonAddrs : Primitives .anon := end Primitives -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Subst.lean b/Ix/Kernel/Subst.lean similarity index 99% rename from Ix/Tc/Subst.lean rename to Ix/Kernel/Subst.lean index 5e8329e57..462a12129 100644 --- a/Ix/Tc/Subst.lean +++ b/Ix/Kernel/Subst.lean @@ -1,6 +1,6 @@ module -public import Ix.Tc.Env +public import Ix.Kernel.Env /-! Mirror: crates/kernel/src/subst.rs @@ -21,7 +21,7 @@ node avoids gratuitous re-interning. public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel /-- State monad over the intern table — the Lean face of Rust's `&mut InternTable` threading. -/ @@ -463,14 +463,14 @@ def internAppChain (base : KExpr m) : List (KExpr m) → InternM m (KExpr m) /-- Cheap beta reduction: peephole-reduce `App(λ…λ. body, args)` without full `subst` in trivial cases (closed body, or single-bvar body). Otherwise - returns the input unchanged (full WHNF handles it). Mirrors lean4lean's + returns the input unchanged (full WHNF handles it). Mirrors the named specification's `Expr.cheapBetaReduce`. -/ def cheapBetaReduce (e : KExpr m) : InternM m (KExpr m) := do match cheapBetaPlan? e with | none => return e | some plan => internAppChain plan.base plan.trailing -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Validate.lean b/Ix/Kernel/Validate.lean similarity index 98% rename from Ix/Tc/Validate.lean rename to Ix/Kernel/Validate.lean index 9b27859fa..f0580674f 100644 --- a/Ix/Tc/Validate.lean +++ b/Ix/Kernel/Validate.lean @@ -1,13 +1,13 @@ module -public import Ix.Tc.Driver -public import Ix.Tc.IngressMeta -public import Ix.Tc.ParCheck -public import Ix.Tc.EgressLean +public import Ix.Kernel.Driver +public import Ix.Kernel.IngressMeta +public import Ix.Kernel.ParCheck +public import Ix.Kernel.EgressLean public import Ix.CanonM /-! -Whole-env validation drivers for the pure-Lean `Ix.Tc` pipeline — the +Whole-env validation drivers for the pure-Lean `Ix.Kernel` pipeline — the shared core behind the `tc-roundtrip` test suite and `ix validate-lean`. Three gates over a Rust-compiled `.ixe` byte image: @@ -18,7 +18,7 @@ Three gates over a Rust-compiled `.ixe` byte image: bytes EXACTLY. 2. `anonRoundtripEnv` — structural kernel roundtrip: every constant anon-ingressed, egressed back to `Ixon.Constant`, canonically compared - (see `Ix.Tc.Egress`); projections byte-exact. Parallel per work item. + (see `Ix.Kernel.Egress`); projections byte-exact. Parallel per work item. 3. `metaRoundtripEnv` — full-fidelity kernel roundtrip against the SOURCE Lean environment (the oracle): phase-parallel meta ingress of the whole env into one merged `KEnv .meta`, then per-named-entry egress to @@ -36,7 +36,7 @@ Three gates over a Rust-compiled `.ixe` byte image: public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel open Std (HashMap) @@ -364,7 +364,7 @@ def metaRoundtripEnvStreaming (leanEnv : Lean.Environment) report := { report with errors := report.errors.push (name, msg) } return report -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc/Verify/Audit/Basic.lean b/Ix/Kernel/Verify/Audit/Basic.lean similarity index 64% rename from Ix/Tc/Verify/Audit/Basic.lean rename to Ix/Kernel/Verify/Audit/Basic.lean index 54f1c2f61..69b1991bb 100644 --- a/Ix/Tc/Verify/Audit/Basic.lean +++ b/Ix/Kernel/Verify/Audit/Basic.lean @@ -1,16 +1,18 @@ import Lean.Elab.Command import Lean.PrivateName -import Lean.Util.CollectAxioms import Lean.Util.FoldConsts +import Ix.Theory.Named.Std.AxiomAudit /-! -# Exact trust-boundary auditing for `Ix.Tc.Verify` +# Exact trust-boundary auditing for `Ix.Kernel.Verify` -`Lean.collectAxioms` gives the kernel-computed, transitive axiom set for a -declaration. This module adds two pieces needed by the verification plan: +The audit traverses the checked declarations directly, including types, +theorem bodies, and inductive constructors. Imported axiom summaries can +omit dependencies of recursive declaration groups, so they are not the +source of truth here. The audit checks: * an exact, per-root allowlist split into ordinary Lean axioms, explicitly - named upstream implementation axioms, quarantined pending-upstream axioms, + named implementation bridge axioms, quarantined pending metatheory axioms, and generated `native_decide` axioms; * an exact list of the reachable declarations that use `sorryAx` directly, so permitting `sorryAx` cannot hide where that debt entered the proof. @@ -21,7 +23,7 @@ import context from the concrete translation relations with which their opaque names currently collide. -/ -namespace Ix.Tc.Verify.Audit +namespace Ix.Kernel.Verify.Audit open Lean open Lean.Elab.Command @@ -29,19 +31,19 @@ open Lean.Elab.Command /-- The complete permitted trust boundary for one exported theorem root. Lean usually gives generated native axioms private names such as -`_private.Ix.Tc.Expr.0....`; a public theorem proved directly by +`_private.Ix.Kernel.Expr.0....`; a public theorem proved directly by `native_decide` can instead expose a public generated axiom. Use `nativeAxiom` below for the private case. `sorryOrigins` is checked by traversing the root's dependency graph. -/ structure RootAllowance where root : Lean.Name standardAxioms : Array Lean.Name := #[] - /-- Nonlogical implementation bridge axioms inherited from an upstream - package. These remain separate from Lean's three permitted logical axioms - so an executable upstream fixture cannot silently widen `standardAxioms`. -/ - upstreamAxioms : Array Lean.Name := #[] - /-- Temporary local witnesses for facts expected from a future upstream - release. Only the quarantined `Ix.Tc.Upstream.Pending` namespace may occur + /-- Nonlogical implementation bridge axioms retained in the local proof + support. These remain separate from Lean's three permitted logical axioms + so an executable fixture cannot silently widen `standardAxioms`. -/ + implementationAxioms : Array Lean.Name := #[] + /-- Temporary local witnesses for unfinished metatheory proofs. + Only the quarantined `Ix.Kernel.Frontier.Pending` namespace may occur here; completed theorem roots must leave this category empty. -/ pendingAxioms : Array Lean.Name := #[] nativeAxioms : Array Lean.Name := #[] @@ -63,49 +65,10 @@ private def permittedStandardAxioms : Array Lean.Name := private def sortNames (xs : Array Name) : Array Name := xs.qsort Name.lt -/-- Direct constant references, following the same declaration cases as -`Lean.collectAxioms`. In particular, opaque theorem values and inductive -constructors are included. -/ -private def directConstants : ConstantInfo → Array Name - | .axiomInfo v => v.type.getUsedConstants - | .defnInfo v => v.type.getUsedConstants ++ v.value.getUsedConstants - | .thmInfo v => v.type.getUsedConstants ++ v.value.getUsedConstants - | .opaqueInfo v => v.type.getUsedConstants ++ v.value.getUsedConstants - | .quotInfo _ => #[] - | .ctorInfo v => v.type.getUsedConstants - | .recInfo v => v.type.getUsedConstants - | .inductInfo v => v.type.getUsedConstants ++ v.ctors - namespace DependencyAudit -structure State where - visited : NameSet := {} - names : Array Name := #[] - origins : Array Name := #[] - -abbrev M := ReaderT Environment (StateM State) - -/-- Traverse the checked kernel environment and record each reachable -declaration whose type or value directly mentions `sorryAx`. -/ -partial def visit (declName : Name) : M Unit := do - let state ← get - unless state.visited.contains declName do - modify fun s => - { s with - visited := s.visited.insert declName - names := s.names.push declName } - let env ← read - match env.checked.get.find? declName with - | none => pure () - | some info => - let dependencies := directConstants info - if declName != ``sorryAx && dependencies.contains ``sorryAx then - modify fun s => { s with origins := s.origins.push declName } - dependencies.forM visit - -def collect (env : Environment) (root : Name) : State := - let (_, state) := ((visit root).run env).run {} - state +abbrev State := Ix.Theory.Named.AxiomAudit.State +abbrev collect := Ix.Theory.Named.AxiomAudit.collect end DependencyAudit @@ -114,20 +77,20 @@ private def validateCategories (allowance : RootAllowance) : for axiomName in allowance.standardAxioms do unless permittedStandardAxioms.contains axiomName do throwError m!"{allowance.root}: {axiomName} is not a permitted standard Lean axiom" - for axiomName in allowance.upstreamAxioms do + for axiomName in allowance.implementationAxioms do let rendered := axiomName.toString unless rendered.startsWith "Lean." || rendered.startsWith "Std." || - rendered.startsWith "Lean4Lean." do - throwError m!"{allowance.root}: upstream axiom is outside Lean/Std/Lean4Lean: {axiomName}" + rendered.startsWith "Ix.Theory.Named." do + throwError m!"{allowance.root}: implementation axiom is outside Lean/Std/Ix.Theory.Named: {axiomName}" if permittedStandardAxioms.contains axiomName then - throwError m!"{allowance.root}: standard axiom misclassified as upstream: {axiomName}" + throwError m!"{allowance.root}: standard axiom misclassified as implementation: {axiomName}" if axiomName == ``sorryAx then throwError m!"{allowance.root}: sorryAx must be accounted for by sorryOrigins" if Lean.isPrivateName axiomName then throwError m!"{allowance.root}: private axiom must be accounted for as native: {axiomName}" for axiomName in allowance.pendingAxioms do - unless axiomName.toString.startsWith "Ix.Tc.Upstream.Pending." do - throwError m!"{allowance.root}: pending axiom is outside Ix.Tc.Upstream.Pending: {axiomName}" + unless axiomName.toString.startsWith "Ix.Kernel.Frontier.Pending." do + throwError m!"{allowance.root}: pending axiom is outside Ix.Kernel.Frontier.Pending: {axiomName}" if permittedStandardAxioms.contains axiomName then throwError m!"{allowance.root}: standard axiom misclassified as pending: {axiomName}" if axiomName == ``sorryAx then @@ -139,18 +102,19 @@ private def validateCategories (allowance : RootAllowance) : throwError m!"{allowance.root}: malformed native_decide axiom: {axiomName}" private def expectedAxioms (allowance : RootAllowance) : Array Lean.Name := - let expected := allowance.standardAxioms ++ allowance.upstreamAxioms ++ + let expected := allowance.standardAxioms ++ allowance.implementationAxioms ++ allowance.pendingAxioms ++ allowance.nativeAxioms sortNames <| if allowance.sorryOrigins.isEmpty then expected else expected.push ``sorryAx -private def checkOne (allowance : RootAllowance) : CommandElabM Unit := do +private def checkOne (allowance : RootAllowance) (dependencyAudit : DependencyAudit.State) : + CommandElabM Unit := do validateCategories allowance let env ← getEnv unless env.contains allowance.root do throwError m!"axiom-audit root does not exist: {allowance.root}" - let actualAxioms := sortNames (← Lean.collectAxioms allowance.root) + let actualAxioms := sortNames dependencyAudit.axioms let expectedAxioms := expectedAxioms allowance unless actualAxioms == expectedAxioms do let missing := expectedAxioms.filter fun name => @@ -164,7 +128,6 @@ private def checkOne (allowance : RootAllowance) : CommandElabM Unit := do -- Origin and architectural-quarantine checks consume the same transitive -- dependency graph. Keep one exact traversal per root: large generated -- recursor proofs make two independent walks unnecessarily expensive. - let dependencyAudit := DependencyAudit.collect env allowance.root let actualOrigins := sortNames dependencyAudit.origins let expectedOrigins := sortNames allowance.sorryOrigins unless actualOrigins == expectedOrigins do @@ -173,12 +136,12 @@ private def checkOne (allowance : RootAllowance) : CommandElabM Unit := do actual direct origins: {repr actualOrigins.toList}" -- A root is unconditional exactly when it has no explicitly enumerated - -- pending-upstream axioms. Such a root must not reach even axiom-free + -- pending metatheory axioms. Such a root must not reach even axiom-free -- helper definitions from the quarantine module: otherwise replacing a -- pending witness could silently change the completed proof surface. if allowance.pendingAxioms.isEmpty then for dependency in dependencyAudit.names do - if dependency.toString.startsWith "Ix.Tc.Upstream.Pending." then + if dependency.toString.startsWith "Ix.Kernel.Frontier.Pending." then throwError m!"{allowance.root}: unconditional root reaches quarantined dependency {dependency}" for forbidden in allowance.forbiddenDependencies do @@ -188,12 +151,17 @@ private def checkOne (allowance : RootAllowance) : CommandElabM Unit := do /-- Check a complete executable trust manifest. Duplicate roots are rejected instead of being silently audited twice. -/ def check (allowances : Array RootAllowance) : CommandElabM Unit := do + let env ← getEnv let mut roots : NameSet := {} + let mut cache : Ix.Theory.Named.AxiomAudit.Cache := {} for allowance in allowances do if roots.contains allowance.root then throwError m!"duplicate axiom-audit root: {allowance.root}" roots := roots.insert allowance.root - checkOne allowance - logInfo m!"Ix.Tc verification trust audit passed for {allowances.size} theorem roots" + let (dependencies, nextCache) := + Ix.Theory.Named.AxiomAudit.collectCached env allowance.root cache + cache := nextCache + checkOne allowance dependencies + logInfo m!"Ix.Kernel verification trust audit passed for {allowances.size} theorem roots" -end Ix.Tc.Verify.Audit +end Ix.Kernel.Verify.Audit diff --git a/Ix/Tc/Verify/Audit/Completed.lean b/Ix/Kernel/Verify/Audit/Completed.lean similarity index 59% rename from Ix/Tc/Verify/Audit/Completed.lean rename to Ix/Kernel/Verify/Audit/Completed.lean index 920f76509..a9659a1d4 100644 --- a/Ix/Tc/Verify/Audit/Completed.lean +++ b/Ix/Kernel/Verify/Audit/Completed.lean @@ -1,138 +1,140 @@ -import Ix.Tc.Verify.Audit.Basic -import Ix.Tc.Verify.Check.Acceptance -import Ix.Tc.Verify.Check.BoundedPipelines -import Ix.Tc.Verify.Check.CheckerEvidence -import Ix.Tc.Verify.Check.FullInferenceApplications -import Ix.Tc.Verify.Check.FullInferenceBinders -import Ix.Tc.Verify.Check.FullInferenceCache -import Ix.Tc.Verify.Check.FullInferenceDispatcher -import Ix.Tc.Verify.Check.FullInferenceProjections -import Ix.Tc.Verify.Check.MemberEvidence -import Ix.Tc.Verify.Check.NatAcceptance -import Ix.Tc.Verify.Check.BlockNatFixture -import Ix.Tc.Verify.Check.PreTranslationScopes -import Ix.Tc.Verify.Check.PositiveFuelSort -import Ix.Tc.Verify.Check.ScopedPositiveFuelCertificate -import Ix.Tc.Verify.Check.SingletonInductive -import Ix.Tc.Verify.Inductive.EnumerationAcceptance -import Ix.Tc.Verify.Check.ProjectionInferencePolicy -import Ix.Tc.Verify.Check.ResetFrame -import Ix.Tc.Verify.Check.SafetyFrame -import Ix.Tc.Verify.Check.PublicStandalone -import Ix.Tc.Verify.Check.PublicBlocks -import Ix.Tc.Verify.Check.ValidatorFrame -import Ix.Tc.Verify.Ctx -import Ix.Tc.Verify.Decl -import Ix.Tc.Verify.DefEq -import Ix.Tc.Verify.DefEq.AcceleratorGates -import Ix.Tc.Verify.DefEq.ApplicationSpine -import Ix.Tc.Verify.DefEq.CacheShell -import Ix.Tc.Verify.DefEq.Closure -import Ix.Tc.Verify.DefEq.DeltaClassification -import Ix.Tc.Verify.DefEq.EqualRankCache -import Ix.Tc.Verify.DefEq.EqualRankPrefix -import Ix.Tc.Verify.DefEq.EqualRankReduction -import Ix.Tc.Verify.DefEq.FinalWhnf.Application -import Ix.Tc.Verify.DefEq.FinalWhnf.Closure -import Ix.Tc.Verify.DefEq.FinalWhnf.Contracts -import Ix.Tc.Verify.DefEq.FinalWhnf.EtaExpansion -import Ix.Tc.Verify.DefEq.FinalWhnf.LetDeclaration -import Ix.Tc.Verify.DefEq.FinalWhnf.NatBridge -import Ix.Tc.Verify.DefEq.FinalWhnf.ProofTail -import Ix.Tc.Verify.DefEq.FinalWhnf.StringExpansion -import Ix.Tc.Verify.DefEq.FinalWhnf.StructuralPrefix -import Ix.Tc.Verify.DefEq.FinalWhnf.StructureEta -import Ix.Tc.Verify.DefEq.FinalWhnf.UnitLike -import Ix.Tc.Verify.DefEq.LazyDelta -import Ix.Tc.Verify.DefEq.LazyDeltaClosure -import Ix.Tc.Verify.DefEq.LazyDeltaIteration -import Ix.Tc.Verify.DefEq.LoopFinish -import Ix.Tc.Verify.DefEq.NatOffset -import Ix.Tc.Verify.DefEq.NatOffsetDecomposition -import Ix.Tc.Verify.DefEq.NatReduction -import Ix.Tc.Verify.DefEq.OneSidedDelta -import Ix.Tc.Verify.DefEq.ProjectionDeltaActive -import Ix.Tc.Verify.DefEq.ProjectionDeltaClosure -import Ix.Tc.Verify.DefEq.ProjectionDeltaEqualRank -import Ix.Tc.Verify.DefEq.ProjectionDeltaFinish -import Ix.Tc.Verify.DefEq.ProjectionDeltaLoop -import Ix.Tc.Verify.DefEq.ProjectionDeltaRank -import Ix.Tc.Verify.DefEq.ProjectionDeltaStep -import Ix.Tc.Verify.DefEq.ProjectionDeltaUnfolding -import Ix.Tc.Verify.DefEq.ProjectionProbe -import Ix.Tc.Verify.DefEq.ProjectionReduction -import Ix.Tc.Verify.DefEq.PropositionClassifier -import Ix.Tc.Verify.DefEq.RankDispatch -import Ix.Tc.Verify.DefEq.SameHeadSpine -import Ix.Tc.Verify.DefEq.SpineArguments -import Ix.Tc.Verify.DefEq.StoppedContinuation -import Ix.Tc.Verify.DefEq.StoppedContinuationClosure -import Ix.Tc.Verify.DefEq.StructuralCongruence -import Ix.Tc.Verify.Driver.Fixtures -import Ix.Tc.Verify.Driver.BooleanAcceptance -import Ix.Tc.Verify.Driver.SupportedAcceptanceFixtures -import Ix.Tc.Verify.Execution -import Ix.Tc.Verify.Frame -import Ix.Tc.Verify.Infer.CacheSoundness -import Ix.Tc.Verify.InferDefEq.Closure -import Ix.Tc.Verify.Inductive.Certificate -import Ix.Tc.Verify.Inductive.AliasFormerAdmission -import Ix.Tc.Verify.Inductive.AliasRecAdmission -import Ix.Tc.Verify.Inductive.AnnotatedPiCertificate -import Ix.Tc.Verify.Inductive.AnnotatedPiAdmission -import Ix.Tc.Verify.Inductive.EliminationBreadthFixture -import Ix.Tc.Verify.Inductive.MutualBlockCertificate -import Ix.Tc.Verify.Inductive.MutualFamilyAdmission -import Ix.Tc.Verify.Inductive.IndexedRecursiveCertificate -import Ix.Tc.Verify.Inductive.RecursivePiCertificate -import Ix.Tc.Verify.Inductive.RecursivePiAdmission -import Ix.Tc.Verify.Inductive.IndexedRecursiveAcceptance -import Ix.Tc.Verify.Inductive.IndexedConstructorValidation -import Ix.Tc.Verify.Inductive.SpecializationIdentity -import Ix.Tc.Verify.Inductive.GeneratedRecursorMetadata -import Ix.Tc.Verify.Inductive.GeneratedRecursorAcceptance -import Ix.Tc.Verify.Inductive.GeneratedRecursorAcceptanceClosure -import Ix.Tc.Verify.Inductive.GeneratedRecursorAdmission -import Ix.Tc.Verify.Inductive.IndexedProducerClosure -import Ix.Tc.Verify.Inductive.GeneratedRecursorCheckerFixture -import Ix.Tc.Verify.Inductive.GeneratedRecursorCommitFixture -import Ix.Tc.Verify.Inductive.GeneratedRecursorComparison -import Ix.Tc.Verify.Inductive.GeneratedRecursorRuleFixture -import Ix.Tc.Verify.Inductive.GeneratedRecursorSelection -import Ix.Tc.Verify.Inductive.GeneratedRecursorSemantics -import Ix.Tc.Verify.Inductive.GeneratedRecursorTypeClosure -import Ix.Tc.Verify.Inductive.GeneratedRecursorTypeFixture -import Ix.Tc.Verify.Inductive.NestedAuxiliaryExpansion -import Ix.Tc.Verify.Inductive.NestedAdmission -import Ix.Tc.Verify.Inductive.NestedConstructorValidation -import Ix.Tc.Verify.Inductive.NestedRecursiveFixture -import Ix.Tc.Verify.Inductive.NestedRecursorAdmission -import Ix.Tc.Verify.Inductive.OccurrenceClosure -import Ix.Tc.Verify.Inductive.PositivityTraceAdapter -import Ix.Tc.Verify.Inductive.RecursivePositivityTraversal -import Ix.Tc.Verify.Ingress.LiteralBlobs -import Ix.Tc.Verify.Ingress.SerializedBoolean -import Ix.Tc.Verify.InstL -import Ix.Tc.Verify.Whnf.Closure -import Ix.Tc.Verify.Knot -import Ix.Tc.Verify.NatFixture -import Ix.Tc.Verify.Projection.ConcreteFixture -import Ix.Tc.Verify.Run -import Ix.Tc.Verify.RecursiveMethods.Closure -import Ix.Tc.Verify.RecursiveMethods.FiniteSupportBoundary -import Ix.Tc.Verify.RecursiveMethods.Public -import Ix.Tc.Verify.Support -import Ix.Tc.Verify.Totalization -import Ix.Tc.Verify.Whnf -import Ix.Tc.Verify.World +import Ix.Kernel.Verify.Audit.Basic +import Ix.Kernel.Verify.Check.Acceptance +import Ix.Kernel.Verify.Check.BoundedPipelines +import Ix.Kernel.Verify.Check.CheckerEvidence +import Ix.Kernel.Verify.Check.FullInferenceApplications +import Ix.Kernel.Verify.Check.FullInferenceBinders +import Ix.Kernel.Verify.Check.FullInferenceCache +import Ix.Kernel.Verify.Check.FullInferenceDispatcher +import Ix.Kernel.Verify.Check.FullInferenceProjections +import Ix.Kernel.Verify.Check.MemberEvidence +import Ix.Kernel.Verify.Check.NatAcceptance +import Ix.Kernel.Verify.Check.BlockNatFixture +import Ix.Kernel.Verify.Check.PreTranslationScopes +import Ix.Kernel.Verify.Check.PositiveFuelSort +import Ix.Kernel.Verify.Check.ScopedPositiveFuelCertificate +import Ix.Kernel.Verify.Check.SingletonInductive +import Ix.Kernel.Verify.Inductive.EnumerationAcceptance +import Ix.Kernel.Verify.Check.ProjectionInferencePolicy +import Ix.Kernel.Verify.Check.ResetFrame +import Ix.Kernel.Verify.Check.SafetyFrame +import Ix.Kernel.Verify.Check.PublicStandalone +import Ix.Kernel.Verify.Check.PublicBlocks +import Ix.Kernel.Verify.Check.ValidatorFrame +import Ix.Kernel.Verify.Ctx +import Ix.Kernel.Verify.Decl +import Ix.Kernel.Verify.DefEq +import Ix.Kernel.Verify.DefEq.AcceleratorGates +import Ix.Kernel.Verify.DefEq.ApplicationSpine +import Ix.Kernel.Verify.DefEq.CacheShell +import Ix.Kernel.Verify.DefEq.Closure +import Ix.Kernel.Verify.DefEq.DeltaClassification +import Ix.Kernel.Verify.DefEq.EqualRankCache +import Ix.Kernel.Verify.DefEq.EqualRankPrefix +import Ix.Kernel.Verify.DefEq.EqualRankReduction +import Ix.Kernel.Verify.DefEq.FinalWhnf.Application +import Ix.Kernel.Verify.DefEq.FinalWhnf.Closure +import Ix.Kernel.Verify.DefEq.FinalWhnf.Contracts +import Ix.Kernel.Verify.DefEq.FinalWhnf.EtaExpansion +import Ix.Kernel.Verify.DefEq.FinalWhnf.LetDeclaration +import Ix.Kernel.Verify.DefEq.FinalWhnf.NatBridge +import Ix.Kernel.Verify.DefEq.FinalWhnf.ProofTail +import Ix.Kernel.Verify.DefEq.FinalWhnf.StringExpansion +import Ix.Kernel.Verify.DefEq.FinalWhnf.StructuralPrefix +import Ix.Kernel.Verify.DefEq.FinalWhnf.StructureEta +import Ix.Kernel.Verify.DefEq.FinalWhnf.UnitLike +import Ix.Kernel.Verify.DefEq.LazyDelta +import Ix.Kernel.Verify.DefEq.LazyDeltaClosure +import Ix.Kernel.Verify.DefEq.LazyDeltaIteration +import Ix.Kernel.Verify.DefEq.LoopFinish +import Ix.Kernel.Verify.DefEq.NatOffset +import Ix.Kernel.Verify.DefEq.NatOffsetDecomposition +import Ix.Kernel.Verify.DefEq.NatReduction +import Ix.Kernel.Verify.DefEq.OneSidedDelta +import Ix.Kernel.Verify.DefEq.ProjectionDeltaActive +import Ix.Kernel.Verify.DefEq.ProjectionDeltaClosure +import Ix.Kernel.Verify.DefEq.ProjectionDeltaEqualRank +import Ix.Kernel.Verify.DefEq.ProjectionDeltaFinish +import Ix.Kernel.Verify.DefEq.ProjectionDeltaLoop +import Ix.Kernel.Verify.DefEq.ProjectionDeltaRank +import Ix.Kernel.Verify.DefEq.ProjectionDeltaStep +import Ix.Kernel.Verify.DefEq.ProjectionDeltaUnfolding +import Ix.Kernel.Verify.DefEq.ProjectionProbe +import Ix.Kernel.Verify.DefEq.ProjectionReduction +import Ix.Kernel.Verify.DefEq.PropositionClassifier +import Ix.Kernel.Verify.DefEq.RankDispatch +import Ix.Kernel.Verify.DefEq.SameHeadSpine +import Ix.Kernel.Verify.DefEq.SpineArguments +import Ix.Kernel.Verify.DefEq.StoppedContinuation +import Ix.Kernel.Verify.DefEq.StoppedContinuationClosure +import Ix.Kernel.Verify.DefEq.StructuralCongruence +import Ix.Kernel.Verify.Driver.Fixtures +import Ix.Kernel.Verify.Driver.BooleanAcceptance +import Ix.Kernel.Verify.Driver.SupportedAcceptanceFixtures +import Ix.Kernel.Verify.Execution +import Ix.Kernel.Verify.Frame +import Ix.Kernel.Verify.Infer.CacheSoundness +import Ix.Kernel.Verify.InferDefEq.Closure +import Ix.Kernel.Verify.Inductive.Certificate +import Ix.Kernel.Verify.Inductive.AliasFormerAdmission +import Ix.Kernel.Verify.Inductive.AliasRecAdmission +import Ix.Kernel.Verify.Inductive.AnnotatedPiCertificate +import Ix.Kernel.Verify.Inductive.AnnotatedPiAdmission +import Ix.Kernel.Verify.Inductive.EliminationBreadthFixture +import Ix.Kernel.Verify.Inductive.MutualBlockCertificate +import Ix.Kernel.Verify.Inductive.MutualFamilyAdmission +import Ix.Kernel.Verify.Inductive.IndexedRecursiveCertificate +import Ix.Kernel.Verify.Inductive.RecursivePiCertificate +import Ix.Kernel.Verify.Inductive.RecursivePiAdmission +import Ix.Kernel.Verify.Inductive.IndexedRecursiveAcceptance +import Ix.Kernel.Verify.Inductive.IndexedConstructorValidation +import Ix.Kernel.Verify.Inductive.SpecializationIdentity +import Ix.Kernel.Verify.Inductive.GeneratedRecursorMetadata +import Ix.Kernel.Verify.Inductive.GeneratedRecursorAcceptance +import Ix.Kernel.Verify.Inductive.GeneratedRecursorAcceptanceClosure +import Ix.Kernel.Verify.Inductive.GeneratedRecursorAdmission +import Ix.Kernel.Verify.Inductive.IndexedProducerClosure +import Ix.Kernel.Verify.Inductive.GeneratedRecursorCheckerFixture +import Ix.Kernel.Verify.Inductive.GeneratedRecursorCommitFixture +import Ix.Kernel.Verify.Inductive.GeneratedRecursorComparison +import Ix.Kernel.Verify.Inductive.GeneratedRecursorRuleFixture +import Ix.Kernel.Verify.Inductive.GeneratedRecursorSelection +import Ix.Kernel.Verify.Inductive.GeneratedRecursorSemantics +import Ix.Kernel.Verify.Inductive.GeneratedRecursorTypeClosure +import Ix.Kernel.Verify.Inductive.GeneratedRecursorTypeFixture +import Ix.Kernel.Verify.Inductive.NestedAuxiliaryExpansion +import Ix.Kernel.Verify.Inductive.NestedAdmission +import Ix.Kernel.Verify.Inductive.NestedConstructorValidation +import Ix.Kernel.Verify.Inductive.NestedRecursiveFixture +import Ix.Kernel.Verify.Inductive.NestedRecursorAdmission +import Ix.Kernel.Verify.Inductive.OccurrenceClosure +import Ix.Kernel.Verify.Inductive.PositivityTraceAdapter +import Ix.Kernel.Verify.Inductive.RecursivePositivityTraversal +import Ix.Kernel.Verify.Ingress.LiteralBlobs +import Ix.Kernel.Verify.Ingress.SerializedBoolean +import Ix.Kernel.Verify.InstL +import Ix.Kernel.Verify.Whnf.Closure +import Ix.Kernel.Verify.Knot +import Ix.Kernel.Verify.NatFixture +import Ix.Kernel.Verify.Projection.ConcreteFixture +import Ix.Kernel.Verify.Run +import Ix.Kernel.Verify.RecursiveMethods.Closure +import Ix.Kernel.Verify.RecursiveMethods.FiniteSupportBoundary +import Ix.Kernel.Verify.RecursiveMethods.Public +import Ix.Kernel.Verify.Support +import Ix.Kernel.Verify.Totalization +import Ix.Kernel.Verify.Whnf +import Ix.Kernel.Verify.World + +open Ix.Theory (VLevel) /-! -# Trust manifest for the completed `Ix.Tc.Verify` proof surface +# Trust manifest for the completed `Ix.Kernel.Verify` proof surface These are the current completed foundations and reusable semantic interfaces -that later C1--C3 roots will consume. A new headline theorem must be added here -when it becomes part of that exported proof surface. The temporary C1/C2 -statement skeletons are audited separately in `Audit/Statements.lean` +used by lookup, admission, and driver composition. A new headline theorem +must be added here when it becomes part of that exported proof surface. The +temporary statement skeletons are audited separately in `Audit/Statements.lean` because their opaque relation names intentionally collide with the concrete relations imported here. @@ -141,9 +143,9 @@ transitive trust boundary of any one interface should produce a focused CI failure. Shared arrays below are only labels for exactly repeated sets. -/ -namespace Ix.Tc.Verify.Audit.Completed +namespace Ix.Kernel.Verify.Audit.Completed -open Ix.Tc.Verify.Audit +open Ix.Kernel.Verify.Audit private def standard : Array Lean.Name := #[``propext, ``Classical.choice, ``Quot.sound] @@ -156,12 +158,12 @@ private def standardWithoutQuot : Array Lean.Name := private def propextOnly : Array Lean.Name := #[``propext] -/- The executable `AnnotatedPi` replay in the pinned Lean4Lean fork crosses +/- The executable `AnnotatedPi` replay in the local named specification crosses its verified wrappers for Lean's pointer-aware expression implementation. Keep that nonlogical upstream footprint distinct from `standard`: these are not ordinary logical axioms and must not become globally permitted. -/ private def annotatedPiUpstreamAxioms : Array Lean.Name := #[ - ``Lean4Lean.ptrEqConstantInfo_eq, + ``Ix.Theory.Named.ptrEqConstantInfo_eq, ``Lean.Expr.abstractRange_eq, ``Lean.Expr.abstract_eq, ``Lean.Expr.eqv_eq, @@ -189,17 +191,17 @@ private def annotatedPiUpstreamAxioms : Array Lean.Name := #[ ``Std.TreeMap.all_eq_all_toList ] -/- Exact direct `sorryAx` frontier inherited from Lean4Lean's executable +/- Exact direct `sorryAx` frontier inherited from Ix.Theory.Named's executable candidate-normalization proof. Unlike the earlier closed-form fixtures, `AnnotatedPi` exercises the verified implementation path far enough to reach the currently declared projection/typechecker proof debt. -/ private def annotatedPiUpstreamDebt : Array Lean.Name := #[ - ``Lean4Lean.VEnv.IsDefEqU.forallE_inv_stratified, - ``Lean4Lean.VEnv.IsDefEqU.sort_forallE_inv, - ``Lean4Lean.VEnv.IsDefEqU.sort_inv, - ``Lean4Lean.VEnv.IsDefEqU.weakN_iff, - ``Lean4Lean.VEnv.WF.registeredStructureHeadInversion, - ``Lean4Lean.TypeChecker.Inner.reduceRecursor.WF + ``Ix.Theory.Named.VEnv.IsDefEqU.forallE_inv_stratified, + ``Ix.Theory.Named.VEnv.IsDefEqU.sort_forallE_inv, + ``Ix.Theory.Named.VEnv.IsDefEqU.sort_inv, + ``Ix.Theory.Named.VEnv.IsDefEqU.weakN_iff, + ``Ix.Theory.Named.VEnv.WF.registeredStructureHeadInversion, + ``Ix.Theory.Named.TypeChecker.Inner.reduceRecursor.WF ] /- `AliasFormer` reaches the same executable normalization boundary as @@ -211,7 +213,7 @@ private def aliasFormerUpstreamAxioms : Array Lean.Name := private def aliasFormerUpstreamDebt : Array Lean.Name := annotatedPiUpstreamDebt.push - ``Lean4Lean.InductiveReplayFixtures.aliasFormerAlignmentRun + ``Ix.Theory.Named.InductiveReplayFixtures.aliasFormerAlignmentRun /- `AliasRec` reaches the same executable normalization boundary while unfolding a reducible wrapper around a recursive constructor field. Keep its @@ -222,31 +224,26 @@ private def aliasRecUpstreamAxioms : Array Lean.Name := private def aliasRecUpstreamDebt : Array Lean.Name := annotatedPiUpstreamDebt -private def blake3Native : Array Lean.Name := #[ - nativeAxiom `Blake3 - `Blake3.HasherOps.hash._native.native_decide.ax_1 -] - -private def expressionNative : Array Lean.Name := blake3Native.push - (nativeAxiom `Ix.Tc.Expr - `Ix.Tc.KExpr.mkVar._native.native_decide.ax_1) +private def expressionNative : Array Lean.Name := #[].push + (nativeAxiom `Ix.Kernel.Expr + `Ix.Kernel.KExpr.mkVar._native.native_decide.ax_1) private def levelNative : Array Lean.Name := expressionNative.push - (nativeAxiom `Ix.Tc.Level - `Ix.Tc.KUniv.mkSucc._native.native_decide.ax_1) + (nativeAxiom `Ix.Kernel.Level + `Ix.Kernel.KUniv.mkSucc._native.native_decide.ax_1) -private def occurrenceValidationNative : Array Lean.Name := blake3Native.push - (nativeAxiom `Ix.Tc.Level - `Ix.Tc.KUniv.mkSucc._native.native_decide.ax_1) +private def occurrenceValidationNative : Array Lean.Name := #[].push + (nativeAxiom `Ix.Kernel.Level + `Ix.Kernel.KUniv.mkSucc._native.native_decide.ax_1) private def specializationIdentityNative : Array Lean.Name := occurrenceValidationNative.push - (nativeAxiom `Ix.Tc.Verify.Inductive.SpecializationIdentity - `Ix.Tc.SpecializationIdentityFixture.semanticUniverseEquality_does_not_collapse_specializationNative._native.native_decide.ax_1_1) + (nativeAxiom `Ix.Kernel.Verify.Inductive.SpecializationIdentity + `Ix.Kernel.SpecializationIdentityFixture.semanticUniverseEquality_does_not_collapse_specializationNative._native.native_decide.ax_1_1) private def univOnlyNative : Array Lean.Name := #[ - nativeAxiom `Ix.Tc.Level - `Ix.Tc.KUniv.mkSucc._native.native_decide.ax_1 + nativeAxiom `Ix.Kernel.Level + `Ix.Kernel.KUniv.mkSucc._native.native_decide.ax_1 ] private def nameDecideNative : Lean.Name := @@ -259,38 +256,31 @@ private def expressionNameNative : Array Lean.Name := expressionNative.push nameDecideNative private def canonicalPrimitivesNative : Array Lean.Name := - blake3Native.push nameDecideNative - -private def ctxAddrNative : Lean.Name := - nativeAxiom `Ix.Tc.Monad - `Ix.Tc.TcM.ctxAddrForLbrUncached._native.native_decide.ax_3 - -private def blake3ContextNative : Array Lean.Name := - blake3Native.push ctxAddrNative + #[].push nameDecideNative private def contextNative : Array Lean.Name := - expressionNative.push ctxAddrNative + expressionNative private def inferNative : Array Lean.Name := - levelNative.push ctxAddrNative + levelNative private def nameContextNative : Array Lean.Name := - nameNative.push ctxAddrNative + nameNative private def canonicalPrimitivesContextNative : Array Lean.Name := - canonicalPrimitivesNative.push ctxAddrNative + canonicalPrimitivesNative private def natAddNeSuccNative : Lean.Name := - nativeAxiom `Ix.Tc.Verify.NatFixture - `Ix.Tc.AmbientNat.natAdd_ne_natSucc._native.native_decide.ax_1_1 + nativeAxiom `Ix.Kernel.Verify.NatFixture + `Ix.Kernel.AmbientNat.natAdd_ne_natSucc._native.native_decide.ax_1_1 private def natAddNeBeqNative : Lean.Name := - nativeAxiom `Ix.Tc.Verify.NatFixture - `Ix.Tc.AmbientNat.natAdd_ne_natBeq._native.native_decide.ax_1_1 + nativeAxiom `Ix.Kernel.Verify.NatFixture + `Ix.Kernel.AmbientNat.natAdd_ne_natBeq._native.native_decide.ax_1_1 private def natAddNeBleNative : Lean.Name := - nativeAxiom `Ix.Tc.Verify.NatFixture - `Ix.Tc.AmbientNat.natAdd_ne_natBle._native.native_decide.ax_1_1 + nativeAxiom `Ix.Kernel.Verify.NatFixture + `Ix.Kernel.AmbientNat.natAdd_ne_natBle._native.native_decide.ax_1_1 private def natReductionNative : Array Lean.Name := (((contextNative.push nameDecideNative).push natAddNeSuccNative).push @@ -311,8 +301,8 @@ private def natBranchOrderNative : Array Lean.Name := private def inductiveNative : Array Lean.Name := (inferNative.push (nativeAxiom `Ix.Environment `Ix.Name.mkStr._native.native_decide.ax_1)).push - (nativeAxiom `Ix.Tc.Inductive - `Ix.Tc.RecM.canonicalAuxOrder._native.native_decide.ax_9) + (nativeAxiom `Ix.Kernel.Inductive + `Ix.Kernel.RecM.canonicalAuxOrder._native.native_decide.ax_9) /- Mutual-block fixtures use many small closed `native_decide` facts. Build their exact private names structurally so the audit stays reviewable while @@ -321,7 +311,7 @@ private def mutualNativeUserName (decl : String) (index : Nat) : Lean.Name := Lean.Name.str (Lean.Name.str (Lean.Name.str - (Lean.Name.str `Ix.Tc.MutualTreeFixture decl) + (Lean.Name.str `Ix.Kernel.MutualTreeFixture decl) "_native") "native_decide") s!"ax_1_{index + 1}" @@ -341,15 +331,15 @@ private def mutualNativeSingletons (moduleName : Lean.Name) private def mutualFamilyAdmissionNativeSeries (decl : String) (count : Nat) : Array Lean.Name := - mutualNativeSeries `Ix.Tc.Verify.Inductive.MutualFamilyAdmission decl count + mutualNativeSeries `Ix.Kernel.Verify.Inductive.MutualFamilyAdmission decl count private def mutualBlockFixtureNativeSeries (decl : String) (count : Nat) : Array Lean.Name := - mutualNativeSeries `Ix.Tc.Verify.Inductive.MutualBlockFixture decl count + mutualNativeSeries `Ix.Kernel.Verify.Inductive.MutualBlockFixture decl count private def mutualRecursorAdmissionNativeSeries (decl : String) (count : Nat) : Array Lean.Name := - mutualNativeSeries `Ix.Tc.Verify.Inductive.MutualRecursorAdmission decl count + mutualNativeSeries `Ix.Kernel.Verify.Inductive.MutualRecursorAdmission decl count private def mutualInternDataValueNative : Lean.Name := nativeAxiom `Ix.CanonM @@ -378,20 +368,20 @@ def mutualFamilyNative : Array Lean.Name := mutualPublicNativeSeries "nameOf_treeList" 2 ++ mutualPublicNativeSeries "familyMembers_eq" 1 ++ mutualPublicNativeSeries "recursorMembers_eq" 1 ++ - mutualNativeSingletons `Ix.Tc.Verify.Inductive.MutualBlockFixture #[ + mutualNativeSingletons `Ix.Kernel.Verify.Inductive.MutualBlockFixture #[ "familyAuxCompileSucceededNative", "familyBlockLoadedNative", "familyIngressSucceededNative", "recursorBlockLoadedNative", "recursorIngressSucceededNative" ] ++ - mutualNativeSingletons `Ix.Tc.Verify.Inductive.MutualBlockValidation #[ + mutualNativeSingletons `Ix.Kernel.Verify.Inductive.MutualBlockValidation #[ "familyKernelSucceededNative", "recursorKernelSucceededNative" ] ++ mutualFamilyAdmissionNativeSeries "familyMemberShapeFactsNative" 19 ++ mutualFamilyAdmissionNativeSeries "ownershipShapeFactsNative" 9 ++ - mutualNativeSingletons `Ix.Tc.Verify.Inductive.MutualFamilyAdmission #[ + mutualNativeSingletons `Ix.Kernel.Verify.Inductive.MutualFamilyAdmission #[ "treeBranchTypeRawNative", "treeLeafTypeRawNative", "treeListConsTypeRawNative", @@ -411,7 +401,7 @@ def mutualRecursorConditionalNative : Array Lean.Name := mutualRecursorAdmissionNativeSeries "physicalSourceMembershipFactsNative" 5 ++ mutualRecursorAdmissionNativeSeries "recursorRepresentationFactsNative" 55 ++ - mutualNativeSingletons `Ix.Tc.Verify.Inductive.MutualRecursorAdmission #[ + mutualNativeSingletons `Ix.Kernel.Verify.Inductive.MutualRecursorAdmission #[ "branchRuleRawNative", "consRuleRawNative", "flatCtorFour", @@ -443,187 +433,187 @@ def mutualRecursorConditionalNative : Array Lean.Name := ] private def recursivePiFixtureNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.RecursivePiFixture name + nativeAxiom `Ix.Kernel.Verify.Inductive.RecursivePiFixture name private def recursivePiRecursorFixtureNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.RecursivePiRecursorFixture name + nativeAxiom `Ix.Kernel.Verify.Inductive.RecursivePiRecursorFixture name private def recursivePiAdmissionNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.RecursivePiAdmission name + nativeAxiom `Ix.Kernel.Verify.Inductive.RecursivePiAdmission name private def annotatedPiCertificateNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.AnnotatedPiCertificate name + nativeAxiom `Ix.Kernel.Verify.Inductive.AnnotatedPiCertificate name private def annotatedPiCertificateBreadthNative : Array Lean.Name := #[ annotatedPiCertificateNativeAxiom - `Ix.Tc.AnnotatedPiCertificateFixture.breadthNative._native.native_decide.ax_1_1 + `Ix.Kernel.AnnotatedPiCertificateFixture.breadthNative._native.native_decide.ax_1_1 ] private def annotatedPiFixtureNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.AnnotatedPiFixture name + nativeAxiom `Ix.Kernel.Verify.Inductive.AnnotatedPiFixture name private def annotatedPiRecursorFixtureNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.AnnotatedPiRecursorFixture name + nativeAxiom `Ix.Kernel.Verify.Inductive.AnnotatedPiRecursorFixture name private def annotatedPiAdmissionNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.AnnotatedPiAdmission name + nativeAxiom `Ix.Kernel.Verify.Inductive.AnnotatedPiAdmission name private def aliasFormerCertificateNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.AliasFormerCertificate name + nativeAxiom `Ix.Kernel.Verify.Inductive.AliasFormerCertificate name private def aliasFormerCertificateBreadthNative : Array Lean.Name := #[ aliasFormerCertificateNativeAxiom - `Ix.Tc.AliasFormerCertificateFixture.breadthNative._native.native_decide.ax_1_1 + `Ix.Kernel.AliasFormerCertificateFixture.breadthNative._native.native_decide.ax_1_1 ] private def aliasFormerFixtureNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.AliasFormerFixture name + nativeAxiom `Ix.Kernel.Verify.Inductive.AliasFormerFixture name private def aliasFormerPatternNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.AliasFormerPattern name + nativeAxiom `Ix.Kernel.Verify.Inductive.AliasFormerPattern name private def aliasFormerRecursorFixtureNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.AliasFormerRecursorFixture name + nativeAxiom `Ix.Kernel.Verify.Inductive.AliasFormerRecursorFixture name private def aliasFormerAdmissionNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.AliasFormerAdmission name + nativeAxiom `Ix.Kernel.Verify.Inductive.AliasFormerAdmission name private def aliasRecCertificateNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.AliasRecCertificate name + nativeAxiom `Ix.Kernel.Verify.Inductive.AliasRecCertificate name private def aliasRecCertificateBreadthNative : Array Lean.Name := #[ aliasRecCertificateNativeAxiom - `Ix.Tc.AliasRecCertificateFixture.breadthNative._native.native_decide.ax_1_1 + `Ix.Kernel.AliasRecCertificateFixture.breadthNative._native.native_decide.ax_1_1 ] private def aliasRecFixtureNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.AliasRecFixture name + nativeAxiom `Ix.Kernel.Verify.Inductive.AliasRecFixture name private def aliasRecRecursorFixtureNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.AliasRecRecursorFixture name + nativeAxiom `Ix.Kernel.Verify.Inductive.AliasRecRecursorFixture name private def aliasRecAdmissionNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.AliasRecAdmission name + nativeAxiom `Ix.Kernel.Verify.Inductive.AliasRecAdmission name /- Exact executable footprint of the family-result-normalizing AliasFormer family/recursor transaction. -/ private def aliasFormerAtomicClosureNative : Array Lean.Name := inductiveNative ++ aliasFormerCertificateBreadthNative ++ #[ aliasFormerAdmissionNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, aliasFormerAdmissionNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.recursorOwnerNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.recursorOwnerNative._native.native_decide.ax_1_1, aliasFormerFixtureNativeAxiom - `Ix.Tc.AliasFormerFixture.entriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerFixture.entriesSizeNative._native.native_decide.ax_1_1, aliasFormerFixtureNativeAxiom - `Ix.Tc.AliasFormerFixture.entriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerFixture.entriesUniqueNative._native.native_decide.ax_1_1, aliasFormerFixtureNativeAxiom - `Ix.Tc.AliasFormerFixture.entryAtOneNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerFixture.entryAtOneNative._native.native_decide.ax_1_1, aliasFormerFixtureNativeAxiom - `Ix.Tc.AliasFormerFixture.entryAtZeroNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerFixture.entryAtZeroNative._native.native_decide.ax_1_1, aliasFormerFixtureNativeAxiom - `Ix.Tc.AliasFormerFixture.entryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerFixture.entryIdsNative._native.native_decide.ax_1_1, aliasFormerFixtureNativeAxiom - `Ix.Tc.AliasFormerFixture.familyEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerFixture.familyEntryNative._native.native_decide.ax_1_1, aliasFormerFixtureNativeAxiom - `Ix.Tc.AliasFormerFixture.familyIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerFixture.familyIngressSucceededNative._native.native_decide.ax_1_1, aliasFormerFixtureNativeAxiom - `Ix.Tc.AliasFormerFixture.familyShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerFixture.familyShapeNative._native.native_decide.ax_1_1, aliasFormerFixtureNativeAxiom - `Ix.Tc.AliasFormerFixture.memberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerFixture.memberKidsNative._native.native_decide.ax_1_1, aliasFormerFixtureNativeAxiom - `Ix.Tc.AliasFormerFixture.mkEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerFixture.mkEntryNative._native.native_decide.ax_1_1, aliasFormerFixtureNativeAxiom - `Ix.Tc.AliasFormerFixture.mkShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerFixture.mkShapeNative._native.native_decide.ax_1_1, aliasFormerFixtureNativeAxiom - `Ix.Tc.AliasFormerFixture.sourceConstructorZero._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerFixture.sourceConstructorZero._native.native_decide.ax_1_1, aliasFormerFixtureNativeAxiom - `Ix.Tc.AliasFormerFixture.typeFamilyAliasIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerFixture.typeFamilyAliasIngressSucceededNative._native.native_decide.ax_1_1, aliasFormerPatternNativeAxiom - `Ix.Tc.AliasFormerPattern.generationCtorPairsNonempty._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerPattern.generationCtorPairsNonempty._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.catalogFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.catalogFamilyNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.catalogFamilyNative._native.native_decide.ax_1_2, + `Ix.Kernel.AliasFormerRecursorFixture.catalogFamilyNative._native.native_decide.ax_1_2, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.catalogMkNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.catalogMkNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.catalogMkNative._native.native_decide.ax_1_2, + `Ix.Kernel.AliasFormerRecursorFixture.catalogMkNative._native.native_decide.ax_1_2, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.catalogMkNative._native.native_decide.ax_1_3, + `Ix.Kernel.AliasFormerRecursorFixture.catalogMkNative._native.native_decide.ax_1_3, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_2, + `Ix.Kernel.AliasFormerRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_2, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_3, + `Ix.Kernel.AliasFormerRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_3, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_4, + `Ix.Kernel.AliasFormerRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_4, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.catalogTypeFamilyAliasNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.catalogTypeFamilyAliasNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.constructorCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.constructorCountNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.familyKernelSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.familyKernelSucceededNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.familyTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.familyTypeRawNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.generationCtorPairZero._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.generationCtorPairZero._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.mkRuleBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.mkRuleBinderCoreNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.mkRuleFieldsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.mkRuleFieldsNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.mkRuleRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.mkRuleRawNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.mkRuleScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.mkRuleScopedNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.mkRuleSizeBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.mkRuleSizeBoundNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.mkSourceNameNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.mkSourceNameNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.mkTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.mkTypeRawNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.nameOfFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.nameOfFamilyNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.nameOfMkNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.nameOfMkNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.nameOfRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.nameOfRecursorNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.nameOfTypeFamilyAliasNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.nameOfTypeFamilyAliasNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.recursorEntriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.recursorEntriesUniqueNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.recursorEntryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.recursorEntryIdsNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.recursorEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.recursorEntryNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.recursorEntrySizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.recursorEntrySizeNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.recursorIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.recursorIngressSucceededNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.recursorKernelSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.recursorKernelSucceededNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.recursorMemberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.recursorMemberKidsNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.recursorUniverseCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.recursorUniverseCountNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.recursorShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.recursorShapeNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.recursorTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasFormerRecursorFixture.recursorTypeRawNative._native.native_decide.ax_1_1, aliasFormerRecursorFixtureNativeAxiom - `Ix.Tc.AliasFormerRecursorFixture.typeFamilyAliasTranslationsNative._native.native_decide.ax_1_1 + `Ix.Kernel.AliasFormerRecursorFixture.typeFamilyAliasTranslationsNative._native.native_decide.ax_1_1 ] /- Exact executable footprint of the recursive-field-normalizing `AliasRec` @@ -631,113 +621,113 @@ family/recursor transaction. -/ private def aliasRecAtomicClosureNative : Array Lean.Name := inductiveNative ++ aliasRecCertificateBreadthNative ++ #[ aliasRecAdmissionNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, aliasRecAdmissionNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.recursorOwnerNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.recursorOwnerNative._native.native_decide.ax_1_1, aliasRecFixtureNativeAxiom - `Ix.Tc.AliasRecFixture.entriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecFixture.entriesSizeNative._native.native_decide.ax_1_1, aliasRecFixtureNativeAxiom - `Ix.Tc.AliasRecFixture.entriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecFixture.entriesUniqueNative._native.native_decide.ax_1_1, aliasRecFixtureNativeAxiom - `Ix.Tc.AliasRecFixture.entryAtOneNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecFixture.entryAtOneNative._native.native_decide.ax_1_1, aliasRecFixtureNativeAxiom - `Ix.Tc.AliasRecFixture.entryAtZeroNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecFixture.entryAtZeroNative._native.native_decide.ax_1_1, aliasRecFixtureNativeAxiom - `Ix.Tc.AliasRecFixture.entryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecFixture.entryIdsNative._native.native_decide.ax_1_1, aliasRecFixtureNativeAxiom - `Ix.Tc.AliasRecFixture.familyEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecFixture.familyEntryNative._native.native_decide.ax_1_1, aliasRecFixtureNativeAxiom - `Ix.Tc.AliasRecFixture.familyIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecFixture.familyIngressSucceededNative._native.native_decide.ax_1_1, aliasRecFixtureNativeAxiom - `Ix.Tc.AliasRecFixture.familyShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecFixture.familyShapeNative._native.native_decide.ax_1_1, aliasRecFixtureNativeAxiom - `Ix.Tc.AliasRecFixture.memberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecFixture.memberKidsNative._native.native_decide.ax_1_1, aliasRecFixtureNativeAxiom - `Ix.Tc.AliasRecFixture.mkEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecFixture.mkEntryNative._native.native_decide.ax_1_1, aliasRecFixtureNativeAxiom - `Ix.Tc.AliasRecFixture.mkShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecFixture.mkShapeNative._native.native_decide.ax_1_1, aliasRecFixtureNativeAxiom - `Ix.Tc.AliasRecFixture.recAliasIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecFixture.recAliasIngressSucceededNative._native.native_decide.ax_1_1, aliasRecFixtureNativeAxiom - `Ix.Tc.AliasRecFixture.sourceConstructorZero._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecFixture.sourceConstructorZero._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.catalogFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.catalogFamilyNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.catalogFamilyNative._native.native_decide.ax_1_2, + `Ix.Kernel.AliasRecRecursorFixture.catalogFamilyNative._native.native_decide.ax_1_2, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.catalogMkNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.catalogMkNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.catalogMkNative._native.native_decide.ax_1_2, + `Ix.Kernel.AliasRecRecursorFixture.catalogMkNative._native.native_decide.ax_1_2, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.catalogMkNative._native.native_decide.ax_1_3, + `Ix.Kernel.AliasRecRecursorFixture.catalogMkNative._native.native_decide.ax_1_3, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.catalogRecAliasNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.catalogRecAliasNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_2, + `Ix.Kernel.AliasRecRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_2, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_3, + `Ix.Kernel.AliasRecRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_3, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_4, + `Ix.Kernel.AliasRecRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_4, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.constructorCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.constructorCountNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.familyKernelSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.familyKernelSucceededNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.familyTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.familyTypeRawNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.generationCtorPairZero._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.generationCtorPairZero._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.mkRuleBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.mkRuleBinderCoreNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.mkRuleFieldsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.mkRuleFieldsNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.mkRuleRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.mkRuleRawNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.mkRuleScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.mkRuleScopedNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.mkRuleSizeBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.mkRuleSizeBoundNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.mkSourceNameNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.mkSourceNameNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.mkTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.mkTypeRawNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.nameOfFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.nameOfFamilyNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.nameOfMkNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.nameOfMkNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.nameOfRecAliasNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.nameOfRecAliasNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.nameOfRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.nameOfRecursorNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.recAliasTranslationsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.recAliasTranslationsNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.recursorEntriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.recursorEntriesUniqueNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.recursorEntryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.recursorEntryIdsNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.recursorEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.recursorEntryNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.recursorEntrySizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.recursorEntrySizeNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.recursorIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.recursorIngressSucceededNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.recursorKernelSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.recursorKernelSucceededNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.recursorMemberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.recursorMemberKidsNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.recursorUniverseCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.recursorUniverseCountNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.recursorShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AliasRecRecursorFixture.recursorShapeNative._native.native_decide.ax_1_1, aliasRecRecursorFixtureNativeAxiom - `Ix.Tc.AliasRecRecursorFixture.recursorTypeRawNative._native.native_decide.ax_1_1 + `Ix.Kernel.AliasRecRecursorFixture.recursorTypeRawNative._native.native_decide.ax_1_1 ] /- Exact executable footprint of the annotation-normalizing family/recursor @@ -746,113 +736,113 @@ family, constructor, and recursor entries. -/ private def annotatedPiAtomicClosureNative : Array Lean.Name := inductiveNative ++ annotatedPiCertificateBreadthNative ++ #[ annotatedPiAdmissionNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, annotatedPiAdmissionNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.recursorOwnerNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.recursorOwnerNative._native.native_decide.ax_1_1, annotatedPiFixtureNativeAxiom - `Ix.Tc.AnnotatedPiFixture.entriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiFixture.entriesSizeNative._native.native_decide.ax_1_1, annotatedPiFixtureNativeAxiom - `Ix.Tc.AnnotatedPiFixture.entriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiFixture.entriesUniqueNative._native.native_decide.ax_1_1, annotatedPiFixtureNativeAxiom - `Ix.Tc.AnnotatedPiFixture.entryAtOneNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiFixture.entryAtOneNative._native.native_decide.ax_1_1, annotatedPiFixtureNativeAxiom - `Ix.Tc.AnnotatedPiFixture.entryAtZeroNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiFixture.entryAtZeroNative._native.native_decide.ax_1_1, annotatedPiFixtureNativeAxiom - `Ix.Tc.AnnotatedPiFixture.entryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiFixture.entryIdsNative._native.native_decide.ax_1_1, annotatedPiFixtureNativeAxiom - `Ix.Tc.AnnotatedPiFixture.familyEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiFixture.familyEntryNative._native.native_decide.ax_1_1, annotatedPiFixtureNativeAxiom - `Ix.Tc.AnnotatedPiFixture.familyIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiFixture.familyIngressSucceededNative._native.native_decide.ax_1_1, annotatedPiFixtureNativeAxiom - `Ix.Tc.AnnotatedPiFixture.familyShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiFixture.familyShapeNative._native.native_decide.ax_1_1, annotatedPiFixtureNativeAxiom - `Ix.Tc.AnnotatedPiFixture.memberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiFixture.memberKidsNative._native.native_decide.ax_1_1, annotatedPiFixtureNativeAxiom - `Ix.Tc.AnnotatedPiFixture.mkEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiFixture.mkEntryNative._native.native_decide.ax_1_1, annotatedPiFixtureNativeAxiom - `Ix.Tc.AnnotatedPiFixture.mkShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiFixture.mkShapeNative._native.native_decide.ax_1_1, annotatedPiFixtureNativeAxiom - `Ix.Tc.AnnotatedPiFixture.outParamIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiFixture.outParamIngressSucceededNative._native.native_decide.ax_1_1, annotatedPiFixtureNativeAxiom - `Ix.Tc.AnnotatedPiFixture.sourceConstructorZero._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiFixture.sourceConstructorZero._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.catalogFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.catalogFamilyNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.catalogFamilyNative._native.native_decide.ax_1_2, + `Ix.Kernel.AnnotatedPiRecursorFixture.catalogFamilyNative._native.native_decide.ax_1_2, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.catalogMkNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.catalogMkNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.catalogMkNative._native.native_decide.ax_1_2, + `Ix.Kernel.AnnotatedPiRecursorFixture.catalogMkNative._native.native_decide.ax_1_2, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.catalogMkNative._native.native_decide.ax_1_3, + `Ix.Kernel.AnnotatedPiRecursorFixture.catalogMkNative._native.native_decide.ax_1_3, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.catalogOutParamNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.catalogOutParamNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_2, + `Ix.Kernel.AnnotatedPiRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_2, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_3, + `Ix.Kernel.AnnotatedPiRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_3, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_4, + `Ix.Kernel.AnnotatedPiRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_4, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.constructorCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.constructorCountNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.familyKernelSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.familyKernelSucceededNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.familyTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.familyTypeRawNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.generationCtorPairZero._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.generationCtorPairZero._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.mkRuleBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.mkRuleBinderCoreNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.mkRuleFieldsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.mkRuleFieldsNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.mkRuleRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.mkRuleRawNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.mkRuleScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.mkRuleScopedNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.mkRuleSizeBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.mkRuleSizeBoundNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.mkSourceNameNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.mkSourceNameNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.mkTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.mkTypeRawNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.nameOfFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.nameOfFamilyNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.nameOfMkNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.nameOfMkNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.nameOfOutParamNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.nameOfOutParamNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.nameOfRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.nameOfRecursorNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.outParamTranslationsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.outParamTranslationsNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.recursorEntriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.recursorEntriesUniqueNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.recursorEntryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.recursorEntryIdsNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.recursorEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.recursorEntryNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.recursorEntrySizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.recursorEntrySizeNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.recursorIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.recursorIngressSucceededNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.recursorKernelSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.recursorKernelSucceededNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.recursorMemberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.recursorMemberKidsNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.recursorUniverseCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.recursorUniverseCountNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.recursorShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.AnnotatedPiRecursorFixture.recursorShapeNative._native.native_decide.ax_1_1, annotatedPiRecursorFixtureNativeAxiom - `Ix.Tc.AnnotatedPiRecursorFixture.recursorTypeRawNative._native.native_decide.ax_1_1 + `Ix.Kernel.AnnotatedPiRecursorFixture.recursorTypeRawNative._native.native_decide.ax_1_1 ] /- Exact executable footprint of the recursive-Pi family/recursor transaction. @@ -861,291 +851,291 @@ the `Acc` closure cannot silently acquire unrelated native assumptions. -/ private def recursivePiAtomicClosureNative : Array Lean.Name := inductiveNative ++ #[ recursivePiFixtureNativeAxiom - `Ix.Tc.RecursivePiFixture.entriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiFixture.entriesSizeNative._native.native_decide.ax_1_1, recursivePiFixtureNativeAxiom - `Ix.Tc.RecursivePiFixture.entriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiFixture.entriesUniqueNative._native.native_decide.ax_1_1, recursivePiFixtureNativeAxiom - `Ix.Tc.RecursivePiFixture.entryAtOneNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiFixture.entryAtOneNative._native.native_decide.ax_1_1, recursivePiFixtureNativeAxiom - `Ix.Tc.RecursivePiFixture.entryAtZeroNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiFixture.entryAtZeroNative._native.native_decide.ax_1_1, recursivePiFixtureNativeAxiom - `Ix.Tc.RecursivePiFixture.entryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiFixture.entryIdsNative._native.native_decide.ax_1_1, recursivePiFixtureNativeAxiom - `Ix.Tc.RecursivePiFixture.familyEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiFixture.familyEntryNative._native.native_decide.ax_1_1, recursivePiFixtureNativeAxiom - `Ix.Tc.RecursivePiFixture.familyShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiFixture.familyShapeNative._native.native_decide.ax_1_1, recursivePiFixtureNativeAxiom - `Ix.Tc.RecursivePiFixture.ingressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiFixture.ingressSucceededNative._native.native_decide.ax_1_1, recursivePiFixtureNativeAxiom - `Ix.Tc.RecursivePiFixture.introEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiFixture.introEntryNative._native.native_decide.ax_1_1, recursivePiFixtureNativeAxiom - `Ix.Tc.RecursivePiFixture.introShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiFixture.introShapeNative._native.native_decide.ax_1_1, recursivePiFixtureNativeAxiom - `Ix.Tc.RecursivePiFixture.memberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiFixture.memberKidsNative._native.native_decide.ax_1_1, recursivePiFixtureNativeAxiom - `Ix.Tc.RecursivePiFixture.sourceConstructorZero._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiFixture.sourceConstructorZero._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.catalogFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.catalogFamilyNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.catalogIntroNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.catalogIntroNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.catalogIntroNative._native.native_decide.ax_1_2, + `Ix.Kernel.RecursivePiRecursorFixture.catalogIntroNative._native.native_decide.ax_1_2, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_2, + `Ix.Kernel.RecursivePiRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_2, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_3, + `Ix.Kernel.RecursivePiRecursorFixture.catalogRecursorNative._native.native_decide.ax_1_3, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.constructorCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.constructorCountNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.familyKernelSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.familyKernelSucceededNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.familyTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.familyTypeRawNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.generationCtorPairZero._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.generationCtorPairZero._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.introRuleBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.introRuleBinderCoreNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.introRuleFieldsNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.introRuleFieldsNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.introRuleRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.introRuleRawNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.introRuleScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.introRuleScopedNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.introRuleSizeBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.introRuleSizeBoundNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.introSourceNameNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.introSourceNameNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.introTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.introTypeRawNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.nameOfFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.nameOfFamilyNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.nameOfIntroNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.nameOfIntroNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.nameOfRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.nameOfRecursorNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.recursorEntriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.recursorEntriesUniqueNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.recursorEntryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.recursorEntryIdsNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.recursorEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.recursorEntryNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.recursorEntrySizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.recursorEntrySizeNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.recursorIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.recursorIngressSucceededNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.recursorKernelSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.recursorKernelSucceededNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.recursorMemberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.recursorMemberKidsNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.recursorShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.recursorShapeNative._native.native_decide.ax_1_1, recursivePiRecursorFixtureNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.recursorTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.recursorTypeRawNative._native.native_decide.ax_1_1, recursivePiAdmissionNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, + `Ix.Kernel.RecursivePiRecursorFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, recursivePiAdmissionNativeAxiom - `Ix.Tc.RecursivePiRecursorFixture.recursorOwnerNative._native.native_decide.ax_1_1 + `Ix.Kernel.RecursivePiRecursorFixture.recursorOwnerNative._native.native_decide.ax_1_1 ] private def enumerationFixtureNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.EnumerationFixture name + nativeAxiom `Ix.Kernel.Verify.Inductive.EnumerationFixture name private def enumerationAcceptanceNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.EnumerationAcceptance name + nativeAxiom `Ix.Kernel.Verify.Inductive.EnumerationAcceptance name private def indexedRecursiveFixtureNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedRecursiveFixture name + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedRecursiveFixture name private def indexedRecursiveAcceptanceNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedRecursiveAcceptance name + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedRecursiveAcceptance name private def eliminationBreadthNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.EliminationBreadthFixture name + nativeAxiom `Ix.Kernel.Verify.Inductive.EliminationBreadthFixture name private def smallEliminationAcceptanceNative : Array Lean.Name := inductiveNative.push mutualInternDataValueNative ++ #[ - `Lean4Lean.InductiveReplayFixtures.smallSourceAlignment06._native.native_decide.ax_1, - `Lean4Lean.InductiveReplayFixtures.smallSourceEliminationResult06_isOk._native.native_decide.ax_1_1, - `Ix.Tc.EliminationBreadthFixture.smallCompiledIdentity._native.native_decide.ax_1_1, - `Ix.Tc.EliminationBreadthFixture.smallCompiledIdentity._native.native_decide.ax_1_2, - `Ix.Tc.EliminationBreadthFixture.smallCompiledIdentity._native.native_decide.ax_1_3, - `Ix.Tc.EliminationBreadthFixture.smallCompiledIdentity._native.native_decide.ax_1_4, - `Ix.Tc.EliminationBreadthFixture.smallCompiledIdentity._native.native_decide.ax_1_5, - `Ix.Tc.EliminationBreadthFixture.smallCompiledIdentity._native.native_decide.ax_1_6, - `Ix.Tc.EliminationBreadthFixture.smallCompiledIdentity._native.native_decide.ax_1_7, - `Ix.Tc.EliminationBreadthFixture.smallComputeKMatches_eq._native.native_decide.ax_1_1, - `Ix.Tc.EliminationBreadthFixture.smallPreparationMatches_eq._native.native_decide.ax_1_1, - `Ix.Tc.EliminationBreadthFixture.smallRecursorShape._native.native_decide.ax_1_1, - `Ix.Tc.EliminationBreadthFixture.smallTheoryRecUvars._native.native_decide.ax_1_1, + `Ix.Theory.Named.InductiveReplayFixtures.smallSourceAlignment06._native.native_decide.ax_1, + `Ix.Theory.Named.InductiveReplayFixtures.smallSourceEliminationResult06_isOk._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.smallCompiledIdentity._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.smallCompiledIdentity._native.native_decide.ax_1_2, + `Ix.Kernel.EliminationBreadthFixture.smallCompiledIdentity._native.native_decide.ax_1_3, + `Ix.Kernel.EliminationBreadthFixture.smallCompiledIdentity._native.native_decide.ax_1_4, + `Ix.Kernel.EliminationBreadthFixture.smallCompiledIdentity._native.native_decide.ax_1_5, + `Ix.Kernel.EliminationBreadthFixture.smallCompiledIdentity._native.native_decide.ax_1_6, + `Ix.Kernel.EliminationBreadthFixture.smallCompiledIdentity._native.native_decide.ax_1_7, + `Ix.Kernel.EliminationBreadthFixture.smallComputeKMatches_eq._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.smallPreparationMatches_eq._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.smallRecursorShape._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.smallTheoryRecUvars._native.native_decide.ax_1_1, eliminationBreadthNativeAxiom - `Ix.Tc.EliminationBreadthFixture.smallCompilerSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.smallCompilerSucceededNative._native.native_decide.ax_1_1, eliminationBreadthNativeAxiom - `Ix.Tc.EliminationBreadthFixture.smallExecutionKNative._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.smallExecutionKNative._native.native_decide.ax_1_1, eliminationBreadthNativeAxiom - `Ix.Tc.EliminationBreadthFixture.smallExecutionModeNative._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.smallExecutionModeNative._native.native_decide.ax_1_1, eliminationBreadthNativeAxiom - `Ix.Tc.EliminationBreadthFixture.smallFamilyIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.smallFamilyIngressSucceededNative._native.native_decide.ax_1_1, eliminationBreadthNativeAxiom - `Ix.Tc.EliminationBreadthFixture.smallFamilyKernelSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.smallFamilyKernelSucceededNative._native.native_decide.ax_1_1, eliminationBreadthNativeAxiom - `Ix.Tc.EliminationBreadthFixture.smallRecursorIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.smallRecursorIngressSucceededNative._native.native_decide.ax_1_1, eliminationBreadthNativeAxiom - `Ix.Tc.EliminationBreadthFixture.smallRecursorKernelSucceededNative._native.native_decide.ax_1_1 + `Ix.Kernel.EliminationBreadthFixture.smallRecursorKernelSucceededNative._native.native_decide.ax_1_1 ] private def kTargetAcceptanceNative : Array Lean.Name := inductiveNative.push mutualInternDataValueNative ++ #[ - `Lean4Lean.InductiveReplayFixtures.eqAlignment06._native.native_decide.ax_1, - `Lean4Lean.InductiveReplayFixtures.eqEliminationResult06_isOk._native.native_decide.ax_1_1, - `Ix.Tc.EliminationBreadthFixture.eqCompiledIdentity._native.native_decide.ax_1_1, - `Ix.Tc.EliminationBreadthFixture.eqCompiledIdentity._native.native_decide.ax_1_2, - `Ix.Tc.EliminationBreadthFixture.eqCompiledIdentity._native.native_decide.ax_1_3, - `Ix.Tc.EliminationBreadthFixture.eqCompiledIdentity._native.native_decide.ax_1_4, - `Ix.Tc.EliminationBreadthFixture.eqCompiledIdentity._native.native_decide.ax_1_5, - `Ix.Tc.EliminationBreadthFixture.eqCompiledIdentity._native.native_decide.ax_1_6, - `Ix.Tc.EliminationBreadthFixture.eqComputeKMatches_eq._native.native_decide.ax_1_1, - `Ix.Tc.EliminationBreadthFixture.eqPreparationMatches_eq._native.native_decide.ax_1_1, - `Ix.Tc.EliminationBreadthFixture.eqRecursorShape._native.native_decide.ax_1_1, - `Ix.Tc.EliminationBreadthFixture.eqTheoryRecUvars._native.native_decide.ax_1_1, + `Ix.Theory.Named.InductiveReplayFixtures.eqAlignment06._native.native_decide.ax_1, + `Ix.Theory.Named.InductiveReplayFixtures.eqEliminationResult06_isOk._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.eqCompiledIdentity._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.eqCompiledIdentity._native.native_decide.ax_1_2, + `Ix.Kernel.EliminationBreadthFixture.eqCompiledIdentity._native.native_decide.ax_1_3, + `Ix.Kernel.EliminationBreadthFixture.eqCompiledIdentity._native.native_decide.ax_1_4, + `Ix.Kernel.EliminationBreadthFixture.eqCompiledIdentity._native.native_decide.ax_1_5, + `Ix.Kernel.EliminationBreadthFixture.eqCompiledIdentity._native.native_decide.ax_1_6, + `Ix.Kernel.EliminationBreadthFixture.eqComputeKMatches_eq._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.eqPreparationMatches_eq._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.eqRecursorShape._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.eqTheoryRecUvars._native.native_decide.ax_1_1, eliminationBreadthNativeAxiom - `Ix.Tc.EliminationBreadthFixture.eqCompilerSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.eqCompilerSucceededNative._native.native_decide.ax_1_1, eliminationBreadthNativeAxiom - `Ix.Tc.EliminationBreadthFixture.eqExecutionKNative._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.eqExecutionKNative._native.native_decide.ax_1_1, eliminationBreadthNativeAxiom - `Ix.Tc.EliminationBreadthFixture.eqExecutionModeNative._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.eqExecutionModeNative._native.native_decide.ax_1_1, eliminationBreadthNativeAxiom - `Ix.Tc.EliminationBreadthFixture.eqFamilyIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.eqFamilyIngressSucceededNative._native.native_decide.ax_1_1, eliminationBreadthNativeAxiom - `Ix.Tc.EliminationBreadthFixture.eqFamilyKernelSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.eqFamilyKernelSucceededNative._native.native_decide.ax_1_1, eliminationBreadthNativeAxiom - `Ix.Tc.EliminationBreadthFixture.eqRecursorIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.EliminationBreadthFixture.eqRecursorIngressSucceededNative._native.native_decide.ax_1_1, eliminationBreadthNativeAxiom - `Ix.Tc.EliminationBreadthFixture.eqRecursorKernelSucceededNative._native.native_decide.ax_1_1 + `Ix.Kernel.EliminationBreadthFixture.eqRecursorKernelSucceededNative._native.native_decide.ax_1_1 ] private def indexedRecursiveFixtureNativeNames : Array Lean.Name := #[ - `Ix.Tc.IndexedRecursiveFixture.catalogConsNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.catalogConsNative._native.native_decide.ax_1_2, - `Ix.Tc.IndexedRecursiveFixture.catalogConsNative._native.native_decide.ax_1_3, - `Ix.Tc.IndexedRecursiveFixture.catalogFamilyNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_2, - `Ix.Tc.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_3, - `Ix.Tc.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_4, - `Ix.Tc.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_5, - `Ix.Tc.IndexedRecursiveFixture.catalogNilNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.catalogNilNative._native.native_decide.ax_1_2, - `Ix.Tc.IndexedRecursiveFixture.catalogRecursorNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.catalogRecursorNative._native.native_decide.ax_1_2, - `Ix.Tc.IndexedRecursiveFixture.catalogRecursorNative._native.native_decide.ax_1_3, - `Ix.Tc.IndexedRecursiveFixture.catalogRecursorNative._native.native_decide.ax_1_4, - `Ix.Tc.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_2, - `Ix.Tc.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_3, - `Ix.Tc.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_4, - `Ix.Tc.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_5, - `Ix.Tc.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_6, - `Ix.Tc.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_7, - `Ix.Tc.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_2, - `Ix.Tc.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_3, - `Ix.Tc.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_4, - `Ix.Tc.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_5, - `Ix.Tc.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_6, - `Ix.Tc.IndexedRecursiveFixture.consEntryNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.consSourceNameNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.consRuleBinderCoreNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.consRuleFieldsNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.consRuleRawNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.consRuleScopedNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.consRuleSizeBoundNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.consShapeNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.consTypeRawNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.constructorCountNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.familyEntriesSizeNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.familyEntriesUniqueNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.familyEntryAtOneNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.familyEntryAtTwoNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.familyEntryAtZeroNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.familyEntryIdsNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.familyEntryNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.familyIngressSucceededNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.familyMemberKidsNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.familyShapeNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.familyTypeRawNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.generationCtorPairOne._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.generationCtorPairZero._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.nameOfConsNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.nameOfFamilyNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.nameOfNatNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.nameOfNilNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.nameOfRecursorNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.nameOfSuccNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.nameOfZeroNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.natConstructorCountNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.natEntriesSizeNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.natEntriesUniqueNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.natEntryAtOneNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.natEntryAtTwoNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.natEntryAtZeroNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.natEntryIdsNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.natEntryNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.natFamilyShapeNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.natIngressSucceededNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.natMemberKidsNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.natSourceConstructorOne._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.natSourceConstructorZero._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.natTypeRawNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.nilEntryNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.nilSourceNameNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.nilRuleBinderCoreNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.nilRuleFieldsNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.nilRuleRawNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.nilRuleScopedNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.nilRuleSizeBoundNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.nilShapeNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.nilTypeRawNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.recursorEntriesSizeNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.recursorEntriesUniqueNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.recursorEntryIdsNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.recursorEntryNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.recursorIngressSucceededNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.recursorMemberKidsNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.recursorShapeNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.recursorTypeRawNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.recursorUniverseCountNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.sourceConstructorOne._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.sourceConstructorZero._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.succEntryNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.succSourceNameNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.succShapeNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.succTypeRawNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.zeroEntryNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.zeroSourceNameNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.zeroShapeNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.zeroTypeRawNative._native.native_decide.ax_1_1 + `Ix.Kernel.IndexedRecursiveFixture.catalogConsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.catalogConsNative._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.catalogConsNative._native.native_decide.ax_1_3, + `Ix.Kernel.IndexedRecursiveFixture.catalogFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_3, + `Ix.Kernel.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_4, + `Ix.Kernel.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_5, + `Ix.Kernel.IndexedRecursiveFixture.catalogNilNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.catalogNilNative._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.catalogRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.catalogRecursorNative._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.catalogRecursorNative._native.native_decide.ax_1_3, + `Ix.Kernel.IndexedRecursiveFixture.catalogRecursorNative._native.native_decide.ax_1_4, + `Ix.Kernel.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_3, + `Ix.Kernel.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_4, + `Ix.Kernel.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_5, + `Ix.Kernel.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_6, + `Ix.Kernel.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_7, + `Ix.Kernel.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_3, + `Ix.Kernel.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_4, + `Ix.Kernel.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_5, + `Ix.Kernel.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_6, + `Ix.Kernel.IndexedRecursiveFixture.consEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consSourceNameNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consRuleBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consRuleFieldsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consRuleRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consRuleScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consRuleSizeBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.constructorCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyEntriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyEntriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyEntryAtOneNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyEntryAtTwoNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyEntryAtZeroNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyEntryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.generationCtorPairOne._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.generationCtorPairZero._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nameOfConsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nameOfFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nameOfNatNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nameOfNilNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nameOfRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nameOfSuccNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nameOfZeroNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natConstructorCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natEntriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natEntriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natEntryAtOneNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natEntryAtTwoNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natEntryAtZeroNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natEntryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natFamilyShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natMemberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natSourceConstructorOne._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natSourceConstructorZero._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilSourceNameNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilRuleBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilRuleFieldsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilRuleRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilRuleScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilRuleSizeBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorEntriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorEntriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorEntryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorMemberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorUniverseCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.sourceConstructorOne._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.sourceConstructorZero._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.succEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.succSourceNameNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.succShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.succTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.zeroEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.zeroSourceNameNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.zeroShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.zeroTypeRawNative._native.native_decide.ax_1_1 ] private def indexedRecursiveAcceptanceNativeNames : Array Lean.Name := #[ - `Ix.Tc.IndexedRecursiveFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.familyKernelSucceededNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.malformedRecursorRejectedNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.natKernelSucceededNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.natNotFamilyDirectOwnerNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.recursorKernelSucceededNative._native.native_decide.ax_1_1, - `Ix.Tc.IndexedRecursiveFixture.recursorOwnerNative._native.native_decide.ax_1_1 + `Ix.Kernel.IndexedRecursiveFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyKernelSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.malformedRecursorRejectedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natKernelSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natNotFamilyDirectOwnerNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorKernelSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorOwnerNative._native.native_decide.ax_1_1 ] private def indexedRecursiveNative : Array Lean.Name := @@ -1161,75 +1151,75 @@ this builder root. -/ private def generatedRecursorTypeFixtureNative : Array Lean.Name := inductiveNative ++ #[ indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorEntriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorEntriesSizeNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorTypeBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorTypeBinderCoreNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorTypeRawNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorTypeScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorTypeScopedNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorTypeSizeBoundNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.GeneratedRecursorTypeFixture - `Ix.Tc.IndexedRecursiveFixture.familyBuildTypeResultNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.GeneratedRecursorTypeFixture - `Ix.Tc.IndexedRecursiveFixture.familyBuildTypeSucceededNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.GeneratedRecursorTypeFixture - `Ix.Tc.IndexedRecursiveFixture.familyPreparationSucceededNative._native.native_decide.ax_1_1 + `Ix.Kernel.IndexedRecursiveFixture.recursorTypeSizeBoundNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.GeneratedRecursorTypeFixture + `Ix.Kernel.IndexedRecursiveFixture.familyBuildTypeResultNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.GeneratedRecursorTypeFixture + `Ix.Kernel.IndexedRecursiveFixture.familyBuildTypeSucceededNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.GeneratedRecursorTypeFixture + `Ix.Kernel.IndexedRecursiveFixture.familyPreparationSucceededNative._native.native_decide.ax_1_1 ] private def generatedRecursorRuleFixtureNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.GeneratedRecursorRuleFixture name + nativeAxiom `Ix.Kernel.Verify.Inductive.GeneratedRecursorRuleFixture name /-- Exact native footprint of the complete IndexedVec peer-alignment and `buildRuleRhs` run. -/ private def generatedRecursorRuleFixtureNative : Array Lean.Name := inductiveNative ++ #[ indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorEntriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorEntriesSizeNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorTypeBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorTypeBinderCoreNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorTypeRawNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorTypeScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorTypeScopedNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorTypeSizeBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorTypeSizeBoundNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.generationCtorPairZero._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.generationCtorPairZero._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.generationCtorPairOne._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.generationCtorPairOne._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nilRuleBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilRuleBinderCoreNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nilRuleFieldsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilRuleFieldsNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nilRuleRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilRuleRawNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nilRuleScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilRuleScopedNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nilRuleSizeBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilRuleSizeBoundNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.consRuleBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consRuleBinderCoreNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.consRuleFieldsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consRuleFieldsNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.consRuleRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consRuleRawNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.consRuleScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consRuleScopedNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.consRuleSizeBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consRuleSizeBoundNative._native.native_decide.ax_1_1, generatedRecursorRuleFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyBuiltRulesNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyBuiltRulesNative._native.native_decide.ax_1_1, generatedRecursorRuleFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyCompletedRecursorTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyCompletedRecursorTypeNative._native.native_decide.ax_1_1, generatedRecursorRuleFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyRulePopulationSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyRulePopulationSucceededNative._native.native_decide.ax_1_1, generatedRecursorRuleFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.generationRuleCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.generationRuleCountNative._native.native_decide.ax_1_1, generatedRecursorRuleFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorRulesLiteralNative._native.native_decide.ax_1_1 + `Ix.Kernel.IndexedRecursiveFixture.recursorRulesLiteralNative._native.native_decide.ax_1_1 ] /- Exact native footprint of the transactional rule commit. This is kept @@ -1239,63 +1229,63 @@ completed-type observation used by the builder fixture. -/ private def generatedRecursorCommitFixtureNative : Array Lean.Name := inductiveNative ++ #[ indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorEntriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorEntriesSizeNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorTypeBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorTypeBinderCoreNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorTypeRawNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorTypeScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorTypeScopedNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorTypeSizeBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorTypeSizeBoundNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.generationCtorPairZero._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.generationCtorPairZero._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.generationCtorPairOne._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.generationCtorPairOne._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nilRuleBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilRuleBinderCoreNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nilRuleFieldsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilRuleFieldsNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nilRuleRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilRuleRawNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nilRuleScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilRuleScopedNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nilRuleSizeBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilRuleSizeBoundNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.consRuleBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consRuleBinderCoreNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.consRuleFieldsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consRuleFieldsNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.consRuleRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consRuleRawNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.consRuleScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consRuleScopedNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.consRuleSizeBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consRuleSizeBoundNative._native.native_decide.ax_1_1, generatedRecursorRuleFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyBuiltRulesNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyBuiltRulesNative._native.native_decide.ax_1_1, generatedRecursorRuleFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyGeneratedSnapshotSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyGeneratedSnapshotSizeNative._native.native_decide.ax_1_1, generatedRecursorRuleFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyGeneratedWithRulesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyGeneratedWithRulesSizeNative._native.native_decide.ax_1_1, generatedRecursorRuleFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyRulePopulationSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyRulePopulationSucceededNative._native.native_decide.ax_1_1, generatedRecursorRuleFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.generationRuleCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.generationRuleCountNative._native.native_decide.ax_1_1, generatedRecursorRuleFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorRulesLiteralNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.GeneratedRecursorCommitFixture - `Ix.Tc.IndexedRecursiveFixture.familyRuleCommitSucceededNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.GeneratedRecursorCommitFixture - `Ix.Tc.IndexedRecursiveFixture.familyGeneratedSnapshotTypeNative._native.native_decide.ax_1_1 + `Ix.Kernel.IndexedRecursiveFixture.recursorRulesLiteralNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.GeneratedRecursorCommitFixture + `Ix.Kernel.IndexedRecursiveFixture.familyRuleCommitSucceededNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.GeneratedRecursorCommitFixture + `Ix.Kernel.IndexedRecursiveFixture.familyGeneratedSnapshotTypeNative._native.native_decide.ax_1_1 ] private def generatedRecursorCheckerFixtureNative : Array Lean.Name := generatedRecursorCommitFixtureNative ++ #[ indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.GeneratedRecursorCheckerFixture - `Ix.Tc.IndexedRecursiveFixture.familyCacheCheckSucceededNative._native.native_decide.ax_1_1 + `Ix.Kernel.IndexedRecursiveFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.GeneratedRecursorCheckerFixture + `Ix.Kernel.IndexedRecursiveFixture.familyCacheCheckSucceededNative._native.native_decide.ax_1_1 ] /- The additional native facts used to construct the concrete IndexedVec @@ -1303,161 +1293,161 @@ semantic world. They are disjoint from the checker execution footprint above, so the concatenated canonical fixture manifest remains exact. -/ private def generatedRecursorCanonicalWorldNative : Array Lean.Name := #[ indexedRecursiveAcceptanceNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, indexedRecursiveAcceptanceNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, indexedRecursiveAcceptanceNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.natNotFamilyDirectOwnerNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natNotFamilyDirectOwnerNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogConsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.catalogConsNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogConsNative._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.catalogConsNative._native.native_decide.ax_1_2, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogConsNative._native.native_decide.ax_1_3, + `Ix.Kernel.IndexedRecursiveFixture.catalogConsNative._native.native_decide.ax_1_3, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.catalogFamilyNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_2, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_3, + `Ix.Kernel.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_3, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_4, + `Ix.Kernel.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_4, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_5, + `Ix.Kernel.IndexedRecursiveFixture.catalogNatNative._native.native_decide.ax_1_5, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogNilNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.catalogNilNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogNilNative._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.catalogNilNative._native.native_decide.ax_1_2, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_2, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_3, + `Ix.Kernel.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_3, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_4, + `Ix.Kernel.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_4, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_5, + `Ix.Kernel.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_5, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_6, + `Ix.Kernel.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_6, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_7, + `Ix.Kernel.IndexedRecursiveFixture.catalogSuccNative._native.native_decide.ax_1_7, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_2, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_3, + `Ix.Kernel.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_3, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_4, + `Ix.Kernel.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_4, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_5, + `Ix.Kernel.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_5, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_6, + `Ix.Kernel.IndexedRecursiveFixture.catalogZeroNative._native.native_decide.ax_1_6, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.consEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consEntryNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.consShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consShapeNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.consSourceNameNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consSourceNameNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.consTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.consTypeRawNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.constructorCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.constructorCountNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyEntriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyEntriesSizeNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyEntriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyEntriesUniqueNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyEntryAtOneNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyEntryAtOneNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyEntryAtTwoNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyEntryAtTwoNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyEntryAtZeroNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyEntryAtZeroNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyEntryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyEntryIdsNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyEntryNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyIngressSucceededNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberKidsNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyShapeNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyTypeRawNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nameOfConsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nameOfConsNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nameOfFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nameOfFamilyNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nameOfNatNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nameOfNatNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nameOfNilNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nameOfNilNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nameOfSuccNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nameOfSuccNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nameOfZeroNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nameOfZeroNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.natConstructorCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natConstructorCountNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.natEntriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natEntriesSizeNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.natEntriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natEntriesUniqueNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.natEntryAtOneNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natEntryAtOneNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.natEntryAtTwoNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natEntryAtTwoNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.natEntryAtZeroNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natEntryAtZeroNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.natEntryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natEntryIdsNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.natEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natEntryNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.natFamilyShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natFamilyShapeNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.natIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natIngressSucceededNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.natMemberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natMemberKidsNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.natSourceConstructorOne._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natSourceConstructorOne._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.natSourceConstructorZero._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natSourceConstructorZero._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.natTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.natTypeRawNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nilEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilEntryNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nilShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilShapeNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nilSourceNameNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilSourceNameNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nilTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nilTypeRawNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.sourceConstructorOne._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.sourceConstructorOne._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.sourceConstructorZero._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.sourceConstructorZero._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.succEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.succEntryNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.succShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.succShapeNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.succSourceNameNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.succSourceNameNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.succTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.succTypeRawNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.zeroEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.zeroEntryNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.zeroShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.zeroShapeNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.zeroSourceNameNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.zeroSourceNameNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.zeroTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.zeroTypeRawNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorShapeNative._native.native_decide.ax_1_1 + `Ix.Kernel.IndexedRecursiveFixture.recursorShapeNative._native.native_decide.ax_1_1 ] private def generatedRecursorCanonicalFixtureNative : Array Lean.Name := @@ -1466,16 +1456,16 @@ private def generatedRecursorCanonicalFixtureNative : Array Lean.Name := private def generatedRecursorCommitFixtureNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.GeneratedRecursorCommitFixture name + nativeAxiom `Ix.Kernel.Verify.Inductive.GeneratedRecursorCommitFixture name private def generatedRecursorMemberFixtureNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.GeneratedRecursorMemberFixture name + nativeAxiom `Ix.Kernel.Verify.Inductive.GeneratedRecursorMemberFixture name private def generatedRecursorInitialInvariantNativeAxiom (name : Lean.Name) : Lean.Name := nativeAxiom - `Ix.Tc.Verify.Inductive.GeneratedRecursorInitialInvariant name + `Ix.Kernel.Verify.Inductive.GeneratedRecursorInitialInvariant name /- Exact executable footprint of the complete concrete recursor-member transaction. The earlier commit and semantic-world manifests are reused @@ -1486,221 +1476,221 @@ private def generatedRecursorAtomicClosureNative : Array Lean.Name := generatedRecursorCommitFixtureNative ++ generatedRecursorCanonicalWorldNative ++ #[ indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorUniverseCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorUniverseCountNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.catalogRecursorNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogRecursorNative._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.catalogRecursorNative._native.native_decide.ax_1_2, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogRecursorNative._native.native_decide.ax_1_3, + `Ix.Kernel.IndexedRecursiveFixture.catalogRecursorNative._native.native_decide.ax_1_3, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.catalogRecursorNative._native.native_decide.ax_1_4, + `Ix.Kernel.IndexedRecursiveFixture.catalogRecursorNative._native.native_decide.ax_1_4, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.nameOfRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.nameOfRecursorNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorEntriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorEntriesUniqueNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorEntryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorEntryIdsNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorEntryNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorIngressSucceededNative._native.native_decide.ax_1_1, indexedRecursiveFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorMemberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorMemberKidsNative._native.native_decide.ax_1_1, indexedRecursiveAcceptanceNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, indexedRecursiveAcceptanceNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorOwnerNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedBlockValidation - `Ix.Tc.IndexedRecursiveFixture.consConcreteHeaderNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedBlockValidation - `Ix.Tc.IndexedRecursiveFixture.familyConcreteHeaderNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.recursorOwnerNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedBlockValidation + `Ix.Kernel.IndexedRecursiveFixture.consConcreteHeaderNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedBlockValidation + `Ix.Kernel.IndexedRecursiveFixture.familyConcreteHeaderNative._native.native_decide.ax_1_1, generatedRecursorCommitFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyInstalledRecursorAtZeroMemberNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyInstalledRecursorAtZeroMemberNative._native.native_decide.ax_1_1, generatedRecursorCommitFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyInstalledRecursorTypeEqNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyInstalledRecursorTypeEqNative._native.native_decide.ax_1_1, generatedRecursorCommitFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyInstalledConsRuleInternSupported._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyInstalledConsRuleInternSupported._native.native_decide.ax_1_1, generatedRecursorCommitFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyInstalledNilRuleInternSupported._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyInstalledNilRuleInternSupported._native.native_decide.ax_1_1, generatedRecursorCommitFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyInstalledRecursorInductiveAddress._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyInstalledRecursorInductiveAddress._native.native_decide.ax_1_1, generatedRecursorCommitFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyInstalledRecursorRules._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyInstalledRecursorRules._native.native_decide.ax_1_1, generatedRecursorCommitFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyInstalledRecursorTypeInternSupported._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyInstalledRecursorTypeInternSupported._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberArityBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberArityBoundNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberSingletonSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberSingletonSizeNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberInitialPrimitivesNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberInitialPrimitivesNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyCharOfNatAbsent._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyCharOfNatAbsent._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberCheckSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberCheckSucceededNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberDirectMajorShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberDirectMajorShapeNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberInitialBlocksCoveredNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberInitialBlocksCoveredNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberInitialClosedFieldsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberInitialClosedFieldsNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberInitialConstsCoveredNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberInitialConstsCoveredNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberInitialEquivEntriesEmpty._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberInitialEquivEntriesEmpty._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberInitialEquivLabelsEmpty._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberInitialEquivLabelsEmpty._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberInitialEquivParentEmpty._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberInitialEquivParentEmpty._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberInitialExprKeysNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberInitialExprKeysNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberInitialRecursorLoaded._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberInitialRecursorLoaded._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberInitialReferencesCovered._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberInitialReferencesCovered._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberInitialUnivKeysNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberInitialUnivKeysNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberMajorSkipRunNeutral._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberMajorSkipRunNeutral._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberOwnerCacheMatchesNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberOwnerCacheMatchesNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberPopulationReferencesCovered._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberPopulationReferencesCovered._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberPreparationMatchesNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberPreparationMatchesNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberRecursorConcreteHeader._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberRecursorConcreteHeader._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberReferenceId_authorized._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberReferenceId_authorized._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberReferenceId_authorized._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberReferenceId_authorized._native.native_decide.ax_1_2, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberReferenceId_authorized._native.native_decide.ax_1_3, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberReferenceId_authorized._native.native_decide.ax_1_3, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberResolutionPrefixMatchesNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberResolutionPrefixMatchesNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberResultLevelNonzero._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberResultLevelNonzero._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberResultSortShape._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberResultSortShape._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberRulePopulationCacheChecksNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberRulePopulationCacheChecksNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberRulePopulationExprKeysNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberRulePopulationExprKeysNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberRulePopulationExtendsNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberRulePopulationExtendsNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberRulePopulationMatchesNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberRulePopulationMatchesNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberRulePopulationNoLazyNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberRulePopulationNoLazyNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberRulePopulationSemanticChecksNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberRulePopulationSemanticChecksNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberRulePopulationUnivKeysNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberRulePopulationUnivKeysNative._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberSnapshotFamilyLoaded._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberSnapshotFamilyLoaded._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberSnapshotGeneratedCache._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberSnapshotGeneratedCache._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyNatSuccLookup._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyNatSuccLookup._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyNatZeroLookup._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyNatZeroLookup._native.native_decide.ax_1_1, generatedRecursorMemberFixtureNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyNilConcreteHeader._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyNilConcreteHeader._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberBlockPeersClassifiedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberBlockPeersClassifiedNative._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberBlockResultKeysClassifiedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberBlockResultKeysClassifiedNative._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberConsInfoLookup._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberConsInfoLookup._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberConsInnerResultLevel_raw._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberConsInnerResultLevel_raw._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberConsResultLevel_raw._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberConsResultLevel_raw._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberConsTypeTranslation._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberConsTypeTranslation._native.native_decide.ax_1_2, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberDefEqCacheEmpty._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberDefEqCacheEmpty._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberDefEqCheapCacheEmpty._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberDefEqCheapCacheEmpty._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberDefEqFailureCacheEmpty._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberDefEqFailureCacheEmpty._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberFamilyInfoLookup._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberFamilyInfoLookup._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberFamilyReferenceTranslation._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberFamilyReferenceTranslation._native.native_decide.ax_1_2, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberFamilyResultLevel_raw._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberFamilyResultLevel_raw._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberFamilyTypeTranslation._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberFamilyTypeTranslation._native.native_decide.ax_1_2, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberInferCensusNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberInferCensusNative._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberInferOnlyCacheEmpty._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberInferOnlyCacheEmpty._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberIsPropCacheEmpty._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberIsPropCacheEmpty._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberIsRecCacheEmpty._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberIsRecCacheEmpty._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberNatBlockAccepted._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberNatBlockAccepted._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberNatBlockAccepted._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberNatBlockAccepted._native.native_decide.ax_1_2, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberNatInfoLookup._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberNatInfoLookup._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberNatReferenceTranslation._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberNatReferenceTranslation._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberNatReferenceWhnf._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberNatReferenceWhnf._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberNatSuccStuckCacheEmpty._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberNatSuccStuckCacheEmpty._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberNatTrusted._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberNatTrusted._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberNatTypeTranslation._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberNatTypeTranslation._native.native_decide.ax_1_2, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberNilInfoLookup._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberNilInfoLookup._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberNilTypeTranslation._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberNilTypeTranslation._native.native_decide.ax_1_2, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberRecMajorsClassifiedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberRecMajorsClassifiedNative._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberRecursorBlocksClassifiedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberRecursorBlocksClassifiedNative._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberRecursorOwnersClassifiedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberRecursorOwnersClassifiedNative._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberRecursorPayloadsInternedNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberRecursorPayloadsInternedNative._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberSuccInfoLookup._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberSuccInfoLookup._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberSuccReferenceTranslation._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberSuccReferenceTranslation._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberSuccTrusted._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberSuccTrusted._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberSuccTypeTranslation._native.native_decide.ax_1_2, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberSuccTypeTranslation._native.native_decide.ax_1_2, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberTypedConstantSyntaxNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberTypedConstantSyntaxNative._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberUnfoldCacheEmpty._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberUnfoldCacheEmpty._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberWhnfCensusNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberWhnfCensusNative._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberWhnfCoreCensusNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberWhnfCoreCensusNative._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberWhnfCoreCheapCacheEmpty._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberWhnfCoreCheapCacheEmpty._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberWhnfNoDeltaCensusNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberWhnfNoDeltaCensusNative._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberWhnfNoDeltaCheapCacheEmpty._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberWhnfNoDeltaCheapCacheEmpty._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberZeroInfoLookup._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberZeroInfoLookup._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberZeroReferenceTranslation._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberZeroReferenceTranslation._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberZeroTrusted._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyMemberZeroTrusted._native.native_decide.ax_1_1, generatedRecursorInitialInvariantNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyMemberZeroTypeTranslation._native.native_decide.ax_1_2 + `Ix.Kernel.IndexedRecursiveFixture.familyMemberZeroTypeTranslation._native.native_decide.ax_1_2 ] /-- Exact executable delta between the existing semantic recursor closure @@ -1709,236 +1699,232 @@ equation. The latter adds only the two outer production block checks. -/ private def indexedProducerClosureNative : Array Lean.Name := generatedRecursorAtomicClosureNative ++ #[ indexedRecursiveAcceptanceNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.familyKernelSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.IndexedRecursiveFixture.familyKernelSucceededNative._native.native_decide.ax_1_1, indexedRecursiveAcceptanceNativeAxiom - `Ix.Tc.IndexedRecursiveFixture.recursorKernelSucceededNative._native.native_decide.ax_1_1 + `Ix.Kernel.IndexedRecursiveFixture.recursorKernelSucceededNative._native.native_decide.ax_1_1 ] /-- Exact executable footprint of the production-linked IndexedVec constructor-validation replay. Keep this separate from the broader end-to-end acceptance fixture so a new observation changes this root's audit. -/ private def indexedConstructorValidationNative : Array Lean.Name := #[ - nativeAxiom `Blake3 - `Blake3.HasherOps.hash._native.native_decide.ax_1, nativeAxiom `Ix.Environment `Ix.Name.mkStr._native.native_decide.ax_1, - nativeAxiom `Ix.Tc.Expr - `Ix.Tc.KExpr.mkVar._native.native_decide.ax_1, - nativeAxiom `Ix.Tc.Inductive - `Ix.Tc.RecM.canonicalAuxOrder._native.native_decide.ax_9, - nativeAxiom `Ix.Tc.Level - `Ix.Tc.KUniv.mkSucc._native.native_decide.ax_1, - nativeAxiom `Ix.Tc.Monad - `Ix.Tc.TcM.ctxAddrForLbrUncached._native.native_decide.ax_3, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedBlockValidation - `Ix.Tc.IndexedRecursiveFixture.consConcreteHeaderNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedBlockValidation - `Ix.Tc.IndexedRecursiveFixture.familyAritySucceededNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedBlockValidation - `Ix.Tc.IndexedRecursiveFixture.familyClassificationMatchesNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedBlockValidation - `Ix.Tc.IndexedRecursiveFixture.familyConcreteHeaderNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedBlockValidation - `Ix.Tc.IndexedRecursiveFixture.familyDiscoveryMatchesNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedBlockValidation - `Ix.Tc.IndexedRecursiveFixture.familyMemberLoadedNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedBlockValidation - `Ix.Tc.IndexedRecursiveFixture.familyNilAfterConsLoadedNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedBlockValidation - `Ix.Tc.IndexedRecursiveFixture.familyNilValidationSucceededNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedBlockValidation - `Ix.Tc.IndexedRecursiveFixture.familyPeerAgreementSucceededNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedBlockValidation - `Ix.Tc.IndexedRecursiveFixture.familyResultLevelSucceededNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedCandidateSyntax - `Ix.Tc.IndexedRecursiveFixture.candidateBlockSyntaxNative._native.native_decide.ax_1_2, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedCandidateSyntax - `Ix.Tc.IndexedRecursiveFixture.candidateBlockSyntaxNative._native.native_decide.ax_1_3, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedCandidateSyntax - `Ix.Tc.IndexedRecursiveFixture.candidateBlockSyntaxNative._native.native_decide.ax_1_4, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedCandidateSyntax - `Ix.Tc.IndexedRecursiveFixture.candidateBlockSyntaxNative._native.native_decide.ax_1_5, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedCandidateSyntax - `Ix.Tc.IndexedRecursiveFixture.candidateBlockSyntaxNative._native.native_decide.ax_1_6, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedCandidateSyntax - `Ix.Tc.IndexedRecursiveFixture.candidateBlockSyntaxNative._native.native_decide.ax_1_7, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedConstructorValidation - `Ix.Tc.IndexedRecursiveFixture.indexedVecConstructorAfterParamShapeNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedConstructorValidation - `Ix.Tc.IndexedRecursiveFixture.indexedVecConstructorAlphaEnsureTypeNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedConstructorValidation - `Ix.Tc.IndexedRecursiveFixture.indexedVecConstructorConsumeAlphaNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedConstructorValidation - `Ix.Tc.IndexedRecursiveFixture.indexedVecConstructorConsumeNatNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedConstructorValidation - `Ix.Tc.IndexedRecursiveFixture.indexedVecConstructorConsumeTailNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedConstructorValidation - `Ix.Tc.IndexedRecursiveFixture.indexedVecConstructorGetTypeAlphaNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedConstructorValidation - `Ix.Tc.IndexedRecursiveFixture.indexedVecConstructorInstantiateHeadNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedConstructorValidation - `Ix.Tc.IndexedRecursiveFixture.indexedVecConstructorInstantiateNNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedConstructorValidation - `Ix.Tc.IndexedRecursiveFixture.indexedVecConstructorInstantiateTailNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedConstructorValidation - `Ix.Tc.IndexedRecursiveFixture.indexedVecConstructorNatEnsureTypeNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedConstructorValidation - `Ix.Tc.IndexedRecursiveFixture.indexedVecConstructorNatUniverse._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedConstructorValidation - `Ix.Tc.IndexedRecursiveFixture.indexedVecConstructorParamIsDefEqNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedConstructorValidation - `Ix.Tc.IndexedRecursiveFixture.indexedVecConstructorParamUniverse._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedConstructorValidation - `Ix.Tc.IndexedRecursiveFixture.indexedVecConstructorResultIsValidNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedConstructorValidation - `Ix.Tc.IndexedRecursiveFixture.indexedVecConstructorTailEnsureTypeNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedConstructorValidation - `Ix.Tc.IndexedRecursiveFixture.indexedVecConstructorTypeShapeNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedPositivityTransport - `Ix.Tc.IndexedRecursiveFixture.familyConsHeadDomainCandidateCheckNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedPositivityTransport - `Ix.Tc.IndexedRecursiveFixture.familyConsNatDomainCandidateCheckNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedPositivityTransport - `Ix.Tc.IndexedRecursiveFixture.familyConsTailDomainCandidateCheckNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedPositivityTransport - `Ix.Tc.IndexedRecursiveFixture.familyConsTailWhnfCandidateCheckNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedPositivityTransport - `Ix.Tc.IndexedRecursiveFixture.indexedVecAlphaCandidateWhnfNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedPositivityTransport - `Ix.Tc.IndexedRecursiveFixture.indexedVecAlphaHasNoIndOccTrusted._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedPositivityTransport - `Ix.Tc.IndexedRecursiveFixture.indexedVecNatCandidateWhnfNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedPositivityTransport - `Ix.Tc.IndexedRecursiveFixture.indexedVecNatHasNoIndOccTrusted._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedPositivityTransport - `Ix.Tc.IndexedRecursiveFixture.indexedVecTailAppIsValidTrusted._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedPositivityTransport - `Ix.Tc.IndexedRecursiveFixture.indexedVecTailCandidateWhnfNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedPositivityTransport - `Ix.Tc.IndexedRecursiveFixture.indexedVecTailHasIndOccTrusted._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsHeadDomainRootFreeNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsHeadOpenSucceededNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsHeadTelescopeWhnfIsForallNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsNatDomainRootFreeNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsNatOpenSucceededNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsNatTelescopeWhnfIsForallNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsPositivityParametersSucceededNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsResultWhnfSucceededNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsResultWhnfTerminalNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsTailDomainMentionsRootNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsTailDomainSucceededNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsTailDomainWhnfNotForallNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsTailDomainWhnfSucceededNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsTailOpenSucceededNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsTailTelescopeWhnfIsForallNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsTailWhnfSpineActiveNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedProductionPositivity - `Ix.Tc.IndexedRecursiveFixture.familyConsTailWhnfSpineIsConstNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedRecursiveAcceptance - `Ix.Tc.IndexedRecursiveFixture.familyKernelSucceededNative._native.native_decide.ax_1_1, - nativeAxiom `Ix.Tc.Verify.Inductive.IndexedRecursiveFixture - `Ix.Tc.IndexedRecursiveFixture.familyEntriesSizeNative._native.native_decide.ax_1_1 + nativeAxiom `Ix.Kernel.Expr + `Ix.Kernel.KExpr.mkVar._native.native_decide.ax_1, + nativeAxiom `Ix.Kernel.Inductive + `Ix.Kernel.RecM.canonicalAuxOrder._native.native_decide.ax_9, + nativeAxiom `Ix.Kernel.Level + `Ix.Kernel.KUniv.mkSucc._native.native_decide.ax_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedBlockValidation + `Ix.Kernel.IndexedRecursiveFixture.consConcreteHeaderNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedBlockValidation + `Ix.Kernel.IndexedRecursiveFixture.familyAritySucceededNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedBlockValidation + `Ix.Kernel.IndexedRecursiveFixture.familyClassificationMatchesNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedBlockValidation + `Ix.Kernel.IndexedRecursiveFixture.familyConcreteHeaderNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedBlockValidation + `Ix.Kernel.IndexedRecursiveFixture.familyDiscoveryMatchesNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedBlockValidation + `Ix.Kernel.IndexedRecursiveFixture.familyMemberLoadedNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedBlockValidation + `Ix.Kernel.IndexedRecursiveFixture.familyNilAfterConsLoadedNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedBlockValidation + `Ix.Kernel.IndexedRecursiveFixture.familyNilValidationSucceededNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedBlockValidation + `Ix.Kernel.IndexedRecursiveFixture.familyPeerAgreementSucceededNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedBlockValidation + `Ix.Kernel.IndexedRecursiveFixture.familyResultLevelSucceededNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedCandidateSyntax + `Ix.Kernel.IndexedRecursiveFixture.candidateBlockSyntaxNative._native.native_decide.ax_1_2, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedCandidateSyntax + `Ix.Kernel.IndexedRecursiveFixture.candidateBlockSyntaxNative._native.native_decide.ax_1_3, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedCandidateSyntax + `Ix.Kernel.IndexedRecursiveFixture.candidateBlockSyntaxNative._native.native_decide.ax_1_4, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedCandidateSyntax + `Ix.Kernel.IndexedRecursiveFixture.candidateBlockSyntaxNative._native.native_decide.ax_1_5, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedCandidateSyntax + `Ix.Kernel.IndexedRecursiveFixture.candidateBlockSyntaxNative._native.native_decide.ax_1_6, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedCandidateSyntax + `Ix.Kernel.IndexedRecursiveFixture.candidateBlockSyntaxNative._native.native_decide.ax_1_7, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedConstructorValidation + `Ix.Kernel.IndexedRecursiveFixture.indexedVecConstructorAfterParamShapeNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedConstructorValidation + `Ix.Kernel.IndexedRecursiveFixture.indexedVecConstructorAlphaEnsureTypeNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedConstructorValidation + `Ix.Kernel.IndexedRecursiveFixture.indexedVecConstructorConsumeAlphaNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedConstructorValidation + `Ix.Kernel.IndexedRecursiveFixture.indexedVecConstructorConsumeNatNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedConstructorValidation + `Ix.Kernel.IndexedRecursiveFixture.indexedVecConstructorConsumeTailNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedConstructorValidation + `Ix.Kernel.IndexedRecursiveFixture.indexedVecConstructorGetTypeAlphaNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedConstructorValidation + `Ix.Kernel.IndexedRecursiveFixture.indexedVecConstructorInstantiateHeadNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedConstructorValidation + `Ix.Kernel.IndexedRecursiveFixture.indexedVecConstructorInstantiateNNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedConstructorValidation + `Ix.Kernel.IndexedRecursiveFixture.indexedVecConstructorInstantiateTailNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedConstructorValidation + `Ix.Kernel.IndexedRecursiveFixture.indexedVecConstructorNatEnsureTypeNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedConstructorValidation + `Ix.Kernel.IndexedRecursiveFixture.indexedVecConstructorNatUniverse._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedConstructorValidation + `Ix.Kernel.IndexedRecursiveFixture.indexedVecConstructorParamIsDefEqNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedConstructorValidation + `Ix.Kernel.IndexedRecursiveFixture.indexedVecConstructorParamUniverse._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedConstructorValidation + `Ix.Kernel.IndexedRecursiveFixture.indexedVecConstructorResultIsValidNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedConstructorValidation + `Ix.Kernel.IndexedRecursiveFixture.indexedVecConstructorTailEnsureTypeNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedConstructorValidation + `Ix.Kernel.IndexedRecursiveFixture.indexedVecConstructorTypeShapeNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedPositivityTransport + `Ix.Kernel.IndexedRecursiveFixture.familyConsHeadDomainCandidateCheckNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedPositivityTransport + `Ix.Kernel.IndexedRecursiveFixture.familyConsNatDomainCandidateCheckNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedPositivityTransport + `Ix.Kernel.IndexedRecursiveFixture.familyConsTailDomainCandidateCheckNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedPositivityTransport + `Ix.Kernel.IndexedRecursiveFixture.familyConsTailWhnfCandidateCheckNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedPositivityTransport + `Ix.Kernel.IndexedRecursiveFixture.indexedVecAlphaCandidateWhnfNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedPositivityTransport + `Ix.Kernel.IndexedRecursiveFixture.indexedVecAlphaHasNoIndOccTrusted._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedPositivityTransport + `Ix.Kernel.IndexedRecursiveFixture.indexedVecNatCandidateWhnfNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedPositivityTransport + `Ix.Kernel.IndexedRecursiveFixture.indexedVecNatHasNoIndOccTrusted._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedPositivityTransport + `Ix.Kernel.IndexedRecursiveFixture.indexedVecTailAppIsValidTrusted._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedPositivityTransport + `Ix.Kernel.IndexedRecursiveFixture.indexedVecTailCandidateWhnfNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedPositivityTransport + `Ix.Kernel.IndexedRecursiveFixture.indexedVecTailHasIndOccTrusted._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsHeadDomainRootFreeNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsHeadOpenSucceededNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsHeadTelescopeWhnfIsForallNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsNatDomainRootFreeNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsNatOpenSucceededNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsNatTelescopeWhnfIsForallNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsPositivityParametersSucceededNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsResultWhnfSucceededNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsResultWhnfTerminalNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsTailDomainMentionsRootNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsTailDomainSucceededNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsTailDomainWhnfNotForallNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsTailDomainWhnfSucceededNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsTailOpenSucceededNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsTailTelescopeWhnfIsForallNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsTailWhnfSpineActiveNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedProductionPositivity + `Ix.Kernel.IndexedRecursiveFixture.familyConsTailWhnfSpineIsConstNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedRecursiveAcceptance + `Ix.Kernel.IndexedRecursiveFixture.familyKernelSucceededNative._native.native_decide.ax_1_1, + nativeAxiom `Ix.Kernel.Verify.Inductive.IndexedRecursiveFixture + `Ix.Kernel.IndexedRecursiveFixture.familyEntriesSizeNative._native.native_decide.ax_1_1 ] private def nestedRecursiveFixtureNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.NestedRecursiveFixture name + nativeAxiom `Ix.Kernel.Verify.Inductive.NestedRecursiveFixture name private def nestedRecursiveActionNative : Array Lean.Name := nameContextNative ++ #[ nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.boxInactiveNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.boxInactiveNative._native.native_decide.ax_1_1, nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedMentionsRootNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.nestedMentionsRootNative._native.native_decide.ax_1_1, nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedSpineNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.nestedSpineNative._native.native_decide.ax_1_1, nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedWhnfSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.nestedWhnfSucceededNative._native.native_decide.ax_1_1, nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.positivitySucceededNative._native.native_decide.ax_1_1 + `Ix.Kernel.NestedRecursiveFixture.positivitySucceededNative._native.native_decide.ax_1_1 ] private def nestedRecursiveProducedNative : Array Lean.Name := nestedRecursiveActionNative ++ #[ nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.boxConcreteHeaderMatchesNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.boxConcreteHeaderMatchesNative._native.native_decide.ax_1_1, nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.boxLookupConcreteNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.boxLookupConcreteNative._native.native_decide.ax_1_1, nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.boxLookupSucceededNative._native.native_decide.ax_1_1 + `Ix.Kernel.NestedRecursiveFixture.boxLookupSucceededNative._native.native_decide.ax_1_1 ] private def nestedRecursiveFreshNative : Array Lean.Name := nestedRecursiveProducedNative.push (nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.positivityRequestAbsentNative._native.native_decide.ax_1_1) + `Ix.Kernel.NestedRecursiveFixture.positivityRequestAbsentNative._native.native_decide.ax_1_1) private def nestedRecursiveReachabilityNative : Array Lean.Name := nestedRecursiveFreshNative ++ #[ nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.builtFlatShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.builtFlatShapeNative._native.native_decide.ax_1_1, nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.flatBuildSucceededNative._native.native_decide.ax_1_1 + `Ix.Kernel.NestedRecursiveFixture.flatBuildSucceededNative._native.native_decide.ax_1_1 ] private def nestedCandidateSyntaxNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.NestedCandidateSyntax name + nativeAxiom `Ix.Kernel.Verify.Inductive.NestedCandidateSyntax name private def nestedPositivityTransportNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.NestedPositivityTransport name + nativeAxiom `Ix.Kernel.Verify.Inductive.NestedPositivityTransport name private def nestedAuxiliaryPositivityNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.NestedAuxiliaryPositivity name + nativeAxiom `Ix.Kernel.Verify.Inductive.NestedAuxiliaryPositivity name private def nestedConstructorValidationNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.NestedConstructorValidation name + nativeAxiom `Ix.Kernel.Verify.Inductive.NestedConstructorValidation name private def nestedCandidateRelationNative : Array Lean.Name := #[ nestedCandidateSyntaxNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanAuxiliaryOccursNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.leanAuxiliaryOccursNative._native.native_decide.ax_1_1, nestedCandidateSyntaxNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanAuxiliarySourceNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.leanAuxiliarySourceNative._native.native_decide.ax_1_1, nestedCandidateSyntaxNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanAuxiliaryTargetNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.leanAuxiliaryTargetNative._native.native_decide.ax_1_1, nestedCandidateSyntaxNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanFlatNodeTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.leanFlatNodeTypeNative._native.native_decide.ax_1_1, nestedCandidateSyntaxNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedDomainCandidateCheckNative._native.native_decide.ax_1_1 + `Ix.Kernel.NestedRecursiveFixture.nestedDomainCandidateCheckNative._native.native_decide.ax_1_1 ] private def nestedRecursiveReachabilityWithResultNative : Array Lean.Name := nestedRecursiveReachabilityNative.push (nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedWhnfResultNative._native.native_decide.ax_1_1) + `Ix.Kernel.NestedRecursiveFixture.nestedWhnfResultNative._native.native_decide.ax_1_1) private def nestedOuterTransportNative : Array Lean.Name := nestedRecursiveReachabilityWithResultNative ++ nestedCandidateRelationNative ++ #[ nestedPositivityTransportNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanAuxiliaryCandidateWhnfNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.leanAuxiliaryCandidateWhnfNative._native.native_decide.ax_1_1, nestedPositivityTransportNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedDomainMentionsRootNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.nestedDomainMentionsRootNative._native.native_decide.ax_1_1, nestedPositivityTransportNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedExternalInactiveNative._native.native_decide.ax_1_1 + `Ix.Kernel.NestedRecursiveFixture.nestedExternalInactiveNative._native.native_decide.ax_1_1 ] private def nestedAuxiliaryCandidateTargetNative : Array Lean.Name := @@ -1946,29 +1932,29 @@ private def nestedAuxiliaryCandidateTargetNative : Array Lean.Name := private def nestedAuxiliaryExecutionNative : Array Lean.Name := #[ nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.auxiliaryDiscoverySucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.auxiliaryDiscoverySucceededNative._native.native_decide.ax_1_1, nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.auxiliaryDomainWhnfNotForallNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.auxiliaryDomainWhnfNotForallNative._native.native_decide.ax_1_1, nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.auxiliaryDomainWhnfResultNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.auxiliaryDomainWhnfResultNative._native.native_decide.ax_1_1, nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.auxiliaryDomainWhnfSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.auxiliaryDomainWhnfSucceededNative._native.native_decide.ax_1_1, nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.auxiliaryFieldWhnfShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.auxiliaryFieldWhnfShapeNative._native.native_decide.ax_1_1, nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.auxiliaryFieldWhnfSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.auxiliaryFieldWhnfSucceededNative._native.native_decide.ax_1_1, nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.auxiliaryInstantiationSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.auxiliaryInstantiationSucceededNative._native.native_decide.ax_1_1, nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.auxiliaryParameterArgsNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.auxiliaryParameterArgsNative._native.native_decide.ax_1_1, nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.auxiliaryStrippingSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.auxiliaryStrippingSucceededNative._native.native_decide.ax_1_1, nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.auxiliarySubstitutionSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.auxiliarySubstitutionSucceededNative._native.native_decide.ax_1_1, nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.auxiliaryTreeMentionsRootNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.auxiliaryTreeMentionsRootNative._native.native_decide.ax_1_1, nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.auxiliaryWrapLookupSucceededNative._native.native_decide.ax_1_1 + `Ix.Kernel.NestedRecursiveFixture.auxiliaryWrapLookupSucceededNative._native.native_decide.ax_1_1 ] private def nestedAuxiliaryProductionNative : Array Lean.Name := @@ -1977,70 +1963,70 @@ private def nestedAuxiliaryProductionNative : Array Lean.Name := private def nestedAuxiliaryTransportNative : Array Lean.Name := nameContextNative ++ #[ nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.auxiliaryDomainWhnfResultNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.auxiliaryDomainWhnfResultNative._native.native_decide.ax_1_1, nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.auxiliaryDomainWhnfSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.auxiliaryDomainWhnfSucceededNative._native.native_decide.ax_1_1, nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.auxiliaryTreeMentionsRootNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.auxiliaryTreeMentionsRootNative._native.native_decide.ax_1_1, nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanTreeCandidateWhnfNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.leanTreeCandidateWhnfNative._native.native_decide.ax_1_1, nestedCandidateSyntaxNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanTreeOccursNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.leanTreeOccursNative._native.native_decide.ax_1_1, nestedCandidateSyntaxNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanTreeTargetNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.leanTreeTargetNative._native.native_decide.ax_1_1, nestedCandidateSyntaxNativeAxiom - `Ix.Tc.NestedRecursiveFixture.treeCandidateCheckNative._native.native_decide.ax_1_1 + `Ix.Kernel.NestedRecursiveFixture.treeCandidateCheckNative._native.native_decide.ax_1_1 ] private def nestedAuxiliaryConstructorNative : Array Lean.Name := nestedAuxiliaryProductionNative ++ #[ nestedAuxiliaryPositivityNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanTreeCandidateWhnfNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.leanTreeCandidateWhnfNative._native.native_decide.ax_1_1, nestedCandidateSyntaxNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanTreeOccursNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.leanTreeOccursNative._native.native_decide.ax_1_1, nestedCandidateSyntaxNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanTreeTargetNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.leanTreeTargetNative._native.native_decide.ax_1_1, nestedCandidateSyntaxNativeAxiom - `Ix.Tc.NestedRecursiveFixture.treeCandidateCheckNative._native.native_decide.ax_1_1 + `Ix.Kernel.NestedRecursiveFixture.treeCandidateCheckNative._native.native_decide.ax_1_1 ] private def nestedNodeConstructorValidationNative : Array Lean.Name := nestedOuterTransportNative ++ #[ nestedConstructorValidationNativeAxiom - `Ix.Tc.NestedRecursiveFixture.consumeLeanAuxiliaryNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.consumeLeanAuxiliaryNative._native.native_decide.ax_1_1, nestedConstructorValidationNativeAxiom - `Ix.Tc.NestedRecursiveFixture.instantiateLeanTreeNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.instantiateLeanTreeNative._native.native_decide.ax_1_1, nestedConstructorValidationNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanAuxiliaryEnsureTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.leanAuxiliaryEnsureTypeNative._native.native_decide.ax_1_1, nestedConstructorValidationNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanFlatFieldUniverse._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.leanFlatFieldUniverse._native.native_decide.ax_1_1, nestedConstructorValidationNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanTreeTerminalNative._native.native_decide.ax_1_1 + `Ix.Kernel.NestedRecursiveFixture.leanTreeTerminalNative._native.native_decide.ax_1_1 ] private def nestedWrapConstructorValidationNative : Array Lean.Name := nestedAuxiliaryConstructorNative ++ #[ nestedCandidateSyntaxNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanFlatWrapTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.leanFlatWrapTypeNative._native.native_decide.ax_1_1, nestedConstructorValidationNativeAxiom - `Ix.Tc.NestedRecursiveFixture.consumeLeanTreeNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.consumeLeanTreeNative._native.native_decide.ax_1_1, nestedConstructorValidationNativeAxiom - `Ix.Tc.NestedRecursiveFixture.instantiateLeanAuxiliaryNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.instantiateLeanAuxiliaryNative._native.native_decide.ax_1_1, nestedConstructorValidationNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanAuxiliaryTerminalNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.leanAuxiliaryTerminalNative._native.native_decide.ax_1_1, nestedConstructorValidationNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanFlatFieldUniverse._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.leanFlatFieldUniverse._native.native_decide.ax_1_1, nestedConstructorValidationNativeAxiom - `Ix.Tc.NestedRecursiveFixture.leanTreeEnsureTypeNative._native.native_decide.ax_1_1 + `Ix.Kernel.NestedRecursiveFixture.leanTreeEnsureTypeNative._native.native_decide.ax_1_1 ] private def nestedTreeCandidateSyntaxNative : Array Lean.Name := expressionNative.push (nestedCandidateSyntaxNativeAxiom - `Ix.Tc.NestedRecursiveFixture.treeCandidateCheckNative._native.native_decide.ax_1_1) + `Ix.Kernel.NestedRecursiveFixture.treeCandidateCheckNative._native.native_decide.ax_1_1) /- The nested semantic transaction has two independently executable halves: -the Lean4Lean source/restoration proof and Ix's physical catalog/checker +the Ix.Theory.Named source/restoration proof and Ix's physical catalog/checker join. Keep their native footprints explicit so the headline audit cannot silently acquire oracle materialization or pending assumptions. -/ private def nativeUnion (left right : Array Lean.Name) : Array Lean.Name := @@ -2048,75 +2034,75 @@ private def nativeUnion (left right : Array Lean.Name) : Array Lean.Name := if names.contains name then names else names.push name) left private def nestedSemanticBoxNative : Array Lean.Name := #[ - `Ix.Tc.NestedRecursiveFixture.semanticBoxAfter_isSome._native.native_decide.ax_1_1, - `Ix.Tc.NestedRecursiveFixture.semanticBoxChecked._native.native_decide.ax_1, - `Ix.Tc.NestedRecursiveFixture.semanticBoxGeneration._native.native_decide.ax_1, - `Ix.Tc.NestedRecursiveFixture.semanticBoxShape._native.native_decide.ax_1_1, - `Ix.Tc.NestedRecursiveFixture.semanticBoxShape._native.native_decide.ax_1_2, - `Ix.Tc.NestedRecursiveFixture.semanticBoxShape._native.native_decide.ax_1_3, - `Ix.Tc.NestedRecursiveFixture.semanticBoxShape._native.native_decide.ax_1_4, - `Ix.Tc.NestedRecursiveFixture.semanticBoxShape._native.native_decide.ax_1_5 + `Ix.Kernel.NestedRecursiveFixture.semanticBoxAfter_isSome._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.semanticBoxChecked._native.native_decide.ax_1, + `Ix.Kernel.NestedRecursiveFixture.semanticBoxGeneration._native.native_decide.ax_1, + `Ix.Kernel.NestedRecursiveFixture.semanticBoxShape._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.semanticBoxShape._native.native_decide.ax_1_2, + `Ix.Kernel.NestedRecursiveFixture.semanticBoxShape._native.native_decide.ax_1_3, + `Ix.Kernel.NestedRecursiveFixture.semanticBoxShape._native.native_decide.ax_1_4, + `Ix.Kernel.NestedRecursiveFixture.semanticBoxShape._native.native_decide.ax_1_5 ] private def nestedSemanticWFNative : Array Lean.Name := nestedSemanticBoxNative ++ #[ - `Ix.Tc.NestedRecursiveFixture.semanticTreeNested_isSome._native.native_decide.ax_1_1, - `Ix.Tc.NestedRecursiveFixture.semanticTreeRecursors_eq._native.native_decide.ax_1_1, - `Ix.Tc.NestedRecursiveFixture.semanticTreeRules_eq._native.native_decide.ax_1_1 + `Ix.Kernel.NestedRecursiveFixture.semanticTreeNested_isSome._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.semanticTreeRecursors_eq._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.semanticTreeRules_eq._native.native_decide.ax_1_1 ] private def nestedSemanticCertificateNative : Array Lean.Name := nestedSemanticWFNative.push - `Ix.Tc.NestedRecursiveFixture.semanticTreeAfter_isSome._native.native_decide.ax_1_1 + `Ix.Kernel.NestedRecursiveFixture.semanticTreeAfter_isSome._native.native_decide.ax_1_1 private def nestedSemanticFactsNative : Array Lean.Name := nestedSemanticCertificateNative ++ #[ - `Ix.Tc.NestedRecursiveFixture.semanticTreeNodeName._native.native_decide.ax_1_1, - `Ix.Tc.NestedRecursiveFixture.semanticTreeRestoredClean._native.native_decide.ax_1_1 + `Ix.Kernel.NestedRecursiveFixture.semanticTreeNodeName._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.semanticTreeRestoredClean._native.native_decide.ax_1_1 ] private def nestedSemanticAdmissionNative : Array Lean.Name := nestedSemanticCertificateNative ++ #[ - `Ix.Tc.NestedRecursiveFixture.semanticTreeNodeName._native.native_decide.ax_1_1, - `Ix.Tc.NestedRecursiveFixture.semanticTreeSourceInventory._native.native_decide.ax_1_1, - `Ix.Tc.NestedRecursiveFixture.semanticTreeSourceInventory._native.native_decide.ax_1_2 + `Ix.Kernel.NestedRecursiveFixture.semanticTreeNodeName._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.semanticTreeSourceInventory._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.semanticTreeSourceInventory._native.native_decide.ax_1_2 ] private def nestedAdmissionNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Inductive.NestedAdmission name + nativeAxiom `Ix.Kernel.Verify.Inductive.NestedAdmission name private def nestedAdmissionPublicNative : Array Lean.Name := #[ - `Ix.Tc.NestedRecursiveFixture.nestedCatalog_node._native.native_decide.ax_1_1, - `Ix.Tc.NestedRecursiveFixture.nestedCatalog_node._native.native_decide.ax_1_2, - `Ix.Tc.NestedRecursiveFixture.nestedCatalog_tree._native.native_decide.ax_1_1, - `Ix.Tc.NestedRecursiveFixture.nestedNameOf_node._native.native_decide.ax_1_1, - `Ix.Tc.NestedRecursiveFixture.nestedNameOf_node._native.native_decide.ax_1_2, - `Ix.Tc.NestedRecursiveFixture.nestedNameOf_node._native.native_decide.ax_1_3, - `Ix.Tc.NestedRecursiveFixture.nestedNameOf_node._native.native_decide.ax_1_4, - `Ix.Tc.NestedRecursiveFixture.nestedNameOf_tree._native.native_decide.ax_1_1, - `Ix.Tc.NestedRecursiveFixture.nestedNameOf_tree._native.native_decide.ax_1_2, - `Ix.Tc.NestedRecursiveFixture.nestedNameOf_tree._native.native_decide.ax_1_3 + `Ix.Kernel.NestedRecursiveFixture.nestedCatalog_node._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.nestedCatalog_node._native.native_decide.ax_1_2, + `Ix.Kernel.NestedRecursiveFixture.nestedCatalog_tree._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.nestedNameOf_node._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.nestedNameOf_node._native.native_decide.ax_1_2, + `Ix.Kernel.NestedRecursiveFixture.nestedNameOf_node._native.native_decide.ax_1_3, + `Ix.Kernel.NestedRecursiveFixture.nestedNameOf_node._native.native_decide.ax_1_4, + `Ix.Kernel.NestedRecursiveFixture.nestedNameOf_tree._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.nestedNameOf_tree._native.native_decide.ax_1_2, + `Ix.Kernel.NestedRecursiveFixture.nestedNameOf_tree._native.native_decide.ax_1_3 ] private def nestedAdmissionPrivateNative : Array Lean.Name := #[ nestedAdmissionNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedFamilyBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.nestedFamilyBlockLoadedNative._native.native_decide.ax_1_1, nestedAdmissionNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedMemberShapeFactsNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.nestedMemberShapeFactsNative._native.native_decide.ax_1_1, nestedAdmissionNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedMemberShapeFactsNative._native.native_decide.ax_1_2, + `Ix.Kernel.NestedRecursiveFixture.nestedMemberShapeFactsNative._native.native_decide.ax_1_2, nestedAdmissionNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedMemberShapeFactsNative._native.native_decide.ax_1_3, + `Ix.Kernel.NestedRecursiveFixture.nestedMemberShapeFactsNative._native.native_decide.ax_1_3, nestedAdmissionNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedMemberShapeFactsNative._native.native_decide.ax_1_4, + `Ix.Kernel.NestedRecursiveFixture.nestedMemberShapeFactsNative._native.native_decide.ax_1_4, nestedAdmissionNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedNodeDirectConstructor._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.nestedNodeDirectConstructor._native.native_decide.ax_1_1, nestedAdmissionNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedNodeTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.nestedNodeTypeRawNative._native.native_decide.ax_1_1, nestedAdmissionNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedTreeDirectOwner._native.native_decide.ax_1_1, + `Ix.Kernel.NestedRecursiveFixture.nestedTreeDirectOwner._native.native_decide.ax_1_1, nestedAdmissionNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedTreeTypeRawNative._native.native_decide.ax_1_1 + `Ix.Kernel.NestedRecursiveFixture.nestedTreeTypeRawNative._native.native_decide.ax_1_1 ] private def nestedFamilyCertificateNative : Array Lean.Name := @@ -2127,7 +2113,7 @@ private def nestedFamilyCertificateNative : Array Lean.Name := private def nestedFamilyKernelNative : Array Lean.Name := inductiveNative.push (nestedAdmissionNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedFamilyKernelSucceededNative._native.native_decide.ax_1_1) + `Ix.Kernel.NestedRecursiveFixture.nestedFamilyKernelSucceededNative._native.native_decide.ax_1_1) private def nestedSemanticTransactionClosureNative : Array Lean.Name := let withSemantics := nativeUnion nestedFamilyCertificateNative @@ -2137,10 +2123,10 @@ private def nestedSemanticTransactionClosureNative : Array Lean.Name := let withWrap := nativeUnion withNode nestedWrapConstructorValidationNative let withBoxIngress := withWrap.push (nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.boxIngressSucceededNative._native.native_decide.ax_1_1) + `Ix.Kernel.NestedRecursiveFixture.boxIngressSucceededNative._native.native_decide.ax_1_1) withBoxIngress.push (nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.treeIngressSucceededNative._native.native_decide.ax_1_1) + `Ix.Kernel.NestedRecursiveFixture.treeIngressSucceededNative._native.native_decide.ax_1_1) /- The physical nested-recursor slice is deliberately separate from the source transaction above. It compiles the retained kernel declarations, @@ -2153,7 +2139,7 @@ private def nestedRecursorNativeUserName (decl : String) Lean.Name.str (Lean.Name.str (Lean.Name.str - (Lean.Name.str `Ix.Tc.NestedRecursiveFixture decl) + (Lean.Name.str `Ix.Kernel.NestedRecursiveFixture decl) "_native") "native_decide") s!"ax_1_{index + 1}" @@ -2171,22 +2157,22 @@ private def nestedRecursorPublicNativeSeries (decl : String) private def nestedRecursorFixtureNativeSeries (decl : String) (count : Nat := 1) : Array Lean.Name := nestedRecursorNativeSeries - `Ix.Tc.Verify.Inductive.NestedRecursorFixture decl count + `Ix.Kernel.Verify.Inductive.NestedRecursorFixture decl count private def nestedRecursorPatternNativeSeries (decl : String) (count : Nat := 1) : Array Lean.Name := nestedRecursorNativeSeries - `Ix.Tc.Verify.Inductive.NestedRecursorPattern decl count + `Ix.Kernel.Verify.Inductive.NestedRecursorPattern decl count private def nestedRecursorSoundnessNativeSeries (decl : String) (count : Nat := 1) : Array Lean.Name := nestedRecursorNativeSeries - `Ix.Tc.Verify.Inductive.NestedRecursorSoundness decl count + `Ix.Kernel.Verify.Inductive.NestedRecursorSoundness decl count private def nestedRecursorAdmissionNativeSeries (decl : String) (count : Nat := 1) : Array Lean.Name := nestedRecursorNativeSeries - `Ix.Tc.Verify.Inductive.NestedRecursorAdmission decl count + `Ix.Kernel.Verify.Inductive.NestedRecursorAdmission decl count private def nestedRecursorCompilerBaseNative : Array Lean.Name := nameContextNative.push mutualInternDataValueNative @@ -2267,7 +2253,7 @@ private def nestedRecursorPublicNative : Array Lean.Name := nestedRecursorPublicNativeSeries "nestedRecursorNameOf_wrap" 2 private def nestedRecursorMemberShapeNative : Array Lean.Name := - nestedRecursorNativeSeries `Ix.Tc.Verify.Inductive.NestedAdmission + nestedRecursorNativeSeries `Ix.Kernel.Verify.Inductive.NestedAdmission "nestedMemberShapeFactsNative" 4 private def nestedRecursorAdmissionFactsNative : Array Lean.Name := @@ -2317,26 +2303,26 @@ private def nestedRecursorOperationalNative : Array Lean.Name := nestedRecursorFixtureNativeSeries "recursorEntriesUniqueNative" ++ nestedRecursorFixtureNativeSeries "recursorEntryIdsNative" ++ nestedRecursorFixtureNativeSeries "recursorIngressSucceededNative" ++ - #[nativeAxiom `Ix.Tc.Inductive - `Ix.Tc.RecM.canonicalAuxOrder._native.native_decide.ax_9] + #[nativeAxiom `Ix.Kernel.Inductive + `Ix.Kernel.RecM.canonicalAuxOrder._native.native_decide.ax_9] private def nestedRecursorAtomicClosureNative : Array Lean.Name := nativeUnion nestedRecursorAtomicAdmissionNative nestedRecursorOperationalNative private def nestedRestoredPatternUpstreamDebt : Array Lean.Name := #[ - ``Lean4Lean.VEnv.IsDefEqU.forallE_inv_stratified, - ``Lean4Lean.VEnv.IsDefEqU.sort_inv + ``Ix.Theory.Named.VEnv.IsDefEqU.forallE_inv_stratified, + ``Ix.Theory.Named.VEnv.IsDefEqU.sort_inv ] private def booleanAcceptanceNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Driver.BooleanAcceptance name + nativeAxiom `Ix.Kernel.Verify.Driver.BooleanAcceptance name private def serializedBooleanNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Ingress.SerializedBoolean name + nativeAxiom `Ix.Kernel.Verify.Ingress.SerializedBoolean name private def literalBlobsNativeAxiom (name : Lean.Name) : Lean.Name := - nativeAxiom `Ix.Tc.Verify.Ingress.LiteralBlobs name + nativeAxiom `Ix.Kernel.Verify.Ingress.LiteralBlobs name /- The explicit Boolean one-family admission consumes the finite catalog, ingress, generation, rule, and pattern facts below. Checker executions are @@ -2344,139 +2330,139 @@ kept out of this shared semantic slice so the one-family root cannot inherit them merely because the larger end-to-end witness also records those runs. -/ private def booleanSemanticFixtureNative : Array Lean.Name := #[ enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogFalseNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.catalogFalseNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogFalseNative._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.catalogFalseNative._native.native_decide.ax_1_2, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.catalogFamilyNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.catalogRecursorNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogRecursorNative._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.catalogRecursorNative._native.native_decide.ax_1_2, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogRecursorNative._native.native_decide.ax_1_3, + `Ix.Kernel.BooleanEnumerationFixture.catalogRecursorNative._native.native_decide.ax_1_3, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogRecursorNative._native.native_decide.ax_1_4, + `Ix.Kernel.BooleanEnumerationFixture.catalogRecursorNative._native.native_decide.ax_1_4, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_2, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_3, + `Ix.Kernel.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_3, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.enumerationShapeNative._native.native_decide.ax_1_6, + `Ix.Kernel.BooleanEnumerationFixture.enumerationShapeNative._native.native_decide.ax_1_6, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.enumerationShapeNative._native.native_decide.ax_1_7, + `Ix.Kernel.BooleanEnumerationFixture.enumerationShapeNative._native.native_decide.ax_1_7, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseEntryNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseRuleBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseRuleBinderCoreNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseRuleFieldsNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseRuleFieldsNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseRuleRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseRuleRawNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseRuleScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseRuleScopedNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseRuleSizeBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseRuleSizeBoundNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseShapeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseSourceTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseSourceTypeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseTypeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseTypeNative._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.falseTypeNative._native.native_decide.ax_1_2, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyConstructorCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyConstructorCountNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntriesSizeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntriesUniqueNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntryAtOneNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntryAtOneNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntryAtTwoNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntryAtTwoNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntryAtZeroNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntryAtZeroNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntryIdsNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntryNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyIngressSucceededNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyMemberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyMemberKidsNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyShapeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyTypeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.generationCtorPairOne._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.generationCtorPairOne._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.generationCtorPairZero._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.generationCtorPairZero._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.nameOfFalseNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.nameOfFalseNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.nameOfFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.nameOfFamilyNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.nameOfRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.nameOfRecursorNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.nameOfTrueNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.nameOfTrueNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorEntriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorEntriesSizeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorEntriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorEntriesUniqueNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorEntryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorEntryIdsNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorEntryNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorIngressSucceededNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorMemberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorMemberKidsNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorShapeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorTypeRawNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.sourceConstructorOne._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.sourceConstructorOne._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.sourceConstructorZero._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.sourceConstructorZero._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueEntryNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueRuleBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueRuleBinderCoreNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueRuleFieldsNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueRuleFieldsNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueRuleRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueRuleRawNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueRuleScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueRuleScopedNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueRuleSizeBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueRuleSizeBoundNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueShapeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueSourceTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueSourceTypeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueTypeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueTypeNative._native.native_decide.ax_1_2 + `Ix.Kernel.BooleanEnumerationFixture.trueTypeNative._native.native_decide.ax_1_2 ] private def booleanSemanticAdmissionNative : Array Lean.Name := nameNative ++ #[ enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorOwnerNative._native.native_decide.ax_1_1 + `Ix.Kernel.BooleanEnumerationFixture.recursorOwnerNative._native.native_decide.ax_1_1 ] ++ booleanSemanticFixtureNative /- The concrete Boolean end-to-end witness additionally evaluates the real @@ -2486,27 +2472,27 @@ than treating the witness as one opaque executable assumption. -/ private def booleanEnumerationNative : Array Lean.Name := inductiveNative ++ #[ enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyBodySucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyBodySucceededNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyClassificationSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyClassificationSucceededNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyKernelSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyKernelSucceededNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorBlockLoadedAfterFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorBlockLoadedAfterFamilyNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorBodySucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorBodySucceededNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorClassificationSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorClassificationSucceededNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorKernelSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorKernelSucceededNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorOwnerNative._native.native_decide.ax_1_1 + `Ix.Kernel.BooleanEnumerationFixture.recursorOwnerNative._native.native_decide.ax_1_1 ] ++ booleanSemanticFixtureNative /- The E3-S family-body bridge consumes only the family-side slice of the @@ -2516,83 +2502,83 @@ recursor run, kernel-run, generated-rule, or recursor-ingress facts merely because the larger E2b witness uses them. -/ private def booleanFamilyBodyNative : Array Lean.Name := inductiveNative ++ #[ enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyBodySucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyBodySucceededNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyClassificationSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyClassificationSucceededNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogFalseNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.catalogFalseNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogFalseNative._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.catalogFalseNative._native.native_decide.ax_1_2, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.catalogFamilyNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_2, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_3, + `Ix.Kernel.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_3, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseEntryNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseShapeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseSourceTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseSourceTypeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseTypeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseTypeNative._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.falseTypeNative._native.native_decide.ax_1_2, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyConstructorCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyConstructorCountNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntriesSizeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntriesUniqueNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntryAtOneNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntryAtOneNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntryAtTwoNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntryAtTwoNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntryAtZeroNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntryAtZeroNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntryIdsNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntryNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyIngressSucceededNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyMemberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyMemberKidsNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyShapeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyTypeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.nameOfFalseNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.nameOfFalseNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.nameOfFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.nameOfFamilyNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.nameOfTrueNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.nameOfTrueNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorEntriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorEntriesSizeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorShapeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.sourceConstructorOne._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.sourceConstructorOne._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.sourceConstructorZero._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.sourceConstructorZero._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueEntryNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueShapeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueSourceTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueSourceTypeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueTypeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueTypeNative._native.native_decide.ax_1_2 + `Ix.Kernel.BooleanEnumerationFixture.trueTypeNative._native.native_decide.ax_1_2 ] /-- Exact evaluator boundary of the final E3-S Boolean whole-driver witness. @@ -2602,185 +2588,185 @@ does not inherit the earlier standalone body/kernel executions as semantic authority for the serial run. -/ def booleanDriverNative : Array Lean.Name := inductiveNative ++ #[ booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.buildAnonWorkNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.buildAnonWorkNative._native.native_decide.ax_1_1, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.checkEnvAnonNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.checkEnvAnonNative._native.native_decide.ax_1_1, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseProjectionEntry._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseProjectionEntry._native.native_decide.ax_1_1, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseProjectionEntry._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.falseProjectionEntry._native.native_decide.ax_1_2, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseProjectionEntry._native.native_decide.ax_1_3, + `Ix.Kernel.BooleanEnumerationFixture.falseProjectionEntry._native.native_decide.ax_1_3, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyBlockEntry._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyBlockEntry._native.native_decide.ax_1_1, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyBlockEntry._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.familyBlockEntry._native.native_decide.ax_1_2, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyBlockEntry._native.native_decide.ax_1_3, + `Ix.Kernel.BooleanEnumerationFixture.familyBlockEntry._native.native_decide.ax_1_3, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyProjectionEntry._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyProjectionEntry._native.native_decide.ax_1_1, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyProjectionEntry._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.familyProjectionEntry._native.native_decide.ax_1_2, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyProjectionEntry._native.native_decide.ax_1_3, + `Ix.Kernel.BooleanEnumerationFixture.familyProjectionEntry._native.native_decide.ax_1_3, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyTargetsNonemptyNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyTargetsNonemptyNative._native.native_decide.ax_1_1, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorBlockEntry._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorBlockEntry._native.native_decide.ax_1_1, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorBlockEntry._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.recursorBlockEntry._native.native_decide.ax_1_2, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorBlockEntry._native.native_decide.ax_1_3, + `Ix.Kernel.BooleanEnumerationFixture.recursorBlockEntry._native.native_decide.ax_1_3, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorProjectionEntry._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorProjectionEntry._native.native_decide.ax_1_1, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorProjectionEntry._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.recursorProjectionEntry._native.native_decide.ax_1_2, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorProjectionEntry._native.native_decide.ax_1_3, + `Ix.Kernel.BooleanEnumerationFixture.recursorProjectionEntry._native.native_decide.ax_1_3, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorTargetsNonemptyNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorTargetsNonemptyNative._native.native_decide.ax_1_1, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.sourceAddressesNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.sourceAddressesNative._native.native_decide.ax_1_1, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.sourceAddressesNodupNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.sourceAddressesNodupNative._native.native_decide.ax_1_1, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.sourceKeysNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.sourceKeysNative._native.native_decide.ax_1_1, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueProjectionEntry._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueProjectionEntry._native.native_decide.ax_1_1, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueProjectionEntry._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.trueProjectionEntry._native.native_decide.ax_1_2, booleanAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueProjectionEntry._native.native_decide.ax_1_3, + `Ix.Kernel.BooleanEnumerationFixture.trueProjectionEntry._native.native_decide.ax_1_3, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyDirectOwnerNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorBlockLoadedNative._native.native_decide.ax_1_1, enumerationAcceptanceNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorOwnerNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorOwnerNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogFalseNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.catalogFalseNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogFalseNative._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.catalogFalseNative._native.native_decide.ax_1_2, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.catalogFamilyNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.catalogRecursorNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogRecursorNative._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.catalogRecursorNative._native.native_decide.ax_1_2, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogRecursorNative._native.native_decide.ax_1_3, + `Ix.Kernel.BooleanEnumerationFixture.catalogRecursorNative._native.native_decide.ax_1_3, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogRecursorNative._native.native_decide.ax_1_4, + `Ix.Kernel.BooleanEnumerationFixture.catalogRecursorNative._native.native_decide.ax_1_4, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_2, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_3, + `Ix.Kernel.BooleanEnumerationFixture.catalogTrueNative._native.native_decide.ax_1_3, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.enumerationShapeNative._native.native_decide.ax_1_6, + `Ix.Kernel.BooleanEnumerationFixture.enumerationShapeNative._native.native_decide.ax_1_6, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.enumerationShapeNative._native.native_decide.ax_1_7, + `Ix.Kernel.BooleanEnumerationFixture.enumerationShapeNative._native.native_decide.ax_1_7, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseEntryNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseRuleBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseRuleBinderCoreNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseRuleFieldsNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseRuleFieldsNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseRuleRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseRuleRawNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseRuleScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseRuleScopedNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseRuleSizeBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseRuleSizeBoundNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseShapeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseSourceTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseSourceTypeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.falseTypeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.falseTypeNative._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanEnumerationFixture.falseTypeNative._native.native_decide.ax_1_2, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyConstructorCountNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyConstructorCountNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntriesSizeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntriesUniqueNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntryAtOneNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntryAtOneNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntryAtTwoNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntryAtTwoNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntryAtZeroNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntryAtZeroNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntryIdsNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyEntryNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyIngressSucceededNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyMemberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyMemberKidsNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyShapeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.familyTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.familyTypeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.generationCtorPairOne._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.generationCtorPairOne._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.generationCtorPairZero._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.generationCtorPairZero._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.nameOfFalseNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.nameOfFalseNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.nameOfFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.nameOfFamilyNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.nameOfRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.nameOfRecursorNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.nameOfTrueNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.nameOfTrueNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorEntriesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorEntriesSizeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorEntriesUniqueNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorEntriesUniqueNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorEntryIdsNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorEntryIdsNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorEntryNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorIngressSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorIngressSucceededNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorMemberKidsNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorMemberKidsNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorRulesSizeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorShapeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.recursorTypeRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.recursorTypeRawNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.sourceConstructorOne._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.sourceConstructorOne._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.sourceConstructorZero._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.sourceConstructorZero._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueEntryNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueEntryNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueRuleBinderCoreNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueRuleBinderCoreNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueRuleFieldsNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueRuleFieldsNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueRuleRawNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueRuleRawNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueRuleScopedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueRuleScopedNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueRuleSizeBoundNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueRuleSizeBoundNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueShapeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueShapeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueSourceTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueSourceTypeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueTypeNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanEnumerationFixture.trueTypeNative._native.native_decide.ax_1_1, enumerationFixtureNativeAxiom - `Ix.Tc.BooleanEnumerationFixture.trueTypeNative._native.native_decide.ax_1_2 + `Ix.Kernel.BooleanEnumerationFixture.trueTypeNative._native.native_decide.ax_1_2 ] /-- Exact evaluator boundary of the serialized T0 Boolean certificate. Each @@ -2788,255 +2774,255 @@ closed computation is named so changes in the byte, eager, lazy, dependency, or driver slices are visible independently in the trust manifest. -/ def serializedBooleanNative : Array Lean.Name := booleanDriverNative ++ #[ serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.blobKeysNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.blobKeysNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.buildAnonWorkNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.buildAnonWorkNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.checkEnvAnonNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.checkEnvAnonNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.decodeSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.decodeSucceededNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.eagerBlockKeysClassifiedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.eagerBlockKeysClassifiedNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.eagerFalseNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.eagerFalseNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.eagerFamilyBlockNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.eagerFamilyBlockNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.eagerFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.eagerFamilyNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.eagerKeysClassifiedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.eagerKeysClassifiedNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.eagerRecursorBlockNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.eagerRecursorBlockNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.eagerRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.eagerRecursorNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.eagerSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.eagerSucceededNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.eagerTrueNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.eagerTrueNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.eagerWorkNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.eagerWorkNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.encodeSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.encodeSucceededNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.falseProjectionEntry._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.falseProjectionEntry._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.falseProjectionEntry._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanSerialized.falseProjectionEntry._native.native_decide.ax_1_2, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.falseProjectionHashNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.falseProjectionHashNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.falseProjectionLookupNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.falseProjectionLookupNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.familyBlockEntry._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.familyBlockEntry._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.familyBlockEntry._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanSerialized.familyBlockEntry._native.native_decide.ax_1_2, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.familyBlockHashNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.familyBlockHashNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.familyBlockLookupNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.familyBlockLookupNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.familyProjectionEntry._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.familyProjectionEntry._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.familyProjectionEntry._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanSerialized.familyProjectionEntry._native.native_decide.ax_1_2, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.familyProjectionHashNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.familyProjectionHashNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.familyProjectionLookupNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.familyProjectionLookupNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.familyTargetsNonemptyNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.familyTargetsNonemptyNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.lazyFalseLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.lazyFalseLoadedNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.lazyFamilyBlockKeysNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.lazyFamilyBlockKeysNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.lazyFamilyBlockNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.lazyFamilyBlockNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.lazyFamilyKeysClassifiedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.lazyFamilyKeysClassifiedNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.lazyFamilyLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.lazyFamilyLoadedNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.lazyFamilySucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.lazyFamilySucceededNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.lazyFinalBlockKeysClassifiedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.lazyFinalBlockKeysClassifiedNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.lazyFinalFalseNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.lazyFinalFalseNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.lazyFinalFamilyBlockNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.lazyFinalFamilyBlockNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.lazyFinalFamilyNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.lazyFinalFamilyNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.lazyFinalRecursorBlockNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.lazyFinalRecursorBlockNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.lazyFinalRecursorNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.lazyFinalRecursorNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.lazyFinalTrueNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.lazyFinalTrueNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.lazyRecursorKeysClassifiedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.lazyRecursorKeysClassifiedNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.lazyRecursorSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.lazyRecursorSucceededNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.lazyTrueLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.lazyTrueLoadedNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.originalFalseProjectionLookupNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.originalFalseProjectionLookupNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.originalFamilyBlockLookupNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.originalFamilyBlockLookupNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.originalFamilyProjectionLookupNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.originalFamilyProjectionLookupNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.originalRecursorBlockLookupNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.originalRecursorBlockLookupNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.originalRecursorProjectionLookupNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.originalRecursorProjectionLookupNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.originalTrueProjectionLookupNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.originalTrueProjectionLookupNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.recursorBlockEntry._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.recursorBlockEntry._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.recursorBlockEntry._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanSerialized.recursorBlockEntry._native.native_decide.ax_1_2, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.recursorBlockHashNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.recursorBlockHashNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.recursorBlockLookupNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.recursorBlockLookupNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.recursorProjectionEntry._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.recursorProjectionEntry._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.recursorProjectionEntry._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanSerialized.recursorProjectionEntry._native.native_decide.ax_1_2, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.recursorProjectionHashNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.recursorProjectionHashNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.recursorProjectionLookupNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.recursorProjectionLookupNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.recursorTargetsNonemptyNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.recursorTargetsNonemptyNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.sourceAddressesNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.sourceAddressesNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.sourceAddressesNodupNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.sourceAddressesNodupNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.sourceKeysNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.sourceKeysNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.trueProjectionEntry._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.trueProjectionEntry._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.trueProjectionEntry._native.native_decide.ax_1_2, + `Ix.Kernel.BooleanSerialized.trueProjectionEntry._native.native_decide.ax_1_2, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.trueProjectionHashNative._native.native_decide.ax_1_1, + `Ix.Kernel.BooleanSerialized.trueProjectionHashNative._native.native_decide.ax_1_1, serializedBooleanNativeAxiom - `Ix.Tc.BooleanSerialized.trueProjectionLookupNative._native.native_decide.ax_1_1 + `Ix.Kernel.BooleanSerialized.trueProjectionLookupNative._native.native_decide.ax_1_1 ] /- Exact evaluator boundary of the non-vacuous literal/blob T0 fixture. -/ private def literalRoundTripNative : Array Lean.Name := nameNative ++ #[ literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.blobKeysClassifiedNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.blobKeysClassifiedNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.decodeSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.decodeSucceededNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.encodeSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.encodeSucceededNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.natBlobHashNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.natBlobHashNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.natBlobLookupNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.natBlobLookupNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.natEntry._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.natEntry._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.natEntry._native.native_decide.ax_1_2, + `Ix.Kernel.SerializedLiteralBlobs.natEntry._native.native_decide.ax_1_2, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.natHashNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.natHashNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.natLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.natLoadedNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.natLookupNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.natLookupNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.natSucceededNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.natSucceededNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.sourceAddressesClassifiedNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.sourceAddressesClassifiedNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.sourceAddressesNodupNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.sourceAddressesNodupNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.sourceKeysClassifiedNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.sourceKeysClassifiedNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.stringBlobHashNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.stringBlobHashNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.stringBlobLookupNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.stringBlobLookupNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.stringEntry._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.stringEntry._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.stringEntry._native.native_decide.ax_1_2, + `Ix.Kernel.SerializedLiteralBlobs.stringEntry._native.native_decide.ax_1_2, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.stringHashNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.stringHashNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.stringLoadedNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.stringLoadedNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.stringLookupNative._native.native_decide.ax_1_1, + `Ix.Kernel.SerializedLiteralBlobs.stringLookupNative._native.native_decide.ax_1_1, literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.stringSucceededNative._native.native_decide.ax_1_1 + `Ix.Kernel.SerializedLiteralBlobs.stringSucceededNative._native.native_decide.ax_1_1 ] private def malformedConstantNative : Array Lean.Name := canonicalPrimitivesNative.push <| literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.malformedConstantRejectedNative._native.native_decide.ax_1_1 + `Ix.Kernel.SerializedLiteralBlobs.malformedConstantRejectedNative._native.native_decide.ax_1_1 private def malformedBlobNative : Array Lean.Name := canonicalPrimitivesNative.push <| literalBlobsNativeAxiom - `Ix.Tc.SerializedLiteralBlobs.malformedBlobRejectedNative._native.native_decide.ax_1_1 + `Ix.Kernel.SerializedLiteralBlobs.malformedBlobRejectedNative._native.native_decide.ax_1_1 /- Direct upstream `sorryAx` origins. Listing the declarations, rather than merely allowing `sorryAx`, makes upstream debt movement visible in review. -The certificate-bearing Lean4Lean pin discharges the former `VInductDecl.WF`, +The certificate-bearing Ix.Theory.Named pin discharges the former `VInductDecl.WF`, `VEnv.addInduct`, `VEnv.addInduct_WF`, and `TrProj` origins. -/ private def forallEInv : Lean.Name := - ``Lean4Lean.VEnv.IsDefEqU.forallE_inv_stratified -private def sortInv : Lean.Name := ``Lean4Lean.VEnv.IsDefEqU.sort_inv + ``Ix.Theory.Named.VEnv.IsDefEqU.forallE_inv_stratified +private def sortInv : Lean.Name := ``Ix.Theory.Named.VEnv.IsDefEqU.sort_inv private def typingDebt : Array Lean.Name := #[forallEInv, sortInv] -/- P0's concrete projection adapter consumes Lean4Lean's structural laws. +/- P0's concrete projection adapter consumes Ix.Theory.Named's structural laws. Its uniqueness law reaches the named registered-structure inversion theorem; -the context-defeq law also crosses Lean4Lean's current unique-typing boundary +the context-defeq law also crosses Ix.Theory.Named's current unique-typing boundary and therefore inherits the two L2 inversion origins. Keep this exact rather than allowing the remainder of the executable inductive-fixture debt. -/ private def projectionDebt : Array Lean.Name := - typingDebt.push ``Lean4Lean.VEnv.WF.registeredStructureHeadInversion + typingDebt.push ``Ix.Theory.Named.VEnv.WF.registeredStructureHeadInversion /- The empty legacy whole-`KEnv` inductive path is forbidden from every G2b consumer root. Keeping this list in the executable audit prevents an innocent-looking helper from reintroducing the old `nomatch` dependency. -/ private def legacyWholeEnv : Array Lean.Name := #[ - ``Ix.Tc.AddKInduct, - ``Ix.Tc.AddKInduct.to_addInduct, - ``Ix.Tc.TrKEnv', - ``Ix.Tc.TrKEnv + ``Ix.Kernel.AddKInduct, + ``Ix.Kernel.AddKInduct.to_addInduct, + ``Ix.Kernel.TrKEnv', + ``Ix.Kernel.TrKEnv ] /- E2a is intentionally a Theory-only certificate consumer. These checker/catalog/pattern declarations must not enter its dependency graph. -/ private def certificateAdapterForbidden : Array Lean.Name := #[ - ``Ix.Tc.Catalog, - ``Ix.Tc.RawInductiveConstRel, - ``Ix.Tc.RawRecursorRuleRel, - ``Ix.Tc.RawRecursorRulePatternRel, - ``Ix.Tc.InductiveOracle, - ``Lean4Lean.TrProj + ``Ix.Kernel.Catalog, + ``Ix.Kernel.RawInductiveConstRel, + ``Ix.Kernel.RawRecursorRuleRel, + ``Ix.Kernel.RawRecursorRulePatternRel, + ``Ix.Kernel.InductiveOracle, + ``Ix.Theory.Named.TrProj ] -/- `AnnotatedPi`'s upstream certificate is produced by Lean4Lean's executable +/- `AnnotatedPi`'s upstream certificate is produced by Ix.Theory.Named's executable normalization pipeline, so it cannot satisfy the earlier closed-form certificate quarantine against `TrProj`. It must still remain independent of all Ix catalog, checker-pattern, and oracle authority. -/ private def annotatedPiCertificateForbidden : Array Lean.Name := #[ - ``Ix.Tc.Catalog, - ``Ix.Tc.RawInductiveConstRel, - ``Ix.Tc.RawRecursorRuleRel, - ``Ix.Tc.RawRecursorRulePatternRel, - ``Ix.Tc.InductiveOracle + ``Ix.Kernel.Catalog, + ``Ix.Kernel.RawInductiveConstRel, + ``Ix.Kernel.RawRecursorRuleRel, + ``Ix.Kernel.RawRecursorRulePatternRel, + ``Ix.Kernel.InductiveOracle ] /- The pre-TrustedBody delta route admitted successful unfolding through a broad reflection oracle and arbitrary cache-write authority. The final K1 closure must use exact trusted declaration certificates instead. -/ private def legacyDeltaAuthority : Array Lean.Name := #[ - ``Ix.Tc.UnfoldCacheWriteOracle, - ``Ix.Tc.DeltaUnfoldReflection, - ``Ix.Tc.RecM.DeltaUnfoldContext, - ``Ix.Tc.RecM.FullWhnfStepContext.ofDelta + ``Ix.Kernel.UnfoldCacheWriteOracle, + ``Ix.Kernel.DeltaUnfoldReflection, + ``Ix.Kernel.RecM.DeltaUnfoldContext, + ``Ix.Kernel.RecM.FullWhnfStepContext.ofDelta ] private def k1ForbiddenDependencies : Array Lean.Name := @@ -3047,13 +3033,13 @@ all-depth, single-support closure interface whose finite-sort obstruction is proved below. The legacy declarations remain audited as compatibility artifacts while consumers migrate. -/ private def legacyAllDepthKnot : Array Lean.Name := #[ - ``Ix.Tc.RecursiveMethodClosureContext, - ``Ix.Tc.RecursiveMethodClosureContext.closedAt, - ``Ix.Tc.RecursiveMethodClosureContext.methodsN, - ``Ix.Tc.RecursiveMethodClosureContext.fullInferenceContext, - ``Ix.Tc.RecursiveMethodClosureContext.next_fullInferenceWFAt, - ``Ix.Tc.RecursiveMethodClosureContext.methodsN_fullInferenceWFAt, - ``Ix.Tc.RecursiveMethodClosureContext.publicInfer_full_wf + ``Ix.Kernel.RecursiveMethodClosureContext, + ``Ix.Kernel.RecursiveMethodClosureContext.closedAt, + ``Ix.Kernel.RecursiveMethodClosureContext.methodsN, + ``Ix.Kernel.RecursiveMethodClosureContext.fullInferenceContext, + ``Ix.Kernel.RecursiveMethodClosureContext.next_fullInferenceWFAt, + ``Ix.Kernel.RecursiveMethodClosureContext.methodsN_fullInferenceWFAt, + ``Ix.Kernel.RecursiveMethodClosureContext.publicInfer_full_wf ] private def boundedKnotForbiddenDependencies : Array Lean.Name := @@ -3062,7 +3048,7 @@ private def boundedKnotForbiddenDependencies : Array Lean.Name := /- E2c occurrence-validation roots must be derived from the production run, not from the ambient semantic inductive oracle retained by E2b. -/ private def occurrenceValidationForbiddenDependencies : Array Lean.Name := - boundedKnotForbiddenDependencies.push ``Ix.Tc.InductiveOracle + boundedKnotForbiddenDependencies.push ``Ix.Kernel.InductiveOracle /- Existing semantic admission returns the shared `TrustedCatalogLog`, whose inductive declaration necessarily mentions its legacy ambient constructor. @@ -3070,19 +3056,19 @@ Constructor-insensitive dependency traversal therefore cannot forbid the `InductiveOracle` type itself here. Instead, quarantine every operation that materializes or admits an oracle-selected future world. -/ private def oracleWorldMaterialization : Array Lean.Name := #[ - ``Ix.Tc.VerifyWorld.admitOracle, - ``Ix.Tc.VerifyWorld.le_admitOracle, - ``Ix.Tc.OracleBlockCertificate.admit, - ``Ix.Tc.OracleBlockCertificate.admitState, - ``Ix.Tc.RecM.certifyOracleBackedBlock, - ``Ix.Tc.RecM.certifyOracleBackedAdmittedBlock, - ``Ix.Tc.SingletonFamilyCatalogLink.oracle, - ``Ix.Tc.SingletonRecursorCatalogLink.oracle, - ``Ix.Tc.InductiveOracle.reindex, - ``Ix.Tc.InductiveOracle.restageMissing, - ``Ix.Tc.IndexedRecursivePattern.oracle, - ``Ix.Tc.IndexedRecursiveFixture.recursorBlockOracle, - ``Ix.Tc.IndexedRecursiveFixture.recursorAtomicAdmission + ``Ix.Kernel.VerifyWorld.admitOracle, + ``Ix.Kernel.VerifyWorld.le_admitOracle, + ``Ix.Kernel.OracleBlockCertificate.admit, + ``Ix.Kernel.OracleBlockCertificate.admitState, + ``Ix.Kernel.RecM.certifyOracleBackedBlock, + ``Ix.Kernel.RecM.certifyOracleBackedAdmittedBlock, + ``Ix.Kernel.SingletonFamilyCatalogLink.oracle, + ``Ix.Kernel.SingletonRecursorCatalogLink.oracle, + ``Ix.Kernel.InductiveOracle.reindex, + ``Ix.Kernel.InductiveOracle.restageMissing, + ``Ix.Kernel.IndexedRecursivePattern.oracle, + ``Ix.Kernel.IndexedRecursiveFixture.recursorBlockOracle, + ``Ix.Kernel.IndexedRecursiveFixture.recursorAtomicAdmission ] private def existingSemanticBlockForbiddenDependencies : Array Lean.Name := @@ -3092,14 +3078,14 @@ private def existingSemanticBlockForbiddenDependencies : Array Lean.Name := finite positive-fuel construction must neither manufacture that model nor reach the older public adapters that consume it. -/ private def legacyGlobalSuffix : Array Lean.Name := #[ - ``Ix.Tc.KernelSuffixModel, - ``Ix.Tc.ScopedKernelSuffixModel.toKernelSuffixModel, - ``Ix.Tc.PropositionClassifierContext, - ``Ix.Tc.RecursiveMethodRunContext, - ``Ix.Tc.TcM.whnf.wf_legacy, - ``Ix.Tc.TcM.infer.wf_legacy, - ``Ix.Tc.TcM.isDefEq.wf_legacy, - ``Ix.Tc.TcM.checkConst.wf_legacy + ``Ix.Kernel.KernelSuffixModel, + ``Ix.Kernel.ScopedKernelSuffixModel.toKernelSuffixModel, + ``Ix.Kernel.PropositionClassifierContext, + ``Ix.Kernel.RecursiveMethodRunContext, + ``Ix.Kernel.TcM.whnf.wf_legacy, + ``Ix.Kernel.TcM.infer.wf_legacy, + ``Ix.Kernel.TcM.isDefEq.wf_legacy, + ``Ix.Kernel.TcM.checkConst.wf_legacy ] private def scopedK2SForbiddenDependencies : Array Lean.Name := @@ -3119,73 +3105,73 @@ set_option maxRecDepth 100000 private def roots : Array RootAllowance := #[ -- Level decision procedures. - { root := ``Ix.Tc.univEq_sound, standardAxioms := standard }, - { root := ``Ix.Tc.univGeq_sound, standardAxioms := standard }, + { root := ``Ix.Kernel.univEq_sound, standardAxioms := standard }, + { root := ``Ix.Kernel.univGeq_sound, standardAxioms := standard }, -- Memoized expression walkers against their pure specifications. - { root := ``Ix.Tc.lift_spec, + { root := ``Ix.Kernel.lift_spec, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.subst_spec, + { root := ``Ix.Kernel.subst_spec, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.simulSubst_spec, + { root := ``Ix.Kernel.simulSubst_spec, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.instantiateRev_spec, + { root := ``Ix.Kernel.instantiateRev_spec, standardAxioms := standard, nativeAxioms := expressionNative }, -- There is not yet an API-level `abstractFVars_spec`; protect the current -- walker master until that final wrapper replaces it. - { root := ``Ix.Tc.abstractFVarsCached_spec, + { root := ``Ix.Kernel.abstractFVarsCached_spec, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.TcM.instantiateUnivParams_wf, + { root := ``Ix.Kernel.TcM.instantiateUnivParams_wf, standardAxioms := standard, nativeAxioms := levelNative }, -- G3a finite run support and generated-term resource bounds. Universe -- instantiation can rebuild sorts/constants and therefore reaches the now -- total expression serializer's standard `UInt8` quotient implementation. - { root := ``Ix.Tc.KExpr.LiftReach.finite, + { root := ``Ix.Kernel.KExpr.LiftReach.finite, standardAxioms := standardWithoutQuot, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.KExpr.SubstReach.finite, + { root := ``Ix.Kernel.KExpr.SubstReach.finite, standardAxioms := standardWithoutQuot, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.KExpr.InstUnivReach.finite, + { root := ``Ix.Kernel.KExpr.InstUnivReach.finite, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.WalkerRequest.reach_finite, + { root := ``Ix.Kernel.WalkerRequest.reach_finite, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.InternTable.exprSupport_finite, + { root := ``Ix.Kernel.InternTable.exprSupport_finite, standardAxioms := standard }, - { root := ``Ix.Tc.RunSupport.collisionFree_of_le, + { root := ``Ix.Kernel.RunSupport.collisionFree_of_le, standardAxioms := propextOnly }, - { root := ``Ix.Tc.RunSupport.singleton_collisionFree, + { root := ``Ix.Kernel.RunSupport.singleton_collisionFree, standardAxioms := propextOnly }, - { root := ``Ix.Tc.WalkerRequest.Bounds.lift_result, + { root := ``Ix.Kernel.WalkerRequest.Bounds.lift_result, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.WalkerRequest.Bounds.subst_result, + { root := ``Ix.Kernel.WalkerRequest.Bounds.subst_result, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.CheckConstSupport.initial_support, + { root := ``Ix.Kernel.CheckConstSupport.initial_support, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.CheckConstSupport.lift, + { root := ``Ix.Kernel.CheckConstSupport.lift, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.CheckConstSupport.subst, + { root := ``Ix.Kernel.CheckConstSupport.subst, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.CheckConstSupport.instUniv, + { root := ``Ix.Kernel.CheckConstSupport.instUniv, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.CheckConstSupport.mono, + { root := ``Ix.Kernel.CheckConstSupport.mono, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.CheckConstSupport.scope, + { root := ``Ix.Kernel.CheckConstSupport.scope, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.ResourceBounds.mono, + { root := ``Ix.Kernel.ResourceBounds.mono, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.AmbientNat.checkSupport, + { root := ``Ix.Kernel.AmbientNat.checkSupport, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.resourceBounds, + { root := ``Ix.Kernel.AmbientNat.resourceBounds, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.supportAcceptance, + { root := ``Ix.Kernel.AmbientNat.supportAcceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, @@ -3193,310 +3179,310 @@ private def roots : Array RootAllowance := #[ -- ties the exact finite request list to an actual TcM computation. The -- simultaneous/reverse instantiation specs can likewise rebuild serialized -- expressions and inherit the same standard quotient footprint. - { root := ``Ix.Tc.KExpr.SimulSubstReach.finite, + { root := ``Ix.Kernel.KExpr.SimulSubstReach.finite, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.KExpr.InstRevReach.finite, + { root := ``Ix.Kernel.KExpr.InstRevReach.finite, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.KExpr.AbstractReach.finite, + { root := ``Ix.Kernel.KExpr.AbstractReach.finite, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.WalkerRequest.univReach_finite, + { root := ``Ix.Kernel.WalkerRequest.univReach_finite, standardAxioms := propextOnly }, - { root := ``Ix.Tc.InternTable.univSupport_finite, + { root := ``Ix.Kernel.InternTable.univSupport_finite, standardAxioms := standard }, - { root := ``Ix.Tc.RunSupport.pair_collisionFree, + { root := ``Ix.Kernel.RunSupport.pair_collisionFree, standardAxioms := propextOnly }, - { root := ``Ix.Tc.WalkerRequest.Bounds.simulSubst_result, + { root := ``Ix.Kernel.WalkerRequest.Bounds.simulSubst_result, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.WalkerRequest.Bounds.instRev_result, + { root := ``Ix.Kernel.WalkerRequest.Bounds.instRev_result, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.WalkerRequest.Bounds.abstractFVars_result, + { root := ``Ix.Kernel.WalkerRequest.Bounds.abstractFVars_result, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.abstractFVars_eq, + { root := ``Ix.Kernel.abstractFVars_eq, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.InternPreservesUnivs.pure, + { root := ``Ix.Kernel.InternPreservesUnivs.pure, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.InternPreservesUnivs.runWalk, + { root := ``Ix.Kernel.InternPreservesUnivs.runWalk, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.WalkPreservesUnivs.pure, + { root := ``Ix.Kernel.WalkPreservesUnivs.pure, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.WalkPreservesUnivs.bind, + { root := ``Ix.Kernel.WalkPreservesUnivs.bind, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.WalkPreservesUnivs.scratchGet, + { root := ``Ix.Kernel.WalkPreservesUnivs.scratchGet, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.WalkPreservesUnivs.scratchInsert, + { root := ``Ix.Kernel.WalkPreservesUnivs.scratchInsert, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.WalkPreservesUnivs.liftIntern, + { root := ``Ix.Kernel.WalkPreservesUnivs.liftIntern, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.WalkPreservesUnivs.internExpr, + { root := ``Ix.Kernel.WalkPreservesUnivs.internExpr, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.lift_preservesUnivs, + { root := ``Ix.Kernel.lift_preservesUnivs, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.subst_preservesUnivs, + { root := ``Ix.Kernel.subst_preservesUnivs, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.simulSubst_preservesUnivs, + { root := ``Ix.Kernel.simulSubst_preservesUnivs, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.instantiateRev_preservesUnivs, + { root := ``Ix.Kernel.instantiateRev_preservesUnivs, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.abstractFVars_preservesUnivs, + { root := ``Ix.Kernel.abstractFVars_preservesUnivs, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.WalkerRequest.Bounds.abstractFVarsCached_result, + { root := ``Ix.Kernel.WalkerRequest.Bounds.abstractFVarsCached_result, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.CheckConstSupport.initial_univ_support, + { root := ``Ix.Kernel.CheckConstSupport.initial_univ_support, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.CheckConstSupport.internExpr, + { root := ``Ix.Kernel.CheckConstSupport.internExpr, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.CheckConstSupport.internUniv, + { root := ``Ix.Kernel.CheckConstSupport.internUniv, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.CheckConstSupport.simulSubst, + { root := ``Ix.Kernel.CheckConstSupport.simulSubst, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.CheckConstSupport.instRev, + { root := ``Ix.Kernel.CheckConstSupport.instRev, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.CheckConstSupport.abstractFVars, + { root := ``Ix.Kernel.CheckConstSupport.abstractFVars, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.ExecutionRequests.bind, - standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.ExecutionRequests.tryCatch, - standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.ExecutionRequests.runRec, + { root := ``Ix.Kernel.ExecutionRequests.bind, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.ExecutionRequests.isolateCheckErrors, - standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.ExecutionRequests.modify, + { root := ``Ix.Kernel.ExecutionRequests.tryCatch, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.ExecutionRequests.weaken, - standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.ExecutionRequests.of_eq, - standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.ExecutionRequests.intern_eq_of_nil, + { root := ``Ix.Kernel.ExecutionRequests.runRec, + standardAxioms := standard, nativeAxioms := inferNative }, + { root := ``Ix.Kernel.ExecutionRequests.isolateCheckErrors, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RunAssumptions.initial, + { root := ``Ix.Kernel.ExecutionRequests.modify, + standardAxioms := standard, nativeAxioms := inferNative }, + { root := ``Ix.Kernel.ExecutionRequests.weaken, + standardAxioms := standard, nativeAxioms := inferNative }, + { root := ``Ix.Kernel.ExecutionRequests.of_eq, + standardAxioms := standard, nativeAxioms := inferNative }, + { root := ``Ix.Kernel.ExecutionRequests.intern_eq_of_nil, + standardAxioms := standard, nativeAxioms := inferNative }, + { root := ``Ix.Kernel.RunAssumptions.initial, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunAssumptions.requestBounds, - standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RunAssumptions.internExpr_spec, + { root := ``Ix.Kernel.RunAssumptions.requestBounds, + standardAxioms := standard, nativeAxioms := inferNative }, + { root := ``Ix.Kernel.RunAssumptions.internExpr_spec, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunAssumptions.internUniv_spec, + { root := ``Ix.Kernel.RunAssumptions.internUniv_spec, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunSupport.CoversIntern.of_expr_univs, + { root := ``Ix.Kernel.RunSupport.CoversIntern.of_expr_univs, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RunAssumptions.lift_spec, + { root := ``Ix.Kernel.RunAssumptions.lift_spec, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunAssumptions.subst_spec, + { root := ``Ix.Kernel.RunAssumptions.subst_spec, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunAssumptions.simulSubst_spec, + { root := ``Ix.Kernel.RunAssumptions.simulSubst_spec, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunAssumptions.instRev_spec, + { root := ``Ix.Kernel.RunAssumptions.instRev_spec, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunAssumptions.abstractFVarsCached_spec, + { root := ``Ix.Kernel.RunAssumptions.abstractFVarsCached_spec, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunAssumptions.abstractFVars_spec, + { root := ``Ix.Kernel.RunAssumptions.abstractFVars_spec, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunAssumptions.instantiateUnivParams_wf, + { root := ``Ix.Kernel.RunAssumptions.instantiateUnivParams_wf, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunAssumptions.runIntern_supported_wf, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.RunAssumptions.lift_wf, + { root := ``Ix.Kernel.RunAssumptions.runIntern_supported_wf, + standardAxioms := standard }, + { root := ``Ix.Kernel.RunAssumptions.lift_wf, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunAssumptions.subst_wf, + { root := ``Ix.Kernel.RunAssumptions.subst_wf, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunAssumptions.simulSubst_wf, + { root := ``Ix.Kernel.RunAssumptions.simulSubst_wf, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunAssumptions.instRev_wf, + { root := ``Ix.Kernel.RunAssumptions.instRev_wf, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunAssumptions.abstractFVars_wf, + { root := ``Ix.Kernel.RunAssumptions.abstractFVars_wf, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunAssumptions.instUniv_wf, + { root := ``Ix.Kernel.RunAssumptions.instUniv_wf, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.AmbientNat.supportExecution, + { root := ``Ix.Kernel.AmbientNat.supportExecution, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.runAssumptions, + { root := ``Ix.Kernel.AmbientNat.runAssumptions, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- Expression translation, typing, uniqueness, and defeq bridges. - { root := ``Ix.Tc.TrKExprS.instL, + { root := ``Ix.Kernel.TrKExprS.instL, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.TrKExprS.inst, + { root := ``Ix.Kernel.TrKExprS.inst, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.TrKExprS.inst_let, + { root := ``Ix.Kernel.TrKExprS.inst_let, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.TrKExprS.inst_let_lbr, + { root := ``Ix.Kernel.TrKExprS.inst_let_lbr, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.TrKExprS.wf, standardAxioms := standard }, - { root := ``Ix.Tc.TrKExpr.wf, standardAxioms := standard }, - { root := ``Ix.Tc.TrKExprS.uniq, + { root := ``Ix.Kernel.TrKExprS.wf, standardAxioms := standard }, + { root := ``Ix.Kernel.TrKExpr.wf, standardAxioms := standard }, + { root := ``Ix.Kernel.TrKExprS.uniq, standardAxioms := standard, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.TrKExprS.defeqDFC, + { root := ``Ix.Kernel.TrKExprS.defeqDFC, standardAxioms := standard, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.TrKExpr.defeq, + { root := ``Ix.Kernel.TrKExpr.defeq, standardAxioms := standard, sorryOrigins := typingDebt }, -- Legacy whole-environment compatibility interfaces. G2b consumer roots -- below are forbidden from depending on these declarations. - { root := ``Ix.Tc.TrKEnv.wf, + { root := ``Ix.Kernel.TrKEnv.wf, standardAxioms := standard }, - { root := ``Ix.Tc.TrKEnv.find?, + { root := ``Ix.Kernel.TrKEnv.find?, standardAxioms := standard }, - { root := ``Ix.Tc.TcM.tick.tcInv, + { root := ``Ix.Kernel.TcM.tick.tcInv, standardAxioms := standard }, - { root := ``Ix.Tc.TcM.instantiateUnivParams.tcInv, + { root := ``Ix.Kernel.TcM.instantiateUnivParams.tcInv, standardAxioms := standard, nativeAxioms := levelNative }, -- The narrow upstream-context dependency behind translation uniqueness. - { root := ``Ix.Tc.KVLCtx.IsDefEq.find?_uniq, + { root := ``Ix.Kernel.KVLCtx.IsDefEq.find?_uniq, standardAxioms := standard }, -- Dual-context reconciliation entry points used by the checker proofs. - { root := ``Ix.Tc.CtxRecon.wf, standardAxioms := standard }, - { root := ``Ix.Tc.CtxRecon.lookupVar, + { root := ``Ix.Kernel.CtxRecon.wf, standardAxioms := standard }, + { root := ``Ix.Kernel.CtxRecon.lookupVar, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.CtxRecon.fvar_resolves, + { root := ``Ix.Kernel.CtxRecon.fvar_resolves, standardAxioms := standard }, -- G1a's non-circular world and one-way lazy-load boundary. - { root := ``Ix.Tc.VerifyWorld.ofCatalog_catalogued_not_trusted, + { root := ``Ix.Kernel.VerifyWorld.ofCatalog_catalogued_not_trusted, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.VerifyWorld.LE.trans, + { root := ``Ix.Kernel.VerifyWorld.LE.trans, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.VerifyWorld.LE.catalogued_iff, + { root := ``Ix.Kernel.VerifyWorld.LE.catalogued_iff, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.LoadedAgrees.world_iff, + { root := ``Ix.Kernel.LoadedAgrees.world_iff, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.LoadedAgrees.insert, + { root := ``Ix.Kernel.LoadedAgrees.insert, standardAxioms := standard }, - { root := ``Ix.Tc.LoadedAgrees.of_extension, + { root := ``Ix.Kernel.LoadedAgrees.of_extension, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.VerifyWorld.ofCatalog_loaded, + { root := ``Ix.Kernel.VerifyWorld.ofCatalog_loaded, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.VerifyWorld.ofCatalog_loaded_not_trusted, + { root := ``Ix.Kernel.VerifyWorld.ofCatalog_loaded_not_trusted, standardAxioms := standard }, -- G1b's raw/pending boundary. Raw correspondence has no declaration-WF -- premise; the fixture roots pin the concrete non-WF pending case. - { root := ``Ix.Tc.RawExprRel.mono, + { root := ``Ix.Kernel.RawExprRel.mono, standardAxioms := standard }, - { root := ``Ix.Tc.RawExprRel.reference_resolved, + { root := ``Ix.Kernel.RawExprRel.reference_resolved, standardAxioms := standard }, - { root := ``Ix.Tc.RawDeclRel.mono, + { root := ``Ix.Kernel.RawDeclRel.mono, standardAxioms := standard }, - { root := ``Ix.Tc.PendingDecl.no_target_lookup, + { root := ``Ix.Kernel.PendingDecl.no_target_lookup, standardAxioms := standard }, - { root := ``Ix.Tc.PendingDecl.no_self_expr_reference, + { root := ``Ix.Kernel.PendingDecl.no_self_expr_reference, standardAxioms := standard }, - { root := ``Ix.Tc.PendingDecl.not_trustedDecl, + { root := ``Ix.Kernel.PendingDecl.not_trustedDecl, standardAxioms := standard }, - { root := ``Ix.Tc.IllTypedPending.pending_but_not_wf, + { root := ``Ix.Kernel.IllTypedPending.pending_but_not_wf, standardAxioms := standard }, - { root := ``Ix.Tc.IllTypedPending.loaded_pending_but_not_wf, + { root := ``Ix.Kernel.IllTypedPending.loaded_pending_but_not_wf, standardAxioms := standard }, -- G1c's trusted-only catalog log and explicit-WF promotion boundary. - { root := ``Ix.Tc.RawDeclRel.wf_le, + { root := ``Ix.Kernel.RawDeclRel.wf_le, standardAxioms := standard }, - { root := ``Ix.Tc.TrustedCatalogLog.wf, + { root := ``Ix.Kernel.TrustedCatalogLog.wf, standardAxioms := standard }, - { root := ``Ix.Tc.TrustedCatalogLog.catalogued, + { root := ``Ix.Kernel.TrustedCatalogLog.catalogued, standardAxioms := standard }, - { root := ``Ix.Tc.TrustedCatalogLog.find, + { root := ``Ix.Kernel.TrustedCatalogLog.find, standardAxioms := standard }, - { root := ``Ix.Tc.TrustedCatalogRel.ofCatalog, + { root := ``Ix.Kernel.TrustedCatalogRel.ofCatalog, standardAxioms := standard }, - { root := ``Ix.Tc.TrustedCatalogRel.find, + { root := ``Ix.Kernel.TrustedCatalogRel.find, standardAxioms := standard }, - { root := ``Ix.Tc.TrustedCatalogEntry.recursorRule, + { root := ``Ix.Kernel.TrustedCatalogEntry.recursorRule, standardAxioms := standard }, - { root := ``Ix.Tc.TrustedCatalogRel.recursorRule, + { root := ``Ix.Kernel.TrustedCatalogRel.recursorRule, standardAxioms := standard }, - { root := ``Ix.Tc.TrustedCatalogEntry.recursorPattern, + { root := ``Ix.Kernel.TrustedCatalogEntry.recursorPattern, standardAxioms := standard }, - { root := ``Ix.Tc.TrustedCatalogRel.recursorPattern, + { root := ``Ix.Kernel.TrustedCatalogRel.recursorPattern, standardAxioms := standard }, - { root := ``Ix.Tc.TrustedDecl.lookup, + { root := ``Ix.Kernel.TrustedDecl.lookup, standardAxioms := standard }, - { root := ``Ix.Tc.TrustedCatalogRel.lookup, + { root := ``Ix.Kernel.TrustedCatalogRel.lookup, standardAxioms := standard }, - { root := ``Ix.Tc.Promotes.trans, + { root := ``Ix.Kernel.Promotes.trans, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.TrustedCatalogRel.promote, + { root := ``Ix.Kernel.TrustedCatalogRel.promote, standardAxioms := standard }, - { root := ``Ix.Tc.IllTypedPending.trustedCatalogRel, + { root := ``Ix.Kernel.IllTypedPending.trustedCatalogRel, standardAxioms := standard }, - { root := ``Ix.Tc.WellTypedPromotion.promotes, + { root := ``Ix.Kernel.WellTypedPromotion.promotes, standardAxioms := standard }, -- G1d's world-based concrete-state invariant. Loading stays -- representation-only, promotion requires a fresh WF witness, and the -- fixed-world Hoare roots pin no-promotion behavior on both outcomes. - { root := ``Ix.Tc.TcStateWF.of_consts_eq, + { root := ``Ix.Kernel.TcStateWF.of_consts_eq, standardAxioms := standard }, - { root := ``Ix.Tc.TcStateWF.load, + { root := ``Ix.Kernel.TcStateWF.load, standardAxioms := standard }, - { root := ``Ix.Tc.TcStateWF.promote, + { root := ``Ix.Kernel.TcStateWF.promote, standardAxioms := standard }, - { root := ``Ix.Tc.TcStateWF.find?, + { root := ``Ix.Kernel.TcStateWF.find?, standardAxioms := standard }, - { root := ``Ix.Tc.TcInv.find?, + { root := ``Ix.Kernel.TcInv.find?, standardAxioms := standard }, - { root := ``Ix.Tc.IllTypedPending.tcInv_pending_but_not_wf, + { root := ``Ix.Kernel.IllTypedPending.tcInv_pending_but_not_wf, standardAxioms := standard }, - { root := ``Ix.Tc.TcM.tick.tcStateWF, + { root := ``Ix.Kernel.TcM.tick.tcStateWF, standardAxioms := standard }, - { root := ``Ix.Tc.TcM.instantiateUnivParams.tcStateWF, + { root := ``Ix.Kernel.TcM.instantiateUnivParams.tcStateWF, standardAxioms := standard, nativeAxioms := levelNative }, - -- Pin A / E2a: the certified-generation adapter may use only Lean4Lean + -- Pin A / E2a: the certified-generation adapter may use only Ix.Theory.Named -- Theory transaction facts, never Ix checker/catalog/pattern authority. - { root := ``Ix.Tc.CertifiedGenerationTransaction.trace, + { root := ``Ix.Kernel.CertifiedGenerationTransaction.trace, standardAxioms := standardWithoutChoice, forbiddenDependencies := certificateAdapterForbidden }, - { root := ``Ix.Tc.CertifiedGenerationTransaction.afterWF, + { root := ``Ix.Kernel.CertifiedGenerationTransaction.afterWF, standardAxioms := standardWithoutChoice, forbiddenDependencies := certificateAdapterForbidden }, - { root := ``Ix.Tc.CertifiedGenerationTransaction.facts, + { root := ``Ix.Kernel.CertifiedGenerationTransaction.facts, standardAxioms := standardWithoutChoice, forbiddenDependencies := certificateAdapterForbidden }, - -- L4L-08's Theory-only block adapter preserves the same quarantine while + -- Spec-08's Theory-only block adapter preserves the same quarantine while -- exposing one atomic all-families/all-constructors/all-recursors/all-rules -- transaction rather than a sequence of singleton admissions. - { root := ``Ix.Tc.CertifiedBlockGenerationTransaction.trace, + { root := ``Ix.Kernel.CertifiedBlockGenerationTransaction.trace, standardAxioms := standardWithoutChoice, forbiddenDependencies := certificateAdapterForbidden }, - { root := ``Ix.Tc.CertifiedBlockGenerationTransaction.afterWF, + { root := ``Ix.Kernel.CertifiedBlockGenerationTransaction.afterWF, standardAxioms := standardWithoutChoice, forbiddenDependencies := certificateAdapterForbidden }, - { root := ``Ix.Tc.CertifiedBlockGenerationTransaction.facts, + { root := ``Ix.Kernel.CertifiedBlockGenerationTransaction.facts, standardAxioms := standardWithoutChoice, forbiddenDependencies := certificateAdapterForbidden }, - -- E2c retains the exact Lean4Lean candidate-producer equation alongside + -- E2c retains the exact Ix.Theory.Named candidate-producer equation alongside -- the certified Theory transaction. Unlike the Theory-only adapter above, -- this Verify-backed bridge deliberately inherits the pinned analyzer debt. - { root := ``Ix.Tc.ProducedGenerationTransaction.facts, + { root := ``Ix.Kernel.ProducedGenerationTransaction.facts, standardAxioms := standard, - upstreamAxioms := annotatedPiUpstreamAxioms, + implementationAxioms := annotatedPiUpstreamAxioms, sorryOrigins := annotatedPiUpstreamDebt, forbiddenDependencies := annotatedPiCertificateForbidden }, - { root := ``Ix.Tc.ExactProducedGenerationTransaction.facts, + { root := ``Ix.Kernel.ExactProducedGenerationTransaction.facts, standardAxioms := standard, - upstreamAxioms := annotatedPiUpstreamAxioms, + implementationAxioms := annotatedPiUpstreamAxioms, sorryOrigins := annotatedPiUpstreamDebt, forbiddenDependencies := annotatedPiCertificateForbidden }, -- First genuine multi-family semantic witness: Tree/TreeList has two -- motives and recursors, five flattened constructors/rules, sibling -- recursion in both directions, and one recursive occurrence below a Pi. - { root := ``Ix.Tc.MutualTreeCertificateFixture.breadth, + { root := ``Ix.Kernel.MutualTreeCertificateFixture.breadth, standardAxioms := standard, forbiddenDependencies := certificateAdapterForbidden }, - { root := ``Ix.Tc.MutualTreeCertificateFixture.certifiedFacts, + { root := ``Ix.Kernel.MutualTreeCertificateFixture.certifiedFacts, standardAxioms := standard, forbiddenDependencies := certificateAdapterForbidden }, - { root := ``Ix.Tc.MutualTreeCertificateFixture.finalEnvWF, + { root := ``Ix.Kernel.MutualTreeCertificateFixture.finalEnvWF, standardAxioms := standard, forbiddenDependencies := certificateAdapterForbidden }, @@ -3504,7 +3490,7 @@ private def roots : Array RootAllowance := #[ -- `TreeList, Tree`. All seven family/constructor entries are linked to the -- complete catalog and admitted atomically without the pending recursor -- pattern/WF witnesses used by the later conditional closure. - { root := ``Ix.Tc.MutualTreeFixture.mutualFamilyAtomicClosure, + { root := ``Ix.Kernel.MutualTreeFixture.mutualFamilyAtomicClosure, standardAxioms := standard, nativeAxioms := mutualFamilyNative }, @@ -3513,28 +3499,28 @@ private def roots : Array RootAllowance := #[ -- elimination, and both generated rules. It remains Theory-only here; -- production Ix catalog correspondence is audited in the later linkage. { root := - ``Ix.Tc.IndexedRecursiveCertificateFixture.transaction_generation, + ``Ix.Kernel.IndexedRecursiveCertificateFixture.transaction_generation, standardAxioms := standard, forbiddenDependencies := certificateAdapterForbidden }, - { root := ``Ix.Tc.IndexedRecursiveCertificateFixture.breadth, + { root := ``Ix.Kernel.IndexedRecursiveCertificateFixture.breadth, standardAxioms := standard, forbiddenDependencies := certificateAdapterForbidden }, - { root := ``Ix.Tc.IndexedRecursiveCertificateFixture.certifiedFacts, + { root := ``Ix.Kernel.IndexedRecursiveCertificateFixture.certifiedFacts, standardAxioms := standard, forbiddenDependencies := certificateAdapterForbidden }, - { root := ``Ix.Tc.IndexedRecursiveCertificateFixture.producedCertificate_eq, + { root := ``Ix.Kernel.IndexedRecursiveCertificateFixture.producedCertificate_eq, standardAxioms := standard, - upstreamAxioms := annotatedPiUpstreamAxioms, + implementationAxioms := annotatedPiUpstreamAxioms, sorryOrigins := annotatedPiUpstreamDebt, forbiddenDependencies := annotatedPiCertificateForbidden }, - { root := ``Ix.Tc.IndexedRecursiveCertificateFixture.producedToCertified_eq, + { root := ``Ix.Kernel.IndexedRecursiveCertificateFixture.producedToCertified_eq, standardAxioms := standard, - upstreamAxioms := annotatedPiUpstreamAxioms, + implementationAxioms := annotatedPiUpstreamAxioms, sorryOrigins := annotatedPiUpstreamDebt, forbiddenDependencies := annotatedPiCertificateForbidden }, - { root := ``Ix.Tc.IndexedRecursiveCertificateFixture.producerLinkedFacts, + { root := ``Ix.Kernel.IndexedRecursiveCertificateFixture.producerLinkedFacts, standardAxioms := standard, - upstreamAxioms := annotatedPiUpstreamAxioms, + implementationAxioms := annotatedPiUpstreamAxioms, sorryOrigins := annotatedPiUpstreamDebt, forbiddenDependencies := annotatedPiCertificateForbidden }, @@ -3544,13 +3530,13 @@ private def roots : Array RootAllowance := #[ -- IndexedVec certificate, these roots remain entirely on the Theory side -- of the catalog/checker boundary. { root := - ``Ix.Tc.RecursivePiCertificateFixture.transaction_generation, + ``Ix.Kernel.RecursivePiCertificateFixture.transaction_generation, standardAxioms := standard, forbiddenDependencies := certificateAdapterForbidden }, - { root := ``Ix.Tc.RecursivePiCertificateFixture.breadth, + { root := ``Ix.Kernel.RecursivePiCertificateFixture.breadth, standardAxioms := standard, forbiddenDependencies := certificateAdapterForbidden }, - { root := ``Ix.Tc.RecursivePiCertificateFixture.certifiedFacts, + { root := ``Ix.Kernel.RecursivePiCertificateFixture.certifiedFacts, standardAxioms := standard, forbiddenDependencies := certificateAdapterForbidden }, @@ -3559,23 +3545,23 @@ private def roots : Array RootAllowance := #[ -- `outParam Prop`, while the analyzer-owned candidate exposes `Prop`. -- These certificate roots remain entirely on the Theory side. { root := - ``Ix.Tc.AnnotatedPiCertificateFixture.transaction_generation, + ``Ix.Kernel.AnnotatedPiCertificateFixture.transaction_generation, standardAxioms := standard, - upstreamAxioms := annotatedPiUpstreamAxioms, + implementationAxioms := annotatedPiUpstreamAxioms, sorryOrigins := annotatedPiUpstreamDebt, forbiddenDependencies := annotatedPiCertificateForbidden }, - { root := ``Ix.Tc.AnnotatedPiCertificateFixture.breadth, + { root := ``Ix.Kernel.AnnotatedPiCertificateFixture.breadth, standardAxioms := standardWithoutChoice, nativeAxioms := annotatedPiCertificateBreadthNative, forbiddenDependencies := annotatedPiCertificateForbidden }, - { root := ``Ix.Tc.AnnotatedPiCertificateFixture.certifiedFacts, + { root := ``Ix.Kernel.AnnotatedPiCertificateFixture.certifiedFacts, standardAxioms := standard, - upstreamAxioms := annotatedPiUpstreamAxioms, + implementationAxioms := annotatedPiUpstreamAxioms, sorryOrigins := annotatedPiUpstreamDebt, forbiddenDependencies := annotatedPiCertificateForbidden }, - { root := ``Ix.Tc.AnnotatedPiCertificateFixture.producerLinkedFacts, + { root := ``Ix.Kernel.AnnotatedPiCertificateFixture.producerLinkedFacts, standardAxioms := standard, - upstreamAxioms := annotatedPiUpstreamAxioms, + implementationAxioms := annotatedPiUpstreamAxioms, sorryOrigins := annotatedPiUpstreamDebt, forbiddenDependencies := annotatedPiCertificateForbidden }, @@ -3584,23 +3570,23 @@ private def roots : Array RootAllowance := #[ -- `Type`. These roots certify the non-identity family-result view without -- Ix catalog, checker-pattern, or oracle authority. { root := - ``Ix.Tc.AliasFormerCertificateFixture.transaction_generation, + ``Ix.Kernel.AliasFormerCertificateFixture.transaction_generation, standardAxioms := standard, - upstreamAxioms := aliasFormerUpstreamAxioms, + implementationAxioms := aliasFormerUpstreamAxioms, sorryOrigins := aliasFormerUpstreamDebt, forbiddenDependencies := annotatedPiCertificateForbidden }, - { root := ``Ix.Tc.AliasFormerCertificateFixture.breadth, + { root := ``Ix.Kernel.AliasFormerCertificateFixture.breadth, standardAxioms := standardWithoutChoice, nativeAxioms := aliasFormerCertificateBreadthNative, forbiddenDependencies := annotatedPiCertificateForbidden }, - { root := ``Ix.Tc.AliasFormerCertificateFixture.certifiedFacts, + { root := ``Ix.Kernel.AliasFormerCertificateFixture.certifiedFacts, standardAxioms := standard, - upstreamAxioms := aliasFormerUpstreamAxioms, + implementationAxioms := aliasFormerUpstreamAxioms, sorryOrigins := aliasFormerUpstreamDebt, forbiddenDependencies := annotatedPiCertificateForbidden }, - { root := ``Ix.Tc.AliasFormerCertificateFixture.producerLinkedFacts, + { root := ``Ix.Kernel.AliasFormerCertificateFixture.producerLinkedFacts, standardAxioms := standard, - upstreamAxioms := aliasFormerUpstreamAxioms, + implementationAxioms := aliasFormerUpstreamAxioms, sorryOrigins := aliasFormerUpstreamDebt, forbiddenDependencies := annotatedPiCertificateForbidden }, @@ -3608,20 +3594,20 @@ private def roots : Array RootAllowance := #[ -- certifying the direct-recursive checked field. The adapter packages the -- pinned upstream generation/WF replay without Ix-side semantic authority. { root := - ``Ix.Tc.AliasRecCertificateFixture.transaction_generation, + ``Ix.Kernel.AliasRecCertificateFixture.transaction_generation, standardAxioms := standard, - upstreamAxioms := aliasRecUpstreamAxioms, + implementationAxioms := aliasRecUpstreamAxioms, sorryOrigins := aliasRecUpstreamDebt, forbiddenDependencies := annotatedPiCertificateForbidden }, - { root := ``Ix.Tc.AliasRecCertificateFixture.breadth, + { root := ``Ix.Kernel.AliasRecCertificateFixture.breadth, standardAxioms := standard, - upstreamAxioms := aliasRecUpstreamAxioms, + implementationAxioms := aliasRecUpstreamAxioms, nativeAxioms := aliasRecCertificateBreadthNative, sorryOrigins := aliasRecUpstreamDebt, forbiddenDependencies := annotatedPiCertificateForbidden }, - { root := ``Ix.Tc.AliasRecCertificateFixture.certifiedFacts, + { root := ``Ix.Kernel.AliasRecCertificateFixture.certifiedFacts, standardAxioms := standard, - upstreamAxioms := aliasRecUpstreamAxioms, + implementationAxioms := aliasRecUpstreamAxioms, sorryOrigins := aliasRecUpstreamDebt, forbiddenDependencies := annotatedPiCertificateForbidden }, @@ -3629,40 +3615,40 @@ private def roots : Array RootAllowance := #[ -- family and strengthen every production guard into the elementwise -- valid-inductive-application invariant, without oracle authority. { root := - ``Ix.Tc.RecM.checkPositiveRecursiveApplicationPreconditions_success_iff, + ``Ix.Kernel.RecM.checkPositiveRecursiveApplicationPreconditions_success_iff, standardAxioms := standard, nativeAxioms := occurrenceValidationNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.positiveUniverseArgumentsAgree_eq_true_iff, + { root := ``Ix.Kernel.RecM.positiveUniverseArgumentsAgree_eq_true_iff, standardAxioms := standard, nativeAxioms := occurrenceValidationNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.positiveIndicesIndependent_eq_true_iff, + { root := ``Ix.Kernel.RecM.positiveIndicesIndependent_eq_true_iff, standardAxioms := standard, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkPositiveParametersFrom_success, + { root := ``Ix.Kernel.RecM.checkPositiveParametersFrom_success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkPositiveParameters_success, + { root := ``Ix.Kernel.RecM.checkPositiveParameters_success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.PositiveParameterComparisonTrace.sound, + { root := ``Ix.Kernel.RecM.PositiveParameterComparisonTrace.sound, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.PositiveParameterComparisonTrace.theoryDefEq, + { root := ``Ix.Kernel.RecM.PositiveParameterComparisonTrace.theoryDefEq, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.RecM.ValidPositiveRecursiveApplicationHeader.theoryParameters, + ``Ix.Kernel.RecM.ValidPositiveRecursiveApplicationHeader.theoryParameters, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.RecM.PositiveParameterComparisonTrace.theoryDefEqScoped, + ``Ix.Kernel.RecM.PositiveParameterComparisonTrace.theoryDefEqScoped, standardAxioms := standard, nativeAxioms := inferNative, -- This is the K2S instantiation bridge, not the oracle-free occurrence @@ -3671,40 +3657,40 @@ private def roots : Array RootAllowance := #[ -- confined to the projected `ScopedWFAtOn.isDefEq` field. forbiddenDependencies := boundedKnotForbiddenDependencies }, { root := - ``Ix.Tc.RecM.ValidPositiveRecursiveApplicationHeader.theoryParametersScoped, + ``Ix.Kernel.RecM.ValidPositiveRecursiveApplicationHeader.theoryParametersScoped, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.positivityGroupMatches_eq_true_iff, + { root := ``Ix.Kernel.RecM.positivityGroupMatches_eq_true_iff, standardAxioms := standardWithoutChoice, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.SpecializationIdentityFixture.semanticUniverseEquality_does_not_collapse_specialization, + ``Ix.Kernel.SpecializationIdentityFixture.semanticUniverseEquality_does_not_collapse_specialization, standardAxioms := standard, nativeAxioms := specializationIdentityNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.RecM.checkPositiveRecursiveApplicationHeader_success, + ``Ix.Kernel.RecM.checkPositiveRecursiveApplicationHeader_success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.PositiveRecursiveApplicationHeaderTrace.valid, + { root := ``Ix.Kernel.RecM.PositiveRecursiveApplicationHeaderTrace.valid, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkPositiveRecursiveApplicationHeader_valid, + { root := ``Ix.Kernel.RecM.checkPositiveRecursiveApplicationHeader_valid, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkPositiveRecursiveApplication_success, + { root := ``Ix.Kernel.RecM.checkPositiveRecursiveApplication_success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.PositiveRecursiveApplicationTrace.valid, + { root := ``Ix.Kernel.RecM.PositiveRecursiveApplicationTrace.valid, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkPositiveRecursiveApplication_valid, + { root := ``Ix.Kernel.RecM.checkPositiveRecursiveApplication_valid, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, @@ -3713,31 +3699,31 @@ private def roots : Array RootAllowance := #[ -- direct recursive-family applications inherit the oracle-free occurrence -- invariant; and forall success exposes the decremented recursive run plus -- exact local-context restoration. - { root := ``Ix.Tc.RecM.withLctxRestoration_success, + { root := ``Ix.Kernel.RecM.withLctxRestoration_success, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkPositivityDomainFuel_rootFree, + { root := ``Ix.Kernel.RecM.checkPositivityDomainFuel_rootFree, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkPositivityDomainFuel_direct, + { root := ``Ix.Kernel.RecM.checkPositivityDomainFuel_direct, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkPositivityDomainFuel_direct_valid, + { root := ``Ix.Kernel.RecM.checkPositivityDomainFuel_direct_valid, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkPositivityDomainFuel_nested, + { root := ``Ix.Kernel.RecM.checkPositivityDomainFuel_nested, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkPositivityDomainFuel_forall_success, + { root := ``Ix.Kernel.RecM.checkPositivityDomainFuel_forall_success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkPositivityDomainFuel_forall_negative, + { root := ``Ix.Kernel.RecM.checkPositivityDomainFuel_forall_negative, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, @@ -3747,79 +3733,79 @@ private def roots : Array RootAllowance := #[ -- traversal, universe instantiation, parameter stripping/substitution, -- recursive field-domain checks, and context restoration. The final root -- classifies every successful production domain without a branch oracle. - { root := ``Ix.Tc.RecM.findNestedPositivityGroup?_some, + { root := ``Ix.Kernel.RecM.findNestedPositivityGroup?_some, standardAxioms := standardWithoutChoice, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.RecM.checkNestedPositivityApplicationPreconditions_success_iff, + ``Ix.Kernel.RecM.checkNestedPositivityApplicationPreconditions_success_iff, standardAxioms := standardWithoutChoice, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.RecM.checkNestedPositivityApplicationResolvedFuel_success, + ``Ix.Kernel.RecM.checkNestedPositivityApplicationResolvedFuel_success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.RecM.checkNestedPositivityApplicationCheckedFuel_success, + ``Ix.Kernel.RecM.checkNestedPositivityApplicationCheckedFuel_success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkNestedConstructorFuel_success, + { root := ``Ix.Kernel.RecM.checkNestedConstructorFuel_success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkNestedConstructorsFuel_success, + { root := ``Ix.Kernel.RecM.checkNestedConstructorsFuel_success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.RecM.checkFreshNestedPositivityApplicationFuel_success, + ``Ix.Kernel.RecM.checkFreshNestedPositivityApplicationFuel_success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.stripNestedCtorParameters_success, + { root := ``Ix.Kernel.RecM.stripNestedCtorParameters_success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkNestedCtorFieldsLoopFuel_success, + { root := ``Ix.Kernel.RecM.checkNestedCtorFieldsLoopFuel_success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkNestedCtorFieldsFuel_success, + { root := ``Ix.Kernel.RecM.checkNestedCtorFieldsFuel_success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.completeNestedConstructor_of_trace, + { root := ``Ix.Kernel.RecM.completeNestedConstructor_of_trace, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.completeNestedConstructorList_of_trace, + { root := ``Ix.Kernel.RecM.completeNestedConstructorList_of_trace, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.completeFreshNestedPositivity_of_trace, + { root := ``Ix.Kernel.RecM.completeFreshNestedPositivity_of_trace, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.completeNestedPositivityChecked_of_trace, + { root := ``Ix.Kernel.RecM.completeNestedPositivityChecked_of_trace, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.completeNestedPositivityResolved_of_trace, + { root := ``Ix.Kernel.RecM.completeNestedPositivityResolved_of_trace, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.RecM.checkNestedPositivityApplicationFuel_success, + ``Ix.Kernel.RecM.checkNestedPositivityApplicationFuel_success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.RecM.checkNestedPositivityApplicationFuel_complete, + ``Ix.Kernel.RecM.checkNestedPositivityApplicationFuel_complete, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkPositivityDomainFuel_success, + { root := ``Ix.Kernel.RecM.checkPositivityDomainFuel_success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, @@ -3831,121 +3817,121 @@ private def roots : Array RootAllowance := #[ -- real public builder returns an aligned, duplicate-free physical/key list. -- The next fixture must identify its positivity request with one detector -- call; it cannot replace that reachability evidence with DefEq. - { root := ``Ix.Tc.lawfulBEqNestedSpecializationKey, + { root := ``Ix.Kernel.lawfulBEqNestedSpecializationKey, standardAxioms := standard, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedAuxiliaryHeaderRel.key_eq, + { root := ``Ix.Kernel.NestedAuxiliaryHeaderRel.key_eq, standardAxioms := propextOnly, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedAuxiliaryHeaderRel.positivityFlatIdentity, + { root := ``Ix.Kernel.NestedAuxiliaryHeaderRel.positivityFlatIdentity, standardAxioms := standard, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedAuxiliaryAppendTrace.member_mem, + { root := ``Ix.Kernel.NestedAuxiliaryAppendTrace.member_mem, standardAxioms := standard, nativeAxioms := occurrenceValidationNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedAuxiliaryAppendTrace.key_mem, + { root := ``Ix.Kernel.NestedAuxiliaryAppendTrace.key_mem, standardAxioms := standard, nativeAxioms := occurrenceValidationNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.appendNestedAuxiliary_fresh, + { root := ``Ix.Kernel.RecM.appendNestedAuxiliary_fresh, standardAxioms := standard, nativeAxioms := occurrenceValidationNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.appendNestedAuxiliary_existing, + { root := ``Ix.Kernel.RecM.appendNestedAuxiliary_existing, standardAxioms := standard, nativeAxioms := occurrenceValidationNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.FlatAuxSeenSound.empty, + { root := ``Ix.Kernel.FlatAuxSeenSound.empty, standardAxioms := propextOnly, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.FlatAuxSeenSound.push, + { root := ``Ix.Kernel.FlatAuxSeenSound.push, standardAxioms := propextOnly, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.FlatAuxTransition.seenSound, + { root := ``Ix.Kernel.FlatAuxTransition.seenSound, standardAxioms := propextOnly, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.FlatAuxTransition.flat_mem, + { root := ``Ix.Kernel.FlatAuxTransition.flat_mem, standardAxioms := propextOnly, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.FlatAuxTransition.key_mem, + { root := ``Ix.Kernel.FlatAuxTransition.key_mem, standardAxioms := propextOnly, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.FlatAuxHistory.single, + { root := ``Ix.Kernel.FlatAuxHistory.single, standardAxioms := propextOnly, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.FlatAuxHistory.trans, + { root := ``Ix.Kernel.FlatAuxHistory.trans, standardAxioms := propextOnly, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.FlatAuxHistory.seenSound, + { root := ``Ix.Kernel.FlatAuxHistory.seenSound, standardAxioms := propextOnly, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.FlatAuxHistory.flat_mem, + { root := ``Ix.Kernel.FlatAuxHistory.flat_mem, standardAxioms := propextOnly, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.FlatAuxHistory.key_mem, + { root := ``Ix.Kernel.FlatAuxHistory.key_mem, standardAxioms := propextOnly, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.FlatAuxQueueExact.empty, + { root := ``Ix.Kernel.FlatAuxQueueExact.empty, standardAxioms := propextOnly, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.FlatAuxQueueExact.pushOriginal, + { root := ``Ix.Kernel.FlatAuxQueueExact.pushOriginal, standardAxioms := propextOnly, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.FlatAuxQueueExact.transition, + { root := ``Ix.Kernel.FlatAuxQueueExact.transition, standardAxioms := standardWithoutChoice, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.FlatAuxQueueExact.history, + { root := ``Ix.Kernel.FlatAuxQueueExact.history, standardAxioms := standardWithoutChoice, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.appendNestedAuxiliary_transition, + { root := ``Ix.Kernel.RecM.appendNestedAuxiliary_transition, standardAxioms := standard, nativeAxioms := occurrenceValidationNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.appendNestedAuxiliary_seenSound, + { root := ``Ix.Kernel.RecM.appendNestedAuxiliary_seenSound, standardAxioms := standard, nativeAxioms := occurrenceValidationNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.tryDetectNestedCore_transition, + { root := ``Ix.Kernel.RecM.tryDetectNestedCore_transition, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.tryDetectNested_transition, + { root := ``Ix.Kernel.RecM.tryDetectNested_transition, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.tryDetectNested_seenSound, + { root := ``Ix.Kernel.RecM.tryDetectNested_seenSound, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.scanFlatConstructorFields_history, + { root := ``Ix.Kernel.RecM.scanFlatConstructorFields_history, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.scanFlatConstructor_history, + { root := ``Ix.Kernel.RecM.scanFlatConstructor_history, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.scanFlatConstructors_history, + { root := ``Ix.Kernel.RecM.scanFlatConstructors_history, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.buildFlatBlockQueueStep_history, + { root := ``Ix.Kernel.RecM.buildFlatBlockQueueStep_history, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.runBounded_flatAuxHistory, + { root := ``Ix.Kernel.RecM.runBounded_flatAuxHistory, standardAxioms := standardWithoutChoice, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.seedFlatBlockMembers_exact, + { root := ``Ix.Kernel.RecM.seedFlatBlockMembers_exact, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.buildFlatBlockWithAuxSeen_exact, + { root := ``Ix.Kernel.RecM.buildFlatBlockWithAuxSeen_exact, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.buildFlatBlock_auxiliaryOrder, + { root := ``Ix.Kernel.RecM.buildFlatBlock_auxiliaryOrder, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.CompleteNestedPositivityApplicationTrace.auxiliaryRequest, + ``Ix.Kernel.CompleteNestedPositivityApplicationTrace.auxiliaryRequest, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.CompleteNestedPositivityApplicationTrace.producedRequest, + ``Ix.Kernel.CompleteNestedPositivityApplicationTrace.producedRequest, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, @@ -3954,206 +3940,206 @@ private def roots : Array RootAllowance := #[ -- runs production ingress, positivity, and flat-block construction on the -- same `Box Tree` occurrence. Its headline root proves that the exact -- fresh positivity request is retained under the audited queue invariant. - { root := ``Ix.Tc.NestedRecursiveFixture.boxIngressRun, + { root := ``Ix.Kernel.NestedRecursiveFixture.boxIngressRun, standardAxioms := standard, nativeAxioms := levelNative.push (nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.boxIngressSucceededNative._native.native_decide.ax_1_1), + `Ix.Kernel.NestedRecursiveFixture.boxIngressSucceededNative._native.native_decide.ax_1_1), forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.treeIngressRun, + { root := ``Ix.Kernel.NestedRecursiveFixture.treeIngressRun, standardAxioms := standard, nativeAxioms := levelNative.push (nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.treeIngressSucceededNative._native.native_decide.ax_1_1), + `Ix.Kernel.NestedRecursiveFixture.treeIngressSucceededNative._native.native_decide.ax_1_1), forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.boxConcreteHeader, + { root := ``Ix.Kernel.NestedRecursiveFixture.boxConcreteHeader, standardAxioms := standard, nativeAxioms := levelNative.push (nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.boxConcreteHeaderMatchesNative._native.native_decide.ax_1_1), + `Ix.Kernel.NestedRecursiveFixture.boxConcreteHeaderMatchesNative._native.native_decide.ax_1_1), forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.nodeConcreteType, + { root := ``Ix.Kernel.NestedRecursiveFixture.nodeConcreteType, standardAxioms := standard, nativeAxioms := levelNative.push (nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nodeConcreteTypeNative._native.native_decide.ax_1_1), + `Ix.Kernel.NestedRecursiveFixture.nodeConcreteTypeNative._native.native_decide.ax_1_1), forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.positivityRun, + { root := ``Ix.Kernel.NestedRecursiveFixture.positivityRun, standardAxioms := standard, nativeAxioms := nameContextNative.push (nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.positivitySucceededNative._native.native_decide.ax_1_1), + `Ix.Kernel.NestedRecursiveFixture.positivitySucceededNative._native.native_decide.ax_1_1), forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.nestedWhnfRun, + { root := ``Ix.Kernel.NestedRecursiveFixture.nestedWhnfRun, standardAxioms := standard, nativeAxioms := nameContextNative.push (nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedWhnfSucceededNative._native.native_decide.ax_1_1), + `Ix.Kernel.NestedRecursiveFixture.nestedWhnfSucceededNative._native.native_decide.ax_1_1), forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.nestedWhnfResult_eq, + { root := ``Ix.Kernel.NestedRecursiveFixture.nestedWhnfResult_eq, standardAxioms := standard, nativeAxioms := nameContextNative.push (nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.nestedWhnfResultNative._native.native_decide.ax_1_1), + `Ix.Kernel.NestedRecursiveFixture.nestedWhnfResultNative._native.native_decide.ax_1_1), forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.nestedActionRun, + { root := ``Ix.Kernel.NestedRecursiveFixture.nestedActionRun, standardAxioms := standard, nativeAxioms := nestedRecursiveActionNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.requestHeaderRelation, + { root := ``Ix.Kernel.NestedRecursiveFixture.requestHeaderRelation, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.positivityCompleteTrace, + { root := ``Ix.Kernel.NestedRecursiveFixture.positivityCompleteTrace, standardAxioms := standard, nativeAxioms := nestedRecursiveActionNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.boxLookupRun, + { root := ``Ix.Kernel.NestedRecursiveFixture.boxLookupRun, standardAxioms := standard, nativeAxioms := nameContextNative.push (nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.boxLookupSucceededNative._native.native_decide.ax_1_1), + `Ix.Kernel.NestedRecursiveFixture.boxLookupSucceededNative._native.native_decide.ax_1_1), forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.boxLookupConcrete_eq, + { root := ``Ix.Kernel.NestedRecursiveFixture.boxLookupConcrete_eq, standardAxioms := standard, nativeAxioms := nameContextNative.push (nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.boxLookupConcreteNative._native.native_decide.ax_1_1), + `Ix.Kernel.NestedRecursiveFixture.boxLookupConcreteNative._native.native_decide.ax_1_1), forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.positivityRequestProduced, + { root := ``Ix.Kernel.NestedRecursiveFixture.positivityRequestProduced, standardAxioms := standard, nativeAxioms := nestedRecursiveProducedNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.positivityRequestFreshExpansion, + { root := ``Ix.Kernel.NestedRecursiveFixture.positivityRequestFreshExpansion, standardAxioms := standard, nativeAxioms := nestedRecursiveFreshNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.flatBuildRun, + { root := ``Ix.Kernel.NestedRecursiveFixture.flatBuildRun, standardAxioms := standard, nativeAxioms := nameContextNative.push (nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.flatBuildSucceededNative._native.native_decide.ax_1_1), + `Ix.Kernel.NestedRecursiveFixture.flatBuildSucceededNative._native.native_decide.ax_1_1), forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.builtFlatShape, + { root := ``Ix.Kernel.NestedRecursiveFixture.builtFlatShape, standardAxioms := standard, nativeAxioms := nameContextNative.push (nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.builtFlatShapeNative._native.native_decide.ax_1_1), + `Ix.Kernel.NestedRecursiveFixture.builtFlatShapeNative._native.native_decide.ax_1_1), forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.requestedAuxiliaryPresent, + { root := ``Ix.Kernel.NestedRecursiveFixture.requestedAuxiliaryPresent, standardAxioms := standard, nativeAxioms := nameContextNative.push (nestedRecursiveFixtureNativeAxiom - `Ix.Tc.NestedRecursiveFixture.builtFlatShapeNative._native.native_decide.ax_1_1), + `Ix.Kernel.NestedRecursiveFixture.builtFlatShapeNative._native.native_decide.ax_1_1), forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.nestedAuxiliaryReachability, + { root := ``Ix.Kernel.NestedRecursiveFixture.nestedAuxiliaryReachability, standardAxioms := standard, nativeAxioms := nestedRecursiveReachabilityNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - -- Exact Lean4Lean syntax and semantic transport for the retained nested + -- Exact Ix.Theory.Named syntax and semantic transport for the retained nested -- member. The outer field reaches the fresh auxiliary; the auxiliary's -- own field recursively reaches the original Tree member at lower fuel. - { root := ``Ix.Tc.NestedRecursiveFixture.treeCandidateSyntax, + { root := ``Ix.Kernel.NestedRecursiveFixture.treeCandidateSyntax, standardAxioms := standard, nativeAxioms := nestedTreeCandidateSyntaxNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.nestedAuxiliaryCandidateTarget, + { root := ``Ix.Kernel.NestedRecursiveFixture.nestedAuxiliaryCandidateTarget, standardAxioms := standard, nativeAxioms := nestedAuxiliaryCandidateTargetNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.nestedOuterPositivityTransport, + { root := ``Ix.Kernel.NestedRecursiveFixture.nestedOuterPositivityTransport, standardAxioms := standard, nativeAxioms := nestedOuterTransportNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.NestedRecursiveFixture.nestedOuterConstructorPositivityTrace, + ``Ix.Kernel.NestedRecursiveFixture.nestedOuterConstructorPositivityTrace, standardAxioms := standard, nativeAxioms := nestedOuterTransportNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.NestedRecursiveFixture.nestedAuxiliaryFieldProductionTrace, + ``Ix.Kernel.NestedRecursiveFixture.nestedAuxiliaryFieldProductionTrace, standardAxioms := standard, nativeAxioms := nestedAuxiliaryProductionNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.NestedRecursiveFixture.nestedAuxiliaryPositivityTransport, + ``Ix.Kernel.NestedRecursiveFixture.nestedAuxiliaryPositivityTransport, standardAxioms := standard, nativeAxioms := nestedAuxiliaryTransportNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.NestedRecursiveFixture.nestedAuxiliaryFieldProductionTraceAt, + ``Ix.Kernel.NestedRecursiveFixture.nestedAuxiliaryFieldProductionTraceAt, standardAxioms := standard, nativeAxioms := nestedAuxiliaryProductionNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.NestedRecursiveFixture.nestedAuxiliaryConstructorPositivityTraceAt, + ``Ix.Kernel.NestedRecursiveFixture.nestedAuxiliaryConstructorPositivityTraceAt, standardAxioms := standard, nativeAxioms := nestedAuxiliaryConstructorNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.NestedRecursiveFixture.nestedAuxiliaryConstructorPositivityTrace, + ``Ix.Kernel.NestedRecursiveFixture.nestedAuxiliaryConstructorPositivityTrace, standardAxioms := standard, nativeAxioms := nestedAuxiliaryConstructorNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.NestedRecursiveFixture.leanFlatNodeConstructorTypeValidationTrace, + ``Ix.Kernel.NestedRecursiveFixture.leanFlatNodeConstructorTypeValidationTrace, standardAxioms := standard, nativeAxioms := nestedNodeConstructorValidationNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.NestedRecursiveFixture.leanFlatNodeConstructorValidationRun, + ``Ix.Kernel.NestedRecursiveFixture.leanFlatNodeConstructorValidationRun, standardAxioms := standard, nativeAxioms := nestedNodeConstructorValidationNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.NestedRecursiveFixture.leanFlatWrapConstructorTypeValidationTrace, + ``Ix.Kernel.NestedRecursiveFixture.leanFlatWrapConstructorTypeValidationTrace, standardAxioms := standard, nativeAxioms := nestedWrapConstructorValidationNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.NestedRecursiveFixture.leanFlatWrapConstructorValidationRun, + ``Ix.Kernel.NestedRecursiveFixture.leanFlatWrapConstructorValidationRun, standardAxioms := standard, nativeAxioms := nestedWrapConstructorValidationNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, -- Completed nested semantic transaction. The generic adapter consumes a - -- pinned Lean4Lean `NestedBlockCertificate`; the concrete roots then prove + -- local named-specification `NestedBlockCertificate`; the concrete roots then prove -- restored source/recursor/rule well-formedness, run Ix's real nested -- family checker, and admit the exact two-member source block atomically. -- No auxiliary flattening name, legacy inductive oracle, or pending axiom -- may enter this completed boundary. - { root := ``Ix.Tc.NestedFamilyCatalogLink.translateMember, + { root := ``Ix.Kernel.NestedFamilyCatalogLink.translateMember, standardAxioms := standard, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.NestedFamilyCatalogLink.semanticEntry, + { root := ``Ix.Kernel.NestedFamilyCatalogLink.semanticEntry, standardAxioms := standard, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.NestedFamilyCatalogLink.transition, + { root := ``Ix.Kernel.NestedFamilyCatalogLink.transition, standardAxioms := standard, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.semanticTreeCertificate, + { root := ``Ix.Kernel.NestedRecursiveFixture.semanticTreeCertificate, standardAxioms := standard, nativeAxioms := nestedSemanticCertificateNative, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.semanticTreeTransactionFacts, + { root := ``Ix.Kernel.NestedRecursiveFixture.semanticTreeTransactionFacts, standardAxioms := standard, nativeAxioms := nestedSemanticFactsNative, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.nestedFamilyKernelRun, + { root := ``Ix.Kernel.NestedRecursiveFixture.nestedFamilyKernelRun, standardAxioms := standard, nativeAxioms := nestedFamilyKernelNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.nestedFamilyBlockCertificate, + { root := ``Ix.Kernel.NestedRecursiveFixture.nestedFamilyBlockCertificate, standardAxioms := standard, nativeAxioms := nestedFamilyCertificateNative, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.nestedFamilyAtomicAdmission, + { root := ``Ix.Kernel.NestedRecursiveFixture.nestedFamilyAtomicAdmission, standardAxioms := standard, nativeAxioms := nestedFamilyCertificateNative, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, { root := - ``Ix.Tc.NestedRecursiveFixture.nestedSemanticTransactionClosure, + ``Ix.Kernel.NestedRecursiveFixture.nestedSemanticTransactionClosure, standardAxioms := standard, nativeAxioms := nestedSemanticTransactionClosureNative, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, @@ -4162,40 +4148,40 @@ private def roots : Array RootAllowance := #[ -- ingress roots pin the actual generated block; the two pattern roots pin -- the restored node/wrap equations independently; the final roots require -- all-or-nothing family-plus-recursor admission. The only `sorryAx` - -- origins are two exact inversion lemmas inherited from Lean4Lean. - { root := ``Ix.Tc.NestedRecursiveFixture.nestedCompilerRun, + -- origins are two exact inversion lemmas inherited from Ix.Theory.Named. + { root := ``Ix.Kernel.NestedRecursiveFixture.nestedCompilerRun, standardAxioms := standard, nativeAxioms := nestedRecursorCompilerRunNative, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.nestedCompiledIdentityFacts, + { root := ``Ix.Kernel.NestedRecursiveFixture.nestedCompiledIdentityFacts, standardAxioms := standard, nativeAxioms := nestedRecursorCompilerIdentityNative, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.recursorIngressRun, + { root := ``Ix.Kernel.NestedRecursiveFixture.recursorIngressRun, standardAxioms := standard, nativeAxioms := nestedRecursorIngressNative, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, { root := - ``Ix.Tc.NestedRecursiveFixture.nestedRecursorRepresentationFacts, + ``Ix.Kernel.NestedRecursiveFixture.nestedRecursorRepresentationFacts, standardAxioms := standard, nativeAxioms := nestedRecursorRepresentationNative, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.treeNodePatternRel, + { root := ``Ix.Kernel.NestedRecursiveFixture.treeNodePatternRel, standardAxioms := standard, nativeAxioms := nestedRecursorNodePatternNative, sorryOrigins := nestedRestoredPatternUpstreamDebt, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.treeWrapPatternRel, + { root := ``Ix.Kernel.NestedRecursiveFixture.treeWrapPatternRel, standardAxioms := standard, nativeAxioms := nestedRecursorWrapPatternNative, sorryOrigins := nestedRestoredPatternUpstreamDebt, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.nestedRecursorAtomicAdmission, + { root := ``Ix.Kernel.NestedRecursiveFixture.nestedRecursorAtomicAdmission, standardAxioms := standard, nativeAxioms := nestedRecursorAtomicAdmissionNative, sorryOrigins := nestedRestoredPatternUpstreamDebt, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.NestedRecursiveFixture.nestedRecursorAtomicClosure, + { root := ``Ix.Kernel.NestedRecursiveFixture.nestedRecursorAtomicClosure, standardAxioms := standard, nativeAxioms := nestedRecursorAtomicClosureNative, sorryOrigins := nestedRestoredPatternUpstreamDebt, @@ -4206,77 +4192,77 @@ private def roots : Array RootAllowance := #[ -- under both best-effort and complete rule population. The final root -- covers the actual anonymous-mode cache insertion phase; none of these -- roots may recover the legacy inductive oracle. - { root := ``Ix.Tc.GeneratedRecursorMetadata.at_of_expectedFlat, + { root := ``Ix.Kernel.GeneratedRecursorMetadata.at_of_expectedFlat, standardAxioms := standardWithoutChoice, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.initialGeneratedRecursor_metadata, + { root := ``Ix.Kernel.initialGeneratedRecursor_metadata, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursor.metadata_setRules, + { root := ``Ix.Kernel.GeneratedRecursor.metadata_setRules, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursor.metadata_withRules, + { root := ``Ix.Kernel.GeneratedRecursor.metadata_withRules, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursor.ty_withRules, + { root := ``Ix.Kernel.GeneratedRecursor.ty_withRules, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursor.map_metadata_modify_withRules, + { root := ``Ix.Kernel.GeneratedRecursor.map_metadata_modify_withRules, standardAxioms := standardWithoutChoice, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursor.map_metadata_zipWithRules, + { root := ``Ix.Kernel.GeneratedRecursor.map_metadata_zipWithRules, standardAxioms := standardWithoutChoice, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursor.map_ty_zipWithRules, + { root := ``Ix.Kernel.GeneratedRecursor.map_ty_zipWithRules, standardAxioms := standardWithoutChoice, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.RecM.commitGeneratedRecursorRulesAt_artifacts, + ``Ix.Kernel.RecM.commitGeneratedRecursorRulesAt_artifacts, standardAxioms := standard, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.RecM.populateOptionalGeneratedRecursorRules_metadata, + ``Ix.Kernel.RecM.populateOptionalGeneratedRecursorRules_metadata, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.RecM.populateCompleteGeneratedRecursorRules_metadata, + ``Ix.Kernel.RecM.populateCompleteGeneratedRecursorRules_metadata, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.populateRecursorRulesFromBlock_artifacts, + { root := ``Ix.Kernel.RecM.populateRecursorRulesFromBlock_artifacts, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.populateRecursorRulesFromBlock_metadata, + { root := ``Ix.Kernel.RecM.populateRecursorRulesFromBlock_metadata, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.GeneratedRecursorSemantics.CanonicalRulesS.generatedRuleAt, + ``Ix.Kernel.GeneratedRecursorSemantics.CanonicalRulesS.generatedRuleAt, standardAxioms := standard, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.GeneratedRecursorSemantics.CanonicalArtifactsS.withRules, + ``Ix.Kernel.GeneratedRecursorSemantics.CanonicalArtifactsS.withRules, standardAxioms := standard, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.GeneratedRecursorSemantics.CanonicalTypeS.canonical, + ``Ix.Kernel.GeneratedRecursorSemantics.CanonicalTypeS.canonical, standardAxioms := standard, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.GeneratedRecursorSemantics.CanonicalRulesS.canonical, + ``Ix.Kernel.GeneratedRecursorSemantics.CanonicalRulesS.canonical, standardAxioms := standard, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.GeneratedRecursorSemantics.CanonicalArtifactsS.canonical, + ``Ix.Kernel.GeneratedRecursorSemantics.CanonicalArtifactsS.canonical, standardAxioms := standard, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.GeneratedRecursorSemantics.RecM.commitGeneratedRecursorRulesAt_canonicalAt, + ``Ix.Kernel.GeneratedRecursorSemantics.RecM.commitGeneratedRecursorRulesAt_canonicalAt, standardAxioms := standard, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.buildGeneratedRecursorTypes_metadata, + { root := ``Ix.Kernel.RecM.buildGeneratedRecursorTypes_metadata, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.buildAndCacheGeneratedRecursors_metadata, + { root := ``Ix.Kernel.RecM.buildAndCacheGeneratedRecursors_metadata, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, @@ -4284,71 +4270,71 @@ private def roots : Array RootAllowance := #[ -- E2c generated-recursor type closure. Production closes the accumulated -- domains through explicit right-to-left intern requests. These roots prove -- exact finite-support execution, operation-shaped structural translation, - -- and equality with Lean4Lean's public canonical mixed recursor type. - { root := ``Ix.Tc.CertifiedGenerationTransaction.generationEnv, + -- and equality with Ix.Theory.Named's public canonical mixed recursor type. + { root := ``Ix.Kernel.CertifiedGenerationTransaction.generationEnv, standardAxioms := standard, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursorTypeClosure.opened_toCtx, + { root := ``Ix.Kernel.GeneratedRecursorTypeClosure.opened_toCtx, standardAxioms := standardWithoutChoice, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursorTypeClosure.isType_forallN_inv, + { root := ``Ix.Kernel.GeneratedRecursorTypeClosure.isType_forallN_inv, standardAxioms := standardWithoutChoice, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursorTypeClosure.onTel_isType_getElem, + { root := ``Ix.Kernel.GeneratedRecursorTypeClosure.onTel_isType_getElem, standardAxioms := propextOnly, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.GeneratedRecursorTypeClosure.canonical_onTel_and_bodyType, + ``Ix.Kernel.GeneratedRecursorTypeClosure.canonical_onTel_and_bodyType, standardAxioms := standard, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursorTypeClosure.canonical_domainType, + { root := ``Ix.Kernel.GeneratedRecursorTypeClosure.canonical_domainType, standardAxioms := standard, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursorTypeClosure.canonical_bodyType, + { root := ``Ix.Kernel.GeneratedRecursorTypeClosure.canonical_bodyType, standardAxioms := standard, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.GeneratedRecursorTypeClosure.TelescopeS.of_canonical, + ``Ix.Kernel.GeneratedRecursorTypeClosure.TelescopeS.of_canonical, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.GeneratedRecursorTypeClosure.closeV_eq_forallN_take, + ``Ix.Kernel.GeneratedRecursorTypeClosure.closeV_eq_forallN_take, standardAxioms := standardWithoutChoice, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursorTypeClosure.closeV_canonical, + { root := ``Ix.Kernel.GeneratedRecursorTypeClosure.closeV_canonical, standardAxioms := standardWithoutChoice, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursorTypeClosure.TelescopeS.close, + { root := ``Ix.Kernel.GeneratedRecursorTypeClosure.TelescopeS.close, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursorTypeClosure.run_exact, + { root := ``Ix.Kernel.GeneratedRecursorTypeClosure.run_exact, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursorTypeClosure.run_translation, + { root := ``Ix.Kernel.GeneratedRecursorTypeClosure.run_translation, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursorTypeClosure.run_canonicalType, + { root := ``Ix.Kernel.GeneratedRecursorTypeClosure.run_canonicalType, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.GeneratedRecursorTypeClosure.buildRecType_decompose, + { root := ``Ix.Kernel.GeneratedRecursorTypeClosure.buildRecType_decompose, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.GeneratedRecursorTypeClosure.buildRecType_canonical_of_body, + ``Ix.Kernel.GeneratedRecursorTypeClosure.buildRecType_canonical_of_body, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.IndexedRecursiveFixture.familyBuildTypeExecution, + { root := ``Ix.Kernel.IndexedRecursiveFixture.familyBuildTypeExecution, standardAxioms := standard, nativeAxioms := generatedRecursorTypeFixtureNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.IndexedRecursiveFixture.familyBuildArtifactsExecution, + { root := ``Ix.Kernel.IndexedRecursiveFixture.familyBuildArtifactsExecution, standardAxioms := standard, nativeAxioms := generatedRecursorRuleFixtureNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, @@ -4358,31 +4344,31 @@ private def roots : Array RootAllowance := #[ -- finite fold; one K2S successor layer preserves the scoped state across -- selection and gives semantic meaning to the repeated type and positional -- rule comparisons. - { root := ``Ix.Tc.RecM.checkGeneratedRecursorFromCache_success, + { root := ``Ix.Kernel.RecM.checkGeneratedRecursorFromCache_success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkGeneratedRecursorFromCache_canonical, + { root := ``Ix.Kernel.RecM.checkGeneratedRecursorFromCache_canonical, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkGeneratedRecursorFromCache_canonicalScoped, + { root := ``Ix.Kernel.RecM.checkGeneratedRecursorFromCache_canonicalScoped, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.RecM.selectGeneratedRecursorIndex_preservesScoped, + { root := ``Ix.Kernel.RecM.selectGeneratedRecursorIndex_preservesScoped, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.IndexedRecursiveFixture.familyRuleCommitExecution, + { root := ``Ix.Kernel.IndexedRecursiveFixture.familyRuleCommitExecution, standardAxioms := standard, nativeAxioms := generatedRecursorCommitFixtureNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.IndexedRecursiveFixture.familyCacheCheckExecution, + { root := ``Ix.Kernel.IndexedRecursiveFixture.familyCacheCheckExecution, standardAxioms := standard, nativeAxioms := generatedRecursorCheckerFixtureNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.IndexedRecursiveFixture.familyCacheCheckCanonicalScoped, + { root := ``Ix.Kernel.IndexedRecursiveFixture.familyCacheCheckCanonicalScoped, standardAxioms := standard, nativeAxioms := generatedRecursorCanonicalFixtureNative, forbiddenDependencies := canonicalRecursorForbiddenDependencies }, @@ -4395,88 +4381,88 @@ private def roots : Array RootAllowance := #[ -- audit forbids every oracle constructor/world-materialization operation -- rather than the `InductiveOracle` type name itself. { root := - ``Ix.Tc.SemanticBlockTransitionCertificate.le_admittedWorld, + ``Ix.Kernel.SemanticBlockTransitionCertificate.le_admittedWorld, standardAxioms := standard, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.SemanticBlockTransitionCertificate.admit, + { root := ``Ix.Kernel.SemanticBlockTransitionCertificate.admit, standardAxioms := standard, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.SemanticBlockTransitionCertificate.admitState, + { root := ``Ix.Kernel.SemanticBlockTransitionCertificate.admitState, standardAxioms := standard, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, { root := - ``Ix.Tc.ExistingSemanticBlockCertificate.le_admittedWorld, + ``Ix.Kernel.ExistingSemanticBlockCertificate.le_admittedWorld, standardAxioms := standard, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.ExistingSemanticBlockCertificate.admit, + { root := ``Ix.Kernel.ExistingSemanticBlockCertificate.admit, standardAxioms := standard, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.ExistingSemanticBlockCertificate.admitState, + { root := ``Ix.Kernel.ExistingSemanticBlockCertificate.admitState, standardAxioms := standard, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.OneFamilyRecursorCertificate.atomicClosure, + { root := ``Ix.Kernel.OneFamilyRecursorCertificate.atomicClosure, standardAxioms := standard, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - { root := ``Ix.Tc.IndexedRecursiveFixture.familyRecursorAtomicClosure, + { root := ``Ix.Kernel.IndexedRecursiveFixture.familyRecursorAtomicClosure, standardAxioms := standard, nativeAxioms := generatedRecursorAtomicClosureNative, sorryOrigins := typingDebt, forbiddenDependencies := canonicalRecursorForbiddenDependencies }, - { root := ``Ix.Tc.IndexedRecursiveFixture.producerLinkedOneFamilyClosure, + { root := ``Ix.Kernel.IndexedRecursiveFixture.producerLinkedOneFamilyClosure, standardAxioms := standard, - upstreamAxioms := annotatedPiUpstreamAxioms, + implementationAxioms := annotatedPiUpstreamAxioms, nativeAxioms := indexedProducerClosureNative, sorryOrigins := annotatedPiUpstreamDebt, forbiddenDependencies := canonicalRecursorForbiddenDependencies }, { root := - ``Ix.Tc.RecursivePiRecursorFixture.recursivePiAtomicClosure, + ``Ix.Kernel.RecursivePiRecursorFixture.recursivePiAtomicClosure, standardAxioms := standard, nativeAxioms := recursivePiAtomicClosureNative, sorryOrigins := typingDebt, forbiddenDependencies := canonicalRecursorForbiddenDependencies }, { root := - ``Ix.Tc.AnnotatedPiRecursorFixture.annotatedPiAtomicClosure, + ``Ix.Kernel.AnnotatedPiRecursorFixture.annotatedPiAtomicClosure, standardAxioms := standard, - upstreamAxioms := annotatedPiUpstreamAxioms, + implementationAxioms := annotatedPiUpstreamAxioms, nativeAxioms := annotatedPiAtomicClosureNative, sorryOrigins := annotatedPiUpstreamDebt, forbiddenDependencies := canonicalRecursorForbiddenDependencies }, { root := - ``Ix.Tc.AliasFormerRecursorFixture.aliasFormerAtomicClosure, + ``Ix.Kernel.AliasFormerRecursorFixture.aliasFormerAtomicClosure, standardAxioms := standard, - upstreamAxioms := aliasFormerUpstreamAxioms, + implementationAxioms := aliasFormerUpstreamAxioms, nativeAxioms := aliasFormerAtomicClosureNative, sorryOrigins := aliasFormerUpstreamDebt, forbiddenDependencies := canonicalRecursorForbiddenDependencies }, { root := - ``Ix.Tc.AliasRecRecursorFixture.aliasRecAtomicClosure, + ``Ix.Kernel.AliasRecRecursorFixture.aliasRecAtomicClosure, standardAxioms := standard, - upstreamAxioms := aliasRecUpstreamAxioms, + implementationAxioms := aliasRecUpstreamAxioms, nativeAxioms := aliasRecAtomicClosureNative, sorryOrigins := aliasRecUpstreamDebt, forbiddenDependencies := canonicalRecursorForbiddenDependencies }, -- E2c flat semantic transport. The refined flat production trace erases -- to the exhaustive classifier, and the operation-shaped cross-kernel - -- contract recursively constructs Lean4Lean's retained positivity trace. + -- contract recursively constructs Ix.Theory.Named's retained positivity trace. -- Nested auxiliary expansion remains a separate explicit bridge. - { root := ``Ix.Tc.FlatPositivityDomainTrace.toPositivityDomainTrace, + { root := ``Ix.Kernel.FlatPositivityDomainTrace.toPositivityDomainTrace, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, { root := - ``Ix.Tc.FlatPositivityTraceTransport.constructorPositivityTrace, + ``Ix.Kernel.FlatPositivityTraceTransport.constructorPositivityTrace, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, -- E2c's concrete cross-kernel trace bridge. These roots start at the -- exact positivity calls selected by the production IndexedVec family - -- checker, transport those operations to Lean4Lean, and replay the complete + -- checker, transport those operations to Ix.Theory.Named, and replay the complete -- retained constructor validator. The direct recursive fixture has no -- nested auxiliary expansion; that remains the next generic E2c bridge. { root := - ``Ix.Tc.IndexedRecursiveFixture.indexedVecConsConstructorValidationRun, + ``Ix.Kernel.IndexedRecursiveFixture.indexedVecConsConstructorValidationRun, standardAxioms := standard, nativeAxioms := indexedConstructorValidationNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, @@ -4487,23 +4473,23 @@ private def roots : Array RootAllowance := #[ -- family/recursor checking, exact ownership, and atomic admission. The -- same executable witness rejects a recursor whose stored index arity was -- changed while its canonical type and rules were retained. - { root := ``Lean4Lean.VEnv.HasType.lamN_appN_beta, + { root := ``Ix.Theory.Named.VEnv.HasType.lamN_appN_beta, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.IndexedRecursivePattern.nilPatternRel, + { root := ``Ix.Kernel.IndexedRecursivePattern.nilPatternRel, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.IndexedRecursivePattern.consPatternRel, + { root := ``Ix.Kernel.IndexedRecursivePattern.consPatternRel, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.IndexedRecursivePattern.oracle, + { root := ``Ix.Kernel.IndexedRecursivePattern.oracle, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.IndexedRecursiveFixture.endToEndAcceptance, + { root := ``Ix.Kernel.IndexedRecursiveFixture.endToEndAcceptance, standardAxioms := standard, nativeAxioms := indexedRecursiveNative, sorryOrigins := typingDebt, @@ -4512,13 +4498,13 @@ private def roots : Array RootAllowance := #[ -- Elimination-breadth regression over exact kernel declarations. These -- roots compile, ingress, and run the production family/recursor checkers -- for both a source-universe-bearing small eliminator and `Eq`'s positive - -- K branch, then relate the stored physical metadata to Lean4Lean's exact + -- K branch, then relate the stored physical metadata to Ix.Theory.Named's exact -- generation trace. - { root := ``Ix.Tc.EliminationBreadthFixture.smallEliminationAcceptance, + { root := ``Ix.Kernel.EliminationBreadthFixture.smallEliminationAcceptance, standardAxioms := standard, nativeAxioms := smallEliminationAcceptanceNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - { root := ``Ix.Tc.EliminationBreadthFixture.kTargetAcceptance, + { root := ``Ix.Kernel.EliminationBreadthFixture.kTargetAcceptance, standardAxioms := standard, nativeAxioms := kTargetAcceptanceNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, @@ -4528,32 +4514,32 @@ private def roots : Array RootAllowance := #[ -- consumes those oracle constructors: its family block advances the exact -- generated Theory environment, and its recursor block consumes entries -- already installed there. - { root := ``Lean4Lean.VEnv.HasType.transfer_appN_telescope, + { root := ``Ix.Theory.Named.VEnv.HasType.transfer_appN_telescope, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.SingletonFamilyCatalogLink.oracle, + { root := ``Ix.Kernel.SingletonFamilyCatalogLink.oracle, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.SingletonRecursorCatalogLink.enumerationPatternRel, + { root := ``Ix.Kernel.SingletonRecursorCatalogLink.enumerationPatternRel, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.SingletonRecursorCatalogLink.oracle, + { root := ``Ix.Kernel.SingletonRecursorCatalogLink.oracle, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.certifySingletonFamilyBlock, + { root := ``Ix.Kernel.RecM.certifySingletonFamilyBlock, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.certifySingletonRecursorBlock, + { root := ``Ix.Kernel.RecM.certifySingletonRecursorBlock, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, -- Oracle-free semantic composition is audited independently of production -- execution so its native boundary contains only the finite representation, -- generation, equation, and pattern checks. - { root := ``Ix.Tc.BooleanEnumerationFixture.oneFamilyAtomicClosure, + { root := ``Ix.Kernel.BooleanEnumerationFixture.oneFamilyAtomicClosure, standardAxioms := standard, nativeAxioms := booleanSemanticAdmissionNative, sorryOrigins := typingDebt, @@ -4561,7 +4547,7 @@ private def roots : Array RootAllowance := #[ -- The headline additionally joins anonymous ingress, both production -- block-body and branch checkers, exact physical/catalog ownership, and the -- composed two-stage semantic transaction in one final world. - { root := ``Ix.Tc.BooleanEnumerationFixture.endToEndAcceptance, + { root := ``Ix.Kernel.BooleanEnumerationFixture.endToEndAcceptance, standardAxioms := standard, nativeAxioms := booleanEnumerationNative, sorryOrigins := typingDebt, forbiddenDependencies := canonicalRecursorForbiddenDependencies }, @@ -4569,410 +4555,408 @@ private def roots : Array RootAllowance := #[ -- G2a's explicit ambient-inductive assumption boundary. Audit every -- oracle projection so adding a field changes this manifest, then pin the -- constructive Nat model and its adversarial loaded-state witness. - { root := ``Ix.Tc.RawInductiveConstRel.mono, + { root := ``Ix.Kernel.RawInductiveConstRel.mono, standardAxioms := standard }, - { root := ``Ix.Tc.TrKExprS.mono, + { root := ``Ix.Kernel.TrKExprS.mono, standardAxioms := standard }, - { root := ``Ix.Tc.RegisteredRecursorRuleRhsRel.mono, + { root := ``Ix.Kernel.RegisteredRecursorRuleRhsRel.mono, standardAxioms := standard }, - { root := ``Ix.Tc.RegisteredRecursorRuleRhsRel.rhsTyped, + { root := ``Ix.Kernel.RegisteredRecursorRuleRhsRel.rhsTyped, standardAxioms := standard }, - { root := ``Ix.Tc.RawRecursorRuleRel.registeredRhs, + { root := ``Ix.Kernel.RawRecursorRuleRel.registeredRhs, standardAxioms := standard }, - { root := ``Ix.Tc.RawRecursorRuleRel.mono, + { root := ``Ix.Kernel.RawRecursorRuleRel.mono, standardAxioms := standard }, - { root := ``Ix.Tc.HeadConstN.of_varN_matches }, - { root := ``Ix.Tc.RecursorIotaPattern.matches_shape }, - { root := ``Ix.Tc.KConst.RecursorRuleAt.hasRecursorRule, + { root := ``Ix.Kernel.HeadConstN.of_varN_matches }, + { root := ``Ix.Kernel.RecursorIotaPattern.matches_shape }, + { root := ``Ix.Kernel.KConst.RecursorRuleAt.hasRecursorRule, standardAxioms := propextOnly }, - { root := ``Ix.Tc.RawRecursorRulePatternRel.mono, + { root := ``Ix.Kernel.RawRecursorRulePatternRel.mono, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.InductiveOracle.members, + { root := ``Ix.Kernel.InductiveOracle.members, standardAxioms := standard }, - { root := ``Ix.Tc.InductiveOracle.nonempty, + { root := ``Ix.Kernel.InductiveOracle.nonempty, standardAxioms := standard }, - { root := ``Ix.Tc.InductiveOracle.fresh, + { root := ``Ix.Kernel.InductiveOracle.fresh, standardAxioms := standard }, - { root := ``Ix.Tc.InductiveOracle.after, + { root := ``Ix.Kernel.InductiveOracle.after, standardAxioms := standard }, - { root := ``Ix.Tc.InductiveOracle.envLE, + { root := ``Ix.Kernel.InductiveOracle.envLE, standardAxioms := standard }, - { root := ``Ix.Tc.InductiveOracle.blockWF, + { root := ``Ix.Kernel.InductiveOracle.blockWF, standardAxioms := standard }, - { root := ``Ix.Tc.InductiveOracle.translateBlock, + { root := ``Ix.Kernel.InductiveOracle.translateBlock, standardAxioms := standard }, - { root := ``Ix.Tc.InductiveOracle.recursorFacts, + { root := ``Ix.Kernel.InductiveOracle.recursorFacts, standardAxioms := standard }, - { root := ``Ix.Tc.InductiveOracle.recursorPatterns, + { root := ``Ix.Kernel.InductiveOracle.recursorPatterns, standardAxioms := standard }, - { root := ``Ix.Tc.InductiveOracle.catalogued, + { root := ``Ix.Kernel.InductiveOracle.catalogued, standardAxioms := standard }, - { root := ``Ix.Tc.AmbientNat.oracle, + { root := ``Ix.Kernel.AmbientNat.oracle, standardAxioms := standard }, - { root := ``Ix.Tc.AmbientNat.nat_lookup_good, + { root := ``Ix.Kernel.AmbientNat.nat_lookup_good, standardAxioms := standard }, - { root := ``Ix.Tc.AmbientNat.badDecl_not_wf, + { root := ``Ix.Kernel.AmbientNat.badDecl_not_wf, standardAxioms := standard }, - { root := ``Ix.Tc.AmbientNat.acceptance, + { root := ``Ix.Kernel.AmbientNat.acceptance, standardAxioms := standard }, - -- G2b's C1--C3 consumer path. These roots resolve exact concrete + -- Lookup and admission roots resolve exact concrete -- constants through trusted-world provenance and are mechanically barred -- from depending on the legacy whole-environment translation. - { root := ``Ix.Tc.TrustedConstRel.mono, + { root := ``Ix.Kernel.TrustedConstRel.mono, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TrustedConstRel.trKExprS_const, + { root := ``Ix.Kernel.TrustedConstRel.trKExprS_const, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TrustedCatalogRel.resolve, + { root := ``Ix.Kernel.TrustedCatalogRel.resolve, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcStateWF.resolve, + { root := ``Ix.Kernel.TcStateWF.resolve, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcInv.resolve, + { root := ``Ix.Kernel.TcInv.resolve, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.natResolved, + { root := ``Ix.Kernel.AmbientNat.natResolved, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.natReferenceTranslates, + { root := ``Ix.Kernel.AmbientNat.natReferenceTranslates, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.bad_not_resolved, + { root := ``Ix.Kernel.AmbientNat.bad_not_resolved, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.natResolvedInv, + { root := ``Ix.Kernel.AmbientNat.natResolvedInv, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, -- G4's lookup isolation, exhaustive semantic-cache provenance, monotone -- warm-world transport, and transactional public-check error boundary. - { root := ``Ix.Tc.PendingDecl.lookup_isolation, + { root := ``Ix.Kernel.PendingDecl.lookup_isolation, standardAxioms := standard }, - { root := ``Ix.Tc.CacheEntry.SupportedBy.mono }, - { root := ``Ix.Tc.CacheAuthority.stable_mono, + { root := ``Ix.Kernel.CacheEntry.SupportedBy.mono }, + { root := ``Ix.Kernel.CacheAuthority.stable_mono, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.CacheProvenance.mono, + { root := ``Ix.Kernel.CacheProvenance.mono, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.CacheProvenance.pending_isolation_stable, + { root := ``Ix.Kernel.CacheProvenance.pending_isolation_stable, standardAxioms := standard }, - { root := ``Ix.Tc.KEnv.restoreBlockCheckResultsOnError_origin, + { root := ``Ix.Kernel.KEnv.restoreBlockCheckResultsOnError_origin, standardAxioms := standard }, - { root := ``Ix.Tc.CacheInvariant.mono, + { root := ``Ix.Kernel.CacheInvariant.mono, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.CacheInvariant.insertWhnf, + { root := ``Ix.Kernel.CacheInvariant.insertWhnf, standardAxioms := standard }, - { root := ``Ix.Tc.CacheInvariant.insertWhnfNoDelta, + { root := ``Ix.Kernel.CacheInvariant.insertWhnfNoDelta, standardAxioms := standard }, - { root := ``Ix.Tc.CacheInvariant.insertWhnfNoDeltaCheap, + { root := ``Ix.Kernel.CacheInvariant.insertWhnfNoDeltaCheap, standardAxioms := standard }, - { root := ``Ix.Tc.CacheInvariant.insertWhnfCore, + { root := ``Ix.Kernel.CacheInvariant.insertWhnfCore, standardAxioms := standard }, - { root := ``Ix.Tc.CacheInvariant.insertWhnfCoreCheap, + { root := ``Ix.Kernel.CacheInvariant.insertWhnfCoreCheap, standardAxioms := standard }, - { root := ``Ix.Tc.CacheInvariant.of_intern_update, + { root := ``Ix.Kernel.CacheInvariant.of_intern_update, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.CacheInvariant.clearReductionCaches, + { root := ``Ix.Kernel.CacheInvariant.clearReductionCaches, standardAxioms := standard }, - { root := ``Ix.Tc.CacheInvariant.restoreCheckCachesOnError, + { root := ``Ix.Kernel.CacheInvariant.restoreCheckCachesOnError, standardAxioms := standard }, - { root := ``Ix.Tc.TcM.isolateCheckErrors_error, + { root := ``Ix.Kernel.TcM.isolateCheckErrors_error, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.TcM.reset_cache_frame, - standardAxioms := standard, - nativeAxioms := #[nativeAxiom `Blake3 - `Blake3.HasherOps.hash._native.native_decide.ax_1] }, - { root := ``Ix.Tc.KernelStateWF.pendingCacheIsolation, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.KernelStateWF.restoreCheckCachesOnError, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.AmbientNat.warmCache_worldTransport, + { root := ``Ix.Kernel.TcM.reset_cache_frame, + standardAxioms := standard }, + { root := ``Ix.Kernel.KernelStateWF.pendingCacheIsolation, + standardAxioms := standard }, + { root := ``Ix.Kernel.KernelStateWF.restoreCheckCachesOnError, + standardAxioms := standard }, + { root := ``Ix.Kernel.AmbientNat.warmCache_worldTransport, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.warmCache_cannotResolvePending, + { root := ``Ix.Kernel.AmbientNat.warmCache_cannotResolvePending, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.cacheAcceptance, + { root := ``Ix.Kernel.AmbientNat.cacheAcceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- K1's concrete Theory reduction meaning, exact five-way cache overlay, -- and real ambient-Nat warm-hit witness. The only sorries are the already -- named upstream inductive-environment boundary. - { root := ``Ix.Tc.WhnfMeaning.refl, + { root := ``Ix.Kernel.WhnfMeaning.refl, standardAxioms := standard }, - { root := ``Ix.Tc.WhnfMeaning.symm, + { root := ``Ix.Kernel.WhnfMeaning.symm, standardAxioms := standard }, - { root := ``Ix.Tc.WhnfMeaning.mono, + { root := ``Ix.Kernel.WhnfMeaning.mono, standardAxioms := standard }, - { root := ``Ix.Tc.ExprCacheKind.isWhnf_iff }, - { root := ``Ix.Tc.WhnfCacheValid.mono, + { root := ``Ix.Kernel.ExprCacheKind.isWhnf_iff }, + { root := ``Ix.Kernel.WhnfCacheValid.mono, standardAxioms := standard }, - { root := ``Ix.Tc.WhnfCacheValid.expr, + { root := ``Ix.Kernel.WhnfCacheValid.expr, standardAxioms := standard }, - { root := ``Ix.Tc.CacheProvenance.isRec_of_trusted, + { root := ``Ix.Kernel.CacheProvenance.isRec_of_trusted, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.IsRecCacheValid.mono, + { root := ``Ix.Kernel.IsRecCacheValid.mono, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.IsRecCacheValid.trusted, + { root := ``Ix.Kernel.IsRecCacheValid.trusted, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.kernelCacheSemantics_isRec_valid, + { root := ``Ix.Kernel.kernelCacheSemantics_isRec_valid, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.CacheProvenance.whnfMeaning, + { root := ``Ix.Kernel.CacheProvenance.whnfMeaning, standardAxioms := standard }, - { root := ``Ix.Tc.CacheInvariant.whnfHit, + { root := ``Ix.Kernel.CacheInvariant.whnfHit, standardAxioms := standard }, - { root := ``Ix.Tc.AmbientNat.supportExpr_whnfMeaning, + { root := ``Ix.Kernel.AmbientNat.supportExpr_whnfMeaning, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.warmHit_whnfMeaning, + { root := ``Ix.Kernel.AmbientNat.warmHit_whnfMeaning, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNative_noAccel, + { root := ``Ix.Kernel.RecM.tryReduceNative_noAccel, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RecM.tryReduceBitvec_noAccel, + { root := ``Ix.Kernel.RecM.tryReduceBitvec_noAccel, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.tryReduceDecidable_noAccel, + { root := ``Ix.Kernel.RecM.tryReduceDecidable_noAccel, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RecM.tryReduceFinValDecidableRec_noAccel, + { root := ``Ix.Kernel.RecM.tryReduceFinValDecidableRec_noAccel, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.WhnfTheory.exprWF, + { root := ``Ix.Kernel.WhnfTheory.exprWF, standardAxioms := standard }, - { root := ``Ix.Tc.WhnfTheory.transMeaning, + { root := ``Ix.Kernel.WhnfTheory.transMeaning, standardAxioms := standard, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RawProjRel.none_ok, + { root := ``Ix.Kernel.RawProjRel.none_ok, standardAxioms := propextOnly }, - { root := ``Ix.Tc.RawProjRel.lean4Lean_ok, + { root := ``Ix.Kernel.RawProjRel.named_ok, standardAxioms := standard, sorryOrigins := projectionDebt }, - { root := ``Ix.Tc.ConcreteProjectionFixture.acceptance, + { root := ``Ix.Kernel.ConcreteProjectionFixture.acceptance, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := projectionDebt }, - { root := ``Ix.Tc.TcM.ctxAddrForLbr_zero, + { root := ``Ix.Kernel.TcM.ctxAddrForLbr_zero, standardAxioms := standard, nativeAxioms := contextNative }, - { root := ``Ix.Tc.TcM.whnfKey_closed, + { root := ``Ix.Kernel.TcM.whnfKey_closed, standardAxioms := standard, nativeAxioms := contextNative }, - { root := ``Ix.Tc.ContextKeyFrame.whnfStateInv, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.TcM.ctxAddrForLbr_wf, + { root := ``Ix.Kernel.ContextKeyFrame.whnfStateInv, + standardAxioms := standard }, + { root := ``Ix.Kernel.TcM.ctxAddrForLbr_wf, standardAxioms := standard, nativeAxioms := contextNative }, - { root := ``Ix.Tc.TcM.whnfKey_wf, + { root := ``Ix.Kernel.TcM.whnfKey_wf, standardAxioms := standard, nativeAxioms := contextNative }, - { root := ``Ix.Tc.TcM.whnfKey_matches_wf, + { root := ``Ix.Kernel.TcM.whnfKey_matches_wf, standardAxioms := standard, nativeAxioms := contextNative }, -- interning frame: exact intern-only framing, execution-indexed simultaneous -- substitution, and the production one-argument beta path. - { root := ``Ix.Tc.InternUpdateFrame.whnfStateInv, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.TcM.runIntern_whnf_wf, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.TcM.runIntern_whnf_eval, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.RunAssumptions.simulSubst_whnf_wf, + { root := ``Ix.Kernel.InternUpdateFrame.whnfStateInv, + standardAxioms := standard }, + { root := ``Ix.Kernel.TcM.runIntern_whnf_wf, + standardAxioms := standard }, + { root := ``Ix.Kernel.TcM.runIntern_whnf_eval, + standardAxioms := standard }, + { root := ``Ix.Kernel.RunAssumptions.simulSubst_whnf_wf, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunAssumptions.simulSubst_whnf_eval, + { root := ``Ix.Kernel.RunAssumptions.simulSubst_whnf_eval, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.WhnfMeaning.beta, + { root := ``Ix.Kernel.WhnfMeaning.beta, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.WhnfMeaning.letE, + { root := ``Ix.Kernel.WhnfMeaning.letE, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.WhnfMeaning.betaSimul, + { root := ``Ix.Kernel.WhnfMeaning.betaSimul, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.WhnfCoreLeaf.eval, + { root := ``Ix.Kernel.RecM.WhnfCoreLeaf.eval, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_betaOne, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_betaOne, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_leaf, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_leaf, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsUncached_betaOne, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsUncached_betaOne, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsUncached_betaOne_wf, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsUncached_betaOne_wf, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlags_leaf_wf, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlags_leaf_wf, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.AmbientNat.warmStateInvAccelerated, + { root := ``Ix.Kernel.AmbientNat.warmStateInvAccelerated, standardAxioms := standard, nativeAxioms := inferNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.warmKey_matches_wf, + { root := ``Ix.Kernel.AmbientNat.warmKey_matches_wf, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.whnfCoreConst_noAccel_acceptance, + { root := ``Ix.Kernel.AmbientNat.whnfCoreConst_noAccel_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.betaIdentityMeaning, + { root := ``Ix.Kernel.AmbientNat.betaIdentityMeaning, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.betaSimulSpec, + { root := ``Ix.Kernel.AmbientNat.betaSimulSpec, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.AmbientNat.betaSimulMeaning, + { root := ``Ix.Kernel.AmbientNat.betaSimulMeaning, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.betaWalker_eval, + { root := ``Ix.Kernel.AmbientNat.betaWalker_eval, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.AmbientNat.betaResultMeaning, + { root := ``Ix.Kernel.AmbientNat.betaResultMeaning, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.betaCoreUncached_eval, + { root := ``Ix.Kernel.AmbientNat.betaCoreUncached_eval, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.AmbientNat.betaCoreUncached_acceptance, + { root := ``Ix.Kernel.AmbientNat.betaCoreUncached_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- zeta reduction: both production zeta branches, including the legacy lifting walk, -- mixed-context semantic lookup, bounded driver, and inhabited fixtures. - { root := ``Ix.Tc.CtxRecon.lctxFindLetVal, + { root := ``Ix.Kernel.CtxRecon.lctxFindLetVal, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.TcM.lookupLetVal_eval, + { root := ``Ix.Kernel.TcM.lookupLetVal_eval, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RunAssumptions.lift_whnf_wf, + { root := ``Ix.Kernel.RunAssumptions.lift_whnf_wf, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RunAssumptions.lift_whnf_eval, + { root := ``Ix.Kernel.RunAssumptions.lift_whnf_eval, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.WhnfMeaning.zetaVar, + { root := ``Ix.Kernel.WhnfMeaning.zetaVar, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.WhnfMeaning.zetaFVar, + { root := ``Ix.Kernel.WhnfMeaning.zetaFVar, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_varZeta, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_varZeta, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_fvarZeta, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_fvarZeta, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsUncached_nextLeaf, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsUncached_nextLeaf, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsUncached_varZeta, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsUncached_varZeta, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsUncached_fvarZeta, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsUncached_fvarZeta, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsUncached_varZeta_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsUncached_varZeta_acceptance, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsUncached_fvarZeta_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsUncached_fvarZeta_acceptance, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.AmbientNat.bvarZetaLiftSpec, + { root := ``Ix.Kernel.AmbientNat.bvarZetaLiftSpec, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.AmbientNat.bvarZetaLookupEval, + { root := ``Ix.Kernel.AmbientNat.bvarZetaLookupEval, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.AmbientNat.bvarZetaMeaning, + { root := ``Ix.Kernel.AmbientNat.bvarZetaMeaning, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.bvarZetaCoreUncachedEval, + { root := ``Ix.Kernel.AmbientNat.bvarZetaCoreUncachedEval, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.AmbientNat.bvarZetaAcceptance, + { root := ``Ix.Kernel.AmbientNat.bvarZetaAcceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fvarZetaMeaning, + { root := ``Ix.Kernel.AmbientNat.fvarZetaMeaning, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fvarZetaCoreUncachedEval, + { root := ``Ix.Kernel.AmbientNat.fvarZetaCoreUncachedEval, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.AmbientNat.fvarZetaAcceptance, + { root := ``Ix.Kernel.AmbientNat.fvarZetaAcceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- projection/iota branch: exact projection/iota branches and bounded-driver composition. -- Semantic success is conditional on an explicit translated-source oracle; -- the two hostile fixtures prove that raw helper success cannot replace it. - { root := ``Ix.Tc.WhnfMeaning.projection, + { root := ``Ix.Kernel.WhnfMeaning.projection, standardAxioms := standard }, - { root := ``Ix.Tc.WhnfMeaning.registeredDefEq, + { root := ``Ix.Kernel.WhnfMeaning.registeredDefEq, standardAxioms := standard }, - { root := ``Ix.Tc.InductiveReductionOracle.projection, - standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.InductiveReductionOracle.iota, + { root := ``Ix.Kernel.InductiveReductionOracle.projection, + standardAxioms := standard, nativeAxioms := levelNative }, + { root := ``Ix.Kernel.InductiveReductionOracle.iota, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_projection, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_projection, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_iota, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_iota, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsUncached_projection, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsUncached_projection, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsUncached_iota, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsUncached_iota, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsUncached_projection_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsUncached_projection_acceptance, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsUncached_iota_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsUncached_iota_acceptance, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.AmbientNat.projectionReduceEval, + { root := ``Ix.Kernel.AmbientNat.projectionReduceEval, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.projectionCoreEval, + { root := ``Ix.Kernel.AmbientNat.projectionCoreEval, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.projectionSource_not_translated, + { root := ``Ix.Kernel.AmbientNat.projectionSource_not_translated, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.projectionAdversarialWitness, + { root := ``Ix.Kernel.AmbientNat.projectionAdversarialWitness, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaStateInv, + { root := ``Ix.Kernel.AmbientNat.iotaStateInv, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaTryEval, + { root := ``Ix.Kernel.AmbientNat.iotaTryEval, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaCoreEval, + { root := ``Ix.Kernel.AmbientNat.iotaCoreEval, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaSource_not_translated, + { root := ``Ix.Kernel.AmbientNat.iotaSource_not_translated, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaAdversarialWitness, + { root := ``Ix.Kernel.AmbientNat.iotaAdversarialWitness, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- structural trace: arbitrary-length structural traces compose exact production -- execution, fixed-world/context invariants, and local Theory meanings. -- The inhabited fixture takes two `.next` steps before its leaf; the -- hostile zero-fuel witness cannot be certified as a successful trace. - { root := ``Ix.Tc.RecM.WhnfCoreTrace.no_zero, + { root := ``Ix.Kernel.RecM.WhnfCoreTrace.no_zero, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfCoreTrace.eval, + { root := ``Ix.Kernel.RecM.WhnfCoreTrace.eval, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfCoreTrace.initialInv, + { root := ``Ix.Kernel.RecM.WhnfCoreTrace.initialInv, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfCoreTrace.finalInv, + { root := ``Ix.Kernel.RecM.WhnfCoreTrace.finalInv, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfCoreTrace.meaning, + { root := ``Ix.Kernel.RecM.WhnfCoreTrace.meaning, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.WhnfCoreTrace.uncached_eval, + { root := ``Ix.Kernel.RecM.WhnfCoreTrace.uncached_eval, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfCoreTrace.uncached_acceptance, + { root := ``Ix.Kernel.RecM.WhnfCoreTrace.uncached_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.AmbientNat.structuralNatLit_type, + { root := ``Ix.Kernel.AmbientNat.structuralNatLit_type, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.structuralWhnfTheory, + { root := ``Ix.Kernel.AmbientNat.structuralWhnfTheory, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.structuralLoopStateInv, + { root := ``Ix.Kernel.AmbientNat.structuralLoopStateInv, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.structuralLoopSourceMeaning, + { root := ``Ix.Kernel.AmbientNat.structuralLoopSourceMeaning, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.structuralLoopBetaMeaning, + { root := ``Ix.Kernel.AmbientNat.structuralLoopBetaMeaning, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.structuralLoopFVarStep, + { root := ``Ix.Kernel.AmbientNat.structuralLoopFVarStep, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.structuralLoopBetaStep, + { root := ``Ix.Kernel.AmbientNat.structuralLoopBetaStep, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.structuralLoopTrace, + { root := ``Ix.Kernel.AmbientNat.structuralLoopTrace, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.structuralLoopAcceptance, + { root := ``Ix.Kernel.AmbientNat.structuralLoopAcceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.structuralLoopZeroFuel, + { root := ``Ix.Kernel.AmbientNat.structuralLoopZeroFuel, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- structural cache: the public structural entry point's keyed body has exact full, @@ -4980,92 +4964,92 @@ private def roots : Array RootAllowance := #[ -- execution-indexed trace and universal provenance before insertion; -- hits require the physical entry, semantic invariant, and executed key -- match. The Nat fixture runs cold-to-warm in both isolated partitions. - { root := ``Ix.Tc.RecM.WhnfCoreNonLeaf.enter, + { root := ``Ix.Kernel.RecM.WhnfCoreNonLeaf.enter, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlags_varNotLet, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlags_varNotLet, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlags_varEnter, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlags_varEnter, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfCoreKeyedEntry.eval, + { root := ``Ix.Kernel.RecM.WhnfCoreKeyedEntry.eval, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsNonLeaf_fullHit, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsNonLeaf_fullHit, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsNonLeaf_cheapHit, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsNonLeaf_cheapHit, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsNonLeaf_fullMiss, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsNonLeaf_fullMiss, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsNonLeaf_cheapMiss, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsNonLeaf_cheapMiss, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsNonLeaf_transient, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsNonLeaf_transient, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfCoreCacheUpdate.full_whnfStateInv, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.RecM.WhnfCoreCacheUpdate.cheap_whnfStateInv, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlags_fullHit_acceptance, + { root := ``Ix.Kernel.RecM.WhnfCoreCacheUpdate.full_whnfStateInv, + standardAxioms := standard }, + { root := ``Ix.Kernel.RecM.WhnfCoreCacheUpdate.cheap_whnfStateInv, + standardAxioms := standard }, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlags_fullHit_acceptance, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlags_cheapHit_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlags_cheapHit_acceptance, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlags_fullMiss_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlags_fullMiss_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlags_cheapMiss_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlags_cheapMiss_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlags_transient_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlags_transient_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.AmbientNat.betaArgMeaning, + { root := ``Ix.Kernel.AmbientNat.betaArgMeaning, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullCoreProvenance, + { root := ``Ix.Kernel.AmbientNat.fullCoreProvenance, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.cheapCoreProvenance, + { root := ``Ix.Kernel.AmbientNat.cheapCoreProvenance, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.coreCacheFreshStateInv, + { root := ``Ix.Kernel.AmbientNat.coreCacheFreshStateInv, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullCoreWarmStateInv, + { root := ``Ix.Kernel.AmbientNat.fullCoreWarmStateInv, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.bothCoreWarmStateInv, + { root := ``Ix.Kernel.AmbientNat.bothCoreWarmStateInv, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.coreCacheKey_eval, + { root := ``Ix.Kernel.AmbientNat.coreCacheKey_eval, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.coreCacheKey_matches, + { root := ``Ix.Kernel.AmbientNat.coreCacheKey_matches, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.betaTransientFalse, + { root := ``Ix.Kernel.AmbientNat.betaTransientFalse, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.betaWalker_eval_state, + { root := ``Ix.Kernel.AmbientNat.betaWalker_eval_state, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.betaStep_state, + { root := ``Ix.Kernel.AmbientNat.betaStep_state, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.coreCacheTrace, + { root := ``Ix.Kernel.AmbientNat.coreCacheTrace, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullCoreColdAcceptance, + { root := ``Ix.Kernel.AmbientNat.fullCoreColdAcceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullCoreWarmAcceptance, + { root := ``Ix.Kernel.AmbientNat.fullCoreWarmAcceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.cheapCorePolicyMissAcceptance, + { root := ``Ix.Kernel.AmbientNat.cheapCorePolicyMissAcceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.cheapCoreWarmAcceptance, + { root := ``Ix.Kernel.AmbientNat.cheapCoreWarmAcceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.coreCachePolicyIsolation, + { root := ``Ix.Kernel.AmbientNat.coreCachePolicyIsolation, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, -- outer WHNF driver: no-delta and full-WHNF now have execution-indexed bounded traces, @@ -5073,174 +5057,174 @@ private def roots : Array RootAllowance := #[ -- and semantic hit/miss acceptance. The Nat fixture executes all nested -- cache layers, proves the cold call consumes exactly one fuel unit, and -- proves the warm public call preserves the entire state. - { root := ``Ix.Tc.WhnfStateInv.of_semantic_fields_eq, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.TcM.stepTrace_disabled, + { root := ``Ix.Kernel.WhnfStateInv.of_semantic_fields_eq, + standardAxioms := standard }, + { root := ``Ix.Kernel.TcM.stepTrace_disabled, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.TcM.bumpStats_disabled, + { root := ``Ix.Kernel.TcM.bumpStats_disabled, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.TcM.tick_success, + { root := ``Ix.Kernel.TcM.tick_success, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.WhnfNoDeltaTrace.no_zero, + { root := ``Ix.Kernel.RecM.WhnfNoDeltaTrace.no_zero, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfNoDeltaTrace.eval, + { root := ``Ix.Kernel.RecM.WhnfNoDeltaTrace.eval, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfNoDeltaTrace.initialInv, + { root := ``Ix.Kernel.RecM.WhnfNoDeltaTrace.initialInv, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfNoDeltaTrace.finalInv, + { root := ``Ix.Kernel.RecM.WhnfNoDeltaTrace.finalInv, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfNoDeltaTrace.meaning, + { root := ``Ix.Kernel.RecM.WhnfNoDeltaTrace.meaning, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.WhnfNoDeltaTrace.uncached_eval, + { root := ``Ix.Kernel.RecM.WhnfNoDeltaTrace.uncached_eval, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfNoDeltaTrace.uncached_acceptance, + { root := ``Ix.Kernel.RecM.WhnfNoDeltaTrace.uncached_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.WhnfFullTrace.no_zero, + { root := ``Ix.Kernel.RecM.WhnfFullTrace.no_zero, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfFullTrace.eval, + { root := ``Ix.Kernel.RecM.WhnfFullTrace.eval, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfFullTrace.initialInv, + { root := ``Ix.Kernel.RecM.WhnfFullTrace.initialInv, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfFullTrace.finalInv, + { root := ``Ix.Kernel.RecM.WhnfFullTrace.finalInv, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfFullTrace.meaning, + { root := ``Ix.Kernel.RecM.WhnfFullTrace.meaning, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.WhnfFullTrace.uncached_eval, + { root := ``Ix.Kernel.RecM.WhnfFullTrace.uncached_eval, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfFullTrace.uncached_acceptance, + { root := ``Ix.Kernel.RecM.WhnfFullTrace.uncached_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.WhnfDriverNonLeaf.noDelta_enter, + { root := ``Ix.Kernel.RecM.WhnfDriverNonLeaf.noDelta_enter, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfDriverNonLeaf.full_enter, + { root := ``Ix.Kernel.RecM.WhnfDriverNonLeaf.full_enter, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfDriverEntry.noDelta_eval, + { root := ``Ix.Kernel.RecM.WhnfDriverEntry.noDelta_eval, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfDriverEntry.full_eval, + { root := ``Ix.Kernel.RecM.WhnfDriverEntry.full_eval, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccModePrefix_disabled, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccModePrefix_disabled, standardAxioms := standard }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccModeMissCharge_disabled, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccModeMissCharge_disabled, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImplNonLeaf_fullHit, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImplNonLeaf_fullHit, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImplNonLeaf_cheapHit, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImplNonLeaf_cheapHit, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImplNonLeaf_fullMiss, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImplNonLeaf_fullMiss, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImplNonLeaf_cheapMiss, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImplNonLeaf_cheapMiss, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImplNonLeaf_stuck, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImplNonLeaf_stuck, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImplNonLeaf_transient, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImplNonLeaf_transient, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImplNonLeaf_nativeNoInsert, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImplNonLeaf_nativeNoInsert, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfDriverCacheUpdate.noDelta_whnfStateInv, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.RecM.WhnfDriverCacheUpdate.noDeltaCheap_whnfStateInv, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.RecM.WhnfDriverCacheUpdate.full_whnfStateInv, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImpl_fullHit_acceptance, + { root := ``Ix.Kernel.RecM.WhnfDriverCacheUpdate.noDelta_whnfStateInv, + standardAxioms := standard }, + { root := ``Ix.Kernel.RecM.WhnfDriverCacheUpdate.noDeltaCheap_whnfStateInv, + standardAxioms := standard }, + { root := ``Ix.Kernel.RecM.WhnfDriverCacheUpdate.full_whnfStateInv, + standardAxioms := standard }, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImpl_fullHit_acceptance, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImpl_cheapHit_acceptance, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImpl_cheapHit_acceptance, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImpl_fullMiss_acceptance, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImpl_fullMiss_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImpl_cheapMiss_acceptance, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImpl_cheapMiss_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImpl_stuck_acceptance, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImpl_stuck_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImpl_transient_acceptance, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImpl_transient_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccModeNonLeaf_hit, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccModeNonLeaf_hit, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccModeNonLeaf_miss, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccModeNonLeaf_miss, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccModeNonLeaf_stuck, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccModeNonLeaf_stuck, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccModeNonLeaf_transient, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccModeNonLeaf_transient, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccModeNonLeaf_nativeNoInsert, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccModeNonLeaf_nativeNoInsert, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccMode_hit_acceptance, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccMode_hit_acceptance, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccMode_miss_acceptance, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccMode_miss_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccMode_stuck_acceptance, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccMode_stuck_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccMode_transient_acceptance, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccMode_transient_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.whnf_public_eq_whnfWithNatSuccMode, + { root := ``Ix.Kernel.RecM.whnf_public_eq_whnfWithNatSuccMode, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.AmbientNat.betaNoDeltaStep, + { root := ``Ix.Kernel.AmbientNat.betaNoDeltaStep, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullNoDeltaProvenance, + { root := ``Ix.Kernel.AmbientNat.fullNoDeltaProvenance, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullWhnfProvenance, + { root := ``Ix.Kernel.AmbientNat.fullWhnfProvenance, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullNoDeltaWarmStateInv, + { root := ``Ix.Kernel.AmbientNat.fullNoDeltaWarmStateInv, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.noDeltaTrace, + { root := ``Ix.Kernel.AmbientNat.noDeltaTrace, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullNoDeltaColdAcceptance, + { root := ``Ix.Kernel.AmbientNat.fullNoDeltaColdAcceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullNoDeltaWarmAcceptance, + { root := ``Ix.Kernel.AmbientNat.fullNoDeltaWarmAcceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.noDeltaCachePolicyIsolation, + { root := ``Ix.Kernel.AmbientNat.noDeltaCachePolicyIsolation, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullWhnfChargedStateInv, + { root := ``Ix.Kernel.AmbientNat.fullWhnfChargedStateInv, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullWhnfPrefixCold, + { root := ``Ix.Kernel.AmbientNat.fullWhnfPrefixCold, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullWhnfMissCharge, + { root := ``Ix.Kernel.AmbientNat.fullWhnfMissCharge, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullWhnfCharged_noDeltaHit, + { root := ``Ix.Kernel.AmbientNat.fullWhnfCharged_noDeltaHit, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.betaFullWhnfStep, + { root := ``Ix.Kernel.AmbientNat.betaFullWhnfStep, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullWhnfTrace, + { root := ``Ix.Kernel.AmbientNat.fullWhnfTrace, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullWhnfWarmStateInv, + { root := ``Ix.Kernel.AmbientNat.fullWhnfWarmStateInv, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullWhnfColdAcceptance, + { root := ``Ix.Kernel.AmbientNat.fullWhnfColdAcceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullWhnfWarmAcceptance, + { root := ``Ix.Kernel.AmbientNat.fullWhnfWarmAcceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullWhnfFuelDiscipline, + { root := ``Ix.Kernel.AmbientNat.fullWhnfFuelDiscipline, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullWhnfCacheLayering, + { root := ``Ix.Kernel.AmbientNat.fullWhnfCacheLayering, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, -- total-outcome boundary: local step contracts now construct success traces and classify @@ -5248,362 +5232,362 @@ private def roots : Array RootAllowance := #[ -- dispatchers close conditionally over suffix reconciliation, transient -- lookup safety, collision-robust insertion provenance, and the local -- semantic step contracts. Instrumentation and miss charging are proved. - { root := ``Ix.Tc.WhnfPost.transMeaning, + { root := ``Ix.Kernel.WhnfPost.transMeaning, standardAxioms := standard, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.WhnfPost.meaning, + { root := ``Ix.Kernel.WhnfPost.meaning, standardAxioms := standard }, - { root := ``Ix.Tc.TcM.isLetVar_wf, + { root := ``Ix.Kernel.TcM.isLetVar_wf, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.TcM.stepTrace_whnf_wf, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.TcM.bumpStats_whnf_wf, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.RecM.WF.liftTcM, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.RecM.WF.get, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.RecM.WF.modifyGet, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.RecM.WF.modify, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.RecM.WhnfCoreTrace.complete, - standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfCoreTrace.uncached_wf, + { root := ``Ix.Kernel.TcM.stepTrace_whnf_wf, + standardAxioms := standard }, + { root := ``Ix.Kernel.TcM.bumpStats_whnf_wf, + standardAxioms := standard }, + { root := ``Ix.Kernel.RecM.WF.liftTcM, + standardAxioms := standard }, + { root := ``Ix.Kernel.RecM.WF.get, + standardAxioms := standard }, + { root := ``Ix.Kernel.RecM.WF.modifyGet, + standardAxioms := standard }, + { root := ``Ix.Kernel.RecM.WF.modify, + standardAxioms := standard }, + { root := ``Ix.Kernel.RecM.WhnfCoreTrace.complete, + standardAxioms := standard, nativeAxioms := inferNative }, + { root := ``Ix.Kernel.RecM.WhnfCoreTrace.uncached_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.WhnfNoDeltaTrace.complete, + { root := ``Ix.Kernel.RecM.WhnfNoDeltaTrace.complete, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfNoDeltaTrace.uncached_wf, + { root := ``Ix.Kernel.RecM.WhnfNoDeltaTrace.uncached_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.WhnfFullTrace.complete, + { root := ``Ix.Kernel.RecM.WhnfFullTrace.complete, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.WhnfFullTrace.uncached_wf, + { root := ``Ix.Kernel.RecM.WhnfFullTrace.uncached_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImplNonLeaf_wf, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImplNonLeaf_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImpl_nonLeaf_wf, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImpl_nonLeaf_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccModeNonLeaf_wf, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccModeNonLeaf_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccMode_nonLeaf_wf, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccMode_nonLeaf_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccModePrefix_wf, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccModeMissCharge_wf, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccMode_nonLeaf_semantic_wf, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccModePrefix_wf, + standardAxioms := standard }, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccModeMissCharge_wf, + standardAxioms := standard }, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccMode_nonLeaf_semantic_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImpl_wf, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImpl_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfWithNatSuccMode_wf, + { root := ``Ix.Kernel.RecM.whnfWithNatSuccMode_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfNoDelta_wf, + { root := ``Ix.Kernel.RecM.whnfNoDelta_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnf_wf, + { root := ``Ix.Kernel.RecM.whnf_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.noDeltaZeroFuel, + { root := ``Ix.Kernel.AmbientNat.noDeltaZeroFuel, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fullWhnfZeroFuel, + { root := ``Ix.Kernel.AmbientNat.fullWhnfZeroFuel, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.whnfLoopErrorSeparation, + { root := ``Ix.Kernel.AmbientNat.whnfLoopErrorSeparation, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfPost.refl, + { root := ``Ix.Kernel.WhnfPost.refl, + standardAxioms := standard }, + { root := ``Ix.Kernel.RecM.WF.bind, standardAxioms := standard }, - { root := ``Ix.Tc.RecM.WF.bind, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.RecM.runBounded_wf, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.RecM.WhnfLeaf.eval, + { root := ``Ix.Kernel.RecM.runBounded_wf, + standardAxioms := standard }, + { root := ``Ix.Kernel.RecM.WhnfLeaf.eval, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnf_leaf_wf, + { root := ``Ix.Kernel.RecM.whnf_leaf_wf, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnf_leaf_wf_of_theory, + { root := ``Ix.Kernel.RecM.whnf_leaf_wf_of_theory, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.AmbientNat.noAccelStateInv, + { root := ``Ix.Kernel.AmbientNat.noAccelStateInv, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.whnfLeaf_noAccel_wf, + { root := ``Ix.Kernel.AmbientNat.whnfLeaf_noAccel_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.whnfLeaf_noAccel_acceptance, + { root := ``Ix.Kernel.AmbientNat.whnfLeaf_noAccel_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.whnfKey_fst, + { root := ``Ix.Kernel.TcM.whnfKey_fst, standardAxioms := standard, nativeAxioms := contextNative }, - { root := ``Ix.Tc.WhnfContextKeys.Matches.sourceAddr, + { root := ``Ix.Kernel.WhnfContextKeys.Matches.sourceAddr, standardAxioms := standard, nativeAxioms := contextNative }, - { root := ``Ix.Tc.CacheProvenance.whnfMeaningOfMatches, + { root := ``Ix.Kernel.CacheProvenance.whnfMeaningOfMatches, standardAxioms := standard, nativeAxioms := contextNative }, - { root := ``Ix.Tc.CacheInvariant.whnfHitOfMatches, + { root := ``Ix.Kernel.CacheInvariant.whnfHitOfMatches, standardAxioms := standard, nativeAxioms := contextNative }, - { root := ``Ix.Tc.AmbientNat.warmKey_matches, + { root := ``Ix.Kernel.AmbientNat.warmKey_matches, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.methodsN_zero, + { root := ``Ix.Kernel.methodsN_zero, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.methodsN_succ_whnf, + { root := ``Ix.Kernel.methodsN_succ_whnf, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.methodsN_succ_whnfCore, + { root := ``Ix.Kernel.methodsN_succ_whnfCore, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.methodsN_succ_whnfMode, + { root := ``Ix.Kernel.methodsN_succ_whnfMode, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.methodsN_succ_whnfCoreFlags, + { root := ``Ix.Kernel.methodsN_succ_whnfCoreFlags, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.methodsN_succ_infer, + { root := ``Ix.Kernel.methodsN_succ_infer, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.methodsN_succ_isDefEq, + { root := ``Ix.Kernel.methodsN_succ_isDefEq, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.methodsOut_whnf, + { root := ``Ix.Kernel.methodsOut_whnf, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.methodsOut_whnfCore, + { root := ``Ix.Kernel.methodsOut_whnfCore, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.methodsOut_whnfMode, + { root := ``Ix.Kernel.methodsOut_whnfMode, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.methodsOut_whnfCoreFlags, + { root := ``Ix.Kernel.methodsOut_whnfCoreFlags, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.methodsOut_infer, + { root := ``Ix.Kernel.methodsOut_infer, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.methodsOut_isDefEq, + { root := ``Ix.Kernel.methodsOut_isDefEq, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.TcM.runRec_apply, + { root := ``Ix.Kernel.TcM.runRec_apply, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.TcM.runRec_directInfer_zero, + { root := ``Ix.Kernel.TcM.runRec_directInfer_zero, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.TcM.whnf_eq_runRec, + { root := ``Ix.Kernel.TcM.whnf_eq_runRec, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.TcM.whnfCore_eq_runRec, + { root := ``Ix.Kernel.TcM.whnfCore_eq_runRec, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.TcM.whnfNoDelta_eq_runRec, + { root := ``Ix.Kernel.TcM.whnfNoDelta_eq_runRec, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.TcM.infer_eq_runRec, + { root := ``Ix.Kernel.TcM.infer_eq_runRec, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.TcM.isDefEq_eq_runRec, + { root := ``Ix.Kernel.TcM.isDefEq_eq_runRec, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.TcM.ensureSort_eq_runRec, + { root := ``Ix.Kernel.TcM.ensureSort_eq_runRec, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.TcM.ensureForall_eq_runRec, + { root := ``Ix.Kernel.TcM.ensureForall_eq_runRec, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.unfoldConstValue_equation, + { root := ``Ix.Kernel.RecM.unfoldConstValue_equation, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RecM.tryDeltaUnfold_equation, + { root := ``Ix.Kernel.RecM.tryDeltaUnfold_equation, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RecM.deltaUnfoldOne_equation, + { root := ``Ix.Kernel.RecM.deltaUnfoldOne_equation, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RecM.applyIotaArg_false, + { root := ``Ix.Kernel.RecM.applyIotaArg_false, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.applyIotaArg_true_lam, + { root := ``Ix.Kernel.RecM.applyIotaArg_true_lam, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.isNatLiteralRecursorApp_equation, + { root := ``Ix.Kernel.RecM.isNatLiteralRecursorApp_equation, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.isTransientNatLiteralWork_equation, + { root := ``Ix.Kernel.RecM.isTransientNatLiteralWork_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.cleanupNatOffsetMajor_equation, + { root := ``Ix.Kernel.RecM.cleanupNatOffsetMajor_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.projectDecidableFinValMinor_equation, + { root := ``Ix.Kernel.RecM.projectDecidableFinValMinor_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.tryReduceFinValDecidableRec_equation, + { root := ``Ix.Kernel.RecM.tryReduceFinValDecidableRec_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.tryReduceProjectionDefinition_equation, + { root := ``Ix.Kernel.RecM.tryReduceProjectionDefinition_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.natRecLiteralParts_equation, + { root := ``Ix.Kernel.RecM.natRecLiteralParts_equation, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.isNatStuckRecursorAddr_equation, + { root := ``Ix.Kernel.RecM.isNatStuckRecursorAddr_equation, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.isStuckNatPredicateProbe_equation, + { root := ``Ix.Kernel.RecM.isStuckNatPredicateProbe_equation, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.bitvecOfNatArgs_equation, + { root := ``Ix.Kernel.RecM.bitvecOfNatArgs_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.charOfNatExpr_equation, + { root := ``Ix.Kernel.RecM.charOfNatExpr_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.tryReduceString_equation, + { root := ``Ix.Kernel.RecM.tryReduceString_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.discoverBlockInductives_equation, + { root := ``Ix.Kernel.RecM.discoverBlockInductives_equation, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.runBounded_zero, + { root := ``Ix.Kernel.RecM.runBounded_zero, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.runBounded_succ, + { root := ``Ix.Kernel.RecM.runBounded_succ, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.consumeBetaLams_equation, + { root := ``Ix.Kernel.RecM.consumeBetaLams_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.consumeBetaLamsFuel_zero, + { root := ``Ix.Kernel.RecM.consumeBetaLamsFuel_zero, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.consumeBetaLamsFuel_succ, + { root := ``Ix.Kernel.RecM.consumeBetaLamsFuel_succ, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.compareRank_equation, + { root := ``Ix.Kernel.RecM.compareRank_equation, standardAxioms := propextOnly }, - { root := ``Ix.Tc.RecM.isNatLike_equation, + { root := ``Ix.Kernel.RecM.isNatLike_equation, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.isNatZero_equation, + { root := ``Ix.Kernel.RecM.isNatZero_equation, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.natSuccOf_equation, + { root := ``Ix.Kernel.RecM.natSuccOf_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.isBoolTrue_equation, + { root := ``Ix.Kernel.RecM.isBoolTrue_equation, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.isDelta_equation, + { root := ``Ix.Kernel.RecM.isDelta_equation, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.isRegular_equation, + { root := ``Ix.Kernel.RecM.isRegular_equation, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.defRankId_equation, + { root := ``Ix.Kernel.RecM.defRankId_equation, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.infer_eq_inferWith, + { root := ``Ix.Kernel.RecM.infer_eq_inferWith, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.inferCall_run, + { root := ``Ix.Kernel.RecM.inferCall_run, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.inferOnlyCall_run, + { root := ``Ix.Kernel.RecM.inferOnlyCall_run, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.isDefEqCall_run, + { root := ``Ix.Kernel.RecM.isDefEqCall_run, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.whnfRec_run, + { root := ``Ix.Kernel.RecM.whnfRec_run, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.whnfModeRec_run, + { root := ``Ix.Kernel.RecM.whnfModeRec_run, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.whnfCoreFlagsRec_run, + { root := ``Ix.Kernel.RecM.whnfCoreFlagsRec_run, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.whnf_eq_whnfWithNatSuccMode, + { root := ``Ix.Kernel.RecM.whnf_eq_whnfWithNatSuccMode, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCore_eq_whnfCoreWithFlags, + { root := ``Ix.Kernel.RecM.whnfCore_eq_whnfCoreWithFlags, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDelta_eq_whnfNoDeltaImpl, + { root := ``Ix.Kernel.RecM.whnfNoDelta_eq_whnfNoDeltaImpl, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.ensureSortDirect_equation, + { root := ``Ix.Kernel.RecM.ensureSortDirect_equation, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.ensureForallDirect_equation, + { root := ``Ix.Kernel.RecM.ensureForallDirect_equation, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.peelProjForall_equation, + { root := ``Ix.Kernel.RecM.peelProjForall_equation, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.checkNoUnsafeRefs_equation, + { root := ``Ix.Kernel.RecM.checkNoUnsafeRefs_equation, standardAxioms := standard }, - { root := ``Ix.Tc.RecM.checkNoUnsafeRefs_go_nil, + { root := ``Ix.Kernel.RecM.checkNoUnsafeRefs_go_nil, standardAxioms := standard }, - { root := ``Ix.Tc.RecM.checkNoUnsafeRefs_go_app, + { root := ``Ix.Kernel.RecM.checkNoUnsafeRefs_go_app, standardAxioms := standard }, - { root := ``Ix.Tc.RecM.validateUnivParamsSeen_equation, + { root := ``Ix.Kernel.RecM.validateUnivParamsSeen_equation, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.validateUnivParamsSeen_go_nil, + { root := ``Ix.Kernel.RecM.validateUnivParamsSeen_go_nil, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.validateUnivParamsSeen_go_max, + { root := ``Ix.Kernel.RecM.validateUnivParamsSeen_go_max, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.validateExprWellScoped_equation, + { root := ``Ix.Kernel.RecM.validateExprWellScoped_equation, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.validateExprWellScoped_go_nil, + { root := ``Ix.Kernel.RecM.validateExprWellScoped_go_nil, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.validateExprWellScoped_go_app, + { root := ``Ix.Kernel.RecM.validateExprWellScoped_go_app, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.peelRuleIhForalls_equation, + { root := ``Ix.Kernel.RecM.peelRuleIhForalls_equation, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.checkPositivityDomain_equation, + { root := ``Ix.Kernel.RecM.checkPositivityDomain_equation, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.checkPositivityDomainFuel_zero, + { root := ``Ix.Kernel.RecM.checkPositivityDomainFuel_zero, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.checkNestedCtorFieldsFuel_zero, + { root := ``Ix.Kernel.RecM.checkNestedCtorFieldsFuel_zero, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.checkNestedCtorFieldsLoopFuel_zero, + { root := ``Ix.Kernel.RecM.checkNestedCtorFieldsLoopFuel_zero, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.countForalls_equation, + { root := ``Ix.Kernel.RecM.countForalls_equation, standardAxioms := standard, nativeAxioms := inferNative }, -- The complete checker dispatch is transparent now; these roots pin its -- exact production trust boundary in addition to the local equations. - { root := ``Ix.Tc.RecM.checkInductive, + { root := ``Ix.Kernel.RecM.checkInductive, standardAxioms := standard, nativeAxioms := inductiveNative }, - { root := ``Ix.Tc.RecM.checkRecursorMemberImpl, + { root := ``Ix.Kernel.RecM.checkRecursorMemberImpl, standardAxioms := standard, nativeAxioms := inductiveNative }, - { root := ``Ix.Tc.RecM.checkConst, + { root := ``Ix.Kernel.RecM.checkConst, standardAxioms := standard, nativeAxioms := inductiveNative }, - { root := ``Ix.Tc.TcM.checkConst, + { root := ``Ix.Kernel.TcM.checkConst, standardAxioms := standard, nativeAxioms := inductiveNative }, - { root := ``Ix.Tc.extractNatValue_app_const_equation, + { root := ``Ix.Kernel.extractNatValue_app_const_equation, standardAxioms := propextOnly }, - { root := ``Ix.Tc.extractNatValue_nat_equation, + { root := ``Ix.Kernel.extractNatValue_nat_equation, standardAxioms := propextOnly }, - { root := ``Ix.Tc.projectionDefinitionInfo_go_equation, + { root := ``Ix.Kernel.projectionDefinitionInfo_go_equation, standardAxioms := propextOnly }, - { root := ``Ix.Tc.EquivManager.find_equation, + { root := ``Ix.Kernel.EquivManager.find_equation, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.EquivManager.find_go_zero }, - { root := ``Ix.Tc.EquivManager.find_go_succ }, - { root := ``Ix.Tc.LocalContext.truncate_equation, + { root := ``Ix.Kernel.EquivManager.find_go_zero }, + { root := ``Ix.Kernel.EquivManager.find_go_succ }, + { root := ``Ix.Kernel.LocalContext.truncate_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.LocalContext.truncate_go_zero, + { root := ``Ix.Kernel.LocalContext.truncate_go_zero, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.LocalContext.truncate_go_succ, + { root := ``Ix.Kernel.LocalContext.truncate_go_succ, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.TcM.restoreDepth_apply, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.TcM.restoreDepth_go_zero, - standardAxioms := standard, nativeAxioms := blake3Native }, - { root := ``Ix.Tc.TcM.ctxSuffixNeed_zero, + { root := ``Ix.Kernel.TcM.restoreDepth_apply, + standardAxioms := standard }, + { root := ``Ix.Kernel.TcM.restoreDepth_go_zero, + standardAxioms := standard }, + { root := ``Ix.Kernel.TcM.ctxSuffixNeed_zero, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.TcM.ctxSuffixNeed_succ, + { root := ``Ix.Kernel.TcM.ctxSuffixNeed_succ, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.TcM.ctxSuffixNeed_of_fixed, + { root := ``Ix.Kernel.TcM.ctxSuffixNeed_of_fixed, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.KExpr.render_equation, + { root := ``Ix.Kernel.KExpr.render_equation, standardAxioms := standard }, - { root := ``Ix.Tc.KExpr.renderFuel_zero, + { root := ``Ix.Kernel.KExpr.renderFuel_zero, standardAxioms := standard }, - { root := ``Ix.Tc.RecM.natOffset_equation, + { root := ``Ix.Kernel.RecM.natOffset_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.natOffsetOrZero_equation, + { root := ``Ix.Kernel.RecM.natOffsetOrZero_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.evalNatOffsetLiteral_equation, + { root := ``Ix.Kernel.RecM.evalNatOffsetLiteral_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.natOffsetFuel_zero, + { root := ``Ix.Kernel.RecM.natOffsetFuel_zero, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.evalNatOffsetLiteralFuel_zero, + { root := ``Ix.Kernel.RecM.evalNatOffsetLiteralFuel_zero, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.tryEvalNatValueForPred_equation, + { root := ``Ix.Kernel.RecM.tryEvalNatValueForPred_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.tryEvalNatValueForPredFuel_zero, + { root := ``Ix.Kernel.RecM.tryEvalNatValueForPredFuel_zero, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.compareKUniv_succ_equation, + { root := ``Ix.Kernel.compareKUniv_succ_equation, standardAxioms := propextOnly }, - { root := ``Ix.Tc.compareKUniv_max_equation, + { root := ``Ix.Kernel.compareKUniv_max_equation, standardAxioms := propextOnly }, - { root := ``Ix.Tc.mergeSorted_equation, + { root := ``Ix.Kernel.mergeSorted_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.mergeSorted_go_zero, + { root := ``Ix.Kernel.mergeSorted_go_zero, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.sortByCompare_equation, + { root := ``Ix.Kernel.sortByCompare_equation, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.sortByCompareFuel_zero, + { root := ``Ix.Kernel.sortByCompareFuel_zero, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.sortKConstsRefineFuel_zero, + { root := ``Ix.Kernel.sortKConstsRefineFuel_zero, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.KExpr.treeSize_pos, + { root := ``Ix.Kernel.KExpr.treeSize_pos, standardAxioms := propextOnly }, - { root := ``Ix.Tc.exprMentionsAddr_equation, + { root := ``Ix.Kernel.exprMentionsAddr_equation, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.exprMentionsAddr_go_nil, + { root := ``Ix.Kernel.exprMentionsAddr_go_nil, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.exprMentionsAddr_go_app, + { root := ``Ix.Kernel.exprMentionsAddr_go_app, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.exprMentionsAddr_go_const, + { root := ``Ix.Kernel.exprMentionsAddr_go_const, standardAxioms := standardWithoutChoice }, -- RuntimeContracts: the repaired step source includes finite support plus an actual @@ -5611,77 +5595,77 @@ private def roots : Array RootAllowance := #[ -- collision-robust write validity. The transient Nat probe is proved -- state-pure for eager states. General lazy execution is reduced to the -- exact invariant contract of the driver-installed environment hook. - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_leaf_wf, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_leaf_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_betaOne_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_betaOne_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_projection_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_projection_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_iota_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_iota_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RunAssumptions.subst_whnf_wf, + { root := ``Ix.Kernel.RunAssumptions.subst_whnf_wf, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RunAssumptions.subst_whnf_eval, + { root := ``Ix.Kernel.RunAssumptions.subst_whnf_eval, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_letE, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_letE, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_letE_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_letE_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- regular-binder fallback: both translated regular-binder forms take the state-pure `.done` -- fallback and cannot be confused with their let-bound zeta siblings. - { root := ``Ix.Tc.TcM.lookupLetVal_none_state, + { root := ``Ix.Kernel.TcM.lookupLetVal_none_state, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_varDone, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_varDone, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_fvarDone, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_fvarDone, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_varDone_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_varDone_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_fvarDone_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_fvarDone_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.bvarStuckAcceptance, + { root := ``Ix.Kernel.AmbientNat.bvarStuckAcceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.fvarStuckAcceptance, + { root := ``Ix.Kernel.AmbientNat.fvarStuckAcceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- stuck-reduction fallback: projection misses and unchanged non-lambda application heads keep -- their original syntax, distinguish helper errors from `none`, and are -- inhabited by translated projection and constructor-application fixtures. - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_projectionDone, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_projectionDone, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_projectionWhnfError, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_projectionWhnfError, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_projectionReduceError, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_projectionReduceError, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_appUnchangedDone, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_appUnchangedDone, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_appHeadError, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_appHeadError, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_appUnchangedIotaError, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_appUnchangedIotaError, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_projectionDone_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_projectionDone_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_appUnchangedDone_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_appUnchangedDone_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.appStuckAcceptance, + { root := ``Ix.Kernel.AmbientNat.appStuckAcceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.ProjectionFallback.acceptance, + { root := ``Ix.Kernel.AmbientNat.ProjectionFallback.acceptance, standardAxioms := standard, nativeAxioms := nameContextNative, forbiddenDependencies := legacyWholeEnv }, @@ -5690,691 +5674,691 @@ private def roots : Array RootAllowance := #[ -- intern-only framing; general multi-beta and changed-head hit/miss/error -- equations consume that helper boundary. The Nat fixtures make argument -- reversal, a trailing argument, and physically changed heads observable. - { root := ``Ix.Tc.InternUpdateFrame.refl, + { root := ``Ix.Kernel.InternUpdateFrame.refl, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.InternUpdateFrame.trans, + { root := ``Ix.Kernel.InternUpdateFrame.trans, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RunAssumptions.internExpr_whnf_eval, + { root := ``Ix.Kernel.RunAssumptions.internExpr_whnf_eval, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.finishAppResult_eq_foldlM, + { root := ``Ix.Kernel.RecM.finishAppResult_eq_foldlM, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.finishAppResult_one, + { root := ``Ix.Kernel.RecM.finishAppResult_one, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.FinishAppRequests.result_eq_foldl, + { root := ``Ix.Kernel.RecM.FinishAppRequests.result_eq_foldl, standardAxioms := standardWithoutQuot, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.FinishAppRequests.support, + { root := ``Ix.Kernel.RecM.FinishAppRequests.support, standardAxioms := standard, nativeAxioms := levelNative }, - { root := ``Ix.Tc.RecM.FinishAppRequests.foldlM_eval, + { root := ``Ix.Kernel.RecM.FinishAppRequests.foldlM_eval, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.FinishAppRequests.eval, + { root := ``Ix.Kernel.RecM.FinishAppRequests.eval, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.FinishAppRequests.final_eq_spec, + { root := ``Ix.Kernel.RecM.FinishAppRequests.final_eq_spec, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_betaMany, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_betaMany, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_appChangedIota, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_appChangedIota, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_appChangedDone, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_appChangedDone, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_appChangedIotaError, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_appChangedIotaError, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_betaMany_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_betaMany_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_appChangedDone_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_appChangedDone_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_appChangedIota_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_appChangedIota_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_appChangedIotaError_acceptance, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_appChangedIotaError_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiBetaStep, + { root := ``Ix.Kernel.AmbientNat.multiBetaStep, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.changedHeadInternSpec, + { root := ``Ix.Kernel.AmbientNat.changedHeadInternSpec, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.AmbientNat.changedHeadStep, + { root := ``Ix.Kernel.AmbientNat.changedHeadStep, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.WhnfKey.closed_represents, + { root := ``Ix.Kernel.RecM.WhnfKey.closed_represents, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.WhnfCacheWriteOracle.closed, + { root := ``Ix.Kernel.RecM.WhnfCacheWriteOracle.closed, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.tryGetConst_noLazy, + { root := ``Ix.Kernel.TcM.tryGetConst_noLazy, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.TcM.lazyIngressAddr_wf, + { root := ``Ix.Kernel.TcM.lazyIngressAddr_wf, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.TcM.tryGetConst_wf, + { root := ``Ix.Kernel.TcM.tryGetConst_wf, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.KId.anon_eq_of_addr_eq }, - { root := ``Ix.Tc.TcM.tryGetConst_success_loaded, + { root := ``Ix.Kernel.KId.anon_eq_of_addr_eq }, + { root := ``Ix.Kernel.TcM.tryGetConst_success_loaded, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.NatRecLiteralPartsSuccessTrace.eval, + { root := ``Ix.Kernel.RecM.NatRecLiteralPartsSuccessTrace.eval, standardAxioms := standard }, - { root := ``Ix.Tc.RecM.NatRecLiteralPartsSuccessTrace.complete, + { root := ``Ix.Kernel.RecM.NatRecLiteralPartsSuccessTrace.complete, standardAxioms := standard }, - { root := ``Ix.Tc.RecM.NatRecLiteralPartsSuccessTrace.trusted, + { root := ``Ix.Kernel.RecM.NatRecLiteralPartsSuccessTrace.trusted, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrustedNatRecLiteralParts.patternAt, + { root := ``Ix.Kernel.RecM.TrustedNatRecLiteralParts.patternAt, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.HeadConstN.matches_varN }, - { root := ``Ix.Tc.HeadConstN.natLit_zero }, - { root := ``Ix.Tc.HeadConstN.natLit_succ }, - { root := ``Ix.Tc.RecursorIotaPattern.matches_of_shapes }, - { root := ``Ix.Tc.RecursorIotaPattern.exists_matches_iff_shapes }, - { root := ``Ix.Tc.RecursorIotaPattern.matches_natZero }, - { root := ``Ix.Tc.RecursorIotaPattern.matches_natSucc }, - { root := ``Ix.Tc.NatRecIotaCase.major_shape }, - { root := ``Ix.Tc.RecursorRulePattern.matches_natLiteral }, - { root := ``Ix.Tc.RecM.TrAppSpine.headConstN, + { root := ``Ix.Kernel.HeadConstN.matches_varN }, + { root := ``Ix.Kernel.HeadConstN.natLit_zero }, + { root := ``Ix.Kernel.HeadConstN.natLit_succ }, + { root := ``Ix.Kernel.RecursorIotaPattern.matches_of_shapes }, + { root := ``Ix.Kernel.RecursorIotaPattern.exists_matches_iff_shapes }, + { root := ``Ix.Kernel.RecursorIotaPattern.matches_natZero }, + { root := ``Ix.Kernel.RecursorIotaPattern.matches_natSucc }, + { root := ``Ix.Kernel.NatRecIotaCase.major_shape }, + { root := ``Ix.Kernel.RecursorRulePattern.matches_natLiteral }, + { root := ``Ix.Kernel.RecM.TrAppSpine.headConstN, standardAxioms := standard }, - { root := ``Ix.Tc.RecM.TrAppSpine.matches_natRecRulePrefix, + { root := ``Ix.Kernel.RecM.TrAppSpine.matches_natRecRulePrefix, standardAxioms := standard }, - { root := ``Ix.Tc.RawRecursorRulePatternRel.matches_natLiteralPrefix, + { root := ``Ix.Kernel.RawRecursorRulePatternRel.matches_natLiteralPrefix, standardAxioms := standard }, - { root := ``Ix.Tc.AmbientNat.linearRecTheoryPrefix_shape }, - { root := ``Ix.Tc.AmbientNat.linearRecZeroPatternMatch }, - { root := ``Ix.Tc.AmbientNat.linearRecSuccPatternMatch }, - { root := ``Ix.Tc.TrustedNatRecursorLayout.caseForMajor, + { root := ``Ix.Kernel.AmbientNat.linearRecTheoryPrefix_shape }, + { root := ``Ix.Kernel.AmbientNat.linearRecZeroPatternMatch }, + { root := ``Ix.Kernel.AmbientNat.linearRecSuccPatternMatch }, + { root := ``Ix.Kernel.TrustedNatRecursorLayout.caseForMajor, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrAppSuffix.tr, + { root := ``Ix.Kernel.RecM.TrAppSuffix.tr, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrAppSpine.splitAt, + { root := ``Ix.Kernel.RecM.TrAppSpine.splitAt, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatRecLiteralPartsDescriptor.patternMajor, + { root := ``Ix.Kernel.RecM.NatRecLiteralPartsDescriptor.patternMajor, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatRecLiteralPartsDescriptor.translatedSplit, + { root := ``Ix.Kernel.RecM.NatRecLiteralPartsDescriptor.translatedSplit, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrustedNatRecLiteralParts.translatedCase, + { root := ``Ix.Kernel.RecM.TrustedNatRecLiteralParts.translatedCase, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrAppSuffix.startHasType, + { root := ``Ix.Kernel.RecM.TrAppSuffix.startHasType, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrAppSuffix.rebase, + { root := ``Ix.Kernel.RecM.TrAppSuffix.rebase, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RawRecursorRulePatternRel.checkedReduction, + { root := ``Ix.Kernel.RawRecursorRulePatternRel.checkedReduction, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatRecLiteralTranslationSplit.checkedRhsSuffix, + { root := ``Ix.Kernel.RecM.NatRecLiteralTranslationSplit.checkedRhsSuffix, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RegisteredRecursorRuleRhsRel.rhsRaw, + { root := ``Ix.Kernel.RegisteredRecursorRuleRhsRel.rhsRaw, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RegisteredRecursorRuleRhsRel.rhsStructural, + { root := ``Ix.Kernel.RegisteredRecursorRuleRhsRel.rhsStructural, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RegisteredRecursorRuleRhsRel.instUnivSpec, + { root := ``Ix.Kernel.RegisteredRecursorRuleRhsRel.instUnivSpec, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RegisteredRecursorRuleRhsRel.instantiateUnivParams_nonempty, + ``Ix.Kernel.RegisteredRecursorRuleRhsRel.instantiateUnivParams_nonempty, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RawRecursorRuleRel.registeredRhsTyped, + { root := ``Ix.Kernel.RawRecursorRuleRel.registeredRhsTyped, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrAppSuffix.rebaseQuot, + { root := ``Ix.Kernel.RecM.TrAppSuffix.rebaseQuot, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.NatRecLiteralTranslationSplit.checkedRhsSuffixQuot, + ``Ix.Kernel.RecM.NatRecLiteralTranslationSplit.checkedRhsSuffixQuot, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.KExpr.Constructed.liftNoIntern_eq_liftSpec, + { root := ``Ix.Kernel.KExpr.Constructed.liftNoIntern_eq_liftSpec, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.KExpr.Constructed.substNoIntern_eq_substSpec, + { root := ``Ix.Kernel.KExpr.Constructed.substNoIntern_eq_substSpec, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.applyIotaArg_true_lam_spec, + { root := ``Ix.Kernel.RecM.applyIotaArg_true_lam_spec, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.applyIotaArg_true_lam_run, + { root := ``Ix.Kernel.RecM.applyIotaArg_true_lam_run, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfMeaning.betaNoIntern, + { root := ``Ix.Kernel.WhnfMeaning.betaNoIntern, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.betaIotaArgRun, + { root := ``Ix.Kernel.AmbientNat.betaIotaArgRun, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.betaNoInternMeaning, + { root := ``Ix.Kernel.AmbientNat.betaNoInternMeaning, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.IotaArgNonLambda.applyIotaArg_true, + { root := ``Ix.Kernel.IotaArgNonLambda.applyIotaArg_true, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.IotaArgNonLambda.applyIotaArg_true_run, + { root := ``Ix.Kernel.IotaArgNonLambda.applyIotaArg_true_run, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfMeaning.appRebuild, + { root := ``Ix.Kernel.WhnfMeaning.appRebuild, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.applyIotaArg_true_nonlam_semantic, + { root := ``Ix.Kernel.RecM.applyIotaArg_true_nonlam_semantic, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.applyIotaArg_false_eval, + { root := ``Ix.Kernel.RecM.applyIotaArg_false_eval, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.applyIotaArg_false_semantic, + { root := ``Ix.Kernel.RecM.applyIotaArg_false_semantic, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.appStuckIotaTransient, + { root := ``Ix.Kernel.AmbientNat.appStuckIotaTransient, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.appStuckIotaInterned, + { root := ``Ix.Kernel.AmbientNat.appStuckIotaInterned, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfMeaning.resultQuot, + { root := ``Ix.Kernel.WhnfMeaning.resultQuot, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfMeaning.ofStructuralQuot, + { root := ``Ix.Kernel.WhnfMeaning.ofStructuralQuot, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.KExpr.substNoIntern_of_lbr_le, + { root := ``Ix.Kernel.KExpr.substNoIntern_of_lbr_le, standardAxioms := standardWithoutQuot, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.KExpr.liftNoIntern_of_lbr_le, + { root := ``Ix.Kernel.KExpr.liftNoIntern_of_lbr_le, standardAxioms := standardWithoutQuot, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.applyIotaArgs_eq_foldlM, + { root := ``Ix.Kernel.RecM.applyIotaArgs_eq_foldlM, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.singleton, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.singleton, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.append, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.append, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.three, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.three, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.ApplyIotaArgsTrace.transientNonLambdaSingleton, + ``Ix.Kernel.RecM.ApplyIotaArgsTrace.transientNonLambdaSingleton, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.ApplyIotaArgsTrace.transientNonLambdaSingletonQuot, + ``Ix.Kernel.RecM.ApplyIotaArgsTrace.transientNonLambdaSingletonQuot, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.internedSingleton, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.internedSingleton, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.transientLambdaSingleton, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.transientLambdaSingleton, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.transientLambdaSingletonQuot, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.transientLambdaSingletonQuot, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.evalList, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.evalList, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.evalArray, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.evalArray, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.sourceTr, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.sourceTr, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.finalQuot, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.finalQuot, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.finalInv, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.finalInv, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.frame, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.frame, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.finalSupport, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.finalSupport, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.acceptance, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.acceptance, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.evalThreeArrays, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.evalThreeArrays, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.threeArrayAcceptance, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.threeArrayAcceptance, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfMeaning.ofQuot, + { root := ``Ix.Kernel.WhnfMeaning.ofQuot, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.sourceQuot, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.sourceQuot, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.acceptanceQuot, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.acceptanceQuot, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaArgsTrace.threeArrayAcceptanceQuot, + { root := ``Ix.Kernel.RecM.ApplyIotaArgsTrace.threeArrayAcceptanceQuot, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.instantiateUnivParams_whnf_of_run, + { root := ``Ix.Kernel.TcM.instantiateUnivParams_whnf_of_run, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaRuleTrace.eval, + { root := ``Ix.Kernel.RecM.ApplyIotaRuleTrace.eval, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaRuleTrace.emptyInstantiation, + { root := ``Ix.Kernel.RecM.ApplyIotaRuleTrace.emptyInstantiation, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaRuleTrace.instantiatePost, + { root := ``Ix.Kernel.RecM.ApplyIotaRuleTrace.instantiatePost, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaRuleTrace.acceptance, + { root := ``Ix.Kernel.RecM.ApplyIotaRuleTrace.acceptance, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaRuleTrace.acceptance_empty, + { root := ``Ix.Kernel.RecM.ApplyIotaRuleTrace.acceptance_empty, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.ApplyIotaRuleTrace.registeredStartQuot_empty, + ``Ix.Kernel.RecM.ApplyIotaRuleTrace.registeredStartQuot_empty, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.ApplyIotaRuleTrace.registeredAcceptance_empty, + ``Ix.Kernel.RecM.ApplyIotaRuleTrace.registeredAcceptance_empty, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.ApplyIotaRuleTrace.registeredStartQuot_nonempty, + ``Ix.Kernel.RecM.ApplyIotaRuleTrace.registeredStartQuot_nonempty, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.ApplyIotaRuleTrace.registeredAcceptance_nonempty, + ``Ix.Kernel.RecM.ApplyIotaRuleTrace.registeredAcceptance_nonempty, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaRuleTrace.checkedMeaning, + { root := ``Ix.Kernel.RecM.ApplyIotaRuleTrace.checkedMeaning, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.ApplyIotaRuleTrace.checkedAcceptance_empty, + ``Ix.Kernel.RecM.ApplyIotaRuleTrace.checkedAcceptance_empty, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.ApplyIotaRuleTrace.checkedAcceptance_nonempty, + ``Ix.Kernel.RecM.ApplyIotaRuleTrace.checkedAcceptance_nonempty, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.KConst.recursorMajorIdx_of_iotaInfo, + { root := ``Ix.Kernel.KConst.recursorMajorIdx_of_iotaInfo, standardAxioms := propextOnly, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.KConst.recursorRuleAt_of_iotaInfo, + { root := ``Ix.Kernel.KConst.recursorRuleAt_of_iotaInfo, standardAxioms := propextOnly, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TryApplyIotaCtorSuccessTrace.eval, + { root := ``Ix.Kernel.RecM.TryApplyIotaCtorSuccessTrace.eval, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaCtorTrace.operational, + { root := ``Ix.Kernel.RecM.ApplyIotaCtorTrace.operational, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaCtorTrace.eval, + { root := ``Ix.Kernel.RecM.ApplyIotaCtorTrace.eval, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaCtorTrace.recursorRuleAt, + { root := ``Ix.Kernel.RecM.ApplyIotaCtorTrace.recursorRuleAt, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaCtorTrace.acceptance_empty, + { root := ``Ix.Kernel.RecM.ApplyIotaCtorTrace.acceptance_empty, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaCtorTrace.checkedAcceptance_empty, + { root := ``Ix.Kernel.RecM.ApplyIotaCtorTrace.checkedAcceptance_empty, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ApplyIotaCtorTrace.checkedAcceptance_nonempty, + { root := ``Ix.Kernel.RecM.ApplyIotaCtorTrace.checkedAcceptance_nonempty, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaCtorOrStructEta_regular, + { root := ``Ix.Kernel.RecM.tryIotaCtorOrStructEta_regular, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaAfterMajorWhnf_regular, + { root := ``Ix.Kernel.RecM.tryIotaAfterMajorWhnf_regular, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaWithFlags_nonKPrefix, + { root := ``Ix.Kernel.RecM.tryIotaWithFlags_nonKPrefix, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaWithFlags_regularCtor, + { root := ``Ix.Kernel.RecM.tryIotaWithFlags_regularCtor, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.tryIotaWithFlags_regularCtor_checkedAcceptance_empty, + ``Ix.Kernel.RecM.tryIotaWithFlags_regularCtor_checkedAcceptance_empty, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.natToConstructor_zero, + { root := ``Ix.Kernel.RecM.natToConstructor_zero, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.natToConstructor_succ, + { root := ``Ix.Kernel.RecM.natToConstructor_succ, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaAfterMajorWhnf_nat, + { root := ``Ix.Kernel.RecM.tryIotaAfterMajorWhnf_nat, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaWithFlags_natCtor, + { root := ``Ix.Kernel.RecM.tryIotaWithFlags_natCtor, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.tryIotaWithFlags_natCtor_checkedAcceptance_empty, + ``Ix.Kernel.RecM.tryIotaWithFlags_natCtor_checkedAcceptance_empty, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.intern_success_frame, + { root := ``Ix.Kernel.RecM.intern_success_frame, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.strLitListToConstructor_empty, + { root := ``Ix.Kernel.RecM.strLitListToConstructor_empty, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.strLitListToConstructor_success_frame, + { root := ``Ix.Kernel.RecM.strLitListToConstructor_success_frame, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.strLitToConstructor_success_frame, + { root := ``Ix.Kernel.RecM.strLitToConstructor_success_frame, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.evalNatOffsetLiteral_str, + { root := ``Ix.Kernel.RecM.evalNatOffsetLiteral_str, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.natOffset_str, + { root := ``Ix.Kernel.RecM.natOffset_str, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.cleanupNatOffsetMajor_str, + { root := ``Ix.Kernel.RecM.cleanupNatOffsetMajor_str, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaAfterMajorWhnf_str, + { root := ``Ix.Kernel.RecM.tryIotaAfterMajorWhnf_str, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaWithFlags_strCtor, + { root := ``Ix.Kernel.RecM.tryIotaWithFlags_strCtor, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.tryIotaWithFlags_strCtor_checkedAcceptance_empty, + ``Ix.Kernel.RecM.tryIotaWithFlags_strCtor_checkedAcceptance_empty, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaStringEmptyFold, + { root := ``Ix.Kernel.AmbientNat.iotaStringEmptyFold, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaStringExpand, + { root := ``Ix.Kernel.AmbientNat.iotaStringExpand, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaStringCallback, + { root := ``Ix.Kernel.AmbientNat.iotaStringCallback, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaStringCleanup, + { root := ``Ix.Kernel.AmbientNat.iotaStringCleanup, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaStringGetZeroOfFrame, + { root := ``Ix.Kernel.AmbientNat.iotaStringGetZeroOfFrame, standardAxioms := standard, nativeAxioms := canonicalPrimitivesNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaStringApplyRule, + { root := ``Ix.Kernel.AmbientNat.iotaStringApplyRule, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaStringApplyCtor, + { root := ``Ix.Kernel.AmbientNat.iotaStringApplyCtor, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaStringAfterEval, + { root := ``Ix.Kernel.AmbientNat.iotaStringAfterEval, standardAxioms := standard, nativeAxioms := nameContextNative, forbiddenDependencies := legacyWholeEnv }, -- ConstructorSynthesis: the positive K-like recursor branch. Optional probes retain -- error-side state, candidate synthesis records the DefEq gate and counter -- order, and the inhabited fixture reaches the real bounded WHNF driver. - { root := ``Ix.Tc.RecM.tryOptional_success, + { root := ``Ix.Kernel.RecM.tryOptional_success, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryOptional_error, + { root := ``Ix.Kernel.RecM.tryOptional_error, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.VerifyKSynthCandidateSuccessTrace.eval, + { root := ``Ix.Kernel.RecM.VerifyKSynthCandidateSuccessTrace.eval, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.VerifyKSynthCandidateRejectTrace.eval, + { root := ``Ix.Kernel.RecM.VerifyKSynthCandidateRejectTrace.eval, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.SynthCtorWhenKSuccessTrace.eval, + { root := ``Ix.Kernel.RecM.SynthCtorWhenKSuccessTrace.eval, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaWithFlags_kPrefix, + { root := ``Ix.Kernel.RecM.tryIotaWithFlags_kPrefix, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaWithFlags_kFallback, + { root := ``Ix.Kernel.RecM.tryIotaWithFlags_kFallback, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaWithFlags_kCtor, + { root := ``Ix.Kernel.RecM.tryIotaWithFlags_kCtor, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.tryIotaWithFlags_kCtor_checkedAcceptance_empty, + ``Ix.Kernel.RecM.tryIotaWithFlags_kCtor_checkedAcceptance_empty, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaIntern, + { root := ``Ix.Kernel.AmbientNat.kIotaIntern, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaMajorInfer, + { root := ``Ix.Kernel.AmbientNat.kIotaMajorInfer, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaMajorWhnf, + { root := ``Ix.Kernel.AmbientNat.kIotaMajorWhnf, standardAxioms := standard, nativeAxioms := canonicalPrimitivesNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaGetRec, + { root := ``Ix.Kernel.AmbientNat.kIotaGetRec, standardAxioms := standard, nativeAxioms := canonicalPrimitivesNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaGetNat, + { root := ``Ix.Kernel.AmbientNat.kIotaGetNat, standardAxioms := standard, nativeAxioms := canonicalPrimitivesNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaMajorInductive, + { root := ``Ix.Kernel.AmbientNat.kIotaMajorInductive, standardAxioms := standard, nativeAxioms := canonicalPrimitivesContextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaCtorInfer, + { root := ``Ix.Kernel.AmbientNat.kIotaCtorInfer, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaAttemptStats, + { root := ``Ix.Kernel.AmbientNat.kIotaAttemptStats, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaTypeDefEq, + { root := ``Ix.Kernel.AmbientNat.kIotaTypeDefEq, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaCandidate, + { root := ``Ix.Kernel.AmbientNat.kIotaCandidate, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaSynth, + { root := ``Ix.Kernel.AmbientNat.kIotaSynth, standardAxioms := standard, nativeAxioms := inferNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaInternFrame, + { root := ``Ix.Kernel.AmbientNat.kIotaInternFrame, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaSynthCleanup, + { root := ``Ix.Kernel.AmbientNat.kIotaSynthCleanup, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaSynthWhnf, + { root := ``Ix.Kernel.AmbientNat.kIotaSynthWhnf, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaGetZeroAfter, + { root := ``Ix.Kernel.AmbientNat.kIotaGetZeroAfter, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaApplyRule, + { root := ``Ix.Kernel.AmbientNat.kIotaApplyRule, standardAxioms := standard, nativeAxioms := nameNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaApplyCtor, + { root := ``Ix.Kernel.AmbientNat.kIotaApplyCtor, standardAxioms := standard, nativeAxioms := nameNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaTryEval, + { root := ``Ix.Kernel.AmbientNat.kIotaTryEval, standardAxioms := standard, nativeAxioms := nameContextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaStepEval, + { root := ``Ix.Kernel.AmbientNat.kIotaStepEval, standardAxioms := standard, nativeAxioms := nameContextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kIotaCoreEval, + { root := ``Ix.Kernel.AmbientNat.kIotaCoreEval, standardAxioms := standard, nativeAxioms := nameContextNative, forbiddenDependencies := legacyWholeEnv }, -- ConstructorSynthesisFallback: exhaustive K-synthesis fallback and error branches. - { root := ``Ix.Tc.RecM.verifyKSynthCandidate_inferMiss, + { root := ``Ix.Kernel.RecM.verifyKSynthCandidate_inferMiss, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.verifyKSynthCandidate_inferError, + { root := ``Ix.Kernel.RecM.verifyKSynthCandidate_inferError, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.verifyKSynthCandidate_defEqError, + { root := ``Ix.Kernel.RecM.verifyKSynthCandidate_defEqError, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.selectKSynthCandidate_mismatch, + { root := ``Ix.Kernel.RecM.selectKSynthCandidate_mismatch, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.selectKSynthCandidate_missing, + { root := ``Ix.Kernel.RecM.selectKSynthCandidate_missing, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.selectKSynthCandidate_nonInductive, + { root := ``Ix.Kernel.RecM.selectKSynthCandidate_nonInductive, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.selectKSynthCandidate_empty, + { root := ``Ix.Kernel.RecM.selectKSynthCandidate_empty, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.selectKSynthCandidate_selected, + { root := ``Ix.Kernel.RecM.selectKSynthCandidate_selected, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.selectKSynthCandidate_selectedError, + { root := ``Ix.Kernel.RecM.selectKSynthCandidate_selectedError, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.synthCtorWhenK_majorInferMiss, + { root := ``Ix.Kernel.RecM.synthCtorWhenK_majorInferMiss, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.synthCtorWhenK_majorInferError, + { root := ``Ix.Kernel.RecM.synthCtorWhenK_majorInferError, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.synthCtorWhenK_majorWhnfMiss, + { root := ``Ix.Kernel.RecM.synthCtorWhenK_majorWhnfMiss, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.synthCtorWhenK_majorWhnfError, + { root := ``Ix.Kernel.RecM.synthCtorWhenK_majorWhnfError, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.synthCtorWhenK_nonConstHead, + { root := ``Ix.Kernel.RecM.synthCtorWhenK_nonConstHead, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.synthCtorWhenK_recursorMissing, + { root := ``Ix.Kernel.RecM.synthCtorWhenK_recursorMissing, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.synthCtorWhenK_majorInductiveMiss, + { root := ``Ix.Kernel.RecM.synthCtorWhenK_majorInductiveMiss, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.synthCtorWhenK_majorInductiveError, + { root := ``Ix.Kernel.RecM.synthCtorWhenK_majorInductiveError, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.SynthCtorWhenKSelectionTrace.eval, + { root := ``Ix.Kernel.RecM.SynthCtorWhenKSelectionTrace.eval, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.SynthCtorWhenKSelectionTrace.mismatch, + { root := ``Ix.Kernel.RecM.SynthCtorWhenKSelectionTrace.mismatch, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.SynthCtorWhenKSelectionTrace.missing, + { root := ``Ix.Kernel.RecM.SynthCtorWhenKSelectionTrace.missing, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.SynthCtorWhenKSelectionTrace.nonInductive, + { root := ``Ix.Kernel.RecM.SynthCtorWhenKSelectionTrace.nonInductive, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.SynthCtorWhenKSelectionTrace.empty, + { root := ``Ix.Kernel.RecM.SynthCtorWhenKSelectionTrace.empty, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.SynthCtorWhenKSelectionTrace.selected, + { root := ``Ix.Kernel.RecM.SynthCtorWhenKSelectionTrace.selected, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.SynthCtorWhenKSelectionTrace.selectedError, + { root := ``Ix.Kernel.RecM.SynthCtorWhenKSelectionTrace.selectedError, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kMajorInferRawError, + { root := ``Ix.Kernel.AmbientNat.kMajorInferRawError, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kMajorInferCaughtMiss, + { root := ``Ix.Kernel.AmbientNat.kMajorInferCaughtMiss, standardAxioms := standard, nativeAxioms := inferNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kCandidateInferRawError, + { root := ``Ix.Kernel.AmbientNat.kCandidateInferRawError, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kCandidateInferCaughtMiss, + { root := ``Ix.Kernel.AmbientNat.kCandidateInferCaughtMiss, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kDefEqRawError, + { root := ``Ix.Kernel.AmbientNat.kDefEqRawError, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kDefEqCandidateError, + { root := ``Ix.Kernel.AmbientNat.kDefEqCandidateError, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kDefEqSynthError, + { root := ``Ix.Kernel.AmbientNat.kDefEqSynthError, standardAxioms := standard, nativeAxioms := inferNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kEmptyGetRec, + { root := ``Ix.Kernel.AmbientNat.kEmptyGetRec, standardAxioms := standard, nativeAxioms := canonicalPrimitivesNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kEmptyGetNat, + { root := ``Ix.Kernel.AmbientNat.kEmptyGetNat, standardAxioms := standard, nativeAxioms := canonicalPrimitivesNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kEmptyMajorInductive, + { root := ``Ix.Kernel.AmbientNat.kEmptyMajorInductive, standardAxioms := standard, nativeAxioms := canonicalPrimitivesContextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.kEmptyInductiveMiss, + { root := ``Ix.Kernel.AmbientNat.kEmptyInductiveMiss, standardAxioms := standard, nativeAxioms := inferNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, @@ -6382,270 +6366,270 @@ private def roots : Array RootAllowance := #[ -- StructEtaControl: exhaustive struct-eta classification, caught probes, rebuild, -- single-rule selection, and final constructor fallthrough. Rebuilding is -- proved total; only universe instantiation can produce a post-guard error. - { root := ``Ix.Tc.RecM.isStructLike_missing, - standardAxioms := standard, nativeAxioms := blake3ContextNative, + { root := ``Ix.Kernel.RecM.isStructLike_missing, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isStructLike_nonInductive, - standardAxioms := standard, nativeAxioms := blake3ContextNative, + { root := ``Ix.Kernel.RecM.isStructLike_nonInductive, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isStructLike_lookupError, - standardAxioms := standard, nativeAxioms := blake3ContextNative, + { root := ``Ix.Kernel.RecM.isStructLike_lookupError, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isStructLike_badShape, - standardAxioms := standard, nativeAxioms := blake3ContextNative, + { root := ``Ix.Kernel.RecM.isStructLike_badShape, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isStructLike_shapeQualified, - standardAxioms := standard, nativeAxioms := blake3ContextNative, + { root := ``Ix.Kernel.RecM.isStructLike_shapeQualified, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isStructLike_recError, - standardAxioms := standard, nativeAxioms := blake3ContextNative, + { root := ``Ix.Kernel.RecM.isStructLike_recError, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.finishStructEtaResult_empty, + { root := ``Ix.Kernel.RecM.finishStructEtaResult_empty, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.structEtaIntern_total, + { root := ``Ix.Kernel.RecM.structEtaIntern_total, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.finishStructEtaFields_total, + { root := ``Ix.Kernel.RecM.finishStructEtaFields_total, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.finishStructEtaResult_of_segments, + { root := ``Ix.Kernel.RecM.finishStructEtaResult_of_segments, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.finishStructEtaResult_total, + { root := ``Ix.Kernel.RecM.finishStructEtaResult_total, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.finishStructEtaResult_ne_error, + { root := ``Ix.Kernel.RecM.finishStructEtaResult_ne_error, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.finishStructEtaAfterSort_prop, + { root := ``Ix.Kernel.RecM.finishStructEtaAfterSort_prop, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.finishStructEtaAfterSort_success, + { root := ``Ix.Kernel.RecM.finishStructEtaAfterSort_success, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.finishStructEtaAfterSort_instantiateError, + { root := ``Ix.Kernel.RecM.finishStructEtaAfterSort_instantiateError, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.finishStructEtaAfterSort_finishError, + { root := ``Ix.Kernel.RecM.finishStructEtaAfterSort_finishError, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaAfterInductive_notStruct, + { root := ``Ix.Kernel.RecM.tryStructEtaAfterInductive_notStruct, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaAfterInductive_structError, + { root := ``Ix.Kernel.RecM.tryStructEtaAfterInductive_structError, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaAfterInductive_majorInferMiss, + { root := ``Ix.Kernel.RecM.tryStructEtaAfterInductive_majorInferMiss, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaAfterInductive_majorInferError, + { root := ``Ix.Kernel.RecM.tryStructEtaAfterInductive_majorInferError, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaAfterInductive_sortInferMiss, + { root := ``Ix.Kernel.RecM.tryStructEtaAfterInductive_sortInferMiss, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaAfterInductive_sortInferError, + { root := ``Ix.Kernel.RecM.tryStructEtaAfterInductive_sortInferError, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaAfterInductive_sortWhnfMiss, + { root := ``Ix.Kernel.RecM.tryStructEtaAfterInductive_sortWhnfMiss, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaAfterInductive_sortWhnfError, + { root := ``Ix.Kernel.RecM.tryStructEtaAfterInductive_sortWhnfError, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructEtaProbeTrace.eval, + { root := ``Ix.Kernel.RecM.StructEtaProbeTrace.eval, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructEtaProbeTrace.prop, + { root := ``Ix.Kernel.RecM.StructEtaProbeTrace.prop, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructEtaProbeTrace.success, + { root := ``Ix.Kernel.RecM.StructEtaProbeTrace.success, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructEtaProbeTrace.finishError, + { root := ``Ix.Kernel.RecM.StructEtaProbeTrace.finishError, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaIota_ruleCount, + { root := ``Ix.Kernel.RecM.tryStructEtaIota_ruleCount, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaIota_recursorMissing, + { root := ``Ix.Kernel.RecM.tryStructEtaIota_recursorMissing, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaIota_recursorError, + { root := ``Ix.Kernel.RecM.tryStructEtaIota_recursorError, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaIota_majorInductiveMiss, + { root := ``Ix.Kernel.RecM.tryStructEtaIota_majorInductiveMiss, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaIota_majorInductiveError, + { root := ``Ix.Kernel.RecM.tryStructEtaIota_majorInductiveError, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructEtaSelectionTrace.eval, + { root := ``Ix.Kernel.RecM.StructEtaSelectionTrace.eval, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructEtaIotaSuccessTrace.eval, + { root := ``Ix.Kernel.RecM.StructEtaIotaSuccessTrace.eval, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructEtaIotaSuccessTrace.acceptance, + { root := ``Ix.Kernel.RecM.StructEtaIotaSuccessTrace.acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- Rebuild: the successful struct-eta rebuild derives its invariant, frame, -- and finite support from the exact projection/application request list. -- The registered Theory equation remains an explicit premise. - { root := ``Ix.Tc.RecM.StructEtaFieldRequests.support, + { root := ``Ix.Kernel.RecM.StructEtaFieldRequests.support, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructEtaFieldRequests.eval, + { root := ``Ix.Kernel.RecM.StructEtaFieldRequests.eval, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructEtaBuildRequests.eval, + { root := ``Ix.Kernel.RecM.StructEtaBuildRequests.eval, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructEtaIotaSuccessTrace.acceptance_of_requests, + { root := ``Ix.Kernel.RecM.StructEtaIotaSuccessTrace.acceptance_of_requests, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- CallbackPrefix: the exact infer-only and optional-catch wrappers preserve the -- complete fixed-world invariant while retaining callback mutations. - { root := ``Ix.Tc.TcM.withInferOnly_eq, + { root := ``Ix.Kernel.TcM.withInferOnly_eq, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.withInferOnly_whnf_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.TcM.withInferOnly_whnf_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.inferOnlyRec_run, + { root := ``Ix.Kernel.RecM.inferOnlyRec_run, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryOptional_run, + { root := ``Ix.Kernel.RecM.tryOptional_run, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryOptional_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.tryOptional_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.inferOnlyRec_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.inferOnlyRec_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryOptionalInferOnlyRec_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.tryOptionalInferOnlyRec_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryOptionalWhnfRec_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.tryOptionalWhnfRec_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, -- RecursionClassifier: recursion classification now owns its complete concrete state -- transaction. Both physical writes require explicit provenance; the -- final write is indexed by the exact classifier execution, and only -- errors inside that classifier enter the erase-and-rethrow handler. - { root := ``Ix.Tc.CacheInvariant.insertIsRec, + { root := ``Ix.Kernel.CacheInvariant.insertIsRec, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.CacheInvariant.eraseIsRec, + { root := ``Ix.Kernel.CacheInvariant.eraseIsRec, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.IsRecCacheUpdate.insert_whnfStateInv, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.IsRecCacheUpdate.insert_whnfStateInv, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.IsRecCacheUpdate.erase_whnfStateInv, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.IsRecCacheUpdate.erase_whnfStateInv, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.IsRecCacheWriteOracle.of_trusted, - standardAxioms := standard, nativeAxioms := blake3ContextNative, + { root := ``Ix.Kernel.RecM.IsRecCacheWriteOracle.of_trusted, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.getConst_wf, + { root := ``Ix.Kernel.TcM.getConst_wf, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.tryGetBlock_wf, + { root := ``Ix.Kernel.TcM.tryGetBlock_wf, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.WhnfCallbackSupports.preserves, + { root := ``Ix.Kernel.RecM.WhnfCallbackSupports.preserves, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.getMajorInductiveId_wf, - standardAxioms := standard, nativeAxioms := blake3ContextNative, + { root := ``Ix.Kernel.RecM.getMajorInductiveId_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.collectSpine_const_references, + { root := ``Ix.Kernel.RecM.collectSpine_const_references, standardAxioms := propextOnly, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.getMajorInductiveId_trusted_wf, - standardAxioms := standard, nativeAxioms := blake3ContextNative, + { root := ``Ix.Kernel.RecM.getMajorInductiveId_trusted_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.discoverBlockInductives_wf, + { root := ``Ix.Kernel.RecM.discoverBlockInductives_wf, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.computeIsRec_wf, - standardAxioms := standard, nativeAxioms := blake3ContextNative, + { root := ``Ix.Kernel.RecM.computeIsRec_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.cacheIsRec_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.cacheIsRec_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.eraseCachedIsRec_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.eraseCachedIsRec_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.computedIsRecClassify_wf, - standardAxioms := standard, nativeAxioms := blake3ContextNative, + { root := ``Ix.Kernel.RecM.computedIsRecClassify_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.computedIsRecMiss_wf, - standardAxioms := standard, nativeAxioms := blake3ContextNative, + { root := ``Ix.Kernel.RecM.computedIsRecMiss_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.computedIsRec_wf, - standardAxioms := standard, nativeAxioms := blake3ContextNative, + { root := ``Ix.Kernel.RecM.computedIsRec_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, -- Classifier: compose the recursion classifier through `isStructLike`, then -- exhaust the single-rule recursor lookup and all three caught struct-eta -- probes. Only the explicitly parameterized cache-write, callback, and -- successful universe/rebuild authorities remain outside these proofs. - { root := ``Ix.Tc.RecM.isStructLike_wf, - standardAxioms := standard, nativeAxioms := blake3ContextNative, + { root := ``Ix.Kernel.RecM.isStructLike_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryOptional_state_wf, + { root := ``Ix.Kernel.RecM.tryOptional_state_wf, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryOptional_fixed_wf, + { root := ``Ix.Kernel.RecM.tryOptional_fixed_wf, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaAfterInductive_wf, + { root := ``Ix.Kernel.RecM.tryStructEtaAfterInductive_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaIota_prefix_wf, + { root := ``Ix.Kernel.RecM.tryStructEtaIota_prefix_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaIota_trusted_prefix_wf, + { root := ``Ix.Kernel.RecM.tryStructEtaIota_trusted_prefix_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructEtaIota_wf, + { root := ``Ix.Kernel.RecM.tryStructEtaIota_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- RebuildTail: the universe-instantiation/rebuild tail now preserves the complete -- invariant from the finite execution request census, including retained -- intern-table updates on a non-backtracking walker error. - { root := ``Ix.Tc.TcM.instantiateUnivParams_whnf_wf, + { root := ``Ix.Kernel.TcM.instantiateUnivParams_whnf_wf, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructEtaBuildRequests.wf, + { root := ``Ix.Kernel.RecM.StructEtaBuildRequests.wf, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.finishStructEtaAfterSort_wf_of_requests, + { root := ``Ix.Kernel.RecM.finishStructEtaAfterSort_wf_of_requests, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, -- CacheShell: both structural-core cache partitions now have explicit -- collision-robust write authority, and the actual public dispatcher is -- closed conditionally on the remaining exhaustive structural step. - { root := ``Ix.Tc.RecM.WhnfCoreCacheWriteOracle.closed, + { root := ``Ix.Kernel.RecM.WhnfCoreCacheWriteOracle.closed, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfSuffixModel.coreCacheWriteOracle, + { root := ``Ix.Kernel.WhnfSuffixModel.coreCacheWriteOracle, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsNonLeaf_wf, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsNonLeaf_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlags_wf, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlags_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -6654,81 +6638,81 @@ private def roots : Array RootAllowance := #[ -- substitution now share one local structural-step contract. The fvar -- theorem exposes the real unchanged-value safety invariant rather than -- inferring closedness or arithmetic bounds from translation alone. - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_fvar_wf, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_fvar_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_letE_wf, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_letE_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_basic_wf, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_basic_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- VariableStep: legacy zeta now derives its semantic weakening from the exact -- lift-walker bounds. The only additional safety fact is the real -- UInt64 `idx + 1` no-wrap condition on an actual let-value hit. - { root := ``Ix.Tc.CtxRecon.lookupLetVal_liftBounds, + { root := ``Ix.Kernel.CtxRecon.lookupLetVal_liftBounds, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.lookupLetVal_noLet, + { root := ``Ix.Kernel.TcM.lookupLetVal_noLet, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfMeaning.zetaVar_liftBounds, + { root := ``Ix.Kernel.WhnfMeaning.zetaVar_liftBounds, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_var_wf, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_var_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_basicVar_wf, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_basicVar_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- RecursiveCallbacks: projection values and application-spine children now have an -- explicit finite-support boundary, and both recursive head callbacks are -- instantiated directly from the predecessor method-table contract. - { root := ``Ix.Tc.RecM.whnfCoreFlagsRec_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.whnfCoreFlagsRec_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrAppSpine.headTr, + { root := ``Ix.Kernel.RecM.TrAppSpine.headTr, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.projectionValueCallback_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.projectionValueCallback_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.applicationHeadCallback_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.applicationHeadCallback_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.applicationArgument_support, + { root := ``Ix.Kernel.RecM.applicationArgument_support, standardAxioms := propextOnly, forbiddenDependencies := legacyWholeEnv }, -- ProjectionStep: all projection-step outcomes now satisfy the local structural -- contract once the exact helper effect/result boundary is instantiated; -- callback and helper errors retain their partial post-state. - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_projection_wf, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_projection_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_basicVarProjection_wf, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_basicVarProjection_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- ApplicationCongruence: the application-head callback is tied to the exact typed suffix, -- and Theory application congruence transports head reduction across every -- argument rebuilt by the finite production certificate. - { root := ``Ix.Tc.RecM.TrAppSpine.toSuffix, + { root := ``Ix.Kernel.RecM.TrAppSpine.toSuffix, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.applicationHeadCallbackWithSuffix_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.applicationHeadCallbackWithSuffix_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.WhnfMeaning.appHeadRebuild, + { root := ``Ix.Kernel.RecM.WhnfMeaning.appHeadRebuild, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, -- ApplicationRebuild: a finite census now executes each dynamic changed-head rebuild and -- returns its exact intern frame, support, and transported Theory meaning. - { root := ``Ix.Tc.RecM.changedHeadFinish_acceptance, + { root := ``Ix.Kernel.RecM.changedHeadFinish_acceptance, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -6736,13 +6720,13 @@ private def roots : Array RootAllowance := #[ -- ApplicationTails: both non-beta application tails are exhaustive over iota hit, -- miss, and error. Changed-head hits compose rebuild congruence with the -- helper result; unchanged misses remain reflexive at the original source. - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_appUnchangedIota, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_appUnchangedIota, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_appUnchanged_wf, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_appUnchanged_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfCoreWithFlagsStep_appChanged_wf, + { root := ``Ix.Kernel.RecM.whnfCoreWithFlagsStep_appChanged_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -6752,48 +6736,48 @@ private def roots : Array RootAllowance := #[ -- and derives selected-field support from the concrete collected spine. -- Only String preprocessing and the installed lazy-ingress hook remain at -- the public helper constructor. - { root := ``Ix.Tc.RecM.WhnfCoreInputSupport.spineArg, + { root := ``Ix.Kernel.RecM.WhnfCoreInputSupport.spineArg, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryProjReduceTail_noAccel_wf, + { root := ``Ix.Kernel.RecM.tryProjReduceTail_noAccel_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ProjectionPrelude.nonString, + { root := ``Ix.Kernel.RecM.ProjectionPrelude.nonString, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ProjectionPrelude.ofString, + { root := ``Ix.Kernel.RecM.ProjectionPrelude.ofString, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryProjReduce_noAccel_wf, + { root := ``Ix.Kernel.RecM.tryProjReduce_noAccel_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ProjectionHelper.noAccel, + { root := ``Ix.Kernel.RecM.ProjectionHelper.noAccel, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ProjectionStringPrelude.ofExpansion, + { root := ``Ix.Kernel.RecM.ProjectionStringPrelude.ofExpansion, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ProjectionHelper.noAccelOfExpansion, + { root := ``Ix.Kernel.RecM.ProjectionHelper.noAccelOfExpansion, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, -- StringExpansion: the remaining String-expansion premise is reduced to a pure, -- finite plan. The actual primitive read, seven prefix interns, recursive -- character fold, and final intern preserve the complete K1 invariant and -- return the exact structurally translated generated expression. - { root := ``Ix.Tc.RecM.strLitListToConstructor_plan_wf, + { root := ``Ix.Kernel.RecM.strLitListToConstructor_plan_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.strLitToConstructorWithPrimitives_plan_wf, + ``Ix.Kernel.RecM.strLitToConstructorWithPrimitives_plan_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.strLitToConstructor_plan_wf, + { root := ``Ix.Kernel.RecM.strLitToConstructor_plan_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ProjectionStringExpansion.ofPlans, + { root := ``Ix.Kernel.RecM.ProjectionStringExpansion.ofPlans, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ProjectionHelper.noAccelOfStringPlans, + { root := ``Ix.Kernel.RecM.ProjectionHelper.noAccelOfStringPlans, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, -- LazyIngress: instantiate the generic lazy-fault plumbing with production's @@ -6801,37 +6785,37 @@ private def roots : Array RootAllowance := #[ -- covers a successful load, an absent address, and an error-carried partial -- environment; hook identity remains visible because `TcState.lazyFault` -- otherwise stores an arbitrary function. - { root := ``Ix.Tc.LazyIngressEnvFrame.refl, + { root := ``Ix.Kernel.LazyIngressEnvFrame.refl, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.LazyIngressEnvFrame.kernelStateWF, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.LazyIngressEnvFrame.kernelStateWF, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.LazyIngressEnvFrame.ctxRecon, + { root := ``Ix.Kernel.LazyIngressEnvFrame.ctxRecon, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.LazyIngressEnvFrame.whnfStateInv, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.LazyIngressEnvFrame.whnfStateInv, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.ingressAnonAddrShallow_absent, + { root := ``Ix.Kernel.ingressAnonAddrShallow_absent, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AnonIngressRefinement.absentOfVerifiedMiss, + { root := ``Ix.Kernel.AnonIngressRefinement.absentOfVerifiedMiss, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AnonIngressRefinement.error, + { root := ``Ix.Kernel.AnonIngressRefinement.error, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AnonIngressRefinement.lazyFaultPreserves, + { root := ``Ix.Kernel.AnonIngressRefinement.lazyFaultPreserves, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AnonLazyIngressContext, + { root := ``Ix.Kernel.AnonLazyIngressContext, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AnonLazyIngressContext.preserves, + { root := ``Ix.Kernel.AnonLazyIngressContext.preserves, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.ProjectionHelper.noAccelOfAnonIngress, + { root := ``Ix.Kernel.RecM.ProjectionHelper.noAccelOfAnonIngress, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, -- NatOffset: the actual post-major iota preprocessing path. Bounded Nat-offset @@ -6839,64 +6823,64 @@ private def roots : Array RootAllowance := #[ -- finite String expansion, the policy-selected recursive callback, and the -- constructor/struct-eta dispatch all preserve the complete K1 invariant. -- Only the ordinary-constructor and struct-eta tails remain named inputs. - { root := ``Ix.Tc.RecM.prims_state_wf, + { root := ``Ix.Kernel.RecM.prims_state_wf, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isNatBinArithAddr_state_wf, + { root := ``Ix.Kernel.RecM.isNatBinArithAddr_state_wf, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.natOffsetReaders_state_wf, + { root := ``Ix.Kernel.RecM.natOffsetReaders_state_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.natOffset_state_wf, + { root := ``Ix.Kernel.RecM.natOffset_state_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.evalNatOffsetLiteral_state_wf, + { root := ``Ix.Kernel.RecM.evalNatOffsetLiteral_state_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.natToConstructor_state_wf, + { root := ``Ix.Kernel.RecM.natToConstructor_state_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.mkNatSucc_state_wf, + { root := ``Ix.Kernel.RecM.mkNatSucc_state_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.mkNatAdd_state_wf, + { root := ``Ix.Kernel.RecM.mkNatAdd_state_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.WF.with_run_eq, + { root := ``Ix.Kernel.TcM.WF.with_run_eq, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.OptionalGeneratedInput, + { root := ``Ix.Kernel.RecM.OptionalGeneratedInput, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatOffsetCleanupInputOracle, - standardAxioms := standardWithoutChoice, + { root := ``Ix.Kernel.RecM.NatOffsetCleanupInputOracle, + standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.cleanupNatOffsetMajor_state_wf, + { root := ``Ix.Kernel.RecM.cleanupNatOffsetMajor_state_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.cleanupNatOffsetMajor_input_wf, + { root := ``Ix.Kernel.RecM.cleanupNatOffsetMajor_input_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TryApplyIotaCtorPreserves, + { root := ``Ix.Kernel.RecM.TryApplyIotaCtorPreserves, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructEtaIotaPreserves, + { root := ``Ix.Kernel.RecM.StructEtaIotaPreserves, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.SelectedStructEtaIotaPreserves, + { root := ``Ix.Kernel.RecM.SelectedStructEtaIotaPreserves, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaCtorOrStructEta_state_wf, + { root := ``Ix.Kernel.RecM.tryIotaCtorOrStructEta_state_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.strLitToConstructor_context_wf, + { root := ``Ix.Kernel.RecM.strLitToConstructor_context_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaAfterCleanup_state_wf, + { root := ``Ix.Kernel.RecM.tryIotaAfterCleanup_state_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaAfterMajorWhnf_state_wf, + { root := ``Ix.Kernel.RecM.tryIotaAfterMajorWhnf_state_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- ApplicationRequests--Ingress: finite ordinary-iota, struct-eta, and K-synthesis request @@ -6904,123 +6888,125 @@ private def roots : Array RootAllowance := #[ -- exhausts the actual tryIotaWithFlags state path through lazy lookup, -- caught probes, both cleanup stages, policy-selected major callbacks, -- statistics updates, and the final uncaught DefEq callback. - { root := ``Ix.Tc.RecM.IotaArgsInternRequests, + { root := ``Ix.Kernel.RecM.IotaArgsInternRequests, standardAxioms := standardWithoutQuot, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.IotaArgsInternRequests.wfList, + { root := ``Ix.Kernel.RecM.IotaArgsInternRequests.wfList, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.IotaArgsInternRequests.wfArray, + { root := ``Ix.Kernel.RecM.IotaArgsInternRequests.wfArray, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.applyIotaArg_true_state_wf, + { root := ``Ix.Kernel.RecM.applyIotaArg_true_state_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.applyIotaArgs_true_state_wf, + { root := ``Ix.Kernel.RecM.applyIotaArgs_true_state_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.IotaRuleRequests, + { root := ``Ix.Kernel.RecM.IotaRuleRequests, + standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.IotaRuleRequestCensus, + { root := ``Ix.Kernel.RecM.IotaRuleRequestCensus, + standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.applyIotaRule_state_wf_of_requests, + { root := ``Ix.Kernel.RecM.applyIotaRule_state_wf_of_requests, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryApplyIotaCtor_state_wf_of_requests, + { root := ``Ix.Kernel.RecM.tryApplyIotaCtor_state_wf_of_requests, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TryApplyIotaCtorPreserves.of_requests, + { root := ``Ix.Kernel.RecM.TryApplyIotaCtorPreserves.of_requests, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructEtaFinishRequests, + { root := ``Ix.Kernel.RecM.StructEtaFinishRequests, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructEtaFinishRequestCensus, + { root := ``Ix.Kernel.RecM.StructEtaFinishRequestCensus, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructEtaFinishPreserves.of_requests, + { root := ``Ix.Kernel.RecM.StructEtaFinishPreserves.of_requests, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructEtaIotaPreserves.of_components, + { root := ``Ix.Kernel.RecM.StructEtaIotaPreserves.of_components, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaAfterMajorWhnf_state_wf_of_contexts, + { root := ``Ix.Kernel.RecM.tryIotaAfterMajorWhnf_state_wf_of_contexts, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.IsDefEqCallbackPreserves, + { root := ``Ix.Kernel.RecM.IsDefEqCallbackPreserves, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.WF.tryFinally_const, + { root := ``Ix.Kernel.TcM.WF.tryFinally_const, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.enterDispatch_whnf_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.enterDispatch_whnf_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.exitDispatch_whnf_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.exitDispatch_whnf_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.callIsDefEq_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.callIsDefEq_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.KSynthCandidateRequests, + { root := ``Ix.Kernel.RecM.KSynthCandidateRequests, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.KSynthCandidateRequestCensus, + { root := ``Ix.Kernel.RecM.KSynthCandidateRequestCensus, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.KSynthCandidateInputs, + { root := ``Ix.Kernel.RecM.KSynthCandidateInputs, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.KSynthCandidateInputOracle, - standardAxioms := standardWithoutChoice, + { root := ``Ix.Kernel.RecM.KSynthCandidateInputOracle, + standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.FinishAppRequests.state_wf, + { root := ``Ix.Kernel.RecM.FinishAppRequests.state_wf, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.verifyKSynthCandidate_state_wf_of_requests, + { root := ``Ix.Kernel.RecM.verifyKSynthCandidate_state_wf_of_requests, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.selectKSynthCandidate_state_wf_of_requests, + { root := ``Ix.Kernel.RecM.selectKSynthCandidate_state_wf_of_requests, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.synthCtorWhenK_state_wf_of_requests, + { root := ``Ix.Kernel.RecM.synthCtorWhenK_state_wf_of_requests, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.verifyKSynthCandidate_state_wf_of_inputs, + { root := ``Ix.Kernel.RecM.verifyKSynthCandidate_state_wf_of_inputs, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.selectKSynthCandidate_state_wf_of_inputs, + { root := ``Ix.Kernel.RecM.selectKSynthCandidate_state_wf_of_inputs, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.synthCtorWhenK_state_wf_of_inputs, + { root := ``Ix.Kernel.RecM.synthCtorWhenK_state_wf_of_inputs, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaWithFlags_state_wf_of_contexts, + { root := ``Ix.Kernel.RecM.tryIotaWithFlags_state_wf_of_contexts, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- OptionalReduction: the exhaustive state proof and the direct admission-owned success -- boundary assemble the ordinary optional-reduction contract. The success -- boundary contributes support and Theory meaning only; it cannot hide an -- error-side or miss-side state assumption. - { root := ``Ix.Tc.IotaCallbackFrameOracle, + { root := ``Ix.Kernel.IotaCallbackFrameOracle, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.IotaSuccessOracle, + { root := ``Ix.Kernel.IotaSuccessOracle, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaWithFlags_optional_wf_of_contexts, + { root := ``Ix.Kernel.RecM.tryIotaWithFlags_optional_wf_of_contexts, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- Reducer: the structural contract is indexed by the actual universe/context -- represented by the cache model. The assembled theorem feeds OptionalReduction into -- the exhaustive syntax step and then through the bounded/cache driver. - { root := ``Ix.Tc.StructuralReduction.WF, + { root := ``Ix.Kernel.StructuralReduction.WF, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructuralCoreContext, + { root := ``Ix.Kernel.RecM.StructuralCoreContext, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.StructuralCoreContext.wf, + { root := ``Ix.Kernel.RecM.StructuralCoreContext.wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -7029,33 +7015,33 @@ private def roots : Array RootAllowance := #[ -- and successful projection followed by a certified complete-spine -- rebuild. Head meaning is transported through the typed suffix rather -- than inferred from expression-address equality. - { root := ``Ix.Tc.RecM.tryProjAppReduce_empty, + { root := ``Ix.Kernel.RecM.tryProjAppReduce_empty, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryProjAppReduce_notProjection, + { root := ``Ix.Kernel.RecM.tryProjAppReduce_notProjection, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryProjAppReduce_projectionWhnfError, + { root := ``Ix.Kernel.RecM.tryProjAppReduce_projectionWhnfError, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryProjAppReduce_projectionReduceError, + { root := ``Ix.Kernel.RecM.tryProjAppReduce_projectionReduceError, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryProjAppReduce_projectionNone, + { root := ``Ix.Kernel.RecM.tryProjAppReduce_projectionNone, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryProjAppReduce_projectionSome, + { root := ``Ix.Kernel.RecM.tryProjAppReduce_projectionSome, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryProjAppReduceFinished_empty_wf, + { root := ``Ix.Kernel.RecM.tryProjAppReduceFinished_empty_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryProjAppReduceFinished_app_optional_wf, + { root := ``Ix.Kernel.RecM.tryProjAppReduceFinished_app_optional_wf, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.tryProjAppReduceFinished_optional_wf_of_contexts, + ``Ix.Kernel.RecM.tryProjAppReduceFinished_optional_wf_of_contexts, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -7063,38 +7049,39 @@ private def roots : Array RootAllowance := #[ -- classifier miss and all three hits. Its state proof derives finite -- generated-node support at each intern; the reflection boundary owns -- only Theory meaning for an observed successful run. - { root := ``Ix.Tc.StringReductionSupport, + { root := ``Ix.Kernel.StringReductionSupport, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.StringReductionReflection, + { root := ``Ix.Kernel.StringReductionReflection, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceString_inv_wf, + { root := ``Ix.Kernel.RecM.tryReduceString_inv_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceString_optional_wf_of_reflection, + { root := ``Ix.Kernel.RecM.tryReduceString_optional_wf_of_reflection, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, -- ProjectionDefinition: projection-wrapper reduction covers the real lazy constant lookup, -- the generated projection, and every suffix intern. The request plan -- exposes all intermediate support obligations instead of assuming that -- support for the final node retroactively makes those interns safe. - { root := ``Ix.Tc.ProjectionDefinitionRequestCensus, + { root := ``Ix.Kernel.ProjectionDefinitionRequestCensus, + standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.ProjectionDefinitionReflection, + { root := ``Ix.Kernel.ProjectionDefinitionReflection, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.projectionDefinitionFinish_eq, + { root := ``Ix.Kernel.RecM.projectionDefinitionFinish_eq, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.FinishAppRequests.finishAppResult_wf, + { root := ``Ix.Kernel.RecM.FinishAppRequests.finishAppResult_wf, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceProjectionDefinition_inv_wf, + { root := ``Ix.Kernel.RecM.tryReduceProjectionDefinition_inv_wf, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.tryReduceProjectionDefinition_optional_wf_of_contexts, + ``Ix.Kernel.RecM.tryReduceProjectionDefinition_optional_wf_of_contexts, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, -- Quotient: quotient reduction derives the selected major's support and @@ -7105,83 +7092,83 @@ private def roots : Array RootAllowance := #[ -- laws. Ix owns the complete dynamic trace, exact lift/ind layouts, -- normalized `Quot.mk` transport, collision-free base intern, and suffix -- reconstruction. - { root := ``Ix.Tc.QuotientReductionRequestCensus, + { root := ``Ix.Kernel.QuotientReductionRequestCensus, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.QuotientReductionReflection, + { root := ``Ix.Kernel.QuotientReductionReflection, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.QuotientReductionLaws, + { root := ``Ix.Kernel.QuotientReductionLaws, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrAppSpine.three, + { root := ``Ix.Kernel.RecM.TrAppSpine.three, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrAppSpine.four, + { root := ``Ix.Kernel.RecM.TrAppSpine.four, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrAppSpine.five, + { root := ``Ix.Kernel.RecM.TrAppSpine.five, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrKExprS.const_name, + { root := ``Ix.Kernel.RecM.TrKExprS.const_name, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.quotientLiftMeaning, + { root := ``Ix.Kernel.RecM.quotientLiftMeaning, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.quotientIndMeaning, + { root := ``Ix.Kernel.RecM.quotientIndMeaning, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.QuotientSelectedSuccessTrace.complete, + { root := ``Ix.Kernel.RecM.QuotientSelectedSuccessTrace.complete, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.QuotientSelectedSuccessTrace.semanticInputs, + { root := ``Ix.Kernel.RecM.QuotientSelectedSuccessTrace.semanticInputs, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.QuotientSelectedSuccessTrace.liftMeaning, + { root := ``Ix.Kernel.RecM.QuotientSelectedSuccessTrace.liftMeaning, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.QuotientSelectedSuccessTrace.indMeaning, + { root := ``Ix.Kernel.RecM.QuotientSelectedSuccessTrace.indMeaning, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.QuotientReductionReflection.of_laws, + { root := ``Ix.Kernel.QuotientReductionReflection.of_laws, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryQuotReduceSelected, + { root := ``Ix.Kernel.RecM.tryQuotReduceSelected, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryQuotReduceSelected_inv_wf, + { root := ``Ix.Kernel.RecM.tryQuotReduceSelected_inv_wf, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryQuotReduce_inv_wf, + { root := ``Ix.Kernel.RecM.tryQuotReduce_inv_wf, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryQuotReduce_optional_wf_of_contexts, + { root := ``Ix.Kernel.RecM.tryQuotReduce_optional_wf_of_contexts, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, -- BaseReductions: the five active no-acceleration reducers are assembled into the -- exact production base oracle for either successor policy. Native and -- BitVec remain independently discharged by the no-acceleration gate. - { root := ``Ix.Tc.RecM.NoDeltaBaseContext, + { root := ``Ix.Kernel.RecM.NoDeltaBaseContext, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NoDeltaBaseContext.oracle, + { root := ``Ix.Kernel.RecM.NoDeltaBaseContext.oracle, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, -- Reducer: Reducer's structural reducer and BaseReductions's active base oracle now feed -- the real bounded, keyed, transient-aware, cache-writing public -- `whnfNoDeltaImpl` shell for every flag and successor policy. - { root := ``Ix.Tc.RecM.NoDeltaDriverContext, + { root := ``Ix.Kernel.RecM.NoDeltaDriverContext, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NoDeltaDriverContext.wf, + { root := ``Ix.Kernel.RecM.NoDeltaDriverContext.wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -7191,279 +7178,279 @@ private def roots : Array RootAllowance := #[ -- the bare fallback closes `deltaUnfoldOne`. The final cache composition -- and method knot are indexed by the active universe count; concrete lazy -- ingress is carried by `AnonLazyIngressContext`, not a free callback. - { root := ``Ix.Tc.OptionalReduction.WFAt, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.OptionalReduction.WFAt, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TrustedDeltaBody.meaning, + { root := ``Ix.Kernel.TrustedDeltaBody.meaning, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.StableWhnfTheory, + { root := ``Ix.Kernel.StableWhnfTheory, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TrustedDeltaBody.unfoldCacheProvenance, + { root := ``Ix.Kernel.TrustedDeltaBody.unfoldCacheProvenance, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.unfoldConstValue_trusted_wf, + { root := ``Ix.Kernel.RecM.unfoldConstValue_trusted_wf, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TrustedDeltaCensus, - standardAxioms := standard, + { root := ``Ix.Kernel.TrustedDeltaCensus, + standardAxioms := standard, nativeAxioms := univOnlyNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryDeltaUnfold_trusted_wf, + { root := ``Ix.Kernel.RecM.tryDeltaUnfold_trusted_wf, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.deltaUnfoldOne_trusted_wf, + { root := ``Ix.Kernel.RecM.deltaUnfoldOne_trusted_wf, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrustedDeltaContext, + { root := ``Ix.Kernel.RecM.TrustedDeltaContext, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrustedDeltaContext.wfAt, + { root := ``Ix.Kernel.RecM.TrustedDeltaContext.wfAt, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.FullWhnfStepContext.ofTrustedDelta, + { root := ``Ix.Kernel.RecM.FullWhnfStepContext.ofTrustedDelta, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.Methods.WhnfClosedAt, + { root := ``Ix.Kernel.Methods.WhnfClosedAt, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.Methods.methodsN_wfAt, + { root := ``Ix.Kernel.Methods.methodsN_wfAt, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.K1ClosureContext, + { root := ``Ix.Kernel.RecM.K1ClosureContext, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.K1ClosureContext.closedAt, + { root := ``Ix.Kernel.RecM.K1ClosureContext.closedAt, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.AmbientNat.structEtaInferOnlyRun, + { root := ``Ix.Kernel.AmbientNat.structEtaInferOnlyRun, standardAxioms := standard, nativeAxioms := expressionNameNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.structEtaOptionalInferOnlyRun, + { root := ``Ix.Kernel.AmbientNat.structEtaOptionalInferOnlyRun, standardAxioms := standard, nativeAxioms := expressionNameNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaCtorOrStructEta_nonConst, + { root := ``Ix.Kernel.RecM.tryIotaCtorOrStructEta_nonConst, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaCtorOrStructEta_missing, + { root := ``Ix.Kernel.RecM.tryIotaCtorOrStructEta_missing, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaCtorOrStructEta_notConstructor, + { root := ``Ix.Kernel.RecM.tryIotaCtorOrStructEta_notConstructor, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaCtorOrStructEta_lookupError, + { root := ``Ix.Kernel.RecM.tryIotaCtorOrStructEta_lookupError, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryIotaCtorOrStructEta_constructor, + { root := ``Ix.Kernel.RecM.tryIotaCtorOrStructEta_constructor, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.structEtaIotaSuccess, + { root := ``Ix.Kernel.AmbientNat.structEtaIotaSuccess, standardAxioms := standard, nativeAxioms := nameContextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.structEtaBuildRequests, + { root := ``Ix.Kernel.AmbientNat.structEtaBuildRequests, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.structEtaDispatchSuccess, + { root := ``Ix.Kernel.AmbientNat.structEtaDispatchSuccess, standardAxioms := standard, nativeAxioms := nameContextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.structEtaIotaAbsent, + { root := ``Ix.Kernel.AmbientNat.structEtaIotaAbsent, standardAxioms := standard, nativeAxioms := nameContextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.structEtaIotaCaughtInferError, + { root := ``Ix.Kernel.AmbientNat.structEtaIotaCaughtInferError, standardAxioms := standard, nativeAxioms := nameContextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaCleanupOfNatValue, + { root := ``Ix.Kernel.AmbientNat.iotaCleanupOfNatValue, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaNatCleanup, + { root := ``Ix.Kernel.AmbientNat.iotaNatCleanup, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaNatCtorCleanup, + { root := ``Ix.Kernel.AmbientNat.iotaNatCtorCleanup, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaNatMajorWhnf, + { root := ``Ix.Kernel.AmbientNat.iotaNatMajorWhnf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaNatZeroExpand, + { root := ``Ix.Kernel.AmbientNat.iotaNatZeroExpand, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaNatSuccExpand, + { root := ``Ix.Kernel.AmbientNat.iotaNatSuccExpand, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaNatApplyRule, + { root := ``Ix.Kernel.AmbientNat.iotaNatApplyRule, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaNatApplyCtor, + { root := ``Ix.Kernel.AmbientNat.iotaNatApplyCtor, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaNatTryEval, + { root := ``Ix.Kernel.AmbientNat.iotaNatTryEval, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaNatStepEval, + { root := ``Ix.Kernel.AmbientNat.iotaNatStepEval, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaNatCoreEval, + { root := ``Ix.Kernel.AmbientNat.iotaNatCoreEval, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaApplyRule, + { root := ``Ix.Kernel.AmbientNat.iotaApplyRule, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaApplyCtor, + { root := ``Ix.Kernel.AmbientNat.iotaApplyCtor, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.support_le_iotaArgsSupport, + { root := ``Ix.Kernel.AmbientNat.support_le_iotaArgsSupport, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaArgsStateInv, + { root := ``Ix.Kernel.AmbientNat.iotaArgsStateInv, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaArgsSupport_head, + { root := ``Ix.Kernel.AmbientNat.iotaArgsSupport_head, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.iotaArgsSupport_source, + { root := ``Ix.Kernel.AmbientNat.iotaArgsSupport_source, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.appStuckIotaTransientThreeSegments, + { root := ``Ix.Kernel.AmbientNat.appStuckIotaTransientThreeSegments, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaFirstResult, + { root := ``Ix.Kernel.AmbientNat.multiIotaFirstResult, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaSecondResult, + { root := ``Ix.Kernel.AmbientNat.multiIotaSecondResult, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.appStuckHead_constructed, + { root := ``Ix.Kernel.AmbientNat.appStuckHead_constructed, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiBetaInner_constructed, + { root := ``Ix.Kernel.AmbientNat.multiBetaInner_constructed, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaIntermediate_constructed, + { root := ``Ix.Kernel.AmbientNat.multiIotaIntermediate_constructed, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.appStuckHead_tr_ctx, + { root := ``Ix.Kernel.AmbientNat.appStuckHead_tr_ctx, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.appStuckHead_type_ctx, + { root := ``Ix.Kernel.AmbientNat.appStuckHead_type_ctx, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaIntermediate_tr, + { root := ``Ix.Kernel.AmbientNat.multiIotaIntermediate_tr, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.support_le_multiIotaSupport, + { root := ``Ix.Kernel.AmbientNat.support_le_multiIotaSupport, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaStateInv, + { root := ``Ix.Kernel.AmbientNat.multiIotaStateInv, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaSupport_start, + { root := ``Ix.Kernel.AmbientNat.multiIotaSupport_start, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaSupport_intermediate, + { root := ``Ix.Kernel.AmbientNat.multiIotaSupport_intermediate, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaSupport_head, + { root := ``Ix.Kernel.AmbientNat.multiIotaSupport_head, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaSupport_result, + { root := ``Ix.Kernel.AmbientNat.multiIotaSupport_result, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaFirstTrace, + { root := ``Ix.Kernel.AmbientNat.multiIotaFirstTrace, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaSecondTrace, + { root := ``Ix.Kernel.AmbientNat.multiIotaSecondTrace, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaThirdTrace, + { root := ``Ix.Kernel.AmbientNat.multiIotaThirdTrace, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaTransientThreeSegments, + { root := ``Ix.Kernel.AmbientNat.multiIotaTransientThreeSegments, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaPrefixSlice, + { root := ``Ix.Kernel.AmbientNat.multiIotaPrefixSlice, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaFieldSlice, + { root := ``Ix.Kernel.AmbientNat.multiIotaFieldSlice, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaTrailingSlice, + { root := ``Ix.Kernel.AmbientNat.multiIotaTrailingSlice, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaRuleEval, + { root := ``Ix.Kernel.AmbientNat.multiIotaRuleEval, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaRuleAcceptance, + { root := ``Ix.Kernel.AmbientNat.multiIotaRuleAcceptance, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaCtorEval, + { root := ``Ix.Kernel.AmbientNat.multiIotaCtorEval, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiIotaCtorAcceptance, + { root := ``Ix.Kernel.AmbientNat.multiIotaCtorAcceptance, standardAxioms := standard, nativeAxioms := levelNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.missingRuleDescriptor, + { root := ``Ix.Kernel.AmbientNat.missingRuleDescriptor, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.AmbientNat.missingRuleDescriptor_noZeroRule, + { root := ``Ix.Kernel.AmbientNat.missingRuleDescriptor_noZeroRule, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.AmbientNat.multiBetaMiddleSplit, + { root := ``Ix.Kernel.AmbientNat.multiBetaMiddleSplit, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.multiBetaMiddleRebase, + { root := ``Ix.Kernel.AmbientNat.multiBetaMiddleRebase, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.linearRecPartsRun, + { root := ``Ix.Kernel.AmbientNat.linearRecPartsRun, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.AmbientNat.linearRecPartsTrace, + { root := ``Ix.Kernel.AmbientNat.linearRecPartsTrace, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.TcM.LazyFaultPreserves.of_none, + { root := ``Ix.Kernel.TcM.LazyFaultPreserves.of_none, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.natRecLiteralParts_wf, + { root := ``Ix.Kernel.RecM.natRecLiteralParts_wf, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.NatRecLiteralPartsPreserves.of_lazy, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.NatRecLiteralPartsPreserves.of_lazy, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatRecLiteralPartsPreserves.eager, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.NatRecLiteralPartsPreserves.eager, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isNatLiteralRecursorApp_wf, + { root := ``Ix.Kernel.RecM.isNatLiteralRecursorApp_wf, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.isTransientNatLiteralWork_wf, + { root := ``Ix.Kernel.RecM.isTransientNatLiteralWork_wf, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.TransientNatWork.preserving, + { root := ``Ix.Kernel.RecM.TransientNatWork.preserving, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isTransientNatLiteralWork_noLazy, + { root := ``Ix.Kernel.RecM.isTransientNatLiteralWork_noLazy, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.TransientNatWork.eager, + { root := ``Ix.Kernel.RecM.TransientNatWork.eager, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, @@ -7472,100 +7459,100 @@ private def roots : Array RootAllowance := #[ -- every partial error state. The semantic package composes structural and -- reducer meanings, while the closed Nat.add fixture makes precedence -- executable and records its three canonical-address decisions explicitly. - { root := ``Ix.Tc.RecM.tryProjAppReduceFinished_some, + { root := ``Ix.Kernel.RecM.tryProjAppReduceFinished_some, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.tryProjAppReduceFinished_none, + { root := ``Ix.Kernel.RecM.tryProjAppReduceFinished_none, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.tryProjAppReduceFinished_projError, + { root := ``Ix.Kernel.RecM.tryProjAppReduceFinished_projError, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.tryProjAppReduceFinished_finishError, + { root := ``Ix.Kernel.RecM.tryProjAppReduceFinished_finishError, standardAxioms := standard, nativeAxioms := expressionNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_projApp, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_projApp, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_bitvec, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_bitvec, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_nat, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_nat, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_native, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_native, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_string, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_string, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_projectionDef, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_projectionDef, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_quotFull, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_quotFull, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_quotCheap, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_quotCheap, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_doneFull, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_doneFull, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_doneCheap, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_doneCheap, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_projError, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_projError, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_bitvecError, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_bitvecError, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_natError, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_natError, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_nativeError, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_nativeError, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_stringError, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_stringError, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_projectionDefError, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_projectionDefError, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_quotFullError, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_quotFullError, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_quotCheapError, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_quotCheapError, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImplStep_ofCore, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImplStep_ofCore, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImplStep_coreError, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImplStep_coreError, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImplStep_reducerError, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImplStep_reducerError, standardAxioms := standard, nativeAxioms := inferNative }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImplStep_next_acceptance, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImplStep_next_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImplStep_done_acceptance, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImplStep_done_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImplStep_error_acceptance, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImplStep_error_acceptance, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_wf, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.noDeltaNatAddReduction, + { root := ``Ix.Kernel.AmbientNat.noDeltaNatAddReduction, standardAxioms := standard, nativeAxioms := natReductionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.noDeltaNatBranchOrder, + { root := ``Ix.Kernel.AmbientNat.noDeltaNatBranchOrder, standardAxioms := standard, nativeAxioms := natBranchOrderNative, forbiddenDependencies := legacyWholeEnv }, -- primitive reduction: `.noAccel` concretely discharges the native and BitVec optional -- reducers. The five active helpers remain an explicit base oracle, which -- now feeds the exhaustive tail, outer step, and public no-delta shell. - { root := ``Ix.Tc.RecM.tryReduceNative_noAccel_optional_wf, + { root := ``Ix.Kernel.RecM.tryReduceNative_noAccel_optional_wf, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceBitvec_noAccel_optional_wf, + { root := ``Ix.Kernel.RecM.tryReduceBitvec_noAccel_optional_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.NoDeltaBaseOracle.toNoAccel, + { root := ``Ix.Kernel.NoDeltaBaseOracle.toNoAccel, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfNoDeltaReducersStep_noAccel_wf, + { root := ``Ix.Kernel.RecM.whnfNoDeltaReducersStep_noAccel_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImplStep_wf, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImplStep_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImplStep_noAccel_wf, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImplStep_noAccel_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfNoDeltaImpl_noAccel_wf_of_base, + { root := ``Ix.Kernel.RecM.whnfNoDeltaImpl_noAccel_wf_of_base, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -7575,69 +7562,69 @@ private def roots : Array RootAllowance := #[ -- reducer closure. The world/context interface then binds the active Nat, -- String, projection, and quotient IDs to trusted Theory names and scopes -- generated results to actual successful helper executions. - { root := ``Ix.Tc.Primitives.ofAnonAddrs_canonical, + { root := ``Ix.Kernel.Primitives.ofAnonAddrs_canonical, standardAxioms := standard, nativeAxioms := canonicalPrimitivesNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfStateInv.noAccel_primitives, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.WhnfStateInv.noAccel_primitives, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfStateInv.accelerated_primitives, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.WhnfStateInv.accelerated_primitives, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.PrimitiveIdAgrees.contains, + { root := ``Ix.Kernel.PrimitiveIdAgrees.contains, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.PrimitiveIdAgrees.mono, + { root := ``Ix.Kernel.PrimitiveIdAgrees.mono, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.NoDeltaPrimitiveTableAgrees.mono, + { root := ``Ix.Kernel.NoDeltaPrimitiveTableAgrees.mono, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.NoDeltaPrimitiveContext.stateTable, + { root := ``Ix.Kernel.NoDeltaPrimitiveContext.stateTable, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, -- Nat reducer callback: the Nat reducer's shared callback/fuel boundary and exact binary -- arithmetic hit. The primitive computation is derived from the bound - -- canonical table and Lean4Lean reflection laws; no raw address equality + -- canonical table and Ix.Theory.Named reflection laws; no raw address equality -- is treated as semantic authority. - { root := ``Ix.Tc.WhnfStateInv.set_recFuel, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.WhnfStateInv.set_recFuel, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.WF.tryCatch, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.WF.tryCatch, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfRec_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.whnfRec_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfNatReducerArg_post_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.whnfNatReducerArg_post_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfNatReducerArg_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.whnfNatReducerArg_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.NoDeltaPrimitiveContext.computeNatBin_defeq, + { root := ``Ix.Kernel.NoDeltaPrimitiveContext.computeNatBin_defeq, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TrKExprS.of_extractNatLit, + { root := ``Ix.Kernel.TrKExprS.of_extractNatLit, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TrKExprS.natExprFromValue, + { root := ``Ix.Kernel.TrKExprS.natExprFromValue, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TrKExprS.natBinExact_inv, + { root := ``Ix.Kernel.TrKExprS.natBinExact_inv, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfPost.of_extractNatLit, + { root := ``Ix.Kernel.WhnfPost.of_extractNatLit, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfMeaning.natBinExact, + { root := ``Ix.Kernel.WhnfMeaning.natBinExact, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binArithExact, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binArithExact, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binArithExact_acceptance, + ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binArithExact_acceptance, standardAxioms := standard, nativeAxioms := contextNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -7645,35 +7632,35 @@ private def roots : Array RootAllowance := #[ -- generic proof uses trusted-name separation instead of native hash -- inequalities, and the finite Bool intern is checked against explicit run -- collision freedom and generated-node support. - { root := ``Ix.Tc.TcM.intern_whnf_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.TcM.intern_whnf_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.intern_whnf_eval, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.TcM.intern_whnf_eval, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.PrimitiveIdAgrees.addr_ne, + { root := ``Ix.Kernel.PrimitiveIdAgrees.addr_ne, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.NoDeltaPrimitiveContext.computeNatBin_classifiers, + { root := ``Ix.Kernel.NoDeltaPrimitiveContext.computeNatBin_classifiers, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.NoDeltaPrimitiveContext.natPredicate_classifiers, + { root := ``Ix.Kernel.NoDeltaPrimitiveContext.natPredicate_classifiers, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.NoDeltaPrimitiveContext.natPredicate_defeq, + { root := ``Ix.Kernel.NoDeltaPrimitiveContext.natPredicate_defeq, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TrKExprS.boolExprFromDecision, + { root := ``Ix.Kernel.TrKExprS.boolExprFromDecision, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatPredicate_exact, + { root := ``Ix.Kernel.RecM.tryReduceNatPredicate_exact, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binPredExact, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binPredExact, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binPredExact_acceptance, + ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binPredExact_acceptance, standardAxioms := standard, nativeAxioms := contextNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -7681,118 +7668,118 @@ private def roots : Array RootAllowance := #[ -- reduction. Callback errors retain their partial state, arithmetic and -- predicate extraction order is pinned, and the complete two-argument -- dispatcher preserves the invariant on every outcome. - { root := ``Ix.Tc.RecM.WF.withInv, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.WF.withInv, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.prims_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.prims_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isNatBinArithAddr_inv_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.isNatBinArithAddr_inv_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isNatBinPredAddr_inv_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.isNatBinPredAddr_inv_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfNatReducerArg_ok_inv, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.whnfNatReducerArg_ok_inv, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.whnfNatReducerArg_error_inv, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.whnfNatReducerArg_error_inv, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatPredicate_bin_inv_wf, + { root := ``Ix.Kernel.RecM.tryReduceNatPredicate_bin_inv_wf, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_bin_inv_wf, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_bin_inv_wf, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatPredicate_argAMiss, + { root := ``Ix.Kernel.RecM.tryReduceNatPredicate_argAMiss, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatPredicate_argAError, + { root := ``Ix.Kernel.RecM.tryReduceNatPredicate_argAError, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatPredicate_extractAMiss, + { root := ``Ix.Kernel.RecM.tryReduceNatPredicate_extractAMiss, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatPredicate_argBMiss, + { root := ``Ix.Kernel.RecM.tryReduceNatPredicate_argBMiss, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatPredicate_argBError, + { root := ``Ix.Kernel.RecM.tryReduceNatPredicate_argBError, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatPredicate_extractBMiss, + { root := ``Ix.Kernel.RecM.tryReduceNatPredicate_extractBMiss, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binPredMiss, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binPredMiss, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binPredError, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binPredError, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binArithArgAMiss, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binArithArgAMiss, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binArithArgAError, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binArithArgAError, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binArithArgBMiss, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binArithArgBMiss, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binArithArgBError, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binArithArgBError, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binArithExtractAMiss, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binArithExtractAMiss, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binArithExtractBMiss, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binArithExtractBMiss, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binArithComputeMiss, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binArithComputeMiss, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, -- binary Nat success: every successful exact-binary Nat run is inverted into its actual -- callback/extraction/computation-or-intern trace, then folded into a -- semantic optional-reduction Hoare slice. Predicate precedence remains -- operationally exhaustive even before canonical classifier separation. - { root := ``Ix.Tc.RecM.isNatBinArithAddr_eval, + { root := ``Ix.Kernel.RecM.isNatBinArithAddr_eval, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isNatBinPredAddr_eval, + { root := ``Ix.Kernel.RecM.isNatBinPredAddr_eval, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isNatBinPredAddr_true, + { root := ``Ix.Kernel.RecM.isNatBinPredAddr_true, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binPredAnyExact, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binPredAnyExact, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatPredicateSuccessTrace.eval, + { root := ``Ix.Kernel.RecM.NatPredicateSuccessTrace.eval, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatPredicateSuccessTrace.complete, + { root := ``Ix.Kernel.RecM.NatPredicateSuccessTrace.complete, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatBinSuccessTrace.eval, + { root := ``Ix.Kernel.RecM.NatBinSuccessTrace.eval, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatBinSuccessTrace.complete, + { root := ``Ix.Kernel.RecM.NatBinSuccessTrace.complete, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatBinSuccessTrace.acceptance, + { root := ``Ix.Kernel.RecM.NatBinSuccessTrace.acceptance, standardAxioms := standard, nativeAxioms := contextNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_bin_optional_wf, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_bin_optional_wf, standardAxioms := standard, nativeAxioms := contextNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.structuralInvariant_does_not_bind_primitives, + { root := ``Ix.Kernel.AmbientNat.structuralInvariant_does_not_bind_primitives, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.productionNoAccelStateInv, + { root := ``Ix.Kernel.AmbientNat.productionNoAccelStateInv, standardAxioms := standard, nativeAxioms := nameNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.noAccelInvariant_rejects_mismatched_primitives, + { root := ``Ix.Kernel.AmbientNat.noAccelInvariant_rejects_mismatched_primitives, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, @@ -7800,80 +7787,80 @@ private def roots : Array RootAllowance := #[ -- spine, exact Nat equations are transported over arbitrary unchanged -- argument suffixes, and finite rebuild certificates preserve state and -- support. Successful general-spine executions are inverted exhaustively. - { root := ``Ix.Tc.RecM.appSpineView_go, + { root := ``Ix.Kernel.RecM.appSpineView_go, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.appSpineView_collectSpine, + { root := ``Ix.Kernel.RecM.appSpineView_collectSpine, standardAxioms := standardWithoutChoice }, - { root := ``Ix.Tc.RecM.trAppSpine_of_tr, + { root := ``Ix.Kernel.RecM.trAppSpine_of_tr, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrAppSpine.argument, + { root := ``Ix.Kernel.RecM.TrAppSpine.argument, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrAppSpine.tr, + { root := ``Ix.Kernel.RecM.TrAppSpine.tr, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.trAppSpine_of_collectSpine, + { root := ``Ix.Kernel.RecM.trAppSpine_of_collectSpine, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TrKExprS.foldlMkApp_initial, + { root := ``Ix.Kernel.TrKExprS.foldlMkApp_initial, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfMeaning.appSameArg, + { root := ``Ix.Kernel.WhnfMeaning.appSameArg, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfMeaning.foldlMkApp, + { root := ``Ix.Kernel.WhnfMeaning.foldlMkApp, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfMeaning.mkAppN, + { root := ``Ix.Kernel.WhnfMeaning.mkAppN, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfMeaning.ofSharedSourceTranslation, + { root := ``Ix.Kernel.WhnfMeaning.ofSharedSourceTranslation, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatPredicate_suffixExact, + { root := ``Ix.Kernel.RecM.tryReduceNatPredicate_suffixExact, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binPredSuffixExact, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binPredSuffixExact, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binArithSuffixExact, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binArithSuffixExact, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binArithSuffix_acceptance, + ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binArithSuffix_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.tryReduceNatWithSuccMode_binPredSuffix_acceptance, + ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_binPredSuffix_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatPredicateSuffixSuccessTrace.eval, + { root := ``Ix.Kernel.RecM.NatPredicateSuffixSuccessTrace.eval, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatPredicateSuffixSuccessTrace.complete, + { root := ``Ix.Kernel.RecM.NatPredicateSuffixSuccessTrace.complete, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatSpineSuccessTrace.eval, + { root := ``Ix.Kernel.RecM.NatSpineSuccessTrace.eval, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatSpineSuccessTrace.complete, + { root := ``Ix.Kernel.RecM.NatSpineSuccessTrace.complete, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.noDeltaNatAddSuffixSpine, + { root := ``Ix.Kernel.AmbientNat.noDeltaNatAddSuffixSpine, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.noDeltaNatAddSuffixFinishRequests, + { root := ``Ix.Kernel.AmbientNat.noDeltaNatAddSuffixFinishRequests, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.noDeltaNatAddSuffixReduction, + { root := ``Ix.Kernel.AmbientNat.noDeltaNatAddSuffixReduction, standardAxioms := standard, nativeAxioms := natSuffixReductionNative, forbiddenDependencies := legacyWholeEnv }, @@ -7882,33 +7869,33 @@ private def roots : Array RootAllowance := #[ -- with only its observed finite rebuild requests, then interpreted as the -- fixed-state optional-reduction Hoare contract. The over-applied Nat.add -- fixture inhabits that execution-indexed coverage boundary. - { root := ``Ix.Tc.RecM.finishAppResult_total, + { root := ``Ix.Kernel.RecM.finishAppResult_total, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.natBinSpine_inputs, + { root := ``Ix.Kernel.RecM.natBinSpine_inputs, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatPredicate_spine_nonhit_inv, + { root := ``Ix.Kernel.RecM.tryReduceNatPredicate_spine_nonhit_inv, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_spine_nonhit_inv, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_spine_nonhit_inv, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatSpineCertifiedSuccess.trace, + { root := ``Ix.Kernel.RecM.NatSpineCertifiedSuccess.trace, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatSpineCertifiedSuccess.acceptance, + { root := ``Ix.Kernel.RecM.NatSpineCertifiedSuccess.acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_spine_optional_wf, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_spine_optional_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.noDeltaNatAddSuffixCertifiedSuccess, + { root := ``Ix.Kernel.AmbientNat.noDeltaNatAddSuffixCertifiedSuccess, standardAxioms := standard, nativeAxioms := natSuffixCertificateNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.noDeltaNatAddSuffixFinishCoverage, + { root := ``Ix.Kernel.AmbientNat.noDeltaNatAddSuffixFinishCoverage, standardAxioms := standard, nativeAxioms := natSuffixReductionNative, forbiddenDependencies := legacyWholeEnv }, @@ -7918,111 +7905,111 @@ private def roots : Array RootAllowance := #[ -- cache/state invariant only under explicit per-key provenance. The -- closed Nat.succ fixture runs through the actual dispatcher and bounded -- driver without mutating the state. - { root := ``Ix.Tc.CacheInvariant.insertNatSuccStuck, + { root := ``Ix.Kernel.CacheInvariant.insertNatSuccStuck, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.CacheInvariant.insertNatSuccStuckList, + { root := ``Ix.Kernel.CacheInvariant.insertNatSuccStuckList, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.CacheInvariant.insertNatSuccStuckArray, + { root := ``Ix.Kernel.CacheInvariant.insertNatSuccStuckArray, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatSuccStuckCacheUpdate.fold_whnfStateInv, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.NatSuccStuckCacheUpdate.fold_whnfStateInv, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccIter_entryHit, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccIter_entryHit, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccIter_entryKeyError, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccIter_entryKeyError, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccIter_entryMiss, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccIter_entryMiss, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccIterStep_linearHit, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccIterStep_linearHit, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccIterStep_linearError, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccIterStep_linearError, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccIterStep_whnfError, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccIterStep_whnfError, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccIterStep_afterWhnf, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccIterStep_afterWhnf, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccAfterWhnf_literal, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccAfterWhnf_literal, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccAfterWhnf_stuck, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccAfterWhnf_stuck, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.recordNatSuccStuck_eval, + { root := ``Ix.Kernel.RecM.recordNatSuccStuck_eval, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.recordNatSuccStuck_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.recordNatSuccStuck_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccPeel_keyError, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccPeel_keyError, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccPeel_afterKey, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccPeel_afterKey, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccPeelAfterKey_hit, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccPeelAfterKey_hit, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccPeelAfterKey_miss, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccPeelAfterKey_miss, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccPeelMiss_keyError, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccPeelMiss_keyError, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccPeelMiss_next, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccPeelMiss_next, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccAfterWhnf_succ, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccAfterWhnf_succ, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_succ_stuck, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_succ_stuck, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_succ_collapse, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_succ_collapse, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.succCollapseSpine, + { root := ``Ix.Kernel.AmbientNat.succCollapseSpine, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.succCollapseLinearMiss, + { root := ``Ix.Kernel.AmbientNat.succCollapseLinearMiss, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.succCollapseWhnf, + { root := ``Ix.Kernel.AmbientNat.succCollapseWhnf, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.succCollapseExtract, + { root := ``Ix.Kernel.AmbientNat.succCollapseExtract, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.succCollapseStep, + { root := ``Ix.Kernel.AmbientNat.succCollapseStep, standardAxioms := standard, nativeAxioms := contextNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.succCollapseKey, + { root := ``Ix.Kernel.AmbientNat.succCollapseKey, standardAxioms := standard, nativeAxioms := contextNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.succCollapseMemoMiss, + { root := ``Ix.Kernel.AmbientNat.succCollapseMemoMiss, standardAxioms := standard, nativeAxioms := expressionNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.succCollapseIter, + { root := ``Ix.Kernel.AmbientNat.succCollapseIter, standardAxioms := standard, nativeAxioms := contextNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.succCollapseReduction, + { root := ``Ix.Kernel.AmbientNat.succCollapseReduction, standardAxioms := standard, nativeAxioms := contextNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, @@ -8032,34 +8019,34 @@ private def roots : Array RootAllowance := #[ -- provenance; the ghost loop state tracks Nat typing, arbitrary successor -- offsets, and every pending marker. Linear Nat.rec recognition remains -- behind its explicit oracle until inductive iota semantics instantiate it. - { root := ``Ix.Tc.WhnfCacheValid.natSuccStuck, + { root := ``Ix.Kernel.WhnfCacheValid.natSuccStuck, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.CacheProvenance.whnfNatSuccStuck, + { root := ``Ix.Kernel.CacheProvenance.whnfNatSuccStuck, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatSuccStuckWriteOracle.forWhnfCache, + { root := ``Ix.Kernel.RecM.NatSuccStuckWriteOracle.forWhnfCache, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.natSucc_hasType, + { root := ``Ix.Kernel.RecM.natSucc_hasType, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.natSuccSpine_tr, + { root := ``Ix.Kernel.RecM.natSuccSpine_tr, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccPeel_wf, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccPeel_wf, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccAfterWhnf_wf, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccAfterWhnf_wf, standardAxioms := standard, nativeAxioms := contextNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccIterStep_wf, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccIterStep_wf, standardAxioms := standard, nativeAxioms := contextNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccIter_wf, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccIter_wf, standardAxioms := standard, nativeAxioms := contextNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -8073,66 +8060,66 @@ private def roots : Array RootAllowance := #[ -- while successful callback meaning reduces the former exact-arity -- assumption to canonical Nat/Bool result-shape separation. Nat.rec -- reflection and that Theory shape fact remain explicit. - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_succ_optional_wf, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_succ_optional_wf, standardAxioms := standard, nativeAxioms := contextNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.NatCollapseRequestCensus.suffix_eq_empty_of_result_shape, + ``Ix.Kernel.RecM.NatCollapseRequestCensus.suffix_eq_empty_of_result_shape, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatCollapseRequestCensus.of_no_suffix, + { root := ``Ix.Kernel.RecM.NatCollapseRequestCensus.of_no_suffix, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatCollapseRequestCensus.of_result_shape, + { root := ``Ix.Kernel.RecM.NatCollapseRequestCensus.of_result_shape, standardAxioms := standard, nativeAxioms := contextNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatCollapseRequestCensus.certify, + { root := ``Ix.Kernel.RecM.NatCollapseRequestCensus.certify, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isNatSuccIhStep_wf, + { root := ``Ix.Kernel.RecM.isNatSuccIhStep_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatSuccLinearRec_effect_wf, + { root := ``Ix.Kernel.RecM.tryReduceNatSuccLinearRec_effect_wf, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatSuccLinearOracle.of_reflection, + { root := ``Ix.Kernel.RecM.NatSuccLinearOracle.of_reflection, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_collapse_optional_wf, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_collapse_optional_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.tryReduceNatWithSuccMode_collapse_optional_wf_of_boundaries, + ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_collapse_optional_wf_of_boundaries, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_stuck_short, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_stuck_short, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryReduceNatWithSuccMode_stuck_optional_wf, + { root := ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_stuck_optional_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.tryReduceNatWithSuccMode_stuck_optional_wf_of_boundary, + ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_stuck_optional_wf_of_boundary, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.tryReduceNatWithSuccMode_optional_wf_of_boundaries, + ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_optional_wf_of_boundaries, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.tryReduceNatWithSuccMode_optional_wf_of_lazy_boundaries, + ``Ix.Kernel.RecM.tryReduceNatWithSuccMode_optional_wf_of_lazy_boundaries, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.AmbientNat.succStuckReduction, + { root := ``Ix.Kernel.AmbientNat.succStuckReduction, standardAxioms := standard, nativeAxioms := contextNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, @@ -8140,257 +8127,257 @@ private def roots : Array RootAllowance := #[ -- K2a: suffix semantics reduce open-context cache validity to one explicit -- operational model. The recursive method table closes by induction from -- an exact one-layer contract split between WHNF and Infer/DefEq ownership. - { root := ``Ix.Tc.WhnfSuffixModel.keyRepresents, + { root := ``Ix.Kernel.WhnfSuffixModel.keyRepresents, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfSuffixModel.cacheWriteOracle, + { root := ``Ix.Kernel.WhnfSuffixModel.cacheWriteOracle, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.Methods.LayerWF.of_parts, + { root := ``Ix.Kernel.Methods.LayerWF.of_parts, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.Methods.Closed.of_parts, + { root := ``Ix.Kernel.Methods.Closed.of_parts, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.Methods.methodsOut_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.Methods.methodsOut_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.Methods.methodsN_wf, + { root := ``Ix.Kernel.Methods.methodsN_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.runRec_wf, + { root := ``Ix.Kernel.TcM.runRec_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, -- K2a also assigns exact meanings to the remaining cache families. A -- positive DefEq result carries Theory equality; negative results are -- intentionally vacuous for the one-way soundness claim. - { root := ``Ix.Tc.InferMeaning.mono, + { root := ``Ix.Kernel.InferMeaning.mono, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.InferMeaning.post, + { root := ``Ix.Kernel.InferMeaning.post, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.InferCacheValid.mono, + { root := ``Ix.Kernel.InferCacheValid.mono, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.CacheInvariant.inferHitOfMatches, + { root := ``Ix.Kernel.CacheInvariant.inferHitOfMatches, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.DefEqMeaning.mono, + { root := ``Ix.Kernel.DefEqMeaning.mono, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.DefEqMeaning.of_translations, + { root := ``Ix.Kernel.DefEqMeaning.of_translations, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.DefEqCacheValid.mono, + { root := ``Ix.Kernel.DefEqCacheValid.mono, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.CacheProvenance.kernelWhnfMeaningOfMatches, + { root := ``Ix.Kernel.CacheProvenance.kernelWhnfMeaningOfMatches, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.CacheProvenance.kernelInferMeaningOfMatches, + { root := ``Ix.Kernel.CacheProvenance.kernelInferMeaningOfMatches, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.CacheProvenance.kernelDefEqMeaning, + { root := ``Ix.Kernel.CacheProvenance.kernelDefEqMeaning, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, -- K2b: production key executions now generate the canonical operational -- context witnesses. Physical inference/DefEq writes preserve every -- cache partition, including the rejection-only same-head failure set. - { root := ``Ix.Tc.CacheInvariant.insertInfer, + { root := ``Ix.Kernel.CacheInvariant.insertInfer, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.CacheInvariant.insertInferOnly, + { root := ``Ix.Kernel.CacheInvariant.insertInferOnly, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.CacheInvariant.insertDefEq, + { root := ``Ix.Kernel.CacheInvariant.insertDefEq, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.CacheInvariant.insertDefEqCheap, + { root := ``Ix.Kernel.CacheInvariant.insertDefEqCheap, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.CacheInvariant.insertDefEqFailure, + { root := ``Ix.Kernel.CacheInvariant.insertDefEqFailure, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.ctxAddrForLbr_empty, + { root := ``Ix.Kernel.TcM.ctxAddrForLbr_empty, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.whnfKey_ctx, + { root := ``Ix.Kernel.TcM.whnfKey_ctx, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.operationalWhnfContextKeys.represents, + { root := ``Ix.Kernel.operationalWhnfContextKeys.represents, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.operationalWhnfContextKeys.representsCtx, + { root := ``Ix.Kernel.operationalWhnfContextKeys.representsCtx, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.ContextDigestSpec.execution, + { root := ``Ix.Kernel.ContextDigestSpec.execution, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.ContextDigestSpec.StateValid, + { root := ``Ix.Kernel.ContextDigestSpec.StateValid, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.ContextDigestSpec.memoValid, + { root := ``Ix.Kernel.ContextDigestSpec.memoValid, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.ContextDigestSpec.preserves, + { root := ``Ix.Kernel.ContextDigestSpec.preserves, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.ctxAddrForLbr_trivial, + { root := ``Ix.Kernel.TcM.ctxAddrForLbr_trivial, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.ctxAddrForLbr_cacheHit, + { root := ``Ix.Kernel.TcM.ctxAddrForLbr_cacheHit, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.ctxAddrForLbr_cacheMiss, + { root := ``Ix.Kernel.TcM.ctxAddrForLbr_cacheMiss, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.ctxAddrForLbr_replay, + { root := ``Ix.Kernel.TcM.ctxAddrForLbr_replay, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.ContextAddrMemoValid, + { root := ``Ix.Kernel.TcM.ContextAddrMemoValid, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.ctxAddrForLbr_memoValid, + { root := ``Ix.Kernel.TcM.ctxAddrForLbr_memoValid, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.scopedOperationalWhnfContextKeys.represents, + { root := ``Ix.Kernel.scopedOperationalWhnfContextKeys.represents, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.scopedOperationalWhnfContextKeys.representsCtx, + { root := ``Ix.Kernel.scopedOperationalWhnfContextKeys.representsCtx, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.scopedOperationalWhnfContextKeys.digest_eq, + { root := ``Ix.Kernel.scopedOperationalWhnfContextKeys.digest_eq, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.scopedOperationalWhnfContextKeys.mem, + { root := ``Ix.Kernel.scopedOperationalWhnfContextKeys.mem, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.WhnfSuffixModel.operational, + { root := ``Ix.Kernel.WhnfSuffixModel.operational, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.inferKey_wf, + { root := ``Ix.Kernel.TcM.inferKey_wf, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.inferKey_operational_matches_wf, + { root := ``Ix.Kernel.TcM.inferKey_operational_matches_wf, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.inferWith_fullHit, + { root := ``Ix.Kernel.RecM.inferWith_fullHit, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.inferWith_inferOnlyHit, + { root := ``Ix.Kernel.RecM.inferWith_inferOnlyHit, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.InferCacheUpdate.full_whnfStateInv, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.InferCacheUpdate.full_whnfStateInv, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.InferCacheUpdate.inferOnly_whnfStateInv, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.InferCacheUpdate.inferOnly_whnfStateInv, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, -- The union-find frame and joint suffix model keep composite context-hash -- transport explicit for WHNF, inference, and DefEq. Collision-robust -- provenance constructors quantify over every supported address peer. - { root := ``Ix.Tc.TcM.withEquiv_eq, + { root := ``Ix.Kernel.TcM.withEquiv_eq, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.withEquiv_whnf_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.TcM.withEquiv_whnf_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.defEqCtxKey_wf, + { root := ``Ix.Kernel.TcM.defEqCtxKey_wf, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.defEqCtxKey_operational_matches_wf, + { root := ``Ix.Kernel.TcM.defEqCtxKey_operational_matches_wf, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.DefEqMeaning.of_addr_beq, + { root := ``Ix.Kernel.DefEqMeaning.of_addr_beq, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.DefEqMeaning.symm, + { root := ``Ix.Kernel.DefEqMeaning.symm, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.KernelSuffixModel.toWhnfSuffixModel, + { root := ``Ix.Kernel.KernelSuffixModel.toWhnfSuffixModel, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.KernelSuffixModel.operational, + { root := ``Ix.Kernel.KernelSuffixModel.operational, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.ContextSuffixSemantics.whnf, + { root := ``Ix.Kernel.ContextSuffixSemantics.whnf, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.ContextSuffixSemantics.infer, + { root := ``Ix.Kernel.ContextSuffixSemantics.infer, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.ContextSuffixSemantics.defEq, + { root := ``Ix.Kernel.ContextSuffixSemantics.defEq, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.ScopedKernelSuffixModel.represents, + { root := ``Ix.Kernel.ScopedKernelSuffixModel.represents, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.ScopedKernelSuffixModel.StateInScope, + { root := ``Ix.Kernel.ScopedKernelSuffixModel.StateInScope, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.ScopedKernelSuffixModel.whnfTransport, + { root := ``Ix.Kernel.ScopedKernelSuffixModel.whnfTransport, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.ScopedKernelSuffixModel.inferTransport, + { root := ``Ix.Kernel.ScopedKernelSuffixModel.inferTransport, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.ScopedKernelSuffixModel.defEqTransport, + { root := ``Ix.Kernel.ScopedKernelSuffixModel.defEqTransport, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.ScopedKernelSuffixModel.finiteOperational, + { root := ``Ix.Kernel.ScopedKernelSuffixModel.finiteOperational, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.ScopedKernelSuffixModel.toKernelSuffixModel, + { root := ``Ix.Kernel.ScopedKernelSuffixModel.toKernelSuffixModel, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.KernelSuffixModel.finiteOperational, + { root := ``Ix.Kernel.KernelSuffixModel.finiteOperational, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.CacheProvenance.kernelDefEqMeaningCanonical, + { root := ``Ix.Kernel.CacheProvenance.kernelDefEqMeaningCanonical, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.KernelSuffixModel.inferProvenance, + { root := ``Ix.Kernel.KernelSuffixModel.inferProvenance, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.KernelSuffixModel.defEqProvenance, + { root := ``Ix.Kernel.KernelSuffixModel.defEqProvenance, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.KernelSuffixModel.defEqFailureProvenance, + { root := ``Ix.Kernel.KernelSuffixModel.defEqFailureProvenance, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.DefEqCacheUpdate.full_whnfStateInv, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.DefEqCacheUpdate.full_whnfStateInv, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.DefEqCacheUpdate.cheap_whnfStateInv, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.DefEqCacheUpdate.cheap_whnfStateInv, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.DefEqCacheUpdate.failure_whnfStateInv, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.DefEqCacheUpdate.failure_whnfStateInv, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, -- First production K2 branches: both inference hit partitions, collision- -- safe DefEq address reflexivity, and a positive full DefEq hit including -- canonical ordering and its final union-find mutation. - { root := ``Ix.Tc.RecM.isDefEq_fullHit_true, + { root := ``Ix.Kernel.RecM.isDefEq_fullHit_true, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isDefEq_fullHit_true_acceptance, + { root := ``Ix.Kernel.RecM.isDefEq_fullHit_true_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isDefEq_addrEq_wf, + { root := ``Ix.Kernel.RecM.isDefEq_addrEq_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.inferWith_fullHit_acceptance, + { root := ``Ix.Kernel.RecM.inferWith_fullHit_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.inferWith_inferOnlyHit_acceptance, + { root := ``Ix.Kernel.RecM.inferWith_inferOnlyHit_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -8398,11 +8385,11 @@ private def roots : Array RootAllowance := #[ -- The memoized proposition classifier closes proof irrelevance's sole -- auxiliary cache family. Positive hits and writes are tied to `Sort 0` -- through expression collision freedom and the explicit suffix model. - { root := ``Ix.Tc.RecM.isPropType_wf, + { root := ``Ix.Kernel.RecM.isPropType_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryProofIrrel_classifier_wf, + { root := ``Ix.Kernel.RecM.tryProofIrrel_classifier_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -8410,20 +8397,20 @@ private def roots : Array RootAllowance := #[ -- Lazy delta is a bounded semantic state machine. These roots expose the -- pair invariant, the fuel-bounded closure, and the exact remaining -- obligations for one iteration and the stopped continuation. - { root := ``Ix.Tc.RecM.DefEqPairInvariant.refl, + { root := ``Ix.Kernel.RecM.DefEqPairInvariant.refl, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.DefEqPairInvariant.conclude, + { root := ``Ix.Kernel.RecM.DefEqPairInvariant.conclude, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.runDefEqLazyDelta_wf, + { root := ``Ix.Kernel.RecM.runDefEqLazyDelta_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isDefEqInnerAfterProofIrrelevance_wf, + { root := ``Ix.Kernel.RecM.isDefEqInnerAfterProofIrrelevance_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.DefEqAfterProofIrrelevance.ofLazyDelta, + { root := ``Ix.Kernel.RecM.DefEqAfterProofIrrelevance.ofLazyDelta, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, @@ -8431,84 +8418,84 @@ private def roots : Array RootAllowance := #[ -- literal/zero guards and both ordinary Nat-reduction attempts. Structural -- offset decomposition and the post-Nat reducer tiers remain explicit -- continuation contracts; negative recognizer results carry no semantics. - { root := ``Ix.Tc.RecM.isNatZero_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.isNatZero_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.IsNatZero.ofContext, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.IsNatZero.ofContext, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryDefEqOffset_wf, + { root := ``Ix.Kernel.RecM.tryDefEqOffset_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TryDefEqOffset.ofContext, + { root := ``Ix.Kernel.RecM.TryDefEqOffset.ofContext, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.defEqLazyDeltaStepAfterOffsetMiss_wf, + { root := ``Ix.Kernel.RecM.defEqLazyDeltaStepAfterOffsetMiss_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.DefEqLazyDeltaAfterOffsetMiss.ofNat, + { root := ``Ix.Kernel.RecM.DefEqLazyDeltaAfterOffsetMiss.ofNat, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.defEqLazyDeltaStepAfterNatMiss_wf, + { root := ``Ix.Kernel.RecM.defEqLazyDeltaStepAfterNatMiss_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.DefEqLazyDeltaAfterNatMiss.ofNoAccel, + { root := ``Ix.Kernel.RecM.DefEqLazyDeltaAfterNatMiss.ofNoAccel, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.classifyDeltaHead_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.classifyDeltaHead_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.defEqLazyDeltaStepAfterAcceleratorMiss_wf, + { root := ``Ix.Kernel.RecM.defEqLazyDeltaStepAfterAcceleratorMiss_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.DefEqLazyDeltaAfterAcceleratorMiss.ofClassification, + ``Ix.Kernel.RecM.DefEqLazyDeltaAfterAcceleratorMiss.ofClassification, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryUnfoldProjApp_wf, + { root := ``Ix.Kernel.RecM.tryUnfoldProjApp_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.defEqLazyDeltaStepAfterDeltaClassification_wf, + { root := ``Ix.Kernel.RecM.defEqLazyDeltaStepAfterDeltaClassification_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.DefEqLazyDeltaAfterDeltaClassification.ofProjection, + ``Ix.Kernel.RecM.DefEqLazyDeltaAfterDeltaClassification.ofProjection, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.finishDefEqLazyDeltaStep_wf, + { root := ``Ix.Kernel.RecM.finishDefEqLazyDeltaStep_wf, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.defEqLazyDeltaStepWithLeftDelta_wf, + { root := ``Ix.Kernel.RecM.defEqLazyDeltaStepWithLeftDelta_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.defEqLazyDeltaStepWithRightDelta_wf, + { root := ``Ix.Kernel.RecM.defEqLazyDeltaStepWithRightDelta_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.rankDeltaHead_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.rankDeltaHead_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.defEqLazyDeltaStepAfterProjectionMiss_wf, + { root := ``Ix.Kernel.RecM.defEqLazyDeltaStepAfterProjectionMiss_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.DefEqLazyDeltaAfterProjectionMiss.ofRankDispatch, + { root := ``Ix.Kernel.RecM.DefEqLazyDeltaAfterProjectionMiss.ofRankDispatch, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.defEqLazyDeltaStepAfterSameHeadMiss_wf, + { root := ``Ix.Kernel.RecM.defEqLazyDeltaStepAfterSameHeadMiss_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.DefEqLazyDeltaAfterSameHeadMiss.ofReduction, + { root := ``Ix.Kernel.RecM.DefEqLazyDeltaAfterSameHeadMiss.ofReduction, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -8517,54 +8504,54 @@ private def roots : Array RootAllowance := #[ -- congruence, and the rejection-only failure-cache shell. A cache hit can -- only skip the comparison; every positive result still comes from the -- semantic same-head proof. - { root := ``Ix.Tc.RecM.allDefEqSpineArgs_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.allDefEqSpineArgs_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrAppSpine.defEq_of_zip, + { root := ``Ix.Kernel.RecM.TrAppSpine.defEq_of_zip, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.sameDefEqUniverses_sound, + { root := ``Ix.Kernel.RecM.sameDefEqUniverses_sound, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.constantHeadsDefEq, + { root := ``Ix.Kernel.RecM.constantHeadsDefEq, standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.trySameHeadSpine_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.trySameHeadSpine_wf, + standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrySameHeadSpine.ofResources, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.TrySameHeadSpine.ofResources, + standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.CacheEntry.defEqFailureReferencesAuthorized, + { root := ``Ix.Kernel.CacheEntry.defEqFailureReferencesAuthorized, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.DefEqFailureCacheResources.ofKernelSuffixModel, + { root := ``Ix.Kernel.DefEqFailureCacheResources.ofKernelSuffixModel, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isRegular_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.isRegular_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.trySameHeadSpineSpeculative_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.trySameHeadSpineSpeculative_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.trySameHeadSpineCached_wf, + { root := ``Ix.Kernel.RecM.trySameHeadSpineCached_wf, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TrySameHeadSpineCached.ofResources, + { root := ``Ix.Kernel.RecM.TrySameHeadSpineCached.ofResources, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.defEqLazyDeltaStepWithEqualRank_wf, + { root := ``Ix.Kernel.RecM.defEqLazyDeltaStepWithEqualRank_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.DefEqLazyDeltaEqualRank.ofPrefix, + { root := ``Ix.Kernel.RecM.DefEqLazyDeltaEqualRank.ofPrefix, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.DefEqLazyDeltaEqualRank.ofKernelResources, + { root := ``Ix.Kernel.RecM.DefEqLazyDeltaEqualRank.ofKernelResources, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -8573,20 +8560,20 @@ private def roots : Array RootAllowance := #[ -- path. Its only semantic input is an exact successful-run reflection; -- recursive equality is transported forward through the common successor -- suffix, without assuming offset injectivity or completeness. - { root := ``Ix.Tc.TcM.WF.withInvRunEq, + { root := ``Ix.Kernel.TcM.WF.withInvRunEq, standardAxioms := standardWithoutChoice, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.natOffsetDecompose_state_wf, + { root := ``Ix.Kernel.RecM.natOffsetDecompose_state_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.natOffsetRebuild_state_wf, + { root := ``Ix.Kernel.RecM.natOffsetRebuild_state_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryDefEqOffsetAfterCandidates_wf, + { root := ``Ix.Kernel.RecM.tryDefEqOffsetAfterCandidates_wf, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TryDefEqOffsetAfterCandidates.ofContext, + { root := ``Ix.Kernel.RecM.TryDefEqOffsetAfterCandidates.ofContext, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -8595,33 +8582,33 @@ private def roots : Array RootAllowance := #[ -- general app probe reconstructs equality through both typed spines; -- structural congruence proves constants and variables directly and -- delegates matching projections to one execution-indexed helper contract. - { root := ``Ix.Tc.RecM.tryDefEqApp_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.tryDefEqApp_wf, + standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TryDefEqApp.ofResources, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.TryDefEqApp.ofResources, + standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryStructuralCongruence_wf, + { root := ``Ix.Kernel.RecM.tryStructuralCongruence_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TryStructuralCongruence.ofResources, + { root := ``Ix.Kernel.RecM.TryStructuralCongruence.ofResources, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isDefEqAfterLazyDeltaStopped_wf, + { root := ``Ix.Kernel.RecM.isDefEqAfterLazyDeltaStopped_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.DefEqAfterLazyDeltaStopped.ofResources, + { root := ``Ix.Kernel.RecM.DefEqAfterLazyDeltaStopped.ofResources, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.DefEqLazyDeltaContext.ofKernelResources, + { root := ``Ix.Kernel.RecM.DefEqLazyDeltaContext.ofKernelResources, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.DefEqAfterProofIrrelevance.ofKernelResources, + { root := ``Ix.Kernel.RecM.DefEqAfterProofIrrelevance.ofKernelResources, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -8629,11 +8616,11 @@ private def roots : Array RootAllowance := #[ -- The structural projection callback's bounded lazy-delta driver preserves -- the original projected semantics across delta steps, direct projection -- reduction, recursive comparison, and normal depth exhaustion. - { root := ``Ix.Tc.RecM.lazyDeltaProjReduction_wf, + { root := ``Ix.Kernel.RecM.lazyDeltaProjReduction_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.LazyDeltaProjReduction.ofResources, + { root := ``Ix.Kernel.RecM.LazyDeltaProjReduction.ofResources, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -8641,30 +8628,30 @@ private def roots : Array RootAllowance := #[ -- Direct projection reduction gets state/support closure from the proved -- no-acceleration helper and consults semantic reflection only for the -- exact successful execution that occurred. - { root := ``Ix.Tc.RecM.tryProjReduce_direct_wf, + { root := ``Ix.Kernel.RecM.tryProjReduce_direct_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TryProjReduce.ofDirectResources, + { root := ``Ix.Kernel.RecM.TryProjReduce.ofDirectResources, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, -- The compact projection-loop delta step exposes its two lazy declaration -- classifications as a proved prefix; the exact branch continuation sees -- only their concrete results. - { root := ``Ix.Tc.RecM.lazyDeltaReductionStep_wf, + { root := ``Ix.Kernel.RecM.lazyDeltaReductionStep_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.LazyDeltaReductionStep.ofClassification, + { root := ``Ix.Kernel.RecM.LazyDeltaReductionStep.ofClassification, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.lazyDeltaReductionStepAfterClassification_wf, + { root := ``Ix.Kernel.RecM.lazyDeltaReductionStepAfterClassification_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.LazyDeltaReductionAfterClassification.ofActive, + ``Ix.Kernel.RecM.LazyDeltaReductionAfterClassification.ofActive, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.LazyDeltaReductionStep.ofActive, + { root := ``Ix.Kernel.RecM.LazyDeltaReductionStep.ofActive, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, @@ -8673,42 +8660,42 @@ private def roots : Array RootAllowance := #[ -- one- or two-sided unfolding, and equal ranks try same-head congruence -- before normalizing both sides. The final two roots assemble that branch -- proof with the already-audited classifier prefix. - { root := ``Ix.Tc.RecM.finishLazyDeltaReductionStep_wf, + { root := ``Ix.Kernel.RecM.finishLazyDeltaReductionStep_wf, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.lazyDeltaReductionStepWithLeftDelta_wf, + { root := ``Ix.Kernel.RecM.lazyDeltaReductionStepWithLeftDelta_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.lazyDeltaReductionStepWithRightDelta_wf, + { root := ``Ix.Kernel.RecM.lazyDeltaReductionStepWithRightDelta_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.lazyDeltaReductionStepAfterSameHeadMiss_wf, + { root := ``Ix.Kernel.RecM.lazyDeltaReductionStepAfterSameHeadMiss_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.lazyDeltaReductionStepWithEqualRank_wf, + { root := ``Ix.Kernel.RecM.lazyDeltaReductionStepWithEqualRank_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.defRankId_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.defRankId_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.lazyDeltaReductionStepWithBothDelta_wf, + { root := ``Ix.Kernel.RecM.lazyDeltaReductionStepWithBothDelta_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.lazyDeltaReductionStepAfterActive_wf, + { root := ``Ix.Kernel.RecM.lazyDeltaReductionStepAfterActive_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.LazyDeltaReductionAfterActive.ofResources, + { root := ``Ix.Kernel.RecM.LazyDeltaReductionAfterActive.ofResources, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.LazyDeltaReductionStep.ofResources, + { root := ``Ix.Kernel.RecM.LazyDeltaReductionStep.ofResources, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -8717,16 +8704,16 @@ private def roots : Array RootAllowance := #[ -- projection comparison, and structural-congruence projection branch from -- named lower reducers. The exact-run direct projection reflection is the -- remaining semantic boundary; the outer loop itself is no longer one. - { root := ``Ix.Tc.RecM.ProjectionDeltaClosureResources.loop, + { root := ``Ix.Kernel.RecM.ProjectionDeltaClosureResources.loop, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.LazyDeltaProjReduction.ofClosureResources, + { root := ``Ix.Kernel.RecM.LazyDeltaProjReduction.ofClosureResources, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.TryStructuralCongruence.ofProjectionDeltaResources, + ``Ix.Kernel.RecM.TryStructuralCongruence.ofProjectionDeltaResources, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -8735,12 +8722,12 @@ private def roots : Array RootAllowance := #[ -- concrete projection loop and reuses that record's core/quick resources; -- only application-spine and final-WHNF contracts remain as sibling inputs. { root := - ``Ix.Tc.RecM.StoppedContinuationClosureResources.stopped, + ``Ix.Kernel.RecM.StoppedContinuationClosureResources.stopped, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, { root := - ``Ix.Tc.RecM.DefEqAfterLazyDeltaStopped.ofClosureResources, + ``Ix.Kernel.RecM.DefEqAfterLazyDeltaStopped.ofClosureResources, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -8750,73 +8737,73 @@ private def roots : Array RootAllowance := #[ -- comparison and every constructor in the prefix are now exhaustive -- concrete proofs. The let roots include exact allocation, common-fvar -- body opening, context transport, and local-scope restoration. - { root := ``Ix.Tc.RecM.isDefEqWhnf_wf, + { root := ``Ix.Kernel.RecM.isDefEqWhnf_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.IsDefEqWhnf.ofPhases, + { root := ``Ix.Kernel.RecM.IsDefEqWhnf.ofPhases, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryDefEqWhnfApp_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.tryDefEqWhnfApp_wf, + standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TryDefEqWhnfApp.ofResources, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.TryDefEqWhnfApp.ofResources, + standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.TcM.openLetWithFV_scope, + { root := ``Ix.Kernel.TcM.openLetWithFV_scope, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.withLctxScope_openLetWithFV_wf, + { root := ``Ix.Kernel.RecM.withLctxScope_openLetWithFV_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryDefEqWhnfLet_wf, + { root := ``Ix.Kernel.RecM.tryDefEqWhnfLet_wf, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TryDefEqWhnfLet.ofResources, + { root := ``Ix.Kernel.RecM.TryDefEqWhnfLet.ofResources, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isNatLike_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.isNatLike_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.natSuccOf_wf, + { root := ``Ix.Kernel.RecM.natSuccOf_wf, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.NatSuccOf.ofResources, + { root := ``Ix.Kernel.RecM.NatSuccOf.ofResources, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isDefEqNatAfterLiteral_wf, + { root := ``Ix.Kernel.RecM.isDefEqNatAfterLiteral_wf, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isDefEqNat_wf, + { root := ``Ix.Kernel.RecM.isDefEqNat_wf, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryDefEqWhnfNat_wf, + { root := ``Ix.Kernel.RecM.tryDefEqWhnfNat_wf, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TryDefEqWhnfNat.ofResources, + { root := ``Ix.Kernel.RecM.TryDefEqWhnfNat.ofResources, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isDefEqWhnfAfterStructural_wf, + { root := ``Ix.Kernel.RecM.isDefEqWhnfAfterStructural_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.IsDefEqWhnfAfterStructural.ofNat, + { root := ``Ix.Kernel.RecM.IsDefEqWhnfAfterStructural.ofNat, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryDefEqWhnfStructural_wf, + { root := ``Ix.Kernel.RecM.tryDefEqWhnfStructural_wf, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TryDefEqWhnfStructural.ofResources, + { root := ``Ix.Kernel.RecM.TryDefEqWhnfStructural.ofResources, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -8825,37 +8812,37 @@ private def roots : Array RootAllowance := #[ -- an explicit term builder. The builder's lifted source and generated #0 -- application are translated structurally before the recursive comparison -- is composed with Theory eta; the ordered reverse attempt uses symmetry. - { root := ``Ix.Tc.TcM.lift_whnf_wf_of_resources, + { root := ``Ix.Kernel.TcM.lift_whnf_wf_of_resources, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.compareEtaExpansion_wf, + { root := ``Ix.Kernel.RecM.compareEtaExpansion_wf, standardAxioms := standard, nativeAxioms := expressionNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryEtaExpansionAfterGuard_wf, + { root := ``Ix.Kernel.RecM.tryEtaExpansionAfterGuard_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryEtaExpansion_wf, + { root := ``Ix.Kernel.RecM.tryEtaExpansion_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryDefEqWhnfEtaAfterGuard_wf, + { root := ``Ix.Kernel.RecM.tryDefEqWhnfEtaAfterGuard_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryDefEqWhnfEta_wf, + { root := ``Ix.Kernel.RecM.tryDefEqWhnfEta_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TryDefEqWhnfEta.ofResources, + { root := ``Ix.Kernel.RecM.TryDefEqWhnfEta.ofResources, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isDefEqWhnfAfterNat_wf, + { root := ``Ix.Kernel.RecM.isDefEqWhnfAfterNat_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.IsDefEqWhnfAfterNat.ofEta, + { root := ``Ix.Kernel.RecM.IsDefEqWhnfAfterNat.ofEta, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -8863,19 +8850,19 @@ private def roots : Array RootAllowance := #[ -- The final-WHNF String phase reuses the exact expansion plans proved for -- the earlier DefEq tier. Its optional result preserves the original -- two-way short-circuit order; reverse success is justified by symmetry. - { root := ``Ix.Tc.RecM.tryDefEqWhnfStringAfterGuard_wf, + { root := ``Ix.Kernel.RecM.tryDefEqWhnfStringAfterGuard_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryDefEqWhnfString_wf, + { root := ``Ix.Kernel.RecM.tryDefEqWhnfString_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TryDefEqWhnfString.ofContext, + { root := ``Ix.Kernel.RecM.TryDefEqWhnfString.ofContext, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isDefEqWhnfAfterEta_wf, + { root := ``Ix.Kernel.RecM.isDefEqWhnfAfterEta_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.IsDefEqWhnfAfterEta.ofString, + { root := ``Ix.Kernel.RecM.IsDefEqWhnfAfterEta.ofString, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, @@ -8883,25 +8870,25 @@ private def roots : Array RootAllowance := #[ -- Proof irrelevance is concrete through the memoized proposition -- classifier; unit-like and structure-eta soundness remain separately -- named contracts until their exact inductive laws are supplied. - { root := ``Ix.Tc.RecM.isDefEqWhnfAfterUnit_wf, + { root := ``Ix.Kernel.RecM.isDefEqWhnfAfterUnit_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.IsDefEqWhnfAfterUnit.ofClassifier, + { root := ``Ix.Kernel.RecM.IsDefEqWhnfAfterUnit.ofClassifier, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isDefEqWhnfAfterStructEta_wf, + { root := ``Ix.Kernel.RecM.isDefEqWhnfAfterStructEta_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.IsDefEqWhnfAfterStructEta.ofUnitAndProof, + { root := ``Ix.Kernel.RecM.IsDefEqWhnfAfterStructEta.ofUnitAndProof, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.isDefEqWhnfAfterString_wf, + { root := ``Ix.Kernel.RecM.isDefEqWhnfAfterString_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.IsDefEqWhnfAfterString.ofStructEta, + { root := ``Ix.Kernel.RecM.IsDefEqWhnfAfterString.ofStructEta, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, @@ -8910,19 +8897,19 @@ private def roots : Array RootAllowance := #[ -- narrow unique-inhabitant law for that trusted zero-index, one-nullary- -- constructor shape; it does not recover the legacy whole-environment -- inductive oracle. - { root := ``Ix.Tc.RecM.isUnitLikeInductive_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.RecM.isUnitLikeInductive_wf, + standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.tryDefEqUnit_wf, + { root := ``Ix.Kernel.RecM.tryDefEqUnit_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.TryDefEqUnit.ofResources, + { root := ``Ix.Kernel.RecM.TryDefEqUnit.ofResources, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, - { root := ``Ix.Tc.RecM.DefEqLazyDeltaStep.ofKernelResources, + { root := ``Ix.Kernel.RecM.DefEqLazyDeltaStep.ofKernelResources, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := legacyWholeEnv }, @@ -8932,57 +8919,57 @@ private def roots : Array RootAllowance := #[ -- The positive structure classifier cannot manufacture semantic eta on -- its own, and every exported root remains quarantined from both legacy -- whole-environment and broad delta-authority paths. - { root := ``Ix.Tc.TrKExprS.prj_components, + { root := ``Ix.Kernel.TrKExprS.prj_components, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.tryEtaStructFields_wf, + { root := ``Ix.Kernel.RecM.tryEtaStructFields_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.etaExpansionBaseLoop_wf, + { root := ``Ix.Kernel.RecM.etaExpansionBaseLoop_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.etaExpansionBase_wf, + { root := ``Ix.Kernel.RecM.etaExpansionBase_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.tryEtaStructAfterTypes_wf, + { root := ``Ix.Kernel.RecM.tryEtaStructAfterTypes_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.normalizeEtaStructSource_wf, + { root := ``Ix.Kernel.RecM.normalizeEtaStructSource_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.tryEtaStructAfterConstructor_wf, + { root := ``Ix.Kernel.RecM.tryEtaStructAfterConstructor_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.tryEtaStructAfterNormalization_wf, + { root := ``Ix.Kernel.RecM.tryEtaStructAfterNormalization_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.tryEtaStruct_wf, + { root := ``Ix.Kernel.RecM.tryEtaStruct_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.tryDefEqWhnfStructEta_wf, + { root := ``Ix.Kernel.RecM.tryDefEqWhnfStructEta_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.TryDefEqWhnfStructEta.ofResources, + { root := ``Ix.Kernel.RecM.TryDefEqWhnfStructEta.ofResources, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, -- The final-WHNF phases are now assembled in exact production order. - { root := ``Ix.Tc.RecM.FinalWhnfClosureResources, + { root := ``Ix.Kernel.RecM.FinalWhnfClosureResources, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.FinalWhnfClosureResources.afterStructural, + { root := ``Ix.Kernel.RecM.FinalWhnfClosureResources.afterStructural, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.FinalWhnfClosureResources.finalWhnf, + { root := ``Ix.Kernel.RecM.FinalWhnfClosureResources.finalWhnf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, @@ -8990,36 +8977,36 @@ private def roots : Array RootAllowance := #[ -- Recursive DefEq closure: trusted finite expression references authorize -- only the two direct roots of an ordinary result entry. The complete -- inner tier then feeds the guarded public cache shell. - { root := ``Ix.Tc.CacheEntry.defEqReferencesAuthorized, + { root := ``Ix.Kernel.CacheEntry.defEqReferencesAuthorized, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.DefEqInner.WF, + { root := ``Ix.Kernel.RecM.DefEqInner.WF, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.isDefEq_wf, + { root := ``Ix.Kernel.RecM.isDefEq_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.DefEqClosureResources, + { root := ``Ix.Kernel.RecM.DefEqClosureResources, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.DefEqClosureResources.stopped, + { root := ``Ix.Kernel.RecM.DefEqClosureResources.stopped, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.DefEqClosureResources.lazyDelta, + { root := ``Ix.Kernel.RecM.DefEqClosureResources.lazyDelta, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.DefEqClosureResources.inner, + { root := ``Ix.Kernel.RecM.DefEqClosureResources.inner, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.DefEqClosureResources.entryPoint, + { root := ``Ix.Kernel.RecM.DefEqClosureResources.entryPoint, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.DefEqClosureResources.nextDefEq_wf, + { root := ``Ix.Kernel.RecM.DefEqClosureResources.nextDefEq_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, @@ -9027,21 +9014,21 @@ private def roots : Array RootAllowance := #[ -- Inference and DefEq consume the same predecessor table and suffix model; -- their fixed-universe pair closes before it is joined to the four WHNF -- fields. - { root := ``Ix.Tc.UncachedInference.Context.nextInfer_wf, + { root := ``Ix.Kernel.UncachedInference.Context.nextInfer_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.Methods.InferDefEqClosedAt, + { root := ``Ix.Kernel.Methods.InferDefEqClosedAt, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.InferDefEqClosureContext, + { root := ``Ix.Kernel.InferDefEqClosureContext, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.InferDefEqClosureContext.layer, + { root := ``Ix.Kernel.InferDefEqClosureContext.layer, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.InferDefEqClosureContext.closedAt, + { root := ``Ix.Kernel.InferDefEqClosureContext.closedAt, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, @@ -9049,29 +9036,29 @@ private def roots : Array RootAllowance := #[ -- Legacy all-depth six-field knot assembly under the canonical production -- cache stack. These roots remain audited as migration adapters, but the -- bounded public interfaces below are forbidden from depending on them. - { root := ``Ix.Tc.kernelCacheFallback, + { root := ``Ix.Kernel.kernelCacheFallback, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.kernelCacheSemantics_eq_k1, + { root := ``Ix.Kernel.kernelCacheSemantics_eq_k1, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.Methods.ClosedAt, + { root := ``Ix.Kernel.Methods.ClosedAt, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.Methods.ClosedAt.of_parts, + { root := ``Ix.Kernel.Methods.ClosedAt.of_parts, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.runRec_wfAt, + { root := ``Ix.Kernel.TcM.runRec_wfAt, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecursiveMethodClosureContext, + { root := ``Ix.Kernel.RecursiveMethodClosureContext, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecursiveMethodClosureContext.closedAt, + { root := ``Ix.Kernel.RecursiveMethodClosureContext.closedAt, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecursiveMethodClosureContext.methodsN, + { root := ``Ix.Kernel.RecursiveMethodClosureContext.methodsN, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, @@ -9081,7 +9068,7 @@ private def roots : Array RootAllowance := #[ -- an unbounded successor-sort chain. The replacement below separates the -- finite result footprint from fuel-indexed method-call domains. { root := - ``Ix.Tc.FiniteSupportBoundary.SyntaxInferenceResources.no_sort_source, + ``Ix.Kernel.FiniteSupportBoundary.SyntaxInferenceResources.no_sort_source, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := k1ForbiddenDependencies }, @@ -9089,40 +9076,40 @@ private def roots : Array RootAllowance := #[ -- method-table depths selected by this run's recursion fuel. The public -- adapters consume the terminal successor-layer domain and have no -- `sorryAx` dependency. - { root := ``Ix.Tc.Methods.CallDomain.empty_within, + { root := ``Ix.Kernel.Methods.CallDomain.empty_within, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.Methods.CallDomain.singletonInfer_within, + { root := ``Ix.Kernel.Methods.CallDomain.singletonInfer_within, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.Methods.methodsOut_wfAtOn, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.Methods.methodsOut_wfAtOn, + standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.Methods.CallScheduleAt.methodsN, + { root := ``Ix.Kernel.Methods.CallScheduleAt.methodsN, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.Methods.CallScheduleAt.nextSelected, + { root := ``Ix.Kernel.Methods.CallScheduleAt.nextSelected, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecursiveMethodRunContext, + { root := ``Ix.Kernel.RecursiveMethodRunContext, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.TcM.whnf.wf, + { root := ``Ix.Kernel.TcM.whnf.wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.TcM.infer.wf, + { root := ``Ix.Kernel.TcM.infer.wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.TcM.isDefEq.wf, + { root := ``Ix.Kernel.TcM.isDefEq.wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.Methods.SortSchedule.two, + { root := ``Ix.Kernel.Methods.SortSchedule.two, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.TcM.infer.sort_wf_bounded, + { root := ``Ix.Kernel.TcM.infer.sort_wf_bounded, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.TcM.infer.sort_wf_fuel_one, + { root := ``Ix.Kernel.TcM.infer.sort_wf_fuel_one, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, @@ -9130,382 +9117,382 @@ private def roots : Array RootAllowance := #[ -- K3 reconstructs the typed source translation from untyped/scoped -- checker ingress. These roots are usable before the final checkConst -- assembly and do not depend on its statement placeholder. - { root := ``Ix.Tc.KUniv.scoped_iff_toVLevel_wf, + { root := ``Ix.Kernel.KUniv.scoped_iff_toVLevel_wf, standardAxioms := propextOnly, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.PreTrKExprS.upgradeOfTyped, + { root := ``Ix.Kernel.PreTrKExprS.upgradeOfTyped, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TrKExprS.openFVarZero, + { root := ``Ix.Kernel.TrKExprS.openFVarZero, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Lean4Lean.VExpr.inst_subst_cons, + { root := ``Ix.Theory.Named.VExpr.inst_subst_cons, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RawCtxInterp.find?_inl, + { root := ``Ix.Kernel.RawCtxInterp.find?_inl, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RawCtxInterp.bvars_eq, + { root := ``Ix.Kernel.RawCtxInterp.bvars_eq, standardAxioms := propextOnly, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RawProjRel.none_substCompatible, + { root := ``Ix.Kernel.RawProjRel.none_substCompatible, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RawExprRel.toPre_of_scoped_aux, + { root := ``Ix.Kernel.RawExprRel.toPre_of_scoped_aux, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RawExprRel.toPre_of_scoped, + { root := ``Ix.Kernel.RawExprRel.toPre_of_scoped, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RawDeclRel.toPre_of_scope, + { root := ``Ix.Kernel.RawDeclRel.toPre_of_scope, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.PendingDecl.toPre_of_scope, + { root := ``Ix.Kernel.PendingDecl.toPre_of_scope, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TypeCheckEvidence.isType, + { root := ``Ix.Kernel.TypeCheckEvidence.isType, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.ValueCheckEvidence.hasType, + { root := ``Ix.Kernel.ValueCheckEvidence.hasType, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.StandaloneCheckEvidence.accepted, + { root := ``Ix.Kernel.StandaloneCheckEvidence.accepted, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.StandaloneCheckResult.accepted, + { root := ``Ix.Kernel.StandaloneCheckResult.accepted, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RawDeclRel.wfOfAccepted, + { root := ``Ix.Kernel.RawDeclRel.wfOfAccepted, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.PendingDecl.promoteOfAccepted, + { root := ``Ix.Kernel.PendingDecl.promoteOfAccepted, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.PendingDecl.checkResultAndPromote, + { root := ``Ix.Kernel.PendingDecl.checkResultAndPromote, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.validateUnivParamsSeen_go_sound, + { root := ``Ix.Kernel.RecM.validateUnivParamsSeen_go_sound, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.validateUnivParamsSeen_sound, + { root := ``Ix.Kernel.RecM.validateUnivParamsSeen_sound, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.validateUnivRootsList_sound, + { root := ``Ix.Kernel.RecM.validateUnivRootsList_sound, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.validateUnivRootsArray_sound, + { root := ``Ix.Kernel.RecM.validateUnivRootsArray_sound, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.validateExprWellScoped_go_sound, + { root := ``Ix.Kernel.RecM.validateExprWellScoped_go_sound, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.validateExprWellScoped_sound, + { root := ``Ix.Kernel.RecM.validateExprWellScoped_sound, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.validateConstWellScoped_sound, + { root := ``Ix.Kernel.RecM.validateConstWellScoped_sound, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.PendingDecl.toPre_of_validation, + { root := ``Ix.Kernel.PendingDecl.toPre_of_validation, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.PendingDecl.checkValidatedResultAndPromote, + { root := ``Ix.Kernel.PendingDecl.checkValidatedResultAndPromote, standardAxioms := standard, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.validateUnivParamsSeen_go_frame, + { root := ``Ix.Kernel.RecM.validateUnivParamsSeen_go_frame, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.validateUnivParamsSeen_frame, + { root := ``Ix.Kernel.RecM.validateUnivParamsSeen_frame, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.validateUnivRootsList_frame, + { root := ``Ix.Kernel.RecM.validateUnivRootsList_frame, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.validateUnivRootsArray_frame, + { root := ``Ix.Kernel.RecM.validateUnivRootsArray_frame, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.getConst_frame, + { root := ``Ix.Kernel.TcM.getConst_frame, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.hasConst_frame, + { root := ``Ix.Kernel.TcM.hasConst_frame, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.validateExprWellScoped_go_frame, + { root := ``Ix.Kernel.RecM.validateExprWellScoped_go_frame, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.validateExprWellScoped_frame, + { root := ``Ix.Kernel.RecM.validateExprWellScoped_frame, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.validateConstWellScoped_frame, + { root := ``Ix.Kernel.RecM.validateConstWellScoped_frame, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.LazyFaultPreserves.withInferOnly, + { root := ``Ix.Kernel.TcM.LazyFaultPreserves.withInferOnly, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkTypePipeline_sound, + { root := ``Ix.Kernel.RecM.checkTypePipeline_sound, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkValuePipeline_sound, + { root := ``Ix.Kernel.RecM.checkValuePipeline_sound, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.Methods.FullInferenceWFAtOn.ofTypedIngress, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.Methods.FullInferenceWFAtOn.ofTypedIngress, + standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.Methods.FullInferenceWFAtOn.ofSingletonSort, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.Methods.FullInferenceWFAtOn.ofSingletonSort, + standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.StandalonePipelineResources.singletonSortAxiom, + { root := ``Ix.Kernel.StandalonePipelineResources.singletonSortAxiom, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkTypePipeline_bounded_sound, + { root := ``Ix.Kernel.RecM.checkTypePipeline_bounded_sound, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkValuePipeline_bounded_sound, + { root := ``Ix.Kernel.RecM.checkValuePipeline_bounded_sound, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkConstMember_axiom_sound, + { root := ``Ix.Kernel.RecM.checkConstMember_axiom_sound, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkConstMember_defn_sound, + { root := ``Ix.Kernel.RecM.checkConstMember_defn_sound, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkConstMember_sound, + { root := ``Ix.Kernel.RecM.checkConstMember_sound, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkConstMember_validation_success, + { root := ``Ix.Kernel.RecM.checkConstMember_validation_success, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkConstMember_pending_sound, + { root := ``Ix.Kernel.RecM.checkConstMember_pending_sound, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkConstMemberFresh_pending_sound, + { root := ``Ix.Kernel.RecM.checkConstMemberFresh_pending_sound, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.StandaloneRoute.axiomRoute, + { root := ``Ix.Kernel.StandaloneRoute.axiomRoute, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkConst_standalone_pending_sound, + { root := ``Ix.Kernel.RecM.checkConst_standalone_pending_sound, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkNoUnsafeRefs_go_frame, + { root := ``Ix.Kernel.RecM.checkNoUnsafeRefs_go_frame, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkNoUnsafeRefs_frame, + { root := ``Ix.Kernel.RecM.checkNoUnsafeRefs_frame, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.KernelStateWF.rebaseWorld, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.KernelStateWF.rebaseWorld, + standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.WhnfStateInv.rebaseWorld, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.WhnfStateInv.rebaseWorld, + standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.reset_whnf_entry, + { root := ``Ix.Kernel.TcM.reset_whnf_entry, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.FullInferPost.of_typed, + { root := ``Ix.Kernel.FullInferPost.of_typed, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.WF.withInv, + { root := ``Ix.Kernel.TcM.WF.withInv, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.inferWith_fullHit_pre_acceptance, + { root := ``Ix.Kernel.RecM.inferWith_fullHit_pre_acceptance, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.inferUncached_sort_full_wf, + { root := ``Ix.Kernel.RecM.inferUncached_sort_full_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.inferUncached_var_full_wf, + { root := ``Ix.Kernel.RecM.inferUncached_var_full_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.inferUncached_fvar_full_wf, + { root := ``Ix.Kernel.RecM.inferUncached_fvar_full_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.inferUncached_const_full_wf, + { root := ``Ix.Kernel.RecM.inferUncached_const_full_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.inferUncached_nat_full_wf, + { root := ``Ix.Kernel.RecM.inferUncached_nat_full_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.inferUncached_str_full_wf, + { root := ``Ix.Kernel.RecM.inferUncached_str_full_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.FullInferenceStepContext, + { root := ``Ix.Kernel.FullInferenceStepContext, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.inferUncached_app_full_wf, + { root := ``Ix.Kernel.RecM.inferUncached_app_full_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.PreservesInferOnly.strengthenWFValue, + { root := ``Ix.Kernel.TcM.PreservesInferOnly.strengthenWFValue, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.PreservesInferOnly.withInferOnly, + { root := ``Ix.Kernel.TcM.PreservesInferOnly.withInferOnly, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.PreservesInferOnly.openBinder, + { root := ``Ix.Kernel.TcM.PreservesInferOnly.openBinder, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.PreservesInferOnly.inferKey, + { root := ``Ix.Kernel.TcM.PreservesInferOnly.inferKey, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.cacheInferResult_preservesInferOnly, + { root := ``Ix.Kernel.RecM.cacheInferResult_preservesInferOnly, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.withLctxScope_preservesInferOnly, + { root := ``Ix.Kernel.RecM.withLctxScope_preservesInferOnly, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.ensureForallDirect_preservesInferOnly, + { root := ``Ix.Kernel.RecM.ensureForallDirect_preservesInferOnly, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.ensureSortDirect_preservesInferOnly, + { root := ``Ix.Kernel.RecM.ensureSortDirect_preservesInferOnly, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.Methods.methodsOut_preservesInferOnly, + { root := ``Ix.Kernel.Methods.methodsOut_preservesInferOnly, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.Methods.methodsN_preservesInferOnly, + { root := ``Ix.Kernel.Methods.methodsN_preservesInferOnly, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.Methods.PreservesInferOnly.isDefEq_full_wf, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.Methods.PreservesInferOnly.isDefEq_full_wf, + standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.openBinder_scope_base, + { root := ``Ix.Kernel.TcM.openBinder_scope_base, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.openBinder_pre_scope, + { root := ``Ix.Kernel.TcM.openBinder_pre_scope, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.KExpr.abstractFVarsSpec_instantiateRevSpec_singleton, + { root := ``Ix.Kernel.KExpr.abstractFVarsSpec_instantiateRevSpec_singleton, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TrKExprS.closeOpenedFVarZero, + { root := ``Ix.Kernel.TrKExprS.closeOpenedFVarZero, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.withLctxScope_openBinder_pre_wf, + { root := ``Ix.Kernel.RecM.withLctxScope_openBinder_pre_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.inferUncached_lam_full_wf, + { root := ``Ix.Kernel.RecM.inferUncached_lam_full_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.inferUncached_all_full_wf, + { root := ``Ix.Kernel.RecM.inferUncached_all_full_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.openLet_scope_base, + { root := ``Ix.Kernel.TcM.openLet_scope_base, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.openLet_pre_scope, + { root := ``Ix.Kernel.TcM.openLet_pre_scope, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.withLctxScope_openLet_pre_wf, + { root := ``Ix.Kernel.RecM.withLctxScope_openLet_pre_wf, standardAxioms := standard, nativeAxioms := expressionNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.inferUncached_let_full_wf, + { root := ``Ix.Kernel.RecM.inferUncached_let_full_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.ProjectionInference.FullWFAt.of_semantic_and_policy, + { root := ``Ix.Kernel.ProjectionInference.FullWFAt.of_semantic_and_policy, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.inferUncached_prj_full_wf, + { root := ``Ix.Kernel.RecM.inferUncached_prj_full_wf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.inferUncached_full_wf, + { root := ``Ix.Kernel.RecM.inferUncached_full_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.inferWith_full_wf, + { root := ``Ix.Kernel.RecM.inferWith_full_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.infer_full_wf, + { root := ``Ix.Kernel.RecM.infer_full_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.PreservesInferOnly.instantiateUnivParams, + { root := ``Ix.Kernel.TcM.PreservesInferOnly.instantiateUnivParams, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.inferUncached_preservesInferOnly, + { root := ``Ix.Kernel.RecM.inferUncached_preservesInferOnly, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.ProjectionInference.preservesInferOnlyAt, + { root := ``Ix.Kernel.ProjectionInference.preservesInferOnlyAt, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecM.infer_preservesInferOnly_of_whnf, + { root := ``Ix.Kernel.RecM.infer_preservesInferOnly_of_whnf, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, -- K3 closes the concrete operational policy and retains the old strong -- all-support inference roots below as compatibility artifacts. The -- public checker now consumes the bounded successor-layer resources above. - { root := ``Ix.Tc.Methods.next_preservesInferOnly, + { root := ``Ix.Kernel.Methods.next_preservesInferOnly, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.Methods.inferOnlyClosed, + { root := ``Ix.Kernel.Methods.inferOnlyClosed, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.Methods.methodsN_concrete_preservesInferOnly, + { root := ``Ix.Kernel.Methods.methodsN_concrete_preservesInferOnly, standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.Methods.FullInferenceWFAt, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.Methods.FullInferenceWFAt, + standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecursiveMethodClosureContext.fullInferenceContext, + { root := ``Ix.Kernel.RecursiveMethodClosureContext.fullInferenceContext, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecursiveMethodClosureContext.next_fullInferenceWFAt, + { root := ``Ix.Kernel.RecursiveMethodClosureContext.next_fullInferenceWFAt, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.Methods.methodsOut_fullInferenceWFAt, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.Methods.methodsOut_fullInferenceWFAt, + standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecursiveMethodClosureContext.methodsN_fullInferenceWFAt, + { root := ``Ix.Kernel.RecursiveMethodClosureContext.methodsN_fullInferenceWFAt, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.RecursiveMethodClosureContext.publicInfer_full_wf, + { root := ``Ix.Kernel.RecursiveMethodClosureContext.publicInfer_full_wf, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.checkConst.rollback_on_error, + { root := ``Ix.Kernel.TcM.checkConst.rollback_on_error, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.checkConst.rollback_preserves_kernel, + { root := ``Ix.Kernel.TcM.checkConst.rollback_preserves_kernel, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.TcM.checkConst.wf, + { root := ``Ix.Kernel.TcM.checkConst.wf, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.TcM.checkConst.rejected_of_no_decl_wf, + { root := ``Ix.Kernel.TcM.checkConst.rejected_of_no_decl_wf, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.TcM.checkConst.axiom_pending_sound, + { root := ``Ix.Kernel.TcM.checkConst.axiom_pending_sound, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkConstMemberFresh_scoped_pending_evidence, + { root := ``Ix.Kernel.RecM.checkConstMemberFresh_scoped_pending_evidence, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := scopedK2SForbiddenDependencies }, @@ -9515,54 +9502,54 @@ private def roots : Array RootAllowance := #[ -- singleton-definition adapter consumes K3; inductive/recursor bodies keep -- their E2 oracle premise explicit. Quotients are audited as excluded from -- this authority rather than being silently admitted by the block theorem. - { root := ``Ix.Tc.ExactCheckBlock.rebaseWorld, + { root := ``Ix.Kernel.ExactCheckBlock.rebaseWorld, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.coordinatedBlockIfKind_success_trace, + { root := ``Ix.Kernel.RecM.coordinatedBlockIfKind_success_trace, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.classifyBlock_wf, + { root := ``Ix.Kernel.RecM.classifyBlock_wf, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.coordinatedBlockFor_some_preserves, + { root := ``Ix.Kernel.RecM.coordinatedBlockFor_some_preserves, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.CacheInvariant.replayCoordinatedMember, + { root := ``Ix.Kernel.CacheInvariant.replayCoordinatedMember, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.CacheInvariant.rejectsSuccessWithUntrustedMember, + { root := ``Ix.Kernel.CacheInvariant.rejectsSuccessWithUntrustedMember, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkCoordinatedBlock_accepted, + { root := ``Ix.Kernel.RecM.checkCoordinatedBlock_accepted, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkCoordinatedBlock_rejected, + { root := ``Ix.Kernel.RecM.checkCoordinatedBlock_rejected, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkConst_success_disposition, + { root := ``Ix.Kernel.RecM.checkConst_success_disposition, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.TcM.checkConst.blockDisposition, + { root := ``Ix.Kernel.TcM.checkConst.blockDisposition, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.certifySingletonDefinition, + { root := ``Ix.Kernel.RecM.certifySingletonDefinition, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.certifySingletonDefinitionScoped, + { root := ``Ix.Kernel.RecM.certifySingletonDefinitionScoped, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.RecM.certifyOracleBackedBlock, + { root := ``Ix.Kernel.RecM.certifyOracleBackedBlock, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.coordinatedBlockFor_quotient, + { root := ``Ix.Kernel.RecM.coordinatedBlockFor_quotient, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.Catalog.quotient_not_coordinated, + { root := ``Ix.Kernel.Catalog.quotient_not_coordinated, standardAxioms := propextOnly, forbiddenDependencies := boundedKnotForbiddenDependencies }, -- Quotients remain physically standalone, but semantic admission is one @@ -9570,74 +9557,74 @@ private def roots : Array RootAllowance := #[ -- equation. The production bridge inverts all four real checkQuot runs, -- converts digest equality through a finite collision scope, and publishes -- the completed transaction as one exact trusted-log event. Its temporary - -- Lean4Lean semantic input is an explicit theorem parameter, not an axiom. - { root := ``Ix.Tc.QuotientAdmissionStep.bind, + -- Ix.Theory.Named semantic input is an explicit theorem parameter, not an axiom. + { root := ``Ix.Kernel.QuotientAdmissionStep.bind, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.QuotientAdmissionStep.le, + { root := ``Ix.Kernel.QuotientAdmissionStep.le, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.QuotientBundleAdmission.catalogEntries, + { root := ``Ix.Kernel.QuotientBundleAdmission.catalogEntries, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.QuotientBundleAdmission.nameAssignments, + { root := ``Ix.Kernel.QuotientBundleAdmission.nameAssignments, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.QuotientBundleAdmission.toAddQuot, + { root := ``Ix.Kernel.QuotientBundleAdmission.toAddQuot, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.QuotientBundleAdmission.le, + { root := ``Ix.Kernel.QuotientBundleAdmission.le, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.QuotientBundleAdmission.quotType, + { root := ``Ix.Kernel.QuotientBundleAdmission.quotType, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.QuotientBundleAdmission.quotCtor, + { root := ``Ix.Kernel.QuotientBundleAdmission.quotCtor, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.QuotientBundleAdmission.quotLift, + { root := ``Ix.Kernel.QuotientBundleAdmission.quotLift, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.QuotientBundleAdmission.quotInd, + { root := ``Ix.Kernel.QuotientBundleAdmission.quotInd, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.QuotientBundleAdmission.quotientDefEq, + { root := ``Ix.Kernel.QuotientBundleAdmission.quotientDefEq, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.QuotientAdmission.wf, + { root := ``Ix.Kernel.QuotientAdmission.wf, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.QuotientAdmission.le, + { root := ``Ix.Kernel.QuotientAdmission.le, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkQuot_success_typeAddress, + { root := ``Ix.Kernel.RecM.checkQuot_success_typeAddress, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkQuot_success_levels, + { root := ``Ix.Kernel.RecM.checkQuot_success_levels, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.RecM.checkQuot_success_type, + { root := ``Ix.Kernel.RecM.checkQuot_success_type, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.CheckedQuotientBundle.toAdmission, + { root := ``Ix.Kernel.CheckedQuotientBundle.toAdmission, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.QuotientAdmission.entry, + { root := ``Ix.Kernel.QuotientAdmission.entry, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.QuotientAdmission.admit, + { root := ``Ix.Kernel.QuotientAdmission.admit, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.QuotientAdmission.newlyTrustedMember, + { root := ``Ix.Kernel.QuotientAdmission.newlyTrustedMember, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.CheckedQuotientBundle.admitAtomically, + { root := ``Ix.Kernel.CheckedQuotientBundle.admitAtomically, standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.AmbientNat.E0.atomicAdmission, + { root := ``Ix.Kernel.AmbientNat.E0.atomicAdmission, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.AmbientNat.E0.rejectsPrematureSuccess, + { root := ``Ix.Kernel.AmbientNat.E0.rejectsPrematureSuccess, standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, @@ -9645,125 +9632,125 @@ private def roots : Array RootAllowance := #[ -- domain, proves buildAnonWork is an exact duplicate-free partition, and -- composes successful items in a constructive collapsed-block order. The -- serial roots recover real successful checkConst calls from the public - -- result array before applying the named C2 success adapter. - { root := ``Ix.Tc.WorkCovers.covered, + -- result array before applying the named per-item success adapter. + { root := ``Ix.Kernel.WorkCovers.covered, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.WorkCovers.subjectOfCovered, + { root := ``Ix.Kernel.WorkCovers.subjectOfCovered, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.VerifyWorld.AcceptsAddress.mono, + { root := ``Ix.Kernel.VerifyWorld.AcceptsAddress.mono, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.WorkItemAccepted.mono, + { root := ``Ix.Kernel.WorkItemAccepted.mono, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.WorkItemAccepted.acceptsAddress, + { root := ``Ix.Kernel.WorkItemAccepted.acceptsAddress, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.WellFoundedBlocks.noTwoCycle, + { root := ``Ix.Kernel.WellFoundedBlocks.noTwoCycle, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.acceptedWorkset_subjectWF, + { root := ``Ix.Kernel.acceptedWorkset_subjectWF, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.IxonEnv.dependencyCatalog_blockOf, + { root := ``Ix.Kernel.IxonEnv.dependencyCatalog_blockOf, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.IxonEnv.dependencyCatalog_dependsOn_iff, + { root := ``Ix.Kernel.IxonEnv.dependencyCatalog_dependsOn_iff, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.IxonExpr.DeclReference.target_mem_refs, + { root := ``Ix.Kernel.IxonExpr.DeclReference.target_mem_refs, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.IxonConstant.SemanticDependency.target_mem_refs, + { root := ``Ix.Kernel.IxonConstant.SemanticDependency.target_mem_refs, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.ExactAnonEntry.getConst, + { root := ``Ix.Kernel.ExactAnonEntry.getConst, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.ExactAnonEntry.constant_unique, + { root := ``Ix.Kernel.ExactAnonEntry.constant_unique, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.ExactAnonEntry.buildAnonWorkItem_eq, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.ExactAnonEntry.buildAnonWorkItem_eq, + standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.AnonWorkEnvWF.buildAnonWork_eq_expected, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.AnonWorkEnvWF.buildAnonWork_eq_expected, + standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.AnonWorkEnvWF.mem_expectedAnonWork_iff, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.AnonWorkEnvWF.mem_expectedAnonWork_iff, + standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.AnonWorkItem.ofConstantInfo_root, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.AnonWorkItem.ofConstantInfo_root, + standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.AnonWorkItem.covers_root, + { root := ``Ix.Kernel.AnonWorkItem.covers_root, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.AnonWorkItem.ofConstantInfo_primary_mem_targets, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.AnonWorkItem.ofConstantInfo_primary_mem_targets, + standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.AnonWorkEnvWF.source_covered, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.AnonWorkEnvWF.source_covered, + standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.AnonWorkEnvWF.covered_is_source, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.AnonWorkEnvWF.covered_is_source, + standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.AnonWorkEnvWF.expected_primary_mem_targets, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.AnonWorkEnvWF.expected_primary_mem_targets, + standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.ExactAnonEntry.blockOfAddr_eq_owner, + { root := ``Ix.Kernel.ExactAnonEntry.blockOfAddr_eq_owner, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.ExactAnonEntry.blockOfAddr_eq_self, + { root := ``Ix.Kernel.ExactAnonEntry.blockOfAddr_eq_self, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.AnonWorkEnvWF.matches_blockOfAddr, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.AnonWorkEnvWF.matches_blockOfAddr, + standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.AnonWorkEnvWF.expectedAnonWork_covers, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.AnonWorkEnvWF.expectedAnonWork_covers, + standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.AnonWorkEnvWF.expectedAnonWork_matchesCatalog, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.AnonWorkEnvWF.expectedAnonWork_matchesCatalog, + standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.AnonWorkEnvWF.buildAnonWork_exact, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.AnonWorkEnvWF.buildAnonWork_exact, + standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.finishAnonCheckItem_results, + { root := ``Ix.Kernel.finishAnonCheckItem_results, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.runAnonCheckItem_preserves_result, + { root := ``Ix.Kernel.runAnonCheckItem_preserves_result, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.runAnonCheckList_preserves_result, + { root := ``Ix.Kernel.runAnonCheckList_preserves_result, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.runAnonCheckItem_error_result, + { root := ``Ix.Kernel.runAnonCheckItem_error_result, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.serialChecksSucceeded_of_results, + { root := ``Ix.Kernel.serialChecksSucceeded_of_results, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.SerialChecksSucceeded.successfulStep, + { root := ``Ix.Kernel.SerialChecksSucceeded.successfulStep, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.SerialChecksSucceeded.allAccepted, + { root := ``Ix.Kernel.SerialChecksSucceeded.allAccepted, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.AnonWorkEnvWF.checkEnvAnon_eq_serial, + { root := ``Ix.Kernel.AnonWorkEnvWF.checkEnvAnon_eq_serial, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.AnonWorkEnvWF.checkEnvAnon_subjectWF, + { root := ``Ix.Kernel.AnonWorkEnvWF.checkEnvAnon_subjectWF, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.E1Fixture.exactSubjectsAndAssumptions, + { root := ``Ix.Kernel.E1Fixture.exactSubjectsAndAssumptions, standardAxioms := standardWithoutChoice, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.E1Fixture.droppingWorkItem_breaks_coverage, + { root := ``Ix.Kernel.E1Fixture.droppingWorkItem_breaks_coverage, standardAxioms := propextOnly, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.E1Fixture.unresolvedDependency_breaks_closure, + { root := ``Ix.Kernel.E1Fixture.unresolvedDependency_breaks_closure, standardAxioms := propextOnly, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.E1Fixture.cyclicStandalones_not_wellFounded, + { root := ``Ix.Kernel.E1Fixture.cyclicStandalones_not_wellFounded, standardAxioms := propextOnly, forbiddenDependencies := boundedKnotForbiddenDependencies }, @@ -9774,68 +9761,68 @@ private def roots : Array RootAllowance := #[ -- Separately, the certificate-backed replay adapter consumes already- -- installed member provenance, admits exact arrays idempotently, and gives -- all-block consumers a path which cannot reach oracle materialization. - { root := ``Ix.Tc.SupportedStandaloneResources.promotes, + { root := ``Ix.Kernel.SupportedStandaloneResources.promotes, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.SupportedBlockBodyResources.certify, + { root := ``Ix.Kernel.SupportedBlockBodyResources.certify, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.SupportedCheckRun.accepts, + { root := ``Ix.Kernel.SupportedCheckRun.accepts, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.SupportedCheckFragment.checkSuccessSound, + { root := ``Ix.Kernel.SupportedCheckFragment.checkSuccessSound, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.AnonWorkEnvWF.checkEnvAnon_supported_subjectWF, + { root := ``Ix.Kernel.AnonWorkEnvWF.checkEnvAnon_supported_subjectWF, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.CertificateBackedBlockResources.newlyTrustedMember, + { root := ``Ix.Kernel.CertificateBackedBlockResources.newlyTrustedMember, standardAxioms := standard, forbiddenDependencies := certificateBackedDriverForbiddenDependencies }, - { root := ``Ix.Tc.CertificateBackedBlockResources.accepts, - standardAxioms := standard, nativeAxioms := blake3Native, + { root := ``Ix.Kernel.CertificateBackedBlockResources.accepts, + standardAxioms := standard, forbiddenDependencies := certificateBackedDriverForbiddenDependencies }, - { root := ``Ix.Tc.CertificateBackedCheckFragment.checkSuccessSound, + { root := ``Ix.Kernel.CertificateBackedCheckFragment.checkSuccessSound, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := certificateBackedDriverForbiddenDependencies }, { root := - ``Ix.Tc.AnonWorkEnvWF.checkEnvAnon_certificateBacked_subjectWF, + ``Ix.Kernel.AnonWorkEnvWF.checkEnvAnon_certificateBacked_subjectWF, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := certificateBackedDriverForbiddenDependencies }, - { root := ``Ix.Tc.BooleanEnumerationFixture.subjectWF, + { root := ``Ix.Kernel.BooleanEnumerationFixture.subjectWF, standardAxioms := standard, nativeAxioms := booleanDriverNative, sorryOrigins := typingDebt, forbiddenDependencies := certificateBackedDriverForbiddenDependencies }, - { root := ``Ix.Tc.BooleanSerialized.subjectWF, + { root := ``Ix.Kernel.BooleanSerialized.subjectWF, standardAxioms := standard, nativeAxioms := serializedBooleanNative, sorryOrigins := typingDebt, forbiddenDependencies := certificateBackedDriverForbiddenDependencies }, - { root := ``Ix.Tc.SerializedLiteralBlobs.literalRoundTrip, + { root := ``Ix.Kernel.SerializedLiteralBlobs.literalRoundTrip, standardAxioms := standard, nativeAxioms := literalRoundTripNative, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.SerializedLiteralBlobs.malformedConstantRejected, + { root := ``Ix.Kernel.SerializedLiteralBlobs.malformedConstantRejected, standardAxioms := standard, nativeAxioms := malformedConstantNative, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.SerializedLiteralBlobs.malformedBlobRejected, + { root := ``Ix.Kernel.SerializedLiteralBlobs.malformedBlobRejected, standardAxioms := standard, nativeAxioms := malformedBlobNative, forbiddenDependencies := scopedK2SForbiddenDependencies }, { root := - ``Ix.Tc.SupportedAcceptanceFixture.block_rejects_standalone_route, + ``Ix.Kernel.SupportedAcceptanceFixture.block_rejects_standalone_route, standardAxioms := propextOnly, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.SupportedAcceptanceFixture.block_rejects_wrong_route, + { root := ``Ix.Kernel.SupportedAcceptanceFixture.block_rejects_wrong_route, standardAxioms := propextOnly, forbiddenDependencies := scopedK2SForbiddenDependencies }, { root := - ``Ix.Tc.SupportedAcceptanceFixture.certificate_backed_definition_excluded, + ``Ix.Kernel.SupportedAcceptanceFixture.certificate_backed_definition_excluded, forbiddenDependencies := certificateBackedDriverForbiddenDependencies }, { root := - ``Ix.Tc.SupportedAcceptanceFixture.booleanFamilyBody_certified, + ``Ix.Kernel.SupportedAcceptanceFixture.booleanFamilyBody_certified, standardAxioms := standard, nativeAxioms := booleanFamilyBodyNative, sorryOrigins := typingDebt, forbiddenDependencies := scopedK2SForbiddenDependencies }, @@ -9844,15 +9831,15 @@ private def roots : Array RootAllowance := #[ -- the joint suffix model explicit; the scoped checker roots below construct -- the finite model from their exact public execution certificate. Two -- exact Blake3 address inequalities remain explicit fixture inputs. - { root := ``Ix.Tc.PositiveFuelSort.methodContractAtFuelOne, + { root := ``Ix.Kernel.PositiveFuelSort.methodContractAtFuelOne, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.PositiveFuelSort.fullInferenceAtFuelOne, + { root := ``Ix.Kernel.PositiveFuelSort.fullInferenceAtFuelOne, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - { root := ``Ix.Tc.PositiveFuelSort.pipelines_cover_concreteAxiom, + { root := ``Ix.Kernel.PositiveFuelSort.pipelines_cover_concreteAxiom, standardAxioms := standard, nativeAxioms := inferNative, sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, @@ -9862,26 +9849,26 @@ private def roots : Array RootAllowance := #[ -- schedule, instantiate `ScopedKernelSuffixModel.finiteOperational`, and -- retain `StateInScope` through successful semantic promotion. None may -- pass through the global suffix-model compatibility path. - { root := ``Ix.Tc.PositiveFuelSort.Checker.model, + { root := ``Ix.Kernel.PositiveFuelSort.Checker.model, standardAxioms := standard, nativeAxioms := contextNative, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.PositiveFuelSort.Checker.initialState_inv, + { root := ``Ix.Kernel.PositiveFuelSort.Checker.initialState_inv, standardAxioms := standard, nativeAxioms := nameContextNative, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.PositiveFuelSort.Checker.inference_run, + { root := ``Ix.Kernel.PositiveFuelSort.Checker.inference_run, standardAxioms := standard, nativeAxioms := nameContextNative, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.PositiveFuelSort.Checker.public_requests, + { root := ``Ix.Kernel.PositiveFuelSort.Checker.public_requests, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.PositiveFuelSort.Checker.runAssumptions, + { root := ``Ix.Kernel.PositiveFuelSort.Checker.runAssumptions, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.PositiveFuelSort.Checker.publicContext, + { root := ``Ix.Kernel.PositiveFuelSort.Checker.publicContext, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := scopedK2SForbiddenDependencies }, - { root := ``Ix.Tc.PositiveFuelSort.Checker.checked_and_promoted, + { root := ``Ix.Kernel.PositiveFuelSort.Checker.checked_and_promoted, standardAxioms := standard, nativeAxioms := inductiveNative, sorryOrigins := typingDebt, forbiddenDependencies := scopedK2SForbiddenDependencies }, @@ -9890,20 +9877,20 @@ private def roots : Array RootAllowance := #[ -- and exact public checker executions for both verdicts. Its valid path -- carries a concrete acceptance result and promotion; its invalid path -- returns the malformed-universe error with exact rollback. - { root := ``Ix.Tc.AmbientNat.goodCheckResult, + { root := ``Ix.Kernel.AmbientNat.goodCheckResult, standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.AmbientNat.initial_good_public, + { root := ``Ix.Kernel.AmbientNat.initial_good_public, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.AmbientNat.reset_bad_public, + { root := ``Ix.Kernel.AmbientNat.reset_bad_public, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := k1ForbiddenDependencies }, - { root := ``Ix.Tc.AmbientNat.publicCheckLifecycle, + { root := ``Ix.Kernel.AmbientNat.publicCheckLifecycle, standardAxioms := standard, nativeAxioms := inductiveNative, forbiddenDependencies := k1ForbiddenDependencies } ] -run_cmd Ix.Tc.Verify.Audit.check roots +run_cmd Ix.Kernel.Verify.Audit.check roots -end Ix.Tc.Verify.Audit.Completed +end Ix.Kernel.Verify.Audit.Completed diff --git a/Ix/Tc/Verify/Audit/Conditional.lean b/Ix/Kernel/Verify/Audit/Conditional.lean similarity index 51% rename from Ix/Tc/Verify/Audit/Conditional.lean rename to Ix/Kernel/Verify/Audit/Conditional.lean index 3e95368ec..2796dce90 100644 --- a/Ix/Tc/Verify/Audit/Conditional.lean +++ b/Ix/Kernel/Verify/Audit/Conditional.lean @@ -1,35 +1,35 @@ -import Ix.Tc.Verify.Audit.Completed -import Ix.Tc.Verify.Inductive.MutualRecursorAdmission +import Ix.Kernel.Verify.Audit.Completed +import Ix.Kernel.Verify.Inductive.MutualRecursorAdmission /-! -# Trust manifest for conditional `Ix.Tc.Verify` roots +# Trust manifest for conditional `Ix.Kernel.Verify` roots This manifest is deliberately separate from `Audit.Completed`. Its roots -may use only individually named witnesses from `Ix.Tc.Upstream.Pending`, and +may use only individually named witnesses from `Ix.Kernel.Frontier.Pending`, and the exact axiom audit fails as soon as either the pending or native footprint changes. Moving a theorem from here to `Completed` therefore requires removing every dependency on the quarantine namespace. -/ -namespace Ix.Tc.Verify.Audit.Conditional +namespace Ix.Kernel.Verify.Audit.Conditional -open Ix.Tc.Verify.Audit +open Ix.Kernel.Verify.Audit private def standard : Array Lean.Name := #[``propext, ``Classical.choice, ``Quot.sound] private def mutualRecursorPending : Array Lean.Name := #[ - ``Ix.Tc.Upstream.Pending.mutualTreePhysicalGenerationWF, - ``Ix.Tc.Upstream.Pending.mutualTreePhysicalRulePatternSound + ``Ix.Kernel.Frontier.Pending.mutualTreePhysicalGenerationWF, + ``Ix.Kernel.Frontier.Pending.mutualTreePhysicalRulePatternSound ] private def roots : Array RootAllowance := #[ - { root := ``Ix.Tc.MutualTreeFixture.mutualRecursorConditionalClosure, + { root := ``Ix.Kernel.MutualTreeFixture.mutualRecursorConditionalClosure, standardAxioms := standard, pendingAxioms := mutualRecursorPending, nativeAxioms := Completed.mutualRecursorConditionalNative } ] -run_cmd Ix.Tc.Verify.Audit.check roots +run_cmd Ix.Kernel.Verify.Audit.check roots -end Ix.Tc.Verify.Audit.Conditional +end Ix.Kernel.Verify.Audit.Conditional diff --git a/Ix/Tc/Verify/Audit/SorryFrontier.lean b/Ix/Kernel/Verify/Audit/SorryFrontier.lean similarity index 66% rename from Ix/Tc/Verify/Audit/SorryFrontier.lean rename to Ix/Kernel/Verify/Audit/SorryFrontier.lean index cad98b7ea..9658ca6f7 100644 --- a/Ix/Tc/Verify/Audit/SorryFrontier.lean +++ b/Ix/Kernel/Verify/Audit/SorryFrontier.lean @@ -1,21 +1,21 @@ -import Ix.Tc.Verify.Audit.Completed -import Ix.Tc.Verify.Audit.Statements +import Ix.Kernel.Verify.Audit.Completed +import Ix.Kernel.Verify.Audit.Statements /-! -# `Ix.Tc.Verify` source sorry frontier +# `Ix.Kernel.Verify` source sorry frontier -Fail the build if any declaration defined in an `Ix.Tc.Verify` source module +Fail the build if any declaration defined in an `Ix.Kernel.Verify` source module directly uses `sorryAx` — the elaborated form of a `sorry` token in that source. Reading it from the checked environment means `sorry` tokens in comments, string/char literals, or nested block comments cannot cause false positives. Filtering is by SOURCE MODULE via `getModuleIdxFor?`, not declaration name, so macro-emitted constants registered -under unqualified names are still attributed to their host module. Upstream -(Lean4Lean) `sorryAx` users are excluded because they live outside the -`Ix.Tc.Verify` namespace — the distinction `lake build --wfail` cannot make. +under unqualified names are still attributed to their host module. Named +specification (`Ix.Theory.Named`) `sorryAx` users are tracked separately because +their source modules are outside `Ix.Kernel.Verify`. Runs as a `run_cmd` at elaboration, next to the trust manifest it complements -(`Ix.Tc.Verify.Audit.check`): a build-time command, not an executable, so it +(`Ix.Kernel.Verify.Audit.check`): a build-time command, not an executable, so it never links the Rust FFI archives an exe over these modules would clash on. Importing the audit roots pulls the verified surface into scope; a declaration in a Verify module not reachable from those roots is not checked here. @@ -23,7 +23,7 @@ in a Verify module not reachable from those roots is not checked here. open Lean Lean.Elab.Command -namespace Ix.Tc.Verify.Audit +namespace Ix.Kernel.Verify.Audit /-- Constants referenced directly by a declaration's type or value, following the same cases as `Lean.collectAxioms`. The `Lean.` qualifiers are load-bearing: @@ -39,7 +39,7 @@ private def sorryFrontierDirectConstants : Lean.ConstantInfo → Array Lean.Name | .recInfo v => v.type.getUsedConstants | .inductInfo v => v.type.getUsedConstants ++ v.ctors -/-- Fail if any `Ix.Tc.Verify` source declaration directly references `sorryAx`. -/ +/-- Fail if any `Ix.Kernel.Verify` source declaration directly references `sorryAx`. -/ def checkSorryFrontier : CommandElabM Unit := do let env ← getEnv let moduleNames := env.allImportedModuleNames @@ -48,15 +48,15 @@ def checkSorryFrontier : CommandElabM Unit := do | none => none | some idx => let mod := moduleNames[idx.toNat]! - if (`Ix.Tc.Verify).isPrefixOf mod && (sorryFrontierDirectConstants info).contains ``sorryAx then some (mod, name) else none + if (`Ix.Kernel.Verify).isPrefixOf mod && (sorryFrontierDirectConstants info).contains ``sorryAx then some (mod, name) else none let offenders := offenders.toArray.qsort (fun a b => Lean.Name.lt a.1 b.1) if offenders.isEmpty then - logInfo m!"Ix.Tc.Verify sorry frontier OK: no source declaration uses sorryAx" + logInfo m!"Ix.Kernel.Verify sorry frontier OK: no source declaration uses sorryAx" else let body := String.intercalate "\n" (offenders.toList.map fun (mod, name) => s!" {mod} :: {name}") - throwError m!"Ix.Tc.Verify sorry frontier changed — {offenders.size} declaration(s) directly use sorryAx:\n{body}\nResolve the sorry, or extend the Ix.Tc.Verify.Audit trust manifest, only when the verification frontier intentionally changes." + throwError m!"Ix.Kernel.Verify sorry frontier changed — {offenders.size} declaration(s) directly use sorryAx:\n{body}\nResolve the sorry, or extend the Ix.Kernel.Verify.Audit trust manifest, only when the verification frontier intentionally changes." run_cmd checkSorryFrontier -end Ix.Tc.Verify.Audit +end Ix.Kernel.Verify.Audit diff --git a/Ix/Tc/Verify/Audit/Statements.lean b/Ix/Kernel/Verify/Audit/Statements.lean similarity index 50% rename from Ix/Tc/Verify/Audit/Statements.lean rename to Ix/Kernel/Verify/Audit/Statements.lean index 7c4fc3186..976bd7d50 100644 --- a/Ix/Tc/Verify/Audit/Statements.lean +++ b/Ix/Kernel/Verify/Audit/Statements.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Audit.Basic -import Ix.Tc.Verify.Audit.Completed -import Ix.Tc.Verify.Statements +import Ix.Kernel.Verify.Audit.Basic +import Ix.Kernel.Verify.Audit.Completed +import Ix.Kernel.Verify.Statements /-! # Trust manifest for the public checker statement frontier @@ -8,7 +8,7 @@ import Ix.Tc.Verify.Statements All seven roots are concrete results over the bounded production recursion schedule and checker. The three recursive-method adapters have no `sorryAx` dependency; the standalone and atomic-block checker roots retain only the two -named Lean4Lean typing lemmas through their singleton-definition branch. The +named Ix.Theory.Named typing lemmas through their singleton-definition branch. The E3-S root executes the exact Boolean serial workset and composes its actual runtime success gates with E1 and fixed certificate-backed E2 entries. This module permits no local statement placeholder and additionally forbids the @@ -16,33 +16,33 @@ Boolean/serialized roots from reaching oracle construction, restaging, or world materialization. -/ -namespace Ix.Tc.Verify.Audit.Statements +namespace Ix.Kernel.Verify.Audit.Statements -open Ix.Tc.Verify.Audit +open Ix.Kernel.Verify.Audit private def standard : Array Lean.Name := #[``propext, ``Classical.choice, ``Quot.sound] private def forallEInv : Lean.Name := - ``Lean4Lean.VEnv.IsDefEqU.forallE_inv_stratified -private def sortInv : Lean.Name := ``Lean4Lean.VEnv.IsDefEqU.sort_inv + ``Ix.Theory.Named.VEnv.IsDefEqU.forallE_inv_stratified +private def sortInv : Lean.Name := ``Ix.Theory.Named.VEnv.IsDefEqU.sort_inv private def checkerDebt : Array Lean.Name := #[forallEInv, sortInv] private def legacyWholeEnv : Array Lean.Name := #[ - ``Ix.Tc.AddKInduct, - ``Ix.Tc.AddKInduct.to_addInduct, - ``Ix.Tc.TrKEnv', - ``Ix.Tc.TrKEnv + ``Ix.Kernel.AddKInduct, + ``Ix.Kernel.AddKInduct.to_addInduct, + ``Ix.Kernel.TrKEnv', + ``Ix.Kernel.TrKEnv ] private def legacyAllDepthKnot : Array Lean.Name := #[ - ``Ix.Tc.RecursiveMethodClosureContext, - ``Ix.Tc.RecursiveMethodClosureContext.closedAt, - ``Ix.Tc.RecursiveMethodClosureContext.methodsN, - ``Ix.Tc.RecursiveMethodClosureContext.fullInferenceContext, - ``Ix.Tc.RecursiveMethodClosureContext.next_fullInferenceWFAt, - ``Ix.Tc.RecursiveMethodClosureContext.methodsN_fullInferenceWFAt, - ``Ix.Tc.RecursiveMethodClosureContext.publicInfer_full_wf + ``Ix.Kernel.RecursiveMethodClosureContext, + ``Ix.Kernel.RecursiveMethodClosureContext.closedAt, + ``Ix.Kernel.RecursiveMethodClosureContext.methodsN, + ``Ix.Kernel.RecursiveMethodClosureContext.fullInferenceContext, + ``Ix.Kernel.RecursiveMethodClosureContext.next_fullInferenceWFAt, + ``Ix.Kernel.RecursiveMethodClosureContext.methodsN_fullInferenceWFAt, + ``Ix.Kernel.RecursiveMethodClosureContext.publicInfer_full_wf ] private def forbidden : Array Lean.Name := @@ -52,14 +52,14 @@ private def forbidden : Array Lean.Name := library, but must not manufacture a global suffix model or pass through the old proposition-classifier/run-context path. -/ private def legacyGlobalSuffix : Array Lean.Name := #[ - ``Ix.Tc.KernelSuffixModel, - ``Ix.Tc.ScopedKernelSuffixModel.toKernelSuffixModel, - ``Ix.Tc.PropositionClassifierContext, - ``Ix.Tc.RecursiveMethodRunContext, - ``Ix.Tc.TcM.whnf.wf_legacy, - ``Ix.Tc.TcM.infer.wf_legacy, - ``Ix.Tc.TcM.isDefEq.wf_legacy, - ``Ix.Tc.TcM.checkConst.wf_legacy + ``Ix.Kernel.KernelSuffixModel, + ``Ix.Kernel.ScopedKernelSuffixModel.toKernelSuffixModel, + ``Ix.Kernel.PropositionClassifierContext, + ``Ix.Kernel.RecursiveMethodRunContext, + ``Ix.Kernel.TcM.whnf.wf_legacy, + ``Ix.Kernel.TcM.infer.wf_legacy, + ``Ix.Kernel.TcM.isDefEq.wf_legacy, + ``Ix.Kernel.TcM.checkConst.wf_legacy ] private def scopedForbidden : Array Lean.Name := @@ -69,79 +69,71 @@ private def scopedForbidden : Array Lean.Name := not regain the retired residual-oracle/world-materialization path through an adapter refactor. -/ private def oracleWorldMaterialization : Array Lean.Name := #[ - ``Ix.Tc.VerifyWorld.admitOracle, - ``Ix.Tc.VerifyWorld.le_admitOracle, - ``Ix.Tc.OracleBlockCertificate.admit, - ``Ix.Tc.OracleBlockCertificate.admitState, - ``Ix.Tc.RecM.certifyOracleBackedBlock, - ``Ix.Tc.RecM.certifyOracleBackedAdmittedBlock, - ``Ix.Tc.SingletonFamilyCatalogLink.oracle, - ``Ix.Tc.SingletonRecursorCatalogLink.oracle, - ``Ix.Tc.InductiveOracle.reindex, - ``Ix.Tc.InductiveOracle.restageMissing + ``Ix.Kernel.VerifyWorld.admitOracle, + ``Ix.Kernel.VerifyWorld.le_admitOracle, + ``Ix.Kernel.OracleBlockCertificate.admit, + ``Ix.Kernel.OracleBlockCertificate.admitState, + ``Ix.Kernel.RecM.certifyOracleBackedBlock, + ``Ix.Kernel.RecM.certifyOracleBackedAdmittedBlock, + ``Ix.Kernel.SingletonFamilyCatalogLink.oracle, + ``Ix.Kernel.SingletonRecursorCatalogLink.oracle, + ``Ix.Kernel.InductiveOracle.reindex, + ``Ix.Kernel.InductiveOracle.restageMissing ] private def certificateBackedForbidden : Array Lean.Name := scopedForbidden ++ oracleWorldMaterialization private def runNative : Array Lean.Name := #[ - nativeAxiom `Blake3 - `Blake3.HasherOps.hash._native.native_decide.ax_1, - nativeAxiom `Ix.Tc.Expr - `Ix.Tc.KExpr.mkVar._native.native_decide.ax_1, - nativeAxiom `Ix.Tc.Level - `Ix.Tc.KUniv.mkSucc._native.native_decide.ax_1, - nativeAxiom `Ix.Tc.Monad - `Ix.Tc.TcM.ctxAddrForLbrUncached._native.native_decide.ax_3 + nativeAxiom `Ix.Kernel.Expr + `Ix.Kernel.KExpr.mkVar._native.native_decide.ax_1, + nativeAxiom `Ix.Kernel.Level + `Ix.Kernel.KUniv.mkSucc._native.native_decide.ax_1, ] private def checkConstNative : Array Lean.Name := #[ - nativeAxiom `Blake3 - `Blake3.HasherOps.hash._native.native_decide.ax_1, - nativeAxiom `Ix.Tc.Expr - `Ix.Tc.KExpr.mkVar._native.native_decide.ax_1, - nativeAxiom `Ix.Tc.Level - `Ix.Tc.KUniv.mkSucc._native.native_decide.ax_1, - nativeAxiom `Ix.Tc.Monad - `Ix.Tc.TcM.ctxAddrForLbrUncached._native.native_decide.ax_3, + nativeAxiom `Ix.Kernel.Expr + `Ix.Kernel.KExpr.mkVar._native.native_decide.ax_1, + nativeAxiom `Ix.Kernel.Level + `Ix.Kernel.KUniv.mkSucc._native.native_decide.ax_1, nativeAxiom `Ix.Environment `Ix.Name.mkStr._native.native_decide.ax_1, - nativeAxiom `Ix.Tc.Inductive - `Ix.Tc.RecM.canonicalAuxOrder._native.native_decide.ax_9 + nativeAxiom `Ix.Kernel.Inductive + `Ix.Kernel.RecM.canonicalAuxOrder._native.native_decide.ax_9 ] private def roots : Array RootAllowance := #[ - { root := ``Ix.Tc.TcM.whnf.wf, + { root := ``Ix.Kernel.TcM.whnf.wf, standardAxioms := standard, nativeAxioms := runNative, forbiddenDependencies := scopedForbidden }, - { root := ``Ix.Tc.TcM.infer.wf, + { root := ``Ix.Kernel.TcM.infer.wf, standardAxioms := standard, nativeAxioms := runNative, forbiddenDependencies := scopedForbidden }, - { root := ``Ix.Tc.TcM.isDefEq.wf, + { root := ``Ix.Kernel.TcM.isDefEq.wf, standardAxioms := standard, nativeAxioms := runNative, forbiddenDependencies := scopedForbidden }, - { root := ``Ix.Tc.TcM.checkConst.wf, + { root := ``Ix.Kernel.TcM.checkConst.wf, standardAxioms := standard, nativeAxioms := checkConstNative, sorryOrigins := checkerDebt, forbiddenDependencies := scopedForbidden }, - { root := ``Ix.Tc.TcM.checkConst.blockDisposition, + { root := ``Ix.Kernel.TcM.checkConst.blockDisposition, standardAxioms := standard, nativeAxioms := checkConstNative, sorryOrigins := checkerDebt, forbiddenDependencies := scopedForbidden }, - { root := ``Ix.Tc.BooleanEnumerationFixture.subjectWF, + { root := ``Ix.Kernel.BooleanEnumerationFixture.subjectWF, standardAxioms := standard, - nativeAxioms := Ix.Tc.Verify.Audit.Completed.booleanDriverNative, + nativeAxioms := Ix.Kernel.Verify.Audit.Completed.booleanDriverNative, sorryOrigins := checkerDebt, forbiddenDependencies := certificateBackedForbidden }, - { root := ``Ix.Tc.BooleanSerialized.subjectWF, + { root := ``Ix.Kernel.BooleanSerialized.subjectWF, standardAxioms := standard, - nativeAxioms := Ix.Tc.Verify.Audit.Completed.serializedBooleanNative, + nativeAxioms := Ix.Kernel.Verify.Audit.Completed.serializedBooleanNative, sorryOrigins := checkerDebt, forbiddenDependencies := certificateBackedForbidden } ] -run_cmd Ix.Tc.Verify.Audit.check roots +run_cmd Ix.Kernel.Verify.Audit.check roots -end Ix.Tc.Verify.Audit.Statements +end Ix.Kernel.Verify.Audit.Statements diff --git a/Ix/Tc/Verify/Cache.lean b/Ix/Kernel/Verify/Cache.lean similarity index 99% rename from Ix/Tc/Verify/Cache.lean rename to Ix/Kernel/Verify/Cache.lean index 307893095..d35ea1e8a 100644 --- a/Ix/Tc/Verify/Cache.lean +++ b/Ix/Kernel/Verify/Cache.lean @@ -1,7 +1,7 @@ -import Ix.Tc.Verify.Decl -import Ix.Tc.Verify.Env -import Ix.Tc.Verify.Monad -import Ix.Tc.Verify.Support +import Ix.Kernel.Verify.Decl +import Ix.Kernel.Verify.Env +import Ix.Kernel.Verify.Monad +import Ix.Kernel.Verify.Support import Std.Data.HashMap.Lemmas import Std.Data.HashSet.Lemmas @@ -56,7 +56,7 @@ does not treat an untagged concrete lookup as trusted merely because it was listed here. -/ -namespace Ix.Tc +namespace Ix.Kernel /-! ## Lookup authority -/ @@ -100,7 +100,7 @@ end LookupScope /-- The pending target can be acquired as the subject, but cannot be used by inference, delta unfolding, definitional equality, or a semantic cache hit. -/ theorem PendingDecl.lookup_isolation {trProj : RawProjRel} - {world : VerifyWorld} {target : KId .anon} {d : Lean4Lean.VDecl} + {world : VerifyWorld} {target : KId .anon} {d : Ix.Theory.Named.VDecl} (h : PendingDecl trProj world target d) : (LookupScope.standalone target).Allows world .subject target ∧ ¬(LookupScope.standalone target).Allows world .semantic target := by @@ -492,7 +492,7 @@ theorem mono {semantics : CacheSemantics} pending target. This is the cache-hit half of the self-unfolding barrier. -/ theorem pending_isolation {semantics : CacheSemantics} {authority : CacheAuthority} {support : RunSupport} {entry : CacheEntry} - {trProj : RawProjRel} {target : KId .anon} {d : Lean4Lean.VDecl} + {trProj : RawProjRel} {target : KId .anon} {d : Ix.Theory.Named.VDecl} (hpending : PendingDecl trProj authority.world target d) (hentry : ¬entry.SubjectScoped) (h : CacheProvenance semantics authority support entry) : @@ -507,7 +507,7 @@ theorem pending_isolation {semantics : CacheSemantics} target: there is no active-block authority left. -/ theorem pending_isolation_stable {semantics : CacheSemantics} {authority : CacheAuthority} {support : RunSupport} {entry : CacheEntry} - {trProj : RawProjRel} {target : KId .anon} {d : Lean4Lean.VDecl} + {trProj : RawProjRel} {target : KId .anon} {d : Ix.Theory.Named.VDecl} (hstable : ∀ id, ¬authority.active id) (hpending : PendingDecl trProj authority.world target d) (h : CacheProvenance semantics authority support entry) : @@ -1606,4 +1606,4 @@ theorem reset_cache_frame (s : TcState m) : end TcM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/Acceptance.lean b/Ix/Kernel/Verify/Check/Acceptance.lean similarity index 96% rename from Ix/Tc/Verify/Check/Acceptance.lean rename to Ix/Kernel/Verify/Check/Acceptance.lean index 37fc999c4..39eaf2b06 100644 --- a/Ix/Tc/Verify/Check/Acceptance.lean +++ b/Ix/Kernel/Verify/Check/Acceptance.lean @@ -1,6 +1,8 @@ -import Ix.Tc.Verify.Check.DeclarationValidation -import Ix.Tc.Verify.Infer.SortTypes -import Ix.Tc.Verify.State +import Ix.Kernel.Verify.Check.DeclarationValidation +import Ix.Kernel.Verify.Infer.SortTypes +import Ix.Kernel.Verify.State + +open Ix.Theory (VLevel) /-! # Standalone declaration acceptance and promotion @@ -18,9 +20,9 @@ This keeps K3's critical implication explicit: No field below assumes a `VDecl.WF` transition. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VConstant VDecl VDefVal VEnv VExpr) +open Ix.Theory.Named (VConstant VDecl VDefVal VEnv VExpr) /-! ## Semantic results of the two checker pipelines -/ @@ -89,14 +91,14 @@ Only the value-check result is needed by Theory's `VDefVal.WF`; keeping both premises makes the operational acceptance boundary exact. -/ inductive StandaloneCheckEvidence (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) : VDecl → Prop - | axiom {ci : Lean4Lean.VConstVal} : + | axiom {ci : Ix.Theory.Named.VConstVal} : TypeCheckEvidence trProj world support ci.uvars [] ci.type → StandaloneCheckEvidence trProj world support (.axiom ci) - | defn {ci : Lean4Lean.VDefVal} : + | defn {ci : Ix.Theory.Named.VDefVal} : TypeCheckEvidence trProj world support ci.uvars [] ci.type → ValueCheckEvidence world ci.uvars [] ci.value ci.type → StandaloneCheckEvidence trProj world support (.def ci) - | opaque {ci : Lean4Lean.VDefVal} : + | opaque {ci : Ix.Theory.Named.VDefVal} : TypeCheckEvidence trProj world support ci.uvars [] ci.type → ValueCheckEvidence world ci.uvars [] ci.value ci.type → StandaloneCheckEvidence trProj world support (.opaque ci) @@ -258,4 +260,4 @@ theorem checkValidatedResultAndPromote end PendingDecl -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/BinderRoundTrip.lean b/Ix/Kernel/Verify/Check/BinderRoundTrip.lean similarity index 95% rename from Ix/Tc/Verify/Check/BinderRoundTrip.lean rename to Ix/Kernel/Verify/Check/BinderRoundTrip.lean index 4321a91af..cc6b44acb 100644 --- a/Ix/Tc/Verify/Check/BinderRoundTrip.lean +++ b/Ix/Kernel/Verify/Check/BinderRoundTrip.lean @@ -1,17 +1,17 @@ -import Ix.Tc.Verify.Check.PreTranslationScopes -import Ix.Tc.Verify.Infer.BinderClosing +import Ix.Kernel.Verify.Check.PreTranslationScopes +import Ix.Kernel.Verify.Infer.BinderClosing /-! # Binder open/close round trip -The Lean4Lean checker closes a freshly opened binder with +The Ix.Theory.Named checker closes a freshly opened binder with `FVarsIn.abstract_instantiate1`. Ix uses address-carrying `KExpr` smart constructors and separate cached walkers, so K3 needs the corresponding pure syntax theorem for `instantiateRevSpec` followed by singleton `abstractFVarsSpec`. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace KExpr @@ -33,9 +33,9 @@ namespace PreTrKExprS /-- A pre-translation can mention only fvars registered by its `KVLCtx`. -/ theorem fvarAbsent - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} - {Delta : KVLCtx} {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {Delta : KVLCtx} {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsource : PreTrKExprS env uvars nameOf trProj Delta source sourceV) {target : FVarId} (hfresh : target ∉ Delta.fvars) : source.FVarAbsent target := by @@ -216,10 +216,10 @@ syntax. The opening bounds justify the pure round trip; the closing bounds justify the production abstraction walker whose translation theorem supplies the typed result. -/ theorem TrKExprS.closeOpenedFVarZero - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} - {Delta : KVLCtx} {decl : Lean4Lean.VLocalDecl} - {body bodyOpen : KExpr .anon} {bodyV : Lean4Lean.VExpr} + {Delta : KVLCtx} {decl : Ix.Theory.Named.VLocalDecl} + {body bodyOpen : KExpr .anon} {bodyV : Ix.Theory.Named.VExpr} {fv : FVarId} {deps : List FVarId} {name : Mode.anon.F Name} (H : TrKExprS env uvars nameOf trProj ((some (fv, deps), decl) :: Delta) bodyOpen bodyV) @@ -239,4 +239,4 @@ theorem TrKExprS.closeOpenedFVarZero rw [hround] at hclosed exact hclosed -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/BlockAcceptance.lean b/Ix/Kernel/Verify/Check/BlockAcceptance.lean similarity index 96% rename from Ix/Tc/Verify/Check/BlockAcceptance.lean rename to Ix/Kernel/Verify/Check/BlockAcceptance.lean index 111168bf5..d0d14584e 100644 --- a/Ix/Tc/Verify/Check/BlockAcceptance.lean +++ b/Ix/Kernel/Verify/Check/BlockAcceptance.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.Acceptance -import Ix.Tc.Verify.Check.BlockCache +import Ix.Kernel.Verify.Check.Acceptance +import Ix.Kernel.Verify.Check.BlockCache /-! # Atomic coordinated-block acceptance @@ -14,13 +14,13 @@ Legacy ambient inductive-family and recursor blocks may still be admitted relative to an explicit `InductiveOracle`. Certificate-backed paths instead use `SemanticBlockTransitionCertificate` for an exact Theory-environment extension or `ExistingSemanticBlockCertificate` for members already installed -by that extension. Definition admission is local, but Lean4Lean currently has +by that extension. Definition admission is local, but Ix.Theory.Named currently has no mutual-definition `VDecl`, so the constructive definition theorem below is deliberately restricted to production's singleton definition blocks. A multi-definition block is not silently decomposed into independent claims. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Exact promotion specialized to the ordered member array of one physical block. Array order remains available through `ExactCheckBlock`; the trust @@ -118,7 +118,7 @@ member array are fixed by the theorem statement. -/ structure SemanticBlockTransitionCertificate (trProj : RawProjRel) (world : VerifyWorld) (block : KId .anon) (members : Array (KId .anon)) (kind : CheckBlockKind) - (afterVEnv : Lean4Lean.VEnv) : Prop where + (afterVEnv : Ix.Theory.Named.VEnv) : Prop where exactBlock : ExactCheckBlock world block members kind fresh : ∀ ⦃id⦄, id ∈ members → ¬world.trusted id envLE : world.venv ≤ afterVEnv @@ -132,7 +132,7 @@ namespace SemanticBlockTransitionCertificate preserving all immutable ghost inputs. -/ def admittedWorld {trProj : RawProjRel} {world : VerifyWorld} {block : KId .anon} {members : Array (KId .anon)} - {kind : CheckBlockKind} {afterVEnv : Lean4Lean.VEnv} + {kind : CheckBlockKind} {afterVEnv : Ix.Theory.Named.VEnv} (certificate : SemanticBlockTransitionCertificate trProj world block members kind afterVEnv) : VerifyWorld where catalog := world.catalog @@ -153,7 +153,7 @@ def admittedWorld {trProj : RawProjRel} {world : VerifyWorld} /-- The explicit semantic transaction is a monotone world extension. -/ theorem le_admittedWorld {trProj : RawProjRel} {world : VerifyWorld} {block : KId .anon} {members : Array (KId .anon)} - {kind : CheckBlockKind} {afterVEnv : Lean4Lean.VEnv} + {kind : CheckBlockKind} {afterVEnv : Ix.Theory.Named.VEnv} (certificate : SemanticBlockTransitionCertificate trProj world block members kind afterVEnv) : world ≤ certificate.admittedWorld := @@ -163,7 +163,7 @@ theorem le_admittedWorld {trProj : RawProjRel} {world : VerifyWorld} and its per-member provenance. -/ theorem admit {trProj : RawProjRel} {world : VerifyWorld} {block : KId .anon} {members : Array (KId .anon)} - {kind : CheckBlockKind} {afterVEnv : Lean4Lean.VEnv} + {kind : CheckBlockKind} {afterVEnv : Ix.Theory.Named.VEnv} (certificate : SemanticBlockTransitionCertificate trProj world block members kind afterVEnv) (hrel : TrustedCatalogRel trProj world) : @@ -180,7 +180,7 @@ theorem admit {trProj : RawProjRel} {world : VerifyWorld} semantic transition. -/ theorem admitState {trProj : RawProjRel} {world : VerifyWorld} {block : KId .anon} {members : Array (KId .anon)} - {kind : CheckBlockKind} {afterVEnv : Lean4Lean.VEnv} + {kind : CheckBlockKind} {afterVEnv : Ix.Theory.Named.VEnv} {state : TcState .anon} (certificate : SemanticBlockTransitionCertificate trProj world block members kind afterVEnv) @@ -208,7 +208,7 @@ Every exact physical member must instead provide the same per-constant, per-rule, and per-pattern provenance consumed by trusted lookups. The important generated-recursor use case is deliberately two-phase. -Lean4Lean's certified family transaction has already installed the generated +Ix.Theory.Named's certified family transaction has already installed the generated recursor and equations in `world.venv`; the separate Ix recursor block remains untrusted until its production comparison succeeds. -/ structure ExistingSemanticBlockCertificate (trProj : RawProjRel) @@ -252,7 +252,7 @@ theorem le_admittedWorld {trProj : RawProjRel} {world : VerifyWorld} (certificate : ExistingSemanticBlockCertificate trProj world block members kind) : world ≤ certificate.admittedWorld := - ⟨rfl, rfl, rfl, fun {_} hold => Or.inr hold, Lean4Lean.VEnv.LE.rfl⟩ + ⟨rfl, rfl, rfl, fun {_} hold => Or.inr hold, Ix.Theory.Named.VEnv.LE.rfl⟩ /-- Commit the exact physical block using only its already-installed semantic entries. No `InductiveOracle` is constructed or consumed. -/ @@ -404,7 +404,7 @@ of standalone declarations would be unsound for mutual references until the Theory exposes a matching atomic declaration form. -/ structure SingletonDefinitionCertificate (trProj : RawProjRel) (world : VerifyWorld) (block id : KId .anon) - (decl : Lean4Lean.VDecl) : Prop where + (decl : Ix.Theory.Named.VDecl) : Prop where exactBlock : ExactCheckBlock world block #[id] .defn pending : PendingDecl trProj world id decl accepted : StandaloneAccepted world.venv decl @@ -415,7 +415,7 @@ namespace SingletonDefinitionCertificate transition. Every accepted member (the singleton) is trusted, and the exact promotion theorem rules out unrelated trust growth. -/ theorem admit {trProj : RawProjRel} {world : VerifyWorld} - {block id : KId .anon} {decl : Lean4Lean.VDecl} + {block id : KId .anon} {decl : Ix.Theory.Named.VDecl} {state : TcState .anon} (certificate : SingletonDefinitionCertificate trProj world block id decl) (hstate : BlockStateWF trProj state world) : @@ -446,4 +446,4 @@ theorem admit {trProj : RawProjRel} {world : VerifyWorld} end SingletonDefinitionCertificate -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/BlockCache.lean b/Ix/Kernel/Verify/Check/BlockCache.lean similarity index 98% rename from Ix/Tc/Verify/Check/BlockCache.lean rename to Ix/Kernel/Verify/Check/BlockCache.lean index bec3e2b7d..777a0c4ed 100644 --- a/Ix/Tc/Verify/Check/BlockCache.lean +++ b/Ix/Kernel/Verify/Check/BlockCache.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.BlockIdentity +import Ix.Kernel.Verify.Check.BlockIdentity /-! # Coordinated block-cache closure @@ -13,7 +13,7 @@ recovers acceptance of the exact immutable block and therefore cannot certify a proper subset or treat the block address as a declaration. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace CacheAuthority @@ -121,4 +121,4 @@ theorem rejectsSuccessWithUntrustedMember end CacheInvariant -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/BlockClassification.lean b/Ix/Kernel/Verify/Check/BlockClassification.lean similarity index 99% rename from Ix/Tc/Verify/Check/BlockClassification.lean rename to Ix/Kernel/Verify/Check/BlockClassification.lean index c74b7b60c..aa91fd77a 100644 --- a/Ix/Tc/Verify/Check/BlockClassification.lean +++ b/Ix/Kernel/Verify/Check/BlockClassification.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.BlockRouting +import Ix.Kernel.Verify.Check.BlockRouting /-! # Coordinated block classification @@ -11,7 +11,7 @@ successful classifier result is exactly that kind. Both success and error paths preserve the caller's invariant through lazy ingress. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM.BlockClassFlags @@ -214,4 +214,4 @@ theorem classifyBlock_success_exact end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/BlockDefinition.lean b/Ix/Kernel/Verify/Check/BlockDefinition.lean similarity index 94% rename from Ix/Tc/Verify/Check/BlockDefinition.lean rename to Ix/Kernel/Verify/Check/BlockDefinition.lean index 395f9a177..573636b84 100644 --- a/Ix/Tc/Verify/Check/BlockDefinition.lean +++ b/Ix/Kernel/Verify/Check/BlockDefinition.lean @@ -1,13 +1,13 @@ -import Ix.Tc.Verify.Check.BlockTransaction -import Ix.Tc.Verify.Check.BlockClassification -import Ix.Tc.Verify.Check.ScopedStandaloneDriver -import Ix.Tc.Verify.Check.StandaloneDriver +import Ix.Kernel.Verify.Check.BlockTransaction +import Ix.Kernel.Verify.Check.BlockClassification +import Ix.Kernel.Verify.Check.ScopedStandaloneDriver +import Ix.Kernel.Verify.Check.StandaloneDriver /-! # Singleton definition blocks The production definition-block branch iterates `checkConstMemberFresh` over -the complete array and then publishes the peak DefEq depth. Lean4Lean does +the complete array and then publishes the peak DefEq depth. Ix.Theory.Named does not yet have an atomic mutual-definition declaration, so the constructive E0 bridge is intentionally the singleton specialization. It extracts the actual member run from `checkClassifiedBlock`, invokes K3 without performing @@ -15,7 +15,7 @@ K3's standalone promotion, and packages that evidence for the enclosing atomic block transaction. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -66,7 +66,7 @@ theorem certifySingletonDefinition model.keys.uvars calls (Methods.next methods)) (hmethodPolicy : (Methods.next methods).PreservesInferOnly) {block requested id : KId .anon} {concrete : KConst .anon} - {decl : Lean4Lean.VDecl} {before after : TcState .anon} + {decl : Ix.Theory.Named.VDecl} {before after : TcState .anon} (hprojection : trProj.SubstCompatible) (hliterals : ∀ literal, world.venv.ContainsLits literal) (hpending : PendingDecl trProj world id decl) @@ -124,7 +124,7 @@ theorem certifySingletonDefinitionScoped (Methods.next methods)) (hmethodPolicy : (Methods.next methods).PreservesInferOnly) {block requested id : KId .anon} {concrete : KConst .anon} - {decl : Lean4Lean.VDecl} {before after : TcState .anon} + {decl : Ix.Theory.Named.VDecl} {before after : TcState .anon} (hprojection : trProj.SubstCompatible) (hliterals : ∀ literal, world.venv.ContainsLits literal) (hpending : PendingDecl trProj world id decl) @@ -170,4 +170,4 @@ theorem certifySingletonDefinitionScoped end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/BlockExecution.lean b/Ix/Kernel/Verify/Check/BlockExecution.lean similarity index 99% rename from Ix/Tc/Verify/Check/BlockExecution.lean rename to Ix/Kernel/Verify/Check/BlockExecution.lean index 93a1474aa..06e076f2a 100644 --- a/Ix/Tc/Verify/Check/BlockExecution.lean +++ b/Ix/Kernel/Verify/Check/BlockExecution.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.BlockAcceptance +import Ix.Kernel.Verify.Check.BlockAcceptance /-! # Coordinated block execution traces @@ -17,7 +17,7 @@ The semantic admission theorem remains in `BlockAcceptance`; this module is the operational half needed to join that transaction to production. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace TcM @@ -380,4 +380,4 @@ theorem withBlockCheckResult {trProj : RawProjRel} end BlockStateWF -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/BlockIdentity.lean b/Ix/Kernel/Verify/Check/BlockIdentity.lean similarity index 99% rename from Ix/Tc/Verify/Check/BlockIdentity.lean rename to Ix/Kernel/Verify/Check/BlockIdentity.lean index c8b60906f..b3e6bc8ef 100644 --- a/Ix/Tc/Verify/Check/BlockIdentity.lean +++ b/Ix/Kernel/Verify/Check/BlockIdentity.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Driver -import Ix.Tc.Verify.State +import Ix.Kernel.Driver +import Ix.Kernel.Verify.State /-! # Coordinated checker-block identity @@ -18,7 +18,7 @@ to any of them. In particular, a catalogued member is still untrusted until the atomic acceptance theorem admits the complete array. -/ -namespace Ix.Tc +namespace Ix.Kernel /-! ## Concrete/world block agreement -/ @@ -277,4 +277,4 @@ end MatchesBlockCatalog end AnonWorkItem -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/BlockNatFixture.lean b/Ix/Kernel/Verify/Check/BlockNatFixture.lean similarity index 97% rename from Ix/Tc/Verify/Check/BlockNatFixture.lean rename to Ix/Kernel/Verify/Check/BlockNatFixture.lean index 84aa6407b..f53bbba2d 100644 --- a/Ix/Tc/Verify/Check/BlockNatFixture.lean +++ b/Ix/Kernel/Verify/Check/BlockNatFixture.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.PublicBlocks -import Ix.Tc.Verify.NatFixture +import Ix.Kernel.Verify.Check.PublicBlocks +import Ix.Kernel.Verify.NatFixture /-! # Concrete Nat block fixture for E0 @@ -10,7 +10,7 @@ the adversarial cache rule without pretending that E2 has already connected the production inductive checker to the oracle. -/ -namespace Ix.Tc.AmbientNat.E0 +namespace Ix.Kernel.AmbientNat.E0 /-- Ordered production member array for the Nat family. -/ def blockMembers : Array (KId .anon) := #[natId, zeroId, succId] @@ -118,4 +118,4 @@ theorem rejectsPrematureSuccess CacheInvariant.rejectsSuccessWithUntrustedMember exactBlock (id := zeroId) (by simp [blockMembers]) (fun h => h) -end Ix.Tc.AmbientNat.E0 +end Ix.Kernel.AmbientNat.E0 diff --git a/Ix/Tc/Verify/Check/BlockOracle.lean b/Ix/Kernel/Verify/Check/BlockOracle.lean similarity index 95% rename from Ix/Tc/Verify/Check/BlockOracle.lean rename to Ix/Kernel/Verify/Check/BlockOracle.lean index 265453653..744ae1c60 100644 --- a/Ix/Tc/Verify/Check/BlockOracle.lean +++ b/Ix/Kernel/Verify/Check/BlockOracle.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.BlockTransaction -import Ix.Tc.Verify.Inductive.Certificate +import Ix.Kernel.Verify.Check.BlockTransaction +import Ix.Kernel.Verify.Inductive.Certificate /-! # Oracle-backed inductive and recursor blocks @@ -7,7 +7,7 @@ import Ix.Tc.Verify.Inductive.Certificate E0 proves the transaction and cache ordering around production block checks. The semantic meaning of a successful inductive/recursor body remains the explicit E2b boundary: E2b must connect the actual Ix validators and generated -recursor patterns to an `InductiveOracle`. The Lean4Lean +recursor patterns to an `InductiveOracle`. The Ix.Theory.Named `CertifiedGenerationTransaction` supplies the Theory-owned portion of that future construction, but cannot determine Ix addresses, member arrays, or checker execution on its own. @@ -16,7 +16,7 @@ This module packages exactly that remaining boundary and ties it to the real classified-body trace. It introduces no unindexed “block succeeded” axiom. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- The E2b resources which remain after E0 has fixed the exact physical array and production classifier kind. The post-state uses temporary block @@ -90,4 +90,4 @@ theorem certifyOracleBackedAdmittedBlock end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/BlockRouteFrame.lean b/Ix/Kernel/Verify/Check/BlockRouteFrame.lean similarity index 98% rename from Ix/Tc/Verify/Check/BlockRouteFrame.lean rename to Ix/Kernel/Verify/Check/BlockRouteFrame.lean index bf2096a85..6fd9ad8f7 100644 --- a/Ix/Tc/Verify/Check/BlockRouteFrame.lean +++ b/Ix/Kernel/Verify/Check/BlockRouteFrame.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.BlockClassification +import Ix.Kernel.Verify.Check.BlockClassification /-! # State framing for successful coordinated routing @@ -9,7 +9,7 @@ if routing returns a coordinated block, every lazy lookup and classification step preserves the caller's invariant up to the exact post-route state. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -121,4 +121,4 @@ theorem coordinatedBlockFor_some_preserves end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/BlockRouting.lean b/Ix/Kernel/Verify/Check/BlockRouting.lean similarity index 99% rename from Ix/Tc/Verify/Check/BlockRouting.lean rename to Ix/Kernel/Verify/Check/BlockRouting.lean index 07598a2da..210f2e0fd 100644 --- a/Ix/Tc/Verify/Check/BlockRouting.lean +++ b/Ix/Kernel/Verify/Check/BlockRouting.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.BlockIdentity -import Ix.Tc.Verify.Infer.Constants +import Ix.Kernel.Verify.Check.BlockIdentity +import Ix.Kernel.Verify.Infer.Constants /-! # Soundness of production block routing @@ -11,7 +11,7 @@ successful concrete parent lookup is the exact catalogued inductive and that the constructor itself belongs to that parent's exact member array. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -295,4 +295,4 @@ theorem coordinatedBlockFor_some_exact end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/BlockTransaction.lean b/Ix/Kernel/Verify/Check/BlockTransaction.lean similarity index 98% rename from Ix/Tc/Verify/Check/BlockTransaction.lean rename to Ix/Kernel/Verify/Check/BlockTransaction.lean index 0ddaf4644..86a4e96b0 100644 --- a/Ix/Tc/Verify/Check/BlockTransaction.lean +++ b/Ix/Kernel/Verify/Check/BlockTransaction.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.BlockExecution +import Ix.Kernel.Verify.Check.BlockExecution /-! # Atomic coordinated-block transactions @@ -16,12 +16,12 @@ There are exactly two currently supported semantic sources: * an inductive or recursor block, relative to the explicit inductive oracle which E2 must construct from the corresponding production checker. -Lean4Lean does not yet expose an atomic mutual-definition declaration, so no +Ix.Theory.Named does not yet expose an atomic mutual-definition declaration, so no constructor below decomposes a multi-definition production block into a sequence of stronger semantic claims. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- The complete checker invariant while one exact coordinated block is active. Only structural block caches may use the additional member @@ -105,7 +105,7 @@ inductive BlockAdmissionEvidence (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (block : KId .anon) : Array (KId .anon) → CheckBlockKind → Prop | singletonDefinition {id : KId .anon} {concrete : KConst .anon} - {decl : Lean4Lean.VDecl} : + {decl : Ix.Theory.Named.VDecl} : PendingDecl trProj world id decl → StandaloneCheckResult trProj world support id concrete decl → BlockAdmissionEvidence trProj world support block #[id] .defn @@ -400,4 +400,4 @@ theorem checkCoordinatedBlock_rejected end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/BoundedPipelines.lean b/Ix/Kernel/Verify/Check/BoundedPipelines.lean similarity index 96% rename from Ix/Tc/Verify/Check/BoundedPipelines.lean rename to Ix/Kernel/Verify/Check/BoundedPipelines.lean index 30055a116..8fd014a12 100644 --- a/Ix/Tc/Verify/Check/BoundedPipelines.lean +++ b/Ix/Kernel/Verify/Check/BoundedPipelines.lean @@ -1,7 +1,7 @@ -import Ix.Tc.Verify.Check.CheckerEvidence -import Ix.Tc.Verify.Check.FullInferenceKnot -import Ix.Tc.Verify.Check.RecursiveMethodPolicy -import Ix.Tc.Verify.RecursiveMethods.CallDomains +import Ix.Kernel.Verify.Check.CheckerEvidence +import Ix.Kernel.Verify.Check.FullInferenceKnot +import Ix.Kernel.Verify.Check.RecursiveMethodPolicy +import Ix.Kernel.Verify.RecursiveMethods.CallDomains /-! # Bounded standalone-checker pipelines @@ -19,7 +19,7 @@ type/value calls made by one concrete standalone declaration and the bounded follow-up calls made on their results. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace Methods @@ -43,7 +43,7 @@ def FullInferenceWFAtOn (world : VerifyWorld) (support : RunSupport) (uvars : Nat) (calls : CallDomain) (methods : Methods .anon) : Prop := ∀ {Delta : KVLCtx} {s : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr}, + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr}, calls.infer source → s.inferOnly = false → PreTrKExprS world.venv uvars world.nameOf trProj Delta source sourceV → @@ -70,7 +70,7 @@ theorem ofTypedIngress uvars calls methods) (policy : methods.PreservesInferOnly) (upgrade : ∀ {Delta : KVLCtx} {source : KExpr .anon} - {sourceV : Lean4Lean.VExpr}, + {sourceV : Ix.Theory.Named.VExpr}, calls.infer source → PreTrKExprS world.venv uvars world.nameOf trProj Delta source sourceV → TrKExprS world.venv uvars world.nameOf trProj Delta source sourceV) : @@ -127,7 +127,7 @@ theorem methodsOut (world : VerifyWorld) (support : RunSupport) (uvars : Nat) (calls : CallDomain) : Methods.FullInferenceWFAtOn semantics trProj world support uvars calls - (Ix.Tc.methodsOut : Methods .anon) := by + (Ix.Kernel.methodsOut : Methods .anon) := by intro Delta s source sourceV hcall hpolicy hsource exact TcM.WF.throw fun _ => hpolicy @@ -155,12 +155,12 @@ structure StandalonePipelineResources valueInfer : ∀ {value declaredType}, valueSources value declaredType → calls.infer value typeWhnf : ∀ {Delta : KVLCtx} {source : KExpr .anon} - {sourceV : Lean4Lean.VExpr} {inferred : KExpr .anon}, + {sourceV : Ix.Theory.Named.VExpr} {inferred : KExpr .anon}, typeSources source → FullInferPost trProj world support uvars Delta source sourceV inferred → calls.AdmitsEnsureSortDirect inferred valueDefEq : ∀ {Delta : KVLCtx} {value declaredType : KExpr .anon} - {valueV : Lean4Lean.VExpr} {inferred : KExpr .anon}, + {valueV : Ix.Theory.Named.VExpr} {inferred : KExpr .anon}, valueSources value declaredType → FullInferPost trProj world support uvars Delta value valueV inferred → calls.isDefEq inferred declaredType @@ -259,7 +259,7 @@ private theorem ensureSortDirect_wfAtOn {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {calls : Methods.CallDomain} {methods : Methods .anon} {Delta : KVLCtx} {s : TcState .anon} {input : KExpr .anon} - {inputV : Lean4Lean.VExpr} + {inputV : Ix.Theory.Named.VExpr} (hmethods : Methods.WFAtOn .noAccel semantics trProj world support uvars calls (Methods.next methods)) (hresources : SortComponentResources support) @@ -320,7 +320,7 @@ theorem checkTypePipeline_bounded_sound calls (Methods.next methods)) (hpolicyMethods : (Methods.next methods).PreservesInferOnly) {Delta : KVLCtx} {s after : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsourceCall : resources.typeSources source) (hsource : PreTrKExprS world.venv uvars world.nameOf trProj Delta source sourceV) @@ -402,7 +402,7 @@ theorem checkValuePipeline_bounded_sound calls (Methods.next methods)) {Delta : KVLCtx} {s after : TcState .anon} {value declaredType : KExpr .anon} - {valueV declaredTypeV : Lean4Lean.VExpr} + {valueV declaredTypeV : Ix.Theory.Named.VExpr} (hvalueCall : resources.valueSources value declaredType) (hvalue : PreTrKExprS world.venv uvars world.nameOf trProj Delta value valueV) @@ -471,4 +471,4 @@ theorem checkValuePipeline_bounded_sound end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/CheckConstExecution.lean b/Ix/Kernel/Verify/Check/CheckConstExecution.lean similarity index 95% rename from Ix/Tc/Verify/Check/CheckConstExecution.lean rename to Ix/Kernel/Verify/Check/CheckConstExecution.lean index fb8678b0d..f74611892 100644 --- a/Ix/Tc/Verify/Check/CheckConstExecution.lean +++ b/Ix/Kernel/Verify/Check/CheckConstExecution.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.BlockRouteFrame -import Ix.Tc.Verify.Check.BlockTransaction +import Ix.Kernel.Verify.Check.BlockRouteFrame +import Ix.Kernel.Verify.Check.BlockTransaction /-! # Production `checkConst` dispatch traces @@ -11,7 +11,7 @@ ends at `checkCoordinatedBlock`; the standalone branch ends at for one branch from being reused for the other. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -65,4 +65,4 @@ theorem checkConst_success_trace end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/CheckConstTransaction.lean b/Ix/Kernel/Verify/Check/CheckConstTransaction.lean similarity index 98% rename from Ix/Tc/Verify/Check/CheckConstTransaction.lean rename to Ix/Kernel/Verify/Check/CheckConstTransaction.lean index 5b1bc35bc..57dd9c9b0 100644 --- a/Ix/Tc/Verify/Check/CheckConstTransaction.lean +++ b/Ix/Kernel/Verify/Check/CheckConstTransaction.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.CheckConstExecution +import Ix.Kernel.Verify.Check.CheckConstExecution /-! # Semantic assembly for production `checkConst` @@ -16,7 +16,7 @@ as the route. Thus the certifier cannot be applied to a TOCTOU-substituted block. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Stable kernel state plus E0's physical/ghost block-table agreement. -/ structure CoordinatedKernelStateWF (semantics : CacheSemantics) @@ -154,4 +154,4 @@ theorem checkConst_success_disposition end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/CheckerEvidence.lean b/Ix/Kernel/Verify/Check/CheckerEvidence.lean similarity index 95% rename from Ix/Tc/Verify/Check/CheckerEvidence.lean rename to Ix/Kernel/Verify/Check/CheckerEvidence.lean index adc4a0035..7dc5c6ad8 100644 --- a/Ix/Tc/Verify/Check/CheckerEvidence.lean +++ b/Ix/Kernel/Verify/Check/CheckerEvidence.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.Acceptance -import Ix.Tc.Verify.Check.FullInferenceCache +import Ix.Kernel.Verify.Check.Acceptance +import Ix.Kernel.Verify.Check.FullInferenceCache /-! # Semantic evidence from the standalone checker pipelines @@ -14,7 +14,7 @@ The value pipeline is parameterized by the semantic contract for the actual `methods.isDefEq` callback used inside recursive inference. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -29,7 +29,7 @@ theorem checkTypePipeline_sound (kernelCacheSemantics model.keys trProj) trProj world support model.keys.uvars methods) {Delta : KVLCtx} {s after : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsourceSupport : support source) (hsource : PreTrKExprS world.venv model.keys.uvars world.nameOf trProj Delta source sourceV) @@ -96,7 +96,7 @@ theorem checkValuePipeline_sound (kernelCacheSemantics model.keys trProj) trProj world support model.keys.uvars methods) (hdefeq : ∀ {Delta : KVLCtx} {s : TcState .anon} - {left right : KExpr .anon} {leftV rightV : Lean4Lean.VExpr}, + {left right : KExpr .anon} {leftV rightV : Ix.Theory.Named.VExpr}, support left → support right → TrKExprS world.venv model.keys.uvars world.nameOf trProj Delta left leftV → @@ -108,7 +108,7 @@ theorem checkValuePipeline_sound world.venv.IsDefEqU model.keys.uvars Delta.toCtx leftV rightV)) {Delta : KVLCtx} {s after : TcState .anon} {value declaredType : KExpr .anon} - {valueV declaredTypeV : Lean4Lean.VExpr} + {valueV declaredTypeV : Ix.Theory.Named.VExpr} (hvalueSupport : support value) (hdeclaredSupport : support declaredType) (hvalue : PreTrKExprS world.venv model.keys.uvars world.nameOf trProj @@ -171,4 +171,4 @@ theorem checkValuePipeline_sound end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/DeclarationIngress.lean b/Ix/Kernel/Verify/Check/DeclarationIngress.lean similarity index 95% rename from Ix/Tc/Verify/Check/DeclarationIngress.lean rename to Ix/Kernel/Verify/Check/DeclarationIngress.lean index b2d89361c..5ec9df1e1 100644 --- a/Ix/Tc/Verify/Check/DeclarationIngress.lean +++ b/Ix/Kernel/Verify/Check/DeclarationIngress.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.PreTranslationIngress +import Ix.Kernel.Verify.Check.PreTranslationIngress /-! # Standalone declaration ingress @@ -12,9 +12,9 @@ consumed by the two `checkConstMember` pipelines. Neither relation contains a typing judgment or a declaration-WF premise. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VDecl VExpr) +open Ix.Theory.Named (VDecl VExpr) /-- Successful standalone validation facts, including the no-wrap budget needed to interpret the validator's `UInt64` binder depth. -/ @@ -40,7 +40,7 @@ inductive StandaloneScope : KConst .anon -> Prop /-- Raw standalone correspondence after scoping validation, but before any typing has been established. -/ -inductive PreDeclRel (env : Lean4Lean.VEnv) +inductive PreDeclRel (env : Ix.Theory.Named.VEnv) (nameOf : Address -> Option Lean.Name) (trProj : RawProjRel) (id : KId .anon) : KConst .anon -> VDecl -> Prop | axiom @@ -74,7 +74,7 @@ namespace RawDeclRel /-- The exact raw declaration becomes a pre-translation declaration once the production validator's standalone certificate is available. -/ theorem toPre_of_scope - {env : Lean4Lean.VEnv} {nameOf : Address -> Option Lean.Name} + {env : Ix.Theory.Named.VEnv} {nameOf : Address -> Option Lean.Name} {trProj : RawProjRel} {id : KId .anon} (hprojection : trProj.SubstCompatible) (hliterals : forall literal, env.ContainsLits literal) @@ -121,4 +121,4 @@ theorem toPre_of_scope end PendingDecl -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/DeclarationValidation.lean b/Ix/Kernel/Verify/Check/DeclarationValidation.lean similarity index 96% rename from Ix/Tc/Verify/Check/DeclarationValidation.lean rename to Ix/Kernel/Verify/Check/DeclarationValidation.lean index db8b7cd33..68824d8c1 100644 --- a/Ix/Tc/Verify/Check/DeclarationValidation.lean +++ b/Ix/Kernel/Verify/Check/DeclarationValidation.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.ValidatorSoundness -import Ix.Tc.Verify.Check.DeclarationIngress +import Ix.Kernel.Verify.Check.ValidatorSoundness +import Ix.Kernel.Verify.Check.DeclarationIngress /-! # Standalone declaration validation @@ -10,7 +10,7 @@ handled by K3. Finite-run coverage and no-wrap size budgets are explicit resources; neither is inferred from a successful validator return. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Static resources needed to interpret successful standalone validation. The size inequalities justify transporting the validator's `UInt64` binder @@ -110,7 +110,7 @@ namespace PendingDecl pending raw declaration into the pre-translation relation. -/ theorem toPre_of_validation {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} - {id : KId .anon} {decl : Lean4Lean.VDecl} {concrete : KConst .anon} + {id : KId .anon} {decl : Ix.Theory.Named.VDecl} {concrete : KConst .anon} (hprojection : trProj.SubstCompatible) (hliterals : ∀ literal, world.venv.ContainsLits literal) (hpending : PendingDecl trProj world id decl) @@ -126,4 +126,4 @@ theorem toPre_of_validation end PendingDecl -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/DefEqBasicPolicy.lean b/Ix/Kernel/Verify/Check/DefEqBasicPolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/DefEqBasicPolicy.lean rename to Ix/Kernel/Verify/Check/DefEqBasicPolicy.lean index 1b2e5ff0e..40402277d 100644 --- a/Ix/Tc/Verify/Check/DefEqBasicPolicy.lean +++ b/Ix/Kernel/Verify/Check/DefEqBasicPolicy.lean @@ -1,5 +1,5 @@ -import Ix.Tc.DefEq -import Ix.Tc.Verify.Check.WhnfHelperPolicy +import Ix.Kernel.DefEq +import Ix.Kernel.Verify.Check.WhnfHelperPolicy /-! # Operational policy for basic definitional-equality helpers @@ -11,7 +11,7 @@ application-spine recursion. Later DefEq phase proofs build exclusively on these concrete lemmas. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace TcM.PreservesInferOnly @@ -464,4 +464,4 @@ theorem tryDefEqWhnfApp_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/DefEqCachePolicy.lean b/Ix/Kernel/Verify/Check/DefEqCachePolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/DefEqCachePolicy.lean rename to Ix/Kernel/Verify/Check/DefEqCachePolicy.lean index 60f09a723..f004e1d63 100644 --- a/Ix/Tc/Verify/Check/DefEqCachePolicy.lean +++ b/Ix/Kernel/Verify/Check/DefEqCachePolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.DefEqPipelinePolicy +import Ix.Kernel.Verify.Check.DefEqPipelinePolicy /-! # Operational policy for DefEq's cache and recursion shell @@ -9,7 +9,7 @@ shell around those tiers preserves the same caller policy on success and on every error path. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -342,4 +342,4 @@ theorem isDefEq_preservesInferOnly_of_inner end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/DefEqEtaPolicy.lean b/Ix/Kernel/Verify/Check/DefEqEtaPolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/DefEqEtaPolicy.lean rename to Ix/Kernel/Verify/Check/DefEqEtaPolicy.lean index b2d0f9021..982a052e9 100644 --- a/Ix/Tc/Verify/Check/DefEqEtaPolicy.lean +++ b/Ix/Kernel/Verify/Check/DefEqEtaPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.DefEqPropositionPolicy +import Ix.Kernel.Verify.Check.DefEqPropositionPolicy /-! # Operational policy for DefEq eta phases @@ -9,7 +9,7 @@ infer-only type comparison, finite projection-field recursion, and the common-base scan. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -358,4 +358,4 @@ theorem tryEtaStruct_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/DefEqFinalWhnfPolicy.lean b/Ix/Kernel/Verify/Check/DefEqFinalWhnfPolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/DefEqFinalWhnfPolicy.lean rename to Ix/Kernel/Verify/Check/DefEqFinalWhnfPolicy.lean index 086e4cfe4..8f62000c5 100644 --- a/Ix/Tc/Verify/Check/DefEqFinalWhnfPolicy.lean +++ b/Ix/Kernel/Verify/Check/DefEqFinalWhnfPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.DefEqEtaPolicy +import Ix.Kernel.Verify.Check.DefEqEtaPolicy /-! # Operational policy for final-WHNF definitional equality @@ -10,7 +10,7 @@ lemmas preserve that exact production order while framing every success and partial error state. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -366,4 +366,4 @@ theorem isDefEqWhnf_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/DefEqLazyDeltaPolicy.lean b/Ix/Kernel/Verify/Check/DefEqLazyDeltaPolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/DefEqLazyDeltaPolicy.lean rename to Ix/Kernel/Verify/Check/DefEqLazyDeltaPolicy.lean index 09ff9bc13..06c25db3d 100644 --- a/Ix/Tc/Verify/Check/DefEqLazyDeltaPolicy.lean +++ b/Ix/Kernel/Verify/Check/DefEqLazyDeltaPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.DefEqProjectionDeltaPolicy +import Ix.Kernel.Verify.Check.DefEqProjectionDeltaPolicy /-! # Operational policy for the main DefEq lazy-delta loop @@ -9,7 +9,7 @@ probes, one- and two-sided unfolding, projection-app probes, and the stopped continuation into final WHNF comparison. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -476,4 +476,4 @@ theorem isDefEqInnerAfterProofIrrelevance_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/DefEqNatPolicy.lean b/Ix/Kernel/Verify/Check/DefEqNatPolicy.lean similarity index 98% rename from Ix/Tc/Verify/Check/DefEqNatPolicy.lean rename to Ix/Kernel/Verify/Check/DefEqNatPolicy.lean index 21675013f..18defcd2e 100644 --- a/Ix/Tc/Verify/Check/DefEqNatPolicy.lean +++ b/Ix/Kernel/Verify/Check/DefEqNatPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.DefEqBasicPolicy +import Ix.Kernel.Verify.Check.DefEqBasicPolicy /-! # Operational policy for DefEq Nat and String bridges @@ -9,7 +9,7 @@ successful recursive comparison is routed through the framed predecessor method table; all misses and allocation errors preserve the same policy. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -188,4 +188,4 @@ theorem tryStringLitExpansion_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/DefEqPipelinePolicy.lean b/Ix/Kernel/Verify/Check/DefEqPipelinePolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/DefEqPipelinePolicy.lean rename to Ix/Kernel/Verify/Check/DefEqPipelinePolicy.lean index ff1d54d36..7a089cc87 100644 --- a/Ix/Tc/Verify/Check/DefEqPipelinePolicy.lean +++ b/Ix/Kernel/Verify/Check/DefEqPipelinePolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.DefEqLazyDeltaPolicy +import Ix.Kernel.Verify.Check.DefEqLazyDeltaPolicy /-! # Operational policy for the DefEq comparison pipeline @@ -8,7 +8,7 @@ lazy-delta policies across the exact production `isDefEqInner` tier order. It stops at the cache/depth shell owned by `isDefEq` itself. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -239,4 +239,4 @@ theorem isDefEqInner_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/DefEqProjectionDeltaPolicy.lean b/Ix/Kernel/Verify/Check/DefEqProjectionDeltaPolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/DefEqProjectionDeltaPolicy.lean rename to Ix/Kernel/Verify/Check/DefEqProjectionDeltaPolicy.lean index 7a1e5a5ab..16b0a662c 100644 --- a/Ix/Tc/Verify/Check/DefEqProjectionDeltaPolicy.lean +++ b/Ix/Kernel/Verify/Check/DefEqProjectionDeltaPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.DefEqFinalWhnfPolicy +import Ix.Kernel.Verify.Check.DefEqFinalWhnfPolicy /-! # Operational policy for projection-directed DefEq delta reduction @@ -9,7 +9,7 @@ callbacks, projection reduction, bounded iteration, and recursive equality all preserve the inference-policy bit on success and error. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -454,4 +454,4 @@ theorem tryDefEqApp_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/DefEqPropositionPolicy.lean b/Ix/Kernel/Verify/Check/DefEqPropositionPolicy.lean similarity index 98% rename from Ix/Tc/Verify/Check/DefEqPropositionPolicy.lean rename to Ix/Kernel/Verify/Check/DefEqPropositionPolicy.lean index 6681c55fb..8d9f85666 100644 --- a/Ix/Tc/Verify/Check/DefEqPropositionPolicy.lean +++ b/Ix/Kernel/Verify/Check/DefEqPropositionPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.DefEqNatPolicy +import Ix.Kernel.Verify.Check.DefEqNatPolicy /-! # Operational policy for DefEq proposition and unit classifiers @@ -9,7 +9,7 @@ shells, lazy declaration lookups, WHNF calls, and recursive equality edges restore the caller's exact inference policy. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -164,4 +164,4 @@ theorem tryDefEqUnit_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/FullInference.lean b/Ix/Kernel/Verify/Check/FullInference.lean similarity index 95% rename from Ix/Tc/Verify/Check/FullInference.lean rename to Ix/Kernel/Verify/Check/FullInference.lean index 2eba0b79a..197c96b91 100644 --- a/Ix/Tc/Verify/Check/FullInference.lean +++ b/Ix/Kernel/Verify/Check/FullInference.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.PreTranslationCompatibility -import Ix.Tc.Verify.Infer.CacheSoundness +import Ix.Kernel.Verify.Check.PreTranslationCompatibility +import Ix.Kernel.Verify.Infer.CacheSoundness /-! # Full inference from untyped checker ingress @@ -15,9 +15,9 @@ an earlier typed translation, and `PreTrKExprS.upgradeOfTyped` reconciles it with the exact translation chosen by the current raw ingress. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- Successful full inference both validates the source translation and returns a Theory type for that exact translated source. -/ @@ -93,4 +93,4 @@ theorem inferWith_fullHit_pre_acceptance end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/FullInferenceApplications.lean b/Ix/Kernel/Verify/Check/FullInferenceApplications.lean similarity index 98% rename from Ix/Tc/Verify/Check/FullInferenceApplications.lean rename to Ix/Kernel/Verify/Check/FullInferenceApplications.lean index ec85c364c..263823f25 100644 --- a/Ix/Tc/Verify/Check/FullInferenceApplications.lean +++ b/Ix/Kernel/Verify/Check/FullInferenceApplications.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.FullInferenceLeaves -import Ix.Tc.Verify.Check.InferencePolicy +import Ix.Kernel.Verify.Check.FullInferenceLeaves +import Ix.Kernel.Verify.Check.InferencePolicy /-! # Full inference for applications @@ -17,9 +17,9 @@ proof must construct this context; an arbitrary `Methods.WFAt` table cannot, because its semantic contract does not constrain that policy bit. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- Strong recursive services used while reconstructing a typed translation from successful full inference. These are properties of one concrete @@ -287,7 +287,7 @@ private theorem fullApplicationResult exact ⟨hsource, codV.inst aV, hresultTr.trKExpr world.venvWF.ordered theory.literalWF theory.projections.wf hDelta, - Lean4Lean.VEnv.HasType.app hfunAtForall hargAtDom⟩ + Ix.Theory.Named.VEnv.HasType.app hfunAtForall hargAtDom⟩ /-- Execute the final dependent-codomain substitution after a successful full application check. `runIntern` cannot throw and its exact frame proves @@ -457,4 +457,4 @@ theorem inferUncached_app_full_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/FullInferenceBinders.lean b/Ix/Kernel/Verify/Check/FullInferenceBinders.lean similarity index 98% rename from Ix/Tc/Verify/Check/FullInferenceBinders.lean rename to Ix/Kernel/Verify/Check/FullInferenceBinders.lean index fcb7b9a07..6000b42af 100644 --- a/Ix/Tc/Verify/Check/FullInferenceBinders.lean +++ b/Ix/Kernel/Verify/Check/FullInferenceBinders.lean @@ -1,8 +1,10 @@ -import Ix.Tc.Verify.Check.BinderRoundTrip -import Ix.Tc.Verify.Check.FullInferenceApplications -import Ix.Tc.Verify.Infer.ForallTypes -import Ix.Tc.Verify.Infer.LambdaTypes -import Ix.Tc.Verify.Infer.LetTypes +import Ix.Kernel.Verify.Check.BinderRoundTrip +import Ix.Kernel.Verify.Check.FullInferenceApplications +import Ix.Kernel.Verify.Infer.ForallTypes +import Ix.Kernel.Verify.Infer.LambdaTypes +import Ix.Kernel.Verify.Infer.LetTypes + +open Ix.Theory (VLevel) /-! # Full inference for binding forms @@ -13,9 +15,9 @@ the domain, infer the freshly opened body, and close its newly established typed translation back to the original de Bruijn syntax. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -138,7 +140,7 @@ private theorem inferLambdaFullTail_wf .lam ⟨u, htySort⟩ htyTr hbodyTr exact ⟨hpolicyFinal, hresultSupport, hsourceTr, .forallE tyV bodyTyV, hresultTr, - Lean4Lean.VEnv.HasType.lam htySort hbodyTy'⟩ + Ix.Theory.Named.VEnv.HasType.lam htySort hbodyTy'⟩ · intro _ _ herror exact herror @@ -321,7 +323,7 @@ private theorem inferForallFullTail_wf world.venvWF.ordered theory.literalWF theory.projections.wf hDelta have hforall : world.venv.HasType uvars Delta.toCtx (.forallE tyV bodyV) (.sort (.imax u1.toVLevel u2.toVLevel)) := - Lean4Lean.VEnv.HasType.forallE htySort (by simpa using hbodySort) + Ix.Theory.Named.VEnv.HasType.forallE htySort (by simpa using hbodySort) have hlevelEq := hu1.mkIMax_equiv hcollision hu2 have hsortEq : world.venv.IsDefEqU uvars Delta.toCtx (.sort (.imax u1.toVLevel u2.toVLevel)) @@ -654,4 +656,4 @@ theorem inferUncached_let_full_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/FullInferenceCache.lean b/Ix/Kernel/Verify/Check/FullInferenceCache.lean similarity index 96% rename from Ix/Tc/Verify/Check/FullInferenceCache.lean rename to Ix/Kernel/Verify/Check/FullInferenceCache.lean index ecb196aa4..17d4a6c3e 100644 --- a/Ix/Tc/Verify/Check/FullInferenceCache.lean +++ b/Ix/Kernel/Verify/Check/FullInferenceCache.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.FullInferenceDispatcher -import Ix.Tc.Verify.Infer.CacheSoundness +import Ix.Kernel.Verify.Check.FullInferenceDispatcher +import Ix.Kernel.Verify.Infer.CacheSoundness /-! # Full-inference cache shell @@ -11,7 +11,7 @@ translation; misses construct ordinary collision-robust K2 provenance before writing the validated cache partition. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace FullUncachedInference.Context @@ -48,7 +48,7 @@ private theorem missTail_full_wf (kernelCacheSemantics model.keys trProj) trProj world support model.keys.uvars methods) {Delta : KVLCtx} {before s : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} {key : Address × Address} (hmatch : model.keys.Matches trProj world before Delta source key) (hsourceSupport : support source) @@ -105,7 +105,7 @@ theorem inferWith_full_wf (kernelCacheSemantics model.keys trProj) trProj world support model.keys.uvars methods) {Delta : KVLCtx} {s : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsourceSupport : support source) (hsource : PreTrKExprS world.venv model.keys.uvars world.nameOf trProj Delta source sourceV) @@ -177,7 +177,7 @@ theorem infer_full_wf (kernelCacheSemantics model.keys trProj) trProj world support model.keys.uvars methods) {Delta : KVLCtx} {s : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsourceSupport : support source) (hsource : PreTrKExprS world.venv model.keys.uvars world.nameOf trProj Delta source sourceV) @@ -195,4 +195,4 @@ theorem infer_full_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/FullInferenceDispatcher.lean b/Ix/Kernel/Verify/Check/FullInferenceDispatcher.lean similarity index 96% rename from Ix/Tc/Verify/Check/FullInferenceDispatcher.lean rename to Ix/Kernel/Verify/Check/FullInferenceDispatcher.lean index 6a9c5ad74..0e0549307 100644 --- a/Ix/Tc/Verify/Check/FullInferenceDispatcher.lean +++ b/Ix/Kernel/Verify/Check/FullInferenceDispatcher.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.FullInferenceProjections -import Ix.Tc.Verify.Infer.Dispatcher +import Ix.Kernel.Verify.Check.FullInferenceProjections +import Ix.Kernel.Verify.Infer.Dispatcher /-! # Exhaustive full-mode inference dispatcher @@ -14,7 +14,7 @@ In particular, neither the ordinary method-table contract nor a successful typing postcondition says what `inferOnly` contains after a partial error. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace FullUncachedInference @@ -47,7 +47,7 @@ private theorem strengthenFullLeaf {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {source : KExpr .anon} - {sourceV : Lean4Lean.VExpr} {methods : Methods .anon} + {sourceV : Ix.Theory.Named.VExpr} {methods : Methods .anon} (hsemantic : TcM.WF (WhnfStateInv .noAccel semantics trProj world support uvars Delta) s ((inferUncached inferCall false source).run methods) @@ -80,7 +80,7 @@ theorem inferUncached_full_wf (context : FullUncachedInference.Context initial program requests semantics trProj world support uvars methods) {Delta : KVLCtx} {s : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsourceSupport : support source) (hsource : PreTrKExprS world.venv uvars world.nameOf trProj Delta source sourceV) @@ -172,4 +172,4 @@ theorem inferUncached_full_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/FullInferenceKnot.lean b/Ix/Kernel/Verify/Check/FullInferenceKnot.lean similarity index 94% rename from Ix/Tc/Verify/Check/FullInferenceKnot.lean rename to Ix/Kernel/Verify/Check/FullInferenceKnot.lean index 62bf3218d..b6eedb221 100644 --- a/Ix/Tc/Verify/Check/FullInferenceKnot.lean +++ b/Ix/Kernel/Verify/Check/FullInferenceKnot.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Check.FullInferenceCache -import Ix.Tc.Verify.Check.RecursiveMethodPolicy -import Ix.Tc.Verify.RecursiveMethods.Closure +import Ix.Kernel.Verify.Check.FullInferenceCache +import Ix.Kernel.Verify.Check.RecursiveMethodPolicy +import Ix.Kernel.Verify.RecursiveMethods.Closure /-! # Full-inference closure of the production recursion knot @@ -17,7 +17,7 @@ one outer `RecM.infer` layer uses only the semantic, operational, and strong full-inference contracts of its strictly smaller callback table. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace Methods @@ -29,7 +29,7 @@ def FullInferenceWFAt (world : VerifyWorld) (support : RunSupport) (uvars : Nat) (methods : Methods .anon) : Prop := ∀ {Delta : KVLCtx} {s : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr}, + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr}, s.inferOnly = false → support source → PreTrKExprS world.venv uvars world.nameOf trProj Delta source sourceV → @@ -159,7 +159,7 @@ theorem methodsN_fullInferenceWFAt Methods.FullInferenceWFAt (kernelCacheSemantics proposition.model.keys trProj) trProj world support proposition.model.keys.uvars - (Ix.Tc.methodsN (m := .anon) depth) := by + (Ix.Kernel.methodsN (m := .anon) depth) := by induction depth with | zero => exact Methods.methodsOut_fullInferenceWFAt @@ -171,14 +171,14 @@ theorem methodsN_fullInferenceWFAt (WhnfStateInv .noAccel (kernelCacheSemantics proposition.model.keys trProj) trProj world support proposition.model.keys.uvars Delta) s - ((Methods.next (Ix.Tc.methodsN depth)).infer source) + ((Methods.next (Ix.Kernel.methodsN depth)).infer source) (fun result after => after.inferOnly = false ∧ FullInferPost trProj world support proposition.model.keys.uvars Delta source sourceV result) (fun _ after => after.inferOnly = false) exact - (context.next_fullInferenceWFAt (Ix.Tc.methodsN depth) + (context.next_fullInferenceWFAt (Ix.Kernel.methodsN depth) (context.methodsN depth) (Methods.methodsN_concrete_preservesInferOnly depth) ih) hbefore hsourceSupport hsource @@ -194,7 +194,7 @@ theorem publicInfer_full_wf (context : RecursiveMethodClosureContext initial program requests support proposition eligible) {Delta : KVLCtx} {s : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hbefore : s.inferOnly = false) (hsourceSupport : support source) (hsource : PreTrKExprS world.venv proposition.model.keys.uvars @@ -209,7 +209,7 @@ theorem publicInfer_full_wf FullInferPost trProj world support proposition.model.keys.uvars Delta source sourceV result) (fun _ after => after.inferOnly = false) := by - let methods := Ix.Tc.methodsN (m := .anon) s.recFuel.toNat + let methods := Ix.Kernel.methodsN (m := .anon) s.recFuel.toNat have hmethods : Methods.WFAt .noAccel (kernelCacheSemantics proposition.model.keys trProj) trProj world support proposition.model.keys.uvars methods := @@ -227,4 +227,4 @@ theorem publicInfer_full_wf end RecursiveMethodClosureContext -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/FullInferenceLeaves.lean b/Ix/Kernel/Verify/Check/FullInferenceLeaves.lean similarity index 95% rename from Ix/Tc/Verify/Check/FullInferenceLeaves.lean rename to Ix/Kernel/Verify/Check/FullInferenceLeaves.lean index 768cd9174..46d13aafe 100644 --- a/Ix/Tc/Verify/Check/FullInferenceLeaves.lean +++ b/Ix/Kernel/Verify/Check/FullInferenceLeaves.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Check.FullInference +import Ix.Kernel.Verify.Check.FullInference + +open Ix.Theory (VLevel) /-! # Full inference for untyped leaf ingress @@ -10,7 +12,7 @@ application and binder constructors remain genuinely new K3 work because their typed constructors contain the checks full inference must establish. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -20,7 +22,7 @@ theorem inferUncached_sort_full_wf {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} {u : KUniv .anon} {info : ExprInfo .anon} - {sourceV : Lean4Lean.VExpr} + {sourceV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hcollision : support.CollisionFree) (hresultSupport : support (KExpr.mkSort (KUniv.mkSucc u))) @@ -48,7 +50,7 @@ theorem inferUncached_var_full_wf {Delta : KVLCtx} {s : TcState .anon} {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} {idx : UInt64} {name : Mode.anon.F Name} - {info : ExprInfo .anon} {sourceV : Lean4Lean.VExpr} + {info : ExprInfo .anon} {sourceV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hsource : PreTrKExprS world.venv uvars world.nameOf trProj Delta (.var idx name info) sourceV) @@ -75,7 +77,7 @@ theorem inferUncached_fvar_full_wf {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} {fv : FVarId} {name : Mode.anon.F Name} - {info : ExprInfo .anon} {sourceV : Lean4Lean.VExpr} + {info : ExprInfo .anon} {sourceV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hsafe : FVarInferSafety layer semantics trProj world support uvars Delta) @@ -103,7 +105,7 @@ theorem inferUncached_const_full_wf {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} {id : KId .anon} {levels : Array (KUniv .anon)} {info : ExprInfo .anon} - {sourceV : Lean4Lean.VExpr} + {sourceV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hfault : TcM.LazyFaultPreserves (WhnfStateInv layer semantics trProj world support uvars Delta)) @@ -136,7 +138,7 @@ theorem inferUncached_nat_full_wf {Delta : KVLCtx} {s : TcState .anon} {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} {n : Nat} {blob : Address} - {info : ExprInfo .anon} {sourceV : Lean4Lean.VExpr} + {info : ExprInfo .anon} {sourceV : Ix.Theory.Named.VExpr} (context : LiteralInferContext world support) (theory : WhnfTheory trProj world uvars) (hsource : PreTrKExprS world.venv uvars world.nameOf trProj Delta @@ -159,7 +161,7 @@ theorem inferUncached_str_full_wf {Delta : KVLCtx} {s : TcState .anon} {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} {value : String} {blob : Address} - {info : ExprInfo .anon} {sourceV : Lean4Lean.VExpr} + {info : ExprInfo .anon} {sourceV : Ix.Theory.Named.VExpr} (context : LiteralInferContext world support) (theory : WhnfTheory trProj world uvars) (hsource : PreTrKExprS world.venv uvars world.nameOf trProj Delta @@ -179,4 +181,4 @@ theorem inferUncached_str_full_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/FullInferenceProjections.lean b/Ix/Kernel/Verify/Check/FullInferenceProjections.lean similarity index 96% rename from Ix/Tc/Verify/Check/FullInferenceProjections.lean rename to Ix/Kernel/Verify/Check/FullInferenceProjections.lean index 0c8ed01ac..cfe360bb4 100644 --- a/Ix/Tc/Verify/Check/FullInferenceProjections.lean +++ b/Ix/Kernel/Verify/Check/FullInferenceProjections.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.FullInferenceBinders -import Ix.Tc.Verify.Infer.ProjectionTypes +import Ix.Kernel.Verify.Check.FullInferenceBinders +import Ix.Kernel.Verify.Infer.ProjectionTypes /-! # Full inference for projections @@ -15,9 +15,9 @@ policy frame. A typing proof alone cannot show that a partial error preserved for one concrete smaller method table. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace ProjectionInference @@ -124,4 +124,4 @@ theorem inferUncached_prj_full_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/InferencePolicy.lean b/Ix/Kernel/Verify/Check/InferencePolicy.lean similarity index 98% rename from Ix/Tc/Verify/Check/InferencePolicy.lean rename to Ix/Kernel/Verify/Check/InferencePolicy.lean index e4cde5e74..cba828a38 100644 --- a/Ix/Tc/Verify/Check/InferencePolicy.lean +++ b/Ix/Kernel/Verify/Check/InferencePolicy.lean @@ -1,7 +1,7 @@ -import Ix.Tc.Verify.Knot -import Ix.Tc.Verify.Infer.Applications -import Ix.Tc.Verify.Whnf.StructEta.CallbackPrefix -import Ix.Tc.Verify.Whnf.StructEta.RecursionClassifier +import Ix.Kernel.Verify.Knot +import Ix.Kernel.Verify.Infer.Applications +import Ix.Kernel.Verify.Whnf.StructEta.CallbackPrefix +import Ix.Kernel.Verify.Whnf.StructEta.RecursionClassifier /-! # Inference-policy frames @@ -19,7 +19,7 @@ smaller callbacks do. No semantic typing claim is bundled into this operational frame. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- An action restores the caller's inference policy on both outcomes. -/ def TcM.PreservesInferOnly (x : TcM .anon alpha) : Prop := @@ -649,7 +649,7 @@ theorem PreservesInferOnly.isDefEq_full_wf {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {methods : Methods .anon} {s : TcState .anon} {left right : KExpr .anon} - {leftV rightV : Lean4Lean.VExpr} + {leftV rightV : Ix.Theory.Named.VExpr} (hsemantic : Methods.WFAt layer semantics trProj world support uvars methods) (hframe : methods.PreservesInferOnly) @@ -677,4 +677,4 @@ theorem PreservesInferOnly.isDefEq_full_wf end Methods -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/MemberEvidence.lean b/Ix/Kernel/Verify/Check/MemberEvidence.lean similarity index 98% rename from Ix/Tc/Verify/Check/MemberEvidence.lean rename to Ix/Kernel/Verify/Check/MemberEvidence.lean index b3e45b2ab..33589ff34 100644 --- a/Ix/Tc/Verify/Check/MemberEvidence.lean +++ b/Ix/Kernel/Verify/Check/MemberEvidence.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.BoundedPipelines -import Ix.Tc.Verify.Check.SafetyFrame +import Ix.Kernel.Verify.Check.BoundedPipelines +import Ix.Kernel.Verify.Check.SafetyFrame /-! # Semantic evidence from standalone member checking @@ -14,7 +14,7 @@ successful execution is nevertheless part of the trace from which the typing evidence is extracted. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -92,7 +92,7 @@ theorem checkConstMember_axiom_sound {id : KId .anon} {name : Mode.anon.F Name} {levelParams : Mode.anon.F (Array Name)} {isUnsafe : Bool} {levels : UInt64} {type : KExpr .anon} - {typeV : Lean4Lean.VExpr} + {typeV : Ix.Theory.Named.VExpr} (hresources : StandaloneValidationResources support (.axio name levelParams isUnsafe levels type)) (hsourceCall : context.typeSources type) @@ -166,7 +166,7 @@ theorem checkConstMember_defn_sound {safety : Ix.DefinitionSafety} {hints : Lean.ReducibilityHints} {levels : UInt64} {type value : KExpr .anon} {leanAll : Mode.anon.F (Array (KId .anon))} {block : KId .anon} - {typeV valueV : Lean4Lean.VExpr} + {typeV valueV : Ix.Theory.Named.VExpr} (hresources : StandaloneValidationResources support (.defn name levelParams kind safety hints levels type value leanAll block)) @@ -350,7 +350,7 @@ theorem checkConstMember_sound model.keys.uvars calls (Methods.next methods)) (hmethodPolicy : (Methods.next methods).PreservesInferOnly) {id : KId .anon} {concrete : KConst .anon} - {decl : Lean4Lean.VDecl} + {decl : Ix.Theory.Named.VDecl} (hingress : PreDeclRel world.venv world.nameOf trProj id concrete decl) (hcovers : context.Covers concrete) (hresources : StandaloneValidationResources support concrete) @@ -456,7 +456,7 @@ theorem checkConstMember_pending_sound model.keys.uvars calls (Methods.next methods)) (hmethodPolicy : (Methods.next methods).PreservesInferOnly) {id : KId .anon} {concrete : KConst .anon} - {decl : Lean4Lean.VDecl} + {decl : Ix.Theory.Named.VDecl} (hprojection : trProj.SubstCompatible) (hliterals : ∀ literal, world.venv.ContainsLits literal) (hpending : PendingDecl trProj world id decl) @@ -496,4 +496,4 @@ theorem checkConstMember_pending_sound end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/NatAcceptance.lean b/Ix/Kernel/Verify/Check/NatAcceptance.lean similarity index 97% rename from Ix/Tc/Verify/Check/NatAcceptance.lean rename to Ix/Kernel/Verify/Check/NatAcceptance.lean index becb4322b..c9de163f8 100644 --- a/Ix/Tc/Verify/Check/NatAcceptance.lean +++ b/Ix/Kernel/Verify/Check/NatAcceptance.lean @@ -1,5 +1,7 @@ -import Ix.Tc.Verify.NatFixture -import Ix.Tc.Verify.Check.PublicStandalone +import Ix.Kernel.Verify.NatFixture +import Ix.Kernel.Verify.Check.PublicStandalone + +open Ix.Theory (VLevel) /-! # Concrete standalone-check acceptance over the ambient Nat world @@ -17,7 +19,7 @@ cache use, reset, validation, acceptance, and rollback rather than about a mock implementation. -/ -namespace Ix.Tc.AmbientNat +namespace Ix.Kernel.AmbientNat def acceptanceEnv : KEnv .anon := { loadedEnv with inferCache := @@ -204,14 +206,14 @@ theorem natType_translation : · simpa [natType] using (TrKExprS.sort (env := worldNat.venv) (nameOf := worldNat.nameOf) (trProj := RawProjRel.none) (Δ := []) oneLevel_wf) - · exact Lean4Lean.VEnv.IsDefEqU.refl - ⟨_, Lean4Lean.VEnv.HasType.sort oneLevel_wf⟩ + · exact Ix.Theory.Named.VEnv.IsDefEqU.refl + ⟨_, Ix.Theory.Named.VEnv.HasType.sort oneLevel_wf⟩ theorem natReference_type : worldNat.venv.HasType 0 [] (.const natName []) (.sort oneLevel.toVLevel) := by exact - (Lean4Lean.VEnv.HasType.const (env := natEnv) (U := 0) (Γ := []) + (Ix.Theory.Named.VEnv.HasType.const (env := natEnv) (U := 0) (Γ := []) (ci := natConstant) (ls := []) natEnv_nat (by simp) rfl) theorem oneLevel_view : @@ -224,8 +226,8 @@ theorem oneLevel_view : | succ hchild => cases hchild exact Or.inl rfl - · exact Lean4Lean.VEnv.IsDefEqU.refl - ⟨_, Lean4Lean.VEnv.HasType.sort oneLevel_wf⟩ + · exact Ix.Theory.Named.VEnv.IsDefEqU.refl + ⟨_, Ix.Theory.Named.VEnv.HasType.sort oneLevel_wf⟩ theorem goodTypeEvidence : TypeCheckEvidence RawProjRel.none worldNat acceptanceSupport 0 [] @@ -549,7 +551,7 @@ structure PublicCheckLifecycle : Prop where invalidPending : PendingDecl RawProjRel.none worldGood IllTypedPending.targetId IllTypedPending.theoryDecl invalidSemantic : - ¬∃ env', Lean4Lean.VDecl.WF worldGood.venv + ¬∃ env', Ix.Theory.Named.VDecl.WF worldGood.venv IllTypedPending.theoryDecl env' invalidExecution : TcM.checkConst IllTypedPending.targetId resetState = @@ -582,4 +584,4 @@ def goodSucceeded : Bool := example : goodSucceeded = true := by simp [goodSucceeded, initial_good_public] -end Ix.Tc.AmbientNat +end Ix.Kernel.AmbientNat diff --git a/Ix/Tc/Verify/Check/PositiveFuelSort.lean b/Ix/Kernel/Verify/Check/PositiveFuelSort.lean similarity index 94% rename from Ix/Tc/Verify/Check/PositiveFuelSort.lean rename to Ix/Kernel/Verify/Check/PositiveFuelSort.lean index 1469c28a3..39cc0d1be 100644 --- a/Ix/Tc/Verify/Check/PositiveFuelSort.lean +++ b/Ix/Kernel/Verify/Check/PositiveFuelSort.lean @@ -1,7 +1,7 @@ -import Ix.Tc.Verify.Check.BoundedPipelines -import Ix.Tc.Verify.RecursiveMethods.SortInference -import Ix.Tc.Verify.RecursiveMethods.ScopedSortInference -import Ix.Tc.Verify.ScopedSuffix.ClosedContext +import Ix.Kernel.Verify.Check.BoundedPipelines +import Ix.Kernel.Verify.RecursiveMethods.SortInference +import Ix.Kernel.Verify.RecursiveMethods.ScopedSortInference +import Ix.Kernel.Verify.ScopedSuffix.ClosedContext /-! # Positive-fuel bounded checker witness @@ -15,7 +15,7 @@ inequalities, strong inference upgrade, and checker pipeline resources are all concrete. -/ -namespace Ix.Tc.PositiveFuelSort +namespace Ix.Kernel.PositiveFuelSort def sourceUniv : KUniv .anon := KUniv.mkZero def resultUniv : KUniv .anon := KUniv.mkSucc sourceUniv @@ -118,8 +118,8 @@ def theory (uvars : Nat) : intro literal hliteral cases literal <;> simp [VerifyWorld.empty, VerifyWorld.ofCatalog, - Lean4Lean.VEnv.ContainsLits, Lean4Lean.VEnv.contains, - Lean4Lean.VEnv.empty] at hliteral + Ix.Theory.Named.VEnv.ContainsLits, Ix.Theory.Named.VEnv.contains, + Ix.Theory.Named.VEnv.empty] at hliteral projections := RawProjRel.none_ok VerifyWorld.empty.venv uvars /-! ## Concrete run-scoped suffix instance -/ @@ -235,12 +235,12 @@ theorem scopedPublicInference_execution result_supported scopedInitialState_baseInv have hbody : (RecM.inferUncached RecM.inferCall false source).run - (Ix.Tc.methodsN (m := .anon) 1) scopedInitialState = + (Ix.Kernel.methodsN (m := .anon) 1) scopedInitialState = .ok result afterIntern := by exact hintern have hshell := RecM.inferWith_fullMiss_success (inferRec := RecM.inferCall) - (methods := Ix.Tc.methodsN (m := .anon) 1) + (methods := Ix.Kernel.methodsN (m := .anon) 1) (source := source) (ty := result) (key := scopedInferKey) (s := scopedInitialState) (sKey := scopedInitialState) (sBody := afterIntern) (by rfl) scopedInitialState_inferKey @@ -277,7 +277,7 @@ theorem methodContractAtFuelOne (kernelCacheSemantics model.keys RawProjRel.none) RawProjRel.none VerifyWorld.empty support model.keys.uvars (.singletonInfer source) - (Methods.next (Ix.Tc.methodsN (m := .anon) 1)) := by + (Methods.next (Ix.Kernel.methodsN (m := .anon) 1)) := by simpa [Methods.SortSchedule.calls] using (scheduleAtFuelOne separation model).nextSelected @@ -290,7 +290,7 @@ theorem fullInferenceAtFuelOne (kernelCacheSemantics model.keys RawProjRel.none) RawProjRel.none VerifyWorld.empty support model.keys.uvars (.singletonInfer source) - (Methods.next (Ix.Tc.methodsN (m := .anon) 1)) := + (Methods.next (Ix.Kernel.methodsN (m := .anon) 1)) := Methods.FullInferenceWFAtOn.ofSingletonSort (methodContractAtFuelOne separation model) (Methods.next_preservesInferOnly _ @@ -304,7 +304,7 @@ def pipelinesAtFuelOne StandalonePipelineResources (kernelCacheSemantics model.keys RawProjRel.none) RawProjRel.none VerifyWorld.empty support model.keys.uvars - (.singletonInfer source) (Ix.Tc.methodsN (m := .anon) 1) := + (.singletonInfer source) (Ix.Kernel.methodsN (m := .anon) 1) := StandalonePipelineResources.singletonSortAxiom (fullInferenceAtFuelOne separation model) sortResources supported_is_sort @@ -317,4 +317,4 @@ theorem pipelines_cover_concreteAxiom (pipelinesAtFuelOne separation model).Covers concreteAxiom := .axiom rfl -end Ix.Tc.PositiveFuelSort +end Ix.Kernel.PositiveFuelSort diff --git a/Ix/Tc/Verify/Check/PreTranslation.lean b/Ix/Kernel/Verify/Check/PreTranslation.lean similarity index 96% rename from Ix/Tc/Verify/Check/PreTranslation.lean rename to Ix/Kernel/Verify/Check/PreTranslation.lean index 9f033a626..f2172cdf0 100644 --- a/Ix/Tc/Verify/Check/PreTranslation.lean +++ b/Ix/Kernel/Verify/Check/PreTranslation.lean @@ -1,6 +1,8 @@ -import Ix.Tc.Verify.Check.Scoped -import Ix.Tc.Verify.Decl -import Ix.Tc.Verify.Trans +import Ix.Kernel.Verify.Check.Scoped +import Ix.Kernel.Verify.Decl +import Ix.Kernel.Verify.Trans + +open Ix.Theory (VLevel) /-! # Untyped structural translation for checker ingress @@ -18,9 +20,9 @@ premise. Successful full inference will upgrade this relation to declaration. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr VEnv VConstant) +open Ix.Theory.Named (VExpr VEnv VConstant) variable (env : VEnv) (uvars : Nat) (nameOf : Address → Option Lean.Name) (trProj : RawProjRel) in @@ -146,4 +148,4 @@ theorem contextScoped end PreTrKExprS -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/PreTranslationCompatibility.lean b/Ix/Kernel/Verify/Check/PreTranslationCompatibility.lean similarity index 95% rename from Ix/Tc/Verify/Check/PreTranslationCompatibility.lean rename to Ix/Kernel/Verify/Check/PreTranslationCompatibility.lean index f4cf8c3bd..2db1c4c26 100644 --- a/Ix/Tc/Verify/Check/PreTranslationCompatibility.lean +++ b/Ix/Kernel/Verify/Check/PreTranslationCompatibility.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.PreTranslationIngress -import Lean4Lean.Theory.Typing.Strong +import Ix.Kernel.Verify.Check.PreTranslationIngress +import Ix.Theory.Named.Typing.Strong /-! # Compatibility of raw and typed structural translations @@ -11,9 +11,9 @@ chosen by the current raw translation and borrows only the typing evidence from the checked witness. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr VEnv) +open Ix.Theory.Named (VExpr VEnv) /-- Upgrade a raw structural translation using any typed translation of the same kernel expression in a pairwise-definitionally-equal context. -/ @@ -138,14 +138,14 @@ theorem PreTrKExprS.upgradeBinderCoreOfWF | app hpreFn hpreArg ihFn ihArg => simp only [KExpr.binderCore, Bool.and_eq_true] at hcore obtain ⟨type, body, hfnType, hargType⟩ := - Lean4Lean.VExpr.WF.app_inv henv.ordered hDelta.toCtx hwf + Ix.Theory.Named.VExpr.WF.app_inv henv.ordered hDelta.toCtx hwf exact .app hfnType hargType (ihFn hDelta hcore.1 ⟨_, hfnType⟩) (ihArg hDelta hcore.2 ⟨_, hargType⟩) | lam hpreType hpreBody ihType ihBody => simp only [KExpr.binderCore, Bool.and_eq_true] at hcore obtain ⟨htype, hbody⟩ := - Lean4Lean.VExpr.WF.lam_inv henv.ordered hDelta.toCtx hwf + Ix.Theory.Named.VExpr.WF.lam_inv henv.ordered hDelta.toCtx hwf have htypeWF : VExpr.WF env uvars _ _ := ⟨_, htype.choose_spec⟩ exact .lam htype @@ -155,7 +155,7 @@ theorem PreTrKExprS.upgradeBinderCoreOfWF simp only [KExpr.binderCore, Bool.and_eq_true] at hcore obtain ⟨_, hwhole⟩ := hwf obtain ⟨htype, hbody⟩ := - Lean4Lean.VEnv.HasType.forallE_inv henv.ordered hwhole + Ix.Theory.Named.VEnv.HasType.forallE_inv henv.ordered hwhole have htypeWF : VExpr.WF env uvars _ _ := ⟨_, htype.choose_spec⟩ have hbodyWF : VExpr.WF env uvars _ _ := @@ -168,4 +168,4 @@ theorem PreTrKExprS.upgradeBinderCoreOfWF | nat => simp [KExpr.binderCore] at hcore | str => simp [KExpr.binderCore] at hcore -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/PreTranslationIngress.lean b/Ix/Kernel/Verify/Check/PreTranslationIngress.lean similarity index 97% rename from Ix/Tc/Verify/Check/PreTranslationIngress.lean rename to Ix/Kernel/Verify/Check/PreTranslationIngress.lean index 9ba869fa1..1bb26dfc7 100644 --- a/Ix/Tc/Verify/Check/PreTranslationIngress.lean +++ b/Ix/Kernel/Verify/Check/PreTranslationIngress.lean @@ -1,5 +1,7 @@ -import Ix.Tc.Verify.Check.PreTranslation -import Ix.Tc.Verify.Subst +import Ix.Kernel.Verify.Check.PreTranslation +import Ix.Kernel.Verify.Subst + +open Ix.Theory (VLevel) /-! # Raw-declaration ingress into `PreTrKExprS` @@ -17,7 +19,7 @@ The main theorem below proves that raw translation plus the syntax-only translation required by full inference. No typing judgment is assumed. -/ -namespace Lean4Lean.VExpr +namespace Ix.Theory.Named.VExpr private def Subst.comp (sigma tau : Subst) : Subst := fun index => (sigma index).subst tau @@ -32,7 +34,7 @@ private theorem Subst.comp_lift {sigma tau : Subst} : rw [lift_eq_lift', lift_eq_lift', lift'_subst, subst_lift'] congr 1 funext inner - simp [Subst.lift_r, Subst.lift_l, Lean4Lean.Lift.liftVar, + simp [Subst.lift_r, Subst.lift_l, Ix.Theory.Named.Lift.liftVar, Subst.lift, lift_eq_lift'] private theorem subst_subst {e : VExpr} {sigma tau : Subst} : @@ -71,11 +73,11 @@ theorem inst_subst_cons (body value : VExpr) (sigma : Subst) : (lift_subst_cons (e := VExpr.bvar index) (sigma := sigma) (value := value.subst sigma)) -end Lean4Lean.VExpr +end Ix.Theory.Named.VExpr -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr VLocalDecl) +open Ix.Theory.Named (VExpr VLocalDecl) private theorem subst_natLit (value : Nat) (sigma : VExpr.Subst) : (VExpr.natLit value).subst sigma = VExpr.natLit value := by @@ -194,7 +196,7 @@ namespace RawExprRel /-- General substitution-aware ingress theorem. The size bound rules out `UInt64` wraparound when the validator descends through a binder. -/ theorem toPre_of_scoped_aux - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address -> Option Lean.Name} {trProj : RawProjRel} (hprojection : trProj.SubstCompatible) (hliterals : forall literal, env.ContainsLits literal) @@ -282,7 +284,7 @@ theorem toPre_of_scoped_aux /-- Closed declaration ingress: successful scoping turns the exact raw Theory term into the `PreTrKExprS` witness consumed by full inference. -/ theorem toPre_of_scoped - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address -> Option Lean.Name} {trProj : RawProjRel} (hprojection : trProj.SubstCompatible) (hliterals : forall literal, env.ContainsLits literal) @@ -298,7 +300,7 @@ theorem toPre_of_scoped lets, and projections makes their ambient semantic hypotheses unnecessary; all remaining premises are syntax-only scoping and exact raw translation. -/ theorem toPreBinderCore_of_scoped_aux - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address -> Option Lean.Name} {trProj : RawProjRel} {ctx : List VExpr} {Delta : KVLCtx} {sigma : VExpr.Subst} {depth : UInt64} {source : KExpr .anon} {sourceV : VExpr} @@ -367,7 +369,7 @@ theorem toPreBinderCore_of_scoped_aux /-- Closed binder-core declarations enter `PreTrKExprS` without requiring an irrelevant primitive-literal environment. -/ theorem toPreBinderCore_of_scoped - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address -> Option Lean.Name} {trProj : RawProjRel} {source : KExpr .anon} {sourceV : VExpr} (hraw : RawExprRel (uvars := uvars) env nameOf trProj [] source sourceV) @@ -380,4 +382,4 @@ theorem toPreBinderCore_of_scoped end RawExprRel -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/PreTranslationOpening.lean b/Ix/Kernel/Verify/Check/PreTranslationOpening.lean similarity index 97% rename from Ix/Tc/Verify/Check/PreTranslationOpening.lean rename to Ix/Kernel/Verify/Check/PreTranslationOpening.lean index 48f402999..d00ad1670 100644 --- a/Ix/Tc/Verify/Check/PreTranslationOpening.lean +++ b/Ix/Kernel/Verify/Check/PreTranslationOpening.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.PreTranslation -import Ix.Tc.Verify.Infer.BinderOpening +import Ix.Kernel.Verify.Check.PreTranslation +import Ix.Kernel.Verify.Infer.BinderOpening /-! # Binder opening for untyped checker ingress @@ -10,9 +10,9 @@ operation as the already verified typed inference path. This file proves that operation preserves the deliberately untyped `PreTrKExprS` relation. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr VEnv VLocalDecl) +open Ix.Theory.Named (VExpr VEnv VLocalDecl) /-- Replacing one de Bruijn binder with its freshly tagged fvar leaves the pre-translation's Theory expression unchanged. -/ @@ -187,4 +187,4 @@ theorem PreTrKExprS.openFVarZero (KExpr.instantiateRevSpec body #[.mkFVar fv name] 0) bodyV := H.openFVar .zero rfl (by simpa using hfresh) (by simpa using hbound) -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/PreTranslationScopes.lean b/Ix/Kernel/Verify/Check/PreTranslationScopes.lean similarity index 97% rename from Ix/Tc/Verify/Check/PreTranslationScopes.lean rename to Ix/Kernel/Verify/Check/PreTranslationScopes.lean index 0ed1b2987..d1bf29d5c 100644 --- a/Ix/Tc/Verify/Check/PreTranslationScopes.lean +++ b/Ix/Kernel/Verify/Check/PreTranslationScopes.lean @@ -1,7 +1,7 @@ -import Ix.Tc.Verify.Check.InferencePolicy -import Ix.Tc.Verify.Check.PreTranslationOpening -import Ix.Tc.Verify.Infer.BinderScopes -import Ix.Tc.Verify.Infer.LetScopes +import Ix.Kernel.Verify.Check.InferencePolicy +import Ix.Kernel.Verify.Check.PreTranslationOpening +import Ix.Kernel.Verify.Infer.BinderScopes +import Ix.Kernel.Verify.Infer.LetScopes /-! # Binder scopes for pre-typed checker ingress @@ -13,7 +13,7 @@ factored operational binder-opening core with `PreTrKExprS.openFVarZero` and the independent inference-policy frame. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace TcM @@ -25,7 +25,7 @@ theorem openBinder_pre_scope {trProj : RawProjRel} {world : VerifyWorld} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} - {type body : KExpr .anon} {typeV bodyV : Lean4Lean.VExpr} + {type body : KExpr .anon} {typeV bodyV : Ix.Theory.Named.VExpr} (htype : TrKExprS world.venv uvars world.nameOf trProj Delta type typeV) (htypeType : world.venv.IsType uvars Delta.toCtx typeV) (hbody : PreTrKExprS world.venv uvars world.nameOf trProj @@ -83,7 +83,7 @@ theorem openLet_pre_scope {Delta : KVLCtx} {s : TcState .anon} {name : Mode.anon.F Name} {type value body : KExpr .anon} - {typeV valueV bodyV : Lean4Lean.VExpr} + {typeV valueV bodyV : Ix.Theory.Named.VExpr} (htype : TrKExprS world.venv uvars world.nameOf trProj Delta type typeV) (hvalue : TrKExprS world.venv uvars world.nameOf trProj Delta value valueV) (hvalueType : world.venv.HasType uvars Delta.toCtx valueV typeV) @@ -147,7 +147,7 @@ theorem withLctxScope_openBinder_pre_wf {trProj : RawProjRel} {world : VerifyWorld} {uvars : Nat} {Delta : KVLCtx} {methods : Methods .anon} {s : TcState .anon} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} - {type body : KExpr .anon} {typeV bodyV : Lean4Lean.VExpr} + {type body : KExpr .anon} {typeV bodyV : Ix.Theory.Named.VExpr} (htype : TrKExprS world.venv uvars world.nameOf trProj Delta type typeV) (htypeType : world.venv.IsType uvars Delta.toCtx typeV) (hbody : PreTrKExprS world.venv uvars world.nameOf trProj @@ -261,7 +261,7 @@ theorem withLctxScope_openLet_pre_wf {Delta : KVLCtx} {methods : Methods .anon} {s : TcState .anon} {name : Mode.anon.F Name} {type value body : KExpr .anon} - {typeV valueV bodyV : Lean4Lean.VExpr} + {typeV valueV bodyV : Ix.Theory.Named.VExpr} (htype : TrKExprS world.venv uvars world.nameOf trProj Delta type typeV) (hvalue : TrKExprS world.venv uvars world.nameOf trProj Delta value valueV) (hvalueType : world.venv.HasType uvars Delta.toCtx valueV typeV) @@ -371,4 +371,4 @@ theorem withLctxScope_openLet_pre_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/ProjectionInferencePolicy.lean b/Ix/Kernel/Verify/Check/ProjectionInferencePolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/ProjectionInferencePolicy.lean rename to Ix/Kernel/Verify/Check/ProjectionInferencePolicy.lean index 268104ae8..2a5896ce5 100644 --- a/Ix/Tc/Verify/Check/ProjectionInferencePolicy.lean +++ b/Ix/Kernel/Verify/Check/ProjectionInferencePolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.UncachedInferencePolicy +import Ix.Kernel.Verify.Check.UncachedInferencePolicy /-! # Operational policy for projection inference @@ -15,7 +15,7 @@ and cache shell, reducing the current inference layer to the current WHNF policy over a policy-framed smaller method table. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -385,4 +385,4 @@ theorem infer_preservesInferOnly_of_whnf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/PublicBlocks.lean b/Ix/Kernel/Verify/Check/PublicBlocks.lean similarity index 80% rename from Ix/Tc/Verify/Check/PublicBlocks.lean rename to Ix/Kernel/Verify/Check/PublicBlocks.lean index 3ba1abfb1..3817f3e11 100644 --- a/Ix/Tc/Verify/Check/PublicBlocks.lean +++ b/Ix/Kernel/Verify/Check/PublicBlocks.lean @@ -1,7 +1,7 @@ -import Ix.Tc.Verify.Check.CheckConstTransaction -import Ix.Tc.Verify.Check.BlockDefinition -import Ix.Tc.Verify.Check.BlockOracle -import Ix.Tc.Verify.Check.QuotientBoundary +import Ix.Kernel.Verify.Check.CheckConstTransaction +import Ix.Kernel.Verify.Check.BlockDefinition +import Ix.Kernel.Verify.Check.BlockOracle +import Ix.Kernel.Verify.Check.QuotientBoundary /-! # Public coordinated-block checker theorem @@ -13,7 +13,7 @@ the semantic disposition is indexed by the public checker's exact final state. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace TcM.checkConst @@ -37,21 +37,21 @@ theorem blockDisposition ExactCheckBlock world block members kind → id ∈ members → RecM.ExactBlockBodySuccessTrace - (Ix.Tc.methodsN (m := .anon) before.recFuel.toNat) + (Ix.Kernel.methodsN (m := .anon) before.recFuel.toNat) block id members kind routed bodyAfter → CertifiedBlockBodySuccess semantics trProj world support - (Ix.Tc.methodsN (m := .anon) before.recFuel.toNat) + (Ix.Kernel.methodsN (m := .anon) before.recFuel.toNat) block id members kind routed bodyAfter) (hrun : TcM.checkConst id before = .ok () after) : CheckConstSuccessDisposition semantics trProj world support - (Ix.Tc.methodsN (m := .anon) before.recFuel.toNat) + (Ix.Kernel.methodsN (m := .anon) before.recFuel.toNat) id before after := by - let methods := Ix.Tc.methodsN (m := .anon) before.recFuel.toNat + let methods := Ix.Kernel.methodsN (m := .anon) before.recFuel.toNat have hbody : (RecM.checkConst id).run methods before = .ok () after := by unfold TcM.checkConst TcM.isolateCheckErrors TcM.runRec at hrun cases hinner : (RecM.checkConst id).run - (Ix.Tc.methodsN (m := .anon) before.recFuel.toNat) before with + (Ix.Kernel.methodsN (m := .anon) before.recFuel.toNat) before with | ok value middle => simp only [hinner] at hrun cases hrun @@ -66,4 +66,4 @@ theorem blockDisposition end TcM.checkConst -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/PublicStandalone.lean b/Ix/Kernel/Verify/Check/PublicStandalone.lean similarity index 91% rename from Ix/Tc/Verify/Check/PublicStandalone.lean rename to Ix/Kernel/Verify/Check/PublicStandalone.lean index c7bd6312c..dae7ce08d 100644 --- a/Ix/Tc/Verify/Check/PublicStandalone.lean +++ b/Ix/Kernel/Verify/Check/PublicStandalone.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.ScopedStandaloneDriver -import Ix.Tc.Verify.RecursiveMethods.Public +import Ix.Kernel.Verify.Check.ScopedStandaloneDriver +import Ix.Kernel.Verify.RecursiveMethods.Public /-! # Public standalone constant checking @@ -16,7 +16,7 @@ definitionally, while standalone definitions may supply a finite routing proof for their concrete block environment. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace TcM.checkConst @@ -29,7 +29,7 @@ theorem rollback_on_error {err : TcError .anon} (hbody : (RecM.checkConst id).run - (Ix.Tc.methodsN (m := .anon) before.recFuel.toNat) before = + (Ix.Kernel.methodsN (m := .anon) before.recFuel.toNat) before = .error err failed) : TcM.checkConst id before = .error err (before.restoreCheckCachesOnError failed) := by @@ -47,7 +47,7 @@ theorem rollback_preserves_kernel {err : TcError .anon} (hbody : (RecM.checkConst id).run - (Ix.Tc.methodsN (m := .anon) before.recFuel.toNat) before = + (Ix.Kernel.methodsN (m := .anon) before.recFuel.toNat) before = .error err failed) (hbefore : KernelStateWF semantics trProj world support before) (hfailedCore : TcStateWF trProj failed world) @@ -74,8 +74,8 @@ theorem wf_legacy (kernelCacheSemantics context.proposition.model.keys trProj) trProj world support context.proposition.model.keys.uvars (context.calls (before.recFuel.toNat + 1)) - (Ix.Tc.methodsN (m := .anon) before.recFuel.toNat)) - {concrete : KConst .anon} {decl : Lean4Lean.VDecl} + (Ix.Kernel.methodsN (m := .anon) before.recFuel.toNat)) + {concrete : KConst .anon} {decl : Ix.Theory.Named.VDecl} (hprojection : trProj.SubstCompatible) (hliterals : ∀ literal, world.venv.ContainsLits literal) (hpending : PendingDecl trProj world id decl) @@ -88,7 +88,7 @@ theorem wf_legacy (WhnfStateInv .noAccel (kernelCacheSemantics context.proposition.model.keys trProj) trProj world support context.proposition.model.keys.uvars []) - (Ix.Tc.methodsN (m := .anon) before.recFuel.toNat) concrete) + (Ix.Kernel.methodsN (m := .anon) before.recFuel.toNat) concrete) (hI : WhnfStateInv .noAccel (kernelCacheSemantics context.proposition.model.keys trProj) trProj world support context.proposition.model.keys.uvars [] before) @@ -105,7 +105,7 @@ theorem wf_legacy (kernelCacheSemantics context.proposition.model.keys trProj) trProj world' support context.proposition.model.keys.uvars [] after ∧ TrustedDecl trProj world' id decl := by - let methods := Ix.Tc.methodsN (m := .anon) before.recFuel.toNat + let methods := Ix.Kernel.methodsN (m := .anon) before.recFuel.toNat have hmethods : Methods.WFAtOn .noAccel (kernelCacheSemantics context.proposition.model.keys trProj) trProj world support context.proposition.model.keys.uvars @@ -123,7 +123,7 @@ theorem wf_legacy unfold TcM.checkConst TcM.isolateCheckErrors TcM.runRec at hrun cases hinner : (RecM.checkConst id).run - (Ix.Tc.methodsN before.recFuel.toNat) before with + (Ix.Kernel.methodsN before.recFuel.toNat) before with | ok value middle => simp only [hinner] at hrun cases hrun @@ -148,8 +148,8 @@ theorem wf requests trProj world support) (pipelines : ScopedStandalonePipelineResources context.model support (context.calls (before.recFuel.toNat + 1)) - (Ix.Tc.methodsN (m := .anon) before.recFuel.toNat)) - {concrete : KConst .anon} {decl : Lean4Lean.VDecl} + (Ix.Kernel.methodsN (m := .anon) before.recFuel.toNat)) + {concrete : KConst .anon} {decl : Ix.Theory.Named.VDecl} (hprojection : trProj.SubstCompatible) (hliterals : ∀ literal, world.venv.ContainsLits literal) (hpending : PendingDecl trProj world id decl) @@ -162,7 +162,7 @@ theorem wf (hroute : StandaloneRoute (ScopedWhnfStateInv context.model .noAccel (kernelCacheSemantics context.model.keys trProj) support []) - (Ix.Tc.methodsN (m := .anon) before.recFuel.toNat) concrete) + (Ix.Kernel.methodsN (m := .anon) before.recFuel.toNat) concrete) (hI : ScopedWhnfStateInv context.model .noAccel (kernelCacheSemantics context.model.keys trProj) support [] before) (hfault : TcM.LazyFaultPreserves @@ -178,7 +178,7 @@ theorem wf support context.model.keys.uvars [] after ∧ context.model.StateInScope after ∧ TrustedDecl trProj world' id decl := by - let methods := Ix.Tc.methodsN (m := .anon) before.recFuel.toNat + let methods := Ix.Kernel.methodsN (m := .anon) before.recFuel.toNat have hmethods : Methods.ScopedWFAtOn context.model .noAccel (kernelCacheSemantics context.model.keys trProj) support (context.calls (before.recFuel.toNat + 1)) (Methods.next methods) := by @@ -195,7 +195,7 @@ theorem wf unfold TcM.checkConst TcM.isolateCheckErrors TcM.runRec at hrun cases hinner : (RecM.checkConst id).run - (Ix.Tc.methodsN before.recFuel.toNat) before with + (Ix.Kernel.methodsN before.recFuel.toNat) before with | ok value middle => simp only [hinner] at hrun cases hrun @@ -222,8 +222,8 @@ theorem rejected_of_no_decl_wf (kernelCacheSemantics context.proposition.model.keys trProj) trProj world support context.proposition.model.keys.uvars (context.calls (before.recFuel.toNat + 1)) - (Ix.Tc.methodsN (m := .anon) before.recFuel.toNat)) - {concrete : KConst .anon} {decl : Lean4Lean.VDecl} + (Ix.Kernel.methodsN (m := .anon) before.recFuel.toNat)) + {concrete : KConst .anon} {decl : Ix.Theory.Named.VDecl} (hprojection : trProj.SubstCompatible) (hliterals : ∀ literal, world.venv.ContainsLits literal) (hpending : PendingDecl trProj world id decl) @@ -236,7 +236,7 @@ theorem rejected_of_no_decl_wf (WhnfStateInv .noAccel (kernelCacheSemantics context.proposition.model.keys trProj) trProj world support context.proposition.model.keys.uvars []) - (Ix.Tc.methodsN (m := .anon) before.recFuel.toNat) concrete) + (Ix.Kernel.methodsN (m := .anon) before.recFuel.toNat) concrete) (hI : WhnfStateInv .noAccel (kernelCacheSemantics context.proposition.model.keys trProj) trProj world support context.proposition.model.keys.uvars [] before) @@ -244,7 +244,7 @@ theorem rejected_of_no_decl_wf (WhnfStateInv .noAccel (kernelCacheSemantics context.proposition.model.keys trProj) trProj world support context.proposition.model.keys.uvars [])) - (hnotWF : ¬∃ world', Lean4Lean.VDecl.WF world.venv decl world') : + (hnotWF : ¬∃ world', Ix.Theory.Named.VDecl.WF world.venv decl world') : ∃ err failed, TcM.checkConst id before = .error err failed ∧ PendingDecl trProj world id decl := by @@ -272,10 +272,10 @@ theorem axiom_pending_sound (kernelCacheSemantics context.proposition.model.keys trProj) trProj world support context.proposition.model.keys.uvars (context.calls (before.recFuel.toNat + 1)) - (Ix.Tc.methodsN (m := .anon) before.recFuel.toNat)) + (Ix.Kernel.methodsN (m := .anon) before.recFuel.toNat)) {name : Mode.anon.F Name} {levelParams : Mode.anon.F (Array Name)} {isUnsafe : Bool} - {levels : UInt64} {type : KExpr .anon} {decl : Lean4Lean.VDecl} + {levels : UInt64} {type : KExpr .anon} {decl : Ix.Theory.Named.VDecl} (hprojection : trProj.SubstCompatible) (hliterals : ∀ literal, world.venv.ContainsLits literal) (hpending : PendingDecl trProj world id decl) @@ -313,4 +313,4 @@ theorem axiom_pending_sound end TcM.checkConst -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/QuotientAdmission.lean b/Ix/Kernel/Verify/Check/QuotientAdmission.lean similarity index 88% rename from Ix/Tc/Verify/Check/QuotientAdmission.lean rename to Ix/Kernel/Verify/Check/QuotientAdmission.lean index d018c0c17..54c609877 100644 --- a/Ix/Tc/Verify/Check/QuotientAdmission.lean +++ b/Ix/Kernel/Verify/Check/QuotientAdmission.lean @@ -1,12 +1,12 @@ -import Ix.Tc.Verify.Env -import Ix.Tc.Primitive -import Lean4Lean.Theory.Typing.QuotLemmas +import Ix.Kernel.Verify.Env +import Ix.Kernel.Primitive +import Ix.Theory.Named.Typing.QuotLemmas /-! # Atomic quotient admission `KConst.quot` declarations are physically standalone entries, but their -semantic meaning is not four independent axioms. Lean4Lean installs `Quot`, +semantic meaning is not four independent axioms. Ix.Theory.Named installs `Quot`, `Quot.mk`, `Quot.lift`, and `Quot.ind` in order and then registers one quotient definitional equation. This file records the corresponding address-keyed Ix boundary without granting a successful check of any one member authority over @@ -16,14 +16,14 @@ The relation deliberately retains each intermediate `VEnv`: later primitive types mention earlier primitives, so translating all four against the initial environment would be false. `QuotientBridge.lean` constructs this relation from four exact `checkQuot` successes, scoped collision freedom, and one -explicit proposition carrying the canonical Lean4Lean semantic transaction. +explicit proposition carrying the canonical Ix.Theory.Named semantic transaction. The theorems below then close the atomic transition without granting authority to any prefix. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VDecl VEnv VConstant) +open Ix.Theory.Named (VDecl VEnv VConstant) /-- One address-keyed quotient insertion in the exact environment where its type is interpreted. The final conjunct prevents a prefix from masquerading @@ -76,7 +76,7 @@ theorem le end QuotientAdmissionStep -/-- The complete address-keyed analogue of Lean4Lean's four-step +/-- The complete address-keyed analogue of Ix.Theory.Named's four-step `AddQuot1` chain. The final equality installs the quotient defeq only after all four exact constants have been added. -/ def QuotientBundleAdmission @@ -84,14 +84,14 @@ def QuotientBundleAdmission (trProj : RawProjRel) (prims : Primitives .anon) (before after : VEnv) : Prop := QuotientAdmissionStep catalog nameOf trProj prims.quotType ``Quot - .type Lean4Lean.quotConst (before := before) fun env₁ => + .type Ix.Theory.Named.quotConst (before := before) fun env₁ => QuotientAdmissionStep catalog nameOf trProj prims.quotCtor ``Quot.mk - .ctor Lean4Lean.quotMkConst (before := env₁) fun env₂ => + .ctor Ix.Theory.Named.quotMkConst (before := env₁) fun env₂ => QuotientAdmissionStep catalog nameOf trProj prims.quotLift ``Quot.lift - .lift Lean4Lean.quotLiftConst (before := env₂) fun env₃ => + .lift Ix.Theory.Named.quotLiftConst (before := env₂) fun env₃ => QuotientAdmissionStep catalog nameOf trProj prims.quotInd ``Quot.ind - .ind Lean4Lean.quotIndConst (before := env₃) fun env₄ => - env₄.addDefEq Lean4Lean.quotDefEq = after + .ind Ix.Theory.Named.quotIndConst (before := env₃) fun env₄ => + env₄.addDefEq Ix.Theory.Named.quotDefEq = after /-- The complete semantic acceptance input: the pre-environment already has the canonical `Eq`, and the four-member Ix bundle follows the exact atomic @@ -149,7 +149,7 @@ theorem nameAssignments obtain ⟨_, _, _, _, hind, _, _, _, h₄⟩ := h₃ exact ⟨htype, hctor, hlift, hind⟩ -/-- A complete Ix bundle witness executes Lean4Lean's production-order +/-- A complete Ix bundle witness executes Ix.Theory.Named's production-order `addQuot` operation exactly. -/ theorem toAddQuot {catalog : Catalog} {nameOf : Address → Option Lean.Name} @@ -195,7 +195,7 @@ theorem quotType {trProj : RawProjRel} {prims : Primitives .anon} {before after : VEnv} (h : QuotientBundleAdmission catalog nameOf trProj prims before after) : - after.constants ``Quot = some Lean4Lean.quotConst := + after.constants ``Quot = some Ix.Theory.Named.quotConst := VEnv.addQuot_quot h.toAddQuot /-- The completed bundle installs the exact quotient constructor. -/ @@ -204,7 +204,7 @@ theorem quotCtor {trProj : RawProjRel} {prims : Primitives .anon} {before after : VEnv} (h : QuotientBundleAdmission catalog nameOf trProj prims before after) : - after.constants ``Quot.mk = some Lean4Lean.quotMkConst := + after.constants ``Quot.mk = some Ix.Theory.Named.quotMkConst := VEnv.addQuot_quotMk h.toAddQuot /-- The completed bundle installs the exact computational eliminator. -/ @@ -213,7 +213,7 @@ theorem quotLift {trProj : RawProjRel} {prims : Primitives .anon} {before after : VEnv} (h : QuotientBundleAdmission catalog nameOf trProj prims before after) : - after.constants ``Quot.lift = some Lean4Lean.quotLiftConst := + after.constants ``Quot.lift = some Ix.Theory.Named.quotLiftConst := VEnv.addQuot_quotLift h.toAddQuot /-- The completed bundle installs the exact propositional eliminator. -/ @@ -222,7 +222,7 @@ theorem quotInd {trProj : RawProjRel} {prims : Primitives .anon} {before after : VEnv} (h : QuotientBundleAdmission catalog nameOf trProj prims before after) : - after.constants ``Quot.ind = some Lean4Lean.quotIndConst := + after.constants ``Quot.ind = some Ix.Theory.Named.quotIndConst := VEnv.addQuot_quotInd h.toAddQuot /-- The quotient reduction equation is available only after the entire @@ -232,14 +232,14 @@ theorem quotientDefEq {trProj : RawProjRel} {prims : Primitives .anon} {before after : VEnv} (h : QuotientBundleAdmission catalog nameOf trProj prims before after) : - after.defeqs Lean4Lean.quotDefEq := + after.defeqs Ix.Theory.Named.quotDefEq := VEnv.addQuot_defeq h.toAddQuot end QuotientBundleAdmission namespace QuotientAdmission -/-- The complete Ix-side witness constructs one Lean4Lean quotient +/-- The complete Ix-side witness constructs one Ix.Theory.Named quotient declaration transition; no member can be promoted separately. -/ theorem wf {catalog : Catalog} {nameOf : Address → Option Lean.Name} @@ -260,4 +260,4 @@ theorem le end QuotientAdmission -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/QuotientBoundary.lean b/Ix/Kernel/Verify/Check/QuotientBoundary.lean similarity index 95% rename from Ix/Tc/Verify/Check/QuotientBoundary.lean rename to Ix/Kernel/Verify/Check/QuotientBoundary.lean index 05206d38d..1ea1fb437 100644 --- a/Ix/Tc/Verify/Check/QuotientBoundary.lean +++ b/Ix/Kernel/Verify/Check/QuotientBoundary.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Check.BlockClassification -import Ix.Tc.Verify.Check.QuotientBridge -import Ix.Tc.Verify.Check.StandaloneDriver +import Ix.Kernel.Verify.Check.BlockClassification +import Ix.Kernel.Verify.Check.QuotientBridge +import Ix.Kernel.Verify.Check.StandaloneDriver /-! # Quotient checking is not a coordinated-block transaction @@ -15,7 +15,7 @@ Quotient acceptance therefore uses the separate four-check atomic bridge; it never acquires inductive-oracle or block-cache authority through E0. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -103,4 +103,4 @@ theorem quotientRoute end StandaloneRoute -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/QuotientBridge.lean b/Ix/Kernel/Verify/Check/QuotientBridge.lean similarity index 94% rename from Ix/Tc/Verify/Check/QuotientBridge.lean rename to Ix/Kernel/Verify/Check/QuotientBridge.lean index a07efa63d..8f32fef69 100644 --- a/Ix/Tc/Verify/Check/QuotientBridge.lean +++ b/Ix/Kernel/Verify/Check/QuotientBridge.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Check.QuotientAdmission -import Ix.Tc.Verify.Whnf.RuntimeContracts -import Ix.Tc.Check +import Ix.Kernel.Verify.Check.QuotientAdmission +import Ix.Kernel.Verify.Whnf.RuntimeContracts +import Ix.Kernel.Check /-! # Production quotient bridge @@ -13,9 +13,9 @@ collision hypothesis, and commits a completed `QuotientAdmission` as one trusted-log event. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VEnv) +open Ix.Theory.Named (VEnv) namespace RecM @@ -275,14 +275,14 @@ theorem quotIndLevels_eq end CheckedQuotientBundle -/-! ## Lean4Lean semantic transaction input -/ +/-! ## Ix.Theory.Named semantic transaction input -/ /-- Canonical semantic proposition for the transaction supplied by -Lean4Lean's quotient-environment proof. This is deliberately independent of +Ix.Theory.Named's quotient-environment proof. This is deliberately independent of the physical catalog: the production runs above establish that the four catalog types are these canonical expressions. -Until Lean4Lean's `addQuot.WF` checker-closure theorem is constructive, callers +Until Ix.Theory.Named's `addQuot.WF` checker-closure theorem is constructive, callers may carry this proposition as the narrow temporary assumption. Making it a `Prop` prevents the boundary from supplying executable data. The Ix bridge itself introduces no axiom and exposes every premise that the future upstream @@ -296,39 +296,39 @@ inductive CanonicalQuotientSemanticTransaction (quotTypeName : nameOf prims.quotType.addr = some ``Quot) (quotTypeTranslated : TrKConstant .safe before nameOf trProj (.quot () () .type 1 (RecM.canonicalQuotType prims .type)) - Lean4Lean.quotConst) + Ix.Theory.Named.quotConst) (quotTypeRaw : RawExprRel (uvars := 1) before nameOf trProj [] - (RecM.canonicalQuotType prims .type) Lean4Lean.quotConst.type) - (addQuotType : before.addConst ``Quot Lean4Lean.quotConst = some env₁) + (RecM.canonicalQuotType prims .type) Ix.Theory.Named.quotConst.type) + (addQuotType : before.addConst ``Quot Ix.Theory.Named.quotConst = some env₁) (env₂ : VEnv) (quotCtorName : nameOf prims.quotCtor.addr = some ``Quot.mk) (quotCtorTranslated : TrKConstant .safe env₁ nameOf trProj (.quot () () .ctor 1 (RecM.canonicalQuotType prims .ctor)) - Lean4Lean.quotMkConst) + Ix.Theory.Named.quotMkConst) (quotCtorRaw : RawExprRel (uvars := 1) env₁ nameOf trProj [] - (RecM.canonicalQuotType prims .ctor) Lean4Lean.quotMkConst.type) - (addQuotCtor : env₁.addConst ``Quot.mk Lean4Lean.quotMkConst = some env₂) + (RecM.canonicalQuotType prims .ctor) Ix.Theory.Named.quotMkConst.type) + (addQuotCtor : env₁.addConst ``Quot.mk Ix.Theory.Named.quotMkConst = some env₂) (env₃ : VEnv) (quotLiftName : nameOf prims.quotLift.addr = some ``Quot.lift) (quotLiftTranslated : TrKConstant .safe env₂ nameOf trProj (.quot () () .lift 2 (RecM.canonicalQuotType prims .lift)) - Lean4Lean.quotLiftConst) + Ix.Theory.Named.quotLiftConst) (quotLiftRaw : RawExprRel (uvars := 2) env₂ nameOf trProj [] - (RecM.canonicalQuotType prims .lift) Lean4Lean.quotLiftConst.type) - (addQuotLift : env₂.addConst ``Quot.lift Lean4Lean.quotLiftConst = some env₃) + (RecM.canonicalQuotType prims .lift) Ix.Theory.Named.quotLiftConst.type) + (addQuotLift : env₂.addConst ``Quot.lift Ix.Theory.Named.quotLiftConst = some env₃) (env₄ : VEnv) (quotIndName : nameOf prims.quotInd.addr = some ``Quot.ind) (quotIndTranslated : TrKConstant .safe env₃ nameOf trProj (.quot () () .ind 1 (RecM.canonicalQuotType prims .ind)) - Lean4Lean.quotIndConst) + Ix.Theory.Named.quotIndConst) (quotIndRaw : RawExprRel (uvars := 1) env₃ nameOf trProj [] - (RecM.canonicalQuotType prims .ind) Lean4Lean.quotIndConst.type) - (addQuotInd : env₃.addConst ``Quot.ind Lean4Lean.quotIndConst = some env₄) - (final : env₄.addDefEq Lean4Lean.quotDefEq = after) + (RecM.canonicalQuotType prims .ind) Ix.Theory.Named.quotIndConst.type) + (addQuotInd : env₃.addConst ``Quot.ind Ix.Theory.Named.quotIndConst = some env₄) + (final : env₄.addDefEq Ix.Theory.Named.quotDefEq = after) namespace CheckedQuotientBundle -/-- Combine four successful physical checks with the canonical Lean4Lean +/-- Combine four successful physical checks with the canonical Ix.Theory.Named transaction. Production supplies exact roles, arities, and collision-safe types; the semantic input supplies the ordered `addQuot` meaning. -/ theorem toAdmission @@ -385,7 +385,7 @@ inductive QuotientMembers (prims : Primitives .anon) : KId .anon → Prop namespace QuotientAdmission -/-- Replaying the single Lean4Lean quotient declaration after a well-formed +/-- Replaying the single Ix.Theory.Named quotient declaration after a well-formed prefix produces a well-formed post-environment. -/ theorem afterWF {catalog : Catalog} {nameOf : Address → Option Lean.Name} @@ -582,7 +582,7 @@ theorem admittedStateWF intern := hstate.intern } /-- End-to-end quotient bridge: a coherent concrete/ghost state, four exact -production runs, scoped collision freedom, freshness, and the Lean4Lean +production runs, scoped collision freedom, freshness, and the Ix.Theory.Named semantic transaction yield one exact four-member promotion and one trusted-log event while preserving the checker-state invariant. -/ theorem admitAtomically @@ -607,4 +607,4 @@ theorem admitAtomically end CheckedQuotientBundle -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/RecursiveMethodPolicy.lean b/Ix/Kernel/Verify/Check/RecursiveMethodPolicy.lean similarity index 97% rename from Ix/Tc/Verify/Check/RecursiveMethodPolicy.lean rename to Ix/Kernel/Verify/Check/RecursiveMethodPolicy.lean index 035fec191..573ed87c4 100644 --- a/Ix/Tc/Verify/Check/RecursiveMethodPolicy.lean +++ b/Ix/Kernel/Verify/Check/RecursiveMethodPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.DefEqCachePolicy +import Ix.Kernel.Verify.Check.DefEqCachePolicy /-! # Operational closure of the recursive method table @@ -9,7 +9,7 @@ with the same six-field frame. This is the non-circular one-layer theorem needed to close every finite `methodsN` approximation. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace Methods @@ -78,4 +78,4 @@ theorem methodsN_concrete_preservesInferOnly (depth : Nat) : end Methods -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/ResetFrame.lean b/Ix/Kernel/Verify/Check/ResetFrame.lean similarity index 97% rename from Ix/Tc/Verify/Check/ResetFrame.lean rename to Ix/Kernel/Verify/Check/ResetFrame.lean index 7cd4e1bec..94f777c42 100644 --- a/Ix/Tc/Verify/Check/ResetFrame.lean +++ b/Ix/Kernel/Verify/Check/ResetFrame.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.MemberEvidence +import Ix.Kernel.Verify.Check.MemberEvidence /-! # Per-constant reset framing @@ -9,7 +9,7 @@ local-context invariant while retaining the stable kernel/cache state needed by the fixed method table. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace TcM @@ -99,4 +99,4 @@ theorem reset_whnf_entry end TcM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/SafetyFrame.lean b/Ix/Kernel/Verify/Check/SafetyFrame.lean similarity index 98% rename from Ix/Tc/Verify/Check/SafetyFrame.lean rename to Ix/Kernel/Verify/Check/SafetyFrame.lean index a142a95f2..816416774 100644 --- a/Ix/Tc/Verify/Check/SafetyFrame.lean +++ b/Ix/Kernel/Verify/Check/SafetyFrame.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.ValidatorFrame +import Ix.Kernel.Verify.Check.ValidatorFrame /-! # State framing for the unsafe-reference traversal @@ -9,7 +9,7 @@ state only through optional constant lookup. Consequently every outcome preserves any invariant framed by the installed lazy-ingress hook. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -139,4 +139,4 @@ theorem checkNoUnsafeRefs_frame end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/Scoped.lean b/Ix/Kernel/Verify/Check/Scoped.lean similarity index 89% rename from Ix/Tc/Verify/Check/Scoped.lean rename to Ix/Kernel/Verify/Check/Scoped.lean index f40b095bb..9f4ced707 100644 --- a/Ix/Tc/Verify/Check/Scoped.lean +++ b/Ix/Kernel/Verify/Check/Scoped.lean @@ -1,5 +1,7 @@ -import Ix.Tc.Verify.Level -import Ix.Tc.Verify.Totalization +import Ix.Kernel.Verify.Level +import Ix.Kernel.Verify.Totalization + +open Ix.Theory (VLevel) /-! # Successful well-scopedness validation @@ -15,7 +17,7 @@ The operational proof below it is kept separate from typing; in particular, being admissible. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace KUniv @@ -34,11 +36,11 @@ theorem scoped_iff_toVLevel_wf {u : KUniv m} {bound : Nat} : u.Scoped bound ↔ u.toVLevel.WF bound := by induction u with | zero => rfl - | succ u _ ih => simpa [Scoped, toVLevel, Lean4Lean.VLevel.WF] using ih + | succ u _ ih => simpa [Scoped, toVLevel, Ix.Theory.VLevel.WF] using ih | max a b _ iha ihb => - simp only [Scoped, toVLevel, Lean4Lean.VLevel.WF, iha, ihb] + simp only [Scoped, toVLevel, Ix.Theory.VLevel.WF, iha, ihb] | imax a b _ iha ihb => - simp only [Scoped, toVLevel, Lean4Lean.VLevel.WF, iha, ihb] + simp only [Scoped, toVLevel, Ix.Theory.VLevel.WF, iha, ihb] | param => rfl theorem Scoped.toVLevel_wf {u : KUniv m} {bound : Nat} @@ -74,4 +76,4 @@ def Scoped (depth : UInt64) (levelBound : Nat) : KExpr m → Prop end KExpr -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/ScopedActiveBlock.lean b/Ix/Kernel/Verify/Check/ScopedActiveBlock.lean similarity index 98% rename from Ix/Tc/Verify/Check/ScopedActiveBlock.lean rename to Ix/Kernel/Verify/Check/ScopedActiveBlock.lean index 082219fde..4768295e6 100644 --- a/Ix/Tc/Verify/Check/ScopedActiveBlock.lean +++ b/Ix/Kernel/Verify/Check/ScopedActiveBlock.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Check.BlockTransaction -import Ix.Tc.Verify.Inductive.StructuralCacheSemantics -import Ix.Tc.Verify.RecursiveMethods.ScopedInference +import Ix.Kernel.Verify.Check.BlockTransaction +import Ix.Kernel.Verify.Inductive.StructuralCacheSemantics +import Ix.Kernel.Verify.RecursiveMethods.ScopedInference /-! # Run-scoped recursive methods inside an active coordinated block @@ -18,7 +18,7 @@ WHNF, inference, or DefEq cache entries; that restriction remains enforced by can consume the active-member disjunct. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- The complete K1/K2 state invariant while one exact coordinated block is active, refined by membership in a finite suffix-model state domain. -/ @@ -262,7 +262,7 @@ theorem isDefEq_eq_activeScoped_wf {support : RunSupport} {members : Array (KId .anon)} {Delta : KVLCtx} {state : TcState .anon} {left right : KExpr .anon} - {leftV rightV : Lean4Lean.VExpr} + {leftV rightV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world model.keys.uvars) (same : left = right) (leftTranslation : TrKExprS world.venv model.keys.uvars world.nameOf @@ -296,7 +296,7 @@ theorem isDefEq_eq_activeScoped_wf leftTranslation rightTranslation _ answerTrue intro _ exact ⟨leftV, leftV, leftTranslation, leftTranslation, - Lean4Lean.VEnv.IsDefEqU.refl + Ix.Theory.Named.VEnv.IsDefEqU.refl (theory.exprWF hI.context leftTranslation)⟩ end RecM @@ -384,4 +384,4 @@ theorem methodsOut_activeScopedWFAtOn end Methods -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/ScopedBoundedPipelines.lean b/Ix/Kernel/Verify/Check/ScopedBoundedPipelines.lean similarity index 96% rename from Ix/Tc/Verify/Check/ScopedBoundedPipelines.lean rename to Ix/Kernel/Verify/Check/ScopedBoundedPipelines.lean index ffdb5cf89..b81ed4d73 100644 --- a/Ix/Tc/Verify/Check/ScopedBoundedPipelines.lean +++ b/Ix/Kernel/Verify/Check/ScopedBoundedPipelines.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.BoundedPipelines -import Ix.Tc.Verify.RecursiveMethods.ScopedCallDomains +import Ix.Kernel.Verify.Check.BoundedPipelines +import Ix.Kernel.Verify.RecursiveMethods.ScopedCallDomains /-! # Run-scoped standalone-checker pipelines @@ -9,7 +9,7 @@ every method callback. It deliberately consumes `Methods.ScopedWFAtOn` directly and contains no conversion to the legacy global suffix model. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace Methods @@ -21,7 +21,7 @@ def ScopedFullInferenceWFAtOn (support : RunSupport) (calls : CallDomain) (methods : Methods .anon) : Prop := ∀ {Delta : KVLCtx} {s : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr}, + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr}, calls.infer source → s.inferOnly = false → PreTrKExprS world.venv model.keys.uvars world.nameOf trProj Delta source @@ -48,7 +48,7 @@ theorem ofTypedIngress (kernelCacheSemantics model.keys trProj) support calls methods) (policy : methods.PreservesInferOnly) (upgrade : ∀ {Delta : KVLCtx} {source : KExpr .anon} - {sourceV : Lean4Lean.VExpr}, + {sourceV : Ix.Theory.Named.VExpr}, calls.infer source → PreTrKExprS world.venv model.keys.uvars world.nameOf trProj Delta source sourceV → @@ -104,13 +104,13 @@ structure ScopedStandalonePipelineResources valueInfer : ∀ {value declaredType}, valueSources value declaredType → calls.infer value typeWhnf : ∀ {Delta : KVLCtx} {source : KExpr .anon} - {sourceV : Lean4Lean.VExpr} {inferred : KExpr .anon}, + {sourceV : Ix.Theory.Named.VExpr} {inferred : KExpr .anon}, typeSources source → FullInferPost trProj world support model.keys.uvars Delta source sourceV inferred → calls.AdmitsEnsureSortDirect inferred valueDefEq : ∀ {Delta : KVLCtx} {value declaredType : KExpr .anon} - {valueV : Lean4Lean.VExpr} {inferred : KExpr .anon}, + {valueV : Ix.Theory.Named.VExpr} {inferred : KExpr .anon}, valueSources value declaredType → FullInferPost trProj world support model.keys.uvars Delta value valueV inferred → @@ -176,7 +176,7 @@ private theorem ensureSortDirect_scopedWFAtOn {model : ScopedKernelSuffixModel trProj world} {support : RunSupport} {calls : Methods.CallDomain} {methods : Methods .anon} {Delta : KVLCtx} {s : TcState .anon} - {input : KExpr .anon} {inputV : Lean4Lean.VExpr} + {input : KExpr .anon} {inputV : Ix.Theory.Named.VExpr} (hmethods : Methods.ScopedWFAtOn model .noAccel (kernelCacheSemantics model.keys trProj) support calls (Methods.next methods)) @@ -239,7 +239,7 @@ theorem checkTypePipeline_scoped_sound (Methods.next methods)) (hpolicyMethods : (Methods.next methods).PreservesInferOnly) {Delta : KVLCtx} {s after : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsourceCall : resources.typeSources source) (hsource : PreTrKExprS world.venv model.keys.uvars world.nameOf trProj Delta source sourceV) @@ -330,7 +330,7 @@ theorem checkValuePipeline_scoped_sound (Methods.next methods)) {Delta : KVLCtx} {s after : TcState .anon} {value declaredType : KExpr .anon} - {valueV declaredTypeV : Lean4Lean.VExpr} + {valueV declaredTypeV : Ix.Theory.Named.VExpr} (hvalueCall : resources.valueSources value declaredType) (hvalue : PreTrKExprS world.venv model.keys.uvars world.nameOf trProj Delta value valueV) @@ -407,4 +407,4 @@ theorem checkValuePipeline_scoped_sound end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/ScopedMemberEvidence.lean b/Ix/Kernel/Verify/Check/ScopedMemberEvidence.lean similarity index 98% rename from Ix/Tc/Verify/Check/ScopedMemberEvidence.lean rename to Ix/Kernel/Verify/Check/ScopedMemberEvidence.lean index d30735c47..450878f28 100644 --- a/Ix/Tc/Verify/Check/ScopedMemberEvidence.lean +++ b/Ix/Kernel/Verify/Check/ScopedMemberEvidence.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Check.MemberEvidence -import Ix.Tc.Verify.Check.ScopedBoundedPipelines -import Ix.Tc.Verify.Check.SafetyFrame +import Ix.Kernel.Verify.Check.MemberEvidence +import Ix.Kernel.Verify.Check.ScopedBoundedPipelines +import Ix.Kernel.Verify.Check.SafetyFrame /-! # Run-scoped evidence from standalone member checking @@ -12,7 +12,7 @@ state carries the original model's `StateInScope` witness alongside the rebased checker invariant. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -79,7 +79,7 @@ theorem checkConstMember_axiom_scoped_sound {id : KId .anon} {name : Mode.anon.F Name} {levelParams : Mode.anon.F (Array Name)} {isUnsafe : Bool} {levels : UInt64} {type : KExpr .anon} - {typeV : Lean4Lean.VExpr} + {typeV : Ix.Theory.Named.VExpr} (hresources : StandaloneValidationResources support (.axio name levelParams isUnsafe levels type)) (hsourceCall : context.typeSources type) @@ -144,7 +144,7 @@ theorem checkConstMember_defn_scoped_sound {safety : Ix.DefinitionSafety} {hints : Lean.ReducibilityHints} {levels : UInt64} {type value : KExpr .anon} {leanAll : Mode.anon.F (Array (KId .anon))} {block : KId .anon} - {typeV valueV : Lean4Lean.VExpr} + {typeV valueV : Ix.Theory.Named.VExpr} (hresources : StandaloneValidationResources support (.defn name levelParams kind safety hints levels type value leanAll block)) @@ -319,7 +319,7 @@ theorem checkConstMember_scoped_sound (Methods.next methods)) (hmethodPolicy : (Methods.next methods).PreservesInferOnly) {id : KId .anon} {concrete : KConst .anon} - {decl : Lean4Lean.VDecl} + {decl : Ix.Theory.Named.VDecl} (hingress : PreDeclRel world.venv world.nameOf trProj id concrete decl) (hcovers : context.Covers concrete) (hresources : StandaloneValidationResources support concrete) @@ -376,7 +376,7 @@ theorem checkConstMember_scoped_pending_sound (Methods.next methods)) (hmethodPolicy : (Methods.next methods).PreservesInferOnly) {id : KId .anon} {concrete : KConst .anon} - {decl : Lean4Lean.VDecl} + {decl : Ix.Theory.Named.VDecl} (hprojection : trProj.SubstCompatible) (hliterals : ∀ literal, world.venv.ContainsLits literal) (hpending : PendingDecl trProj world id decl) @@ -416,4 +416,4 @@ theorem checkConstMember_scoped_pending_sound end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/ScopedPositiveFuelAxiom.lean b/Ix/Kernel/Verify/Check/ScopedPositiveFuelAxiom.lean similarity index 96% rename from Ix/Tc/Verify/Check/ScopedPositiveFuelAxiom.lean rename to Ix/Kernel/Verify/Check/ScopedPositiveFuelAxiom.lean index bbc0a1d44..8fd04565e 100644 --- a/Ix/Tc/Verify/Check/ScopedPositiveFuelAxiom.lean +++ b/Ix/Kernel/Verify/Check/ScopedPositiveFuelAxiom.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.PositiveFuelSort -import Ix.Tc.Verify.Check.PublicStandalone +import Ix.Kernel.Verify.Check.PositiveFuelSort +import Ix.Kernel.Verify.Check.PublicStandalone /-! # Executed positive-fuel checker over a finite suffix model @@ -10,7 +10,7 @@ closed eager states; the method schedule admits exactly the one sort inference performed by the checker. -/ -namespace Ix.Tc.PositiveFuelSort.Checker +namespace Ix.Kernel.PositiveFuelSort.Checker open PositiveFuelSort @@ -18,7 +18,7 @@ def targetAddress : Address := ⟨⟨Array.replicate 32 (37 : UInt8)⟩⟩ def targetId : KId .anon := ⟨targetAddress, ()⟩ -def targetName : Lean.Name := `Ix.Tc.Verify.positiveFuelSortAxiom +def targetName : Lean.Name := `Ix.Kernel.Verify.positiveFuelSortAxiom def catalog : Catalog := fun id => if id == targetId then some concreteAxiom else none @@ -39,7 +39,7 @@ def world : VerifyWorld where world.nameOf targetId.addr = some targetName := by simp [world, targetId] -def declaration : Lean4Lean.VDecl := +def declaration : Ix.Theory.Named.VDecl := .axiom { name := targetName, uvars := 0, type := .sort .zero } theorem rawDeclaration : @@ -58,7 +58,7 @@ theorem pending : rw [hsource] at href simp [KExpr.References] at href · intro name hname - change Lean4Lean.VEnv.empty.constants name = none + change Ix.Theory.Named.VEnv.empty.constants name = none rfl def env : KEnv .anon := @@ -134,8 +134,8 @@ def theory : WhnfTheory RawProjRel.none world model.keys.uvars where literalWF := by intro literal hliteral cases literal <;> - simp [world, Lean4Lean.VEnv.ContainsLits, - Lean4Lean.VEnv.contains, Lean4Lean.VEnv.empty] at hliteral + simp [world, Ix.Theory.Named.VEnv.ContainsLits, + Ix.Theory.Named.VEnv.contains, Ix.Theory.Named.VEnv.empty] at hliteral projections := RawProjRel.none_ok world.venv model.keys.uvars theorem trustedReferences : RecM.TrustedReferences world support := by @@ -155,14 +155,14 @@ theorem methodContract (separation : AddressSeparation) : Methods.ScopedWFAtOn model .noAccel (kernelCacheSemantics model.keys RawProjRel.none) support (.singletonInfer source) - (Methods.next (Ix.Tc.methodsN (m := .anon) 1)) := by + (Methods.next (Ix.Kernel.methodsN (m := .anon) 1)) := by simpa [Methods.ScopedSortSchedule.calls] using (schedule separation).nextSelected theorem fullInference (separation : AddressSeparation) : Methods.ScopedFullInferenceWFAtOn model support (.singletonInfer source) - (Methods.next (Ix.Tc.methodsN (m := .anon) 1)) := + (Methods.next (Ix.Kernel.methodsN (m := .anon) 1)) := Methods.ScopedFullInferenceWFAtOn.ofSingletonSort (methodContract separation) (Methods.next_preservesInferOnly _ @@ -170,7 +170,7 @@ theorem fullInference (separation : AddressSeparation) : def pipelines (separation : AddressSeparation) : ScopedStandalonePipelineResources model support - (.singletonInfer source) (Ix.Tc.methodsN (m := .anon) 1) := + (.singletonInfer source) (Ix.Kernel.methodsN (m := .anon) 1) := ScopedStandalonePipelineResources.singletonSortAxiom (fullInference separation) sortResources supported_is_sort @@ -197,7 +197,7 @@ theorem validationResources : /-! ## Exact production execution -/ -def methods : Methods .anon := Ix.Tc.methodsN 1 +def methods : Methods .anon := Ix.Kernel.methodsN 1 theorem initial_loaded : initialState.env.get? targetId = some concreteAxiom := by @@ -487,4 +487,4 @@ theorem checked_and_promoted (separation : AddressSeparation) : hevidence.1.1.rebaseWorld hpromotes.1 hcore, hevidence.1.2, htrusted⟩ -end Ix.Tc.PositiveFuelSort.Checker +end Ix.Kernel.PositiveFuelSort.Checker diff --git a/Ix/Tc/Verify/Check/ScopedPositiveFuelCertificate.lean b/Ix/Kernel/Verify/Check/ScopedPositiveFuelCertificate.lean similarity index 98% rename from Ix/Tc/Verify/Check/ScopedPositiveFuelCertificate.lean rename to Ix/Kernel/Verify/Check/ScopedPositiveFuelCertificate.lean index b9060b27e..bb9f4a65c 100644 --- a/Ix/Tc/Verify/Check/ScopedPositiveFuelCertificate.lean +++ b/Ix/Kernel/Verify/Check/ScopedPositiveFuelCertificate.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.ScopedPositiveFuelAxiom +import Ix.Kernel.Verify.Check.ScopedPositiveFuelAxiom /-! # Execution certificate for the scoped positive-fuel checker @@ -9,7 +9,7 @@ operation; reset, validation, lookup, routing, and cache insertion are certified silent steps. -/ -namespace Ix.Tc.PositiveFuelSort.Checker +namespace Ix.Kernel.PositiveFuelSort.Checker theorem tryGetConst_requests_of_loaded {state : TcState .anon} {id : KId .anon} {constant : KConst .anon} @@ -312,4 +312,4 @@ def publicContext (separation : AddressSeparation) : schedule := by simpa [initialState] using schedule separation -end Ix.Tc.PositiveFuelSort.Checker +end Ix.Kernel.PositiveFuelSort.Checker diff --git a/Ix/Tc/Verify/Check/ScopedStandaloneDriver.lean b/Ix/Kernel/Verify/Check/ScopedStandaloneDriver.lean similarity index 98% rename from Ix/Tc/Verify/Check/ScopedStandaloneDriver.lean rename to Ix/Kernel/Verify/Check/ScopedStandaloneDriver.lean index adbeeccf0..14df5a649 100644 --- a/Ix/Tc/Verify/Check/ScopedStandaloneDriver.lean +++ b/Ix/Kernel/Verify/Check/ScopedStandaloneDriver.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.ScopedMemberEvidence -import Ix.Tc.Verify.Check.StandaloneDriver +import Ix.Kernel.Verify.Check.ScopedMemberEvidence +import Ix.Kernel.Verify.Check.StandaloneDriver /-! # Run-scoped standalone per-constant driver @@ -11,7 +11,7 @@ frame theorem. Its effect on a suffix model's chosen state domain is therefore exposed as a small, explicit contract. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -30,7 +30,7 @@ theorem checkConstMemberFresh_scoped_pending_evidence (Methods.next methods)) (hmethodPolicy : (Methods.next methods).PreservesInferOnly) {id : KId .anon} {concrete : KConst .anon} - {decl : Lean4Lean.VDecl} + {decl : Ix.Theory.Named.VDecl} (hprojection : trProj.SubstCompatible) (hliterals : ∀ literal, world.venv.ContainsLits literal) (hpending : PendingDecl trProj world id decl) @@ -113,7 +113,7 @@ theorem checkConstMemberFresh_scoped_pending_sound (Methods.next methods)) (hmethodPolicy : (Methods.next methods).PreservesInferOnly) {id : KId .anon} {concrete : KConst .anon} - {decl : Lean4Lean.VDecl} + {decl : Ix.Theory.Named.VDecl} (hprojection : trProj.SubstCompatible) (hliterals : ∀ literal, world.venv.ContainsLits literal) (hpending : PendingDecl trProj world id decl) @@ -195,7 +195,7 @@ theorem checkConst_standalone_scoped_pending_sound (Methods.next methods)) (hmethodPolicy : (Methods.next methods).PreservesInferOnly) {id : KId .anon} {concrete : KConst .anon} - {decl : Lean4Lean.VDecl} + {decl : Ix.Theory.Named.VDecl} (hprojection : trProj.SubstCompatible) (hliterals : ∀ literal, world.venv.ContainsLits literal) (hpending : PendingDecl trProj world id decl) @@ -259,4 +259,4 @@ theorem checkConst_standalone_scoped_pending_sound end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/SingletonInductive.lean b/Ix/Kernel/Verify/Check/SingletonInductive.lean similarity index 92% rename from Ix/Tc/Verify/Check/SingletonInductive.lean rename to Ix/Kernel/Verify/Check/SingletonInductive.lean index 3e8f38327..4e0679e3c 100644 --- a/Ix/Tc/Verify/Check/SingletonInductive.lean +++ b/Ix/Kernel/Verify/Check/SingletonInductive.lean @@ -1,7 +1,7 @@ -import Ix.Tc.Verify.Check.BlockOracle -import Ix.Tc.Verify.Inductive.IngressExecution -import Ix.Tc.Verify.Inductive.SingletonIngress -import Ix.Tc.Verify.Inductive.SingletonOracle +import Ix.Kernel.Verify.Check.BlockOracle +import Ix.Kernel.Verify.Inductive.IngressExecution +import Ix.Kernel.Verify.Inductive.SingletonIngress +import Ix.Kernel.Verify.Inductive.SingletonOracle /-! # Certificate-backed singleton family blocks @@ -17,7 +17,7 @@ certifies that block with the enumeration oracle built from its registered generated equations. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace SingletonFamilyCatalogLink @@ -26,7 +26,7 @@ for an E0 inductive-block trace. -/ def blockResources {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} - {source : Lean4Lean.VInductDecl} {theoryAfter : Lean4Lean.VEnv} + {source : Ix.Theory.Named.VInductDecl} {theoryAfter : Ix.Theory.Named.VEnv} {tx : CertifiedGenerationTransaction source world.venv theoryAfter} (link : SingletonFamilyCatalogLink trProj world.catalog world.nameOf world.trusted tx) @@ -49,7 +49,7 @@ one-member production recursor block. -/ def blockResources {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} - {source : Lean4Lean.VInductDecl} {theoryAfter : Lean4Lean.VEnv} + {source : Ix.Theory.Named.VInductDecl} {theoryAfter : Ix.Theory.Named.VEnv} {tx : CertifiedGenerationTransaction source world.venv theoryAfter} {family : SingletonFamilyCatalogLink trProj world.catalog world.nameOf world.trusted tx} @@ -76,7 +76,7 @@ combining E0's exact trace with E2a/E2b's exact catalog link. -/ theorem certifySingletonFamilyBlock {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {methods : Methods .anon} - {source : Lean4Lean.VInductDecl} {theoryAfter : Lean4Lean.VEnv} + {source : Ix.Theory.Named.VInductDecl} {theoryAfter : Ix.Theory.Named.VEnv} {tx : CertifiedGenerationTransaction source world.venv theoryAfter} (link : SingletonFamilyCatalogLink trProj world.catalog world.nameOf world.trusted tx) @@ -95,7 +95,7 @@ combining E0's exact trace with E2a/E2b's generated-rule correspondence. -/ theorem certifySingletonRecursorBlock {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {methods : Methods .anon} - {source : Lean4Lean.VInductDecl} {theoryAfter : Lean4Lean.VEnv} + {source : Ix.Theory.Named.VInductDecl} {theoryAfter : Ix.Theory.Named.VEnv} {tx : CertifiedGenerationTransaction source world.venv theoryAfter} {family : SingletonFamilyCatalogLink trProj world.catalog world.nameOf world.trusted tx} @@ -123,7 +123,7 @@ before the family exists semantically. -/ theorem certifySingletonFamilyBlockPostAdmission {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {methods : Methods .anon} - {source : Lean4Lean.VInductDecl} {theoryAfter : Lean4Lean.VEnv} + {source : Ix.Theory.Named.VInductDecl} {theoryAfter : Ix.Theory.Named.VEnv} {tx : CertifiedGenerationTransaction source world.venv theoryAfter} (link : SingletonFamilyCatalogLink trProj world.catalog world.nameOf world.trusted tx) @@ -144,7 +144,7 @@ theorem certifySingletonFamilyBlockPostAdmission theorem certifySingletonRecursorBlockPostAdmission {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {methods : Methods .anon} - {source : Lean4Lean.VInductDecl} {theoryAfter : Lean4Lean.VEnv} + {source : Ix.Theory.Named.VInductDecl} {theoryAfter : Ix.Theory.Named.VEnv} {tx : CertifiedGenerationTransaction source world.venv theoryAfter} {family : SingletonFamilyCatalogLink trProj world.catalog world.nameOf world.trusted tx} @@ -174,7 +174,7 @@ trace prove that none of the linked addresses was already admitted. -/ theorem certifySingletonFamilyIngressBlock {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {methods : Methods .anon} - {source : Lean4Lean.VInductDecl} {theoryAfter : Lean4Lean.VEnv} + {source : Ix.Theory.Named.VInductDecl} {theoryAfter : Ix.Theory.Named.VEnv} {tx : CertifiedGenerationTransaction source world.venv theoryAfter} {block requested : KId .anon} {before after : TcState .anon} (view : SingletonFamilyIngressView trProj after.env world.nameOf tx) @@ -191,12 +191,12 @@ theorem certifySingletonFamilyIngressBlock /-- Certify an actual successful singleton recursor block from the recursor entry loaded in its production post-state. The preceding family link fixes -the constructor order used by both the Ix rule array and Lean4Lean's +the constructor order used by both the Ix rule array and Ix.Theory.Named's generated equations. -/ theorem certifySingletonRecursorIngressBlock {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {methods : Methods .anon} - {source : Lean4Lean.VInductDecl} {theoryAfter : Lean4Lean.VEnv} + {source : Ix.Theory.Named.VInductDecl} {theoryAfter : Ix.Theory.Named.VEnv} {tx : CertifiedGenerationTransaction source world.venv theoryAfter} {family : SingletonFamilyCatalogLink trProj world.catalog world.nameOf world.trusted tx} @@ -225,7 +225,7 @@ trace, loaded-catalog invariant, trusted log, and E2a transaction. -/ theorem certifySingletonFamilyIngressExecution {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {methods : Methods .anon} - {source : Lean4Lean.VInductDecl} {theoryAfter : Lean4Lean.VEnv} + {source : Ix.Theory.Named.VInductDecl} {theoryAfter : Ix.Theory.Named.VEnv} {tx : CertifiedGenerationTransaction source world.venv theoryAfter} {ixonEnv : Ixon.Env} {blockConstant : Ixon.Constant} {blockAddr : Address} {ingressBefore ingressAfter : AnonEnv} @@ -260,7 +260,7 @@ the supported enumeration shape. -/ theorem certifySingletonRecursorIngressExecution {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {methods : Methods .anon} - {source : Lean4Lean.VInductDecl} {theoryAfter : Lean4Lean.VEnv} + {source : Ix.Theory.Named.VInductDecl} {theoryAfter : Ix.Theory.Named.VEnv} {tx : CertifiedGenerationTransaction source world.venv theoryAfter} {family : SingletonFamilyCatalogLink trProj world.catalog world.nameOf world.trusted tx} @@ -294,4 +294,4 @@ theorem certifySingletonRecursorIngressExecution end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/StandaloneDriver.lean b/Ix/Kernel/Verify/Check/StandaloneDriver.lean similarity index 98% rename from Ix/Tc/Verify/Check/StandaloneDriver.lean rename to Ix/Kernel/Verify/Check/StandaloneDriver.lean index c47ac7633..5e4f707c1 100644 --- a/Ix/Tc/Verify/Check/StandaloneDriver.lean +++ b/Ix/Kernel/Verify/Check/StandaloneDriver.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.ResetFrame +import Ix.Kernel.Verify.Check.ResetFrame /-! # Standalone per-constant driver @@ -10,7 +10,7 @@ catalog agreement proof prevents a successful lazy lookup from silently changing which pending declaration is certified. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Operational boundary separating K3 standalone checking from E0 block coordination. The exact production router must preserve the checker @@ -55,7 +55,7 @@ theorem checkConstMemberFresh_pending_sound model.keys.uvars calls (Methods.next methods)) (hmethodPolicy : (Methods.next methods).PreservesInferOnly) {id : KId .anon} {concrete : KConst .anon} - {decl : Lean4Lean.VDecl} + {decl : Ix.Theory.Named.VDecl} (hprojection : trProj.SubstCompatible) (hliterals : ∀ literal, world.venv.ContainsLits literal) (hpending : PendingDecl trProj world id decl) @@ -136,7 +136,7 @@ theorem checkConstMemberFresh_pending_evidence model.keys.uvars calls (Methods.next methods)) (hmethodPolicy : (Methods.next methods).PreservesInferOnly) {id : KId .anon} {concrete : KConst .anon} - {decl : Lean4Lean.VDecl} + {decl : Ix.Theory.Named.VDecl} (hprojection : trProj.SubstCompatible) (hliterals : ∀ literal, world.venv.ContainsLits literal) (hpending : PendingDecl trProj world id decl) @@ -219,7 +219,7 @@ theorem checkConst_standalone_pending_sound model.keys.uvars calls (Methods.next methods)) (hmethodPolicy : (Methods.next methods).PreservesInferOnly) {id : KId .anon} {concrete : KConst .anon} - {decl : Lean4Lean.VDecl} + {decl : Ix.Theory.Named.VDecl} (hprojection : trProj.SubstCompatible) (hliterals : ∀ literal, world.venv.ContainsLits literal) (hpending : PendingDecl trProj world id decl) @@ -283,4 +283,4 @@ theorem checkConst_standalone_pending_sound end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/UncachedInferencePolicy.lean b/Ix/Kernel/Verify/Check/UncachedInferencePolicy.lean similarity index 98% rename from Ix/Tc/Verify/Check/UncachedInferencePolicy.lean rename to Ix/Kernel/Verify/Check/UncachedInferencePolicy.lean index 047063dab..899d59afc 100644 --- a/Ix/Tc/Verify/Check/UncachedInferencePolicy.lean +++ b/Ix/Kernel/Verify/Check/UncachedInferencePolicy.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.UniverseInstantiationPolicy -import Ix.Tc.Verify.Check.FullInferenceProjections +import Ix.Kernel.Verify.Check.UniverseInstantiationPolicy +import Ix.Kernel.Verify.Check.FullInferenceProjections /-! # Operational policy for uncached inference @@ -16,7 +16,7 @@ last premise needed to feed this theorem into the already verified inference cache shell. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -286,4 +286,4 @@ theorem inferUncached_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/UniverseInstantiationPolicy.lean b/Ix/Kernel/Verify/Check/UniverseInstantiationPolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/UniverseInstantiationPolicy.lean rename to Ix/Kernel/Verify/Check/UniverseInstantiationPolicy.lean index 32b2c7e55..5fde07bc6 100644 --- a/Ix/Tc/Verify/Check/UniverseInstantiationPolicy.lean +++ b/Ix/Kernel/Verify/Check/UniverseInstantiationPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.InferencePolicy +import Ix.Kernel.Verify.Check.InferencePolicy /-! # Inference-policy frame for universe instantiation @@ -15,7 +15,7 @@ constant-universe array loop, recursive child failures, interning, and memo writes on both outcomes. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace TcM.PreservesInferOnly @@ -277,4 +277,4 @@ theorem instantiateUnivParams (e : KExpr .anon) end TcM.PreservesInferOnly -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/ValidationReach.lean b/Ix/Kernel/Verify/Check/ValidationReach.lean similarity index 99% rename from Ix/Tc/Verify/Check/ValidationReach.lean rename to Ix/Kernel/Verify/Check/ValidationReach.lean index 23181e518..956aeddce 100644 --- a/Ix/Tc/Verify/Check/ValidationReach.lean +++ b/Ix/Kernel/Verify/Check/ValidationReach.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.Scoped -import Ix.Tc.Verify.Support +import Ix.Kernel.Verify.Check.Scoped +import Ix.Kernel.Verify.Support /-! # Finite syntax support for well-scopedness validation @@ -15,7 +15,7 @@ connects their finite footprints to an existing `RunSupport`, whose separate `CollisionFree` field is consumed by the operational proof. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace KUniv @@ -404,4 +404,4 @@ end ValidationCoverage end KExpr -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/ValidatorFrame.lean b/Ix/Kernel/Verify/Check/ValidatorFrame.lean similarity index 99% rename from Ix/Tc/Verify/Check/ValidatorFrame.lean rename to Ix/Kernel/Verify/Check/ValidatorFrame.lean index dc4b37e0d..adee2bc3b 100644 --- a/Ix/Tc/Verify/Check/ValidatorFrame.lean +++ b/Ix/Kernel/Verify/Check/ValidatorFrame.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.DeclarationValidation -import Ix.Tc.Verify.Whnf.RuntimeContracts +import Ix.Kernel.Verify.Check.DeclarationValidation +import Ix.Kernel.Verify.Whnf.RuntimeContracts /-! # State framing for the scoping validators @@ -11,7 +11,7 @@ starts. This module proves that every validator outcome preserves an arbitrary state invariant whose installed lazy-fault hook preserves it. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -314,4 +314,4 @@ theorem validateConstWellScoped_frame end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/ValidatorSoundness.lean b/Ix/Kernel/Verify/Check/ValidatorSoundness.lean similarity index 99% rename from Ix/Tc/Verify/Check/ValidatorSoundness.lean rename to Ix/Kernel/Verify/Check/ValidatorSoundness.lean index 9b23fd76e..259f94b5b 100644 --- a/Ix/Tc/Verify/Check/ValidatorSoundness.lean +++ b/Ix/Kernel/Verify/Check/ValidatorSoundness.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.ValidationReach +import Ix.Kernel.Verify.Check.ValidationReach /-! # Soundness of the address-memoized scoping validators @@ -16,7 +16,7 @@ local guards imply full structural scoping. Address equality is converted to syntax equality only through finite-run collision freedom. -/ -namespace Ix.Tc +namespace Ix.Kernel open Std (HashSet) @@ -1220,4 +1220,4 @@ theorem validateExprWellScoped_sound end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/WhnfBasicHelperPolicy.lean b/Ix/Kernel/Verify/Check/WhnfBasicHelperPolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/WhnfBasicHelperPolicy.lean rename to Ix/Kernel/Verify/Check/WhnfBasicHelperPolicy.lean index 75c10e05c..b09951b81 100644 --- a/Ix/Tc/Verify/Check/WhnfBasicHelperPolicy.lean +++ b/Ix/Kernel/Verify/Check/WhnfBasicHelperPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.WhnfReductionPolicy +import Ix.Kernel.Verify.Check.WhnfReductionPolicy /-! # Operational policy for non-recursive WHNF helpers @@ -11,7 +11,7 @@ application-finisher components rather than retained as an independent assumption. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -455,4 +455,4 @@ theorem tryQuotReduce_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/WhnfBitVecPolicy.lean b/Ix/Kernel/Verify/Check/WhnfBitVecPolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/WhnfBitVecPolicy.lean rename to Ix/Kernel/Verify/Check/WhnfBitVecPolicy.lean index c161a1354..19fdef3c7 100644 --- a/Ix/Tc/Verify/Check/WhnfBitVecPolicy.lean +++ b/Ix/Kernel/Verify/Check/WhnfBitVecPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.WhnfNatPolicy +import Ix.Kernel.Verify.Check.WhnfNatPolicy /-! # Operational inference-policy frame for BitVec reduction @@ -9,7 +9,7 @@ pipeline. All recursive WHNF calls, fallback paths, and rebuilt applications restore the caller's inference policy. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -489,4 +489,4 @@ theorem tryReduceBitvec_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/WhnfDecidablePolicy.lean b/Ix/Kernel/Verify/Check/WhnfDecidablePolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/WhnfDecidablePolicy.lean rename to Ix/Kernel/Verify/Check/WhnfDecidablePolicy.lean index c3a07dc68..acb0d2b37 100644 --- a/Ix/Tc/Verify/Check/WhnfDecidablePolicy.lean +++ b/Ix/Kernel/Verify/Check/WhnfDecidablePolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.WhnfBitVecPolicy +import Ix.Kernel.Verify.Check.WhnfBitVecPolicy /-! # Operational inference-policy frame for decidability reduction @@ -9,7 +9,7 @@ recursive type normalization, canonical proof-term interning, application rebuilding, and every accelerator fallback. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -348,4 +348,4 @@ theorem tryReduceDecidable_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/WhnfDriverPolicy.lean b/Ix/Kernel/Verify/Check/WhnfDriverPolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/WhnfDriverPolicy.lean rename to Ix/Kernel/Verify/Check/WhnfDriverPolicy.lean index a82b59ff0..2736c89b0 100644 --- a/Ix/Tc/Verify/Check/WhnfDriverPolicy.lean +++ b/Ix/Kernel/Verify/Check/WhnfDriverPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.ProjectionInferencePolicy +import Ix.Kernel.Verify.Check.ProjectionInferencePolicy /-! # Operational inference-policy frame for WHNF drivers @@ -14,7 +14,7 @@ individual structural, no-delta, and full-WHNF helper seams; this file ensures that no additional policy obligation is hidden in the outer drivers. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -547,4 +547,4 @@ theorem whnf_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/WhnfHelperPolicy.lean b/Ix/Kernel/Verify/Check/WhnfHelperPolicy.lean similarity index 95% rename from Ix/Tc/Verify/Check/WhnfHelperPolicy.lean rename to Ix/Kernel/Verify/Check/WhnfHelperPolicy.lean index 28c3653c2..0a97dcae8 100644 --- a/Ix/Tc/Verify/Check/WhnfHelperPolicy.lean +++ b/Ix/Kernel/Verify/Check/WhnfHelperPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.WhnfIotaDispatchPolicy +import Ix.Kernel.Verify.Check.WhnfIotaDispatchPolicy /-! # Concrete operational policy for every WHNF helper @@ -10,7 +10,7 @@ quotient reduction, and delta unfolding are all tied to their concrete implementations under one fixed predecessor method table. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -41,4 +41,4 @@ def concreteWhnfReductionPolicy end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/WhnfIotaBasePolicy.lean b/Ix/Kernel/Verify/Check/WhnfIotaBasePolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/WhnfIotaBasePolicy.lean rename to Ix/Kernel/Verify/Check/WhnfIotaBasePolicy.lean index 5b085de0c..5d4762dca 100644 --- a/Ix/Tc/Verify/Check/WhnfIotaBasePolicy.lean +++ b/Ix/Kernel/Verify/Check/WhnfIotaBasePolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.WhnfDecidablePolicy +import Ix.Kernel.Verify.Check.WhnfDecidablePolicy /-! # Operational inference-policy frame for iota rule execution @@ -10,7 +10,7 @@ workers are proved together over their shared fuel, so their production fallback behavior remains explicit. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -283,4 +283,4 @@ theorem cleanupNatOffsetMajor_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/WhnfIotaDispatchPolicy.lean b/Ix/Kernel/Verify/Check/WhnfIotaDispatchPolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/WhnfIotaDispatchPolicy.lean rename to Ix/Kernel/Verify/Check/WhnfIotaDispatchPolicy.lean index 989acfbce..a59f178d5 100644 --- a/Ix/Tc/Verify/Check/WhnfIotaDispatchPolicy.lean +++ b/Ix/Kernel/Verify/Check/WhnfIotaDispatchPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.WhnfIotaSynthesisPolicy +import Ix.Kernel.Verify.Check.WhnfIotaSynthesisPolicy /-! # Operational inference-policy frame for complete iota dispatch @@ -10,7 +10,7 @@ major normalization. Its public theorem covers every branch of production the WHNF reducer frame. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -267,4 +267,4 @@ theorem tryIotaWithFlags_preservesInferOnly exact TcM.PreservesInferOnly.pure none end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/WhnfIotaRecursionPolicy.lean b/Ix/Kernel/Verify/Check/WhnfIotaRecursionPolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/WhnfIotaRecursionPolicy.lean rename to Ix/Kernel/Verify/Check/WhnfIotaRecursionPolicy.lean index 15597b74f..4af485247 100644 --- a/Ix/Tc/Verify/Check/WhnfIotaRecursionPolicy.lean +++ b/Ix/Kernel/Verify/Check/WhnfIotaRecursionPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.WhnfIotaScopePolicy +import Ix.Kernel.Verify.Check.WhnfIotaScopePolicy /-! # Operational inference-policy frame for iota recursion classification @@ -9,7 +9,7 @@ bounded field scanning, legacy-context restoration, provisional and final cache writes, and cleanup of the provisional entry on classifier errors. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -309,4 +309,4 @@ theorem computedIsRec_preservesInferOnly exact TcM.PreservesInferOnly.throw _ end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/WhnfIotaScopePolicy.lean b/Ix/Kernel/Verify/Check/WhnfIotaScopePolicy.lean similarity index 98% rename from Ix/Tc/Verify/Check/WhnfIotaScopePolicy.lean rename to Ix/Kernel/Verify/Check/WhnfIotaScopePolicy.lean index d9edfcdb4..acf05e7da 100644 --- a/Ix/Tc/Verify/Check/WhnfIotaScopePolicy.lean +++ b/Ix/Kernel/Verify/Check/WhnfIotaScopePolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.WhnfIotaBasePolicy +import Ix.Kernel.Verify.Check.WhnfIotaBasePolicy /-! # Operational inference-policy frame for scoped iota callbacks @@ -9,7 +9,7 @@ restoration. These are the scoped callback primitives shared by struct eta and K-like constructor synthesis. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace TcM.PreservesInferOnly @@ -186,4 +186,4 @@ theorem getMajorInductiveId_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/WhnfIotaSynthesisPolicy.lean b/Ix/Kernel/Verify/Check/WhnfIotaSynthesisPolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/WhnfIotaSynthesisPolicy.lean rename to Ix/Kernel/Verify/Check/WhnfIotaSynthesisPolicy.lean index 120274ae3..6a5d68ef9 100644 --- a/Ix/Tc/Verify/Check/WhnfIotaSynthesisPolicy.lean +++ b/Ix/Kernel/Verify/Check/WhnfIotaSynthesisPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.WhnfIotaRecursionPolicy +import Ix.Kernel.Verify.Check.WhnfIotaRecursionPolicy /-! # Operational inference-policy frame for struct eta and K synthesis @@ -10,7 +10,7 @@ inference and WHNF probes, universe instantiation, projection rebuilding, DefEq validation, and synthesis statistics on acceptance and rejection. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -351,4 +351,4 @@ theorem synthCtorWhenK_preservesInferOnly exact TcM.PreservesInferOnly.pure KSynthOutcome.inconclusive end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/WhnfNatArgumentPolicy.lean b/Ix/Kernel/Verify/Check/WhnfNatArgumentPolicy.lean similarity index 98% rename from Ix/Tc/Verify/Check/WhnfNatArgumentPolicy.lean rename to Ix/Kernel/Verify/Check/WhnfNatArgumentPolicy.lean index 6727156cd..3e24dd2d1 100644 --- a/Ix/Tc/Verify/Check/WhnfNatArgumentPolicy.lean +++ b/Ix/Kernel/Verify/Check/WhnfNatArgumentPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.WhnfProjectionPolicy +import Ix.Kernel.Verify.Check.WhnfProjectionPolicy /-! # Operational policy for Nat argument normalization and stuck offsets @@ -9,7 +9,7 @@ successful callbacks, caught exhaustion, and propagated errors. It then closes the complete `tryNatOffsetStuck` classifier and rebuild path. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -166,4 +166,4 @@ theorem tryNatOffsetStuck_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/WhnfNatPolicy.lean b/Ix/Kernel/Verify/Check/WhnfNatPolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/WhnfNatPolicy.lean rename to Ix/Kernel/Verify/Check/WhnfNatPolicy.lean index 98ec1977c..1f2de24c9 100644 --- a/Ix/Tc/Verify/Check/WhnfNatPolicy.lean +++ b/Ix/Kernel/Verify/Check/WhnfNatPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.WhnfNativePolicy +import Ix.Kernel.Verify.Check.WhnfNativePolicy /-! # Operational inference-policy frame for native Nat reduction @@ -9,7 +9,7 @@ memo, callback and partial-error paths, local Nat-argument fuel, interning, and application rebuilding. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -456,4 +456,4 @@ theorem tryReduceNatWithSuccMode_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/WhnfNativePolicy.lean b/Ix/Kernel/Verify/Check/WhnfNativePolicy.lean similarity index 98% rename from Ix/Tc/Verify/Check/WhnfNativePolicy.lean rename to Ix/Kernel/Verify/Check/WhnfNativePolicy.lean index 8a15b3e73..f98686d59 100644 --- a/Ix/Tc/Verify/Check/WhnfNativePolicy.lean +++ b/Ix/Kernel/Verify/Check/WhnfNativePolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.WhnfNatArgumentPolicy +import Ix.Kernel.Verify.Check.WhnfNatArgumentPolicy /-! # Operational inference-policy frame for native WHNF reduction @@ -9,7 +9,7 @@ then owns lazy declaration lookup, universe instantiation, the recursive WHNF callback, and restoration of the re-entrancy guard on both outcomes. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -125,4 +125,4 @@ theorem tryReduceNative_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/WhnfProjectionPolicy.lean b/Ix/Kernel/Verify/Check/WhnfProjectionPolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/WhnfProjectionPolicy.lean rename to Ix/Kernel/Verify/Check/WhnfProjectionPolicy.lean index 21b1fb210..2a9d880af 100644 --- a/Ix/Tc/Verify/Check/WhnfProjectionPolicy.lean +++ b/Ix/Kernel/Verify/Check/WhnfProjectionPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.WhnfBasicHelperPolicy +import Ix.Kernel.Verify.Check.WhnfBasicHelperPolicy /-! # Operational policy for WHNF projection reduction @@ -10,7 +10,7 @@ intern operation, lazy constructor lookup, recursive WHNF callback, miss, and partial error is covered. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -269,4 +269,4 @@ theorem tryProjReduce_preservesInferOnly end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Check/WhnfReductionPolicy.lean b/Ix/Kernel/Verify/Check/WhnfReductionPolicy.lean similarity index 99% rename from Ix/Tc/Verify/Check/WhnfReductionPolicy.lean rename to Ix/Kernel/Verify/Check/WhnfReductionPolicy.lean index 5e5696d7d..cfa4705f0 100644 --- a/Ix/Tc/Verify/Check/WhnfReductionPolicy.lean +++ b/Ix/Kernel/Verify/Check/WhnfReductionPolicy.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.WhnfDriverPolicy +import Ix.Kernel.Verify.Check.WhnfDriverPolicy /-! # Operational inference-policy frame for WHNF reduction steps @@ -13,7 +13,7 @@ helper fields are discharged, `reductionPolicy` supplies the complete step contract consumed by `WhnfDriverPolicy`. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -593,4 +593,4 @@ def WhnfHelperPolicyAt.reductionPolicy end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean new file mode 100644 index 000000000..c00dfe950 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency.lean @@ -0,0 +1,19 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Level +import Ix.Kernel.Verify.Consistency.Expr +import Ix.Kernel.Verify.Consistency.Judgment +import Ix.Kernel.Verify.Consistency.Infer +import Ix.Kernel.Verify.Consistency.Audit + +/-! +# Refinement into the set-theoretic consistency model + +This library connects production kernel operations to `Ix.Theory`. Each +transport keeps its representation, arithmetic, and dependency assumptions +explicit. The component theorems are distinct from a full soundness theorem +for `checkEnvAnon`. +-/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean new file mode 100644 index 000000000..9fc86756d --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -0,0 +1,51 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Infer +import Ix.Kernel.Verify.Audit.Basic + +/-! Exact full-dependency boundaries for the direct model-refinement roots. +No root permits proof holes, implementation bridge axioms, or pending +metatheory axioms. Native assumptions are enumerated only where a production +smart constructor or dispatcher still contains a generated native proof. -/ + +namespace Ix.Kernel.Consistency.Audit + +open Kernel.Verify.Audit + +private def standard : Array Lean.Name := #[``propext, ``Classical.choice, ``Quot.sound] +private def levelNative : Lean.Name := + nativeAxiom `Ix.Kernel.Level `Ix.Kernel.KUniv.mkSucc._native.native_decide.ax_1 +private def expressionNative : Lean.Name := + nativeAxiom `Ix.Kernel.Expr `Ix.Kernel.KExpr.mkVar._native.native_decide.ax_1 + +def roots : Array RootAllowance := #[ + { root := ``readLevel_eq, standardAxioms := #[``propext] }, + { root := ``readLevel_eval, standardAxioms := #[``propext] }, + { root := ``readLevel_wf, standardAxioms := #[``propext] }, + { root := ``readLevel_eraseMeta, standardAxioms := #[``propext] }, + { root := ``readLevel_mkSucc, standardAxioms := #[``propext, ``Classical.choice], + nativeAxioms := #[levelNative] }, + { root := ``univEq_sound, standardAxioms := standard }, + { root := ``univGeq_sound, standardAxioms := standard }, + { root := ``readExpr?_mkSort, standardAxioms := standard, + nativeAxioms := #[expressionNative] }, + { root := ``readExpr?_eraseMeta, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``beq_readExpr?, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``internExpr_readExpr?, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``ModelTyping.sort, standardAxioms := standard, + nativeAxioms := #[expressionNative, levelNative] }, + { root := ``ModelTyping.of_beq, standardAxioms := standard }, + { root := ``ModelTyping.internExpr, standardAxioms := standard }, + { root := ``ModelTyping.internType, standardAxioms := standard }, + { root := ``ModelTyping.no_false, standardAxioms := standard }, + { root := ``sort_conversion, standardAxioms := standard }, + { root := ``inferUncached_sort_sound, standardAxioms := standard, + nativeAxioms := #[expressionNative, levelNative] } +] + +run_cmd Kernel.Verify.Audit.check roots + +end Ix.Kernel.Consistency.Audit diff --git a/Ix/Kernel/Verify/Consistency/Expr.lean b/Ix/Kernel/Verify/Consistency/Expr.lean new file mode 100644 index 000000000..2166660f7 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Expr.lean @@ -0,0 +1,88 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Level +import Ix.Kernel.Verify.Expr +import Ix.Theory.Expr + +/-! +# Reading kernel expressions into the consistency model + +References are resolved to explicit block/member/constructor coordinates. +The reader uses the expression tree, not cached hashes or scope annotations. +It preserves projections and natural literals, and expands a let by +capture-avoiding substitution. Free variables, unresolved references, and +string literals have no reading in this initial fragment. + +The theorems below connect the production hash-equality fast path and intern +table to this reading. Their finite-support collision hypotheses are retained; +no global hash-injectivity assumption is introduced. +-/ + +namespace Ix.Kernel.Consistency + +universe u +variable {β : Type u} {m : Mode} + +/-- A structural reading into the new theory. The reference resolver will be +supplied by the declaration/store refinement; an unknown address fails. -/ +def readExpr? (resolve : Address → Option (Theory.ConstRef β)) : + KExpr m → Option (Theory.VExpr β) + | .var index _ _ => some (.bvar index.toNat) + | .fvar .. => none + | .sort level _ => some (.sort (readLevel level)) + | .const id levels _ => do + let ref ← resolve id.addr + return .const ref (levels.toList.map readLevel) + | .app fn arg _ => do + return .app (← readExpr? resolve fn) (← readExpr? resolve arg) + | .lam _ _ domain body _ => do + return .lam (← readExpr? resolve domain) (← readExpr? resolve body) + | .all _ _ domain body _ => do + return .forallE (← readExpr? resolve domain) (← readExpr? resolve body) + | .letE _ domain value body _ _ => do + let _ ← readExpr? resolve domain + let value ← readExpr? resolve value + let body ← readExpr? resolve body + return body.inst value + | .prj id index value _ => do + let ref ← resolve id.addr + return .proj ref index.toNat (← readExpr? resolve value) + | .nat value _ _ => some (.natLit value) + | .str .. => none + +/-- Hashing and display metadata do not change a smart-constructed sort. -/ +@[simp] theorem readExpr?_mkSort + (resolve : Address → Option (Theory.ConstRef β)) (u : KUniv m) : + readExpr? resolve (KExpr.mkSort u) = some (.sort (readLevel u)) := rfl + +/-- Kernel metadata erasure preserves the exact model syntax. -/ +@[simp] theorem readExpr?_eraseMeta + (resolve : Address → Option (Theory.ConstRef β)) (e : KExpr m) : + readExpr? resolve e.eraseMeta = readExpr? resolve e := by + induction e <;> + simp_all [readExpr?, KExpr.eraseMeta, KId.eraseMeta, + Array.toList_map, List.map_map, Function.comp_def] + +/-- The production `BEq` fast path preserves the model reading when the +specific compared expressions have faithful addresses. -/ +theorem beq_readExpr? {resolve : Address → Option (Theory.ConstRef β)} + {left right : KExpr m} (faithful : left.AddrFaithful right) + (equal : (left == right) = true) : + readExpr? resolve left = readExpr? resolve right := by + have erased := faithful (KExpr.beq_def left right ▸ equal) + simpa only [readExpr?_eraseMeta] using + congrArg (readExpr? resolve) erased + +/-- The production intern table preserves the model reading of a candidate. +Collision-freedom is required only on the table support plus that candidate. -/ +theorem internExpr_readExpr? {resolve : Address → Option (Theory.ConstRef β)} + {table : InternTable m} {e : KExpr m} (coherent : table.WF) + (faithful : KExpr.KeyCollisionFree fun v => table.ExprSupport v ∨ v = e) : + readExpr? resolve (table.internExpr e).1 = readExpr? resolve e := by + simpa only [readExpr?_eraseMeta] using + congrArg (readExpr? resolve) (table.internExpr_eraseMeta coherent faithful) + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Infer.lean b/Ix/Kernel/Verify/Consistency/Infer.lean new file mode 100644 index 000000000..7c336a005 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Infer.lean @@ -0,0 +1,47 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Infer +import Ix.Kernel.Verify.Consistency.Judgment + +/-! +# Production sort inference in the set-theory model + +This theorem examines the actual uncached inference dispatcher and its intern +table update. The intern-table premises are its concrete key coherence and +collision freedom on the table support plus the newly constructed type. +No named-calculus typing or checker-soundness assumption is used. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v +variable {β : Type u} {m : Mode} + +/-- Every successful execution of the production sort-inference branch +establishes the model typing postcondition for its actual returned type. -/ +theorem inferUncached_sort_sound + {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} + {inferRec : KExpr m → RecM m (KExpr m)} {inferOnly : Bool} + {methods : Methods m} {before after : TcState m} + {level : KUniv m} {info : ExprInfo m} {type : KExpr m} + (coherent : before.env.intern.WF) + (faithful : KExpr.KeyCollisionFree fun e => + before.env.intern.ExprSupport e ∨ e = KExpr.mkSort (KUniv.mkSucc level)) + (accepted : RecM.inferUncached inferRec inferOnly (.sort level info) methods before = + .ok type after) : + ModelTyping.{u,v} resolve entries context (.sort level info) type := by + change EStateM.Result.ok + (before.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).1 + { before with env := { before.env with intern := + (before.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).2 } } = + .ok type after at accepted + cases accepted + exact ModelTyping.internType coherent faithful (ModelTyping.sort level info) + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Judgment.lean b/Ix/Kernel/Verify/Consistency/Judgment.lean new file mode 100644 index 000000000..39def4633 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Judgment.lean @@ -0,0 +1,118 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Expr +import Ix.Theory.Certified.Accept + +/-! +# Semantic postcondition for kernel typing + +`ModelTyping` states the postcondition that the production inference and +conversion proofs must establish in the new model. It ties both annotated +endpoints to the actual `KExpr` trees. It is not defined by checker success, +and this module does not assume that checker success establishes it. + +The current producers transport existing evidence through hash equality and +interning. The consistency theorem constructs a dependency model from an +admitted environment and rules out this postcondition at the primitive False. +The remaining checker refinement must produce the postcondition for each +successful inference/declaration path. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model Theory.Model.SetTheory + +universe u v +variable {β : Type u} {m : Mode} + +/-- An annotated typing judgment whose two erasures read the exact kernel +term and type. This is a semantic specification, not a runtime acceptance bit. -/ +def ModelTyping (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (context : Model.Context β) + (term type : KExpr m) : Prop := + ∃ e A : AExpr β, + readExpr? resolve term = some e.erase ∧ + readExpr? resolve type = some A.erase ∧ + TypingClaim.{u,v} entries context e A + +namespace ModelTyping + +/-- The sort rule applies to the exact source and production successor type. -/ +theorem sort {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} + (level : KUniv m) (info : ExprInfo m) : + ModelTyping.{u,v} resolve entries context (.sort level info) + (KExpr.mkSort (KUniv.mkSucc level)) := by + refine ⟨.sort (readLevel level), .sort (.succ (readLevel level)), rfl, ?_, + TypingClaim.sort _⟩ + simp only [readExpr?_mkSort, readLevel_mkSucc, AExpr.erase] + +/-- Reusing a hash-equal term preserves the semantic typing postcondition +under the comparison's existing address-faithfulness hypothesis. -/ +theorem of_beq {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} + {left right type : KExpr m} (faithful : left.AddrFaithful right) + (equal : (left == right) = true) + (typed : ModelTyping.{u,v} resolve entries context right type) : + ModelTyping.{u,v} resolve entries context left type := by + obtain ⟨e, A, he, hA, typing⟩ := typed + exact ⟨e, A, (beq_readExpr? faithful equal).trans he, hA, typing⟩ + +/-- Interning a typed candidate preserves its semantic typing postcondition. -/ +theorem internExpr {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} + {table : InternTable m} {term type : KExpr m} + (coherent : table.WF) + (faithful : KExpr.KeyCollisionFree fun e => table.ExprSupport e ∨ e = term) + (typed : ModelTyping.{u,v} resolve entries context term type) : + ModelTyping.{u,v} resolve entries context (table.internExpr term).1 type := by + obtain ⟨e, A, he, hA, typing⟩ := typed + exact ⟨e, A, (internExpr_readExpr? coherent faithful).trans he, hA, typing⟩ + +/-- Interning the inferred type also preserves the semantic judgment. -/ +theorem internType {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} + {table : InternTable m} {term type : KExpr m} + (coherent : table.WF) + (faithful : KExpr.KeyCollisionFree fun e => table.ExprSupport e ∨ e = type) + (typed : ModelTyping.{u,v} resolve entries context term type) : + ModelTyping.{u,v} resolve entries context term (table.internExpr type).1 := by + obtain ⟨e, A, he, hA, typing⟩ := typed + exact ⟨e, A, he, (internExpr_readExpr? coherent faithful).trans hA, typing⟩ + +/-- A closed kernel typing judgment at primitive False contradicts the +set-theoretic model. Dependency realizations are constructed by admission. -/ +theorem no_false [DecidableEq β] + {resolve : Address → Option (ConstRef β)} + {signature : Certified.PrimitiveSignature β} {store : Theory.Store β} + (admitted : Certified.AdmittedEnvironment.{u,v} signature store) + {term type : KExpr m} + (isFalse : readExpr? resolve type = some signature.falseExpr) + (typed : ModelTyping.{u,v} resolve admitted.entries [] term type) + (V : Type v) [SetTheory V] : False := by + obtain ⟨e, A, _he, hA, typing⟩ := typed + have erased : A.erase = signature.falseExpr := Option.some.inj (hA.symm.trans isFalse) + have falseExpr : A = .const signature.falseType [] := AExpr.eq_const_of_erase_eq erased + obtain ⟨constants, compatible⟩ := admitted.model V + have member := (typing V constants compatible.realizes [] (fun _ => empty) + (Model.Context.valid_nil constants [] (fun _ => empty))).2.2 + rw [falseExpr] at member + simp only [interp, List.map_nil, compatible.falseValue] at member + exact not_mem_empty _ member + +end ModelTyping + +/-- A successful production universe equality supplies semantic conversion +between the corresponding annotated sorts in every compatible model. -/ +theorem sort_conversion {entries : Model.Environment β} {context : Model.Context β} + {left right : KUniv m} (faithful : left.AddrFaithful right) + (boundLeft : left.size < UInt64.size) (boundRight : right.size < UInt64.size) + (accepted : Kernel.univEq left right = true) : + ConversionClaim.{u,v} entries context (.sort (readLevel left)) (.sort (readLevel right)) := + ConversionClaim.sort (Theory.VLevel.equiv_def.mp + (univEq_sound faithful boundLeft boundRight accepted)) + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Level.lean b/Ix/Kernel/Verify/Consistency/Level.lean new file mode 100644 index 000000000..59441f43d --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Level.lean @@ -0,0 +1,74 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Level +import Ix.Theory.VLevel + +open Ix.Theory (VLevel) + +/-! +# Kernel universe decisions in the consistency model + +The normalization proof and the set-theoretic model share `Theory.VLevel`. +This module states the production decisions directly in that model. The +comparison hypotheses are faithfulness of the compared addresses and bounds +excluding UInt64 overflow. +-/ + +namespace Ix.Kernel.Consistency + +variable {m : Mode} + +/-- Read a kernel universe structurally, without consulting its cached hash. -/ +def readLevel : KUniv m → Theory.VLevel + | .zero _ => .zero + | .succ u _ => .succ (readLevel u) + | .max a b _ => .max (readLevel a) (readLevel b) + | .imax a b _ => .imax (readLevel a) (readLevel b) + | .param index _ _ => .param index.toNat + +/-- Display metadata cannot change a universe's model reading. -/ +@[simp] theorem readLevel_eraseMeta (u : KUniv m) : + readLevel u.eraseMeta = readLevel u := by + induction u <;> simp_all [KUniv.eraseMeta, readLevel] + +/-- The structural reader and the normalization proof use the same syntax. -/ +theorem readLevel_eq (u : KUniv m) : readLevel u = u.toVLevel := by + induction u <;> simp_all [readLevel, KUniv.toVLevel] + +/-- The production successor constructor preserves the semantic successor. -/ +@[simp] theorem readLevel_mkSucc (u : KUniv m) : + readLevel (KUniv.mkSucc u) = .succ (readLevel u) := by + rw [readLevel_eq, KUniv.toVLevel_mkSucc, ← readLevel_eq] + +/-- The structural reader preserves universe evaluation. -/ +theorem readLevel_eval (levels : List Nat) (u : KUniv m) : + (readLevel u).eval levels = u.toVLevel.eval levels := by + rw [readLevel_eq] + +/-- Reading preserves the existing checker's universe-parameter scope. -/ +theorem readLevel_wf (n : Nat) (u : KUniv m) : + (readLevel u).WF n ↔ u.toVLevel.WF n := by + rw [readLevel_eq] + +/-- Successful production universe equality is equality in the new model. -/ +theorem univEq_sound {u v : KUniv m} (faithful : u.AddrFaithful v) + (boundU : u.size < UInt64.size) (boundV : v.size < UInt64.size) + (accepted : Kernel.univEq u v = true) : readLevel u ≈ readLevel v := by + apply Theory.VLevel.equiv_def.mpr + intro levels + rw [readLevel_eval, readLevel_eval] + exact Ix.Theory.VLevel.equiv_def.mp + (Kernel.univEq_sound faithful boundU boundV accepted) levels + +/-- Successful production universe comparison is inclusion in the new model. -/ +theorem univGeq_sound {u v : KUniv m} (faithful : u.AddrFaithful v) + (boundU : u.size < UInt64.size) (boundV : v.size < UInt64.size) + (accepted : Kernel.univGeq u v = true) : readLevel v ≤ readLevel u := by + intro levels + rw [readLevel_eval, readLevel_eval] + exact Kernel.univGeq_sound faithful boundU boundV accepted levels + +end Ix.Kernel.Consistency diff --git a/Ix/Tc/Verify/Ctx.lean b/Ix/Kernel/Verify/Ctx.lean similarity index 96% rename from Ix/Tc/Verify/Ctx.lean rename to Ix/Kernel/Verify/Ctx.lean index 4e044b8ca..6cc948568 100644 --- a/Ix/Tc/Verify/Ctx.lean +++ b/Ix/Kernel/Verify/Ctx.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.State -import Ix.Tc.Lctx +import Ix.Kernel.Verify.State +import Ix.Kernel.Lctx /-! # Dual-context reconciliation: `CtxRecon` @@ -45,9 +45,9 @@ production equations live in `Verify/Totalization`. Preservation lemmas for `CtxRecon` over more than one pop remain part of the checker-soundness layer. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr VEnv VLocalDecl) +open Ix.Theory.Named (VExpr VEnv VLocalDecl) /-! ### List helpers (local, no stdlib-name gambling) -/ @@ -441,7 +441,7 @@ theorem bvar_frame {bs : List (KExpr .anon × Option (KExpr .anon))} simp only [List.getElem?_cons_zero] at hb cases hb refine ⟨Δ, .vlam ty', 0, ?_, ?_, ?_, .vlam h1 h2⟩ - · simpa [Lean4Lean.VLocalDecl.depth] using + · simpa [Ix.Theory.Named.VLocalDecl.depth] using KVLCtx.KBVLift.skip (.vlam ty') .refl · simp [KVLCtx.find?, KVLCtx.next] · simp only [KVLCtx.fvars_cons_none] @@ -450,10 +450,10 @@ theorem bvar_frame {bs : List (KExpr .anon × Option (KExpr .anon))} simp only [List.getElem?_cons_succ] at hb obtain ⟨Δ₀, vd, m, W, hf, hsub, htr⟩ := ih nd hb refine ⟨Δ₀, vd, m + 1, ?_, ?_, ?_, htr⟩ - · simpa [Lean4Lean.VLocalDecl.depth, Nat.add_assoc] using + · simpa [Ix.Theory.Named.VLocalDecl.depth, Nat.add_assoc] using KVLCtx.KBVLift.skip (.vlam ty') W · simp only [KVLCtx.find?, KVLCtx.next, Option.bind_eq_bind, hf] - simp [Lean4Lean.VLocalDecl.depth, Lean4Lean.VExpr.liftN_liftN] + simp [Ix.Theory.Named.VLocalDecl.depth, Ix.Theory.Named.VExpr.liftN_liftN] · simp only [KVLCtx.fvars_cons_none] exact hsub | @bvar_let bs fs Δ ty₀ val₀ ty' val' H1 h1 h2 h3 ih => @@ -463,7 +463,7 @@ theorem bvar_frame {bs : List (KExpr .anon × Option (KExpr .anon))} simp only [List.getElem?_cons_zero] at hb cases hb refine ⟨Δ, .vlet ty' val', 0, ?_, ?_, ?_, .vlet h1 h2 h3⟩ - · simpa [Lean4Lean.VLocalDecl.depth] using + · simpa [Ix.Theory.Named.VLocalDecl.depth] using KVLCtx.KBVLift.skip (.vlet ty' val') .refl · simp [KVLCtx.find?, KVLCtx.next] · simp only [KVLCtx.fvars_cons_none] @@ -472,10 +472,10 @@ theorem bvar_frame {bs : List (KExpr .anon × Option (KExpr .anon))} simp only [List.getElem?_cons_succ] at hb obtain ⟨Δ₀, vd, m, W, hf, hsub, htr⟩ := ih nd hb refine ⟨Δ₀, vd, m, ?_, ?_, ?_, htr⟩ - · simpa [Lean4Lean.VLocalDecl.depth] using + · simpa [Ix.Theory.Named.VLocalDecl.depth] using KVLCtx.KBVLift.skip (.vlet ty' val') W · simp only [KVLCtx.find?, KVLCtx.next, Option.bind_eq_bind, hf] - simp [Lean4Lean.VLocalDecl.depth] + simp [Ix.Theory.Named.VLocalDecl.depth] · simp only [KVLCtx.fvars_cons_none] exact hsub | @fvar bs fs Δ fv deps d vd' H1 h1 h2 ih => @@ -489,7 +489,7 @@ theorem bvar_frame {bs : List (KExpr .anon × Option (KExpr .anon))} · simpa [Nat.add_assoc] using KVLCtx.KBVLift.skip_fvar (fv, deps) vd' hfresh W · simp only [KVLCtx.find?, KVLCtx.next, Option.bind_eq_bind, hf] - simp [Lean4Lean.VExpr.liftN_liftN] + simp [Ix.Theory.Named.VExpr.liftN_liftN] · simp only [KVLCtx.fvars_cons_some] exact fun x hx => List.mem_cons_of_mem _ (hsub hx) @@ -517,20 +517,20 @@ theorem fvar_frame {bs : List (KExpr .anon × Option (KExpr .anon))} intro nd j fv d hb obtain ⟨Δ₀, vd, dn, m, W, hf, hsub, htr⟩ := ih nd hb refine ⟨Δ₀, vd, dn + 1, m + 1, ?_, ?_, ?_, htr⟩ - · simpa [Lean4Lean.VLocalDecl.depth, Nat.add_assoc] using + · simpa [Ix.Theory.Named.VLocalDecl.depth, Nat.add_assoc] using KVLCtx.KBVLift.skip (.vlam ty') W · simp only [KVLCtx.find?, KVLCtx.next, Option.bind_eq_bind, hf] - simp [Lean4Lean.VLocalDecl.depth, Lean4Lean.VExpr.liftN_liftN] + simp [Ix.Theory.Named.VLocalDecl.depth, Ix.Theory.Named.VExpr.liftN_liftN] · simp only [KVLCtx.fvars_cons_none] exact hsub | @bvar_let bs fs Δ ty₀ val₀ ty' val' H1 h1 h2 h3 ih => intro nd j fv d hb obtain ⟨Δ₀, vd, dn, m, W, hf, hsub, htr⟩ := ih nd hb refine ⟨Δ₀, vd, dn + 1, m, ?_, ?_, ?_, htr⟩ - · simpa [Lean4Lean.VLocalDecl.depth] using + · simpa [Ix.Theory.Named.VLocalDecl.depth] using KVLCtx.KBVLift.skip (.vlet ty' val') W · simp only [KVLCtx.find?, KVLCtx.next, Option.bind_eq_bind, hf] - simp [Lean4Lean.VLocalDecl.depth] + simp [Ix.Theory.Named.VLocalDecl.depth] · simp only [KVLCtx.fvars_cons_none] exact hsub | @fvar bs fs Δ fv₀ deps₀ d₀ vd₀ H1 h1 h2 ih => @@ -561,7 +561,7 @@ theorem fvar_frame {bs : List (KExpr .anon × Option (KExpr .anon))} · simpa [Nat.add_assoc] using KVLCtx.KBVLift.skip_fvar (fv₀, deps₀) vd₀ hfresh W · simp [KVLCtx.find?, KVLCtx.next, hne, hf, - Lean4Lean.VExpr.liftN_liftN] + Ix.Theory.Named.VExpr.liftN_liftN] · simp only [KVLCtx.fvars_cons_some] exact fun x hx => List.mem_cons_of_mem _ (hsub hx) @@ -675,7 +675,7 @@ private theorem frame_of_read (hidx : idx.toNat < s.ctx.size) (hty : s.ctx[s.ctx.size - 1 - idx.toNat]? = some ty) (hov : s.letVals[s.ctx.size - 1 - idx.toNat]? = some ov) : - ∃ (Δ₀ : KVLCtx) (vd : Lean4Lean.VLocalDecl) (m : Nat), + ∃ (Δ₀ : KVLCtx) (vd : Ix.Theory.Named.VLocalDecl) (m : Nat), KVLCtx.KBVLift Δ₀ Δ (idx.toNat + 1) 0 (vd.depth + m) 0 ∧ KVLCtx.find? Δ (.inl idx.toNat) = some (vd.value.liftN m 0, vd.type.liftN m 0) ∧ @@ -719,13 +719,13 @@ theorem lookupVar {idx : UInt64} {ty : KExpr .anon} refine ⟨_, _, hf, ?_⟩ have hw := h1.weakBV henv htp.weakN W hshift (show (0 : UInt64).toNat = 0 from rfl) hbig - simpa [Lean4Lean.VLocalDecl.type, Lean4Lean.VLocalDecl.depth, - Lean4Lean.VExpr.lift, Lean4Lean.VExpr.liftN_liftN] using hw + simpa [Ix.Theory.Named.VLocalDecl.type, Ix.Theory.Named.VLocalDecl.depth, + Ix.Theory.Named.VExpr.lift, Ix.Theory.Named.VExpr.liftN_liftN] using hw | vlet h1 h2 h3 => refine ⟨_, _, hf, ?_⟩ have hw := h1.weakBV henv htp.weakN W hshift (show (0 : UInt64).toNat = 0 from rfl) hbig - simpa [Lean4Lean.VLocalDecl.type, Lean4Lean.VLocalDecl.depth] + simpa [Ix.Theory.Named.VLocalDecl.type, Ix.Theory.Named.VLocalDecl.depth] using hw /-- `lookupLetVal`'s soundness core: at a let frame, the lifted stored @@ -752,7 +752,7 @@ theorem lookupLetVal {idx : UInt64} {ty val : KExpr .anon} refine ⟨_, _, hf, ?_⟩ have hw := h2.weakBV henv htp.weakN W hshift (show (0 : UInt64).toNat = 0 from rfl) hbig - simpa [Lean4Lean.VLocalDecl.value, Lean4Lean.VLocalDecl.depth] + simpa [Ix.Theory.Named.VLocalDecl.value, Ix.Theory.Named.VLocalDecl.depth] using hw /-- Walker-tight sibling of `lookupLetVal`. The older theorem asks for the @@ -781,7 +781,7 @@ theorem lookupLetVal_liftBounds {idx : UInt64} {ty val : KExpr .anon} refine ⟨_, _, hf, ?_⟩ have hw := h2.weakBV_lbr henv htp.weakN hcon W hshift (show (0 : UInt64).toNat = 0 from rfl) hcut hlift - simpa [Lean4Lean.VLocalDecl.value, Lean4Lean.VLocalDecl.depth] + simpa [Ix.Theory.Named.VLocalDecl.value, Ix.Theory.Named.VLocalDecl.depth] using hw /-- The fvar-side lookup bridge at the concrete state: a successful @@ -791,7 +791,7 @@ theorem lookupLetVal_liftBounds {idx : UInt64} {ty val : KExpr .anon} theorem lctxFind? {fv : FVarId} {d : LocalDecl .anon} (h : CtxRecon env uvars nameOf trProj s Δ) (hf : s.lctx.find? fv = some d) : - ∃ (Δ₀ : KVLCtx) (vd : Lean4Lean.VLocalDecl) (dn m : Nat), + ∃ (Δ₀ : KVLCtx) (vd : Ix.Theory.Named.VLocalDecl) (dn m : Nat), KVLCtx.KBVLift Δ₀ Δ dn 0 (vd.depth + m) 0 ∧ KVLCtx.find? Δ (.inr fv) = some (vd.value.liftN m 0, vd.type.liftN m 0) ∧ @@ -846,8 +846,8 @@ theorem lctxFindType {fv : FVarId} {d : LocalDecl .anon} (by simpa using (show ty.size < UInt64.size by omega)) simp [hclosed'] rw [hid] at hw - simpa [LocalDecl.ty, Lean4Lean.VLocalDecl.type, - Lean4Lean.VLocalDecl.depth, Lean4Lean.VExpr.liftN_liftN, + simpa [LocalDecl.ty, Ix.Theory.Named.VLocalDecl.type, + Ix.Theory.Named.VLocalDecl.depth, Ix.Theory.Named.VExpr.liftN_liftN, Nat.add_comm] using hw | @vlet nm ty val ty' val' hty hval hvalType => have hcon' : KExpr.Constructed ty := by @@ -863,8 +863,8 @@ theorem lctxFindType {fv : FVarId} {d : LocalDecl .anon} (by simpa using (show ty.size < UInt64.size by omega)) simp [hclosed'] rw [hid] at hw - simpa [LocalDecl.ty, Lean4Lean.VLocalDecl.type, - Lean4Lean.VLocalDecl.depth] using hw + simpa [LocalDecl.ty, Ix.Theory.Named.VLocalDecl.type, + Ix.Theory.Named.VLocalDecl.depth] using hw /-- A let-valued fvar lookup yields a translation of the concrete stored value at the current mixed context, provided that value is closed with @@ -898,7 +898,7 @@ theorem lctxFindLetVal {fv : FVarId} {nm : Mode.anon.F Name} (by simpa using (show val.size < UInt64.size by omega)) simp [hclosed] rw [hid] at hw - simpa [Lean4Lean.VLocalDecl.value, Lean4Lean.VLocalDecl.depth] using hw + simpa [Ix.Theory.Named.VLocalDecl.value, Ix.Theory.Named.VLocalDecl.depth] using hw /-- Fvar leaves resolve — the bare `TrKExprS.fvar` premise. -/ theorem fvar_resolves {fv : FVarId} {d : LocalDecl .anon} @@ -1154,4 +1154,4 @@ theorem closeFVar {fv : FVarId} {deps : List FVarId} {vd : VLocalDecl} end CtxRecon -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Decl.lean b/Ix/Kernel/Verify/Decl.lean similarity index 95% rename from Ix/Tc/Verify/Decl.lean rename to Ix/Kernel/Verify/Decl.lean index 1f0cbb9b1..0d46a008d 100644 --- a/Ix/Tc/Verify/Decl.lean +++ b/Ix/Kernel/Verify/Decl.lean @@ -1,7 +1,9 @@ -import Ix.Tc.Verify.World -import Ix.Tc.Verify.Level -import Lean4Lean.Verify.Typing.Expr -import Lean4Lean.Theory.Typing.Lemmas +import Ix.Kernel.Verify.World +import Ix.Kernel.Verify.Level +import Ix.Theory.Named.Verify.Typing.Expr +import Ix.Theory.Named.Typing.Lemmas + +open Ix.Theory (VLevel) /-! # Raw, pending, and trusted declarations @@ -30,14 +32,14 @@ catalogued, pending, and raw-translatable, but cannot be a well-formed Theory declaration in the empty trusted environment. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr VLevel VEnv VConstant VConstVal VDefVal VDecl) +open Ix.Theory.Named (VExpr VEnv VConstant VConstVal VDefVal VDecl) /-- The abstract projection component used by expression translation. Projection semantics are indexed by the ambient universe-parameter count. -Lean4Lean's concrete `TrProj` changes that index under universe +Ix.Theory.Named's concrete `TrProj` changes that index under universe instantiation, so erasing it here would make the `instL` law impossible to state faithfully. The raw boundary still carries no closure or typing contract; those laws live in `TrProjOK`. -/ @@ -300,7 +302,7 @@ inductive RawDefKindRel (ci : VDefVal) : Ix.DefKind → VDecl → Prop No constructor asks for `ci.WF` or `d.WF`. Quotients and inductive-family members cannot be represented soundly one constant at a time in -Lean4Lean.Theory, so they intentionally have no constructor here; their +Ix.Theory.Named.Theory, so they intentionally have no constructor here; their future relation is atomic at the block level. -/ inductive RawDeclRel (env : VEnv) (nameOf : Address → Option Lean.Name) (trProj : RawProjRel) (id : KId .anon) : KConst .anon → VDecl → Prop @@ -348,7 +350,7 @@ theorem mono {env env' : VEnv} (henv : env ≤ env') exact .defn hname (hty.mono henv) (hval.mono henv) hkind /-- A WF transition for a raw standalone declaration extends its input -environment. Lean4Lean does not provide this for arbitrary `VDecl.WF` +environment. Ix.Theory.Named does not provide this for arbitrary `VDecl.WF` (notably, its abstract `addInduct` has no extension theorem), but it follows constructively for exactly the axiom/definition/opaque cases admitted by `RawDeclRel`. -/ @@ -359,17 +361,17 @@ theorem wf_le {env env' : VEnv} {nameOf : Address → Option Lean.Name} cases hraw with | «axiom» => cases hwf with - | «axiom» _ hadd => exact Lean4Lean.VEnv.addConst_le hadd + | «axiom» _ hadd => exact Ix.Theory.Named.VEnv.addConst_le hadd | defn _ _ _ hkind => cases hkind with | defn => cases hwf with | «def» _ hadd => - exact (Lean4Lean.VEnv.addConst_le hadd).trans - Lean4Lean.VEnv.addDefEq_le + exact (Ix.Theory.Named.VEnv.addConst_le hadd).trans + Ix.Theory.Named.VEnv.addDefEq_le | opaq | thm => cases hwf with - | «opaque» _ hadd => exact Lean4Lean.VEnv.addConst_le hadd + | «opaque» _ hadd => exact Ix.Theory.Named.VEnv.addConst_le hadd /-- Target freshness rules out self-reference in every expression translated by a raw standalone declaration. -/ @@ -450,7 +452,7 @@ theorem lookup {trProj : RawProjRel} {world : VerifyWorld} cases hwf with | «axiom» hconstant hadd => exact ⟨_, _, .axiom _, hname, - hinstalled.constants (Lean4Lean.VEnv.addConst_self hadd)⟩ + hinstalled.constants (Ix.Theory.Named.VEnv.addConst_self hadd)⟩ | defn hname hty hval hkind => cases hkind with | defn => @@ -458,13 +460,13 @@ theorem lookup {trProj : RawProjRel} {world : VerifyWorld} | «def» hconstant hadd => exact ⟨_, _, .defn _, hname, hinstalled.constants - (Lean4Lean.VEnv.addDefEq_le.constants - (Lean4Lean.VEnv.addConst_self hadd))⟩ + (Ix.Theory.Named.VEnv.addDefEq_le.constants + (Ix.Theory.Named.VEnv.addConst_self hadd))⟩ | opaq | thm => cases hwf with | «opaque» hconstant hadd => exact ⟨_, _, .opaque _, hname, - hinstalled.constants (Lean4Lean.VEnv.addConst_self hadd)⟩ + hinstalled.constants (Ix.Theory.Named.VEnv.addConst_self hadd)⟩ end TrustedDecl @@ -510,7 +512,7 @@ end PendingDecl namespace IllTypedPending -def targetName : Lean.Name := `Ix.Tc.Verify.illTypedPending +def targetName : Lean.Name := `Ix.Kernel.Verify.illTypedPending /-- A fixed 32-byte address keeps the fixture independent of the Blake3 FFI and its generated `native_decide` axiom. Address coherence is a separate @@ -569,7 +571,7 @@ theorem theoryConstant_not_wf : ¬theoryConstant.toVConstant.WF world.venv := by intro hwf have hlevel : (VLevel.param 0).WF 0 := - hwf.sort_inv Lean4Lean.VEnv.Ordered.empty + hwf.sort_inv Ix.Theory.Named.VEnv.Ordered.empty exact (Nat.not_lt_zero 0) hlevel /-- Consequently there is no Theory declaration-WF step from the pending @@ -606,4 +608,4 @@ end Loaded end IllTypedPending -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq.lean b/Ix/Kernel/Verify/DefEq.lean similarity index 99% rename from Ix/Tc/Verify/DefEq.lean rename to Ix/Kernel/Verify/DefEq.lean index 03a855cb4..9bd1dfb45 100644 --- a/Ix/Tc/Verify/DefEq.lean +++ b/Ix/Kernel/Verify/DefEq.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Infer -import Ix.Tc.Verify.Whnf.Closure +import Ix.Kernel.Verify.Infer +import Ix.Kernel.Verify.Whnf.Closure import Batteries.Data.UInt /-! @@ -10,9 +10,9 @@ equality. A cached `false` (including the narrow failure set) can only reject an otherwise valid declaration, so it carries no acceptance claim here. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) private theorem uint64_max_comm (a b : UInt64) : max a b = max b a := by apply UInt64.toNat_inj.mp @@ -227,7 +227,7 @@ theorem of_addr_beq {trProj : RawProjRel} {world : VerifyWorld} subst b intro _ exact ⟨va, va, ha, ha, - Lean4Lean.VEnv.IsDefEqU.refl (theory.exprWF hctx ha)⟩ + Ix.Theory.Named.VEnv.IsDefEqU.refl (theory.exprWF hctx ha)⟩ theorem symm {trProj : RawProjRel} {world : VerifyWorld} {uvars : Nat} {Delta : KVLCtx} {a b : KExpr .anon} {answer : Bool} @@ -2836,4 +2836,4 @@ theorem inferWith_inferOnlyHit_acceptance end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/AcceleratorGates.lean b/Ix/Kernel/Verify/DefEq/AcceleratorGates.lean similarity index 97% rename from Ix/Tc/Verify/DefEq/AcceleratorGates.lean rename to Ix/Kernel/Verify/DefEq/AcceleratorGates.lean index 0e59621d3..3227e9db3 100644 --- a/Ix/Tc/Verify/DefEq/AcceleratorGates.lean +++ b/Ix/Kernel/Verify/DefEq/AcceleratorGates.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.NatReduction -import Ix.Tc.Verify.Whnf.Driver.FullStep +import Ix.Kernel.Verify.DefEq.NatReduction +import Ix.Kernel.Verify.Whnf.Driver.FullStep /-! # Lazy-delta accelerator gates @@ -11,7 +11,7 @@ those four operationally unreachable hit branches from lazy delta and exposes the first substantive remaining tail: delta-head classification. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -35,7 +35,7 @@ theorem defEqLazyDeltaStepAfterNatMiss_wf {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {state : TcState .anon} - {leftSource rightSource : Lean4Lean.VExpr} + {leftSource rightSource : Ix.Theory.Named.VExpr} {left right : KExpr .anon} (theory : WhnfTheory trProj world uvars) (hafter : DefEqLazyDeltaAfterAcceleratorMiss.WFAt .noAccel semantics @@ -158,4 +158,4 @@ end DefEqLazyDeltaAfterNatMiss end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/ApplicationSpine.lean b/Ix/Kernel/Verify/DefEq/ApplicationSpine.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/ApplicationSpine.lean rename to Ix/Kernel/Verify/DefEq/ApplicationSpine.lean index 7c0704617..f062e5ab0 100644 --- a/Ix/Tc/Verify/DefEq/ApplicationSpine.lean +++ b/Ix/Kernel/Verify/DefEq/ApplicationSpine.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.SpineArguments +import Ix.Kernel.Verify.DefEq.SpineArguments /-! # General application-spine comparison @@ -9,9 +9,9 @@ argument loop. A positive result is reconstructed through the complete typed spines; constructor misses and unequal arities carry no semantic claim. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- Finite support coverage for the head and arguments selected by the exact production `collectSpine` executions. -/ @@ -145,4 +145,4 @@ end TryDefEqApp end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/BoolTrue.lean b/Ix/Kernel/Verify/DefEq/BoolTrue.lean similarity index 99% rename from Ix/Tc/Verify/DefEq/BoolTrue.lean rename to Ix/Kernel/Verify/DefEq/BoolTrue.lean index 6ac0fc3ec..515983494 100644 --- a/Ix/Tc/Verify/DefEq/BoolTrue.lean +++ b/Ix/Kernel/Verify/DefEq/BoolTrue.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.Structural +import Ix.Kernel.Verify.DefEq.Structural /-! # Eager Bool.true definitional equality @@ -9,9 +9,9 @@ Acceptance is sound only when the runtime primitive address is tied to the trusted Theory name; address equality by itself is not authority. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- Minimal trusted binding for the one primitive read by the eager Boolean tier. -/ @@ -316,4 +316,4 @@ theorem DefEqAfterBoolTrue.closesInner end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/CacheBranches.lean b/Ix/Kernel/Verify/DefEq/CacheBranches.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/CacheBranches.lean rename to Ix/Kernel/Verify/DefEq/CacheBranches.lean index f200f3670..13a46592d 100644 --- a/Ix/Tc/Verify/DefEq/CacheBranches.lean +++ b/Ix/Kernel/Verify/DefEq/CacheBranches.lean @@ -1,15 +1,15 @@ -import Ix.Tc.Verify.DefEq +import Ix.Kernel.Verify.DefEq /-! # DefEq cache-policy branches This module verifies cache exits whose state effects depend on cheap mode. The semantic manager and guarded root-cache foundations live in -`Ix.Tc.Verify.DefEq`; the exhaustive cache shell will assemble these branches +`Ix.Kernel.Verify.DefEq`; the exhaustive cache shell will assemble these branches before entering recursive comparison. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -100,7 +100,7 @@ theorem isDefEq_fullHitCheapMode_true_acceptance {methods : Methods .anon} {layer : WhnfLayer} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} (model : KernelSuffixModel trProj world) - {Delta : KVLCtx} {a b : KExpr .anon} {va vb : Lean4Lean.VExpr} + {Delta : KVLCtx} {a b : KExpr .anon} {va vb : Ix.Theory.Named.VExpr} {ctxAddr : Address} {s s1 s2 s3 s4 : TcState .anon} (theory : WhnfTheory trProj world model.keys.uvars) (hcollision : support.CollisionFree) @@ -344,7 +344,7 @@ theorem isDefEq_rootCheapHit_true_acceptance {methods : Methods .anon} {layer : WhnfLayer} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} (model : KernelSuffixModel trProj world) - {Delta : KVLCtx} {a b : KExpr .anon} {va vb : Lean4Lean.VExpr} + {Delta : KVLCtx} {a b : KExpr .anon} {va vb : Ix.Theory.Named.VExpr} {ctxAddr : Address} {aRoot bRoot : EqKey} {s s1 s2 s3 s4 s5 : TcState .anon} (theory : WhnfTheory trProj world model.keys.uvars) @@ -506,7 +506,7 @@ theorem guardedRootHit_copyBoth {layer : WhnfLayer} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} (model : KernelSuffixModel trProj world) {kind : DefEqCacheKind} {Delta : KVLCtx} - {a b : KExpr .anon} {va vb : Lean4Lean.VExpr} + {a b : KExpr .anon} {va vb : Ix.Theory.Named.VExpr} {ctxAddr : Address} {aRoot bRoot : EqKey} {s : TcState .anon} (theory : WhnfTheory trProj world model.keys.uvars) (hcollision : support.CollisionFree) @@ -724,7 +724,7 @@ theorem isDefEq_rootFullHitCheapMode_true_acceptance {methods : Methods .anon} {layer : WhnfLayer} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} (model : KernelSuffixModel trProj world) - {Delta : KVLCtx} {a b : KExpr .anon} {va vb : Lean4Lean.VExpr} + {Delta : KVLCtx} {a b : KExpr .anon} {va vb : Ix.Theory.Named.VExpr} {ctxAddr : Address} {aRoot bRoot : EqKey} {s s1 s2 s3 s4 s5 : TcState .anon} (theory : WhnfTheory trProj world model.keys.uvars) @@ -844,4 +844,4 @@ theorem isDefEq_rootFullHitCheapMode_true_acceptance end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/CacheShell.lean b/Ix/Kernel/Verify/DefEq/CacheShell.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/CacheShell.lean rename to Ix/Kernel/Verify/DefEq/CacheShell.lean index b6f72ddd9..29630d2fd 100644 --- a/Ix/Tc/Verify/DefEq/CacheShell.lean +++ b/Ix/Kernel/Verify/DefEq/CacheShell.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.CacheBranches +import Ix.Kernel.Verify.DefEq.CacheBranches /-! # DefEq cache shell @@ -10,7 +10,7 @@ final cache write. The bridge theorems below connect the entry-point prefix to those production-owned functions. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -252,7 +252,7 @@ private theorem guardedRootResult (theory : WhnfTheory trProj world model.keys.uvars) (hcollision : support.CollisionFree) {kind : DefEqCacheKind} {answer : Bool} {Delta : KVLCtx} - {a b : KExpr .anon} {va vb : Lean4Lean.VExpr} + {a b : KExpr .anon} {va vb : Ix.Theory.Named.VExpr} {ctxAddr : Address} {aRoot bRoot : EqKey} {s : TcState .anon} (hI : WhnfStateInv layer (kernelCacheSemantics model.keys trProj) trProj world support model.keys.uvars Delta s) @@ -307,7 +307,7 @@ private theorem applyFullRootResult_wf {layer : WhnfLayer} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {model : KernelSuffixModel trProj world} {Delta : KVLCtx} {s : TcState .anon} - {a b : KExpr .anon} {va vb : Lean4Lean.VExpr} {ctxAddr : Address} + {a b : KExpr .anon} {va vb : Ix.Theory.Named.VExpr} {ctxAddr : Address} {answer cheapMode : Bool} (hcollision : support.CollisionFree) (haSupport : support a) (hbSupport : support b) @@ -369,7 +369,7 @@ private theorem applyCheapRootResult_wf {layer : WhnfLayer} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {model : KernelSuffixModel trProj world} {Delta : KVLCtx} {s : TcState .anon} - {a b : KExpr .anon} {va vb : Lean4Lean.VExpr} {ctxAddr : Address} + {a b : KExpr .anon} {va vb : Ix.Theory.Named.VExpr} {ctxAddr : Address} {answer : Bool} (hcollision : support.CollisionFree) (haSupport : support a) (hbSupport : support b) @@ -442,7 +442,7 @@ private theorem applyDirectFullHit_wf {layer : WhnfLayer} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {model : KernelSuffixModel trProj world} {Delta : KVLCtx} {s : TcState .anon} - {a b : KExpr .anon} {va vb : Lean4Lean.VExpr} {ctxAddr : Address} + {a b : KExpr .anon} {va vb : Ix.Theory.Named.VExpr} {ctxAddr : Address} {answer cheapMode : Bool} (hcollision : support.CollisionFree) (haSupport : support a) (hbSupport : support b) @@ -537,7 +537,7 @@ private theorem applyDirectCheapHit_wf {layer : WhnfLayer} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {model : KernelSuffixModel trProj world} {Delta : KVLCtx} {s : TcState .anon} - {a b : KExpr .anon} {va vb : Lean4Lean.VExpr} {ctxAddr : Address} + {a b : KExpr .anon} {va vb : Ix.Theory.Named.VExpr} {ctxAddr : Address} {answer : Bool} (hcollision : support.CollisionFree) (haSupport : support a) (hbSupport : support b) @@ -609,7 +609,7 @@ theorem isDefEqAfterRootCacheMiss_wf (hcollision : support.CollisionFree) (hinner : DefEqInner.WF layer trProj world support model) {Delta : KVLCtx} {s : TcState .anon} - {a b : KExpr .anon} {va vb : Lean4Lean.VExpr} {ctxAddr : Address} + {a b : KExpr .anon} {va vb : Ix.Theory.Named.VExpr} {ctxAddr : Address} {cheapMode : Bool} (haSupport : support a) (hbSupport : support b) (ha : TrKExprS world.venv model.keys.uvars world.nameOf trProj Delta a va) @@ -770,7 +770,7 @@ theorem isDefEqAfterDirectCacheMiss_wf (hcollision : support.CollisionFree) (hinner : DefEqInner.WF layer trProj world support model) {Delta : KVLCtx} {s : TcState .anon} - {a b : KExpr .anon} {va vb : Lean4Lean.VExpr} {ctxAddr : Address} + {a b : KExpr .anon} {va vb : Ix.Theory.Named.VExpr} {ctxAddr : Address} {cheapMode : Bool} (haSupport : support a) (hbSupport : support b) (ha : TrKExprS world.venv model.keys.uvars world.nameOf trProj Delta a va) @@ -907,7 +907,7 @@ theorem isDefEq_wf (hcollision : support.CollisionFree) (hinner : DefEqInner.WF layer trProj world support model) {Delta : KVLCtx} {s : TcState .anon} - {a b : KExpr .anon} {va vb : Lean4Lean.VExpr} + {a b : KExpr .anon} {va vb : Ix.Theory.Named.VExpr} (haSupport : support a) (hbSupport : support b) (ha : TrKExprS world.venv model.keys.uvars world.nameOf trProj Delta a va) (hb : TrKExprS world.venv model.keys.uvars world.nameOf trProj Delta b vb) @@ -1046,4 +1046,4 @@ theorem isDefEq_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/CheapReduction.lean b/Ix/Kernel/Verify/DefEq/CheapReduction.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/CheapReduction.lean rename to Ix/Kernel/Verify/DefEq/CheapReduction.lean index 7b68bcd85..52500b8f2 100644 --- a/Ix/Tc/Verify/DefEq/CheapReduction.lean +++ b/Ix/Kernel/Verify/DefEq/CheapReduction.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.StringLiteral -import Ix.Tc.Verify.Whnf.NoDelta.Reducer +import Ix.Kernel.Verify.DefEq.StringLiteral +import Ix.Kernel.Verify.Whnf.NoDelta.Reducer /-! # Cheap DefEq reduction prefix @@ -10,9 +10,9 @@ cheap-depth scope itself and composes both passes with address collision freedom and the already verified structural comparison. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -332,4 +332,4 @@ end DefEqAfterNoDeltaPass end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/Closure.lean b/Ix/Kernel/Verify/DefEq/Closure.lean similarity index 97% rename from Ix/Tc/Verify/DefEq/Closure.lean rename to Ix/Kernel/Verify/DefEq/Closure.lean index 752558a78..15b510c9c 100644 --- a/Ix/Tc/Verify/DefEq/Closure.lean +++ b/Ix/Kernel/Verify/DefEq/Closure.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.DefEq.CacheShell -import Ix.Tc.Verify.DefEq.FinalWhnf.Closure -import Ix.Tc.Verify.DefEq.LazyDeltaClosure +import Ix.Kernel.Verify.DefEq.CacheShell +import Ix.Kernel.Verify.DefEq.FinalWhnf.Closure +import Ix.Kernel.Verify.DefEq.LazyDeltaClosure /-! # Complete definitional-equality closure @@ -12,7 +12,7 @@ records: projection delta owns the shared Theory/collision/structural facts, while final WHNF owns the direct reducer and primitive-expansion facts. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace CacheEntry @@ -176,4 +176,4 @@ end DefEqClosureResources end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/DeltaClassification.lean b/Ix/Kernel/Verify/DefEq/DeltaClassification.lean similarity index 96% rename from Ix/Tc/Verify/DefEq/DeltaClassification.lean rename to Ix/Kernel/Verify/DefEq/DeltaClassification.lean index 7f3d9525f..db4d21057 100644 --- a/Ix/Tc/Verify/DefEq/DeltaClassification.lean +++ b/Ix/Kernel/Verify/DefEq/DeltaClassification.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.AcceleratorGates -import Ix.Tc.Verify.Whnf.Runtime.LazyIngress +import Ix.Kernel.Verify.DefEq.AcceleratorGates +import Ix.Kernel.Verify.Whnf.Runtime.LazyIngress /-! # Lazy-delta head classification @@ -11,7 +11,7 @@ no semantic claim is attached to a negative answer. This module also closes the exact stopped branch where neither head is classified as reducible. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -64,7 +64,7 @@ theorem defEqLazyDeltaStepAfterAcceleratorMiss_wf {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {state : TcState .anon} - {leftSource rightSource : Lean4Lean.VExpr} + {leftSource rightSource : Ix.Theory.Named.VExpr} {left right : KExpr .anon} (hfault : TcM.LazyFaultPreserves (WhnfStateInv layer semantics trProj world support uvars Delta)) @@ -110,4 +110,4 @@ end DefEqLazyDeltaAfterAcceleratorMiss end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/EqualRankCache.lean b/Ix/Kernel/Verify/DefEq/EqualRankCache.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/EqualRankCache.lean rename to Ix/Kernel/Verify/DefEq/EqualRankCache.lean index 30004e11c..b4ce02801 100644 --- a/Ix/Tc/Verify/DefEq/EqualRankCache.lean +++ b/Ix/Kernel/Verify/DefEq/EqualRankCache.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.SameHeadSpine +import Ix.Kernel.Verify.DefEq.SameHeadSpine /-! # Equal-rank same-head cache @@ -9,7 +9,7 @@ proves the exact lookup/attempt/write shell and preserves provenance for the single write made after a genuine comparison miss. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Provenance available for every concrete failure marker this run may insert. -/ @@ -95,7 +95,7 @@ theorem trySameHeadSpineSpeculative_wf {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {state : TcState .anon} - {left right : KExpr .anon} {leftV rightV : Lean4Lean.VExpr} + {left right : KExpr .anon} {leftV rightV : Ix.Theory.Named.VExpr} (hsame : TrySameHeadSpine.WFAt layer semantics trProj world support uvars) (hleftSupport : support left) (hrightSupport : support right) @@ -201,7 +201,7 @@ theorem trySameHeadSpineCached_wf {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {state : TcState .anon} - {left right : KExpr .anon} {leftV rightV : Lean4Lean.VExpr} + {left right : KExpr .anon} {leftV rightV : Ix.Theory.Named.VExpr} (hcache : DefEqFailureCacheResources semantics world support) (hsame : TrySameHeadSpine.WFAt layer semantics trProj world support uvars) @@ -275,4 +275,4 @@ end TrySameHeadSpineCached end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/EqualRankPrefix.lean b/Ix/Kernel/Verify/DefEq/EqualRankPrefix.lean similarity index 97% rename from Ix/Tc/Verify/DefEq/EqualRankPrefix.lean rename to Ix/Kernel/Verify/DefEq/EqualRankPrefix.lean index c045f9cbf..b8135d0bb 100644 --- a/Ix/Tc/Verify/DefEq/EqualRankPrefix.lean +++ b/Ix/Kernel/Verify/DefEq/EqualRankPrefix.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.EqualRankCache +import Ix.Kernel.Verify.DefEq.EqualRankCache /-! # Equal-rank prefix assembly @@ -8,9 +8,9 @@ attempt, and the already-proved two-sided reduction continuation into the complete equal-rank lazy-delta contract. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -118,4 +118,4 @@ end DefEqLazyDeltaEqualRank end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/EqualRankReduction.lean b/Ix/Kernel/Verify/DefEq/EqualRankReduction.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/EqualRankReduction.lean rename to Ix/Kernel/Verify/DefEq/EqualRankReduction.lean index 1d95349af..207a6764b 100644 --- a/Ix/Tc/Verify/DefEq/EqualRankReduction.lean +++ b/Ix/Kernel/Verify/DefEq/EqualRankReduction.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.RankDispatch +import Ix.Kernel.Verify.DefEq.RankDispatch /-! # Equal-rank two-sided reduction @@ -9,9 +9,9 @@ four hit/miss combinations in that exact order and feeds every productive combination through the common finishing checks. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -148,4 +148,4 @@ end DefEqLazyDeltaAfterSameHeadMiss end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/FinalWhnf/Application.lean b/Ix/Kernel/Verify/DefEq/FinalWhnf/Application.lean similarity index 97% rename from Ix/Tc/Verify/DefEq/FinalWhnf/Application.lean rename to Ix/Kernel/Verify/DefEq/FinalWhnf/Application.lean index 4833994da..75f7b62e2 100644 --- a/Ix/Tc/Verify/DefEq/FinalWhnf/Application.lean +++ b/Ix/Kernel/Verify/DefEq/FinalWhnf/Application.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.FinalWhnf.Contracts +import Ix.Kernel.Verify.DefEq.FinalWhnf.Contracts /-! # Final-WHNF application comparison @@ -8,9 +8,9 @@ constructor-directed final comparison. Argument equality is requested only after function equality succeeds, matching the production order. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- Finite child coverage for supported applications selected by the final WHNF comparator. -/ @@ -117,4 +117,4 @@ end TryDefEqWhnfApp end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/FinalWhnf/Closure.lean b/Ix/Kernel/Verify/DefEq/FinalWhnf/Closure.lean similarity index 91% rename from Ix/Tc/Verify/DefEq/FinalWhnf/Closure.lean rename to Ix/Kernel/Verify/DefEq/FinalWhnf/Closure.lean index b97a2f20a..36c1c5e6b 100644 --- a/Ix/Tc/Verify/DefEq/FinalWhnf/Closure.lean +++ b/Ix/Kernel/Verify/DefEq/FinalWhnf/Closure.lean @@ -1,10 +1,10 @@ -import Ix.Tc.Verify.DefEq.FinalWhnf.NatBridge -import Ix.Tc.Verify.DefEq.FinalWhnf.EtaExpansion -import Ix.Tc.Verify.DefEq.FinalWhnf.StringExpansion -import Ix.Tc.Verify.DefEq.FinalWhnf.StructuralPrefix -import Ix.Tc.Verify.DefEq.FinalWhnf.StructureEta -import Ix.Tc.Verify.DefEq.FinalWhnf.UnitLike -import Ix.Tc.Verify.DefEq.PropositionClassifier +import Ix.Kernel.Verify.DefEq.FinalWhnf.NatBridge +import Ix.Kernel.Verify.DefEq.FinalWhnf.EtaExpansion +import Ix.Kernel.Verify.DefEq.FinalWhnf.StringExpansion +import Ix.Kernel.Verify.DefEq.FinalWhnf.StructuralPrefix +import Ix.Kernel.Verify.DefEq.FinalWhnf.StructureEta +import Ix.Kernel.Verify.DefEq.FinalWhnf.UnitLike +import Ix.Kernel.Verify.DefEq.PropositionClassifier /-! # Complete final-WHNF comparison @@ -15,7 +15,7 @@ irrelevance fallbacks. This module assembles those independently verified phases under one canonical K2 suffix model. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Concrete resources for every production phase of `isDefEqWhnf`. The @@ -102,4 +102,4 @@ theorem finalWhnf end FinalWhnfClosureResources end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/FinalWhnf/Contracts.lean b/Ix/Kernel/Verify/DefEq/FinalWhnf/Contracts.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/FinalWhnf/Contracts.lean rename to Ix/Kernel/Verify/DefEq/FinalWhnf/Contracts.lean index f758d6b91..feae0f768 100644 --- a/Ix/Tc/Verify/DefEq/FinalWhnf/Contracts.lean +++ b/Ix/Kernel/Verify/DefEq/FinalWhnf/Contracts.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.StoppedContinuation +import Ix.Kernel.Verify.DefEq.StoppedContinuation /-! # Final-WHNF comparison contracts @@ -9,7 +9,7 @@ contracts let their exhaustive proofs be developed independently and then compose them back into `isDefEqWhnf`. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -19,7 +19,7 @@ def TryDefEqWhnfLet.WFAt (layer : WhnfLayer) (world : VerifyWorld) (support : RunSupport) (uvars : Nat) : Prop := ∀ {Delta state leftName rightName ty1 val1 body1 ty2 val2 body2} {leftNondep rightNondep : Bool} - {leftInfo rightInfo : ExprInfo .anon} {leftV rightV : Lean4Lean.VExpr}, + {leftInfo rightInfo : ExprInfo .anon} {leftV rightV : Ix.Theory.Named.VExpr}, support (.letE leftName ty1 val1 body1 leftNondep leftInfo) → support (.letE rightName ty2 val2 body2 rightNondep rightInfo) → TrKExprS world.venv uvars world.nameOf trProj Delta @@ -212,7 +212,7 @@ theorem isDefEqWhnf_wf {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {state : TcState .anon} - {left right : KExpr .anon} {leftV rightV : Lean4Lean.VExpr} + {left right : KExpr .anon} {leftV rightV : Ix.Theory.Named.VExpr} (hstructural : TryDefEqWhnfStructural.WFAt layer semantics trProj world support uvars) (htail : IsDefEqWhnfAfterStructural.WFAt layer semantics trProj world @@ -255,4 +255,4 @@ end IsDefEqWhnf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/FinalWhnf/EtaExpansion.lean b/Ix/Kernel/Verify/DefEq/FinalWhnf/EtaExpansion.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/FinalWhnf/EtaExpansion.lean rename to Ix/Kernel/Verify/DefEq/FinalWhnf/EtaExpansion.lean index 314f923cc..c61d22b4c 100644 --- a/Ix/Tc/Verify/DefEq/FinalWhnf/EtaExpansion.lean +++ b/Ix/Kernel/Verify/DefEq/FinalWhnf/EtaExpansion.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.FinalWhnf.Contracts -import Ix.Tc.Verify.DefEq.ProofIrrelevance +import Ix.Kernel.Verify.DefEq.FinalWhnf.Contracts +import Ix.Kernel.Verify.DefEq.ProofIrrelevance /-! # Final-WHNF lambda eta @@ -12,9 +12,9 @@ cover the exact lift footprint and generated syntax; no semantic eta callback is assumed. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- Finite walker and generated-node closure for lambda eta. -/ structure FinalWhnfEtaResources (support : RunSupport) : Prop where @@ -55,7 +55,7 @@ theorem lift_whnf_wf_of_resources InternUpdateFrame state after) := TcM.runIntern_whnf_wf (fun intern hwf hsupport => by - have post := Ix.Tc.lift_spec hcollision.expr hbounds.1 hbounds.2.1 + have post := Ix.Kernel.lift_spec hcollision.expr hbounds.1 hbounds.2.1 hreach hwf hsupport.expr exact ⟨post.1, post.2.1, hsupport.of_expr_univs post.2.2 @@ -421,4 +421,4 @@ end IsDefEqWhnfAfterNat end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/FinalWhnf/LetDeclaration.lean b/Ix/Kernel/Verify/DefEq/FinalWhnf/LetDeclaration.lean similarity index 99% rename from Ix/Tc/Verify/DefEq/FinalWhnf/LetDeclaration.lean rename to Ix/Kernel/Verify/DefEq/FinalWhnf/LetDeclaration.lean index 5445f31de..0814bea5c 100644 --- a/Ix/Tc/Verify/DefEq/FinalWhnf/LetDeclaration.lean +++ b/Ix/Kernel/Verify/DefEq/FinalWhnf/LetDeclaration.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.FinalWhnf.Contracts -import Ix.Tc.Verify.Infer.LetScopes +import Ix.Kernel.Verify.DefEq.FinalWhnf.Contracts +import Ix.Kernel.Verify.Infer.LetScopes /-! # Final-WHNF let-declaration comparison @@ -11,9 +11,9 @@ opened bodies recursively. This module verifies that exact scoped program, including allocation failure and scope restoration on callback errors. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- Finite constructor descent and body-opening coverage for supported lets. The right body is opened with the left let's display name, so body resources @@ -449,4 +449,4 @@ end TryDefEqWhnfLet end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/FinalWhnf/NatBridge.lean b/Ix/Kernel/Verify/DefEq/FinalWhnf/NatBridge.lean similarity index 97% rename from Ix/Tc/Verify/DefEq/FinalWhnf/NatBridge.lean rename to Ix/Kernel/Verify/DefEq/FinalWhnf/NatBridge.lean index 34562eb1b..47dc6cde8 100644 --- a/Ix/Tc/Verify/DefEq/FinalWhnf/NatBridge.lean +++ b/Ix/Kernel/Verify/DefEq/FinalWhnf/NatBridge.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.FinalWhnf.Contracts -import Ix.Tc.Verify.DefEq.NatOffset +import Ix.Kernel.Verify.DefEq.FinalWhnf.Contracts +import Ix.Kernel.Verify.DefEq.NatOffset /-! # Final-WHNF Nat bridge @@ -11,9 +11,9 @@ predecessor equality can therefore be lifted through `Nat.succ` without assuming injectivity or reflection beyond the trusted primitive table. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- Trusted primitive facts and finite support needed by the final Nat comparison. -/ @@ -41,9 +41,9 @@ private theorem finalNatLit_hasType obtain ⟨ci, hlookup⟩ := htable.natZero.contains hcatalog have hci := hprims.natZero hlookup subst ci - exact Lean4Lean.VEnv.HasType.const hlookup (by simp) rfl + exact Ix.Theory.Named.VEnv.HasType.const hlookup (by simp) rfl | n + 1 => - Lean4Lean.VEnv.HasType.app + Ix.Theory.Named.VEnv.HasType.app (natSucc_hasType hcatalog htable hprims) (finalNatLit_hasType hcatalog htable hprims n) @@ -135,7 +135,7 @@ theorem natSuccOf_wf have hpredTr : TrKExprS world.venv uvars world.nameOf trProj Delta (natExprFromValue predecessor : KExpr .anon) (.natLit predecessor) := by - exact .nat (by simpa [Lean4Lean.VEnv.ContainsLits] using + exact .nat (by simpa [Ix.Theory.Named.VEnv.ContainsLits] using resources.natContains) have hpredType : world.venv.HasType uvars Delta.toCtx (.natLit predecessor) .nat := @@ -240,7 +240,7 @@ theorem isDefEqNatAfterLiteral_wf have hrightValue := hrightZero rfl subst leftV subst rightV - exact Lean4Lean.VEnv.IsDefEqU.refl <| + exact Ix.Theory.Named.VEnv.IsDefEqU.refl <| hleft.wf world.venvWF.ordered theory.literalWF theory.projections.wf hI.2.1.wf | false => @@ -346,7 +346,7 @@ theorem isDefEqNat_wf exact RecM.WF.pure fun hI hanswer => by have hvalue := eq_of_beq hanswer subst_vars - exact Lean4Lean.VEnv.IsDefEqU.refl <| + exact Ix.Theory.Named.VEnv.IsDefEqU.refl <| hleftTr.wf world.venvWF.ordered theory.literalWF theory.projections.wf hI.2.1.wf @@ -452,4 +452,4 @@ end IsDefEqWhnfAfterStructural end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/FinalWhnf/ProofTail.lean b/Ix/Kernel/Verify/DefEq/FinalWhnf/ProofTail.lean similarity index 97% rename from Ix/Tc/Verify/DefEq/FinalWhnf/ProofTail.lean rename to Ix/Kernel/Verify/DefEq/FinalWhnf/ProofTail.lean index 019226425..55a884362 100644 --- a/Ix/Tc/Verify/DefEq/FinalWhnf/ProofTail.lean +++ b/Ix/Kernel/Verify/DefEq/FinalWhnf/ProofTail.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.FinalWhnf.Contracts -import Ix.Tc.Verify.DefEq.ProofIrrelevance +import Ix.Kernel.Verify.DefEq.FinalWhnf.Contracts +import Ix.Kernel.Verify.DefEq.ProofIrrelevance /-! # Final-WHNF proof-irrelevance tail @@ -9,9 +9,9 @@ module packages it at the final-WHNF seam and composes it with an independently verified unit-like shortcut. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -144,4 +144,4 @@ end IsDefEqWhnfAfterString end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/FinalWhnf/StringExpansion.lean b/Ix/Kernel/Verify/DefEq/FinalWhnf/StringExpansion.lean similarity index 97% rename from Ix/Tc/Verify/DefEq/FinalWhnf/StringExpansion.lean rename to Ix/Kernel/Verify/DefEq/FinalWhnf/StringExpansion.lean index c23d6562e..82744e37e 100644 --- a/Ix/Tc/Verify/DefEq/FinalWhnf/StringExpansion.lean +++ b/Ix/Kernel/Verify/DefEq/FinalWhnf/StringExpansion.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.FinalWhnf.Contracts -import Ix.Tc.Verify.DefEq.StringLiteral +import Ix.Kernel.Verify.DefEq.FinalWhnf.Contracts +import Ix.Kernel.Verify.DefEq.StringLiteral /-! # Final-WHNF String-literal expansion @@ -9,9 +9,9 @@ final-WHNF comparator. Each compact literal is expanded by the exact K1 plan, whose result translates to the same Theory literal as the source syntax. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -148,4 +148,4 @@ end IsDefEqWhnfAfterEta end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/FinalWhnf/StructuralPrefix.lean b/Ix/Kernel/Verify/DefEq/FinalWhnf/StructuralPrefix.lean similarity index 95% rename from Ix/Tc/Verify/DefEq/FinalWhnf/StructuralPrefix.lean rename to Ix/Kernel/Verify/DefEq/FinalWhnf/StructuralPrefix.lean index 8a48063bd..0627c4c00 100644 --- a/Ix/Tc/Verify/DefEq/FinalWhnf/StructuralPrefix.lean +++ b/Ix/Kernel/Verify/DefEq/FinalWhnf/StructuralPrefix.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.FinalWhnf.Application -import Ix.Tc.Verify.DefEq.FinalWhnf.LetDeclaration +import Ix.Kernel.Verify.DefEq.FinalWhnf.Application +import Ix.Kernel.Verify.DefEq.FinalWhnf.LetDeclaration /-! # Final-WHNF structural prefix @@ -11,9 +11,9 @@ contract so its allocation and dual-body opening proof can be discharged in isolation. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -75,7 +75,7 @@ theorem tryDefEqWhnfStructural_wf (hleftLookup.symm.trans hrightLookup) have hvalue : leftV = rightV := congrArg Prod.fst hp subst rightV - exact Lean4Lean.VEnv.IsDefEqU.refl hleftWF + exact Ix.Theory.Named.VEnv.IsDefEqU.refl hleftWF · rename_i leftU leftInfo rightU rightInfo cases hleft with | sort hleftWF => @@ -153,7 +153,7 @@ theorem tryDefEqWhnfStructural_wf hI.2.1.wf cases hleft cases hright - exact Lean4Lean.VEnv.IsDefEqU.refl hleftWF + exact Ix.Theory.Named.VEnv.IsDefEqU.refl hleftWF · rename_i leftString leftBlob leftInfo rightString rightBlob rightInfo cases hvalue : leftString == rightString with | false => @@ -167,7 +167,7 @@ theorem tryDefEqWhnfStructural_wf hI.2.1.wf cases hleft cases hright - exact Lean4Lean.VEnv.IsDefEqU.refl hleftWF + exact Ix.Theory.Named.VEnv.IsDefEqU.refl hleftWF namespace TryDefEqWhnfStructural @@ -189,4 +189,4 @@ end TryDefEqWhnfStructural end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/FinalWhnf/StructureEta.lean b/Ix/Kernel/Verify/DefEq/FinalWhnf/StructureEta.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/FinalWhnf/StructureEta.lean rename to Ix/Kernel/Verify/DefEq/FinalWhnf/StructureEta.lean index b29b620ff..c21041c10 100644 --- a/Ix/Tc/Verify/DefEq/FinalWhnf/StructureEta.lean +++ b/Ix/Kernel/Verify/DefEq/FinalWhnf/StructureEta.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.DefEq.FinalWhnf.StructureEtaTail -import Ix.Tc.Verify.Infer.Constants -import Ix.Tc.Verify.Infer.ProjectionTypes +import Ix.Kernel.Verify.DefEq.FinalWhnf.StructureEtaTail +import Ix.Kernel.Verify.Infer.Constants +import Ix.Kernel.Verify.Infer.ProjectionTypes /-! # Final-WHNF structure eta @@ -13,9 +13,9 @@ Theory boundary consumes that token together with the exact constructor metadata, typing derivations, and field equations selected by production. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- Exact constructor fields retained from the declaration returned by the production lookup. -/ @@ -156,7 +156,7 @@ theorem normalizeEtaStructSource_wf simp only exact RecM.WF.pure fun hI => ⟨hsourceSupport, sourceV, hsource, - Lean4Lean.VEnv.IsDefEqU.refl (theory.exprWF hI.2.1 hsource)⟩ + Ix.Theory.Named.VEnv.IsDefEqU.refl (theory.exprWF hI.2.1 hsource)⟩ /-- Exhaust the size check, structure classifier, both caught inference calls, inferred-type comparison, and the verified structure-eta tail for one @@ -504,4 +504,4 @@ end TryDefEqWhnfStructEta end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/FinalWhnf/StructureEtaBase.lean b/Ix/Kernel/Verify/DefEq/FinalWhnf/StructureEtaBase.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/FinalWhnf/StructureEtaBase.lean rename to Ix/Kernel/Verify/DefEq/FinalWhnf/StructureEtaBase.lean index c8451cf2f..b684ebbdc 100644 --- a/Ix/Tc/Verify/DefEq/FinalWhnf/StructureEtaBase.lean +++ b/Ix/Kernel/Verify/DefEq/FinalWhnf/StructureEtaBase.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.FinalWhnf.StructureEtaFields -import Ix.Tc.Verify.DefEq.CheapReduction +import Ix.Kernel.Verify.DefEq.FinalWhnf.StructureEtaFields +import Ix.Kernel.Verify.DefEq.CheapReduction /-! # Structure-eta common-base scan @@ -12,9 +12,9 @@ state. A successful result carries the semantic projection equality for each scanned field. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- Invert the structural translation of one concrete projection while retaining the resolved structure name and raw projection witness. -/ @@ -304,7 +304,7 @@ theorem etaExpansionBaseLoop_wf | none => have hvalueRefl : world.venv.IsDefEqU uvars Delta.toCtx valueV valueV := - Lean4Lean.VEnv.IsDefEqU.refl + Ix.Theory.Named.VEnv.IsDefEqU.refl (theory.exprWF hIValue.2.1 hvalueTr) exact hcontinue hvalueSupport hvalueTr hvalueRefl | some chosen => @@ -359,4 +359,4 @@ theorem etaExpansionBase_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/FinalWhnf/StructureEtaFields.lean b/Ix/Kernel/Verify/DefEq/FinalWhnf/StructureEtaFields.lean similarity index 97% rename from Ix/Tc/Verify/DefEq/FinalWhnf/StructureEtaFields.lean rename to Ix/Kernel/Verify/DefEq/FinalWhnf/StructureEtaFields.lean index d291cb45f..988e97f0c 100644 --- a/Ix/Tc/Verify/DefEq/FinalWhnf/StructureEtaFields.lean +++ b/Ix/Kernel/Verify/DefEq/FinalWhnf/StructureEtaFields.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.FinalWhnf.Contracts +import Ix.Kernel.Verify.DefEq.FinalWhnf.Contracts /-! # Structure-eta field comparison @@ -11,9 +11,9 @@ structure metadata is not assumed here and remains the responsibility of the outer constructor/classifier proof. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -113,4 +113,4 @@ theorem tryEtaStructFields_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/FinalWhnf/StructureEtaTail.lean b/Ix/Kernel/Verify/DefEq/FinalWhnf/StructureEtaTail.lean similarity index 97% rename from Ix/Tc/Verify/DefEq/FinalWhnf/StructureEtaTail.lean rename to Ix/Kernel/Verify/DefEq/FinalWhnf/StructureEtaTail.lean index 0b63558ef..646e4f5fa 100644 --- a/Ix/Tc/Verify/DefEq/FinalWhnf/StructureEtaTail.lean +++ b/Ix/Kernel/Verify/DefEq/FinalWhnf/StructureEtaTail.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.FinalWhnf.StructureEtaBase +import Ix.Kernel.Verify.DefEq.FinalWhnf.StructureEtaBase /-! # Structure-eta tail after type agreement @@ -9,9 +9,9 @@ types. The only semantic input is an eta law indexed by the exact field projection equations proved by those loops. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- Exact semantic continuation needed after all structure fields agree. The outer constructor/classifier proof supplies this from the trusted @@ -129,4 +129,4 @@ theorem tryEtaStructAfterTypes_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/FinalWhnf/UnitLike.lean b/Ix/Kernel/Verify/DefEq/FinalWhnf/UnitLike.lean similarity index 96% rename from Ix/Tc/Verify/DefEq/FinalWhnf/UnitLike.lean rename to Ix/Kernel/Verify/DefEq/FinalWhnf/UnitLike.lean index 83418c0ad..e6b5410cb 100644 --- a/Ix/Tc/Verify/DefEq/FinalWhnf/UnitLike.lean +++ b/Ix/Kernel/Verify/DefEq/FinalWhnf/UnitLike.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.DefEq.FinalWhnf.ProofTail -import Ix.Tc.Verify.Infer.Constants -import Ix.Tc.Verify.Whnf.StructEta.RecursionClassifier +import Ix.Kernel.Verify.DefEq.FinalWhnf.ProofTail +import Ix.Kernel.Verify.Infer.Constants +import Ix.Kernel.Verify.Whnf.StructEta.RecursionClassifier /-! # Final-WHNF unit-like equality @@ -8,15 +8,15 @@ import Ix.Tc.Verify.Whnf.StructEta.RecursionClassifier The operational classifier is proved exhaustively against the immutable catalog. Its semantic conclusion uses one deliberately narrow inductive law: inhabitants of a type headed by a trusted zero-index inductive with one -nullary constructor are definitionally equal. Lean4Lean's current +nullary constructor are definitionally equal. Ix.Theory.Named's current `VEnv.addInduct` interface does not expose that law, so it remains an explicit construction obligation for the inductive-theory bridge rather than being inferred from concrete metadata alone. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- Constructor metadata accepted by production's unit-like shortcut. -/ def KConst.IsNullaryConstructor : KConst .anon → Prop @@ -30,7 +30,7 @@ def KConst.IsUnitLikeInductive (catalog : Catalog) : KConst .anon → Prop ∃ ctor, catalog ctors[0]! = some ctor ∧ ctor.IsNullaryConstructor | _ => False -/-- Semantic inductive law missing from Lean4Lean's current `addInduct` +/-- Semantic inductive law missing from Ix.Theory.Named's current `addInduct` specification. It is indexed by the exact trusted catalog shape and by the actual structurally translated type selected by production. -/ structure FinalWhnfUnitTheory (trProj : RawProjRel) @@ -252,4 +252,4 @@ end TryDefEqUnit end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/LazyDelta.lean b/Ix/Kernel/Verify/DefEq/LazyDelta.lean similarity index 99% rename from Ix/Tc/Verify/DefEq/LazyDelta.lean rename to Ix/Kernel/Verify/DefEq/LazyDelta.lean index 9a92b1e44..57dd93a2b 100644 --- a/Ix/Tc/Verify/DefEq/LazyDelta.lean +++ b/Ix/Kernel/Verify/DefEq/LazyDelta.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.ProofIrrelevance +import Ix.Kernel.Verify.DefEq.ProofIrrelevance /-! # Bounded lazy-delta DefEq closure @@ -10,9 +10,9 @@ step and the stopped tail. Individual reduction branches discharge those contracts in subsequent modules. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -280,4 +280,4 @@ theorem DefEqAfterProofIrrelevance.ofLazyDelta end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/LazyDeltaClosure.lean b/Ix/Kernel/Verify/DefEq/LazyDeltaClosure.lean similarity index 93% rename from Ix/Tc/Verify/DefEq/LazyDeltaClosure.lean rename to Ix/Kernel/Verify/DefEq/LazyDeltaClosure.lean index c3a2f4125..1716ac4dc 100644 --- a/Ix/Tc/Verify/DefEq/LazyDeltaClosure.lean +++ b/Ix/Kernel/Verify/DefEq/LazyDeltaClosure.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.LazyDeltaIteration -import Ix.Tc.Verify.DefEq.StoppedContinuationClosure +import Ix.Kernel.Verify.DefEq.LazyDeltaIteration +import Ix.Kernel.Verify.DefEq.StoppedContinuationClosure /-! # Complete lazy-delta tier assembly @@ -9,7 +9,7 @@ for a stopped pair. This module joins those independently proved executable surfaces under the canonical K2 suffix/cache model. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -58,4 +58,4 @@ end DefEqAfterProofIrrelevance end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/LazyDeltaIteration.lean b/Ix/Kernel/Verify/DefEq/LazyDeltaIteration.lean similarity index 97% rename from Ix/Tc/Verify/DefEq/LazyDeltaIteration.lean rename to Ix/Kernel/Verify/DefEq/LazyDeltaIteration.lean index 82913e244..bf6b3180a 100644 --- a/Ix/Tc/Verify/DefEq/LazyDeltaIteration.lean +++ b/Ix/Kernel/Verify/DefEq/LazyDeltaIteration.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.EqualRankPrefix -import Ix.Tc.Verify.DefEq.NatOffsetDecomposition +import Ix.Kernel.Verify.DefEq.EqualRankPrefix +import Ix.Kernel.Verify.DefEq.NatOffsetDecomposition /-! # Complete lazy-delta iteration assembly @@ -14,7 +14,7 @@ DefEq, and finite run-scoped resources for same-head comparison and cache writes. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -108,4 +108,4 @@ end DefEqLazyDeltaStep end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/LoopFinish.lean b/Ix/Kernel/Verify/DefEq/LoopFinish.lean similarity index 95% rename from Ix/Tc/Verify/DefEq/LoopFinish.lean rename to Ix/Kernel/Verify/DefEq/LoopFinish.lean index 79e1411de..18a1926f0 100644 --- a/Ix/Tc/Verify/DefEq/LoopFinish.lean +++ b/Ix/Kernel/Verify/DefEq/LoopFinish.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.ProjectionProbe +import Ix.Kernel.Verify.DefEq.ProjectionProbe /-! # Lazy-delta loop finishing checks @@ -9,9 +9,9 @@ the bounded driver. This module discharges both accepting checks against the current pair and transports their result back through the loop invariant. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -63,4 +63,4 @@ theorem finishDefEqLazyDeltaStep_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/NatOffset.lean b/Ix/Kernel/Verify/DefEq/NatOffset.lean similarity index 96% rename from Ix/Tc/Verify/DefEq/NatOffset.lean rename to Ix/Kernel/Verify/DefEq/NatOffset.lean index 5aeee1ed3..103066960 100644 --- a/Ix/Tc/Verify/DefEq/NatOffset.lean +++ b/Ix/Kernel/Verify/DefEq/NatOffset.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.LazyDelta +import Ix.Kernel.Verify.DefEq.LazyDelta /-! # Nat-offset comparison @@ -9,9 +9,9 @@ the literal case and leaves the latter path behind a separately named contract. In particular, no negative result is assigned semantic meaning. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -182,7 +182,7 @@ theorem ofCandidates {uvars : Nat} (hafter : TryDefEqOffsetAfterCandidates.WFAt layer semantics trProj world support uvars) : - Ix.Tc.RecM.TryDefEqOffsetAfterZeroMiss.WFAt layer semantics trProj world + Ix.Kernel.RecM.TryDefEqOffsetAfterZeroMiss.WFAt layer semantics trProj world support uvars := by intro Delta state left right leftV rightV hleftSupport hrightSupport hleft hright @@ -235,7 +235,7 @@ theorem tryDefEqOffsetAfterLiteral_wf have hrightValue := hrightZero rfl subst leftV subst rightV - exact Lean4Lean.VEnv.IsDefEqU.refl hleftWF + exact Ix.Theory.Named.VEnv.IsDefEqU.refl hleftWF namespace TryDefEqOffsetAfterLiteral @@ -248,7 +248,7 @@ theorem ofZero (hzero : IsNatZero.WFAt layer semantics trProj world support uvars) (hafter : TryDefEqOffsetAfterZeroMiss.WFAt layer semantics trProj world support uvars) : - Ix.Tc.RecM.TryDefEqOffsetAfterLiteral.WFAt layer semantics trProj world + Ix.Kernel.RecM.TryDefEqOffsetAfterLiteral.WFAt layer semantics trProj world support uvars := by intro Delta state left right leftV rightV hleftSupport hrightSupport hleft hright @@ -296,7 +296,7 @@ theorem tryDefEqOffset_wf exact RecM.WF.pure fun _ hanswer => by have hvalues := eq_of_beq hanswer cases hvalues - exact Lean4Lean.VEnv.IsDefEqU.refl hleftWF + exact Ix.Theory.Named.VEnv.IsDefEqU.refl hleftWF namespace TryDefEqOffset @@ -308,7 +308,7 @@ theorem ofAfterLiteral (theory : WhnfTheory trProj world uvars) (hafter : TryDefEqOffsetAfterLiteral.WFAt layer semantics trProj world support uvars) : - Ix.Tc.RecM.TryDefEqOffset.WFAt layer semantics trProj world support + Ix.Kernel.RecM.TryDefEqOffset.WFAt layer semantics trProj world support uvars := by intro Delta state left right leftV rightV hleftSupport hrightSupport hleft hright @@ -325,7 +325,7 @@ theorem ofContext (zeroContext : NatZeroContext world) (hafter : TryDefEqOffsetAfterCandidates.WFAt .noAccel semantics trProj world support uvars) : - Ix.Tc.RecM.TryDefEqOffset.WFAt .noAccel semantics trProj world support + Ix.Kernel.RecM.TryDefEqOffset.WFAt .noAccel semantics trProj world support uvars := ofAfterLiteral theory <| TryDefEqOffsetAfterLiteral.ofZero theory @@ -336,4 +336,4 @@ end TryDefEqOffset end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/NatOffsetDecomposition.lean b/Ix/Kernel/Verify/DefEq/NatOffsetDecomposition.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/NatOffsetDecomposition.lean rename to Ix/Kernel/Verify/DefEq/NatOffsetDecomposition.lean index 5fb8ce726..1d6d5d58e 100644 --- a/Ix/Tc/Verify/DefEq/NatOffsetDecomposition.lean +++ b/Ix/Kernel/Verify/DefEq/NatOffsetDecomposition.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.NatOffset -import Ix.Tc.Verify.Whnf.Iota.NatOffset +import Ix.Kernel.Verify.DefEq.NatOffset +import Ix.Kernel.Verify.Whnf.Iota.NatOffset /-! # Nat-offset decomposition and reconstruction @@ -15,9 +15,9 @@ about successful parser/rebuilder executions. The latter is indexed by the exact production runs, so it cannot authorize an unrelated generated term. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace TcM.WF @@ -274,4 +274,4 @@ end TryDefEqOffsetAfterCandidates end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/NatReduction.lean b/Ix/Kernel/Verify/DefEq/NatReduction.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/NatReduction.lean rename to Ix/Kernel/Verify/DefEq/NatReduction.lean index 6327d751a..d64d279c0 100644 --- a/Ix/Tc/Verify/DefEq/NatReduction.lean +++ b/Ix/Kernel/Verify/DefEq/NatReduction.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.NatOffset +import Ix.Kernel.Verify.DefEq.NatOffset /-! # Lazy-delta Nat reduction @@ -9,9 +9,9 @@ against the opposite operand. This module composes the existing optional reducer and predecessor DefEq contracts with the lazy-delta pair invariant. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -127,4 +127,4 @@ end DefEqLazyDeltaAfterOffsetMiss end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/OneSidedDelta.lean b/Ix/Kernel/Verify/DefEq/OneSidedDelta.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/OneSidedDelta.lean rename to Ix/Kernel/Verify/DefEq/OneSidedDelta.lean index b6c5c8509..cdc0e215c 100644 --- a/Ix/Tc/Verify/DefEq/OneSidedDelta.lean +++ b/Ix/Kernel/Verify/DefEq/OneSidedDelta.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.LoopFinish +import Ix.Kernel.Verify.DefEq.LoopFinish /-! # One-sided lazy-delta unfolding @@ -9,9 +9,9 @@ finishing checks. This module gives those shared production helpers their complete pair-invariant contracts. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -116,4 +116,4 @@ theorem defEqLazyDeltaStepWithRightDelta_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/ProjectionDeltaActive.lean b/Ix/Kernel/Verify/DefEq/ProjectionDeltaActive.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/ProjectionDeltaActive.lean rename to Ix/Kernel/Verify/DefEq/ProjectionDeltaActive.lean index 761d03ecb..dab1c786f 100644 --- a/Ix/Tc/Verify/DefEq/ProjectionDeltaActive.lean +++ b/Ix/Kernel/Verify/DefEq/ProjectionDeltaActive.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.ProjectionDeltaRank +import Ix.Kernel.Verify.DefEq.ProjectionDeltaRank /-! # Active projection-delta branches @@ -10,9 +10,9 @@ unfold, normalization, and finishing proofs into the exact lower-step contract used by the bounded projection driver. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -133,4 +133,4 @@ end LazyDeltaReductionStep end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/ProjectionDeltaClosure.lean b/Ix/Kernel/Verify/DefEq/ProjectionDeltaClosure.lean similarity index 97% rename from Ix/Tc/Verify/DefEq/ProjectionDeltaClosure.lean rename to Ix/Kernel/Verify/DefEq/ProjectionDeltaClosure.lean index 35d6e2d1c..c2e8f645e 100644 --- a/Ix/Tc/Verify/DefEq/ProjectionDeltaClosure.lean +++ b/Ix/Kernel/Verify/DefEq/ProjectionDeltaClosure.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.ProjectionDeltaActive -import Ix.Tc.Verify.DefEq.ProjectionProbe +import Ix.Kernel.Verify.DefEq.ProjectionDeltaActive +import Ix.Kernel.Verify.DefEq.ProjectionProbe /-! # Projection-directed delta closure @@ -15,7 +15,7 @@ left here is `DirectProjectionReflection`, indexed by the exact successful execution of `tryProjReduce`. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -121,4 +121,4 @@ end TryStructuralCongruence end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/ProjectionDeltaEqualRank.lean b/Ix/Kernel/Verify/DefEq/ProjectionDeltaEqualRank.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/ProjectionDeltaEqualRank.lean rename to Ix/Kernel/Verify/DefEq/ProjectionDeltaEqualRank.lean index dbb267250..4ccd66b7d 100644 --- a/Ix/Tc/Verify/DefEq/ProjectionDeltaEqualRank.lean +++ b/Ix/Kernel/Verify/DefEq/ProjectionDeltaEqualRank.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.EqualRankCache -import Ix.Tc.Verify.DefEq.ProjectionDeltaUnfolding +import Ix.Kernel.Verify.DefEq.EqualRankCache +import Ix.Kernel.Verify.DefEq.ProjectionDeltaUnfolding /-! # Equal-rank projection-delta reduction @@ -12,9 +12,9 @@ iteration is intentionally absent here; this proof follows the actual compact helper. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -183,4 +183,4 @@ theorem lazyDeltaReductionStepWithEqualRank_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/ProjectionDeltaFinish.lean b/Ix/Kernel/Verify/DefEq/ProjectionDeltaFinish.lean similarity index 96% rename from Ix/Tc/Verify/DefEq/ProjectionDeltaFinish.lean rename to Ix/Kernel/Verify/DefEq/ProjectionDeltaFinish.lean index 7dfe06a60..298e94a1f 100644 --- a/Ix/Tc/Verify/DefEq/ProjectionDeltaFinish.lean +++ b/Ix/Kernel/Verify/DefEq/ProjectionDeltaFinish.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.ProjectionDeltaStep +import Ix.Kernel.Verify.DefEq.ProjectionDeltaStep /-! # Finishing a productive projection-directed delta step @@ -9,9 +9,9 @@ reports equality or schedules the transformed pair for another bounded iteration. This module proves that shared finish once. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -64,4 +64,4 @@ theorem finishLazyDeltaReductionStep_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/ProjectionDeltaLoop.lean b/Ix/Kernel/Verify/DefEq/ProjectionDeltaLoop.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/ProjectionDeltaLoop.lean rename to Ix/Kernel/Verify/DefEq/ProjectionDeltaLoop.lean index 19e0874b2..e23100fc8 100644 --- a/Ix/Tc/Verify/DefEq/ProjectionDeltaLoop.lean +++ b/Ix/Kernel/Verify/DefEq/ProjectionDeltaLoop.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.StructuralCongruence +import Ix.Kernel.Verify.DefEq.StructuralCongruence /-! # Projection-directed lazy-delta loop @@ -10,9 +10,9 @@ final recursive comparison. This module proves the bounded driver from exact contracts for those two lower helpers. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- A supported projection node exposes its value to the projection-directed loop. -/ @@ -228,4 +228,4 @@ end LazyDeltaProjReduction end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/ProjectionDeltaRank.lean b/Ix/Kernel/Verify/DefEq/ProjectionDeltaRank.lean similarity index 95% rename from Ix/Tc/Verify/DefEq/ProjectionDeltaRank.lean rename to Ix/Kernel/Verify/DefEq/ProjectionDeltaRank.lean index f36da90b9..b28130a5f 100644 --- a/Ix/Tc/Verify/DefEq/ProjectionDeltaRank.lean +++ b/Ix/Kernel/Verify/DefEq/ProjectionDeltaRank.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.ProjectionDeltaEqualRank +import Ix.Kernel.Verify.DefEq.ProjectionDeltaEqualRank /-! # Projection-delta rank dispatch @@ -9,9 +9,9 @@ Rank values carry no semantic authority: every selected helper is proved sound independently. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -68,4 +68,4 @@ theorem lazyDeltaReductionStepWithBothDelta_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/ProjectionDeltaStep.lean b/Ix/Kernel/Verify/DefEq/ProjectionDeltaStep.lean similarity index 96% rename from Ix/Tc/Verify/DefEq/ProjectionDeltaStep.lean rename to Ix/Kernel/Verify/DefEq/ProjectionDeltaStep.lean index a95be5e66..49f579a13 100644 --- a/Ix/Tc/Verify/DefEq/ProjectionDeltaStep.lean +++ b/Ix/Kernel/Verify/DefEq/ProjectionDeltaStep.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.DeltaClassification -import Ix.Tc.Verify.DefEq.ProjectionReduction +import Ix.Kernel.Verify.DefEq.DeltaClassification +import Ix.Kernel.Verify.DefEq.ProjectionReduction /-! # Projection-directed delta step @@ -12,7 +12,7 @@ success, absence, and partial-error behavior through the installed anonymous lazy-ingress contract. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -54,7 +54,7 @@ theorem lazyDeltaReductionStepAfterClassification_wf {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {state : TcState .anon} - {leftSource rightSource : Lean4Lean.VExpr} + {leftSource rightSource : Ix.Theory.Named.VExpr} {left right : KExpr .anon} {aHead bHead : Option (KId .anon)} {aDelta bDelta : Bool} (hactive : LazyDeltaReductionAfterActive.WFAt layer semantics trProj @@ -99,7 +99,7 @@ theorem lazyDeltaReductionStep_wf {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {state : TcState .anon} - {leftSource rightSource : Lean4Lean.VExpr} + {leftSource rightSource : Ix.Theory.Named.VExpr} {left right : KExpr .anon} (ingress : AnonLazyIngressContext .noAccel semantics trProj world support) @@ -151,4 +151,4 @@ end LazyDeltaReductionStep end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/ProjectionDeltaUnfolding.lean b/Ix/Kernel/Verify/DefEq/ProjectionDeltaUnfolding.lean similarity index 97% rename from Ix/Tc/Verify/DefEq/ProjectionDeltaUnfolding.lean rename to Ix/Kernel/Verify/DefEq/ProjectionDeltaUnfolding.lean index 425ab6fa7..430bc046e 100644 --- a/Ix/Tc/Verify/DefEq/ProjectionDeltaUnfolding.lean +++ b/Ix/Kernel/Verify/DefEq/ProjectionDeltaUnfolding.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.ProjectionDeltaFinish +import Ix.Kernel.Verify.DefEq.ProjectionDeltaFinish /-! # One-sided projection-delta unfolding @@ -9,9 +9,9 @@ finish. The two theorems here cover both directions, including unfold misses and errors from either helper. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -111,4 +111,4 @@ theorem lazyDeltaReductionStepWithRightDelta_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/ProjectionProbe.lean b/Ix/Kernel/Verify/DefEq/ProjectionProbe.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/ProjectionProbe.lean rename to Ix/Kernel/Verify/DefEq/ProjectionProbe.lean index 5e7944929..09131a2a6 100644 --- a/Ix/Tc/Verify/DefEq/ProjectionProbe.lean +++ b/Ix/Kernel/Verify/DefEq/ProjectionProbe.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.DeltaClassification +import Ix.Kernel.Verify.DefEq.DeltaClassification /-! # Lazy-delta projection probe @@ -10,9 +10,9 @@ closes both asymmetric branches, transporting a successful projection result into the loop's pair invariant. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -148,4 +148,4 @@ end DefEqLazyDeltaAfterDeltaClassification end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/ProjectionReduction.lean b/Ix/Kernel/Verify/DefEq/ProjectionReduction.lean similarity index 96% rename from Ix/Tc/Verify/DefEq/ProjectionReduction.lean rename to Ix/Kernel/Verify/DefEq/ProjectionReduction.lean index ec0ffd1df..bf151c577 100644 --- a/Ix/Tc/Verify/DefEq/ProjectionReduction.lean +++ b/Ix/Kernel/Verify/DefEq/ProjectionReduction.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.ProjectionDeltaLoop -import Ix.Tc.Verify.Whnf.Projection.NoAccelTail +import Ix.Kernel.Verify.DefEq.ProjectionDeltaLoop +import Ix.Kernel.Verify.Whnf.Projection.NoAccelTail /-! # Direct projection reduction inside DefEq @@ -12,9 +12,9 @@ helper execution. It therefore cannot authorize a different projection, input, result, method table, or pair of states. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -106,4 +106,4 @@ end TryProjReduce end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/ProofIrrelevance.lean b/Ix/Kernel/Verify/DefEq/ProofIrrelevance.lean similarity index 97% rename from Ix/Tc/Verify/DefEq/ProofIrrelevance.lean rename to Ix/Kernel/Verify/DefEq/ProofIrrelevance.lean index 0dc6175be..69fea4359 100644 --- a/Ix/Tc/Verify/DefEq/ProofIrrelevance.lean +++ b/Ix/Kernel/Verify/DefEq/ProofIrrelevance.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.CheapReduction -import Ix.Tc.Verify.Whnf.StructEta.CallbackPrefix +import Ix.Kernel.Verify.DefEq.CheapReduction +import Ix.Kernel.Verify.Whnf.StructEta.CallbackPrefix /-! # Pre-delta proof irrelevance @@ -10,9 +10,9 @@ types recursively. A positive result is justified by Theory proof irrelevance; caught callback errors remain ordinary misses. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -170,4 +170,4 @@ end DefEqAfterProofIrrelevance end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/PropositionClassifier.lean b/Ix/Kernel/Verify/DefEq/PropositionClassifier.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/PropositionClassifier.lean rename to Ix/Kernel/Verify/DefEq/PropositionClassifier.lean index ddc66951b..28d2118fa 100644 --- a/Ix/Tc/Verify/DefEq/PropositionClassifier.lean +++ b/Ix/Kernel/Verify/DefEq/PropositionClassifier.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.DefEq.ProofIrrelevance +import Ix.Kernel.Verify.DefEq.ProofIrrelevance + +open Ix.Theory (VLevel) /-! # Memoized proposition classification @@ -10,9 +12,9 @@ type with the direct K1 reducer, and install only a provenance-certified classification. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- Resources needed by the concrete proposition classifier. Direct WHNF is the already-closed K1 reducer; inference remains a predecessor-table edge @@ -226,4 +228,4 @@ theorem tryProofIrrel_classifier_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/RankDispatch.lean b/Ix/Kernel/Verify/DefEq/RankDispatch.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/RankDispatch.lean rename to Ix/Kernel/Verify/DefEq/RankDispatch.lean index b0cf3ee57..aabf7fc8c 100644 --- a/Ix/Tc/Verify/DefEq/RankDispatch.lean +++ b/Ix/Kernel/Verify/DefEq/RankDispatch.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.OneSidedDelta +import Ix.Kernel.Verify.DefEq.OneSidedDelta /-! # Lazy-delta rank dispatch @@ -10,9 +10,9 @@ independently. Their declaration lookups must nevertheless preserve the state invariant across lazy ingress. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -134,4 +134,4 @@ end DefEqLazyDeltaAfterProjectionMiss end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/SameHeadSpine.lean b/Ix/Kernel/Verify/DefEq/SameHeadSpine.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/SameHeadSpine.lean rename to Ix/Kernel/Verify/DefEq/SameHeadSpine.lean index 85c4dba3c..5dbd00743 100644 --- a/Ix/Tc/Verify/DefEq/SameHeadSpine.lean +++ b/Ix/Kernel/Verify/DefEq/SameHeadSpine.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.DefEq.SpineArguments +import Ix.Kernel.Verify.DefEq.SpineArguments + +open Ix.Theory (VLevel) /-! # Same-head constant spines @@ -9,9 +11,9 @@ comparison, and successful recursive comparisons of every raw argument are lifted through the complete typed application spine. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr VLevel) +open Ix.Theory.Named (VExpr) /-- Finite support coverage needed by constant-headed spine comparison. -/ structure SameHeadSpineResources (support : RunSupport) : Prop where @@ -143,7 +145,7 @@ theorem constantHeadsDefEq cases hconst obtain ⟨hlength, hpairs⟩ := sameDefEqUniverses_sound hcollision hleftLevels hrightLevels hlevels - refine ⟨_, Lean4Lean.VEnv.IsDefEq.constDF hleftConst ?_ ?_ ?_ ?_⟩ + refine ⟨_, Ix.Theory.Named.VEnv.IsDefEq.constDF hleftConst ?_ ?_ ?_ ?_⟩ · intro level hmem obtain ⟨raw, hraw, rfl⟩ := List.mem_map.mp hmem exact hleftWF raw (by simpa using hraw) @@ -284,4 +286,4 @@ end TrySameHeadSpine end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/SpineArguments.lean b/Ix/Kernel/Verify/DefEq/SpineArguments.lean similarity index 97% rename from Ix/Tc/Verify/DefEq/SpineArguments.lean rename to Ix/Kernel/Verify/DefEq/SpineArguments.lean index 78cac35f7..d23cdccd9 100644 --- a/Ix/Tc/Verify/DefEq/SpineArguments.lean +++ b/Ix/Kernel/Verify/DefEq/SpineArguments.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.EqualRankReduction +import Ix.Kernel.Verify.DefEq.EqualRankReduction /-! # Recursive application-spine arguments @@ -8,9 +8,9 @@ one left-to-right recursive DefEq loop. This module proves that loop once and gives its positive result a compositional Theory meaning. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -204,10 +204,10 @@ theorem defEq_of_zip hleftArgTr hrightArgTr have hlastTyped := hlastEq.of_l world.venvWF hDelta.toCtx hleftArg - exact ⟨_, Lean4Lean.VEnv.IsDefEq.appDF hcurrentTyped hlastTyped⟩ + exact ⟨_, Ix.Theory.Named.VEnv.IsDefEq.appDF hcurrentTyped hlastTyped⟩ end TrAppSpine end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/StoppedContinuation.lean b/Ix/Kernel/Verify/DefEq/StoppedContinuation.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/StoppedContinuation.lean rename to Ix/Kernel/Verify/DefEq/StoppedContinuation.lean index dde2cdbc3..67b77796d 100644 --- a/Ix/Tc/Verify/DefEq/StoppedContinuation.lean +++ b/Ix/Kernel/Verify/DefEq/StoppedContinuation.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.ApplicationSpine -import Ix.Tc.Verify.DefEq.StructuralCongruence +import Ix.Kernel.Verify.DefEq.ApplicationSpine +import Ix.Kernel.Verify.DefEq.StructuralCongruence /-! # Stopped lazy-delta continuation @@ -11,9 +11,9 @@ and the final WHNF comparator in that order. This module proves that exact outer control flow from contracts for its substantive helpers. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM @@ -190,4 +190,4 @@ end DefEqAfterLazyDeltaStopped end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/StoppedContinuationClosure.lean b/Ix/Kernel/Verify/DefEq/StoppedContinuationClosure.lean similarity index 94% rename from Ix/Tc/Verify/DefEq/StoppedContinuationClosure.lean rename to Ix/Kernel/Verify/DefEq/StoppedContinuationClosure.lean index 9a0839005..87cb2d2e3 100644 --- a/Ix/Tc/Verify/DefEq/StoppedContinuationClosure.lean +++ b/Ix/Kernel/Verify/DefEq/StoppedContinuationClosure.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.ProjectionDeltaClosure -import Ix.Tc.Verify.DefEq.StoppedContinuation +import Ix.Kernel.Verify.DefEq.ProjectionDeltaClosure +import Ix.Kernel.Verify.DefEq.StoppedContinuation /-! # Stopped-continuation closure @@ -10,7 +10,7 @@ comparison. This module constructs that resource record with the structural projection branch supplied by the concrete projection-delta closure. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -67,4 +67,4 @@ end DefEqAfterLazyDeltaStopped end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/StringLiteral.lean b/Ix/Kernel/Verify/DefEq/StringLiteral.lean similarity index 98% rename from Ix/Tc/Verify/DefEq/StringLiteral.lean rename to Ix/Kernel/Verify/DefEq/StringLiteral.lean index 2b33592fc..0a89eb4b4 100644 --- a/Ix/Tc/Verify/DefEq/StringLiteral.lean +++ b/Ix/Kernel/Verify/DefEq/StringLiteral.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.BoolTrue -import Ix.Tc.Verify.Whnf.Projection.StringExpansion +import Ix.Kernel.Verify.DefEq.BoolTrue +import Ix.Kernel.Verify.Whnf.Projection.StringExpansion /-! # String-literal definitional equality @@ -11,9 +11,9 @@ stronger fact recorded here: that exact generated term translates to the same Theory literal as the compact source syntax. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- A K1 String-expansion plan together with the exact Theory meaning needed by DefEq. Merely knowing that the generated expression has *some* @@ -200,4 +200,4 @@ end DefEqAfterStringExpansion end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/Structural.lean b/Ix/Kernel/Verify/DefEq/Structural.lean similarity index 97% rename from Ix/Tc/Verify/DefEq/Structural.lean rename to Ix/Kernel/Verify/DefEq/Structural.lean index 195fc3905..2fc370351 100644 --- a/Ix/Tc/Verify/DefEq/Structural.lean +++ b/Ix/Kernel/Verify/DefEq/Structural.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Infer.BinderScopes -import Ix.Tc.Verify.Infer.Callbacks -import Ix.Tc.Verify.Infer.SortTypes +import Ix.Kernel.Verify.Infer.BinderScopes +import Ix.Kernel.Verify.Infer.Callbacks +import Ix.Kernel.Verify.Infer.SortTypes /-! # Structural definitional equality @@ -12,9 +12,9 @@ so its proof must be transported into the first domain's context before the recursive callback is invoked. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- Finite resources needed to open both bodies of one quick binder comparison with the common production fvar. -/ @@ -220,8 +220,8 @@ theorem quickDefEq_wf have hbodyTyped : world.venv.IsDefEq uvars (ty1V :: Delta.toCtx) body1V body2V bodyTy := hbodyEq.of_l world.venvWF hDeltaBody.toCtx (by - simpa [KVLCtx.toCtx, Lean4Lean.VEnv.HasType] using hbody1Typed) - exact (Lean4Lean.VEnv.IsDefEq.lamDF + simpa [KVLCtx.toCtx, Ix.Theory.Named.VEnv.HasType] using hbody1Typed) + exact (Ix.Theory.Named.VEnv.IsDefEq.lamDF hdomainTyped hbodyTyped).toU · intro _ _ _ trivial @@ -253,7 +253,7 @@ theorem quickDefEq_wf (ty1V :: Delta.toCtx) body1V body2V (.sort bodyLevel) := hbodyEq.of_l world.venvWF hDeltaBody.toCtx (by simpa [KVLCtx.toCtx] using hbody1Sort) - exact (Lean4Lean.VEnv.IsDefEq.forallEDF + exact (Ix.Theory.Named.VEnv.IsDefEq.forallEDF hdomainTyped hbodyTyped).toU · intro _ _ _ trivial @@ -315,4 +315,4 @@ end DefEqAfterQuick end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/DefEq/StructuralCongruence.lean b/Ix/Kernel/Verify/DefEq/StructuralCongruence.lean similarity index 97% rename from Ix/Tc/Verify/DefEq/StructuralCongruence.lean rename to Ix/Kernel/Verify/DefEq/StructuralCongruence.lean index 08b130443..afe7c9131 100644 --- a/Ix/Tc/Verify/DefEq/StructuralCongruence.lean +++ b/Ix/Kernel/Verify/DefEq/StructuralCongruence.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq.SameHeadSpine +import Ix.Kernel.Verify.DefEq.SameHeadSpine /-! # Post-delta structural congruence @@ -10,9 +10,9 @@ other shapes and all failed guards return `false` without a completeness claim. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- Finite universe support for constant nodes selected by structural congruence. -/ @@ -94,7 +94,7 @@ theorem tryStructuralCongruence_wf (hleftLookup.symm.trans hrightLookup) have hvalue : leftV = rightV := congrArg Prod.fst hp subst rightV - exact Lean4Lean.VEnv.IsDefEqU.refl hleftWF + exact Ix.Theory.Named.VEnv.IsDefEqU.refl hleftWF · rename_i leftId leftLevels leftInfo rightId rightLevels rightInfo exact RecM.WF.pure fun _ hanswer => by obtain ⟨hid, hlevels⟩ := Bool.and_eq_true_iff.mp hanswer @@ -145,4 +145,4 @@ end TryStructuralCongruence end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Driver/BooleanAcceptance.lean b/Ix/Kernel/Verify/Driver/BooleanAcceptance.lean similarity index 99% rename from Ix/Tc/Verify/Driver/BooleanAcceptance.lean rename to Ix/Kernel/Verify/Driver/BooleanAcceptance.lean index a10caebea..2690273ea 100644 --- a/Ix/Tc/Verify/Driver/BooleanAcceptance.lean +++ b/Ix/Kernel/Verify/Driver/BooleanAcceptance.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Driver.SupportedAcceptance -import Ix.Tc.Verify.Inductive.EnumerationAcceptance +import Ix.Kernel.Verify.Driver.SupportedAcceptance +import Ix.Kernel.Verify.Inductive.EnumerationAcceptance /-! # Certificate-backed Boolean driver acceptance @@ -20,7 +20,7 @@ then transported to each monotone current world and replayed idempotently; neither row constructs or admits an `InductiveOracle`. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace BooleanEnumerationFixture @@ -596,4 +596,4 @@ theorem subjectWF : end BooleanEnumerationFixture -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Driver/Dependencies.lean b/Ix/Kernel/Verify/Driver/Dependencies.lean similarity index 99% rename from Ix/Tc/Verify/Driver/Dependencies.lean rename to Ix/Kernel/Verify/Driver/Dependencies.lean index 281c211ea..ace78133c 100644 --- a/Ix/Tc/Verify/Driver/Dependencies.lean +++ b/Ix/Kernel/Verify/Driver/Dependencies.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Driver.Model +import Ix.Kernel.Verify.Driver.Model /-! # Semantic dependencies of Ixon constants @@ -15,7 +15,7 @@ the expression constructors which ingress turns into kernel constants: * `.nat`/`.str` are data dependencies, not Theory declaration dependencies. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace IxonExpr @@ -179,4 +179,4 @@ theorem dependencyCatalog_dependsOn_iff (env : Ixon.Env) end IxonEnv -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Driver/Enumeration.lean b/Ix/Kernel/Verify/Driver/Enumeration.lean similarity index 99% rename from Ix/Tc/Verify/Driver/Enumeration.lean rename to Ix/Kernel/Verify/Driver/Enumeration.lean index 16411f32b..8bb44fac5 100644 --- a/Ix/Tc/Verify/Driver/Enumeration.lean +++ b/Ix/Kernel/Verify/Driver/Enumeration.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Driver.Dependencies +import Ix.Kernel.Verify.Driver.Dependencies /-! # Exact `buildAnonWork` enumeration @@ -14,7 +14,7 @@ Under that structural contract, the production builder succeeds, its collapses to its work item's root. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- `Except` carries no `DecidableEq` instance, so the `get`/`peekTag` equations of `ExactAnonEntry` cannot otherwise build the instance the @@ -570,4 +570,4 @@ theorem buildAnonWork_exact {env : Ixon.Env} end AnonWorkEnvWF -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Driver/Fixtures.lean b/Ix/Kernel/Verify/Driver/Fixtures.lean similarity index 98% rename from Ix/Tc/Verify/Driver/Fixtures.lean rename to Ix/Kernel/Verify/Driver/Fixtures.lean index 84c9403e4..1d3567768 100644 --- a/Ix/Tc/Verify/Driver/Fixtures.lean +++ b/Ix/Kernel/Verify/Driver/Fixtures.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Driver.Serial +import Ix.Kernel.Verify.Driver.Serial /-! # Adversarial E1 fixtures @@ -9,7 +9,7 @@ coverage, dependency closure, or graph well-foundedness cannot be hidden by content-address computation. -/ -namespace Ix.Tc.E1Fixture +namespace Ix.Kernel.E1Fixture def address (byte : UInt8) : Address := ⟨⟨Array.replicate 32 byte⟩⟩ @@ -178,4 +178,4 @@ theorem cyclicStandalones_not_wellFounded : · rfl · simp [firstItem, secondItem, AnonWorkItem.root] -end Ix.Tc.E1Fixture +end Ix.Kernel.E1Fixture diff --git a/Ix/Tc/Verify/Driver/Model.lean b/Ix/Kernel/Verify/Driver/Model.lean similarity index 97% rename from Ix/Tc/Verify/Driver/Model.lean rename to Ix/Kernel/Verify/Driver/Model.lean index a51e486a0..6428b01fa 100644 --- a/Ix/Tc/Verify/Driver/Model.lean +++ b/Ix/Kernel/Verify/Driver/Model.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Check.BlockIdentity +import Ix.Kernel.Verify.Check.BlockIdentity /-! # Workset and dependency model @@ -16,7 +16,7 @@ and every projection address have the same `blockOf` image. Standalones map to themselves. -/ -namespace Ix.Tc +namespace Ix.Kernel /-! ## Canonical finite address sets -/ @@ -188,7 +188,7 @@ def AssumptionsWF (baseline : VerifyWorld) (assumptions : FiniteAddressSet) : Prop := ∀ {addr}, addr ∈ assumptions → baseline.AcceptsAddress addr -/-- Per-item C2 consequence needed by composition. The rule is reusable at +/-- Per-item admission consequence needed by composition. The rule is reusable at any extension of `baseline`: once every dependency outside the item's own collapsed block is accepted, the item can be admitted atomically. -/ def AllAccepted (baseline : VerifyWorld) (catalog : DependencyCatalog) @@ -266,7 +266,7 @@ end WellFoundedBlocks /-! ## Checked-set composition -/ -/-- C3's semantic result: some final Theory world extends the baseline, +/-- Driver composition's semantic result: some final Theory world extends the baseline, accepts exactly the advertised subject domain at the raw-address interface, retains every explicit assumption, and records the closure/disjointness contracts needed for later claim-root binding. -/ @@ -338,7 +338,7 @@ private theorem composeTopological exact .inl (.head _) · exact .inr hrestMember -/-- The E1 checked-set theorem. Successful per-item C2 rules are reordered +/-- The checked-set theorem. Successful per-item admission rules are reordered by the constructive collapsed-block schedule; runtime address order is not assumed to be topological. -/ theorem acceptedWorkset_subjectWF @@ -366,4 +366,4 @@ theorem acceptedWorkset_subjectWF · intro addr haddr exact (hassumptions haddr).mono hfinal -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Driver/Serial.lean b/Ix/Kernel/Verify/Driver/Serial.lean similarity index 95% rename from Ix/Tc/Verify/Driver/Serial.lean rename to Ix/Kernel/Verify/Driver/Serial.lean index 1a19001f4..5a7de4f2c 100644 --- a/Ix/Tc/Verify/Driver/Serial.lean +++ b/Ix/Kernel/Verify/Driver/Serial.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Driver.Enumeration +import Ix.Kernel.Verify.Driver.Enumeration /-! # Serial `checkEnvAnon` composition @@ -8,13 +8,13 @@ per-item checker calls which produced it. A failed call always contributes at least one `CheckResult` for a well-formed production work item, so an all-success result array yields a concrete serial success trace. -`CheckSuccessSound` is the named C2 adapter: it consumes an actual successful +`CheckSuccessSound` is the named admission adapter: it consumes an actual successful `TcM.checkConst` execution and returns the reusable semantic admission rule needed by dependency-order composition. The serial corollary therefore does not assume `AllAccepted` directly. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Every public result row reports success. -/ def AllCheckResultsSucceeded (results : Array CheckResult) : Prop := @@ -146,9 +146,9 @@ theorem successfulStep {cfg : CheckCfg} {initial : AnonCheckLoopState} end SerialChecksSucceeded -/-- The concrete C2 adapter used by the serial corollary. Its premise is an +/-- The concrete admission adapter used by the serial corollary. Its premise is an actual successful `TcM.checkConst` call; its conclusion is the reusable -dependency-relative admission rule produced by the K3/E0 per-item theorem. -/ +dependency-relative admission rule produced by the per-item theorem. -/ def CheckSuccessSound (baseline : VerifyWorld) (catalog : DependencyCatalog) (work : Array AnonWorkItem) : Prop := ∀ item, item ∈ work → @@ -162,7 +162,7 @@ def CheckSuccessSound (baseline : VerifyWorld) current.AcceptsAddress target) → ∃ after, current ≤ after ∧ WorkItemAccepted after item -/-- A successful serial trace plus the concrete C2 adapter constructs the +/-- A successful serial trace plus the concrete admission adapter constructs the abstract admission predicate needed by topological composition. -/ theorem SerialChecksSucceeded.allAccepted {cfg : CheckCfg} {initial : AnonCheckLoopState} @@ -191,7 +191,7 @@ theorem checkEnvAnon_eq_serial {env : Ixon.Env} /-- E1's production serial-driver corollary. All emitted rows succeeding is converted to concrete per-item success traces, those traces are interpreted -through the C2 success rule, and the resulting admissions are reordered by +through the per-item success rule, and the resulting admissions are reordered by the proved collapsed-block schedule. -/ theorem checkEnvAnon_subjectWF {env : Ixon.Env} (h : AnonWorkEnvWF env) @@ -226,4 +226,4 @@ theorem checkEnvAnon_subjectWF end AnonWorkEnvWF -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Driver/SupportedAcceptance.lean b/Ix/Kernel/Verify/Driver/SupportedAcceptance.lean similarity index 96% rename from Ix/Tc/Verify/Driver/SupportedAcceptance.lean rename to Ix/Kernel/Verify/Driver/SupportedAcceptance.lean index f9c1e422d..e2c019ad2 100644 --- a/Ix/Tc/Verify/Driver/SupportedAcceptance.lean +++ b/Ix/Kernel/Verify/Driver/SupportedAcceptance.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Check.PublicBlocks -import Ix.Tc.Verify.Check.PublicStandalone -import Ix.Tc.Verify.Driver.Serial +import Ix.Kernel.Verify.Check.PublicBlocks +import Ix.Kernel.Verify.Check.PublicStandalone +import Ix.Kernel.Verify.Driver.Serial /-! # Supported production-checker acceptance @@ -26,7 +26,7 @@ The source-to-kernel route agreement remains an explicit representation premise until the later ingress/refinement phase discharges it generically. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace AnonWorkItem @@ -58,8 +58,8 @@ structure SupportedStandaloneResources (concrete : KConst .anon) : Type where pipelines : ScopedStandalonePipelineResources context.model support (context.calls (initial.recFuel.toNat + 1)) - (Ix.Tc.methodsN (m := .anon) initial.recFuel.toNat) - decl : Lean4Lean.VDecl + (Ix.Kernel.methodsN (m := .anon) initial.recFuel.toNat) + decl : Ix.Theory.Named.VDecl projection : trProj.SubstCompatible literals : ∀ literal, world.venv.ContainsLits literal pending : PendingDecl trProj world id decl @@ -72,7 +72,7 @@ structure SupportedStandaloneResources route : StandaloneRoute (ScopedWhnfStateInv context.model .noAccel (kernelCacheSemantics context.model.keys trProj) support []) - (Ix.Tc.methodsN (m := .anon) initial.recFuel.toNat) concrete + (Ix.Kernel.methodsN (m := .anon) initial.recFuel.toNat) concrete namespace SupportedStandaloneResources @@ -108,7 +108,7 @@ end SupportedStandaloneResources /-- Exhaustive body evidence supported by the E3-S adapter. The first constructor is constructive K3 evidence for the only definition -block shape currently modeled atomically by Lean4Lean: one definition. The +block shape currently modeled atomically by Ix.Theory.Named: one definition. The second constructor keeps the E2 inductive/recursor oracle visible. In particular there is no constructor containing a prebuilt `CertifiedBlockBodySuccess`. -/ @@ -122,10 +122,10 @@ inductive SupportedBlockBodyResources TcState .anon → TcState .anon → Prop | singletonDefinition {block requested member : KId .anon} {concrete : KConst .anon} - {decl : Lean4Lean.VDecl} {before after : TcState .anon} + {decl : Ix.Theory.Named.VDecl} {before after : TcState .anon} (pipelines : ScopedStandalonePipelineResources context.model support (context.calls (initial.recFuel.toNat + 1)) - (Ix.Tc.methodsN (m := .anon) initial.recFuel.toNat)) + (Ix.Kernel.methodsN (m := .anon) initial.recFuel.toNat)) (projection : trProj.SubstCompatible) (literals : ∀ literal, world.venv.ContainsLits literal) (pending : PendingDecl trProj world member decl) @@ -168,17 +168,17 @@ theorem certify kind before after) (hexact : ExactCheckBlock world block members kind) (trace : RecM.ExactBlockBodySuccessTrace - (Ix.Tc.methodsN (m := .anon) initial.recFuel.toNat) + (Ix.Kernel.methodsN (m := .anon) initial.recFuel.toNat) block requested members kind before after) : CertifiedBlockBodySuccess (kernelCacheSemantics context.model.keys trProj) trProj world support - (Ix.Tc.methodsN (m := .anon) initial.recFuel.toNat) + (Ix.Kernel.methodsN (m := .anon) initial.recFuel.toNat) block requested members kind before after := by cases resources with | singletonDefinition pipelines projection literals pending catalog validation covered collision uvars resetScope initialInv lazyFault blocksAfter => - let methods := Ix.Tc.methodsN (m := .anon) initial.recFuel.toNat + let methods := Ix.Kernel.methodsN (m := .anon) initial.recFuel.toNat have hmethods : Methods.ScopedWFAtOn context.model .noAccel (kernelCacheSemantics context.model.keys trProj) support (context.calls (initial.recFuel.toNat + 1)) @@ -291,7 +291,7 @@ theorem le_admittedWorld (resources : CertificateBackedBlockResources world blockAddr primary targets) : world ≤ resources.admittedWorld := - ⟨rfl, rfl, rfl, fun {_} hold => Or.inr hold, Lean4Lean.VEnv.LE.rfl⟩ + ⟨rfl, rfl, rfl, fun {_} hold => Or.inr hold, Ix.Theory.Named.VEnv.LE.rfl⟩ /-- Reindex installed semantic entries across an arbitrary monotone current world. This is the reusable bridge used by E1 after prior work items have @@ -391,14 +391,14 @@ structure SupportedCheckRun (world : VerifyWorld) (item : AnonWorkItem) TcM.getConst (⟨item.primary, ()⟩ : KId .anon) initial = .ok concrete loaded → (RecM.coordinatedBlockFor concrete).run - (Ix.Tc.methodsN (m := .anon) initial.recFuel.toNat) loaded = + (Ix.Kernel.methodsN (m := .anon) initial.recFuel.toNat) loaded = .ok selected routed → item.SelectedBlockMatches selected standalone : ∀ {concrete : KConst .anon} {loaded routed : TcState .anon}, TcM.getConst (⟨item.primary, ()⟩ : KId .anon) initial = .ok concrete loaded → (RecM.coordinatedBlockFor concrete).run - (Ix.Tc.methodsN (m := .anon) initial.recFuel.toNat) loaded = + (Ix.Kernel.methodsN (m := .anon) initial.recFuel.toNat) loaded = .ok none routed → SupportedStandaloneResources context concrete blockBody : ∀ {block : KId .anon} {members : Array (KId .anon)} @@ -406,7 +406,7 @@ structure SupportedCheckRun (world : VerifyWorld) (item : AnonWorkItem) ExactCheckBlock world block members kind → (⟨item.primary, ()⟩ : KId .anon) ∈ members → RecM.ExactBlockBodySuccessTrace - (Ix.Tc.methodsN (m := .anon) initial.recFuel.toNat) + (Ix.Kernel.methodsN (m := .anon) initial.recFuel.toNat) block (⟨item.primary, ()⟩ : KId .anon) members kind routed bodyAfter → SupportedBlockBodyResources context block @@ -668,4 +668,4 @@ theorem checkEnvAnon_supported_subjectWF end AnonWorkEnvWF -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Driver/SupportedAcceptanceFixtures.lean b/Ix/Kernel/Verify/Driver/SupportedAcceptanceFixtures.lean similarity index 95% rename from Ix/Tc/Verify/Driver/SupportedAcceptanceFixtures.lean rename to Ix/Kernel/Verify/Driver/SupportedAcceptanceFixtures.lean index 3003e1764..4feb1f616 100644 --- a/Ix/Tc/Verify/Driver/SupportedAcceptanceFixtures.lean +++ b/Ix/Kernel/Verify/Driver/SupportedAcceptanceFixtures.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Driver.Fixtures -import Ix.Tc.Verify.Driver.SupportedAcceptance -import Ix.Tc.Verify.Inductive.EnumerationAcceptance +import Ix.Kernel.Verify.Driver.Fixtures +import Ix.Kernel.Verify.Driver.SupportedAcceptance +import Ix.Kernel.Verify.Inductive.EnumerationAcceptance /-! # Supported-acceptance adversarial and inductive fixtures @@ -13,7 +13,7 @@ constructor; the only remaining inputs are the explicitly advertised scoped recursive context and active cache invariant. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace SupportedAcceptanceFixture @@ -88,7 +88,7 @@ theorem booleanFamilyBody_certified CertifiedBlockBodySuccess (kernelCacheSemantics context.model.keys RawProjRel.none) RawProjRel.none BooleanEnumerationFixture.world support - (Ix.Tc.methodsN (m := .anon) + (Ix.Kernel.methodsN (m := .anon) BooleanEnumerationFixture.checkerInitial.recFuel.toNat) BooleanEnumerationFixture.familyBlockId BooleanEnumerationFixture.familyId @@ -103,4 +103,4 @@ theorem booleanFamilyBody_certified end SupportedAcceptanceFixture -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Env.lean b/Ix/Kernel/Verify/Env.lean similarity index 98% rename from Ix/Tc/Verify/Env.lean rename to Ix/Kernel/Verify/Env.lean index b3c1283bf..f80f9f6d9 100644 --- a/Ix/Tc/Verify/Env.lean +++ b/Ix/Kernel/Verify/Env.lean @@ -1,8 +1,10 @@ -import Ix.Tc.Verify.Trans -import Ix.Tc.Verify.Decl -import Ix.Tc.Verify.Inductive -import Ix.Tc.Const -import Ix.Tc.Env +import Ix.Kernel.Verify.Trans +import Ix.Kernel.Verify.Decl +import Ix.Kernel.Verify.Inductive +import Ix.Kernel.Const +import Ix.Kernel.Env + +open Ix.Theory (VLevel) /-! # Environment translation: `KEnv` ↔ `VEnv` @@ -86,10 +88,10 @@ theorem le_antisymm {a b : Ix.DefinitionSafety} : end Ix.DefinitionSafety -namespace Ix.Tc +namespace Ix.Kernel open Std (HashMap) -open Lean4Lean (VExpr VLevel VEnv VConstant VConstVal VDefVal VDecl +open Ix.Theory.Named (VExpr VEnv VConstant VConstVal VDefVal VDecl VInductDecl) /-! ### Constant safety -/ @@ -417,10 +419,10 @@ arbitrary well-formed Theory constant. It must select the exact constant and Lean name installed by `VEnv.addQuot` for the concrete declaration's role. -/ inductive QuotientConstRel : Ix.QuotKind → Lean.Name → VConstant → Prop - | quotType : QuotientConstRel .type ``Quot Lean4Lean.quotConst - | quotCtor : QuotientConstRel .ctor ``Quot.mk Lean4Lean.quotMkConst - | quotLift : QuotientConstRel .lift ``Quot.lift Lean4Lean.quotLiftConst - | quotInd : QuotientConstRel .ind ``Quot.ind Lean4Lean.quotIndConst + | quotType : QuotientConstRel .type ``Quot Ix.Theory.Named.quotConst + | quotCtor : QuotientConstRel .ctor ``Quot.mk Ix.Theory.Named.quotMkConst + | quotLift : QuotientConstRel .lift ``Quot.lift Ix.Theory.Named.quotLiftConst + | quotInd : QuotientConstRel .ind ``Quot.ind Ix.Theory.Named.quotIndConst /-- Provenance for one trusted catalog id. Standalone entries retain their actual declaration-WF transition. Ambient inductive-family entries retain the @@ -543,7 +545,7 @@ theorem recursorRule {trProj : RawProjRel} {catalog : Catalog} subst c exact False.elim hrule -/-- Recover the exact Lean4Lean iota-pattern witness associated with a +/-- Recover the exact Ix.Theory.Named iota-pattern witness associated with a trusted concrete recursor rule. -/ theorem recursorPattern {trProj : RawProjRel} {catalog : Catalog} {nameOf : Address → Option Lean.Name} {env : VEnv} @@ -577,7 +579,7 @@ end TrustedCatalogEntry /-- Consumer-facing provenance for one exact concrete constant. Unlike legacy `TrKConstant`, this relation does not require a translation log -over the whole concrete `KEnv`. It states only what C1--C3 consumers need at +over the whole concrete `KEnv`. It states what lookup and admission need at a trusted lookup: the immutable catalog entry is exactly `c`; its assigned Theory constant is installed and well-formed; universe arities agree; and the concrete type has a raw translation to that Theory type. Both standalone @@ -1002,7 +1004,7 @@ theorem IllTypedPending.trustedCatalogRel : namespace WellTypedPromotion -def targetName : Lean.Name := `Ix.Tc.Verify.wellTypedPromotion +def targetName : Lean.Name := `Ix.Kernel.Verify.wellTypedPromotion def fixtureAddress : Address := ⟨⟨Array.replicate 32 1⟩⟩ @@ -1060,7 +1062,7 @@ theorem pending : PendingDecl RawProjRel.none world targetId theoryDecl := by theorem theoryConstant_wf : theoryConstant.toVConstant.WF world.venv := by - exact ⟨.succ .zero, Lean4Lean.VEnv.HasType.sort (l := .zero) trivial⟩ + exact ⟨.succ .zero, Ix.Theory.Named.VEnv.HasType.sort (l := .zero) trivial⟩ theorem addConst : world.venv.addConst targetName theoryConstant.toVConstant = @@ -1085,4 +1087,4 @@ theorem promotes : end WellTypedPromotion -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/EquivalenceManager.lean b/Ix/Kernel/Verify/EquivalenceManager.lean similarity index 99% rename from Ix/Tc/Verify/EquivalenceManager.lean rename to Ix/Kernel/Verify/EquivalenceManager.lean index 7cc2c4719..0cadc80b9 100644 --- a/Ix/Tc/Verify/EquivalenceManager.lean +++ b/Ix/Kernel/Verify/EquivalenceManager.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Totalization -import Ix.Tc.Verify.Expr +import Ix.Kernel.Verify.Totalization +import Ix.Kernel.Verify.Expr import Batteries.Data.Array.Lemmas import Std.Data.HashMap.Lemmas @@ -14,7 +14,7 @@ edge remains semantically valid. That is sufficient for sound positive queries, root representatives, path compression, and union. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace EquivManager @@ -561,4 +561,4 @@ end WF end EquivManager -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Execution.lean b/Ix/Kernel/Verify/Execution.lean similarity index 99% rename from Ix/Tc/Verify/Execution.lean rename to Ix/Kernel/Verify/Execution.lean index 7c84bb886..4cac142ea 100644 --- a/Ix/Tc/Verify/Execution.lean +++ b/Ix/Kernel/Verify/Execution.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Support +import Ix.Kernel.Verify.Support /-! # G3b: execution-indexed finite run assumptions @@ -23,7 +23,7 @@ skeletons can use its concrete execution and support boundary without colliding with their temporary translation-relation placeholders. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- A proof-level decomposition of a `TcM` computation into audited interning operations. Lists conservatively include all continuation/handler @@ -253,4 +253,4 @@ theorem requestBounds {α : Type} {initial : TcState .anon} end RunAssumptions -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Expr.lean b/Ix/Kernel/Verify/Expr.lean similarity index 99% rename from Ix/Tc/Verify/Expr.lean rename to Ix/Kernel/Verify/Expr.lean index f7b0c7391..9f27b7c6d 100644 --- a/Ix/Tc/Verify/Expr.lean +++ b/Ix/Kernel/Verify/Expr.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Env -import Ix.Tc.Verify.Level +import Ix.Kernel.Env +import Ix.Kernel.Verify.Level import Std.Data.HashMap.Lemmas /-! @@ -31,7 +31,7 @@ module keeps as a named finite-support hypothesis — never a global axiom instance. -/ -namespace Ix.Tc +namespace Ix.Kernel variable {m : Mode} @@ -434,4 +434,4 @@ theorem internExpr_addr (hwf : it.WF) end InternTable -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Frame.lean b/Ix/Kernel/Verify/Frame.lean similarity index 98% rename from Ix/Tc/Verify/Frame.lean rename to Ix/Kernel/Verify/Frame.lean index 94948ac13..dccb04baa 100644 --- a/Ix/Tc/Verify/Frame.lean +++ b/Ix/Kernel/Verify/Frame.lean @@ -1,9 +1,9 @@ -import Ix.Tc.Verify.Subst +import Ix.Kernel.Verify.Subst /-! # Intern-table frame lemmas -The expression walkers in `Ix.Tc.Subst` mutate only the expression side of +The expression walkers in `Ix.Kernel.Subst` mutate only the expression side of `InternTable`. Their semantic master theorems establish expression support and table well-formedness, but the run-scoped invariant also tracks the finite universe range. This file proves the missing operational frame directly from @@ -14,7 +14,7 @@ The small effect predicates make sequential state threading explicit. They are intentionally restricted to anon mode, matching the verified kernel. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- An `InternM` action leaves the universe intern map unchanged. -/ def InternPreservesUnivs (x : InternM .anon α) : Prop := @@ -32,7 +32,7 @@ theorem pure (a : α) : InternPreservesUnivs (pure a) := by rfl theorem runWalk {x : WalkM .anon α} (h : WalkPreservesUnivs x) : - InternPreservesUnivs (Ix.Tc.runWalk x) := by + InternPreservesUnivs (Ix.Kernel.runWalk x) := by intro it exact h it {} @@ -53,12 +53,12 @@ theorem bind {x : WalkM .anon α} {f : α → WalkM .anon β} exact (hf _ _ _).trans (hx _ _) theorem scratchGet (key : Address × UInt64) : - WalkPreservesUnivs (Ix.Tc.scratchGet? key) := by + WalkPreservesUnivs (Ix.Kernel.scratchGet? key) := by intro it sc rfl theorem scratchInsert (key : Address × UInt64) (e : KExpr .anon) : - WalkPreservesUnivs (Ix.Tc.scratchInsert key e) := by + WalkPreservesUnivs (Ix.Kernel.scratchInsert key e) := by intro it sc rfl @@ -837,4 +837,4 @@ theorem abstractFVars_preservesUnivs (body : KExpr .anon) · exact .pure _ · exact .runWalk (abstractFVarsCached_preservesUnivs body _ _ 0) -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Upstream/Pending.lean b/Ix/Kernel/Verify/Frontier/Pending.lean similarity index 69% rename from Ix/Tc/Verify/Upstream/Pending.lean rename to Ix/Kernel/Verify/Frontier/Pending.lean index 58ea11c17..56660c6be 100644 --- a/Ix/Tc/Verify/Upstream/Pending.lean +++ b/Ix/Kernel/Verify/Frontier/Pending.lean @@ -1,25 +1,25 @@ -import Ix.Tc.Verify.Inductive.BlockPatternSoundness -import Lean4Lean.Verify.Environment.MutualInductiveFixtures +import Ix.Kernel.Verify.Inductive.BlockPatternSoundness +import Ix.Theory.Named.Verify.Environment.MutualInductiveFixtures /-! -# Quarantined pending-upstream witnesses +# Quarantined pending metatheory witnesses Nothing in this module may state an Ix ingress, address, ownership, checker, cache, collision, or workset fact. Conditional consumers import it directly; unconditional completed roots must not depend on it. -/ -namespace Ix.Tc.Upstream.Pending +namespace Ix.Kernel.Frontier.Pending -open Lean4Lean -open Lean4Lean.MutualInductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.MutualInductiveFixtures /-! ## Physical mutual-family permutation Ix's canonical mutual SCC order for this fixture is `TreeList, Tree`, while -the retained Lean declaration order is `Tree, TreeList`. Lean4Lean can -compute the exact reversed generation descriptor today; the missing upstream -piece is a theorem transporting block-generation WF across that family +the retained Lean declaration order is `Tree, TreeList`. The named specification +computes the exact reversed generation descriptor; the missing local +proof transports block-generation WF across that family permutation. -/ def mutualTreePhysicalDecl : VInductDecl := @@ -33,7 +33,7 @@ def mutualTreePhysicalBlockEnv : VEnv := (VEnv.empty.stageInductiveTypes mutualTreePhysicalDecl.types).get (by decide) -/-- Fixture-specific stand-in for the future Lean4Lean permutation theorem +/-- Fixture-specific stand-in for the missing named-specification permutation theorem `VInductDecl.BlockGenerationChecked.permuteFamiliesWF`. This is a Theory-only statement: it certifies the computed `TreeList, Tree` @@ -63,15 +63,14 @@ def mutualTreePhysicalCertificate : success := mutualTreePhysicalSuccess beforeWF := ⟨[], .empty⟩ -/-- Fixture-specific stand-in for the future Lean4Lean consumer theorem +/-- Fixture-specific stand-in for the missing named-specification consumer theorem `VInductDecl.BlockCertificate.recursorPatternSound` (the constructive wrapper around `BlockGenerationChecked.pat_wf`). -The current pin exposes the exact pattern payload and its registered rule but -does not yet publish this environment-parametric consumer conclusion without -the upstream metatheory gap. Delete this declaration, and replace its sole -use with that theorem, when the upstream result lands. -/ +The local specification exposes the exact pattern payload and its registered +rule. This conclusion still requires a metatheory proof. Completing that proof +must replace this axiom and its sole conditional use. -/ axiom mutualTreePhysicalRulePatternSound : CertifiedBlockRulePatternSound mutualTreePhysicalCertificate -end Ix.Tc.Upstream.Pending +end Ix.Kernel.Frontier.Pending diff --git a/Ix/Tc/Verify/Inductive.lean b/Ix/Kernel/Verify/Inductive.lean similarity index 94% rename from Ix/Tc/Verify/Inductive.lean rename to Ix/Kernel/Verify/Inductive.lean index baa2535c2..82567cb06 100644 --- a/Ix/Tc/Verify/Inductive.lean +++ b/Ix/Kernel/Verify/Inductive.lean @@ -1,12 +1,14 @@ -import Ix.Tc.Verify.Decl -import Ix.Tc.Verify.Inductive.Certificate -import Ix.Tc.Verify.Trans -import Lean4Lean.Theory.Typing.Pattern +import Ix.Kernel.Verify.Decl +import Ix.Kernel.Verify.Inductive.Certificate +import Ix.Kernel.Verify.Trans +import Ix.Theory.Named.Typing.Pattern + +open Ix.Theory (VLevel) /-! # Ambient inductive oracle -G2 introduced this interface before Lean4Lean had a usable inductive +G2 introduced this interface before Ix.Theory.Named had a usable inductive specification, so it records the semantic consequences needed by the checker directly: @@ -17,7 +19,7 @@ directly: * every concrete recursor rule has an explicit, well-formed Theory defeq witness headed by that recursor. -Pin A now provides Lean4Lean's proved normalized `GenerationCertificate` and +Pin A now provides Ix.Theory.Named's proved normalized `GenerationCertificate` and `addInductCertified` transaction. `Inductive/Certificate.lean` derives the Theory-owned environment, lookup, freshness, and rule-registration facts from that certificate. It intentionally cannot supply the Ix-owned catalog/name @@ -31,9 +33,9 @@ closed Nat model while the recursor clause prevents WHNF proofs from treating computation rules as an unrecorded ambient fact. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VConstant VDefEq VEnv VExpr) +open Ix.Theory.Named (VConstant VDefEq VEnv VExpr) /-- Concrete declaration kinds admitted through an ambient inductive block. Standalone declarations and quotients cannot cross this boundary. -/ @@ -125,12 +127,12 @@ def KConst.ConstructorAt (c : KConst .anon) (index : Nat) /-- A Theory expression is an application spine headed by `name`. -/ inductive HeadConst (name : Lean.Name) : VExpr → Prop - | const (levels : List Lean4Lean.VLevel) : + | const (levels : List Ix.Theory.VLevel) : HeadConst name (.const name levels) | app {fn arg : VExpr} : HeadConst name fn → HeadConst name (.app fn arg) /-- A closed rewrite equation may bind its complete rule telescope before -the recursor-headed application. Lean4Lean's generated iota equations and +the recursor-headed application. Ix.Theory.Named's generated iota equations and production's stored `RecRule.rhs` both use exactly this closed-lambda shape; requiring `HeadConst name defeq.lhs` at the outer node would reject every nonempty generated rule telescope. -/ @@ -168,7 +170,7 @@ end HeadConstUnderLambdas head. This is the counted form needed to distinguish an iota major from an arbitrary later occurrence of the same constructor. -/ inductive HeadConstN (name : Lean.Name) : Nat → VExpr → Prop - | const (levels : List Lean4Lean.VLevel) : + | const (levels : List Ix.Theory.VLevel) : HeadConstN name 0 (.const name levels) | app {arity : Nat} {fn arg : VExpr} : HeadConstN name arity fn → HeadConstN name (arity + 1) (.app fn arg) @@ -178,21 +180,21 @@ namespace HeadConstN /-- Matching `varN (const name) arity` exposes exactly that many application arguments over `name`. -/ theorem of_varN_matches {name : Lean.Name} {arity : Nat} {source : VExpr} - {levels : List Lean4Lean.VLevel} - {captures : ((Lean4Lean.Pattern.const name).varN arity).Path → VExpr} - (h : Lean4Lean.Pattern.Matches - ((Lean4Lean.Pattern.const name).varN arity) + {levels : List Ix.Theory.VLevel} + {captures : ((Ix.Theory.Named.Pattern.const name).varN arity).Path → VExpr} + (h : Ix.Theory.Named.Pattern.Matches + ((Ix.Theory.Named.Pattern.const name).varN arity) source levels captures) : HeadConstN name arity source := by induction arity generalizing source with | zero => - change Lean4Lean.Pattern.Matches (.const name) + change Ix.Theory.Named.Pattern.Matches (.const name) source levels captures at h cases h exact .const levels | succ arity ih => - change Lean4Lean.Pattern.Matches - (.var ((Lean4Lean.Pattern.const name).varN arity)) + change Ix.Theory.Named.Pattern.Matches + (.var ((Ix.Theory.Named.Pattern.const name).varN arity)) source levels captures at h cases h with | var hprefix => @@ -204,8 +206,8 @@ end HeadConstN /-- Exact Theory pattern selected by an ordinary constructor iota rule. -/ def RecursorIotaPattern (recursorName : Lean.Name) (majorIdx : Nat) (constructorName : Lean.Name) (constructorArgs : Nat) : - Lean4Lean.Pattern := - (Lean4Lean.SimplePattern.iota recursorName majorIdx constructorName + Ix.Theory.Named.Pattern := + (Ix.Theory.Named.SimplePattern.iota recursorName majorIdx constructorName constructorArgs).toPattern namespace RecursorIotaPattern @@ -216,17 +218,17 @@ contains precisely the parameters/motives/minors/indices before it. -/ theorem matches_shape {recursorName constructorName : Lean.Name} {majorIdx constructorArgs : Nat} {source : VExpr} - {levels : List Lean4Lean.VLevel} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern recursorName majorIdx constructorName constructorArgs).Path → VExpr} - (h : Lean4Lean.Pattern.Matches + (h : Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern recursorName majorIdx constructorName constructorArgs) source levels captures) : ∃ recursorPrefix major, source = .app recursorPrefix major ∧ HeadConstN recursorName majorIdx recursorPrefix ∧ HeadConstN constructorName constructorArgs major := by - simp only [RecursorIotaPattern, Lean4Lean.SimplePattern.toPattern] at h + simp only [RecursorIotaPattern, Ix.Theory.Named.SimplePattern.toPattern] at h cases h with | app hrecursor hconstructor => exact ⟨_, _, rfl, HeadConstN.of_varN_matches hrecursor, @@ -342,7 +344,7 @@ end RawRecursorRuleRel `RawRecursorRuleRel` records a registered equation and its translated RHS, but a recursor-headed expression alone does not determine which argument is the major or which constructor rule was selected. This relation retains the -missing data in Lean4Lean's own rewrite vocabulary: +missing data in Ix.Theory.Named's own rewrite vocabulary: * the rule's exact array index and the production major index; * the exact catalogued constructor at that index, including parameter and @@ -399,14 +401,14 @@ def RecursorRulePattern.Sound (env : VEnv) ∀ {env' : VEnv}, env ≤ env' → env'.WF → ∀ {uvars : Nat} {Gamma : List VExpr} {source : VExpr} - {levels : List Lean4Lean.VLevel} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr} {A : VExpr}, - Lean4Lean.OnCtx Gamma (env'.IsType uvars) → - Lean4Lean.Pattern.Matches + Ix.Theory.Named.OnCtx Gamma (env'.IsType uvars) → + Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -436,14 +438,14 @@ def RawRecursorRulePatternRel (env : VEnv) (catalog : Catalog) ∀ {env' : VEnv}, env ≤ env' → env'.WF → ∀ {uvars : Nat} {Gamma : List VExpr} {source : VExpr} - {levels : List Lean4Lean.VLevel} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr} {A : VExpr}, - Lean4Lean.OnCtx Gamma (env'.IsType uvars) → - Lean4Lean.Pattern.Matches + Ix.Theory.Named.OnCtx Gamma (env'.IsType uvars) → + Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -668,4 +670,4 @@ def restageMissing end InductiveOracle -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/AliasFormerAdmission.lean b/Ix/Kernel/Verify/Inductive/AliasFormerAdmission.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/AliasFormerAdmission.lean rename to Ix/Kernel/Verify/Inductive/AliasFormerAdmission.lean index f5f84d8e8..e0ad3d12c 100644 --- a/Ix/Tc/Verify/Inductive/AliasFormerAdmission.lean +++ b/Ix/Kernel/Verify/Inductive/AliasFormerAdmission.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Inductive.AliasFormerPattern -import Ix.Tc.Verify.Inductive.OneFamilyAdmission +import Ix.Kernel.Verify.Inductive.AliasFormerPattern +import Ix.Kernel.Verify.Inductive.OneFamilyAdmission /-! # Oracle-free family-result-normalizing singleton admission @@ -12,10 +12,10 @@ the family world. The sole iota pattern is justified by the registered generated equation, not by an inductive oracle. -/ -namespace Ix.Tc.AliasFormerRecursorFixture +namespace Ix.Kernel.AliasFormerRecursorFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures open AliasFormerCertificateFixture open AliasFormerFixture @@ -403,4 +403,4 @@ theorem aliasFormerAtomicClosure : AliasFormerAtomicClosure where iota := AliasFormerPattern.patternRel concreteRule_ruleAt familyResultNormalization := AliasFormerCertificateFixture.breadth -end Ix.Tc.AliasFormerRecursorFixture +end Ix.Kernel.AliasFormerRecursorFixture diff --git a/Ix/Tc/Verify/Inductive/AliasFormerCertificate.lean b/Ix/Kernel/Verify/Inductive/AliasFormerCertificate.lean similarity index 91% rename from Ix/Tc/Verify/Inductive/AliasFormerCertificate.lean rename to Ix/Kernel/Verify/Inductive/AliasFormerCertificate.lean index 10f443821..54c3894b8 100644 --- a/Ix/Tc/Verify/Inductive/AliasFormerCertificate.lean +++ b/Ix/Kernel/Verify/Inductive/AliasFormerCertificate.lean @@ -1,11 +1,11 @@ -import Ix.Tc.Verify.Inductive.ProducedGenerationTransaction -import Lean4Lean.Verify.Environment.InductiveFixtures +import Ix.Kernel.Verify.Inductive.ProducedGenerationTransaction +import Ix.Theory.Named.Verify.Environment.InductiveFixtures /-! # Certified family-result-normalizing fixture `AliasFormer` is declared with the raw result sort `TypeFamilyAlias`, a -transparent alias for `Type`. Lean4Lean's ordinary candidate pipeline +transparent alias for `Type`. Ix.Theory.Named's ordinary candidate pipeline normalizes that result to `Sort 1` before dependent inductive analysis while retaining the raw family and constructor declarations in the generated environment. @@ -15,11 +15,11 @@ anonymous ingress, production checking, and catalog admission are separate layers built on this certificate. -/ -namespace Ix.Tc.AliasFormerCertificateFixture +namespace Ix.Kernel.AliasFormerCertificateFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures noncomputable section @@ -53,8 +53,8 @@ theorem beforeWF : typeFamilyAliasEnv.WF := /-- Exact post-environment selected by that certificate. -/ def finalEnv : VEnv := aliasFormerFinalEnv -/-- The exact L4L-01E package. Its producer-shape index is deliberately -inferred here because Lean4Lean keeps the fixture's concrete shape witness +/-- The exact Spec-01E package. Its producer-shape index is deliberately +inferred here because Ix.Theory.Named keeps the fixture's concrete shape witness private while exposing this public dependent existence theorem. -/ def exactPackage := Classical.choice aliasFormerExactProducedGenerationCandidatePackage_exists @@ -75,7 +75,7 @@ def exactProducedTransaction := exact aliasFormer_addInductCertified_checked) beforeWF -/-- Intentional operational erasure of the exact L4L-01E indices. -/ +/-- Intentional operational erasure of the exact Spec-01E indices. -/ def producedTransaction : ProducedGenerationTransaction typeFamilyAliasEnv finalEnv [] := exactProducedTransaction.toProduced @@ -191,4 +191,4 @@ theorem certifiedFacts : end -end Ix.Tc.AliasFormerCertificateFixture +end Ix.Kernel.AliasFormerCertificateFixture diff --git a/Ix/Tc/Verify/Inductive/AliasFormerFixture.lean b/Ix/Kernel/Verify/Inductive/AliasFormerFixture.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/AliasFormerFixture.lean rename to Ix/Kernel/Verify/Inductive/AliasFormerFixture.lean index e31af049b..f18a65ca7 100644 --- a/Ix/Tc/Verify/Inductive/AliasFormerFixture.lean +++ b/Ix/Kernel/Verify/Inductive/AliasFormerFixture.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Inductive.ConcreteFixture -import Ix.Tc.Verify.Inductive.AliasFormerCertificate -import Ix.Tc.Verify.Ingress.AnonStructural +import Ix.Kernel.Verify.Inductive.ConcreteFixture +import Ix.Kernel.Verify.Inductive.AliasFormerCertificate +import Ix.Kernel.Verify.Ingress.AnonStructural /-! # Production family-result-normalizing fixture @@ -15,11 +15,11 @@ related to the exact Theory declaration, and promoted before the certified family transition; no ambient normalization or inductive oracle is used. -/ -namespace Ix.Tc.AliasFormerFixture +namespace Ix.Kernel.AliasFormerFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures open AliasFormerCertificateFixture open InductiveConcreteFixture @@ -570,4 +570,4 @@ def familyLink : SingletonFamilyCatalogLink RawProjRel.none world.catalog interpretation.toCatalogLinkOfEntries familyIngressExecution catalogEntry trustedCatalog -end Ix.Tc.AliasFormerFixture +end Ix.Kernel.AliasFormerFixture diff --git a/Ix/Tc/Verify/Inductive/AliasFormerPattern.lean b/Ix/Kernel/Verify/Inductive/AliasFormerPattern.lean similarity index 90% rename from Ix/Tc/Verify/Inductive/AliasFormerPattern.lean rename to Ix/Kernel/Verify/Inductive/AliasFormerPattern.lean index 56723f491..746eb262e 100644 --- a/Ix/Tc/Verify/Inductive/AliasFormerPattern.lean +++ b/Ix/Kernel/Verify/Inductive/AliasFormerPattern.lean @@ -1,19 +1,19 @@ -import Ix.Tc.Verify.Inductive.SingletonEnumeration -import Ix.Tc.Verify.Inductive.AliasFormerRecursorFixture +import Ix.Kernel.Verify.Inductive.SingletonEnumeration +import Ix.Kernel.Verify.Inductive.AliasFormerRecursorFixture /-! # Family-result-normalizing enumeration pattern -Once Lean4Lean has normalized `TypeFamilyAlias` to `Type`, `AliasFormer` is +Once Ix.Theory.Named has normalized `TypeFamilyAlias` to `Type`, `AliasFormer` is the one-constructor instance of the certified singleton-enumeration fragment. This module proves that exact fragment classification and instantiates the generic registered-equation pattern theorem for `AliasFormer.mk`. -/ -namespace Ix.Tc.AliasFormerPattern +namespace Ix.Kernel.AliasFormerPattern -open Lean4Lean -open Lean4Lean.InductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures open AliasFormerCertificateFixture open AliasFormerRecursorFixture @@ -87,4 +87,4 @@ theorem patternRel {rule : RecRule .anon} RawRecursorRulePatternRel.of_metadata_sound (metadata hrule) (sound hrule) -end Ix.Tc.AliasFormerPattern +end Ix.Kernel.AliasFormerPattern diff --git a/Ix/Tc/Verify/Inductive/AliasFormerRecursorFixture.lean b/Ix/Kernel/Verify/Inductive/AliasFormerRecursorFixture.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/AliasFormerRecursorFixture.lean rename to Ix/Kernel/Verify/Inductive/AliasFormerRecursorFixture.lean index b29ca8516..0722f0c27 100644 --- a/Ix/Tc/Verify/Inductive/AliasFormerRecursorFixture.lean +++ b/Ix/Kernel/Verify/Inductive/AliasFormerRecursorFixture.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Inductive.AliasFormerFixture +import Ix.Kernel.Verify.Inductive.AliasFormerFixture /-! # Production `AliasFormer.rec` fixture @@ -9,11 +9,11 @@ the normalized result `Type`, the stored recursor and iota equation are the exact artifacts registered by the certificate for the raw declaration. -/ -namespace Ix.Tc.AliasFormerRecursorFixture +namespace Ix.Kernel.AliasFormerRecursorFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures open AliasFormerCertificateFixture open AliasFormerFixture open InductiveConcreteFixture @@ -720,4 +720,4 @@ def recursorLink : SingletonRecursorCatalogLink RawProjRel.none world.catalog recursorInterpretation.toCatalogLinkOfEntry recursorIngressExecution catalog_recursor trustedCatalog -end Ix.Tc.AliasFormerRecursorFixture +end Ix.Kernel.AliasFormerRecursorFixture diff --git a/Ix/Tc/Verify/Inductive/AliasRecAdmission.lean b/Ix/Kernel/Verify/Inductive/AliasRecAdmission.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/AliasRecAdmission.lean rename to Ix/Kernel/Verify/Inductive/AliasRecAdmission.lean index 7dcc65165..a1027c0ee 100644 --- a/Ix/Tc/Verify/Inductive/AliasRecAdmission.lean +++ b/Ix/Kernel/Verify/Inductive/AliasRecAdmission.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Inductive.AliasRecSoundness -import Ix.Tc.Verify.Inductive.OneFamilyAdmission +import Ix.Kernel.Verify.Inductive.AliasRecSoundness +import Ix.Kernel.Verify.Inductive.OneFamilyAdmission /-! # Oracle-free recursive-field-normalizing direct-recursive admission @@ -12,10 +12,10 @@ the family world. The sole iota pattern is justified by the registered generated equation, not by an inductive oracle. -/ -namespace Ix.Tc.AliasRecRecursorFixture +namespace Ix.Kernel.AliasRecRecursorFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures open AliasRecCertificateFixture open AliasRecFixture @@ -401,5 +401,5 @@ theorem aliasRecAtomicClosure : AliasRecAtomicClosure where iota := AliasRecPattern.patternRel concreteRule_ruleAt recursiveFieldNormalization := AliasRecCertificateFixture.breadth -end Ix.Tc.AliasRecRecursorFixture +end Ix.Kernel.AliasRecRecursorFixture diff --git a/Ix/Tc/Verify/Inductive/AliasRecCertificate.lean b/Ix/Kernel/Verify/Inductive/AliasRecCertificate.lean similarity index 91% rename from Ix/Tc/Verify/Inductive/AliasRecCertificate.lean rename to Ix/Kernel/Verify/Inductive/AliasRecCertificate.lean index 0aec2232e..f407a1c63 100644 --- a/Ix/Tc/Verify/Inductive/AliasRecCertificate.lean +++ b/Ix/Kernel/Verify/Inductive/AliasRecCertificate.lean @@ -1,27 +1,27 @@ -import Ix.Tc.Verify.Inductive.Certificate -import Lean4Lean.Verify.Environment.InductiveFixtures +import Ix.Kernel.Verify.Inductive.Certificate +import Ix.Theory.Named.Verify.Environment.InductiveFixtures /-! # Certified recursive-field-normalizing fixture `AliasRec.mk` retains the raw field `RecAlias AliasRec`, where `RecAlias` is -a transparent identity definition. Lean4Lean's checked normalization path +a transparent identity definition. Ix.Theory.Named's checked normalization path unfolds that field to the direct recursive occurrence `AliasRec` before dependent inductive analysis, while the generated environment continues to store the raw constructor declaration. -Lean4Lean exposes the exact checked generation and its semantic WF theorem, +Ix.Theory.Named exposes the exact checked generation and its semantic WF theorem, but deliberately does not add another fixture-specific public certificate wrapper. This module performs that narrow consumer-side packaging. Physical anonymous ingress, production checking, and catalog admission remain separate layers. -/ -namespace Ix.Tc.AliasRecCertificateFixture +namespace Ix.Kernel.AliasRecCertificateFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures /-- The transparent identity definition preceding `AliasRec`. -/ def recAliasValue : VDefVal := recAliasVal @@ -45,7 +45,7 @@ theorem recAliasDeclWF : theorem beforeWF : recAliasEnv.WF := ⟨[.def recAliasValue], .decl recAliasDeclWF .empty⟩ -/-- Consumer-facing packaging of Lean4Lean's exact checker-produced +/-- Consumer-facing packaging of Ix.Theory.Named's exact checker-produced `AliasRec` generation. The proof field is erased by `addInductCertified`; the executable generation is definitionally the upstream checked artifact. -/ def certificate : aliasRecRawDecl.GenerationCertificate recAliasEnv where @@ -155,4 +155,4 @@ theorem certifiedFacts : CertifiedGenerationFacts recAliasEnv finalEnv transaction.certificate := transaction.facts -end Ix.Tc.AliasRecCertificateFixture +end Ix.Kernel.AliasRecCertificateFixture diff --git a/Ix/Tc/Verify/Inductive/AliasRecFixture.lean b/Ix/Kernel/Verify/Inductive/AliasRecFixture.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/AliasRecFixture.lean rename to Ix/Kernel/Verify/Inductive/AliasRecFixture.lean index 6106ea15b..4118e5a8b 100644 --- a/Ix/Tc/Verify/Inductive/AliasRecFixture.lean +++ b/Ix/Kernel/Verify/Inductive/AliasRecFixture.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Inductive.ConcreteFixture -import Ix.Tc.Verify.Inductive.AliasRecCertificate -import Ix.Tc.Verify.Ingress.AnonStructural +import Ix.Kernel.Verify.Inductive.ConcreteFixture +import Ix.Kernel.Verify.Inductive.AliasRecCertificate +import Ix.Kernel.Verify.Ingress.AnonStructural /-! # Production recursive-field-normalizing fixture @@ -15,11 +15,11 @@ related to the exact Theory declaration, and promoted before the certified family transition; no ambient inductive or normalization oracle is used. -/ -namespace Ix.Tc.AliasRecFixture +namespace Ix.Kernel.AliasRecFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures open AliasRecCertificateFixture open InductiveConcreteFixture @@ -569,4 +569,4 @@ def familyLink : SingletonFamilyCatalogLink RawProjRel.none world.catalog interpretation.toCatalogLinkOfEntries familyIngressExecution catalogEntry trustedCatalog -end Ix.Tc.AliasRecFixture +end Ix.Kernel.AliasRecFixture diff --git a/Ix/Tc/Verify/Inductive/AliasRecPattern.lean b/Ix/Kernel/Verify/Inductive/AliasRecPattern.lean similarity index 96% rename from Ix/Tc/Verify/Inductive/AliasRecPattern.lean rename to Ix/Kernel/Verify/Inductive/AliasRecPattern.lean index 60dbcf95f..48b6309b8 100644 --- a/Ix/Tc/Verify/Inductive/AliasRecPattern.lean +++ b/Ix/Kernel/Verify/Inductive/AliasRecPattern.lean @@ -1,5 +1,7 @@ -import Ix.Tc.Verify.Inductive.IotaPattern -import Ix.Tc.Verify.Inductive.AliasRecRecursorFixture +import Ix.Kernel.Verify.Inductive.IotaPattern +import Ix.Kernel.Verify.Inductive.AliasRecRecursorFixture + +open Ix.Theory (VLevel) /-! # Recursive-field-normalizing direct-recursive iota pattern @@ -15,10 +17,10 @@ motive, minor premise, and constructor-field captures. The soundness proof beta-reduces that application to the registered equation. -/ -namespace Ix.Tc.AliasRecPattern +namespace Ix.Kernel.AliasRecPattern -open Lean4Lean -open Lean4Lean.InductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures open AliasRecCertificateFixture open AliasRecFixture open AliasRecRecursorFixture @@ -182,5 +184,5 @@ theorem metadata {rule : RecRule .anon} simpa only [pattern, mkFieldCount, show (1 : UInt64).toNat = 1 from rfl] using hfields -end Ix.Tc.AliasRecPattern +end Ix.Kernel.AliasRecPattern diff --git a/Ix/Tc/Verify/Inductive/AliasRecRecursorFixture.lean b/Ix/Kernel/Verify/Inductive/AliasRecRecursorFixture.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/AliasRecRecursorFixture.lean rename to Ix/Kernel/Verify/Inductive/AliasRecRecursorFixture.lean index 8fc9dcbe3..0f6fb668f 100644 --- a/Ix/Tc/Verify/Inductive/AliasRecRecursorFixture.lean +++ b/Ix/Kernel/Verify/Inductive/AliasRecRecursorFixture.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Inductive.AliasRecFixture +import Ix.Kernel.Verify.Inductive.AliasRecFixture /-! # Production `AliasRec.rec` fixture @@ -9,11 +9,11 @@ recursive-field-alias-bearing family. Its minor premise retains the raw field normalized direct-recursive value `motive a`. -/ -namespace Ix.Tc.AliasRecRecursorFixture +namespace Ix.Kernel.AliasRecRecursorFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures open AliasRecCertificateFixture open AliasRecFixture open InductiveConcreteFixture @@ -726,4 +726,4 @@ def recursorLink : SingletonRecursorCatalogLink RawProjRel.none world.catalog recursorInterpretation.toCatalogLinkOfEntry recursorIngressExecution catalog_recursor trustedCatalog -end Ix.Tc.AliasRecRecursorFixture +end Ix.Kernel.AliasRecRecursorFixture diff --git a/Ix/Tc/Verify/Inductive/AliasRecSoundness.lean b/Ix/Kernel/Verify/Inductive/AliasRecSoundness.lean similarity index 95% rename from Ix/Tc/Verify/Inductive/AliasRecSoundness.lean rename to Ix/Kernel/Verify/Inductive/AliasRecSoundness.lean index cbdb4d98d..66a4f4f2e 100644 --- a/Ix/Tc/Verify/Inductive/AliasRecSoundness.lean +++ b/Ix/Kernel/Verify/Inductive/AliasRecSoundness.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Inductive.AliasRecPattern +import Ix.Kernel.Verify.Inductive.AliasRecPattern + +open Ix.Theory (VLevel) /-! # Recursive-field-normalizing direct-recursive iota soundness @@ -12,10 +14,10 @@ throughout; recursive classification comes from the certified normalized view. -/ -namespace Ix.Tc.AliasRecPattern +namespace Ix.Kernel.AliasRecPattern -open Lean4Lean -open Lean4Lean.InductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures open AliasRecCertificateFixture open AliasRecFixture open AliasRecRecursorFixture @@ -210,7 +212,7 @@ theorem patternSound {rule : RecRule .anon} hconstructorApplied⟩ := htype.app_inv hfutureWF.ordered hGamma obtain ⟨recursorHeadType, hrecursorHeadTyped⟩ := - Lean4Lean.VEnv.HasType.appN_head hfutureWF hGamma hrecursorApplied + Ix.Theory.Named.VEnv.HasType.appN_head hfutureWF hGamma hrecursorApplied obtain ⟨recursorConstant, hrecursorLookup, hlevelsWF, hlevelsArity⟩ := hrecursorHeadTyped.const_inv hfutureWF.ordered hGamma have hcertifiedRecursorLookup := @@ -233,7 +235,7 @@ theorem patternSound {rule : RecRule .anon} subst levelTail obtain ⟨constructorHeadType, hconstructorHeadTyped⟩ := - Lean4Lean.VEnv.HasType.appN_head hfutureWF hGamma hconstructorApplied + Ix.Theory.Named.VEnv.HasType.appN_head hfutureWF hGamma hconstructorApplied obtain ⟨constructorConstant, hconstructorLookup, constructorLevelsWF, hconstructorLevelsArity⟩ := hconstructorHeadTyped.const_inv hfutureWF.ordered hGamma @@ -282,7 +284,7 @@ theorem patternSound {rule : RecRule .anon} have hrecursorConstantTyped : future.HasType uvars Gamma (.const ``AliasRec.rec [u]) (generation.recType.instL [u]) := by - have htyped := Lean4Lean.VEnv.HasType.const (Γ := Gamma) + have htyped := Ix.Theory.Named.VEnv.HasType.const (Γ := Gamma) hcertifiedRecursorLookup hlevelsWF hlevelsArity rw [generationRecursorName] at htyped simpa [VInductDecl.GenerationChecked.recursor] using htyped @@ -306,14 +308,14 @@ theorem patternSound {rule : RecRule .anon} (VExpr.appN ((generation.rule 0 normalized).lhs.instL [u]) [motive, minor]) (equationFieldType u motive minor) := - Lean4Lean.VEnv.HasType.transfer_appN_telescope_instRev + Ix.Theory.Named.VEnv.HasType.transfer_appN_telescope_instRev hfutureWF hGamma hcommonLength hrecursorApplied hrecursorCommonType hequationLhsCommonType have hconstructorConstantTyped : future.HasType uvars Gamma (.const ``AliasRec.mk []) constructorFieldType := by simpa [constructorFieldType, normalizedName] using - (Lean4Lean.VEnv.HasType.const (Γ := Gamma) + (Ix.Theory.Named.VEnv.HasType.const (Γ := Gamma) hcertifiedConstructorLookup constructorLevelsWF hconstructorLevelsArity) have hconstructorFieldHead : future.HasType uvars Gamma @@ -342,7 +344,7 @@ theorem patternSound {rule : RecRule .anon} rw [fieldBinders_eq] rfl have hequationLhsFieldsApplied := - Lean4Lean.VEnv.HasType.transfer_appN_telescope_instRev + Ix.Theory.Named.VEnv.HasType.transfer_appN_telescope_instRev hfutureWF hGamma hfieldLength hconstructorApplied hconstructorFieldHead' hequationFieldHead have hequationLhsApplied : future.HasType uvars Gamma @@ -354,7 +356,7 @@ theorem patternSound {rule : RecRule .anon} VExpr.appN_append] exact hequationLhsFieldsApplied have hequationApplied := - Lean4Lean.VEnv.IsDefEq.appN_same hfutureWF hGamma hequation + Ix.Theory.Named.VEnv.IsDefEq.appN_same hfutureWF hGamma hequation hequationLhsApplied have hequationLhsApplied' := hequationLhsApplied @@ -364,7 +366,7 @@ theorem patternSound {rule : RecRule .anon} (ruleBinders.map (VExpr.instL [u])).length := by simpa only [List.length_cons, List.length_nil, List.length_map] using ruleBinders_length.symm - have hlhsBeta := Lean4Lean.VEnv.HasType.lamN_appN_beta + have hlhsBeta := Ix.Theory.Named.VEnv.HasType.lamN_appN_beta hfutureWF hGamma hruleArgsLength hequationLhsApplied' rw [lhsBody_open] at hlhsBeta have hlhsBeta' : future.IsDefEqU uvars Gamma @@ -399,4 +401,4 @@ theorem patternRel {rule : RecRule .anon} RawRecursorRulePatternRel.of_metadata_sound (metadata hrule) (patternSound hrule) -end Ix.Tc.AliasRecPattern +end Ix.Kernel.AliasRecPattern diff --git a/Ix/Tc/Verify/Inductive/AnnotatedPiAdmission.lean b/Ix/Kernel/Verify/Inductive/AnnotatedPiAdmission.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/AnnotatedPiAdmission.lean rename to Ix/Kernel/Verify/Inductive/AnnotatedPiAdmission.lean index c55dc99b7..b5e2899b8 100644 --- a/Ix/Tc/Verify/Inductive/AnnotatedPiAdmission.lean +++ b/Ix/Kernel/Verify/Inductive/AnnotatedPiAdmission.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Inductive.AnnotatedPiSoundness -import Ix.Tc.Verify.Inductive.OneFamilyAdmission +import Ix.Kernel.Verify.Inductive.AnnotatedPiSoundness +import Ix.Kernel.Verify.Inductive.OneFamilyAdmission /-! # Oracle-free annotation-normalizing recursive-Pi admission @@ -12,10 +12,10 @@ the family world. The sole iota pattern is justified by the registered generated equation, not by an inductive oracle. -/ -namespace Ix.Tc.AnnotatedPiRecursorFixture +namespace Ix.Kernel.AnnotatedPiRecursorFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures open AnnotatedPiCertificateFixture open AnnotatedPiFixture @@ -403,4 +403,4 @@ theorem annotatedPiAtomicClosure : AnnotatedPiAtomicClosure where iota := AnnotatedPiPattern.patternRel concreteRule_ruleAt annotationNormalization := AnnotatedPiCertificateFixture.breadth -end Ix.Tc.AnnotatedPiRecursorFixture +end Ix.Kernel.AnnotatedPiRecursorFixture diff --git a/Ix/Tc/Verify/Inductive/AnnotatedPiCertificate.lean b/Ix/Kernel/Verify/Inductive/AnnotatedPiCertificate.lean similarity index 91% rename from Ix/Tc/Verify/Inductive/AnnotatedPiCertificate.lean rename to Ix/Kernel/Verify/Inductive/AnnotatedPiCertificate.lean index 258dc2bf2..cef1c052a 100644 --- a/Ix/Tc/Verify/Inductive/AnnotatedPiCertificate.lean +++ b/Ix/Kernel/Verify/Inductive/AnnotatedPiCertificate.lean @@ -1,26 +1,26 @@ -import Ix.Tc.Verify.Inductive.ProducedGenerationTransaction -import Lean4Lean.Verify.Environment.InductiveFixtures +import Ix.Kernel.Verify.Inductive.ProducedGenerationTransaction +import Ix.Theory.Named.Verify.Environment.InductiveFixtures /-! # Certified annotation-normalizing recursive-Pi fixture `AnnotatedPi.mk` retains the raw binder domain `outParam Prop`, while the -Lean4Lean analyzer classifies recursion through the normalized domain `Prop`. +Ix.Theory.Named analyzer classifies recursion through the normalized domain `Prop`. This is the first Ix transaction whose certified generation is deliberately non-identity: the stored family and generated artifacts preserve raw syntax, but recursive classification is owned by the checked view produced by the -ordinary Lean4Lean candidate pipeline. +ordinary Ix.Theory.Named candidate pipeline. The module is still Theory-facing. Physical anonymous ingress, production checking, and catalog admission are separate layers built on this exact certificate. -/ -namespace Ix.Tc.AnnotatedPiCertificateFixture +namespace Ix.Kernel.AnnotatedPiCertificateFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures noncomputable section @@ -51,7 +51,7 @@ theorem outParamDeclWF : rfl /-- Explicit well-formed history for the annotation environment. The public -Lean4Lean certificate needs this environment as input, rather than silently +Ix.Theory.Named certificate needs this environment as input, rather than silently treating the reducible annotation as a primitive. -/ theorem beforeWF : outParamEnv.WF := ⟨[.def outParamValue], .decl outParamDeclWF .empty⟩ @@ -59,8 +59,8 @@ theorem beforeWF : outParamEnv.WF := /-- Exact post-environment selected by that certificate. -/ def finalEnv : VEnv := annotatedPiFinalEnv -/-- The exact L4L-01E package. Its producer-shape index is deliberately -inferred here because Lean4Lean keeps the fixture's concrete shape witness +/-- The exact Spec-01E package. Its producer-shape index is deliberately +inferred here because Ix.Theory.Named keeps the fixture's concrete shape witness private while exposing this public dependent existence theorem. -/ def exactPackage := Classical.choice annotatedPiExactProducedGenerationCandidatePackage_exists @@ -81,7 +81,7 @@ def exactProducedTransaction := exact annotatedPi_addInductCertified) beforeWF -/-- Intentional operational erasure of the exact L4L-01E indices. -/ +/-- Intentional operational erasure of the exact Spec-01E indices. -/ def producedTransaction : ProducedGenerationTransaction outParamEnv finalEnv [] := exactProducedTransaction.toProduced @@ -211,4 +211,4 @@ theorem certifiedFacts : end -end Ix.Tc.AnnotatedPiCertificateFixture +end Ix.Kernel.AnnotatedPiCertificateFixture diff --git a/Ix/Tc/Verify/Inductive/AnnotatedPiFixture.lean b/Ix/Kernel/Verify/Inductive/AnnotatedPiFixture.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/AnnotatedPiFixture.lean rename to Ix/Kernel/Verify/Inductive/AnnotatedPiFixture.lean index 1809344e6..225fd5a72 100644 --- a/Ix/Tc/Verify/Inductive/AnnotatedPiFixture.lean +++ b/Ix/Kernel/Verify/Inductive/AnnotatedPiFixture.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Inductive.ConcreteFixture -import Ix.Tc.Verify.Inductive.AnnotatedPiCertificate -import Ix.Tc.Verify.Ingress.AnonStructural +import Ix.Kernel.Verify.Inductive.ConcreteFixture +import Ix.Kernel.Verify.Inductive.AnnotatedPiCertificate +import Ix.Kernel.Verify.Ingress.AnonStructural /-! # Production annotation-normalizing recursive-Pi fixture @@ -15,11 +15,11 @@ related to the exact Theory declaration, and promoted before the certified family transition; no ambient inductive or normalization oracle is used. -/ -namespace Ix.Tc.AnnotatedPiFixture +namespace Ix.Kernel.AnnotatedPiFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures open AnnotatedPiCertificateFixture open InductiveConcreteFixture @@ -570,4 +570,4 @@ def familyLink : SingletonFamilyCatalogLink RawProjRel.none world.catalog interpretation.toCatalogLinkOfEntries familyIngressExecution catalogEntry trustedCatalog -end Ix.Tc.AnnotatedPiFixture +end Ix.Kernel.AnnotatedPiFixture diff --git a/Ix/Tc/Verify/Inductive/AnnotatedPiPattern.lean b/Ix/Kernel/Verify/Inductive/AnnotatedPiPattern.lean similarity index 96% rename from Ix/Tc/Verify/Inductive/AnnotatedPiPattern.lean rename to Ix/Kernel/Verify/Inductive/AnnotatedPiPattern.lean index effa2ef86..44bbca657 100644 --- a/Ix/Tc/Verify/Inductive/AnnotatedPiPattern.lean +++ b/Ix/Kernel/Verify/Inductive/AnnotatedPiPattern.lean @@ -1,5 +1,7 @@ -import Ix.Tc.Verify.Inductive.IotaPattern -import Ix.Tc.Verify.Inductive.AnnotatedPiRecursorFixture +import Ix.Kernel.Verify.Inductive.IotaPattern +import Ix.Kernel.Verify.Inductive.AnnotatedPiRecursorFixture + +open Ix.Theory (VLevel) /-! # Annotation-normalizing recursive-Pi iota pattern @@ -16,10 +18,10 @@ apply it to the motive, minor premise, and constructor field captures. The soundness proof beta-reduces that application to the registered equation. -/ -namespace Ix.Tc.AnnotatedPiPattern +namespace Ix.Kernel.AnnotatedPiPattern -open Lean4Lean -open Lean4Lean.InductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures open AnnotatedPiCertificateFixture open AnnotatedPiFixture open AnnotatedPiRecursorFixture @@ -183,4 +185,4 @@ theorem metadata {rule : RecRule .anon} simpa only [pattern, mkFieldCount, show (1 : UInt64).toNat = 1 from rfl] using hfields -end Ix.Tc.AnnotatedPiPattern +end Ix.Kernel.AnnotatedPiPattern diff --git a/Ix/Tc/Verify/Inductive/AnnotatedPiRecursorFixture.lean b/Ix/Kernel/Verify/Inductive/AnnotatedPiRecursorFixture.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/AnnotatedPiRecursorFixture.lean rename to Ix/Kernel/Verify/Inductive/AnnotatedPiRecursorFixture.lean index f317169c2..c2c15a417 100644 --- a/Ix/Tc/Verify/Inductive/AnnotatedPiRecursorFixture.lean +++ b/Ix/Kernel/Verify/Inductive/AnnotatedPiRecursorFixture.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Inductive.AnnotatedPiFixture +import Ix.Kernel.Verify.Inductive.AnnotatedPiFixture /-! # Production `AnnotatedPi.rec` fixture @@ -9,11 +9,11 @@ the raw field type `(p : outParam Prop) -> AnnotatedPi` with the normalized induction-hypothesis domain `(p : Prop) -> motive (f p)`. -/ -namespace Ix.Tc.AnnotatedPiRecursorFixture +namespace Ix.Kernel.AnnotatedPiRecursorFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures open AnnotatedPiCertificateFixture open AnnotatedPiFixture open InductiveConcreteFixture @@ -731,4 +731,4 @@ def recursorLink : SingletonRecursorCatalogLink RawProjRel.none world.catalog recursorInterpretation.toCatalogLinkOfEntry recursorIngressExecution catalog_recursor trustedCatalog -end Ix.Tc.AnnotatedPiRecursorFixture +end Ix.Kernel.AnnotatedPiRecursorFixture diff --git a/Ix/Tc/Verify/Inductive/AnnotatedPiSoundness.lean b/Ix/Kernel/Verify/Inductive/AnnotatedPiSoundness.lean similarity index 95% rename from Ix/Tc/Verify/Inductive/AnnotatedPiSoundness.lean rename to Ix/Kernel/Verify/Inductive/AnnotatedPiSoundness.lean index 24e74839e..0023a6b7e 100644 --- a/Ix/Tc/Verify/Inductive/AnnotatedPiSoundness.lean +++ b/Ix/Kernel/Verify/Inductive/AnnotatedPiSoundness.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Inductive.AnnotatedPiPattern +import Ix.Kernel.Verify.Inductive.AnnotatedPiPattern + +open Ix.Theory (VLevel) /-! # Annotation-normalizing recursive-Pi iota soundness @@ -11,10 +13,10 @@ already the complete generated-rule telescope. The reducible `outParam Prop` annotation remains present in that raw field binder throughout the argument. -/ -namespace Ix.Tc.AnnotatedPiPattern +namespace Ix.Kernel.AnnotatedPiPattern -open Lean4Lean -open Lean4Lean.InductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures open AnnotatedPiCertificateFixture open AnnotatedPiFixture open AnnotatedPiRecursorFixture @@ -209,7 +211,7 @@ theorem patternSound {rule : RecRule .anon} hconstructorApplied⟩ := htype.app_inv hfutureWF.ordered hGamma obtain ⟨recursorHeadType, hrecursorHeadTyped⟩ := - Lean4Lean.VEnv.HasType.appN_head hfutureWF hGamma hrecursorApplied + Ix.Theory.Named.VEnv.HasType.appN_head hfutureWF hGamma hrecursorApplied obtain ⟨recursorConstant, hrecursorLookup, hlevelsWF, hlevelsArity⟩ := hrecursorHeadTyped.const_inv hfutureWF.ordered hGamma have hcertifiedRecursorLookup := @@ -232,7 +234,7 @@ theorem patternSound {rule : RecRule .anon} subst levelTail obtain ⟨constructorHeadType, hconstructorHeadTyped⟩ := - Lean4Lean.VEnv.HasType.appN_head hfutureWF hGamma hconstructorApplied + Ix.Theory.Named.VEnv.HasType.appN_head hfutureWF hGamma hconstructorApplied obtain ⟨constructorConstant, hconstructorLookup, constructorLevelsWF, hconstructorLevelsArity⟩ := hconstructorHeadTyped.const_inv hfutureWF.ordered hGamma @@ -281,7 +283,7 @@ theorem patternSound {rule : RecRule .anon} have hrecursorConstantTyped : future.HasType uvars Gamma (.const ``AnnotatedPi.rec [u]) (generation.recType.instL [u]) := by - have htyped := Lean4Lean.VEnv.HasType.const (Γ := Gamma) + have htyped := Ix.Theory.Named.VEnv.HasType.const (Γ := Gamma) hcertifiedRecursorLookup hlevelsWF hlevelsArity rw [generationRecursorName] at htyped simpa [VInductDecl.GenerationChecked.recursor] using htyped @@ -305,14 +307,14 @@ theorem patternSound {rule : RecRule .anon} (VExpr.appN ((generation.rule 0 normalized).lhs.instL [u]) [motive, minor]) (equationFieldType u motive minor) := - Lean4Lean.VEnv.HasType.transfer_appN_telescope_instRev + Ix.Theory.Named.VEnv.HasType.transfer_appN_telescope_instRev hfutureWF hGamma hcommonLength hrecursorApplied hrecursorCommonType hequationLhsCommonType have hconstructorConstantTyped : future.HasType uvars Gamma (.const ``AnnotatedPi.mk []) constructorFieldType := by simpa [constructorFieldType, normalizedName] using - (Lean4Lean.VEnv.HasType.const (Γ := Gamma) + (Ix.Theory.Named.VEnv.HasType.const (Γ := Gamma) hcertifiedConstructorLookup constructorLevelsWF hconstructorLevelsArity) have hconstructorFieldHead : future.HasType uvars Gamma @@ -341,7 +343,7 @@ theorem patternSound {rule : RecRule .anon} rw [fieldBinders_eq] rfl have hequationLhsFieldsApplied := - Lean4Lean.VEnv.HasType.transfer_appN_telescope_instRev + Ix.Theory.Named.VEnv.HasType.transfer_appN_telescope_instRev hfutureWF hGamma hfieldLength hconstructorApplied hconstructorFieldHead' hequationFieldHead have hequationLhsApplied : future.HasType uvars Gamma @@ -353,7 +355,7 @@ theorem patternSound {rule : RecRule .anon} VExpr.appN_append] exact hequationLhsFieldsApplied have hequationApplied := - Lean4Lean.VEnv.IsDefEq.appN_same hfutureWF hGamma hequation + Ix.Theory.Named.VEnv.IsDefEq.appN_same hfutureWF hGamma hequation hequationLhsApplied have hequationLhsApplied' := hequationLhsApplied @@ -363,7 +365,7 @@ theorem patternSound {rule : RecRule .anon} (ruleBinders.map (VExpr.instL [u])).length := by simpa only [List.length_cons, List.length_nil, List.length_map] using ruleBinders_length.symm - have hlhsBeta := Lean4Lean.VEnv.HasType.lamN_appN_beta + have hlhsBeta := Ix.Theory.Named.VEnv.HasType.lamN_appN_beta hfutureWF hGamma hruleArgsLength hequationLhsApplied' rw [lhsBody_open] at hlhsBeta have hlhsBeta' : future.IsDefEqU uvars Gamma @@ -399,4 +401,4 @@ theorem patternRel {rule : RecRule .anon} RawRecursorRulePatternRel.of_metadata_sound (metadata hrule) (patternSound hrule) -end Ix.Tc.AnnotatedPiPattern +end Ix.Kernel.AnnotatedPiPattern diff --git a/Ix/Tc/Verify/Inductive/BlockCertificate.lean b/Ix/Kernel/Verify/Inductive/BlockCertificate.lean similarity index 91% rename from Ix/Tc/Verify/Inductive/BlockCertificate.lean rename to Ix/Kernel/Verify/Inductive/BlockCertificate.lean index 70672f555..7f9c1c284 100644 --- a/Ix/Tc/Verify/Inductive/BlockCertificate.lean +++ b/Ix/Kernel/Verify/Inductive/BlockCertificate.lean @@ -1,21 +1,21 @@ -import Lean4Lean.Theory.Typing.InductiveCertificate +import Ix.Theory.Named.Typing.InductiveCertificate /-! # Certified mutual-inductive block transactions -This is the Theory-only consumer boundary for Lean4Lean's L4L-08 block +This is the Theory-only consumer boundary for Ix.Theory.Named's Spec-08 block generation certificate. A mutual block is one atomic semantic transaction: all family constants are inserted before any constructors, all constructors before any recursors, and all recursors before the globally flattened iota rules. It must not be represented as a list of singleton transactions. As with `CertifiedGenerationTransaction`, this module imports no executable -Lean4Lean verifier and mentions no Ix address, catalog, or checker state. +Ix.Theory.Named verifier and mentions no Ix address, catalog, or checker state. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean +open Ix.Theory.Named /-- One successful proof-carrying block generation, together with the well-formed Theory environment it extends. -/ @@ -54,7 +54,7 @@ structure CertifiedBlockGenerationFacts {source : VInductDecl} namespace CertifiedBlockGenerationTransaction -/-- Repackage Ix's historical transaction adapter as Lean4Lean's current +/-- Repackage Ix's historical transaction adapter as Ix.Theory.Named's current consumer certificate. This is definitionally the same semantic input, successful atomic transaction, and pre-environment WF proof; no second generation run or compatibility axiom is introduced. -/ @@ -65,7 +65,7 @@ def toBlockCertificate {source : VInductDecl} {before after : VEnv} success := tx.success beforeWF := tx.beforeWF -/-- Recover L4L-08's exact four-phase trace for the atomic block. -/ +/-- Recover Spec-08's exact four-phase trace for the atomic block. -/ theorem trace {source : VInductDecl} {before after : VEnv} (tx : CertifiedBlockGenerationTransaction source before after) : Nonempty (VEnv.AddInductBlockGenerationTrace before after @@ -102,4 +102,4 @@ theorem facts {source : VInductDecl} {before after : VEnv} end CertifiedBlockGenerationTransaction -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/BlockPatternSoundness.lean b/Ix/Kernel/Verify/Inductive/BlockPatternSoundness.lean similarity index 90% rename from Ix/Tc/Verify/Inductive/BlockPatternSoundness.lean rename to Ix/Kernel/Verify/Inductive/BlockPatternSoundness.lean index 122afbf8b..3ac6b51d7 100644 --- a/Ix/Tc/Verify/Inductive/BlockPatternSoundness.lean +++ b/Ix/Kernel/Verify/Inductive/BlockPatternSoundness.lean @@ -1,23 +1,25 @@ -import Ix.Tc.Verify.Inductive.BlockCertificate +import Ix.Kernel.Verify.Inductive.BlockCertificate + +open Ix.Theory (VLevel) /-! # Consumer boundary for certified generated-pattern soundness -Lean4Lean's block certificate already identifies the exact generated pattern, +Ix.Theory.Named's block certificate already identifies the exact generated pattern, RHS template, checks, registered equation, and well-formed post-environment for every flattened constructor. The remaining consumer theorem says that a well-typed match satisfying those checks is definitionally equal to that RHS in every future environment. -This interface is deliberately stated entirely in Lean4Lean's Theory +This interface is deliberately stated entirely in Ix.Theory.Named's Theory vocabulary. It carries no Ix address, catalog, ownership, ingress, or checker claim, so a temporary upstream witness cannot discharge any downstream correspondence obligation. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean +open Ix.Theory.Named /-- Environment-parametric soundness of one dependent pattern payload. Keeping this independent of any certificate makes transport along an equality @@ -64,4 +66,4 @@ def CertifiedBlockRulePatternSound (certificate.generation.ruleCheck certificate.ruleClosure (List.mem_of_getElem? entry)) -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/CandidateSyntax.lean b/Ix/Kernel/Verify/Inductive/CandidateSyntax.lean similarity index 95% rename from Ix/Tc/Verify/Inductive/CandidateSyntax.lean rename to Ix/Kernel/Verify/Inductive/CandidateSyntax.lean index 7f38c432e..97f5d3b15 100644 --- a/Ix/Tc/Verify/Inductive/CandidateSyntax.lean +++ b/Ix/Kernel/Verify/Inductive/CandidateSyntax.lean @@ -1,13 +1,13 @@ -import Ix.Tc.Verify.Inductive.PositivityTraceAdapter -import Ix.Tc.Verify.Totalization +import Ix.Kernel.Verify.Inductive.PositivityTraceAdapter +import Ix.Kernel.Verify.Totalization /-! -# Exact candidate syntax shared by Ix and Lean4Lean +# Exact candidate syntax shared by Ix and Ix.Theory.Named The Theory-level `VExpr` relations used elsewhere in the verification are semantic: they deliberately forget binder annotations and concrete free- variable identifiers. Constructor positivity has one stricter boundary. -Lean4Lean's `isValidIndApp?` inspects the exact kernel `Lean.Expr`, so a +Ix.Theory.Named's `isValidIndApp?` inspects the exact kernel `Lean.Expr`, so a Theory-level definitional-equality result cannot justify that executable syntax test. @@ -20,7 +20,7 @@ of those choices and discharges the corresponding field of `FlatPositivityTraceTransport` without a semantic oracle. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Exact correspondence between one Ix positivity expression and one Lean kernel candidate expression. This intentionally excludes syntax erased by @@ -330,18 +330,18 @@ theorem mentionsAnyAddr_eq_hasIndOcc (blocks : CandidateBlockRel nameOf ixAddrs leanConsts) (relation : CandidateSyntaxRel nameOf fvarRel levelRel ixExpr leanExpr) : exprMentionsAnyAddr ixExpr ixAddrs = - Lean4Lean.AddInductive.hasIndOcc leanConsts leanExpr := by + Ix.Theory.Named.AddInductive.hasIndOcc leanConsts leanExpr := by induction relation with | bvar => simp [exprMentionsAnyAddr, exprMentionsAddr, - exprMentionsAddr.go, Lean4Lean.AddInductive.hasIndOcc] + exprMentionsAddr.go, Ix.Theory.Named.AddInductive.hasIndOcc] | fvar => simp [exprMentionsAnyAddr, exprMentionsAddr, - exprMentionsAddr.go, Lean4Lean.AddInductive.hasIndOcc] + exprMentionsAddr.go, Ix.Theory.Named.AddInductive.hasIndOcc] | sort => simp [exprMentionsAnyAddr, exprMentionsAddr, - exprMentionsAddr.go, Lean4Lean.AddInductive.hasIndOcc] + exprMentionsAddr.go, Ix.Theory.Named.AddInductive.hasIndOcc] | @const id ixLevels info leanName leanLevels hname levels => simp only [exprMentionsAnyAddr, exprMentionsAddr_equation, exprMentionsAddr_go_const, exprMentionsAddr_go_nil, - Lean4Lean.AddInductive.hasIndOcc] + Ix.Theory.Named.AddInductive.hasIndOcc] have occurrence : exprMentionsAddr (.const id ixLevels info) = (fun addr => id.addr == addr) := by @@ -350,11 +350,11 @@ theorem mentionsAnyAddr_eq_hasIndOcc rw [occurrence] exact blocks hname | app fn arg ihFn ihArg => - rw [mentionsAny_app, Lean4Lean.AddInductive.hasIndOcc, ihFn, ihArg] + rw [mentionsAny_app, Ix.Theory.Named.AddInductive.hasIndOcc, ihFn, ihArg] | forallE domain body ihDomain ihBody => - rw [mentionsAny_all, Lean4Lean.AddInductive.hasIndOcc, + rw [mentionsAny_all, Ix.Theory.Named.AddInductive.hasIndOcc, ihDomain, ihBody] end CandidateSyntaxRel -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/Certificate.lean b/Ix/Kernel/Verify/Inductive/Certificate.lean similarity index 94% rename from Ix/Tc/Verify/Inductive/Certificate.lean rename to Ix/Kernel/Verify/Inductive/Certificate.lean index ead590788..06eaa4596 100644 --- a/Ix/Tc/Verify/Inductive/Certificate.lean +++ b/Ix/Kernel/Verify/Inductive/Certificate.lean @@ -1,11 +1,11 @@ -import Lean4Lean.Theory.Typing.EnvLemmas +import Ix.Theory.Named.Typing.EnvLemmas /-! # Certified inductive-generation transactions -This module is the Theory-only consumer boundary for Lean4Lean's normalized +This module is the Theory-only consumer boundary for Ix.Theory.Named's normalized inductive-generation certificate. It deliberately imports no -`Lean4Lean.Verify` module and mentions no Ix catalog, address, checker state, +`Ix.Theory.Named.Verify` module and mentions no Ix catalog, address, checker state, or recursor-pattern relation. `GenerationCertificate` proves that one exact normalized generation is @@ -20,9 +20,9 @@ checked, how their addresses map to names, or that production iota metadata matches the generated Theory rules. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean +open Ix.Theory.Named /-- One successful proof-carrying normalized inductive transaction, together with the well-formed input environment needed to extend a Theory history. @@ -36,7 +36,7 @@ structure CertifiedGenerationTransaction (source : VInductDecl) beforeWF : before.WF /-- The complete Theory-owned consequences of a certified generation -transaction. All fields concern only the Lean4Lean source, generated +transaction. All fields concern only the Ix.Theory.Named source, generated artifacts, and input/output `VEnv`s. -/ structure CertifiedGenerationFacts {source : VInductDecl} (before after : VEnv) (certificate : source.GenerationCertificate before) : @@ -128,4 +128,4 @@ theorem facts {source : VInductDecl} {before after : VEnv} end CertifiedGenerationTransaction -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/ConcreteFixture.lean b/Ix/Kernel/Verify/Inductive/ConcreteFixture.lean similarity index 97% rename from Ix/Tc/Verify/Inductive/ConcreteFixture.lean rename to Ix/Kernel/Verify/Inductive/ConcreteFixture.lean index 41eb54154..b311259a3 100644 --- a/Ix/Tc/Verify/Inductive/ConcreteFixture.lean +++ b/Ix/Kernel/Verify/Inductive/ConcreteFixture.lean @@ -1,5 +1,7 @@ -import Ix.Tc.Verify.Inductive.IngressExecution -import Ix.Tc.Verify.Check.PreTranslationCompatibility +import Ix.Kernel.Verify.Inductive.IngressExecution +import Ix.Kernel.Verify.Check.PreTranslationCompatibility + +open Ix.Theory (VLevel) /-! # Shared executable inductive-fixture support @@ -11,9 +13,9 @@ core binder syntax returned by anonymous ingress. Keeping these here avoids making indexed-recursive coverage depend on the Boolean enumeration fixture. -/ -namespace Ix.Tc.InductiveConcreteFixture +namespace Ix.Kernel.InductiveConcreteFixture -open Lean4Lean (VEnv VExpr) +open Ix.Theory.Named (VEnv VExpr) /-- Store a constant at its production content address. -/ def storeConstant (env : Ixon.Env) (constant : Ixon.Constant) : @@ -214,4 +216,4 @@ instance kExprScopedDecidable (depth : UInt64) (levelBound : Nat) .isFalse fun hscoped => h ((scopedExprB_eq_true_iff depth levelBound expression).mpr hscoped) -end Ix.Tc.InductiveConcreteFixture +end Ix.Kernel.InductiveConcreteFixture diff --git a/Ix/Tc/Verify/Inductive/ConstructorPositivityTraversal.lean b/Ix/Kernel/Verify/Inductive/ConstructorPositivityTraversal.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/ConstructorPositivityTraversal.lean rename to Ix/Kernel/Verify/Inductive/ConstructorPositivityTraversal.lean index 3a5adfac5..7df19c39f 100644 --- a/Ix/Tc/Verify/Inductive/ConstructorPositivityTraversal.lean +++ b/Ix/Kernel/Verify/Inductive/ConstructorPositivityTraversal.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Inductive.RecursivePositivityTraversal +import Ix.Kernel.Verify.Inductive.RecursivePositivityTraversal /-! # Complete constructor-positivity traversal @@ -11,7 +11,7 @@ parameter opening, source-ordered field traversal, and final local-context restoration. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Exact successful execution of the production parameter-opening loop. The `short` case records the deliberately permissive result used so A1/A2 can @@ -366,4 +366,4 @@ theorem checkPositivity_success (methods : Methods m) exact .success (checkPositivityCore_success methods hcore) hrestored end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/ConstructorValidationTraversal.lean b/Ix/Kernel/Verify/Inductive/ConstructorValidationTraversal.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/ConstructorValidationTraversal.lean rename to Ix/Kernel/Verify/Inductive/ConstructorValidationTraversal.lean index 3b2914fd9..106a2826f 100644 --- a/Ix/Tc/Verify/Inductive/ConstructorValidationTraversal.lean +++ b/Ix/Kernel/Verify/Inductive/ConstructorValidationTraversal.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Inductive.ConstructorPositivityTraversal +import Ix.Kernel.Verify.Inductive.ConstructorPositivityTraversal /-! # Production constructor-validation traversal @@ -10,7 +10,7 @@ execution around that call: derived metadata, shared-parameter agreement, safety gating, field-universe validation, and constructor-return validation. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Successful constructor-metadata validation together with the exact physical constructor header selected by its lookup. The aggregate run alone @@ -704,4 +704,4 @@ theorem checkInductiveBlockImpl_success (methods : Methods m) (checkInductiveMembers_success methods hinductives) hrun end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/EliminationBreadthFixture.lean b/Ix/Kernel/Verify/Inductive/EliminationBreadthFixture.lean similarity index 97% rename from Ix/Tc/Verify/Inductive/EliminationBreadthFixture.lean rename to Ix/Kernel/Verify/Inductive/EliminationBreadthFixture.lean index 0592aee14..7d8f4403d 100644 --- a/Ix/Tc/Verify/Inductive/EliminationBreadthFixture.lean +++ b/Ix/Kernel/Verify/Inductive/EliminationBreadthFixture.lean @@ -1,14 +1,14 @@ import Ix.CompileDriver -import Ix.Tc.Verify.Inductive.ConcreteFixture -import Ix.Tc.Verify.Inductive.SingletonRecursor -import Ix.Tc.Verify.Ingress.AnonStructural -import Lean4Lean.Verify.Environment.EliminationFixturesEq -import Lean4Lean.Verify.Environment.EliminationFixturesSmall +import Ix.Kernel.Verify.Inductive.ConcreteFixture +import Ix.Kernel.Verify.Inductive.SingletonRecursor +import Ix.Kernel.Verify.Ingress.AnonStructural +import Ix.Theory.Named.Verify.Environment.EliminationFixturesEq +import Ix.Theory.Named.Verify.Environment.EliminationFixturesSmall /-! # Concrete small-elimination and K-target breadth -Lean4Lean's L4L-06 fixtures retain the exact kernel elimination traversal and +Ix.Theory.Named's Spec-06 fixtures retain the exact kernel elimination traversal and align it with the generated Theory metadata. This module takes the remaining Ix step: it compiles those same kernel declarations to production Ixon blocks, ingresses the family and recursor projections, and runs both production block @@ -16,7 +16,7 @@ checkers. The two cases are deliberately complementary: -* `L4L06SmallSource` keeps its source universe but introduces no fresh +* `Spec06SmallSource` keeps its source universe but introduces no fresh elimination universe; and * `Eq` uses large elimination and declares the independently computed K bit. @@ -24,11 +24,11 @@ Thus the shared singleton-recursor relation is exercised at both universe layouts and at both values of the physical `k` field. -/ -namespace Ix.Tc.EliminationBreadthFixture +namespace Ix.Kernel.EliminationBreadthFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures local instance eliminationAddressDecidableEq : DecidableEq Address := AnonStructural.addressDecidableEq @@ -686,4 +686,4 @@ theorem kTargetAcceptance : KTargetAcceptance where productionLayout := eqPreparationMatches_eq productionK := eqComputeKMatches_eq -end Ix.Tc.EliminationBreadthFixture +end Ix.Kernel.EliminationBreadthFixture diff --git a/Ix/Tc/Verify/Inductive/EnumerationAcceptance.lean b/Ix/Kernel/Verify/Inductive/EnumerationAcceptance.lean similarity index 97% rename from Ix/Tc/Verify/Inductive/EnumerationAcceptance.lean rename to Ix/Kernel/Verify/Inductive/EnumerationAcceptance.lean index fefada66f..23edafca0 100644 --- a/Ix/Tc/Verify/Inductive/EnumerationAcceptance.lean +++ b/Ix/Kernel/Verify/Inductive/EnumerationAcceptance.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Inductive.EnumerationFixture -import Ix.Tc.Verify.Inductive.OneFamilyAdmission +import Ix.Kernel.Verify.Inductive.EnumerationFixture +import Ix.Kernel.Verify.Inductive.OneFamilyAdmission /-! # Concrete singleton-enumeration checker acceptance @@ -10,7 +10,7 @@ constructs its canonical recursor cache; the separate recursor block is then checked against that exact generated result. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace BooleanEnumerationFixture @@ -359,8 +359,8 @@ theorem familyOwner : directInductiveOwner_inductiveMemberOf familyDirectOwner private theorem certifiedConstructor_inductiveMemberOf - {source : Lean4Lean.VInductDecl} {familyId block : KId .anon} - {index : Nat} {sourceConstructor : Lean4Lean.VConstVal} + {source : Ix.Theory.Named.VInductDecl} {familyId block : KId .anon} + {index : Nat} {sourceConstructor : Ix.Theory.Named.VConstVal} {concrete familyConcrete : KConst .anon} {catalog : Catalog} (hshape : concrete.IsCertifiedSingletonConstructor source familyId index sourceConstructor) @@ -391,7 +391,7 @@ theorem recursorOwner : recursorOwnerNative private theorem certifiedRecursor_not_inductiveMemberOf - {source : Lean4Lean.VInductDecl} {generation : source.GenerationChecked} + {source : Ix.Theory.Named.VInductDecl} {generation : source.GenerationChecked} {constructorIds : Array (KId .anon)} {concrete : KConst .anon} {catalog : Catalog} {block : KId .anon} (hshape : concrete.IsCertifiedSingletonRecursor source generation @@ -407,7 +407,7 @@ theorem recursorNotFamilyOwner : certifiedRecursor_not_inductiveMemberOf recursorShape private theorem certifiedFamily_not_recursorMemberOf - {source : Lean4Lean.VInductDecl} {generation : source.GenerationChecked} + {source : Ix.Theory.Named.VInductDecl} {generation : source.GenerationChecked} {constructorIds : Array (KId .anon)} {concrete : KConst .anon} {block : KId .anon} (hshape : concrete.IsCertifiedSingletonFamily source generation @@ -423,8 +423,8 @@ theorem familyNotRecursorOwner : certifiedFamily_not_recursorMemberOf familyShape private theorem certifiedConstructor_not_recursorMemberOf - {source : Lean4Lean.VInductDecl} {familyId : KId .anon} - {index : Nat} {sourceConstructor : Lean4Lean.VConstVal} + {source : Ix.Theory.Named.VInductDecl} {familyId : KId .anon} + {index : Nat} {sourceConstructor : Ix.Theory.Named.VConstVal} {concrete : KConst .anon} {block : KId .anon} (hshape : concrete.IsCertifiedSingletonConstructor source familyId index sourceConstructor) : @@ -729,4 +729,4 @@ theorem endToEndAcceptance : EndToEndAcceptance where end BooleanEnumerationFixture -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/EnumerationFixture.lean b/Ix/Kernel/Verify/Inductive/EnumerationFixture.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/EnumerationFixture.lean rename to Ix/Kernel/Verify/Inductive/EnumerationFixture.lean index 641e2ef46..c02c71f8e 100644 --- a/Ix/Tc/Verify/Inductive/EnumerationFixture.lean +++ b/Ix/Kernel/Verify/Inductive/EnumerationFixture.lean @@ -1,22 +1,24 @@ -import Ix.Tc.Verify.Check.SingletonInductive -import Ix.Tc.Verify.Check.PreTranslationCompatibility -import Lean4Lean.Theory.InductiveFixtures +import Ix.Kernel.Verify.Check.SingletonInductive +import Ix.Kernel.Verify.Check.PreTranslationCompatibility +import Ix.Theory.Named.InductiveFixtures + +open Ix.Theory (VLevel) /-! # Concrete singleton-enumeration fixture This module closes E2b's executable witness with a two-constructor Boolean -enumeration. The Theory side uses Lean4Lean's checked identity-generation +enumeration. The Theory side uses Ix.Theory.Named's checked identity-generation certificate; the concrete side below is built from the actual Ixon block encoding and production anonymous ingress/checker functions. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace BooleanEnumerationFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures open VInductDecl local instance anonKIdDecidableEq : DecidableEq (KId .anon) := fun left right => @@ -25,7 +27,7 @@ local instance anonKIdDecidableEq : DecidableEq (KId .anon) := fun left right => else .isFalse fun equality => h (by cases equality; exact beq_self_eq_true left) -/-! ## Lean4Lean certificate -/ +/-! ## Ix.Theory.Named certificate -/ def checked : boolDecl.Checked where type := boolType @@ -1275,4 +1277,4 @@ def recursorLink : SingletonRecursorCatalogLink RawProjRel.none world.catalog end BooleanEnumerationFixture -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/ExactLeanSyntax.lean b/Ix/Kernel/Verify/Inductive/ExactLeanSyntax.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/ExactLeanSyntax.lean rename to Ix/Kernel/Verify/Inductive/ExactLeanSyntax.lean index cd080cf0b..c7bfa00c3 100644 --- a/Ix/Tc/Verify/Inductive/ExactLeanSyntax.lean +++ b/Ix/Kernel/Verify/Inductive/ExactLeanSyntax.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Inductive.CandidateSyntax +import Ix.Kernel.Verify.Inductive.CandidateSyntax /-! # Exact executable comparison for Lean kernel syntax @@ -17,7 +17,7 @@ separate exact metadata relation. Every successful comparison is proved to imply ordinary Lean equality. -/ -namespace Ix.Tc.ExactLeanSyntax +namespace Ix.Kernel.ExactLeanSyntax /-- Structural equality for universe levels, ignoring their cached `data` field and comparing metavariable identifiers through their names. -/ @@ -214,4 +214,4 @@ theorem exceptBool_eq_ok_of_check {outcome : Except ε Bool} outcome = .ok expected := by cases outcome <;> simp_all [exceptBoolCheck] -end Ix.Tc.ExactLeanSyntax +end Ix.Kernel.ExactLeanSyntax diff --git a/Ix/Tc/Verify/Inductive/GeneratedRecursorAcceptance.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorAcceptance.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/GeneratedRecursorAcceptance.lean rename to Ix/Kernel/Verify/Inductive/GeneratedRecursorAcceptance.lean index 8dbaa86fb..91df67353 100644 --- a/Ix/Tc/Verify/Inductive/GeneratedRecursorAcceptance.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorAcceptance.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Inductive.GeneratedRecursorComparison -import Ix.Tc.Verify.RecursiveMethods.CallDomains +import Ix.Kernel.Verify.Inductive.GeneratedRecursorComparison +import Ix.Kernel.Verify.RecursiveMethods.CallDomains /-! # Semantic acceptance of a generated-recursor candidate @@ -15,9 +15,9 @@ entry yields a DefEq-quotiented canonical stored type and every same-index stored rule. No coherence-only or whole-comparison oracle is available. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VEnv VExpr VInductDecl) +open Ix.Theory.Named (VEnv VExpr VInductDecl) open GeneratedRecursorSemantics namespace GeneratedRecursorSemantics @@ -402,4 +402,4 @@ theorem checkGeneratedRecursorFromCache_canonical end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/GeneratedRecursorAcceptanceClosure.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorAcceptanceClosure.lean similarity index 97% rename from Ix/Tc/Verify/Inductive/GeneratedRecursorAcceptanceClosure.lean rename to Ix/Kernel/Verify/Inductive/GeneratedRecursorAcceptanceClosure.lean index 034ac7560..78c4e2a5b 100644 --- a/Ix/Tc/Verify/Inductive/GeneratedRecursorAcceptanceClosure.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorAcceptanceClosure.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Inductive.GeneratedRecursorAcceptance -import Ix.Tc.Verify.Check.ScopedActiveBlock -import Ix.Tc.Verify.RecursiveMethods.ScopedCallDomains +import Ix.Kernel.Verify.Inductive.GeneratedRecursorAcceptance +import Ix.Kernel.Verify.Check.ScopedActiveBlock +import Ix.Kernel.Verify.RecursiveMethods.ScopedCallDomains /-! # Run-scoped generated-recursor acceptance closure @@ -11,7 +11,7 @@ through K2S's finite successor-layer method contract. It does not require a global DefEq oracle or place every expression in an unbounded call domain. -/ -namespace Ix.Tc +namespace Ix.Kernel open GeneratedRecursorSemantics @@ -81,7 +81,7 @@ theorem checkGeneratedRecursorCandidate_canonicalScoped {model : ScopedKernelSuffixModel trProj world} {layer : WhnfLayer} {semantics : CacheSemantics} {support : RunSupport} {calls : Methods.CallDomain} - {source : Lean4Lean.VInductDecl} + {source : Ix.Theory.Named.VInductDecl} {generation : source.GenerationChecked} {ty : KExpr .anon} {declaredLvls : UInt64} {declaredIsUnsafe : Bool} {params motives minors indices : UInt64} @@ -121,7 +121,7 @@ theorem checkGeneratedRecursorFromCache_canonicalScoped {model : ScopedKernelSuffixModel trProj world} {layer : WhnfLayer} {semantics : CacheSemantics} {support : RunSupport} {calls : Methods.CallDomain} - {source : Lean4Lean.VInductDecl} + {source : Ix.Theory.Named.VInductDecl} {generation : source.GenerationChecked} {recBlock id : KId .anon} {ty : KExpr .anon} {declaredLvls : UInt64} {declaredIsUnsafe : Bool} @@ -171,7 +171,7 @@ theorem checkGeneratedRecursorCandidate_canonicalActiveScoped {layer : WhnfLayer} {semantics : CacheSemantics} {support : RunSupport} {members : Array (KId .anon)} {calls : Methods.CallDomain} - {source : Lean4Lean.VInductDecl} + {source : Ix.Theory.Named.VInductDecl} {generation : source.GenerationChecked} {ty : KExpr .anon} {declaredLvls : UInt64} {declaredIsUnsafe : Bool} {params motives minors indices : UInt64} @@ -213,7 +213,7 @@ theorem checkGeneratedRecursorFromCache_canonicalActiveScoped {layer : WhnfLayer} {semantics : CacheSemantics} {support : RunSupport} {members : Array (KId .anon)} {calls : Methods.CallDomain} - {source : Lean4Lean.VInductDecl} + {source : Ix.Theory.Named.VInductDecl} {generation : source.GenerationChecked} {recBlock id : KId .anon} {ty : KExpr .anon} {declaredLvls : UInt64} {declaredIsUnsafe : Bool} @@ -257,4 +257,4 @@ theorem checkGeneratedRecursorFromCache_canonicalActiveScoped end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/GeneratedRecursorAdmission.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorAdmission.lean similarity index 95% rename from Ix/Tc/Verify/Inductive/GeneratedRecursorAdmission.lean rename to Ix/Kernel/Verify/Inductive/GeneratedRecursorAdmission.lean index 063556602..6709d5c35 100644 --- a/Ix/Tc/Verify/Inductive/GeneratedRecursorAdmission.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorAdmission.lean @@ -1,12 +1,12 @@ -import Ix.Tc.Verify.Inductive.GeneratedRecursorInitialInvariant -import Ix.Tc.Verify.Inductive.OneFamilyAdmission +import Ix.Kernel.Verify.Inductive.GeneratedRecursorInitialInvariant +import Ix.Kernel.Verify.Inductive.OneFamilyAdmission /-! # Canonical generated-recursor admission This module closes the first E2c production recursor transaction. The certified family transaction has already installed `IndexedVec.rec` and its -two equations in Lean4Lean's Theory environment. Ix nevertheless keeps the +two equations in Ix.Theory.Named's Theory environment. Ix nevertheless keeps the separately ingressed recursor block untrusted while `checkRecursorMemberImpl` compares its immutable declaration against the generated cache. @@ -19,11 +19,11 @@ pattern, and only then converts active cache authority into stable trust and publishes the block-success verdict. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures open IndexedRecursiveCertificateFixture /-! ## Per-member semantic provenance already present after family generation -/ @@ -209,4 +209,4 @@ theorem familyRecursorAtomicClosure : CanonicalRecursorAtomicClosure where oneFamily := oneFamilyAtomicClosure stable := familyMemberCheckStable -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/GeneratedRecursorCheckerFixture.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorCheckerFixture.lean similarity index 97% rename from Ix/Tc/Verify/Inductive/GeneratedRecursorCheckerFixture.lean rename to Ix/Kernel/Verify/Inductive/GeneratedRecursorCheckerFixture.lean index cc4db09be..f6fcce8c8 100644 --- a/Ix/Tc/Verify/Inductive/GeneratedRecursorCheckerFixture.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorCheckerFixture.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Inductive.GeneratedRecursorSelection -import Ix.Tc.Verify.Inductive.GeneratedRecursorAcceptanceClosure -import Ix.Tc.Verify.Inductive.GeneratedRecursorCommitFixture +import Ix.Kernel.Verify.Inductive.GeneratedRecursorSelection +import Ix.Kernel.Verify.Inductive.GeneratedRecursorAcceptanceClosure +import Ix.Kernel.Verify.Inductive.GeneratedRecursorCommitFixture /-! # Production generated-recursor checker fixture @@ -15,11 +15,11 @@ Consequently, later semantic closure cannot justify the stored declaration by silently reusing the generated artifact's translation. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture open GeneratedRecursorSemantics open IndexedRecursiveCertificateFixture -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named.InductiveReplayFixtures /-! ## Actual frozen-cache checker execution -/ @@ -112,7 +112,7 @@ theorem familyStoredArtifactTranslations : transaction.certificate.generation.recursor.uvars nameOf RawProjRel.none recursorConcrete.ty recursorRules := by refine ⟨⟨transaction.certificate.generation.recType, ?_⟩, ?_⟩ - · simpa [Lean4Lean.VInductDecl.GenerationChecked.recursor] using + · simpa [Ix.Theory.Named.VInductDecl.GenerationChecked.recursor] using recursorTypeTyped · intro index hindex have indexTwo : index < 2 := by @@ -371,4 +371,4 @@ theorem familyCacheCheckExecution : ⟨familyCacheCheckRun, familyInstalledRecursorCanonical, familyStoredArtifactTranslations⟩ -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/GeneratedRecursorCommitFixture.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorCommitFixture.lean similarity index 97% rename from Ix/Tc/Verify/Inductive/GeneratedRecursorCommitFixture.lean rename to Ix/Kernel/Verify/Inductive/GeneratedRecursorCommitFixture.lean index 9704834d3..e922291ff 100644 --- a/Ix/Tc/Verify/Inductive/GeneratedRecursorCommitFixture.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorCommitFixture.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Inductive.GeneratedRecursorRuleFixture +import Ix.Kernel.Verify.Inductive.GeneratedRecursorRuleFixture /-! # Production generated-recursor commit fixture @@ -16,11 +16,11 @@ proofs do not need to trust either a callback-mutated cache type or a callback-written rule array. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture open GeneratedRecursorSemantics open IndexedRecursiveCertificateFixture -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named.InductiveReplayFixtures local instance generatedCommitAddressDecidableEq : DecidableEq Address := AnonStructural.addressDecidableEq @@ -257,7 +257,7 @@ theorem familyGeneratedSnapshotType_eq : familyGeneratedSnapshotTypeNative /-- The immutable type selected by the public commit is the exact -Lean4Lean-generated recursor type. -/ +Ix.Theory.Named-generated recursor type. -/ theorem familyGeneratedSnapshotTypeCanonical : CanonicalTypeS indexedVecFinalEnv nameOf RawProjRel.none transaction.certificate.generation @@ -268,7 +268,7 @@ theorem familyGeneratedSnapshotTypeCanonical : [] (familyGeneratedSnapshot[0]'familyGeneratedSnapshotNonempty).ty transaction.certificate.generation.recType rw [familyGeneratedSnapshotType_eq] - simpa [Lean4Lean.VInductDecl.GenerationChecked.recursor] using + simpa [Ix.Theory.Named.VInductDecl.GenerationChecked.recursor] using recursorTypeTyped /-- The local completed entry's position-zero rule array is canonical. -/ @@ -336,4 +336,4 @@ theorem familyRuleCommitExecution : installed[0] := ⟨familyRuleCommitRun, familyRuleCommitCanonical⟩ -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/GeneratedRecursorComparison.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorComparison.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/GeneratedRecursorComparison.lean rename to Ix/Kernel/Verify/Inductive/GeneratedRecursorComparison.lean index 49b7efd07..66cd7903a 100644 --- a/Ix/Tc/Verify/Inductive/GeneratedRecursorComparison.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorComparison.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Inductive.GeneratedRecursorSemantics +import Ix.Kernel.Verify.Inductive.GeneratedRecursorSemantics /-! # Exhaustive generated-recursor comparison @@ -15,7 +15,7 @@ semantic layer can interpret only these concrete successful DefEq calls; it does not receive an oracle for the comparison as a whole. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Exact successful executions of the positional generated/stored rule comparisons, including all state changes made by DefEq. -/ @@ -263,4 +263,4 @@ theorem checkGeneratedRecursorFromCache_success end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/GeneratedRecursorInitialInvariant.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorInitialInvariant.lean similarity index 97% rename from Ix/Tc/Verify/Inductive/GeneratedRecursorInitialInvariant.lean rename to Ix/Kernel/Verify/Inductive/GeneratedRecursorInitialInvariant.lean index 0ba194afc..125320ce2 100644 --- a/Ix/Tc/Verify/Inductive/GeneratedRecursorInitialInvariant.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorInitialInvariant.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Inductive.GeneratedRecursorMemberFixture +import Ix.Kernel.Verify.Inductive.GeneratedRecursorMemberFixture + +open Ix.Theory (VLevel) /-! # Concrete generated-recursor ingress invariant @@ -11,11 +13,11 @@ canonical finite maps; it does not assume injectivity of Blake3 outside that run. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures open IndexedRecursiveCertificateFixture local instance initialInvariantKConstDecidableEq : @@ -294,37 +296,37 @@ private theorem familyMemberResolveExact private theorem familyMemberNatInfoLookup : familyAcceptedWorld.venv.constants ``Nat = - some Lean4Lean.InductiveFixtures.natType.toVConstant := by + some Ix.Theory.Named.InductiveFixtures.natType.toVConstant := by native_decide private theorem familyMemberZeroInfoLookup : familyAcceptedWorld.venv.constants ``Nat.zero = - some Lean4Lean.InductiveFixtures.natType.ctors[0].toVConstant := by + some Ix.Theory.Named.InductiveFixtures.natType.ctors[0].toVConstant := by native_decide private theorem familyMemberSuccInfoLookup : familyAcceptedWorld.venv.constants ``Nat.succ = - some Lean4Lean.InductiveFixtures.natType.ctors[1].toVConstant := by + some Ix.Theory.Named.InductiveFixtures.natType.ctors[1].toVConstant := by native_decide private theorem familyMemberFamilyInfoLookup : familyAcceptedWorld.venv.constants ``IndexedVec = - some Lean4Lean.InductiveFixtures.indexedVecType.toVConstant := by + some Ix.Theory.Named.InductiveFixtures.indexedVecType.toVConstant := by native_decide private theorem familyMemberNilInfoLookup : familyAcceptedWorld.venv.constants ``IndexedVec.nil = - some Lean4Lean.InductiveFixtures.indexedVecType.ctors[0].toVConstant := by + some Ix.Theory.Named.InductiveFixtures.indexedVecType.ctors[0].toVConstant := by native_decide private theorem familyMemberConsInfoLookup : familyAcceptedWorld.venv.constants ``IndexedVec.cons = - some Lean4Lean.InductiveFixtures.indexedVecType.ctors[1].toVConstant := by + some Ix.Theory.Named.InductiveFixtures.indexedVecType.ctors[1].toVConstant := by native_decide private theorem familyMemberResolveNatExact : TrustedConstRel RawProjRel.none familyAcceptedWorld natId natConcrete - ``Nat Lean4Lean.InductiveFixtures.natType.toVConstant := by + ``Nat Ix.Theory.Named.InductiveFixtures.natType.toVConstant := by apply familyMemberResolveExact familyMemberResolveNat · simpa only [familyAcceptedWorld_nameOf_eq] using nameOf_nat · exact familyMemberNatInfoLookup @@ -332,7 +334,7 @@ private theorem familyMemberResolveNatExact : private theorem familyMemberResolveZeroExact : TrustedConstRel RawProjRel.none familyAcceptedWorld zeroId zeroConcrete ``Nat.zero - Lean4Lean.InductiveFixtures.natType.ctors[0].toVConstant := by + Ix.Theory.Named.InductiveFixtures.natType.ctors[0].toVConstant := by apply familyMemberResolveExact familyMemberResolveZero · simpa only [familyAcceptedWorld_nameOf_eq] using nameOf_zero · exact familyMemberZeroInfoLookup @@ -340,7 +342,7 @@ private theorem familyMemberResolveZeroExact : private theorem familyMemberResolveSuccExact : TrustedConstRel RawProjRel.none familyAcceptedWorld succId succConcrete ``Nat.succ - Lean4Lean.InductiveFixtures.natType.ctors[1].toVConstant := by + Ix.Theory.Named.InductiveFixtures.natType.ctors[1].toVConstant := by apply familyMemberResolveExact familyMemberResolveSucc · simpa only [familyAcceptedWorld_nameOf_eq] using nameOf_succ · exact familyMemberSuccInfoLookup @@ -348,7 +350,7 @@ private theorem familyMemberResolveSuccExact : private theorem familyMemberResolveFamilyExact : TrustedConstRel RawProjRel.none familyAcceptedWorld familyId familyConcrete ``IndexedVec - Lean4Lean.InductiveFixtures.indexedVecType.toVConstant := by + Ix.Theory.Named.InductiveFixtures.indexedVecType.toVConstant := by apply familyMemberResolveExact familyMemberResolveFamily · simpa only [familyAcceptedWorld_nameOf_eq] using nameOf_family · exact familyMemberFamilyInfoLookup @@ -356,7 +358,7 @@ private theorem familyMemberResolveFamilyExact : private theorem familyMemberResolveNilExact : TrustedConstRel RawProjRel.none familyAcceptedWorld nilId nilConcrete ``IndexedVec.nil - Lean4Lean.InductiveFixtures.indexedVecType.ctors[0].toVConstant := by + Ix.Theory.Named.InductiveFixtures.indexedVecType.ctors[0].toVConstant := by apply familyMemberResolveExact familyMemberResolveNil · simpa only [familyAcceptedWorld_nameOf_eq] using nameOf_nil · exact familyMemberNilInfoLookup @@ -364,27 +366,27 @@ private theorem familyMemberResolveNilExact : private theorem familyMemberResolveConsExact : TrustedConstRel RawProjRel.none familyAcceptedWorld consId consConcrete ``IndexedVec.cons - Lean4Lean.InductiveFixtures.indexedVecType.ctors[1].toVConstant := by + Ix.Theory.Named.InductiveFixtures.indexedVecType.ctors[1].toVConstant := by apply familyMemberResolveExact familyMemberResolveCons · simpa only [familyAcceptedWorld_nameOf_eq] using nameOf_cons · exact familyMemberConsInfoLookup private theorem familyMemberNatTypeHasType : familyAcceptedWorld.venv.HasType familyMemberModel.keys.uvars [] - Lean4Lean.InductiveFixtures.natType.type + Ix.Theory.Named.InductiveFixtures.natType.type (.sort (.succ (.succ .zero))) := by type_tac private theorem familyMemberZeroTypeHasType : familyAcceptedWorld.venv.HasType familyMemberModel.keys.uvars [] - Lean4Lean.InductiveFixtures.natType.ctors[0].type - Lean4Lean.InductiveFixtures.natType.type := by + Ix.Theory.Named.InductiveFixtures.natType.ctors[0].type + Ix.Theory.Named.InductiveFixtures.natType.type := by have hNat := familyMemberNatInfoLookup type_tac private theorem familyMemberSuccTypeHasType : familyAcceptedWorld.venv.HasType familyMemberModel.keys.uvars [] - Lean4Lean.InductiveFixtures.natType.ctors[1].type + Ix.Theory.Named.InductiveFixtures.natType.ctors[1].type (.sort (.succ .zero)) := by have raw : familyAcceptedWorld.venv.HasType familyMemberModel.keys.uvars [] (.forallE (.const ``Nat []) (.const ``Nat [])) @@ -399,7 +401,7 @@ private theorem familyMemberSuccTypeHasType : private theorem familyMemberFamilyTypeHasType : familyAcceptedWorld.venv.HasType familyMemberModel.keys.uvars [] - Lean4Lean.InductiveFixtures.indexedVecType.type + Ix.Theory.Named.InductiveFixtures.indexedVecType.type (.sort (.max (.succ .zero) (.succ (.succ (.param 0))))) := by have raw : familyAcceptedWorld.venv.HasType familyMemberModel.keys.uvars [] (.forallE (.sort (.succ (.param 0))) @@ -418,7 +420,7 @@ private theorem familyMemberFamilyTypeHasType : private theorem familyMemberNilTypeHasType : familyAcceptedWorld.venv.HasType familyMemberModel.keys.uvars [] - Lean4Lean.InductiveFixtures.indexedVecType.ctors[0].type + Ix.Theory.Named.InductiveFixtures.indexedVecType.ctors[0].type (.sort (.succ (.succ (.param 0)))) := by have raw : familyAcceptedWorld.venv.HasType familyMemberModel.keys.uvars [] (.forallE (.sort (.succ (.param 0))) @@ -455,11 +457,11 @@ private theorem familyMemberNilTypeHasType : private theorem familyMemberConsTypeHasType : familyAcceptedWorld.venv.HasType familyMemberModel.keys.uvars [] - Lean4Lean.InductiveFixtures.indexedVecType.ctors[1].type + Ix.Theory.Named.InductiveFixtures.indexedVecType.ctors[1].type (.sort (.max (.succ (.succ (.param 0))) (.max (.succ .zero) (.succ (.param 0))))) := by have raw : familyAcceptedWorld.venv.HasType familyMemberModel.keys.uvars [] - Lean4Lean.InductiveFixtures.indexedVecType.ctors[1].type + Ix.Theory.Named.InductiveFixtures.indexedVecType.ctors[1].type (.sort (.imax (.succ (.succ (.param 0))) (.imax (.succ .zero) (.imax (.succ (.param 0)) @@ -1704,4 +1706,4 @@ theorem familyMemberCheckCanonicalConcrete : (fun _ support => familyMemberSupport_new support) familyMemberInitial_activeInvariant -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/GeneratedRecursorMemberCheck.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorMemberCheck.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/GeneratedRecursorMemberCheck.lean rename to Ix/Kernel/Verify/Inductive/GeneratedRecursorMemberCheck.lean index 5ff2c1550..bb6320dc7 100644 --- a/Ix/Tc/Verify/Inductive/GeneratedRecursorMemberCheck.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorMemberCheck.lean @@ -1,7 +1,7 @@ -import Ix.Tc.Verify.Inductive.GeneratedRecursorAcceptanceClosure -import Ix.Tc.Verify.Inductive.StructuralCacheSemantics -import Ix.Tc.Verify.Check.BlockIdentity -import Ix.Tc.Verify.Whnf.StructEta.ExactMajorTelescope +import Ix.Kernel.Verify.Inductive.GeneratedRecursorAcceptanceClosure +import Ix.Kernel.Verify.Inductive.StructuralCacheSemantics +import Ix.Kernel.Verify.Check.BlockIdentity +import Ix.Kernel.Verify.Whnf.StructEta.ExactMajorTelescope /-! # Generated-recursor member-check handoff @@ -17,7 +17,7 @@ postulate that the prelude preserves the K2S invariant; subsequent modules must prove that from the individual production operations. -/ -namespace Ix.Tc +namespace Ix.Kernel open GeneratedRecursorSemantics @@ -939,7 +939,7 @@ theorem checkPreparedRecursorMember_canonicalScoped {model : ScopedKernelSuffixModel trProj world} {layer : WhnfLayer} {semantics : CacheSemantics} {support : RunSupport} {calls : Methods.CallDomain} - {source : Lean4Lean.VInductDecl} + {source : Ix.Theory.Named.VInductDecl} {generation : source.GenerationChecked} {id : KId .anon} {prepared : PreparedRecursorMemberCheck .anon} {methods : Methods .anon} {initial final : TcState .anon} @@ -983,7 +983,7 @@ theorem checkPreparedRecursorMember_canonicalActiveScoped {layer : WhnfLayer} {semantics : CacheSemantics} {support : RunSupport} {members : Array (KId .anon)} {calls : Methods.CallDomain} - {source : Lean4Lean.VInductDecl} + {source : Ix.Theory.Named.VInductDecl} {generation : source.GenerationChecked} {id : KId .anon} {prepared : PreparedRecursorMemberCheck .anon} {methods : Methods .anon} {initial final : TcState .anon} @@ -1024,4 +1024,4 @@ theorem checkPreparedRecursorMember_canonicalActiveScoped end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/GeneratedRecursorMemberFixture.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorMemberFixture.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/GeneratedRecursorMemberFixture.lean rename to Ix/Kernel/Verify/Inductive/GeneratedRecursorMemberFixture.lean index 8e50253b5..17324a966 100644 --- a/Ix/Tc/Verify/Inductive/GeneratedRecursorMemberFixture.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorMemberFixture.lean @@ -1,9 +1,9 @@ -import Ix.Tc.Verify.Inductive.GeneratedRecursorCheckerFixture -import Ix.Tc.Verify.Inductive.GeneratedRecursorMemberCheck -import Ix.Tc.Verify.Inductive.IndexedBlockValidation -import Ix.Tc.Verify.Inductive.ResultSortTelescope -import Ix.Tc.Verify.RecursiveMethods.ScopedInference -import Ix.Tc.Verify.ScopedSuffix.ClosedContext +import Ix.Kernel.Verify.Inductive.GeneratedRecursorCheckerFixture +import Ix.Kernel.Verify.Inductive.GeneratedRecursorMemberCheck +import Ix.Kernel.Verify.Inductive.IndexedBlockValidation +import Ix.Kernel.Verify.Inductive.ResultSortTelescope +import Ix.Kernel.Verify.RecursiveMethods.ScopedInference +import Ix.Kernel.Verify.ScopedSuffix.ClosedContext /-! # Production recursor-member preparation fixture @@ -20,12 +20,12 @@ separate operation-level preservation obligations; no whole-prelude oracle is introduced. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture open GeneratedRecursorSemantics open IndexedRecursiveCertificateFixture -open Lean4Lean -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveReplayFixtures local instance generatedMemberAddressDecidableEq : DecidableEq Address := AnonStructural.addressDecidableEq @@ -44,11 +44,11 @@ local instance generatedMemberKConstDecidableEq : DecidableEq (KConst .anon) := AnonStructural.constDecidableEq local instance generatedMemberVConstantDecidableEq : - DecidableEq Lean4Lean.VConstant := by + DecidableEq Ix.Theory.Named.VConstant := by intro left right cases left cases right - simp only [Lean4Lean.VConstant.mk.injEq] + simp only [Ix.Theory.Named.VConstant.mk.injEq] infer_instance local instance generatedMemberRecRuleDecidableEq : @@ -160,7 +160,7 @@ theorem familyMemberNatLit_type (value : Nat) : induction value with | zero => simpa [VExpr.natLit, VExpr.natZero, VExpr.nat, VExpr.instL] using - (Lean4Lean.VEnv.HasType.const + (Ix.Theory.Named.VEnv.HasType.const (env := familyAcceptedWorld.venv) (U := familyMemberModel.keys.uvars) (Γ := []) (ci := ⟨0, VExpr.nat⟩) (ls := []) familyNatZeroLookup @@ -169,11 +169,11 @@ theorem familyMemberNatLit_type (value : Nat) : have successor : familyAcceptedWorld.venv.HasType familyMemberModel.keys.uvars [] (.const ``Nat.succ []) (.forallE VExpr.nat VExpr.nat) := by - exact Lean4Lean.VEnv.HasType.const familyNatSuccLookup + exact Ix.Theory.Named.VEnv.HasType.const familyNatSuccLookup (by simp) rfl simpa [VExpr.natLit, VExpr.natSucc, VExpr.nat, VExpr.inst, VExpr.instL] using - Lean4Lean.VEnv.HasType.app successor ih + Ix.Theory.Named.VEnv.HasType.app successor ih /-- The complete literal/projection theory needed by the finite generated- artifact comparison schedule. String literals are impossible because the @@ -631,7 +631,7 @@ theorem familyMemberInitial_equivalences rw [familyMemberInitialEquivEntriesEmpty] at member simp at member -/-- Empty runtime stacks reconstruct the empty Lean4Lean local context even +/-- Empty runtime stacks reconstruct the empty Ix.Theory.Named local context even though the environment and its semantic caches are warm. -/ theorem familyMemberInitial_context : CtxRecon familyAcceptedWorld.venv familyMemberModel.keys.uvars @@ -2435,4 +2435,4 @@ theorem familyMemberCheckCanonicalFromInitialActiveScoped rw [familyMemberPreparationRun] at post exact familyPreparedMemberCheckCanonicalActiveScoped uvars successor post.1 -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/GeneratedRecursorMetadata.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorMetadata.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/GeneratedRecursorMetadata.lean rename to Ix/Kernel/Verify/Inductive/GeneratedRecursorMetadata.lean index c873b366a..147e0d0eb 100644 --- a/Ix/Tc/Verify/Inductive/GeneratedRecursorMetadata.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorMetadata.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Inductive.NestedAuxiliaryExpansion -import Ix.Tc.Verify.Env +import Ix.Kernel.Verify.Inductive.NestedAuxiliaryExpansion +import Ix.Kernel.Verify.Env /-! # Generated recursor metadata from the certified flat block @@ -16,7 +16,7 @@ it makes no assumption about the expression returned by `buildRecType` or the state in which that callback succeeds. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace GeneratedRecursorMetadata @@ -634,4 +634,4 @@ theorem buildAndCacheGeneratedRecursors_metadata end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/GeneratedRecursorRuleFixture.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorRuleFixture.lean similarity index 94% rename from Ix/Tc/Verify/Inductive/GeneratedRecursorRuleFixture.lean rename to Ix/Kernel/Verify/Inductive/GeneratedRecursorRuleFixture.lean index da97d6f17..a4b5738c9 100644 --- a/Ix/Tc/Verify/Inductive/GeneratedRecursorRuleFixture.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorRuleFixture.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Inductive.GeneratedRecursorTypeFixture +import Ix.Kernel.Verify.Inductive.GeneratedRecursorTypeFixture /-! # Production generated-recursor rule fixture @@ -11,15 +11,15 @@ invokes `buildRuleRhs` for both constructors before returning its local batch. As with the type fixture, total projections have unreachable fallbacks. The public run theorem proves the successful data-bearing branch, and the final -theorem identifies every returned rule position with Lean4Lean's canonical +theorem identifies every returned rule position with Ix.Theory.Named's canonical normalized-constructor rule. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture open GeneratedRecursorSemantics open IndexedRecursiveCertificateFixture -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named.InductiveReplayFixtures local instance generatedRuleKIdDecidableEq : DecidableEq (KId .anon) := AnonStructural.idDecidableEq @@ -127,7 +127,7 @@ private theorem generationRuleCountNative : native_decide /-- The array returned by the actual complete-rule builder is positionally the -canonical Lean4Lean rule array for the certified IndexedVec generation. -/ +canonical Ix.Theory.Named rule array for the certified IndexedVec generation. -/ theorem familyBuildRulesCanonical : CanonicalRulesS indexedVecFinalEnv nameOf RawProjRel.none transaction.certificate.generation familyBuiltRules := by @@ -157,7 +157,7 @@ theorem familyCompletedTypeCanonical : transaction.certificate.generation familyCompletedRecursor := by unfold CanonicalTypeS rw [familyCompletedRecursorType_eq] - simpa [Lean4Lean.VInductDecl.GenerationChecked.recursor] using + simpa [Ix.Theory.Named.VInductDecl.GenerationChecked.recursor] using recursorTypeTyped /-- The actual local result of the rule-population core contains both the @@ -187,4 +187,4 @@ theorem familyBuildArtifactsExecution : transaction.certificate.generation familyCompletedRecursor := ⟨familyRulePopulationRun, familyCompletedArtifactsCanonical⟩ -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/GeneratedRecursorSelection.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorSelection.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/GeneratedRecursorSelection.lean rename to Ix/Kernel/Verify/Inductive/GeneratedRecursorSelection.lean index 15d91f28e..9183b0c16 100644 --- a/Ix/Tc/Verify/Inductive/GeneratedRecursorSelection.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorSelection.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Inductive.GeneratedRecursorAcceptance -import Ix.Tc.Verify.Check.ScopedActiveBlock -import Ix.Tc.Verify.RecursiveMethods.ScopedCallDomains +import Ix.Kernel.Verify.Inductive.GeneratedRecursorAcceptance +import Ix.Kernel.Verify.Check.ScopedActiveBlock +import Ix.Kernel.Verify.RecursiveMethods.ScopedCallDomains /-! # Generated-recursor selection callbacks @@ -12,7 +12,7 @@ call is tied to the exact generated array position and the frozen stored type; metadata-only iterations are state-pure. -/ -namespace Ix.Tc +namespace Ix.Kernel open GeneratedRecursorSemantics @@ -30,7 +30,7 @@ def GeneratedSelectionCallPlan (calls : Methods.CallDomain) /-- Closed translations for the stored type and every generated type that the finite selection fold can reach. -/ structure GeneratedSelectionTranslationPlan - (env : Lean4Lean.VEnv) (uvars : Nat) + (env : Ix.Theory.Named.VEnv) (uvars : Nat) (nameOf : Address → Option Lean.Name) (trProj : RawProjRel) (generated : Array (GeneratedRecursor .anon)) (ty : KExpr .anon) : Prop where @@ -291,4 +291,4 @@ theorem selectGeneratedRecursorIndex_preservesActiveScoped end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/GeneratedRecursorSemantics.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorSemantics.lean similarity index 94% rename from Ix/Tc/Verify/Inductive/GeneratedRecursorSemantics.lean rename to Ix/Kernel/Verify/Inductive/GeneratedRecursorSemantics.lean index 79207e2c4..90a3e53e1 100644 --- a/Ix/Tc/Verify/Inductive/GeneratedRecursorSemantics.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorSemantics.lean @@ -1,13 +1,13 @@ -import Ix.Tc.Verify.Inductive.GeneratedRecursorMetadata -import Ix.Tc.Verify.Trans +import Ix.Kernel.Verify.Inductive.GeneratedRecursorMetadata +import Ix.Kernel.Verify.Trans /-! # Canonical semantics of generated recursor artifacts This is the representation boundary for E2c's generated artifacts. It names the exact structural correspondence that the production builders must prove: -the generated type is Lean4Lean's `GenerationChecked.recType`, and rule `i` -is Lean4Lean's `GenerationChecked.rule i` for constructor `i`. Rule lookup is +the generated type is Ix.Theory.Named's `GenerationChecked.recType`, and rule `i` +is Ix.Theory.Named's `GenerationChecked.rule i` for constructor `i`. Rule lookup is positional; equal bodies at a different index are not interchangeable. Types and rules are deliberately separate. The public production commit @@ -16,14 +16,14 @@ local builder. `commitGeneratedRecursorRulesAt_canonicalAt` proves that these two independently established facts compose across that exact transaction. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VEnv VExpr VInductDecl) +open Ix.Theory.Named (VEnv VExpr VInductDecl) namespace GeneratedRecursorSemantics /-- Exact structural correspondence between one immutable Ix generated type -and Lean4Lean's canonical mixed recursor type. -/ +and Ix.Theory.Named's canonical mixed recursor type. -/ def CanonicalTypeS (env : VEnv) (nameOf : Address → Option Lean.Name) (trProj : RawProjRel) {source : VInductDecl} (generation : source.GenerationChecked) @@ -32,7 +32,7 @@ def CanonicalTypeS (env : VEnv) (nameOf : Address → Option Lean.Name) generation.recType /-- Exact positional correspondence between one Ix rule array and all rules -generated by the same Lean4Lean generation. -/ +generated by the same Ix.Theory.Named generation. -/ structure CanonicalRulesS (env : VEnv) (nameOf : Address → Option Lean.Name) (trProj : RawProjRel) {source : VInductDecl} (generation : source.GenerationChecked) @@ -58,7 +58,7 @@ structure CanonicalArtifactsS (env : VEnv) namespace CanonicalRulesS /-- A positional Ix rule corresponds to the rule at the same position in -Lean4Lean's public `generatedRules` array. -/ +Ix.Theory.Named's public `generatedRules` array. -/ theorem generatedRuleAt {env : VEnv} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {source : VInductDecl} @@ -104,7 +104,7 @@ end CanonicalArtifactsS /-! ## Defeq-quotiented form consumed by recursor acceptance -/ /-- Semantic type correspondence after quotienting the structural target by -Lean4Lean definitional equality. Stored recursors accepted by production need +Ix.Theory.Named definitional equality. Stored recursors accepted by production need this form: `isDefEq` does not imply syntax equality. -/ def CanonicalType (env : VEnv) (nameOf : Address → Option Lean.Name) (trProj : RawProjRel) {source : VInductDecl} @@ -240,4 +240,4 @@ theorem RecM.commitGeneratedRecursorRulesAt_canonicalAt end GeneratedRecursorSemantics -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/GeneratedRecursorTypeClosure.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorTypeClosure.lean similarity index 96% rename from Ix/Tc/Verify/Inductive/GeneratedRecursorTypeClosure.lean rename to Ix/Kernel/Verify/Inductive/GeneratedRecursorTypeClosure.lean index f072f0be9..750eff66d 100644 --- a/Ix/Tc/Verify/Inductive/GeneratedRecursorTypeClosure.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorTypeClosure.lean @@ -1,5 +1,7 @@ -import Ix.Tc.Verify.Inductive.GeneratedRecursorSemantics -import Ix.Tc.Verify.Whnf +import Ix.Kernel.Verify.Inductive.GeneratedRecursorSemantics +import Ix.Kernel.Verify.Whnf + +open Ix.Theory (VLevel) /-! # Generated recursor telescope closure @@ -12,7 +14,7 @@ obtained. The proof has two deliberately separate parts: -* `closeK`/`closeV` are the exact Ix and Lean4Lean reverse closures; +* `closeK`/`closeV` are the exact Ix and Ix.Theory.Named reverse closures; * `TelescopeS` records each domain at the context in which production built it, rather than assuming a relation for the already-closed result. @@ -21,9 +23,9 @@ intern request. Thus hash-consing cannot silently replace a generated forall with a colliding expression. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VEnv VExpr VInductDecl VLevel VLocalDecl) +open Ix.Theory.Named (VEnv VExpr VInductDecl VLocalDecl) namespace GeneratedRecursorTypeClosure @@ -34,13 +36,13 @@ def closeK (domains : Array (KExpr .anon)) : Nat → KExpr .anon → KExpr .anon closeK domains remaining (.mkAll RecM.anonN RecM.anonBi domains[remaining]! body) -/-- Lean4Lean-side closure with the same array positions and order. -/ +/-- Ix.Theory.Named-side closure with the same array positions and order. -/ def closeV (domains : Array VExpr) : Nat → VExpr → VExpr | 0, body => body | remaining + 1, body => closeV domains remaining (.forallE domains[remaining]! body) -/-- The flattened target domain list used by Lean4Lean's canonical mixed +/-- The flattened target domain list used by Ix.Theory.Named's canonical mixed recursor type. -/ def canonicalMajorDomain {source : VInductDecl} (generation : source.GenerationChecked) : VExpr := @@ -91,7 +93,7 @@ theorem closeV_eq_forallN_take (domains : Array VExpr) (count : Nat) simp only [Option.toList_some, VExpr.forallN_append, VExpr.forallN, getElem!_def, hindex, Array.getElem?_eq_getElem] -/-- Closing the whole canonical flattened telescope is exactly Lean4Lean's +/-- Closing the whole canonical flattened telescope is exactly Ix.Theory.Named's public mixed recursor type, not merely a definitionally equal variant. -/ theorem closeV_canonical {source : VInductDecl} (generation : source.GenerationChecked) : @@ -133,7 +135,7 @@ theorem opened_toCtx (base : KVLCtx) (domains : Array VExpr) (count : Nat) Array.getElem?_eq_getElem, List.reverse_append, List.reverse_singleton, List.cons_append, List.nil_append] -/-- Inversion for a well-formed iterated forall. Lean4Lean provides the +/-- Inversion for a well-formed iterated forall. Ix.Theory.Named provides the constructor theorem `IsType.forallN`; the converse is what lets the production builder consume the canonical result type one open domain at a time. -/ theorem isType_forallN_inv @@ -146,7 +148,7 @@ theorem isType_forallN_inv | nil => exact ⟨trivial, h⟩ | cons domain domains ih => obtain ⟨hdomain, hrest⟩ := - Lean4Lean.VEnv.IsType.forallE_inv henv h + Ix.Theory.Named.VEnv.IsType.forallE_inv henv h obtain ⟨htelescope, hbody⟩ := ih hrest exact ⟨⟨hdomain, htelescope⟩, by simpa [List.reverse_cons, List.append_assoc] using hbody⟩ @@ -170,7 +172,7 @@ theorem onTel_isType_getElem simpa [List.take, List.reverse_cons, List.append_assoc] using ih hrest index htail -/-- Lean4Lean's semantic generation invariant decomposes into precisely the +/-- Ix.Theory.Named's semantic generation invariant decomposes into precisely the flattened target telescope and open body used by the production builder. -/ theorem canonical_onTel_and_bodyType {source : VInductDecl} {generation : source.GenerationChecked} @@ -252,7 +254,7 @@ namespace TelescopeS /-- Construct the operation-shaped telescope from only the Ix-to-target relations produced by the live builder. All target typing fields are recovered -from Lean4Lean's generation invariant. -/ +from Ix.Theory.Named's generation invariant. -/ theorem of_canonical {source : VInductDecl} {generation : source.GenerationChecked} {env : VEnv} @@ -330,7 +332,7 @@ theorem close have hclosedBodyType : env.IsType uvars (opened base targetDomains count).toCtx targetBody' := - Lean4Lean.VEnv.IsType.forallE hdomainType hbodyType + Ix.Theory.Named.VEnv.IsType.forallE hdomainType hbodyType have hprefix : TelescopeS env uvars nameOf trProj base ixDomains targetDomains count ixBody' targetBody' := { ixBound := by omega @@ -521,7 +523,7 @@ structure CanonicalBodyS (support : RunSupport) (env : VEnv) /-- Once the actual domain/body run establishes its operation-shaped postcondition, the real `buildRecType` execution returns a structurally -canonical Lean4Lean recursor type. This theorem closes all control-flow and +canonical Ix.Theory.Named recursor type. This theorem closes all control-flow and hash-consing obligations after that body boundary. -/ theorem buildRecType_canonical_of_body {support : RunSupport} (hcollision : support.CollisionFree) @@ -554,4 +556,4 @@ theorem buildRecType_canonical_of_body end GeneratedRecursorTypeClosure -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/GeneratedRecursorTypeFixture.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorTypeFixture.lean similarity index 92% rename from Ix/Tc/Verify/Inductive/GeneratedRecursorTypeFixture.lean rename to Ix/Kernel/Verify/Inductive/GeneratedRecursorTypeFixture.lean index 3e6d55d85..7411db576 100644 --- a/Ix/Tc/Verify/Inductive/GeneratedRecursorTypeFixture.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorTypeFixture.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Inductive.GeneratedRecursorTypeClosure -import Ix.Tc.Verify.Inductive.IndexedRecursiveAcceptance -import Ix.Tc.Verify.Ingress.AnonStructural +import Ix.Kernel.Verify.Inductive.GeneratedRecursorTypeClosure +import Ix.Kernel.Verify.Inductive.IndexedRecursiveAcceptance +import Ix.Kernel.Verify.Ingress.AnonStructural /-! # Production generated-recursor type fixture @@ -14,15 +14,15 @@ production helpers. The fallback values only make the projections total. The public run theorems prove that neither fallback is taken, and the final theorem relates -the expression returned by that exact execution to Lean4Lean's canonical +the expression returned by that exact execution to Ix.Theory.Named's canonical mixed recursor type. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture open GeneratedRecursorSemantics open IndexedRecursiveCertificateFixture -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named.InductiveReplayFixtures local instance generatedTypeKIdDecidableEq : DecidableEq (KId .anon) := AnonStructural.idDecidableEq @@ -163,7 +163,7 @@ def familyBuiltRecursor : GeneratedRecursor .anon := familyBuildTypeResult /-- The type produced by the concrete `buildRecType` execution is the exact -structural translation of Lean4Lean's canonical `IndexedVec` recursor type. -/ +structural translation of Ix.Theory.Named's canonical `IndexedVec` recursor type. -/ theorem familyBuildTypeCanonical : CanonicalTypeS indexedVecFinalEnv nameOf RawProjRel.none transaction.certificate.generation familyBuiltRecursor := by @@ -172,7 +172,7 @@ theorem familyBuildTypeCanonical : transaction.certificate.generation.recursor.uvars nameOf RawProjRel.none [] familyBuildTypeResult transaction.certificate.generation.recType rw [familyBuildTypeResult_eq] - simpa [Lean4Lean.VInductDecl.GenerationChecked.recursor] using + simpa [Ix.Theory.Named.VInductDecl.GenerationChecked.recursor] using recursorTypeTyped /-- One data-bearing theorem packages the production execution and its exact @@ -192,4 +192,4 @@ theorem familyBuildTypeExecution : simpa [familyBuiltRecursor, RecM.initialGeneratedRecursor] using familyBuildTypeRun -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/IndexedBlockValidation.lean b/Ix/Kernel/Verify/Inductive/IndexedBlockValidation.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/IndexedBlockValidation.lean rename to Ix/Kernel/Verify/Inductive/IndexedBlockValidation.lean index 4c810a83a..32a6ecdc0 100644 --- a/Ix/Tc/Verify/Inductive/IndexedBlockValidation.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedBlockValidation.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Inductive.ConstructorValidationTraversal -import Ix.Tc.Verify.Inductive.IndexedRecursiveAcceptance -import Ix.Tc.Verify.Ingress.AnonStructural +import Ix.Kernel.Verify.Inductive.ConstructorValidationTraversal +import Ix.Kernel.Verify.Inductive.IndexedRecursiveAcceptance +import Ix.Kernel.Verify.Ingress.AnonStructural /-! # IndexedVec production block-validation trace @@ -13,7 +13,7 @@ header, constructor order, and safety-gated positivity executions reached by that one block run. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture local instance blockValidationIdDecidableEq : DecidableEq (KId .anon) := AnonStructural.idDecidableEq @@ -471,4 +471,4 @@ theorem indexedVecConsProductionPositivityTrace : exact ⟨indLevel, _, _, _, _, _, _, _, _, metadata, parameters, run, trace, universes, returnType⟩ -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/IndexedCandidateOperations.lean b/Ix/Kernel/Verify/Inductive/IndexedCandidateOperations.lean similarity index 97% rename from Ix/Tc/Verify/Inductive/IndexedCandidateOperations.lean rename to Ix/Kernel/Verify/Inductive/IndexedCandidateOperations.lean index aa5d2e96e..823ac328c 100644 --- a/Ix/Tc/Verify/Inductive/IndexedCandidateOperations.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedCandidateOperations.lean @@ -1,19 +1,19 @@ -import Ix.Tc.Verify.Inductive.IndexedCandidateSyntax -import Ix.Tc.Verify.Inductive.IndexedRecursiveAcceptance +import Ix.Kernel.Verify.Inductive.IndexedCandidateSyntax +import Ix.Kernel.Verify.Inductive.IndexedRecursiveAcceptance /-! # IndexedVec candidate operations The closed syntax relation is not enough once constructor validation opens its telescope. This module follows the same anonymous binder instantiation used -by `TcM.openBinderAnon`, pairs each minted Ix identifier with Lean4Lean's +by `TcM.openBinderAnon`, pairs each minted Ix identifier with Ix.Theory.Named's corresponding validation identifier, and checks the three actual `cons` field domains at which positivity is invoked. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named.InductiveReplayFixtures /-- Domain projection used only after a separately checked constructor shape. Returning the source in the impossible fallback keeps the definition total. -/ @@ -430,4 +430,4 @@ theorem tailDomainCandidateSyntax : indexedVecConstructorNExpr) := CandidateSyntax.rel_of_check tailDomainCandidateCheckNative -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/IndexedCandidateSyntax.lean b/Ix/Kernel/Verify/Inductive/IndexedCandidateSyntax.lean similarity index 81% rename from Ix/Tc/Verify/Inductive/IndexedCandidateSyntax.lean rename to Ix/Kernel/Verify/Inductive/IndexedCandidateSyntax.lean index d8810062b..3629e913a 100644 --- a/Ix/Tc/Verify/Inductive/IndexedCandidateSyntax.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedCandidateSyntax.lean @@ -1,23 +1,23 @@ -import Ix.Tc.Verify.Inductive.CandidateSyntax -import Ix.Tc.Verify.Inductive.IndexedRecursiveFixture -import Lean4Lean.Verify.Environment.IndexedVecOuterReplay +import Ix.Kernel.Verify.Inductive.CandidateSyntax +import Ix.Kernel.Verify.Inductive.IndexedRecursiveFixture +import Ix.Theory.Named.Verify.Environment.IndexedVecOuterReplay /-! # Exact IndexedVec candidate syntax This module connects the actual anonymous expressions produced by Ix ingress -to the exact Lean kernel expressions consumed by Lean4Lean's constructor +to the exact Lean kernel expressions consumed by Ix.Theory.Named's constructor validator. The relation is deliberately syntactic: positivity occurrence and `isValidIndApp?` inspect the candidate expression rather than its Theory denotation. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture -open Lean4Lean.InductiveReplayFixtures -open Lean4Lean.InductiveReplayFixtures.IndexedVecConsReplay +open Ix.Theory.Named.InductiveReplayFixtures +open Ix.Theory.Named.InductiveReplayFixtures.IndexedVecConsReplay -/-! ## Proof-independent Lean4Lean validation fixture +/-! ## Proof-independent Ix.Theory.Named validation fixture The upstream replay exposes the right executable values, but its public `indexedVecCtorValidationContext` is projected out of a proof-bearing family @@ -30,7 +30,7 @@ data. These are the values E2c relates to production Ix execution. /-- Post-family context in which the two IndexedVec constructors are checked. It has the validated parameter/index local context and the staged environment containing the family constant, without retaining an upstream replay proof. -/ -def indexedVecConstructorContext : Lean4Lean.AddInductive.Context := +def indexedVecConstructorContext : Ix.Theory.Named.AddInductive.Context := { indexedVecValidationFamilyContext with env := ctorEnv } def indexedVecConstructorAlpha : Lean.Expr := @@ -45,18 +45,18 @@ def indexedVecConstructorNId : Lean.FVarId := def indexedVecConstructorNExpr : Lean.Expr := indexedVecConstructorContext.freshExpr -def indexedVecConstructorNContext : Lean4Lean.AddInductive.Context := +def indexedVecConstructorNContext : Ix.Theory.Named.AddInductive.Context := indexedVecConstructorContext.pushLocalDecl consNName .implicit (.const ``Nat []) def indexedVecConstructorHeadId : Lean.FVarId := indexedVecConstructorNContext.freshFVarId -def indexedVecConstructorHeadContext : Lean4Lean.AddInductive.Context := +def indexedVecConstructorHeadContext : Ix.Theory.Named.AddInductive.Context := indexedVecConstructorNContext.pushLocalDecl consHeadName .default indexedVecConstructorAlpha -def indexedVecConstructorTailContext : Lean4Lean.AddInductive.Context := +def indexedVecConstructorTailContext : Ix.Theory.Named.AddInductive.Context := indexedVecConstructorHeadContext.pushLocalDecl consTailName .default (ctorIndexedVecApp indexedVecConstructorAlpha indexedVecConstructorNExpr) @@ -65,13 +65,13 @@ def indexedVecConstructorTailContext : Lean4Lean.AddInductive.Context := one-index family spine. Writing the finite record directly prevents the statement from retaining `CandidateExprTrace.singletonCandidateInductiveStats` through an upstream proof object. -/ -def indexedVecConstructorStats : Lean4Lean.AddInductive.InductiveStats where +def indexedVecConstructorStats : Ix.Theory.Named.AddInductive.InductiveStats where lctx := indexedVecConstructorContext.lctx levels := [.param `u] resultLevel := .succ (.param `u) nindices := #[1] indConsts := - #[.const ``Lean4Lean.InductiveFixtures.IndexedVec [.param `u]] + #[.const ``Ix.Theory.Named.InductiveFixtures.IndexedVec [.param `u]] params := #[indexedVecConstructorAlpha] isNotZero := true @@ -120,7 +120,7 @@ private theorem consCandidateCheckNative : native_decide /-- The family type selected by production anonymous ingress is exactly the -Lean4Lean IndexedVec family candidate, modulo irrelevant binder metadata. -/ +Ix.Theory.Named IndexedVec family candidate, modulo irrelevant binder metadata. -/ theorem familyCandidateSyntax : CandidateSyntaxRel nameOf (fun ixId leanId => closedFVarMatches ixId leanId = true) @@ -130,7 +130,7 @@ theorem familyCandidateSyntax : CandidateSyntax.rel_of_check familyCandidateCheckNative /-- The ingressed nil constructor type is the exact candidate validated by -Lean4Lean. -/ +Ix.Theory.Named. -/ theorem nilCandidateSyntax : CandidateSyntaxRel nameOf (fun ixId leanId => closedFVarMatches ixId leanId = true) @@ -140,7 +140,7 @@ theorem nilCandidateSyntax : CandidateSyntax.rel_of_check nilCandidateCheckNative /-- The ingressed cons constructor type is the exact candidate validated by -Lean4Lean, including its recursive family application and changing index. -/ +Ix.Theory.Named, including its recursive family application and changing index. -/ theorem consCandidateSyntax : CandidateSyntaxRel nameOf (fun ixId leanId => closedFVarMatches ixId leanId = true) @@ -153,7 +153,7 @@ private theorem candidateBlockSyntaxNative : CandidateBlockRel nameOf #[familyId.addr] indexedVecConstructorStats.indConsts := by rw [show indexedVecConstructorStats.indConsts = - #[.const ``Lean4Lean.InductiveFixtures.IndexedVec [.param `u]] by rfl] + #[.const ``Ix.Theory.Named.InductiveFixtures.IndexedVec [.param `u]] by rfl] intro id leanName hname unfold nameOf at hname repeat' split at hname @@ -161,7 +161,7 @@ private theorem candidateBlockSyntaxNative : all_goals subst_vars all_goals native_decide -/-- The physical singleton-family address and Lean4Lean's singleton constant +/-- The physical singleton-family address and Ix.Theory.Named's singleton constant array make the same occurrence decision. The proof analyzes the concrete ingress name map, so it assumes neither address nor name injectivity. -/ theorem candidateBlockSyntax : @@ -169,4 +169,4 @@ theorem candidateBlockSyntax : indexedVecConstructorStats.indConsts := candidateBlockSyntaxNative -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/IndexedCandidateTransaction.lean b/Ix/Kernel/Verify/Inductive/IndexedCandidateTransaction.lean similarity index 78% rename from Ix/Tc/Verify/Inductive/IndexedCandidateTransaction.lean rename to Ix/Kernel/Verify/Inductive/IndexedCandidateTransaction.lean index b6ac31279..75bfda340 100644 --- a/Ix/Tc/Verify/Inductive/IndexedCandidateTransaction.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedCandidateTransaction.lean @@ -1,25 +1,25 @@ -import Ix.Tc.Verify.Inductive.IndexedRecursiveCertificate -import Ix.Tc.Verify.Inductive.ProducedGenerationTransaction -import Lean4Lean.Verify.Environment.IndexedVecSemanticReplay +import Ix.Kernel.Verify.Inductive.IndexedRecursiveCertificate +import Ix.Kernel.Verify.Inductive.ProducedGenerationTransaction +import Ix.Theory.Named.Verify.Environment.IndexedVecSemanticReplay /-! # Producer-linked IndexedVec generation transaction The existing `IndexedVec` certificate is deliberately reconstructed through the Theory-only API so its semantic roots keep a minimal trust footprint. -Lean4Lean also exposes the exact ordinary metadata producer and dependent +Ix.Theory.Named also exposes the exact ordinary metadata producer and dependent semantic package for that same generation. This module proves those two paths meet, without making the producer replay a dependency of the clean Theory-only transaction. -/ -namespace Ix.Tc.IndexedRecursiveCertificateFixture +namespace Ix.Kernel.IndexedRecursiveCertificateFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures -/-- The L4L-01E package retains the exact source declaration and checked +/-- The Spec-01E package retains the exact source declaration and checked generation selected by the ordinary metadata producer. -/ noncomputable def exactPackage : VInductDecl.ExactProducedGenerationCandidatePackage natFinalEnv [`u] @@ -45,7 +45,7 @@ noncomputable def exactProducedTransaction : exact indexedVecSemantic_addInductCertified beforeWF := natWF -/-- Intentional operational erasure of the exact L4L-01E indices. -/ +/-- Intentional operational erasure of the exact Spec-01E indices. -/ noncomputable def producedTransaction : ProducedGenerationTransaction natFinalEnv indexedVecFinalEnv [`u] := exactProducedTransaction.toProduced @@ -66,4 +66,4 @@ before erasure. -/ theorem producerLinkedFacts : producedTransaction.Facts := producedTransaction.facts -end Ix.Tc.IndexedRecursiveCertificateFixture +end Ix.Kernel.IndexedRecursiveCertificateFixture diff --git a/Ix/Tc/Verify/Inductive/IndexedConstructorPositivity.lean b/Ix/Kernel/Verify/Inductive/IndexedConstructorPositivity.lean similarity index 91% rename from Ix/Tc/Verify/Inductive/IndexedConstructorPositivity.lean rename to Ix/Kernel/Verify/Inductive/IndexedConstructorPositivity.lean index 53c4c38a9..84b75ce6a 100644 --- a/Ix/Tc/Verify/Inductive/IndexedConstructorPositivity.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedConstructorPositivity.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Inductive.ConstructorPositivityTraversal -import Ix.Tc.Verify.Inductive.IndexedPositivityTransport +import Ix.Kernel.Verify.Inductive.ConstructorPositivityTraversal +import Ix.Kernel.Verify.Inductive.IndexedPositivityTransport /-! # IndexedVec enclosing constructor-positivity execution @@ -15,7 +15,7 @@ also records the shared-parameter opening, source-ordered bounded field loop, and public local-context restoration performed by production. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture /-- Exact result of running production strict positivity on the ingressed `IndexedVec.cons` declaration. -/ @@ -57,4 +57,4 @@ theorem indexedVecConsIxPositivityTrace : checkerMethods checkerInitial ixConsPositivityAfter := RecM.checkPositivity_success checkerMethods ixConsPositivityRun -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/IndexedConstructorValidation.lean b/Ix/Kernel/Verify/Inductive/IndexedConstructorValidation.lean similarity index 85% rename from Ix/Tc/Verify/Inductive/IndexedConstructorValidation.lean rename to Ix/Kernel/Verify/Inductive/IndexedConstructorValidation.lean index 530ad3a1c..cde7de964 100644 --- a/Ix/Tc/Verify/Inductive/IndexedConstructorValidation.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedConstructorValidation.lean @@ -1,24 +1,24 @@ -import Ix.Tc.Verify.Inductive.IndexedPositivityTransport +import Ix.Kernel.Verify.Inductive.IndexedPositivityTransport /-! # IndexedVec constructor-validation trace This module places the three production-derived positivity artifacts back into -Lean4Lean's complete retained constructor telescope. The resulting trace +Ix.Theory.Named's complete retained constructor telescope. The resulting trace records the shared parameter check, all ordinary field type/universe checks, the transported positivity evidence, and the terminal indexed-family application. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture -open Lean4Lean.InductiveReplayFixtures -open Lean4Lean.InductiveReplayFixtures.IndexedVecConsReplay +open Ix.Theory.Named.InductiveReplayFixtures +open Ix.Theory.Named.InductiveReplayFixtures.IndexedVecConsReplay private abbrev ConsValidationTrace - (context : Lean4Lean.AddInductive.Context) (source : Lean.Expr) + (context : Ix.Theory.Named.AddInductive.Context) (source : Lean.Expr) (argIdx fuel : Nat) := - Lean4Lean.AddInductive.ConstructorTypeValidationTrace + Ix.Theory.Named.AddInductive.ConstructorTypeValidationTrace indexedVecConstructorStats false 0 indexedVecKernelCons.name context source argIdx fuel @@ -26,13 +26,13 @@ private abbrev ConsValidationTrace private theorem indexedVecConstructorGetTypeAlphaNative : ExactLeanSyntax.exceptExprCheck - (Lean4Lean.AddInductive.getType indexedVecConstructorAlpha + (Ix.Theory.Named.AddInductive.getType indexedVecConstructorAlpha indexedVecConstructorContext) (.sort (.succ (.param `u))) = true := by native_decide private theorem indexedVecConstructorGetTypeAlpha : - Lean4Lean.AddInductive.getType indexedVecConstructorAlpha + Ix.Theory.Named.AddInductive.getType indexedVecConstructorAlpha indexedVecConstructorContext = .ok (.sort (.succ (.param `u))) := ExactLeanSyntax.exceptExpr_eq_ok_of_check @@ -40,116 +40,116 @@ private theorem indexedVecConstructorGetTypeAlpha : private theorem indexedVecConstructorParamIsDefEqNative : ExactLeanSyntax.exceptBoolCheck - (Lean4Lean.TypeChecker.M.run indexedVecConstructorContext.env + (Ix.Theory.Named.TypeChecker.M.run indexedVecConstructorContext.env indexedVecConstructorContext.safety indexedVecConstructorContext.lctx indexedVecConstructorContext.lparams indexedVecConstructorContext.fuel - (Lean4Lean.TypeChecker.isDefEq + (Ix.Theory.Named.TypeChecker.isDefEq (.sort (.succ (.param `u))) (.sort (.succ (.param `u))))) true = true := by native_decide private theorem indexedVecConstructorParamIsDefEq : - Lean4Lean.AddInductive.CandidateIsDefEqStep.Valid + Ix.Theory.Named.AddInductive.CandidateIsDefEqStep.Valid ⟨indexedVecConstructorContext, .sort (.succ (.param `u)), .sort (.succ (.param `u))⟩ := by - unfold Lean4Lean.AddInductive.CandidateIsDefEqStep.Valid + unfold Ix.Theory.Named.AddInductive.CandidateIsDefEqStep.Valid exact ExactLeanSyntax.exceptBool_eq_ok_of_check indexedVecConstructorParamIsDefEqNative private theorem indexedVecConstructorNatEnsureTypeNative : ExactLeanSyntax.exceptExprCheck - (Lean4Lean.TypeChecker.M.run indexedVecConstructorContext.env + (Ix.Theory.Named.TypeChecker.M.run indexedVecConstructorContext.env indexedVecConstructorContext.safety indexedVecConstructorContext.lctx indexedVecConstructorContext.lparams indexedVecConstructorContext.fuel - (Lean4Lean.TypeChecker.ensureType (.const ``Nat []))) + (Ix.Theory.Named.TypeChecker.ensureType (.const ``Nat []))) (.sort (.succ .zero)) = true := by native_decide private theorem indexedVecConstructorNatEnsureType : - Lean4Lean.AddInductive.ConstructorEnsureTypeStep.Valid + Ix.Theory.Named.AddInductive.ConstructorEnsureTypeStep.Valid ⟨indexedVecConstructorContext, .const ``Nat [], .sort (.succ .zero)⟩ := by - unfold Lean4Lean.AddInductive.ConstructorEnsureTypeStep.Valid + unfold Ix.Theory.Named.AddInductive.ConstructorEnsureTypeStep.Valid exact ExactLeanSyntax.exceptExpr_eq_ok_of_check indexedVecConstructorNatEnsureTypeNative private theorem indexedVecConstructorAlphaEnsureTypeNative : ExactLeanSyntax.exceptExprCheck - (Lean4Lean.TypeChecker.M.run indexedVecConstructorNContext.env + (Ix.Theory.Named.TypeChecker.M.run indexedVecConstructorNContext.env indexedVecConstructorNContext.safety indexedVecConstructorNContext.lctx indexedVecConstructorNContext.lparams indexedVecConstructorNContext.fuel - (Lean4Lean.TypeChecker.ensureType indexedVecConstructorAlpha)) + (Ix.Theory.Named.TypeChecker.ensureType indexedVecConstructorAlpha)) (.sort (.succ (.param `u))) = true := by native_decide private theorem indexedVecConstructorAlphaEnsureType : - Lean4Lean.AddInductive.ConstructorEnsureTypeStep.Valid + Ix.Theory.Named.AddInductive.ConstructorEnsureTypeStep.Valid ⟨indexedVecConstructorNContext, indexedVecConstructorAlpha, .sort (.succ (.param `u))⟩ := by - unfold Lean4Lean.AddInductive.ConstructorEnsureTypeStep.Valid + unfold Ix.Theory.Named.AddInductive.ConstructorEnsureTypeStep.Valid exact ExactLeanSyntax.exceptExpr_eq_ok_of_check indexedVecConstructorAlphaEnsureTypeNative private theorem indexedVecConstructorTailEnsureTypeNative : ExactLeanSyntax.exceptExprCheck - (Lean4Lean.TypeChecker.M.run indexedVecConstructorHeadContext.env + (Ix.Theory.Named.TypeChecker.M.run indexedVecConstructorHeadContext.env indexedVecConstructorHeadContext.safety indexedVecConstructorHeadContext.lctx indexedVecConstructorHeadContext.lparams indexedVecConstructorHeadContext.fuel - (Lean4Lean.TypeChecker.ensureType + (Ix.Theory.Named.TypeChecker.ensureType (ctorIndexedVecApp indexedVecConstructorAlpha indexedVecConstructorNExpr))) (.sort (.succ (.param `u))) = true := by native_decide private theorem indexedVecConstructorTailEnsureType : - Lean4Lean.AddInductive.ConstructorEnsureTypeStep.Valid + Ix.Theory.Named.AddInductive.ConstructorEnsureTypeStep.Valid ⟨indexedVecConstructorHeadContext, ctorIndexedVecApp indexedVecConstructorAlpha indexedVecConstructorNExpr, .sort (.succ (.param `u))⟩ := by - unfold Lean4Lean.AddInductive.ConstructorEnsureTypeStep.Valid + unfold Ix.Theory.Named.AddInductive.ConstructorEnsureTypeStep.Valid exact ExactLeanSyntax.exceptExpr_eq_ok_of_check indexedVecConstructorTailEnsureTypeNative private theorem indexedVecConstructorResultIsValidNative : - Lean4Lean.AddInductive.isValidIndAppIdx indexedVecConstructorStats + Ix.Theory.Named.AddInductive.isValidIndAppIdx indexedVecConstructorStats indexedVecConstructorResult 0 = true := by native_decide private theorem indexedVecConstructorConsumeNatNative : ExactLeanSyntax.exprCheck - (Lean4Lean.AddInductive.consumeTypeAnnotations (.const ``Nat [])) + (Ix.Theory.Named.AddInductive.consumeTypeAnnotations (.const ``Nat [])) (.const ``Nat []) = true := by native_decide private theorem indexedVecConstructorConsumeNat : - Lean4Lean.AddInductive.consumeTypeAnnotations (.const ``Nat []) = + Ix.Theory.Named.AddInductive.consumeTypeAnnotations (.const ``Nat []) = .const ``Nat [] := ExactLeanSyntax.expr_eq_of_check indexedVecConstructorConsumeNatNative private theorem indexedVecConstructorConsumeAlphaNative : ExactLeanSyntax.exprCheck - (Lean4Lean.AddInductive.consumeTypeAnnotations + (Ix.Theory.Named.AddInductive.consumeTypeAnnotations indexedVecConstructorAlpha) indexedVecConstructorAlpha = true := by native_decide private theorem indexedVecConstructorConsumeAlpha : - Lean4Lean.AddInductive.consumeTypeAnnotations + Ix.Theory.Named.AddInductive.consumeTypeAnnotations indexedVecConstructorAlpha = indexedVecConstructorAlpha := ExactLeanSyntax.expr_eq_of_check indexedVecConstructorConsumeAlphaNative private theorem indexedVecConstructorConsumeTailNative : ExactLeanSyntax.exprCheck - (Lean4Lean.AddInductive.consumeTypeAnnotations + (Ix.Theory.Named.AddInductive.consumeTypeAnnotations (ctorIndexedVecApp indexedVecConstructorAlpha indexedVecConstructorNExpr)) (ctorIndexedVecApp indexedVecConstructorAlpha @@ -157,7 +157,7 @@ private theorem indexedVecConstructorConsumeTailNative : native_decide private theorem indexedVecConstructorConsumeTail : - Lean4Lean.AddInductive.consumeTypeAnnotations + Ix.Theory.Named.AddInductive.consumeTypeAnnotations (ctorIndexedVecApp indexedVecConstructorAlpha indexedVecConstructorNExpr) = ctorIndexedVecApp indexedVecConstructorAlpha @@ -266,19 +266,19 @@ private theorem indexedVecConstructorInstantiateTail : indexedVecConstructorInstantiateTailNative private theorem indexedVecConstructorNatUniverse : - Lean4Lean.AddInductive.levelStructGe + Ix.Theory.Named.AddInductive.levelStructGe indexedVecConstructorStats.resultLevel (.succ .zero) = true := by native_decide private theorem indexedVecConstructorParamUniverse : - Lean4Lean.AddInductive.levelStructGe + Ix.Theory.Named.AddInductive.levelStructGe indexedVecConstructorStats.resultLevel (.succ (.param `u)) = true := by native_decide /-- Complete retained validation of the real `IndexedVec.cons` candidate. Unlike applying `ConstructorTypeValidationTrace.exists_of_run` to the -already-known Lean4Lean replay, this construction explicitly installs the +already-known Ix.Theory.Named replay, this construction explicitly installs the three traces transported from the production Ix positivity calls. -/ theorem indexedVecConsConstructorTypeValidationTrace : Nonempty (ConsValidationTrace indexedVecConstructorContext @@ -392,10 +392,10 @@ theorem indexedVecConsConstructorTypeValidationTrace : validator, so the vertical slice reaches the complete method rather than only its positivity helper. -/ theorem indexedVecConsConstructorValidationRun : - Lean4Lean.AddInductive.checkConstructorType + Ix.Theory.Named.AddInductive.checkConstructorType indexedVecConstructorStats false 0 indexedVecKernelCons.name indexedVecKernelCons.type indexedVecConstructorContext = .ok () := by obtain ⟨trace⟩ := indexedVecConsConstructorTypeValidationTrace exact trace.check_run -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/IndexedPositivityTransport.lean b/Ix/Kernel/Verify/Inductive/IndexedPositivityTransport.lean similarity index 90% rename from Ix/Tc/Verify/Inductive/IndexedPositivityTransport.lean rename to Ix/Kernel/Verify/Inductive/IndexedPositivityTransport.lean index 56f054155..c9fda586b 100644 --- a/Ix/Tc/Verify/Inductive/IndexedPositivityTransport.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedPositivityTransport.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Inductive.IndexedCandidateOperations -import Ix.Tc.Verify.Inductive.IndexedProductionPositivity -import Ix.Tc.Verify.Inductive.ExactLeanSyntax +import Ix.Kernel.Verify.Inductive.IndexedCandidateOperations +import Ix.Kernel.Verify.Inductive.IndexedProductionPositivity +import Ix.Kernel.Verify.Inductive.ExactLeanSyntax /-! # IndexedVec positivity transport @@ -12,11 +12,11 @@ exact Ix WHNF cache transition, so the correspondence does not silently treat the stateful production reducer as a pure function. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named.InductiveReplayFixtures -/-! ## Finite Lean4Lean observations at the trust boundary +/-! ## Finite Ix.Theory.Named observations at the trust boundary The upstream replay modules are useful for naming the exact candidate states, but their proof lemmas intentionally depend on reflected implementation @@ -25,25 +25,25 @@ consumed below with private native facts, so the exported transport depends on the concrete executions without admitting those reflected equations. -/ private theorem indexedVecNatHasNoIndOccTrusted : - Lean4Lean.AddInductive.hasIndOcc + Ix.Theory.Named.AddInductive.hasIndOcc indexedVecConstructorStats.indConsts (.const ``Nat []) = false := by native_decide private theorem indexedVecAlphaHasNoIndOccTrusted : - Lean4Lean.AddInductive.hasIndOcc + Ix.Theory.Named.AddInductive.hasIndOcc indexedVecConstructorStats.indConsts indexedVecConstructorAlpha = false := by native_decide private theorem indexedVecTailHasIndOccTrusted : - Lean4Lean.AddInductive.hasIndOcc + Ix.Theory.Named.AddInductive.hasIndOcc indexedVecConstructorStats.indConsts (ctorIndexedVecApp indexedVecConstructorAlpha indexedVecConstructorNExpr) = true := by native_decide private theorem indexedVecTailAppIsValidTrusted : - Lean4Lean.AddInductive.isValidIndApp? + Ix.Theory.Named.AddInductive.isValidIndApp? indexedVecConstructorStats (ctorIndexedVecApp indexedVecConstructorAlpha indexedVecConstructorNExpr) = some 0 := by @@ -122,48 +122,48 @@ theorem ixConsTailDomainMentionsRoot : tailDomainCandidateSyntax] exact indexedVecTailHasIndOccTrusted -/-! ## Exact Lean4Lean WHNF observations -/ +/-! ## Exact Ix.Theory.Named WHNF observations -/ private theorem indexedVecNatCandidateWhnfNative : ExactLeanSyntax.exceptExprCheck - (Lean4Lean.TypeChecker.M.run ctorEnv + (Ix.Theory.Named.TypeChecker.M.run ctorEnv .safe indexedVecConstructorContext.lctx [`u] - ({} : Lean4Lean.FuelConfig) - (Lean4Lean.TypeChecker.whnf (.const ``Nat []))) + ({} : Ix.Theory.Named.FuelConfig) + (Ix.Theory.Named.TypeChecker.whnf (.const ``Nat []))) (.const ``Nat []) = true := by native_decide theorem indexedVecNatCandidateWhnf : - Lean4Lean.AddInductive.CandidateWhnfStep.Valid + Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid ⟨indexedVecConstructorContext, (.const ``Nat []), (.const ``Nat [])⟩ := by - unfold Lean4Lean.AddInductive.CandidateWhnfStep.Valid + unfold Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid exact ExactLeanSyntax.exceptExpr_eq_ok_of_check indexedVecNatCandidateWhnfNative private theorem indexedVecAlphaCandidateWhnfNative : ExactLeanSyntax.exceptExprCheck - (Lean4Lean.TypeChecker.M.run ctorEnv + (Ix.Theory.Named.TypeChecker.M.run ctorEnv .safe indexedVecConstructorNContext.lctx [`u] - ({} : Lean4Lean.FuelConfig) - (Lean4Lean.TypeChecker.whnf indexedVecConstructorAlpha)) + ({} : Ix.Theory.Named.FuelConfig) + (Ix.Theory.Named.TypeChecker.whnf indexedVecConstructorAlpha)) indexedVecConstructorAlpha = true := by native_decide theorem indexedVecAlphaCandidateWhnf : - Lean4Lean.AddInductive.CandidateWhnfStep.Valid + Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid ⟨indexedVecConstructorNContext, indexedVecConstructorAlpha, indexedVecConstructorAlpha⟩ := by - unfold Lean4Lean.AddInductive.CandidateWhnfStep.Valid + unfold Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid exact ExactLeanSyntax.exceptExpr_eq_ok_of_check indexedVecAlphaCandidateWhnfNative private theorem indexedVecTailCandidateWhnfNative : ExactLeanSyntax.exceptExprCheck - (Lean4Lean.TypeChecker.M.run ctorEnv + (Ix.Theory.Named.TypeChecker.M.run ctorEnv .safe indexedVecConstructorHeadContext.lctx [`u] - ({} : Lean4Lean.FuelConfig) - (Lean4Lean.TypeChecker.whnf + ({} : Ix.Theory.Named.FuelConfig) + (Ix.Theory.Named.TypeChecker.whnf (ctorIndexedVecApp indexedVecConstructorAlpha indexedVecConstructorNExpr))) (ctorIndexedVecApp indexedVecConstructorAlpha @@ -171,12 +171,12 @@ private theorem indexedVecTailCandidateWhnfNative : native_decide theorem indexedVecTailCandidateWhnf : - Lean4Lean.AddInductive.CandidateWhnfStep.Valid + Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid ⟨indexedVecConstructorHeadContext, ctorIndexedVecApp indexedVecConstructorAlpha indexedVecConstructorNExpr, ctorIndexedVecApp indexedVecConstructorAlpha indexedVecConstructorNExpr⟩ := by - unfold Lean4Lean.AddInductive.CandidateWhnfStep.Valid + unfold Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid exact ExactLeanSyntax.exceptExpr_eq_ok_of_check indexedVecTailCandidateWhnfNative @@ -184,9 +184,9 @@ theorem indexedVecTailCandidateWhnf : /-- Sources of the three production positivity calls in the `cons` constructor. Each constructor fixes both the actual Ix binder state and the -corresponding Lean4Lean validation context. -/ +corresponding Ix.Theory.Named validation context. -/ inductive IndexedPositivitySourceRel : - TcState .anon → Lean4Lean.AddInductive.Context → + TcState .anon → Ix.Theory.Named.AddInductive.Context → KExpr .anon → Lean.Expr → Prop | nat : IndexedPositivitySourceRel ixConsNatDomainState indexedVecConstructorContext ixConsNatDomain (.const ``Nat []) @@ -201,7 +201,7 @@ inductive IndexedPositivitySourceRel : syntax is the projected production WHNF result, related structurally rather than equated by its content address. -/ inductive IndexedPositivityResultRel : - TcState .anon → Lean4Lean.AddInductive.Context → + TcState .anon → Ix.Theory.Named.AddInductive.Context → KExpr .anon → Lean.Expr → Prop | tail {ixResult : KExpr .anon} (candidate : CandidateSyntaxRel nameOf @@ -220,7 +220,7 @@ theorem indexedPositivitySourceMentions (relation : IndexedPositivitySourceRel ixState leanContext ixExpr leanExpr) : exprMentionsAnyAddr ixExpr #[familyId.addr] = - Lean4Lean.AddInductive.hasIndOcc + Ix.Theory.Named.AddInductive.hasIndOcc indexedVecConstructorStats.indConsts leanExpr := by cases relation with | nat => @@ -234,15 +234,15 @@ theorem indexedPositivitySourceMentions candidateBlockSyntax tailDomainCandidateSyntax private theorem indexedPositivityRootFree - {ixState : TcState .anon} {leanContext : Lean4Lean.AddInductive.Context} + {ixState : TcState .anon} {leanContext : Ix.Theory.Named.AddInductive.Context} {ixSource : KExpr .anon} {leanSource : Lean.Expr} (relation : IndexedPositivitySourceRel ixState leanContext ixSource leanSource) (free : exprMentionsAnyAddr ixSource #[familyId.addr] = false) : ∃ leanResult, - Lean4Lean.AddInductive.CandidateWhnfStep.Valid + Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid ⟨leanContext, leanSource, leanResult⟩ ∧ - Lean4Lean.AddInductive.hasIndOcc + Ix.Theory.Named.AddInductive.hasIndOcc indexedVecConstructorStats.indConsts leanResult = false := by cases relation with | nat => @@ -257,7 +257,7 @@ private theorem indexedPositivityRootFree private theorem indexedPositivityWhnf {ixBefore ixAfter : TcState .anon} - {leanContext : Lean4Lean.AddInductive.Context} + {leanContext : Ix.Theory.Named.AddInductive.Context} {ixSource ixResult : KExpr .anon} {leanSource : Lean.Expr} (relation : IndexedPositivitySourceRel ixBefore leanContext ixSource leanSource) @@ -265,7 +265,7 @@ private theorem indexedPositivityWhnf (run : (RecM.whnf ixSource).run checkerMethods ixBefore = .ok ixResult ixAfter) : ∃ leanResult, - Lean4Lean.AddInductive.CandidateWhnfStep.Valid + Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid ⟨leanContext, leanSource, leanResult⟩ ∧ IndexedPositivityResultRel ixAfter leanContext ixResult leanResult := by cases relation with @@ -284,7 +284,7 @@ private theorem indexedPositivityWhnf .tail ixConsTailWhnfResultCandidateSyntax⟩ private theorem indexedPositivityForall - {ixState : TcState .anon} {leanContext : Lean4Lean.AddInductive.Context} + {ixState : TcState .anon} {leanContext : Ix.Theory.Named.AddInductive.Context} {ixName : Mode.anon.F Name} {ixBinder : Mode.anon.F Lean.BinderInfo} {ixDomain ixBody : KExpr .anon} {ixInfo : ExprInfo .anon} {leanExpr : Lean.Expr} @@ -299,13 +299,13 @@ private theorem indexedPositivityForall .ok (ixOpen, ixFVar) ixAfterOpen → IndexedPositivitySourceRel ixAfterOpen (leanContext.pushLocalDecl leanName leanBinder - (Lean4Lean.AddInductive.consumeTypeAnnotations leanDomain)) + (Ix.Theory.Named.AddInductive.consumeTypeAnnotations leanDomain)) ixOpen (leanBody.instantiate1 leanContext.freshExpr) := by cases relation with | tail candidate => cases candidate private theorem indexedPositivityDirect - {ixState : TcState .anon} {leanContext : Lean4Lean.AddInductive.Context} + {ixState : TcState .anon} {leanContext : Ix.Theory.Named.AddInductive.Context} {ixResult : KExpr .anon} {leanResult : Lean.Expr} {id : KId .anon} {us : Array (KUniv .anon)} {info : ExprInfo .anon} {args : Array (KExpr .anon)} {groups : Array (PositivityGroup .anon)} @@ -317,10 +317,10 @@ private theorem indexedPositivityDirect (_valid : ValidPositiveRecursiveApplication id us args groups #[familyId.addr] checkerMethods ixState final) : ∃ targetIdx, - Lean4Lean.AddInductive.hasIndOcc + Ix.Theory.Named.AddInductive.hasIndOcc indexedVecConstructorStats.indConsts leanResult = true ∧ leanResult.isForall = false ∧ - Lean4Lean.AddInductive.isValidIndApp? + Ix.Theory.Named.AddInductive.isValidIndApp? indexedVecConstructorStats leanResult = some targetIdx := by cases relation with | tail _ => @@ -516,10 +516,10 @@ theorem indexedVecTailFlatPositivityTrace : ixConsTailPositivityAfter := indexedVecTailFlatPositivityTraceAt 0 -/-! ## Lean4Lean constructor-positivity artifacts -/ +/-! ## Ix.Theory.Named constructor-positivity artifacts -/ theorem indexedVecNatConstructorPositivityTraceAt (fuel : Nat) : - Nonempty (Lean4Lean.AddInductive.ConstructorPositivityTrace + Nonempty (Ix.Theory.Named.AddInductive.ConstructorPositivityTrace indexedVecConstructorStats indexedVecKernelCons.name 1 indexedVecConstructorContext (.const ``Nat []) (fuel + 1)) := FlatPositivityTraceTransport.constructorPositivityTrace @@ -527,7 +527,7 @@ theorem indexedVecNatConstructorPositivityTraceAt (fuel : Nat) : (indexedVecNatFlatPositivityTraceAt fuel) rfl .nat theorem indexedVecHeadConstructorPositivityTraceAt (fuel : Nat) : - Nonempty (Lean4Lean.AddInductive.ConstructorPositivityTrace + Nonempty (Ix.Theory.Named.AddInductive.ConstructorPositivityTrace indexedVecConstructorStats indexedVecKernelCons.name 2 indexedVecConstructorNContext indexedVecConstructorAlpha (fuel + 1)) := FlatPositivityTraceTransport.constructorPositivityTrace @@ -535,7 +535,7 @@ theorem indexedVecHeadConstructorPositivityTraceAt (fuel : Nat) : (indexedVecHeadFlatPositivityTraceAt fuel) rfl .head theorem indexedVecTailConstructorPositivityTraceAt (fuel : Nat) : - Nonempty (Lean4Lean.AddInductive.ConstructorPositivityTrace + Nonempty (Ix.Theory.Named.AddInductive.ConstructorPositivityTrace indexedVecConstructorStats indexedVecKernelCons.name 3 indexedVecConstructorHeadContext (ctorIndexedVecApp indexedVecConstructorAlpha @@ -545,19 +545,19 @@ theorem indexedVecTailConstructorPositivityTraceAt (fuel : Nat) : (indexedVecTailFlatPositivityTraceAt fuel) rfl .tail theorem indexedVecNatConstructorPositivityTrace : - Nonempty (Lean4Lean.AddInductive.ConstructorPositivityTrace + Nonempty (Ix.Theory.Named.AddInductive.ConstructorPositivityTrace indexedVecConstructorStats indexedVecKernelCons.name 1 indexedVecConstructorContext (.const ``Nat []) 1) := indexedVecNatConstructorPositivityTraceAt 0 theorem indexedVecHeadConstructorPositivityTrace : - Nonempty (Lean4Lean.AddInductive.ConstructorPositivityTrace + Nonempty (Ix.Theory.Named.AddInductive.ConstructorPositivityTrace indexedVecConstructorStats indexedVecKernelCons.name 2 indexedVecConstructorNContext indexedVecConstructorAlpha 1) := indexedVecHeadConstructorPositivityTraceAt 0 theorem indexedVecTailConstructorPositivityTrace : - Nonempty (Lean4Lean.AddInductive.ConstructorPositivityTrace + Nonempty (Ix.Theory.Named.AddInductive.ConstructorPositivityTrace indexedVecConstructorStats indexedVecKernelCons.name 3 indexedVecConstructorHeadContext (ctorIndexedVecApp indexedVecConstructorAlpha @@ -660,7 +660,7 @@ theorem familyConsTailWhnfCandidateSyntax : /-- Sources are indexed by the exact states reached by the real family-block checker, rather than by a replay from `checkerInitial`. -/ inductive ProductionIndexedPositivitySourceRel : - TcState .anon → Lean4Lean.AddInductive.Context → + TcState .anon → Ix.Theory.Named.AddInductive.Context → KExpr .anon → Lean.Expr → Prop | nat : ProductionIndexedPositivitySourceRel familyConsNatDomainState indexedVecConstructorContext familyConsNatDomain (.const ``Nat []) @@ -675,7 +675,7 @@ inductive ProductionIndexedPositivitySourceRel : /-- The mentioned production source has exactly one WHNF result shape in this fixture: the recursive `IndexedVec α n` application. -/ inductive ProductionIndexedPositivityResultRel : - TcState .anon → Lean4Lean.AddInductive.Context → + TcState .anon → Ix.Theory.Named.AddInductive.Context → KExpr .anon → Lean.Expr → Prop | tail {ixResult : KExpr .anon} (candidate : CandidateSyntaxRel nameOf @@ -695,7 +695,7 @@ theorem productionIndexedPositivitySourceMentions (relation : ProductionIndexedPositivitySourceRel ixState leanContext ixExpr leanExpr) : exprMentionsAnyAddr ixExpr #[familyId.addr] = - Lean4Lean.AddInductive.hasIndOcc + Ix.Theory.Named.AddInductive.hasIndOcc indexedVecConstructorStats.indConsts leanExpr := by cases relation with | nat => @@ -709,15 +709,15 @@ theorem productionIndexedPositivitySourceMentions candidateBlockSyntax familyConsTailDomainCandidateSyntax private theorem productionIndexedPositivityRootFree - {ixState : TcState .anon} {leanContext : Lean4Lean.AddInductive.Context} + {ixState : TcState .anon} {leanContext : Ix.Theory.Named.AddInductive.Context} {ixSource : KExpr .anon} {leanSource : Lean.Expr} (relation : ProductionIndexedPositivitySourceRel ixState leanContext ixSource leanSource) (free : exprMentionsAnyAddr ixSource #[familyId.addr] = false) : ∃ leanResult, - Lean4Lean.AddInductive.CandidateWhnfStep.Valid + Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid ⟨leanContext, leanSource, leanResult⟩ ∧ - Lean4Lean.AddInductive.hasIndOcc + Ix.Theory.Named.AddInductive.hasIndOcc indexedVecConstructorStats.indConsts leanResult = false := by cases relation with | nat => @@ -732,7 +732,7 @@ private theorem productionIndexedPositivityRootFree private theorem productionIndexedPositivityWhnf {ixBefore ixAfter : TcState .anon} - {leanContext : Lean4Lean.AddInductive.Context} + {leanContext : Ix.Theory.Named.AddInductive.Context} {ixSource ixResult : KExpr .anon} {leanSource : Lean.Expr} (relation : ProductionIndexedPositivitySourceRel ixBefore leanContext ixSource leanSource) @@ -740,7 +740,7 @@ private theorem productionIndexedPositivityWhnf (run : (RecM.whnf ixSource).run checkerMethods ixBefore = .ok ixResult ixAfter) : ∃ leanResult, - Lean4Lean.AddInductive.CandidateWhnfStep.Valid + Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid ⟨leanContext, leanSource, leanResult⟩ ∧ ProductionIndexedPositivityResultRel ixAfter leanContext ixResult leanResult := by @@ -760,7 +760,7 @@ private theorem productionIndexedPositivityWhnf .tail familyConsTailWhnfCandidateSyntax⟩ private theorem productionIndexedPositivityForall - {ixState : TcState .anon} {leanContext : Lean4Lean.AddInductive.Context} + {ixState : TcState .anon} {leanContext : Ix.Theory.Named.AddInductive.Context} {ixName : Mode.anon.F Name} {ixBinder : Mode.anon.F Lean.BinderInfo} {ixDomain ixBody : KExpr .anon} {ixInfo : ExprInfo .anon} {leanExpr : Lean.Expr} @@ -776,13 +776,13 @@ private theorem productionIndexedPositivityForall .ok (ixOpen, ixFVar) ixAfterOpen → ProductionIndexedPositivitySourceRel ixAfterOpen (leanContext.pushLocalDecl leanName leanBinder - (Lean4Lean.AddInductive.consumeTypeAnnotations leanDomain)) + (Ix.Theory.Named.AddInductive.consumeTypeAnnotations leanDomain)) ixOpen (leanBody.instantiate1 leanContext.freshExpr) := by cases relation with | tail candidate => cases candidate private theorem productionIndexedPositivityDirect - {ixState : TcState .anon} {leanContext : Lean4Lean.AddInductive.Context} + {ixState : TcState .anon} {leanContext : Ix.Theory.Named.AddInductive.Context} {ixResult : KExpr .anon} {leanResult : Lean.Expr} {id : KId .anon} {us : Array (KUniv .anon)} {info : ExprInfo .anon} {args : Array (KExpr .anon)} {groups : Array (PositivityGroup .anon)} @@ -794,10 +794,10 @@ private theorem productionIndexedPositivityDirect (_valid : ValidPositiveRecursiveApplication id us args groups #[familyId.addr] checkerMethods ixState final) : ∃ targetIdx, - Lean4Lean.AddInductive.hasIndOcc + Ix.Theory.Named.AddInductive.hasIndOcc indexedVecConstructorStats.indConsts leanResult = true ∧ leanResult.isForall = false ∧ - Lean4Lean.AddInductive.isValidIndApp? + Ix.Theory.Named.AddInductive.isValidIndApp? indexedVecConstructorStats leanResult = some targetIdx := by cases relation with | tail _ => @@ -818,7 +818,7 @@ theorem indexedVecProductionFlatPositivityTransport : /-! ## Production trace transport at the candidate checker's fuel -Ix enters constructor positivity with `maxWhnfFuel`, while Lean4Lean's retained +Ix enters constructor positivity with `maxWhnfFuel`, while Ix.Theory.Named's retained candidate context has its own inductive fuel. Fuel cannot be changed for an arbitrary recursive trace. The following lemmas first inspect the traces projected from the real production run and only reindex the two branch forms @@ -921,7 +921,7 @@ theorem indexedVecProductionTailFlatPositivityTraceAt (fuel : Nat) : active valid theorem indexedVecProductionNatConstructorPositivityTraceAt (fuel : Nat) : - Nonempty (Lean4Lean.AddInductive.ConstructorPositivityTrace + Nonempty (Ix.Theory.Named.AddInductive.ConstructorPositivityTrace indexedVecConstructorStats indexedVecKernelCons.name 1 indexedVecConstructorContext (.const ``Nat []) (fuel + 1)) := FlatPositivityTraceTransport.constructorPositivityTrace @@ -930,7 +930,7 @@ theorem indexedVecProductionNatConstructorPositivityTraceAt (fuel : Nat) : ProductionIndexedPositivitySourceRel.nat theorem indexedVecProductionHeadConstructorPositivityTraceAt (fuel : Nat) : - Nonempty (Lean4Lean.AddInductive.ConstructorPositivityTrace + Nonempty (Ix.Theory.Named.AddInductive.ConstructorPositivityTrace indexedVecConstructorStats indexedVecKernelCons.name 2 indexedVecConstructorNContext indexedVecConstructorAlpha (fuel + 1)) := FlatPositivityTraceTransport.constructorPositivityTrace @@ -939,7 +939,7 @@ theorem indexedVecProductionHeadConstructorPositivityTraceAt (fuel : Nat) : ProductionIndexedPositivitySourceRel.head theorem indexedVecProductionTailConstructorPositivityTraceAt (fuel : Nat) : - Nonempty (Lean4Lean.AddInductive.ConstructorPositivityTrace + Nonempty (Ix.Theory.Named.AddInductive.ConstructorPositivityTrace indexedVecConstructorStats indexedVecKernelCons.name 3 indexedVecConstructorHeadContext (ctorIndexedVecApp indexedVecConstructorAlpha @@ -949,4 +949,4 @@ theorem indexedVecProductionTailConstructorPositivityTraceAt (fuel : Nat) : (indexedVecProductionTailFlatPositivityTraceAt fuel) rfl ProductionIndexedPositivitySourceRel.tail -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/IndexedProducerClosure.lean b/Ix/Kernel/Verify/Inductive/IndexedProducerClosure.lean similarity index 84% rename from Ix/Tc/Verify/Inductive/IndexedProducerClosure.lean rename to Ix/Kernel/Verify/Inductive/IndexedProducerClosure.lean index b781120ff..82113542d 100644 --- a/Ix/Tc/Verify/Inductive/IndexedProducerClosure.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedProducerClosure.lean @@ -1,18 +1,18 @@ -import Ix.Tc.Verify.Inductive.GeneratedRecursorAdmission -import Ix.Tc.Verify.Inductive.IndexedCandidateTransaction +import Ix.Kernel.Verify.Inductive.GeneratedRecursorAdmission +import Ix.Kernel.Verify.Inductive.IndexedCandidateTransaction /-! # Producer-linked IndexedVec one-family closure The trust-minimal `IndexedVec` Theory transaction and the executable -Lean4Lean candidate replay are intentionally audited separately. This module +Ix.Theory.Named candidate replay are intentionally audited separately. This module joins them only at a stronger E2c root: the exact producer-selected package erases to the same certificate transaction consumed by the Ix family and generated-recursor admission, while all three anonymous ingress calls and both production block checks remain explicit. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture open IndexedRecursiveCertificateFixture @@ -38,7 +38,7 @@ structure ProducerLinkedOneFamilyClosure : Prop where checkerMethods familyKernelAfter = .ok () recursorKernelAfter semantic : CanonicalRecursorAtomicClosure -/-- The exact outer Lean4Lean producer, production Ix executions, and +/-- The exact outer Ix.Theory.Named producer, production Ix executions, and oracle-free one-family semantic closure for the concrete indexed-recursive fixture. -/ theorem producerLinkedOneFamilyClosure : ProducerLinkedOneFamilyClosure where @@ -51,4 +51,4 @@ theorem producerLinkedOneFamilyClosure : ProducerLinkedOneFamilyClosure where recursorChecked := recursorKernelRun semantic := familyRecursorAtomicClosure -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/IndexedProductionPositivity.lean b/Ix/Kernel/Verify/Inductive/IndexedProductionPositivity.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/IndexedProductionPositivity.lean rename to Ix/Kernel/Verify/Inductive/IndexedProductionPositivity.lean index b9095aad8..73668a254 100644 --- a/Ix/Tc/Verify/Inductive/IndexedProductionPositivity.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedProductionPositivity.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Inductive.IndexedBlockValidation -import Ix.Tc.Verify.Inductive.PositivityTraceAdapter +import Ix.Kernel.Verify.Inductive.IndexedBlockValidation +import Ix.Kernel.Verify.Inductive.PositivityTraceAdapter /-! # Production-selected IndexedVec positivity @@ -11,7 +11,7 @@ projected from the `IndexedVec.cons` validation selected by the exact states is reconstructed by running positivity a second time. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture /-- The production-selected constructor is the exact ingressed `cons` declaration, not merely some telescope that happened to pass the same @@ -620,7 +620,7 @@ theorem familyConsResultWhnfTerminal : /-- Exact three-field view of the positivity traversal selected by the real family-block execution. Besides the complete enclosing loop, the view exposes -the direct-only flat traces consumed by the Lean4Lean adapter. -/ +the direct-only flat traces consumed by the Ix.Theory.Named adapter. -/ structure IndexedVecConsProductionFieldProjection : Prop where nat : FlatPositivityDomainTrace familyConsPositivityGroups #[familyId.addr] checkerMethods maxWhnfFuel.toNat familyConsNatDomain @@ -910,4 +910,4 @@ theorem indexedVecConsProductionParameterBranch : | fields parameters fields => exact ⟨ctorTy, initial, final, .inr ⟨_, _, _, parameters, fields⟩⟩ -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/IndexedRecursiveAcceptance.lean b/Ix/Kernel/Verify/Inductive/IndexedRecursiveAcceptance.lean similarity index 96% rename from Ix/Tc/Verify/Inductive/IndexedRecursiveAcceptance.lean rename to Ix/Kernel/Verify/Inductive/IndexedRecursiveAcceptance.lean index c04eb5156..e5b062822 100644 --- a/Ix/Tc/Verify/Inductive/IndexedRecursiveAcceptance.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedRecursiveAcceptance.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.SingletonInductive -import Ix.Tc.Verify.Inductive.IndexedRecursiveFixture +import Ix.Kernel.Verify.Check.SingletonInductive +import Ix.Kernel.Verify.Inductive.IndexedRecursiveFixture /-! # Production acceptance of the indexed-recursive fixture @@ -10,10 +10,10 @@ ingressed recursor block is compared against that cached result. Every run starts from the exact final anonymous ingress state retained by the fixture. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture -open Lean4Lean -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveReplayFixtures open IndexedRecursiveCertificateFixture local instance acceptanceAnonKIdDecidableEq : DecidableEq (KId .anon) := @@ -183,8 +183,8 @@ private theorem directInductiveOwner_inductiveMemberOf simp_all [IsDirectInductiveOwner, KConst.IsInductiveMemberOf] private theorem certifiedConstructor_inductiveMemberOf - {source : Lean4Lean.VInductDecl} {familyId block : KId .anon} - {index : Nat} {sourceConstructor : Lean4Lean.VConstVal} + {source : Ix.Theory.Named.VInductDecl} {familyId block : KId .anon} + {index : Nat} {sourceConstructor : Ix.Theory.Named.VConstVal} {concrete familyConcrete : KConst .anon} {catalog : Catalog} (hshape : concrete.IsCertifiedSingletonConstructor source familyId index sourceConstructor) @@ -197,8 +197,8 @@ private theorem certifiedConstructor_inductiveMemberOf exact hfamilyOwner private theorem certifiedConstructor_not_inductiveMemberOf - {source : Lean4Lean.VInductDecl} {familyId block : KId .anon} - {index : Nat} {sourceConstructor : Lean4Lean.VConstVal} + {source : Ix.Theory.Named.VInductDecl} {familyId block : KId .anon} + {index : Nat} {sourceConstructor : Ix.Theory.Named.VConstVal} {concrete familyConcrete : KConst .anon} {catalog : Catalog} (hshape : concrete.IsCertifiedSingletonConstructor source familyId index sourceConstructor) @@ -220,7 +220,7 @@ private theorem certifiedConstructor_not_inductiveMemberOf simp [KConst.IsCertifiedSingletonConstructor] at hshape private theorem certifiedFamily_not_inductiveMemberOf - {source : Lean4Lean.VInductDecl} {generation : source.GenerationChecked} + {source : Ix.Theory.Named.VInductDecl} {generation : source.GenerationChecked} {constructorIds : Array (KId .anon)} {concrete : KConst .anon} {catalog : Catalog} {block : KId .anon} (hshape : concrete.IsCertifiedSingletonFamily source generation @@ -233,7 +233,7 @@ private theorem certifiedFamily_not_inductiveMemberOf KConst.IsInductiveMemberOf, IsDirectInductiveOwner] private theorem certifiedRecursor_not_inductiveMemberOf - {source : Lean4Lean.VInductDecl} {generation : source.GenerationChecked} + {source : Ix.Theory.Named.VInductDecl} {generation : source.GenerationChecked} {constructorIds : Array (KId .anon)} {concrete : KConst .anon} {catalog : Catalog} {block : KId .anon} (hshape : concrete.IsCertifiedSingletonRecursor source generation @@ -245,7 +245,7 @@ private theorem certifiedRecursor_not_inductiveMemberOf KConst.IsInductiveMemberOf] private theorem certifiedFamily_not_recursorMemberOf - {source : Lean4Lean.VInductDecl} {generation : source.GenerationChecked} + {source : Ix.Theory.Named.VInductDecl} {generation : source.GenerationChecked} {constructorIds : Array (KId .anon)} {concrete : KConst .anon} {block : KId .anon} (hshape : concrete.IsCertifiedSingletonFamily source generation @@ -257,8 +257,8 @@ private theorem certifiedFamily_not_recursorMemberOf KConst.IsRecursorMemberOf] private theorem certifiedConstructor_not_recursorMemberOf - {source : Lean4Lean.VInductDecl} {familyId : KId .anon} - {index : Nat} {sourceConstructor : Lean4Lean.VConstVal} + {source : Ix.Theory.Named.VInductDecl} {familyId : KId .anon} + {index : Nat} {sourceConstructor : Ix.Theory.Named.VConstVal} {concrete : KConst .anon} {block : KId .anon} (hshape : concrete.IsCertifiedSingletonConstructor source familyId index sourceConstructor) : @@ -631,4 +631,4 @@ theorem endToEndAcceptance : EndToEndAcceptance where admittedRecursor := recursorAtomicAdmission rejectsMalformedRecursor := malformedRecursorRun -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/IndexedRecursiveCertificate.lean b/Ix/Kernel/Verify/Inductive/IndexedRecursiveCertificate.lean similarity index 91% rename from Ix/Tc/Verify/Inductive/IndexedRecursiveCertificate.lean rename to Ix/Kernel/Verify/Inductive/IndexedRecursiveCertificate.lean index 1729e1249..cb47c4ea1 100644 --- a/Ix/Tc/Verify/Inductive/IndexedRecursiveCertificate.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedRecursiveCertificate.lean @@ -1,10 +1,10 @@ -import Ix.Tc.Verify.Inductive.Certificate -import Lean4Lean.Verify.Environment.InductiveFixtures +import Ix.Kernel.Verify.Inductive.Certificate +import Ix.Theory.Named.Verify.Environment.InductiveFixtures /-! # Certified parameterized, indexed, recursive generation fixture -Lean4Lean's `IndexedVec` fixture is the first certificate in the dependency +Ix.Theory.Named's `IndexedVec` fixture is the first certificate in the dependency whose source is simultaneously parameterized, indexed, and recursive. This module reconstructs it through the Theory-only proof-carrying transaction boundary and records the exact breadth facts that distinguish it from E2b's @@ -14,18 +14,18 @@ The ambient `Nat` environment is reconstructed through its own certified transaction instead of importing the implementation-reflection replay proof. This is important for the trust manifest: the resulting certificate depends only on the accepted Lean axioms, not on persistent-map or reflected-expression -equations from the `Lean4Lean.Verify` layer. +equations from the `Ix.Theory.Named.Verify` layer. Nothing here asserts an Ix catalog correspondence. That separate link must be constructed from production anonymous ingress and checking, so the certificate cannot silently choose the concrete declarations it certifies. -/ -namespace Ix.Tc.IndexedRecursiveCertificateFixture +namespace Ix.Kernel.IndexedRecursiveCertificateFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures /-- The exact proof-carrying transaction that constructs the ambient `Nat` environment needed by `IndexedVec`. -/ @@ -117,4 +117,4 @@ theorem certifiedFacts : transaction.certificate := transaction.facts -end Ix.Tc.IndexedRecursiveCertificateFixture +end Ix.Kernel.IndexedRecursiveCertificateFixture diff --git a/Ix/Tc/Verify/Inductive/IndexedRecursiveFixture.lean b/Ix/Kernel/Verify/Inductive/IndexedRecursiveFixture.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/IndexedRecursiveFixture.lean rename to Ix/Kernel/Verify/Inductive/IndexedRecursiveFixture.lean index 82b0a5bec..22d66da7e 100644 --- a/Ix/Tc/Verify/Inductive/IndexedRecursiveFixture.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedRecursiveFixture.lean @@ -1,21 +1,21 @@ -import Ix.Tc.Verify.Inductive.ConcreteFixture -import Ix.Tc.Verify.Inductive.IndexedRecursiveOracle +import Ix.Kernel.Verify.Inductive.ConcreteFixture +import Ix.Kernel.Verify.Inductive.IndexedRecursiveOracle /-! # Concrete parameterized, indexed, recursive fixture -This module connects the certified Lean4Lean `IndexedVec` generation to the +This module connects the certified Ix.Theory.Named `IndexedVec` generation to the actual anonymous Ixon layout. `Nat`, the family/constructor block, and the separate recursor block are ingressed in dependency order. Later sections retain the exact converted entries so the production checker and atomic admission theorem consume the same physical declarations. -/ -namespace Ix.Tc.IndexedRecursiveFixture +namespace Ix.Kernel.IndexedRecursiveFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures open IndexedRecursiveCertificateFixture open InductiveConcreteFixture @@ -1291,4 +1291,4 @@ def recursorLink : SingletonRecursorCatalogLink RawProjRel.none world.catalog recursorInterpretation.toCatalogLinkOfEntry recursorIngressExecution catalog_recursor trustedCatalog -end Ix.Tc.IndexedRecursiveFixture +end Ix.Kernel.IndexedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/IndexedRecursiveOracle.lean b/Ix/Kernel/Verify/Inductive/IndexedRecursiveOracle.lean similarity index 93% rename from Ix/Tc/Verify/Inductive/IndexedRecursiveOracle.lean rename to Ix/Kernel/Verify/Inductive/IndexedRecursiveOracle.lean index 3bdc8f8f0..09be3e79a 100644 --- a/Ix/Tc/Verify/Inductive/IndexedRecursiveOracle.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedRecursiveOracle.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Inductive.IndexedRecursiveSoundness +import Ix.Kernel.Verify.Inductive.IndexedRecursiveSoundness /-! # Certificate-backed indexed recursive recursor oracle @@ -9,11 +9,11 @@ patterns validate a uniform parameter and a changing index; the second rule also reconstructs the recursive call at the predecessor index. -/ -namespace Ix.Tc.IndexedRecursivePattern +namespace Ix.Kernel.IndexedRecursivePattern -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures open IndexedRecursiveCertificateFixture /-- The exact two-rule indexed-recursive recursor oracle. The physical Ix @@ -84,4 +84,4 @@ def oracle change (id ∈ link.members) ↔ id ∈ link.members exact Iff.rfl -end Ix.Tc.IndexedRecursivePattern +end Ix.Kernel.IndexedRecursivePattern diff --git a/Ix/Tc/Verify/Inductive/IndexedRecursivePattern.lean b/Ix/Kernel/Verify/Inductive/IndexedRecursivePattern.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/IndexedRecursivePattern.lean rename to Ix/Kernel/Verify/Inductive/IndexedRecursivePattern.lean index b877f21f9..d7f766f8f 100644 --- a/Ix/Tc/Verify/Inductive/IndexedRecursivePattern.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedRecursivePattern.lean @@ -1,5 +1,7 @@ -import Ix.Tc.Verify.Inductive.IndexedRecursiveCertificate -import Ix.Tc.Verify.Inductive.IotaPattern +import Ix.Kernel.Verify.Inductive.IndexedRecursiveCertificate +import Ix.Kernel.Verify.Inductive.IotaPattern + +open Ix.Theory (VLevel) /-! # Generated iota patterns for the indexed recursive fixture @@ -11,11 +13,11 @@ three fields and constructs the recursive call at the predecessor index from captured recursor and constructor arguments. -/ -namespace Ix.Tc.IndexedRecursivePattern +namespace Ix.Kernel.IndexedRecursivePattern -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures open IndexedRecursiveCertificateFixture def recursorName : Lean.Name := ``IndexedVec.rec @@ -428,4 +430,4 @@ theorem consPatternMetadata simpa only [consPattern, consFieldCount, show (3 : UInt64).toNat = 3 from rfl] using hfields -end Ix.Tc.IndexedRecursivePattern +end Ix.Kernel.IndexedRecursivePattern diff --git a/Ix/Tc/Verify/Inductive/IndexedRecursiveSoundness.lean b/Ix/Kernel/Verify/Inductive/IndexedRecursiveSoundness.lean similarity index 96% rename from Ix/Tc/Verify/Inductive/IndexedRecursiveSoundness.lean rename to Ix/Kernel/Verify/Inductive/IndexedRecursiveSoundness.lean index b7cdf6983..5c2bedecf 100644 --- a/Ix/Tc/Verify/Inductive/IndexedRecursiveSoundness.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedRecursiveSoundness.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Inductive.IndexedRecursivePattern +import Ix.Kernel.Verify.Inductive.IndexedRecursivePattern + +open Ix.Theory (VLevel) /-! # Indexed recursive iota soundness @@ -9,11 +11,11 @@ production WHNF. All equation shapes below reduce from the retained `GenerationChecked.rule`; no independently supplied rewrite law is used. -/ -namespace Ix.Tc.IndexedRecursivePattern +namespace Ix.Kernel.IndexedRecursivePattern -open Lean4Lean -open Lean4Lean.InductiveFixtures -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures open IndexedRecursiveCertificateFixture private abbrev generation := transaction.certificate.generation @@ -561,7 +563,7 @@ theorem nilPatternSound hconstructorApplied⟩ := htype.app_inv hfutureWF.ordered hGamma obtain ⟨recursorHeadType, hrecursorHeadTyped⟩ := - Lean4Lean.VEnv.HasType.appN_head hfutureWF hGamma hrecursorApplied + Ix.Theory.Named.VEnv.HasType.appN_head hfutureWF hGamma hrecursorApplied obtain ⟨recursorConstant, hrecursorLookup, hlevelsWF, hlevelsArity⟩ := hrecursorHeadTyped.const_inv hfutureWF.ordered hGamma have hcertifiedRecursorLookup := @@ -586,7 +588,7 @@ theorem nilPatternSound subst levelTail obtain ⟨constructorHeadType, hconstructorHeadTyped⟩ := - Lean4Lean.VEnv.HasType.appN_head hfutureWF hGamma hconstructorApplied + Ix.Theory.Named.VEnv.HasType.appN_head hfutureWF hGamma hconstructorApplied obtain ⟨constructorConstant, hconstructorLookup, hconstructorLevelsWF, hconstructorLevelsArity⟩ := hconstructorHeadTyped.const_inv hfutureWF.ordered hGamma @@ -623,7 +625,7 @@ theorem nilPatternSound have hrecursorConstantTyped : future.HasType uvars Gamma (.const ``IndexedVec.rec [v, u]) (generation.recType.instL [v, u]) := by - have htyped := Lean4Lean.VEnv.HasType.const (Γ := Gamma) + have htyped := Ix.Theory.Named.VEnv.HasType.const (Γ := Gamma) hcertifiedRecursorLookup hlevelsWF hlevelsArity rw [generationRecursorName] at htyped simpa [VInductDecl.GenerationChecked.recursor] using htyped @@ -639,20 +641,20 @@ theorem nilPatternSound (.forallE majorDomain majorBody) := by simpa using hrecursorApplied obtain ⟨recursorParameterResult, hrecursorParameterApplied⟩ := - Lean4Lean.VEnv.HasType.appN_prefix hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.appN_prefix hfutureWF hGamma (prefixArgs := [alpha]) (suffixArgs := [motive, nilMinor, consMinor, index]) hrecursorAppliedSplit have hrecursorAlpha : future.HasType uvars Gamma alpha (.sort (.succ u)) := - Lean4Lean.VEnv.HasType.app_argument_of_head hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.app_argument_of_head hfutureWF hGamma hrecursorParameterApplied hrecursorParameterHead have hconstructorConstantTyped : future.HasType uvars Gamma (.const ``IndexedVec.nil [constructorU]) (nilNormalized.raw.toVConstant.type.instL [constructorU]) := by simpa [nilNormalizedName] using - (Lean4Lean.VEnv.HasType.const (Γ := Gamma) + (Ix.Theory.Named.VEnv.HasType.const (Γ := Gamma) hcertifiedConstructorLookup hconstructorLevelsWF hconstructorLevelsArity) have hconstructorParameterHead : future.HasType uvars Gamma @@ -664,7 +666,7 @@ theorem nilPatternSound exact hconstructorConstantTyped have hconstructorAlpha : future.HasType uvars Gamma constructorAlpha (.sort (.succ constructorU)) := - Lean4Lean.VEnv.HasType.app_argument_of_head hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.app_argument_of_head hfutureWF hGamma hconstructorApplied hconstructorParameterHead obtain ⟨parameterType, hparameterTyped⟩ := hparameter' @@ -679,7 +681,7 @@ theorem nilPatternSound hrecursorSort.trans hfutureWF hGamma hconstructorSort.symm have huniverse : u ≈ constructorU := VLevel.succ_congr_iff.mp - (Lean4Lean.VEnv.IsDefEqU.sort_inv hfutureWF hGamma hsorts) + (Ix.Theory.Named.VEnv.IsDefEqU.sort_inv hfutureWF hGamma hsorts) have hnilLookup := hcertifiedConstructorLookup rw [nilNormalizedName] at hnilLookup @@ -717,7 +719,7 @@ theorem nilPatternSound [alpha, motive, nilMinor, consMinor, .const ``Nat.zero []]) := by refine ⟨indexBody.inst index, ?_⟩ simpa only [VExpr.appN] using - (Lean4Lean.VEnv.IsDefEq.appDF hrecursorBeforeIndex hindexAtDomain) + (Ix.Theory.Named.VEnv.IsDefEq.appDF hrecursorBeforeIndex hindexAtDomain) have hrecursorIndexEqTyped : future.IsDefEq uvars Gamma (VExpr.appN (.const ``IndexedVec.rec [v, u]) [alpha, motive, nilMinor, consMinor, index]) @@ -776,18 +778,18 @@ theorem nilPatternSound (.forallE majorDomain majorBody) := by simpa using hrecursorApplied obtain ⟨recursorCommonResult, hrecursorCommonApplied⟩ := - Lean4Lean.VEnv.HasType.appN_prefix hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.appN_prefix hfutureWF hGamma (prefixArgs := [alpha, motive, nilMinor, consMinor]) (suffixArgs := [index]) hrecursorCommonAppliedSplit have hcommonLength : [alpha, motive, nilMinor, consMinor].length = (commonBinders.map (VExpr.instL [v, u])).length := by simp have hequationLhsApplied := - Lean4Lean.VEnv.HasType.transfer_appN_telescope_instRev + Ix.Theory.Named.VEnv.HasType.transfer_appN_telescope_instRev hfutureWF hGamma hcommonLength hrecursorCommonApplied hrecursorCommonType hequationLhsCommonType have hequationApplied := - Lean4Lean.VEnv.IsDefEq.appN_same hfutureWF hGamma hequation + Ix.Theory.Named.VEnv.IsDefEq.appN_same hfutureWF hGamma hequation hequationLhsApplied have hequationRhsApplied := (hequationApplied.of_l hfutureWF hGamma @@ -800,7 +802,7 @@ theorem nilPatternSound have hequationLhsApplied' := hequationLhsApplied rw [(generatedRule_shape 0 nilNormalized).1, VExpr.instL_lamN] at hequationLhsApplied' - have hlhsBeta := Lean4Lean.VEnv.HasType.lamN_appN_beta + have hlhsBeta := Ix.Theory.Named.VEnv.HasType.lamN_appN_beta hfutureWF hGamma hruleBinderLength hequationLhsApplied' rw [nilLhsBody_open] at hlhsBeta have hlhsBeta' : future.IsDefEqU uvars Gamma @@ -817,7 +819,7 @@ theorem nilPatternSound have hequationRhsApplied' := hequationRhsApplied rw [(generatedRule_shape 0 nilNormalized).2.1, VExpr.instL_lamN] at hequationRhsApplied' - have hrhsBeta := Lean4Lean.VEnv.HasType.lamN_appN_beta + have hrhsBeta := Ix.Theory.Named.VEnv.HasType.lamN_appN_beta hfutureWF hGamma hruleBinderLength hequationRhsApplied' rw [nilRhsBody_open] at hrhsBeta have hrhsBeta' : future.IsDefEqU uvars Gamma @@ -928,7 +930,7 @@ theorem consPatternSound hconstructorApplied⟩ := htype.app_inv hfutureWF.ordered hGamma obtain ⟨recursorHeadType, hrecursorHeadTyped⟩ := - Lean4Lean.VEnv.HasType.appN_head hfutureWF hGamma hrecursorApplied + Ix.Theory.Named.VEnv.HasType.appN_head hfutureWF hGamma hrecursorApplied obtain ⟨recursorConstant, hrecursorLookup, hlevelsWF, hlevelsArity⟩ := hrecursorHeadTyped.const_inv hfutureWF.ordered hGamma have hcertifiedRecursorLookup := @@ -953,7 +955,7 @@ theorem consPatternSound subst levelTail obtain ⟨constructorHeadType, hconstructorHeadTyped⟩ := - Lean4Lean.VEnv.HasType.appN_head hfutureWF hGamma hconstructorApplied + Ix.Theory.Named.VEnv.HasType.appN_head hfutureWF hGamma hconstructorApplied obtain ⟨constructorConstant, hconstructorLookup, hconstructorLevelsWF, hconstructorLevelsArity⟩ := hconstructorHeadTyped.const_inv hfutureWF.ordered hGamma @@ -990,7 +992,7 @@ theorem consPatternSound have hrecursorConstantTyped : future.HasType uvars Gamma (.const ``IndexedVec.rec [v, u]) (generation.recType.instL [v, u]) := by - have htyped := Lean4Lean.VEnv.HasType.const (Γ := Gamma) + have htyped := Ix.Theory.Named.VEnv.HasType.const (Γ := Gamma) hcertifiedRecursorLookup hlevelsWF hlevelsArity rw [generationRecursorName] at htyped simpa [VInductDecl.GenerationChecked.recursor] using htyped @@ -1006,20 +1008,20 @@ theorem consPatternSound (.forallE majorDomain majorBody) := by simpa using hrecursorApplied obtain ⟨recursorParameterResult, hrecursorParameterApplied⟩ := - Lean4Lean.VEnv.HasType.appN_prefix hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.appN_prefix hfutureWF hGamma (prefixArgs := [alpha]) (suffixArgs := [motive, nilMinor, consMinor, index]) hrecursorAppliedSplit have hrecursorAlpha : future.HasType uvars Gamma alpha (.sort (.succ u)) := - Lean4Lean.VEnv.HasType.app_argument_of_head hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.app_argument_of_head hfutureWF hGamma hrecursorParameterApplied hrecursorParameterHead have hconstructorConstantTyped : future.HasType uvars Gamma (.const ``IndexedVec.cons [constructorU]) (consNormalized.raw.toVConstant.type.instL [constructorU]) := by simpa [consNormalizedName] using - (Lean4Lean.VEnv.HasType.const (Γ := Gamma) + (Ix.Theory.Named.VEnv.HasType.const (Γ := Gamma) hcertifiedConstructorLookup hconstructorLevelsWF hconstructorLevelsArity) have hconstructorParameterHead : future.HasType uvars Gamma @@ -1034,12 +1036,12 @@ theorem consPatternSound ([constructorAlpha] ++ [n, a, as])) majorDomain := by simpa using hconstructorApplied obtain ⟨constructorParameterResult, hconstructorParameterApplied⟩ := - Lean4Lean.VEnv.HasType.appN_prefix hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.appN_prefix hfutureWF hGamma (prefixArgs := [constructorAlpha]) (suffixArgs := [n, a, as]) hconstructorAppliedSplit have hconstructorAlpha : future.HasType uvars Gamma constructorAlpha (.sort (.succ constructorU)) := - Lean4Lean.VEnv.HasType.app_argument_of_head hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.app_argument_of_head hfutureWF hGamma hconstructorParameterApplied hconstructorParameterHead obtain ⟨parameterType, hparameterTyped⟩ := hparameter' @@ -1054,7 +1056,7 @@ theorem consPatternSound hrecursorSort.trans hfutureWF hGamma hconstructorSort.symm have huniverse : u ≈ constructorU := VLevel.succ_congr_iff.mp - (Lean4Lean.VEnv.IsDefEqU.sort_inv hfutureWF hGamma hsorts) + (Ix.Theory.Named.VEnv.IsDefEqU.sort_inv hfutureWF hGamma hsorts) have hconsLookup := hcertifiedConstructorLookup rw [consNormalizedName] at hconsLookup @@ -1094,7 +1096,7 @@ theorem consPatternSound [constructorAlpha, n, a, as]) (VExpr.appN (.const ``IndexedVec.cons [u]) [alpha, n, a, as]) := by simpa only [VExpr.appN] using - (Lean4Lean.VEnv.IsDefEq.appN_same hfutureWF hGamma + (Ix.Theory.Named.VEnv.IsDefEq.appN_same hfutureWF hGamma hconstructorPrefixEqTyped hconstructorAppliedFromPrefix) obtain ⟨indexDomain, indexBody, hrecursorBeforeIndex, hindexTyped⟩ := @@ -1110,7 +1112,7 @@ theorem consPatternSound .app (.const ``Nat.succ []) n]) := by refine ⟨indexBody.inst index, ?_⟩ simpa only [VExpr.appN] using - (Lean4Lean.VEnv.IsDefEq.appDF hrecursorBeforeIndex hindexAtDomain) + (Ix.Theory.Named.VEnv.IsDefEq.appDF hrecursorBeforeIndex hindexAtDomain) have hrecursorIndexEqTyped : future.IsDefEq uvars Gamma (VExpr.appN (.const ``IndexedVec.rec [v, u]) [alpha, motive, nilMinor, consMinor, index]) @@ -1175,7 +1177,7 @@ theorem consPatternSound (.forallE majorDomain majorBody) := by simpa using hrecursorApplied obtain ⟨recursorCommonResult, hrecursorCommonApplied⟩ := - Lean4Lean.VEnv.HasType.appN_prefix hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.appN_prefix hfutureWF hGamma (prefixArgs := [alpha, motive, nilMinor, consMinor]) (suffixArgs := [index]) hrecursorCommonAppliedSplit have hcommonLength : @@ -1185,14 +1187,14 @@ theorem consPatternSound (VExpr.appN ((generation.rule 1 consNormalized).lhs.instL [v, u]) [alpha, motive, nilMinor, consMinor]) (consEquationFieldType v u alpha motive nilMinor consMinor) := by - exact Lean4Lean.VEnv.HasType.transfer_appN_telescope_instRev + exact Ix.Theory.Named.VEnv.HasType.transfer_appN_telescope_instRev hfutureWF hGamma hcommonLength hrecursorCommonApplied hrecursorCommonType hequationLhsCommonType have hcanonicalConstructorConstantTyped : future.HasType uvars Gamma (.const ``IndexedVec.cons [u]) (consNormalized.raw.toVConstant.type.instL [u]) := by - exact Lean4Lean.VEnv.HasType.const hconsLookup + exact Ix.Theory.Named.VEnv.HasType.const hconsLookup (fun level hlevel => by simp only [List.mem_singleton] at hlevel subst level @@ -1207,7 +1209,7 @@ theorem consPatternSound have hcanonicalConstructorPrefix : future.HasType uvars Gamma (.app (.const ``IndexedVec.cons [u]) alpha) (consConstructorFieldType u alpha) := by - have happ := Lean4Lean.VEnv.HasType.app + have happ := Ix.Theory.Named.VEnv.HasType.app hcanonicalConstructorParameterHead hrecursorAlpha simpa [consConstructorFieldType, VExpr.instRev] using happ have hcanonicalConstructorApplied : future.HasType uvars Gamma @@ -1252,7 +1254,7 @@ theorem consPatternSound VExpr.instRev, VExpr.instL, VExpr.inst, VExpr.instVar, VExpr.telN, VExpr.dropN] have hequationLhsFieldsApplied := - Lean4Lean.VEnv.HasType.transfer_appN_telescope_instRev + Ix.Theory.Named.VEnv.HasType.transfer_appN_telescope_instRev hfutureWF hGamma hfieldLength hcanonicalConstructorApplied hconstructorFieldHead' hequationFieldHead have hequationLhsApplied : future.HasType uvars Gamma @@ -1267,7 +1269,7 @@ theorem consPatternSound VExpr.appN_append] exact hequationLhsFieldsApplied have hequationApplied := - Lean4Lean.VEnv.IsDefEq.appN_same hfutureWF hGamma hequation + Ix.Theory.Named.VEnv.IsDefEq.appN_same hfutureWF hGamma hequation hequationLhsApplied have hequationRhsApplied := (hequationApplied.of_l hfutureWF hGamma @@ -1280,7 +1282,7 @@ theorem consPatternSound have hequationLhsApplied' := hequationLhsApplied rw [(generatedRule_shape 1 consNormalized).1, VExpr.instL_lamN] at hequationLhsApplied' - have hlhsBeta := Lean4Lean.VEnv.HasType.lamN_appN_beta + have hlhsBeta := Ix.Theory.Named.VEnv.HasType.lamN_appN_beta hfutureWF hGamma hruleBinderLength hequationLhsApplied' rw [consLhsBody_open] at hlhsBeta have hlhsBeta' : future.IsDefEqU uvars Gamma @@ -1299,7 +1301,7 @@ theorem consPatternSound have hequationRhsApplied' := hequationRhsApplied rw [(generatedRule_shape 1 consNormalized).2.1, VExpr.instL_lamN] at hequationRhsApplied' - have hrhsBeta := Lean4Lean.VEnv.HasType.lamN_appN_beta + have hrhsBeta := Ix.Theory.Named.VEnv.HasType.lamN_appN_beta hfutureWF hGamma hruleBinderLength hequationRhsApplied' rw [consRhsBody_open] at hrhsBeta have hrhsBeta' : future.IsDefEqU uvars Gamma @@ -1366,4 +1368,4 @@ theorem consPatternRel (consPatternMetadata link hone hrule) (consPatternSound link hrule (family.constructorIds[1]'hone)) -end Ix.Tc.IndexedRecursivePattern +end Ix.Kernel.IndexedRecursivePattern diff --git a/Ix/Tc/Verify/Inductive/IngressExecution.lean b/Ix/Kernel/Verify/Inductive/IngressExecution.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/IngressExecution.lean rename to Ix/Kernel/Verify/Inductive/IngressExecution.lean index ebfc29670..2b27759a0 100644 --- a/Ix/Tc/Verify/Inductive/IngressExecution.lean +++ b/Ix/Kernel/Verify/Inductive/IngressExecution.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Ingress -import Ix.Tc.Verify.Inductive.SingletonIngress +import Ix.Kernel.Ingress +import Ix.Kernel.Verify.Inductive.SingletonIngress /-! # Anonymous inductive-block ingress execution @@ -17,7 +17,7 @@ discharge it for their actual projection addresses; no Blake3 injectivity is smuggled into the model. -/ -namespace Ix.Tc +namespace Ix.Kernel /-! ## Flat insertion semantics -/ @@ -196,7 +196,7 @@ end AnonBlockIngressSuccessTrace /-! ## Singleton source interpretations -/ -open Lean4Lean (VEnv VInductDecl) +open Ix.Theory.Named (VEnv VInductDecl) /-- Ghost interpretation of one production family-block conversion result. @@ -509,4 +509,4 @@ def toCatalogLinkOfEntry end SingletonRecursorIngressInterpretation -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/IotaPattern.lean b/Ix/Kernel/Verify/Inductive/IotaPattern.lean similarity index 79% rename from Ix/Tc/Verify/Inductive/IotaPattern.lean rename to Ix/Kernel/Verify/Inductive/IotaPattern.lean index 95788cded..7022c09d1 100644 --- a/Ix/Tc/Verify/Inductive/IotaPattern.lean +++ b/Ix/Kernel/Verify/Inductive/IotaPattern.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Inductive.RuleApplication +import Ix.Kernel.Verify.Inductive.RuleApplication + +open Ix.Theory (VLevel) /-! # Constructive iota-pattern paths @@ -11,15 +13,15 @@ at a constructor's exact rule index; an off-by-one or reversed-spine adapter cannot satisfy the positional theorem. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean +open Ix.Theory.Named /-- The dependent path of the `index`-th (left-to-right) argument captured by `pattern.varN arity`. -/ -def IotaVarPath (pattern : Lean4Lean.Pattern) : +def IotaVarPath (pattern : Ix.Theory.Named.Pattern) : (arity : Nat) → Fin arity → - (Lean4Lean.Pattern.varN pattern arity).Path + (Ix.Theory.Named.Pattern.varN pattern arity).Path | 0, index => Fin.elim0 index | arity + 1, index => if h : index.val < arity then @@ -30,35 +32,35 @@ def IotaVarPath (pattern : Lean4Lean.Pattern) : /-- Invert a `varN` match to the exact constant-headed argument list and identify every dependent capture path with its positional list entry. -/ theorem iotaVarMatch_spine - {name : Lean.Name} {arity : Nat} {source : Lean4Lean.VExpr} - {levels : List Lean4Lean.VLevel} - {captures : ((Lean4Lean.Pattern.const name).varN arity).Path → - Lean4Lean.VExpr} - (hmatch : Lean4Lean.Pattern.Matches - ((Lean4Lean.Pattern.const name).varN arity) + {name : Lean.Name} {arity : Nat} {source : Ix.Theory.Named.VExpr} + {levels : List Ix.Theory.VLevel} + {captures : ((Ix.Theory.Named.Pattern.const name).varN arity).Path → + Ix.Theory.Named.VExpr} + (hmatch : Ix.Theory.Named.Pattern.Matches + ((Ix.Theory.Named.Pattern.const name).varN arity) source levels captures) : - ∃ arguments : List Lean4Lean.VExpr, + ∃ arguments : List Ix.Theory.Named.VExpr, arguments.length = arity ∧ - source = Lean4Lean.VExpr.appN (.const name levels) arguments ∧ + source = Ix.Theory.Named.VExpr.appN (.const name levels) arguments ∧ ∀ index : Fin arity, arguments[index.val]? = some (captures (IotaVarPath (.const name) arity index)) := by induction arity generalizing source with | zero => - change Lean4Lean.Pattern.Matches (.const name) + change Ix.Theory.Named.Pattern.Matches (.const name) source levels captures at hmatch cases hmatch exact ⟨[], rfl, rfl, fun index => Fin.elim0 index⟩ | succ arity ih => - change Lean4Lean.Pattern.Matches - (.var ((Lean4Lean.Pattern.const name).varN arity)) + change Ix.Theory.Named.Pattern.Matches + (.var ((Ix.Theory.Named.Pattern.const name).varN arity)) source levels captures at hmatch cases hmatch with | var hprefix => rename_i fn argument prefixCaptures obtain ⟨arguments, hlength, rfl, hcaptures⟩ := ih hprefix refine ⟨arguments ++ [argument], by simp [hlength], ?_, ?_⟩ - · rw [Lean4Lean.VExpr.appN_append] + · rw [Ix.Theory.Named.VExpr.appN_append] rfl · intro index by_cases hlt : index.val < arity @@ -117,9 +119,9 @@ def constructorArgumentRhs @[simp] theorem recursorArgumentRhs_apply (recursorName : Lean.Name) (majorIdx : Nat) (constructorName : Lean.Name) (constructorArgs : Nat) - (index : Fin majorIdx) (levels : List Lean4Lean.VLevel) + (index : Fin majorIdx) (levels : List Ix.Theory.VLevel) (captures : (RecursorIotaPattern recursorName majorIdx constructorName - constructorArgs).Path → Lean4Lean.VExpr) : + constructorArgs).Path → Ix.Theory.Named.VExpr) : (recursorArgumentRhs recursorName majorIdx constructorName constructorArgs index).apply levels captures = captures (recursorArgumentPath recursorName majorIdx constructorName @@ -128,39 +130,39 @@ def constructorArgumentRhs @[simp] theorem constructorArgumentRhs_apply (recursorName : Lean.Name) (majorIdx : Nat) (constructorName : Lean.Name) (constructorArgs : Nat) - (index : Fin constructorArgs) (levels : List Lean4Lean.VLevel) + (index : Fin constructorArgs) (levels : List Ix.Theory.VLevel) (captures : (RecursorIotaPattern recursorName majorIdx constructorName - constructorArgs).Path → Lean4Lean.VExpr) : + constructorArgs).Path → Ix.Theory.Named.VExpr) : (constructorArgumentRhs recursorName majorIdx constructorName constructorArgs index).apply levels captures = captures (constructorArgumentPath recursorName majorIdx constructorName constructorArgs index) := rfl /-- A complete iota match exposes both positional application spines. The -constructor universe list is existential because Lean4Lean's pattern result +constructor universe list is existential because Ix.Theory.Named's pattern result retains the recursor levels only. -/ theorem matches_spines {recursorName constructorName : Lean.Name} - {majorIdx constructorArgs : Nat} {source : Lean4Lean.VExpr} - {levels : List Lean4Lean.VLevel} + {majorIdx constructorArgs : Nat} {source : Ix.Theory.Named.VExpr} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern recursorName majorIdx constructorName - constructorArgs).Path → Lean4Lean.VExpr} - (hmatch : Lean4Lean.Pattern.Matches + constructorArgs).Path → Ix.Theory.Named.VExpr} + (hmatch : Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern recursorName majorIdx constructorName constructorArgs) source levels captures) : ∃ recursorArguments constructorLevels constructorArguments, recursorArguments.length = majorIdx ∧ constructorArguments.length = constructorArgs ∧ source = .app - (Lean4Lean.VExpr.appN (.const recursorName levels) + (Ix.Theory.Named.VExpr.appN (.const recursorName levels) recursorArguments) - (Lean4Lean.VExpr.appN (.const constructorName constructorLevels) + (Ix.Theory.Named.VExpr.appN (.const constructorName constructorLevels) constructorArguments) ∧ (∀ index : Fin majorIdx, recursorArguments[index.val]? = some (captures (recursorArgumentPath recursorName majorIdx constructorName constructorArgs index))) := by - simp only [RecursorIotaPattern, Lean4Lean.SimplePattern.toPattern] at hmatch + simp only [RecursorIotaPattern, Ix.Theory.Named.SimplePattern.toPattern] at hmatch cases hmatch with | app hrecursor hconstructor => obtain ⟨recursorArguments, hrecLength, rfl, hrecCaptures⟩ := @@ -178,20 +180,20 @@ constructor equations as well as the recursor-prefix equations: their generated RHS and their index-consistency checks mention constructor fields. -/ theorem matches_spines_full {recursorName constructorName : Lean.Name} - {majorIdx constructorArgs : Nat} {source : Lean4Lean.VExpr} - {levels : List Lean4Lean.VLevel} + {majorIdx constructorArgs : Nat} {source : Ix.Theory.Named.VExpr} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern recursorName majorIdx constructorName - constructorArgs).Path → Lean4Lean.VExpr} - (hmatch : Lean4Lean.Pattern.Matches + constructorArgs).Path → Ix.Theory.Named.VExpr} + (hmatch : Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern recursorName majorIdx constructorName constructorArgs) source levels captures) : ∃ recursorArguments constructorLevels constructorArguments, recursorArguments.length = majorIdx ∧ constructorArguments.length = constructorArgs ∧ source = .app - (Lean4Lean.VExpr.appN (.const recursorName levels) + (Ix.Theory.Named.VExpr.appN (.const recursorName levels) recursorArguments) - (Lean4Lean.VExpr.appN (.const constructorName constructorLevels) + (Ix.Theory.Named.VExpr.appN (.const constructorName constructorLevels) constructorArguments) ∧ (∀ index : Fin majorIdx, recursorArguments[index.val]? = some @@ -201,7 +203,7 @@ theorem matches_spines_full constructorArguments[index.val]? = some (captures (constructorArgumentPath recursorName majorIdx constructorName constructorArgs index))) := by - simp only [RecursorIotaPattern, Lean4Lean.SimplePattern.toPattern] at hmatch + simp only [RecursorIotaPattern, Ix.Theory.Named.SimplePattern.toPattern] at hmatch cases hmatch with | app hrecursor hconstructor => obtain ⟨recursorArguments, hrecLength, rfl, hrecCaptures⟩ := @@ -217,4 +219,4 @@ theorem matches_spines_full end RecursorIotaPattern -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/MutualBlockCertificate.lean b/Ix/Kernel/Verify/Inductive/MutualBlockCertificate.lean similarity index 85% rename from Ix/Tc/Verify/Inductive/MutualBlockCertificate.lean rename to Ix/Kernel/Verify/Inductive/MutualBlockCertificate.lean index 9341a10f1..15731b877 100644 --- a/Ix/Tc/Verify/Inductive/MutualBlockCertificate.lean +++ b/Ix/Kernel/Verify/Inductive/MutualBlockCertificate.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Inductive.BlockCertificate -import Lean4Lean.Verify.Environment.MutualInductiveFixtures +import Ix.Kernel.Verify.Inductive.BlockCertificate +import Ix.Theory.Named.Verify.Environment.MutualInductiveFixtures /-! # Genuine mutual-block certificate fixture @@ -10,16 +10,16 @@ globally flattened constructors, one generated recursor per family, and five globally flattened iota rules. Recursive fields target both the sibling family and their own family, including one recursive occurrence below a Pi. -This module consumes Lean4Lean's retained L4L-08 certificate through Ix's +This module consumes Ix.Theory.Named's retained Spec-08 certificate through Ix's Theory-only block adapter. Physical Ix ingress, checker execution, catalog linkage, and recursor admission remain separate obligations. -/ -namespace Ix.Tc.MutualTreeCertificateFixture +namespace Ix.Kernel.MutualTreeCertificateFixture -open Lean4Lean -open Lean4Lean.MutualInductiveFixtures -open Lean4Lean.MutualInductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.MutualInductiveFixtures +open Ix.Theory.Named.MutualInductiveReplayFixtures /-- One atomic two-family Theory transaction. -/ def transaction : @@ -28,10 +28,10 @@ def transaction : success := tree_addInductBlockCertified beforeWF := ⟨[], .empty⟩ -/-- The same completed transaction exposed through Lean4Lean's latest +/-- The same completed transaction exposed through Ix.Theory.Named's latest consumer API. In particular, its derived rule-closure and iota-pattern theorems are available to the physical recursor link without an Ix axiom. -/ -def lean4leanCertificate : +def specificationCertificate : treeDecl.BlockCertificate VEnv.empty treeFinalEnv := transaction.toBlockCertificate @@ -80,7 +80,7 @@ theorem breadth : BreadthFacts where treeListTargetsItself := rfl /-- Complete block-wide family/constructor/recursor/rule consequences from -the exact retained L4L-08 transaction. -/ +the exact retained Spec-08 transaction. -/ theorem certifiedFacts : CertifiedBlockGenerationFacts VEnv.empty treeFinalEnv transaction.certificate := @@ -91,4 +91,4 @@ history, rather than two sequential singleton history entries. -/ theorem finalEnvWF : treeFinalEnv.WF := transaction.afterWF -end Ix.Tc.MutualTreeCertificateFixture +end Ix.Kernel.MutualTreeCertificateFixture diff --git a/Ix/Tc/Verify/Inductive/MutualBlockFixture.lean b/Ix/Kernel/Verify/Inductive/MutualBlockFixture.lean similarity index 97% rename from Ix/Tc/Verify/Inductive/MutualBlockFixture.lean rename to Ix/Kernel/Verify/Inductive/MutualBlockFixture.lean index 5d1f29d2f..6c7595844 100644 --- a/Ix/Tc/Verify/Inductive/MutualBlockFixture.lean +++ b/Ix/Kernel/Verify/Inductive/MutualBlockFixture.lean @@ -1,12 +1,12 @@ import Ix.CompileDriver -import Ix.Tc.Verify.Inductive.ConcreteFixture -import Ix.Tc.Verify.Inductive.MutualBlockCertificate +import Ix.Kernel.Verify.Inductive.ConcreteFixture +import Ix.Kernel.Verify.Inductive.MutualBlockCertificate /-! # Physical mutual `Tree`/`TreeList` fixture This module compiles the exact kernel metadata already retained by the -Lean4Lean mutual-inductive replay. It therefore exercises the same pure Ix +Ix.Theory.Named mutual-inductive replay. It therefore exercises the same pure Ix compiler used by production instead of maintaining a second handwritten Ixon encoding of the two families, five constructors, two recursors, and five rules. @@ -17,10 +17,10 @@ tables are the physical inputs for the mutual checker and semantic-admission links built in the following modules. -/ -namespace Ix.Tc.MutualTreeFixture +namespace Ix.Kernel.MutualTreeFixture -open Lean4Lean.MutualInductiveFixtures -open Lean4Lean.MutualInductiveReplayFixtures +open Ix.Theory.Named.MutualInductiveFixtures +open Ix.Theory.Named.MutualInductiveReplayFixtures open InductiveConcreteFixture local instance mutualAnonKIdDecidableEq : DecidableEq (KId .anon) := @@ -384,4 +384,4 @@ theorem recursorBlockLoaded : recursorIngressAfter.getBlock? recursorBlockId = some recursorMembers := recursorBlockLoadedNative -end Ix.Tc.MutualTreeFixture +end Ix.Kernel.MutualTreeFixture diff --git a/Ix/Tc/Verify/Inductive/MutualBlockValidation.lean b/Ix/Kernel/Verify/Inductive/MutualBlockValidation.lean similarity index 97% rename from Ix/Tc/Verify/Inductive/MutualBlockValidation.lean rename to Ix/Kernel/Verify/Inductive/MutualBlockValidation.lean index 9b98e0427..100317017 100644 --- a/Ix/Tc/Verify/Inductive/MutualBlockValidation.lean +++ b/Ix/Kernel/Verify/Inductive/MutualBlockValidation.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.SingletonInductive -import Ix.Tc.Verify.Inductive.MutualBlockFixture +import Ix.Kernel.Verify.Check.SingletonInductive +import Ix.Kernel.Verify.Inductive.MutualBlockFixture /-! # Production validation of the mutual `Tree`/`TreeList` blocks @@ -10,7 +10,7 @@ by the real recursor-block checker for the separately owned physical block. No semantic certificate or oracle participates in either execution. -/ -namespace Ix.Tc.MutualTreeFixture +namespace Ix.Kernel.MutualTreeFixture local instance validationAnonKIdDecidableEq : DecidableEq (KId .anon) := fun left right => @@ -167,4 +167,4 @@ theorem endToEndExecution : EndToEndExecution where familyKernel := familyKernelRun recursorKernel := recursorKernelRun -end Ix.Tc.MutualTreeFixture +end Ix.Kernel.MutualTreeFixture diff --git a/Ix/Tc/Verify/Inductive/MutualFamily.lean b/Ix/Kernel/Verify/Inductive/MutualFamily.lean similarity index 94% rename from Ix/Tc/Verify/Inductive/MutualFamily.lean rename to Ix/Kernel/Verify/Inductive/MutualFamily.lean index 5501508ea..80b18fa7f 100644 --- a/Ix/Tc/Verify/Inductive/MutualFamily.lean +++ b/Ix/Kernel/Verify/Inductive/MutualFamily.lean @@ -1,10 +1,10 @@ -import Ix.Tc.Verify.Check.BlockAcceptance -import Ix.Tc.Verify.Inductive.BlockCertificate +import Ix.Kernel.Verify.Check.BlockAcceptance +import Ix.Kernel.Verify.Inductive.BlockCertificate /-! # Certified mutual-family admission -A Lean4Lean block certificate owns one atomic semantic transaction for every +A Ix.Theory.Named block certificate owns one atomic semantic transaction for every family and constructor in a mutual declaration. This module supplies the Ix-facing representation boundary for the corresponding physical family/constructor block. It does not split the source declaration into @@ -15,9 +15,9 @@ physical Ix block, although their Theory constants and equations are already installed by the same source transaction. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VConstant VConstVal VEnv VInductDecl) +open Ix.Theory.Named (VConstant VConstVal VEnv VInductDecl) /-- Exact concrete kinds permitted in the family/constructor half of a mutual-inductive transaction. -/ @@ -55,7 +55,7 @@ end KConst.IsMutualFamilyMember /-- A concrete member is tied either to one source family or to one source constructor of the complete mutual declaration. The disjunction retains -the exact source inventory membership used by Lean4Lean's lookup theorems. -/ +the exact source inventory membership used by Ix.Theory.Named's lookup theorems. -/ def MutualSourceMember (source : VInductDecl) (name : Lean.Name) (constant : VConstant) : Prop := (∃ family, family ∈ source.types ∧ name = family.name ∧ @@ -89,7 +89,7 @@ structure MutualFamilyCatalogLink (trProj : RawProjRel) namespace MutualFamilyCatalogLink /-- Derive the exact Theory lookup and constant-WF facts from the current -Lean4Lean consumer certificate. -/ +Ix.Theory.Named consumer certificate. -/ theorem translateMember {trProj : RawProjRel} {world : VerifyWorld} {source : VInductDecl} {after : VEnv} {tx : CertifiedBlockGenerationTransaction source world.venv after} @@ -154,4 +154,4 @@ theorem transition {trProj : RawProjRel} {world : VerifyWorld} end MutualFamilyCatalogLink -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/MutualFamilyAdmission.lean b/Ix/Kernel/Verify/Inductive/MutualFamilyAdmission.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/MutualFamilyAdmission.lean rename to Ix/Kernel/Verify/Inductive/MutualFamilyAdmission.lean index 1e90ad411..078de4a84 100644 --- a/Ix/Tc/Verify/Inductive/MutualFamilyAdmission.lean +++ b/Ix/Kernel/Verify/Inductive/MutualFamilyAdmission.lean @@ -1,11 +1,11 @@ -import Ix.Tc.Verify.Inductive.MutualBlockValidation -import Ix.Tc.Verify.Inductive.MutualFamily +import Ix.Kernel.Verify.Inductive.MutualBlockValidation +import Ix.Kernel.Verify.Inductive.MutualFamily /-! # Atomic mutual `Tree`/`TreeList` family admission This module joins the production compiler/ingress/checker witness to the -single Lean4Lean `Tree`/`TreeList` block transaction. The seven physical +single Ix.Theory.Named `Tree`/`TreeList` block transaction. The seven physical family/constructor declarations are interpreted in the complete source inventory and admitted in one atomic semantic transition. @@ -14,11 +14,11 @@ semantic constants and rules are already installed by this transaction; the next recursor-link module supplies their rule and pattern provenance. -/ -namespace Ix.Tc.MutualTreeFixture +namespace Ix.Kernel.MutualTreeFixture -open Lean4Lean -open Lean4Lean.MutualInductiveFixtures -open Lean4Lean.MutualInductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.MutualInductiveFixtures +open Ix.Theory.Named.MutualInductiveReplayFixtures open MutualTreeCertificateFixture local instance admissionAnonKIdDecidableEq : DecidableEq (KId .anon) := @@ -677,7 +677,7 @@ structure MutualFamilyAtomicClosure : Prop where execution : EndToEndExecution exactFamily : ExactCheckBlock world familyBlockId familyMembers .inductive' exactRecursor : ExactCheckBlock world recursorBlockId recursorMembers .recursor - ruleClosure : lean4leanCertificate.generation.RuleClosure + ruleClosure : specificationCertificate.generation.RuleClosure familyAdmission : AtomicBlockAdmission RawProjRel.none world familyAcceptedWorld familyBlockId familyMembers .inductive' @@ -687,8 +687,8 @@ theorem mutualFamilyAtomicClosure : MutualFamilyAtomicClosure where execution := endToEndExecution exactFamily := exactFamilyBlock exactRecursor := exactRecursorBlock - ruleClosure := lean4leanCertificate.ruleClosure + ruleClosure := specificationCertificate.ruleClosure familyAdmission := familyAtomicAdmission accepted := familyBlockAccepted -end Ix.Tc.MutualTreeFixture +end Ix.Kernel.MutualTreeFixture diff --git a/Ix/Tc/Verify/Inductive/MutualRecursor.lean b/Ix/Kernel/Verify/Inductive/MutualRecursor.lean similarity index 94% rename from Ix/Tc/Verify/Inductive/MutualRecursor.lean rename to Ix/Kernel/Verify/Inductive/MutualRecursor.lean index 3af3791c8..87edab147 100644 --- a/Ix/Tc/Verify/Inductive/MutualRecursor.lean +++ b/Ix/Kernel/Verify/Inductive/MutualRecursor.lean @@ -1,21 +1,21 @@ -import Ix.Tc.Verify.Check.PreTranslationCompatibility -import Ix.Tc.Verify.Inductive.BlockPatternSoundness -import Ix.Tc.Verify.Inductive.MutualFamily +import Ix.Kernel.Verify.Check.PreTranslationCompatibility +import Ix.Kernel.Verify.Inductive.BlockPatternSoundness +import Ix.Kernel.Verify.Inductive.MutualFamily /-! # Generated recursors of a certified mutual block This module contains the reusable, representation-neutral part of the second -physical block in Ix's mutual-inductive layout. Lean4Lean installs all family +physical block in Ix's mutual-inductive layout. Ix.Theory.Named installs all family recursors and globally flattened equations in the original atomic source transaction; Ix later checks a separately owned recursor block. The lemmas below retain the exact global generated-rule position while permitting each physical recursor to dispatch by its family-local constructor index. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VConstant VConstVal VDefEq VEnv VExpr VInductDecl) +open Ix.Theory.Named (VConstant VConstVal VDefEq VEnv VExpr VInductDecl) namespace CertifiedMutualGeneration @@ -51,11 +51,11 @@ namespace CertifiedMutualRecursor variable {source : VInductDecl} {before after : VEnv} -/-- Repackage Lean4Lean's exact generated RHS/check payload as the finite +/-- Repackage Ix.Theory.Named's exact generated RHS/check payload as the finite pattern record consumed by Ix. `ruleIndex` is family-local (the physical dispatch index); `index` remains the global flattened equation position. `argumentArity` is the one explicit representation equality connecting the -serialized parameter/field split to Lean4Lean's pattern arity. -/ +serialized parameter/field split to Ix.Theory.Named's pattern arity. -/ def generatedPattern (certificate : source.BlockCertificate before after) {index : Nat} {constructor : VInductDecl.NormalizedBlockCtor} @@ -181,4 +181,4 @@ theorem generatedPatternRel end CertifiedMutualRecursor -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/MutualRecursorAdmission.lean b/Ix/Kernel/Verify/Inductive/MutualRecursorAdmission.lean similarity index 92% rename from Ix/Tc/Verify/Inductive/MutualRecursorAdmission.lean rename to Ix/Kernel/Verify/Inductive/MutualRecursorAdmission.lean index 721ec6306..f2368aad5 100644 --- a/Ix/Tc/Verify/Inductive/MutualRecursorAdmission.lean +++ b/Ix/Kernel/Verify/Inductive/MutualRecursorAdmission.lean @@ -1,11 +1,11 @@ -import Ix.Tc.Verify.Inductive.MutualFamilyAdmission -import Ix.Tc.Verify.Inductive.MutualRecursor -import Ix.Tc.Verify.Upstream.Pending +import Ix.Kernel.Verify.Inductive.MutualFamilyAdmission +import Ix.Kernel.Verify.Inductive.MutualRecursor +import Ix.Kernel.Verify.Frontier.Pending /-! # Conditional atomic admission of the mutual `Tree`/`TreeList` recursors -The original Lean4Lean transaction has already installed both generated +The original Ix.Theory.Named transaction has already installed both generated recursors and all five globally flattened equations. This module proves the complete Ix-side correspondence for the separately owned two-member physical recursor block: exact family-local dispatch, global equation positions, @@ -13,27 +13,27 @@ structural translations, constructor metadata, ownership, and admission. The reversed-order semantic certificate and generated-pattern soundness are conditional on two fixture-specific Theory witnesses in -`Verify.Upstream.Pending`: family-permutation preservation of generation WF, +`Verify.Frontier.Pending`: family-permutation preservation of generation WF, and the certified rule-pattern conclusion. None of the Ix representation, ingress, ownership, or production-execution facts are assumed there. -/ -namespace Ix.Tc.MutualTreeFixture +namespace Ix.Kernel.MutualTreeFixture -open Lean4Lean -open Lean4Lean.MutualInductiveFixtures -open Lean4Lean.MutualInductiveReplayFixtures +open Ix.Theory.Named +open Ix.Theory.Named.MutualInductiveFixtures +open Ix.Theory.Named.MutualInductiveReplayFixtures open MutualTreeCertificateFixture /- The physical compiler canonically orders this SCC as `TreeList, Tree`. These private aliases keep every rule index below tied to the exact reversed -Lean4Lean descriptor certified in the quarantined upstream module. -/ +Ix.Theory.Named descriptor certified in the quarantined upstream module. -/ private abbrev treeGeneration := - Upstream.Pending.mutualTreePhysicalGeneration -private abbrev lean4leanCertificate := - Upstream.Pending.mutualTreePhysicalCertificate + Frontier.Pending.mutualTreePhysicalGeneration +private abbrev specificationCertificate := + Frontier.Pending.mutualTreePhysicalCertificate private abbrev treeFinalEnv := - Upstream.Pending.mutualTreePhysicalFinalEnv + Frontier.Pending.mutualTreePhysicalFinalEnv local instance anonKIdDecidableEq : DecidableEq (KId .anon) := fun left right => @@ -266,7 +266,7 @@ theorem recursorRepresentationFacts : RecursorRepresentationFacts := recursorRepresentationFactsNative private theorem certificateGeneration_eq : - lean4leanCertificate.generation = treeGeneration := rfl + specificationCertificate.generation = treeGeneration := rfl private theorem treeRecTypeRawNative : RawExprRel (uvars := treeRecConcrete.lvls.toNat) treeFinalEnv nameOf @@ -370,8 +370,8 @@ private theorem upgradeRule nameOf RawProjRel.none [] rule.rhs (treeGeneration.rule index constructor).rhs := by let pre := raw.toPreBinderCore_of_scoped binderCore hscoped hsize - have ruleWF := (lean4leanCertificate.recursorRuleFacts entry).wf - exact pre.upgradeBinderCoreOfWF lean4leanCertificate.afterWF + have ruleWF := (specificationCertificate.recursorRuleFacts entry).wf + exact pre.upgradeBinderCoreOfWF specificationCertificate.afterWF (Delta := []) (hDelta := trivial) binderCore ⟨_, ruleWF.2⟩ theorem leafRuleTyped : @@ -443,35 +443,35 @@ theorem treeListRecRaw : theorem treeRecLookup : treeFinalEnv.constants ``Tree.rec = some treeRecSource.toVConstant := by - have lookup := lean4leanCertificate.recursorLookup + have lookup := specificationCertificate.recursorLookup (List.mem_of_getElem? treeRecSourceAt) simpa [recursorRepresentationFacts.treeRecName] using lookup theorem treeListRecLookup : treeFinalEnv.constants ``TreeList.rec = some treeListRecSource.toVConstant := by - have lookup := lean4leanCertificate.recursorLookup + have lookup := specificationCertificate.recursorLookup (List.mem_of_getElem? treeListRecSourceAt) simpa [recursorRepresentationFacts.treeListRecName] using lookup theorem leafRegistered : RegisteredRecursorRuleRhsRel treeFinalEnv nameOf RawProjRel.none treeRecId treeRecConcrete leafRule (treeGeneration.rule 2 leafNormalized) := - CertifiedMutualRecursor.registeredRule lean4leanCertificate treeRecRaw + CertifiedMutualRecursor.registeredRule specificationCertificate treeRecRaw treeRecLookup recursorRepresentationFacts.leafRecursorName.symm leafEntry leafRuleRaw leafRuleTyped theorem nodeRegistered : RegisteredRecursorRuleRhsRel treeFinalEnv nameOf RawProjRel.none treeRecId treeRecConcrete nodeRule (treeGeneration.rule 3 nodeNormalized) := - CertifiedMutualRecursor.registeredRule lean4leanCertificate treeRecRaw + CertifiedMutualRecursor.registeredRule specificationCertificate treeRecRaw treeRecLookup recursorRepresentationFacts.nodeRecursorName.symm nodeEntry nodeRuleRaw nodeRuleTyped theorem branchRegistered : RegisteredRecursorRuleRhsRel treeFinalEnv nameOf RawProjRel.none treeRecId treeRecConcrete branchRule (treeGeneration.rule 4 branchNormalized) := - CertifiedMutualRecursor.registeredRule lean4leanCertificate treeRecRaw + CertifiedMutualRecursor.registeredRule specificationCertificate treeRecRaw treeRecLookup recursorRepresentationFacts.branchRecursorName.symm branchEntry branchRuleRaw branchRuleTyped @@ -479,7 +479,7 @@ theorem nilRegistered : RegisteredRecursorRuleRhsRel treeFinalEnv nameOf RawProjRel.none treeListRecId treeListRecConcrete nilRule (treeGeneration.rule 0 nilNormalized) := - CertifiedMutualRecursor.registeredRule lean4leanCertificate treeListRecRaw + CertifiedMutualRecursor.registeredRule specificationCertificate treeListRecRaw treeListRecLookup recursorRepresentationFacts.nilRecursorName.symm nilEntry nilRuleRaw nilRuleTyped @@ -487,34 +487,34 @@ theorem consRegistered : RegisteredRecursorRuleRhsRel treeFinalEnv nameOf RawProjRel.none treeListRecId treeListRecConcrete consRule (treeGeneration.rule 1 consNormalized) := - CertifiedMutualRecursor.registeredRule lean4leanCertificate treeListRecRaw + CertifiedMutualRecursor.registeredRule specificationCertificate treeListRecRaw treeListRecLookup recursorRepresentationFacts.consRecursorName.symm consEntry consRuleRaw consRuleTyped /-! ## Exact generated patterns -/ def leafPattern : RecursorRulePattern := - CertifiedMutualRecursor.generatedPattern lean4leanCertificate + CertifiedMutualRecursor.generatedPattern specificationCertificate leafEntry treeLeafId 0 1 1 recursorRepresentationFacts.leafArgumentArity def nodePattern : RecursorRulePattern := - CertifiedMutualRecursor.generatedPattern lean4leanCertificate + CertifiedMutualRecursor.generatedPattern specificationCertificate nodeEntry treeNodeId 1 1 1 recursorRepresentationFacts.nodeArgumentArity def branchPattern : RecursorRulePattern := - CertifiedMutualRecursor.generatedPattern lean4leanCertificate + CertifiedMutualRecursor.generatedPattern specificationCertificate branchEntry treeBranchId 2 1 1 recursorRepresentationFacts.branchArgumentArity def nilPattern : RecursorRulePattern := - CertifiedMutualRecursor.generatedPattern lean4leanCertificate + CertifiedMutualRecursor.generatedPattern specificationCertificate nilEntry treeListNilId 0 1 0 recursorRepresentationFacts.nilArgumentArity def consPattern : RecursorRulePattern := - CertifiedMutualRecursor.generatedPattern lean4leanCertificate + CertifiedMutualRecursor.generatedPattern specificationCertificate consEntry treeListConsId 1 1 2 recursorRepresentationFacts.consArgumentArity @@ -616,40 +616,40 @@ private theorem consPatternMetadata : theorem leafPatternRel : RawRecursorRulePatternRel treeFinalEnv catalog nameOf treeRecId treeRecConcrete leafRule leafPattern := - CertifiedMutualRecursor.generatedPatternRel lean4leanCertificate - Upstream.Pending.mutualTreePhysicalRulePatternSound + CertifiedMutualRecursor.generatedPatternRel specificationCertificate + Frontier.Pending.mutualTreePhysicalRulePatternSound leafEntry treeLeafId 0 1 1 recursorRepresentationFacts.leafArgumentArity leafPatternMetadata theorem nodePatternRel : RawRecursorRulePatternRel treeFinalEnv catalog nameOf treeRecId treeRecConcrete nodeRule nodePattern := - CertifiedMutualRecursor.generatedPatternRel lean4leanCertificate - Upstream.Pending.mutualTreePhysicalRulePatternSound + CertifiedMutualRecursor.generatedPatternRel specificationCertificate + Frontier.Pending.mutualTreePhysicalRulePatternSound nodeEntry treeNodeId 1 1 1 recursorRepresentationFacts.nodeArgumentArity nodePatternMetadata theorem branchPatternRel : RawRecursorRulePatternRel treeFinalEnv catalog nameOf treeRecId treeRecConcrete branchRule branchPattern := - CertifiedMutualRecursor.generatedPatternRel lean4leanCertificate - Upstream.Pending.mutualTreePhysicalRulePatternSound + CertifiedMutualRecursor.generatedPatternRel specificationCertificate + Frontier.Pending.mutualTreePhysicalRulePatternSound branchEntry treeBranchId 2 1 1 recursorRepresentationFacts.branchArgumentArity branchPatternMetadata theorem nilPatternRel : RawRecursorRulePatternRel treeFinalEnv catalog nameOf treeListRecId treeListRecConcrete nilRule nilPattern := - CertifiedMutualRecursor.generatedPatternRel lean4leanCertificate - Upstream.Pending.mutualTreePhysicalRulePatternSound + CertifiedMutualRecursor.generatedPatternRel specificationCertificate + Frontier.Pending.mutualTreePhysicalRulePatternSound nilEntry treeListNilId 0 1 0 recursorRepresentationFacts.nilArgumentArity nilPatternMetadata theorem consPatternRel : RawRecursorRulePatternRel treeFinalEnv catalog nameOf treeListRecId treeListRecConcrete consRule consPattern := - CertifiedMutualRecursor.generatedPatternRel lean4leanCertificate - Upstream.Pending.mutualTreePhysicalRulePatternSound + CertifiedMutualRecursor.generatedPatternRel specificationCertificate + Frontier.Pending.mutualTreePhysicalRulePatternSound consEntry treeListConsId 1 1 2 recursorRepresentationFacts.consArgumentArity consPatternMetadata @@ -657,11 +657,11 @@ theorem consPatternRel : /-- Historical adapter for the exact `TreeList, Tree` certificate. This is the same computed generation and post-environment exposed by the current -Lean4Lean consumer package in `Upstream.Pending`. -/ +Ix.Theory.Named consumer package in `Frontier.Pending`. -/ def physicalTransaction : CertifiedBlockGenerationTransaction - Upstream.Pending.mutualTreePhysicalDecl VEnv.empty treeFinalEnv where - certificate := Upstream.Pending.mutualTreePhysicalSemantic - success := Upstream.Pending.mutualTreePhysicalSuccess + Frontier.Pending.mutualTreePhysicalDecl VEnv.empty treeFinalEnv where + certificate := Frontier.Pending.mutualTreePhysicalSemantic + success := Frontier.Pending.mutualTreePhysicalSuccess beforeWF := ⟨[], .empty⟩ private theorem physicalTreeTypeRawNative : @@ -750,15 +750,15 @@ theorem physicalConsTypeRaw : structure PhysicalSourceMembershipFacts : Prop where leaf : treeLeafSource ∈ - Upstream.Pending.mutualTreePhysicalDecl.blockConstructorConstants + Frontier.Pending.mutualTreePhysicalDecl.blockConstructorConstants node : treeNodeSource ∈ - Upstream.Pending.mutualTreePhysicalDecl.blockConstructorConstants + Frontier.Pending.mutualTreePhysicalDecl.blockConstructorConstants branch : treeBranchSource ∈ - Upstream.Pending.mutualTreePhysicalDecl.blockConstructorConstants + Frontier.Pending.mutualTreePhysicalDecl.blockConstructorConstants nil : treeListNilSource ∈ - Upstream.Pending.mutualTreePhysicalDecl.blockConstructorConstants + Frontier.Pending.mutualTreePhysicalDecl.blockConstructorConstants cons : treeListConsSource ∈ - Upstream.Pending.mutualTreePhysicalDecl.blockConstructorConstants + Frontier.Pending.mutualTreePhysicalDecl.blockConstructorConstants private theorem physicalSourceMembershipFactsNative : PhysicalSourceMembershipFacts := by @@ -784,7 +784,7 @@ def physicalFamilyLink : nameOf_treeList, familyMemberShapeFacts.treeListUvars, physicalTreeListTypeRaw, .inl ⟨treeListType, by - simp [Upstream.Pending.mutualTreePhysicalDecl], rfl, rfl⟩⟩ + simp [Frontier.Pending.mutualTreePhysicalDecl], rfl, rfl⟩⟩ · exact ⟨treeListNilConcrete, ``TreeList.nil, treeListNilSource.toVConstant, catalog_nil, familyMemberShapeFacts.nilKind, nameOf_nil, @@ -801,7 +801,7 @@ def physicalFamilyLink : catalog_tree, familyMemberShapeFacts.treeKind, nameOf_tree, familyMemberShapeFacts.treeUvars, physicalTreeTypeRaw, .inl ⟨treeType, by - simp [Upstream.Pending.mutualTreePhysicalDecl], rfl, rfl⟩⟩ + simp [Frontier.Pending.mutualTreePhysicalDecl], rfl, rfl⟩⟩ · exact ⟨treeLeafConcrete, ``Tree.leaf, treeLeafSource.toVConstant, catalog_leaf, familyMemberShapeFacts.leafKind, nameOf_leaf, @@ -932,7 +932,7 @@ private theorem treeListRecPattern {index : Nat} {rule : RecRule .anon} private theorem treeRecSemanticEntry : TrustedCatalogEntry RawProjRel.none catalog nameOf treeFinalEnv treeRecId := .ambient catalog_treeRec treeRecRaw treeRecLookup - (lean4leanCertificate.afterWF.ordered.constWF treeRecLookup) + (specificationCertificate.afterWF.ordered.constWF treeRecLookup) (fun {_} hrule => treeRecRule hrule) (fun {_ _} hrule => treeRecPattern hrule) @@ -940,7 +940,7 @@ private theorem treeListRecSemanticEntry : TrustedCatalogEntry RawProjRel.none catalog nameOf treeFinalEnv treeListRecId := .ambient catalog_treeListRec treeListRecRaw treeListRecLookup - (lean4leanCertificate.afterWF.ordered.constWF treeListRecLookup) + (specificationCertificate.afterWF.ordered.constWF treeListRecLookup) (fun {_} hrule => treeListRecRule hrule) (fun {_ _} hrule => treeListRecPattern hrule) @@ -1025,7 +1025,7 @@ rule-pattern soundness. -/ structure MutualRecursorConditionalClosure : Prop where execution : EndToEndExecution family : MutualFamilyAtomicClosure - patternSound : CertifiedBlockRulePatternSound lean4leanCertificate + patternSound : CertifiedBlockRulePatternSound specificationCertificate physicalFamilyAdmission : AtomicBlockAdmission RawProjRel.none world physicalFamilyAcceptedWorld familyBlockId familyMembers .inductive' @@ -1039,7 +1039,7 @@ theorem mutualRecursorConditionalClosure : MutualRecursorConditionalClosure where execution := endToEndExecution family := mutualFamilyAtomicClosure - patternSound := Upstream.Pending.mutualTreePhysicalRulePatternSound + patternSound := Frontier.Pending.mutualTreePhysicalRulePatternSound physicalFamilyAdmission := physicalFamilyAtomicAdmission recursorAdmission := familyRecursorAtomicAdmission familyAccepted := @@ -1047,4 +1047,4 @@ theorem mutualRecursorConditionalClosure : familyRecursorAtomicAdmission.promotion.le recursorAccepted := familyRecursorBlockAccepted -end Ix.Tc.MutualTreeFixture +end Ix.Kernel.MutualTreeFixture diff --git a/Ix/Tc/Verify/Inductive/NestedAdmission.lean b/Ix/Kernel/Verify/Inductive/NestedAdmission.lean similarity index 96% rename from Ix/Tc/Verify/Inductive/NestedAdmission.lean rename to Ix/Kernel/Verify/Inductive/NestedAdmission.lean index d5e632520..db073465b 100644 --- a/Ix/Tc/Verify/Inductive/NestedAdmission.lean +++ b/Ix/Kernel/Verify/Inductive/NestedAdmission.lean @@ -1,19 +1,19 @@ -import Ix.Tc.Verify.Inductive.NestedSemanticTransaction +import Ix.Kernel.Verify.Inductive.NestedSemanticTransaction /-! # Atomic admission of the nested `LeanTree` source block -This module joins the concrete Ix block to the completed Lean4Lean nested +This module joins the concrete Ix block to the completed Ix.Theory.Named nested transaction. The physical block contains only the stored `Tree` family and -`node` constructor. Lean4Lean's transaction independently flattens the +`node` constructor. Ix.Theory.Named's transaction independently flattens the nested dependency, checks the restored constants/equations, removes every auxiliary name, and commits the source family, constructor, two recursors, and two rules at one public `addInductNested` boundary. -/ -namespace Ix.Tc.NestedRecursiveFixture +namespace Ix.Kernel.NestedRecursiveFixture -open Lean4Lean +open Ix.Theory.Named open InductiveConcreteFixture local instance nestedAdmissionAddressDecidableEq : DecidableEq Address := @@ -322,10 +322,10 @@ structure NestedSemanticTransactionClosure : Prop where groups rootGroup.addrs #[treeId.addr] checkerMethods nestedWhnfAfter positivityAfter flatNodeValidation : - Lean4Lean.AddInductive.checkConstructorType leanFlatStats false 0 + Ix.Theory.Named.AddInductive.checkConstructorType leanFlatStats false 0 leanFlatNode.name leanFlatNode.type leanFlatConstructorContext = .ok () flatWrapValidation : - Lean4Lean.AddInductive.checkConstructorType leanFlatStats false 1 + Ix.Theory.Named.AddInductive.checkConstructorType leanFlatStats false 1 leanFlatWrap.name leanFlatWrap.type leanFlatConstructorContext = .ok () semantic : SemanticTreeTransactionFacts ruleMetadata : @@ -352,4 +352,4 @@ theorem nestedSemanticTransactionClosure : admission := nestedFamilyAtomicAdmission accepted := nestedFamilyBlockAccepted -end Ix.Tc.NestedRecursiveFixture +end Ix.Kernel.NestedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/NestedAuxiliaryExpansion.lean b/Ix/Kernel/Verify/Inductive/NestedAuxiliaryExpansion.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/NestedAuxiliaryExpansion.lean rename to Ix/Kernel/Verify/Inductive/NestedAuxiliaryExpansion.lean index e45a41f02..419c7a945 100644 --- a/Ix/Tc/Verify/Inductive/NestedAuxiliaryExpansion.lean +++ b/Ix/Kernel/Verify/Inductive/NestedAuxiliaryExpansion.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Inductive.NestedPositivityTraversal +import Ix.Kernel.Verify.Inductive.NestedPositivityTraversal /-! # Nested positivity requests and flat-block auxiliary expansion @@ -18,7 +18,7 @@ small representation bridge that later block traversal must derive from the two executions of the same loaded header. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Equality lawfulness is needed only for the physical `auxSeen` membership proofs in this module. Keep it local so the broader positivity traversal @@ -1359,4 +1359,4 @@ theorem CompleteNestedPositivityApplicationTrace.producedRequest simpa only [NestedPositivityAuxiliaryRequest.ProducedBy] using trace.auxiliaryRequest -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/NestedAuxiliaryPositivity.lean b/Ix/Kernel/Verify/Inductive/NestedAuxiliaryPositivity.lean similarity index 93% rename from Ix/Tc/Verify/Inductive/NestedAuxiliaryPositivity.lean rename to Ix/Kernel/Verify/Inductive/NestedAuxiliaryPositivity.lean index 34de2e3a9..1157bc3ec 100644 --- a/Ix/Tc/Verify/Inductive/NestedAuxiliaryPositivity.lean +++ b/Ix/Kernel/Verify/Inductive/NestedAuxiliaryPositivity.lean @@ -1,18 +1,18 @@ -import Ix.Tc.Verify.Inductive.NestedPositivityTransport +import Ix.Kernel.Verify.Inductive.NestedPositivityTransport /-! # Positivity of a generated nested auxiliary The outer `Tree.node` field is checked by Ix as the nested application -`Box Tree`, while Lean4Lean rewrites that application to a generated flat +`Box Tree`, while Ix.Theory.Named rewrites that application to a generated flat family. This module follows the other half of that transformation: the production nested traversal copies `Box.wrap`, strips its `Box` parameter, substitutes `Tree`, and checks the resulting `Tree` field. The retained -execution is transported to the direct recursive field of Lean4Lean's copied +execution is transported to the direct recursive field of Ix.Theory.Named's copied auxiliary constructor. -/ -namespace Ix.Tc.NestedRecursiveFixture +namespace Ix.Kernel.NestedRecursiveFixture local instance auxiliaryAddressDecidableEq : DecidableEq Address := AnonStructural.addressDecidableEq @@ -421,17 +421,17 @@ theorem nestedAuxiliaryFieldProductionTrace : private theorem leanTreeCandidateWhnfNative : ExactLeanSyntax.exceptExprCheck - (Lean4Lean.TypeChecker.M.run leanFlatConstructorContext.env + (Ix.Theory.Named.TypeChecker.M.run leanFlatConstructorContext.env leanFlatConstructorContext.safety leanFlatConstructorContext.lctx leanFlatConstructorContext.lparams leanFlatConstructorContext.fuel - (Lean4Lean.TypeChecker.whnf leanTreeExpr)) + (Ix.Theory.Named.TypeChecker.whnf leanTreeExpr)) leanTreeExpr = true := by native_decide theorem leanTreeCandidateWhnf : - Lean4Lean.AddInductive.CandidateWhnfStep.Valid + Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid ⟨leanFlatConstructorContext, leanTreeExpr, leanTreeExpr⟩ := by - unfold Lean4Lean.AddInductive.CandidateWhnfStep.Valid + unfold Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid exact ExactLeanSyntax.exceptExpr_eq_ok_of_check leanTreeCandidateWhnfNative @@ -440,14 +440,14 @@ constructor. Its state is the state selected by production after reducing the copied constructor telescope, rather than a separately initialized fixture state. -/ inductive NestedAuxiliaryPositivitySourceRel : - TcState .anon → Lean4Lean.AddInductive.Context → + TcState .anon → Ix.Theory.Named.AddInductive.Context → KExpr .anon → Lean.Expr → Prop | domain : NestedAuxiliaryPositivitySourceRel auxiliaryFieldWhnfAfter leanFlatConstructorContext treeExpr leanTreeExpr /-- Exact post-WHNF syntax relation for the recursive `Tree` field. -/ inductive NestedAuxiliaryPositivityResultRel : - TcState .anon → Lean4Lean.AddInductive.Context → + TcState .anon → Ix.Theory.Named.AddInductive.Context → KExpr .anon → Lean.Expr → Prop | domain {ixResult : KExpr .anon} (candidate : CandidateSyntaxRel nestedCandidateNameOf @@ -460,15 +460,15 @@ inductive NestedAuxiliaryPositivityResultRel : private theorem nestedAuxiliaryRootFree {ixState : TcState .anon} - {leanContext : Lean4Lean.AddInductive.Context} + {leanContext : Ix.Theory.Named.AddInductive.Context} {ixSource : KExpr .anon} {leanSource : Lean.Expr} (relation : NestedAuxiliaryPositivitySourceRel ixState leanContext ixSource leanSource) (free : exprMentionsAnyAddr ixSource #[treeId.addr] = false) : ∃ leanResult, - Lean4Lean.AddInductive.CandidateWhnfStep.Valid + Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid ⟨leanContext, leanSource, leanResult⟩ ∧ - Lean4Lean.AddInductive.hasIndOcc leanFlatStats.indConsts leanResult = + Ix.Theory.Named.AddInductive.hasIndOcc leanFlatStats.indConsts leanResult = false := by cases relation rw [auxiliaryTreeMentionsRootNative] at free @@ -477,7 +477,7 @@ private theorem nestedAuxiliaryRootFree set_option maxRecDepth 100000 in private theorem nestedAuxiliaryWhnf {ixBefore ixAfter : TcState .anon} - {leanContext : Lean4Lean.AddInductive.Context} + {leanContext : Ix.Theory.Named.AddInductive.Context} {ixSource ixResult : KExpr .anon} {leanSource : Lean.Expr} (relation : NestedAuxiliaryPositivitySourceRel ixBefore leanContext ixSource leanSource) @@ -485,7 +485,7 @@ private theorem nestedAuxiliaryWhnf (run : (RecM.whnf ixSource).run checkerMethods ixBefore = .ok ixResult ixAfter) : ∃ leanResult, - Lean4Lean.AddInductive.CandidateWhnfStep.Valid + Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid ⟨leanContext, leanSource, leanResult⟩ ∧ NestedAuxiliaryPositivityResultRel ixAfter leanContext ixResult leanResult := by @@ -500,13 +500,13 @@ private theorem nestedAuxiliaryMentions (relation : NestedAuxiliaryPositivitySourceRel ixState leanContext ixExpr leanExpr) : exprMentionsAnyAddr ixExpr #[treeId.addr] = - Lean4Lean.AddInductive.hasIndOcc leanFlatStats.indConsts leanExpr := by + Ix.Theory.Named.AddInductive.hasIndOcc leanFlatStats.indConsts leanExpr := by cases relation rw [auxiliaryTreeMentionsRootNative, leanTreeOccurs] private theorem nestedAuxiliaryForall {ixState : TcState .anon} - {leanContext : Lean4Lean.AddInductive.Context} + {leanContext : Ix.Theory.Named.AddInductive.Context} {ixName : Mode.anon.F Name} {ixBinder : Mode.anon.F Lean.BinderInfo} {ixDomain ixBody : KExpr .anon} {ixInfo : ExprInfo .anon} {leanExpr : Lean.Expr} @@ -522,14 +522,14 @@ private theorem nestedAuxiliaryForall .ok (ixOpen, ixFVar) ixAfterOpen → NestedAuxiliaryPositivitySourceRel ixAfterOpen (leanContext.pushLocalDecl leanName leanBinder - (Lean4Lean.AddInductive.consumeTypeAnnotations leanDomain)) + (Ix.Theory.Named.AddInductive.consumeTypeAnnotations leanDomain)) ixOpen (leanBody.instantiate1 leanContext.freshExpr) := by cases relation with | domain candidate => cases candidate private theorem nestedAuxiliaryDirect {ixState : TcState .anon} - {leanContext : Lean4Lean.AddInductive.Context} + {leanContext : Ix.Theory.Named.AddInductive.Context} {ixResult : KExpr .anon} {leanResult : Lean.Expr} {id : KId .anon} {us : Array (KUniv .anon)} {info : ExprInfo .anon} {args : Array (KExpr .anon)} @@ -542,10 +542,10 @@ private theorem nestedAuxiliaryDirect (_valid : ValidPositiveRecursiveApplication id us args traceGroups #[treeId.addr] checkerMethods ixState final) : ∃ targetIdx, - Lean4Lean.AddInductive.hasIndOcc leanFlatStats.indConsts leanResult = + Ix.Theory.Named.AddInductive.hasIndOcc leanFlatStats.indConsts leanResult = true ∧ leanResult.isForall = false ∧ - Lean4Lean.AddInductive.isValidIndApp? leanFlatStats leanResult = + Ix.Theory.Named.AddInductive.isValidIndApp? leanFlatStats leanResult = some targetIdx := by cases relation exact ⟨0, leanTreeOccurs, rfl, leanTreeTarget⟩ @@ -592,9 +592,9 @@ theorem nestedAuxiliaryFieldProductionTraceAt (fuel : Nat) : active valid⟩ /-- The production-derived direct field trace transported at any positive -Lean4Lean positivity fuel. -/ +Ix.Theory.Named positivity fuel. -/ theorem nestedAuxiliaryConstructorPositivityTraceAt (fuel : Nat) : - Nonempty (Lean4Lean.AddInductive.ConstructorPositivityTrace leanFlatStats + Nonempty (Ix.Theory.Named.AddInductive.ConstructorPositivityTrace leanFlatStats leanFlatWrap.name 0 leanFlatConstructorContext leanTreeExpr (fuel + 1)) := by rcases nestedAuxiliaryFieldProductionTraceAt fuel with @@ -603,9 +603,9 @@ theorem nestedAuxiliaryConstructorPositivityTraceAt (fuel : Nat) : nestedAuxiliaryPositivityTransport trace root .domain /-- The inner call retained from production's nested traversal constructs the -exact Lean4Lean positivity trace for the copied auxiliary field. -/ +exact Ix.Theory.Named positivity trace for the copied auxiliary field. -/ theorem nestedAuxiliaryConstructorPositivityTrace : - Nonempty (Lean4Lean.AddInductive.ConstructorPositivityTrace leanFlatStats + Nonempty (Ix.Theory.Named.AddInductive.ConstructorPositivityTrace leanFlatStats leanFlatWrap.name 0 leanFlatConstructorContext leanTreeExpr (positivityFuel - 3)) := by rcases nestedAuxiliaryFieldProductionTrace with @@ -613,4 +613,4 @@ theorem nestedAuxiliaryConstructorPositivityTrace : exact FlatPositivityTraceTransport.constructorPositivityTrace nestedAuxiliaryPositivityTransport trace root .domain -end Ix.Tc.NestedRecursiveFixture +end Ix.Kernel.NestedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/NestedBlockCertificate.lean b/Ix/Kernel/Verify/Inductive/NestedBlockCertificate.lean similarity index 95% rename from Ix/Tc/Verify/Inductive/NestedBlockCertificate.lean rename to Ix/Kernel/Verify/Inductive/NestedBlockCertificate.lean index dd24863b7..0de4f579f 100644 --- a/Ix/Tc/Verify/Inductive/NestedBlockCertificate.lean +++ b/Ix/Kernel/Verify/Inductive/NestedBlockCertificate.lean @@ -1,10 +1,10 @@ -import Ix.Tc.Verify.Check.BlockAcceptance -import Lean4Lean.Theory.Typing.InductiveCertificate +import Ix.Kernel.Verify.Check.BlockAcceptance +import Ix.Theory.Named.Typing.InductiveCertificate /-! # Certified nested-block transactions -This is the Ix consumer boundary for Lean4Lean's `NestedBlockCertificate`. +This is the Ix consumer boundary for Ix.Theory.Named's `NestedBlockCertificate`. The Theory transaction stores the original source families and constructors, then restores every generated recursor and rule before committing them. The Ix-facing link below covers only the physical source block; generated @@ -15,9 +15,9 @@ after an exhaustive physical catalog link and an `ExactCheckBlock` prove that the one nested transaction describes the complete checked block. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VConstant VConstVal VEnv VInductDecl) +open Ix.Theory.Named (VConstant VConstVal VEnv VInductDecl) /-- Exact concrete kinds permitted in the stored source half of a nested transaction. -/ @@ -143,4 +143,4 @@ theorem transition {trProj : RawProjRel} {world : VerifyWorld} end NestedFamilyCatalogLink -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/NestedCandidateSyntax.lean b/Ix/Kernel/Verify/Inductive/NestedCandidateSyntax.lean similarity index 85% rename from Ix/Tc/Verify/Inductive/NestedCandidateSyntax.lean rename to Ix/Kernel/Verify/Inductive/NestedCandidateSyntax.lean index b787181b6..ca7e07bdf 100644 --- a/Ix/Tc/Verify/Inductive/NestedCandidateSyntax.lean +++ b/Ix/Kernel/Verify/Inductive/NestedCandidateSyntax.lean @@ -1,27 +1,27 @@ -import Ix.Tc.Verify.Inductive.ExactLeanSyntax -import Ix.Tc.Verify.Inductive.NestedRecursiveFixture -import Lean4Lean.Verify.Environment.InductiveFixtures +import Ix.Kernel.Verify.Inductive.ExactLeanSyntax +import Ix.Kernel.Verify.Inductive.NestedRecursiveFixture +import Ix.Theory.Named.Verify.Environment.InductiveFixtures /-! -# Lean4Lean candidate produced by nested elimination +# Ix.Theory.Named candidate produced by nested elimination The production Ix fixture reaches `Box Tree` and records an auxiliary flat member whose physical identity remains the external `Box` address plus its -exact specialization key. Lean4Lean represents the same operation +exact specialization key. Ix.Theory.Named represents the same operation differently: `ElimNestedInductive.run` creates a fresh family constant and rewrites both the outer constructor and the copied external constructor to refer to that constant. -This module executes that real Lean4Lean transformation for the same +This module executes that real Ix.Theory.Named transformation for the same monomorphic `Box`/`Tree` shape and retains the exact flattened syntax. The fresh auxiliary name is therefore an output of the producer, not a name chosen by the transport proof. -/ -namespace Ix.Tc.NestedRecursiveFixture +namespace Ix.Kernel.NestedRecursiveFixture open Lean Meta Elab Term -open Lean4Lean.InductiveReplayFixtures +open Ix.Theory.Named.InductiveReplayFixtures /-! ## Previously declared external family -/ @@ -50,8 +50,8 @@ def leanNestedBaseEnv : Lean.Kernel.Environment := /-! ## Unflattened source declaration -/ -def leanTreeName : Lean.Name := `Ix.Tc.NestedRecursiveFixture.LeanTree -def leanNodeName : Lean.Name := `Ix.Tc.NestedRecursiveFixture.LeanTree.node +def leanTreeName : Lean.Name := `Ix.Kernel.NestedRecursiveFixture.LeanTree +def leanNodeName : Lean.Name := `Ix.Kernel.NestedRecursiveFixture.LeanTree.node def leanTreeExpr : Lean.Expr := .const leanTreeName [] def leanNestedDomain : Lean.Expr := .app (.const ``LeanBox []) leanTreeExpr @@ -63,7 +63,7 @@ def leanTreeSource : Lean.InductiveType := type := .forallE `value leanNestedDomain leanTreeExpr .default }] } def leanNestedEliminationOutcome := - (Lean4Lean.ElimNestedInductive.run 1000 0 [leanTreeSource] + (Ix.Theory.Named.ElimNestedInductive.run 1000 0 [leanTreeSource] leanNestedBaseEnv).run' { lvls := [], newTypes := #[leanTreeSource] } @@ -196,7 +196,7 @@ theorem leanAuxiliarySource : /-! ## Constructor-validation candidate -/ /-- Exact statistics of the two-member flattened mutual block. -/ -def leanFlatStats : Lean4Lean.AddInductive.InductiveStats where +def leanFlatStats : Ix.Theory.Named.AddInductive.InductiveStats where levels := [] resultLevel := .succ .zero nindices := #[0, 0] @@ -204,16 +204,16 @@ def leanFlatStats : Lean4Lean.AddInductive.InductiveStats where params := #[] isNotZero := true -def leanFlatFamilyContext : Lean4Lean.AddInductive.Context where +def leanFlatFamilyContext : Ix.Theory.Named.AddInductive.Context where env := leanNestedBaseEnv lparams := [] safety := .safe allowPrimitive := false - fuel := { ({} : Lean4Lean.FuelConfig) with + fuel := { ({} : Ix.Theory.Named.FuelConfig) with inductiveFuel := positivityFuel } def leanFlatDeclarationOutcome := - Lean4Lean.AddInductive.declareInductiveTypes leanFlatStats 0 + Ix.Theory.Named.AddInductive.declareInductiveTypes leanFlatStats 0 leanFlatTypes.toArray 1 false leanFlatFamilyContext def leanFlatConstructorEnv : Lean.Kernel.Environment := @@ -221,7 +221,7 @@ def leanFlatConstructorEnv : Lean.Kernel.Environment := | .ok environment => environment | .error _ => leanNestedBaseEnv -def leanFlatConstructorContext : Lean4Lean.AddInductive.Context := +def leanFlatConstructorContext : Ix.Theory.Named.AddInductive.Context := { leanFlatFamilyContext with env := leanFlatConstructorEnv } private theorem leanFlatDeclarationSucceededNative : @@ -240,39 +240,39 @@ theorem leanFlatDeclarationRun : cases outcome <;> simp_all private theorem leanTreeTargetNative : - Lean4Lean.AddInductive.isValidIndApp? leanFlatStats leanTreeExpr = + Ix.Theory.Named.AddInductive.isValidIndApp? leanFlatStats leanTreeExpr = some 0 := by native_decide theorem leanTreeTarget : - Lean4Lean.AddInductive.isValidIndApp? leanFlatStats leanTreeExpr = + Ix.Theory.Named.AddInductive.isValidIndApp? leanFlatStats leanTreeExpr = some 0 := leanTreeTargetNative private theorem leanAuxiliaryTargetNative : - Lean4Lean.AddInductive.isValidIndApp? leanFlatStats leanAuxiliaryExpr = + Ix.Theory.Named.AddInductive.isValidIndApp? leanFlatStats leanAuxiliaryExpr = some 1 := by native_decide theorem leanAuxiliaryTarget : - Lean4Lean.AddInductive.isValidIndApp? leanFlatStats leanAuxiliaryExpr = + Ix.Theory.Named.AddInductive.isValidIndApp? leanFlatStats leanAuxiliaryExpr = some 1 := leanAuxiliaryTargetNative private theorem leanTreeOccursNative : - Lean4Lean.AddInductive.hasIndOcc leanFlatStats.indConsts leanTreeExpr = + Ix.Theory.Named.AddInductive.hasIndOcc leanFlatStats.indConsts leanTreeExpr = true := by native_decide theorem leanTreeOccurs : - Lean4Lean.AddInductive.hasIndOcc leanFlatStats.indConsts leanTreeExpr = + Ix.Theory.Named.AddInductive.hasIndOcc leanFlatStats.indConsts leanTreeExpr = true := leanTreeOccursNative private theorem leanAuxiliaryOccursNative : - Lean4Lean.AddInductive.hasIndOcc leanFlatStats.indConsts + Ix.Theory.Named.AddInductive.hasIndOcc leanFlatStats.indConsts leanAuxiliaryExpr = true := by native_decide theorem leanAuxiliaryOccurs : - Lean4Lean.AddInductive.hasIndOcc leanFlatStats.indConsts + Ix.Theory.Named.AddInductive.hasIndOcc leanFlatStats.indConsts leanAuxiliaryExpr = true := leanAuxiliaryOccursNative /-! ## Exact cross-representation target certificate -/ @@ -289,7 +289,7 @@ private theorem nestedDomainCandidateCheckNative : nestedDomain leanNestedDomain = true := by native_decide -/-- Before flattening, the actual ingressed Ix domain and Lean4Lean's nested +/-- Before flattening, the actual ingressed Ix domain and Ix.Theory.Named's nested source have the same constant/application syntax. -/ theorem nestedDomainCandidateSyntax : CandidateSyntaxRel nestedCandidateNameOf @@ -315,11 +315,11 @@ theorem treeCandidateSyntax : treeExpr leanTreeExpr := CandidateSyntax.rel_of_check treeCandidateCheckNative -/-- Evidence that the target accepted by Lean4Lean is exactly the auxiliary +/-- Evidence that the target accepted by Ix.Theory.Named is exactly the auxiliary requested and retained by production Ix positivity/flat-block construction. The certificate keeps both representations visible: candidate syntax first -identifies `Box Tree`, the Lean4Lean eliminator's reverse map identifies the +identifies `Box Tree`, the Ix.Theory.Named eliminator's reverse map identifies the fresh target with that source, and the Ix request/header/key facts identify the physical flat member with the same specialization. -/ structure NestedAuxiliaryCandidateTarget : Prop where @@ -347,9 +347,9 @@ structure NestedAuxiliaryCandidateTarget : Prop where leanAuxiliarySource? leanAuxiliaryName = some leanNestedDomain outerRewrite : leanFlatNode.type = .forallE `value leanAuxiliaryExpr leanTreeExpr .default - occurs : Lean4Lean.AddInductive.hasIndOcc leanFlatStats.indConsts + occurs : Ix.Theory.Named.AddInductive.hasIndOcc leanFlatStats.indConsts leanAuxiliaryExpr = true - valid : Lean4Lean.AddInductive.isValidIndApp? leanFlatStats + valid : Ix.Theory.Named.AddInductive.isValidIndApp? leanFlatStats leanAuxiliaryExpr = some 1 /-- The concrete nested fixture closes every field of the cross-representation @@ -370,4 +370,4 @@ theorem nestedAuxiliaryCandidateTarget : NestedAuxiliaryCandidateTarget := by occurs := leanAuxiliaryOccurs valid := leanAuxiliaryTarget } -end Ix.Tc.NestedRecursiveFixture +end Ix.Kernel.NestedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/NestedConstructorValidation.lean b/Ix/Kernel/Verify/Inductive/NestedConstructorValidation.lean similarity index 78% rename from Ix/Tc/Verify/Inductive/NestedConstructorValidation.lean rename to Ix/Kernel/Verify/Inductive/NestedConstructorValidation.lean index 64737cc85..56e4104ea 100644 --- a/Ix/Tc/Verify/Inductive/NestedConstructorValidation.lean +++ b/Ix/Kernel/Verify/Inductive/NestedConstructorValidation.lean @@ -1,33 +1,33 @@ -import Ix.Tc.Verify.Inductive.NestedAuxiliaryPositivity +import Ix.Kernel.Verify.Inductive.NestedAuxiliaryPositivity /-! # Constructor validation for the flattened nested fixture This module places both production-derived positivity traces into -Lean4Lean's exact constructor validator. The outer `Tree.node` field uses the +Ix.Theory.Named's exact constructor validator. The outer `Tree.node` field uses the nested-production transport, while the generated `Box.wrap` field uses the retained copied-constructor traversal. Both are checked in the same two-family environment produced by the real nested eliminator. -/ -namespace Ix.Tc.NestedRecursiveFixture +namespace Ix.Kernel.NestedRecursiveFixture private abbrev NodeValidationTrace - (context : Lean4Lean.AddInductive.Context) (source : Lean.Expr) + (context : Ix.Theory.Named.AddInductive.Context) (source : Lean.Expr) (argIdx fuel : Nat) := - Lean4Lean.AddInductive.ConstructorTypeValidationTrace leanFlatStats false + Ix.Theory.Named.AddInductive.ConstructorTypeValidationTrace leanFlatStats false 0 leanFlatNode.name context source argIdx fuel private abbrev WrapValidationTrace - (context : Lean4Lean.AddInductive.Context) (source : Lean.Expr) + (context : Ix.Theory.Named.AddInductive.Context) (source : Lean.Expr) (argIdx fuel : Nat) := - Lean4Lean.AddInductive.ConstructorTypeValidationTrace leanFlatStats false + Ix.Theory.Named.AddInductive.ConstructorTypeValidationTrace leanFlatStats false 1 leanFlatWrap.name context source argIdx fuel -def leanFlatNodeFieldContext : Lean4Lean.AddInductive.Context := +def leanFlatNodeFieldContext : Ix.Theory.Named.AddInductive.Context := leanFlatConstructorContext.pushLocalDecl `value .default leanAuxiliaryExpr -def leanFlatWrapFieldContext : Lean4Lean.AddInductive.Context := +def leanFlatWrapFieldContext : Ix.Theory.Named.AddInductive.Context := leanFlatConstructorContext.pushLocalDecl leanFlatWrapBinderName .default leanTreeExpr @@ -35,71 +35,71 @@ def leanFlatWrapFieldContext : Lean4Lean.AddInductive.Context := private theorem leanAuxiliaryEnsureTypeNative : ExactLeanSyntax.exceptExprCheck - (Lean4Lean.TypeChecker.M.run leanFlatConstructorContext.env + (Ix.Theory.Named.TypeChecker.M.run leanFlatConstructorContext.env leanFlatConstructorContext.safety leanFlatConstructorContext.lctx leanFlatConstructorContext.lparams leanFlatConstructorContext.fuel - (Lean4Lean.TypeChecker.ensureType leanAuxiliaryExpr)) + (Ix.Theory.Named.TypeChecker.ensureType leanAuxiliaryExpr)) (.sort (.succ .zero)) = true := by native_decide private theorem leanAuxiliaryEnsureType : - Lean4Lean.AddInductive.ConstructorEnsureTypeStep.Valid + Ix.Theory.Named.AddInductive.ConstructorEnsureTypeStep.Valid ⟨leanFlatConstructorContext, leanAuxiliaryExpr, .sort (.succ .zero)⟩ := by - unfold Lean4Lean.AddInductive.ConstructorEnsureTypeStep.Valid + unfold Ix.Theory.Named.AddInductive.ConstructorEnsureTypeStep.Valid exact ExactLeanSyntax.exceptExpr_eq_ok_of_check leanAuxiliaryEnsureTypeNative private theorem leanTreeEnsureTypeNative : ExactLeanSyntax.exceptExprCheck - (Lean4Lean.TypeChecker.M.run leanFlatConstructorContext.env + (Ix.Theory.Named.TypeChecker.M.run leanFlatConstructorContext.env leanFlatConstructorContext.safety leanFlatConstructorContext.lctx leanFlatConstructorContext.lparams leanFlatConstructorContext.fuel - (Lean4Lean.TypeChecker.ensureType leanTreeExpr)) + (Ix.Theory.Named.TypeChecker.ensureType leanTreeExpr)) (.sort (.succ .zero)) = true := by native_decide private theorem leanTreeEnsureType : - Lean4Lean.AddInductive.ConstructorEnsureTypeStep.Valid + Ix.Theory.Named.AddInductive.ConstructorEnsureTypeStep.Valid ⟨leanFlatConstructorContext, leanTreeExpr, .sort (.succ .zero)⟩ := by - unfold Lean4Lean.AddInductive.ConstructorEnsureTypeStep.Valid + unfold Ix.Theory.Named.AddInductive.ConstructorEnsureTypeStep.Valid exact ExactLeanSyntax.exceptExpr_eq_ok_of_check leanTreeEnsureTypeNative private theorem leanFlatFieldUniverse : - Lean4Lean.AddInductive.levelStructGe leanFlatStats.resultLevel + Ix.Theory.Named.AddInductive.levelStructGe leanFlatStats.resultLevel (.succ .zero) = true := by native_decide private theorem leanTreeTerminalNative : - Lean4Lean.AddInductive.isValidIndAppIdx leanFlatStats leanTreeExpr 0 = + Ix.Theory.Named.AddInductive.isValidIndAppIdx leanFlatStats leanTreeExpr 0 = true := by native_decide private theorem leanAuxiliaryTerminalNative : - Lean4Lean.AddInductive.isValidIndAppIdx leanFlatStats + Ix.Theory.Named.AddInductive.isValidIndAppIdx leanFlatStats leanAuxiliaryExpr 1 = true := by native_decide private theorem consumeLeanAuxiliaryNative : ExactLeanSyntax.exprCheck - (Lean4Lean.AddInductive.consumeTypeAnnotations leanAuxiliaryExpr) + (Ix.Theory.Named.AddInductive.consumeTypeAnnotations leanAuxiliaryExpr) leanAuxiliaryExpr = true := by native_decide private theorem consumeLeanAuxiliary : - Lean4Lean.AddInductive.consumeTypeAnnotations leanAuxiliaryExpr = + Ix.Theory.Named.AddInductive.consumeTypeAnnotations leanAuxiliaryExpr = leanAuxiliaryExpr := ExactLeanSyntax.expr_eq_of_check consumeLeanAuxiliaryNative private theorem consumeLeanTreeNative : ExactLeanSyntax.exprCheck - (Lean4Lean.AddInductive.consumeTypeAnnotations leanTreeExpr) + (Ix.Theory.Named.AddInductive.consumeTypeAnnotations leanTreeExpr) leanTreeExpr = true := by native_decide private theorem consumeLeanTree : - Lean4Lean.AddInductive.consumeTypeAnnotations leanTreeExpr = + Ix.Theory.Named.AddInductive.consumeTypeAnnotations leanTreeExpr = leanTreeExpr := ExactLeanSyntax.expr_eq_of_check consumeLeanTreeNative @@ -160,10 +160,10 @@ theorem leanFlatNodeConstructorTypeValidationTrace : rw [consumeLeanAuxiliary, instantiateLeanTree] simpa [leanFlatNodeFieldContext, positivityFuel] using terminalTrace -/-- The assembled outer trace replays Lean4Lean's public constructor +/-- The assembled outer trace replays Ix.Theory.Named's public constructor validator. -/ theorem leanFlatNodeConstructorValidationRun : - Lean4Lean.AddInductive.checkConstructorType leanFlatStats false 0 + Ix.Theory.Named.AddInductive.checkConstructorType leanFlatStats false 0 leanFlatNode.name leanFlatNode.type leanFlatConstructorContext = .ok () := by obtain ⟨trace⟩ := leanFlatNodeConstructorTypeValidationTrace @@ -206,10 +206,10 @@ theorem leanFlatWrapConstructorTypeValidationTrace : /-- The generated auxiliary also passes the public constructor validator from the production-derived positivity evidence. -/ theorem leanFlatWrapConstructorValidationRun : - Lean4Lean.AddInductive.checkConstructorType leanFlatStats false 1 + Ix.Theory.Named.AddInductive.checkConstructorType leanFlatStats false 1 leanFlatWrap.name leanFlatWrap.type leanFlatConstructorContext = .ok () := by obtain ⟨trace⟩ := leanFlatWrapConstructorTypeValidationTrace exact trace.check_run -end Ix.Tc.NestedRecursiveFixture +end Ix.Kernel.NestedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/NestedPositivityTransport.lean b/Ix/Kernel/Verify/Inductive/NestedPositivityTransport.lean similarity index 84% rename from Ix/Tc/Verify/Inductive/NestedPositivityTransport.lean rename to Ix/Kernel/Verify/Inductive/NestedPositivityTransport.lean index 9c64dda7b..4428fe988 100644 --- a/Ix/Tc/Verify/Inductive/NestedPositivityTransport.lean +++ b/Ix/Kernel/Verify/Inductive/NestedPositivityTransport.lean @@ -1,11 +1,11 @@ -import Ix.Tc.Verify.Inductive.NestedCandidateSyntax +import Ix.Kernel.Verify.Inductive.NestedCandidateSyntax /-! # Transporting the concrete nested positivity branch This module instantiates `FlattenedPositivityTraceTransport` for the outer `Tree.node : Box Tree → Tree` field. Ix validates the pre-flattening -`Box Tree` application by recursively traversing `Box.wrap`; Lean4Lean +`Box Tree` application by recursively traversing `Box.wrap`; Ix.Theory.Named validates the post-flattening generated auxiliary as a direct member of the two-family mutual block. @@ -15,24 +15,24 @@ complete branch trace and then consumes the audited cross-representation target certificate from `NestedCandidateSyntax`. -/ -namespace Ix.Tc.NestedRecursiveFixture +namespace Ix.Kernel.NestedRecursiveFixture /-! ## Exact candidate WHNF -/ private theorem leanAuxiliaryCandidateWhnfNative : ExactLeanSyntax.exceptExprCheck - (Lean4Lean.TypeChecker.M.run leanFlatConstructorContext.env + (Ix.Theory.Named.TypeChecker.M.run leanFlatConstructorContext.env leanFlatConstructorContext.safety leanFlatConstructorContext.lctx leanFlatConstructorContext.lparams leanFlatConstructorContext.fuel - (Lean4Lean.TypeChecker.whnf leanAuxiliaryExpr)) + (Ix.Theory.Named.TypeChecker.whnf leanAuxiliaryExpr)) leanAuxiliaryExpr = true := by native_decide theorem leanAuxiliaryCandidateWhnf : - Lean4Lean.AddInductive.CandidateWhnfStep.Valid + Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid ⟨leanFlatConstructorContext, leanAuxiliaryExpr, leanAuxiliaryExpr⟩ := by - unfold Lean4Lean.AddInductive.CandidateWhnfStep.Valid + unfold Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid exact ExactLeanSyntax.exceptExpr_eq_ok_of_check leanAuxiliaryCandidateWhnfNative @@ -52,7 +52,7 @@ private theorem nestedResultSpineNative : /-! ## Exact operation relations -/ inductive NestedOuterPositivitySourceRel : - TcState .anon → Lean4Lean.AddInductive.Context → + TcState .anon → Ix.Theory.Named.AddInductive.Context → KExpr .anon → Lean.Expr → Prop | domain : NestedOuterPositivitySourceRel checkerInitial leanFlatConstructorContext nestedDomain leanAuxiliaryExpr @@ -62,7 +62,7 @@ is the fresh auxiliary produced for that exact application. The explicit shape equality makes impossible result forms eliminable without assuming an injective address/content correspondence. -/ inductive NestedOuterPositivityResultRel : - TcState .anon → Lean4Lean.AddInductive.Context → + TcState .anon → Ix.Theory.Named.AddInductive.Context → KExpr .anon → Lean.Expr → Prop | domain {ixResult : KExpr .anon} (shape : ixResult = nestedDomain) : @@ -71,15 +71,15 @@ inductive NestedOuterPositivityResultRel : private theorem nestedOuterRootFree {ixState : TcState .anon} - {leanContext : Lean4Lean.AddInductive.Context} + {leanContext : Ix.Theory.Named.AddInductive.Context} {ixSource : KExpr .anon} {leanSource : Lean.Expr} (relation : NestedOuterPositivitySourceRel ixState leanContext ixSource leanSource) (free : exprMentionsAnyAddr ixSource #[treeId.addr] = false) : ∃ leanResult, - Lean4Lean.AddInductive.CandidateWhnfStep.Valid + Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid ⟨leanContext, leanSource, leanResult⟩ ∧ - Lean4Lean.AddInductive.hasIndOcc leanFlatStats.indConsts leanResult = + Ix.Theory.Named.AddInductive.hasIndOcc leanFlatStats.indConsts leanResult = false := by cases relation rw [nestedDomainMentionsRootNative] at free @@ -87,7 +87,7 @@ private theorem nestedOuterRootFree private theorem nestedOuterWhnf {ixBefore ixAfter : TcState .anon} - {leanContext : Lean4Lean.AddInductive.Context} + {leanContext : Ix.Theory.Named.AddInductive.Context} {ixSource ixResult : KExpr .anon} {leanSource : Lean.Expr} (relation : NestedOuterPositivitySourceRel ixBefore leanContext ixSource leanSource) @@ -95,7 +95,7 @@ private theorem nestedOuterWhnf (run : (RecM.whnf ixSource).run checkerMethods ixBefore = .ok ixResult ixAfter) : ∃ leanResult, - Lean4Lean.AddInductive.CandidateWhnfStep.Valid + Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid ⟨leanContext, leanSource, leanResult⟩ ∧ NestedOuterPositivityResultRel ixAfter leanContext ixResult leanResult := by @@ -109,13 +109,13 @@ private theorem nestedOuterMentions (relation : NestedOuterPositivitySourceRel ixState leanContext ixExpr leanExpr) : exprMentionsAnyAddr ixExpr #[treeId.addr] = - Lean4Lean.AddInductive.hasIndOcc leanFlatStats.indConsts leanExpr := by + Ix.Theory.Named.AddInductive.hasIndOcc leanFlatStats.indConsts leanExpr := by cases relation rw [nestedDomainMentionsRootNative, leanAuxiliaryOccurs] private theorem nestedOuterForall {ixState : TcState .anon} - {leanContext : Lean4Lean.AddInductive.Context} + {leanContext : Ix.Theory.Named.AddInductive.Context} {ixName : Mode.anon.F Name} {ixBinder : Mode.anon.F Lean.BinderInfo} {ixDomain ixBody : KExpr .anon} {ixInfo : ExprInfo .anon} {leanExpr : Lean.Expr} @@ -131,7 +131,7 @@ private theorem nestedOuterForall .ok (ixOpen, ixFVar) ixAfterOpen → NestedOuterPositivitySourceRel ixAfterOpen (leanContext.pushLocalDecl leanName leanBinder - (Lean4Lean.AddInductive.consumeTypeAnnotations leanDomain)) + (Ix.Theory.Named.AddInductive.consumeTypeAnnotations leanDomain)) ixOpen (leanBody.instantiate1 leanContext.freshExpr) := by cases relation with | domain shape => cases shape @@ -174,7 +174,7 @@ theorem nestedTraceProducesCanonicalRequest private theorem nestedOuterDirect {ixState : TcState .anon} - {leanContext : Lean4Lean.AddInductive.Context} + {leanContext : Ix.Theory.Named.AddInductive.Context} {ixResult : KExpr .anon} {leanResult : Lean.Expr} {id : KId .anon} {us : Array (KUniv .anon)} {info : ExprInfo .anon} {args : Array (KExpr .anon)} @@ -187,10 +187,10 @@ private theorem nestedOuterDirect (_valid : ValidPositiveRecursiveApplication id us args traceGroups #[treeId.addr] checkerMethods ixState final) : ∃ targetIdx, - Lean4Lean.AddInductive.hasIndOcc leanFlatStats.indConsts leanResult = + Ix.Theory.Named.AddInductive.hasIndOcc leanFlatStats.indConsts leanResult = true ∧ leanResult.isForall = false ∧ - Lean4Lean.AddInductive.isValidIndApp? leanFlatStats leanResult = + Ix.Theory.Named.AddInductive.isValidIndApp? leanFlatStats leanResult = some targetIdx := by cases relation with | domain shape => @@ -201,7 +201,7 @@ private theorem nestedOuterDirect private theorem nestedOuterNested {fuel : Nat} {ixState : TcState .anon} - {leanContext : Lean4Lean.AddInductive.Context} + {leanContext : Ix.Theory.Named.AddInductive.Context} {ixResult : KExpr .anon} {leanResult : Lean.Expr} {id : KId .anon} {us : Array (KUniv .anon)} {info : ExprInfo .anon} {args : Array (KExpr .anon)} @@ -214,10 +214,10 @@ private theorem nestedOuterNested (trace : CompleteNestedPositivityApplicationTrace fuel id us args traceGroups #[treeId.addr] traceActive checkerMethods ixState final) : ∃ targetIdx, - Lean4Lean.AddInductive.hasIndOcc leanFlatStats.indConsts leanResult = + Ix.Theory.Named.AddInductive.hasIndOcc leanFlatStats.indConsts leanResult = true ∧ leanResult.isForall = false ∧ - Lean4Lean.AddInductive.isValidIndApp? leanFlatStats leanResult = + Ix.Theory.Named.AddInductive.isValidIndApp? leanFlatStats leanResult = some targetIdx := by cases relation with | domain shape => @@ -229,7 +229,7 @@ private theorem nestedOuterNested /-- Complete operation-shaped transport for the outer nested field. The `nested` field is discharged from the exact producer request/flat target -certificate; it is not a preconstructed Lean4Lean trace. -/ +certificate; it is not a preconstructed Ix.Theory.Named trace. -/ theorem nestedOuterPositivityTransport : FlattenedPositivityTraceTransport leanFlatStats #[treeId.addr] checkerMethods NestedOuterPositivitySourceRel @@ -249,13 +249,13 @@ def nestedOuterProductionTrace : PositivityDomainTrace groups RecM.checkPositivityDomainFuel_success checkerMethods positivityRun /-- The actual nested production execution constructs the exact retained -Lean4Lean positivity trace for the flattened outer constructor field. -/ +Ix.Theory.Named positivity trace for the flattened outer constructor field. -/ theorem nestedOuterConstructorPositivityTrace : - Nonempty (Lean4Lean.AddInductive.ConstructorPositivityTrace leanFlatStats + Nonempty (Ix.Theory.Named.AddInductive.ConstructorPositivityTrace leanFlatStats leanFlatNode.name 0 leanFlatConstructorContext leanAuxiliaryExpr positivityFuel) := by exact FlattenedPositivityTraceTransport.constructorPositivityTrace nestedOuterPositivityTransport nestedOuterProductionTrace (by rfl) .domain -end Ix.Tc.NestedRecursiveFixture +end Ix.Kernel.NestedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/NestedPositivityTraversal.lean b/Ix/Kernel/Verify/Inductive/NestedPositivityTraversal.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/NestedPositivityTraversal.lean rename to Ix/Kernel/Verify/Inductive/NestedPositivityTraversal.lean index 46a3b46ca..705e51286 100644 --- a/Ix/Tc/Verify/Inductive/NestedPositivityTraversal.lean +++ b/Ix/Kernel/Verify/Inductive/NestedPositivityTraversal.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Inductive.PositivityTraversal -import Ix.Tc.Verify.Inductive.SpecializationIdentity +import Ix.Kernel.Verify.Inductive.PositivityTraversal +import Ix.Kernel.Verify.Inductive.SpecializationIdentity /-! # Nested-family positivity traversal @@ -15,7 +15,7 @@ the exact loaded inductive header and continues from the real post-lookup checker state with the header's concrete arities, block, and constructor list. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Header information read from an external family reached by nested positivity traversal. -/ @@ -953,4 +953,4 @@ theorem checkNestedPositivityApplicationFuel_complete (checkNestedPositivityApplicationResolvedFuel_success hresolved)⟩ end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/NestedRecursiveFixture.lean b/Ix/Kernel/Verify/Inductive/NestedRecursiveFixture.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/NestedRecursiveFixture.lean rename to Ix/Kernel/Verify/Inductive/NestedRecursiveFixture.lean index 38b21bf6d..65551c631 100644 --- a/Ix/Tc/Verify/Inductive/NestedRecursiveFixture.lean +++ b/Ix/Kernel/Verify/Inductive/NestedRecursiveFixture.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Inductive.ConcreteFixture -import Ix.Tc.Verify.Inductive.NestedAuxiliaryExpansion -import Ix.Tc.Verify.Ingress.AnonStructural +import Ix.Kernel.Verify.Inductive.ConcreteFixture +import Ix.Kernel.Verify.Inductive.NestedAuxiliaryExpansion +import Ix.Kernel.Verify.Ingress.AnonStructural /-! # Concrete nested-recursive reachability fixture @@ -17,7 +17,7 @@ certificate identifies the request extracted from positivity with the exact auxiliary member retained by the public builder. -/ -namespace Ix.Tc.NestedRecursiveFixture +namespace Ix.Kernel.NestedRecursiveFixture open InductiveConcreteFixture @@ -571,4 +571,4 @@ theorem nestedAuxiliaryReachability : exact ⟨member, by simpa using member_mem, by simp [aux, key_eq]⟩ simpa using keyList -end Ix.Tc.NestedRecursiveFixture +end Ix.Kernel.NestedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/NestedRecursorAdmission.lean b/Ix/Kernel/Verify/Inductive/NestedRecursorAdmission.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/NestedRecursorAdmission.lean rename to Ix/Kernel/Verify/Inductive/NestedRecursorAdmission.lean index dae66ce31..e222af38f 100644 --- a/Ix/Tc/Verify/Inductive/NestedRecursorAdmission.lean +++ b/Ix/Kernel/Verify/Inductive/NestedRecursorAdmission.lean @@ -1,11 +1,11 @@ -import Ix.Tc.Verify.Inductive.NestedRecursorSoundness -import Ix.Tc.Verify.Inductive.OneFamilyAdmission +import Ix.Kernel.Verify.Inductive.NestedRecursorSoundness +import Ix.Kernel.Verify.Inductive.OneFamilyAdmission /-! # Atomic admission of the nested family and both restored recursors The aux-aware compiler stores the source `LeanTree` block and a distinct -two-member recursor block. Lean4Lean's nested transaction installs the +two-member recursor block. Ix.Theory.Named's nested transaction installs the source family, constructor, both restored recursors, and both equations in one semantic step. This module reconciles those shapes as two exact physical admissions: the source block advances the Theory environment, then the @@ -13,9 +13,9 @@ already-installed recursor block is admitted with its two concrete iota patterns. -/ -namespace Ix.Tc.NestedRecursiveFixture +namespace Ix.Kernel.NestedRecursiveFixture -open Lean4Lean +open Ix.Theory.Named open InductiveConcreteFixture local instance nestedRecursorAdmissionAddressDecidableEq : @@ -623,4 +623,4 @@ theorem nestedRecursorAtomicClosure : NestedRecursorAtomicClosure where familyAccepted := nestedOneFamilyAtomicClosure.familyAccepted recursorAccepted := nestedOneFamilyAtomicClosure.recursorAccepted -end Ix.Tc.NestedRecursiveFixture +end Ix.Kernel.NestedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/NestedRecursorFixture.lean b/Ix/Kernel/Verify/Inductive/NestedRecursorFixture.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/NestedRecursorFixture.lean rename to Ix/Kernel/Verify/Inductive/NestedRecursorFixture.lean index d7bee2a58..7c7e904ec 100644 --- a/Ix/Tc/Verify/Inductive/NestedRecursorFixture.lean +++ b/Ix/Kernel/Verify/Inductive/NestedRecursorFixture.lean @@ -1,5 +1,5 @@ import Ix.CompileDriver -import Ix.Tc.Verify.Inductive.NestedAdmission +import Ix.Kernel.Verify.Inductive.NestedAdmission /-! # Production nested-recursion block for `LeanTree` @@ -12,7 +12,7 @@ compiled source projections back to the already checked physical families; the recursor block is then ingressed and checked after that family block. -/ -namespace Ix.Tc.NestedRecursiveFixture +namespace Ix.Kernel.NestedRecursiveFixture open InductiveConcreteFixture @@ -299,4 +299,4 @@ theorem nestedRecursorKernelRun : generalize houtcome : nestedRecursorKernelOutcome = outcome at success ⊢ cases outcome <;> simp_all [nestedRecursorKernelOutcome] -end Ix.Tc.NestedRecursiveFixture +end Ix.Kernel.NestedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/NestedRecursorPattern.lean b/Ix/Kernel/Verify/Inductive/NestedRecursorPattern.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/NestedRecursorPattern.lean rename to Ix/Kernel/Verify/Inductive/NestedRecursorPattern.lean index 8ea13cd7e..6cd2ceb2b 100644 --- a/Ix/Tc/Verify/Inductive/NestedRecursorPattern.lean +++ b/Ix/Kernel/Verify/Inductive/NestedRecursorPattern.lean @@ -1,20 +1,22 @@ -import Ix.Tc.Verify.Inductive.IotaPattern -import Ix.Tc.Verify.Inductive.NestedRecursorFixture +import Ix.Kernel.Verify.Inductive.IotaPattern +import Ix.Kernel.Verify.Inductive.NestedRecursorFixture + +open Ix.Theory (VLevel) /-! # Physical restored-recursion patterns for `LeanTree` The aux-aware compiler emits one physical recursor for the stored `LeanTree` -family and one for the flattened `LeanBox LeanTree` dependency. Lean4Lean's +family and one for the flattened `LeanBox LeanTree` dependency. Ix.Theory.Named's nested transaction restores those declarations as `LeanTree.rec` and `LeanTree.rec_1`, with one registered equation apiece. This module proves the complete representation correspondence and constructs the exact two iota patterns consumed by semantic admission. -/ -namespace Ix.Tc.NestedRecursiveFixture +namespace Ix.Kernel.NestedRecursiveFixture -open Lean4Lean +open Ix.Theory.Named open InductiveConcreteFixture local instance nestedPatternAddressDecidableEq : DecidableEq Address := @@ -592,4 +594,4 @@ structure NestedRestoredPatternSound : Prop where node : treeNodePattern.Sound semanticTreeEnv wrap : treeWrapPattern.Sound semanticTreeEnv -end Ix.Tc.NestedRecursiveFixture +end Ix.Kernel.NestedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/NestedRecursorSoundness.lean b/Ix/Kernel/Verify/Inductive/NestedRecursorSoundness.lean similarity index 95% rename from Ix/Tc/Verify/Inductive/NestedRecursorSoundness.lean rename to Ix/Kernel/Verify/Inductive/NestedRecursorSoundness.lean index 38b65e320..8eb2198b5 100644 --- a/Ix/Tc/Verify/Inductive/NestedRecursorSoundness.lean +++ b/Ix/Kernel/Verify/Inductive/NestedRecursorSoundness.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Inductive.NestedRecursorPattern +import Ix.Kernel.Verify.Inductive.NestedRecursorPattern + +open Ix.Theory (VLevel) /-! # Soundness of the two restored nested iota equations @@ -11,9 +13,9 @@ single pattern check to identify the physical `LeanBox` parameter with the restored specialization `LeanTree`. -/ -namespace Ix.Tc.NestedRecursiveFixture +namespace Ix.Kernel.NestedRecursiveFixture -open Lean4Lean +open Ix.Theory.Named /-! ## Shared concrete telescope shapes -/ @@ -287,7 +289,7 @@ theorem treeNodePatternSound : treeNodePattern.Sound semanticTreeEnv := by hconstructorApplied⟩ := htype.app_inv hfutureWF.ordered hGamma obtain ⟨recursorHeadType, hrecursorHeadTyped⟩ := - Lean4Lean.VEnv.HasType.appN_head hfutureWF hGamma hrecursorApplied + Ix.Theory.Named.VEnv.HasType.appN_head hfutureWF hGamma hrecursorApplied obtain ⟨recursorConstant, hrecursorLookup, hlevelsWF, hlevelsArity⟩ := hrecursorHeadTyped.const_inv hfutureWF.ordered hGamma have hcertifiedRecursorLookup := @@ -308,7 +310,7 @@ theorem treeNodePatternSound : treeNodePattern.Sound semanticTreeEnv := by subst levelTail obtain ⟨constructorHeadType, hconstructorHeadTyped⟩ := - Lean4Lean.VEnv.HasType.appN_head hfutureWF hGamma hconstructorApplied + Ix.Theory.Named.VEnv.HasType.appN_head hfutureWF hGamma hconstructorApplied obtain ⟨constructorConstant, hconstructorLookup, constructorLevelsWF, hconstructorLevelsArity⟩ := hconstructorHeadTyped.const_inv hfutureWF.ordered hGamma @@ -337,7 +339,7 @@ theorem treeNodePatternSound : treeNodePattern.Sound semanticTreeEnv := by have hrecursorConstantTyped : future.HasType uvars Gamma (.const ``LeanTree.rec [u]) (semanticTreeRec.type.instL [u]) := by - simpa using (Lean4Lean.VEnv.HasType.const (Γ := Gamma) + simpa using (Ix.Theory.Named.VEnv.HasType.const (Γ := Gamma) hcertifiedRecursorLookup hlevelsWF hlevelsArity) have hrecursorCommonType : future.HasType uvars Gamma (.const ``LeanTree.rec [u]) @@ -360,13 +362,13 @@ theorem treeNodePatternSound : treeNodePattern.Sound semanticTreeEnv := by (VExpr.appN (semanticTreeNodeRule.lhs.instL [u]) [motiveTree, motiveBox, nodeMinor, wrapMinor]) (nodeEquationFieldType u motiveTree motiveBox nodeMinor wrapMinor) := - Lean4Lean.VEnv.HasType.transfer_appN_telescope_instRev + Ix.Theory.Named.VEnv.HasType.transfer_appN_telescope_instRev hfutureWF hGamma hcommonLength hrecursorApplied hrecursorCommonType hequationLhsCommonType have hconstructorConstantTyped : future.HasType uvars Gamma (.const ``LeanTree.node []) nodeConstructorFieldType := by - have htyped := Lean4Lean.VEnv.HasType.const (Γ := Gamma) + have htyped := Ix.Theory.Named.VEnv.HasType.const (Γ := Gamma) hcertifiedConstructorLookup constructorLevelsWF hconstructorLevelsArity rw [nodeConstructorTypeInstLNil] at htyped @@ -404,7 +406,7 @@ theorem treeNodePatternSound : treeNodePattern.Sound semanticTreeEnv := by rw [nodeFieldBindersEq] rfl have hequationLhsFieldsApplied := - Lean4Lean.VEnv.HasType.transfer_appN_telescope_instRev + Ix.Theory.Named.VEnv.HasType.transfer_appN_telescope_instRev hfutureWF hGamma hfieldLength hconstructorApplied hconstructorFieldHead' hequationFieldHead have hequationLhsApplied : future.HasType uvars Gamma @@ -419,7 +421,7 @@ theorem treeNodePatternSound : treeNodePattern.Sound semanticTreeEnv := by VExpr.appN_append] exact hequationLhsFieldsApplied have hequationApplied := - Lean4Lean.VEnv.IsDefEq.appN_same hfutureWF hGamma hequation + Ix.Theory.Named.VEnv.IsDefEq.appN_same hfutureWF hGamma hequation hequationLhsApplied have hequationLhsApplied' := hequationLhsApplied @@ -429,7 +431,7 @@ theorem treeNodePatternSound : treeNodePattern.Sound semanticTreeEnv := by (nodeRuleBinders.map (VExpr.instL [u])).length := by simpa only [List.length_cons, List.length_nil, List.length_map] using nodeRuleBindersLength.symm - have hlhsBeta := Lean4Lean.VEnv.HasType.lamN_appN_beta + have hlhsBeta := Ix.Theory.Named.VEnv.HasType.lamN_appN_beta hfutureWF hGamma hruleArgsLength hequationLhsApplied' rw [nodeLhsBodyOpen] at hlhsBeta have hlhsBeta' : future.IsDefEqU uvars Gamma @@ -511,7 +513,7 @@ theorem treeWrapPatternSound : treeWrapPattern.Sound semanticTreeEnv := by hconstructorApplied⟩ := htype.app_inv hfutureWF.ordered hGamma obtain ⟨recursorHeadType, hrecursorHeadTyped⟩ := - Lean4Lean.VEnv.HasType.appN_head hfutureWF hGamma hrecursorApplied + Ix.Theory.Named.VEnv.HasType.appN_head hfutureWF hGamma hrecursorApplied obtain ⟨recursorConstant, hrecursorLookup, hlevelsWF, hlevelsArity⟩ := hrecursorHeadTyped.const_inv hfutureWF.ordered hGamma have hcertifiedRecursorLookup := @@ -532,7 +534,7 @@ theorem treeWrapPatternSound : treeWrapPattern.Sound semanticTreeEnv := by subst levelTail obtain ⟨constructorHeadType, hconstructorHeadTyped⟩ := - Lean4Lean.VEnv.HasType.appN_head hfutureWF hGamma hconstructorApplied + Ix.Theory.Named.VEnv.HasType.appN_head hfutureWF hGamma hconstructorApplied obtain ⟨constructorConstant, hconstructorLookup, constructorLevelsWF, hconstructorLevelsArity⟩ := hconstructorHeadTyped.const_inv hfutureWF.ordered hGamma @@ -555,11 +557,11 @@ theorem treeWrapPatternSound : treeWrapPattern.Sound semanticTreeEnv := by have hrecursorConstantTyped : future.HasType uvars Gamma (.const ``LeanTree.rec_1 [u]) (semanticTreeRecOne.type.instL [u]) := by - simpa using (Lean4Lean.VEnv.HasType.const (Γ := Gamma) + simpa using (Ix.Theory.Named.VEnv.HasType.const (Γ := Gamma) hcertifiedRecursorLookup hlevelsWF hlevelsArity) have hconstructorConstantTyped : future.HasType uvars Gamma (.const ``LeanBox.wrap []) semanticBoxWrap.type := by - have htyped := Lean4Lean.VEnv.HasType.const (Γ := Gamma) + have htyped := Ix.Theory.Named.VEnv.HasType.const (Γ := Gamma) hcertifiedConstructorLookup constructorLevelsWF hconstructorLevelsArity rw [wrapConstructorTypeInstLNil] at htyped @@ -575,12 +577,12 @@ theorem treeWrapPatternSound : treeWrapPattern.Sound semanticTreeEnv := by ([constructorAlpha] ++ [field])) majorDomain := by simpa using hconstructorApplied obtain ⟨constructorParameterResult, hconstructorParameterApplied⟩ := - Lean4Lean.VEnv.HasType.appN_prefix hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.appN_prefix hfutureWF hGamma (prefixArgs := [constructorAlpha]) (suffixArgs := [field]) hconstructorAppliedSplit have hconstructorAlpha : future.HasType uvars Gamma constructorAlpha (.sort (.succ .zero)) := - Lean4Lean.VEnv.HasType.app_argument_of_head hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.app_argument_of_head hfutureWF hGamma hconstructorParameterApplied hconstructorParameterHead have hparameterAtConstructor : future.IsDefEq uvars Gamma constructorAlpha (.const ``LeanTree []) (.sort (.succ .zero)) := @@ -606,7 +608,7 @@ theorem treeWrapPatternSound : treeWrapPattern.Sound semanticTreeEnv := by (VExpr.appN (.const ``LeanBox.wrap []) [.const ``LeanTree [], field]) := by simpa only [VExpr.appN] using - (Lean4Lean.VEnv.IsDefEq.appN_same hfutureWF hGamma + (Ix.Theory.Named.VEnv.IsDefEq.appN_same hfutureWF hGamma hconstructorPrefixEqTyped hconstructorAppliedFromPrefix) have hconstructorEqTyped : future.IsDefEq uvars Gamma (VExpr.appN (.const ``LeanBox.wrap []) [constructorAlpha, field]) @@ -653,7 +655,7 @@ theorem treeWrapPatternSound : treeWrapPattern.Sound semanticTreeEnv := by (VExpr.appN (semanticTreeWrapRule.lhs.instL [u]) [motiveTree, motiveBox, nodeMinor, wrapMinor]) (wrapEquationFieldType u motiveTree motiveBox nodeMinor wrapMinor) := - Lean4Lean.VEnv.HasType.transfer_appN_telescope_instRev + Ix.Theory.Named.VEnv.HasType.transfer_appN_telescope_instRev hfutureWF hGamma hcommonLength hrecursorApplied hrecursorCommonType hequationLhsCommonType @@ -661,14 +663,14 @@ theorem treeWrapPatternSound : treeWrapPattern.Sound semanticTreeEnv := by hfuture.constants semanticTreeTransactionFacts.sourceFamily have htreeConstantTyped : future.HasType uvars Gamma (.const ``LeanTree []) (.sort (.succ .zero)) := by - have htyped := Lean4Lean.VEnv.HasType.const (U := uvars) + have htyped := Ix.Theory.Named.VEnv.HasType.const (U := uvars) (Γ := Gamma) (ls := []) hcertifiedTreeLookup (by simp) (by rfl) rw [treeFamilyTypeInstLNil, treeFamilyTypeShape] at htyped exact htyped have hcanonicalConstructorPrefix : future.HasType uvars Gamma (.app (.const ``LeanBox.wrap []) (.const ``LeanTree [])) wrapConstructorFieldType := by - have happ := Lean4Lean.VEnv.HasType.app + have happ := Ix.Theory.Named.VEnv.HasType.app hconstructorParameterHead htreeConstantTyped change future.HasType uvars Gamma (.app (.const ``LeanBox.wrap []) (.const ``LeanTree [])) @@ -715,7 +717,7 @@ theorem treeWrapPatternSound : treeWrapPattern.Sound semanticTreeEnv := by rw [wrapFieldBindersEq] rfl have hequationLhsFieldsApplied := - Lean4Lean.VEnv.HasType.transfer_appN_telescope_instRev + Ix.Theory.Named.VEnv.HasType.transfer_appN_telescope_instRev hfutureWF hGamma hfieldLength hcanonicalConstructorApplied hconstructorFieldHead' hequationFieldHead have hequationLhsApplied : future.HasType uvars Gamma @@ -730,7 +732,7 @@ theorem treeWrapPatternSound : treeWrapPattern.Sound semanticTreeEnv := by VExpr.appN_append] exact hequationLhsFieldsApplied have hequationApplied := - Lean4Lean.VEnv.IsDefEq.appN_same hfutureWF hGamma hequation + Ix.Theory.Named.VEnv.IsDefEq.appN_same hfutureWF hGamma hequation hequationLhsApplied have hequationLhsApplied' := hequationLhsApplied @@ -740,7 +742,7 @@ theorem treeWrapPatternSound : treeWrapPattern.Sound semanticTreeEnv := by (wrapRuleBinders.map (VExpr.instL [u])).length := by simpa only [List.length_cons, List.length_nil, List.length_map] using wrapRuleBindersLength.symm - have hlhsBeta := Lean4Lean.VEnv.HasType.lamN_appN_beta + have hlhsBeta := Ix.Theory.Named.VEnv.HasType.lamN_appN_beta hfutureWF hGamma hruleArgsLength hequationLhsApplied' rw [wrapLhsBodyOpen] at hlhsBeta have hlhsBeta' : future.IsDefEqU uvars Gamma @@ -788,4 +790,4 @@ theorem treeWrapPatternRel : RawRecursorRulePatternRel.of_metadata_sound treeWrapPatternMetadata treeWrapPatternSound -end Ix.Tc.NestedRecursiveFixture +end Ix.Kernel.NestedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/NestedSemanticTransaction.lean b/Ix/Kernel/Verify/Inductive/NestedSemanticTransaction.lean similarity index 97% rename from Ix/Tc/Verify/Inductive/NestedSemanticTransaction.lean rename to Ix/Kernel/Verify/Inductive/NestedSemanticTransaction.lean index 88b8b96ae..feab1157c 100644 --- a/Ix/Tc/Verify/Inductive/NestedSemanticTransaction.lean +++ b/Ix/Kernel/Verify/Inductive/NestedSemanticTransaction.lean @@ -1,13 +1,15 @@ -import Ix.Tc.Verify.Inductive.Certificate -import Ix.Tc.Verify.Inductive.NestedBlockCertificate -import Ix.Tc.Verify.Inductive.NestedConstructorValidation -import Lean4Lean.Verify.Environment.NestedReplay +import Ix.Kernel.Verify.Inductive.Certificate +import Ix.Kernel.Verify.Inductive.NestedBlockCertificate +import Ix.Kernel.Verify.Inductive.NestedConstructorValidation +import Ix.Theory.Named.Verify.Environment.NestedReplay + +open Ix.Theory (VLevel) /-! # Semantic transaction for the concrete nested `LeanBox`/`LeanTree` fixture The operational fixture has already connected Ix ingress and positivity to -Lean4Lean's flattened constructor validation. This module keeps the same +Ix.Theory.Named's flattened constructor validation. This module keeps the same exact names and builds the missing Theory half: * a certified dependency transaction for `LeanBox`; @@ -21,12 +23,12 @@ to Lean's stored nested metadata, so they cannot be substituted for semantic or physical correspondence evidence. -/ -namespace Ix.Tc.NestedRecursiveFixture +namespace Ix.Kernel.NestedRecursiveFixture open Lean -open Lean4Lean -open Lean4Lean.InductiveReplayFixtures -open Lean4Lean.NestedRepresentation +open Ix.Theory.Named +open Ix.Theory.Named.InductiveReplayFixtures +open Ix.Theory.Named.NestedRepresentation open VInductDecl local instance : Inhabited VEnv := ⟨.empty⟩ @@ -520,4 +522,4 @@ theorem semanticTreeTransactionFacts : SemanticTreeTransactionFacts := by rw [semanticTreeRules_eq] simp [semanticTreeRules] -end Ix.Tc.NestedRecursiveFixture +end Ix.Kernel.NestedRecursiveFixture diff --git a/Ix/Tc/Verify/Inductive/OccurrenceClosure.lean b/Ix/Kernel/Verify/Inductive/OccurrenceClosure.lean similarity index 97% rename from Ix/Tc/Verify/Inductive/OccurrenceClosure.lean rename to Ix/Kernel/Verify/Inductive/OccurrenceClosure.lean index 2c4f3dec5..d66f5f8fb 100644 --- a/Ix/Tc/Verify/Inductive/OccurrenceClosure.lean +++ b/Ix/Kernel/Verify/Inductive/OccurrenceClosure.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Inductive.OccurrenceValidation -import Ix.Tc.Verify.RecursiveMethods.ScopedCallDomains +import Ix.Kernel.Verify.Inductive.OccurrenceValidation +import Ix.Kernel.Verify.RecursiveMethods.ScopedCallDomains /-! # Run-scoped recursive-occurrence closure @@ -16,7 +16,7 @@ finite `Methods.ScopedCallScheduleAt` and preserves the scoped suffix-state witness on every intermediate checker state. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Exact finite method-call footprint of one parameter-comparison slice. -/ def PositiveParameterCallPlan (calls : Methods.CallDomain) @@ -113,4 +113,4 @@ theorem theoryParametersScoped end RecM.ValidPositiveRecursiveApplicationHeader -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/OccurrenceValidation.lean b/Ix/Kernel/Verify/Inductive/OccurrenceValidation.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/OccurrenceValidation.lean rename to Ix/Kernel/Verify/Inductive/OccurrenceValidation.lean index d05995579..2751b612c 100644 --- a/Ix/Tc/Verify/Inductive/OccurrenceValidation.lean +++ b/Ix/Kernel/Verify/Inductive/OccurrenceValidation.lean @@ -1,8 +1,8 @@ -import Ix.Tc.Verify.Decl -import Ix.Tc.Verify.Support -import Ix.Tc.Verify.Totalization -import Ix.Tc.Verify.Trans -import Ix.Tc.Verify.World +import Ix.Kernel.Verify.Decl +import Ix.Kernel.Verify.Support +import Ix.Kernel.Verify.Totalization +import Ix.Kernel.Verify.Trans +import Ix.Kernel.Verify.World /-! # Recursive-occurrence validation @@ -14,7 +14,7 @@ guard, and the complete state-threaded parameter-definitional-equality loop. No semantic inductive oracle is used here. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Header information read from the actual recursive-family declaration. -/ def KConst.PositiveRecursiveHeader (concrete : KConst m) @@ -38,7 +38,7 @@ def PositiveUniverseSpecialization (group : PositivityGroup m) ∀ i, i < us.size → univEq us[i]! (.mkParam i.toUInt64 RecM.anonN : KUniv m) = true -/-- Elementwise form of Lean4Lean's root-family-free index condition. -/ +/-- Elementwise form of Ix.Theory.Named's root-family-free index condition. -/ def RootIndicesIndependent (args : Array (KExpr m)) (nParams : Nat) (rootAddrs : Array Address) : Prop := let indices := args.extract nParams args.size @@ -109,7 +109,7 @@ def PositiveParameterDefEqContract (uvars : Nat) (Delta : KVLCtx) (methods : Methods .anon) (invariant : TcState .anon → Prop) : Prop := ∀ {state : TcState .anon} {argument parameter : KExpr .anon} - {argumentV parameterV : Lean4Lean.VExpr}, + {argumentV parameterV : Ix.Theory.Named.VExpr}, support argument → support parameter → TrKExprS world.venv uvars world.nameOf trProj Delta argument argumentV → @@ -375,7 +375,7 @@ theorem ValidPositiveRecursiveApplicationHeader.theoryParameters rcases valid with ⟨_, _, _, _, afterParameters, _, trace, _, hfinal⟩ have hsemantic := - Ix.Tc.RecM.PositiveParameterComparisonTrace.theoryDefEq defEq trace + Ix.Kernel.RecM.PositiveParameterComparisonTrace.theoryDefEq defEq trace hinitial plan exact ⟨afterParameters, hsemantic.1, hsemantic.2, hfinal⟩ @@ -618,4 +618,4 @@ theorem ValidPositiveRecursiveApplication.run | ctor => contradiction end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/OneFamilyAdmission.lean b/Ix/Kernel/Verify/Inductive/OneFamilyAdmission.lean similarity index 90% rename from Ix/Tc/Verify/Inductive/OneFamilyAdmission.lean rename to Ix/Kernel/Verify/Inductive/OneFamilyAdmission.lean index 0a5a7a039..c51745e5f 100644 --- a/Ix/Tc/Verify/Inductive/OneFamilyAdmission.lean +++ b/Ix/Kernel/Verify/Inductive/OneFamilyAdmission.lean @@ -1,9 +1,9 @@ -import Ix.Tc.Verify.Check.BlockAcceptance +import Ix.Kernel.Verify.Check.BlockAcceptance /-! # Oracle-free one-family generated-recursor admission -Lean4Lean's current public generation certificate covers one inductive family +Ix.Theory.Named's current public generation certificate covers one inductive family at a time. Such a transaction has two distinct physical Ix blocks: * the family/constructor block advances the certified Theory environment; and @@ -15,7 +15,7 @@ environment, constructing an `InductiveOracle`, or pretending that a mutual declaration is a sequence of singleton declarations. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Complete semantic evidence for the two physical blocks generated by one certified inductive family. Freshness of the recursor is stated in the @@ -25,7 +25,7 @@ structure OneFamilyRecursorCertificate (trProj : RawProjRel) (world : VerifyWorld) (familyBlock : KId .anon) (familyMembers : Array (KId .anon)) (recursorBlock : KId .anon) (recursorMembers : Array (KId .anon)) - (afterVEnv : Lean4Lean.VEnv) : Prop where + (afterVEnv : Ix.Theory.Named.VEnv) : Prop where family : SemanticBlockTransitionCertificate trProj world familyBlock familyMembers .inductive' afterVEnv @@ -39,7 +39,7 @@ namespace OneFamilyRecursorCertificate def familyWorld {trProj : RawProjRel} {world : VerifyWorld} {familyBlock : KId .anon} {familyMembers : Array (KId .anon)} {recursorBlock : KId .anon} {recursorMembers : Array (KId .anon)} - {afterVEnv : Lean4Lean.VEnv} + {afterVEnv : Ix.Theory.Named.VEnv} (certificate : OneFamilyRecursorCertificate trProj world familyBlock familyMembers recursorBlock recursorMembers afterVEnv) : VerifyWorld := certificate.family.admittedWorld @@ -49,7 +49,7 @@ admitted. -/ def admittedWorld {trProj : RawProjRel} {world : VerifyWorld} {familyBlock : KId .anon} {familyMembers : Array (KId .anon)} {recursorBlock : KId .anon} {recursorMembers : Array (KId .anon)} - {afterVEnv : Lean4Lean.VEnv} + {afterVEnv : Ix.Theory.Named.VEnv} (certificate : OneFamilyRecursorCertificate trProj world familyBlock familyMembers recursorBlock recursorMembers afterVEnv) : VerifyWorld := certificate.recursor.admittedWorld @@ -58,7 +58,7 @@ def admittedWorld {trProj : RawProjRel} {world : VerifyWorld} theorem familyAdmission {trProj : RawProjRel} {world : VerifyWorld} {familyBlock : KId .anon} {familyMembers : Array (KId .anon)} {recursorBlock : KId .anon} {recursorMembers : Array (KId .anon)} - {afterVEnv : Lean4Lean.VEnv} + {afterVEnv : Ix.Theory.Named.VEnv} (certificate : OneFamilyRecursorCertificate trProj world familyBlock familyMembers recursorBlock recursorMembers afterVEnv) (trusted : TrustedCatalogRel trProj world) : @@ -71,7 +71,7 @@ stage and admits only semantic entries already present in `afterVEnv`. -/ theorem recursorAdmission {trProj : RawProjRel} {world : VerifyWorld} {familyBlock : KId .anon} {familyMembers : Array (KId .anon)} {recursorBlock : KId .anon} {recursorMembers : Array (KId .anon)} - {afterVEnv : Lean4Lean.VEnv} + {afterVEnv : Ix.Theory.Named.VEnv} (certificate : OneFamilyRecursorCertificate trProj world familyBlock familyMembers recursorBlock recursorMembers afterVEnv) (trusted : TrustedCatalogRel trProj world) : @@ -84,7 +84,7 @@ theorem recursorAdmission {trProj : RawProjRel} {world : VerifyWorld} theorem le_admittedWorld {trProj : RawProjRel} {world : VerifyWorld} {familyBlock : KId .anon} {familyMembers : Array (KId .anon)} {recursorBlock : KId .anon} {recursorMembers : Array (KId .anon)} - {afterVEnv : Lean4Lean.VEnv} + {afterVEnv : Ix.Theory.Named.VEnv} (certificate : OneFamilyRecursorCertificate trProj world familyBlock familyMembers recursorBlock recursorMembers afterVEnv) : world ≤ certificate.admittedWorld := @@ -96,7 +96,7 @@ by the family members and the original trusted set. -/ theorem trusted_iff {trProj : RawProjRel} {world : VerifyWorld} {familyBlock : KId .anon} {familyMembers : Array (KId .anon)} {recursorBlock : KId .anon} {recursorMembers : Array (KId .anon)} - {afterVEnv : Lean4Lean.VEnv} + {afterVEnv : Ix.Theory.Named.VEnv} (certificate : OneFamilyRecursorCertificate trProj world familyBlock familyMembers recursorBlock recursorMembers afterVEnv) (id : KId .anon) : @@ -107,7 +107,7 @@ theorem trusted_iff {trProj : RawProjRel} {world : VerifyWorld} @[simp] theorem admittedWorld_venv {trProj : RawProjRel} {world : VerifyWorld} {familyBlock : KId .anon} {familyMembers : Array (KId .anon)} {recursorBlock : KId .anon} - {recursorMembers : Array (KId .anon)} {afterVEnv : Lean4Lean.VEnv} + {recursorMembers : Array (KId .anon)} {afterVEnv : Ix.Theory.Named.VEnv} (certificate : OneFamilyRecursorCertificate trProj world familyBlock familyMembers recursorBlock recursorMembers afterVEnv) : certificate.admittedWorld.venv = afterVEnv := @@ -116,7 +116,7 @@ theorem trusted_iff {trProj : RawProjRel} {world : VerifyWorld} @[simp] theorem admittedWorld_catalog {trProj : RawProjRel} {world : VerifyWorld} {familyBlock : KId .anon} {familyMembers : Array (KId .anon)} {recursorBlock : KId .anon} - {recursorMembers : Array (KId .anon)} {afterVEnv : Lean4Lean.VEnv} + {recursorMembers : Array (KId .anon)} {afterVEnv : Ix.Theory.Named.VEnv} (certificate : OneFamilyRecursorCertificate trProj world familyBlock familyMembers recursorBlock recursorMembers afterVEnv) : certificate.admittedWorld.catalog = world.catalog := @@ -125,7 +125,7 @@ theorem trusted_iff {trProj : RawProjRel} {world : VerifyWorld} @[simp] theorem admittedWorld_blocks {trProj : RawProjRel} {world : VerifyWorld} {familyBlock : KId .anon} {familyMembers : Array (KId .anon)} {recursorBlock : KId .anon} - {recursorMembers : Array (KId .anon)} {afterVEnv : Lean4Lean.VEnv} + {recursorMembers : Array (KId .anon)} {afterVEnv : Ix.Theory.Named.VEnv} (certificate : OneFamilyRecursorCertificate trProj world familyBlock familyMembers recursorBlock recursorMembers afterVEnv) : certificate.admittedWorld.blocks = world.blocks := @@ -134,7 +134,7 @@ theorem trusted_iff {trProj : RawProjRel} {world : VerifyWorld} @[simp] theorem admittedWorld_nameOf {trProj : RawProjRel} {world : VerifyWorld} {familyBlock : KId .anon} {familyMembers : Array (KId .anon)} {recursorBlock : KId .anon} - {recursorMembers : Array (KId .anon)} {afterVEnv : Lean4Lean.VEnv} + {recursorMembers : Array (KId .anon)} {afterVEnv : Ix.Theory.Named.VEnv} (certificate : OneFamilyRecursorCertificate trProj world familyBlock familyMembers recursorBlock recursorMembers afterVEnv) : certificate.admittedWorld.nameOf = world.nameOf := @@ -146,7 +146,7 @@ world, not only the two intermediate promotion witnesses. -/ structure AtomicClosure {trProj : RawProjRel} {world : VerifyWorld} {familyBlock : KId .anon} {familyMembers : Array (KId .anon)} {recursorBlock : KId .anon} {recursorMembers : Array (KId .anon)} - {afterVEnv : Lean4Lean.VEnv} + {afterVEnv : Ix.Theory.Named.VEnv} (certificate : OneFamilyRecursorCertificate trProj world familyBlock familyMembers recursorBlock recursorMembers afterVEnv) : Prop where familyAdmission : @@ -162,7 +162,7 @@ structure AtomicClosure {trProj : RawProjRel} {world : VerifyWorld} theorem atomicClosure {trProj : RawProjRel} {world : VerifyWorld} {familyBlock : KId .anon} {familyMembers : Array (KId .anon)} {recursorBlock : KId .anon} {recursorMembers : Array (KId .anon)} - {afterVEnv : Lean4Lean.VEnv} + {afterVEnv : Ix.Theory.Named.VEnv} (certificate : OneFamilyRecursorCertificate trProj world familyBlock familyMembers recursorBlock recursorMembers afterVEnv) (trusted : TrustedCatalogRel trProj world) : @@ -178,4 +178,4 @@ theorem atomicClosure {trProj : RawProjRel} {world : VerifyWorld} end OneFamilyRecursorCertificate -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/PositivityTraceAdapter.lean b/Ix/Kernel/Verify/Inductive/PositivityTraceAdapter.lean similarity index 85% rename from Ix/Tc/Verify/Inductive/PositivityTraceAdapter.lean rename to Ix/Kernel/Verify/Inductive/PositivityTraceAdapter.lean index b497aff1e..fb0b62a2c 100644 --- a/Ix/Tc/Verify/Inductive/PositivityTraceAdapter.lean +++ b/Ix/Kernel/Verify/Inductive/PositivityTraceAdapter.lean @@ -1,11 +1,11 @@ -import Ix.Tc.Verify.Inductive.RecursivePositivityTraversal -import Lean4Lean.Inductive.ValidationTrace +import Ix.Kernel.Verify.Inductive.RecursivePositivityTraversal +import Ix.Theory.Named.Reference.Inductive.ValidationTrace /-! -# Transporting production positivity into Lean4Lean's retained trace +# Transporting production positivity into Ix.Theory.Named's retained trace `PositivityDomainTrace` records the successful Ix execution, while -Lean4Lean's `ConstructorPositivityTrace` records the corresponding successful +Ix.Theory.Named's `ConstructorPositivityTrace` records the corresponding successful Lean-kernel execution. These traces cannot be cast into one another: they use different expression representations, local contexts, reducers, and state models. @@ -24,7 +24,7 @@ certified flat-block auxiliary expansion rather than supplied through this interface. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Exact successful production positivity traversal for the flat fragment. @@ -97,33 +97,33 @@ end FlatPositivityDomainTrace /-- Primitive cross-kernel correspondence needed to transport the flat positivity fragment. -`SourceRel` is indexed by the current Ix state and Lean4Lean constructor +`SourceRel` is indexed by the current Ix state and Ix.Theory.Named constructor context so the forall field must relate the *actual* free variable allocated by each checker. `ResultRel` separately relates the one WHNF result consumed by the branch discriminator. Keeping these phases distinct avoids demanding that a post-WHNF cache state be closed under an execution that production never performs. The direct field consumes Ix's already-proved logical -recursive-application invariant; it may not assume the final Lean4Lean trace +recursive-application invariant; it may not assume the final Ix.Theory.Named trace itself. -/ structure FlatPositivityTraceTransport - (stats : Lean4Lean.AddInductive.InductiveStats) + (stats : Ix.Theory.Named.AddInductive.InductiveStats) (rootAddrs : Array Address) (methods : Methods .anon) - (SourceRel ResultRel : TcState .anon → Lean4Lean.AddInductive.Context → + (SourceRel ResultRel : TcState .anon → Ix.Theory.Named.AddInductive.Context → KExpr .anon → Lean.Expr → Prop) : Prop where /-- A syntactically root-free Ix domain has a corresponding successful - Lean4Lean WHNF result with no occurrence of the flat block. Establishing + Ix.Theory.Named WHNF result with no occurrence of the flat block. Establishing this field requires the declaration-order/freshness argument that reduction of an older constant cannot introduce a newly declared family. -/ rootFree : ∀ {ixState leanContext ixSource leanSource}, SourceRel ixState leanContext ixSource leanSource → exprMentionsAnyAddr ixSource rootAddrs = false → ∃ leanResult, - Lean4Lean.AddInductive.CandidateWhnfStep.Valid + Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid ⟨leanContext, leanSource, leanResult⟩ ∧ - Lean4Lean.AddInductive.hasIndOcc stats.indConsts leanResult = false + Ix.Theory.Named.AddInductive.hasIndOcc stats.indConsts leanResult = false /-- One exact successful production WHNF execution on a root-mentioning - source corresponds to one exact successful Lean4Lean candidate-WHNF + source corresponds to one exact successful Ix.Theory.Named candidate-WHNF execution, and their results remain related in the post-Ix state. Production checks occurrence before entering this branch. Retaining that @@ -134,7 +134,7 @@ structure FlatPositivityTraceTransport exprMentionsAnyAddr ixSource rootAddrs = true → (RecM.whnf ixSource).run methods ixBefore = .ok ixResult ixAfter → ∃ leanResult, - Lean4Lean.AddInductive.CandidateWhnfStep.Valid + Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid ⟨leanContext, leanSource, leanResult⟩ ∧ ResultRel ixAfter leanContext ixResult leanResult @@ -143,11 +143,11 @@ structure FlatPositivityTraceTransport mentions : ∀ {ixState leanContext ixExpr leanExpr}, SourceRel ixState leanContext ixExpr leanExpr → exprMentionsAnyAddr ixExpr rootAddrs = - Lean4Lean.AddInductive.hasIndOcc stats.indConsts leanExpr + Ix.Theory.Named.AddInductive.hasIndOcc stats.indConsts leanExpr /-- A related Ix forall is a Lean forall. Its domains are related, and the two checkers' concrete binder-opening operations produce related bodies in - the exact extended Lean4Lean context. -/ + the exact extended Ix.Theory.Named context. -/ forallE : ∀ {ixState leanContext ixName ixBinder ixDomain ixBody ixInfo leanExpr}, ResultRel ixState leanContext @@ -161,11 +161,11 @@ structure FlatPositivityTraceTransport .ok (ixOpen, ixFVar) ixAfterOpen → SourceRel ixAfterOpen (leanContext.pushLocalDecl leanName leanBinder - (Lean4Lean.AddInductive.consumeTypeAnnotations leanDomain)) + (Ix.Theory.Named.AddInductive.consumeTypeAnnotations leanDomain)) ixOpen (leanBody.instantiate1 leanContext.freshExpr) /-- A production-validated active-family application becomes a valid flat - Lean4Lean target at the same related WHNF node. Parameter/universe + Ix.Theory.Named target at the same related WHNF node. Parameter/universe uniformity and index independence must be discharged from `valid`; they are not hidden in a whole-trace premise. -/ direct : ∀ {ixState leanContext ixResult leanResult id us info args groups @@ -176,36 +176,36 @@ structure FlatPositivityTraceTransport ValidPositiveRecursiveApplication id us args groups rootAddrs methods ixState final → ∃ targetIdx, - Lean4Lean.AddInductive.hasIndOcc stats.indConsts leanResult = true ∧ + Ix.Theory.Named.AddInductive.hasIndOcc stats.indConsts leanResult = true ∧ leanResult.isForall = false ∧ - Lean4Lean.AddInductive.isValidIndApp? stats leanResult = some targetIdx + Ix.Theory.Named.AddInductive.isValidIndApp? stats leanResult = some targetIdx namespace FlatPositivityTraceTransport -/-- Assemble Lean4Lean's exact retained positivity trace from a direct-only +/-- Assemble Ix.Theory.Named's exact retained positivity trace from a direct-only successful production trace. The result is wrapped in `Nonempty` because the Ix execution trace lives in `Prop`; this keeps the recursion within propositional elimination while still -supplying the Type-valued Lean4Lean trace to downstream semantic consumers. -/ +supplying the Type-valued Ix.Theory.Named trace to downstream semantic consumers. -/ theorem constructorPositivityTrace - {stats : Lean4Lean.AddInductive.InductiveStats} + {stats : Ix.Theory.Named.AddInductive.InductiveStats} {rootAddrs : Array Address} {methods : Methods .anon} {SourceRel ResultRel : TcState .anon → - Lean4Lean.AddInductive.Context → + Ix.Theory.Named.AddInductive.Context → KExpr .anon → Lean.Expr → Prop} (transport : FlatPositivityTraceTransport stats rootAddrs methods SourceRel ResultRel) : ∀ {groups : Array (PositivityGroup .anon)} {activeAddrs : Array Address} {fuel : Nat} {ixSource : KExpr .anon} {ixInitial ixFinal : TcState .anon} - {leanContext : Lean4Lean.AddInductive.Context} + {leanContext : Ix.Theory.Named.AddInductive.Context} {leanSource : Lean.Expr} {ctor : Lean.Name} {argIdx : Nat} (_trace : FlatPositivityDomainTrace groups activeAddrs methods fuel ixSource ixInitial ixFinal), groups[0]?.map (·.addrs) = some rootAddrs → SourceRel ixInitial leanContext ixSource leanSource → - Nonempty (Lean4Lean.AddInductive.ConstructorPositivityTrace stats ctor + Nonempty (Ix.Theory.Named.AddInductive.ConstructorPositivityTrace stats ctor argIdx leanContext leanSource fuel) | groups, activeAddrs, _, _, _, _, leanContext, leanSource, ctor, argIdx, .rootFree (fuel := innerFuel) (rootGroup := rootGroup) root free, @@ -228,14 +228,14 @@ theorem constructorPositivityTrace domainRelated, openRelated⟩ := transport.forallE resultRelated subst leanResult have leanDomainFree : - Lean4Lean.AddInductive.hasIndOcc stats.indConsts leanDomain = + Ix.Theory.Named.AddInductive.hasIndOcc stats.indConsts leanDomain = false := by rw [← transport.mentions domainRelated] simpa [rootAddrsEq] using domainFree have tailRelated := openRelated opening obtain ⟨leanTail⟩ := constructorPositivityTrace transport tail rootMatches tailRelated - cases leanOccurs : Lean4Lean.AddInductive.hasIndOcc stats.indConsts + cases leanOccurs : Ix.Theory.Named.AddInductive.hasIndOcc stats.indConsts (.forallE leanName leanDomain leanBody leanBinder) with | false => exact ⟨.absent leanContext leanSource @@ -277,13 +277,13 @@ remains the theorem below. Concrete instances must derive this field from an exact auxiliary request, its `NestedAuxiliaryHeaderRel`, physical `FlatAuxPresent` evidence, and the -candidate-level relation identifying that member with the Lean4Lean target. +candidate-level relation identifying that member with the Ix.Theory.Named target. This makes the flat-block correspondence visible at the only control-flow point where the two kernels' source syntax differs. -/ structure FlattenedPositivityTraceTransport - (stats : Lean4Lean.AddInductive.InductiveStats) + (stats : Ix.Theory.Named.AddInductive.InductiveStats) (rootAddrs : Array Address) (methods : Methods .anon) - (SourceRel ResultRel : TcState .anon → Lean4Lean.AddInductive.Context → + (SourceRel ResultRel : TcState .anon → Ix.Theory.Named.AddInductive.Context → KExpr .anon → Lean.Expr → Prop) : Prop extends FlatPositivityTraceTransport stats rootAddrs methods SourceRel ResultRel where @@ -297,37 +297,37 @@ structure FlattenedPositivityTraceTransport CompleteNestedPositivityApplicationTrace fuel id us args groups rootAddrs activeAddrs methods ixState final → ∃ targetIdx, - Lean4Lean.AddInductive.hasIndOcc stats.indConsts leanResult = true ∧ + Ix.Theory.Named.AddInductive.hasIndOcc stats.indConsts leanResult = true ∧ leanResult.isForall = false ∧ - Lean4Lean.AddInductive.isValidIndApp? stats leanResult = some targetIdx + Ix.Theory.Named.AddInductive.isValidIndApp? stats leanResult = some targetIdx namespace FlattenedPositivityTraceTransport -/-- Assemble Lean4Lean's retained positivity trace from the exhaustive +/-- Assemble Ix.Theory.Named's retained positivity trace from the exhaustive production traversal, including applications eliminated into exact generated flat auxiliaries. As in the direct-only adapter, `Nonempty` keeps elimination of the -proof-valued Ix trace within `Prop` while exposing Lean4Lean's Type-valued +proof-valued Ix trace within `Prop` while exposing Ix.Theory.Named's Type-valued trace to the enclosing constructor-validation proof. -/ theorem constructorPositivityTrace - {stats : Lean4Lean.AddInductive.InductiveStats} + {stats : Ix.Theory.Named.AddInductive.InductiveStats} {rootAddrs : Array Address} {methods : Methods .anon} {SourceRel ResultRel : TcState .anon → - Lean4Lean.AddInductive.Context → + Ix.Theory.Named.AddInductive.Context → KExpr .anon → Lean.Expr → Prop} (transport : FlattenedPositivityTraceTransport stats rootAddrs methods SourceRel ResultRel) : ∀ {groups : Array (PositivityGroup .anon)} {activeAddrs : Array Address} {fuel : Nat} {ixSource : KExpr .anon} {ixInitial ixFinal : TcState .anon} - {leanContext : Lean4Lean.AddInductive.Context} + {leanContext : Ix.Theory.Named.AddInductive.Context} {leanSource : Lean.Expr} {ctor : Lean.Name} {argIdx : Nat} (_trace : PositivityDomainTrace groups activeAddrs methods fuel ixSource ixInitial ixFinal), groups[0]?.map (·.addrs) = some rootAddrs → SourceRel ixInitial leanContext ixSource leanSource → - Nonempty (Lean4Lean.AddInductive.ConstructorPositivityTrace stats ctor + Nonempty (Ix.Theory.Named.AddInductive.ConstructorPositivityTrace stats ctor argIdx leanContext leanSource fuel) | groups, activeAddrs, _, _, _, _, leanContext, leanSource, ctor, argIdx, .rootFree (fuel := innerFuel) (rootGroup := rootGroup) root free, @@ -350,14 +350,14 @@ theorem constructorPositivityTrace domainRelated, openRelated⟩ := transport.forallE resultRelated subst leanResult have leanDomainFree : - Lean4Lean.AddInductive.hasIndOcc stats.indConsts leanDomain = + Ix.Theory.Named.AddInductive.hasIndOcc stats.indConsts leanDomain = false := by rw [← transport.mentions domainRelated] simpa [rootAddrsEq] using domainFree have tailRelated := openRelated opening obtain ⟨leanTail⟩ := constructorPositivityTrace transport tail rootMatches tailRelated - cases leanOccurs : Lean4Lean.AddInductive.hasIndOcc stats.indConsts + cases leanOccurs : Ix.Theory.Named.AddInductive.hasIndOcc stats.indConsts (.forallE leanName leanDomain leanBody leanBinder) with | false => exact ⟨.absent leanContext leanSource @@ -389,4 +389,4 @@ theorem constructorPositivityTrace end FlattenedPositivityTraceTransport -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/PositivityTraversal.lean b/Ix/Kernel/Verify/Inductive/PositivityTraversal.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/PositivityTraversal.lean rename to Ix/Kernel/Verify/Inductive/PositivityTraversal.lean index 4db3e0f54..338a5867d 100644 --- a/Ix/Tc/Verify/Inductive/PositivityTraversal.lean +++ b/Ix/Kernel/Verify/Inductive/PositivityTraversal.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Inductive.OccurrenceValidation +import Ix.Kernel.Verify.Inductive.OccurrenceValidation /-! # Production positivity traversal @@ -14,7 +14,7 @@ its real intermediate checker state. The final theorem composes that equation with the oracle-free occurrence invariant from `OccurrenceValidation`. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM private theorem runTryCatch (body : RecM m α) @@ -324,4 +324,4 @@ theorem checkPositivityDomainFuel_forall_negative rfl end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/ProducedGenerationTransaction.lean b/Ix/Kernel/Verify/Inductive/ProducedGenerationTransaction.lean similarity index 91% rename from Ix/Tc/Verify/Inductive/ProducedGenerationTransaction.lean rename to Ix/Kernel/Verify/Inductive/ProducedGenerationTransaction.lean index a142181f7..0f6b94cc2 100644 --- a/Ix/Tc/Verify/Inductive/ProducedGenerationTransaction.lean +++ b/Ix/Kernel/Verify/Inductive/ProducedGenerationTransaction.lean @@ -1,25 +1,25 @@ -import Ix.Tc.Verify.Inductive.Certificate -import Lean4Lean.Verify.Environment.ConstructorValidation +import Ix.Kernel.Verify.Inductive.Certificate +import Ix.Theory.Named.Verify.Environment.ConstructorValidation /-! # Producer-linked inductive-generation transactions `CertifiedGenerationTransaction` is intentionally Theory-only: it retains the generation certificate and the exact `VEnv.addInductCertified` result, but it -does not remember which ordinary Lean4Lean metadata execution selected that +does not remember which ordinary Ix.Theory.Named metadata execution selected that certificate. For E2c we need both facts at once. A successful outer producer call must not be allowed to justify Theory semantics by itself, and an independently chosen Theory certificate must not be passed off as the result of that producer. The -record below therefore owns Lean4Lean's dependent +record below therefore owns Ix.Theory.Named's dependent `ProducedGenerationCandidatePackage`, then erases it through the existing Theory-only transaction only at the consumer boundary. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean +open Ix.Theory.Named /-- One exact producer-selected singleton package together with its successful certified Theory insertion and well-formed input environment. @@ -98,7 +98,7 @@ end ProducedGenerationTransaction /-! ## Exact dependent producer transactions -/ -/-- The L4L-01E producer closure before source and generation indices are +/-- The Spec-01E producer closure before source and generation indices are erased. Its type retains the exact raw family, kernel source, producer arguments, normalized source declaration, and checked generation selected by one successful outer candidate execution. @@ -110,7 +110,7 @@ generation that merely happens to have the same erased package type. -/ structure ExactProducedGenerationTransaction {source : VInductDecl} {raw : VInductiveType} {kernelSource : Lean.InductiveType} {numNested : Nat} - {isUnsafe : Bool} {context : Lean4Lean.AddInductive.Context} + {isUnsafe : Bool} {context : Ix.Theory.Named.AddInductive.Context} (before after : VEnv) (Us : List Name) (producedCandidate : VInductDecl.ProducedGenerationShapeCandidate source raw kernelSource numNested isUnsafe context) @@ -129,7 +129,7 @@ consumer boundary. -/ noncomputable def toProduced {source : VInductDecl} {raw : VInductiveType} {kernelSource : Lean.InductiveType} {numNested : Nat} - {isUnsafe : Bool} {context : Lean4Lean.AddInductive.Context} + {isUnsafe : Bool} {context : Ix.Theory.Named.AddInductive.Context} {before after : VEnv} {Us : List Name} {producedCandidate : VInductDecl.ProducedGenerationShapeCandidate source raw kernelSource numNested isUnsafe context} @@ -144,7 +144,7 @@ noncomputable def toProduced @[simp] theorem toProduced_source {source : VInductDecl} {raw : VInductiveType} {kernelSource : Lean.InductiveType} {numNested : Nat} - {isUnsafe : Bool} {context : Lean4Lean.AddInductive.Context} + {isUnsafe : Bool} {context : Ix.Theory.Named.AddInductive.Context} {before after : VEnv} {Us : List Name} {producedCandidate : VInductDecl.ProducedGenerationShapeCandidate source raw kernelSource numNested isUnsafe context} @@ -156,7 +156,7 @@ noncomputable def toProduced @[simp] theorem toProduced_generation {source : VInductDecl} {raw : VInductiveType} {kernelSource : Lean.InductiveType} {numNested : Nat} - {isUnsafe : Bool} {context : Lean4Lean.AddInductive.Context} + {isUnsafe : Bool} {context : Ix.Theory.Named.AddInductive.Context} {before after : VEnv} {Us : List Name} {producedCandidate : VInductDecl.ProducedGenerationShapeCandidate source raw kernelSource numNested isUnsafe context} @@ -170,7 +170,7 @@ intentional erasure. -/ theorem facts {source : VInductDecl} {raw : VInductiveType} {kernelSource : Lean.InductiveType} {numNested : Nat} - {isUnsafe : Bool} {context : Lean4Lean.AddInductive.Context} + {isUnsafe : Bool} {context : Ix.Theory.Named.AddInductive.Context} {before after : VEnv} {Us : List Name} {producedCandidate : VInductDecl.ProducedGenerationShapeCandidate source raw kernelSource numNested isUnsafe context} @@ -181,4 +181,4 @@ theorem facts end ExactProducedGenerationTransaction -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/RecursivePiAcceptance.lean b/Ix/Kernel/Verify/Inductive/RecursivePiAcceptance.lean similarity index 95% rename from Ix/Tc/Verify/Inductive/RecursivePiAcceptance.lean rename to Ix/Kernel/Verify/Inductive/RecursivePiAcceptance.lean index 23bfa4832..e6df65ae8 100644 --- a/Ix/Tc/Verify/Inductive/RecursivePiAcceptance.lean +++ b/Ix/Kernel/Verify/Inductive/RecursivePiAcceptance.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Check.BlockAcceptance -import Ix.Tc.Verify.Inductive.RecursivePiFixture +import Ix.Kernel.Verify.Check.BlockAcceptance +import Ix.Kernel.Verify.Inductive.RecursivePiFixture /-! # Production acceptance of the recursive-Pi family @@ -9,7 +9,7 @@ This module joins three independently checked facts about the same physical * anonymous ingress produced its exact family/constructor member array; * the production family checker accepted that exact array; and -* the Lean4Lean certificate gives every member its stable Theory meaning in +* the Ix.Theory.Named certificate gives every member its stable Theory meaning in one atomic semantic transition. The result is deliberately limited to the family block. The separately @@ -17,10 +17,10 @@ generated `Acc.rec` declaration and its recursive-Pi iota rule are the next slice. -/ -namespace Ix.Tc.RecursivePiFixture +namespace Ix.Kernel.RecursivePiFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures open RecursivePiCertificateFixture local instance acceptanceAnonKIdDecidableEq : DecidableEq (KId .anon) := @@ -193,4 +193,4 @@ theorem checkedSemanticAdmission : CheckedSemanticAdmission where admitted := familyAtomicAdmission recursivePi := breadth -end Ix.Tc.RecursivePiFixture +end Ix.Kernel.RecursivePiFixture diff --git a/Ix/Tc/Verify/Inductive/RecursivePiAdmission.lean b/Ix/Kernel/Verify/Inductive/RecursivePiAdmission.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/RecursivePiAdmission.lean rename to Ix/Kernel/Verify/Inductive/RecursivePiAdmission.lean index 05703e0f3..0d02dcc1b 100644 --- a/Ix/Tc/Verify/Inductive/RecursivePiAdmission.lean +++ b/Ix/Kernel/Verify/Inductive/RecursivePiAdmission.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Inductive.RecursivePiSoundness -import Ix.Tc.Verify.Inductive.OneFamilyAdmission +import Ix.Kernel.Verify.Inductive.RecursivePiSoundness +import Ix.Kernel.Verify.Inductive.OneFamilyAdmission /-! # Oracle-free recursive-Pi admission @@ -12,10 +12,10 @@ No `InductiveOracle`, ambient future-world choice, or sequential stand-in for a mutual declaration is used. -/ -namespace Ix.Tc.RecursivePiRecursorFixture +namespace Ix.Kernel.RecursivePiRecursorFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures open RecursivePiCertificateFixture open RecursivePiFixture @@ -386,4 +386,4 @@ theorem recursivePiAtomicClosure : RecursivePiAtomicClosure where iota := RecursivePiPattern.patternRel concreteRule_ruleAt recursivePi := RecursivePiCertificateFixture.breadth -end Ix.Tc.RecursivePiRecursorFixture +end Ix.Kernel.RecursivePiRecursorFixture diff --git a/Ix/Tc/Verify/Inductive/RecursivePiCertificate.lean b/Ix/Kernel/Verify/Inductive/RecursivePiCertificate.lean similarity index 89% rename from Ix/Tc/Verify/Inductive/RecursivePiCertificate.lean rename to Ix/Kernel/Verify/Inductive/RecursivePiCertificate.lean index da449432e..f6f4a4824 100644 --- a/Ix/Tc/Verify/Inductive/RecursivePiCertificate.lean +++ b/Ix/Kernel/Verify/Inductive/RecursivePiCertificate.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Inductive.Certificate -import Lean4Lean.Theory.InductiveFixtures +import Ix.Kernel.Verify.Inductive.Certificate +import Ix.Theory.Named.InductiveFixtures /-! # Certified recursive-Pi generation fixture @@ -10,18 +10,18 @@ the recursive argument is not a direct family application, and its induction hypothesis is itself a function. This module stays on the Theory-only side of the boundary. It constructs the -public proof-carrying Lean4Lean transaction directly from `accDecl_wf`; it +public proof-carrying Ix.Theory.Named transaction directly from `accDecl_wf`; it does not import the reflected Lean-environment replay or assert any Ix catalog correspondence. Production ingress, constructor traversal, and generated recursor comparison remain separate obligations. -/ -namespace Ix.Tc.RecursivePiCertificateFixture +namespace Ix.Kernel.RecursivePiCertificateFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures -/-- The public Lean4Lean certificate for the actual `Acc` declaration. -/ +/-- The public Ix.Theory.Named certificate for the actual `Acc` declaration. -/ def certificate : accDecl.GenerationCertificate VEnv.empty where generation := accChecked.identityGeneration wf := (accChecked.wf_of_decl accDecl_wf).identityGeneration .empty @@ -87,4 +87,4 @@ theorem certifiedFacts : CertifiedGenerationFacts VEnv.empty finalEnv transaction.certificate := transaction.facts -end Ix.Tc.RecursivePiCertificateFixture +end Ix.Kernel.RecursivePiCertificateFixture diff --git a/Ix/Tc/Verify/Inductive/RecursivePiFixture.lean b/Ix/Kernel/Verify/Inductive/RecursivePiFixture.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/RecursivePiFixture.lean rename to Ix/Kernel/Verify/Inductive/RecursivePiFixture.lean index 0d288d901..ea6807196 100644 --- a/Ix/Tc/Verify/Inductive/RecursivePiFixture.lean +++ b/Ix/Kernel/Verify/Inductive/RecursivePiFixture.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Inductive.ConcreteFixture -import Ix.Tc.Verify.Inductive.RecursivePiCertificate +import Ix.Kernel.Verify.Inductive.ConcreteFixture +import Ix.Kernel.Verify.Inductive.RecursivePiCertificate /-! # Production recursive-Pi fixture @@ -16,10 +16,10 @@ check. Semantic catalog linkage and generated-recursor admission are layered on these exact executions rather than assumed by the fixture. -/ -namespace Ix.Tc.RecursivePiFixture +namespace Ix.Kernel.RecursivePiFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures open RecursivePiCertificateFixture open InductiveConcreteFixture @@ -441,4 +441,4 @@ def familyLink : SingletonFamilyCatalogLink RawProjRel.none world.catalog interpretation.toCatalogLinkOfEntries ingressExecution catalogEntry trustedCatalog -end Ix.Tc.RecursivePiFixture +end Ix.Kernel.RecursivePiFixture diff --git a/Ix/Tc/Verify/Inductive/RecursivePiPattern.lean b/Ix/Kernel/Verify/Inductive/RecursivePiPattern.lean similarity index 96% rename from Ix/Tc/Verify/Inductive/RecursivePiPattern.lean rename to Ix/Kernel/Verify/Inductive/RecursivePiPattern.lean index e96311acc..d74c85737 100644 --- a/Ix/Tc/Verify/Inductive/RecursivePiPattern.lean +++ b/Ix/Kernel/Verify/Inductive/RecursivePiPattern.lean @@ -1,11 +1,13 @@ -import Ix.Tc.Verify.Inductive.IotaPattern -import Ix.Tc.Verify.Inductive.RecursivePiRecursorFixture +import Ix.Kernel.Verify.Inductive.IotaPattern +import Ix.Kernel.Verify.Inductive.RecursivePiRecursorFixture + +open Ix.Theory (VLevel) /-! # Recursive-Pi iota pattern The generated `Acc.intro` equation is the first supported iota rule whose -recursive call occurs beneath a function telescope. Lean4Lean's pattern RHS +recursive call occurs beneath a function telescope. Ix.Theory.Named's pattern RHS language intentionally contains only closed constants, applications, and captures; it has no primitive lambda constructor. We retain exact alignment with the registered equation by using its complete closed RHS lambda as a @@ -14,10 +16,10 @@ Beta reduction of that application constructs the two-binder induction hypothesis without adding an unverified pattern-language extension. -/ -namespace Ix.Tc.RecursivePiPattern +namespace Ix.Kernel.RecursivePiPattern -open Lean4Lean -open Lean4Lean.InductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures open RecursivePiCertificateFixture open RecursivePiRecursorFixture @@ -229,4 +231,4 @@ theorem metadata {rule : RecRule .anon} simpa only [pattern, introFieldCount, show (2 : UInt64).toNat = 2 from rfl] using hfields -end Ix.Tc.RecursivePiPattern +end Ix.Kernel.RecursivePiPattern diff --git a/Ix/Tc/Verify/Inductive/RecursivePiRecursorFixture.lean b/Ix/Kernel/Verify/Inductive/RecursivePiRecursorFixture.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/RecursivePiRecursorFixture.lean rename to Ix/Kernel/Verify/Inductive/RecursivePiRecursorFixture.lean index f73c5de7d..43c7e018e 100644 --- a/Ix/Tc/Verify/Inductive/RecursivePiRecursorFixture.lean +++ b/Ix/Kernel/Verify/Inductive/RecursivePiRecursorFixture.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Inductive.RecursivePiAcceptance +import Ix.Kernel.Verify.Inductive.RecursivePiAcceptance /-! # Production recursive-Pi recursor fixture @@ -12,10 +12,10 @@ environment. Thus successful comparison exercises the production recursor generator and rule builder, not merely the Theory-side certificate. -/ -namespace Ix.Tc.RecursivePiRecursorFixture +namespace Ix.Kernel.RecursivePiRecursorFixture -open Lean4Lean -open Lean4Lean.InductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures open RecursivePiCertificateFixture open RecursivePiFixture open InductiveConcreteFixture @@ -684,4 +684,4 @@ def recursorLink : SingletonRecursorCatalogLink RawProjRel.none world.catalog recursorInterpretation.toCatalogLinkOfEntry recursorIngressExecution catalog_recursor trustedCatalog -end Ix.Tc.RecursivePiRecursorFixture +end Ix.Kernel.RecursivePiRecursorFixture diff --git a/Ix/Tc/Verify/Inductive/RecursivePiSoundness.lean b/Ix/Kernel/Verify/Inductive/RecursivePiSoundness.lean similarity index 94% rename from Ix/Tc/Verify/Inductive/RecursivePiSoundness.lean rename to Ix/Kernel/Verify/Inductive/RecursivePiSoundness.lean index cee669019..e880344ef 100644 --- a/Ix/Tc/Verify/Inductive/RecursivePiSoundness.lean +++ b/Ix/Kernel/Verify/Inductive/RecursivePiSoundness.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Inductive.RecursivePiPattern +import Ix.Kernel.Verify.Inductive.RecursivePiPattern + +open Ix.Theory (VLevel) /-! # Recursive-Pi iota soundness @@ -11,10 +13,10 @@ application, so the functional induction hypothesis beneath two lambdas is not reconstructed by an independent rewrite axiom. -/ -namespace Ix.Tc.RecursivePiPattern +namespace Ix.Kernel.RecursivePiPattern -open Lean4Lean -open Lean4Lean.InductiveFixtures +open Ix.Theory.Named +open Ix.Theory.Named.InductiveFixtures open RecursivePiCertificateFixture open RecursivePiRecursorFixture @@ -377,7 +379,7 @@ theorem patternSound {rule : RecRule .anon} hconstructorApplied⟩ := htype.app_inv hfutureWF.ordered hGamma obtain ⟨recursorHeadType, hrecursorHeadTyped⟩ := - Lean4Lean.VEnv.HasType.appN_head hfutureWF hGamma hrecursorApplied + Ix.Theory.Named.VEnv.HasType.appN_head hfutureWF hGamma hrecursorApplied obtain ⟨recursorConstant, hrecursorLookup, hlevelsWF, hlevelsArity⟩ := hrecursorHeadTyped.const_inv hfutureWF.ordered hGamma have hcertifiedRecursorLookup := @@ -402,7 +404,7 @@ theorem patternSound {rule : RecRule .anon} subst levelTail obtain ⟨constructorHeadType, hconstructorHeadTyped⟩ := - Lean4Lean.VEnv.HasType.appN_head hfutureWF hGamma hconstructorApplied + Ix.Theory.Named.VEnv.HasType.appN_head hfutureWF hGamma hconstructorApplied obtain ⟨constructorConstant, hconstructorLookup, hconstructorLevelsWF, hconstructorLevelsArity⟩ := hconstructorHeadTyped.const_inv hfutureWF.ordered hGamma @@ -437,7 +439,7 @@ theorem patternSound {rule : RecRule .anon} have hrecursorConstantTyped : future.HasType uvars Gamma (.const ``Acc.rec [v, u]) (generation.recType.instL [v, u]) := by - have htyped := Lean4Lean.VEnv.HasType.const (Γ := Gamma) + have htyped := Ix.Theory.Named.VEnv.HasType.const (Γ := Gamma) hcertifiedRecursorLookup hlevelsWF hlevelsArity rw [generationRecursorName] at htyped simpa [VInductDecl.GenerationChecked.recursor] using htyped @@ -453,19 +455,19 @@ theorem patternSound {rule : RecRule .anon} (.forallE majorDomain majorBody) := by simpa using hrecursorApplied obtain ⟨recursorParameterResult, hrecursorParameterApplied⟩ := - Lean4Lean.VEnv.HasType.appN_prefix hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.appN_prefix hfutureWF hGamma (prefixArgs := [alpha]) (suffixArgs := [relation, motive, minor, index]) hrecursorAppliedSplit have hrecursorAlpha : future.HasType uvars Gamma alpha (.sort u) := - Lean4Lean.VEnv.HasType.app_argument_of_head hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.app_argument_of_head hfutureWF hGamma hrecursorParameterApplied hrecursorParameterHead have hconstructorConstantTyped : future.HasType uvars Gamma (.const ``Acc.intro [constructorU]) (normalized.raw.toVConstant.type.instL [constructorU]) := by simpa [normalizedName] using - (Lean4Lean.VEnv.HasType.const (Γ := Gamma) + (Ix.Theory.Named.VEnv.HasType.const (Γ := Gamma) hcertifiedConstructorLookup hconstructorLevelsWF hconstructorLevelsArity) have hconstructorParameterHead : future.HasType uvars Gamma @@ -482,13 +484,13 @@ theorem patternSound {rule : RecRule .anon} majorDomain := by simpa using hconstructorApplied obtain ⟨constructorParameterResult, hconstructorParameterApplied⟩ := - Lean4Lean.VEnv.HasType.appN_prefix hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.appN_prefix hfutureWF hGamma (prefixArgs := [constructorAlpha]) (suffixArgs := [constructorRelation, constructorIndex, recursiveField]) hconstructorAppliedSplit have hconstructorAlpha : future.HasType uvars Gamma constructorAlpha (.sort constructorU) := - Lean4Lean.VEnv.HasType.app_argument_of_head hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.app_argument_of_head hfutureWF hGamma hconstructorParameterApplied hconstructorParameterHead obtain ⟨parameterType, hparameterTyped⟩ := hparameterEq @@ -502,7 +504,7 @@ theorem patternSound {rule : RecRule .anon} (.sort u) (.sort constructorU) := hrecursorSort.trans hfutureWF hGamma hconstructorSort.symm have huniverse : u ≈ constructorU := - Lean4Lean.VEnv.IsDefEqU.sort_inv hfutureWF hGamma hsorts + Ix.Theory.Named.VEnv.IsDefEqU.sort_inv hfutureWF hGamma hsorts have hintroLookup := hcertifiedConstructorLookup rw [normalizedName] at hintroLookup @@ -519,7 +521,7 @@ theorem patternSound {rule : RecRule .anon} rw [constructorType_parameter] at hconstructorConstantEq have hparameterAtConstructor : future.IsDefEq uvars Gamma constructorAlpha alpha (.sort constructorU) := - Lean4Lean.VEnv.IsDefEqU.defeqDF hfutureWF hGamma + Ix.Theory.Named.VEnv.IsDefEqU.defeqDF hfutureWF hGamma hconstructorSort.symm hparameterTyped.symm have hconstructorAlphaPrefixEq : future.IsDefEqU uvars Gamma (.app (.const ``Acc.intro [constructorU]) constructorAlpha) @@ -527,7 +529,7 @@ theorem patternSound {rule : RecRule .anon} ⟨_, .appDF hconstructorConstantEq hparameterAtConstructor⟩ obtain ⟨constructorParamsResult, hconstructorParamsApplied⟩ := - Lean4Lean.VEnv.HasType.appN_prefix hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.appN_prefix hfutureWF hGamma (prefixArgs := [constructorAlpha, constructorRelation]) (suffixArgs := [constructorIndex, recursiveField]) hconstructorAppliedSplit @@ -548,11 +550,11 @@ theorem patternSound {rule : RecRule .anon} (VExpr.appN (.const ``Acc.intro [u]) [alpha, relation]) := by refine ⟨relationBody.inst constructorRelation, ?_⟩ simpa only [VExpr.appN] using - (Lean4Lean.VEnv.IsDefEq.appDF hconstructorAlphaPrefixEqTyped + (Ix.Theory.Named.VEnv.IsDefEq.appDF hconstructorAlphaPrefixEqTyped hrelationAtConstructor) obtain ⟨constructorIndexResult, hconstructorIndexApplied⟩ := - Lean4Lean.VEnv.HasType.appN_prefix hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.appN_prefix hfutureWF hGamma (prefixArgs := [constructorAlpha, constructorRelation, constructorIndex]) (suffixArgs := [recursiveField]) hconstructorAppliedSplit @@ -575,7 +577,7 @@ theorem patternSound {rule : RecRule .anon} [alpha, relation, index]) := by refine ⟨indexBody.inst constructorIndex, ?_⟩ simpa only [VExpr.appN] using - (Lean4Lean.VEnv.IsDefEq.appDF hconstructorParamsEqTyped + (Ix.Theory.Named.VEnv.IsDefEq.appDF hconstructorParamsEqTyped hindexAtConstructor) obtain ⟨fieldDomain, fieldBody, hconstructorIndexHead, hrecursiveFieldTyped⟩ := @@ -593,7 +595,7 @@ theorem patternSound {rule : RecRule .anon} (VExpr.appN (.const ``Acc.intro [u]) [alpha, relation, index, recursiveField]) := by simpa only [VExpr.appN] using - (Lean4Lean.VEnv.IsDefEq.appN_same hfutureWF hGamma + (Ix.Theory.Named.VEnv.IsDefEq.appN_same hfutureWF hGamma hconstructorIndexEqTyped hconstructorApplied) have hconstructorEqTyped : future.IsDefEq uvars Gamma (VExpr.appN (.const ``Acc.intro [constructorU]) @@ -652,7 +654,7 @@ theorem patternSound {rule : RecRule .anon} (.forallE majorDomain majorBody) := by simpa using hrecursorApplied obtain ⟨recursorCommonResult, hrecursorCommonApplied⟩ := - Lean4Lean.VEnv.HasType.appN_prefix hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.appN_prefix hfutureWF hGamma (prefixArgs := [alpha, relation, motive, minor]) (suffixArgs := [index]) hrecursorCommonAppliedSplit have hcommonLength : @@ -662,14 +664,14 @@ theorem patternSound {rule : RecRule .anon} (VExpr.appN ((generation.rule 0 normalized).lhs.instL [v, u]) [alpha, relation, motive, minor]) (equationFieldType v u alpha relation motive minor) := - Lean4Lean.VEnv.HasType.transfer_appN_telescope_instRev + Ix.Theory.Named.VEnv.HasType.transfer_appN_telescope_instRev hfutureWF hGamma hcommonLength hrecursorCommonApplied hrecursorCommonType hequationLhsCommonType have hcanonicalConstructorConstantTyped : future.HasType uvars Gamma (.const ``Acc.intro [u]) (normalized.raw.toVConstant.type.instL [u]) := - Lean4Lean.VEnv.HasType.const hintroLookup + Ix.Theory.Named.VEnv.HasType.const hintroLookup (fun level hlevel => by simp only [List.mem_singleton] at hlevel subst level @@ -682,7 +684,7 @@ theorem patternSound {rule : RecRule .anon} rw [← constructorType_parameter] exact hcanonicalConstructorConstantTyped obtain ⟨recursorParamsResult, hrecursorParamsApplied⟩ := - Lean4Lean.VEnv.HasType.appN_prefix hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.appN_prefix hfutureWF hGamma (prefixArgs := [alpha, relation]) (suffixArgs := [motive, minor, index]) hrecursorAppliedSplit have hrecursorAlphaHead : future.HasType uvars Gamma @@ -690,13 +692,13 @@ theorem patternSound {rule : RecRule .anon} (.forallE (relationType alpha) (VExpr.dropN 1 ((VExpr.dropN 1 (generation.recType.instL [v, u])).inst alpha))) := by - have happ := Lean4Lean.VEnv.HasType.app hrecursorParameterHead + have happ := Ix.Theory.Named.VEnv.HasType.app hrecursorParameterHead hrecursorAlpha rw [recType_relation] at happ exact happ have hrecursorRelation : future.HasType uvars Gamma relation (relationType alpha) := - Lean4Lean.VEnv.HasType.app_argument_of_head hfutureWF hGamma + Ix.Theory.Named.VEnv.HasType.app_argument_of_head hfutureWF hGamma hrecursorParamsApplied hrecursorAlphaHead have hcanonicalConstructorAlphaHead : future.HasType uvars Gamma (.app (.const ``Acc.intro [u]) alpha) @@ -704,14 +706,14 @@ theorem patternSound {rule : RecRule .anon} (VExpr.dropN 1 ((VExpr.dropN 1 (normalized.raw.toVConstant.type.instL [u])).inst alpha))) := by - have happ := Lean4Lean.VEnv.HasType.app + have happ := Ix.Theory.Named.VEnv.HasType.app hcanonicalConstructorParameterHead hrecursorAlpha rw [constructorType_relation] at happ exact happ have hcanonicalConstructorPrefix : future.HasType uvars Gamma (VExpr.appN (.const ``Acc.intro [u]) [alpha, relation]) (constructorFieldType u alpha relation) := by - have happ := Lean4Lean.VEnv.HasType.app + have happ := Ix.Theory.Named.VEnv.HasType.app hcanonicalConstructorAlphaHead hrecursorRelation change future.HasType uvars Gamma (.app (.app (.const ``Acc.intro [u]) alpha) relation) @@ -757,7 +759,7 @@ theorem patternSound {rule : RecRule .anon} VExpr.instL, VExpr.inst, VExpr.instVar, VExpr.telN, VExpr.dropN] have hequationLhsFieldsApplied := - Lean4Lean.VEnv.HasType.transfer_appN_telescope_instRev + Ix.Theory.Named.VEnv.HasType.transfer_appN_telescope_instRev hfutureWF hGamma hfieldLength hcanonicalConstructorApplied hconstructorFieldHead' hequationFieldHead have hequationLhsApplied : future.HasType uvars Gamma @@ -772,12 +774,12 @@ theorem patternSound {rule : RecRule .anon} VExpr.appN_append] exact hequationLhsFieldsApplied have hequationApplied := - Lean4Lean.VEnv.IsDefEq.appN_same hfutureWF hGamma hequation + Ix.Theory.Named.VEnv.IsDefEq.appN_same hfutureWF hGamma hequation hequationLhsApplied have hequationLhsApplied' := hequationLhsApplied rw [generatedRule_shape.1, VExpr.instL_lamN] at hequationLhsApplied' - have hlhsBeta := Lean4Lean.VEnv.HasType.lamN_appN_beta + have hlhsBeta := Ix.Theory.Named.VEnv.HasType.lamN_appN_beta hfutureWF hGamma (by simp) hequationLhsApplied' rw [lhsBody_open] at hlhsBeta have hlhsBeta' : future.IsDefEqU uvars Gamma @@ -818,4 +820,4 @@ theorem patternRel {rule : RecRule .anon} RawRecursorRulePatternRel.of_metadata_sound (metadata hrule) (patternSound hrule) -end Ix.Tc.RecursivePiPattern +end Ix.Kernel.RecursivePiPattern diff --git a/Ix/Tc/Verify/Inductive/RecursivePositivityTraversal.lean b/Ix/Kernel/Verify/Inductive/RecursivePositivityTraversal.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/RecursivePositivityTraversal.lean rename to Ix/Kernel/Verify/Inductive/RecursivePositivityTraversal.lean index 8e16bb435..c3ae6c621 100644 --- a/Ix/Tc/Verify/Inductive/RecursivePositivityTraversal.lean +++ b/Ix/Kernel/Verify/Inductive/RecursivePositivityTraversal.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Inductive.NestedPositivityTraversal +import Ix.Kernel.Verify.Inductive.NestedPositivityTraversal /-! # Exhaustive recursive positivity traversal @@ -11,7 +11,7 @@ terminal constant spine is either a validated active-family occurrence or a fully expanded nested-family traversal. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- The WHNF branch discriminator used by positivity after the forall case has been excluded. Naming it prevents indexed trace constructors from making @@ -298,4 +298,4 @@ theorem checkPositivityDomainFuel_success hterminal end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/ResultSortTelescope.lean b/Ix/Kernel/Verify/Inductive/ResultSortTelescope.lean similarity index 97% rename from Ix/Tc/Verify/Inductive/ResultSortTelescope.lean rename to Ix/Kernel/Verify/Inductive/ResultSortTelescope.lean index 02f1ec47a..bc22d6e41 100644 --- a/Ix/Tc/Verify/Inductive/ResultSortTelescope.lean +++ b/Ix/Kernel/Verify/Inductive/ResultSortTelescope.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Inductive -import Ix.Tc.Verify.Whnf.StructEta.ExactMajorTelescope +import Ix.Kernel.Inductive +import Ix.Kernel.Verify.Whnf.StructEta.ExactMajorTelescope /-! # Exact result-sort telescope restoration @@ -13,7 +13,7 @@ The theorem is deliberately operational. It grants no authority to a WHNF callback and makes no scoped suffix model admit the temporary telescope. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Pure syntax certificate for a fixed forall prefix ending in a sort. -/ @@ -100,4 +100,4 @@ theorem getResultSortLevel_direct_exact exact scratch_tryFinally_ok bodyRun cleanup end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/RuleApplication.lean b/Ix/Kernel/Verify/Inductive/RuleApplication.lean similarity index 98% rename from Ix/Tc/Verify/Inductive/RuleApplication.lean rename to Ix/Kernel/Verify/Inductive/RuleApplication.lean index b79d3f1c1..fcc975464 100644 --- a/Ix/Tc/Verify/Inductive/RuleApplication.lean +++ b/Ix/Kernel/Verify/Inductive/RuleApplication.lean @@ -1,9 +1,9 @@ -import Ix.Tc.Verify.Inductive.SingletonRecursor +import Ix.Kernel.Verify.Inductive.SingletonRecursor /-! # Applying closed generated equations -Lean4Lean registers generated iota equations as closed lambda telescopes, +Ix.Theory.Named registers generated iota equations as closed lambda telescopes, whereas the production Ix reducer sees the recursor application after that telescope has been supplied. These lemmas isolate the ordinary dependent beta reasoning needed to cross that boundary. @@ -15,7 +15,7 @@ lambda-wrapped equations. The later singleton pattern compiler must provide the exact telescope arguments and use the lemmas below. -/ -namespace Lean4Lean.VExpr +namespace Ix.Theory.Named.VExpr /-- Positional lookup in the reverse de Bruijn range used by generated rule telescopes. -/ @@ -51,9 +51,9 @@ theorem instRev_bvar_at (arguments : List VExpr) (index : Nat) rw [List.getElem?_eq_getElem hindex] at hrange exact Option.some.inj hrange -end Lean4Lean.VExpr +end Ix.Theory.Named.VExpr -namespace Lean4Lean.VEnv +namespace Ix.Theory.Named.VEnv /-- Typing an application spine also types its original head. This is a small inversion helper for the equation-application proofs below. -/ @@ -309,7 +309,7 @@ theorem HasType.beta_head_appN exact IsDefEq.appN_same henv hGamma hbeta hsource /-- Supplying exactly one argument per closed lambda binder beta-reduces to -Lean4Lean's outer-to-inner `instRev` operation. This is the reusable semantic +Ix.Theory.Named's outer-to-inner `instRev` operation. This is the reusable semantic bridge from a registered closed equation to an open generated rule body. -/ theorem HasType.lamN_appN_beta {env : VEnv} {U : Nat} {Gamma : List VExpr} @@ -360,4 +360,4 @@ theorem HasType.lamN_appN_beta simpa only [VExpr.lamN, VExpr.appN, VExpr.instRev, Nat.zero_add, hrestLength] using hcombined -end Lean4Lean.VEnv +end Ix.Theory.Named.VEnv diff --git a/Ix/Tc/Verify/Inductive/SingletonEnumeration.lean b/Ix/Kernel/Verify/Inductive/SingletonEnumeration.lean similarity index 96% rename from Ix/Tc/Verify/Inductive/SingletonEnumeration.lean rename to Ix/Kernel/Verify/Inductive/SingletonEnumeration.lean index b9beebddb..ff64eaac0 100644 --- a/Ix/Tc/Verify/Inductive/SingletonEnumeration.lean +++ b/Ix/Kernel/Verify/Inductive/SingletonEnumeration.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Inductive.IotaPattern +import Ix.Kernel.Verify.Inductive.IotaPattern + +open Ix.Theory (VLevel) /-! # Certified singleton enumerations @@ -14,9 +16,9 @@ retained by E2a. It is therefore a decidable fragment boundary around the actual generated artifacts, not a second inductive-declaration model. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VConstVal VEnv VExpr VInductDecl) +open Ix.Theory.Named (VConstVal VEnv VExpr VInductDecl) namespace CertifiedSingletonGeneration @@ -28,7 +30,7 @@ structure IsEnumeration {source : VInductDecl} noUniverses : source.uvars = 0 noParameters : source.nparams = 0 /-- E2b's enumeration pattern currently covers the ordinary large - eliminator. Small and K elimination use the L4L-06 universe layout and + eliminator. Small and K elimination use the Spec-06 universe layout and remain explicit E2c breadth cases. -/ largeElimination : generation.elimination = .large noIndices : generation.block.rawIndices = [] @@ -73,8 +75,8 @@ recursor, independently of the selected elimination mode. -/ (shape : IsEnumeration generation) : generation.sourceLevels = [] := by simp [VInductDecl.GenerationChecked.sourceLevels, - Lean4Lean.VInductDecl.ElimMode.sourceLevels, - Lean4Lean.VLevel.params', shape.noUniverses] + Ix.Theory.Named.VInductDecl.ElimMode.sourceLevels, + Ix.Theory.VLevel.params', shape.noUniverses] @[simp] theorem paramsTel_nil {source : VInductDecl} {generation : source.GenerationChecked} @@ -145,7 +147,7 @@ theorem rule_lhs {source : VInductDecl} shape.resultIndices_nil hconstructor] simp [ruleBinders, VExpr.liftTelN, VExpr.appN, VExpr.bvarRevRange, shape.noUniverses, shape.noParameters, - Lean4Lean.VLevel.params', shape.largeElimination] + Ix.Theory.VLevel.params', shape.largeElimination] /-- Exact generated right-hand side: enum rule `index` is the corresponding minor variable and has no field/IH application suffix. -/ @@ -168,7 +170,7 @@ theorem rule_rhs {source : VInductDecl} open a generated enumeration equation. -/ @[simp] theorem ruleBinders_instL_length {source : VInductDecl} {generation : source.GenerationChecked} - (levels : List Lean4Lean.VLevel) : + (levels : List Ix.Theory.VLevel) : ((ruleBinders generation).map (VExpr.instL levels)).length = generation.block.ctorPairs.length + 1 := by simp @@ -179,7 +181,7 @@ retains only its final major binder after that common prefix. -/ theorem recType_instantiated {source : VInductDecl} {generation : source.GenerationChecked} (shape : IsEnumeration generation) - (levels : List Lean4Lean.VLevel) : + (levels : List Ix.Theory.VLevel) : generation.recType.instL levels = VExpr.forallN ((ruleBinders generation).map (VExpr.instL levels)) @@ -202,7 +204,7 @@ theorem ruleType_instantiated {source : VInductDecl} (shape : IsEnumeration generation) {index : Nat} {normalized : VInductDecl.NormalizedCtor} (hconstructor : generation.block.ctorPairs[index]? = some normalized) - (levels : List Lean4Lean.VLevel) : + (levels : List Ix.Theory.VLevel) : (generation.rule index normalized).type.instL levels = VExpr.forallN ((ruleBinders generation).map (VExpr.instL levels)) @@ -224,7 +226,7 @@ theorem ruleLhsBody_instantiated {source : VInductDecl} {generation : source.GenerationChecked} (shape : IsEnumeration generation) (normalized : VInductDecl.NormalizedCtor) - (levels : List Lean4Lean.VLevel) (arguments : List VExpr) + (levels : List Ix.Theory.VLevel) (arguments : List VExpr) (hlevels : levels.length = generation.recUvars) (harguments : arguments.length = generation.block.ctorPairs.length + 1) : @@ -245,7 +247,7 @@ theorem ruleLhsBody_instantiated {source : VInductDecl} arguments) (.const normalized.raw.name []) := by simp only [VExpr.instL, VExpr.instL_appN, - Lean4Lean.VLevel.inst_map_id hlevels, + Ix.Theory.VLevel.inst_map_id hlevels, shape.sourceLevels_nil, VInductDecl.bvarRevRange_instL] change VExpr.instRev @@ -270,7 +272,7 @@ left-to-right minor argument encoded by the dependent pattern path. -/ theorem ruleRhsBody_instantiated {source : VInductDecl} {generation : source.GenerationChecked} (index : Nat) (hindex : index < generation.block.ctorPairs.length) - (levels : List Lean4Lean.VLevel) (arguments : List VExpr) + (levels : List Ix.Theory.VLevel) (arguments : List VExpr) (harguments : arguments.length = generation.block.ctorPairs.length + 1) : VExpr.instRev @@ -464,7 +466,7 @@ theorem enumerationPatternMetadata /-- The compiled enum pattern is semantically justified by the exact registered generated equation. This is the central E2b bridge: a successful -pattern match is reduced through Lean4Lean's registered equality, rather than +pattern match is reduced through Ix.Theory.Named's registered equality, rather than through an independently postulated iota law. -/ theorem enumerationPatternSound {trProj : RawProjRel} {catalog : Catalog} @@ -491,7 +493,7 @@ theorem enumerationPatternSound generation.block.ctorPairs.length := by rw [family.constructorCount, ← generation.rawCtors_eq] simp - change Lean4Lean.Pattern.Matches + change Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern (.str generation.block.sourceType.name "rec") (family.constructorIds.size + 1) normalized.raw.name 0) @@ -506,7 +508,7 @@ theorem enumerationPatternSound htype.app_inv hfutureWF.ordered hGamma have hrecursorHead := - Lean4Lean.VEnv.HasType.appN_head hfutureWF hGamma hrecursorApplied + Ix.Theory.Named.VEnv.HasType.appN_head hfutureWF hGamma hrecursorApplied obtain ⟨recursorHeadType, hrecursorHeadType⟩ := hrecursorHead obtain ⟨recursorConstant, hrecursorLookup, hlevelsWF, hlevelsArity⟩ := @@ -582,7 +584,7 @@ theorem enumerationPatternSound (.const (.str generation.block.sourceType.name "rec") levels) (generation.recType.instL levels) := by simpa [VInductDecl.GenerationChecked.recursor] using - (Lean4Lean.VEnv.HasType.const + (Ix.Theory.Named.VEnv.HasType.const (Γ := Gamma) hcertifiedRecursorLookup hlevelsWF hlevelsArity) have hrecursorCommonType : future.HasType uvars Gamma (.const (.str generation.block.sourceType.name "rec") levels) @@ -611,11 +613,11 @@ theorem enumerationPatternSound CertifiedSingletonGeneration.IsEnumeration.ruleBinders_length, hcount] obtain ⟨equationApplicationType, hequationLhsApplied⟩ := - Lean4Lean.VEnv.HasType.transfer_appN_telescope + Ix.Theory.Named.VEnv.HasType.transfer_appN_telescope hfutureWF hGamma hargumentLength hrecursorApplied hrecursorCommonType hequationLhsCommonType have hequationApplied := - Lean4Lean.VEnv.IsDefEq.appN_same hfutureWF hGamma hequation + Ix.Theory.Named.VEnv.IsDefEq.appN_same hfutureWF hGamma hequation hequationLhsApplied have hequationRhsApplied : future.HasType uvars Gamma (VExpr.appN ((generation.rule index normalized).rhs.instL levels) @@ -639,7 +641,7 @@ theorem enumerationPatternSound rw [CertifiedSingletonGeneration.IsEnumeration.rule_lhs shape hnormalized, VExpr.instL_lamN] at hcopy exact hcopy - have hlhsBeta := Lean4Lean.VEnv.HasType.lamN_appN_beta + have hlhsBeta := Ix.Theory.Named.VEnv.HasType.lamN_appN_beta hfutureWF hGamma hargumentLength hequationLhsApplied' rw [CertifiedSingletonGeneration.IsEnumeration.ruleLhsBody_instantiated shape normalized levels recursorArguments @@ -668,7 +670,7 @@ theorem enumerationPatternSound rw [CertifiedSingletonGeneration.IsEnumeration.rule_rhs shape hnormalized, VExpr.instL_lamN] at hcopy exact hcopy - have hrhsBeta := Lean4Lean.VEnv.HasType.lamN_appN_beta + have hrhsBeta := Ix.Theory.Named.VEnv.HasType.lamN_appN_beta hfutureWF hGamma hargumentLength hequationRhsApplied' have hindexGeneration : index < generation.block.ctorPairs.length := by simpa [hcount] using hindex @@ -713,7 +715,7 @@ theorem enumerationPatternSound simpa [SingletonRecursorCatalogLink.enumerationPattern, selected, RecursorIotaPattern.recursorArgumentRhs_apply, RecursorIotaPattern.recursorArgumentRhs, - Lean4Lean.Pattern.RHS.apply] using hresult + Ix.Theory.Named.Pattern.RHS.apply] using hresult /-- Package the finite pattern metadata and its registered-equation proof as the exact historical relation consumed by the production iota verifier. -/ @@ -742,4 +744,4 @@ theorem enumerationPatternRel end SingletonRecursorCatalogLink -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/SingletonFamily.lean b/Ix/Kernel/Verify/Inductive/SingletonFamily.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/SingletonFamily.lean rename to Ix/Kernel/Verify/Inductive/SingletonFamily.lean index 0a39133c1..57e960a1a 100644 --- a/Ix/Tc/Verify/Inductive/SingletonFamily.lean +++ b/Ix/Kernel/Verify/Inductive/SingletonFamily.lean @@ -1,9 +1,9 @@ -import Ix.Tc.Verify.Inductive +import Ix.Kernel.Verify.Inductive /-! # Certified singleton-family admission -This module is the first production-facing half of E2b. A Lean4Lean +This module is the first production-facing half of E2b. A Ix.Theory.Named `CertifiedGenerationTransaction` already owns the semantic generation of one family, all of its constructors, its recursor, and its iota equations. What that transaction cannot know is which anonymous Ix addresses contain the @@ -26,9 +26,9 @@ are derived below from the E2a transaction. The later checker adapter must construct this link from ingress plus a successful production block run. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VConstVal VEnv VExpr VInductDecl) +open Ix.Theory.Named (VConstVal VEnv VExpr VInductDecl) /-! ## Header facts retained by normalized generation -/ @@ -470,4 +470,4 @@ def oracle end SingletonFamilyCatalogLink -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/SingletonIngress.lean b/Ix/Kernel/Verify/Inductive/SingletonIngress.lean similarity index 97% rename from Ix/Tc/Verify/Inductive/SingletonIngress.lean rename to Ix/Kernel/Verify/Inductive/SingletonIngress.lean index 65f505047..06264b13a 100644 --- a/Ix/Tc/Verify/Inductive/SingletonIngress.lean +++ b/Ix/Kernel/Verify/Inductive/SingletonIngress.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Inductive.SingletonRecursor -import Ix.Tc.Verify.Env +import Ix.Kernel.Verify.Inductive.SingletonRecursor +import Ix.Kernel.Verify.Env /-! # Loaded singleton-inductive ingress correspondence @@ -16,7 +16,7 @@ semantic authority to loading: recursor block; and * `toCatalogLink` transports those representation facts through `LoadedAgrees` and derives pre-admission freshness from the trusted log and - the Lean4Lean generation transaction. + the Ix.Theory.Named generation transaction. The views contain no catalog lookup, trusted-membership negation, declaration WF, or checker-success premise. In particular, an anonymous `KEnv` entry @@ -25,9 +25,9 @@ deliberate ghost input and will be constructed from the corresponding Ixon ingress trace. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VConstVal VEnv VInductDecl) +open Ix.Theory.Named (VConstVal VEnv VInductDecl) /-! ## Family and constructor ingress -/ @@ -124,7 +124,7 @@ theorem familyFresh Option.some.inj (hname.symm.trans view.familyName) subst name have hcollision : - (none : Option Lean4Lean.VConstant) = some ci := + (none : Option Ix.Theory.Named.VConstant) = some ci := tx.facts.familyFresh.symm.trans hlookup cases hcollision @@ -151,7 +151,7 @@ theorem constructorFresh tx.certificate.generation.block.sourceType.ctors := List.mem_of_getElem? hsource have hcollision : - (none : Option Lean4Lean.VConstant) = some ci := + (none : Option Ix.Theory.Named.VConstant) = some ci := (tx.facts.ctorFresh hsourceMem).symm.trans hlookup cases hcollision @@ -288,7 +288,7 @@ theorem recursorFresh Option.some.inj (hname.symm.trans view.recursorName) subst name have hcollision : - (none : Option Lean4Lean.VConstant) = some ci := + (none : Option Ix.Theory.Named.VConstant) = some ci := tx.facts.recursorFresh.symm.trans hlookup cases hcollision @@ -316,4 +316,4 @@ def toCatalogLink end SingletonRecursorIngressView -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/SingletonOracle.lean b/Ix/Kernel/Verify/Inductive/SingletonOracle.lean similarity index 96% rename from Ix/Tc/Verify/Inductive/SingletonOracle.lean rename to Ix/Kernel/Verify/Inductive/SingletonOracle.lean index 276ca8cf4..b35038ec6 100644 --- a/Ix/Tc/Verify/Inductive/SingletonOracle.lean +++ b/Ix/Kernel/Verify/Inductive/SingletonOracle.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Inductive.SingletonEnumeration +import Ix.Kernel.Verify.Inductive.SingletonEnumeration /-! # Certificate-backed singleton recursor oracle @@ -10,9 +10,9 @@ generated equations and pattern-soundness theorem rather than an ambient reflection premise. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VEnv VInductDecl) +open Ix.Theory.Named (VEnv VInductDecl) namespace SingletonRecursorCatalogLink @@ -84,4 +84,4 @@ def oracle end SingletonRecursorCatalogLink -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/SingletonRecursor.lean b/Ix/Kernel/Verify/Inductive/SingletonRecursor.lean similarity index 97% rename from Ix/Tc/Verify/Inductive/SingletonRecursor.lean rename to Ix/Kernel/Verify/Inductive/SingletonRecursor.lean index 489660981..f4e04c4d1 100644 --- a/Ix/Tc/Verify/Inductive/SingletonRecursor.lean +++ b/Ix/Kernel/Verify/Inductive/SingletonRecursor.lean @@ -1,9 +1,9 @@ -import Ix.Tc.Verify.Inductive.SingletonFamily +import Ix.Kernel.Verify.Inductive.SingletonFamily /-! # Certified singleton-recursor correspondence -The Lean4Lean transaction used by E2a installs a singleton family's recursor +The Ix.Theory.Named transaction used by E2a installs a singleton family's recursor and all of its iota equations atomically with the family. Anonymous Ix ingress does not: the family/constructor block and the recursor block are distinct physical blocks. This module links the latter block to the exact @@ -16,9 +16,9 @@ compilation is kept for the next module because it has an additional semantic obligation beyond raw/structural translation. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VConstVal VDefEq VEnv VExpr VInductDecl) +open Ix.Theory.Named (VConstVal VDefEq VEnv VExpr VInductDecl) namespace CertifiedSingletonGeneration @@ -51,7 +51,7 @@ theorem rawConstructorAt {source : VInductDecl} exact hmapped /-- A generated iota equation is recursor-headed below its closed rule -telescope. This is the shape actually emitted by Lean4Lean; its outer node +telescope. This is the shape actually emitted by Ix.Theory.Named; its outer node is never directly a constant-headed application when the telescope is nonempty. -/ theorem generatedRuleHead {source : VInductDecl} @@ -120,7 +120,7 @@ theorem levels VInductDecl.GenerationChecked.recursor] /-- The physical recursor's declared K bit is exactly the independently -computed flag retained by the certified Lean4Lean generation. -/ +computed flag retained by the certified Ix.Theory.Named generation. -/ theorem kTarget {source : VInductDecl} {generation : source.GenerationChecked} {constructorIds : Array (KId .anon)} {concrete : KConst .anon} @@ -362,4 +362,4 @@ theorem member_eq end SingletonRecursorCatalogLink -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/SpecializationIdentity.lean b/Ix/Kernel/Verify/Inductive/SpecializationIdentity.lean similarity index 97% rename from Ix/Tc/Verify/Inductive/SpecializationIdentity.lean rename to Ix/Kernel/Verify/Inductive/SpecializationIdentity.lean index dbad71590..351868235 100644 --- a/Ix/Tc/Verify/Inductive/SpecializationIdentity.lean +++ b/Ix/Kernel/Verify/Inductive/SpecializationIdentity.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Inductive.OccurrenceValidation +import Ix.Kernel.Verify.Inductive.OccurrenceValidation /-! # Nested-inductive specialization identity @@ -9,7 +9,7 @@ intentionally broader than this identity: syntactically distinct applications receive distinct generated auxiliaries even when they denote equal types. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- The production specialization key uses structural Boolean equality. Its derived implementation is lawful once raw address equality is known lawful. @@ -93,4 +93,4 @@ theorem semanticUniverseEquality_does_not_collapse_specialization : end SpecializationIdentityFixture -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Inductive/StructuralCacheSemantics.lean b/Ix/Kernel/Verify/Inductive/StructuralCacheSemantics.lean similarity index 99% rename from Ix/Tc/Verify/Inductive/StructuralCacheSemantics.lean rename to Ix/Kernel/Verify/Inductive/StructuralCacheSemantics.lean index 99cf177ee..1a240ec47 100644 --- a/Ix/Tc/Verify/Inductive/StructuralCacheSemantics.lean +++ b/Ix/Kernel/Verify/Inductive/StructuralCacheSemantics.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq +import Ix.Kernel.Verify.DefEq /-! # Certified semantics for inductive structural caches @@ -17,7 +17,7 @@ consumer obligation: the production recursor-member checker validates those artifacts exhaustively before accepting a stored recursor. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace CacheAuthority @@ -340,4 +340,4 @@ theorem insertRecursor end ScopedWhnfStateInv -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer.lean b/Ix/Kernel/Verify/Infer.lean similarity index 99% rename from Ix/Tc/Verify/Infer.lean rename to Ix/Kernel/Verify/Infer.lean index 8f347c140..b97f376b9 100644 --- a/Ix/Tc/Verify/Infer.lean +++ b/Ix/Kernel/Verify/Infer.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Suffix -import Ix.Tc.Verify.Knot +import Ix.Kernel.Verify.Suffix +import Ix.Kernel.Verify.Knot /-! # K2 inference semantics @@ -9,9 +9,9 @@ with its exact Theory meaning. Algorithmic branch proofs will consume the hit and insertion interfaces defined here. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace TcM @@ -349,4 +349,4 @@ theorem inferHitOfMatches {keys : WhnfContextKeys} {trProj : RawProjRel} end CacheInvariant -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/Applications.lean b/Ix/Kernel/Verify/Infer/Applications.lean similarity index 97% rename from Ix/Tc/Verify/Infer/Applications.lean rename to Ix/Kernel/Verify/Infer/Applications.lean index 53a71457b..6bee2d1b1 100644 --- a/Ix/Tc/Verify/Infer/Applications.lean +++ b/Ix/Kernel/Verify/Infer/Applications.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Infer.FunctionTypes -import Ix.Tc.Verify.Whnf.Beta.LambdaInstantiation +import Ix.Kernel.Verify.Infer.FunctionTypes +import Ix.Kernel.Verify.Whnf.Beta.LambdaInstantiation /-! # Application inference @@ -16,7 +16,7 @@ the exact family of substitution requests reachable after a supported codomain has been exposed. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Finite-support obligations for supported applications that reach the uncached inference dispatcher. Quantifying the final clause over supported @@ -73,7 +73,7 @@ private theorem applicationResult (theory : WhnfTheory trProj world uvars) (hDelta : KVLCtx.WF world.venv uvars Delta) {a cod : KExpr .anon} - {fV aV A B fTyV domV codV : Lean4Lean.VExpr} + {fV aV A B fTyV domV codV : Ix.Theory.Named.VExpr} (hfun : world.venv.HasType uvars Delta.toCtx fV (.forallE A B)) (harg : world.venv.HasType uvars Delta.toCtx aV A) (hargTr : TrKExprS world.venv uvars world.nameOf trProj Delta a aV) @@ -114,7 +114,7 @@ private theorem applicationResult refine ⟨codV.inst aV, ?_, ?_⟩ · exact hresultTr.trKExpr world.venvWF.ordered theory.literalWF theory.projections.wf hDelta - · exact (Lean4Lean.VEnv.HasType.app hfun harg).defeqU_r + · exact (Ix.Theory.Named.VEnv.HasType.app hfun harg).defeqU_r world.venvWF hDelta hcodInstEq /-- Execute the final substitution and package its support and Theory @@ -128,7 +128,7 @@ private theorem finishApplication_wf {s : TcState .anon} (theory : WhnfTheory trProj world uvars) {a cod : KExpr .anon} - {fV aV A B fTyV domV codV : Lean4Lean.VExpr} + {fV aV A B fTyV domV codV : Ix.Theory.Named.VExpr} (hfun : world.venv.HasType uvars Delta.toCtx fV (.forallE A B)) (harg : world.venv.HasType uvars Delta.toCtx aV A) (hargTr : TrKExprS world.venv uvars world.nameOf trProj Delta a aV) @@ -167,7 +167,7 @@ theorem inferUncached_app_wf {world : VerifyWorld} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {inferOnly : Bool} {f a : KExpr .anon} {info : ExprInfo .anon} - {sourceV : Lean4Lean.VExpr} + {sourceV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hwhnf : DirectWhnf.WFAt semantics trProj world support uvars) (hcomponents : ForallComponentSupport support) @@ -283,4 +283,4 @@ theorem inferUncached_app_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/BinderClosing.lean b/Ix/Kernel/Verify/Infer/BinderClosing.lean similarity index 98% rename from Ix/Tc/Verify/Infer/BinderClosing.lean rename to Ix/Kernel/Verify/Infer/BinderClosing.lean index a9773d984..6f4d580f2 100644 --- a/Ix/Tc/Verify/Infer/BinderClosing.lean +++ b/Ix/Kernel/Verify/Infer/BinderClosing.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Infer.BinderScopes +import Ix.Kernel.Verify.Infer.BinderScopes /-! # Semantic binder closing for inference @@ -10,9 +10,9 @@ fvar abstraction retags the concrete expression back to the original de Bruijn context without changing its Theory translation. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr VLocalDecl) +open Ix.Theory.Named (VExpr VLocalDecl) namespace KVLCtx.RetagFVar @@ -110,7 +110,7 @@ theorem abstractFVarPositions_singleton_miss {fv other : FVarId} binder depth. `Constructed` supplies the no-wrap fact for the `i + 1` variable arm; the size bound supplies every recursive `depth + 1`. -/ theorem TrKExprS.closeFVarSpec - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop} {target : KVLCtx} {body : KExpr .anon} {bodyV : VExpr} @@ -258,7 +258,7 @@ theorem TrKExprS.closeFVarSpec /-- Entry-depth form used after `openBinder`/`openLet`. -/ theorem TrKExprS.closeFVarZero - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop} {Delta : KVLCtx} {decl : VLocalDecl} @@ -279,7 +279,7 @@ theorem TrKExprS.closeFVarZero /-- The API-level fast path is semantically identical to the singleton abstraction specification under its audited bounds. -/ theorem TrKExprS.closeFVarResult - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop} {Delta : KVLCtx} {decl : VLocalDecl} @@ -432,4 +432,4 @@ theorem close_whnf_wf end SingletonAbstractionResources -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/BinderOpening.lean b/Ix/Kernel/Verify/Infer/BinderOpening.lean similarity index 98% rename from Ix/Tc/Verify/Infer/BinderOpening.lean rename to Ix/Kernel/Verify/Infer/BinderOpening.lean index ea9092332..8050b81e1 100644 --- a/Ix/Tc/Verify/Infer/BinderOpening.lean +++ b/Ix/Kernel/Verify/Infer/BinderOpening.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Infer.ScopedLocals +import Ix.Kernel.Verify.Infer.ScopedLocals /-! # Semantic binder opening for inference @@ -10,9 +10,9 @@ that retagging and proves that `instantiateRev` preserves the translated Theory expression. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr VLocalDecl) +open Ix.Theory.Named (VExpr VLocalDecl) namespace KVLCtx @@ -125,7 +125,7 @@ end KVLCtx /-- Replacing one de Bruijn binder with its freshly tagged fvar leaves the Theory expression unchanged. -/ theorem TrKExprS.openFVar - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop} {source : KVLCtx} {body : KExpr .anon} {bodyV : VExpr} @@ -282,7 +282,7 @@ theorem TrKExprS.openFVar /-- Entry-depth specialization used by the three production binder branches. -/ theorem TrKExprS.openFVarZero - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop} {Delta : KVLCtx} {decl : VLocalDecl} @@ -297,4 +297,4 @@ theorem TrKExprS.openFVarZero (KExpr.instantiateRevSpec body #[.mkFVar fv name] 0) bodyV := H.openFVar .zero rfl (by simpa using hfresh) (by simpa using hbound) -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/BinderScopes.lean b/Ix/Kernel/Verify/Infer/BinderScopes.lean similarity index 98% rename from Ix/Tc/Verify/Infer/BinderScopes.lean rename to Ix/Kernel/Verify/Infer/BinderScopes.lean index a5d9281f2..64999d9ea 100644 --- a/Ix/Tc/Verify/Infer/BinderScopes.lean +++ b/Ix/Kernel/Verify/Infer/BinderScopes.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Infer.BinderOpening +import Ix.Kernel.Verify.Infer.BinderOpening /-! # Operational binder scopes for inference @@ -9,9 +9,9 @@ opening a de Bruijn binder. This module verifies the production extension, walker execution, and the allocation-exhaustion error path. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-- Finite closure needed by a generic recursive method contract when it opens `body` with a freshly allocated anonymous-mode fvar. The fvar id is a @@ -53,7 +53,7 @@ theorem instRev_whnf_wf_of_resources InternUpdateFrame s after) := TcM.runIntern_whnf_wf (fun it hwf hsupport => by - have post := Ix.Tc.instantiateRev_spec hcollision.expr hbounds.1 + have post := Ix.Kernel.instantiateRev_spec hcollision.expr hbounds.1 hbounds.2.2 hreach hwf hsupport.expr exact ⟨post.1, post.2.1, hsupport.of_expr_univs post.2.2 @@ -78,7 +78,7 @@ theorem instRev_whnf_eval_of_resources InternUpdateFrame s after := TcM.runIntern_whnf_eval (fun it hwf hsupport => by - have post := Ix.Tc.instantiateRev_spec hcollision.expr hbounds.1 + have post := Ix.Kernel.instantiateRev_spec hcollision.expr hbounds.1 hbounds.2.2 hreach hwf hsupport.expr exact ⟨post.1, post.2.1, hsupport.of_expr_univs post.2.2 @@ -374,4 +374,4 @@ theorem withLctxScope_openBinder_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/CacheShell.lean b/Ix/Kernel/Verify/Infer/CacheShell.lean similarity index 99% rename from Ix/Tc/Verify/Infer/CacheShell.lean rename to Ix/Kernel/Verify/Infer/CacheShell.lean index e9166c92e..52a8e40d6 100644 --- a/Ix/Tc/Verify/Infer/CacheShell.lean +++ b/Ix/Kernel/Verify/Infer/CacheShell.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq +import Ix.Kernel.Verify.DefEq /-! # Inference cache shell @@ -9,7 +9,7 @@ partitions and for the full/infer-only miss paths, including partial errors before any result is cached. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -217,4 +217,4 @@ theorem inferWith_inferOnlyMiss_error end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/CacheSoundness.lean b/Ix/Kernel/Verify/Infer/CacheSoundness.lean similarity index 97% rename from Ix/Tc/Verify/Infer/CacheSoundness.lean rename to Ix/Kernel/Verify/Infer/CacheSoundness.lean index 620b3ebc9..54061b764 100644 --- a/Ix/Tc/Verify/Infer/CacheSoundness.lean +++ b/Ix/Kernel/Verify/Infer/CacheSoundness.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Infer.Dispatcher +import Ix.Kernel.Verify.Infer.Dispatcher /-! # Inference cache soundness @@ -10,7 +10,7 @@ finite expression collision freedom, suffix transport, and the concrete uncached typing result before mutating either cache partition. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace TcM @@ -90,7 +90,7 @@ private theorem missTail_wf (kernelCacheSemantics model.keys trProj) trProj world support model.keys.uvars) {Delta : KVLCtx} {before s : TcState .anon} {inferOnly : Bool} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} {key : Address × Address} (hmatch : model.keys.Matches trProj world before Delta source key) (hsourceSupport : support source) @@ -151,7 +151,7 @@ theorem inferWith_wf (kernelCacheSemantics model.keys trProj) trProj world support model.keys.uvars) {Delta : KVLCtx} {s : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsourceSupport : support source) (hsource : TrKExprS world.venv model.keys.uvars world.nameOf trProj Delta source sourceV) : @@ -241,7 +241,7 @@ theorem infer_wf (kernelCacheSemantics model.keys trProj) trProj world support model.keys.uvars) {Delta : KVLCtx} {s : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsourceSupport : support source) (hsource : TrKExprS world.venv model.keys.uvars world.nameOf trProj Delta source sourceV) : @@ -272,7 +272,7 @@ theorem nextInfer_wf (kernelCacheSemantics model.keys trProj) trProj world support model.keys.uvars methods) : forall {Delta : KVLCtx} {s : TcState .anon} {source : KExpr .anon} - {sourceV : Lean4Lean.VExpr}, + {sourceV : Ix.Theory.Named.VExpr}, support source -> TrKExprS world.venv model.keys.uvars world.nameOf trProj Delta source sourceV -> @@ -287,4 +287,4 @@ theorem nextInfer_wf end UncachedInference.Context -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/Callbacks.lean b/Ix/Kernel/Verify/Infer/Callbacks.lean similarity index 92% rename from Ix/Tc/Verify/Infer/Callbacks.lean rename to Ix/Kernel/Verify/Infer/Callbacks.lean index fe79547e2..849a4183c 100644 --- a/Ix/Tc/Verify/Infer/Callbacks.lean +++ b/Ix/Kernel/Verify/Infer/Callbacks.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Infer.Literals +import Ix.Kernel.Verify.Infer.Literals /-! # Inference callback contracts @@ -9,7 +9,7 @@ and the already-closed direct WHNF implementation used by `ensureSortDirect` and `ensureForallDirect`. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace DirectWhnf @@ -36,7 +36,7 @@ theorem inferCall_wf {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsource : support source) (htr : TrKExprS world.venv uvars world.nameOf trProj Delta source sourceV) : @@ -53,7 +53,7 @@ theorem isDefEqCall_wf {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} - {a b : KExpr .anon} {va vb : Lean4Lean.VExpr} + {a b : KExpr .anon} {va vb : Ix.Theory.Named.VExpr} (haSupport : support a) (hbSupport : support b) (ha : TrKExprS world.venv uvars world.nameOf trProj Delta a va) (hb : TrKExprS world.venv uvars world.nameOf trProj Delta b vb) : @@ -66,4 +66,4 @@ theorem isDefEqCall_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/CheapBeta.lean b/Ix/Kernel/Verify/Infer/CheapBeta.lean similarity index 98% rename from Ix/Tc/Verify/Infer/CheapBeta.lean rename to Ix/Kernel/Verify/Infer/CheapBeta.lean index 6675d9765..f7feb33a0 100644 --- a/Ix/Tc/Verify/Infer/CheapBeta.lean +++ b/Ix/Kernel/Verify/Infer/CheapBeta.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Infer.BinderScopes -import Ix.Tc.Verify.Whnf.Beta.Meaning -import Ix.Tc.Verify.Whnf.Structural.ApplicationCongruence +import Ix.Kernel.Verify.Infer.BinderScopes +import Ix.Kernel.Verify.Whnf.Beta.Meaning +import Ix.Kernel.Verify.Whnf.Structural.ApplicationCongruence /-! # Audited cheap beta reduction @@ -12,7 +12,7 @@ invariant. The Theory-level beta meaning is intentionally a separate layer; the operational theorem here cannot silently assume it. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM.BetaPeel @@ -215,7 +215,7 @@ theorem KExpr.cheapBetaReduceResult_meaning {trProj : RawProjRel} {world : VerifyWorld} {uvars : Nat} (theory : WhnfTheory trProj world uvars) {Delta : KVLCtx} (hDelta : KVLCtx.WF world.venv uvars Delta) - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsource : TrKExprS world.venv uvars world.nameOf trProj Delta source sourceV) (hbounds : WalkerRequest.Bounds (.cheapBeta source)) : @@ -459,4 +459,4 @@ theorem cheapBeta_whnf_wf end RunAssumptions -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/Constants.lean b/Ix/Kernel/Verify/Infer/Constants.lean similarity index 96% rename from Ix/Tc/Verify/Infer/Constants.lean rename to Ix/Kernel/Verify/Infer/Constants.lean index 11b35141d..d40014ef9 100644 --- a/Ix/Tc/Verify/Infer/Constants.lean +++ b/Ix/Kernel/Verify/Infer/Constants.lean @@ -1,5 +1,7 @@ -import Ix.Tc.Verify.Infer.LeafCases -import Ix.Tc.Verify.Whnf.Delta.StableCache +import Ix.Kernel.Verify.Infer.LeafCases +import Ix.Kernel.Verify.Whnf.Delta.StableCache + +open Ix.Theory (VLevel) /-! # Constant inference @@ -12,7 +14,7 @@ closure must derive it from declaration admission rather than silently upgrading raw syntax correspondence. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace TcM @@ -101,7 +103,7 @@ handled separately because production deliberately skips the walker there. -/ theorem instantiatedType {trProj : RawProjRel} {world : VerifyWorld} {id : KId .anon} {c : KConst .anon} {name : Lean.Name} - {ci : Lean4Lean.VConstant} + {ci : Ix.Theory.Named.VConstant} (h : TrustedConstRel trProj world id c name ci) (htype : TrKExprS world.venv ci.uvars world.nameOf trProj [] c.ty ci.type) @@ -138,7 +140,7 @@ theorem instantiatedType have hlevels := hciType.levelWF (by trivial) simpa only [hzero] using hlevels.1 have hinst : ci.type.instL [] = ci.type := by - simpa [Lean4Lean.VLevel.params] using hciLevels.instL_id + simpa [Ix.Theory.VLevel.params] using hciLevels.instL_id simpa [hinst] using htypeDelta.trKExpr world.venvWF.ordered theory.literalWF theory.projections.wf hDelta @@ -169,7 +171,7 @@ theorem inferUncached_const_wf {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} {id : KId .anon} {us : Array (KUniv .anon)} {info : ExprInfo .anon} - {sourceV : Lean4Lean.VExpr} + {sourceV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hfault : TcM.LazyFaultPreserves (WhnfStateInv layer semantics trProj world support uvars Delta)) @@ -219,7 +221,7 @@ theorem inferUncached_const_wf sourceCi.type.instL (us.toList.map KUniv.toVLevel), ?_, ?_⟩ · exact hrel.instantiatedType htype theory hus harity hspec resources hIfinal.2.1.wf - · exact Lean4Lean.VEnv.HasType.const hlookup + · exact Ix.Theory.Named.VEnv.HasType.const hlookup (by intro level hlevel obtain ⟨source, hsourceLevel, rfl⟩ := List.mem_map.1 hlevel @@ -230,4 +232,4 @@ theorem inferUncached_const_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/Dispatcher.lean b/Ix/Kernel/Verify/Infer/Dispatcher.lean similarity index 94% rename from Ix/Tc/Verify/Infer/Dispatcher.lean rename to Ix/Kernel/Verify/Infer/Dispatcher.lean index 5642ebc49..e73864fa0 100644 --- a/Ix/Tc/Verify/Infer/Dispatcher.lean +++ b/Ix/Kernel/Verify/Infer/Dispatcher.lean @@ -1,11 +1,11 @@ -import Ix.Tc.Verify.Infer.Applications -import Ix.Tc.Verify.Infer.Constants -import Ix.Tc.Verify.Infer.ForallTypes -import Ix.Tc.Verify.Infer.LambdaTypes -import Ix.Tc.Verify.Infer.LeafCases -import Ix.Tc.Verify.Infer.LetTypes -import Ix.Tc.Verify.Infer.Literals -import Ix.Tc.Verify.Infer.ProjectionTypes +import Ix.Kernel.Verify.Infer.Applications +import Ix.Kernel.Verify.Infer.Constants +import Ix.Kernel.Verify.Infer.ForallTypes +import Ix.Kernel.Verify.Infer.LambdaTypes +import Ix.Kernel.Verify.Infer.LeafCases +import Ix.Kernel.Verify.Infer.LetTypes +import Ix.Kernel.Verify.Infer.Literals +import Ix.Kernel.Verify.Infer.ProjectionTypes /-! # Uncached inference dispatcher @@ -21,7 +21,7 @@ the syntax-only census facts. This avoids requiring facts about arbitrary invalid states merely to state recursive inference closure. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- State-indexed resources for the legacy de Bruijn-variable inference branch. The translated source establishes that the lookup is in range; this @@ -104,7 +104,7 @@ theorem inferUncached_wf (context : UncachedInference.Context initial program requests semantics trProj world support uvars) {Delta : KVLCtx} {s : TcState .anon} {inferOnly : Bool} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsourceSupport : support source) (hsource : TrKExprS world.venv uvars world.nameOf trProj Delta source sourceV) : @@ -167,4 +167,4 @@ theorem inferUncached_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/ForallTypes.lean b/Ix/Kernel/Verify/Infer/ForallTypes.lean similarity index 95% rename from Ix/Tc/Verify/Infer/ForallTypes.lean rename to Ix/Kernel/Verify/Infer/ForallTypes.lean index efe4228c5..1f0b3435b 100644 --- a/Ix/Tc/Verify/Infer/ForallTypes.lean +++ b/Ix/Kernel/Verify/Infer/ForallTypes.lean @@ -1,5 +1,7 @@ -import Ix.Tc.Verify.Infer.SortTypes -import Ix.Tc.Verify.Infer.BinderScopes +import Ix.Kernel.Verify.Infer.SortTypes +import Ix.Kernel.Verify.Infer.BinderScopes + +open Ix.Theory (VLevel) /-! # Forall inference @@ -10,7 +12,7 @@ opening and cleanup, the simplifying universe `imax` constructor, and final expression interning. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Finite result closure for `forall` inference. The premise ranges only over the finite universe support of the run, rather than over all levels. -/ @@ -29,7 +31,7 @@ private theorem inferForallTail_wf {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {fv : FVarId} {deps : List FVarId} - {tyV bodyV input1 : Lean4Lean.VExpr} {bodyOpen : KExpr .anon} + {tyV bodyV input1 : Ix.Theory.Named.VExpr} {bodyOpen : KExpr .anon} {u1 : KUniv .anon} (theory : WhnfTheory trProj world uvars) (hwhnf : DirectWhnf.WFAt semantics trProj world support uvars) @@ -78,7 +80,7 @@ private theorem inferForallTail_wf hbodyTy.defeqU_r world.venvWF hIfinal.2.1.wf hu2.inputEq have hforall : world.venv.HasType uvars Delta.toCtx (.forallE tyV bodyV) (.sort (.imax u1.toVLevel u2.toVLevel)) := - Lean4Lean.VEnv.HasType.forallE htySort (by simpa using hbodySort) + Ix.Theory.Named.VEnv.HasType.forallE htySort (by simpa using hbodySort) have hlevelEq := hu1.mkIMax_equiv hcollision hu2 have hsortEq : world.venv.IsDefEqU uvars Delta.toCtx (.sort (.imax u1.toVLevel u2.toVLevel)) @@ -104,7 +106,7 @@ theorem inferUncached_all_wf {s : TcState .anon} {inferOnly : Bool} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {ty body : KExpr .anon} {info : ExprInfo .anon} - {sourceV : Lean4Lean.VExpr} + {sourceV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hwhnf : DirectWhnf.WFAt semantics trProj world support uvars) (hresources : SortComponentResources support) @@ -155,4 +157,4 @@ theorem inferUncached_all_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/FunctionTypes.lean b/Ix/Kernel/Verify/Infer/FunctionTypes.lean similarity index 94% rename from Ix/Tc/Verify/Infer/FunctionTypes.lean rename to Ix/Kernel/Verify/Infer/FunctionTypes.lean index bc62597b0..6e0dd64a6 100644 --- a/Ix/Tc/Verify/Infer/FunctionTypes.lean +++ b/Ix/Kernel/Verify/Infer/FunctionTypes.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Infer.Callbacks +import Ix.Kernel.Verify.Infer.Callbacks /-! # Function-type exposure for inference @@ -9,7 +9,7 @@ fallback expose the same semantic view, while retaining finite support for the returned domain and codomain. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Finite-support descent needed after a supported Pi is exposed. Run support is intentionally not globally constructor-closed. -/ @@ -23,7 +23,7 @@ the caller's quotient translation of the original inferred type to the exact structural translations of the returned concrete components. -/ def ForallView (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (uvars : Nat) (Delta : KVLCtx) - (inputV : Lean4Lean.VExpr) (dom cod : KExpr .anon) : Prop := + (inputV : Ix.Theory.Named.VExpr) (dom cod : KExpr .anon) : Prop := exists domV codV, support dom /\ support cod /\ world.venv.IsType uvars Delta.toCtx domV /\ @@ -39,7 +39,7 @@ private theorem ensureForallWhnf_wf {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {input : KExpr .anon} - {inputCoreV inputV : Lean4Lean.VExpr} + {inputCoreV inputV : Ix.Theory.Named.VExpr} (hwhnf : DirectWhnf.WFAt semantics trProj world support uvars) (hcomponents : ForallComponentSupport support) (hinputSupport : support input) @@ -76,7 +76,7 @@ theorem ensureForallDirect_wf {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {input : KExpr .anon} - {inputV : Lean4Lean.VExpr} + {inputV : Ix.Theory.Named.VExpr} (hwhnf : DirectWhnf.WFAt semantics trProj world support uvars) (hcomponents : ForallComponentSupport support) (hinputSupport : support input) @@ -103,4 +103,4 @@ theorem ensureForallDirect_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/LambdaTypes.lean b/Ix/Kernel/Verify/Infer/LambdaTypes.lean similarity index 95% rename from Ix/Tc/Verify/Infer/LambdaTypes.lean rename to Ix/Kernel/Verify/Infer/LambdaTypes.lean index f543138d0..89e931f15 100644 --- a/Ix/Tc/Verify/Infer/LambdaTypes.lean +++ b/Ix/Kernel/Verify/Infer/LambdaTypes.lean @@ -1,7 +1,7 @@ -import Ix.Tc.Verify.Infer.CheapBeta -import Ix.Tc.Verify.Infer.BinderClosing -import Ix.Tc.Verify.Infer.SortTypes -import Ix.Tc.Verify.Whnf.Iota.ArgumentExecution +import Ix.Kernel.Verify.Infer.CheapBeta +import Ix.Kernel.Verify.Infer.BinderClosing +import Ix.Kernel.Verify.Infer.SortTypes +import Ix.Kernel.Verify.Whnf.Iota.ArgumentExecution /-! # Lambda inference @@ -11,7 +11,7 @@ validation, fresh-fvar binder opening, recursive body inference, cheap beta, singleton abstraction, anonymous Pi reconstruction, and scoped cleanup. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Finite closure for the anonymous Pi nodes produced from supported body types. The body argument ranges only over the finite run support. -/ @@ -29,7 +29,7 @@ private theorem inferLambdaTail_wf {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {fv : FVarId} {deps : List FVarId} - {ty bodyOpen : KExpr .anon} {tyV bodyV : Lean4Lean.VExpr} + {ty bodyOpen : KExpr .anon} {tyV bodyV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hcheap : CheapBetaResources support) (habstract : SingletonAbstractionResources support) @@ -106,7 +106,7 @@ private theorem inferLambdaTail_wf (tyV :: Delta.toCtx) bodyV bodyTyV := by simpa [KVLCtx.toCtx] using hbodyTy exact ⟨hresultSupport, .forallE tyV bodyTyV, hresultTr, - Lean4Lean.VEnv.HasType.lam htySort hbodyTy'⟩ + Ix.Theory.Named.VEnv.HasType.lam htySort hbodyTy'⟩ · intro _ _ _ trivial @@ -118,7 +118,7 @@ private theorem inferLambdaScoped_wf {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} - {ty body : KExpr .anon} {tyV bodyV : Lean4Lean.VExpr} + {ty body : KExpr .anon} {tyV bodyV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hcheap : CheapBetaResources support) (habstract : SingletonAbstractionResources support) @@ -177,7 +177,7 @@ theorem inferUncached_lam_wf {s : TcState .anon} {inferOnly : Bool} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {ty body : KExpr .anon} {info : ExprInfo .anon} - {sourceV : Lean4Lean.VExpr} + {sourceV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hwhnf : DirectWhnf.WFAt semantics trProj world support uvars) (hsorts : SortComponentResources support) @@ -225,4 +225,4 @@ theorem inferUncached_lam_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/LeafCases.lean b/Ix/Kernel/Verify/Infer/LeafCases.lean similarity index 96% rename from Ix/Tc/Verify/Infer/LeafCases.lean rename to Ix/Kernel/Verify/Infer/LeafCases.lean index 9ae843527..ca6077b2d 100644 --- a/Ix/Tc/Verify/Infer/LeafCases.lean +++ b/Ix/Kernel/Verify/Infer/LeafCases.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Infer.CacheShell +import Ix.Kernel.Verify.Infer.CacheShell + +open Ix.Theory (VLevel) /-! # Non-recursive inference cases @@ -9,7 +11,7 @@ proofs separate makes the semantic boundary explicit: each branch must produce a supported concrete type together with a Theory typing derivation. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace TcM @@ -55,7 +57,7 @@ theorem inferUncached_sort_wf {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} {u : KUniv .anon} {info : ExprInfo .anon} - {sourceV : Lean4Lean.VExpr} + {sourceV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hcollision : support.CollisionFree) (hresultSupport : support (KExpr.mkSort (KUniv.mkSucc u))) @@ -79,7 +81,7 @@ theorem inferUncached_sort_wf world.venvWF.ordered theory.literalWF theory.projections.wf hI.2.1.wf · simpa only [KUniv.toVLevel_mkSucc] using - (Lean4Lean.VEnv.HasType.sort hu) + (Ix.Theory.Named.VEnv.HasType.sort hu) · intro _ _ _ trivial @@ -95,7 +97,7 @@ theorem inferUncached_var_wf {Delta : KVLCtx} {s : TcState .anon} {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} {idx : UInt64} {name : Mode.anon.F Name} - {info : ExprInfo .anon} {sourceV : Lean4Lean.VExpr} + {info : ExprInfo .anon} {sourceV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hsource : TrKExprS world.venv uvars world.nameOf trProj Delta (.var idx name info) sourceV) @@ -169,7 +171,7 @@ theorem inferUncached_fvar_wf {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} {fv : FVarId} {name : Mode.anon.F Name} - {info : ExprInfo .anon} {sourceV : Lean4Lean.VExpr} + {info : ExprInfo .anon} {sourceV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hsafe : FVarInferSafety layer semantics trProj world support uvars Delta) @@ -206,4 +208,4 @@ theorem inferUncached_fvar_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/LetScopes.lean b/Ix/Kernel/Verify/Infer/LetScopes.lean similarity index 99% rename from Ix/Tc/Verify/Infer/LetScopes.lean rename to Ix/Kernel/Verify/Infer/LetScopes.lean index b2df97c68..a1abc967a 100644 --- a/Ix/Tc/Verify/Infer/LetScopes.lean +++ b/Ix/Kernel/Verify/Infer/LetScopes.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Infer.BinderScopes +import Ix.Kernel.Verify.Infer.BinderScopes /-! # Operational let scopes for inference @@ -8,9 +8,9 @@ pushes an `ldecl` and translates to a Theory `vlet`. Keeping its proof separate makes that semantic distinction visible at the API boundary. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace TcM @@ -304,4 +304,4 @@ theorem withLctxScope_openLet_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/LetTypes.lean b/Ix/Kernel/Verify/Infer/LetTypes.lean similarity index 95% rename from Ix/Tc/Verify/Infer/LetTypes.lean rename to Ix/Kernel/Verify/Infer/LetTypes.lean index fe98e4165..bc614b75d 100644 --- a/Ix/Tc/Verify/Infer/LetTypes.lean +++ b/Ix/Kernel/Verify/Infer/LetTypes.lean @@ -1,9 +1,9 @@ -import Ix.Tc.Verify.Infer.LetScopes -import Ix.Tc.Verify.Infer.BinderClosing -import Ix.Tc.Verify.Infer.Substitution -import Ix.Tc.Verify.Infer.CheapBeta -import Ix.Tc.Verify.Infer.SortTypes -import Ix.Tc.Verify.Whnf.Iota.ArgumentExecution +import Ix.Kernel.Verify.Infer.LetScopes +import Ix.Kernel.Verify.Infer.BinderClosing +import Ix.Kernel.Verify.Infer.Substitution +import Ix.Kernel.Verify.Infer.CheapBeta +import Ix.Kernel.Verify.Infer.SortTypes +import Ix.Kernel.Verify.Whnf.Iota.ArgumentExecution /-! # Let inference @@ -13,7 +13,7 @@ body inference, singleton abstraction, eager value substitution, cheap beta, and scoped cleanup for the production `letE` branch. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -25,7 +25,7 @@ private theorem inferLetTail_wf {Delta : KVLCtx} {s : TcState .anon} {fv : FVarId} {deps : List FVarId} {val bodyOpen : KExpr .anon} - {tyV valV bodyV : Lean4Lean.VExpr} + {tyV valV bodyV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (habstract : SingletonAbstractionResources support) (hsubst : SubstitutionResources support) @@ -96,7 +96,7 @@ private theorem inferLetScoped_wf {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {name : Mode.anon.F Name} - {ty val body : KExpr .anon} {tyV valV bodyV : Lean4Lean.VExpr} + {ty val body : KExpr .anon} {tyV valV bodyV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (habstract : SingletonAbstractionResources support) (hsubst : SubstitutionResources support) @@ -154,7 +154,7 @@ theorem inferUncached_let_wf {s : TcState .anon} {inferOnly : Bool} {name : Mode.anon.F Name} {ty val body : KExpr .anon} {nondep : Bool} {info : ExprInfo .anon} - {sourceV : Lean4Lean.VExpr} + {sourceV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hwhnf : DirectWhnf.WFAt semantics trProj world support uvars) (hsorts : SortComponentResources support) @@ -225,4 +225,4 @@ theorem inferUncached_let_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/Literals.lean b/Ix/Kernel/Verify/Infer/Literals.lean similarity index 88% rename from Ix/Tc/Verify/Infer/Literals.lean rename to Ix/Kernel/Verify/Infer/Literals.lean index e3caab00f..039f26d01 100644 --- a/Ix/Tc/Verify/Infer/Literals.lean +++ b/Ix/Kernel/Verify/Infer/Literals.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Infer.Constants +import Ix.Kernel.Verify.Infer.Constants /-! # Literal inference @@ -11,7 +11,7 @@ constant accepts an empty universe array. Those representation obligations are therefore exposed explicitly below. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Exact Theory interpretation of the two primitive-table entries read by literal inference. Trust and address-to-name agreement come from @@ -35,7 +35,7 @@ theorem nat_tr (hcatalog : TrustedCatalogRel trProj world) (htable : LiteralPrimitiveTableAgrees world prims) : TrKExprS world.venv uvars world.nameOf trProj Delta - (KExpr.mkConst prims.nat #[]) Lean4Lean.VExpr.nat := by + (KExpr.mkConst prims.nat #[]) Ix.Theory.Named.VExpr.nat := by rw [KExpr.mkConst_shape] obtain ⟨ci, hlookup⟩ := htable.nat.contains hcatalog exact @@ -49,7 +49,7 @@ theorem string_tr (hcatalog : TrustedCatalogRel trProj world) (htable : LiteralPrimitiveTableAgrees world prims) : TrKExprS world.venv uvars world.nameOf trProj Delta - (KExpr.mkConst prims.string #[]) Lean4Lean.VExpr.string := by + (KExpr.mkConst prims.string #[]) Ix.Theory.Named.VExpr.string := by rw [KExpr.mkConst_shape] obtain ⟨ci, hlookup⟩ := htable.string.contains hcatalog exact @@ -83,7 +83,7 @@ theorem inferUncached_nat_wf {Delta : KVLCtx} {s : TcState .anon} {inferRec : KExpr .anon -> RecM .anon (KExpr .anon)} {inferOnly : Bool} {n : Nat} {blob : Address} - {info : ExprInfo .anon} {sourceV : Lean4Lean.VExpr} + {info : ExprInfo .anon} {sourceV : Ix.Theory.Named.VExpr} (context : LiteralInferContext world support) (theory : WhnfTheory trProj world uvars) (hsource : TrKExprS world.venv uvars world.nameOf trProj Delta @@ -110,14 +110,14 @@ theorem inferUncached_nat_wf TcM.intern_whnf_wf context.collisionFree hsupport) · intro result final hresult rcases hresult with ⟨hIfinal, rfl, _⟩ - refine ⟨hsupport, Lean4Lean.VExpr.nat, ?_, ?_⟩ + refine ⟨hsupport, Ix.Theory.Named.VExpr.nat, ?_, ?_⟩ · exact (htable.nat_tr hcatalog).trKExpr world.venvWF.ordered theory.literalWF theory.projections.wf hIfinal.2.1.wf · have htype0 : world.venv.HasType uvars [] - (.natLit n) Lean4Lean.VExpr.nat := by - simpa [Lean4Lean.VLCtx.toCtx] using - (Lean4Lean.TrExprS.natLit + (.natLit n) Ix.Theory.Named.VExpr.nat := by + simpa [Ix.Theory.Named.VLCtx.toCtx] using + (Ix.Theory.Named.TrExprS.natLit (Us := List.replicate uvars Lean.Name.anonymous) (Δ := []) context.theoryPrimitives hcontains n).2 exact htype0.weak0 world.venvWF (Γ := Delta.toCtx) @@ -125,7 +125,7 @@ theorem inferUncached_nat_wf trivial /-- A concrete String literal infers the runtime String constant. The source -typing is the full Lean4Lean literal construction, including `Char.ofNat` and +typing is the full Ix.Theory.Named literal construction, including `Char.ofNat` and `String.ofList`; the returned type is still the primitive `String` entry. -/ theorem inferUncached_str_wf {semantics : CacheSemantics} {trProj : RawProjRel} @@ -133,7 +133,7 @@ theorem inferUncached_str_wf {Delta : KVLCtx} {s : TcState .anon} {inferRec : KExpr .anon -> RecM .anon (KExpr .anon)} {inferOnly : Bool} {value : String} {blob : Address} - {info : ExprInfo .anon} {sourceV : Lean4Lean.VExpr} + {info : ExprInfo .anon} {sourceV : Ix.Theory.Named.VExpr} (context : LiteralInferContext world support) (theory : WhnfTheory trProj world uvars) (hsource : TrKExprS world.venv uvars world.nameOf trProj Delta @@ -160,15 +160,15 @@ theorem inferUncached_str_wf TcM.intern_whnf_wf context.collisionFree hsupport) · intro result final hresult rcases hresult with ⟨hIfinal, rfl, _⟩ - refine ⟨hsupport, Lean4Lean.VExpr.string, ?_, ?_⟩ + refine ⟨hsupport, Ix.Theory.Named.VExpr.string, ?_, ?_⟩ · exact (htable.string_tr hcatalog).trKExpr world.venvWF.ordered theory.literalWF theory.projections.wf hIfinal.2.1.wf · have htype0 : world.venv.HasType uvars [] - (.trLiteral (.strVal value)) Lean4Lean.VExpr.string := by - simpa [Lean4Lean.VExpr.string, Lean4Lean.VLCtx.toCtx, + (.trLiteral (.strVal value)) Ix.Theory.Named.VExpr.string := by + simpa [Ix.Theory.Named.VExpr.string, Ix.Theory.Named.VLCtx.toCtx, Lean.Literal.typeName] using - (Lean4Lean.TrExprS.trLiteral world.venvWF.ordered + (Ix.Theory.Named.TrExprS.trLiteral world.venvWF.ordered (Us := List.replicate uvars Lean.Name.anonymous) (Δ := []) context.theoryPrimitives (.strVal value) hcontains).2 exact htype0.weak0 world.venvWF (Γ := Delta.toCtx) @@ -177,4 +177,4 @@ theorem inferUncached_str_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/ProjectionClassification.lean b/Ix/Kernel/Verify/Infer/ProjectionClassification.lean similarity index 97% rename from Ix/Tc/Verify/Infer/ProjectionClassification.lean rename to Ix/Kernel/Verify/Infer/ProjectionClassification.lean index f4d490b6d..5ee091fa3 100644 --- a/Ix/Tc/Verify/Infer/ProjectionClassification.lean +++ b/Ix/Kernel/Verify/Infer/ProjectionClassification.lean @@ -1,7 +1,7 @@ import Init.Data.Range.Lemmas -import Ix.Tc.Verify.Infer.Constants -import Ix.Tc.Verify.Infer.ProjectionTelescope -import Ix.Tc.Verify.Whnf.StructEta.RecursionClassifier +import Ix.Kernel.Verify.Infer.Constants +import Ix.Kernel.Verify.Infer.ProjectionTelescope +import Ix.Kernel.Verify.Whnf.StructEta.RecursionClassifier /-! # Projection result-sort classification @@ -15,7 +15,7 @@ those intermediate bodies have a structural translation in the caller's context. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- The exact universe-instantiation request selected when the classifier's lookup returns the catalogued inductive declaration. Other declaration @@ -194,4 +194,4 @@ theorem inductiveAppIsProp_state_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/ProjectionTelescope.lean b/Ix/Kernel/Verify/Infer/ProjectionTelescope.lean similarity index 94% rename from Ix/Tc/Verify/Infer/ProjectionTelescope.lean rename to Ix/Kernel/Verify/Infer/ProjectionTelescope.lean index 177ac930d..3a10513b3 100644 --- a/Ix/Tc/Verify/Infer/ProjectionTelescope.lean +++ b/Ix/Kernel/Verify/Infer/ProjectionTelescope.lean @@ -1,6 +1,6 @@ import Init.Data.Range.Lemmas -import Ix.Tc.Verify.Infer.LetTypes -import Ix.Tc.Verify.Infer.FunctionTypes +import Ix.Kernel.Verify.Infer.LetTypes +import Ix.Kernel.Verify.Infer.FunctionTypes /-! # Projection telescope exposure @@ -11,7 +11,7 @@ WHNF reducer. This module proves that both paths expose the same supported Theory forall view; the diagnostic string affects only the error payload. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- A concrete argument is admissible for every supported Π view that the production peeler may expose from `inputV`. The universal formulation avoids @@ -19,8 +19,8 @@ choosing a particular structural translation before WHNF has run; translation uniqueness makes all successful views definitionally coherent. -/ def ProjectionArgumentFits (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (uvars : Nat) (Delta : KVLCtx) - (inputV : Lean4Lean.VExpr) (arg : KExpr .anon) : Prop := - ∀ {dom cod : KExpr .anon} {domV codV : Lean4Lean.VExpr}, + (inputV : Ix.Theory.Named.VExpr) (arg : KExpr .anon) : Prop := + ∀ {dom cod : KExpr .anon} {domV codV : Ix.Theory.Named.VExpr}, support dom → support cod → world.venv.IsType uvars Delta.toCtx domV → world.venv.IsType uvars (domV :: Delta.toCtx) codV → @@ -41,7 +41,7 @@ theorem peelProjForall_wf {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {input : KExpr .anon} - {inputV : Lean4Lean.VExpr} {err : String} + {inputV : Ix.Theory.Named.VExpr} {err : String} (hwhnf : DirectWhnf.WFAt semantics trProj world support uvars) (hcomponents : ForallComponentSupport support) (hinputSupport : support input) @@ -89,7 +89,7 @@ private theorem substProjForallBody_wf {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} - {inputV : Lean4Lean.VExpr} {dom body arg : KExpr .anon} + {inputV : Ix.Theory.Named.VExpr} {dom body arg : KExpr .anon} (theory : WhnfTheory trProj world uvars) (hsubst : SubstitutionResources support) (hcollision : support.CollisionFree) @@ -136,7 +136,7 @@ private theorem instantiateProjParamBody_wf {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} - {current arg : KExpr .anon} {currentV : Lean4Lean.VExpr} + {current arg : KExpr .anon} {currentV : Ix.Theory.Named.VExpr} {err : String} (theory : WhnfTheory trProj world uvars) (hwhnf : DirectWhnf.WFAt semantics trProj world support uvars) @@ -171,7 +171,7 @@ theorem instantiateProjParamStep_wf {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {args : Array (KExpr .anon)} {i : Nat} (hidx : i < args.size) - {current : KExpr .anon} {currentV : Lean4Lean.VExpr} + {current : KExpr .anon} {currentV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hwhnf : DirectWhnf.WFAt semantics trProj world support uvars) (hcomponents : ForallComponentSupport support) @@ -215,14 +215,14 @@ replace any intermediate produced by `subst`. -/ def ProjectionParameterPlan (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (uvars : Nat) (Delta : KVLCtx) (args : Array (KExpr .anon)) : - List Nat → Lean4Lean.VExpr → Prop + List Nat → Ix.Theory.Named.VExpr → Prop | [], _ => True | i :: indices, currentV => ∃ hidx : i < args.size, support (args[i]'hidx) ∧ ProjectionArgumentFits trProj world support uvars Delta currentV (args[i]'hidx) ∧ - ∀ {next : KExpr .anon} {nextV : Lean4Lean.VExpr}, + ∀ {next : KExpr .anon} {nextV : Ix.Theory.Named.VExpr}, support next → TrKExpr world.venv uvars world.nameOf trProj Delta next nextV → ProjectionParameterPlan trProj world support uvars Delta args @@ -242,7 +242,7 @@ theorem instantiateProjParamsList_wf (hcollision : support.CollisionFree) {args : Array (KExpr .anon)} : ∀ (indices : List Nat) {current : KExpr .anon} - {currentV : Lean4Lean.VExpr} {s : TcState .anon}, + {currentV : Ix.Theory.Named.VExpr} {s : TcState .anon}, support current → TrKExpr world.venv uvars world.nameOf trProj Delta current currentV → ProjectionParameterPlan trProj world support uvars Delta args indices @@ -289,7 +289,7 @@ theorem instantiateProjParams_wf (hsubst : SubstitutionResources support) (hcollision : support.CollisionFree) {args : Array (KExpr .anon)} {numParams : Nat} - {ctorTy : KExpr .anon} {ctorTyV : Lean4Lean.VExpr} + {ctorTy : KExpr .anon} {ctorTyV : Ix.Theory.Named.VExpr} (hctorSupport : support ctorTy) (hctor : TrKExpr world.venv uvars world.nameOf trProj Delta ctorTy ctorTyV) @@ -314,9 +314,9 @@ theorem instantiateProjParams_wf supported Π view of the current constructor-field telescope. -/ def ProjectionFieldResultFits (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) - (uvars : Nat) (Delta : KVLCtx) (inputV projectedV : Lean4Lean.VExpr) : + (uvars : Nat) (Delta : KVLCtx) (inputV projectedV : Ix.Theory.Named.VExpr) : Prop := - ∀ {dom body : KExpr .anon} {domV bodyV : Lean4Lean.VExpr}, + ∀ {dom body : KExpr .anon} {domV bodyV : Ix.Theory.Named.VExpr}, support dom → support body → world.venv.IsType uvars Delta.toCtx domV → world.venv.IsType uvars (domV :: Delta.toCtx) bodyV → @@ -333,8 +333,8 @@ substitutes; it cannot choose the subsequent telescope result. -/ structure ProjectionFieldStepPlan (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (uvars : Nat) (Delta : KVLCtx) (structId : KId .anon) - (field : UInt64) (val : KExpr .anon) (projectedV : Lean4Lean.VExpr) - (i : Nat) (currentV : Lean4Lean.VExpr) : Prop where + (field : UInt64) (val : KExpr .anon) (projectedV : Ix.Theory.Named.VExpr) + (i : Nat) (currentV : Ix.Theory.Named.VExpr) : Prop where selected : i = field.toNat → ProjectionFieldResultFits trProj world support uvars Delta currentV projectedV @@ -347,7 +347,7 @@ structure ProjectionFieldStepPlan intermediate telescope yielded to the surrounding traversal. -/ def ProjectionFieldActionPost (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) - (uvars : Nat) (Delta : KVLCtx) (projectedV : Lean4Lean.VExpr) : + (uvars : Nat) (Delta : KVLCtx) (projectedV : Ix.Theory.Named.VExpr) : ForInStep (KExpr .anon) → Prop | .done result => support result ∧ InferPost trProj world uvars Delta projectedV result @@ -363,7 +363,7 @@ private theorem inferProjectionFieldSort_wf {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} - {dom : KExpr .anon} {domV : Lean4Lean.VExpr} + {dom : KExpr .anon} {domV : Ix.Theory.Named.VExpr} (hwhnf : DirectWhnf.WFAt semantics trProj world support uvars) (hsorts : SortComponentResources support) (hdomSupport : support dom) @@ -388,7 +388,7 @@ private theorem finishSelectedProjectionField_wf {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} - {inputV projectedV : Lean4Lean.VExpr} + {inputV projectedV : Ix.Theory.Named.VExpr} {dom body : KExpr .anon} (theory : WhnfTheory trProj world uvars) (hview : ForallView trProj world support uvars Delta inputV dom body) @@ -418,7 +418,7 @@ private theorem finishPrecedingProjectionField_wf {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {structId : KId .anon} {i : Nat} {val : KExpr .anon} - {inputV projectedV : Lean4Lean.VExpr} + {inputV projectedV : Ix.Theory.Named.VExpr} {dom body : KExpr .anon} (theory : WhnfTheory trProj world uvars) (hsubst : SubstitutionResources support) @@ -455,7 +455,7 @@ theorem inferProjFieldStep_wf {Delta : KVLCtx} {s : TcState .anon} {structId : KId .anon} {field : UInt64} {val current : KExpr .anon} {isPropStruct : Bool} {i : Nat} - {currentV projectedV : Lean4Lean.VExpr} + {currentV projectedV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hwhnf : DirectWhnf.WFAt semantics trProj world support uvars) (hcomponents : ForallComponentSupport support) @@ -532,13 +532,13 @@ production fold immediately. -/ def ProjectionFieldPlan (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (uvars : Nat) (Delta : KVLCtx) (structId : KId .anon) - (field : UInt64) (val : KExpr .anon) (projectedV : Lean4Lean.VExpr) : - List Nat → Lean4Lean.VExpr → Prop + (field : UInt64) (val : KExpr .anon) (projectedV : Ix.Theory.Named.VExpr) : + List Nat → Ix.Theory.Named.VExpr → Prop | [], _ => True | i :: indices, currentV => ProjectionFieldStepPlan trProj world support uvars Delta structId field val projectedV i currentV ∧ - ∀ {next : KExpr .anon} {nextV : Lean4Lean.VExpr}, + ∀ {next : KExpr .anon} {nextV : Ix.Theory.Named.VExpr}, support next → TrKExpr world.venv uvars world.nameOf trProj Delta next nextV → ProjectionFieldPlan trProj world support uvars Delta structId field @@ -549,7 +549,7 @@ def ProjectionFieldPlan last yielded telescope and will be rejected by production as unreachable. -/ def ProjectionFieldLoopPost (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) - (uvars : Nat) (Delta : KVLCtx) (projectedV : Lean4Lean.VExpr) : + (uvars : Nat) (Delta : KVLCtx) (projectedV : Ix.Theory.Named.VExpr) : Option (KExpr .anon) × KExpr .anon → Prop | (some result, _) => support result ∧ InferPost trProj world uvars Delta projectedV result @@ -565,7 +565,7 @@ private theorem inferProjFieldLoopStep_wf {Delta : KVLCtx} {s : TcState .anon} {structId : KId .anon} {field : UInt64} {val current : KExpr .anon} {isPropStruct : Bool} {i : Nat} - {currentV projectedV : Lean4Lean.VExpr} + {currentV projectedV : Ix.Theory.Named.VExpr} (theory : WhnfTheory trProj world uvars) (hwhnf : DirectWhnf.WFAt semantics trProj world support uvars) (hcomponents : ForallComponentSupport support) @@ -619,9 +619,9 @@ theorem inferProjFieldsList_wf (hsubst : SubstitutionResources support) (hcollision : support.CollisionFree) {structId : KId .anon} {field : UInt64} {val : KExpr .anon} - {isPropStruct : Bool} {projectedV : Lean4Lean.VExpr} : + {isPropStruct : Bool} {projectedV : Ix.Theory.Named.VExpr} : ∀ (indices : List Nat) {current : KExpr .anon} - {currentV : Lean4Lean.VExpr} {s : TcState .anon}, + {currentV : Ix.Theory.Named.VExpr} {s : TcState .anon}, support current → TrKExpr world.venv uvars world.nameOf trProj Delta current currentV → ProjectionFieldPlan trProj world support uvars Delta structId field val @@ -671,7 +671,7 @@ theorem inferProjFields_wf (hsubst : SubstitutionResources support) (hcollision : support.CollisionFree) {structId : KId .anon} {field : UInt64} {val ctorTy : KExpr .anon} - {isPropStruct : Bool} {ctorTyV projectedV : Lean4Lean.VExpr} + {isPropStruct : Bool} {ctorTyV projectedV : Ix.Theory.Named.VExpr} (hctorSupport : support ctorTy) (hctor : TrKExpr world.venv uvars world.nameOf trProj Delta ctorTy ctorTyV) @@ -703,4 +703,4 @@ theorem inferProjFields_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/ProjectionTypes.lean b/Ix/Kernel/Verify/Infer/ProjectionTypes.lean similarity index 97% rename from Ix/Tc/Verify/Infer/ProjectionTypes.lean rename to Ix/Kernel/Verify/Infer/ProjectionTypes.lean index 80a1762a7..f1e3cfe9a 100644 --- a/Ix/Tc/Verify/Infer/ProjectionTypes.lean +++ b/Ix/Kernel/Verify/Infer/ProjectionTypes.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Infer.ProjectionClassification +import Ix.Kernel.Verify.Infer.ProjectionClassification /-! # Projection inference @@ -19,7 +19,7 @@ edge, error propagation, and composition with the helper—without treating a successful helper execution as semantic evidence by itself. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Finite child coverage for a supported projection source. Run support is finite and intentionally not closed under arbitrary syntax descent. -/ @@ -66,7 +66,7 @@ structure ConstructorTypingPlan (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (uvars : Nat) (Delta : KVLCtx) (structId : KId .anon) (field : UInt64) (val : KExpr .anon) - (projectedV : Lean4Lean.VExpr) (args : Array (KExpr .anon)) + (projectedV : Ix.Theory.Named.VExpr) (args : Array (KExpr .anon)) (levels : Array (KUniv .anon)) (numParams : Nat) (ctorTy : KExpr .anon) : Prop where instantiated : ∀ {instantiated : KExpr .anon}, @@ -81,7 +81,7 @@ structure ConstructorTypingPlan ([0:numParams] : _root_.Std.Legacy.Range).step) instantiatedV ∧ ∀ {parameterized : KExpr .anon} - {parameterizedV : Lean4Lean.VExpr}, + {parameterizedV : Ix.Theory.Named.VExpr}, support parameterized → TrKExpr world.venv uvars world.nameOf trProj Delta parameterized parameterizedV → @@ -101,7 +101,7 @@ selection, and the source projection witness. -/ def DeclarationOracle (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (uvars : Nat) : Prop := ∀ {Delta : KVLCtx} {structId headId : KId .anon} {field : UInt64} - {val : KExpr .anon} {valV projectedV valTyV reducedTyV : Lean4Lean.VExpr} + {val : KExpr .anon} {valV projectedV valTyV reducedTyV : Ix.Theory.Named.VExpr} {structName : Lean.Name} {levels : Array (KUniv .anon)} {headInfo : ExprInfo .anon} {args : Array (KExpr .anon)} {indName : Mode.anon.F Name} @@ -141,7 +141,7 @@ def WF (semantics : CacheSemantics) (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (uvars : Nat) : Prop := ∀ {Delta : KVLCtx} {s : TcState .anon} {structId : KId .anon} {field : UInt64} {val valTy : KExpr .anon} - {valV projectedV : Lean4Lean.VExpr} {structName : Lean.Name}, + {valV projectedV : Ix.Theory.Named.VExpr} {structName : Lean.Name}, world.nameOf structId.addr = some structName → TrKExprS world.venv uvars world.nameOf trProj Delta val valV → trProj uvars Delta.toCtx structName field.toNat valV projectedV → @@ -190,7 +190,7 @@ theorem inferProj_wf {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {structId : KId .anon} {field : UInt64} - {val valTy : KExpr .anon} {valV projectedV : Lean4Lean.VExpr} + {val valTy : KExpr .anon} {valV projectedV : Ix.Theory.Named.VExpr} {structName : Lean.Name} (theory : WhnfTheory trProj world uvars) (hwhnf : DirectWhnf.WFAt semantics trProj world support uvars) @@ -355,7 +355,7 @@ theorem inferUncached_prj_wf {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {inferOnly : Bool} {structId : KId .anon} {field : UInt64} {val : KExpr .anon} - {info : ExprInfo .anon} {sourceV : Lean4Lean.VExpr} + {info : ExprInfo .anon} {sourceV : Ix.Theory.Named.VExpr} (hinputs : ProjectionValueSupport support) (hprojection : ProjectionInference.WF semantics trProj world support uvars) @@ -380,4 +380,4 @@ theorem inferUncached_prj_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/ScopedLocals.lean b/Ix/Kernel/Verify/Infer/ScopedLocals.lean similarity index 97% rename from Ix/Tc/Verify/Infer/ScopedLocals.lean rename to Ix/Kernel/Verify/Infer/ScopedLocals.lean index 05d5b0013..627a7e0ab 100644 --- a/Ix/Tc/Verify/Infer/ScopedLocals.lean +++ b/Ix/Kernel/Verify/Infer/ScopedLocals.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Infer.Callbacks +import Ix.Kernel.Verify.Infer.Callbacks /-! # Scoped local contexts for inference @@ -9,7 +9,7 @@ and failure. This module connects the operational cleanup to the ghost context used by the verification invariant. -/ -namespace Ix.Tc +namespace Ix.Kernel @[simp] theorem LocalContext.truncate_size (lctx : LocalContext m) : lctx.truncate lctx.size = lctx := by @@ -110,7 +110,7 @@ theorem openFVar {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {before after : TcState .anon} - {d : LocalDecl .anon} {vd : Lean4Lean.VLocalDecl} + {d : LocalDecl .anon} {vd : Ix.Theory.Named.VLocalDecl} {deps : List FVarId} (hbefore : WhnfStateInv layer semantics trProj world support uvars Delta before) @@ -140,7 +140,7 @@ theorem closeFVar {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {fv : FVarId} {deps : List FVarId} - {vd : Lean4Lean.VLocalDecl} {saved : Nat} + {vd : Ix.Theory.Named.VLocalDecl} {saved : Nat} (h : WhnfStateInv layer semantics trProj world support uvars ((some (fv, deps), vd) :: Delta) s) (hsaved : saved = Delta.fvars.length) : @@ -163,7 +163,7 @@ theorem closeFVarAtEntry {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {before after : TcState .anon} {fv : FVarId} {deps : List FVarId} - {vd : Lean4Lean.VLocalDecl} + {vd : Ix.Theory.Named.VLocalDecl} (hbefore : WhnfStateInv layer semantics trProj world support uvars Delta before) (hafter : WhnfStateInv layer semantics trProj world support uvars @@ -238,4 +238,4 @@ theorem withLctxScope_eq (x : RecM .anon α) end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/SortTypes.lean b/Ix/Kernel/Verify/Infer/SortTypes.lean similarity index 92% rename from Ix/Tc/Verify/Infer/SortTypes.lean rename to Ix/Kernel/Verify/Infer/SortTypes.lean index ad66c499a..f7d5b5dc3 100644 --- a/Ix/Tc/Verify/Infer/SortTypes.lean +++ b/Ix/Kernel/Verify/Infer/SortTypes.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Infer.Callbacks +import Ix.Kernel.Verify.Infer.Callbacks + +open Ix.Theory (VLevel) /-! # Sort exposure for inference @@ -8,7 +10,7 @@ an inferred type. This module proves the syntactic sort fast path and the direct-WHNF fallback against one shared semantic view. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Finite descent resources for a supported concrete sort. Smart universe constructors compare addresses throughout their argument subtrees and use @@ -24,7 +26,7 @@ def SortComponentResources (support : RunSupport) : Prop := connects the caller's quotient translation to the selected Theory sort and retains exactly the subtree support needed by later smart constructors. -/ structure SortView (world : VerifyWorld) (support : RunSupport) - (uvars : Nat) (Delta : KVLCtx) (inputV : Lean4Lean.VExpr) + (uvars : Nat) (Delta : KVLCtx) (inputV : Ix.Theory.Named.VExpr) (result : KUniv .anon) : Prop where sizeBound : result.size < UInt64.size subtermSupport : ∀ x, KUniv.Sub x result → support.univ x @@ -34,7 +36,7 @@ structure SortView (world : VerifyWorld) (support : RunSupport) theorem SortView.rootSupport {world : VerifyWorld} {support : RunSupport} {uvars : Nat} - {Delta : KVLCtx} {inputV : Lean4Lean.VExpr} {result : KUniv .anon} + {Delta : KVLCtx} {inputV : Ix.Theory.Named.VExpr} {result : KUniv .anon} (h : SortView world support uvars Delta inputV result) : support.univ result := h.subtermSupport result .refl @@ -46,7 +48,7 @@ comparison made by the smart constructor is covered by the two finite subterm footprints retained in the sort views. -/ theorem mkIMax_equiv {world : VerifyWorld} {support : RunSupport} {uvars : Nat} - {DeltaA DeltaB : KVLCtx} {inputA inputB : Lean4Lean.VExpr} + {DeltaA DeltaB : KVLCtx} {inputA inputB : Ix.Theory.Named.VExpr} {a b : KUniv .anon} (hcf : support.CollisionFree) (ha : SortView world support uvars DeltaA inputA a) @@ -73,7 +75,7 @@ private theorem ensureSortWhnf_wf {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {input : KExpr .anon} - {inputCoreV inputV : Lean4Lean.VExpr} + {inputCoreV inputV : Ix.Theory.Named.VExpr} (hwhnf : DirectWhnf.WFAt semantics trProj world support uvars) (hresources : SortComponentResources support) (hinputSupport : support input) @@ -109,7 +111,7 @@ theorem ensureSortDirect_wf {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {input : KExpr .anon} - {inputV : Lean4Lean.VExpr} + {inputV : Ix.Theory.Named.VExpr} (hwhnf : DirectWhnf.WFAt semantics trProj world support uvars) (hresources : SortComponentResources support) (hinputSupport : support input) @@ -136,4 +138,4 @@ theorem ensureSortDirect_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Infer/Substitution.lean b/Ix/Kernel/Verify/Infer/Substitution.lean similarity index 93% rename from Ix/Tc/Verify/Infer/Substitution.lean rename to Ix/Kernel/Verify/Infer/Substitution.lean index c3308fc95..0a74bf1b1 100644 --- a/Ix/Tc/Verify/Infer/Substitution.lean +++ b/Ix/Kernel/Verify/Infer/Substitution.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Infer.Callbacks +import Ix.Kernel.Verify.Infer.Callbacks /-! # Recursive-result substitution resources @@ -8,7 +8,7 @@ callback. Since that body is dynamic, this module exposes the walker through a finite support closure rather than static request-list membership. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Finite operational and arithmetic closure for substitution over supported inputs. -/ @@ -44,7 +44,7 @@ theorem whnf_wf hbodySupport hargSupport apply TcM.WF.mono (TcM.runIntern_whnf_wf (fun it hwf hcover => by - have post := Ix.Tc.subst_spec hcollision.expr hbody harg hcut hargsz + have post := Ix.Kernel.subst_spec hcollision.expr hbody harg hcut hargsz hreach hwf hcover.expr exact ⟨post.1, post.2.1, hcover.of_expr_univs post.2.2 @@ -57,4 +57,4 @@ theorem whnf_wf end SubstitutionResources -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/InferDefEq/Closure.lean b/Ix/Kernel/Verify/InferDefEq/Closure.lean similarity index 96% rename from Ix/Tc/Verify/InferDefEq/Closure.lean rename to Ix/Kernel/Verify/InferDefEq/Closure.lean index 58527e9ed..830f4759b 100644 --- a/Ix/Tc/Verify/InferDefEq/Closure.lean +++ b/Ix/Kernel/Verify/InferDefEq/Closure.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq.Closure -import Ix.Tc.Verify.Infer.CacheSoundness +import Ix.Kernel.Verify.DefEq.Closure +import Ix.Kernel.Verify.Infer.CacheSoundness /-! # Recursive inference and definitional-equality closure @@ -10,7 +10,7 @@ universe induction step: both fields may call a strictly smaller method table, and neither proof assumes the next table is already sound. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Concrete resources for the inference and DefEq fields of one production method-table layer. The shared proposition context fixes the suffix model, @@ -66,4 +66,4 @@ theorem closedAt end InferDefEqClosureContext -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Ingress/AnonStructural.lean b/Ix/Kernel/Verify/Ingress/AnonStructural.lean similarity index 94% rename from Ix/Tc/Verify/Ingress/AnonStructural.lean rename to Ix/Kernel/Verify/Ingress/AnonStructural.lean index 77f899f4c..0c3cb779e 100644 --- a/Ix/Tc/Verify/Ingress/AnonStructural.lean +++ b/Ix/Kernel/Verify/Ingress/AnonStructural.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Const -import Ix.Tc.Verify.Expr +import Ix.Kernel.Const +import Ix.Kernel.Verify.Expr /-! # Structural equality for anonymous kernel values @@ -14,7 +14,7 @@ left-inverse back to the production datatype. Equality reflected through that left-inverse is therefore structural equality, not hash equality. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace AnonStructural def addressDecidableEq : DecidableEq Address := @@ -63,13 +63,13 @@ structure ExprInfo where hasFVars : Bool deriving DecidableEq -def ExprInfo.ofKernel (info : Ix.Tc.ExprInfo .anon) : ExprInfo := +def ExprInfo.ofKernel (info : Ix.Kernel.ExprInfo .anon) : ExprInfo := ⟨info.addr, info.lbr, info.count0, info.hasFVars⟩ -def ExprInfo.toKernel (info : ExprInfo) : Ix.Tc.ExprInfo .anon := +def ExprInfo.toKernel (info : ExprInfo) : Ix.Kernel.ExprInfo .anon := ⟨info.addr, info.lbr, info.count0, info.hasFVars, (), (), ()⟩ -@[simp] theorem ExprInfo.roundtrip (info : Ix.Tc.ExprInfo .anon) : +@[simp] theorem ExprInfo.roundtrip (info : Ix.Kernel.ExprInfo .anon) : (ofKernel info).toKernel = info := by cases info rfl @@ -135,13 +135,13 @@ structure RecRule where rhs : Expr deriving DecidableEq -def RecRule.ofKernel (rule : Ix.Tc.RecRule .anon) : RecRule := +def RecRule.ofKernel (rule : Ix.Kernel.RecRule .anon) : RecRule := ⟨rule.fields, Expr.ofKernel rule.rhs⟩ -def RecRule.toKernel (rule : RecRule) : Ix.Tc.RecRule .anon := +def RecRule.toKernel (rule : RecRule) : Ix.Kernel.RecRule .anon := ⟨(), rule.fields, rule.rhs.toKernel⟩ -@[simp] theorem RecRule.roundtrip (rule : Ix.Tc.RecRule .anon) : +@[simp] theorem RecRule.roundtrip (rule : Ix.Kernel.RecRule .anon) : (ofKernel rule).toKernel = rule := by cases rule simp [ofKernel, toKernel] @@ -228,4 +228,4 @@ def constDecidableEq : DecidableEq (KConst .anon) := decidableEqOfRoundtrip Const.ofKernel Const.toKernel Const.roundtrip end AnonStructural -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Ingress/LiteralBlobs.lean b/Ix/Kernel/Verify/Ingress/LiteralBlobs.lean similarity index 99% rename from Ix/Tc/Verify/Ingress/LiteralBlobs.lean rename to Ix/Kernel/Verify/Ingress/LiteralBlobs.lean index 41aa8c664..4b266d104 100644 --- a/Ix/Tc/Verify/Ingress/LiteralBlobs.lean +++ b/Ix/Kernel/Verify/Ingress/LiteralBlobs.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Ingress.AnonStructural -import Ix.Tc.Verify.Ingress.Representation +import Ix.Kernel.Verify.Ingress.AnonStructural +import Ix.Kernel.Verify.Ingress.Representation /-! # Serialized literal/blob ingress @@ -12,7 +12,7 @@ malformed environments demonstrate that the decoder rejects a constant or a blob stored under an address that does not commit to its bytes. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace SerializedLiteralBlobs local instance addressDecidableEq : DecidableEq Address := @@ -462,4 +462,4 @@ theorem malformedBlobRejected : IxonEnv.serializedDecodeRejected_of_true malformedBlobRejectedNative end SerializedLiteralBlobs -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Ingress/Representation.lean b/Ix/Kernel/Verify/Ingress/Representation.lean similarity index 98% rename from Ix/Tc/Verify/Ingress/Representation.lean rename to Ix/Kernel/Verify/Ingress/Representation.lean index 79640e35e..4711d3c26 100644 --- a/Ix/Tc/Verify/Ingress/Representation.lean +++ b/Ix/Kernel/Verify/Ingress/Representation.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Driver.Enumeration -import Ix.Tc.Verify.Driver.Serial -import Ix.Tc.Verify.Whnf.Runtime.LazyIngress +import Ix.Kernel.Verify.Driver.Enumeration +import Ix.Kernel.Verify.Driver.Serial +import Ix.Kernel.Verify.Whnf.Runtime.LazyIngress /-! # Serialized Ixon representation correspondence @@ -18,7 +18,7 @@ later Rust transport phase; T0 reasons from the pure decoder and the actual Lean eager/lazy ingress functions. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace IxonEnv @@ -261,4 +261,4 @@ def SerializedSubjectWF (bytes : ByteArray) (world : VerifyWorld) Nonempty (SerializedSubjectCertificate bytes world dependencies assumptions lazyRequests) -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Ingress/SerializedBoolean.lean b/Ix/Kernel/Verify/Ingress/SerializedBoolean.lean similarity index 99% rename from Ix/Tc/Verify/Ingress/SerializedBoolean.lean rename to Ix/Kernel/Verify/Ingress/SerializedBoolean.lean index 3d5cdaac7..fbd208d02 100644 --- a/Ix/Tc/Verify/Ingress/SerializedBoolean.lean +++ b/Ix/Kernel/Verify/Ingress/SerializedBoolean.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Driver.BooleanAcceptance -import Ix.Tc.Verify.Ingress.AnonStructural -import Ix.Tc.Verify.Ingress.Representation +import Ix.Kernel.Verify.Driver.BooleanAcceptance +import Ix.Kernel.Verify.Ingress.AnonStructural +import Ix.Kernel.Verify.Ingress.Representation /-! # Serialized Boolean acceptance @@ -11,7 +11,7 @@ decoder, erases anonymous-irrelevant metadata, and reconnects the decoded source to the existing E3-S semantic world. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace BooleanSerialized open BooleanEnumerationFixture @@ -996,4 +996,4 @@ theorem subjectWF : ⟨certificate⟩ end BooleanSerialized -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/InstL.lean b/Ix/Kernel/Verify/InstL.lean similarity index 95% rename from Ix/Tc/Verify/InstL.lean rename to Ix/Kernel/Verify/InstL.lean index a22da5287..e80a4eb37 100644 --- a/Ix/Tc/Verify/InstL.lean +++ b/Ix/Kernel/Verify/InstL.lean @@ -1,5 +1,7 @@ -import Ix.Tc.Verify.Trans -import Ix.Tc.Verify.InstUniv +import Ix.Kernel.Verify.Trans +import Ix.Kernel.Verify.InstUniv + +open Ix.Theory (VLevel) /-! # Universe instantiation tracks `VExpr.instL` through the translation @@ -20,9 +22,9 @@ The collision-freedom and no-wrap side conditions quantify over per level). -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr VLevel VEnv) +open Ix.Theory.Named (VExpr VEnv) /-! ### Level occurrences and the reach set -/ @@ -207,25 +209,25 @@ private theorem forall₂_toVLevel {us : Array (KUniv .anon)} : spine carries only the literal level `.zero`, which `inst` fixes.) -/ private theorem instL_listCharLit (s : List Char) (ls : List VLevel) : - (Lean4Lean.VExpr.listCharLit s).instL ls - = Lean4Lean.VExpr.listCharLit s := by + (Ix.Theory.Named.VExpr.listCharLit s).instL ls + = Ix.Theory.Named.VExpr.listCharLit s := by induction s with | nil => rfl | cons c s ih => - show Lean4Lean.VExpr.app (Lean4Lean.VExpr.app _ (Lean4Lean.VExpr.app _ - ((Lean4Lean.VExpr.natLit c.toNat).instL ls))) - ((Lean4Lean.VExpr.listCharLit s).instL ls) = _ - rw [Lean4Lean.VExpr.instL_natLit, ih] + show Ix.Theory.Named.VExpr.app (Ix.Theory.Named.VExpr.app _ (Ix.Theory.Named.VExpr.app _ + ((Ix.Theory.Named.VExpr.natLit c.toNat).instL ls))) + ((Ix.Theory.Named.VExpr.listCharLit s).instL ls) = _ + rw [Ix.Theory.Named.VExpr.instL_natLit, ih] rfl private theorem instL_trLiteral (l : Lean.Literal) (ls : List VLevel) : - (Lean4Lean.VExpr.trLiteral l).instL ls - = Lean4Lean.VExpr.trLiteral l := by + (Ix.Theory.Named.VExpr.trLiteral l).instL ls + = Ix.Theory.Named.VExpr.trLiteral l := by cases l with - | natVal v => exact Lean4Lean.VExpr.instL_natLit + | natVal v => exact Ix.Theory.Named.VExpr.instL_natLit | strVal s => - show Lean4Lean.VExpr.app _ - ((Lean4Lean.VExpr.listCharLit _).instL ls) = _ + show Ix.Theory.Named.VExpr.app _ + ((Ix.Theory.Named.VExpr.listCharLit _).instL ls) = _ rw [instL_listCharLit] rfl @@ -321,7 +323,7 @@ theorem TrKExprS.instL {env : VEnv} {uvars U' : Nat} have harr := array_mapM_ok hmus have hsize : vs.size = curUs.size := by rw [← Array.length_toList, ← Array.length_toList] - exact (Lean4Lean.List.Forall₂.length_eq harr).symm + exact (Ix.Theory.Named.List.Forall₂.length_eq harr).symm have hwf : ∀ w ∈ vs, (KUniv.toVLevel w).WF U' := by intro w hw obtain ⟨a, _, hab⟩ := forall₂_mem_right harr w (by simpa using hw) @@ -473,9 +475,9 @@ theorem TrKExprS.instL {env : VEnv} {uvars U' : Nat} = .ok (.nat n blob md) := rfl rw [hb] at hr cases hr - rw [show (Lean4Lean.VExpr.natLit n).instL + rw [show (Ix.Theory.Named.VExpr.natLit n).instL (us.toList.map KUniv.toVLevel) - = Lean4Lean.VExpr.natLit n from Lean4Lean.VExpr.instL_natLit] + = Ix.Theory.Named.VExpr.natLit n from Ix.Theory.Named.VExpr.instL_natLit] exact (TrKExprS.nat l1).trKExpr henv.ordered hlit htp.wf (wf_instL_size hus hΔ) | @str Δ s blob md l1 => @@ -488,4 +490,4 @@ theorem TrKExprS.instL {env : VEnv} {uvars U' : Nat} exact (TrKExprS.str l1).trKExpr henv.ordered hlit htp.wf (wf_instL_size hus hΔ) -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/InstUniv.lean b/Ix/Kernel/Verify/InstUniv.lean similarity index 99% rename from Ix/Tc/Verify/InstUniv.lean rename to Ix/Kernel/Verify/InstUniv.lean index 2dff7aa99..c3f2e2d1e 100644 --- a/Ix/Tc/Verify/InstUniv.lean +++ b/Ix/Kernel/Verify/InstUniv.lean @@ -1,13 +1,13 @@ -import Ix.Tc.Verify.Subst -import Ix.Tc.Verify.Monad -import Ix.Tc.Verify.Level +import Ix.Kernel.Verify.Subst +import Ix.Kernel.Verify.Monad +import Ix.Kernel.Verify.Level /-! # `instantiateUnivParams` memo-soundness The last of the five expression walkers: universe-parameter substitution with per-call address-keyed memoization (`TcM.instUnivInner`, -`Ix/Tc/Monad.lean`). Unlike the four `WalkM` siblings this one runs over +`Ix/Kernel/Monad.lean`). Unlike the four `WalkM` siblings this one runs over `TcM` — `StateT (HashMap Address (KExpr m)) (TcM m)` — so its soundness statement is the first composition of the walker kit with the EStateM Hoare kernel (`TcM.WF`): the intern table lives inside `TcState.env`, @@ -30,11 +30,11 @@ Scope: anon mode, matching the sibling masters (`internKey = addr`, `eraseMeta = id`, exact spec equality). -/ -namespace Ix.Tc +namespace Ix.Kernel open Std (HashMap) open EStateM (Result) -open Lean4Lean (VLevel) +open Ix.Theory (VLevel) /-! ### The pure spec -/ diff --git a/Ix/Tc/Verify/Knot.lean b/Ix/Kernel/Verify/Knot.lean similarity index 99% rename from Ix/Tc/Verify/Knot.lean rename to Ix/Kernel/Verify/Knot.lean index f09d54e96..a4fda3c5c 100644 --- a/Ix/Tc/Verify/Knot.lean +++ b/Ix/Kernel/Verify/Knot.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf +import Ix.Kernel.Verify.Whnf /-! # Verification of the recursive method knot @@ -18,7 +18,7 @@ K1 supplies the four WHNF fields and K2 supplies inference and definitional equality. No theorem below assumes the recursive table is already closed. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace Methods @@ -353,4 +353,4 @@ theorem runRec_wfAt end TcM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Level.lean b/Ix/Kernel/Verify/Level.lean similarity index 98% rename from Ix/Tc/Verify/Level.lean rename to Ix/Kernel/Verify/Level.lean index d4f2e486f..3dfb8d4b5 100644 --- a/Ix/Tc/Verify/Level.lean +++ b/Ix/Kernel/Verify/Level.lean @@ -1,16 +1,13 @@ -import Ix.Tc.Level +import Ix.Kernel.Level import Batteries.Recycling.RBTree.Lemmas -import Lean4Lean.Theory.VLevel +import Ix.Theory.Named.VLevel /-! -# Level slice: `KUniv` soundness against `Lean4Lean.Theory.VLevel` +# Universe soundness against `Ix.Theory.VLevel` -The first verification slice. This file is also the lean4lean-interop -spike. Interop outcome (2026-07-21): proof files are **classic** (no `module` header) — -a `module` file cannot import the classic-import lean4lean dep at all -(`cannot import non-`module` … from `module``), while classic files import -module-system `Ix.Tc` fine and see its `@[expose]` bodies. The sanity -`rfl`-lemmas below certify the cross-boundary unfolding. +These proofs use classic imports to combine the named specification's helper +lemmas with the exposed production definitions in `Ix.Kernel`. The `rfl` +lemmas below verify unfolding across that module boundary. `KUniv` and `VLevel` align constructor-for-constructor (both carry positional params), so the translation `toVLevel` is a total structural @@ -21,23 +18,22 @@ the headline theorems state that the kernel's `univEq`/`univGeq` decide compared pair (the `CollisionFree` pilot: `==` on `KUniv` is Blake3 address equality, sound only up to hash collisions). -Status: the slice is **sorry-free** — the full chain `univEq_sound` / -`univGeq_sound` ← `normalizeLevel_eval` ← the 4-way `normalizeAux_eval` -keystone + `subsumption_eval` (upstream's `Verify/Level.lean:545` is -still a live sorry there; proven here via the pure per-key model and a -lex (path length, constant-after-var) strong induction) is closed, -conditional only on addr-faithfulness and the UInt64 no-wrap bounds. +The proofs of `univEq_sound` and `univGeq_sound` contain no proof holes. +They use `normalizeLevel_eval`, the four cases of `normalizeAux_eval`, and +`subsumption_eval`, with address faithfulness and UInt64 bounds as explicit +hypotheses. Subsumption is proved using a pure model of each key and strong +induction on path length and the constant contribution after a variable. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VLevel) +open Ix.Theory (VLevel) variable {m : Mode} namespace KUniv -/-- Translate a kernel level to the lean4lean theory's `VLevel`. +/-- Translate a kernel level to the shared theory's `VLevel`. Structure-preserving; addresses and display names are dropped (`VLevel` is the fully anonymous mathematical object). -/ def toVLevel : KUniv m → VLevel @@ -72,7 +68,7 @@ def eraseMeta : KUniv m → KUniv .anon | param => rfl /-! Interop sanity checks: these unfold `@[expose]` bodies from -`Ix.Tc.Level` (smart constructors through `Id.run`) against `VLevel` +`Ix.Kernel.Level` (smart constructors through `Id.run`) against `VLevel` constructors from the classic-import dep — the interop spike proper. -/ theorem toVLevel_mkZero : (mkZero (m := m)).toVLevel = .zero := rfl @@ -118,15 +114,11 @@ theorem KUniv.toVLevel_of_isZero {v : KUniv m} (h : v.isZero = true) : /-! ### Denotation of the canonical form -Port of the upstream denotation layer (lean4lean `Verify/Level.lean`, -`evalParam`/`Node.eval`/`evalPath`/`NormLevel.eval`), leaner here because -params are positional (no `ls : List Name` plumbing — `evalParam` *is* -`VLevel.eval`'s param case) but with a genuinely new obligation class: Ix -stores offsets/constants as `UInt64`, so the normalization bookkeeping's -`+ 1`s carry no-wrap side conditions, threaded as size bounds -(`k.toNat + l.size < UInt64.size`). An in-memory `KUniv` can never -violate them; a 2⁶⁴-succ tower genuinely would wrap and mis-normalize — -the hypotheses are load-bearing, not pedantry. -/ +This adapts the denotation layer in `Ix.Theory.Named.Verify.Level` to +positional parameters: `evalParam` is the parameter case of `VLevel.eval`. +Ix stores offsets and constants as `UInt64`, so additions also require +bounds preventing wraparound, such as `k.toNat + l.size < UInt64.size`. +Those bounds are explicit hypotheses throughout the normalization proof. -/ namespace Level @@ -281,7 +273,7 @@ private theorem foldl_max_le {α : Type _} {l : List α} {f : α → Nat} | nil => simp | cons a l ih => rw [List.foldl_cons, ih, Nat.max_le] - simp [List.forall_mem_cons, and_assoc] + simp [and_assoc] theorem NormNode.eval_le {ρ : List Nat} {n : NormNode} {x : Nat} : n.eval ρ ≤ x ↔ n.constant.toNat ≤ x ∧ ∀ v ∈ n.vars, v.eval ρ ≤ x := by @@ -450,14 +442,16 @@ theorem EvalPaths.one_le {ρ : List Nat} {path : Path} {n : Nat} private theorem imax_max (a b c : Nat) : Lean.Nat.imax a (max b c) = max (Lean.Nat.imax a b) (Lean.Nat.imax a c) := by by_cases hb : b = 0 <;> by_cases hc : c = 0 <;> - simp [Lean.Nat.imax, hb, hc, Nat.max_eq_max, Nat.max_eq_zero_iff] <;> omega + simp [Lean.Nat.imax, hb, hc, Nat.max_eq_max, Nat.max_eq_zero_iff] + all_goals omega /-- `imax(a, imax(b, c)) = max(imax(a, c), imax(b, c))` — the semantic content of the `normalizeImaxImax` rewrite. -/ private theorem imax_imax (a b c : Nat) : Lean.Nat.imax a (Lean.Nat.imax b c) = max (Lean.Nat.imax a c) (Lean.Nat.imax b c) := by by_cases hc : c = 0 <;> by_cases hb : b = 0 <;> - simp [Lean.Nat.imax, hb, hc, Nat.max_eq_max, Nat.max_eq_zero_iff] <;> omega + simp [Lean.Nat.imax, hb, hc, Nat.max_eq_max, Nat.max_eq_zero_iff] + all_goals omega /-! #### Insert-eval lemmas -/ @@ -738,7 +732,7 @@ private theorem normalizeAux_eval' {ρ : List Nat} : | .imax u (.zero b_ad) ad, path, k, acc, hk, hp => by simp only [normalizeAux] rw [NormLevel.addConst_eval hp] - simp [KUniv.toVLevel, VLevel.eval, Lean.Nat.imax] + simp [KUniv.toVLevel, VLevel.eval, VLevel.natIMax] | .imax u (.succ v b_ad) ad, path, k, acc, hk, hp => by have hk1 : (k + 1).toNat = k.toNat + 1 := toNat_add_one (by simp [KUniv.size] at hk; omega) @@ -928,7 +922,7 @@ private theorem normalizeImaxDispatch_eval' {ρ : List Nat} : | a, .zero b_ad, path, k, acc, hk, hp => by simp only [normalizeImaxDispatch] rw [NormLevel.addConst_eval hp] - simp [KUniv.toVLevel, VLevel.eval, Lean.Nat.imax] + simp [KUniv.toVLevel, VLevel.eval, VLevel.natIMax] | a, .succ v b_ad, path, k, acc, hk, hp => by have hsza := KUniv.size_pos a have hk1 : (k + 1).toNat = k.toNat + 1 := @@ -1182,7 +1176,7 @@ theorem subsumption_eq_model (acc : NormLevel) : rw [processEntry] refine forIn_id_eq_foldl _ _ _ _ ?_ rintro ⟨p2, n2⟩ hmem2 n1 - simp only [letFun, subsumeStep] + simp only [subsumeStep] split · split · split @@ -1789,7 +1783,7 @@ theorem varsOnPath_subsumption {l : NormLevel} (hl : VarsOnPath l) : (P := fun (n1f : NormNode) => ∀ v ∈ n1f.vars, v ∈ n1₀.vars) _ _ _ ?_ ?_ · exact fun v hv => hv rintro ⟨p2, n2⟩ hmem2 n1cur hcur v hv - simp only [match_id_ite, match_id_bind_unit] at hv + simp only [match_id_ite] at hv split at hv · split at hv · split at hv @@ -2335,8 +2329,7 @@ private theorem le_subsumptionModel_eval {ρ : List Nat} {l : NormLevel} exact ⟨CC (p1.length + 1) p1 n1₀ hf (Nat.lt_succ_self _) hnz, fun v hv => V (p1.length + 1) p1 n1₀ hf (Nat.lt_succ_self _) hnz v hv⟩ -/-- **Fresh** (upstream's live sorry, lean4lean `Verify/Level.lean:545`): - the subsumption pass drops only dominated contributions. Needs the +/-- The subsumption pass drops only dominated contributions. Needs the var-placement invariant (stated raw; defeq to `VarsOnPath l`): the `maxVarOffset + 1` zeroing branch is justified by a var of the *same* entry whose active index contributes ≥ 1 — for raw maps it is @@ -2400,7 +2393,7 @@ theorem KUniv.isSemanticZero_eq_false {m : Mode} {u : KUniv m} rw [KUniv.isSemanticZero, Bool.or_eq_false_iff] refine ⟨?_, ?_⟩ · cases u <;> simp_all [KUniv.isZero, KUniv.toVLevel, - Lean4Lean.VLevel.eval] + Ix.Theory.VLevel.eval] · rw [Bool.eq_false_iff, ne_eq, List.all_eq_true] intro hall have hzero : Level.NormLevel.eval ρ (Level.normalizeLevel u) = 0 := @@ -2417,7 +2410,7 @@ theorem KUniv.toVLevel_equiv_zero_of_isSemanticZero {m : Mode} {u : KUniv m} (hu : u.size < UInt64.size) (hzero : u.isSemanticZero = true) : KUniv.toVLevel u ≈ .zero := by rw [KUniv.isSemanticZero, Bool.or_eq_true] at hzero - refine Lean4Lean.VLevel.equiv_def.mpr fun ρ => ?_ + refine Ix.Theory.VLevel.equiv_def.mpr fun ρ => ?_ show _ = 0 rcases hzero with h | h · rw [KUniv.toVLevel_of_isZero h]; rfl @@ -2455,7 +2448,7 @@ theorem normLevelLe_sound {u v : KUniv m} universe-equality decision procedure accepts, the translations are semantically equal under every parameter assignment. Conditional on addr-faithfulness of the compared pair (the `u == v` fast path at - Ix/Tc/Level.lean:426 — discharged through the `CollisionFree` pilot) + Ix/Kernel/Level.lean:426 — discharged through the `CollisionFree` pilot) and the UInt64 no-wrap size bounds. -/ theorem univEq_sound {u v : KUniv m} (hinj : u.AddrFaithful v) (hu : u.size < UInt64.size) (hv : v.size < UInt64.size) @@ -2468,7 +2461,7 @@ theorem univEq_sound {u v : KUniv m} (hinj : u.AddrFaithful v) /-- **Soundness of `univGeq`** (headline, Level slice): if the kernel accepts `u ≥ v`, then `⟦v⟧ρ ≤ ⟦u⟧ρ` for every assignment. All three - disjuncts of Ix/Tc/Level.lean:430 discharged: the addr fast path via + disjuncts of Ix/Kernel/Level.lean:430 discharged: the addr fast path via addr-faithfulness, `v.isZero` via `VLevel.zero_le`, and the canonical order via `normLevelLe_sound`. -/ theorem univGeq_sound {u v : KUniv m} (hinj : u.AddrFaithful v) @@ -2482,7 +2475,7 @@ theorem univGeq_sound {u v : KUniv m} (hinj : u.AddrFaithful v) /-! ### Simplifying smart constructors: `mkMax`/`mkIMax` soundness -`substUniv` (universe instantiation, `Ix/Tc/Monad.lean`) rebuilds with +`substUniv` (universe instantiation, `Ix/Kernel/Monad.lean`) rebuilds with the SIMPLIFYING `mkMax`/`mkIMax` (Lean/Rust parity), so its Theory correspondence is an equivalence `≈`, never a syntactic equality. This block proves the two constructors sound wrt `VLevel.eval`, conditional @@ -2979,4 +2972,4 @@ theorem toVLevel_mkIMax_wf {n : Nat} {a b : KUniv m} end KUniv -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Monad.lean b/Ix/Kernel/Verify/Monad.lean similarity index 98% rename from Ix/Tc/Verify/Monad.lean rename to Ix/Kernel/Verify/Monad.lean index b49e3b0a0..0d1789cd7 100644 --- a/Ix/Tc/Verify/Monad.lean +++ b/Ix/Kernel/Verify/Monad.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Knot +import Ix.Kernel.Knot /-! # EStateM Hoare kernel for `TcM` @@ -6,7 +6,7 @@ import Ix.Tc.Knot The verification's Hoare kernel, core form. `TcM m = EStateM (TcError m) (TcState m)` is **non-backtracking**: state written before a throw survives `tryCatch` (cache inserts, consumed fuel — load-bearing Rust parity, see -Ix/Tc/Monad.lean's module doc). So unlike lean4lean's `M.WF` (StateT over +Ix/Kernel/Monad.lean's module doc). So unlike the named specification's `M.WF` (StateT over `Except`, where errors discard state and `throw := nofun`), the triple here constrains **both outcomes**: an invariant `I` holds on the post-state of success *and* error, `Q` on success, `E` on error (default trivial — most @@ -18,7 +18,7 @@ immutable catalog and a growing trusted semantic environment; the combinator lemmas below carry over verbatim. -/ -namespace Ix.Tc +namespace Ix.Kernel variable {m : Mode} @@ -246,4 +246,4 @@ theorem TcM.tick.wf {I : TcState m → Prop} · exact TcM.WF.throw fun _ => ⟨rfl, rfl⟩ · exact TcM.WF.set (fun hI => hfuel _ hI) (fun _ => rfl) -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/NatFixture.lean b/Ix/Kernel/Verify/NatFixture.lean similarity index 98% rename from Ix/Tc/Verify/NatFixture.lean rename to Ix/Kernel/Verify/NatFixture.lean index 70c7df3b5..ce4d55760 100644 --- a/Ix/Tc/Verify/NatFixture.lean +++ b/Ix/Kernel/Verify/NatFixture.lean @@ -1,6 +1,8 @@ -import Ix.Tc.Verify.Run -import Ix.Tc.Verify.Whnf -import Ix.Tc.Verify.Whnf.Structural.BetaBoundary +import Ix.Kernel.Verify.Run +import Ix.Kernel.Verify.Whnf +import Ix.Kernel.Verify.Whnf.Structural.BetaBoundary + +open Ix.Theory (VLevel) /-! # G2a ambient-Nat fixture @@ -11,7 +13,7 @@ inductive/constructor kinds, while the Theory model contains the semantic constants those entries denote. The constants are installed through object-language `VDecl.axiom` steps; those are ordinary constructors of the Theory judgment, not new Lean axioms. This is deliberately an ambient model: -it does not claim an eliminator or pretend that Lean4Lean's still-opaque +it does not claim an eliminator or pretend that Ix.Theory.Named's still-opaque `VEnv.addInduct` has been verified. The fixture then promotes one ordinary axiom whose type is `Nat` and leaves a @@ -27,9 +29,9 @@ walker family, and a non-empty `ExecutionRequests` certificate for the exact same request list. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VConstant VConstVal VDecl VEnv VExpr VLevel) +open Ix.Theory.Named (VConstant VConstVal VDecl VEnv VExpr) namespace AmbientNat @@ -51,7 +53,7 @@ def iotaId : KId .anon := ⟨iotaAddress, ()⟩ def natName : Lean.Name := `Nat def zeroName : Lean.Name := `Nat.zero def succName : Lean.Name := `Nat.succ -def goodName : Lean.Name := `Ix.Tc.Verify.ambientNatWitness +def goodName : Lean.Name := `Ix.Kernel.Verify.ambientNatWitness def info (addr : Address) : ExprInfo .anon where addr := addr @@ -678,7 +680,7 @@ theorem bad_not_resolved : rintro ⟨_, _, hresolved⟩ exact bad_not_trusted hresolved.trusted -/-- The existential-world form used by C1--C3 consumers is non-vacuous on the +/-- The existential-world form used by lookup and admission is non-vacuous on the same ambient Nat state. -/ theorem natResolvedInv (prims : Primitives .anon) : ∃ world, worldGood ≤ world ∧ @@ -1189,7 +1191,7 @@ theorem warmCache_cannotResolvePending (prims : Primitives .anon) : /-- G4's formal acceptance witness contains both the fresh and nonempty warm states, transported provenance, and pending-declaration isolation. The -executable failed-then-valid regression lives in `Tests.Ix.Tc.CheckTests`. -/ +executable failed-then-valid regression lives in `Tests.Ix.Kernel.CheckTests`. -/ theorem cacheAcceptance (prims : Primitives .anon) : KernelStateWF whnfSemantics RawProjRel.none worldGood support (state prims) ∧ @@ -1242,7 +1244,7 @@ theorem betaArg_tr : theorem betaA_type : worldGood.venv.HasType 0 [] (.const natName []) (.sort (.succ .zero)) := by - exact Lean4Lean.VEnv.HasType.const (env := worldGood.venv) + exact Ix.Theory.Named.VEnv.HasType.const (env := worldGood.venv) (U := 0) (Γ := []) (ci := natConstant) (ls := []) (by simpa [worldGood, goodEnv, goodName, natName] using natEnv_nat) (by intro l hl; simp at hl) rfl @@ -1250,12 +1252,12 @@ theorem betaA_type : theorem betaBody_type : worldGood.venv.HasType 0 [(.const natName [])] (.bvar 0) (.const natName []) := by - exact Lean4Lean.VEnv.HasType.bvar .zero + exact Ix.Theory.Named.VEnv.HasType.bvar .zero theorem betaArg_type : worldGood.venv.HasType 0 [] (.const zeroName []) (.const natName []) := by - exact Lean4Lean.VEnv.HasType.const (env := worldGood.venv) + exact Ix.Theory.Named.VEnv.HasType.const (env := worldGood.venv) (U := 0) (Γ := []) (ci := zeroConstant) (ls := []) (by simpa [worldGood, goodEnv, goodName, zeroName] using natEnv_zero) (by intro l hl; simp at hl) rfl @@ -3316,12 +3318,12 @@ theorem structuralNatLit_type (n : Nat) : | succ n ih => have hsucc : worldGood.venv.HasType 0 [] (.const succName []) (.forallE (.const natName []) (.const natName [])) := by - exact Lean4Lean.VEnv.HasType.const (env := worldGood.venv) + exact Ix.Theory.Named.VEnv.HasType.const (env := worldGood.venv) (U := 0) (Γ := []) (ci := succConstant) (ls := []) (by simpa [worldGood, goodEnv, goodName, succName] using natEnv_succ) (by intro l hl; simp at hl) rfl - simpa [Lean4Lean.VExpr.inst, VExpr.natLit, VExpr.natSucc, succName] using - Lean4Lean.VEnv.HasType.app hsucc ih + simpa [Ix.Theory.Named.VExpr.inst, VExpr.natLit, VExpr.natSucc, succName] using + Ix.Theory.Named.VEnv.HasType.app hsucc ih /-- The finite Nat world supplies the uniform literal/projection facts needed to compose arbitrary structural trace meanings. -/ @@ -3331,7 +3333,7 @@ def structuralWhnfTheory : WhnfTheory RawProjRel.none worldGood 0 where cases literal with | natVal n => exact ⟨_, structuralNatLit_type n⟩ | strVal value => - simp [Lean4Lean.VEnv.ContainsLits, Lean4Lean.VEnv.contains, + simp [Ix.Theory.Named.VEnv.ContainsLits, Ix.Theory.Named.VEnv.contains, worldGood, goodEnv, natEnv, natEnv₂, natEnv₁, goodName, natName, zeroName, succName] at hliteral projections := RawProjRel.none_ok worldGood.venv 0 @@ -3343,15 +3345,15 @@ theorem structuralBetaSource_tr : betaSource (.app (.lam (.const natName []) (.bvar 0)) (.const zeroName [])) := by rw [betaSource, betaLam, KExpr.mkApp_shape, KExpr.mkLam_shape] - exact .app (Lean4Lean.VEnv.HasType.lam betaA_type betaBody_type) + exact .app (Ix.Theory.Named.VEnv.HasType.lam betaA_type betaBody_type) betaArg_type (.lam ⟨_, betaA_type⟩ betaTy_tr betaBody_tr) betaArg_tr theorem structuralBetaSource_type : worldGood.venv.HasType 0 [] (.app (.lam (.const natName []) (.bvar 0)) (.const zeroName [])) (.const natName []) := by - simpa [Lean4Lean.VExpr.inst] using Lean4Lean.VEnv.HasType.app - (Lean4Lean.VEnv.HasType.lam betaA_type betaBody_type) betaArg_type + simpa [Ix.Theory.Named.VExpr.inst] using Ix.Theory.Named.VEnv.HasType.app + (Ix.Theory.Named.VEnv.HasType.lam betaA_type betaBody_type) betaArg_type theorem structuralBetaSource_constructed : KExpr.Constructed betaSource := by unfold betaSource betaLam betaBody @@ -5053,7 +5055,7 @@ theorem appStuckHead_tr : theorem appStuckHead_type : worldGood.venv.HasType 0 [] (.const succName []) (.forallE (.const natName []) (.const natName [])) := by - exact Lean4Lean.VEnv.HasType.const (env := worldGood.venv) + exact Ix.Theory.Named.VEnv.HasType.const (env := worldGood.venv) (U := 0) (Γ := []) (ci := succConstant) (ls := []) (by simpa [worldGood, goodEnv, goodName, succName] using natEnv_succ) (by intro l hl; simp at hl) rfl @@ -5141,13 +5143,13 @@ its source-translation premise impossible. This identity interpretation is nonempty and closed under every structural translation operation. -/ namespace ProjectionFallback -def projectionName : Lean.Name := `Ix.Tc.Verify.projectionFallback +def projectionName : Lean.Name := `Ix.Kernel.Verify.projectionFallback def projectionRel : RawProjRel := fun _ _ _ _ value result => result = value theorem projectionRel_ok : - TrProjOK Lean4Lean.VEnv.empty 0 projectionRel := by + TrProjOK Ix.Theory.Named.VEnv.empty 0 projectionRel := by refine { weakN := ?_ instN := ?_ @@ -5223,8 +5225,8 @@ def theory : WhnfTheory projectionRel world 0 where literalWF := by intro literal hliteral cases literal <;> - simp [Lean4Lean.VEnv.ContainsLits, Lean4Lean.VEnv.contains, - Lean4Lean.VEnv.empty, world] + simp [Ix.Theory.Named.VEnv.ContainsLits, Ix.Theory.Named.VEnv.contains, + Ix.Theory.Named.VEnv.empty, world] at hliteral projections := projectionRel_ok @@ -5355,10 +5357,10 @@ theorem multiNatTr (Δ : KVLCtx) : (by simpa [worldGood, goodEnv, goodName, natName] using natEnv_nat) (by intro l hl; simp at hl) rfl -theorem multiNatType (Γ : List Lean4Lean.VExpr) : +theorem multiNatType (Γ : List Ix.Theory.Named.VExpr) : worldGood.venv.HasType 0 Γ (.const natName []) (.sort (.succ .zero)) := by - exact Lean4Lean.VEnv.HasType.const (env := worldGood.venv) + exact Ix.Theory.Named.VEnv.HasType.const (env := worldGood.venv) (U := 0) (Γ := Γ) (ci := natConstant) (ls := []) (by simpa [worldGood, goodEnv, goodName, natName] using natEnv_nat) (by intro l hl; simp at hl) rfl @@ -5374,12 +5376,12 @@ theorem multiBetaFunTyTr : (multiNatTr []) (multiNatTr [(none, .vlam (.const natName []))]) -theorem multiBetaFunType (Γ : List Lean4Lean.VExpr) : +theorem multiBetaFunType (Γ : List Ix.Theory.Named.VExpr) : worldGood.venv.HasType 0 Γ (.forallE (.const natName []) (.const natName [])) - (.sort ((Lean4Lean.VLevel.succ .zero).imax - (Lean4Lean.VLevel.succ .zero))) := - Lean4Lean.VEnv.HasType.forallE (multiNatType Γ) + (.sort ((Ix.Theory.VLevel.succ .zero).imax + (Ix.Theory.VLevel.succ .zero))) := + Ix.Theory.Named.VEnv.HasType.forallE (multiNatType Γ) (multiNatType ((.const natName []) :: Γ)) theorem multiBetaBodyTr : @@ -5395,8 +5397,8 @@ theorem multiBetaBodyType : [(.const natName []), (.forallE (.const natName []) (.const natName []))] (.bvar 1) (.forallE (.const natName []) (.const natName [])) := by - exact Lean4Lean.VEnv.HasType.bvar - (Lean4Lean.Lookup.succ (Lean4Lean.Lookup.zero)) + exact Ix.Theory.Named.VEnv.HasType.bvar + (Ix.Theory.Named.Lookup.succ (Ix.Theory.Named.Lookup.zero)) theorem multiBetaInnerTr : TrKExprS worldGood.venv 0 worldGood.nameOf RawProjRel.none @@ -5417,7 +5419,7 @@ theorem multiBetaInnerType : (.lam (.const natName []) (.bvar 1)) (.forallE (.const natName []) (.forallE (.const natName []) (.const natName []))) := by - exact Lean4Lean.VEnv.HasType.lam + exact Ix.Theory.Named.VEnv.HasType.lam (multiNatType [(.forallE (.const natName []) (.const natName []))]) multiBetaBodyType @@ -5438,18 +5440,18 @@ theorem multiBetaLamType : (.forallE (.forallE (.const natName []) (.const natName [])) (.forallE (.const natName []) (.forallE (.const natName []) (.const natName [])))) := by - exact Lean4Lean.VEnv.HasType.lam (multiBetaFunType []) multiBetaInnerType + exact Ix.Theory.Named.VEnv.HasType.lam (multiBetaFunType []) multiBetaInnerType -def multiBetaApp1V : Lean4Lean.VExpr := +def multiBetaApp1V : Ix.Theory.Named.VExpr := .app (.lam (.forallE (.const natName []) (.const natName [])) (.lam (.const natName []) (.bvar 1))) (.const succName []) -def multiBetaApp2V : Lean4Lean.VExpr := +def multiBetaApp2V : Ix.Theory.Named.VExpr := .app multiBetaApp1V (.const zeroName []) -def multiBetaSourceV : Lean4Lean.VExpr := +def multiBetaSourceV : Ix.Theory.Named.VExpr := .app multiBetaApp2V (.const zeroName []) theorem multiBetaApp1Type : @@ -5457,13 +5459,13 @@ theorem multiBetaApp1Type : (.forallE (.const natName []) (.forallE (.const natName []) (.const natName []))) := by unfold multiBetaApp1V - simpa [Lean4Lean.VExpr.inst] using Lean4Lean.VEnv.HasType.app multiBetaLamType appStuckHead_type + simpa [Ix.Theory.Named.VExpr.inst] using Ix.Theory.Named.VEnv.HasType.app multiBetaLamType appStuckHead_type theorem multiBetaApp2Type : worldGood.venv.HasType 0 [] multiBetaApp2V (.forallE (.const natName []) (.const natName [])) := by unfold multiBetaApp2V - simpa [Lean4Lean.VExpr.inst] using Lean4Lean.VEnv.HasType.app multiBetaApp1Type betaArg_type + simpa [Ix.Theory.Named.VExpr.inst] using Ix.Theory.Named.VEnv.HasType.app multiBetaApp1Type betaArg_type theorem multiBetaSourceTr : TrKExprS worldGood.venv 0 worldGood.nameOf RawProjRel.none [] @@ -5482,7 +5484,7 @@ theorem multiBetaSourceType : worldGood.venv.HasType 0 [] multiBetaSourceV (.const natName []) := by unfold multiBetaSourceV - simpa [Lean4Lean.VExpr.inst] using Lean4Lean.VEnv.HasType.app multiBetaApp2Type betaArg_type + simpa [Ix.Theory.Named.VExpr.inst] using Ix.Theory.Named.VEnv.HasType.app multiBetaApp2Type betaArg_type /-- The one dynamically generated trailing application is a real execution request, not an unindexed support assumption. -/ @@ -5715,10 +5717,10 @@ theorem appStuckHead_tr_ctx (Delta : KVLCtx) : (by simpa [worldGood, goodEnv, goodName, succName] using natEnv_succ) (by intro l hl; simp at hl) rfl -theorem appStuckHead_type_ctx (Gamma : List Lean4Lean.VExpr) : +theorem appStuckHead_type_ctx (Gamma : List Ix.Theory.Named.VExpr) : worldGood.venv.HasType 0 Gamma (.const succName []) (.forallE (.const natName []) (.const natName [])) := by - exact Lean4Lean.VEnv.HasType.const (env := worldGood.venv) + exact Ix.Theory.Named.VEnv.HasType.const (env := worldGood.venv) (U := 0) (Γ := Gamma) (ci := succConstant) (ls := []) (by simpa [worldGood, goodEnv, goodName, succName] using natEnv_succ) (by intro l hl; simp at hl) rfl @@ -6117,8 +6119,8 @@ theorem changedHeadMeaning : WhnfMeaning RawProjRel.none worldGood 0 [] changedHeadSource changedHeadRebuilt := by exact ⟨_, _, changedHeadSourceTr, changedHeadRebuiltTr, - Lean4Lean.VEnv.IsDefEqU.refl - ⟨_, Lean4Lean.VEnv.HasType.app appStuckHead_type betaArg_type⟩⟩ + Ix.Theory.Named.VEnv.IsDefEqU.refl + ⟨_, Ix.Theory.Named.VEnv.HasType.app appStuckHead_type betaArg_type⟩⟩ def changedHeadSupport : RunSupport := RunSupport.singleton changedHeadRebuilt @@ -6333,7 +6335,7 @@ theorem linearRecPartsTrace (prims : Primitives .anon) : /-- A concrete two-argument recursor prefix mirroring the descriptor fixture's major position. The argument values are immaterial to pattern matching; the count and constant head are not. -/ -def linearRecTheoryPrefix : Lean4Lean.VExpr := +def linearRecTheoryPrefix : Ix.Theory.Named.VExpr := .app (.app (.const ``Nat.rec []) (.const ``Nat [])) (.const ``Nat []) @@ -6344,26 +6346,26 @@ theorem linearRecTheoryPrefix_shape : simpa using HeadConstN.app (HeadConstN.app (HeadConstN.const (name := ``Nat.rec) [])) -/-- The zero branch constructs Lean4Lean's real dependent capture map. -/ +/-- The zero branch constructs Ix.Theory.Named's real dependent capture map. -/ theorem linearRecZeroPatternMatch : - ∃ (levels : List Lean4Lean.VLevel) + ∃ (levels : List Ix.Theory.VLevel) (captures : (RecursorIotaPattern ``Nat.rec 2 ``Nat.zero 0).Path → - Lean4Lean.VExpr), - Lean4Lean.Pattern.Matches + Ix.Theory.Named.VExpr), + Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern ``Nat.rec 2 ``Nat.zero 0) - (.app linearRecTheoryPrefix (Lean4Lean.VExpr.natLit 0)) + (.app linearRecTheoryPrefix (Ix.Theory.Named.VExpr.natLit 0)) levels captures := RecursorIotaPattern.matches_natZero linearRecTheoryPrefix_shape /-- The successor branch constructs a capture map whose constructor argument is the canonical predecessor numeral. -/ theorem linearRecSuccPatternMatch : - ∃ (levels : List Lean4Lean.VLevel) + ∃ (levels : List Ix.Theory.VLevel) (captures : (RecursorIotaPattern ``Nat.rec 2 ``Nat.succ 1).Path → - Lean4Lean.VExpr), - Lean4Lean.Pattern.Matches + Ix.Theory.Named.VExpr), + Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern ``Nat.rec 2 ``Nat.succ 1) - (.app linearRecTheoryPrefix (Lean4Lean.VExpr.natLit 3)) + (.app linearRecTheoryPrefix (Ix.Theory.Named.VExpr.natLit 3)) levels captures := by simpa using (RecursorIotaPattern.matches_natSucc (predecessor := 2) linearRecTheoryPrefix_shape) @@ -6395,7 +6397,7 @@ theorem missingRuleDescriptor_noZeroRule : argument retains the final argument in a nonempty typed suffix. -/ theorem multiBetaMiddleSplit : ∃ (priorArgs laterArgs : List (KExpr .anon)) - (priorV majorV : Lean4Lean.VExpr), + (priorV majorV : Ix.Theory.Named.VExpr), [appStuckHead, betaArg, betaArg] = priorArgs ++ betaArg :: laterArgs ∧ 1 = priorArgs.length ∧ @@ -6432,7 +6434,7 @@ Replacing the through-middle prefix by its own translation reconstructs the final application rather than silently returning the prefix. -/ theorem multiBetaMiddleRebase : ∃ (priorArgs laterArgs : List (KExpr .anon)) - (resultV : Lean4Lean.VExpr), + (resultV : Ix.Theory.Named.VExpr), laterArgs ≠ [] ∧ TrKExprS worldGood.venv 0 worldGood.nameOf RawProjRel.none [] (laterArgs.foldl KExpr.mkApp @@ -6468,4 +6470,4 @@ theorem acceptance (prims : Primitives .anon) : end AmbientNat -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Projection/Concrete.lean b/Ix/Kernel/Verify/Projection/Concrete.lean similarity index 75% rename from Ix/Tc/Verify/Projection/Concrete.lean rename to Ix/Kernel/Verify/Projection/Concrete.lean index 1d6279946..0f7d2eaaf 100644 --- a/Ix/Tc/Verify/Projection/Concrete.lean +++ b/Ix/Kernel/Verify/Projection/Concrete.lean @@ -1,17 +1,19 @@ -import Ix.Tc.Verify.Decl -import Ix.Tc.Verify.Trans -import Lean4Lean.Verify.Typing.Lemmas +import Ix.Kernel.Verify.Decl +import Ix.Kernel.Verify.Trans +import Ix.Theory.Named.Verify.Typing.Lemmas + +open Ix.Theory (VLevel) /-! -# Concrete Lean4Lean projection adapter +# Concrete Ix.Theory.Named projection adapter Ix keeps projection translation abstract so the checker proofs do not depend on one implementation of structure projections. This module closes that -boundary with Lean4Lean's registered, recursor-encoded `TrProj` relation. +boundary with Ix.Theory.Named's registered, recursor-encoded `TrProj` relation. The field mapping is direct: -* `weakN` uses Lean4Lean's general-depth weakening theorem; +* `weakN` uses Ix.Theory.Named's general-depth weakening theorem; * `instN`, `wf`, `uniq`, and `defeqDFC` consume the corresponding fields of `TrProj.structuralLaws`; * `instL` uses the named universe-instantiation theorem, whose statement is @@ -19,24 +21,24 @@ The field mapping is direct: * `monoU` instantiates with the identity parameter spine and discharges the resulting identities from the source context and projection typing data. -Consequently the adapter inherits Lean4Lean's named +Consequently the adapter inherits Ix.Theory.Named's named `VEnv.WF.registeredStructureHeadInversion` debt through projection uniqueness and the existing `VEnv.IsDefEqU.forallE_inv_stratified` / `VEnv.IsDefEqU.sort_inv` debts through context-defeq/unique typing; it introduces no Ix axiom or pending assumption. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (OnCtx VEnv VExpr VLevel) +open Ix.Theory.Named (OnCtx VEnv VExpr) namespace RawProjRel -/-- Lean4Lean's concrete, environment-indexed projection semantics in Ix's +/-- Ix.Theory.Named's concrete, environment-indexed projection semantics in Ix's universe-indexed projection slot. -/ -abbrev lean4Lean (env : VEnv) := +abbrev named (env : VEnv) := fun uvars ctx structName field major result => - Lean4Lean.TrProj env uvars ctx structName field major result + Ix.Theory.Named.TrProj env uvars ctx structName field major result private theorem vlevelWF_mono {before after : Nat} (hle : before ≤ after) : @@ -69,15 +71,15 @@ private theorem context_instParams_eq {uvars : Nat} : simp only [List.map_cons, context_instParams_eq hctx, htype.instL_id] /-- A concrete projection remains the same projection when only the available -universe-parameter budget grows. Lean4Lean supplies universe instantiation; +universe-parameter budget grows. Ix.Theory.Named supplies universe instantiation; its typing data proves that instantiating with the original parameter spine is the identity on the context, major, and computed result. -/ -private theorem lean4Lean_monoU +private theorem named_monoU {env : VEnv} {before after : Nat} {ctx : List VExpr} {structName : Lean.Name} {field : Nat} {major result : VExpr} (hle : before ≤ after) (hctx : OnCtx ctx (env.IsType before)) - (hproj : Lean4Lean.TrProj env before ctx structName field major result) : - Lean4Lean.TrProj env after ctx structName field major result := by + (hproj : Ix.Theory.Named.TrProj env before ctx structName field major result) : + Ix.Theory.Named.TrProj env after ctx structName field major result := by have hctxLevels := context_levelWF hctx have hlevels : ∀ level ∈ VLevel.params before, level.WF after := by intro level hlevel @@ -86,7 +88,7 @@ private theorem lean4Lean_monoU have hmajorWF : VExpr.WF env before ctx major := ⟨_, htheory.majorType⟩ have hresultWF : VExpr.WF env before ctx result := - (show Lean4Lean.TrProj env before ctx structName field major result from + (show Ix.Theory.Named.TrProj env before ctx structName field major result from ⟨view, levels, params, hname, htheory⟩).wf hmajorWF have hmajorLevels : major.LevelWF before := (htheory.majorType.levelWF hctxLevels).1 @@ -94,19 +96,19 @@ private theorem lean4Lean_monoU have hresultLevels : result.LevelWF before := (hresultType.levelWF hctxLevels).1 have hinst := - (show Lean4Lean.TrProj env before ctx structName field major result from + (show Ix.Theory.Named.TrProj env before ctx structName field major result from ⟨view, levels, params, hname, htheory⟩).instL hlevels rw [context_instParams_eq hctxLevels, hmajorLevels.instL_id, hresultLevels.instL_id] at hinst exact hinst -/-- Lean4Lean's concrete projection relation satisfies every Ix projection +/-- Ix.Theory.Named's concrete projection relation satisfies every Ix projection capability. The `uvars` index selects the laws used by ordinary checker proofs; universe instantiation and monotonicity remain polymorphic because declaration translation crosses universe counts. -/ -theorem lean4Lean_ok (henv : VEnv.WF env) (uvars : Nat) : - TrProjOK env uvars (lean4Lean env) := by - let laws := Lean4Lean.TrProj.structuralLaws henv +theorem named_ok (henv : VEnv.WF env) (uvars : Nat) : + TrProjOK env uvars (named env) := by + let laws := Ix.Theory.Named.TrProj.structuralLaws henv refine { weakN := ?_ instN := ?_ @@ -116,7 +118,7 @@ theorem lean4Lean_ok (henv : VEnv.WF env) (uvars : Nat) : instL := ?_ monoU := ?_ } · intro Γ Γ' n k s i e e' hlift hproj - exact Lean4Lean.TrProj.weakN henv.ordered hlift hproj + exact Ix.Theory.Named.TrProj.weakN henv.ordered hlift hproj · intro Γ₀ e₀ A₀ k Γ₁ Γ s i e e' htype hinst hproj exact laws.termSubstitution htype hinst hproj · intro Γ s i e e' hproj hwf @@ -128,7 +130,7 @@ theorem lean4Lean_ok (henv : VEnv.WF env) (uvars : Nat) : · intro U U' levels Γ s i e e' hlevels hproj exact hproj.instL hlevels · intro U U' Γ s i e e' hle hctx hproj - exact lean4Lean_monoU hle hctx hproj + exact named_monoU hle hctx hproj end RawProjRel -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Projection/ConcreteFixture.lean b/Ix/Kernel/Verify/Projection/ConcreteFixture.lean similarity index 80% rename from Ix/Tc/Verify/Projection/ConcreteFixture.lean rename to Ix/Kernel/Verify/Projection/ConcreteFixture.lean index 1772b64cf..1ee609625 100644 --- a/Ix/Tc/Verify/Projection/ConcreteFixture.lean +++ b/Ix/Kernel/Verify/Projection/ConcreteFixture.lean @@ -1,10 +1,10 @@ -import Ix.Tc.Verify.Projection.Concrete -import Lean4Lean.Tests.ProjectionExpressibility +import Ix.Kernel.Verify.Projection.Concrete +import Ix.Theory.Named.Fixtures.ProjectionExpressibility /-! # Production-syntax fixture for the concrete projection relation -This fixture connects an actual Ix `KExpr.mkPrj` node to Lean4Lean's +This fixture connects an actual Ix `KExpr.mkPrj` node to Ix.Theory.Named's universe-polymorphic, dependent `DependentRecord` projection fixture. The major is the leading local variable in the exact Theory context retained by the registered structure view; the result is the recursor-encoded key @@ -16,10 +16,10 @@ environment-indexed `TrProj` witness, and the acceptance theorem packages the real adapter laws with that production syntax node. -/ -namespace Ix.Tc.ConcreteProjectionFixture +namespace Ix.Kernel.ConcreteProjectionFixture -open Lean4Lean (VExpr VLocalDecl) -open Lean4Lean.Tests.ProjectionExpressibility +open Ix.Theory.Named (VExpr VLocalDecl) +open Ix.Theory.Named.Tests.ProjectionExpressibility def structureAddress : Address := ⟨⟨Array.replicate 32 64⟩⟩ @@ -34,11 +34,11 @@ theorem nameOf_structure : simp [nameOf] abbrev projectionRel := - Ix.Tc.RawProjRel.lean4Lean dependentRecordEnv + Ix.Kernel.RawProjRel.named dependentRecordEnv theorem projectionLaws : TrProjOK dependentRecordEnv 2 projectionRel := - Ix.Tc.RawProjRel.lean4Lean_ok dependentRecordEnv_wf 2 + Ix.Kernel.RawProjRel.named_ok dependentRecordEnv_wf 2 /-- The Verify compatibility witness exposing the registered key projection through the concrete relation used by Ix. -/ @@ -48,7 +48,7 @@ theorem keyProjection : exact ⟨dependentRecordView, symbolicLevels, symbolicMajorParams, rfl, key_representable⟩ -/-- The mixed Ix context corresponding definitionally to Lean4Lean's +/-- The mixed Ix context corresponding definitionally to Ix.Theory.Named's `[major, family, α]` Theory context. -/ def context : KVLCtx := [(none, .vlam symbolicMajorBinderType), @@ -72,14 +72,14 @@ theorem sourceConstructed : source.Constructed := by exact .prj (.var (by decide)) theorem majorRaw : - _root_.Ix.Tc.RawExprRel (uvars := 2) dependentRecordEnv nameOf projectionRel + _root_.Ix.Kernel.RawExprRel (uvars := 2) dependentRecordEnv nameOf projectionRel symbolicContext major symbolicMajor := by unfold major rw [KExpr.mkVar_shape] exact .var theorem sourceRaw : - _root_.Ix.Tc.RawExprRel (uvars := 2) dependentRecordEnv nameOf projectionRel + _root_.Ix.Kernel.RawExprRel (uvars := 2) dependentRecordEnv nameOf projectionRel symbolicContext source symbolicKeyResult := by unfold source rw [KExpr.mkPrj_shape] @@ -101,11 +101,11 @@ theorem sourceStructural : exact .prj nameOf_structure majorStructural keyProjection /-- The same concrete projection survives an increased universe budget via -the Ix capability derived from Lean4Lean universe instantiation. -/ +the Ix capability derived from Ix.Theory.Named universe instantiation. -/ theorem keyProjectionAtThree : projectionRel 3 symbolicContext ``DependentRecord 0 symbolicMajor symbolicKeyResult := - (Ix.Tc.RawProjRel.lean4Lean_ok dependentRecordEnv_wf 3).monoU + (Ix.Kernel.RawProjRel.named_ok dependentRecordEnv_wf 3).monoU (by omega) contextWF.toCtx keyProjection /-- P0's concrete vertical slice: a smart-constructor-built Ix projection is @@ -114,7 +114,7 @@ boundary, using the real registered projection relation and its complete Ix law bundle. -/ theorem acceptance : source.Constructed ∧ - _root_.Ix.Tc.RawExprRel (uvars := 2) dependentRecordEnv nameOf projectionRel + _root_.Ix.Kernel.RawExprRel (uvars := 2) dependentRecordEnv nameOf projectionRel symbolicContext source symbolicKeyResult ∧ TrKExprS dependentRecordEnv 2 nameOf projectionRel context source symbolicKeyResult ∧ @@ -124,4 +124,4 @@ theorem acceptance : ⟨sourceConstructed, sourceRaw, sourceStructural, projectionLaws, keyProjectionAtThree⟩ -end Ix.Tc.ConcreteProjectionFixture +end Ix.Kernel.ConcreteProjectionFixture diff --git a/Ix/Tc/Verify/RecursiveMethods/CallDomains.lean b/Ix/Kernel/Verify/RecursiveMethods/CallDomains.lean similarity index 98% rename from Ix/Tc/Verify/RecursiveMethods/CallDomains.lean rename to Ix/Kernel/Verify/RecursiveMethods/CallDomains.lean index d9bd2afa1..82fb4b1b9 100644 --- a/Ix/Tc/Verify/RecursiveMethods/CallDomains.lean +++ b/Ix/Kernel/Verify/RecursiveMethods/CallDomains.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Knot +import Ix.Kernel.Verify.Knot /-! # Fuel-indexed recursive-method call domains @@ -21,7 +21,7 @@ is the entire run support; `FiniteSupportBoundary` proves why that special case cannot be the final public interface for sort-producing runs. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace Methods @@ -255,14 +255,14 @@ theorem methodsN calls depth) : ∀ n, n ≤ depth → Methods.WFAtOn layer semantics trProj world scope uvars (calls n) - (Ix.Tc.methodsN (m := .anon) n) + (Ix.Kernel.methodsN (m := .anon) n) | 0, _ => methodsOut_wfAtOn layer semantics trProj world scope uvars (calls 0) (schedule.within 0 (Nat.zero_le depth)) | n + 1, hn => by rw [Methods.methodsN_succ] exact schedule.step n (Nat.lt_of_succ_le hn) - (Ix.Tc.methodsN (m := .anon) n) + (Ix.Kernel.methodsN (m := .anon) n) (schedule.methodsN n (Nat.le_trans (Nat.le_succ n) hn)) /-- The contract selected at the schedule's terminal depth. -/ @@ -273,7 +273,7 @@ theorem selected (schedule : CallScheduleAt layer semantics trProj world scope uvars calls depth) : Methods.WFAtOn layer semantics trProj world scope uvars (calls depth) - (Ix.Tc.methodsN (m := .anon) depth) := + (Ix.Kernel.methodsN (m := .anon) depth) := schedule.methodsN depth (Nat.le_refl depth) /-- The method body executed by `TcM.runRec` sits one layer above its @@ -288,9 +288,9 @@ theorem nextSelected calls (depth + 1)) : Methods.WFAtOn layer semantics trProj world scope uvars (calls (depth + 1)) - (Methods.next (Ix.Tc.methodsN (m := .anon) depth)) := + (Methods.next (Ix.Kernel.methodsN (m := .anon) depth)) := schedule.step depth (Nat.lt_succ_self depth) - (Ix.Tc.methodsN (m := .anon) depth) + (Ix.Kernel.methodsN (m := .anon) depth) (schedule.methodsN depth (Nat.le_succ depth)) end CallScheduleAt @@ -479,8 +479,8 @@ theorem runRec_wfAtOn TcM.WF (WhnfStateInv layer semantics trProj world scope uvars Delta) s (TcM.runRec action) Q E := by exact - haction (Ix.Tc.methodsN (m := .anon) s.recFuel.toNat) schedule.selected + haction (Ix.Kernel.methodsN (m := .anon) s.recFuel.toNat) schedule.selected end TcM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/RecursiveMethods/Closure.lean b/Ix/Kernel/Verify/RecursiveMethods/Closure.lean similarity index 96% rename from Ix/Tc/Verify/RecursiveMethods/Closure.lean rename to Ix/Kernel/Verify/RecursiveMethods/Closure.lean index 784cab9b3..495ea8a56 100644 --- a/Ix/Tc/Verify/RecursiveMethods/Closure.lean +++ b/Ix/Kernel/Verify/RecursiveMethods/Closure.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.InferDefEq.Closure -import Ix.Tc.Verify.Whnf.Closure +import Ix.Kernel.Verify.InferDefEq.Closure +import Ix.Kernel.Verify.Whnf.Closure /-! # Complete recursive method-table closure @@ -10,7 +10,7 @@ cache stack and one predecessor table before `methodsN` can be justified. This module performs that final fixed-universe assembly. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- The semantic cache layers beneath K1's outer WHNF and delta layers. -/ def kernelCacheFallback (keys : WhnfContextKeys) (trProj : RawProjRel) : @@ -75,4 +75,4 @@ theorem methodsN end RecursiveMethodClosureContext -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/RecursiveMethods/FiniteSupportBoundary.lean b/Ix/Kernel/Verify/RecursiveMethods/FiniteSupportBoundary.lean similarity index 98% rename from Ix/Tc/Verify/RecursiveMethods/FiniteSupportBoundary.lean rename to Ix/Kernel/Verify/RecursiveMethods/FiniteSupportBoundary.lean index 19bc4d6b9..3d090864c 100644 --- a/Ix/Tc/Verify/RecursiveMethods/FiniteSupportBoundary.lean +++ b/Ix/Kernel/Verify/RecursiveMethods/FiniteSupportBoundary.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Infer.Dispatcher +import Ix.Kernel.Verify.Infer.Dispatcher /-! # Finite-support boundary for recursive inference @@ -15,7 +15,7 @@ interface failure explicit so a recursive-method closure cannot accidentally hide it behind an uninhabitable premise. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace FiniteSupportBoundary @@ -95,4 +95,4 @@ theorem SyntaxInferenceResources.no_sort_source end FiniteSupportBoundary -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/RecursiveMethods/Inference.lean b/Ix/Kernel/Verify/RecursiveMethods/Inference.lean similarity index 96% rename from Ix/Tc/Verify/RecursiveMethods/Inference.lean rename to Ix/Kernel/Verify/RecursiveMethods/Inference.lean index accfac3e7..523189c36 100644 --- a/Ix/Tc/Verify/RecursiveMethods/Inference.lean +++ b/Ix/Kernel/Verify/RecursiveMethods/Inference.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Infer.CacheSoundness -import Ix.Tc.Verify.RecursiveMethods.CallDomains +import Ix.Kernel.Verify.Infer.CacheSoundness +import Ix.Kernel.Verify.RecursiveMethods.CallDomains /-! # Call-domain inference layer @@ -16,7 +16,7 @@ contains the all-support `SyntaxInferenceResources` field whose sort clause is provably uninhabitable for any finite support containing a sort. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Per-layer resources for production inference. `current` guards only the outer calls proved at this layer; `predecessor` governs recursive back-edges @@ -30,7 +30,7 @@ structure InferenceCallDomainContext theory : WhnfTheory trProj world model.keys.uvars references : RecM.TrustedReferences world scope uncached : ∀ {Delta : KVLCtx} {s : TcState .anon} {inferOnly : Bool} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr}, + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr}, current.infer source → TrKExprS world.venv model.keys.uvars world.nameOf trProj Delta source sourceV → @@ -98,7 +98,7 @@ private theorem missTail_wfOn {current predecessor : Methods.CallDomain} (context : InferenceCallDomainContext scope model current predecessor) {Delta : KVLCtx} {before s : TcState .anon} {inferOnly : Bool} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} {key : Address × Address} (hmatch : model.keys.Matches trProj world before Delta source key) (hcall : current.infer source) @@ -152,7 +152,7 @@ theorem inferWith_wfOn {current predecessor : Methods.CallDomain} (context : InferenceCallDomainContext scope model current predecessor) {Delta : KVLCtx} {s : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hcall : current.infer source) (hsource : TrKExprS world.venv model.keys.uvars world.nameOf trProj Delta source sourceV) : @@ -244,7 +244,7 @@ theorem nextInfer_wfAtOn (kernelCacheSemantics model.keys trProj) trProj world scope model.keys.uvars predecessor predecessorMethods) : ∀ {Delta : KVLCtx} {s : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr}, + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr}, current.infer source → TrKExprS world.venv model.keys.uvars world.nameOf trProj Delta source sourceV → @@ -260,4 +260,4 @@ theorem nextInfer_wfAtOn end InferenceCallDomainContext -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/RecursiveMethods/Public.lean b/Ix/Kernel/Verify/RecursiveMethods/Public.lean similarity index 94% rename from Ix/Tc/Verify/RecursiveMethods/Public.lean rename to Ix/Kernel/Verify/RecursiveMethods/Public.lean index bd927fd9c..40ca11952 100644 --- a/Ix/Tc/Verify/RecursiveMethods/Public.lean +++ b/Ix/Kernel/Verify/RecursiveMethods/Public.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.RecursiveMethods.CallDomains -import Ix.Tc.Verify.RecursiveMethods.Closure -import Ix.Tc.Verify.RecursiveMethods.ScopedCallDomains +import Ix.Kernel.Verify.RecursiveMethods.CallDomains +import Ix.Kernel.Verify.RecursiveMethods.Closure +import Ix.Kernel.Verify.RecursiveMethods.ScopedCallDomains /-! # Public recursive-method soundness over run-scoped bounded call domains @@ -18,7 +18,7 @@ finite run to infer a sort and return its successor sort without demanding an infinite successor-sort closure. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Legacy globally quantified evidence for one public recursive-method run. New public roots consume `ScopedRecursiveMethodRunContext` below. This type @@ -93,7 +93,7 @@ theorem wf_legacy {world : VerifyWorld} {support : RunSupport} (context : RecursiveMethodRunContext initial (TcM.whnf e) requests trProj world support) - {Delta : KVLCtx} {sourceV : Lean4Lean.VExpr} + {Delta : KVLCtx} {sourceV : Ix.Theory.Named.VExpr} (hcall : (context.calls (initial.recFuel.toNat + 1)).whnf e) (hsource : TrKExprS world.venv context.proposition.model.keys.uvars world.nameOf trProj Delta e @@ -112,7 +112,7 @@ theorem wf {world : VerifyWorld} {support : RunSupport} (context : ScopedRecursiveMethodRunContext initial (TcM.whnf e) requests trProj world support) - {Delta : KVLCtx} {sourceV : Lean4Lean.VExpr} + {Delta : KVLCtx} {sourceV : Ix.Theory.Named.VExpr} (hcall : (context.calls (initial.recFuel.toNat + 1)).whnf e) (hsource : TrKExprS world.venv context.model.keys.uvars world.nameOf trProj Delta e sourceV) : @@ -135,7 +135,7 @@ theorem wf_legacy {world : VerifyWorld} {support : RunSupport} (context : RecursiveMethodRunContext initial (TcM.infer e) requests trProj world support) - {Delta : KVLCtx} {sourceV : Lean4Lean.VExpr} + {Delta : KVLCtx} {sourceV : Ix.Theory.Named.VExpr} (hcall : (context.calls (initial.recFuel.toNat + 1)).infer e) (hsource : TrKExprS world.venv context.proposition.model.keys.uvars world.nameOf trProj Delta e @@ -154,7 +154,7 @@ theorem wf {world : VerifyWorld} {support : RunSupport} (context : ScopedRecursiveMethodRunContext initial (TcM.infer e) requests trProj world support) - {Delta : KVLCtx} {sourceV : Lean4Lean.VExpr} + {Delta : KVLCtx} {sourceV : Ix.Theory.Named.VExpr} (hcall : (context.calls (initial.recFuel.toNat + 1)).infer e) (hsource : TrKExprS world.venv context.model.keys.uvars world.nameOf trProj Delta e sourceV) : @@ -177,7 +177,7 @@ theorem wf_legacy {world : VerifyWorld} {support : RunSupport} (context : RecursiveMethodRunContext initial (TcM.isDefEq a b) requests trProj world support) - {Delta : KVLCtx} {va vb : Lean4Lean.VExpr} + {Delta : KVLCtx} {va vb : Ix.Theory.Named.VExpr} (hcall : (context.calls (initial.recFuel.toNat + 1)).isDefEq a b) (ha : TrKExprS world.venv context.proposition.model.keys.uvars world.nameOf trProj Delta a va) @@ -198,7 +198,7 @@ theorem wf {world : VerifyWorld} {support : RunSupport} (context : ScopedRecursiveMethodRunContext initial (TcM.isDefEq a b) requests trProj world support) - {Delta : KVLCtx} {va vb : Lean4Lean.VExpr} + {Delta : KVLCtx} {va vb : Ix.Theory.Named.VExpr} (hcall : (context.calls (initial.recFuel.toNat + 1)).isDefEq a b) (ha : TrKExprS world.venv context.model.keys.uvars world.nameOf trProj Delta a va) @@ -212,4 +212,4 @@ theorem wf end TcM.isDefEq -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/RecursiveMethods/ScopedCallDomains.lean b/Ix/Kernel/Verify/RecursiveMethods/ScopedCallDomains.lean similarity index 96% rename from Ix/Tc/Verify/RecursiveMethods/ScopedCallDomains.lean rename to Ix/Kernel/Verify/RecursiveMethods/ScopedCallDomains.lean index a10318c27..1de663397 100644 --- a/Ix/Tc/Verify/RecursiveMethods/ScopedCallDomains.lean +++ b/Ix/Kernel/Verify/RecursiveMethods/ScopedCallDomains.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.DefEq -import Ix.Tc.Verify.RecursiveMethods.CallDomains +import Ix.Kernel.Verify.DefEq +import Ix.Kernel.Verify.RecursiveMethods.CallDomains /-! # Run-scoped recursive-method call domains @@ -15,7 +15,7 @@ all-depth/global-model interfaces remain compatibility artifacts and are not used to justify the scoped schedule below. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace Methods @@ -129,14 +129,14 @@ theorem methodsN (schedule : ScopedCallScheduleAt model layer semantics scope calls depth) : ∀ n, n ≤ depth → ScopedWFAtOn model layer semantics scope (calls n) - (Ix.Tc.methodsN (m := .anon) n) + (Ix.Kernel.methodsN (m := .anon) n) | 0, _ => methodsOut_scopedWFAtOn model layer semantics scope (calls 0) (schedule.within 0 (Nat.zero_le depth)) | n + 1, hn => by rw [Methods.methodsN_succ] exact schedule.step n (Nat.lt_of_succ_le hn) - (Ix.Tc.methodsN (m := .anon) n) + (Ix.Kernel.methodsN (m := .anon) n) (schedule.methodsN n (Nat.le_trans (Nat.le_succ n) hn)) theorem selected @@ -146,7 +146,7 @@ theorem selected {scope : RunSupport} {calls : Nat → CallDomain} {depth : Nat} (schedule : ScopedCallScheduleAt model layer semantics scope calls depth) : ScopedWFAtOn model layer semantics scope (calls depth) - (Ix.Tc.methodsN (m := .anon) depth) := + (Ix.Kernel.methodsN (m := .anon) depth) := schedule.methodsN depth (Nat.le_refl depth) theorem nextSelected @@ -157,9 +157,9 @@ theorem nextSelected (schedule : ScopedCallScheduleAt model layer semantics scope calls (depth + 1)) : ScopedWFAtOn model layer semantics scope (calls (depth + 1)) - (Methods.next (Ix.Tc.methodsN (m := .anon) depth)) := + (Methods.next (Ix.Kernel.methodsN (m := .anon) depth)) := schedule.step depth (Nat.lt_succ_self depth) - (Ix.Tc.methodsN (m := .anon) depth) + (Ix.Kernel.methodsN (m := .anon) depth) (schedule.methodsN depth (Nat.le_succ depth)) end ScopedCallScheduleAt @@ -310,8 +310,8 @@ theorem runRec_scoped_wfAtOn TcM.WF (ScopedWhnfStateInv model layer semantics scope Delta) s (TcM.runRec action) Q E := by exact - haction (Ix.Tc.methodsN (m := .anon) s.recFuel.toNat) schedule.selected + haction (Ix.Kernel.methodsN (m := .anon) s.recFuel.toNat) schedule.selected end TcM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/RecursiveMethods/ScopedInference.lean b/Ix/Kernel/Verify/RecursiveMethods/ScopedInference.lean similarity index 98% rename from Ix/Tc/Verify/RecursiveMethods/ScopedInference.lean rename to Ix/Kernel/Verify/RecursiveMethods/ScopedInference.lean index 74e3a81c8..520ceb5e4 100644 --- a/Ix/Tc/Verify/RecursiveMethods/ScopedInference.lean +++ b/Ix/Kernel/Verify/RecursiveMethods/ScopedInference.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Infer.CacheSoundness -import Ix.Tc.Verify.RecursiveMethods.ScopedCallDomains +import Ix.Kernel.Verify.Infer.CacheSoundness +import Ix.Kernel.Verify.RecursiveMethods.ScopedCallDomains /-! # Run-scoped call-domain inference @@ -13,7 +13,7 @@ No theorem in this module converts a `ScopedKernelSuffixModel` to the legacy globally quantified `KernelSuffixModel`. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace InternTable @@ -310,7 +310,7 @@ structure ScopedInferenceCallDomainContext theory : WhnfTheory trProj world model.keys.uvars references : RecM.TrustedReferences world scope uncached : ∀ {Delta : KVLCtx} {s : TcState .anon} {inferOnly : Bool} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr}, + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr}, current.infer source → TrKExprS world.venv model.keys.uvars world.nameOf trProj Delta source sourceV → @@ -383,7 +383,7 @@ private theorem missTail_scopedWFOn (context : ScopedInferenceCallDomainContext scope model current predecessor) {Delta : KVLCtx} {before s : TcState .anon} {inferOnly : Bool} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} {key : Address × Address} (hmatch : model.keys.Matches trProj world before Delta source key) (hcall : current.infer source) @@ -438,7 +438,7 @@ theorem inferWith_scopedWFOn (context : ScopedInferenceCallDomainContext scope model current predecessor) {Delta : KVLCtx} {s : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hcall : current.infer source) (hsource : TrKExprS world.venv model.keys.uvars world.nameOf trProj Delta source sourceV) : @@ -532,7 +532,7 @@ theorem nextInfer_scopedWFAtOn (kernelCacheSemantics model.keys trProj) scope predecessor predecessorMethods) : ∀ {Delta : KVLCtx} {s : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr}, + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr}, current.infer source → TrKExprS world.venv model.keys.uvars world.nameOf trProj Delta source sourceV → @@ -549,4 +549,4 @@ theorem nextInfer_scopedWFAtOn end ScopedInferenceCallDomainContext -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/RecursiveMethods/ScopedSortInference.lean b/Ix/Kernel/Verify/RecursiveMethods/ScopedSortInference.lean similarity index 96% rename from Ix/Tc/Verify/RecursiveMethods/ScopedSortInference.lean rename to Ix/Kernel/Verify/RecursiveMethods/ScopedSortInference.lean index 3111a9e0e..f099c235f 100644 --- a/Ix/Tc/Verify/RecursiveMethods/ScopedSortInference.lean +++ b/Ix/Kernel/Verify/RecursiveMethods/ScopedSortInference.lean @@ -1,5 +1,7 @@ -import Ix.Tc.Verify.Infer.LeafCases -import Ix.Tc.Verify.RecursiveMethods.ScopedInference +import Ix.Kernel.Verify.Infer.LeafCases +import Ix.Kernel.Verify.RecursiveMethods.ScopedInference + +open Ix.Theory (VLevel) /-! # Positive-fuel inference under a run-scoped suffix model @@ -11,7 +13,7 @@ recursive callback. Key memoization, interning, and cache insertion all preserve `StateInScope` explicitly. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace ScopedInferenceCallDomainContext @@ -52,7 +54,7 @@ def sort world.venvWF.ordered theory.literalWF theory.projections.wf hI.1.2.1.wf · simpa only [KUniv.toVLevel_mkSucc] using - (Lean4Lean.VEnv.HasType.sort hu) + (Ix.Theory.Named.VEnv.HasType.sort hu) · intro _ _ _ trivial @@ -161,7 +163,7 @@ theorem sort_scoped_wf_bounded (hresultSupport : scope (KExpr.mkSort (KUniv.mkSucc u))) (theory : WhnfTheory trProj world model.keys.uvars) (references : RecM.TrustedReferences world scope) - {Delta : KVLCtx} {sourceV : Lean4Lean.VExpr} + {Delta : KVLCtx} {sourceV : Ix.Theory.Named.VExpr} (hsource : TrKExprS world.venv model.keys.uvars world.nameOf trProj Delta (.sort u info) sourceV) : TcM.WF @@ -188,7 +190,7 @@ theorem sort_scoped_wf_fuel_one (hresultSupport : scope (KExpr.mkSort (KUniv.mkSucc u))) (theory : WhnfTheory trProj world model.keys.uvars) (references : RecM.TrustedReferences world scope) - {Delta : KVLCtx} {sourceV : Lean4Lean.VExpr} + {Delta : KVLCtx} {sourceV : Ix.Theory.Named.VExpr} (hsource : TrKExprS world.venv model.keys.uvars world.nameOf trProj Delta (.sort u info) sourceV) : TcM.WF @@ -217,7 +219,7 @@ theorem sort_finiteOperational_wf_fuel_one (hresultSupport : scope (KExpr.mkSort (KUniv.mkSucc u))) (theory : WhnfTheory trProj world uvars) (references : RecM.TrustedReferences world scope) - {Delta : KVLCtx} {sourceV : Lean4Lean.VExpr} + {Delta : KVLCtx} {sourceV : Ix.Theory.Named.VExpr} (hsource : TrKExprS world.venv uvars world.nameOf trProj Delta (.sort u info) sourceV) : let model := ScopedKernelSuffixModel.finiteOperational spec digestScope @@ -234,4 +236,4 @@ theorem sort_finiteOperational_wf_fuel_one end TcM.infer -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/RecursiveMethods/SortInference.lean b/Ix/Kernel/Verify/RecursiveMethods/SortInference.lean similarity index 96% rename from Ix/Tc/Verify/RecursiveMethods/SortInference.lean rename to Ix/Kernel/Verify/RecursiveMethods/SortInference.lean index 0cac65417..64aa22580 100644 --- a/Ix/Tc/Verify/RecursiveMethods/SortInference.lean +++ b/Ix/Kernel/Verify/RecursiveMethods/SortInference.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Infer.LeafCases -import Ix.Tc.Verify.RecursiveMethods.Inference -import Ix.Tc.Verify.RecursiveMethods.Public +import Ix.Kernel.Verify.Infer.LeafCases +import Ix.Kernel.Verify.RecursiveMethods.Inference +import Ix.Kernel.Verify.RecursiveMethods.Public /-! # A positive-fuel inference schedule @@ -15,7 +15,7 @@ membership of the result does not make it another inference call, so this contract does not demand closure under an infinite tower of successor sorts. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace InferenceCallDomainContext @@ -120,7 +120,7 @@ theorem selected Methods.WFAtOn .noAccel (kernelCacheSemantics model.keys trProj) trProj world scope model.keys.uvars (.singletonInfer (.sort u info)) - (Ix.Tc.methodsN (m := .anon) 1) := by + (Ix.Kernel.methodsN (m := .anon) 1) := by simpa [calls] using (one context).selected /-- The same finite source/result footprint supports every finite table depth: @@ -184,7 +184,7 @@ theorem sort_wf_bounded (hresultSupport : scope (KExpr.mkSort (KUniv.mkSucc u))) (theory : WhnfTheory trProj world proposition.model.keys.uvars) (references : RecM.TrustedReferences world scope) - {Delta : KVLCtx} {sourceV : Lean4Lean.VExpr} + {Delta : KVLCtx} {sourceV : Ix.Theory.Named.VExpr} (hsource : TrKExprS world.venv proposition.model.keys.uvars world.nameOf trProj Delta (.sort u info) sourceV) : TcM.WF @@ -221,7 +221,7 @@ theorem sort_wf_fuel_one (hresultSupport : scope (KExpr.mkSort (KUniv.mkSucc u))) (theory : WhnfTheory trProj world proposition.model.keys.uvars) (references : RecM.TrustedReferences world scope) - {Delta : KVLCtx} {sourceV : Lean4Lean.VExpr} + {Delta : KVLCtx} {sourceV : Ix.Theory.Named.VExpr} (hsource : TrKExprS world.venv proposition.model.keys.uvars world.nameOf trProj Delta (.sort u info) sourceV) : TcM.WF @@ -247,4 +247,4 @@ theorem sort_wf_fuel_one end TcM.infer -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Run.lean b/Ix/Kernel/Verify/Run.lean similarity index 97% rename from Ix/Tc/Verify/Run.lean rename to Ix/Kernel/Verify/Run.lean index 08f5bc69c..0c53a2472 100644 --- a/Ix/Tc/Verify/Run.lean +++ b/Ix/Kernel/Verify/Run.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.State -import Ix.Tc.Verify.Execution -import Ix.Tc.Verify.Frame +import Ix.Kernel.Verify.State +import Ix.Kernel.Verify.Execution +import Ix.Kernel.Verify.Frame /-! # G3b: execution-indexed run assumptions @@ -19,7 +19,7 @@ translation relations. The adapter lemmas here project that bundle into the existing walker masters and retain both finite intern ranges in post-states. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- State invariant used by run-level adapters and top-level statements. G4 adds stable-world provenance for every warm cache entry. -/ @@ -122,7 +122,7 @@ theorem lift_spec {α : Type} {initial : TcState .anon} (lift e shift cutoff it).2.WF ∧ support.CoversIntern (lift e shift cutoff it).2 := by obtain ⟨hcon, hcut, _⟩ := h.requestBounds hmem - have post := Ix.Tc.lift_spec h.collisionFree.expr hcon hcut + have post := Ix.Kernel.lift_spec h.collisionFree.expr hcon hcut (h.coverage.lift hmem) hwf hsup.expr exact ⟨post.1, post.2.1, hsup.of_expr_univs post.2.2 (lift_preservesUnivs e shift cutoff it)⟩ @@ -139,7 +139,7 @@ theorem subst_spec {α : Type} {initial : TcState .anon} (subst body arg depth it).2.WF ∧ support.CoversIntern (subst body arg depth it).2 := by obtain ⟨hbody, harg, hcut, hargsz, _⟩ := h.requestBounds hmem - have post := Ix.Tc.subst_spec h.collisionFree.expr hbody harg hcut hargsz + have post := Ix.Kernel.subst_spec h.collisionFree.expr hbody harg hcut hargsz (h.coverage.subst hmem) hwf hsup.expr exact ⟨post.1, post.2.1, hsup.of_expr_univs post.2.2 (subst_preservesUnivs body arg depth it)⟩ @@ -157,7 +157,7 @@ theorem simulSubst_spec {α : Type} {initial : TcState .anon} (simulSubst body substs depth it).2.WF ∧ support.CoversIntern (simulSubst body substs depth it).2 := by obtain ⟨hbody, hsubsts, hsizes, hwalk, _⟩ := h.requestBounds hmem - have post := Ix.Tc.simulSubst_spec h.collisionFree.expr hbody hsubsts + have post := Ix.Kernel.simulSubst_spec h.collisionFree.expr hbody hsubsts hsizes hwalk (h.coverage.simulSubst hmem) hwf hsup.expr exact ⟨post.1, post.2.1, hsup.of_expr_univs post.2.2 @@ -176,7 +176,7 @@ theorem instRev_spec {α : Type} {initial : TcState .anon} (instantiateRev body fvars it).2.WF ∧ support.CoversIntern (instantiateRev body fvars it).2 := by obtain ⟨hbody, _, hwalk⟩ := h.requestBounds hmem - have post := Ix.Tc.instantiateRev_spec h.collisionFree.expr hbody hwalk + have post := Ix.Kernel.instantiateRev_spec h.collisionFree.expr hbody hwalk (h.coverage.instRev hmem) hwf hsup.expr exact ⟨post.1, post.2.1, hsup.of_expr_univs post.2.2 @@ -213,7 +213,7 @@ theorem abstractFVars_support_spec rfl rw [hrun] exact ⟨by simp [KExpr.abstractFVarsResult, hfast], hwf, hsup.expr⟩ - · have cached := Ix.Tc.abstractFVarsCached_spec + · have cached := Ix.Kernel.abstractFVarsCached_spec hcollision.expr hbody (depth := 0) (it := it) (sc := {}) (by simpa using hwalk) hreach hwf hsup.expr @@ -271,7 +271,7 @@ theorem abstractFVarsCached_spec {α : Type} fvars.size.toUInt64 depth (it, sc)) := by subst depth obtain ⟨hbody, _, hwalk, _⟩ := h.requestBounds hmem - exact Ix.Tc.abstractFVarsCached_spec h.collisionFree.expr hbody + exact Ix.Kernel.abstractFVarsCached_spec h.collisionFree.expr hbody (by simpa using hwalk) (h.coverage.abstractFVars hmem) hwf hsup hsc theorem instantiateUnivParams_wf {α : Type} @@ -460,4 +460,4 @@ theorem instUniv_wf {α : Type} {initial : TcState .anon} end RunAssumptions -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/ScopedSuffix/ClosedContext.lean b/Ix/Kernel/Verify/ScopedSuffix/ClosedContext.lean similarity index 98% rename from Ix/Tc/Verify/ScopedSuffix/ClosedContext.lean rename to Ix/Kernel/Verify/ScopedSuffix/ClosedContext.lean index fbd6c44f0..f9054d391 100644 --- a/Ix/Tc/Verify/ScopedSuffix/ClosedContext.lean +++ b/Ix/Kernel/Verify/ScopedSuffix/ClosedContext.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.DefEq +import Ix.Kernel.Verify.DefEq /-! # Finite suffix model for closed checker states @@ -13,7 +13,7 @@ proved from `TcM.ctxAddrForLbr_empty`, and the model's state predicate fixes the concrete fields needed to derive the empty reconciliation. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Concrete eager checker states with no legacy/opened local bindings and no driver-owned lazy-ingress hook. @@ -62,7 +62,7 @@ theorem contextDigestFrame {before after : TcState .anon} /-- Reconciliation from a concretely closed state has exactly the empty semantic local context. -/ theorem delta_eq_nil - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {s : TcState .anon} {Delta : KVLCtx} (hclosed : ClosedContextState s) @@ -201,4 +201,4 @@ theorem model_resetPreservesScope end ClosedContextDigest -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/State.lean b/Ix/Kernel/Verify/State.lean similarity index 98% rename from Ix/Tc/Verify/State.lean rename to Ix/Kernel/Verify/State.lean index 878541a03..d40d7b0eb 100644 --- a/Ix/Tc/Verify/State.lean +++ b/Ix/Kernel/Verify/State.lean @@ -1,8 +1,8 @@ -import Ix.Tc.Verify.Env -import Ix.Tc.Verify.Monad -import Ix.Tc.Verify.InstUniv -import Ix.Tc.Verify.Cache -import Ix.Tc.Verify.EquivalenceManager +import Ix.Kernel.Verify.Env +import Ix.Kernel.Verify.Monad +import Ix.Kernel.Verify.InstUniv +import Ix.Kernel.Verify.Cache +import Ix.Kernel.Verify.EquivalenceManager /-! # The verification world and the run invariant @@ -32,9 +32,9 @@ through `KernelStateWF` below. Dual-context agreement and the concrete reduction/inference/native semantic contracts remain K1/K2 obligations. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VDecl VExpr) +open Ix.Theory.Named (VDecl VExpr) /-- A concrete checker state is coherent with one verification world. @@ -82,7 +82,7 @@ theorem TcStateWF.load {trProj : RawProjRel} {s : TcState .anon} supplied by successful checking. The concrete state remains unchanged. -/ theorem TcStateWF.promote {trProj : RawProjRel} {s : TcState .anon} {world : VerifyWorld} {id : KId .anon} {d : VDecl} - {venv' : Lean4Lean.VEnv} + {venv' : Ix.Theory.Named.VEnv} (h : TcStateWF trProj s world) (hpending : PendingDecl trProj world id d) (hwf : VDecl.WF world.venv d venv') : @@ -395,4 +395,4 @@ theorem TcM.instantiateUnivParams.tcInv rw [hrun] at hrunWF exact ⟨⟨world, hworld, hrunWF.1⟩, hrunWF.2⟩ -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Statements.lean b/Ix/Kernel/Verify/Statements.lean similarity index 89% rename from Ix/Tc/Verify/Statements.lean rename to Ix/Kernel/Verify/Statements.lean index 5d457294f..b37e30cce 100644 --- a/Ix/Tc/Verify/Statements.lean +++ b/Ix/Kernel/Verify/Statements.lean @@ -1,8 +1,8 @@ -import Ix.Tc.Verify.Check.PublicStandalone -import Ix.Tc.Verify.Check.PublicBlocks -import Ix.Tc.Verify.Driver.BooleanAcceptance -import Ix.Tc.Verify.Ingress.SerializedBoolean -import Ix.Tc.Verify.RecursiveMethods.Public +import Ix.Kernel.Verify.Check.PublicStandalone +import Ix.Kernel.Verify.Check.PublicBlocks +import Ix.Kernel.Verify.Driver.BooleanAcceptance +import Ix.Kernel.Verify.Ingress.SerializedBoolean +import Ix.Kernel.Verify.RecursiveMethods.Public /-! # Public checker theorem frontier @@ -34,7 +34,7 @@ coordinated transaction, physical/ghost identity, and cache-publication layers. Singleton definition blocks are constructive. Inductive and recursor bodies remain relative in the generic adapter to an explicitly supplied `InductiveOracle` resource; the public E3-S root instantiates both -resources from the Lean4Lean Boolean generation certificate. Quotient +resources from the Ix.Theory.Named Boolean generation certificate. Quotient semantics, mutual/nested inductives, indexed or parameterized families, and multi-definition blocks remain outside this certificate-backed release fragment. Collision, finite-resource, lazy-ingress, source-to-router diff --git a/Ix/Tc/Verify/Subst.lean b/Ix/Kernel/Verify/Subst.lean similarity index 99% rename from Ix/Tc/Verify/Subst.lean rename to Ix/Kernel/Verify/Subst.lean index e716a7684..09f265676 100644 --- a/Ix/Tc/Verify/Subst.lean +++ b/Ix/Kernel/Verify/Subst.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Subst -import Ix.Tc.Verify.Expr +import Ix.Kernel.Subst +import Ix.Kernel.Verify.Expr /-! # Subst slice: `lift`/`subst` walkers against pure specs @@ -31,7 +31,7 @@ UInt64.size` for the binder-descent `cutoff + 1`s). An in-memory expression cannot violate them; a 2⁶⁴-deep tower genuinely would. -/ -namespace Ix.Tc +namespace Ix.Kernel variable {m : Mode} @@ -4740,4 +4740,4 @@ theorem Constructed.abstractFVarsSpec {body : KExpr .anon} end KExpr -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Suffix.lean b/Ix/Kernel/Verify/Suffix.lean similarity index 99% rename from Ix/Tc/Verify/Suffix.lean rename to Ix/Kernel/Verify/Suffix.lean index 28df86478..3dc7f5df8 100644 --- a/Ix/Tc/Verify/Suffix.lean +++ b/Ix/Kernel/Verify/Suffix.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf +import Ix.Kernel.Verify.Whnf /-! # K2 suffix-context transport boundary @@ -15,7 +15,7 @@ these clauses prevents either arbitrary-state context identification or bare address equality from entering the cache proof. -/ -namespace Ix.Tc +namespace Ix.Kernel /-! ## Exact production memo behavior -/ @@ -589,4 +589,4 @@ theorem cacheWriteOracle {trProj : RawProjRel} {fallback : CacheSemantics} end WhnfSuffixModel -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Support.lean b/Ix/Kernel/Verify/Support.lean similarity index 99% rename from Ix/Tc/Verify/Support.lean rename to Ix/Kernel/Verify/Support.lean index 8676da2d2..4fef22776 100644 --- a/Ix/Tc/Verify/Support.lean +++ b/Ix/Kernel/Verify/Support.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.InstUniv +import Ix.Kernel.Verify.InstUniv import Std.Data.HashMap.Lemmas /-! @@ -26,7 +26,7 @@ operations. Nothing here treats constructor closure as support: that would be infinite and would make finite collision freedom impossible. -/ -namespace Ix.Tc +namespace Ix.Kernel /-! ## Constructive finite predicates -/ @@ -803,4 +803,4 @@ theorem mono {before after : List WalkerRequest} end ResourceBounds -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Totalization.lean b/Ix/Kernel/Verify/Totalization.lean similarity index 99% rename from Ix/Tc/Verify/Totalization.lean rename to Ix/Kernel/Verify/Totalization.lean index 9f511d6a5..e9eeb84e3 100644 --- a/Ix/Tc/Verify/Totalization.lean +++ b/Ix/Kernel/Verify/Totalization.lean @@ -1,6 +1,6 @@ -import Ix.Tc.Verify.Monad -import Ix.Tc.CanonicalCheck -import Ix.Tc.Check +import Ix.Kernel.Verify.Monad +import Ix.Kernel.CanonicalCheck +import Ix.Kernel.Check /-! # K0: equations for the total recursive-methods knot @@ -11,7 +11,7 @@ These equations expose the production definitions needed by the later table runs the selected kernel method under the predecessor table. -/ -namespace Ix.Tc +namespace Ix.Kernel variable {m : Mode} @@ -883,4 +883,4 @@ theorem TcM.runRec_directInfer_zero (e : KExpr m) (s : TcState m) @[simp] theorem TcM.ensureForall_eq_runRec (e : KExpr m) : TcM.ensureForall e = TcM.runRec (RecM.ensureForallDirect e) := rfl -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Trans.lean b/Ix/Kernel/Verify/Trans.lean similarity index 94% rename from Ix/Tc/Verify/Trans.lean rename to Ix/Kernel/Verify/Trans.lean index 19ddd7929..30cf5d5d4 100644 --- a/Ix/Tc/Verify/Trans.lean +++ b/Ix/Kernel/Verify/Trans.lean @@ -1,13 +1,15 @@ -import Ix.Tc.Verify.Subst -import Ix.Tc.Verify.VLCtx -import Lean4Lean.Verify.Typing.Expr -import Lean4Lean.Verify.Typing.Lemmas -import Lean4Lean.Theory.Typing.Lemmas +import Ix.Kernel.Verify.Subst +import Ix.Kernel.Verify.VLCtx +import Ix.Theory.Named.Verify.Typing.Expr +import Ix.Theory.Named.Verify.Typing.Lemmas +import Ix.Theory.Named.Typing.Lemmas + +open Ix.Theory (VLevel) /-! # `TrKExprS` — the KExpr ↔ VExpr translation relation -The Ix.Tc restatement of lean4lean's `TrExprS`, rule-for-rule where the +The Ix.Kernel restatement of the named specification's `TrExprS`, rule-for-rule where the source languages agree, with the divergences owned deliberately: - **Universes are total.** Anon-mode `KUniv` parameters are already de @@ -46,9 +48,9 @@ Reused from the dep (VExpr-only content, fvar/Expr-agnostic): eventually `VEnv.HasPrimitives`. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr VLevel VLocalDecl VEnv VConstant) +open Ix.Theory.Named (VExpr VLocalDecl VEnv VConstant) /-- Typing-level context well-formedness, mirroring upstream `VLCtx.WF`: fvar-structural freshness plus per-entry `VLocalDecl.WF` (types are @@ -58,7 +60,7 @@ def KVLCtx.WF (env : VEnv) (U : Nat) : KVLCtx → Prop | (ofv, d) :: (Δ : KVLCtx) => KVLCtx.WF env U Δ ∧ (∀ fv deps, ofv = some (fv, deps) → fv ∉ Δ.fvars ∧ deps ⊆ Δ.fvars) ∧ - Lean4Lean.VLocalDecl.WF env U Δ.toCtx d + Ix.Theory.Named.VLocalDecl.WF env U Δ.toCtx d theorem KVLCtx.WF.fvwf {env : VEnv} {U : Nat} : ∀ {Δ : KVLCtx}, KVLCtx.WF env U Δ → Δ.FVWF @@ -96,7 +98,7 @@ def VarsScoped (bvars : Nat) (fvars : List FVarId) : KExpr m → Prop body.VarsScoped (bvars + 1) fvars | .prj _ _ value _ => value.VarsScoped bvars fvars -/-- Structural scope at one mixed Ix/Lean4Lean local context. -/ +/-- Structural scope at one mixed Ix/Ix.Theory.Named local context. -/ def ContextScoped (Delta : KVLCtx) (e : KExpr m) : Prop := e.VarsScoped Delta.bvars Delta.fvars @@ -334,7 +336,7 @@ inductive KBVLift : KVLCtx → KVLCtx → Nat → Nat → Nat → Nat → Prop theorem KBVLift.toCtx {Δ Δ' : KVLCtx} {dn dk n k : Nat} (W : KBVLift Δ Δ' dn dk n k) : - Lean4Lean.Ctx.LiftN n k Δ.toCtx Δ'.toCtx := by + Ix.Theory.Named.Ctx.LiftN n k Δ.toCtx Δ'.toCtx := by induction W with | refl => exact .zero [] | @skip _ Δ' _ _ d _ ih => @@ -474,15 +476,15 @@ protected theorem KBVLift.find? {Δ Δ' : KVLCtx} {dn dk n k : Nat} | refl => simp [H] | @skip _ Δ' _ _ d _ ih => obtain v | fv := v <;> simp [find?, liftVar, next] <;> - exact ⟨_, _, ih H, by simp [Lean4Lean.VExpr.liftN_liftN]⟩ + exact ⟨_, _, ih H, by simp [Ix.Theory.Named.VExpr.liftN_liftN]⟩ | @skip_fvar Δ Δ' _ _ fv d hfresh _ ih => obtain v | fv' := v · simp [find?, liftVar, next] - exact ⟨_, _, ih H, by simp [Lean4Lean.VExpr.liftN_liftN]⟩ + exact ⟨_, _, ih H, by simp [Ix.Theory.Named.VExpr.liftN_liftN]⟩ · have hne : ¬fv.1 = fv' := fun hEq => hfresh (hEq ▸ find?_inr_mem H) simp [find?, liftVar, next, hne] - exact ⟨_, _, ih H, by simp [Lean4Lean.VExpr.liftN_liftN]⟩ + exact ⟨_, _, ih H, by simp [Ix.Theory.Named.VExpr.liftN_liftN]⟩ | cons d _ ih => obtain (_ | v) | fv := v <;> simp [liftVar] <;> [ (simp [find?, next] at H ⊢; simp [← H]); @@ -496,43 +498,43 @@ protected theorem KBVLift.find? {Δ Δ' : KVLCtx} {dn dk n k : Nat} ( simp [find?, next] at H ⊢ obtain ⟨e, A, H, rfl, rfl⟩ := H refine ⟨_, _, ih H, ?_⟩ )] <;> - open Lean4Lean.VLocalDecl in + open Ix.Theory.Named.VLocalDecl in cases d <;> - simp [Lean4Lean.VExpr.lift_liftN', liftN, value, type, depth, - Lean4Lean.VExpr.liftN] + simp [Ix.Theory.Named.VExpr.lift_liftN', liftN, value, type, depth, + Ix.Theory.Named.VExpr.liftN] end KVLCtx /-- Closed literal encodings are `liftN`-invariant. -/ private theorem liftN_natLit (v n k : Nat) : - (Lean4Lean.VExpr.natLit v).liftN n k = Lean4Lean.VExpr.natLit v := by + (Ix.Theory.Named.VExpr.natLit v).liftN n k = Ix.Theory.Named.VExpr.natLit v := by induction v with | zero => rfl | succ v ih => - show Lean4Lean.VExpr.app _ _ = _ - rw [show ((Lean4Lean.VExpr.natLit v).liftN n k) - = Lean4Lean.VExpr.natLit v from ih] + show Ix.Theory.Named.VExpr.app _ _ = _ + rw [show ((Ix.Theory.Named.VExpr.natLit v).liftN n k) + = Ix.Theory.Named.VExpr.natLit v from ih] rfl private theorem liftN_listCharLit (s : List Char) (n k : Nat) : - (Lean4Lean.VExpr.listCharLit s).liftN n k - = Lean4Lean.VExpr.listCharLit s := by + (Ix.Theory.Named.VExpr.listCharLit s).liftN n k + = Ix.Theory.Named.VExpr.listCharLit s := by induction s with | nil => rfl | cons c s ih => - show Lean4Lean.VExpr.app (Lean4Lean.VExpr.app _ (Lean4Lean.VExpr.app _ - ((Lean4Lean.VExpr.natLit c.toNat).liftN n k))) - ((Lean4Lean.VExpr.listCharLit s).liftN n k) = _ + show Ix.Theory.Named.VExpr.app (Ix.Theory.Named.VExpr.app _ (Ix.Theory.Named.VExpr.app _ + ((Ix.Theory.Named.VExpr.natLit c.toNat).liftN n k))) + ((Ix.Theory.Named.VExpr.listCharLit s).liftN n k) = _ rw [liftN_natLit, ih] rfl private theorem liftN_trLiteral (l : Lean.Literal) (n k : Nat) : - (Lean4Lean.VExpr.trLiteral l).liftN n k - = Lean4Lean.VExpr.trLiteral l := by + (Ix.Theory.Named.VExpr.trLiteral l).liftN n k + = Ix.Theory.Named.VExpr.trLiteral l := by cases l with | natVal v => exact liftN_natLit v n k | strVal s => - show Lean4Lean.VExpr.app _ ((Lean4Lean.VExpr.listCharLit _).liftN n k) = _ + show Ix.Theory.Named.VExpr.app _ ((Ix.Theory.Named.VExpr.listCharLit _).liftN n k) = _ rw [liftN_listCharLit] rfl @@ -541,12 +543,12 @@ private theorem liftN_trLiteral (l : Lean.Literal) (n k : Nat) : walker's `UInt64` arguments; `htp` is the trProj-weakening the abstract projection parameter must satisfy (upstream `TrProj.weakN`'s shape). -/ -theorem TrKExprS.weakBV {env : Lean4Lean.VEnv} {uvars : Nat} +theorem TrKExprS.weakBV {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop} (henv : env.Ordered) (htp : ∀ {Γ Γ' : List VExpr} {n k : Nat} {s : Lean.Name} {i : Nat} - {e e' : VExpr}, Lean4Lean.Ctx.LiftN n k Γ Γ' → + {e e' : VExpr}, Ix.Theory.Named.Ctx.LiftN n k Γ Γ' → trProj uvars Γ s i e e' → trProj uvars Γ' s i (e.liftN n k) (e'.liftN n k)) {Δ : KVLCtx} {e : KExpr .anon} {e' : VExpr} @@ -651,13 +653,13 @@ theorem TrKExprS.weakBV {env : Lean4Lean.VEnv} {uvars : Nat} (htp W.toCtx htrp) | @nat Δ v blob md h => intro Δ' dn dk n k shift cutoff W hshift hcutoff hbig - rw [show (Lean4Lean.VExpr.natLit v).liftN n k - = Lean4Lean.VExpr.natLit v from liftN_natLit v n k] + rw [show (Ix.Theory.Named.VExpr.natLit v).liftN n k + = Ix.Theory.Named.VExpr.natLit v from liftN_natLit v n k] exact .nat h | @str Δ s blob md h => intro Δ' dn dk n k shift cutoff W hshift hcutoff hbig - rw [show (Lean4Lean.VExpr.trLiteral (.strVal s)).liftN n k - = Lean4Lean.VExpr.trLiteral (.strVal s) from + rw [show (Ix.Theory.Named.VExpr.trLiteral (.strVal s)).liftN n k + = Ix.Theory.Named.VExpr.trLiteral (.strVal s) from liftN_trLiteral (.strVal s) n k] exact .str h @@ -692,12 +694,12 @@ private theorem tr_toNat_le_sat1_add_one (x : UInt64) : mention only the source expression: `hcut` bounds binder descent and `hlift` bounds shifted loose indices. These are exactly the two no-wrap obligations carried by `WalkerRequest.Bounds (.lift ...)`. -/ -theorem TrKExprS.weakBV_lbr {env : Lean4Lean.VEnv} {uvars : Nat} +theorem TrKExprS.weakBV_lbr {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop} (henv : env.Ordered) (htp : ∀ {Γ Γ' : List VExpr} {n k : Nat} {s : Lean.Name} {i : Nat} - {e e' : VExpr}, Lean4Lean.Ctx.LiftN n k Γ Γ' → + {e e' : VExpr}, Ix.Theory.Named.Ctx.LiftN n k Γ Γ' → trProj uvars Γ s i e e' → trProj uvars Γ' s i (e.liftN n k) (e'.liftN n k)) {Δ : KVLCtx} {e : KExpr .anon} {e' : VExpr} @@ -848,14 +850,14 @@ theorem TrKExprS.weakBV_lbr {env : Lean4Lean.VEnv} {uvars : Nat} | @nat Δ v blob info h => cases hcon intro Δ' dn dk n k shift cutoff W hshift hcutoff hcut hlift - rw [show (Lean4Lean.VExpr.natLit v).liftN n k - = Lean4Lean.VExpr.natLit v from liftN_natLit v n k] + rw [show (Ix.Theory.Named.VExpr.natLit v).liftN n k + = Ix.Theory.Named.VExpr.natLit v from liftN_natLit v n k] exact .nat h | @str Δ v blob info h => cases hcon intro Δ' dn dk n k shift cutoff W hshift hcutoff hcut hlift - rw [show (Lean4Lean.VExpr.trLiteral (.strVal v)).liftN n k - = Lean4Lean.VExpr.trLiteral (.strVal v) from + rw [show (Ix.Theory.Named.VExpr.trLiteral (.strVal v)).liftN n k + = Ix.Theory.Named.VExpr.trLiteral (.strVal v) from liftN_trLiteral (.strVal v) n k] exact .str h @@ -889,7 +891,7 @@ private theorem depth_inst (d : VLocalDecl) (e₀ : VExpr) (k : Nat) : protected theorem KInstN.toCtx {Δ₀ : KVLCtx} {e₀ A₀ : VExpr} {dk k : Nat} {Δ₁ Δ : KVLCtx} (W : KInstN Δ₀ e₀ A₀ dk k Δ₁ Δ) : - Lean4Lean.Ctx.InstN Δ₀.toCtx e₀ A₀ k Δ₁.toCtx Δ.toCtx := by + Ix.Theory.Named.Ctx.InstN Δ₀.toCtx e₀ A₀ k Δ₁.toCtx Δ.toCtx := by induction W with | zero => exact .zero | @succ dk k Γ Γ' d _ ih => @@ -924,7 +926,7 @@ private theorem liftN_depth_inst (d : VLocalDecl) (f e₀ : VExpr) cases d with | vlam A => show (f.liftN 1).inst e₀ (k + 1) = (f.inst e₀ k).liftN 1 - exact (Lean4Lean.VExpr.lift_instN_lo f e₀).symm + exact (Ix.Theory.Named.VExpr.lift_instN_lo f e₀).symm | vlet A v => show (f.liftN 0).inst e₀ (k + 0) = (f.inst e₀ k).liftN 0 simp @@ -945,7 +947,7 @@ theorem KInstN.find?_hit {Δ₀ : KVLCtx} {e₀ A₀ : VExpr} {dk k : Nat} intro e' A H simp [find?, next] at H obtain ⟨e, A', H, rfl, rfl⟩ := H - rw [liftN_depth_inst, ih H, Lean4Lean.VExpr.liftN_liftN] + rw [liftN_depth_inst, ih H, Ix.Theory.Named.VExpr.liftN_liftN] /-- Below the substitution site: indices are untouched and values instantiate pointwise. -/ @@ -964,10 +966,10 @@ theorem KInstN.find?_lt {Δ₀ : KVLCtx} {e₀ A₀ : VExpr} {dk k : Nat} obtain ⟨rfl, rfl⟩ := H constructor <;> · cases d <;> - simp [Lean4Lean.VLocalDecl.inst, Lean4Lean.VLocalDecl.value, - Lean4Lean.VLocalDecl.type, Lean4Lean.VLocalDecl.depth, - Lean4Lean.VExpr.inst, Lean4Lean.VExpr.instVar, - Lean4Lean.VExpr.lift_instN_lo] + simp [Ix.Theory.Named.VLocalDecl.inst, Ix.Theory.Named.VLocalDecl.value, + Ix.Theory.Named.VLocalDecl.type, Ix.Theory.Named.VLocalDecl.depth, + Ix.Theory.Named.VExpr.inst, Ix.Theory.Named.VExpr.instVar, + Ix.Theory.Named.VExpr.lift_instN_lo] | j + 1 => simp [find?, next] at H ⊢ obtain ⟨e, A', H, rfl, rfl⟩ := H @@ -988,8 +990,8 @@ theorem KInstN.find?_gt {Δ₀ : KVLCtx} {e₀ A₀ : VExpr} {dk k : Nat} simp [find?, next] at H obtain ⟨e, A', H, rfl, rfl⟩ := H simp only [Nat.add_sub_cancel] - rw [show Lean4Lean.VLocalDecl.depth (.vlam A₀) = 1 from rfl, - Lean4Lean.VExpr.inst_liftN, Lean4Lean.VExpr.inst_liftN] + rw [show Ix.Theory.Named.VLocalDecl.depth (.vlam A₀) = 1 from rfl, + Ix.Theory.Named.VExpr.inst_liftN, Ix.Theory.Named.VExpr.inst_liftN] exact H | @succ dk k Γ Γ' d _ ih => intro j e' A hj H @@ -1017,8 +1019,8 @@ theorem KInstN.find?_fvar {Δ₀ : KVLCtx} {e₀ A₀ : VExpr} {dk k : Nat} intro fv e' A H simp [find?, next] at H obtain ⟨e, A', H, rfl, rfl⟩ := H - rw [show Lean4Lean.VLocalDecl.depth (.vlam A₀) = 1 from rfl, - Lean4Lean.VExpr.inst_liftN, Lean4Lean.VExpr.inst_liftN] + rw [show Ix.Theory.Named.VLocalDecl.depth (.vlam A₀) = 1 from rfl, + Ix.Theory.Named.VExpr.inst_liftN, Ix.Theory.Named.VExpr.inst_liftN] exact H | @succ dk k Γ Γ' d _ ih => intro fv e' A H @@ -1088,12 +1090,12 @@ theorem KInstLet.find?_hit {Δ₀ : KVLCtx} {e₀ A₀ : VExpr} intro e' A H simp [find?, next] at H obtain ⟨rfl, rfl⟩ := H - simp [Lean4Lean.VLocalDecl.value] + simp [Ix.Theory.Named.VLocalDecl.value] | @succ dk k Γ Γ' d _ ih => intro e' A H simp [find?, next] at H obtain ⟨e, A', H, rfl, rfl⟩ := H - rw [ih H, Lean4Lean.VExpr.liftN_liftN] + rw [ih H, Ix.Theory.Named.VExpr.liftN_liftN] /-- References below the removed let retain both their index and resolved Theory pair. -/ @@ -1129,7 +1131,7 @@ theorem KInstLet.find?_gt {Δ₀ : KVLCtx} {e₀ A₀ : VExpr} | j + 1, _ => simp [find?, next] at H obtain ⟨e, A', H, rfl, rfl⟩ := H - simpa [Lean4Lean.VLocalDecl.depth] using H + simpa [Ix.Theory.Named.VLocalDecl.depth] using H | @succ dk k Γ Γ' d _ ih => intro j e' A hj H match j, hj with @@ -1153,7 +1155,7 @@ theorem KInstLet.find?_fvar {Δ₀ : KVLCtx} {e₀ A₀ : VExpr} intro fv e' A H simp [find?, next] at H obtain ⟨e, A', H, rfl, rfl⟩ := H - simpa [Lean4Lean.VLocalDecl.depth] using H + simpa [Ix.Theory.Named.VLocalDecl.depth] using H | @succ dk k Γ Γ' d _ ih => intro fv e' A H simp [find?, next] at H ⊢ @@ -1164,35 +1166,35 @@ end KVLCtx /-- Closed literal encodings are `inst`-invariant. -/ private theorem inst_natLit (v : Nat) (e₀ : VExpr) (k : Nat) : - (Lean4Lean.VExpr.natLit v).inst e₀ k = Lean4Lean.VExpr.natLit v := by + (Ix.Theory.Named.VExpr.natLit v).inst e₀ k = Ix.Theory.Named.VExpr.natLit v := by induction v with | zero => rfl | succ v ih => - show Lean4Lean.VExpr.app _ _ = _ - rw [show ((Lean4Lean.VExpr.natLit v).inst e₀ k) - = Lean4Lean.VExpr.natLit v from ih] + show Ix.Theory.Named.VExpr.app _ _ = _ + rw [show ((Ix.Theory.Named.VExpr.natLit v).inst e₀ k) + = Ix.Theory.Named.VExpr.natLit v from ih] rfl private theorem inst_listCharLit (s : List Char) (e₀ : VExpr) (k : Nat) : - (Lean4Lean.VExpr.listCharLit s).inst e₀ k - = Lean4Lean.VExpr.listCharLit s := by + (Ix.Theory.Named.VExpr.listCharLit s).inst e₀ k + = Ix.Theory.Named.VExpr.listCharLit s := by induction s with | nil => rfl | cons c s ih => - show Lean4Lean.VExpr.app (Lean4Lean.VExpr.app _ (Lean4Lean.VExpr.app _ - ((Lean4Lean.VExpr.natLit c.toNat).inst e₀ k))) - ((Lean4Lean.VExpr.listCharLit s).inst e₀ k) = _ + show Ix.Theory.Named.VExpr.app (Ix.Theory.Named.VExpr.app _ (Ix.Theory.Named.VExpr.app _ + ((Ix.Theory.Named.VExpr.natLit c.toNat).inst e₀ k))) + ((Ix.Theory.Named.VExpr.listCharLit s).inst e₀ k) = _ rw [inst_natLit, ih] rfl private theorem inst_trLiteral (l : Lean.Literal) (e₀ : VExpr) (k : Nat) : - (Lean4Lean.VExpr.trLiteral l).inst e₀ k - = Lean4Lean.VExpr.trLiteral l := by + (Ix.Theory.Named.VExpr.trLiteral l).inst e₀ k + = Ix.Theory.Named.VExpr.trLiteral l := by cases l with | natVal v => exact inst_natLit v e₀ k | strVal s => - show Lean4Lean.VExpr.app _ - ((Lean4Lean.VExpr.listCharLit _).inst e₀ k) = _ + show Ix.Theory.Named.VExpr.app _ + ((Ix.Theory.Named.VExpr.listCharLit _).inst e₀ k) = _ rw [inst_listCharLit] rfl @@ -1201,18 +1203,18 @@ private theorem inst_trLiteral (l : Lean.Literal) (e₀ : VExpr) (k : Nat) : the weakening/instantiation closure the abstract projection parameter must satisfy (upstream's `TrProj.weakN`/`TrProj.instN`, both sorried there). -/ -theorem TrKExprS.instN {env : Lean4Lean.VEnv} {uvars : Nat} +theorem TrKExprS.instN {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop} (henv : env.Ordered) (htp : ∀ {Γ Γ' : List VExpr} {n k : Nat} {s : Lean.Name} {i : Nat} - {e e' : VExpr}, Lean4Lean.Ctx.LiftN n k Γ Γ' → + {e e' : VExpr}, Ix.Theory.Named.Ctx.LiftN n k Γ Γ' → trProj uvars Γ s i e e' → trProj uvars Γ' s i (e.liftN n k) (e'.liftN n k)) (htpI : ∀ {Γ₀ : List VExpr} {e₀ A₀ : VExpr} {k : Nat} {Γ₁ Γ : List VExpr} {s : Lean.Name} {i : Nat} {e e' : VExpr}, env.HasType uvars Γ₀ e₀ A₀ → - Lean4Lean.Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ → + Ix.Theory.Named.Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ → trProj uvars Γ₁ s i e e' → trProj uvars Γ s i (e.inst e₀ k) (e'.inst e₀ k)) {Δ₀ : KVLCtx} {arg : KExpr .anon} {e₀' A₀ : VExpr} @@ -1330,13 +1332,13 @@ theorem TrKExprS.instN {env : Lean4Lean.VEnv} {uvars : Nat} (htpI t₀ W.toCtx htrp) | @nat Δ₁' v blob md h => intro Δ dk k depth W hdepth hbig - rw [show (Lean4Lean.VExpr.natLit v).inst e₀' k - = Lean4Lean.VExpr.natLit v from inst_natLit v e₀' k] + rw [show (Ix.Theory.Named.VExpr.natLit v).inst e₀' k + = Ix.Theory.Named.VExpr.natLit v from inst_natLit v e₀' k] exact .nat h | @str Δ₁' s blob md h => intro Δ dk k depth W hdepth hbig - rw [show (Lean4Lean.VExpr.trLiteral (.strVal s)).inst e₀' k - = Lean4Lean.VExpr.trLiteral (.strVal s) from + rw [show (Ix.Theory.Named.VExpr.trLiteral (.strVal s)).inst e₀' k + = Ix.Theory.Named.VExpr.trLiteral (.strVal s) from inst_trLiteral (.strVal s) e₀' k] exact .str h @@ -1344,12 +1346,12 @@ theorem TrKExprS.instN {env : Lean4Lean.VEnv} {uvars : Nat} context `vlet` leaves the translated Theory expression unchanged. This mirrors upstream `TrExprS.instN_let`, while retaining the explicit UInt64 resource bound required by `KExpr.substSpec`. -/ -theorem TrKExprS.instN_let {env : Lean4Lean.VEnv} {uvars : Nat} +theorem TrKExprS.instN_let {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop} (henv : env.Ordered) (htp : ∀ {Γ Γ' : List VExpr} {n k : Nat} {s : Lean.Name} {i : Nat} - {e e' : VExpr}, Lean4Lean.Ctx.LiftN n k Γ Γ' → + {e e' : VExpr}, Ix.Theory.Named.Ctx.LiftN n k Γ Γ' → trProj uvars Γ s i e e' → trProj uvars Γ' s i (e.liftN n k) (e'.liftN n k)) {Δ₀ : KVLCtx} {arg : KExpr .anon} {e₀' A₀ : VExpr} @@ -1474,12 +1476,12 @@ theorem TrKExprS.instN_let {env : Lean4Lean.VEnv} {uvars : Nat} /-- Walker-tight let instantiation. The bound is the final conjunct of `WalkerRequest.Bounds (.subst body arg depth)`, and `harg` is its constructed-argument conjunct. No ambient-context size is needed. -/ -theorem TrKExprS.instN_let_lbr {env : Lean4Lean.VEnv} {uvars : Nat} +theorem TrKExprS.instN_let_lbr {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop} (henv : env.Ordered) (htp : ∀ {Γ Γ' : List VExpr} {n k : Nat} {s : Lean.Name} {i : Nat} - {e e' : VExpr}, Lean4Lean.Ctx.LiftN n k Γ Γ' → + {e e' : VExpr}, Ix.Theory.Named.Ctx.LiftN n k Γ Γ' → trProj uvars Γ s i e e' → trProj uvars Γ' s i (e.liftN n k) (e'.liftN n k)) {Δ₀ : KVLCtx} {arg : KExpr .anon} {e₀' A₀ : VExpr} @@ -1592,18 +1594,18 @@ theorem TrKExprS.instN_let_lbr {env : Lean4Lean.VEnv} {uvars : Nat} /-- **Beta step at the API level**: substituting under one `vlam`. Upstream `TrExprS.inst`. -/ -theorem TrKExprS.inst {env : Lean4Lean.VEnv} {uvars : Nat} +theorem TrKExprS.inst {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop} (henv : env.Ordered) (htp : ∀ {Γ Γ' : List VExpr} {n k : Nat} {s : Lean.Name} {i : Nat} - {e e' : VExpr}, Lean4Lean.Ctx.LiftN n k Γ Γ' → + {e e' : VExpr}, Ix.Theory.Named.Ctx.LiftN n k Γ Γ' → trProj uvars Γ s i e e' → trProj uvars Γ' s i (e.liftN n k) (e'.liftN n k)) (htpI : ∀ {Γ₀ : List VExpr} {e₀ A₀ : VExpr} {k : Nat} {Γ₁ Γ : List VExpr} {s : Lean.Name} {i : Nat} {e e' : VExpr}, env.HasType uvars Γ₀ e₀ A₀ → - Lean4Lean.Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ → + Ix.Theory.Named.Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ → trProj uvars Γ₁ s i e e' → trProj uvars Γ s i (e.inst e₀ k) (e'.inst e₀ k)) {Δ : KVLCtx} {arg body : KExpr .anon} {e₀' A₀ body' : VExpr} @@ -1618,12 +1620,12 @@ theorem TrKExprS.inst {env : Lean4Lean.VEnv} {uvars : Nat} /-- **Explicit-let step at the API level**: substituting under one `vlet` preserves the already-inlined Theory translation. Upstream `TrExprS.inst_let`. -/ -theorem TrKExprS.inst_let {env : Lean4Lean.VEnv} {uvars : Nat} +theorem TrKExprS.inst_let {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop} (henv : env.Ordered) (htp : ∀ {Γ Γ' : List VExpr} {n k : Nat} {s : Lean.Name} {i : Nat} - {e e' : VExpr}, Lean4Lean.Ctx.LiftN n k Γ Γ' → + {e e' : VExpr}, Ix.Theory.Named.Ctx.LiftN n k Γ Γ' → trProj uvars Γ s i e e' → trProj uvars Γ' s i (e.liftN n k) (e'.liftN n k)) {Δ : KVLCtx} {arg body : KExpr .anon} {e₀' A₀ body' : VExpr} @@ -1637,12 +1639,12 @@ theorem TrKExprS.inst_let {env : Lean4Lean.VEnv} {uvars : Nat} /-- Explicit-let instantiation with the exact depth-zero substitution resource bound, independent of the ambient context size. -/ -theorem TrKExprS.inst_let_lbr {env : Lean4Lean.VEnv} {uvars : Nat} +theorem TrKExprS.inst_let_lbr {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop} (henv : env.Ordered) (htp : ∀ {Γ Γ' : List VExpr} {n k : Nat} {s : Lean.Name} {i : Nat} - {e e' : VExpr}, Lean4Lean.Ctx.LiftN n k Γ Γ' → + {e e' : VExpr}, Ix.Theory.Named.Ctx.LiftN n k Γ Γ' → trProj uvars Γ s i e e' → trProj uvars Γ' s i (e.liftN n k) (e'.liftN n k)) {Δ : KVLCtx} {arg body : KExpr .anon} {e₀' A₀ body' : VExpr} @@ -1670,7 +1672,7 @@ theorem KVLCtx.vlocalDecl_wf_hasType {env : VEnv} {U : Nat} {Δ : KVLCtx} /-- `VLocalDecl.is_liftN` re-keyed: consing an entry lifts the bare context by the entry's depth. -/ theorem KVLCtx.is_liftN {Δ : KVLCtx} {ofv} : - ∀ {d}, Lean4Lean.Ctx.LiftN (VLocalDecl.depth d) 0 Δ.toCtx + ∀ {d}, Ix.Theory.Named.Ctx.LiftN (VLocalDecl.depth d) 0 Δ.toCtx (KVLCtx.toCtx ((ofv, d) :: Δ)) | .vlam _ => .one | .vlet .. => .zero [] @@ -1694,7 +1696,7 @@ theorem KVLCtx.WF.find?_wf {env : VEnv} {U : Nat} (henv : env.Ordered) : translation context is `OnCtx`-well-formed. -/ theorem KVLCtx.WF.toCtx {env : VEnv} {U : Nat} : ∀ {Δ : KVLCtx}, KVLCtx.WF env U Δ → - Lean4Lean.OnCtx Δ.toCtx (env.IsType U) + Ix.Theory.Named.OnCtx Δ.toCtx (env.IsType U) | [], _ => ⟨⟩ | (_, .vlam _) :: _, ⟨hΔ, _, hA⟩ => ⟨hΔ.toCtx, hA⟩ | (_, .vlet ..) :: _, ⟨hΔ, _, _⟩ => hΔ.toCtx @@ -1702,7 +1704,7 @@ theorem KVLCtx.WF.toCtx {env : VEnv} {U : Nat} : /-- Upstream idiom (Verify/Typing/Lemmas.lean:216): context WF coerces to the bare-context `OnCtx` whenever a Theory lemma wants it. -/ instance {env : VEnv} {U : Nat} {Δ : KVLCtx} : - Coe (KVLCtx.WF env U Δ) (Lean4Lean.OnCtx Δ.toCtx (env.IsType U)) := + Coe (KVLCtx.WF env U Δ) (Ix.Theory.Named.OnCtx Δ.toCtx (env.IsType U)) := ⟨KVLCtx.WF.toCtx⟩ /-- A term well-formed in the empty context is well-formed anywhere @@ -1711,7 +1713,7 @@ private theorem wf_weak0 {env : VEnv} (henv : env.Ordered) {U : Nat} {e : VExpr} (h : VExpr.WF env U [] e) (Γ : List VExpr) : VExpr.WF env U Γ e := by obtain ⟨A, hA⟩ := h - have W : Lean4Lean.Ctx.LiftN Γ.length 0 [] (Γ ++ []) := .zero Γ rfl + have W : Ix.Theory.Named.Ctx.LiftN Γ.length 0 [] (Γ ++ []) := .zero Γ rfl rw [List.append_nil] at W have h2 := hA.weakN henv W have hce : e.ClosedN 0 := (hA.closedN' henv.closed trivial).1 @@ -1882,7 +1884,7 @@ inductive IsDefEq : KVLCtx → KVLCtx → Prop | cons {Δ₁ Δ₂ : KVLCtx} {ofv} {d₁ d₂ : VLocalDecl} : IsDefEq Δ₁ Δ₂ → (∀ fv deps, ofv = some (fv, deps) → fv ∉ Δ₁.fvars ∧ deps ⊆ Δ₁.fvars) → - Lean4Lean.VLocalDecl.IsDefEq env U Δ₁.toCtx d₁ d₂ → + Ix.Theory.Named.VLocalDecl.IsDefEq env U Δ₁.toCtx d₁ d₂ → IsDefEq ((ofv, d₁) :: Δ₁) ((ofv, d₂) :: Δ₂) theorem IsDefEq.refl {env : VEnv} {U : Nat} (henv : env.Ordered) : @@ -1890,7 +1892,7 @@ theorem IsDefEq.refl {env : VEnv} {U : Nat} (henv : env.Ordered) : | [], _ => .nil | (_, _) :: _, ⟨h1, h2, h3⟩ => .cons (IsDefEq.refl henv h1) h2 - (Lean4Lean.VLocalDecl.IsDefEq.refl henv h1.toCtx h3) + (Ix.Theory.Named.VLocalDecl.IsDefEq.refl henv h1.toCtx h3) theorem IsDefEq.defeqCtx {env : VEnv} {U : Nat} {Δ₁ Δ₂ : KVLCtx} : IsDefEq env U Δ₁ Δ₂ → @@ -1948,7 +1950,7 @@ theorem IsDefEq.find?_uniq {env : VEnv} {U : Nat} (henv : VEnv.WF env) : exact ⟨⟨_, h4⟩, h3⟩ · simp rintro d₁' n₁' H1' rfl rfl d₂' n₂' H2' rfl rfl - simpa [Lean4Lean.VLocalDecl.depth, KVLCtx.toCtx] using + simpa [Ix.Theory.Named.VLocalDecl.depth, KVLCtx.toCtx] using find?_uniq henv hΔ H1' H2' /-- Transport of `find?`-success along a context defeq (upstream @@ -1988,19 +1990,19 @@ makes the two targets syntactically equal, so self-defeq suffices — no /-- Closure properties of the abstract, universe-indexed projection translation. The substitution premise and the universe-index change are - kept exactly as Lean4Lean states them; erasing either makes the concrete + kept exactly as Ix.Theory.Named states them; erasing either makes the concrete `TrProj` impossible to instantiate. -/ structure TrProjOK (env : VEnv) (uvars : Nat) (trProj : Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop) : Prop where weakN : ∀ {Γ Γ' : List VExpr} {n k : Nat} {s : Lean.Name} {i : Nat} - {e e' : VExpr}, Lean4Lean.Ctx.LiftN n k Γ Γ' → + {e e' : VExpr}, Ix.Theory.Named.Ctx.LiftN n k Γ Γ' → trProj uvars Γ s i e e' → trProj uvars Γ' s i (e.liftN n k) (e'.liftN n k) instN : ∀ {Γ₀ : List VExpr} {e₀ A₀ : VExpr} {k : Nat} {Γ₁ Γ : List VExpr} {s : Lean.Name} {i : Nat} {e e' : VExpr}, env.HasType uvars Γ₀ e₀ A₀ → - Lean4Lean.Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ → + Ix.Theory.Named.Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ → trProj uvars Γ₁ s i e e' → trProj uvars Γ s i (e.inst e₀ k) (e'.inst e₀ k) wf : ∀ {Γ : List VExpr} {s : Lean.Name} {i : Nat} {e e' : VExpr}, @@ -2022,7 +2024,7 @@ structure TrProjOK (env : VEnv) (uvars : Nat) trProj U' (Γ.map (VExpr.instL ls)) s i (e.instL ls) (e'.instL ls) monoU : ∀ {U U' : Nat} {Γ : List VExpr} {s : Lean.Name} {i : Nat} - {e e' : VExpr}, U ≤ U' → Lean4Lean.OnCtx Γ (env.IsType U) → + {e e' : VExpr}, U ≤ U' → Ix.Theory.Named.OnCtx Γ (env.IsType U) → trProj U Γ s i e e' → trProj U' Γ s i e e' /-- Uniqueness of structural translation up to defeq (upstream @@ -2225,7 +2227,7 @@ theorem TrKExpr.defeq {env : VEnv} {uvars : Nat} {trProj : Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop} (henv : VEnv.WF env) {m : Mode} {Δ : KVLCtx} {e : KExpr m} {e₁ e₂ : VExpr} - (hΔ : Lean4Lean.OnCtx Δ.toCtx (env.IsType uvars)) + (hΔ : Ix.Theory.Named.OnCtx Δ.toCtx (env.IsType uvars)) (h1 : TrKExpr env uvars nameOf trProj Δ e e₁) (h2 : env.IsDefEqU uvars Δ.toCtx e₁ e₂) : TrKExpr env uvars nameOf trProj Δ e e₂ := @@ -2239,7 +2241,7 @@ theorem TrKExpr.app {env : VEnv} {uvars : Nat} (henv : VEnv.WF env) {m : Mode} {Δ : KVLCtx} {f a : KExpr m} {md : ExprInfo m} {f' a' A B : VExpr} - (hΔ : Lean4Lean.OnCtx Δ.toCtx (env.IsType uvars)) + (hΔ : Ix.Theory.Named.OnCtx Δ.toCtx (env.IsType uvars)) (h1 : env.HasType uvars Δ.toCtx f' (.forallE A B)) (h2 : env.HasType uvars Δ.toCtx a' A) (h3 : TrKExpr env uvars nameOf trProj Δ f f') @@ -2361,4 +2363,4 @@ theorem TrKExpr.prj {env : VEnv} {uvars : Nat} have ⟨_, H2'⟩ := htp.defeqDFC hΓ h2.symm H2 ⟨_, .prj h1 s2 H2', htp.uniq hΓ H2' H2 h2⟩ -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/VLCtx.lean b/Ix/Kernel/Verify/VLCtx.lean similarity index 93% rename from Ix/Tc/Verify/VLCtx.lean rename to Ix/Kernel/Verify/VLCtx.lean index c59e2aaa0..4bde03d5e 100644 --- a/Ix/Tc/Verify/VLCtx.lean +++ b/Ix/Kernel/Verify/VLCtx.lean @@ -1,10 +1,12 @@ -import Ix.Tc.Env -import Lean4Lean.Verify.VLCtx +import Ix.Kernel.Env +import Ix.Theory.Named.Verify.VLCtx + +open Ix.Theory (VLevel) /-! # `KVLCtx` — the translation-side local context -The Ix.Tc analogue of lean4lean's `VLCtx`: a list of local declarations, +The Ix.Kernel analogue of the named specification's `VLCtx`: a list of local declarations, each optionally tagged with the `FVarId` that addresses it (plus that entry's fvar dependency list, used by the well-formedness layer in Verify/Ctx.lean). Entries tagged `none` are addressable only by de @@ -17,7 +19,7 @@ Design notes: `VExpr`-valued and fvar-agnostic (Theory-shaped despite living in upstream's Verify tree), so its `liftN`/`inst`/`instL` lemmas transfer. Only the fvar-keyed list structure is re-stated, keyed by - `Ix.Tc.FVarId` (the checker's per-`TypeChecker` `UInt64` ids) instead + `Ix.Kernel.FVarId` (the checker's per-`TypeChecker` `UInt64` ids) instead of `Lean.FVarId`. - `find?` mirrors upstream exactly: a bvar hit on a `vlam` produces `.bvar 0` lifted by the depth walked; a hit on a `vlet` produces the @@ -29,9 +31,9 @@ Design notes: only the fvar-structural `FVWF` is here. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr VLevel VLocalDecl) +open Ix.Theory.Named (VExpr VLocalDecl) /-! Lawfulness of `FVarId` equality (KId pattern) — here because the fvar-keyed context machinery needs it from `Trans.lean` on. -/ @@ -126,4 +128,4 @@ def FVWF : KVLCtx → Prop end KVLCtx -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf.lean b/Ix/Kernel/Verify/Whnf.lean similarity index 99% rename from Ix/Tc/Verify/Whnf.lean rename to Ix/Kernel/Verify/Whnf.lean index d4067df1a..a3113ecbe 100644 --- a/Ix/Tc/Verify/Whnf.lean +++ b/Ix/Kernel/Verify/Whnf.lean @@ -1,7 +1,9 @@ -import Ix.Tc.Verify.Ctx -import Ix.Tc.Verify.Inductive -import Ix.Tc.Verify.Run -import Ix.Tc.Verify.State +import Ix.Kernel.Verify.Ctx +import Ix.Kernel.Verify.Inductive +import Ix.Kernel.Verify.Run +import Ix.Kernel.Verify.State + +open Ix.Theory (VLevel) /-! # WHNF soundness boundary @@ -24,9 +26,9 @@ from this layer and continue through a caller-supplied fallback semantics; K2 replaces that fallback with their exact typing contracts. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) open Std (HashSet) /-! ## Concrete reduction meaning -/ @@ -216,7 +218,7 @@ def InternUpdateFrame (before after : TcState .anon) : Prop := /-- The empty projection relation satisfies every structural closure law vacuously. This is the canonical K1 fixture interpretation for fragments that contain no projection nodes. -/ -theorem RawProjRel.none_ok (env : Lean4Lean.VEnv) (uvars : Nat) : +theorem RawProjRel.none_ok (env : Ix.Theory.Named.VEnv) (uvars : Nat) : TrProjOK env uvars RawProjRel.none := by constructor <;> intros <;> contradiction @@ -1550,7 +1552,7 @@ theorem letE {trProj : RawProjRel} {world : VerifyWorld} {uvars : Nat} TrKExprS.inst_let_lbr world.venvWF.ordered theory.projections.weakN hvalCon hbody hval hbig exact ⟨bodyV, bodyV, hsource, hresult, - Lean4Lean.VEnv.IsDefEqU.refl (theory.exprWF hctx hresult)⟩ + Ix.Theory.Named.VEnv.IsDefEqU.refl (theory.exprWF hctx hresult)⟩ /-- One concrete beta step. The result is the same `substSpec` computed by the verified substitution walker; the proof uses `TrKExprS.instN` and the @@ -1561,7 +1563,7 @@ theorem beta {trProj : RawProjRel} {world : VerifyWorld} {uvars : Nat} {Δ : KVLCtx} {nm : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {ty body arg : KExpr .anon} {lamMd appMd : ExprInfo .anon} - {A bodyV argV B : VExpr} {u : Lean4Lean.VLevel} + {A bodyV argV B : VExpr} {u : Ix.Theory.VLevel} (hty : TrKExprS world.venv uvars world.nameOf trProj Δ ty A) (hbody : TrKExprS world.venv uvars world.nameOf trProj ((none, .vlam A) :: Δ) body bodyV) @@ -1579,7 +1581,7 @@ theorem beta {trProj : RawProjRel} {world : VerifyWorld} {uvars : Nat} have hsource : TrKExprS world.venv uvars world.nameOf trProj Δ (.app (.lam nm bi ty body lamMd) arg appMd) (.app (.lam A bodyV) argV) := - .app (Lean4Lean.VEnv.HasType.lam hA hbodyTy) hargTy hlam harg + .app (Ix.Theory.Named.VEnv.HasType.lam hA hbodyTy) hargTy hlam harg have hresult : TrKExprS world.venv uvars world.nameOf trProj Δ (KExpr.substSpec body arg 0) (bodyV.inst argV) := TrKExprS.instN world.venvWF.ordered projections.weakN @@ -1630,7 +1632,7 @@ instantiated left- and right-hand sides; mere membership of a raw recursor rule does not determine the production argument spine. -/ theorem registeredDefEq {trProj : RawProjRel} {world : VerifyWorld} {uvars : Nat} {Δ : KVLCtx} {source result : KExpr .anon} - {df : Lean4Lean.VDefEq} {levels : List Lean4Lean.VLevel} + {df : Ix.Theory.Named.VDefEq} {levels : List Ix.Theory.VLevel} (hsource : TrKExprS world.venv uvars world.nameOf trProj Δ source (df.lhs.instL levels)) (hresult : TrKExprS world.venv uvars world.nameOf trProj Δ result @@ -5708,7 +5710,7 @@ theorem appSpineView_collectSpine (e : KExpr m) : /-- Translation-indexed spine view. Each extension retains the exact function and argument typing derivations needed for semantic application congruence. -/ -inductive TrAppSpine (env : Lean4Lean.VEnv) (uvars : Nat) +inductive TrAppSpine (env : Ix.Theory.Named.VEnv) (uvars : Nat) (nameOf : Address → Option Lean.Name) (trProj : RawProjRel) (Δ : KVLCtx) (head : KExpr .anon) : List (KExpr .anon) → VExpr → Prop @@ -5726,7 +5728,7 @@ inductive TrAppSpine (env : Lean4Lean.VEnv) (uvars : Nat) /-- Every structural translation induces the corresponding typed spine translation; expression metadata is deliberately absent from the view. -/ theorem trAppSpine_of_tr - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Δ : KVLCtx} {e : KExpr .anon} {eV : VExpr} (h : TrKExprS env uvars nameOf trProj Δ e eV) : @@ -5757,7 +5759,7 @@ translation and typing derivation. This is the membership form needed by descriptor-driven reducers whose argument positions are discovered only at runtime. -/ theorem argument - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {head arg : KExpr .anon} {args : List (KExpr .anon)} {resultV : VExpr} @@ -5777,7 +5779,7 @@ theorem argument /-- Rebuild the canonical metadata-free raw spine without changing its Theory translation. -/ theorem tr - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Δ : KVLCtx} {head : KExpr .anon} {args : List (KExpr .anon)} {eV : VExpr} @@ -5797,7 +5799,7 @@ end TrAppSpine /-- Re-index a source translation by the head and array returned by the actual production `collectSpine`. -/ theorem trAppSpine_of_collectSpine - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Δ : KVLCtx} {source head : KExpr .anon} {args : Array (KExpr .anon)} {sourceV : VExpr} @@ -7585,7 +7587,7 @@ structure NoDeltaInputSupport (support : RunSupport) : Prop where support head ∧ ∀ (i : Nat) (hi : i < args.size), support args[i] /-- The concrete K1 input for active no-delta primitive proofs. It binds the -canonical anon table to trusted Theory names, carries Lean4Lean's primitive +canonical anon table to trusted Theory names, carries Ix.Theory.Named's primitive reflection laws, records the quotient lift equation, and scopes generated syntax to actual supported executions. This is necessary but intentionally not sufficient for `NoDeltaBaseOracle`: helper state frames and branch-level @@ -7595,7 +7597,7 @@ structure NoDeltaPrimitiveContext (world : VerifyWorld) (support : RunSupport) table : ∀ prims, prims.CanonicalAnon → NoDeltaPrimitiveTableAgrees world prims theoryPrimitives : world.venv.HasPrimitives - quotientDefEq : world.venv.defeqs Lean4Lean.quotDefEq + quotientDefEq : world.venv.defeqs Ix.Theory.Named.quotDefEq collisionFree : support.CollisionFree inputs : NoDeltaInputSupport support generated : NoDeltaGeneratedSupport support flags natSuccMode @@ -7616,7 +7618,7 @@ theorem stateTable /-- `computeNatBin` uses the fixed canonical address table. Under the production table binding, every successful arithmetic result is therefore -one of Lean4Lean's reflected primitive equations, lifted from the empty +one of Ix.Theory.Named's reflected primitive equations, lifted from the empty universe/local context to the current checker context. -/ theorem computeNatBin_defeq {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} @@ -8085,7 +8087,7 @@ theorem natPredicate_classifiers · simp /-- Reflect the concrete predicate decision selected by production into the -corresponding Lean4Lean `Nat.beq` or `Nat.ble` equation. -/ +corresponding Ix.Theory.Named `Nat.beq` or `Nat.ble` equation. -/ theorem natPredicate_defeq {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {flags : WhnfFlags} {natSuccMode : NatSuccMode} @@ -8344,7 +8346,7 @@ theorem appSameArg have hfunEq := hsourceEq.trans world.venvWF hΔ hdefeq have hfunEqTyped := hfunEq.of_l world.venvWF hΔ hsourceType have hargEq : world.venv.IsDefEqU uvars Δ.toCtx _ _ := - Lean4Lean.VEnv.IsDefEqU.refl ⟨_, hargType⟩ + Ix.Theory.Named.VEnv.IsDefEqU.refl ⟨_, hargType⟩ have hargEqTyped := hargEq.of_l world.venvWF hΔ hargType have hresultTr' : TrKExprS world.venv uvars world.nameOf trProj Δ (KExpr.mkApp result arg) (.app resultV argV) := by @@ -11877,14 +11879,14 @@ to a function type in a well-formed context. This is strictly narrower than `ExactArity` below. It says nothing about production classifiers, concrete spines, method tables, or run support, and -is the intended target for Lean4Lean's eventual canonical-type +is the intended target for Ix.Theory.Named's eventual canonical-type no-confusion theorem. -/ structure NatBoolResultShapeSeparation (world : VerifyWorld) : Prop where nat : ∀ {uvars : Nat} {Gamma : List VExpr} {A B : VExpr}, - Lean4Lean.OnCtx Gamma (world.venv.IsType uvars) → + Ix.Theory.Named.OnCtx Gamma (world.venv.IsType uvars) → ¬ world.venv.IsDefEqU uvars Gamma .nat (.forallE A B) bool : ∀ {uvars : Nat} {Gamma : List VExpr} {A B : VExpr}, - Lean4Lean.OnCtx Gamma (world.venv.IsType uvars) → + Ix.Theory.Named.OnCtx Gamma (world.venv.IsType uvars) → ¬ world.venv.IsDefEqU uvars Gamma .bool (.forallE A B) /-- A translated application suffix must be empty when its base has a @@ -12088,8 +12090,8 @@ theorem of_result_shape rw [KExpr.mkApp_shape, KExpr.mkApp_shape] exact hbaseMeaningExact have hnatType₀ : world.venv.HasType uvars [] (.natLit value) .nat := by - simpa [Lean4Lean.VLCtx.toCtx] using - (Lean4Lean.TrExprS.natLit + simpa [Ix.Theory.Named.VLCtx.toCtx] using + (Ix.Theory.Named.TrExprS.natLit (Us := List.replicate uvars Lean.Name.anonymous) (Δ := []) context.theoryPrimitives (htable.nat.contains hcatalog) value).2 have hnatType : world.venv.HasType uvars Delta.toCtx @@ -12206,8 +12208,8 @@ theorem of_result_shape exact hbaseMeaningExact have hboolType₀ : world.venv.HasType uvars [] (.boolLit decision) .bool := by - simpa [Lean4Lean.VLCtx.toCtx] using - (Lean4Lean.TrExprS.boolLit + simpa [Ix.Theory.Named.VLCtx.toCtx] using + (Ix.Theory.Named.TrExprS.boolLit (Us := List.replicate uvars Lean.Name.anonymous) (Δ := []) context.theoryPrimitives (htable.boolType.contains hcatalog) decision).2 @@ -12857,25 +12859,25 @@ theorem natSucc_hasType obtain ⟨ci, hlookup⟩ := htable.natSucc.contains hcatalog have hci := hprims.natSucc hlookup subst ci - exact Lean4Lean.VEnv.HasType.const hlookup (by simp) rfl + exact Ix.Theory.Named.VEnv.HasType.const hlookup (by simp) rfl /-- Successor reconstruction preserves the canonical Nat type. -/ theorem natSuccIterV_hasType - {env : Lean4Lean.VEnv} {uvars : Nat} {Gamma : List VExpr} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {Gamma : List VExpr} (hsucc : env.HasType uvars Gamma .natSucc (.forallE .nat .nat)) {value : VExpr} (hvalue : env.HasType uvars Gamma value .nat) (offset : Nat) : env.HasType uvars Gamma (natSuccIterV offset value) .nat := by induction offset with | zero => exact hvalue - | succ offset ih => exact Lean4Lean.VEnv.HasType.app hsucc ih + | succ offset ih => exact Ix.Theory.Named.VEnv.HasType.app hsucc ih /-- Definitional equality of the current inner Nat lifts through every successor already represented by the loop offset. -/ theorem natSuccIterV_congr - {env : Lean4Lean.VEnv} (henv : env.WF) + {env : Ix.Theory.Named.VEnv} (henv : env.WF) {uvars : Nat} {Gamma : List VExpr} - (hGamma : Lean4Lean.OnCtx Gamma (env.IsType uvars)) + (hGamma : Ix.Theory.Named.OnCtx Gamma (env.IsType uvars)) (hsucc : env.HasType uvars Gamma .natSucc (.forallE .nat .nat)) {left right : VExpr} (hleft : env.HasType uvars Gamma left .nat) @@ -12975,7 +12977,7 @@ theorem isNatSuccSpine_wf /-- Singleton inversion for the typed application-spine view. -/ theorem trAppSpine_singleton - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {head arg : KExpr .anon} {resultV : VExpr} @@ -13037,7 +13039,7 @@ theorem natSuccSpine_tr have htypes := hfun.uniqU world.venvWF hDelta.toCtx hsucc obtain ⟨⟨_, hdomain⟩, _⟩ := htypes.forallE_inv world.venvWF hDelta.toCtx - have hargNat := Lean4Lean.VEnv.HasType.defeqU_r + have hargNat := Ix.Theory.Named.VEnv.HasType.defeqU_r world.venvWF hDelta.toCtx ⟨_, hdomain⟩ harg exact ⟨curV, hcur, hargNat, rfl⟩ @@ -13681,7 +13683,7 @@ theorem tryReduceNatWithSuccMode_succ_optional_wf hI.1.core.trustedCatalog htable context.theoryPrimitives have happType : world.venv.HasType uvars Delta.toCtx (.app .natSucc argV) .nat := - Lean4Lean.VEnv.HasType.app hsucc hargType + Ix.Theory.Named.VEnv.HasType.app hsucc hargType have hsourceEq : world.venv.IsDefEqU uvars Delta.toCtx sourceV (natSuccIterV 1 argV) := by rw [hsourceV] @@ -15446,4 +15448,4 @@ theorem whnfNoDeltaImpl_noAccel_wf_of_base end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Beta/ArgumentAlignment.lean b/Ix/Kernel/Verify/Whnf/Beta/ArgumentAlignment.lean similarity index 97% rename from Ix/Tc/Verify/Whnf/Beta/ArgumentAlignment.lean rename to Ix/Kernel/Verify/Whnf/Beta/ArgumentAlignment.lean index 262fb8489..666541530 100644 --- a/Ix/Tc/Verify/Whnf/Beta/ArgumentAlignment.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/ArgumentAlignment.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Beta.InstantiationChain +import Ix.Kernel.Verify.Whnf.Beta.InstantiationChain /-! # Align concrete and Theory beta arguments @@ -9,9 +9,9 @@ the pointwise alignment once, including array/list indexing, so the one-pass translation theorem can use the exact selected argument in its variable arm. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean +open Ix.Theory.Named namespace RecM @@ -132,4 +132,4 @@ theorem ofValues end SimulArgs end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Beta/ConsumptionBoundary.lean b/Ix/Kernel/Verify/Whnf/Beta/ConsumptionBoundary.lean similarity index 87% rename from Ix/Tc/Verify/Whnf/Beta/ConsumptionBoundary.lean rename to Ix/Kernel/Verify/Whnf/Beta/ConsumptionBoundary.lean index 6355c3b21..0e3cfd9cf 100644 --- a/Ix/Tc/Verify/Whnf/Beta/ConsumptionBoundary.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/ConsumptionBoundary.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Beta.LambdaPeeling +import Ix.Kernel.Verify.Whnf.Beta.LambdaPeeling /-! # Typed splitting at the multi-beta consumption boundary @@ -9,7 +9,7 @@ position, retaining both the applications consumed by beta and every unconsumed application rebuilt afterward. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM namespace TrAppSuffix @@ -18,13 +18,13 @@ definitional equality while retaining the suffix as a `TrAppSuffix`. Unlike `rebase`, this form is intended for a second structural transformation of the replacement prefix before the original trailing arguments are reattached. -/ theorem rebaseStart - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address -> Option Lean.Name} {trProj : RawProjRel} - {Delta : KVLCtx} {start : Lean4Lean.VExpr} - {args : List (KExpr .anon)} {resultV : Lean4Lean.VExpr} + {Delta : KVLCtx} {start : Ix.Theory.Named.VExpr} + {args : List (KExpr .anon)} {resultV : Ix.Theory.Named.VExpr} (h : TrAppSuffix env uvars nameOf trProj Delta start args resultV) (henv : env.WF) (hDelta : KVLCtx.WF env uvars Delta) - {replacementV : Lean4Lean.VExpr} + {replacementV : Ix.Theory.Named.VExpr} (hreplacement : env.IsDefEqU uvars Delta.toCtx start replacementV) : exists resultV', TrAppSuffix env uvars nameOf trProj Delta replacementV args resultV' /\ @@ -41,15 +41,15 @@ theorem rebaseStart hcurrentEq.of_l henv hDelta.toCtx hfun exact ⟨.app currentV' argV, .app hcurrentSuffix hcurrentType harg hargTr, - (Lean4Lean.VEnv.IsDefEq.appDF hcurrentEqAt harg).toU⟩ + (Ix.Theory.Named.VEnv.IsDefEq.appDF hcurrentEqAt harg).toU⟩ /-- Split a typed application suffix after exactly `n` arguments. Both pieces retain their original typing derivations and production order. -/ theorem splitAt - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address -> Option Lean.Name} {trProj : RawProjRel} - {Delta : KVLCtx} {start : Lean4Lean.VExpr} - {args : List (KExpr .anon)} {resultV : Lean4Lean.VExpr} + {Delta : KVLCtx} {start : Ix.Theory.Named.VExpr} + {args : List (KExpr .anon)} {resultV : Ix.Theory.Named.VExpr} (h : TrAppSuffix env uvars nameOf trProj Delta start args resultV) (n : Nat) (hn : n <= args.length) : exists middleV, @@ -84,9 +84,9 @@ theorem splitAt arguments; the second contains exactly the `Array.extract` rebuilt by `finishAppResult`. -/ theorem splitConsume - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address -> Option Lean.Name} {trProj : RawProjRel} - {Delta : KVLCtx} {startV resultV : Lean4Lean.VExpr} + {Delta : KVLCtx} {startV resultV : Ix.Theory.Named.VExpr} {start body : KExpr .anon} {args consumed : Array (KExpr .anon)} (hconsume : consumeBetaLams start args = (body, consumed)) (h : TrAppSuffix env uvars nameOf trProj Delta startV args.toList @@ -108,4 +108,4 @@ theorem splitConsume end TrAppSuffix end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Beta/DependentContexts.lean b/Ix/Kernel/Verify/Whnf/Beta/DependentContexts.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Beta/DependentContexts.lean rename to Ix/Kernel/Verify/Whnf/Beta/DependentContexts.lean index 1041a688a..ce78965bd 100644 --- a/Ix/Tc/Verify/Whnf/Beta/DependentContexts.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/DependentContexts.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Beta.PrefixSemantics +import Ix.Kernel.Verify.Whnf.Beta.PrefixSemantics /-! # Dependent context chains for simultaneous beta @@ -10,7 +10,7 @@ syntax binder and transports the abstract projection relation through every Theory instantiation. -/ -namespace Lean4Lean.VLocalDecl +namespace Ix.Theory.Named.VLocalDecl /-- Instantiate a declaration by outer-to-inner beta arguments. -/ def instBetaArgs (d : VLocalDecl) : List VExpr → (depth : Nat) → VLocalDecl @@ -52,11 +52,11 @@ theorem instBetaArgs_depth (d : VLocalDecl) (args : List VExpr) rw [instBetaArgs, VLocalDecl.inst, VExpr.instBetaArgs, VExpr.instBetaArgs, ih] -end Lean4Lean.VLocalDecl +end Ix.Theory.Named.VLocalDecl -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean +open Ix.Theory.Named namespace KVLCtx @@ -279,7 +279,7 @@ theorem projection (htpI : ∀ {Γ₀ : List VExpr} {e₀ A₀ : VExpr} {position : Nat} {Γ₁ Γ : List VExpr} {s : Lean.Name} {i : Nat} {e e' : VExpr}, env.HasType uvars Γ₀ e₀ A₀ → - Lean4Lean.Ctx.InstN Γ₀ e₀ A₀ position Γ₁ Γ → + Ix.Theory.Named.Ctx.InstN Γ₀ e₀ A₀ position Γ₁ Γ → trProj uvars Γ₁ s i e e' → trProj uvars Γ s i (e.inst e₀ position) (e'.inst e₀ position)) {structName : Lean.Name} {field : Nat} {value result : VExpr} @@ -295,4 +295,4 @@ theorem projection end KInsts end KVLCtx -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Beta/InstantiationChain.lean b/Ix/Kernel/Verify/Whnf/Beta/InstantiationChain.lean similarity index 95% rename from Ix/Tc/Verify/Whnf/Beta/InstantiationChain.lean rename to Ix/Kernel/Verify/Whnf/Beta/InstantiationChain.lean index 3cf8cf957..740287607 100644 --- a/Ix/Tc/Verify/Whnf/Beta/InstantiationChain.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/InstantiationChain.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Beta.DependentContexts +import Ix.Kernel.Verify.Whnf.Beta.DependentContexts /-! # The peeled telescope induces a dependent instantiation chain @@ -10,9 +10,9 @@ context chain itself follows from the recovered lambda declarations and the exact Theory argument values. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean +open Ix.Theory.Named namespace RecM.BetaPeel.Tr @@ -52,7 +52,7 @@ theorem contextInsts hfun'.lam_inv world.venvWF.ordered hDelta.toCtx have hlam' : world.venv.HasType uvars Delta.toCtx (.lam A' bodyV') (.forallE A' B') := - Lean4Lean.VEnv.HasType.lam hA' hbodyV' + Ix.Theory.Named.VEnv.HasType.lam hA' hbodyV' have hforallEq : world.venv.IsDefEqU uvars Delta.toCtx (.forallE domain codomain) (.forallE A' B') := hfun'.uniqU world.venvWF hDelta.toCtx hlam' @@ -75,4 +75,4 @@ theorem contextInsts exact hlifted'.append hfinal end RecM.BetaPeel.Tr -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Beta/LambdaInstantiation.lean b/Ix/Kernel/Verify/Whnf/Beta/LambdaInstantiation.lean similarity index 83% rename from Ix/Tc/Verify/Whnf/Beta/LambdaInstantiation.lean rename to Ix/Kernel/Verify/Whnf/Beta/LambdaInstantiation.lean index ebc32c44c..754e5b526 100644 --- a/Ix/Tc/Verify/Whnf/Beta/LambdaInstantiation.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/LambdaInstantiation.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Beta.SimultaneousSubstitution +import Ix.Kernel.Verify.Whnf.Beta.SimultaneousSubstitution /-! # Walker-tight lambda instantiation @@ -10,57 +10,57 @@ This slice replays the same structural proof using `TrKExprS.weakBV_lbr`, so the theorem consumes precisely the final bound carried by `WalkerRequest`. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean +open Ix.Theory.Named private theorem instNatLit_bx (v : Nat) (e₀ : VExpr) (k : Nat) : - (Lean4Lean.VExpr.natLit v).inst e₀ k = Lean4Lean.VExpr.natLit v := by + (Ix.Theory.Named.VExpr.natLit v).inst e₀ k = Ix.Theory.Named.VExpr.natLit v := by induction v with | zero => rfl | succ v ih => - show Lean4Lean.VExpr.app _ _ = _ - rw [show ((Lean4Lean.VExpr.natLit v).inst e₀ k) = - Lean4Lean.VExpr.natLit v from ih] + show Ix.Theory.Named.VExpr.app _ _ = _ + rw [show ((Ix.Theory.Named.VExpr.natLit v).inst e₀ k) = + Ix.Theory.Named.VExpr.natLit v from ih] rfl private theorem instListCharLit_bx (s : List Char) (e₀ : VExpr) (k : Nat) : - (Lean4Lean.VExpr.listCharLit s).inst e₀ k = - Lean4Lean.VExpr.listCharLit s := by + (Ix.Theory.Named.VExpr.listCharLit s).inst e₀ k = + Ix.Theory.Named.VExpr.listCharLit s := by induction s with | nil => rfl | cons c s ih => - show Lean4Lean.VExpr.app (Lean4Lean.VExpr.app _ (Lean4Lean.VExpr.app _ - ((Lean4Lean.VExpr.natLit c.toNat).inst e₀ k))) - ((Lean4Lean.VExpr.listCharLit s).inst e₀ k) = _ + show Ix.Theory.Named.VExpr.app (Ix.Theory.Named.VExpr.app _ (Ix.Theory.Named.VExpr.app _ + ((Ix.Theory.Named.VExpr.natLit c.toNat).inst e₀ k))) + ((Ix.Theory.Named.VExpr.listCharLit s).inst e₀ k) = _ rw [instNatLit_bx, ih] rfl private theorem instTrLiteral_bx (l : Lean.Literal) (e₀ : VExpr) (k : Nat) : - (Lean4Lean.VExpr.trLiteral l).inst e₀ k = - Lean4Lean.VExpr.trLiteral l := by + (Ix.Theory.Named.VExpr.trLiteral l).inst e₀ k = + Ix.Theory.Named.VExpr.trLiteral l := by cases l with | natVal v => exact instNatLit_bx v e₀ k | strVal s => - show Lean4Lean.VExpr.app _ - ((Lean4Lean.VExpr.listCharLit _).inst e₀ k) = _ + show Ix.Theory.Named.VExpr.app _ + ((Ix.Theory.Named.VExpr.listCharLit _).inst e₀ k) = _ rw [instListCharLit_bx] rfl /-- `substSpec` tracks Theory instantiation under the exact loose-binder bound carried by a substitution walker request. -/ -theorem TrKExprS.instN_lbr {env : Lean4Lean.VEnv} {uvars : Nat} +theorem TrKExprS.instN_lbr {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop} (henv : env.Ordered) (htp : ∀ {Γ Γ' : List VExpr} {n k : Nat} {s : Lean.Name} {i : Nat} - {e e' : VExpr}, Lean4Lean.Ctx.LiftN n k Γ Γ' → + {e e' : VExpr}, Ix.Theory.Named.Ctx.LiftN n k Γ Γ' → trProj uvars Γ s i e e' → trProj uvars Γ' s i (e.liftN n k) (e'.liftN n k)) (htpI : ∀ {Γ₀ : List VExpr} {e₀ A₀ : VExpr} {k : Nat} {Γ₁ Γ : List VExpr} {s : Lean.Name} {i : Nat} {e e' : VExpr}, env.HasType uvars Γ₀ e₀ A₀ → - Lean4Lean.Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ → + Ix.Theory.Named.Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ → trProj uvars Γ₁ s i e e' → trProj uvars Γ s i (e.inst e₀ k) (e'.inst e₀ k)) {Δ₀ : KVLCtx} {arg : KExpr .anon} {e₀' A₀ : VExpr} @@ -167,14 +167,14 @@ theorem TrKExprS.instN_lbr {env : Lean4Lean.VEnv} {uvars : Nat} exact .prj h1 (ihval W hdepth (by omega)) (htpI t₀ W.toCtx htrp) | @nat Δ₁' v blob md h => intro Δ dk k depth W hdepth hbig - rw [show (Lean4Lean.VExpr.natLit v).inst e₀' k = - Lean4Lean.VExpr.natLit v from instNatLit_bx v e₀' k] + rw [show (Ix.Theory.Named.VExpr.natLit v).inst e₀' k = + Ix.Theory.Named.VExpr.natLit v from instNatLit_bx v e₀' k] exact .nat h | @str Δ₁' s blob md h => intro Δ dk k depth W hdepth hbig - rw [show (Lean4Lean.VExpr.trLiteral (.strVal s)).inst e₀' k = - Lean4Lean.VExpr.trLiteral (.strVal s) from + rw [show (Ix.Theory.Named.VExpr.trLiteral (.strVal s)).inst e₀' k = + Ix.Theory.Named.VExpr.trLiteral (.strVal s) from instTrLiteral_bx (.strVal s) e₀' k] exact .str h -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Beta/LambdaPeeling.lean b/Ix/Kernel/Verify/Whnf/Beta/LambdaPeeling.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Beta/LambdaPeeling.lean rename to Ix/Kernel/Verify/Whnf/Beta/LambdaPeeling.lean index edeed4615..0d70cf0f8 100644 --- a/Ix/Tc/Verify/Whnf/Beta/LambdaPeeling.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/LambdaPeeling.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Structural.StepAssembly +import Ix.Kernel.Verify.Whnf.Structural.StepAssembly /-! # Certified lambda peeling for general beta @@ -9,7 +9,7 @@ spine was consumed. This slice gives the loop a structural certificate and proves that the returned array is exactly a prefix of the input arguments. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- A sequence of lambda bodies reached by consuming arguments in production @@ -122,4 +122,4 @@ theorem consumed_append_remaining end BetaPeel end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Beta/LiftSubstitution.lean b/Ix/Kernel/Verify/Whnf/Beta/LiftSubstitution.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/Beta/LiftSubstitution.lean rename to Ix/Kernel/Verify/Whnf/Beta/LiftSubstitution.lean index 722f2e9f3..616c017fd 100644 --- a/Ix/Tc/Verify/Whnf/Beta/LiftSubstitution.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/LiftSubstitution.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Beta.SingletonSubstitution +import Ix.Kernel.Verify.Whnf.Beta.SingletonSubstitution /-! # Lift/substitution cancellation for multi-beta @@ -9,7 +9,7 @@ remove precisely the added lift. This file proves that pure syntactic law with the same no-wrap discipline as the production walkers. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace KExpr private theorem toNat_max_bv (a b : UInt64) : @@ -225,4 +225,4 @@ theorem substSpec_liftSpec_succ simpa only [UInt64.add_zero] using h end KExpr -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Beta/Meaning.lean b/Ix/Kernel/Verify/Whnf/Beta/Meaning.lean similarity index 94% rename from Ix/Tc/Verify/Whnf/Beta/Meaning.lean rename to Ix/Kernel/Verify/Whnf/Beta/Meaning.lean index c5c25ddeb..9ac67798a 100644 --- a/Ix/Tc/Verify/Whnf/Beta/Meaning.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/Meaning.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Beta.Translation +import Ix.Kernel.Verify.Whnf.Beta.Translation /-! # Constructive multi-beta meaning @@ -10,9 +10,9 @@ file assembles those pieces into `BetaPrefixMeaning`, eliminating the last semantic oracle specific to general multi-beta. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean +open Ix.Theory.Named namespace RecM @@ -42,4 +42,4 @@ theorem betaManyMeaning (trProj : RawProjRel) (world : VerifyWorld) : BetaManyMeaningOracle.of_prefix (betaPrefixMeaning trProj world) end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Beta/PeelTrace.lean b/Ix/Kernel/Verify/Whnf/Beta/PeelTrace.lean similarity index 81% rename from Ix/Tc/Verify/Whnf/Beta/PeelTrace.lean rename to Ix/Kernel/Verify/Whnf/Beta/PeelTrace.lean index 472647ada..029464ff5 100644 --- a/Ix/Tc/Verify/Whnf/Beta/PeelTrace.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/PeelTrace.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Beta.SemanticCore +import Ix.Kernel.Verify.Whnf.Beta.SemanticCore /-! # Translated lambda-peel traces @@ -10,7 +10,7 @@ body. Subsequent simultaneous-instantiation proofs can therefore reason from the actual binder stack rather than only from the number of consumed terms. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM namespace BetaPeel @@ -19,10 +19,10 @@ namespace BetaPeel The final context is the original `Delta` extended by one `vlam` entry per consumed argument, in the same innermost-first order used by de Bruijn indices. -/ -inductive Tr (env : Lean4Lean.VEnv) (uvars : Nat) +inductive Tr (env : Ix.Theory.Named.VEnv) (uvars : Nat) (nameOf : Address -> Option Lean.Name) (trProj : RawProjRel) - (Delta : KVLCtx) (start : KExpr .anon) (startV : Lean4Lean.VExpr) : - List (KExpr .anon) -> KExpr .anon -> KVLCtx -> Lean4Lean.VExpr -> Prop + (Delta : KVLCtx) (start : KExpr .anon) (startV : Ix.Theory.Named.VExpr) : + List (KExpr .anon) -> KExpr .anon -> KVLCtx -> Ix.Theory.Named.VExpr -> Prop | nil (hstart : TrKExprS env uvars nameOf trProj Delta start startV) : Tr env uvars nameOf trProj Delta start startV [] start Delta startV @@ -30,7 +30,7 @@ inductive Tr (env : Lean4Lean.VEnv) (uvars : Nat) {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {ty body : KExpr .anon} {info : ExprInfo .anon} {arg : KExpr .anon} {currentDelta : KVLCtx} - {A bodyV : Lean4Lean.VExpr} + {A bodyV : Ix.Theory.Named.VExpr} (hprefix : Tr env uvars nameOf trProj Delta start startV consumed (.lam name bi ty body info) currentDelta (.lam A bodyV)) (hA : env.IsType uvars currentDelta.toCtx A) @@ -45,11 +45,11 @@ namespace Tr /-- The final concrete body in a translated peel trace has the structural translation stored at the trace endpoint. -/ theorem result - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address -> Option Lean.Name} {trProj : RawProjRel} - {Delta : KVLCtx} {start : KExpr .anon} {startV : Lean4Lean.VExpr} + {Delta : KVLCtx} {start : KExpr .anon} {startV : Ix.Theory.Named.VExpr} {consumed : List (KExpr .anon)} {body : KExpr .anon} - {bodyDelta : KVLCtx} {bodyV : Lean4Lean.VExpr} + {bodyDelta : KVLCtx} {bodyV : Ix.Theory.Named.VExpr} (h : Tr env uvars nameOf trProj Delta start startV consumed body bodyDelta bodyV) : TrKExprS env uvars nameOf trProj bodyDelta body bodyV := by @@ -60,11 +60,11 @@ theorem result /-- Every consumed lambda contributes exactly one Theory binder to the final mixed context. -/ theorem bvars - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address -> Option Lean.Name} {trProj : RawProjRel} - {Delta : KVLCtx} {start : KExpr .anon} {startV : Lean4Lean.VExpr} + {Delta : KVLCtx} {start : KExpr .anon} {startV : Ix.Theory.Named.VExpr} {consumed : List (KExpr .anon)} {body : KExpr .anon} - {bodyDelta : KVLCtx} {bodyV : Lean4Lean.VExpr} + {bodyDelta : KVLCtx} {bodyV : Ix.Theory.Named.VExpr} (h : Tr env uvars nameOf trProj Delta start startV consumed body bodyDelta bodyV) : bodyDelta.bvars = Delta.bvars + consumed.length := by @@ -80,10 +80,10 @@ end Tr translated peel trace and an exact structural translation of the final raw body under the recovered binder context. -/ theorem translate - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address -> Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {start body : KExpr .anon} - {consumed : List (KExpr .anon)} {startV : Lean4Lean.VExpr} + {consumed : List (KExpr .anon)} {startV : Ix.Theory.Named.VExpr} (hpeel : BetaPeel start consumed body) (hstart : TrKExprS env uvars nameOf trProj Delta start startV) : exists bodyDelta bodyV, @@ -100,4 +100,4 @@ theorem translate end BetaPeel end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Beta/PrefixSemantics.lean b/Ix/Kernel/Verify/Whnf/Beta/PrefixSemantics.lean similarity index 95% rename from Ix/Tc/Verify/Whnf/Beta/PrefixSemantics.lean rename to Ix/Kernel/Verify/Whnf/Beta/PrefixSemantics.lean index 5bbfb809a..98fdcabae 100644 --- a/Ix/Tc/Verify/Whnf/Beta/PrefixSemantics.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/PrefixSemantics.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Whnf.Beta.LambdaInstantiation +import Ix.Kernel.Verify.Whnf.Beta.LambdaInstantiation + +open Ix.Theory (VLevel) /-! # Theory semantics of a peeled beta prefix @@ -11,13 +13,13 @@ separate one-pass theorem so it need not pretend that sequential intermediate terms satisfy production's size bound. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean +open Ix.Theory.Named -end Ix.Tc +end Ix.Kernel -namespace Lean4Lean.VExpr +namespace Ix.Theory.Named.VExpr /-- Instantiate outer-to-inner beta arguments. The first argument removes the outermost remaining binder; the final argument removes the binder at @@ -124,17 +126,17 @@ theorem instBetaArgs_liftN (e : VExpr) (args : List VExpr) (depth : Nat) : (depth + args.length) + 1 := by omega rw [hamount, inst_liftN_total, ih] -end Lean4Lean.VExpr +end Ix.Theory.Named.VExpr -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean +open Ix.Theory.Named namespace RecM.TrAppSuffix /-- A typed suffix together with the exact Theory argument values in the same production order as its concrete arguments. -/ -inductive Values (env : Lean4Lean.VEnv) (uvars : Nat) +inductive Values (env : Ix.Theory.Named.VEnv) (uvars : Nat) (nameOf : Address → Option Lean.Name) (trProj : RawProjRel) (Delta : KVLCtx) (start : VExpr) : List (KExpr .anon) → List VExpr → VExpr → Prop @@ -152,7 +154,7 @@ namespace Values /-- Every typed suffix exposes its exact Theory argument list. -/ theorem ofSuffix - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {start : VExpr} {args : List (KExpr .anon)} {resultV : VExpr} @@ -168,7 +170,7 @@ theorem ofSuffix /-- The empty concrete suffix has no Theory arguments and leaves its start expression unchanged. -/ theorem nil_inv - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {start resultV : VExpr} {argValues : List VExpr} (h : Values env uvars nameOf trProj Delta start [] argValues resultV) : @@ -180,7 +182,7 @@ theorem nil_inv /-- Exact last-argument view, retaining the Theory-value list. -/ theorem unsnoc - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {start : VExpr} {args : List (KExpr .anon)} {arg : KExpr .anon} @@ -207,7 +209,7 @@ end Values /-- Exact last-argument view of a typed suffix. -/ theorem unsnoc - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {start : VExpr} {args : List (KExpr .anon)} {arg : KExpr .anon} {resultV : VExpr} @@ -263,7 +265,7 @@ theorem theoryMeaning induction h generalizing argValues appliedV with | nil hstart => obtain ⟨rfl, rfl⟩ := happs.nil_inv - exact Lean4Lean.VEnv.IsDefEqU.refl + exact Ix.Theory.Named.VEnv.IsDefEqU.refl (hstart.wf world.venvWF.ordered theory.literalWF theory.projections.wf hDelta) | @snoc consumed name bi ty body info arg currentDelta A bodyV hprefix @@ -281,7 +283,7 @@ theorem theoryMeaning hfun'.lam_inv world.venvWF.ordered hDelta.toCtx have hlam' : world.venv.HasType uvars Delta.toCtx (.lam A' bodyV') (.forallE A' B') := - Lean4Lean.VEnv.HasType.lam hA' hbodyV' + Ix.Theory.Named.VEnv.HasType.lam hA' hbodyV' have hforallEq : world.venv.IsDefEqU uvars Delta.toCtx (.forallE domain codomain) (.forallE A' B') := hfun'.uniqU world.venvWF hDelta.toCtx hlam' @@ -293,7 +295,7 @@ theorem theoryMeaning harg.defeqU_r world.venvWF hDelta.toCtx hdomainEq have happCong : world.venv.IsDefEqU uvars Delta.toCtx (.app currentV argV) (.app (.lam A' bodyV') argV) := - (Lean4Lean.VEnv.IsDefEq.appDF + (Ix.Theory.Named.VEnv.IsDefEq.appDF (hprefixEq.of_l world.venvWF hDelta.toCtx hfun) harg).toU have hbeta : world.venv.IsDefEqU uvars Delta.toCtx (.app (.lam A' bodyV') argV) (bodyV'.inst argV) := @@ -303,4 +305,4 @@ theorem theoryMeaning end RecM.BetaPeel.Tr -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Beta/SemanticCore.lean b/Ix/Kernel/Verify/Whnf/Beta/SemanticCore.lean similarity index 95% rename from Ix/Tc/Verify/Whnf/Beta/SemanticCore.lean rename to Ix/Kernel/Verify/Whnf/Beta/SemanticCore.lean index e9523bb74..344d60d98 100644 --- a/Ix/Tc/Verify/Whnf/Beta/SemanticCore.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/SemanticCore.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Beta.ConsumptionBoundary +import Ix.Kernel.Verify.Whnf.Beta.ConsumptionBoundary /-! # Isolate the semantic core of general multi-beta @@ -11,7 +11,7 @@ substitution theorem, leaving `BetaPrefixMeaning` as the minimal semantic statement that must be proved structurally. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Semantic core of production multi-beta. Starting from a translated @@ -22,7 +22,7 @@ def BetaPrefixMeaning (trProj : RawProjRel) (world : VerifyWorld) : Prop := forall {uvars : Nat}, WhnfTheory trProj world uvars -> forall {Delta : KVLCtx} {start body : KExpr .anon} {consumed : Array (KExpr .anon)} - {startV consumedV : Lean4Lean.VExpr}, + {startV consumedV : Ix.Theory.Named.VExpr}, KVLCtx.WF world.venv uvars Delta -> TrKExprS world.venv uvars world.nameOf trProj Delta start startV -> BetaPeel start consumed.toList body -> @@ -63,4 +63,4 @@ theorem of_prefix {trProj : RawProjRel} {world : VerifyWorld} end BetaManyMeaningOracle end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Beta/SimultaneousSubstitution.lean b/Ix/Kernel/Verify/Whnf/Beta/SimultaneousSubstitution.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/Beta/SimultaneousSubstitution.lean rename to Ix/Kernel/Verify/Whnf/Beta/SimultaneousSubstitution.lean index b4f6ce3e5..ee51c3db5 100644 --- a/Ix/Tc/Verify/Whnf/Beta/SimultaneousSubstitution.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/SimultaneousSubstitution.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Beta.LiftSubstitution +import Ix.Kernel.Verify.Whnf.Beta.LiftSubstitution /-! # Simultaneous-substitution decomposition @@ -9,7 +9,7 @@ result is exactly the older simultaneous substitution one binder deeper, followed by ordinary beta substitution for the newly peeled argument. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace KExpr private theorem toNat_toUInt64_bw (k : Nat) : @@ -691,4 +691,4 @@ theorem simulSubstSpec_cons | str => rfl end KExpr -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Beta/SingletonSubstitution.lean b/Ix/Kernel/Verify/Whnf/Beta/SingletonSubstitution.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Beta/SingletonSubstitution.lean rename to Ix/Kernel/Verify/Whnf/Beta/SingletonSubstitution.lean index d621c0169..3129e2d37 100644 --- a/Ix/Tc/Verify/Whnf/Beta/SingletonSubstitution.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/SingletonSubstitution.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Beta.PeelTrace +import Ix.Kernel.Verify.Whnf.Beta.PeelTrace /-! # Singleton simultaneous substitution @@ -9,7 +9,7 @@ single substitution as a premise. This slice proves that equality uniformly from the same no-wrap bound required by the walker. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace KExpr /-- A one-element simultaneous substitution is exactly the ordinary single @@ -118,4 +118,4 @@ theorem simulSubstSpec_singleton | str => rfl end KExpr -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Beta/Translation.lean b/Ix/Kernel/Verify/Whnf/Beta/Translation.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Beta/Translation.lean rename to Ix/Kernel/Verify/Whnf/Beta/Translation.lean index 4604d391c..87bcc3835 100644 --- a/Ix/Tc/Verify/Whnf/Beta/Translation.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/Translation.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Beta.ArgumentAlignment +import Ix.Kernel.Verify.Whnf.Beta.ArgumentAlignment /-! # One-pass translation of simultaneous beta substitution @@ -9,9 +9,9 @@ theorems for variables. No sequential concrete intermediate is constructed, so the proof consumes exactly production's original `WalkerRequest.Bounds`. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean +open Ix.Theory.Named private theorem toNat_toUInt64_cc (value : Nat) : value.toUInt64.toNat = value % UInt64.size := by @@ -61,13 +61,13 @@ theorem TrKExprS.simulSubstBeta {trProj : RawProjRel} (henv : env.Ordered) (htp : ∀ {Γ Γ' : List VExpr} {n k : Nat} {s : Lean.Name} {i : Nat} - {e e' : VExpr}, Lean4Lean.Ctx.LiftN n k Γ Γ' → + {e e' : VExpr}, Ix.Theory.Named.Ctx.LiftN n k Γ Γ' → trProj uvars Γ s i e e' → trProj uvars Γ' s i (e.liftN n k) (e'.liftN n k)) (htpI : ∀ {Γ₀ : List VExpr} {e₀ A₀ : VExpr} {position : Nat} {Γ₁ Γ : List VExpr} {s : Lean.Name} {i : Nat} {e e' : VExpr}, env.HasType uvars Γ₀ e₀ A₀ → - Lean4Lean.Ctx.InstN Γ₀ e₀ A₀ position Γ₁ Γ → + Ix.Theory.Named.Ctx.InstN Γ₀ e₀ A₀ position Γ₁ Γ → trProj uvars Γ₁ s i e e' → trProj uvars Γ s i (e.inst e₀ position) (e'.inst e₀ position)) {base : KVLCtx} {substs : Array (KExpr .anon)} @@ -354,4 +354,4 @@ theorem TrKExprS.simulSubstBeta rw [instBetaArgs_trLiteral_cc] exact .str hlit -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Closure.lean b/Ix/Kernel/Verify/Whnf/Closure.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Closure.lean rename to Ix/Kernel/Verify/Whnf/Closure.lean index b9d9b284c..6a4acd990 100644 --- a/Ix/Tc/Verify/Whnf/Closure.lean +++ b/Ix/Kernel/Verify/Whnf/Closure.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Whnf.Delta.OptionalReduction -import Ix.Tc.Verify.Knot +import Ix.Kernel.Verify.Whnf.Delta.OptionalReduction +import Ix.Kernel.Verify.Knot /-! # Four-field fixed-universe WHNF closure @@ -40,7 +40,7 @@ concrete successful, absent, stuck, and partial-error executions in substitute for K2's two missing recursive fields. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- The final K1 cache composition at the universe count encoded by `keys`: WHNF expression entries outside, universe-sensitive delta bodies underneath, @@ -140,4 +140,4 @@ theorem closedAt end K1ClosureContext end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Delta/CacheExecution.lean b/Ix/Kernel/Verify/Whnf/Delta/CacheExecution.lean similarity index 96% rename from Ix/Tc/Verify/Whnf/Delta/CacheExecution.lean rename to Ix/Kernel/Verify/Whnf/Delta/CacheExecution.lean index ddb379603..71e63f63f 100644 --- a/Ix/Tc/Verify/Whnf/Delta/CacheExecution.lean +++ b/Ix/Kernel/Verify/Whnf/Delta/CacheExecution.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Delta.StableCache +import Ix.Kernel.Verify.Whnf.Delta.StableCache /-! # Certified production unfold-cache execution @@ -16,7 +16,7 @@ physical paths of production's `unfoldConstValue`: No arbitrary head/result write authority is used. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace CacheProvenance @@ -69,14 +69,14 @@ theorem unfoldConstValue_trusted_wf (theory : StableWhnfTheory trProj world keys.uvars) (hreferences : TrustedReferences world support) {id : KId .anon} {concrete : KConst .anon} - {ci : Lean4Lean.VDefVal} {kind : Ix.DefKind} + {ci : Ix.Theory.Named.VDefVal} {kind : Ix.DefKind} {lvls : UInt64} {body : KExpr .anon} (trusted : TrustedDeltaBody trProj world id concrete ci kind lvls body) {us : Array (KUniv .anon)} {info : ExprInfo .anon} (resources : DeltaInstantiationResources us body) (hheadSupport : support (.const id us info)) (hrequest : WalkerRequest.instUniv body us ∈ requests) - {Delta : KVLCtx} {headV : Lean4Lean.VExpr} + {Delta : KVLCtx} {headV : Ix.Theory.Named.VExpr} (hhead : TrKExprS world.venv keys.uvars world.nameOf trProj Delta (.const id us info) headV) {s : TcState .anon} : @@ -140,4 +140,4 @@ theorem unfoldConstValue_trusted_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Delta/CacheSemantics.lean b/Ix/Kernel/Verify/Whnf/Delta/CacheSemantics.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Delta/CacheSemantics.lean rename to Ix/Kernel/Verify/Whnf/Delta/CacheSemantics.lean index 8f721d12c..f679c9e00 100644 --- a/Ix/Tc/Verify/Whnf/Delta/CacheSemantics.lean +++ b/Ix/Kernel/Verify/Whnf/Delta/CacheSemantics.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Delta.Integration +import Ix.Kernel.Verify.Whnf.Delta.Integration /-! # Semantic unfold-cache entries @@ -14,7 +14,7 @@ universe count is fixed, matching the `Methods.WFAt` contract used by the recursive reducer. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Exact fixed-universe validity for one unfold-cache entry. Every finite-support source sharing the stored address must reduce to the cached @@ -126,4 +126,4 @@ theorem unfoldOfMeaning {uvars : Nat} {trProj : RawProjRel} end CacheProvenance -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Delta/ClosedTranslation.lean b/Ix/Kernel/Verify/Whnf/Delta/ClosedTranslation.lean similarity index 80% rename from Ix/Tc/Verify/Whnf/Delta/ClosedTranslation.lean rename to Ix/Kernel/Verify/Whnf/Delta/ClosedTranslation.lean index a63aa8a38..cb0b3c6cc 100644 --- a/Ix/Tc/Verify/Whnf/Delta/ClosedTranslation.lean +++ b/Ix/Kernel/Verify/Whnf/Delta/ClosedTranslation.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Delta.CacheSemantics +import Ix.Kernel.Verify.Whnf.Delta.CacheSemantics /-! # Closed-expression translation under caller contexts @@ -15,9 +15,9 @@ cases compositional and exposes exactly where projection weakening and closedness are used. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr VEnv) +open Ix.Theory.Named (VExpr VEnv) namespace KVLCtx @@ -93,24 +93,24 @@ theorem weakRight {env : VEnv} {uvars : Nat} exact .const hname hlookup hlevels harity | @app inner f arg info f' arg' A B hfunTy hargTy hfun harg ihfun iharg => - have hclosed : Lean4Lean.CtxClosed inner.toCtx := - Lean4Lean.VEnv.CtxWF.closed henv hinner.toCtx + have hclosed : Ix.Theory.Named.CtxClosed inner.toCtx := + Ix.Theory.Named.VEnv.CtxWF.closed henv hinner.toCtx refine .app (A := A) (B := B) ?_ ?_ (ihfun hinner outer) (iharg hinner outer) - · simpa only [KVLCtx.toCtx_appendOuter, Lean4Lean.VEnv.HasType] using + · simpa only [KVLCtx.toCtx_appendOuter, Ix.Theory.Named.VEnv.HasType] using hfunTy.weakR henv hclosed outer.toCtx - · simpa only [KVLCtx.toCtx_appendOuter, Lean4Lean.VEnv.HasType] using + · simpa only [KVLCtx.toCtx_appendOuter, Ix.Theory.Named.VEnv.HasType] using hargTy.weakR henv hclosed outer.toCtx | @lam inner name bi ty body info ty' body' hty htyTr hbodyTr ihty ihbody => - have hclosed : Lean4Lean.CtxClosed inner.toCtx := - Lean4Lean.VEnv.CtxWF.closed henv hinner.toCtx + have hclosed : Ix.Theory.Named.CtxClosed inner.toCtx := + Ix.Theory.Named.VEnv.CtxWF.closed henv hinner.toCtx have htyOriginal := hty obtain ⟨level, htyHasType⟩ := hty have hty' : env.IsType uvars (KVLCtx.appendOuter inner outer).toCtx ty' := by refine ⟨level, ?_⟩ - simpa only [KVLCtx.toCtx_appendOuter, Lean4Lean.VEnv.HasType] using + simpa only [KVLCtx.toCtx_appendOuter, Ix.Theory.Named.VEnv.HasType] using htyHasType.weakR henv hclosed outer.toCtx have hbodyInner : KVLCtx.WF env uvars ((none, .vlam ty') :: inner) := @@ -118,40 +118,40 @@ theorem weakRight {env : VEnv} {uvars : Nat} exact .lam hty' (ihty hinner outer) (ihbody hbodyInner outer) | @all inner name bi ty body info ty' body' hty hbodyTy htyTr hbodyTr ihty ihbody => - have hclosed : Lean4Lean.CtxClosed inner.toCtx := - Lean4Lean.VEnv.CtxWF.closed henv hinner.toCtx + have hclosed : Ix.Theory.Named.CtxClosed inner.toCtx := + Ix.Theory.Named.VEnv.CtxWF.closed henv hinner.toCtx have htyOriginal := hty obtain ⟨level, htyHasType⟩ := hty have hty' : env.IsType uvars (KVLCtx.appendOuter inner outer).toCtx ty' := by refine ⟨level, ?_⟩ - simpa only [KVLCtx.toCtx_appendOuter, Lean4Lean.VEnv.HasType] using + simpa only [KVLCtx.toCtx_appendOuter, Ix.Theory.Named.VEnv.HasType] using htyHasType.weakR henv hclosed outer.toCtx have hbodyInner : KVLCtx.WF env uvars ((none, .vlam ty') :: inner) := ⟨hinner, nofun, htyOriginal⟩ have hbodyClosed : - Lean4Lean.CtxClosed - (KVLCtx.toCtx ((none, Lean4Lean.VLocalDecl.vlam ty') :: inner)) := - Lean4Lean.VEnv.CtxWF.closed henv hbodyInner.toCtx + Ix.Theory.Named.CtxClosed + (KVLCtx.toCtx ((none, Ix.Theory.Named.VLocalDecl.vlam ty') :: inner)) := + Ix.Theory.Named.VEnv.CtxWF.closed henv hbodyInner.toCtx obtain ⟨bodyLevel, hbodyHasType⟩ := hbodyTy have hbodyTy' : env.IsType uvars (KVLCtx.appendOuter - ((none, Lean4Lean.VLocalDecl.vlam ty') :: inner) outer).toCtx + ((none, Ix.Theory.Named.VLocalDecl.vlam ty') :: inner) outer).toCtx body' := by refine ⟨bodyLevel, ?_⟩ - simpa only [KVLCtx.toCtx_appendOuter, Lean4Lean.VEnv.HasType] using + simpa only [KVLCtx.toCtx_appendOuter, Ix.Theory.Named.VEnv.HasType] using hbodyHasType.weakR henv hbodyClosed outer.toCtx exact .all hty' hbodyTy' (ihty hinner outer) (ihbody hbodyInner outer) | @letE inner name ty val body nondep info ty' val' body' hvalTy htyTr hvalTr hbodyTr ihty ihval ihbody => - have hclosed : Lean4Lean.CtxClosed inner.toCtx := - Lean4Lean.VEnv.CtxWF.closed henv hinner.toCtx + have hclosed : Ix.Theory.Named.CtxClosed inner.toCtx := + Ix.Theory.Named.VEnv.CtxWF.closed henv hinner.toCtx have hvalTy' : env.HasType uvars (KVLCtx.appendOuter inner outer).toCtx val' ty' := by - simpa only [KVLCtx.toCtx_appendOuter, Lean4Lean.VEnv.HasType] using + simpa only [KVLCtx.toCtx_appendOuter, Ix.Theory.Named.VEnv.HasType] using hvalTy.weakR henv hclosed outer.toCtx have hbodyInner : KVLCtx.WF env uvars ((none, .vlet ty' val') :: inner) := @@ -160,8 +160,8 @@ theorem weakRight {env : VEnv} {uvars : Nat} (ihbody hbodyInner outer) | @prj inner sid field val info structName val' result' hname hvalTr hproj ihval => - have hclosed : Lean4Lean.CtxClosed inner.toCtx := - Lean4Lean.VEnv.CtxWF.closed henv hinner.toCtx + have hclosed : Ix.Theory.Named.CtxClosed inner.toCtx := + Ix.Theory.Named.VEnv.CtxWF.closed henv hinner.toCtx have hvalWF : VExpr.WF env uvars inner.toCtx val' := hvalTr.wf henv hlit htp.wf hinner have hresultWF : VExpr.WF env uvars inner.toCtx result' := @@ -171,9 +171,9 @@ theorem weakRight {env : VEnv} {uvars : Nat} have hresultClosed : result'.ClosedN inner.toCtx.length := hresultWF.closedN henv hclosed have hlift : - Lean4Lean.Ctx.LiftN outer.toCtx.length inner.toCtx.length + Ix.Theory.Named.Ctx.LiftN outer.toCtx.length inner.toCtx.length inner.toCtx (inner.toCtx ++ outer.toCtx) := - Lean4Lean.Ctx.LiftN.right hclosed outer.toCtx + Ix.Theory.Named.Ctx.LiftN.right hclosed outer.toCtx have hproj' := htp.weakN hlift hproj have hproj'' : trProj uvars (KVLCtx.appendOuter inner outer).toCtx structName @@ -206,13 +206,13 @@ theorem weakRight {env : VEnv} {uvars : Nat} (outer : KVLCtx) : TrKExpr env uvars nameOf trProj (KVLCtx.appendOuter inner outer) e e' := by obtain ⟨structural, hstructural, targetTy, htarget⟩ := H - have hclosed : Lean4Lean.CtxClosed inner.toCtx := - Lean4Lean.VEnv.CtxWF.closed henv hinner.toCtx + have hclosed : Ix.Theory.Named.CtxClosed inner.toCtx := + Ix.Theory.Named.VEnv.CtxWF.closed henv hinner.toCtx refine ⟨structural, hstructural.weakRight henv hlit htp hinner outer, targetTy, ?_⟩ - simpa only [KVLCtx.toCtx_appendOuter, Lean4Lean.VEnv.HasType] using + simpa only [KVLCtx.toCtx_appendOuter, Ix.Theory.Named.VEnv.HasType] using htarget.weakR henv hclosed outer.toCtx end TrKExpr -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Delta/Integration.lean b/Ix/Kernel/Verify/Whnf/Delta/Integration.lean similarity index 97% rename from Ix/Tc/Verify/Whnf/Delta/Integration.lean rename to Ix/Kernel/Verify/Whnf/Delta/Integration.lean index 82f13c2e0..b8af069d3 100644 --- a/Ix/Tc/Verify/Whnf/Delta/Integration.lean +++ b/Ix/Kernel/Verify/Whnf/Delta/Integration.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Delta.UnfoldingState +import Ix.Kernel.Verify.Whnf.Delta.UnfoldingState /-! # Package delta and the fourth public WHNF field @@ -15,7 +15,7 @@ theorem below upgrades Reducer's arbitrary-flags `WhnfMeaning` result to the `WhnfPost` shape required by `Methods.WhnfLayerWF`. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Complete run-scoped authority for production delta unfolding. The @@ -93,7 +93,7 @@ theorem publicFlags_wf (context : StructuralCoreContext initial program requests keys fallback trProj world support Delta flags) {source : KExpr .anon} (hsourceSupport : support source) - {sourceV : Lean4Lean.VExpr} {s : TcState .anon} + {sourceV : Ix.Theory.Named.VExpr} {s : TcState .anon} (hsource : TrKExprS world.venv keys.uvars world.nameOf trProj Delta source sourceV) : RecM.WF .noAccel @@ -116,4 +116,4 @@ theorem publicFlags_wf end StructuralCoreContext end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Delta/OptionalReduction.lean b/Ix/Kernel/Verify/Whnf/Delta/OptionalReduction.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Delta/OptionalReduction.lean rename to Ix/Kernel/Verify/Whnf/Delta/OptionalReduction.lean index 15232910c..e45372c99 100644 --- a/Ix/Tc/Verify/Whnf/Delta/OptionalReduction.lean +++ b/Ix/Kernel/Verify/Whnf/Delta/OptionalReduction.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Delta.SpineUnfolding +import Ix.Kernel.Verify.Whnf.Delta.SpineUnfolding /-! # Exact delta optional-reducer closure @@ -9,7 +9,7 @@ the same trusted declaration census and packages the complete reducer in the fixed-universe optional contract consumed by the full-WHNF step. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Local run-equation strengthening used to connect a successful lazy @@ -54,7 +54,7 @@ theorem deltaUnfoldOne_trusted_wf (unfoldCacheSemantics keys.uvars trProj fallback)) trProj world support keys.uvars Delta)) (hreferences : TrustedReferences world support) - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} {s : TcState .anon} (hsourceSupport : support source) (hsource : TrKExprS world.venv keys.uvars world.nameOf trProj Delta @@ -184,4 +184,4 @@ def FullWhnfStepContext.ofTrustedDelta delta := delta.wfAt end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Delta/SpineUnfolding.lean b/Ix/Kernel/Verify/Whnf/Delta/SpineUnfolding.lean similarity index 96% rename from Ix/Tc/Verify/Whnf/Delta/SpineUnfolding.lean rename to Ix/Kernel/Verify/Whnf/Delta/SpineUnfolding.lean index f3793bb80..c11904702 100644 --- a/Ix/Tc/Verify/Whnf/Delta/SpineUnfolding.lean +++ b/Ix/Kernel/Verify/Whnf/Delta/SpineUnfolding.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Delta.CacheExecution +import Ix.Kernel.Verify.Whnf.Delta.CacheExecution /-! # Trusted spine-aware delta unfolding @@ -11,7 +11,7 @@ the typed spine to prove that rebuilding preserves the complete source meaning. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Run-scoped trusted resolution and resource bounds for every supported reducible constant head that delta unfolding may reach. @@ -29,7 +29,7 @@ structure TrustedDeltaCensus (trProj : RawProjRel) (world : VerifyWorld) DeltaBodyShape kind lvls body concrete → world.catalog id = some concrete → kind ≠ .opaq → - ∃ ci : Lean4Lean.VDefVal, + ∃ ci : Ix.Theory.Named.VDefVal, TrustedDeltaBody trProj world id concrete ci kind lvls body ∧ DeltaInstantiationResources us body @@ -42,7 +42,7 @@ theorem toPost {trProj : RawProjRel} {world : VerifyWorld} {uvars : Nat} (theory : WhnfTheory trProj world uvars) {Delta : KVLCtx} (hDelta : KVLCtx.WF world.venv uvars Delta) - {source result : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source result : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsource : TrKExprS world.venv uvars world.nameOf trProj Delta source sourceV) (h : WhnfMeaning trProj world uvars Delta source result) : @@ -112,7 +112,7 @@ theorem tryDeltaUnfold_trusted_wf (unfoldCacheSemantics keys.uvars trProj fallback)) trProj world support keys.uvars Delta)) (hreferences : TrustedReferences world support) - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} {s : TcState .anon} (hsourceSupport : support source) (hsource : TrKExprS world.venv keys.uvars world.nameOf trProj Delta @@ -190,4 +190,4 @@ theorem tryDeltaUnfold_trusted_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Delta/StableCache.lean b/Ix/Kernel/Verify/Whnf/Delta/StableCache.lean similarity index 94% rename from Ix/Tc/Verify/Whnf/Delta/StableCache.lean rename to Ix/Kernel/Verify/Whnf/Delta/StableCache.lean index 33f925cb7..14c01710b 100644 --- a/Ix/Tc/Verify/Whnf/Delta/StableCache.lean +++ b/Ix/Kernel/Verify/Whnf/Delta/StableCache.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Whnf.Delta.TrustedBody +import Ix.Kernel.Verify.Whnf.Delta.TrustedBody + +open Ix.Theory (VLevel) /-! # Stable trusted delta-cache provenance @@ -21,7 +23,7 @@ request: obligations at every permitted extension. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Theory closure at one universe count for every extension of the world in which a stable cache entry may be interpreted. -/ @@ -73,11 +75,11 @@ constant found at the same source node. -/ theorem sourceInputs {trProj : RawProjRel} {world : VerifyWorld} {id : KId .anon} {concrete : KConst .anon} - {ci : Lean4Lean.VDefVal} {kind : Ix.DefKind} + {ci : Ix.Theory.Named.VDefVal} {kind : Ix.DefKind} {lvls : UInt64} {body : KExpr .anon} (h : TrustedDeltaBody trProj world id concrete ci kind lvls body) {uvars : Nat} {us : Array (KUniv .anon)} {info : ExprInfo .anon} - {Delta : KVLCtx} {sourceV : Lean4Lean.VExpr} + {Delta : KVLCtx} {sourceV : Ix.Theory.Named.VExpr} (hsource : TrKExprS world.venv uvars world.nameOf trProj Delta (.const id us info) sourceV) : sourceV = @@ -97,7 +99,7 @@ by the stable cache authority. -/ theorem futureMeaning {trProj : RawProjRel} {world : VerifyWorld} {id : KId .anon} {concrete : KConst .anon} - {ci : Lean4Lean.VDefVal} {kind : Ix.DefKind} + {ci : Ix.Theory.Named.VDefVal} {kind : Ix.DefKind} {lvls : UInt64} {body : KExpr .anon} (h : TrustedDeltaBody trProj world id concrete ci kind lvls body) {uvars : Nat} (theory : StableWhnfTheory trProj world uvars) @@ -119,7 +121,7 @@ for `UnfoldCacheWriteOracle.write`. -/ theorem unfoldCacheProvenance {trProj : RawProjRel} {world : VerifyWorld} {id : KId .anon} {concrete : KConst .anon} - {ci : Lean4Lean.VDefVal} {kind : Ix.DefKind} + {ci : Ix.Theory.Named.VDefVal} {kind : Ix.DefKind} {lvls : UInt64} {body : KExpr .anon} (h : TrustedDeltaBody trProj world id concrete ci kind lvls body) {uvars : Nat} {fallback : CacheSemantics} @@ -144,4 +146,4 @@ theorem unfoldCacheProvenance end TrustedDeltaBody -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Delta/TrustedBody.lean b/Ix/Kernel/Verify/Whnf/Delta/TrustedBody.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Delta/TrustedBody.lean rename to Ix/Kernel/Verify/Whnf/Delta/TrustedBody.lean index 723072d15..b42339ef3 100644 --- a/Ix/Tc/Verify/Whnf/Delta/TrustedBody.lean +++ b/Ix/Kernel/Verify/Whnf/Delta/TrustedBody.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Whnf.Delta.UniverseMonotonicity +import Ix.Kernel.Verify.Whnf.Delta.UniverseMonotonicity + +open Ix.Theory (VLevel) /-! # Exact trusted delta-body semantics @@ -26,9 +28,9 @@ body unchanged. ClosedTranslation then weakens the closed body translation into caller's arbitrary mixed context. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VDefVal VEnv VExpr VLevel) +open Ix.Theory.Named (VDefVal VEnv VExpr) /-- The exact definition-shaped catalog fields relevant to delta unfolding. Keeping this as an indexed proposition lets a certificate retain the complete @@ -283,4 +285,4 @@ theorem meaning end TrustedDeltaBody -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Delta/UnfoldingState.lean b/Ix/Kernel/Verify/Whnf/Delta/UnfoldingState.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/Delta/UnfoldingState.lean rename to Ix/Kernel/Verify/Whnf/Delta/UnfoldingState.lean index c7b7538c1..fa330f0f4 100644 --- a/Ix/Tc/Verify/Whnf/Delta/UnfoldingState.lean +++ b/Ix/Kernel/Verify/Whnf/Delta/UnfoldingState.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Driver.PublicReducers +import Ix.Kernel.Verify.Whnf.Driver.PublicReducers /-! # Delta unfolding state and support closure @@ -17,7 +17,7 @@ reflection field: a loaded definition-shaped catalog entry is not by itself evidence that its body is the trusted definition installed in `VerifyWorld`. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Collision-robust provenance for the universe-instantiated definition body cached under the concrete constant-head address. -/ @@ -63,7 +63,7 @@ structure DeltaUnfoldReflection (semantics : CacheSemantics) (support : RunSupport) : Prop where success : ∀ {uvars : Nat} {Delta : KVLCtx} {methods : Methods .anon} {source result : KExpr .anon} - {sourceV : Lean4Lean.VExpr} {s sf : TcState .anon}, + {sourceV : Ix.Theory.Named.VExpr} {s sf : TcState .anon}, Methods.WFAt .noAccel semantics trProj world support uvars methods → support source → TrKExprS world.venv uvars world.nameOf trProj Delta source sourceV → @@ -350,4 +350,4 @@ theorem deltaUnfoldOne_optional_wf_of_contexts reflection.success hmethods hsourceSupport hsource hI hrunDelta⟩ end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Delta/UniverseMonotonicity.lean b/Ix/Kernel/Verify/Whnf/Delta/UniverseMonotonicity.lean similarity index 97% rename from Ix/Tc/Verify/Whnf/Delta/UniverseMonotonicity.lean rename to Ix/Kernel/Verify/Whnf/Delta/UniverseMonotonicity.lean index 0578a29f9..5341b9b64 100644 --- a/Ix/Tc/Verify/Whnf/Delta/UniverseMonotonicity.lean +++ b/Ix/Kernel/Verify/Whnf/Delta/UniverseMonotonicity.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Whnf.Delta.ClosedTranslation +import Ix.Kernel.Verify.Whnf.Delta.ClosedTranslation + +open Ix.Theory (VLevel) /-! # Universe-count monotonicity for structural translation @@ -15,9 +17,9 @@ Structural translation follows by induction, carrying the source mixed context's well-formedness through binder cases. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (OnCtx VEnv VExpr VLevel) +open Ix.Theory.Named (OnCtx VEnv VExpr) /-- A well-formed universe level remains well-formed when the available parameter count grows. -/ @@ -159,4 +161,4 @@ theorem monoU {env : VEnv} {before after : Nat} end TrKExprS -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Driver/FullStep.lean b/Ix/Kernel/Verify/Whnf/Driver/FullStep.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/Driver/FullStep.lean rename to Ix/Kernel/Verify/Whnf/Driver/FullStep.lean index a8745f155..0dc97ef09 100644 --- a/Ix/Tc/Verify/Whnf/Driver/FullStep.lean +++ b/Ix/Kernel/Verify/Whnf/Driver/FullStep.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.NoDelta.Reducer +import Ix.Kernel.Verify.Whnf.NoDelta.Reducer /-! # Full-WHNF one-step closure @@ -14,7 +14,7 @@ boundary: unlike a miss, successful unfolding must justify both support for the generated expression and its Theory meaning. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- The Decidable acceleration gate satisfies the optional-reducer contract @@ -203,4 +203,4 @@ theorem wf end FullWhnfStepContext end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Driver/PublicReducers.lean b/Ix/Kernel/Verify/Whnf/Driver/PublicReducers.lean similarity index 94% rename from Ix/Tc/Verify/Whnf/Driver/PublicReducers.lean rename to Ix/Kernel/Verify/Whnf/Driver/PublicReducers.lean index 220d51446..6755b23fe 100644 --- a/Ix/Tc/Verify/Whnf/Driver/PublicReducers.lean +++ b/Ix/Kernel/Verify/Whnf/Driver/PublicReducers.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Driver.FullStep +import Ix.Kernel.Verify.Whnf.Driver.FullStep /-! # Public full-WHNF reducers @@ -10,7 +10,7 @@ instrumentation, cache hits and writes, and both Nat successor policies. This slice supplies FullStep's concrete step to those theorems. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM namespace FullWhnfStepContext @@ -26,7 +26,7 @@ theorem publicMode_wf trProj world support Delta) (natSuccMode : NatSuccMode) {source : KExpr .anon} (hsourceSupport : support source) - {sourceV : Lean4Lean.VExpr} {s : TcState .anon} + {sourceV : Ix.Theory.Named.VExpr} {s : TcState .anon} (hsource : TrKExprS world.venv keys.uvars world.nameOf trProj Delta source sourceV) : RecM.WF .noAccel @@ -56,7 +56,7 @@ theorem publicWhnf_wf (context : FullWhnfStepContext initial program requests keys fallback trProj world support Delta) {source : KExpr .anon} (hsourceSupport : support source) - {sourceV : Lean4Lean.VExpr} {s : TcState .anon} + {sourceV : Ix.Theory.Named.VExpr} {s : TcState .anon} (hsource : TrKExprS world.venv keys.uvars world.nameOf trProj Delta source sourceV) : RecM.WF .noAccel @@ -77,7 +77,7 @@ theorem publicCore_wf (context : FullWhnfStepContext initial program requests keys fallback trProj world support Delta) {source : KExpr .anon} (hsourceSupport : support source) - {sourceV : Lean4Lean.VExpr} {s : TcState .anon} + {sourceV : Ix.Theory.Named.VExpr} {s : TcState .anon} (hsource : TrKExprS world.venv keys.uvars world.nameOf trProj Delta source sourceV) : RecM.WF .noAccel @@ -100,4 +100,4 @@ theorem publicCore_wf end FullWhnfStepContext end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/ApplicationRequests.lean b/Ix/Kernel/Verify/Whnf/Iota/ApplicationRequests.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Iota/ApplicationRequests.lean rename to Ix/Kernel/Verify/Whnf/Iota/ApplicationRequests.lean index ec61b5aa8..95ba62c0f 100644 --- a/Ix/Tc/Verify/Whnf/Iota/ApplicationRequests.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/ApplicationRequests.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Iota.NatOffset +import Ix.Kernel.Verify.Whnf.Iota.NatOffset /-! # Finite request closure for ordinary iota application @@ -16,7 +16,7 @@ The census is indexed by the exact three production argument slices. Thus a certificate for a convenient argument order cannot justify the real helper. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Exact non-transient intern requests for one left-to-right iota argument @@ -59,7 +59,7 @@ theorem wfList rw [List.foldlM_cons, ReaderT.run_bind] apply TcM.WF.bind (Q₁ := fun next _ => next = KExpr.mkApp result arg) - · rw [Ix.Tc.RecM.applyIotaArg_false, ReaderT.run_monadLift] + · rw [Ix.Kernel.RecM.applyIotaArg_false, ReaderT.run_monadLift] exact TcM.WF.mono (TcM.intern_whnf_wf hrun.collisionFree (hrun.coverage.internExpr request)) @@ -267,4 +267,4 @@ theorem of_requests end TryApplyIotaCtorPreserves end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/ArgumentBranches.lean b/Ix/Kernel/Verify/Whnf/Iota/ArgumentBranches.lean similarity index 95% rename from Ix/Tc/Verify/Whnf/Iota/ArgumentBranches.lean rename to Ix/Kernel/Verify/Whnf/Iota/ArgumentBranches.lean index 390f1e5ff..efe7f4bc6 100644 --- a/Ix/Tc/Verify/Whnf/Iota/ArgumentBranches.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/ArgumentBranches.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Iota.Substitution +import Ix.Kernel.Verify.Whnf.Iota.Substitution /-! # Remaining production iota-argument branches @@ -15,7 +15,7 @@ contracts are the branch-local inputs needed for the subsequent proof of the three production application loops. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Every expression shape that bypasses transient beta in `applyIotaArg`. Unlike `WhnfCoreNonLambda`, this includes `app`: an intermediate recursor RHS @@ -63,7 +63,7 @@ theorem appRebuild {trProj : RawProjRel} {world : VerifyWorld} {uvars : Nat} {Delta : KVLCtx} {result arg : KExpr .anon} {sourceInfo : ExprInfo .anon} - {resultV argV A B : Lean4Lean.VExpr} + {resultV argV A B : Ix.Theory.Named.VExpr} (hresultTy : world.venv.HasType uvars Delta.toCtx resultV (.forallE A B)) (hargTy : world.venv.HasType uvars Delta.toCtx argV A) @@ -80,8 +80,8 @@ theorem appRebuild rw [KExpr.mkApp_shape] exact .app hresultTy hargTy hresultTr hargTr exact ⟨_, _, hsource, hrebuilt, - Lean4Lean.VEnv.IsDefEqU.refl - ⟨_, Lean4Lean.VEnv.HasType.app hresultTy hargTy⟩⟩ + Ix.Theory.Named.VEnv.IsDefEqU.refl + ⟨_, Ix.Theory.Named.VEnv.HasType.app hresultTy hargTy⟩⟩ end WhnfMeaning @@ -95,7 +95,7 @@ theorem applyIotaArg_true_nonlam_semantic {sourceInfo : ExprInfo .anon} (hnonlam : IotaArgNonLambda result) (methods : Methods .anon) (s : TcState .anon) - {resultV argV A B : Lean4Lean.VExpr} + {resultV argV A B : Ix.Theory.Named.VExpr} (hresultTy : world.venv.HasType uvars Delta.toCtx resultV (.forallE A B)) (hargTy : world.venv.HasType uvars Delta.toCtx argV A) @@ -129,7 +129,7 @@ theorem applyIotaArg_false_eval obtain ⟨s', hintern, hI', hframe⟩ := TcM.intern_whnf_eval hcollision hsupport hI refine ⟨s', ?_, hI', hframe⟩ - rw [Ix.Tc.RecM.applyIotaArg_false] + rw [Ix.Kernel.RecM.applyIotaArg_false] exact hintern /-- Full non-transient per-argument contract: execution is intern-only, the @@ -144,7 +144,7 @@ theorem applyIotaArg_false_semantic (hsupport : support (KExpr.mkApp result arg)) (hI : WhnfStateInv layer semantics trProj world support uvars Delta s) (methods : Methods .anon) - {resultV argV A B : Lean4Lean.VExpr} + {resultV argV A B : Ix.Theory.Named.VExpr} (hresultTy : world.venv.HasType uvars Delta.toCtx resultV (.forallE A B)) (hargTy : world.venv.HasType uvars Delta.toCtx argV A) @@ -165,4 +165,4 @@ theorem applyIotaArg_false_semantic end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/ArgumentExecution.lean b/Ix/Kernel/Verify/Whnf/Iota/ArgumentExecution.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Iota/ArgumentExecution.lean rename to Ix/Kernel/Verify/Whnf/Iota/ArgumentExecution.lean index bdfc4f840..236a8d200 100644 --- a/Ix/Tc/Verify/Whnf/Iota/ArgumentExecution.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/ArgumentExecution.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Whnf.Iota.ArgumentBranches +import Ix.Kernel.Verify.Whnf.Iota.ArgumentBranches + +open Ix.Theory (VLevel) /-! # Semantic execution of iota argument lists @@ -17,9 +19,9 @@ finite support, and `WhnfMeaning`; induction then transports the quotient translation through every intermediate. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace KExpr @@ -289,7 +291,7 @@ theorem transientLambdaSingletonQuot {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {ty body arg : KExpr .anon} {info : ExprInfo .anon} {expectedV expectedA expectedB A bodyV argV B : VExpr} - {univ : Lean4Lean.VLevel} + {univ : Ix.Theory.VLevel} {s : TcState .anon} (hexpectedTy : world.venv.HasType uvars Delta.toCtx expectedV (.forallE expectedA expectedB)) @@ -313,7 +315,7 @@ theorem transientLambdaSingletonQuot have hrun : (applyIotaArg (.lam name bi ty body info) arg true).run methods s = .ok (substNoIntern body arg 0) s := by - rw [Ix.Tc.RecM.applyIotaArg_true_lam] + rw [Ix.Kernel.RecM.applyIotaArg_true_lam] rfl have hmeaning := WhnfMeaning.betaNoIntern (trProj := trProj) (world := world) (uvars := uvars) (Delta := Delta) @@ -337,7 +339,7 @@ theorem transientLambdaSingleton {uvars : Nat} {Delta : KVLCtx} {methods : Methods .anon} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {ty body arg : KExpr .anon} {info : ExprInfo .anon} - {A bodyV argV B : VExpr} {univ : Lean4Lean.VLevel} + {A bodyV argV B : VExpr} {univ : Ix.Theory.VLevel} {s : TcState .anon} (projections : TrProjOK world.venv uvars trProj) (hty : TrKExprS world.venv uvars world.nameOf trProj Delta ty A) @@ -356,7 +358,7 @@ theorem transientLambdaSingleton methods true (.lam name bi ty body info) (.lam A bodyV) s [arg] (substNoIntern body arg 0) (.app (.lam A bodyV) argV) s := transientLambdaSingletonQuot - (Lean4Lean.VEnv.HasType.lam hA hbodyTy) hargTy projections + (Ix.Theory.Named.VEnv.HasType.lam hA hbodyTy) hargTy projections hty hbody harg hA hbodyTy hargTy hbodyCon hargCon hbig hI hresultSupport @@ -613,4 +615,4 @@ end ApplyIotaArgsTrace end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/ConstructorDispatch.lean b/Ix/Kernel/Verify/Whnf/Iota/ConstructorDispatch.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Iota/ConstructorDispatch.lean rename to Ix/Kernel/Verify/Whnf/Iota/ConstructorDispatch.lean index da8d98de5..b281ccd9a 100644 --- a/Ix/Tc/Verify/Whnf/Iota/ConstructorDispatch.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/ConstructorDispatch.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Whnf.Iota.SelectedRule +import Ix.Kernel.Verify.Whnf.Iota.SelectedRule + +open Ix.Theory (VLevel) /-! # Ordinary-constructor iota dispatch @@ -15,9 +17,9 @@ and String-literal expansion. Those remain separate exhaustive branches; the regular theorem cannot silently justify any of them. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VDefEq VExpr) +open Ix.Theory.Named (VDefEq VExpr) namespace KConst @@ -236,12 +238,12 @@ theorem checkedAcceptance_empty (hsourceTr : TrKExprS world.venv 0 world.nameOf trProj [] source sourceV) (hsourceType : world.venv.HasType 0 [] sourceV sourceType) - {levels : List Lean4Lean.VLevel} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr} - (hmatch : Lean4Lean.Pattern.Matches + (hmatch : Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -299,12 +301,12 @@ theorem checkedAcceptance_nonempty (hsourceTr : TrKExprS world.venv uvars world.nameOf trProj [] source sourceV) (hsourceType : world.venv.HasType uvars [] sourceV sourceType) - {levels : List Lean4Lean.VLevel} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr} - (hmatch : Lean4Lean.Pattern.Matches + (hmatch : Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -559,12 +561,12 @@ theorem tryIotaWithFlags_regularCtor_checkedAcceptance_empty (hsourceTr : TrKExprS world.venv 0 world.nameOf trProj [] source sourceV) (hsourceType : world.venv.HasType 0 [] sourceV sourceType) - {levels : List Lean4Lean.VLevel} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr} - (hmatch : Lean4Lean.Pattern.Matches + (hmatch : Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -597,4 +599,4 @@ theorem tryIotaWithFlags_regularCtor_checkedAcceptance_empty end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/ConstructorSynthesis.lean b/Ix/Kernel/Verify/Whnf/Iota/ConstructorSynthesis.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/Iota/ConstructorSynthesis.lean rename to Ix/Kernel/Verify/Whnf/Iota/ConstructorSynthesis.lean index 9bcf46184..a6827e46e 100644 --- a/Ix/Tc/Verify/Whnf/Iota/ConstructorSynthesis.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/ConstructorSynthesis.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Whnf.Iota.StringLiteral +import Ix.Kernel.Verify.Whnf.Iota.StringLiteral + +open Ix.Theory (VLevel) /-! # Successful K-like constructor synthesis @@ -15,9 +17,9 @@ flag: malformed or untrusted catalog entries may legitimately make any of the caught stages return `none`, and callback errors may retain partial state. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VDefEq VExpr) +open Ix.Theory.Named (VDefEq VExpr) namespace RecM @@ -538,12 +540,12 @@ theorem tryIotaWithFlags_kCtor_checkedAcceptance_empty {sourceV sourceType : VExpr} (hsourceTr : TrKExprS world.venv 0 world.nameOf trProj [] source sourceV) (hsourceType : world.venv.HasType 0 [] sourceV sourceType) - {levels : List Lean4Lean.VLevel} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr} - (hmatch : Lean4Lean.Pattern.Matches + (hmatch : Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -575,4 +577,4 @@ theorem tryIotaWithFlags_kCtor_checkedAcceptance_empty end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/ConstructorSynthesisFallback.lean b/Ix/Kernel/Verify/Whnf/Iota/ConstructorSynthesisFallback.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/Iota/ConstructorSynthesisFallback.lean rename to Ix/Kernel/Verify/Whnf/Iota/ConstructorSynthesisFallback.lean index 1bc62a92a..3b5a0f0f1 100644 --- a/Ix/Tc/Verify/Whnf/Iota/ConstructorSynthesisFallback.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/ConstructorSynthesisFallback.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Iota.ConstructorSynthesis +import Ix.Kernel.Verify.Whnf.Iota.ConstructorSynthesis /-! # Exhaustive K-synthesis fallback branches @@ -16,7 +16,7 @@ malformed-inductive cases (for example an empty constructor array) from the defensive repeated-lookup cases without assuming catalog immutability. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -683,4 +683,4 @@ end SynthCtorWhenKSelectionTrace end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/Ingress.lean b/Ix/Kernel/Verify/Whnf/Iota/Ingress.lean similarity index 97% rename from Ix/Tc/Verify/Whnf/Iota/Ingress.lean rename to Ix/Kernel/Verify/Whnf/Iota/Ingress.lean index b2b209bb7..df8217d32 100644 --- a/Ix/Tc/Verify/Whnf/Iota/Ingress.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/Ingress.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Iota.SynthesisRequests +import Ix.Kernel.Verify.Whnf.Iota.SynthesisRequests /-! # Actual iota ingress state closure @@ -16,7 +16,7 @@ from finite run censuses; the remaining callback premises are confined to bounded helper scans over open declaration telescopes. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Exhaustive state closure of the actual iota reducer. -/ @@ -46,7 +46,7 @@ theorem tryIotaWithFlags_state_wf_of_contexts (hreferences : TrustedReferences world support) (hwrites : ∀ id, world.trusted id → IsRecCacheWriteOracle semantics world support methods id) - (e : KExpr .anon) {sourceV : Lean4Lean.VExpr} + (e : KExpr .anon) {sourceV : Ix.Theory.Named.VExpr} (hsourceSupport : support e) (hsource : TrKExprS world.venv uvars world.nameOf trProj Delta e sourceV) (flags : WhnfFlags) (s : TcState .anon) : @@ -58,12 +58,12 @@ theorem tryIotaWithFlags_state_wf_of_contexts WhnfStateInv .noAccel semantics trProj world support uvars Delta have hpost : ∀ (recId : KId .anon) (recr : IotaInfo .anon) (recUs : Array (KUniv .anon)) (spine : Array (KExpr .anon)) - (major : KExpr .anon) {majorV : Lean4Lean.VExpr} + (major : KExpr .anon) {majorV : Ix.Theory.Named.VExpr} (after : TcState .anon), support major → TrKExprS world.venv uvars world.nameOf trProj Delta major majorV → support spine[recr.majorIdx]! → - ∀ {spineMajorV : Lean4Lean.VExpr}, + ∀ {spineMajorV : Ix.Theory.Named.VExpr}, TrKExprS world.venv uvars world.nameOf trProj Delta spine[recr.majorIdx]! spineMajorV → TcM.WF I after @@ -186,4 +186,4 @@ theorem tryIotaWithFlags_state_wf_of_contexts exact TcM.WF.pure (fun _ => trivial) end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/NatLiteral.lean b/Ix/Kernel/Verify/Whnf/Iota/NatLiteral.lean similarity index 97% rename from Ix/Tc/Verify/Whnf/Iota/NatLiteral.lean rename to Ix/Kernel/Verify/Whnf/Iota/NatLiteral.lean index 0dba7dc2c..10645e160 100644 --- a/Ix/Tc/Verify/Whnf/Iota/NatLiteral.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/NatLiteral.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Whnf.Iota.ConstructorDispatch +import Ix.Kernel.Verify.Whnf.Iota.ConstructorDispatch + +open Ix.Theory (VLevel) /-! # Nat-literal iota preprocessing @@ -14,9 +16,9 @@ WHNF callback after constructing the String spine. K synthesis and struct eta likewise retain their own inference and recursive-WHNF obligations. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VDefEq VExpr) +open Ix.Theory.Named (VDefEq VExpr) namespace RecM @@ -193,12 +195,12 @@ theorem tryIotaWithFlags_natCtor_checkedAcceptance_empty (hsourceTr : TrKExprS world.venv 0 world.nameOf trProj [] source sourceV) (hsourceType : world.venv.HasType 0 [] sourceV sourceType) - {levels : List Lean4Lean.VLevel} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr} - (hmatch : Lean4Lean.Pattern.Matches + (hmatch : Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -231,4 +233,4 @@ theorem tryIotaWithFlags_natCtor_checkedAcceptance_empty end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/NatOffset.lean b/Ix/Kernel/Verify/Whnf/Iota/NatOffset.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/Iota/NatOffset.lean rename to Ix/Kernel/Verify/Whnf/Iota/NatOffset.lean index 69a561423..fb5b60aca 100644 --- a/Ix/Tc/Verify/Whnf/Iota/NatOffset.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/NatOffset.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Runtime.LazyIngress +import Ix.Kernel.Verify.Whnf.Runtime.LazyIngress /-! # State closure for iota's Nat-offset preprocessing @@ -13,7 +13,7 @@ that fact for every input and every invariant, then closes the complete cleanup helper without leaving it as an iota runtime premise. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM set_option maxHeartbeats 800000 @@ -235,7 +235,7 @@ structure NatOffsetCleanupInputOracle (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) : Prop where generated : ∀ {uvars : Nat} {Delta : KVLCtx} {methods : Methods .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} {before after : TcState .anon} {result : KExpr .anon}, support source → TrKExprS world.venv uvars world.nameOf trProj Delta source sourceV → @@ -297,7 +297,7 @@ theorem cleanupNatOffsetMajor_input_wf {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {methods : Methods .anon} (inputs : NatOffsetCleanupInputOracle trProj world support) - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsourceSupport : support source) (hsource : TrKExprS world.venv uvars world.nameOf trProj Delta source sourceV) @@ -638,4 +638,4 @@ theorem tryIotaAfterMajorWhnf_state_wf | some cleanedMajor => exact hfinish cleanedMajor false afterCleanup end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/NatPatternMatching.lean b/Ix/Kernel/Verify/Whnf/Iota/NatPatternMatching.lean similarity index 86% rename from Ix/Tc/Verify/Whnf/Iota/NatPatternMatching.lean rename to Ix/Kernel/Verify/Whnf/Iota/NatPatternMatching.lean index a083c7404..b73b6e318 100644 --- a/Ix/Tc/Verify/Whnf/Iota/NatPatternMatching.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/NatPatternMatching.lean @@ -1,11 +1,13 @@ -import Ix.Tc.Verify.Whnf.Iota.NatRecognizer +import Ix.Kernel.Verify.Whnf.Iota.NatRecognizer + +open Ix.Theory (VLevel) /-! # Constructive Nat-iota pattern matching NatRecognizer identifies the exact recursor rule and the literal-major position used by the linear Nat recognizer. This slice crosses the next semantic boundary: -it constructs Lean4Lean's dependent `Pattern.Matches` capture map from exact +it constructs Ix.Theory.Named's dependent `Pattern.Matches` capture map from exact constant-spine shapes. The bridge deliberately ends at the application through the major argument. @@ -14,22 +16,22 @@ can be used to justify the production fast path; silently matching a prefix as though it were the whole source would lose over-application semantics. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace HeadConstN /-- Every exact constant-headed spine constructively matches the corresponding -`varN` pattern. The existential capture map is produced by Lean4Lean's own +`varN` pattern. The existential capture map is produced by Ix.Theory.Named's own `Pattern.Matches.var` constructor at each application. -/ theorem matches_varN {name : Lean.Name} {arity : Nat} {source : VExpr} (h : HeadConstN name arity source) : - ∃ (levels : List Lean4Lean.VLevel) - (captures : ((Lean4Lean.Pattern.const name).varN arity).Path → VExpr), - Lean4Lean.Pattern.Matches - ((Lean4Lean.Pattern.const name).varN arity) + ∃ (levels : List Ix.Theory.VLevel) + (captures : ((Ix.Theory.Named.Pattern.const name).varN arity).Path → VExpr), + Ix.Theory.Named.Pattern.Matches + ((Ix.Theory.Named.Pattern.const name).varN arity) source levels captures := by induction h with | const levels => @@ -37,10 +39,10 @@ theorem matches_varN | @app arity fn arg hprefix ih => obtain ⟨levels, captures, hmatch⟩ := ih refine ⟨levels, - (fun path : Option (((Lean4Lean.Pattern.const name).varN arity).Path) => + (fun path : Option (((Ix.Theory.Named.Pattern.const name).varN arity).Path) => path.elim arg captures), ?_⟩ - simpa only [Lean4Lean.Pattern.varN, Nat.add_comm] using - (Lean4Lean.Pattern.Matches.var (a' := arg) hmatch) + simpa only [Ix.Theory.Named.Pattern.varN, Nat.add_comm] using + (Ix.Theory.Named.Pattern.Matches.var (a' := arg) hmatch) /-- The canonical Theory numeral zero is a nullary `Nat.zero` spine. -/ theorem natLit_zero : @@ -60,7 +62,7 @@ end HeadConstN namespace RecursorIotaPattern /-- Exact recursor and constructor spines construct the dependent match for -Lean4Lean's ordinary iota pattern. The recursor levels and both capture maps +Ix.Theory.Named's ordinary iota pattern. The recursor levels and both capture maps are exactly those built by `Pattern.Matches`; no choice principle is needed. -/ theorem matches_of_shapes {recursorName constructorName : Lean.Name} @@ -68,10 +70,10 @@ theorem matches_of_shapes {recursorPrefix major : VExpr} (hrecursor : HeadConstN recursorName majorIdx recursorPrefix) (hconstructor : HeadConstN constructorName constructorArgs major) : - ∃ (levels : List Lean4Lean.VLevel) + ∃ (levels : List Ix.Theory.VLevel) (captures : (RecursorIotaPattern recursorName majorIdx constructorName constructorArgs).Path → VExpr), - Lean4Lean.Pattern.Matches + Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern recursorName majorIdx constructorName constructorArgs) (.app recursorPrefix major) levels captures := by @@ -80,8 +82,8 @@ theorem matches_of_shapes obtain ⟨_, constructorCaptures, hconstructorMatch⟩ := hconstructor.matches_varN refine ⟨recursorLevels, Sum.elim recursorCaptures constructorCaptures, ?_⟩ - simpa only [RecursorIotaPattern, Lean4Lean.SimplePattern.toPattern] using - Lean4Lean.Pattern.Matches.app hrecursorMatch hconstructorMatch + simpa only [RecursorIotaPattern, Ix.Theory.Named.SimplePattern.toPattern] using + Ix.Theory.Named.Pattern.Matches.app hrecursorMatch hconstructorMatch /-- Constructive matching and the counted-spine view are equivalent. This packages the registered-rule inversion together with the capture-map @@ -90,10 +92,10 @@ direction. -/ theorem exists_matches_iff_shapes {recursorName constructorName : Lean.Name} {majorIdx constructorArgs : Nat} {source : VExpr} : - (∃ (levels : List Lean4Lean.VLevel) + (∃ (levels : List Ix.Theory.VLevel) (captures : (RecursorIotaPattern recursorName majorIdx constructorName constructorArgs).Path → VExpr), - Lean4Lean.Pattern.Matches + Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern recursorName majorIdx constructorName constructorArgs) source levels captures) ↔ @@ -112,10 +114,10 @@ theorem matches_natZero {recursorName : Lean.Name} {majorIdx : Nat} {recursorPrefix : VExpr} (hrecursor : HeadConstN recursorName majorIdx recursorPrefix) : - ∃ (levels : List Lean4Lean.VLevel) + ∃ (levels : List Ix.Theory.VLevel) (captures : (RecursorIotaPattern recursorName majorIdx ``Nat.zero 0).Path → VExpr), - Lean4Lean.Pattern.Matches + Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern recursorName majorIdx ``Nat.zero 0) (.app recursorPrefix (VExpr.natLit 0)) levels captures := matches_of_shapes hrecursor HeadConstN.natLit_zero @@ -126,10 +128,10 @@ theorem matches_natSucc {recursorName : Lean.Name} {majorIdx predecessor : Nat} {recursorPrefix : VExpr} (hrecursor : HeadConstN recursorName majorIdx recursorPrefix) : - ∃ (levels : List Lean4Lean.VLevel) + ∃ (levels : List Ix.Theory.VLevel) (captures : (RecursorIotaPattern recursorName majorIdx ``Nat.succ 1).Path → VExpr), - Lean4Lean.Pattern.Matches + Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern recursorName majorIdx ``Nat.succ 1) (.app recursorPrefix (VExpr.natLit (predecessor + 1))) levels captures := @@ -175,19 +177,19 @@ end NatRecIotaCase namespace RecursorRulePattern /-- Once the recursor prefix and Nat constructor case are fixed, the exact -trusted rule pattern has a concrete Lean4Lean match and capture map. -/ +trusted rule pattern has a concrete Ix.Theory.Named match and capture map. -/ theorem matches_natLiteral {pattern : RecursorRulePattern} {major : Nat} {recursorPrefix : VExpr} (hrecursor : HeadConstN pattern.recursorName pattern.majorIdx recursorPrefix) (hcase : NatRecIotaCase pattern major) : - ∃ (levels : List Lean4Lean.VLevel) + ∃ (levels : List Ix.Theory.VLevel) (captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr), - Lean4Lean.Pattern.Matches + Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -204,7 +206,7 @@ namespace TrAppSpine exactly counted Theory constant spine. In particular, this theorem does not forget how many arguments precede a descriptor-selected major. -/ theorem headConstN - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {id : KId .anon} {us : Array (KUniv .anon)} {info : ExprInfo .anon} @@ -228,7 +230,7 @@ theorem headConstN recursor half of the selected pattern match. The length equality is an explicit prefix-boundary obligation. -/ theorem matches_natRecRulePrefix - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {id : KId .anon} {us : Array (KUniv .anon)} {info : ExprInfo .anon} @@ -239,12 +241,12 @@ theorem matches_natRecRulePrefix (hname : nameOf id.addr = some pattern.recursorName) (hlength : args.length = pattern.majorIdx) (hcase : NatRecIotaCase pattern major) : - ∃ (levels : List Lean4Lean.VLevel) + ∃ (levels : List Ix.Theory.VLevel) (captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr), - Lean4Lean.Pattern.Matches + Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -264,7 +266,7 @@ pattern provenance. Constructor shape remains a separate Nat-specific fact, so a catalogued rule at index zero or one is not silently assumed to be the corresponding Nat rule. -/ theorem matches_natLiteralPrefix - {env : Lean4Lean.VEnv} {catalog : Catalog} + {env : Ix.Theory.Named.VEnv} {catalog : Catalog} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {id : KId .anon} {recursor : KConst .anon} {rule : RecRule .anon} {pattern : RecursorRulePattern} @@ -277,12 +279,12 @@ theorem matches_natLiteralPrefix (.const id us info) args recursorPrefix) (hlength : args.length = pattern.majorIdx) (hcase : NatRecIotaCase pattern major) : - ∃ (levels : List Lean4Lean.VLevel) + ∃ (levels : List Ix.Theory.VLevel) (captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr), - Lean4Lean.Pattern.Matches + Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -292,4 +294,4 @@ theorem matches_natLiteralPrefix end RawRecursorRulePatternRel -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/NatRecognizer.lean b/Ix/Kernel/Verify/Whnf/Iota/NatRecognizer.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/Iota/NatRecognizer.lean rename to Ix/Kernel/Verify/Whnf/Iota/NatRecognizer.lean index 1ad3dbf2c..72f005f25 100644 --- a/Ix/Tc/Verify/Whnf/Iota/NatRecognizer.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/NatRecognizer.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.RuntimeContracts +import Ix.Kernel.Verify.Whnf.RuntimeContracts /-! # Linear Nat-recognizer success provenance @@ -12,7 +12,7 @@ from being applied to a recursor rule or major index that execution never selected. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace KId @@ -332,4 +332,4 @@ theorem patternAt end TrustedNatRecLiteralParts end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/NatReduction.lean b/Ix/Kernel/Verify/Whnf/Iota/NatReduction.lean similarity index 91% rename from Ix/Tc/Verify/Whnf/Iota/NatReduction.lean rename to Ix/Kernel/Verify/Whnf/Iota/NatReduction.lean index 9c34fe7d0..4da4e5808 100644 --- a/Ix/Tc/Verify/Whnf/Iota/NatReduction.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/NatReduction.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Whnf.Iota.NatRuleLayout +import Ix.Kernel.Verify.Whnf.Iota.NatRuleLayout + +open Ix.Theory (VLevel) /-! # Checked Nat-iota reduction through typed suffixes @@ -16,9 +18,9 @@ inductive-admission/RHS obligations; neither follows from rule-slot existence or from a successful pattern match alone. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM namespace TrAppSuffix @@ -27,7 +29,7 @@ namespace TrAppSuffix application is typed. For a nonempty suffix, the first applicable function type is recovered by walking backward through the snoc derivation. -/ theorem startHasType - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {start : VExpr} {args : List (KExpr .anon)} {resultV resultType : VExpr} @@ -44,7 +46,7 @@ order, and the complete old and new applications remain definitionally equal. In particular, the result expression contains `args`; this theorem cannot justify dropping a trailing application. -/ theorem rebase - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {start : VExpr} {args : List (KExpr .anon)} {resultV : VExpr} @@ -75,7 +77,7 @@ theorem rebase simp only [List.foldl_cons, List.foldl_nil] rw [KExpr.mkApp_shape] exact .app hcurrentType harg hcurrentTr hargTr - · exact (Lean4Lean.VEnv.IsDefEq.appDF hcurrentEqAt harg).toU + · exact (Ix.Theory.Named.VEnv.IsDefEq.appDF hcurrentEqAt harg).toU end TrAppSuffix end RecM @@ -86,7 +88,7 @@ namespace RawRecursorRulePatternRel environment. A match is deliberately insufficient: the pattern's explicit definitional-equality checks must also be discharged. -/ theorem checkedReduction - {env : Lean4Lean.VEnv} {catalog : Catalog} + {env : Ix.Theory.Named.VEnv} {catalog : Catalog} {nameOf : Address → Option Lean.Name} {id : KId .anon} {recursor : KConst .anon} {rule : RecRule .anon} {pattern : RecursorRulePattern} @@ -94,13 +96,13 @@ theorem checkedReduction rule pattern) (henv : env.WF) {uvars : Nat} {Gamma : List VExpr} {source A : VExpr} - {levels : List Lean4Lean.VLevel} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr} - (hGamma : Lean4Lean.OnCtx Gamma (env.IsType uvars)) - (hmatch : Lean4Lean.Pattern.Matches + (hGamma : Ix.Theory.Named.OnCtx Gamma (env.IsType uvars)) + (hmatch : Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -113,7 +115,7 @@ theorem checkedReduction (pattern.rhs.apply levels captures) := by rcases hpattern with ⟨_, _, _, _, _, _, _, hsound⟩ - exact hsound Lean4Lean.VEnv.LE.rfl henv hGamma hmatch htype hchecks + exact hsound Ix.Theory.Named.VEnv.LE.rfl henv hGamma hmatch htype hchecks end RawRecursorRulePatternRel @@ -144,12 +146,12 @@ theorem checkedRhsSuffix {pattern : RecursorRulePattern} (hpattern : RawRecursorRulePatternRel world.venv world.catalog world.nameOf id recursor rule pattern) - {levels : List Lean4Lean.VLevel} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr} - (hmatch : Lean4Lean.Pattern.Matches + (hmatch : Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -177,4 +179,4 @@ theorem checkedRhsSuffix end NatRecLiteralTranslationSplit end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/NatRuleLayout.lean b/Ix/Kernel/Verify/Whnf/Iota/NatRuleLayout.lean similarity index 96% rename from Ix/Tc/Verify/Whnf/Iota/NatRuleLayout.lean rename to Ix/Kernel/Verify/Whnf/Iota/NatRuleLayout.lean index cb260928b..6bef289d4 100644 --- a/Ix/Tc/Verify/Whnf/Iota/NatRuleLayout.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/NatRuleLayout.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Whnf.Iota.NatPatternMatching +import Ix.Kernel.Verify.Whnf.Iota.NatPatternMatching + +open Ix.Theory (VLevel) /-! # Trusted Nat-rule layout and through-major spine splitting @@ -15,9 +17,9 @@ suffix separately, so later RHS reasoning cannot accidentally discard an over-application. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) /-! ## Trusted Nat recursor layout -/ @@ -102,7 +104,7 @@ namespace RecM /-- Typed translation of a left-associated application suffix starting from an already translated prefix. The suffix is stored in production order. -/ -inductive TrAppSuffix (env : Lean4Lean.VEnv) (uvars : Nat) +inductive TrAppSuffix (env : Ix.Theory.Named.VEnv) (uvars : Nat) (nameOf : Address → Option Lean.Name) (trProj : RawProjRel) (Delta : KVLCtx) (start : VExpr) : List (KExpr .anon) → VExpr → Prop @@ -119,7 +121,7 @@ namespace TrAppSuffix /-- Reattach a certified suffix to a translated concrete prefix. -/ theorem tr - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {start : VExpr} {args : List (KExpr .anon)} {resultV : VExpr} @@ -144,7 +146,7 @@ namespace TrAppSpine argument. `throughTr` ends exactly after applying the major; `suffixTr` accounts for every later argument. -/ def SplitAt - (env : Lean4Lean.VEnv) (uvars : Nat) + (env : Ix.Theory.Named.VEnv) (uvars : Nat) (nameOf : Address → Option Lean.Name) (trProj : RawProjRel) (Delta : KVLCtx) (head : KExpr .anon) (args : List (KExpr .anon)) (majorIdx : Nat) @@ -164,7 +166,7 @@ def SplitAt The proof follows the snoc structure of `TrAppSpine`, distinguishing a hit in the prior prefix from the newly appended final argument. -/ theorem splitAt - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {head major : KExpr .anon} {args : List (KExpr .anon)} {majorIdx : Nat} {resultV : VExpr} @@ -210,7 +212,7 @@ end TrAppSpine /-- Exact translated decomposition induced by the literal position recorded in a successful Nat-recognizer descriptor. -/ def NatRecLiteralTranslationSplit - (env : Lean4Lean.VEnv) (uvars : Nat) + (env : Ix.Theory.Named.VEnv) (uvars : Nat) (nameOf : Address → Option Lean.Name) (trProj : RawProjRel) (Delta : KVLCtx) (id : KId .anon) (source : KExpr .anon) (parts : NatRecLiteralParts .anon) @@ -269,7 +271,7 @@ theorem patternMajor The major translation becomes the canonical Theory numeral by inversion of the owned literal translation rule. -/ theorem translatedSplit - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {id : KId .anon} {recursor : KConst .anon} {source : KExpr .anon} {parts : NatRecLiteralParts .anon} @@ -324,7 +326,7 @@ theorem translatedCase ∃ (id : KId .anon) (recursor : KConst .anon) (rule : RecRule .anon) (pattern : RecursorRulePattern) (priorArgs laterArgs : List (KExpr .anon)) (priorV : VExpr) - (levels : List Lean4Lean.VLevel) + (levels : List Ix.Theory.VLevel) (captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -338,7 +340,7 @@ theorem translatedCase RawRecursorRulePatternRel world.venv world.catalog world.nameOf id recursor rule pattern ∧ NatRecIotaCase pattern parts.major ∧ - Lean4Lean.Pattern.Matches + Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -363,4 +365,4 @@ end TrustedNatRecLiteralParts end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/OptionalReduction.lean b/Ix/Kernel/Verify/Whnf/Iota/OptionalReduction.lean similarity index 97% rename from Ix/Tc/Verify/Whnf/Iota/OptionalReduction.lean rename to Ix/Kernel/Verify/Whnf/Iota/OptionalReduction.lean index 286e3fee1..262a58432 100644 --- a/Ix/Tc/Verify/Whnf/Iota/OptionalReduction.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/OptionalReduction.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Iota.Ingress +import Ix.Kernel.Verify.Whnf.Iota.Ingress /-! # Exhaustive iota optional-reduction contract @@ -21,7 +21,7 @@ ingress is supplied separately by `AnonLazyIngressContext`, which identifies the actual installed `ingressAnonAddrShallow` hook. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Remaining non-method authority used by `tryIotaWithFlags`. @@ -51,7 +51,7 @@ structure IotaSuccessOracle (semantics : CacheSemantics) (support : RunSupport) : Prop where accept : ∀ {uvars : Nat} {Delta : KVLCtx} {methods : Methods .anon} {source : KExpr .anon} - {sourceV : Lean4Lean.VExpr} {flags : WhnfFlags} + {sourceV : Ix.Theory.Named.VExpr} {flags : WhnfFlags} {s sf : TcState .anon} {result : KExpr .anon}, Methods.WFAt .noAccel semantics trProj world support uvars methods → support source → @@ -119,4 +119,4 @@ theorem tryIotaWithFlags_optional_wf_of_contexts success.accept hmethods hsourceSupport hsource hI hrunIota⟩ end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/RuleInstantiation.lean b/Ix/Kernel/Verify/Whnf/Iota/RuleInstantiation.lean similarity index 97% rename from Ix/Tc/Verify/Whnf/Iota/RuleInstantiation.lean rename to Ix/Kernel/Verify/Whnf/Iota/RuleInstantiation.lean index 5d785cb08..620a82b35 100644 --- a/Ix/Tc/Verify/Whnf/Iota/RuleInstantiation.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/RuleInstantiation.lean @@ -1,5 +1,7 @@ -import Ix.Tc.Verify.Whnf.Iota.NatReduction -import Ix.Tc.Verify.InstL +import Ix.Kernel.Verify.Whnf.Iota.NatReduction +import Ix.Kernel.Verify.InstL + +open Ix.Theory (VLevel) /-! # Typed registered recursor RHS instantiation @@ -23,9 +25,9 @@ arguments. Modeling those applications is the next bridge, and conflating the two terms here would be unsound. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VDefEq VEnv VExpr) +open Ix.Theory.Named (VDefEq VEnv VExpr) namespace RegisteredRecursorRuleRhsRel @@ -137,4 +139,4 @@ theorem registeredRhsTyped end RawRecursorRuleRel -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/RuleSuffixTransport.lean b/Ix/Kernel/Verify/Whnf/Iota/RuleSuffixTransport.lean similarity index 94% rename from Ix/Tc/Verify/Whnf/Iota/RuleSuffixTransport.lean rename to Ix/Kernel/Verify/Whnf/Iota/RuleSuffixTransport.lean index 31daeebf8..94cc3fb77 100644 --- a/Ix/Tc/Verify/Whnf/Iota/RuleSuffixTransport.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/RuleSuffixTransport.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Whnf.Iota.RuleInstantiation +import Ix.Kernel.Verify.Whnf.Iota.RuleInstantiation + +open Ix.Theory (VLevel) /-! # Quotient-aware registered RHS suffix transport @@ -20,9 +22,9 @@ The theorem still does not identify an instantiated registered body with sequence remains an explicit subsequent obligation. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) namespace RecM namespace TrAppSuffix @@ -35,7 +37,7 @@ the structural suffix theorem. The result is structural again. In particular, all original concrete suffix arguments remain visible in `args.foldl KExpr.mkApp replacement`. -/ theorem rebaseQuot - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {start : VExpr} {args : List (KExpr .anon)} {resultV : VExpr} @@ -81,12 +83,12 @@ theorem checkedRhsSuffixQuot {pattern : RecursorRulePattern} (hpattern : RawRecursorRulePatternRel world.venv world.catalog world.nameOf id recursor rule pattern) - {levels : List Lean4Lean.VLevel} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr} - (hmatch : Lean4Lean.Pattern.Matches + (hmatch : Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -115,4 +117,4 @@ theorem checkedRhsSuffixQuot end NatRecLiteralTranslationSplit end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/SelectedRule.lean b/Ix/Kernel/Verify/Whnf/Iota/SelectedRule.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Iota/SelectedRule.lean rename to Ix/Kernel/Verify/Whnf/Iota/SelectedRule.lean index b53cf19a5..680fd94ca 100644 --- a/Ix/Tc/Verify/Whnf/Iota/SelectedRule.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/SelectedRule.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Whnf.Iota.ArgumentExecution +import Ix.Kernel.Verify.Whnf.Iota.ArgumentExecution + +open Ix.Theory (VLevel) /-! # Checked execution of one selected iota rule @@ -17,9 +19,9 @@ certificate, the selected-rule trace proves exact production execution, state/intern framing, finite support, and source-to-result `WhnfMeaning`. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VDefEq VExpr) +open Ix.Theory.Named (VDefEq VExpr) namespace WhnfMeaning @@ -225,7 +227,7 @@ argument slices is the pattern RHS under the match's levels and captures. Current `RawRecursorRuleRel` and `RawRecursorRulePatternRel` do not imply this equation because they record their RHS values independently. -/ def IotaRhsApplicationAligned - (pattern : RecursorRulePattern) (levels : List Lean4Lean.VLevel) + (pattern : RecursorRulePattern) (levels : List Ix.Theory.VLevel) (captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -605,12 +607,12 @@ theorem checkedMeaning (hsourceTr : TrKExprS world.venv uvars world.nameOf trProj Delta source sourceV) (hsourceType : world.venv.HasType uvars Delta.toCtx sourceV sourceType) - {levels : List Lean4Lean.VLevel} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr} - (hmatch : Lean4Lean.Pattern.Matches + (hmatch : Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -662,12 +664,12 @@ theorem checkedAcceptance_empty (hsourceTr : TrKExprS world.venv 0 world.nameOf trProj [] source sourceV) (hsourceType : world.venv.HasType 0 [] sourceV sourceType) - {levels : List Lean4Lean.VLevel} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr} - (hmatch : Lean4Lean.Pattern.Matches + (hmatch : Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -731,12 +733,12 @@ theorem checkedAcceptance_nonempty (hsourceTr : TrKExprS world.venv uvars world.nameOf trProj [] source sourceV) (hsourceType : world.venv.HasType uvars [] sourceV sourceType) - {levels : List Lean4Lean.VLevel} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr} - (hmatch : Lean4Lean.Pattern.Matches + (hmatch : Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -763,4 +765,4 @@ end ApplyIotaRuleTrace end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/StringLiteral.lean b/Ix/Kernel/Verify/Whnf/Iota/StringLiteral.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Iota/StringLiteral.lean rename to Ix/Kernel/Verify/Whnf/Iota/StringLiteral.lean index 88c530e6e..8efaddced 100644 --- a/Ix/Tc/Verify/Whnf/Iota/StringLiteral.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/StringLiteral.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Whnf.Iota.NatLiteral +import Ix.Kernel.Verify.Whnf.Iota.NatLiteral + +open Ix.Theory (VLevel) /-! # String-literal iota preprocessing @@ -15,9 +17,9 @@ spines is a separate helper-closure obligation; this file does not disguise it as a consequence of the operational callback equation. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VDefEq VExpr) +open Ix.Theory.Named (VDefEq VExpr) namespace RecM @@ -393,12 +395,12 @@ theorem tryIotaWithFlags_strCtor_checkedAcceptance_empty (hsourceTr : TrKExprS world.venv 0 world.nameOf trProj [] source sourceV) (hsourceType : world.venv.HasType 0 [] sourceV sourceType) - {levels : List Lean4Lean.VLevel} + {levels : List Ix.Theory.VLevel} {captures : (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + pattern.constructorFields.toNat)).Path → VExpr} - (hmatch : Lean4Lean.Pattern.Matches + (hmatch : Ix.Theory.Named.Pattern.Matches (RecursorIotaPattern pattern.recursorName pattern.majorIdx pattern.constructorName (pattern.constructorParams.toNat + @@ -431,4 +433,4 @@ theorem tryIotaWithFlags_strCtor_checkedAcceptance_empty end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/StructEtaControl.lean b/Ix/Kernel/Verify/Whnf/Iota/StructEtaControl.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/Iota/StructEtaControl.lean rename to Ix/Kernel/Verify/Whnf/Iota/StructEtaControl.lean index 199e21469..8ebcb7d67 100644 --- a/Ix/Tc/Verify/Whnf/Iota/StructEtaControl.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/StructEtaControl.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Iota.ConstructorSynthesisFallback +import Ix.Kernel.Verify.Whnf.Iota.ConstructorSynthesisFallback /-! # Struct-eta iota control-flow closure @@ -16,7 +16,7 @@ structure-like does not itself manufacture the registered Theory recursor equation or projection interpretation. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -1035,4 +1035,4 @@ theorem tryIotaCtorOrStructEta_constructor end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/Substitution.lean b/Ix/Kernel/Verify/Whnf/Iota/Substitution.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Iota/Substitution.lean rename to Ix/Kernel/Verify/Whnf/Iota/Substitution.lean index a8eb642c8..91994ae4f 100644 --- a/Ix/Tc/Verify/Whnf/Iota/Substitution.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/Substitution.lean @@ -1,5 +1,7 @@ -import Ix.Tc.Verify.Whnf.Iota.RuleSuffixTransport -import Ix.Tc.Verify.Totalization +import Ix.Kernel.Verify.Whnf.Iota.RuleSuffixTransport +import Ix.Kernel.Verify.Totalization + +open Ix.Theory (VLevel) /-! # Transient iota substitution agrees with the verified spec @@ -17,7 +19,7 @@ uses the equality to expose the production `applyIotaArg` transient-lambda branch as the verified substitution spec and as a semantic beta reduction. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace KExpr @@ -268,7 +270,7 @@ theorem applyIotaArg_true_lam_spec (hbig : body.size + arg.size < UInt64.size) : RecM.applyIotaArg (.lam name bi dom body info) arg true = pure (KExpr.substSpec body arg 0) := by - rw [Ix.Tc.RecM.applyIotaArg_true_lam, + rw [Ix.Kernel.RecM.applyIotaArg_true_lam, hbody.substNoIntern_eq_substSpec harg (depth := 0) (by rw [show (0 : UInt64).toNat = 0 from rfl]; omega) (by omega)] @@ -299,7 +301,7 @@ theorem betaNoIntern {Delta : KVLCtx} {nm : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {ty body arg : KExpr .anon} {lamMd appMd : ExprInfo .anon} - {A bodyV argV B : Lean4Lean.VExpr} {u : Lean4Lean.VLevel} + {A bodyV argV B : Ix.Theory.Named.VExpr} {u : Ix.Theory.VLevel} (hty : TrKExprS world.venv uvars world.nameOf trProj Delta ty A) (hbody : TrKExprS world.venv uvars world.nameOf trProj ((none, .vlam A) :: Delta) body bodyV) @@ -320,4 +322,4 @@ theorem betaNoIntern end WhnfMeaning -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Iota/SynthesisRequests.lean b/Ix/Kernel/Verify/Whnf/Iota/SynthesisRequests.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Iota/SynthesisRequests.lean rename to Ix/Kernel/Verify/Whnf/Iota/SynthesisRequests.lean index 7ecb711cc..69221e64e 100644 --- a/Ix/Tc/Verify/Whnf/Iota/SynthesisRequests.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/SynthesisRequests.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.StructEta.RebuildRequests +import Ix.Kernel.Verify.Whnf.StructEta.RebuildRequests /-! # Finite request closure for K-synthesis @@ -21,7 +21,7 @@ instantiate it. No catalog, walker, or generated-expression effect remains abstract. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- State-only contract for the actual DefEq back-edge, including production's @@ -75,7 +75,7 @@ theorem callIsDefEq_wf {uvars : Nat} {Delta : KVLCtx} {methods : Methods .anon} (hmethods : Methods.WFAt layer semantics trProj world support uvars methods) - {a b : KExpr .anon} {va vb : Lean4Lean.VExpr} + {a b : KExpr .anon} {va vb : Ix.Theory.Named.VExpr} (haSupport : support a) (hbSupport : support b) (ha : TrKExprS world.venv uvars world.nameOf trProj Delta a va) (hb : TrKExprS world.venv uvars world.nameOf trProj Delta b vb) @@ -136,7 +136,7 @@ def KSynthCandidateInputs {params : Nat} (plan : KSynthCandidateRequests requests ctorId tyUs tyArgs params) (majorTyW : KExpr .anon) : Prop := - ∃ majorTyWV ctorAppV : Lean4Lean.VExpr, + ∃ majorTyWV ctorAppV : Ix.Theory.Named.VExpr, support majorTyW ∧ TrKExprS world.venv uvars world.nameOf trProj Delta majorTyW majorTyWV ∧ @@ -168,7 +168,7 @@ structure KSynthCandidateInputOracle (support : RunSupport) : Prop where candidate : ∀ {uvars : Nat} {Delta : KVLCtx} - {majorTyW : KExpr .anon} {majorTyWV : Lean4Lean.VExpr} + {majorTyW : KExpr .anon} {majorTyWV : Ix.Theory.Named.VExpr} {tyHeadId indId ctorId : KId .anon} {tyUs : Array (KUniv .anon)} {tyInfo : ExprInfo .anon} {tyArgs : Array (KExpr .anon)} {params : Nat} @@ -468,7 +468,7 @@ theorem selectKSynthCandidate_state_wf_of_inputs (hfault : TcM.LazyFaultPreserves (WhnfStateInv layer semantics trProj world support uvars Delta)) (candidateInputs : KSynthCandidateInputOracle trProj world support) - {majorTyW : KExpr .anon} {majorTyWV : Lean4Lean.VExpr} + {majorTyW : KExpr .anon} {majorTyWV : Ix.Theory.Named.VExpr} {tyHeadId : KId .anon} {tyUs : Array (KUniv .anon)} {tyInfo : ExprInfo .anon} {tyArgs : Array (KExpr .anon)} {indId : KId .anon} {params : Nat} @@ -663,7 +663,7 @@ theorem synthCtorWhenK_state_wf_of_inputs (WhnfStateInv layer semantics trProj world support uvars current)) (hreferences : TrustedReferences world support) (candidateInputs : KSynthCandidateInputOracle trProj world support) - {major : KExpr .anon} {majorV : Lean4Lean.VExpr} + {major : KExpr .anon} {majorV : Ix.Theory.Named.VExpr} (hmajorSupport : support major) (hmajorTr : TrKExprS world.venv uvars world.nameOf trProj Delta major majorV) @@ -757,4 +757,4 @@ theorem synthCtorWhenK_state_wf_of_inputs afterScan end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/NoDelta/BaseReductions.lean b/Ix/Kernel/Verify/Whnf/NoDelta/BaseReductions.lean similarity index 97% rename from Ix/Tc/Verify/Whnf/NoDelta/BaseReductions.lean rename to Ix/Kernel/Verify/Whnf/NoDelta/BaseReductions.lean index dc21fcce9..954f38dec 100644 --- a/Ix/Tc/Verify/Whnf/NoDelta/BaseReductions.lean +++ b/Ix/Kernel/Verify/Whnf/NoDelta/BaseReductions.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.NoDelta.QuotientReflection +import Ix.Kernel.Verify.Whnf.NoDelta.QuotientReflection /-! # Assemble the active no-delta base oracle @@ -10,7 +10,7 @@ constructs the `NoDeltaBaseOracle` consumed by the already-proved ordered no-delta step. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Complete input package for the five active no-delta reducers. @@ -92,4 +92,4 @@ theorem oracle end NoDeltaBaseContext end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/NoDelta/ProjectionApplication.lean b/Ix/Kernel/Verify/Whnf/NoDelta/ProjectionApplication.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/NoDelta/ProjectionApplication.lean rename to Ix/Kernel/Verify/Whnf/NoDelta/ProjectionApplication.lean index 95a18174d..088ed9097 100644 --- a/Ix/Tc/Verify/Whnf/NoDelta/ProjectionApplication.lean +++ b/Ix/Kernel/Verify/Whnf/NoDelta/ProjectionApplication.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Structural.Reducer +import Ix.Kernel.Verify.Whnf.Structural.Reducer /-! # Projection-application no-delta field @@ -14,7 +14,7 @@ boundary supplies meaning for the changed head; and the finite application census certifies the exact left-to-right suffix rebuilt by production. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-! ## Exact raw-helper equations -/ @@ -202,7 +202,7 @@ theorem tryProjAppReduceFinished_app_optional_wf (horacle : InductiveReductionOracle .noAccel semantics trProj world support) {f arg : KExpr .anon} {info : ExprInfo .anon} {flags : WhnfFlags} - {uvars : Nat} {Delta : KVLCtx} {sourceV : Lean4Lean.VExpr} + {uvars : Nat} {Delta : KVLCtx} {sourceV : Ix.Theory.Named.VExpr} {s : TcState .anon} (hsourceSupport : support (.app f arg info)) (hsource : TrKExprS world.venv uvars world.nameOf trProj Delta @@ -340,4 +340,4 @@ theorem tryProjAppReduceFinished_optional_wf_of_contexts (hempty := rfl) end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/NoDelta/ProjectionDefinition.lean b/Ix/Kernel/Verify/Whnf/NoDelta/ProjectionDefinition.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/NoDelta/ProjectionDefinition.lean rename to Ix/Kernel/Verify/Whnf/NoDelta/ProjectionDefinition.lean index 3b0c84de1..0c342d09b 100644 --- a/Ix/Tc/Verify/Whnf/NoDelta/ProjectionDefinition.lean +++ b/Ix/Kernel/Verify/Whnf/NoDelta/ProjectionDefinition.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.NoDelta.StringPrimitive +import Ix.Kernel.Verify.Whnf.NoDelta.StringPrimitive /-! # Projection-definition no-delta field @@ -14,7 +14,7 @@ the run support; support for only the final expression is not enough to make the intern-table collision argument sound. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Finite request plan for a recognized projection-wrapper definition. @@ -48,7 +48,7 @@ structure ProjectionDefinitionReflection (semantics : CacheSemantics) (support : RunSupport) : Prop where success : ∀ {uvars : Nat} {Delta : KVLCtx} {methods : Methods .anon} {source result : KExpr .anon} - {sourceV : Lean4Lean.VExpr} {s sf : TcState .anon}, + {sourceV : Ix.Theory.Named.VExpr} {s sf : TcState .anon}, Methods.WFAt .noAccel semantics trProj world support uvars methods → support source → TrKExprS world.venv uvars world.nameOf trProj Delta source sourceV → @@ -211,4 +211,4 @@ theorem tryReduceProjectionDefinition_optional_wf_of_contexts hrunProjection⟩ end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/NoDelta/Quotient.lean b/Ix/Kernel/Verify/Whnf/NoDelta/Quotient.lean similarity index 97% rename from Ix/Tc/Verify/Whnf/NoDelta/Quotient.lean rename to Ix/Kernel/Verify/Whnf/NoDelta/Quotient.lean index 96eb1258d..9e0341399 100644 --- a/Ix/Tc/Verify/Whnf/NoDelta/Quotient.lean +++ b/Ix/Kernel/Verify/Whnf/NoDelta/Quotient.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.NoDelta.ProjectionDefinition +import Ix.Kernel.Verify.Whnf.NoDelta.ProjectionDefinition /-! # Quotient no-delta field @@ -10,7 +10,7 @@ callback errors and every generated intern, from finite input and request coverage. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Finite generated-node plan for one selected quotient reduction. @@ -44,7 +44,7 @@ structure QuotientReductionReflection (semantics : CacheSemantics) (support : RunSupport) : Prop where success : ∀ {uvars : Nat} {Delta : KVLCtx} {methods : Methods .anon} {source result : KExpr .anon} - {sourceV : Lean4Lean.VExpr} {s sf : TcState .anon}, + {sourceV : Ix.Theory.Named.VExpr} {s sf : TcState .anon}, Methods.WFAt .noAccel semantics trProj world support uvars methods → support source → TrKExprS world.venv uvars world.nameOf trProj Delta source sourceV → @@ -85,7 +85,7 @@ theorem tryQuotReduceSelected_inv_wf (inputs : NoDeltaInputSupport support) {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {uvars : Nat} {Delta : KVLCtx} - {source head : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source head : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} {args : Array (KExpr .anon)} {prims : Primitives .anon} {fIdx majorIdx : Nat} {s : TcState .anon} (hsourceSupport : support source) @@ -164,7 +164,7 @@ theorem tryQuotReduce_inv_wf (inputs : NoDeltaInputSupport support) {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {uvars : Nat} {Delta : KVLCtx} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} {s : TcState .anon} (hsourceSupport : support source) (hsource : TrKExprS world.venv uvars world.nameOf trProj Delta source @@ -253,4 +253,4 @@ theorem tryQuotReduce_optional_wf_of_contexts reflection.success hmethods hsourceSupport hsource hI hrunQuot⟩ end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/NoDelta/QuotientReflection.lean b/Ix/Kernel/Verify/Whnf/NoDelta/QuotientReflection.lean similarity index 97% rename from Ix/Tc/Verify/Whnf/NoDelta/QuotientReflection.lean rename to Ix/Kernel/Verify/Whnf/NoDelta/QuotientReflection.lean index 38068dd08..549a76cbe 100644 --- a/Ix/Tc/Verify/Whnf/NoDelta/QuotientReflection.lean +++ b/Ix/Kernel/Verify/Whnf/NoDelta/QuotientReflection.lean @@ -1,4 +1,6 @@ -import Ix.Tc.Verify.Whnf.NoDelta.Quotient +import Ix.Kernel.Verify.Whnf.NoDelta.Quotient + +open Ix.Theory (VLevel) /-! # Quotient reduction reflection @@ -7,27 +9,27 @@ This module turns the successful production `tryQuotReduce` path into the semantic `QuotientReductionReflection` consumed by the no-delta reducer. The only conditional input is a pair of Theory-level contraction laws. They mention no Ix state, addresses, hashes, support predicates, or executions and -are the narrow temporary interface intended to be replaced by Lean4Lean's +are the narrow temporary interface intended to be replaced by Ix.Theory.Named's constructive quotient result. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VExpr) +open Ix.Theory.Named (VExpr) -/-- Theory-only quotient contraction laws expected from Lean4Lean L4L-19B. +/-- Theory-only quotient contraction laws expected from Ix.Theory.Named Spec-19B. Both laws are deliberately phrased after the quotient major has been related to an exact `Quot.mk` application. The Ix adapter below owns the proof of that relation from the real recursive-WHNF callback and owns all concrete spine and suffix alignment. -/ -structure QuotientReductionLaws (env : Lean4Lean.VEnv) : Prop where +structure QuotientReductionLaws (env : Ix.Theory.Named.VEnv) : Prop where lift : ∀ {uvars : Nat} {Gamma : List VExpr} - {liftLevels mkLevels : List Lean4Lean.VLevel} + {liftLevels mkLevels : List Ix.Theory.VLevel} {alpha relation beta fn respects ctorAlpha ctorRelation representative major : VExpr}, env.WF → - env.defeqs Lean4Lean.quotDefEq → + env.defeqs Ix.Theory.Named.quotDefEq → VExpr.WF env uvars Gamma (.app (VExpr.appN (.const ``Quot.lift liftLevels) @@ -46,7 +48,7 @@ structure QuotientReductionLaws (env : Lean4Lean.VEnv) : Prop where major) (.app fn representative) ind : ∀ {uvars : Nat} {Gamma : List VExpr} - {indLevels mkLevels : List Lean4Lean.VLevel} + {indLevels mkLevels : List Ix.Theory.VLevel} {alpha relation motive fn ctorAlpha ctorRelation representative major : VExpr}, env.WF → @@ -141,7 +143,7 @@ namespace TrAppSpine /-- Invert an exact three-argument translated spine. -/ theorem three - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {head a b c : KExpr .anon} {resultV : VExpr} (h : TrAppSpine env uvars nameOf trProj Delta head [a, b, c] @@ -169,7 +171,7 @@ theorem three /-- Invert an exact four-argument translated spine. -/ theorem four - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {head a b c d : KExpr .anon} {resultV : VExpr} (h : TrAppSpine env uvars nameOf trProj Delta head [a, b, c, d] @@ -201,7 +203,7 @@ theorem four /-- Invert an exact five-argument translated spine. -/ theorem five - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {head a b c d e : KExpr .anon} {resultV : VExpr} (h : TrAppSpine env uvars nameOf trProj Delta head [a, b, c, d, e] @@ -242,7 +244,7 @@ namespace TrKExprS /-- A translated constant whose address has an exact trusted-name binding has that name in its Theory syntax. -/ theorem const_name - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {id : KId .anon} {us : Array (KUniv .anon)} {info : ExprInfo .anon} {value : VExpr} {name : Lean.Name} @@ -265,7 +267,7 @@ theorem quotientLiftMeaning (theory : WhnfTheory trProj world uvars) (laws : QuotientReductionLaws world.venv) (htable : NoDeltaPrimitiveTableAgrees world prims) - (hregistered : world.venv.defeqs Lean4Lean.quotDefEq) + (hregistered : world.venv.defeqs Ix.Theory.Named.quotDefEq) (hDelta : KVLCtx.WF world.venv uvars Delta) {source : KExpr .anon} {sourceV : VExpr} {id : KId .anon} {us : Array (KUniv .anon)} @@ -305,7 +307,7 @@ theorem quotientLiftMeaning exact htable.quotLift.2 have hliftHead : liftHeadV = .const ``Quot.lift (us.toList.map KUniv.toVLevel) := - Ix.Tc.RecM.TrKExprS.const_name hliftHeadTr hliftName + Ix.Kernel.RecM.TrKExprS.const_name hliftHeadTr hliftName rw [hliftHead] at hpriorShape subst priorV @@ -337,7 +339,7 @@ theorem quotientLiftMeaning exact htable.quotCtor.2 have hmkHead : mkHeadV = .const ``Quot.mk (mkUs.toList.map KUniv.toVLevel) := - Ix.Tc.RecM.TrKExprS.const_name hmkHeadTr hmkName + Ix.Kernel.RecM.TrKExprS.const_name hmkHeadTr hmkName rw [hmkHead] at hmkShape subst majorWhnfV @@ -419,7 +421,7 @@ theorem quotientIndMeaning exact htable.quotInd.2 have hindHead : indHeadV = .const ``Quot.ind (us.toList.map KUniv.toVLevel) := - Ix.Tc.RecM.TrKExprS.const_name hindHeadTr hindName + Ix.Kernel.RecM.TrKExprS.const_name hindHeadTr hindName rw [hindHead] at hpriorShape subst priorV @@ -451,7 +453,7 @@ theorem quotientIndMeaning exact htable.quotCtor.2 have hmkHead : mkHeadV = .const ``Quot.mk (mkUs.toList.map KUniv.toVLevel) := - Ix.Tc.RecM.TrKExprS.const_name hmkHeadTr hmkName + Ix.Kernel.RecM.TrKExprS.const_name hmkHeadTr hmkName rw [hmkHead] at hmkShape subst majorWhnfV @@ -837,4 +839,4 @@ theorem of_laws cases hrun end QuotientReductionReflection -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/NoDelta/Reducer.lean b/Ix/Kernel/Verify/Whnf/NoDelta/Reducer.lean similarity index 94% rename from Ix/Tc/Verify/Whnf/NoDelta/Reducer.lean rename to Ix/Kernel/Verify/Whnf/NoDelta/Reducer.lean index 5e09646ac..316335442 100644 --- a/Ix/Tc/Verify/Whnf/NoDelta/Reducer.lean +++ b/Ix/Kernel/Verify/Whnf/NoDelta/Reducer.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.NoDelta.BaseReductions +import Ix.Kernel.Verify.Whnf.NoDelta.BaseReductions /-! # Public no-delta reducer @@ -10,7 +10,7 @@ and collision-robust cache writes. This slice supplies those two concrete components to that shell. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Complete fixed-context input for the public no-delta reducer. -/ @@ -42,7 +42,7 @@ theorem wf trProj world support Delta flags) (mode : NatSuccMode) {source : KExpr .anon} (hsourceSupport : support source) - {sourceV : Lean4Lean.VExpr} {s : TcState .anon} + {sourceV : Ix.Theory.Named.VExpr} {s : TcState .anon} (hsource : TrKExprS world.venv keys.uvars world.nameOf trProj Delta source sourceV) : RecM.WF .noAccel (whnfCacheSemantics keys trProj fallback) trProj world @@ -65,4 +65,4 @@ theorem wf end NoDeltaDriverContext end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/NoDelta/StringPrimitive.lean b/Ix/Kernel/Verify/Whnf/NoDelta/StringPrimitive.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/NoDelta/StringPrimitive.lean rename to Ix/Kernel/Verify/Whnf/NoDelta/StringPrimitive.lean index 99af60b5d..87d9413d8 100644 --- a/Ix/Tc/Verify/Whnf/NoDelta/StringPrimitive.lean +++ b/Ix/Kernel/Verify/Whnf/NoDelta/StringPrimitive.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.NoDelta.ProjectionApplication +import Ix.Kernel.Verify.Whnf.NoDelta.ProjectionApplication /-! # String primitive no-delta field @@ -13,7 +13,7 @@ exact generated nodes. Theory computation remains a deliberately narrow reflection boundary indexed by an observed successful production run. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Finite generated-node support for the String reducer. @@ -75,7 +75,7 @@ structure StringReductionReflection (semantics : CacheSemantics) (support : RunSupport) : Prop where success : ∀ {uvars : Nat} {Delta : KVLCtx} {methods : Methods .anon} {source result : KExpr .anon} - {sourceV : Lean4Lean.VExpr} {s sf : TcState .anon}, + {sourceV : Ix.Theory.Named.VExpr} {s sf : TcState .anon}, Methods.WFAt .noAccel semantics trProj world support uvars methods → support source → TrKExprS world.venv uvars world.nameOf trProj Delta source sourceV → @@ -281,4 +281,4 @@ theorem tryReduceString_optional_wf_of_reflection reflection.success hmethods hsourceSupport hsource hI hrun⟩ end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Projection/NoAccelTail.lean b/Ix/Kernel/Verify/Whnf/Projection/NoAccelTail.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/Projection/NoAccelTail.lean rename to Ix/Kernel/Verify/Whnf/Projection/NoAccelTail.lean index 31ecac194..af1030ea3 100644 --- a/Ix/Tc/Verify/Whnf/Projection/NoAccelTail.lean +++ b/Ix/Kernel/Verify/Whnf/Projection/NoAccelTail.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Structural.VerifiedStep +import Ix.Kernel.Verify.Whnf.Structural.VerifiedStep /-! # Concrete no-acceleration projection tail @@ -13,7 +13,7 @@ Only String-literal construction/normalization and the installed lazy-ingress hook remain as explicit premises when the tail is composed below. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM namespace WhnfCoreInputSupport @@ -267,4 +267,4 @@ theorem noAccel end ProjectionHelper end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Projection/StringCallback.lean b/Ix/Kernel/Verify/Whnf/Projection/StringCallback.lean similarity index 97% rename from Ix/Tc/Verify/Whnf/Projection/StringCallback.lean rename to Ix/Kernel/Verify/Whnf/Projection/StringCallback.lean index eab362128..8d3b1ffbc 100644 --- a/Ix/Tc/Verify/Whnf/Projection/StringCallback.lean +++ b/Ix/Kernel/Verify/Whnf/Projection/StringCallback.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Projection.NoAccelTail +import Ix.Kernel.Verify.Whnf.Projection.NoAccelTail /-! # Projection String callback closure @@ -10,7 +10,7 @@ interned constructor expansion itself: finite support and structural translation of the exact generated term. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM attribute [local irreducible] whnfRec strLitToConstructor @@ -82,4 +82,4 @@ theorem noAccelOfExpansion end ProjectionHelper end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Projection/StringExpansion.lean b/Ix/Kernel/Verify/Whnf/Projection/StringExpansion.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/Projection/StringExpansion.lean rename to Ix/Kernel/Verify/Whnf/Projection/StringExpansion.lean index 1cc3a1b50..ad7a264f4 100644 --- a/Ix/Tc/Verify/Whnf/Projection/StringExpansion.lean +++ b/Ix/Kernel/Verify/Whnf/Projection/StringExpansion.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Projection.StringCallback +import Ix.Kernel.Verify.Whnf.Projection.StringCallback /-! # Finite String-constructor expansion plan @@ -10,7 +10,7 @@ collisions are excluded on the run domain, and the final generated term has a structural Theory translation. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM def stringCharConst (p : Primitives .anon) : KExpr .anon := @@ -376,4 +376,4 @@ theorem noAccelOfStringPlans end ProjectionHelper end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/README.md b/Ix/Kernel/Verify/Whnf/README.md similarity index 97% rename from Ix/Tc/Verify/Whnf/README.md rename to Ix/Kernel/Verify/Whnf/README.md index 5a8806528..f204baea5 100644 --- a/Ix/Tc/Verify/Whnf/README.md +++ b/Ix/Kernel/Verify/Whnf/README.md @@ -30,4 +30,4 @@ precise proof-dependency order needed by Lean. the boundary with the later `infer`/`isDefEq` closure work. The foundational semantic definitions remain in the sibling module -`Ix.Tc.Verify.Whnf` (`../Whnf.lean`). +`Ix.Kernel.Verify.Whnf` (`../Whnf.lean`). diff --git a/Ix/Tc/Verify/Whnf/Runtime/LazyIngress.lean b/Ix/Kernel/Verify/Whnf/Runtime/LazyIngress.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/Runtime/LazyIngress.lean rename to Ix/Kernel/Verify/Whnf/Runtime/LazyIngress.lean index 568254423..99045d5ce 100644 --- a/Ix/Tc/Verify/Whnf/Runtime/LazyIngress.lean +++ b/Ix/Kernel/Verify/Whnf/Runtime/LazyIngress.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Whnf.Projection.StringExpansion -import Ix.Tc.Ingress +import Ix.Kernel.Verify.Whnf.Projection.StringExpansion +import Ix.Kernel.Ingress /-! # Concrete anonymous lazy-ingress refinement @@ -17,7 +17,7 @@ frame. A separate installed-hook premise identifies the otherwise arbitrary function stored in `TcState`. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Exact environment facts needed after one lazy-ingress callback. @@ -324,4 +324,4 @@ theorem noAccelOfAnonIngress end RecM.ProjectionHelper -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/RuntimeContracts.lean b/Ix/Kernel/Verify/Whnf/RuntimeContracts.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/RuntimeContracts.lean rename to Ix/Kernel/Verify/Whnf/RuntimeContracts.lean index 04bdf6ff3..47ae3bf13 100644 --- a/Ix/Tc/Verify/Whnf/RuntimeContracts.lean +++ b/Ix/Kernel/Verify/Whnf/RuntimeContracts.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf +import Ix.Kernel.Verify.Whnf /-! # Closing the remaining WHNF runtime contracts @@ -8,7 +8,7 @@ for eagerly ingressed states and exposes the strictly smaller lazy-ingress obligation needed by the same proof in driver-backed states. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace TcM @@ -432,4 +432,4 @@ end TransientNatWork end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/StructEta/CallbackPrefix.lean b/Ix/Kernel/Verify/Whnf/StructEta/CallbackPrefix.lean similarity index 96% rename from Ix/Tc/Verify/Whnf/StructEta/CallbackPrefix.lean rename to Ix/Kernel/Verify/Whnf/StructEta/CallbackPrefix.lean index d639f48a7..884b7be7f 100644 --- a/Ix/Tc/Verify/Whnf/StructEta/CallbackPrefix.lean +++ b/Ix/Kernel/Verify/Whnf/StructEta/CallbackPrefix.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.StructEta.Rebuild +import Ix.Kernel.Verify.Whnf.StructEta.Rebuild /-! # Struct-eta callback-prefix preservation @@ -11,7 +11,7 @@ full fixed-world invariant on success and error; they do not claim that a caught callback is state-pure. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace TcM @@ -148,7 +148,7 @@ theorem inferOnlyRec_wf {uvars : Nat} {Delta : KVLCtx} {methods : Methods .anon} (hmethods : Methods.WFAt layer semantics trProj world support uvars methods) - {s : TcState .anon} {e : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {s : TcState .anon} {e : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsource : support e) (htr : TrKExprS world.venv uvars world.nameOf trProj Delta e sourceV) : TcM.WF (WhnfStateInv layer semantics trProj world support uvars Delta) s @@ -168,7 +168,7 @@ theorem tryOptionalInferOnlyRec_wf {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} - {s : TcState .anon} {e : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {s : TcState .anon} {e : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsource : support e) (htr : TrKExprS world.venv uvars world.nameOf trProj Delta e sourceV) : RecM.WF layer semantics trProj world support uvars Delta s @@ -196,7 +196,7 @@ theorem tryOptionalWhnfRec_wf {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} - {s : TcState .anon} {e : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {s : TcState .anon} {e : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} (hsource : support e) (htr : TrKExprS world.venv uvars world.nameOf trProj Delta e sourceV) : RecM.WF layer semantics trProj world support uvars Delta s @@ -220,4 +220,4 @@ theorem tryOptionalWhnfRec_wf exact herror end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/StructEta/Classifier.lean b/Ix/Kernel/Verify/Whnf/StructEta/Classifier.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/StructEta/Classifier.lean rename to Ix/Kernel/Verify/Whnf/StructEta/Classifier.lean index 250da9a2e..bc4ef4452 100644 --- a/Ix/Tc/Verify/Whnf/StructEta/Classifier.lean +++ b/Ix/Kernel/Verify/Whnf/StructEta/Classifier.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.StructEta.RecursionClassifier +import Ix.Kernel.Verify.Whnf.StructEta.RecursionClassifier /-! # Struct-eta classifier state closure @@ -14,7 +14,7 @@ one constructor and no indices is not, by itself, a Theory proof of the struct-eta equation selected later in the reducer. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Exact typed/effect input for the recursor declaration instance scanned by @@ -174,7 +174,7 @@ theorem tryStructEtaAfterInductive_wf (hfault : TcM.LazyFaultPreserves (WhnfStateInv layer semantics trProj world support uvars Delta)) (hwrites : IsRecCacheWriteOracle semantics world support methods indId) - {majorV : Lean4Lean.VExpr} + {majorV : Ix.Theory.Named.VExpr} (hmajorSupport : support spine[recr.majorIdx]!) (hmajorTr : TrKExprS world.venv uvars world.nameOf trProj Delta spine[recr.majorIdx]! majorV) @@ -345,7 +345,7 @@ theorem tryStructEtaIota_wf (hreferences : TrustedReferences world support) (hwrites : ∀ id, world.trusted id → IsRecCacheWriteOracle semantics world support methods id) - {majorV : Lean4Lean.VExpr} + {majorV : Ix.Theory.Named.VExpr} (hmajorSupport : support spine[recr.majorIdx]!) (hmajorTr : TrKExprS world.venv uvars world.nameOf trProj Delta spine[recr.majorIdx]! majorV) @@ -363,4 +363,4 @@ theorem tryStructEtaIota_wf (hwrites indId htrusted) hmajorSupport hmajorTr hfinish end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/StructEta/ExactMajorTelescope.lean b/Ix/Kernel/Verify/Whnf/StructEta/ExactMajorTelescope.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/StructEta/ExactMajorTelescope.lean rename to Ix/Kernel/Verify/Whnf/StructEta/ExactMajorTelescope.lean index d05486bb5..f23a030ac 100644 --- a/Ix/Tc/Verify/Whnf/StructEta/ExactMajorTelescope.lean +++ b/Ix/Kernel/Verify/Whnf/StructEta/ExactMajorTelescope.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.StructEta.ScopedTelescope +import Ix.Kernel.Verify.Whnf.StructEta.ScopedTelescope /-! # Exact major-premise telescope restoration @@ -21,7 +21,7 @@ This module proves that fact from: No semantic state model is widened to admit the temporary telescope. -/ -namespace Ix.Tc +namespace Ix.Kernel /-- Exact state stack generated exclusively by successful `pushLocal` calls. Unlike `ScratchLamExtension`, this relation retains the operational @@ -249,4 +249,4 @@ theorem getMajorInductiveId_direct_exact exact scratch_tryFinally_ok bodyRun cleanup end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/StructEta/Rebuild.lean b/Ix/Kernel/Verify/Whnf/StructEta/Rebuild.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/StructEta/Rebuild.lean rename to Ix/Kernel/Verify/Whnf/StructEta/Rebuild.lean index dce3929fb..2af833370 100644 --- a/Ix/Tc/Verify/Whnf/StructEta/Rebuild.lean +++ b/Ix/Kernel/Verify/Whnf/StructEta/Rebuild.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Iota.StructEtaControl +import Ix.Kernel.Verify.Whnf.Iota.StructEtaControl /-! # Finite struct-eta rebuild closure @@ -15,7 +15,7 @@ that the selected recursor rule is a registered Theory equation or that raw projections have the required interpretation. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Finite request certificate for the projection/application pairs generated @@ -213,4 +213,4 @@ theorem acceptance_of_requests end StructEtaIotaSuccessTrace end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/StructEta/RebuildRequests.lean b/Ix/Kernel/Verify/Whnf/StructEta/RebuildRequests.lean similarity index 97% rename from Ix/Tc/Verify/Whnf/StructEta/RebuildRequests.lean rename to Ix/Kernel/Verify/Whnf/StructEta/RebuildRequests.lean index 873c87405..c373b5fb9 100644 --- a/Ix/Tc/Verify/Whnf/StructEta/RebuildRequests.lean +++ b/Ix/Kernel/Verify/Whnf/StructEta/RebuildRequests.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Iota.ApplicationRequests +import Ix.Kernel.Verify.Whnf.Iota.ApplicationRequests /-! # Finite request closure for the struct-eta rebuild tail @@ -12,7 +12,7 @@ recursor and spine. The inference probes are derived from `Methods.WF`; only the helper-scan and recursion-cache authorities remain. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- The exact finite requests for one successful struct-eta H3 tail. -/ @@ -85,7 +85,7 @@ theorem of_components (hreferences : TrustedReferences world support) (hwrites : ∀ id, world.trusted id → IsRecCacheWriteOracle semantics world support methods id) - {majorV : Lean4Lean.VExpr} + {majorV : Ix.Theory.Named.VExpr} (hmajorSupport : support spine[recr.majorIdx]!) (hmajorTr : TrKExprS world.venv uvars world.nameOf trProj Delta spine[recr.majorIdx]! majorV) : @@ -125,7 +125,7 @@ theorem tryIotaAfterMajorWhnf_state_wf_of_contexts IsRecCacheWriteOracle semantics world support methods id) {flags : WhnfFlags} {recId : KId .anon} {recr : IotaInfo .anon} {recUs : Array (KUniv .anon)} {spine : Array (KExpr .anon)} - {majorV : Lean4Lean.VExpr} + {majorV : Ix.Theory.Named.VExpr} (hmajorSupport : support spine[recr.majorIdx]!) (hmajorTr : TrKExprS world.venv uvars world.nameOf trProj Delta spine[recr.majorIdx]! majorV) @@ -142,4 +142,4 @@ theorem tryIotaAfterMajorWhnf_state_wf_of_contexts hctorInputs hrecInputs hfault hreferences hwrites hmajorSupport hmajorTr) end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/StructEta/RebuildTail.lean b/Ix/Kernel/Verify/Whnf/StructEta/RebuildTail.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/StructEta/RebuildTail.lean rename to Ix/Kernel/Verify/Whnf/StructEta/RebuildTail.lean index 6eb618e19..ff2231721 100644 --- a/Ix/Tc/Verify/Whnf/StructEta/RebuildTail.lean +++ b/Ix/Kernel/Verify/Whnf/StructEta/RebuildTail.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.StructEta.Classifier +import Ix.Kernel.Verify.Whnf.StructEta.Classifier /-! # Struct-eta universe and rebuild tail @@ -10,7 +10,7 @@ invariant on success and partial-state error, and a successful RHS is rebuilt only through request-certified projection/application interning. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace TcM @@ -148,4 +148,4 @@ theorem finishStructEtaAfterSort_wf_of_requests exact TcM.WF.pure fun _ => trivial end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/StructEta/RecursionClassifier.lean b/Ix/Kernel/Verify/Whnf/StructEta/RecursionClassifier.lean similarity index 99% rename from Ix/Tc/Verify/Whnf/StructEta/RecursionClassifier.lean rename to Ix/Kernel/Verify/Whnf/StructEta/RecursionClassifier.lean index ac04231ff..f667d6cac 100644 --- a/Ix/Tc/Verify/Whnf/StructEta/RecursionClassifier.lean +++ b/Ix/Kernel/Verify/Whnf/StructEta/RecursionClassifier.lean @@ -1,5 +1,5 @@ import Init.Data.Range.Lemmas -import Ix.Tc.Verify.Whnf.StructEta.ScopedClassifier +import Ix.Kernel.Verify.Whnf.StructEta.ScopedClassifier /-! # Recursion-classifier and major-inductive helper effects @@ -15,7 +15,7 @@ re-entrant eta. Physical insertion alone is therefore not evidence that either Boolean is semantically valid. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -454,7 +454,7 @@ theorem getMajorInductiveId_wf TcM.LazyFaultPreserves (WhnfStateInv layer semantics trProj world support uvars Delta)) (hreferences : TrustedReferences world support) - {Delta : KVLCtx} {recTy : KExpr .anon} {recTyV : Lean4Lean.VExpr} + {Delta : KVLCtx} {recTy : KExpr .anon} {recTyV : Ix.Theory.Named.VExpr} {s : TcState .anon} (skip : UInt64) (hrecSupport : support recTy) (hrecTr : @@ -516,7 +516,7 @@ theorem getMajorInductiveId_trusted_wf TcM.LazyFaultPreserves (WhnfStateInv layer semantics trProj world support uvars Delta)) (hreferences : TrustedReferences world support) - {Delta : KVLCtx} {recTy : KExpr .anon} {recTyV : Lean4Lean.VExpr} + {Delta : KVLCtx} {recTy : KExpr .anon} {recTyV : Ix.Theory.Named.VExpr} {s : TcState .anon} (skip : UInt64) (hrecSupport : support recTy) (hrecTr : @@ -803,4 +803,4 @@ theorem computedIsRec_wf end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/StructEta/ScopedClassifier.lean b/Ix/Kernel/Verify/Whnf/StructEta/ScopedClassifier.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/StructEta/ScopedClassifier.lean rename to Ix/Kernel/Verify/Whnf/StructEta/ScopedClassifier.lean index 1e0a0fc13..b69bee7de 100644 --- a/Ix/Tc/Verify/Whnf/StructEta/ScopedClassifier.lean +++ b/Ix/Kernel/Verify/Whnf/StructEta/ScopedClassifier.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.StructEta.ScopedTelescope +import Ix.Kernel.Verify.Whnf.StructEta.ScopedTelescope /-! # Scoped recursion-classifier steps @@ -9,7 +9,7 @@ results and partial-error states before the complete classifier loop is assembled. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM def ScratchScopedForInStep @@ -50,7 +50,7 @@ theorem scratch_computeIsRecParamStep_scoped Methods.WFAt layer semantics trProj world support uvars methods) (hinputs : ScratchTelescopeInputSupport support) {base : KVLCtx} {n : Nat} {current : KVLCtx} - {ty : KExpr .anon} {tyV : Lean4Lean.VExpr} {s : TcState .anon} + {ty : KExpr .anon} {tyV : Ix.Theory.Named.VExpr} {s : TcState .anon} (hExtension : ScratchLamExtension base n current) (hsupport : support ty) (htr : TrKExprS world.venv uvars world.nameOf trProj current ty tyV) @@ -122,7 +122,7 @@ theorem scratch_computeIsRecFieldStep_scoped (hinputs : ScratchTelescopeInputSupport support) {base : KVLCtx} {n : Nat} {current : KVLCtx} {blockAddrs : Array Address} - {ty : KExpr .anon} {tyV : Lean4Lean.VExpr} {s : TcState .anon} + {ty : KExpr .anon} {tyV : Ix.Theory.Named.VExpr} {s : TcState .anon} (hExtension : ScratchLamExtension base n current) (hsupport : support ty) (htr : TrKExprS world.venv uvars world.nameOf trProj current ty tyV) @@ -365,7 +365,7 @@ theorem scratch_computeIsRecCtorBody_scoped Methods.WFAt layer semantics trProj world support uvars methods) (hinputs : ScratchTelescopeInputSupport support) {base : KVLCtx} {ctorTy : KExpr .anon} - {ctorTyV : Lean4Lean.VExpr} {s : TcState .anon} + {ctorTyV : Ix.Theory.Named.VExpr} {s : TcState .anon} (nParams : Nat) (blockAddrs : Array Address) (hctorSupport : support ctorTy) (hctorTr : @@ -443,7 +443,7 @@ theorem scratch_computeIsRecCtor_wf Methods.WFAt layer semantics trProj world support uvars methods) (hinputs : ScratchTelescopeInputSupport support) {Delta : KVLCtx} {ctorTy : KExpr .anon} - {ctorTyV : Lean4Lean.VExpr} {s : TcState .anon} + {ctorTyV : Ix.Theory.Named.VExpr} {s : TcState .anon} (nParams : Nat) (blockAddrs : Array Address) (hctorSupport : support ctorTy) (hctorTr : @@ -488,4 +488,4 @@ theorem scratch_computeIsRecCtor_wf exact ⟨hFinal, trivial⟩ end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/StructEta/ScopedTelescope.lean b/Ix/Kernel/Verify/Whnf/StructEta/ScopedTelescope.lean similarity index 97% rename from Ix/Tc/Verify/Whnf/StructEta/ScopedTelescope.lean rename to Ix/Kernel/Verify/Whnf/StructEta/ScopedTelescope.lean index 9931a5689..6d243c270 100644 --- a/Ix/Tc/Verify/Whnf/StructEta/ScopedTelescope.lean +++ b/Ix/Kernel/Verify/Whnf/StructEta/ScopedTelescope.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.StructEta.CallbackPrefix +import Ix.Kernel.Verify.Whnf.StructEta.CallbackPrefix /-! # Scoped telescope state @@ -9,7 +9,7 @@ ambient WHNF state invariant while tracking the temporary lambda extension of the caller's context. -/ -namespace Ix.Tc +namespace Ix.Kernel theorem scratch_pushLocal_run {ty : KExpr .anon} {s s' : TcState .anon} @@ -35,7 +35,7 @@ theorem scratch_pushLocal_inv {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s s' : TcState .anon} - {ty : KExpr .anon} {tyV : Lean4Lean.VExpr} + {ty : KExpr .anon} {tyV : Ix.Theory.Named.VExpr} (hI : WhnfStateInv layer semantics trProj world support uvars Delta s) (htr : TrKExprS world.venv uvars world.nameOf trProj Delta ty tyV) (htype : world.venv.IsType uvars Delta.toCtx tyV) @@ -60,9 +60,9 @@ theorem scratch_pushLocal_inv simpa only [WhnfLayer.StateOK, hprims] using hI.2.2 theorem scratch_lam_back - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} - {s : TcState .anon} {Delta : KVLCtx} {tyV : Lean4Lean.VExpr} + {s : TcState .anon} {Delta : KVLCtx} {tyV : Ix.Theory.Named.VExpr} (h : CtxRecon env uvars nameOf trProj s ((none, .vlam tyV) :: Delta)) : s.letVals.back? = some none := by @@ -75,9 +75,9 @@ theorem scratch_lam_back Array.getLast?_toList] using hhead theorem scratch_popLocal_run - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address → Option Lean.Name} {trProj : RawProjRel} - {s s' : TcState .anon} {Delta : KVLCtx} {tyV : Lean4Lean.VExpr} + {s s' : TcState .anon} {Delta : KVLCtx} {tyV : Ix.Theory.Named.VExpr} (hctxRecon : CtxRecon env uvars nameOf trProj s ((none, .vlam tyV) :: Delta)) (hrun : TcM.popLocal s = .ok () s') : @@ -104,7 +104,7 @@ theorem scratch_popLocal_inv {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s s' : TcState .anon} - {tyV : Lean4Lean.VExpr} + {tyV : Ix.Theory.Named.VExpr} (hI : WhnfStateInv layer semantics trProj world support uvars ((none, .vlam tyV) :: Delta) s) (hrun : TcM.popLocal s = .ok () s') : @@ -128,7 +128,7 @@ theorem scratch_popLocal_inv inductive ScratchLamExtension (base : KVLCtx) : Nat → KVLCtx → Prop | zero : ScratchLamExtension base 0 base - | succ {n : Nat} {current : KVLCtx} {tyV : Lean4Lean.VExpr} : + | succ {n : Nat} {current : KVLCtx} {tyV : Ix.Theory.Named.VExpr} : ScratchLamExtension base n current → ScratchLamExtension base (n + 1) ((none, .vlam tyV) :: current) @@ -581,7 +581,7 @@ theorem scratch_majorInductiveBody_scoped TcM.LazyFaultPreserves (WhnfStateInv layer semantics trProj world support uvars Delta)) (hreferences : ScratchTrustedReferences world support) - {base : KVLCtx} {recTy : KExpr .anon} {recTyV : Lean4Lean.VExpr} + {base : KVLCtx} {recTy : KExpr .anon} {recTyV : Ix.Theory.Named.VExpr} {s : TcState .anon} (skip : UInt64) (hrecSupport : support recTy) (hrecTr : TrKExprS world.venv uvars world.nameOf trProj base recTy recTyV) @@ -669,7 +669,7 @@ theorem scratch_getMajorInductiveId_wf TcM.LazyFaultPreserves (WhnfStateInv layer semantics trProj world support uvars Delta)) (hreferences : ScratchTrustedReferences world support) - {Delta : KVLCtx} {recTy : KExpr .anon} {recTyV : Lean4Lean.VExpr} + {Delta : KVLCtx} {recTy : KExpr .anon} {recTyV : Ix.Theory.Named.VExpr} {s : TcState .anon} (skip : UInt64) (hrecSupport : support recTy) (hrecTr : @@ -710,4 +710,4 @@ theorem scratch_getMajorInductiveId_wf exact ⟨hFinal, trivial⟩ end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Structural/ApplicationCongruence.lean b/Ix/Kernel/Verify/Whnf/Structural/ApplicationCongruence.lean similarity index 92% rename from Ix/Tc/Verify/Whnf/Structural/ApplicationCongruence.lean rename to Ix/Kernel/Verify/Whnf/Structural/ApplicationCongruence.lean index 1778c5981..40fa133e3 100644 --- a/Ix/Tc/Verify/Whnf/Structural/ApplicationCongruence.lean +++ b/Ix/Kernel/Verify/Whnf/Structural/ApplicationCongruence.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Structural.ProjectionStep +import Ix.Kernel.Verify.Whnf.Structural.ProjectionStep /-! # Changed-head application congruence @@ -14,7 +14,7 @@ suffix. A `FinishAppRequests` certificate then identifies the semantic left fold with the expression actually returned by `finishAppResult`. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM namespace TrAppSpine @@ -23,10 +23,10 @@ namespace TrAppSpine suffix. Unlike `headTr`, this keeps the chosen head translation connected to all argument typing derivations. -/ theorem toSuffix - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address -> Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {head : KExpr .anon} - {args : List (KExpr .anon)} {resultV : Lean4Lean.VExpr} + {args : List (KExpr .anon)} {resultV : Ix.Theory.Named.VExpr} (h : TrAppSpine env uvars nameOf trProj Delta head args resultV) : exists headV, TrKExprS env uvars nameOf trProj Delta head headV /\ @@ -46,7 +46,7 @@ theorem applicationHeadCallbackWithSuffix_wf {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {f arg head : KExpr .anon} {info : ExprInfo .anon} - {args : Array (KExpr .anon)} {sourceV : Lean4Lean.VExpr} + {args : Array (KExpr .anon)} {sourceV : Ix.Theory.Named.VExpr} {flags : WhnfFlags} (hinputs : WhnfCoreInputSupport support) (hsupport : support (.app f arg info)) @@ -77,7 +77,7 @@ result. -/ theorem appHeadRebuild {trProj : RawProjRel} {world : VerifyWorld} {uvars : Nat} {Delta : KVLCtx} {source changed rebuilt : KExpr .anon} - {args : Array (KExpr .anon)} {sourceV headV : Lean4Lean.VExpr} + {args : Array (KExpr .anon)} {sourceV headV : Ix.Theory.Named.VExpr} {requests : List WalkerRequest} (hDelta : KVLCtx.WF world.venv uvars Delta) (hsource : TrKExprS world.venv uvars world.nameOf trProj Delta source @@ -99,4 +99,4 @@ theorem appHeadRebuild end WhnfMeaning end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Structural/ApplicationRebuild.lean b/Ix/Kernel/Verify/Whnf/Structural/ApplicationRebuild.lean similarity index 94% rename from Ix/Tc/Verify/Whnf/Structural/ApplicationRebuild.lean rename to Ix/Kernel/Verify/Whnf/Structural/ApplicationRebuild.lean index 92a536dde..bcfb2c8a5 100644 --- a/Ix/Tc/Verify/Whnf/Structural/ApplicationRebuild.lean +++ b/Ix/Kernel/Verify/Whnf/Structural/ApplicationRebuild.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Structural.ApplicationCongruence +import Ix.Kernel.Verify.Whnf.Structural.ApplicationCongruence /-! # Changed-head rebuild execution @@ -10,7 +10,7 @@ head callback. The guard keeps the obligation finite while covering the dynamic callback result rather than one hand-picked expression. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Finite request census for rebuilding the complete argument suffix after @@ -39,7 +39,7 @@ theorem changedHeadFinish_acceptance {trProj : RawProjRel} {world : VerifyWorld} {uvars : Nat} {Delta : KVLCtx} {methods : Methods .anon} {s : TcState .anon} {f arg head changed : KExpr .anon} {info : ExprInfo .anon} - {args : Array (KExpr .anon)} {sourceV headV : Lean4Lean.VExpr} + {args : Array (KExpr .anon)} {sourceV headV : Ix.Theory.Named.VExpr} (hsourceSupport : support (.app f arg info)) (hsource : TrKExprS world.venv uvars world.nameOf trProj Delta (.app f arg info) sourceV) @@ -68,4 +68,4 @@ theorem changedHeadFinish_acceptance hrebuiltSupport, hmeaning⟩ end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Structural/ApplicationStep.lean b/Ix/Kernel/Verify/Whnf/Structural/ApplicationStep.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Structural/ApplicationStep.lean rename to Ix/Kernel/Verify/Whnf/Structural/ApplicationStep.lean index 2e0af9769..5688fd2c8 100644 --- a/Ix/Tc/Verify/Whnf/Structural/ApplicationStep.lean +++ b/Ix/Kernel/Verify/Whnf/Structural/ApplicationStep.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Structural.BetaBoundary +import Ix.Kernel.Verify.Whnf.Structural.BetaBoundary /-! # Exhaustive application-step closure @@ -14,7 +14,7 @@ It uses the run's collision-freedom certificate over the supported callback result and original head before rewriting the callback equation. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Every outcome of the production application branch satisfies the local @@ -118,4 +118,4 @@ theorem whnfCoreWithFlagsStep_app_wf | str => exact hnonLambda .str hI end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Structural/ApplicationTails.lean b/Ix/Kernel/Verify/Whnf/Structural/ApplicationTails.lean similarity index 97% rename from Ix/Tc/Verify/Whnf/Structural/ApplicationTails.lean rename to Ix/Kernel/Verify/Whnf/Structural/ApplicationTails.lean index 669994afe..4127fe455 100644 --- a/Ix/Tc/Verify/Whnf/Structural/ApplicationTails.lean +++ b/Ix/Kernel/Verify/Whnf/Structural/ApplicationTails.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Structural.ApplicationRebuild +import Ix.Kernel.Verify.Whnf.Structural.ApplicationRebuild /-! # Non-beta application tails @@ -11,7 +11,7 @@ invokes iota on that rebuilt source. In both cases the ordinary error states. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Generic unchanged-head/iota-hit equation. The older specialized theorem @@ -62,7 +62,7 @@ theorem whnfCoreWithFlagsStep_appUnchanged_wf (hmethods : Methods.WFAt layer semantics trProj world support uvars methods) (hsourceSupport : support (.app f arg info)) - {sourceV : Lean4Lean.VExpr} + {sourceV : Ix.Theory.Named.VExpr} (hsource : TrKExprS world.venv uvars world.nameOf trProj Delta (.app f arg info) sourceV) (hspine : (.app f arg info : KExpr .anon).collectSpine = (head, args)) @@ -112,7 +112,7 @@ theorem whnfCoreWithFlagsStep_appChanged_wf {Delta : KVLCtx} {methods : Methods .anon} {s s1 : TcState .anon} {f arg head changed : KExpr .anon} {info : ExprInfo .anon} {args : Array (KExpr .anon)} - {sourceV headV : Lean4Lean.VExpr} {flags : WhnfFlags} + {sourceV headV : Ix.Theory.Named.VExpr} {flags : WhnfFlags} (theory : WhnfTheory trProj world uvars) (hiota : OptionalReduction.WF layer semantics trProj world support (fun source => tryIotaWithFlags source flags)) @@ -165,4 +165,4 @@ theorem whnfCoreWithFlagsStep_appChanged_wf exact ⟨hiotaPost.1, hiotaPost.2.1, hmeaning⟩ end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Structural/BasicStep.lean b/Ix/Kernel/Verify/Whnf/Structural/BasicStep.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Structural/BasicStep.lean rename to Ix/Kernel/Verify/Whnf/Structural/BasicStep.lean index 7243dc066..f9d33dd5c 100644 --- a/Ix/Tc/Verify/Whnf/Structural/BasicStep.lean +++ b/Ix/Kernel/Verify/Whnf/Structural/BasicStep.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Structural.CacheShell +import Ix.Kernel.Verify.Whnf.Structural.CacheShell /-! # Basic structural-step closure @@ -14,7 +14,7 @@ stack, and within the current weakening bound. Naming this state obligation prevents a translated-but-stale local value from being accepted silently. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Runtime safety needed by production's unchanged let-fvar return. This @@ -161,4 +161,4 @@ theorem whnfCoreWithFlagsStep_basic_wf | letE => exact whnfCoreWithFlagsStep_letE_wf hrun hcensus theory end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Structural/BetaBoundary.lean b/Ix/Kernel/Verify/Whnf/Structural/BetaBoundary.lean similarity index 96% rename from Ix/Tc/Verify/Whnf/Structural/BetaBoundary.lean rename to Ix/Kernel/Verify/Whnf/Structural/BetaBoundary.lean index 39c32284a..5c86dcf30 100644 --- a/Ix/Tc/Verify/Whnf/Structural/BetaBoundary.lean +++ b/Ix/Kernel/Verify/Whnf/Structural/BetaBoundary.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Structural.ApplicationTails +import Ix.Kernel.Verify.Whnf.Structural.ApplicationTails /-! # General beta branch boundary and execution @@ -15,7 +15,7 @@ the substitution bounds to the final certified rebuild. No state effect, support fact, or production execution is hidden in that interface. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Finite request census for every dynamic multi-beta branch reachable from @@ -50,7 +50,7 @@ def BetaManyMeaningOracle (trProj : RawProjRel) (world : VerifyWorld) : Prop := {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {ty body body0 : KExpr .anon} {lamInfo : ExprInfo .anon} {consumed : Array (KExpr .anon)} {result : KExpr .anon} - {sourceV headV : Lean4Lean.VExpr}, + {sourceV headV : Ix.Theory.Named.VExpr}, KVLCtx.WF world.venv uvars Delta -> TrKExprS world.venv uvars world.nameOf trProj Delta (.app f arg info) sourceV -> @@ -82,7 +82,7 @@ theorem whnfCoreWithFlagsStep_appBeta_wf {info : ExprInfo .anon} {args : Array (KExpr .anon)} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {ty body body0 : KExpr .anon} {lamInfo : ExprInfo .anon} - {consumed : Array (KExpr .anon)} {sourceV headV : Lean4Lean.VExpr} + {consumed : Array (KExpr .anon)} {sourceV headV : Ix.Theory.Named.VExpr} {flags : WhnfFlags} (theory : WhnfTheory trProj world uvars) (hmeaning : BetaManyMeaningOracle trProj world) @@ -125,4 +125,4 @@ theorem whnfCoreWithFlagsStep_appBeta_wf exact ⟨hI3, hresultSupport, hresultMeaning⟩ end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Structural/CacheShell.lean b/Ix/Kernel/Verify/Whnf/Structural/CacheShell.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Structural/CacheShell.lean rename to Ix/Kernel/Verify/Whnf/Structural/CacheShell.lean index 7a43392c4..776a977ca 100644 --- a/Ix/Tc/Verify/Whnf/Structural/CacheShell.lean +++ b/Ix/Kernel/Verify/Whnf/Structural/CacheShell.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Whnf.StructEta.RebuildTail -import Ix.Tc.Verify.Suffix +import Ix.Kernel.Verify.Whnf.StructEta.RebuildTail +import Ix.Kernel.Verify.Suffix /-! # Structural-core cache shell @@ -17,7 +17,7 @@ constructed, this file turns it into the complete public Nat bypass, and the legacy-variable prefix. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM @@ -125,7 +125,7 @@ theorem whnfCoreWithFlagsNonLeaf_wf stepError) (hwrites : WhnfCoreCacheWriteOracle keys trProj fallback world support) (hsupport : support source) - {sourceV : Lean4Lean.VExpr} {s : TcState .anon} + {sourceV : Ix.Theory.Named.VExpr} {s : TcState .anon} (hsource : TrKExprS world.venv keys.uvars world.nameOf trProj Delta source sourceV) : RecM.WF layer (whnfCacheSemantics keys trProj fallback) trProj world @@ -275,7 +275,7 @@ theorem whnfCoreWithFlags_wf stepError) (hwrites : WhnfCoreCacheWriteOracle keys trProj fallback world support) (hsupport : support source) - {sourceV : Lean4Lean.VExpr} {s : TcState .anon} + {sourceV : Ix.Theory.Named.VExpr} {s : TcState .anon} (hsource : TrKExprS world.venv keys.uvars world.nameOf trProj Delta source sourceV) : RecM.WF layer (whnfCacheSemantics keys trProj fallback) trProj world @@ -389,4 +389,4 @@ theorem coreCacheWriteOracle end WhnfSuffixModel -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Structural/ProjectionStep.lean b/Ix/Kernel/Verify/Whnf/Structural/ProjectionStep.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Structural/ProjectionStep.lean rename to Ix/Kernel/Verify/Whnf/Structural/ProjectionStep.lean index c717b7bdb..5cb237693 100644 --- a/Ix/Tc/Verify/Whnf/Structural/ProjectionStep.lean +++ b/Ix/Kernel/Verify/Whnf/Structural/ProjectionStep.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Structural.RecursiveCallbacks +import Ix.Kernel.Verify.Whnf.Structural.RecursiveCallbacks /-! # Exhaustive projection-step closure @@ -18,7 +18,7 @@ with `InductiveReductionOracle`; a syntax-directed helper execution alone is not treated as a Theory projection equation. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM namespace ProjectionHelper @@ -152,4 +152,4 @@ theorem whnfCoreWithFlagsStep_basicVarProjection_wf horacle end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Structural/RecursiveCallbacks.lean b/Ix/Kernel/Verify/Whnf/Structural/RecursiveCallbacks.lean similarity index 93% rename from Ix/Tc/Verify/Whnf/Structural/RecursiveCallbacks.lean rename to Ix/Kernel/Verify/Whnf/Structural/RecursiveCallbacks.lean index 708bcf076..cac2111cc 100644 --- a/Ix/Tc/Verify/Whnf/Structural/RecursiveCallbacks.lean +++ b/Ix/Kernel/Verify/Whnf/Structural/RecursiveCallbacks.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Structural.VariableStep +import Ix.Kernel.Verify.Whnf.Structural.VariableStep /-! # Structural recursive-callback closure @@ -16,7 +16,7 @@ only a support boundary; semantic translation of each child is derived from the translated parent. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Finite support closure needed by one structural-WHNF iteration. The app @@ -38,7 +38,7 @@ theorem whnfCoreFlagsRec_wf {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} - {source : KExpr .anon} {sourceV : Lean4Lean.VExpr} + {source : KExpr .anon} {sourceV : Ix.Theory.Named.VExpr} {flags : WhnfFlags} (hsource : support source) (htr : TrKExprS world.venv uvars world.nameOf trProj Delta source @@ -55,10 +55,10 @@ namespace TrAppSpine /-- A typed application spine retains the translation of its raw head. -/ theorem headTr - {env : Lean4Lean.VEnv} {uvars : Nat} + {env : Ix.Theory.Named.VEnv} {uvars : Nat} {nameOf : Address -> Option Lean.Name} {trProj : RawProjRel} {Delta : KVLCtx} {head : KExpr .anon} - {args : List (KExpr .anon)} {resultV : Lean4Lean.VExpr} + {args : List (KExpr .anon)} {resultV : Ix.Theory.Named.VExpr} (h : TrAppSpine env uvars nameOf trProj Delta head args resultV) : exists headV, TrKExprS env uvars nameOf trProj Delta head headV := by @@ -76,7 +76,7 @@ theorem projectionValueCallback_wf {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {id : KId .anon} {field : UInt64} {value : KExpr .anon} - {info : ExprInfo .anon} {sourceV : Lean4Lean.VExpr} + {info : ExprInfo .anon} {sourceV : Ix.Theory.Named.VExpr} {flags : WhnfFlags} (hinputs : WhnfCoreInputSupport support) (hsupport : support (.prj id field value info)) @@ -109,7 +109,7 @@ theorem applicationHeadCallback_wf {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} {f arg head : KExpr .anon} {info : ExprInfo .anon} - {args : Array (KExpr .anon)} {sourceV : Lean4Lean.VExpr} + {args : Array (KExpr .anon)} {sourceV : Ix.Theory.Named.VExpr} {flags : WhnfFlags} (hinputs : WhnfCoreInputSupport support) (hsupport : support (.app f arg info)) @@ -142,4 +142,4 @@ theorem applicationArgument_support (hinputs.app hsupport hspine).2 child hmem end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Structural/Reducer.lean b/Ix/Kernel/Verify/Whnf/Structural/Reducer.lean similarity index 97% rename from Ix/Tc/Verify/Whnf/Structural/Reducer.lean rename to Ix/Kernel/Verify/Whnf/Structural/Reducer.lean index adc6d8fce..d4e432ab8 100644 --- a/Ix/Tc/Verify/Whnf/Structural/Reducer.lean +++ b/Ix/Kernel/Verify/Whnf/Structural/Reducer.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Verify.Whnf.Structural.VerifiedStep -import Ix.Tc.Verify.Whnf.Iota.OptionalReduction +import Ix.Kernel.Verify.Whnf.Structural.VerifiedStep +import Ix.Kernel.Verify.Whnf.Iota.OptionalReduction /-! # Construct the structural reducer @@ -16,7 +16,7 @@ state/semantic composition rather than a free `OptionalReduction.WF` parameter. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Complete fixed-context input for the production structural reducer. @@ -115,4 +115,4 @@ theorem wf end StructuralCoreContext end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Structural/StepAssembly.lean b/Ix/Kernel/Verify/Whnf/Structural/StepAssembly.lean similarity index 97% rename from Ix/Tc/Verify/Whnf/Structural/StepAssembly.lean rename to Ix/Kernel/Verify/Whnf/Structural/StepAssembly.lean index cfdd1b8f5..34a187746 100644 --- a/Ix/Tc/Verify/Whnf/Structural/StepAssembly.lean +++ b/Ix/Kernel/Verify/Whnf/Structural/StepAssembly.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Structural.ApplicationStep +import Ix.Kernel.Verify.Whnf.Structural.ApplicationStep /-! # Exhaustive structural-step assembly @@ -8,7 +8,7 @@ the single local `WhnfStep.WF` consumed by the already verified bounded loop and cache shell; no syntax branch remains implicit in a classifier premise. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Exhaustive contract for one actual `whnfCoreWithFlagsStep` iteration. -/ @@ -82,4 +82,4 @@ theorem whnfCoreWithFlagsStep_wf (.basicVar (.basic (.leaf .str))) s hsource end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Structural/VariableStep.lean b/Ix/Kernel/Verify/Whnf/Structural/VariableStep.lean similarity index 98% rename from Ix/Tc/Verify/Whnf/Structural/VariableStep.lean rename to Ix/Kernel/Verify/Whnf/Structural/VariableStep.lean index e81b293f5..e4211a760 100644 --- a/Ix/Tc/Verify/Whnf/Structural/VariableStep.lean +++ b/Ix/Kernel/Verify/Whnf/Structural/VariableStep.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Structural.BasicStep +import Ix.Kernel.Verify.Whnf.Structural.BasicStep /-! # Legacy-variable structural-step closure @@ -11,7 +11,7 @@ those walker-tight bounds to the semantic context without introducing the older, stronger `Δ.bvars + val.size` assumption. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace TcM @@ -56,7 +56,7 @@ theorem zetaVar_liftBounds world.venvWF.ordered htp hidx hshift hty hov hcon hcut hlift have hsource : TrKExprS world.venv uvars world.nameOf trProj Delta (.var idx name info) e := .var hfind - have hwf : Lean4Lean.VExpr.WF world.venv uvars Delta.toCtx e := + have hwf : Ix.Theory.Named.VExpr.WF world.venv uvars Delta.toCtx e := ⟨A, hctx.wf.find?_wf world.venvWF.ordered hfind⟩ exact ⟨e, e, hsource, hresult, hwf⟩ @@ -192,4 +192,4 @@ theorem whnfCoreWithFlagsStep_basicVar_wf exact whnfCoreWithFlagsStep_var_wf hrun theory hvar end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/Whnf/Structural/VerifiedStep.lean b/Ix/Kernel/Verify/Whnf/Structural/VerifiedStep.lean similarity index 96% rename from Ix/Tc/Verify/Whnf/Structural/VerifiedStep.lean rename to Ix/Kernel/Verify/Whnf/Structural/VerifiedStep.lean index 0263c3dd0..44ef665bf 100644 --- a/Ix/Tc/Verify/Whnf/Structural/VerifiedStep.lean +++ b/Ix/Kernel/Verify/Whnf/Structural/VerifiedStep.lean @@ -1,4 +1,4 @@ -import Ix.Tc.Verify.Whnf.Beta.Meaning +import Ix.Kernel.Verify.Whnf.Beta.Meaning /-! # Structural step without a beta oracle @@ -9,7 +9,7 @@ Theory and translation invariants, so the production structural step can now be exposed with only its genuine helper and finite-run boundaries. -/ -namespace Ix.Tc +namespace Ix.Kernel namespace RecM /-- Exhaustive `whnfCoreWithFlagsStep` closure with general multi-beta proved @@ -41,4 +41,4 @@ theorem whnfCoreWithFlagsStep_constructive_wf hinputs hprojection hinductive (betaManyMeaning trProj world) hiota end RecM -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Verify/World.lean b/Ix/Kernel/Verify/World.lean similarity index 98% rename from Ix/Tc/Verify/World.lean rename to Ix/Kernel/Verify/World.lean index f5cad0701..3876b7358 100644 --- a/Ix/Tc/Verify/World.lean +++ b/Ix/Kernel/Verify/World.lean @@ -1,5 +1,5 @@ -import Ix.Tc.Env -import Lean4Lean.Theory.Typing.Env +import Ix.Kernel.Env +import Ix.Theory.Named.Typing.Env /-! # Non-circular verification worlds @@ -13,7 +13,7 @@ whole-`KEnv` relation conflates: assigned to each coordinated checker block; * `VerifyWorld.trusted`: the ghost index intended to track declarations already admitted to the semantic world; -* `VerifyWorld.venv`: the well-formed Lean4Lean environment for that trusted +* `VerifyWorld.venv`: the well-formed Ix.Theory.Named environment for that trusted world; * `LoadedAgrees`: the one-way relation from the concrete lazy-load cache to the catalog. @@ -32,9 +32,9 @@ constants through `TrustedConstRel`. The legacy `TrKEnv` remains only as a quarantined compatibility proof interface. -/ -namespace Ix.Tc +namespace Ix.Kernel -open Lean4Lean (VEnv) +open Ix.Theory.Named (VEnv) /-- Immutable ghost input. A catalog entry says which concrete declaration is committed at an id; it says nothing about that declaration's typing. -/ @@ -356,4 +356,4 @@ theorem VerifyWorld.ofCatalog_loaded_not_trusted {catalog : Catalog} end CataloguedLoaded -end Ix.Tc +end Ix.Kernel diff --git a/Ix/Tc/Whnf.lean b/Ix/Kernel/Whnf.lean similarity index 99% rename from Ix/Tc/Whnf.lean rename to Ix/Kernel/Whnf.lean index 871e07457..d74511c4f 100644 --- a/Ix/Tc/Whnf.lean +++ b/Ix/Kernel/Whnf.lean @@ -1,6 +1,6 @@ module -public import Ix.Tc.Monad +public import Ix.Kernel.Monad /-! Mirror: crates/kernel/src/whnf.rs (plus `str_lit_to_constructor` from @@ -35,7 +35,7 @@ which otherwise descend into Nat.rec towers and trip maxDefEqDepth). public section @[expose] section -namespace Ix.Tc +namespace Ix.Kernel open Std (HashMap HashSet) @@ -90,7 +90,7 @@ def KConst.iotaCtorInfo? : KConst m → Option (Nat × Nat) | _ => none /-- Nat value from a literal or the `Nat.zero` constructor (C++ - `is_nat_lit_ext` / lean4lean `rawNatLitExt?`). -/ + `is_nat_lit_ext` / the named specification `rawNatLitExt?`). -/ def extractNatLit (e : KExpr m) (prims : Primitives m) : Option Nat := match e with | .nat val _ _ => some val @@ -121,7 +121,7 @@ def computeNatBin (addr : Address) (p : PrimAddrs) (a b : Nat) : Option Nat := else if addr == p.natDiv then some (if b == 0 then 0 else a / b) else if addr == p.natMod then some (if b == 0 then a else a % b) else if addr == p.natPow then - -- Matches C++ `ReducePowMaxExp` / lean4lean `reducePowMaxExp`. + -- Matches C++ `ReducePowMaxExp` / the named specification `reducePowMaxExp`. if b ≤ 16777216 then some (a ^ b) else none else if addr == p.natGcd then some (Nat.gcd a b) else if addr == p.natLand then some (a &&& b) @@ -174,7 +174,7 @@ namespace RecM Spelled with `tryCatch` and `pure` rather than `try`/`catch` with `return`: a `return` inside a `try` block elaborates through the early-return transformer, burying the plain `EStateM.tryCatch` this reduces to and that -`Ix/Tc/Verify/Whnf/Iota/ConstructorSynthesis.lean` reasons about. Both +`Ix/Kernel/Verify/Whnf/Iota/ConstructorSynthesis.lean` reasons about. Both spellings denote the same function. -/ @[inline] def try? (x : RecM m α) : RecM m (Option α) := tryCatch (do let a ← x; pure (some a)) (fun _ => pure none) @@ -240,7 +240,7 @@ def strLitListToConstructor (charOfNat cons : KExpr m) : /-- `"abc" → String.ofList (List.cons (Char.ofNat 97) … List.nil)` — the kernel's string-literal constructor expansion (def_eq.rs `str_lit_to_constructor`; `Char.ofNat` + `String.ofList`, matching - lean4lean / C++). -/ + the named specification / C++). -/ def strLitToConstructor (s : String) : RecM m (KExpr m) := do let p ← prims let charConst ← TcM.intern (.mkConst p.charType #[]) @@ -488,7 +488,7 @@ def tryApplyIotaCtor (recr : IotaInfo m) (recUs : Array (KUniv m)) (spine ctorArgs : Array (KExpr m)) (cidx ctorFields : Nat) (transient : Bool) : RecM m (Option (KExpr m)) := do let some rule := recr.rules[cidx]? | return none - -- H6: level arity; H5: fields ≤ ctor args (lean4lean Reduce.lean:75-76). + -- H6: level arity; H5: fields ≤ ctor args (the named specification Reduce.lean:75-76). if recUs.size.toUInt64 != recr.lvls then return none if ctorFields > ctorArgs.size then @@ -789,7 +789,7 @@ def whnfWithNatSuccModeStep (natSuccMode : NatSuccMode) if seen.contains cur.addr then return .done cur let seen := seen.insert cur.addr - -- Native reduction runs before nat reduction (lean4lean order). + -- Native reduction runs before nat reduction (the named specification order). if let some reduced ← tryReduceNative cur then return .next (reduced, seen) if let some reduced ← tryReduceBitvec cur then @@ -919,7 +919,7 @@ def whnfCoreWithFlagsStep (cur : KExpr m) (flags : WhnfFlags) : | some val => return .next val | none => return .done cur | .fvar id _ _ => - -- Let-bound fvar zeta-reduction (lean4lean `whnfFVar`). + -- Let-bound fvar zeta-reduction (the named specification `whnfFVar`). match (← get).lctx.find? id with | some (.ldecl _ _ val) => return .next val | _ => return .done cur @@ -1193,7 +1193,7 @@ def tryStructEtaAfterInductive (recUs : Array (KUniv m)) /-- Struct-eta iota: single-rule recursor over a non-recursive one-ctor zero-index inductive; rebuild the rule with projections of the major. - Prop-typed majors are excluded (lean4lean `toCtorWhenStruct`). -/ + Prop-typed majors are excluded (the named specification `toCtorWhenStruct`). -/ def tryStructEtaIota (recId : KId m) (recr : IotaInfo m) (recUs : Array (KUniv m)) (spine : Array (KExpr m)) : RecM m (Option (KExpr m)) := do @@ -1973,7 +1973,7 @@ def tryReduceNative (e : KExpr m) : RecM m (Option (KExpr m)) := do tryReduceNativeMarker p isReduceBool argId argUs -- ### `is_rec` verification (inductive.rs `computed_is_rec` — hosted here --- because struct-likeness needs it; `Ix.Tc.Inductive` reuses it) +-- because struct-likeness needs it; `Ix.Kernel.Inductive` reuses it) /-- Finish one constructor-parameter peel after the recursive WHNF callback. Naming the post-callback seam keeps the binder mutation equation stable for @@ -2106,7 +2106,7 @@ attribute [irreducible] tryProjPrepare tryProjReduce end RecM -end Ix.Tc +end Ix.Kernel end end diff --git a/Ix/Tc.lean b/Ix/Tc.lean deleted file mode 100644 index 3f0962a55..000000000 --- a/Ix/Tc.lean +++ /dev/null @@ -1,54 +0,0 @@ -module - -public import Ix.Tc.Mode -public import Ix.Tc.Id -public import Ix.Tc.Level -public import Ix.Tc.Expr -public import Ix.Tc.Error -public import Ix.Tc.Const -public import Ix.Tc.Equiv -public import Ix.Tc.Env -public import Ix.Tc.Primitive -public import Ix.Tc.Subst -public import Ix.Tc.Lctx -public import Ix.Tc.Monad -public import Ix.Tc.Ingress -public import Ix.Tc.IngressMeta -public import Ix.Tc.Egress -public import Ix.Tc.EgressLean -public import Ix.Tc.Driver -public import Ix.Tc.ParCheck -public import Ix.Tc.Validate -public import Ix.Tc.Whnf -public import Ix.Tc.Infer -public import Ix.Tc.DefEq -public import Ix.Tc.Knot -public import Ix.Tc.CanonicalCheck -public import Ix.Tc.Inductive -public import Ix.Tc.Check - -/-! -# Ix.Tc — pure-Lean Ix kernel over Ixon - -A correctness-first, formalizable port of the Rust Ix kernel -(`crates/kernel`), operating over the Ixon content-addressed format with -separated anon and meta modes. Designed for correctness and formalization; -for performance, use the Rust kernel. - -Module map (mirrors `crates/kernel/src/` file-for-file): - -| Lean module | Rust source | -|-------------------|----------------| -| `Ix.Tc.Mode` | `mode.rs` | -| `Ix.Tc.Id` | `id.rs` | -| `Ix.Tc.Error` | `error.rs` | -| `Ix.Tc.Level` | `level.rs` | -| `Ix.Tc.Expr` | `expr.rs` | -| `Ix.Tc.Const` | `constant.rs` | -| `Ix.Tc.Equiv` | `equiv.rs` | -| `Ix.Tc.Env` | `env.rs` | -| `Ix.Tc.Primitive` | `primitive.rs` | -| `Ix.Tc.Subst` | `subst.rs` | -| `Ix.Tc.Lctx` | `lctx.rs` | -| `Ix.Tc.Monad` | `tc.rs` | --/ diff --git a/Ix/Theory.lean b/Ix/Theory.lean new file mode 100644 index 000000000..8687dd69f --- /dev/null +++ b/Ix/Theory.lean @@ -0,0 +1,6 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified diff --git a/Ix/Theory/Certificate/Build.lean b/Ix/Theory/Certificate/Build.lean new file mode 100644 index 000000000..a8adb93e0 --- /dev/null +++ b/Ix/Theory/Certificate/Build.lean @@ -0,0 +1,211 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certificate.OrdinarySource +import Ix.Theory.Certificate.Standard +import Ix.Theory.Certificate.Quotient +import Ix.Theory.Certificate.Structure +import Ix.Theory.Certificate.Modeled +import Ix.Theory.Certified.Accept + +namespace Ix.Theory.Certificate + +open Model Certified + +universe u +variable {β : Type u} [DecidableEq β] +variable [Hints β] + +def hasLiteral : VExpr β → Bool + | .natLit _ => true + | .app a b | .lam a b | .forallE a b => hasLiteral a || hasLiteral b + | .proj _ _ e => hasLiteral e + | _ => false + +def constantHasLiteral : Const β → Bool + | .defn _ _ type body _ => hasLiteral type || hasLiteral body + | .induct _ _ _ type ctors _ => hasLiteral type || ctors.any (hasLiteral ·.type) + | .recursor _ _ _ _ _ type rules _ _ => hasLiteral type || rules.any (hasLiteral ·.rhs) + | .axiom _ type _ | .quot _ _ type => hasLiteral type + +def literalDependencies? (natural : Option (ConstRef β)) (used : Bool) : Option (List (ConstRef β)) := + if used then natural.map (fun r => [r]) else some [] + +inductive DeclarationSource (β : Type u) where + | definition (ref : ConstRef β) + | ordinary (source recursor : β) + | standard (ref : ConstRef β) + | quotient (refs : Certified.Quotient.Refs β) + | modeled (candidate : Modeled.Candidate β) + +structure SourceGroup (β : Type u) where + declaration : DeclarationSource β + references : List (ConstRef β) + dependencies : List (ConstRef β) + +def recursorFamily? : VExpr β → Option β + | .forallE _ (.forallE A B) => recursorFamily? (.forallE A B) + | .forallE A _ => match A.appHead with + | .const (.member source 0) _ => some source + | _ => none + | _ => none + +def findRecursor? (store : Store β) (source : β) : Option β := + store.dom.find? fun candidate => match store.blocks candidate with + | some ⟨[.recursor _ _ _ _ _ type _ _ .safe]⟩ => recursorFamily? type == some source + | _ => false + +def ordinaryGroup? (store : Store β) (source recursor : β) + (natural : Option (ConstRef β) := none) : Option (SourceGroup β) := do + let ⟨[family@(.induct _ _ _ _ ctors .safe)]⟩ ← store.blocks source | none + let ⟨[recr@(.recursor _ _ _ _ _ _ _ _ .safe)]⟩ ← store.blocks recursor | none + let references := [.member source 0, .member recursor 0] ++ + (List.range ctors.length).map (.ctor source 0 ·) + let literals ← literalDependencies? natural (constantHasLiteral family || constantHasLiteral recr) + return ⟨.ordinary source recursor, references, + ((family.refs ++ recr.refs).filter fun r => !references.contains r) ++ literals⟩ + +def quotientGroup (refs : Certified.Quotient.Refs β) : SourceGroup β := + ⟨.quotient refs, Certified.Quotient.kinds.map refs.ref, [refs.eq, refs.eqRefl, refs.eqRec]⟩ + +def modeledGroup? (store : Store β) (candidate : Modeled.Candidate β) + (natural : Option (ConstRef β) := none) : Option (SourceGroup β) := do + let references ← Certified.Modeled.sourceRefs? store candidate.source candidate.recursors + let sources := references.filterMap (store.lookup ·) + let rawReferences := sources.flatMap Const.refs + let literals ← literalDependencies? natural (sources.any constantHasLiteral) + return ⟨.modeled candidate, references, + rawReferences.filter (fun ref => !references.contains ref) ++ candidate.models ++ + candidate.proofReferences ++ literals⟩ + +def sourceGroup? (store : Store β) (r : ConstRef β) + (natural : Option (ConstRef β) := none) (models : List (Modeled.Candidate β) := []) : Option (SourceGroup β) := do + match models.findSome? (fun candidate => do + let group ← modeledGroup? store candidate natural + if group.references.contains r then some group else none) with + | some group => return group + | none => pure ⟨⟩ + match r with + | .ctor source 0 _ => ordinaryGroup? store source (← findRecursor? store source) natural + | .ctor .. => none + | .member source member => + match ← store.lookup r with + | .defn _ _ type body .safe => + return ⟨.definition r, [r], type.refs ++ body.refs ++ + (← literalDependencies? natural (hasLiteral type || hasLiteral body))⟩ + | .axiom _ type .safe => + match Quotient.refs? store r with + | some refs => return quotientGroup refs + | none => return ⟨.standard r, [r], type.refs ++ (← literalDependencies? natural (hasLiteral type))⟩ + | .quot .. => return quotientGroup (← Quotient.refs? store r) + | .induct .. => + if member != 0 then none else ordinaryGroup? store source (← findRecursor? store source) natural + | .recursor _ _ _ _ _ type _ _ .safe => + if member != 0 then none else ordinaryGroup? store (← recursorFamily? type) source natural + | _ => none + +/-- Dependency order groups a whole ordinary family and recursor as one unit. +A cycle, missing reference, or unsupported declaration produces no suggestion. -/ +def dependencyOrder? (fuel : Nat) (signature : PrimitiveSignature β) (store : Store β) + (pending visited active : List (ConstRef β)) + (order : List (DeclarationSource β) := []) (models : List (Modeled.Candidate β) := []) : + Option (List (ConstRef β) × List (DeclarationSource β)) := + match fuel, pending with + | _, [] => some (visited, order) + | 0, _ :: _ => none + | fuel + 1, r :: rest => + if r = signature.falseType ∨ r = signature.falseElim ∨ r ∈ visited then + dependencyOrder? fuel signature store rest visited active order models + else if r ∈ active then none + else do + let group ← sourceGroup? store r signature.natType models + if group.references.any (active.contains ·) then none else do + let (visited, order) ← dependencyOrder? fuel signature store + group.dependencies visited (group.references ++ active) order models + dependencyOrder? fuel signature store rest (visited ++ group.references) active + (order ++ [group.declaration]) models + +def definitionWitness? (fuel : Nat) (entries : Environment β) (r : ConstRef β) + (source : Const β) : Option (DefinitionWitness β) := + match source with + | .defn n _ type body .safe => do + let T ← inferSource? fuel n entries [] type + let .sort l := T.type | none + let b ← inferSource? fuel n entries [] body + let bw ← castWith? fuel n entries [] b T.expression + return ⟨r, annotations T.expression, annotations b.expression, l, T.witness, bw⟩ + | _ => none + +def declarationWitnesses? (fuel : Nat) (store : Store β) (entries : Environment β) : + List (DeclarationSource β) → Option (Environment β × List (DeclarationWitness β)) + | [] => some (entries, []) + | .definition r :: rest => do + let source ← store.lookup r + let witness ← definitionWitness? fuel entries r source + let reading ← readDefinition? source witness.typeAnnotations witness.bodyAnnotations + let (entries', witnesses) ← + declarationWitnesses? fuel store (entries.insert r reading.val.entry) rest + return (entries', .definition witness :: witnesses) + | .ordinary source recursor :: rest => do + let witness ← Ordinary.sourceBlock? fuel entries store source recursor + if Hints.natural (β := β) = some (.member source 0) then + if witness.shape.shape != Certified.Natural.shape then none else do + let (entries', witnesses) ← declarationWitnesses? fuel store + (Certified.Natural.environment entries source recursor witness.mode) rest + return (entries', .natural witness :: witnesses) + else + match Structure.witness? fuel entries witness with + | some structured => + let (entries', witnesses) ← declarationWitnesses? fuel store + (structured.facts.description.publishedEnvironment entries source recursor witness.mode) rest + return (entries', .structure structured :: witnesses) + | none => + let (entries', witnesses) ← declarationWitnesses? fuel store + (witness.shape.shape.publishedEnvironment entries source recursor witness.mode) rest + return (entries', .ordinary witness :: witnesses) + | .standard ref :: rest => do + let source ← store.lookup ref + let witness ← Standard.witness? fuel store entries ref source + let (entries', witnesses) ← declarationWitnesses? fuel store (entries.insert ref witness.spec.entry) rest + return (entries', .standard witness :: witnesses) + | .quotient refs :: rest => do + let witness ← Quotient.witness? fuel entries refs + let (entries', witnesses) ← declarationWitnesses? fuel store (refs.environment entries) rest + return (entries', .quotient witness :: witnesses) + | .modeled candidate :: rest => do + let witness ← candidate.witness? fuel entries store + let (entries', witnesses) ← declarationWitnesses? fuel store + (Certified.Modeled.environment entries witness.companions) rest + return (entries', .modeled witness :: witnesses) + +/-- A generic search driver over the exact input store and source syntax. +This is untrusted preprocessing: the final acceptance call validates the +entire returned witness again, including the primitive metadata and policy. -/ +def proofWitness? (fuel : Nat) (signature : PrimitiveSignature β) + (input : ProofInput β) (models : List (Modeled.Candidate β) := []) : Option (ProofWitness β) := + letI : Hints β := ⟨signature.natType⟩ + do + let literals ← literalDependencies? signature.natType (hasLiteral input.proposition || hasLiteral input.proof) + let (_, order) ← dependencyOrder? fuel signature input.store + (input.proposition.refs ++ input.proof.refs ++ literals) [] [] [] models + let (entries, declarations) ← + declarationWitnesses? fuel input.store signature.environment order + let P ← inferSource? fuel input.universes entries [] input.proposition + let Pw ← castWith? fuel input.universes entries [] P (.sort .zero) + let e ← inferSource? fuel input.universes entries [] input.proof + let ew ← castWith? fuel input.universes entries [] e P.expression + return ⟨declarations, annotations e.expression, annotations P.expression, ew, Pw⟩ + +/-- Declaration-only search also covers data declarations and complete source +groups. The independent store validator checks every requested reference. -/ +def storeWitness? (fuel : Nat) (signature : PrimitiveSignature β) (store : Store β) + (targets : List (ConstRef β)) (models : List (Modeled.Candidate β) := []) : Option (List (DeclarationWitness β)) := + letI : Hints β := ⟨signature.natType⟩ + do + let (_, order) ← dependencyOrder? fuel signature store targets [] [] [] models + let (_, declarations) ← declarationWitnesses? fuel store signature.environment order + return declarations + +end Ix.Theory.Certificate diff --git a/Ix/Theory/Certificate/Claims.lean b/Ix/Theory/Certificate/Claims.lean new file mode 100644 index 000000000..3e799214a --- /dev/null +++ b/Ix/Theory/Certificate/Claims.lean @@ -0,0 +1,50 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certificate.Build +import Ix.Theory.Certified.Claims + +namespace Ix.Theory.Certificate + +open Model Certified + +universe u +variable {β : Type u} [DecidableEq β] [Hints β] + +/-- Untrusted construction of an ordered opaque frontier. The claim checker +independently re-reads each source type and verifies its formation. -/ +def frontierWitnesses? (fuel : Nat) (store : Store β) (entries : Environment β) : + List (ConstRef β) → Option (Environment β × List (FrontierWitness β)) + | [] => some (entries, []) + | ref :: refs => match store.uvars ref, store.type ref with + | some universes, some type => do + let inferred ← inferSource? fuel universes entries [] type + let .sort level := inferred.type | none + let witness : FrontierWitness β := ⟨ref, annotations inferred.expression, level, inferred.witness⟩ + let reading ← readFrontierHeader? store witness + let (entries', rest) ← frontierWitnesses? fuel store + (entries.insert ref reading.header.entry) refs + return (entries', witness :: rest) + | _, _ => none + +def claimNode? (fuel : Nat) (signature : PrimitiveSignature β) (store : Store β) + (frontier subjects : List (ConstRef β)) (models : List (Modeled.Candidate β) := []) : Option (ClaimNode β) := + letI : Hints β := ⟨signature.natType⟩ + do + let (entries, dependencies) ← frontierWitnesses? fuel store signature.environment frontier + let (_, order) ← dependencyOrder? fuel signature store subjects frontier [] [] models + let (_, declarations) ← declarationWitnesses? fuel store entries order + return ⟨subjects, dependencies, declarations⟩ + +def batchFrontier? (fuel : Nat) (signature : PrimitiveSignature β) (store : Store β) + (nodes : List (ClaimNode β)) : Option (List (FrontierWitness β)) := + letI : Hints β := ⟨signature.natType⟩ + do + let subjects := nodes.flatMap (·.subjects) + let refs := (nodes.flatMap fun node => node.frontier.map (·.ref)).filter + (fun ref => !subjects.contains ref) |>.eraseDups + return (← frontierWitnesses? fuel store signature.environment refs).2 + +end Ix.Theory.Certificate diff --git a/Ix/Theory/Certificate/Modeled.lean b/Ix/Theory/Certificate/Modeled.lean new file mode 100644 index 000000000..618fb171f --- /dev/null +++ b/Ix/Theory/Certificate/Modeled.lean @@ -0,0 +1,104 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certificate.Quotient +import Ix.Theory.Certified.Modeled.Admission + +/-! Untrusted construction of exact model-companion certificates. Model +declarations are ordinary earlier checked declarations. This builder derives +all source headers and rule statements from the store, reads annotations in +the mapped model environment, and searches for conversion or a supplied +propositional proof. Its output must pass public declaration admission. +-/ + +namespace Ix.Theory.Certificate.Modeled + +open Model Certified Certified.Modeled + +universe u +variable {β : Type u} [DecidableEq β] [Hints β] + +structure ProofHint (β : Type u) where + equality : ConstRef β + reflexivity : ConstRef β + recursor : ConstRef β + proof : VExpr β + +/-- A finite untrusted package selects earlier model declarations and optional +equation proofs. The store remains the source of every original statement. -/ +structure Candidate (β : Type u) where + source : β + recursors : List (ConstRef β) + models : List (ConstRef β) + proofs : List (ConstRef β × List (Option (ProofHint β))) := [] + +def Candidate.hint (candidate : Candidate β) (ref : ConstRef β) (index : Nat) : Option (ProofHint β) := do + let (_, proofs) ← candidate.proofs.find? (fun pair => pair.1 == ref) + (proofs[index]?).join + +def Candidate.proofReferences (candidate : Candidate β) : List (ConstRef β) := + candidate.proofs.flatMap fun (_, proofs) => proofs.flatMap fun proof => + match proof with + | none => [] + | some proof => [proof.equality, proof.reflexivity, proof.recursor] ++ proof.proof.refs + +def target (pairs : List (ConstRef β × ConstRef β)) (ref : ConstRef β) : ConstRef β := + ((pairs.find? (fun pair => pair.1 == ref)).map (·.2)).getD ref + +def readMapped? (fuel n : Nat) (entries : Environment β) + (mapping : ConstRef β → ConstRef β) (raw : VExpr β) : Option (AExpr β) := do + let inferred ← inferSource? fuel n entries [] (raw.mapRefs mapping) + let annotated ← readAnnotations? n 0 raw (annotations inferred.expression) + if annotated.val.mapRefs mapping = inferred.expression then some annotated.val else none + +def rule? (fuel : Nat) (entries : Environment β) (mapping : ConstRef β → ConstRef β) + (raw : RawRule β) (hint : Option (ProofHint β)) : + Option (Signature.Rule β × EquationWitness β) := do + let type ← readMapped? fuel raw.universes entries mapping raw.type + let lhs ← readMapped? fuel raw.universes entries mapping raw.lhs + let rhs ← readMapped? fuel raw.universes entries mapping raw.rhs + let rule : Signature.Rule β := ⟨raw.universes, type, lhs, rhs⟩ + let mapped : Signature.Rule β := ⟨raw.universes, type.mapRefs mapping, lhs.mapRefs mapping, rhs.mapRefs mapping⟩ + let formation ← Quotient.ruleWitness? fuel entries mapped + match hint with + | none => do + let proof ← conversion? fuel raw.universes entries [] mapped.lhs mapped.rhs + return (rule, ⟨formation, .conversion proof⟩) + | some hint => do + let inferred ← inferSource? fuel raw.universes entries [] hint.proof + let expected := Basis.Equality.applied hint.equality formation.level mapped.type mapped.lhs mapped.rhs + let proof ← castWith? fuel raw.universes entries [] inferred expected + return (rule, ⟨formation, .propositional hint.equality hint.reflexivity hint.recursor inferred.expression proof⟩) + +def companion? (fuel : Nat) (entries : Environment β) (store : Store β) + (pairs : List (ConstRef β × ConstRef β)) (ref model : ConstRef β) + (hints : Nat → Option (ProofHint β)) : Option (Companion β × List (EquationWitness β)) := do + let raw ← store.type ref + let some universes := store.uvars ref | none + let modelEntry ← entries model + if modelEntry.universes != universes then none else do + let reading ← readAnnotations? universes 0 raw (annotations modelEntry.type) + if reading.val.mapRefs (target pairs) != modelEntry.type then none else do + let rawRules ← sourceRules? store ref + let rules ← rawRules.zipIdx.mapM fun (raw, index) => rule? fuel entries (target pairs) raw (hints index) + return (⟨⟨ref, universes, reading.val⟩, model, rules.map (·.1)⟩, rules.map (·.2)) + +/-- Model targets are positional in the actual source layout. Duplicated +targets are allowed; their exact types and equations still undergo the same +check, which supplies semantic justification for auxiliary merging. -/ +def witness? (fuel : Nat) (entries : Environment β) (store : Store β) (source : β) + (recursors models : List (ConstRef β)) + (hints : ConstRef β → Nat → Option (ProofHint β) := fun _ _ => none) : Option (Witness β) := do + let refs ← sourceRefs? store source recursors + if refs.length != models.length then none else do + let pairs := refs.zip models + let checked ← pairs.mapM fun (ref, model) => companion? fuel entries store pairs ref model (hints ref) + return ⟨source, recursors, checked.map (·.1), checked.map (·.2)⟩ + +def Candidate.witness? (candidate : Candidate β) (fuel : Nat) (entries : Environment β) + (store : Store β) : Option (Witness β) := + Modeled.witness? fuel entries store candidate.source candidate.recursors candidate.models candidate.hint + +end Ix.Theory.Certificate.Modeled diff --git a/Ix/Theory/Certificate/Ordinary.lean b/Ix/Theory/Certificate/Ordinary.lean new file mode 100644 index 000000000..531f83b72 --- /dev/null +++ b/Ix/Theory/Certificate/Ordinary.lean @@ -0,0 +1,98 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certificate.Suggest +import Ix.Theory.Certified.Ordinary.Checked + +/-! Untrusted witness construction for a proposed ordinary description. -/ + +namespace Ix.Theory.Certificate.Ordinary + +open Model Certified Certified.Ordinary + +universe u +variable {β : Type u} [DecidableEq β] +variable [Hints β] + +def domains? (fuel n : Nat) (entries : Environment β) : + Context β → List (AExpr β) → Option (List (DomainWitness β)) + | _, [] => some [] + | Γ, A :: rest => do + let typed ← inferAnnotated? fuel n entries Γ A + let .sort level := typed.type | none + let tail ← domains? fuel n entries (Γ.push A) rest + return ⟨level, typed.witness⟩ :: tail + +def arguments? (fuel n : Nat) (entries : Environment β) (Γ : Context β) : + List (AExpr β) → List (AExpr β) → Option (List (TypingWitness β)) + | [], [] => some [] + | A :: domains, a :: args => do + let typed ← inferAnnotated? fuel n entries Γ a + let witness ← castWith? fuel n entries Γ typed A + let tail ← arguments? fuel n entries Γ (Telescope.inst a domains) args + return witness :: tail + | _, _ => none + +def recursive? (fuel : Nat) (entries : Environment β) (shape : Shape β) + (ctor : Constructor β) (field : RecursiveField β) : Option (RecursiveWitness β) := do + let domains ← domains? fuel shape.universes entries (ctor.context shape) field.domains + let indices ← arguments? fuel shape.universes entries + (Telescope.context (ctor.context shape) field.domains) + (Telescope.lift (ctor.fields.length + field.domains.length) shape.indices) field.indices + return ⟨domains, indices⟩ + +def constructor? (fuel : Nat) (entries : Environment β) (shape : Shape β) + (ctor : Constructor β) : Option (ConstructorWitness β) := do + let fields ← domains? fuel shape.universes entries shape.parameterContext ctor.fields + let indices ← arguments? fuel shape.universes entries (ctor.context shape) + (Telescope.lift ctor.fields.length shape.indices) ctor.indices + let recursive ← ctor.recursive.mapM (recursive? fuel entries shape ctor) + return ⟨fields, indices, recursive⟩ + +def shape? (fuel : Nat) (entries : Environment β) (shape : Shape β) : Option (ShapeWitness β) := do + let parameters ← domains? fuel shape.universes entries [] shape.parameters + let indices ← domains? fuel shape.universes entries shape.parameterContext shape.indices + let constructors ← shape.constructors.mapM (constructor? fuel entries shape) + return ⟨shape, parameters, indices, constructors⟩ + +def type? (fuel n : Nat) (entries : Environment β) (type : AExpr β) : Option (Shape.TypeWitness β) := do + let typed ← inferAnnotated? fuel n entries [] type + let .sort level := typed.type | none + return ⟨level, typed.witness⟩ + +def constructorTypes? (fuel : Nat) (entries : Environment β) (shape : Shape β) (source : β) : + Option (List (Shape.TypeWitness β)) := + shape.constructors.mapM fun ctor => type? fuel shape.universes (shape.familyEnvironment entries source) + (ctor.type shape source) + +def recursorType? (fuel : Nat) (entries : Environment β) (shape : Shape β) (source : β) + (mode : Inductive.ElimMode) : Option (Shape.TypeWitness β) := + type? fuel (mode.recUvars shape.universes) (shape.constructorEnvironment entries source) (shape.recursorType source mode) + +def rule? (fuel : Nat) (entries : Environment β) (shape : Shape β) (source recursor : β) + (mode : Inductive.ElimMode) (i : Nat) (ctor : Constructor β) : Option (RuleWitness β) := do + let n := mode.recUvars shape.universes + let type := shape.ruleType source mode i ctor + let typed ← type? fuel n entries type + let lhs ← inferAnnotated? fuel n entries [] (shape.ruleLhs source recursor mode i ctor) + let lh ← castWith? fuel n entries [] lhs type + let rhs ← inferAnnotated? fuel n entries [] (shape.ruleRhs source recursor mode i ctor) + let rh ← castWith? fuel n entries [] rhs type + return ⟨typed.level, typed.witness, lh, rh⟩ + +def rules? (fuel : Nat) (entries : Environment β) (shape : Shape β) (source recursor : β) + (mode : Inductive.ElimMode) : Option (List (RuleWitness β)) := + shape.constructors.zipIdx.mapM fun (ctor, i) => rule? fuel + (shape.recursorEnvironment entries source recursor mode) shape source recursor mode i ctor + +def block? (fuel : Nat) (entries : Environment β) (shape : Shape β) (source recursor : β) + (mode : Inductive.ElimMode) : Option (BlockWitness β) := do + let sw ← shape? fuel entries shape + let ct ← constructorTypes? fuel entries shape source + let rt ← recursorType? fuel entries shape source mode + let rs ← rules? fuel entries shape source recursor mode + return ⟨source, recursor, sw, mode, ct, rt, rs⟩ + +end Ix.Theory.Certificate.Ordinary diff --git a/Ix/Theory/Certificate/OrdinarySource.lean b/Ix/Theory/Certificate/OrdinarySource.lean new file mode 100644 index 000000000..663fac582 --- /dev/null +++ b/Ix/Theory/Certificate/OrdinarySource.lean @@ -0,0 +1,98 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certificate.Ordinary + +/-! Untrusted recovery of ordinary descriptions from exact source declarations. +Every removed context slot is checked by lifting back, and the completed +description must reproduce all original source metadata and expressions. -/ + +namespace Ix.Theory.Certificate.Ordinary + +open Model Certified Certified.Ordinary + +universe u +variable {β : Type u} [DecidableEq β] +variable [Hints β] + +def telescope : VExpr β → List (VExpr β) × VExpr β + | .forallE A B => let (tail, result) := telescope B; (A :: tail, result) + | e => ([], e) + +def sourceDomains? (fuel n : Nat) (entries : Environment β) : + Context β → List (VExpr β) → Option (List (AExpr β)) + | _, [] => some [] + | Γ, A :: rest => do + let typed ← inferSource? fuel n entries Γ A + let .sort _ := typed.type | none + let tail ← sourceDomains? fuel n entries (Γ.push typed.expression) rest + return typed.expression :: tail + +def removeVariables? (count : Nat) (e : VExpr β) : Option (VExpr β) := + let candidate := e.unliftN count 0 + if candidate.liftN count = e then some candidate else none + +def sourceIndices? (fuel : Nat) (entries : Environment β) (shape : Shape β) + (source : β) (Γ : Context β) (offset : Nat) (result : VExpr β) : + Option (List (AExpr β)) := do + if result.appHead != .const (.member source 0) (VLevel.params shape.universes) then none else do + let args := result.appArgs [] + if args.length != shape.parameters.length + shape.indices.length then none else do + if args.take shape.parameters.length != VExpr.bvarRevRange offset shape.parameters.length then + none + else + (args.drop shape.parameters.length).mapM fun index => do + return (← inferSource? fuel shape.universes entries Γ index).expression + +def sourceRecursive? (fuel : Nat) (entries : Environment β) (shape : Shape β) + (source : β) (fields : List (AExpr β)) (previous : Nat) (raw : VExpr β) : + Option (RecursiveField β) := do + let raw ← removeVariables? previous raw + let (domains, result) := telescope raw + let Γ := Telescope.context shape.parameterContext fields + let domains ← sourceDomains? fuel shape.universes entries Γ domains + let indices ← sourceIndices? fuel entries shape source (Telescope.context Γ domains) + (fields.length + domains.length) result + return ⟨domains, indices⟩ + +def sourceConstructor? (fuel : Nat) (entries : Environment β) (shape : Shape β) + (source : β) (raw : Ctor β) : Option (Constructor β) := do + let (binders, result) := telescope raw.type + if binders.take shape.parameters.length != shape.parameters.map AExpr.erase then none else do + let fields := binders.drop shape.parameters.length + let ordinary := fields.takeWhile fun A => !A.refs.contains (.member source 0) + let recursive := fields.drop ordinary.length + let fields ← sourceDomains? fuel shape.universes entries shape.parameterContext ordinary + let recursive ← recursive.zipIdx.mapM fun (raw, previous) => + sourceRecursive? fuel entries shape source fields previous raw + let result ← removeVariables? recursive.length result + let indices ← sourceIndices? fuel entries shape source + (Telescope.context shape.parameterContext fields) fields.length result + return ⟨fields, recursive, indices⟩ + +def description? (fuel : Nat) (entries : Environment β) (source : β) (raw : Const β) : + Option (Shape β) := do + let .induct n p i type constructors .safe := raw | none + let (binders, result) := telescope type + let .sort level := result | none + if binders.length != p + i then none else do + let parameters ← sourceDomains? fuel n entries [] (binders.take p) + let indices ← sourceDomains? fuel n entries (Telescope.context [] parameters) (binders.drop p) + let shape : Shape β := ⟨n, parameters, indices, level, []⟩ + let constructors ← constructors.mapM (sourceConstructor? fuel entries shape source) + let shape := { shape with constructors } + if shape.source source = raw then some shape else none + +def sourceBlock? (fuel : Nat) (entries : Environment β) (store : Store β) + (source recursor : β) : Option (BlockWitness β) := do + let ⟨[raw]⟩ ← store.blocks source | none + let shape ← description? fuel entries source raw + if shape.RecursorSourceMatches store source recursor .small then + block? fuel entries shape source recursor .small + else if shape.RecursorSourceMatches store source recursor .large then + block? fuel entries shape source recursor .large + else none + +end Ix.Theory.Certificate.Ordinary diff --git a/Ix/Theory/Certificate/Quotient.lean b/Ix/Theory/Certificate/Quotient.lean new file mode 100644 index 000000000..b95b434d4 --- /dev/null +++ b/Ix/Theory/Certificate/Quotient.lean @@ -0,0 +1,75 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certificate.Standard +import Ix.Theory.Certified.Quotient.Publish + +namespace Ix.Theory.Certificate.Quotient + +open Model Certified Certified.Quotient + +universe u +variable {β : Type u} [DecidableEq β] +variable [Hints β] + +def memberRefs (store : Store β) : List (ConstRef β) := + store.dom.flatMap fun block => + match store.blocks block with + | some value => (List.range value.members.length).map (.member block ·) + | none => [] + +def findSource? (store : Store β) (source : Const β) : Option (ConstRef β) := + (memberRefs store).find? fun r => store.lookup r == some source + +/-- Discover the quotient package from an exact lift type. The other members +are found by declaration content, independently of addresses or source names. -/ +def refsForLift? (store : Store β) (lift : ConstRef β) : Option (Refs β) := do + let .quot .lift 2 type ← store.lookup lift | none + type.refs.findSome? fun eq => do + let .member esource 0 := eq | none + let eqRefl := ConstRef.ctor esource 0 0 + let eqRec ← Standard.recursorFor? store 2 (Certified.Basis.Equality.recType eq eqRefl) + type.refs.findSome? fun family => do + let refs : Refs β := { eq, type := family, ctor := lift, lift, ind := lift, eqRefl, eqRec, sound := lift } + if (refs.source .lift) != .quot .lift 2 type then none else do + let ctor ← findSource? store (refs.source .ctor) + let refs := { refs with ctor } + let ind ← findSource? store (refs.source .ind) + let sound ← findSource? store (refs.source .sound) + let refs := { refs with ind, sound } + if refs.ExactSource store then some refs else none + +def refs? (store : Store β) (ref : ConstRef β) : Option (Refs β) := + (memberRefs store).findSome? fun lift => do + let refs ← refsForLift? store lift + if (kinds.map refs.ref).contains ref then some refs else none + +def typeWitnesses? (fuel : Nat) (entries : Environment β) : + List (Signature.Header β) → Option (List (Signature.TypeWitness β)) + | [] => some [] + | header :: headers => do + let inferred ← inferAnnotated? fuel header.universes entries [] header.type + let .sort level := inferred.type | none + let rest ← typeWitnesses? fuel (entries.insert header.ref header.entry) headers + return ⟨level, inferred.witness⟩ :: rest + +def ruleWitness? (fuel : Nat) (entries : Environment β) (rule : Signature.Rule β) : + Option (Signature.RuleWitness β) := do + let type ← inferAnnotated? fuel rule.universes entries [] rule.type + let .sort level := type.type | none + let lhs ← inferAnnotated? fuel rule.universes entries [] rule.lhs + let lhs ← castWith? fuel rule.universes entries [] lhs rule.type + let rhs ← inferAnnotated? fuel rule.universes entries [] rule.rhs + let rhs ← castWith? fuel rule.universes entries [] rhs rule.type + return ⟨level, type.witness, lhs, rhs⟩ + +def witness? (fuel : Nat) (entries : Environment β) (refs : Refs β) : Option (Witness β) := do + let types ← typeWitnesses? fuel entries refs.headers + let formed := refs.typeEnvironment entries + let liftRule ← ruleWitness? fuel formed refs.liftRule + let indRule ← ruleWitness? fuel formed refs.indRule + return ⟨refs, types, liftRule, indRule⟩ + +end Ix.Theory.Certificate.Quotient diff --git a/Ix/Theory/Certificate/Standard.lean b/Ix/Theory/Certificate/Standard.lean new file mode 100644 index 000000000..27a505a3f --- /dev/null +++ b/Ix/Theory/Certificate/Standard.lean @@ -0,0 +1,54 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certificate.Suggest +import Ix.Theory.Certified.Standard.Checked + +namespace Ix.Theory.Certificate.Standard + +open Model Certified Certified.Standard Certified.Basis + +universe u +variable {β : Type u} [DecidableEq β] +variable [Hints β] + +def recursorFor? (store : Store β) (n : Nat) (type : AExpr β) : Option (ConstRef β) := + (store.dom.find? fun source => match store.blocks source with + | some ⟨[.recursor m _ _ _ _ raw _ _ .safe]⟩ => m == n && raw == type.erase + | _ => false).map (.member · 0) + +/-- Recover only known standard schemas, then require exact source erasure. +All prerequisite interfaces are checked again by standard admission. -/ +def spec? (store : Store β) (type : VExpr β) : Option (Spec β) := do + let spec ← match type with + | .forallE (.sort .zero) (.forallE (.sort .zero) (.forallE premise result)) => do + let .const iff [] := premise.appHead | none + let .const eq [.succ .zero] := result.appHead | none + let .member isource 0 := iff | none + let .member esource 0 := eq | none + let ei := ConstRef.ctor esource 0 0 + let ii := ConstRef.ctor isource 0 0 + let er ← recursorFor? store 2 (Equality.recType eq ei) + let ir ← recursorFor? store 1 (Iff.recType iff ii) + return Spec.propext eq ei er iff ii ir + | .forallE (.sort (.param 0)) (.forallE premise (.bvar 1)) => do + let .const ne [.param 0] := premise.appHead | none + let .member source 0 := ne | none + let ctor := ConstRef.ctor source 0 0 + let recursor ← recursorFor? store 1 (Nonempty.recType ne ctor) + return Spec.choice ne ctor recursor + | _ => none + if spec.type.erase = type then some spec else none + +def witness? (fuel : Nat) (store : Store β) (entries : Environment β) + (ref : ConstRef β) (source : Const β) : Option (Witness β) := do + let .axiom _ type .safe := source | none + let spec ← spec? store type + if spec.source != source then none else do + let inferred ← inferAnnotated? fuel spec.universes entries [] spec.type + let .sort level := inferred.type | none + return ⟨ref, spec, level, inferred.witness⟩ + +end Ix.Theory.Certificate.Standard diff --git a/Ix/Theory/Certificate/Structure.lean b/Ix/Theory/Certificate/Structure.lean new file mode 100644 index 000000000..2e123615b --- /dev/null +++ b/Ix/Theory/Certificate/Structure.lean @@ -0,0 +1,43 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certificate.OrdinarySource +import Ix.Theory.Certificate.Quotient +import Ix.Theory.Certified.Structure.Publish + +namespace Ix.Theory.Certificate.Structure + +open Model Certified Certified.Structure + +universe u +variable {β : Type u} [DecidableEq β] +variable [Hints β] + +def description? (block : Certified.Ordinary.BlockWitness β) : Option (Description β) := do + let shape := block.shape.shape + let [ctor] := shape.constructors | none + let [witness] := block.shape.constructors | none + if ctor.fields.length != witness.fields.length then none else + let fields := (ctor.fields.zip witness.fields).map fun (domain, witness) => ⟨domain, witness.level⟩ + let d : Description β := ⟨shape.universes, shape.parameters, fields, shape.level⟩ + if d.ordinary = shape ∧ fields.all (checkZeroImplies shape.level ·.level) then some d else none + +/-- Recover all structure data from the already source-recovered ordinary +block, then suggest whole typing checks for its dependent projections. -/ +def witness? (fuel : Nat) (entries : Environment β) (block : Certified.Ordinary.BlockWitness β) : + Option (Witness β) := do + let d ← description? block + let [ctor] := block.shape.constructors | none + let domains ← Ordinary.domains? fuel d.universes + (d.ordinary.publishedEnvironment entries block.source block.recursor block.mode) [] + (d.projectionDomains block.source) + let facts : FactsWitness β := ⟨d, block, ctor.fields.map DomainWitness.typing, domains⟩ + let stage := d.factEnvironment entries block.source block.recursor block.mode + let eta ← Quotient.ruleWitness? fuel stage (d.etaRule block.source) + let iota ← d.fields.zipIdx.mapM fun (field, i) => Quotient.ruleWitness? fuel + (d.iotaEnvironment entries block.source block.recursor block.mode i) (d.iotaRule block.source i field) + return ⟨facts, eta, iota⟩ + +end Ix.Theory.Certificate.Structure diff --git a/Ix/Theory/Certificate/Suggest.lean b/Ix/Theory/Certificate/Suggest.lean new file mode 100644 index 000000000..1b8197d41 --- /dev/null +++ b/Ix/Theory/Certificate/Suggest.lean @@ -0,0 +1,417 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Operations + +/-! +# Untrusted certificate suggestions + +This bounded producer handles dependent functions with syntactic product +heads, conservative universe simplification, and conversion certificates. +Its output is ordinary data. +Only the certified validator can turn a suggestion into acceptance. Failure +to find a certificate is a decline and says nothing about untypability. +-/ + +namespace Ix.Theory.Certificate + +open Model Certified + +universe u +variable {β : Type u} [DecidableEq β] + +/-- Untrusted search hints. The public builder derives these from the fixed +profile; the semantic validator independently checks every resulting fact. -/ +class Hints (β : Type u) where + natural : Option (ConstRef β) + +instance (priority := low) : Hints β := ⟨none⟩ + +variable [Hints β] + +def annotations : AExpr β → AnnotationTree + | .bvar _ | .sort _ | .const .. | .natLit _ => .leaf + | .app f a => .app (annotations f) (annotations a) + | .lam p A b => .lam p.toRaw (annotations A) (annotations b) + | .forallE p A B => .forallE p.toRaw (annotations A) (annotations B) + | .proj _ _ e => .proj (annotations e) + +structure Suggestion (β : Type u) where + expression : AExpr β + type : AExpr β + witness : TypingWitness β + +/-- A limited conversion search. The validator independently rechecks every +level equality and the type of the expected type. -/ +def cast? (n : Nat) (suggestion : Suggestion β) (type : AExpr β) : Option (TypingWitness β) := + if suggestion.type = type then some suggestion.witness + else + match suggestion.type, type with + | .sort l, .sort l' => + if LevelEq.check n l l' then + some (.conv suggestion.type (.succ l') suggestion.witness .sort .sort) + else none + | _, _ => none + +def headConstant? : AExpr β → Option (ConstRef β × List VLevel) + | .const r levels => some (r, levels) + | .app f _ => headConstant? f + | _ => none + +def applicationArgs : AExpr β → List (AExpr β) → List (AExpr β) + | .app f a, args => applicationArgs f (a :: args) + | _, args => args + +def lambdaBody : AExpr β → Nat × AExpr β + | .lam _ _ body => let (n, result) := lambdaBody body; (n + 1, result) + | e => (0, e) + +/-- Collect possible arguments of a closed equation telescope. This search +does not certify matching: the fully instantiated endpoint is compared below +and every application/beta step is subsequently checked by the validator. -/ +def matchEquationArgs? : AExpr β → AExpr β → List (Option (AExpr β)) → Option (List (Option (AExpr β))) + | .bvar i, actual, slots => do + let slot ← slots[i]? + match slot with + | none => some (slots.set i (some actual)) + | some previous => if previous = actual then some slots else none + | .app f x, .app g y, slots => do + let slots ← matchEquationArgs? f g slots + matchEquationArgs? x y slots + | .const r ls, .const q ms, slots => if r = q ∧ ls = ms then some slots else none + | .sort l, .sort m, slots => if l = m then some slots else none + | .proj r i major, .proj q j actual, slots => + if r = q ∧ i = j then matchEquationArgs? major actual slots else none + | .lam .., .lam .., slots | .forallE .., .forallE .., slots => some slots + | _, _, _ => none + +def instantiateLambdas? : AExpr β → List (AExpr β) → Option (AExpr β) + | e, [] => some e + | .lam _ _ body, arg :: rest => instantiateLambdas? (body.inst arg) rest + | _, _ => none + +def equationArguments? (pattern actual : AExpr β) : Option (List (AExpr β)) := do + let (count, body) := lambdaBody pattern + let slots ← matchEquationArgs? body actual (List.replicate count none) + let args ← slots.reverse.mapM id + let instantiated ← instantiateLambdas? pattern args + if instantiated = actual then some args else none + +/-- Suggest the same equation with a different final major proof. The full +application prefix must still match exactly. A subsequent typed conversion +must justify replacing that proof; this matcher alone grants no equation. -/ +def equationProofArguments? (pattern actual : AExpr β) : Option (List (AExpr β)) := do + let (count, body) := lambdaBody pattern + let .app rulePrefix _ := body | none + let .app actualPrefix _ := actual | none + let slots ← matchEquationArgs? rulePrefix actualPrefix (List.replicate count none) + let args ← slots.reverse.mapM id + let .app instantiatedPrefix _ ← instantiateLambdas? pattern args | none + if instantiatedPrefix = actualPrefix then some args else none + +mutual +def inferSource? (fuel n : Nat) (entries : Environment β) (Γ : Context β) : + VExpr β → Option (Suggestion β) := + match fuel with + | 0 => fun _ => none + | fuel + 1 => fun source => + match source with + | .bvar i => do + let A ← Γ[i]? + return ⟨.bvar i, A, .bvar⟩ + | .sort l => + if l.WF n then some ⟨.sort l, .sort (.succ l), .sort⟩ else none + | .const r ls => do + let entry ← entries r + if ls.length = entry.universes ∧ ∀ l ∈ ls, l.WF n then + return ⟨.const r ls, entry.type.instL ls, .const⟩ + else none + | .forallE A B => do + let a ← inferSource? fuel n entries Γ A + let a ← normalizeType? fuel n entries Γ a + let .sort lA := a.type | none + let b ← inferSource? fuel n entries (Γ.push a.expression) B + let b ← normalizeType? fuel n entries (Γ.push a.expression) b + let .sort lB := b.type | none + return ⟨.forallE (zeroCondition lB) a.expression b.expression, .sort (.imax lA lB), + .forallE lA lB a.witness b.witness⟩ + | .lam A body => do + let a ← inferSource? fuel n entries Γ A + let a ← normalizeType? fuel n entries Γ a + let .sort lA := a.type | none + let b ← inferSource? fuel n entries (Γ.push a.expression) body + let b ← normalizeType? fuel n entries (Γ.push a.expression) b + let B ← inferAnnotated? fuel n entries (Γ.push a.expression) b.type + let B ← normalizeType? fuel n entries (Γ.push a.expression) B + let .sort lB := B.type | none + return ⟨.lam (zeroCondition lB) a.expression b.expression, + .forallE (zeroCondition lB) a.expression b.type, + .lam lA lB b.type a.witness B.witness b.witness⟩ + | .app f arg => do + let fn ← inferSource? fuel n entries Γ f + let fn ← normalizeType? fuel n entries Γ fn + let .forallE p A B := fn.type | none + let a ← inferSource? fuel n entries Γ arg + let aw ← castWith? fuel n entries Γ a A + return ⟨.app fn.expression a.expression, B.inst a.expression, + .app p A B fn.witness aw⟩ + | .proj ref index major => do + let major ← inferSource? fuel n entries Γ major + let typed ← normalizeType? fuel n entries Γ major + let (owner, ls) ← headConstant? typed.type + if owner != ref then none else do + let entry ← entries ref + if ls.length != entry.universes then none else + entry.facts.zipIdx.findSome? fun (fact, factIndex) => do + let .typed expression type := fact | none + let (_, .proj family field _) := lambdaBody expression | none + if family != ref || field != index then none else do + let result ← applyFact? fuel n entries Γ + ⟨expression.instL ls, type.instL ls, .fact ref factIndex ls⟩ + (applicationArgs typed.type [] ++ [major.expression]) + if result.expression = .proj ref index major.expression then some result else none + | .natLit value => do + let ref ← Hints.natural (β := β) + let entry ← entries ref + if entry.universes != 0 then none else + entry.facts.zipIdx.findSome? fun (fact, i) => match fact with + | .natural _ _ => some ⟨.natLit value, .const ref [], .natLit ref i⟩ + | _ => none + +/-- Instantiate an admitted closed typing fact using checked beta results. +The result retains the actual source projection rather than a replacement +term with a merely convertible type. -/ +def applyFact? (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (fact : Suggestion β) (args : List (AExpr β)) : Option (Suggestion β) := + match fuel with + | 0 => none + | fuel + 1 => match args with + | [] => some fact + | arg :: rest => do + let .lam p D body := fact.expression | none + let .forallE q A B := fact.type | none + if p != q || D != A then none else do + let a ← inferAnnotated? fuel n entries Γ arg + let aw ← castWith? fuel n entries Γ a D + applyFact? fuel n entries Γ ⟨body.inst arg, B.inst arg, + .betaResult p D body arg B fact.witness aw⟩ rest + +def inferAnnotated? (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (e : AExpr β) : Option (Suggestion β) := + match fuel with + | 0 => none + | fuel + 1 => do + let result ← inferSource? fuel n entries Γ e.erase + if result.expression = e then some result else none + +/-- Build a conversion certificate rather than normalizing away source +syntax. The semantic validator checks the target's sort independently. -/ +def castWith? (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (suggestion : Suggestion β) (type : AExpr β) : Option (TypingWitness β) := + match fuel with + | 0 => none + | fuel + 1 => + match cast? n suggestion type with + | some witness => some witness + | none => do + let target ← inferAnnotated? fuel n entries Γ type + let target ← normalizeType? fuel n entries Γ target + let .sort l := target.type | none + let witness ← conversion? fuel n entries Γ suggestion.type type + return .conv suggestion.type l suggestion.witness target.witness witness + +def beta? (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (e : AExpr β) : Option (AExpr β × ConversionWitness β) := + match fuel with + | 0 => none + | fuel + 1 => do + let .app (.lam p D body) arg := e | none + let fn ← inferAnnotated? fuel n entries Γ (.lam p D body) + let a ← inferAnnotated? fuel n entries Γ arg + let aw ← castWith? fuel n entries Γ a D + return (body.inst arg, .beta fn.type fn.witness aw) + +def eta? (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (a b : AExpr β) : Option (ConversionWitness β) := + match fuel with + | 0 => none + | fuel + 1 => do + let .lam p D body := a | none + let fn ← inferAnnotated? fuel n entries Γ b + let fn ← normalizeType? fuel n entries Γ fn + let .forallE q D' B := fn.type | none + if p = q ∧ D = D' ∧ body = .app (b.liftN 1) (.bvar 0) then + return .eta B fn.witness + else none + +/-- A checked beta/delta step may occur in the function of an application. -/ +def headStep? (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (e : AExpr β) : Option (AExpr β × ConversionWitness β) := + match fuel with + | 0 => none + | fuel + 1 => + let delta := fun (_ : Unit) => do + let .const r ls := e | none + let entry ← entries r + if ls.length != entry.universes then none else do + let body ← entry.body + return (body.instL ls, .delta) + let app := fun (_ : Unit) => do + let .app f x := e | none + let (f', w) ← headStep? fuel n entries Γ f <|> equationStep? fuel n entries Γ f + return (.app f' x, .app w .refl) + let argument := fun (_ : Unit) => do + let .app f x := e | none + let (x', w) ← headStep? fuel n entries Γ x <|> equationStep? fuel n entries Γ x + return (.app f x', .app .refl w) + let projection := fun (_ : Unit) => do + let .proj r i x := e | none + let (x', w) ← headStep? fuel n entries Γ x <|> equationStep? fuel n entries Γ x + return (.proj r i x', .proj w) + let literal := fun (_ : Unit) => do + let .natLit value := e | none + let ref ← Hints.natural (β := β) + let entry ← entries ref + if entry.universes != 0 then none else + entry.facts.zipIdx.findSome? fun (fact, i) => match fact with + | .natural zero succ => some + ((match value with | 0 => .const zero [] | n + 1 => .app (.const succ []) (.natLit n)), .natLiteral ref i) + | _ => none + beta? fuel n entries Γ e <|> delta () <|> app () <|> argument () <|> projection () <|> literal () + +def equationStep? (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (e : AExpr β) : Option (AExpr β × ConversionWitness β) := + match fuel with + | 0 => none + | fuel + 1 => do + let (ref, levels) : ConstRef β × List VLevel ← match e with + | .proj ref _ major => do + let typed ← inferAnnotated? fuel n entries Γ major + let typed ← normalizeType? fuel n entries Γ typed + let (owner, levels) ← headConstant? typed.type + if owner = ref then some (ref, levels) else none + | _ => do + let (ref, levels) ← headConstant? e + let owner : ConstRef β := match ref with | .ctor source member _ => .member source member | _ => ref + some (owner, levels) + let entry ← entries ref + if levels.length != entry.universes then none else + entry.equations.zipIdx.findSome? fun (law, i) => do + let lhs := law.lhs.instL levels + let rhs := law.rhs.instL levels + let args ← equationArguments? lhs e <|> (do + let (_, .app _ constructor) := lambdaBody lhs | none + let (.ctor .., _) ← headConstant? constructor | none + let .app fn major := e | none + let expanded ← etaMajor? fuel n entries Γ major + equationArguments? lhs (.app fn expanded)) <|> (do + let .app _ major := e | none + let typed ← inferAnnotated? fuel n entries Γ major + let type ← inferAnnotated? fuel n entries Γ typed.type + let _ ← cast? n type (.sort .zero) + equationProofArguments? lhs e) + let lhsApplied := args.foldl AExpr.app lhs + let rhsApplied := args.foldl AExpr.app rhs + let beta ← conversion? fuel n entries Γ lhsApplied e + let equation := args.foldl (fun w _ => ConversionWitness.app w .refl) (.equation ref i levels) + return (rhsApplied, .trans lhsApplied (.symm beta) equation) + +/-- A stuck eliminator may use an existing structure's checked eta equation +to expose its constructor. This only suggests arguments: `equationStep?` +still constructs and checks conversion of the complete original application +to the chosen rule's complete instantiated left side. Model companions with +no eta equation cannot acquire this behavior from the search. -/ +def etaMajor? (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (major : AExpr β) : Option (AExpr β) := + match fuel with + | 0 => none + | fuel + 1 => do + let typed ← inferAnnotated? fuel n entries Γ major + let typed ← normalizeType? fuel n entries Γ typed + let (family, levels) ← headConstant? typed.type + let entry ← entries family + if levels.length != entry.universes then none else + entry.equations.findSome? fun law => do + let arguments := applicationArgs typed.type [] ++ [major] + let rhs ← instantiateLambdas? (law.rhs.instL levels) arguments + if rhs != major then none else do + let lhs ← instantiateLambdas? (law.lhs.instL levels) arguments + if lhs = major then none else some lhs + +/-- Reduction of an inferred type is accompanied by a conversion witness +and a fresh formation witness for the resulting type. -/ +def normalizeType? (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (suggestion : Suggestion β) : Option (Suggestion β) := + match fuel with + | 0 => none + | fuel + 1 => + match suggestion.type with + | .sort _ | .forallE .. => some suggestion + | _ => + match headStep? fuel n entries Γ suggestion.type <|> equationStep? fuel n entries Γ suggestion.type with + | none => some suggestion + | some (type, conversion) => do + let formed ← inferAnnotated? fuel n entries Γ type + let formed ← normalizeType? fuel n entries Γ formed + let .sort level := formed.type | none + normalizeType? fuel n entries Γ + ⟨suggestion.expression, type, .conv suggestion.type level suggestion.witness formed.witness conversion⟩ + +def conversion? (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (a b : AExpr β) : Option (ConversionWitness β) := + match fuel with + | 0 => none + | fuel + 1 => + if a = b then some .refl + else + -- Once a checked reduction is available, follow it without exploring + -- alternate reduction orders again after fuel exhaustion. + match headStep? fuel n entries Γ a with + | some (a', w) => do return .trans a' w (← conversion? fuel n entries Γ a' b) + | none => match headStep? fuel n entries Γ b with + | some (b', w) => do return .trans b' (← conversion? fuel n entries Γ a b') (.symm w) + | none => + let congruence := fun (_ : Unit) => + match a, b with + | .sort l, .sort l' => if LevelEq.check n l l' then some .sort else none + | .app f x, .app g y => do + return .app (← conversion? fuel n entries Γ f g) (← conversion? fuel n entries Γ x y) + | .proj r i x, .proj q j y => + if r = q ∧ i = j then (.proj ·) <$> conversion? fuel n entries Γ x y else none + | .lam p D body, .lam q D' body' => do + if p != q then none else do + let domain ← inferAnnotated? fuel n entries Γ D + let .sort l := domain.type | none + return .lam l domain.witness (← conversion? fuel n entries Γ D D') + (← conversion? fuel n entries (Γ.push D) body body') + | .forallE p D B, .forallE q D' B' => do + if p != q then none else do + let domain ← inferAnnotated? fuel n entries Γ D + let .sort l := domain.type | none + return .forallE l domain.witness (← conversion? fuel n entries Γ D D') + (← conversion? fuel n entries (Γ.push D) B B') + | _, _ => none + let proofIrrel := fun (_ : Unit) => do + let left ← inferAnnotated? fuel n entries Γ a + let type ← inferAnnotated? fuel n entries Γ left.type + let typeWitness ← cast? n type (.sort .zero) + let right ← inferAnnotated? fuel n entries Γ b + let rightWitness ← castWith? fuel n entries Γ right left.type + return .proofIrrel left.type typeWitness left.witness rightWitness + eta? fuel n entries Γ a b <|> + ((.symm ·) <$> eta? fuel n entries Γ b a) <|> congruence () <|> + (match equationStep? fuel n entries Γ a with + | some (a', w) => do return .trans a' w (← conversion? fuel n entries Γ a' b) + | none => match equationStep? fuel n entries Γ b with + | some (b', w) => do return .trans b' (← conversion? fuel n entries Γ a b') (.symm w) + | none => proofIrrel ()) +end + +def inferenceWitness? (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (source : VExpr β) : Option (InferenceWitness β) := do + let suggestion ← inferSource? fuel n entries Γ source + return ⟨annotations suggestion.expression, suggestion.type, suggestion.witness⟩ + +end Ix.Theory.Certificate diff --git a/Ix/Theory/Certified.lean b/Ix/Theory/Certified.lean new file mode 100644 index 000000000..0f772d961 --- /dev/null +++ b/Ix/Theory/Certified.lean @@ -0,0 +1,48 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Accept +import Ix.Theory.Certified.Store +import Ix.Theory.Certified.Source +import Ix.Theory.Certified.ClaimComposition +import Ix.Theory.Certified.Operations +import Ix.Theory.Certified.Ordinary.Admission + +/-! +# Certified-profile mathematical boundary + +This target exports a witness validator for dependent functions, declaration +admission with compatible model extension, and abstract acceptance/model and +no-False theorems over exact Ix syntax. Declaration checking also constructs +one compatible model for every requested member and constructor. Connecting +these results to canonical serialized input and the production Ix.Kernel checker +requires a separate adapter and refinement proof. Conditional claims check formed +source headers and extend every compatible frontier model. Aggregation checks +an acyclic provider order and replays admissions in one shared interface; +closed bundles construct their model and retain all logical axiom use. The +Ix claim/byte adapter and backend acceptance have separate refinement roots. + +Ordinary-inductive admission constructs indexed carriers and eliminators, +checks the exact source and stored recursor types and rule endpoints, and +publishes their proved equations atomically. The supported shape class uses +ordinary fields followed by independent strictly positive recursive fields. +Standard propext and choice admission checks exact types and prerequisite +interfaces and constructs their realizations. Eq K uses typed proof +irrelevance with admitted computation. Quotient admission constructs its +carrier, constructor, lift, Prop induction and soundness values and proves +the complete computation equations. Structure admission produces dependent +projection facts and whole eta/iota equations, including the checked Prop +field restriction. Nat admission pins the zero/successor source and proves +membership and constructor conversion for every numeral. Mutual/nested +admission checks companions in an earlier model environment and validates +their complete translated source equations before simultaneous publication. +Reference restoration, permutation and shared model values have proved +semantic transport laws. This route supplies no projection, eta or K facts +for the new families. Other literal forms still need separate producers. + +The semantic results explicitly quantify over `V` and `[Model.SetTheory V]`. +The existence of this set-theoretic model is a mathematical hypothesis, not a +theorem of Lean. Audit tooling and experimental metatheory are separate targets. +-/ diff --git a/Ix/Theory/Certified/Accept.lean b/Ix/Theory/Certified/Accept.lean new file mode 100644 index 000000000..fe209ea45 --- /dev/null +++ b/Ix/Theory/Certified/Accept.lean @@ -0,0 +1,139 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.Admission + +/-! +# Closed acceptance of the certified profile + +Acceptance checks the exact primitive declarations, a dependency-ordered list +of safe definitions and modeled ordinary blocks, the original proof and proposition readings, +and both typing certificates. It constructs its dependency model and starts +from the empty context. This is an abstract Ix-syntax acceptance result; +serialized bytes, Ix.Kernel execution, claims, and proving backends need their own +refinement theorems. +-/ + +namespace Ix.Theory.Certified + +open Model Model.SetTheory + +universe u v +variable {β : Type u} [DecidableEq β] + +structure ProofInput (β : Type u) where + store : Store β + universes : Nat + proof : VExpr β + proposition : VExpr β + +structure ProofWitness (β : Type u) where + declarations : List (DeclarationWitness β) + proofAnnotations : AnnotationTree + propositionAnnotations : AnnotationTree + proofWitness : TypingWitness β + propositionWitness : TypingWitness β + +structure CheckedProof (signature : PrimitiveSignature β) (input : ProofInput β) where + environment : AdmittedEnvironment.{u,v} signature input.store + proof : Reading input.universes 0 input.proof + proposition : Reading input.universes 0 input.proposition + proofReferences : proof.val.ReferencesIn environment.entries + propositionReferences : proposition.val.ReferencesIn environment.entries + typing : TypingClaim.{u,v} environment.entries [] proof.val proposition.val + isProp : TypingClaim.{u,v} environment.entries [] proposition.val (.sort .zero) + +def checkProofCertified (fuel : Nat) (signature : PrimitiveSignature β) + (input : ProofInput β) (witness : ProofWitness β) : + Option (CheckedProof.{u,v} signature input) := do + let initial ← initialize? signature input.store + let declarations ← admitDeclarations? fuel initial witness.declarations + let proof ← readAnnotations? input.universes 0 input.proof witness.proofAnnotations + let proposition ← readAnnotations? input.universes 0 input.proposition witness.propositionAnnotations + if he : proof.val.ReferencesIn declarations.val.entries then + if hP : proposition.val.ReferencesIn declarations.val.entries then do + let hp ← verifyType.{u,v} fuel input.universes declarations.val.entries [] + proposition.val (.sort .zero) witness.propositionWitness + let ht ← verifyType.{u,v} fuel input.universes declarations.val.entries [] + proof.val proposition.val witness.proofWitness + return ⟨declarations.val, proof, proposition, he, hP, ht.down, hp.down⟩ + else none + else none + +def acceptsCertified (fuel : Nat) (signature : PrimitiveSignature β) + (input : ProofInput β) (witness : ProofWitness β) : Bool := + (checkProofCertified.{u,v} fuel signature input witness).isSome + +/-- Acceptance constructs the model; it does not ask the caller to supply a +realization of unchecked declarations, annotations, or a nonempty context. -/ +theorem accepted_has_model {fuel : Nat} {signature : PrimitiveSignature β} + {input : ProofInput β} {witness : ProofWitness β} + (h : acceptsCertified.{u,v} fuel signature input witness = true) + (V : Type v) [SetTheory V] (levels : List Nat) (env : Nat → V) : + ∃ result : CheckedProof.{u,v} signature input, + checkProofCertified fuel signature input witness = some result ∧ + ∃ constants : Assignment β V, signature.Compatible result.environment.entries constants ∧ + WellDenoted constants levels env result.proof.val ∧ + WellDenoted constants levels env result.proposition.val ∧ + interp constants levels env result.proof.val ∈ˢ + interp constants levels env result.proposition.val := by + unfold acceptsCertified at h + cases hc : checkProofCertified.{u,v} fuel signature input witness with + | none => simp [hc] at h + | some result => + obtain ⟨constants, hM⟩ := result.environment.model V + exact ⟨result, rfl, constants, hM, + result.typing V constants hM.realizes levels env (Context.valid_nil constants levels env)⟩ + +/-- The accepted proposition is inhabited in every compatible interpretation +of the checked dependency interface, not only in the model chosen above. -/ +theorem accepted_proof_sound {fuel : Nat} {signature : PrimitiveSignature β} + {input : ProofInput β} {witness : ProofWitness β} + (h : acceptsCertified.{u,v} fuel signature input witness = true) : + ∃ result : CheckedProof.{u,v} signature input, + checkProofCertified fuel signature input witness = some result ∧ + ∀ (V : Type v) [SetTheory V] (constants : Assignment β V), + signature.Compatible result.environment.entries constants → ∀ levels env, + interp constants levels env result.proof.val ∈ˢ + interp constants levels env result.proposition.val := by + unfold acceptsCertified at h + cases hc : checkProofCertified.{u,v} fuel signature input witness with + | none => simp [hc] at h + | some result => + refine ⟨result, rfl, ?_⟩ + intro V _ constants hM levels env + exact (result.typing V constants hM.realizes levels env + (Context.valid_nil constants levels env)).2.2 + +theorem no_proof_of_False {fuel : Nat} {signature : PrimitiveSignature β} + {input : ProofInput β} {witness : ProofWitness β} + (V : Type v) [SetTheory V] + (hFalse : input.proposition = signature.falseExpr) + (h : acceptsCertified.{u,v} fuel signature input witness = true) : False := by + obtain ⟨result, _, constants, hM, _, _, hmem⟩ := + accepted_has_model h V [] (fun _ => empty) + have herase : result.proposition.val.erase = .const signature.falseType [] := + result.proposition.property.1.trans hFalse + have he := AExpr.eq_const_of_erase_eq herase + rw [he] at hmem + simp only [interp, List.map_nil, hM.falseValue] at hmem + exact not_mem_empty _ hmem + +/-- A source-level empty proposition can also be reached through certified +definitions or conversions. Its semantic emptiness contradicts acceptance. -/ +theorem no_proof_of_empty {fuel : Nat} {signature : PrimitiveSignature β} + {input : ProofInput β} {witness : ProofWitness β} + (V : Type v) [SetTheory V] + (hempty : ∀ result : CheckedProof.{u,v} signature input, + checkProofCertified fuel signature input witness = some result → + ∀ constants : Assignment β V, signature.Compatible result.environment.entries constants → + interp constants [] (fun _ => empty) result.proposition.val = empty) + (h : acceptsCertified.{u,v} fuel signature input witness = true) : False := by + obtain ⟨result, hc, constants, hM, _, _, hmem⟩ := + accepted_has_model h V [] (fun _ => empty) + rw [hempty result hc constants hM] at hmem + exact not_mem_empty _ hmem + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Admission.lean b/Ix/Theory/Certified/Admission.lean new file mode 100644 index 000000000..a993c1ddb --- /dev/null +++ b/Ix/Theory/Certified/Admission.lean @@ -0,0 +1,245 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Checker +import Ix.Theory.Certified.Prelude +import Ix.Theory.Certified.Ordinary.Checked +import Ix.Theory.Certified.Standard.Checked +import Ix.Theory.Certified.Quotient.Publish +import Ix.Theory.Certified.Structure.Publish +import Ix.Theory.Certified.Natural.Publish +import Ix.Theory.Certified.Modeled.Source + +namespace Ix.Theory.Certified + +open Model + +universe u v +variable {β : Type u} [DecidableEq β] + +def DefinitionReading.entry (reading : DefinitionReading β) : ConstantEntry β := + ⟨reading.universes, reading.type, some reading.body, [], []⟩ + +/-- Every published dependency has an exact source declaration in the input +store. The two exceptional forms are the completely pinned primitives. -/ +def EntrySource (signature : PrimitiveSignature β) (store : Store β) + (r : ConstRef β) (entry : ConstantEntry β) : Prop := + (r = signature.falseType ∧ entry = PrimitiveSignature.falseEntry ∧ + store.lookup r = some PrimitiveSignature.falseDeclaration) ∨ + (r = signature.falseElim ∧ entry = signature.falseElimEntry ∧ + store.lookup r = some signature.falseElimDeclaration) ∨ + (∃ (kind : DefKind) (body : AExpr β), entry.body = some body ∧ + entry.equations = [] ∧ entry.facts = [] ∧ + store.lookup r = some (.defn entry.universes kind entry.type.erase body.erase .safe)) ∨ + Ordinary.EntrySource store r entry ∨ Standard.EntrySource store r entry ∨ + Quotient.EntrySource store r entry ∨ Structure.EntrySource store r entry ∨ + Natural.EntrySource signature.natType store r entry ∨ Modeled.EntrySource store r entry + +/-- An interface may be an explicit conditional frontier. Scope and reference +closure are checked here; existence of a realization is a separate obligation. -/ +structure CheckedInterface (signature : PrimitiveSignature β) where + entries : Environment β + wf : entries.WF + present : signature.Present entries + +/-- The model field is produced by the initialization and admission functions, +starting with the fixed prelude and extending it with checked bodies. -/ +structure AdmittedEnvironment (signature : PrimitiveSignature β) (store : Store β) where + entries : Environment β + wf : entries.WF + present : signature.Present entries + source : ∀ r entry, entries r = some entry → EntrySource signature store r entry + model : ∀ (V : Type v) [SetTheory V], ∃ constants : Assignment β V, + signature.Compatible entries constants + +def AdmittedEnvironment.interface {signature : PrimitiveSignature β} {store : Store β} + (state : AdmittedEnvironment.{u,v} signature store) : CheckedInterface signature := + ⟨state.entries, state.wf, state.present⟩ + +/-- An extension preserves each previously selected interpretation. -/ +structure Extends (signature : PrimitiveSignature β) (entries entries' : Environment β) : Prop where + lookup : ∀ r entry, entries r = some entry → entries' r = some entry + models : ∀ (V : Type v) [SetTheory V] (constants : Assignment β V), + signature.Compatible entries constants → ∃ constants' : Assignment β V, + signature.Compatible entries' constants' ∧ Assignment.AgreesOn entries constants constants' + +omit [DecidableEq β] in +theorem Extends.refl (signature : PrimitiveSignature β) (entries : Environment β) : + Extends.{u,v} signature entries entries := + ⟨fun _ _ h => h, fun _ _ constants h => ⟨constants, h, fun _ _ _ _ => rfl⟩⟩ + +omit [DecidableEq β] in +theorem Extends.trans {signature : PrimitiveSignature β} {a b c : Environment β} + (hab : Extends.{u,v} signature a b) (hbc : Extends.{u,v} signature b c) : + Extends.{u,v} signature a c := by + refine ⟨fun r entry h => hbc.lookup r entry (hab.lookup r entry h), ?_⟩ + intro V _ constants hM + obtain ⟨constants', hM', hagree⟩ := hab.models V constants hM + obtain ⟨constants'', hM'', hagree'⟩ := hbc.models V constants' hM' + refine ⟨constants'', hM'', ?_⟩ + intro r entry hr levels + exact (hagree' r entry (hab.lookup r entry hr) levels).trans (hagree r entry hr levels) + +/-- Admission checks and their universal extension theorem do not require a +model of a deferred frontier. Every newly published entry has a checked source; +an existing frontier entry retains its exact interface. -/ +structure CheckedExtension (signature : PrimitiveSignature β) (store : Store β) + (input : CheckedInterface signature) where + result : CheckedInterface signature + extension : Extends.{u,v} signature input.entries result.entries + source : ∀ r entry, result.entries r = some entry → + input.entries r = some entry ∨ EntrySource signature store r entry + +def CheckedExtension.refl {signature : PrimitiveSignature β} {store : Store β} + (input : CheckedInterface signature) : CheckedExtension.{u,v} signature store input := + ⟨input, Extends.refl signature input.entries, fun _ _ h => Or.inl h⟩ + +def CheckedExtension.trans {signature : PrimitiveSignature β} {store : Store β} + {input : CheckedInterface signature} (first : CheckedExtension.{u,v} signature store input) + (second : CheckedExtension.{u,v} signature store first.result) : + CheckedExtension.{u,v} signature store input where + result := second.result + extension := first.extension.trans second.extension + source := by + intro r entry h + rcases second.source r entry h with old | new + · exact first.source r entry old + · exact Or.inr new + +/-- A closed admission uses the same checked extension and supplies the model +and source provenance constructed by its earlier successful admissions. -/ +def CheckedExtension.admit {signature : PrimitiveSignature β} {store : Store β} + (state : AdmittedEnvironment.{u,v} signature store) + (checked : CheckedExtension.{u,v} signature store state.interface) : + { result : AdmittedEnvironment.{u,v} signature store // + Extends.{u,v} signature state.entries result.entries } := + ⟨{ + entries := checked.result.entries + wf := checked.result.wf + present := checked.result.present + source := by + intro r entry h + rcases checked.source r entry h with old | new + · exact state.source r entry old + · exact new + model := by + intro V _ + obtain ⟨constants, hM⟩ := state.model V + obtain ⟨constants', hM', _⟩ := checked.extension.models V constants hM + exact ⟨constants', hM'⟩ + }, checked.extension⟩ + +def initialize? (signature : PrimitiveSignature β) (store : Store β) : + Option (AdmittedEnvironment.{u,v} signature store) := + if h : signature.validate store = true then + some { + entries := signature.environment + wf := signature.environment_wf + present := signature.present_environment + source := by + intro r entry hr + have hv := (signature.validate_iff store).mp h + unfold PrimitiveSignature.environment at hr + split at hr + next he => + subst r + cases Option.some.inj hr + exact Or.inl ⟨rfl, rfl, hv.1⟩ + next => + split at hr + next he => + subst r + cases Option.some.inj hr + exact Or.inr (Or.inl ⟨rfl, rfl, hv.2⟩) + next => contradiction + model := fun V _ => ⟨signature.assignment, signature.compatible_assignment⟩ + } + else none + +structure DefinitionWitness (β : Type u) where + ref : ConstRef β + typeAnnotations : AnnotationTree + bodyAnnotations : AnnotationTree + typeLevel : VLevel + typeWitness : TypingWitness β + bodyWitness : TypingWitness β + +def checkDefinitionExtension? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : CheckedInterface signature) (witness : DefinitionWitness β) : + Option (CheckedExtension.{u,v} signature store state) := + match hs : store.lookup witness.ref with + | none => none + | some source => do + let reading : { reading : DefinitionReading β // reading.erase = source } ← + readDefinition? source witness.typeAnnotations witness.bodyAnnotations + let entry := reading.val.entry + if fresh : state.entries witness.ref = none then + if hTr : reading.val.type.ReferencesIn state.entries then + if hBr : reading.val.body.ReferencesIn state.entries then do + let hT ← verifyType.{u,v} fuel reading.val.universes state.entries [] + reading.val.type (.sort witness.typeLevel) witness.typeWitness + let hB ← verifyType.{u,v} fuel reading.val.universes state.entries [] + reading.val.body reading.val.type witness.bodyWitness + have extension : Extends.{u,v} signature state.entries + (state.entries.insert witness.ref entry) := by + refine ⟨fun _ _ h => Environment.insert_old fresh h, ?_⟩ + intro V _ constants hM + obtain ⟨constants', hM', hagree⟩ := extend_definition (entry := entry) state.wf fresh rfl rfl rfl + reading.val.bodyScope hTr hBr hT.down hB.down constants hM.realizes + exact ⟨constants', hM.extend signature state.present hM' hagree, hagree⟩ + let result : CheckedInterface signature := { + entries := state.entries.insert witness.ref entry + wf := state.wf.insert reading.val.typeScope + (fun body hb => by cases Option.some.inj hb; exact reading.val.bodyScope) + hTr (fun body hb => by cases Option.some.inj hb; exact hBr) + (by simp [entry, DefinitionReading.entry]) + (by simp [entry, DefinitionReading.entry]) + (by simp [entry, DefinitionReading.entry]) + (by simp [entry, DefinitionReading.entry]) + present := state.present.insert signature fresh + } + return { + result, extension + source := by + intro r old h + dsimp only [result] at h + unfold Environment.insert at h + split at h + next hr => + subst r + cases Option.some.inj h + refine Or.inr (Or.inr (Or.inr (Or.inl ⟨reading.val.kind, reading.val.body, rfl, rfl, rfl, ?_⟩))) + exact hs.trans (congrArg some reading.property.symm) + next => exact Or.inl h + } + else none + else none + else none + +def admitDefinition? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : AdmittedEnvironment.{u,v} signature store) (witness : DefinitionWitness β) : + Option { result : AdmittedEnvironment.{u,v} signature store // + Extends.{u,v} signature state.entries result.entries } := do + let checked ← checkDefinitionExtension?.{u,v} fuel (store := store) state.interface witness + return checked.admit state + +def admitDefinitions? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : AdmittedEnvironment.{u,v} signature store) : + List (DefinitionWitness β) → + Option { result : AdmittedEnvironment.{u,v} signature store // + Extends.{u,v} signature state.entries result.entries } + | [] => some ⟨state, Extends.refl signature state.entries⟩ + | witness :: rest => do + let step ← admitDefinition? fuel state witness + let rest ← admitDefinitions? fuel step.val rest + return ⟨rest.val, step.property.trans rest.property⟩ + +theorem admitDefinition?_extends {fuel : Nat} {signature : PrimitiveSignature β} + {store : Store β} {state : AdmittedEnvironment.{u,v} signature store} + {witness : DefinitionWitness β} {result} + (_ : admitDefinition? fuel state witness = some result) : + Extends.{u,v} signature state.entries result.val.entries := result.property + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Basis/Equality.lean b/Ix/Theory/Certified/Basis/Equality.lean new file mode 100644 index 000000000..bedac20da --- /dev/null +++ b/Ix/Theory/Certified/Basis/Equality.lean @@ -0,0 +1,199 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Basis.Interface +import Ix.Theory.Certified.Ordinary.Checked + +/-! Equality's meaning follows from the admitted former, reflexivity and +dependent eliminator types, in every compatible model. It does not assume +that an arbitrary prefix assignment is the model chosen by one producer. -/ + +namespace Ix.Theory.Certified.Basis.Equality + +open Model Model.SetTheory Model.SetModel + +universe u v +variable {β : Type u} + +def shape : Ordinary.Shape β := + ⟨1, [.sort (.param 0), .bvar 0], [.bvar 1], .zero, [⟨[], [], [.bvar 0]⟩]⟩ + +def type : AExpr β := + .forallE .never (.sort (.param 0)) + (.forallE .never (.bvar 0) (.forallE .never (.bvar 1) (.sort .zero))) + +def applied (family : ConstRef β) (l : VLevel) (A a b : AExpr β) : AExpr β := + .appN (.const family [l]) [A, a, b] + +def reflexivity (refl : ConstRef β) (l : VLevel) (A a : AExpr β) : AExpr β := + .appN (.const refl [l]) [A, a] + +def reflType (family : ConstRef β) : AExpr β := + .forallE .always (.sort (.param 0)) (.forallE .always (.bvar 0) + (applied family (.param 0) (.bvar 1) (.bvar 0) (.bvar 0))) + +def recType (family refl : ConstRef β) : AExpr β := + .forallE (.param 0) (.sort (.param 1)) <| + .forallE (.param 0) (.bvar 0) <| + .forallE (.param 0) + (.forallE .never (.bvar 1) + (.forallE .never (applied family (.param 1) (.bvar 2) (.bvar 1) (.bvar 0)) (.sort (.param 0)))) <| + .forallE (.param 0) + (.appN (.bvar 0) [.bvar 1, reflexivity refl (.param 1) (.bvar 2) (.bvar 1)]) <| + .forallE (.param 0) (.bvar 3) <| + .forallE (.param 0) (applied family (.param 1) (.bvar 4) (.bvar 3) (.bvar 0)) <| + .appN (.bvar 3) [.bvar 1, .bvar 0] + +structure Interface (entries : Environment β) (family refl recursor : ConstRef β) : Prop where + former : entries.HasType family 1 type + reflexivity : entries.HasType refl 1 (reflType family) + elimination : entries.HasType recursor 2 (recType family refl) + +instance [DecidableEq β] (entries : Environment β) (family refl recursor : ConstRef β) : + Decidable (Interface entries family refl recursor) := + decidable_of_iff (entries.HasType family 1 type ∧ entries.HasType refl 1 (reflType family) ∧ + entries.HasType recursor 2 (recType family refl)) + ⟨fun h => ⟨h.1, h.2.1, h.2.2⟩, fun h => ⟨h.former, h.reflexivity, h.elimination⟩⟩ + +theorem shape_type : (shape : Ordinary.Shape β).type = type := rfl + +theorem shape_reflType (source : β) : + (⟨[], [], [.bvar 0]⟩ : Ordinary.Constructor β).type shape source = + reflType (.member source 0) := rfl + +theorem shape_recType (source : β) : shape.recursorType source .large = + recType (.member source 0) (.ctor source 0 0) := rfl + +theorem Interface.of_checked [DecidableEq β] {entries : Environment β} {store : Store β} {source recursor : β} + (h : Ordinary.CheckedBlock.{u,v} entries store source recursor shape .large) : + Interface (shape.publishedEnvironment entries source recursor .large) + (.member source 0) (.ctor source 0 0) (.member recursor 0) := by + constructor + · refine ⟨shape.familyEntry, ?_, rfl, shape_type⟩ + exact Environment.insert_old h.recursorChecked.fresh + (Environment.overlay_old (Ordinary.Shape.constructorEntries_fresh h.shapeChecked) + (Environment.insert_same ..)) + · refine ⟨shape.constructorEntry source ⟨[], [], [.bvar 0]⟩, ?_, rfl, shape_reflType source⟩ + apply Environment.insert_old h.recursorChecked.fresh + apply Environment.overlay_new + simp [Ordinary.Shape.constructorEntries, shape] + · exact ⟨shape.publishedRecursorEntry source recursor .large, + Environment.insert_same .., rfl, shape_recType source⟩ + +theorem Interface.of_extension {entries next : Environment β} {family refl recursor : ConstRef β} + (h : Interface entries family refl recursor) + (hext : ∀ r entry, entries r = some entry → next r = some entry) : + Interface next family refl recursor := by + constructor + all_goals + first + | obtain ⟨entry, he, hn, ht⟩ := h.former + exact ⟨entry, hext _ _ he, hn, ht⟩ + | obtain ⟨entry, he, hn, ht⟩ := h.reflexivity + exact ⟨entry, hext _ _ he, hn, ht⟩ + | obtain ⟨entry, he, hn, ht⟩ := h.elimination + exact ⟨entry, hext _ _ he, hn, ht⟩ + +variable {V : Type v} [SetTheory V] + +noncomputable def value (constants : Assignment β V) (family : ConstRef β) + (u : Nat) (A a b : V) : V := app (app (app (constants family [u]) A) a) b + +noncomputable def reflValue (constants : Assignment β V) (refl : ConstRef β) + (u : Nat) (A a : V) : V := app (app (constants refl [u]) A) a + +theorem type_interp (constants : Assignment β V) (u : Nat) (env : Nat → V) : + interp constants [u] env (type : AExpr β) = + piR 1 (univ u) (fun A => piR 1 A (fun _ => piR 1 A (fun _ => univZero))) := by + simp [type, interp, VLevel.eval, Valuation.cons, univ_zero] + +theorem reflType_interp (constants : Assignment β V) (family : ConstRef β) + (u : Nat) (env : Nat → V) : + interp constants [u] env (reflType family) = + piR 0 (univ u) (fun A => piR 0 A (fun a => value constants family u A a a)) := by + simp [reflType, applied, AExpr.appN, value, interp, VLevel.eval, Valuation.cons] + +theorem recType_zero_interp (constants : Assignment β V) (family refl : ConstRef β) + (u : Nat) (env : Nat → V) : + interp constants [0, u] env (recType family refl) = + piR 0 (univ u) (fun A => piR 0 A (fun a => + piR 0 (piR 1 A (fun b => piR 1 (value constants family u A a b) (fun _ => univZero))) + (fun motive => piR 0 (app (app motive a) (reflValue constants refl u A a)) + (fun _ => piR 0 A (fun b => piR 0 (value constants family u A a b) + (fun h => app (app motive b) h)))))) := by + simp [recType, applied, reflexivity, AExpr.appN, value, reflValue, interp, VLevel.eval, + Valuation.cons, regime, PropWhen.param, PropWhen.holds, univ_zero] + +variable {entries : Environment β} {family refl recursor : ConstRef β} + {constants : Assignment β V} + +theorem value_mem_univZero (h : Interface entries family refl recursor) (hM : Realizes constants entries) + {u : Nat} {A a b : V} (hA : A ∈ˢ univ u) (ha : a ∈ˢ A) (hb : b ∈ˢ A) : + value constants family u A a b ∈ˢ univZero := by + have ht := h.former.member hM (levels := [u]) rfl (fun _ => empty) + rw [type_interp] at ht + have h1 : app (constants family [u]) A ∈ˢ + piR 1 A (fun _ => piR 1 A (fun _ => univZero)) := + app_mem_piR_pos (by decide : 1 ≠ 0) ht hA + have h2 : app (app (constants family [u]) A) a ∈ˢ piR 1 A (fun _ => univZero) := + app_mem_piR_pos (by decide : 1 ≠ 0) h1 ha + exact app_mem_piR_pos (by decide : 1 ≠ 0) h2 hb + +theorem reflValue_mem (h : Interface entries family refl recursor) (hM : Realizes constants entries) + {u : Nat} {A a : V} (hA : A ∈ˢ univ u) (ha : a ∈ˢ A) : + reflValue constants refl u A a ∈ˢ value constants family u A a a := by + have ht := h.reflexivity.member hM (levels := [u]) rfl (fun _ => empty) + rw [reflType_interp] at ht + have h1 : app (constants refl [u]) A ∈ˢ piR 0 A (fun x => value constants family u A x x) := + app_mem_piR ht hA (fun _ _ _ => piR_zero_mem_univZero) + exact app_mem_piR (B := fun x => value constants family u A x x) h1 ha + (fun _ b hb => value_mem_univZero h hM hA hb hb) + +theorem exists_of_mem_piR_zero {A f a : V} {B : V → V} + (hf : f ∈ˢ piR 0 A B) (ha : a ∈ˢ A) : ∃ b, b ∈ˢ B a := by + rw [piR_zero] at hf + exact of_mem_truthVal hf a ha + +theorem eq_of_mem (h : Interface entries family refl recursor) (hM : Realizes constants entries) + {u : Nat} {A a b proof : V} (hA : A ∈ˢ univ u) (ha : a ∈ˢ A) (hb : b ∈ˢ A) + (hp : proof ∈ˢ value constants family u A a b) : a = b := by + let motive := lamR 1 A (fun b => lamR 1 (value constants family u A a b) (fun _ => eqv a b)) + have hm : motive ∈ˢ piR 1 A (fun b => + piR 1 (value constants family u A a b) (fun _ => univZero)) := + lamR_mem fun _ _ => lamR_mem fun _ _ => eqv_mem_univZero _ _ + have hbeta (x q : V) (hx : x ∈ˢ A) (hq : q ∈ˢ value constants family u A a x) : + app (app motive x) q = eqv a x := by + rw [app_lamR_pos (by decide : 1 ≠ 0) hx, app_lamR_pos (by decide : 1 ≠ 0) hq] + have hminor : (pt : V) ∈ˢ app (app motive a) (reflValue constants refl u A a) := by + rw [hbeta _ _ ha (reflValue_mem h hM hA ha)] + exact pt_mem_eqv_self _ + have hr := h.elimination.member hM (levels := [0, u]) rfl (fun _ => empty) + rw [recType_zero_interp] at hr + obtain ⟨rA, hrA⟩ := exists_of_mem_piR_zero hr hA + obtain ⟨ra, hra⟩ := exists_of_mem_piR_zero hrA ha + obtain ⟨rm, hrm⟩ := exists_of_mem_piR_zero hra hm + obtain ⟨ri, hri⟩ := exists_of_mem_piR_zero hrm hminor + obtain ⟨rb, hrb⟩ := exists_of_mem_piR_zero hri hb + obtain ⟨result, hresult⟩ := exists_of_mem_piR_zero hrb hp + rw [hbeta _ _ hb hp] at hresult + exact eq_of_mem_eqv hresult + +/-- The prerequisite needed by standard axiom realizations is derived from +admitted types. No equality-meaning oracle is added to the prefix model. -/ +theorem value_eq_eqv (h : Interface entries family refl recursor) (hM : Realizes constants entries) + {u : Nat} {A a b : V} (hA : A ∈ˢ univ u) (ha : a ∈ˢ A) (hb : b ∈ˢ A) : + value constants family u A a b = eqv a b := by + apply univZero_ext (value_mem_univZero h hM hA ha hb) (eqv_mem_univZero _ _) + · intro hp + have he := eq_of_mem h hM hA ha hb hp + subst b + exact pt_mem_eqv_self _ + · intro hp + have he := eq_of_mem_eqv hp + subst b + have hr := reflValue_mem h hM hA ha + rwa [eq_pt_of_mem_univZero (value_mem_univZero h hM hA ha ha) hr] at hr + +end Ix.Theory.Certified.Basis.Equality diff --git a/Ix/Theory/Certified/Basis/Iff.lean b/Ix/Theory/Certified/Basis/Iff.lean new file mode 100644 index 000000000..e5209ba86 --- /dev/null +++ b/Ix/Theory/Certified/Basis/Iff.lean @@ -0,0 +1,171 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Basis.Equality + +namespace Ix.Theory.Certified.Basis.Iff + +open Model Model.SetTheory Model.SetModel + +universe u v +variable {β : Type u} + +def arrow (P Q : AExpr β) : AExpr β := .forallE .always P (Q.liftN 1) + +def constructor : Ordinary.Constructor β := + ⟨[arrow (.bvar 1) (.bvar 0), arrow (.bvar 1) (.bvar 2)], [], []⟩ + +def shape : Ordinary.Shape β := ⟨0, [.sort .zero, .sort .zero], [], .zero, [constructor]⟩ + +def type : AExpr β := + .forallE .never (.sort .zero) (.forallE .never (.sort .zero) (.sort .zero)) + +def applied (family : ConstRef β) (P Q : AExpr β) : AExpr β := .appN (.const family []) [P, Q] + +def introduction (ctor : ConstRef β) (P Q f g : AExpr β) : AExpr β := + .appN (.const ctor []) [P, Q, f, g] + +def introType (family : ConstRef β) : AExpr β := + .forallE .always (.sort .zero) <| .forallE .always (.sort .zero) <| + .forallE .always (arrow (.bvar 1) (.bvar 0)) <| + .forallE .always (arrow (.bvar 1) (.bvar 2)) <| + applied family (.bvar 3) (.bvar 2) + +def recType (family ctor : ConstRef β) : AExpr β := + .forallE (.param 0) (.sort .zero) <| .forallE (.param 0) (.sort .zero) <| + .forallE (.param 0) (.forallE .never (applied family (.bvar 1) (.bvar 0)) (.sort (.param 0))) <| + .forallE (.param 0) + (.forallE (.param 0) (arrow (.bvar 2) (.bvar 1)) + (.forallE (.param 0) (arrow (.bvar 2) (.bvar 3)) + (.app (.bvar 2) (introduction ctor (.bvar 4) (.bvar 3) (.bvar 1) (.bvar 0))))) <| + .forallE (.param 0) (applied family (.bvar 3) (.bvar 2)) <| + .app (.bvar 2) (.bvar 0) + +structure Interface (entries : Environment β) (family ctor recursor : ConstRef β) : Prop where + former : entries.HasType family 0 type + introduction : entries.HasType ctor 0 (introType family) + elimination : entries.HasType recursor 1 (recType family ctor) + +instance [DecidableEq β] (entries : Environment β) (family ctor recursor : ConstRef β) : + Decidable (Interface entries family ctor recursor) := + decidable_of_iff (entries.HasType family 0 type ∧ entries.HasType ctor 0 (introType family) ∧ + entries.HasType recursor 1 (recType family ctor)) + ⟨fun h => ⟨h.1, h.2.1, h.2.2⟩, fun h => ⟨h.former, h.introduction, h.elimination⟩⟩ + +theorem shape_type : (shape : Ordinary.Shape β).type = type := rfl +theorem shape_introType (source : β) : constructor.type shape source = introType (.member source 0) := rfl +theorem shape_recType (source : β) : shape.recursorType source .large = + recType (.member source 0) (.ctor source 0 0) := rfl + +theorem Interface.of_checked [DecidableEq β] {entries : Environment β} {store : Store β} {source recursor : β} + (h : Ordinary.CheckedBlock.{u,v} entries store source recursor shape .large) : + Interface (shape.publishedEnvironment entries source recursor .large) + (.member source 0) (.ctor source 0 0) (.member recursor 0) := by + constructor + · refine ⟨shape.familyEntry, ?_, rfl, shape_type⟩ + exact Environment.insert_old h.recursorChecked.fresh + (Environment.overlay_old (Ordinary.Shape.constructorEntries_fresh h.shapeChecked) + (Environment.insert_same ..)) + · refine ⟨shape.constructorEntry source constructor, ?_, rfl, shape_introType source⟩ + apply Environment.insert_old h.recursorChecked.fresh + apply Environment.overlay_new + simp [Ordinary.Shape.constructorEntries, shape] + · exact ⟨shape.publishedRecursorEntry source recursor .large, + Environment.insert_same .., rfl, shape_recType source⟩ + +variable {V : Type v} [SetTheory V] + +noncomputable def implication (P Q : V) : V := piR 0 P (fun _ => Q) +noncomputable def value (constants : Assignment β V) (family : ConstRef β) (P Q : V) : V := + app (app (constants family []) P) Q +noncomputable def introValue (constants : Assignment β V) (ctor : ConstRef β) (P Q f g : V) : V := + app (app (app (app (constants ctor []) P) Q) f) g + +theorem type_interp (constants : Assignment β V) (env : Nat → V) : + interp constants [] env (type : AExpr β) = + piR 1 univZero (fun _ => piR 1 univZero (fun _ => univZero)) := by + simp [type, interp, VLevel.eval, univ_zero] + +theorem introType_interp (constants : Assignment β V) (family : ConstRef β) (env : Nat → V) : + interp constants [] env (introType family) = + piR 0 univZero (fun P => piR 0 univZero (fun Q => + piR 0 (implication P Q) (fun _ => piR 0 (implication Q P) (fun _ => value constants family P Q)))) := by + simp [introType, arrow, applied, implication, value, AExpr.appN, AExpr.liftN, liftVar, + interp, VLevel.eval, Valuation.cons, univ_zero] + +theorem recType_zero_interp (constants : Assignment β V) (family ctor : ConstRef β) (env : Nat → V) : + interp constants [0] env (recType family ctor) = + piR 0 univZero (fun P => piR 0 univZero (fun Q => + piR 0 (piR 1 (value constants family P Q) (fun _ => univZero)) (fun motive => + piR 0 (piR 0 (implication P Q) (fun f => piR 0 (implication Q P) + (fun g => app motive (introValue constants ctor P Q f g)))) + (fun _ => piR 0 (value constants family P Q) (fun h => app motive h))))) := by + simp [recType, arrow, applied, introduction, implication, value, introValue, + AExpr.appN, AExpr.liftN, liftVar, interp, VLevel.eval, Valuation.cons, regime, + PropWhen.param, PropWhen.always, PropWhen.holds, univ_zero] + +variable {entries : Environment β} {family ctor recursor : ConstRef β} + {constants : Assignment β V} + +theorem value_mem_univZero (h : Interface entries family ctor recursor) (hM : Realizes constants entries) + {P Q : V} (hP : P ∈ˢ univZero) (hQ : Q ∈ˢ univZero) : + value constants family P Q ∈ˢ univZero := by + have ht := h.former.member hM (levels := []) rfl (fun _ => empty) + rw [type_interp] at ht + have h1 : app (constants family []) P ∈ˢ piR 1 univZero (fun _ => univZero) := + app_mem_piR_pos (by decide : 1 ≠ 0) ht hP + exact app_mem_piR_pos (by decide : 1 ≠ 0) h1 hQ + +theorem introValue_mem (h : Interface entries family ctor recursor) (hM : Realizes constants entries) + {P Q f g : V} (hP : P ∈ˢ univZero) (hQ : Q ∈ˢ univZero) + (hf : f ∈ˢ implication P Q) (hg : g ∈ˢ implication Q P) : + introValue constants ctor P Q f g ∈ˢ value constants family P Q := by + have ht := h.introduction.member hM (levels := []) rfl (fun _ => empty) + rw [introType_interp] at ht + have h1 := app_mem_piR ht hP (fun _ _ _ => piR_zero_mem_univZero) + have h2 := app_mem_piR h1 hQ (fun _ _ _ => piR_zero_mem_univZero) + have h3 := app_mem_piR h2 hf (fun _ _ _ => piR_zero_mem_univZero) + exact app_mem_piR h3 hg (fun _ _ _ => value_mem_univZero h hM hP hQ) + +theorem eliminate (h : Interface entries family ctor recursor) (hM : Realizes constants entries) + {P Q C proof : V} (hP : P ∈ˢ univZero) (hQ : Q ∈ˢ univZero) (hC : C ∈ˢ univZero) + (minor : ∀ f, f ∈ˢ implication P Q → ∀ g, g ∈ˢ implication Q P → (pt : V) ∈ˢ C) + (hp : proof ∈ˢ value constants family P Q) : (pt : V) ∈ˢ C := by + let motive := lamR 1 (value constants family P Q) (fun _ => C) + have hm : motive ∈ˢ piR 1 (value constants family P Q) (fun _ => univZero) := + lamR_mem fun _ _ => hC + let minorValue := lamR 0 (implication P Q) (fun _ => lamR 0 (implication Q P) (fun _ => pt)) + have hminor : minorValue ∈ˢ piR 0 (implication P Q) (fun f => piR 0 (implication Q P) + (fun g => app motive (introValue constants ctor P Q f g))) := by + apply lamR_mem + intro f hf + apply lamR_mem + intro g hg + rw [app_lamR_pos (by decide : 1 ≠ 0) (introValue_mem h hM hP hQ hf hg)] + exact minor f hf g hg + have hr := h.elimination.member hM (levels := [0]) rfl (fun _ => empty) + rw [recType_zero_interp] at hr + obtain ⟨rP, hrP⟩ := Equality.exists_of_mem_piR_zero hr hP + obtain ⟨rQ, hrQ⟩ := Equality.exists_of_mem_piR_zero hrP hQ + obtain ⟨rm, hrm⟩ := Equality.exists_of_mem_piR_zero hrQ hm + obtain ⟨ri, hri⟩ := Equality.exists_of_mem_piR_zero hrm hminor + obtain ⟨result, hresult⟩ := Equality.exists_of_mem_piR_zero hri hp + rw [app_lamR_pos (by decide : 1 ≠ 0) hp] at hresult + rwa [eq_pt_of_mem_univZero hC hresult] at hresult + +theorem eq_of_mem (h : Interface entries family ctor recursor) (hM : Realizes constants entries) + {P Q proof : V} (hP : P ∈ˢ univZero) (hQ : Q ∈ˢ univZero) + (hp : proof ∈ˢ value constants family P Q) : P = Q := by + have hf : (pt : V) ∈ˢ implication P Q := eliminate h hM hP hQ piR_zero_mem_univZero + (fun f hf _ _ => by rwa [eq_pt_of_mem_piR_zero hf] at hf) hp + have hg : (pt : V) ∈ˢ implication Q P := eliminate h hM hP hQ piR_zero_mem_univZero + (fun _ _ g hg => by rwa [eq_pt_of_mem_piR_zero hg] at hg) hp + apply univZero_ext hP hQ + · intro hpt + simpa only [app_pt] using app_mem_piR hf hpt (fun _ _ _ => hQ) + · intro hpt + simpa only [app_pt] using app_mem_piR hg hpt (fun _ _ _ => hP) + +end Ix.Theory.Certified.Basis.Iff diff --git a/Ix/Theory/Certified/Basis/Interface.lean b/Ix/Theory/Certified/Basis/Interface.lean new file mode 100644 index 000000000..761e3ce2e --- /dev/null +++ b/Ix/Theory/Certified/Basis/Interface.lean @@ -0,0 +1,38 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Environment + +namespace Ix.Theory.Model.Environment + +open SetTheory + +universe u v +variable {β : Type u} + +/-- An exact type interface read from an already admitted entry. Bodies and +equations remain those of that entry; this predicate grants no admission. -/ +def HasType (entries : Environment β) (ref : ConstRef β) (n : Nat) (type : AExpr β) : Prop := + ∃ entry, entries ref = some entry ∧ entry.universes = n ∧ entry.type = type + +instance [DecidableEq β] (entries : Environment β) (ref : ConstRef β) (n : Nat) (type : AExpr β) : + Decidable (entries.HasType ref n type) := + match h : entries ref with + | none => .isFalse (by rintro ⟨entry, he, _⟩; simp [h] at he) + | some entry => + if he : entry.universes = n ∧ entry.type = type then + .isTrue ⟨entry, h, he⟩ + else .isFalse (by rintro ⟨other, ho, hu, ht⟩; cases h.symm.trans ho; exact he ⟨hu, ht⟩) + +variable {V : Type v} [SetTheory V] {constants : Assignment β V} {entries : Environment β} + +theorem HasType.member {ref : ConstRef β} {n : Nat} {type : AExpr β} + (h : entries.HasType ref n type) (hM : Realizes constants entries) + {levels : List Nat} (hn : levels.length = n) (env : Nat → V) : + constants ref levels ∈ˢ interp constants levels env type := by + obtain ⟨entry, he, rfl, rfl⟩ := h + exact hM.member ref entry he levels hn env + +end Ix.Theory.Model.Environment diff --git a/Ix/Theory/Certified/Basis/Nonempty.lean b/Ix/Theory/Certified/Basis/Nonempty.lean new file mode 100644 index 000000000..e371e006e --- /dev/null +++ b/Ix/Theory/Certified/Basis/Nonempty.lean @@ -0,0 +1,138 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Basis.Equality + +namespace Ix.Theory.Certified.Basis.Nonempty + +open Model Model.SetTheory Model.SetModel + +universe u v +variable {β : Type u} + +def constructor : Ordinary.Constructor β := ⟨[.bvar 0], [], []⟩ +def shape : Ordinary.Shape β := ⟨1, [.sort (.param 0)], [], .zero, [constructor]⟩ +def type : AExpr β := .forallE .never (.sort (.param 0)) (.sort .zero) +def applied (family : ConstRef β) (l : VLevel) (A : AExpr β) : AExpr β := .app (.const family [l]) A +def introduction (ctor : ConstRef β) (l : VLevel) (A a : AExpr β) : AExpr β := + .appN (.const ctor [l]) [A, a] +def introType (family : ConstRef β) : AExpr β := + .forallE .always (.sort (.param 0)) (.forallE .always (.bvar 0) (applied family (.param 0) (.bvar 1))) +def recType (family ctor : ConstRef β) : AExpr β := + .forallE .always (.sort (.param 0)) <| + .forallE .always (.forallE .never (applied family (.param 0) (.bvar 0)) (.sort .zero)) <| + .forallE .always + (.forallE .always (.bvar 1) + (.app (.bvar 1) (introduction ctor (.param 0) (.bvar 2) (.bvar 0)))) <| + .forallE .always (applied family (.param 0) (.bvar 2)) <| + .app (.bvar 2) (.bvar 0) + +structure Interface (entries : Environment β) (family ctor recursor : ConstRef β) : Prop where + former : entries.HasType family 1 type + introduction : entries.HasType ctor 1 (introType family) + elimination : entries.HasType recursor 1 (recType family ctor) + +instance [DecidableEq β] (entries : Environment β) (family ctor recursor : ConstRef β) : + Decidable (Interface entries family ctor recursor) := + decidable_of_iff (entries.HasType family 1 type ∧ entries.HasType ctor 1 (introType family) ∧ + entries.HasType recursor 1 (recType family ctor)) + ⟨fun h => ⟨h.1, h.2.1, h.2.2⟩, fun h => ⟨h.former, h.introduction, h.elimination⟩⟩ + +theorem shape_type : (shape : Ordinary.Shape β).type = type := rfl +theorem shape_introType (source : β) : constructor.type shape source = introType (.member source 0) := rfl +theorem shape_recType (source : β) : shape.recursorType source .small = + recType (.member source 0) (.ctor source 0 0) := rfl + +theorem Interface.of_checked [DecidableEq β] {entries : Environment β} {store : Store β} {source recursor : β} + (h : Ordinary.CheckedBlock.{u,v} entries store source recursor shape .small) : + Interface (shape.publishedEnvironment entries source recursor .small) + (.member source 0) (.ctor source 0 0) (.member recursor 0) := by + constructor + · refine ⟨shape.familyEntry, ?_, rfl, shape_type⟩ + exact Environment.insert_old h.recursorChecked.fresh + (Environment.overlay_old (Ordinary.Shape.constructorEntries_fresh h.shapeChecked) + (Environment.insert_same ..)) + · refine ⟨shape.constructorEntry source constructor, ?_, rfl, shape_introType source⟩ + apply Environment.insert_old h.recursorChecked.fresh + apply Environment.overlay_new + simp [Ordinary.Shape.constructorEntries, shape] + · exact ⟨shape.publishedRecursorEntry source recursor .small, + Environment.insert_same .., rfl, shape_recType source⟩ + +variable {V : Type v} [SetTheory V] + +noncomputable def value (constants : Assignment β V) (family : ConstRef β) (u : Nat) (A : V) : V := + app (constants family [u]) A +noncomputable def introValue (constants : Assignment β V) (ctor : ConstRef β) (u : Nat) (A a : V) : V := + app (app (constants ctor [u]) A) a + +theorem type_interp (constants : Assignment β V) (u : Nat) (env : Nat → V) : + interp constants [u] env (type : AExpr β) = piR 1 (univ u) (fun _ => univZero) := by + simp [type, interp, VLevel.eval, univ_zero] + +theorem introType_interp (constants : Assignment β V) (family : ConstRef β) (u : Nat) (env : Nat → V) : + interp constants [u] env (introType family) = + piR 0 (univ u) (fun A => piR 0 A (fun _ => value constants family u A)) := by + simp [introType, applied, value, interp, VLevel.eval, Valuation.cons] + +theorem recType_interp (constants : Assignment β V) (family ctor : ConstRef β) (u : Nat) (env : Nat → V) : + interp constants [u] env (recType family ctor) = + piR 0 (univ u) (fun A => + piR 0 (piR 1 (value constants family u A) (fun _ => univZero)) (fun motive => + piR 0 (piR 0 A (fun a => app motive (introValue constants ctor u A a))) + (fun _ => piR 0 (value constants family u A) (fun h => app motive h)))) := by + simp [recType, applied, introduction, value, introValue, AExpr.appN, interp, + VLevel.eval, Valuation.cons, univ_zero] + +variable {entries : Environment β} {family ctor recursor : ConstRef β} + {constants : Assignment β V} + +theorem value_mem_univZero (h : Interface entries family ctor recursor) (hM : Realizes constants entries) + {u : Nat} {A : V} (hA : A ∈ˢ univ u) : value constants family u A ∈ˢ univZero := by + have ht := h.former.member hM (levels := [u]) rfl (fun _ => empty) + rw [type_interp] at ht + exact app_mem_piR_pos (by decide : 1 ≠ 0) ht hA + +theorem introValue_mem (h : Interface entries family ctor recursor) (hM : Realizes constants entries) + {u : Nat} {A a : V} (hA : A ∈ˢ univ u) (ha : a ∈ˢ A) : + introValue constants ctor u A a ∈ˢ value constants family u A := by + have ht := h.introduction.member hM (levels := [u]) rfl (fun _ => empty) + rw [introType_interp] at ht + have h1 := app_mem_piR ht hA (fun _ _ _ => piR_zero_mem_univZero) + exact app_mem_piR h1 ha (fun _ _ _ => value_mem_univZero h hM hA) + +theorem exists_of_mem (h : Interface entries family ctor recursor) (hM : Realizes constants entries) + {u : Nat} {A proof : V} (hA : A ∈ˢ univ u) (hp : proof ∈ˢ value constants family u A) : + ∃ a, a ∈ˢ A := by + let C : V := truthVal (∃ a, a ∈ˢ A) + let motive := lamR 1 (value constants family u A) (fun _ => C) + have hm : motive ∈ˢ piR 1 (value constants family u A) (fun _ => univZero) := + lamR_mem fun _ _ => truthVal_mem_univZero _ + let minor := lamR 0 A (fun _ => pt) + have hminor : minor ∈ˢ piR 0 A (fun a => app motive (introValue constants ctor u A a)) := by + apply lamR_mem + intro a ha + rw [app_lamR_pos (by decide : 1 ≠ 0) (introValue_mem h hM hA ha)] + exact pt_mem_truthVal ⟨a, ha⟩ + have hr := h.elimination.member hM (levels := [u]) rfl (fun _ => empty) + rw [recType_interp] at hr + obtain ⟨rA, hrA⟩ := Equality.exists_of_mem_piR_zero hr hA + obtain ⟨rm, hrm⟩ := Equality.exists_of_mem_piR_zero hrA hm + obtain ⟨ri, hri⟩ := Equality.exists_of_mem_piR_zero hrm hminor + obtain ⟨result, hresult⟩ := Equality.exists_of_mem_piR_zero hri hp + rw [app_lamR_pos (by decide : 1 ≠ 0) hp] at hresult + exact of_mem_truthVal hresult + +theorem value_eq_truthVal (h : Interface entries family ctor recursor) (hM : Realizes constants entries) + {u : Nat} {A : V} (hA : A ∈ˢ univ u) : + value constants family u A = truthVal (∃ a, a ∈ˢ A) := by + apply univZero_ext (value_mem_univZero h hM hA) (truthVal_mem_univZero _) + · exact fun hp => pt_mem_truthVal (exists_of_mem h hM hA hp) + · intro hp + obtain ⟨a, ha⟩ := of_mem_truthVal hp + have hc := introValue_mem h hM hA ha + rwa [eq_pt_of_mem_univZero (value_mem_univZero h hM hA) hc] at hc + +end Ix.Theory.Certified.Basis.Nonempty diff --git a/Ix/Theory/Certified/Checker.lean b/Ix/Theory/Certified/Checker.lean new file mode 100644 index 000000000..c2a2aa7d2 --- /dev/null +++ b/Ix/Theory/Certified/Checker.lean @@ -0,0 +1,316 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Judgment +import Ix.Theory.Certified.LevelEq + +/-! +# Executable validation of dependent-function certificates + +Witnesses are untrusted trees of rule choices and intermediate syntax. The +validator checks their exact endpoints, scopes, annotations, and dependency +lookups. Every recursive success constructs its semantic proof through the +rule theorems; no semantic proof is an input. Exhausted fuel and unsupported +syntax fail. Proof fields in successful results are erased during execution. +-/ + +namespace Ix.Theory.Certified + +open Model + +universe u v + +/-- An erased result at the data universe of the checked syntax. This permits +the proof-producing validator to compose with declaration readers. -/ +structure CheckedClaim (claim : Prop) : Type u where + down : claim + +mutual +inductive TypingWitness (β : Type u) where + | sort + | bvar + | const + | app (condition : PropWhen) (domain codomain : AExpr β) + (fn arg : TypingWitness β) + | lam (domainLevel codomainLevel : VLevel) (codomain : AExpr β) + (domainType codomainType body : TypingWitness β) + | forallE (domainLevel codomainLevel : VLevel) + (domainType codomainType : TypingWitness β) + | conv (intermediate : AExpr β) (targetLevel : VLevel) + (term target : TypingWitness β) (conversion : ConversionWitness β) + | fact (ref : ConstRef β) (index : Nat) (levels : List VLevel) + | natLit (ref : ConstRef β) (index : Nat) + | betaResult (condition : PropWhen) (domain body argument codomain : AExpr β) + (lambda argumentWitness : TypingWitness β) + +inductive ConversionWitness (β : Type u) where + | refl + | symm (proof : ConversionWitness β) + | trans (middle : AExpr β) (left right : ConversionWitness β) + | app (fn arg : ConversionWitness β) + | lam (domainLevel : VLevel) (domainType : TypingWitness β) + (domain body : ConversionWitness β) + | forallE (domainLevel : VLevel) (domainType : TypingWitness β) + (domain body : ConversionWitness β) + | beta (lambdaType : AExpr β) (lambda arg : TypingWitness β) + | eta (codomain : AExpr β) (fn : TypingWitness β) + | proofIrrel (type : AExpr β) (typeProof left right : TypingWitness β) + | delta + | sort + | equation (ref : ConstRef β) (index : Nat) (levels : List VLevel) + | natLiteral (ref : ConstRef β) (index : Nat) + | proj (major : ConversionWitness β) +end + +variable {β : Type u} [DecidableEq β] + +mutual +def verifyType (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (e A : AExpr β) (witness : TypingWitness β) : + Option (CheckedClaim.{u} (TypingClaim.{u,v} entries Γ e A)) := + match fuel with + | 0 => none + | fuel + 1 => + if e.Scope n Γ.length ∧ A.Scope n Γ.length then + match e, witness with + | .sort l, .sort => + if h : A = .sort (.succ l) then + some ⟨by subst A; exact TypingClaim.sort l⟩ + else none + | .bvar i, .bvar => + match h : Γ[i]? with + | none => none + | some B => + if hA : A = B then some ⟨by subst A; exact TypingClaim.bvar h⟩ else none + | .const r ls, .const => + match h : entries r with + | none => none + | some entry => + if hn : ls.length = entry.universes then + if hA : A = entry.type.instL ls then + some ⟨by subst A; exact TypingClaim.const h hn⟩ + else none + else none + | .app f a, .app p D B wf wa => + if hA : A = B.inst a then do + let hf ← verifyType fuel n entries Γ f (.forallE p D B) wf + let ha ← verifyType fuel n entries Γ a D wa + return ⟨by subst A; exact TypingClaim.app hf.down ha.down⟩ + else none + | .lam p D body, .lam lD lB B wD wB wb => + if hp : p = zeroCondition lB then + if hA : A = .forallE p D B then do + let hD ← verifyType fuel n entries Γ D (.sort lD) wD + let hB ← verifyType fuel n entries (Γ.push D) B (.sort lB) wB + let hb ← verifyType fuel n entries (Γ.push D) body B wb + return ⟨by subst A; exact TypingClaim.lam hD.down hB.down hb.down hp⟩ + else none + else none + | .forallE p D B, .forallE lD lB wD wB => + if hp : p = zeroCondition lB then + if hA : A = .sort (.imax lD lB) then do + let hD ← verifyType fuel n entries Γ D (.sort lD) wD + let hB ← verifyType fuel n entries (Γ.push D) B (.sort lB) wB + return ⟨by subst A; exact TypingClaim.forallE hD.down hB.down hp⟩ + else none + else none + | e, .conv B lA we wA wc => do + let he ← verifyType fuel n entries Γ e B we + let hA ← verifyType fuel n entries Γ A (.sort lA) wA + let hc ← verifyConversion fuel n entries Γ B A wc + return ⟨TypingClaim.conv he.down hA.down hc.down⟩ + | e, .fact r index ls => + match hr : entries r with + | none => none + | some entry => + match hf : entry.facts[index]? with + | some (.typed value type) => + if hn : ls.length = entry.universes then + if ∀ l ∈ ls, l.WF n then + if he : e = value.instL ls then + if hA : A = type.instL ls then + some ⟨by subst e A; exact TypingClaim.fact hr (List.mem_of_getElem? hf) hn⟩ + else none + else none + else none + else none + | _ => none + | .natLit value, .natLit r index => + match hr : entries r with + | none => none + | some entry => + match hf : entry.facts[index]? with + | some (.natural _ _) => + if hn : entry.universes = 0 then + if hA : A = .const r [] then + some ⟨by subst A; exact TypingClaim.natLit hr (List.mem_of_getElem? hf) hn value⟩ + else none + else none + | _ => none + | e, .betaResult p D body arg B wl wa => + if he : e = body.inst arg then + if hA : A = B.inst arg then do + let hl ← verifyType fuel n entries Γ (.lam p D body) (.forallE p D B) wl + let ha ← verifyType fuel n entries Γ arg D wa + return ⟨by subst e A; exact TypingClaim.betaResult hl.down ha.down⟩ + else none + else none + | _, _ => none + else none + +def verifyConversion (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (a b : AExpr β) (witness : ConversionWitness β) : + Option (CheckedClaim.{u} (ConversionClaim.{u,v} entries Γ a b)) := + match fuel with + | 0 => none + | fuel + 1 => + if a.Scope n Γ.length ∧ b.Scope n Γ.length then + match a, b, witness with + | a, b, .refl => + if h : a = b then some ⟨by subst b; exact ConversionClaim.refl a⟩ else none + | a, b, .symm w => do + let h ← verifyConversion fuel n entries Γ b a w + return ⟨ConversionClaim.symm h.down⟩ + | a, b, .trans c w₁ w₂ => do + let h₁ ← verifyConversion fuel n entries Γ a c w₁ + let h₂ ← verifyConversion fuel n entries Γ c b w₂ + return ⟨ConversionClaim.trans h₁.down h₂.down⟩ + | .app f a, .app f' a', .app wf wa => do + let hf ← verifyConversion fuel n entries Γ f f' wf + let ha ← verifyConversion fuel n entries Γ a a' wa + return ⟨ConversionClaim.app hf.down ha.down⟩ + | .proj r i a, .proj r' i' b, .proj w => + if hr : r = r' then + if hi : i = i' then do + let h ← verifyConversion fuel n entries Γ a b w + return ⟨by subst r' i'; exact ConversionClaim.proj h.down⟩ + else none + else none + | .lam p D body, .lam p' D' body', .lam lD wD wd wb => + if hp : p = p' then do + let hD ← verifyType fuel n entries Γ D (.sort lD) wD + let hd ← verifyConversion fuel n entries Γ D D' wd + let hb ← verifyConversion fuel n entries (Γ.push D) body body' wb + return ⟨by subst p'; exact ConversionClaim.lam hD.down hd.down hb.down⟩ + else none + | .forallE p D B, .forallE p' D' B', .forallE lD wD wd wb => + if hp : p = p' then do + let hD ← verifyType fuel n entries Γ D (.sort lD) wD + let hd ← verifyConversion fuel n entries Γ D D' wd + let hb ← verifyConversion fuel n entries (Γ.push D) B B' wb + return ⟨by subst p'; exact ConversionClaim.forallE hD.down hd.down hb.down⟩ + else none + | .app (.lam p D body) arg, result, .beta T wl wa => + if hr : result = body.inst arg then do + let hl ← verifyType fuel n entries Γ (.lam p D body) T wl + let ha ← verifyType fuel n entries Γ arg D wa + return ⟨by subst result; exact ConversionClaim.beta hl.down ha.down⟩ + else none + | .lam p D body, f, .eta B wf => + if hb : body = .app (f.liftN 1) (.bvar 0) then do + let hf ← verifyType fuel n entries Γ f (.forallE p D B) wf + return ⟨by subst body; exact ConversionClaim.eta hf.down⟩ + else none + | a, b, .proofIrrel A wA wa wb => do + let hA ← verifyType fuel n entries Γ A (.sort .zero) wA + let ha ← verifyType fuel n entries Γ a A wa + let hb ← verifyType fuel n entries Γ b A wb + return ⟨ConversionClaim.proofIrrel hA.down ha.down hb.down⟩ + | .const r ls, b, .delta => + match h : entries r with + | none => none + | some entry => + match hb : entry.body with + | none => none + | some body => + if hn : ls.length = entry.universes then + if hB : b = body.instL ls then + some ⟨by subst b; exact ConversionClaim.delta h hb hn⟩ + else none + else none + | .sort l, .sort l', .sort => + if h : LevelEq.check n l l' = true then + some ⟨ConversionClaim.sort (LevelEq.check_sound h).2.2⟩ + else none + | a, b, .equation r index ls => + match h : entries r with + | none => none + | some entry => + match he : entry.equations[index]? with + | none => none + | some law => + if hn : ls.length = entry.universes then + if hls : ∀ l ∈ ls, l.WF n then + if ha : a = law.lhs.instL ls then + if hb : b = law.rhs.instL ls then + some ⟨by + subst a b + exact ConversionClaim.equation h (List.mem_of_getElem? he) hn⟩ + else none + else none + else none + else none + | .natLit value, result, .natLiteral r index => + match hr : entries r with + | none => none + | some entry => + match hf : entry.facts[index]? with + | some (.natural zero succ) => + if hn : entry.universes = 0 then + match value with + | 0 => + if he : result = .const zero [] then + some ⟨by subst result; exact ConversionClaim.natZero hr (List.mem_of_getElem? hf) hn⟩ + else none + | value + 1 => + if he : result = .app (.const succ []) (.natLit value) then + some ⟨by subst result; exact ConversionClaim.natSucc hr (List.mem_of_getElem? hf) hn value⟩ + else none + else none + | _ => none + | _, _, _ => none + else none +end + +/-- Soundness is extracted only from an actual successful execution. The +dependency model and context are the documented local preconditions; the +closed acceptance producer must construct them. -/ +theorem verifyType_sound {fuel n : Nat} {entries : Environment β} {Γ : Context β} + {e A : AExpr β} {witness : TypingWitness β} {result} + (_ : verifyType.{u,v} fuel n entries Γ e A witness = some result) : + TypingClaim.{u,v} entries Γ e A := result.down + +theorem verifyConversion_sound {fuel n : Nat} {entries : Environment β} {Γ : Context β} + {a b : AExpr β} {witness : ConversionWitness β} {result} + (_ : verifyConversion.{u,v} fuel n entries Γ a b witness = some result) : + ConversionClaim.{u,v} entries Γ a b := result.down + +def checkTypeCertified (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (e A : AExpr β) (witness : TypingWitness β) : Bool := + (verifyType.{u,v} fuel n entries Γ e A witness).isSome + +def defeqCertified (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (a b : AExpr β) (witness : ConversionWitness β) : Bool := + (verifyConversion.{u,v} fuel n entries Γ a b witness).isSome + +theorem checkTypeCertified_sound {fuel n : Nat} {entries : Environment β} + {Γ : Context β} {e A : AExpr β} {witness : TypingWitness β} + (h : checkTypeCertified.{u,v} fuel n entries Γ e A witness = true) : + TypingClaim.{u,v} entries Γ e A := by + unfold checkTypeCertified at h + cases hr : verifyType.{u,v} fuel n entries Γ e A witness with + | none => simp [hr] at h + | some result => exact result.down + +theorem defeqCertified_sound {fuel n : Nat} {entries : Environment β} + {Γ : Context β} {a b : AExpr β} {witness : ConversionWitness β} + (h : defeqCertified.{u,v} fuel n entries Γ a b witness = true) : + ConversionClaim.{u,v} entries Γ a b := by + unfold defeqCertified at h + cases hr : verifyConversion.{u,v} fuel n entries Γ a b witness with + | none => simp [hr] at h + | some result => exact result.down + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/ClaimComposition.lean b/Ix/Theory/Certified/ClaimComposition.lean new file mode 100644 index 000000000..9c7c05420 --- /dev/null +++ b/Ix/Theory/Certified/ClaimComposition.lean @@ -0,0 +1,245 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Claims + +namespace Ix.Theory.Certified + +open Model Model.SetTheory + +universe u v +variable {β : Type u} [DecidableEq β] + +theorem mem_outstanding {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + {nodes : List (CheckedNode.{u,v} fuel signature store)} {ref : ConstRef β} : + ref ∈ outstanding nodes ↔ ref ∈ nodeFrontiers nodes ∧ ref ∉ nodeSubjects nodes := by + simp [outstanding] + +theorem CheckedBatch.exact_subjects {fuel : Nat} {signature : PrimitiveSignature β} + {store : Store β} {inputs : List (ClaimNode β)} + (batch : CheckedBatch.{u,v} fuel signature store inputs) : + nodeSubjects batch.nodes = inputs.flatMap (·.subjects) := by + have h := congrArg (fun nodes => nodes.flatMap (·.subjects)) batch.exactNodes + simpa only [nodeSubjects, List.flatMap_map, Function.comp_def] using h + +theorem CheckedBatch.exact_frontiers {fuel : Nat} {signature : PrimitiveSignature β} + {store : Store β} {inputs : List (ClaimNode β)} + (batch : CheckedBatch.{u,v} fuel signature store inputs) : + nodeFrontiers batch.nodes = inputs.flatMap (fun node => node.frontier.map (·.ref)) := by + calc + nodeFrontiers batch.nodes = batch.nodes.flatMap (fun node => node.input.frontier.map (·.ref)) := by + exact congrArg (fun f => batch.nodes.flatMap f) (funext fun node => node.frontier_refs) + _ = inputs.flatMap (fun node => node.frontier.map (·.ref)) := by + simpa only [List.flatMap_map, Function.comp_def] using + congrArg (fun nodes => nodes.flatMap (fun node => node.frontier.map (·.ref))) batch.exactNodes + +theorem CheckedBatch.frontier_coverage {fuel : Nat} {signature : PrimitiveSignature β} + {store : Store β} {inputs : List (ClaimNode β)} + (batch : CheckedBatch.{u,v} fuel signature store inputs) {ref : ConstRef β} : + ref ∈ batch.receipt.frontier.refs ↔ + (∃ node ∈ inputs, ref ∈ node.frontier.map (·.ref)) ∧ + ¬ ∃ node ∈ inputs, ref ∈ node.subjects := by + rw [batch.exactFrontier, mem_outstanding, batch.exact_subjects, batch.exact_frontiers] + simp + +/-- Conservative logical-use manifest, including every axiom admitted by a +leaf and by the final replay. Neither kind of use is structurally discharged. -/ +def CheckedBatch.logicalUses {fuel : Nat} {signature : PrimitiveSignature β} + {store : Store β} {inputs : List (ClaimNode β)} + (batch : CheckedBatch.{u,v} fuel signature store inputs) : List (ConstRef β) := + (logicalAxioms store batch.receipt.checked.result.entries ++ + batch.nodes.flatMap (fun node => logicalAxioms store node.receipt.checked.result.entries)).eraseDups + +theorem CheckedBatch.leaf_axiom_retained {fuel : Nat} {signature : PrimitiveSignature β} + {store : Store β} {inputs : List (ClaimNode β)} + (batch : CheckedBatch.{u,v} fuel signature store inputs) + {node : CheckedNode.{u,v} fuel signature store} (hn : node ∈ batch.nodes) {ref : ConstRef β} + (hr : ref ∈ logicalAxioms store node.receipt.checked.result.entries) : + ref ∈ batch.logicalUses := by + simp only [logicalUses, List.mem_eraseDups, List.mem_append, List.mem_flatMap] + exact Or.inr ⟨node, hn, hr⟩ + +theorem CheckedBatch.logicalUses_authorized {fuel : Nat} {signature : PrimitiveSignature β} + {store : Store β} {inputs : List (ClaimNode β)} + (batch : CheckedBatch.{u,v} fuel signature store inputs) {ref : ConstRef β} + (h : ref ∈ batch.logicalUses) : + ∃ entry, Standard.EntrySource store ref entry ∨ Quotient.EntrySource store ref entry := by + simp only [logicalUses, List.mem_eraseDups, List.mem_append, List.mem_flatMap] at h + rcases h with h | ⟨node, _, h⟩ + · obtain ⟨entry, _, hs⟩ := batch.receipt.logical_axioms_authorized h + exact ⟨entry, hs⟩ + · obtain ⟨entry, _, hs⟩ := node.receipt.logical_axioms_authorized h + exact ⟨entry, hs⟩ + +/-- Every discharged dependency has a provider at a strictly earlier position; +only an explicit external frontier may lack such a provider. -/ +theorem DependencyOrder.before {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + {known : List (ConstRef β)} {nodes : List (CheckedNode.{u,v} fuel signature store)} + (order : DependencyOrder known nodes) {index : Nat} {node : CheckedNode.{u,v} fuel signature store} + (hn : nodes[index]? = some node) {ref : ConstRef β} (hr : ref ∈ node.receipt.frontier.refs) : + ref ∈ known ∨ ∃ previous, previous < index ∧ + ∃ provider, nodes[previous]? = some provider ∧ ref ∈ provider.input.subjects := by + induction order generalizing index with + | nil => simp at hn + | @cons known first rest available tail ih => + cases index with + | zero => + cases Option.some.inj hn + exact Or.inl (available ref hr) + | succ index => + rcases ih hn with hk | ⟨previous, hp, provider, hs, ho⟩ + · rcases List.mem_append.mp hk with hk | hk + · exact Or.inl hk + · exact Or.inr ⟨0, Nat.zero_lt_succ _, first, rfl, hk⟩ + · exact Or.inr ⟨previous + 1, Nat.succ_lt_succ hp, provider, hs, ho⟩ + +theorem unique_subject_provider {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + {nodes : List (CheckedNode.{u,v} fuel signature store)} (unique : (nodeSubjects nodes).Nodup) + {i j : Nat} {a b : CheckedNode.{u,v} fuel signature store} + (ha : nodes[i]? = some a) (hb : nodes[j]? = some b) {ref : ConstRef β} + (hra : ref ∈ a.input.subjects) (hrb : ref ∈ b.input.subjects) : i = j := by + have hp := (List.pairwise_flatMap.mp unique).2 + obtain ⟨hi, hai⟩ := List.getElem?_eq_some_iff.mp ha + obtain ⟨hj, hbj⟩ := List.getElem?_eq_some_iff.mp hb + rcases Nat.lt_trichotomy i j with hij | he | hji + · have h := List.pairwise_iff_getElem.mp hp i j hi hj hij + rw [hai, hbj] at h + exact False.elim (h ref hra ref hrb rfl) + · exact he + · have h := List.pairwise_iff_getElem.mp hp j i hj hi hji + rw [hai, hbj] at h + exact False.elim (h ref hrb ref hra rfl) + +/-- The relation points from a supplying node to a consuming node. Its rank +is the actual checked schedule position, not a prover-supplied acyclicity flag. -/ +def DependencyEdge {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + (nodes : List (CheckedNode.{u,v} fuel signature store)) (provider consumer : Nat) : Prop := + ∃ a b ref, nodes[provider]? = some a ∧ nodes[consumer]? = some b ∧ + ref ∈ a.input.subjects ∧ ref ∈ b.receipt.frontier.refs ∧ + ∀ previous, previous < provider → ∀ other, nodes[previous]? = some other → ref ∉ other.input.subjects + +theorem first_subject_provider {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + {nodes : List (CheckedNode.{u,v} fuel signature store)} {ref : ConstRef β} + (h : ref ∈ nodeSubjects nodes) : + ∃ index : Nat, ∃ node : CheckedNode.{u,v} fuel signature store, + nodes[index]? = some node ∧ ref ∈ node.input.subjects ∧ + ∀ previous : Nat, previous < index → ∀ other : CheckedNode.{u,v} fuel signature store, + nodes[previous]? = some other → ref ∉ other.input.subjects := by + induction nodes with + | nil => cases h + | cons first rest ih => + by_cases hf : ref ∈ first.input.subjects + · exact ⟨0, first, rfl, hf, fun previous hp => False.elim (Nat.not_lt_zero previous hp)⟩ + · have hr : ref ∈ nodeSubjects rest := (List.mem_append.mp h).resolve_left hf + obtain ⟨index, node, hn, hs, least⟩ := ih hr + refine ⟨index + 1, node, hn, hs, ?_⟩ + intro previous hp other ho + cases previous with + | zero => cases Option.some.inj ho; exact hf + | succ previous => exact least previous (Nat.lt_of_succ_lt_succ hp) other ho + +/-- Shared subjects choose their first checked provider. This supplies an +actual edge for every discharged frontier member, as opposed to merely +asserting that a separately supplied graph is acyclic. -/ +theorem DependencyOrder.edge_for {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + {known : List (ConstRef β)} {nodes : List (CheckedNode.{u,v} fuel signature store)} + (order : DependencyOrder known nodes) {consumer : Nat} + {node : CheckedNode.{u,v} fuel signature store} (hn : nodes[consumer]? = some node) + {ref : ConstRef β} (hr : ref ∈ node.receipt.frontier.refs) : + ref ∈ known ∨ ∃ provider, DependencyEdge nodes provider consumer := by + rcases order.before hn hr with hk | ⟨previous, _, owner, ho, hs⟩ + · exact Or.inl hk + · obtain ⟨index, first, hf, hr', least⟩ := first_subject_provider + (List.mem_flatMap.mpr ⟨owner, List.mem_of_getElem? ho, hs⟩) + exact Or.inr ⟨index, first, node, ref, hf, hn, hr', hr, least⟩ + +theorem DependencyOrder.rank {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + {known : List (ConstRef β)} {nodes : List (CheckedNode.{u,v} fuel signature store)} + (order : DependencyOrder known nodes) + (external : ∀ ref ∈ known, ref ∉ nodeSubjects nodes) {provider consumer : Nat} + (edge : DependencyEdge nodes provider consumer) : provider < consumer := by + obtain ⟨a, b, ref, ha, hb, hra, hrb, first⟩ := edge + rcases order.before hb hrb with hk | ⟨previous, hp, owner, ho, hr⟩ + · exact False.elim (external ref hk + (List.mem_flatMap.mpr ⟨a, List.mem_of_getElem? ha, hra⟩)) + · by_cases h : provider < consumer + · exact h + · have earlier : previous < provider := Nat.lt_of_lt_of_le hp (Nat.le_of_not_gt h) + exact False.elim (first previous earlier owner ho hr) + +theorem CheckedBatch.external_disjoint {fuel : Nat} {signature : PrimitiveSignature β} + {store : Store β} {inputs : List (ClaimNode β)} + (batch : CheckedBatch.{u,v} fuel signature store inputs) {ref : ConstRef β} + (h : ref ∈ batch.receipt.frontier.refs ++ [signature.falseType, signature.falseElim]) : + ref ∉ nodeSubjects batch.nodes := by + intro hs + rcases List.mem_append.mp h with hf | hp + · exact (mem_outstanding.mp ((batch.exactFrontier ref).mp hf)).2 hs + · have fresh := batch.receipt.fresh ref hs + rcases List.mem_cons.mp hp with rfl | hp + · rw [batch.receipt.frontier.interface.present.1] at fresh; cases fresh + · rcases List.mem_cons.mp hp with rfl | hp + · rw [batch.receipt.frontier.interface.present.2] at fresh; cases fresh + · cases hp + +theorem CheckedBatch.acyclic {fuel : Nat} {signature : PrimitiveSignature β} + {store : Store β} {inputs : List (ClaimNode β)} + (batch : CheckedBatch.{u,v} fuel signature store inputs) : + WellFounded (DependencyEdge batch.nodes) := + Subrelation.wf + (fun h => batch.order.rank (fun _ h => batch.external_disjoint h) h) + Nat.lt_wfRel.wf + +theorem CheckedBatch.no_circular_discharge {fuel : Nat} {signature : PrimitiveSignature β} + {store : Store β} {inputs : List (ClaimNode β)} + (batch : CheckedBatch.{u,v} fuel signature store inputs) {a b : Nat} + (ab : DependencyEdge batch.nodes a b) (ba : DependencyEdge batch.nodes b a) : False := by + have hab := batch.order.rank (fun _ h => batch.external_disjoint h) ab + have hba := batch.order.rank (fun _ h => batch.external_disjoint h) ba + exact Nat.lt_asymm hab hba + +/-- Composition retains the leaf sequence. Validation of any grouping runs +the same source-bound checker over that complete sequence. -/ +def composeClaims (left right : List (ClaimNode β)) : List (ClaimNode β) := left ++ right + +omit [DecidableEq β] in +theorem composeClaims_assoc (a b c : List (ClaimNode β)) : + composeClaims (composeClaims a b) c = composeClaims a (composeClaims b c) := + List.append_assoc .. + +theorem checkBatch?_assoc (fuel : Nat) (signature : PrimitiveSignature β) (store : Store β) + (frontier : List (FrontierWitness β)) (a b c : List (ClaimNode β)) : + acceptsBatch.{u,v} fuel signature store frontier (composeClaims (composeClaims a b) c) = + acceptsBatch.{u,v} fuel signature store frontier (composeClaims a (composeClaims b c)) := by + rw [composeClaims_assoc] + +/-- Erasing structural obligations has no effect on the model's axiom-use +manifest. Every recorded axiom retains its exact realized source schema. -/ +theorem CheckedBatch.logical_axioms_authorized {fuel : Nat} {signature : PrimitiveSignature β} + {store : Store β} {inputs : List (ClaimNode β)} + (batch : CheckedBatch.{u,v} fuel signature store inputs) {ref : ConstRef β} + (h : ref ∈ logicalAxioms store batch.receipt.checked.result.entries) : + ∃ entry, batch.receipt.checked.result.entries ref = some entry ∧ + (Standard.EntrySource store ref entry ∨ Quotient.EntrySource store ref entry) := + batch.receipt.logical_axioms_authorized h + +theorem CheckedBatch.no_False {fuel : Nat} {signature : PrimitiveSignature β} + {store : Store β} {inputs : List (ClaimNode β)} + (batch : CheckedBatch.{u,v} fuel signature store inputs) + (closed : batch.receipt.frontier.refs = []) {ref : ConstRef β} + (subject : ref ∈ nodeSubjects batch.nodes) + (type : store.type ref = some signature.falseExpr) + (V : Type v) [SetTheory V] : False := by + obtain ⟨constants, hM, subjects⟩ := batch.closed_has_model closed V + obtain ⟨entry, _, hs, hm⟩ := subjects ref subject + have he : entry.type.erase = .const signature.falseType [] := + Option.some.inj (hs.type.symm.trans type) + have hc := AExpr.eq_const_of_erase_eq he + have member := hm (List.replicate entry.universes 0) (by simp) (fun _ => empty) + rw [hc] at member + simp only [interp, List.map_nil, hM.falseValue] at member + exact not_mem_empty _ member + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Claims.lean b/Ix/Theory/Certified/Claims.lean new file mode 100644 index 000000000..c80ce755c --- /dev/null +++ b/Ix/Theory/Certified/Claims.lean @@ -0,0 +1,242 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.LogicalPolicy + +/-! A certified claim bundle is an ordered dependency DAG. Aggregation checks +each conditional leaf, matches its complete deferred headers, and replays its +admission in one growing interface. Set subtraction alone grants no meaning. +The checked result extends every compatible interpretation of its remaining +frontier and constructs a model when that frontier is empty. -/ + +namespace Ix.Theory.Certified + +open Model Model.SetTheory + +universe u v +variable {β : Type u} [DecidableEq β] + +structure ClaimNode (β : Type u) where + subjects : List (ConstRef β) + frontier : List (FrontierWitness β) + declarations : List (DeclarationWitness β) + deriving Inhabited + +structure CheckedNode (fuel : Nat) (signature : PrimitiveSignature β) (store : Store β) where + input : ClaimNode β + receipt : ConditionalStore.{u,v} signature store input.subjects + validation : checkConditionalStore? fuel signature store input.frontier input.subjects + input.declarations = some receipt + +def checkNode? (fuel : Nat) (signature : PrimitiveSignature β) (store : Store β) + (node : ClaimNode β) : Option (CheckedNode.{u,v} fuel signature store) := + match h : checkConditionalStore?.{u,v} fuel signature store node.frontier node.subjects node.declarations with + | none => none + | some receipt => some ⟨node, receipt, h⟩ + +theorem checkNode?_input {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + {node : ClaimNode β} {result : CheckedNode.{u,v} fuel signature store} + (h : checkNode? fuel signature store node = some result) : result.input = node := by + unfold checkNode? at h + split at h + · cases h + · cases Option.some.inj h; rfl + +theorem CheckedNode.frontier_refs {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + (node : CheckedNode.{u,v} fuel signature store) : + node.receipt.frontier.refs = node.input.frontier.map (·.ref) := + checkFrontier?_refs (checkConditionalStore?_frontier node.validation) + +def nodeSubjects {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + (nodes : List (CheckedNode.{u,v} fuel signature store)) : List (ConstRef β) := + nodes.flatMap (·.input.subjects) + +def nodeFrontiers {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + (nodes : List (CheckedNode.{u,v} fuel signature store)) : List (ConstRef β) := + nodes.flatMap (·.receipt.frontier.refs) + +def outstanding {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + (nodes : List (CheckedNode.{u,v} fuel signature store)) : List (ConstRef β) := + (nodeFrontiers nodes).filter (fun ref => !(nodeSubjects nodes).contains ref) |>.eraseDups + +/-- Dependencies refer to the external frontier or to subjects of earlier +nodes. Internal recursion stays inside one atomic declaration producer. -/ +inductive DependencyOrder {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} : + List (ConstRef β) → List (CheckedNode.{u,v} fuel signature store) → Prop where + | nil (known) : DependencyOrder known [] + | cons {known node rest} + (available : ∀ ref ∈ node.receipt.frontier.refs, ref ∈ known) + (tail : DependencyOrder (known ++ node.input.subjects) rest) : + DependencyOrder known (node :: rest) + +omit [DecidableEq β] in +theorem HeaderPresent.extend {signature : PrimitiveSignature β} {a b : Environment β} + (extension : Extends.{u,v} signature a b) {header : Signature.Header β} + (h : HeaderPresent a header) : HeaderPresent b header := by + obtain ⟨entry, he, hu, ht⟩ := h.entry + simp only [HeaderPresent, extension.lookup _ _ he] + exact ⟨hu, ht⟩ + +structure ReplayResult {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + (state : CheckedInterface signature) (known : List (ConstRef β)) + (nodes : List (CheckedNode.{u,v} fuel signature store)) where + checked : CheckedExtension.{u,v} signature store state + order : DependencyOrder known nodes + subjects : ∀ ref ∈ nodeSubjects nodes, (checked.result.entries ref).isSome = true + frontiers : ∀ node ∈ nodes, ∀ header ∈ node.receipt.frontier.headers, + HeaderPresent checked.result.entries header + +def Ordinary.BlockWitness.outputs (witness : Ordinary.BlockWitness β) : List (ConstRef β) := + [.member witness.source 0, .member witness.recursor 0] ++ + (List.range witness.shape.shape.constructors.length).map (.ctor witness.source 0 ·) + +def DeclarationWitness.outputs : DeclarationWitness β → List (ConstRef β) + | .definition witness => [witness.ref] + | .ordinary witness | .natural witness => witness.outputs + | .standard witness => [witness.ref] + | .quotient witness => Quotient.kinds.map witness.refs.ref + | .structure witness => witness.facts.block.outputs + | .modeled witness => witness.companions.map (·.header.ref) + +/-- Reusing an already available declaration grants no additional equation, +body or fact: the interface is unchanged. Every other declaration undergoes +the full admission check. This permits shared, independently checked closures +without replacing any chosen interpretation. -/ +def replayDeclarations? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : CheckedInterface signature) : List (DeclarationWitness β) → + Option (CheckedExtension.{u,v} signature store state) + | [] => some (CheckedExtension.refl state) + | witness :: rest => do + let step ← if witness.outputs.all (fun ref => (state.entries ref).isSome) then + some (CheckedExtension.refl state) + else checkDeclarationExtension?.{u,v} fuel (store := store) state witness + let rest ← replayDeclarations? fuel step.result rest + return step.trans rest + +/-- All semantic checks run again in the shared prefix. This constructs +compatibility of separately checked leaves instead of selecting unrelated +models and presuming they agree. Only published subjects discharge frontiers. -/ +def replayNodes? {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + (state : CheckedInterface signature) (known : List (ConstRef β)) : + (nodes : List (CheckedNode.{u,v} fuel signature store)) → + Option (ReplayResult state known nodes) + | [] => some ⟨CheckedExtension.refl state, .nil known, by simp [nodeSubjects], by simp⟩ + | node :: nodes => + if available : node.receipt.frontier.refs.all (known.contains ·) = true then + if aligned : node.receipt.frontier.headers.all (fun h => decide (HeaderPresent state.entries h)) = true then + do + let step ← replayDeclarations?.{u,v} fuel (store := store) state node.input.declarations + if present : node.input.subjects.all (fun ref => (step.result.entries ref).isSome) = true then do + let tail ← replayNodes? step.result (known ++ node.input.subjects) nodes + return { + checked := step.trans tail.checked + order := .cons (by simpa using List.all_eq_true.mp available) tail.order + subjects := by + intro ref hr + rcases List.mem_append.mp hr with hhead | htail + · have hp := List.all_eq_true.mp present ref hhead + obtain ⟨entry, he⟩ := Option.isSome_iff_exists.mp hp + exact Option.isSome_iff_exists.mpr ⟨entry, tail.checked.extension.lookup _ _ he⟩ + · exact tail.subjects ref htail + frontiers := by + intro other hm header hh + rcases List.mem_cons.mp hm with rfl | hm + · have h : HeaderPresent state.entries header := of_decide_eq_true + (List.all_eq_true.mp aligned header hh) + exact (h.extend step.extension).extend tail.checked.extension + · exact tail.frontiers other hm header hh + } + else none + else none + else none + +structure CheckedBatch (fuel : Nat) (signature : PrimitiveSignature β) (store : Store β) + (inputs : List (ClaimNode β)) where + nodes : List (CheckedNode.{u,v} fuel signature store) + exactNodes : nodes.map (·.input) = inputs + receipt : ConditionalStore.{u,v} signature store (nodeSubjects nodes) + exactFrontier : ∀ ref, ref ∈ receipt.frontier.refs ↔ ref ∈ outstanding nodes + order : DependencyOrder + (receipt.frontier.refs ++ [signature.falseType, signature.falseElim]) nodes + frontiers : ∀ node ∈ nodes, ∀ header ∈ node.receipt.frontier.headers, + HeaderPresent receipt.checked.result.entries header + +def checkBatch? (fuel : Nat) (signature : PrimitiveSignature β) (store : Store β) + (frontier : List (FrontierWitness β)) (inputs : List (ClaimNode β)) : + Option (CheckedBatch.{u,v} fuel signature store inputs) := + if inputs.length > fuel then none else + match hn : inputs.mapM (checkNode?.{u,v} fuel signature store) with + | none => none + | some nodes => do + let dependencies ← checkFrontier?.{u,v} fuel signature store frontier + if exactFrontier : dependencies.refs.all (outstanding nodes |>.contains ·) = true ∧ + (outstanding nodes).all (dependencies.refs.contains ·) = true then + if fresh : (nodeSubjects nodes).all (fun ref => (dependencies.interface.entries ref).isNone) = true then do + let replay ← replayNodes? dependencies.interface + (dependencies.refs ++ [signature.falseType, signature.falseElim]) nodes + return { + nodes + exactNodes := by + have h := mapM_projection (checkNode?.{u,v} fuel signature store) (·.input) id + (fun _ _ h => checkNode?_input h) hn + simpa using h + receipt := ⟨dependencies, replay.checked, fun r hr => Option.isNone_iff_eq_none.mp + (List.all_eq_true.mp fresh r hr), replay.subjects⟩ + exactFrontier := by + intro ref + constructor + · intro hr; simpa using List.all_eq_true.mp exactFrontier.1 ref hr + · intro hr; simpa using List.all_eq_true.mp exactFrontier.2 ref hr + order := replay.order + frontiers := replay.frontiers + } + else none + else none + +def acceptsBatch (fuel : Nat) (signature : PrimitiveSignature β) (store : Store β) + (frontier : List (FrontierWitness β)) (inputs : List (ClaimNode β)) : Bool := + (checkBatch?.{u,v} fuel signature store frontier inputs).isSome + +/-- Every leaf's deferred interface is interpreted by the same final model. -/ +theorem CheckedBatch.compatible_leaves {fuel : Nat} {signature : PrimitiveSignature β} + {store : Store β} {inputs : List (ClaimNode β)} + (batch : CheckedBatch.{u,v} fuel signature store inputs) + (V : Type v) [SetTheory V] (constants : Assignment β V) + (hM : signature.Compatible batch.receipt.checked.result.entries constants) + {node : CheckedNode.{u,v} fuel signature store} (hn : node ∈ batch.nodes) : + signature.Compatible node.receipt.frontier.interface.entries constants := + node.receipt.frontier.compatible batch.receipt.checked.result + (batch.frontiers node hn) V constants hM + +theorem CheckedFrontier.empty_interface {signature : PrimitiveSignature β} {store : Store β} + (frontier : CheckedFrontier.{u,v} signature store) (h : frontier.refs = []) : + frontier.interface.entries = signature.environment := by + have hh : frontier.headers = [] := List.map_eq_nil_iff.mp h + simp [CheckedFrontier.interface, hh, Signature.environment] + +/-- A closed aggregate constructs its model from the pinned prelude. No +realization of a private assumption set is a hypothesis of this theorem. -/ +theorem CheckedBatch.closed_has_model {fuel : Nat} {signature : PrimitiveSignature β} + {store : Store β} {inputs : List (ClaimNode β)} + (batch : CheckedBatch.{u,v} fuel signature store inputs) + (closed : batch.receipt.frontier.refs = []) (V : Type v) [SetTheory V] : + ∃ constants : Assignment β V, + signature.Compatible batch.receipt.checked.result.entries constants ∧ + ∀ ref ∈ nodeSubjects batch.nodes, ∃ entry, + batch.receipt.checked.result.entries ref = some entry ∧ + SourceHeader store ref entry ∧ + ∀ levels, levels.length = entry.universes → ∀ env, + constants ref levels ∈ˢ interp constants levels env entry.type := by + have initial : signature.Compatible batch.receipt.frontier.interface.entries + (signature.assignment (V := V)) := by + rw [batch.receipt.frontier.empty_interface closed] + exact signature.compatible_assignment + obtain ⟨constants, hM, _, subjects⟩ := batch.receipt.subject_sound V signature.assignment initial + refine ⟨constants, hM, ?_⟩ + intro ref hr + obtain ⟨entry, he, _, hs, hm⟩ := subjects ref hr + exact ⟨entry, he, hs, fun levels hl env => (hm levels hl env).2⟩ + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Frontier.lean b/Ix/Theory/Certified/Frontier.lean new file mode 100644 index 000000000..1627a6c97 --- /dev/null +++ b/Ix/Theory/Certified/Frontier.lean @@ -0,0 +1,279 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Source + +/-! Conditional interfaces contain formed, source-bound headers. They carry +no assumed bodies, equations or primitive facts, and no claim that a model +exists. Object axioms and quotient primitives must use their semantic +producers; they cannot be deferred and later erased by frontier subtraction. -/ + +namespace Ix.Theory.Certified + +open Model Model.SetTheory + +universe u v +variable {β : Type u} [DecidableEq β] + +def deferredSource (store : Store β) (ref : ConstRef β) : Bool := + match store.lookup ref with + | some (.defn _ _ _ _ .safe) | some (.induct _ _ _ _ _ .safe) | + some (.recursor _ _ _ _ _ _ _ _ .safe) => true + | some _ => false + | none => match store.lookupCtor ref with + | some ctor => ctor.safety == .safe + | none => false + +structure FrontierWitness (β : Type u) where + ref : ConstRef β + annotations : AnnotationTree + level : VLevel + typing : TypingWitness β + +structure FrontierHeader (store : Store β) where + header : Signature.Header β + source : SourceHeader store header.ref header.entry + allowed : deferredSource store header.ref = true + +def readFrontierHeader? (store : Store β) (witness : FrontierWitness β) : + Option (FrontierHeader store) := + if ha : deferredSource store witness.ref = true then + match ht : store.type witness.ref with + | none => none + | some type => match hn : store.uvars witness.ref with + | none => none + | some universes => do + let reading ← readAnnotations? universes 0 type witness.annotations + return { + header := ⟨witness.ref, universes, reading.val⟩ + source := ⟨ht.trans (congrArg some reading.property.1.symm), hn⟩ + allowed := ha + } + else none + +omit [DecidableEq β] in +theorem readFrontierHeader?_ref {store : Store β} {witness : FrontierWitness β} + {reading : FrontierHeader store} (h : readFrontierHeader? store witness = some reading) : + reading.header.ref = witness.ref := by + unfold readFrontierHeader? at h + split at h + · split at h + · cases h + · split at h + · cases h + · simp only [bind, Option.bind_eq_some_iff] at h + obtain ⟨value, _, h⟩ := h + cases Option.some.inj h + rfl + · cases h + +omit [DecidableEq β] in +theorem mapM_projection {α : Type _} {γ : Type _} {δ : Type _} + (f : α → Option γ) (project : γ → δ) (source : α → δ) + (step : ∀ a b, f a = some b → project b = source a) + {inputs : List α} {outputs : List γ} (h : inputs.mapM f = some outputs) : + outputs.map project = inputs.map source := by + induction inputs generalizing outputs with + | nil => simp only [List.mapM_nil, pure, Option.some.injEq] at h; subst outputs; rfl + | cons a rest ih => + simp only [List.mapM_cons, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨b, hb, bs, hbs, rfl⟩ := h + simp only [List.map_cons, step a b hb, ih hbs] + +structure CheckedFrontier (signature : PrimitiveSignature β) (store : Store β) where + readings : List (FrontierHeader store) + validated : signature.validate store = true + formed : Signature.Formed.{u,v} signature.environment (readings.map (·.header)) + +def CheckedFrontier.headers {signature : PrimitiveSignature β} {store : Store β} + (frontier : CheckedFrontier.{u,v} signature store) : List (Signature.Header β) := + frontier.readings.map (·.header) + +def CheckedFrontier.refs {signature : PrimitiveSignature β} {store : Store β} + (frontier : CheckedFrontier.{u,v} signature store) : List (ConstRef β) := + frontier.headers.map (·.ref) + +def CheckedFrontier.interface {signature : PrimitiveSignature β} {store : Store β} + (frontier : CheckedFrontier.{u,v} signature store) : CheckedInterface signature where + entries := Signature.environment signature.environment frontier.headers + wf := frontier.formed.wf signature.environment_wf + present := ⟨frontier.formed.old signature.environment_false, + frontier.formed.old signature.environment_falseElim⟩ + +def checkFrontier? (fuel : Nat) (signature : PrimitiveSignature β) (store : Store β) + (witnesses : List (FrontierWitness β)) : Option (CheckedFrontier.{u,v} signature store) := do + if hv : signature.validate store = true then + if witnesses.length > fuel then none else do + let readings ← witnesses.mapM (readFrontierHeader? store) + let checked ← Signature.checkTypes.{u,v} fuel signature.environment + (readings.map (·.header)) (witnesses.map fun witness => ⟨witness.level, witness.typing⟩) + return ⟨readings, hv, checked.down⟩ + else none + +theorem checkFrontier?_refs {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + {witnesses : List (FrontierWitness β)} {result : CheckedFrontier.{u,v} signature store} + (h : checkFrontier? fuel signature store witnesses = some result) : + result.refs = witnesses.map (·.ref) := by + unfold checkFrontier? at h + split at h + · split at h + · cases h + · simp only [bind, Option.bind_eq_some_iff] at h + obtain ⟨readings, hr, formed, _, h⟩ := h + cases Option.some.inj h + simpa only [CheckedFrontier.refs, CheckedFrontier.headers, List.map_map, Function.comp_def] using + mapM_projection (readFrontierHeader? store) (fun r => r.header.ref) (·.ref) + (fun _ _ h => readFrontierHeader?_ref h) hr + · cases h + +theorem CheckedFrontier.header_source {signature : PrimitiveSignature β} {store : Store β} + (frontier : CheckedFrontier.{u,v} signature store) {header : Signature.Header β} + (h : header ∈ frontier.headers) : SourceHeader store header.ref header.entry := by + obtain ⟨reading, _, rfl⟩ := List.mem_map.mp h + exact reading.source + +/-- A provider may contain additional checked bodies and laws. Its entire +annotated type and universe arity must match the deferred header exactly. -/ +def HeaderPresent (entries : Environment β) (header : Signature.Header β) : Prop := + match entries header.ref with + | none => False + | some entry => entry.universes = header.universes ∧ entry.type = header.type + +instance (entries : Environment β) (header : Signature.Header β) : + Decidable (HeaderPresent entries header) := by + unfold HeaderPresent + split <;> infer_instance + +omit [DecidableEq β] in +theorem HeaderPresent.entry {entries : Environment β} {header : Signature.Header β} + (h : HeaderPresent entries header) : + ∃ entry, entries header.ref = some entry ∧ + entry.universes = header.universes ∧ entry.type = header.type := by + unfold HeaderPresent at h + cases he : entries header.ref with + | none => simp [he] at h + | some entry => exact ⟨entry, rfl, by simpa only [he] using h⟩ + +/-- The compatible interpretation of every provider gives one interpretation +of the complete deferred view, including shared references and primitive pins. -/ +theorem CheckedFrontier.compatible {signature : PrimitiveSignature β} {store : Store β} + (frontier : CheckedFrontier.{u,v} signature store) (provider : CheckedInterface signature) + (headers : ∀ header ∈ frontier.headers, HeaderPresent provider.entries header) + (V : Type v) [SetTheory V] (constants : Assignment β V) + (hM : signature.Compatible provider.entries constants) : + signature.Compatible frontier.interface.entries constants := by + have old : ∀ r entry, signature.environment r = some entry → + provider.entries r = some entry := by + intro r entry he + unfold PrimitiveSignature.environment at he + split at he + · subst r; cases Option.some.inj he; exact provider.present.1 + · split at he + · subst r; cases Option.some.inj he; exact provider.present.2 + · cases he + have he : ∀ r entry, frontier.interface.entries r = some entry → + ∃ actual, provider.entries r = some actual ∧ + actual.universes = entry.universes ∧ actual.type = entry.type ∧ + entry.body = none ∧ entry.equations = [] ∧ entry.facts = [] := by + intro r entry hr + rcases Signature.environment_source hr with hbase | ⟨header, hh, rfl, rfl⟩ + · refine ⟨entry, old r entry hbase, rfl, rfl, ?_⟩ + unfold PrimitiveSignature.environment at hbase + split at hbase + · cases Option.some.inj hbase; exact ⟨rfl, rfl, rfl⟩ + · split at hbase + · cases Option.some.inj hbase; exact ⟨rfl, rfl, rfl⟩ + · cases hbase + · obtain ⟨actual, ha, hu, ht⟩ := (headers header hh).entry + exact ⟨actual, ha, hu, ht, rfl, rfl, rfl⟩ + refine ⟨?_, hM.falseValue, hM.falseElimValue⟩ + constructor + · intro r entry hr levels hn env + obtain ⟨actual, ha, hu, ht, _⟩ := he r entry hr + rw [← ht] + exact hM.realizes.typeValid r actual ha levels (hn.trans hu.symm) env + · intro r entry hr levels hn env + obtain ⟨actual, ha, hu, ht, _⟩ := he r entry hr + rw [← ht] + exact hM.realizes.member r actual ha levels (hn.trans hu.symm) env + · intro r entry hr body hb + rw [(he r entry hr).choose_spec.2.2.2.1] at hb + cases hb + · intro r entry hr body hb + rw [(he r entry hr).choose_spec.2.2.2.1] at hb + cases hb + · intro r entry hr law hl + rw [(he r entry hr).choose_spec.2.2.2.2.1] at hl + cases hl + · intro r entry hr fact hf + rw [(he r entry hr).choose_spec.2.2.2.2.2] at hf + cases hf + +structure ConditionalStore (signature : PrimitiveSignature β) (store : Store β) + (subjects : List (ConstRef β)) where + frontier : CheckedFrontier.{u,v} signature store + checked : CheckedExtension.{u,v} signature store frontier.interface + fresh : ∀ r ∈ subjects, frontier.interface.entries r = none + present : ∀ r ∈ subjects, (checked.result.entries r).isSome = true + +def checkConditionalStore? (fuel : Nat) (signature : PrimitiveSignature β) (store : Store β) + (frontier : List (FrontierWitness β)) (subjects : List (ConstRef β)) + (witness : List (DeclarationWitness β)) : Option (ConditionalStore.{u,v} signature store subjects) := do + let dependencies ← checkFrontier?.{u,v} fuel signature store frontier + if fresh : subjects.all (fun r => (dependencies.interface.entries r).isNone) = true then + let checked ← checkDeclarationExtensions?.{u,v} fuel (store := store) dependencies.interface witness + if present : subjects.all (fun r => (checked.result.entries r).isSome) = true then + return ⟨dependencies, checked, fun r hr => Option.isNone_iff_eq_none.mp + (List.all_eq_true.mp fresh r hr), List.all_eq_true.mp present⟩ + else none + else none + +theorem checkConditionalStore?_frontier {fuel : Nat} {signature : PrimitiveSignature β} + {store : Store β} {frontier : List (FrontierWitness β)} {subjects : List (ConstRef β)} + {witness : List (DeclarationWitness β)} {result : ConditionalStore.{u,v} signature store subjects} + (h : checkConditionalStore? fuel signature store frontier subjects witness = some result) : + checkFrontier? fuel signature store frontier = some result.frontier := by + unfold checkConditionalStore? at h + simp only [bind, Option.bind_eq_some_iff] at h + obtain ⟨dependencies, hd, h⟩ := h + split at h + · simp only [Option.bind_eq_some_iff] at h + obtain ⟨checked, _, h⟩ := h + split at h + · cases Option.some.inj h; exact hd + · cases h + · cases h + +/-- No realization of the frontier is fabricated. Every realization supplied +by checked dependencies extends compatibly to all the exact subject types. -/ +theorem ConditionalStore.subject_sound {signature : PrimitiveSignature β} {store : Store β} + {subjects : List (ConstRef β)} (receipt : ConditionalStore.{u,v} signature store subjects) + (V : Type v) [SetTheory V] (constants : Assignment β V) + (hM : signature.Compatible receipt.frontier.interface.entries constants) : + ∃ constants' : Assignment β V, + signature.Compatible receipt.checked.result.entries constants' ∧ + Assignment.AgreesOn receipt.frontier.interface.entries constants constants' ∧ + ∀ ref ∈ subjects, ∃ entry, receipt.checked.result.entries ref = some entry ∧ + EntrySource signature store ref entry ∧ SourceHeader store ref entry ∧ + ∀ levels, levels.length = entry.universes → ∀ env, + WellDenoted constants' levels env entry.type ∧ + constants' ref levels ∈ˢ interp constants' levels env entry.type := by + obtain ⟨constants', hM', ha⟩ := receipt.checked.extension.models V constants hM + refine ⟨constants', hM', ha, ?_⟩ + intro ref hr + have hp := receipt.present ref hr + cases he : receipt.checked.result.entries ref with + | none => simp [he] at hp + | some entry => + have hs : EntrySource signature store ref entry := by + rcases receipt.checked.source ref entry he with old | new + · rw [receipt.fresh ref hr] at old; cases old + · exact new + refine ⟨entry, rfl, hs, hs.header, ?_⟩ + intro levels hl env + exact ⟨hM'.realizes.typeValid ref entry he levels hl env, + hM'.realizes.member ref entry he levels hl env⟩ + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Level.lean b/Ix/Theory/Certified/Level.lean new file mode 100644 index 000000000..787ce5a18 --- /dev/null +++ b/Ix/Theory/Certified/Level.lean @@ -0,0 +1,121 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.PropWhen + +namespace Ix.Theory.Certified + +open VLevel + +/-- Compute the exact zero condition of a level. `imax` is zero exactly when +its right operand is zero; its left operand still needs a scoping check. -/ +def zeroCondition : VLevel → PropWhen + | .zero => .always + | .succ _ => .never + | .max a b => (zeroCondition a).inter (zeroCondition b) + | .imax _ b => zeroCondition b + | .param i => .param i + +theorem zeroCondition_correct (l : VLevel) (values : List Nat) : + (zeroCondition l).holds (values.getD · 0) = (l.eval values == 0) := by + induction l with + | zero => rfl + | succ l ih => simp [zeroCondition, eval] + | max a b ha hb => + rw [zeroCondition, PropWhen.holds_inter, ha, hb] + apply Bool.eq_iff_iff.mpr + simp [eval, Nat.max_eq_zero_iff] + | imax a b ha hb => + simp only [zeroCondition, hb] + apply Bool.eq_iff_iff.mpr + by_cases h : b.eval values = 0 + · simp [eval, natIMax, h] + · simp [eval, natIMax, h, Nat.max_eq_zero_iff] + | param i => simp [zeroCondition, eval] + +theorem zeroCondition_wf {n : Nat} {l : VLevel} (h : l.WF n) : + (zeroCondition l).WF n := by + induction l with + | zero => simp [zeroCondition, PropWhen.always, PropWhen.WF] + | succ l ih => trivial + | max a b ha hb => exact PropWhen.WF.inter (ha h.1) (hb h.2) + | imax a b ha hb => exact hb h.2 + | param i => simpa [zeroCondition, PropWhen.param, PropWhen.WF, VLevel.WF] using h + +/-- Substitute actual anonymous levels into an annotation. Out-of-bounds +fallback agrees with `VLevel.inst`; successful validation rules it out. -/ +def instCondition (levels : List VLevel) (p : PropWhen) : PropWhen := + p.bind (fun i => zeroCondition (levels.getD i .zero)) + +theorem zeroCondition_inst (l : VLevel) (levels : List VLevel) : + zeroCondition (l.inst levels) = instCondition levels (zeroCondition l) := by + induction l with + | zero => rfl + | succ l ih => rfl + | max a b ha hb => + simpa only [zeroCondition, VLevel.inst, instCondition, PropWhen.bind_inter] using + congr (congrArg PropWhen.inter ha) hb + | imax a b ha hb => exact hb + | param i => simp [VLevel.inst, zeroCondition, instCondition] + +theorem instCondition_correct (levels : List VLevel) (p : PropWhen) (values : List Nat) : + (instCondition levels p).holds (values.getD · 0) = + p.holds ((levels.map (VLevel.eval values)).getD · 0) := by + apply PropWhen.holds_bind_of + intro i + rw [zeroCondition_correct] + simp only [List.getD_eq_getElem?_getD, List.getElem?_map] + cases levels[i]? <;> rfl + +theorem instCondition_comp (p : PropWhen) (levels levels' : List VLevel) : + instCondition levels' (instCondition levels p) = + instCondition (levels.map (VLevel.inst levels')) p := by + cases p with + | never => rfl + | allZero ps hs => + change instCondition levels' (PropWhen.bindList _ ps) = PropWhen.bindList _ ps + clear hs + induction ps with + | nil => rfl + | cons i ps ih => + simp only [PropWhen.bindList, instCondition, PropWhen.bind_inter] at ih ⊢ + rw [ih] + congr 1 + rw [← instCondition, ← zeroCondition_inst] + simp only [List.getD_eq_getElem?_getD, List.getElem?_map] + cases levels[i]? <;> rfl + +/-- Validate the exact claimed condition, its canonical encoding, and every +universe index of the source level. This is executable and fails closed. -/ +def validateZero? (n : Nat) (l : VLevel) (raw : Option (List Nat)) : Option PropWhen := do + if !decide (l.WF n) then none else do + let p ← PropWhen.fromRaw? n raw + if p = zeroCondition l then some p else none + +theorem validateZero?_sound {n : Nat} {l : VLevel} {raw : Option (List Nat)} + {p : PropWhen} (h : validateZero? n l raw = some p) : + l.WF n ∧ p.toRaw = raw ∧ p.WF n ∧ p = zeroCondition l := by + simp only [validateZero?] at h + split at h + · contradiction + next hw => + have hw : l.WF n := by simpa using hw + cases he : PropWhen.fromRaw? n raw with + | none => simp [he] at h + | some q => + simp only [he] at h + change (if q = zeroCondition l then some q else none) = some p at h + split at h + next hq => + cases h + obtain ⟨hr, hqw⟩ := PropWhen.fromRaw?_sound he + exact ⟨hw, hr, hqw, hq⟩ + · contradiction + +theorem validateZero?_complete {n : Nat} {l : VLevel} (h : l.WF n) : + validateZero? n l (zeroCondition l).toRaw = some (zeroCondition l) := by + simp [validateZero?, h, PropWhen.fromRaw?_complete (zeroCondition_wf h)] + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/LevelEq.lean b/Ix/Theory/Certified/LevelEq.lean new file mode 100644 index 000000000..04c431306 --- /dev/null +++ b/Ix/Theory/Certified/LevelEq.lean @@ -0,0 +1,116 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Level + +namespace Ix.Theory.Certified + +namespace LevelEq + +/-- A structural upper-bound check, used only when it proves pointwise +ordering. In particular, a field at u fits a carrier at u+1. -/ +def leq (a b : VLevel) : Bool := + if a = b then true else + match a, b with + | .zero, _ => true + | .max a b, c => leq a c && leq b c + | a, .max b c => leq a b || leq a c + | .succ a, .succ b => leq a b + | a, .succ b => leq a b + | .imax a b, c => leq a c && leq b c + | _, _ => false +termination_by sizeOf a + sizeOf b + +theorem leq_sound {a b : VLevel} (h : leq a b = true) (values : List Nat) : + a.eval values ≤ b.eval values := by + unfold leq at h + split at h + next he => subst b; exact Nat.le_refl _ + next => + split at h + next => exact Nat.zero_le _ + next a b c => + simp only [Bool.and_eq_true] at h + exact Nat.max_le.mpr ⟨leq_sound h.1 values, leq_sound h.2 values⟩ + next a b c => + simp only [Bool.or_eq_true] at h + rcases h with h | h + · exact Nat.le_trans (leq_sound h values) (Nat.le_max_left _ _) + · exact Nat.le_trans (leq_sound h values) (Nat.le_max_right _ _) + next a b => exact Nat.succ_le_succ (leq_sound h values) + next a b => exact Nat.le_trans (leq_sound h values) (Nat.le_succ _) + next a b c => + simp only [Bool.and_eq_true] at h + have hm := Nat.max_le.mpr ⟨leq_sound h.1 values, leq_sound h.2 values⟩ + change VLevel.natIMax _ _ ≤ _ + unfold VLevel.natIMax + split + · exact Nat.zero_le _ + · exact hm + next => contradiction +termination_by sizeOf a + sizeOf b + +/-- Conservative simplification. Failure to identify equivalent levels is a +decline, never permission to compare their zero conditions as full levels. -/ +def max (a b : VLevel) : VLevel := + match a, b with + | .zero, b => b + | a, .zero => a + | a, b => if leq a b then b else if leq b a then a else .max a b + +theorem eval_max (a b : VLevel) (values : List Nat) : + (max a b).eval values = Nat.max (a.eval values) (b.eval values) := by + unfold max + split <;> simp_all [VLevel.eval] + split + next h => exact (Nat.max_eq_right (leq_sound h values)).symm + next => + split + next h => exact (Nat.max_eq_left (leq_sound h values)).symm + next => rfl + +def imax (a b : VLevel) : VLevel := + match a, b with + | _, .zero => .zero + | a, .succ b => max a (.succ b) + | .zero, b => b + | a, b => if a = b then a else .imax a b + +theorem eval_imax (a b : VLevel) (values : List Nat) : + (imax a b).eval values = VLevel.natIMax (a.eval values) (b.eval values) := by + unfold imax + split + · simp [VLevel.eval, VLevel.natIMax] + · simp [eval_max, VLevel.eval, VLevel.natIMax] + · simp only [VLevel.eval, VLevel.natIMax, Nat.zero_max] + split <;> simp_all + · split + · subst b + simp [VLevel.natIMax] + · rfl + +def normalize : VLevel → VLevel + | .zero => .zero + | .param i => .param i + | .succ a => .succ (normalize a) + | .max a b => max (normalize a) (normalize b) + | .imax a b => imax (normalize a) (normalize b) + +theorem eval_normalize (l : VLevel) (values : List Nat) : + (normalize l).eval values = l.eval values := by + induction l <;> simp_all [normalize, VLevel.eval, eval_max, eval_imax] + +def check (n : Nat) (a b : VLevel) : Bool := + decide (a.WF n ∧ b.WF n) && decide (normalize a = normalize b) + +theorem check_sound {n : Nat} {a b : VLevel} (h : check n a b = true) : + a.WF n ∧ b.WF n ∧ ∀ values, a.eval values = b.eval values := by + simp only [check, Bool.and_eq_true, decide_eq_true_eq] at h + refine ⟨h.1.1, h.1.2, fun values => ?_⟩ + rw [← eval_normalize a values, ← eval_normalize b values, h.2] + +end LevelEq + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/LogicalPolicy.lean b/Ix/Theory/Certified/LogicalPolicy.lean new file mode 100644 index 000000000..974b2dcb0 --- /dev/null +++ b/Ix/Theory/Certified/LogicalPolicy.lean @@ -0,0 +1,130 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Frontier + +namespace Ix.Theory.Certified + +open Model + +universe u v +variable {β : Type u} [DecidableEq β] + +/-- Enumerate source members, retaining exact block and member positions. -/ +def sourceMembers (store : Store β) : List (ConstRef β) := + store.dom.flatMap fun block => match store.blocks block with + | none => [] + | some contents => (List.range contents.members.length).map (.member block ·) + +omit [DecidableEq β] in +theorem lookup_mem_sourceMembers {store : Store β} {ref : ConstRef β} {source : Const β} + (h : store.lookup ref = some source) : ref ∈ sourceMembers store := by + cases ref with + | ctor => cases h + | member block index => + cases hb : store.blocks block with + | none => simp [Store.lookup, hb] at h + | some contents => + have hm : contents.members[index]? = some source := by simpa [Store.lookup, hb] using h + have hi : index < contents.members.length := (List.getElem?_eq_some_iff.mp hm).choose + apply List.mem_flatMap.mpr + refine ⟨block, (store.mem_dom block).mp (by simp [hb]), ?_⟩ + simp only [hb] + exact List.mem_map.mpr ⟨index, List.mem_range.mpr hi, rfl⟩ + +def isLogicalAxiom (store : Store β) (ref : ConstRef β) : Bool := + match store.lookup ref with + | some (.axiom ..) => true + | _ => false + +/-- The manifest records all admitted source axioms, even unused ones. It is +computed from source kinds and the actual checked environment; it never +subtracts checked subjects or trusts a prover's declaration of axiom use. -/ +def logicalAxioms (store : Store β) (entries : Environment β) : List (ConstRef β) := + (sourceMembers store).filter fun ref => isLogicalAxiom store ref && (entries ref).isSome + +omit [DecidableEq β] in +theorem mem_logicalAxioms {store : Store β} {entries : Environment β} {ref : ConstRef β} : + ref ∈ logicalAxioms store entries ↔ + (∃ n type safety, store.lookup ref = some (.axiom n type safety)) ∧ + ∃ entry, entries ref = some entry := by + constructor + · intro h + obtain ⟨_, h⟩ := List.mem_filter.mp h + have hh : isLogicalAxiom store ref = true ∧ (entries ref).isSome = true := by simpa using h + obtain ⟨ha, he⟩ := hh + have hs : ∃ n type safety, store.lookup ref = some (.axiom n type safety) := by + unfold isLogicalAxiom at ha + cases hl : store.lookup ref with + | none => simp [hl] at ha + | some source => cases source <;> simp_all + exact ⟨hs, Option.isSome_iff_exists.mp he⟩ + · rintro ⟨⟨n, type, safety, hs⟩, entry, he⟩ + exact List.mem_filter.mpr ⟨lookup_mem_sourceMembers hs, by simp [isLogicalAxiom, hs, he]⟩ + +omit [DecidableEq β] in +theorem logicalAxioms_monotone {signature : PrimitiveSignature β} {store : Store β} + {a b : Environment β} (h : Extends.{u,v} signature a b) {ref : ConstRef β} + (hr : ref ∈ logicalAxioms store a) : ref ∈ logicalAxioms store b := by + obtain ⟨source, entry, he⟩ := mem_logicalAxioms.mp hr + exact mem_logicalAxioms.mpr ⟨source, entry, h.lookup ref entry he⟩ + +theorem CheckedFrontier.no_axioms {signature : PrimitiveSignature β} {store : Store β} + (frontier : CheckedFrontier.{u,v} signature store) {ref : ConstRef β} {entry : ConstantEntry β} + {n : Nat} {type : VExpr β} {safety : Safety} + (hs : store.lookup ref = some (.axiom n type safety)) : + frontier.interface.entries ref ≠ some entry := by + intro hr + rcases Signature.environment_source hr with old | ⟨header, hh, rfl, _⟩ + · have hv := (signature.validate_iff store).mp frontier.validated + unfold PrimitiveSignature.environment at old + split at old + · subst ref; rw [hv.1] at hs; cases hs + · split at old + · subst ref; rw [hv.2] at hs; cases hs + · cases old + · obtain ⟨reading, _, rfl⟩ := List.mem_map.mp hh + have ha := reading.allowed + simp [deferredSource, hs] at ha + +omit [DecidableEq β] in +/-- Every object axiom in an admitted interface is one of the exactly realized +standard schemas or quotient soundness, with its original source preserved. -/ +theorem EntrySource.axiom_policy {signature : PrimitiveSignature β} {store : Store β} + {ref : ConstRef β} {entry : ConstantEntry β} {n : Nat} {type : VExpr β} {safety : Safety} + (source : EntrySource signature store ref entry) + (hs : store.lookup ref = some (.axiom n type safety)) : + Standard.EntrySource store ref entry ∨ Quotient.EntrySource store ref entry := by + rcases source with ⟨_, _, he⟩ | ⟨_, _, he⟩ | ⟨_, _, _, _, _, he⟩ | h | h | h | h | h | h + · rw [he] at hs; cases hs + · rw [he] at hs; cases hs + · rw [he] at hs; cases hs + · rcases h with ⟨shape, block, recursor, mode, hb, hr, h⟩ + rcases h with ⟨rfl, _⟩ | ⟨index, ctor, _, rfl, _⟩ | ⟨rfl, _⟩ + · simp [Store.lookup, hb, Ordinary.Shape.source] at hs + · cases hs + · rcases hr with hr | ⟨_, hr⟩ <;> + simp [Store.lookup, hr, Ordinary.Shape.recursorSource] at hs + · exact Or.inl h + · exact Or.inr h + · rcases h with ⟨description, block, recursor, mode, hb, _, rfl, _⟩ + simp [Store.lookup, hb, Ordinary.Shape.source] at hs + · rcases h with ⟨_, block, recursor, mode, hb, _, rfl, _⟩ + simp [Store.lookup, hb, Ordinary.Shape.source] at hs + · exact False.elim (h.not_axiom hs) + +theorem ConditionalStore.logical_axioms_authorized {signature : PrimitiveSignature β} + {store : Store β} {subjects : List (ConstRef β)} + (receipt : ConditionalStore.{u,v} signature store subjects) {ref : ConstRef β} + (h : ref ∈ logicalAxioms store receipt.checked.result.entries) : + ∃ entry, receipt.checked.result.entries ref = some entry ∧ + (Standard.EntrySource store ref entry ∨ Quotient.EntrySource store ref entry) := by + obtain ⟨⟨n, type, safety, hs⟩, entry, he⟩ := mem_logicalAxioms.mp h + refine ⟨entry, he, ?_⟩ + rcases receipt.checked.source ref entry he with old | new + · exact False.elim (receipt.frontier.no_axioms hs old) + · exact new.axiom_policy hs + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Modeled/Admission.lean b/Ix/Theory/Certified/Modeled/Admission.lean new file mode 100644 index 000000000..0eea3996c --- /dev/null +++ b/Ix/Theory/Certified/Modeled/Admission.lean @@ -0,0 +1,59 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Admission + +namespace Ix.Theory.Certified + +open Model + +universe u v +variable {β : Type u} [DecidableEq β] + +/-- Simultaneous model companions preserve every earlier interpretation. +Models and their equation proofs must already be available through preceding +admissions; the new source block cannot justify its own model. -/ +def checkModeledExtension? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : CheckedInterface signature) (witness : Modeled.Witness β) : + Option (CheckedExtension.{u,v} signature store state) := do + let checked ← Modeled.check?.{u,v} fuel state.entries store witness + let entries := Modeled.environment state.entries witness.companions + have extension : Extends.{u,v} signature state.entries entries := by + constructor + · intro ref entry he + exact Environment.overlay_old checked.down.models.fresh_overlay he + · intro V _ constants hM + let constants' := Modeled.assignment constants witness.companions + have hm := Modeled.assignment_realizes checked.down.models state.wf constants hM.realizes + have ha := Modeled.assignment_agrees checked.down.models constants + exact ⟨constants', hM.extend signature state.present hm ha, ha⟩ + let result : CheckedInterface signature := { + entries + wf := Modeled.environment_wf checked.down.models state.wf + present := ⟨extension.lookup _ _ state.present.1, extension.lookup _ _ state.present.2⟩ + } + return { + result, extension + source := by + intro ref entry he + dsimp only [result] at he + rcases Modeled.publishedEntry_source checked.down he with old | new + · exact Or.inl old + · exact Or.inr (Or.inr (Or.inr (Or.inr (Or.inr (Or.inr (Or.inr (Or.inr (Or.inr new)))))))) + } + +def admitModeled? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : AdmittedEnvironment.{u,v} signature store) (witness : Modeled.Witness β) : + Option { result : AdmittedEnvironment.{u,v} signature store // + Extends.{u,v} signature state.entries result.entries } := do + let checked ← checkModeledExtension?.{u,v} fuel (store := store) state.interface witness + return checked.admit state + +theorem admitModeled?_extends {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + {state : AdmittedEnvironment.{u,v} signature store} {witness : Modeled.Witness β} {result} + (_ : admitModeled? fuel state witness = some result) : + Extends.{u,v} signature state.entries result.val.entries := result.property + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Modeled/Equation.lean b/Ix/Theory/Certified/Modeled/Equation.lean new file mode 100644 index 000000000..fa270584d --- /dev/null +++ b/Ix/Theory/Certified/Modeled/Equation.lean @@ -0,0 +1,73 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Signature +import Ix.Theory.Certified.Basis.Equality + +/-! Model equations may follow from checked conversion or from a checked +proof of the realized equality relation. Both complete endpoints and their +common type are checked before an equation can be published. -/ + +namespace Ix.Theory.Certified.Modeled + +open Model Model.SetTheory + +universe u v +variable {β : Type u} [DecidableEq β] + +inductive EquationProof (β : Type u) where + | conversion (witness : ConversionWitness β) + | propositional (family reflexivity recursor : ConstRef β) + (proof : AExpr β) (witness : TypingWitness β) + +structure EquationWitness (β : Type u) where + formation : Signature.RuleWitness β + proof : EquationProof β + +structure CheckedEquation (entries : Environment β) (rule : Signature.Rule β) : Prop where + formed : Signature.RuleFormed.{u,v} entries rule + equality : ConversionClaim.{u,v} entries [] rule.lhs rule.rhs + +omit [DecidableEq β] in +theorem propositional_equation {entries : Environment β} {family reflexivity recursor : ConstRef β} + (interface : Basis.Equality.Interface entries family reflexivity recursor) + {rule : Signature.Rule β} {level : VLevel} {proof : AExpr β} + (type : TypingClaim.{u,v} entries [] rule.type (.sort level)) + (lhs : TypingClaim.{u,v} entries [] rule.lhs rule.type) + (rhs : TypingClaim.{u,v} entries [] rule.rhs rule.type) + (checked : TypingClaim.{u,v} entries [] proof + (Basis.Equality.applied family level rule.type rule.lhs rule.rhs)) : + ConversionClaim.{u,v} entries [] rule.lhs rule.rhs := by + intro V _ constants hM levels env hΓ + have htype := (type V constants hM levels env hΓ).2.2 + have hlhs := (lhs V constants hM levels env hΓ).2.2 + have hrhs := (rhs V constants hM levels env hΓ).2.2 + have hproof := (checked V constants hM levels env hΓ).2.2 + apply Basis.Equality.eq_of_mem interface hM (by simpa only [interp] using htype) hlhs hrhs + simpa only [Basis.Equality.applied, AExpr.appN, interp, List.map_cons, List.map_nil, + Basis.Equality.value] using hproof + +def checkEquation? (fuel : Nat) (entries : Environment β) (rule : Signature.Rule β) + (witness : EquationWitness β) : Option (CheckedClaim.{u} (CheckedEquation.{u,v} entries rule)) := do + let formed ← Signature.checkRule.{u,v} fuel entries rule witness.formation + match witness.proof with + | .conversion witness => + let equality ← verifyConversion.{u,v} fuel rule.universes entries [] rule.lhs rule.rhs witness + return ⟨formed.down, equality.down⟩ + | .propositional family reflexivity recursor proof typing => + if hi : Basis.Equality.Interface entries family reflexivity recursor then do + let type ← verifyType.{u,v} fuel rule.universes entries [] rule.type + (.sort witness.formation.level) witness.formation.type + let checked ← verifyType.{u,v} fuel rule.universes entries [] proof + (Basis.Equality.applied family witness.formation.level rule.type rule.lhs rule.rhs) typing + return ⟨formed.down, propositional_equation hi type.down formed.down.lhs formed.down.rhs checked.down⟩ + else none + +theorem checkEquation?_sound {fuel : Nat} {entries : Environment β} {rule : Signature.Rule β} + {witness : EquationWitness β} {result} + (_ : checkEquation?.{u,v} fuel entries rule witness = some result) : + CheckedEquation.{u,v} entries rule := result.down + +end Ix.Theory.Certified.Modeled diff --git a/Ix/Theory/Certified/Modeled/Source.lean b/Ix/Theory/Certified/Modeled/Source.lean new file mode 100644 index 000000000..da6dd905f --- /dev/null +++ b/Ix/Theory/Certified/Modeled/Source.lean @@ -0,0 +1,268 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Modeled.Transport + +/-! Exact source readings for model companions. The rule reader constructs +the whole left endpoint from the stored recursor type, specializes the actual +constructor at the major's universe and parameter arguments, and retains the +stored right endpoint. It grants no equation until `checkCompanions?` checks +the corresponding complete equation in the earlier model environment. + +Mutual recursors use the store's global constructor offsets. Only rules for +the recursor's actual major family are published on that recursor. Copies of +other families' rules in a shared table do not enable a reduction. Restored +nested auxiliary recursors use their target block's native constructor slots. +-/ + +namespace Ix.Theory.Certified.Modeled + +open Model + +universe u v +variable {β : Type u} [DecidableEq β] + +structure FamilySource (β : Type u) where + universes : Nat + parameters : Nat + indices : Nat + type : VExpr β + constructors : List (Ctor β) + +def familySource? (store : Store β) (ref : ConstRef β) : Option (FamilySource β) := do + let .induct universes parameters indices type constructors .safe ← store.lookup ref | none + return ⟨universes, parameters, indices, type, constructors⟩ + +structure RecursorSource (β : Type u) where + universes : Nat + parameters : Nat + indices : Nat + motives : Nat + minors : Nat + type : VExpr β + rules : List (RecRule β) + +/-- K is deliberately unavailable on this admission route. It requires its +own semantic law; a model equation does not grant proof replacement. -/ +def recursorSource? (store : Store β) (ref : ConstRef β) : Option (RecursorSource β) := do + let .recursor universes parameters indices motives minors type rules false .safe ← store.lookup ref | none + return ⟨universes, parameters, indices, motives, minors, type, rules⟩ + +def RecursorSource.prefix (source : RecursorSource β) : Nat := + source.parameters + source.motives + source.minors + +/-- Unlike `telN`/`dropN`, a short telescope positively declines. -/ +def splitPi? : Nat → VExpr β → Option (List (VExpr β) × VExpr β) + | 0, type => some ([], type) + | n + 1, .forallE domain body => do + let (domains, result) ← splitPi? n body + return (domain :: domains, result) + | _ + 1, _ => none + +def splitLam? : Nat → VExpr β → Option (List (VExpr β) × VExpr β) + | 0, expression => some ([], expression) + | n + 1, .lam domain body => do + let (domains, result) ← splitLam? n body + return (domain :: domains, result) + | _ + 1, _ => none + +def applyPi? : VExpr β → List (VExpr β) → Option (VExpr β) + | type, [] => some type + | .forallE _ body, argument :: rest => applyPi? (body.inst argument) rest + | _, _ :: _ => none + +/-- Inspect a constant-headed application through explicit beta redexes. +Restoration may leave a parameter abstraction applied to its arguments. No +definition, equation, projection or other conversion is used by this reader. +The original type and complete equation are still checked without rewriting +their source erasures. Fuel exhaustion is a positive decline. -/ +def betaHead? : Nat → VExpr β → Option (VExpr β) + | 0, _ => none + | fuel + 1, .app function argument => do + let function ← betaHead? fuel function + match function with + | .lam _ body => betaHead? fuel (body.inst argument) + | _ => some (.app function argument) + | _ + 1, expression => some expression + +structure MajorSource (β : Type u) where + family : ConstRef β + levels : List VLevel + parameters : List (VExpr β) + prefixDomains : List (VExpr β) + +/-- Read the actual major, including nested parameter expressions. Removing +the index frame must round-trip exactly, so parameters cannot secretly depend +on the fresh result indices. The index arguments themselves are pinned. -/ +def majorSource? (store : Store β) (source : RecursorSource β) : Option (MajorSource β) := do + let (prefixDomains, tail) ← splitPi? source.prefix source.type + let (_, .forallE major _) ← splitPi? source.indices tail | none + let major ← betaHead? 512 major + let .const family levels := major.appHead | none + let info ← familySource? store family + if info.indices != source.indices || levels.length != info.universes then none else do + let arguments := major.appArgs [] + if arguments.length != info.parameters + source.indices then none else do + if arguments.drop info.parameters != VExpr.bvarRevRange 0 source.indices then none else do + let actual := arguments.take info.parameters + let parameters := actual.map (fun p => p.unliftN source.indices 0) + if parameters.map (·.liftN source.indices) != actual then none else + return ⟨family, levels, parameters, prefixDomains⟩ + +structure RawRule (β : Type u) where + universes : Nat + type : VExpr β + lhs : VExpr β + rhs : VExpr β + deriving DecidableEq + +def eraseRule (rule : Signature.Rule β) : RawRule β := + ⟨rule.universes, rule.type.erase, rule.lhs.erase, rule.rhs.erase⟩ + +/-- Construct the exact source rule at a constructor's native flattened slot. +The specialized field telescope, result family, levels, parameters and indices +determine its whole common type. The complete stored lambda telescope is +retained on the right, and `checkEquation?` checks it against that type; beta +redexes left by restoration need not have identical domain spellings. -/ +def ruleSource? (store : Store β) (recursor : ConstRef β) (source : RecursorSource β) + (major : MajorSource β) (constructor : ConstRef β) : Option (RawRule β) := do + let .ctor block member _ := constructor | none + if major.family != .member block member then none else do + let family ← familySource? store major.family + let ctor ← store.lookupCtor constructor + if ctor.safety != .safe || ctor.uvars != family.universes || + ctor.nparams != family.parameters then none else do + let some slot := store.ctorRuleIndex? constructor | none + let stored ← source.rules[slot]? + if stored.nfields != ctor.nfields then none else do + let specialized ← applyPi? ((ctor.type.instL major.levels).liftN source.prefix) major.parameters + let (fields, result) ← splitPi? ctor.nfields specialized + if result.appHead != .const major.family major.levels then none else do + let args := result.appArgs [] + let parameters := major.parameters.map (·.liftN ctor.nfields) + if args.length != ctor.nparams + source.indices || args.take ctor.nparams != parameters then none else do + let constructorValue := VExpr.appN (.const constructor major.levels) + (parameters ++ VExpr.bvarRevRange 0 ctor.nfields) + let callArgs := VExpr.bvarRevRange ctor.nfields source.prefix ++ + args.drop ctor.nparams ++ [constructorValue] + let resultType ← applyPi? (source.type.liftN (source.prefix + ctor.nfields)) callArgs + let telescope := major.prefixDomains ++ fields + let _ ← splitLam? telescope.length stored.rhs + return ⟨source.universes, VExpr.forallN telescope resultType, + VExpr.lamN telescope (VExpr.appN (.const recursor ((List.range source.universes).map VLevel.param)) + callArgs), stored.rhs⟩ + +def familyConstructors? (store : Store β) (ref : ConstRef β) : Option (List (ConstRef β)) := do + let .member block member := ref | none + let source ← familySource? store ref + return (List.range source.constructors.length).map (.ctor block member ·) + +def recursorRules? (store : Store β) (ref : ConstRef β) : Option (List (RawRule β)) := do + let source ← recursorSource? store ref + let major ← majorSource? store source + let constructors ← familyConstructors? store major.family + constructors.mapM (ruleSource? store ref source major) + +/-- A family or constructor gains no computation, projection, eta or K fact +from this route. A recursor gains exactly its checked source equations. -/ +def sourceRules? (store : Store β) (ref : ConstRef β) : Option (List (RawRule β)) := + match ref with + | .ctor block member _ => do + let _ ← familySource? store (.member block member) + let ctor ← store.lookupCtor ref + if ctor.safety = .safe then some [] else none + | .member .. => + match store.lookup ref with + | some (.induct _ _ _ _ _ .safe) => some [] + | some (.recursor ..) => recursorRules? store ref + | _ => none + +def SourceMatches (store : Store β) (companion : Companion β) : Prop := + store.type companion.header.ref = some companion.header.type.erase ∧ + store.uvars companion.header.ref = some companion.header.universes ∧ + sourceRules? store companion.header.ref = some (companion.rules.map eraseRule) + +instance (store : Store β) (companion : Companion β) : Decidable (SourceMatches store companion) := + inferInstanceAs (Decidable (_ ∧ _ ∧ _)) + +def sourceFamilies? (store : Store β) (source : β) : Option (List (ConstRef β)) := do + let contents ← store.blocks source + if contents.members.isEmpty then none else do + contents.members.zipIdx.mapM fun (constant, index) => + match constant with + | .induct _ _ _ _ _ .safe => some (.member source index) + | _ => none + +/-- Public family and constructor order is the actual block/member/constructor +order. Original recursors come first, in family order; checked auxiliary +recursors may follow. Each auxiliary's own major and native slots are read +independently, so a proposed permutation cannot change a rule's owner. -/ +def sourceRefs? (store : Store β) (source : β) (recursors : List (ConstRef β)) : + Option (List (ConstRef β)) := do + let families ← sourceFamilies? store source + if recursors.length < families.length then none else do + let majors ← recursors.mapM fun ref => do + let info ← recursorSource? store ref + let major ← majorSource? store info + return major.family + if majors.take families.length != families then none else do + let members ← families.mapM fun family => do + let ctors ← familyConstructors? store family + return family :: ctors + return members.flatten ++ recursors + +structure Witness (β : Type u) where + source : β + recursors : List (ConstRef β) + companions : List (Companion β) + equations : List (List (EquationWitness β)) + +structure Checked (entries : Environment β) (store : Store β) (witness : Witness β) : Prop where + layout : sourceRefs? store witness.source witness.recursors = some (witness.companions.map (·.header.ref)) + source : ∀ companion ∈ witness.companions, SourceMatches store companion + models : CheckedCompanions.{u,v} entries witness.companions + +def check? (fuel : Nat) (entries : Environment β) (store : Store β) (witness : Witness β) : + Option (CheckedClaim.{u} (Checked.{u,v} entries store witness)) := + if hl : sourceRefs? store witness.source witness.recursors = some (witness.companions.map (·.header.ref)) then + if hs : ∀ companion ∈ witness.companions, SourceMatches store companion then do + let checked ← checkCompanions?.{u,v} fuel entries witness.companions witness.equations + return ⟨hl, hs, checked.down⟩ + else none + else none + +theorem check?_sound {fuel : Nat} {entries : Environment β} {store : Store β} {witness : Witness β} {result} + (_ : check?.{u,v} fuel entries store witness = some result) : Checked.{u,v} entries store witness := result.down + +/-- Data-only provenance, kept in each published source entry. -/ +def EntrySource (store : Store β) (ref : ConstRef β) (entry : ConstantEntry β) : Prop := + ∃ decision : DecidableEq β, ∃ companion : Companion β, + @SourceMatches β decision store companion ∧ ref = companion.header.ref ∧ entry = companion.entry + +omit [DecidableEq β] in +theorem EntrySource.not_axiom {store : Store β} {ref : ConstRef β} {entry : ConstantEntry β} + (source : EntrySource store ref entry) {n : Nat} {type : VExpr β} {safety : Safety} + (h : store.lookup ref = some (.axiom n type safety)) : False := by + obtain ⟨decision, companion, hm, hr, _⟩ := source + letI := decision + have hh := hm.2.2 + rw [← hr] at hh + cases ref with + | ctor => cases h + | member => simp [sourceRules?, h] at hh + +theorem publishedEntry_source {entries : Environment β} {store : Store β} {witness : Witness β} + (checked : Checked.{u,v} entries store witness) {ref : ConstRef β} {entry : ConstantEntry β} + (h : environment entries witness.companions ref = some entry) : + entries ref = some entry ∨ EntrySource store ref entry := by + unfold environment Environment.overlay additions at h + cases hc : lookupCompanion witness.companions ref with + | none => simp only [hc, Option.map_none] at h; exact Or.inl h + | some companion => + have he : companion.entry = entry := by simpa [hc] using h + obtain ⟨hm, hr⟩ := lookupCompanion_sound hc + exact Or.inr ⟨inferInstance, companion, checked.source companion hm, hr.symm, he.symm⟩ + +end Ix.Theory.Certified.Modeled diff --git a/Ix/Theory/Certified/Modeled/Transport.lean b/Ix/Theory/Certified/Modeled/Transport.lean new file mode 100644 index 000000000..b568ad910 --- /dev/null +++ b/Ix/Theory/Certified/Modeled/Transport.lean @@ -0,0 +1,290 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Modeled.Equation +import Ix.Theory.Model.ReferenceMap + +/-! Simultaneous publication of source companions. Every target is already +in the checked prefix; exact mapped types and complete model equations are +validated here. Source kind and stored-rule correspondence are a separate +mandatory check before this internal result becomes declaration admission. -/ + +namespace Ix.Theory.Certified.Modeled + +open Model Model.SetTheory + +universe u v +variable {β : Type u} [DecidableEq β] + +structure Companion (β : Type u) where + header : Signature.Header β + model : ConstRef β + rules : List (Signature.Rule β) + +def Companion.entry (companion : Companion β) : ConstantEntry β := + ⟨companion.header.universes, companion.header.type, none, + companion.rules.map (fun rule => ⟨rule.lhs, rule.rhs⟩), []⟩ + +def lookupCompanion : List (Companion β) → ConstRef β → Option (Companion β) + | [], _ => none + | companion :: rest, ref => + if companion.header.ref = ref then some companion else lookupCompanion rest ref + +theorem lookupCompanion_sound {companions : List (Companion β)} {ref : ConstRef β} {companion : Companion β} + (h : lookupCompanion companions ref = some companion) : + companion ∈ companions ∧ companion.header.ref = ref := by + induction companions with + | nil => cases h + | cons head rest ih => + unfold lookupCompanion at h + split at h + · cases Option.some.inj h + exact ⟨List.mem_cons_self, ‹companion.header.ref = ref›⟩ + · obtain ⟨hm, hr⟩ := ih h + exact ⟨List.mem_cons_of_mem _ hm, hr⟩ + +theorem lookupCompanion_of_mem {companions : List (Companion β)} + (unique : (companions.map (·.header.ref)).Nodup) {companion : Companion β} + (member : companion ∈ companions) : lookupCompanion companions companion.header.ref = some companion := by + induction companions with + | nil => cases member + | cons head rest ih => + have hu := List.nodup_cons.mp unique + rcases List.mem_cons.mp member with rfl | hm + · simp [lookupCompanion] + · have hne : head.header.ref ≠ companion.header.ref := by + intro he + apply hu.1 + change head.header.ref ∈ rest.map (·.header.ref) + rw [he] + exact List.mem_map.mpr ⟨companion, hm, rfl⟩ + simp only [lookupCompanion, if_neg hne] + exact ih hu.2 hm + +def mapping (companions : List (Companion β)) (ref : ConstRef β) : ConstRef β := + match lookupCompanion companions ref with + | none => ref + | some companion => companion.model + +def additions (companions : List (Companion β)) : Environment β := + fun ref => (lookupCompanion companions ref).map Companion.entry + +def environment (entries : Environment β) (companions : List (Companion β)) : Environment β := + entries.overlay (additions companions) + +def mapRule (companions : List (Companion β)) (rule : Signature.Rule β) : Signature.Rule β := + ⟨rule.universes, rule.type.mapRefs (mapping companions), + rule.lhs.mapRefs (mapping companions), rule.rhs.mapRefs (mapping companions)⟩ + +structure CompanionChecked (entries : Environment β) (companions : List (Companion β)) + (companion : Companion β) : Prop where + modelEntry : ∃ entry, entries companion.model = some entry ∧ + entry.universes = companion.header.universes ∧ + entry.type = companion.header.type.mapRefs (mapping companions) + scope : companion.header.type.Scope companion.header.universes 0 + references : companion.header.type.ReferencesIn (environment entries companions) + ruleScopes : ∀ rule ∈ companion.rules, rule.universes = companion.header.universes ∧ + rule.lhs.Scope companion.header.universes 0 ∧ rule.rhs.Scope companion.header.universes 0 + ruleReferences : ∀ rule ∈ companion.rules, + rule.lhs.ReferencesIn (environment entries companions) ∧ rule.rhs.ReferencesIn (environment entries companions) + rules : ∀ rule ∈ companion.rules, CheckedEquation.{u,v} entries (mapRule companions rule) + +structure CheckedCompanions (entries : Environment β) (companions : List (Companion β)) : Prop where + unique : (companions.map (·.header.ref)).Nodup + fresh : ∀ companion ∈ companions, entries companion.header.ref = none + checked : ∀ companion ∈ companions, CompanionChecked.{u,v} entries companions companion + +def checkRules? (fuel : Nat) (entries : Environment β) (companions : List (Companion β)) : + (rules : List (Signature.Rule β)) → List (EquationWitness β) → + Option (CheckedClaim.{u} (∀ rule ∈ rules, CheckedEquation.{u,v} entries (mapRule companions rule))) + | [], [] => some ⟨by simp⟩ + | rule :: rules, witness :: witnesses => do + let first ← checkEquation?.{u,v} fuel entries (mapRule companions rule) witness + let rest ← checkRules? fuel entries companions rules witnesses + return ⟨by + intro candidate h + rcases List.mem_cons.mp h with rfl | h + · exact first.down + · exact rest.down candidate h⟩ + | _, _ => none + +def checkCompanion? (fuel : Nat) (entries : Environment β) (companions : List (Companion β)) + (companion : Companion β) (witnesses : List (EquationWitness β)) : + Option (CheckedClaim.{u} (CompanionChecked.{u,v} entries companions companion)) := + match he : entries companion.model with + | none => none + | some entry => + if hn : entry.universes = companion.header.universes then + if ht : entry.type = companion.header.type.mapRefs (mapping companions) then + if hs : companion.header.type.Scope companion.header.universes 0 then + if hr : companion.header.type.ReferencesIn (environment entries companions) then + if hqs : ∀ rule ∈ companion.rules, rule.universes = companion.header.universes ∧ + rule.lhs.Scope companion.header.universes 0 ∧ rule.rhs.Scope companion.header.universes 0 then + if hqr : ∀ rule ∈ companion.rules, + rule.lhs.ReferencesIn (environment entries companions) ∧ + rule.rhs.ReferencesIn (environment entries companions) then do + let rules ← checkRules?.{u,v} fuel entries companions companion.rules witnesses + return ⟨⟨entry, he, hn, ht⟩, hs, hr, hqs, hqr, rules.down⟩ + else none + else none + else none + else none + else none + else none + +def checkEach? (fuel : Nat) (entries : Environment β) (companions : List (Companion β)) : + (pending : List (Companion β)) → List (List (EquationWitness β)) → + Option (CheckedClaim.{u} (∀ companion ∈ pending, CompanionChecked.{u,v} entries companions companion)) + | [], [] => some ⟨by simp⟩ + | companion :: rest, witnesses :: remaining => do + let first ← checkCompanion?.{u,v} fuel entries companions companion witnesses + let others ← checkEach? fuel entries companions rest remaining + return ⟨by + intro candidate h + rcases List.mem_cons.mp h with rfl | h + · exact first.down + · exact others.down candidate h⟩ + | _, _ => none + +def checkCompanions? (fuel : Nat) (entries : Environment β) (companions : List (Companion β)) + (witnesses : List (List (EquationWitness β))) : + Option (CheckedClaim.{u} (CheckedCompanions.{u,v} entries companions)) := + if hu : (companions.map (·.header.ref)).Nodup then + if hf : ∀ companion ∈ companions, entries companion.header.ref = none then do + let checked ← checkEach?.{u,v} fuel entries companions companions witnesses + return ⟨hu, hf, checked.down⟩ + else none + else none + +theorem CheckedCompanions.fixed_old {entries : Environment β} {companions : List (Companion β)} + (checked : CheckedCompanions.{u,v} entries companions) {ref : ConstRef β} {entry : ConstantEntry β} + (old : entries ref = some entry) : mapping companions ref = ref := by + cases hc : lookupCompanion companions ref with + | none => simp [mapping, hc] + | some companion => + obtain ⟨hm, hr⟩ := lookupCompanion_sound hc + have hf := checked.fresh companion hm + rw [hr, old] at hf + cases hf + +theorem CheckedCompanions.fresh_overlay {entries : Environment β} {companions : List (Companion β)} + (checked : CheckedCompanions.{u,v} entries companions) : entries.Fresh (additions companions) := by + intro ref entry he + cases hc : lookupCompanion companions ref with + | none => simp [additions, hc] at he + | some companion => + obtain ⟨hm, hr⟩ := lookupCompanion_sound hc + rw [← hr] + exact checked.fresh companion hm + +theorem CompanionChecked.closed {entries : Environment β} {companions : List (Companion β)} + {companion : Companion β} (checked : CompanionChecked.{u,v} entries companions companion) : + EntryClosed (environment entries companions) companion.entry := by + constructor + · exact checked.scope + · intro body h; cases h + · exact checked.references + · intro body h; cases h + · intro law hl + obtain ⟨rule, hr, rfl⟩ := List.mem_map.mp hl + exact (checked.ruleScopes rule hr).2 + · intro law hl + obtain ⟨rule, hr, rfl⟩ := List.mem_map.mp hl + exact checked.ruleReferences rule hr + · intro fact hf; cases hf + · intro fact hf; cases hf + +/-- New entries may refer to one another. All are checked against the final +finite signature, while old entries retain their established closure. -/ +theorem environment_wf {entries : Environment β} {companions : List (Companion β)} + (checked : CheckedCompanions.{u,v} entries companions) (wf : entries.WF) : + (environment entries companions).WF := by + have hnew : ∀ ref entry, additions companions ref = some entry → + EntryClosed (environment entries companions) entry := by + intro ref entry he + cases hc : lookupCompanion companions ref with + | none => simp [additions, hc] at he + | some companion => + have hm := (lookupCompanion_sound hc).1 + have heq : companion.entry = entry := by simpa [additions, hc] using he + rw [← heq] + exact (checked.checked companion hm).closed + constructor + all_goals + intro ref entry he + change entries.overlay (additions companions) ref = some entry at he + unfold Environment.overlay at he + split at he + · cases Option.some.inj he + first + | exact (hnew _ _ ‹_›).typeScope + | exact (hnew _ _ ‹_›).bodyScope + | exact (hnew _ _ ‹_›).typeReferences + | exact (hnew _ _ ‹_›).bodyReferences + | exact (hnew _ _ ‹_›).equationScope + | exact (hnew _ _ ‹_›).equationReferences + | exact (hnew _ _ ‹_›).factScope + | exact (hnew _ _ ‹_›).factReferences + · first + | exact wf.typeScope ref entry he + | exact wf.bodyScope ref entry he + | exact (wf.typeReferences ref entry he).overlay + | exact fun body hb => (wf.bodyReferences ref entry he body hb).overlay + | exact wf.equationScope ref entry he + | exact fun law hl => ⟨(wf.equationReferences ref entry he law hl).1.overlay, + (wf.equationReferences ref entry he law hl).2.overlay⟩ + | exact wf.factScope ref entry he + | exact fun fact hf => (wf.factReferences ref entry he fact hf).overlay + +variable {V : Type v} [SetTheory V] + +def assignment (constants : Assignment β V) (companions : List (Companion β)) : Assignment β V := + fun ref levels => constants (mapping companions ref) levels + +omit [SetTheory V] in +theorem assignment_agrees {entries : Environment β} {companions : List (Companion β)} + (checked : CheckedCompanions.{u,v} entries companions) (constants : Assignment β V) : + Assignment.AgreesOn entries constants (assignment constants companions) := by + intro ref entry he levels + simp only [assignment, checked.fixed_old he] + +theorem assignment_realizes {entries : Environment β} {companions : List (Companion β)} + (checked : CheckedCompanions.{u,v} entries companions) (wf : entries.WF) + (constants : Assignment β V) (model : Realizes constants entries) : + Realizes (assignment constants companions) (environment entries companions) := by + apply (model.of_agrees wf (assignment_agrees checked constants)).overlay + intro ref entry he + cases hc : lookupCompanion companions ref with + | none => simp [additions, hc] at he + | some companion => + obtain ⟨hm, hr⟩ := lookupCompanion_sound hc + have heq : companion.entry = entry := by simpa [additions, hc] using he + rw [← heq] + have hcomp := checked.checked companion hm + obtain ⟨target, ht, hn, htype⟩ := hcomp.modelEntry + constructor + · intro levels hlevels env + have h := model.typeValid companion.model target ht levels (hlevels.trans hn.symm) env + rw [htype, wellDenoted_mapRefs] at h + exact h + · intro levels hlevels env + have h := model.member companion.model target ht levels (hlevels.trans hn.symm) env + rw [htype, interp_mapRefs] at h + change constants (mapping companions ref) levels ∈ˢ + interp (fun ref values => constants (mapping companions ref) values) levels env companion.header.type + rw [mapping, hc] + exact h + · intro body hb; cases hb + · intro body hb; cases hb + · intro law hl levels _ env + obtain ⟨rule, hmem, rfl⟩ := List.mem_map.mp hl + have h := (hcomp.rules rule hmem).equality V constants model levels env + (Context.valid_nil constants levels env) + change interp (fun ref values => constants (mapping companions ref) values) levels env rule.lhs = + interp (fun ref values => constants (mapping companions ref) values) levels env rule.rhs + simpa only [mapRule, interp_mapRefs] using h + · intro fact hf; cases hf + +end Ix.Theory.Certified.Modeled diff --git a/Ix/Theory/Certified/Natural/Admission.lean b/Ix/Theory/Certified/Natural/Admission.lean new file mode 100644 index 000000000..d4b70d298 --- /dev/null +++ b/Ix/Theory/Certified/Natural/Admission.lean @@ -0,0 +1,63 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Admission + +namespace Ix.Theory.Certified + +open Model + +universe u v +variable {β : Type u} [DecidableEq β] + +/-- Literal support is installed with the canonical Nat model in one atomic +extension, at the reference selected by the public primitive signature. -/ +def checkNaturalExtension? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : CheckedInterface signature) (witness : Ordinary.BlockWitness β) : + Option (CheckedExtension.{u,v} signature store state) := + match hp : signature.natType with + | none => none + | some pin => do + let checked ← Natural.check.{u,v} fuel state.entries store pin witness + let entries := Natural.environment state.entries witness.source witness.recursor witness.mode + have extension : Extends.{u,v} signature state.entries entries := by + constructor + · intro r entry hr + exact Natural.environment_old checked.down hr + · intro V _ constants hM + let constants' := Natural.shape.recursorAssignment constants witness.source witness.recursor witness.mode + have hm := Natural.assignment_realizes checked.down state.wf constants hM.realizes + have ha := Ordinary.Shape.publishedAssignment_agrees checked.down.block constants + exact ⟨constants', hM.extend signature state.present hm ha, ha⟩ + let result : CheckedInterface signature := { + entries + wf := Natural.environment_wf checked.down state.wf + present := ⟨extension.lookup _ _ state.present.1, extension.lookup _ _ state.present.2⟩ + } + return { + result, extension + source := by + intro r entry hr + dsimp only [result] at hr + rcases Natural.environment_source checked.down hr with hold | hordinary | hnatural + · exact Or.inl hold + · exact Or.inr (Or.inr (Or.inr (Or.inr (Or.inl hordinary)))) + · rw [← hp] at hnatural + exact Or.inr (Or.inr (Or.inr (Or.inr (Or.inr (Or.inr (Or.inr (Or.inr (Or.inl hnatural)))))))) + } + +def admitNatural? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : AdmittedEnvironment.{u,v} signature store) (witness : Ordinary.BlockWitness β) : + Option { result : AdmittedEnvironment.{u,v} signature store // + Extends.{u,v} signature state.entries result.entries } := do + let checked ← checkNaturalExtension?.{u,v} fuel (store := store) state.interface witness + return checked.admit state + +theorem admitNatural?_extends {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + {state : AdmittedEnvironment.{u,v} signature store} {witness : Ordinary.BlockWitness β} {result} + (_ : admitNatural? fuel state witness = some result) : + Extends.{u,v} signature state.entries result.val.entries := result.property + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Natural/Checked.lean b/Ix/Theory/Certified/Natural/Checked.lean new file mode 100644 index 000000000..49939ad6a --- /dev/null +++ b/Ix/Theory/Certified/Natural/Checked.lean @@ -0,0 +1,46 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Natural.Value + +namespace Ix.Theory.Certified.Natural + +open Model + +universe u v +variable {β : Type u} [DecidableEq β] + +def fact (source : β) : ConstantFact β := .natural (.ctor source 0 0) (.ctor source 0 1) +def entry (source : β) : ConstantEntry β := { (shape : Ordinary.Shape β).familyEntry with facts := [fact source] } +def environment (entries : Environment β) (source recursor : β) (mode : Inductive.ElimMode) : Environment β := + (shape.publishedEnvironment entries source recursor mode).insert (.member source 0) (entry source) + +structure Checked (entries : Environment β) (store : Store β) (pin : ConstRef β) + (source recursor : β) (mode : Inductive.ElimMode) : Prop where + block : Ordinary.CheckedBlock.{u,v} entries store source recursor shape mode + primitive : pin = .member source 0 + references : (fact source).ReferencesIn (shape.publishedEnvironment entries source recursor mode) + +/-- Literal support is tied to the profile's selected Nat reference and the +whole ordinary zero/successor source, not to a diagnostic name or marker. -/ +def check (fuel : Nat) (entries : Environment β) (store : Store β) (pin : ConstRef β) + (witness : Ordinary.BlockWitness β) : + Option (CheckedClaim.{u} (Checked.{u,v} entries store pin witness.source witness.recursor witness.mode)) := + if hp : pin = .member witness.source 0 then + if hs : witness.shape.shape = shape then + if hr : (fact witness.source).ReferencesIn + (shape.publishedEnvironment entries witness.source witness.recursor witness.mode) then do + let block ← Ordinary.checkBlock.{u,v} fuel entries store witness + return ⟨⟨hs ▸ block.down, hp, hr⟩⟩ + else none + else none + else none + +theorem check_sound {fuel : Nat} {entries : Environment β} {store : Store β} {pin : ConstRef β} + {witness : Ordinary.BlockWitness β} {result} + (_ : check.{u,v} fuel entries store pin witness = some result) : + Checked.{u,v} entries store pin witness.source witness.recursor witness.mode := result.down + +end Ix.Theory.Certified.Natural diff --git a/Ix/Theory/Certified/Natural/Publish.lean b/Ix/Theory/Certified/Natural/Publish.lean new file mode 100644 index 000000000..259d8524d --- /dev/null +++ b/Ix/Theory/Certified/Natural/Publish.lean @@ -0,0 +1,90 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Natural.Checked + +namespace Ix.Theory.Certified.Natural + +open Model Model.SetTheory + +universe u v +variable {β : Type u} [DecidableEq β] {entries : Environment β} {store : Store β} + {pin : ConstRef β} {source recursor : β} {mode : Inductive.ElimMode} + +theorem family_lookup (h : Checked.{u,v} entries store pin source recursor mode) : + shape.publishedEnvironment entries source recursor mode (.member source 0) = some (shape : Ordinary.Shape β).familyEntry := by + apply Environment.insert_old h.block.recursorChecked.fresh + simp [Ordinary.Shape.constructorEnvironment, Environment.overlay, Ordinary.Shape.constructorEntries, + Ordinary.Shape.familyEnvironment] + +theorem environment_wf (h : Checked.{u,v} entries store pin source recursor mode) (hE : entries.WF) : + (environment entries source recursor mode).WF := by + have hb := Ordinary.Shape.publishedEnvironment_wf h.block hE + exact hb.insert (hb.typeScope _ (shape : Ordinary.Shape β).familyEntry (family_lookup h)) + (by simp [entry, Ordinary.Shape.familyEntry]) + (hb.typeReferences _ (shape : Ordinary.Shape β).familyEntry (family_lookup h)) + (by simp [entry, Ordinary.Shape.familyEntry]) + (by simp [entry, Ordinary.Shape.familyEntry]) + (by simp [entry, Ordinary.Shape.familyEntry]) + (by simp [entry, fact, ConstantFact.Scope, shape, Ordinary.Shape.familyEntry]) + (by simpa only [entry, List.mem_singleton, forall_eq] using h.references) + +theorem environment_old (h : Checked.{u,v} entries store pin source recursor mode) + {r : ConstRef β} {old : ConstantEntry β} (hr : entries r = some old) : + environment entries source recursor mode r = some old := by + simp only [environment, Environment.insert, + if_neg (fresh_ne (h.block.shapeChecked.fresh _ (List.mem_cons_self ..)) hr)] + exact Ordinary.Shape.publishedEnvironment_old h.block hr + +variable {V : Type v} [SetTheory V] + +theorem assignment_realizes (h : Checked.{u,v} entries store pin source recursor mode) + (hE : entries.WF) (constants : Assignment β V) (hM : Realizes constants entries) : + Realizes (shape.recursorAssignment constants source recursor mode) (environment entries source recursor mode) := by + have hb := Ordinary.Shape.publishedAssignment_realizes h.block hE constants hM + have hr := Ordinary.Shape.recursorAssignment_reading h.block.shapeChecked h.block.recursorChecked constants + apply hb.insert + constructor + · exact hb.typeValid _ (shape : Ordinary.Shape β).familyEntry (family_lookup h) + · exact hb.member _ (shape : Ordinary.Shape β).familyEntry (family_lookup h) + · intro body hb; cases hb + · intro body hb; cases hb + · intro law hl; cases hl + · intro f hf levels hn _ + cases List.mem_singleton.mp hf + have he : levels = [] := List.eq_nil_of_length_eq_zero hn + subst levels + exact ⟨rfl, meaning h.block.shapeChecked hr.toConstructorReading hM⟩ + +end Ix.Theory.Certified.Natural + +namespace Ix.Theory.Certified.Natural + +open Model +universe u v +variable {β : Type u} [DecidableEq β] + +def EntrySource (pin : Option (ConstRef β)) (store : Store β) (r : ConstRef β) (e : ConstantEntry β) : Prop := + pin = some r ∧ ∃ source recursor mode, + store.blocks source = some ⟨[shape.source source]⟩ ∧ + shape.RecursorSourceMatches store source recursor mode ∧ r = .member source 0 ∧ e = entry source + +theorem environment_source {entries : Environment β} {store : Store β} {pin : ConstRef β} + {source recursor : β} {mode : Inductive.ElimMode} + (h : Checked.{u,v} entries store pin source recursor mode) {r : ConstRef β} {e : ConstantEntry β} + (hr : environment entries source recursor mode r = some e) : + entries r = some e ∨ Ordinary.EntrySource store r e ∨ EntrySource (some pin) store r e := by + unfold environment Environment.insert at hr + split at hr + next he => + cases Option.some.inj hr + exact Or.inr (Or.inr ⟨congrArg some (h.primitive.trans he.symm), source, recursor, mode, + h.block.shapeChecked.exactSource, h.block.recursorChecked.exactSource, he, rfl⟩) + next => + rcases Ordinary.publishedEntry_source h.block hr with hold | hnew + · exact Or.inl hold + · exact Or.inr (Or.inl hnew) + +end Ix.Theory.Certified.Natural diff --git a/Ix/Theory/Certified/Natural/Value.lean b/Ix/Theory/Certified/Natural/Value.lean new file mode 100644 index 000000000..526ed0bc9 --- /dev/null +++ b/Ix/Theory/Certified/Natural/Value.lean @@ -0,0 +1,120 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.Checked + +namespace Ix.Theory.Certified.Natural + +open Model Model.SetTheory Model.SetTheory.Tower Model.InductiveCodes Ordinary + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +def zeroConstructor : Constructor β := ⟨[], [], []⟩ +def succConstructor : Constructor β := ⟨[], [⟨[], []⟩], []⟩ +def shape : Shape β := ⟨0, [], [], .succ .zero, [zeroConstructor, succConstructor]⟩ + +variable (constants : Assignment β V) + +theorem zero_positions : + shape.positions constants [] (fun _ => empty) (inj 0 (pt : V)) = empty := by + apply eq_empty + intro p hp + obtain ⟨i, field, _, hi, _, _⟩ := Shape.mem_positions + (shape := shape) (constants := constants) (levels := []) (env := fun _ => empty) + (show (shape : Shape β).constructors[0]? = some zeroConstructor from rfl) + (xs := []) rfl hp + simp [zeroConstructor] at hi + +theorem succ_positions : + shape.positions constants [] (fun _ => empty) (inj 1 (pt : V)) = sing (inj 0 pt) := by + apply ext + intro p + constructor + · intro hp + obtain ⟨i, field, ys, hi, hys, rfl⟩ := Shape.mem_positions + (shape := shape) (constants := constants) (levels := []) (env := fun _ => empty) + (show (shape : Shape β).constructors[1]? = some succConstructor from rfl) + (xs := []) rfl hp + have he : i = 0 := by + have hb := (List.getElem?_eq_some_iff.mp hi).1 + simp only [succConstructor, List.length_singleton] at hb + omega + subst i + cases Option.some.inj hi + cases ys with + | nil => exact mem_sing.mpr rfl + | cons _ _ => exact hys.elim + · intro hp + cases mem_sing.mp hp + exact Shape.inj_mem_positions (shape := shape) (constants := constants) (levels := []) (env := fun _ => empty) + (show (shape : Shape β).constructors[1]? = some succConstructor from rfl) + (show (succConstructor : Constructor β).recursive[0]? = some ⟨[], []⟩ from rfl) + (xs := []) (ys := []) rfl trivial + +theorem zero_value : + shape.constructorValue constants [] (fun _ => empty) zeroConstructor 0 [] [] = (Numeral.value 0 : V) := by + have hg : shape.branches constants [] (fun _ => empty) zeroConstructor 0 [] [] = (empty : V) := by + rw [Shape.branches, mkTower, zero_positions] + apply eq_empty + intro p hp + obtain ⟨x, hx, _⟩ := mem_graph.mp hp + exact not_mem_empty x hx + rw [Shape.constructorValue, hg] + rfl + +theorem succ_value (n : V) : + shape.constructorValue constants [] (fun _ => empty) succConstructor 1 [] [n] = Numeral.succ n := by + have hg : shape.branches constants [] (fun _ => empty) succConstructor 1 [] [n] = + graph (fun _ => n) (sing (inj 0 pt)) := by + rw [Shape.branches, mkTower, succ_positions] + apply graph_congr + intro p hp + cases mem_sing.mp hp + simp only [Shape.branchBody, tag_inj, succConstructor, List.getElem?_cons_zero, + List.getD_cons_zero, List.length_nil, projList, Telescope.applyN] + rw [Shape.constructorValue, hg] + rfl + +variable {constants} {entries : Environment β} {store : Store β} {source : β} + +/-- Every literal is a member of the exact carrier built for zero/successor. +This is proved by natural-number induction, not supplied as a certificate. -/ +theorem value_mem (h : CheckedShape.{u,v} entries store source shape) + (hM : Realizes constants entries) (n : Nat) : + Numeral.value n ∈ˢ shape.familyValue constants [] := by + induction n with + | zero => + have hm := Shape.constructorValue_mem h hM (Context.valid_nil constants [] (fun _ => empty)) + (show (shape : Shape β).constructors[0]? = some zeroConstructor from rfl) + (xs := []) (fs := []) (by trivial) (by trivial) + rw [zero_value] at hm + exact hm + | succ n ih => + have hm := Shape.constructorValue_mem h hM (Context.valid_nil constants [] (fun _ => empty)) + (show (shape : Shape β).constructors[1]? = some succConstructor from rfl) + (xs := []) (fs := [Numeral.value n]) (by trivial) (show _ ∧ True from ⟨ih, trivial⟩) + rw [succ_value] at hm + exact hm + +theorem meaning (h : CheckedShape.{u,v} entries store source shape) + {reading : Assignment β V} (hr : ConstructorReading entries shape source constants reading) + (hM : Realizes constants entries) : + NaturalMeaning reading (.member source 0) (.ctor source 0 0) (.ctor source 0 1) := by + constructor + · intro n + rw [hr.family [] rfl] + exact value_mem h hM n + · rw [hr.constructor [] rfl 0 zeroConstructor rfl] + exact zero_value constants + · intro n + rw [hr.constructor [] rfl 1 succConstructor rfl] + have he := Shape.constructorClosedValue_apply h hM + (show (shape : Shape β).constructors[1]? = some succConstructor from rfl) + (levels := []) (ps := []) (xs := []) (fs := [Numeral.value n]) (by trivial) (by trivial) + (show _ ∧ True from ⟨value_mem h hM n, trivial⟩) + simpa only [Telescope.applyN, Telescope.extend, List.nil_append, succ_value, Numeral.value] using he + +end Ix.Theory.Certified.Natural diff --git a/Ix/Theory/Certified/Operations.lean b/Ix/Theory/Certified/Operations.lean new file mode 100644 index 000000000..012f2573d --- /dev/null +++ b/Ix/Theory/Certified/Operations.lean @@ -0,0 +1,103 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Checker + +namespace Ix.Theory.Certified + +open Model + +universe u v +variable {β : Type u} [DecidableEq β] + +structure InferenceWitness (β : Type u) where + annotations : AnnotationTree + type : AExpr β + typing : TypingWitness β + +structure InferenceResult (entries : Environment β) (n : Nat) (Γ : Context β) + (source : VExpr β) where + reading : Reading n Γ.length source + type : AExpr β + typeScope : type.Scope n Γ.length + sound : TypingClaim.{u,v} entries Γ reading.val type + +/-- Witness-directed inference. An unchecked type suggestion is accepted only +after the complete recursive typing validator establishes its meaning. -/ +def inferCertified (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (source : VExpr β) (witness : InferenceWitness β) : + Option (InferenceResult.{u,v} entries n Γ source) := do + let reading ← readAnnotations? n Γ.length source witness.annotations + if hs : witness.type.Scope n Γ.length then do + let h ← verifyType.{u,v} fuel n entries Γ reading.val witness.type witness.typing + return ⟨reading, witness.type, hs, h.down⟩ + else none + +/-- The permitted head forms, including a stuck application spine. Definitions +with bodies must be unfolded; unsupported literals and projections fail. -/ +def whnfShape (entries : Environment β) : AExpr β → Bool + | .bvar _ | .sort _ | .lam .. | .forallE .. => true + | .const r _ => (entries r).any (fun entry => entry.body.isNone) + | .app (.lam ..) _ => false + | .app f _ => whnfShape entries f + | .natLit _ | .proj .. => false + +structure WhnfWitness (β : Type u) where + source : InferenceWitness β + result : AExpr β + resultTyping : TypingWitness β + conversion : ConversionWitness β + +structure WhnfResult (entries : Environment β) (n : Nat) (Γ : Context β) + (source : VExpr β) where + input : InferenceResult.{u,v} entries n Γ source + result : AExpr β + resultScope : result.Scope n Γ.length + resultShape : whnfShape entries result = true + resultTyping : TypingClaim.{u,v} entries Γ result input.type + equal : ConversionClaim.{u,v} entries Γ input.reading.val result + +/-- Conservative validation rechecks both endpoints and all conversion +premises, including every actual beta domain. It establishes hereditary +validity for its result and does not consume an unchecked inference invariant. -/ +def whnfCertified (fuel n : Nat) (entries : Environment β) (Γ : Context β) + (source : VExpr β) (witness : WhnfWitness β) : + Option (WhnfResult.{u,v} entries n Γ source) := do + let input ← inferCertified fuel n entries Γ source witness.source + if hs : witness.result.Scope n Γ.length then + if hw : whnfShape entries witness.result = true then do + let ht ← verifyType.{u,v} fuel n entries Γ witness.result input.type witness.resultTyping + let he ← verifyConversion.{u,v} fuel n entries Γ input.reading.val witness.result + witness.conversion + return ⟨input, witness.result, hs, hw, ht.down, he.down⟩ + else none + else none + +theorem inferCertified_sound {fuel n : Nat} {entries : Environment β} + {Γ : Context β} {source : VExpr β} {witness : InferenceWitness β} + {result : InferenceResult.{u,v} entries n Γ source} + (_ : inferCertified fuel n entries Γ source witness = some result) : + result.reading.val.erase = source ∧ result.type.Scope n Γ.length ∧ + TypingClaim.{u,v} entries Γ result.reading.val result.type := + ⟨result.reading.property.1, result.typeScope, result.sound⟩ + +theorem whnfCertified_sound {fuel n : Nat} {entries : Environment β} + {Γ : Context β} {source : VExpr β} {witness : WhnfWitness β} + {result : WhnfResult.{u,v} entries n Γ source} + (_ : whnfCertified fuel n entries Γ source witness = some result) + (V : Type v) [SetTheory V] (constants : Assignment β V) + (hM : Realizes constants entries) (levels : List Nat) (env : Nat → V) + (hΓ : Γ.Valid constants levels env) : + result.input.reading.val.erase = source ∧ whnfShape entries result.result = true ∧ + WellDenoted constants levels env result.input.reading.val ∧ + WellDenoted constants levels env result.result ∧ + interp constants levels env result.input.reading.val = + interp constants levels env result.result := + ⟨result.input.reading.property.1, result.resultShape, + (result.input.sound V constants hM levels env hΓ).1, + (result.resultTyping V constants hM levels env hΓ).1, + result.equal V constants hM levels env hΓ⟩ + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Ordinary/Admission.lean b/Ix/Theory/Certified/Ordinary/Admission.lean new file mode 100644 index 000000000..bef0afc9d --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/Admission.lean @@ -0,0 +1,113 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Standard.Admission +import Ix.Theory.Certified.Quotient.Admission +import Ix.Theory.Certified.Structure.Admission +import Ix.Theory.Certified.Natural.Admission +import Ix.Theory.Certified.Modeled.Admission + +namespace Ix.Theory.Certified + +open Model + +universe u v +variable {β : Type u} [DecidableEq β] + +/-- Publish the family, constructors, recursor, and its complete equations +atomically, after their formation and semantic realization have been produced. -/ +def checkOrdinaryExtension? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : CheckedInterface signature) (witness : Ordinary.BlockWitness β) : + Option (CheckedExtension.{u,v} signature store state) := do + let checked ← Ordinary.checkBlock.{u,v} fuel state.entries store witness + let shape := witness.shape.shape + let entries := shape.publishedEnvironment state.entries witness.source witness.recursor witness.mode + have extension : Extends.{u,v} signature state.entries entries := by + constructor + · intro r entry hr + exact Ordinary.Shape.publishedEnvironment_old checked.down hr + · intro V _ constants hM + let constants' := shape.recursorAssignment constants witness.source witness.recursor witness.mode + have hnew := Ordinary.Shape.publishedAssignment_realizes checked.down state.wf constants hM.realizes + have hagree := Ordinary.Shape.publishedAssignment_agrees checked.down constants + exact ⟨constants', hM.extend signature state.present hnew hagree, hagree⟩ + let result : CheckedInterface signature := { + entries + wf := Ordinary.Shape.publishedEnvironment_wf checked.down state.wf + present := ⟨extension.lookup _ _ state.present.1, extension.lookup _ _ state.present.2⟩ + } + return { + result, extension + source := by + intro r entry hr + dsimp only [result] at hr + rcases Ordinary.publishedEntry_source checked.down hr with hold | hnew + · exact Or.inl hold + · exact Or.inr (Or.inr (Or.inr (Or.inr (Or.inl hnew)))) + } + +def admitOrdinary? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : AdmittedEnvironment.{u,v} signature store) (witness : Ordinary.BlockWitness β) : + Option { result : AdmittedEnvironment.{u,v} signature store // + Extends.{u,v} signature state.entries result.entries } := do + let checked ← checkOrdinaryExtension?.{u,v} fuel (store := store) state.interface witness + return checked.admit state + +/-- Untrusted declaration choices in dependency order. A signature stage is +never itself an element of this public admission list. -/ +inductive DeclarationWitness (β : Type u) where + | definition (witness : DefinitionWitness β) + | ordinary (witness : Ordinary.BlockWitness β) + | standard (witness : Standard.Witness β) + | quotient (witness : Quotient.Witness β) + | structure (witness : Structure.Witness β) + | natural (witness : Ordinary.BlockWitness β) + | modeled (witness : Modeled.Witness β) + +def checkDeclarationExtension? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : CheckedInterface signature) : DeclarationWitness β → + Option (CheckedExtension.{u,v} signature store state) + | .definition witness => checkDefinitionExtension? fuel state witness + | .ordinary witness => checkOrdinaryExtension? fuel state witness + | .standard witness => checkStandardExtension? fuel state witness + | .quotient witness => checkQuotientExtension? fuel state witness + | .structure witness => checkStructureExtension? fuel state witness + | .natural witness => checkNaturalExtension? fuel state witness + | .modeled witness => checkModeledExtension? fuel state witness + +def checkDeclarationExtensions? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : CheckedInterface signature) : List (DeclarationWitness β) → + Option (CheckedExtension.{u,v} signature store state) + | [] => some (CheckedExtension.refl state) + | witness :: rest => do + let step ← checkDeclarationExtension? fuel state witness + let rest ← checkDeclarationExtensions? fuel step.result rest + return step.trans rest + +def admitDeclaration? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : AdmittedEnvironment.{u,v} signature store) : DeclarationWitness β → + Option { result : AdmittedEnvironment.{u,v} signature store // + Extends.{u,v} signature state.entries result.entries } + | .definition witness => admitDefinition? fuel state witness + | .ordinary witness => admitOrdinary? fuel state witness + | .standard witness => admitStandard? fuel state witness + | .quotient witness => admitQuotient? fuel state witness + | .structure witness => admitStructure? fuel state witness + | .natural witness => admitNatural? fuel state witness + | .modeled witness => admitModeled? fuel state witness + +def admitDeclarations? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : AdmittedEnvironment.{u,v} signature store) (witness : List (DeclarationWitness β)) : + Option { result : AdmittedEnvironment.{u,v} signature store // + Extends.{u,v} signature state.entries result.entries } := do + let checked ← checkDeclarationExtensions?.{u,v} fuel (store := store) state.interface witness + return checked.admit state + +theorem admitOrdinary?_extends {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + {state : AdmittedEnvironment.{u,v} signature store} {witness : Ordinary.BlockWitness β} {result} + (_ : admitOrdinary? fuel state witness = some result) : + Extends.{u,v} signature state.entries result.val.entries := result.property + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Ordinary/Checked.lean b/Ix/Theory/Certified/Ordinary/Checked.lean new file mode 100644 index 000000000..75e3d8547 --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/Checked.lean @@ -0,0 +1,164 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.RuleEquations + +namespace Ix.Theory.Certified.Ordinary + +open Model Model.SetTheory Inductive + +universe u v +variable {β : Type u} [DecidableEq β] + +structure BlockWitness (β : Type u) where + source : β + recursor : β + shape : ShapeWitness β + mode : ElimMode + constructorTypes : List (Shape.TypeWitness β) + recursorType : Shape.TypeWitness β + rules : List (RuleWitness β) + +structure CheckedBlock (entries : Environment β) (store : Store β) (source recursor : β) + (shape : Shape β) (mode : ElimMode) : Prop where + shapeChecked : CheckedShape.{u,v} entries store source shape + constructors : shape.ConstructorFormation.{u,v} entries source + recursorChecked : shape.RecursorFormation.{u,v} entries store source recursor mode + elimination : ModeEvidence.{u,v} entries shape mode + rules : ∀ i ctor, shape.constructors[i]? = some ctor → + shape.RuleFormation.{u,v} (shape.recursorEnvironment entries source recursor mode) source recursor mode i ctor + +def checkBlock (fuel : Nat) (entries : Environment β) (store : Store β) (witness : BlockWitness β) : + Option (CheckedClaim.{u} (CheckedBlock.{u,v} entries store witness.source witness.recursor witness.shape.shape witness.mode)) := do + let shape ← checkShape.{u,v} fuel entries store witness.source witness.shape + let mode ← checkMode.{u,v} fuel entries witness.shape witness.mode + let constructors ← witness.shape.shape.checkConstructorTypes.{u,v} fuel entries witness.source + witness.shape.shape.constructors witness.constructorTypes + let recursor ← witness.shape.shape.checkRecursorType.{u,v} fuel entries store witness.source witness.recursor + witness.mode witness.recursorType + let rules ← witness.shape.shape.checkRules.{u,v} fuel + (witness.shape.shape.recursorEnvironment entries witness.source witness.recursor witness.mode) + witness.source witness.recursor witness.mode witness.shape.shape.constructors.zipIdx witness.rules + return ⟨⟨shape.down, constructors.down, recursor.down, mode.down, fun i ctor hc => + rules.down ctor i (List.mk_mem_zipIdx_iff_getElem?.mpr hc)⟩⟩ + +theorem checkBlock_sound {fuel : Nat} {entries : Environment β} {store : Store β} {witness : BlockWitness β} + {result} (_ : checkBlock.{u,v} fuel entries store witness = some result) : + CheckedBlock.{u,v} entries store witness.source witness.recursor witness.shape.shape witness.mode := result.down + +namespace Shape + +def publishedRecursorEntry (shape : Shape β) (source recursor : β) (mode : ElimMode) : ConstantEntry β := + { shape.recursorEntry source mode with equations := shape.recursorLaws source recursor mode } + +def publishedEnvironment (shape : Shape β) (entries : Environment β) (source recursor : β) + (mode : ElimMode) : Environment β := + (shape.constructorEnvironment entries source).insert (.member recursor 0) + (shape.publishedRecursorEntry source recursor mode) + +omit [DecidableEq β] in +theorem recursorLaws_member {shape : Shape β} {source recursor : β} {mode : ElimMode} + {law : ConstantEquation β} (hl : law ∈ shape.recursorLaws source recursor mode) : + ∃ i ctor, shape.constructors[i]? = some ctor ∧ + law = ⟨shape.ruleLhs source recursor mode i ctor, shape.ruleRhs source recursor mode i ctor⟩ := by + obtain ⟨⟨ctor, i⟩, hc, he⟩ := List.mem_map.mp hl + exact ⟨i, ctor, List.mk_mem_zipIdx_iff_getElem?.mp hc, he.symm⟩ + +theorem publishedEnvironment_wf {entries : Environment β} {store : Store β} {source recursor : β} + {shape : Shape β} {mode : ElimMode} (h : CheckedBlock.{u,v} entries store source recursor shape mode) + (hE : entries.WF) : (shape.publishedEnvironment entries source recursor mode).WF := by + have hwf := (recursorEnvironment_wf h.shapeChecked hE h.constructors h.recursorChecked).insert + (r := .member recursor 0) (entry := shape.publishedRecursorEntry source recursor mode) + h.recursorChecked.closed.typeScope (by simp [publishedRecursorEntry, recursorEntry]) + h.recursorChecked.closed.typeReferences.insert (by simp [publishedRecursorEntry, recursorEntry]) + (by + intro law hl + obtain ⟨i, ctor, hc, rfl⟩ := recursorLaws_member hl + exact (h.rules i ctor hc).scope) + (by + intro law hl + obtain ⟨i, ctor, hc, rfl⟩ := recursorLaws_member hl + exact (h.rules i ctor hc).references) + (by simp [publishedRecursorEntry, recursorEntry]) (by simp [publishedRecursorEntry, recursorEntry]) + simpa only [publishedEnvironment, recursorEnvironment, Environment.insert_replace] using hwf + +theorem publishedEnvironment_old {entries : Environment β} {store : Store β} {source recursor : β} + {shape : Shape β} {mode : ElimMode} (h : CheckedBlock.{u,v} entries store source recursor shape mode) + {r : ConstRef β} {entry : ConstantEntry β} (hr : entries r = some entry) : + shape.publishedEnvironment entries source recursor mode r = some entry := + Environment.insert_old h.recursorChecked.fresh (Environment.overlay_old + (constructorEntries_fresh h.shapeChecked) (Environment.insert_old + (h.shapeChecked.fresh _ (List.mem_cons_self ..)) hr)) + +variable {V : Type v} [SetTheory V] + +theorem publishedAssignment_realizes {entries : Environment β} {store : Store β} {source recursor : β} + {shape : Shape β} {mode : ElimMode} (h : CheckedBlock.{u,v} entries store source recursor shape mode) + (hE : entries.WF) (constants : Assignment β V) (hM : Realizes constants entries) : + Realizes (shape.recursorAssignment constants source recursor mode) + (shape.publishedEnvironment entries source recursor mode) := by + have hstage := recursorAssignment_realizes h.shapeChecked hE h.constructors h.recursorChecked h.elimination constants hM + have hlocal : EntryRealization (shape.recursorAssignment constants source recursor mode) (.member recursor 0) + (shape.publishedRecursorEntry source recursor mode) := by + constructor + · exact hstage.typeValid (.member recursor 0) (shape.recursorEntry source mode) (Environment.insert_same ..) + · exact hstage.member (.member recursor 0) (shape.recursorEntry source mode) (Environment.insert_same ..) + · intro body hb; cases hb + · intro body hb; cases hb + · intro law hl levels hn env + obtain ⟨i, ctor, hc, rfl⟩ := recursorLaws_member hl + exact produced_rule_eq h.shapeChecked hE h.constructors h.recursorChecked h.elimination hM hn hc (h.rules i ctor hc) env + · intro fact hf; cases hf + have hm := hstage.insert hlocal + simpa only [publishedEnvironment, recursorEnvironment, Environment.insert_replace] using hm + +theorem publishedAssignment_agrees {entries : Environment β} {store : Store β} {source recursor : β} + {shape : Shape β} {mode : ElimMode} (h : CheckedBlock.{u,v} entries store source recursor shape mode) + (constants : Assignment β V) : + Assignment.AgreesOn entries constants (shape.recursorAssignment constants source recursor mode) := + (recursorAssignment_reading h.shapeChecked h.recursorChecked constants).agrees + +end Shape + +/-- Source provenance is data-only. Both blocks and every equation endpoint +are tied to the exact ordinary description and recursor stored in the input. -/ +def EntrySource (store : Store β) (r : ConstRef β) (entry : ConstantEntry β) : Prop := + ∃ shape : Shape β, ∃ source recursor mode, + store.blocks source = some ⟨[shape.source source]⟩ ∧ + shape.RecursorSourceMatches store source recursor mode ∧ + ((r = .member source 0 ∧ entry = shape.familyEntry) ∨ + (∃ i ctor, shape.constructors[i]? = some ctor ∧ r = .ctor source 0 i ∧ entry = shape.constructorEntry source ctor) ∨ + (r = .member recursor 0 ∧ entry = shape.publishedRecursorEntry source recursor mode)) + +theorem publishedEntry_source {entries : Environment β} {store : Store β} {source recursor : β} + {shape : Shape β} {mode : ElimMode} (h : CheckedBlock.{u,v} entries store source recursor shape mode) + {r : ConstRef β} {entry : ConstantEntry β} + (hr : shape.publishedEnvironment entries source recursor mode r = some entry) : + entries r = some entry ∨ EntrySource store r entry := by + have wrap : ∀ entry, ((r = .member source 0 ∧ entry = shape.familyEntry) ∨ + (∃ i ctor, shape.constructors[i]? = some ctor ∧ r = .ctor source 0 i ∧ entry = shape.constructorEntry source ctor) ∨ + (r = .member recursor 0 ∧ entry = shape.publishedRecursorEntry source recursor mode)) → EntrySource store r entry := + fun _ hh => ⟨shape, source, recursor, mode, h.shapeChecked.exactSource, h.recursorChecked.exactSource, hh⟩ + unfold Shape.publishedEnvironment Environment.insert at hr + split at hr + next he => + cases Option.some.inj hr + exact Or.inr (wrap _ (Or.inr (Or.inr ⟨he, rfl⟩))) + next => + unfold Shape.constructorEnvironment Environment.overlay at hr + split at hr + next entry' he => + cases Option.some.inj hr + obtain ⟨i, ctor, hr, hc, he'⟩ := Shape.constructorEntries_some he + exact Or.inr (wrap _ (Or.inr (Or.inl ⟨i, ctor, hc, hr, he'⟩))) + next => + unfold Shape.familyEnvironment Environment.insert at hr + split at hr + next he => + cases Option.some.inj hr + exact Or.inr (wrap _ (Or.inl ⟨he, rfl⟩)) + next => exact Or.inl hr + +end Ix.Theory.Certified.Ordinary diff --git a/Ix/Theory/Certified/Ordinary/Computation.lean b/Ix/Theory/Certified/Ordinary/Computation.lean new file mode 100644 index 000000000..00c9c2274 --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/Computation.lean @@ -0,0 +1,133 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.Eliminator + +namespace Ix.Theory.Certified.Ordinary.Shape + +open Model Model.SetTheory Model.SetTheory.Tower Model.SetModel Model.InductiveCodes + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +noncomputable def recursiveCall (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (v : Nat) (m : V) (minors : List V) + (hD : (shape.container constants levels env).WF (shape.level.eval levels)) + (hlarge : (shape.container constants levels env).LargeElim (shape.level.eval levels)) + (xs fs : List V) (j : Nat) (field : RecursiveField β) : V := + Telescope.curry v (Telescope.interpret constants levels (Telescope.extend env xs) field.domains) fun ys => + Telescope.applyN (shape.largeValue constants levels env v m minors hD hlarge) + (field.indices.map (interp constants levels (Telescope.extend (Telescope.extend env xs) ys)) ++ + [Telescope.applyN (fs.getD j empty) ys]) + +noncomputable def recursiveCalls (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (v : Nat) (m : V) (minors : List V) + (hD : (shape.container constants levels env).WF (shape.level.eval levels)) + (hlarge : (shape.container constants levels env).LargeElim (shape.level.eval levels)) + (ctor : Constructor β) (xs fs : List V) : List V := + ctor.recursive.zipIdx.map fun (field, j) => + shape.recursiveCall constants levels env v m minors hD hlarge xs fs j field + +variable {entries : Environment β} {store : Store β} {source : β} {shape : Shape β} + {constants : Assignment β V} {levels : List Nat} {env : Nat → V} + +theorem ihValue_childResults (h : CheckedShape.{u,v} entries store source shape) + (hM : Realizes constants entries) (hΓ : shape.parameterContext.Valid constants levels env) + {v : Nat} {m : V} {minors : List V} (hm : m ∈ˢ shape.motiveSet constants levels env v) + (hminors : shape.MinorValuesFit constants levels env v m minors) + (hD : (shape.container constants levels env).WF (shape.level.eval levels)) + (hlarge : (shape.container constants levels env).LargeElim (shape.level.eval levels)) + {i j : Nat} {ctor : Constructor β} {field : RecursiveField β} + (hc : shape.constructors[i]? = some ctor) (hf : ctor.recursive[j]? = some field) {xs fs : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) + (hfs : shape.RecursiveValuesFit constants levels env ctor xs fs) + {index : V} (hi : index ∈ˢ shape.indexSet constants levels env) + (ha : inj i (mkTower xs) ∈ˢ (shape.container constants levels env).shapes index) + {g : V} (hg : g ∈ˢ piSet (shape.positions constants levels env (inj i (mkTower xs))) + (fun p => app (shape.carrier constants levels env) + (shape.targetIndex constants levels env (inj i (mkTower xs)) p))) + (he : g = shape.branches constants levels env ctor i xs fs) : + shape.ihValue constants levels env v xs j field + (IndexedContainer.childResults hD hi ha hg + (IndexedContainer.fold hD hlarge (shape.algebra constants levels env v m minors))) = + shape.recursiveCall constants levels env v m minors hD hlarge xs fs j field := by + subst g + have hctor := h.constructors ctor (List.mem_of_getElem? hc) + have hfield := hctor.2.2.2.2 field (List.mem_of_getElem? hf) + apply Telescope.curry_congr + intro ys hy + have hp := inj_mem_positions hc hf (FitsS.length_eq hx) hy + have ht := recursiveTarget_fits hctor hfield hM hΓ hx hy + have hchild := Telescope.applyN_mem _ (hfs.get hf) hy (recursiveResult_zero hctor hfield hM hΓ hx) + rw [IndexedContainer.app_childResults hD hi ha hg _ hp, + largeValue_apply h hM hΓ hm hminors hD hlarge ht hchild, + IndexedContainer.foldAt_eq hD hlarge _ (mkTower_mem (by decide : 1 ≠ 0) ht) hchild] + apply congrArg (IndexedContainer.fold hD hlarge (shape.algebra constants levels env v m minors)) + apply Subtype.ext + simp only [IndexedContainer.child, container, + targetIndex_inj constants levels env hc hf (FitsS.length_eq hx) (FitsS.length_eq hy), + app_branches hc hf (FitsS.length_eq hx) hy] + +theorem ihValues_childResults (h : CheckedShape.{u,v} entries store source shape) + (hM : Realizes constants entries) (hΓ : shape.parameterContext.Valid constants levels env) + {v : Nat} {m : V} {minors : List V} (hm : m ∈ˢ shape.motiveSet constants levels env v) + (hminors : shape.MinorValuesFit constants levels env v m minors) + (hD : (shape.container constants levels env).WF (shape.level.eval levels)) + (hlarge : (shape.container constants levels env).LargeElim (shape.level.eval levels)) + {i : Nat} {ctor : Constructor β} (hc : shape.constructors[i]? = some ctor) {xs fs : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) + (hfs : shape.RecursiveValuesFit constants levels env ctor xs fs) + {index : V} (hi : index ∈ˢ shape.indexSet constants levels env) + (ha : inj i (mkTower xs) ∈ˢ (shape.container constants levels env).shapes index) + {g : V} (hg : g ∈ˢ piSet (shape.positions constants levels env (inj i (mkTower xs))) + (fun p => app (shape.carrier constants levels env) + (shape.targetIndex constants levels env (inj i (mkTower xs)) p))) + (he : g = shape.branches constants levels env ctor i xs fs) : + shape.ihValues constants levels env v ctor xs + (IndexedContainer.childResults hD hi ha hg + (IndexedContainer.fold hD hlarge (shape.algebra constants levels env v m minors))) = + shape.recursiveCalls constants levels env v m minors hD hlarge ctor xs fs := by + apply List.ext_getElem? + intro j + simp only [ihValues, recursiveCalls, List.getElem?_map, List.getElem?_zipIdx] + cases hf : ctor.recursive[j]? with + | none => rfl + | some field => + simp only [Option.map_some, Nat.zero_add] + exact congrArg some (ihValue_childResults h hM hΓ hm hminors hD hlarge hc hf hx hfs hi ha hg he) + +/-- Semantic iota at the original constructor's result indices, with one +recursive call for each source field and every functional argument. -/ +theorem largeValue_iota (h : CheckedShape.{u,v} entries store source shape) + (hM : Realizes constants entries) (hΓ : shape.parameterContext.Valid constants levels env) + {v : Nat} {m : V} {minors : List V} (hm : m ∈ˢ shape.motiveSet constants levels env v) + (hminors : shape.MinorValuesFit constants levels env v m minors) + (hD : (shape.container constants levels env).WF (shape.level.eval levels)) + (hlarge : (shape.container constants levels env).LargeElim (shape.level.eval levels)) + {i : Nat} {ctor : Constructor β} (hc : shape.constructors[i]? = some ctor) {xs fs : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) + (hfs : shape.RecursiveValuesFit constants levels env ctor xs fs) : + Telescope.applyN (shape.largeValue constants levels env v m minors hD hlarge) + (ctor.indices.map (interp constants levels (Telescope.extend env xs)) ++ + [shape.constructorValue constants levels env ctor i xs fs]) = + Telescope.applyN (minors.getD i empty) + (xs ++ fs ++ shape.recursiveCalls constants levels env v m minors hD hlarge ctor xs fs) := by + have hctor := h.constructors ctor (List.mem_of_getElem? hc) + have ht := constructorResult_fits hctor hM hΓ hx + have hi := constructorResult_mem hctor hM hΓ hx + have ha : inj i (mkTower xs) ∈ˢ (shape.container constants levels env).shapes + (mkTower (ctor.indices.map (interp constants levels (Telescope.extend env xs)))) := + mem_sep.mpr ⟨inj_mem_allShapes hc hx, resultIndex_inj constants levels env hc (FitsS.length_eq hx)⟩ + have hg := branches_mem hc hctor hM hΓ hx hfs + have hx' := constructorValue_mem h hM hΓ hc hx hfs + rw [largeValue_apply h hM hΓ hm hminors hD hlarge ht hx', + IndexedContainer.foldAt_eq hD hlarge _ hi hx'] + change IndexedContainer.fold hD hlarge _ ⟨(_, IndexedContainer.node _ _ _), _⟩ = _ + rw [IndexedContainer.fold_node hD hlarge _ hi ha hg] + simp only [algebra, tag_inj, hc, ordinaryValues_inj _ (FitsS.length_eq hx)] + rw [decodeFields_branches hc hctor hM hΓ hx hfs, + ihValues_childResults h hM hΓ hm hminors hD hlarge hc hx hfs hi ha hg rfl] + +end Ix.Theory.Certified.Ordinary.Shape diff --git a/Ix/Theory/Certified/Ordinary/ConstructorStage.lean b/Ix/Theory/Certified/Ordinary/ConstructorStage.lean new file mode 100644 index 000000000..d2d72a78b --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/ConstructorStage.lean @@ -0,0 +1,154 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.RecursorValue +import Ix.Theory.Model.Signature + +namespace Ix.Theory.Certified.Ordinary + +open Model Model.SetTheory Model.SetTheory.Tower Model.SetModel + +universe u v +variable {β : Type u} [DecidableEq β] + +namespace Shape + +def constructorEntry (shape : Shape β) (source : β) (ctor : Constructor β) : ConstantEntry β := + ⟨shape.universes, ctor.type shape source, none, [], []⟩ + +def constructorEntries (shape : Shape β) (source : β) : Environment β + | .ctor b 0 i => if b = source then (shape.constructors[i]?).map (shape.constructorEntry source) else none + | _ => none + +theorem constructorEntries_some {shape : Shape β} {source : β} {r : ConstRef β} {entry : ConstantEntry β} + (h : shape.constructorEntries source r = some entry) : + ∃ i ctor, r = .ctor source 0 i ∧ shape.constructors[i]? = some ctor ∧ entry = shape.constructorEntry source ctor := by + cases r with + | member _ _ => simp [constructorEntries] at h + | ctor b member i => + cases member with + | succ _ => simp [constructorEntries] at h + | zero => + by_cases hb : b = source + · subst b + simp only [constructorEntries, ↓reduceIte] at h + obtain ⟨ctor, hc, he⟩ := Option.map_eq_some_iff.mp h + exact ⟨i, ctor, rfl, hc, he.symm⟩ + · simp [constructorEntries, hb] at h + +def constructorEnvironment (shape : Shape β) (entries : Environment β) (source : β) : Environment β := + (shape.familyEnvironment entries source).overlay (shape.constructorEntries source) + +theorem constructorEntries_fresh {entries : Environment β} {store : Store β} {source : β} {shape : Shape β} + (h : CheckedShape.{u,v} entries store source shape) : + (shape.familyEnvironment entries source).Fresh (shape.constructorEntries source) := by + intro r entry hr + obtain ⟨i, ctor, rfl, hc, _⟩ := constructorEntries_some hr + have hf := h.fresh (.ctor source 0 i) (List.mem_cons_of_mem _ + (List.mem_map.mpr ⟨i, List.mem_range.mpr (List.getElem?_eq_some_iff.mp hc).1, rfl⟩)) + simpa [familyEnvironment, Environment.insert] using hf + +def ConstructorFormation (entries : Environment β) (shape : Shape β) (source : β) : Prop := + ∀ ctor ∈ shape.constructors, + EntryClosed (shape.familyEnvironment entries source) (shape.constructorEntry source ctor) ∧ + ∃ l, TypingClaim.{u,v} (shape.familyEnvironment entries source) [] (ctor.type shape source) (.sort l) + +structure TypeWitness (β : Type u) where + level : VLevel + witness : TypingWitness β + +def checkConstructorTypes (fuel : Nat) (entries : Environment β) (shape : Shape β) (source : β) : + (ctors : List (Constructor β)) → List (TypeWitness β) → + Option (CheckedClaim.{u} (∀ ctor ∈ ctors, + EntryClosed (shape.familyEnvironment entries source) (shape.constructorEntry source ctor) ∧ + ∃ l, TypingClaim.{u,v} (shape.familyEnvironment entries source) [] (ctor.type shape source) (.sort l))) + | [], [] => some ⟨by simp⟩ + | ctor :: ctors, witness :: witnesses => + if hs : (ctor.type shape source).Scope shape.universes 0 then + if hr : (ctor.type shape source).ReferencesIn (shape.familyEnvironment entries source) then do + let ht ← verifyType.{u,v} fuel shape.universes (shape.familyEnvironment entries source) [] + (ctor.type shape source) (.sort witness.level) witness.witness + let rest ← checkConstructorTypes fuel entries shape source ctors witnesses + return ⟨by + intro ctor' hc + rcases List.mem_cons.mp hc with rfl | hc + · exact ⟨⟨hs, by simp [constructorEntry], hr, by simp [constructorEntry], + by simp [constructorEntry], by simp [constructorEntry], + by simp [constructorEntry], by simp [constructorEntry]⟩, witness.level, ht.down⟩ + · exact rest.down ctor' hc⟩ + else none + else none + | _, _ => none + +theorem constructorEnvironment_wf {entries : Environment β} {store : Store β} {source : β} {shape : Shape β} + (h : CheckedShape.{u,v} entries store source shape) (hE : entries.WF) + (hT : ConstructorFormation.{u,v} entries shape source) : + (shape.constructorEnvironment entries source).WF := by + apply (familyEnvironment_wf h hE).overlay + intro r entry hr + obtain ⟨i, ctor, rfl, hc, rfl⟩ := constructorEntries_some hr + exact (hT ctor (List.mem_of_getElem? hc)).1 + +variable {V : Type v} [SetTheory V] + +noncomputable def constructorValues (shape : Shape β) (constants : Assignment β V) : Assignment β V + | .ctor _ 0 i, levels => + match shape.constructors[i]? with + | some ctor => shape.constructorClosedValue constants levels i ctor + | none => empty + | _, _ => empty + +noncomputable def constructorAssignment (shape : Shape β) (constants : Assignment β V) (source : β) : Assignment β V := + (shape.familyAssignment constants source).overlay (shape.constructorValues constants) (shape.constructorEntries source) + +theorem constructorAssignment_agreesFamily {entries : Environment β} {store : Store β} {source : β} + {shape : Shape β} (h : CheckedShape.{u,v} entries store source shape) (constants : Assignment β V) : + Assignment.AgreesOn (shape.familyEnvironment entries source) (shape.familyAssignment constants source) + (shape.constructorAssignment constants source) := + Assignment.overlay_agrees (constructorEntries_fresh h) _ _ + +theorem constructorAssignment_reading {entries : Environment β} {store : Store β} {source : β} + {shape : Shape β} (h : CheckedShape.{u,v} entries store source shape) (constants : Assignment β V) : + ConstructorReading entries shape source constants (shape.constructorAssignment constants source) where + agrees := by + intro r entry hr levels + exact (constructorAssignment_agreesFamily h constants r entry + (Environment.insert_old (h.fresh _ (List.mem_cons_self ..)) hr) levels).trans + (familyAssignment_agrees h constants r entry hr levels) + family := by + intro levels _ + simp only [constructorAssignment, Assignment.overlay, constructorEntries, + familyAssignment, Assignment.insert_same] + constructor := by + intro levels _ i ctor hc + simp only [constructorAssignment, Assignment.overlay, constructorEntries, + ↓reduceIte, hc, Option.map_some, constructorValues] + +theorem constructorAssignment_realizes {entries : Environment β} {store : Store β} {source : β} + {shape : Shape β} (h : CheckedShape.{u,v} entries store source shape) (hE : entries.WF) + (hT : ConstructorFormation.{u,v} entries shape source) + (constants : Assignment β V) (hM : Realizes constants entries) : + Realizes (shape.constructorAssignment constants source) (shape.constructorEnvironment entries source) := by + have hr := constructorAssignment_reading h constants + have hbase := (familyAssignment_realizes h hE constants hM).of_agrees + (familyEnvironment_wf h hE) (constructorAssignment_agreesFamily h constants) + apply hbase.overlay + intro r entry hentry + obtain ⟨i, ctor, rfl, hc, rfl⟩ := constructorEntries_some hentry + obtain ⟨hclosed, l, htype⟩ := hT ctor (List.mem_of_getElem? hc) + constructor + · intro levels hn env + exact (htype V _ hbase levels env (Context.valid_nil _ _ _)).1 + · intro levels hn env + rw [hr.constructor levels hn i ctor hc] + have hm := constructorClosedValue_mem_source h hr.toFamilyReading hM hn hc + rwa [interp_closed (ctor.type shape source) _ levels hclosed.typeScope (fun _ => empty) env] at hm + · intro body hb; cases hb + · intro body hb; cases hb + · intro law hl; simp only [constructorEntry, List.not_mem_nil] at hl + · intro fact hf; simp only [constructorEntry, List.not_mem_nil] at hf + +end Shape +end Ix.Theory.Certified.Ordinary diff --git a/Ix/Theory/Certified/Ordinary/Constructors.lean b/Ix/Theory/Certified/Ordinary/Constructors.lean new file mode 100644 index 000000000..a06885be8 --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/Constructors.lean @@ -0,0 +1,237 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.Container + +namespace Ix.Theory.Certified.Ordinary.Shape + +open Model Model.SetTheory Model.SetTheory.Tower Model.InductiveCodes + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +noncomputable def carrier (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) : V := (shape.container constants levels env).carrier (shape.level.eval levels) + +noncomputable def recursiveSet (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (xs : List V) (field : RecursiveField β) : V := + Telescope.piN (shape.level.eval levels) + (Telescope.interpret constants levels (Telescope.extend env xs) field.domains) + (fun ys => app (shape.carrier constants levels env) + (mkTower (field.indices.map (interp constants levels (Telescope.extend (Telescope.extend env xs) ys))))) + +def RecursiveValuesFit (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (ctor : Constructor β) (xs fs : List V) : Prop := + FitsS (Telescope.simple (ctor.recursive.map (shape.recursiveSet constants levels env xs))) fs + +noncomputable def branchBody (ctor : Constructor β) (fs : List V) (p : V) : V := + match ctor.recursive[tag p]? with + | none => empty + | some field => Telescope.applyN (fs.getD (tag p) empty) (projList field.domains.length (ssnd p)) + +noncomputable def branches (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (ctor : Constructor β) (i : Nat) (xs fs : List V) : V := + graph (branchBody ctor fs) (shape.positions constants levels env (inj i (mkTower xs))) + +noncomputable def constructorValue (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (ctor : Constructor β) (i : Nat) (xs fs : List V) : V := + IndexedContainer.node (shape.level.eval levels) (inj i (mkTower xs)) + (shape.branches constants levels env ctor i xs fs) + +theorem RecursiveValuesFit.get {shape : Shape β} {constants : Assignment β V} {levels : List Nat} + {env : Nat → V} {ctor : Constructor β} {xs fs : List V} + (h : shape.RecursiveValuesFit constants levels env ctor xs fs) + {j : Nat} {field : RecursiveField β} (hf : ctor.recursive[j]? = some field) : + fs.getD j empty ∈ˢ shape.recursiveSet constants levels env xs field := by + apply Telescope.fits_simple_getD h + simp only [List.getElem?_map, hf, Option.map_some] + +theorem app_branches {shape : Shape β} {constants : Assignment β V} {levels : List Nat} + {env : Nat → V} {ctor : Constructor β} {field : RecursiveField β} {i j : Nat} + (hc : shape.constructors[i]? = some ctor) (hf : ctor.recursive[j]? = some field) + {xs ys fs : List V} (hx : xs.length = ctor.fields.length) + (hy : FitsS (Telescope.interpret constants levels (Telescope.extend env xs) field.domains) ys) : + app (shape.branches constants levels env ctor i xs fs) (inj j (mkTower ys)) = + Telescope.applyN (fs.getD j empty) ys := by + rw [branches, app_graph (inj_mem_positions hc hf hx hy)] + simp only [branchBody, tag_inj, hf, ssnd_inj, projList_mkTower _ _ (FitsS.length_eq hy)] + +variable {entries : Environment β} {shape : Shape β} {constants : Assignment β V} + {levels : List Nat} {env : Nat → V} {ctor : Constructor β} + +theorem recursiveSet_mem_univ {field : RecursiveField β} + (h : ConstructorEvidence.{u,v} entries shape ctor) + (hr : RecursiveEvidence.{u,v} entries shape ctor field) (hM : Realizes constants entries) + (hΓ : shape.parameterContext.Valid constants levels env) {xs : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) : + shape.recursiveSet constants levels env xs field ∈ˢ (univ (shape.level.eval levels) : V) := by + apply Telescope.piN_mem_univ + · exact fun hw => hr.2.2.1 shape.level rfl V constants hM levels _ + (h.2.1.valid constants hM levels hΓ hx) hw + · intro ys hy + exact (shape.container constants levels env).carrier_fibre_mem (shape.level.eval levels) + (recursiveTarget_mem h hr hM hΓ hx hy) + +theorem recursiveResult_zero {field : RecursiveField β} + (h : ConstructorEvidence.{u,v} entries shape ctor) + (hr : RecursiveEvidence.{u,v} entries shape ctor field) (hM : Realizes constants entries) + (hΓ : shape.parameterContext.Valid constants levels env) {xs : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) + (hw : shape.level.eval levels = 0) : + ∀ ys, FitsS (Telescope.interpret constants levels (Telescope.extend env xs) field.domains) ys → + app (shape.carrier constants levels env) + (mkTower (field.indices.map (interp constants levels (Telescope.extend (Telescope.extend env xs) ys)))) + ∈ˢ (univZero : V) := by + intro ys hy + have hm := (shape.container constants levels env).carrier_fibre_mem (shape.level.eval levels) + (recursiveTarget_mem h hr hM hΓ hx hy) + simpa only [hw, univ_zero, carrier] using hm + +theorem branches_mem {i : Nat} (hc : shape.constructors[i]? = some ctor) + (h : ConstructorEvidence.{u,v} entries shape ctor) (hM : Realizes constants entries) + (hΓ : shape.parameterContext.Valid constants levels env) {xs fs : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) + (hfs : shape.RecursiveValuesFit constants levels env ctor xs fs) : + shape.branches constants levels env ctor i xs fs ∈ˢ + piSet (shape.positions constants levels env (inj i (mkTower xs))) + (fun p => app (shape.carrier constants levels env) + (shape.targetIndex constants levels env (inj i (mkTower xs)) p)) := by + apply graph_mem_piSet + intro p hp + obtain ⟨j, field, ys, hf, hy, rfl⟩ := mem_positions hc (FitsS.length_eq hx) hp + simp only [branchBody, tag_inj, hf, ssnd_inj, projList_mkTower _ _ (FitsS.length_eq hy), + targetIndex_inj constants levels env hc hf (FitsS.length_eq hx) (FitsS.length_eq hy)] + exact Telescope.applyN_mem _ (hfs.get hf) hy + (recursiveResult_zero h (h.2.2.2.2 field (List.mem_of_getElem? hf)) hM hΓ hx) + +theorem constructorValue_mem {store : Store β} {source : β} + (h : CheckedShape.{u,v} entries store source shape) (hM : Realizes constants entries) + (hΓ : shape.parameterContext.Valid constants levels env) {i : Nat} + (hc : shape.constructors[i]? = some ctor) {xs fs : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) + (hfs : shape.RecursiveValuesFit constants levels env ctor xs fs) : + shape.constructorValue constants levels env ctor i xs fs ∈ˢ + app (shape.carrier constants levels env) + (mkTower (ctor.indices.map (interp constants levels (Telescope.extend env xs)))) := by + have hctor := h.constructors ctor (List.mem_of_getElem? hc) + apply IndexedContainer.node_mem_carrier (container_wf h hM hΓ) + (constructorResult_mem hctor hM hΓ hx) + · exact mem_sep.mpr ⟨inj_mem_allShapes hc hx, resultIndex_inj constants levels env hc (FitsS.length_eq hx)⟩ + · exact branches_mem hc hctor hM hΓ hx hfs + +/-- Reconstruct one recursive function from the container's branch graph. +Eta will recover the original source function, including at Prop. -/ +noncomputable def decodeField (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (xs : List V) (j : Nat) (field : RecursiveField β) (g : V) : V := + Telescope.curry (shape.level.eval levels) + (Telescope.interpret constants levels (Telescope.extend env xs) field.domains) + (fun ys => app g (inj j (mkTower ys))) + +noncomputable def decodeFields (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (ctor : Constructor β) (xs : List V) (g : V) : List V := + ctor.recursive.zipIdx.map fun (field, j) => shape.decodeField constants levels env xs j field g + +theorem getD_decodeFields (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (ctor : Constructor β) (xs : List V) (g : V) {j : Nat} {field : RecursiveField β} + (hf : ctor.recursive[j]? = some field) : + (shape.decodeFields constants levels env ctor xs g).getD j empty = + shape.decodeField constants levels env xs j field g := by + simp only [decodeFields, List.getD_eq_getElem?_getD, List.getElem?_map, + List.getElem?_zipIdx, hf, Option.map_some, Nat.zero_add, Option.getD_some] + +theorem decodeField_mem {i j : Nat} {field : RecursiveField β} + (hc : shape.constructors[i]? = some ctor) (hf : ctor.recursive[j]? = some field) + {xs : List V} (hx : xs.length = ctor.fields.length) {g : V} + (hg : g ∈ˢ piSet (shape.positions constants levels env (inj i (mkTower xs))) + (fun p => app (shape.carrier constants levels env) + (shape.targetIndex constants levels env (inj i (mkTower xs)) p))) : + shape.decodeField constants levels env xs j field g ∈ˢ shape.recursiveSet constants levels env xs field := by + apply Telescope.curry_mem + intro ys hy + have hp := app_mem_of_mem_piSet hg (inj_mem_positions hc hf hx hy) + rwa [targetIndex_inj constants levels env hc hf hx (FitsS.length_eq hy)] at hp + +theorem decodeFields_fit {i : Nat} (hc : shape.constructors[i]? = some ctor) + {xs : List V} (hx : xs.length = ctor.fields.length) {g : V} + (hg : g ∈ˢ piSet (shape.positions constants levels env (inj i (mkTower xs))) + (fun p => app (shape.carrier constants levels env) + (shape.targetIndex constants levels env (inj i (mkTower xs)) p))) : + shape.RecursiveValuesFit constants levels env ctor xs (shape.decodeFields constants levels env ctor xs g) := by + apply Telescope.fits_simple_map + · simp only [decodeFields, List.length_map, List.length_zipIdx] + · intro j field hf + rw [getD_decodeFields _ _ _ _ _ _ _ hf] + exact decodeField_mem hc hf hx hg + +theorem applyN_decodeField {i j : Nat} {field : RecursiveField β} + (hc : shape.constructors[i]? = some ctor) (hf : ctor.recursive[j]? = some field) + (h : ConstructorEvidence.{u,v} entries shape ctor) (hM : Realizes constants entries) + (hΓ : shape.parameterContext.Valid constants levels env) {xs ys : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) + (hy : FitsS (Telescope.interpret constants levels (Telescope.extend env xs) field.domains) ys) {g : V} + (hg : g ∈ˢ piSet (shape.positions constants levels env (inj i (mkTower xs))) + (fun p => app (shape.carrier constants levels env) + (shape.targetIndex constants levels env (inj i (mkTower xs)) p))) : + Telescope.applyN (shape.decodeField constants levels env xs j field g) ys = app g (inj j (mkTower ys)) := by + apply Telescope.applyN_curry _ hy + · intro zs hz + have hm := app_mem_of_mem_piSet hg (inj_mem_positions hc hf (FitsS.length_eq hx) hz) + rwa [targetIndex_inj constants levels env hc hf (FitsS.length_eq hx) (FitsS.length_eq hz)] at hm + · exact recursiveResult_zero h (h.2.2.2.2 field (List.mem_of_getElem? hf)) hM hΓ hx + +theorem decodeField_branches {i j : Nat} {field : RecursiveField β} + (hc : shape.constructors[i]? = some ctor) (hf : ctor.recursive[j]? = some field) + (h : ConstructorEvidence.{u,v} entries shape ctor) (hM : Realizes constants entries) + (hΓ : shape.parameterContext.Valid constants levels env) {xs fs : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) + (hfs : shape.RecursiveValuesFit constants levels env ctor xs fs) : + shape.decodeField constants levels env xs j field (shape.branches constants levels env ctor i xs fs) = + fs.getD j empty := by + change Telescope.curry _ _ _ = _ + calc + _ = Telescope.curry (shape.level.eval levels) + (Telescope.interpret constants levels (Telescope.extend env xs) field.domains) + (Telescope.applyN (fs.getD j empty)) := by + apply Telescope.curry_congr + exact fun ys hy => app_branches hc hf (FitsS.length_eq hx) hy + _ = _ := Telescope.curry_applyN _ (hfs.get hf) + (recursiveResult_zero h (h.2.2.2.2 field (List.mem_of_getElem? hf)) hM hΓ hx) + +theorem decodeFields_branches {i : Nat} (hc : shape.constructors[i]? = some ctor) + (h : ConstructorEvidence.{u,v} entries shape ctor) (hM : Realizes constants entries) + (hΓ : shape.parameterContext.Valid constants levels env) {xs fs : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) + (hfs : shape.RecursiveValuesFit constants levels env ctor xs fs) : + shape.decodeFields constants levels env ctor xs (shape.branches constants levels env ctor i xs fs) = fs := by + apply List.ext_getElem + · simpa only [decodeFields, List.length_map, List.length_zipIdx] using (FitsS.length_eq hfs).symm + · intro j hj hj' + have hjc : j < ctor.recursive.length := by + simpa only [decodeFields, List.length_map, List.length_zipIdx] using hj + have hf := List.getElem?_eq_getElem (l := ctor.recursive) hjc + have he := (getD_decodeFields shape constants levels env ctor xs _ hf).trans + (decodeField_branches hc hf h hM hΓ hx hfs) + simpa only [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hj, + List.getElem?_eq_getElem hj', Option.getD_some] using he + +theorem branches_decodeFields {i : Nat} (hc : shape.constructors[i]? = some ctor) + (h : ConstructorEvidence.{u,v} entries shape ctor) (hM : Realizes constants entries) + (hΓ : shape.parameterContext.Valid constants levels env) {xs : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) {g : V} + (hg : g ∈ˢ piSet (shape.positions constants levels env (inj i (mkTower xs))) + (fun p => app (shape.carrier constants levels env) + (shape.targetIndex constants levels env (inj i (mkTower xs)) p))) : + shape.branches constants levels env ctor i xs (shape.decodeFields constants levels env ctor xs g) = g := by + apply eq_of_mem_piSet_app_eq (branches_mem hc h hM hΓ hx (decodeFields_fit hc (FitsS.length_eq hx) hg)) hg + intro p hp + obtain ⟨j, field, ys, hf, hy, rfl⟩ := mem_positions hc (FitsS.length_eq hx) hp + rw [app_branches hc hf (FitsS.length_eq hx) hy, getD_decodeFields _ _ _ _ _ _ _ hf] + apply Telescope.applyN_curry _ hy + · intro zs hz + have hm := app_mem_of_mem_piSet hg (inj_mem_positions hc hf (FitsS.length_eq hx) hz) + rwa [targetIndex_inj constants levels env hc hf (FitsS.length_eq hx) (FitsS.length_eq hz)] at hm + · exact recursiveResult_zero h (h.2.2.2.2 field (List.mem_of_getElem? hf)) hM hΓ hx + +end Ix.Theory.Certified.Ordinary.Shape diff --git a/Ix/Theory/Certified/Ordinary/Container.lean b/Ix/Theory/Certified/Ordinary/Container.lean new file mode 100644 index 000000000..c30fb56ea --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/Container.lean @@ -0,0 +1,266 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.Shape +import Ix.Theory.Model.Inductive.Container +import Ix.Theory.Model.Inductive.Codes + +/-! +Construct the indexed container from the exact checked source description. +Shapes contain a constructor tag and its ordinary fields. Positions contain +a recursive-field tag and that function field's arguments. Indices are +dependent tuple codes. No carrier, closed member, or semantic law is supplied +by the witness. +-/ + +namespace Ix.Theory.Certified.Ordinary + +open Model Model.SetTheory Model.SetTheory.Tower Model.InductiveCodes + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +namespace Shape + +noncomputable def indexSet (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) : V := towerSet 1 (Telescope.interpret constants levels env shape.indices) + +noncomputable def ordinaryValues (ctor : Constructor β) (a : V) : List V := + projList ctor.fields.length (ssnd a) + +noncomputable def ordinaryEnv (ctor : Constructor β) (env : Nat → V) (a : V) : Nat → V := + Telescope.extend env (ordinaryValues ctor a) + +noncomputable def ordinaryFibre (shape : Shape β) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) (i : Nat) : V := + match shape.constructors[i]? with + | none => empty + | some ctor => towerSet 1 (Telescope.interpret constants levels env ctor.fields) + +noncomputable def allShapes (shape : Shape β) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) : V := sumSet 1 (shape.ordinaryFibre constants levels env) + +noncomputable def resultIndex (shape : Shape β) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) (a : V) : V := + match shape.constructors[tag a]? with + | none => empty + | some ctor => mkTower (ctor.indices.map (interp constants levels (ordinaryEnv ctor env a))) + +noncomputable def positionFibre (ctor : Constructor β) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) (a : V) (i : Nat) : V := + match ctor.recursive[i]? with + | none => empty + | some field => towerSet 1 (Telescope.interpret constants levels (ordinaryEnv ctor env a) field.domains) + +noncomputable def positions (shape : Shape β) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) (a : V) : V := + match shape.constructors[tag a]? with + | none => empty + | some ctor => sumSet 1 (positionFibre ctor constants levels env a) + +noncomputable def targetIndex (shape : Shape β) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) (a p : V) : V := + match shape.constructors[tag a]? with + | none => empty + | some ctor => + match ctor.recursive[tag p]? with + | none => empty + | some field => + let args := projList field.domains.length (ssnd p) + let extended := Telescope.extend (ordinaryEnv ctor env a) args + mkTower (field.indices.map (interp constants levels extended)) + +noncomputable def container (shape : Shape β) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) : IndexedContainer V where + indices := shape.indexSet constants levels env + shapes i := sep (shape.allShapes constants levels env) + (fun a => shape.resultIndex constants levels env a = i) + positions := shape.positions constants levels env + target := shape.targetIndex constants levels env + +theorem ordinaryValues_inj {ctor : Constructor β} (i : Nat) {xs : List V} + (hlen : xs.length = ctor.fields.length) : + ordinaryValues ctor (inj i (mkTower xs)) = xs := by + simp only [ordinaryValues, ssnd_inj, projList_mkTower _ _ hlen] + +theorem ordinaryEnv_inj {ctor : Constructor β} (env : Nat → V) (i : Nat) {xs : List V} + (hlen : xs.length = ctor.fields.length) : + ordinaryEnv ctor env (inj i (mkTower xs)) = Telescope.extend env xs := by + rw [ordinaryEnv, ordinaryValues_inj i hlen] + +theorem resultIndex_inj {shape : Shape β} (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) {i : Nat} {ctor : Constructor β} (hc : shape.constructors[i]? = some ctor) + {xs : List V} (hlen : xs.length = ctor.fields.length) : + shape.resultIndex constants levels env (inj i (mkTower xs)) = + mkTower (ctor.indices.map (interp constants levels (Telescope.extend env xs))) := by + simp only [resultIndex, tag_inj, hc, ordinaryEnv_inj env i hlen] + +theorem positions_inj {shape : Shape β} (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) {i : Nat} {ctor : Constructor β} (hc : shape.constructors[i]? = some ctor) (x : V) : + shape.positions constants levels env (inj i x) = + sumSet 1 (positionFibre ctor constants levels env (inj i x)) := by + simp only [positions, tag_inj, hc] + +theorem targetIndex_inj {shape : Shape β} (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) {i j : Nat} {ctor : Constructor β} {field : RecursiveField β} + (hc : shape.constructors[i]? = some ctor) (hf : ctor.recursive[j]? = some field) + {xs ys : List V} (hx : xs.length = ctor.fields.length) (hy : ys.length = field.domains.length) : + shape.targetIndex constants levels env (inj i (mkTower xs)) (inj j (mkTower ys)) = + mkTower (field.indices.map (interp constants levels (Telescope.extend (Telescope.extend env xs) ys))) := by + simp only [targetIndex, tag_inj, hc, hf, ssnd_inj, + projList_mkTower _ _ hy, ordinaryEnv_inj env i hx] + +theorem mem_allShapes {shape : Shape β} {constants : Assignment β V} {levels : List Nat} + {env : Nat → V} {a : V} (ha : a ∈ˢ shape.allShapes constants levels env) : + ∃ i ctor xs, shape.constructors[i]? = some ctor ∧ + FitsS (Telescope.interpret constants levels env ctor.fields) xs ∧ a = inj i (mkTower xs) := by + obtain ⟨i, x, hx, rfl⟩ := sumSet_elim (by decide : 1 ≠ 0) ha + cases hc : shape.constructors[i]? with + | none => + simp only [ordinaryFibre, hc] at hx + exact (not_mem_empty x hx).elim + | some ctor => + simp only [ordinaryFibre, hc] at hx + obtain ⟨hfit, he⟩ := towerSet_elim (by decide : 1 ≠ 0) _ hx + exact ⟨i, ctor, _, hc, hfit, congrArg (inj i) he⟩ + +theorem mem_positions {shape : Shape β} {constants : Assignment β V} {levels : List Nat} + {env : Nat → V} {i : Nat} {ctor : Constructor β} (hc : shape.constructors[i]? = some ctor) + {xs : List V} (hx : xs.length = ctor.fields.length) {p : V} + (hp : p ∈ˢ shape.positions constants levels env (inj i (mkTower xs))) : + ∃ j field ys, ctor.recursive[j]? = some field ∧ + FitsS (Telescope.interpret constants levels (Telescope.extend env xs) field.domains) ys ∧ + p = inj j (mkTower ys) := by + rw [positions_inj constants levels env hc] at hp + obtain ⟨j, y, hy, rfl⟩ := sumSet_elim (by decide : 1 ≠ 0) hp + cases hf : ctor.recursive[j]? with + | none => + simp only [positionFibre, hf] at hy + exact (not_mem_empty y hy).elim + | some field => + simp only [positionFibre, hf, ordinaryEnv_inj env i hx] at hy + obtain ⟨hfit, he⟩ := towerSet_elim (by decide : 1 ≠ 0) _ hy + exact ⟨j, field, _, hf, hfit, congrArg (inj j) he⟩ + +theorem inj_mem_allShapes {shape : Shape β} {constants : Assignment β V} {levels : List Nat} + {env : Nat → V} {i : Nat} {ctor : Constructor β} (hc : shape.constructors[i]? = some ctor) + {xs : List V} (hxs : FitsS (Telescope.interpret constants levels env ctor.fields) xs) : + inj i (mkTower xs) ∈ˢ shape.allShapes constants levels env := by + apply inj_mem (by decide : 1 ≠ 0) + simpa only [ordinaryFibre, hc] using mkTower_mem (by decide : 1 ≠ 0) hxs + +theorem inj_mem_positions {shape : Shape β} {constants : Assignment β V} {levels : List Nat} + {env : Nat → V} {i j : Nat} {ctor : Constructor β} {field : RecursiveField β} + (hc : shape.constructors[i]? = some ctor) (hf : ctor.recursive[j]? = some field) + {xs ys : List V} (hx : xs.length = ctor.fields.length) + (hys : FitsS (Telescope.interpret constants levels (Telescope.extend env xs) field.domains) ys) : + inj j (mkTower ys) ∈ˢ shape.positions constants levels env (inj i (mkTower xs)) := by + rw [positions_inj constants levels env hc] + apply inj_mem (by decide : 1 ≠ 0) + simpa only [positionFibre, hf, ordinaryEnv_inj env i hx] using + mkTower_mem (by decide : 1 ≠ 0) hys + +variable {entries : Environment β} {shape : Shape β} {constants : Assignment β V} + {levels : List Nat} {env : Nat → V} + +theorem constructorResult_fits {ctor : Constructor β} + (h : ConstructorEvidence.{u,v} entries shape ctor) (hM : Realizes constants entries) + (hΓ : shape.parameterContext.Valid constants levels env) {xs : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) : + FitsS (Telescope.interpret constants levels env shape.indices) + (ctor.indices.map (interp constants levels (Telescope.extend env xs))) := by + have hΓ' := h.2.1.valid constants hM levels hΓ hx + have ht := h.2.2.2.1 V constants hM levels (Telescope.extend env xs) hΓ' + have ht' := (Telescope.fits_lift constants levels (Telescope.extend env xs) + ctor.fields.length shape.indices 0 _).mp ht + rw [← FitsS.length_eq hx, Telescope.skip_extend] at ht' + exact ht' + +theorem constructorResult_mem {ctor : Constructor β} + (h : ConstructorEvidence.{u,v} entries shape ctor) (hM : Realizes constants entries) + (hΓ : shape.parameterContext.Valid constants levels env) {xs : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) : + mkTower (ctor.indices.map (interp constants levels (Telescope.extend env xs))) ∈ˢ + shape.indexSet constants levels env := + mkTower_mem (by decide : 1 ≠ 0) (constructorResult_fits h hM hΓ hx) + +theorem recursiveTarget_fits {ctor : Constructor β} {field : RecursiveField β} + (h : ConstructorEvidence.{u,v} entries shape ctor) + (hr : RecursiveEvidence.{u,v} entries shape ctor field) (hM : Realizes constants entries) + (hΓ : shape.parameterContext.Valid constants levels env) {xs ys : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) + (hy : FitsS (Telescope.interpret constants levels (Telescope.extend env xs) field.domains) ys) : + FitsS (Telescope.interpret constants levels env shape.indices) + (field.indices.map (interp constants levels (Telescope.extend (Telescope.extend env xs) ys))) := by + have hΓ' := h.2.1.valid constants hM levels hΓ hx + have hΓ'' := hr.2.1.valid constants hM levels hΓ' hy + have ht := hr.2.2.2 V constants hM levels _ hΓ'' + have ht' := (Telescope.fits_lift constants levels (Telescope.extend (Telescope.extend env xs) ys) + (ctor.fields.length + field.domains.length) shape.indices 0 _).mp ht + rw [← Telescope.extend_append, ← FitsS.length_eq hx, ← FitsS.length_eq hy, + ← List.length_append, Telescope.skip_extend] at ht' + simpa only [Telescope.extend_append] using ht' + +theorem recursiveTarget_mem {ctor : Constructor β} {field : RecursiveField β} + (h : ConstructorEvidence.{u,v} entries shape ctor) + (hr : RecursiveEvidence.{u,v} entries shape ctor field) (hM : Realizes constants entries) + (hΓ : shape.parameterContext.Valid constants levels env) {xs ys : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) + (hy : FitsS (Telescope.interpret constants levels (Telescope.extend env xs) field.domains) ys) : + mkTower (field.indices.map (interp constants levels (Telescope.extend (Telescope.extend env xs) ys))) ∈ˢ + shape.indexSet constants levels env := + mkTower_mem (by decide : 1 ≠ 0) (recursiveTarget_fits h hr hM hΓ hx hy) + +theorem allShapes_mem + (h : ∀ ctor ∈ shape.constructors, ConstructorEvidence.{u,v} entries shape ctor) + (hM : Realizes constants entries) (hΓ : shape.parameterContext.Valid constants levels env) + (hw : shape.level.eval levels ≠ 0) : + shape.allShapes constants levels env ∈ˢ (univ (shape.level.eval levels) : V) := by + apply sum_graph_mem hw + intro i + cases hc : shape.constructors[i]? with + | none => simpa only [ordinaryFibre, hc] using empty_mem_univ (V := V) (shape.level.eval levels) + | some ctor => + simp only [ordinaryFibre, hc] + apply Telescope.tower_graph_mem hw + exact (h ctor (List.mem_of_getElem? hc)).2.2.1 shape.level rfl V constants hM levels env hΓ hw + +/-- Every fixed-point premise is produced from checked field sorts and +dependent index applications. In particular, target-index membership is not +an assumed container property of the input. -/ +theorem container_wf {store : Store β} {source : β} + (h : CheckedShape.{u,v} entries store source shape) (hM : Realizes constants entries) + (hΓ : shape.parameterContext.Valid constants levels env) : + (shape.container constants levels env).WF (shape.level.eval levels) := by + constructor + · intro hw i hi + exact univ_sep_mem (allShapes_mem h.constructors hM hΓ hw) + · intro hw i a hi ha + obtain ⟨ha, _⟩ := mem_sep.mp ha + obtain ⟨j, ctor, xs, hc, hxs, rfl⟩ := mem_allShapes ha + change shape.positions constants levels env _ ∈ˢ _ + rw [positions_inj constants levels env hc] + apply sum_graph_mem hw + intro k + cases hf : ctor.recursive[k]? with + | none => simpa only [positionFibre, hf] using empty_mem_univ (V := V) (shape.level.eval levels) + | some field => + simp only [positionFibre, hf, ordinaryEnv_inj env j (FitsS.length_eq hxs)] + apply Telescope.tower_graph_mem hw + have hctor := h.constructors ctor (List.mem_of_getElem? hc) + have hfield := hctor.2.2.2.2 field (List.mem_of_getElem? hf) + exact hfield.2.2.1 shape.level rfl V constants hM levels _ + (hctor.2.1.valid constants hM levels hΓ hxs) hw + · intro i a p hi ha hp + obtain ⟨ha, _⟩ := mem_sep.mp ha + obtain ⟨j, ctor, xs, hc, hxs, rfl⟩ := mem_allShapes ha + obtain ⟨k, field, ys, hf, hys, rfl⟩ := mem_positions hc (FitsS.length_eq hxs) hp + change shape.targetIndex constants levels env _ _ ∈ˢ shape.indexSet constants levels env + rw [targetIndex_inj constants levels env hc hf (FitsS.length_eq hxs) (FitsS.length_eq hys)] + have hctor := h.constructors ctor (List.mem_of_getElem? hc) + exact recursiveTarget_mem hctor (hctor.2.2.2.2 field (List.mem_of_getElem? hf)) hM hΓ hxs hys + +end Shape +end Ix.Theory.Certified.Ordinary diff --git a/Ix/Theory/Certified/Ordinary/Eliminator.lean b/Ix/Theory/Certified/Ordinary/Eliminator.lean new file mode 100644 index 000000000..937c805b2 --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/Eliminator.lean @@ -0,0 +1,264 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.LargeElim + +namespace Ix.Theory.Certified.Ordinary.Shape + +open Model Model.SetTheory Model.SetTheory.Tower Model.SetModel Model.InductiveCodes + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +noncomputable def motiveSet (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (motiveLevel : Nat) : V := + Telescope.piN 1 (Telescope.interpret constants levels env shape.indices) + (fun is => piR 1 (app (shape.carrier constants levels env) (mkTower is)) + (fun _ => univ motiveLevel)) + +noncomputable def motive (shape : Shape β) (m : V) (i x : V) : V := + app (Telescope.applyN m (projList shape.indices.length i)) x + +theorem motive_mem {shape : Shape β} {constants : Assignment β V} {levels : List Nat} + {env : Nat → V} {v : Nat} {m i x : V} + (hm : m ∈ˢ shape.motiveSet constants levels env v) + (hi : i ∈ˢ shape.indexSet constants levels env) + (hx : x ∈ˢ app (shape.carrier constants levels env) i) : + shape.motive m i x ∈ˢ (univ v : V) := by + obtain ⟨hfit, he⟩ := towerSet_elim (by decide : 1 ≠ 0) _ hi + have ht := Telescope.applyN_mem _ hm hfit (fun h => (by omega : False).elim) + rw [← he] at ht + exact app_mem_piR_pos (by decide : 1 ≠ 0) ht hx + +noncomputable def ihSet (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (v : Nat) (m : V) (xs fs : List V) (j : Nat) (field : RecursiveField β) : V := + Telescope.piN v (Telescope.interpret constants levels (Telescope.extend env xs) field.domains) + (fun ys => shape.motive m + (mkTower (field.indices.map (interp constants levels (Telescope.extend (Telescope.extend env xs) ys)))) + (Telescope.applyN (fs.getD j empty) ys)) + +noncomputable def ihTypes (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (v : Nat) (m : V) (ctor : Constructor β) (xs fs : List V) : List V := + ctor.recursive.zipIdx.map fun (field, j) => shape.ihSet constants levels env v m xs fs j field + +def IHValuesFit (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (v : Nat) (m : V) (ctor : Constructor β) (xs fs hs : List V) : Prop := + FitsS (Telescope.simple (shape.ihTypes constants levels env v m ctor xs fs)) hs + +noncomputable def minorSet (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (v : Nat) (m : V) (i : Nat) (ctor : Constructor β) : V := + Telescope.piN v (Telescope.interpret constants levels env ctor.fields) fun xs => + Telescope.piN v (Telescope.simple (ctor.recursive.map (shape.recursiveSet constants levels env xs))) fun fs => + Telescope.piN v (Telescope.simple (shape.ihTypes constants levels env v m ctor xs fs)) fun _ => + shape.motive m (mkTower (ctor.indices.map (interp constants levels (Telescope.extend env xs)))) + (shape.constructorValue constants levels env ctor i xs fs) + +noncomputable def minorTypes (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (v : Nat) (m : V) : List V := + shape.constructors.zipIdx.map fun (ctor, i) => shape.minorSet constants levels env v m i ctor + +def MinorValuesFit (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (v : Nat) (m : V) (minors : List V) : Prop := + FitsS (Telescope.simple (shape.minorTypes constants levels env v m)) minors + +theorem MinorValuesFit.get {shape : Shape β} {constants : Assignment β V} {levels : List Nat} + {env : Nat → V} {v : Nat} {m : V} {minors : List V} + (h : shape.MinorValuesFit constants levels env v m minors) + {i : Nat} {ctor : Constructor β} (hc : shape.constructors[i]? = some ctor) : + minors.getD i empty ∈ˢ shape.minorSet constants levels env v m i ctor := by + apply Telescope.fits_simple_getD h + simp only [minorTypes, List.getElem?_map, List.getElem?_zipIdx, hc, Option.map_some, Nat.zero_add] + +variable {entries : Environment β} {store : Store β} {source : β} {shape : Shape β} + {constants : Assignment β V} {levels : List Nat} {env : Nat → V} + +theorem minorResult_mem_univ (h : CheckedShape.{u,v} entries store source shape) + (hM : Realizes constants entries) (hΓ : shape.parameterContext.Valid constants levels env) + {v : Nat} {m : V} (hm : m ∈ˢ shape.motiveSet constants levels env v) + {i : Nat} {ctor : Constructor β} (hc : shape.constructors[i]? = some ctor) {xs fs : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) + (hfs : shape.RecursiveValuesFit constants levels env ctor xs fs) : + shape.motive m (mkTower (ctor.indices.map (interp constants levels (Telescope.extend env xs)))) + (shape.constructorValue constants levels env ctor i xs fs) ∈ˢ (univ v : V) := + motive_mem hm (constructorResult_mem (h.constructors ctor (List.mem_of_getElem? hc)) hM hΓ hx) + (constructorValue_mem h hM hΓ hc hx hfs) + +theorem minor_apply_mem (h : CheckedShape.{u,v} entries store source shape) + (hM : Realizes constants entries) (hΓ : shape.parameterContext.Valid constants levels env) + {v : Nat} {m minor : V} (hm : m ∈ˢ shape.motiveSet constants levels env v) + {i : Nat} {ctor : Constructor β} (hc : shape.constructors[i]? = some ctor) + (hminor : minor ∈ˢ shape.minorSet constants levels env v m i ctor) {xs fs hs : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) + (hfs : shape.RecursiveValuesFit constants levels env ctor xs fs) + (hhs : shape.IHValuesFit constants levels env v m ctor xs fs hs) : + Telescope.applyN minor (xs ++ fs ++ hs) ∈ˢ + shape.motive m (mkTower (ctor.indices.map (interp constants levels (Telescope.extend env xs)))) + (shape.constructorValue constants levels env ctor i xs fs) := by + simp only [Telescope.applyN_append] + have hxstep := Telescope.applyN_mem _ hminor hx (fun hv ys hy => by + subst v + apply Telescope.piN_zero_mem + intro gs hgs + apply Telescope.piN_zero_mem + intro _ _ + simpa only [univ_zero] using minorResult_mem_univ h hM hΓ hm hc hy hgs) + have hfsstep := Telescope.applyN_mem _ hxstep hfs (fun hv gs hgs => by + subst v + apply Telescope.piN_zero_mem + intro _ _ + simpa only [univ_zero] using minorResult_mem_univ h hM hΓ hm hc hx hgs) + exact Telescope.applyN_mem _ hfsstep hhs (fun hv _ _ => by + simpa only [hv, univ_zero] using minorResult_mem_univ h hM hΓ hm hc hx hfs) + +noncomputable def ihValue (_shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (v : Nat) (xs : List V) (j : Nat) (field : RecursiveField β) (ih : V) : V := + Telescope.curry v (Telescope.interpret constants levels (Telescope.extend env xs) field.domains) + (fun ys => app ih (inj j (mkTower ys))) + +noncomputable def ihValues (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (v : Nat) (ctor : Constructor β) (xs : List V) (ih : V) : List V := + ctor.recursive.zipIdx.map fun (field, j) => shape.ihValue constants levels env v xs j field ih + +theorem ihValue_mem (h : CheckedShape.{u,v} entries store source shape) + (hM : Realizes constants entries) (hΓ : shape.parameterContext.Valid constants levels env) + {v : Nat} {m : V} {i j : Nat} {ctor : Constructor β} {field : RecursiveField β} + (hc : shape.constructors[i]? = some ctor) (hf : ctor.recursive[j]? = some field) {xs : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) {g ih : V} + (hg : g ∈ˢ piSet (shape.positions constants levels env (inj i (mkTower xs))) + (fun p => app (shape.carrier constants levels env) + (shape.targetIndex constants levels env (inj i (mkTower xs)) p))) + (hih : ih ∈ˢ piSet (shape.positions constants levels env (inj i (mkTower xs))) + (fun p => shape.motive m (shape.targetIndex constants levels env (inj i (mkTower xs)) p) (app g p))) : + shape.ihValue constants levels env v xs j field ih ∈ˢ + shape.ihSet constants levels env v m xs (shape.decodeFields constants levels env ctor xs g) j field := by + apply Telescope.curry_mem + intro ys hy + have hm := app_mem_of_mem_piSet hih (inj_mem_positions hc hf (FitsS.length_eq hx) hy) + rw [targetIndex_inj constants levels env hc hf (FitsS.length_eq hx) (FitsS.length_eq hy)] at hm + rw [getD_decodeFields _ _ _ _ _ _ _ hf, + applyN_decodeField hc hf (h.constructors ctor (List.mem_of_getElem? hc)) hM hΓ hx hy hg] + exact hm + +theorem ihValues_fit (h : CheckedShape.{u,v} entries store source shape) + (hM : Realizes constants entries) (hΓ : shape.parameterContext.Valid constants levels env) + {v : Nat} {m : V} {i : Nat} {ctor : Constructor β} + (hc : shape.constructors[i]? = some ctor) {xs : List V} + (hx : FitsS (Telescope.interpret constants levels env ctor.fields) xs) {g ih : V} + (hg : g ∈ˢ piSet (shape.positions constants levels env (inj i (mkTower xs))) + (fun p => app (shape.carrier constants levels env) + (shape.targetIndex constants levels env (inj i (mkTower xs)) p))) + (hih : ih ∈ˢ piSet (shape.positions constants levels env (inj i (mkTower xs))) + (fun p => shape.motive m (shape.targetIndex constants levels env (inj i (mkTower xs)) p) (app g p))) : + shape.IHValuesFit constants levels env v m ctor xs (shape.decodeFields constants levels env ctor xs g) + (shape.ihValues constants levels env v ctor xs ih) := by + apply Telescope.fits_simple_of_getD + · simp only [ihValues, ihTypes, List.length_map, List.length_zipIdx] + · intro j A hA + simp only [ihTypes, List.getElem?_map, List.getElem?_zipIdx] at hA + cases hf : ctor.recursive[j]? with + | none => simp [hf] at hA + | some field => + simp only [hf, Option.map_some, Nat.zero_add, Option.some.injEq] at hA + subst A + simp only [ihValues, List.getD_eq_getElem?_getD, List.getElem?_map, + List.getElem?_zipIdx, hf, Option.map_some, Nat.zero_add, Option.getD_some] + exact ihValue_mem h hM hΓ hc hf hx hg hih + +noncomputable def algebra (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (v : Nat) (_m : V) (minors : List V) (_i a g ih : V) : V := + match shape.constructors[tag a]? with + | none => empty + | some ctor => + let xs := ordinaryValues ctor a + let fs := shape.decodeFields constants levels env ctor xs g + let hs := shape.ihValues constants levels env v ctor xs ih + Telescope.applyN (minors.getD (tag a) empty) (xs ++ fs ++ hs) + +theorem algebra_typing (h : CheckedShape.{u,v} entries store source shape) + (hM : Realizes constants entries) (hΓ : shape.parameterContext.Valid constants levels env) + {v : Nat} {m : V} {minors : List V} (hm : m ∈ˢ shape.motiveSet constants levels env v) + (hminors : shape.MinorValuesFit constants levels env v m minors) : + (shape.container constants levels env).AlgebraTyping (shape.level.eval levels) + (shape.motive m) (shape.algebra constants levels env v m minors) := by + intro i hi a ha g hg ih hih + obtain ⟨has, hresult⟩ := mem_sep.mp ha + obtain ⟨j, ctor, xs, hc, hx, rfl⟩ := mem_allShapes has + rw [resultIndex_inj constants levels env hc (FitsS.length_eq hx)] at hresult + have hctor := h.constructors ctor (List.mem_of_getElem? hc) + have hfs := decodeFields_fit hc (FitsS.length_eq hx) hg + have hhs := ihValues_fit (v := v) h hM hΓ hc hx hg hih + have hstep := minor_apply_mem h hM hΓ hm hc (hminors.get hc) hx hfs hhs + rw [constructorValue, branches_decodeFields hc hctor hM hΓ hx hg, hresult] at hstep + simpa only [algebra, tag_inj, hc, ordinaryValues_inj _ (FitsS.length_eq hx)] using hstep + +noncomputable def recursorSet (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (v : Nat) (m : V) : V := + Telescope.piN v (Telescope.interpret constants levels env shape.indices) fun is => + piR v (app (shape.carrier constants levels env) (mkTower is)) (shape.motive m (mkTower is)) + +noncomputable def largeValue (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (v : Nat) (m : V) (minors : List V) + (hD : (shape.container constants levels env).WF (shape.level.eval levels)) + (hlarge : (shape.container constants levels env).LargeElim (shape.level.eval levels)) : V := + Telescope.curry v (Telescope.interpret constants levels env shape.indices) fun is => + lamR v (app (shape.carrier constants levels env) (mkTower is)) fun x => + IndexedContainer.foldAt hD hlarge (shape.algebra constants levels env v m minors) (mkTower is) x + +theorem largeValue_mem (h : CheckedShape.{u,v} entries store source shape) + (hM : Realizes constants entries) (hΓ : shape.parameterContext.Valid constants levels env) + {v : Nat} {m : V} {minors : List V} (hm : m ∈ˢ shape.motiveSet constants levels env v) + (hminors : shape.MinorValuesFit constants levels env v m minors) + (hD : (shape.container constants levels env).WF (shape.level.eval levels)) + (hlarge : (shape.container constants levels env).LargeElim (shape.level.eval levels)) : + shape.largeValue constants levels env v m minors hD hlarge ∈ˢ shape.recursorSet constants levels env v m := by + apply Telescope.curry_mem + intro is his + apply lamR_mem + intro x hx + exact IndexedContainer.foldAt_mem hD hlarge (algebra_typing h hM hΓ hm hminors) + (mkTower_mem (by decide : 1 ≠ 0) his) hx + +theorem largeValue_apply (h : CheckedShape.{u,v} entries store source shape) + (hM : Realizes constants entries) (hΓ : shape.parameterContext.Valid constants levels env) + {v : Nat} {m : V} {minors : List V} (hm : m ∈ˢ shape.motiveSet constants levels env v) + (hminors : shape.MinorValuesFit constants levels env v m minors) + (hD : (shape.container constants levels env).WF (shape.level.eval levels)) + (hlarge : (shape.container constants levels env).LargeElim (shape.level.eval levels)) + {is : List V} (his : FitsS (Telescope.interpret constants levels env shape.indices) is) + {x : V} (hx : x ∈ˢ app (shape.carrier constants levels env) (mkTower is)) : + Telescope.applyN (shape.largeValue constants levels env v m minors hD hlarge) (is ++ [x]) = + IndexedContainer.foldAt hD hlarge (shape.algebra constants levels env v m minors) (mkTower is) x := by + have hi := mkTower_mem (by decide : 1 ≠ 0) his + have hleaf ys hys y hy := IndexedContainer.foldAt_mem hD hlarge (algebra_typing h hM hΓ hm hminors) + (mkTower_mem (by decide : 1 ≠ 0) hys) (i := mkTower ys) (x := y) hy + have hlam ys hys := lamR_mem (v := v) (hleaf ys hys) + rw [Telescope.applyN_append] + change app (Telescope.applyN (Telescope.curry v _ _) is) x = _ + simp only [carrier] + rw [Telescope.applyN_curry (w := v) _ his hlam (fun hv ys hys => by + subst v + exact piR_zero_mem_univZero)] + exact app_lamR hx (hleaf is his) (fun hv y hy => by + simpa only [hv, univ_zero] using motive_mem hm hi hy) + +/-- Small elimination constructs the proof point by structural induction. +It applies equally to empty, singleton, and many-constructor Prop families. -/ +theorem pt_mem_smallRecursorSet (h : CheckedShape.{u,v} entries store source shape) + (hM : Realizes constants entries) (hΓ : shape.parameterContext.Valid constants levels env) + {m : V} {minors : List V} (hm : m ∈ˢ shape.motiveSet constants levels env 0) + (hminors : shape.MinorValuesFit constants levels env 0 m minors) : + (pt : V) ∈ˢ shape.recursorSet constants levels env 0 m := by + have helim := IndexedContainer.small_elim (container_wf h hM hΓ) + (fun i hi x hx => by simpa only [univ_zero] using motive_mem hm hi hx) + (algebra_typing h hM hΓ hm hminors) + have hvalue := Telescope.curry_mem (w := 0) + (Telescope.interpret constants levels env shape.indices) + (R := fun is => piR 0 (app (shape.carrier constants levels env) (mkTower is)) + (shape.motive m (mkTower is))) + (f := fun _ => (pt : V)) (fun is his => + pt_mem_piR_zero_of (fun x hx => helim (mkTower is) (mkTower_mem (by decide : 1 ≠ 0) his) x hx)) + rwa [Telescope.curry_point] at hvalue + +end Ix.Theory.Certified.Ordinary.Shape diff --git a/Ix/Theory/Certified/Ordinary/Family.lean b/Ix/Theory/Certified/Ordinary/Family.lean new file mode 100644 index 000000000..2af5d8999 --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/Family.lean @@ -0,0 +1,139 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.Computation +import Ix.Theory.Model.Extension + +namespace Ix.Theory.Certified.Ordinary + +open Model Model.SetTheory Model.SetTheory.Tower Model.SetModel + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +theorem eval_params {n : Nat} {levels : List Nat} (h : levels.length = n) : + (VLevel.params n).map (VLevel.eval levels) = levels := by + apply List.ext_getElem (by simp [h]) + intro i hi hi' + simp only [VLevel.params, List.getElem_map, List.getElem_range, VLevel.eval, + List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hi', Option.getD_some] + +theorem interp_parameterVars_skip (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (offset count : Nat) : + (parameterVars offset count).map (interp constants levels env) = + (parameterVars 0 count).map (interp constants levels (Valuation.skip offset 0 env)) := by + induction count <;> simp_all [parameterVars, interp, Valuation.skip] + +theorem interp_parameterVars_extend (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (xs : List V) : + (parameterVars 0 xs.length).map (interp constants levels (Telescope.extend env xs)) = xs := by + induction xs generalizing env with + | nil => rfl + | cons x xs ih => + simp only [List.length_cons, parameterVars, Nat.zero_add, List.map_cons, interp, + Telescope.extend, ih] + have he := Telescope.extend_beyond (Valuation.cons x env) xs 0 + simpa only [Nat.add_zero, Valuation.cons_zero] using congrArg (· :: xs) he + +theorem interp_parameterVars_middle (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (xs tail : List V) : + (parameterVars tail.length xs.length).map + (interp constants levels (Telescope.extend (Telescope.extend env xs) tail)) = xs := by + rw [interp_parameterVars_skip, Telescope.skip_extend, interp_parameterVars_extend] + +namespace Shape + +noncomputable def familyValue (shape : Shape β) (constants : Assignment β V) (levels : List Nat) : V := + Telescope.curry 1 (Telescope.interpret constants levels (fun _ => empty) shape.parameters) fun ps => + Telescope.curry 1 (Telescope.interpret constants levels (Telescope.extend (fun _ => empty) ps) shape.indices) + (fun is => app (shape.carrier constants levels (Telescope.extend (fun _ => empty) ps)) (mkTower is)) + +theorem type_typing {entries : Environment β} {store : Store β} {source : β} {shape : Shape β} + (h : CheckedShape.{u,v} entries store source shape) : + ∃ l, TypingClaim.{u,v} entries [] shape.type (.sort l) := by + obtain ⟨l, _, hl⟩ := (h.parameters.append h.indices).forallN (TypingClaim.sort shape.level) + exact ⟨l, hl⟩ + +theorem type_references {entries : Environment β} {store : Store β} {source : β} {shape : Shape β} + (h : CheckedShape.{u,v} entries store source shape) : shape.type.ReferencesIn entries := + AExpr.ReferencesIn.forallN h.references (by simp [AExpr.ReferencesIn, AExpr.references]) + +theorem familyValue_mem (shape : Shape β) (constants : Assignment β V) (levels : List Nat) : + shape.familyValue constants levels ∈ˢ interp constants levels (fun _ => empty) shape.type := by + simp only [type, AExpr.forallN_append, AExpr.interp_forallN, regime_never, interp] + apply Telescope.curry_mem + intro ps hps + apply Telescope.curry_mem + intro is his + exact (shape.container constants levels (Telescope.extend (fun _ => empty) ps)).carrier_fibre_mem + (shape.level.eval levels) (mkTower_mem (by decide : 1 ≠ 0) his) + +theorem familyValue_apply (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + {ps is : List V} + (hps : FitsS (Telescope.interpret constants levels (fun _ => empty) shape.parameters) ps) + (his : FitsS (Telescope.interpret constants levels (Telescope.extend (fun _ => empty) ps) shape.indices) is) : + Telescope.applyN (shape.familyValue constants levels) (ps ++ is) = + app (shape.carrier constants levels (Telescope.extend (fun _ => empty) ps)) (mkTower is) := by + have hresult xs ys hys := + (shape.container constants levels (Telescope.extend (fun _ => empty) xs)).carrier_fibre_mem + (shape.level.eval levels) (i := mkTower ys) (mkTower_mem (by decide : 1 ≠ 0) hys) + have hinner xs := Telescope.curry_mem (w := 1) + (Telescope.interpret constants levels (Telescope.extend (fun _ => empty) xs) shape.indices) (hresult xs) + simp only [familyValue, carrier] + rw [Telescope.applyN_append, + Telescope.applyN_curry (w := 1) _ hps (fun xs _ => hinner xs) (fun h => (by omega : False).elim)] + exact Telescope.applyN_curry _ his (hresult ps) (fun h => (by omega : False).elim) + +def familyEntry (shape : Shape β) : ConstantEntry β := ⟨shape.universes, shape.type, none, [], []⟩ + +def familyEnvironment [DecidableEq β] (shape : Shape β) (entries : Environment β) (source : β) : Environment β := + entries.insert (.member source 0) shape.familyEntry + +noncomputable def familyAssignment [DecidableEq β] (shape : Shape β) (constants : Assignment β V) + (source : β) : Assignment β V := constants.insert (.member source 0) (shape.familyValue constants) + +theorem familyEnvironment_wf [DecidableEq β] {entries : Environment β} {store : Store β} {source : β} + {shape : Shape β} (h : CheckedShape.{u,v} entries store source shape) (hE : entries.WF) : + (shape.familyEnvironment entries source).WF := + hE.insert h.scope (by simp [familyEntry]) (type_references h) (by simp [familyEntry]) + (by simp [familyEntry]) (by simp [familyEntry]) + (by simp [familyEntry]) (by simp [familyEntry]) + +theorem familyAssignment_agrees [DecidableEq β] {entries : Environment β} {store : Store β} {source : β} + {shape : Shape β} (h : CheckedShape.{u,v} entries store source shape) (constants : Assignment β V) : + Assignment.AgreesOn entries constants (shape.familyAssignment constants source) := + Assignment.insert_agrees (h.fresh _ (List.mem_cons_self ..)) constants (shape.familyValue constants) + +/-- The family signature receives its constructed carrier before constructor +or recursor checking. This is an internal stage with no reduction equations. -/ +theorem familyAssignment_realizes [DecidableEq β] {entries : Environment β} {store : Store β} {source : β} + {shape : Shape β} (h : CheckedShape.{u,v} entries store source shape) (hE : entries.WF) + (constants : Assignment β V) (hM : Realizes constants entries) : + Realizes (shape.familyAssignment constants source) (shape.familyEnvironment entries source) := by + have hagree := familyAssignment_agrees h constants + have hrefs := type_references h + obtain ⟨l, htype⟩ := type_typing h + apply (hM.of_agrees hE hagree).insert + constructor + · intro levels hn env + exact (hagree.wellDenoted hrefs levels env).mpr + (htype V constants hM levels env (Context.valid_nil constants levels env)).1 + · intro levels hn env + change shape.familyAssignment constants source (.member source 0) levels ∈ˢ + interp (shape.familyAssignment constants source) levels env shape.type + rw [hagree.interp hrefs levels env, familyAssignment, Assignment.insert_same] + have hm := shape.familyValue_mem constants levels + rwa [interp_closed shape.type constants levels h.scope (fun _ => empty) env] at hm + · intro body hb + cases hb + · intro body hb + cases hb + · intro law hl + simp only [familyEntry, List.not_mem_nil] at hl + · intro fact hf + simp only [familyEntry, List.not_mem_nil] at hf + +end Shape +end Ix.Theory.Certified.Ordinary diff --git a/Ix/Theory/Certified/Ordinary/LargeElim.lean b/Ix/Theory/Certified/Ordinary/LargeElim.lean new file mode 100644 index 000000000..976aa0736 --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/LargeElim.lean @@ -0,0 +1,65 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.Constructors +import Ix.Theory.Model.Inductive.Recursor + +namespace Ix.Theory.Certified.Ordinary + +open Model Model.SetTheory Model.SetTheory.Tower + +universe u v +variable {β : Type u} + +def LargeEvidence (entries : Environment β) (shape : Shape β) : Prop := + (∀ levels, shape.level.eval levels ≠ 0) ∨ shape.constructors = [] ∨ + ∃ ctor, shape.constructors = [ctor] ∧ + TelescopeProp.{u,v} entries shape.parameterContext ctor.fields shape.level + +def checkLarge [DecidableEq β] (fuel : Nat) (entries : Environment β) (witness : ShapeWitness β) : + Option (CheckedClaim.{u} (LargeEvidence.{u,v} entries witness.shape)) := + if hp : zeroCondition witness.shape.level = .never then + some ⟨Or.inl (by + intro levels he + have hz := zeroCondition_correct witness.shape.level levels + simp only [hp, PropWhen.holds_never, he, beq_self_eq_true, Bool.false_eq_true] at hz)⟩ + else + match hc : witness.shape.constructors, witness.constructors with + | [], [] => some ⟨Or.inr (Or.inl hc)⟩ + | [ctor], [proof] => do + let fields ← verifyPropTelescope.{u,v} fuel witness.shape.universes entries witness.shape.level + witness.shape.parameterContext ctor.fields proof.fields + return ⟨Or.inr (Or.inr ⟨ctor, hc, fields.down⟩)⟩ + | _, _ => none + +theorem checkLarge_sound [DecidableEq β] {fuel : Nat} {entries : Environment β} {witness : ShapeWitness β} + {result} (_ : checkLarge.{u,v} fuel entries witness = some result) : + LargeEvidence.{u,v} entries witness.shape := result.down + +variable {V : Type v} [SetTheory V] + +theorem Shape.container_large {entries : Environment β} {shape : Shape β} + (hlarge : LargeEvidence.{u,v} entries shape) {constants : Assignment β V} + (hM : Realizes constants entries) {levels : List Nat} {env : Nat → V} + (hΓ : shape.parameterContext.Valid constants levels env) : + (shape.container constants levels env).LargeElim (shape.level.eval levels) := by + intro hw i hi a b ha hb + rcases hlarge with hpos | hempty | ⟨ctor, hsingle, hprop⟩ + · exact (hpos levels hw).elim + · obtain ⟨j, ctor, xs, hc, _, _⟩ := Shape.mem_allShapes (mem_sep.mp ha).1 + simp only [hempty, List.getElem?_nil] at hc + contradiction + · obtain ⟨j, ca, xs, hca, hxs, rfl⟩ := Shape.mem_allShapes (mem_sep.mp ha).1 + obtain ⟨k, cb, ys, hcb, hys, rfl⟩ := Shape.mem_allShapes (mem_sep.mp hb).1 + have hj : j = 0 := by cases j <;> simp_all + have hk : k = 0 := by cases k <;> simp_all + subst j k + have heca : ctor = ca := by simpa only [hsingle, List.getElem?_cons_zero, Option.some.injEq] using hca + have hecb : ctor = cb := by simpa only [hsingle, List.getElem?_cons_zero, Option.some.injEq] using hcb + subst ca cb + have he := Telescope.fits_unique_of_prop (hprop V constants hM levels env hΓ hw) hxs hys + rw [he] + +end Ix.Theory.Certified.Ordinary diff --git a/Ix/Theory/Certified/Ordinary/Reading.lean b/Ix/Theory/Certified/Ordinary/Reading.lean new file mode 100644 index 000000000..86a5cb955 --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/Reading.lean @@ -0,0 +1,309 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.Family + +namespace Ix.Theory.Model + +open SetTheory SetTheory.Tower + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +theorem Assignment.AgreesOn.telescope {entries : Environment β} {constants constants' : Assignment β V} + (h : Assignment.AgreesOn entries constants constants') {domains : List (AExpr β)} + (hrefs : ∀ A ∈ domains, A.ReferencesIn entries) (levels : List Nat) (env : Nat → V) : + Telescope.interpret constants' levels env domains = Telescope.interpret constants levels env domains := by + induction domains generalizing env with + | nil => rfl + | cons A rest ih => + simp only [Telescope.interpret, h.interp (hrefs A (List.mem_cons_self ..))] + congr 1 + funext x + exact ih (fun D hd => hrefs D (List.mem_cons_of_mem A hd)) _ + +end Ix.Theory.Model + +namespace Ix.Theory.Certified.Ordinary + +open Model Model.SetTheory Model.SetTheory.Tower Model.SetModel + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +/-- Interface preserved while signatures are staged. The initial producer +installs the constructed family; subsequent fresh members preserve it. -/ +structure FamilyReading (entries : Environment β) (shape : Shape β) (source : β) + (constants reading : Assignment β V) : Prop where + agrees : Assignment.AgreesOn entries constants reading + family : ∀ levels, levels.length = shape.universes → + reading (.member source 0) levels = shape.familyValue constants levels + +theorem Shape.familyAssignment_reading [DecidableEq β] {entries : Environment β} {store : Store β} + {source : β} {shape : Shape β} (h : CheckedShape.{u,v} entries store source shape) + (constants : Assignment β V) : + FamilyReading entries shape source constants (shape.familyAssignment constants source) := + ⟨Shape.familyAssignment_agrees h constants, fun _ _ => Assignment.insert_same ..⟩ + +namespace Shape + +variable {entries : Environment β} {shape : Shape β} {source : β} + {constants reading : Assignment β V} {levels : List Nat} + +theorem familyApp_interp (h : FamilyReading entries shape source constants reading) + (hn : levels.length = shape.universes) {ps extra : List V} {indices : List (AExpr β)} + (hps : FitsS (Telescope.interpret constants levels (fun _ => empty) shape.parameters) ps) + (his : FitsS (Telescope.interpret constants levels (Telescope.extend (fun _ => empty) ps) shape.indices) + (indices.map (interp reading levels (Telescope.extend (Telescope.extend (fun _ => empty) ps) extra)))) : + interp reading levels (Telescope.extend (Telescope.extend (fun _ => empty) ps) extra) + (shape.familyApp source extra.length indices) = + app (shape.carrier constants levels (Telescope.extend (fun _ => empty) ps)) + (mkTower (indices.map (interp reading levels (Telescope.extend (Telescope.extend (fun _ => empty) ps) extra)))) := by + have hp : (parameterVars extra.length shape.parameters.length).map + (interp reading levels (Telescope.extend (Telescope.extend (fun _ => empty) ps) extra)) = ps := by + rw [interp_parameterVars_skip, Telescope.skip_extend, ← FitsS.length_eq hps, + interp_parameterVars_extend] + rw [familyApp, AExpr.interp_appN] + simp only [interp, eval_params hn, h.family levels hn, List.map_append, hp] + exact familyValue_apply shape constants levels hps his + +theorem recursiveType_interp {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : FamilyReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = shape.universes) {ctor : Constructor β} {field : RecursiveField β} + (hc : ctor ∈ shape.constructors) (hf : field ∈ ctor.recursive) {ps xs previous : List V} + (hps : FitsS (Telescope.interpret constants levels (fun _ => empty) shape.parameters) ps) + (hxs : FitsS (Telescope.interpret constants levels (Telescope.extend (fun _ => empty) ps) ctor.fields) xs) : + interp reading levels (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs) previous) + ((field.type shape source ctor.fields.length).liftN previous.length) = + shape.recursiveSet constants levels (Telescope.extend (fun _ => empty) ps) xs field := by + have hctor := h.constructors ctor hc + have hfield := hctor.2.2.2.2 field hf + have hΓ := h.parameters.valid constants hM levels (Context.valid_nil constants levels (fun _ => empty)) hps + rw [interp_liftN, Telescope.skip_extend, RecursiveField.type, AExpr.interp_forallN, + hr.agrees.telescope (fun A ha => hfield.1 A (List.mem_append_left _ ha))] + apply Telescope.piN_zero_agree (regime_zeroCondition shape.level levels) + intro ys hys + have he : field.indices.map (interp reading levels + (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs) ys)) = + field.indices.map (interp constants levels + (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs) ys)) := + List.map_congr_left fun e he => hr.agrees.interp (hfield.1 e (List.mem_append_right _ he)) levels _ + have hit := recursiveTarget_fits hctor hfield hM hΓ hxs hys + have hit' : FitsS (Telescope.interpret constants levels (Telescope.extend (fun _ => empty) ps) shape.indices) + (field.indices.map (interp reading levels (Telescope.extend (Telescope.extend (fun _ => empty) ps) (xs ++ ys)))) := by + simpa only [Telescope.extend_append, he] using hit + have happ := familyApp_interp hr hn hps hit' + simpa only [List.length_append, FitsS.length_eq hxs, FitsS.length_eq hys, + Telescope.extend_append, he] using happ + +theorem recursiveForall_interp {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : FamilyReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = shape.universes) {ctor : Constructor β} (hc : ctor ∈ shape.constructors) + {fields : List (RecursiveField β)} (hf : ∀ field ∈ fields, field ∈ ctor.recursive) {ps xs : List V} + (hps : FitsS (Telescope.interpret constants levels (fun _ => empty) shape.parameters) ps) + (hxs : FitsS (Telescope.interpret constants levels (Telescope.extend (fun _ => empty) ps) ctor.fields) xs) + (previous : List V) (B : AExpr β) : + interp reading levels (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs) previous) + (.forallN (zeroCondition shape.level) + (recursiveTypesFrom shape source ctor.fields.length fields previous.length) B) = + Telescope.piN (shape.level.eval levels) + (Telescope.simple (fields.map (shape.recursiveSet constants levels (Telescope.extend (fun _ => empty) ps) xs))) + (fun fs => interp reading levels + (Telescope.extend (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs) previous) fs) B) := by + induction fields generalizing previous with + | nil => rfl + | cons field fields ih => + simp only [recursiveTypesFrom, List.zipIdx_cons, List.map_cons, AExpr.forallN, interp] + rw [recursiveType_interp h hr hM hn hc (hf field (List.mem_cons_self ..)) hps hxs] + apply piR_zero_agree (regime_zeroCondition shape.level levels) + intro x _ + have htail := ih (fun f hm => hf f (List.mem_cons_of_mem field hm)) (previous ++ [x]) + simpa only [recursiveTypesFrom, List.length_append, List.length_singleton, + Telescope.extend_append, Telescope.extend] using htail + +theorem piN_recursiveTypes {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : FamilyReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = shape.universes) {ctor : Constructor β} (hc : ctor ∈ shape.constructors) + {fields : List (RecursiveField β)} (hf : ∀ field ∈ fields, field ∈ ctor.recursive) {ps xs : List V} + (hps : FitsS (Telescope.interpret constants levels (fun _ => empty) shape.parameters) ps) + (hxs : FitsS (Telescope.interpret constants levels (Telescope.extend (fun _ => empty) ps) ctor.fields) xs) + (previous : List V) (w : Nat) (R : List V → V) : + Telescope.piN w (Telescope.interpret reading levels + (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs) previous) + (recursiveTypesFrom shape source ctor.fields.length fields previous.length)) R = + Telescope.piN w + (Telescope.simple (fields.map (shape.recursiveSet constants levels (Telescope.extend (fun _ => empty) ps) xs))) R := by + induction fields generalizing previous R with + | nil => rfl + | cons field fields ih => + change piR w (interp reading levels _ ((field.type shape source ctor.fields.length).liftN previous.length)) _ = + piR w (shape.recursiveSet constants levels (Telescope.extend (fun _ => empty) ps) xs field) _ + rw [recursiveType_interp h hr hM hn hc (hf field (List.mem_cons_self ..)) hps hxs] + congr 1 + funext f + have ht := ih (fun f hf' => hf f (List.mem_cons_of_mem field hf')) (previous ++ [f]) (fun fs => R (f :: fs)) + have hlen : (previous ++ [f]).length = previous.length + 1 := by simp + rw [hlen] at ht + simpa only [recursiveTypesFrom, List.length_append, List.length_singleton, + Telescope.extend_append, Telescope.extend] using ht + +theorem curry_recursiveTypes {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : FamilyReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = shape.universes) {ctor : Constructor β} (hc : ctor ∈ shape.constructors) + {fields : List (RecursiveField β)} (hf : ∀ field ∈ fields, field ∈ ctor.recursive) {ps xs : List V} + (hps : FitsS (Telescope.interpret constants levels (fun _ => empty) shape.parameters) ps) + (hxs : FitsS (Telescope.interpret constants levels (Telescope.extend (fun _ => empty) ps) ctor.fields) xs) + (previous : List V) (w : Nat) (f : List V → V) : + Telescope.curry w (Telescope.interpret reading levels + (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs) previous) + (recursiveTypesFrom shape source ctor.fields.length fields previous.length)) f = + Telescope.curry w + (Telescope.simple (fields.map (shape.recursiveSet constants levels (Telescope.extend (fun _ => empty) ps) xs))) f := by + induction fields generalizing previous f with + | nil => rfl + | cons field fields ih => + change lamR w (interp reading levels _ ((field.type shape source ctor.fields.length).liftN previous.length)) _ = + lamR w (shape.recursiveSet constants levels (Telescope.extend (fun _ => empty) ps) xs field) _ + rw [recursiveType_interp h hr hM hn hc (hf field (List.mem_cons_self ..)) hps hxs] + congr 1 + funext x + have ht := ih (fun f hf' => hf f (List.mem_cons_of_mem field hf')) (previous ++ [x]) (fun xs => f (x :: xs)) + have hlen : (previous ++ [x]).length = previous.length + 1 := by simp + rw [hlen] at ht + simpa only [recursiveTypesFrom, List.length_append, List.length_singleton, + Telescope.extend_append, Telescope.extend] using ht + +theorem constructorResult_interp {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : FamilyReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = shape.universes) {ctor : Constructor β} (hc : ctor ∈ shape.constructors) + {ps xs fs : List V} + (hps : FitsS (Telescope.interpret constants levels (fun _ => empty) shape.parameters) ps) + (hxs : FitsS (Telescope.interpret constants levels (Telescope.extend (fun _ => empty) ps) ctor.fields) xs) + (hlen : fs.length = ctor.recursive.length) : + interp reading levels (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs) fs) + (shape.familyApp source (ctor.fields.length + ctor.recursive.length) + (ctor.indices.map (AExpr.liftN ctor.recursive.length ·))) = + app (shape.carrier constants levels (Telescope.extend (fun _ => empty) ps)) + (mkTower (ctor.indices.map (interp constants levels (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs)))) := by + have hctor := h.constructors ctor hc + have hΓ := h.parameters.valid constants hM levels (Context.valid_nil constants levels (fun _ => empty)) hps + have he : (ctor.indices.map (AExpr.liftN ctor.recursive.length ·)).map + (interp reading levels (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs) fs)) = + ctor.indices.map (interp constants levels (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs)) := by + rw [List.map_map] + apply List.map_congr_left + intro e he + simp only [Function.comp_def, interp_liftN, ← hlen, Telescope.skip_extend, + hr.agrees.interp (hctor.1 e (List.mem_append_right _ he))] + have hit := constructorResult_fits hctor hM hΓ hxs + have hit' : FitsS (Telescope.interpret constants levels (Telescope.extend (fun _ => empty) ps) shape.indices) + ((ctor.indices.map (AExpr.liftN ctor.recursive.length ·)).map + (interp reading levels (Telescope.extend (Telescope.extend (fun _ => empty) ps) (xs ++ fs)))) := by + simpa only [Telescope.extend_append, he] using hit + have happ := familyApp_interp hr hn hps hit' + simpa only [List.length_append, FitsS.length_eq hxs, hlen, Telescope.extend_append, he] using happ + +noncomputable def constructorSet (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (ctor : Constructor β) : V := + Telescope.piN (shape.level.eval levels) + (Telescope.interpret constants levels (fun _ => empty) shape.parameters) fun ps => + Telescope.piN (shape.level.eval levels) + (Telescope.interpret constants levels (Telescope.extend (fun _ => empty) ps) ctor.fields) fun xs => + Telescope.piN (shape.level.eval levels) + (Telescope.simple (ctor.recursive.map (shape.recursiveSet constants levels (Telescope.extend (fun _ => empty) ps) xs))) + (fun _ => app (shape.carrier constants levels (Telescope.extend (fun _ => empty) ps)) + (mkTower (ctor.indices.map (interp constants levels (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs))))) + +theorem constructorType_interp {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : FamilyReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = shape.universes) {ctor : Constructor β} (hc : ctor ∈ shape.constructors) : + interp reading levels (fun _ => empty) (ctor.type shape source) = shape.constructorSet constants levels ctor := by + have hctor := h.constructors ctor hc + rw [Constructor.type, AExpr.interp_forallN, + hr.agrees.telescope (fun A hA => h.references A (List.mem_append_left _ hA))] + apply Telescope.piN_zero_agree (regime_zeroCondition shape.level levels) + intro ps hps + rw [AExpr.interp_forallN, + hr.agrees.telescope (fun A hA => hctor.1 A (List.mem_append_left _ hA))] + apply Telescope.piN_zero_agree (regime_zeroCondition shape.level levels) + intro xs hxs + rw [Constructor.recursiveTypes] + have ht := recursiveForall_interp h hr hM hn hc (fun _ h => h) hps hxs [] + (shape.familyApp source (ctor.fields.length + ctor.recursive.length) + (ctor.indices.map (AExpr.liftN ctor.recursive.length ·))) + simp only [Telescope.extend, List.length_nil] at ht + rw [ht] + apply Telescope.piN_congr + intro fs hfs + exact constructorResult_interp h hr hM hn hc hps hxs (by simpa using FitsS.length_eq hfs) + +noncomputable def constructorClosedValue (shape : Shape β) (constants : Assignment β V) + (levels : List Nat) (i : Nat) (ctor : Constructor β) : V := + Telescope.curry (shape.level.eval levels) + (Telescope.interpret constants levels (fun _ => empty) shape.parameters) fun ps => + Telescope.curry (shape.level.eval levels) + (Telescope.interpret constants levels (Telescope.extend (fun _ => empty) ps) ctor.fields) fun xs => + Telescope.curry (shape.level.eval levels) + (Telescope.simple (ctor.recursive.map (shape.recursiveSet constants levels (Telescope.extend (fun _ => empty) ps) xs))) + (fun fs => shape.constructorValue constants levels (Telescope.extend (fun _ => empty) ps) ctor i xs fs) + +theorem constructorClosedValue_mem {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hM : Realizes constants entries) {i : Nat} {ctor : Constructor β} (hc : shape.constructors[i]? = some ctor) : + shape.constructorClosedValue constants levels i ctor ∈ˢ shape.constructorSet constants levels ctor := by + apply Telescope.curry_mem + intro ps hps + apply Telescope.curry_mem + intro xs hxs + apply Telescope.curry_mem + intro fs hfs + have hΓ := h.parameters.valid constants hM levels (Context.valid_nil constants levels (fun _ => empty)) hps + exact constructorValue_mem h hM hΓ hc hxs hfs + +theorem constructorClosedValue_mem_source {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : FamilyReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = shape.universes) {i : Nat} {ctor : Constructor β} + (hc : shape.constructors[i]? = some ctor) : + shape.constructorClosedValue constants levels i ctor ∈ˢ interp reading levels (fun _ => empty) (ctor.type shape source) := by + rw [constructorType_interp h hr hM hn (List.mem_of_getElem? hc)] + exact constructorClosedValue_mem h hM hc + +theorem constructorClosedValue_apply {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hM : Realizes constants entries) {i : Nat} {ctor : Constructor β} + (hc : shape.constructors[i]? = some ctor) {ps xs fs : List V} + (hps : FitsS (Telescope.interpret constants levels (fun _ => empty) shape.parameters) ps) + (hxs : FitsS (Telescope.interpret constants levels (Telescope.extend (fun _ => empty) ps) ctor.fields) xs) + (hfs : shape.RecursiveValuesFit constants levels (Telescope.extend (fun _ => empty) ps) ctor xs fs) : + Telescope.applyN (shape.constructorClosedValue constants levels i ctor) (ps ++ xs ++ fs) = + shape.constructorValue constants levels (Telescope.extend (fun _ => empty) ps) ctor i xs fs := by + have hresult (ps : List V) hps xs hxs fs hfs := constructorValue_mem h hM + (h.parameters.valid constants hM levels (xs := ps) (Context.valid_nil constants levels (fun _ => empty)) hps) + hc (xs := xs) hxs (fs := fs) hfs + have hbound (ps : List V) hps xs hxs : + app (shape.carrier constants levels (Telescope.extend (fun _ => empty) ps)) + (mkTower (ctor.indices.map (interp constants levels (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs)))) + ∈ˢ (univ (shape.level.eval levels) : V) := + (shape.container constants levels (Telescope.extend (fun _ => empty) ps)).carrier_fibre_mem _ + (constructorResult_mem (h.constructors ctor (List.mem_of_getElem? hc)) hM + (h.parameters.valid constants hM levels (xs := ps) (Context.valid_nil constants levels (fun _ => empty)) hps) hxs) + have hrec ps hps xs hxs := Telescope.curry_mem (w := shape.level.eval levels) _ (hresult ps hps xs hxs) + have hfields ps hps := Telescope.curry_mem (w := shape.level.eval levels) _ (hrec ps hps) + rw [constructorClosedValue, Telescope.applyN_append, Telescope.applyN_append, + Telescope.applyN_curry _ hps hfields (fun hw ps hps => by + rw [hw] + apply Telescope.piN_zero_mem + intro xs hxs + apply Telescope.piN_zero_mem + intro _ _ + simpa only [hw, univ_zero] using hbound ps hps xs hxs), + Telescope.applyN_curry _ hxs (hrec ps hps) (fun hw xs hxs => by + rw [hw] + apply Telescope.piN_zero_mem + intro _ _ + simpa only [hw, univ_zero] using hbound ps hps xs hxs)] + exact Telescope.applyN_curry _ hfs (hresult ps hps xs hxs) + (fun hw _ _ => by simpa only [hw, univ_zero] using hbound ps hps xs hxs) + +end Shape +end Ix.Theory.Certified.Ordinary diff --git a/Ix/Theory/Certified/Ordinary/RecursorReading.lean b/Ix/Theory/Certified/Ordinary/RecursorReading.lean new file mode 100644 index 000000000..65ba51332 --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/RecursorReading.lean @@ -0,0 +1,415 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.RecursorSyntax + +namespace Ix.Theory.Inductive.ElimMode + +theorem sourceLevels_eval (mode : ElimMode) {levels : List Nat} {sourceUvars : Nat} + (h : levels.length = mode.recUvars sourceUvars) : + (mode.sourceLevels sourceUvars).map (VLevel.eval levels) = mode.sourceArgs levels := by + apply List.ext_get (by simp [sourceLevels, sourceArgs_length mode h]) + intro i hi _ + have hi' : i < sourceUvars := by simpa [sourceLevels] using hi + have bound : i + mode.offset < levels.length := by rw [h, recUvars]; omega + simp [sourceLevels, VLevel.params', VLevel.eval, sourceArgs, + List.getD_eq_getElem?_getD, List.getElem?_eq_getElem bound, Nat.add_comm] + +end Ix.Theory.Inductive.ElimMode + +namespace Ix.Theory.Certified.Ordinary + +open Model Model.SetTheory Model.SetTheory.Tower Model.SetModel Inductive + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +structure ConstructorReading (entries : Environment β) (shape : Shape β) (source : β) + (constants reading : Assignment β V) : Prop extends FamilyReading entries shape source constants reading where + constructor : ∀ levels, levels.length = shape.universes → ∀ i ctor, + shape.constructors[i]? = some ctor → + reading (.ctor source 0 i) levels = shape.constructorClosedValue constants levels i ctor + +namespace Shape + +variable {entries : Environment β} {shape : Shape β} {source : β} + {constants reading : Assignment β V} {levels : List Nat} {mode : ElimMode} + +theorem motiveType_interp (hr : FamilyReading entries shape source constants reading) + (hn : levels.length = mode.recUvars shape.universes) {ps : List V} + (hps : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (fun _ => empty) shape.parameters) ps) + (hrefs : ∀ A ∈ shape.indices, A.ReferencesIn entries) : + interp reading levels (Telescope.extend (fun _ => empty) ps) (shape.motiveType source mode) = + shape.motiveSet constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (mode.motiveLevel.eval levels) := by + rw [motiveType, AExpr.interp_forallN, Telescope.piN_instL, mode.sourceLevels_eval hn, + hr.agrees.telescope hrefs] + apply Telescope.piN_congr + intro is his + have hvars := interp_parameterVars_extend reading (mode.sourceArgs levels) + (Telescope.extend (fun _ => empty) ps) is + rw [FitsS.length_eq his] at hvars + have hit : FitsS (Telescope.interpret constants (mode.sourceArgs levels) + (Telescope.extend (fun _ => empty) ps) shape.indices) + ((parameterVars 0 shape.indices.length).map + (interp reading (mode.sourceArgs levels) + (Telescope.extend (Telescope.extend (fun _ => empty) ps) is))) := by + simpa only [hvars] using his + have happ := familyApp_interp hr (mode.sourceArgs_length hn) hps hit + simp only [interp, regime_never, interp_instL, mode.sourceLevels_eval hn, + FitsS.length_eq his, hvars] at happ ⊢ + rw [happ] + +theorem minorFields_piN {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : FamilyReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = mode.recUvars shape.universes) {ctor : Constructor β} (hc : ctor ∈ shape.constructors) + {ps : List V} + (hps : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (fun _ => empty) shape.parameters) ps) + (m : V) (w : Nat) (R : List V → V) : + Telescope.piN w (Telescope.interpret reading levels + (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) (shape.minorFields source mode ctor)) R = + Telescope.piN w + (Telescope.interpret constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor.fields) + (fun xs => Telescope.piN w + (Telescope.simple (ctor.recursive.map + (shape.recursiveSet constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) xs))) + (fun fs => R (xs ++ fs))) := by + rw [minorFields, Telescope.piN_lift, Valuation.skip_succ_cons, Valuation.skip_zero, + Telescope.piN_instL, mode.sourceLevels_eval hn, Telescope.piN_append, + hr.agrees.telescope (fun A ha => (h.constructors ctor hc).1 A (List.mem_append_left _ ha))] + apply Telescope.piN_congr + intro xs hxs + have ht := piN_recursiveTypes h hr hM (mode.sourceArgs_length hn) hc (fun _ h => h) hps hxs [] w + (fun fs => R (xs ++ fs)) + simpa only [Constructor.recursiveTypes, List.length_nil, Telescope.extend] using ht + +theorem ihType_interp {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : FamilyReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = mode.recUvars shape.universes) {ctor : Constructor β} (hc : ctor ∈ shape.constructors) + {j : Nat} {field : RecursiveField β} (hf : ctor.recursive[j]? = some field) + {ps xs fs : List V} + (hps : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (fun _ => empty) shape.parameters) ps) + (hxs : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor.fields) xs) + (hfs : fs.length = ctor.recursive.length) (m : V) : + interp reading levels + (Telescope.extend (Telescope.extend (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) xs) fs) + (shape.ihType mode ctor j field) = + shape.ihSet constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (mode.motiveLevel.eval levels) m xs fs j field := by + have hctor := h.constructors ctor hc + have hfield := hctor.2.2.2.2 field (List.mem_of_getElem? hf) + have hΓ := h.parameters.valid constants hM (mode.sourceArgs levels) + (Context.valid_nil constants (mode.sourceArgs levels) (fun _ => empty)) hps + have hxlen := FitsS.length_eq hxs + have hskip : Valuation.skip 1 ctor.fields.length + (Telescope.extend (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) xs) = + Telescope.extend (Telescope.extend (fun _ => empty) ps) xs := by + simpa only [List.length_singleton, Telescope.extend, hxlen] using + Telescope.skip_middle (Telescope.extend (fun _ => empty) ps) [m] xs + rw [ihType, AExpr.interp_forallN, Telescope.piN_lift, ← hfs, Telescope.skip_extend, + Telescope.piN_lift, hskip, Telescope.piN_instL, mode.sourceLevels_eval hn, + hr.agrees.telescope (fun A ha => hfield.1 A (List.mem_append_left _ ha))] + apply Telescope.piN_zero_agree (regime_zeroCondition mode.motiveLevel levels) + intro ys hys + have hylen := FitsS.length_eq hys + have hit := recursiveTarget_fits hctor hfield hM hΓ hxs hys + have hidx : field.indices.map (fun e => interp reading levels + (Telescope.extend (Telescope.extend (Telescope.extend + (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) xs) fs) ys) + (((e.instL (mode.sourceLevels shape.universes)).liftN 1 + (ctor.fields.length + field.domains.length)).liftN ctor.recursive.length field.domains.length)) = + field.indices.map (interp constants (mode.sourceArgs levels) + (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs) ys)) := by + apply List.map_congr_left + intro e he + rw [interp_liftN, ← hfs, ← hylen, Telescope.skip_middle, interp_liftN] + rw [show ctor.fields.length + ys.length = ys.length + ctor.fields.length by omega, + Telescope.skip_extend_at, hskip, interp_instL, mode.sourceLevels_eval hn, + hr.agrees.interp (hfield.1 e (List.mem_append_right _ he))] + have hmvar : Telescope.extend (Telescope.extend (Telescope.extend + (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) xs) fs) ys + (ctor.fields.length + ctor.recursive.length + field.domains.length) = m := by + rw [← hxlen, ← hfs, ← hylen] + have ht := Telescope.extend_beyond (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) (xs ++ fs ++ ys) 0 + simpa only [List.length_append, Nat.add_zero, Telescope.extend_append, Valuation.cons_zero] using ht + have hfvar : Telescope.extend (Telescope.extend (Telescope.extend + (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) xs) fs) ys + (ctor.recursive.length - 1 - j + field.domains.length) = fs.getD j empty := by + rw [← hfs, ← hylen, Nat.add_comm, Telescope.extend_beyond, + Telescope.extend_getD _ _ j (by + rw [hfs] + exact (List.getElem?_eq_some_iff.mp hf).1)] + have hvars := interp_parameterVars_extend reading levels + (Telescope.extend (Telescope.extend (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) xs) fs) ys + rw [hylen] at hvars + simp only [hfs, AExpr.interp_appN, interp, List.map_append, List.map_map, Function.comp_def, + List.map_cons, List.map_nil, Telescope.applyN_append, Telescope.applyN, hmvar, hfvar, hvars, hidx] + rw [motive, projList_mkTower _ _ (FitsS.length_eq hit)] + +theorem ihTypes_piN {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : FamilyReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = mode.recUvars shape.universes) {ctor : Constructor β} (hc : ctor ∈ shape.constructors) + {ps xs fs : List V} + (hps : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (fun _ => empty) shape.parameters) ps) + (hxs : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor.fields) xs) + (hfs : fs.length = ctor.recursive.length) (m : V) (w : Nat) (R : List V → V) : + Telescope.piN w (Telescope.interpret reading levels + (Telescope.extend (Telescope.extend (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) xs) fs) + (shape.ihTypesSyntax mode ctor)) R = + Telescope.piN w (Telescope.simple (shape.ihTypes constants (mode.sourceArgs levels) + (Telescope.extend (fun _ => empty) ps) (mode.motiveLevel.eval levels) m ctor xs fs)) R := by + have ht := Telescope.piN_independent reading levels + (Telescope.extend (Telescope.extend (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) xs) fs) + (ctor.recursive.zipIdx.map fun (field, j) => shape.ihType mode ctor j field) [] w R + simp only [List.length_nil, Telescope.extend] at ht + rw [ihTypesSyntax, ht] + have he : ((ctor.recursive.zipIdx.map fun (field, j) => shape.ihType mode ctor j field).map + (interp reading levels (Telescope.extend + (Telescope.extend (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) xs) fs))) = + shape.ihTypes constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (mode.motiveLevel.eval levels) m ctor xs fs := by + apply List.ext_getElem? + intro j + simp only [ihTypes, List.getElem?_map, List.getElem?_zipIdx] + cases hf : ctor.recursive[j]? with + | none => rfl + | some field => + simp only [Option.map_some, Nat.zero_add] + exact congrArg some (ihType_interp h hr hM hn hc hf hps hxs hfs m) + rw [he] + +theorem constructorVars_interp (hr : ConstructorReading entries shape source constants reading) + (hn : levels.length = mode.recUvars shape.universes) {i : Nat} {ctor : Constructor β} + (hc : shape.constructors[i]? = some ctor) (ps extra fields tail : List V) + (hps : ps.length = shape.parameters.length) : + interp reading levels + (Telescope.extend (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) extra) fields) tail) + (.appN (.const (.ctor source 0 i) (mode.sourceLevels shape.universes)) + (parameterVars (extra.length + fields.length + tail.length) shape.parameters.length ++ + parameterVars tail.length fields.length)) = + Telescope.applyN (shape.constructorClosedValue constants (mode.sourceArgs levels) i ctor) (ps ++ fields) := by + have hp := interp_parameterVars_middle reading levels (fun _ => empty) ps (extra ++ fields ++ tail) + simp only [List.length_append, hps, Telescope.extend_append] at hp + have hf := interp_parameterVars_middle reading levels + (Telescope.extend (Telescope.extend (fun _ => empty) ps) extra) fields tail + simp only [AExpr.interp_appN, interp, mode.sourceLevels_eval hn, + hr.constructor _ (mode.sourceArgs_length hn) i ctor hc, List.map_append, hp, hf] + +theorem minorResult_interp {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : ConstructorReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = mode.recUvars shape.universes) {i : Nat} {ctor : Constructor β} + (hc : shape.constructors[i]? = some ctor) {ps xs fs hs : List V} + (hps : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (fun _ => empty) shape.parameters) ps) + (hxs : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor.fields) xs) + (hfs : shape.RecursiveValuesFit constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor xs fs) + (hhs : hs.length = ctor.recursive.length) (m : V) : + interp reading levels + (Telescope.extend (Telescope.extend (Telescope.extend + (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) xs) fs) hs) + (shape.minorResult source mode i ctor) = + shape.motive m + (mkTower (ctor.indices.map (interp constants (mode.sourceArgs levels) + (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs)))) + (shape.constructorValue constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor i xs fs) := by + have hxlen := FitsS.length_eq hxs + have hflen : fs.length = ctor.recursive.length := by simpa using FitsS.length_eq hfs + have hctor := h.constructors ctor (List.mem_of_getElem? hc) + have hΓ := h.parameters.valid constants hM (mode.sourceArgs levels) + (Context.valid_nil constants (mode.sourceArgs levels) (fun _ => empty)) hps + have hit := constructorResult_fits hctor hM hΓ hxs + have hcv := constructorVars_interp hr hn hc ps [m] (xs ++ fs) hs (FitsS.length_eq hps) + rw [← List.append_assoc, constructorClosedValue_apply h hM hc hps hxs hfs] at hcv + simp only [List.length_singleton, List.length_append, hxlen, hflen, hhs, + Telescope.extend_append, Telescope.extend, Nat.add_assoc] at hcv + have hidx : ctor.indices.map (fun e => interp reading levels + (Telescope.extend (Telescope.extend (Telescope.extend + (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) xs) fs) hs) + (((e.instL (mode.sourceLevels shape.universes)).liftN 1 ctor.fields.length).liftN + (ctor.recursive.length + ctor.recursive.length))) = + ctor.indices.map (interp constants (mode.sourceArgs levels) + (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs)) := by + apply List.map_congr_left + intro e he + rw [interp_liftN, show ctor.recursive.length + ctor.recursive.length = (fs ++ hs).length by simp [hflen, hhs], + ← Telescope.extend_append, Telescope.skip_extend, interp_liftN, ← hxlen] + have ht := Telescope.skip_middle (Telescope.extend (fun _ => empty) ps) [m] xs + simp only [List.length_singleton, Telescope.extend] at ht + rw [ht, interp_instL, mode.sourceLevels_eval hn, + hr.agrees.interp (hctor.1 e (List.mem_append_right _ he))] + have hmvar : Telescope.extend (Telescope.extend (Telescope.extend + (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) xs) fs) hs + (ctor.fields.length + ctor.recursive.length + ctor.recursive.length) = m := by + have ht := Telescope.extend_beyond (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) (xs ++ fs ++ hs) 0 + simpa only [List.length_append, Nat.add_zero, Telescope.extend_append, Valuation.cons_zero, + hxlen, hflen, hhs] using ht + simp only [Nat.add_assoc] at hmvar + rw [minorResult, AExpr.interp_appN] + simp only [interp, hmvar, List.map_append, List.map_map, Function.comp_def, hidx, + List.map_cons, List.map_nil, Telescope.applyN_append, Telescope.applyN, Nat.add_assoc, hcv] + rw [motive, projList_mkTower _ _ (FitsS.length_eq hit)] + +theorem minorType_interp {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : ConstructorReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = mode.recUvars shape.universes) {i : Nat} {ctor : Constructor β} + (hc : shape.constructors[i]? = some ctor) {ps : List V} + (hps : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (fun _ => empty) shape.parameters) ps) + (m : V) : + interp reading levels (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) + (shape.minorType source mode i ctor) = + shape.minorSet constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (mode.motiveLevel.eval levels) m i ctor := by + rw [minorType, AExpr.interp_forallN, minorFields_piN h hr.toFamilyReading hM hn + (List.mem_of_getElem? hc) hps] + apply Telescope.piN_zero_agree (regime_zeroCondition mode.motiveLevel levels) + intro xs hxs + apply Telescope.piN_zero_agree (regime_zeroCondition mode.motiveLevel levels) + intro fs hfs + have hflen : fs.length = ctor.recursive.length := by simpa using FitsS.length_eq hfs + rw [Telescope.extend_append, AExpr.interp_forallN, + ihTypes_piN h hr.toFamilyReading hM hn (List.mem_of_getElem? hc) hps hxs hflen] + apply Telescope.piN_zero_agree (regime_zeroCondition mode.motiveLevel levels) + intro hs hhs + exact minorResult_interp h hr hM hn hc hps hxs hfs + (by simpa only [ihTypes, List.length_map, List.length_zipIdx] using FitsS.length_eq hhs) m + +theorem minorTypes_piN {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : ConstructorReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = mode.recUvars shape.universes) {ps : List V} + (hps : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (fun _ => empty) shape.parameters) ps) + (m : V) (w : Nat) (R : List V → V) : + Telescope.piN w (Telescope.interpret reading levels + (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) (shape.minorTypesSyntax source mode)) R = + Telescope.piN w (Telescope.simple (shape.minorTypes constants (mode.sourceArgs levels) + (Telescope.extend (fun _ => empty) ps) (mode.motiveLevel.eval levels) m)) R := by + have ht := Telescope.piN_independent reading levels + (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) + (shape.constructors.zipIdx.map fun (ctor, i) => shape.minorType source mode i ctor) [] w R + simp only [List.length_nil, Telescope.extend] at ht + rw [minorTypesSyntax, ht] + have he : ((shape.constructors.zipIdx.map fun (ctor, i) => shape.minorType source mode i ctor).map + (interp reading levels (Valuation.cons m (Telescope.extend (fun _ => empty) ps)))) = + shape.minorTypes constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (mode.motiveLevel.eval levels) m := by + apply List.ext_getElem? + intro i + simp only [minorTypes, List.getElem?_map, List.getElem?_zipIdx] + cases hc : shape.constructors[i]? with + | none => rfl + | some ctor => + simp only [Option.map_some, Nat.zero_add] + exact congrArg some (minorType_interp h hr hM hn hc hps m) + rw [he] + +theorem minorTypes_curry {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : ConstructorReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = mode.recUvars shape.universes) {ps : List V} + (hps : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (fun _ => empty) shape.parameters) ps) + (m : V) (w : Nat) (f : List V → V) : + Telescope.curry w (Telescope.interpret reading levels + (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) (shape.minorTypesSyntax source mode)) f = + Telescope.curry w (Telescope.simple (shape.minorTypes constants (mode.sourceArgs levels) + (Telescope.extend (fun _ => empty) ps) (mode.motiveLevel.eval levels) m)) f := by + have ht := Telescope.curry_independent reading levels + (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) + (shape.constructors.zipIdx.map fun (ctor, i) => shape.minorType source mode i ctor) [] w f + simp only [List.length_nil, Telescope.extend] at ht + rw [minorTypesSyntax, ht] + have he : ((shape.constructors.zipIdx.map fun (ctor, i) => shape.minorType source mode i ctor).map + (interp reading levels (Valuation.cons m (Telescope.extend (fun _ => empty) ps)))) = + shape.minorTypes constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (mode.motiveLevel.eval levels) m := by + apply List.ext_getElem? + intro i + simp only [minorTypes, List.getElem?_map, List.getElem?_zipIdx] + cases hc : shape.constructors[i]? with + | none => rfl + | some ctor => + simp only [Option.map_some, Nat.zero_add] + exact congrArg some (minorType_interp h hr hM hn hc hps m) + rw [he] + +theorem recursorTail_interp {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : FamilyReading entries shape source constants reading) + (hn : levels.length = mode.recUvars shape.universes) {ps minors : List V} + (hps : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (fun _ => empty) shape.parameters) ps) + (hlen : minors.length = shape.constructors.length) (m : V) : + interp reading levels (Telescope.extend (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) minors) + (shape.recursorTail source mode) = + shape.recursorSet constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (mode.motiveLevel.eval levels) m := by + have hcommon : 1 + shape.constructors.length = (m :: minors).length := by simp [hlen, Nat.add_comm] + have hskip : Valuation.skip (1 + shape.constructors.length) 0 + (Telescope.extend (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) minors) = + Telescope.extend (fun _ => empty) ps := by + simpa only [← hcommon, Telescope.extend] using + Telescope.skip_extend (Telescope.extend (fun _ => empty) ps) (m :: minors) + rw [recursorTail, AExpr.interp_forallN, Telescope.piN_lift, hskip, + Telescope.piN_instL, mode.sourceLevels_eval hn, + hr.agrees.telescope (fun A ha => h.references A (List.mem_append_right _ ha))] + apply Telescope.piN_zero_agree (regime_zeroCondition mode.motiveLevel levels) + intro is his + have hil := FitsS.length_eq his + have hvars := interp_parameterVars_extend reading (mode.sourceArgs levels) + (Telescope.extend (fun _ => empty) ps) is + rw [hil] at hvars + have hit : FitsS (Telescope.interpret constants (mode.sourceArgs levels) + (Telescope.extend (fun _ => empty) ps) shape.indices) + ((parameterVars 0 shape.indices.length).map + (interp reading (mode.sourceArgs levels) + (Telescope.extend (Telescope.extend (fun _ => empty) ps) is))) := by + simpa only [hvars] using his + have happ := familyApp_interp hr (mode.sourceArgs_length hn) hps hit + simp only [hil, hvars] at happ + have hskip' : Valuation.skip (1 + shape.constructors.length) shape.indices.length + (Telescope.extend (Telescope.extend (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) minors) is) = + Telescope.extend (Telescope.extend (fun _ => empty) ps) is := by + simpa only [← hcommon, Telescope.extend, hil] using + Telescope.skip_middle (Telescope.extend (fun _ => empty) ps) (m :: minors) is + rw [interp, interp_liftN, hskip', interp_instL, mode.sourceLevels_eval hn, happ] + apply piR_zero_agree (regime_zeroCondition mode.motiveLevel levels) + intro x _ + have hmvar : Valuation.cons x + (Telescope.extend (Telescope.extend (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) minors) is) + (shape.constructors.length + shape.indices.length + 1) = m := by + rw [Valuation.cons_succ] + have ht := Telescope.extend_beyond (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) (minors ++ is) 0 + simpa only [List.length_append, Nat.add_zero, hlen, hil, Telescope.extend_append, Valuation.cons_zero] using ht + have hisvars := interp_parameterVars_middle reading levels + (Telescope.extend (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) minors) is [x] + simp only [List.length_singleton, hil, Telescope.extend] at hisvars + simp only [AExpr.interp_appN, interp, hmvar, List.map_append, hisvars, List.map_cons, + List.map_nil, Valuation.cons_zero, Telescope.applyN_append, Telescope.applyN] + rw [motive, projList_mkTower _ _ hil] + +noncomputable def closedRecursorSet (shape : Shape β) (constants : Assignment β V) + (levels : List Nat) (mode : ElimMode) : V := + let ls := mode.sourceArgs levels + let v := mode.motiveLevel.eval levels + Telescope.piN v (Telescope.interpret constants ls (fun _ => empty) shape.parameters) fun ps => + piR v (shape.motiveSet constants ls (Telescope.extend (fun _ => empty) ps) v) fun m => + Telescope.piN v (Telescope.simple (shape.minorTypes constants ls (Telescope.extend (fun _ => empty) ps) v m)) + (fun _ => shape.recursorSet constants ls (Telescope.extend (fun _ => empty) ps) v m) + +theorem recursorType_interp {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : ConstructorReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = mode.recUvars shape.universes) : + interp reading levels (fun _ => empty) (shape.recursorType source mode) = + shape.closedRecursorSet constants levels mode := by + rw [recursorType, AExpr.interp_forallN, Telescope.piN_instL, mode.sourceLevels_eval hn, + hr.agrees.telescope (fun A ha => h.references A (List.mem_append_left _ ha))] + apply Telescope.piN_zero_agree (regime_zeroCondition mode.motiveLevel levels) + intro ps hps + rw [interp, motiveType_interp hr.toFamilyReading hn hps + (fun A ha => h.references A (List.mem_append_right _ ha))] + apply piR_zero_agree (regime_zeroCondition mode.motiveLevel levels) + intro m _ + rw [AExpr.interp_forallN, minorTypes_piN h hr hM hn hps] + apply Telescope.piN_zero_agree (regime_zeroCondition mode.motiveLevel levels) + intro minors hminors + exact recursorTail_interp h hr.toFamilyReading hn hps + (by simpa only [minorTypes, List.length_map, List.length_zipIdx] using FitsS.length_eq hminors) m + +end Shape +end Ix.Theory.Certified.Ordinary diff --git a/Ix/Theory/Certified/Ordinary/RecursorStage.lean b/Ix/Theory/Certified/Ordinary/RecursorStage.lean new file mode 100644 index 000000000..9ef6c73c5 --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/RecursorStage.lean @@ -0,0 +1,163 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.ConstructorStage + +namespace Ix.Theory.Certified.Ordinary + +open Model Model.SetTheory Model.SetTheory.Tower Model.SetModel Inductive + +universe u v +variable {β : Type u} [DecidableEq β] + +namespace Shape + +def recursorEntry (shape : Shape β) (source : β) (mode : ElimMode) : ConstantEntry β := + ⟨mode.recUvars shape.universes, shape.recursorType source mode, none, [], []⟩ + +def recursorSource (shape : Shape β) (source recursor : β) (mode : ElimMode) (k : Bool := false) : Const β := + .recursor (mode.recUvars shape.universes) shape.parameters.length shape.indices.length 1 + shape.constructors.length (shape.recursorType source mode).erase + (shape.constructors.zipIdx.map fun (ctor, i) => + ⟨ctor.fields.length + ctor.recursive.length, (shape.ruleRhs source recursor mode i ctor).erase⟩) + k .safe + +/-- K metadata is supported for a singleton proposition whose constructor has +no fields. Replacing a major proof still requires a typed proof-irrelevance +certificate at the exact constructor result, followed by the admitted rule. -/ +def SupportsK (shape : Shape β) : Prop := + shape.level = .zero ∧ ∃ ctor, shape.constructors = [ctor] ∧ ctor.fields = [] ∧ ctor.recursive = [] + +instance (shape : Shape β) : Decidable shape.SupportsK := + match h : shape.constructors with + | [ctor] => decidable_of_iff (shape.level = .zero ∧ ctor.fields = [] ∧ ctor.recursive = []) (by + simp [SupportsK, h]) + | [] => .isFalse (by simp [SupportsK, h]) + | _ :: _ :: _ => .isFalse (by simp [SupportsK, h]) + +def RecursorSourceMatches (shape : Shape β) (store : Store β) (source recursor : β) (mode : ElimMode) : Prop := + store.blocks recursor = some ⟨[shape.recursorSource source recursor mode]⟩ ∨ + (shape.SupportsK ∧ store.blocks recursor = some ⟨[shape.recursorSource source recursor mode true]⟩) + +instance (shape : Shape β) (store : Store β) (source recursor : β) (mode : ElimMode) : + Decidable (shape.RecursorSourceMatches store source recursor mode) := by + unfold RecursorSourceMatches + infer_instance + +def recursorLaws (shape : Shape β) (source recursor : β) (mode : ElimMode) : List (ConstantEquation β) := + shape.constructors.zipIdx.map fun (ctor, i) => + ⟨shape.ruleLhs source recursor mode i ctor, shape.ruleRhs source recursor mode i ctor⟩ + +def recursorEnvironment (shape : Shape β) (entries : Environment β) (source recursor : β) + (mode : ElimMode) : Environment β := + (shape.constructorEnvironment entries source).insert (.member recursor 0) (shape.recursorEntry source mode) + +structure RecursorFormation (entries : Environment β) (store : Store β) (shape : Shape β) + (source recursor : β) (mode : ElimMode) : Prop where + exactSource : shape.RecursorSourceMatches store source recursor mode + fresh : shape.constructorEnvironment entries source (.member recursor 0) = none + closed : EntryClosed (shape.constructorEnvironment entries source) (shape.recursorEntry source mode) + typing : ∃ l, TypingClaim.{u,v} (shape.constructorEnvironment entries source) [] + (shape.recursorType source mode) (.sort l) + +def checkRecursorType (fuel : Nat) (entries : Environment β) (store : Store β) (shape : Shape β) + (source recursor : β) (mode : ElimMode) (witness : TypeWitness β) : + Option (CheckedClaim.{u} (RecursorFormation.{u,v} entries store shape source recursor mode)) := + if hsource : shape.RecursorSourceMatches store source recursor mode then + if hfresh : shape.constructorEnvironment entries source (.member recursor 0) = none then + if hs : (shape.recursorType source mode).Scope (mode.recUvars shape.universes) 0 then + if hr : (shape.recursorType source mode).ReferencesIn (shape.constructorEnvironment entries source) then do + let ht ← verifyType.{u,v} fuel (mode.recUvars shape.universes) (shape.constructorEnvironment entries source) [] + (shape.recursorType source mode) (.sort witness.level) witness.witness + return ⟨⟨hsource, hfresh, ⟨hs, by simp [recursorEntry], hr, by simp [recursorEntry], + by simp [recursorEntry], by simp [recursorEntry], + by simp [recursorEntry], by simp [recursorEntry]⟩, witness.level, ht.down⟩⟩ + else none + else none + else none + else none + +theorem recursorEnvironment_wf {entries : Environment β} {store : Store β} {source recursor : β} + {shape : Shape β} {mode : ElimMode} + (h : CheckedShape.{u,v} entries store source shape) (hE : entries.WF) + (hC : ConstructorFormation.{u,v} entries shape source) + (hR : RecursorFormation.{u,v} entries store shape source recursor mode) : + (shape.recursorEnvironment entries source recursor mode).WF := + (constructorEnvironment_wf h hE hC).insert hR.closed.typeScope hR.closed.bodyScope + hR.closed.typeReferences hR.closed.bodyReferences hR.closed.equationScope hR.closed.equationReferences + hR.closed.factScope hR.closed.factReferences + +variable {V : Type v} [SetTheory V] + +noncomputable def recursorAssignment (shape : Shape β) (constants : Assignment β V) + (source recursor : β) (mode : ElimMode) : Assignment β V := + (shape.constructorAssignment constants source).insert (.member recursor 0) + (fun levels => shape.closedRecursorValue constants levels mode) + +end Shape + +variable {V : Type v} [SetTheory V] + +structure RecursorReading (entries : Environment β) (shape : Shape β) (source recursor : β) + (mode : ElimMode) (constants reading : Assignment β V) : Prop + extends ConstructorReading entries shape source constants reading where + recursor : ∀ levels, levels.length = mode.recUvars shape.universes → + reading (.member recursor 0) levels = shape.closedRecursorValue constants levels mode + +namespace Shape + +theorem recursorAssignment_agreesConstructors {entries : Environment β} {store : Store β} {source recursor : β} + {shape : Shape β} {mode : ElimMode} + (hR : RecursorFormation.{u,v} entries store shape source recursor mode) (constants : Assignment β V) : + Assignment.AgreesOn (shape.constructorEnvironment entries source) (shape.constructorAssignment constants source) + (shape.recursorAssignment constants source recursor mode) := Assignment.insert_agrees hR.fresh _ _ + +theorem recursorAssignment_reading {entries : Environment β} {store : Store β} {source recursor : β} + {shape : Shape β} {mode : ElimMode} (h : CheckedShape.{u,v} entries store source shape) + (hR : RecursorFormation.{u,v} entries store shape source recursor mode) (constants : Assignment β V) : + RecursorReading entries shape source recursor mode constants (shape.recursorAssignment constants source recursor mode) := by + have hr := constructorAssignment_reading h constants + have ha := recursorAssignment_agreesConstructors hR constants + constructor + · constructor + · constructor + · intro r entry he levels + exact (ha r entry (Environment.overlay_old (constructorEntries_fresh h) + (Environment.insert_old (h.fresh _ (List.mem_cons_self ..)) he)) levels).trans (hr.agrees r entry he levels) + · intro levels hn + exact (ha _ _ (Environment.overlay_old (constructorEntries_fresh h) (Environment.insert_same ..)) levels).trans + (hr.family levels hn) + · intro levels hn i ctor hc + exact (ha _ (shape.constructorEntry source ctor) (Environment.overlay_new (by + simp only [constructorEntries, ↓reduceIte, hc, Option.map_some])) levels).trans (hr.constructor levels hn i ctor hc) + · intro levels _ + exact Assignment.insert_same .. + +theorem recursorAssignment_realizes {entries : Environment β} {store : Store β} {source recursor : β} + {shape : Shape β} {mode : ElimMode} + (h : CheckedShape.{u,v} entries store source shape) (hE : entries.WF) + (hC : ConstructorFormation.{u,v} entries shape source) + (hR : RecursorFormation.{u,v} entries store shape source recursor mode) + (hmode : ModeEvidence.{u,v} entries shape mode) (constants : Assignment β V) (hM : Realizes constants entries) : + Realizes (shape.recursorAssignment constants source recursor mode) (shape.recursorEnvironment entries source recursor mode) := by + have hr := recursorAssignment_reading h hR constants + have hbase := (constructorAssignment_realizes h hE hC constants hM).of_agrees + (constructorEnvironment_wf h hE hC) (recursorAssignment_agreesConstructors hR constants) + obtain ⟨l, ht⟩ := hR.typing + apply hbase.insert + constructor + · intro levels hn env + exact (ht V _ hbase levels env (Context.valid_nil _ _ _)).1 + · intro levels hn env + rw [hr.recursor levels hn] + have hm := closedRecursorValue_mem_source h hr.toConstructorReading hmode hM hn + rwa [interp_closed (shape.recursorType source mode) _ levels hR.closed.typeScope (fun _ => empty) env] at hm + · intro body hb; cases hb + · intro body hb; cases hb + · intro law hl; simp only [recursorEntry, List.not_mem_nil] at hl + · intro fact hf; simp only [recursorEntry, List.not_mem_nil] at hf + +end Shape +end Ix.Theory.Certified.Ordinary diff --git a/Ix/Theory/Certified/Ordinary/RecursorSyntax.lean b/Ix/Theory/Certified/Ordinary/RecursorSyntax.lean new file mode 100644 index 000000000..41ceed1b6 --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/RecursorSyntax.lean @@ -0,0 +1,155 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.Reading +import Ix.Theory.Inductive.Levels + +/-! +Annotated ordinary recursor syntax. Every source expression is transported +through the explicit source-universe map; the motive keeps its own universe. +The generated syntax is subsequently compared with the complete stored type +and rule, and is checked for formation before any equation is published. +-/ + +namespace Ix.Theory.Certified.Ordinary + +open Model Inductive + +universe u +variable {β : Type u} + +namespace Shape + +def motiveType (shape : Shape β) (source : β) (mode : ElimMode) : AExpr β := + .forallN .never (shape.indices.map (AExpr.instL (mode.sourceLevels shape.universes))) <| + .forallE .never + ((shape.familyApp source shape.indices.length (parameterVars 0 shape.indices.length)).instL + (mode.sourceLevels shape.universes)) + (.sort mode.motiveLevel) + +def minorFields (shape : Shape β) (source : β) (mode : ElimMode) (ctor : Constructor β) : + List (AExpr β) := + Telescope.lift 1 ((ctor.fields ++ ctor.recursiveTypes shape source).map + (AExpr.instL (mode.sourceLevels shape.universes))) + +def ihType (shape : Shape β) (mode : ElimMode) (ctor : Constructor β) + (j : Nat) (field : RecursiveField β) : AExpr β := + let sl := mode.sourceLevels shape.universes + let a := ctor.fields.length + let b := ctor.recursive.length + let d := field.domains.length + .forallN (zeroCondition mode.motiveLevel) + (Telescope.lift b (Telescope.lift 1 (field.domains.map (AExpr.instL sl)) a)) + (.appN (.bvar (a + b + d)) + (field.indices.map (fun e => ((e.instL sl).liftN 1 (a + d)).liftN b d) ++ + [.appN (.bvar (b - 1 - j + d)) (parameterVars 0 d)])) + +def ihTypesSyntax (shape : Shape β) (mode : ElimMode) (ctor : Constructor β) : List (AExpr β) := + Telescope.independent (ctor.recursive.zipIdx.map fun (field, j) => shape.ihType mode ctor j field) + +def minorResult (shape : Shape β) (source : β) (mode : ElimMode) (i : Nat) + (ctor : Constructor β) : AExpr β := + let sl := mode.sourceLevels shape.universes + let a := ctor.fields.length + let b := ctor.recursive.length + .appN (.bvar (a + b + b)) + (ctor.indices.map (fun e => ((e.instL sl).liftN 1 a).liftN (b + b)) ++ + [.appN (.const (.ctor source 0 i) sl) + (parameterVars (1 + a + b + b) shape.parameters.length ++ parameterVars b (a + b))]) + +def minorType (shape : Shape β) (source : β) (mode : ElimMode) (i : Nat) + (ctor : Constructor β) : AExpr β := + .forallN (zeroCondition mode.motiveLevel) (shape.minorFields source mode ctor) <| + .forallN (zeroCondition mode.motiveLevel) (shape.ihTypesSyntax mode ctor) <| + shape.minorResult source mode i ctor + +def minorTypesSyntax (shape : Shape β) (source : β) (mode : ElimMode) : List (AExpr β) := + Telescope.independent (shape.constructors.zipIdx.map fun (ctor, i) => shape.minorType source mode i ctor) + +def recursorTail (shape : Shape β) (source : β) (mode : ElimMode) : AExpr β := + let sl := mode.sourceLevels shape.universes + let pv := zeroCondition mode.motiveLevel + let c := shape.constructors.length + let n := shape.indices.length + .forallN pv (Telescope.lift (1 + c) (shape.indices.map (AExpr.instL sl))) <| + .forallE pv + (((shape.familyApp source n (parameterVars 0 n)).instL sl).liftN (1 + c) n) + (.appN (.bvar (c + n + 1)) (parameterVars 1 n ++ [.bvar 0])) + +def recursorType (shape : Shape β) (source : β) (mode : ElimMode) : AExpr β := + let pv := zeroCondition mode.motiveLevel + .forallN pv (shape.parameters.map (AExpr.instL (mode.sourceLevels shape.universes))) <| + .forallE pv (shape.motiveType source mode) <| + .forallN pv (shape.minorTypesSyntax source mode) <| + shape.recursorTail source mode + +def ruleBinders (shape : Shape β) (source : β) (mode : ElimMode) (ctor : Constructor β) : + List (AExpr β) := + shape.parameters.map (AExpr.instL (mode.sourceLevels shape.universes)) ++ + [shape.motiveType source mode] ++ shape.minorTypesSyntax source mode ++ + Telescope.lift (1 + shape.constructors.length) + ((ctor.fields ++ ctor.recursiveTypes shape source).map + (AExpr.instL (mode.sourceLevels shape.universes))) + +def ruleCall (shape : Shape β) (recursor : β) (mode : ElimMode) + (ctor : Constructor β) (j : Nat) (field : RecursiveField β) : AExpr β := + let sl := mode.sourceLevels shape.universes + let common := 1 + shape.constructors.length + let a := ctor.fields.length + let b := ctor.recursive.length + let d := field.domains.length + .lamN (zeroCondition mode.motiveLevel) + (Telescope.lift b (Telescope.lift common (field.domains.map (AExpr.instL sl)) a)) + (.appN (.const (.member recursor 0) (mode.recLevels shape.universes)) + (parameterVars (a + b + d) (shape.parameters.length + common) ++ + field.indices.map (fun e => ((e.instL sl).liftN common (a + d)).liftN b d) ++ + [.appN (.bvar (b - 1 - j + d)) (parameterVars 0 d)])) + +def ruleRhsBody (shape : Shape β) (recursor : β) (mode : ElimMode) (i : Nat) + (ctor : Constructor β) : AExpr β := + .appN (.bvar (shape.constructors.length - 1 - i + ctor.fields.length + ctor.recursive.length)) + (parameterVars 0 (ctor.fields.length + ctor.recursive.length) ++ + ctor.recursive.zipIdx.map (fun (field, j) => shape.ruleCall recursor mode ctor j field)) + +def ruleRhs (shape : Shape β) (source recursor : β) (mode : ElimMode) (i : Nat) + (ctor : Constructor β) : AExpr β := + .lamN (zeroCondition mode.motiveLevel) (shape.ruleBinders source mode ctor) + (shape.ruleRhsBody recursor mode i ctor) + +def ruleIndices (shape : Shape β) (mode : ElimMode) (ctor : Constructor β) : List (AExpr β) := + ctor.indices.map fun e => + ((e.instL (mode.sourceLevels shape.universes)).liftN (1 + shape.constructors.length) + ctor.fields.length).liftN ctor.recursive.length + +def ruleConstructor (shape : Shape β) (source : β) (mode : ElimMode) (i : Nat) + (ctor : Constructor β) : AExpr β := + let n := ctor.fields.length + ctor.recursive.length + .appN (.const (.ctor source 0 i) (mode.sourceLevels shape.universes)) + (parameterVars (1 + shape.constructors.length + n) shape.parameters.length ++ parameterVars 0 n) + +def ruleResult (shape : Shape β) (source : β) (mode : ElimMode) (i : Nat) + (ctor : Constructor β) : AExpr β := + .appN (.bvar (shape.constructors.length + ctor.fields.length + ctor.recursive.length)) + (shape.ruleIndices mode ctor ++ [shape.ruleConstructor source mode i ctor]) + +def ruleType (shape : Shape β) (source : β) (mode : ElimMode) (i : Nat) + (ctor : Constructor β) : AExpr β := + .forallN (zeroCondition mode.motiveLevel) (shape.ruleBinders source mode ctor) + (shape.ruleResult source mode i ctor) + +def ruleLhsBody (shape : Shape β) (source recursor : β) (mode : ElimMode) (i : Nat) + (ctor : Constructor β) : AExpr β := + .appN (.const (.member recursor 0) (mode.recLevels shape.universes)) + (parameterVars (ctor.fields.length + ctor.recursive.length) + (shape.parameters.length + 1 + shape.constructors.length) ++ + shape.ruleIndices mode ctor ++ [shape.ruleConstructor source mode i ctor]) + +def ruleLhs (shape : Shape β) (source recursor : β) (mode : ElimMode) (i : Nat) + (ctor : Constructor β) : AExpr β := + .lamN (zeroCondition mode.motiveLevel) (shape.ruleBinders source mode ctor) + (shape.ruleLhsBody source recursor mode i ctor) + +end Shape +end Ix.Theory.Certified.Ordinary diff --git a/Ix/Theory/Certified/Ordinary/RecursorValue.lean b/Ix/Theory/Certified/Ordinary/RecursorValue.lean new file mode 100644 index 000000000..cffca1022 --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/RecursorValue.lean @@ -0,0 +1,136 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.RecursorReading + +namespace Ix.Theory.Certified.Ordinary + +open Model Model.SetTheory Model.SetTheory.Tower Model.SetModel Inductive + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +def ModeEvidence (entries : Environment β) (shape : Shape β) : ElimMode → Prop + | .small => True + | .large => LargeEvidence.{u,v} entries shape + +def checkMode [DecidableEq β] (fuel : Nat) (entries : Environment β) (witness : ShapeWitness β) + (mode : ElimMode) : Option (CheckedClaim.{u} (ModeEvidence.{u,v} entries witness.shape mode)) := + match mode with + | .small => some ⟨trivial⟩ + | .large => checkLarge fuel entries witness + +namespace Shape + +noncomputable def recursorAt (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (mode : ElimMode) (m : V) (minors : List V) : V := by + classical + exact match mode with + | .small => pt + | .large => + if hD : (shape.container constants (mode.sourceArgs levels) env).WF + (shape.level.eval (mode.sourceArgs levels)) then + if hlarge : (shape.container constants (mode.sourceArgs levels) env).LargeElim + (shape.level.eval (mode.sourceArgs levels)) then + shape.largeValue constants (mode.sourceArgs levels) env (mode.motiveLevel.eval levels) m minors hD hlarge + else pt + else pt + +theorem recursorAt_large (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (m : V) (minors : List V) + (hD : (shape.container constants (ElimMode.large.sourceArgs levels) env).WF + (shape.level.eval (ElimMode.large.sourceArgs levels))) + (hlarge : (shape.container constants (ElimMode.large.sourceArgs levels) env).LargeElim + (shape.level.eval (ElimMode.large.sourceArgs levels))) : + shape.recursorAt constants levels env .large m minors = + shape.largeValue constants (ElimMode.large.sourceArgs levels) env + (ElimMode.large.motiveLevel.eval levels) m minors hD hlarge := by + simp only [recursorAt, dif_pos hD, dif_pos hlarge] + +theorem recursorSet_zero (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (m : V) : + shape.recursorSet constants levels env 0 m ∈ˢ (univZero : V) := + Telescope.piN_zero_mem _ (fun _ _ => piR_zero_mem_univZero) + +variable {entries : Environment β} {shape : Shape β} {source : β} {store : Store β} + {constants : Assignment β V} {levels : List Nat} {env : Nat → V} {mode : ElimMode} + +theorem recursorAt_mem (h : CheckedShape.{u,v} entries store source shape) + (hmode : ModeEvidence.{u,v} entries shape mode) (hM : Realizes constants entries) + (hΓ : shape.parameterContext.Valid constants (mode.sourceArgs levels) env) + {m : V} {minors : List V} + (hm : m ∈ˢ shape.motiveSet constants (mode.sourceArgs levels) env (mode.motiveLevel.eval levels)) + (hminors : shape.MinorValuesFit constants (mode.sourceArgs levels) env (mode.motiveLevel.eval levels) m minors) : + shape.recursorAt constants levels env mode m minors ∈ˢ + shape.recursorSet constants (mode.sourceArgs levels) env (mode.motiveLevel.eval levels) m := by + cases mode with + | small => exact pt_mem_smallRecursorSet h hM hΓ hm hminors + | large => + rw [recursorAt_large _ _ _ _ _ _ (container_wf h hM hΓ) (container_large hmode hM hΓ)] + exact largeValue_mem h hM hΓ hm hminors _ _ + +noncomputable def closedRecursorValue (shape : Shape β) (constants : Assignment β V) + (levels : List Nat) (mode : ElimMode) : V := + let ls := mode.sourceArgs levels + let v := mode.motiveLevel.eval levels + Telescope.curry v (Telescope.interpret constants ls (fun _ => empty) shape.parameters) fun ps => + lamR v (shape.motiveSet constants ls (Telescope.extend (fun _ => empty) ps) v) fun m => + Telescope.curry v (Telescope.simple (shape.minorTypes constants ls (Telescope.extend (fun _ => empty) ps) v m)) + (fun minors => shape.recursorAt constants levels (Telescope.extend (fun _ => empty) ps) mode m minors) + +theorem closedRecursorValue_mem (h : CheckedShape.{u,v} entries store source shape) + (hmode : ModeEvidence.{u,v} entries shape mode) (hM : Realizes constants entries) : + shape.closedRecursorValue constants levels mode ∈ˢ shape.closedRecursorSet constants levels mode := by + apply Telescope.curry_mem + intro ps hps + apply lamR_mem + intro m hm + apply Telescope.curry_mem + intro minors hminors + exact recursorAt_mem h hmode hM + (h.parameters.valid constants hM (mode.sourceArgs levels) + (Context.valid_nil constants (mode.sourceArgs levels) (fun _ => empty)) hps) hm hminors + +theorem closedRecursorValue_mem_source (h : CheckedShape.{u,v} entries store source shape) + {reading : Assignment β V} (hr : ConstructorReading entries shape source constants reading) + (hmode : ModeEvidence.{u,v} entries shape mode) (hM : Realizes constants entries) + (hn : levels.length = mode.recUvars shape.universes) : + shape.closedRecursorValue constants levels mode ∈ˢ + interp reading levels (fun _ => empty) (shape.recursorType source mode) := by + rw [recursorType_interp h hr hM hn] + exact closedRecursorValue_mem h hmode hM + +theorem closedRecursorValue_apply (h : CheckedShape.{u,v} entries store source shape) + (hmode : ModeEvidence.{u,v} entries shape mode) (hM : Realizes constants entries) {ps : List V} + (hps : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (fun _ => empty) shape.parameters) ps) + {m : V} {minors : List V} + (hm : m ∈ˢ shape.motiveSet constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (mode.motiveLevel.eval levels)) + (hminors : shape.MinorValuesFit constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (mode.motiveLevel.eval levels) m minors) : + Telescope.applyN (shape.closedRecursorValue constants levels mode) (ps ++ [m] ++ minors) = + shape.recursorAt constants levels (Telescope.extend (fun _ => empty) ps) mode m minors := by + have hbody (ps : List V) hps m hm minors hminors := recursorAt_mem h hmode hM + (h.parameters.valid constants hM (mode.sourceArgs levels) (xs := ps) + (Context.valid_nil constants (mode.sourceArgs levels) (fun _ => empty)) hps) + (m := m) hm (minors := minors) hminors + have hminor ps hps m hm := Telescope.curry_mem (w := mode.motiveLevel.eval levels) _ (hbody ps hps m hm) + have hmotive ps hps := lamR_mem (v := mode.motiveLevel.eval levels) (hminor ps hps) + rw [closedRecursorValue, Telescope.applyN_append, Telescope.applyN_append, + Telescope.applyN_curry _ hps hmotive (fun hv _ _ => by + rw [hv] + exact piR_zero_mem_univZero)] + change Telescope.applyN (app (lamR _ _ _) m) minors = _ + rw [app_lamR hm (hminor ps hps) (fun hv _ _ => by + rw [hv] + apply Telescope.piN_zero_mem + intro _ _ + exact recursorSet_zero _ _ _ _ _)] + exact Telescope.applyN_curry _ hminors (hbody ps hps m hm) (fun hv _ _ => by + rw [hv] + exact recursorSet_zero _ _ _ _ _) + +end Shape +end Ix.Theory.Certified.Ordinary diff --git a/Ix/Theory/Certified/Ordinary/RuleChecks.lean b/Ix/Theory/Certified/Ordinary/RuleChecks.lean new file mode 100644 index 000000000..874c81654 --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/RuleChecks.lean @@ -0,0 +1,90 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.RecursorStage + +namespace Ix.Theory.Certified.Ordinary + +open Model Model.SetTheory Model.SetModel Inductive + +universe u v +variable {β : Type u} [DecidableEq β] + +structure RuleWitness (β : Type u) where + typeLevel : VLevel + typeWitness : TypingWitness β + lhsWitness : TypingWitness β + rhsWitness : TypingWitness β + +namespace Shape + +/-- These checks run against the formed recursor signature with no equations. +They establish the whole closed rule's type, both endpoint memberships, and +the syntactic scope/reference facts needed when its equation is published. -/ +structure RuleFormation (entries : Environment β) (shape : Shape β) (source recursor : β) + (mode : ElimMode) (i : Nat) (ctor : Constructor β) : Prop where + scope : (shape.ruleLhs source recursor mode i ctor).Scope (mode.recUvars shape.universes) 0 ∧ + (shape.ruleRhs source recursor mode i ctor).Scope (mode.recUvars shape.universes) 0 + references : (shape.ruleLhs source recursor mode i ctor).ReferencesIn entries ∧ + (shape.ruleRhs source recursor mode i ctor).ReferencesIn entries + type : ∃ l, TypingClaim.{u,v} entries [] (shape.ruleType source mode i ctor) (.sort l) ∧ + (mode = .small → ∀ levels, l.eval levels = 0) + lhs : TypingClaim.{u,v} entries [] (shape.ruleLhs source recursor mode i ctor) (shape.ruleType source mode i ctor) + rhs : TypingClaim.{u,v} entries [] (shape.ruleRhs source recursor mode i ctor) (shape.ruleType source mode i ctor) + +def checkRule (fuel : Nat) (entries : Environment β) (shape : Shape β) (source recursor : β) + (mode : ElimMode) (i : Nat) (ctor : Constructor β) (witness : RuleWitness β) : + Option (CheckedClaim.{u} (RuleFormation.{u,v} entries shape source recursor mode i ctor)) := + if hs : (shape.ruleLhs source recursor mode i ctor).Scope (mode.recUvars shape.universes) 0 ∧ + (shape.ruleRhs source recursor mode i ctor).Scope (mode.recUvars shape.universes) 0 then + if hr : (shape.ruleLhs source recursor mode i ctor).ReferencesIn entries ∧ + (shape.ruleRhs source recursor mode i ctor).ReferencesIn entries then + if hsmall : mode = .small → LevelEq.check (mode.recUvars shape.universes) witness.typeLevel .zero = true then do + let ht ← verifyType.{u,v} fuel (mode.recUvars shape.universes) entries [] + (shape.ruleType source mode i ctor) (.sort witness.typeLevel) witness.typeWitness + let hl ← verifyType.{u,v} fuel (mode.recUvars shape.universes) entries [] + (shape.ruleLhs source recursor mode i ctor) (shape.ruleType source mode i ctor) witness.lhsWitness + let hh ← verifyType.{u,v} fuel (mode.recUvars shape.universes) entries [] + (shape.ruleRhs source recursor mode i ctor) (shape.ruleType source mode i ctor) witness.rhsWitness + return ⟨⟨hs, hr, ⟨witness.typeLevel, ht.down, by + intro he levels + exact (LevelEq.check_sound (hsmall he)).2.2 levels⟩, hl.down, hh.down⟩⟩ + else none + else none + else none + +def checkRules (fuel : Nat) (entries : Environment β) (shape : Shape β) (source recursor : β) + (mode : ElimMode) : (ctors : List (Constructor β × Nat)) → List (RuleWitness β) → + Option (CheckedClaim.{u} (∀ ctor i, (ctor, i) ∈ ctors → + RuleFormation.{u,v} entries shape source recursor mode i ctor)) + | [], [] => some ⟨by simp⟩ + | (ctor, i) :: ctors, witness :: witnesses => do + let rule ← checkRule fuel entries shape source recursor mode i ctor witness + let rest ← checkRules fuel entries shape source recursor mode ctors witnesses + return ⟨by + intro ctor' i' hc + rcases List.mem_cons.mp hc with he | hc + · cases he; exact rule.down + · exact rest.down ctor' i' hc⟩ + | _, _ => none + +variable {V : Type v} [SetTheory V] + +omit [DecidableEq β] in +theorem small_rule_eq {entries : Environment β} {shape : Shape β} {source recursor : β} + {i : Nat} {ctor : Constructor β} + (h : RuleFormation.{u,v} entries shape source recursor .small i ctor) + (constants : Assignment β V) (hM : Realizes constants entries) (levels : List Nat) (env : Nat → V) : + interp constants levels env (shape.ruleLhs source recursor .small i ctor) = + interp constants levels env (shape.ruleRhs source recursor .small i ctor) := by + obtain ⟨l, ht, hz⟩ := h.type + have hΓ := Context.valid_nil constants levels env + have hprop := (ht V constants hM levels env hΓ).2.2 + rw [interp, hz rfl levels, univ_zero] at hprop + exact subsingleton_of_mem_univZero hprop (h.lhs V constants hM levels env hΓ).2.2 + (h.rhs V constants hM levels env hΓ).2.2 + +end Shape +end Ix.Theory.Certified.Ordinary diff --git a/Ix/Theory/Certified/Ordinary/RuleEquations.lean b/Ix/Theory/Certified/Ordinary/RuleEquations.lean new file mode 100644 index 000000000..9af9d737b --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/RuleEquations.lean @@ -0,0 +1,115 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.RuleReading + +namespace Ix.Theory.Certified.Ordinary.Shape + +open Model Model.SetTheory Model.SetTheory.Tower Model.SetModel Inductive + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + {entries : Environment β} {shape : Shape β} {source recursor : β} + {constants reading : Assignment β V} {levels : List Nat} {mode : ElimMode} + +theorem ruleFields_curry {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : FamilyReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = mode.recUvars shape.universes) {ctor : Constructor β} (hc : ctor ∈ shape.constructors) + {ps minors : List V} + (hps : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (fun _ => empty) shape.parameters) ps) + (hmlen : minors.length = shape.constructors.length) (m : V) (w : Nat) (f : List V → V) : + Telescope.curry w (Telescope.interpret reading levels + (Telescope.extend (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) minors) + (Telescope.lift (1 + shape.constructors.length) ((ctor.fields ++ ctor.recursiveTypes shape source).map + (AExpr.instL (mode.sourceLevels shape.universes))))) f = + Telescope.curry w + (Telescope.interpret constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor.fields) + (fun xs => Telescope.curry w + (Telescope.simple (ctor.recursive.map + (shape.recursiveSet constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) xs))) + (fun fs => f (xs ++ fs))) := by + have hskip : Valuation.skip (1 + shape.constructors.length) 0 + (Telescope.extend (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) minors) = + Telescope.extend (fun _ => empty) ps := by + have ht := Telescope.skip_extend (Telescope.extend (fun _ => empty) ps) (m :: minors) + simpa only [List.length_cons, hmlen, Telescope.extend, Nat.add_comm] using ht + rw [Telescope.curry_lift, hskip, Telescope.curry_instL, mode.sourceLevels_eval hn, + Telescope.curry_append, + hr.agrees.telescope (fun A ha => (h.constructors ctor hc).1 A (List.mem_append_left _ ha))] + apply Telescope.curry_congr + intro xs hxs + have ht := curry_recursiveTypes h hr hM (mode.sourceArgs_length hn) hc (fun _ h => h) hps hxs [] w + (fun fs => f (xs ++ fs)) + simpa only [Constructor.recursiveTypes, List.length_nil, Telescope.extend] using ht + +/-- Equality of the complete lambda-abstracted rule, including parameters, +the motive, every minor premise, and all constructor fields. -/ +theorem large_rule_eq {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : RecursorReading entries shape source recursor .large constants reading) + (hmode : ModeEvidence.{u,v} entries shape .large) (hM : Realizes constants entries) + (hn : levels.length = ElimMode.large.recUvars shape.universes) {i : Nat} {ctor : Constructor β} + (hc : shape.constructors[i]? = some ctor) : + interp reading levels (fun _ => empty) (shape.ruleLhs source recursor .large i ctor) = + interp reading levels (fun _ => empty) (shape.ruleRhs source recursor .large i ctor) := by + simp only [ruleLhs, ruleRhs, ruleBinders, AExpr.lamN_append, AExpr.interp_lamN, + Telescope.curry_instL, ElimMode.sourceLevels_eval _ hn, + hr.agrees.telescope (fun A ha => h.references A (List.mem_append_left _ ha))] + apply Telescope.curry_congr + intro ps hps + change lamR _ (interp reading levels _ (shape.motiveType source .large)) _ = lamR _ _ _ + rw [motiveType_interp hr.toFamilyReading hn hps (fun A ha => h.references A (List.mem_append_right _ ha))] + apply lamR_congr + intro m hm + change Telescope.curry _ (Telescope.interpret reading levels + (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) (shape.minorTypesSyntax source .large)) _ = + Telescope.curry _ (Telescope.interpret reading levels + (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) (shape.minorTypesSyntax source .large)) _ + simp only [Telescope.extend] + simp only [minorTypes_curry h hr.toConstructorReading hM hn hps m] + apply Telescope.curry_congr + intro minors hminors + have hmlen : minors.length = shape.constructors.length := by + simpa only [minorTypes, List.length_map, List.length_zipIdx] using FitsS.length_eq hminors + simp only [ruleFields_curry h hr.toFamilyReading hM hn (List.mem_of_getElem? hc) hps hmlen m] + apply Telescope.curry_congr + intro xs hxs + apply Telescope.curry_congr + intro fs hfs + simp only [Telescope.extend_append] + exact large_ruleBody_eq h hr hmode hM hn hc hps hxs hfs hm hminors + +theorem large_rule_eq_closed {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : RecursorReading entries shape source recursor .large constants reading) + (hmode : ModeEvidence.{u,v} entries shape .large) (hM : Realizes constants entries) + (hn : levels.length = ElimMode.large.recUvars shape.universes) {i : Nat} {ctor : Constructor β} + (hc : shape.constructors[i]? = some ctor) + (hscope : (shape.ruleLhs source recursor .large i ctor).Scope (ElimMode.large.recUvars shape.universes) 0 ∧ + (shape.ruleRhs source recursor .large i ctor).Scope (ElimMode.large.recUvars shape.universes) 0) + (env : Nat → V) : + interp reading levels env (shape.ruleLhs source recursor .large i ctor) = + interp reading levels env (shape.ruleRhs source recursor .large i ctor) := by + rw [← interp_closed _ _ _ hscope.1 (fun _ => empty) env, + ← interp_closed _ _ _ hscope.2 (fun _ => empty) env] + exact large_rule_eq h hr hmode hM hn hc + +variable [DecidableEq β] + +theorem produced_rule_eq {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hE : entries.WF) (hC : ConstructorFormation.{u,v} entries shape source) + (hR : RecursorFormation.{u,v} entries store shape source recursor mode) + (hmode : ModeEvidence.{u,v} entries shape mode) (hM : Realizes constants entries) + (hn : levels.length = mode.recUvars shape.universes) {i : Nat} {ctor : Constructor β} + (hc : shape.constructors[i]? = some ctor) + (hRule : RuleFormation.{u,v} (shape.recursorEnvironment entries source recursor mode) + shape source recursor mode i ctor) (env : Nat → V) : + interp (shape.recursorAssignment constants source recursor mode) levels env (shape.ruleLhs source recursor mode i ctor) = + interp (shape.recursorAssignment constants source recursor mode) levels env (shape.ruleRhs source recursor mode i ctor) := by + cases mode with + | small => + exact small_rule_eq hRule _ (recursorAssignment_realizes h hE hC hR hmode constants hM) levels env + | large => + exact large_rule_eq_closed h (recursorAssignment_reading h hR constants) hmode hM hn hc hRule.scope env + +end Ix.Theory.Certified.Ordinary.Shape diff --git a/Ix/Theory/Certified/Ordinary/RuleReading.lean b/Ix/Theory/Certified/Ordinary/RuleReading.lean new file mode 100644 index 000000000..ab200fac7 --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/RuleReading.lean @@ -0,0 +1,270 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.RuleChecks + +namespace Ix.Theory.Model + +open SetTheory + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +theorem Assignment.AgreesOn.interp_inserted {entries : Environment β} + {constants reading : Assignment β V} (h : Assignment.AgreesOn entries constants reading) + (levels : List Nat) (sourceLevels : List VLevel) (env : Nat → V) + (shared xs fs ys : List V) {e : AExpr β} (hrefs : e.ReferencesIn entries) : + Model.interp reading levels + (Telescope.extend (Telescope.extend (Telescope.extend (Telescope.extend env shared) xs) fs) ys) + (((e.instL sourceLevels).liftN shared.length (xs.length + ys.length)).liftN fs.length ys.length) = + Model.interp constants (sourceLevels.map (VLevel.eval levels)) (Telescope.extend (Telescope.extend env xs) ys) e := by + rw [interp_liftN, Telescope.skip_middle, interp_liftN, + show xs.length + ys.length = ys.length + xs.length by omega, + Telescope.skip_extend_at, Telescope.skip_middle, interp_instL, h.interp hrefs] + +end Ix.Theory.Model + +namespace Ix.Theory.Certified.Ordinary.Shape + +open Model Model.SetTheory Model.SetTheory.Tower Model.SetModel Inductive + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +noncomputable def recursorCall (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (mode : ElimMode) (m : V) (minors xs fs : List V) (j : Nat) (field : RecursiveField β) : V := + Telescope.curry (mode.motiveLevel.eval levels) + (Telescope.interpret constants (mode.sourceArgs levels) (Telescope.extend env xs) field.domains) fun ys => + Telescope.applyN (shape.recursorAt constants levels env mode m minors) + (field.indices.map (interp constants (mode.sourceArgs levels) (Telescope.extend (Telescope.extend env xs) ys)) ++ + [Telescope.applyN (fs.getD j empty) ys]) + +noncomputable def recursorCalls (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (mode : ElimMode) (m : V) (minors : List V) (ctor : Constructor β) (xs fs : List V) : List V := + ctor.recursive.zipIdx.map fun (field, j) => shape.recursorCall constants levels env mode m minors xs fs j field + +theorem recursorCall_large (shape : Shape β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (m : V) (minors xs fs : List V) (j : Nat) (field : RecursiveField β) + (hD : (shape.container constants (ElimMode.large.sourceArgs levels) env).WF + (shape.level.eval (ElimMode.large.sourceArgs levels))) + (hlarge : (shape.container constants (ElimMode.large.sourceArgs levels) env).LargeElim + (shape.level.eval (ElimMode.large.sourceArgs levels))) : + shape.recursorCall constants levels env .large m minors xs fs j field = + shape.recursiveCall constants (ElimMode.large.sourceArgs levels) env + (ElimMode.large.motiveLevel.eval levels) m minors hD hlarge xs fs j field := by + simp only [recursorCall, recursiveCall, recursorAt_large _ _ _ _ _ _ hD hlarge] + +variable {entries : Environment β} {shape : Shape β} {source recursor : β} + {constants reading : Assignment β V} {levels : List Nat} {mode : ElimMode} + +theorem ruleCall_interp {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : RecursorReading entries shape source recursor mode constants reading) + (hmode : ModeEvidence.{u,v} entries shape mode) (hM : Realizes constants entries) + (hn : levels.length = mode.recUvars shape.universes) {ctor : Constructor β} (hc : ctor ∈ shape.constructors) + {j : Nat} {field : RecursiveField β} (hf : ctor.recursive[j]? = some field) + {ps xs fs minors : List V} {m : V} + (hps : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (fun _ => empty) shape.parameters) ps) + (hxs : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor.fields) xs) + (hfs : fs.length = ctor.recursive.length) + (hm : m ∈ˢ shape.motiveSet constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (mode.motiveLevel.eval levels)) + (hminors : shape.MinorValuesFit constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (mode.motiveLevel.eval levels) m minors) : + interp reading levels + (Telescope.extend (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) (m :: minors)) xs) fs) + (shape.ruleCall recursor mode ctor j field) = + shape.recursorCall constants levels (Telescope.extend (fun _ => empty) ps) mode m minors xs fs j field := by + have hctor := h.constructors ctor hc + have hfield := hctor.2.2.2.2 field (List.mem_of_getElem? hf) + have hplen := FitsS.length_eq hps + have hxlen := FitsS.length_eq hxs + have hmlen : minors.length = shape.constructors.length := by + simpa only [minorTypes, List.length_map, List.length_zipIdx] using FitsS.length_eq hminors + have hcommon : 1 + shape.constructors.length = (m :: minors).length := by simp [hmlen, Nat.add_comm] + have hskip : Valuation.skip (1 + shape.constructors.length) ctor.fields.length + (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) (m :: minors)) xs) = + Telescope.extend (Telescope.extend (fun _ => empty) ps) xs := by + simpa only [← hcommon, hxlen] using Telescope.skip_middle (Telescope.extend (fun _ => empty) ps) (m :: minors) xs + rw [ruleCall, AExpr.interp_lamN, Telescope.curry_lift, ← hfs, Telescope.skip_extend, + Telescope.curry_lift, hskip, Telescope.curry_instL, mode.sourceLevels_eval hn, + hr.agrees.telescope (fun A ha => hfield.1 A (List.mem_append_left _ ha))] + apply Telescope.curry_zero_agree (regime_zeroCondition mode.motiveLevel levels) + intro ys hys + have hylen := FitsS.length_eq hys + have hprefix := interp_parameterVars_middle reading levels (fun _ => empty) (ps ++ [m] ++ minors) (xs ++ fs ++ ys) + simp only [List.length_append, List.length_singleton, Telescope.extend_append, Telescope.extend, + hplen, hxlen, hfs, hmlen, hylen, Nat.add_assoc] at hprefix + have hidx : field.indices.map (fun e => interp reading levels + (Telescope.extend (Telescope.extend (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) + (m :: minors)) xs) fs) ys) + (((e.instL (mode.sourceLevels shape.universes)).liftN (1 + shape.constructors.length) + (ctor.fields.length + field.domains.length)).liftN ctor.recursive.length field.domains.length)) = + field.indices.map (interp constants (mode.sourceArgs levels) + (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs) ys)) := by + apply List.map_congr_left + intro e he + have ht := hr.agrees.interp_inserted levels (mode.sourceLevels shape.universes) + (Telescope.extend (fun _ => empty) ps) (m :: minors) xs fs ys (hfield.1 e (List.mem_append_right _ he)) + simpa only [mode.sourceLevels_eval hn, ← hcommon, hxlen, hfs, hylen] using ht + have hfvar : Telescope.extend (Telescope.extend (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) + (m :: minors)) xs) fs) ys (ctor.recursive.length - 1 - j + field.domains.length) = fs.getD j empty := by + rw [← hfs, ← hylen, Nat.add_comm, Telescope.extend_beyond, + Telescope.extend_getD _ _ j (by rw [hfs]; exact (List.getElem?_eq_some_iff.mp hf).1)] + have hvars := interp_parameterVars_extend reading levels + (Telescope.extend (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) (m :: minors)) xs) fs) ys + rw [hylen] at hvars + simp only [Telescope.extend] at hidx hfvar hvars + simp only [hfs, AExpr.interp_appN, interp, ElimMode.recLevels, eval_params hn, + hr.recursor _ hn, List.map_append, List.map_map, Function.comp_def, List.map_cons, + List.map_nil, hidx, hfvar, hvars, Nat.add_assoc, Telescope.extend, hprefix] + rw [Telescope.applyN_append, Telescope.applyN_append, closedRecursorValue_apply h hmode hM hps hm hminors, + Telescope.applyN_append] + +theorem ruleConstructor_interp {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : ConstructorReading entries shape source constants reading) (hM : Realizes constants entries) + (hn : levels.length = mode.recUvars shape.universes) {i : Nat} {ctor : Constructor β} + (hc : shape.constructors[i]? = some ctor) {ps xs fs minors : List V} (m : V) + (hps : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (fun _ => empty) shape.parameters) ps) + (hxs : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor.fields) xs) + (hfs : shape.RecursiveValuesFit constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor xs fs) + (hmlen : minors.length = shape.constructors.length) : + interp reading levels + (Telescope.extend (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) (m :: minors)) xs) fs) + (shape.ruleConstructor source mode i ctor) = + shape.constructorValue constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor i xs fs := by + have hflen : fs.length = ctor.recursive.length := by simpa using FitsS.length_eq hfs + have ht := constructorVars_interp hr hn hc ps (m :: minors) (xs ++ fs) [] (FitsS.length_eq hps) + rw [← List.append_assoc, constructorClosedValue_apply h hM hc hps hxs hfs] at ht + simpa only [ruleConstructor, List.length_cons, List.length_nil, List.length_append, + hmlen, FitsS.length_eq hxs, hflen, Nat.add_zero, Nat.zero_add, Telescope.extend_append, Telescope.extend, + Nat.add_assoc, Nat.add_comm, Nat.add_left_comm] using ht + +theorem ruleIndices_interp {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : FamilyReading entries shape source constants reading) + (hn : levels.length = mode.recUvars shape.universes) {ctor : Constructor β} (hc : ctor ∈ shape.constructors) + (ps xs fs minors : List V) (m : V) (hxlen : xs.length = ctor.fields.length) + (hflen : fs.length = ctor.recursive.length) (hmlen : minors.length = shape.constructors.length) : + (shape.ruleIndices mode ctor).map (interp reading levels + (Telescope.extend (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) (m :: minors)) xs) fs)) = + ctor.indices.map (interp constants (mode.sourceArgs levels) + (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs)) := by + rw [ruleIndices, List.map_map] + apply List.map_congr_left + intro e he + have ht := hr.agrees.interp_inserted levels (mode.sourceLevels shape.universes) + (Telescope.extend (fun _ => empty) ps) (m :: minors) xs fs [] + ((h.constructors ctor hc).1 e (List.mem_append_right _ he)) + simpa only [mode.sourceLevels_eval hn, List.length_cons, List.length_nil, Nat.add_zero, Nat.zero_add, + hxlen, hflen, hmlen, Telescope.extend, Nat.add_comm, Function.comp_def] using ht + +theorem ruleLhsBody_interp {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : RecursorReading entries shape source recursor mode constants reading) + (hmode : ModeEvidence.{u,v} entries shape mode) (hM : Realizes constants entries) + (hn : levels.length = mode.recUvars shape.universes) {i : Nat} {ctor : Constructor β} + (hc : shape.constructors[i]? = some ctor) {ps xs fs minors : List V} {m : V} + (hps : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (fun _ => empty) shape.parameters) ps) + (hxs : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor.fields) xs) + (hfs : shape.RecursiveValuesFit constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor xs fs) + (hm : m ∈ˢ shape.motiveSet constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (mode.motiveLevel.eval levels)) + (hminors : shape.MinorValuesFit constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (mode.motiveLevel.eval levels) m minors) : + interp reading levels + (Telescope.extend (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) (m :: minors)) xs) fs) + (shape.ruleLhsBody source recursor mode i ctor) = + Telescope.applyN (shape.recursorAt constants levels (Telescope.extend (fun _ => empty) ps) mode m minors) + (ctor.indices.map (interp constants (mode.sourceArgs levels) (Telescope.extend (Telescope.extend (fun _ => empty) ps) xs)) ++ + [shape.constructorValue constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor i xs fs]) := by + have hflen : fs.length = ctor.recursive.length := by simpa using FitsS.length_eq hfs + have hmlen : minors.length = shape.constructors.length := by + simpa only [minorTypes, List.length_map, List.length_zipIdx] using FitsS.length_eq hminors + have hprefix := interp_parameterVars_middle reading levels (fun _ => empty) (ps ++ [m] ++ minors) (xs ++ fs) + simp only [List.length_append, List.length_singleton, Telescope.extend_append, Telescope.extend, + FitsS.length_eq hps, FitsS.length_eq hxs, hflen, hmlen] at hprefix + have hcv := ruleConstructor_interp h hr.toConstructorReading hM hn hc m hps hxs hfs hmlen + have hidx := ruleIndices_interp h hr.toFamilyReading hn (List.mem_of_getElem? hc) + ps xs fs minors m (FitsS.length_eq hxs) hflen hmlen + simp only [Telescope.extend] at hcv hidx + rw [ruleLhsBody, AExpr.interp_appN] + simp only [interp, ElimMode.recLevels, eval_params hn, hr.recursor _ hn, List.map_append, + List.map_cons, List.map_nil, hcv, hidx, Telescope.extend, hprefix] + rw [Telescope.applyN_append, Telescope.applyN_append, closedRecursorValue_apply h hmode hM hps hm hminors, + Telescope.applyN_append] + +theorem ruleRhsBody_interp {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : RecursorReading entries shape source recursor mode constants reading) + (hmode : ModeEvidence.{u,v} entries shape mode) (hM : Realizes constants entries) + (hn : levels.length = mode.recUvars shape.universes) {i : Nat} {ctor : Constructor β} + (hc : shape.constructors[i]? = some ctor) {ps xs fs minors : List V} {m : V} + (hps : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (fun _ => empty) shape.parameters) ps) + (hxs : FitsS (Telescope.interpret constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor.fields) xs) + (hfs : shape.RecursiveValuesFit constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor xs fs) + (hm : m ∈ˢ shape.motiveSet constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (mode.motiveLevel.eval levels)) + (hminors : shape.MinorValuesFit constants (mode.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (mode.motiveLevel.eval levels) m minors) : + interp reading levels + (Telescope.extend (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) (m :: minors)) xs) fs) + (shape.ruleRhsBody recursor mode i ctor) = + Telescope.applyN (minors.getD i empty) + (xs ++ fs ++ shape.recursorCalls constants levels (Telescope.extend (fun _ => empty) ps) mode m minors ctor xs fs) := by + have hflen : fs.length = ctor.recursive.length := by simpa using FitsS.length_eq hfs + have hmlen : minors.length = shape.constructors.length := by + simpa only [minorTypes, List.length_map, List.length_zipIdx] using FitsS.length_eq hminors + have hmvar : Telescope.extend + (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) (m :: minors)) xs) fs + (shape.constructors.length - 1 - i + ctor.fields.length + ctor.recursive.length) = minors.getD i empty := by + have ht := (Telescope.extend_beyond (Telescope.extend (Valuation.cons m (Telescope.extend (fun _ => empty) ps)) minors) + (xs ++ fs) (minors.length - 1 - i)).trans + (Telescope.extend_getD _ minors i (by rw [hmlen]; exact (List.getElem?_eq_some_iff.mp hc).1)) + simpa only [List.length_append, hmlen, FitsS.length_eq hxs, hflen, + Telescope.extend_append, Telescope.extend, Nat.add_comm, Nat.add_assoc, Nat.add_left_comm] using ht + have hfields := interp_parameterVars_extend reading levels + (Telescope.extend (Telescope.extend (fun _ => empty) ps) (m :: minors)) (xs ++ fs) + simp only [List.length_append, FitsS.length_eq hxs, hflen, Telescope.extend_append] at hfields + have hcalls : (ctor.recursive.zipIdx.map fun (field, j) => shape.ruleCall recursor mode ctor j field).map + (interp reading levels (Telescope.extend + (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) (m :: minors)) xs) fs)) = + shape.recursorCalls constants levels (Telescope.extend (fun _ => empty) ps) mode m minors ctor xs fs := by + apply List.ext_getElem? + intro j + simp only [recursorCalls, List.getElem?_map, List.getElem?_zipIdx] + cases hf : ctor.recursive[j]? with + | none => rfl + | some field => + simp only [Option.map_some, Nat.zero_add] + exact congrArg some (ruleCall_interp h hr hmode hM hn (List.mem_of_getElem? hc) hf hps hxs hflen hm hminors) + rw [ruleRhsBody, AExpr.interp_appN] + simp only [interp, hmvar, List.map_append, hfields, hcalls] + +theorem large_ruleBody_eq {store : Store β} (h : CheckedShape.{u,v} entries store source shape) + (hr : RecursorReading entries shape source recursor .large constants reading) + (hmode : ModeEvidence.{u,v} entries shape .large) (hM : Realizes constants entries) + (hn : levels.length = ElimMode.large.recUvars shape.universes) {i : Nat} {ctor : Constructor β} + (hc : shape.constructors[i]? = some ctor) {ps xs fs minors : List V} {m : V} + (hps : FitsS (Telescope.interpret constants (ElimMode.large.sourceArgs levels) (fun _ => empty) shape.parameters) ps) + (hxs : FitsS (Telescope.interpret constants (ElimMode.large.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor.fields) xs) + (hfs : shape.RecursiveValuesFit constants (ElimMode.large.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) ctor xs fs) + (hm : m ∈ˢ shape.motiveSet constants (ElimMode.large.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (ElimMode.large.motiveLevel.eval levels)) + (hminors : shape.MinorValuesFit constants (ElimMode.large.sourceArgs levels) (Telescope.extend (fun _ => empty) ps) + (ElimMode.large.motiveLevel.eval levels) m minors) : + interp reading levels + (Telescope.extend (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) (m :: minors)) xs) fs) + (shape.ruleLhsBody source recursor .large i ctor) = + interp reading levels + (Telescope.extend (Telescope.extend (Telescope.extend (Telescope.extend (fun _ => empty) ps) (m :: minors)) xs) fs) + (shape.ruleRhsBody recursor .large i ctor) := by + rw [ruleLhsBody_interp h hr hmode hM hn hc hps hxs hfs hm hminors, + ruleRhsBody_interp h hr hmode hM hn hc hps hxs hfs hm hminors] + have hΓ := h.parameters.valid constants hM (ElimMode.large.sourceArgs levels) + (Context.valid_nil constants (ElimMode.large.sourceArgs levels) (fun _ => empty)) hps + have hD := container_wf h hM hΓ + have hlarge := container_large hmode hM hΓ + rw [recursorAt_large _ _ _ _ _ _ hD hlarge] + simp only [recursorCalls, recursorCall_large _ _ _ _ _ _ _ _ _ _ hD hlarge] + exact largeValue_iota h hM hΓ hm hminors hD hlarge hc hxs hfs + +end Ix.Theory.Certified.Ordinary.Shape diff --git a/Ix/Theory/Certified/Ordinary/Shape.lean b/Ix/Theory/Certified/Ordinary/Shape.lean new file mode 100644 index 000000000..7ae8b8037 --- /dev/null +++ b/Ix/Theory/Certified/Ordinary/Shape.lean @@ -0,0 +1,212 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Telescope +import Ix.Theory.Model.Support +import Ix.Theory.Store + +/-! +An executable producer for the ordinary container shape class. All ordinary +fields precede recursive fields. A recursive field is a dependent function +over checked external domains returning this family with the same parameters +and checked indices. Its domains and indices cannot depend on other recursive +fields. Exact source comparison, formation, and universe bounds are checked +here; publishing recursor equations requires the later realization producer. +-/ + +namespace Ix.Theory.Certified.Ordinary + +open Model + +universe u v + +structure RecursiveField (β : Type u) where + domains : List (AExpr β) + indices : List (AExpr β) +deriving DecidableEq + +structure Constructor (β : Type u) where + fields : List (AExpr β) + recursive : List (RecursiveField β) + indices : List (AExpr β) +deriving DecidableEq + +structure Shape (β : Type u) where + universes : Nat + parameters : List (AExpr β) + indices : List (AExpr β) + level : VLevel + constructors : List (Constructor β) +deriving DecidableEq + +variable {β : Type u} + +def parameterVars (offset : Nat) : Nat → List (AExpr β) + | 0 => [] + | n + 1 => .bvar (offset + n) :: parameterVars offset n + +@[simp] theorem erase_parameterVars (offset count : Nat) : + (parameterVars (β := β) offset count).map AExpr.erase = VExpr.bvarRevRange offset count := by + induction count <;> simp_all [parameterVars, VExpr.bvarRevRange, AExpr.erase] + +def Shape.familyApp (shape : Shape β) (source : β) (offset : Nat) + (indices : List (AExpr β)) : AExpr β := + .appN (.const (.member source 0) (VLevel.params shape.universes)) + (parameterVars offset shape.parameters.length ++ indices) + +def Shape.type (shape : Shape β) : AExpr β := + .forallN .never (shape.parameters ++ shape.indices) (.sort shape.level) + +def RecursiveField.type (field : RecursiveField β) (shape : Shape β) + (source : β) (ordinaryFields : Nat) : AExpr β := + .forallN (zeroCondition shape.level) field.domains + (shape.familyApp source (ordinaryFields + field.domains.length) field.indices) + +def recursiveTypesFrom (shape : Shape β) (source : β) (ordinaryFields : Nat) + (fields : List (RecursiveField β)) (offset : Nat) : List (AExpr β) := + (fields.zipIdx offset).map fun (field, previous) => + (field.type shape source ordinaryFields).liftN previous + +def Constructor.recursiveTypes (ctor : Constructor β) (shape : Shape β) (source : β) : List (AExpr β) := + recursiveTypesFrom shape source ctor.fields.length ctor.recursive 0 + +def Constructor.type (ctor : Constructor β) (shape : Shape β) (source : β) : AExpr β := + .forallN (zeroCondition shape.level) shape.parameters <| + .forallN (zeroCondition shape.level) ctor.fields <| + .forallN (zeroCondition shape.level) (ctor.recursiveTypes shape source) <| + shape.familyApp source (ctor.fields.length + ctor.recursive.length) + (ctor.indices.map (AExpr.liftN ctor.recursive.length ·)) + +def Constructor.source (ctor : Constructor β) (shape : Shape β) (source : β) : Ctor β := + ⟨shape.universes, shape.parameters.length, ctor.fields.length + ctor.recursive.length, + (ctor.type shape source).erase, .safe⟩ + +def Shape.source (shape : Shape β) (source : β) : Const β := + .induct shape.universes shape.parameters.length shape.indices.length shape.type.erase + (shape.constructors.map (Constructor.source · shape source)) .safe + +def Shape.references (shape : Shape β) (source : β) : List (ConstRef β) := + .member source 0 :: (List.range shape.constructors.length).map (.ctor source 0 ·) + +def Shape.parameterContext (shape : Shape β) : Context β := Telescope.context [] shape.parameters + +def Constructor.context (ctor : Constructor β) (shape : Shape β) : Context β := + Telescope.context shape.parameterContext ctor.fields + +def RecursiveEvidence (entries : Environment β) (shape : Shape β) (ctor : Constructor β) + (field : RecursiveField β) : Prop := + (∀ e ∈ field.domains ++ field.indices, e.ReferencesIn entries) ∧ + Telescope.Formed.{u,v} entries (ctor.context shape) field.domains ∧ + TelescopeBound.{u,v} entries (ctor.context shape) field.domains (some shape.level) ∧ + ArgumentsFit.{u,v} entries (Telescope.context (ctor.context shape) field.domains) + (Telescope.lift (ctor.fields.length + field.domains.length) shape.indices) field.indices + +def ConstructorEvidence (entries : Environment β) (shape : Shape β) (ctor : Constructor β) : Prop := + (∀ e ∈ ctor.fields ++ ctor.indices, e.ReferencesIn entries) ∧ + Telescope.Formed.{u,v} entries shape.parameterContext ctor.fields ∧ + TelescopeBound.{u,v} entries shape.parameterContext ctor.fields (some shape.level) ∧ + ArgumentsFit.{u,v} entries (ctor.context shape) + (Telescope.lift ctor.fields.length shape.indices) ctor.indices ∧ + ∀ field ∈ ctor.recursive, RecursiveEvidence.{u,v} entries shape ctor field + +/-- This is checked shape/formation evidence, not an admitted environment or +a premise asserting that an inductive has a model. -/ +structure CheckedShape (entries : Environment β) (store : Store β) (source : β) + (shape : Shape β) : Prop where + exactSource : store.blocks source = some ⟨[shape.source source]⟩ + fresh : ∀ r ∈ shape.references source, entries r = none + references : ∀ e ∈ shape.parameters ++ shape.indices, e.ReferencesIn entries + scope : shape.type.Scope shape.universes 0 + constructorScope : ∀ ctor ∈ shape.constructors, (ctor.type shape source).Scope shape.universes 0 + parameters : Telescope.Formed.{u,v} entries [] shape.parameters + indices : Telescope.Formed.{u,v} entries shape.parameterContext shape.indices + constructors : ∀ ctor ∈ shape.constructors, ConstructorEvidence.{u,v} entries shape ctor + +structure RecursiveWitness (β : Type u) where + domains : List (DomainWitness β) + indices : List (TypingWitness β) + +structure ConstructorWitness (β : Type u) where + fields : List (DomainWitness β) + indices : List (TypingWitness β) + recursive : List (RecursiveWitness β) + +structure ShapeWitness (β : Type u) where + shape : Shape β + parameters : List (DomainWitness β) + indices : List (DomainWitness β) + constructors : List (ConstructorWitness β) + +variable [DecidableEq β] + +def checkRecursive (fuel : Nat) (entries : Environment β) (shape : Shape β) + (ctor : Constructor β) : (fields : List (RecursiveField β)) → List (RecursiveWitness β) → + Option (CheckedClaim.{u} (∀ field ∈ fields, RecursiveEvidence.{u,v} entries shape ctor field)) + | [], [] => some ⟨by simp⟩ + | field :: fields, witness :: witnesses => + if hrefs : ∀ e ∈ field.domains ++ field.indices, e.ReferencesIn entries then do + let domains ← verifyTelescope.{u,v} fuel shape.universes entries (some shape.level) + (ctor.context shape) field.domains witness.domains + let indices ← verifyArguments.{u,v} fuel shape.universes entries + (Telescope.context (ctor.context shape) field.domains) + (Telescope.lift (ctor.fields.length + field.domains.length) shape.indices) + field.indices witness.indices + let rest ← checkRecursive fuel entries shape ctor fields witnesses + return ⟨by + intro field' hf + rcases List.mem_cons.mp hf with rfl | hf + · exact ⟨hrefs, domains.down.1, domains.down.2, indices.down⟩ + · exact rest.down field' hf⟩ + else none + | _, _ => none + +def checkConstructors (fuel : Nat) (entries : Environment β) (shape : Shape β) : + (ctors : List (Constructor β)) → List (ConstructorWitness β) → + Option (CheckedClaim.{u} (∀ ctor ∈ ctors, ConstructorEvidence.{u,v} entries shape ctor)) + | [], [] => some ⟨by simp⟩ + | ctor :: ctors, witness :: witnesses => + if hrefs : ∀ e ∈ ctor.fields ++ ctor.indices, e.ReferencesIn entries then do + let fields ← verifyTelescope.{u,v} fuel shape.universes entries (some shape.level) + shape.parameterContext ctor.fields witness.fields + let indices ← verifyArguments.{u,v} fuel shape.universes entries (ctor.context shape) + (Telescope.lift ctor.fields.length shape.indices) ctor.indices witness.indices + let recursive ← checkRecursive fuel entries shape ctor ctor.recursive witness.recursive + let rest ← checkConstructors fuel entries shape ctors witnesses + return ⟨by + intro ctor' hc + rcases List.mem_cons.mp hc with rfl | hc + · exact ⟨hrefs, fields.down.1, fields.down.2, indices.down, recursive.down⟩ + · exact rest.down ctor' hc⟩ + else none + | _, _ => none + +def checkShape (fuel : Nat) (entries : Environment β) (store : Store β) (source : β) + (witness : ShapeWitness β) : + Option (CheckedClaim.{u} (CheckedShape.{u,v} entries store source witness.shape)) := + let shape := witness.shape + if hs : store.blocks source = some ⟨[shape.source source]⟩ then + if hfresh : ∀ r ∈ shape.references source, entries r = none then + if hrefs : ∀ e ∈ shape.parameters ++ shape.indices, e.ReferencesIn entries then + if hscope : shape.type.Scope shape.universes 0 then + if hctors : ∀ ctor ∈ shape.constructors, (ctor.type shape source).Scope shape.universes 0 then do + let parameters ← verifyTelescope.{u,v} fuel shape.universes entries none [] + shape.parameters witness.parameters + let indices ← verifyTelescope.{u,v} fuel shape.universes entries none + shape.parameterContext shape.indices witness.indices + let constructors ← checkConstructors fuel entries shape shape.constructors witness.constructors + return ⟨⟨hs, hfresh, hrefs, hscope, hctors, + parameters.down.1, indices.down.1, constructors.down⟩⟩ + else none + else none + else none + else none + else none + +theorem checkShape_sound {fuel : Nat} {entries : Environment β} {store : Store β} {source : β} + {witness : ShapeWitness β} {result} + (_ : checkShape.{u,v} fuel entries store source witness = some result) : + CheckedShape.{u,v} entries store source witness.shape := result.down + +end Ix.Theory.Certified.Ordinary diff --git a/Ix/Theory/Certified/Policy.lean b/Ix/Theory/Certified/Policy.lean new file mode 100644 index 000000000..0b69e92aa --- /dev/null +++ b/Ix/Theory/Certified/Policy.lean @@ -0,0 +1,131 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Annotated +import Ix.Theory.Store + +/-! +# Primitive identity and the initial declaration policy + +The profile admits ordinary safe definitions, theorems and opaque declarations +only with a checked body. Initialization excludes object-language axioms; +the separate standard admission branch realizes supported exact schemas. Empty-inductive +primitives are selected by exact references and complete declaration shapes. +This is the policy/reading layer; the semantic checker must validate each body +before publishing a declaration as accepted. +-/ + +namespace Ix.Theory.Certified + +open Model + +universe u +variable {β : Type u} + +/-- The public configuration fixes these references. Diagnostic names and +semantic equality cannot select primitives or identify authenticated bytes. -/ +structure PrimitiveSignature (β : Type u) where + falseType : ConstRef β + falseElim : ConstRef β + distinct : falseType ≠ falseElim + natType : Option (ConstRef β) := none + +namespace PrimitiveSignature + +def falseExpr (signature : PrimitiveSignature β) : VExpr β := + .const signature.falseType [] + +/-- `False.rec.{u} : (motive : False → Sort u) → (x : False) → motive x`. -/ +def falseElimType (signature : PrimitiveSignature β) : VExpr β := + .forallE (.forallE signature.falseExpr (.sort (.param 0))) + (.forallE signature.falseExpr (.app (.bvar 1) (.bvar 0))) + +def falseDeclaration : Const β := .induct 0 0 0 (.sort .zero) [] .safe + +def falseElimDeclaration (signature : PrimitiveSignature β) : Const β := + .recursor 1 0 0 1 0 signature.falseElimType [] false .safe + +/-- A decidable match on the whole declaration, including rule and constructor +lists, safety, universe arity, and recursor counts. -/ +def validate [DecidableEq β] (signature : PrimitiveSignature β) (store : Store β) : Bool := + store.lookup signature.falseType == some falseDeclaration && + store.lookup signature.falseElim == some signature.falseElimDeclaration + +theorem validate_iff [DecidableEq β] (signature : PrimitiveSignature β) (store : Store β) : + signature.validate store = true ↔ + store.lookup signature.falseType = some falseDeclaration ∧ + store.lookup signature.falseElim = some signature.falseElimDeclaration := by + simp [validate] + +def rename (mapping : AddressEquiv β γ) (signature : PrimitiveSignature β) : + PrimitiveSignature γ where + falseType := signature.falseType.rename mapping + falseElim := signature.falseElim.rename mapping + distinct := fun h => signature.distinct (ConstRef.rename_injective mapping h) + natType := signature.natType.map (ConstRef.rename mapping) + +@[simp] theorem falseExpr_rename (mapping : AddressEquiv β γ) + (signature : PrimitiveSignature β) : + (signature.rename mapping).falseExpr = signature.falseExpr.rename mapping := rfl + +@[simp] theorem falseElimType_rename (mapping : AddressEquiv β γ) + (signature : PrimitiveSignature β) : + (signature.rename mapping).falseElimType = signature.falseElimType.rename mapping := rfl + +/-- The fixed annotation of the eliminator's type. The motive space is always +a positive sort; the remaining binders follow the result universe parameter. -/ +def falseElimReading (signature : PrimitiveSignature β) : AExpr β := + .forallE (.param 0) + (.forallE .never (.const signature.falseType []) (.sort (.param 0))) + (.forallE (.param 0) (.const signature.falseType []) (.app (.bvar 1) (.bvar 0))) + +@[simp] theorem erase_falseElimReading (signature : PrimitiveSignature β) : + signature.falseElimReading.erase = signature.falseElimType := rfl + +theorem falseElimReading_scoped (signature : PrimitiveSignature β) : + signature.falseElimReading.Scope 1 0 := by + simp [falseElimReading, AExpr.Scope, PropWhen.WF, PropWhen.param, VLevel.WF] + +end PrimitiveSignature + +/-- Data retained only after the declaration-kind and structural checks. +The body has not yet passed the semantic type checker. -/ +structure DefinitionReading (β : Type u) where + universes : Nat + kind : DefKind + type : AExpr β + body : AExpr β + typeScope : type.Scope universes 0 + bodyScope : body.Scope universes 0 + +def DefinitionReading.erase (entry : DefinitionReading β) : Const β := + .defn entry.universes entry.kind entry.type.erase entry.body.erase .safe + +/-- The definition-like declaration reading branch. Missing +opaque bodies, arbitrary axioms, unsafe declarations, and unmodeled primitive +kinds cannot acquire a successful reading through this function. -/ +def readDefinition? (source : Const β) (typeTree bodyTree : AnnotationTree) : + Option { entry : DefinitionReading β // entry.erase = source } := + match source with + | .defn n kind type body .safe => do + let type' ← readAnnotations? n 0 type typeTree + let body' ← readAnnotations? n 0 body bodyTree + return ⟨⟨n, kind, type'.val, body'.val, type'.property.2, body'.property.2⟩, + by simp [DefinitionReading.erase, type'.property.1, body'.property.1]⟩ + | _ => none + +theorem axiom_rejected (n : Nat) (type : VExpr β) (safety : Safety) + (typeTree bodyTree : AnnotationTree) : + readDefinition? (.axiom n type safety) typeTree bodyTree = none := rfl + +theorem unsafe_definition_rejected (n : Nat) (kind : DefKind) (type body : VExpr β) + (typeTree bodyTree : AnnotationTree) : + readDefinition? (.defn n kind type body .unsafe) typeTree bodyTree = none := rfl + +theorem partial_definition_rejected (n : Nat) (kind : DefKind) (type body : VExpr β) + (typeTree bodyTree : AnnotationTree) : + readDefinition? (.defn n kind type body .partial) typeTree bodyTree = none := rfl + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Prelude.lean b/Ix/Theory/Certified/Prelude.lean new file mode 100644 index 000000000..6fb0fd351 --- /dev/null +++ b/Ix/Theory/Certified/Prelude.lean @@ -0,0 +1,247 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Policy +import Ix.Theory.Model.Extension + +namespace Ix.Theory.Certified + +open Model Model.SetTheory Model.SetModel + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +namespace PrimitiveSignature + +variable (signature : PrimitiveSignature β) + +def falseEntry : ConstantEntry β := ⟨0, .sort .zero, none, [], []⟩ +def falseElimEntry : ConstantEntry β := ⟨1, signature.falseElimReading, none, [], []⟩ + +def environment [DecidableEq β] : Environment β := fun r => + if r = signature.falseType then some falseEntry + else if r = signature.falseElim then some signature.falseElimEntry + else none + +@[simp] theorem environment_false [DecidableEq β] : + signature.environment signature.falseType = some falseEntry := by + simp [environment] + +@[simp] theorem environment_falseElim [DecidableEq β] : + signature.environment signature.falseElim = some signature.falseElimEntry := by + simp [environment, Ne.symm signature.distinct] + +theorem environment_equations [DecidableEq β] {r : ConstRef β} {entry : ConstantEntry β} + (h : signature.environment r = some entry) : entry.equations = [] := by + unfold environment at h + split at h + · cases Option.some.inj h; rfl + · split at h + · cases Option.some.inj h; rfl + · contradiction + +def Present (entries : Environment β) : Prop := + entries signature.falseType = some falseEntry ∧ + entries signature.falseElim = some signature.falseElimEntry + +theorem environment_facts [DecidableEq β] {r : ConstRef β} {entry : ConstantEntry β} + (h : signature.environment r = some entry) : entry.facts = [] := by + unfold environment at h + split at h + · cases Option.some.inj h; rfl + · split at h + · cases Option.some.inj h; rfl + · contradiction + +theorem present_environment [DecidableEq β] : signature.Present signature.environment := + ⟨signature.environment_false, signature.environment_falseElim⟩ + +theorem Present.insert [DecidableEq β] {entries : Environment β} {r : ConstRef β} + {entry : ConstantEntry β} (h : signature.Present entries) (fresh : entries r = none) : + signature.Present (entries.insert r entry) := + ⟨Environment.insert_old fresh h.1, Environment.insert_old fresh h.2⟩ + +/-- Compatibility fixes primitive values in addition to declaration membership. +In particular, membership in Prop cannot choose the meaning of False. -/ +structure Compatible (entries : Environment β) (constants : Assignment β V) : Prop where + realizes : Realizes constants entries + falseValue : ∀ levels, constants signature.falseType levels = empty + falseElimValue : ∀ levels, + constants signature.falseElim levels = Model.falseElimValue (levels.getD 0 0) + +theorem Compatible.extend {entries entries' : Environment β} + {constants constants' : Assignment β V} + (h : signature.Compatible entries constants) (present : signature.Present entries) + (hM : Realizes constants' entries') + (agree : Assignment.AgreesOn entries constants constants') : + signature.Compatible entries' constants' where + realizes := hM + falseValue levels := (agree _ _ present.1 levels).trans (h.falseValue levels) + falseElimValue levels := (agree _ _ present.2 levels).trans (h.falseElimValue levels) + +noncomputable def assignment [DecidableEq β] : Assignment β V := fun r levels => + if r = signature.falseType then falseValue + else if r = signature.falseElim then falseElimValue (levels.getD 0 0) + else empty + +@[simp] theorem assignment_false [DecidableEq β] (levels : List Nat) : + signature.assignment signature.falseType levels = (empty : V) := by + simp [assignment, falseValue] + +@[simp] theorem assignment_falseElim [DecidableEq β] (levels : List Nat) : + signature.assignment signature.falseElim levels = (falseElimValue (levels.getD 0 0) : V) := by + simp [assignment, Ne.symm signature.distinct] + +theorem interp_falseElimReading (constants : Assignment β V) + (hfalse : constants signature.falseType [] = empty) + (levels : List Nat) (env : Nat → V) : + interp constants levels env signature.falseElimReading = + (Model.falseElimType (levels.getD 0 0) : V) := by + have hz : regime (.param 0) levels = 0 ↔ levels.getD 0 0 = 0 := + regime_zeroCondition (.param 0) levels + simp only [falseElimReading, interp, List.map_nil, hfalse, regime_never, + VLevel.eval, Valuation.cons_succ, Valuation.cons_zero, Model.falseElimType] + have hdom : piR 1 (empty : V) (fun _ => univ (levels.getD 0 0)) = + piR (levels.getD 0 0 + 1) empty (fun _ => univ (levels.getD 0 0)) := + piR_zero_agree (by simp) (fun _ _ => rfl) + rw [hdom] + apply piR_zero_agree hz + intro motive _ + exact piR_zero_agree hz (fun _ _ => rfl) + +theorem wellDenoted_falseElimReading (constants : Assignment β V) + (hfalse : constants signature.falseType [] = empty) + (levels : List Nat) (env : Nat → V) : + WellDenoted constants levels env signature.falseElimReading := by + have hz : regime (.param 0) levels = 0 ↔ levels.getD 0 0 = 0 := + regime_zeroCondition (.param 0) levels + simp only [falseElimReading, WellDenoted, interp, List.map_nil, hfalse, + regime_never, VLevel.eval, Valuation.cons_succ, Valuation.cons_zero] + refine ⟨⟨trivial, fun _ _ => trivial, levels.getD 0 0 + 1, by simp, + fun _ _ => univ_mem_univ _⟩, ?_, levels.getD 0 0, hz, ?_⟩ + · intro motive _ + exact ⟨trivial, fun x hx => (not_mem_empty x hx).elim, levels.getD 0 0, hz, + fun x hx => (not_mem_empty x hx).elim⟩ + · intro motive _ + have hpi := piR_mem_univ (V := V) (a := 0) (b := levels.getD 0 0) + (B := fun x => app motive x) (empty_mem_univ 0) + (fun x hx => (not_mem_empty x hx).elim) + rw [← piR_zero_agree hz (fun _ _ => rfl)] at hpi + have hi : VLevel.natIMax 0 (levels.getD 0 0) = levels.getD 0 0 := by + unfold VLevel.natIMax + split + · exact Eq.symm ‹_ = 0› + · exact Nat.zero_max _ + simpa only [hi] using hpi + +/-- A produced interpretation of the fixed, two-entry prelude. All later +declaration extensions must preserve these exact values. -/ +theorem realizes [DecidableEq β] : + Realizes (signature.assignment : Assignment β V) signature.environment := by + constructor + · intro r entry h levels _ env + unfold environment at h + split at h + next hr => + cases Option.some.inj h + exact trivial + next hr => + split at h + next he => + cases Option.some.inj h + exact signature.wellDenoted_falseElimReading _ (signature.assignment_false []) levels env + next => contradiction + · intro r entry h levels _ env + unfold environment at h + split at h + next hr => + subst r + cases Option.some.inj h + simpa only [assignment_false, falseEntry, interp, VLevel.eval] using + (empty_mem_univ 0 : (empty : V) ∈ˢ univ 0) + next hr => + split at h + next he => + subst r + cases Option.some.inj h + simp only [falseElimEntry, assignment_falseElim, + interp_falseElimReading _ _ (signature.assignment_false [])] + exact falseElimValue_mem _ + next => contradiction + · intro r entry h body hb levels _ env + unfold environment at h + split at h + · cases Option.some.inj h; contradiction + · split at h + · cases Option.some.inj h; contradiction + · contradiction + + · intro r entry h body hb levels _ env + unfold environment at h + split at h + · cases Option.some.inj h; contradiction + · split at h + · cases Option.some.inj h; contradiction + · contradiction + + · intro r entry h equation he + simp only [signature.environment_equations h, List.not_mem_nil] at he + + · intro r entry h fact hf + simp only [signature.environment_facts h, List.not_mem_nil] at hf + +theorem compatible_assignment [DecidableEq β] : + signature.Compatible signature.environment (signature.assignment : Assignment β V) := + ⟨signature.realizes, signature.assignment_false, signature.assignment_falseElim⟩ + +theorem environment_wf [DecidableEq β] : signature.environment.WF := by + have hrefs : signature.falseElimReading.ReferencesIn signature.environment := by + intro r hr + simp only [falseElimReading, AExpr.references, List.append_nil, + List.mem_append, List.mem_singleton, or_self] at hr + subst r + simp + constructor + · intro r entry h + unfold environment at h + split at h + · cases Option.some.inj h; exact trivial + · split at h + · cases Option.some.inj h; exact signature.falseElimReading_scoped + · contradiction + · intro r entry h body hb + unfold environment at h + split at h + · cases Option.some.inj h; contradiction + · split at h + · cases Option.some.inj h; contradiction + · contradiction + · intro r entry h + unfold environment at h + split at h + · cases Option.some.inj h + simp [falseEntry, AExpr.ReferencesIn, AExpr.references] + · split at h + · cases Option.some.inj h; exact hrefs + · contradiction + · intro r entry h body hb + unfold environment at h + split at h + · cases Option.some.inj h; contradiction + · split at h + · cases Option.some.inj h; contradiction + · contradiction + · intro r entry h equation he + simp only [signature.environment_equations h, List.not_mem_nil] at he + · intro r entry h equation he + simp only [signature.environment_equations h, List.not_mem_nil] at he + · intro r entry h fact hf + simp only [signature.environment_facts h, List.not_mem_nil] at hf + · intro r entry h fact hf + simp only [signature.environment_facts h, List.not_mem_nil] at hf + +end PrimitiveSignature + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/PropWhen.lean b/Ix/Theory/Certified/PropWhen.lean new file mode 100644 index 000000000..0c164d66a --- /dev/null +++ b/Ix/Theory/Certified/PropWhen.lean @@ -0,0 +1,330 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.VLevel + +/-! +# Checked zero conditions for anonymous universe parameters + +A condition is either never true or says that a finite set of parameter +indices are all zero. Strictly increasing lists give one representation for +each finite set. The empty set is the condition that always holds. + +These conditions describe only whether a level is zero. They do not identify +levels: for example, `Sort 1` and `Sort 2` both have condition `never`. +The decoder checks bounds independently, including for conditions that are +always false. Level well-formedness must also be checked on the source level. +-/ + +namespace Ix.Theory.Certified + +namespace IndexSet + +def insert (x : Nat) : List Nat → List Nat + | [] => [x] + | y :: ys => + if x < y then x :: y :: ys + else if x = y then y :: ys + else y :: insert x ys + +@[simp] theorem mem_insert {z x : Nat} {xs : List Nat} : + z ∈ insert x xs ↔ z = x ∨ z ∈ xs := by + induction xs with + | nil => simp [insert] + | cons y ys ih => + simp only [insert] + split + · simp + · split + · subst x; simp + · simp only [List.mem_cons, ih, or_left_comm] + +theorem sorted_insert {x : Nat} {xs : List Nat} + (h : xs.Pairwise (· < ·)) : (insert x xs).Pairwise (· < ·) := by + induction xs with + | nil => simp [insert] + | cons y ys ih => + obtain ⟨hy, hs⟩ := List.pairwise_cons.mp h + simp only [insert] + split + next hxy => + apply List.pairwise_cons.mpr + exact ⟨fun z hz => (List.mem_cons.mp hz).elim (fun e => e ▸ hxy) + (fun hz => Nat.lt_trans hxy (hy z hz)), h⟩ + next hxy => + split + · exact h + next hne => + apply List.pairwise_cons.mpr + refine ⟨?_, ih hs⟩ + intro z hz + rcases mem_insert.mp hz with rfl | hz + · omega + · exact hy z hz + +def normalize : List Nat → List Nat + | [] => [] + | x :: xs => insert x (normalize xs) + +@[simp] theorem mem_normalize {z : Nat} {xs : List Nat} : + z ∈ normalize xs ↔ z ∈ xs := by + induction xs <;> simp [normalize, *] + +theorem sorted_normalize (xs : List Nat) : (normalize xs).Pairwise (· < ·) := by + induction xs with + | nil => exact .nil + | cons x xs ih => exact sorted_insert ih + +/-- Equality of increasing lists is determined by their members. -/ +theorem eq_of_members {xs ys : List Nat} + (hx : xs.Pairwise (· < ·)) (hy : ys.Pairwise (· < ·)) + (hm : ∀ i, i ∈ xs ↔ i ∈ ys) : xs = ys := by + induction xs generalizing ys with + | nil => + symm + exact List.eq_nil_iff_forall_not_mem.mpr fun i hi => by simpa using (hm i).mpr hi + | cons x xs ih => + cases ys with + | nil => simpa using (hm x).mp (List.mem_cons_self ..) + | cons y ys => + obtain ⟨hxmin, hxs⟩ := List.pairwise_cons.mp hx + obtain ⟨hymin, hys⟩ := List.pairwise_cons.mp hy + have hxy : x = y := by + have hxmem := (hm x).mp (List.mem_cons_self ..) + have hymem := (hm y).mpr (List.mem_cons_self ..) + simp only [List.mem_cons] at hxmem hymem + rcases hxmem with h | h + · exact h + rcases hymem with h' | h' + · exact h'.symm + have := hxmin y h' + have := hymin x h + omega + subst y + congr 1 + apply ih hxs hys + intro i + have hxi : i ∈ xs → i ≠ x := fun hi e => by + have := hxmin i hi + omega + have hyi : i ∈ ys → i ≠ x := fun hi e => by + have := hymin i hi + omega + specialize hm i + simp only [List.mem_cons] at hm + by_cases hie : i = x + · subst i; simp_all + · simpa [hie] using hm + +end IndexSet + +inductive PropWhen where + | never + | allZero (params : List Nat) (sorted : params.Pairwise (· < ·)) +deriving DecidableEq, Repr + +namespace PropWhen + +def always : PropWhen := .allZero [] .nil + +def ofList (params : List Nat) : PropWhen := + .allZero (IndexSet.normalize params) (IndexSet.sorted_normalize params) + +def param (i : Nat) : PropWhen := .allZero [i] (by simp) + +def toRaw : PropWhen → Option (List Nat) + | .never => none + | .allZero ps _ => some ps + +theorem toRaw_injective {p q : PropWhen} (h : p.toRaw = q.toRaw) : p = q := by + cases p <;> cases q <;> simp_all [toRaw] + +def holds (valuation : Nat → Nat) : PropWhen → Bool + | .never => false + | .allZero ps _ => ps.all (valuation · == 0) + +@[simp] theorem holds_never (v : Nat → Nat) : holds v .never = false := rfl + +@[simp] theorem holds_always (v : Nat → Nat) : holds v always = true := rfl + +@[simp] theorem holds_param (v : Nat → Nat) (i : Nat) : + holds v (param i) = (v i == 0) := by simp [holds, param] + +@[simp] theorem holds_ofList (v : Nat → Nat) (ps : List Nat) : + holds v (ofList ps) = ps.all (v · == 0) := by + apply Bool.eq_iff_iff.mpr + simp [holds, ofList, List.all_eq_true] + +private theorem holds_separator (ps : List Nat) (hs : ps.Pairwise (· < ·)) (i : Nat) : + holds (fun j => if j = i then 1 else 0) (.allZero ps hs) = decide (i ∉ ps) := by + apply Bool.eq_iff_iff.mpr + simp only [holds, List.all_eq_true, beq_iff_eq, decide_eq_true_eq] + constructor + · intro h hi + have := h i hi + simp at this + · intro h j hj + have hji : j ≠ i := fun e => h (e ▸ hj) + simp [hji] + +/-- The representation is canonical, including the distinction between +`never` and a nonempty set of zero requirements. -/ +theorem eq_of_holds {p q : PropWhen} + (h : ∀ v : Nat → Nat, holds v p = holds v q) : p = q := by + cases p with + | never => + cases q with + | never => rfl + | allZero qs hq => simpa [holds] using h (fun _ => 0) + | allZero ps hp => + cases q with + | never => simpa [holds] using h (fun _ => 0) + | allZero qs hq => + have hm : ∀ i, i ∈ ps ↔ i ∈ qs := by + intro i + have hi := h (fun j => if j = i then 1 else 0) + rw [holds_separator, holds_separator] at hi + have hn : i ∉ ps ↔ i ∉ qs := by simpa using Bool.eq_iff_iff.mp hi + by_cases hp' : i ∈ ps <;> by_cases hq' : i ∈ qs <;> simp_all + have := IndexSet.eq_of_members hp hq hm + subst qs + rfl + +theorem eq_iff_holds (p q : PropWhen) : + p = q ↔ ∀ v : Nat → Nat, holds v p = holds v q := + ⟨fun h _ => h ▸ rfl, eq_of_holds⟩ + +/-- All named parameters are in scope; this is separate from the zero test. -/ +def WF (n : Nat) : PropWhen → Prop + | .never => True + | .allZero ps _ => ∀ i ∈ ps, i < n + +instance {n : Nat} {p : PropWhen} : Decidable (WF n p) := by + cases p <;> unfold WF <;> infer_instance + +/-- Decode the canonical representation. Malformed ordering and out-of-range +indices are rejected, rather than silently normalized or defaulted to zero. -/ +def fromRaw? (n : Nat) : Option (List Nat) → Option PropWhen + | none => some .never + | some ps => + if hs : ps.Pairwise (· < ·) then + if ∀ i ∈ ps, i < n then some (.allZero ps hs) else none + else none + +theorem fromRaw?_sound {n : Nat} {raw : Option (List Nat)} {p : PropWhen} + (h : fromRaw? n raw = some p) : p.toRaw = raw ∧ p.WF n := by + cases raw with + | none => simp [fromRaw?] at h; subst p; simp [toRaw, WF] + | some ps => + simp only [fromRaw?] at h + split at h + · split at h + next hb => cases h; exact ⟨rfl, hb⟩ + · contradiction + · contradiction + +theorem fromRaw?_complete {n : Nat} {p : PropWhen} (h : p.WF n) : + fromRaw? n p.toRaw = some p := by + cases p with + | never => rfl + | allZero ps hs => simp_all [fromRaw?, toRaw, WF] + +def inter : PropWhen → PropWhen → PropWhen + | .allZero ps _, .allZero qs _ => ofList (ps ++ qs) + | _, _ => .never + +@[simp] theorem holds_inter (v : Nat → Nat) (p q : PropWhen) : + holds v (inter p q) = (holds v p && holds v q) := by + apply Bool.eq_iff_iff.mpr + cases p <;> cases q <;> + simp [inter, holds, ofList, List.all_eq_true, or_imp, forall_and] + +theorem inter_comm (p q : PropWhen) : inter p q = inter q p := by + apply eq_of_holds; intro v; simp [Bool.and_comm] + +theorem inter_assoc (p q r : PropWhen) : inter (inter p q) r = inter p (inter q r) := by + apply eq_of_holds; intro v; simp [Bool.and_assoc] + +@[simp] theorem inter_always (p : PropWhen) : inter p always = p := by + apply eq_of_holds; intro v; simp + +@[simp] theorem always_inter (p : PropWhen) : inter always p = p := by + rw [inter_comm, inter_always] + +@[simp] theorem inter_self (p : PropWhen) : inter p p = p := by + apply eq_of_holds; intro v; simp + +theorem WF.inter {n : Nat} {p q : PropWhen} (hp : p.WF n) (hq : q.WF n) : + (p.inter q).WF n := by + cases p <;> cases q <;> + simp_all [PropWhen.inter, ofList, PropWhen.WF, or_imp] + +def bindList (f : Nat → PropWhen) : List Nat → PropWhen + | [] => always + | i :: ps => inter (f i) (bindList f ps) + +def bind (f : Nat → PropWhen) : PropWhen → PropWhen + | .never => .never + | .allZero ps _ => bindList f ps + +theorem holds_bindList (v : Nat → Nat) (f : Nat → PropWhen) (ps : List Nat) : + holds v (bindList f ps) = ps.all (fun i => holds v (f i)) := by + induction ps <;> simp [bindList, *] + +theorem holds_bind (v : Nat → Nat) (f : Nat → PropWhen) (p : PropWhen) : + holds v (bind f p) = holds (fun i => if holds v (f i) then 0 else 1) p := by + cases p with + | never => rfl + | allZero ps hs => + change holds v (bindList f ps) = + ps.all (fun i => (if holds v (f i) then 0 else 1) == 0) + rw [holds_bindList] + apply List.all_congr rfl + intro i + cases holds v (f i) <;> rfl + +theorem holds_bind_of (v w : Nat → Nat) (f : Nat → PropWhen) (p : PropWhen) + (h : ∀ i, holds v (f i) = (w i == 0)) : holds v (bind f p) = holds w p := by + cases p with + | never => rfl + | allZero ps hs => + change holds v (bindList f ps) = ps.all (w · == 0) + rw [holds_bindList] + exact List.all_congr rfl h + +@[simp] theorem bind_never (f : Nat → PropWhen) : bind f .never = .never := rfl + +@[simp] theorem bind_always (f : Nat → PropWhen) : bind f always = always := rfl + +@[simp] theorem bind_param (f : Nat → PropWhen) (i : Nat) : bind f (param i) = f i := by + simp [bind, param, bindList] + +theorem bind_inter (f : Nat → PropWhen) (p q : PropWhen) : + bind f (inter p q) = inter (bind f p) (bind f q) := by + apply eq_of_holds + intro v + simp only [holds_bind, holds_inter] + +@[simp] theorem bind_id (p : PropWhen) : bind param p = p := by + apply eq_of_holds + intro v + exact holds_bind_of v v param p (fun i => holds_param v i) + +theorem bindList_wf {n : Nat} {f : Nat → PropWhen} {ps : List Nat} + (h : ∀ i ∈ ps, (f i).WF n) : (bindList f ps).WF n := by + induction ps with + | nil => simp [bindList, always, WF] + | cons i ps ih => + exact WF.inter (h i (by simp)) (ih fun j hj => h j (by simp [hj])) + +theorem WF.bind {n k : Nat} {f : Nat → PropWhen} {p : PropWhen} + (hp : p.WF k) (h : ∀ i, i < k → (f i).WF n) : (bind f p).WF n := by + cases p with + | never => trivial + | allZero ps hs => exact bindList_wf fun i hi => h i (hp i hi) + +end PropWhen + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Quotient/Admission.lean b/Ix/Theory/Certified/Quotient/Admission.lean new file mode 100644 index 000000000..3359186dc --- /dev/null +++ b/Ix/Theory/Certified/Quotient/Admission.lean @@ -0,0 +1,57 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Admission + +namespace Ix.Theory.Certified + +open Model + +universe u v +variable {β : Type u} [DecidableEq β] + +/-- Publish the exact quotient primitive package only after all source and +formation checks. Its values and computation laws are produced internally. -/ +def checkQuotientExtension? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : CheckedInterface signature) (witness : Quotient.Witness β) : + Option (CheckedExtension.{u,v} signature store state) := do + let checked ← Quotient.check.{u,v} fuel state.entries store witness + let refs := witness.refs + have extension : Extends.{u,v} signature state.entries (refs.environment state.entries) := by + constructor + · exact fun _ _ h => Quotient.environment_old checked.down h + · intro V _ constants hM + let constants' := refs.assignment constants + have ha := Quotient.assignment_agrees checked.down constants + have hm := Quotient.assignment_realizes checked.down state.wf constants hM.realizes + exact ⟨constants', hM.extend signature state.present hm ha, ha⟩ + let result : CheckedInterface signature := { + entries := refs.environment state.entries + wf := Quotient.environment_wf checked.down state.wf + present := ⟨extension.lookup _ _ state.present.1, extension.lookup _ _ state.present.2⟩ + } + return { + result, extension + source := by + intro r entry hr + dsimp only [result] at hr + rcases Quotient.environment_source checked.down hr with hold | hnew + · exact Or.inl hold + · exact Or.inr (Or.inr (Or.inr (Or.inr (Or.inr (Or.inr (Or.inl hnew)))))) + } + +def admitQuotient? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : AdmittedEnvironment.{u,v} signature store) (witness : Quotient.Witness β) : + Option { result : AdmittedEnvironment.{u,v} signature store // + Extends.{u,v} signature state.entries result.entries } := do + let checked ← checkQuotientExtension?.{u,v} fuel (store := store) state.interface witness + return checked.admit state + +theorem admitQuotient?_extends {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + {state : AdmittedEnvironment.{u,v} signature store} {witness : Quotient.Witness β} {result} + (_ : admitQuotient? fuel state witness = some result) : + Extends.{u,v} signature state.entries result.val.entries := result.property + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Quotient/Checked.lean b/Ix/Theory/Certified/Quotient/Checked.lean new file mode 100644 index 000000000..4179e66a5 --- /dev/null +++ b/Ix/Theory/Certified/Quotient/Checked.lean @@ -0,0 +1,179 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Quotient.Reading +import Ix.Theory.Certified.Signature + +namespace Ix.Theory.Certified.Quotient + +open Model Model.SetTheory + +universe u v +variable {β : Type u} [DecidableEq β] + +theorem mem_kinds (kind : Kind) : kind ∈ kinds := by cases kind <;> simp [kinds] + +def Refs.header (refs : Refs β) (kind : Kind) : Signature.Header β := + ⟨refs.ref kind, kind.universes, refs.entryType kind⟩ + +def Refs.headers (refs : Refs β) : List (Signature.Header β) := kinds.map refs.header + +def Refs.typeEnvironment (refs : Refs β) (entries : Environment β) : Environment β := + Signature.environment entries refs.headers + +def Refs.liftRule (refs : Refs β) : Signature.Rule β := + ⟨2, liftRuleType refs, liftRuleLhs refs, liftRuleRhs refs⟩ + +def Refs.indRule (refs : Refs β) : Signature.Rule β := + ⟨1, indRuleType refs, indRuleLhs refs, indRuleRhs refs⟩ + +def Refs.ExactSource (refs : Refs β) (store : Store β) : Prop := + ∀ kind ∈ kinds, store.lookup (refs.ref kind) = some (refs.source kind) + +def Refs.Fresh (refs : Refs β) (entries : Environment β) : Prop := + ∀ kind ∈ kinds, entries (refs.ref kind) = none + +instance (refs : Refs β) (store : Store β) : Decidable (refs.ExactSource store) := + inferInstanceAs (Decidable (∀ kind ∈ kinds, store.lookup (refs.ref kind) = some (refs.source kind))) + +instance (refs : Refs β) (entries : Environment β) : Decidable (refs.Fresh entries) := + inferInstanceAs (Decidable (∀ kind ∈ kinds, entries (refs.ref kind) = none)) + +structure Witness (β : Type u) where + refs : Refs β + types : List (Signature.TypeWitness β) + liftRule : Signature.RuleWitness β + indRule : Signature.RuleWitness β + +structure Checked (entries : Environment β) (store : Store β) (refs : Refs β) : Prop where + exactSource : refs.ExactSource store + fresh : refs.Fresh entries + equality : Basis.Equality.Interface entries refs.eq refs.eqRefl refs.eqRec + types : Signature.Formed.{u,v} entries refs.headers + liftRule : Signature.RuleFormed.{u,v} (refs.typeEnvironment entries) refs.liftRule + indRule : Signature.RuleFormed.{u,v} (refs.typeEnvironment entries) refs.indRule + +def check (fuel : Nat) (entries : Environment β) (store : Store β) (witness : Witness β) : + Option (CheckedClaim.{u} (Checked.{u,v} entries store witness.refs)) := + if hs : witness.refs.ExactSource store then + if hf : witness.refs.Fresh entries then + if he : Basis.Equality.Interface entries witness.refs.eq witness.refs.eqRefl witness.refs.eqRec then do + let ht ← Signature.checkTypes.{u,v} fuel entries witness.refs.headers witness.types + let hl ← Signature.checkRule.{u,v} fuel (witness.refs.typeEnvironment entries) witness.refs.liftRule witness.liftRule + let hi ← Signature.checkRule.{u,v} fuel (witness.refs.typeEnvironment entries) witness.refs.indRule witness.indRule + return ⟨⟨hs, hf, he, ht.down, hl.down, hi.down⟩⟩ + else none + else none + else none + +theorem check_sound {fuel : Nat} {entries : Environment β} {store : Store β} {witness : Witness β} + {result} (_ : check.{u,v} fuel entries store witness = some result) : + Checked.{u,v} entries store witness.refs := result.down + +omit [DecidableEq β] in +theorem Refs.ExactSource.injective {refs : Refs β} {store : Store β} (h : refs.ExactSource store) : + Function.Injective refs.ref := by + intro a b he + have ha := h a (mem_kinds a) + have hb := h b (mem_kinds b) + rw [he, hb] at ha + have hs := Option.some.inj ha + cases a <;> cases b <;> simp [Refs.source] at hs <;> rfl + +def Refs.kind? (refs : Refs β) (r : ConstRef β) : Option Kind := + if r = refs.type then some .type else + if r = refs.ctor then some .ctor else + if r = refs.lift then some .lift else + if r = refs.ind then some .ind else + if r = refs.sound then some .sound else none + +theorem Refs.kind?_same {refs : Refs β} {store : Store β} (h : refs.ExactSource store) (kind : Kind) : + refs.kind? (refs.ref kind) = some kind := by + have he (a b : Kind) (hne : a ≠ b) : refs.ref a ≠ refs.ref b := fun hab => hne (h.injective hab) + have ht := he .ctor .type (by decide) + have hlt := he .lift .type (by decide) + have hlc := he .lift .ctor (by decide) + have hit := he .ind .type (by decide) + have hic := he .ind .ctor (by decide) + have hil := he .ind .lift (by decide) + have hst := he .sound .type (by decide) + have hsc := he .sound .ctor (by decide) + have hsl := he .sound .lift (by decide) + have hsi := he .sound .ind (by decide) + cases kind <;> simp_all [Refs.kind?, Refs.ref] + +theorem Refs.kind?_sound {refs : Refs β} {r : ConstRef β} {kind : Kind} + (h : refs.kind? r = some kind) : r = refs.ref kind := by + unfold Refs.kind? at h + split at h + · cases Option.some.inj h; assumption + · split at h + · cases Option.some.inj h; assumption + · split at h + · cases Option.some.inj h; assumption + · split at h + · cases Option.some.inj h; assumption + · split at h + · cases Option.some.inj h; assumption + · contradiction + +variable {entries : Environment β} {store : Store β} {refs : Refs β} + +theorem typeEnvironment_lookup (h : Checked.{u,v} entries store refs) (kind : Kind) : + refs.typeEnvironment entries (refs.ref kind) = some (refs.header kind).entry := + h.types.lookup (List.mem_map.mpr ⟨kind, mem_kinds kind, rfl⟩) + +variable {V : Type v} [SetTheory V] + +noncomputable def Refs.assignment (refs : Refs β) (constants : Assignment β V) : Assignment β V := + fun r levels => match refs.kind? r with + | some kind => refs.value constants kind levels + | none => constants r levels + +theorem assignment_same (h : Checked.{u,v} entries store refs) (constants : Assignment β V) + (kind : Kind) (levels : List Nat) : + refs.assignment constants (refs.ref kind) levels = refs.value constants kind levels := by + simp only [Refs.assignment, Refs.kind?_same h.exactSource] + +theorem assignment_agrees (h : Checked.{u,v} entries store refs) (constants : Assignment β V) : + Assignment.AgreesOn entries constants (refs.assignment constants) := by + intro r entry hr levels + unfold Refs.assignment + cases hk : refs.kind? r with + | none => rfl + | some kind => + have he := Refs.kind?_sound hk + have hf := h.fresh kind (mem_kinds kind) + rw [← he, hr] at hf + contradiction + +theorem assignment_reading (h : Checked.{u,v} entries store refs) (constants : Assignment β V) : + Reading (refs.assignment constants) refs := by + have heq (levels) : refs.assignment constants refs.eq levels = constants refs.eq levels := by + obtain ⟨entry, he, _, _⟩ := h.equality.former + exact assignment_agrees h constants _ _ he levels + constructor + · intro u + exact assignment_same h constants .type [u] + · intro u + exact assignment_same h constants .ctor [u] + · intro u v + rw [show refs.lift = refs.ref .lift from rfl, assignment_same h constants] + simp [Refs.value, liftValue, invariantSet, Basis.Equality.value, heq] + · intro u + exact assignment_same h constants .ind [u] + · intro u + exact assignment_same h constants .sound [u] + +theorem typeAssignment_realizes (h : Checked.{u,v} entries store refs) (hE : entries.WF) + (constants : Assignment β V) (hM : Realizes constants entries) : + Realizes (refs.assignment constants) (refs.typeEnvironment entries) := by + have hm := hM.of_agrees hE (assignment_agrees h constants) + apply h.types.realizes hm + intro header hh levels hn env + obtain ⟨kind, _, rfl⟩ := List.mem_map.mp hh + exact value_mem (assignment_reading h constants) h.equality hm kind levels hn env + +end Ix.Theory.Certified.Quotient diff --git a/Ix/Theory/Certified/Quotient/Publish.lean b/Ix/Theory/Certified/Quotient/Publish.lean new file mode 100644 index 000000000..938df6a02 --- /dev/null +++ b/Ix/Theory/Certified/Quotient/Publish.lean @@ -0,0 +1,195 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Quotient.Checked + +namespace Ix.Theory.Certified.Quotient + +open Model Model.SetTheory + +universe u v +variable {β : Type u} [DecidableEq β] + +def Refs.environment (refs : Refs β) (entries : Environment β) : Environment β := + fun r => match refs.kind? r with + | some kind => some (refs.entry kind) + | none => entries r + +variable {entries : Environment β} {store : Store β} {refs : Refs β} + +theorem environment_same (h : Checked.{u,v} entries store refs) (kind : Kind) : + refs.environment entries (refs.ref kind) = some (refs.entry kind) := by + simp only [Refs.environment, Refs.kind?_same h.exactSource] + +theorem environment_old (h : Checked.{u,v} entries store refs) {r : ConstRef β} + {entry : ConstantEntry β} (hr : entries r = some entry) : refs.environment entries r = some entry := by + unfold Refs.environment + cases hk : refs.kind? r with + | none => exact hr + | some kind => + have he := Refs.kind?_sound hk + have hf := h.fresh kind (mem_kinds kind) + rw [← he, hr] at hf + contradiction + +theorem environment_cases {r : ConstRef β} {entry : ConstantEntry β} + (hr : refs.environment entries r = some entry) : + entries r = some entry ∨ ∃ kind, r = refs.ref kind ∧ entry = refs.entry kind := by + unfold Refs.environment at hr + split at hr + · exact Or.inr ⟨_, Refs.kind?_sound ‹_›, (Option.some.inj hr).symm⟩ + · exact Or.inl hr + +theorem environment_covers_types (h : Checked.{u,v} entries store refs) {r : ConstRef β} + {entry : ConstantEntry β} (hr : refs.typeEnvironment entries r = some entry) : + (refs.environment entries r).isSome := by + rcases Signature.environment_source hr with hold | ⟨header, hm, he, _⟩ + · simp only [environment_old h hold, Option.isSome_some] + · obtain ⟨kind, _, rfl⟩ := List.mem_map.mp hm + subst r + simp only [Refs.header, environment_same h, Option.isSome_some] + +theorem references_from_types (h : Checked.{u,v} entries store refs) {e : AExpr β} + (hr : e.ReferencesIn (refs.typeEnvironment entries)) : e.ReferencesIn (refs.environment entries) := by + intro r he + have hh := hr r he + cases ht : refs.typeEnvironment entries r with + | none => simp [ht] at hh + | some entry => exact environment_covers_types h ht + +theorem references_from_old (h : Checked.{u,v} entries store refs) {e : AExpr β} + (hr : e.ReferencesIn entries) : e.ReferencesIn (refs.environment entries) := by + intro r he + have hh := hr r he + cases ht : entries r with + | none => simp [ht] at hh + | some entry => simp only [environment_old h ht, Option.isSome_some] + +theorem fact_references_from_old (h : Checked.{u,v} entries store refs) {fact : ConstantFact β} + (hr : fact.ReferencesIn entries) : fact.ReferencesIn (refs.environment entries) := by + intro r he + have hh := hr r he + cases ht : entries r with + | none => simp [ht] at hh + | some entry => simp only [environment_old h ht, Option.isSome_some] + +theorem equation_scope (h : Checked.{u,v} entries store refs) (kind : Kind) + {law : ConstantEquation β} (hl : law ∈ refs.equations kind) : + law.lhs.Scope kind.universes 0 ∧ law.rhs.Scope kind.universes 0 := by + cases kind with + | type | ctor | sound => cases hl + | lift => + have he := List.mem_singleton.mp hl + subst law + exact h.liftRule.scope.2 + | ind => + have he := List.mem_singleton.mp hl + subst law + exact h.indRule.scope.2 + +theorem equation_references (h : Checked.{u,v} entries store refs) (kind : Kind) + {law : ConstantEquation β} (hl : law ∈ refs.equations kind) : + law.lhs.ReferencesIn (refs.environment entries) ∧ law.rhs.ReferencesIn (refs.environment entries) := by + cases kind with + | type | ctor | sound => cases hl + | lift => + have he := List.mem_singleton.mp hl + subst law + exact ⟨references_from_types h h.liftRule.references.2.1, references_from_types h h.liftRule.references.2.2⟩ + | ind => + have he := List.mem_singleton.mp hl + subst law + exact ⟨references_from_types h h.indRule.references.2.1, references_from_types h h.indRule.references.2.2⟩ + +theorem entry_closed (h : Checked.{u,v} entries store refs) (hE : entries.WF) (kind : Kind) : + EntryClosed (refs.environment entries) (refs.entry kind) := by + have ht := h.types.wf hE + have hl := typeEnvironment_lookup h kind + constructor + · exact ht.typeScope _ (refs.header kind).entry hl + · intro body hb; cases hb + · exact references_from_types h (ht.typeReferences _ (refs.header kind).entry hl) + · intro body hb; cases hb + · exact fun _ hh => equation_scope h kind hh + · exact fun _ hh => equation_references h kind hh + · intro fact hf; cases hf + · intro fact hf; cases hf + +theorem environment_wf (h : Checked.{u,v} entries store refs) (hE : entries.WF) : + (refs.environment entries).WF := by + have hc : ∀ r entry, refs.environment entries r = some entry → EntryClosed (refs.environment entries) entry := by + intro r entry hr + rcases environment_cases hr with hold | ⟨kind, _, rfl⟩ + · exact ⟨hE.typeScope _ _ hold, hE.bodyScope _ _ hold, + references_from_old h (hE.typeReferences _ _ hold), + fun body hb => references_from_old h (hE.bodyReferences _ _ hold body hb), + hE.equationScope _ _ hold, + fun law hl => ⟨references_from_old h (hE.equationReferences _ _ hold law hl).1, + references_from_old h (hE.equationReferences _ _ hold law hl).2⟩, + hE.factScope _ _ hold, + fun fact hf => fact_references_from_old h (hE.factReferences _ _ hold fact hf)⟩ + · exact entry_closed h hE kind + exact ⟨fun r e hr => (hc r e hr).typeScope, fun r e hr => (hc r e hr).bodyScope, + fun r e hr => (hc r e hr).typeReferences, fun r e hr => (hc r e hr).bodyReferences, + fun r e hr => (hc r e hr).equationScope, fun r e hr => (hc r e hr).equationReferences, + fun r e hr => (hc r e hr).factScope, fun r e hr => (hc r e hr).factReferences⟩ + +/-- The exact five source declarations, including the safe soundness axiom, +determine each published type and computation equation. -/ +def EntrySource (store : Store β) (r : ConstRef β) (entry : ConstantEntry β) : Prop := + ∃ refs : Refs β, ∃ kind, refs.ExactSource store ∧ r = refs.ref kind ∧ entry = refs.entry kind + +theorem environment_source (h : Checked.{u,v} entries store refs) {r : ConstRef β} {entry : ConstantEntry β} + (hr : refs.environment entries r = some entry) : entries r = some entry ∨ EntrySource store r entry := by + rcases environment_cases hr with hold | ⟨kind, he, hv⟩ + · exact Or.inl hold + · exact Or.inr ⟨refs, kind, h.exactSource, he, hv⟩ + +variable {V : Type v} [SetTheory V] + +theorem equation_value (h : Checked.{u,v} entries store refs) (hE : entries.WF) + (constants : Assignment β V) (hM : Realizes constants entries) (kind : Kind) + {law : ConstantEquation β} (hl : law ∈ refs.equations kind) + (levels : List Nat) (hn : levels.length = kind.universes) (env : Nat → V) : + interp (refs.assignment constants) levels env law.lhs = interp (refs.assignment constants) levels env law.rhs := by + cases kind with + | type | ctor | sound => cases hl + | lift => + have he := List.mem_singleton.mp hl + subst law + cases levels with + | nil => cases hn + | cons u tail => + obtain ⟨v, rfl⟩ := List.length_eq_one_iff.mp (Nat.succ.inj hn) + exact liftRule_eq (assignment_reading h constants) h.equality + (hM.of_agrees hE (assignment_agrees h constants)) u v env + | ind => + have he := List.mem_singleton.mp hl + subst law + exact indRule_eq _ _ _ _ + +theorem assignment_realizes (h : Checked.{u,v} entries store refs) (hE : entries.WF) + (constants : Assignment β V) (hM : Realizes constants entries) : + Realizes (refs.assignment constants) (refs.environment entries) := by + have hs := typeAssignment_realizes h hE constants hM + have ho := hM.of_agrees hE (assignment_agrees h constants) + have he : ∀ r entry, refs.environment entries r = some entry → EntryRealization (refs.assignment constants) r entry := by + intro r entry hr + rcases environment_cases hr with hold | ⟨kind, rfl, rfl⟩ + · exact ⟨ho.typeValid _ _ hold, ho.member _ _ hold, ho.bodyValid _ _ hold, + ho.bodyValue _ _ hold, ho.equationValue _ _ hold, ho.factMeaning _ _ hold⟩ + · have ht := typeEnvironment_lookup h kind + constructor + · exact hs.typeValid _ (refs.header kind).entry ht + · exact hs.member _ (refs.header kind).entry ht + · intro body hb; cases hb + · intro body hb; cases hb + · exact fun law hl => equation_value h hE constants hM kind hl + · intro fact hf; cases hf + exact ⟨fun r e hr => (he r e hr).typeValid, fun r e hr => (he r e hr).member, + fun r e hr => (he r e hr).bodyValid, fun r e hr => (he r e hr).bodyValue, + fun r e hr => (he r e hr).equationValue, fun r e hr => (he r e hr).factMeaning⟩ + +end Ix.Theory.Certified.Quotient diff --git a/Ix/Theory/Certified/Quotient/Reading.lean b/Ix/Theory/Certified/Quotient/Reading.lean new file mode 100644 index 000000000..3a0706afc --- /dev/null +++ b/Ix/Theory/Certified/Quotient/Reading.lean @@ -0,0 +1,184 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Quotient.Value + +namespace Ix.Theory.Certified.Quotient + +open Model Model.SetTheory Model.SetModel + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +noncomputable def Refs.value (refs : Refs β) (constants : Assignment β V) + (kind : Kind) (levels : List Nat) : V := + match kind with + | .type => formerValue (levels.getD 0 0) + | .ctor => constructorValue (levels.getD 0 0) + | .lift => liftValue constants refs.eq (levels.getD 0 0) (levels.getD 1 0) + | .ind | .sound => pt + +/-- This reading is proved for the internally constructed assignment. It is +not a field of an input certificate. -/ +structure Reading (constants : Assignment β V) (refs : Refs β) : Prop where + former : ∀ u, constants refs.type [u] = formerValue u + ctor : ∀ u, constants refs.ctor [u] = constructorValue u + lift : ∀ u v, constants refs.lift [u, v] = liftValue constants refs.eq u v + ind : ∀ u, constants refs.ind [u] = pt + sound : ∀ u, constants refs.sound [u] = pt + +noncomputable def formerAt (constants : Assignment β V) (refs : Refs β) (u : Nat) (A R : V) : V := + app (app (constants refs.type [u]) A) R + +noncomputable def constructorAt (constants : Assignment β V) (refs : Refs β) (u : Nat) (A R a : V) : V := + app (app (app (constants refs.ctor [u]) A) R) a + +variable {constants : Assignment β V} {refs : Refs β} + +theorem formerAt_eq (h : Reading constants refs) {u : Nat} {A R : V} + (hA : A ∈ˢ univ u) (hR : R ∈ˢ relationSet A) : formerAt constants refs u A R = quotSet u A R := by + rw [formerAt, h.former] + exact formerValue_apply hA hR + +theorem constructorAt_eq (h : Reading constants refs) {u : Nat} {A R a : V} + (hA : A ∈ˢ univ u) (hR : R ∈ˢ relationSet A) (ha : a ∈ˢ A) : + constructorAt constants refs u A R a = quotClass u A R a := by + rw [constructorAt, h.ctor] + exact constructorValue_apply hA hR ha + +theorem typeType_interp (constants : Assignment β V) (u : Nat) (env : Nat → V) : + interp constants [u] env (typeType : AExpr β) = formerSet u := by + simp [typeType, relationType, formerSet, relationSet, interp, VLevel.eval, Valuation.cons, univ_zero] + +theorem ctorType_interp (h : Reading constants refs) (u : Nat) (env : Nat → V) : + interp constants [u] env (ctorType refs) = constructorSet u := by + simp [ctorType, applied, AExpr.appN, relationType, interp, VLevel.eval, Valuation.cons, regime, PropWhen.param, PropWhen.holds, univ_zero] + change piR (bit u) (univ u) (fun A => piR (bit u) (relationSet A) (fun R => + piR (bit u) A (fun _ => formerAt constants refs u A R))) = constructorSet u + apply piR_congr + intro A hA + apply piR_congr + intro R hR + simp only [formerAt_eq h hA hR] + +theorem liftType_interp (h : Reading constants refs) (u v : Nat) (env : Nat → V) : + interp constants [u, v] env (liftType refs) = liftSet constants refs.eq u v := by + simp [liftType, liftPrefix, AExpr.forallN, invariantType, Basis.Equality.applied, + relationType, applied, AExpr.appN, interp, VLevel.eval, + Valuation.cons, regime, PropWhen.param, PropWhen.holds, univ_zero] + change piR (bit v) (univ u) (fun A => piR (bit v) (relationSet A) (fun R => + piR (bit v) (univ v) (fun B => piR (bit v) (piR (bit v) A (fun _ => B)) (fun f => + piR (bit v) (invariantSet constants refs.eq v A R B f) (fun _ => + piR (bit v) (formerAt constants refs u A R) (fun _ => B)))))) = liftSet constants refs.eq u v + apply piR_congr + intro A hA + apply piR_congr + intro R hR + simp only [formerAt_eq h hA hR] + +theorem indType_interp (h : Reading constants refs) (u : Nat) (env : Nat → V) : + interp constants [u] env (indType refs) = indSet u := by + simp [indType, indPrefix, AExpr.forallN, relationType, applied, constructed, AExpr.appN, + interp, VLevel.eval, Valuation.cons, + regime, PropWhen.holds, univ_zero] + change piR 0 (univ u) (fun A => piR 0 (relationSet A) (fun R => + piR 0 (piR 1 (formerAt constants refs u A R) (fun _ => univZero)) (fun motive => + piR 0 (piR 0 A (fun a => app motive (constructorAt constants refs u A R a))) (fun _ => + piR 0 (formerAt constants refs u A R) (fun q => app motive q))))) = indSet u + apply piR_congr + intro A hA + apply piR_congr + intro R hR + simp only [formerAt_eq h hA hR] + apply piR_congr + intro motive _ + have he : piR 0 A (fun a => app motive (constructorAt constants refs u A R a)) = + piR 0 A (fun a => app motive (quotClass u A R a)) := by + apply piR_congr + intro a ha + rw [constructorAt_eq h hA hR ha] + rw [he] + +theorem soundType_interp (h : Reading constants refs) (u : Nat) (env : Nat → V) : + interp constants [u] env (soundType refs) = soundSet constants refs.eq u := by + simp [soundType, relationType, applied, constructed, Basis.Equality.applied, AExpr.appN, + interp, VLevel.eval, Valuation.cons, + regime, PropWhen.holds, univ_zero] + change piR 0 (univ u) (fun A => piR 0 (relationSet A) (fun R => + piR 0 A (fun a => piR 0 A (fun b => piR 0 (app (app R a) b) (fun _ => + Basis.Equality.value constants refs.eq u (formerAt constants refs u A R) + (constructorAt constants refs u A R a) (constructorAt constants refs u A R b)))))) = + soundSet constants refs.eq u + apply piR_congr + intro A hA + apply piR_congr + intro R hR + apply piR_congr + intro a ha + apply piR_congr + intro b hb + simp only [formerAt_eq h hA hR, constructorAt_eq h hA hR ha, constructorAt_eq h hA hR hb] + +theorem value_mem {entries : Environment β} (h : Reading constants refs) + (hE : Basis.Equality.Interface entries refs.eq refs.eqRefl refs.eqRec) (hM : Realizes constants entries) + (kind : Kind) (levels : List Nat) (hn : levels.length = kind.universes) (env : Nat → V) : + constants (refs.ref kind) levels ∈ˢ interp constants levels env (refs.entryType kind) := by + cases kind with + | type => + obtain ⟨u, rfl⟩ := List.length_eq_one_iff.mp hn + rw [Refs.ref, h.former, Refs.entryType, typeType_interp] + exact formerValue_mem u + | ctor => + obtain ⟨u, rfl⟩ := List.length_eq_one_iff.mp hn + rw [Refs.ref, h.ctor, Refs.entryType, ctorType_interp h] + exact constructorValue_mem u + | lift => + cases levels with + | nil => cases hn + | cons u tail => + obtain ⟨v, rfl⟩ := List.length_eq_one_iff.mp (Nat.succ.inj hn) + rw [Refs.ref, h.lift, Refs.entryType, liftType_interp h] + exact liftValue_mem constants refs.eq u v + | ind => + obtain ⟨u, rfl⟩ := List.length_eq_one_iff.mp hn + rw [Refs.ref, h.ind, Refs.entryType, indType_interp h] + exact indValue_mem u + | sound => + obtain ⟨u, rfl⟩ := List.length_eq_one_iff.mp hn + rw [Refs.ref, h.sound, Refs.entryType, soundType_interp h] + exact soundValue_mem hE hM u + +/-- Equality of the complete lambda endpoints published as the primitive +computation law, for every environment and both universe regimes. -/ +theorem liftRule_eq {entries : Environment β} (h : Reading constants refs) + (hE : Basis.Equality.Interface entries refs.eq refs.eqRefl refs.eqRec) (hM : Realizes constants entries) + (u v : Nat) (env : Nat → V) : + interp constants [u, v] env (liftRuleLhs refs) = interp constants [u, v] env (liftRuleRhs refs) := by + simp [liftRuleLhs, liftRuleRhs, liftRuleBinders, liftPrefix, List.cons_append, List.nil_append, + AExpr.lamN, relationType, invariantType, Basis.Equality.applied, AExpr.appN, constructed, + interp, VLevel.eval, Valuation.cons, regime, PropWhen.param, PropWhen.holds, univ_zero] + apply lamR_congr + intro A hA + apply lamR_congr + intro R hR + apply lamR_congr + intro B hB + apply lamR_congr + intro f hf + apply lamR_congr + intro proof hp + apply lamR_congr + intro a ha + change app (app (app (app (app (app (constants refs.lift [u, v]) A) R) B) f) proof) + (constructorAt constants refs u A R a) = app f a + rw [h.lift, constructorAt_eq h hA hR ha] + exact liftValue_apply hE hM hA hR hB hf hp ha + +theorem indRule_eq (constants : Assignment β V) (refs : Refs β) (levels : List Nat) (env : Nat → V) : + interp constants levels env (indRuleLhs refs) = interp constants levels env (indRuleRhs refs) := by + simp only [indRuleLhs, indRuleRhs, indRuleBinders, indPrefix, List.cons_append, List.nil_append, + AExpr.lamN, interp, regime_always, lamR_zero] + +end Ix.Theory.Certified.Quotient diff --git a/Ix/Theory/Certified/Quotient/Syntax.lean b/Ix/Theory/Certified/Quotient/Syntax.lean new file mode 100644 index 000000000..b7499de97 --- /dev/null +++ b/Ix/Theory/Certified/Quotient/Syntax.lean @@ -0,0 +1,144 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Basis.Equality +import Ix.Theory.Quot + +namespace Ix.Theory.Certified.Quotient + +open Model + +universe u +variable {β : Type u} + +/-- The five quotient declarations are realized together. References may be +members of different authenticated blocks. Equality must already be admitted. -/ +structure Refs (β : Type u) extends Store.QuotRefs β where + eqRefl : ConstRef β + eqRec : ConstRef β + sound : ConstRef β + deriving DecidableEq + +inductive Kind where + | type | ctor | lift | ind | sound + deriving DecidableEq + +def kinds : List Kind := [.type, .ctor, .lift, .ind, .sound] + +def Refs.ref (refs : Refs β) : Kind → ConstRef β + | .type => refs.type + | .ctor => refs.ctor + | .lift => refs.lift + | .ind => refs.ind + | .sound => refs.sound + +def Kind.universes : Kind → Nat + | .lift => 2 + | _ => 1 + +def relationType : AExpr β := + .forallE .never (.bvar 0) (.forallE .never (.bvar 1) (.sort .zero)) + +def applied (refs : Refs β) (l : VLevel) (A R : AExpr β) : AExpr β := + .appN (.const refs.type [l]) [A, R] + +def constructed (refs : Refs β) (l : VLevel) (A R a : AExpr β) : AExpr β := + .appN (.const refs.ctor [l]) [A, R, a] + +def typeType : AExpr β := + .forallE .never (.sort (.param 0)) (.forallE .never relationType (.sort (.param 0))) + +def ctorType (refs : Refs β) : AExpr β := + .forallE (.param 0) (.sort (.param 0)) <| + .forallE (.param 0) relationType <| + .forallE (.param 0) (.bvar 1) <| + applied refs (.param 0) (.bvar 2) (.bvar 1) + +/-- In the context `A, R, B, f`, invariance of `f` under `R`. -/ +def invariantType (refs : Refs β) : AExpr β := + .forallE .always (.bvar 3) <| + .forallE .always (.bvar 4) <| + .forallE .always (.appN (.bvar 4) [.bvar 1, .bvar 0]) <| + Basis.Equality.applied refs.eq (.param 1) (.bvar 4) + (.app (.bvar 3) (.bvar 2)) (.app (.bvar 3) (.bvar 1)) + +def liftPrefix (refs : Refs β) : List (AExpr β) := + [.sort (.param 0), relationType, .sort (.param 1), + .forallE (.param 1) (.bvar 2) (.bvar 1), invariantType refs] + +def liftType (refs : Refs β) : AExpr β := + .forallN (.param 1) (liftPrefix refs) <| + .forallE (.param 1) (applied refs (.param 0) (.bvar 4) (.bvar 3)) (.bvar 3) + +def indPrefix (refs : Refs β) : List (AExpr β) := + [.sort (.param 0), relationType, + .forallE .never (applied refs (.param 0) (.bvar 1) (.bvar 0)) (.sort .zero), + .forallE .always (.bvar 2) + (.app (.bvar 1) (constructed refs (.param 0) (.bvar 3) (.bvar 2) (.bvar 0)))] + +def indType (refs : Refs β) : AExpr β := + .forallN .always (indPrefix refs) <| + .forallE .always (applied refs (.param 0) (.bvar 3) (.bvar 2)) (.app (.bvar 2) (.bvar 0)) + +def soundType (refs : Refs β) : AExpr β := + .forallE .always (.sort (.param 0)) <| + .forallE .always relationType <| + .forallE .always (.bvar 1) <| + .forallE .always (.bvar 2) <| + .forallE .always (.appN (.bvar 2) [.bvar 1, .bvar 0]) <| + Basis.Equality.applied refs.eq (.param 0) + (applied refs (.param 0) (.bvar 4) (.bvar 3)) + (constructed refs (.param 0) (.bvar 4) (.bvar 3) (.bvar 2)) + (constructed refs (.param 0) (.bvar 4) (.bvar 3) (.bvar 1)) + +def Refs.entryType (refs : Refs β) : Kind → AExpr β + | .type => typeType + | .ctor => ctorType refs + | .lift => liftType refs + | .ind => indType refs + | .sound => soundType refs + +def Refs.source (refs : Refs β) : Kind → Const β + | .type => .quot .type 1 typeType.erase + | .ctor => .quot .ctor 1 (ctorType refs).erase + | .lift => .quot .lift 2 (liftType refs).erase + | .ind => .quot .ind 1 (indType refs).erase + | .sound => .axiom 1 (soundType refs).erase .safe + +def liftRuleBinders (refs : Refs β) : List (AExpr β) := liftPrefix refs ++ [.bvar 4] +def liftRuleType (refs : Refs β) : AExpr β := .forallN (.param 1) (liftRuleBinders refs) (.bvar 3) +def liftRuleLhs (refs : Refs β) : AExpr β := + .lamN (.param 1) (liftRuleBinders refs) <| + .appN (.const refs.lift [.param 0, .param 1]) + [.bvar 5, .bvar 4, .bvar 3, .bvar 2, .bvar 1, + constructed refs (.param 0) (.bvar 5) (.bvar 4) (.bvar 0)] +def liftRuleRhs (refs : Refs β) : AExpr β := + .lamN (.param 1) (liftRuleBinders refs) (.app (.bvar 2) (.bvar 0)) + +def indRuleBinders (refs : Refs β) : List (AExpr β) := indPrefix refs ++ [.bvar 3] +def indRuleType (refs : Refs β) : AExpr β := + .forallN .always (indRuleBinders refs) <| + .app (.bvar 2) (constructed refs (.param 0) (.bvar 4) (.bvar 3) (.bvar 0)) +def indRuleLhs (refs : Refs β) : AExpr β := + .lamN .always (indRuleBinders refs) <| + .appN (.const refs.ind [.param 0]) [.bvar 4, .bvar 3, .bvar 2, .bvar 1, + constructed refs (.param 0) (.bvar 4) (.bvar 3) (.bvar 0)] +def indRuleRhs (refs : Refs β) : AExpr β := + .lamN .always (indRuleBinders refs) (.app (.bvar 1) (.bvar 0)) + +def Refs.equations (refs : Refs β) : Kind → List (ConstantEquation β) + | .lift => [⟨liftRuleLhs refs, liftRuleRhs refs⟩] + | .ind => [⟨indRuleLhs refs, indRuleRhs refs⟩] + | _ => [] + +def Refs.entry (refs : Refs β) (kind : Kind) : ConstantEntry β := + ⟨kind.universes, refs.entryType kind, none, refs.equations kind, []⟩ + +theorem typeType_erase : (typeType : AExpr β).erase = Store.Quotient.typeType := rfl +theorem ctorType_erase (refs : Refs β) : (ctorType refs).erase = Store.Quotient.ctorType refs.toQuotRefs := rfl +theorem liftType_erase (refs : Refs β) : (liftType refs).erase = Store.Quotient.liftType refs.toQuotRefs := rfl +theorem indType_erase (refs : Refs β) : (indType refs).erase = Store.Quotient.indType refs.toQuotRefs := rfl + +end Ix.Theory.Certified.Quotient diff --git a/Ix/Theory/Certified/Quotient/Value.lean b/Ix/Theory/Certified/Quotient/Value.lean new file mode 100644 index 000000000..45794753e --- /dev/null +++ b/Ix/Theory/Certified/Quotient/Value.lean @@ -0,0 +1,185 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Quotient.Syntax +import Ix.Theory.Model.SetTheory.Derive.Quot + +namespace Ix.Theory.Certified.Quotient + +open Model Model.SetTheory Model.SetModel + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +def bit (u : Nat) : Nat := if u = 0 then 0 else 1 + +noncomputable def relationSet (A : V) : V := + piR 1 A (fun _ => piR 1 A (fun _ => univZero)) + +noncomputable def formerValue (u : Nat) : V := + lamR 1 (univ u) (fun A => lamR 1 (relationSet A) (fun R => quotSet u A R)) + +noncomputable def constructorValue (u : Nat) : V := + lamR (bit u) (univ u) (fun A => lamR (bit u) (relationSet A) (fun R => + lamR (bit u) A (fun a => quotClass u A R a))) + +noncomputable def invariantSet (constants : Assignment β V) (eq : ConstRef β) + (v : Nat) (A R B f : V) : V := + piR 0 A (fun a => piR 0 A (fun b => piR 0 (app (app R a) b) + (fun _ => Basis.Equality.value constants eq v B (app f a) (app f b)))) + +noncomputable def liftValue (constants : Assignment β V) (eq : ConstRef β) (u v : Nat) : V := + lamR (bit v) (univ u) (fun A => lamR (bit v) (relationSet A) (fun R => + lamR (bit v) (univ v) (fun B => lamR (bit v) (piR (bit v) A (fun _ => B)) (fun f => + lamR (bit v) (invariantSet constants eq v A R B f) (fun _ => + lamR (bit v) (quotSet u A R) (fun q => app f (qrep u A R q))))))) + +noncomputable def formerSet (u : Nat) : V := + piR 1 (univ u) (fun A => piR 1 (relationSet A) (fun _ => univ u)) + +noncomputable def constructorSet (u : Nat) : V := + piR (bit u) (univ u) (fun A => piR (bit u) (relationSet A) (fun R => + piR (bit u) A (fun _ => quotSet u A R))) + +noncomputable def liftSet (constants : Assignment β V) (eq : ConstRef β) (u v : Nat) : V := + piR (bit v) (univ u) (fun A => piR (bit v) (relationSet A) (fun R => + piR (bit v) (univ v) (fun B => piR (bit v) (piR (bit v) A (fun _ => B)) (fun f => + piR (bit v) (invariantSet constants eq v A R B f) (fun _ => + piR (bit v) (quotSet u A R) (fun _ => B)))))) + +noncomputable def indSet (u : Nat) : V := + piR 0 (univ u) (fun A => piR 0 (relationSet A) (fun R => + piR 0 (piR 1 (quotSet u A R) (fun _ => univZero)) (fun motive => + piR 0 (piR 0 A (fun a => app motive (quotClass u A R a))) (fun _ => + piR 0 (quotSet u A R) (fun q => app motive q))))) + +noncomputable def soundSet (constants : Assignment β V) (eq : ConstRef β) (u : Nat) : V := + piR 0 (univ u) (fun A => piR 0 (relationSet A) (fun R => + piR 0 A (fun a => piR 0 A (fun b => piR 0 (app (app R a) b) (fun _ => + Basis.Equality.value constants eq u (quotSet u A R) + (quotClass u A R a) (quotClass u A R b)))))) + +theorem app_mem_bit {u : Nat} {A B f a : V} (hB : B ∈ˢ univ u) + (hf : f ∈ˢ piR (bit u) A (fun _ => B)) (ha : a ∈ˢ A) : app f a ∈ˢ B := by + apply app_mem_piR hf ha + intro hu _ _ + have he : u = 0 := by simpa [bit] using hu + simpa [he, univ_zero] using hB + +theorem formerValue_mem (u : Nat) : (formerValue u : V) ∈ˢ formerSet u := by + apply lamR_mem + intro A hA + exact lamR_mem fun _ _ => quotSet_mem_univ hA + +theorem constructorValue_mem (u : Nat) : (constructorValue u : V) ∈ˢ constructorSet u := by + apply lamR_mem + intro A _ + apply lamR_mem + intro R _ + exact lamR_mem fun _ ha => quotClass_mem ha + +theorem formerValue_apply {u : Nat} {A R : V} (hA : A ∈ˢ univ u) (hR : R ∈ˢ relationSet A) : + app (app (formerValue u) A) R = quotSet u A R := by + rw [formerValue, app_lamR_pos (by decide : 1 ≠ 0) hA, + app_lamR_pos (by decide : 1 ≠ 0) hR] + +theorem constructorValue_apply {u : Nat} {A R a : V} + (hA : A ∈ˢ univ u) (hR : R ∈ˢ relationSet A) (ha : a ∈ˢ A) : + app (app (app (constructorValue u) A) R) a = quotClass u A R a := by + by_cases hu : u = 0 + · subst u + simp [constructorValue, bit, lamR_zero, app_pt, quotClass] + · simp only [constructorValue, bit, if_neg hu] + rw [app_lamR_pos (by decide : 1 ≠ 0) hA, + app_lamR_pos (by decide : 1 ≠ 0) hR, app_lamR_pos (by decide : 1 ≠ 0) ha] + +theorem liftValue_mem (constants : Assignment β V) (eq : ConstRef β) (u v : Nat) : + liftValue constants eq u v ∈ˢ liftSet constants eq u v := by + apply lamR_mem + intro A _ + apply lamR_mem + intro R _ + apply lamR_mem + intro B hB + apply lamR_mem + intro f hf + apply lamR_mem + intro _ _ + apply lamR_mem + intro q hq + exact app_mem_bit hB hf (qrep_spec hq).1 + +theorem indValue_mem (u : Nat) : (pt : V) ∈ˢ indSet u := by + apply pt_mem_piR_zero_of + intro A _ + apply pt_mem_piR_zero_of + intro R _ + apply pt_mem_piR_zero_of + intro motive hm + apply pt_mem_piR_zero_of + intro f hf + apply pt_mem_piR_zero_of + intro q hq + obtain ⟨a, ha, rfl⟩ := quotClass_surj hq + have hprop : ∀ x, x ∈ˢ quotSet u A R → app motive x ∈ˢ univZero := + fun _ hx => app_mem_piR_pos (by decide : 1 ≠ 0) hm hx + have hf' : app f a ∈ˢ app motive (quotClass u A R a) := + app_mem_piR hf ha (fun _ a ha => hprop _ (quotClass_mem ha)) + rwa [eq_pt_of_mem_univZero (hprop _ (quotClass_mem ha)) hf'] at hf' + +theorem soundValue_mem {entries : Environment β} {constants : Assignment β V} {eq refl recursor : ConstRef β} + (hE : Basis.Equality.Interface entries eq refl recursor) (hM : Realizes constants entries) (u : Nat) : + (pt : V) ∈ˢ soundSet constants eq u := by + apply pt_mem_piR_zero_of + intro A hA + apply pt_mem_piR_zero_of + intro R _ + apply pt_mem_piR_zero_of + intro a ha + apply pt_mem_piR_zero_of + intro b hb + apply pt_mem_piR_zero_of + intro h hh + rw [Basis.Equality.value_eq_eqv hE hM (quotSet_mem_univ hA) (quotClass_mem ha) (quotClass_mem hb), + quotSound ha hb hh] + exact pt_mem_eqv_self _ + +theorem invariant_of_mem {entries : Environment β} {constants : Assignment β V} + {eq refl recursor : ConstRef β} (hE : Basis.Equality.Interface entries eq refl recursor) + (hM : Realizes constants entries) {v : Nat} {A R B f h : V} + (hB : B ∈ˢ univ v) (hf : f ∈ˢ piR (bit v) A (fun _ => B)) + (hh : h ∈ˢ invariantSet constants eq v A R B f) : + ∀ a b, a ∈ˢ A → b ∈ˢ A → (∃ w, w ∈ˢ app (app R a) b) → app f a = app f b := by + intro a b ha hb ⟨w, hw⟩ + obtain ⟨ha', hha⟩ := Basis.Equality.exists_of_mem_piR_zero hh ha + obtain ⟨hb', hhb⟩ := Basis.Equality.exists_of_mem_piR_zero hha hb + obtain ⟨q, hq⟩ := Basis.Equality.exists_of_mem_piR_zero hhb hw + exact Basis.Equality.eq_of_mem hE hM hB (app_mem_bit hB hf ha) (app_mem_bit hB hf hb) hq + +/-- The whole quotient-lift application computes for any typed invariant +function. Both the Prop and graph regimes are included. -/ +theorem liftValue_apply {entries : Environment β} {constants : Assignment β V} + {eq refl recursor : ConstRef β} (hE : Basis.Equality.Interface entries eq refl recursor) + (hM : Realizes constants entries) {u v : Nat} {A R B f h a : V} + (hA : A ∈ˢ univ u) (hR : R ∈ˢ relationSet A) (hB : B ∈ˢ univ v) + (hf : f ∈ˢ piR (bit v) A (fun _ => B)) + (hh : h ∈ˢ invariantSet constants eq v A R B f) (ha : a ∈ˢ A) : + app (app (app (app (app (app (liftValue constants eq u v) A) R) B) f) h) + (quotClass u A R a) = app f a := by + by_cases hv : v = 0 + · subst v + have he : f = pt := eq_pt_of_mem_piR_zero hf + simp [liftValue, bit, lamR_zero, he, app_pt] + · simp only [liftValue, bit, if_neg hv] + rw [app_lamR_pos (by decide : 1 ≠ 0) hA, + app_lamR_pos (by decide : 1 ≠ 0) hR, + app_lamR_pos (by decide : 1 ≠ 0) hB, + app_lamR_pos (by decide : 1 ≠ 0) (by simpa only [bit, if_neg hv] using hf), + app_lamR_pos (by decide : 1 ≠ 0) hh, + app_lamR_pos (by decide : 1 ≠ 0) (quotClass_mem ha)] + have hs := qrep_spec (u := u) (R := R) (quotClass_mem ha) + exact app_eq_of_quotClass_eq hA hs.1 ha (invariant_of_mem hE hM hB hf hh) hs.2.symm + +end Ix.Theory.Certified.Quotient diff --git a/Ix/Theory/Certified/Signature.lean b/Ix/Theory/Certified/Signature.lean new file mode 100644 index 000000000..240fdd808 --- /dev/null +++ b/Ix/Theory/Certified/Signature.lean @@ -0,0 +1,162 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Checker +import Ix.Theory.Model.Signature + +/-! Internal formation of a finite signature. These operations publish no +admitted environment: membership and every computation equation must still +be constructed by the caller's semantic producer. -/ + +namespace Ix.Theory.Certified.Signature + +open Model + +universe u v +variable {β : Type u} [DecidableEq β] + +structure Header (β : Type u) where + ref : ConstRef β + universes : Nat + type : AExpr β + +def Header.entry (header : Header β) : ConstantEntry β := + ⟨header.universes, header.type, none, [], []⟩ + +def environment (entries : Environment β) : List (Header β) → Environment β + | [] => entries + | header :: rest => environment (entries.insert header.ref header.entry) rest + +structure TypeWitness (β : Type u) where + level : VLevel + typing : TypingWitness β + +inductive Formed : Environment β → List (Header β) → Prop where + | nil (entries) : Formed entries [] + | cons {entries header rest} (fresh : entries header.ref = none) + (scope : header.type.Scope header.universes 0) (references : header.type.ReferencesIn entries) + {level : VLevel} (typing : TypingClaim.{u,v} entries [] header.type (.sort level)) + (tail : Formed (entries.insert header.ref header.entry) rest) : Formed entries (header :: rest) + +def checkTypes (fuel : Nat) (entries : Environment β) : (headers : List (Header β)) → + List (TypeWitness β) → Option (CheckedClaim.{u} (Formed.{u,v} entries headers)) + | [], [] => some ⟨.nil entries⟩ + | header :: headers, witness :: witnesses => + if hf : entries header.ref = none then + if hs : header.type.Scope header.universes 0 then + if hr : header.type.ReferencesIn entries then do + let ht ← verifyType.{u,v} fuel header.universes entries [] header.type (.sort witness.level) witness.typing + let rest ← checkTypes fuel (entries.insert header.ref header.entry) headers witnesses + return ⟨.cons hf hs hr ht.down rest.down⟩ + else none + else none + else none + | _, _ => none + +theorem checkTypes_sound {fuel : Nat} {entries : Environment β} {headers : List (Header β)} + {witnesses : List (TypeWitness β)} {result} + (_ : checkTypes.{u,v} fuel entries headers witnesses = some result) : Formed.{u,v} entries headers := result.down + +theorem Formed.old {entries : Environment β} {headers : List (Header β)} + (h : Formed.{u,v} entries headers) {r : ConstRef β} {entry : ConstantEntry β} + (hr : entries r = some entry) : environment entries headers r = some entry := by + induction h with + | nil => exact hr + | cons hf _ _ _ _ ih => exact ih (Environment.insert_old hf hr) + +theorem Formed.lookup {entries : Environment β} {headers : List (Header β)} + (h : Formed.{u,v} entries headers) {header : Header β} (hm : header ∈ headers) : + environment entries headers header.ref = some header.entry := by + induction h with + | nil => cases hm + | @cons entries hd rest hf hs hr level ht tail ih => + rcases List.mem_cons.mp hm with he | hm + · subst header + exact tail.old (Environment.insert_same ..) + · exact ih hm + +theorem environment_source {entries : Environment β} {headers : List (Header β)} + {r : ConstRef β} {entry : ConstantEntry β} (h : environment entries headers r = some entry) : + entries r = some entry ∨ ∃ header ∈ headers, r = header.ref ∧ entry = header.entry := by + induction headers generalizing entries with + | nil => exact Or.inl h + | cons header rest ih => + rcases ih h with hold | ⟨hd, hm, he, hv⟩ + · unfold Environment.insert at hold + split at hold + · exact Or.inr ⟨header, List.mem_cons_self, ‹r = header.ref›, (Option.some.inj hold).symm⟩ + · exact Or.inl hold + · exact Or.inr ⟨hd, List.mem_cons_of_mem _ hm, he, hv⟩ + +theorem Formed.wf {entries : Environment β} {headers : List (Header β)} + (h : Formed.{u,v} entries headers) (hE : entries.WF) : (environment entries headers).WF := by + induction h with + | nil => exact hE + | cons _ hs hr _ _ ih => + apply ih + exact hE.insert hs (by simp [Header.entry]) hr (by simp [Header.entry]) + (by simp [Header.entry]) (by simp [Header.entry]) + (by simp [Header.entry]) (by simp [Header.entry]) + +structure Rule (β : Type u) where + universes : Nat + type : AExpr β + lhs : AExpr β + rhs : AExpr β + +structure RuleWitness (β : Type u) where + level : VLevel + type : TypingWitness β + lhs : TypingWitness β + rhs : TypingWitness β + +structure RuleFormed (entries : Environment β) (rule : Rule β) : Prop where + scope : rule.type.Scope rule.universes 0 ∧ rule.lhs.Scope rule.universes 0 ∧ rule.rhs.Scope rule.universes 0 + references : rule.type.ReferencesIn entries ∧ rule.lhs.ReferencesIn entries ∧ rule.rhs.ReferencesIn entries + type : ∃ level, TypingClaim.{u,v} entries [] rule.type (.sort level) + lhs : TypingClaim.{u,v} entries [] rule.lhs rule.type + rhs : TypingClaim.{u,v} entries [] rule.rhs rule.type + +/-- Both complete endpoints are checked against their common formed type, +before the environment has acquired the new computation equations. -/ +def checkRule (fuel : Nat) (entries : Environment β) (rule : Rule β) (witness : RuleWitness β) : + Option (CheckedClaim.{u} (RuleFormed.{u,v} entries rule)) := + if hs : rule.type.Scope rule.universes 0 ∧ rule.lhs.Scope rule.universes 0 ∧ rule.rhs.Scope rule.universes 0 then + if hr : rule.type.ReferencesIn entries ∧ rule.lhs.ReferencesIn entries ∧ rule.rhs.ReferencesIn entries then do + let ht ← verifyType.{u,v} fuel rule.universes entries [] rule.type (.sort witness.level) witness.type + let hl ← verifyType.{u,v} fuel rule.universes entries [] rule.lhs rule.type witness.lhs + let hh ← verifyType.{u,v} fuel rule.universes entries [] rule.rhs rule.type witness.rhs + return ⟨⟨hs, hr, ⟨witness.level, ht.down⟩, hl.down, hh.down⟩⟩ + else none + else none + +variable {V : Type v} [SetTheory V] +open SetTheory + +/-- Formation supplies hereditary validity. The caller supplies membership +from the concrete mathematical values, in the same assignment throughout. -/ +theorem Formed.realizes {entries : Environment β} {headers : List (Header β)} + (h : Formed.{u,v} entries headers) {constants : Assignment β V} (hM : Realizes constants entries) + (members : ∀ header ∈ headers, ∀ levels, levels.length = header.universes → ∀ env, + constants header.ref levels ∈ˢ interp constants levels env header.type) : + Realizes constants (environment entries headers) := by + induction h with + | nil => exact hM + | cons _ _ _ ht _ ih => + apply ih + · apply hM.insert + constructor + · intro levels _ env + exact (ht V constants hM levels env (Context.valid_nil constants levels env)).1 + · intro levels hn env + exact members _ List.mem_cons_self levels hn env + · intro body hb; cases hb + · intro body hb; cases hb + · intro law hl; cases hl + · intro fact hf; cases hf + · intro header hm + exact members header (List.mem_cons_of_mem _ hm) + +end Ix.Theory.Certified.Signature diff --git a/Ix/Theory/Certified/Source.lean b/Ix/Theory/Certified/Source.lean new file mode 100644 index 000000000..af60f6e20 --- /dev/null +++ b/Ix/Theory/Certified/Source.lean @@ -0,0 +1,153 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Store + +/-! Every admitted entry retains the exact type and universe arity of its +source member or constructor. Semantic facts and equations add evidence; +they do not replace the authenticated declaration's statement. -/ + +namespace Ix.Theory.Certified + +open Model Model.SetTheory + +universe u v +variable {β : Type u} + +structure SourceHeader (store : Store β) (ref : ConstRef β) (entry : ConstantEntry β) : Prop where + type : store.type ref = some entry.type.erase + universes : store.uvars ref = some entry.universes + +namespace Ordinary.Shape + +theorem family_sourceHeader {store : Store β} {shape : Ordinary.Shape β} {source : β} + (h : store.blocks source = some ⟨[shape.source source]⟩) : + SourceHeader store (.member source 0) shape.familyEntry := by + constructor <;> simp [Store.type, Store.uvars, Store.lookup, h, Shape.source, familyEntry, + Const.type, Const.uvars] + +theorem constructor_sourceHeader {store : Store β} {shape : Ordinary.Shape β} {source : β} + {index : Nat} {ctor : Ordinary.Constructor β} + (h : store.blocks source = some ⟨[shape.source source]⟩) + (hc : shape.constructors[index]? = some ctor) : + SourceHeader store (.ctor source 0 index) (shape.constructorEntry source ctor) := by + constructor <;> simp [Store.type, Store.uvars, Store.lookup, Store.lookupCtor, h, + Shape.source, List.getElem?_map, hc, Ordinary.Constructor.source, constructorEntry] + +theorem recursor_sourceHeader {store : Store β} {shape : Ordinary.Shape β} {source recursor : β} + {mode : Inductive.ElimMode} (h : shape.RecursorSourceMatches store source recursor mode) : + SourceHeader store (.member recursor 0) (shape.publishedRecursorEntry source recursor mode) := by + rcases h with h | ⟨_, h⟩ <;> + constructor <;> simp [Store.type, Store.uvars, Store.lookup, h, + recursorSource, publishedRecursorEntry, recursorEntry, Const.type, Const.uvars] + +end Ordinary.Shape + +theorem Ordinary.EntrySource.header {store : Store β} {ref : ConstRef β} {entry : ConstantEntry β} + (h : Ordinary.EntrySource store ref entry) : SourceHeader store ref entry := by + rcases h with ⟨shape, source, recursor, mode, hs, hr, h⟩ + rcases h with ⟨rfl, rfl⟩ | ⟨index, ctor, hc, rfl, rfl⟩ | ⟨rfl, rfl⟩ + · exact shape.family_sourceHeader hs + · exact shape.constructor_sourceHeader hs hc + · exact shape.recursor_sourceHeader hr + +theorem Standard.EntrySource.header {store : Store β} {ref : ConstRef β} {entry : ConstantEntry β} + (h : Standard.EntrySource store ref entry) : SourceHeader store ref entry := by + rcases h with ⟨spec, hs, rfl⟩ + constructor <;> simp [Store.type, Store.uvars, hs, Standard.Spec.source, Standard.Spec.entry, + Const.type, Const.uvars] + +theorem Quotient.EntrySource.header {store : Store β} {ref : ConstRef β} {entry : ConstantEntry β} + (h : Quotient.EntrySource store ref entry) : SourceHeader store ref entry := by + rcases h with ⟨refs, kind, hs, rfl, rfl⟩ + have he := hs kind (by cases kind <;> simp [Quotient.kinds]) + constructor <;> cases kind <;> + simp [Store.type, Store.uvars, he, Quotient.Refs.source, Quotient.Refs.entry, + Quotient.Refs.entryType, Quotient.Kind.universes, Const.type, Const.uvars] + +theorem Structure.EntrySource.header {store : Store β} {ref : ConstRef β} {entry : ConstantEntry β} + (h : Structure.EntrySource store ref entry) : SourceHeader store ref entry := by + rcases h with ⟨description, source, recursor, mode, hs, _, rfl, rfl⟩ + exact ⟨(description.ordinary.family_sourceHeader hs).type, + (description.ordinary.family_sourceHeader hs).universes⟩ + +theorem Natural.EntrySource.header {pin : Option (ConstRef β)} {store : Store β} + {ref : ConstRef β} {entry : ConstantEntry β} + (h : Natural.EntrySource pin store ref entry) : SourceHeader store ref entry := by + rcases h with ⟨_, source, recursor, mode, hs, _, rfl, rfl⟩ + exact ⟨(Ordinary.Shape.family_sourceHeader hs).type, + (Ordinary.Shape.family_sourceHeader hs).universes⟩ + +theorem Modeled.EntrySource.header {store : Store β} {ref : ConstRef β} {entry : ConstantEntry β} + (h : Modeled.EntrySource store ref entry) : SourceHeader store ref entry := by + obtain ⟨_, companion, hs, rfl, rfl⟩ := h + exact ⟨hs.1, hs.2.1⟩ + +/-- This covers every enabled admission form, including pinned primitives, +definition bodies, ordinary constructors and certified semantic extensions. -/ +theorem EntrySource.header {signature : PrimitiveSignature β} {store : Store β} + {ref : ConstRef β} {entry : ConstantEntry β} + (h : EntrySource signature store ref entry) : SourceHeader store ref entry := by + rcases h with ⟨rfl, rfl, hs⟩ | ⟨rfl, rfl, hs⟩ | ⟨kind, body, _, _, _, hs⟩ | h | h | h | h | h | h + · constructor <;> simp [Store.type, Store.uvars, hs, PrimitiveSignature.falseDeclaration, + PrimitiveSignature.falseEntry, Const.type, Const.uvars, AExpr.erase] + · constructor <;> simp [Store.type, Store.uvars, hs, PrimitiveSignature.falseElimDeclaration, + PrimitiveSignature.falseElimEntry, Const.type, Const.uvars] + · constructor <;> simp [Store.type, Store.uvars, hs, Const.type, Const.uvars] + · exact h.header + · exact h.header + · exact h.header + · exact h.header + · exact h.header + · exact h.header + +theorem AdmittedEnvironment.sourceHeader {signature : PrimitiveSignature β} {store : Store β} + (state : AdmittedEnvironment.{u,v} signature store) {ref : ConstRef β} {entry : ConstantEntry β} + (h : state.entries ref = some entry) : SourceHeader store ref entry := + (state.source ref entry h).header + +/-- Every requested declaration is tied to its original source header and +is valid in each compatible interpretation of the admitted interface. -/ +theorem CheckedStore.subject_sound {signature : PrimitiveSignature β} {store : Store β} + {subjects : List (ConstRef β)} (result : CheckedStore.{u,v} signature store subjects) + {ref : ConstRef β} (h : ref ∈ subjects) : + ∃ entry, result.environment.entries ref = some entry ∧ + SourceHeader store ref entry ∧ + ∀ (V : Type v) [SetTheory V] (constants : Assignment β V), + signature.Compatible result.environment.entries constants → + ∀ levels, levels.length = entry.universes → ∀ env, + WellDenoted constants levels env entry.type ∧ + constants ref levels ∈ˢ interp constants levels env entry.type := by + have hp := result.targetsPresent ref h + cases he : result.environment.entries ref with + | none => simp [he] at hp + | some entry => + refine ⟨entry, rfl, result.environment.sourceHeader he, ?_⟩ + intro V _ constants hM levels hl env + exact ⟨hM.realizes.typeValid ref entry he levels hl env, + hM.realizes.member ref entry he levels hl env⟩ + +/-- The exact source type has an inhabitant in a model constructed by the +checker; the source header is not an additional caller hypothesis. -/ +theorem accepted_store_source_sound [DecidableEq β] {fuel : Nat} {signature : PrimitiveSignature β} + {store : Store β} {subjects : List (ConstRef β)} {witness : List (DeclarationWitness β)} + (h : acceptsStoreCertified.{u,v} fuel signature store subjects witness = true) + (V : Type v) [SetTheory V] : + ∃ result : CheckedStore.{u,v} signature store subjects, + checkStoreCertified fuel signature store subjects witness = some result ∧ + ∃ constants : Assignment β V, + signature.Compatible result.environment.entries constants ∧ + ∀ ref ∈ subjects, ∃ entry, result.environment.entries ref = some entry ∧ + SourceHeader store ref entry ∧ + ∀ levels, levels.length = entry.universes → ∀ env, + WellDenoted constants levels env entry.type ∧ + constants ref levels ∈ˢ interp constants levels env entry.type := by + obtain ⟨result, checked, constants, compatible, _⟩ := accepted_store_has_model h V + refine ⟨result, checked, constants, compatible, ?_⟩ + intro ref hr + obtain ⟨entry, he, hs, hm⟩ := result.subject_sound hr + exact ⟨entry, he, hs, hm V constants compatible⟩ + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Standard/Admission.lean b/Ix/Theory/Certified/Standard/Admission.lean new file mode 100644 index 000000000..4fa38b320 --- /dev/null +++ b/Ix/Theory/Certified/Standard/Admission.lean @@ -0,0 +1,61 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Admission + +namespace Ix.Theory.Certified + +open Model + +universe u v +variable {β : Type u} [DecidableEq β] + +/-- Standard axiom schemas acquire values only after exact source, prerequisite +interface, and whole-type checks. No caller-supplied realization is accepted. -/ +def checkStandardExtension? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : CheckedInterface signature) (witness : Standard.Witness β) : + Option (CheckedExtension.{u,v} signature store state) := do + let checked ← Standard.check.{u,v} fuel state.entries store witness + have extension : Extends.{u,v} signature state.entries + (state.entries.insert witness.ref witness.spec.entry) := by + constructor + · exact fun _ _ h => Environment.insert_old checked.down.fresh h + · intro V _ constants hM + let constants' := Standard.assignment constants witness + have ha := Standard.assignment_agrees checked.down constants + have hm := Standard.assignment_realizes checked.down state.wf constants hM.realizes + exact ⟨constants', hM.extend signature state.present hm ha, ha⟩ + let result : CheckedInterface signature := { + entries := state.entries.insert witness.ref witness.spec.entry + wf := Standard.environment_wf checked.down state.wf + present := state.present.insert signature checked.down.fresh + } + return { + result, extension + source := by + intro r entry hr + dsimp only [result] at hr + unfold Environment.insert at hr + split at hr + next he => + subst r + cases Option.some.inj hr + exact Or.inr (Or.inr (Or.inr (Or.inr (Or.inr (Or.inl ⟨witness.spec, checked.down.exactSource, rfl⟩))))) + next => exact Or.inl hr + } + +def admitStandard? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : AdmittedEnvironment.{u,v} signature store) (witness : Standard.Witness β) : + Option { result : AdmittedEnvironment.{u,v} signature store // + Extends.{u,v} signature state.entries result.entries } := do + let checked ← checkStandardExtension?.{u,v} fuel (store := store) state.interface witness + return checked.admit state + +theorem admitStandard?_extends {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + {state : AdmittedEnvironment.{u,v} signature store} {witness : Standard.Witness β} {result} + (_ : admitStandard? fuel state witness = some result) : + Extends.{u,v} signature state.entries result.val.entries := result.property + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Standard/Checked.lean b/Ix/Theory/Certified/Standard/Checked.lean new file mode 100644 index 000000000..f2cd6dc34 --- /dev/null +++ b/Ix/Theory/Certified/Standard/Checked.lean @@ -0,0 +1,89 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Standard.Realization + +namespace Ix.Theory.Certified.Standard + +open Model Model.SetTheory + +universe u v +variable {β : Type u} [DecidableEq β] + +structure Witness (β : Type u) where + ref : ConstRef β + spec : Spec β + level : VLevel + typing : TypingWitness β + +structure Checked (entries : Environment β) (store : Store β) (witness : Witness β) : Prop where + exactSource : store.lookup witness.ref = some witness.spec.source + fresh : entries witness.ref = none + prerequisites : witness.spec.Prerequisites entries + scope : witness.spec.type.Scope witness.spec.universes 0 + references : witness.spec.type.ReferencesIn entries + typing : TypingClaim.{u,v} entries [] witness.spec.type (.sort witness.level) + +def check (fuel : Nat) (entries : Environment β) (store : Store β) (witness : Witness β) : + Option (CheckedClaim.{u} (Checked.{u,v} entries store witness)) := + if hs : store.lookup witness.ref = some witness.spec.source then + if hf : entries witness.ref = none then + if hp : witness.spec.Prerequisites entries then + if hc : witness.spec.type.Scope witness.spec.universes 0 then + if hr : witness.spec.type.ReferencesIn entries then do + let ht ← verifyType.{u,v} fuel witness.spec.universes entries [] + witness.spec.type (.sort witness.level) witness.typing + return ⟨⟨hs, hf, hp, hc, hr, ht.down⟩⟩ + else none + else none + else none + else none + else none + +theorem check_sound {fuel : Nat} {entries : Environment β} {store : Store β} {witness : Witness β} + {result} (_ : check.{u,v} fuel entries store witness = some result) : + Checked.{u,v} entries store witness := result.down + +def EntrySource (store : Store β) (ref : ConstRef β) (entry : ConstantEntry β) : Prop := + ∃ spec : Spec β, store.lookup ref = some spec.source ∧ entry = spec.entry + +variable {entries : Environment β} {store : Store β} {witness : Witness β} + +theorem environment_wf (h : Checked.{u,v} entries store witness) (hE : entries.WF) : + (entries.insert witness.ref witness.spec.entry).WF := by + apply hE.insert h.scope (by simp [Spec.entry]) h.references + (by simp [Spec.entry]) (by simp [Spec.entry]) (by simp [Spec.entry]) + (by simp [Spec.entry]) (by simp [Spec.entry]) + +variable {V : Type v} [SetTheory V] + +noncomputable def assignment (constants : Assignment β V) (witness : Witness β) : Assignment β V := + constants.insert witness.ref (witness.spec.value constants) + +theorem assignment_agrees (h : Checked.{u,v} entries store witness) (constants : Assignment β V) : + Assignment.AgreesOn entries constants (assignment constants witness) := + Assignment.insert_agrees h.fresh _ _ + +theorem assignment_realizes (h : Checked.{u,v} entries store witness) (hE : entries.WF) + (constants : Assignment β V) (hM : Realizes constants entries) : + Realizes (assignment constants witness) (entries.insert witness.ref witness.spec.entry) := by + have ha := assignment_agrees h constants + apply (hM.of_agrees hE ha).insert + constructor + · intro levels hn env + exact (h.typing V (assignment constants witness) (hM.of_agrees hE ha) levels env + (Context.valid_nil (assignment constants witness) levels env)).1 + · intro levels hn env + change (assignment constants witness) witness.ref levels ∈ˢ + interp (assignment constants witness) levels env witness.spec.type + rw [ha.interp h.references] + rw [assignment, Assignment.insert_same] + exact witness.spec.value_mem h.prerequisites hM levels hn env + · intro body hb; cases hb + · intro body hb; cases hb + · intro law hl; simp only [Spec.entry, List.not_mem_nil] at hl + · intro fact hf; simp only [Spec.entry, List.not_mem_nil] at hf + +end Ix.Theory.Certified.Standard diff --git a/Ix/Theory/Certified/Standard/Realization.lean b/Ix/Theory/Certified/Standard/Realization.lean new file mode 100644 index 000000000..5a7d3e534 --- /dev/null +++ b/Ix/Theory/Certified/Standard/Realization.lean @@ -0,0 +1,127 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Basis.Iff +import Ix.Theory.Certified.Basis.Nonempty + +/-! Realizations of the exact standard propext and choice types. Their +prerequisite meanings follow from admitted inductive interfaces in every +compatible prefix model, rather than being new semantic assumptions. -/ + +namespace Ix.Theory.Certified.Standard + +open Model Model.SetTheory Model.SetModel Basis + +universe u v +variable {β : Type u} + +inductive Spec (β : Type u) where + | propext (eq eqRefl eqRec iff iffIntro iffRec : ConstRef β) + | choice (nonempty intro recursor : ConstRef β) + +def Spec.universes : Spec β → Nat + | .propext .. => 0 + | .choice .. => 1 + +def propextType (eq iff : ConstRef β) : AExpr β := + .forallE .always (.sort .zero) <| .forallE .always (.sort .zero) <| + .forallE .always (Iff.applied iff (.bvar 1) (.bvar 0)) <| + Equality.applied eq (.succ .zero) (.sort .zero) (.bvar 2) (.bvar 1) + +def choiceType (nonempty : ConstRef β) : AExpr β := + .forallE (.param 0) (.sort (.param 0)) <| + .forallE (.param 0) (Nonempty.applied nonempty (.param 0) (.bvar 0)) (.bvar 1) + +def Spec.type : Spec β → AExpr β + | .propext eq _ _ iff _ _ => propextType eq iff + | .choice nonempty _ _ => choiceType nonempty + +def Spec.Prerequisites (entries : Environment β) : Spec β → Prop + | .propext eq eqRefl eqRec iff iffIntro iffRec => + Equality.Interface entries eq eqRefl eqRec ∧ Iff.Interface entries iff iffIntro iffRec + | .choice nonempty intro recursor => Nonempty.Interface entries nonempty intro recursor + +instance [DecidableEq β] (entries : Environment β) (spec : Spec β) : + Decidable (spec.Prerequisites entries) := by cases spec <;> unfold Spec.Prerequisites <;> infer_instance + +def Spec.entry (spec : Spec β) : ConstantEntry β := ⟨spec.universes, spec.type, none, [], []⟩ +def Spec.source (spec : Spec β) : Const β := .axiom spec.universes spec.type.erase .safe + +variable {V : Type v} [SetTheory V] + +open Classical in +noncomputable def chooseSet (A : V) : V := + if h : ∃ a, a ∈ˢ A then Classical.choose h else empty + +theorem chooseSet_mem {A : V} (h : ∃ a, a ∈ˢ A) : chooseSet A ∈ˢ A := by + rw [chooseSet, dif_pos h] + exact Classical.choose_spec h + +noncomputable def choiceValue (constants : Assignment β V) (nonempty : ConstRef β) (u : Nat) : V := + lamR (if u = 0 then 0 else 1) (univ u) (fun A => + lamR (if u = 0 then 0 else 1) (Nonempty.value constants nonempty u A) (fun _ => chooseSet A)) + +noncomputable def Spec.value (spec : Spec β) (constants : Assignment β V) (levels : List Nat) : V := + match spec with + | .propext .. => pt + | .choice nonempty _ _ => choiceValue constants nonempty (levels.getD 0 0) + +theorem propextType_interp (constants : Assignment β V) (eq iff : ConstRef β) (env : Nat → V) : + interp constants [] env (propextType eq iff) = + piR 0 univZero (fun P => piR 0 univZero (fun Q => + piR 0 (Iff.value constants iff P Q) (fun _ => Equality.value constants eq 1 univZero P Q))) := by + simp [propextType, Iff.applied, Equality.applied, Iff.value, Equality.value, + AExpr.appN, interp, VLevel.eval, Valuation.cons, univ_zero] + +theorem choiceType_interp (constants : Assignment β V) (nonempty : ConstRef β) (u : Nat) (env : Nat → V) : + interp constants [u] env (choiceType nonempty) = + piR (if u = 0 then 0 else 1) (univ u) (fun A => + piR (if u = 0 then 0 else 1) (Nonempty.value constants nonempty u A) (fun _ => A)) := by + simp [choiceType, Nonempty.applied, Nonempty.value, interp, VLevel.eval, Valuation.cons, + regime, PropWhen.param, PropWhen.holds] + +theorem propextValue_mem {entries : Environment β} {constants : Assignment β V} + {eq eqRefl eqRec iff iffIntro iffRec : ConstRef β} + (hE : Equality.Interface entries eq eqRefl eqRec) + (hI : Iff.Interface entries iff iffIntro iffRec) (hM : Realizes constants entries) (env : Nat → V) : + (pt : V) ∈ˢ interp constants [] env (propextType eq iff) := by + rw [propextType_interp] + apply pt_mem_piR_zero_of + intro P hP + apply pt_mem_piR_zero_of + intro Q hQ + apply pt_mem_piR_zero_of + intro proof hp + rw [Equality.value_eq_eqv hE hM (by simpa only [univ_zero] using univ_mem_univ (V := V) 0) hP hQ] + have he := Iff.eq_of_mem hI hM hP hQ hp + subst Q + exact pt_mem_eqv_self _ + +theorem choiceValue_mem {entries : Environment β} {constants : Assignment β V} + {nonempty intro recursor : ConstRef β} + (h : Nonempty.Interface entries nonempty intro recursor) (hM : Realizes constants entries) + (u : Nat) (env : Nat → V) : + choiceValue constants nonempty u ∈ˢ interp constants [u] env (choiceType nonempty) := by + rw [choiceType_interp] + apply lamR_mem + intro A hA + apply lamR_mem + intro proof hp + exact chooseSet_mem (Nonempty.exists_of_mem h hM hA hp) + +theorem Spec.value_mem {entries : Environment β} {constants : Assignment β V} + (spec : Spec β) (h : spec.Prerequisites entries) (hM : Realizes constants entries) + (levels : List Nat) (hn : levels.length = spec.universes) (env : Nat → V) : + spec.value constants levels ∈ˢ interp constants levels env spec.type := by + cases spec with + | propext eq eqRefl eqRec iff iffIntro iffRec => + have he : levels = [] := List.length_eq_zero_iff.mp hn + subst levels + exact propextValue_mem h.1 h.2 hM env + | choice nonempty intro recursor => + obtain ⟨u, rfl⟩ := List.length_eq_one_iff.mp hn + exact choiceValue_mem h hM u env + +end Ix.Theory.Certified.Standard diff --git a/Ix/Theory/Certified/Store.lean b/Ix/Theory/Certified/Store.lean new file mode 100644 index 000000000..2087aad95 --- /dev/null +++ b/Ix/Theory/Certified/Store.lean @@ -0,0 +1,69 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.Admission + +/-! Certification of requested declarations, including data declarations. +Logical proof acceptance additionally checks the requested proposition is in +Prop. A declaration certificate constructs a compatible model for every +requested type, body and admitted computation rule. -/ + +namespace Ix.Theory.Certified + +open Model Model.SetTheory + +universe u v +variable {β : Type u} [DecidableEq β] + +structure CheckedStore (signature : PrimitiveSignature β) (store : Store β) + (targets : List (ConstRef β)) where + environment : AdmittedEnvironment.{u,v} signature store + targetsPresent : ∀ r ∈ targets, (environment.entries r).isSome = true + +def checkStoreCertified (fuel : Nat) (signature : PrimitiveSignature β) (store : Store β) + (targets : List (ConstRef β)) (witness : List (DeclarationWitness β)) : + Option (CheckedStore.{u,v} signature store targets) := do + let initial ← initialize? signature store + let admitted ← admitDeclarations? fuel initial witness + if h : targets.all (fun r => (admitted.val.entries r).isSome) = true then + return ⟨admitted.val, List.all_eq_true.mp h⟩ + else none + +def acceptsStoreCertified (fuel : Nat) (signature : PrimitiveSignature β) (store : Store β) + (targets : List (ConstRef β)) (witness : List (DeclarationWitness β)) : Bool := + (checkStoreCertified.{u,v} fuel signature store targets witness).isSome + +/-- Successful admission constructs one compatible model for the entire +requested interface, at every permitted universe instance. -/ +theorem accepted_store_has_model {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + {targets : List (ConstRef β)} {witness : List (DeclarationWitness β)} + (h : acceptsStoreCertified.{u,v} fuel signature store targets witness = true) + (V : Type v) [SetTheory V] : + ∃ result : CheckedStore.{u,v} signature store targets, + checkStoreCertified fuel signature store targets witness = some result ∧ + ∃ constants : Assignment β V, + signature.Compatible result.environment.entries constants ∧ + ∀ r ∈ targets, ∃ entry, result.environment.entries r = some entry ∧ + EntrySource signature store r entry ∧ + ∀ levels, levels.length = entry.universes → ∀ env : Nat → V, + WellDenoted constants levels env entry.type ∧ + constants r levels ∈ˢ interp constants levels env entry.type := by + unfold acceptsStoreCertified at h + cases hc : checkStoreCertified.{u,v} fuel signature store targets witness with + | none => simp [hc] at h + | some result => + obtain ⟨constants, hM⟩ := result.environment.model V + refine ⟨result, rfl, constants, hM, ?_⟩ + intro r hr + have present := result.targetsPresent r hr + cases he : result.environment.entries r with + | none => simp [he] at present + | some entry => + refine ⟨entry, rfl, result.environment.source r entry he, ?_⟩ + intro levels hl env + exact ⟨hM.realizes.typeValid r entry he levels hl env, + hM.realizes.member r entry he levels hl env⟩ + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Structure/Admission.lean b/Ix/Theory/Certified/Structure/Admission.lean new file mode 100644 index 000000000..894546f1f --- /dev/null +++ b/Ix/Theory/Certified/Structure/Admission.lean @@ -0,0 +1,62 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Admission + +namespace Ix.Theory.Certified + +open Model + +universe u v +variable {β : Type u} [DecidableEq β] + +/-- Construct the structure and its projection facts in one extension of the +original prefix. This never strengthens an already admitted arbitrary model +by assuming that it used this producer's concrete field representation. -/ +def checkStructureExtension? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : CheckedInterface signature) (witness : Structure.Witness β) : + Option (CheckedExtension.{u,v} signature store state) := do + let checked ← Structure.check.{u,v} fuel state.entries store witness + let d := witness.facts.description + let block := witness.facts.block + let entries := d.publishedEnvironment state.entries block.source block.recursor block.mode + have extension : Extends.{u,v} signature state.entries entries := by + constructor + · intro r entry hr + exact d.publishedEnvironment_old checked.down hr + · intro V _ constants hM + let constants' := d.ordinary.recursorAssignment constants block.source block.recursor block.mode + have hm := d.publishedAssignment_realizes checked.down state.wf constants hM.realizes + have ha := Ordinary.Shape.publishedAssignment_agrees checked.down.facts.block constants + exact ⟨constants', hM.extend signature state.present hm ha, ha⟩ + let result : CheckedInterface signature := { + entries + wf := d.publishedEnvironment_wf checked.down state.wf + present := ⟨extension.lookup _ _ state.present.1, extension.lookup _ _ state.present.2⟩ + } + return { + result, extension + source := by + intro r entry hr + dsimp only [result] at hr + rcases Structure.publishedEntry_source checked.down hr with hold | hordinary | hstructure + · exact Or.inl hold + · exact Or.inr (Or.inr (Or.inr (Or.inr (Or.inl hordinary)))) + · exact Or.inr (Or.inr (Or.inr (Or.inr (Or.inr (Or.inr (Or.inr (Or.inl hstructure))))))) + } + +def admitStructure? (fuel : Nat) {signature : PrimitiveSignature β} {store : Store β} + (state : AdmittedEnvironment.{u,v} signature store) (witness : Structure.Witness β) : + Option { result : AdmittedEnvironment.{u,v} signature store // + Extends.{u,v} signature state.entries result.entries } := do + let checked ← checkStructureExtension?.{u,v} fuel (store := store) state.interface witness + return checked.admit state + +theorem admitStructure?_extends {fuel : Nat} {signature : PrimitiveSignature β} {store : Store β} + {state : AdmittedEnvironment.{u,v} signature store} {witness : Structure.Witness β} {result} + (_ : admitStructure? fuel state witness = some result) : + Extends.{u,v} signature state.entries result.val.entries := result.property + +end Ix.Theory.Certified diff --git a/Ix/Theory/Certified/Structure/Checked.lean b/Ix/Theory/Certified/Structure/Checked.lean new file mode 100644 index 000000000..66cc121a2 --- /dev/null +++ b/Ix/Theory/Certified/Structure/Checked.lean @@ -0,0 +1,117 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Structure.Computation +import Ix.Theory.Certified.Signature + +namespace Ix.Theory.Certified.Structure + +open Model + +universe u v +variable {β : Type u} [DecidableEq β] + +def Description.factEntry (d : Description β) (source : β) : ConstantEntry β := + { d.ordinary.familyEntry with facts := d.facts source } + +def Description.factEnvironment (d : Description β) (entries : Environment β) (source recursor : β) + (mode : Inductive.ElimMode) : Environment β := + (d.ordinary.publishedEnvironment entries source recursor mode).insert (.member source 0) (d.factEntry source) + +/-- Field i may use the already produced equations for earlier fields when +checking its dependent result. Its own equation is absent from this stage. -/ +def Description.iotaEnvironment (d : Description β) (entries : Environment β) (source recursor : β) + (mode : Inductive.ElimMode) (count : Nat) : Environment β := + (d.ordinary.publishedEnvironment entries source recursor mode).insert (.member source 0) + { d.factEntry source with equations := ((d.equations source).drop 1).take count } + +structure FactsWitness (β : Type u) where + description : Description β + block : Ordinary.BlockWitness β + fields : List (TypingWitness β) + domains : List (DomainWitness β) + +structure FactsChecked (entries : Environment β) (store : Store β) (d : Description β) + (source recursor : β) (mode : Inductive.ElimMode) : Prop where + block : Ordinary.CheckedBlock.{u,v} entries store source recursor d.ordinary mode + fields : FieldsFormed.{u,v} entries d.level d.ordinary.parameterContext d.fields + domains : Telescope.Formed.{u,v} (d.ordinary.publishedEnvironment entries source recursor mode) [] + (d.projectionDomains source) + scope : ∀ fact ∈ d.facts source, fact.Scope d.universes + references : ∀ fact ∈ d.facts source, + fact.ReferencesIn (d.ordinary.publishedEnvironment entries source recursor mode) + +def checkFacts (fuel : Nat) (entries : Environment β) (store : Store β) (witness : FactsWitness β) : + Option (CheckedClaim.{u} (FactsChecked.{u,v} entries store witness.description + witness.block.source witness.block.recursor witness.block.mode)) := + let d := witness.description + let block := witness.block + if he : block.shape.shape = d.ordinary then + if hs : ∀ fact ∈ d.facts block.source, fact.Scope d.universes then + if hr : ∀ fact ∈ d.facts block.source, + fact.ReferencesIn (d.ordinary.publishedEnvironment entries block.source block.recursor block.mode) then do + let hb ← Ordinary.checkBlock.{u,v} fuel entries store block + let hf ← checkFields.{u,v} fuel d.universes entries d.level d.ordinary.parameterContext d.fields witness.fields + let hd ← verifyTelescope.{u,v} fuel d.universes + (d.ordinary.publishedEnvironment entries block.source block.recursor block.mode) none [] + (d.projectionDomains block.source) witness.domains + return ⟨⟨he ▸ hb.down, hf.down, hd.down.1, hs, hr⟩⟩ + else none + else none + else none + +def Description.etaRule (d : Description β) (source : β) : Signature.Rule β := + ⟨d.universes, .forallN (zeroCondition d.level) (d.projectionDomains source) + (d.ordinary.familyApp source 1 []), d.etaLhs source, d.etaRhs source⟩ + +def Description.iotaRule (d : Description β) (source : β) (i : Nat) (field : Field β) : Signature.Rule β := + ⟨d.universes, .forallN (zeroCondition field.level) (d.parameters ++ d.constructor.fields) + (field.domain.liftN (d.fields.length - i)), d.iotaLhs source i field, d.iotaRhs i field⟩ + +structure Witness (β : Type u) where + facts : FactsWitness β + eta : Signature.RuleWitness β + iota : List (Signature.RuleWitness β) + +structure Checked (entries : Environment β) (store : Store β) (d : Description β) + (source recursor : β) (mode : Inductive.ElimMode) : Prop where + facts : FactsChecked.{u,v} entries store d source recursor mode + eta : Signature.RuleFormed.{u,v} (d.factEnvironment entries source recursor mode) (d.etaRule source) + iota : ∀ field i, (field, i) ∈ d.fields.zipIdx → + Signature.RuleFormed.{u,v} (d.iotaEnvironment entries source recursor mode i) (d.iotaRule source i field) + +def checkIota (fuel : Nat) (entries : Environment β) (d : Description β) (source recursor : β) + (mode : Inductive.ElimMode) : + (fields : List (Field β × Nat)) → List (Signature.RuleWitness β) → + Option (CheckedClaim.{u} (∀ field i, (field, i) ∈ fields → + Signature.RuleFormed.{u,v} (d.iotaEnvironment entries source recursor mode i) (d.iotaRule source i field))) + | [], [] => some ⟨by simp⟩ + | (field, i) :: fields, witness :: witnesses => do + let ht ← Signature.checkRule.{u,v} fuel (d.iotaEnvironment entries source recursor mode i) (d.iotaRule source i field) witness + let rest ← checkIota fuel entries d source recursor mode fields witnesses + return ⟨by + intro field' j hj + rcases List.mem_cons.mp hj with he | hj + · cases he; exact ht.down + · exact rest.down field' j hj⟩ + | _, _ => none + +def check (fuel : Nat) (entries : Environment β) (store : Store β) (witness : Witness β) : + Option (CheckedClaim.{u} (Checked.{u,v} entries store witness.facts.description + witness.facts.block.source witness.facts.block.recursor witness.facts.block.mode)) := do + let facts ← checkFacts.{u,v} fuel entries store witness.facts + let d := witness.facts.description + let block := witness.facts.block + let stage := d.factEnvironment entries block.source block.recursor block.mode + let eta ← Signature.checkRule.{u,v} fuel stage (d.etaRule block.source) witness.eta + let iota ← checkIota.{u,v} fuel entries d block.source block.recursor block.mode d.fields.zipIdx witness.iota + return ⟨⟨facts.down, eta.down, iota.down⟩⟩ + +theorem check_sound {fuel : Nat} {entries : Environment β} {store : Store β} {witness : Witness β} + {result} (_ : check.{u,v} fuel entries store witness = some result) : + Checked.{u,v} entries store witness.facts.description witness.facts.block.source + witness.facts.block.recursor witness.facts.block.mode := result.down + +end Ix.Theory.Certified.Structure diff --git a/Ix/Theory/Certified/Structure/Computation.lean b/Ix/Theory/Certified/Structure/Computation.lean new file mode 100644 index 000000000..a6fffc68f --- /dev/null +++ b/Ix/Theory/Certified/Structure/Computation.lean @@ -0,0 +1,84 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Structure.Reading + +namespace Ix.Theory.Certified.Structure.Description + +open Model Model.SetTheory Model.SetTheory.Tower Model.SetModel Ordinary + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + {d : Description β} {entries : Environment β} {store : Store β} {source : β} + {constants reading : Assignment β V} {levels : List Nat} + +theorem constructorApp_interp (h : CheckedShape.{u,v} entries store source d.ordinary) + (hr : ConstructorReading entries d.ordinary source constants reading) (hM : Realizes constants entries) + (hn : levels.length = d.universes) {ps xs : List V} + (hps : FitsS (Telescope.interpret constants levels (fun _ => empty) d.parameters) ps) + (hxs : FitsS (Telescope.interpret constants levels (Telescope.extend (fun _ => empty) ps) d.constructor.fields) xs) + (env : Nat → V) (args : List (AExpr β)) (hargs : args.map (interp reading levels env) = ps ++ xs) : + interp reading levels env (.appN (.const (.ctor source 0 0) (VLevel.params d.universes)) args) = + d.ordinary.constructorValue constants levels (Telescope.extend (fun _ => empty) ps) d.constructor 0 xs [] := by + rw [AExpr.interp_appN, interp, eval_params hn, hr.constructor levels hn 0 d.constructor rfl, hargs] + simpa only [List.append_nil] using Shape.constructorClosedValue_apply h hM + (show d.ordinary.constructors[0]? = some d.constructor from rfl) hps hxs + (show d.ordinary.RecursiveValuesFit constants levels (Telescope.extend (fun _ => empty) ps) d.constructor xs [] from trivial) + +theorem eta_eq (h : CheckedShape.{u,v} entries store source d.ordinary) + (hF : FieldsFormed.{u,v} entries d.level d.ordinary.parameterContext d.fields) + (hr : ConstructorReading entries d.ordinary source constants reading) (hM : Realizes constants entries) + (hn : levels.length = d.universes) : + interp reading levels (fun _ => empty) (d.etaLhs source) = + interp reading levels (fun _ => empty) (d.etaRhs source) := by + simp only [etaLhs, etaRhs, projectionDomains, AExpr.lamN_append, AExpr.interp_lamN, + hr.agrees.telescope (fun A hA => h.references A (by simpa [ordinary] using hA))] + apply Telescope.curry_congr + intro ps hps + change lamR _ (interp reading levels _ (d.ordinary.familyApp source 0 [])) _ = lamR _ _ _ + apply lamR_congr + intro x hx + have he := Shape.familyApp_interp hr.toFamilyReading hn (extra := []) (indices := []) hps (by trivial) + have hx' : x ∈ˢ app (d.ordinary.carrier constants levels (Telescope.extend (fun _ => empty) ps)) pt := by + simpa only [List.length_nil, List.map_nil, mkTower, Telescope.extend] using he ▸ hx + have hΓ := h.parameters.valid constants hM levels (Context.valid_nil constants levels (fun _ => empty)) hps + have hxs := d.projections_fit h hF hM hΓ hx' + change interp reading levels (Valuation.cons x _) (.appN _ _) = x + rw [d.constructorApp_interp h hr hM hn hps hxs _ _ (by + rw [List.map_append, projections_interp, interp_parameterVars_skip, + Valuation.skip_succ_cons, Valuation.skip_zero, ← FitsS.length_eq hps, interp_parameterVars_extend] + rfl)] + exact d.constructor_eta h hM hΓ hx' + +theorem iota_eq (h : CheckedShape.{u,v} entries store source d.ordinary) + (hF : FieldsFormed.{u,v} entries d.level d.ordinary.parameterContext d.fields) + (hr : ConstructorReading entries d.ordinary source constants reading) (hM : Realizes constants entries) + (hn : levels.length = d.universes) {i : Nat} {field : Field β} (hi : d.fields[i]? = some field) : + interp reading levels (fun _ => empty) (d.iotaLhs source i field) = + interp reading levels (fun _ => empty) (d.iotaRhs i field) := by + simp only [iotaLhs, iotaRhs, AExpr.lamN_append, AExpr.interp_lamN, + hr.agrees.telescope (fun A hA => h.references A (by simpa [ordinary] using hA))] + apply Telescope.curry_congr + intro ps hps + rw [hr.agrees.telescope (fun A hA => (h.constructors d.constructor (by simp [ordinary])).1 A + (List.mem_append_left _ hA))] + apply Telescope.curry_congr + intro xs hxs + have hΓ := h.parameters.valid constants hM levels (Context.valid_nil constants levels (fun _ => empty)) hps + have hlen : xs.length = d.fields.length := by simpa [constructor] using FitsS.length_eq hxs + have hib := (List.getElem?_eq_some_iff.mp hi).1 + change projectValue i (interp reading levels _ (.appN _ _)) = _ + rw [d.constructorApp_interp h hr hM hn hps hxs _ _ (by + rw [← Telescope.extend_append] + have hlength : (ps ++ xs).length = d.parameters.length + d.fields.length := by + rw [List.length_append, FitsS.length_eq hps, hlen] + rw [← hlength, interp_parameterVars_extend])] + have he := congrArg (fun zs : List V => zs.getD i empty) (d.constructor_iota hF hM hΓ hxs) + rw [projectValues_getD _ _ _ hib] at he + rw [he] + symm + simpa only [interp, hlen] using Telescope.extend_getD (Telescope.extend (fun _ => empty) ps) xs i (by omega) + +end Ix.Theory.Certified.Structure.Description diff --git a/Ix/Theory/Certified/Structure/Publish.lean b/Ix/Theory/Certified/Structure/Publish.lean new file mode 100644 index 000000000..0360343c5 --- /dev/null +++ b/Ix/Theory/Certified/Structure/Publish.lean @@ -0,0 +1,161 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Structure.Checked + +namespace Ix.Theory.Certified.Structure + +open Model Model.SetTheory + +universe u v +variable {β : Type u} [DecidableEq β] + +theorem ordinary_family_lookup {entries : Environment β} {store : Store β} + {source recursor : β} {shape : Ordinary.Shape β} {mode : Inductive.ElimMode} + (h : Ordinary.CheckedBlock.{u,v} entries store source recursor shape mode) : + shape.publishedEnvironment entries source recursor mode (.member source 0) = some shape.familyEntry := by + apply Environment.insert_old h.recursorChecked.fresh + simp [Ordinary.Shape.constructorEnvironment, Environment.overlay, Ordinary.Shape.constructorEntries, + Ordinary.Shape.familyEnvironment] + +namespace Description + +def publishedEntry (d : Description β) (source : β) : ConstantEntry β := + { d.factEntry source with equations := d.equations source } + +def publishedEnvironment (d : Description β) (entries : Environment β) (source recursor : β) + (mode : Inductive.ElimMode) : Environment β := + (d.ordinary.publishedEnvironment entries source recursor mode).insert (.member source 0) (d.publishedEntry source) + +variable {d : Description β} {entries : Environment β} {store : Store β} + {source recursor : β} {mode : Inductive.ElimMode} + +theorem iotaEnvironment_references {i : Nat} {e : AExpr β} + (h : e.ReferencesIn (d.iotaEnvironment entries source recursor mode i)) : + e.ReferencesIn (d.factEnvironment entries source recursor mode) := by + intro r hr + have he := h r hr + by_cases heq : r = .member source 0 + · simp [factEnvironment, Environment.insert, heq] + · simpa only [iotaEnvironment, factEnvironment, Environment.insert, if_neg heq] using he + +theorem factEnvironment_wf (h : FactsChecked.{u,v} entries store d source recursor mode) (hE : entries.WF) : + (d.factEnvironment entries source recursor mode).WF := by + have hbase := Ordinary.Shape.publishedEnvironment_wf h.block hE + have hfam := ordinary_family_lookup h.block + exact hbase.insert (hbase.typeScope _ d.ordinary.familyEntry hfam) (by simp [factEntry, Ordinary.Shape.familyEntry]) + (hbase.typeReferences _ d.ordinary.familyEntry hfam) (by simp [factEntry, Ordinary.Shape.familyEntry]) + (by simp [factEntry, Ordinary.Shape.familyEntry]) (by simp [factEntry, Ordinary.Shape.familyEntry]) + h.scope h.references + +theorem publishedEnvironment_wf (h : Checked.{u,v} entries store d source recursor mode) (hE : entries.WF) : + (d.publishedEnvironment entries source recursor mode).WF := by + have hbase := d.factEnvironment_wf h.facts hE + have hwf := hbase.insert (r := .member source 0) (entry := d.publishedEntry source) + (hbase.typeScope _ (d.factEntry source) (Environment.insert_same ..)) + (by simp [publishedEntry, factEntry, Ordinary.Shape.familyEntry]) + (hbase.typeReferences _ (d.factEntry source) (Environment.insert_same ..)) + (by simp [publishedEntry, factEntry, Ordinary.Shape.familyEntry]) + (by + intro law hl + rcases List.mem_cons.mp hl with he | hrest + · cases he; exact h.eta.scope.2 + · obtain ⟨⟨field, i⟩, hfi, he⟩ := List.mem_map.mp hrest + cases he + exact (h.iota field i hfi).scope.2) + (by + intro law hl + rcases List.mem_cons.mp hl with he | hrest + · cases he; exact h.eta.references.2 + · obtain ⟨⟨field, i⟩, hfi, he⟩ := List.mem_map.mp hrest + cases he + exact ⟨iotaEnvironment_references (h.iota field i hfi).references.2.1, + iotaEnvironment_references (h.iota field i hfi).references.2.2⟩) + h.facts.scope (fun fact hf => (h.facts.references fact hf).insert) + simpa only [publishedEnvironment, factEnvironment, Environment.insert_replace] using hwf + +theorem publishedEnvironment_old (h : Checked.{u,v} entries store d source recursor mode) + {r : ConstRef β} {entry : ConstantEntry β} (hr : entries r = some entry) : + d.publishedEnvironment entries source recursor mode r = some entry := by + simp only [publishedEnvironment, Environment.insert, + if_neg (fresh_ne (h.facts.block.shapeChecked.fresh _ (List.mem_cons_self ..)) hr)] + exact Ordinary.Shape.publishedEnvironment_old h.facts.block hr + +variable {V : Type v} [SetTheory V] + +theorem factAssignment_realizes (h : FactsChecked.{u,v} entries store d source recursor mode) + (hE : entries.WF) (constants : Assignment β V) (hM : Realizes constants entries) : + Realizes (d.ordinary.recursorAssignment constants source recursor mode) + (d.factEnvironment entries source recursor mode) := by + have hbase := Ordinary.Shape.publishedAssignment_realizes h.block hE constants hM + have hr := Ordinary.Shape.recursorAssignment_reading h.block.shapeChecked h.block.recursorChecked constants + apply hbase.insert + constructor + · exact hbase.typeValid _ d.ordinary.familyEntry (ordinary_family_lookup h.block) + · exact hbase.member _ d.ordinary.familyEntry (ordinary_family_lookup h.block) + · intro body hb; cases hb + · intro body hb; cases hb + · intro law hl; cases hl + · intro fact hf levels hn env + obtain ⟨⟨field, i⟩, hi, rfl⟩ := List.mem_map.mp hf + have hscope := h.scope _ (List.mem_map.mpr ⟨(field, i), hi, rfl⟩) + exact d.projection_meaning h.block.shapeChecked h.fields hr.toFamilyReading hM hn hbase h.domains + (List.mk_mem_zipIdx_iff_getElem?.mp hi) hscope env + +theorem publishedAssignment_realizes (h : Checked.{u,v} entries store d source recursor mode) + (hE : entries.WF) (constants : Assignment β V) (hM : Realizes constants entries) : + Realizes (d.ordinary.recursorAssignment constants source recursor mode) + (d.publishedEnvironment entries source recursor mode) := by + have hbase := d.factAssignment_realizes h.facts hE constants hM + have hr := Ordinary.Shape.recursorAssignment_reading h.facts.block.shapeChecked h.facts.block.recursorChecked constants + have hlocal : EntryRealization (d.ordinary.recursorAssignment constants source recursor mode) + (.member source 0) (d.publishedEntry source) := by + constructor + · exact hbase.typeValid _ (d.factEntry source) (Environment.insert_same ..) + · exact hbase.member _ (d.factEntry source) (Environment.insert_same ..) + · intro body hb; cases hb + · intro body hb; cases hb + · intro law hl levels hn env + rcases List.mem_cons.mp hl with he | hrest + · cases he + exact (interp_closed (d.etaLhs source) _ levels h.eta.scope.2.1 (fun _ => empty) env).symm.trans + ((d.eta_eq h.facts.block.shapeChecked h.facts.fields hr.toConstructorReading hM hn).trans + (interp_closed (d.etaRhs source) _ levels h.eta.scope.2.2 (fun _ => empty) env)) + · obtain ⟨⟨field, i⟩, hfi, he⟩ := List.mem_map.mp hrest + cases he + exact (interp_closed (d.iotaLhs source i field) _ levels (h.iota field i hfi).scope.2.1 (fun _ => empty) env).symm.trans + ((d.iota_eq h.facts.block.shapeChecked h.facts.fields hr.toConstructorReading hM hn + (List.mk_mem_zipIdx_iff_getElem?.mp hfi)).trans + (interp_closed (d.iotaRhs i field) _ levels (h.iota field i hfi).scope.2.2 (fun _ => empty) env)) + · exact hbase.factMeaning _ (d.factEntry source) (Environment.insert_same ..) + simpa only [publishedEnvironment, factEnvironment, Environment.insert_replace] using hbase.insert hlocal + +end Description + +/-- The enhanced family entry has an exact source shape and exact recursor; +the projection facts and equations are wholly computed from that shape. -/ +def EntrySource (store : Store β) (r : ConstRef β) (entry : ConstantEntry β) : Prop := + ∃ d : Description β, ∃ source recursor mode, + store.blocks source = some ⟨[d.ordinary.source source]⟩ ∧ + d.ordinary.RecursorSourceMatches store source recursor mode ∧ + r = .member source 0 ∧ entry = d.publishedEntry source + +theorem publishedEntry_source {entries : Environment β} {store : Store β} + {d : Description β} {source recursor : β} {mode : Inductive.ElimMode} + (h : Checked.{u,v} entries store d source recursor mode) {r : ConstRef β} {entry : ConstantEntry β} + (hr : d.publishedEnvironment entries source recursor mode r = some entry) : + entries r = some entry ∨ Ordinary.EntrySource store r entry ∨ EntrySource store r entry := by + unfold Description.publishedEnvironment Environment.insert at hr + split at hr + next he => + cases Option.some.inj hr + exact Or.inr (Or.inr ⟨d, source, recursor, mode, h.facts.block.shapeChecked.exactSource, + h.facts.block.recursorChecked.exactSource, he, rfl⟩) + next => + rcases Ordinary.publishedEntry_source h.facts.block hr with hold | hnew + · exact Or.inl hold + · exact Or.inr (Or.inl hnew) + +end Ix.Theory.Certified.Structure diff --git a/Ix/Theory/Certified/Structure/Reading.lean b/Ix/Theory/Certified/Structure/Reading.lean new file mode 100644 index 000000000..8d2c2b7ec --- /dev/null +++ b/Ix/Theory/Certified/Structure/Reading.lean @@ -0,0 +1,149 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Structure.Value + +namespace Ix.Theory.Certified.Structure + +open Model Model.SetTheory Model.SetTheory.Tower + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +theorem FieldsFormed.field_reading {entries : Environment β} {w : VLevel} {Γ : Context β} + {fields : List (Field β)} (h : FieldsFormed.{u,v} entries w Γ fields) + (constants : Assignment β V) (hM : Realizes constants entries) (levels : List Nat) + {env : Nat → V} (hΓ : Γ.Valid constants levels env) {xs : List V} + (hxs : FitsS (Telescope.interpret constants levels env (fields.map Field.domain)) xs) + {i : Nat} {field : Field β} (hf : fields[i]? = some field) : + WellDenoted constants levels (Telescope.extend env (xs.take i)) field.domain ∧ + interp constants levels (Telescope.extend env (xs.take i)) field.domain ∈ˢ + (univ (field.level.eval levels) : V) ∧ + xs.getD i empty ∈ˢ interp constants levels (Telescope.extend env (xs.take i)) field.domain := by + induction h generalizing env xs i with + | nil => simp at hf + | @cons Γ head rest hA hz htail ih => + cases xs with + | nil => exact hxs.elim + | cons x xs => + have ht := hA V constants hM levels env hΓ + cases i with + | zero => + cases Option.some.inj hf + exact ⟨ht.1, ht.2.2, hxs.1⟩ + | succ i => + exact ih (hΓ.push ht.1 hxs.1) hxs.2 hf + +theorem projections_interp (source : β) (count : Nat) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) : + (projections source count).map (interp constants levels env) = projectValues count (env 0) := by + apply List.ext_get (by simp [projections, projectValues_length]) + intro i hi hj + have hic : i < count := by simpa [projections] using hi + simpa only [List.get_eq_getElem, projections, List.getElem_map, List.getElem_range, interp, projectValue, projectValues] using + (projList_get count i (ssnd (sfst (env 0))) hic).symm + +theorem projectionValuation (env : Nat → V) (x : V) (i : Nat) : + Valuation.skip 1 i (Telescope.extend (Valuation.cons x env) (projectValues i x)) = + Telescope.extend env (projectValues i x) := by + simpa only [projectValues_length, Nat.add_zero, Valuation.skip_succ_cons, Valuation.skip_zero] using + Telescope.skip_extend_at (Valuation.cons x env) (projectValues i x) 1 0 + +theorem fieldResult_interp (source : β) (i : Nat) (field : Field β) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) (x : V) : + interp constants levels (Valuation.cons x env) (fieldResult source i field) = + interp constants levels (Telescope.extend env (projectValues i x)) field.domain := by + rw [fieldResult, AExpr.interp_instRev, projections_interp, interp_liftN] + exact congrArg (fun env => interp constants levels env field.domain) (projectionValuation env x i) + +theorem fieldResult_wellDenoted (source : β) (i : Nat) (field : Field β) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) (x : V) + (hf : WellDenoted constants levels (Telescope.extend env (projectValues i x)) field.domain) : + WellDenoted constants levels (Valuation.cons x env) (fieldResult source i field) := by + apply AExpr.wellDenoted_instRev + · intro e he + obtain ⟨j, _, rfl⟩ := List.mem_map.mp he + trivial + · rw [projections_interp, wellDenoted_liftN] + simpa only [Valuation.cons, projectionValuation] using hf + +namespace Description + +variable {d : Description β} {entries : Environment β} {store : Store β} {source : β} + {constants reading : Assignment β V} {levels : List Nat} + +/-- The dependent result uses the projections of the very same major value. +Its formation is derived from the checked field telescope in the old model. -/ +theorem fieldResult_meaning (h : Ordinary.CheckedShape.{u,v} entries store source d.ordinary) + (hF : FieldsFormed.{u,v} entries d.level d.ordinary.parameterContext d.fields) + (hr : Ordinary.FamilyReading entries d.ordinary source constants reading) + (hM : Realizes constants entries) {env : Nat → V} + (hΓ : d.ordinary.parameterContext.Valid constants levels env) {i : Nat} {field : Field β} + (hi : d.fields[i]? = some field) {x : V} + (hx : x ∈ˢ app (d.ordinary.carrier constants levels env) pt) : + WellDenoted reading levels (Valuation.cons x env) (fieldResult source i field) ∧ + interp reading levels (Valuation.cons x env) (fieldResult source i field) ∈ˢ + (univ (field.level.eval levels) : V) ∧ + projectValue i x ∈ˢ interp reading levels (Valuation.cons x env) (fieldResult source i field) := by + have hib := (List.getElem?_eq_some_iff.mp hi).1 + have hxs := d.projections_fit h hF hM hΓ hx + have ht := hF.field_reading constants hM levels hΓ hxs hi + rw [projectValues_take _ _ _ (Nat.le_of_lt hib), projectValues_getD _ _ _ hib] at ht + have href : field.domain.ReferencesIn entries := + (h.constructors d.constructor (by simp [ordinary])).1 field.domain + (by simpa [constructor] using List.mem_map.mpr ⟨field, List.mem_of_getElem? hi, rfl⟩) + refine ⟨fieldResult_wellDenoted source i field reading levels env x ?_, ?_, ?_⟩ + · exact (hr.agrees.wellDenoted href levels _).mpr ht.1 + · rw [fieldResult_interp, hr.agrees.interp href] + exact ht.2.1 + · rw [fieldResult_interp, hr.agrees.interp href] + exact ht.2.2 + +theorem projection_meaning (h : Ordinary.CheckedShape.{u,v} entries store source d.ordinary) + (hF : FieldsFormed.{u,v} entries d.level d.ordinary.parameterContext d.fields) + (hr : Ordinary.FamilyReading entries d.ordinary source constants reading) + (hM : Realizes constants entries) (hn : levels.length = d.universes) + {stage : Environment β} (hstage : Realizes reading stage) + (hD : Telescope.Formed.{u,v} stage [] (d.projectionDomains source)) + {i : Nat} {field : Field β} (hi : d.fields[i]? = some field) + (hs : (d.projection source i field).Scope d.universes 0 ∧ + (d.projectionType source i field).Scope d.universes 0) (env : Nat → V) : + (ConstantFact.typed (d.projection source i field) (d.projectionType source i field)).Meaning + reading (.member source 0) levels env := by + obtain ⟨_, _, ht⟩ := hD.lamN_semantics reading hstage levels + (.proj (.member source 0) i (.bvar 0)) (fieldResult source i field) field.level + have hm := ht (fun _ => empty) (Context.valid_nil reading levels (fun _ => empty)) (by + intro args hargs + obtain ⟨ps, zs, rfl, hps, hzs⟩ := (Telescope.fits_append_iff reading levels (fun _ => empty) + d.parameters [d.ordinary.familyApp source 0 []] args).mp hargs + have hp := hr.agrees.telescope (fun A hA => h.references A (by simpa [ordinary] using hA)) levels (fun _ => empty) + rw [hp] at hps + cases zs with + | nil => exact hzs.elim + | cons x rest => + cases rest with + | cons _ _ => exact hzs.2.elim + | nil => + have he := Ordinary.Shape.familyApp_interp hr hn (extra := []) (indices := []) hps (by trivial) + have hx : x ∈ˢ app (d.ordinary.carrier constants levels (Telescope.extend (fun _ => empty) ps)) pt := by + simpa only [List.length_nil, List.map_nil, mkTower, Telescope.extend] using he ▸ hzs.1 + have hΓ := h.parameters.valid constants hM levels (Context.valid_nil constants levels (fun _ => empty)) hps + have ht := d.fieldResult_meaning h hF hr hM hΓ hi hx + simpa only [Telescope.extend_append, Telescope.extend, interp, Valuation.cons_zero, WellDenoted] using + (show True ∧ WellDenoted reading levels (Valuation.cons x (Telescope.extend (fun _ => empty) ps)) + (fieldResult source i field) ∧ + projectValue i x ∈ˢ interp reading levels (Valuation.cons x (Telescope.extend (fun _ => empty) ps)) + (fieldResult source i field) ∧ + interp reading levels (Valuation.cons x (Telescope.extend (fun _ => empty) ps)) + (fieldResult source i field) ∈ˢ (univ (field.level.eval levels) : V) from + ⟨trivial, ht.1, ht.2.2, ht.2.1⟩)) + refine ⟨(wellDenoted_closed _ reading levels hs.1 (fun _ => empty) env).mp hm.1, + (wellDenoted_closed _ reading levels hs.2 (fun _ => empty) env).mp hm.2.1, ?_⟩ + rw [← interp_closed _ reading levels hs.1 (fun _ => empty) env, + ← interp_closed _ reading levels hs.2 (fun _ => empty) env] + exact hm.2.2.1 + +end Description +end Ix.Theory.Certified.Structure diff --git a/Ix/Theory/Certified/Structure/Syntax.lean b/Ix/Theory/Certified/Structure/Syntax.lean new file mode 100644 index 000000000..92d513150 --- /dev/null +++ b/Ix/Theory/Certified/Structure/Syntax.lean @@ -0,0 +1,111 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Ordinary.Checked +import Ix.Theory.Model.TelescopeSemantics + +namespace Ix.Theory.Certified.Structure + +open Model + +universe u v +variable {β : Type u} + +structure Field (β : Type u) where + domain : AExpr β + level : VLevel +deriving DecidableEq + +/-- A single nonrecursive constructor with dependent fields. The additional +field sorts license projections out of a proof-valued structure. -/ +structure Description (β : Type u) where + universes : Nat + parameters : List (AExpr β) + fields : List (Field β) + level : VLevel +deriving DecidableEq + +def Description.constructor (d : Description β) : Ordinary.Constructor β := + ⟨d.fields.map Field.domain, [], []⟩ + +def Description.ordinary (d : Description β) : Ordinary.Shape β := + ⟨d.universes, d.parameters, [], d.level, [d.constructor]⟩ + +def projections (source : β) (count : Nat) : List (AExpr β) := + (List.range count).map fun i => .proj (.member source 0) i (.bvar 0) + +/-- Insert the major premise below preceding fields, then substitute their +actual projections in outermost-first order. -/ +def fieldResult (source : β) (index : Nat) (field : Field β) : AExpr β := + (field.domain.liftN 1 index).instRev (projections source index) + +def Description.projectionDomains (d : Description β) (source : β) : List (AExpr β) := + d.parameters ++ [d.ordinary.familyApp source 0 []] + +def Description.projection (d : Description β) (source : β) (i : Nat) (field : Field β) : AExpr β := + .lamN (zeroCondition field.level) (d.projectionDomains source) (.proj (.member source 0) i (.bvar 0)) + +def Description.projectionType (d : Description β) (source : β) (i : Nat) (field : Field β) : AExpr β := + .forallN (zeroCondition field.level) (d.projectionDomains source) (fieldResult source i field) + +def Description.facts (d : Description β) (source : β) : List (ConstantFact β) := + d.fields.zipIdx.map fun (field, i) => .typed (d.projection source i field) (d.projectionType source i field) + +def Description.etaLhs (d : Description β) (source : β) : AExpr β := + .lamN (zeroCondition d.level) (d.projectionDomains source) + (.appN (.const (.ctor source 0 0) (VLevel.params d.universes)) + (Ordinary.parameterVars 1 d.parameters.length ++ projections source d.fields.length)) + +def Description.etaRhs (d : Description β) (source : β) : AExpr β := + .lamN (zeroCondition d.level) (d.projectionDomains source) (.bvar 0) + +def Description.iotaLhs (d : Description β) (source : β) (i : Nat) (field : Field β) : AExpr β := + .lamN (zeroCondition field.level) (d.parameters ++ d.constructor.fields) + (.proj (.member source 0) i (.appN (.const (.ctor source 0 0) (VLevel.params d.universes)) + (Ordinary.parameterVars 0 (d.parameters.length + d.fields.length)))) + +def Description.iotaRhs (d : Description β) (i : Nat) (field : Field β) : AExpr β := + .lamN (zeroCondition field.level) (d.parameters ++ d.constructor.fields) + (.bvar (d.fields.length - 1 - i)) + +def Description.equations (d : Description β) (source : β) : List (ConstantEquation β) := + ⟨d.etaLhs source, d.etaRhs source⟩ :: d.fields.zipIdx.map fun (field, i) => + ⟨d.iotaLhs source i field, d.iotaRhs i field⟩ + +inductive FieldsFormed (entries : Environment β) (w : VLevel) : Context β → List (Field β) → Prop + | nil {Γ} : FieldsFormed entries w Γ [] + | cons {Γ field rest} + (domain : TypingClaim.{u,v} entries Γ field.domain (.sort field.level)) + (proofField : ∀ levels, w.eval levels = 0 → field.level.eval levels = 0) + (tail : FieldsFormed entries w (Γ.push field.domain) rest) : + FieldsFormed entries w Γ (field :: rest) + +theorem FieldsFormed.formed {entries : Environment β} {w : VLevel} {Γ : Context β} + {fields : List (Field β)} (h : FieldsFormed.{u,v} entries w Γ fields) : + Telescope.Formed.{u,v} entries Γ (fields.map Field.domain) := by + induction h with + | nil => exact .nil + | cons hA _ _ ih => exact .cons _ hA ih + +theorem FieldsFormed.prop {entries : Environment β} {w : VLevel} {Γ : Context β} + {fields : List (Field β)} (h : FieldsFormed.{u,v} entries w Γ fields) : + TelescopeProp.{u,v} entries Γ (fields.map Field.domain) w := by + induction h with + | nil => exact TelescopeProp.nil entries _ w + | cons hA hz _ ih => exact TelescopeProp.cons hA hz ih + +def checkFields [DecidableEq β] (fuel n : Nat) (entries : Environment β) (w : VLevel) : + (Γ : Context β) → (fields : List (Field β)) → List (TypingWitness β) → + Option (CheckedClaim.{u} (FieldsFormed.{u,v} entries w Γ fields)) + | _, [], [] => some ⟨.nil⟩ + | Γ, field :: fields, witness :: witnesses => + if hz : checkZeroImplies w field.level = true then do + let hA ← verifyType.{u,v} fuel n entries Γ field.domain (.sort field.level) witness + let rest ← checkFields fuel n entries w (Γ.push field.domain) fields witnesses + return ⟨.cons hA.down (checkZeroImplies_sound hz) rest.down⟩ + else none + | _, _, _ => none + +end Ix.Theory.Certified.Structure diff --git a/Ix/Theory/Certified/Structure/Value.lean b/Ix/Theory/Certified/Structure/Value.lean new file mode 100644 index 000000000..0595f507a --- /dev/null +++ b/Ix/Theory/Certified/Structure/Value.lean @@ -0,0 +1,141 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Structure.Syntax + +namespace Ix.Theory.Certified.Structure + +open Model Model.SetTheory Model.SetTheory.Tower + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +noncomputable def projectValues (count : Nat) (x : V) : List V := + projList count (ssnd (sfst x)) + +theorem projectValues_length (count : Nat) (x : V) : + (projectValues count x).length = count := projList_length .. + +theorem projectValues_getD (count i : Nat) (x : V) (hi : i < count) : + (projectValues count x).getD i empty = projectValue i x := by + rw [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem + (show i < (projectValues count x).length by simpa only [projectValues_length] using hi), Option.getD_some] + exact projList_get count i (ssnd (sfst x)) hi + +theorem projectValues_take (count i : Nat) (x : V) (hi : i ≤ count) : + (projectValues count x).take i = projectValues i x := projList_take count i _ hi + +theorem projectValues_node (count : Nat) (xs : List V) (hlen : xs.length = count) (g : V) : + projectValues count (spair (inj 0 (mkTower xs)) g) = xs := by + simp only [projectValues, sfst_spair, ssnd_inj, projList_mkTower _ _ hlen] + +theorem projectValues_pt (count : Nat) : + projectValues count (pt : V) = List.replicate count pt := by + simp only [projectValues, sfst_pt, ssnd_pt, projList_pt] + +namespace Description + +variable (d : Description β) (constants : Assignment β V) (levels : List Nat) (env : Nat → V) + +theorem positions_empty (xs : List V) (hlen : xs.length = d.constructor.fields.length) : + d.ordinary.positions constants levels env (inj 0 (mkTower xs)) = empty := by + apply ext + intro p + constructor + · intro hp + obtain ⟨j, field, _, hj, _, _⟩ := Ordinary.Shape.mem_positions + (show d.ordinary.constructors[0]? = some d.constructor from rfl) hlen hp + simp [constructor] at hj + · intro hp + exact (not_mem_empty p hp).elim + +theorem branches_empty (xs : List V) (hlen : xs.length = d.constructor.fields.length) : + d.ordinary.branches constants levels env d.constructor 0 xs [] = empty := by + rw [Ordinary.Shape.branches, d.positions_empty constants levels env xs hlen] + apply ext + intro p + constructor + · intro hp + obtain ⟨x, hx, _⟩ := mem_graph.mp hp + exact (not_mem_empty x hx).elim + · intro hp + exact (not_mem_empty p hp).elim + +theorem constructorValue_eq (xs : List V) (hlen : xs.length = d.constructor.fields.length) : + d.ordinary.constructorValue constants levels env d.constructor 0 xs [] = + IndexedContainer.node (d.level.eval levels) (inj 0 (mkTower xs)) empty := by + rw [Ordinary.Shape.constructorValue, d.branches_empty constants levels env xs hlen] + rfl + +variable {d constants levels env} {entries : Environment β} {store : Store β} {source : β} + +theorem carrier_member (h : Ordinary.CheckedShape.{u,v} entries store source d.ordinary) + (hM : Realizes constants entries) (hΓ : d.ordinary.parameterContext.Valid constants levels env) + {x : V} (hx : x ∈ˢ app (d.ordinary.carrier constants levels env) pt) : + ∃ xs, FitsS (Telescope.interpret constants levels env d.constructor.fields) xs ∧ + x = IndexedContainer.node (d.level.eval levels) (inj 0 (mkTower xs)) empty := by + rw [Ordinary.Shape.carrier, IndexedContainer.carrier_eq (Ordinary.Shape.container_wf h hM hΓ) + (show (pt : V) ∈ˢ (d.ordinary.container constants levels env).indices from pt_mem_unitSet)] at hx + obtain ⟨a, ha, g, hg, rfl⟩ := IndexedContainer.mem_fibre.mp hx + obtain ⟨i, ctor, xs, hc, hxs, rfl⟩ := Ordinary.Shape.mem_allShapes (mem_sep.mp ha).1 + have hi : i = 0 := by + have hi := (List.getElem?_eq_some_iff.mp hc).1 + simp only [ordinary, List.length_singleton] at hi + omega + subst i + have he : ctor = d.constructor := by simpa only [ordinary, List.getElem?_cons_zero, Option.some.injEq] using hc.symm + subst ctor + have hg' : g = empty := by + change g ∈ˢ piSet (d.ordinary.positions constants levels env _) _ at hg + rw [d.positions_empty constants levels env xs (FitsS.length_eq hxs)] at hg + rw [← eq_graph_app_of_mem_piSet hg] + apply ext + intro p + constructor + · intro hp + obtain ⟨x, hx, _⟩ := mem_graph.mp hp + exact (not_mem_empty x hx).elim + · intro hp + exact (not_mem_empty p hp).elim + exact ⟨xs, hxs, congrArg (IndexedContainer.node (d.level.eval levels) (inj 0 (mkTower xs))) hg'⟩ + +theorem projections_fit (h : Ordinary.CheckedShape.{u,v} entries store source d.ordinary) + (hF : FieldsFormed.{u,v} entries d.level d.ordinary.parameterContext d.fields) + (hM : Realizes constants entries) (hΓ : d.ordinary.parameterContext.Valid constants levels env) + {x : V} (hx : x ∈ˢ app (d.ordinary.carrier constants levels env) pt) : + FitsS (Telescope.interpret constants levels env d.constructor.fields) (projectValues d.fields.length x) := by + obtain ⟨xs, hxs, rfl⟩ := carrier_member h hM hΓ hx + have hlen : xs.length = d.fields.length := by simpa [constructor] using FitsS.length_eq hxs + by_cases hw : d.level.eval levels = 0 + · rw [IndexedContainer.node, if_pos hw, projectValues_pt] + simpa only [constructor, List.length_map] using fitsS_replicate_of_prop (hF.prop V constants hM levels env hΓ hw) hxs + · rw [IndexedContainer.node, if_neg hw, projectValues_node _ xs hlen] + exact hxs + +theorem constructor_eta (h : Ordinary.CheckedShape.{u,v} entries store source d.ordinary) + (hM : Realizes constants entries) (hΓ : d.ordinary.parameterContext.Valid constants levels env) + {x : V} (hx : x ∈ˢ app (d.ordinary.carrier constants levels env) pt) : + d.ordinary.constructorValue constants levels env d.constructor 0 (projectValues d.fields.length x) [] = x := by + obtain ⟨xs, hxs, rfl⟩ := carrier_member h hM hΓ hx + rw [d.constructorValue_eq constants levels env _ (by simp [constructor, projectValues_length])] + by_cases hw : d.level.eval levels = 0 + · simp only [IndexedContainer.node, if_pos hw] + · simp only [IndexedContainer.node, if_neg hw, + projectValues_node _ xs (by simpa [constructor] using FitsS.length_eq hxs)] + +theorem constructor_iota (hF : FieldsFormed.{u,v} entries d.level d.ordinary.parameterContext d.fields) + (hM : Realizes constants entries) (hΓ : d.ordinary.parameterContext.Valid constants levels env) + {xs : List V} (hxs : FitsS (Telescope.interpret constants levels env d.constructor.fields) xs) : + projectValues d.fields.length (d.ordinary.constructorValue constants levels env d.constructor 0 xs []) = xs := by + have hlen := FitsS.length_eq hxs + rw [d.constructorValue_eq constants levels env xs hlen] + by_cases hw : d.level.eval levels = 0 + · rw [IndexedContainer.node, if_pos hw, projectValues_pt] + exact Telescope.fits_unique_of_prop (hF.prop V constants hM levels env hΓ hw) + (by simpa only [List.length_map] using fitsS_replicate_of_prop (hF.prop V constants hM levels env hΓ hw) hxs) hxs + · rw [IndexedContainer.node, if_neg hw, projectValues_node _ xs (by simpa [constructor] using hlen)] + +end Description +end Ix.Theory.Certified.Structure diff --git a/Ix/Theory/Certified/Telescope.lean b/Ix/Theory/Certified/Telescope.lean new file mode 100644 index 000000000..06c831053 --- /dev/null +++ b/Ix/Theory/Certified/Telescope.lean @@ -0,0 +1,172 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Checker +import Ix.Theory.Model.Inductive.Telescope + +namespace Ix.Theory.Certified + +open Model Model.SetTheory Model.SetTheory.Tower + +universe u v +variable {β : Type u} [DecidableEq β] + +structure DomainWitness (β : Type u) where + level : VLevel + typing : TypingWitness β + +/-- A positive carrier bounds each field domain. Prop carriers may quantify +over larger domains; this does not by itself license large elimination. -/ +def checkDomainBound (n : Nat) (bound : Option VLevel) (level : VLevel) : Bool := + match bound with + | none => true + | some w => LevelEq.check n w .zero || LevelEq.check n (.max level w) w + +theorem checkDomainBound_sound {n : Nat} {bound : Option VLevel} {level : VLevel} + (h : checkDomainBound n bound level = true) : + ∀ w, bound = some w → ∀ levels, + w.eval levels ≠ 0 → level.eval levels ≤ w.eval levels := by + intro w hw levels hpos + subst bound + simp only [checkDomainBound, Bool.or_eq_true] at h + rcases h with h | h + · exact (hpos ((LevelEq.check_sound h).2.2 levels)).elim + · have he := (LevelEq.check_sound h).2.2 levels + change max (level.eval levels) (w.eval levels) = w.eval levels at he + exact he ▸ Nat.le_max_left _ _ + +/-- Bounds are consequences of checked field sorts, uniformly over every +compatible model and satisfying prefix. -/ +def TelescopeBound (entries : Environment β) (Γ : Context β) (domains : List (AExpr β)) + (bound : Option VLevel) : Prop := + ∀ w, bound = some w → ∀ (V : Type v) [SetTheory V] (constants : Assignment β V), + Realizes constants entries → ∀ levels env, Γ.Valid constants levels env → + w.eval levels ≠ 0 → BoundS (w.eval levels) (Telescope.interpret constants levels env domains) + +omit [DecidableEq β] in +theorem TelescopeBound.nil (entries : Environment β) (Γ : Context β) (bound : Option VLevel) : + TelescopeBound.{u,v} entries Γ [] bound := fun _ _ _ _ _ _ _ _ _ _ => trivial + +omit [DecidableEq β] in +theorem TelescopeBound.cons {entries : Environment β} {Γ : Context β} + {A : AExpr β} {rest : List (AExpr β)} {bound : Option VLevel} {l : VLevel} + (hA : TypingClaim.{u,v} entries Γ A (.sort l)) + (hl : ∀ w, bound = some w → ∀ levels, w.eval levels ≠ 0 → l.eval levels ≤ w.eval levels) + (hrest : TelescopeBound.{u,v} entries (Γ.push A) rest bound) : + TelescopeBound.{u,v} entries Γ (A :: rest) bound := by + intro w hw V _ constants hM levels env hΓ hpos + have htype := hA V constants hM levels env hΓ + refine ⟨univ_mono (hl w hw levels hpos) _ htype.2.2, ?_⟩ + intro x hx + exact hrest w hw V constants hM levels (Valuation.cons x env) (hΓ.push htype.1 hx) hpos + +/-- A finite witness contains only proposed sorts and typing rule choices. +Every domain is checked in the context extended by its actual predecessors. -/ +def verifyTelescope (fuel n : Nat) (entries : Environment β) (bound : Option VLevel) : + (Γ : Context β) → (domains : List (AExpr β)) → List (DomainWitness β) → + Option (CheckedClaim.{u} (Telescope.Formed.{u,v} entries Γ domains ∧ + TelescopeBound.{u,v} entries Γ domains bound)) + | Γ, [], [] => some ⟨⟨.nil, TelescopeBound.nil entries Γ bound⟩⟩ + | Γ, A :: rest, witness :: witnesses => + if hl : checkDomainBound n bound witness.level = true then do + let hA ← verifyType.{u,v} fuel n entries Γ A (.sort witness.level) witness.typing + let hrest ← verifyTelescope fuel n entries bound (Γ.push A) rest witnesses + return ⟨⟨.cons witness.level hA.down hrest.down.1, + TelescopeBound.cons hA.down (checkDomainBound_sound hl) hrest.down.2⟩⟩ + else none + | _, _, _ => none + +theorem verifyTelescope_sound {fuel n : Nat} {entries : Environment β} {bound : Option VLevel} + {Γ : Context β} {domains : List (AExpr β)} {witnesses : List (DomainWitness β)} + {result} (_ : verifyTelescope.{u,v} fuel n entries bound Γ domains witnesses = some result) : + Telescope.Formed.{u,v} entries Γ domains ∧ TelescopeBound.{u,v} entries Γ domains bound := + result.down + +/-- Indices are checked against their actual dependent telescope, with every +earlier argument substituted before the next check. -/ +def ArgumentsFit (entries : Environment β) (Γ : Context β) + (domains args : List (AExpr β)) : Prop := + ∀ (V : Type v) [SetTheory V] (constants : Assignment β V), Realizes constants entries → + ∀ levels env, Γ.Valid constants levels env → + FitsS (Telescope.interpret constants levels env domains) (args.map (interp constants levels env)) + +omit [DecidableEq β] in +theorem ArgumentsFit.nil (entries : Environment β) (Γ : Context β) : + ArgumentsFit.{u,v} entries Γ [] [] := fun _ _ _ _ _ _ _ => trivial + +omit [DecidableEq β] in +theorem ArgumentsFit.cons {entries : Environment β} {Γ : Context β} + {A a : AExpr β} {domains args : List (AExpr β)} + (ha : TypingClaim.{u,v} entries Γ a A) + (hrest : ArgumentsFit.{u,v} entries Γ (Telescope.inst a domains) args) : + ArgumentsFit.{u,v} entries Γ (A :: domains) (a :: args) := by + intro V _ constants hM levels env hΓ + refine ⟨(ha V constants hM levels env hΓ).2.2, ?_⟩ + have h := (Telescope.fits_inst constants levels env a domains 0 _).mp + (hrest V constants hM levels env hΓ) + simpa only [Valuation.skip_zero, Valuation.insert_zero] using h + +def verifyArguments (fuel n : Nat) (entries : Environment β) (Γ : Context β) : + (domains args : List (AExpr β)) → List (TypingWitness β) → + Option (CheckedClaim.{u} (ArgumentsFit.{u,v} entries Γ domains args)) + | [], [], [] => some ⟨ArgumentsFit.nil entries Γ⟩ + | A :: domains, a :: args, witness :: witnesses => do + let ha ← verifyType.{u,v} fuel n entries Γ a A witness + let hrest ← verifyArguments fuel n entries Γ (Telescope.inst a domains) args witnesses + return ⟨ArgumentsFit.cons ha.down hrest.down⟩ + | _, _, _ => none + +theorem verifyArguments_sound {fuel n : Nat} {entries : Environment β} {Γ : Context β} + {domains args : List (AExpr β)} {witnesses : List (TypingWitness β)} + {result} (_ : verifyArguments.{u,v} fuel n entries Γ domains args witnesses = some result) : + ArgumentsFit.{u,v} entries Γ domains args := result.down + +def checkZeroImplies (a b : VLevel) : Bool := + decide ((zeroCondition a).inter (zeroCondition b) = zeroCondition a) + +theorem checkZeroImplies_sound {a b : VLevel} (h : checkZeroImplies a b = true) + (levels : List Nat) (ha : a.eval levels = 0) : b.eval levels = 0 := by + have he : (zeroCondition a).inter (zeroCondition b) = zeroCondition a := of_decide_eq_true h + have hv := congrArg (PropWhen.holds (levels.getD · 0)) he + simpa only [PropWhen.holds_inter, zeroCondition_correct, ha, beq_self_eq_true, + Bool.true_and, beq_iff_eq] using hv + +def TelescopeProp (entries : Environment β) (Γ : Context β) (domains : List (AExpr β)) + (atLevel : VLevel) : Prop := + ∀ (V : Type v) [SetTheory V] (constants : Assignment β V), Realizes constants entries → + ∀ levels env, Γ.Valid constants levels env → atLevel.eval levels = 0 → + PropS (Telescope.interpret constants levels env domains) + +omit [DecidableEq β] in +theorem TelescopeProp.nil (entries : Environment β) (Γ : Context β) (atLevel : VLevel) : + TelescopeProp.{u,v} entries Γ [] atLevel := fun _ _ _ _ _ _ _ _ => trivial + +omit [DecidableEq β] in +theorem TelescopeProp.cons {entries : Environment β} {Γ : Context β} + {A : AExpr β} {rest : List (AExpr β)} {atLevel l : VLevel} + (hA : TypingClaim.{u,v} entries Γ A (.sort l)) + (hl : ∀ levels, atLevel.eval levels = 0 → l.eval levels = 0) + (htail : TelescopeProp.{u,v} entries (Γ.push A) rest atLevel) : + TelescopeProp.{u,v} entries Γ (A :: rest) atLevel := by + intro V _ constants hM levels env hΓ hz + have htype := hA V constants hM levels env hΓ + refine ⟨?_, fun x hx => htail V constants hM levels _ (hΓ.push htype.1 hx) hz⟩ + simpa only [interp, hl levels hz, univ_zero] using htype.2.2 + +/-- The singleton exception checks proof-valued ordinary fields. Merely +having one constructor is insufficient for large elimination from Prop. -/ +def verifyPropTelescope (fuel n : Nat) (entries : Environment β) (atLevel : VLevel) : + (Γ : Context β) → (domains : List (AExpr β)) → List (DomainWitness β) → + Option (CheckedClaim.{u} (TelescopeProp.{u,v} entries Γ domains atLevel)) + | Γ, [], [] => some ⟨TelescopeProp.nil entries Γ atLevel⟩ + | Γ, A :: rest, witness :: witnesses => + if hl : checkZeroImplies atLevel witness.level = true then do + let hA ← verifyType.{u,v} fuel n entries Γ A (.sort witness.level) witness.typing + let htail ← verifyPropTelescope fuel n entries atLevel (Γ.push A) rest witnesses + return ⟨TelescopeProp.cons hA.down (checkZeroImplies_sound hl) htail.down⟩ + else none + | _, _, _ => none + +end Ix.Theory.Certified diff --git a/Ix/Theory/Const.lean b/Ix/Theory/Const.lean new file mode 100644 index 000000000..59800d894 --- /dev/null +++ b/Ix/Theory/Const.lean @@ -0,0 +1,165 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Expr + +namespace Ix.Theory + +/-- Whether a declaration participates in the safe kernel fragment. -/ +inductive Safety where + | safe + | «unsafe» + | «partial» + deriving DecidableEq, Repr + +/-- The reduction behavior of a definition-like constant. -/ +inductive DefKind where + | definition + | theorem + | opaque + deriving DecidableEq, Repr + +/-- The four primitive constants that make up Lean's quotient interface. -/ +inductive QuotKind where + | type + | ctor + | lift + | ind + deriving DecidableEq, Repr + +/-- Trusted constructor metadata nested under its inductive family. -/ +structure Ctor (β : Type u) where + uvars : Nat + nparams : Nat + nfields : Nat + type : VExpr β + safety : Safety + deriving DecidableEq + +/-- One recursor reduction rule, positional in its constructor list. -/ +structure RecRule (β : Type u) where + nfields : Nat + rhs : VExpr β + deriving DecidableEq + +/-- Anonymous constant data stored in a content-addressed block. -/ +inductive Const (β : Type u) where + | axiom (uvars : Nat) (type : VExpr β) (safety : Safety) + | defn (uvars : Nat) (kind : DefKind) (type value : VExpr β) + (safety : Safety) + | quot (kind : QuotKind) (uvars : Nat) (type : VExpr β) + | induct (uvars nparams nindices : Nat) (type : VExpr β) + (ctors : List (Ctor β)) (safety : Safety) + | recursor (uvars nparams nindices nmotives nminors : Nat) (type : VExpr β) + (rules : List (RecRule β)) (k : Bool) (safety : Safety) + deriving DecidableEq + +/-- A content-addressed unit. Members are ordered and addressed positionally. -/ +structure Block (β : Type u) where + members : List (Const β) + deriving DecidableEq + +/-- A compact tag shared by ordinary members and nested constructors. -/ +inductive ConstKind where + | axiom + | defn (kind : DefKind) + | quot (kind : QuotKind) + | induct + | ctor + | recursor + deriving DecidableEq, Repr + +namespace Ctor + +def refs (ctor : Ctor β) : List (ConstRef β) := ctor.type.refs + +/-- Syntactic closure of a constructor declaration. -/ +def Closed (ctor : Ctor β) : Prop := ctor.type.Closed + +/-- The constructor's declared field telescope, excluding shared parameters. -/ +def fieldTypes (ctor : Ctor β) : List (VExpr β) := + (ctor.type.telN (ctor.nparams + ctor.nfields)).drop ctor.nparams + +/-- Whether a field type refers to any member of the indicated block. -/ +def recursiveInBlock [DecidableEq β] (ctor : Ctor β) (block : β) : Bool := + ctor.fieldTypes.any fun field => + field.refs.any fun ref => ref.block == block + +end Ctor + +namespace RecRule + +def refs (rule : RecRule β) : List (ConstRef β) := rule.rhs.refs + +/-- Syntactic closure of a stored recursor rule. -/ +def Closed (rule : RecRule β) : Prop := rule.rhs.Closed + +end RecRule + +namespace Const + +/-- Every trusted expression carried by a constant is term-variable closed. -/ +def Closed : Const β → Prop + | .axiom _ type _ => type.Closed + | .defn _ _ type value _ => type.Closed ∧ value.Closed + | .quot _ _ type => type.Closed + | .induct _ _ _ type ctors _ => + type.Closed ∧ ∀ ctor ∈ ctors, ctor.Closed + | .recursor _ _ _ _ _ type rules _ _ => + type.Closed ∧ ∀ rule ∈ rules, rule.Closed + +/-- Number of constructors contributed to a block's flattened rule order. -/ +def ctorCount : Const β → Nat + | .induct _ _ _ _ ctors _ => ctors.length + | _ => 0 + +def kind : Const β → ConstKind + | .axiom .. => .axiom + | .defn _ kind .. => .defn kind + | .quot kind .. => .quot kind + | .induct .. => .induct + | .recursor .. => .recursor + +def uvars : Const β → Nat + | .axiom uvars .. | .defn uvars .. | .induct uvars .. | + .recursor uvars .. => uvars + | .quot _ uvars _ => uvars + +def type : Const β → VExpr β + | .axiom _ type _ | .defn _ _ type _ _ | .quot _ _ type | + .induct _ _ _ type _ _ | .recursor _ _ _ _ _ type _ _ _ => type + +theorem Closed.type_closed {constant : Const β} + (closed : constant.Closed) : constant.type.Closed := by + cases constant <;> simp_all [Closed, type] + +/-- Every external reference occurring in a member's trusted expressions. -/ +def refs : Const β → List (ConstRef β) + | .axiom _ type _ => type.refs + | .defn _ _ type value _ => type.refs ++ value.refs + | .quot _ _ type => type.refs + | .induct _ _ _ type ctors _ => type.refs ++ ctors.flatMap Ctor.refs + | .recursor _ _ _ _ _ type rules _ _ => type.refs ++ rules.flatMap RecRule.refs + +end Const + +namespace Block + +/-- Every constant stored in a block is syntactically closed. -/ +def Closed (block : Block β) : Prop := + ∀ constant ∈ block.members, constant.Closed + +theorem Closed.member {block : Block β} (closed : block.Closed) + {index : Nat} {constant : Const β} + (found : block.members[index]? = some constant) : constant.Closed := + closed constant (List.mem_iff_getElem?.2 ⟨index, found⟩) + +/-- References from all member types, values, constructor types, and recursor rules. -/ +def refs (block : Block β) : List (ConstRef β) := + block.members.flatMap Const.refs + +end Block + +end Ix.Theory diff --git a/Ix/Theory/Expr.lean b/Ix/Theory/Expr.lean new file mode 100644 index 000000000..cfa7f5ba6 --- /dev/null +++ b/Ix/Theory/Expr.lean @@ -0,0 +1,284 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Ref +import Ix.Theory.VLevel + +/-! +# Anonymous kernel expressions + +`VExpr β` is the binder-name-free term syntax used by the Theory. Constants +refer to members of content-addressed blocks through `ConstRef β`; projections +and natural-number literals remain structural instead of being expanded by a +front-end translation. +-/ + +namespace Ix.Theory + +inductive VExpr (β : Type u) where + | bvar (deBruijnIndex : Nat) + | sort (u : VLevel) + | const (ref : ConstRef β) (levels : List VLevel) + | app (fn arg : VExpr β) + | lam (binderType body : VExpr β) + | forallE (binderType body : VExpr β) + | proj (ref : ConstRef β) (index : Nat) (expr : VExpr β) + | natLit (value : Nat) +deriving DecidableEq, Hashable + +instance : Inhabited (VExpr β) := ⟨.sort .zero⟩ + +/-- Shift a de Bruijn index by `n` when it is at or above cutoff `k`. -/ +def liftVar (n i : Nat) (k := 0) : Nat := if i < k then i else n + i + +namespace VExpr + +/-- Iterated application, with arguments ordered from left to right. -/ +def appN (f : VExpr β) : List (VExpr β) → VExpr β + | [] => f + | a :: as => (f.app a).appN as + +variable (n : Nat) in +/-- Insert `n` variables at cutoff `k`. -/ +def liftN : VExpr β → (k : _ := 0) → VExpr β + | .bvar i, k => .bvar (liftVar n i k) + | .sort u, _ => .sort u + | .const r us, _ => .const r us + | .app fn arg, k => .app (fn.liftN k) (arg.liftN k) + | .lam ty body, k => .lam (ty.liftN k) (body.liftN (k + 1)) + | .forallE ty body, k => .forallE (ty.liftN k) (body.liftN (k + 1)) + | .proj r i e, k => .proj r i (e.liftN k) + | .natLit value, _ => .natLit value + +abbrev lift (e : VExpr β) : VExpr β := liftN 1 e + +/-- Every free de Bruijn index in the expression is below `k`. -/ +def ClosedN : VExpr β → (k : _ := 0) → Prop + | .bvar i, k => i < k + | .sort .., _ | .const .., _ | .natLit .., _ => True + | .app fn arg, k => fn.ClosedN k ∧ arg.ClosedN k + | .lam ty body, k => ty.ClosedN k ∧ body.ClosedN (k + 1) + | .forallE ty body, k => ty.ClosedN k ∧ body.ClosedN (k + 1) + | .proj _ _ e, k => e.ClosedN k + +abbrev Closed (e : VExpr β) : Prop := ClosedN e + +variable (levels : List VLevel) in +/-- Instantiate the universe parameters in an expression. -/ +def instL : VExpr β → VExpr β + | .bvar i => .bvar i + | .sort u => .sort (u.inst levels) + | .const r us => .const r (us.map (VLevel.inst levels)) + | .app fn arg => .app fn.instL arg.instL + | .lam ty body => .lam ty.instL body.instL + | .forallE ty body => .forallE ty.instL body.instL + | .proj r i e => .proj r i e.instL + | .natLit value => .natLit value + +/-- Replace interpreted constant occurrences by closed values, instantiated +at the occurrence's universe arguments. Projection annotations remain store +references; only expression-position constants are interpreted. -/ +def substConst (interp : ConstRef β → Option (VExpr β)) : + VExpr β → VExpr β + | .bvar index => .bvar index + | .sort level => .sort level + | .const ref levels => + match interp ref with + | some value => value.instL levels + | none => .const ref levels + | .app function argument => + .app (function.substConst interp) (argument.substConst interp) + | .lam domain body => + .lam (domain.substConst interp) (body.substConst interp) + | .forallE domain body => + .forallE (domain.substConst interp) (body.substConst interp) + | .proj ref field expression => + .proj ref field (expression.substConst interp) + | .natLit value => .natLit value + +/-- All universe parameters in an expression are below `U`. -/ +def LevelWF (U : Nat) : VExpr β → Prop + | .bvar _ | .natLit _ => True + | .sort l => l.WF U + | .const _ levels => ∀ l ∈ levels, l.WF U + | .app e₁ e₂ | .lam e₁ e₂ | .forallE e₁ e₂ => e₁.LevelWF U ∧ e₂.LevelWF U + | .proj _ _ e => e.LevelWF U + +/-- Instantiate de Bruijn index `k` in a single variable occurrence. -/ +def instVar (i : Nat) (e : VExpr β) (k := 0) : VExpr β := + if i < k then .bvar i else if i = k then liftN k e else .bvar (i - 1) + +/-- Instantiate de Bruijn index `k` throughout an expression. -/ +def inst : VExpr β → VExpr β → (k : _ := 0) → VExpr β + | .bvar i, e, k => instVar i e k + | .sort u, _, _ => .sort u + | .const r us, _, _ => .const r us + | .app fn arg, e, k => .app (fn.inst e k) (arg.inst e k) + | .lam ty body, e, k => .lam (ty.inst e k) (body.inst e (k + 1)) + | .forallE ty body, e, k => .forallE (ty.inst e k) (body.inst e (k + 1)) + | .proj r i p, e, k => .proj r i (p.inst e k) + | .natLit value, _, _ => .natLit value + +/-- Remove `n` variables at cutoff `k`, using `default` for missing terms. -/ +def unliftN (e : VExpr β) (n k : Nat) : VExpr β := + match n with + | 0 => e + | n + 1 => unliftN (e.inst default k) n k + +/-- Structural form of `Skips`. -/ +def Skips' (n : Nat) : VExpr β → (k : _ := 0) → Prop + | .bvar i, k => i < k + n → i < k + | .sort .., _ | .const .., _ | .natLit .., _ => True + | .app fn arg, k => fn.Skips' n k ∧ arg.Skips' n k + | .lam ty body, k => ty.Skips' n k ∧ body.Skips' n (k + 1) + | .forallE ty body, k => ty.Skips' n k ∧ body.Skips' n (k + 1) + | .proj _ _ e, k => e.Skips' n k + +/-- `[bvar (off+m-1), ..., bvar off]`. -/ +def bvarRevRange (off : Nat) : Nat → List (VExpr β) + | 0 => [] + | m + 1 => .bvar (off + m) :: bvarRevRange off m + +/-- Iterated lambda; the binder list is outermost first. -/ +def lamN : List (VExpr β) → VExpr β → VExpr β + | [], e => e + | A :: As, e => .lam A (lamN As e) + +/-- Iterated pi; the binder list is outermost first. -/ +def forallN : List (VExpr β) → VExpr β → VExpr β + | [], e => e + | A :: As, e => .forallE A (forallN As e) + +/-- Insert `n` binders below a telescope at depth `k`. -/ +def liftTelN (n : Nat) : List (VExpr β) → Nat → List (VExpr β) + | [], _ => [] + | A :: As, k => A.liftN n k :: liftTelN n As (k + 1) + +/-- The first `n` binder types of an iterated pi, outermost first. -/ +def telN : Nat → VExpr β → List (VExpr β) + | 0, _ => [] + | n + 1, .forallE A rest => A :: telN n rest + | _ + 1, _ => [] + +/-- Strip up to `n` binders from an iterated pi. -/ +def dropN : Nat → VExpr β → VExpr β + | 0, e => e + | n + 1, .forallE _ rest => dropN n rest + | _ + 1, e => e + +/-- The result after all leading pi binders. -/ +def resultOf : VExpr β → VExpr β + | .forallE _ rest => resultOf rest + | e => e + +/-- Collect application arguments, outermost first. -/ +def appArgs : VExpr β → List (VExpr β) → List (VExpr β) + | .app f a, acc => appArgs f (a :: acc) + | _, acc => acc + +/-- Remove every application node and return its head. -/ +def appHead : VExpr β → VExpr β + | .app f _ => appHead f + | e => e + +/-- Instantiate a term under each entry of a telescope. -/ +def instTelN (a : VExpr β) : List (VExpr β) → Nat → List (VExpr β) + | [], _ => [] + | A :: As, k => A.inst a k :: instTelN a As (k + 1) + +/-- Consume an outermost-first argument list from a body. -/ +def instRev : VExpr β → List (VExpr β) → VExpr β + | C, [] => C + | C, e :: es => instRev (C.inst e es.length) es + +/-- Consume an outermost-first argument list above a fixed binder offset. -/ +def instRevAt : VExpr β → List (VExpr β) → Nat → VExpr β + | e, [], _ => e + | e, a :: as, k => instRevAt (e.inst a (k + as.length)) as k + +/-- Whether an expression mentions a particular constant reference. -/ +def mentions [DecidableEq β] : VExpr β → ConstRef β → Bool + | .bvar _, _ | .sort _, _ | .natLit _, _ => false + | .const r _, target => decide (r = target) + | .app e₁ e₂, target | .lam e₁ e₂, target | .forallE e₁ e₂, target => + e₁.mentions target || e₂.mentions target + | .proj r _ e, target => decide (r = target) || e.mentions target + +/-- All constant references occurring in an expression, including projection heads. -/ +def refs : VExpr β → List (ConstRef β) + | .bvar _ | .sort _ | .natLit _ => [] + | .const r _ => [r] + | .app e₁ e₂ | .lam e₁ e₂ | .forallE e₁ e₂ => e₁.refs ++ e₂.refs + | .proj r _ e => r :: e.refs + +end VExpr + +/-- A context embedding represented by skips and retained binders. -/ +inductive Lift : Type where + | refl + | skip (tail : Lift) + | cons (tail : Lift) + +namespace Lift + +@[simp] def skipN (l : Lift) : Nat → Lift + | 0 => l + | n + 1 => .skip (skipN l n) + +@[simp] def consN (l : Lift) : Nat → Lift + | 0 => l + | n + 1 => .cons (consN l n) + +@[simp] def comp (l₁ l₂ : Lift) : Lift := + match l₂, l₁ with + | .refl, l₁ => l₁ + | .skip l₂, l₁ => .skip (l₁.comp l₂) + | .cons l₂, .refl => .cons l₂ + | .cons l₂, .skip l₁ => .skip (l₁.comp l₂) + | .cons l₂, .cons l₁ => .cons (l₁.comp l₂) + +@[simp] def dom : Lift → Nat + | .refl => 0 + | .skip l => l.dom + | .cons l => l.dom + 1 + +@[simp] def size : Lift → Nat + | .refl => 0 + | .skip l | .cons l => l.size + 1 + +@[simp] def depth : Lift → Nat + | .refl => 0 + | .skip l => l.depth + 1 + | .cons l => l.depth + +@[simp] protected def liftVar : Lift → Nat → Nat + | .refl, n => n + | .skip l, n => l.liftVar n + 1 + | .cons _, 0 => 0 + | .cons l, n + 1 => l.liftVar n + 1 + +@[simp] def diff : Lift → Lift → Lift + | .refl, _ => .refl + | l, .refl => l + | .skip l₁, .skip l₂ | .cons l₁, .skip l₂ => diff l₁ l₂ + | .skip l₁, .cons l₂ => .skip (diff l₁ l₂) + | .cons l₁, .cons l₂ => .cons (l₁.diff l₂) + +end Lift + +namespace VExpr + +@[simp] def lift' : VExpr β → Lift → VExpr β + | .bvar i, ρ => .bvar (ρ.liftVar i) + | .sort u, _ => .sort u + | .const r us, _ => .const r us + | .app fn arg, ρ => .app (fn.lift' ρ) (arg.lift' ρ) + | .lam ty body, ρ => .lam (ty.lift' ρ) (body.lift' ρ.cons) + | .forallE ty body, ρ => .forallE (ty.lift' ρ) (body.lift' ρ.cons) + | .proj r i e, ρ => .proj r i (e.lift' ρ) + | .natLit value, _ => .natLit value + +end VExpr +end Ix.Theory diff --git a/Ix/Theory/Inductive/Levels.lean b/Ix/Theory/Inductive/Levels.lean new file mode 100644 index 000000000..5b54f8a4a --- /dev/null +++ b/Ix/Theory/Inductive/Levels.lean @@ -0,0 +1,124 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.VLevel + +/-! +The universe layout shared by recursor generation and its computation rules. +Large elimination reserves the first recursor universe for the motive; +constructors and source families receive only the remaining universes. +-/ + +namespace Ix.Theory + +namespace VLevel + +/-- `count` consecutive universe parameters beginning at `offset`. -/ +def params' (count offset : Nat) : List VLevel := + (List.range count).map fun index => .param (index + offset) + +@[simp] theorem params'_length : (params' count offset).length = count := by + simp [params'] + +end VLevel + +namespace Inductive + +/-- Whether a recursor may eliminate into a fresh universe or only into Prop. -/ +inductive ElimMode where + | large + | small + deriving DecidableEq, Repr + +namespace ElimMode + +def ofBool : Bool → ElimMode + | false => .small + | true => .large + +/-- Universe-slot offset occupied by the large-elimination motive. -/ +def offset : ElimMode → Nat + | .large => 1 + | .small => 0 + +def recUvars (mode : ElimMode) (sourceUvars : Nat) : Nat := + sourceUvars + mode.offset + +def motiveLevel : ElimMode → VLevel + | .large => .param 0 + | .small => .zero + +def sourceLevels (mode : ElimMode) (sourceUvars : Nat) : List VLevel := + VLevel.params' sourceUvars mode.offset + +def recLevels (mode : ElimMode) (sourceUvars : Nat) : List VLevel := + VLevel.params (mode.recUvars sourceUvars) + +/-- Actual arguments of the source family/constructor at a recursor instance. +The recursor and its rule RHS retain the complete argument list. -/ +def sourceArgs (mode : ElimMode) (levels : List α) : List α := + levels.drop mode.offset + +@[simp] theorem large_offset : ElimMode.large.offset = 1 := rfl +@[simp] theorem small_offset : ElimMode.small.offset = 0 := rfl + +@[simp] theorem large_recUvars (sourceUvars : Nat) : + ElimMode.large.recUvars sourceUvars = sourceUvars + 1 := rfl + +@[simp] theorem small_recUvars (sourceUvars : Nat) : + ElimMode.small.recUvars sourceUvars = sourceUvars := by + simp [recUvars, offset] + +@[simp] theorem small_sourceArgs (levels : List α) : + ElimMode.small.sourceArgs levels = levels := rfl + +@[simp] theorem large_sourceArgs_cons (motive : α) (levels : List α) : + ElimMode.large.sourceArgs (motive :: levels) = levels := rfl + +@[simp] theorem map_sourceArgs (mode : ElimMode) (f : α → β) (levels : List α) : + (mode.sourceArgs levels).map f = mode.sourceArgs (levels.map f) := by + simp [sourceArgs] + +theorem sourceArgs_length (mode : ElimMode) {levels : List α} {sourceUvars : Nat} + (h : levels.length = mode.recUvars sourceUvars) : + (mode.sourceArgs levels).length = sourceUvars := by + simp [sourceArgs, h, recUvars] + +theorem sourceArgs_wf (mode : ElimMode) {levels : List VLevel} + (h : ∀ level ∈ levels, level.WF U) : + ∀ level ∈ mode.sourceArgs levels, level.WF U := by + intro level hl + exact h level (List.mem_of_mem_drop hl) + +theorem sourceLevels_wf (mode : ElimMode) (sourceUvars : Nat) : + ∀ level ∈ mode.sourceLevels sourceUvars, level.WF (mode.recUvars sourceUvars) := by + simp [sourceLevels, VLevel.params', VLevel.WF, recUvars] + +/-- Generation's symbolic source map selects exactly the source arguments +of a concrete recursor instance. This also covers equal-arity small elimination. -/ +theorem sourceLevels_inst (mode : ElimMode) {levels : List VLevel} {sourceUvars : Nat} + (h : levels.length = mode.recUvars sourceUvars) : + (mode.sourceLevels sourceUvars).map (VLevel.inst levels) = mode.sourceArgs levels := by + apply List.ext_get (by simp [sourceLevels, sourceArgs_length mode h]) + intro i hi _ + have hi' : i < sourceUvars := by simpa [sourceLevels] using hi + have bound : i + mode.offset < levels.length := by + rw [h, recUvars] + omega + simp [sourceLevels, VLevel.params', VLevel.inst, sourceArgs, + List.getD_eq_getElem?_getD, List.getElem?_eq_getElem bound, Nat.add_comm] + +theorem sourceArgs_recLevels (mode : ElimMode) (sourceUvars : Nat) : + mode.sourceArgs (mode.recLevels sourceUvars) = mode.sourceLevels sourceUvars := by + have h := sourceLevels_inst mode (sourceUvars := sourceUvars) + (levels := mode.recLevels sourceUvars) + (by simp [recLevels]) + rw [← h] + exact (List.map_congr_left (g := id) fun level hl => + VLevel.inst_id (mode.sourceLevels_wf sourceUvars level hl)).trans (List.map_id _) + +end ElimMode +end Inductive +end Ix.Theory diff --git a/Ix/Theory/LICENSE b/Ix/Theory/LICENSE new file mode 100644 index 000000000..c13342b95 --- /dev/null +++ b/Ix/Theory/LICENSE @@ -0,0 +1,234 @@ +Lean4Ix licensing +================= + +Copyright (c) 2026 Argument Computer Corporation + +This file is derived from lean4lean and has been modified by Argument Computer Corporation. +The Apache License, Version 2.0 text reproduced below is unchanged. + +Except for third-party or upstream material that is separately identified, +Lean4Ix is licensed, at your option, under either: + + * the MIT License, as published in LICENSE-MIT; or + * the Apache License, Version 2.0, published in LICENSE-APACHE and + reproduced below. + +SPDX-License-Identifier: MIT OR Apache-2.0 + +This grant covers material copyrighted by Argument Computer Corporation and +any upstream material that its applicable license permits Argument Computer +Corporation to sublicense under these terms. Work derived from lean4lean or +other upstream projects may retain its original licensing, copyright, +attribution, notice, and modification requirements. + +To the maximum extent permitted by those upstream licenses, our intent is to +incorporate, distribute, and, where permitted, sublicense that work as part of +the unified Lean4Ix package under the MIT OR Apache-2.0 licensing choice. We +preserve all notices required by the applicable upstream licenses and comply +with every condition necessary to incorporate and redistribute that work. +Where an upstream license does not permit this unified dual licensing, its +terms continue to control the +affected material. Nothing in this notice grants rights beyond those provided +by the applicable upstream licenses. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Ix/Theory/LICENSE-APACHE b/Ix/Theory/LICENSE-APACHE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/Ix/Theory/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Ix/Theory/LICENSE-MIT b/Ix/Theory/LICENSE-MIT new file mode 100644 index 000000000..7590c7281 --- /dev/null +++ b/Ix/Theory/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Argument Computer Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Ix/Theory/Model/Annotated.lean b/Ix/Theory/Model/Annotated.lean new file mode 100644 index 000000000..cf7bc21af --- /dev/null +++ b/Ix/Theory/Model/Annotated.lean @@ -0,0 +1,221 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Level +import Ix.Theory.Rename + +/-! +# Annotated readings of exact anonymous expressions + +Every binder occurrence carries its own zero condition. These are structural +readings, not evidence of typing: the semantic checker must establish that +each condition describes the inferred codomain sort in the current context. + +The operations below preserve erasure exactly, including block/member and +constructor positions, level arguments, projection heads, and literals. +-/ + +namespace Ix.Theory.Model + +open Certified + +inductive AExpr (β : Type u) where + | bvar (index : Nat) + | sort (level : VLevel) + | const (ref : ConstRef β) (levels : List VLevel) + | app (fn arg : AExpr β) + | lam (condition : PropWhen) (domain body : AExpr β) + | forallE (condition : PropWhen) (domain body : AExpr β) + | proj (ref : ConstRef β) (field : Nat) (major : AExpr β) + | natLit (value : Nat) +deriving DecidableEq + +namespace AExpr + +def erase : AExpr β → VExpr β + | .bvar i => .bvar i + | .sort l => .sort l + | .const r ls => .const r ls + | .app f a => .app f.erase a.erase + | .lam _ a b => .lam a.erase b.erase + | .forallE _ a b => .forallE a.erase b.erase + | .proj r i e => .proj r i e.erase + | .natLit v => .natLit v + +theorem eq_const_of_erase_eq {e : AExpr β} {r : ConstRef β} {ls : List VLevel} + (h : e.erase = .const r ls) : e = .const r ls := by + cases e <;> simp_all [erase] + +/-- Scope of syntax and annotations, independently of semantic validity. -/ +def Scope (universes depth : Nat) : AExpr β → Prop + | .bvar i => i < depth + | .sort l => l.WF universes + | .const _ ls => ∀ l ∈ ls, l.WF universes + | .app f a => f.Scope universes depth ∧ a.Scope universes depth + | .lam p a b | .forallE p a b => + p.WF universes ∧ a.Scope universes depth ∧ b.Scope universes (depth + 1) + | .proj _ _ e => e.Scope universes depth + | .natLit _ => True + +instance decidableScope {n k : Nat} : ∀ {e : AExpr β}, Decidable (e.Scope n k) + | .bvar i => inferInstanceAs (Decidable (i < k)) + | .sort l => VLevel.decidable_WF (l := l) + | .const _ ls => inferInstanceAs (Decidable (∀ l ∈ ls, VLevel.WF n l)) + | .app f a => @instDecidableAnd _ _ (decidableScope (e := f)) (decidableScope (e := a)) + | .lam p A b | .forallE p A b => + @instDecidableAnd _ _ (inferInstanceAs (Decidable (p.WF n))) + (@instDecidableAnd _ _ (decidableScope (e := A)) (decidableScope (e := b))) + | .proj _ _ e => decidableScope (e := e) + | .natLit _ => instDecidableTrue + +theorem Scope.erase {u k : Nat} {e : AExpr β} (h : e.Scope u k) : + e.erase.LevelWF u ∧ e.erase.ClosedN k := by + induction e generalizing k with + | bvar i => exact ⟨trivial, h⟩ + | sort l => exact ⟨h, trivial⟩ + | const r ls => exact ⟨h, trivial⟩ + | app f a hf ha => + exact ⟨⟨(hf h.1).1, (ha h.2).1⟩, ⟨(hf h.1).2, (ha h.2).2⟩⟩ + | lam p a b ha hb | forallE p a b ha hb => + exact ⟨⟨(ha h.2.1).1, (hb h.2.2).1⟩, ⟨(ha h.2.1).2, (hb h.2.2).2⟩⟩ + | proj r i e ih => exact ih h + | natLit v => exact ⟨trivial, trivial⟩ + +def liftN (count : Nat) : AExpr β → (cutoff : Nat := 0) → AExpr β + | .bvar i, k => .bvar (liftVar count i k) + | .sort l, _ => .sort l + | .const r ls, _ => .const r ls + | .app f a, k => .app (f.liftN count k) (a.liftN count k) + | .lam p a b, k => .lam p (a.liftN count k) (b.liftN count (k + 1)) + | .forallE p a b, k => .forallE p (a.liftN count k) (b.liftN count (k + 1)) + | .proj r i e, k => .proj r i (e.liftN count k) + | .natLit v, _ => .natLit v + +@[simp] theorem erase_liftN (e : AExpr β) (n k : Nat) : + (e.liftN n k).erase = e.erase.liftN n k := by + induction e generalizing k <;> simp_all [liftN, erase, VExpr.liftN] + +def instVar (i : Nat) (a : AExpr β) (k : Nat) : AExpr β := + if i < k then .bvar i else if i = k then a.liftN k else .bvar (i - 1) + +def inst : AExpr β → AExpr β → (cutoff : Nat := 0) → AExpr β + | .bvar i, a, k => instVar i a k + | .sort l, _, _ => .sort l + | .const r ls, _, _ => .const r ls + | .app f a, e, k => .app (f.inst e k) (a.inst e k) + | .lam p a b, e, k => .lam p (a.inst e k) (b.inst e (k + 1)) + | .forallE p a b, e, k => .forallE p (a.inst e k) (b.inst e (k + 1)) + | .proj r i e, a, k => .proj r i (e.inst a k) + | .natLit v, _, _ => .natLit v + +@[simp] theorem erase_inst (e a : AExpr β) (k : Nat) : + (e.inst a k).erase = e.erase.inst a.erase k := by + induction e generalizing k with + | bvar i => + by_cases hi : i < k + · simp [inst, erase, VExpr.inst, instVar, VExpr.instVar, hi] + · by_cases he : i = k <;> + simp [inst, erase, VExpr.inst, instVar, VExpr.instVar, hi, he] + | _ => simp_all [inst, erase, VExpr.inst] + +def instL (levels : List VLevel) : AExpr β → AExpr β + | .bvar i => .bvar i + | .sort l => .sort (l.inst levels) + | .const r ls => .const r (ls.map (VLevel.inst levels)) + | .app f a => .app (f.instL levels) (a.instL levels) + | .lam p a b => .lam (instCondition levels p) (a.instL levels) (b.instL levels) + | .forallE p a b => .forallE (instCondition levels p) (a.instL levels) (b.instL levels) + | .proj r i e => .proj r i (e.instL levels) + | .natLit v => .natLit v + +@[simp] theorem erase_instL (e : AExpr β) (ls : List VLevel) : + (e.instL ls).erase = e.erase.instL ls := by + induction e <;> simp_all [instL, erase, VExpr.instL] + +theorem instL_instL (e : AExpr β) (ls ls' : List VLevel) : + (e.instL ls).instL ls' = e.instL (ls.map (VLevel.inst ls')) := by + induction e <;> + simp_all [instL, VLevel.inst_inst, instCondition_comp, List.map_map, Function.comp_def] + +def rename (mapping : β → γ) : AExpr β → AExpr γ + | .bvar i => .bvar i + | .sort l => .sort l + | .const r ls => .const (r.rename mapping) ls + | .app f a => .app (f.rename mapping) (a.rename mapping) + | .lam p a b => .lam p (a.rename mapping) (b.rename mapping) + | .forallE p a b => .forallE p (a.rename mapping) (b.rename mapping) + | .proj r i e => .proj (r.rename mapping) i (e.rename mapping) + | .natLit v => .natLit v + +@[simp] theorem erase_rename (e : AExpr β) (mapping : β → γ) : + (e.rename mapping).erase = e.erase.rename mapping := by + induction e <;> simp_all [rename, erase, VExpr.rename] + +end AExpr + +/-- Untrusted annotations are indexed by occurrence, not a shared node ID. +Leaf syntax comes from the source expression and cannot be changed here. -/ +inductive AnnotationTree where + | leaf + | app (fn arg : AnnotationTree) + | lam (condition : Option (List Nat)) (domain body : AnnotationTree) + | forallE (condition : Option (List Nat)) (domain body : AnnotationTree) + | proj (major : AnnotationTree) +deriving DecidableEq, Repr + +private def readCondition? (n : Nat) (raw : Option (List Nat)) : + Option { p : PropWhen // p.toRaw = raw ∧ p.WF n } := + match h : PropWhen.fromRaw? n raw with + | none => none + | some p => some ⟨p, PropWhen.fromRaw?_sound h⟩ + +universe u +variable {β : Type u} + +/-- Exact, scoped reading produced by the structural validator. -/ +abbrev Reading (n k : Nat) (source : VExpr β) := + { e : AExpr β // e.erase = source ∧ e.Scope n k } + +/-- Check shape, canonical conditions, and every term/universe index against +the exact input occurrence. Typing must subsequently validate binder meaning. +Proof fields are erased at runtime; every data check below is executable. -/ +def readAnnotations? (n k : Nat) (source : VExpr β) (tree : AnnotationTree) : + Option (Reading n k source) := + match source, tree with + | .bvar i, .leaf => if h : i < k then some ⟨.bvar i, rfl, h⟩ else none + | .sort l, .leaf => if h : l.WF n then some ⟨.sort l, rfl, h⟩ else none + | .const r ls, .leaf => + if h : ∀ l ∈ ls, l.WF n then some ⟨.const r ls, rfl, h⟩ else none + | .app f a, .app tf ta => do + let f' ← readAnnotations? n k f tf + let a' ← readAnnotations? n k a ta + return ⟨.app f'.val a'.val, + by simp [AExpr.erase, f'.property.1, a'.property.1], + f'.property.2, a'.property.2⟩ + | .lam a b, .lam raw ta tb => + match readCondition? n raw with + | none => none + | some p => do + let a' ← readAnnotations? n k a ta + let b' ← readAnnotations? n (k + 1) b tb + return ⟨.lam p.val a'.val b'.val, + by simp [AExpr.erase, a'.property.1, b'.property.1], + p.property.2, a'.property.2, b'.property.2⟩ + | .forallE a b, .forallE raw ta tb => + match readCondition? n raw with + | none => none + | some p => do + let a' ← readAnnotations? n k a ta + let b' ← readAnnotations? n (k + 1) b tb + return ⟨.forallE p.val a'.val b'.val, + by simp [AExpr.erase, a'.property.1, b'.property.1], + p.property.2, a'.property.2, b'.property.2⟩ + | .proj r i e, .proj te => do + let e' ← readAnnotations? n k e te + return ⟨.proj r i e'.val, by simp [AExpr.erase, e'.property.1], e'.property.2⟩ + | .natLit v, .leaf => some ⟨.natLit v, rfl, trivial⟩ + | _, _ => none + +end Ix.Theory.Model diff --git a/Ix/Theory/Model/Context.lean b/Ix/Theory/Model/Context.lean new file mode 100644 index 000000000..f4579d895 --- /dev/null +++ b/Ix/Theory/Model/Context.lean @@ -0,0 +1,56 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.WellDenoted + +namespace Ix.Theory.Model + +open SetTheory + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +/-- All entries are expressed in the full current context. Pushing a binder +lifts both its type and every existing entry. -/ +abbrev Context (β : Type u) := List (AExpr β) + +def Context.push (A : AExpr β) (Γ : Context β) : Context β := + A.liftN 1 :: Γ.map (AExpr.liftN 1 ·) + +def Context.Valid (constants : Assignment β V) (levels : List Nat) + (Γ : Context β) (env : Nat → V) : Prop := + ∀ i A, Γ[i]? = some A → + WellDenoted constants levels env A ∧ env i ∈ˢ interp constants levels env A + +theorem Context.valid_nil (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) : Context.Valid constants levels [] env := by + intro i A h + simp at h + +omit [SetTheory V] in +@[simp] theorem Valuation.skip_one_cons (x : V) (env : Nat → V) : + Valuation.skip 1 0 (Valuation.cons x env) = env := by + funext i + simp [Valuation.skip, Nat.add_comm 1 i] + +theorem Context.Valid.push {constants : Assignment β V} {levels : List Nat} + {Γ : Context β} {env : Nat → V} {A : AExpr β} {x : V} + (hΓ : Γ.Valid constants levels env) (hA : WellDenoted constants levels env A) + (hx : x ∈ˢ interp constants levels env A) : + (Γ.push A).Valid constants levels (Valuation.cons x env) := by + intro i B hi + cases i with + | zero => + simp only [Context.push, List.getElem?_cons_zero, Option.some.injEq] at hi + subst B + simpa only [wellDenoted_liftN, interp_liftN, Valuation.skip_one_cons, + Valuation.cons_zero] using And.intro hA hx + | succ i => + simp only [Context.push, List.getElem?_cons_succ, List.getElem?_map] at hi + obtain ⟨C, hC, rfl⟩ := Option.map_eq_some_iff.mp hi + simpa only [wellDenoted_liftN, interp_liftN, Valuation.skip_one_cons, + Valuation.cons_succ] using hΓ i C hC + +end Ix.Theory.Model diff --git a/Ix/Theory/Model/Environment.lean b/Ix/Theory/Model/Environment.lean new file mode 100644 index 000000000..7bd16fbcd --- /dev/null +++ b/Ix/Theory/Model/Environment.lean @@ -0,0 +1,76 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Context + +namespace Ix.Theory.Model + +open SetTheory + +universe u v + +/-- A closed equation includes the whole rule telescope as lambdas. Its data +does not authorize reduction; an admitted model must establish its equality. -/ +structure ConstantEquation (β : Type u) where + lhs : AExpr β + rhs : AExpr β +deriving DecidableEq + +/-- Data describing additional proved behavior of an admitted declaration. +Only semantic producers may publish these facts; the certificate supplies +no proof of their meaning. -/ +inductive ConstantFact (β : Type u) where + | typed (expression type : AExpr β) + | natural (zero succ : ConstRef β) +deriving DecidableEq + +structure NaturalMeaning {β : Type u} {V : Type v} [SetTheory V] + (constants : Assignment β V) (family zero succ : ConstRef β) : Prop where + member : ∀ n, Numeral.value n ∈ˢ constants family [] + zeroValue : constants zero [] = Numeral.value 0 + succValue : ∀ n, SetTheory.app (constants succ []) (Numeral.value n) = Numeral.value (n + 1) + +def ConstantFact.Meaning {β : Type u} {V : Type v} [SetTheory V] + (constants : Assignment β V) (owner : ConstRef β) (levels : List Nat) (env : Nat → V) : + ConstantFact β → Prop + | .typed e type => WellDenoted constants levels env e ∧ WellDenoted constants levels env type ∧ + interp constants levels env e ∈ˢ interp constants levels env type + | .natural zero succ => levels = [] ∧ NaturalMeaning constants owner zero succ + +/-- An entry becomes available to the checker only after admission of its +primitive realization, safe checked body, or atomic inductive realization. +An absent body does not license axioms or reduction equations. -/ +structure ConstantEntry (β : Type u) where + universes : Nat + type : AExpr β + body : Option (AExpr β) + equations : List (ConstantEquation β) := [] + facts : List (ConstantFact β) := [] + +abbrev Environment (β : Type u) := ConstRef β → Option (ConstantEntry β) + +/-- Meaning of an exact dependency interface. A declaration producer must +establish these facts for every universe instance and context valuation. -/ +structure Realizes {β : Type u} {V : Type v} [SetTheory V] + (constants : Assignment β V) (entries : Environment β) : Prop where + typeValid : ∀ r entry, entries r = some entry → ∀ levels, + levels.length = entry.universes → ∀ env, + WellDenoted constants levels env entry.type + member : ∀ r entry, entries r = some entry → ∀ levels, + levels.length = entry.universes → ∀ env, + constants r levels ∈ˢ interp constants levels env entry.type + bodyValid : ∀ r entry, entries r = some entry → ∀ body, entry.body = some body → + ∀ levels, levels.length = entry.universes → ∀ env, + WellDenoted constants levels env body + bodyValue : ∀ r entry, entries r = some entry → ∀ body, entry.body = some body → + ∀ levels, levels.length = entry.universes → ∀ env, + constants r levels = interp constants levels env body + equationValue : ∀ r entry, entries r = some entry → ∀ equation ∈ entry.equations, + ∀ levels, levels.length = entry.universes → ∀ env, + interp constants levels env equation.lhs = interp constants levels env equation.rhs + factMeaning : ∀ r entry, entries r = some entry → ∀ fact ∈ entry.facts, + ∀ levels, levels.length = entry.universes → ∀ env, fact.Meaning constants r levels env + +end Ix.Theory.Model diff --git a/Ix/Theory/Model/Extension.lean b/Ix/Theory/Model/Extension.lean new file mode 100644 index 000000000..ca67eb91b --- /dev/null +++ b/Ix/Theory/Model/Extension.lean @@ -0,0 +1,346 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Support +import Ix.Theory.Model.Judgment + +namespace Ix.Theory.Model + +open SetTheory + +universe u v +variable {β : Type u} [DecidableEq β] + +def Environment.insert (entries : Environment β) (r : ConstRef β) + (entry : ConstantEntry β) : Environment β := + fun q => if q = r then some entry else entries q + +def Assignment.insert (constants : Assignment β V) (r : ConstRef β) + (value : List Nat → V) : Assignment β V := + fun q levels => if q = r then value levels else constants q levels + +/-- All exact old references retain their values at every universe instance. -/ +def Assignment.AgreesOn (entries : Environment β) (constants constants' : Assignment β V) : Prop := + ∀ r entry, entries r = some entry → ∀ levels, constants' r levels = constants r levels + +@[simp] theorem Environment.insert_same (entries : Environment β) (r : ConstRef β) + (entry : ConstantEntry β) : entries.insert r entry r = some entry := by + simp only [insert, ↓reduceIte] + +theorem Environment.insert_replace (entries : Environment β) (r : ConstRef β) + (old entry : ConstantEntry β) : (entries.insert r old).insert r entry = entries.insert r entry := by + funext q + by_cases h : q = r <;> simp [insert, h] + +@[simp] theorem Assignment.insert_same (constants : Assignment β V) (r : ConstRef β) + (value : List Nat → V) (levels : List Nat) : + constants.insert r value r levels = value levels := by + simp only [insert, ↓reduceIte] + +omit [DecidableEq β] in +theorem fresh_ne {entries : Environment β} {r q : ConstRef β} {entry : ConstantEntry β} + (fresh : entries r = none) (h : entries q = some entry) : q ≠ r := by + intro hqr + subst q + rw [fresh] at h + contradiction + +theorem Environment.insert_old {entries : Environment β} {r q : ConstRef β} + {entry old : ConstantEntry β} (fresh : entries r = none) (h : entries q = some old) : + entries.insert r entry q = some old := by + simp only [insert, if_neg (fresh_ne fresh h), h] + +theorem Assignment.insert_agrees {entries : Environment β} {r : ConstRef β} + (fresh : entries r = none) (constants : Assignment β V) (value : List Nat → V) : + Assignment.AgreesOn entries constants (constants.insert r value) := by + intro q entry h levels + simp only [Assignment.insert, if_neg (fresh_ne fresh h)] + +theorem AExpr.ReferencesIn.insert {entries : Environment β} {r : ConstRef β} + {entry : ConstantEntry β} {e : AExpr β} (h : e.ReferencesIn entries) : + e.ReferencesIn (entries.insert r entry) := by + intro q hq + unfold Environment.insert + split + · rfl + · exact h q hq + +theorem ConstantFact.ReferencesIn.insert {entries : Environment β} {r : ConstRef β} + {entry : ConstantEntry β} {fact : ConstantFact β} (h : fact.ReferencesIn entries) : + fact.ReferencesIn (entries.insert r entry) := by + intro q hq + unfold Environment.insert + split + · rfl + · exact h q hq + +theorem Environment.WF.insert {entries : Environment β} {r : ConstRef β} + {entry : ConstantEntry β} (hE : entries.WF) + (hTs : entry.type.Scope entry.universes 0) + (hBs : ∀ body, entry.body = some body → body.Scope entry.universes 0) + (hTr : entry.type.ReferencesIn entries) + (hBr : ∀ body, entry.body = some body → body.ReferencesIn entries) + (hQs : ∀ equation ∈ entry.equations, + equation.lhs.Scope entry.universes 0 ∧ equation.rhs.Scope entry.universes 0) + (hQr : ∀ equation ∈ entry.equations, + equation.lhs.ReferencesIn entries ∧ equation.rhs.ReferencesIn entries) + (hFs : ∀ fact ∈ entry.facts, fact.Scope entry.universes) + (hFr : ∀ fact ∈ entry.facts, fact.ReferencesIn entries) : + (entries.insert r entry).WF := by + constructor + · intro q old h + unfold Environment.insert at h + split at h + · cases Option.some.inj h; exact hTs + · exact hE.typeScope q old h + · intro q old h body hb + unfold Environment.insert at h + split at h + · cases Option.some.inj h; exact hBs body hb + · exact hE.bodyScope q old h body hb + · intro q old h + unfold Environment.insert at h + split at h + · cases Option.some.inj h; exact hTr.insert + · exact (hE.typeReferences q old h).insert + · intro q old h body hb + unfold Environment.insert at h + split at h + · cases Option.some.inj h; exact (hBr body hb).insert + · exact (hE.bodyReferences q old h body hb).insert + · intro q old h equation he + unfold Environment.insert at h + split at h + · cases Option.some.inj h; exact hQs equation he + · exact hE.equationScope q old h equation he + · intro q old h equation he + unfold Environment.insert at h + split at h + · cases Option.some.inj h + exact ⟨(hQr equation he).1.insert, (hQr equation he).2.insert⟩ + · exact ⟨(hE.equationReferences q old h equation he).1.insert, + (hE.equationReferences q old h equation he).2.insert⟩ + · intro q old h fact hf + unfold Environment.insert at h + split at h + · cases Option.some.inj h; exact hFs fact hf + · exact hE.factScope q old h fact hf + · intro q old h fact hf + unfold Environment.insert at h + split at h + · cases Option.some.inj h; exact (hFr fact hf).insert + · exact (hE.factReferences q old h fact hf).insert + +variable {V : Type v} [SetTheory V] + +omit [DecidableEq β] in +theorem Assignment.AgreesOn.interp {entries : Environment β} + {constants constants' : Assignment β V} + (h : Assignment.AgreesOn entries constants constants') + {e : AExpr β} (he : e.ReferencesIn entries) (levels : List Nat) (env : Nat → V) : + interp constants' levels env e = interp constants levels env e := by + apply interp_congr_constants + intro r hr ls + have hl := he r hr + cases hE : entries r with + | none => simp [hE] at hl + | some entry => exact h r entry hE ls + +omit [DecidableEq β] in +theorem Assignment.AgreesOn.wellDenoted {entries : Environment β} + {constants constants' : Assignment β V} + (h : Assignment.AgreesOn entries constants constants') + {e : AExpr β} (he : e.ReferencesIn entries) (levels : List Nat) (env : Nat → V) : + WellDenoted constants' levels env e ↔ WellDenoted constants levels env e := by + apply wellDenoted_congr_constants + intro r hr ls + have hl := he r hr + cases hE : entries r with + | none => simp [hE] at hl + | some entry => exact h r entry hE ls + +omit [DecidableEq β] in +theorem Assignment.AgreesOn.factMeaning {entries : Environment β} + {constants constants' : Assignment β V} (h : Assignment.AgreesOn entries constants constants') + {r : ConstRef β} {entry : ConstantEntry β} (hr : entries r = some entry) + {fact : ConstantFact β} (hf : fact.ReferencesIn entries) (levels : List Nat) (env : Nat → V) + (hm : fact.Meaning constants r levels env) : fact.Meaning constants' r levels env := by + have heq (q) (hq : q ∈ fact.references) (ls) : constants' q ls = constants q ls := by + have hv := hf q hq + cases he : entries q with + | none => simp [he] at hv + | some value => exact h q value he ls + cases fact with + | typed e type => + have her : e.ReferencesIn entries := fun q hq => hf q (List.mem_append_left _ hq) + have htr : type.ReferencesIn entries := fun q hq => hf q (List.mem_append_right _ hq) + simpa only [ConstantFact.Meaning, h.wellDenoted her, h.wellDenoted htr, + h.interp her, h.interp htr] using hm + | natural zero succ => + refine ⟨hm.1, ?_⟩ + have hz := heq zero (by simp [ConstantFact.references]) [] + have hs := heq succ (by simp [ConstantFact.references]) [] + constructor + · intro n; rw [h r entry hr []]; exact hm.2.member n + · rw [hz]; exact hm.2.zeroValue + · intro n; rw [hs]; exact hm.2.succValue n + +omit [DecidableEq β] in +theorem Realizes.of_agrees {entries : Environment β} {constants constants' : Assignment β V} + (hM : Realizes constants entries) (hE : entries.WF) + (h : Assignment.AgreesOn entries constants constants') : Realizes constants' entries where + typeValid r entry hr levels hn env := + (h.wellDenoted (hE.typeReferences r entry hr) levels env).mpr (hM.typeValid r entry hr levels hn env) + member r entry hr levels hn env := by + rw [h r entry hr levels, h.interp (hE.typeReferences r entry hr) levels env] + exact hM.member r entry hr levels hn env + bodyValid r entry hr body hb levels hn env := + (h.wellDenoted (hE.bodyReferences r entry hr body hb) levels env).mpr + (hM.bodyValid r entry hr body hb levels hn env) + bodyValue r entry hr body hb levels hn env := by + rw [h r entry hr levels, h.interp (hE.bodyReferences r entry hr body hb) levels env] + exact hM.bodyValue r entry hr body hb levels hn env + equationValue r entry hr law hl levels hn env := by + rw [h.interp (hE.equationReferences r entry hr law hl).1 levels env, + h.interp (hE.equationReferences r entry hr law hl).2 levels env] + exact hM.equationValue r entry hr law hl levels hn env + factMeaning r entry hr fact hf levels hn env := + h.factMeaning hr (hE.factReferences r entry hr fact hf) levels env + (hM.factMeaning r entry hr fact hf levels hn env) + +/-- Local mathematical realization package. Admission constructs every field; +certificate data cannot supply this proof package. -/ +structure EntryRealization (constants : Assignment β V) (r : ConstRef β) + (entry : ConstantEntry β) : Prop where + typeValid : ∀ levels, levels.length = entry.universes → ∀ env, + WellDenoted constants levels env entry.type + member : ∀ levels, levels.length = entry.universes → ∀ env, + constants r levels ∈ˢ interp constants levels env entry.type + bodyValid : ∀ body, entry.body = some body → ∀ levels, levels.length = entry.universes → ∀ env, + WellDenoted constants levels env body + bodyValue : ∀ body, entry.body = some body → ∀ levels, levels.length = entry.universes → ∀ env, + constants r levels = interp constants levels env body + equationValue : ∀ law ∈ entry.equations, ∀ levels, levels.length = entry.universes → ∀ env, + interp constants levels env law.lhs = interp constants levels env law.rhs + factMeaning : ∀ fact ∈ entry.facts, ∀ levels, levels.length = entry.universes → ∀ env, + fact.Meaning constants r levels env + +theorem Realizes.insert {entries : Environment β} {constants : Assignment β V} + {r : ConstRef β} {entry : ConstantEntry β} (hM : Realizes constants entries) + (h : EntryRealization constants r entry) : Realizes constants (entries.insert r entry) := by + constructor + · intro q old hq + unfold Environment.insert at hq + split at hq + · cases Option.some.inj hq; exact h.typeValid + · exact hM.typeValid q old hq + · intro q old hq + unfold Environment.insert at hq + split at hq + next he => subst q; cases Option.some.inj hq; exact h.member + next => exact hM.member q old hq + · intro q old hq + unfold Environment.insert at hq + split at hq + · cases Option.some.inj hq; exact h.bodyValid + · exact hM.bodyValid q old hq + · intro q old hq + unfold Environment.insert at hq + split at hq + next he => subst q; cases Option.some.inj hq; exact h.bodyValue + next => exact hM.bodyValue q old hq + · intro q old hq + unfold Environment.insert at hq + split at hq + · cases Option.some.inj hq; exact h.equationValue + · exact hM.equationValue q old hq + · intro q old hq + unfold Environment.insert at hq + split at hq + next he => subst q; cases Option.some.inj hq; exact h.factMeaning + next => exact hM.factMeaning q old hq + +/-- Safe-definition admission extends every compatible old model. The new +value is constructed from the checked body in that very model. -/ +theorem extend_definition {entries : Environment β} {r : ConstRef β} + {entry : ConstantEntry β} {body : AExpr β} {level : VLevel} + (hE : entries.WF) (fresh : entries r = none) (hb : entry.body = some body) + (hq : entry.equations = []) (hf : entry.facts = []) + (hBs : body.Scope entry.universes 0) + (hTr : entry.type.ReferencesIn entries) (hBr : body.ReferencesIn entries) + (hT : TypingClaim.{u,v} entries [] entry.type (.sort level)) + (hB : TypingClaim.{u,v} entries [] body entry.type) + (constants : Assignment β V) (hM : Realizes constants entries) : + ∃ constants' : Assignment β V, + Realizes constants' (entries.insert r entry) ∧ + Assignment.AgreesOn entries constants constants' := by + let constants' := constants.insert r (fun levels => interp constants levels (fun _ => empty) body) + have hagree : Assignment.AgreesOn entries constants constants' := by + intro q old h levels + simp only [constants', Assignment.insert, if_neg (fresh_ne fresh h)] + have hnew (levels : List Nat) (env : Nat → V) : + constants' r levels = interp constants levels env body := by + simp only [constants', Assignment.insert, ↓reduceIte] + exact interp_closed body constants levels hBs _ env + refine ⟨constants', ?_, hagree⟩ + constructor + · intro q old h levels _ env + unfold Environment.insert at h + split at h + · cases Option.some.inj h + exact (hagree.wellDenoted hTr levels env).mpr + (hT V constants hM levels env (Context.valid_nil constants levels env)).1 + · exact (hagree.wellDenoted (hE.typeReferences q old h) levels env).mpr + (hM.typeValid q old h levels ‹_› env) + · intro q old h levels _ env + unfold Environment.insert at h + split at h + next hqr => + subst q + cases Option.some.inj h + rw [hnew levels env, hagree.interp hTr levels env] + exact (hB V constants hM levels env (Context.valid_nil constants levels env)).2.2 + next => + rw [hagree q old h levels, hagree.interp (hE.typeReferences q old h) levels env] + exact hM.member q old h levels ‹_› env + · intro q old h b hb' levels _ env + unfold Environment.insert at h + split at h + · cases Option.some.inj h + have he : body = b := Option.some.inj (hb.symm.trans hb') + subst b + exact (hagree.wellDenoted hBr levels env).mpr + (hB V constants hM levels env (Context.valid_nil constants levels env)).1 + · exact (hagree.wellDenoted (hE.bodyReferences q old h b hb') levels env).mpr + (hM.bodyValid q old h b hb' levels ‹_› env) + · intro q old h b hb' levels _ env + unfold Environment.insert at h + split at h + next hqr => + subst q + cases Option.some.inj h + have he : body = b := Option.some.inj (hb.symm.trans hb') + subst b + rw [hnew levels env, hagree.interp hBr levels env] + next => + rw [hagree q old h levels, hagree.interp (hE.bodyReferences q old h b hb') levels env] + exact hM.bodyValue q old h b hb' levels ‹_› env + · intro q old h equation he levels hn env + unfold Environment.insert at h + split at h + · cases Option.some.inj h + simp only [hq, List.not_mem_nil] at he + · rw [hagree.interp (hE.equationReferences q old h equation he).1 levels env, + hagree.interp (hE.equationReferences q old h equation he).2 levels env] + exact hM.equationValue q old h equation he levels hn env + · intro q old h fact hfact levels hn env + unfold Environment.insert at h + split at h + · cases Option.some.inj h + simp only [hf, List.not_mem_nil] at hfact + · exact hagree.factMeaning h (hE.factReferences q old h fact hfact) levels env + (hM.factMeaning q old h fact hfact levels hn env) + +end Ix.Theory.Model diff --git a/Ix/Theory/Model/Inductive/Codes.lean b/Ix/Theory/Model/Inductive/Codes.lean new file mode 100644 index 000000000..0f2551dce --- /dev/null +++ b/Ix/Theory/Model/Inductive/Codes.lean @@ -0,0 +1,43 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Inductive.Telescope +import Ix.Theory.Model.SetModel.TaggedSum + +namespace Ix.Theory.Model.InductiveCodes + +open SetTheory SetTheory.Tower + +universe u +variable {V : Type u} [SetTheory V] + +open Classical in +noncomputable def natIndex (x : V) : Nat := + if h : ∃ i, x = vnat i then Classical.choose h else 0 + +theorem natIndex_vnat (i : Nat) : natIndex (vnat i : V) = i := by + unfold natIndex + rw [dif_pos ⟨i, rfl⟩] + exact (vnat_inj (Classical.choose_spec (⟨i, rfl⟩ : ∃ j, (vnat i : V) = vnat j))).symm + +noncomputable def tag (x : V) : Nat := natIndex (sfst x) + +@[simp] theorem tag_inj (i : Nat) (x : V) : tag (inj i x) = i := by + simp only [tag, sfst_inj, natIndex_vnat] + +theorem sum_graph_mem {w : Nat} (hw : w ≠ 0) {f : Nat → V} + (hf : ∀ i, f i ∈ˢ (univ w : V)) : sumSet 1 f ∈ˢ (univ w : V) := by + cases w with + | zero => exact (hw rfl).elim + | succ w => + unfold sumSet + rw [sigmaSet_pos (by decide : 1 ≠ 0)] + apply (univ_isTGUniverse hw).sigmaPairs_mem (omega_mem_univ_succ w) + intro k hk + obtain ⟨i, rfl⟩ := mem_omega_iff.mp hk + rw [natFibre_vnat] + exact hf i + +end Ix.Theory.Model.InductiveCodes diff --git a/Ix/Theory/Model/Inductive/Container.lean b/Ix/Theory/Model/Inductive/Container.lean new file mode 100644 index 000000000..0b8ad1103 --- /dev/null +++ b/Ix/Theory/Model/Inductive/Container.lean @@ -0,0 +1,192 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.SetModel.Container + +/-! +Indexed strictly positive carriers over the explicit set-theory interface. +This supplies a carrier and its induction principle from shape/position data; +the source validator must construct the bounds and target-index proofs. +No field postulates a fixed point, constructor law, or recursor law. +-/ + +namespace Ix.Theory.Model + +open SetTheory + +universe u + +structure IndexedContainer (V : Type u) where + indices : V + shapes : V → V + positions : V → V + target : V → V → V + +namespace IndexedContainer + +variable {V : Type u} [SetTheory V] + +structure WF (D : IndexedContainer V) (w : Nat) : Prop where + shapes_mem : w ≠ 0 → ∀ i, i ∈ˢ D.indices → D.shapes i ∈ˢ (univ w : V) + positions_mem : w ≠ 0 → ∀ i a, i ∈ˢ D.indices → a ∈ˢ D.shapes i → + D.positions a ∈ˢ (univ w : V) + target_mem : ∀ i a p, i ∈ˢ D.indices → a ∈ˢ D.shapes i → p ∈ˢ D.positions a → + D.target a p ∈ˢ D.indices + +noncomputable def node (w : Nat) (a g : V) : V := + if w = 0 then pt else spair a g + +noncomputable def fibre (D : IndexedContainer V) (w : Nat) (X i : V) : V := + sigmaSet w (D.shapes i) fun a => piSet (D.positions a) fun p => app X (D.target a p) + +noncomputable def step (D : IndexedContainer V) (w : Nat) : V := + graph (fun X => graph (D.fibre w X) D.indices) (famSpace w D.indices) + +noncomputable def carrier (D : IndexedContainer V) (w : Nat) : V := + lfpFamSet w D.indices (D.step w) + +theorem node_mem_fibre {D : IndexedContainer V} {w : Nat} {X i a g : V} + (ha : a ∈ˢ D.shapes i) + (hg : g ∈ˢ piSet (D.positions a) (fun p => app X (D.target a p))) : + node w a g ∈ˢ D.fibre w X i := by + by_cases hw : w = 0 + · subst w + exact pt_mem_sigma ha hg + · simpa only [node, if_neg hw, fibre] using spair_mem hw ha hg + +theorem mem_fibre {D : IndexedContainer V} {w : Nat} {X i x : V} : + x ∈ˢ D.fibre w X i ↔ ∃ a, a ∈ˢ D.shapes i ∧ + ∃ g, g ∈ˢ piSet (D.positions a) (fun p => app X (D.target a p)) ∧ x = node w a g := by + constructor + · intro hx + obtain ⟨a, g, ha, hg, hz, hp⟩ := mem_sigma_elim hx + refine ⟨a, ha, g, hg, ?_⟩ + by_cases hw : w = 0 + · simpa only [node, if_pos hw] using hz hw + · simpa only [node, if_neg hw] using hp hw + · rintro ⟨a, ha, g, hg, rfl⟩ + exact node_mem_fibre ha hg + +theorem app_step {D : IndexedContainer V} {w : Nat} {X : V} + (hX : X ∈ˢ famSpace w D.indices) : + app (D.step w) X = graph (D.fibre w X) D.indices := app_graph hX + +theorem app_app_step {D : IndexedContainer V} {w : Nat} {X i : V} + (hX : X ∈ˢ famSpace w D.indices) (hi : i ∈ˢ D.indices) : + app (app (D.step w) X) i = D.fibre w X i := by + rw [app_step hX, app_graph hi] + +theorem piSet_mono {A : V} {B C : V → V} (h : ∀ a, a ∈ˢ A → B a ⊆ˢ C a) : + piSet A B ⊆ˢ piSet A C := by + intro g hg + obtain ⟨hsub, htotal⟩ := mem_piSet.mp hg + refine mem_piSet.mpr ⟨?_, htotal⟩ + intro q hq + obtain ⟨a, ha, b, hb, rfl⟩ := mem_sigmaPairs.mp (hsub q hq) + exact mem_sigmaPairs.mpr ⟨a, ha, b, h a ha b hb, rfl⟩ + +theorem step_maps {D : IndexedContainer V} {w : Nat} (hD : D.WF w) : + MapsFam w D.indices (D.step w) := by + intro X hX + rw [app_step hX] + refine graph_mem_famSpace fun i hi => ?_ + by_cases hw : w = 0 + · subst w + simpa only [fibre, sigmaSet_zero, univ_zero] using truthVal_mem_univZero + (∃ a, a ∈ˢ D.shapes i ∧ ∃ g, g ∈ˢ piSet (D.positions a) + (fun p => app X (D.target a p))) + · rw [fibre, sigmaSet_pos hw] + let hU := univ_isTGUniverse (V := V) hw + refine hU.sigmaPairs_mem (hD.shapes_mem hw i hi) fun a ha => ?_ + exact hU.piSet_mem (hD.positions_mem hw i a hi ha) fun p hp => + famSpace_app hX (hD.target_mem i a p hi ha hp) + +theorem step_mono {D : IndexedContainer V} {w : Nat} (hD : D.WF w) : + MonoFam w D.indices (D.step w) := by + intro X Y hX hY hXY i hi x hx + rw [app_app_step hX hi] at hx + rw [app_app_step hY hi] + obtain ⟨a, ha, g, hg, rfl⟩ := mem_fibre.mp hx + exact node_mem_fibre ha (piSet_mono + (fun p hp => hXY _ (hD.target_mem i a p hi ha hp)) g hg) + +/-- The closed member needed by the fixed-point theorem is constructed. +At Prop the top truth family suffices; positive sorts use accessible codes. -/ +theorem closed_exists {D : IndexedContainer V} {w : Nat} (hD : D.WF w) : + ∃ L, IsClosedFam w D.indices (D.step w) L := by + by_cases hw : w = 0 + · subst w + let L := graph (fun _ => (unitSet : V)) D.indices + have hL : L ∈ˢ famSpace 0 D.indices := + graph_mem_famSpace fun _ _ => unitSet_mem_univ 0 + refine ⟨L, hL, ?_⟩ + intro i hi x hx + rw [app_app_step hL hi] at hx + obtain ⟨a, _, g, _, rfl⟩ := mem_fibre.mp hx + simpa [L, app_graph hi, node] using (pt_mem_unitSet (V := V)) + · apply container_closed_exists hw (fun X => app (D.step w) X) + D.shapes D.positions D.target spair + (hD.shapes_mem hw) (hD.positions_mem hw) hD.target_mem + · intro i a g hi ha hg + let hU := univ_isTGUniverse (V := V) hw + simpa only [spair] using hU.kpair_mem (empty_mem_univ w) + (hU.transitive (hD.shapes_mem hw i hi) ha) hg + · intro X hX i hi x hx + rw [app_app_step hX hi] at hx + obtain ⟨a, ha, g, hg, h⟩ := mem_fibre.mp hx + exact ⟨a, ha, g, hg, by simpa only [node, if_neg hw] using h⟩ + +theorem carrier_mem (D : IndexedContainer V) (w : Nat) : + D.carrier w ∈ˢ famSpace w D.indices := lfpFamSet_mem _ _ _ + +theorem carrier_fibre_mem (D : IndexedContainer V) (w : Nat) {i : V} + (hi : i ∈ˢ D.indices) : app (D.carrier w) i ∈ˢ (univ w : V) := + famSpace_app (D.carrier_mem w) hi + +theorem carrier_eq {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + {i : V} (hi : i ∈ˢ D.indices) : + app (D.carrier w) i = D.fibre w (D.carrier w) i := by + rw [← app_app_step (D.carrier_mem w) hi] + exact (app_lfpFamSet_eq (closed_exists hD) (step_mono hD) (step_maps hD) hi).symm + +theorem node_mem_carrier {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + {i a g : V} (hi : i ∈ˢ D.indices) (ha : a ∈ˢ D.shapes i) + (hg : g ∈ˢ piSet (D.positions a) (fun p => app (D.carrier w) (D.target a p))) : + node w a g ∈ˢ app (D.carrier w) i := by + rw [carrier_eq hD hi] + exact node_mem_fibre ha hg + +/-- Structural induction supplies hypotheses at every recursive position, +including all arguments of recursive function fields. -/ +theorem induction {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + (P : V → V → Prop) + (hstep : ∀ i, i ∈ˢ D.indices → ∀ a, a ∈ˢ D.shapes i → ∀ g, + g ∈ˢ piSet (D.positions a) (fun p => app (D.carrier w) (D.target a p)) → + (∀ p, p ∈ˢ D.positions a → P (D.target a p) (app g p)) → P i (node w a g)) : + ∀ i, i ∈ˢ D.indices → ∀ x, x ∈ˢ app (D.carrier w) i → P i x := by + apply lfpFamSet_induction (closed_exists hD) (step_mono hD) P + intro i hi x hx + let X := graph (fun i => sep (app (D.carrier w) i) (P i)) D.indices + have hX : X ∈ˢ famSpace w D.indices := + graph_mem_famSpace fun i hi => univ_sep_mem (D.carrier_fibre_mem w hi) + change x ∈ˢ app (app (D.step w) X) i at hx + rw [app_app_step hX hi] at hx + obtain ⟨a, ha, g, hg, rfl⟩ := mem_fibre.mp hx + have hchild (p : V) (hp : p ∈ˢ D.positions a) : + app g p ∈ˢ app (D.carrier w) (D.target a p) ∧ P (D.target a p) (app g p) := by + have h := app_mem_of_mem_piSet hg hp + rw [show X = graph (fun i => sep (app (D.carrier w) i) (P i)) D.indices from rfl, + app_graph (hD.target_mem i a p hi ha hp)] at h + exact mem_sep.mp h + apply hstep i hi a ha g + · apply piSet_mono (B := fun p => app X (D.target a p)) _ g hg + intro p hp y hy + rw [show X = graph (fun i => sep (app (D.carrier w) i) (P i)) D.indices from rfl, + app_graph (hD.target_mem i a p hi ha hp)] at hy + exact (mem_sep.mp hy).1 + · exact fun p hp => (hchild p hp).2 + +end IndexedContainer +end Ix.Theory.Model diff --git a/Ix/Theory/Model/Inductive/Recursor.lean b/Ix/Theory/Model/Inductive/Recursor.lean new file mode 100644 index 000000000..0c3acc373 --- /dev/null +++ b/Ix/Theory/Model/Inductive/Recursor.lean @@ -0,0 +1,218 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Inductive.Container + +/-! +Recursion over indexed carriers. Large elimination requires recoverable +constructor data: automatic in positive sorts, and derived from singleton +shape fibres at Prop. Accessibility is proved by the carrier's induction +principle. No well-founded recursion or computation law is assumed. +-/ + +namespace Ix.Theory.Model.IndexedContainer + +open SetTheory + +universe u +variable {V : Type u} [SetTheory V] + +abbrev Element (D : IndexedContainer V) (w : Nat) := + { ix : V × V // ix.1 ∈ˢ D.indices ∧ ix.2 ∈ˢ app (D.carrier w) ix.1 } + +/-- At Prop every constructor value is the proof point. Large elimination +therefore needs at most one shape at each index. The source producer checks +this criterion; positive carriers need no singleton restriction. -/ +def LargeElim (D : IndexedContainer V) (w : Nat) : Prop := + w = 0 → ∀ i, i ∈ˢ D.indices → ∀ a b, + a ∈ˢ D.shapes i → b ∈ˢ D.shapes i → a = b + +theorem largeElim_of_pos (D : IndexedContainer V) {w : Nat} (hw : w ≠ 0) : + D.LargeElim w := fun h => False.elim (hw h) + +theorem node_inj {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + (hlarge : D.LargeElim w) {i a b g h : V} + (hi : i ∈ˢ D.indices) (ha : a ∈ˢ D.shapes i) (hb : b ∈ˢ D.shapes i) + (hg : g ∈ˢ piSet (D.positions a) (fun p => app (D.carrier w) (D.target a p))) + (hh : h ∈ˢ piSet (D.positions b) (fun p => app (D.carrier w) (D.target b p))) + (he : node w a g = node w b h) : a = b ∧ g = h := by + by_cases hw : w = 0 + · subst w + have hab := hlarge rfl i hi a b ha hb + subst b + refine ⟨rfl, eq_of_mem_piSet_app_eq hg hh ?_⟩ + intro p hp + have hP : app (D.carrier 0) (D.target a p) ∈ˢ (univZero : V) := by + simpa only [univ_zero] using D.carrier_fibre_mem 0 (hD.target_mem i a p hi ha hp) + exact (eq_pt_of_mem_univZero hP (app_mem_of_mem_piSet (a := p) hg hp)).trans + (eq_pt_of_mem_univZero hP (app_mem_of_mem_piSet (a := p) hh hp)).symm + · apply kpair_inj + simpa only [node, if_neg hw, spair] using he + +def Child (D : IndexedContainer V) (w : Nat) (child parent : D.Element w) : Prop := + ∃ a, a ∈ˢ D.shapes parent.val.1 ∧ ∃ g, + g ∈ˢ piSet (D.positions a) (fun p => app (D.carrier w) (D.target a p)) ∧ + parent.val.2 = node w a g ∧ ∃ p, p ∈ˢ D.positions a ∧ + child.val = (D.target a p, app g p) + +theorem child_wellFounded {D : IndexedContainer V} {w : Nat} + (hD : D.WF w) (hlarge : D.LargeElim w) : WellFounded (D.Child w) := by + constructor + rintro ⟨⟨i, x⟩, hi, hx⟩ + apply D.induction hD (fun i x => ∀ h : i ∈ˢ D.indices ∧ x ∈ˢ app (D.carrier w) i, + Acc (D.Child w) ⟨(i, x), h⟩) ?_ i hi x hx ⟨hi, hx⟩ + intro i hi a ha g hg ih h + constructor + intro y hy + obtain ⟨b, hb, g', hg', he, p, hp, hyp⟩ := hy + obtain ⟨rfl, rfl⟩ := node_inj hD hlarge hi ha hb hg hg' he + have hy' : y = ⟨(D.target a p, app g p), + hD.target_mem i a p hi ha hp, app_mem_of_mem_piSet hg hp⟩ := Subtype.ext hyp + rw [hy'] + exact ih p hp ⟨hD.target_mem i a p hi ha hp, app_mem_of_mem_piSet hg hp⟩ + +structure NodeView (D : IndexedContainer V) (w : Nat) (z : D.Element w) where + shape : V + branches : V + shape_mem : shape ∈ˢ D.shapes z.val.1 + branches_mem : branches ∈ˢ piSet (D.positions shape) + (fun p => app (D.carrier w) (D.target shape p)) + equation : z.val.2 = node w shape branches + +noncomputable def view {D : IndexedContainer V} {w : Nat} + (hD : D.WF w) (z : D.Element w) : D.NodeView w z := + let ex := mem_fibre.mp ((carrier_eq hD z.property.1) ▸ z.property.2) + let a := Classical.choose ex + let ha := Classical.choose_spec ex + let g := Classical.choose ha.2 + let hg := Classical.choose_spec ha.2 + ⟨a, g, ha.1, hg.1, hg.2⟩ + +noncomputable def child {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + {i a g : V} (hi : i ∈ˢ D.indices) (ha : a ∈ˢ D.shapes i) + (hg : g ∈ˢ piSet (D.positions a) (fun p => app (D.carrier w) (D.target a p))) + (p : V) (hp : p ∈ˢ D.positions a) : D.Element w := + ⟨(D.target a p, app g p), hD.target_mem i a p hi ha hp, app_mem_of_mem_piSet hg hp⟩ + +theorem child_rel {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + {z : D.Element w} (v : D.NodeView w z) {p : V} (hp : p ∈ˢ D.positions v.shape) : + D.Child w (child hD z.property.1 v.shape_mem v.branches_mem p hp) z := + ⟨v.shape, v.shape_mem, v.branches, v.branches_mem, v.equation, p, hp, rfl⟩ + +noncomputable def childResults {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + {i a g : V} (hi : i ∈ˢ D.indices) (ha : a ∈ˢ D.shapes i) + (hg : g ∈ˢ piSet (D.positions a) (fun p => app (D.carrier w) (D.target a p))) + (r : D.Element w → V) : V := + open Classical in + graph (fun p => if hp : p ∈ˢ D.positions a then r (child hD hi ha hg p hp) else empty) + (D.positions a) + +theorem app_childResults {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + {i a g : V} (hi : i ∈ˢ D.indices) (ha : a ∈ˢ D.shapes i) + (hg : g ∈ˢ piSet (D.positions a) (fun p => app (D.carrier w) (D.target a p))) + (r : D.Element w → V) {p : V} (hp : p ∈ˢ D.positions a) : + app (childResults hD hi ha hg r) p = r (child hD hi ha hg p hp) := by + simp only [childResults, app_graph hp, dif_pos hp] + +noncomputable def foldStep {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + (algebra : V → V → V → V → V) (z : D.Element w) + (r : ∀ y, D.Child w y z → V) : V := + open Classical in + let v := view hD z + algebra z.val.1 v.shape v.branches + (graph (fun p => if hp : p ∈ˢ D.positions v.shape then + r (child hD z.property.1 v.shape_mem v.branches_mem p hp) (child_rel hD v hp) + else empty) (D.positions v.shape)) + +noncomputable def fold {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + (hlarge : D.LargeElim w) (algebra : V → V → V → V → V) : D.Element w → V := + (child_wellFounded hD hlarge).fix (foldStep hD algebra) + +theorem fold_eq_view {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + (hlarge : D.LargeElim w) (algebra : V → V → V → V → V) (z : D.Element w) : + fold hD hlarge algebra z = + algebra z.val.1 (view hD z).shape (view hD z).branches + (childResults hD z.property.1 (view hD z).shape_mem (view hD z).branches_mem + (fold hD hlarge algebra)) := by + rw [fold, WellFounded.fix_eq] + rfl + +theorem fold_node {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + (hlarge : D.LargeElim w) (algebra : V → V → V → V → V) + {i a g : V} (hi : i ∈ˢ D.indices) (ha : a ∈ˢ D.shapes i) + (hg : g ∈ˢ piSet (D.positions a) (fun p => app (D.carrier w) (D.target a p))) : + fold hD hlarge algebra ⟨(i, node w a g), hi, node_mem_carrier hD hi ha hg⟩ = + algebra i a g (childResults hD hi ha hg (fold hD hlarge algebra)) := by + let z : D.Element w := ⟨(i, node w a g), hi, node_mem_carrier hD hi ha hg⟩ + have inj := node_inj hD hlarge hi ha (view hD z).shape_mem hg + (view hD z).branches_mem (view hD z).equation + change fold hD hlarge algebra z = _ + rw [fold_eq_view] + simp only [← inj.1, ← inj.2] + rfl + +def AlgebraTyping (D : IndexedContainer V) (w : Nat) + (M : V → V → V) (algebra : V → V → V → V → V) : Prop := + ∀ i, i ∈ˢ D.indices → ∀ a, a ∈ˢ D.shapes i → ∀ g, + g ∈ˢ piSet (D.positions a) (fun p => app (D.carrier w) (D.target a p)) → + ∀ ih, ih ∈ˢ piSet (D.positions a) (fun p => M (D.target a p) (app g p)) → + algebra i a g ih ∈ˢ M i (node w a g) + +theorem fold_mem {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + (hlarge : D.LargeElim w) {M : V → V → V} {algebra : V → V → V → V → V} + (ha : D.AlgebraTyping w M algebra) (z : D.Element w) : + fold hD hlarge algebra z ∈ˢ M z.val.1 z.val.2 := by + induction z using (child_wellFounded hD hlarge).induction with + | h z ih => + rw [fold_eq_view] + let v := view hD z + have hresults : childResults hD z.property.1 v.shape_mem v.branches_mem + (fold hD hlarge algebra) ∈ˢ + piSet (D.positions v.shape) (fun p => M (D.target v.shape p) (app v.branches p)) := by + apply graph_mem_piSet + intro p hp + simpa only [dif_pos hp, child] using + ih (child hD z.property.1 v.shape_mem v.branches_mem p hp) (child_rel hD v hp) + have hstep := ha z.val.1 z.property.1 v.shape v.shape_mem v.branches v.branches_mem + _ hresults + exact (congrArg (fun x => _ ∈ˢ M z.val.1 x) v.equation).mpr hstep + +/-- Total form used inside function graphs. Only typed domain elements reach +the recursive branch; the value outside that domain is irrelevant. -/ +noncomputable def foldAt {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + (hlarge : D.LargeElim w) (algebra : V → V → V → V → V) (i x : V) : V := + open Classical in + if h : i ∈ˢ D.indices ∧ x ∈ˢ app (D.carrier w) i then + fold hD hlarge algebra ⟨(i, x), h⟩ else empty + +theorem foldAt_eq {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + (hlarge : D.LargeElim w) (algebra : V → V → V → V → V) {i x : V} + (hi : i ∈ˢ D.indices) (hx : x ∈ˢ app (D.carrier w) i) : + foldAt hD hlarge algebra i x = fold hD hlarge algebra ⟨(i, x), hi, hx⟩ := by + simp only [foldAt, dif_pos (And.intro hi hx)] + +theorem foldAt_mem {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + (hlarge : D.LargeElim w) {M : V → V → V} {algebra : V → V → V → V → V} + (ha : D.AlgebraTyping w M algebra) {i x : V} + (hi : i ∈ˢ D.indices) (hx : x ∈ˢ app (D.carrier w) i) : + foldAt hD hlarge algebra i x ∈ˢ M i x := by + rw [foldAt_eq hD hlarge algebra hi hx] + exact fold_mem hD hlarge ha _ + +/-- Small elimination does not need constructor injectivity. A Prop-valued +motive follows directly by structural induction, even for many constructors. -/ +theorem small_elim {D : IndexedContainer V} {w : Nat} (hD : D.WF w) + {M : V → V → V} {algebra : V → V → V → V → V} + (hM : ∀ i, i ∈ˢ D.indices → ∀ x, x ∈ˢ app (D.carrier w) i → M i x ∈ˢ (univZero : V)) + (ha : D.AlgebraTyping w M algebra) : + ∀ i, i ∈ˢ D.indices → ∀ x, x ∈ˢ app (D.carrier w) i → (pt : V) ∈ˢ M i x := by + apply D.induction hD (fun i x => (pt : V) ∈ˢ M i x) + intro i hi a ha' g hg ih + have hstep := ha i hi a ha' g hg (graph (fun _ => (pt : V)) (D.positions a)) + (graph_mem_piSet ih) + have he := eq_pt_of_mem_univZero (hM i hi _ (node_mem_carrier hD hi ha' hg)) hstep + rwa [he] at hstep + +end Ix.Theory.Model.IndexedContainer diff --git a/Ix/Theory/Model/Inductive/Telescope.lean b/Ix/Theory/Model/Inductive/Telescope.lean new file mode 100644 index 000000000..01d0ddf80 --- /dev/null +++ b/Ix/Theory/Model/Inductive/Telescope.lean @@ -0,0 +1,585 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Judgment +import Ix.Theory.Model.Support +import Ix.Theory.Model.SetModel.TupleTower + +/-! +Dependent telescopes used by inductive parameters, indices, constructor +fields, and recursive function domains. Curried functions and tuple codes +share the same fitting relation. Computation requires fitting arguments, +including when a function is represented by the proof point. +-/ + +namespace Ix.Theory.Model.Telescope + +open SetTheory SetTheory.Tower SetModel + +universe u v +variable {V : Type v} [SetTheory V] + +noncomputable def piN (w : Nat) : {n : Nat} → TeleS V n → (List V → V) → V + | _, .nil, R => R [] + | _, .cons A B, R => piR w A (fun x => piN w (B x) (fun xs => R (x :: xs))) + +noncomputable def curry (w : Nat) : {n : Nat} → TeleS V n → (List V → V) → V + | _, .nil, f => f [] + | _, .cons A B, f => lamR w A (fun x => curry w (B x) (fun xs => f (x :: xs))) + +noncomputable def applyN : V → List V → V + | f, [] => f + | f, x :: xs => applyN (app f x) xs + +theorem applyN_append (f : V) (xs ys : List V) : + applyN f (xs ++ ys) = applyN (applyN f xs) ys := by + induction xs generalizing f <;> simp_all [applyN] + +theorem curry_mem {w : Nat} : ∀ {n} (T : TeleS V n) {f R : List V → V}, + (∀ xs, FitsS T xs → f xs ∈ˢ R xs) → curry w T f ∈ˢ piN w T R + | _, .nil, _, _, hf => hf [] trivial + | _, .cons _ B, _, _, hf => + lamR_mem fun x hx => curry_mem (B x) (fun xs hxs => hf (x :: xs) ⟨hx, hxs⟩) + +theorem piN_zero_mem : ∀ {n} (T : TeleS V n) {R : List V → V}, + (∀ xs, FitsS T xs → R xs ∈ˢ (univZero : V)) → + piN 0 T R ∈ˢ (univZero : V) + | _, .nil, _, hR => hR [] trivial + | _, .cons _ _, _, _ => piR_zero_mem_univZero + +theorem applyN_mem {w : Nat} : ∀ {n} (T : TeleS V n) {f : V} {R : List V → V} + {xs : List V}, f ∈ˢ piN w T R → FitsS T xs → + (w = 0 → ∀ ys, FitsS T ys → R ys ∈ˢ (univZero : V)) → + applyN f xs ∈ˢ R xs + | _, .nil, _, _, [], hf, _, _ => hf + | _, .cons _ B, _, R, x :: xs, hf, hxs, hR => by + apply applyN_mem (B x) (R := fun ys => R (x :: ys)) + (app_mem_piR hf hxs.1 ?_) hxs.2 + · intro hw ys hys + exact hR hw (x :: ys) ⟨hxs.1, hys⟩ + · intro hw y hy + subst w + exact piN_zero_mem (B y) (fun ys hys => hR rfl (y :: ys) ⟨hy, hys⟩) + +theorem applyN_curry {w : Nat} : ∀ {n} (T : TeleS V n) {f R : List V → V} + {xs : List V}, FitsS T xs → + (∀ ys, FitsS T ys → f ys ∈ˢ R ys) → + (w = 0 → ∀ ys, FitsS T ys → R ys ∈ˢ (univZero : V)) → + applyN (curry w T f) xs = f xs + | _, .nil, _, _, [], _, _, _ => rfl + | _, .cons _ B, f, R, x :: xs, hxs, hf, hR => by + change applyN (app (lamR w _ _) x) xs = f (x :: xs) + rw [app_lamR hxs.1 (B := fun y => piN w (B y) (fun ys => R (y :: ys))) + (fun y hy => curry_mem (B y) (fun ys hys => hf (y :: ys) ⟨hy, hys⟩)) + (fun hw y hy => by + subst w + exact piN_zero_mem (B y) (fun ys hys => hR rfl (y :: ys) ⟨hy, hys⟩))] + exact applyN_curry (B x) hxs.2 + (fun ys hys => hf (x :: ys) ⟨hxs.1, hys⟩) + (fun hw ys hys => hR hw (x :: ys) ⟨hxs.1, hys⟩) + +theorem curry_congr {w : Nat} : ∀ {n} (T : TeleS V n) {f g : List V → V}, + (∀ xs, FitsS T xs → f xs = g xs) → curry w T f = curry w T g + | _, .nil, _, _, h => h [] trivial + | _, .cons _ B, _, _, h => + lamR_congr fun x hx => curry_congr (B x) (fun xs hxs => h (x :: xs) ⟨hx, hxs⟩) + +theorem piN_zero_agree {w w' : Nat} (hw : w = 0 ↔ w' = 0) : + ∀ {n} (T : TeleS V n) {R R' : List V → V}, + (∀ xs, FitsS T xs → R xs = R' xs) → piN w T R = piN w' T R' + | _, .nil, _, _, h => h [] trivial + | _, .cons _ B, _, _, h => + piR_zero_agree hw fun x hx => piN_zero_agree hw (B x) (fun xs hxs => h (x :: xs) ⟨hx, hxs⟩) + +theorem piN_congr {w : Nat} {n : Nat} (T : TeleS V n) {R R' : List V → V} + (h : ∀ xs, FitsS T xs → R xs = R' xs) : piN w T R = piN w T R' := + piN_zero_agree Iff.rfl T h + +theorem curry_zero_agree {w w' : Nat} (hw : w = 0 ↔ w' = 0) : + ∀ {n} (T : TeleS V n) {f f' : List V → V}, + (∀ xs, FitsS T xs → f xs = f' xs) → curry w T f = curry w' T f' + | _, .nil, _, _, h => h [] trivial + | _, .cons _ B, _, _, h => + lamR_zero_agree hw fun x hx => curry_zero_agree hw (B x) (fun xs hxs => h (x :: xs) ⟨hx, hxs⟩) + +theorem curry_point : ∀ {n} (T : TeleS V n), curry 0 T (fun _ => (pt : V)) = pt + | _, .nil => rfl + | _, .cons _ _ => lamR_zero + +theorem curry_applyN {w : Nat} : ∀ {n} (T : TeleS V n) {f : V} {R : List V → V}, + f ∈ˢ piN w T R → + (w = 0 → ∀ ys, FitsS T ys → R ys ∈ˢ (univZero : V)) → + curry w T (applyN f) = f + | _, .nil, _, _, _, _ => rfl + | _, .cons _ B, _, R, hf, hR => by + change lamR w _ (fun x => curry w (B x) (applyN (app _ x))) = _ + calc + _ = lamR w _ (fun x => app _ x) := by + apply lamR_congr + intro x hx + apply curry_applyN (B x) (R := fun ys => R (x :: ys)) + (app_mem_piR hf hx ?_) + · intro hw ys hys + exact hR hw (x :: ys) ⟨hx, hys⟩ + · intro hw y hy + subst w + exact piN_zero_mem (B y) (fun ys hys => hR rfl (y :: ys) ⟨hy, hys⟩) + _ = _ := lamR_eta hf + +/-- Tuple codes always preserve their fields, even when a family itself is +Prop-valued. The carrier bounds are needed only at positive sorts. -/ +theorem tower_graph_mem {w : Nat} (hw : w ≠ 0) : ∀ {n} (T : TeleS V n), + BoundS w T → towerSet 1 T ∈ˢ (univ w : V) + | _, .nil, _ => unitSet_mem_univ w + | _, .cons _ B, hB => by + change sigmaSet 1 _ _ ∈ˢ (univ w : V) + rw [sigmaSet_pos (by decide : 1 ≠ 0)] + exact (univ_isTGUniverse hw).sigmaPairs_mem hB.1 + (fun x hx => tower_graph_mem hw (B x) (hB.2 x hx)) + +theorem piN_mem_univ {w : Nat} : ∀ {n} (T : TeleS V n) {R : List V → V}, + (w ≠ 0 → BoundS w T) → (∀ xs, FitsS T xs → R xs ∈ˢ (univ w : V)) → + piN w T R ∈ˢ (univ w : V) + | _, .nil, _, _, hR => hR [] trivial + | _, .cons A B, R, hT, hR => by + by_cases hw : w = 0 + · subst w + exact univ_zero (V := V) ▸ piR_zero_mem_univZero + · change piR w A (fun x => piN w (B x) (fun xs => R (x :: xs))) ∈ˢ (univ w : V) + rw [piR_pos hw] + apply (univ_isTGUniverse hw).piSet_mem (hT hw).1 + intro x hx + exact piN_mem_univ (B x) (fun _ => (hT hw).2 x hx) + (fun xs hxs => hR (x :: xs) ⟨hx, hxs⟩) + +def simple : (domains : List V) → TeleS V domains.length + | [] => .nil + | A :: rest => .cons A (fun _ => simple rest) + +theorem fits_simple_getD : ∀ {domains xs : List V}, FitsS (simple domains) xs → + ∀ {i A}, domains[i]? = some A → xs.getD i empty ∈ˢ A + | [], [], _, _, _, h => by simp at h + | _ :: _, _ :: _, h, 0, _, hA => by + cases Option.some.inj hA + exact h.1 + | _ :: _, _ :: _, h, i + 1, _, hA => fits_simple_getD h.2 hA + +theorem fits_simple_of_getD : ∀ {domains xs : List V}, xs.length = domains.length → + (∀ i A, domains[i]? = some A → xs.getD i empty ∈ˢ A) → FitsS (simple domains) xs + | [], [], _, _ => trivial + | _ :: _, _ :: _, hlen, h => + ⟨h 0 _ rfl, fits_simple_of_getD (Nat.succ.inj hlen) (fun i A hA => h (i + 1) A hA)⟩ + +theorem fits_simple_map {α : Type u} (f : α → V) (domains : List α) (xs : List V) + (hlen : xs.length = domains.length) + (h : ∀ i a, domains[i]? = some a → xs.getD i empty ∈ˢ f a) : + FitsS (simple (domains.map f)) xs := by + apply fits_simple_of_getD (hlen.trans (List.length_map ..).symm) + intro i A hA + obtain ⟨a, ha, rfl⟩ := Option.map_eq_some_iff.mp + ((List.getElem?_map (l := domains) (f := f)).symm.trans hA) + exact h i a ha + +theorem fits_unique_of_prop : ∀ {n} {T : TeleS V n} {xs ys : List V}, + PropS T → FitsS T xs → FitsS T ys → xs = ys + | _, .nil, [], [], _, _, _ => rfl + | _, .cons _ B, x :: xs, y :: ys, hT, hx, hy => by + have he := subsingleton_of_mem_univZero hT.1 hx.1 hy.1 + subst y + exact congrArg (x :: ·) (fits_unique_of_prop (T := B x) (hT.2 x hx.1) hx.2 hy.2) + +variable {β : Type u} + +def context (Γ : Context β) : List (AExpr β) → Context β + | [] => Γ + | A :: rest => context (Γ.push A) rest + +def extend (env : Nat → V) : List V → Nat → V + | [] => env + | x :: xs => extend (Valuation.cons x env) xs + +omit [SetTheory V] in +theorem extend_beyond (env : Nat → V) (xs : List V) (i : Nat) : + extend env xs (xs.length + i) = env i := by + induction xs generalizing env i with + | nil => simp [extend] + | cons x xs ih => + simpa only [extend, List.length_cons, Nat.add_assoc, Nat.add_comm, Nat.add_left_comm, + Valuation.cons_succ] using ih (Valuation.cons x env) (i + 1) + +theorem extend_getD (env : Nat → V) (xs : List V) (i : Nat) (hi : i < xs.length) : + extend env xs (xs.length - 1 - i) = xs.getD i empty := by + induction xs generalizing env i with + | nil => simp at hi + | cons x xs ih => + cases i with + | zero => + simpa only [extend, List.length_cons, Nat.add_sub_cancel, Nat.sub_zero, + List.getD_cons_zero, Valuation.cons_zero, Nat.add_zero] using + extend_beyond (Valuation.cons x env) xs 0 + | succ i => + have hidx : (x :: xs).length - 1 - (i + 1) = xs.length - 1 - i := by simp; omega + rw [hidx] + exact ih (Valuation.cons x env) i (by simpa using hi) + +omit [SetTheory V] in +theorem skip_extend (env : Nat → V) (xs : List V) : + Valuation.skip xs.length 0 (extend env xs) = env := by + funext i + simpa only [Valuation.skip, Nat.not_lt_zero, ↓reduceIte] using extend_beyond env xs i + +omit [SetTheory V] in +theorem extend_append (env : Nat → V) (xs ys : List V) : + extend env (xs ++ ys) = extend (extend env xs) ys := by + induction xs generalizing env <;> simp_all [extend] + +omit [SetTheory V] in +theorem skip_extend_at (env : Nat → V) (xs : List V) (count cutoff : Nat) : + Valuation.skip count (xs.length + cutoff) (extend env xs) = + extend (Valuation.skip count cutoff env) xs := by + induction xs generalizing env cutoff with + | nil => simp only [List.length_nil, Nat.zero_add, extend] + | cons x xs ih => + simpa only [extend, List.length_cons, Nat.add_assoc, Nat.add_comm, Nat.add_left_comm, + Valuation.skip_cons] using ih (Valuation.cons x env) (cutoff + 1) + +omit [SetTheory V] in +theorem skip_middle (env : Nat → V) (middle tail : List V) : + Valuation.skip middle.length tail.length (extend (extend env middle) tail) = extend env tail := by + simpa only [Nat.add_zero, skip_extend] using skip_extend_at (extend env middle) tail middle.length 0 + +noncomputable def interpret (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) : (domains : List (AExpr β)) → TeleS V domains.length + | [] => .nil + | A :: rest => .cons (interp constants levels env A) + (fun x => interpret constants levels (Valuation.cons x env) rest) + +theorem piN_append (constants : Assignment β V) (levels : List Nat) (env : Nat → V) + (left right : List (AExpr β)) (w : Nat) (R : List V → V) : + piN w (interpret constants levels env (left ++ right)) R = + piN w (interpret constants levels env left) (fun xs => + piN w (interpret constants levels (extend env xs) right) (fun ys => R (xs ++ ys))) := by + induction left generalizing env R with + | nil => rfl + | cons A rest ih => + change piR w _ _ = piR w _ _ + congr 1 + funext x + exact ih (Valuation.cons x env) (fun xs => R (x :: xs)) + +theorem curry_append (constants : Assignment β V) (levels : List Nat) (env : Nat → V) + (left right : List (AExpr β)) (w : Nat) (f : List V → V) : + curry w (interpret constants levels env (left ++ right)) f = + curry w (interpret constants levels env left) (fun xs => + curry w (interpret constants levels (extend env xs) right) (fun ys => f (xs ++ ys))) := by + induction left generalizing env f with + | nil => rfl + | cons A rest ih => + change lamR w _ _ = lamR w _ _ + congr 1 + funext x + exact ih (Valuation.cons x env) (fun xs => f (x :: xs)) + +def lift (count : Nat) : List (AExpr β) → (cutoff : Nat := 0) → List (AExpr β) + | [], _ => [] + | A :: rest, k => A.liftN count k :: lift count rest (k + 1) + +def inst (arg : AExpr β) : List (AExpr β) → (cutoff : Nat := 0) → List (AExpr β) + | [], _ => [] + | A :: rest, k => A.inst arg k :: inst arg rest (k + 1) + +/-- A telescope of domains that all live in the same outer context. -/ +def independent : List (AExpr β) → (offset : Nat := 0) → List (AExpr β) + | [], _ => [] + | A :: rest, offset => A.liftN offset :: independent rest (offset + 1) + +theorem piN_independent (constants : Assignment β V) (levels : List Nat) (env : Nat → V) + (domains : List (AExpr β)) (previous : List V) (w : Nat) (R : List V → V) : + piN w (interpret constants levels (extend env previous) (independent domains previous.length)) R = + piN w (simple (domains.map (interp constants levels env))) R := by + induction domains generalizing previous R with + | nil => rfl + | cons A rest ih => + change piR w (interp constants levels (extend env previous) (A.liftN previous.length)) _ = piR w _ _ + rw [interp_liftN, skip_extend] + congr 1 + funext x + have ht := ih (previous ++ [x]) (fun xs => R (x :: xs)) + have hlen : (previous ++ [x]).length = previous.length + 1 := by simp + rw [hlen] at ht + simpa only [extend_append, extend] using ht + +theorem curry_independent (constants : Assignment β V) (levels : List Nat) (env : Nat → V) + (domains : List (AExpr β)) (previous : List V) (w : Nat) (f : List V → V) : + curry w (interpret constants levels (extend env previous) (independent domains previous.length)) f = + curry w (simple (domains.map (interp constants levels env))) f := by + induction domains generalizing previous f with + | nil => rfl + | cons A rest ih => + change lamR w (interp constants levels (extend env previous) (A.liftN previous.length)) _ = lamR w _ _ + rw [interp_liftN, skip_extend] + congr 1 + funext x + have ht := ih (previous ++ [x]) (fun xs => f (x :: xs)) + have hlen : (previous ++ [x]).length = previous.length + 1 := by simp + rw [hlen] at ht + simpa only [extend_append, extend] using ht + +@[simp] theorem length_lift (count : Nat) (domains : List (AExpr β)) (k : Nat) : + (lift count domains k).length = domains.length := by + induction domains generalizing k <;> simp_all [lift] + +@[simp] theorem length_inst (arg : AExpr β) (domains : List (AExpr β)) (k : Nat) : + (inst arg domains k).length = domains.length := by + induction domains generalizing k <;> simp_all [inst] + +theorem fits_lift (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (count : Nat) (domains : List (AExpr β)) (k : Nat) (xs : List V) : + FitsS (interpret constants levels env (lift count domains k)) xs ↔ + FitsS (interpret constants levels (Valuation.skip count k env) domains) xs := by + induction domains generalizing env k xs with + | nil => rfl + | cons A rest ih => + cases xs with + | nil => rfl + | cons x xs => + simp only [lift, interpret, FitsS, interp_liftN, ih, Valuation.skip_cons] + +theorem fits_inst (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (arg : AExpr β) (domains : List (AExpr β)) (k : Nat) (xs : List V) : + FitsS (interpret constants levels env (inst arg domains k)) xs ↔ + FitsS (interpret constants levels + (Valuation.insert k (interp constants levels (Valuation.skip k 0 env) arg) env) domains) xs := by + induction domains generalizing env k xs with + | nil => rfl + | cons A rest ih => + cases xs with + | nil => rfl + | cons x xs => + simp only [inst, interpret, FitsS, interp_inst, ih, + Valuation.skip_succ_cons, Valuation.insert_cons] + +theorem piN_lift (constants : Assignment β V) (levels : List Nat) (env : Nat → V) + (count : Nat) (domains : List (AExpr β)) (cutoff w : Nat) (R : List V → V) : + piN w (interpret constants levels env (lift count domains cutoff)) R = + piN w (interpret constants levels (Valuation.skip count cutoff env) domains) R := by + induction domains generalizing env cutoff R with + | nil => rfl + | cons A rest ih => + simp only [lift, interpret, piN, interp_liftN] + congr 1 + funext x + simpa only [Valuation.skip_cons] using ih (Valuation.cons x env) (cutoff + 1) (fun xs => R (x :: xs)) + +theorem curry_lift (constants : Assignment β V) (levels : List Nat) (env : Nat → V) + (count : Nat) (domains : List (AExpr β)) (cutoff w : Nat) (f : List V → V) : + curry w (interpret constants levels env (lift count domains cutoff)) f = + curry w (interpret constants levels (Valuation.skip count cutoff env) domains) f := by + induction domains generalizing env cutoff f with + | nil => rfl + | cons A rest ih => + simp only [lift, interpret, curry, interp_liftN] + congr 1 + funext x + simpa only [Valuation.skip_cons] using ih (Valuation.cons x env) (cutoff + 1) (fun xs => f (x :: xs)) + +theorem piN_instL (constants : Assignment β V) (levels : List Nat) (env : Nat → V) + (sourceLevels : List VLevel) (domains : List (AExpr β)) (w : Nat) (R : List V → V) : + piN w (interpret constants levels env (domains.map (AExpr.instL sourceLevels))) R = + piN w (interpret constants (sourceLevels.map (VLevel.eval levels)) env domains) R := by + induction domains generalizing env R with + | nil => rfl + | cons A rest ih => + change piR w (interp constants levels env (A.instL sourceLevels)) + (fun x => piN w (interpret constants levels (Valuation.cons x env) + (rest.map (AExpr.instL sourceLevels))) (fun xs => R (x :: xs))) = + piR w (interp constants (sourceLevels.map (VLevel.eval levels)) env A) _ + rw [interp_instL] + congr 1 + funext x + exact ih (Valuation.cons x env) (fun xs => R (x :: xs)) + +theorem curry_instL (constants : Assignment β V) (levels : List Nat) (env : Nat → V) + (sourceLevels : List VLevel) (domains : List (AExpr β)) (w : Nat) (f : List V → V) : + curry w (interpret constants levels env (domains.map (AExpr.instL sourceLevels))) f = + curry w (interpret constants (sourceLevels.map (VLevel.eval levels)) env domains) f := by + induction domains generalizing env f with + | nil => rfl + | cons A rest ih => + change lamR w (interp constants levels env (A.instL sourceLevels)) + (fun x => curry w (interpret constants levels (Valuation.cons x env) + (rest.map (AExpr.instL sourceLevels))) (fun xs => f (x :: xs))) = + lamR w (interp constants (sourceLevels.map (VLevel.eval levels)) env A) _ + rw [interp_instL] + congr 1 + funext x + exact ih (Valuation.cons x env) (fun xs => f (x :: xs)) + +theorem fits_instL (constants : Assignment β V) (levels : List Nat) (env : Nat → V) + (sourceLevels : List VLevel) (domains : List (AExpr β)) (xs : List V) : + FitsS (interpret constants levels env (domains.map (AExpr.instL sourceLevels))) xs ↔ + FitsS (interpret constants (sourceLevels.map (VLevel.eval levels)) env domains) xs := by + induction domains generalizing env xs with + | nil => rfl + | cons A rest ih => + cases xs with + | nil => rfl + | cons x xs => + change (x ∈ˢ interp constants levels env (A.instL sourceLevels) ∧ + FitsS (interpret constants levels (Valuation.cons x env) (rest.map (AExpr.instL sourceLevels))) xs) ↔ _ + simp only [interp_instL, ih, interpret, FitsS] + +/-- Every field sort is derived by the same semantic typing relation as an +ordinary term. The executable producer checks each successive context. -/ +inductive Formed (entries : Environment β) : Context β → List (AExpr β) → Prop + | nil {Γ} : Formed entries Γ [] + | cons {Γ A rest} (level : VLevel) + (domain : TypingClaim.{u,v} entries Γ A (.sort level)) + (tail : Formed entries (Γ.push A) rest) : Formed entries Γ (A :: rest) + +theorem Formed.valid {entries : Environment β} {Γ : Context β} {domains : List (AExpr β)} + (h : Formed.{u,v} entries Γ domains) (constants : Assignment β V) + (hM : Realizes constants entries) (levels : List Nat) : + ∀ {env xs}, Γ.Valid constants levels env → FitsS (interpret constants levels env domains) xs → + (context Γ domains).Valid constants levels (extend env xs) := by + induction h with + | nil => + intro env xs hΓ hxs + cases xs with + | nil => exact hΓ + | cons _ _ => exact hxs.elim + | cons l hA hrest ih => + intro env xs hΓ hxs + cases xs with + | nil => exact hxs.elim + | cons x xs => + exact ih (env := Valuation.cons x env) (xs := xs) + (hΓ.push (hA V constants hM levels env hΓ).1 hxs.1) hxs.2 + +theorem Formed.append {entries : Environment β} {Γ : Context β} {left right : List (AExpr β)} + (hl : Formed.{u,v} entries Γ left) (hr : Formed.{u,v} entries (context Γ left) right) : + Formed.{u,v} entries Γ (left ++ right) := by + induction hl with + | nil => exact hr + | cons l hA hrest ih => exact .cons l hA (ih hr) + +end Ix.Theory.Model.Telescope + +namespace Ix.Theory.Model.AExpr + +open Certified +universe u v +variable {β : Type u} + +def forallN (p : PropWhen) : List (AExpr β) → AExpr β → AExpr β + | [], B => B + | A :: rest, B => .forallE p A (forallN p rest B) + +def lamN (p : PropWhen) : List (AExpr β) → AExpr β → AExpr β + | [], body => body + | A :: rest, body => .lam p A (lamN p rest body) + +def appN (f : AExpr β) : List (AExpr β) → AExpr β + | [] => f + | x :: xs => appN (.app f x) xs + +theorem forallN_append (p : PropWhen) (left right : List (AExpr β)) (B : AExpr β) : + forallN p (left ++ right) B = forallN p left (forallN p right B) := by + induction left <;> simp_all [forallN] + +theorem lamN_append (p : PropWhen) (left right : List (AExpr β)) (body : AExpr β) : + lamN p (left ++ right) body = lamN p left (lamN p right body) := by + induction left <;> simp_all [lamN] + +theorem appN_append (f : AExpr β) (left right : List (AExpr β)) : + appN f (left ++ right) = appN (appN f left) right := by + induction left generalizing f <;> simp_all [appN] + +theorem ReferencesIn.forallN {entries : Environment β} {p : PropWhen} + {domains : List (AExpr β)} {B : AExpr β} + (hD : ∀ A ∈ domains, A.ReferencesIn entries) (hB : B.ReferencesIn entries) : + (forallN p domains B).ReferencesIn entries := by + induction domains with + | nil => exact hB + | cons A rest ih => + have hA := hD A (List.mem_cons_self ..) + have ht := ih (fun D h => hD D (List.mem_cons_of_mem A h)) + intro r hr + exact (List.mem_append.mp hr).elim (hA r) (ht r) + +theorem ReferencesIn.lamN {entries : Environment β} {p : PropWhen} + {domains : List (AExpr β)} {body : AExpr β} + (hD : ∀ A ∈ domains, A.ReferencesIn entries) (hb : body.ReferencesIn entries) : + (lamN p domains body).ReferencesIn entries := by + induction domains with + | nil => exact hb + | cons A rest ih => + have hA := hD A (List.mem_cons_self ..) + have ht := ih (fun D h => hD D (List.mem_cons_of_mem A h)) + intro r hr + exact (List.mem_append.mp hr).elim (hA r) (ht r) + +@[simp] theorem erase_forallN (p : PropWhen) (domains : List (AExpr β)) (B : AExpr β) : + (forallN p domains B).erase = VExpr.forallN (domains.map erase) B.erase := by + induction domains <;> simp_all [forallN, erase, VExpr.forallN] + +@[simp] theorem erase_lamN (p : PropWhen) (domains : List (AExpr β)) (B : AExpr β) : + (lamN p domains B).erase = VExpr.lamN (domains.map erase) B.erase := by + induction domains <;> simp_all [lamN, erase, VExpr.lamN] + +@[simp] theorem erase_appN (f : AExpr β) (args : List (AExpr β)) : + (appN f args).erase = VExpr.appN f.erase (args.map erase) := by + induction args generalizing f <;> simp_all [appN, erase, VExpr.appN] + +variable {V : Type v} [SetTheory V] + +theorem interp_forallN (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (p : PropWhen) (domains : List (AExpr β)) (B : AExpr β) : + interp constants levels env (forallN p domains B) = + Telescope.piN (regime p levels) (Telescope.interpret constants levels env domains) + (fun xs => interp constants levels (Telescope.extend env xs) B) := by + induction domains generalizing env with + | nil => rfl + | cons A rest ih => + simp only [forallN, interp, Telescope.interpret, Telescope.piN, Telescope.extend, ih] + +theorem interp_lamN (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (p : PropWhen) (domains : List (AExpr β)) (body : AExpr β) : + interp constants levels env (lamN p domains body) = + Telescope.curry (regime p levels) (Telescope.interpret constants levels env domains) + (fun xs => interp constants levels (Telescope.extend env xs) body) := by + induction domains generalizing env with + | nil => rfl + | cons A rest ih => + simp only [lamN, interp, Telescope.interpret, Telescope.curry, Telescope.extend, ih] + +theorem interp_appN (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (f : AExpr β) (args : List (AExpr β)) : + interp constants levels env (appN f args) = + Telescope.applyN (interp constants levels env f) (args.map (interp constants levels env)) := by + induction args generalizing f <;> simp_all [appN, interp, Telescope.applyN] + +end Ix.Theory.Model.AExpr + +namespace Ix.Theory.Model.Telescope + +open Certified +universe u v +variable {β : Type u} + +/-- Formed domains give formation of the entire product telescope. Every +outer binder has the zero condition of the final result sort. -/ +theorem Formed.forallN {entries : Environment β} {Γ : Context β} {domains : List (AExpr β)} + (h : Formed.{u,v} entries Γ domains) {B : AExpr β} {b : VLevel} + (hB : TypingClaim.{u,v} entries (context Γ domains) B (.sort b)) : + ∃ l, zeroCondition l = zeroCondition b ∧ + TypingClaim.{u,v} entries Γ (.forallN (zeroCondition b) domains B) (.sort l) := by + induction h with + | nil => exact ⟨b, rfl, hB⟩ + | cons a hA hrest ih => + obtain ⟨l, hl, htail⟩ := ih hB + exact ⟨.imax a l, hl, TypingClaim.forallE hA htail hl.symm⟩ + +end Ix.Theory.Model.Telescope diff --git a/Ix/Theory/Model/Instantiation.lean b/Ix/Theory/Model/Instantiation.lean new file mode 100644 index 000000000..dcaef4529 --- /dev/null +++ b/Ix/Theory/Model/Instantiation.lean @@ -0,0 +1,96 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Inductive.Telescope + +namespace Ix.Theory.Model + +open SetTheory + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +omit [SetTheory V] in +theorem Valuation.insert_extend_at (env : Nat → V) (xs : List V) (a : V) (k : Nat) : + Valuation.insert (xs.length + k) a (Telescope.extend env xs) = + Telescope.extend (Valuation.insert k a env) xs := by + induction xs generalizing env k with + | nil => simp only [List.length_nil, Nat.zero_add, Telescope.extend] + | cons x xs ih => + simpa only [List.length_cons, Telescope.extend, Nat.add_assoc, Nat.add_comm, + Nat.add_left_comm, Valuation.insert_cons] using ih (Valuation.cons x env) (k + 1) + +omit [SetTheory V] in +theorem Valuation.insert_extend (env : Nat → V) (xs : List V) (a : V) : + Valuation.insert xs.length a (Telescope.extend env xs) = + Telescope.extend (Valuation.cons a env) xs := by + simpa only [Nat.add_zero, Valuation.insert_zero] using Valuation.insert_extend_at env xs a 0 + +namespace AExpr + +/-- Simultaneous substitution of an outermost-first field list. -/ +def instRev : AExpr β → List (AExpr β) → AExpr β + | e, [] => e + | e, a :: rest => instRev (e.inst a rest.length) rest + +theorem erase_instRev (e : AExpr β) (args : List (AExpr β)) : + (e.instRev args).erase = e.erase.instRev (args.map AExpr.erase) := by + induction args generalizing e <;> simp_all [instRev, VExpr.instRev] + +theorem interp_instRev (e : AExpr β) (args : List (AExpr β)) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) : + interp constants levels env (e.instRev args) = + interp constants levels (Telescope.extend env (args.map (interp constants levels env))) e := by + induction args generalizing e with + | nil => rfl + | cons a rest ih => + rw [instRev, ih, interp_inst] + have hlen : (rest.map (interp constants levels env)).length = rest.length := List.length_map .. + rw [← hlen, Telescope.skip_extend, Valuation.insert_extend] + rfl + +theorem wellDenoted_instRev (e : AExpr β) (args : List (AExpr β)) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) + (ha : ∀ a ∈ args, WellDenoted constants levels env a) + (he : WellDenoted constants levels + (Telescope.extend env (args.map (interp constants levels env))) e) : + WellDenoted constants levels env (e.instRev args) := by + induction args generalizing e with + | nil => exact he + | cons a rest ih => + apply ih (e.inst a rest.length) (fun b hb => ha b (List.mem_cons_of_mem _ hb)) + have hlen : (rest.map (interp constants levels env)).length = rest.length := List.length_map .. + apply wellDenoted_inst + · simpa only [← hlen, Telescope.skip_extend] using ha a List.mem_cons_self + · simpa only [← hlen, Telescope.skip_extend, Valuation.insert_extend, List.map_cons, Telescope.extend] using he + +end AExpr + +theorem wellDenoted_congr_env (e : AExpr β) (constants : Assignment β V) + (levels : List Nat) {n k : Nat} (hs : e.Scope n k) (env env' : Nat → V) + (h : ∀ i, i < k → env i = env' i) : + WellDenoted constants levels env e ↔ WellDenoted constants levels env' e := by + induction e generalizing k env env' with + | app f a hf ha => + simp only [WellDenoted, hf hs.1 env env' h, ha hs.2 env env' h, + interp_congr_env f constants levels hs.1 env env' h, + interp_congr_env a constants levels hs.2 env env' h] + | lam p A b hA hb | forallE p A b hA hb => + have hcons (x : V) : ∀ i, i < k + 1 → Valuation.cons x env i = Valuation.cons x env' i := by + intro i hi + cases i with + | zero => rfl + | succ i => exact h i (by omega) + simp only [WellDenoted, hA hs.2.1 env env' h, interp_congr_env A constants levels hs.2.1 env env' h, + hb hs.2.2 _ _ (hcons _), interp_congr_env b constants levels hs.2.2 _ _ (hcons _)] + | proj _ _ e ih => exact ih hs env env' h + | _ => rfl + +theorem wellDenoted_closed (e : AExpr β) (constants : Assignment β V) + (levels : List Nat) {n : Nat} (hs : e.Scope n 0) (env env' : Nat → V) : + WellDenoted constants levels env e ↔ WellDenoted constants levels env' e := + wellDenoted_congr_env e constants levels hs env env' (fun _ h => (Nat.not_lt_zero _ h).elim) + +end Ix.Theory.Model diff --git a/Ix/Theory/Model/Interpret.lean b/Ix/Theory/Model/Interpret.lean new file mode 100644 index 000000000..90b71cba6 --- /dev/null +++ b/Ix/Theory/Model/Interpret.lean @@ -0,0 +1,150 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Annotated +import Ix.Theory.Model.PrimitiveValues +import Ix.Theory.Model.Value + +/-! +# Total interpretation of annotated Ix syntax + +Constants are interpreted through an explicit assignment, whose compatibility +with checked declarations is a separate invariant. This definition does not +traverse store references. Projections use the uniform ordinary-node field +decoder, and literals use fixed numeral values. Typing either requires +additional facts whose meanings are constructed during declaration admission. +-/ + +namespace Ix.Theory.Model + +open SetTheory SetModel + +universe u v w + +namespace Valuation + +def cons (x : V) (env : Nat → V) : Nat → V + | 0 => x + | i + 1 => env i + +@[simp] theorem cons_zero (x : V) (env : Nat → V) : cons x env 0 = x := rfl +@[simp] theorem cons_succ (x : V) (env : Nat → V) (i : Nat) : cons x env (i + 1) = env i := rfl + +def skip (count cutoff : Nat) (env : Nat → V) : Nat → V := + fun i => if i < cutoff then env i else env (count + i) + +def insert (cutoff : Nat) (x : V) (env : Nat → V) : Nat → V := + fun i => if i < cutoff then env i else if i = cutoff then x else env (i - 1) + +@[simp] theorem skip_zero (env : Nat → V) : skip 0 0 env = env := by + funext i; simp [skip] + +@[simp] theorem skip_cons (n k : Nat) (x : V) (env : Nat → V) : + skip n (k + 1) (cons x env) = cons x (skip n k env) := by + funext i + cases i <;> simp [skip, cons] + +@[simp] theorem insert_cons (k : Nat) (x y : V) (env : Nat → V) : + insert (k + 1) x (cons y env) = cons y (insert k x env) := by + funext i + cases i with + | zero => simp [insert] + | succ i => + by_cases h : i < k + · simp [insert, h] + · by_cases e : i = k + · simp [insert, e] + · have hi : i ≠ 0 := by omega + cases i with + | zero => exact (hi rfl).elim + | succ i => simp [insert, h, e] + +@[simp] theorem insert_zero (x : V) (env : Nat → V) : insert 0 x env = cons x env := by + funext i; cases i <;> simp [insert] + +@[simp] theorem skip_succ_cons (n : Nat) (x : V) (env : Nat → V) : + skip (n + 1) 0 (cons x env) = skip n 0 env := by + funext i + simp [skip, show n + 1 + i = (n + i) + 1 by omega] + +end Valuation + +/-- A value for every exact reference and concrete universe argument list. -/ +abbrev Assignment (β : Type u) (V : Type v) := ConstRef β → List Nat → V + +variable {β : Type u} {V : Type v} [SetTheory V] + +noncomputable def interp (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) : AExpr β → V + | .bvar i => env i + | .sort l => univ (l.eval levels) + | .const r ls => constants r (ls.map (VLevel.eval levels)) + | .app f a => app (interp constants levels env f) (interp constants levels env a) + | .lam p a b => lamR (regime p levels) (interp constants levels env a) + (fun x => interp constants levels (Valuation.cons x env) b) + | .forallE p a b => piR (regime p levels) (interp constants levels env a) + (fun x => interp constants levels (Valuation.cons x env) b) + | .proj _ i e => projectValue i (interp constants levels env e) + | .natLit n => Numeral.value n + +theorem interp_liftN (e : AExpr β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (n k : Nat) : + interp constants levels env (e.liftN n k) = + interp constants levels (Valuation.skip n k env) e := by + induction e generalizing k env with + | bvar i => simp [AExpr.liftN, interp, liftVar, Valuation.skip]; split <;> rfl + | lam p a b ha hb | forallE p a b ha hb => + simp only [AExpr.liftN, interp, ha] + congr 1 + funext x + rw [hb, Valuation.skip_cons] + | _ => simp_all [AExpr.liftN, interp] + +theorem interp_inst (e a : AExpr β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (k : Nat) : + interp constants levels env (e.inst a k) = + interp constants levels + (Valuation.insert k (interp constants levels (Valuation.skip k 0 env) a) env) e := by + induction e generalizing k env with + | bvar i => + by_cases h : i < k + · simp [AExpr.inst, AExpr.instVar, interp, Valuation.insert, h] + · by_cases h' : i = k <;> + simp [AExpr.inst, AExpr.instVar, interp, Valuation.insert, h, h', interp_liftN] + | lam p A b hA hb | forallE p A b hA hb => + simp only [AExpr.inst, interp, hA] + congr 1 + funext x + rw [hb, Valuation.skip_succ_cons, Valuation.insert_cons] + | _ => simp_all [AExpr.inst, interp] + +theorem interp_instL (e : AExpr β) (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) (ls : List VLevel) : + interp constants levels env (e.instL ls) = + interp constants (ls.map (VLevel.eval levels)) env e := by + induction e generalizing env with + | const r us => + simp [AExpr.instL, interp, List.map_map, Function.comp_def, VLevel.eval_inst] + | lam p A b hA hb | forallE p A b hA hb => + simp only [AExpr.instL, interp, hA, regime_instCondition] + congr 1 + funext x + exact hb _ + | _ => simp_all [AExpr.instL, interp, VLevel.eval_inst] + +theorem interp_rename {γ : Type w} (e : AExpr β) (mapping : β → γ) + (constants : Assignment β V) (constants' : Assignment γ V) + (h : ∀ r ls, constants' (r.rename mapping) ls = constants r ls) + (levels : List Nat) (env : Nat → V) : + interp constants' levels env (e.rename mapping) = interp constants levels env e := by + induction e generalizing env with + | lam p A b hA hb | forallE p A b hA hb => + simp only [AExpr.rename, interp, hA] + congr 1 + funext x + exact hb _ + | _ => simp_all [AExpr.rename, interp] + +end Ix.Theory.Model diff --git a/Ix/Theory/Model/Judgment.lean b/Ix/Theory/Model/Judgment.lean new file mode 100644 index 000000000..17fe19535 --- /dev/null +++ b/Ix/Theory/Model/Judgment.lean @@ -0,0 +1,280 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Environment + +/-! +# Semantic contracts and their rule producers + +These specifications quantify over every compatible dependency assignment, +universe valuation, and satisfying context. The executable validator uses the +proved rules below to construct these propositions; they are never unchecked +witness fields supplied by its caller. +-/ + +namespace Ix.Theory.Model + +open SetTheory SetModel Certified + +universe u v +variable {β : Type u} + +/-- Membership together with the hereditary invariants for term and type. -/ +def TypingClaim (entries : Environment β) (Γ : Context β) (e A : AExpr β) : Prop := + ∀ (V : Type v) [SetTheory V] (constants : Assignment β V), + Realizes constants entries → ∀ levels env, Γ.Valid constants levels env → + WellDenoted constants levels env e ∧ WellDenoted constants levels env A ∧ + interp constants levels env e ∈ˢ interp constants levels env A + +/-- Equality is a congruence on the total interpretation. Conversion does not +manufacture typing or hereditary validity of either endpoint. -/ +def ConversionClaim (entries : Environment β) (Γ : Context β) (a b : AExpr β) : Prop := + ∀ (V : Type v) [SetTheory V] (constants : Assignment β V), + Realizes constants entries → ∀ levels env, Γ.Valid constants levels env → + interp constants levels env a = interp constants levels env b + +variable {entries : Environment β} {Γ : Context β} + +namespace TypingClaim + +theorem sort (l : VLevel) : TypingClaim.{u,v} entries Γ (.sort l) (.sort (.succ l)) := by + intro V _ constants _ levels env _ + exact ⟨trivial, trivial, univ_mem_univ _⟩ + +theorem bvar {i : Nat} {A : AExpr β} (h : Γ[i]? = some A) : + TypingClaim.{u,v} entries Γ (.bvar i) A := by + intro V _ constants _ levels env hΓ + exact ⟨trivial, hΓ i A h⟩ + +theorem const {r : ConstRef β} {entry : ConstantEntry β} {ls : List VLevel} + (h : entries r = some entry) (hn : ls.length = entry.universes) : + TypingClaim.{u,v} entries Γ (.const r ls) (entry.type.instL ls) := by + intro V _ constants hM levels env _ + have hlen : (ls.map (VLevel.eval levels)).length = entry.universes := by simpa using hn + refine ⟨trivial, (wellDenoted_instL ..).mpr (hM.typeValid r entry h _ hlen env), ?_⟩ + simpa only [interp, interp_instL] using hM.member r entry h _ hlen env + +theorem forallE {A B : AExpr β} {a b : VLevel} {p : PropWhen} + (hA : TypingClaim.{u,v} entries Γ A (.sort a)) + (hB : TypingClaim.{u,v} entries (Γ.push A) B (.sort b)) + (hp : p = zeroCondition b) : + TypingClaim.{u,v} entries Γ (.forallE p A B) (.sort (.imax a b)) := by + intro V _ constants hM levels env hΓ + obtain ⟨hAw, _, hAm⟩ := hA V constants hM levels env hΓ + have hBx x hx := hB V constants hM levels (Valuation.cons x env) (hΓ.push hAw hx) + have hz : regime p levels = 0 ↔ b.eval levels = 0 := by + rw [hp]; exact regime_zeroCondition .. + refine ⟨⟨hAw, fun x hx => (hBx x hx).1, + b.eval levels, hz, fun x hx => (hBx x hx).2.2⟩, trivial, ?_⟩ + exact annotated_pi_mem_univ hp hAm (fun x hx => (hBx x hx).2.2) + +theorem lam {A B body : AExpr β} {a b : VLevel} {p : PropWhen} + (hA : TypingClaim.{u,v} entries Γ A (.sort a)) + (hB : TypingClaim.{u,v} entries (Γ.push A) B (.sort b)) + (hbody : TypingClaim.{u,v} entries (Γ.push A) body B) + (hp : p = zeroCondition b) : + TypingClaim.{u,v} entries Γ (.lam p A body) (.forallE p A B) := by + intro V _ constants hM levels env hΓ + obtain ⟨hAw, _, _⟩ := hA V constants hM levels env hΓ + have hBx x hx := hB V constants hM levels (Valuation.cons x env) (hΓ.push hAw hx) + have hbodyx x hx := hbody V constants hM levels (Valuation.cons x env) (hΓ.push hAw hx) + have hz : regime p levels = 0 ↔ b.eval levels = 0 := by + rw [hp]; exact regime_zeroCondition .. + refine ⟨⟨hAw, fun x hx => (hbodyx x hx).1, b.eval levels, + (fun x => interp constants levels (Valuation.cons x env) B), hz, + fun x hx => ⟨(hbodyx x hx).2.2, (hBx x hx).2.2⟩⟩, ?_, ?_⟩ + · exact (forallE hA hB hp V constants hM levels env hΓ).1 + · exact lamR_mem (fun x hx => (hbodyx x hx).2.2) + +theorem app {f a A B : AExpr β} {p : PropWhen} + (hf : TypingClaim.{u,v} entries Γ f (.forallE p A B)) + (ha : TypingClaim.{u,v} entries Γ a A) : + TypingClaim.{u,v} entries Γ (.app f a) (B.inst a) := by + intro V _ constants hM levels env hΓ + obtain ⟨hfw, hpw, hfm⟩ := hf V constants hM levels env hΓ + obtain ⟨haw, _, ham⟩ := ha V constants hM levels env hΓ + obtain ⟨_, hBw, bv, hz, hBm⟩ := hpw + have hfm' : interp constants levels env f ∈ˢ + piR bv (interp constants levels env A) + (fun x => interp constants levels (Valuation.cons x env) B) := by + simpa only [interp, piR_zero_agree hz (fun _ _ => rfl)] using hfm + refine ⟨⟨hfw, haw, bv, interp constants levels env A, + (fun x => interp constants levels (Valuation.cons x env) B), hfm', ham, hBm⟩, + ?_, ?_⟩ + · apply wellDenoted_inst + · simpa using haw + · simpa using hBw _ ham + · simp only [interp_inst, Valuation.skip_zero, Valuation.insert_zero] + apply app_mem_piR hfm' ham + intro hzero x hx + simpa only [hzero, univ_zero] using hBm x hx + +theorem conv {e A B : AExpr β} {l : VLevel} + (he : TypingClaim.{u,v} entries Γ e A) + (hB : TypingClaim.{u,v} entries Γ B (.sort l)) + (hEq : ConversionClaim.{u,v} entries Γ A B) : + TypingClaim.{u,v} entries Γ e B := by + intro V _ constants hM levels env hΓ + obtain ⟨hew, _, hem⟩ := he V constants hM levels env hΓ + exact ⟨hew, (hB V constants hM levels env hΓ).1, + hEq V constants hM levels env hΓ ▸ hem⟩ + +/-- Closed typed facts come only from an admitted semantic producer. -/ +theorem fact {r : ConstRef β} {entry : ConstantEntry β} {e A : AExpr β} {ls : List VLevel} + (hr : entries r = some entry) (hf : .typed e A ∈ entry.facts) (hn : ls.length = entry.universes) : + TypingClaim.{u,v} entries Γ (e.instL ls) (A.instL ls) := by + intro V _ constants hM levels env _ + have hh := hM.factMeaning r entry hr (.typed e A) hf (ls.map (VLevel.eval levels)) + (by simpa using hn) env + simpa only [ConstantFact.Meaning, wellDenoted_instL, interp_instL] using hh + +theorem natLit {r zero succ : ConstRef β} {entry : ConstantEntry β} + (hr : entries r = some entry) (hf : .natural zero succ ∈ entry.facts) + (hn : entry.universes = 0) (value : Nat) : + TypingClaim.{u,v} entries Γ (.natLit value) (.const r []) := by + intro V _ constants hM _ env _ + have hh := hM.factMeaning r entry hr (.natural zero succ) hf [] (by simpa using hn.symm) env + exact ⟨trivial, trivial, hh.2.member value⟩ + +/-- A typed lambda application gives a typed result of beta reduction, +including the hereditary validity of that result. -/ +theorem betaResult {p : PropWhen} {D body arg B : AExpr β} + (hl : TypingClaim.{u,v} entries Γ (.lam p D body) (.forallE p D B)) + (ha : TypingClaim.{u,v} entries Γ arg D) : + TypingClaim.{u,v} entries Γ (body.inst arg) (B.inst arg) := by + intro V _ constants hM levels env hΓ + have hl' := hl V constants hM levels env hΓ + have ha' := ha V constants hM levels env hΓ + have happ := app hl ha V constants hM levels env hΓ + have hbeta := wellDenoted_beta hl'.1 ha'.1 ha'.2.2 + exact ⟨hbeta.1, happ.2.1, hbeta.2 ▸ happ.2.2⟩ + +end TypingClaim + +namespace ConversionClaim + +theorem natZero {r zero succ : ConstRef β} {entry : ConstantEntry β} + (hr : entries r = some entry) (hf : .natural zero succ ∈ entry.facts) (hn : entry.universes = 0) : + ConversionClaim.{u,v} entries Γ (.natLit 0) (.const zero []) := by + intro V _ constants hM _ env _ + have hh := hM.factMeaning r entry hr (.natural zero succ) hf [] (by simpa using hn.symm) env + exact hh.2.zeroValue.symm + +theorem natSucc {r zero succ : ConstRef β} {entry : ConstantEntry β} + (hr : entries r = some entry) (hf : .natural zero succ ∈ entry.facts) (hn : entry.universes = 0) (value : Nat) : + ConversionClaim.{u,v} entries Γ (.natLit (value + 1)) (.app (.const succ []) (.natLit value)) := by + intro V _ constants hM _ env _ + have hh := hM.factMeaning r entry hr (.natural zero succ) hf [] (by simpa using hn.symm) env + exact (hh.2.succValue value).symm + +theorem refl (e : AExpr β) : ConversionClaim.{u,v} entries Γ e e := by + intro V _ constants hM levels env hΓ + rfl + +theorem symm {a b : AExpr β} (h : ConversionClaim.{u,v} entries Γ a b) : + ConversionClaim.{u,v} entries Γ b a := by + intro V _ constants hM levels env hΓ + exact (h V constants hM levels env hΓ).symm + +theorem trans {a b c : AExpr β} (h : ConversionClaim.{u,v} entries Γ a b) + (h' : ConversionClaim.{u,v} entries Γ b c) : + ConversionClaim.{u,v} entries Γ a c := by + intro V _ constants hM levels env hΓ + exact (h V constants hM levels env hΓ).trans (h' V constants hM levels env hΓ) + +theorem app {f f' a a' : AExpr β} (hf : ConversionClaim.{u,v} entries Γ f f') + (ha : ConversionClaim.{u,v} entries Γ a a') : + ConversionClaim.{u,v} entries Γ (.app f a) (.app f' a') := by + intro V _ constants hM levels env hΓ + simp only [interp, hf V constants hM levels env hΓ, ha V constants hM levels env hΓ] + +theorem proj {r : ConstRef β} {i : Nat} {a b : AExpr β} + (h : ConversionClaim.{u,v} entries Γ a b) : + ConversionClaim.{u,v} entries Γ (.proj r i a) (.proj r i b) := by + intro V _ constants hM levels env hΓ + exact congrArg (projectValue i) (h V constants hM levels env hΓ) + +theorem lam {A A' b b' : AExpr β} {p : PropWhen} {l : VLevel} + (hA : TypingClaim.{u,v} entries Γ A (.sort l)) + (hdom : ConversionClaim.{u,v} entries Γ A A') + (hbody : ConversionClaim.{u,v} entries (Γ.push A) b b') : + ConversionClaim.{u,v} entries Γ (.lam p A b) (.lam p A' b') := by + intro V _ constants hM levels env hΓ + have hAw := (hA V constants hM levels env hΓ).1 + simp only [interp] + rw [← hdom V constants hM levels env hΓ] + apply lamR_congr + intro x hx + exact hbody V constants hM levels _ (hΓ.push hAw hx) + +theorem forallE {A A' B B' : AExpr β} {p : PropWhen} {l : VLevel} + (hA : TypingClaim.{u,v} entries Γ A (.sort l)) + (hdom : ConversionClaim.{u,v} entries Γ A A') + (hbody : ConversionClaim.{u,v} entries (Γ.push A) B B') : + ConversionClaim.{u,v} entries Γ (.forallE p A B) (.forallE p A' B') := by + intro V _ constants hM levels env hΓ + have hAw := (hA V constants hM levels env hΓ).1 + simp only [interp] + rw [← hdom V constants hM levels env hΓ] + apply piR_congr + intro x hx + exact hbody V constants hM levels _ (hΓ.push hAw hx) + +theorem beta {p : PropWhen} {A b a T : AExpr β} + (hl : TypingClaim.{u,v} entries Γ (.lam p A b) T) + (ha : TypingClaim.{u,v} entries Γ a A) : + ConversionClaim.{u,v} entries Γ (.app (.lam p A b) a) (b.inst a) := by + intro V _ constants hM levels env hΓ + have hl' := (hl V constants hM levels env hΓ).1 + obtain ⟨haw, _, ham⟩ := ha V constants hM levels env hΓ + exact (wellDenoted_beta hl' haw ham).2 + +theorem eta {p : PropWhen} {A B f : AExpr β} + (hf : TypingClaim.{u,v} entries Γ f (.forallE p A B)) : + ConversionClaim.{u,v} entries Γ (.lam p A (.app (f.liftN 1) (.bvar 0))) f := by + intro V _ constants hM levels env hΓ + have hfm := (hf V constants hM levels env hΓ).2.2 + simp only [interp, interp_liftN, Valuation.skip_one_cons, Valuation.cons_zero] + exact lamR_eta hfm + +theorem proofIrrel {A a b : AExpr β} + (hA : TypingClaim.{u,v} entries Γ A (.sort .zero)) + (ha : TypingClaim.{u,v} entries Γ a A) + (hb : TypingClaim.{u,v} entries Γ b A) : + ConversionClaim.{u,v} entries Γ a b := by + intro V _ constants hM levels env hΓ + have hAm := (hA V constants hM levels env hΓ).2.2 + have ham := (ha V constants hM levels env hΓ).2.2 + have hbm := (hb V constants hM levels env hΓ).2.2 + exact subsingleton_of_mem_univZero + (by simpa only [interp, VLevel.eval, univ_zero] using hAm) ham hbm + +theorem delta {r : ConstRef β} {entry : ConstantEntry β} {body : AExpr β} + {ls : List VLevel} (h : entries r = some entry) (hb : entry.body = some body) + (hn : ls.length = entry.universes) : + ConversionClaim.{u,v} entries Γ (.const r ls) (body.instL ls) := by + intro V _ constants hM levels env _ + have hlen : (ls.map (VLevel.eval levels)).length = entry.universes := by simpa using hn + simpa only [interp, interp_instL] using hM.bodyValue r entry h body hb _ hlen env + +theorem sort {l l' : VLevel} (h : ∀ values, l.eval values = l'.eval values) : + ConversionClaim.{u,v} entries Γ (.sort l) (.sort l') := by + intro V _ constants hM levels env hΓ + simp only [interp, h] + +/-- Only an equation of the selected admitted entry may be instantiated. +Typing of its applications remains a separate checker obligation. -/ +theorem equation {r : ConstRef β} {entry : ConstantEntry β} + {law : ConstantEquation β} {ls : List VLevel} + (h : entries r = some entry) (he : law ∈ entry.equations) + (hn : ls.length = entry.universes) : + ConversionClaim.{u,v} entries Γ (law.lhs.instL ls) (law.rhs.instL ls) := by + intro V _ constants hM levels env _ + have hlen : (ls.map (VLevel.eval levels)).length = entry.universes := by simpa using hn + simpa only [interp_instL] using hM.equationValue r entry h law he _ hlen env + +end ConversionClaim + +end Ix.Theory.Model diff --git a/Ix/Theory/Model/PrimitiveValues.lean b/Ix/Theory/Model/PrimitiveValues.lean new file mode 100644 index 000000000..802f66b5f --- /dev/null +++ b/Ix/Theory/Model/PrimitiveValues.lean @@ -0,0 +1,38 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.SetModel.TaggedSum + +namespace Ix.Theory.Model + +open SetTheory SetTheory.Tower + +universe u +variable {V : Type u} [SetTheory V] + +/-- The ordinary container stores a tagged tuple as the first component of +its node. These total destructors also fix the proof point. Ownership and +field legality are obligations of the certified projection rule. -/ +noncomputable def projectValue (field : Nat) (value : V) : V := + projS field (ssnd (sfst value)) + +theorem projectValue_pt (field : Nat) : projectValue field (pt : V) = pt := by + simp only [projectValue, sfst_pt, ssnd_pt, projS_pt] + +theorem projectValue_node (field tag : Nat) (fields : List V) (branches : V) : + projectValue field (spair (inj tag (mkTower fields)) branches) = projS field (mkTower fields) := by + simp only [projectValue, sfst_spair, ssnd_inj] + +namespace Numeral + +noncomputable def zero : V := spair (inj 0 pt) empty +noncomputable def succ (n : V) : V := spair (inj 1 pt) (graph (fun _ => n) (sing (inj 0 pt))) +noncomputable def value : Nat → V + | 0 => zero + | n + 1 => succ (value n) + +end Numeral + +end Ix.Theory.Model diff --git a/Ix/Theory/Model/ReferenceMap.lean b/Ix/Theory/Model/ReferenceMap.lean new file mode 100644 index 000000000..3e345d050 --- /dev/null +++ b/Ix/Theory/Model/ReferenceMap.lean @@ -0,0 +1,168 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Support + +/-! Whole-reference substitution for independently checked model companions. +A constructor may map to a definition, so this operation is more general +than renaming a block address while retaining its member/constructor tag. -/ + +namespace Ix.Theory + +universe u v w x +variable {β : Type u} {γ : Type v} {δ : Type w} + +def VExpr.mapRefs (mapping : ConstRef β → ConstRef γ) : VExpr β → VExpr γ + | .bvar i => .bvar i + | .sort level => .sort level + | .const ref levels => .const (mapping ref) levels + | .app f a => .app (f.mapRefs mapping) (a.mapRefs mapping) + | .lam type body => .lam (type.mapRefs mapping) (body.mapRefs mapping) + | .forallE type body => .forallE (type.mapRefs mapping) (body.mapRefs mapping) + | .proj ref index major => .proj (mapping ref) index (major.mapRefs mapping) + | .natLit n => .natLit n + +namespace Model + +def AExpr.mapRefs (mapping : ConstRef β → ConstRef γ) : AExpr β → AExpr γ + | .bvar i => .bvar i + | .sort level => .sort level + | .const ref levels => .const (mapping ref) levels + | .app f a => .app (f.mapRefs mapping) (a.mapRefs mapping) + | .lam condition type body => .lam condition (type.mapRefs mapping) (body.mapRefs mapping) + | .forallE condition type body => .forallE condition (type.mapRefs mapping) (body.mapRefs mapping) + | .proj ref index major => .proj (mapping ref) index (major.mapRefs mapping) + | .natLit n => .natLit n + +@[simp] theorem AExpr.erase_mapRefs (e : AExpr β) (mapping : ConstRef β → ConstRef γ) : + (e.mapRefs mapping).erase = e.erase.mapRefs mapping := by + induction e <;> simp_all [AExpr.mapRefs, AExpr.erase, VExpr.mapRefs] + +@[simp] theorem AExpr.scope_mapRefs (e : AExpr β) (mapping : ConstRef β → ConstRef γ) (n k : Nat) : + (e.mapRefs mapping).Scope n k ↔ e.Scope n k := by + induction e generalizing k <;> simp_all [AExpr.mapRefs, AExpr.Scope] + +@[simp] theorem AExpr.references_mapRefs (e : AExpr β) (mapping : ConstRef β → ConstRef γ) : + (e.mapRefs mapping).references = e.references.map mapping := by + induction e <;> simp_all [AExpr.mapRefs, AExpr.references] + +@[simp] theorem AExpr.mapRefs_liftN (e : AExpr β) (mapping : ConstRef β → ConstRef γ) (n k : Nat) : + (e.liftN n k).mapRefs mapping = (e.mapRefs mapping).liftN n k := by + induction e generalizing k <;> simp_all [AExpr.mapRefs, AExpr.liftN] + +@[simp] theorem AExpr.mapRefs_inst (e a : AExpr β) (mapping : ConstRef β → ConstRef γ) (k : Nat) : + (e.inst a k).mapRefs mapping = (e.mapRefs mapping).inst (a.mapRefs mapping) k := by + induction e generalizing k with + | bvar i => + by_cases hi : i < k + · simp [AExpr.inst, AExpr.instVar, AExpr.mapRefs, hi] + · by_cases he : i = k <;> simp [AExpr.inst, AExpr.instVar, AExpr.mapRefs, hi, he] + | _ => simp_all [AExpr.inst, AExpr.mapRefs] + +@[simp] theorem AExpr.mapRefs_instL (e : AExpr β) (mapping : ConstRef β → ConstRef γ) (levels : List VLevel) : + (e.instL levels).mapRefs mapping = (e.mapRefs mapping).instL levels := by + induction e <;> simp_all [AExpr.instL, AExpr.mapRefs] + +@[simp] theorem AExpr.mapRefs_id (e : AExpr β) : e.mapRefs id = e := by + induction e <;> simp_all [AExpr.mapRefs] + +theorem AExpr.mapRefs_comp (e : AExpr β) (first : ConstRef β → ConstRef γ) (second : ConstRef γ → ConstRef δ) : + (e.mapRefs first).mapRefs second = e.mapRefs (second ∘ first) := by + induction e <;> simp_all [AExpr.mapRefs, Function.comp_def] + +theorem AExpr.mapRefs_congr (e : AExpr β) (first second : ConstRef β → ConstRef γ) + (h : ∀ ref ∈ e.references, first ref = second ref) : e.mapRefs first = e.mapRefs second := by + induction e with + | const ref levels => simp [AExpr.mapRefs, h ref (by simp [AExpr.references])] + | app f a hf ha | lam condition f a hf ha | forallE condition f a hf ha => + simp only [AExpr.mapRefs, + hf (fun ref hr => h ref (List.mem_append_left _ hr)), + ha (fun ref hr => h ref (List.mem_append_right _ hr))] + | proj ref index major ih => + simp only [AExpr.mapRefs, h ref (List.mem_cons_self), ih (fun ref hr => h ref (List.mem_cons_of_mem _ hr))] + | _ => rfl + +/-- Restoration needs an inverse only on the references actually used by +the restored expression. Unrelated auxiliaries are outside the premise. -/ +theorem AExpr.mapRefs_restore (e : AExpr β) (forward : ConstRef β → ConstRef γ) + (backward : ConstRef γ → ConstRef β) + (h : ∀ ref ∈ e.references, backward (forward ref) = ref) : + (e.mapRefs forward).mapRefs backward = e := by + rw [AExpr.mapRefs_comp, e.mapRefs_congr (backward ∘ forward) id h, AExpr.mapRefs_id] + +variable {V : Type x} [SetTheory V] + +theorem interp_mapRefs (e : AExpr β) (mapping : ConstRef β → ConstRef γ) + (constants : Assignment γ V) (levels : List Nat) (env : Nat → V) : + interp constants levels env (e.mapRefs mapping) = + interp (fun ref values => constants (mapping ref) values) levels env e := by + induction e generalizing env with + | lam condition type body ht hb | forallE condition type body ht hb => + simp only [AExpr.mapRefs, interp, ht] + congr 1 + funext value + exact hb _ + | _ => simp_all [AExpr.mapRefs, interp] + +theorem wellDenoted_mapRefs (e : AExpr β) (mapping : ConstRef β → ConstRef γ) + (constants : Assignment γ V) (levels : List Nat) (env : Nat → V) : + WellDenoted constants levels env (e.mapRefs mapping) ↔ + WellDenoted (fun ref values => constants (mapping ref) values) levels env e := by + induction e generalizing env with + | lam condition type body ht hb | forallE condition type body ht hb => + simp only [AExpr.mapRefs, WellDenoted, ht, hb, interp_mapRefs] + | _ => simp_all [AExpr.mapRefs, WellDenoted, interp_mapRefs] + +/-- A permutation changes addresses and pulls the assignment back through its +inverse. Only references actually used by the expression need an inverse. -/ +theorem interp_mapRefs_permutation (e : AExpr β) (forward : ConstRef β → ConstRef γ) + (backward : ConstRef γ → ConstRef β) (constants : Assignment β V) + (inverse : ∀ ref ∈ e.references, backward (forward ref) = ref) + (levels : List Nat) (env : Nat → V) : + interp (fun ref values => constants (backward ref) values) levels env (e.mapRefs forward) = + interp constants levels env e := by + rw [interp_mapRefs] + exact interp_congr_constants e _ constants + (fun ref hr values => congrArg (fun r => constants r values) (inverse ref hr)) levels env + +theorem wellDenoted_mapRefs_permutation (e : AExpr β) (forward : ConstRef β → ConstRef γ) + (backward : ConstRef γ → ConstRef β) (constants : Assignment β V) + (inverse : ∀ ref ∈ e.references, backward (forward ref) = ref) + (levels : List Nat) (env : Nat → V) : + WellDenoted (fun ref values => constants (backward ref) values) levels env (e.mapRefs forward) ↔ + WellDenoted constants levels env e := by + rw [wellDenoted_mapRefs] + exact wellDenoted_congr_constants e _ constants + (fun ref hr values => congrArg (fun r => constants r values) (inverse ref hr)) levels env + +/-- Restoring the retained reference support preserves the entire interpreted +expression, including binder annotations and both sides of a rule. -/ +theorem interp_mapRefs_restore (e : AExpr β) (forward : ConstRef β → ConstRef γ) + (backward : ConstRef γ → ConstRef β) (constants : Assignment β V) + (inverse : ∀ ref ∈ e.references, backward (forward ref) = ref) + (levels : List Nat) (env : Nat → V) : + interp constants levels env ((e.mapRefs forward).mapRefs backward) = interp constants levels env e := by + rw [e.mapRefs_restore forward backward inverse] + +/-- Equivalent model auxiliaries may share a representative. No injectivity +is needed: the checked consumer must establish agreement of their values. -/ +theorem interp_mapRefs_merge (e : AExpr β) (before : Assignment β V) (after : Assignment γ V) + (mapping : ConstRef β → ConstRef γ) + (agree : ∀ ref ∈ e.references, ∀ levels, before ref levels = after (mapping ref) levels) + (levels : List Nat) (env : Nat → V) : + interp before levels env e = interp after levels env (e.mapRefs mapping) := by + rw [interp_mapRefs] + exact interp_congr_constants e before _ agree levels env + +theorem wellDenoted_mapRefs_merge (e : AExpr β) (before : Assignment β V) (after : Assignment γ V) + (mapping : ConstRef β → ConstRef γ) + (agree : ∀ ref ∈ e.references, ∀ levels, before ref levels = after (mapping ref) levels) + (levels : List Nat) (env : Nat → V) : + WellDenoted before levels env e ↔ WellDenoted after levels env (e.mapRefs mapping) := by + rw [wellDenoted_mapRefs] + exact wellDenoted_congr_constants e before _ agree levels env + +end Model +end Ix.Theory diff --git a/Ix/Theory/Model/SetModel/Container.lean b/Ix/Theory/Model/SetModel/Container.lean new file mode 100644 index 000000000..de3a279a5 --- /dev/null +++ b/Ix/Theory/Model/SetModel/Container.lean @@ -0,0 +1,665 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetModel/Container.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetModel.RecGraph +public import Ix.Theory.Model.SetModel.Iter +public import Ix.Theory.Model.SetTheory.Derive.Choice + +@[expose] public section + +/-! +# The closure witness of a member container (task #202, Stage B) + +A recursive family with function-space slots (`WType`, `PSet`, …) is the +least fixed point of a functor whose fibres contain *functions* from +member domains into the family's own fibres. Its least pre-fixed point +(`lfpFamSet`) needs a CLOSED MEMBER family to be intersected from; for +finitary blocks that is the ω-iterate, for `Prop`-valued ones the top +family — with a function-space slot at `Sort w`, `w ≠ 0`, neither works +(the family's ranks are unbounded below the universe). + +This module exhibits the witness abstractly, for a functor `Φ` on +families over an index set `I` PRESENTED AS A CONTAINER: every element +of `Φ X` at `i` is `mk a g` for a shape `a ∈ A i` and a function `g` +from the shape's positions `B a` into the fibres of `X` at the targets +`tgt a p` (`helim`); shapes and position sets are members of `univ w`. +The witness is the family of DECODED TREE CODES: a code is a set of +labelled paths (a subset of a member `codeSpace t` built from the shapes +and positions reachable from `t`), decoding is the recursion theorem +(`RecGraph`) along the immediate-subcode relation on the accessible +codes, and the decoded family is closed because a constructor step on +decoded subcodes is the decoding of the assembled code (`mkCode`). +Membership comes from replacement (`image_mem`) alone — the fibres are +images of members — so no injection and no size argument beyond the +universe's own closure is needed. Everything here is over the bare +`SetTheory` interface; no syntax. + +The index set `I` need not be a member of any universe (an index type +may live above the family's sort): the reachable shapes are collected +by depth (`shapesN`) through the shapes and positions only, which ARE +members; no index is ever put inside a set. +-/ + +namespace Ix.Theory.Model.SetTheory + +open Ix.Theory.Model.SetTheory.Tower (natUnion mem_natUnion natUnion_mem_univ_pos) + +universe u + +variable {V : Type u} [SetTheory V] + +/-! ## Pair projections on `kpair` -/ + + +/-! ## The spaces reachable from an index -/ + +section Spaces + +variable (A B : V → V) (tgt : V → V → V) + +/-- The shapes reachable from `t` in exactly `n` steps. -/ +noncomputable def shapesN (t : V) : Nat → V + | 0 => A t + | n + 1 => sUnion (image (fun a => sUnion (image (fun p => A (tgt a p)) (B a))) (shapesN t n)) + +/-- The shapes reachable from `t`. -/ +noncomputable def shapes (t : V) : V := natUnion (shapesN A B tgt t) + +/-- The positions of the reachable shapes. -/ +noncomputable def positions (t : V) : V := sUnion (image B (shapes A B tgt t)) + +/-- Paths of length `n`: nested pairs of positions, the first step +outermost, `pt` the empty path. -/ +noncomputable def pathsN (t : V) : Nat → V + | 0 => unitSet + | n + 1 => sigmaPairs (positions A B tgt t) (fun _ => pathsN t n) + +/-- The paths from `t`. -/ +noncomputable def paths (t : V) : V := natUnion (pathsN A B tgt t) + +/-- The code space at `t`: the sets of labelled paths. -/ +noncomputable def codeSpace (t : V) : V := + power (sigmaPairs (paths A B tgt t) (fun _ => shapes A B tgt t)) + +variable {A B tgt} + +theorem mem_shapesN_succ {t x : V} {n : Nat} : + x ∈ˢ shapesN A B tgt t (n + 1) ↔ + ∃ a, a ∈ˢ shapesN A B tgt t n ∧ ∃ p, p ∈ˢ B a ∧ x ∈ˢ A (tgt a p) := by + show x ∈ˢ sUnion (image _ _) ↔ _ + rw [mem_sUnion] + constructor + · rintro ⟨y, hy, hxy⟩ + obtain ⟨a, ha, rfl⟩ := mem_image.mp hy + obtain ⟨z, hz, hxz⟩ := mem_sUnion.mp hxy + obtain ⟨p, hp, rfl⟩ := mem_image.mp hz + exact ⟨a, ha, p, hp, hxz⟩ + · rintro ⟨a, ha, p, hp, hx⟩ + exact ⟨_, mem_image.mpr ⟨a, ha, rfl⟩, mem_sUnion.mpr ⟨_, mem_image.mpr ⟨p, hp, rfl⟩, hx⟩⟩ + +theorem mem_shapes {t x : V} : x ∈ˢ shapes A B tgt t ↔ ∃ n, x ∈ˢ shapesN A B tgt t n := + mem_natUnion + +theorem mem_positions {t p : V} : + p ∈ˢ positions A B tgt t ↔ ∃ a, a ∈ˢ shapes A B tgt t ∧ p ∈ˢ B a := by + show p ∈ˢ sUnion (image B _) ↔ _ + rw [mem_sUnion] + constructor + · rintro ⟨y, hy, hpy⟩ + obtain ⟨a, ha, rfl⟩ := mem_image.mp hy + exact ⟨a, ha, hpy⟩ + · rintro ⟨a, ha, hp⟩ + exact ⟨B a, mem_image.mpr ⟨a, ha, rfl⟩, hp⟩ + +theorem mem_paths {t x : V} : x ∈ˢ paths A B tgt t ↔ ∃ n, x ∈ˢ pathsN A B tgt t n := + mem_natUnion + +theorem pt_mem_paths (t : V) : (pt : V) ∈ˢ paths A B tgt t := + mem_paths.mpr ⟨0, pt_mem_unitSet⟩ + +theorem mem_pathsN_succ {t x : V} {n : Nat} : + x ∈ˢ pathsN A B tgt t (n + 1) ↔ + ∃ p, p ∈ˢ positions A B tgt t ∧ ∃ q, q ∈ˢ pathsN A B tgt t n ∧ x = kpair p q := + mem_sigmaPairs + +/-- Paths are closed under consing a position. -/ +theorem kpair_mem_paths {t p q : V} (hp : p ∈ˢ positions A B tgt t) (hq : q ∈ˢ paths A B tgt t) : + kpair p q ∈ˢ paths A B tgt t := by + obtain ⟨n, hn⟩ := mem_paths.mp hq + exact mem_paths.mpr ⟨n + 1, mem_pathsN_succ.mpr ⟨p, hp, q, hn, rfl⟩⟩ + +/-- A path's tail is a path (and its head a position). -/ +theorem paths_uncons {t p q : V} (h : kpair p q ∈ˢ paths A B tgt t) : + p ∈ˢ positions A B tgt t ∧ q ∈ˢ paths A B tgt t := by + obtain ⟨n, hn⟩ := mem_paths.mp h + cases n with + | zero => + exact absurd (mem_unitSet_iff.mp hn).symm (pt_ne_kpair p q) + | succ n => + obtain ⟨p', hp', q', hq', heq⟩ := mem_pathsN_succ.mp hn + obtain ⟨rfl, rfl⟩ := kpair_inj heq + exact ⟨hp', mem_paths.mpr ⟨n, hq'⟩⟩ + +theorem mem_codeSpace {t S : V} : + S ∈ˢ codeSpace A B tgt t ↔ + ∀ x, x ∈ˢ S → ∃ q, q ∈ˢ paths A B tgt t ∧ ∃ a, a ∈ˢ shapes A B tgt t ∧ x = kpair q a := by + show S ∈ˢ power _ ↔ _ + rw [mem_power] + constructor + · intro h x hx + exact mem_sigmaPairs.mp (h x hx) + · intro h x hx + obtain ⟨q, hq, a, ha, rfl⟩ := h x hx + exact mem_sigmaPairs.mpr ⟨q, hq, a, ha, rfl⟩ + +/-! ### Membership in the universe -/ + +section Univ + +variable {w : Nat} (hw : w ≠ 0) {I : V} + (hA : ∀ i, i ∈ˢ I → A i ∈ˢ (univ w : V)) + (hB : ∀ i a, i ∈ˢ I → a ∈ˢ A i → B a ∈ˢ (univ w : V)) + (htgt : ∀ i a p, i ∈ˢ I → a ∈ˢ A i → p ∈ˢ B a → tgt a p ∈ˢ I) +include hw hA hB htgt + +omit hw hA hB in +/-- Every reachable shape is a shape at some index. -/ +theorem shapesN_sub {t : V} (ht : t ∈ˢ I) : + ∀ n a, a ∈ˢ shapesN A B tgt t n → ∃ i, i ∈ˢ I ∧ a ∈ˢ A i + | 0, a, ha => ⟨t, ht, ha⟩ + | n + 1, a, ha => by + obtain ⟨a', ha', p, hp, hx⟩ := mem_shapesN_succ.mp ha + obtain ⟨i, hi, hai⟩ := shapesN_sub ht n a' ha' + exact ⟨tgt a' p, htgt i a' p hi hai hp, hx⟩ + +theorem shapesN_mem {t : V} (ht : t ∈ˢ I) : ∀ n, shapesN A B tgt t n ∈ˢ (univ w : V) + | 0 => hA t ht + | n + 1 => by + have hU := univ_isTGUniverse (V := V) hw + show sUnion (image _ _) ∈ˢ _ + refine hU.famUnion_mem (shapesN_mem ht n) fun a ha => ?_ + obtain ⟨i, hi, hai⟩ := shapesN_sub htgt ht n a ha + refine hU.famUnion_mem (hB i a hi hai) fun p hp => ?_ + exact hA _ (htgt i a p hi hai hp) + +theorem shapes_mem {t : V} (ht : t ∈ˢ I) : shapes A B tgt t ∈ˢ (univ w : V) := + natUnion_mem_univ_pos hw (shapesN_mem hw hA hB htgt ht) + +omit hw hA hB in +theorem shapes_sub {t a : V} (ht : t ∈ˢ I) (ha : a ∈ˢ shapes A B tgt t) : + ∃ i, i ∈ˢ I ∧ a ∈ˢ A i := by + obtain ⟨n, hn⟩ := mem_shapes.mp ha + exact shapesN_sub htgt ht n a hn + +theorem positions_mem {t : V} (ht : t ∈ˢ I) : positions A B tgt t ∈ˢ (univ w : V) := by + have hU := univ_isTGUniverse (V := V) hw + refine hU.famUnion_mem (shapes_mem hw hA hB htgt ht) fun a ha => ?_ + obtain ⟨i, hi, hai⟩ := shapes_sub htgt ht ha + exact hB i a hi hai + +theorem pathsN_mem {t : V} (ht : t ∈ˢ I) : ∀ n, pathsN A B tgt t n ∈ˢ (univ w : V) + | 0 => unitSet_mem_univ w + | n + 1 => (univ_isTGUniverse hw).sigmaPairs_mem (positions_mem hw hA hB htgt ht) + fun _ _ => pathsN_mem ht n + +theorem paths_mem {t : V} (ht : t ∈ˢ I) : paths A B tgt t ∈ˢ (univ w : V) := + natUnion_mem_univ_pos hw (pathsN_mem hw hA hB htgt ht) + +theorem codeSpace_mem {t : V} (ht : t ∈ˢ I) : codeSpace A B tgt t ∈ˢ (univ w : V) := by + have hU := univ_isTGUniverse (V := V) hw + exact hU.power_mem (hU.sigmaPairs_mem (paths_mem hw hA hB htgt ht) + fun _ _ => shapes_mem hw hA hB htgt ht) + +end Univ + +/-! ### The spaces grow along a step -/ + +section Step + +variable {t a p : V} (ha : a ∈ˢ A t) (hp : p ∈ˢ B a) +include ha hp + +theorem shapesN_step_sub : ∀ n, shapesN A B tgt (tgt a p) n ⊆ˢ shapesN A B tgt t (n + 1) + | 0 => fun x hx => mem_shapesN_succ.mpr ⟨a, ha, p, hp, hx⟩ + | n + 1 => fun x hx => by + obtain ⟨a', ha', p', hp', hx'⟩ := mem_shapesN_succ.mp hx + exact mem_shapesN_succ.mpr ⟨a', shapesN_step_sub n a' ha', p', hp', hx'⟩ + +theorem shapes_step_sub : shapes A B tgt (tgt a p) ⊆ˢ shapes A B tgt t := by + intro x hx + obtain ⟨n, hn⟩ := mem_shapes.mp hx + exact mem_shapes.mpr ⟨n + 1, shapesN_step_sub ha hp n x hn⟩ + +theorem positions_step_sub : positions A B tgt (tgt a p) ⊆ˢ positions A B tgt t := by + intro x hx + obtain ⟨a', ha', hx'⟩ := mem_positions.mp hx + exact mem_positions.mpr ⟨a', shapes_step_sub ha hp a' ha', hx'⟩ + +theorem pathsN_step_sub : ∀ n, pathsN A B tgt (tgt a p) n ⊆ˢ pathsN A B tgt t n + | 0 => Subset.refl _ + | n + 1 => fun x hx => by + obtain ⟨p', hp', q, hq, rfl⟩ := mem_pathsN_succ.mp hx + exact mem_pathsN_succ.mpr ⟨p', positions_step_sub ha hp p' hp', q, pathsN_step_sub n q hq, rfl⟩ + +theorem paths_step_sub : paths A B tgt (tgt a p) ⊆ˢ paths A B tgt t := by + intro x hx + obtain ⟨n, hn⟩ := mem_paths.mp hx + exact mem_paths.mpr ⟨n, pathsN_step_sub ha hp n x hn⟩ + +/-- A position of a root shape is a position of the root's space. -/ +theorem mem_positions_of_root : p ∈ˢ positions A B tgt t := + mem_positions.mpr ⟨a, mem_shapes.mpr ⟨0, ha⟩, hp⟩ + +end Step + +end Spaces + +/-! ## Codes: the root label, the subcodes, the assembly -/ + +section Codes + +variable (B : V → V) + +/-- The labels at the empty path. -/ +noncomputable def rootLabels (S : V) : V := image ssnd (sep S fun pr => sfst pr = pt) + +/-- A code has a root: some label sits at the empty path. -/ +def HasRoot (S : V) : Prop := ∃ a, kpair pt a ∈ˢ S + +/-- The root label (a choice among the labels at the empty path). -/ +noncomputable def lab (S : V) : V := schoice (rootLabels S) + +/-- The subcode at position `p`: the labelled paths under `p`, the +first step stripped. -/ +noncomputable def subCode (S p : V) : V := + image (fun pr => kpair (ssnd (sfst pr)) (ssnd pr)) (sep S fun pr => ∃ q, sfst pr = kpair p q) + +/-- The code of a tree with root shape `a` and subcodes `app g p` at +the positions `p ∈ B a`: the root pair and every subcode's pairs with +the position prepended. -/ +noncomputable def mkCode (a g : V) : V := + binUnion (sing (kpair pt a)) + (sUnion (image (fun p => image (fun pr => kpair (kpair p (sfst pr)) (ssnd pr)) (app g p)) (B a))) + +variable {B} + +theorem mem_mkCode {a g x : V} : + x ∈ˢ mkCode B a g ↔ + x = kpair pt a ∨ ∃ p, p ∈ˢ B a ∧ ∃ pr, pr ∈ˢ app g p ∧ x = kpair (kpair p (sfst pr)) (ssnd pr) := by + unfold mkCode + rw [mem_binUnion, mem_sing, mem_sUnion] + refine or_congr Iff.rfl ⟨?_, ?_⟩ + · rintro ⟨y, hy, hxy⟩ + obtain ⟨p, hp, rfl⟩ := mem_image.mp hy + obtain ⟨pr, hpr, rfl⟩ := mem_image.mp hxy + exact ⟨p, hp, pr, hpr, rfl⟩ + · rintro ⟨p, hp, pr, hpr, rfl⟩ + exact ⟨_, mem_image.mpr ⟨p, hp, rfl⟩, mem_image.mpr ⟨pr, hpr, rfl⟩⟩ + +theorem mem_rootLabels {S x : V} : + x ∈ˢ rootLabels S ↔ ∃ pr, pr ∈ˢ S ∧ sfst pr = pt ∧ ssnd pr = x := by + unfold rootLabels + rw [mem_image] + constructor + · rintro ⟨pr, hpr, rfl⟩ + exact ⟨pr, (mem_sep.mp hpr).1, (mem_sep.mp hpr).2, rfl⟩ + · rintro ⟨pr, hpr, h1, rfl⟩ + exact ⟨pr, mem_sep.mpr ⟨hpr, h1⟩, rfl⟩ + +theorem hasRoot_mkCode (a g : V) : HasRoot (mkCode B a g) := + ⟨a, mem_mkCode.mpr (Or.inl rfl)⟩ + +theorem rootLabels_mkCode (a g : V) : rootLabels (mkCode B a g) = sing a := by + apply ext + intro x + rw [mem_rootLabels, mem_sing] + constructor + · rintro ⟨pr, hpr, h1, rfl⟩ + rcases mem_mkCode.mp hpr with rfl | ⟨p, -, pr', -, rfl⟩ + · exact ssnd_kpair _ _ + · rw [sfst_kpair] at h1 + exact absurd h1.symm (pt_ne_kpair _ _) + · rintro rfl + exact ⟨kpair pt x, mem_mkCode.mpr (Or.inl rfl), sfst_kpair _ _, ssnd_kpair _ _⟩ + +theorem lab_mkCode (a g : V) : lab (mkCode B a g) = a := by + unfold lab + rw [rootLabels_mkCode] + exact mem_sing.mp (schoice_mem (mem_sing.mpr rfl)) + +/-- The subcode of an assembled code at a position is the subcode put +there (the codes' elements being labelled paths). -/ +theorem subCode_mkCode {a g p : V} (hp : p ∈ˢ B a) + (hpairs : ∀ x, x ∈ˢ app g p → ∃ q b, x = kpair q b) : + subCode (mkCode B a g) p = app g p := by + apply ext + intro x + unfold subCode + rw [mem_image] + constructor + · rintro ⟨pr, hpr, rfl⟩ + obtain ⟨hmem, q, hq⟩ := mem_sep.mp hpr + rcases mem_mkCode.mp hmem with rfl | ⟨p', hp', pr', hpr', rfl⟩ + · rw [sfst_kpair] at hq + exact absurd hq (pt_ne_kpair _ _) + · rw [sfst_kpair] at hq + obtain ⟨rfl, rfl⟩ := kpair_inj hq + obtain ⟨q', b, rfl⟩ := hpairs pr' hpr' + simp only [sfst_kpair, ssnd_kpair] + exact hpr' + · intro hx + obtain ⟨q, b, rfl⟩ := hpairs x hx + refine ⟨kpair (kpair p q) b, mem_sep.mpr ⟨mem_mkCode.mpr (Or.inr ⟨p, hp, kpair q b, hx, ?_⟩), q, sfst_kpair _ _⟩, ?_⟩ + · rw [sfst_kpair, ssnd_kpair] + · rw [sfst_kpair, ssnd_kpair, ssnd_kpair] + +section InSpace + +variable {A : V → V} {tgt : V → V → V} + +theorem codeSpace_pairs {t S : V} (hS : S ∈ˢ codeSpace A B tgt t) : + ∀ x, x ∈ˢ S → ∃ q a, x = kpair q a := by + intro x hx + obtain ⟨q, -, a, -, rfl⟩ := mem_codeSpace.mp hS x hx + exact ⟨q, a, rfl⟩ + +/-- Subcodes stay in the code space. -/ +theorem subCode_mem_codeSpace {t S p : V} (hS : S ∈ˢ codeSpace A B tgt t) : + subCode S p ∈ˢ codeSpace A B tgt t := by + rw [mem_codeSpace] + intro x hx + unfold subCode at hx + obtain ⟨pr, hpr, rfl⟩ := mem_image.mp hx + obtain ⟨hmem, q, hq⟩ := mem_sep.mp hpr + obtain ⟨q', hq', a, ha, rfl⟩ := mem_codeSpace.mp hS pr hmem + rw [sfst_kpair] at hq + subst hq + rw [sfst_kpair, ssnd_kpair, ssnd_kpair] + exact ⟨q, (paths_uncons hq').2, a, ha, rfl⟩ + +/-- The assembled code of a root shape at `t` and subcodes at the +targets is in the code space at `t`. -/ +theorem mkCode_mem_codeSpace {t a g : V} (ha : a ∈ˢ A t) + (hg : ∀ p, p ∈ˢ B a → app g p ∈ˢ codeSpace A B tgt (tgt a p)) : + mkCode B a g ∈ˢ codeSpace A B tgt t := by + rw [mem_codeSpace] + intro x hx + rcases mem_mkCode.mp hx with rfl | ⟨p, hp, pr, hpr, rfl⟩ + · exact ⟨pt, pt_mem_paths t, a, mem_shapes.mpr ⟨0, ha⟩, rfl⟩ + · obtain ⟨q, hq, b, hb, rfl⟩ := mem_codeSpace.mp (hg p hp) pr hpr + rw [sfst_kpair, ssnd_kpair] + refine ⟨kpair p q, kpair_mem_paths (mem_positions_of_root ha hp) (paths_step_sub ha hp q hq), + b, shapes_step_sub ha hp b hb, rfl⟩ + +end InSpace + +end Codes + +/-! ## Decoding: the recursion theorem along the subcodes -/ + +section Decode + +variable (A B : V → V) (tgt : V → V → V) (I : V) (w : Nat) (mk : V → V → V) + +/-- All codes at all indices (a set; membership in a universe is not +needed for a recursion index set). -/ +noncomputable def allCodes : V := sUnion (image (codeSpace A B tgt) I) + +open Classical in +/-- The immediate subcodes of a code with a root. -/ +noncomputable def codePred (S : V) : V := + if HasRoot S then image (subCode S) (B (lab S)) else empty + +open Classical in +/-- The decoding step: the container's builder at the root label (a +shape at some index) and the decoded subcodes (the point off the +guard). -/ +noncomputable def codeStep (S g : V) : V := + if HasRoot S ∧ ∃ i, i ∈ˢ I ∧ lab S ∈ˢ A i then + mk (lab S) (graph (fun p => app g (subCode S p)) (B (lab S))) + else pt + +/-- The decoding graph: the recursion theorem's least fixed point at +level `w + 1`, bounded by `univ w`. -/ +noncomputable def decodeGraph : V := + recGraph (w + 1) (allCodes A B tgt I) (codePred B) (fun _ => univ w) (codeStep A B I mk) + +/-- The accessible codes. -/ +noncomputable def accCodes : V := accFam (allCodes A B tgt I) (codePred B) (fun _ => True) + +/-- The decoding: the selector of the decoding graph. -/ +noncomputable def decode (S : V) : V := recSel (decodeGraph A B tgt I w mk) S + +variable {A B tgt I w mk} + +theorem mem_allCodes {S : V} : S ∈ˢ allCodes A B tgt I ↔ ∃ t, t ∈ˢ I ∧ S ∈ˢ codeSpace A B tgt t := by + unfold allCodes + rw [mem_sUnion] + constructor + · rintro ⟨y, hy, hS⟩ + obtain ⟨t, ht, rfl⟩ := mem_image.mp hy + exact ⟨t, ht, hS⟩ + · rintro ⟨t, ht, hS⟩ + exact ⟨_, mem_image.mpr ⟨t, ht, rfl⟩, hS⟩ + +theorem codePred_of_root {S : V} (h : HasRoot S) : codePred B S = image (subCode S) (B (lab S)) := by + unfold codePred; exact if_pos h + +theorem codePred_of_not {S : V} (h : ¬ HasRoot S) : codePred B S = empty := by + unfold codePred; exact if_neg h + +theorem codePred_sub {S : V} (hS : S ∈ˢ allCodes A B tgt I) : codePred B S ⊆ˢ allCodes A B tgt I := by + intro x hx + by_cases h : HasRoot S + · rw [codePred_of_root h] at hx + obtain ⟨p, -, rfl⟩ := mem_image.mp hx + obtain ⟨t, ht, hSt⟩ := mem_allCodes.mp hS + exact mem_allCodes.mpr ⟨t, ht, subCode_mem_codeSpace hSt⟩ + · rw [codePred_of_not h] at hx + exact absurd hx (not_mem_empty x) + +/-- **Accessibility, unfolded**: a code is accessible iff its immediate +subcodes are. -/ +theorem accFam_iff {J : V} {pred : V → V} {Cond : V → Prop} (hpred : ∀ i, i ∈ˢ J → pred i ⊆ˢ J) + {i : V} (hi : i ∈ˢ J) : + (∃ y, y ∈ˢ app (accFam J pred Cond) i) ↔ + Cond i ∧ ∀ j, j ∈ˢ pred i → ∃ y, y ∈ˢ app (accFam J pred Cond) j := by + have h := app_lfpFamSet_eq (F := accStep J pred Cond) ⟨_, accStep_closed (pred := pred) (Cond := Cond)⟩ + (accStep_mono hpred) accStep_maps hi + unfold accFam + rw [← h, app_app_accStep (lfpFamSet_mem _ _ _) hi] + unfold accFibre + constructor + · rintro ⟨y, hy⟩ + exact (mem_truthVal.mp hy).1 + · intro hc + exact ⟨pt, mem_truthVal.mpr ⟨hc, rfl⟩⟩ + +theorem accCodes_iff {S : V} (hS : S ∈ˢ allCodes A B tgt I) : + (∃ y, y ∈ˢ app (accCodes A B tgt I) S) ↔ + ∀ S', S' ∈ˢ codePred B S → ∃ y, y ∈ˢ app (accCodes A B tgt I) S' := by + unfold accCodes + rw [accFam_iff (fun _ hS' => codePred_sub hS') hS] + exact ⟨fun h => h.2, fun h => ⟨trivial, h⟩⟩ + +section Facts + +variable {w : Nat} (hw : w ≠ 0) + (hB : ∀ i a, i ∈ˢ I → a ∈ˢ A i → B a ∈ˢ (univ w : V)) + (hmkU : ∀ i a g, i ∈ˢ I → a ∈ˢ A i → g ∈ˢ (univ w : V) → mk a g ∈ˢ (univ w : V)) +include hw hB hmkU + +omit hw hB hmkU in +theorem decodeGraph_hB : ∀ S, S ∈ˢ allCodes A B tgt I → (univ w : V) ∈ˢ (univ (w + 1) : V) := + fun _ _ => univ_mem_univ w + +/-- The step lands in the bound: the builder at members. -/ +theorem codeStep_mem {S g : V} (hS : S ∈ˢ allCodes A B tgt I) + (hg : g ∈ˢ piSet (codePred B S) fun j => app (decodeGraph A B tgt I w mk) j) : + codeStep A B I mk S g ∈ˢ (univ w : V) := by + have hU := univ_isTGUniverse (V := V) hw + unfold codeStep + split + · next h => + obtain ⟨hroot, i, hi, hlab⟩ := h + have hBl : B (lab S) ∈ˢ (univ w : V) := hB i _ hi hlab + refine hmkU i _ _ hi hlab ?_ + -- the graph of the decoded subcodes over the member position set + unfold graph + refine hU.image_mem hBl fun p hp => ?_ + refine hU.kpair_mem hBl (hU.transitive hBl hp) ?_ + -- the value: in the decoding graph's fibre, bounded by the universe + have hpred : subCode S p ∈ˢ codePred B S := by + rw [codePred_of_root hroot] + exact mem_image.mpr ⟨p, hp, rfl⟩ + have hv := app_mem_of_mem_piSet hg hpred + unfold decodeGraph at hv + rw [app_recGraph_eq decodeGraph_hB (fun _ hS' => codePred_sub hS') + (codePred_sub hS _ hpred)] at hv + exact (mem_recGraphFibre.mp hv).1 + · exact hU.pt_mem (empty_mem_univ w) + +theorem decodeGraph_hst : ∀ S, S ∈ˢ allCodes A B tgt I → + ∀ g, g ∈ˢ piSet (codePred B S) (fun j => app (decodeGraph A B tgt I w mk) j) → + codeStep A B I mk S g ∈ˢ (univ w : V) := + fun _ hS _ hg => codeStep_mem hw hB hmkU hS hg + +/-- **Accessible codes decode uniquely**: the decoding graph's fibre is +a singleton. -/ +theorem decode_unique {S : V} (hS : S ∈ˢ allCodes A B tgt I) + (hacc : ∃ y, y ∈ˢ app (accCodes A B tgt I) S) : + (∃ v, v ∈ˢ app (decodeGraph A B tgt I w mk) S) ∧ + ∀ v v', v ∈ˢ app (decodeGraph A B tgt I w mk) S → v' ∈ˢ app (decodeGraph A B tgt I w mk) S → + v = v' := + recGraph_exists_unique decodeGraph_hB (fun _ hS' => codePred_sub hS') + (decodeGraph_hst hw hB hmkU) S hS _ hacc.choose_spec + +/-- The decoding of an accessible code is a member. -/ +theorem decode_mem_univ {S : V} (hS : S ∈ˢ allCodes A B tgt I) + (hacc : ∃ y, y ∈ˢ app (accCodes A B tgt I) S) : + decode A B tgt I w mk S ∈ˢ (univ w : V) := by + have hv := recSel_mem (decode_unique hw hB hmkU hS hacc).1 + unfold decodeGraph at hv + rw [app_recGraph_eq decodeGraph_hB (fun _ hS' => codePred_sub hS') hS] at hv + exact (mem_recGraphFibre.mp hv).1 + +/-- **The decoding equation** at an accessible code with a root: the +builder at the root label and the decoded subcodes. -/ +theorem decode_eq {S : V} (hS : S ∈ˢ allCodes A B tgt I) + (hacc : ∃ y, y ∈ˢ app (accCodes A B tgt I) S) (hroot : HasRoot S) + (hlab : ∃ i, i ∈ˢ I ∧ lab S ∈ˢ A i) : + decode A B tgt I w mk S + = mk (lab S) (graph (fun p => decode A B tgt I w mk (subCode S p)) (B (lab S))) := by + have hP : ∀ j, j ∈ˢ codePred B S → + (∃ v, v ∈ˢ app (decodeGraph A B tgt I w mk) j) ∧ + ∀ v v', v ∈ˢ app (decodeGraph A B tgt I w mk) j → v' ∈ˢ app (decodeGraph A B tgt I w mk) j → + v = v' := + fun j hj => decode_unique hw hB hmkU (codePred_sub hS j hj) ((accCodes_iff hS).mp hacc j hj) + have heq := recSel_eq decodeGraph_hB (fun _ hS' => codePred_sub hS') hS + (decode_unique hw hB hmkU hS hacc).1 hP + unfold decode + unfold decodeGraph at heq ⊢ + rw [heq] + unfold codeStep + rw [if_pos ⟨hroot, hlab⟩] + congr 1 + refine graph_congr fun p hp => ?_ + rw [app_graph (show subCode S p ∈ˢ codePred B S from by + rw [codePred_of_root hroot]; exact mem_image.mpr ⟨p, hp, rfl⟩)] + +omit hw hB hmkU in +/-- An assembled code of accessible subcodes is accessible. -/ +theorem acc_mkCode {t a g : V} (ht : t ∈ˢ I) (ha : a ∈ˢ A t) + (hg : ∀ p, p ∈ˢ B a → app g p ∈ˢ codeSpace A B tgt (tgt a p)) + (hacc : ∀ p, p ∈ˢ B a → ∃ y, y ∈ˢ app (accCodes A B tgt I) (app g p)) : + ∃ y, y ∈ˢ app (accCodes A B tgt I) (mkCode B a g) := by + have hS : mkCode B a g ∈ˢ allCodes A B tgt I := + mem_allCodes.mpr ⟨t, ht, mkCode_mem_codeSpace ha hg⟩ + rw [accCodes_iff hS] + intro S' hS' + rw [codePred_of_root (hasRoot_mkCode a g), lab_mkCode] at hS' + obtain ⟨p, hp, rfl⟩ := mem_image.mp hS' + rw [subCode_mkCode hp (codeSpace_pairs (hg p hp))] + exact hacc p hp + +end Facts + +end Decode + +/-! ## The closure witness -/ + +/-- **A member container has a closed member family**: the family of +the decoded accessible codes. The functor `Φ` on families over `I` is +presented as a container by `helim` — every element of `Φ X` at `i` is +`mk a g` for a shape `a ∈ A i` and a function `g` from the positions +`B a` into the fibres of `X` at the targets `tgt a p`; shapes and +position sets are members of `univ w`, the builder keeps members. The +decoded family's fibres are images of members (replacement), and a +constructor step on decoded subcodes decodes the assembled code. -/ +theorem container_closed_exists {w : Nat} (hw : w ≠ 0) {I : V} (Φ : V → V) + (A B : V → V) (tgt : V → V → V) (mk : V → V → V) + (hA : ∀ i, i ∈ˢ I → A i ∈ˢ (univ w : V)) + (hB : ∀ i a, i ∈ˢ I → a ∈ˢ A i → B a ∈ˢ (univ w : V)) + (htgt : ∀ i a p, i ∈ˢ I → a ∈ˢ A i → p ∈ˢ B a → tgt a p ∈ˢ I) + (hmkU : ∀ i a g, i ∈ˢ I → a ∈ˢ A i → g ∈ˢ (univ w : V) → mk a g ∈ˢ (univ w : V)) + (helim : ∀ X, X ∈ˢ famSpace w I → ∀ i, i ∈ˢ I → ∀ x, x ∈ˢ app (Φ X) i → + ∃ a, a ∈ˢ A i ∧ ∃ g, g ∈ˢ piSet (B a) (fun p => app X (tgt a p)) ∧ x = mk a g) : + ∃ L, L ∈ˢ famSpace w I ∧ FamLe I (Φ L) L := by + have hU := univ_isTGUniverse (V := V) hw + let Lf : V → V := fun i => image (decode A B tgt I w mk) + (sep (codeSpace A B tgt i) fun S => + (∃ y, y ∈ˢ app (accCodes A B tgt I) S) ∧ HasRoot S ∧ lab S ∈ˢ A i) + have hLmem : graph Lf I ∈ˢ famSpace w I := by + refine graph_mem_famSpace fun i hi => ?_ + refine hU.image_mem (hU.sep_mem (codeSpace_mem hw hA hB htgt hi)) fun S hS => ?_ + obtain ⟨hSi, hacc, -, -⟩ := mem_sep.mp hS + exact decode_mem_univ hw hB hmkU (mem_allCodes.mpr ⟨i, hi, hSi⟩) hacc + refine ⟨graph Lf I, hLmem, ?_⟩ + intro i hi x hx + obtain ⟨a, ha, g, hg, rfl⟩ := helim _ hLmem i hi x hx + -- every subtree is the decoding of an accessible code at its target + have hsub : ∀ p, p ∈ˢ B a → ∃ S, S ∈ˢ codeSpace A B tgt (tgt a p) ∧ + (∃ y, y ∈ˢ app (accCodes A B tgt I) S) ∧ HasRoot S ∧ lab S ∈ˢ A (tgt a p) ∧ + decode A B tgt I w mk S = app g p := by + intro p hp + have hgp := app_mem_of_mem_piSet hg hp + rw [app_graph (htgt i a p hi ha hp)] at hgp + obtain ⟨S, hS, hdec⟩ := mem_image.mp hgp + obtain ⟨hSi, hacc, hroot, hlab⟩ := mem_sep.mp hS + exact ⟨S, hSi, hacc, hroot, hlab, hdec.symm⟩ + classical + let cs : V → V := fun p => if h : p ∈ˢ B a then Classical.choose (hsub p h) else empty + have hcs : ∀ p, p ∈ˢ B a → cs p ∈ˢ codeSpace A B tgt (tgt a p) ∧ + (∃ y, y ∈ˢ app (accCodes A B tgt I) (cs p)) ∧ HasRoot (cs p) ∧ lab (cs p) ∈ˢ A (tgt a p) ∧ + decode A B tgt I w mk (cs p) = app g p := by + intro p hp + have hcp : cs p = Classical.choose (hsub p hp) := by + show (if h : p ∈ˢ B a then Classical.choose (hsub p h) else empty) = _ + rw [dif_pos hp] + rw [hcp] + exact Classical.choose_spec (hsub p hp) + have hgS : ∀ p, p ∈ˢ B a → app (graph cs (B a)) p = cs p := fun p hp => app_graph hp + have hSmem : mkCode B a (graph cs (B a)) ∈ˢ codeSpace A B tgt i := + mkCode_mem_codeSpace ha fun p hp => by rw [hgS p hp]; exact (hcs p hp).1 + have hSacc : ∃ y, y ∈ˢ app (accCodes A B tgt I) (mkCode B a (graph cs (B a))) := + acc_mkCode hi ha (fun p hp => by rw [hgS p hp]; exact (hcs p hp).1) + (fun p hp => by rw [hgS p hp]; exact (hcs p hp).2.1) + rw [app_graph hi] + refine mem_image.mpr ⟨mkCode B a (graph cs (B a)), + mem_sep.mpr ⟨hSmem, hSacc, hasRoot_mkCode a _, by rw [lab_mkCode]; exact ha⟩, ?_⟩ + rw [decode_eq hw hB hmkU (mem_allCodes.mpr ⟨i, hi, hSmem⟩) hSacc (hasRoot_mkCode a _) + ⟨i, hi, by rw [lab_mkCode]; exact ha⟩, lab_mkCode] + congr 1 + refine (eq_graph_app_of_mem_piSet hg).symm.trans (graph_congr fun p hp => ?_) + rw [subCode_mkCode hp (fun x hx => codeSpace_pairs (hcs p hp).1 x (by rwa [hgS p hp] at hx)), + hgS p hp] + exact ((hcs p hp).2.2.2.2).symm + +end Ix.Theory.Model.SetTheory diff --git a/Ix/Theory/Model/SetModel/Iter.lean b/Ix/Theory/Model/SetModel/Iter.lean new file mode 100644 index 000000000..ac009bc78 --- /dev/null +++ b/Ix/Theory/Model/SetModel/Iter.lean @@ -0,0 +1,107 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetModel/Iter.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetModel.TaggedSum + +@[expose] public section + +/-! +# The ω-iterate of a set functor (task #188) + +The one place iteration survives in the recursive-type model: the +carrier is the Knaster–Tarski least pre-fixed point +(`Ix.Theory/Model/SetTheory/Derive/Lfp.lean`), and every law about it assumes a +CLOSED MEMBER of the universe exists. For a *finitary* tower functor +that witness is the ω-iterate + + iterF Φ 0 = ∅, iterF Φ (n+1) = Φ (iterF Φ n), iterU Φ = ⋃ₙ iterF Φ n + +— a countable union of members of `univ w`, hence a member (`ω ∈ univ +w` at `w ≥ 1`, `omega_mem_univ_succ`; at `w = 0` truth values), and +closed under `Φ` whenever every member of `Φ (iterU Φ)` already lies in +some `Φ (iterF Φ n)` (`iterU_closed_of` — the finitary condition, +discharged for the tower functor in `Ix.Theory/Model/Semantics/Tower/FixLeaf.lean` +by bounding the ranks of a tuple's finitely many recursive fields). +The union is also where the recursor's semantic fixed point is built +by rank recursion. + +Everything here is over the bare `SetTheory` interface; no syntax. +-/ + +namespace Ix.Theory.Model.SetTheory.Tower + +universe u + +variable {V : Type u} [SetTheory V] + +/-- The finite iterates of a set functor from the empty set. -/ +noncomputable def iterF (Φ : V → V) : Nat → V + | 0 => empty + | n + 1 => Φ (iterF Φ n) + +@[simp] theorem iterF_zero (Φ : V → V) : iterF Φ 0 = empty := rfl +@[simp] theorem iterF_succ (Φ : V → V) (n : Nat) : iterF Φ (n + 1) = Φ (iterF Φ n) := rfl + +/-- The union of a countable family `f 0 ∪ f 1 ∪ …` (the ω-indexed union +through the tag fibre). -/ +noncomputable def natUnion (f : Nat → V) : V := + sUnion (image (natFibre f) omega) + +theorem mem_natUnion {f : Nat → V} {x : V} : x ∈ˢ natUnion f ↔ ∃ n, x ∈ˢ f n := by + unfold natUnion + rw [mem_sUnion] + constructor + · rintro ⟨y, hy, hxy⟩ + obtain ⟨k, hk, rfl⟩ := mem_image.mp hy + obtain ⟨n, rfl, hfib⟩ := natFibre_of_mem f hk + rw [hfib] at hxy + exact ⟨n, hxy⟩ + · rintro ⟨n, hn⟩ + exact ⟨natFibre f (vnat n), mem_image.mpr ⟨vnat n, vnat_mem_omega n, rfl⟩, + by rw [natFibre_vnat]; exact hn⟩ + +/-- **Formation** (graph regime): a countable union of members of a +positive level is a member. -/ +theorem natUnion_mem_univ_pos {w : Nat} (hw : w ≠ 0) {f : Nat → V} + (h : ∀ n, f n ∈ˢ (univ w : V)) : natUnion f ∈ˢ (univ w : V) := by + obtain ⟨w', rfl⟩ : ∃ w', w = w' + 1 := ⟨w - 1, by omega⟩ + unfold natUnion + refine (univ_isTGUniverse (Nat.succ_ne_zero w')).famUnion_mem (omega_mem_univ_succ w') ?_ + intro k hk + obtain ⟨n, rfl, hfib⟩ := natFibre_of_mem f hk + rw [hfib] + exact h n + +/-- **Formation** (squash regime): a union of truth values is a truth +value. -/ +theorem natUnion_mem_univZero {f : Nat → V} (h : ∀ n, f n ∈ˢ (univZero : V)) : + natUnion f ∈ˢ (univZero : V) := by + rw [mem_univZero] + intro x hx + obtain ⟨n, hn⟩ := mem_natUnion.mp hx + exact (mem_univZero.mp (h n)) x hn + +/-- The ω-iterate: the union of the finite iterates. -/ +noncomputable def iterU (Φ : V → V) : V := natUnion (iterF Φ) + +theorem mem_iterU {Φ : V → V} {x : V} : x ∈ˢ iterU Φ ↔ ∃ n, x ∈ˢ iterF Φ n := + mem_natUnion + +/-- **Closure** under a finitary functor: if every member of +`Φ (iterU Φ)` lies in some finite stage's image, the ω-iterate is +closed. -/ +theorem iterU_closed_of {Φ : V → V} + (hfin : ∀ x, x ∈ˢ Φ (iterU Φ) → ∃ n, x ∈ˢ Φ (iterF Φ n)) : + Φ (iterU Φ) ⊆ˢ iterU Φ := by + intro x hx + obtain ⟨n, hn⟩ := hfin x hx + exact mem_iterU.mpr ⟨n + 1, hn⟩ + +end Ix.Theory.Model.SetTheory.Tower diff --git a/Ix/Theory/Model/SetModel/Ops.lean b/Ix/Theory/Model/SetModel/Ops.lean new file mode 100644 index 000000000..23d5d2b8a --- /dev/null +++ b/Ix/Theory/Model/SetModel/Ops.lean @@ -0,0 +1,366 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetModel/Ops.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetTheory.Derive.Univ + +@[expose] public section + +/-! +# The two-regime product and abstraction (task #151, tier B) + +`piR`/`lamR` are the **annotation-driven** dependent product and +abstraction: they read a numeral — the codomain sort of the binder, +supplied by the annotation pass — and dispatch on it, rather than +inspecting the semantic value the way the domain-relative collapse +(`pcol`/`piC`/`lamC`, `Ix.Theory/Model/SetTheory/Derive/Pi.lean` — deleted at +task #221, unread since these operators replaced it) does. + +* **`v = 0` — the truth-value (squash) regime.** `piR 0 A B` is the + truth value `[∀ x ∈ A, B x inhabited]`, `lamR 0 A F` is the canonical + proof. Propositions are subsets of the canonical one-element set + `unitSet = {pt}`, so proof irrelevance and impredicativity are both + immediate: *a product landing in `Prop` is small whatever its domain + is* (`piR_zero_mem_univZero`), with no size or universe side + condition anywhere. +* **`v ≠ 0` — the graph regime.** `piR v A B` is `piSet A B`, the set + of total single-valued function graphs over `A`; `lamR v A F` is the + literal graph. **No collapse, no proof point**: a member of a + positive-regime product is a graph, is never `pt` + (`not_pt_mem_piR_pos`), determines its own domain + (`piR_dom_unique`, with *no* `≠ pt` side condition), and applies to + the canonical junk `∅` off that domain (`app_off_dom_piR_pos`). + +The operators are the pre-#100 `SetTheory.pi`/`SetTheory.lam` (see the +git history of `Derive/Pi.lean`), restated in this namespace so that +`Ix.Theory/Model/SetTheory/*` is untouched; the law battery below is that file's, +plus the *inversion* laws that only the annotation-driven definition can +have (`mem_piR_pos`, `piR_dom_unique`, `not_pt_mem_piR_pos`). + +**On `pt`.** The proof point appears in exactly one place: the `v = 0` +branch of `lamR`, as the canonical inhabitant of a true proposition. +That is forced — `SetTheory`'s `univ 0 = power unitSet` fixes the +canonical one-element set, so the unique element of a true truth value +*is* `pt` — and it is not a collapse: no clause here tests a value, and +the graph regime never produces, contains, or consults it. So `pt` is +*demoted, not deleted*: deleting it is not achievable — it would mean +re-deriving `univZero` over a different singleton, and renaming the +canonical point changes nothing — and not needed, because nothing here +tests for it. +-/ + +namespace Ix.Theory.Model.SetModel + +open SetTheory + +universe u + +variable {V : Type u} [SetTheory V] + +/-- The dependent product at codomain sort `v`: a truth value at `0`, +the set of function graphs above it. -/ +noncomputable def piR (v : Nat) (A : V) (B : V → V) : V := + if v = 0 then truthVal (∀ x, x ∈ˢ A → ∃ y, y ∈ˢ B x) else piSet A B + +/-- Abstraction at codomain sort `v`: the canonical proof at `0`, the +literal function graph above it. -/ +noncomputable def lamR (v : Nat) (A : V) (F : V → V) : V := + if v = 0 then pt else graph F A + +/-! ## The two branches -/ + +theorem piR_zero {A : V} {B : V → V} : + piR 0 A B = truthVal (∀ x, x ∈ˢ A → ∃ y, y ∈ˢ B x) := if_pos rfl + +theorem piR_pos {v : Nat} (hv : v ≠ 0) {A : V} {B : V → V} : + piR v A B = piSet A B := if_neg hv + +theorem lamR_zero {A : V} {F : V → V} : lamR 0 A F = (pt : V) := if_pos rfl + +theorem lamR_pos {v : Nat} (hv : v ≠ 0) {A : V} {F : V → V} : + lamR v A F = graph F A := if_neg hv + +/-! ## Congruence -/ + +theorem piR_congr {v : Nat} {A : V} {B B' : V → V} + (h : ∀ x, x ∈ˢ A → B x = B' x) : piR v A B = piR v A B' := by + rcases Nat.eq_zero_or_pos v with rfl | hv + · rw [piR_zero, piR_zero] + exact truthVal_congr + ⟨fun hi x hx => h x hx ▸ hi x hx, fun hi x hx => (h x hx).symm ▸ hi x hx⟩ + · rw [piR_pos (Nat.pos_iff_ne_zero.mp hv), piR_pos (Nat.pos_iff_ne_zero.mp hv)] + exact piSet_congr h + +theorem lamR_congr {v : Nat} {A : V} {F F' : V → V} + (h : ∀ x, x ∈ˢ A → F x = F' x) : lamR v A F = lamR v A F' := by + rcases Nat.eq_zero_or_pos v with rfl | hv + · rw [lamR_zero, lamR_zero] + · rw [lamR_pos (Nat.pos_iff_ne_zero.mp hv), lamR_pos (Nat.pos_iff_ne_zero.mp hv)] + exact graph_congr h + +/-! ## Zero-agreement + +`piR`/`lamR` read their numeral **only through the `v = 0` test**, so +annotations that agree on zero-ness are interchangeable. This is the +pre-#100 `pi_congr_zero_agree`/`lam_congr_zero_agree` pair, and it is +what lets a λ-tower carry its *result* sort at every binder rather than +the exact `imax` fold: in `(a₁ : A₁) → … → (aₙ : Aₙ) → T` the sort of +each suffix is `imax (…) r` with `r` the sort of `T`, and +`imax x y = 0 ↔ y = 0`. See `Interp/Value.lean`'s annotation +convention. -/ + +theorem piR_zero_agree {v v' : Nat} (hz : v = 0 ↔ v' = 0) {A : V} + {B B' : V → V} (h : ∀ x, x ∈ˢ A → B x = B' x) : piR v A B = piR v' A B' := by + by_cases hv : v = 0 + · rw [hv, hz.mp hv]; exact piR_congr h + · have hv' : v' ≠ 0 := fun h0 => hv (hz.mpr h0) + rw [piR_pos hv, piR_pos hv'] + exact piSet_congr h + +theorem lamR_zero_agree {v v' : Nat} (hz : v = 0 ↔ v' = 0) {A : V} + {F F' : V → V} (h : ∀ x, x ∈ˢ A → F x = F' x) : lamR v A F = lamR v' A F' := by + by_cases hv : v = 0 + · rw [hv, hz.mp hv, lamR_zero, lamR_zero] + · have hv' : v' ≠ 0 := fun h0 => hv (hz.mpr h0) + rw [lamR_pos hv, lamR_pos hv'] + exact graph_congr h + +/-- `imax`'s zero test is its codomain's — the arithmetic behind the +tower convention. -/ +theorem imax_eq_zero_iff (x y : Nat) : + (if y = 0 then 0 else Nat.max x y) = 0 ↔ y = 0 := by + by_cases hy : y = 0 + · simp [hy] + · rw [if_neg hy] + exact ⟨fun h => absurd (Nat.le_zero.mp (h ▸ Nat.le_max_right x y)) hy, + fun h => absurd h hy⟩ + +/-! ## Introduction, elimination, beta, eta -/ + +/-- Introduction: fibre-wise members abstract into the product. At +`v = 0` the premise itself witnesses every fibre inhabited. -/ +theorem lamR_mem {v : Nat} {A : V} {F B : V → V} + (hF : ∀ x, x ∈ˢ A → F x ∈ˢ B x) : lamR v A F ∈ˢ piR v A B := by + rcases Nat.eq_zero_or_pos v with rfl | hv + · rw [lamR_zero, piR_zero] + exact pt_mem_truthVal fun x hx => ⟨F x, hF x hx⟩ + · rw [lamR_pos (Nat.pos_iff_ne_zero.mp hv), piR_pos (Nat.pos_iff_ne_zero.mp hv)] + exact graph_mem_piSet hF + +/-- Introduction across zero-agreeing annotations: a tower annotated +with its result sort still inhabits the product annotated with the +exact `imax`. -/ +theorem lamR_mem_zero_agree {v v' : Nat} (hz : v = 0 ↔ v' = 0) {A : V} + {F B : V → V} (hF : ∀ x, x ∈ˢ A → F x ∈ˢ B x) : lamR v A F ∈ˢ piR v' A B := by + rw [lamR_zero_agree hz (fun _ _ => rfl) (F' := F)] + exact lamR_mem hF + +/-- **Proof irrelevance at products**: inhabitants of a `Prop`-valued +product are the canonical proof. -/ +theorem eq_pt_of_mem_piR_zero {A f : V} {B : V → V} (hf : f ∈ˢ piR 0 A B) : + f = pt := by + rw [piR_zero] at hf; exact eq_pt_of_mem_truthVal hf + +/-- **Squash-regime introduction from inhabitation.** At `v = 0` the +product is a truth value, so membership of the canonical proof needs +only that every fibre is *inhabited* — strictly weaker than +`lamR_mem`'s pointwise `F x ∈ˢ B x`, and the form every tower whose +value carries no regime tag has to use at kind `0` +(`Interp/BasisOk.lean`, the `psigmaMk` finding). -/ +theorem pt_mem_piR_zero {A : V} {B : V → V} + (h : ∀ x, x ∈ˢ A → ∃ y, y ∈ˢ B x) : (pt : V) ∈ˢ piR 0 A B := by + rw [piR_zero]; exact pt_mem_truthVal h + +/-- The pointwise form, matching the collapse lane's +`pt_mem_piC_iff.mpr` so the `pt`-valued towers port line for line. -/ +theorem pt_mem_piR_zero_of {A : V} {B : V → V} + (h : ∀ x, x ∈ˢ A → (pt : V) ∈ˢ B x) : (pt : V) ∈ˢ piR 0 A B := + pt_mem_piR_zero fun x hx => ⟨pt, h x hx⟩ + +/-- Elimination. The fibre premise is needed only at `v = 0`, where +the fibres must be truth values. -/ +theorem app_mem_piR {v : Nat} {A f a : V} {B : V → V} + (hf : f ∈ˢ piR v A B) (ha : a ∈ˢ A) + (hB0 : v = 0 → ∀ x, x ∈ˢ A → B x ∈ˢ (univZero : V)) : app f a ∈ˢ B a := by + rcases Nat.eq_zero_or_pos v with rfl | hv + · have hfp : f = pt := eq_pt_of_mem_piR_zero hf + rw [piR_zero] at hf + obtain ⟨y, hy⟩ := of_mem_truthVal hf a ha + rw [hfp, app_pt] + rwa [eq_pt_of_mem_univZero (hB0 rfl a ha) hy] at hy + · rw [piR_pos (Nat.pos_iff_ne_zero.mp hv)] at hf + exact app_mem_of_mem_piSet hf ha + +/-- Elimination in the graph regime: no fibre premise at all. -/ +theorem app_mem_piR_pos {v : Nat} {A f a : V} {B : V → V} (hv : v ≠ 0) + (hf : f ∈ˢ piR v A B) (ha : a ∈ˢ A) : app f a ∈ˢ B a := by + rw [piR_pos hv] at hf; exact app_mem_of_mem_piSet hf ha + +/-- Beta, conditional on domain membership (set-theoretic functions +have set domains). -/ +theorem app_lamR {v : Nat} {A a : V} {F B : V → V} + (ha : a ∈ˢ A) (hF : ∀ x, x ∈ˢ A → F x ∈ˢ B x) + (hB0 : v = 0 → ∀ x, x ∈ˢ A → B x ∈ˢ (univZero : V)) : + app (lamR v A F) a = F a := by + rcases Nat.eq_zero_or_pos v with rfl | hv + · rw [lamR_zero, app_pt] + exact (eq_pt_of_mem_univZero (hB0 rfl a ha) (hF a ha)).symm + · rw [lamR_pos (Nat.pos_iff_ne_zero.mp hv)] + exact app_graph ha + +/-- **Beta in the graph regime**: application of an abstraction on its +domain computes, with no typing premise whatsoever — it is literally +`app_graph`. -/ +theorem app_lamR_pos {v : Nat} {A a : V} {F : V → V} (hv : v ≠ 0) + (ha : a ∈ˢ A) : app (lamR v A F) a = F a := by + rw [lamR_pos hv]; exact app_graph ha + +/-- **Off-domain application in the graph regime** — `app_lamR_pos`'s +complement. The rigidity a *type former*'s application is inverted +with: off its domain a graph-regime abstraction applies to the +canonical junk `∅`, which has no members, so an inhabited application +forces its argument into the domain. Added for the caps tier's +pinned-pair η row (task #161). -/ +theorem app_lamR_of_not_mem {v : Nat} {A a : V} {F : V → V} (hv : v ≠ 0) + (ha : ¬ a ∈ˢ A) : app (lamR v A F) a = (empty : V) := by + rw [lamR_pos hv]; exact app_graph_of_not_mem ha + +/-- Graph-regime abstractions are graphs, never the proof point. -/ +theorem lamR_ne_pt {v : Nat} {A : V} {F : V → V} (hv : v ≠ 0) : + lamR v A F ≠ pt := by rw [lamR_pos hv]; exact graph_ne_pt + +/-- Eta: a member of a product is the abstraction of its +applications. -/ +theorem lamR_eta {v : Nat} {A f : V} {B : V → V} (hf : f ∈ˢ piR v A B) : + lamR v A (fun x => app f x) = f := by + rcases Nat.eq_zero_or_pos v with rfl | hv + · rw [lamR_zero, eq_pt_of_mem_piR_zero hf] + · have hv' : v ≠ 0 := Nat.pos_iff_ne_zero.mp hv + rw [piR_pos hv'] at hf + rw [lamR_pos hv'] + exact eq_graph_app_of_mem_piSet hf + +/-- Function extensionality for product members: on-domain agreement is +total agreement. At `v = 0` both sides are the canonical proof; above +it both are graphs over `A`, whose off-domain applications are the same +canonical junk. -/ +theorem eq_of_mem_piR_app_eq {v : Nat} {A f g : V} {B B' : V → V} + (hf : f ∈ˢ piR v A B) (hg : g ∈ˢ piR v A B') + (h : ∀ x, x ∈ˢ A → app f x = app g x) : f = g := by + rw [← lamR_eta hf, ← lamR_eta hg]; exact lamR_congr h + +/-! ## The graph regime: inversion and junk-freeness + +These are the laws the collapse cannot have. Under `piC` a product +member is either a graph *or* the proof point (`mem_piC_cases`), so +every consumer dispatches; here the annotation has already decided, and +membership in a positive-regime product is *by definition* graph-hood. -/ + +/-- **The prized inversion.** A member of a graph-regime product is a +graph whose domain is exactly the product's domain, whose applications +land pointwise in the fibres, which is never the proof point, and which +applies to the canonical junk `∅` off the domain. Every clause is by +definition of `piSet`; nothing about `B` is used. -/ +theorem mem_piR_pos {v : Nat} {A f : V} {B : V → V} (hv : v ≠ 0) + (hf : f ∈ˢ piR v A B) : + graph (fun x => app f x) A = f ∧ + (∀ x, x ∈ˢ A → app f x ∈ˢ B x) ∧ + (∀ a, ¬ a ∈ˢ A → app f a = empty) ∧ + f ≠ pt := by + rw [piR_pos hv] at hf + exact ⟨eq_graph_app_of_mem_piSet hf, fun x hx => app_mem_of_mem_piSet hf hx, + fun a ha => app_off_dom_of_mem_piSet hf ha, ne_pt_of_mem_piSet hf⟩ + +/-- The proof point never inhabits a graph-regime product — for *any* +domain and *any* fibre family, in particular a universe-valued one. +This is the removal of the collapse's universe-cohabitation wall, where +`pt ∈ˢ piC A (fun _ => univ 0)` holds at an unknown-empty domain. -/ +theorem not_pt_mem_piR_pos {v : Nat} {A : V} {B : V → V} (hv : v ≠ 0) : + ¬ (pt : V) ∈ˢ piR v A B := + fun h => (mem_piR_pos hv h).2.2.2 rfl + +/-- A graph-regime member applied off the domain is canonical junk — +never a proof point, never anything a consumer must dispatch on. -/ +theorem app_off_dom_piR_pos {v : Nat} {A f a : V} {B : V → V} (hv : v ≠ 0) + (hf : f ∈ˢ piR v A B) (ha : ¬ a ∈ˢ A) : app f a = empty := + (mem_piR_pos hv hf).2.2.1 a ha + +/-- **Domain uniqueness, unconditional.** A graph determines its own +domain, so membership in two graph-regime products identifies their +domains — with no `≠ pt` side condition (`piC_dom_unique` needs one, +and supplying it is what the collapse made hard). -/ +theorem piR_dom_unique {v v' : Nat} {A A' f : V} {B B' : V → V} + (hv : v ≠ 0) (hv' : v' ≠ 0) + (h1 : f ∈ˢ piR v A B) (h2 : f ∈ˢ piR v' A' B') : A = A' := by + rw [piR_pos hv] at h1 + rw [piR_pos hv'] at h2 + obtain ⟨hsub, htot⟩ := mem_piSet.mp h1 + obtain ⟨hsub', htot'⟩ := mem_piSet.mp h2 + refine ext fun x => ⟨fun hx => ?_, fun hx => ?_⟩ + · obtain ⟨y, hy, -⟩ := htot x hx + obtain ⟨x2, hx2, y2, -, hp⟩ := mem_sigmaPairs.mp (hsub' _ hy) + obtain ⟨rfl, rfl⟩ := kpair_inj hp + exact hx2 + · obtain ⟨y, hy, -⟩ := htot' x hx + obtain ⟨x2, hx2, y2, -, hp⟩ := mem_sigmaPairs.mp (hsub _ hy) + obtain ⟨rfl, rfl⟩ := kpair_inj hp + exact hx2 + +/-! ## The truth-value regime -/ + +/-- **Impredicativity.** A product landing in `Prop` is a truth value +— for an arbitrary domain `A` and arbitrary fibres, with no size +condition. This is the one place a set-theoretic model of Lean has to +say something, and here it is the `v = 0` branch of a numeral test. -/ +theorem piR_zero_mem_univZero {A : V} {B : V → V} : + piR 0 A B ∈ˢ (univZero : V) := by + rw [piR_zero]; exact truthVal_mem_univZero _ + +/-- Members of a truth value are all equal: the `v = 0` regime is +subsingleton-valued. -/ +theorem subsingleton_of_mem_univZero {T x y : V} (hT : T ∈ˢ (univZero : V)) + (hx : x ∈ˢ T) (hy : y ∈ˢ T) : x = y := + (eq_pt_of_mem_univZero hT hx).trans (eq_pt_of_mem_univZero hT hy).symm + +/-- Proof irrelevance for the squash regime, in subsingleton form. -/ +theorem piR_zero_subsingleton {A x y : V} {B : V → V} + (hx : x ∈ˢ piR 0 A B) (hy : y ∈ˢ piR 0 A B) : x = y := + (eq_pt_of_mem_piR_zero hx).trans (eq_pt_of_mem_piR_zero hy).symm + +/-- The empty-domain product is truth — in the graph regime too, where +it is the singleton `{∅}` of the empty graph rather than a collapsed +point. (Contrast `piC_empty`, where *every* empty-domain product is +`unitSet` and *every* empty-domain abstraction collapses to `pt` — the +countermodel that blocked the #100 flip.) -/ +theorem piR_pos_empty {v : Nat} (hv : v ≠ 0) (B : V → V) : + piR v (empty : V) B = sing (empty : V) := by + rw [piR_pos hv] + refine ext fun f => ?_ + rw [mem_sing, mem_piSet] + constructor + · rintro ⟨hsub, -⟩ + refine eq_empty fun z hz => ?_ + obtain ⟨x, hx, -⟩ := mem_sigmaPairs.mp (hsub z hz) + exact not_mem_empty x hx + · rintro rfl + exact ⟨fun z hz => absurd hz (not_mem_empty z), + fun x hx => absurd hx (not_mem_empty x)⟩ + +/-- Empty-domain abstraction in the graph regime is the empty graph, +**not** the proof point: the annotation, not the (vacuous) value test, +decides. This is exactly the clause whose collapse analogue +(`lamC_empty`) produced the #100 countermodel. -/ +theorem lamR_pos_empty {v : Nat} (hv : v ≠ 0) (F : V → V) : + lamR v (empty : V) F = (empty : V) := by + rw [lamR_pos hv] + refine eq_empty fun z hz => ?_ + obtain ⟨x, hx, -⟩ := mem_graph.mp hz + exact not_mem_empty x hx + +end Ix.Theory.Model.SetModel diff --git a/Ix/Theory/Model/SetModel/RecGraph.lean b/Ix/Theory/Model/SetModel/RecGraph.lean new file mode 100644 index 000000000..675de1d68 --- /dev/null +++ b/Ix/Theory/Model/SetModel/RecGraph.lean @@ -0,0 +1,270 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetModel/RecGraph.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetTheory.Derive.LfpFam +import Ix.Theory.Model.SetTheory.Derive.Pt +@[expose] public section + +/-! +# The recursion theorem by lfp induction (task #202, Stage A2) + +The recursive squash regime's large eliminator: the family lives at +`Prop` (its fibres are truth values, the sole proof the point) and the +recursor eliminates into `Sort ℓ`, `ℓ ≠ 0`. The recursor's value at an +index `i` is determined by the recursion equation `R i = st i (R on the +predecessors of i)` — well-founded along the family's least fixed +point, which is why it exists and is unique. + +This module states that abstractly, `Expr`-free: over an index set `I` +with predecessor sets `pred i ⊆ I`, a bound `B i ∈ univ ℓ` and a step +`st i g` (`g` a choice function of predecessor values), the GRAPH +functor `recGraphStep` sends a family `S` to the family of values +`st i g` for `g ∈ Π_{j ∈ pred i} S j` (within `B i`); it is monotone +and `B` is a closed family, so its least fixed point `recGraph` exists, +and at every index whose `Acc`-family fibre (`accFam`: inhabited iff +`Cond i` and every predecessor's fibre is) is inhabited, `recGraph`'s +fibre is a SINGLETON (`recGraph_exists_unique`) — by +`lfpFamSet_induction` on the `Acc` family: the predecessors' fibres +are singletons, the graph of their elements witnesses existence +(`app_lfpFamSet_eq`), and any element is the step at that same choice +function, by function extensionality on `piSet`. +-/ + +namespace Ix.Theory.Model.SetTheory + +universe u + +variable {V : Type u} [SetTheory V] + +section RecGraph + +variable (ℓ : Nat) (I : V) (pred : V → V) (B : V → V) (st : V → V → V) + +/-- The graph functor's fibre at `i` over the family `S`. -/ +noncomputable def recGraphFibre (S i : V) : V := + sep (B i) fun v => ∃ g, g ∈ˢ piSet (pred i) (fun j => app S j) ∧ v = st i g + +/-- The graph functor on families over `I`, as a set-function on the +family space. -/ +noncomputable def recGraphStep : V := + graph (fun S => graph (fun i => recGraphFibre pred B st S i) I) (famSpace ℓ I) + +/-- **The recursor's graph**: the least fixed point of the graph +functor. -/ +noncomputable def recGraph : V := lfpFamSet ℓ I (recGraphStep ℓ I pred B st) + +variable {ℓ I pred B st} + +theorem app_recGraphStep {S : V} (hS : S ∈ˢ famSpace ℓ I) : + app (recGraphStep ℓ I pred B st) S = graph (fun i => recGraphFibre pred B st S i) I := + app_graph hS + +theorem app_app_recGraphStep {S i : V} (hS : S ∈ˢ famSpace ℓ I) (hi : i ∈ˢ I) : + app (app (recGraphStep ℓ I pred B st) S) i = recGraphFibre pred B st S i := by + rw [app_recGraphStep hS, app_graph hi] + +theorem mem_recGraphFibre {S i v : V} : + v ∈ˢ recGraphFibre pred B st S i ↔ + v ∈ˢ B i ∧ ∃ g, g ∈ˢ piSet (pred i) (fun j => app S j) ∧ v = st i g := + mem_sep + +/-- The functor maps the family space into itself. -/ +theorem recGraphStep_maps (hB : ∀ i, i ∈ˢ I → B i ∈ˢ (univ ℓ : V)) : + MapsFam ℓ I (recGraphStep ℓ I pred B st) := by + intro S hS + rw [app_recGraphStep hS] + exact graph_mem_famSpace fun i hi => univ_sep_mem (hB i hi) + +/-- The functor is monotone: more predecessor values, more steps. -/ +theorem recGraphStep_mono (hpred : ∀ i, i ∈ˢ I → pred i ⊆ˢ I) : + MonoFam ℓ I (recGraphStep ℓ I pred B st) := by + intro X Y hX hY hle i hi v hv + rw [app_app_recGraphStep hX hi] at hv + rw [app_app_recGraphStep hY hi] + obtain ⟨hvB, g, hg, rfl⟩ := mem_recGraphFibre.mp hv + refine mem_recGraphFibre.mpr ⟨hvB, g, ?_, rfl⟩ + obtain ⟨hsub, htot⟩ := mem_piSet.mp hg + refine mem_piSet.mpr ⟨fun p hp => ?_, htot⟩ + obtain ⟨j, hj, y, hy, rfl⟩ := mem_sigmaPairs.mp (hsub p hp) + exact mem_sigmaPairs.mpr ⟨j, hj, y, hle j (hpred i hi j hj) y hy, rfl⟩ + +/-- The bound is a closed family. -/ +theorem recGraphStep_closed (hB : ∀ i, i ∈ˢ I → B i ∈ˢ (univ ℓ : V)) : + IsClosedFam ℓ I (recGraphStep ℓ I pred B st) (graph B I) := by + refine ⟨graph_mem_famSpace hB, fun i hi v hv => ?_⟩ + rw [app_app_recGraphStep (graph_mem_famSpace hB) hi] at hv + rw [app_graph hi] + exact (mem_recGraphFibre.mp hv).1 + +/-- **The fixed-point equation**, fibrewise. -/ +theorem app_recGraph_eq (hB : ∀ i, i ∈ˢ I → B i ∈ˢ (univ ℓ : V)) + (hpred : ∀ i, i ∈ˢ I → pred i ⊆ˢ I) {i : V} (hi : i ∈ˢ I) : + app (recGraph ℓ I pred B st) i = recGraphFibre pred B st (recGraph ℓ I pred B st) i := by + unfold recGraph + rw [← app_lfpFamSet_eq ⟨_, recGraphStep_closed hB⟩ (recGraphStep_mono hpred) + (recGraphStep_maps hB) hi, app_app_recGraphStep (lfpFamSet_mem _ _ _) hi] + +end RecGraph + +/-! ## The `Acc` family -/ + +section AccFam + +variable (I : V) (pred : V → V) (Cond : V → Prop) + +/-- The `Acc` functor's fibre: inhabited iff the side condition holds +and every predecessor's fibre is inhabited. -/ +noncomputable def accFibre (X i : V) : V := + truthVal (Cond i ∧ ∀ j, j ∈ˢ pred i → ∃ y, y ∈ˢ app X j) + +noncomputable def accStep : V := + graph (fun X => graph (fun i => accFibre pred Cond X i) I) (famSpace 0 I) + +/-- **The `Acc` family**: the least fixed point of the `Acc` functor at +`Prop`. -/ +noncomputable def accFam : V := lfpFamSet 0 I (accStep I pred Cond) + +variable {I pred Cond} + +theorem app_app_accStep {X i : V} (hX : X ∈ˢ famSpace 0 I) (hi : i ∈ˢ I) : + app (app (accStep I pred Cond) X) i = accFibre pred Cond X i := by + unfold accStep + rw [app_graph hX, app_graph hi] + +theorem accStep_maps : MapsFam 0 I (accStep I pred Cond) := by + intro X hX + unfold accStep + rw [app_graph hX] + refine graph_mem_famSpace fun i _ => ?_ + rw [univ_zero] + exact truthVal_mem_univZero _ + +theorem accStep_mono (hpred : ∀ i, i ∈ˢ I → pred i ⊆ˢ I) : MonoFam 0 I (accStep I pred Cond) := by + intro X Y hX hY hle i hi x hx + rw [app_app_accStep hX hi] at hx + rw [app_app_accStep hY hi] + obtain ⟨⟨hc, hall⟩, rfl⟩ := mem_truthVal.mp hx + refine mem_truthVal.mpr ⟨⟨hc, fun j hj => ?_⟩, rfl⟩ + obtain ⟨y, hy⟩ := hall j hj + exact ⟨y, hle j (hpred i hi j hj) y hy⟩ + +theorem accStep_closed : IsClosedFam 0 I (accStep I pred Cond) (graph (fun _ => unitSet) I) := by + refine ⟨graph_mem_famSpace fun _ _ => by rw [univ_zero]; exact mem_univZero.mpr (Subset.refl _), + fun i hi x hx => ?_⟩ + rw [app_app_accStep (graph_mem_famSpace fun _ _ => by + rw [univ_zero]; exact mem_univZero.mpr (Subset.refl _)) hi] at hx + rw [app_graph hi] + obtain ⟨-, rfl⟩ := mem_truthVal.mp hx + exact pt_mem_unitSet + +end AccFam + +/-! ## The recursion theorem -/ + +section RecTheorem + +variable {ℓ : Nat} {I : V} {pred : V → V} {B : V → V} {st : V → V → V} {Cond : V → Prop} + +/-- **The local step**: at an index whose predecessors' fibres are all +singletons, the recursor's graph has exactly one value. -/ +theorem recGraph_singleton_of_preds (hB : ∀ i, i ∈ˢ I → B i ∈ˢ (univ ℓ : V)) + (hpred : ∀ i, i ∈ˢ I → pred i ⊆ˢ I) + {i : V} (hi : i ∈ˢ I) + (hst : ∀ g, g ∈ˢ piSet (pred i) (fun j => app (recGraph ℓ I pred B st) j) → st i g ∈ˢ B i) + (hP : ∀ j, j ∈ˢ pred i → (∃ v, v ∈ˢ app (recGraph ℓ I pred B st) j) ∧ + ∀ v v', v ∈ˢ app (recGraph ℓ I pred B st) j → v' ∈ˢ app (recGraph ℓ I pred B st) j → + v = v') : + (∃ v, v ∈ˢ app (recGraph ℓ I pred B st) i) ∧ + ∀ v v', v ∈ˢ app (recGraph ℓ I pred B st) i → v' ∈ˢ app (recGraph ℓ I pred B st) i → + v = v' := by + -- the choice function of the predecessors' values + have hchoice : ∀ j, ∃ v, j ∈ˢ pred i → v ∈ˢ app (recGraph ℓ I pred B st) j := fun j => + Classical.byCases (fun h : j ∈ˢ pred i => ⟨_, fun _ => Classical.choose_spec (hP j h).1⟩) + (fun h => ⟨pt, fun h' => absurd h' h⟩) + have hu : ∀ j, j ∈ˢ pred i → + Classical.choose (hchoice j) ∈ˢ app (recGraph ℓ I pred B st) j := + fun j hj => Classical.choose_spec (hchoice j) hj + have hg : graph (fun j => Classical.choose (hchoice j)) (pred i) + ∈ˢ piSet (pred i) (fun j => app (recGraph ℓ I pred B st) j) := graph_mem_piSet hu + -- any value at `i` is the step at that choice function + have key : ∀ v, v ∈ˢ app (recGraph ℓ I pred B st) i → + v = st i (graph (fun j => Classical.choose (hchoice j)) (pred i)) := by + intro v hv + rw [app_recGraph_eq hB hpred hi] at hv + obtain ⟨-, g', hg', rfl⟩ := mem_recGraphFibre.mp hv + congr 1 + rw [← eq_graph_app_of_mem_piSet hg'] + exact graph_congr fun j hj => (hP j hj).2 _ _ (app_mem_of_mem_piSet hg' hj) (hu j hj) + refine ⟨⟨st i (graph (fun j => Classical.choose (hchoice j)) (pred i)), ?_⟩, + fun v v' hv hv' => by rw [key v hv, key v' hv']⟩ + rw [app_recGraph_eq hB hpred hi] + exact mem_recGraphFibre.mpr ⟨hst _ hg, _, hg, rfl⟩ + +/-- The graph's selector: the fibre's element (the point off the graph). -/ +noncomputable def recSel (G : V) (i : V) : V := + open Classical in + if h : ∃ v, v ∈ˢ app G i then Classical.choose h else pt + +theorem recSel_mem {G i : V} (h : ∃ v, v ∈ˢ app G i) : recSel G i ∈ˢ app G i := by + unfold recSel + rw [dif_pos h] + exact Classical.choose_spec h + +/-- **The recursion equation** at an index whose fibre and whose +predecessors' fibres are singletons: the selector's value is the step +at the selector's graph over the predecessors. -/ +theorem recSel_eq (hB : ∀ i, i ∈ˢ I → B i ∈ˢ (univ ℓ : V)) + (hpred : ∀ i, i ∈ˢ I → pred i ⊆ˢ I) {i : V} (hi : i ∈ˢ I) + (hPi : ∃ v, v ∈ˢ app (recGraph ℓ I pred B st) i) + (hP : ∀ j, j ∈ˢ pred i → (∃ v, v ∈ˢ app (recGraph ℓ I pred B st) j) ∧ + ∀ v v', v ∈ˢ app (recGraph ℓ I pred B st) j → v' ∈ˢ app (recGraph ℓ I pred B st) j → + v = v') : + recSel (recGraph ℓ I pred B st) i + = st i (graph (fun j => recSel (recGraph ℓ I pred B st) j) (pred i)) := by + have hv := recSel_mem hPi + rw [app_recGraph_eq hB hpred hi] at hv + obtain ⟨-, g', hg', hst⟩ := mem_recGraphFibre.mp hv + rw [hst] + congr 1 + rw [← eq_graph_app_of_mem_piSet hg'] + exact graph_congr fun j hj => + (hP j hj).2 _ _ (app_mem_of_mem_piSet hg' hj) (recSel_mem (hP j hj).1) + +/-- **The recursion theorem by lfp induction**: at every index whose +`Acc`-family fibre is inhabited, the recursor's graph has exactly one +value. -/ +theorem recGraph_exists_unique (hB : ∀ i, i ∈ˢ I → B i ∈ˢ (univ ℓ : V)) + (hpred : ∀ i, i ∈ˢ I → pred i ⊆ˢ I) + (hst : ∀ i, i ∈ˢ I → ∀ g, g ∈ˢ piSet (pred i) (fun j => app (recGraph ℓ I pred B st) j) → + st i g ∈ˢ B i) : + ∀ i, i ∈ˢ I → ∀ x, x ∈ˢ app (accFam I pred Cond) i → + (∃ v, v ∈ˢ app (recGraph ℓ I pred B st) i) ∧ + ∀ v v', v ∈ˢ app (recGraph ℓ I pred B st) i → v' ∈ˢ app (recGraph ℓ I pred B st) i → + v = v' := by + refine lfpFamSet_induction ⟨_, accStep_closed (pred := pred) (Cond := Cond)⟩ (accStep_mono hpred) + (fun i _ => (∃ v, v ∈ˢ app (recGraph ℓ I pred B st) i) ∧ + ∀ v v', v ∈ˢ app (recGraph ℓ I pred B st) i → v' ∈ˢ app (recGraph ℓ I pred B st) i → + v = v') ?_ + intro i hi x hx + have hsubmem : graph (fun i => sep (app (lfpFamSet 0 I (accStep I pred Cond)) i) + (fun _ => (∃ v, v ∈ˢ app (recGraph ℓ I pred B st) i) ∧ + ∀ v v', v ∈ˢ app (recGraph ℓ I pred B st) i → v' ∈ˢ app (recGraph ℓ I pred B st) i → + v = v')) I ∈ˢ famSpace 0 I := + graph_mem_famSpace fun i hi => univ_sep_mem (famSpace_app (lfpFamSet_mem _ _ _) hi) + rw [app_app_accStep hsubmem hi] at hx + obtain ⟨⟨-, hall⟩, -⟩ := mem_truthVal.mp hx + refine recGraph_singleton_of_preds hB hpred hi (hst i hi) fun j hj => ?_ + obtain ⟨y, hy⟩ := hall j hj + rw [app_graph (hpred i hi j hj)] at hy + exact (mem_sep.mp hy).2 + +end RecTheorem + +end Ix.Theory.Model.SetTheory diff --git a/Ix/Theory/Model/SetModel/TaggedSum.lean b/Ix/Theory/Model/SetModel/TaggedSum.lean new file mode 100644 index 000000000..83620708b --- /dev/null +++ b/Ix/Theory/Model/SetModel/TaggedSum.lean @@ -0,0 +1,186 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetModel/TaggedSum.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetModel.TupleTower + +@[expose] public section + +/-! +# The tagged disjoint union (task #175 sum-types) + +The semantic carrier of a directly-installed inductive with **any +number of constructors other than one**: a value is the pair of a +**numeral tag** — the constructor's index, a finite ordinal +`vnat i ∈ ω` — and that constructor's tuple tower +(`Ix.Theory/Model/SetModel/TupleTower.lean`): + + sumSet w f = sigmaSet w ω (natFibre f) f i = the i-th tower + inj i a = spair (vnat i) a + +`natFibre f` is the fibre function over `ω`: `f i` at `vnat i`, junk +(`empty`) off the numerals — never consulted there, since every member +of `ω` is a unique numeral (`mem_omega_iff`/`vnat_inj`). The same +function is the **case split** the recursor performs: on a member +`inj i a` the recursor reads `i` back through `natFibre` and applies +the `i`-th branch to `a` (`sumRec`). + +Both regimes ride one definition: `sigmaSet` reads `w` only through +its zero test, so at `w = 0` the carrier is the truth value "some +constructor's tower is inhabited" (`sumSet_zero_elim`) with every +member the proof point, and above `0` it is the set of tagged pairs +(`sumSet_elim`). The tag domain `ω` sits in `univ w` for every `w ≥ 1` +(`omega_mem_univ_succ` + cumulativity), which is where the carrier's +formation lands (`sumSet_mem_univ`); at `w = 0` no bound is needed +(`sumSet_zero_mem_univZero`). + +Everything here is over the bare `SetTheory` interface; no syntax. +-/ + +namespace Ix.Theory.Model.SetTheory.Tower + +universe u + +variable {V : Type u} [SetTheory V] + +open Classical in +/-- The fibre function over the numerals: `f i` at `vnat i`, junk off +the numerals. -/ +noncomputable def natFibre (f : Nat → V) (k : V) : V := + if h : ∃ i, k = vnat i then f (Classical.choose h) else empty + +theorem natFibre_vnat (f : Nat → V) (i : Nat) : natFibre f (vnat i) = f i := by + unfold natFibre + rw [dif_pos ⟨i, rfl⟩] + congr 1 + exact (vnat_inj (Classical.choose_spec (⟨i, rfl⟩ : ∃ i', (vnat i : V) = vnat i'))).symm + +/-- Every member of `ω` is a numeral, at which the fibre is the +named set. -/ +theorem natFibre_of_mem (f : Nat → V) {k : V} (hk : k ∈ˢ (omega : V)) : + ∃ i, k = vnat i ∧ natFibre f k = f i := by + obtain ⟨i, rfl⟩ := mem_omega_iff.mp hk + exact ⟨i, rfl, natFibre_vnat f i⟩ + +/-- Fibre functions agreeing on the numerals agree on `ω`. -/ +theorem natFibre_congr {f g : Nat → V} (h : ∀ i, f i = g i) (k : V) : + natFibre f k = natFibre g k := by + unfold natFibre + split + · rw [h] + · rfl + +/-- **The tagged sum carrier.** -/ +noncomputable def sumSet (w : Nat) (f : Nat → V) : V := + sigmaSet w omega (natFibre f) + +/-- **The injection** of constructor `i`. -/ +noncomputable def inj (i : Nat) (a : V) : V := spair (vnat i) a + +/-- The recursor's case split: the branch selected by the tag, applied +to the payload (`natFibre` over the branches). -/ +noncomputable def sumRec (r : Nat → V → V) (x : V) : V := + natFibre (fun i => r i (ssnd x)) (sfst x) + +/-! ## Laws -/ + +theorem sumSet_congr {w : Nat} {f g : Nat → V} (h : ∀ i, f i = g i) : + sumSet w f = sumSet w g := by + unfold sumSet + exact sigma_congr fun k _ => natFibre_congr h k + +/-- **Intro** (graph regime): the injection of a fitting payload is in +the carrier. -/ +theorem inj_mem {w : Nat} (hw : w ≠ 0) {f : Nat → V} {i : Nat} {a : V} + (ha : a ∈ˢ f i) : inj i a ∈ˢ sumSet w f := by + unfold inj sumSet + refine spair_mem hw (vnat_mem_omega i) ?_ + rw [natFibre_vnat] + exact ha + +/-- **Intro** (squash regime): the carrier at `w = 0` holds the point +whenever some constructor's tower is inhabited. -/ +theorem pt_mem_sumSet_zero {f : Nat → V} {i : Nat} {a : V} (ha : a ∈ˢ f i) : + (pt : V) ∈ˢ sumSet 0 f := by + unfold sumSet + exact pt_mem_sigma (a := vnat i) (b := a) (vnat_mem_omega i) + (by rw [natFibre_vnat]; exact ha) + +/-- **Elim** (graph regime): every carrier member is the injection of +a fitting payload. -/ +theorem sumSet_elim {w : Nat} (hw : w ≠ 0) {f : Nat → V} {x : V} + (hx : x ∈ˢ sumSet w f) : ∃ i a, a ∈ˢ f i ∧ x = inj i a := by + unfold sumSet at hx + obtain ⟨k, a, hk, ha, -, hpos⟩ := mem_sigma_elim hx + obtain ⟨i, rfl, hfib⟩ := natFibre_of_mem f hk + rw [hfib] at ha + exact ⟨i, a, ha, hpos hw⟩ + +/-- **Elim** (squash regime): a `w = 0` carrier member is the point, +and some constructor's tower is inhabited. -/ +theorem sumSet_zero_elim {f : Nat → V} {x : V} (hx : x ∈ˢ sumSet 0 f) : + x = pt ∧ ∃ i a, a ∈ˢ f i := by + unfold sumSet at hx + obtain ⟨k, a, hk, ha, hz, -⟩ := mem_sigma_elim hx + obtain ⟨i, rfl, hfib⟩ := natFibre_of_mem f hk + rw [hfib] at ha + exact ⟨hz rfl, i, a, ha⟩ + +/-- **Tag disjointness and injectivity**: equal injections have equal +tags and payloads. -/ +theorem inj_inj {i j : Nat} {a b : V} (h : inj i a = inj j b) : i = j ∧ a = b := by + unfold inj at h + have h1 := congrArg sfst h + have h2 := congrArg ssnd h + rw [sfst_spair, sfst_spair] at h1 + rw [ssnd_spair, ssnd_spair] at h2 + exact ⟨vnat_inj h1, h2⟩ + +theorem sfst_inj (i : Nat) (a : V) : sfst (inj i a) = vnat i := sfst_spair _ _ +theorem ssnd_inj (i : Nat) (a : V) : ssnd (inj i a) = a := ssnd_spair _ _ + +/-- **Formation** (graph regime): with every tower in `univ w`, the +carrier is too — the tag domain `ω` sits in every `univ w` above `0`. -/ +theorem sumSet_mem_univ {w : Nat} (hw : w ≠ 0) {f : Nat → V} + (hf : ∀ i, f i ∈ˢ (univ w : V)) : sumSet w f ∈ˢ (univ w : V) := by + obtain ⟨w', rfl⟩ : ∃ w', w = w' + 1 := ⟨w - 1, by omega⟩ + have hω : (omega : V) ∈ˢ univ (w' + 1) := omega_mem_univ_succ w' + have h := sigma_mem_univ (u := w' + 1) (v := w' + 1) (B := natFibre f) hω (fun k hk => by + obtain ⟨i, rfl, hfib⟩ := natFibre_of_mem f hk + rw [hfib] + exact hf i) + rwa [show Nat.max (w' + 1) (w' + 1) = w' + 1 from Nat.max_self _] at h + +/-- **Formation** (squash regime), unconditional. -/ +theorem sumSet_zero_mem_univZero (f : Nat → V) : sumSet 0 f ∈ˢ (univZero : V) := by + unfold sumSet + rw [sigmaSet_zero] + exact truthVal_mem_univZero _ + +/-- **Iota** for the case split — unconditional. -/ +theorem sumRec_inj (r : Nat → V → V) (i : Nat) (a : V) : sumRec r (inj i a) = r i a := by + unfold sumRec + rw [sfst_inj, ssnd_inj, natFibre_vnat] + +/-- **Storage hygiene**: the carrier is never the proof point. -/ +theorem sumSet_ne_pt {w : Nat} (f : Nat → V) : sumSet w f ≠ (pt : V) := sigmaSet_ne_pt + +/-! ## Degeneracy checks -/ + +/-- Zero constructors: the carrier is empty in the graph regime. -/ +example {w : Nat} (hw : w ≠ 0) {x : V} (hx : x ∈ˢ sumSet w (fun _ => (empty : V))) : False := by + obtain ⟨i, a, ha, -⟩ := sumSet_elim hw hx + exact not_mem_empty a ha + +/-- Two constructors: the two injections are distinct. -/ +example (a b : V) : inj 0 a ≠ inj 1 b := fun h => by + have := (inj_inj h).1 + omega + +end Ix.Theory.Model.SetTheory.Tower diff --git a/Ix/Theory/Model/SetModel/TupleTower.lean b/Ix/Theory/Model/SetModel/TupleTower.lean new file mode 100644 index 000000000..dd3b3283b --- /dev/null +++ b/Ix/Theory/Model/SetModel/TupleTower.lean @@ -0,0 +1,424 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetModel/TupleTower.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetTheory.Derive.Sigma + +@[expose] public section + +/-! +# The uniform tuple model: unit-terminated pair towers (agent/tuple-model) + +The semantic carrier construction for directly-installed structures +(DESIGN.md, "AMENDMENT — the uniform tuple model"): every qualifying +structure's carrier is the **right-nested pair tower with unit +terminator** + + towerSet w ⟨F₀, …, F_{n−1}⟩ = F₀ ⋉ (F₁ ⋉ (… ⋉ (F_{n−1} ⋉ unitSet))) + +(each `⋉` a `sigmaSet w`, dependency right-nested along the field +telescope), the constructor is the uniform tupler `mkTower`, and the +semantic projection family is ONE definition depending only on the +index: + + projS i = sfst ∘ ssnd^i (structure-independent) + +The unit terminator is what buys index-only uniformity: with a bare +tail the last field would be read by `ssnd^{n−1}` (no `sfst`), making +the family depend on the arity — i.e. on the structure. It also +gives the 0/1-field degeneracies for free (`n = 0`: the carrier is +`unitSet`, members exactly `pt = mkTower []`; `n = 1`: members are +`spair a pt` and `projS 0 = sfst` is lawful). + +**ProjCoherence by uniform tupling**: the constructor is one +instantiation-independent function with global left inverses +(`kpair_inj` iterated), so the parked study's boundary predicate is +discharged by representation — tier form `mkTower_inj`, proved through +the iota law alone. + +**No `pw` datum — `pt` is separate from pairs** (user refinement, +2026-09-04): the interface keeps the proof point apart from every +Kuratowski pair (`pt_ne_kpair`, the `Derive/Pt.lean` selection +principle, surfaced as `sfst_pt`/`ssnd_pt`; the task-#109 pt-freshness +battery is the systematic form), so the destructors fix `pt` and +`projS i pt = pt` holds outright. A `Prop` structure's element +denotes `pt` and its proof fields denote `pt`, so projection-of-`pt` +is the *correct* answer by proof irrelevance — iota and membership +hold with one uniform `projS`, no per-structure variant. The squash +membership law carries the proof-field premise `PropS` (every field +set a truth value — the levelwise `structSort = 0 → fieldSort = 0` +bound); that is not a datum on the projection but the per-use +legality the checker's own `infer_proj` Prop restriction discharges. +The two regimes are disjoint by the same separation: graph-regime +members of a nonempty tower are never `pt` (`tower_mem_ne_pt`, from +`ptFresh_sigmaSet_pos`), squash members are exactly `pt`. + +Everything here is over the bare `SetTheory` interface; no syntax, no +environment. Kernel wiring is out of scope (post-B4; see the DESIGN +handoff record). +-/ + +namespace Ix.Theory.Model.SetTheory.Tower + +universe u + +/-- A dependent field-set telescope over `V`, indexed by its length: +field `i`'s set may depend on the values of fields `0..i−1`. -/ +inductive TeleS (V : Type u) : Nat → Type u where + | nil : TeleS V 0 + | cons {n : Nat} (A : V) (B : V → TeleS V n) : TeleS V (n + 1) + +variable {V : Type u} [SetTheory V] + +/-- `FitsS T as`: the value list `as` fits the telescope `T` — right +length, each value in its field's set at the earlier values. -/ +def FitsS : {n : Nat} → TeleS V n → List V → Prop + | _, .nil, [] => True + | _, .nil, _ :: _ => False + | _, .cons _ _, [] => False + | _, .cons A B, a :: as => a ∈ˢ A ∧ FitsS (B a) as + +/-- The carrier: the right-nested `sigmaSet w` tower over the +telescope, terminated by `unitSet`. -/ +noncomputable def towerSet (w : Nat) : {n : Nat} → TeleS V n → V + | _, .nil => unitSet + | _, .cons A B => sigmaSet w A (fun a => towerSet w (B a)) + +/-- The uniform tupler: the right-nested Kuratowski pair tower with +`pt` (the sole member of `unitSet`) as terminator. -/ +noncomputable def mkTower : List V → V + | [] => pt + | a :: as => spair a (mkTower as) + +/-- **The uniform semantic projection family**: `projS i = sfst ∘ +ssnd^i`. One definition for every structure and every index. -/ +noncomputable def projS : Nat → V → V + | 0, x => sfst x + | i + 1, x => projS i (ssnd x) + +/-- The first-`n` projection tuple of a value. -/ +noncomputable def projList : Nat → V → List V + | 0, _ => [] + | n + 1, x => sfst x :: projList n (ssnd x) + +/-- The `k`-fold second projection (task #210 Part A): the tuple tower +below `k` leading pair components — `projS (i + k) x = projS i (dropS k +x)`, so a projection table with offset `k` reads its fields off +`dropS k` of the subject (`k = 1` at the fixpoint route's tagged +tower, whose first component is the constructor tag). -/ +noncomputable def dropS : Nat → V → V + | 0, x => x + | k + 1, x => dropS k (ssnd x) + +theorem projS_add_dropS : ∀ (i k : Nat) (x : V), projS (i + k) x = projS i (dropS k x) + | _, 0, _ => rfl + | i, k + 1, x => by + show projS (i + k) (ssnd x) = projS i (dropS k (ssnd x)) + exact projS_add_dropS i k (ssnd x) + +theorem dropS_pt : ∀ k : Nat, dropS k (pt : V) = pt + | 0 => rfl + | k + 1 => by show dropS k (ssnd pt) = pt; rw [ssnd_pt, dropS_pt k] + +/-- The `i`-th field set of a telescope at a prefix valuation +(`empty` out of range — never consumed in range). -/ +noncomputable def teleNth : {n : Nat} → TeleS V n → Nat → List V → V + | _, .nil, _, _ => empty + | _, .cons A _, 0, _ => A + | _, .cons _ B, i + 1, a :: as => teleNth (B a) i as + | _, .cons _ _, _ + 1, [] => empty + +/-- `PropS T`: every field set is a truth value, hereditarily — the +levelwise `structSort = 0 → fieldSort = 0` bound, telescope-side. -/ +def PropS : {n : Nat} → TeleS V n → Prop + | _, .nil => True + | _, .cons A B => A ∈ˢ (univZero : V) ∧ ∀ a, a ∈ˢ A → PropS (B a) + +/-- `BoundS w T`: every field set lives in `univ w`, hereditarily — +the formation premise (per-field sorts `≤ w` via cumulativity). -/ +def BoundS (w : Nat) : {n : Nat} → TeleS V n → Prop + | _, .nil => True + | _, .cons A B => A ∈ˢ (univ w : V) ∧ ∀ a, a ∈ˢ A → BoundS w (B a) + +/-! ## Auxiliary lemmas -/ + +theorem FitsS.length_eq : ∀ {n} {T : TeleS V n} {as : List V}, + FitsS T as → as.length = n + | _, .nil, [], _ => rfl + | _, .cons _ _, _ :: _, h => + congrArg Nat.succ (FitsS.length_eq h.2) + +theorem projList_length : ∀ (n : Nat) (x : V), (projList n x).length = n + | 0, _ => rfl + | n + 1, x => congrArg Nat.succ (projList_length n (ssnd x)) + +theorem projList_get : ∀ (n i : Nat) (x : V) (h : i < n), + (projList n x)[i]'((projList_length n x).symm ▸ h) = projS i x + | _ + 1, 0, _, _ => rfl + | n + 1, i + 1, x, h => + projList_get n i (ssnd x) (Nat.lt_of_succ_lt_succ h) + +theorem projList_take : ∀ (n i : Nat) (x : V), i ≤ n → + (projList n x).take i = projList i x + | _, 0, _, _ => rfl + | n + 1, i + 1, x, h => by + show sfst x :: (projList n (ssnd x)).take i = sfst x :: projList i (ssnd x) + rw [projList_take n i (ssnd x) (Nat.le_of_succ_le_succ h)] + +theorem projList_mkTower : ∀ (n : Nat) (as : List V), as.length = n → + projList n (mkTower as) = as + | 0, [], _ => rfl + | n + 1, a :: as, h => by + show sfst (spair a (mkTower as)) :: projList n (ssnd (spair a (mkTower as))) + = a :: as + rw [sfst_spair, ssnd_spair, projList_mkTower n as (Nat.succ.inj h)] + +theorem projList_mkTower_append : ∀ (as bs : List V), projList as.length (mkTower (as ++ bs)) = as + | [], _ => rfl + | a :: as, bs => by + show sfst (spair a (mkTower (as ++ bs))) :: projList as.length (ssnd (spair a (mkTower (as ++ bs)))) + = a :: as + rw [sfst_spair, ssnd_spair, projList_mkTower_append as bs] + +theorem projList_pt : ∀ n : Nat, projList n (pt : V) = List.replicate n pt + | 0 => rfl + | n + 1 => by + show sfst (pt : V) :: projList n (ssnd (pt : V)) = pt :: List.replicate n pt + rw [sfst_pt, ssnd_pt, projList_pt n] + +/-- `projS` fixes `pt`: the interface's pt-vs-pair separation +(`sfst_pt`/`ssnd_pt`, from `pt_ne_kpair`) makes projection-of-`pt` +return `pt` — the correct proof-field answer, with no separate +`Prop`-structure variant. -/ +theorem projS_pt : ∀ i : Nat, projS i (pt : V) = pt + | 0 => sfst_pt + | i + 1 => by rw [projS, ssnd_pt, projS_pt i] + +/-- `FitsS` reads off `teleNth` membership: each fitting value is in +its field's set at the earlier values. -/ +theorem fitsS_mem_teleNth : ∀ {n} {T : TeleS V n} {as : List V}, + (hf : FitsS T as) → ∀ (i : Nat) (h : i < n), + as[i]'(FitsS.length_eq hf ▸ h) ∈ˢ teleNth T i (as.take i) + | _, .cons _ _, _ :: _, hf, 0, _ => hf.1 + | _, .cons _ B, a :: as, hf, i + 1, h => + fitsS_mem_teleNth (T := B a) (as := as) hf.2 i (Nat.lt_of_succ_lt_succ h) + +/-- `PropS` forces every fitting value to `pt`, so the all-`pt` list +fits whenever anything does. -/ +theorem fitsS_replicate_of_prop : ∀ {n} {T : TeleS V n} {as : List V}, + PropS T → FitsS T as → FitsS T (List.replicate n pt) + | _, .nil, [], _, _ => trivial + | _, .cons _ B, a :: as, hP, hf => by + have ha : a = pt := eq_pt_of_mem_univZero hP.1 hf.1 + show (pt : V) ∈ˢ _ ∧ FitsS (B pt) (List.replicate _ pt) + exact ⟨ha ▸ hf.1, ha ▸ fitsS_replicate_of_prop (hP.2 a hf.1) hf.2⟩ + +/-! ## The law families (frozen in the DESIGN amendment) -/ + +/-- **Intro** (graph regime): a fitting tuple's tower is in the +carrier. -/ +theorem mkTower_mem {w : Nat} (hw : w ≠ 0) : + ∀ {n} {T : TeleS V n} {as : List V}, + FitsS T as → mkTower as ∈ˢ towerSet w T + | _, .nil, [], _ => pt_mem_unitSet + | _, .cons _ B, a :: _, hf => + spair_mem hw hf.1 (mkTower_mem hw (T := B a) hf.2) + +/-- **Intro** (squash regime): the carrier at `w = 0` is the truth +value of fittability. -/ +theorem pt_mem_tower : ∀ {n} {T : TeleS V n} {as : List V}, + FitsS T as → (pt : V) ∈ˢ towerSet 0 T + | _, .nil, [], _ => pt_mem_unitSet + | _, .cons _ B, a :: _, hf => + pt_mem_sigma hf.1 (pt_mem_tower (T := B a) hf.2) + +/-- **Iota — UNCONDITIONAL**: no membership premise, no level. The +checker's `.proj`/`mk` reduction is denotation-sound with no typing of +the fields at all. -/ +theorem projS_mkTower : ∀ (i : Nat) (as : List V) (h : i < as.length), + projS i (mkTower as) = as[i] + | 0, a :: as, _ => by + show sfst (spair a (mkTower as)) = a + exact sfst_spair a (mkTower as) + | i + 1, a :: as, h => by + show projS i (ssnd (spair a (mkTower as))) = as[i]'(Nat.lt_of_succ_lt_succ h) + rw [ssnd_spair] + exact projS_mkTower i as (Nat.lt_of_succ_lt_succ h) + +/-- The projection list of a point-terminated tower is the fields' +prefix (task #210: the fixpoint route's constructor payload). -/ +theorem projList_mkTower_take {fs : List V} {i : Nat} (hi : i ≤ fs.length) : + projList i (mkTower (fs ++ [pt])) = fs.take i := by + have h1 : projList (fs.length + 1) (mkTower (fs ++ [pt])) = fs ++ [pt] := projList_mkTower _ _ (by simp) + have h2 := projList_take (fs.length + 1) i (mkTower (fs ++ [pt])) (by omega) + rw [h1, List.take_append_of_le_length hi] at h2 + exact h2.symm + +/-- A field projection of a point-terminated tower, `getD`-form. -/ +theorem projS_mkTower_getD {fs : List V} {i : Nat} (hi : i < fs.length) : + projS i (mkTower (fs ++ [pt])) = fs.getD i pt := by + rw [projS_mkTower i (fs ++ [pt]) (by simp; omega), List.getElem_append_left hi, + List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hi, Option.getD_some] + +/-- **Eta + elim** (graph regime): every carrier member IS the tower +of its own projections, and those projections fit the telescope. -/ +theorem towerSet_elim {w : Nat} (hw : w ≠ 0) : + ∀ {n} (T : TeleS V n) {x : V}, x ∈ˢ towerSet w T → + FitsS T (projList n x) ∧ x = mkTower (projList n x) + | _, .nil, x, hx => ⟨trivial, mem_unitSet_iff.mp hx⟩ + | _, .cons A B, x, hx => by + obtain ⟨a, b, ha, hb, -, hpos⟩ := + mem_sigma_elim (A := A) (B := fun a => towerSet w (B a)) hx + have hx' : x = spair a b := hpos hw + subst hx' + obtain ⟨hfit, heta⟩ := towerSet_elim hw (B a) hb + constructor + · show sfst (spair a b) ∈ˢ A ∧ FitsS (B (sfst (spair a b))) + (projList _ (ssnd (spair a b))) + rw [sfst_spair, ssnd_spair] + exact ⟨ha, hfit⟩ + · show spair a b + = spair (sfst (spair a b)) (mkTower (projList _ (ssnd (spair a b)))) + rw [sfst_spair, ssnd_spair, ← heta] + +/-- **Elim** (squash regime): a `w = 0` carrier member is `pt`, and +some tuple fits. -/ +theorem towerSet_zero_elim : ∀ {n} (T : TeleS V n) {x : V}, + x ∈ˢ towerSet 0 T → x = pt ∧ ∃ as, FitsS T as + | _, .nil, x, hx => ⟨mem_unitSet_iff.mp hx, [], trivial⟩ + | _, .cons A B, x, hx => by + obtain ⟨a, b, ha, hb, hz, -⟩ := + mem_sigma_elim (A := A) (B := fun a => towerSet 0 (B a)) hx + obtain ⟨-, as, hfit⟩ := towerSet_zero_elim (B a) hb + exact ⟨hz rfl, a :: as, ha, hfit⟩ + +/-- **Membership** (graph regime): the `i`-th projection lands in the +`i`-th field set at the earlier projections. -/ +theorem projS_mem {w : Nat} (hw : w ≠ 0) {n} {T : TeleS V n} {x : V} + (hx : x ∈ˢ towerSet w T) (i : Nat) (h : i < n) : + projS i x ∈ˢ teleNth T i (projList i x) := by + obtain ⟨hfit, -⟩ := towerSet_elim hw T hx + have hm := fitsS_mem_teleNth hfit i h + rw [projList_take n i x (Nat.le_of_lt h)] at hm + rw [← projList_get n i x h] + exact hm + +/-- **Membership** (squash regime): the SAME statement, with the +proof-field premise `PropS` in place of `w ≠ 0` — every member is +`pt`, `projS` fixes it, and every truth-value field set at the +all-`pt` prefix contains it (proof irrelevance, semantically). -/ +theorem projS_mem_zero {n} {T : TeleS V n} {x : V} (hP : PropS T) + (hx : x ∈ˢ towerSet 0 T) (i : Nat) (h : i < n) : + projS i x ∈ˢ teleNth T i (projList i x) := by + obtain ⟨rfl, as, hfit⟩ := towerSet_zero_elim T hx + have hrep := fitsS_replicate_of_prop hP hfit + have hm := fitsS_mem_teleNth hrep i h + rw [List.take_replicate, Nat.min_eq_left (Nat.le_of_lt h), + List.getElem_replicate] at hm + rw [projS_pt, projList_pt] + exact hm + +/-- **Coherence by uniform tupling**: `ProjCoh` holds by +representation — equal towers of equal arity have equal components. +Proved through the iota law alone (`kpair_inj` iterated). -/ +theorem mkTower_inj {as bs : List V} (hlen : as.length = bs.length) + (h : mkTower as = mkTower bs) : as = bs := by + apply List.ext_getElem hlen + intro i h1 h2 + rw [← projS_mkTower i as h1, ← projS_mkTower i bs h2, h] + +/-- **The recursor, DERIVED** (graph regime): large-elimination typing +and iota for `r := m ∘ projList n`, with eta (`towerSet_elim`) as the +load-bearing step of the typing — the probe's `builtModel_recElimU2`, +promoted to every arity and dependency shape. -/ +theorem towerRec {w : Nat} (hw : w ≠ 0) {n} (T : TeleS V n) + (M : V → V) (m : List V → V) + (hm : ∀ as, FitsS T as → m as ∈ˢ M (mkTower as)) : + ∃ r : V → V, (∀ x, x ∈ˢ towerSet w T → r x ∈ˢ M x) ∧ + (∀ as, FitsS T as → r (mkTower as) = m as) := by + refine ⟨fun x => m (projList n x), fun x hx => ?_, fun as hf => ?_⟩ + · obtain ⟨hfit, heta⟩ := towerSet_elim hw T hx + have := hm _ hfit + rwa [← heta] at this + · show m (projList n (mkTower as)) = m as + rw [projList_mkTower n as hf.length_eq] + +/-- **The recursor at squash**: constant elimination is lawful — the +semantic form of subsingleton elimination (the minor's value must be +instantiation-independent, which all-proof-field instantiations +satisfy with `m = pt`). -/ +theorem towerRec_zero {n} (T : TeleS V n) (M : V → V) (m : V) + (hm : (∃ as, FitsS T as) → m ∈ˢ M pt) : + ∀ x, x ∈ˢ towerSet 0 T → m ∈ˢ M x := by + intro x hx + obtain ⟨rfl, hex⟩ := towerSet_zero_elim T hx + exact hm hex + +/-- **Formation**: the carrier lives at the structure's own level, +given the per-field bound (cumulativity is applied by the consumer +when a field's sort is `< w`). -/ +theorem towerSet_mem_univ {w : Nat} : ∀ {n} (T : TeleS V n), + BoundS w T → towerSet w T ∈ˢ (univ w : V) + | _, .nil, _ => unitSet_mem_univ w + | _, .cons A B, hb => by + simp only [towerSet] + have h := sigma_mem_univ (u := w) (v := w) hb.1 + (fun a ha => towerSet_mem_univ (B a) (hb.2 a ha)) + rwa [show Nat.max w w = w from Nat.max_self w] at h + +/-- **Formation, squash regime — UNCONDITIONAL**: at `w = 0` the +carrier is a truth value with no field bounds at all (`sigmaSet 0` +truncates whatever its arguments are), so definitely-`Prop` +structures with arbitrary-sorted data fields (`Exists`) still get a +lawful carrier; only their *projections* wait on `PropS`. -/ +theorem towerSet_zero_mem_univZero : ∀ {n} (T : TeleS V n), + towerSet 0 T ∈ˢ (univZero : V) + | _, .nil => univ_zero (V := V) ▸ unitSet_mem_univ 0 + | _, .cons _ B => by + show sigmaSet 0 _ (fun a => towerSet 0 (B a)) ∈ˢ (univZero : V) + rw [sigmaSet_zero] + exact truthVal_mem_univZero _ + +/-- **Storage hygiene**: a tower carrier is never the proof point +(the task-#100 collapse-era non-`pt`-ness of stored values). -/ +theorem towerSet_ne_pt {w : Nat} : ∀ {n} (T : TeleS V n), + towerSet w T ≠ (pt : V) + | _, .nil => fun h => + truthVal_ne_pt True ((truthVal_eq_unitSet trivial).trans h) + | _, .cons _ _ => sigmaSet_ne_pt + +/-- **The 0-field degeneracy**: the empty tower is `unitSet` at every +level — hence unit-likeness (next lemma) and, at `w = 0`, the correct +truth value `⟦True⟧`. -/ +theorem towerSet_nil {w : Nat} : towerSet w (.nil : TeleS V 0) = unitSet := by + rfl + +/-- Unit-likeness at `n = 0`: any two members are equal. -/ +theorem tower_nil_unitlike {w : Nat} {x y : V} + (hx : x ∈ˢ towerSet w (.nil : TeleS V 0)) + (hy : y ∈ˢ towerSet w (.nil : TeleS V 0)) : x = y := + (mem_unitSet_iff.mp hx).trans (mem_unitSet_iff.mp hy).symm + +/-! ## Degeneracy checks (build-time regressions) -/ + +/-- `n = 1`: the terminator makes `projS 0 = sfst` lawful — the sole +field of a one-field tower reads back. -/ +example (a : V) : projS 0 (mkTower [a]) = a := + projS_mkTower 0 [a] (by simp) + +/-- `n = 2`: both indices read back through the one uniform family. -/ +example (a b : V) : projS 0 (mkTower [a, b]) = a ∧ projS 1 (mkTower [a, b]) = b := + ⟨projS_mkTower 0 [a, b] (by simp), projS_mkTower 1 [a, b] (by simp)⟩ + +/-- `n = 0`: the empty tower's sole member is `mkTower []`. -/ +example {w : Nat} {x : V} (hx : x ∈ˢ towerSet w (.nil : TeleS V 0)) : + x = mkTower [] := + mem_unitSet_iff.mp hx + +end Ix.Theory.Model.SetTheory.Tower diff --git a/Ix/Theory/Model/SetTheory/Core.lean b/Ix/Theory/Model/SetTheory/Core.lean new file mode 100644 index 000000000..f6baf1797 --- /dev/null +++ b/Ix/Theory/Model/SetTheory/Core.lean @@ -0,0 +1,166 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetTheory/Core.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +@[expose] public section + +/-! +# The axiomatic core: ZF⁻ plus an ω-chain of Grothendieck universes + +The `SetTheory` class below is the *entire* axiomatic interface of the +consistency proof; every operator and law the model construction uses +(`Ix.Theory/Model/SetTheory/Basic.lean`) is *derived* from it in +`Ix.Theory/Model/SetTheory/Derive/*`, never assumed. + +The set-theoretic axioms are **extensionality, pairing, union, power +set, regularity, the replacement scheme, and an ω-chain of +Grothendieck universes** `univChain 0 ∈ univChain 1 ∈ …` — +universehood stated as the matrix of Tarski's Axiom A (A. Tarski, +*Über unerreichbare Kardinalzahlen*, Fund. Math. 30 (1938), 68–89) +strengthened with a transitivity clause, i.e. as Grothendieck +universes (SGA 4, Exp. I, Appendix). Infinity and choice are +derivable and therefore absent. + +This calibrates the axiomatic strength to exactly what the checker +consumes — the ω-indexed tower `univ 0, univ 1, univ 2, …` — and to +the known consistency strength of Lean itself: ZFC plus a strictly +increasing ω-sequence of inaccessible cardinals — the +`OmegaInaccessibles` hypothesis +`∃ κ : ℕ → Cardinal, StrictMono κ ∧ ∀ n, (κ n).IsInaccessible` +of Mario Carneiro, *The Type Theory of Lean*, master's thesis, +Carnegie Mellon University, 2019, §1.2. Under that hypothesis the intended +model takes `univChain n := V_{κ n}`. This is strictly weaker than +full Tarski–Grothendieck set theory (Tarski's Axiom A places a +universe above *every* set, a proper class of inaccessibles; cf. the +Mizar axiomatics, A. Trybulec, *Tarski Grothendieck Set Theory*, +Formalized Mathematics 1(1), 1990). + +Deliberate deviations from a first-order presentation: + +* **Replacement** is a Lean-level scheme: the image operator takes an + arbitrary function `V → V`. This is the usual strengthening when the + ambient logic can quantify over class functions; `V_κ` for `κ` + inaccessible still satisfies it. +* **Choice is not a field.** A first-order axiomatization must assert + choice; here the ambient logic is Lean with `Classical.choice`, and + every set-level form of choice over `V` (the global selector + `schoice`, and the Jech-form choice-function statement, *Set Theory*, + §5) is a *theorem* — replacement applied to a classically chosen + selector. See `Ix.Theory/Model/SetTheory/Derive/Choice.lean`. Asserting it + here would add redundant axiomatic content; global choice is supplied + by the meta-logic, not by this class. +* **No `nonempty` field.** First-order logic's nonempty domain is + implied: `univChain` already exhibits elements of `V`. + +Everything else — the empty set, separation, ordered pairs, infinity, +function graphs, the universe tower, quotients — is constructed in +`Ix.Theory/Model/SetTheory/Derive/*`. +-/ + +namespace Ix.Theory.Model + +universe u + +/-- `y` and `u` are equinumerous: some (meta-level) function restricts to +a bijection from the members of `y` onto the members of `u`. This is the +notion Tarski's Axiom A is stated with; using a Lean-level function keeps +ordered pairs out of the core (a first-order presentation instead +describes set-level bijections by formulas). For the intended models this is +equivalent: a set-level bijection yields a meta-level one by choice, and +the axiom's disjunction is only ever *used* by refuting this side via a +diagonal argument (`Derive/Universe.lean`). -/ +def Equinumerous {V : Type u} (mem : V → V → Prop) (y u : V) : Prop := + ∃ f : V → V, + (∀ z, mem z y → mem (f z) u) ∧ + (∀ z z', mem z y → mem z' y → f z = f z' → z = z') ∧ + (∀ w, mem w u → ∃ z, mem z y ∧ f z = w) + +/-- The matrix of Tarski's Axiom A (Tarski 1938), strengthened with the +transitivity clause: `u` is a Grothendieck universe (SGA 4, Exp. I, +Appendix). The four clauses, in order: + +1. *transitivity*: members of members are members — the clause that + distinguishes a Grothendieck universe from a bare Tarski one, and + what makes the empty set fall out of regularity; +2. *subsets of members are members*; +3. *power sets stay inside*: some member contains all subsets of a + member (with clause 2 this makes `power y` itself a member); +4. *inaccessibility*: a subset of `u` is equinumerous with `u` or a + member. Dropping the equinumerosity disjunct is **inconsistent** + (`u ⊆ u` would force `u ∈ u`, against regularity). -/ +def IsTGUniverse {V : Type u} (mem : V → V → Prop) (u : V) : Prop := + (∀ y z, mem y u → mem z y → mem z u) ∧ + (∀ y z, mem y u → (∀ w, mem w z → mem w y) → mem z u) ∧ + (∀ y, mem y u → ∃ p, mem p u ∧ ∀ z, (∀ w, mem w z → mem w y) → mem z p) ∧ + (∀ y, (∀ w, mem w y → mem w u) → Equinumerous mem y u ∨ mem y u) + +/-- A model of set theory of exactly the strength the checker needs: +membership, the six ZF⁻ axioms (extensionality, pairing, union, power +set, regularity, Lean-level replacement), and an ω-chain of +Grothendieck universes. Infinity is derivable; choice is inherited +from the meta-logic (`Classical.choice`); see the module docstring. -/ +class SetTheory (V : Type u) where + /-- Set membership. -/ + Mem : V → V → Prop + /-- Extensionality: sets with the same members are equal. -/ + ext : ∀ {x y : V}, (∀ z, Mem z x ↔ Mem z y) → x = y + /-- Pairing: the unordered pair. -/ + upair : V → V → V + /-- Characterization of the unordered pair. -/ + mem_upair : ∀ {z a b : V}, Mem z (upair a b) ↔ z = a ∨ z = b + /-- Union: the union of the members. -/ + sUnion : V → V + /-- Characterization of the union. -/ + mem_sUnion : ∀ {z x : V}, Mem z (sUnion x) ↔ ∃ y, Mem y x ∧ Mem z y + /-- Power set. -/ + power : V → V + /-- Characterization of the power set: members are the subsets. -/ + mem_power : ∀ {z x : V}, Mem z (power x) ↔ ∀ w, Mem w z → Mem w x + /-- Regularity: every nonempty set has an `∈`-minimal member. -/ + regularity : ∀ x : V, (∃ y, Mem y x) → ∃ y, Mem y x ∧ ¬ ∃ z, Mem z y ∧ Mem z x + /-- Replacement, as a Lean-level scheme: the image of a set under an + arbitrary function `V → V`. -/ + image : (V → V) → V → V + /-- Characterization of the replacement image. -/ + mem_image : ∀ {f : V → V} {a z : V}, Mem z (image f a) ↔ ∃ w, Mem w a ∧ z = f w + /-- An ω-chain of Grothendieck universes: the sets interpreting the + universe tower (Carneiro's ω-many inaccessibles, op. cit.; + intended model `V_{κ n}`). -/ + univChain : Nat → V + /-- The chain increases strictly: each universe is a member of the + next. -/ + univChain_mem : ∀ n : Nat, Mem (univChain n) (univChain (n + 1)) + /-- Each chain member is a Grothendieck universe (Tarski's Axiom A + matrix with transitivity, `IsTGUniverse`). -/ + univChain_tg : ∀ n : Nat, IsTGUniverse Mem (univChain n) + +namespace SetTheory + +@[inherit_doc] scoped infix:50 " ∈ˢ " => Mem + +variable {V : Type u} [SetTheory V] + +/-- Subset, from membership. -/ +protected def Subset (x y : V) : Prop := ∀ z, z ∈ˢ x → z ∈ˢ y + +@[inherit_doc] scoped infix:50 " ⊆ˢ " => SetTheory.Subset + +theorem Subset.refl (x : V) : x ⊆ˢ x := fun _ hz => hz + +theorem Subset.trans {x y z : V} (h₁ : x ⊆ˢ y) (h₂ : y ⊆ˢ z) : x ⊆ˢ z := + fun w hw => h₂ w (h₁ w hw) + +theorem Subset.antisymm {x y : V} (h₁ : x ⊆ˢ y) (h₂ : y ⊆ˢ x) : x = y := + ext fun z => ⟨h₁ z, h₂ z⟩ + +theorem mem_power_iff_subset {z x : V} : z ∈ˢ power x ↔ z ⊆ˢ x := mem_power + +end SetTheory + +end Ix.Theory.Model diff --git a/Ix/Theory/Model/SetTheory/Derive/Choice.lean b/Ix/Theory/Model/SetTheory/Derive/Choice.lean new file mode 100644 index 000000000..1b58066b5 --- /dev/null +++ b/Ix/Theory/Model/SetTheory/Derive/Choice.lean @@ -0,0 +1,75 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetTheory/Derive/Choice.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetTheory.Derive.Graphs + +@[expose] public section + +/-! +# Choice: the global selector, and the eighth axiom as a theorem + +A first-order development of Tarski–Grothendieck set theory asserts (or +derives from Axiom A) an axiom of choice, because it cannot reach the +meta-level. Here the meta-logic is Lean with `Classical.choice`, so +choice over `V` is *derived*: + +* `schoice : V → V` — a global selector, `schoice A ∈ A` whenever `A` + is inhabited (Lean-level choice on the membership predicate); +* `set_choice` — the Jech-form statement (*Set Theory*, §5): every + family has a *set* choice function, obtained as the replacement + graph of `schoice`, spelled through `kpair` as a single-valued set + of ordered pairs. +-/ + +namespace Ix.Theory.Model.SetTheory + +universe u + +variable {V : Type u} [SetTheory V] + +open Classical in +/-- Global choice: a uniform selection from nonempty sets. On sets +without members (and only there) it returns the set itself. -/ +noncomputable def schoice (A : V) : V := + if h : ∃ x, x ∈ˢ A then Classical.choose h else A + +theorem schoice_mem {A x : V} (hx : x ∈ˢ A) : schoice A ∈ˢ A := by + unfold schoice + rw [dif_pos ⟨x, hx⟩] + exact Classical.choose_spec (⟨x, hx⟩ : ∃ x, x ∈ˢ A) + +/-- The axiom of choice, Jech-form, as a theorem: every family `X` has +a set-level choice function — a single-valued set of pairs, total on +`X`, selecting a member from every inhabited `A ∈ X`. -/ +theorem set_choice (X : V) : + ∃ f : V, + (∀ A c, kpair A c ∈ˢ f → A ∈ˢ X) ∧ + (∀ A, A ∈ˢ X → ∃ c, kpair A c ∈ˢ f ∧ ∀ c', kpair A c' ∈ˢ f → c' = c) ∧ + (∀ A c, kpair A c ∈ˢ f → (∃ x, x ∈ˢ A) → c ∈ˢ A) := by + refine ⟨graph schoice X, ?_, ?_, ?_⟩ + · intro A c hp + obtain ⟨A', hA', hp'⟩ := mem_graph.mp hp + obtain ⟨rfl, -⟩ := kpair_inj hp' + exact hA' + · intro A hA + refine ⟨schoice A, mem_graph.mpr ⟨A, hA, rfl⟩, ?_⟩ + intro c' hc' + obtain ⟨A', -, hp'⟩ := mem_graph.mp hc' + obtain ⟨rfl, rfl⟩ := kpair_inj hp' + rfl + · intro A c hp ⟨x, hx⟩ + obtain ⟨A', -, hp'⟩ := mem_graph.mp hp + obtain ⟨rfl, rfl⟩ := kpair_inj hp' + exact schoice_mem hx + +/- Opaque interface operator (see `Derive/Empty.lean`). -/ +attribute [irreducible] schoice + +end Ix.Theory.Model.SetTheory diff --git a/Ix/Theory/Model/SetTheory/Derive/Empty.lean b/Ix/Theory/Model/SetTheory/Derive/Empty.lean new file mode 100644 index 000000000..2c202a614 --- /dev/null +++ b/Ix/Theory/Model/SetTheory/Derive/Empty.lean @@ -0,0 +1,79 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetTheory/Derive/Empty.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetTheory.Core + +@[expose] public section + +/-! +# The empty set, derived + +The transitivity clause in `IsTGUniverse` makes this cheap — +`univChain 1` is inhabited (by `univChain 0`) and transitive, so +regularity's `∈`-minimal member of it has no members at all. + +Also here: the small consequences of regularity everything downstream +wants — `x ∉ x` and the impossibility of membership 2-cycles. +-/ + +namespace Ix.Theory.Model.SetTheory + +universe u + +variable {V : Type u} [SetTheory V] + +theorem empty_exists : ∃ e : V, ∀ z, ¬ z ∈ˢ e := by + obtain ⟨htrans, -, -, -⟩ := univChain_tg (V := V) 1 + obtain ⟨y, hy, hmin⟩ := + regularity (univChain (V := V) 1) ⟨univChain 0, univChain_mem 0⟩ + exact ⟨y, fun z hz => hmin ⟨z, hz, htrans y z hy hz⟩⟩ + +/-- The empty set. -/ +noncomputable def empty : V := Classical.choose empty_exists + +theorem not_mem_empty (z : V) : ¬ z ∈ˢ (empty : V) := + Classical.choose_spec empty_exists z + +theorem eq_empty {x : V} (h : ∀ z, ¬ z ∈ˢ x) : x = empty := + ext fun z => ⟨fun hz => absurd hz (h z), fun hz => absurd hz (not_mem_empty z)⟩ + +theorem eq_empty_iff {x : V} : x = empty ↔ ∀ z, ¬ z ∈ˢ x := + ⟨fun h z => h ▸ not_mem_empty z, eq_empty⟩ + +theorem ne_empty_of_mem {x z : V} (h : z ∈ˢ x) : x ≠ empty := + fun he => not_mem_empty z (he ▸ h) + +theorem nonempty_of_ne_empty {x : V} (h : x ≠ empty) : ∃ z, z ∈ˢ x := + Classical.byContradiction fun hn => h (eq_empty fun z hz => hn ⟨z, hz⟩) + +theorem empty_subset (x : V) : (empty : V) ⊆ˢ x := + fun z hz => absurd hz (not_mem_empty z) + +/-- No set is a member of itself (regularity at `{x}`). -/ +theorem not_mem_self (x : V) : ¬ x ∈ˢ x := by + intro hx + obtain ⟨y, hy, hmin⟩ := regularity (upair x x) ⟨x, mem_upair.mpr (Or.inl rfl)⟩ + have hyx : y = x := by rcases mem_upair.mp hy with h | h <;> exact h + subst hyx + exact hmin ⟨y, hx, mem_upair.mpr (Or.inl rfl)⟩ + +/- Interface operators are opaque from here on (as the legacy class +projections were): consumers reason only through the laws, never by +unfolding. Everything above this line may use the definition. -/ +attribute [irreducible] empty + +/-- No membership 2-cycles (regularity at `{a, b}`). -/ +theorem no_two_cycle {a b : V} (hab : a ∈ˢ b) (hba : b ∈ˢ a) : False := by + obtain ⟨y, hy, hmin⟩ := regularity (upair a b) ⟨a, mem_upair.mpr (Or.inl rfl)⟩ + rcases mem_upair.mp hy with h | h <;> subst h + · exact hmin ⟨b, hba, mem_upair.mpr (Or.inr rfl)⟩ + · exact hmin ⟨a, hab, mem_upair.mpr (Or.inl rfl)⟩ + +end Ix.Theory.Model.SetTheory diff --git a/Ix/Theory/Model/SetTheory/Derive/Graphs.lean b/Ix/Theory/Model/SetTheory/Derive/Graphs.lean new file mode 100644 index 000000000..4449e747b --- /dev/null +++ b/Ix/Theory/Model/SetTheory/Derive/Graphs.lean @@ -0,0 +1,252 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetTheory/Derive/Graphs.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetTheory.Derive.Pt + +@[expose] public section + +/-! +# Function graphs, application, and the raw dependent-function set + +* `graph F A = {⟨x, F x⟩ : x ∈ A}` — the set-theoretic function graph + (replacement); never equal to `pt`, since `pt`'s element `ptTag` has + an empty member and so is not a Kuratowski pair. +* `app f a = ⋃ {y : ⟨a, y⟩ ∈ f}` — untagged application on non-`pt` + arguments; `app pt a = pt` is the tag that makes proofs degenerate + (`app_pt` in the `SetTheory` interface). +* `sigmaPairs A B = {⟨x, y⟩ : x ∈ A, y ∈ B x}` — the raw dependent + pair set (also the `w ≠ 0` sigma). +* `piSet A B ⊆ power (sigmaPairs A B)` — the total single-valued + graphs: the `v ≠ 0` dependent product. + +The level-`0` truncations (`lam 0 = pt`, `pi 0` a truth value) were +layered on top in `Derive/Pi.lean`, deleted at task #221: the model +reads the *annotation-driven* `piR`/`lamR` (`SetModel/Ops.lean`), which +dispatch on the annotation instead of collapsing. +-/ + +namespace Ix.Theory.Model.SetTheory + +universe u + +variable {V : Type u} [SetTheory V] + +/-- The function graph `{⟨x, F x⟩ : x ∈ A}`. -/ +noncomputable def graph (F : V → V) (A : V) : V := + image (fun x => kpair x (F x)) A + +theorem mem_graph {F : V → V} {A p : V} : + p ∈ˢ graph F A ↔ ∃ x, x ∈ˢ A ∧ p = kpair x (F x) := mem_image + +theorem graph_ne_pt {F : V → V} {A : V} : graph F A ≠ (pt : V) := by + intro h + obtain ⟨x, -, hx⟩ := + mem_graph.mp (h ▸ ptTag_mem_pt : (ptTag : V) ∈ˢ graph F A) + exact ptTag_ne_kpair x (F x) hx + +theorem graph_congr {F F' : V → V} {A : V} (h : ∀ x, x ∈ˢ A → F x = F' x) : + graph F A = graph F' A := + image_congr fun x hx => by rw [h x hx] + +open Classical in +/-- Tagged set-theoretic application: the union of the values paired +with `a` in `f` — except at the proof point, which applies to `pt` +again. -/ +noncomputable def app (f a : V) : V := + if f = pt then pt else sUnion (sep (sUnion (sUnion f)) (fun y => kpair a y ∈ˢ f)) + +theorem app_pt (a : V) : app (pt : V) a = pt := by + unfold app; exact if_pos rfl + +/-- Application computes on single-valued positions. -/ +theorem app_eq_of_unique {f a b : V} (hf : f ≠ pt) (hab : kpair a b ∈ˢ f) + (huniq : ∀ y, kpair a y ∈ˢ f → y = b) : app f a = b := by + unfold app + rw [if_neg hf] + have : sep (sUnion (sUnion f)) (fun y => kpair a y ∈ˢ f) = sing b := by + apply ext fun z => ?_ + rw [mem_sep, mem_sing] + constructor + · exact fun ⟨_, hz⟩ => huniq z hz + · rintro rfl + refine ⟨?_, hab⟩ + exact mem_sUnion.mpr ⟨upair a z, mem_sUnion.mpr ⟨kpair a z, hab, mem_upair_right _ _⟩, + mem_upair_right a z⟩ + rw [this, sUnion_sing] + +/-- Beta on graphs. -/ +theorem app_graph {F : V → V} {A a : V} (ha : a ∈ˢ A) : + app (graph F A) a = F a := by + refine app_eq_of_unique graph_ne_pt (mem_graph.mpr ⟨a, ha, rfl⟩) ?_ + intro y hy + obtain ⟨x, -, hx⟩ := mem_graph.mp hy + obtain ⟨rfl, rfl⟩ := kpair_inj hx + rfl + +/-- The raw dependent pair set `{⟨x, y⟩ : x ∈ A, y ∈ B x}`. -/ +noncomputable def sigmaPairs (A : V) (B : V → V) : V := + sUnion (image (fun x => image (fun y => kpair x y) (B x)) A) + +theorem mem_sigmaPairs {A p : V} {B : V → V} : + p ∈ˢ sigmaPairs A B ↔ ∃ x, x ∈ˢ A ∧ ∃ y, y ∈ˢ B x ∧ p = kpair x y := by + unfold sigmaPairs + rw [mem_sUnion] + constructor + · rintro ⟨s, hs, hps⟩ + obtain ⟨x, hx, rfl⟩ := mem_image.mp hs + obtain ⟨y, hy, rfl⟩ := mem_image.mp hps + exact ⟨x, hx, y, hy, rfl⟩ + · rintro ⟨x, hx, y, hy, rfl⟩ + exact ⟨image (fun y => kpair x y) (B x), mem_image.mpr ⟨x, hx, rfl⟩, + mem_image.mpr ⟨y, hy, rfl⟩⟩ + +theorem sigmaPairs_congr {A : V} {B B' : V → V} + (h : ∀ x, x ∈ˢ A → B x = B' x) : sigmaPairs A B = sigmaPairs A B' := by + unfold sigmaPairs + congr 1 + exact image_congr fun x hx => by rw [h x hx] + +theorem _root_.Ix.Theory.Model.IsTGUniverse.sigmaPairs_mem {U A : V} {B : V → V} + (hU : IsTGUniverse (Mem (V := V)) U) (hA : A ∈ˢ U) + (hB : ∀ x, x ∈ˢ A → B x ∈ˢ U) : sigmaPairs A B ∈ˢ U := + hU.famUnion_mem hA fun x hx => + hU.image_mem (hB x hx) fun _y hy => + hU.kpair_mem hA (hU.transitive hA hx) (hU.transitive (hB x hx) hy) + +/-- The set of total single-valued dependent graphs on `A` with fibres +`B`: the `v ≠ 0` dependent product. -/ +noncomputable def piSet (A : V) (B : V → V) : V := + sep (power (sigmaPairs A B)) + (fun f => ∀ x, x ∈ˢ A → ∃ y, kpair x y ∈ˢ f ∧ ∀ y', kpair x y' ∈ˢ f → y' = y) + +theorem mem_piSet {A f : V} {B : V → V} : + f ∈ˢ piSet A B ↔ f ⊆ˢ sigmaPairs A B ∧ + ∀ x, x ∈ˢ A → ∃ y, kpair x y ∈ˢ f ∧ ∀ y', kpair x y' ∈ˢ f → y' = y := by + unfold piSet + rw [mem_sep, mem_power_iff_subset] + +theorem piSet_congr {A : V} {B B' : V → V} + (h : ∀ x, x ∈ˢ A → B x = B' x) : piSet A B = piSet A B' := by + unfold piSet + rw [sigmaPairs_congr h] + +theorem _root_.Ix.Theory.Model.IsTGUniverse.piSet_mem {U A : V} {B : V → V} + (hU : IsTGUniverse (Mem (V := V)) U) (hA : A ∈ˢ U) + (hB : ∀ x, x ∈ˢ A → B x ∈ˢ U) : piSet A B ∈ˢ U := + hU.mem_of_subset_mem (hU.power_mem (hU.sigmaPairs_mem hA hB)) sep_subset + +theorem graph_mem_piSet {A : V} {B F : V → V} + (hF : ∀ x, x ∈ˢ A → F x ∈ˢ B x) : graph F A ∈ˢ piSet A B := by + rw [mem_piSet] + constructor + · intro p hp + obtain ⟨x, hx, rfl⟩ := mem_graph.mp hp + exact mem_sigmaPairs.mpr ⟨x, hx, F x, hF x hx, rfl⟩ + · intro x hx + refine ⟨F x, mem_graph.mpr ⟨x, hx, rfl⟩, ?_⟩ + intro y' hy' + obtain ⟨x', -, hx'⟩ := mem_graph.mp hy' + obtain ⟨rfl, rfl⟩ := kpair_inj hx' + rfl + +theorem ne_pt_of_mem_piSet {A f : V} {B : V → V} (hf : f ∈ˢ piSet A B) : + f ≠ pt := by + rintro rfl + have := (mem_piSet.mp hf).1 ptTag ptTag_mem_pt + obtain ⟨x, -, y, -, hy⟩ := mem_sigmaPairs.mp this + exact ptTag_ne_kpair x y hy + +theorem app_mem_of_mem_piSet {A f a : V} {B : V → V} + (hf : f ∈ˢ piSet A B) (ha : a ∈ˢ A) : app f a ∈ˢ B a := by + obtain ⟨hsub, htot⟩ := mem_piSet.mp hf + obtain ⟨y, hy, huniq⟩ := htot a ha + rw [app_eq_of_unique (ne_pt_of_mem_piSet hf) hy huniq] + obtain ⟨x', -, y', hy', hp⟩ := mem_sigmaPairs.mp (hsub _ hy) + obtain ⟨rfl, rfl⟩ := kpair_inj hp + exact hy' + +/-- Eta: a member of `piSet A B` is the graph of its own application. -/ +theorem eq_graph_app_of_mem_piSet {A f : V} {B : V → V} + (hf : f ∈ˢ piSet A B) : graph (fun x => app f x) A = f := by + obtain ⟨hsub, htot⟩ := mem_piSet.mp hf + apply ext fun p => ?_ + rw [mem_graph] + constructor + · rintro ⟨x, hx, rfl⟩ + obtain ⟨y, hy, huniq⟩ := htot x hx + rwa [app_eq_of_unique (ne_pt_of_mem_piSet hf) hy huniq] + · intro hp + obtain ⟨x, hx, y, -, rfl⟩ := mem_sigmaPairs.mp (hsub p hp) + obtain ⟨y', hy', huniq⟩ := htot x hx + refine ⟨x, hx, ?_⟩ + rw [app_eq_of_unique (ne_pt_of_mem_piSet hf) hy' huniq, huniq y hp] + +/-- Members of `piSet A' B` that are graphs over `A` pin the domain: +every `x ∈ A'` lies in `A`. -/ +theorem graph_dom_of_mem_piSet {A A' : V} {B F : V → V} + (hf : graph F A ∈ˢ piSet A' B) : ∀ x, x ∈ˢ A' → x ∈ˢ A := by + intro x hx + obtain ⟨y, hy, -⟩ := (mem_piSet.mp hf).2 x hx + obtain ⟨x', hx', hp⟩ := mem_graph.mp hy + obtain ⟨rfl, rfl⟩ := kpair_inj hp + exact hx' + +/-! ### Off-domain and junk behavior of `app` + +`app` is total: on a non-`pt` value with no pair at the argument — +in particular off a graph's domain, or on the canonical junk value +`empty` itself — it returns `empty`. These lemmas record that a +graph's off-domain behavior is *canonical*: two graphs over the same +domain that agree on the domain agree everywhere, which is what makes +a **total** equality between interpreted function towers equivalent to +pointwise agreement on fitting inputs (`eq_of_mem_piSet_app_eq`, and +`eq_of_mem_pi_app_eq`, deleted with `Derive/Pi.lean` at task #221). -/ + +theorem app_eq_empty_of_not_mem {f a : V} (hf : f ≠ pt) + (h : ∀ y, ¬ kpair a y ∈ˢ f) : app f a = empty := by + unfold app + rw [if_neg hf] + refine eq_empty fun z hz => ?_ + obtain ⟨y, hy, -⟩ := mem_sUnion.mp hz + exact h y (mem_sep.mp hy).2 + +/-- `app` off a graph's domain is the canonical junk value. -/ +theorem app_graph_of_not_mem {F : V → V} {A a : V} (ha : ¬ a ∈ˢ A) : + app (graph F A) a = empty := by + refine app_eq_empty_of_not_mem graph_ne_pt fun y hy => ?_ + obtain ⟨x, hx, hp⟩ := mem_graph.mp hy + obtain ⟨rfl, rfl⟩ := kpair_inj hp + exact ha hx + +/-- `app` on the canonical junk value returns junk: junk propagates +through applications. -/ +theorem app_empty (a : V) : app (empty : V) a = empty := + app_eq_empty_of_not_mem (Ne.symm pt_ne_empty) + (fun _y hy => not_mem_empty _ hy) + +/-- A member of `piSet` applied off the domain is junk. -/ +theorem app_off_dom_of_mem_piSet {A f a : V} {B : V → V} + (hf : f ∈ˢ piSet A B) (ha : ¬ a ∈ˢ A) : app f a = empty := by + rw [← eq_graph_app_of_mem_piSet hf, app_graph_of_not_mem ha] + +/-- Function extensionality for members of the raw dependent product: +two total single-valued graphs over the same domain that agree under +application on every domain member are equal (off the domain both +apply to canonical junk, so nothing else distinguishes them). -/ +theorem eq_of_mem_piSet_app_eq {A f g : V} {B B' : V → V} + (hf : f ∈ˢ piSet A B) (hg : g ∈ˢ piSet A B') + (h : ∀ x, x ∈ˢ A → app f x = app g x) : f = g := by + rw [← eq_graph_app_of_mem_piSet hf, ← eq_graph_app_of_mem_piSet hg] + exact graph_congr h + +/- Opaque interface operator (see `Derive/Empty.lean`). -/ +attribute [irreducible] app + +end Ix.Theory.Model.SetTheory diff --git a/Ix/Theory/Model/SetTheory/Derive/Lfp.lean b/Ix/Theory/Model/SetTheory/Derive/Lfp.lean new file mode 100644 index 000000000..be3793ea7 --- /dev/null +++ b/Ix/Theory/Model/SetTheory/Derive/Lfp.lean @@ -0,0 +1,162 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetTheory/Derive/Lfp.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetTheory.Derive.Univ + +@[expose] public section + +/-! +# Least pre-fixed points inside a universe (task #188) + +The carrier of a directly installed **recursive** inductive type is the +Knaster–Tarski least pre-fixed point of its constructor-tower functor, +taken *inside* a universe: + + lfpSet w F = ⋂ { X ∈ univ w | F X ⊆ X } + +where `F` is a set-level function (a graph over `univ w`, so `F X` is +`app F X`). The intersection is realised as a separation over a +(classically chosen) closed member — `{x ∈ L₀ | x lies in every +`F`-closed member of `univ w`}` — so it is a set, and a *subset of a +member* of `univ w`, hence a member. When NO closed member exists the +definition returns the empty set instead. That junk value is what +makes the operator **total** at the type `(Sort w → Sort w) → Sort w`: +`lfpSet w F ∈ univ w` for every `F` (`lfpSet_mem_univ`), so the basis +constant `lfp` needs no certificate argument. Every LAW below assumes +a closed member exists (`∃ L, IsClosedIn w F L`) — the semantic side +exhibits one (the ω-iterate of a finitary tower functor, +`Ix.Theory/Model/SetModel/Iter.lean`). + +Under that hypothesis and monotonicity the standard facts hold: the +least pre-fixed point is a fixed point (`lfpSet_closed`, +`lfpSet_fixed`), and it is contained in every closed member +(`lfpSet_subset`) — which IS structural induction (`lfpSet_induction`): +a property closed under the functor holds on the whole carrier. No +rank, no ordinal, no iteration is consulted by the recursor's laws. + +The RECURSOR of a recursive type needs no operator of its own: it is a +fixed point of its one-step unfolding, selected by the basis +`Classical.choice` from the (spelled) sigma type of fixed points +(`Ix.Theory/Model/Semantics/Tower/FixRec.lean`). + +Everything here is over the bare `SetTheory` interface; no syntax. +-/ + +namespace Ix.Theory.Model.SetTheory + +universe u + +variable {V : Type u} [SetTheory V] + +/-! ## Universe helpers -/ + +/-- A subset of a member of `univ w` is a member: at `w = 0` members of +`univZero` are the subsets of `unitSet`; above, the Grothendieck +clause. -/ +theorem univ_mem_of_subset_mem {w : Nat} {y z : V} (hy : y ∈ˢ (univ w : V)) (hz : z ⊆ˢ y) : + z ∈ˢ (univ w : V) := by + rcases Nat.eq_zero_or_pos w with rfl | hw + · rw [univ_zero] at hy ⊢ + rw [mem_univZero] at hy ⊢ + exact hz.trans hy + · exact (univ_isTGUniverse (Nat.pos_iff_ne_zero.mp hw)).mem_of_subset_mem hy hz + +/-- Separation stays inside every level. -/ +theorem univ_sep_mem {w : Nat} {a : V} {p : V → Prop} (ha : a ∈ˢ (univ w : V)) : + sep a p ∈ˢ (univ w : V) := + univ_mem_of_subset_mem ha sep_subset + +/-! ## The least pre-fixed point -/ + +/-- An `F`-closed member of `univ w`: a pre-fixed point of `app F`. -/ +def IsClosedIn (w : Nat) (F X : V) : Prop := + X ∈ˢ (univ w : V) ∧ app F X ⊆ˢ X + +open Classical in +/-- **The least pre-fixed point of `F` inside `univ w`** — the +intersection of the `F`-closed members of `univ w` when there is one +(separated from a chosen closed member), the empty set otherwise (see +the module docstring). -/ +noncomputable def lfpSet (w : Nat) (F : V) : V := + if h : ∃ L, IsClosedIn w F L then + sep (Classical.choose h) (fun x => ∀ X, IsClosedIn w F X → x ∈ˢ X) + else empty + +theorem lfpSet_of_not {w : Nat} {F : V} (h : ¬ ∃ L, IsClosedIn w F L) : + lfpSet w F = empty := by + unfold lfpSet; exact dif_neg h + +theorem mem_lfpSet {w : Nat} {F x : V} (h : ∃ L, IsClosedIn w F L) : + x ∈ˢ lfpSet w F ↔ ∀ X, IsClosedIn w F X → x ∈ˢ X := by + unfold lfpSet + rw [dif_pos h, mem_sep] + exact ⟨fun hx => hx.2, fun hx => ⟨hx _ (Classical.choose_spec h), hx⟩⟩ + +/-- **Leastness**: the least pre-fixed point lies in every closed +member. -/ +theorem lfpSet_subset {w : Nat} {F X : V} (hX : IsClosedIn w F X) : lfpSet w F ⊆ˢ X := + fun _x hx => (mem_lfpSet ⟨X, hX⟩).mp hx X hX + +/-- **Formation, unconditional**: the least pre-fixed point is a member +of the universe — a subset of a closed member when there is one, the +empty set otherwise. -/ +theorem lfpSet_mem_univ (w : Nat) (F : V) : lfpSet w F ∈ˢ (univ w : V) := by + by_cases h : ∃ L, IsClosedIn w F L + · obtain ⟨L, hL⟩ := h + exact univ_mem_of_subset_mem hL.1 (lfpSet_subset hL) + · rw [lfpSet_of_not h]; exact empty_mem_univ w + +/-- Monotonicity of a set-level functor on the universe. -/ +def MonoIn (w : Nat) (F : V) : Prop := + ∀ X Y, X ∈ˢ (univ w : V) → Y ∈ˢ (univ w : V) → X ⊆ˢ Y → app F X ⊆ˢ app F Y + +/-- The functor maps the universe into itself. -/ +def MapsIn (w : Nat) (F : V) : Prop := + ∀ X, X ∈ˢ (univ w : V) → app F X ∈ˢ (univ w : V) + +/-- **Closure**: the least pre-fixed point is a pre-fixed point. -/ +theorem lfpSet_closed {w : Nat} {F : V} (h : ∃ L, IsClosedIn w F L) + (hmono : MonoIn w F) : app F (lfpSet w F) ⊆ˢ lfpSet w F := by + intro x hx + rw [mem_lfpSet h] + intro X hX + exact hX.2 x (hmono _ _ (lfpSet_mem_univ w F) hX.1 (lfpSet_subset hX) x hx) + +/-- **The fixed-point equation's other half**: the least pre-fixed +point is a post-fixed point, because its image is itself closed. -/ +theorem lfpSet_fixed {w : Nat} {F : V} (h : ∃ L, IsClosedIn w F L) + (hmono : MonoIn w F) (hmaps : MapsIn w F) : lfpSet w F ⊆ˢ app F (lfpSet w F) := by + refine lfpSet_subset ⟨hmaps _ (lfpSet_mem_univ w F), ?_⟩ + exact hmono _ _ (hmaps _ (lfpSet_mem_univ w F)) (lfpSet_mem_univ w F) + (lfpSet_closed h hmono) + +/-- The fixed-point equation. -/ +theorem lfpSet_eq {w : Nat} {F : V} (h : ∃ L, IsClosedIn w F L) + (hmono : MonoIn w F) (hmaps : MapsIn w F) : app F (lfpSet w F) = lfpSet w F := + Subset.antisymm (lfpSet_closed h hmono) (lfpSet_fixed h hmono hmaps) + +/-- **Structural induction**: a property closed under the functor on +the carrier holds on the whole carrier — the members of the carrier +satisfying it form a closed member of the universe, in which the +carrier lies by leastness. -/ +theorem lfpSet_induction {w : Nat} {F : V} (h : ∃ L, IsClosedIn w F L) + (hmono : MonoIn w F) (P : V → Prop) + (hP : ∀ x, x ∈ˢ app F (sep (lfpSet w F) P) → P x) : + ∀ x, x ∈ˢ lfpSet w F → P x := by + intro x hx + have hS : IsClosedIn w F (sep (lfpSet w F) P) := by + refine ⟨univ_sep_mem (lfpSet_mem_univ w F), fun y hy => ?_⟩ + rw [mem_sep] + refine ⟨?_, hP y hy⟩ + exact lfpSet_closed h hmono y + (hmono _ _ (univ_sep_mem (lfpSet_mem_univ w F)) (lfpSet_mem_univ w F) sep_subset y hy) + exact (mem_sep.mp (lfpSet_subset hS x hx)).2 + +end Ix.Theory.Model.SetTheory diff --git a/Ix/Theory/Model/SetTheory/Derive/LfpFam.lean b/Ix/Theory/Model/SetTheory/Derive/LfpFam.lean new file mode 100644 index 000000000..7123ed505 --- /dev/null +++ b/Ix/Theory/Model/SetTheory/Derive/LfpFam.lean @@ -0,0 +1,170 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetTheory/Derive/LfpFam.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetTheory.Derive.Lfp +import Ix.Theory.Model.SetTheory.Derive.Graphs +@[expose] public section + +/-! +# Least pre-fixed points of family functors (task #188, indexed) + +The carrier of a directly installed recursive **family** `T : I → Sort w` +is the least pre-fixed point of its constructor-tower functor acting on +FAMILIES — graphs over the index-tuple set `I` with values in `univ w` +(`famSpace`), ordered pointwise (`FamLe`). This is `Lfp.lean` fibrewise: + + lfpFamSet w I F = i ↦ {x ∈ L₀ i | ∀ closed X, x ∈ X i} + +over a classically chosen closed family `L₀` (the empty family when +there is none — TOTAL, so the basis constant `lfpFam` needs no +certificate: `lfpFamSet_mem`). Under a closed member and monotonicity +the least pre-fixed family is a fixed point (`lfpFamSet_eq`) and +supports fibrewise structural induction (`lfpFamSet_induction`); the +closed member is exhibited by the semantics (the ω-iterate family, per +fibre — `Ix.Theory/Model/SetModel/Iter.lean`'s `natUnion`). + +Everything here is over the bare `SetTheory` interface; no syntax. +-/ + +namespace Ix.Theory.Model.SetTheory + +universe u + +variable {V : Type u} [SetTheory V] + +/-- The family space over `I`: graphs into `univ w`. -/ +noncomputable def famSpace (w : Nat) (I : V) : V := piSet I fun _ => univ w + +/-- The pointwise order on families over `I`. -/ +def FamLe (I X Y : V) : Prop := ∀ i, i ∈ˢ I → app X i ⊆ˢ app Y i + +theorem FamLe.refl (I X : V) : FamLe I X X := fun _ _ => Subset.refl _ + +theorem FamLe.trans {I X Y Z : V} (h₁ : FamLe I X Y) (h₂ : FamLe I Y Z) : FamLe I X Z := + fun i hi => Subset.trans (h₁ i hi) (h₂ i hi) + +/-- An `F`-closed family: a pre-fixed point of `app F` in the family +space. -/ +def IsClosedFam (w : Nat) (I F X : V) : Prop := + X ∈ˢ famSpace w I ∧ FamLe I (app F X) X + +theorem famSpace_app {w : Nat} {I X i : V} (hX : X ∈ˢ famSpace w I) (hi : i ∈ˢ I) : + app X i ∈ˢ (univ w : V) := + app_mem_of_mem_piSet hX hi + +/-- Members of the family space agreeing pointwise are equal. -/ +theorem famSpace_ext {w : Nat} {I X Y : V} (hX : X ∈ˢ famSpace w I) (hY : Y ∈ˢ famSpace w I) + (h : ∀ i, i ∈ˢ I → app X i = app Y i) : X = Y := + eq_of_mem_piSet_app_eq hX hY h + +/-- A graph with fibres in `univ w` is in the family space. -/ +theorem graph_mem_famSpace {w : Nat} {I : V} {G : V → V} (h : ∀ i, i ∈ˢ I → G i ∈ˢ (univ w : V)) : + graph G I ∈ˢ famSpace w I := + graph_mem_piSet h + +open Classical in +/-- **The least pre-fixed family of `F` over `I`** — fibrewise the +intersection of the closed families when there is one (separated from +a chosen closed family), the empty family otherwise. -/ +noncomputable def lfpFamSet (w : Nat) (I F : V) : V := + if h : ∃ L, IsClosedFam w I F L then + graph (fun i => sep (app (Classical.choose h) i) + (fun x => ∀ X, IsClosedFam w I F X → x ∈ˢ app X i)) I + else graph (fun _ => empty) I + +theorem lfpFamSet_of_not {w : Nat} {I F : V} (h : ¬ ∃ L, IsClosedFam w I F L) : + lfpFamSet w I F = graph (fun _ => empty) I := by + unfold lfpFamSet; exact dif_neg h + +theorem mem_app_lfpFamSet {w : Nat} {I F i x : V} (h : ∃ L, IsClosedFam w I F L) (hi : i ∈ˢ I) : + x ∈ˢ app (lfpFamSet w I F) i ↔ ∀ X, IsClosedFam w I F X → x ∈ˢ app X i := by + unfold lfpFamSet + rw [dif_pos h, app_graph hi, mem_sep] + exact ⟨fun hx => hx.2, fun hx => ⟨hx _ (Classical.choose_spec h), hx⟩⟩ + +/-- **Leastness**: the least pre-fixed family lies in every closed +family. -/ +theorem lfpFamSet_le {w : Nat} {I F X : V} (hX : IsClosedFam w I F X) : + FamLe I (lfpFamSet w I F) X := + fun _ hi _x hx => (mem_app_lfpFamSet ⟨X, hX⟩ hi).mp hx X hX + +/-- **Formation, unconditional**: the least pre-fixed family is in the +family space. -/ +theorem lfpFamSet_mem (w : Nat) (I F : V) : lfpFamSet w I F ∈ˢ famSpace w I := by + by_cases h : ∃ L, IsClosedFam w I F L + · unfold lfpFamSet + rw [dif_pos h] + exact graph_mem_famSpace fun _ hi => + univ_sep_mem (famSpace_app (Classical.choose_spec h).1 hi) + · rw [lfpFamSet_of_not h] + exact graph_mem_famSpace fun _ _ => empty_mem_univ w + +/-- Monotonicity of a family functor on the family space. -/ +def MonoFam (w : Nat) (I F : V) : Prop := + ∀ X Y, X ∈ˢ famSpace w I → Y ∈ˢ famSpace w I → FamLe I X Y → FamLe I (app F X) (app F Y) + +/-- The functor maps the family space into itself. -/ +def MapsFam (w : Nat) (I F : V) : Prop := + ∀ X, X ∈ˢ famSpace w I → app F X ∈ˢ famSpace w I + +/-- **Closure**: the least pre-fixed family is a pre-fixed point. -/ +theorem lfpFamSet_closed {w : Nat} {I F : V} (h : ∃ L, IsClosedFam w I F L) + (hmono : MonoFam w I F) : FamLe I (app F (lfpFamSet w I F)) (lfpFamSet w I F) := by + intro i hi x hx + rw [mem_app_lfpFamSet h hi] + intro X hX + exact hX.2 i hi x (hmono _ _ (lfpFamSet_mem w I F) hX.1 (lfpFamSet_le hX) i hi x hx) + +/-- The least pre-fixed family is a post-fixed point. -/ +theorem lfpFamSet_fixed {w : Nat} {I F : V} (h : ∃ L, IsClosedFam w I F L) + (hmono : MonoFam w I F) (hmaps : MapsFam w I F) : + FamLe I (lfpFamSet w I F) (app F (lfpFamSet w I F)) := by + refine lfpFamSet_le ⟨hmaps _ (lfpFamSet_mem w I F), ?_⟩ + exact hmono _ _ (hmaps _ (lfpFamSet_mem w I F)) (lfpFamSet_mem w I F) + (lfpFamSet_closed h hmono) + +/-- The fixed-point equation, fibrewise. -/ +theorem app_lfpFamSet_eq {w : Nat} {I F : V} (h : ∃ L, IsClosedFam w I F L) + (hmono : MonoFam w I F) (hmaps : MapsFam w I F) {i : V} (hi : i ∈ˢ I) : + app (app F (lfpFamSet w I F)) i = app (lfpFamSet w I F) i := + Subset.antisymm (lfpFamSet_closed h hmono i hi) (lfpFamSet_fixed h hmono hmaps i hi) + +/-- The fixed-point equation. -/ +theorem lfpFamSet_eq {w : Nat} {I F : V} (h : ∃ L, IsClosedFam w I F L) + (hmono : MonoFam w I F) (hmaps : MapsFam w I F) : + app F (lfpFamSet w I F) = lfpFamSet w I F := + famSpace_ext (hmaps _ (lfpFamSet_mem w I F)) (lfpFamSet_mem w I F) + fun _ hi => app_lfpFamSet_eq h hmono hmaps hi + +/-- **Structural induction**, fibrewise: a property closed under the +functor on the carrier holds on the whole carrier. -/ +theorem lfpFamSet_induction {w : Nat} {I F : V} (h : ∃ L, IsClosedFam w I F L) + (hmono : MonoFam w I F) (P : V → V → Prop) + (hP : ∀ i, i ∈ˢ I → ∀ x, + x ∈ˢ app (app F (graph (fun i => sep (app (lfpFamSet w I F) i) (P i)) I)) i → P i x) : + ∀ i, i ∈ˢ I → ∀ x, x ∈ˢ app (lfpFamSet w I F) i → P i x := by + intro i hi x hx + have hSmem : graph (fun i => sep (app (lfpFamSet w I F) i) (P i)) I ∈ˢ famSpace w I := + graph_mem_famSpace fun i hi => univ_sep_mem (famSpace_app (lfpFamSet_mem w I F) hi) + have hSle : FamLe I (graph (fun i => sep (app (lfpFamSet w I F) i) (P i)) I) (lfpFamSet w I F) := by + intro i hi y hy + rw [app_graph hi] at hy + exact (mem_sep.mp hy).1 + have hS : IsClosedFam w I F (graph (fun i => sep (app (lfpFamSet w I F) i) (P i)) I) := by + refine ⟨hSmem, fun i hi y hy => ?_⟩ + rw [app_graph hi, mem_sep] + refine ⟨?_, hP i hi y hy⟩ + exact lfpFamSet_closed h hmono i hi y + (hmono _ _ hSmem (lfpFamSet_mem w I F) hSle i hi y hy) + have := lfpFamSet_le hS i hi x hx + rw [app_graph hi] at this + exact (mem_sep.mp this).2 + +end Ix.Theory.Model.SetTheory diff --git a/Ix/Theory/Model/SetTheory/Derive/Omega.lean b/Ix/Theory/Model/SetTheory/Derive/Omega.lean new file mode 100644 index 000000000..4eabed9d0 --- /dev/null +++ b/Ix/Theory/Model/SetTheory/Derive/Omega.lean @@ -0,0 +1,142 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetTheory/Derive/Omega.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetTheory.Derive.Universe + +@[expose] public section + +/-! +# Infinity, derived: the finite ordinals + +Infinity is derived from the universe chain + Replacement: an +*inhabited* Grothendieck universe is an inductive *set* — it contains +`∅` and is closed under the von Neumann successor `n ↦ n ∪ {n}` +(von Neumann 1923) by the pairing/union closures — so `ω` can be +separated out of one as the members of *every* inductive set. +Leastness is then definitional. The inductive universe used is +`univChain 1`: it is inhabited by `univChain 0` (note `univChain 0` +itself need not be — the empty set satisfies `IsTGUniverse` +vacuously). + +`vnat : Nat → V` names the members of `ω` from the meta-level; every +member of `ω` is a unique `vnat k` (`mem_omega_iff`, `vnat_inj`), +which is what makes recursion on `ω` cheap in `Derive/Natrec.lean`. + +`ω` itself is a *member* of any universe that has the inductive +universe `univChain 1` as a member — arranged for the tower in +`Derive/Univ.lean`. (Mere universehood does not suffice: `V_ω` is a +Tarski universe without `ω`.) +-/ + +namespace Ix.Theory.Model.SetTheory + +universe u + +variable {V : Type u} [SetTheory V] + +/-- The von Neumann successor `n ∪ {n}`. -/ +noncomputable def vsucc (n : V) : V := binUnion n (sing n) + +theorem mem_vsucc {z n : V} : z ∈ˢ vsucc n ↔ z ∈ˢ n ∨ z = n := by + rw [vsucc, mem_binUnion, mem_sing] + +theorem self_mem_vsucc (n : V) : n ∈ˢ vsucc n := mem_vsucc.mpr (Or.inr rfl) + +theorem vsucc_ne_empty (n : V) : vsucc n ≠ empty := + ne_empty_of_mem (self_mem_vsucc n) + +/-- An inductive set: contains `∅`, closed under the successor. -/ +def Inductive (I : V) : Prop := + (empty : V) ∈ˢ I ∧ ∀ n, n ∈ˢ I → vsucc n ∈ˢ I + +/-- An inhabited Grothendieck universe is an inductive set. -/ +theorem _root_.Ix.Theory.Model.IsTGUniverse.inductive_self {U y : V} + (hU : IsTGUniverse (Mem (V := V)) U) (hy : y ∈ˢ U) : Inductive U := + ⟨hU.empty_mem hy, fun _n hn => + hU.binUnion_mem hy hn (hU.sing_mem hy hn)⟩ + +theorem univChain_one_inductive : Inductive (univChain 1 : V) := + (univChain_tg 1).inductive_self (univChain_mem 0) + +/-- The finite ordinals: the members of every inductive set, separated +from the inductive universe `univChain 1`. -/ +noncomputable def omega : V := + sep (univChain 1) (fun n => ∀ I : V, Inductive I → n ∈ˢ I) + +theorem mem_omega {n : V} : n ∈ˢ (omega : V) ↔ ∀ I : V, Inductive I → n ∈ˢ I := by + rw [omega, mem_sep] + exact ⟨fun h => h.2, fun h => ⟨h _ univChain_one_inductive, h⟩⟩ + +theorem omega_subset_inductive {I : V} (hI : Inductive I) : (omega : V) ⊆ˢ I := + fun _ hn => mem_omega.mp hn I hI + +theorem omega_inductive : Inductive (omega : V) := by + constructor + · exact mem_omega.mpr fun I hI => hI.1 + · intro n hn + exact mem_omega.mpr fun I hI => hI.2 n (mem_omega.mp hn I hI) + +theorem empty_mem_omega : (empty : V) ∈ˢ omega := omega_inductive.1 + +theorem vsucc_mem_omega {n : V} (hn : n ∈ˢ (omega : V)) : vsucc n ∈ˢ (omega : V) := + omega_inductive.2 n hn + +/-- The `k`-th von Neumann natural. -/ +noncomputable def vnat : Nat → V + | 0 => empty + | k + 1 => vsucc (vnat k) + +theorem vnat_mem_omega : ∀ k, (vnat k : V) ∈ˢ omega + | 0 => empty_mem_omega + | k + 1 => vsucc_mem_omega (vnat_mem_omega k) + +/-- Every finite ordinal is named by a meta-level natural. -/ +theorem mem_omega_iff {n : V} : n ∈ˢ (omega : V) ↔ ∃ k, n = vnat k := by + constructor + · intro hn + have hind : Inductive (sep (omega : V) (fun n => ∃ k, n = vnat k)) := by + constructor + · exact mem_sep.mpr ⟨empty_mem_omega, 0, rfl⟩ + · intro m hm + obtain ⟨hmo, k, rfl⟩ := mem_sep.mp hm + exact mem_sep.mpr ⟨vsucc_mem_omega hmo, k + 1, rfl⟩ + exact (mem_sep.mp (omega_subset_inductive hind n hn)).2 + · rintro ⟨k, rfl⟩ + exact vnat_mem_omega k + +theorem vnat_mem_vnat_of_lt : ∀ {k l : Nat}, k < l → (vnat k : V) ∈ˢ vnat l := by + intro k l hkl + induction l with + | zero => exact absurd hkl (Nat.not_lt_zero k) + | succ l ih => + rcases Nat.lt_succ_iff_lt_or_eq.mp hkl with h | rfl + · exact mem_vsucc.mpr (Or.inl (ih h)) + · exact self_mem_vsucc _ + +theorem vnat_inj {k l : Nat} (h : (vnat k : V) = vnat l) : k = l := by + rcases Nat.lt_trichotomy k l with hlt | heq | hgt + · exact absurd (h ▸ vnat_mem_vnat_of_lt (V := V) hlt) (not_mem_self _) + · exact heq + · exact absurd (h ▸ vnat_mem_vnat_of_lt (V := V) hgt) (not_mem_self _) + +theorem omega_subset_univChain_one : (omega : V) ⊆ˢ univChain 1 := sep_subset + +/-- `ω` is a member of any universe having the inductive universe +`univChain 1` as a member. -/ +theorem _root_.Ix.Theory.Model.IsTGUniverse.omega_mem {U : V} + (hU : IsTGUniverse (Mem (V := V)) U) (h1 : (univChain 1 : V) ∈ˢ U) : + (omega : V) ∈ˢ U := + hU.mem_of_subset_mem h1 omega_subset_univChain_one + +/- Opaque interface operator (see `Derive/Empty.lean`). `vsucc` stays +reducible: `Derive/Natrec.lean` computes with it through `vnat`. -/ +attribute [irreducible] omega + +end Ix.Theory.Model.SetTheory diff --git a/Ix/Theory/Model/SetTheory/Derive/Pair.lean b/Ix/Theory/Model/SetTheory/Derive/Pair.lean new file mode 100644 index 000000000..cdaf20257 --- /dev/null +++ b/Ix/Theory/Model/SetTheory/Derive/Pair.lean @@ -0,0 +1,128 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetTheory/Derive/Pair.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetTheory.Derive.Sep + +@[expose] public section + +/-! +# Singletons, binary unions, and Kuratowski ordered pairs + +Singleton and ordered pair are derived from the unordered pair — the +Kuratowski pair `⟨a, b⟩ := {{a}, {a, b}}` (Kuratowski, Fund. Math. 2 +(1921)); binary union is the union of an unordered pair. The payoff +lemmas are the +injectivity of the Kuratowski pair and the fact that an ordered pair is +never `{∅}` — the tag that keeps the proof point `pt` apart from +function graphs (`Derive/Pt.lean`, `Derive/Graphs.lean`). +-/ + +namespace Ix.Theory.Model.SetTheory + +universe u + +variable {V : Type u} [SetTheory V] + +/-- The singleton `{a}`. -/ +noncomputable def sing (a : V) : V := upair a a + +theorem mem_sing {z a : V} : z ∈ˢ sing a ↔ z = a := by + rw [sing, mem_upair]; exact ⟨fun h => h.elim id id, Or.inl⟩ + +theorem sing_inj {a b : V} (h : sing a = sing b) : a = b := + mem_sing.mp (h ▸ mem_sing.mpr rfl) + +theorem sUnion_sing (a : V) : sUnion (sing a) = a := + ext fun z => by + rw [mem_sUnion] + constructor + · rintro ⟨y, hy, hz⟩; rwa [mem_sing.mp hy] at hz + · exact fun hz => ⟨a, mem_sing.mpr rfl, hz⟩ + +/-- Binary union `a ∪ b := ⋃ {a, b}`. -/ +noncomputable def binUnion (a b : V) : V := sUnion (upair a b) + +theorem mem_binUnion {z a b : V} : z ∈ˢ binUnion a b ↔ z ∈ˢ a ∨ z ∈ˢ b := by + rw [binUnion, mem_sUnion] + constructor + · rintro ⟨y, hy, hz⟩ + rcases mem_upair.mp hy with h | h <;> subst h + · exact Or.inl hz + · exact Or.inr hz + · rintro (hz | hz) + · exact ⟨a, mem_upair.mpr (Or.inl rfl), hz⟩ + · exact ⟨b, mem_upair.mpr (Or.inr rfl), hz⟩ + +/-- The Kuratowski ordered pair `⟨a, b⟩ = {{a}, {a, b}}`. -/ +noncomputable def kpair (a b : V) : V := upair (sing a) (upair a b) + +theorem upair_eq_sing {a b c : V} (h : upair a b = sing c) : a = c ∧ b = c := by + constructor + · exact mem_sing.mp (h ▸ mem_upair.mpr (Or.inl rfl)) + · exact mem_sing.mp (h ▸ mem_upair.mpr (Or.inr rfl)) + +theorem mem_upair_left (a b : V) : a ∈ˢ upair a b := mem_upair.mpr (Or.inl rfl) +theorem mem_upair_right (a b : V) : b ∈ˢ upair a b := mem_upair.mpr (Or.inr rfl) + +theorem upair_comm (a b : V) : upair a b = upair b a := + ext fun z => by rw [mem_upair, mem_upair]; exact Or.comm + +/-- First-component injectivity of the Kuratowski pair. -/ +theorem kpair_inj_left {a b c d : V} (h : kpair a b = kpair c d) : a = c := by + have hac : sing a ∈ˢ kpair c d := h ▸ mem_upair_left (sing a) (upair a b) + rcases mem_upair.mp hac with h1 | h1 + · exact sing_inj h1 + · -- `{a} = {c, d}`, so `c = a` (and `d = a`). + obtain ⟨hc, -⟩ := upair_eq_sing (h1.symm) + exact hc.symm + +theorem kpair_inj {a b c d : V} (h : kpair a b = kpair c d) : a = c ∧ b = d := by + obtain rfl : a = c := kpair_inj_left h + refine ⟨rfl, ?_⟩ + -- `{a, b} ∈ {{a}, {a, d}}` and `{a, d} ∈ {{a}, {a, b}}`. + have hb : upair a b ∈ˢ kpair a d := by + rw [← h]; exact mem_upair_right _ _ + have hd : upair a d ∈ˢ kpair a b := by + rw [h]; exact mem_upair_right _ _ + rcases mem_upair.mp hb with h1 | h1 + · -- `{a, b} = {a}`, so `b = a`; then `{a, d}` collapses too, so `d = a = b`. + obtain ⟨-, hba⟩ := upair_eq_sing h1 + rcases mem_upair.mp hd with h2 | h2 + · obtain ⟨-, hda⟩ := upair_eq_sing h2 + exact hba.trans hda.symm + · -- `{a, d} = {a, b}`: `d` is `a` or `b`, and `b = a` closes both. + rcases mem_upair.mp (h2 ▸ mem_upair_right a d) with hda | hdb + · exact hba.trans hda.symm + · exact hdb.symm + · -- `{a, b} = {a, d}`: `b` is `a` or `d`. + rcases mem_upair.mp (h1 ▸ mem_upair_right a b) with hba | hbd + · -- `b = a`; then `d ∈ {a, d} = {a, b} = {a, a}`, so `d = a = b`. + rcases mem_upair.mp (h1.symm ▸ mem_upair_right a d) with hda | hdb + · exact hba.trans hda.symm + · exact hdb.symm + · exact hbd + +theorem kpair_ne_empty {a b : V} : kpair a b ≠ empty := + ne_empty_of_mem (mem_upair_left (sing a) (upair a b)) + +/-- Every member of a Kuratowski pair is nonempty — the fact that keeps +`{∅}` (the proof point) out of the pair/graph world. -/ +theorem mem_kpair_nonempty {a b z : V} (hz : z ∈ˢ kpair a b) : ∃ w, w ∈ˢ z := by + rcases mem_upair.mp hz with h | h <;> subst h + · exact ⟨a, mem_sing.mpr rfl⟩ + · exact ⟨a, mem_upair_left a b⟩ + +theorem mem_sUnion_kpair_left (a b : V) : a ∈ˢ sUnion (kpair a b) := + mem_sUnion.mpr ⟨sing a, mem_upair_left _ _, mem_sing.mpr rfl⟩ + +theorem mem_sUnion_kpair_right (a b : V) : b ∈ˢ sUnion (kpair a b) := + mem_sUnion.mpr ⟨upair a b, mem_upair_right _ _, mem_upair_right a b⟩ + +end Ix.Theory.Model.SetTheory diff --git a/Ix/Theory/Model/SetTheory/Derive/Pt.lean b/Ix/Theory/Model/SetTheory/Derive/Pt.lean new file mode 100644 index 000000000..4b6bc036a --- /dev/null +++ b/Ix/Theory/Model/SetTheory/Derive/Pt.lean @@ -0,0 +1,240 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetTheory/Derive/Pt.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetTheory.Derive.Universe + +@[expose] public section + +/-! +# The proof point, truth values, `univ 0`, and `eqv` + +* `pt := {ptTag}` with `ptTag := {∅, {{∅}}}` — the tagged proof + point: the canonical inhabitant of every true proposition. The tag + is chosen so that **no data-value encoding produces `pt`** (task + #109; the battery was `Derive/PtFresh.lean`, deleted at task #221 with +the collapse it served). Selection principle: + `pt` must be a singleton whose element (a) has an *empty* member — + so neither the tag nor `pt` is a Kuratowski pair, pair elements + being nonempty (the anti-pair tag `Derive/Graphs.lean` exploits, one + level up from the old `pt = {∅}`); (b) is *not a singleton* — else + `pt = {{a}} = kpair a a`, a writable pair value; (c) is not `∅` — + else `pt = {∅} = vnat 1`, a writable numeral (the old collision); + and (d) has members that **cohabit no writable type** — else `pt` is + a writable singleton quotient class (`{2}` = the class of + `Quot.mk (· = 2 ∧ · = 2) 2` killed the `{vnat 2}` candidate). + `ptTag`'s members `∅` and `{{∅}} = kpair ∅ ∅` live in `Nat` resp. + pair types only, and no writable type hosts both. +* `unitSet := {pt}` — the true truth value, and the model of `PUnit`. +* `univZero := power unitSet = {∅, {pt}}` — the set of truth values, + the `U₀ = {∅, {•}}` of Mario Carneiro, *The Type Theory of Lean*, + master's thesis, Carnegie Mellon University, 2019; stating it as a + power set makes + "members of `univ 0` are subsets of `{pt}`" definitional, and + propositional extensionality one application of `ext`. +* `truthVal p` — the truth value of a meta-level proposition, `{pt}` + if `p` holds and `∅` otherwise (classical); `eqv x y` is + `truthVal (x = y)`. +-/ + +namespace Ix.Theory.Model.SetTheory + +universe u + +variable {V : Type u} [SetTheory V] + +/-- The proof-point tag `{∅, {{∅}}}` (see the module docstring for the +selection principle). -/ +noncomputable def ptTag : V := upair empty (sing (sing empty)) + +theorem mem_ptTag {z : V} : + z ∈ˢ (ptTag : V) ↔ z = empty ∨ z = sing (sing empty) := mem_upair + +theorem empty_mem_ptTag : (empty : V) ∈ˢ ptTag := + mem_ptTag.mpr (Or.inl rfl) + +theorem ptTag_ne_empty : (ptTag : V) ≠ empty := + ne_empty_of_mem empty_mem_ptTag + +/-- The tag is not a singleton: its two members `∅` and `{{∅}}` +differ. (Blocks `pt = kpair a a = {{a}}`.) -/ +theorem ptTag_ne_sing (a : V) : (ptTag : V) ≠ sing a := by + intro h + obtain ⟨h1, h2⟩ := upair_eq_sing h + exact ne_empty_of_mem (mem_sing.mpr rfl) (h2.trans h1.symm) + +/-- The tag is not a Kuratowski pair: `∅` is among its members, while +every member of a pair is nonempty. -/ +theorem ptTag_ne_kpair (a b : V) : (ptTag : V) ≠ kpair a b := by + intro h + obtain ⟨w, hw⟩ := mem_kpair_nonempty (h ▸ empty_mem_ptTag) + exact not_mem_empty w hw + +/-- The proof point `pt = {ptTag}`. -/ +noncomputable def pt : V := sing ptTag + +theorem mem_pt {z : V} : z ∈ˢ (pt : V) ↔ z = ptTag := mem_sing + +theorem ptTag_mem_pt : (ptTag : V) ∈ˢ pt := mem_pt.mpr rfl + +theorem pt_ne_empty : (pt : V) ≠ empty := + ne_empty_of_mem ptTag_mem_pt + +/-- `pt` is not a Kuratowski pair: `pt` is a singleton, so the pair +would be degenerate (`kpair a a = {{a}}`), forcing the tag to be the +singleton `{a}` — which it is not. -/ +theorem pt_ne_kpair (a b : V) : (pt : V) ≠ kpair a b := by + intro h + obtain ⟨h1, -⟩ := upair_eq_sing h.symm + exact ptTag_ne_sing a h1.symm + +/-- `pt` is not a member of its own tag (blocks the two-step membership +cycle `pt ∈ ptTag ∈ pt` that `not_mem_self` cannot see). -/ +theorem pt_not_mem_ptTag : ¬ (pt : V) ∈ˢ ptTag := by + intro h + rcases mem_ptTag.mp h with hpe | hps + · exact pt_ne_empty hpe + · have hm := ptTag_mem_pt (V := V) + rw [hps] at hm + exact ptTag_ne_sing empty (mem_sing.mp hm) + +/-- The canonical singleton `{pt}`: the true truth value. -/ +noncomputable def unitSet : V := sing pt + +theorem mem_unitSet_iff {z : V} : z ∈ˢ (unitSet : V) ↔ z = pt := mem_sing + +theorem pt_mem_unitSet : (pt : V) ∈ˢ unitSet := mem_unitSet_iff.mpr rfl + +theorem unitSet_ne_empty : (unitSet : V) ≠ empty := + ne_empty_of_mem pt_mem_unitSet + +/-- The interpretation of `Sort 0`: the set `{∅, {pt}}` of truth +values, stated as the power set of `{pt}`. -/ +noncomputable def univZero : V := power unitSet + +theorem mem_univZero {T : V} : T ∈ˢ (univZero : V) ↔ T ⊆ˢ unitSet := + mem_power_iff_subset + +/-- Members of `univ 0` have at most the proof point as element. -/ +theorem eq_pt_of_mem_univZero {T x : V} (hT : T ∈ˢ (univZero : V)) + (hx : x ∈ˢ T) : x = pt := + mem_unitSet_iff.mp (mem_univZero.mp hT x hx) + +/-- Propositional extensionality: truth values with the same +`pt`-membership are equal. -/ +theorem univZero_ext {A B : V} (hA : A ∈ˢ (univZero : V)) + (hB : B ∈ˢ (univZero : V)) (hab : pt ∈ˢ A → pt ∈ˢ B) + (hba : pt ∈ˢ B → pt ∈ˢ A) : A = B := + ext fun z => + ⟨fun hz => by + have h := eq_pt_of_mem_univZero hA hz; subst h; exact hab hz, + fun hz => by + have h := eq_pt_of_mem_univZero hB hz; subst h; exact hba hz⟩ + +open Classical in +/-- The truth value of a meta-level proposition: `{pt}` if it holds, +`∅` otherwise. -/ +noncomputable def truthVal (p : Prop) : V := if p then unitSet else empty + +theorem mem_truthVal {p : Prop} {z : V} : + z ∈ˢ (truthVal p : V) ↔ p ∧ z = pt := by + unfold truthVal + split + · next hp => exact ⟨fun hz => ⟨hp, mem_unitSet_iff.mp hz⟩, fun ⟨_, hz⟩ => hz ▸ pt_mem_unitSet⟩ + · next hp => + exact ⟨fun hz => absurd hz (not_mem_empty z), fun ⟨h, _⟩ => absurd h hp⟩ + +theorem pt_mem_truthVal {p : Prop} (hp : p) : (pt : V) ∈ˢ truthVal p := + mem_truthVal.mpr ⟨hp, rfl⟩ + +theorem of_mem_truthVal {p : Prop} {z : V} (hz : z ∈ˢ (truthVal p : V)) : p := + (mem_truthVal.mp hz).1 + +theorem eq_pt_of_mem_truthVal {p : Prop} {z : V} (hz : z ∈ˢ (truthVal p : V)) : + z = pt := + (mem_truthVal.mp hz).2 + +theorem truthVal_eq_unitSet {p : Prop} (hp : p) : (truthVal p : V) = unitSet := by + unfold truthVal; exact if_pos hp + +theorem truthVal_eq_empty {p : Prop} (hp : ¬ p) : (truthVal p : V) = empty := by + unfold truthVal; exact if_neg hp + +/-- Truth values are `∅` or `{pt}` — never the point `{∅}` itself. -/ +theorem truthVal_ne_pt (p : Prop) : (truthVal p : V) ≠ pt := by + intro h + by_cases hp : p + · rw [truthVal_eq_unitSet hp] at h + have hm := pt_mem_unitSet (V := V) + rw [h] at hm + exact not_mem_self (pt : V) hm + · rw [truthVal_eq_empty hp] at h + exact pt_ne_empty h.symm + +theorem truthVal_congr {p q : Prop} (h : p ↔ q) : + (truthVal p : V) = truthVal q := by + rcases Classical.em p with hp | hp + · rw [truthVal_eq_unitSet hp, truthVal_eq_unitSet (h.mp hp)] + · rw [truthVal_eq_empty hp, truthVal_eq_empty (fun hq => hp (h.mpr hq))] + +theorem truthVal_mem_univZero (p : Prop) : + (truthVal p : V) ∈ˢ univZero := by + rcases Classical.em p with hp | hp + · rw [truthVal_eq_unitSet hp] + exact mem_univZero.mpr (Subset.refl _) + · rw [truthVal_eq_empty hp] + exact mem_univZero.mpr (empty_subset _) + +/-- Every member of `univ 0` is the truth value of its own +inhabitedness. -/ +theorem mem_univZero_eq_truthVal {T : V} (hT : T ∈ˢ (univZero : V)) : + T = truthVal (pt ∈ˢ T) := by + rcases Classical.em ((pt : V) ∈ˢ T) with hp | hp + · rw [truthVal_eq_unitSet hp] + exact ext fun z => ⟨fun hz => eq_pt_of_mem_univZero hT hz ▸ pt_mem_unitSet, + fun hz => mem_unitSet_iff.mp hz ▸ hp⟩ + · rw [truthVal_eq_empty hp] + exact eq_empty fun z hz => hp (eq_pt_of_mem_univZero hT hz ▸ hz) + +/-- The truth value of an equality. -/ +noncomputable def eqv (x y : V) : V := truthVal (x = y) + +theorem eqv_mem_univZero (x y : V) : eqv x y ∈ˢ (univZero : V) := + truthVal_mem_univZero _ + +theorem eq_of_mem_eqv {a x y : V} (h : a ∈ˢ eqv x y) : x = y := + of_mem_truthVal h + +theorem pt_mem_eqv_self (x : V) : (pt : V) ∈ˢ eqv x x := + pt_mem_truthVal rfl + +/-- `pt`, `unitSet`, `univZero` and truth values live in every +(inhabited) Grothendieck universe. -/ +theorem _root_.Ix.Theory.Model.IsTGUniverse.pt_mem {U y : V} + (hU : IsTGUniverse (Mem (V := V)) U) (hy : y ∈ˢ U) : (pt : V) ∈ˢ U := + hU.sing_mem hy (hU.upair_mem hy (hU.empty_mem hy) + (hU.sing_mem hy (hU.sing_mem hy (hU.empty_mem hy)))) + +theorem _root_.Ix.Theory.Model.IsTGUniverse.unitSet_mem {U y : V} + (hU : IsTGUniverse (Mem (V := V)) U) (hy : y ∈ˢ U) : (unitSet : V) ∈ˢ U := + hU.sing_mem hy (hU.pt_mem hy) + +theorem _root_.Ix.Theory.Model.IsTGUniverse.univZero_mem {U y : V} + (hU : IsTGUniverse (Mem (V := V)) U) (hy : y ∈ˢ U) : (univZero : V) ∈ˢ U := + hU.power_mem (hU.unitSet_mem hy) + +theorem _root_.Ix.Theory.Model.IsTGUniverse.truthVal_mem {U y : V} + (hU : IsTGUniverse (Mem (V := V)) U) (hy : y ∈ˢ U) (p : Prop) : + (truthVal p : V) ∈ˢ U := + hU.transitive (hU.univZero_mem hy) (truthVal_mem_univZero p) + +/- Opaque interface operators (see `Derive/Empty.lean`). -/ +attribute [irreducible] ptTag pt unitSet eqv + +end Ix.Theory.Model.SetTheory diff --git a/Ix/Theory/Model/SetTheory/Derive/Quot.lean b/Ix/Theory/Model/SetTheory/Derive/Quot.lean new file mode 100644 index 000000000..4e2355e72 --- /dev/null +++ b/Ix/Theory/Model/SetTheory/Derive/Quot.lean @@ -0,0 +1,234 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetTheory/Derive/Quot.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetTheory.Derive.Univ + +@[expose] public section + +/-! +# Quotients + +`quotSet u A R` is, for `u ≠ 0`, the set of equivalence classes of the +equivalence closure of "`app (app R a) b` is inhabited" on `A` (classes +by separation, the class set by replacement); for `u = 0` the base +lives in `Prop`, everything collapses to the proof point, and the +quotient is `image (fun _ => pt) A` — the truth value `[A inhabited]`. + +`quotLift` lifts `f` to the quotient as the graph of +`q ↦ app f (representative of q)` (representatives by choice) — except +when `f` is the proof point, where the lift is the proof point too. +That tag is what makes the beta law `app (quotLift …) (quotClass … a) = +app f a` hold with *no typing premise on `f`* (matching the interface): +a `pt`-tagged `f` beta-reduces to `pt` on both sides, any other `f` +goes through the representative and the invariance premise, with the +`u = 0` collapse handled by `A ⊆ {pt}` (from `A ∈ univ 0`). +-/ + +namespace Ix.Theory.Model.SetTheory + +universe u + +variable {V : Type u} [SetTheory V] + +/-- The equivalence closure, on `A`, of "`app (app R a) b` is +inhabited". -/ +inductive QuotRel (A R : V) : V → V → Prop where + | base {a b : V} : a ∈ˢ A → b ∈ˢ A → (∃ w, w ∈ˢ app (app R a) b) → + QuotRel A R a b + | refl {a : V} : a ∈ˢ A → QuotRel A R a a + | symm {a b : V} : QuotRel A R a b → QuotRel A R b a + | trans {a b c : V} : QuotRel A R a b → QuotRel A R b c → QuotRel A R a c + +theorem QuotRel.mem {A R a b : V} (h : QuotRel A R a b) : a ∈ˢ A ∧ b ∈ˢ A := by + induction h with + | base ha hb _ => exact ⟨ha, hb⟩ + | refl ha => exact ⟨ha, ha⟩ + | symm _ ih => exact ⟨ih.2, ih.1⟩ + | trans _ _ ih₁ ih₂ => exact ⟨ih₁.1, ih₂.2⟩ + +/-- The `QuotRel`-class of `a` in `A`. -/ +noncomputable def qclass (A R a : V) : V := sep A (fun b => QuotRel A R a b) + +theorem mem_qclass {A R a b : V} : + b ∈ˢ qclass A R a ↔ b ∈ˢ A ∧ QuotRel A R a b := mem_sep + +theorem self_mem_qclass {A R a : V} (ha : a ∈ˢ A) : a ∈ˢ qclass A R a := + mem_qclass.mpr ⟨ha, QuotRel.refl ha⟩ + +theorem qclass_eq_of_rel {A R a b : V} (h : QuotRel A R a b) : + qclass A R a = qclass A R b := + ext fun z => by + rw [mem_qclass, mem_qclass] + exact ⟨fun ⟨hz, hr⟩ => ⟨hz, (h.symm).trans hr⟩, + fun ⟨hz, hr⟩ => ⟨hz, h.trans hr⟩⟩ + +theorem rel_of_qclass_eq {A R a b : V} (ha : a ∈ˢ A) (_hb : b ∈ˢ A) + (h : qclass A R a = qclass A R b) : QuotRel A R a b := + (mem_qclass.mp (h ▸ self_mem_qclass ha)).2.symm + +open Classical in +/-- The quotient (see module docs). -/ +noncomputable def quotSet (u : Nat) (A R : V) : V := + if u = 0 then image (fun _ => pt) A else image (fun a => qclass A R a) A + +open Classical in +/-- The class of `a` in the quotient. -/ +noncomputable def quotClass (u : Nat) (A R a : V) : V := + if u = 0 then pt else qclass A R a + +theorem quotClass_mem {u : Nat} {A R a : V} (ha : a ∈ˢ A) : + quotClass u A R a ∈ˢ quotSet u A R := by + unfold quotClass quotSet + split + · exact mem_image.mpr ⟨a, ha, rfl⟩ + · exact mem_image.mpr ⟨a, ha, rfl⟩ + +theorem quotClass_surj {u : Nat} {A R q : V} (hq : q ∈ˢ quotSet u A R) : + ∃ a, a ∈ˢ A ∧ q = quotClass u A R a := by + unfold quotSet at hq + unfold quotClass + split at hq + · next h => + obtain ⟨a, ha, rfl⟩ := mem_image.mp hq + exact ⟨a, ha, (if_pos h).symm⟩ + · next h => + obtain ⟨a, ha, rfl⟩ := mem_image.mp hq + exact ⟨a, ha, (if_neg h).symm⟩ + +/-- A class formed at ONE pair of parameters that lands in the quotient +of ANOTHER: its representative lies in the second parameter's carrier, +and the class is the second quotient's class of that same +representative. Nothing relates the two parameter pairs — at a positive +level the first class is a `qclass` of the second's, and at level zero +both classes are the point and the second carrier is inhabited because +the quotient is. -/ +theorem quotClass_of_mem_quotSet {u : Nat} {Aset R A' R' a : V} + (hAset : Aset ∈ˢ (univ u : V)) (hA' : A' ∈ˢ (univ u : V)) (ha : a ∈ˢ A') + (hmem : quotClass u A' R' a ∈ˢ quotSet u Aset R) : + a ∈ˢ Aset ∧ quotClass u A' R' a = quotClass u Aset R a := by + obtain ⟨b, hb, hcls⟩ := quotClass_surj hmem + by_cases hu : u = 0 + · subst hu + refine ⟨?_, by unfold quotClass; rw [if_pos rfl, if_pos rfl]⟩ + rw [univ_zero] at hA' hAset + rw [eq_pt_of_mem_univZero hA' ha, ← eq_pt_of_mem_univZero hAset hb] + exact hb + · unfold quotClass at hcls ⊢ + rw [if_neg hu, if_neg hu] at hcls + rw [if_neg hu, if_neg hu] + have hab : a ∈ˢ qclass Aset R b := by rw [← hcls]; exact self_mem_qclass ha + obtain ⟨haA, hrel⟩ := mem_qclass.mp hab + exact ⟨haA, hcls.trans (qclass_eq_of_rel hrel)⟩ + +theorem quotSound {u : Nat} {A R a b w : V} (ha : a ∈ˢ A) (hb : b ∈ˢ A) + (hw : w ∈ˢ app (app R a) b) : quotClass u A R a = quotClass u A R b := by + unfold quotClass + split + · rfl + · exact qclass_eq_of_rel (QuotRel.base ha hb ⟨w, hw⟩) + +theorem quotSet_mem_univ {u : Nat} {A R : V} (hA : A ∈ˢ (univ u : V)) : + quotSet u A R ∈ˢ (univ u : V) := by + unfold quotSet + split + · next h => + subst h + rw [univ_zero] + refine mem_univZero.mpr fun z hz => ?_ + obtain ⟨-, -, rfl⟩ := mem_image.mp hz + exact pt_mem_unitSet + · next h => + refine (univ_isTGUniverse h).mem_of_subset_mem + ((univ_isTGUniverse h).power_mem hA) fun z hz => ?_ + obtain ⟨a, -, rfl⟩ := mem_image.mp hz + exact mem_power.mpr fun w hw => (mem_qclass.mp hw).1 + +open Classical in +/-- A representative of a quotient class, by choice. -/ +noncomputable def qrep (u : Nat) (A R q : V) : V := + if h : ∃ a, a ∈ˢ A ∧ q = quotClass u A R a then Classical.choose h else empty + +theorem qrep_spec {u : Nat} {A R q : V} (hq : q ∈ˢ quotSet u A R) : + qrep u A R q ∈ˢ A ∧ q = quotClass u A R (qrep u A R q) := by + unfold qrep + rw [dif_pos (quotClass_surj hq)] + exact Classical.choose_spec (quotClass_surj hq) + +/-- The invariance premise extends from the base relation to its +equivalence closure. -/ +theorem app_eq_of_rel {A R f a b : V} + (hinv : ∀ a' b', a' ∈ˢ A → b' ∈ˢ A → (∃ w, w ∈ˢ app (app R a') b') → + app f a' = app f b') + (h : QuotRel A R a b) : app f a = app f b := by + induction h with + | base ha hb hw => exact hinv _ _ ha hb hw + | refl _ => rfl + | symm _ ih => exact ih.symm + | trans _ _ ih₁ ih₂ => exact ih₁.trans ih₂ + +/-- Equal classes have equal `f`-values: by closure invariance at +`u ≠ 0`, and by the `A ⊆ {pt}` collapse at `u = 0`. -/ +theorem app_eq_of_quotClass_eq {u : Nat} {A R f a b : V} + (hA : A ∈ˢ (univ u : V)) (ha : a ∈ˢ A) (hb : b ∈ˢ A) + (hinv : ∀ a' b', a' ∈ˢ A → b' ∈ˢ A → (∃ w, w ∈ˢ app (app R a') b') → + app f a' = app f b') + (hq : quotClass u A R a = quotClass u A R b) : app f a = app f b := by + unfold quotClass at hq + split at hq + · next h => + subst h + rw [univ_zero] at hA + rw [eq_pt_of_mem_univZero hA ha, eq_pt_of_mem_univZero hA hb] + · exact app_eq_of_rel hinv (rel_of_qclass_eq ha hb hq) + +/-! ## pt-freshness refutation evidence (task #109) + +Quotient types are **not** unconditionally pt-fresh, for *any* +constructible proof point: classes are arbitrary nonempty subsets of +the base, so with base `ptTag` itself and a total relation the class +set is exactly `{ptTag} = pt`. Never resurrect a `quotSet_ne_pt`; the +#109 syntactic freshness guards exclude `Quot`-typed slots instead. +(The base `ptTag` is not the interpretation of any *writable* type — +the obstruction is semantic, in the ∀-A-R quantification of the model +lemmas. The old `pt = {∅}` was the unique choice immune to this — the +empty set is never a class — which is exactly what the re-choice +trades for data freshness.) -/ + +theorem quotSet_eq_pt_countermodel : + ∃ A R : V, quotSet 1 A R = pt := by + refine ⟨ptTag, graph (fun _ => graph (fun _ => unitSet) ptTag) ptTag, ?_⟩ + have hrel : ∀ a b : V, a ∈ˢ (ptTag : V) → b ∈ˢ (ptTag : V) → + QuotRel ptTag (graph (fun _ => graph (fun _ => unitSet) ptTag) ptTag) + a b := by + intro a b ha hb + refine QuotRel.base ha hb ⟨pt, ?_⟩ + rw [app_graph ha, app_graph hb] + exact pt_mem_unitSet + have hclass : ∀ a : V, a ∈ˢ (ptTag : V) → + qclass ptTag (graph (fun _ => graph (fun _ => unitSet) ptTag) ptTag) a + = ptTag := by + intro a ha + apply ext fun z => ?_ + rw [mem_qclass] + exact ⟨fun h => h.1, fun hz => ⟨hz, hrel a z ha hz⟩⟩ + unfold quotSet + rw [if_neg Nat.one_ne_zero] + apply ext fun z => ?_ + rw [mem_image, mem_pt] + constructor + · rintro ⟨a, ha, rfl⟩ + exact hclass a ha + · rintro rfl + exact ⟨empty, empty_mem_ptTag, (hclass empty empty_mem_ptTag).symm⟩ + +/- Opaque interface operators (see `Derive/Empty.lean`). -/ +attribute [irreducible] quotSet quotClass + +end Ix.Theory.Model.SetTheory diff --git a/Ix/Theory/Model/SetTheory/Derive/Sep.lean b/Ix/Theory/Model/SetTheory/Derive/Sep.lean new file mode 100644 index 000000000..71f78d3f0 --- /dev/null +++ b/Ix/Theory/Model/SetTheory/Derive/Sep.lean @@ -0,0 +1,74 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetTheory/Derive/Sep.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetTheory.Derive.Empty + +@[expose] public section + +/-! +# Separation, derived from replacement + +The classical trick: to separate `{x ∈ a : p x}`, either the result is +empty (take `empty`), or some witness `w₀ ∈ a` with `p w₀` exists and +the total function `x ↦ if p x then x else w₀` replaces `a` onto +exactly the separated set (the default value `w₀` is already a member +of it). The detour is forced because replacement's antecedent demands +totality. +-/ + +namespace Ix.Theory.Model.SetTheory + +universe u + +variable {V : Type u} [SetTheory V] + +open Classical in +/-- Separation: `{x ∈ a : p x}`, for an arbitrary meta-level predicate. -/ +noncomputable def sep (a : V) (p : V → Prop) : V := + if h : ∃ x, x ∈ˢ a ∧ p x then + image (fun x => if p x then x else Classical.choose h) a + else empty + +theorem mem_sep {a z : V} {p : V → Prop} : z ∈ˢ sep a p ↔ z ∈ˢ a ∧ p z := by + unfold sep + split + · next h => + obtain ⟨hw₀a, hw₀p⟩ := Classical.choose_spec h + rw [mem_image] + constructor + · rintro ⟨w, hw, rfl⟩ + split + · next hpw => exact ⟨hw, hpw⟩ + · exact ⟨hw₀a, hw₀p⟩ + · rintro ⟨hz, hp⟩ + exact ⟨z, hz, by simp [hp]⟩ + · next h => + constructor + · intro hz; exact absurd hz (not_mem_empty z) + · intro ⟨hz, hp⟩; exact absurd ⟨z, hz, hp⟩ h + +theorem sep_subset {a : V} {p : V → Prop} : sep a p ⊆ˢ a := + fun _ hz => (mem_sep.mp hz).1 + +theorem sep_congr {a : V} {p q : V → Prop} (h : ∀ x, x ∈ˢ a → (p x ↔ q x)) : + sep a p = sep a q := + ext fun z => by + rw [mem_sep, mem_sep] + exact ⟨fun ⟨hz, hp⟩ => ⟨hz, (h z hz).mp hp⟩, fun ⟨hz, hq⟩ => ⟨hz, (h z hz).mpr hq⟩⟩ + +/-- Image congruence, the companion fact. -/ +theorem image_congr {a : V} {f g : V → V} (h : ∀ x, x ∈ˢ a → f x = g x) : + image f a = image g a := + ext fun z => by + rw [mem_image, mem_image] + exact ⟨fun ⟨w, hw, hz⟩ => ⟨w, hw, hz.trans (h w hw)⟩, + fun ⟨w, hw, hz⟩ => ⟨w, hw, hz.trans (h w hw).symm⟩⟩ + +end Ix.Theory.Model.SetTheory diff --git a/Ix/Theory/Model/SetTheory/Derive/Sigma.lean b/Ix/Theory/Model/SetTheory/Derive/Sigma.lean new file mode 100644 index 000000000..5bbcd9e6f --- /dev/null +++ b/Ix/Theory/Model/SetTheory/Derive/Sigma.lean @@ -0,0 +1,158 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetTheory/Derive/Sigma.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetTheory.Derive.Univ + +@[expose] public section + +/-! +# Dependent pairs, with the level-0 truncation + +`sigmaSet w A B` is, for `w ≠ 0`, the raw pair set `sigmaPairs A B` +(Kuratowski pairs `⟨a, b⟩` with `a ∈ A`, `b ∈ B a`); for `w = 0` the +truth value `[∃ a ∈ A, B a inhabited]` (the `Prop` collapse). + +`spair` is the Kuratowski pair; `sfst`/`ssnd` extract the components +classically (well-defined by `kpair_inj`) and default to `pt` on +non-pairs — in particular on `pt` itself, which is never a pair, so +`sfst pt = ssnd pt = pt` holds by the tag. +-/ + +namespace Ix.Theory.Model.SetTheory + +universe u + +variable {V : Type u} [SetTheory V] + +open Classical in +/-- Dependent pair set at level `w` (see module docs). -/ +noncomputable def sigmaSet (w : Nat) (A : V) (B : V → V) : V := + if w = 0 then truthVal (∃ x, x ∈ˢ A ∧ ∃ y, y ∈ˢ B x) else sigmaPairs A B + +/-- Pairing: the Kuratowski pair. -/ +noncomputable def spair (a b : V) : V := kpair a b + +open Classical in +/-- First projection, `pt` on non-pairs. -/ +noncomputable def sfst (p : V) : V := + if h : ∃ a b, p = kpair a b then Classical.choose h else pt + +open Classical in +/-- Second projection, `pt` on non-pairs. -/ +noncomputable def ssnd (p : V) : V := + if h : ∃ a b, p = kpair a b then Classical.choose (Classical.choose_spec h) + else pt + +theorem sigmaSet_zero {A : V} {B : V → V} : + sigmaSet 0 A B = truthVal (∃ x, x ∈ˢ A ∧ ∃ y, y ∈ˢ B x) := by + unfold sigmaSet; exact if_pos rfl + +theorem sigmaSet_pos {w : Nat} (hw : w ≠ 0) {A : V} {B : V → V} : + sigmaSet w A B = sigmaPairs A B := by + unfold sigmaSet; exact if_neg hw + +theorem sigma_congr {w : Nat} {A : V} {B B' : V → V} + (h : ∀ x, x ∈ˢ A → B x = B' x) : sigmaSet w A B = sigmaSet w A B' := by + rcases Nat.eq_zero_or_pos w with rfl | hw + · rw [sigmaSet_zero, sigmaSet_zero] + refine truthVal_congr ⟨?_, ?_⟩ + · rintro ⟨x, hx, y, hy⟩; exact ⟨x, hx, y, h x hx ▸ hy⟩ + · rintro ⟨x, hx, y, hy⟩; exact ⟨x, hx, y, (h x hx).symm ▸ hy⟩ + · rw [sigmaSet_pos (Nat.pos_iff_ne_zero.mp hw), sigmaSet_pos (Nat.pos_iff_ne_zero.mp hw)] + exact sigmaPairs_congr h + +theorem spair_mem {w : Nat} {A : V} {B : V → V} {a b : V} (hw : w ≠ 0) + (ha : a ∈ˢ A) (hb : b ∈ˢ B a) : spair a b ∈ˢ sigmaSet w A B := by + rw [sigmaSet_pos hw] + exact mem_sigmaPairs.mpr ⟨a, ha, b, hb, rfl⟩ + +theorem pt_mem_sigma {A : V} {B : V → V} {a b : V} + (ha : a ∈ˢ A) (hb : b ∈ˢ B a) : (pt : V) ∈ˢ sigmaSet 0 A B := by + rw [sigmaSet_zero] + exact pt_mem_truthVal ⟨a, ha, b, hb⟩ + +theorem mem_sigma_elim {w : Nat} {A : V} {B : V → V} {t : V} + (ht : t ∈ˢ sigmaSet w A B) : + ∃ a b, a ∈ˢ A ∧ b ∈ˢ B a ∧ (w = 0 → t = pt) ∧ (w ≠ 0 → t = spair a b) := by + rcases Nat.eq_zero_or_pos w with rfl | hw + · rw [sigmaSet_zero] at ht + obtain ⟨a, ha, b, hb⟩ := of_mem_truthVal ht + exact ⟨a, b, ha, hb, fun _ => eq_pt_of_mem_truthVal ht, fun h0 => absurd rfl h0⟩ + · have hw' : w ≠ 0 := Nat.pos_iff_ne_zero.mp hw + rw [sigmaSet_pos hw'] at ht + obtain ⟨a, ha, b, hb, rfl⟩ := mem_sigmaPairs.mp ht + exact ⟨a, b, ha, hb, fun h0 => absurd h0 hw', fun _ => rfl⟩ + +theorem sfst_spair (a b : V) : sfst (spair a b) = a := by + unfold sfst spair + rw [dif_pos ⟨a, b, rfl⟩] + have hs := Classical.choose_spec + (⟨a, b, rfl⟩ : ∃ a' b', (kpair a b : V) = kpair a' b') + have hs2 := Classical.choose_spec hs + exact (kpair_inj hs2).1.symm + +theorem ssnd_spair (a b : V) : ssnd (spair a b) = b := by + unfold ssnd spair + rw [dif_pos ⟨a, b, rfl⟩] + have hs := Classical.choose_spec + (⟨a, b, rfl⟩ : ∃ a' b', (kpair a b : V) = kpair a' b') + have hs2 := Classical.choose_spec hs + exact (kpair_inj hs2).2.symm + +theorem spair_eq_kpair (a b : V) : spair a b = kpair a b := by unfold spair; rfl +theorem sfst_kpair (a b : V) : sfst (kpair a b) = a := by rw [← spair_eq_kpair]; exact sfst_spair a b +theorem ssnd_kpair (a b : V) : ssnd (kpair a b) = b := by rw [← spair_eq_kpair]; exact ssnd_spair a b + +theorem sfst_pt : sfst (pt : V) = pt := by + unfold sfst + rw [dif_neg] + rintro ⟨a, b, h⟩ + exact pt_ne_kpair a b h + +theorem ssnd_pt : ssnd (pt : V) = pt := by + unfold ssnd + rw [dif_neg] + rintro ⟨a, b, h⟩ + exact pt_ne_kpair a b h + +/-- A pair set is never the proof point: at level `0` it is a truth +value, at positive levels its members are Kuratowski pairs while `pt`'s +one member is `∅`. (The collapse-era replacement for tag-based +non-`pt`-ness of stored pair-set values, task #100.) -/ +theorem sigmaSet_ne_pt {w : Nat} {A : V} {B : V → V} : + sigmaSet w A B ≠ (pt : V) := by + rcases Nat.eq_zero_or_pos w with rfl | hw + · rw [sigmaSet_zero] + exact truthVal_ne_pt _ + · rw [sigmaSet_pos (Nat.pos_iff_ne_zero.mp hw)] + intro h + have hmem : (ptTag : V) ∈ˢ sigmaPairs A B := by + rw [h] + exact ptTag_mem_pt + obtain ⟨a, -, b, -, hp⟩ := mem_sigmaPairs.mp hmem + exact ptTag_ne_kpair a b hp + +/-- Formation along the tower, at the joint level `max u v`. -/ +theorem sigma_mem_univ {u v : Nat} {A : V} {B : V → V} + (hA : A ∈ˢ (univ u : V)) (hB : ∀ x, x ∈ˢ A → B x ∈ˢ (univ v : V)) : + sigmaSet (Nat.max u v) A B ∈ˢ (univ (Nat.max u v) : V) := by + rcases Nat.eq_zero_or_pos (Nat.max u v) with hw | hw + · rw [hw, sigmaSet_zero, univ_zero] + exact truthVal_mem_univZero _ + · have hw' : Nat.max u v ≠ 0 := Nat.pos_iff_ne_zero.mp hw + rw [sigmaSet_pos hw'] + exact (univ_isTGUniverse hw').sigmaPairs_mem + (univ_mono (Nat.le_max_left u v) A hA) + (fun x hx => univ_mono (Nat.le_max_right u v) _ (hB x hx)) + +/- Opaque interface operators (see `Derive/Empty.lean`). -/ +attribute [irreducible] sigmaSet spair sfst ssnd + +end Ix.Theory.Model.SetTheory diff --git a/Ix/Theory/Model/SetTheory/Derive/Univ.lean b/Ix/Theory/Model/SetTheory/Derive/Univ.lean new file mode 100644 index 000000000..75b8523cf --- /dev/null +++ b/Ix/Theory/Model/SetTheory/Derive/Univ.lean @@ -0,0 +1,110 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetTheory/Derive/Univ.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetTheory.Derive.Graphs +public import Ix.Theory.Model.SetTheory.Derive.Omega + +@[expose] public section + +/-! +# The universe tower + +`univ 0` is the set of truth values (`univZero`, the `U₀` of Mario +Carneiro, *The Type Theory of Lean*, master's thesis, Carnegie Mellon +University, 2019); +`univ (n+1)` is the chain universe `univChain (n+2)`. The tower +starts two levels up the chain so that every positive level has the +inductive set `univChain 1` as a member — which is what puts `ω` +inside every positive level (a bare universe need not contain `ω`; +`V_ω` is one — and `univChain 0` need not be inhabited, the empty set +satisfying `IsTGUniverse` vacuously, so `univChain 1` is the first chain +member known to be inductive). `univ 0 ∈ univ 1` because `univZero` +is built from `∅` by pairing/power closure inside the inhabited +universe `univChain 1 ∈ univChain 2`. Cumulativity holds because +positive levels are transitive and each level is a member of the +next. +-/ + +namespace Ix.Theory.Model.SetTheory + +universe u + +variable {V : Type u} [SetTheory V] + +/-- The universe tower interpreting `Sort n`. -/ +noncomputable def univ : Nat → V + | 0 => univZero + | n + 1 => univChain (n + 2) + +theorem univ_zero : (univ 0 : V) = univZero := by rfl + +/-- Positive levels are Grothendieck universes. -/ +theorem univ_isTGUniverse {n : Nat} (hn : n ≠ 0) : + IsTGUniverse (Mem (V := V)) (univ n) := by + match n, hn with + | m + 1, _ => exact univChain_tg _ + +theorem univChain_one_mem_univ_succ (n : Nat) : + (univChain 1 : V) ∈ˢ univ (n + 1) := + univChain_mem_of_lt (Nat.succ_lt_succ n.succ_pos) + +theorem univ_mem_univ (n : Nat) : (univ n : V) ∈ˢ univ (n + 1) := by + match n with + | 0 => + exact (univChain_tg 2).transitive (univChain_mem 1) + ((univChain_tg 1).univZero_mem (univChain_mem 0)) + | m + 1 => exact univChain_mem (m + 2) + +theorem univ_subset_succ (n : Nat) : (univ n : V) ⊆ˢ univ (n + 1) := + (univ_isTGUniverse (Nat.succ_ne_zero n)).subset_of_mem (univ_mem_univ n) + +theorem univ_mono {m n : Nat} (h : m ≤ n) : (univ m : V) ⊆ˢ univ n := by + induction n with + | zero => cases Nat.le_zero.mp h; exact Subset.refl _ + | succ n ih => + rcases Nat.lt_succ_iff_lt_or_eq.mp (Nat.lt_succ_of_le h) with h' | rfl + · exact (ih (Nat.lt_succ_iff.mp h')).trans (univ_subset_succ n) + · exact Subset.refl _ + +/-- **The universe tower is injective.** `univ u ∈ˢ univ (u+1) ⊆ˢ univ v` +whenever `u < v`, so an equality of two levels' universes would put a set +inside itself, against regularity (`not_mem_self`). + +Note what this does *not* say, and what nothing can: the tower is +**cumulative** (`univ_mono`), so a *membership* `x ∈ˢ univ u` fixes only +a lower bound on `u` and two memberships of one value never determine a +level. Injectivity of `univ` itself is the only handle on levels the +tower offers, and every consumer that needs "the sort is `u`" has to +reach it through an equality of universes, not through a typing. -/ +theorem univ_inj {u v : Nat} (h : (univ u : V) = univ v) : u = v := by + rcases Nat.lt_trichotomy u v with hlt | heq | hgt + · exact absurd (h ▸ univ_mono (V := V) hlt _ (univ_mem_univ u)) + (not_mem_self (univ v : V)) + · exact heq + · exact absurd (h ▸ univ_mono (V := V) hgt _ (univ_mem_univ v)) + (not_mem_self (univ u : V)) + +theorem omega_mem_univ_succ (n : Nat) : (omega : V) ∈ˢ univ (n + 1) := + (univ_isTGUniverse (Nat.succ_ne_zero n)).omega_mem (univChain_one_mem_univ_succ n) + +theorem empty_mem_univ : ∀ n : Nat, (empty : V) ∈ˢ univ n + | 0 => mem_univZero.mpr (empty_subset _) + | n + 1 => + (univ_isTGUniverse (Nat.succ_ne_zero n)).empty_mem (univChain_one_mem_univ_succ n) + +theorem unitSet_mem_univ : ∀ n : Nat, (unitSet : V) ∈ˢ univ n + | 0 => mem_univZero.mpr (Subset.refl _) + | n + 1 => + (univ_isTGUniverse (Nat.succ_ne_zero n)).unitSet_mem (univChain_one_mem_univ_succ n) + +/- Opaque interface operator (see `Derive/Empty.lean`). -/ +attribute [irreducible] univ + +end Ix.Theory.Model.SetTheory diff --git a/Ix/Theory/Model/SetTheory/Derive/Universe.lean b/Ix/Theory/Model/SetTheory/Derive/Universe.lean new file mode 100644 index 000000000..6ae805d1f --- /dev/null +++ b/Ix/Theory/Model/SetTheory/Derive/Universe.lean @@ -0,0 +1,175 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source: ConLeche/SetTheory/Derive/Universe.lean +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespace and import paths adapted to Ix.Theory.Model. +-/ + +module + +public import Ix.Theory.Model.SetTheory.Derive.Pair + +@[expose] public section + +/-! +# Grothendieck universes: the diagonal argument and the closure laws + +The engine room, recovering the Grothendieck-universe closure laws +(SGA 4, Exp. I, Appendix) from the Axiom-A matrix `IsTGUniverse` +(Tarski 1938) — applied downstream to the chain members `univChain n`. +The inaccessibility clause offers, for any subset `S ⊆ u`, the +disjunction `S ≈ u ∨ S ∈ u`; the single lemma `covered_mem` turns it +into a closure property by refuting the first disjunct with Cantor's +diagonal whenever `S` is covered by a function from a *member* of `u`. +Everything else — pairing, power, binary union, replacement images, +`⋃` of a member — reduces to it (`⋃` via a two-step surjection +argument ending in a membership 2-cycle). +-/ + +namespace Ix.Theory.Model.SetTheory + +universe u + +variable {V : Type u} [SetTheory V] + +section IsTGUniverse + +variable {U : V} (hU : IsTGUniverse (Mem (V := V)) U) + +include hU + +theorem _root_.Ix.Theory.Model.IsTGUniverse.transitive : ∀ {y z : V}, y ∈ˢ U → z ∈ˢ y → z ∈ˢ U := + fun {y z} hy hz => hU.1 y z hy hz + +theorem _root_.Ix.Theory.Model.IsTGUniverse.mem_of_subset_mem : ∀ {y z : V}, y ∈ˢ U → z ⊆ˢ y → z ∈ˢ U := + fun {y z} hy hz => hU.2.1 y z hy hz + +theorem _root_.Ix.Theory.Model.IsTGUniverse.subset_of_mem {y : V} (hy : y ∈ˢ U) : y ⊆ˢ U := + fun _ hz => hU.transitive hy hz + +theorem _root_.Ix.Theory.Model.IsTGUniverse.power_mem {y : V} (hy : y ∈ˢ U) : power y ∈ˢ U := by + obtain ⟨p, hp, hsub⟩ := hU.2.2.1 y hy + exact hU.mem_of_subset_mem hp fun z hz => hsub z (mem_power.mp hz) + +/-- Cantor's diagonal, in the form everything downstream uses: a subset +of `U` covered by the image of a *member* of `U` is itself a member. +The equinumerosity disjunct is refuted by diagonalizing the covering +composed with the would-be surjection onto `U`. -/ +theorem _root_.Ix.Theory.Model.IsTGUniverse.covered_mem {I S : V} {F : V → V} + (hI : I ∈ˢ U) (hS : S ⊆ˢ U) + (hcov : ∀ b, b ∈ˢ S → ∃ x, x ∈ˢ I ∧ F x = b) : S ∈ˢ U := by + rcases hU.2.2.2 S hS with ⟨f, -, -, hsurj⟩ | hmem + · -- `f : S ↠ U`; diagonalize `x ↦ f (F x) : I ↠ U` at + -- `D := {x ∈ I : x ∉ f (F x)}`. + exfalso + have hDU : sep I (fun x => ¬ x ∈ˢ f (F x)) ∈ˢ U := + hU.mem_of_subset_mem hI sep_subset + obtain ⟨b, hbS, hfb⟩ := hsurj _ hDU + obtain ⟨j, hjI, hFj⟩ := hcov b hbS + have hGj : f (F j) = sep I (fun x => ¬ x ∈ˢ f (F x)) := by rw [hFj, hfb] + have hiff : j ∈ˢ sep I (fun x => ¬ x ∈ˢ f (F x)) ↔ + ¬ j ∈ˢ sep I (fun x => ¬ x ∈ˢ f (F x)) := by + constructor + · intro hj + have := (mem_sep.mp hj).2 + rwa [hGj] at this + · intro hn + exact mem_sep.mpr ⟨hjI, by rwa [hGj]⟩ + rcases Classical.em (j ∈ˢ sep I (fun x => ¬ x ∈ˢ f (F x))) with hj | hj + · exact hiff.mp hj hj + · exact hj (hiff.mpr hj) + · exact hmem + +/-- Replacement closure: the image of a member under a fibre-wise +member-valued function is a member. -/ +theorem _root_.Ix.Theory.Model.IsTGUniverse.image_mem {A : V} {F : V → V} + (hA : A ∈ˢ U) (hF : ∀ x, x ∈ˢ A → F x ∈ˢ U) : image F A ∈ˢ U := + hU.covered_mem hA + (fun z hz => by + obtain ⟨w, hw, rfl⟩ := mem_image.mp hz + exact hF w hw) + (fun b hb => by + obtain ⟨w, hw, rfl⟩ := mem_image.mp hb + exact ⟨w, hw, rfl⟩) + +theorem _root_.Ix.Theory.Model.IsTGUniverse.empty_mem {y : V} (hy : y ∈ˢ U) : (empty : V) ∈ˢ U := + hU.mem_of_subset_mem hy (empty_subset y) + +open Classical in +/-- Pairing closure, via `covered_mem` from the two-element member +`power (power ∅) = {∅, {∅}}`. -/ +theorem _root_.Ix.Theory.Model.IsTGUniverse.upair_mem {a b y : V} (hy : y ∈ˢ U) + (ha : a ∈ˢ U) (hb : b ∈ˢ U) : upair a b ∈ˢ U := by + have h2 : power (power (empty : V)) ∈ˢ U := + (hU.empty_mem hy |> hU.power_mem) |> hU.power_mem + refine hU.covered_mem (F := fun x => if x = empty then a else b) h2 ?_ ?_ + · intro z hz + rcases mem_upair.mp hz with h | h <;> subst h <;> assumption + · intro z hz + rcases mem_upair.mp hz with h | h <;> subst h + · exact ⟨empty, mem_power.mpr (empty_subset _), by simp⟩ + · refine ⟨power empty, mem_power.mpr (Subset.refl _), ?_⟩ + have hne : power (empty : V) ≠ empty := + ne_empty_of_mem (mem_power.mpr (Subset.refl _)) + simp [hne] + +theorem _root_.Ix.Theory.Model.IsTGUniverse.sing_mem {a y : V} (hy : y ∈ˢ U) (ha : a ∈ˢ U) : + sing a ∈ˢ U := hU.upair_mem hy ha ha + +/-- `⋃` closure. A surjection `f : ⋃s ↠ U` would make `U` the union +of the member `S = {f-image of w : w ∈ s}`, putting `S ∈ U ⊆ ⋃S` — a +2-cycle. -/ +theorem _root_.Ix.Theory.Model.IsTGUniverse.sUnion_mem {s : V} (hs : s ∈ˢ U) : sUnion s ∈ˢ U := by + have hsub : sUnion s ⊆ˢ U := fun z hz => by + obtain ⟨y, hy, hzy⟩ := mem_sUnion.mp hz + exact hU.transitive (hU.transitive hs hy) hzy + rcases hU.2.2.2 (sUnion s) hsub with ⟨f, hinto, -, hsurj⟩ | hmem + · exfalso + -- For each `w ∈ s`, the `f`-image of `w` is a member of `U` … + have himg : ∀ w, w ∈ˢ s → image f w ∈ˢ U := fun w hw => + hU.image_mem (hU.transitive hs hw) fun x hx => + hinto x (mem_sUnion.mpr ⟨w, hw, hx⟩) + -- … so the set of all these images is a member too … + have hS : image (fun w => image f w) s ∈ˢ U := + hU.image_mem hs fun w hw => himg w hw + -- … but `U ⊆ ⋃(that set)`, giving `S ∈ U ⊆ ⋃S`: a 2-cycle. + obtain ⟨x, hx, hfx⟩ := hsurj _ hS + obtain ⟨w, hw, hxw⟩ := mem_sUnion.mp hx + have : image (fun w => image f w) s ∈ˢ image f w := + hfx ▸ mem_image.mpr ⟨x, hxw, rfl⟩ + exact no_two_cycle this (mem_image.mpr ⟨w, hw, rfl⟩) + · exact hmem + +theorem _root_.Ix.Theory.Model.IsTGUniverse.binUnion_mem {a b y : V} (hy : y ∈ˢ U) + (ha : a ∈ˢ U) (hb : b ∈ˢ U) : binUnion a b ∈ˢ U := + hU.sUnion_mem (hU.upair_mem hy ha hb) + +theorem _root_.Ix.Theory.Model.IsTGUniverse.kpair_mem {a b y : V} (hy : y ∈ˢ U) + (ha : a ∈ˢ U) (hb : b ∈ˢ U) : kpair a b ∈ˢ U := + hU.upair_mem hy (hU.sing_mem hy ha) (hU.upair_mem hy ha hb) + +theorem _root_.Ix.Theory.Model.IsTGUniverse.sep_mem {a : V} {p : V → Prop} (ha : a ∈ˢ U) : + sep a p ∈ˢ U := + hU.mem_of_subset_mem ha sep_subset + +/-- Union of a member-indexed family of members. -/ +theorem _root_.Ix.Theory.Model.IsTGUniverse.famUnion_mem {A : V} {F : V → V} + (hA : A ∈ˢ U) (hF : ∀ x, x ∈ˢ A → F x ∈ˢ U) : + sUnion (image F A) ∈ˢ U := + hU.sUnion_mem (hU.image_mem hA hF) + +end IsTGUniverse + +/-- Chain membership generalizes along the ordering, by transitivity +of the higher universe. -/ +theorem univChain_mem_of_lt {m n : Nat} (h : m < n) : + (univChain m : V) ∈ˢ univChain n := by + induction n with + | zero => exact absurd h (Nat.not_lt_zero m) + | succ n ih => + rcases Nat.lt_succ_iff_lt_or_eq.mp h with h' | rfl + · exact (univChain_tg (n + 1)).transitive (univChain_mem n) (ih h') + · exact univChain_mem m + +end Ix.Theory.Model.SetTheory diff --git a/Ix/Theory/Model/Signature.lean b/Ix/Theory/Model/Signature.lean new file mode 100644 index 000000000..fd69a8e35 --- /dev/null +++ b/Ix/Theory/Model/Signature.lean @@ -0,0 +1,159 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Extension + +namespace Ix.Theory.Model + +universe u v +variable {β : Type u} + +/-- An internal signature overlay does not by itself admit declarations. -/ +def Environment.overlay (entries additions : Environment β) : Environment β := + fun r => match additions r with + | some entry => some entry + | none => entries r + +def Assignment.overlay (constants values : Assignment β V) (additions : Environment β) : Assignment β V := + fun r levels => match additions r with + | some _ => values r levels + | none => constants r levels + +def Environment.Fresh (entries additions : Environment β) : Prop := + ∀ r entry, additions r = some entry → entries r = none + +theorem Environment.overlay_new {entries additions : Environment β} {r : ConstRef β} + {entry : ConstantEntry β} (h : additions r = some entry) : + entries.overlay additions r = some entry := by simp only [overlay, h] + +theorem Environment.overlay_old {entries additions : Environment β} + (hf : entries.Fresh additions) {r : ConstRef β} {entry : ConstantEntry β} + (h : entries r = some entry) : entries.overlay additions r = some entry := by + cases ha : additions r with + | none => simpa only [overlay, ha] using h + | some new => have he := hf r new ha; rw [h] at he; contradiction + +theorem Assignment.overlay_agrees {entries additions : Environment β} (hf : entries.Fresh additions) + (constants values : Assignment β V) : Assignment.AgreesOn entries constants (constants.overlay values additions) := by + intro r entry hr levels + cases ha : additions r with + | none => simp only [overlay, ha] + | some new => have he := hf r new ha; rw [hr] at he; contradiction + +theorem AExpr.ReferencesIn.overlay {entries additions : Environment β} {e : AExpr β} + (h : e.ReferencesIn entries) : e.ReferencesIn (entries.overlay additions) := by + intro r hr + cases ha : additions r with + | none => simpa only [Environment.overlay, ha] using h r hr + | some _ => simp only [Environment.overlay, ha, Option.isSome_some] + +theorem ConstantFact.ReferencesIn.overlay {entries additions : Environment β} {fact : ConstantFact β} + (h : fact.ReferencesIn entries) : fact.ReferencesIn (entries.overlay additions) := by + intro r hr + cases ha : additions r with + | none => simpa only [Environment.overlay, ha] using h r hr + | some _ => simp only [Environment.overlay, ha, Option.isSome_some] + +/-- The syntactic closure needed for a signature entry. Formation and the +value's membership are separate obligations and cannot be supplied by data. -/ +structure EntryClosed (entries : Environment β) (entry : ConstantEntry β) : Prop where + typeScope : entry.type.Scope entry.universes 0 + bodyScope : ∀ body, entry.body = some body → body.Scope entry.universes 0 + typeReferences : entry.type.ReferencesIn entries + bodyReferences : ∀ body, entry.body = some body → body.ReferencesIn entries + equationScope : ∀ law ∈ entry.equations, + law.lhs.Scope entry.universes 0 ∧ law.rhs.Scope entry.universes 0 + equationReferences : ∀ law ∈ entry.equations, + law.lhs.ReferencesIn entries ∧ law.rhs.ReferencesIn entries + factScope : ∀ fact ∈ entry.facts, fact.Scope entry.universes + factReferences : ∀ fact ∈ entry.facts, fact.ReferencesIn entries + +theorem Environment.WF.overlay {entries additions : Environment β} (hE : entries.WF) + (hN : ∀ r entry, additions r = some entry → EntryClosed entries entry) : + (entries.overlay additions).WF := by + constructor + · intro r entry hr + unfold Environment.overlay at hr + split at hr + · cases Option.some.inj hr; exact (hN _ _ ‹_›).typeScope + · exact hE.typeScope r entry hr + · intro r entry hr body hb + unfold Environment.overlay at hr + split at hr + · cases Option.some.inj hr; exact (hN _ _ ‹_›).bodyScope body hb + · exact hE.bodyScope r entry hr body hb + · intro r entry hr + unfold Environment.overlay at hr + split at hr + · cases Option.some.inj hr; exact (hN _ _ ‹_›).typeReferences.overlay + · exact (hE.typeReferences r entry hr).overlay + · intro r entry hr body hb + unfold Environment.overlay at hr + split at hr + · cases Option.some.inj hr; exact ((hN _ _ ‹_›).bodyReferences body hb).overlay + · exact (hE.bodyReferences r entry hr body hb).overlay + · intro r entry hr law hl + unfold Environment.overlay at hr + split at hr + · cases Option.some.inj hr; exact (hN _ _ ‹_›).equationScope law hl + · exact hE.equationScope r entry hr law hl + · intro r entry hr law hl + unfold Environment.overlay at hr + split at hr + · cases Option.some.inj hr + exact ⟨((hN _ _ ‹_›).equationReferences law hl).1.overlay, + ((hN _ _ ‹_›).equationReferences law hl).2.overlay⟩ + · exact ⟨(hE.equationReferences r entry hr law hl).1.overlay, + (hE.equationReferences r entry hr law hl).2.overlay⟩ + · intro r entry hr fact hf + unfold Environment.overlay at hr + split at hr + · cases Option.some.inj hr; exact (hN _ _ ‹_›).factScope fact hf + · exact hE.factScope r entry hr fact hf + · intro r entry hr fact hf + unfold Environment.overlay at hr + split at hr + · cases Option.some.inj hr; exact ((hN _ _ ‹_›).factReferences fact hf).overlay + · exact (hE.factReferences r entry hr fact hf).overlay + +variable {V : Type v} [SetTheory V] + +theorem Realizes.overlay {entries additions : Environment β} {constants : Assignment β V} + (hM : Realizes constants entries) + (hN : ∀ r entry, additions r = some entry → EntryRealization constants r entry) : + Realizes constants (entries.overlay additions) := by + constructor + · intro r entry hr + unfold Environment.overlay at hr + split at hr + · cases Option.some.inj hr; exact (hN _ _ ‹_›).typeValid + · exact hM.typeValid r entry hr + · intro r entry hr + unfold Environment.overlay at hr + split at hr + · cases Option.some.inj hr; exact (hN _ _ ‹_›).member + · exact hM.member r entry hr + · intro r entry hr + unfold Environment.overlay at hr + split at hr + · cases Option.some.inj hr; exact (hN _ _ ‹_›).bodyValid + · exact hM.bodyValid r entry hr + · intro r entry hr + unfold Environment.overlay at hr + split at hr + · cases Option.some.inj hr; exact (hN _ _ ‹_›).bodyValue + · exact hM.bodyValue r entry hr + · intro r entry hr + unfold Environment.overlay at hr + split at hr + · cases Option.some.inj hr; exact (hN _ _ ‹_›).equationValue + · exact hM.equationValue r entry hr + · intro r entry hr + unfold Environment.overlay at hr + split at hr + · cases Option.some.inj hr; exact (hN _ _ ‹_›).factMeaning + · exact hM.factMeaning r entry hr + +end Ix.Theory.Model diff --git a/Ix/Theory/Model/Support.lean b/Ix/Theory/Model/Support.lean new file mode 100644 index 000000000..f6902993d --- /dev/null +++ b/Ix/Theory/Model/Support.lean @@ -0,0 +1,126 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Environment + +namespace Ix.Theory.Model + +open SetTheory + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +namespace AExpr + +def references : AExpr β → List (ConstRef β) + | .bvar _ | .sort _ | .natLit _ => [] + | .const r _ => [r] + | .app f a | .lam _ f a | .forallE _ f a => f.references ++ a.references + | .proj r _ e => r :: e.references + +def ReferencesIn (entries : Environment β) (e : AExpr β) : Prop := + ∀ r ∈ e.references, (entries r).isSome = true + +instance {entries : Environment β} {e : AExpr β} : Decidable (e.ReferencesIn entries) := + inferInstanceAs (Decidable (∀ r ∈ e.references, (entries r).isSome = true)) + +end AExpr + +def ConstantFact.Scope (n : Nat) : ConstantFact β → Prop + | .typed e type => e.Scope n 0 ∧ type.Scope n 0 + | .natural .. => n = 0 + +def ConstantFact.references : ConstantFact β → List (ConstRef β) + | .typed e type => e.references ++ type.references + | .natural zero succ => [zero, succ] + +def ConstantFact.ReferencesIn (entries : Environment β) (fact : ConstantFact β) : Prop := + ∀ r ∈ fact.references, (entries r).isSome = true + +instance {n : Nat} {fact : ConstantFact β} : Decidable (fact.Scope n) := by + cases fact <;> unfold ConstantFact.Scope <;> infer_instance + +instance {entries : Environment β} {fact : ConstantFact β} : Decidable (fact.ReferencesIn entries) := + inferInstanceAs (Decidable (∀ r ∈ fact.references, (entries r).isSome = true)) + +theorem interp_congr_constants (e : AExpr β) (constants constants' : Assignment β V) + (h : ∀ r ∈ e.references, ∀ ls, constants r ls = constants' r ls) + (levels : List Nat) (env : Nat → V) : + interp constants levels env e = interp constants' levels env e := by + induction e generalizing env with + | const r ls => exact h r (by simp [AExpr.references]) _ + | app f a hf ha => + have hf' := hf (fun r hr => h r (List.mem_append_left _ hr)) env + have ha' := ha (fun r hr => h r (List.mem_append_right _ hr)) env + simp only [interp, hf', ha'] + | lam p A b hA hb | forallE p A b hA hb => + have hA' := hA (fun r hr => h r (List.mem_append_left _ hr)) env + simp only [interp, hA'] + congr 1 + funext x + exact hb (fun r hr => h r (List.mem_append_right _ hr)) _ + | proj r i e ih => + simp only [interp, ih (fun q hq => h q (List.mem_cons_of_mem _ hq)) env] + | _ => rfl + +theorem wellDenoted_congr_constants (e : AExpr β) (constants constants' : Assignment β V) + (h : ∀ r ∈ e.references, ∀ ls, constants r ls = constants' r ls) + (levels : List Nat) (env : Nat → V) : + WellDenoted constants levels env e ↔ WellDenoted constants' levels env e := by + induction e generalizing env with + | app f a hf ha => + have hfr := fun r hr => h r (List.mem_append_left _ hr) + have har := fun r hr => h r (List.mem_append_right _ hr) + simp only [WellDenoted, hf hfr, ha har, interp_congr_constants _ _ _ hfr, + interp_congr_constants _ _ _ har] + | lam p A b hA hb | forallE p A b hA hb => + have hAr := fun r hr => h r (List.mem_append_left _ hr) + have hbr := fun r hr => h r (List.mem_append_right _ hr) + simp only [WellDenoted, hA hAr, hb hbr, interp_congr_constants _ _ _ hAr, + interp_congr_constants _ _ _ hbr] + | proj r i e ih => exact ih (fun q hq => h q (List.mem_cons_of_mem _ hq)) env + | _ => rfl + +theorem interp_congr_env (e : AExpr β) (constants : Assignment β V) + (levels : List Nat) {n k : Nat} (hs : e.Scope n k) (env env' : Nat → V) + (h : ∀ i, i < k → env i = env' i) : + interp constants levels env e = interp constants levels env' e := by + induction e generalizing k env env' with + | bvar i => exact h i hs + | app f a hf ha => simp only [interp, hf hs.1 env env' h, ha hs.2 env env' h] + | lam p A b hA hb | forallE p A b hA hb => + simp only [interp, hA hs.2.1 env env' h] + congr 1 + funext x + apply hb hs.2.2 + intro i hi + cases i with + | zero => rfl + | succ i => exact h i (by omega) + | proj r i e ih => simp only [interp, ih hs env env' h] + | _ => rfl + +theorem interp_closed (e : AExpr β) (constants : Assignment β V) + (levels : List Nat) {n : Nat} (hs : e.Scope n 0) (env env' : Nat → V) : + interp constants levels env e = interp constants levels env' e := + interp_congr_env e constants levels hs env env' (fun _ h => (Nat.not_lt_zero _ h).elim) + +/-- Syntactic closure of an admitted interface. Semantic realizability remains +a separate, produced fact. -/ +structure Environment.WF (entries : Environment β) : Prop where + typeScope : ∀ r entry, entries r = some entry → entry.type.Scope entry.universes 0 + bodyScope : ∀ r entry, entries r = some entry → ∀ body, entry.body = some body → + body.Scope entry.universes 0 + typeReferences : ∀ r entry, entries r = some entry → entry.type.ReferencesIn entries + bodyReferences : ∀ r entry, entries r = some entry → ∀ body, entry.body = some body → + body.ReferencesIn entries + equationScope : ∀ r entry, entries r = some entry → ∀ equation ∈ entry.equations, + equation.lhs.Scope entry.universes 0 ∧ equation.rhs.Scope entry.universes 0 + equationReferences : ∀ r entry, entries r = some entry → ∀ equation ∈ entry.equations, + equation.lhs.ReferencesIn entries ∧ equation.rhs.ReferencesIn entries + factScope : ∀ r entry, entries r = some entry → ∀ fact ∈ entry.facts, fact.Scope entry.universes + factReferences : ∀ r entry, entries r = some entry → ∀ fact ∈ entry.facts, fact.ReferencesIn entries + +end Ix.Theory.Model diff --git a/Ix/Theory/Model/TelescopeSemantics.lean b/Ix/Theory/Model/TelescopeSemantics.lean new file mode 100644 index 000000000..855662026 --- /dev/null +++ b/Ix/Theory/Model/TelescopeSemantics.lean @@ -0,0 +1,81 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Instantiation + +namespace Ix.Theory.Model.Telescope + +open SetTheory SetTheory.Tower SetModel Certified + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +theorem fits_append_iff (constants : Assignment β V) (levels : List Nat) (env : Nat → V) + (left right : List (AExpr β)) (xs : List V) : + FitsS (interpret constants levels env (left ++ right)) xs ↔ + ∃ ys zs, xs = ys ++ zs ∧ FitsS (interpret constants levels env left) ys ∧ + FitsS (interpret constants levels (extend env ys) right) zs := by + induction left generalizing env xs with + | nil => + constructor + · intro h + exact ⟨[], xs, rfl, trivial, h⟩ + · rintro ⟨ys, zs, rfl, hy, hz⟩ + cases ys with + | nil => exact hz + | cons _ _ => exact hy.elim + | cons A rest ih => + constructor + · intro h + cases xs with + | nil => exact h.elim + | cons x xs => + obtain ⟨ys, zs, rfl, hy, hz⟩ := (ih (Valuation.cons x env) xs).mp h.2 + exact ⟨x :: ys, zs, rfl, ⟨h.1, hy⟩, hz⟩ + · rintro ⟨ys, zs, rfl, hy, hz⟩ + cases ys with + | nil => exact hy.elim + | cons y ys => exact ⟨hy.1, (ih (Valuation.cons y env) (ys ++ zs)).mpr ⟨ys, zs, rfl, hy.2, hz⟩⟩ + +/-- Close a pointwise semantic fact over a formed telescope. The result +includes hereditary validity of both complete endpoints and a uniform sort +for the resulting product. It does not assume a model for any new fact. -/ +theorem Formed.lamN_semantics {entries : Environment β} {Γ : Context β} + {domains : List (AExpr β)} (h : Formed.{u,v} entries Γ domains) + (constants : Assignment β V) (hM : Realizes constants entries) (levels : List Nat) + (body B : AExpr β) (b : VLevel) : + ∃ l, zeroCondition l = zeroCondition b ∧ ∀ env, Γ.Valid constants levels env → + (∀ xs, FitsS (interpret constants levels env domains) xs → + let env' := extend env xs + WellDenoted constants levels env' body ∧ WellDenoted constants levels env' B ∧ + interp constants levels env' body ∈ˢ interp constants levels env' B ∧ + interp constants levels env' B ∈ˢ (univ (b.eval levels) : V)) → + WellDenoted constants levels env (.lamN (zeroCondition b) domains body) ∧ + WellDenoted constants levels env (.forallN (zeroCondition b) domains B) ∧ + interp constants levels env (.lamN (zeroCondition b) domains body) ∈ˢ + interp constants levels env (.forallN (zeroCondition b) domains B) ∧ + interp constants levels env (.forallN (zeroCondition b) domains B) ∈ˢ + (univ (l.eval levels) : V) := by + induction h with + | nil => exact ⟨b, rfl, fun env _ hb => hb [] trivial⟩ + | @cons Γ A rest a hA hrest ih => + obtain ⟨l, hl, ih⟩ := ih + refine ⟨.imax a l, hl, ?_⟩ + intro env hΓ hb + have hAt := hA V constants hM levels env hΓ + have ht x hx := ih (Valuation.cons x env) (hΓ.push hAt.1 hx) + (fun xs hxs => hb (x :: xs) ⟨hx, hxs⟩) + have hz : regime (zeroCondition b) levels = 0 ↔ l.eval levels = 0 := by + rw [← hl] + exact regime_zeroCondition .. + refine ⟨⟨hAt.1, fun x hx => (ht x hx).1, l.eval levels, + (fun x => interp constants levels (Valuation.cons x env) (.forallN (zeroCondition b) rest B)), + hz, fun x hx => ⟨(ht x hx).2.2.1, (ht x hx).2.2.2⟩⟩, + ⟨hAt.1, fun x hx => (ht x hx).2.1, l.eval levels, hz, + fun x hx => (ht x hx).2.2.2⟩, ?_, ?_⟩ + · exact lamR_mem (fun x hx => (ht x hx).2.2.1) + · exact annotated_pi_mem_univ hl.symm hAt.2.2 (fun x hx => (ht x hx).2.2.2) + +end Ix.Theory.Model.Telescope diff --git a/Ix/Theory/Model/Value.lean b/Ix/Theory/Model/Value.lean new file mode 100644 index 000000000..b710b905f --- /dev/null +++ b/Ix/Theory/Model/Value.lean @@ -0,0 +1,90 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.SetModel.Ops +import Ix.Theory.Certified.Level + +namespace Ix.Theory.Model + +open SetTheory SetModel Certified + +/-- Only zero versus positive matters to the set-theoretic binder operators. -/ +def regime (p : PropWhen) (values : List Nat) : Nat := + if p.holds (values.getD · 0) then 0 else 1 + +@[simp] theorem regime_zeroCondition (l : VLevel) (values : List Nat) : + regime (zeroCondition l) values = 0 ↔ l.eval values = 0 := by + unfold regime + rw [zeroCondition_correct] + by_cases h : l.eval values = 0 <;> simp [h] + +@[simp] theorem regime_always (values : List Nat) : regime .always values = 0 := rfl + +@[simp] theorem regime_never (values : List Nat) : regime .never values = 1 := rfl + +@[simp] theorem regime_instCondition (p : PropWhen) (ls : List VLevel) (values : List Nat) : + regime (instCondition ls p) values = regime p (ls.map (VLevel.eval values)) := by + unfold regime + rw [instCondition_correct] + +universe u +variable {V : Type u} [SetTheory V] + +/-- The universe bound for the actual product operator, including an +arbitrary-size domain when the codomain sort is zero. -/ +theorem piR_mem_univ {a b : Nat} {A : V} {B : V → V} + (hA : A ∈ˢ univ a) (hB : ∀ x, x ∈ˢ A → B x ∈ˢ univ b) : + piR b A B ∈ˢ univ (VLevel.natIMax a b) := by + cases b with + | zero => + change piR 0 A B ∈ˢ univ 0 + rw [univ_zero] + exact piR_zero_mem_univZero + | succ b => + have hn : Nat.max a (b + 1) ≠ 0 := + Nat.ne_of_gt (Nat.lt_of_lt_of_le (Nat.zero_lt_succ b) (Nat.le_max_right a (b + 1))) + simp only [VLevel.natIMax, Nat.add_one_ne_zero, ↓reduceIte] + rw [piR_pos (Nat.succ_ne_zero b)] + apply (univ_isTGUniverse hn).piSet_mem + · exact univ_mono (Nat.le_max_left ..) A hA + · intro x hx + exact univ_mono (Nat.le_max_right ..) (B x) (hB x hx) + +theorem annotated_pi_mem_univ {a : Nat} {b : VLevel} {p : PropWhen} + {values : List Nat} {A : V} {B : V → V} + (hp : p = zeroCondition b) + (hA : A ∈ˢ univ a) (hB : ∀ x, x ∈ˢ A → B x ∈ˢ univ (b.eval values)) : + piR (regime p values) A B ∈ˢ univ (VLevel.natIMax a (b.eval values)) := by + subst p + rw [piR_zero_agree (regime_zeroCondition b values) (fun _ _ => rfl)] + exact piR_mem_univ hA hB + +/-- The fixed empty proposition used by the certified primitive signature. -/ +noncomputable def falseValue : V := empty + +theorem falseValue_uninhabited (x : V) : ¬ x ∈ˢ (falseValue : V) := not_mem_empty x + +theorem falseValue_has_sort : (falseValue : V) ∈ˢ univ 0 := empty_mem_univ 0 + +/-- Type of dependent elimination from the empty proposition. -/ +noncomputable def falseElimType (level : Nat) : V := + piR level (piR (level + 1) empty (fun _ => univ level)) fun motive => + piR level empty (fun x => app motive x) + +/-- The empty eliminator is a function graph at positive sorts and the proof +point at sort zero. Its type is inhabited because its second domain is empty. -/ +noncomputable def falseElimValue (level : Nat) : V := + lamR level (piR (level + 1) empty (fun _ => univ level)) fun _ => + lamR level empty (fun _ => empty) + +theorem falseElimValue_mem (level : Nat) : + (falseElimValue level : V) ∈ˢ falseElimType level := by + apply lamR_mem + intro motive _ + apply lamR_mem + intro x hx + exact (not_mem_empty x hx).elim + +end Ix.Theory.Model diff --git a/Ix/Theory/Model/WellDenoted.lean b/Ix/Theory/Model/WellDenoted.lean new file mode 100644 index 000000000..2cfb83cb2 --- /dev/null +++ b/Ix/Theory/Model/WellDenoted.lean @@ -0,0 +1,148 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Interpret + +/-! +# Hereditary validity of annotated expressions + +Application carries domain membership, and both binder forms carry a +codomain universe whose zero test agrees with the annotation. The invariant +is a semantic specification, not input accepted on trust: checker rules must +construct it. Projection validity follows its major premise; literal validity +is structural. Their membership still requires admitted primitive facts. +-/ + +namespace Ix.Theory.Model + +open SetTheory SetModel + +universe u v w +variable {β : Type u} {V : Type v} [SetTheory V] + +def WellDenoted (constants : Assignment β V) (levels : List Nat) + (env : Nat → V) : AExpr β → Prop + | .bvar _ | .sort _ | .const .. => True + | .app f a => + WellDenoted constants levels env f ∧ WellDenoted constants levels env a ∧ + ∃ (v : Nat) (A : V) (B : V → V), + interp constants levels env f ∈ˢ piR v A B ∧ + interp constants levels env a ∈ˢ A ∧ + ∀ x, x ∈ˢ A → B x ∈ˢ univ v + | .lam p A b => + WellDenoted constants levels env A ∧ + (∀ x, x ∈ˢ interp constants levels env A → + WellDenoted constants levels (Valuation.cons x env) b) ∧ + ∃ (v : Nat) (B : V → V), (regime p levels = 0 ↔ v = 0) ∧ + ∀ x, x ∈ˢ interp constants levels env A → + interp constants levels (Valuation.cons x env) b ∈ˢ B x ∧ + B x ∈ˢ univ v + | .forallE p A B => + WellDenoted constants levels env A ∧ + (∀ x, x ∈ˢ interp constants levels env A → + WellDenoted constants levels (Valuation.cons x env) B) ∧ + ∃ v : Nat, (regime p levels = 0 ↔ v = 0) ∧ + ∀ x, x ∈ˢ interp constants levels env A → + interp constants levels (Valuation.cons x env) B ∈ˢ univ v + | .proj _ _ e => WellDenoted constants levels env e + | .natLit _ => True + +theorem wellDenoted_liftN (e : AExpr β) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) (n k : Nat) : + WellDenoted constants levels env (e.liftN n k) ↔ + WellDenoted constants levels (Valuation.skip n k env) e := by + induction e generalizing k env with + | lam p A b hA hb | forallE p A b hA hb => + simp only [AExpr.liftN, WellDenoted, hA, hb, interp_liftN, Valuation.skip_cons] + | _ => simp_all [AExpr.liftN, WellDenoted, interp_liftN] + +/-- Substitution also needs hereditary validity of the substituted argument. +Interpretation equality alone cannot supply this fact. -/ +theorem wellDenoted_inst (e a : AExpr β) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) (k : Nat) + (ha : WellDenoted constants levels (Valuation.skip k 0 env) a) + (he : WellDenoted constants levels + (Valuation.insert k (interp constants levels (Valuation.skip k 0 env) a) env) e) : + WellDenoted constants levels env (e.inst a k) := by + induction e generalizing k env with + | bvar i => + by_cases hi : i < k + · simp [AExpr.inst, AExpr.instVar, hi, WellDenoted] + · by_cases hik : i = k + · simpa [AExpr.inst, AExpr.instVar, hi, hik, wellDenoted_liftN] using ha + · simp [AExpr.inst, AExpr.instVar, hi, hik, WellDenoted] + | app f b hf hb => + rcases he with ⟨hfw, hbw, v, A, B, hfm, hbm, hB⟩ + exact ⟨hf env k ha hfw, hb env k ha hbw, v, A, B, + by simpa only [interp_inst] using hfm, + by simpa only [interp_inst] using hbm, hB⟩ + | lam p A b hA hb => + rcases he with ⟨hAw, hbw, v, B, hz, hB⟩ + refine ⟨hA env k ha hAw, ?_, v, B, hz, ?_⟩ + · intro x hx + apply hb (Valuation.cons x env) (k + 1) + · simpa only [Valuation.skip_succ_cons] using ha + · simp only [Valuation.skip_succ_cons, Valuation.insert_cons] + exact hbw x (by simpa only [interp_inst] using hx) + · intro x hx + simpa only [interp_inst, Valuation.skip_succ_cons, Valuation.insert_cons] using + hB x (by simpa only [interp_inst] using hx) + | forallE p A b hA hb => + rcases he with ⟨hAw, hbw, v, hz, hB⟩ + refine ⟨hA env k ha hAw, ?_, v, hz, ?_⟩ + · intro x hx + apply hb (Valuation.cons x env) (k + 1) + · simpa only [Valuation.skip_succ_cons] using ha + · simp only [Valuation.skip_succ_cons, Valuation.insert_cons] + exact hbw x (by simpa only [interp_inst] using hx) + · intro x hx + simpa only [interp_inst, Valuation.skip_succ_cons, Valuation.insert_cons] using + hB x (by simpa only [interp_inst] using hx) + | proj _ _ e ih => exact ih env k ha he + | _ => exact he + +theorem wellDenoted_instL (e : AExpr β) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) (ls : List VLevel) : + WellDenoted constants levels env (e.instL ls) ↔ + WellDenoted constants (ls.map (VLevel.eval levels)) env e := by + induction e generalizing env with + | lam p A b hA hb | forallE p A b hA hb => + simp only [AExpr.instL, WellDenoted, hA, hb, interp_instL, regime_instCondition] + | _ => simp_all [AExpr.instL, WellDenoted, interp_instL] + +theorem wellDenoted_rename {γ : Type w} (e : AExpr β) (mapping : β → γ) + (constants : Assignment β V) (constants' : Assignment γ V) + (h : ∀ r ls, constants' (r.rename mapping) ls = constants r ls) + (levels : List Nat) (env : Nat → V) : + WellDenoted constants' levels env (e.rename mapping) ↔ + WellDenoted constants levels env e := by + induction e generalizing env with + | lam p A b hA hb | forallE p A b hA hb => + simp only [AExpr.rename, WellDenoted, hA, hb, interp_rename _ _ _ _ h] + | _ => simp_all [AExpr.rename, WellDenoted, interp_rename _ _ _ _ h] + +/-- Conservative beta uses the actual lambda domain even in the proof regime. +The application invariant's possibly different product domain is insufficient +for this step when the lambda's value is the proof point. -/ +theorem wellDenoted_beta {constants : Assignment β V} {levels : List Nat} + {env : Nat → V} {p : Certified.PropWhen} {A b a : AExpr β} + (hl : WellDenoted constants levels env (.lam p A b)) + (haw : WellDenoted constants levels env a) + (ha : interp constants levels env a ∈ˢ interp constants levels env A) : + WellDenoted constants levels env (b.inst a) ∧ + interp constants levels env (.app (.lam p A b) a) = + interp constants levels env (b.inst a) := by + rcases hl with ⟨_, hbw, v, B, hz, hB⟩ + constructor + · apply wellDenoted_inst + · simpa using haw + · simpa using hbw _ ha + · simp only [interp, interp_inst, Valuation.skip_zero, Valuation.insert_zero] + apply app_lamR ha (fun x hx => (hB x hx).1) + intro hp x hx + have hv := hz.mp hp + simpa only [hv, univ_zero] using (hB x hx).2 + +end Ix.Theory.Model diff --git a/Ix/Theory/NOTICE b/Ix/Theory/NOTICE new file mode 100644 index 000000000..60eaca747 --- /dev/null +++ b/Ix/Theory/NOTICE @@ -0,0 +1,51 @@ +Lean4Ix notices +================ + +Copyright (c) 2026 Argument Computer Corporation + +Lean4Ix is based on lean4lean: + + https://github.com/digama0/lean4lean + +The lean4lean-derived portions were received under the Apache License, +Version 2.0. Their original copyright, attribution, licensing, and +modification-notice requirements continue to apply. The repository history +and retained per-file notices provide the detailed provenance of those +portions. + +The executable kernel model is itself derived from the Lean 4 kernel. Certain +files also identify more specific Apache-2.0 sources in their headers: + + * Main.lean contains code copied from leanprover-community/import-graph. + * Lean4Lean/Std/ToExpr.lean contains code from mathlib. + * Lean4Lean/Verify/QSort.lean is adapted from Lean 4's qsort verification. + +Lean 4, Batteries, import-graph, and mathlib are distributed under the Apache +License, Version 2.0. When a Lean4Ix distribution includes their code or +artifacts, preserve the license and notice material shipped with them. + +Lean4Ix/Model/SetTheory and Lean4Ix/Model/SetModel contain a port of the +con-leche set-theoretic foundation, dependent-function operators, least +fixed points, tuple and container constructions, and recursor graphs, +received under the Apache License, Version 2.0: + + https://github.com/leanprover/con-leche + revision 86cd20a65660d757cedc81561a44579099b565d0 + +The port retains the upstream license in LICENSE-APACHE. Per-file headers +identify the original files and modifications. The exact source and ported +file hashes are recorded in docs/consistency/con-leche-port.json. This upstream +code remains subject to Apache-2.0; the repository's dual-license default does +not relicense it. + +The generated lake-manifest.json dependency lock and the single-value +lean-toolchain toolchain pin may differ from their lean4lean counterparts. +They are mechanical package metadata governed by the licensing and +modification notices distributed with this repository. + +Except where a contribution or upstream component is explicitly identified +under different terms, contributions intentionally submitted for inclusion in +Lean4Ix are accepted under the same MIT OR Apache-2.0 choice described in +LICENSE, LICENSE-MIT, and LICENSE-APACHE. + +This NOTICE is informational and does not modify any applicable license. diff --git a/Ix/Theory/Named/ConstructorValidityFixtures.lean b/Ix/Theory/Named/ConstructorValidityFixtures.lean new file mode 100644 index 000000000..7d5348b7b --- /dev/null +++ b/Ix/Theory/Named/ConstructorValidityFixtures.lean @@ -0,0 +1,176 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.InductiveFixtures + +open Ix.Theory (VLevel) + +/-! +# Constructor-validity parity fixtures + +Focused singleton declarations for Spec-05. The positive declarations retain +real kernel metadata while remaining inside the already-supported large- +elimination and non-K fragment. The failed declarations pin the nearest +Lean elaborator/kernel rejection for each neighboring positivity or universe +branch; `#guard_msgs` rolls every failed declaration back. +-/ + +namespace Ix.Theory.Named +namespace InductiveFixtures +open VInductDecl + +universe u + +/-! ## Type-valued dependency and positivity matrix -/ + +/-- A source-ordered constructor covering the non-Prop universe boundary, +dependent proof fields, direct recursion, a recursive function, and an +independent/dependent suffix after both recursive outer fields. -/ +inductive ConstructorValidityMatrix (α : Type u) (P : α → Prop) : Type u where + | mk (x : α) (proof : P x) + (direct : ConstructorValidityMatrix α P) + (function : (y : α) → ConstructorValidityMatrix α P) + (later : α) (laterProof : P later) : + ConstructorValidityMatrix α P + +def constructorValidityMatrixType : VInductiveType where + name := ``ConstructorValidityMatrix + uvars := 1 + type := vconst(type_of% @ConstructorValidityMatrix).type + ctors := [⟨vconst(type_of% @ConstructorValidityMatrix.mk), + ``ConstructorValidityMatrix.mk⟩] + +def constructorValidityMatrixDecl : VInductDecl := + ⟨1, 2, [constructorValidityMatrixType]⟩ + +example : constructorValidityMatrixDecl.stage3 = true := rfl + +def constructorValidityMatrixChecked : constructorValidityMatrixDecl.Checked := + constructorValidityMatrixDecl.checked?.get (by decide) + +def constructorValidityMatrixGenerationChecked : + GenerationChecked constructorValidityMatrixDecl := + (identityGeneration? constructorValidityMatrixDecl).get (by decide) + +private def spec05PermC (constant : VConstant) + (levels : List VLevel) : VConstant := + ⟨constant.uvars, constant.type.instL levels⟩ + +example : constructorValidityMatrixGenerationChecked.recursor = + spec05PermC (vconst(type_of% @ConstructorValidityMatrix.rec)) + [.param 1, .param 0] := rfl + +example : constructorValidityMatrixChecked.resultLevel = + .succ (.param 0) := rfl + +example : constructorValidityMatrixChecked.constructors[0].fields.length = 6 := + rfl + +example : constructorValidityMatrixChecked.constructors[0].recursive.map + (fun position => (position.fieldIndex, position.binders.length)) = + [(2, 0), (3, 1)] := rfl + +example : + constructorValidityMatrixGenerationChecked.generatedRules.length = 1 := + rfl + +/-! ## Prop-valued recursive-function universe boundary -/ + +/-- The ordinary field `a : α` reaches the impredicative-Prop constructor +universe branch. The varying recursive target prevents parameter promotion, +and the single constructor remains a legitimate non-K large eliminator. -/ +inductive PropRecursiveBoundary (α : Type u) : α → Prop where + | mk (a : α) (next : (b : α) → PropRecursiveBoundary α b) : + PropRecursiveBoundary α a + +def propRecursiveBoundaryType : VInductiveType where + name := ``PropRecursiveBoundary + uvars := 1 + type := vconst(type_of% @PropRecursiveBoundary).type + ctors := [⟨vconst(type_of% @PropRecursiveBoundary.mk), + ``PropRecursiveBoundary.mk⟩] + +def propRecursiveBoundaryDecl : VInductDecl := + ⟨1, 1, [propRecursiveBoundaryType]⟩ + +example : propRecursiveBoundaryDecl.stage3 = true := rfl + +def propRecursiveBoundaryChecked : propRecursiveBoundaryDecl.Checked := + propRecursiveBoundaryDecl.checked?.get (by decide) + +def propRecursiveBoundaryGenerationChecked : + GenerationChecked propRecursiveBoundaryDecl := + (identityGeneration? propRecursiveBoundaryDecl).get (by decide) + +example : propRecursiveBoundaryGenerationChecked.recursor = + spec05PermC (vconst(type_of% @PropRecursiveBoundary.rec)) + [.param 1, .param 0] := rfl + +example : propRecursiveBoundaryChecked.resultLevel = .zero := rfl + +example : propRecursiveBoundaryChecked.constructors[0].fields.length = 2 := rfl + +example : propRecursiveBoundaryChecked.constructors[0].recursive.map + (fun position => (position.fieldIndex, position.binders.length)) = + [(1, 1)] := rfl + +example : propRecursiveBoundaryGenerationChecked.generatedRules.length = 1 := + rfl + +/-! ## Nearest-kernel rejection matrix -/ + +namespace KernelDifferential + +opaque Spec05TypeBox : Type → Type +opaque Spec05ProofBox : Type → Prop +opaque Spec05DepProofBox (α : Type) : α → Prop + +/-- +error: (kernel) arg #1 of 'Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05NestedNegative.mk' has a non positive occurrence of the datatypes being declared +-/ +#guard_msgs (whitespace := lax) in +inductive Spec05NestedNegative : Type where + | mk : ((Spec05NestedNegative → Prop) → Spec05NestedNegative) → + Spec05NestedNegative + +/-- +error: (kernel) arg #1 of 'Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05FamilyNonrecursive.mk' contains a non valid occurrence of the datatypes being declared +-/ +#guard_msgs (whitespace := lax) in +inductive Spec05FamilyNonrecursive : Type where + | mk : Spec05TypeBox Spec05FamilyNonrecursive → Spec05FamilyNonrecursive + +/-- +error: (kernel) arg #1 of 'Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05FamilyProof.mk' contains a non valid occurrence of the datatypes being declared +-/ +#guard_msgs (whitespace := lax) in +inductive Spec05FamilyProof : Type where + | mk : Spec05ProofBox Spec05FamilyProof → Spec05FamilyProof + +/-- +error: (kernel) arg #2 of 'Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05RecursiveDependency.mk' contains a non valid occurrence of the datatypes being declared +-/ +#guard_msgs (whitespace := lax) in +inductive Spec05RecursiveDependency : Type where + | mk (recursive : Spec05RecursiveDependency) + (proof : Spec05DepProofBox Spec05RecursiveDependency recursive) : + Spec05RecursiveDependency + +/-- +error: Invalid universe level in constructor `Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05UniverseReject.mk`: Parameter `α` has type + Type +at universe level + 2 +which is not less than or equal to the inductive type's resulting universe level + 1 +-/ +#guard_msgs (whitespace := lax) in +inductive Spec05UniverseReject : Type where + | mk (α : Type) : Spec05UniverseReject + +end KernelDifferential +end InductiveFixtures +end Ix.Theory.Named diff --git a/Ix/Theory/Named/Fixtures/ProjectionExpressibility.lean b/Ix/Theory/Named/Fixtures/ProjectionExpressibility.lean new file mode 100644 index 000000000..e5c89c2ca --- /dev/null +++ b/Ix/Theory/Named/Fixtures/ProjectionExpressibility.lean @@ -0,0 +1,1307 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Meta +import Ix.Theory.Named.Projection +import Ix.Theory.Named.Typing.InductiveLemmas + +open Ix.Theory (VLevel) + +/-! +# Projection expressibility fixtures + +The main fixture is simultaneously parameterized, universe-polymorphic, and +dependent: the final field type mentions the preceding projection. It is +small enough that the complete recursor encoding remains definitionally +inspectable. +-/ + +namespace Ix.Theory.Named.Tests.ProjectionExpressibility + +open Ix.Theory.Named VInductDecl + +universe u v + +structure DependentRecord (α : Type u) (family : α → Type v) where + key : α + value : family key + +def dependentRecordCtor : VConstVal := + ⟨vconst(type_of% @DependentRecord.mk), ``DependentRecord.mk⟩ + +def dependentRecordType : VInductiveType where + name := ``DependentRecord + uvars := 2 + type := vconst(type_of% @DependentRecord).type + ctors := [dependentRecordCtor] + +def dependentRecordDecl : VInductDecl := + ⟨2, 2, [dependentRecordType]⟩ + +example : dependentRecordDecl.checked?.isSome = true := rfl + +def dependentRecordChecked : dependentRecordDecl.Checked := + dependentRecordDecl.checked?.get (by decide) + +def dependentRecordGeneration : dependentRecordDecl.GenerationChecked := + dependentRecordChecked.identityGeneration + +def dependentRecordView : VStructureView where + source := dependentRecordDecl + generation := dependentRecordGeneration + constructor := dependentRecordGeneration.block.ctorPairs[0] + constructor_eq := rfl + raw_indices_eq := rfl + checked_indices_eq := rfl + recursive_eq := rfl + fieldSorts := [.succ (.param 0), .succ (.param 1)] + fieldSorts_length := rfl + +def dependentRecordEnv : VEnv := + (VEnv.empty.addInductGeneration dependentRecordGeneration).get (by decide) + +theorem dependentRecord_add : + VEnv.empty.addInductGeneration dependentRecordGeneration = + some dependentRecordEnv := rfl + +theorem dependentRecord_trace : + Nonempty (VEnv.AddInductGenerationTrace VEnv.empty + dependentRecordEnv dependentRecordGeneration) := + VEnv.addInductGeneration_trace dependentRecord_add + +theorem dependentRecordDecl_wf : + dependentRecordDecl.WF VEnv.empty := by + refine ⟨rfl, ?_⟩ + intro ty hty + have hty' : ty = dependentRecordType := + List.mem_singleton.1 (by simpa [dependentRecordDecl] using hty) + subst ty + refine ⟨?_, ?_⟩ + · exact ⟨⟨_, by type_tac⟩, ⟨⟨_, by type_tac⟩, trivial⟩⟩ + · intro c hc + have hc' : c = dependentRecordCtor := by + simpa [dependentRecordType] using hc + subst c + constructor + · simp [dependentRecordDecl, dependentRecordType, + dependentRecordCtor, VInductDecl.fieldsWF, + VInductDecl.ctorFields, VInductDecl.isRecField, + VInductDecl.recArg?, VInductDecl.recTarget?, + VInductDecl.recFieldIdxs, VInductDecl.sortLevel, + VExpr.dropN, VExpr.resultOf, VExpr.appHead, + VExpr.appArgs] + exact ⟨ + ⟨VLevel.succ (.param 0), by type_tac, VLevel.le_max_left⟩, + ⟨VLevel.succ (.param 1), by type_tac, VLevel.le_max_right⟩⟩ + · simp [dependentRecordDecl, dependentRecordType, + dependentRecordCtor, VInductDecl.ctorFields, + VInductDecl.recFieldIdxs, VInductDecl.sortLevel, + VExpr.dropN, VExpr.resultOf, VExpr.forallN, + VExpr.liftTelN, VExpr.appArgs] + exact .nil + +theorem dependentRecordGeneration_wf : + dependentRecordGeneration.WF VEnv.empty := + (dependentRecordChecked.wf_of_decl + dependentRecordDecl_wf).identityGeneration .empty + +theorem dependentRecordEnv_ordered : dependentRecordEnv.Ordered := + VEnv.addInductGeneration_WF .empty dependentRecordGeneration_wf + dependentRecord_add + +theorem dependentRecordEnv_wf : dependentRecordEnv.WF := + ⟨[.induct dependentRecordDecl], + .decl (.induct dependentRecordGeneration_wf dependentRecord_add) .empty⟩ + +theorem dependentRecord_generation_semantics : + dependentRecordView.GenerationSemantics dependentRecordEnv := by + rcases dependentRecord_trace with ⟨trace⟩ + exact .ofGenerationTrace dependentRecordGeneration_wf trace + +theorem dependentRecord_registered : + dependentRecordView.Registered dependentRecordEnv := by + rcases dependentRecord_trace with ⟨trace⟩ + refine { + family := trace.family_lookup + constructor := ?_ + recursor := trace.rec_lookup + rules := fun _ h => trace.rule_mem h } + apply trace.ctor_lookup + rw [← dependentRecordGeneration.rawCtors_eq] + exact List.mem_map.2 ⟨dependentRecordView.constructor, + by + change dependentRecordView.constructor ∈ + dependentRecordView.generation.block.ctorPairs + rw [dependentRecordView.constructor_eq] + simp, + rfl⟩ + +theorem dependentRecord_view_wf : + dependentRecordView.WF dependentRecordEnv := by + refine { + toRegistered := dependentRecord_registered + generationSemantics := dependentRecord_generation_semantics + parameters := ?_ + parameters_length := rfl + fieldTelescope := ?_ + smallFields := ?_ } + · exact ⟨⟨_, by type_tac⟩, ⟨⟨_, by type_tac⟩, trivial⟩⟩ + · exact .cons (by type_tac) (.cons (by type_tac) .nil) + · intro h + change VInductDecl.ElimMode.large = .small at h + contradiction + +/-- The checked artifact retains both parameters and exactly the two +dependent fields from the real kernel declaration. -/ +example : dependentRecordGeneration.block.rawParams = + [.sort (.succ (.param 0)), + .forallE (.bvar 0) (.sort (.succ (.param 1)))] := rfl + +example : dependentRecordView.fields = + [.bvar 1, .app (.bvar 1) (.bvar 0)] := rfl + +example : dependentRecordGeneration.elimination = .large := rfl + +private def permC (ci : VConstant) (levels : List VLevel) : VConstant := + ⟨ci.uvars, ci.type.instL levels⟩ + +example : dependentRecordGeneration.recursor = + permC (vconst(type_of% @DependentRecord.rec)) + [.param 1, .param 2, .param 0] := rfl + +def symbolicLevels : List VLevel := [.param 0, .param 1] + +/-- Parameters in the context `[family, α]`, outermost first. -/ +def symbolicParams : List VExpr := [.bvar 1, .bvar 0] + +def symbolicStructureType : VExpr := + dependentRecordView.structureType symbolicLevels symbolicParams + +example : dependentRecordView.specializedFields symbolicLevels symbolicParams = + [.bvar 1, .app (.bvar 1) (.bvar 0)] := rfl + +def keyCode : VStructureView.ProjectionCode := + (dependentRecordView.projectionCodes symbolicLevels symbolicParams)[0] + +def valueCode : VStructureView.ProjectionCode := + (dependentRecordView.projectionCodes symbolicLevels symbolicParams)[1] + +/-- Constructor reduction selects the first field for `key`. -/ +example : keyCode.minor = + .lam (.bvar 1) + (.lam (.app (.bvar 1) (.bvar 0)) (.bvar 1)) := rfl + +/-- Constructor reduction selects the second field for `value`. -/ +example : valueCode.minor = + .lam (.bvar 1) + (.lam (.app (.bvar 1) (.bvar 0)) (.bvar 0)) := rfl + +/-- The first field type is `α`. -/ +example : keyCode.typeFn = + .lam symbolicStructureType (.bvar 2) := rfl + +/-- The dependent second field type is `family (key major)`: the earlier +projection program occurs in the later motive, rather than being supplied by +an unconstrained witness. -/ +example : valueCode.typeFn = + .lam symbolicStructureType + (.app (.bvar 1) (.app keyCode.projector.lift (.bvar 0))) := rfl + +example : dependentRecordView.projectionLevels keyCode.fieldSort symbolicLevels = + [.succ (.param 0), .param 0, .param 1] := rfl + +example : dependentRecordView.projectionLevels valueCode.fieldSort symbolicLevels = + [.succ (.param 1), .param 0, .param 1] := rfl + +example : dependentRecordView.project? symbolicLevels symbolicParams 2 (.bvar 0) = + none := rfl + +/-- Eta reconstruction uses every generated projector in constructor-field +order, including the projector whose motive depends on the earlier field. -/ +example : dependentRecordView.etaRebuild symbolicLevels symbolicParams + (.bvar 0) = + VExpr.appN (.const ``DependentRecord.mk symbolicLevels) + (symbolicParams ++ + [.app keyCode.projector (.bvar 0), + .app valueCode.projector (.bvar 0)]) := rfl + +/-! A fully constrained `VEnv.TrProj` witness in a universe-polymorphic +local context. -/ + +def symbolicAlphaType : VExpr := .sort (.succ (.param 0)) + +def symbolicFamilyType : VExpr := + .forallE (.bvar 0) (.sort (.succ (.param 1))) + +/-- The major binder type is written over `[family, α]`. -/ +def symbolicMajorBinderType : VExpr := + dependentRecordView.structureType symbolicLevels [.bvar 1, .bvar 0] + +def symbolicContext : List VExpr := + [symbolicMajorBinderType, symbolicFamilyType, symbolicAlphaType] + +/-- The same parameters as seen under the major binder. -/ +def symbolicMajorParams : List VExpr := [.bvar 2, .bvar 1] + +def symbolicMajor : VExpr := .bvar 0 + +theorem symbolicLevels_wf : + ∀ level ∈ symbolicLevels, level.WF 2 := by + simp [symbolicLevels, VLevel.WF] + +theorem symbolicParams_spine : + ∃ resultLevel, dependentRecordEnv.SpineWF 2 symbolicContext + (dependentRecordView.familyType.instL symbolicLevels) + symbolicMajorParams (.sort resultLevel) := by + refine ⟨.max (.succ (.param 0)) (.succ (.param 1)), + .cons (by type_tac) ?_⟩ + exact .cons (by type_tac) .nil + +theorem symbolicMajor_hasType : + dependentRecordEnv.HasType 2 symbolicContext symbolicMajor + (dependentRecordView.structureType symbolicLevels symbolicMajorParams) := by + exact .bvar .zero + +def symbolicKeyCode : VStructureView.ProjectionCode := + (dependentRecordView.projectionCodes symbolicLevels symbolicMajorParams)[0] + +def symbolicValueCode : VStructureView.ProjectionCode := + (dependentRecordView.projectionCodes symbolicLevels symbolicMajorParams)[1] + +def symbolicFieldContext : List VExpr := + [.app (.bvar 3) (.bvar 0), .bvar 3, + dependentRecordView.structureType symbolicLevels symbolicMajorParams] ++ + symbolicContext + +def symbolicConstructorApp : VExpr := + dependentRecordView.projectionConstructorApp symbolicLevels + (symbolicMajorParams.map (VExpr.liftN 1)) + [.bvar 3, .app (.bvar 3) (.bvar 0)] + +def symbolicInnerStructureType : VExpr := + dependentRecordView.structureType symbolicLevels [.bvar 5, .bvar 4] + +theorem symbolicConstructor_hasType : + dependentRecordEnv.HasType 2 symbolicFieldContext symbolicConstructorApp + symbolicInnerStructureType := by + have hc := VEnv.HasType.const + (Γ := symbolicFieldContext) dependentRecord_view_wf.constructor + symbolicLevels_wf (by rfl) + have hα : dependentRecordEnv.HasType 2 symbolicFieldContext + (.bvar 5) (.sort (.succ (.param 0))) := by + type_tac + have hFamily : dependentRecordEnv.HasType 2 symbolicFieldContext + (.bvar 4) (.forallE (.bvar 5) (.sort (.succ (.param 1)))) := by + type_tac + have hKey : dependentRecordEnv.HasType 2 symbolicFieldContext + (.bvar 1) (.bvar 5) := by + type_tac + have hValue : dependentRecordEnv.HasType 2 symbolicFieldContext + (.bvar 0) (.app (.bvar 4) (.bvar 1)) := by + type_tac + have hcα := hc.app hα + have hcFamily := hcα.app hFamily + have hcKey := hcFamily.app hKey + have hcValue := hcKey.app hValue + change dependentRecordEnv.HasType 2 symbolicFieldContext + symbolicConstructorApp symbolicInnerStructureType at hcValue + exact hcValue + +private def takeLamDomains : Nat → VExpr → List VExpr + | 0, _ => [] + | n + 1, .lam A body => A :: takeLamDomains n body + | _ + 1, _ => [] + +private def dropLamBody : Nat → VExpr → VExpr + | 0, e => e + | n + 1, .lam _ body => dropLamBody n body + | _ + 1, e => e + +private def dropForallBody : Nat → VExpr → VExpr + | 0, e => e + | n + 1, .forallE _ body => dropForallBody n body + | _ + 1, e => e + +theorem symbolicStructure_isType : dependentRecordEnv.IsType 2 symbolicContext + (dependentRecordView.structureType symbolicLevels symbolicMajorParams) := by + obtain ⟨resultLevel, hspine⟩ := symbolicParams_spine + have hfamily := VEnv.HasType.const + (Γ := symbolicContext) dependentRecord_view_wf.family + symbolicLevels_wf (by rfl) + exact ⟨resultLevel, by + simpa [VStructureView.structureType] using hspine.hasType_appN hfamily⟩ + +theorem symbolicMajorBinder_isType : dependentRecordEnv.IsType 2 + [symbolicFamilyType, symbolicAlphaType] symbolicMajorBinderType := by + let resultLevel := VLevel.max (.succ (.param 0)) (.succ (.param 1)) + have hspine : dependentRecordEnv.SpineWF 2 + [symbolicFamilyType, symbolicAlphaType] + (dependentRecordView.familyType.instL symbolicLevels) + [.bvar 1, .bvar 0] (.sort resultLevel) := by + refine .cons (by type_tac) ?_ + exact .cons (by type_tac) .nil + have hfamily := VEnv.HasType.const + (Γ := [symbolicFamilyType, symbolicAlphaType]) + dependentRecord_view_wf.family symbolicLevels_wf (by rfl) + exact ⟨resultLevel, by + simpa [symbolicMajorBinderType, VStructureView.structureType] using + hspine.hasType_appN hfamily⟩ + +theorem symbolicFieldContext_wf : + OnCtx symbolicFieldContext (dependentRecordEnv.IsType 2) := by + refine ⟨?_, ⟨_, by type_tac⟩⟩ + refine ⟨?_, ⟨_, by type_tac⟩⟩ + refine ⟨?_, symbolicStructure_isType⟩ + refine ⟨?_, symbolicMajorBinder_isType⟩ + refine ⟨?_, ⟨_, by type_tac⟩⟩ + exact ⟨trivial, ⟨_, by type_tac⟩⟩ + +def symbolicKeyMotive : VExpr := symbolicKeyCode.typeFn.liftN 3 + +def symbolicKeyMinor : VExpr := symbolicKeyCode.minor.liftN 3 + +def symbolicKeyRuleLevels : List VLevel := + dependentRecordView.projectionLevels symbolicKeyCode.fieldSort symbolicLevels + +def symbolicKeyRule : VDefEq := dependentRecordGeneration.generatedRules[0] + +def symbolicKeyRuleType : VExpr := + .forallE (.sort (.succ (.param 0))) + (.forallE (.forallE (.bvar 0) (.sort (.succ (.param 1)))) + (.forallE + (.forallE + (dependentRecordView.structureType symbolicLevels [.bvar 1, .bvar 0]) + (.sort (.succ (.param 0)))) + (.forallE + (.forallE (.bvar 2) + (.forallE (.app (.bvar 2) (.bvar 0)) + (.app (.bvar 2) + (.app + (.app + (.app + (.app (.const ``DependentRecord.mk symbolicLevels) + (.bvar 4)) + (.bvar 3)) + (.bvar 1)) + (.bvar 0))))) + (.forallE (.bvar 3) + (.forallE (.app (.bvar 3) (.bvar 0)) + (.app (.bvar 3) + (.app + (.app + (.app + (.app (.const ``DependentRecord.mk symbolicLevels) + (.bvar 5)) + (.bvar 4)) + (.bvar 1)) + (.bvar 0)))))))) + +theorem symbolicKeyRuleType_eq : + symbolicKeyRule.type.instL symbolicKeyRuleLevels = + symbolicKeyRuleType := rfl + +def symbolicKeyRuleArgs : List VExpr := + [.bvar 5, .bvar 4, symbolicKeyMotive, symbolicKeyMinor, + .bvar 1, .bvar 0] + +def symbolicKeyRuleResult : VExpr := + VExpr.instRev (dropForallBody 6 symbolicKeyRuleType) symbolicKeyRuleArgs + +theorem symbolicKeyRule_spine : + dependentRecordEnv.SpineWF 2 symbolicFieldContext + (symbolicKeyRule.type.instL symbolicKeyRuleLevels) + symbolicKeyRuleArgs symbolicKeyRuleResult := by + rw [symbolicKeyRuleType_eq] + unfold symbolicKeyRuleArgs symbolicKeyRuleResult + refine .cons (by type_tac) ?_ + refine .cons (by type_tac) ?_ + refine .cons ?_ ?_ + · have hMotiveShape : symbolicKeyMotive = + .lam + ((dependentRecordView.structureType symbolicLevels + symbolicMajorParams).liftN 3) + (.bvar 6) := rfl + rw [hMotiveShape] + obtain ⟨structureLevel, hstructure⟩ := + symbolicStructure_isType.weakN dependentRecordEnv_ordered + (Ctx.LiftN.zero + [.app (.bvar 3) (.bvar 0), .bvar 3, + dependentRecordView.structureType symbolicLevels + symbolicMajorParams]) + exact VEnv.HasType.lam (u := structureLevel) hstructure (by type_tac) + · refine .cons ?_ ?_ + · change dependentRecordEnv.HasType 2 symbolicFieldContext + symbolicKeyMinor + (.forallE (.bvar 5) + (.forallE (.app (.bvar 5) (.bvar 0)) + (.app (symbolicKeyMotive.liftN 2) + (.app + (.app + (.app + (.app (.const ``DependentRecord.mk symbolicLevels) + (.bvar 7)) + (.bvar 6)) + (.bvar 1)) + (.bvar 0))))) + have hMinorShape : symbolicKeyMinor = + .lam (.bvar 5) + (.lam (.app (.bvar 5) (.bvar 0)) (.bvar 1)) := rfl + rw [hMinorShape] + refine .lam (by type_tac) ?_ + refine .lam (by type_tac) ?_ + have hMotiveLiftShape : symbolicKeyMotive.liftN 2 = + .lam + (dependentRecordView.structureType symbolicLevels + [.bvar 7, .bvar 6]) + (.bvar 8) := rfl + rw [hMotiveLiftShape] + let innerCtor : VExpr := + .app + (.app + (.app + (.app (.const ``DependentRecord.mk symbolicLevels) (.bvar 7)) + (.bvar 6)) + (.bvar 1)) + (.bvar 0) + let innerStructure : VExpr := + dependentRecordView.structureType symbolicLevels [.bvar 7, .bvar 6] + have hkey : dependentRecordEnv.HasType 2 + ((.app (.bvar 5) (.bvar 0)) :: .bvar 5 :: symbolicFieldContext) + (.bvar 1) (.bvar 7) := by + type_tac + have hctor : dependentRecordEnv.HasType 2 + ((.app (.bvar 5) (.bvar 0)) :: .bvar 5 :: symbolicFieldContext) + innerCtor innerStructure := by + have hc := VEnv.HasType.const + (Γ := ((.app (.bvar 5) (.bvar 0)) :: .bvar 5 :: + symbolicFieldContext)) + dependentRecord_view_wf.constructor symbolicLevels_wf (by rfl) + have hα : dependentRecordEnv.HasType 2 + ((.app (.bvar 5) (.bvar 0)) :: .bvar 5 :: symbolicFieldContext) + (.bvar 7) (.sort (.succ (.param 0))) := by + type_tac + have hFamily : dependentRecordEnv.HasType 2 + ((.app (.bvar 5) (.bvar 0)) :: .bvar 5 :: symbolicFieldContext) + (.bvar 6) + (.forallE (.bvar 7) (.sort (.succ (.param 1)))) := by + type_tac + have hValue : dependentRecordEnv.HasType 2 + ((.app (.bvar 5) (.bvar 0)) :: .bvar 5 :: symbolicFieldContext) + (.bvar 0) (.app (.bvar 6) (.bvar 1)) := by + type_tac + have hcValue := (((hc.app hα).app hFamily).app hkey).app hValue + change dependentRecordEnv.HasType 2 + ((.app (.bvar 5) (.bvar 0)) :: .bvar 5 :: symbolicFieldContext) + innerCtor innerStructure at hcValue + exact hcValue + have hbody : dependentRecordEnv.HasType 2 + (innerStructure :: (.app (.bvar 5) (.bvar 0)) :: .bvar 5 :: + symbolicFieldContext) + (.bvar 8) (.sort (.succ (.param 0))) := by + dsimp [innerStructure] + type_tac + have hbetaRaw := VEnv.IsDefEq.beta hbody hctor + have hbeta : dependentRecordEnv.IsDefEq 2 + ((.app (.bvar 5) (.bvar 0)) :: .bvar 5 :: symbolicFieldContext) + (.app (.lam innerStructure (.bvar 8)) innerCtor) + (.bvar 7) (.sort (.succ (.param 0))) := by + simpa [innerCtor, innerStructure, VExpr.inst, VExpr.instVar] using hbetaRaw + exact hbeta.symm.defeq hkey + · refine .cons (by type_tac) ?_ + exact .cons (by type_tac) .nil + +def symbolicKeyRuleBinders : List VExpr := + takeLamDomains 6 (symbolicKeyRule.lhs.instL symbolicKeyRuleLevels) + +def symbolicKeyRuleLhsBody : VExpr := + dropLamBody 6 (symbolicKeyRule.lhs.instL symbolicKeyRuleLevels) + +def symbolicKeyRuleRhsBody : VExpr := + dropLamBody 6 (symbolicKeyRule.rhs.instL symbolicKeyRuleLevels) + +def symbolicKeyRuleTypeBody : VExpr := + dropForallBody 6 (symbolicKeyRule.type.instL symbolicKeyRuleLevels) + +theorem symbolicKeyRule_lhs_shape : + symbolicKeyRule.lhs.instL symbolicKeyRuleLevels = + VExpr.lamN symbolicKeyRuleBinders symbolicKeyRuleLhsBody := rfl + +theorem symbolicKeyRule_rhs_shape : + symbolicKeyRule.rhs.instL symbolicKeyRuleLevels = + VExpr.lamN symbolicKeyRuleBinders symbolicKeyRuleRhsBody := rfl + +theorem symbolicKeyRule_type_shape : + symbolicKeyRule.type.instL symbolicKeyRuleLevels = + VExpr.forallN symbolicKeyRuleBinders symbolicKeyRuleTypeBody := rfl + +theorem symbolicKeyRuleBinders_length : symbolicKeyRuleBinders.length = 6 := rfl + +theorem symbolicKeyRuleArgs_length : symbolicKeyRuleArgs.length = 6 := rfl + +theorem symbolicKeyRule_registered : dependentRecordEnv.defeqs symbolicKeyRule := by + apply dependentRecord_view_wf.rules + decide + +theorem symbolicKeyRule_levels_wf : + ∀ level ∈ symbolicKeyRuleLevels, level.WF 2 := by + decide + +theorem symbolicKeyRule_levels_length : + symbolicKeyRuleLevels.length = symbolicKeyRule.uvars := by + decide + +theorem symbolicKeyRule_reduces : dependentRecordEnv.IsDefEqU 2 + symbolicFieldContext + (VExpr.instRev symbolicKeyRuleLhsBody symbolicKeyRuleArgs) + (VExpr.instRev symbolicKeyRuleRhsBody symbolicKeyRuleArgs) := by + have hextra : dependentRecordEnv.IsDefEq 2 symbolicFieldContext + (symbolicKeyRule.lhs.instL symbolicKeyRuleLevels) + (symbolicKeyRule.rhs.instL symbolicKeyRuleLevels) + (symbolicKeyRule.type.instL symbolicKeyRuleLevels) := + .extra symbolicKeyRule_registered symbolicKeyRule_levels_wf + symbolicKeyRule_levels_length + have happlied := hextra.appN_congr symbolicKeyRule_spine + have hlhsType := hextra.hasType.1 + rw [symbolicKeyRule_lhs_shape] at hlhsType + obtain ⟨hlhsTel, lhsType, hlhsBody⟩ := VEnv.HasType.lamN_wf + dependentRecordEnv_ordered symbolicFieldContext_wf hlhsType + have hlhsSpine := symbolicKeyRule_spine + rw [symbolicKeyRule_type_shape] at hlhsSpine + have hlhsRetarget := hlhsSpine.retarget + (symbolicKeyRuleArgs_length.trans symbolicKeyRuleBinders_length.symm) + lhsType + have hcollapseL := VEnv.IsDefEq.appN_lamN dependentRecordEnv_ordered + hlhsTel hlhsBody hlhsRetarget + (symbolicKeyRuleArgs_length.trans symbolicKeyRuleBinders_length.symm) + have hrhsType := hextra.hasType.2 + rw [symbolicKeyRule_rhs_shape] at hrhsType + obtain ⟨hrhsTel, rhsType, hrhsBody⟩ := VEnv.HasType.lamN_wf + dependentRecordEnv_ordered symbolicFieldContext_wf hrhsType + have hrhsSpine := symbolicKeyRule_spine + rw [symbolicKeyRule_type_shape] at hrhsSpine + have hrhsRetarget := hrhsSpine.retarget + (symbolicKeyRuleArgs_length.trans symbolicKeyRuleBinders_length.symm) + rhsType + have hcollapseR := VEnv.IsDefEq.appN_lamN dependentRecordEnv_ordered + hrhsTel hrhsBody hrhsRetarget + (symbolicKeyRuleArgs_length.trans symbolicKeyRuleBinders_length.symm) + rw [symbolicKeyRule_lhs_shape, symbolicKeyRule_rhs_shape] at happlied + exact VEnv.IsDefEqU.trans dependentRecordEnv_wf symbolicFieldContext_wf + ⟨_, hcollapseL.symm⟩ + (VEnv.IsDefEqU.trans dependentRecordEnv_wf symbolicFieldContext_wf + ⟨_, happlied⟩ ⟨_, hcollapseR⟩) + +theorem symbolicKeyProjector_hasType : + dependentRecordEnv.HasType 2 symbolicContext symbolicKeyCode.projector + (.forallE + (dependentRecordView.structureType symbolicLevels symbolicMajorParams) + (.app symbolicKeyCode.typeFn.lift (.bvar 0))) := by + obtain ⟨resultLevel, hspine⟩ := symbolicParams_spine + have hfamily := VEnv.HasType.const + (Γ := symbolicContext) dependentRecord_view_wf.family + symbolicLevels_wf (by rfl) + have hstructure : dependentRecordEnv.HasType 2 symbolicContext + (dependentRecordView.structureType symbolicLevels symbolicMajorParams) + (.sort resultLevel) := by + simpa [VStructureView.structureType] using hspine.hasType_appN hfamily + have W : Ctx.LiftN 1 0 symbolicContext + (dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) := .one + change dependentRecordEnv.HasType 2 symbolicContext (.lam _ _) + (.forallE _ _) + refine .lam hstructure ?_ + change dependentRecordEnv.HasType 2 + (dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (VExpr.appN + (.const dependentRecordView.recursorName + (dependentRecordView.projectionLevels + symbolicKeyCode.fieldSort symbolicLevels)) + (symbolicMajorParams.map (VExpr.liftN 1) ++ + [symbolicKeyCode.typeFn.lift, symbolicKeyCode.minor.lift, + .bvar 0])) + (.app symbolicKeyCode.typeFn.lift (.bvar 0)) + apply dependentRecord_view_wf.recursorProjection_hasType + dependentRecordEnv_ordered symbolicLevels symbolicLevels_wf rfl + (symbolicMajorParams.map (VExpr.liftN 1)) (by rfl) + (fieldSort := symbolicKeyCode.fieldSort) + · refine ⟨resultLevel, ?_⟩ + have hfamilyClosed : + (dependentRecordView.familyType.instL symbolicLevels).ClosedN 0 := by + simpa using + (dependentRecordEnv_ordered.closedC + dependentRecord_view_wf.family).instL + have hspine' := hspine.weakN dependentRecordEnv_ordered W + rw [hfamilyClosed.liftN_eq (Nat.zero_le _)] at hspine' + simpa [VExpr.liftN] using hspine' + · change VLevel.WF 2 (.succ (.param 0)) + decide + · rfl + · exact ⟨resultLevel, by + simpa [VExpr.liftN] using hstructure.weakN dependentRecordEnv_ordered W⟩ + · change dependentRecordEnv.HasType 2 + (dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (.lam + (dependentRecordView.structureType symbolicLevels + (symbolicMajorParams.map (VExpr.liftN 1))) + (.bvar 4)) + (.forallE + (dependentRecordView.structureType symbolicLevels + (symbolicMajorParams.map (VExpr.liftN 1))) + (.sort (.succ (.param 0)))) + refine VEnv.HasType.lam (u := resultLevel) ?_ (by type_tac) + simpa [VExpr.liftN] using + hstructure.weakN dependentRecordEnv_ordered W + · change dependentRecordEnv.HasType 2 + (dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (.lam (.bvar 3) + (.lam (.app (.bvar 3) (.bvar 0)) (.bvar 1))) + (.forallE (.bvar 3) + (.forallE (.app (.bvar 3) (.bvar 0)) + (.app + (.lam + (.app + (.app (.const ``DependentRecord symbolicLevels) (.bvar 5)) + (.bvar 4)) + (.bvar 6)) + (.app + (.app + (.app + (.app (.const ``DependentRecord.mk symbolicLevels) + (.bvar 5)) + (.bvar 4)) + (.bvar 1)) + (.bvar 0))))) + refine .lam (by type_tac) ?_ + refine .lam (by type_tac) ?_ + have hkey : dependentRecordEnv.HasType 2 + ((.app (.bvar 3) (.bvar 0)) :: .bvar 3 :: + dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (.bvar 1) (.bvar 5) := by + type_tac + apply (show dependentRecordEnv.IsDefEq 2 + ((.app (.bvar 3) (.bvar 0)) :: .bvar 3 :: + dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (.bvar 5) + (.app + (.lam + (.app + (.app (.const ``DependentRecord symbolicLevels) (.bvar 5)) + (.bvar 4)) + (.bvar 6)) + (.app + (.app + (.app + (.app (.const ``DependentRecord.mk symbolicLevels) (.bvar 5)) + (.bvar 4)) + (.bvar 1)) + (.bvar 0))) + (.sort (.succ (.param 0))) from ?_).defeq hkey + let S : VExpr := + .app + (.app (.const ``DependentRecord symbolicLevels) (.bvar 5)) + (.bvar 4) + let ctorApp : VExpr := + .app + (.app + (.app + (.app (.const ``DependentRecord.mk symbolicLevels) (.bvar 5)) + (.bvar 4)) + (.bvar 1)) + (.bvar 0) + have hbody : dependentRecordEnv.HasType 2 + (S :: (.app (.bvar 3) (.bvar 0)) :: .bvar 3 :: + dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (.bvar 6) (.sort (.succ (.param 0))) := by + dsimp [S] + type_tac + have hctor : dependentRecordEnv.HasType 2 + ((.app (.bvar 3) (.bvar 0)) :: .bvar 3 :: + dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + ctorApp S := by + have hc := VEnv.HasType.const + (Γ := ((.app (.bvar 3) (.bvar 0)) :: .bvar 3 :: + dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext)) + dependentRecord_view_wf.constructor symbolicLevels_wf (by rfl) + have hα : dependentRecordEnv.HasType 2 + ((.app (.bvar 3) (.bvar 0)) :: .bvar 3 :: + dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (.bvar 5) (.sort (.succ (.param 0))) := by + type_tac + have hFamily : dependentRecordEnv.HasType 2 + ((.app (.bvar 3) (.bvar 0)) :: .bvar 3 :: + dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (.bvar 4) + (.forallE (.bvar 5) (.sort (.succ (.param 1)))) := by + type_tac + have hKey : dependentRecordEnv.HasType 2 + ((.app (.bvar 3) (.bvar 0)) :: .bvar 3 :: + dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (.bvar 1) (.bvar 5) := by + type_tac + have hValue : dependentRecordEnv.HasType 2 + ((.app (.bvar 3) (.bvar 0)) :: .bvar 3 :: + dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (.bvar 0) (.app (.bvar 4) (.bvar 1)) := by + type_tac + have hcα := hc.app hα + have hcFamily := hcα.app hFamily + have hcKey := hcFamily.app hKey + have hcValue := hcKey.app hValue + change dependentRecordEnv.HasType 2 + ((.app (.bvar 3) (.bvar 0)) :: .bvar 3 :: + dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + ctorApp S at hcValue + exact hcValue + have hbeta := VEnv.IsDefEq.beta hbody hctor + simpa [S, ctorApp, VExpr.inst, VExpr.instVar] using hbeta.symm + · exact .bvar .zero + +def symbolicKeyProjectorBody : VExpr := + match symbolicKeyCode.projector.liftN 3 with + | .lam _ body => body + | expression => expression + +theorem symbolicKeyProjector_lift_shape : + symbolicKeyCode.projector.liftN 3 = + .lam symbolicInnerStructureType symbolicKeyProjectorBody := by + decide + +theorem symbolicKeyProjector_beta_shape : + symbolicKeyProjectorBody.inst symbolicConstructorApp = + VExpr.instRev symbolicKeyRuleLhsBody symbolicKeyRuleArgs := by + decide + +theorem symbolicKeyRule_rhs_result_shape : + VExpr.instRev symbolicKeyRuleRhsBody symbolicKeyRuleArgs = + .app (.app symbolicKeyMinor (.bvar 1)) (.bvar 0) := by + decide + +/-- The generated key projector computes on the generated constructor by +the registered recursor iota rule. -/ +theorem symbolicKey_constructor_defeq : dependentRecordEnv.IsDefEq 2 + symbolicFieldContext + (.app (symbolicKeyCode.projector.liftN 3) symbolicConstructorApp) + (.bvar 1) (.bvar 5) := by + have W3 : Ctx.LiftN 3 0 symbolicContext symbolicFieldContext := + .zero [.app (.bvar 3) (.bvar 0), .bvar 3, + dependentRecordView.structureType symbolicLevels symbolicMajorParams] + have hprojector := symbolicKeyProjector_hasType.weakN + dependentRecordEnv_ordered W3 + rw [symbolicKeyProjector_lift_shape] at hprojector + obtain ⟨_, ⟨projectorBodyType, hprojectorBody⟩⟩ := + hprojector.lam_inv dependentRecordEnv_ordered symbolicFieldContext_wf + have hprojectorBeta := VEnv.IsDefEq.beta hprojectorBody + symbolicConstructor_hasType + rw [← symbolicKeyProjector_lift_shape, + symbolicKeyProjector_beta_shape] at hprojectorBeta + have hprojectorToRule : dependentRecordEnv.IsDefEqU 2 + symbolicFieldContext + (.app (symbolicKeyCode.projector.liftN 3) symbolicConstructorApp) + (VExpr.instRev symbolicKeyRuleLhsBody symbolicKeyRuleArgs) := + ⟨projectorBodyType.inst symbolicConstructorApp, hprojectorBeta⟩ + + have houterBody : dependentRecordEnv.HasType 2 + ((.bvar 5) :: symbolicFieldContext) + (.lam (.app (.bvar 5) (.bvar 0)) (.bvar 1)) + (.forallE (.app (.bvar 5) (.bvar 0)) (.bvar 7)) := by + refine .lam (by type_tac) (by type_tac) + have hkey : dependentRecordEnv.HasType 2 symbolicFieldContext + (.bvar 1) (.bvar 5) := by + type_tac + have houterBeta := VEnv.IsDefEq.beta houterBody hkey + change dependentRecordEnv.IsDefEq 2 symbolicFieldContext + (.app symbolicKeyMinor (.bvar 1)) _ _ at houterBeta + have hvalue : dependentRecordEnv.HasType 2 symbolicFieldContext + (.bvar 0) (.app (.bvar 4) (.bvar 1)) := by + type_tac + have houterApplied := VEnv.IsDefEq.appDF houterBeta hvalue + have hinnerBody : dependentRecordEnv.HasType 2 + ((.app (.bvar 4) (.bvar 1)) :: symbolicFieldContext) + (.bvar 2) (.bvar 6) := by + type_tac + have hinnerBeta := VEnv.IsDefEq.beta hinnerBody hvalue + have hminorToKey := houterApplied.trans hinnerBeta + rw [← symbolicKeyRule_rhs_result_shape] at hminorToKey + have hresult := VEnv.IsDefEqU.trans dependentRecordEnv_wf + symbolicFieldContext_wf hprojectorToRule + (VEnv.IsDefEqU.trans dependentRecordEnv_wf symbolicFieldContext_wf + symbolicKeyRule_reduces ⟨_, hminorToKey⟩) + exact hresult.of_r dependentRecordEnv_wf symbolicFieldContext_wf hkey + +def symbolicValueTypeFnBody : VExpr := + .app (.bvar 5) + (.app (symbolicKeyCode.projector.liftN 4) (.bvar 0)) + +theorem symbolicValueTypeFn_lift_shape : + symbolicValueCode.typeFn.lift.liftN 2 = + .lam symbolicInnerStructureType symbolicValueTypeFnBody := by + decide + +theorem symbolicValueTypeFn_beta_shape : + symbolicValueTypeFnBody.inst symbolicConstructorApp = + .app (.bvar 4) + (.app (symbolicKeyCode.projector.liftN 3) + symbolicConstructorApp) := by + decide + +theorem symbolicValueTypeFnBody_hasType : dependentRecordEnv.HasType 2 + (symbolicInnerStructureType :: symbolicFieldContext) + symbolicValueTypeFnBody (.sort (.succ (.param 1))) := by + have W4 : Ctx.LiftN 4 0 symbolicContext + (symbolicInnerStructureType :: symbolicFieldContext) := + .zero [symbolicInnerStructureType, + .app (.bvar 3) (.bvar 0), .bvar 3, + dependentRecordView.structureType symbolicLevels symbolicMajorParams] + have hkeyProjector := symbolicKeyProjector_hasType.weakN + dependentRecordEnv_ordered W4 + have hkeyAtMajor := hkeyProjector.app (VEnv.HasType.bvar (.zero)) + change dependentRecordEnv.HasType 2 + (symbolicInnerStructureType :: symbolicFieldContext) _ + (.app + (.lam + (dependentRecordView.structureType symbolicLevels + [.bvar 6, .bvar 5]) + (.bvar 7)) + (.bvar 0)) at hkeyAtMajor + have hkeyBetaRaw : dependentRecordEnv.IsDefEq 2 + (symbolicInnerStructureType :: symbolicFieldContext) + (.app + (.lam + (dependentRecordView.structureType symbolicLevels + [.bvar 6, .bvar 5]) + (.bvar 7)) + (.bvar 0)) + ((VExpr.bvar 7).inst (.bvar 0)) + ((VExpr.sort (.succ (.param 0))).inst (.bvar 0)) := by + apply VEnv.IsDefEq.beta + · type_tac + · exact .bvar .zero + have hkeyBeta : dependentRecordEnv.IsDefEq 2 + (symbolicInnerStructureType :: symbolicFieldContext) + (.app + (.lam + (dependentRecordView.structureType symbolicLevels + [.bvar 6, .bvar 5]) + (.bvar 7)) + (.bvar 0)) + (.bvar 6) (.sort (.succ (.param 0))) := by + simpa [VExpr.inst, VExpr.instVar] using hkeyBetaRaw + have hkeyAtMajor' := hkeyBeta.defeq hkeyAtMajor + have hfamily : dependentRecordEnv.HasType 2 + (symbolicInnerStructureType :: symbolicFieldContext) + (.bvar 5) + (.forallE (.bvar 6) (.sort (.succ (.param 1)))) := by + type_tac + exact hfamily.app hkeyAtMajor' + +theorem symbolicValueProjector_hasType : + dependentRecordEnv.HasType 2 symbolicContext symbolicValueCode.projector + (.forallE + (dependentRecordView.structureType symbolicLevels symbolicMajorParams) + (.app symbolicValueCode.typeFn.lift (.bvar 0))) := by + obtain ⟨resultLevel, hspine⟩ := symbolicParams_spine + have hfamily := VEnv.HasType.const + (Γ := symbolicContext) dependentRecord_view_wf.family + symbolicLevels_wf (by rfl) + have hstructure : dependentRecordEnv.HasType 2 symbolicContext + (dependentRecordView.structureType symbolicLevels symbolicMajorParams) + (.sort resultLevel) := by + simpa [VStructureView.structureType] using hspine.hasType_appN hfamily + have W : Ctx.LiftN 1 0 symbolicContext + (dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) := .one + change dependentRecordEnv.HasType 2 symbolicContext (.lam _ _) + (.forallE _ _) + refine .lam hstructure ?_ + change dependentRecordEnv.HasType 2 + (dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (VExpr.appN + (.const dependentRecordView.recursorName + (dependentRecordView.projectionLevels + symbolicValueCode.fieldSort symbolicLevels)) + (symbolicMajorParams.map (VExpr.liftN 1) ++ + [symbolicValueCode.typeFn.lift, symbolicValueCode.minor.lift, + .bvar 0])) + (.app symbolicValueCode.typeFn.lift (.bvar 0)) + apply dependentRecord_view_wf.recursorProjection_hasType + dependentRecordEnv_ordered symbolicLevels symbolicLevels_wf rfl + (symbolicMajorParams.map (VExpr.liftN 1)) (by rfl) + (fieldSort := symbolicValueCode.fieldSort) + · refine ⟨resultLevel, ?_⟩ + have hfamilyClosed : + (dependentRecordView.familyType.instL symbolicLevels).ClosedN 0 := by + simpa using + (dependentRecordEnv_ordered.closedC + dependentRecord_view_wf.family).instL + have hspine' := hspine.weakN dependentRecordEnv_ordered W + rw [hfamilyClosed.liftN_eq (Nat.zero_le _)] at hspine' + simpa [VExpr.liftN] using hspine' + · change VLevel.WF 2 (.succ (.param 1)) + decide + · rfl + · exact ⟨resultLevel, by + simpa [VExpr.liftN] using hstructure.weakN dependentRecordEnv_ordered W⟩ + · change dependentRecordEnv.HasType 2 + (dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + symbolicValueCode.typeFn.lift + (.forallE + (dependentRecordView.structureType symbolicLevels + (symbolicMajorParams.map (VExpr.liftN 1))) + (.sort (.succ (.param 1)))) + change dependentRecordEnv.HasType 2 + (dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (.lam + (dependentRecordView.structureType symbolicLevels + (symbolicMajorParams.map (VExpr.liftN 1))) + (.app (.bvar 3) + (.app (symbolicKeyCode.projector.lift.liftN 1 1) (.bvar 0)))) + (.forallE + (dependentRecordView.structureType symbolicLevels + (symbolicMajorParams.map (VExpr.liftN 1))) + (.sort (.succ (.param 1)))) + refine VEnv.HasType.lam (u := resultLevel) ?_ ?_ + · simpa [VExpr.liftN] using + hstructure.weakN dependentRecordEnv_ordered W + · have Wbody : Ctx.LiftN 1 0 + (dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (dependentRecordView.structureType symbolicLevels + (symbolicMajorParams.map (VExpr.liftN 1)) :: + dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) := .one + have hkeyProjector := + (symbolicKeyProjector_hasType.weakN dependentRecordEnv_ordered W).weakN + dependentRecordEnv_ordered Wbody + have hkeyAtMajor := hkeyProjector.app (VEnv.HasType.bvar (.zero)) + have hkeyTypeFn : symbolicKeyCode.typeFn = + .lam + (dependentRecordView.structureType symbolicLevels symbolicMajorParams) + (.bvar 3) := rfl + rw [hkeyTypeFn] at hkeyAtMajor + change dependentRecordEnv.HasType 2 + (dependentRecordView.structureType symbolicLevels + (symbolicMajorParams.map (VExpr.liftN 1)) :: + dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + _ + (.app + (.lam + (dependentRecordView.structureType symbolicLevels + [.bvar 4, .bvar 3]) + (.bvar 5)) + (.bvar 0)) at hkeyAtMajor + have hkeyBetaRaw : dependentRecordEnv.IsDefEq 2 + (dependentRecordView.structureType symbolicLevels + (symbolicMajorParams.map (VExpr.liftN 1)) :: + dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (.app + (.lam + (dependentRecordView.structureType symbolicLevels + [.bvar 4, .bvar 3]) + (.bvar 5)) + (.bvar 0)) + ((VExpr.bvar 5).inst (.bvar 0)) + ((VExpr.sort (.succ (.param 0))).inst (.bvar 0)) := by + apply VEnv.IsDefEq.beta + · type_tac + · exact .bvar .zero + have hkeyBeta : dependentRecordEnv.IsDefEq 2 + (dependentRecordView.structureType symbolicLevels + (symbolicMajorParams.map (VExpr.liftN 1)) :: + dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (.app + (.lam + (dependentRecordView.structureType symbolicLevels + [.bvar 4, .bvar 3]) + (.bvar 5)) + (.bvar 0)) + (.bvar 4) (.sort (.succ (.param 0))) := by + simpa [VExpr.inst, VExpr.instVar] using hkeyBetaRaw + have hkeyAtMajor' := hkeyBeta.defeq hkeyAtMajor + have hprojectorLift : + VExpr.liftN 1 (VExpr.liftN 1 symbolicKeyCode.projector) = + symbolicKeyCode.projector.lift.liftN 1 1 := rfl + rw [hprojectorLift] at hkeyAtMajor' + have hfamilyAtMajor : dependentRecordEnv.HasType 2 + (dependentRecordView.structureType symbolicLevels + (symbolicMajorParams.map (VExpr.liftN 1)) :: + dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (.bvar 3) + (.forallE (.bvar 4) (.sort (.succ (.param 1)))) := by + type_tac + exact hfamilyAtMajor.app hkeyAtMajor' + · change dependentRecordEnv.HasType 2 + (dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (.lam (.bvar 3) + (.lam (.app (.bvar 3) (.bvar 0)) (.bvar 0))) + (dependentRecordView.projectionMinorType symbolicLevels + (symbolicMajorParams.map (VExpr.liftN 1)) + (dependentRecordView.specializedFields symbolicLevels + (symbolicMajorParams.map (VExpr.liftN 1))) + symbolicValueCode.typeFn.lift) + have hfields : dependentRecordView.specializedFields symbolicLevels + (symbolicMajorParams.map (VExpr.liftN 1)) = + [.bvar 3, .app (.bvar 3) (.bvar 0)] := rfl + rw [hfields] + change dependentRecordEnv.HasType 2 + (dependentRecordView.structureType symbolicLevels symbolicMajorParams :: + symbolicContext) + (.lam (.bvar 3) + (.lam (.app (.bvar 3) (.bvar 0)) (.bvar 0))) + (.forallE (.bvar 3) + (.forallE (.app (.bvar 3) (.bvar 0)) + (.app (symbolicValueCode.typeFn.lift.liftN 2) + (dependentRecordView.projectionConstructorApp symbolicLevels + (symbolicMajorParams.map (VExpr.liftN 1)) + [.bvar 3, .app (.bvar 3) (.bvar 0)])))) + refine .lam (by type_tac) ?_ + refine .lam (by type_tac) ?_ + have htargetBeta := VEnv.IsDefEq.beta + symbolicValueTypeFnBody_hasType symbolicConstructor_hasType + rw [← symbolicValueTypeFn_lift_shape, + symbolicValueTypeFn_beta_shape] at htargetBeta + have hfamily : dependentRecordEnv.HasType 2 symbolicFieldContext + (.bvar 4) + (.forallE (.bvar 5) (.sort (.succ (.param 1)))) := by + type_tac + have htargetToNatural := htargetBeta.trans + (VEnv.IsDefEq.appDF hfamily symbolicKey_constructor_defeq) + have hvalue : dependentRecordEnv.HasType 2 symbolicFieldContext + (.bvar 0) (.app (.bvar 4) (.bvar 1)) := by + type_tac + exact htargetToNatural.defeq' hvalue + · exact .bvar .zero + +def symbolicKeyResult : VExpr := + .app symbolicKeyCode.projector symbolicMajor + +def symbolicValueResult : VExpr := + .app symbolicValueCode.projector symbolicMajor + +theorem key_representable : + dependentRecordEnv.TrProj 2 symbolicContext dependentRecordView + symbolicLevels symbolicMajorParams 0 symbolicMajor symbolicKeyResult := by + refine { + viewWF := dependentRecord_view_wf + levelsWF := symbolicLevels_wf + levels_length := rfl + params_length := rfl + paramsSpine := symbolicParams_spine + majorType := symbolicMajor_hasType + program := ⟨symbolicKeyCode, rfl, rfl, + symbolicKeyProjector_hasType⟩ } + +theorem value_representable : + dependentRecordEnv.TrProj 2 symbolicContext dependentRecordView + symbolicLevels symbolicMajorParams 1 symbolicMajor symbolicValueResult := by + refine { + viewWF := dependentRecord_view_wf + levelsWF := symbolicLevels_wf + levels_length := rfl + params_length := rfl + paramsSpine := symbolicParams_spine + majorType := symbolicMajor_hasType + program := ⟨symbolicValueCode, rfl, rfl, + symbolicValueProjector_hasType⟩ } + +/-- The one generated iota equation used by both projection programs is +actually registered in the final Theory environment. -/ +example : dependentRecordGeneration.generatedRules.length = 1 := rfl + +theorem dependentRecord_rules_registered : + ∀ rule ∈ dependentRecordGeneration.generatedRules, + dependentRecordEnv.defeqs rule := + dependentRecord_view_wf.rules + +/-! ## Frozen legacy surface + +The seven fields below preserve the exact pre-Spec-13 theorem shapes. They +are intentionally only statement data: constructing this bundle would +reintroduce the old proof obligations. In particular, `wf` permits +unrelated contexts, `uniq` permits unrelated structure names, and every +field omits the environment, universe instantiation, and parameter spine. -/ + +abbrev LegacyTrProj := + List VExpr → Name → Nat → VExpr → VExpr → Prop + +structure LegacyProjectionLaws (R : LegacyTrProj) : Prop where + weak : ∀ {n Γ Γ' s i e e'}, + Ctx.Lift' n Γ Γ' → R Γ s i e e' → + R Γ' s i (e.lift' n) (e'.lift' n) + inverseWeakening : ∀ {env U l Γ Γ' s i e e'}, + VEnv.WF env → OnCtx Γ' (env.IsType U) → Ctx.Lift' l Γ Γ' → + R Γ' s i (e.lift' l) e' → ∃ result, R Γ s i e result + contextDefEq : ∀ {env U Γ₁ Γ₂ s i e₁ e₂ result}, + VEnv.WF env → env.IsDefEqCtx U [] Γ₁ Γ₂ → + env.IsDefEqU U Γ₁ e₁ e₂ → R Γ₁ s i e₁ result → + ∃ result', R Γ₂ s i e₂ result' + wellFormed : ∀ {env U Δ Γ s i e result}, + R Δ s i e result → VExpr.WF env U Γ e → + VExpr.WF env U Γ result + unique : ∀ {env U Γ₁ Γ₂ s₁ s₂ i e₁ e₂ result₁ result₂}, + VEnv.WF env → env.IsDefEqCtx U [] Γ₁ Γ₂ → + R Γ₁ s₁ i e₁ result₁ → R Γ₂ s₂ i e₂ result₂ → + env.IsDefEqU U Γ₁ e₁ e₂ → + env.IsDefEqU U Γ₁ result₁ result₂ + termSubstitution : ∀ {Γ₀ Γ₁ Γ s i e e' e₀ A₀ k}, + Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ → R Γ₁ s i e e' → + R Γ s i (e.inst e₀ k) (e'.inst e₀ k) + universeInstantiation : ∀ {U' Γ s i e e'} {ls : List VLevel}, + (∀ level ∈ ls, level.WF U') → R Γ s i e e' → + R (Γ.map (VExpr.instL ls)) s i (e.instL ls) (e'.instL ls) + +/-! ## Zero-field behavior -/ + +universe w + +structure EmptyRecord (α : Type w) where + +def emptyRecordCtor : VConstVal := + ⟨vconst(type_of% @EmptyRecord.mk), ``EmptyRecord.mk⟩ + +def emptyRecordType : VInductiveType where + name := ``EmptyRecord + uvars := 1 + type := vconst(type_of% @EmptyRecord).type + ctors := [emptyRecordCtor] + +def emptyRecordDecl : VInductDecl := + ⟨1, 1, [emptyRecordType]⟩ + +example : emptyRecordDecl.checked?.isSome = true := rfl + +def emptyRecordChecked : emptyRecordDecl.Checked := + emptyRecordDecl.checked?.get (by decide) + +def emptyRecordGeneration : emptyRecordDecl.GenerationChecked := + emptyRecordChecked.identityGeneration + +def emptyRecordView : VStructureView where + source := emptyRecordDecl + generation := emptyRecordGeneration + constructor := emptyRecordGeneration.block.ctorPairs[0] + constructor_eq := rfl + raw_indices_eq := rfl + checked_indices_eq := rfl + recursive_eq := rfl + fieldSorts := [] + fieldSorts_length := rfl + +def emptyRecordEnv : VEnv := + (VEnv.empty.addInductGeneration emptyRecordGeneration).get (by decide) + +theorem emptyRecord_add : + VEnv.empty.addInductGeneration emptyRecordGeneration = + some emptyRecordEnv := rfl + +theorem emptyRecord_trace : + Nonempty (VEnv.AddInductGenerationTrace VEnv.empty + emptyRecordEnv emptyRecordGeneration) := + VEnv.addInductGeneration_trace emptyRecord_add + +theorem emptyRecordDecl_wf : emptyRecordDecl.WF VEnv.empty := by + refine ⟨rfl, ?_⟩ + intro ty hty + have hty' : ty = emptyRecordType := + List.mem_singleton.1 (by simpa [emptyRecordDecl] using hty) + subst ty + refine ⟨⟨⟨_, by type_tac⟩, trivial⟩, ?_⟩ + intro c hc + have hc' : c = emptyRecordCtor := by + simpa [emptyRecordType] using hc + subst c + constructor + · simp [emptyRecordDecl, emptyRecordType, emptyRecordCtor, + VInductDecl.fieldsWF, VInductDecl.ctorFields, + VExpr.dropN] + · simp [emptyRecordDecl, emptyRecordType, emptyRecordCtor, + VInductDecl.ctorFields, VInductDecl.recFieldIdxs, + VInductDecl.sortLevel, VExpr.dropN, VExpr.resultOf, + VExpr.forallN, VExpr.liftTelN, VExpr.appArgs] + exact .nil + +theorem emptyRecordGeneration_wf : + emptyRecordGeneration.WF VEnv.empty := + (emptyRecordChecked.wf_of_decl + emptyRecordDecl_wf).identityGeneration .empty + +theorem emptyRecord_generation_semantics : + emptyRecordView.GenerationSemantics emptyRecordEnv := by + rcases emptyRecord_trace with ⟨trace⟩ + exact .ofGenerationTrace emptyRecordGeneration_wf trace + +theorem emptyRecord_registered : emptyRecordView.Registered emptyRecordEnv := by + rcases emptyRecord_trace with ⟨trace⟩ + refine { + family := trace.family_lookup + constructor := ?_ + recursor := trace.rec_lookup + rules := fun _ h => trace.rule_mem h } + apply trace.ctor_lookup + rw [← emptyRecordGeneration.rawCtors_eq] + exact List.mem_map.2 ⟨emptyRecordView.constructor, + by + change emptyRecordView.constructor ∈ + emptyRecordView.generation.block.ctorPairs + rw [emptyRecordView.constructor_eq] + simp, + rfl⟩ + +theorem emptyRecord_view_wf : emptyRecordView.WF emptyRecordEnv := by + refine { + toRegistered := emptyRecord_registered + generationSemantics := emptyRecord_generation_semantics + parameters := ⟨⟨_, by type_tac⟩, trivial⟩ + parameters_length := rfl + fieldTelescope := .nil + smallFields := ?_ } + intro _ level hlevel + change level ∈ ([] : List VLevel) at hlevel + contradiction + +example : emptyRecordView.fields = [] := rfl + +example : emptyRecordView.projectionCodes [.param 0] [.bvar 0] = [] := rfl + +theorem emptyRecord_project_none (idx : Nat) (major : VExpr) : + emptyRecordView.project? [.param 0] [.bvar 0] idx major = none := by + simp [VStructureView.project?, show + emptyRecordView.projectionCodes [.param 0] [.bvar 0] = [] from rfl] + +#guard_named_axioms Ix.Theory.Named.Tests.ProjectionExpressibility.dependentRecord_view_wf [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.Tests.ProjectionExpressibility.key_representable [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.Tests.ProjectionExpressibility.value_representable [ + propext, + sorryAx, + Classical.choice, + Quot.sound] + +#guard_named_axioms Ix.Theory.Named.Tests.ProjectionExpressibility.emptyRecord_project_none [propext, Classical.choice, Quot.sound] + +end Ix.Theory.Named.Tests.ProjectionExpressibility diff --git a/Ix/Theory/Named/Inductive.lean b/Ix/Theory/Named/Inductive.lean new file mode 100644 index 000000000..2e67b9fd2 --- /dev/null +++ b/Ix/Theory/Named/Inductive.lean @@ -0,0 +1,3045 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.VDecl +import Ix.Theory.Named.Typing.Basic + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named + +deriving instance DecidableEq for VLevel +deriving instance DecidableEq for VExpr + +/-- Syntactic underapproximation of `VLevel.IsNeverZero`, +mirroring `Lean.Level.isNeverZero`. -/ +def _root_.Ix.Theory.VLevel.isNeverZero : VLevel → Bool + | .zero | .param _ => false + | .succ _ => true + | .max l₁ l₂ => l₁.isNeverZero || l₂.isNeverZero + | .imax _ l₂ => l₂.isNeverZero + +instance VExpr.decClosedN : ∀ (e : VExpr) (k : Nat), Decidable (e.ClosedN k) + | .bvar i, k => inferInstanceAs (Decidable (i < k)) + | .sort .., _ | .const .., _ => inferInstanceAs (Decidable True) + | .app e1 e2, k => @instDecidableAnd _ _ (decClosedN e1 k) (decClosedN e2 k) + | .lam e1 e2, k => @instDecidableAnd _ _ (decClosedN e1 k) (decClosedN e2 (k+1)) + | .forallE e1 e2, k => @instDecidableAnd _ _ (decClosedN e1 k) (decClosedN e2 (k+1)) + +instance VExpr.decLevelWF : ∀ (e : VExpr) (U : Nat), Decidable (e.LevelWF U) + | .bvar _, _ => inferInstanceAs (Decidable True) + | .sort l, U => VLevel.decidable_WF (n := U) (l := l) + | .const _ ls, U => inferInstanceAs (Decidable (∀ l ∈ ls, l.WF U)) + | .app e1 e2, U | .lam e1 e2, U | .forallE e1 e2, U => + @instDecidableAnd _ _ (decLevelWF e1 U) (decLevelWF e2 U) + +def VExpr.hasConst (n : Name) : VExpr → Bool + | .bvar _ | .sort _ => false + | .const c _ => c == n + | .app e1 e2 | .lam e1 e2 | .forallE e1 e2 => e1.hasConst n || e2.hasConst n + +/-- Context lifting changes only bound-variable indices and therefore +preserves the constants occurring in a Theory expression. -/ +@[simp] theorem VExpr.hasConst_lift' (expression : VExpr) (lift : Lift) + (name : Name) : + (expression.lift' lift).hasConst name = expression.hasConst name := by + induction expression generalizing lift <;> + simp [VExpr.hasConst, *] + +/-- `[.bvar (off+m-1), ..., .bvar off]`: the spine referring to the last `m` +binders, skipping the innermost `off`. -/ +def VExpr.bvarRevRange (off : Nat) : Nat → List VExpr + | 0 => [] + | m+1 => .bvar (off + m) :: bvarRevRange off m + +/-- Iterated lambda; the binder list is outermost first. -/ +def VExpr.lamN : List VExpr → VExpr → VExpr + | [], e => e + | A :: As, e => .lam A (lamN As e) + +/-- Iterated pi; the binder list is outermost first. -/ +def VExpr.forallN : List VExpr → VExpr → VExpr + | [], e => e + | A :: As, e => .forallE A (forallN As e) + +/-- Insert `n` binders below a telescope at depth `k`: the entry at depth +`j` within the telescope lifts by `n` at cutoff `k+j`. -/ +def VExpr.liftTelN (n : Nat) : List VExpr → Nat → List VExpr + | [], _ => [] + | A :: As, k => A.liftN n k :: liftTelN n As (k+1) + +/-- The first `n` binder types of an iterated pi, outermost first. -/ +def VExpr.telN : Nat → VExpr → List VExpr + | 0, _ => [] + | n+1, .forallE A rest => A :: telN n rest + | _+1, _ => [] + +/-- Strip `n` binders from an iterated pi. -/ +def VExpr.dropN : Nat → VExpr → VExpr + | 0, e => e + | n+1, .forallE _ rest => dropN n rest + | _+1, e => e + +/-- The result of an iterated pi. -/ +def VExpr.resultOf : VExpr → VExpr + | .forallE _ rest => resultOf rest + | e => e + +/-- The application spine arguments, outermost first. -/ +def VExpr.appArgs : VExpr → List VExpr → List VExpr + | .app f a, acc => appArgs f (a :: acc) + | _, acc => acc + +def VExpr.appHead : VExpr → VExpr + | .app f _ => appHead f + | e => e + +/-- The levels `[.param k, ..., .param (n+k-1)]`; `VLevel.params` shifted by `k`. +The recursor universe list is the elimination level (`.param 0`) followed by +the declaration's levels shifted by one, so declaration-world expressions +enter the recursor's universe context via `instL (params' n 1)`. -/ +def _root_.Ix.Theory.VLevel.params' (n k : Nat) : List VLevel := (List.range n).map fun i => .param (i + k) + +/-- Well-formedness of a binder telescope over a context. -/ +def VEnv.OnTel (env : VEnv) (U : Nat) : List VExpr → List VExpr → Prop + | _, [] => True + | Γ, A :: As => env.IsType U Γ A ∧ OnTel env U (A :: Γ) As + +/-- Pointwise definitional equality of two binder telescopes, checked in the +context generated by the left (raw) telescope. This is deliberately stronger +than whole-`forall` equality: it is the structural evidence a normalizer must +produce so mixed raw/view artifacts never need `forall` injectivity to recover +their binder alignment. -/ +def VEnv.TelDefEq (env : VEnv) (U : Nat) : + List VExpr → List VExpr → List VExpr → Prop + | _, [], [] => True + | Γ, A :: As, A' :: As' => + (∃ u, env.IsDefEq U Γ A A' (.sort u)) ∧ + TelDefEq env U (A :: Γ) As As' + | _, _, _ => False + +namespace VInductDecl + +variable (U : Nat) (T : Name) (np : Nat) + +/-- The result sort of an inductive type past its parameters and +indices. -/ +def sortLevel (ty : VInductiveType) : VLevel := + match VExpr.resultOf (VExpr.dropN np ty.type) with + | .sort l => l + | _ => .zero + +/-- Constructor field types, outermost first. -/ +def ctorFields : VExpr → List VExpr + | .forallE B rest => B :: ctorFields rest + | _ => [] + +/-- The block type applied to its parameters, seen from `off` binders past +the parameter telescope (declaration universes). -/ +def recApp (off : Nat) : VExpr := + VExpr.appN (.const T (VLevel.params U)) (VExpr.bvarRevRange off np) + +/-- Is `B` a direct recursive occurrence at field depth `j`: the block +constant applied to the parameter variables and `ni` block-free index +arguments. -/ +def isRecField (ni j : Nat) (B : VExpr) : Bool := + VExpr.appHead B == .const T (VLevel.params U) && + (VExpr.appArgs B []).length == np + ni && + (VExpr.appArgs B []).take np == VExpr.bvarRevRange j np && + ((VExpr.appArgs B []).drop np).all fun e => !e.hasConst T + +/-- The index arguments of a recursive occurrence (or of a constructor's +result). -/ +def recFieldIdxs (B : VExpr) : List VExpr := (VExpr.appArgs B []).drop np + +/-- One recursive constructor argument after normalization. `binders` is the +possibly empty Pi telescope leading to the recursive target. `fieldIndex` +addresses the constructor field itself, while target indices live under both +the preceding constructor fields and these binders. `targetType` is the +source-ordered family ordinal retained by block analysis; it remains zero in +the one-family compatibility analyzer. -/ +structure RecArg where + fieldIndex : Nat + binders : List VExpr + targetType : Nat + indices : List VExpr + +/-- Whether an expression mentions any family in a block. Mutual analysis +uses one block-wide name set in family formers, recursive-Pi domains, and +recursive indices; checking only the current family would miss negative +occurrences of its siblings. -/ +def _root_.Ix.Theory.Named.VExpr.hasAnyConst (names : List Name) (e : VExpr) : Bool := + names.any e.hasConst + +/-- The part of a family declaration needed to recognize a recursive target. +The position of a header in `familyHeaders` is the `RecArg.targetType` stored +by the block analyzer. -/ +structure FamilyHeader where + name : Name + indices : Nat + +/-- Family headers in declaration order. -/ +def familyHeaders (np : Nat) (types : List VInductiveType) : + List FamilyHeader := + types.map fun ty => + ⟨ty.name, (ctorFields (VExpr.dropN np ty.type)).length⟩ + +/-- Recognize an application of any family in a mutual block. The returned +ordinal is its exact position in `headers`; parameter arguments must be the +shared parameter variables and every index must be free of all block +families. -/ +def blockTarget? (U np j : Nat) (headers : List FamilyHeader) + (names : List Name) (B : VExpr) : Option (Nat × List VExpr) := + let head := B.appHead + let args := B.appArgs [] + let rec loop (target : Nat) : List FamilyHeader → + Option (Nat × List VExpr) + | [] => none + | header :: headers => + if head == .const header.name (VLevel.params U) && + args.length == np + header.indices && + args.take np == VExpr.bvarRevRange j np && + (args.drop np).all fun e => !e.hasAnyConst names then + some (target, args.drop np) + else + loop (target + 1) headers + loop 0 headers + +/-- Recognize a recursive mutual target below a possibly empty positive Pi +telescope. No block family may occur in a Pi domain. -/ +def blockRecTarget? (U np : Nat) (headers : List FamilyHeader) + (names : List Name) : Nat → VExpr → + Option (List VExpr × Nat × List VExpr) + | j, .forallE A rest => + if A.hasAnyConst names then none + else + match blockRecTarget? U np headers names (j+1) rest with + | some (binders, target, indices) => + some (A :: binders, target, indices) + | none => none + | j, B => + match blockTarget? U np j headers names B with + | some (target, indices) => some ([], target, indices) + | none => none + +/-- Analyze one constructor field against every family in a block. -/ +def blockRecArg? (U np : Nat) (headers : List FamilyHeader) + (names : List Name) (j : Nat) (B : VExpr) : Option RecArg := + match blockRecTarget? U np headers names j B with + | some (binders, targetType, indices) => + some { fieldIndex := j, binders, targetType, indices } + | none => none + +/-- Recursive arguments in source field order, with block-relative target +ordinals. -/ +def blockRecArgs (U np : Nat) (headers : List FamilyHeader) + (names : List Name) : List VExpr → (j : Nat := 0) → List RecArg + | [], _ => [] + | B :: Bs, j => + match blockRecArg? U np headers names j B with + | some r => r :: blockRecArgs U np headers names Bs (j+1) + | none => blockRecArgs U np headers names Bs (j+1) + +/-- Positional mutual recursive classification for every constructor field. +Unlike `blockRecArgs`, this list retains `none` entries, giving semantic +consumers an exact field-by-field alignment without rerunning the analyzer. -/ +def blockRecArgsAt (U np : Nat) (headers : List FamilyHeader) + (names : List Name) : List VExpr → (j : Nat := 0) → + List (Option RecArg) + | [], _ => [] + | B :: Bs, j => + blockRecArg? U np headers names j B :: + blockRecArgsAt U np headers names Bs (j + 1) + +/-- A mutual constructor field is either a positive recursive target of some +family in the block or is free of every block family. -/ +def blockStage3Field (U np : Nat) (headers : List FamilyHeader) + (names : List Name) (j : Nat) (B : VExpr) : Bool := + (blockRecArg? U np headers names j B).isSome || + !B.hasAnyConst names + +/-- Structural mutual constructor shape. Fields may target any family, but +the constructor result must target its owning family ordinal. -/ +def blockStage3Ctor (U np : Nat) (headers : List FamilyHeader) + (names : List Name) (owner : Nat) : Nat → VExpr → Bool + | j, .forallE B rest => + blockStage3Field U np headers names j B && + blockStage3Ctor U np headers names owner (j+1) rest + | j, result => + match blockTarget? U np j headers names result with + | some (target, _) => target == owner + | none => false + +/-- Recognize the kernel's one-family `isRecArg` shape in raw VExpr normal +form. Pi domains must be free of the family (the local strict-positivity +condition); the terminal target must be the family applied to the declaration +parameters and family-free indices. The returned telescope is outermost first. +-/ +def recTarget? (ni : Nat) : Nat → VExpr → Option (List VExpr × List VExpr) + | j, .forallE A rest => + if A.hasConst T then none + else + match recTarget? ni (j+1) rest with + | some (As, idxs) => some (A :: As, idxs) + | none => none + | j, B => + if isRecField U T np ni j B then some ([], recFieldIdxs np B) else none + +/-- Analyze a constructor field as a recursive argument, retaining its outer +field position and the complete binder telescope leading to the target. -/ +def recArg? (ni j : Nat) (B : VExpr) : Option RecArg := + match recTarget? U T np ni j B with + | some (binders, indices) => + some { fieldIndex := j, binders, targetType := 0, indices } + | none => none + +/-- Recursive arguments of a constructor field telescope, in field order. -/ +def recArgs (ni : Nat) : List VExpr → (j : Nat := 0) → List RecArg + | [], _ => [] + | B :: Bs, j => + match recArg? U T np ni j B with + | some r => r :: recArgs ni Bs (j+1) + | none => recArgs ni Bs (j+1) + +/-- Positional one-family recursive classification retained for compatibility +with the block-wide checked-constructor representation. -/ +def recArgsAt (ni : Nat) : List VExpr → (j : Nat := 0) → + List (Option RecArg) + | [], _ => [] + | B :: Bs, j => recArg? U T np ni j B :: recArgsAt ni Bs (j + 1) + +/-- Positions of the directly recursive fields, with their index +arguments (`j` counts binders past the parameters). -/ +def recPairs (ni : Nat) : List VExpr → (j : Nat := 0) → List (Nat × List VExpr) + | [], _ => [] + | B :: Bs, j => + if isRecField U T np ni j B then (j, recFieldIdxs np B) :: recPairs ni Bs (j+1) + else recPairs ni Bs (j+1) + +/-- Induction-hypothesis binder types for the recursive positions with +their (recursor-universe) index arguments, at depth `p` past the `m` +field binders in context `params ++ [motive]`: the `p`-th is +`motive idxs xⱼ`, the index arguments shifted from field depth `j` (past +the parameters, no motive) into position. -/ +def ihsFrom (m : Nat) : List (Nat × List VExpr) → Nat → List VExpr + | [], _ => [] + | (j, idxs) :: rs, p => + VExpr.appN (.bvar (m+p)) + ((idxs.map fun e => (e.liftN 1 j).liftN (m-j+p)) ++ [.bvar (m-1-j+p)]) :: + ihsFrom m rs (p+1) + +/-- A stage-3 field is either an analyzed recursive argument (possibly under +a strictly-positive Pi telescope) or a type not mentioning the block at all. +The analyzer rejects family occurrences in recursive-Pi domains and indices. +-/ +def stage3Field (ni j : Nat) (B : VExpr) : Bool := + (recArg? U T np ni j B).isSome || !B.hasConst T + +/-- Stage-3 constructor type past the parameters: a telescope of stage-3 +fields ending in the block's type constant applied to the parameters and +block-free index arguments. -/ +def stage3Ctor (ni : Nat) : Nat → VExpr → Bool + | j, .forallE B rest => stage3Field U T np ni j B && stage3Ctor ni (j+1) rest + | j, e => isRecField U T np ni j e + +/-- Levels that are structurally forced to evaluate to zero. This is the +environment-free fragment needed to recognize proof-valued constructor +fields; Verify supplies the exact ordinary-checker sort observations. -/ +def _root_.Ix.Theory.VLevel.isDefinitelyZero : VLevel → Bool + | .zero => true + | .max l₁ l₂ => l₁.isDefinitelyZero && l₂.isDefinitelyZero + | .imax _ l₂ => l₂.isDefinitelyZero + | .param _ | .succ _ => false + +/-- Partial local type synthesis used only to recognize fields whose inferred +sort is structurally `Prop`. Constants and terms requiring reduction remain +unknown and are conservatively treated as data. -/ +def inferLocalType? (Γ : List VExpr) : VExpr → Option VExpr + | .bvar i => Γ[i]? + | .sort l => some (.sort (.succ l)) + | .const _ _ => none + | .app f a => do + let .forallE _ body ← inferLocalType? Γ f | none + return body.inst a + | .lam A body => do + let bodyType ← inferLocalType? (A :: Γ) body + return .forallE A bodyType + | .forallE A body => do + let .sort u ← inferLocalType? Γ A | none + let .sort v ← inferLocalType? (A :: Γ) body | none + return .sort (.imax u v) + +/-- Whether the partial local synthesis proves that a binder is a proof. -/ +def knownProofField (Γ : List VExpr) (B : VExpr) : Bool := + match inferLocalType? Γ B with + | some (.sort l) => l.isDefinitelyZero + | _ => false + +/-- The kernel's singleton large-elimination criterion. Recursive arguments +and proof fields impose no recovery condition; every remaining data field +must occur literally in the constructor result's index spine. The proof-field +test is a conservative environment-free reflection here and is connected to +the exact checker result by Verify. -/ +def subsingletonOK (ni : Nat) (ct : VExpr) : Bool := + let Bs := ctorFields (VExpr.dropN np ct) + let m := Bs.length + let ridx := recFieldIdxs np (VExpr.resultOf (VExpr.dropN np ct)) + let rec loop (Γ : List VExpr) (j : Nat) : List VExpr → Bool + | [] => true + | B :: Bs => + ((recArg? U T np ni j B).isSome || knownProofField Γ B || + ridx.contains (.bvar (m-1-j))) && + loop (B :: Γ) (j+1) Bs + loop (VExpr.telN np ct).reverse 0 Bs + +/-- Large elimination: a never-zero result sort, or the (syntactic) +subsingleton criterion. -/ +def largeElim (ni : Nat) (ty : VInductiveType) : Bool := + (sortLevel np ty).isNeverZero || + match ty.ctors with + | [] => true + | [c] => subsingletonOK U T np ni c.type + | _ => false + +/-- The constructor-shape fragment of the kernel's K-target test. Only the +visible shared-parameter prefix may precede the constructor result. -/ +def isKTargetCtor (nparams : Nat) : Nat → VExpr → Bool + | i, .forallE _ body => + i < nparams && isKTargetCtor nparams (i + 1) body + | _, _ => true + +/-- Whether a checked one-family declaration receives the kernel's K-like +recursor reduction flag. This decision is deliberately independent of +`largeElim`: K eligibility must never be used to bypass the ordinary +large-elimination criterion. -/ +def isKTarget (np : Nat) (resultLevel : VLevel) + (ty : VInductiveType) : Bool := + resultLevel == .zero && + match ty.ctors with + | [ctor] => isKTargetCtor np 0 ctor.type + | _ => false + +/-- A family type is checked before its own constant is declared, so neither +its parameter nor index domains may already mention the family. This is the +one-family form of `checkInductiveTypes`' pre-environment type check. -/ +def typeFormerOK (np : Nat) (ty : VInductiveType) : Bool := + (VExpr.telN np ty.type).all (fun e => !e.hasConst ty.name) && + (ctorFields (VExpr.dropN np ty.type)).all (fun e => !e.hasConst ty.name) + +/-- The direct indexed recursion checks proved by the Stage-3 metatheory. -/ +def stage3DirectCore (U np : Nat) (ty : VInductiveType) : Bool := + let ni := (ctorFields (VExpr.dropN np ty.type)).length + ty.uvars == U && + (VExpr.telN np ty.type).length == np && + (match VExpr.resultOf (VExpr.dropN np ty.type) with + | .sort l => decide (l.WF U) + | _ => false) && + typeFormerOK np ty && + ty.ctors.all fun c => c.uvars == U && + VExpr.telN np c.type == VExpr.telN np ty.type && + stage3Ctor U ty.name np ni 0 (VExpr.dropN np c.type) + +/-- Names reserved by a one-family transaction, in insertion order. -/ +def generatedNames (ty : VInductiveType) : List Name := + ty.name :: (ty.ctors.map (fun c => c.name) ++ [.str ty.name "rec"]) + +/-- Internal duplicate/collision check, matching the sequential kernel name +reservations before any object becomes observable. -/ +def namesOK (ty : VInductiveType) : Bool := decide (generatedNames ty).Nodup + +/-- Kernel metadata types are closed before checking. Keeping this in the +computational descriptor prevents `addInduct` from accepting a hand-built +declaration with loose variables even when its result spine has the expected +head. -/ +def closedOK (ty : VInductiveType) : Bool := + decide (ty.type.ClosedN 0) && ty.ctors.all fun c => decide (c.type.ClosedN 0) + +/-- Every universe annotation in the family and constructor metadata must +refer to one of the declaration's universe parameters. The result-sort check +alone is insufficient: bad parameters or fields can hide an out-of-range +level while leaving the result universe valid. -/ +def levelsOK (U : Nat) (ty : VInductiveType) : Bool := + decide (ty.type.LevelWF U) && ty.ctors.all fun c => decide (c.type.LevelWF U) + +/-- The boolean core of the current one-family checker. Keeping this named +separately from `checked?` lets proofs unpack the accepted checks while all +consumers obtain their normalized data from the same descriptor. -/ +def stage3Core : VInductDecl → Bool + | ⟨U, np, [ty]⟩ => + stage3DirectCore U np ty && (namesOK ty && (closedOK ty && levelsOK U ty)) + | _ => false + +/-- Family names in exact declaration order. -/ +def familyNames (types : List VInductiveType) : List Name := + types.map (·.name) + +/-- The shared parameter telescope selected by the first family. Empty +blocks are rejected by `checkedBlock?`; defining this total function keeps the +dependent traversal itself free of a singleton/nonempty witness. -/ +def blockParams (np : Nat) : List VInductiveType → List VExpr + | [] => [] + | ty :: _ => VExpr.telN np ty.type + +/-- A family former in a mutual block is checked before any family constant +is staged, so every parameter and index domain must be free of every family +in the block. -/ +def blockTypeFormerOK (np : Nat) (names : List Name) + (ty : VInductiveType) : Bool := + (VExpr.telN np ty.type).all (fun e => !e.hasAnyConst names) && + (ctorFields (VExpr.dropN np ty.type)).all + (fun e => !e.hasAnyConst names) + +/-- Names reserved by the mutual pipeline: all families, then all constructors +in family/source order, then one recursor per family. Validation retains this +order as checked data, and the block transaction inserts the same three +constant phases before installing generated rules. -/ +def blockGeneratedNames (types : List VInductiveType) : List Name := + types.map (·.name) ++ + types.flatMap (fun ty => ty.ctors.map (·.name)) ++ + types.map (fun ty => .str ty.name "rec") + +/-- Block-wide duplicate/collision check for all future generated names. -/ +def blockNamesOK (types : List VInductiveType) : Bool := + decide (blockGeneratedNames types).Nodup + +/-- Environment-free structural representation check for one family in a +mutual block. It checks raw metadata anatomy and cross-family positivity. +Environment-sensitive normalization, shared-result-universe semantics, and +staging are supplied separately by `ValidatedBlock.WF` and +`ValidationCertificate`. -/ +def blockFamilyCore (source : VInductDecl) (params : List VExpr) + (owner : Nat) (ty : VInductiveType) : Bool := + let names := familyNames source.types + let headers := familyHeaders source.nparams source.types + ty.uvars == source.uvars && + params.length == source.nparams && + VExpr.telN source.nparams ty.type == params && + (match VExpr.resultOf (VExpr.dropN source.nparams ty.type) with + | .sort l => decide (l.WF source.uvars) + | _ => false) && + blockTypeFormerOK source.nparams names ty && + closedOK ty && levelsOK source.uvars ty && + ty.ctors.all fun c => + c.uvars == source.uvars && + VExpr.telN source.nparams c.type == params && + blockStage3Ctor source.uvars source.nparams headers names owner 0 + (VExpr.dropN source.nparams c.type) + +/-- Constructor identities retained across normalization. Types may change by +WHNF/definitional equality, but names, universe arities, order, and count may +not. -/ +def sameCtorHeaders : List VConstVal → List VConstVal → Bool + | [], [] => true + | c :: cs, c' :: cs' => + c.name == c'.name && c.uvars == c'.uvars && sameCtorHeaders cs cs' + | _, _ => false + +/-- Family identities retained across normalization. -/ +def sameTypeHeaders : List VInductiveType → List VInductiveType → Bool + | [], [] => true + | ty :: tys, ty' :: tys' => + ty.name == ty'.name && ty.uvars == ty'.uvars && + sameCtorHeaders ty.ctors ty'.ctors && sameTypeHeaders tys tys' + | _, _ => false + +/-- Computational coherence between stored kernel metadata and a normalized +analysis view. Normalization may change expression payloads only; declaration +arity and every generated identity remain fixed. -/ +def normalizationShape (source view : VInductDecl) : Bool := + source.uvars == view.uvars && + source.nparams == view.nparams && + sameTypeHeaders source.types view.types + +/-- Constructor headers are reflexively coherent. Kept computational so the +identity normalization below reduces without an external oracle. -/ +theorem sameCtorHeaders_refl : ∀ cs, sameCtorHeaders cs cs = true + | [] => rfl + | c :: cs => by + change ((c.name == c.name) && (c.uvars == c.uvars) && + sameCtorHeaders cs cs) = true + rw [beq_self_eq_true, beq_self_eq_true, sameCtorHeaders_refl] + rfl + +/-- Family headers are reflexively coherent. -/ +theorem sameTypeHeaders_refl : ∀ tys, sameTypeHeaders tys tys = true + | [] => rfl + | ty :: tys => by + change ((ty.name == ty.name) && (ty.uvars == ty.uvars) && + sameCtorHeaders ty.ctors ty.ctors && sameTypeHeaders tys tys) = true + rw [beq_self_eq_true, beq_self_eq_true, sameCtorHeaders_refl, + sameTypeHeaders_refl] + rfl + +theorem normalizationShape_refl (source : VInductDecl) : + normalizationShape source source = true := by + change ((source.uvars == source.uvars) && + (source.nparams == source.nparams) && + sameTypeHeaders source.types source.types) = true + rw [beq_self_eq_true, beq_self_eq_true, sameTypeHeaders_refl] + rfl + +/-- A named, environment-independent normalization boundary. `source` is the +stored declaration whose constants must be added to the Theory environment; +`view` is the WHNF/defeq-normalized syntax inspected by the inductive +analyzer. The boolean coherence proof prevents normalization from changing +declaration identities or ordering. -/ +structure Normalization (source : VInductDecl) where + view : VInductDecl + shape_eq : normalizationShape source view = true + +/-- Construct a normalization boundary by computation. -/ +def normalization? (source view : VInductDecl) : Option (Normalization source) := + if h : normalizationShape source view then some ⟨view, h⟩ else none + +/-- Identity normalization is the compatibility path for declarations already +in the analyzer's raw VExpr normal form. -/ +def Normalization.identity (source : VInductDecl) : Normalization source where + view := source + shape_eq := normalizationShape_refl source + +/-- Semantic validity of a one-family normalization view. The family type is +definitionally equal in the input environment; constructor types are +definitionally equal after the raw family constant has been introduced, which +matches the staging of the kernel's inductive check. `Normalization.BlockWF` +is the arbitrary-block counterpart. -/ +def Normalization.WF {source : VInductDecl} (norm : Normalization source) + (env : VEnv) : Prop := + ∃ raw view, + source.types = [raw] ∧ norm.view.types = [view] ∧ + env.IsDefEqU source.uvars [] raw.type view.type ∧ + ∀ envT, env.addConst raw.name raw.toVConstant = some envT → + List.Forall₂ + (fun rawCtor viewCtor => + envT.IsDefEqU source.uvars [] rawCtor.type viewCtor.type) + raw.ctors view.ctors + +/-- Stage every raw family constant, in source order, without adding any +constructors, recursors, or reduction rules. This is the Theory image of the +environment used by Lean between `declareInductiveTypes` and +`checkConstructors`; it is deliberately validation-only. -/ +def _root_.Ix.Theory.Named.VEnv.stageInductiveTypes (env : VEnv) + (types : List VInductiveType) : Option VEnv := + types.foldlM (fun env type => + env.addConst type.name type.toVConstant) env + +/-- Semantic validity of a normalization view for an arbitrary mutual block. + +Every family type is compared in the common input environment. The exact raw +family list is then staged as one source-ordered fold, and every constructor +comparison is performed in the resulting shared environment. The nested +`Forall₂` relations retain family and constructor order and cannot truncate a +reordered or shorter view. -/ +def Normalization.BlockWF {source : VInductDecl} + (norm : Normalization source) (env blockEnv : VEnv) : Prop := + env.stageInductiveTypes source.types = some blockEnv ∧ + List.Forall₂ + (fun raw view => + env.IsDefEqU source.uvars [] raw.type view.type ∧ + List.Forall₂ + (fun rawCtor viewCtor => + blockEnv.IsDefEqU source.uvars [] + rawCtor.type viewCtor.type) + raw.ctors view.ctors) + source.types norm.view.types + +/-- Whether the recursor may eliminate into a fresh universe or is confined +to `Prop`. -/ +inductive ElimMode where + | large + | small + deriving DecidableEq, Repr + +/-- Interpret the ordinary checker's Boolean large-elimination result in the +consumer-neutral Theory representation. -/ +def ElimMode.ofBool : Bool → ElimMode + | false => .small + | true => .large + +@[simp] theorem ElimMode.ofBool_false : ElimMode.ofBool false = .small := rfl +@[simp] theorem ElimMode.ofBool_true : ElimMode.ofBool true = .large := rfl + +/-- Universe-slot offset used by recursor metadata. Large elimination inserts +the fresh motive universe before the declaration universes; small elimination +adds no universe parameter. -/ +def ElimMode.offset : ElimMode → Nat + | .large => 1 + | .small => 0 + +/-- Universe arity of the generated recursor. -/ +abbrev ElimMode.recUvars (mode : ElimMode) (U : Nat) : Nat := U + mode.offset + +/-- The motive's result level in the recursor universe context. -/ +def ElimMode.motiveLevel : ElimMode → VLevel + | .large => .param 0 + | .small => .zero + +/-- Declaration universes as seen by the recursor. Large elimination shifts +them past the fresh motive level; small elimination retains their order. -/ +abbrev ElimMode.sourceLevels (mode : ElimMode) (U : Nat) : List VLevel := + VLevel.params' U mode.offset + +/-- Identity universe arguments for recursive calls to the generated +recursor. -/ +abbrev ElimMode.recLevels (mode : ElimMode) (U : Nat) : List VLevel := + VLevel.params (mode.recUvars U) + +@[simp] theorem ElimMode.large_offset : ElimMode.large.offset = 1 := rfl +@[simp] theorem ElimMode.small_offset : ElimMode.small.offset = 0 := rfl + +@[simp] theorem ElimMode.large_recUvars (U : Nat) : + ElimMode.large.recUvars U = U + 1 := rfl + +@[simp] theorem ElimMode.small_recUvars (U : Nat) : + ElimMode.small.recUvars U = U := by + simp [ElimMode.recUvars, ElimMode.offset] + +@[simp] theorem ElimMode.large_sourceLevels (U : Nat) : + ElimMode.large.sourceLevels U = VLevel.params' U 1 := rfl + +@[simp] theorem ElimMode.small_sourceLevels (U : Nat) : + ElimMode.small.sourceLevels U = VLevel.params U := rfl + +@[simp] theorem ElimMode.large_motiveLevel : + ElimMode.large.motiveLevel = .param 0 := rfl + +@[simp] theorem ElimMode.small_motiveLevel : + ElimMode.small.motiveLevel = .zero := rfl + +/-- Environment-free elimination analysis used by the raw compatibility +path. Verify's ordinary checker replay refines the singleton criterion with +the exact inferred field sorts. -/ +def eliminationMode (U : Nat) (T : Name) (np ni : Nat) + (ty : VInductiveType) : ElimMode := + if largeElim U T np ni ty then .large else .small + +/-- Normalized data for one constructor. -/ +structure CheckedCtor where + value : VConstVal + fields : List VExpr + recursive : List RecArg + recursiveAt : List (Option RecArg) + resultIndices : List VExpr + +def CheckedCtor.ofDirect (U : Nat) (T : Name) (np ni : Nat) + (c : VConstVal) : CheckedCtor where + value := c + fields := ctorFields (VExpr.dropN np c.type) + recursive := recArgs U T np ni (ctorFields (VExpr.dropN np c.type)) + recursiveAt := recArgsAt U T np ni (ctorFields (VExpr.dropN np c.type)) + resultIndices := recFieldIdxs np (VExpr.resultOf (VExpr.dropN np c.type)) + +/-- Analyze one constructor against the complete source-ordered family block. +Unlike `ofDirect`, recursive descriptors retain the ordinal of a sibling +target instead of forcing `targetType := 0`. -/ +def CheckedCtor.ofBlock (source : VInductDecl) + (c : VConstVal) : CheckedCtor where + value := c + fields := ctorFields (VExpr.dropN source.nparams c.type) + recursive := blockRecArgs source.uvars source.nparams + (familyHeaders source.nparams source.types) (familyNames source.types) + (ctorFields (VExpr.dropN source.nparams c.type)) + recursiveAt := blockRecArgsAt source.uvars source.nparams + (familyHeaders source.nparams source.types) (familyNames source.types) + (ctorFields (VExpr.dropN source.nparams c.type)) + resultIndices := recFieldIdxs source.nparams + (VExpr.resultOf (VExpr.dropN source.nparams c.type)) + +/-- Checked representation of one family at its exact block ordinal. The +family itself is a type index rather than a replaceable field; this prevents a +descriptor from being reused for another position or declaration. -/ +structure CheckedFamily (source : VInductDecl) (params : List VExpr) + (ordinal : Nat) (type : VInductiveType) where + params_eq : VExpr.telN source.nparams type.type = params + indices : List VExpr + indices_eq : indices = ctorFields (VExpr.dropN source.nparams type.type) + resultLevel : VLevel + result_eq : VExpr.resultOf (VExpr.dropN source.nparams type.type) = + .sort resultLevel + constructors : List CheckedCtor + constructors_eq : constructors = type.ctors.map (CheckedCtor.ofBlock source) + accepted : blockFamilyCore source params ordinal type = true + +/-- Recover the source family indexing a checked family. -/ +def CheckedFamily.value {source : VInductDecl} {params : List VExpr} + {ordinal : Nat} {type : VInductiveType} + (_ : CheckedFamily source params ordinal type) : VInductiveType := + type + +/-- Recover the block-relative ordinal indexing a checked family. -/ +def CheckedFamily.ordinal {source : VInductDecl} {params : List VExpr} + {ordinal : Nat} {type : VInductiveType} + (_ : CheckedFamily source params ordinal type) : Nat := + ordinal + +/-- A genuinely dependent family spine. Its list index is the exact suffix +of `source.types`, and its natural-number index is the ordinal of that +suffix's head. Consequently neither family order nor recursive-target +numbering is represented by an unchecked parallel list. -/ +inductive CheckedFamilies (source : VInductDecl) (params : List VExpr) : + Nat → List VInductiveType → Type where + | nil {ordinal : Nat} : CheckedFamilies source params ordinal [] + | cons {ordinal : Nat} {type : VInductiveType} + {types : List VInductiveType} + (head : CheckedFamily source params ordinal type) + (tail : CheckedFamilies source params (ordinal + 1) types) : + CheckedFamilies source params ordinal (type :: types) + +namespace CheckedFamilies + +/-- Erase only the dependent evidence, retaining exact family source order. -/ +def values {source : VInductDecl} {params : List VExpr} : + {ordinal : Nat} → {types : List VInductiveType} → + CheckedFamilies source params ordinal types → List VInductiveType + | _, _, .nil => [] + | _, _, .cons head tail => head.value :: values tail + +/-- Erasing a dependent family spine recovers its exact source-list index. -/ +@[simp] theorem values_eq {source : VInductDecl} {params : List VExpr} + {ordinal : Nat} {types : List VInductiveType} + (families : CheckedFamilies source params ordinal types) : + families.values = types := by + induction families with + | nil => rfl + | cons head tail ih => + simp only [values, CheckedFamily.value] + rw [ih] + +/-- Family ordinals in the same order as `values`. -/ +def ordinals {source : VInductDecl} {params : List VExpr} : + {ordinal : Nat} → {types : List VInductiveType} → + CheckedFamilies source params ordinal types → List Nat + | _, _, .nil => [] + | _, _, .cons head tail => head.ordinal :: ordinals tail + +/-- Per-family index telescopes in declaration order. -/ +def indices {source : VInductDecl} {params : List VExpr} : + {ordinal : Nat} → {types : List VInductiveType} → + CheckedFamilies source params ordinal types → List (List VExpr) + | _, _, .nil => [] + | _, _, .cons head tail => head.indices :: indices tail + +/-- Per-family result levels in declaration order. -/ +def resultLevels {source : VInductDecl} {params : List VExpr} : + {ordinal : Nat} → {types : List VInductiveType} → + CheckedFamilies source params ordinal types → List VLevel + | _, _, .nil => [] + | _, _, .cons head tail => head.resultLevel :: resultLevels tail + +/-- Ordered constructor descriptors, grouped by source family. -/ +def constructors {source : VInductDecl} {params : List VExpr} : + {ordinal : Nat} → {types : List VInductiveType} → + CheckedFamilies source params ordinal types → List (List CheckedCtor) + | _, _, .nil => [] + | _, _, .cons head tail => head.constructors :: constructors tail + +/-- Family names projected from the dependent source spine. -/ +def names {source : VInductDecl} {params : List VExpr} + {ordinal : Nat} {types : List VInductiveType} + (families : CheckedFamilies source params ordinal types) : List Name := + families.values.map (·.name) + +/-- Constructor names retain both family order and within-family order. -/ +def constructorNames {source : VInductDecl} {params : List VExpr} + {ordinal : Nat} {types : List VInductiveType} + (families : CheckedFamilies source params ordinal types) : + List (List Name) := + families.constructors.map fun constructors => + constructors.map (·.value.name) + +/-- Recursive target ordinals retain family, constructor, and field order. -/ +def recursiveTargets {source : VInductDecl} {params : List VExpr} + {ordinal : Nat} {types : List VInductiveType} + (families : CheckedFamilies source params ordinal types) : + List (List (List Nat)) := + families.constructors.map fun constructors => + constructors.map fun constructor => + constructor.recursive.map (·.targetType) + +end CheckedFamilies + +/-- Check one family while retaining every computed representation component. +This is a pure structural pass; no environment or generated constant is an +input. -/ +def checkedFamily? (source : VInductDecl) (params : List VExpr) + (ordinal : Nat) (type : VInductiveType) : + Option (CheckedFamily source params ordinal type) := + let indices := ctorFields (VExpr.dropN source.nparams type.type) + match hresult : VExpr.resultOf (VExpr.dropN source.nparams type.type) with + | .sort resultLevel => + if hparams : VExpr.telN source.nparams type.type = params then + if haccepted : blockFamilyCore source params ordinal type then + some { + params_eq := hparams + indices + indices_eq := rfl + resultLevel + result_eq := hresult + constructors := type.ctors.map (CheckedCtor.ofBlock source) + constructors_eq := rfl + accepted := haccepted } + else none + else none + | _ => none + +/-- Analyze an arbitrary source-ordered family list into the dependent spine. +The ordinal advances together with the list index. -/ +def checkedFamilies? (source : VInductDecl) (params : List VExpr) : + (ordinal : Nat) → (types : List VInductiveType) → + Option (CheckedFamilies source params ordinal types) + | _, [] => some .nil + | ordinal, type :: types => do + let head ← checkedFamily? source params ordinal type + let tail ← checkedFamilies? source params (ordinal + 1) types + return .cons head tail + +/-- Source-indexed checked representation of a complete inductive block. +Shared parameters are stored once; every per-family component lives in the +dependent `families` spine indexed by `source.types` itself. This is the +Spec-08A analysis boundary and intentionally has no generation or insertion +projection. -/ +structure CheckedBlock (source : VInductDecl) where + params : List VExpr + params_eq : params = blockParams source.nparams source.types + params_length : params.length = source.nparams + families : CheckedFamilies source params 0 source.types + nonempty : source.types.isEmpty = false + names : List Name + names_eq : names = blockGeneratedNames source.types + names_nodup : names.Nodup + +/-- Analyze a complete block without singleton destructuring. Empty blocks, +inconsistent raw parameter surfaces, malformed families/constructors, and +block-wide generated-name collisions are rejected before a descriptor is +returned. -/ +def checkedBlock? (source : VInductDecl) : Option source.CheckedBlock := + let params := blockParams source.nparams source.types + if hnonempty : source.types.isEmpty = false then + if hparams : params.length = source.nparams then + if hnames : (blockGeneratedNames source.types).Nodup then + match checkedFamilies? source params 0 source.types with + | some families => some { + params + params_eq := rfl + params_length := hparams + families + nonempty := hnonempty + names := blockGeneratedNames source.types + names_eq := rfl + names_nodup := hnames } + | none => none + else none + else none + else none + +/-- Analyze the normalized view of an arbitrary source block. The +`Normalization` index fixes every family and constructor header while the +dependent `CheckedBlock` fixes the complete normalized family order. -/ +def Normalization.checkedBlock? {source : VInductDecl} + (norm : Normalization source) : Option norm.view.CheckedBlock := + norm.view.checkedBlock? + +/-- One accepted raw/view mutual block. Unlike the legacy +`NormalizedChecked`, this type performs no singleton projection and exposes no +generation operation. -/ +structure NormalizedCheckedBlock (source : VInductDecl) where + normalization : Normalization source + checked : normalization.view.CheckedBlock + checked_eq : normalization.checkedBlock? = some checked + +/-- Analyze one normalization boundary and retain the exact dependent block +descriptor that accepted its view. -/ +def Normalization.checkBlock? {source : VInductDecl} + (norm : Normalization source) : Option (NormalizedCheckedBlock source) := + match hchecked : norm.checkedBlock? with + | some checked => some ⟨norm, checked, hchecked⟩ + | none => none + +/-- Construct and analyze a raw/view mutual normalization in one +computational transaction. -/ +def normalizedCheckedBlock? (source view : VInductDecl) : + Option (NormalizedCheckedBlock source) := do + let norm ← normalization? source view + norm.checkBlock? + +/-- Compatibility analyzer for a block already in analyzer normal form. -/ +def identityCheckedBlock? (source : VInductDecl) : + Option (NormalizedCheckedBlock source) := + (Normalization.identity source).checkBlock? + +/-- The validator-owned shared result universe paired with one exact checked +normalization view. Universe equality is semantic (`VLevel.Equiv`) and is +therefore certified by `ValidatedBlock.WF`, not guessed by the structural +analyzer. -/ +structure ValidatedBlock (source : VInductDecl) where + block : NormalizedCheckedBlock source + resultLevel : VLevel + +/-- Data-bearing compatibility result for the one-family generation path. +`CheckedBlock` and `ValidatedBlock` provide the public block-wide analysis and +validation; this legacy singleton projection remains available for existing +one-family certificates. The descriptor is dependent on its source +declaration, so it cannot silently describe a different block. -/ +structure Checked (source : VInductDecl) where + type : VInductiveType + types_eq : source.types = [type] + params : List VExpr + params_eq : params = VExpr.telN source.nparams type.type + indices : List VExpr + indices_eq : indices = ctorFields (VExpr.dropN source.nparams type.type) + resultLevel : VLevel + result_eq : VExpr.resultOf (VExpr.dropN source.nparams type.type) = .sort resultLevel + elimination : ElimMode + elimination_eq : elimination = + eliminationMode source.uvars type.name source.nparams indices.length type + kTarget : Bool + kTarget_eq : kTarget = isKTarget source.nparams resultLevel type + names : List Name + names_eq : names = generatedNames type + constructors : List CheckedCtor + constructors_eq : constructors = type.ctors.map + (CheckedCtor.ofDirect source.uvars type.name source.nparams indices.length) + accepted : stage3Core source = true + +/-- Analyze the current direct-indexed class once and retain every normalized +component needed downstream. This is intentionally an `Option` rather than a +boolean so successful checking cannot discard the analysis it just computed. -/ +def checked? : (decl : VInductDecl) → Option decl.Checked + | ⟨U, np, [ty]⟩ => + match hresult : VExpr.resultOf (VExpr.dropN np ty.type) with + | .sort l => + if h : stage3Core ⟨U, np, [ty]⟩ then + let indices := ctorFields (VExpr.dropN np ty.type) + some { + type := ty + types_eq := rfl + params := VExpr.telN np ty.type + params_eq := rfl + indices + indices_eq := rfl + resultLevel := l + result_eq := hresult + elimination := eliminationMode U ty.name np indices.length ty + elimination_eq := rfl + kTarget := isKTarget np l ty + kTarget_eq := rfl + names := generatedNames ty + names_eq := rfl + constructors := ty.ctors.map (CheckedCtor.ofDirect U ty.name np indices.length) + constructors_eq := rfl + accepted := h } + else none + | _ => none + | _ => none + +/-- A checked descriptor is proof-irrelevantly unique for its source +declaration. Every data field is fixed by the equations stored in `Checked`; +the remaining fields are propositions. -/ +theorem Checked.unique {decl : VInductDecl} + (a b : decl.Checked) : a = b := by + have htype : a.type = b.type := by + have hb := b.types_eq + rw [a.types_eq] at hb + injection hb + have hparams : a.params = b.params := by + rw [a.params_eq, b.params_eq, htype] + have hindices : a.indices = b.indices := by + rw [a.indices_eq, b.indices_eq, htype] + have hlevel : a.resultLevel = b.resultLevel := by + have hb := b.result_eq + rw [← htype, a.result_eq] at hb + injection hb + have helim : a.elimination = b.elimination := by + rw [a.elimination_eq, b.elimination_eq, htype, hindices] + have hkTarget : a.kTarget = b.kTarget := by + rw [a.kTarget_eq, b.kTarget_eq, htype, hlevel] + have hnames : a.names = b.names := by + rw [a.names_eq, b.names_eq, htype] + have hctors : a.constructors = b.constructors := by + rw [a.constructors_eq, b.constructors_eq, htype, hindices] + cases a + cases b + simp_all + +/-- Possessing a checked descriptor proves that the analyzer succeeds. This +is kept separate from uniqueness because reducing the dependent match in +`checked?` only needs its Boolean success result. -/ +theorem Checked.analyzer_isSome {decl : VInductDecl} + (checked : decl.Checked) : decl.checked?.isSome = true := by + cases decl with + | mk U np types => + rcases checked with + ⟨type, types_eq, params, params_eq, + indices, indices_eq, resultLevel, result_eq, + elimination, elimination_eq, kTarget, kTarget_eq, names, names_eq, + constructors, constructors_eq, accepted⟩ + change types = [type] at types_eq + subst types + subst params + subst indices + subst kTarget + subst names + subst constructors + change (VExpr.dropN np type.type).resultOf = + .sort resultLevel at result_eq + simp only [VInductDecl.checked?] + split <;> simp_all + +/-- Every `Checked` value is the exact data-bearing result returned by the +analyzer. Consumers can therefore package an already-retained witness without +running or choosing a second analysis. -/ +theorem Checked.analyzer_eq {decl : VInductDecl} + (checked : decl.Checked) : decl.checked? = some checked := by + cases h : decl.checked? with + | none => + have his := checked.analyzer_isSome + simp [h] at his + | some actual => + exact congrArg some (actual.unique checked) + +/-- The layout fragment of `stage3DirectCore` needed to pair an identity +normalization position-for-position. -/ +theorem Checked.direct_layout {decl : VInductDecl} + (checked : decl.Checked) : + (VExpr.telN decl.nparams checked.type.type).length = decl.nparams ∧ + ∀ c ∈ checked.type.ctors, + (VExpr.telN decl.nparams c.type).length = decl.nparams := by + cases decl with + | mk U np types => + have htypes : types = [checked.type] := checked.types_eq + have hcore := checked.accepted + simp only [stage3Core, htypes, Bool.and_eq_true] at hcore + have hdirect := hcore.1 + simp only [stage3DirectCore, Bool.and_eq_true, beq_iff_eq, + List.all_eq_true] at hdirect + obtain ⟨⟨⟨⟨-, hparams⟩, -⟩, -⟩, hctors⟩ := hdirect + refine ⟨hparams, fun c hc => ?_⟩ + rw [(hctors c hc).1.2] + exact hparams + +/-- Analyze an explicit normalized view without consulting the environment. +Semantic soundness is supplied separately by `Normalization.WF`; this split +matches the existing `Checked`/`Checked.WF` contract. -/ +def Normalization.checked? {source : VInductDecl} (norm : Normalization source) : + Option norm.view.Checked := + norm.view.checked? + +/-- Acceptance of an explicit normalized view. -/ +def Normalization.accepted {source : VInductDecl} (norm : Normalization source) : Bool := + norm.checked?.isSome + +/-- One accepted raw/view pair. The raw declaration remains the index of this +object; every WHNF-sensitive decision is retained in the single checked view +that produced `checked_eq`. -/ +structure NormalizedChecked (source : VInductDecl) where + normalization : Normalization source + sourceType : VInductiveType + source_types_eq : source.types = [sourceType] + checked : normalization.view.Checked + checked_eq : normalization.checked? = some checked + +/-- Analyze a normalization once and retain the exact dependent checked +descriptor that drove acceptance. -/ +def Normalization.check? {source : VInductDecl} (norm : Normalization source) : + Option (NormalizedChecked source) := + match hsource : source.types with + | [sourceType] => + match hchecked : norm.checked? with + | some checked => some ⟨norm, sourceType, hsource, checked, hchecked⟩ + | none => none + | _ => none + +/-- Construct and analyze a raw/view pair in one computational transaction. -/ +def normalizedChecked? (source view : VInductDecl) : Option (NormalizedChecked source) := do + let norm ← normalization? source view + norm.check? + +/-- Compatibility analyzer for metadata already accepted in raw syntax. -/ +def identityChecked? (source : VInductDecl) : Option (NormalizedChecked source) := + (Normalization.identity source).check? + +/-- Repackage an already-retained analyzer witness through the identity +normalization boundary. This is the compatibility block used by the public +`Checked` artifact accessors. -/ +def Checked.identityBlock {decl : VInductDecl} + (checked : decl.Checked) : NormalizedChecked decl where + normalization := Normalization.identity decl + sourceType := checked.type + source_types_eq := checked.types_eq + checked := checked + checked_eq := checked.analyzer_eq + +/-- One raw constructor paired, by position, with the checked descriptor from +the normalized view. The raw value owns kernel-observable binder syntax; +`view` owns WHNF-sensitive recursive classification and result indices. -/ +structure NormalizedCtor where + raw : VConstVal + view : CheckedCtor + +/-- Positional pairing used by the mixed artifact layer. A separate layout +check below rejects unequal list lengths instead of relying on `zip`'s +truncation behavior. -/ +def pairNormalizedCtors : List VConstVal → List CheckedCtor → List NormalizedCtor + | raw :: raws, view :: views => + ⟨raw, view⟩ :: pairNormalizedCtors raws views + | _, _ => [] + +/-- Raw constructor fields, kept distinct from the normalized fields stored in +the checked view. -/ +def NormalizedCtor.rawFields (ctor : NormalizedCtor) (np : Nat) : List VExpr := + ctorFields (VExpr.dropN np ctor.raw.type) + +/-- All stored raw constructor binders, including the constructor's own +parameter prefix. -/ +def NormalizedCtor.declaredBinders (ctor : NormalizedCtor) (np : Nat) : + List VExpr := + VExpr.telN np ctor.raw.type ++ ctor.rawFields np + +/-- The result retained in the stored raw constructor type. -/ +def NormalizedCtor.rawResult (ctor : NormalizedCtor) (np : Nat) : VExpr := + VExpr.resultOf (VExpr.dropN np ctor.raw.type) + +/-- The structural facts required before raw binders and normalized recursive +facts may drive one artifact. Semantic defeq remains the responsibility of +`Normalization.WF`; this Boolean only certifies positional layout. -/ +def NormalizedCtor.generationShape (np : Nat) (ctor : NormalizedCtor) : Bool := + ctor.raw.name == ctor.view.value.name && + ctor.raw.uvars == ctor.view.value.uvars && + (VExpr.telN np ctor.raw.type).length == np && + (ctor.rawFields np).length == ctor.view.fields.length + +def NormalizedChecked.rawParams {source : VInductDecl} + (block : NormalizedChecked source) : List VExpr := + VExpr.telN source.nparams block.sourceType.type + +def NormalizedChecked.rawIndices {source : VInductDecl} + (block : NormalizedChecked source) : List VExpr := + ctorFields (VExpr.dropN source.nparams block.sourceType.type) + +/-- The raw family result after its parameter and index binders. -/ +def NormalizedChecked.rawResult {source : VInductDecl} + (block : NormalizedChecked source) : VExpr := + VExpr.resultOf (VExpr.dropN source.nparams block.sourceType.type) + +/-- Whether a stored parameter domain has one of the four top-level type +annotations consumed by Lean before it creates the common recursor parameter +telescope. Ordinary reducible constants are intentionally not inspected: +their raw syntax remains observable in generated kernel metadata. -/ +def _root_.Ix.Theory.Named.VExpr.hasTypeAnnotation : VExpr → Bool + | .app (.const name _) _ => + name == ``_root_.outParam || name == ``_root_.semiOutParam + | .app (.app (.const name _) _) _ => + name == ``_root_.optParam || name == ``_root_.autoParam + | _ => false + +/-- Parameter surface emitted by recursor generation. Annotation wrappers use +the analyzer-owned consumed view; every other parameter keeps the stored raw +domain even when its WHNF differs. -/ +def generationParam (raw view : VExpr) : VExpr := + if raw.hasTypeAnnotation then view else raw + +/-- Positional common-parameter telescope used by generated artifacts. -/ +def generationParams : List VExpr → List VExpr → List VExpr + | raw :: raws, view :: views => + generationParam raw view :: generationParams raws views + | _, _ => [] + +@[simp] theorem generationParams_self : + ∀ params, generationParams params params = params + | [] => rfl + | param :: params => by + simp [generationParams, generationParam, generationParams_self params] + +theorem generationParams_length_of_eq : + ∀ {raw view : List VExpr}, raw.length = view.length → + (generationParams raw view).length = raw.length + | [], [], _ => rfl + | _ :: _, _ :: _, h => by + simp only [generationParams, List.length_cons] + exact congrArg Nat.succ + (generationParams_length_of_eq (Nat.succ.inj h)) + +def NormalizedChecked.generationParams {source : VInductDecl} + (block : NormalizedChecked source) : List VExpr := + VInductDecl.generationParams block.rawParams block.checked.params + +def NormalizedChecked.ctorPairs {source : VInductDecl} + (block : NormalizedChecked source) : List NormalizedCtor := + pairNormalizedCtors block.sourceType.ctors block.checked.constructors + +/-- Executable layout gate for mixed raw/view generation. It proves that every +raw binder position has exactly one normalized analysis position without +claiming that their expression payloads are syntactically equal. -/ +def NormalizedChecked.generationShape {source : VInductDecl} + (block : NormalizedChecked source) : Bool := + block.rawParams.length == source.nparams && + block.rawParams.length == block.checked.params.length && + block.rawIndices.length == block.checked.indices.length && + block.ctorPairs.length == block.sourceType.ctors.length && + block.ctorPairs.length == block.checked.constructors.length && + block.ctorPairs.all (NormalizedCtor.generationShape source.nparams) + +/-- Pairing a constructor list with its direct checked analysis preserves its +length and satisfies every per-constructor identity-layout check. -/ +theorem pairNormalizedCtors_ofDirect_generationShape + (U : Nat) (T : Name) (np ni : Nat) : + ∀ (cs : List VConstVal), + (∀ c ∈ cs, (VExpr.telN np c.type).length = np) → + let pairs := pairNormalizedCtors cs + (cs.map (CheckedCtor.ofDirect U T np ni)) + pairs.length = cs.length ∧ + pairs.all (NormalizedCtor.generationShape np) = true + | [], _ => by simp [pairNormalizedCtors] + | c :: cs, h => by + have hc := h c (.head _) + have hcs : ∀ c ∈ cs, (VExpr.telN np c.type).length = np := + fun c hc => h c (.tail _ hc) + have ih := pairNormalizedCtors_ofDirect_generationShape + U T np ni cs hcs + simp [pairNormalizedCtors, NormalizedCtor.generationShape, + CheckedCtor.ofDirect, NormalizedCtor.rawFields, hc, ih] + +/-- A normalized checked block whose raw outer telescopes are positionally +usable by artifact generation. This is still analysis data, not semantic +normalization evidence; preservation additionally requires +`NormalizedChecked.WF`. -/ +structure GenerationChecked (source : VInductDecl) where + block : NormalizedChecked source + shape_eq : block.generationShape = true + +/-- Identity normalization always satisfies the mixed generator's positional +layout gate. The only non-reflexive facts are the parameter and constructor +parameter lengths already certified by Stage 3. -/ +theorem Checked.identityBlock_generationShape {decl : VInductDecl} + (checked : decl.Checked) : + checked.identityBlock.generationShape = true := by + have hp : (VExpr.telN decl.nparams checked.type.type).length = + decl.nparams := checked.direct_layout.1 + have hcs : ∀ c ∈ checked.type.ctors, + (VExpr.telN decl.nparams c.type).length = decl.nparams := + checked.direct_layout.2 + have hpairs := pairNormalizedCtors_ofDirect_generationShape + decl.uvars checked.type.name decl.nparams checked.indices.length + checked.type.ctors hcs + simp only [NormalizedChecked.generationShape, Bool.and_eq_true, + beq_iff_eq, Checked.identityBlock, NormalizedChecked.rawParams, + NormalizedChecked.rawIndices, NormalizedChecked.ctorPairs] + rw [checked.params_eq, checked.indices_eq, checked.constructors_eq] + simp only [List.length_map] + exact ⟨⟨⟨⟨⟨hp, trivial⟩, trivial⟩, hpairs.1⟩, + hpairs.1⟩, hpairs.2⟩ + +/-- Canonical mixed-generation witness for a declaration already accepted in +raw analyzer normal form. -/ +def Checked.identityGeneration {decl : VInductDecl} + (checked : decl.Checked) : GenerationChecked decl where + block := checked.identityBlock + shape_eq := checked.identityBlock_generationShape + +@[simp] theorem Checked.identityGeneration_sourceType + {decl : VInductDecl} (checked : decl.Checked) : + checked.identityGeneration.block.sourceType = checked.type := rfl + +@[simp] theorem Checked.identityGeneration_checked + {decl : VInductDecl} (checked : decl.Checked) : + checked.identityGeneration.block.checked = checked := rfl + +def NormalizedChecked.generation? {source : VInductDecl} + (block : NormalizedChecked source) : Option (GenerationChecked source) := + if h : block.generationShape then some ⟨block, h⟩ else none + +def Normalization.generation? {source : VInductDecl} + (norm : Normalization source) : Option (GenerationChecked source) := do + let block ← norm.check? + block.generation? + +def normalizedGeneration? (source view : VInductDecl) : + Option (GenerationChecked source) := do + let block ← normalizedChecked? source view + block.generation? + +def identityGeneration? (source : VInductDecl) : + Option (GenerationChecked source) := do + let block ← identityChecked? source + block.generation? + +/-- Compatibility acceptance predicate for the legacy one-family checked +descriptor. The public block-wide predicate is `stage3`, defined once the +mutual generation descriptor is available below. -/ +def singletonStage3 (decl : VInductDecl) : Bool := decl.checked?.isSome + +/-- The parameter telescope in the recursor's universe context. -/ +def paramsTel (ty : VInductiveType) (mode : ElimMode := .large) : List VExpr := + (VExpr.telN np ty.type).map (VExpr.instL (mode.sourceLevels U)) + +/-- The index telescope in the recursor's universe context (at parameter +depth). -/ +def idxTel (ty : VInductiveType) (mode : ElimMode := .large) : List VExpr := + (ctorFields (VExpr.dropN np ty.type)).map + (VExpr.instL (mode.sourceLevels U)) + +/-- `recApp` in the recursor's universe context. -/ +def recApp' (off : Nat) (mode : ElimMode := .large) : VExpr := + VExpr.appN (.const T (mode.sourceLevels U)) (VExpr.bvarRevRange off np) + +/-- `motive : ∀ indices, T params indices → Sort u`, in context +`params`. -/ +def motiveType (ty : VInductiveType) (mode : ElimMode := .large) : VExpr := + let ni := (idxTel U np ty mode).length + VExpr.forallN (idxTel U np ty mode) + (.forallE (VExpr.appN (.const T (mode.sourceLevels U)) + (VExpr.bvarRevRange ni np ++ VExpr.bvarRevRange 0 ni)) + (.sort mode.motiveLevel)) + +/-- Constructor fields in the recursor's universe context (still at +parameter depth, no motive shift). -/ +def ctorFieldsR (c : VConstVal) (mode : ElimMode := .large) : List VExpr := + (ctorFields (VExpr.dropN np c.type)).map + (VExpr.instL (mode.sourceLevels U)) + +/-- The recursive positions of a constructor with their index arguments, +in the recursor's universe context. -/ +def recPairsR (ni : Nat) (c : VConstVal) + (mode : ElimMode := .large) : List (Nat × List VExpr) := + (recPairs U T np ni (ctorFields (VExpr.dropN np c.type))).map + fun (j, idxs) => (j, idxs.map (VExpr.instL (mode.sourceLevels U))) + +/-- A recursive-argument descriptor transported into the recursor's universe +context. Telescope dependency is preserved because universe instantiation does +not change term-variable indices. -/ +def RecArg.instL (r : RecArg) (ls : List VLevel) : RecArg where + fieldIndex := r.fieldIndex + binders := r.binders.map (VExpr.instL ls) + targetType := r.targetType + indices := r.indices.map (VExpr.instL ls) + +/-- Recursive constructor arguments, including recursive Pi arguments, in the +recursor universe context. -/ +def recArgsR (ni : Nat) (c : VConstVal) + (mode : ElimMode := .large) : List RecArg := + (recArgs U T np ni (ctorFields (VExpr.dropN np c.type))).map + fun r => r.instL (mode.sourceLevels U) + +/-- The result index arguments of a constructor, recursor universes (at +result depth: past the parameters and all fields, no motive). -/ +def ctorIdxs (c : VConstVal) (mode : ElimMode := .large) : List VExpr := + (recFieldIdxs np (VExpr.resultOf (VExpr.dropN np c.type))).map + (VExpr.instL (mode.sourceLevels U)) + +/-- The minor premise for one constructor, in context `params ++ [motive]`: +`∀ fields, ∀ ihs, motive idxs (ctor params fields)`, with one induction +hypothesis per directly recursive field. The fields shift by one for the +interposed motive. -/ +def minorType (ty : VInductiveType) (c : VConstVal) + (mode : ElimMode := .large) : VExpr := + let Bs := ctorFieldsR U np c mode + let m := Bs.length + let ni := (idxTel U np ty mode).length + let rsP := recPairsR U T np ni c mode + let r := rsP.length + VExpr.forallN (VExpr.liftTelN 1 Bs 0) + (VExpr.forallN (ihsFrom m rsP 0) + (VExpr.appN (.bvar (m+r)) + (((ctorIdxs U np c mode).map fun e => (e.liftN 1 m).liftN r) ++ + [VExpr.appN (.const c.name (mode.sourceLevels U)) + (VExpr.bvarRevRange (r+m+1) np ++ VExpr.bvarRevRange r m)]))) + +/-- Binder telescope of a functional induction hypothesis. Starting from the +recursive argument's own telescope, insert the motive below the preceding +constructor fields, then insert the current/later fields and earlier induction +hypotheses above them. -/ +def RecArg.minorBinders (m p : Nat) (r : RecArg) : List VExpr := + VExpr.liftTelN (m - r.fieldIndex + p) + (VExpr.liftTelN 1 r.binders r.fieldIndex) 0 + +/-- The induction-hypothesis type generated for one recursive argument. For a +direct recursive field this reduces to `motive indices field`; under a Pi it +is `∀ xs, motive indices (field xs)`. -/ +def RecArg.minorIH (m p : Nat) (r : RecArg) : VExpr := + let n := r.binders.length + VExpr.forallN (r.minorBinders m p) + (VExpr.appN (.bvar (m+p+n)) + ((r.indices.map fun e => + (e.liftN 1 (r.fieldIndex+n)).liftN (m-r.fieldIndex+p) n) ++ + [VExpr.appN (.bvar (m-1-r.fieldIndex+p+n)) + (VExpr.bvarRevRange 0 n)])) + +/-- Functional induction-hypothesis telescope, one entry per recursive +constructor argument. -/ +def ihsFromRecArgs (m : Nat) : List RecArg → Nat → List VExpr + | [], _ => [] + | r :: rs, p => r.minorIH m p :: ihsFromRecArgs m rs (p+1) + +/-- General one-family minor premise, extending `minorType` to recursive +arguments beneath Pi telescopes. -/ +def minorTypeRec (ty : VInductiveType) (c : VConstVal) + (mode : ElimMode := .large) : VExpr := + let Bs := ctorFieldsR U np c mode + let m := Bs.length + let ni := (idxTel U np ty mode).length + let rs := recArgsR U T np ni c mode + let r := rs.length + VExpr.forallN (VExpr.liftTelN 1 Bs 0) + (VExpr.forallN (ihsFromRecArgs m rs 0) + (VExpr.appN (.bvar (m+r)) + (((ctorIdxs U np c mode).map fun e => (e.liftN 1 m).liftN r) ++ + [VExpr.appN (.const c.name (mode.sourceLevels U)) + (VExpr.bvarRevRange (r+m+1) np ++ VExpr.bvarRevRange r m)]))) + +def minorTypesRec (ty : VInductiveType) : List VConstVal → + (i : Nat := 0) → (mode : ElimMode := .large) → List VExpr + | [], _, _ => [] + | c :: cs, i, mode => + VExpr.liftN i (minorTypeRec U T np ty c mode) :: + minorTypesRec ty cs (i+1) mode + +def recTypeRec (ty : VInductiveType) + (mode : ElimMode := .large) : VExpr := + let k := ty.ctors.length + let ni := (idxTel U np ty mode).length + VExpr.forallN (paramsTel U np ty mode) <| + .forallE (motiveType U T np ty mode) <| + VExpr.forallN (minorTypesRec U T np ty ty.ctors (mode := mode)) <| + VExpr.forallN (VExpr.liftTelN (k+1) (idxTel U np ty mode) 0) <| + .forallE (VExpr.appN (.const T (mode.sourceLevels U)) + (VExpr.bvarRevRange (ni+k+1) np ++ VExpr.bvarRevRange 0 ni)) <| + .app (VExpr.appN (.bvar (ni+k+1)) (VExpr.bvarRevRange 1 ni)) (.bvar 0) + +def recConstRec (ty : VInductiveType) + (mode : ElimMode := .large) : VConstant := + ⟨mode.recUvars U, recTypeRec U T np ty mode⟩ + +/-- The telescope introduced around a functional recursive call in an iota +RHS. It is the source Pi telescope transported under the recursor's common +arguments and the complete constructor-field context. -/ +def RecArg.ruleBinders (m k : Nat) (r : RecArg) : List VExpr := + VExpr.liftTelN (m-r.fieldIndex) + (VExpr.liftTelN (k+1) r.binders r.fieldIndex) 0 + +/-- Recursive call contributed to an iota RHS. Direct recursion reduces to a +plain call; a recursive Pi argument produces a lambda applying the field to +the freshly bound arguments before recurring. -/ +def RecArg.ruleCall (m k : Nat) (recBase : VExpr) (r : RecArg) : VExpr := + let n := r.binders.length + VExpr.lamN (r.ruleBinders m k) + (VExpr.appN (recBase.liftN n) + ((r.indices.map fun e => + (e.liftN (k+1) (r.fieldIndex+n)).liftN (m-r.fieldIndex) n) ++ + [VExpr.appN (.bvar (m-1-r.fieldIndex+n)) + (VExpr.bvarRevRange 0 n)])) + +/-- The functional-IH type expected of `ruleCall` in the complete iota-rule +context. Keeping this normalization named makes the preservation proof and +the generated RHS share one de Bruijn convention. -/ +def RecArg.ruleIH (m k : Nat) (r : RecArg) : VExpr := + let n := r.binders.length + VExpr.forallN (r.ruleBinders m k) + (VExpr.appN (.bvar (k+m+n)) + ((r.indices.map fun e => + (e.liftN (k+1) (r.fieldIndex+n)).liftN (m-r.fieldIndex) n) ++ + [VExpr.appN (.bvar (m-1-r.fieldIndex+n)) + (VExpr.bvarRevRange 0 n)])) + +/-- Functional-IH telescope in an iota-rule context. Each entry is weakened +past the preceding IH binders, while its base type is exactly `ruleIH`. -/ +def ruleIHs (m k : Nat) : List RecArg → Nat → List VExpr + | [], _ => [] + | r :: rs, p => (r.ruleIH m k).liftN p :: ruleIHs m k rs (p+1) + +def ruleRec (ty : VInductiveType) (i : Nat) (c : VConstVal) + (mode : ElimMode := .large) : VDefEq := + let k := ty.ctors.length + let Bs := ctorFieldsR U np c mode + let m := Bs.length + let ni := (idxTel U np ty mode).length + let rs := recArgsR U T np ni c mode + let binders := paramsTel U np ty mode ++ + motiveType U T np ty mode :: + minorTypesRec U T np ty ty.ctors (mode := mode) ++ + VExpr.liftTelN (k+1) Bs 0 + let recBase := VExpr.appN (.const (.str T "rec") (mode.recLevels U)) + (VExpr.bvarRevRange m (np+k+1)) + let idxR := (ctorIdxs U np c mode).map fun e => e.liftN (k+1) m + let ctorApp := VExpr.appN (.const c.name (mode.sourceLevels U)) + (VExpr.bvarRevRange (m+k+1) np ++ VExpr.bvarRevRange 0 m) + let ihs := rs.map fun r => r.ruleCall m k recBase + { uvars := mode.recUvars U + lhs := VExpr.lamN binders (VExpr.appN recBase (idxR ++ [ctorApp])) + rhs := VExpr.lamN binders + (VExpr.appN (.bvar (k-1-i+m)) (VExpr.bvarRevRange 0 m ++ ihs)) + type := VExpr.forallN binders (VExpr.appN (.bvar (k+m)) (idxR ++ [ctorApp])) } + +def rulesRec (ty : VInductiveType) + (mode : ElimMode := .large) : List VDefEq := + ty.ctors.zipIdx.map fun (c, i) => ruleRec U T np ty i c mode + +/-- Minor premise types in position: the `i`-th lives under +`params ++ motive` and the previous `i` minors. -/ +def minorTypes (ty : VInductiveType) : List VConstVal → (i : Nat := 0) → List VExpr + | [], _ => [] + | c :: cs, i => VExpr.liftN i (minorType U T np ty c) :: minorTypes ty cs (i+1) + +/-- The recursor type `∀ params, ∀ (motive : ∀ indices, T params indices → +Sort u) (minors..) (indices..) (t : T params indices), motive indices t`. -/ +def recType (ty : VInductiveType) : VExpr := + let k := ty.ctors.length + let ni := (idxTel U np ty).length + VExpr.forallN (paramsTel U np ty) <| + .forallE (motiveType U T np ty) <| + VExpr.forallN (minorTypes U T np ty ty.ctors) <| + VExpr.forallN (VExpr.liftTelN (k+1) (idxTel U np ty) 0) <| + .forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (ni+k+1) np ++ VExpr.bvarRevRange 0 ni)) <| + .app (VExpr.appN (.bvar (ni+k+1)) (VExpr.bvarRevRange 1 ni)) (.bvar 0) + +def recConst (ty : VInductiveType) : VConstant := ⟨U + 1, recType U T np ty⟩ + +/-- The iota rule for the `i`-th constructor, as a closed defeq between +lambda telescopes over `params ++ motive :: minors ++ fields` (the same +shape as `quotDefEq`, and the same telescope the kernel's +`RecursorRule.rhs` binds). The left body is the `SimplePattern.iota` +spine: the recursor applied to the parameters, motive, minors and the +constructor's result indices, with a constructor-headed major. -/ +def rule (ty : VInductiveType) (i : Nat) (c : VConstVal) : VDefEq := + let k := ty.ctors.length + let Bs := ctorFieldsR U np c + let m := Bs.length + let ni := (idxTel U np ty).length + let rsP := recPairsR U T np ni c + let binders := paramsTel U np ty ++ + motiveType U T np ty :: minorTypes U T np ty ty.ctors ++ + VExpr.liftTelN (k+1) Bs 0 + let recBase := VExpr.appN (.const (.str T "rec") (VLevel.params (U+1))) + (VExpr.bvarRevRange m (np+k+1)) + let idxR := (ctorIdxs U np c).map fun e => e.liftN (k+1) m + let ctorApp := VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange (m+k+1) np ++ VExpr.bvarRevRange 0 m) + let ihs := rsP.map fun (j, idxs) => + VExpr.appN recBase + ((idxs.map fun e => (e.liftN (k+1) j).liftN (m-j)) ++ [.bvar (m-1-j)]) + { uvars := U + 1 + lhs := VExpr.lamN binders (VExpr.appN recBase (idxR ++ [ctorApp])) + rhs := VExpr.lamN binders + (VExpr.appN (.bvar (k-1-i+m)) (VExpr.bvarRevRange 0 m ++ ihs)) + type := VExpr.forallN binders (VExpr.appN (.bvar (k+m)) (idxR ++ [ctorApp])) } + +def rules (ty : VInductiveType) : List VDefEq := + ty.ctors.zipIdx.map fun (c, i) => rule U T np ty i c + +namespace NormalizedCtor + +/-- Raw field binders transported to the recursor universe context. -/ +def fieldsR (ctor : NormalizedCtor) (U np : Nat) + (mode : ElimMode := .large) : List VExpr := + (ctor.rawFields np).map (VExpr.instL (mode.sourceLevels U)) + +/-- Normalized recursive classifications transported without re-peeling the +raw constructor type. -/ +def recArgsR (ctor : NormalizedCtor) (U : Nat) + (mode : ElimMode := .large) : List RecArg := + ctor.view.recursive.map fun r => r.instL (mode.sourceLevels U) + +/-- Normalized constructor-result indices in recursor universes. -/ +def resultIndicesR (ctor : NormalizedCtor) (U : Nat) + (mode : ElimMode := .large) : List VExpr := + ctor.view.resultIndices.map (VExpr.instL (mode.sourceLevels U)) + +end NormalizedCtor + +/-! ## Mixed raw/view artifacts + +These definitions are additive until their preservation theorem is complete. +Raw family/constructor telescopes supply emitted binder syntax; the checked +view supplies result indices and recursive-argument classification. No helper +below re-runs `recArg?` on raw metadata. +-/ + +namespace GenerationChecked + +/-- Elimination mode retained by the single checked analysis. -/ +abbrev elimination {source : VInductDecl} (gen : GenerationChecked source) : + ElimMode := + gen.block.checked.elimination + +/-- K-like reduction flag retained by the single checked analysis. It is +metadata for reduction and does not select the elimination mode. -/ +abbrev kTarget {source : VInductDecl} (gen : GenerationChecked source) : Bool := + gen.block.checked.kTarget + +/-- Universe arity of this generated recursor. -/ +abbrev recUvars {source : VInductDecl} (gen : GenerationChecked source) : Nat := + gen.elimination.recUvars source.uvars + +/-- Declaration universes in this recursor's universe context. -/ +abbrev sourceLevels {source : VInductDecl} + (gen : GenerationChecked source) : List VLevel := + gen.elimination.sourceLevels source.uvars + +/-- Result level of the motive. -/ +abbrev motiveLevel {source : VInductDecl} + (gen : GenerationChecked source) : VLevel := + gen.elimination.motiveLevel + +/-- Identity universe arguments of recursive recursor calls. -/ +abbrev recLevels {source : VInductDecl} + (gen : GenerationChecked source) : List VLevel := + gen.elimination.recLevels source.uvars + +/-- Kernel-observable family-parameter telescope in recursor universes. + +Lean consumes the four parameter annotations before emission, but otherwise +retains stored syntax rather than replacing reducible aliases by their WHNF. -/ +def paramsTel {source : VInductDecl} (gen : GenerationChecked source) : + List VExpr := + gen.block.generationParams.map + (VExpr.instL gen.sourceLevels) + +/-- Raw index-binder telescope in recursor universes. -/ +def idxTel {source : VInductDecl} (gen : GenerationChecked source) : + List VExpr := + gen.block.rawIndices.map (VExpr.instL gen.sourceLevels) + +/-- Mixed motive: raw index binders, normalized index arity, and the retained +raw family identity. -/ +def motiveType {source : VInductDecl} (gen : GenerationChecked source) : VExpr := + let ni := gen.idxTel.length + VExpr.forallN gen.idxTel + (.forallE + (VExpr.appN + (.const gen.block.sourceType.name gen.sourceLevels) + (VExpr.bvarRevRange ni source.nparams ++ VExpr.bvarRevRange 0 ni)) + (.sort gen.motiveLevel)) + +/-- One mixed minor premise. Raw fields are bound verbatim, while induction +hypotheses and constructor result indices come from the checked view. -/ +def minorType {source : VInductDecl} (ctor : NormalizedCtor) + (mode : ElimMode := .large) : VExpr := + let Bs := ctor.fieldsR source.uvars source.nparams mode + let m := Bs.length + let rs := ctor.recArgsR source.uvars mode + let r := rs.length + VExpr.forallN (VExpr.liftTelN 1 Bs 0) + (VExpr.forallN (ihsFromRecArgs m rs 0) + (VExpr.appN (.bvar (m+r)) + ((ctor.resultIndicesR source.uvars mode |>.map fun e => + (e.liftN 1 m).liftN r) ++ + [VExpr.appN + (.const ctor.raw.name (mode.sourceLevels source.uvars)) + (VExpr.bvarRevRange (r+m+1) source.nparams ++ + VExpr.bvarRevRange r m)]))) + +def minorTypesAux {source : VInductDecl} (gen : GenerationChecked source) : + List NormalizedCtor → (i : Nat := 0) → List VExpr + | [], _ => [] + | ctor :: ctors, i => + VExpr.liftN i (minorType (source := source) ctor gen.elimination) :: + gen.minorTypesAux ctors (i+1) + +def minorTypes {source : VInductDecl} (gen : GenerationChecked source) : + List VExpr := + gen.minorTypesAux gen.block.ctorPairs + +/-- Mixed recursor type. Its binder surface is raw, but every +WHNF-sensitive choice was retained by the checked view. -/ +def recType {source : VInductDecl} (gen : GenerationChecked source) : VExpr := + let k := gen.block.ctorPairs.length + let ni := gen.idxTel.length + VExpr.forallN gen.paramsTel <| + .forallE gen.motiveType <| + VExpr.forallN gen.minorTypes <| + VExpr.forallN (VExpr.liftTelN (k+1) gen.idxTel 0) <| + .forallE + (VExpr.appN + (.const gen.block.sourceType.name + gen.sourceLevels) + (VExpr.bvarRevRange (ni+k+1) source.nparams ++ + VExpr.bvarRevRange 0 ni)) <| + .app + (VExpr.appN (.bvar (ni+k+1)) (VExpr.bvarRevRange 1 ni)) + (.bvar 0) + +def recursor {source : VInductDecl} (gen : GenerationChecked source) : + VConstant := + ⟨gen.recUvars, gen.recType⟩ + +/-- One mixed iota rule. Raw fields and constructor names are observable in +the rule telescope; normalized recursive descriptors determine recursive calls +and normalized result indices determine the major's index spine. -/ +def rule {source : VInductDecl} (gen : GenerationChecked source) + (i : Nat) (ctor : NormalizedCtor) : VDefEq := + let k := gen.block.ctorPairs.length + let Bs := ctor.fieldsR source.uvars source.nparams gen.elimination + let m := Bs.length + let rs := ctor.recArgsR source.uvars gen.elimination + let binders := gen.paramsTel ++ + gen.motiveType :: gen.minorTypes ++ + VExpr.liftTelN (k+1) Bs 0 + let recBase := VExpr.appN + (.const (.str gen.block.sourceType.name "rec") + gen.recLevels) + (VExpr.bvarRevRange m (source.nparams+k+1)) + let idxR := ctor.resultIndicesR source.uvars gen.elimination |>.map fun e => + e.liftN (k+1) m + let ctorApp := VExpr.appN + (.const ctor.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (m+k+1) source.nparams ++ + VExpr.bvarRevRange 0 m) + let ihs := rs.map fun r => r.ruleCall m k recBase + { uvars := gen.recUvars + lhs := VExpr.lamN binders (VExpr.appN recBase (idxR ++ [ctorApp])) + rhs := VExpr.lamN binders + (VExpr.appN (.bvar (k-1-i+m)) (VExpr.bvarRevRange 0 m ++ ihs)) + type := VExpr.forallN binders + (VExpr.appN (.bvar (k+m)) (idxR ++ [ctorApp])) } + +def generatedRules {source : VInductDecl} (gen : GenerationChecked source) : + List VDefEq := + gen.block.ctorPairs.zipIdx.map fun (ctor, i) => gen.rule i ctor + +end GenerationChecked + +namespace Checked + +@[simp] theorem identityGeneration_elimination {decl : VInductDecl} + (checked : decl.Checked) : + checked.identityGeneration.elimination = checked.elimination := rfl + +@[simp] theorem identityGeneration_kTarget {decl : VInductDecl} + (checked : decl.Checked) : + checked.identityGeneration.kTarget = checked.kTarget := rfl + +@[simp] theorem identityGeneration_recUvars {decl : VInductDecl} + (checked : decl.Checked) : + checked.identityGeneration.recUvars = + checked.elimination.recUvars decl.uvars := rfl + +@[simp] theorem identityGeneration_sourceLevels {decl : VInductDecl} + (checked : decl.Checked) : + checked.identityGeneration.sourceLevels = + checked.elimination.sourceLevels decl.uvars := rfl + +@[simp] theorem identityGeneration_motiveLevel {decl : VInductDecl} + (checked : decl.Checked) : + checked.identityGeneration.motiveLevel = checked.elimination.motiveLevel := rfl + +@[simp] theorem identityGeneration_recLevels {decl : VInductDecl} + (checked : decl.Checked) : + checked.identityGeneration.recLevels = + checked.elimination.recLevels decl.uvars := rfl + +private theorem identity_paramsTel {decl : VInductDecl} + (checked : decl.Checked) : + checked.identityGeneration.paramsTel = + VInductDecl.paramsTel decl.uvars decl.nparams checked.type + checked.elimination := by + simp [GenerationChecked.paramsTel, VInductDecl.paramsTel, + GenerationChecked.sourceLevels, GenerationChecked.elimination, + NormalizedChecked.generationParams, NormalizedChecked.rawParams, + Checked.identityGeneration, Checked.identityBlock, checked.params_eq] + +private theorem identity_idxTel {decl : VInductDecl} + (checked : decl.Checked) : + checked.identityGeneration.idxTel = + VInductDecl.idxTel decl.uvars decl.nparams checked.type + checked.elimination := rfl + +private theorem identity_motiveType {decl : VInductDecl} + (checked : decl.Checked) : + checked.identityGeneration.motiveType = + VInductDecl.motiveType decl.uvars checked.type.name decl.nparams + checked.type checked.elimination := rfl + +private theorem identity_minorType {decl : VInductDecl} + (checked : decl.Checked) (c : VConstVal) : + GenerationChecked.minorType (source := decl) + ⟨c, CheckedCtor.ofDirect decl.uvars checked.type.name decl.nparams + checked.indices.length c⟩ checked.elimination = + VInductDecl.minorTypeRec decl.uvars checked.type.name decl.nparams + checked.type c checked.elimination := by + have hni : checked.indices.length = + (VInductDecl.idxTel decl.uvars decl.nparams checked.type + checked.elimination).length := by + rw [checked.indices_eq] + simp [VInductDecl.idxTel] + rw [hni] + rfl + +private theorem identity_minorTypesAux {decl : VInductDecl} + (checked : decl.Checked) : + ∀ (cs : List VConstVal) (i : Nat), + checked.identityGeneration.minorTypesAux + (pairNormalizedCtors cs + (cs.map (CheckedCtor.ofDirect decl.uvars checked.type.name + decl.nparams checked.indices.length))) i = + VInductDecl.minorTypesRec decl.uvars checked.type.name decl.nparams + checked.type cs i checked.elimination + | [], _ => rfl + | c :: cs, i => by + simp [pairNormalizedCtors, GenerationChecked.minorTypesAux, + VInductDecl.minorTypesRec, identity_minorType, + identity_minorTypesAux checked cs (i+1)] + +private theorem identity_minorTypes {decl : VInductDecl} + (checked : decl.Checked) : + checked.identityGeneration.minorTypes = + VInductDecl.minorTypesRec decl.uvars checked.type.name decl.nparams + checked.type checked.type.ctors (mode := checked.elimination) := by + unfold GenerationChecked.minorTypes + simp only [Checked.identityGeneration, Checked.identityBlock, + NormalizedChecked.ctorPairs] + rw [checked.constructors_eq] + exact identity_minorTypesAux checked checked.type.ctors 0 + +private theorem identity_recType {decl : VInductDecl} + (checked : decl.Checked) : + checked.identityGeneration.recType = + VInductDecl.recTypeRec decl.uvars checked.type.name decl.nparams + checked.type checked.elimination := by + simp only [GenerationChecked.recType, VInductDecl.recTypeRec] + rw [identity_paramsTel checked, identity_motiveType checked, + identity_minorTypes checked, identity_idxTel checked] + simp only [Checked.identityGeneration, Checked.identityBlock, + NormalizedChecked.ctorPairs] + rw [checked.constructors_eq, + pairNormalizedCtors_ofDirect_generationShape + decl.uvars checked.type.name decl.nparams checked.indices.length + checked.type.ctors checked.direct_layout.2 |>.1] + +private theorem identity_recursor {decl : VInductDecl} + (checked : decl.Checked) : + checked.identityGeneration.recursor = + VInductDecl.recConstRec decl.uvars checked.type.name decl.nparams + checked.type checked.elimination := by + unfold GenerationChecked.recursor VInductDecl.recConstRec + rw [identity_recType checked] + simp [GenerationChecked.recUvars, GenerationChecked.elimination, + Checked.identityGeneration, Checked.identityBlock] + +private theorem identity_rule {decl : VInductDecl} + (checked : decl.Checked) (i : Nat) (c : VConstVal) : + checked.identityGeneration.rule i + ⟨c, CheckedCtor.ofDirect decl.uvars checked.type.name decl.nparams + checked.indices.length c⟩ = + VInductDecl.ruleRec decl.uvars checked.type.name decl.nparams + checked.type i c checked.elimination := by + have hni : checked.indices.length = + (VInductDecl.idxTel decl.uvars decl.nparams checked.type + checked.elimination).length := by + rw [checked.indices_eq] + simp [VInductDecl.idxTel] + have hk : checked.identityGeneration.block.ctorPairs.length = + checked.type.ctors.length := by + simp only [Checked.identityGeneration, Checked.identityBlock, + NormalizedChecked.ctorPairs] + rw [checked.constructors_eq] + exact (pairNormalizedCtors_ofDirect_generationShape + decl.uvars checked.type.name decl.nparams checked.indices.length + checked.type.ctors checked.direct_layout.2).1 + unfold GenerationChecked.rule VInductDecl.ruleRec + rw [identity_paramsTel checked, identity_motiveType checked, + identity_minorTypes checked, hk, hni] + rfl + +private theorem identity_rulesAux {decl : VInductDecl} + (checked : decl.Checked) : + ∀ (cs : List VConstVal) (n : Nat), + List.map + (fun (ctor, i) => checked.identityGeneration.rule i ctor) + (List.zipIdx + (pairNormalizedCtors cs + (cs.map (CheckedCtor.ofDirect decl.uvars checked.type.name + decl.nparams checked.indices.length))) n) = + List.map + (fun (c, i) => + VInductDecl.ruleRec decl.uvars checked.type.name decl.nparams + checked.type i c checked.elimination) + (List.zipIdx cs n) + | [], _ => rfl + | c :: cs, n => by + simp [pairNormalizedCtors, List.zipIdx, identity_rule, + identity_rulesAux checked cs (n+1)] + +private theorem identity_generatedRules {decl : VInductDecl} + (checked : decl.Checked) : + checked.identityGeneration.generatedRules = + VInductDecl.rulesRec decl.uvars checked.type.name decl.nparams + checked.type checked.elimination := by + unfold GenerationChecked.generatedRules VInductDecl.rulesRec + simp only [Checked.identityGeneration, Checked.identityBlock, + NormalizedChecked.ctorPairs] + rw [checked.constructors_eq] + exact identity_rulesAux checked checked.type.ctors 0 + +/-- Public motive generation is the identity-normalization specialization of +the mixed raw/view implementation. -/ +def motiveType {decl : VInductDecl} (checked : decl.Checked) : VExpr := + checked.identityGeneration.motiveType + +/-- Public minor generation is the identity-normalization specialization of +the mixed raw/view implementation. -/ +def minorTypes {decl : VInductDecl} (checked : decl.Checked) : List VExpr := + checked.identityGeneration.minorTypes + +/-- Public recursor generation is the identity-normalization specialization +of the mixed raw/view implementation. -/ +def recursor {decl : VInductDecl} (checked : decl.Checked) : VConstant := + checked.identityGeneration.recursor + +/-- Public iota-rule generation is the identity-normalization specialization +of the mixed raw/view implementation. -/ +def generatedRules {decl : VInductDecl} (checked : decl.Checked) : List VDefEq := + checked.identityGeneration.generatedRules + +/-- The public motive retains the exact legacy identity-normal form. -/ +theorem motiveType_eq_legacy {decl : VInductDecl} + (checked : decl.Checked) : + checked.motiveType = + VInductDecl.motiveType decl.uvars checked.type.name decl.nparams + checked.type checked.elimination := + identity_motiveType checked + +/-- The public minor telescope retains the exact legacy identity-normal +form, including functional recursive arguments. -/ +theorem minorTypes_eq_legacy {decl : VInductDecl} + (checked : decl.Checked) : + checked.minorTypes = + VInductDecl.minorTypesRec decl.uvars checked.type.name decl.nparams + checked.type checked.type.ctors (mode := checked.elimination) := + identity_minorTypes checked + +/-- The public recursor retains the exact legacy identity-normal form while +being generated by the mixed implementation. -/ +theorem recursor_eq_legacy {decl : VInductDecl} + (checked : decl.Checked) : + checked.recursor = + VInductDecl.recConstRec decl.uvars checked.type.name decl.nparams + checked.type checked.elimination := + identity_recursor checked + +/-- The public iota-rule list retains the exact legacy identity-normal form +while being generated by the mixed implementation. -/ +theorem generatedRules_eq_legacy {decl : VInductDecl} + (checked : decl.Checked) : + checked.generatedRules = + VInductDecl.rulesRec decl.uvars checked.type.name decl.nparams + checked.type checked.elimination := + identity_generatedRules checked + +end Checked + +/-! ## Mutual raw/view artifacts + +The singleton generator above remains the compatibility proof surface while +the block generator below mirrors `AddInductive.mkRecInfos`: all motives are +introduced in family order, all constructor minors are then flattened in +family/constructor order, and every recursive descriptor selects its target +family by the ordinal retained by `CheckedCtor.ofBlock`. +-/ + +/-- Erased data for one member of the dependent checked-family spine. The +ordinal and normalized family value travel with the projections they index, +so later positional pairing does not manufacture a parallel family order. -/ +structure CheckedFamilyData where + ordinal : Nat + value : VInductiveType + indices : List VExpr + resultLevel : VLevel + constructors : List CheckedCtor + +namespace CheckedFamilies + +/-- Erase a dependent checked-family spine without changing its order. -/ +def data {source : VInductDecl} {params : List VExpr} : + {ordinal : Nat} → {types : List VInductiveType} → + CheckedFamilies source params ordinal types → List CheckedFamilyData + | _, _, .nil => [] + | _, _, .cons head tail => + { ordinal := head.ordinal + value := head.value + indices := head.indices + resultLevel := head.resultLevel + constructors := head.constructors } :: data tail + +end CheckedFamilies + +/-- One stored family paired positionally with the checked normalized family +that drove generation. Stored family/index syntax remains observable in +metadata; the view owns recursive classification and result indices. -/ +structure NormalizedFamily where + raw : VInductiveType + view : CheckedFamilyData + +/-- Positional family pairing. `blockGenerationShape` separately proves +that neither side is truncated. -/ +def pairNormalizedFamilies : + List VInductiveType → List CheckedFamilyData → List NormalizedFamily + | raw :: raws, view :: views => + ⟨raw, view⟩ :: pairNormalizedFamilies raws views + | _, _ => [] + +def NormalizedFamily.rawParams (family : NormalizedFamily) (np : Nat) : + List VExpr := + VExpr.telN np family.raw.type + +def NormalizedFamily.rawIndices (family : NormalizedFamily) (np : Nat) : + List VExpr := + ctorFields (VExpr.dropN np family.raw.type) + +def NormalizedFamily.ctorPairs (family : NormalizedFamily) : + List NormalizedCtor := + pairNormalizedCtors family.raw.ctors family.view.constructors + +/-- One flattened constructor together with its source-family identity. -/ +structure NormalizedBlockCtor where + owner : Nat + familyName : Name + familyIndices : List VExpr + ctor : NormalizedCtor + +def NormalizedFamily.blockCtors (family : NormalizedFamily) : + List NormalizedBlockCtor := + family.ctorPairs.map fun ctor => + { owner := family.view.ordinal + familyName := family.raw.name + familyIndices := family.view.indices + ctor } + +/-- Per-family layout gate for mutual mixed generation. -/ +def NormalizedFamily.generationShape (np : Nat) + (family : NormalizedFamily) : Bool := + family.raw.name == family.view.value.name && + family.raw.uvars == family.view.value.uvars && + (family.rawParams np).length == np && + (family.rawIndices np).length == family.view.indices.length && + family.ctorPairs.length == family.raw.ctors.length && + family.ctorPairs.length == family.view.constructors.length && + family.ctorPairs.all (NormalizedCtor.generationShape np) + +def NormalizedCheckedBlock.rawParams {source : VInductDecl} + (_block : NormalizedCheckedBlock source) : List VExpr := + blockParams source.nparams source.types + +def NormalizedCheckedBlock.familyPairs {source : VInductDecl} + (block : NormalizedCheckedBlock source) : List NormalizedFamily := + pairNormalizedFamilies source.types block.checked.families.data + +def NormalizedCheckedBlock.flatCtors {source : VInductDecl} + (block : NormalizedCheckedBlock source) : List NormalizedBlockCtor := + block.familyPairs.flatMap (·.blockCtors) + +/-- Executable positional gate for a complete normalized block. It checks +the shared parameter surface, every family/index position, and every +constructor position before raw and checked syntax are mixed. -/ +def NormalizedCheckedBlock.blockGenerationShape {source : VInductDecl} + (block : NormalizedCheckedBlock source) : Bool := + block.rawParams.length == source.nparams && + block.rawParams.length == block.checked.params.length && + block.familyPairs.length == source.types.length && + block.familyPairs.length == block.checked.families.data.length && + block.familyPairs.all + (NormalizedFamily.generationShape source.nparams) && + block.familyPairs.all fun family => + family.raw.uvars == source.uvars && + family.ctorPairs.all fun ctor => ctor.raw.uvars == source.uvars + +/-- A validator-owned mutual normalization whose raw/view positions are +usable by artifact generation. -/ +structure BlockGenerationChecked (source : VInductDecl) where + validated : ValidatedBlock source + shape_eq : validated.block.blockGenerationShape = true + +def ValidatedBlock.generation? {source : VInductDecl} + (validated : ValidatedBlock source) : + Option (BlockGenerationChecked source) := + if h : validated.block.blockGenerationShape then + some ⟨validated, h⟩ + else none + +/-- The common result level selected by an identity-normalized checked block. +The empty fallback is unreachable after `checkedBlock?`, but keeps the +executable analyzer total. -/ +def CheckedBlock.firstResultLevel {source : VInductDecl} + (checked : CheckedBlock source) : VLevel := + checked.families.resultLevels.head?.getD .zero + +/-- Analyze an already-normalized arbitrary block for mutual generation, +without any singleton destructuring. -/ +def identityBlockGeneration? (source : VInductDecl) : + Option (BlockGenerationChecked source) := do + let block ← identityCheckedBlock? source + let validated : ValidatedBlock source := + { block + resultLevel := block.checked.firstResultLevel } + validated.generation? + +/-- Public structural acceptance is exact block-generation readiness. This +retains the complete source-ordered mutual descriptor instead of projecting a +singleton family and then rebuilding generation data. -/ +def stage3 (source : VInductDecl) : Bool := + source.identityBlockGeneration?.isSome + +namespace BlockGenerationChecked + +abbrev block {source : VInductDecl} (gen : BlockGenerationChecked source) : + NormalizedCheckedBlock source := + gen.validated.block + +abbrev checked {source : VInductDecl} (gen : BlockGenerationChecked source) : + gen.block.normalization.view.CheckedBlock := + gen.block.checked + +def families {source : VInductDecl} (gen : BlockGenerationChecked source) : + List NormalizedFamily := + gen.block.familyPairs + +def flatCtors {source : VInductDecl} (gen : BlockGenerationChecked source) : + List NormalizedBlockCtor := + gen.block.flatCtors + +abbrev familyCount {source : VInductDecl} + (gen : BlockGenerationChecked source) : Nat := + gen.families.length + +abbrev minorCount {source : VInductDecl} + (gen : BlockGenerationChecked source) : Nat := + gen.flatCtors.length + +/-- Whether the accepted block contains any recursive constructor argument. +For a positive checked block this is the kernel's `inductInfo.isRec` decision +expressed through the analyzer-owned recursive descriptors. -/ +def isRec {source : VInductDecl} + (gen : BlockGenerationChecked source) : Bool := + gen.flatCtors.any fun constructor => + !constructor.ctor.view.recursive.isEmpty + +/-- Whether some recursive argument is hidden beneath a function telescope, +matching the kernel's `inductInfo.isReflexive` flag. -/ +def isReflexive {source : VInductDecl} + (gen : BlockGenerationChecked source) : Bool := + gen.flatCtors.any fun constructor => + constructor.ctor.view.recursive.any fun recursive => + !recursive.binders.isEmpty + +/-- The block large-elimination decision. A non-Prop common result admits +large elimination; at `Prop`, only the kernel's singleton exception can do +so. In particular every genuinely mutual Prop block is small. -/ +def elimination {source : VInductDecl} + (gen : BlockGenerationChecked source) : ElimMode := + if gen.validated.resultLevel.isNeverZero then .large + else + match gen.families with + | [family] => + if largeElim source.uvars family.raw.name source.nparams + family.view.indices.length family.view.value then .large else .small + | _ => .small + +/-- K-like reduction is a singleton-only kernel flag. -/ +def kTarget {source : VInductDecl} + (gen : BlockGenerationChecked source) : Bool := + match gen.families with + | [family] => + isKTarget source.nparams gen.validated.resultLevel family.view.value + | _ => false + +abbrev recUvars {source : VInductDecl} + (gen : BlockGenerationChecked source) : Nat := + gen.elimination.recUvars source.uvars + +abbrev sourceLevels {source : VInductDecl} + (gen : BlockGenerationChecked source) : List VLevel := + gen.elimination.sourceLevels source.uvars + +abbrev motiveLevel {source : VInductDecl} + (gen : BlockGenerationChecked source) : VLevel := + gen.elimination.motiveLevel + +abbrev recLevels {source : VInductDecl} + (gen : BlockGenerationChecked source) : List VLevel := + gen.elimination.recLevels source.uvars + +/-- Shared emitted parameters in recursor universes. -/ +def paramsTel {source : VInductDecl} + (gen : BlockGenerationChecked source) : List VExpr := + generationParams gen.block.rawParams gen.block.checked.params |>.map + (VExpr.instL gen.sourceLevels) + +def familyNameAt {source : VInductDecl} + (gen : BlockGenerationChecked source) (ordinal : Nat) : Name := + (gen.families[ordinal]?).map (fun family => family.raw.name) |>.getD .anonymous + +def idxTel {source : VInductDecl} (gen : BlockGenerationChecked source) + (family : NormalizedFamily) : List VExpr := + (family.rawIndices source.nparams).map (VExpr.instL gen.sourceLevels) + +/-- One family motive before preceding motives have been inserted. -/ +def motiveType {source : VInductDecl} + (gen : BlockGenerationChecked source) + (family : NormalizedFamily) : VExpr := + let indices := gen.idxTel family + let ni := indices.length + VExpr.forallN indices + (.forallE + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange ni source.nparams ++ + VExpr.bvarRevRange 0 ni)) + (.sort gen.motiveLevel)) + +/-- Motives in kernel order. Each later motive is weakened past all earlier +motives while retaining the shared parameter context. -/ +def motiveTypesAux {source : VInductDecl} + (gen : BlockGenerationChecked source) : + List NormalizedFamily → (i : Nat := 0) → List VExpr + | [], _ => [] + | family :: families, i => + (gen.motiveType family).liftN i :: + gen.motiveTypesAux families (i + 1) + +def motiveTypes {source : VInductDecl} + (gen : BlockGenerationChecked source) : List VExpr := + gen.motiveTypesAux gen.families + +/-- Binder telescope of one mutual functional induction hypothesis. -/ +def blockMinorBinders (d m p : Nat) (r : RecArg) : List VExpr := + VExpr.liftTelN (m - r.fieldIndex + p) + (VExpr.liftTelN d r.binders r.fieldIndex) 0 + +/-- Mutual induction hypothesis routed to the recursive argument's target +motive. -/ +def blockMinorIH (d m p : Nat) (r : RecArg) : VExpr := + let n := r.binders.length + VExpr.forallN (blockMinorBinders d m p r) + (VExpr.appN (.bvar (d - 1 - r.targetType + m + p + n)) + ((r.indices.map fun e => + (e.liftN d (r.fieldIndex + n)).liftN + (m - r.fieldIndex + p) n) ++ + [VExpr.appN (.bvar (m - 1 - r.fieldIndex + p + n)) + (VExpr.bvarRevRange 0 n)])) + +def blockIHsFromRecArgs (d m : Nat) : List RecArg → Nat → List VExpr + | [], _ => [] + | r :: rs, p => + blockMinorIH d m p r :: blockIHsFromRecArgs d m rs (p + 1) + +/-- One constructor minor in the context of all block motives. -/ +def minorType {source : VInductDecl} + (gen : BlockGenerationChecked source) + (constructor : NormalizedBlockCtor) : VExpr := + let d := gen.familyCount + let Bs := constructor.ctor.fieldsR source.uvars source.nparams gen.elimination + let m := Bs.length + let rs := constructor.ctor.recArgsR source.uvars gen.elimination + let r := rs.length + VExpr.forallN (VExpr.liftTelN d Bs 0) + (VExpr.forallN (blockIHsFromRecArgs d m rs 0) + (VExpr.appN (.bvar (d - 1 - constructor.owner + m + r)) + ((constructor.ctor.resultIndicesR source.uvars gen.elimination |>.map + fun e => (e.liftN d m).liftN r) ++ + [VExpr.appN (.const constructor.ctor.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (r + m + d) source.nparams ++ + VExpr.bvarRevRange r m)]))) + +/-- Globally flattened constructor minors in family/constructor order. -/ +def minorTypesAux {source : VInductDecl} + (gen : BlockGenerationChecked source) : + List NormalizedBlockCtor → (i : Nat := 0) → List VExpr + | [], _ => [] + | constructor :: constructors, i => + (gen.minorType constructor).liftN i :: + gen.minorTypesAux constructors (i + 1) + +def minorTypes {source : VInductDecl} + (gen : BlockGenerationChecked source) : List VExpr := + gen.minorTypesAux gen.flatCtors + +/-- One recursor type for the selected family, sharing every motive and minor +with the other family recursors. -/ +def recType {source : VInductDecl} + (gen : BlockGenerationChecked source) + (family : NormalizedFamily) : VExpr := + let d := gen.familyCount + let k := gen.minorCount + let indices := gen.idxTel family + let ni := indices.length + VExpr.forallN gen.paramsTel <| + VExpr.forallN gen.motiveTypes <| + VExpr.forallN gen.minorTypes <| + VExpr.forallN (VExpr.liftTelN (d + k) indices 0) <| + .forallE + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (ni + d + k) source.nparams ++ + VExpr.bvarRevRange 0 ni)) <| + .app + (VExpr.appN + (.bvar (d - 1 - family.view.ordinal + k + ni + 1)) + (VExpr.bvarRevRange 1 ni)) + (.bvar 0) + +def recursor {source : VInductDecl} + (gen : BlockGenerationChecked source) + (family : NormalizedFamily) : VConstant := + ⟨gen.recUvars, gen.recType family⟩ + +/-- Named generated recursors in family order. -/ +def recursors {source : VInductDecl} + (gen : BlockGenerationChecked source) : List VConstVal := + gen.families.map fun family => + ⟨gen.recursor family, .str family.raw.name "rec"⟩ + +/-- Telescope introduced around a mutual recursive rule call. -/ +def blockRuleBinders (common m : Nat) (r : RecArg) : List VExpr := + VExpr.liftTelN (m - r.fieldIndex) + (VExpr.liftTelN common r.binders r.fieldIndex) 0 + +/-- Recursive iota-rule call routed to the target family's recursor. -/ +def blockRuleCall (common m : Nat) (recBase : VExpr) + (r : RecArg) : VExpr := + let n := r.binders.length + VExpr.lamN (blockRuleBinders common m r) + (VExpr.appN (recBase.liftN n) + ((r.indices.map fun e => + (e.liftN common (r.fieldIndex + n)).liftN + (m - r.fieldIndex) n) ++ + [VExpr.appN (.bvar (m - 1 - r.fieldIndex + n)) + (VExpr.bvarRevRange 0 n)])) + +def recBase {source : VInductDecl} + (gen : BlockGenerationChecked source) (m target : Nat) : VExpr := + VExpr.appN + (.const (.str (gen.familyNameAt target) "rec") gen.recLevels) + (VExpr.bvarRevRange m + (source.nparams + gen.familyCount + gen.minorCount)) + +/-- One mutual iota rule at its global flattened minor ordinal. -/ +def rule {source : VInductDecl} + (gen : BlockGenerationChecked source) + (minorIndex : Nat) (constructor : NormalizedBlockCtor) : VDefEq := + let d := gen.familyCount + let k := gen.minorCount + let common := d + k + let Bs := constructor.ctor.fieldsR source.uvars source.nparams gen.elimination + let m := Bs.length + let rs := constructor.ctor.recArgsR source.uvars gen.elimination + let binders := gen.paramsTel ++ gen.motiveTypes ++ gen.minorTypes ++ + VExpr.liftTelN common Bs 0 + let ownerRecBase := gen.recBase m constructor.owner + let idxR := constructor.ctor.resultIndicesR source.uvars gen.elimination |>.map + fun e => e.liftN common m + let ctorApp := VExpr.appN + (.const constructor.ctor.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (m + common) source.nparams ++ + VExpr.bvarRevRange 0 m) + let ihs := rs.map fun recursive => + blockRuleCall common m (gen.recBase m recursive.targetType) recursive + { uvars := gen.recUvars + lhs := VExpr.lamN binders + (VExpr.appN ownerRecBase (idxR ++ [ctorApp])) + rhs := VExpr.lamN binders + (VExpr.appN (.bvar (k - 1 - minorIndex + m)) + (VExpr.bvarRevRange 0 m ++ ihs)) + type := VExpr.forallN binders + (VExpr.appN + (.bvar (d - 1 - constructor.owner + k + m)) + (idxR ++ [ctorApp])) } + +def generatedRules {source : VInductDecl} + (gen : BlockGenerationChecked source) : List VDefEq := + gen.flatCtors.zipIdx.map fun (constructor, i) => + gen.rule i constructor + +end BlockGenerationChecked + +/-- Semantic evidence for a recursive argument beneath a Pi telescope. The +binder domains are checked over the constructor context before the recursive +field; its terminal index spine is checked underneath those binders. -/ +def RecArg.WF (r : RecArg) (env : VEnv) (l : VLevel) (Is : List VExpr) + (Γ : List VExpr) : Prop := + VEnv.OnTel env U Γ r.binders ∧ + env.SpineWF U (r.binders.reverse ++ Γ) + (VExpr.forallN (VExpr.liftTelN (r.fieldIndex + r.binders.length) Is 0) + (.sort l)) + r.indices (.sort l) + +/-- Semantic well-formedness of every constructor field in a mutual block. + +Recursive fields may end in any source-indexed family and may sit below a Pi +telescope. Their target ordinal selects the corresponding index telescope +from `familyIndices`. A non-recursive field must be a block-free type in the +pre-family environment and obey the common result-universe bound. -/ +def blockFieldsWF (source : VInductDecl) (env : VEnv) + (resultLevel : VLevel) (familyIndices : List (List VExpr)) : + List VExpr → Nat → List VExpr → Prop + | _, _, [] => True + | Γ, j, B :: Bs => + (match blockRecArg? source.uvars source.nparams + (familyHeaders source.nparams source.types) + (familyNames source.types) j B with + | some recursive => + match familyIndices[recursive.targetType]? with + | some indices => + recursive.WF source.uvars env resultLevel indices Γ + | none => False + | none => + ∃ u, env.HasType source.uvars Γ B (.sort u) ∧ + (resultLevel = .zero ∨ u ≤ resultLevel)) ∧ + blockFieldsWF source env resultLevel familyIndices + (B :: Γ) (j + 1) Bs + +/-- Interpret the analyzer-retained positional classifications rather than +recomputing recursive recognition from field syntax. The list lengths and +field indices are checked in the proposition, while each recursive target +selects its exact source-ordered family index telescope. -/ +def checkedBlockFieldsWF (env : VEnv) (U : Nat) + (resultLevel : VLevel) (familyIndices : List (List VExpr)) : + List VExpr → List (Option RecArg) → List VExpr → Nat → Prop + | [], [], _, _ => True + | B :: Bs, classification :: classifications, Γ, j => + (match classification with + | some recursive => + recursive.fieldIndex = j ∧ + match familyIndices[recursive.targetType]? with + | some indices => recursive.WF U env resultLevel indices Γ + | none => False + | none => + ∃ u, env.HasType U Γ B (.sort u) ∧ + (resultLevel = .zero ∨ u ≤ resultLevel)) ∧ + checkedBlockFieldsWF env U resultLevel familyIndices + Bs classifications (B :: Γ) (j + 1) + | _, _, _, _ => False + +/-- Interpret a dependent source-ordered family spine at one shared result +universe. Family formers and the block-free portions of constructor fields +are interpreted in the common pre-family environment. Recursive targets are +reduced to their selected family index telescopes, so this layer does not +pretend that generated constants or recursors already exist. -/ +def CheckedFamilies.WF {source : VInductDecl} {params : List VExpr} + {ordinal : Nat} {types : List VInductiveType} + (families : CheckedFamilies source params ordinal types) + (env : VEnv) (resultLevel : VLevel) + (familyIndices : List (List VExpr)) : Prop := + match families with + | .nil => True + | .cons head tail => + head.resultLevel ≈ resultLevel ∧ + VEnv.OnTel env source.uvars [] (params ++ head.indices) ∧ + (∀ constructor ∈ head.constructors, + blockFieldsWF source env resultLevel familyIndices + params.reverse 0 constructor.fields ∧ + env.SpineWF source.uvars + (constructor.fields.reverse ++ params.reverse) + (VExpr.forallN + (VExpr.liftTelN constructor.fields.length head.indices 0) + (.sort resultLevel)) + constructor.resultIndices (.sort resultLevel)) ∧ + tail.WF env resultLevel familyIndices + +/-- Erased semantic fold over the exact projections of a dependent checked +family spine. `CheckedBlock.WF` uses this presentation so concrete checked +descriptors obtained by computation reduce through their public projections +without exposing proof fields stored inside `Option.get`. -/ +def checkedFamilyListsWF (source : VInductDecl) (params : List VExpr) + (env : VEnv) (resultLevel : VLevel) + (familyIndices : List (List VExpr)) : + List VLevel → List (List VExpr) → List (List CheckedCtor) → Prop + | [], [], [] => True + | level :: levels, indices :: indicesTail, + constructors :: constructorsTail => + level ≈ resultLevel ∧ + VEnv.OnTel env source.uvars [] (params ++ indices) ∧ + (∀ constructor ∈ constructors, + checkedBlockFieldsWF env source.uvars resultLevel familyIndices + constructor.fields constructor.recursiveAt params.reverse 0 ∧ + env.SpineWF source.uvars + (constructor.fields.reverse ++ params.reverse) + (VExpr.forallN + (VExpr.liftTelN constructor.fields.length indices 0) + (.sort resultLevel)) + constructor.resultIndices (.sort resultLevel)) ∧ + checkedFamilyListsWF source params env resultLevel familyIndices + levels indicesTail constructorsTail + | _, _, _ => False + +/-- Environment-indexed semantics of a structurally checked mutual block. +The common `resultLevel` is explicit and every family result is required to be +semantically equivalent to it. -/ +def CheckedBlock.WF {source : VInductDecl} + (checked : source.CheckedBlock) (env : VEnv) + (resultLevel : VLevel) : Prop := + checkedFamilyListsWF source checked.params env resultLevel + checked.families.indices checked.families.resultLevels + checked.families.indices checked.families.constructors + +/-- Complete Theory semantics of one validator-owned mutual normalization. +Normalization compares raw family types before staging and raw constructor +types after all-family staging; the checked view supplies the block-wide +strict-positivity and result-spine interpretation. -/ +def ValidatedBlock.WF {source : VInductDecl} + (validated : ValidatedBlock source) (env blockEnv : VEnv) : Prop := + validated.block.normalization.BlockWF env blockEnv ∧ + validated.block.checked.WF env validated.resultLevel + +/-- Consumer-facing validation-only package. It deliberately contains no +generated motive, recursor, rule, or environment insertion beyond the +temporary all-family validation stage. -/ +structure ValidationCertificate (source : VInductDecl) (env : VEnv) where + validated : ValidatedBlock source + blockEnv : VEnv + wf : validated.WF env blockEnv + +/-- Semantic well-formedness of constructor fields over the pre-environment. +Direct recursive fields retain the Stage-3 spine contract. A recursive Pi +field additionally carries a well-formed binder telescope and the terminal +index spine below it. Non-recursive fields are well-formed types; non-Prop +families retain the universe bound, while Prop uses Lean's impredicative +exception. -/ +def fieldsWF (env : VEnv) (l : VLevel) (Is : List VExpr) : + List VExpr → Nat → List VExpr → Prop + | _, _, [] => True + | Γ, j, B :: Bs => + (isRecField U T np Is.length j B = true ∨ + (∃ r, recArg? U T np Is.length j B = some r ∧ r.binders ≠ [] ∧ + r.WF U env l Is Γ) ∨ + recArg? U T np Is.length j B = none ∧ + ∃ u, env.HasType U Γ B (.sort u) ∧ (l = .zero ∨ u ≤ l)) ∧ + (isRecField U T np Is.length j B = true → + env.SpineWF U Γ (VExpr.forallN (VExpr.liftTelN j Is 0) (.sort l)) + (recFieldIdxs np B) (.sort l)) ∧ + fieldsWF env l Is (B :: Γ) (j+1) Bs + +/-- Environment-indexed semantic evidence for a successful structural +analysis. This is the one-family counterpart of the kernel's `checkType` and +constructor checking passes: `Checked` owns normalized syntax, while +`Checked.WF` proves that syntax meaningful over the input environment. -/ +def Checked.WF {decl : VInductDecl} (checked : decl.Checked) (env : VEnv) : Prop := + VEnv.OnTel env decl.uvars [] (checked.params ++ checked.indices) ∧ + ∀ c ∈ checked.type.ctors, + fieldsWF decl.uvars checked.type.name decl.nparams env checked.resultLevel + checked.indices checked.params.reverse 0 + (ctorFields (VExpr.dropN decl.nparams c.type)) ∧ + env.SpineWF decl.uvars + ((ctorFields (VExpr.dropN decl.nparams c.type)).reverse ++ checked.params.reverse) + (VExpr.forallN + (VExpr.liftTelN (ctorFields (VExpr.dropN decl.nparams c.type)).length + checked.indices 0) + (.sort checked.resultLevel)) + (recFieldIdxs decl.nparams (VExpr.resultOf (VExpr.dropN decl.nparams c.type))) + (.sort checked.resultLevel) + +/-- Semantic certificate for an accepted raw/view pair. The normalization +justifies the raw metadata against the view, while `Checked.WF` justifies the +view's analyzer facts in the same input Theory environment. -/ +def NormalizedChecked.WF {source : VInductDecl} + (block : NormalizedChecked source) (env : VEnv) : Prop := + block.normalization.WF env ∧ block.checked.WF env + +/-- The binders emitted for one mixed minor/rule: the checked family +parameters followed by the stored raw constructor fields. + +Lean consumes parameter annotations while checking the family and uses those +checked locals when it builds the recursor. Constructor fields, by contrast, +retain their stored surface syntax. -/ +def NormalizedCtor.emittedBinders {source : VInductDecl} + (block : NormalizedChecked source) (ctor : NormalizedCtor) : List VExpr := + block.checked.params ++ ctor.rawFields source.nparams + +/-- The normalized binders whose semantic analysis drives the mixed artifact. +These expressions are never emitted in place of their raw partners. -/ +def NormalizedCtor.viewBinders {source : VInductDecl} + (block : NormalizedChecked source) (ctor : NormalizedCtor) : List VExpr := + block.checked.params ++ ctor.view.fields + +/-- The normalized constructor result reconstructed with the retained raw +family identity. It is interpreted in either of the raw contexts certified +below. -/ +def NormalizedCtor.resultTarget {source : VInductDecl} + (block : NormalizedChecked source) (ctor : NormalizedCtor) : VExpr := + VExpr.appN (.const block.sourceType.name (VLevel.params source.uvars)) + (VExpr.bvarRevRange (ctor.rawFields source.nparams).length source.nparams ++ + ctor.view.resultIndices) + +/-- Staged semantic evidence for one raw/view constructor pair. + +`declaredTel`/`declaredResult` validate the exact stored constructor type so it +can be inserted. `emittedTel`/`emittedResult` validate the binder context used +by mixed minors and rules. The two raw parameter prefixes may be merely +definitionally equal; recording both paths therefore anticipates the kernel's +constructor-parameter behavior without assuming syntactic equality. -/ +structure NormalizedCtor.WF {source : VInductDecl} + (block : NormalizedChecked source) (ctor : NormalizedCtor) + (env : VEnv) : Prop where + declaredTel : + env.TelDefEq source.uvars [] + (ctor.declaredBinders source.nparams) (ctor.viewBinders block) + declaredResult : + env.IsDefEq source.uvars (ctor.declaredBinders source.nparams).reverse + (ctor.rawResult source.nparams) (ctor.resultTarget block) + (.sort block.checked.resultLevel) + emittedTel : + env.TelDefEq source.uvars [] + (ctor.emittedBinders block) (ctor.viewBinders block) + emittedResult : + env.IsDefEq source.uvars (ctor.emittedBinders block).reverse + (ctor.rawResult source.nparams) (ctor.resultTarget block) + (.sort block.checked.resultLevel) + +/-- Semantic certificate for a generation-ready raw/view block. + +The family telescope/result are certified before family insertion. +Constructor certificates are quantified over the exact environment produced +by inserting the raw family constant. This staging is essential for aliases +whose constructor fields mention that family. No field of this structure is a +normalization oracle: every equality is an ordinary Theory derivation. -/ +structure GenerationChecked.WF {source : VInductDecl} + (gen : GenerationChecked source) (env : VEnv) : Prop where + blockWF : gen.block.WF env + familyTel : + env.TelDefEq source.uvars [] + (gen.block.rawParams ++ gen.block.rawIndices) + (gen.block.checked.params ++ gen.block.checked.indices) + familyResult : + env.IsDefEq source.uvars + (gen.block.rawParams ++ gen.block.rawIndices).reverse + gen.block.rawResult (.sort gen.block.checked.resultLevel) + (.sort (.succ gen.block.checked.resultLevel)) + ctors : + ∀ envT, env.addConst gen.block.sourceType.name + gen.block.sourceType.toVConstant = some envT → + ∀ ctor ∈ gen.block.ctorPairs, ctor.WF gen.block envT + +/-! ### Mutual generation certificates -/ + +def NormalizedFamily.rawResult (family : NormalizedFamily) (np : Nat) : + VExpr := + VExpr.resultOf (VExpr.dropN np family.raw.type) + +def NormalizedBlockCtor.declaredBinders {source : VInductDecl} + (constructor : NormalizedBlockCtor) : List VExpr := + constructor.ctor.declaredBinders source.nparams + +/-- Constructor binders used by generated mutual artifacts before universe +instantiation. -/ +def NormalizedBlockCtor.emittedBinders {source : VInductDecl} + (gen : BlockGenerationChecked source) + (constructor : NormalizedBlockCtor) : List VExpr := + gen.block.checked.params ++ constructor.ctor.rawFields source.nparams + +def NormalizedBlockCtor.viewBinders {source : VInductDecl} + (gen : BlockGenerationChecked source) + (constructor : NormalizedBlockCtor) : List VExpr := + gen.block.checked.params ++ constructor.ctor.view.fields + +def NormalizedBlockCtor.rawResult {source : VInductDecl} + (constructor : NormalizedBlockCtor) : VExpr := + constructor.ctor.rawResult source.nparams + +/-- Normalized constructor result reconstructed with the stored owner name. -/ +def NormalizedBlockCtor.resultTarget {source : VInductDecl} + (_gen : BlockGenerationChecked source) + (constructor : NormalizedBlockCtor) : VExpr := + VExpr.appN + (.const constructor.familyName (VLevel.params source.uvars)) + (VExpr.bvarRevRange + (constructor.ctor.rawFields source.nparams).length source.nparams ++ + constructor.ctor.view.resultIndices) + +/-- Granular raw/view semantics for one mutual family. -/ +structure NormalizedFamily.WF {source : VInductDecl} + (gen : BlockGenerationChecked source) (family : NormalizedFamily) + (env : VEnv) : Prop where + familyTel : + env.TelDefEq source.uvars [] + (family.rawParams source.nparams ++ family.rawIndices source.nparams) + (gen.block.checked.params ++ family.view.indices) + familyResult : + env.IsDefEq source.uvars + (family.rawParams source.nparams ++ + family.rawIndices source.nparams).reverse + (family.rawResult source.nparams) + (.sort gen.validated.resultLevel) + (.sort (.succ gen.validated.resultLevel)) + +/-- Declaration-stage raw/view semantics for one constructor after every +family has been staged. -/ +structure NormalizedBlockCtor.WF {source : VInductDecl} + (gen : BlockGenerationChecked source) + (constructor : NormalizedBlockCtor) (env : VEnv) : Prop where + declaredTel : + env.TelDefEq source.uvars [] + (NormalizedBlockCtor.declaredBinders (source := source) constructor) + (NormalizedBlockCtor.viewBinders gen constructor) + declaredResult : + env.IsDefEq source.uvars + (NormalizedBlockCtor.declaredBinders + (source := source) constructor).reverse + (NormalizedBlockCtor.rawResult (source := source) constructor) + (NormalizedBlockCtor.resultTarget gen constructor) + (.sort gen.validated.resultLevel) + emittedTel : + env.TelDefEq source.uvars [] + (NormalizedBlockCtor.emittedBinders gen constructor) + (NormalizedBlockCtor.viewBinders gen constructor) + emittedResult : + env.IsDefEq source.uvars + (NormalizedBlockCtor.emittedBinders gen constructor).reverse + (NormalizedBlockCtor.rawResult (source := source) constructor) + (NormalizedBlockCtor.resultTarget gen constructor) + (.sort gen.validated.resultLevel) + owner : ∃ family ∈ gen.families, + family.view.ordinal = constructor.owner ∧ + family.raw.name = constructor.familyName ∧ + family.view.indices = constructor.familyIndices + recursive : ∀ recursive ∈ constructor.ctor.view.recursive, + ∃ family ∈ gen.families, + family.view.ordinal = recursive.targetType ∧ + (∃ B, + constructor.ctor.view.fields[recursive.fieldIndex]? = some B ∧ + B = VExpr.forallN recursive.binders + (VExpr.appN + (.const family.raw.name (VLevel.params source.uvars)) + (VExpr.bvarRevRange + (recursive.fieldIndex + recursive.binders.length) + source.nparams ++ recursive.indices))) ∧ + recursive.WF source.uvars env gen.validated.resultLevel + family.view.indices + ((constructor.ctor.view.fields.take recursive.fieldIndex).reverse ++ + gen.block.checked.params.reverse) + resultSpine : + env.SpineWF source.uvars + (constructor.ctor.view.fields.reverse ++ + gen.block.checked.params.reverse) + (VExpr.forallN + (VExpr.liftTelN constructor.ctor.view.fields.length + constructor.familyIndices 0) + (.sort gen.validated.resultLevel)) + constructor.ctor.view.resultIndices + (.sort gen.validated.resultLevel) + +/-- Semantic input to block artifact preservation. No field assumes a +generated motive, minor, recursor, or rule typing judgment. -/ +structure BlockGenerationChecked.WF {source : VInductDecl} + (gen : BlockGenerationChecked source) (env blockEnv : VEnv) : Prop where + blockWF : gen.validated.WF env blockEnv + resultLevelWF : gen.validated.resultLevel.WF source.uvars + paramsTel : + env.TelDefEq source.uvars [] gen.block.rawParams + gen.block.checked.params + families : ∀ family ∈ gen.families, family.WF gen env + constructors : + ∀ constructor ∈ gen.flatCtors, + NormalizedBlockCtor.WF gen constructor blockEnv + +/-- Consumer-facing semantic package for one generation-ready inductive +declaration. The executable transaction inspects only `generation`; `wf` is +the ordinary Theory certificate used by preservation and is never a +normalization oracle. + +Verify can erase checker-specific candidate provenance to this boundary +before handing a normalized transaction to downstream consumers. -/ +structure GenerationCertificate (source : VInductDecl) (env : VEnv) where + generation : GenerationChecked source + wf : generation.WF env + +/-- Consumer-facing proof-carrying mutual generation package. -/ +structure BlockGenerationCertificate (source : VInductDecl) (env : VEnv) where + generation : BlockGenerationChecked source + blockEnv : VEnv + wf : generation.WF env blockEnv + +end VInductDecl + +/-- Legacy declaration-level semantic contract for one-family compatibility. +Block-wide preservation consumes `BlockGenerationChecked.WF` directly. -/ +def VInductDecl.WF (env : VEnv) (decl : VInductDecl) : Prop := + decl.singletonStage3 ∧ + ∀ ty ∈ decl.types, + VEnv.OnTel env decl.uvars [] + (VExpr.telN decl.nparams ty.type ++ + VInductDecl.ctorFields (VExpr.dropN decl.nparams ty.type)) ∧ + ∀ c ∈ ty.ctors, + VInductDecl.fieldsWF decl.uvars ty.name decl.nparams env + (VInductDecl.sortLevel decl.nparams ty) + (VInductDecl.ctorFields (VExpr.dropN decl.nparams ty.type)) + (VExpr.telN decl.nparams ty.type).reverse 0 + (VInductDecl.ctorFields (VExpr.dropN decl.nparams c.type)) ∧ + env.SpineWF decl.uvars + ((VInductDecl.ctorFields (VExpr.dropN decl.nparams c.type)).reverse ++ + (VExpr.telN decl.nparams ty.type).reverse) + (VExpr.forallN + (VExpr.liftTelN (VInductDecl.ctorFields (VExpr.dropN decl.nparams c.type)).length + (VInductDecl.ctorFields (VExpr.dropN decl.nparams ty.type)) 0) + (.sort (VInductDecl.sortLevel decl.nparams ty))) + (VInductDecl.recFieldIdxs decl.nparams + (VExpr.resultOf (VExpr.dropN decl.nparams c.type))) + (.sort (VInductDecl.sortLevel decl.nparams ty)) + +/-- Raw family constants of a block in source order. -/ +def VInductDecl.blockTypeConstants (source : VInductDecl) : + List VConstVal := + source.types.map (·.toVConstVal) + +/-- Raw constructor constants flattened in family/constructor order. -/ +def VInductDecl.blockConstructorConstants (source : VInductDecl) : + List VConstVal := + source.types.flatMap (·.ctors) + +/-- The single computational transaction for a generation-ready raw/view +block. Stored family and constructor constants come from the raw source; +recursor and rules come from the mixed artifact implementation. Semantic +validity is a theorem premise, never an input to this computation. -/ +def VEnv.addInductGeneration {source : VInductDecl} + (env : VEnv) (gen : source.GenerationChecked) : Option VEnv := do + let ty := gen.block.sourceType + let env ← env.addConst ty.name ty.toVConstant + let env ← ty.ctors.foldlM + (fun env c => env.addConst c.name c.toVConstant) env + let env ← env.addConst (.str ty.name "rec") gen.recursor + return gen.generatedRules.foldl VEnv.addDefEq env + +/-- The block-wide generation transaction. Its four phases deliberately +match the kernel's visibility boundaries: every family is present before any +constructor, every constructor before any recursor, and every recursor before +the first reduction rule. -/ +def VEnv.addInductBlockGeneration {source : VInductDecl} + (env : VEnv) (gen : source.BlockGenerationChecked) : Option VEnv := do + let env ← source.blockTypeConstants.foldlM + (fun env type => env.addConst type.name type.toVConstant) env + let env ← source.blockConstructorConstants.foldlM + (fun env constructor => env.addConst constructor.name constructor.toVConstant) env + let env ← gen.recursors.foldlM + (fun env recursor => env.addConst recursor.name recursor.toVConstant) env + return gen.generatedRules.foldl VEnv.addDefEq env + +/-- Public proof-carrying wrapper around `addInductGeneration`. + +The certificate's proof is erased and does not influence computation. This +entry point lets a verified producer expose a non-identity normalization +without exposing its checker trace or asking a consumer to remember a +separate preservation premise. -/ +def VEnv.addInductCertified {source : VInductDecl} + (env : VEnv) (certificate : source.GenerationCertificate env) : + Option VEnv := + env.addInductGeneration certificate.generation + +/-- Proof-carrying wrapper for a block-wide generation transaction. The +certificate is erased from computation. -/ +def VEnv.addInductBlockCertified {source : VInductDecl} + (env : VEnv) (certificate : source.BlockGenerationCertificate env) : + Option VEnv := + env.addInductBlockGeneration certificate.generation + +@[simp] theorem VEnv.addInductCertified_eq_addInductGeneration + {source : VInductDecl} (env : VEnv) + (certificate : source.GenerationCertificate env) : + env.addInductCertified certificate = + env.addInductGeneration certificate.generation := + rfl + +@[simp] theorem VEnv.addInductBlockCertified_eq_addInductBlockGeneration + {source : VInductDecl} (env : VEnv) + (certificate : source.BlockGenerationCertificate env) : + env.addInductBlockCertified certificate = + env.addInductBlockGeneration certificate.generation := + rfl + +/-- Exact intermediate states of a successful normalized inductive +transaction. Stable lookup, freshness, monotonicity, and preservation +consequences are derived from this one trace in the typing layer. -/ +structure VEnv.AddInductGenerationTrace {source : VInductDecl} + (env env' : VEnv) (gen : source.GenerationChecked) where + typeEnv : VEnv + ctorEnv : VEnv + recEnv : VEnv + addType : + env.addConst gen.block.sourceType.name + gen.block.sourceType.toVConstant = some typeEnv + addCtors : + gen.block.sourceType.ctors.foldlM + (fun env c => env.addConst c.name c.toVConstant) typeEnv = + some ctorEnv + addRec : + ctorEnv.addConst (.str gen.block.sourceType.name "rec") + gen.recursor = some recEnv + addRules : + gen.generatedRules.foldl VEnv.addDefEq recEnv = env' + +/-- Exact phase boundaries of a successful block-wide generation +transaction. -/ +structure VEnv.AddInductBlockGenerationTrace {source : VInductDecl} + (env env' : VEnv) (gen : source.BlockGenerationChecked) where + typeEnv : VEnv + ctorEnv : VEnv + recEnv : VEnv + addTypes : + source.blockTypeConstants.foldlM + (fun env type => env.addConst type.name type.toVConstant) env = + some typeEnv + addCtors : + source.blockConstructorConstants.foldlM + (fun env constructor => env.addConst constructor.name constructor.toVConstant) typeEnv = + some ctorEnv + addRecs : + gen.recursors.foldlM + (fun env recursor => env.addConst recursor.name recursor.toVConstant) + ctorEnv = some recEnv + addRules : + gen.generatedRules.foldl VEnv.addDefEq recEnv = env' + +/-- The raw-normal-form API analyzes and inserts a complete mutual block. -/ +def VEnv.addInduct (env : VEnv) (decl : VInductDecl) : Option VEnv := do + let generation ← decl.identityBlockGeneration? + env.addInductBlockGeneration generation + +/-- Compatibility wrapper for the pre-Spec-08C one-family raw transaction. + +Unlike `addInduct`, this deliberately projects the legacy `Checked` artifact +and therefore rejects every genuinely mutual declaration. It remains only +for a deprecation window so existing one-family consumers can separate their +API migration from the semantic switch to block-wide generation. -/ +@[deprecated VEnv.addInduct (since := "2026-08-07")] +def VEnv.addInductSingleton (env : VEnv) (decl : VInductDecl) : Option VEnv := do + let checked ← decl.checked? + env.addInductGeneration checked.identityGeneration + +/-- The compatibility wrapper is exactly the former identity-normalization +transaction; it is not a second block-generation path. -/ +@[simp, deprecated VEnv.addInduct (since := "2026-08-07")] +theorem VEnv.addInductSingleton_eq_addInductGeneration + (env : VEnv) (decl : VInductDecl) : + env.addInductSingleton decl = + decl.checked? >>= fun checked => + env.addInductGeneration checked.identityGeneration := + rfl + +/-- The raw public API is transparently the identity-normalization +specialization of the normalized block transaction. -/ +theorem VEnv.addInduct_eq_addInductBlockGeneration + (env : VEnv) (decl : VInductDecl) : + env.addInduct decl = + decl.identityBlockGeneration? >>= fun generation => + env.addInductBlockGeneration generation := + rfl + +#guard_named_axioms Ix.Theory.Named.VEnv.addInductSingleton_eq_addInductGeneration [propext, Classical.choice, Quot.sound] + +/-- Stable, consumer-facing consequences of a successful `addInduct` +transaction. This deliberately hides the internal `foldlM` sequence: a +caller receives the complete output environment and all generated objects, or +`addInduct` returns `none` without exposing an intermediate environment. -/ +structure VEnv.AddInductSuccess (env env' : VEnv) (decl : VInductDecl) : Prop where + generation : ∃ generation, decl.identityBlockGeneration? = some generation + accepted : decl.stage3 = true + le : env ≤ env' + type_fresh : ∀ ty ∈ decl.types, env.constants ty.name = none + type_lookup : ∀ ty ∈ decl.types, env'.constants ty.name = some ty.toVConstant + ctor_fresh : ∀ ty ∈ decl.types, ∀ c ∈ ty.ctors, env.constants c.name = none + ctor_lookup : ∀ ty ∈ decl.types, ∀ c ∈ ty.ctors, + env'.constants c.name = some c.toVConstant + rec_fresh : ∀ generation, decl.identityBlockGeneration? = some generation → + ∀ recursor ∈ generation.recursors, env.constants recursor.name = none + rec_lookup : ∀ generation, decl.identityBlockGeneration? = some generation → + ∀ recursor ∈ generation.recursors, + env'.constants recursor.name = some recursor.toVConstant + rule_mem : ∀ generation, decl.identityBlockGeneration? = some generation → + ∀ df ∈ generation.generatedRules, env'.defeqs df diff --git a/Ix/Theory/Named/InductiveFixtures.lean b/Ix/Theory/Named/InductiveFixtures.lean new file mode 100644 index 000000000..f1b2ff3be --- /dev/null +++ b/Ix/Theory/Named/InductiveFixtures.lean @@ -0,0 +1,2897 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Inductive +import Ix.Theory.Named.Meta +import Ix.Theory.Named.Typing.InductiveLemmas +import Ix.Theory.Named.Typing.Meta + +open Ix.Theory (VLevel) + +/-! Adequacy fixtures for `VEnv.addInduct` (stage 3): run the generator on +hand-written declarations and check the output against the real kernel's +constants, translated by the `vconst`/`vdefeq` macros. A mismatch in +telescope order, universe conventions, or de Bruijn arithmetic fails these +`rfl`s. -/ + +namespace Ix.Theory.Named +namespace InductiveFixtures +open VInductDecl + +/-- Permute the universe parameters of a translated constant. The `vconst` +and `vdefeq` macros number universes by occurrence order (declaration +levels first), while the kernel's recursors put the elimination level +first; these fixtures compare via the explicit permutation. -/ +private def permC (ci : VConstant) (ls : List VLevel) : VConstant := + ⟨ci.uvars, ci.type.instL ls⟩ + +private def permE (df : VDefEq) (ls : List VLevel) : VDefEq := + ⟨df.uvars, df.lhs.instL ls, df.rhs.instL ls, df.type.instL ls⟩ + +/-! ## Nat -/ + +def natType : VInductiveType where + name := ``Nat + uvars := 0 + type := vexpr(Type) + ctors := [⟨vconst(type_of% @Nat.zero), ``Nat.zero⟩, ⟨vconst(type_of% @Nat.succ), ``Nat.succ⟩] + +def natDecl : VInductDecl := ⟨0, 0, [natType]⟩ + +example : natDecl.stage3 = true := rfl + +/-- The shared checked analysis retains the normalized declaration data that +generation and verification consume. -/ +def natChecked : natDecl.Checked := natDecl.checked?.get (by decide) + +/-- Identity normalization packages the existing raw-normal-form path without +changing its analyzer result. -/ +def natNormalizedChecked : NormalizedChecked natDecl := + (identityChecked? natDecl).get (by decide) + +def natGenerationChecked : GenerationChecked natDecl := + (identityGeneration? natDecl).get (by decide) + +/-- The public block descriptor specializes to the same singleton metadata. -/ +def natBlockGenerationChecked : BlockGenerationChecked natDecl := + (identityBlockGeneration? natDecl).get (by decide) + +example : natNormalizedChecked.normalization.view = natDecl := rfl +example : natNormalizedChecked.checked.type = natType := rfl +example : (normalizedChecked? natDecl natDecl).isSome = true := rfl +example : (identityChecked? natDecl).isSome = natDecl.checked?.isSome := + identityChecked?_isSome natDecl +example : natGenerationChecked.block = natNormalizedChecked := rfl +example : natGenerationChecked.block.ctorPairs.length = 2 := rfl +example : natGenerationChecked.motiveType = natChecked.motiveType := rfl +example : natGenerationChecked.minorTypes = natChecked.minorTypes := rfl +example : natGenerationChecked.recursor = natChecked.recursor := rfl +example : natGenerationChecked.generatedRules = natChecked.generatedRules := rfl + +example : natChecked.type = natType := rfl +example : natChecked.params = [] := rfl +example : natChecked.indices = [] := rfl +example : natChecked.resultLevel = .succ .zero := rfl +example : natChecked.elimination = .large := rfl +example : natChecked.kTarget = false := rfl +example : natGenerationChecked.kTarget = false := rfl +example : natChecked.constructors.length = 2 := rfl +example : natChecked.constructors[1].recursive.length = 1 := rfl +example : natChecked.constructors[1].recursive[0].fieldIndex = 0 := rfl +example : natChecked.constructors[1].recursive[0].binders = [] := rfl +example : natChecked.recursor = recConst 0 ``Nat 0 natType := rfl +example : natChecked.generatedRules = rules 0 ``Nat 0 natType := rfl +example : natChecked.type.type.LevelWF natDecl.uvars := natChecked.type_levelWF +example : ∀ c ∈ natChecked.type.ctors, c.type.LevelWF natDecl.uvars := + fun _ hc => natChecked.ctor_levelWF hc + +/-- The generated recursor is exactly the kernel's `Nat.rec`. -/ +example : recConst 0 ``Nat 0 natType = vconst(type_of% @Nat.rec) := rfl + +/-- The generated iota rules are exactly the kernel's reduction rules for +`Nat.rec`, phrased as closed lambda-telescope defeqs like `quotDefEq`. -/ +example : (rules 0 ``Nat 0 natType)[0]? = + some (vdefeq(motive z s => @Nat.rec motive z s .zero ≡ z)) := rfl + +example : (rules 0 ``Nat 0 natType)[1]? = + some (vdefeq(motive z s n => @Nat.rec motive z s (.succ n) ≡ s n (@Nat.rec motive z s n))) := + rfl + +example : (VEnv.empty.addInduct natDecl).isSome = true := rfl + +example : (VEnv.empty.addInduct natDecl).map (·.constants ``Nat) = + some (some natType.toVConstant) := rfl + +example : (VEnv.empty.addInduct natDecl).map (·.constants ``Nat.rec) = + some (some (recConst 0 ``Nat 0 natType)) := rfl + +/-- The successor iota rule is registered in the output environment. -/ +example : ∀ env', VEnv.empty.addInduct natDecl = some env' → + env'.defeqs (rule 0 ``Nat 0 natType 1 ⟨vconst(type_of% @Nat.succ), ``Nat.succ⟩) := by + rintro env' ⟨⟩; exact .inl rfl + +/-! ## Bool -/ + +def boolType : VInductiveType where + name := ``Bool + uvars := 0 + type := vexpr(Type) + ctors := [⟨vconst(type_of% @Bool.false), ``Bool.false⟩, ⟨vconst(type_of% @Bool.true), ``Bool.true⟩] + +def boolDecl : VInductDecl := ⟨0, 0, [boolType]⟩ + +example : boolDecl.stage3 = true := rfl + +example : recConst 0 ``Bool 0 boolType = vconst(type_of% @Bool.rec) := rfl + +example : (rules 0 ``Bool 0 boolType)[0]? = + some (vdefeq(motive f t => @Bool.rec motive f t .false ≡ f)) := rfl + +example : (rules 0 ``Bool 0 boolType)[1]? = + some (vdefeq(motive f t => @Bool.rec motive f t .true ≡ t)) := rfl + +/-! ## Unit/Empty edge shapes + +`Unit` is a reducible alias for `PUnit` on this Lean revision, so the actual +one-constructor kernel metadata is recorded under `PUnit`. Together with +`Empty`, these fixtures exercise the one- and zero-constructor generation +paths without inventing an alias-level recursor that the kernel does not +declare. -/ + +def punitType : VInductiveType where + name := ``PUnit + uvars := 1 + type := vconst(type_of% @PUnit).type + ctors := [⟨vconst(type_of% @PUnit.unit), ``PUnit.unit⟩] + +def punitDecl : VInductDecl := ⟨1, 0, [punitType]⟩ + +example : punitDecl.stage3 = true := rfl + +def punitChecked : punitDecl.Checked := punitDecl.checked?.get (by decide) + +def punitGenerationChecked : GenerationChecked punitDecl := + (identityGeneration? punitDecl).get (by decide) + +example : punitChecked.params = [] := rfl +example : punitChecked.indices = [] := rfl +example : punitChecked.resultLevel = .param 0 := rfl +example : punitChecked.elimination = .large := rfl +example : punitChecked.kTarget = false := rfl +example : punitChecked.constructors.length = 1 := rfl +example : punitChecked.constructors[0].fields = [] := rfl +example : punitChecked.constructors[0].recursive = [] := rfl +example : punitGenerationChecked.block.ctorPairs.length = 1 := rfl +example : punitGenerationChecked.minorTypes.length = 1 := rfl +example : punitGenerationChecked.generatedRules.length = 1 := rfl + +/-- The one-constructor recursor retains the fresh elimination universe before +the source universe, and its sole minor occurs before the major. -/ +example : punitGenerationChecked.recursor = + permC (vconst(type_of% @PUnit.rec)) [.param 1, .param 0] := rfl + +example : punitGenerationChecked.generatedRules[0]? = + some (permE + (vdefeq((motive : PUnit.{u} → Sort v) unit => + @PUnit.rec.{v, u} motive unit @PUnit.unit.{u} ≡ unit)) + [.param 1, .param 0]) := rfl + +theorem punitDecl_wf : punitDecl.WF VEnv.empty := by + refine ⟨rfl, ?_⟩ + intro ty hty + have hty' : ty = punitType := + List.mem_singleton.1 (by simpa [punitDecl] using hty) + subst ty + refine ⟨?_, ?_⟩ + · change True + trivial + · intro c hc + have hc' := List.mem_singleton.1 hc + subst c + constructor + · change True + trivial + · exact .nil + +def punitEnv : VEnv := + (VEnv.empty.addInduct punitDecl).get (by decide) + +/-- The public checked transaction and the normalized generation core are the +same computation for this identity-normalized edge fixture. -/ +example : VEnv.empty.addInduct punitDecl = + VEnv.empty.addInductGeneration punitGenerationChecked := rfl + +theorem punitEnv_ordered : punitEnv.Ordered := + VEnv.addInductGeneration_WF .empty + ((punitChecked.wf_of_decl punitDecl_wf).identityGeneration .empty) rfl + +def emptyType : VInductiveType where + name := ``Empty + uvars := 0 + type := vconst(type_of% @Empty).type + ctors := [] + +def emptyDecl : VInductDecl := ⟨0, 0, [emptyType]⟩ + +example : emptyDecl.stage3 = true := rfl + +def emptyChecked : emptyDecl.Checked := emptyDecl.checked?.get (by decide) + +def emptyGenerationChecked : GenerationChecked emptyDecl := + (identityGeneration? emptyDecl).get (by decide) + +def emptyBlockGenerationChecked : BlockGenerationChecked emptyDecl := + (identityBlockGeneration? emptyDecl).get (by decide) + +example : emptyChecked.params = [] := rfl +example : emptyChecked.indices = [] := rfl +example : emptyChecked.resultLevel = .succ .zero := rfl +example : emptyChecked.elimination = .large := rfl +example : emptyChecked.kTarget = false := rfl +example : emptyChecked.constructors = [] := rfl +example : emptyGenerationChecked.block.ctorPairs = [] := rfl +example : emptyGenerationChecked.minorTypes = [] := rfl +example : emptyGenerationChecked.generatedRules = [] := rfl + +/-- Empty elimination has a motive and major but no constructor minor. -/ +example : emptyGenerationChecked.recursor = + vconst(type_of% @Empty.rec) := rfl + +theorem emptyDecl_wf : emptyDecl.WF VEnv.empty := by + refine ⟨rfl, ?_⟩ + intro ty hty + have hty' : ty = emptyType := + List.mem_singleton.1 (by simpa [emptyDecl] using hty) + subst ty + refine ⟨?_, ?_⟩ + · change True + trivial + · intro c hc + simp [emptyType] at hc + +def emptyEnv : VEnv := + (VEnv.empty.addInduct emptyDecl).get (by decide) + +example : VEnv.empty.addInduct emptyDecl = + VEnv.empty.addInductGeneration emptyGenerationChecked := rfl + +theorem emptyEnv_ordered : emptyEnv.Ordered := + VEnv.addInductGeneration_WF .empty + ((emptyChecked.wf_of_decl emptyDecl_wf).identityGeneration .empty) rfl + +/-! ## List: one parameter, a dependent field, direct recursion -/ + +def listType : VInductiveType where + name := ``List + uvars := 1 + type := vconst(type_of% @List).type + ctors := [⟨vconst(type_of% @List.nil), ``List.nil⟩, ⟨vconst(type_of% @List.cons), ``List.cons⟩] + +def listDecl : VInductDecl := ⟨1, 1, [listType]⟩ + +example : listDecl.stage3 = true := rfl + +/-- The generated recursor is exactly the kernel's `List.rec`, with the +occurrence-ordered `vconst` universes permuted to the kernel's +elimination-level-first convention. -/ +example : recConst 1 ``List 1 listType = + permC (vconst(type_of% @List.rec)) [.param 1, .param 0] := rfl + +example : (rules 1 ``List 1 listType)[0]? = + some (permE (vdefeq(α motive n c => @List.rec α motive n c (@List.nil α) ≡ n)) + [.param 1, .param 0]) := rfl + +example : (rules 1 ``List 1 listType)[1]? = + some (permE (vdefeq(α motive n c hd tl => + @List.rec α motive n c (@List.cons α hd tl) ≡ + c hd tl (@List.rec α motive n c tl))) + [.param 1, .param 0]) := rfl + +example : (VEnv.empty.addInduct listDecl).isSome = true := rfl + +/-! ## Prod: two parameters, no recursion -/ + +def prodType : VInductiveType where + name := ``Prod + uvars := 2 + type := vconst(type_of% @Prod).type + ctors := [⟨vconst(type_of% @Prod.mk), ``Prod.mk⟩] + +def prodDecl : VInductDecl := ⟨2, 2, [prodType]⟩ + +example : prodDecl.stage3 = true := rfl + +example : recConst 2 ``Prod 2 prodType = + permC (vconst(type_of% @Prod.rec)) [.param 1, .param 2, .param 0] := rfl + +example : (rules 2 ``Prod 2 prodType)[0]? = + some (permE (vdefeq(α β motive mk a b => + @Prod.rec α β motive mk (@Prod.mk α β a b) ≡ mk a b)) + [.param 1, .param 2, .param 0]) := rfl + +/-! ## Option: one parameter, two constructors -/ + +def optionType : VInductiveType where + name := ``Option + uvars := 1 + type := vconst(type_of% @Option).type + ctors := [⟨vconst(type_of% @Option.none), ``Option.none⟩, + ⟨vconst(type_of% @Option.some), ``Option.some⟩] + +def optionDecl : VInductDecl := ⟨1, 1, [optionType]⟩ + +example : optionDecl.stage3 = true := rfl + +example : recConst 1 ``Option 1 optionType = + permC (vconst(type_of% @Option.rec)) [.param 1, .param 0] := rfl + +example : (rules 1 ``Option 1 optionType)[0]? = + some (permE (vdefeq(α motive n s => @Option.rec α motive n s (@Option.none α) ≡ n)) + [.param 1, .param 0]) := rfl + +example : (rules 1 ``Option 1 optionType)[1]? = + some (permE (vdefeq(α motive n s a => @Option.rec α motive n s (@Option.some α a) ≡ s a)) + [.param 1, .param 0]) := rfl + +/-! ## Eq: parameters, one index, Prop-valued with subsingleton +elimination -/ + +def eqType : VInductiveType where + name := ``Eq + uvars := 1 + type := vconst(type_of% @Eq).type + ctors := [⟨vconst(type_of% @Eq.refl), ``Eq.refl⟩] + +def eqDecl : VInductDecl := ⟨1, 2, [eqType]⟩ + +example : eqDecl.stage3 = true := rfl + +def eqChecked : eqDecl.Checked := eqDecl.checked?.get (by decide) + +def eqGenerationChecked : GenerationChecked eqDecl := + (identityGeneration? eqDecl).get (by decide) + +example : eqChecked.params = [.sort (.param 0), .bvar 0] := rfl +example : eqChecked.indices = [.bvar 1] := rfl +example : eqChecked.resultLevel = .zero := rfl +example : eqChecked.elimination = .large := rfl +example : eqChecked.kTarget = true := rfl +example : eqGenerationChecked.kTarget = true := rfl +example : eqChecked.constructors[0].resultIndices = [.bvar 0] := rfl +example : eqChecked.recursor = recConst 1 ``Eq 2 eqType := rfl +example : eqGenerationChecked.motiveType = eqChecked.motiveType := rfl +example : eqGenerationChecked.minorTypes = eqChecked.minorTypes := rfl +example : eqGenerationChecked.recursor = eqChecked.recursor := rfl +example : eqGenerationChecked.generatedRules = eqChecked.generatedRules := rfl + +/-- The generated recursor is exactly the kernel's `Eq.rec`. -/ +example : recConst 1 ``Eq 2 eqType = + permC (vconst(type_of% @Eq.rec)) [.param 1, .param 0] := rfl + +example : (rules 1 ``Eq 2 eqType)[0]? = + some (permE (vdefeq(α a motive r => + @Eq.rec α a motive r a (@Eq.refl α a) ≡ r)) [.param 1, .param 0]) := rfl + +example : (VEnv.empty.addInduct eqDecl).isSome = true := rfl + +/-! ## HEq: two indices, one of them a sort -/ + +def heqType : VInductiveType where + name := ``HEq + uvars := 1 + type := vconst(type_of% @HEq).type + ctors := [⟨vconst(type_of% @HEq.refl), ``HEq.refl⟩] + +def heqDecl : VInductDecl := ⟨1, 2, [heqType]⟩ + +example : heqDecl.stage3 = true := rfl + +example : recConst 1 ``HEq 2 heqType = + permC (vconst(type_of% @HEq.rec)) [.param 1, .param 0] := rfl + +example : (rules 1 ``HEq 2 heqType)[0]? = + some (permE (vdefeq((α : Sort u) (a : α) + (motive : {β : Sort u} → (b : β) → HEq a b → Sort v) + (r : @motive α a (@HEq.refl α a)) => + @HEq.rec α a (@motive) r α a (@HEq.refl α a) ≡ r)) [.param 1, .param 0]) := rfl + +/-! ## IndexedVec: recursive occurrence at a changing index -/ + +inductive IndexedVec (α : Type u) : Nat → Type u where + | nil : IndexedVec α Nat.zero + | cons {n} : α → IndexedVec α n → IndexedVec α (Nat.succ n) + +def indexedVecType : VInductiveType where + name := ``IndexedVec + uvars := 1 + type := vconst(type_of% @IndexedVec).type + ctors := [⟨vconst(type_of% @IndexedVec.nil), ``IndexedVec.nil⟩, + ⟨vconst(type_of% @IndexedVec.cons), ``IndexedVec.cons⟩] + +def indexedVecDecl : VInductDecl := ⟨1, 1, [indexedVecType]⟩ + +example : indexedVecDecl.stage3 = true := rfl + +def indexedVecChecked : indexedVecDecl.Checked := + indexedVecDecl.checked?.get (by decide) + +def indexedVecGenerationChecked : GenerationChecked indexedVecDecl := + (identityGeneration? indexedVecDecl).get (by decide) + +example : indexedVecChecked.params = [.sort (.succ (.param 0))] := rfl +example : indexedVecChecked.indices = [.const ``Nat []] := rfl +example : indexedVecChecked.resultLevel = .succ (.param 0) := rfl +example : indexedVecChecked.elimination = .large := rfl +example : indexedVecChecked.constructors.length = 2 := rfl +example : indexedVecChecked.constructors[1].fields.length = 3 := rfl +example : indexedVecChecked.constructors[1].recursive.length = 1 := rfl +example : indexedVecChecked.constructors[1].recursive[0].fieldIndex = 2 := rfl +example : indexedVecChecked.constructors[1].recursive[0].binders = [] := rfl +example : indexedVecChecked.constructors[1].recursive[0].targetType = 0 := rfl +example : indexedVecChecked.constructors[1].recursive[0].indices = [.bvar 1] := rfl +example : indexedVecChecked.constructors[1].resultIndices = + [VExpr.app (VExpr.const ``Nat.succ []) (VExpr.bvar 2)] := rfl +example : indexedVecChecked.recursor = + recConst 1 ``IndexedVec 1 indexedVecType := rfl +example : indexedVecChecked.generatedRules = + rules 1 ``IndexedVec 1 indexedVecType := rfl +example : indexedVecGenerationChecked.motiveType = + indexedVecChecked.motiveType := rfl +example : indexedVecGenerationChecked.minorTypes = + indexedVecChecked.minorTypes := rfl +example : indexedVecGenerationChecked.recursor = + indexedVecChecked.recursor := rfl +example : indexedVecGenerationChecked.generatedRules = + indexedVecChecked.generatedRules := rfl + +/-- Semantic clients can migrate from the legacy declaration-level contract +to the normalized descriptor without re-analyzing its syntax. -/ +example {env : VEnv} (h : indexedVecDecl.WF env) : + indexedVecChecked.WF env := + indexedVecChecked.wf_of_decl h + +/-- The descriptor contract remains definitionally compatible with existing +preservation clients while that migration is in progress. -/ +example {env : VEnv} (h : indexedVecChecked.WF env) : + indexedVecDecl.WF env := + indexedVecChecked.to_declWF rfl h + +example : recConst 1 ``IndexedVec 1 indexedVecType = + permC (vconst(type_of% @IndexedVec.rec)) [.param 1, .param 0] := rfl + +example : (rules 1 ``IndexedVec 1 indexedVecType)[0]? = + some (permE (vdefeq((α : Type u) + (motive : (n : Nat) → IndexedVec α n → Sort v) + (nil : motive Nat.zero (@IndexedVec.nil α)) + (cons : {n : Nat} → (a : α) → (as : IndexedVec α n) → + motive n as → motive (Nat.succ n) (@IndexedVec.cons α n a as)) => + @IndexedVec.rec α motive nil (@cons) Nat.zero (@IndexedVec.nil α) ≡ nil)) + [.param 1, .param 0]) := rfl + +example : (rules 1 ``IndexedVec 1 indexedVecType)[1]? = + some (permE (vdefeq((α : Type u) + (motive : (n : Nat) → IndexedVec α n → Sort v) + (nil : motive Nat.zero (@IndexedVec.nil α)) + (cons : {n : Nat} → (a : α) → (as : IndexedVec α n) → + motive n as → motive (Nat.succ n) (@IndexedVec.cons α n a as)) + (n : Nat) (a : α) (as : IndexedVec α n) => + @IndexedVec.rec α motive nil (@cons) (Nat.succ n) + (@IndexedVec.cons α n a as) ≡ + @cons n a as (@IndexedVec.rec α motive nil (@cons) n as))) + [.param 1, .param 0]) := rfl + +example : (VEnv.empty.addInduct indexedVecDecl).isSome = true := rfl + +/-- Consumers obtain constructor lookup without unfolding the transactional +constructor fold. -/ +example : ∀ env', VEnv.empty.addInduct indexedVecDecl = some env' → + env'.constants ``IndexedVec.cons = some indexedVecType.ctors[1].toVConstant := by + intro env' hadd + exact VEnv.addInduct_ctor_lookup hadd (.head _) (.tail _ (.head _)) + +/-- The complete all-or-nothing postcondition is available from the same +successful call. -/ +example : ∀ env', VEnv.empty.addInduct indexedVecDecl = some env' → + VEnv.AddInductSuccess VEnv.empty env' indexedVecDecl := + fun _ => VEnv.addInduct_success + +/-- The transaction certificate exposes the exact block-generation result for +ix-like consumers without re-running structural analysis. -/ +example : ∀ env', VEnv.empty.addInduct indexedVecDecl = some env' → + ∃ generation, + indexedVecDecl.identityBlockGeneration? = some generation := + fun _ => VEnv.addInduct_generation + +/-! ## Acc: recursive argument beneath a Pi telescope + +`Acc.intro`'s recursive field is a function taking two arguments, and its +induction hypothesis is itself a two-argument function. This fixture exercises +the complete public checked/generation/transaction path, not a side generator. -/ + +def accType : VInductiveType where + name := ``Acc + uvars := 1 + type := vconst(type_of% @Acc).type + ctors := [⟨vconst(type_of% @Acc.intro), ``Acc.intro⟩] + +def accDecl : VInductDecl := ⟨1, 2, [accType]⟩ + +example : accDecl.stage3 = true := rfl + +def accChecked : accDecl.Checked := accDecl.checked?.get (by decide) + +def accGenerationChecked : GenerationChecked accDecl := + (identityGeneration? accDecl).get (by decide) + +def accBlockGenerationChecked : BlockGenerationChecked accDecl := + (identityBlockGeneration? accDecl).get (by decide) + +def accRecArgs : List RecArg := + recArgs 1 ``Acc 2 1 (ctorFields (VExpr.dropN 2 accType.ctors[0].type)) + +example : (ctorFields (VExpr.dropN 2 accType.ctors[0].type)).length = 2 := rfl +example : accRecArgs.length = 1 := rfl +example : accRecArgs[0].fieldIndex = 1 := rfl +example : accRecArgs[0].binders.length = 2 := rfl +example : accRecArgs[0].targetType = 0 := rfl +example : accRecArgs[0].indices = [.bvar 1] := rfl +example : accChecked.type = accType := rfl +example : accChecked.constructors[0].recursive = accRecArgs := rfl +example : accChecked.recursor = recConstRec 1 ``Acc 2 accType := rfl +example : accChecked.generatedRules = rulesRec 1 ``Acc 2 accType := rfl +example : accGenerationChecked.motiveType = accChecked.motiveType := rfl +example : accGenerationChecked.minorTypes = accChecked.minorTypes := rfl +example : accGenerationChecked.recursor = accChecked.recursor := rfl +example : accGenerationChecked.generatedRules = accChecked.generatedRules := rfl + +/-- The generalized recursor type is definitionally the kernel's `Acc.rec`, +including its functional induction hypothesis. -/ +example : recConstRec 1 ``Acc 2 accType = + permC (vconst(type_of% @Acc.rec)) [.param 1, .param 0] := rfl + +/-- The generalized iota RHS recurs under both binders of the recursive +function argument, exactly as Lean's kernel rule does. -/ +example : (rulesRec 1 ``Acc 2 accType)[0]? = + some (permE (vdefeq((α : Sort u) (r : α → α → Prop) + (motive : (a : α) → Acc r a → Sort v) + (intro : (a : α) → (h : (b : α) → r b a → Acc r b) → + ((b : α) → (hba : r b a) → motive b (h b hba)) → + motive a (@Acc.intro α r a h)) + (a : α) (h : (b : α) → r b a → Acc r b) => + @Acc.rec α r motive intro a (@Acc.intro α r a h) ≡ + intro a h (fun b hba => @Acc.rec α r motive intro b (h b hba)))) + [.param 1, .param 0]) := rfl + +/-- Recursive-Pi declarations now run through the same all-or-nothing public +transaction as direct recursive declarations. -/ +example : (VEnv.empty.addInduct accDecl).isSome = true := rfl + +example : (VEnv.empty.addInduct accDecl).map (·.constants ``Acc.rec) = + some (some (recConstRec 1 ``Acc 2 accType)) := rfl + +example : ∀ env', VEnv.empty.addInduct accDecl = some env' → + env'.defeqs (ruleRec 1 ``Acc 2 accType 0 accType.ctors[0]) := by + intro env' hadd + apply VEnv.addInduct_rule_mem hadd + (generation := accBlockGenerationChecked) rfl + change ruleRec 1 ``Acc 2 accType 0 accType.ctors[0] ∈ + accBlockGenerationChecked.generatedRules + exact .head _ + +example : ∀ env', VEnv.empty.addInduct accDecl = some env' → + VEnv.AddInductSuccess VEnv.empty env' accDecl := + fun _ => VEnv.addInduct_success + +/-- `Acc` satisfies the semantic declaration contract, including the +impredicative-Prop exception for its universe-polymorphic index field and the +typed telescope beneath its recursive Pi argument. -/ +theorem accDecl_wf : accDecl.WF VEnv.empty := by + refine ⟨rfl, ?_⟩ + intro ty hty + have hty' : ty = accType := List.mem_singleton.1 (by simpa [accDecl] using hty) + subst ty + refine ⟨?_, ?_⟩ + · change VEnv.empty.OnTel 1 [] + [.sort (.param 0), + .forallE (.bvar 0) (.forallE (.bvar 1) (.sort .zero)), + .bvar 1] + exact ⟨⟨_, by type_tac⟩, ⟨⟨_, by type_tac⟩, ⟨⟨_, by type_tac⟩, trivial⟩⟩⟩ + · intro c hc + have hc' := List.mem_singleton.1 hc + subst c + constructor + · change fieldsWF 1 ``Acc 2 VEnv.empty .zero [.bvar 1] + [.forallE (.bvar 0) (.forallE (.bvar 1) (.sort .zero)), .sort (.param 0)] 0 + [.bvar 1, + .forallE (.bvar 2) + (.forallE (.app (.app (.bvar 2) (.bvar 0)) (.bvar 1)) + (.app (.app (.app (.const ``Acc [.param 0]) (.bvar 4)) (.bvar 3)) + (.bvar 1)))] + refine ⟨?_, ?_, ?_⟩ + · exact .inr (.inr ⟨rfl, .param 0, by type_tac, .inl rfl⟩) + · intro h + change false = true at h + contradiction + · dsimp only [fieldsWF] + refine ⟨?_, ?_, trivial⟩ + · refine .inr (.inl ⟨accRecArgs[0], rfl, ?_, ?_⟩) + · decide + · change + VEnv.empty.OnTel 1 + [.bvar 1, + .forallE (.bvar 0) (.forallE (.bvar 1) (.sort .zero)), + .sort (.param 0)] + [.bvar 2, .app (.app (.bvar 2) (.bvar 0)) (.bvar 1)] ∧ + VEnv.empty.SpineWF 1 + [.app (.app (.bvar 2) (.bvar 0)) (.bvar 1), .bvar 2, + .bvar 1, + .forallE (.bvar 0) (.forallE (.bvar 1) (.sort .zero)), + .sort (.param 0)] + (.forallE (.bvar 4) (.sort .zero)) [.bvar 1] (.sort .zero) + constructor + · exact ⟨⟨_, by type_tac⟩, ⟨⟨_, by type_tac⟩, trivial⟩⟩ + · exact .cons (by type_tac) .nil + · intro h + change false = true at h + contradiction + · change VEnv.empty.SpineWF 1 + [.forallE (.bvar 2) + (.forallE (.app (.app (.bvar 2) (.bvar 0)) (.bvar 1)) + (.app (.app (.app (.const ``Acc [.param 0]) (.bvar 4)) (.bvar 3)) + (.bvar 1))), + .bvar 1, + .forallE (.bvar 0) (.forallE (.bvar 1) (.sort .zero)), + .sort (.param 0)] + (.forallE (.bvar 3) (.sort .zero)) [.bvar 1] (.sort .zero) + exact .cons (by type_tac) .nil + +/-- The concrete public Acc transaction preserves environment order. -/ +def accEnv : VEnv := (VEnv.empty.addInduct accDecl).get (by decide) + +example : accEnv.Ordered := + VEnv.addInductGeneration_WF .empty + ((accChecked.wf_of_decl accDecl_wf).identityGeneration .empty) rfl + +/-- A collision at the generated recursor name still rejects the whole Acc +transaction; no recursive-Pi special case bypasses freshness. -/ +def accRecCollisionEnv : VEnv := + (VEnv.empty.addConst ``Acc.rec ⟨0, .sort .zero⟩).get (by decide) + +example : accRecCollisionEnv.addInduct accDecl = none := + VEnv.addInduct_eq_none_of_rec_present + (generation := accBlockGenerationChecked) rfl (.head _) ⟨_, rfl⟩ + +/-! ## AnnotatedPi: recursive Pi normalization below a constructor field + +Lean retains `outParam` in the constructor's raw recursive-function domain, +while inductive analysis consumes it before recognizing the recursive target. +This fixture combines the annotation and recursive-Pi seams in one declaration +and keeps the raw binder syntax in generated artifacts. -/ + +inductive AnnotatedPi : Type where + | mk : ((p : outParam Prop) → AnnotatedPi) → AnnotatedPi + +def outParamDefEq : VDefEq := + vdefeq(@outParam ≡ fun (α : Sort u) => α) + +def outParamConstEnv : VEnv := + (VEnv.empty.addConst ``outParam (vconst(type_of% @outParam))).get + (by decide) + +def outParamEnv : VEnv := outParamConstEnv.addDefEq outParamDefEq + +theorem outParamConstant_wf : + (vconst(type_of% @outParam) : VConstant).WF VEnv.empty := by + exact ⟨_, VEnv.HasType.forallE + (VEnv.HasType.sort (by decide)) + (VEnv.HasType.sort (by decide))⟩ + +theorem outParamConstEnv_ordered : outParamConstEnv.Ordered := by + apply VEnv.Ordered.const VEnv.Ordered.empty + (ci := vconst(type_of% @outParam)) + · exact outParamConstant_wf + · rfl + +theorem outParamEnv_ordered : outParamEnv.Ordered := by + apply VEnv.Ordered.defeq outParamConstEnv_ordered + constructor + · exact VEnv.HasType.const0 rfl + (outParamConstant_wf.mono + (VEnv.addConst_le (by rfl : + VEnv.empty.addConst ``outParam (vconst(type_of% @outParam)) = + some outParamConstEnv))) + · exact VEnv.HasType.lam + (VEnv.HasType.sort (by decide)) + (VEnv.HasType.bvar .zero) + +def annotatedPiRawType : VInductiveType where + name := ``AnnotatedPi + uvars := 0 + type := vconst(type_of% @AnnotatedPi).type + ctors := [⟨vconst(type_of% @AnnotatedPi.mk), ``AnnotatedPi.mk⟩] + +def annotatedPiRawDecl : VInductDecl := ⟨0, 0, [annotatedPiRawType]⟩ + +def annotatedPiViewCtor : VConstVal where + name := ``AnnotatedPi.mk + uvars := 0 + type := .forallE + (.forallE (.sort .zero) (.const ``AnnotatedPi [])) + (.const ``AnnotatedPi []) + +def annotatedPiViewType : VInductiveType := + { annotatedPiRawType with ctors := [annotatedPiViewCtor] } + +def annotatedPiViewDecl : VInductDecl := ⟨0, 0, [annotatedPiViewType]⟩ + +example : annotatedPiRawType.ctors[0].type = + .forallE + (.forallE + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) + (.const ``AnnotatedPi [])) + (.const ``AnnotatedPi []) := rfl + +example : annotatedPiViewDecl.checked?.isSome = true := rfl +example : normalizationShape annotatedPiRawDecl annotatedPiViewDecl = true := + rfl + +def annotatedPiNormalization : Normalization annotatedPiRawDecl where + view := annotatedPiViewDecl + shape_eq := rfl + +def annotatedPiViewChecked : annotatedPiViewDecl.Checked := + annotatedPiViewDecl.checked?.get (by decide) + +def annotatedPiBlock : NormalizedChecked annotatedPiRawDecl := + annotatedPiNormalization.check?.get (by decide) + +def annotatedPiGenerationChecked : GenerationChecked annotatedPiRawDecl := + annotatedPiBlock.generation?.get (by decide) + +def annotatedPiRecArg : RecArg where + fieldIndex := 0 + binders := [.sort .zero] + targetType := 0 + indices := [] + +example : annotatedPiViewChecked.constructors[0].recursive = + [annotatedPiRecArg] := rfl + +example : annotatedPiGenerationChecked.block.ctorPairs[0].rawFields 0 = + [.forallE + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) + (.const ``AnnotatedPi [])] := rfl + +example : annotatedPiGenerationChecked.recursor = + vconst(type_of% @AnnotatedPi.rec) := rfl + +example : annotatedPiGenerationChecked.generatedRules[0].rhs = + (vdefeq((motive : AnnotatedPi → Sort u) + (mk : (f : (p : outParam Prop) → AnnotatedPi) → + ((p : Prop) → motive (f p)) → motive (@AnnotatedPi.mk f)) + (f : (p : outParam Prop) → AnnotatedPi) => + @AnnotatedPi.rec motive mk (@AnnotatedPi.mk f) ≡ + mk f (fun p => @AnnotatedPi.rec motive mk (f p)))).rhs := rfl + +/-- The normalized recursive-Pi view is semantically well formed without +using the annotation definition; the raw-to-view bridge is supplied later by +the exact checker candidate. -/ +theorem annotatedPiViewDecl_wf : annotatedPiViewDecl.WF VEnv.empty := by + refine ⟨rfl, ?_⟩ + intro ty hty + have hty' : ty = annotatedPiViewType := + List.mem_singleton.1 (by simpa [annotatedPiViewDecl] using hty) + subst ty + refine ⟨by trivial, ?_⟩ + intro c hc + have hc' : c = annotatedPiViewCtor := + List.mem_singleton.1 (by simpa [annotatedPiViewType] using hc) + subst c + constructor + · change fieldsWF 0 ``AnnotatedPi 0 VEnv.empty (.succ .zero) [] [] 0 + [.forallE (.sort .zero) (.const ``AnnotatedPi [])] + refine ⟨?_, ?_, trivial⟩ + · right + left + refine ⟨annotatedPiRecArg, ?_, ?_, ?_⟩ + · rfl + · simp [annotatedPiRecArg] + · exact ⟨⟨⟨_, VEnv.HasType.sort (by decide)⟩, trivial⟩, .nil⟩ + · intro h + change false = true at h + contradiction + · change VEnv.empty.SpineWF 0 + [.forallE (.sort .zero) (.const ``AnnotatedPi [])] + (.sort (.succ .zero)) [] (.sort (.succ .zero)) + exact .nil + +theorem annotatedPiViewChecked_wf : + annotatedPiViewChecked.WF outParamEnv := by + apply VInductDecl.Checked.WF.mono + ((VEnv.addConst_le (by rfl : + VEnv.empty.addConst ``outParam (vconst(type_of% @outParam)) = + some outParamConstEnv)).trans VEnv.addDefEq_le) + exact annotatedPiViewChecked.wf_of_decl annotatedPiViewDecl_wf + +/-! ## AnnotatedParam: definitionally equal constructor parameters + +Family validation consumes the `outParam` annotation before recording its +parameter local. Constructor metadata retains the annotation, so the ordinary +validator must use definitional equality rather than syntax when it checks the +constructor's parameter prefix. -/ + +inductive AnnotatedParam (alpha : outParam Type) : Type where + | mk : AnnotatedParam alpha + +def annotatedParamRawType : VInductiveType where + name := ``AnnotatedParam + uvars := 0 + type := vconst(type_of% @AnnotatedParam).type + ctors := [⟨vconst(type_of% @AnnotatedParam.mk), ``AnnotatedParam.mk⟩] + +def annotatedParamRawDecl : VInductDecl := + ⟨0, 1, [annotatedParamRawType]⟩ + +def annotatedParamViewCtor : VConstVal where + name := ``AnnotatedParam.mk + uvars := 0 + type := .forallE (.sort (.succ .zero)) + (.app (.const ``AnnotatedParam []) (.bvar 0)) + +def annotatedParamViewType : VInductiveType where + name := ``AnnotatedParam + uvars := 0 + type := .forallE (.sort (.succ .zero)) (.sort (.succ .zero)) + ctors := [annotatedParamViewCtor] + +def annotatedParamViewDecl : VInductDecl := + ⟨0, 1, [annotatedParamViewType]⟩ + +example : annotatedParamRawType.type = + .forallE + (.app (.const ``outParam [.succ (.succ .zero)]) + (.sort (.succ .zero))) + (.sort (.succ .zero)) := rfl + +example : annotatedParamRawType.ctors[0].type = + .forallE + (.app (.const ``outParam [.succ (.succ .zero)]) + (.sort (.succ .zero))) + (.app (.const ``AnnotatedParam []) (.bvar 0)) := rfl + +example : annotatedParamViewDecl.checked?.isSome = true := rfl +example : normalizationShape annotatedParamRawDecl annotatedParamViewDecl = + true := rfl + +def annotatedParamNormalization : Normalization annotatedParamRawDecl where + view := annotatedParamViewDecl + shape_eq := rfl + +def annotatedParamViewChecked : annotatedParamViewDecl.Checked := + annotatedParamViewDecl.checked?.get (by decide) + +def annotatedParamBlock : NormalizedChecked annotatedParamRawDecl := + annotatedParamNormalization.check?.get (by decide) + +def annotatedParamGenerationChecked : + GenerationChecked annotatedParamRawDecl := + annotatedParamBlock.generation?.get (by decide) + +example : annotatedParamGenerationChecked.recursor = + vconst(type_of% @AnnotatedParam.rec) := rfl + +example : annotatedParamGenerationChecked.generatedRules[0]? = + some (vdefeq((alpha : Type) + (motive : AnnotatedParam alpha → Sort u) + (mk : motive (@AnnotatedParam.mk alpha)) => + @AnnotatedParam.rec alpha motive mk (@AnnotatedParam.mk alpha) ≡ mk)) := rfl + +/-- The raw parameter domain retained by kernel metadata before annotation +consumption. -/ +def annotatedParamRawDomain : VExpr := + .app (.const ``outParam [.succ (.succ .zero)]) + (.sort (.succ .zero)) + +/-- The stored `outParam Type` parameter and the checked `Type` parameter are +definitionally equal in the exact pre-declaration environment. -/ +theorem annotatedParamRawDomain_defeq : + outParamEnv.IsDefEq 0 [] annotatedParamRawDomain + (.sort (.succ .zero)) (.sort (.succ (.succ .zero))) := by + have hfn : outParamEnv.IsDefEq 0 [] + (.const ``outParam [.succ (.succ .zero)]) + (.lam (.sort (.succ (.succ .zero))) (.bvar 0)) + (.forallE (.sort (.succ (.succ .zero))) + (.sort (.succ (.succ .zero)))) := by + simpa [outParamDefEq, VExpr.instL, VLevel.inst] using + (VEnv.IsDefEq.extra (env := outParamEnv) (uvars := 0) (Γ := []) + (df := outParamDefEq) (ls := [.succ (.succ .zero)]) + (by simp [outParamEnv, VEnv.addDefEq]) + (by simp; decide) rfl) + have harg : outParamEnv.HasType 0 [] + (.sort (.succ .zero)) (.sort (.succ (.succ .zero))) := + VEnv.HasType.sort (by decide) + exact (VEnv.IsDefEq.appDF hfn harg).trans + (VEnv.IsDefEq.beta (VEnv.HasType.bvar .zero) harg) + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.annotatedParamRawDomain_defeq [propext, Quot.sound] + +/-- The annotation-consumed declaration accepted by the structural analyzer +has the ordinary direct semantic interpretation. -/ +theorem annotatedParamViewDecl_wf : + annotatedParamViewDecl.WF VEnv.empty := by + refine ⟨rfl, ?_⟩ + intro ty hty + have hty' : ty = annotatedParamViewType := + List.mem_singleton.1 (by + simpa [annotatedParamViewDecl] using hty) + subst ty + refine ⟨?_, ?_⟩ + · change VEnv.OnTel VEnv.empty 0 [] [.sort (.succ .zero)] + exact ⟨⟨_, VEnv.HasType.sort (by decide)⟩, trivial⟩ + · intro c hc + have hc' : c = annotatedParamViewCtor := + List.mem_singleton.1 (by + simpa [annotatedParamViewType] using hc) + subst c + exact ⟨trivial, .nil⟩ + +/-- Exact Theory environment after staging the stored family constant. -/ +def annotatedParamTypeEnv : VEnv := + (outParamEnv.addConst annotatedParamRawType.name + annotatedParamRawType.toVConstant).get (by decide) + +theorem annotatedParamTypeEnv_ordered : + annotatedParamTypeEnv.Ordered := by + have hbody : outParamEnv.HasType 0 [annotatedParamRawDomain] + (.sort (.succ .zero)) (.sort (.succ (.succ .zero))) := + VEnv.HasType.sort (by decide) + apply VEnv.Ordered.const (n := annotatedParamRawType.name) + (ci := annotatedParamRawType.toVConstant) + outParamEnv_ordered + · exact ⟨.imax (.succ (.succ .zero)) (.succ (.succ .zero)), + VEnv.HasType.forallE + (VEnv.IsDefEq.hasType annotatedParamRawDomain_defeq).1 hbody⟩ + · rfl + +private theorem annotatedParamFamilyApp_hasType (domain : VExpr) + (hdomain : annotatedParamTypeEnv.HasType 0 [domain] + (.bvar 0) annotatedParamRawDomain) : + annotatedParamTypeEnv.HasType 0 [domain] + (.app (.const ``AnnotatedParam []) (.bvar 0)) + (.sort (.succ .zero)) := by + apply VEnv.HasType.app + (A := annotatedParamRawDomain) (B := .sort (.succ .zero)) + · simpa [annotatedParamRawType, annotatedParamRawDomain, + VExpr.instL, VLevel.inst] using + VEnv.HasType.const (env := annotatedParamTypeEnv) (U := 0) + (Γ := [domain]) (c := ``AnnotatedParam) + (ci := annotatedParamRawType.toVConstant) (ls := []) + (VEnv.addConst_self (show + outParamEnv.addConst annotatedParamRawType.name + annotatedParamRawType.toVConstant = + some annotatedParamTypeEnv from rfl)) + (by simp) rfl + · exact hdomain + +theorem annotatedParamRawCtorBody_hasType : + annotatedParamTypeEnv.HasType 0 [annotatedParamRawDomain] + (.app (.const ``AnnotatedParam []) (.bvar 0)) + (.sort (.succ .zero)) := by + apply annotatedParamFamilyApp_hasType + simpa [annotatedParamRawDomain, VExpr.liftN] using + (VEnv.HasType.bvar (env := annotatedParamTypeEnv) (U := 0) + (Lookup.zero (Γ := []) (ty := annotatedParamRawDomain))) + +theorem annotatedParamCheckedCtorBody_hasType : + annotatedParamTypeEnv.HasType 0 [.sort (.succ .zero)] + (.app (.const ``AnnotatedParam []) (.bvar 0)) + (.sort (.succ .zero)) := by + apply annotatedParamFamilyApp_hasType + have hb : annotatedParamTypeEnv.HasType 0 [.sort (.succ .zero)] + (.bvar 0) (.sort (.succ .zero)) := by + simpa [VExpr.liftN] using + (VEnv.HasType.bvar (env := annotatedParamTypeEnv) (U := 0) + (Lookup.zero (Γ := []) (ty := .sort (.succ .zero)))) + have hd := annotatedParamRawDomain_defeq.mono + (VEnv.addConst_le (show + outParamEnv.addConst annotatedParamRawType.name + annotatedParamRawType.toVConstant = + some annotatedParamTypeEnv from rfl)) + exact (hd.weak0 annotatedParamTypeEnv_ordered).symm.defeq hb + +/-- The raw family and constructor metadata are semantically related to the +annotation-consumed analyzer view at their exact declaration stages. -/ +theorem annotatedParamNormalization_wf : + annotatedParamNormalization.WF outParamEnv := by + refine ⟨annotatedParamRawType, annotatedParamViewType, + rfl, rfl, ?_, ?_⟩ + · have hbody : outParamEnv.HasType 0 [annotatedParamRawDomain] + (.sort (.succ .zero)) (.sort (.succ (.succ .zero))) := + VEnv.HasType.sort (by decide) + exact ⟨_, VEnv.IsDefEq.forallEDF + annotatedParamRawDomain_defeq hbody⟩ + · intro envT hadd + have henv : envT = annotatedParamTypeEnv := by + have : some envT = some annotatedParamTypeEnv := + hadd.symm.trans (show + outParamEnv.addConst annotatedParamRawType.name + annotatedParamRawType.toVConstant = + some annotatedParamTypeEnv from rfl) + exact Option.some.inj this + subst envT + exact .cons ⟨_, VEnv.IsDefEq.forallEDF + (annotatedParamRawDomain_defeq.mono + (VEnv.addConst_le (show + outParamEnv.addConst annotatedParamRawType.name + annotatedParamRawType.toVConstant = + some annotatedParamTypeEnv from rfl))) + annotatedParamRawCtorBody_hasType⟩ .nil + +theorem annotatedParamViewChecked_wf : + annotatedParamViewChecked.WF outParamEnv := by + apply VInductDecl.Checked.WF.mono + ((VEnv.addConst_le (by rfl : + VEnv.empty.addConst ``outParam (vconst(type_of% @outParam)) = + some outParamConstEnv)).trans VEnv.addDefEq_le) + exact annotatedParamViewChecked.wf_of_decl annotatedParamViewDecl_wf + +/-- The mixed generation value uses the checked parameter for emitted +recursor binders while retaining the raw constructor surface, and all four +raw/view telescope/result obligations hold in their respective contexts. -/ +theorem annotatedParamGenerationChecked_wf : + annotatedParamGenerationChecked.WF outParamEnv := by + refine { + blockWF := ⟨annotatedParamNormalization_wf, + annotatedParamViewChecked_wf⟩ + familyTel := ?_ + familyResult := ?_ + ctors := ?_ } + · change outParamEnv.TelDefEq 0 [] [annotatedParamRawDomain] + [.sort (.succ .zero)] + exact ⟨⟨_, annotatedParamRawDomain_defeq⟩, trivial⟩ + · exact VEnv.HasType.sort (by decide) + · intro envT hadd ctor hctor + have henv : envT = annotatedParamTypeEnv := by + have : some envT = some annotatedParamTypeEnv := + hadd.symm.trans (show + outParamEnv.addConst annotatedParamRawType.name + annotatedParamRawType.toVConstant = + some annotatedParamTypeEnv from rfl) + exact Option.some.inj this + subst envT + change ctor ∈ [⟨annotatedParamRawType.ctors[0], + annotatedParamViewChecked.constructors[0]⟩] at hctor + obtain rfl := List.mem_singleton.1 hctor + refine { + declaredTel := ⟨⟨_, annotatedParamRawDomain_defeq.mono + (VEnv.addConst_le (show + outParamEnv.addConst annotatedParamRawType.name + annotatedParamRawType.toVConstant = + some annotatedParamTypeEnv from rfl))⟩, trivial⟩ + declaredResult := annotatedParamRawCtorBody_hasType + emittedTel := ⟨⟨_, VEnv.HasType.sort (by decide)⟩, trivial⟩ + emittedResult := annotatedParamCheckedCtorBody_hasType } + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.annotatedParamGenerationChecked_wf [propext, Quot.sound] + +/-! ## Explicit normalization boundary + +Lean stores reducible aliases in inductive metadata even though +`checkInductiveTypes`, positivity, and recursive-argument recognition inspect +their WHNF. These kernel-accepted declarations demonstrate why raw metadata +cannot simply be assumed normalized. Their explicit views exercise the new +environment-independent `Normalization` boundary; semantic defeq evidence and +raw-syntax-preserving artifact generation remain separate obligations. +-/ + +abbrev TypeFamilyAlias := Type + +inductive AliasFormer : TypeFamilyAlias where + | mk : AliasFormer + +def aliasFormerRawType : VInductiveType where + name := ``AliasFormer + uvars := 0 + type := vconst(type_of% @AliasFormer).type + ctors := [⟨vconst(type_of% @AliasFormer.mk), ``AliasFormer.mk⟩] + +def aliasFormerRawDecl : VInductDecl := ⟨0, 0, [aliasFormerRawType]⟩ + +example : aliasFormerRawType.type = .const ``TypeFamilyAlias [] := rfl +example : aliasFormerRawDecl.checked? = none := rfl + +def aliasFormerViewType : VInductiveType := + { aliasFormerRawType with type := .sort (.succ .zero) } + +def aliasFormerViewDecl : VInductDecl := ⟨0, 0, [aliasFormerViewType]⟩ + +example : aliasFormerViewDecl.checked?.isSome = true := rfl +example : normalizationShape aliasFormerRawDecl aliasFormerViewDecl = true := rfl + +def aliasFormerNormalization : Normalization aliasFormerRawDecl where + view := aliasFormerViewDecl + shape_eq := rfl + +def aliasFormerViewChecked : aliasFormerViewDecl.Checked := + aliasFormerViewDecl.checked?.get (by decide) + +def aliasFormerBlock : NormalizedChecked aliasFormerRawDecl := + aliasFormerNormalization.check?.get (by decide) + +def aliasFormerGenerationChecked : GenerationChecked aliasFormerRawDecl := + aliasFormerBlock.generation?.get (by decide) + +example : aliasFormerNormalization.accepted = true := rfl +example : (normalizedChecked? aliasFormerRawDecl aliasFormerViewDecl).isSome = true := rfl +example : aliasFormerBlock.checked.type = aliasFormerViewType := rfl +example : aliasFormerGenerationChecked.recursor = + vconst(type_of% @AliasFormer.rec) := rfl +example : aliasFormerGenerationChecked.generatedRules[0]? = + some (vdefeq(motive mk => + @AliasFormer.rec motive mk AliasFormer.mk ≡ mk)) := rfl +example : ∃ raw, + aliasFormerRawDecl.types = [raw] ∧ + raw.name = aliasFormerBlock.checked.type.name ∧ + raw.uvars = aliasFormerBlock.checked.type.uvars ∧ + List.Forall₂ CtorHeaderEq raw.ctors aliasFormerBlock.checked.type.ctors := + aliasFormerBlock.source_anatomy + +def typeFamilyAliasDefEq : VDefEq := vdefeq(TypeFamilyAlias ≡ Type) + +def typeFamilyAliasConstEnv : VEnv := + (VEnv.empty.addConst ``TypeFamilyAlias + (vconst(type_of% @TypeFamilyAlias))).get (by decide) + +def typeFamilyAliasEnv : VEnv := + typeFamilyAliasConstEnv.addDefEq typeFamilyAliasDefEq + +theorem typeFamilyAliasConstant_wf : + (vconst(type_of% @TypeFamilyAlias) : VConstant).WF + VEnv.empty := by + exact ⟨_, VEnv.HasType.sort (by decide)⟩ + +theorem typeFamilyAliasConstEnv_ordered : + typeFamilyAliasConstEnv.Ordered := by + apply VEnv.Ordered.const VEnv.Ordered.empty + (ci := vconst(type_of% @TypeFamilyAlias)) + · exact typeFamilyAliasConstant_wf + · rfl + +theorem typeFamilyAliasEnv_ordered : + typeFamilyAliasEnv.Ordered := by + apply VEnv.Ordered.defeq typeFamilyAliasConstEnv_ordered + constructor + · exact VEnv.HasType.const0 rfl + (typeFamilyAliasConstant_wf.mono + (VEnv.addConst_le (by rfl : + VEnv.empty.addConst ``TypeFamilyAlias + (vconst(type_of% @TypeFamilyAlias)) = + some typeFamilyAliasConstEnv))) + · exact VEnv.HasType.sort (by decide) + +/-- The explicit family-type view is not merely shape-compatible: the stored +alias and its WHNF are definitionally equal in the pre-environment, and the +unchanged constructor remains equal after inserting the raw family constant. +-/ +theorem aliasFormerNormalization_wf : + aliasFormerNormalization.WF typeFamilyAliasEnv := by + refine ⟨aliasFormerRawType, aliasFormerViewType, rfl, rfl, ?_, ?_⟩ + · refine ⟨_, .extra (df := typeFamilyAliasDefEq) (ls := []) ?_ + (fun _ h => nomatch h) rfl⟩ + simp [typeFamilyAliasEnv, VEnv.addDefEq] + · intro envT hadd + exact .cons + ⟨_, .constDF (VEnv.addConst_self hadd) (by simp) (by simp) rfl .nil⟩ + .nil + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasFormerNormalization_wf [propext, Quot.sound] + +/-- The normalized view is a semantically well-formed direct declaration in +the same pre-environment. -/ +theorem aliasFormerViewDecl_wf : + aliasFormerViewDecl.WF typeFamilyAliasEnv := by + refine ⟨rfl, ?_⟩ + intro ty hty + have hty' : ty = aliasFormerViewType := + List.mem_singleton.1 (by simpa [aliasFormerViewDecl] using hty) + subst ty + refine ⟨?_, ?_⟩ + · trivial + · intro c hc + have hc' : c = aliasFormerRawType.ctors[0] := + List.mem_singleton.1 (by + simpa [aliasFormerViewType, aliasFormerRawType] using hc) + subst c + exact ⟨trivial, .nil⟩ + +/-- The paired block carries both the semantic normalization certificate and +the checked normalized view required by downstream generation. -/ +theorem aliasFormerBlock_wf : aliasFormerBlock.WF typeFamilyAliasEnv := by + refine ⟨aliasFormerNormalization_wf, ?_⟩ + change aliasFormerViewChecked.WF typeFamilyAliasEnv + exact aliasFormerViewChecked.wf_of_decl aliasFormerViewDecl_wf + +theorem aliasFormerGenerationChecked_wf : + aliasFormerGenerationChecked.WF typeFamilyAliasEnv := by + refine { + blockWF := aliasFormerBlock_wf + familyTel := by trivial + familyResult := ?_ + ctors := ?_ } + · change typeFamilyAliasEnv.IsDefEq 0 [] + (.const ``TypeFamilyAlias []) (.sort (.succ .zero)) + (.sort (.succ (.succ .zero))) + exact .extra (df := typeFamilyAliasDefEq) (ls := []) + (by simp [typeFamilyAliasEnv, VEnv.addDefEq]) + (fun _ h => nomatch h) rfl + · intro envT hadd ctor hctor + change ctor ∈ + [⟨aliasFormerRawType.ctors[0], aliasFormerViewChecked.constructors[0]⟩] + at hctor + obtain rfl := List.mem_singleton.1 hctor + have hfamily₀ : envT.HasType 0 [] + (.const aliasFormerGenerationChecked.block.sourceType.name []) + (aliasFormerGenerationChecked.block.sourceType.type.instL []) := + .const (VEnv.addConst_self hadd) (by simp) (by rfl) + change envT.HasType 0 [] (.const ``AliasFormer []) + (.const ``TypeFamilyAlias []) at hfamily₀ + have halias₀ : typeFamilyAliasEnv.IsDefEq 0 [] + (.const ``TypeFamilyAlias []) (.sort (.succ .zero)) + (.sort (.succ (.succ .zero))) := + .extra (df := typeFamilyAliasDefEq) (ls := []) + (by simp [typeFamilyAliasEnv, VEnv.addDefEq]) + (fun _ h => nomatch h) rfl + have halias := halias₀.mono (VEnv.addConst_le hadd) + have hfamily : envT.IsDefEq 0 [] + (.const ``AliasFormer []) (.const ``AliasFormer []) + (.sort (.succ .zero)) := + halias.defeq hfamily₀ + exact { + declaredTel := by trivial + declaredResult := hfamily + emittedTel := by trivial + emittedResult := hfamily } + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasFormerBlock_wf [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasFormerGenerationChecked_wf [propext, Quot.sound] + +/-- The non-identity family-alias case executes the same normalized +transaction used by the public identity wrapper. -/ +def aliasFormerFinalEnv : VEnv := + (typeFamilyAliasEnv.addInductGeneration + aliasFormerGenerationChecked).get (by decide) + +theorem aliasFormer_addInductGeneration : + typeFamilyAliasEnv.addInductGeneration + aliasFormerGenerationChecked = + some aliasFormerFinalEnv := rfl + +theorem aliasFormerFinalEnv_trace : + Nonempty (VEnv.AddInductGenerationTrace + typeFamilyAliasEnv aliasFormerFinalEnv + aliasFormerGenerationChecked) := + VEnv.addInductGeneration_trace aliasFormer_addInductGeneration + +theorem aliasFormerFinalEnv_le : + typeFamilyAliasEnv ≤ aliasFormerFinalEnv := by + rcases aliasFormerFinalEnv_trace with ⟨H⟩ + exact H.le + +theorem aliasFormerFinalEnv_family_fresh : + typeFamilyAliasEnv.constants ``AliasFormer = none := by + rcases aliasFormerFinalEnv_trace with ⟨H⟩ + exact H.family_fresh + +theorem aliasFormerFinalEnv_family_lookup : + aliasFormerFinalEnv.constants ``AliasFormer = + some aliasFormerRawType.toVConstant := by + rcases aliasFormerFinalEnv_trace with ⟨H⟩ + exact H.family_lookup + +theorem aliasFormerFinalEnv_ctor_fresh : + ∀ c ∈ aliasFormerRawType.ctors, + typeFamilyAliasEnv.constants c.name = none := by + intro c hc + rcases aliasFormerFinalEnv_trace with ⟨H⟩ + exact H.ctor_fresh hc + +theorem aliasFormerFinalEnv_ctor_lookup : + ∀ c ∈ aliasFormerRawType.ctors, + aliasFormerFinalEnv.constants c.name = + some c.toVConstant := by + intro c hc + rcases aliasFormerFinalEnv_trace with ⟨H⟩ + exact H.ctor_lookup hc + +theorem aliasFormerFinalEnv_rec_fresh : + typeFamilyAliasEnv.constants ``AliasFormer.rec = none := by + rcases aliasFormerFinalEnv_trace with ⟨H⟩ + exact H.rec_fresh + +theorem aliasFormerFinalEnv_rec_lookup : + aliasFormerFinalEnv.constants ``AliasFormer.rec = + some (vconst(type_of% @AliasFormer.rec)) := by + rcases aliasFormerFinalEnv_trace with ⟨H⟩ + exact H.rec_lookup + +theorem aliasFormerFinalEnv_rule_mem : + ∀ df ∈ aliasFormerGenerationChecked.generatedRules, + aliasFormerFinalEnv.defeqs df := by + intro df hdf + rcases aliasFormerFinalEnv_trace with ⟨H⟩ + exact H.rule_mem hdf + +theorem aliasFormerFinalEnv_iota_mem : + aliasFormerFinalEnv.defeqs + (vdefeq(motive mk => + @AliasFormer.rec motive mk AliasFormer.mk ≡ mk)) := by + apply aliasFormerFinalEnv_rule_mem + exact .head _ + +theorem aliasFormerFinalEnv_ordered : + aliasFormerFinalEnv.Ordered := + VEnv.addInductGeneration_WF + typeFamilyAliasEnv_ordered + aliasFormerGenerationChecked_wf + aliasFormer_addInductGeneration + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasFormerFinalEnv_trace [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasFormerFinalEnv_family_lookup [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasFormerFinalEnv_ctor_lookup [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasFormerFinalEnv_rec_lookup [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasFormerFinalEnv_iota_mem [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasFormerFinalEnv_ordered [propext, Classical.choice, Quot.sound] + +abbrev RecAlias (α : Sort u) := α + +inductive AliasRec : Type where + | mk : RecAlias AliasRec → AliasRec + +def aliasRecRawType : VInductiveType where + name := ``AliasRec + uvars := 0 + type := vconst(type_of% @AliasRec).type + ctors := [⟨vconst(type_of% @AliasRec.mk), ``AliasRec.mk⟩] + +def aliasRecRawDecl : VInductDecl := ⟨0, 0, [aliasRecRawType]⟩ + +def aliasRecRawField : VExpr := + (VExpr.const ``RecAlias [.succ .zero]).app (VExpr.const ``AliasRec []) + +example : ctorFields aliasRecRawType.ctors[0].type = [aliasRecRawField] := rfl +example : aliasRecRawDecl.checked? = none := rfl + +def aliasRecViewCtor : VConstVal := + { aliasRecRawType.ctors[0] with + type := .forallE (.const ``AliasRec []) (.const ``AliasRec []) } + +def aliasRecViewType : VInductiveType := + { aliasRecRawType with ctors := [aliasRecViewCtor] } + +def aliasRecViewDecl : VInductDecl := ⟨0, 0, [aliasRecViewType]⟩ + +example : aliasRecViewDecl.checked?.isSome = true := rfl +example : normalizationShape aliasRecRawDecl aliasRecViewDecl = true := rfl + +def aliasRecNormalization : Normalization aliasRecRawDecl where + view := aliasRecViewDecl + shape_eq := rfl + +def aliasRecViewChecked : aliasRecViewDecl.Checked := + aliasRecViewDecl.checked?.get (by decide) + +def aliasRecBlock : NormalizedChecked aliasRecRawDecl := + aliasRecNormalization.check?.get (by decide) + +def aliasRecGenerationChecked : GenerationChecked aliasRecRawDecl := + aliasRecBlock.generation?.get (by decide) + +example : aliasRecNormalization.accepted = true := rfl +example : (normalizedChecked? aliasRecRawDecl aliasRecViewDecl).isSome = true := rfl +example : aliasRecBlock.checked.type = aliasRecViewType := rfl +example : ∃ raw, + aliasRecRawDecl.types = [raw] ∧ + raw.name = aliasRecBlock.checked.type.name ∧ + raw.uvars = aliasRecBlock.checked.type.uvars ∧ + List.Forall₂ CtorHeaderEq raw.ctors aliasRecBlock.checked.type.ctors := + aliasRecBlock.source_anatomy +example : aliasRecViewChecked.constructors[0].fields = [.const ``AliasRec []] := rfl +example : aliasRecViewChecked.constructors[0].recursive.length = 1 := rfl +example : aliasRecViewChecked.constructors[0].recursive[0].binders = [] := rfl +example : aliasRecGenerationChecked.block.ctorPairs[0].rawFields 0 = + [aliasRecRawField] := rfl +example : (ctorFields aliasRecGenerationChecked.minorTypes[0])[0]? = + some aliasRecRawField := rfl +example : aliasRecGenerationChecked.recursor = + vconst(type_of% @AliasRec.rec) := rfl +example : aliasRecGenerationChecked.generatedRules[0]? = + some (vdefeq(motive mk a => + @AliasRec.rec motive mk (@AliasRec.mk a) ≡ + mk a (@AliasRec.rec motive mk a))) := rfl + +def recAliasDefEq : VDefEq := + vdefeq(@RecAlias ≡ fun (α : Sort u) => α) + +def recAliasConstEnv : VEnv := + (VEnv.empty.addConst ``RecAlias (vconst(type_of% @RecAlias))).get (by decide) + +def recAliasEnv : VEnv := recAliasConstEnv.addDefEq recAliasDefEq + +theorem recAliasConstant_wf : + (vconst(type_of% @RecAlias) : VConstant).WF VEnv.empty := by + apply VEnv.IsType.forallE + · exact ⟨_, VEnv.HasType.sort (by decide)⟩ + · exact ⟨_, VEnv.HasType.sort (by decide)⟩ + +theorem recAliasConstEnv_ordered : + recAliasConstEnv.Ordered := by + apply VEnv.Ordered.const VEnv.Ordered.empty + (ci := vconst(type_of% @RecAlias)) + · exact recAliasConstant_wf + · rfl + +theorem recAliasEnv_ordered : + recAliasEnv.Ordered := by + apply VEnv.Ordered.defeq recAliasConstEnv_ordered + constructor + · exact VEnv.HasType.const0 rfl + (recAliasConstant_wf.mono + (VEnv.addConst_le (by rfl : + VEnv.empty.addConst ``RecAlias + (vconst(type_of% @RecAlias)) = + some recAliasConstEnv))) + · exact VEnv.HasType.lam + (VEnv.HasType.sort (by decide)) + (VEnv.HasType.bvar .zero) + +/-- The raw aliased field and the normalized recursive target are +definitionally equal after the raw family constant has been inserted. -/ +theorem aliasRecField_defeq {envT : VEnv} + (hadd : recAliasEnv.addConst ``AliasRec aliasRecRawType.toVConstant = + some envT) : + envT.IsDefEq 0 [] aliasRecRawField (.const ``AliasRec []) + (.sort (.succ .zero)) := by + have hlookup : + envT.constants ``AliasRec = some aliasRecRawType.toVConstant := + VEnv.addConst_self hadd + have hfamily : envT.HasType 0 [] (.const ``AliasRec []) + (.sort (.succ .zero)) := + .constDF hlookup (fun _ h => nomatch h) (fun _ h => nomatch h) rfl .nil + have hdelta : envT.IsDefEq 0 [] + (.const ``RecAlias [.succ .zero]) + (.lam (.sort (.succ .zero)) (.bvar 0)) + (.forallE (.sort (.succ .zero)) (.sort (.succ .zero))) := by + apply VEnv.IsDefEq.extra + (df := recAliasDefEq) (ls := [.succ .zero]) (Γ := []) + · exact (VEnv.addConst_le hadd).defeqs (by + simp [recAliasEnv, VEnv.addDefEq]) + · intro l hl + simp only [List.mem_singleton] at hl + subst l + decide + · rfl + have happ := VEnv.IsDefEq.appDF hdelta hfamily + have hbeta : envT.IsDefEq 0 [] + (.app (.lam (.sort (.succ .zero)) (.bvar 0)) (.const ``AliasRec [])) + (.const ``AliasRec []) (.sort (.succ .zero)) := + .beta (.bvar .zero) hfamily + exact happ.trans hbeta + +/-- The raw family result remains well typed under the raw aliased field +binder. -/ +theorem aliasRecResult_hasType {envT : VEnv} + (hadd : recAliasEnv.addConst ``AliasRec aliasRecRawType.toVConstant = + some envT) : + envT.HasType 0 [aliasRecRawField] (.const ``AliasRec []) + (.sort (.succ .zero)) := + .constDF (VEnv.addConst_self hadd) (fun _ h => nomatch h) + (fun _ h => nomatch h) rfl .nil + +/-- Field WHNF is also justified semantically. The constructor comparison is +staged after insertion of the raw `AliasRec` family constant, and derives +`RecAlias AliasRec ≡ AliasRec` by delta, application congruence, and beta. +-/ +theorem aliasRecNormalization_wf : + aliasRecNormalization.WF recAliasEnv := by + refine ⟨aliasRecRawType, aliasRecViewType, rfl, rfl, ?_, ?_⟩ + · exact ⟨_, by type_tac⟩ + · intro envT hadd + exact .cons + ⟨_, .forallEDF (aliasRecField_defeq hadd) + (aliasRecResult_hasType hadd)⟩ + .nil + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasRecNormalization_wf [propext, Quot.sound] + +/-- The normalized direct-recursive view satisfies the semantic declaration +contract independently of the raw aliased field syntax. -/ +theorem aliasRecViewDecl_wf : aliasRecViewDecl.WF recAliasEnv := by + refine ⟨rfl, ?_⟩ + intro ty hty + have hty' : ty = aliasRecViewType := + List.mem_singleton.1 (by simpa [aliasRecViewDecl] using hty) + subst ty + refine ⟨?_, ?_⟩ + · trivial + · intro c hc + have hc' : c = aliasRecViewCtor := + List.mem_singleton.1 (by simpa [aliasRecViewType] using hc) + subst c + refine ⟨?_, .nil⟩ + exact ⟨.inl rfl, fun _ => .nil, trivial⟩ + +/-- Recursive-field recognition is certified on the normalized view while the +paired block continues to retain the raw aliased constructor syntax. -/ +theorem aliasRecBlock_wf : aliasRecBlock.WF recAliasEnv := by + refine ⟨aliasRecNormalization_wf, ?_⟩ + change aliasRecViewChecked.WF recAliasEnv + exact aliasRecViewChecked.wf_of_decl aliasRecViewDecl_wf + +theorem aliasRecGenerationChecked_wf : + aliasRecGenerationChecked.WF recAliasEnv := by + refine { + blockWF := aliasRecBlock_wf + familyTel := by trivial + familyResult := by + exact .sortDF (by decide) (by decide) rfl + ctors := ?_ } + intro envT hadd ctor hctor + change recAliasEnv.addConst ``AliasRec aliasRecRawType.toVConstant = + some envT at hadd + change ctor ∈ + [⟨aliasRecRawType.ctors[0], aliasRecViewChecked.constructors[0]⟩] + at hctor + obtain rfl := List.mem_singleton.1 hctor + have hfield := aliasRecField_defeq hadd + have hresult := aliasRecResult_hasType hadd + exact { + declaredTel := ⟨⟨_, hfield⟩, trivial⟩ + declaredResult := hresult + emittedTel := ⟨⟨_, hfield⟩, trivial⟩ + emittedResult := hresult } + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasRecBlock_wf [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasRecGenerationChecked_wf [propext, Quot.sound] + +/-- The recursive-field alias case preserves the raw aliased constructor +binder while using the normalized view to generate recursion artifacts. -/ +def aliasRecFinalEnv : VEnv := + (recAliasEnv.addInductGeneration + aliasRecGenerationChecked).get (by decide) + +theorem aliasRec_addInductGeneration : + recAliasEnv.addInductGeneration aliasRecGenerationChecked = + some aliasRecFinalEnv := rfl + +theorem aliasRecFinalEnv_trace : + Nonempty (VEnv.AddInductGenerationTrace + recAliasEnv aliasRecFinalEnv aliasRecGenerationChecked) := + VEnv.addInductGeneration_trace aliasRec_addInductGeneration + +theorem aliasRecFinalEnv_le : + recAliasEnv ≤ aliasRecFinalEnv := by + rcases aliasRecFinalEnv_trace with ⟨H⟩ + exact H.le + +theorem aliasRecFinalEnv_family_fresh : + recAliasEnv.constants ``AliasRec = none := by + rcases aliasRecFinalEnv_trace with ⟨H⟩ + exact H.family_fresh + +theorem aliasRecFinalEnv_family_lookup : + aliasRecFinalEnv.constants ``AliasRec = + some aliasRecRawType.toVConstant := by + rcases aliasRecFinalEnv_trace with ⟨H⟩ + exact H.family_lookup + +theorem aliasRecFinalEnv_ctor_fresh : + ∀ c ∈ aliasRecRawType.ctors, + recAliasEnv.constants c.name = none := by + intro c hc + rcases aliasRecFinalEnv_trace with ⟨H⟩ + exact H.ctor_fresh hc + +theorem aliasRecFinalEnv_ctor_lookup : + ∀ c ∈ aliasRecRawType.ctors, + aliasRecFinalEnv.constants c.name = + some c.toVConstant := by + intro c hc + rcases aliasRecFinalEnv_trace with ⟨H⟩ + exact H.ctor_lookup hc + +theorem aliasRecFinalEnv_rec_fresh : + recAliasEnv.constants ``AliasRec.rec = none := by + rcases aliasRecFinalEnv_trace with ⟨H⟩ + exact H.rec_fresh + +theorem aliasRecFinalEnv_rec_lookup : + aliasRecFinalEnv.constants ``AliasRec.rec = + some (vconst(type_of% @AliasRec.rec)) := by + rcases aliasRecFinalEnv_trace with ⟨H⟩ + exact H.rec_lookup + +theorem aliasRecFinalEnv_rule_mem : + ∀ df ∈ aliasRecGenerationChecked.generatedRules, + aliasRecFinalEnv.defeqs df := by + intro df hdf + rcases aliasRecFinalEnv_trace with ⟨H⟩ + exact H.rule_mem hdf + +theorem aliasRecFinalEnv_iota_mem : + aliasRecFinalEnv.defeqs + (vdefeq(motive mk a => + @AliasRec.rec motive mk (@AliasRec.mk a) ≡ + mk a (@AliasRec.rec motive mk a))) := by + apply aliasRecFinalEnv_rule_mem + exact .head _ + +theorem aliasRecFinalEnv_ordered : + aliasRecFinalEnv.Ordered := + VEnv.addInductGeneration_WF + recAliasEnv_ordered + aliasRecGenerationChecked_wf + aliasRec_addInductGeneration + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasRecFinalEnv_trace [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasRecFinalEnv_family_lookup [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasRecFinalEnv_ctor_lookup [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasRecFinalEnv_rec_lookup [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasRecFinalEnv_iota_mem [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.aliasRecFinalEnv_ordered [propext, Classical.choice, Quot.sound] + +/-! ## Normalization differential matrix + +The isolated `AliasFormer` and `AliasRec` fixtures above establish that Lean +retains reducible aliases at family results and direct recursive targets. The +single indexed declaration below covers every remaining normalization +position in one real kernel payload: parameter and index domains, ordinary +fields, direct recursion, recursion hidden behind a Pi-producing alias, and +beta/let redexes retained in alias definitions. Its view is deliberately +written out so changes to either Lean's stored metadata or the Theory +normalizer fail by computation. +-/ + +abbrev MatrixBetaAlias (alpha : Sort u) := + (fun type : Sort u => type) alpha + +abbrev MatrixLetAlias (alpha : Sort u) := + let type := alpha + type + +abbrev MatrixPiAlias (alpha : Sort u) := (proof : Prop) → alpha + +abbrev MatrixIndexAlias + (index : TypeFamilyAlias) : TypeFamilyAlias := index + +inductive NormalizationMatrix (alpha : TypeFamilyAlias) : + TypeFamilyAlias → Type 1 where + | mk (index : TypeFamilyAlias) + (ordinary : RecAlias Prop) + (beta : MatrixBetaAlias Prop) + (letBound : MatrixLetAlias Prop) + (direct : RecAlias + (NormalizationMatrix alpha (MatrixIndexAlias index))) + (piHidden : MatrixPiAlias + (NormalizationMatrix alpha (MatrixIndexAlias index))) + (betaRecursive : MatrixBetaAlias + (NormalizationMatrix alpha (MatrixIndexAlias index))) + (letRecursive : MatrixLetAlias + (NormalizationMatrix alpha (MatrixIndexAlias index))) : + NormalizationMatrix alpha (MatrixIndexAlias index) + +def normalizationMatrixRawType : VInductiveType where + name := ``NormalizationMatrix + uvars := 0 + type := vconst(type_of% @NormalizationMatrix).type + ctors := [⟨vconst(type_of% @NormalizationMatrix.mk), + ``NormalizationMatrix.mk⟩] + +def normalizationMatrixRawDecl : VInductDecl := + ⟨0, 1, [normalizationMatrixRawType]⟩ + +def normalizationMatrixTarget (alpha index : VExpr) : VExpr := + (VExpr.const ``NormalizationMatrix []).app alpha |>.app + ((VExpr.const ``MatrixIndexAlias []).app index) + +def normalizationMatrixViewCtorType : VExpr := + .forallE (.sort (.succ .zero)) <| + .forallE (.sort (.succ .zero)) <| + .forallE (.sort .zero) <| + .forallE (.sort .zero) <| + .forallE (.sort .zero) <| + .forallE (normalizationMatrixTarget (.bvar 4) (.bvar 3)) <| + .forallE (.forallE (.sort .zero) + (normalizationMatrixTarget (.bvar 6) (.bvar 5))) <| + .forallE (normalizationMatrixTarget (.bvar 6) (.bvar 5)) <| + .forallE (normalizationMatrixTarget (.bvar 7) (.bvar 6)) <| + normalizationMatrixTarget (.bvar 8) (.bvar 7) + +def normalizationMatrixViewCtor : VConstVal := + { normalizationMatrixRawType.ctors[0] with + type := normalizationMatrixViewCtorType } + +def normalizationMatrixViewType : VInductiveType := + { normalizationMatrixRawType with + type := .forallE (.sort (.succ .zero)) + (.forallE (.sort (.succ .zero)) + (.sort (.succ (.succ .zero)))) + ctors := [normalizationMatrixViewCtor] } + +def normalizationMatrixViewDecl : VInductDecl := + ⟨0, 1, [normalizationMatrixViewType]⟩ + +example : normalizationMatrixRawType.type = + (VExpr.const ``TypeFamilyAlias []).forallE + ((VExpr.const ``TypeFamilyAlias []).forallE + (VExpr.sort (.succ (.succ .zero)))) := rfl + +example : normalizationMatrixRawType.ctors[0].type = + vconst(type_of% @NormalizationMatrix.mk).type := rfl + +example : normalizationMatrixRawDecl.checked? = none := rfl +example : normalizationMatrixViewDecl.checked?.isSome = true := rfl +example : normalizationShape normalizationMatrixRawDecl + normalizationMatrixViewDecl = true := rfl + +def normalizationMatrixNormalization : + Normalization normalizationMatrixRawDecl where + view := normalizationMatrixViewDecl + shape_eq := rfl + +def normalizationMatrixViewChecked : + normalizationMatrixViewDecl.Checked := + normalizationMatrixViewDecl.checked?.get (by decide) + +def normalizationMatrixBlock : + NormalizedChecked normalizationMatrixRawDecl := + normalizationMatrixNormalization.check?.get (by decide) + +def normalizationMatrixGenerationChecked : + GenerationChecked normalizationMatrixRawDecl := + normalizationMatrixBlock.generation?.get (by decide) + +example : normalizationMatrixNormalization.accepted = true := rfl +example : (normalizedChecked? normalizationMatrixRawDecl + normalizationMatrixViewDecl).isSome = true := rfl +example : normalizationMatrixBlock.checked.type = + normalizationMatrixViewType := rfl +example : normalizationMatrixViewChecked.params = + [.sort (.succ .zero)] := rfl +example : normalizationMatrixViewChecked.indices = + [.sort (.succ .zero)] := rfl +example : normalizationMatrixViewChecked.constructors[0].fields.length = 8 := + rfl +example : normalizationMatrixViewChecked.constructors[0].recursive.length = 4 := + rfl +example : normalizationMatrixViewChecked.constructors[0].recursive.map + (fun position => (position.fieldIndex, position.binders.length)) = + [(4, 0), (5, 1), (6, 0), (7, 0)] := rfl +example : normalizationMatrixGenerationChecked.recursor = + vconst(type_of% @NormalizationMatrix.rec) := rfl +example : normalizationMatrixGenerationChecked.generatedRules[0]? = + some (vdefeq(alpha motive mk index ordinary beta letBound direct + piHidden betaRecursive letRecursive => + @NormalizationMatrix.rec alpha motive mk (MatrixIndexAlias index) + (@NormalizationMatrix.mk alpha index ordinary beta letBound direct + piHidden betaRecursive letRecursive) ≡ + mk index ordinary beta letBound direct piHidden betaRecursive + letRecursive + (@NormalizationMatrix.rec alpha motive mk + (MatrixIndexAlias index) direct) + (fun proof => @NormalizationMatrix.rec alpha motive mk + (MatrixIndexAlias index) (piHidden proof)) + (@NormalizationMatrix.rec alpha motive mk + (MatrixIndexAlias index) betaRecursive) + (@NormalizationMatrix.rec alpha motive mk + (MatrixIndexAlias index) letRecursive))) := rfl +example : normalizationMatrixGenerationChecked.generatedRules.length = 1 := rfl + +/-! The semantic pre-environment contains the exact reducible definitions +whose WHNFs justify the normalized descriptor. Each abbreviation is added +as a constant followed by its delta equation so the final transaction uses +the same staged environment discipline as ordinary declarations. -/ + +def normalizationMatrixRecAliasConstEnv : VEnv := + (typeFamilyAliasEnv.addConst ``RecAlias + (vconst(type_of% @RecAlias))).get (by decide) + +def normalizationMatrixRecAliasEnv : VEnv := + normalizationMatrixRecAliasConstEnv.addDefEq recAliasDefEq + +theorem normalizationMatrixRecAliasEnv_ordered : + normalizationMatrixRecAliasEnv.Ordered := by + apply VEnv.Ordered.defeq + · apply VEnv.Ordered.const (n := ``RecAlias) + (ci := vconst(type_of% @RecAlias)) + (env' := normalizationMatrixRecAliasConstEnv) + typeFamilyAliasEnv_ordered + · exact ⟨_, by type_tac⟩ + · rfl + · have hlookup : normalizationMatrixRecAliasConstEnv.constants + ``RecAlias = some (vconst(type_of% @RecAlias)) := rfl + constructor <;> type_tac + +def matrixBetaAliasDefEq : VDefEq := + vdefeq(@MatrixBetaAlias ≡ fun (alpha : Sort u) => + (fun type : Sort u => type) alpha) + +def normalizationMatrixBetaAliasConstEnv : VEnv := + (normalizationMatrixRecAliasEnv.addConst ``MatrixBetaAlias + (vconst(type_of% @MatrixBetaAlias))).get (by decide) + +def normalizationMatrixBetaAliasEnv : VEnv := + normalizationMatrixBetaAliasConstEnv.addDefEq matrixBetaAliasDefEq + +theorem normalizationMatrixBetaAliasEnv_ordered : + normalizationMatrixBetaAliasEnv.Ordered := by + apply VEnv.Ordered.defeq + · apply VEnv.Ordered.const (n := ``MatrixBetaAlias) + (ci := vconst(type_of% @MatrixBetaAlias)) + (env' := normalizationMatrixBetaAliasConstEnv) + normalizationMatrixRecAliasEnv_ordered + · exact ⟨_, by type_tac⟩ + · rfl + · have hlookup : normalizationMatrixBetaAliasConstEnv.constants + ``MatrixBetaAlias = some (vconst(type_of% @MatrixBetaAlias)) := rfl + constructor <;> type_tac + +def matrixLetAliasDefEq : VDefEq := + vdefeq(@MatrixLetAlias ≡ fun (alpha : Sort u) => + let type := alpha + type) + +def normalizationMatrixLetAliasConstEnv : VEnv := + (normalizationMatrixBetaAliasEnv.addConst ``MatrixLetAlias + (vconst(type_of% @MatrixLetAlias))).get (by decide) + +def normalizationMatrixLetAliasEnv : VEnv := + normalizationMatrixLetAliasConstEnv.addDefEq matrixLetAliasDefEq + +theorem normalizationMatrixLetAliasEnv_ordered : + normalizationMatrixLetAliasEnv.Ordered := by + apply VEnv.Ordered.defeq + · apply VEnv.Ordered.const (n := ``MatrixLetAlias) + (ci := vconst(type_of% @MatrixLetAlias)) + (env' := normalizationMatrixLetAliasConstEnv) + normalizationMatrixBetaAliasEnv_ordered + · exact ⟨_, by type_tac⟩ + · rfl + · have hlookup : normalizationMatrixLetAliasConstEnv.constants + ``MatrixLetAlias = some (vconst(type_of% @MatrixLetAlias)) := rfl + constructor <;> type_tac + +def matrixPiAliasDefEq : VDefEq := + vdefeq(@MatrixPiAlias ≡ fun (alpha : Sort u) => + (proof : Prop) → alpha) + +def normalizationMatrixPiAliasConstEnv : VEnv := + (normalizationMatrixLetAliasEnv.addConst ``MatrixPiAlias + (vconst(type_of% @MatrixPiAlias))).get (by decide) + +def normalizationMatrixPiAliasEnv : VEnv := + normalizationMatrixPiAliasConstEnv.addDefEq matrixPiAliasDefEq + +theorem normalizationMatrixPiAliasEnv_ordered : + normalizationMatrixPiAliasEnv.Ordered := by + apply VEnv.Ordered.defeq + · apply VEnv.Ordered.const (n := ``MatrixPiAlias) + (ci := vconst(type_of% @MatrixPiAlias)) + (env' := normalizationMatrixPiAliasConstEnv) + normalizationMatrixLetAliasEnv_ordered + · exact ⟨_, by type_tac⟩ + · rfl + · have hlookup : normalizationMatrixPiAliasConstEnv.constants + ``MatrixPiAlias = some (vconst(type_of% @MatrixPiAlias)) := rfl + constructor + · type_tac + · apply VEnv.HasType.lam + · exact VEnv.HasType.sort (by decide) + · apply VEnv.IsDefEq.defeq + (VEnv.IsDefEq.sortDF + (l := .imax (.succ .zero) (.param 0)) + (l' := .param 0) (by decide) (by decide) (by + rw [VLevel.equiv_def] + intro ls + simp only [VLevel.eval, Nat.zero_add] + let n := ls.getD 0 0 + change Lean.Nat.imax 1 n = n + by_cases h : n = 0 + · simp [Lean.Nat.imax, h] + · have hn : 1 ≤ n := Nat.one_le_iff_ne_zero.mpr h + simp [Lean.Nat.imax, h, Nat.max_eq_right hn])) + exact VEnv.HasType.forallE + (VEnv.HasType.sort (by decide)) + (VEnv.HasType.bvar (.succ .zero)) + +def matrixIndexAliasDefEq : VDefEq := + vdefeq(MatrixIndexAlias ≡ fun index : TypeFamilyAlias => index) + +def normalizationMatrixIndexAliasConstEnv : VEnv := + (normalizationMatrixPiAliasEnv.addConst ``MatrixIndexAlias + (vconst(type_of% @MatrixIndexAlias))).get (by decide) + +def normalizationMatrixAliasEnv : VEnv := + normalizationMatrixIndexAliasConstEnv.addDefEq matrixIndexAliasDefEq + +theorem normalizationMatrixAliasEnv_ordered : + normalizationMatrixAliasEnv.Ordered := by + apply VEnv.Ordered.defeq + · apply VEnv.Ordered.const (n := ``MatrixIndexAlias) + (ci := vconst(type_of% @MatrixIndexAlias)) + (env' := normalizationMatrixIndexAliasConstEnv) + normalizationMatrixPiAliasEnv_ordered + · have hfamily : normalizationMatrixPiAliasEnv.constants + ``TypeFamilyAlias = some (vconst(type_of% @TypeFamilyAlias)) := rfl + exact ⟨_, by type_tac⟩ + · rfl + · have hfamily : normalizationMatrixIndexAliasConstEnv.constants + ``TypeFamilyAlias = some (vconst(type_of% @TypeFamilyAlias)) := rfl + have hlookup : normalizationMatrixIndexAliasConstEnv.constants + ``MatrixIndexAlias = some (vconst(type_of% @MatrixIndexAlias)) := rfl + constructor <;> type_tac + +theorem normalizationMatrixRecAliasEnv_le_betaAliasEnv : + normalizationMatrixRecAliasEnv ≤ normalizationMatrixBetaAliasEnv := + (VEnv.addConst_le (by rfl : normalizationMatrixRecAliasEnv.addConst + ``MatrixBetaAlias (vconst(type_of% @MatrixBetaAlias)) = + some normalizationMatrixBetaAliasConstEnv)).trans VEnv.addDefEq_le + +theorem normalizationMatrixBetaAliasEnv_le_letAliasEnv : + normalizationMatrixBetaAliasEnv ≤ normalizationMatrixLetAliasEnv := + (VEnv.addConst_le (by rfl : normalizationMatrixBetaAliasEnv.addConst + ``MatrixLetAlias (vconst(type_of% @MatrixLetAlias)) = + some normalizationMatrixLetAliasConstEnv)).trans VEnv.addDefEq_le + +theorem normalizationMatrixLetAliasEnv_le_piAliasEnv : + normalizationMatrixLetAliasEnv ≤ normalizationMatrixPiAliasEnv := + (VEnv.addConst_le (by rfl : normalizationMatrixLetAliasEnv.addConst + ``MatrixPiAlias (vconst(type_of% @MatrixPiAlias)) = + some normalizationMatrixPiAliasConstEnv)).trans VEnv.addDefEq_le + +theorem normalizationMatrixPiAliasEnv_le_aliasEnv : + normalizationMatrixPiAliasEnv ≤ normalizationMatrixAliasEnv := + (VEnv.addConst_le (by rfl : normalizationMatrixPiAliasEnv.addConst + ``MatrixIndexAlias (vconst(type_of% @MatrixIndexAlias)) = + some normalizationMatrixIndexAliasConstEnv)).trans VEnv.addDefEq_le + +theorem normalizationMatrixRecAliasEnv_le_aliasEnv : + normalizationMatrixRecAliasEnv ≤ normalizationMatrixAliasEnv := + normalizationMatrixRecAliasEnv_le_betaAliasEnv.trans <| + normalizationMatrixBetaAliasEnv_le_letAliasEnv.trans <| + normalizationMatrixLetAliasEnv_le_piAliasEnv.trans + normalizationMatrixPiAliasEnv_le_aliasEnv + +theorem typeFamilyAliasEnv_le_normalizationMatrixAliasEnv : + typeFamilyAliasEnv ≤ normalizationMatrixAliasEnv := by + exact ((VEnv.addConst_le (by rfl : typeFamilyAliasEnv.addConst ``RecAlias + (vconst(type_of% @RecAlias)) = + some normalizationMatrixRecAliasConstEnv)).trans VEnv.addDefEq_le).trans + normalizationMatrixRecAliasEnv_le_aliasEnv + +theorem normalizationMatrix_typeFamilyAliasDefEq_mem : + normalizationMatrixAliasEnv.defeqs typeFamilyAliasDefEq := + typeFamilyAliasEnv_le_normalizationMatrixAliasEnv.defeqs + VEnv.addDefEq_self + +theorem normalizationMatrix_recAliasDefEq_mem : + normalizationMatrixAliasEnv.defeqs recAliasDefEq := + normalizationMatrixRecAliasEnv_le_aliasEnv.defeqs VEnv.addDefEq_self + +theorem normalizationMatrix_betaAliasDefEq_mem : + normalizationMatrixAliasEnv.defeqs matrixBetaAliasDefEq := + (normalizationMatrixBetaAliasEnv_le_letAliasEnv.trans <| + normalizationMatrixLetAliasEnv_le_piAliasEnv.trans + normalizationMatrixPiAliasEnv_le_aliasEnv).defeqs VEnv.addDefEq_self + +theorem normalizationMatrix_letAliasDefEq_mem : + normalizationMatrixAliasEnv.defeqs matrixLetAliasDefEq := + (normalizationMatrixLetAliasEnv_le_piAliasEnv.trans + normalizationMatrixPiAliasEnv_le_aliasEnv).defeqs VEnv.addDefEq_self + +theorem normalizationMatrix_piAliasDefEq_mem : + normalizationMatrixAliasEnv.defeqs matrixPiAliasDefEq := + normalizationMatrixPiAliasEnv_le_aliasEnv.defeqs VEnv.addDefEq_self + +theorem normalizationMatrixTypeFamily_defeq {env : VEnv} {U : Nat} + {Γ : List VExpr} (henv : normalizationMatrixAliasEnv ≤ env) : + env.IsDefEq U Γ (VExpr.const ``TypeFamilyAlias []) + (VExpr.sort (.succ .zero)) + (VExpr.sort (.succ (.succ .zero))) := by + exact .extra (df := typeFamilyAliasDefEq) (ls := []) + (henv.defeqs normalizationMatrix_typeFamilyAliasDefEq_mem) + (fun _ h => nomatch h) rfl + +theorem normalizationMatrixRecAlias_app_defeq {env : VEnv} {U : Nat} + {Γ : List VExpr} {u : VLevel} {A : VExpr} (hu : u.WF U) + (henv : normalizationMatrixAliasEnv ≤ env) + (hA : env.HasType U Γ A (VExpr.sort u)) : + env.IsDefEq U Γ ((VExpr.const ``RecAlias [u]).app A) A + (VExpr.sort u) := by + have hdelta : env.IsDefEq U Γ (VExpr.const ``RecAlias [u]) + (VExpr.lam (VExpr.sort u) (VExpr.bvar 0)) + (VExpr.forallE (VExpr.sort u) (VExpr.sort u)) := + .extra (df := recAliasDefEq) (ls := [u]) + (henv.defeqs normalizationMatrix_recAliasDefEq_mem) + (by simpa using hu) rfl + have hbeta := VEnv.IsDefEq.beta (VEnv.IsDefEq.bvar .zero) hA + exact (VEnv.IsDefEq.appDF hdelta hA).trans (by + simpa [VExpr.inst, VExpr.instVar, VExpr.liftN] using hbeta) + +theorem normalizationMatrixBetaAlias_app_defeq {env : VEnv} {U : Nat} + {Γ : List VExpr} {u : VLevel} {A : VExpr} (hu : u.WF U) + (henv : normalizationMatrixAliasEnv ≤ env) + (hA : env.HasType U Γ A (VExpr.sort u)) : + env.IsDefEq U Γ ((VExpr.const ``MatrixBetaAlias [u]).app A) A + (VExpr.sort u) := by + have hdelta : env.IsDefEq U Γ (VExpr.const ``MatrixBetaAlias [u]) + (VExpr.lam (VExpr.sort u) + ((VExpr.lam (VExpr.sort u) (VExpr.bvar 0)).app (VExpr.bvar 0))) + (VExpr.forallE (VExpr.sort u) (VExpr.sort u)) := + .extra (df := matrixBetaAliasDefEq) (ls := [u]) + (henv.defeqs normalizationMatrix_betaAliasDefEq_mem) + (by simpa using hu) rfl + have hbody : env.HasType U (VExpr.sort u :: Γ) + ((VExpr.lam (VExpr.sort u) (VExpr.bvar 0)).app (VExpr.bvar 0)) + (VExpr.sort u) := + VEnv.HasType.app + (VEnv.HasType.lam (VEnv.HasType.sort hu) (VEnv.HasType.bvar .zero)) + (VEnv.HasType.bvar .zero) + have houterBeta := VEnv.IsDefEq.beta hbody hA + have houter := (VEnv.IsDefEq.appDF hdelta hA).trans (by + simpa [VExpr.inst, VExpr.instVar, VExpr.liftN] using houterBeta) + have hinnerBeta := VEnv.IsDefEq.beta (VEnv.IsDefEq.bvar .zero) hA + exact houter.trans (by + simpa [VExpr.inst, VExpr.instVar, VExpr.liftN] using hinnerBeta) + +theorem normalizationMatrixLetAlias_app_defeq {env : VEnv} {U : Nat} + {Γ : List VExpr} {u : VLevel} {A : VExpr} (hu : u.WF U) + (henv : normalizationMatrixAliasEnv ≤ env) + (hA : env.HasType U Γ A (VExpr.sort u)) : + env.IsDefEq U Γ ((VExpr.const ``MatrixLetAlias [u]).app A) A + (VExpr.sort u) := by + have hdelta : env.IsDefEq U Γ (VExpr.const ``MatrixLetAlias [u]) + (VExpr.lam (VExpr.sort u) (VExpr.bvar 0)) + (VExpr.forallE (VExpr.sort u) (VExpr.sort u)) := + .extra (df := matrixLetAliasDefEq) (ls := [u]) + (henv.defeqs normalizationMatrix_letAliasDefEq_mem) + (by simpa using hu) rfl + have hbeta := VEnv.IsDefEq.beta (VEnv.IsDefEq.bvar .zero) hA + exact (VEnv.IsDefEq.appDF hdelta hA).trans (by + simpa [VExpr.inst, VExpr.instVar, VExpr.liftN] using hbeta) + +private theorem normalizationMatrix_one_imax_equiv (u : VLevel) : + .imax (.succ .zero) u ≈ u := by + rw [VLevel.equiv_def] + intro ls + simp only [VLevel.eval, Nat.zero_add] + let n := u.eval ls + change Lean.Nat.imax 1 n = n + by_cases h : n = 0 + · simp [Lean.Nat.imax, h] + · have hn : 1 ≤ n := Nat.one_le_iff_ne_zero.mpr h + simp [Lean.Nat.imax, h, Nat.max_eq_right hn] + +theorem normalizationMatrixPiAlias_app_defeq {env : VEnv} {U : Nat} + {Γ : List VExpr} {u : VLevel} {A : VExpr} (hu : u.WF U) + (henv : normalizationMatrixAliasEnv ≤ env) + (hA : env.HasType U Γ A (VExpr.sort u)) : + env.IsDefEq U Γ ((VExpr.const ``MatrixPiAlias [u]).app A) + (VExpr.forallE (VExpr.sort .zero) A.lift) (VExpr.sort u) := by + have hdelta : env.IsDefEq U Γ (VExpr.const ``MatrixPiAlias [u]) + (VExpr.lam (VExpr.sort u) + (VExpr.forallE (VExpr.sort .zero) (VExpr.bvar 1))) + (VExpr.forallE (VExpr.sort u) (VExpr.sort u)) := + .extra (df := matrixPiAliasDefEq) (ls := [u]) + (henv.defeqs normalizationMatrix_piAliasDefEq_mem) + (by simpa using hu) rfl + have hbody : env.HasType U (VExpr.sort u :: Γ) + (VExpr.forallE (VExpr.sort .zero) (VExpr.bvar 1)) + (VExpr.sort u) := by + apply VEnv.IsDefEq.defeq + (VEnv.IsDefEq.sortDF + (l := .imax (.succ .zero) u) (l' := u) + ⟨trivial, hu⟩ hu (normalizationMatrix_one_imax_equiv u)) + exact VEnv.HasType.forallE (VEnv.HasType.sort (by trivial)) + (VEnv.HasType.bvar (.succ .zero)) + have hbeta := VEnv.IsDefEq.beta hbody hA + exact (VEnv.IsDefEq.appDF hdelta hA).trans (by + simpa [VExpr.inst, VExpr.instVar, VExpr.liftN] using hbeta) + +theorem normalizationMatrixIndexAlias_app_hasType {env : VEnv} {U : Nat} + {Γ : List VExpr} {index : VExpr} + (henv : normalizationMatrixAliasEnv ≤ env) + (hindex : env.HasType U Γ index (VExpr.sort (.succ .zero))) : + env.HasType U Γ ((VExpr.const ``MatrixIndexAlias []).app index) + (VExpr.sort (.succ .zero)) := by + have hfn : env.HasType U Γ (VExpr.const ``MatrixIndexAlias []) + ((VExpr.const ``TypeFamilyAlias []).forallE + (VExpr.const ``TypeFamilyAlias [])) := + .constDF (henv.constants (by rfl : normalizationMatrixAliasEnv.constants + ``MatrixIndexAlias = some (vconst(type_of% @MatrixIndexAlias)))) + (fun _ h => nomatch h) (fun _ h => nomatch h) rfl .nil + have hindexRaw := (normalizationMatrixTypeFamily_defeq henv).defeq' hindex + exact (normalizationMatrixTypeFamily_defeq henv).defeq + (VEnv.HasType.app hfn hindexRaw) + +theorem normalizationMatrixTarget_hasType {env : VEnv} {U : Nat} + {Γ : List VExpr} {alpha index : VExpr} + (henv : normalizationMatrixAliasEnv ≤ env) + (hfamily : env.constants ``NormalizationMatrix = + some normalizationMatrixRawType.toVConstant) + (halpha : env.HasType U Γ alpha (VExpr.const ``TypeFamilyAlias [])) + (hindex : env.HasType U Γ index (VExpr.const ``TypeFamilyAlias [])) : + env.HasType U Γ (normalizationMatrixTarget alpha index) + (VExpr.sort (.succ (.succ .zero))) := by + have hfn : env.HasType U Γ (VExpr.const ``NormalizationMatrix []) + (VExpr.forallE (VExpr.const ``TypeFamilyAlias []) + (VExpr.forallE (VExpr.const ``TypeFamilyAlias []) + (VExpr.sort (.succ (.succ .zero))))) := + .constDF hfamily (fun _ h => nomatch h) (fun _ h => nomatch h) + rfl .nil + have hindexFn : env.HasType U Γ (VExpr.const ``MatrixIndexAlias []) + ((VExpr.const ``TypeFamilyAlias []).forallE + (VExpr.const ``TypeFamilyAlias [])) := + .constDF (henv.constants (by rfl : normalizationMatrixAliasEnv.constants + ``MatrixIndexAlias = some (vconst(type_of% @MatrixIndexAlias)))) + (fun _ h => nomatch h) (fun _ h => nomatch h) rfl .nil + exact VEnv.HasType.app (VEnv.HasType.app hfn halpha) + (VEnv.HasType.app hindexFn hindex) + +theorem normalizationMatrixNormalization_wf : + normalizationMatrixNormalization.WF normalizationMatrixAliasEnv := by + refine ⟨normalizationMatrixRawType, normalizationMatrixViewType, + rfl, rfl, ?_, ?_⟩ + · refine ⟨VExpr.sort (.imax (.succ (.succ .zero)) + (.imax (.succ (.succ .zero)) + (.succ (.succ (.succ .zero))))), ?_⟩ + change normalizationMatrixAliasEnv.IsDefEq 0 [] + ((VExpr.const ``TypeFamilyAlias []).forallE + ((VExpr.const ``TypeFamilyAlias []).forallE + (VExpr.sort (.succ (.succ .zero))))) + ((VExpr.sort (.succ .zero)).forallE + ((VExpr.sort (.succ .zero)).forallE + (VExpr.sort (.succ (.succ .zero))))) + (VExpr.sort (.imax (.succ (.succ .zero)) + (.imax (.succ (.succ .zero)) + (.succ (.succ (.succ .zero)))))) + apply VEnv.IsDefEq.forallEDF + · exact normalizationMatrixTypeFamily_defeq .rfl + · apply VEnv.IsDefEq.forallEDF + · exact normalizationMatrixTypeFamily_defeq .rfl + · exact VEnv.IsDefEq.sortDF (by decide) (by decide) rfl + · intro envT hadd + have henv : normalizationMatrixAliasEnv ≤ envT := + VEnv.addConst_le hadd + have htypeFamily : envT.constants ``TypeFamilyAlias = + some (vconst(type_of% @TypeFamilyAlias)) := + henv.constants (by rfl) + have hrecAlias : envT.constants ``RecAlias = + some (vconst(type_of% @RecAlias)) := + henv.constants (by rfl) + have hbetaAlias : envT.constants ``MatrixBetaAlias = + some (vconst(type_of% @MatrixBetaAlias)) := + henv.constants (by rfl) + have hletAlias : envT.constants ``MatrixLetAlias = + some (vconst(type_of% @MatrixLetAlias)) := + henv.constants (by rfl) + have hpiAlias : envT.constants ``MatrixPiAlias = + some (vconst(type_of% @MatrixPiAlias)) := + henv.constants (by rfl) + have hindexAlias : envT.constants ``MatrixIndexAlias = + some (vconst(type_of% @MatrixIndexAlias)) := + henv.constants (by rfl) + have hfamily : envT.constants ``NormalizationMatrix = + some normalizationMatrixRawType.toVConstant := + VEnv.addConst_self hadd + exact .cons ⟨_, by + apply VEnv.IsDefEq.forallEDF + · exact normalizationMatrixTypeFamily_defeq henv + · apply VEnv.IsDefEq.forallEDF + · exact normalizationMatrixTypeFamily_defeq henv + · apply VEnv.IsDefEq.forallEDF + · exact normalizationMatrixRecAlias_app_defeq + (by decide) henv (by type_tac) + · apply VEnv.IsDefEq.forallEDF + · exact normalizationMatrixBetaAlias_app_defeq + (by decide) henv (by type_tac) + · apply VEnv.IsDefEq.forallEDF + · exact normalizationMatrixLetAlias_app_defeq + (by decide) henv (by type_tac) + · apply VEnv.IsDefEq.forallEDF + · exact normalizationMatrixRecAlias_app_defeq + (by decide) henv (by type_tac) + · apply VEnv.IsDefEq.forallEDF + · exact normalizationMatrixPiAlias_app_defeq + (by decide) henv (by type_tac) + · apply VEnv.IsDefEq.forallEDF + · exact normalizationMatrixBetaAlias_app_defeq + (by decide) henv (by type_tac) + · apply VEnv.IsDefEq.forallEDF + · exact normalizationMatrixLetAlias_app_defeq + (by decide) henv (by type_tac) + · type_tac⟩ .nil + +theorem normalizationMatrixViewChecked_wf : + normalizationMatrixViewChecked.WF normalizationMatrixAliasEnv := by + refine ⟨?_, ?_⟩ + · change normalizationMatrixAliasEnv.OnTel 0 [] + [VExpr.sort (.succ .zero), VExpr.sort (.succ .zero)] + exact ⟨⟨_, by type_tac⟩, ⟨⟨_, by type_tac⟩, trivial⟩⟩ + · intro c hc + change c ∈ [normalizationMatrixViewCtor] at hc + have hc' : c = normalizationMatrixViewCtor := + List.mem_singleton.1 hc + subst c + constructor + · change fieldsWF 0 ``NormalizationMatrix 1 + normalizationMatrixAliasEnv (.succ (.succ .zero)) + [VExpr.sort (.succ .zero)] [VExpr.sort (.succ .zero)] 0 + [VExpr.sort (.succ .zero), VExpr.sort .zero, + VExpr.sort .zero, VExpr.sort .zero, + normalizationMatrixTarget (.bvar 4) (.bvar 3), + (VExpr.sort .zero).forallE + (normalizationMatrixTarget (.bvar 6) (.bvar 5)), + normalizationMatrixTarget (.bvar 6) (.bvar 5), + normalizationMatrixTarget (.bvar 7) (.bvar 6)] + refine ⟨?_, ?_, ?_⟩ + · exact .inr (.inr ⟨rfl, .succ (.succ .zero), by type_tac, + .inr (VLevel.le_refl _)⟩) + · intro h + change false = true at h + contradiction + · refine ⟨?_, ?_, ?_⟩ + · exact .inr (.inr ⟨rfl, .succ .zero, by type_tac, + .inr VLevel.le_succ⟩) + · intro h + change false = true at h + contradiction + · refine ⟨?_, ?_, ?_⟩ + · exact .inr (.inr ⟨rfl, .succ .zero, by type_tac, + .inr VLevel.le_succ⟩) + · intro h + change false = true at h + contradiction + · refine ⟨?_, ?_, ?_⟩ + · exact .inr (.inr ⟨rfl, .succ .zero, by type_tac, + .inr VLevel.le_succ⟩) + · intro h + change false = true at h + contradiction + · refine ⟨?_, ?_, ?_⟩ + · exact .inl rfl + · intro _ + exact .cons + (normalizationMatrixIndexAlias_app_hasType .rfl + (by type_tac)) .nil + · refine ⟨?_, ?_, ?_⟩ + · refine .inr (.inl ⟨_, rfl, by decide, ?_⟩) + constructor + · exact ⟨⟨_, by type_tac⟩, trivial⟩ + · exact .cons + (normalizationMatrixIndexAlias_app_hasType .rfl + (by type_tac)) .nil + · intro h + change false = true at h + contradiction + · refine ⟨?_, ?_, ?_⟩ + · exact .inl rfl + · intro _ + exact .cons + (normalizationMatrixIndexAlias_app_hasType .rfl + (by type_tac)) .nil + · refine ⟨?_, ?_, trivial⟩ + · exact .inl rfl + · intro _ + exact .cons + (normalizationMatrixIndexAlias_app_hasType .rfl + (by type_tac)) .nil + · exact .cons + (normalizationMatrixIndexAlias_app_hasType .rfl (by type_tac)) .nil + +theorem normalizationMatrixBlock_wf : + normalizationMatrixBlock.WF normalizationMatrixAliasEnv := + ⟨normalizationMatrixNormalization_wf, + normalizationMatrixViewChecked_wf⟩ + +theorem normalizationMatrixGenerationChecked_wf : + normalizationMatrixGenerationChecked.WF normalizationMatrixAliasEnv := by + refine { + blockWF := normalizationMatrixBlock_wf + familyTel := ?_ + familyResult := ?_ + ctors := ?_ } + · refine ⟨⟨_, normalizationMatrixTypeFamily_defeq .rfl⟩, ?_⟩ + exact ⟨⟨_, normalizationMatrixTypeFamily_defeq .rfl⟩, trivial⟩ + · exact VEnv.IsDefEq.sortDF (by decide) (by decide) rfl + · intro envT hadd ctor hctor + change ctor ∈ + [⟨normalizationMatrixRawType.ctors[0], + normalizationMatrixViewChecked.constructors[0]⟩] at hctor + obtain rfl := List.mem_singleton.1 hctor + have henv : normalizationMatrixAliasEnv ≤ envT := + VEnv.addConst_le hadd + have hfamily : envT.constants ``NormalizationMatrix = + some normalizationMatrixRawType.toVConstant := + VEnv.addConst_self hadd + have hindexAlias : envT.constants ``MatrixIndexAlias = + some (vconst(type_of% @MatrixIndexAlias)) := + henv.constants (by rfl) + refine { + declaredTel := ?_ + declaredResult := ?_ + emittedTel := ?_ + emittedResult := ?_ } + · refine ⟨⟨_, normalizationMatrixTypeFamily_defeq henv⟩, ?_⟩ + refine ⟨⟨_, normalizationMatrixTypeFamily_defeq henv⟩, ?_⟩ + refine ⟨⟨_, normalizationMatrixRecAlias_app_defeq + (by decide) henv (by type_tac)⟩, ?_⟩ + refine ⟨⟨_, normalizationMatrixBetaAlias_app_defeq + (by decide) henv (by type_tac)⟩, ?_⟩ + refine ⟨⟨_, normalizationMatrixLetAlias_app_defeq + (by decide) henv (by type_tac)⟩, ?_⟩ + refine ⟨⟨_, normalizationMatrixRecAlias_app_defeq + (by decide) henv (by type_tac)⟩, ?_⟩ + refine ⟨⟨_, normalizationMatrixPiAlias_app_defeq + (by decide) henv (by type_tac)⟩, ?_⟩ + refine ⟨⟨_, normalizationMatrixBetaAlias_app_defeq + (by decide) henv (by type_tac)⟩, ?_⟩ + exact ⟨⟨_, normalizationMatrixLetAlias_app_defeq + (by decide) henv (by type_tac)⟩, trivial⟩ + · exact normalizationMatrixTarget_hasType henv hfamily + (by type_tac) (by type_tac) + · refine ⟨⟨_, by type_tac⟩, ?_⟩ + refine ⟨⟨_, normalizationMatrixTypeFamily_defeq henv⟩, ?_⟩ + refine ⟨⟨_, normalizationMatrixRecAlias_app_defeq + (by decide) henv (by type_tac)⟩, ?_⟩ + refine ⟨⟨_, normalizationMatrixBetaAlias_app_defeq + (by decide) henv (by type_tac)⟩, ?_⟩ + refine ⟨⟨_, normalizationMatrixLetAlias_app_defeq + (by decide) henv (by type_tac)⟩, ?_⟩ + refine ⟨⟨_, normalizationMatrixRecAlias_app_defeq + (by decide) henv (by + exact normalizationMatrixTarget_hasType henv hfamily + ((normalizationMatrixTypeFamily_defeq henv).defeq' + (by type_tac)) + (by type_tac))⟩, ?_⟩ + refine ⟨⟨_, normalizationMatrixPiAlias_app_defeq + (by decide) henv (by + exact normalizationMatrixTarget_hasType henv hfamily + ((normalizationMatrixTypeFamily_defeq henv).defeq' + (by type_tac)) + (by type_tac))⟩, ?_⟩ + refine ⟨⟨_, normalizationMatrixBetaAlias_app_defeq + (by decide) henv (by + exact normalizationMatrixTarget_hasType henv hfamily + ((normalizationMatrixTypeFamily_defeq henv).defeq' + (by type_tac)) + (by type_tac))⟩, ?_⟩ + exact ⟨⟨_, normalizationMatrixLetAlias_app_defeq + (by decide) henv (by + exact normalizationMatrixTarget_hasType henv hfamily + ((normalizationMatrixTypeFamily_defeq henv).defeq' + (by type_tac)) + (by type_tac))⟩, trivial⟩ + · exact normalizationMatrixTarget_hasType henv hfamily + ((normalizationMatrixTypeFamily_defeq henv).defeq' + (by type_tac)) + (by type_tac) + +def normalizationMatrixFinalEnv : VEnv := + (normalizationMatrixAliasEnv.addInductGeneration + normalizationMatrixGenerationChecked).get (by decide) + +theorem normalizationMatrix_addInductGeneration : + normalizationMatrixAliasEnv.addInductGeneration + normalizationMatrixGenerationChecked = + some normalizationMatrixFinalEnv := rfl + +theorem normalizationMatrixFinalEnv_trace : + Nonempty (VEnv.AddInductGenerationTrace normalizationMatrixAliasEnv + normalizationMatrixFinalEnv normalizationMatrixGenerationChecked) := + VEnv.addInductGeneration_trace normalizationMatrix_addInductGeneration + +theorem normalizationMatrixFinalEnv_family_lookup : + normalizationMatrixFinalEnv.constants ``NormalizationMatrix = + some normalizationMatrixRawType.toVConstant := by + rcases normalizationMatrixFinalEnv_trace with ⟨H⟩ + exact H.family_lookup + +theorem normalizationMatrixFinalEnv_ctor_lookup : + normalizationMatrixFinalEnv.constants ``NormalizationMatrix.mk = + some normalizationMatrixRawType.ctors[0].toVConstant := by + rcases normalizationMatrixFinalEnv_trace with ⟨H⟩ + exact H.ctor_lookup (.head _) + +theorem normalizationMatrixFinalEnv_rec_lookup : + normalizationMatrixFinalEnv.constants ``NormalizationMatrix.rec = + some (vconst(type_of% @NormalizationMatrix.rec)) := by + rcases normalizationMatrixFinalEnv_trace with ⟨H⟩ + exact H.rec_lookup + +theorem normalizationMatrixFinalEnv_iota_mem : + normalizationMatrixFinalEnv.defeqs + (vdefeq(alpha motive mk index ordinary beta letBound direct + piHidden betaRecursive letRecursive => + @NormalizationMatrix.rec alpha motive mk (MatrixIndexAlias index) + (@NormalizationMatrix.mk alpha index ordinary beta letBound direct + piHidden betaRecursive letRecursive) ≡ + mk index ordinary beta letBound direct piHidden betaRecursive + letRecursive + (@NormalizationMatrix.rec alpha motive mk + (MatrixIndexAlias index) direct) + (fun proof => @NormalizationMatrix.rec alpha motive mk + (MatrixIndexAlias index) (piHidden proof)) + (@NormalizationMatrix.rec alpha motive mk + (MatrixIndexAlias index) betaRecursive) + (@NormalizationMatrix.rec alpha motive mk + (MatrixIndexAlias index) letRecursive))) := by + rcases normalizationMatrixFinalEnv_trace with ⟨H⟩ + apply H.rule_mem + exact .head _ + +theorem normalizationMatrixFinalEnv_ordered : + normalizationMatrixFinalEnv.Ordered := + VEnv.addInductGeneration_WF normalizationMatrixAliasEnv_ordered + normalizationMatrixGenerationChecked_wf + normalizationMatrix_addInductGeneration + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.normalizationMatrixNormalization_wf [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.normalizationMatrixViewChecked_wf [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.normalizationMatrixGenerationChecked_wf [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.normalizationMatrixFinalEnv_trace [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.normalizationMatrixFinalEnv_iota_mem [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.normalizationMatrixFinalEnv_ordered [propext, Classical.choice, Quot.sound] + +/-! ## Checked-analysis rejection fixtures -/ + +/- +The next three declarations are kernel-side differential fixtures for the +recursive-Pi rejection branches. `#guard_msgs` checks the actual elaborator / +kernel outcome while rolling the failed declaration back, so the VExpr +fixtures below can independently exercise the Theory analyzer and public +transaction. +-/ + +namespace KernelDifferential + +/-- +error: (kernel) arg #1 of 'Ix.Theory.Named.InductiveFixtures.KernelDifferential.KernelRejectRecDomain.mk' has a non positive occurrence of the datatypes being declared +-/ +#guard_msgs (whitespace := lax) in +inductive KernelRejectRecDomain : Type where + | mk : (KernelRejectRecDomain → KernelRejectRecDomain) → KernelRejectRecDomain + +/-- +error: Mismatched inductive type parameter in + KernelRejectRecTarget β +The provided argument + β +is not definitionally equal to the expected parameter + α + +Note: The value of parameter `α` must be fixed throughout the inductive declaration. Consider making this parameter an index if it must vary. +-/ +#guard_msgs (whitespace := lax) in +inductive KernelRejectRecTarget (α : Type) : Type where + | mk : ((β : Type) → KernelRejectRecTarget β) → KernelRejectRecTarget α + +/-- +error: (kernel) arg #1 of 'Ix.Theory.Named.InductiveFixtures.KernelDifferential.KernelRejectRecIndex.mk' contains a non valid occurrence of the datatypes being declared +-/ +#guard_msgs (whitespace := lax) in +inductive KernelRejectRecIndex : Type → Type where + | mk : ((n : Nat) → KernelRejectRecIndex (KernelRejectRecIndex Nat)) → + KernelRejectRecIndex Nat + +end KernelDifferential + +/-- A recursive function argument may not mention the family in one of its +binder domains. This is the raw-VExpr counterpart of +`KernelRejectRecDomain`. -/ +def recDomainTypeName : Name := .mkSimple "RecDomainFixture" + +def recDomainCtorName : Name := .str recDomainTypeName "mk" + +def recDomainField : VExpr := + .forallE (.const recDomainTypeName []) (.const recDomainTypeName []) + +def recDomainType : VInductiveType where + name := recDomainTypeName + uvars := 0 + type := .sort (.succ .zero) + ctors := [⟨⟨0, .forallE recDomainField (.const recDomainTypeName [])⟩, + recDomainCtorName⟩] + +def recDomainDecl : VInductDecl := ⟨0, 0, [recDomainType]⟩ + +example : namesOK recDomainType = true := rfl +example : closedOK recDomainType = true := rfl +example : levelsOK 0 recDomainType = true := rfl +example : typeFormerOK 0 recDomainType = true := rfl +example : largeElim 0 recDomainTypeName 0 0 recDomainType = true := rfl +example : recTarget? 0 recDomainTypeName 0 0 0 recDomainField = none := rfl +example : stage3Field 0 recDomainTypeName 0 0 0 recDomainField = false := rfl +example : stage3DirectCore 0 0 recDomainType = false := rfl +example : recDomainDecl.checked? = none := rfl +example : VEnv.empty.addInduct recDomainDecl = none := rfl + +/-- A recursive target below a Pi must retain the declaration's fixed +parameters. Here the terminal target uses the locally bound `β` instead of +the outer parameter, matching `KernelRejectRecTarget`. -/ +def recTargetTypeName : Name := .mkSimple "RecTargetFixture" + +def recTargetCtorName : Name := .str recTargetTypeName "mk" + +def recTargetField : VExpr := + .forallE (.sort (.succ .zero)) + ((VExpr.const recTargetTypeName []).app (VExpr.bvar 0)) + +def recTargetType : VInductiveType where + name := recTargetTypeName + uvars := 0 + type := .forallE (.sort (.succ .zero)) (.sort (.succ .zero)) + ctors := [⟨⟨0, + .forallE (.sort (.succ .zero)) + (.forallE recTargetField + ((VExpr.const recTargetTypeName []).app (VExpr.bvar 1)))⟩, + recTargetCtorName⟩] + +def recTargetDecl : VInductDecl := ⟨0, 1, [recTargetType]⟩ + +example : namesOK recTargetType = true := rfl +example : closedOK recTargetType = true := rfl +example : levelsOK 0 recTargetType = true := rfl +example : typeFormerOK 1 recTargetType = true := rfl +example : largeElim 0 recTargetTypeName 1 0 recTargetType = true := rfl +example : isRecField 0 recTargetTypeName 1 0 1 + ((VExpr.const recTargetTypeName []).app (VExpr.bvar 0)) = false := rfl +example : recTarget? 0 recTargetTypeName 1 0 0 recTargetField = none := rfl +example : stage3Field 0 recTargetTypeName 1 0 0 recTargetField = false := rfl +example : stage3DirectCore 0 1 recTargetType = false := rfl +example : recTargetDecl.checked? = none := rfl +example : VEnv.empty.addInduct recTargetDecl = none := rfl + +/-- Recursive target indices must be free of the family. The inner +`RecIndexFixture Nat` occurs inside the outer recursive target's index, +matching `KernelRejectRecIndex`. -/ +def recIndexTypeName : Name := .mkSimple "RecIndexFixture" + +def recIndexCtorName : Name := .str recIndexTypeName "mk" + +def recIndexTarget : VExpr := + (VExpr.const recIndexTypeName []).app + ((VExpr.const recIndexTypeName []).app (VExpr.const ``Nat [])) + +def recIndexField : VExpr := .forallE (VExpr.const ``Nat []) recIndexTarget + +def recIndexType : VInductiveType where + name := recIndexTypeName + uvars := 0 + type := .forallE (.sort (.succ .zero)) (.sort (.succ .zero)) + ctors := [⟨⟨0, + .forallE recIndexField + ((VExpr.const recIndexTypeName []).app (VExpr.const ``Nat []))⟩, + recIndexCtorName⟩] + +def recIndexDecl : VInductDecl := ⟨0, 0, [recIndexType]⟩ + +example : namesOK recIndexType = true := rfl +example : closedOK recIndexType = true := rfl +example : levelsOK 0 recIndexType = true := rfl +example : typeFormerOK 0 recIndexType = true := rfl +example : largeElim 0 recIndexTypeName 0 1 recIndexType = true := rfl +example : isRecField 0 recIndexTypeName 0 1 1 recIndexTarget = false := rfl +example : recTarget? 0 recIndexTypeName 0 1 0 recIndexField = none := rfl +example : stage3Field 0 recIndexTypeName 0 1 0 recIndexField = false := rfl +example : stage3DirectCore 0 0 recIndexType = false := rfl +example : recIndexDecl.checked? = none := rfl +example : VEnv.empty.addInduct recIndexDecl = none := rfl + +/-- The old direct-recursion shape alone admits duplicate constructor names; +the shared I2 descriptor rejects the block before a partial transaction. -/ +def duplicateCtorTypeName : Name := .mkSimple "DuplicateCtorFixture" + +def duplicateCtorName : Name := .str duplicateCtorTypeName "mk" + +def duplicateCtorType : VInductiveType where + name := duplicateCtorTypeName + uvars := 0 + type := VExpr.sort (.succ .zero) + ctors := [⟨⟨0, VExpr.const duplicateCtorTypeName []⟩, duplicateCtorName⟩, + ⟨⟨0, VExpr.const duplicateCtorTypeName []⟩, duplicateCtorName⟩] + +def duplicateCtorDecl : VInductDecl := ⟨0, 0, [duplicateCtorType]⟩ + +example : stage3DirectCore 0 0 duplicateCtorType = true := rfl +example : namesOK duplicateCtorType = false := rfl +example : duplicateCtorDecl.checked? = none := rfl +example : VEnv.empty.addInduct duplicateCtorDecl = none := rfl + +/-- Internal uniqueness covers collisions between different generated roles, +not only two constructors with the same name. -/ +def typeCtorAliasName : Name := .mkSimple "TypeCtorAliasFixture" + +def typeCtorAliasType : VInductiveType where + name := typeCtorAliasName + uvars := 0 + type := VExpr.sort (.succ .zero) + ctors := [⟨⟨0, VExpr.const typeCtorAliasName []⟩, typeCtorAliasName⟩] + +def typeCtorAliasDecl : VInductDecl := ⟨0, 0, [typeCtorAliasType]⟩ + +example : stage3DirectCore 0 0 typeCtorAliasType = true := rfl +example : namesOK typeCtorAliasType = false := rfl +example : typeCtorAliasDecl.checked? = none := rfl + +def ctorRecAliasTypeName : Name := .mkSimple "CtorRecAliasFixture" + +def ctorRecAliasType : VInductiveType where + name := ctorRecAliasTypeName + uvars := 0 + type := VExpr.sort (.succ .zero) + ctors := [⟨⟨0, VExpr.const ctorRecAliasTypeName []⟩, + .str ctorRecAliasTypeName "rec"⟩] + +def ctorRecAliasDecl : VInductDecl := ⟨0, 0, [ctorRecAliasType]⟩ + +example : stage3DirectCore 0 0 ctorRecAliasType = true := rfl +example : namesOK ctorRecAliasType = false := rfl +example : ctorRecAliasDecl.checked? = none := rfl + +/-- A constructor result with a loose index variable has the expected direct +head/spine shape, but is not legal closed kernel metadata. -/ +def looseIndexTypeName : Name := .mkSimple "LooseIndexFixture" + +def looseIndexCtorName : Name := .str looseIndexTypeName "mk" + +def looseIndexType : VInductiveType where + name := looseIndexTypeName + uvars := 0 + type := VExpr.forallE (VExpr.const ``Nat []) (VExpr.sort (.succ .zero)) + ctors := [⟨⟨0, (VExpr.const looseIndexTypeName []).app (.bvar 0)⟩, + looseIndexCtorName⟩] + +def looseIndexDecl : VInductDecl := ⟨0, 0, [looseIndexType]⟩ + +example : stage3DirectCore 0 0 looseIndexType = true := rfl +example : closedOK looseIndexType = false := rfl +example : looseIndexDecl.checked? = none := rfl +example : VEnv.empty.addInduct looseIndexDecl = none := rfl + +/-- The family type is checked before its own constant exists. A self +occurrence hidden in a parameter domain must therefore be rejected even when +the constructor result has the otherwise expected head and parameter spine. -/ +def selfParamTypeName : Name := .mkSimple "SelfParamFixture" + +def selfParamCtorName : Name := .str selfParamTypeName "mk" + +def selfParamType : VInductiveType where + name := selfParamTypeName + uvars := 0 + type := VExpr.forallE (VExpr.const selfParamTypeName []) (VExpr.sort (.succ .zero)) + ctors := [⟨⟨0, VExpr.forallE (VExpr.const selfParamTypeName []) + ((VExpr.const selfParamTypeName []).app (.bvar 0))⟩, selfParamCtorName⟩] + +def selfParamDecl : VInductDecl := ⟨0, 1, [selfParamType]⟩ + +example : typeFormerOK 1 selfParamType = false := rfl +example : closedOK selfParamType = true := rfl +example : levelsOK 0 selfParamType = true := rfl +example : selfParamDecl.checked? = none := rfl +example : VEnv.empty.addInduct selfParamDecl = none := rfl + +/-- An out-of-range universe can hide in a parameter while the result sort and +direct constructor shape remain valid. `levelsOK` checks the complete metadata +rather than only the result. -/ +def badParamLevelTypeName : Name := .mkSimple "BadParamLevelFixture" + +def badParamLevelCtorName : Name := .str badParamLevelTypeName "mk" + +def badParamLevelType : VInductiveType where + name := badParamLevelTypeName + uvars := 0 + type := VExpr.forallE (VExpr.sort (.param 0)) (VExpr.sort (.succ .zero)) + ctors := [⟨⟨0, VExpr.forallE (VExpr.sort (.param 0)) + ((VExpr.const badParamLevelTypeName []).app (.bvar 0))⟩, badParamLevelCtorName⟩] + +def badParamLevelDecl : VInductDecl := ⟨0, 1, [badParamLevelType]⟩ + +example : stage3DirectCore 0 1 badParamLevelType = true := rfl +example : closedOK badParamLevelType = true := rfl +example : levelsOK 0 badParamLevelType = false := rfl +example : badParamLevelDecl.checked? = none := rfl +example : VEnv.empty.addInduct badParamLevelDecl = none := rfl + +/-- Constructor fields receive the same full universe-range check. -/ +def badCtorLevelTypeName : Name := .mkSimple "BadCtorLevelFixture" + +def badCtorLevelCtorName : Name := .str badCtorLevelTypeName "mk" + +def badCtorLevelType : VInductiveType where + name := badCtorLevelTypeName + uvars := 0 + type := VExpr.sort (.succ .zero) + ctors := [⟨⟨0, VExpr.forallE (VExpr.sort (.param 0)) + (VExpr.const badCtorLevelTypeName [])⟩, badCtorLevelCtorName⟩] + +def badCtorLevelDecl : VInductDecl := ⟨0, 0, [badCtorLevelType]⟩ + +example : stage3DirectCore 0 0 badCtorLevelType = true := rfl +example : closedOK badCtorLevelType = true := rfl +example : levelsOK 0 badCtorLevelType = false := rfl +example : badCtorLevelDecl.checked? = none := rfl +example : VEnv.empty.addInduct badCtorLevelDecl = none := rfl + +/-- A non-sort family result never reaches descriptor construction. -/ +def nonSortResultTypeName : Name := .mkSimple "NonSortResultFixture" + +def nonSortResultType : VInductiveType where + name := nonSortResultTypeName + uvars := 0 + type := VExpr.const ``Nat [] + ctors := [] + +def nonSortResultDecl : VInductDecl := ⟨0, 0, [nonSortResultType]⟩ + +example : stage3DirectCore 0 0 nonSortResultType = false := rfl +example : nonSortResultDecl.checked? = none := rfl +example : VEnv.empty.addInduct nonSortResultDecl = none := rfl + +/-- A constructor must return the family being declared, not merely any +well-formed closed type. -/ +def wrongCtorHeadTypeName : Name := .mkSimple "WrongCtorHeadFixture" + +def wrongCtorHeadCtorName : Name := .str wrongCtorHeadTypeName "mk" + +def wrongCtorHeadType : VInductiveType where + name := wrongCtorHeadTypeName + uvars := 0 + type := VExpr.sort (.succ .zero) + ctors := [⟨⟨0, VExpr.const ``Nat []⟩, wrongCtorHeadCtorName⟩] + +def wrongCtorHeadDecl : VInductDecl := ⟨0, 0, [wrongCtorHeadType]⟩ + +example : stage3DirectCore 0 0 wrongCtorHeadType = false := rfl +example : wrongCtorHeadDecl.checked? = none := rfl +example : VEnv.empty.addInduct wrongCtorHeadDecl = none := rfl + +/-- Constructor result parameters must be the declaration's parameter +variables in order, not arbitrary closed expressions of a plausible shape. -/ +def wrongParamSpineTypeName : Name := .mkSimple "WrongParamSpineFixture" + +def wrongParamSpineCtorName : Name := .str wrongParamSpineTypeName "mk" + +def wrongParamSpineType : VInductiveType where + name := wrongParamSpineTypeName + uvars := 0 + type := VExpr.forallE (VExpr.sort (.succ .zero)) (VExpr.sort (.succ .zero)) + ctors := [⟨⟨0, VExpr.forallE (VExpr.sort (.succ .zero)) + ((VExpr.const wrongParamSpineTypeName []).app (.sort .zero))⟩, + wrongParamSpineCtorName⟩] + +def wrongParamSpineDecl : VInductDecl := ⟨0, 1, [wrongParamSpineType]⟩ + +example : stage3DirectCore 0 1 wrongParamSpineType = false := rfl +example : wrongParamSpineDecl.checked? = none := rfl +example : VEnv.empty.addInduct wrongParamSpineDecl = none := rfl + +/-- `nparams` cannot exceed the actual leading pi telescope. -/ +def shortParamDecl : VInductDecl := ⟨0, 1, [natType]⟩ + +example : stage3DirectCore 0 1 natType = false := rfl +example : shortParamDecl.checked? = none := rfl +example : VEnv.empty.addInduct shortParamDecl = none := rfl + +/-- Declaration, family, and constructor universe counts must agree even when +the expressions themselves happen not to mention a level parameter. -/ +def badTypeUvars : VInductiveType := { natType with uvars := 1 } +def badTypeUvarsDecl : VInductDecl := ⟨0, 0, [badTypeUvars]⟩ + +example : stage3DirectCore 0 0 badTypeUvars = false := rfl +example : badTypeUvarsDecl.checked? = none := rfl + +def badCtorUvarsType : VInductiveType := + { natType with ctors := [{ natType.ctors[0] with uvars := 1 }, natType.ctors[1]] } +def badCtorUvarsDecl : VInductDecl := ⟨0, 0, [badCtorUvarsType]⟩ + +example : stage3DirectCore 0 0 badCtorUvarsType = false := rfl +example : badCtorUvarsDecl.checked? = none := rfl + +/-! Environment-relative collisions are checked transactionally, after the +environment-independent descriptor has established internal `Nodup`. -/ + +def ctorCollisionEnv : VEnv := + (VEnv.empty.addConst ``Nat.zero ⟨0, .sort .zero⟩).get (by decide) + +example : ctorCollisionEnv.constants ``Nat.zero = some ⟨0, .sort .zero⟩ := rfl +example : ctorCollisionEnv.addInduct natDecl = none := + VEnv.addInduct_eq_none_of_ctor_present (.head _) (.head _) ⟨_, rfl⟩ + +def recCollisionEnv : VEnv := + (VEnv.empty.addConst ``Nat.rec ⟨0, .sort .zero⟩).get (by decide) + +example : recCollisionEnv.constants ``Nat.rec = some ⟨0, .sort .zero⟩ := rfl +example : recCollisionEnv.addInduct natDecl = none := + VEnv.addInduct_eq_none_of_rec_present + (generation := natBlockGenerationChecked) rfl (.head _) ⟨_, rfl⟩ + +/-! ## Small elimination -/ + +/-- `Or` is the canonical small-elimination family: its motive remains in +`Prop`, so the generated recursor introduces no fresh universe parameter. -/ +def orType : VInductiveType where + name := ``Or + uvars := 0 + type := vconst(type_of% @Or).type + ctors := [⟨vconst(type_of% @Or.inl), ``Or.inl⟩, + ⟨vconst(type_of% @Or.inr), ``Or.inr⟩] + +def orDecl : VInductDecl := ⟨0, 2, [orType]⟩ + +def orChecked : orDecl.Checked := orDecl.checked?.get (by decide) + +example : orDecl.stage3 = true := rfl +example : orChecked.elimination = .small := rfl +example : orChecked.kTarget = false := rfl +example : orChecked.recursor.uvars = 0 := rfl +example : orChecked.recursor = vconst(type_of% @Or.rec) := rfl +example : orChecked.generatedRules[0]? = + some (vdefeq(a b motive inl inr h => + @Or.rec a b motive inl inr (@Or.inl a b h) ≡ inl h)) := rfl +example : orChecked.generatedRules[1]? = + some (vdefeq(a b motive inl inr h => + @Or.rec a b motive inl inr (@Or.inr a b h) ≡ inr h)) := rfl +example : (VEnv.empty.addInduct orDecl).isSome = true := rfl + +/-- `And` is the canonical singleton-Prop exception: both constructor fields +are proofs, so Lean legitimately gives it a large eliminator with one fresh +universe parameter. -/ +def andType : VInductiveType where + name := ``And + uvars := 0 + type := vconst(type_of% @And).type + ctors := [⟨vconst(type_of% @And.intro), ``And.intro⟩] + +def andDecl : VInductDecl := ⟨0, 2, [andType]⟩ + +def andChecked : andDecl.Checked := andDecl.checked?.get (by decide) + +example : andDecl.stage3 = true := rfl +example : andChecked.elimination = .large := rfl +/-- Large elimination and K eligibility are independent: `And` has the +singleton-proof elimination exception, but its constructor has fields. -/ +example : andChecked.kTarget = false := rfl +example : andChecked.recursor.uvars = 1 := rfl +example : andChecked.recursor = vconst(type_of% @And.rec) := rfl +example : andChecked.generatedRules[0]? = + some (vdefeq(a b motive intro left right => + @And.rec a b motive intro (@And.intro a b left right) ≡ + intro left right)) := rfl +example : (VEnv.empty.addInduct andDecl).isSome = true := rfl + +/-! ## Conservativity: malformed declarations and name collisions refuse. -/ + +/-- A name collision rejects the whole transaction. -/ +example (env : VEnv) (h : env.contains ``Nat) : env.addInduct natDecl = none := + VEnv.addInduct_eq_none_of_type_present (.head _) h diff --git a/Ix/Theory/Named/LICENSE b/Ix/Theory/Named/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/Ix/Theory/Named/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Ix/Theory/Named/Literals.lean b/Ix/Theory/Named/Literals.lean new file mode 100644 index 000000000..47a31b000 --- /dev/null +++ b/Ix/Theory/Named/Literals.lean @@ -0,0 +1,682 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Inductive +import Ix.Theory.Named.Typing.Strong + +open Ix.Theory (VLevel) + +/-! # Theory encodings of Lean literals and primitive reflection + +This file contains only `VExpr`/`VEnv` semantics. Traversal of `Lean.Expr` +and `Literal.toConstructor` belongs to the Verify translation layer. +-/ + +namespace Ix.Theory.Named +open Lean + +def VEnv.ContainsLits (env : VEnv) : Literal → Prop + | .natVal _ => env.contains ``Nat + | .strVal _ => env.contains ``Char.ofNat ∧ env.contains ``String.ofList + +def VExpr.bool : VExpr := .const ``Bool [] +def VExpr.boolTrue : VExpr := .const ``Bool.true [] +def VExpr.boolFalse : VExpr := .const ``Bool.false [] +def VExpr.boolLit : Bool → VExpr + | .false => .boolFalse + | .true => .boolTrue + +def VExpr.nat : VExpr := .const ``Nat [] +def VExpr.natZero : VExpr := .const ``Nat.zero [] +def VExpr.natSucc : VExpr := .const ``Nat.succ [] +def VExpr.natLit : Nat → VExpr + | 0 => .natZero + | n+1 => .app .natSucc (.natLit n) + +def VExpr.char : VExpr := .const ``Char [] +def VExpr.string : VExpr := .const ``String [] +def VExpr.stringOfList : VExpr := .const ``String.ofList [] +def VExpr.listChar : VExpr := .app (.const ``List [.zero]) .char +def VExpr.listCharNil : VExpr := .app (.const ``List.nil [.zero]) .char +def VExpr.listCharCons : VExpr := .app (.const ``List.cons [.zero]) .char +def VExpr.charOfNat : VExpr := .const ``Char.ofNat [] +def VExpr.listCharLit : List Char → VExpr + | [] => .listCharNil + | a :: as => + .app (.app .listCharCons (.app .charOfNat (.natLit a.toNat))) (.listCharLit as) + +def VExpr.trLiteral : Literal → VExpr + | .natVal n => .natLit n + | .strVal s => .app .stringOfList (.listCharLit s.toList) + +def VExpr.literalType : Literal → VExpr + | .natVal _ => .nat + | .strVal _ => .string + +/-! ## Exact prelude artifacts + +`ContainsLits` deliberately records only name occurrence. The declarations +below describe the exact Theory artifacts that make those names meaningful. +The inductive recursors and iota rules are generated by the same +consumer-neutral Theory machinery used by `VEnv.addInduct`. +-/ + +namespace LiteralPrelude + +def boolFalse : VConstVal := + { name := ``Bool.false, uvars := 0, type := .bool } + +def boolTrue : VConstVal := + { name := ``Bool.true, uvars := 0, type := .bool } + +def boolType : VInductiveType where + name := ``Bool + uvars := 0 + type := .sort (.succ .zero) + ctors := [boolFalse, boolTrue] + +def boolRec : VConstant := VInductDecl.recConst 0 ``Bool 0 boolType +def boolIotas : List VDefEq := VInductDecl.rules 0 ``Bool 0 boolType + +def natZero : VConstVal := + { name := ``Nat.zero, uvars := 0, type := .nat } + +def natSucc : VConstVal := + { name := ``Nat.succ, uvars := 0, type := .forallE .nat .nat } + +def natType : VInductiveType where + name := ``Nat + uvars := 0 + type := .sort (.succ .zero) + ctors := [natZero, natSucc] + +def natRec : VConstant := VInductDecl.recConst 0 ``Nat 0 natType +def natIotas : List VDefEq := VInductDecl.rules 0 ``Nat 0 natType + +def char : VConstant := { uvars := 0, type := .sort (.succ .zero) } +def charOfNat : VConstant := { uvars := 0, type := .forallE .nat .char } + +def listNil : VConstVal where + name := ``List.nil + uvars := 1 + type := .forallE (.sort (.succ (.param 0))) + (.app (.const ``List [.param 0]) (.bvar 0)) + +def listCons : VConstVal where + name := ``List.cons + uvars := 1 + type := .forallE (.sort (.succ (.param 0))) <| + .forallE (.bvar 0) <| + .forallE (.app (.const ``List [.param 0]) (.bvar 1)) + (.app (.const ``List [.param 0]) (.bvar 2)) + +def listType : VInductiveType where + name := ``List + uvars := 1 + type := .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0))) + ctors := [listNil, listCons] + +def listRec : VConstant := VInductDecl.recConst 1 ``List 1 listType +def listIotas : List VDefEq := VInductDecl.rules 1 ``List 1 listType + +def string : VConstant := { uvars := 0, type := .sort (.succ .zero) } +def stringOfList : VConstant := + { uvars := 0, type := .forallE .listChar .string } + +end LiteralPrelude + +/-- The exact kernel-facing prelude fragment needed to interpret Theory +literals. In contrast with `ContainsLits`, this records declaration types, +recursors, iota rules, and an ordered construction history. -/ +structure VEnv.PreludeReady (env : VEnv) : Prop where + ordered : env.Ordered + bool : env.constants ``Bool = some LiteralPrelude.boolType.toVConstant + boolFalse : env.constants ``Bool.false = + some LiteralPrelude.boolFalse.toVConstant + boolTrue : env.constants ``Bool.true = + some LiteralPrelude.boolTrue.toVConstant + boolRec : env.constants ``Bool.rec = some LiteralPrelude.boolRec + boolIotas : ∀ df ∈ LiteralPrelude.boolIotas, env.defeqs df + nat : env.constants ``Nat = some LiteralPrelude.natType.toVConstant + natZero : env.constants ``Nat.zero = + some LiteralPrelude.natZero.toVConstant + natSucc : env.constants ``Nat.succ = + some LiteralPrelude.natSucc.toVConstant + natRec : env.constants ``Nat.rec = some LiteralPrelude.natRec + natIotas : ∀ df ∈ LiteralPrelude.natIotas, env.defeqs df + char : env.constants ``Char = some LiteralPrelude.char + charOfNat : env.constants ``Char.ofNat = some LiteralPrelude.charOfNat + list : env.constants ``List = some LiteralPrelude.listType.toVConstant + listNil : env.constants ``List.nil = + some LiteralPrelude.listNil.toVConstant + listCons : env.constants ``List.cons = + some LiteralPrelude.listCons.toVConstant + listRec : env.constants ``List.rec = some LiteralPrelude.listRec + listIotas : ∀ df ∈ LiteralPrelude.listIotas, env.defeqs df + string : env.constants ``String = some LiteralPrelude.string + stringOfList : env.constants ``String.ofList = some LiteralPrelude.stringOfList + +namespace VEnv.PreludeReady + +/-- Exact prelude artifacts transport across environment inclusion. The +target ordering premise is necessary because arbitrary `VEnv.LE` growth may +append an ill-typed declaration. -/ +theorem mono {env env' : VEnv} (H : env.PreludeReady) (henv : env ≤ env') + (hordered : env'.Ordered) : env'.PreludeReady where + ordered := hordered + bool := henv.constants H.bool + boolFalse := henv.constants H.boolFalse + boolTrue := henv.constants H.boolTrue + boolRec := henv.constants H.boolRec + boolIotas := fun df hdf => henv.defeqs (H.boolIotas df hdf) + nat := henv.constants H.nat + natZero := henv.constants H.natZero + natSucc := henv.constants H.natSucc + natRec := henv.constants H.natRec + natIotas := fun df hdf => henv.defeqs (H.natIotas df hdf) + char := henv.constants H.char + charOfNat := henv.constants H.charOfNat + list := henv.constants H.list + listNil := henv.constants H.listNil + listCons := henv.constants H.listCons + listRec := henv.constants H.listRec + listIotas := fun df hdf => henv.defeqs (H.listIotas df hdf) + string := henv.constants H.string + stringOfList := henv.constants H.stringOfList + +/-- Any successful well-formed constant insertion preserves readiness. It is +necessarily unrelated to the ready prelude: all of those names are already +occupied, while `addConst` succeeds only at a fresh name. -/ +theorem addConst {env env' : VEnv} (H : env.PreludeReady) + (hci : ci.WF env) (hadd : env.addConst name ci = some env') : + env'.PreludeReady := + H.mono (VEnv.addConst_le hadd) (.const H.ordered hci hadd) + +/-- Adding a well-formed unrelated definitional equation preserves prelude +readiness. -/ +theorem addDefEq {env : VEnv} (H : env.PreludeReady) (hdf : df.WF env) : + (env.addDefEq df).PreludeReady := + H.mono VEnv.addDefEq_le (.defeq H.ordered hdf) + +/-- A ready prelude contains every name used by the direct literal encoding. -/ +theorem containsLits {env : VEnv} (H : env.PreludeReady) : + ∀ l, env.ContainsLits l + | .natVal _ => ⟨_, H.nat⟩ + | .strVal _ => ⟨⟨_, H.charOfNat⟩, ⟨_, H.stringOfList⟩⟩ + +end VEnv.PreludeReady + +def VEnv.ReflectsNatNatNat (env : VEnv) (fc : Name) (f : Nat → Nat → Nat) := + env.contains fc → + ∀ a b, env.IsDefEqU 0 [] + (.app (.app (.const fc []) (.natLit a)) (.natLit b)) (.natLit (f a b)) + +def VEnv.ReflectsNatNatBool (env : VEnv) (fc : Name) (f : Nat → Nat → Bool) := + env.contains fc → + ∀ a b, env.IsDefEqU 0 [] + (.app (.app (.const fc []) (.natLit a)) (.natLit b)) (.boolLit (f a b)) + +structure VEnv.HasPrimitives (env : VEnv) : Prop where + bool : env.contains ``Bool → env.contains ``Bool.false ∧ env.contains ``Bool.true + boolFalse : env.constants ``Bool.false = some ci → ci = { uvars := 0, type := .bool } + boolTrue : env.constants ``Bool.true = some ci → ci = { uvars := 0, type := .bool } + nat : env.contains ``Nat → env.contains ``Nat.zero ∧ env.contains ``Nat.succ + natZero : env.constants ``Nat.zero = some ci → ci = { uvars := 0, type := .nat } + natSucc : env.constants ``Nat.succ = some ci → + ci = { uvars := 0, type := .forallE .nat .nat } + natAdd : env.ReflectsNatNatNat ``Nat.add Nat.add + natSub : env.ReflectsNatNatNat ``Nat.sub Nat.sub + natMul : env.ReflectsNatNatNat ``Nat.mul Nat.mul + natPow : env.ReflectsNatNatNat ``Nat.pow Nat.pow + natGcd : env.ReflectsNatNatNat ``Nat.gcd Nat.gcd + natMod : env.ReflectsNatNatNat ``Nat.mod Nat.mod + natDiv : env.ReflectsNatNatNat ``Nat.div Nat.div + natBEq : env.ReflectsNatNatBool ``Nat.beq Nat.beq + natBLE : env.ReflectsNatNatBool ``Nat.ble Nat.ble + natLAnd : env.ReflectsNatNatNat ``Nat.land Nat.land + natLOr : env.ReflectsNatNatNat ``Nat.lor Nat.lor + natXor : env.ReflectsNatNatNat ``Nat.xor Nat.xor + natShiftLeft : env.ReflectsNatNatNat ``Nat.shiftLeft Nat.shiftLeft + natShiftRight : env.ReflectsNatNatNat ``Nat.shiftRight Nat.shiftRight + charOfNat : env.constants ``Char.ofNat = some ci → + ci = { uvars := 0, type := .forallE .nat .char } + stringOfList : env.constants ``String.ofList = some ci → + ci = { uvars := 0, type := .forallE .listChar .string } ∧ + env.HasType 0 [] .listCharNil .listChar ∧ + env.HasType 0 [] .listCharCons (.forallE .char <| .forallE .listChar .listChar) + +/-- A well-formed Boolean literal can only occur when the corresponding +Boolean declaration is present. -/ +theorem VExpr.WF.boolLit_has_type (wf : env.Ordered) + (henv : env.HasPrimitives) (hΓ : OnCtx Γ (env.IsType U)) + (H : VExpr.WF env U Γ (.boolLit b)) : env.contains ``Bool := by + suffices env.HasType U Γ (.boolLit b) .bool by + have ⟨_, H⟩ := this.isType wf hΓ + have ⟨_, H, _⟩ := VEnv.HasType.const_inv wf hΓ H + exact ⟨_, H⟩ + cases b with + have ⟨_, h1, h2, h3⟩ := + let ⟨_, H⟩ := H + VEnv.HasType.const_inv wf hΓ H + | false => cases henv.boolFalse h1; exact .const h1 h2 h3 + | true => cases henv.boolTrue h1; exact .const h1 h2 h3 + +/-- The primitive constants whose Theory reflections are tracked by +`VEnv.HasPrimitives`. `Nat.pred` and `Nat.bitwise` are kernel primitive names +too, but they have no dedicated fields in that contract. -/ +def VEnv.reflectedPrimitiveNames : List Name := [ + ``Bool, ``Bool.false, ``Bool.true, + ``Nat, ``Nat.zero, ``Nat.succ, + ``Nat.add, ``Nat.sub, ``Nat.mul, ``Nat.pow, + ``Nat.gcd, ``Nat.mod, ``Nat.div, ``Nat.beq, ``Nat.ble, + ``Nat.land, ``Nat.lor, ``Nat.xor, + ``Nat.shiftLeft, ``Nat.shiftRight, + ``Char.ofNat, ``String.ofList] + +/-- An environment containing none of the hard-coded reflected primitive +names satisfies the primitive-reflection contract vacuously. -/ +theorem VEnv.HasPrimitives.of_avoids + {env : VEnv} + (h : ∀ n ∈ VEnv.reflectedPrimitiveNames, env.constants n = none) : + env.HasPrimitives := by + have noContains (n) (hn : n ∈ VEnv.reflectedPrimitiveNames) : + ¬env.contains n := by + rintro ⟨ci, hci⟩ + rw [h n hn] at hci + contradiction + have noLookup (n) (hn : n ∈ VEnv.reflectedPrimitiveNames) + {ci} (hci : env.constants n = some ci) : False := by + rw [h n hn] at hci + contradiction + exact { + bool := fun hc => + (noContains ``Bool (by simp [VEnv.reflectedPrimitiveNames]) hc).elim + boolFalse := fun hci => + (noLookup ``Bool.false + (by simp [VEnv.reflectedPrimitiveNames]) hci).elim + boolTrue := fun hci => + (noLookup ``Bool.true + (by simp [VEnv.reflectedPrimitiveNames]) hci).elim + nat := fun hc => + (noContains ``Nat (by simp [VEnv.reflectedPrimitiveNames]) hc).elim + natZero := fun hci => + (noLookup ``Nat.zero + (by simp [VEnv.reflectedPrimitiveNames]) hci).elim + natSucc := fun hci => + (noLookup ``Nat.succ + (by simp [VEnv.reflectedPrimitiveNames]) hci).elim + natAdd := fun hc => + (noContains ``Nat.add + (by simp [VEnv.reflectedPrimitiveNames]) hc).elim + natSub := fun hc => + (noContains ``Nat.sub + (by simp [VEnv.reflectedPrimitiveNames]) hc).elim + natMul := fun hc => + (noContains ``Nat.mul + (by simp [VEnv.reflectedPrimitiveNames]) hc).elim + natPow := fun hc => + (noContains ``Nat.pow + (by simp [VEnv.reflectedPrimitiveNames]) hc).elim + natGcd := fun hc => + (noContains ``Nat.gcd + (by simp [VEnv.reflectedPrimitiveNames]) hc).elim + natMod := fun hc => + (noContains ``Nat.mod + (by simp [VEnv.reflectedPrimitiveNames]) hc).elim + natDiv := fun hc => + (noContains ``Nat.div + (by simp [VEnv.reflectedPrimitiveNames]) hc).elim + natBEq := fun hc => + (noContains ``Nat.beq + (by simp [VEnv.reflectedPrimitiveNames]) hc).elim + natBLE := fun hc => + (noContains ``Nat.ble + (by simp [VEnv.reflectedPrimitiveNames]) hc).elim + natLAnd := fun hc => + (noContains ``Nat.land + (by simp [VEnv.reflectedPrimitiveNames]) hc).elim + natLOr := fun hc => + (noContains ``Nat.lor + (by simp [VEnv.reflectedPrimitiveNames]) hc).elim + natXor := fun hc => + (noContains ``Nat.xor + (by simp [VEnv.reflectedPrimitiveNames]) hc).elim + natShiftLeft := fun hc => + (noContains ``Nat.shiftLeft + (by simp [VEnv.reflectedPrimitiveNames]) hc).elim + natShiftRight := fun hc => + (noContains ``Nat.shiftRight + (by simp [VEnv.reflectedPrimitiveNames]) hc).elim + charOfNat := fun hci => + (noLookup ``Char.ofNat + (by simp [VEnv.reflectedPrimitiveNames]) hci).elim + stringOfList := fun hci => + (noLookup ``String.ofList + (by simp [VEnv.reflectedPrimitiveNames]) hci).elim } + +/-- A fresh Theory constant leaves every other lookup unchanged. -/ +theorem VEnv.addConst_other + {env env' : VEnv} {name other : Name} {ci : VConstant} + (hadd : env.addConst name ci = some env') + (hne : name ≠ other) : + env'.constants other = env.constants other := by + unfold VEnv.addConst at hadd + split at hadd <;> cases hadd + simp [hne] + +/-- Inserting a non-reflected constant preserves the primitive-reflection +contract. -/ +theorem VEnv.HasPrimitives.addConst + {env env' : VEnv} {name : Name} {ci : VConstant} + (H : env.HasPrimitives) + (hname : name ∉ VEnv.reflectedPrimitiveNames) + (hadd : env.addConst name ci = some env') : + env'.HasPrimitives := by + have lookup (other : Name) (hother : other ∈ VEnv.reflectedPrimitiveNames) : + env'.constants other = env.constants other := + VEnv.addConst_other hadd (by + intro equality + apply hname + simpa only [equality] using hother) + have oldContains (other : Name) + (hother : other ∈ VEnv.reflectedPrimitiveNames) : + env'.contains other → env.contains other := by + rintro ⟨value, hvalue⟩ + exact ⟨value, by simpa only [lookup other hother] using hvalue⟩ + have newContains (other : Name) : + env.contains other → env'.contains other := by + rintro ⟨value, hvalue⟩ + exact ⟨value, (VEnv.addConst_le hadd).constants hvalue⟩ + have hle := VEnv.addConst_le hadd + exact { + bool := fun h => by + obtain ⟨hfalse, htrue⟩ := H.bool (oldContains ``Bool + (by simp [VEnv.reflectedPrimitiveNames]) h) + exact ⟨newContains _ hfalse, newContains _ htrue⟩ + boolFalse := fun h => H.boolFalse (by + simpa only [lookup ``Bool.false + (by simp [VEnv.reflectedPrimitiveNames])] using h) + boolTrue := fun h => H.boolTrue (by + simpa only [lookup ``Bool.true + (by simp [VEnv.reflectedPrimitiveNames])] using h) + nat := fun h => by + obtain ⟨hzero, hsucc⟩ := H.nat (oldContains ``Nat + (by simp [VEnv.reflectedPrimitiveNames]) h) + exact ⟨newContains _ hzero, newContains _ hsucc⟩ + natZero := fun h => H.natZero (by + simpa only [lookup ``Nat.zero + (by simp [VEnv.reflectedPrimitiveNames])] using h) + natSucc := fun h => H.natSucc (by + simpa only [lookup ``Nat.succ + (by simp [VEnv.reflectedPrimitiveNames])] using h) + natAdd := fun h a b => + (H.natAdd (oldContains ``Nat.add + (by simp [VEnv.reflectedPrimitiveNames]) h) a b).mono hle + natSub := fun h a b => + (H.natSub (oldContains ``Nat.sub + (by simp [VEnv.reflectedPrimitiveNames]) h) a b).mono hle + natMul := fun h a b => + (H.natMul (oldContains ``Nat.mul + (by simp [VEnv.reflectedPrimitiveNames]) h) a b).mono hle + natPow := fun h a b => + (H.natPow (oldContains ``Nat.pow + (by simp [VEnv.reflectedPrimitiveNames]) h) a b).mono hle + natGcd := fun h a b => + (H.natGcd (oldContains ``Nat.gcd + (by simp [VEnv.reflectedPrimitiveNames]) h) a b).mono hle + natMod := fun h a b => + (H.natMod (oldContains ``Nat.mod + (by simp [VEnv.reflectedPrimitiveNames]) h) a b).mono hle + natDiv := fun h a b => + (H.natDiv (oldContains ``Nat.div + (by simp [VEnv.reflectedPrimitiveNames]) h) a b).mono hle + natBEq := fun h a b => + (H.natBEq (oldContains ``Nat.beq + (by simp [VEnv.reflectedPrimitiveNames]) h) a b).mono hle + natBLE := fun h a b => + (H.natBLE (oldContains ``Nat.ble + (by simp [VEnv.reflectedPrimitiveNames]) h) a b).mono hle + natLAnd := fun h a b => + (H.natLAnd (oldContains ``Nat.land + (by simp [VEnv.reflectedPrimitiveNames]) h) a b).mono hle + natLOr := fun h a b => + (H.natLOr (oldContains ``Nat.lor + (by simp [VEnv.reflectedPrimitiveNames]) h) a b).mono hle + natXor := fun h a b => + (H.natXor (oldContains ``Nat.xor + (by simp [VEnv.reflectedPrimitiveNames]) h) a b).mono hle + natShiftLeft := fun h a b => + (H.natShiftLeft (oldContains ``Nat.shiftLeft + (by simp [VEnv.reflectedPrimitiveNames]) h) a b).mono hle + natShiftRight := fun h a b => + (H.natShiftRight (oldContains ``Nat.shiftRight + (by simp [VEnv.reflectedPrimitiveNames]) h) a b).mono hle + charOfNat := fun h => H.charOfNat (by + simpa only [lookup ``Char.ofNat + (by simp [VEnv.reflectedPrimitiveNames])] using h) + stringOfList := fun h => by + obtain ⟨hconstant, hnil, hcons⟩ := H.stringOfList (by + simpa only [lookup ``String.ofList + (by simp [VEnv.reflectedPrimitiveNames])] using h) + exact ⟨hconstant, hnil.mono hle, hcons.mono hle⟩ } + +variable! {env env' : VEnv} (henv : env ≤ env') in +theorem VEnv.ContainsLits.mono : ∀ {l}, env.ContainsLits l → env'.ContainsLits l + | .natVal _, ⟨_, H⟩ => ⟨_, henv.constants H⟩ + | .strVal _, ⟨⟨_, H1⟩, ⟨_, H2⟩⟩ => + ⟨⟨_, henv.constants H1⟩, ⟨_, henv.constants H2⟩⟩ + +namespace VEnv.PreludeReady + +theorem boolFalse_lookup {env : VEnv} (H : env.PreludeReady) : + env.constants ``Bool.false = some { uvars := 0, type := VExpr.bool } := by + simpa [LiteralPrelude.boolFalse] using H.boolFalse + +theorem boolTrue_lookup {env : VEnv} (H : env.PreludeReady) : + env.constants ``Bool.true = some { uvars := 0, type := VExpr.bool } := by + simpa [LiteralPrelude.boolTrue] using H.boolTrue + +theorem natZero_lookup {env : VEnv} (H : env.PreludeReady) : + env.constants ``Nat.zero = some { uvars := 0, type := VExpr.nat } := by + simpa [LiteralPrelude.natZero] using H.natZero + +theorem natSucc_lookup {env : VEnv} (H : env.PreludeReady) : + env.constants ``Nat.succ = + some { uvars := 0, type := VExpr.forallE .nat .nat } := by + simpa [LiteralPrelude.natSucc] using H.natSucc + +theorem char_lookup {env : VEnv} (H : env.PreludeReady) : + env.constants ``Char = + some { uvars := 0, type := VExpr.sort (.succ .zero) } := by + simpa [LiteralPrelude.char] using H.char + +theorem charOfNat_lookup {env : VEnv} (H : env.PreludeReady) : + env.constants ``Char.ofNat = + some { uvars := 0, type := VExpr.forallE .nat .char } := by + simpa [LiteralPrelude.charOfNat] using H.charOfNat + +theorem stringOfList_lookup {env : VEnv} (H : env.PreludeReady) : + env.constants ``String.ofList = + some { uvars := 0, type := VExpr.forallE .listChar .string } := by + simpa [LiteralPrelude.stringOfList] using H.stringOfList + +theorem boolLit_hasType {env : VEnv} (H : env.PreludeReady) (b : Bool) : + env.HasType U Γ (.boolLit b) .bool := by + cases b + · exact .const H.boolFalse_lookup nofun rfl + · exact .const H.boolTrue_lookup nofun rfl + +theorem natLit_hasType {env : VEnv} (H : env.PreludeReady) (n : Nat) : + env.HasType U Γ (.natLit n) .nat := by + induction n with + | zero => exact .const H.natZero_lookup nofun rfl + | succ n ih => + simpa [VExpr.natLit, VExpr.natSucc, VExpr.nat, VExpr.instL, VExpr.inst] using + VEnv.HasType.app + (VEnv.HasType.const (ls := []) H.natSucc_lookup (by simp) rfl) ih + +theorem charOfNat_hasType {env : VEnv} (H : env.PreludeReady) : + env.HasType U Γ .charOfNat (.forallE .nat .char) := + .const H.charOfNat_lookup nofun rfl + +theorem listCharNil_hasType {env : VEnv} (H : env.PreludeReady) : + env.HasType U Γ .listCharNil .listChar := by + have hnil : env.constants ``List.nil = some { + uvars := 1 + type := VExpr.forallE (.sort (.succ (.param 0))) + (.app (.const ``List [.param 0]) (.bvar 0)) } := by + simpa [LiteralPrelude.listNil] using H.listNil + exact .app (.const hnil (by simp [VLevel.WF]) rfl) + (.const H.char_lookup nofun rfl) + +theorem listCharCons_hasType {env : VEnv} (H : env.PreludeReady) : + env.HasType U Γ .listCharCons + (.forallE .char <| .forallE .listChar .listChar) := by + have hcons : env.constants ``List.cons = some { + uvars := 1 + type := VExpr.forallE (.sort (.succ (.param 0))) <| + .forallE (.bvar 0) <| + .forallE (.app (.const ``List [.param 0]) (.bvar 1)) + (.app (.const ``List [.param 0]) (.bvar 2)) } := by + simpa [LiteralPrelude.listCons] using H.listCons + exact .app (.const hcons (by simp [VLevel.WF]) rfl) + (.const H.char_lookup nofun rfl) + +theorem listCharLit_hasType {env : VEnv} (H : env.PreludeReady) + (cs : List Char) : env.HasType U Γ (.listCharLit cs) .listChar := by + induction cs with + | nil => exact H.listCharNil_hasType + | cons c cs ih => + exact (H.listCharCons_hasType.app + (H.charOfNat_hasType.app (H.natLit_hasType c.toNat))).app ih + +theorem trLiteral_hasType {env : VEnv} (H : env.PreludeReady) (l : Literal) : + env.HasType U Γ (.trLiteral l) (.literalType l) := by + cases l with + | natVal n => simpa [VExpr.trLiteral, VExpr.literalType] using H.natLit_hasType n + | strVal s => + simpa [VExpr.trLiteral, VExpr.literalType, VExpr.stringOfList, VExpr.string, + VExpr.instL, VExpr.inst] using + VEnv.HasType.app + (VEnv.HasType.const (ls := []) H.stringOfList_lookup (by simp) rfl) + (H.listCharLit_hasType s.toList) + +/-- Exact readiness, not name occurrence alone, makes a direct literal +encoding well-formed. Pattern matching the containment witness ensures the +literal-facing premise is checked against the exact ready lookup. -/ +theorem trLiteral_wf {env : VEnv} (H : env.PreludeReady) (l : Literal) + (hcontains : env.ContainsLits l) : + VExpr.WF env U [] (.trLiteral l) := by + cases l with + | natVal n => + obtain ⟨ci, hci⟩ := hcontains + have : ci = LiteralPrelude.natType.toVConstant := by + exact (Option.some.inj (H.nat.symm.trans hci)).symm + subst ci + exact ⟨_, H.trLiteral_hasType (.natVal n)⟩ + | strVal s => + obtain ⟨⟨charOfNat, hcharOfNat⟩, ⟨stringOfList, hstringOfList⟩⟩ := hcontains + have : charOfNat = LiteralPrelude.charOfNat := by + exact (Option.some.inj (H.charOfNat.symm.trans hcharOfNat)).symm + subst charOfNat + have : stringOfList = LiteralPrelude.stringOfList := by + exact (Option.some.inj (H.stringOfList.symm.trans hstringOfList)).symm + subst stringOfList + exact ⟨_, H.trLiteral_hasType (.strVal s)⟩ + +end VEnv.PreludeReady + +@[simp] theorem VExpr.instL_boolFalse : VExpr.boolFalse.instL ls = VExpr.boolFalse := by + simp [boolFalse, instL] + +@[simp] theorem VExpr.instL_boolTrue : VExpr.boolTrue.instL ls = VExpr.boolTrue := by + simp [boolTrue, instL] + +@[simp] theorem VExpr.instL_boolLit : (VExpr.boolLit b).instL ls = VExpr.boolLit b := by + cases b <;> simp [boolLit] + +@[simp] theorem VExpr.liftN_boolLit : (VExpr.boolLit b).liftN n k = VExpr.boolLit b := by + cases b <;> rfl + +@[simp] theorem VExpr.lift'_boolLit : (VExpr.boolLit b).lift' ρ = VExpr.boolLit b := by + cases b <;> rfl + +@[simp] theorem VExpr.inst_boolLit : (VExpr.boolLit b).inst e k = VExpr.boolLit b := by + cases b <;> rfl + +@[simp] theorem VExpr.instL_natZero : VExpr.natZero.instL ls = .natZero := by + simp [natZero, instL] + +@[simp] theorem VExpr.instL_natSucc : VExpr.natSucc.instL ls = .natSucc := by + simp [natSucc, instL] + +@[simp] theorem VExpr.instL_natLit : (VExpr.natLit n).instL ls = VExpr.natLit n := by + induction n <;> simp [*, natLit, instL] + +@[simp] theorem VExpr.liftN_natLit : (VExpr.natLit a).liftN n k = VExpr.natLit a := by + induction a <;> simp [natLit, natZero, natSucc, VExpr.liftN, *] + +@[simp] theorem VExpr.lift'_natLit : (VExpr.natLit a).lift' ρ = VExpr.natLit a := by + induction a <;> simp [natLit, natZero, natSucc, VExpr.lift', *] + +@[simp] theorem VExpr.inst_natLit : (VExpr.natLit a).inst e k = VExpr.natLit a := by + induction a <;> simp [natLit, natZero, natSucc, VExpr.inst, *] + +@[simp] theorem VExpr.liftN_listCharLit : + (VExpr.listCharLit cs).liftN n k = VExpr.listCharLit cs := by + induction cs <;> + simp [listCharLit, listCharNil, listCharCons, char, charOfNat, VExpr.liftN, *] + +@[simp] theorem VExpr.lift'_listCharLit : + (VExpr.listCharLit cs).lift' ρ = VExpr.listCharLit cs := by + induction cs <;> + simp [listCharLit, listCharNil, listCharCons, char, charOfNat, VExpr.lift', *] + +@[simp] theorem VExpr.inst_listCharLit : + (VExpr.listCharLit cs).inst e k = VExpr.listCharLit cs := by + induction cs <;> + simp [listCharLit, listCharNil, listCharCons, char, charOfNat, VExpr.inst, *] + +@[simp] theorem VExpr.instL_listCharLit : + (VExpr.listCharLit cs).instL ls = VExpr.listCharLit cs := by + induction cs <;> + simp [listCharLit, listCharNil, listCharCons, char, charOfNat, + VExpr.instL, VLevel.inst, *] + +@[simp] theorem VExpr.liftN_trLiteral : + (VExpr.trLiteral l).liftN n k = VExpr.trLiteral l := by + cases l <;> simp [trLiteral, stringOfList, VExpr.liftN] + +@[simp] theorem VExpr.lift'_trLiteral : + (VExpr.trLiteral l).lift' ρ = VExpr.trLiteral l := by + cases l <;> simp [trLiteral, stringOfList, VExpr.lift'] + +@[simp] theorem VExpr.inst_trLiteral : + (VExpr.trLiteral l).inst e k = VExpr.trLiteral l := by + cases l <;> simp [trLiteral, stringOfList, VExpr.inst] + +@[simp] theorem VExpr.instL_trLiteral : + (VExpr.trLiteral l).instL ls = VExpr.trLiteral l := by + cases l <;> simp [trLiteral, stringOfList, VExpr.instL] + +theorem VEnv.HasPrimitives.nat_of_charOfNat (wf : Ordered env) + (henv : env.HasPrimitives) (H : env.contains ``Char.ofNat) : env.contains ``Nat := by + let ⟨_, H⟩ := H + have ⟨_, H⟩ := wf.constWF (henv.charOfNat H ▸ H) + let ⟨⟨_, H⟩, _⟩ := H.forallE_inv wf + let ⟨_, H, _⟩ := H.const_inv (Γ := []) wf (by trivial) + exact ⟨_, H⟩ + +#guard_named_axioms Ix.Theory.Named.VEnv.PreludeReady.mono [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.PreludeReady.addConst [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.PreludeReady.addDefEq [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.PreludeReady.trLiteral_wf [propext, Classical.choice, Quot.sound] + +end Ix.Theory.Named diff --git a/Ix/Theory/Named/LocalContext.lean b/Ix/Theory/Named/LocalContext.lean new file mode 100644 index 000000000..43edc5356 --- /dev/null +++ b/Ix/Theory/Named/LocalContext.lean @@ -0,0 +1,150 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Typing.UniqueTyping + +open Ix.Theory (VLevel) + +/-! # Theory local declarations + +The implementation-independent core of a local context. `VLocalDecl` only +mentions Theory expressions; the `Lean.FVarId` bookkeeping used by the +verified Lean-expression translator remains in `Ix.Theory.Named.Verify.VLCtx`. +-/ + +namespace Ix.Theory.Named +open VEnv + +inductive VLocalDecl where + | vlam (type : VExpr) + | vlet (type value : VExpr) + +def VLocalDecl.depth : VLocalDecl → Nat + | .vlam .. => 1 + | .vlet .. => 0 + +def VLocalDecl.value : VLocalDecl → VExpr + | .vlam .. => .bvar 0 + | .vlet _ e => e + +def VLocalDecl.type' : VLocalDecl → VExpr + | .vlam A + | .vlet A _ => A + +def VLocalDecl.type : VLocalDecl → VExpr + | .vlam A => A.lift + | .vlet A _ => A + +def VLocalDecl.lift' : VLocalDecl → Lift → VLocalDecl + | .vlam A, n => .vlam (A.lift' n) + | .vlet A e, n => .vlet (A.lift' n) (e.lift' n) + +def VLocalDecl.liftN : VLocalDecl → Nat → Nat → VLocalDecl + | .vlam A, n, k => .vlam (A.liftN n k) + | .vlet A e, n, k => .vlet (A.liftN n k) (e.liftN n k) + +def VLocalDecl.inst : VLocalDecl → VExpr → (k : Nat := 0) → VLocalDecl + | .vlam A, e₀, k => .vlam (A.inst e₀ k) + | .vlet A e, e₀, k => .vlet (A.inst e₀ k) (e.inst e₀ k) + +def VLocalDecl.instL : VLocalDecl → List VLevel → VLocalDecl + | .vlam A, ls => .vlam (A.instL ls) + | .vlet A e, ls => .vlet (A.instL ls) (e.instL ls) + +def VLocalDecl.WF (env : VEnv) (U : Nat) (Γ : List VExpr) : VLocalDecl → Prop + | .vlam type => env.IsType U Γ type + | .vlet type value => env.HasType U Γ value type + +def VLocalDecl.ClosedN : VLocalDecl → (k : Nat := 0) → Prop + | .vlam A, k => A.ClosedN k + | .vlet A e, k => A.ClosedN k ∧ e.ClosedN k + +variable! (env : VEnv) (U : Nat) (Γ : List VExpr) in +inductive VLocalDecl.IsDefEq : VLocalDecl → VLocalDecl → Prop + | vlam : env.IsDefEq U Γ type₁ type₂ (.sort u) → + VLocalDecl.IsDefEq (.vlam type₁) (.vlam type₂) + | vlet : + env.IsDefEq U Γ value₁ value₂ type₁ → env.IsDefEq U Γ type₁ type₂ (.sort u) → + VLocalDecl.IsDefEq (.vlet type₁ value₁) (.vlet type₂ value₂) + +theorem VLocalDecl.lift'_consN_skipN {d : VLocalDecl} : + d.lift' (.consN (.skipN .refl n) k) = d.liftN n k := by + cases d <;> simp [VLocalDecl.lift', VLocalDecl.liftN, VExpr.lift'_consN_skipN] + +nonrec theorem VLocalDecl.WF.weakN (henv : env.Ordered) (W : Ctx.LiftN n k Γ Γ') : + ∀ {d}, WF env U Γ d → WF env U Γ' (d.liftN n k) + | .vlam _, H | .vlet .., H => H.weakN henv W + +nonrec theorem VLocalDecl.WF.instN (henv : env.Ordered) (W : Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ) + (h₀ : env.HasType U Γ₀ e₀ A₀) : ∀ {d}, WF env U Γ₁ d → WF env U Γ (d.inst e₀ k) + | .vlam _, H | .vlet .., H => H.instN henv W h₀ + +nonrec theorem VLocalDecl.WF.instL {env : VEnv} (hls : ∀ l ∈ ls, l.WF U') : + ∀ {d}, WF env ls.length Γ d → WF env U' (Γ.map (·.instL ls)) (d.instL ls) + | .vlam _, H | .vlet .., H => H.instL hls + +@[simp] theorem VLocalDecl.lift'_depth {d : VLocalDecl} : (d.lift' n).depth = d.depth := by + cases d <;> rfl + +theorem VLocalDecl.lift'_comp {d : VLocalDecl} : + d.lift' (.comp l₁ l₂) = (d.lift' l₁).lift' l₂ := by + cases d <;> simp [VLocalDecl.lift', VExpr.lift'_comp] + +variable! (henv : VEnv.WF env) (hΓ' : OnCtx Γ' (env.IsType U)) + (W : Ctx.Lift' n Γ Γ') in +theorem VLocalDecl.weak'_iff : + VLocalDecl.WF env U Γ' (d.lift' n) ↔ VLocalDecl.WF env U Γ d := + match d with + | .vlam .. => IsType.weak'_iff henv hΓ' W + | .vlet .. => HasType.weak'_iff henv hΓ' W + +variable! (henv : VEnv.WF env) (hΓ' : OnCtx Γ' (env.IsType U)) + (W : Ctx.LiftN n k Γ Γ') in +theorem VLocalDecl.weakN_iff : + VLocalDecl.WF env U Γ' (d.liftN n k) ↔ VLocalDecl.WF env U Γ d := + match d with + | .vlam .. => IsType.weakN_iff henv hΓ' W + | .vlet .. => HasType.weakN_iff henv hΓ' W + +variable! (henv : Ordered env) (hΓ : OnCtx Γ (IsType env U)) in +theorem VLocalDecl.IsDefEq.refl : + ∀ {d}, VLocalDecl.WF env U Γ d → VLocalDecl.IsDefEq env U Γ d d + | .vlam _, ⟨_, h1⟩ => .vlam h1 + | .vlet .., h1 => let ⟨_, h2⟩ := h1.isType henv hΓ; .vlet h1 h2 + +theorem VLocalDecl.IsDefEq.wf : + VLocalDecl.IsDefEq env U Γ d₁ d₂ → VLocalDecl.WF env U Γ d₁ + | .vlam h3 => ⟨_, h3.hasType.1⟩ + | .vlet h3 _ => h3.hasType.1 + +theorem VLocalDecl.IsDefEq.mono (henv : env ≤ env') : + VLocalDecl.IsDefEq env U Γ d₁ d₂ → VLocalDecl.IsDefEq env' U Γ d₁ d₂ + | .vlam h => .vlam (h.mono henv) + | .vlet h₁ h₂ => .vlet (h₁.mono henv) (h₂.mono henv) + +theorem VLocalDecl.IsDefEq.symm : + VLocalDecl.IsDefEq env U Γ d₁ d₂ → VLocalDecl.IsDefEq env U Γ d₂ d₁ + | .vlam h1 => .vlam h1.symm + | .vlet h1 h2 => .vlet (h2.defeqDF h1.symm) h2.symm + +theorem VLocalDecl.IsDefEq.defeqDFC (henv : Ordered env) + (hΓ : IsDefEqCtx env U Γ₀ Γ₁ Γ₂) : + VLocalDecl.IsDefEq env U Γ₁ d₁ d₂ → VLocalDecl.IsDefEq env U Γ₂ d₁ d₂ + | .vlam h1 => .vlam (h1.defeqDFC henv hΓ) + | .vlet h1 h2 => .vlet (h1.defeqDFC henv hΓ) (h2.defeqDFC henv hΓ) + +#guard_named_axioms Ix.Theory.Named.VLocalDecl.WF.weakN [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VLocalDecl.weakN_iff [ + propext, + sorryAx, + Classical.choice, + Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VLocalDecl.IsDefEq.defeqDFC [propext, Quot.sound] + +end Ix.Theory.Named diff --git a/Ix/Theory/Named/Meta.lean b/Ix/Theory/Named/Meta.lean new file mode 100644 index 000000000..eefcfc9b9 --- /dev/null +++ b/Ix/Theory/Named/Meta.lean @@ -0,0 +1,120 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Batteries.Lean.Expr +import Ix.Theory.Named.Std.Control +import Ix.Theory.Named.Std.ToExpr +import Ix.Theory.Named.VEnv +import Ix.Theory.Named.Reference.Inductive.Reduce + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named + +namespace Meta +open Lean Meta Elab Term + +def expandProj (structName : Name) (idx : Nat) (e : Expr) : MetaM Expr := do + let failed {α} : Unit → MetaM α := fun _ => do + throwError "invalid projection{indentExpr (mkProj structName idx e)}" + let ival ← getConstInfoInduct structName + let [ctor] := ival.ctors | failed () + let ctorInfo ← getConstInfoCtor ctor + unless idx < ctorInfo.numFields do failed () + let args : Array Term ← (Array.range ctorInfo.numFields).mapM fun _ => do + `($(mkIdent (← mkFreshId)):ident) + let args' ← args.mapM fun x => `(Lean.Parser.Term.funBinder| $x) + let casesOn := mkCIdent (mkCasesOnName structName) + TermElabM.run' do + let stx ← `($casesOn:ident $(← e.toSyntax) fun $args'* => $(args[idx]!)) + elabTerm stx (← inferType (.proj structName idx e)) + +partial def expandExpr (e : Expr) : MetaM Expr := + Meta.transform e + (pre := fun + | .mdata _ e => return .visit e + | .proj s i e => return .continue (← expandProj s i e) + | e@(.mvar _) => return .continue (← instantiateMVars e) + | _ => return .continue) + +variable (ls : List Name) (fvarToIdx : FVarIdMap Nat) in +partial def ofExpr : Expr → (k :_:= 0) → MetaM VExpr + | .bvar i, _ => return .bvar i + | .sort u, _ => return .sort (← VLevel.ofLevel ls u) + | .const c us, _ => return .const c (← liftM <| us.mapM (VLevel.ofLevel ls)) + | .app fn arg, k => return .app (← ofExpr fn k) (← ofExpr arg k) + | .lam _ ty body _, k => return .lam (← ofExpr ty k) (← ofExpr body (k+1)) + | .forallE _ ty body _, k => return .forallE (← ofExpr ty k) (← ofExpr body (k+1)) + | .mdata _ e, k => ofExpr e k + | .lit l, k => ofExpr l.toConstructor k + | .letE _ _ value body _, k => return (← ofExpr body (k+1)).inst (← ofExpr value k) + | e@(.proj ..), _ => throwError "invalid expression {e}" + | e@(.mvar ..), _ => throwError "expression contains metavariables {e}" + | .fvar e, k => do + if let some i := fvarToIdx.get? e then return .bvar (i+k) + let lctx ← getLCtx + let some e := (do (← lctx.find? e).value?) | throwError "undeclared free var {Expr.fvar e}" + ofExpr e + +deriving instance ToExpr for VLevel +deriving instance ToExpr for VExpr +deriving instance ToExpr for VConstant +deriving instance ToExpr for VDefEq + +def toVExprWrapper (bis : Option (TSyntaxArray ``Parser.Term.funBinder)) + (x : FVarIdMap Nat → TermElabM α) : TermElabM α := do + withLevelNames [] <| + elabFunBinders (bis.getD #[]) none fun xs _ => + withAutoBoundImplicit <| + x <| + xs.foldr (fun fvar (n, m) => (n+1, m.insert fvar.fvarId! n)) (0, ({}:FVarIdMap Nat)) |>.2 + +def elabForVExpr (e : Expr) : TermElabM Expr := do + let e ← levelMVarToParam e + if ← logUnassignedUsingErrorInfos (← getMVars e) then throwAbortCommand + expandExpr e + +def toVExprCore (bis : Option (TSyntaxArray ``Parser.Term.funBinder)) + (e : Term) : TermElabM (Nat × VExpr) := do + toVExprWrapper bis fun map => do + let e ← elabForVExpr (← elabTerm e none) + let ls := (← getLevelNames).reverse + return (ls.length, ← ofExpr ls map e) + +syntax "vexpr(" atomic(Parser.Term.funBinder* " ⊢ ")? term ")" : term + +elab_rules : term + | `(vexpr($[$bis* ⊢]? $e:term)) => return toExpr (← toVExprCore bis e).2 + +syntax "vconst(" atomic(Parser.Term.funBinder* " ⊢ ")? term ")" : term + +elab_rules : term + | `(vconst($[$bis* ⊢]? $e:term)) => do + let (n, ve) ← toVExprCore bis e + return toExpr (⟨n, ve⟩ : VConstant) + +syntax "vdefeq(" atomic(Parser.Term.funBinder* " ⊢ ")? + atomic(Parser.Term.funBinder* " => ")? term " ≡ " term ")" : term + +elab_rules : term + | `(vdefeq($[$bis* ⊢]? $[$args* =>]? $e₁:term ≡ $e₂:term)) => do + toVExprWrapper bis fun map => do + let (e₁, e₂) ← match args with + | some args => pure (← `(fun $args* => $e₁), ← `(fun $args* => $e₂)) + | none => pure (e₁, e₂) + let e₁ ← elabTerm e₁ none + let ty ← inferType e₁ + let e₂ ← elabTerm e₂ ty + let e₁ ← elabForVExpr e₁ + let e₂ ← elabForVExpr e₂ + let ty ← elabForVExpr ty + let ls := (← getLevelNames).reverse + return toExpr { + uvars := ls.length + lhs := ← ofExpr ls map e₁ + rhs := ← ofExpr ls map e₂ + type := ← ofExpr ls map ty + : VDefEq } diff --git a/Ix/Theory/Named/MutualInductiveFixtures.lean b/Ix/Theory/Named/MutualInductiveFixtures.lean new file mode 100644 index 000000000..23cfbdb48 --- /dev/null +++ b/Ix/Theory/Named/MutualInductiveFixtures.lean @@ -0,0 +1,199 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Inductive +import Ix.Theory.Named.Meta + +/-! +# Mutual public-generation fixtures + +These real kernel declarations pin shared parameters, per-family +indices/results, constructor order, and cross-family recursive target +ordinals at the `VInductDecl.CheckedBlock` boundary, then exercise the +block-wide public generation transaction added by Spec-08C. The Verify mutual +fixture supplies semantic preservation, complete kernel metadata comparison, +and environment replay. +-/ + +namespace Ix.Theory.Named.MutualInductiveFixtures + +open VInductDecl + +universe u + +/-! ## Unindexed Tree/TreeList -/ + +mutual + +inductive Tree (α : Type u) : Type u where + | leaf : α → Tree α + | node : TreeList α → Tree α + | branch : (α → TreeList α) → Tree α + +inductive TreeList (α : Type u) : Type u where + | nil : TreeList α + | cons : Tree α → TreeList α → TreeList α + +end + +def treeType : VInductiveType where + name := ``Tree + uvars := 1 + type := vconst(type_of% @Tree).type + ctors := [⟨vconst(type_of% @Tree.leaf), ``Tree.leaf⟩, + ⟨vconst(type_of% @Tree.node), ``Tree.node⟩, + ⟨vconst(type_of% @Tree.branch), ``Tree.branch⟩] + +def treeListType : VInductiveType where + name := ``TreeList + uvars := 1 + type := vconst(type_of% @TreeList).type + ctors := [⟨vconst(type_of% @TreeList.nil), ``TreeList.nil⟩, + ⟨vconst(type_of% @TreeList.cons), ``TreeList.cons⟩] + +def treeDecl : VInductDecl := + ⟨1, 1, [treeType, treeListType]⟩ + +def treeChecked : treeDecl.CheckedBlock := + treeDecl.checkedBlock?.get (by decide) + +/-- Block-wide generation data for the real unindexed mutual declaration. -/ +def treeGeneration : BlockGenerationChecked treeDecl := + treeDecl.identityBlockGeneration?.get (by decide) + +example : treeChecked.params = [.sort (.succ (.param 0))] := rfl +example : treeChecked.families.values = [treeType, treeListType] := rfl +example : treeChecked.families.ordinals = [0, 1] := rfl +example : treeChecked.families.names = [``Tree, ``TreeList] := rfl +example : treeChecked.families.indices = [[], []] := rfl +example : treeChecked.families.resultLevels = + [.succ (.param 0), .succ (.param 0)] := rfl +example : treeChecked.families.constructorNames = + [[``Tree.leaf, ``Tree.node, ``Tree.branch], + [``TreeList.nil, ``TreeList.cons]] := rfl +example : treeChecked.families.recursiveTargets = + [[[], [1], [1]], [[], [0, 1]]] := rfl +example : treeChecked.names = + [``Tree, ``TreeList, ``Tree.leaf, ``Tree.node, ``Tree.branch, + ``TreeList.nil, ``TreeList.cons, ``Tree.rec, ``TreeList.rec] := rfl + +example : treeGeneration.families.map (·.raw.name) = + [``Tree, ``TreeList] := rfl +example : treeGeneration.motiveTypes.length = 2 := rfl +example : treeGeneration.minorTypes.length = 5 := rfl +example : treeGeneration.recursors.map (·.name) = + [``Tree.rec, ``TreeList.rec] := rfl +example : treeGeneration.generatedRules.length = 5 := rfl + +example : treeChecked.families.constructors[0][1].recursive[0].fieldIndex = 0 := rfl +example : treeChecked.families.constructors[0][1].recursive[0].targetType = 1 := rfl +example : treeChecked.families.constructors[0][2].recursive[0].fieldIndex = 0 := rfl +example : treeChecked.families.constructors[0][2].recursive[0].binders.length = 1 := rfl +example : treeChecked.families.constructors[0][2].recursive[0].targetType = 1 := rfl +example : treeChecked.families.constructors[1][1].recursive[0].targetType = 0 := rfl +example : treeChecked.families.constructors[1][1].recursive[1].targetType = 1 := rfl + +/-! ## Indexed mutual block -/ + +mutual + +inductive IndexedTree (α : Type u) : Nat → Type u where + | leaf : α → IndexedTree α .zero + | node {n : Nat} : IndexedTreeList α n → IndexedTree α (.succ n) + +inductive IndexedTreeList (α : Type u) : Nat → Type u where + | nil : IndexedTreeList α .zero + | cons {n : Nat} : + IndexedTree α n → IndexedTreeList α n → IndexedTreeList α (.succ n) + +end + +def indexedTreeType : VInductiveType where + name := ``IndexedTree + uvars := 1 + type := vconst(type_of% @IndexedTree).type + ctors := [⟨vconst(type_of% @IndexedTree.leaf), ``IndexedTree.leaf⟩, + ⟨vconst(type_of% @IndexedTree.node), ``IndexedTree.node⟩] + +def indexedTreeListType : VInductiveType where + name := ``IndexedTreeList + uvars := 1 + type := vconst(type_of% @IndexedTreeList).type + ctors := [⟨vconst(type_of% @IndexedTreeList.nil), ``IndexedTreeList.nil⟩, + ⟨vconst(type_of% @IndexedTreeList.cons), ``IndexedTreeList.cons⟩] + +def indexedTreeDecl : VInductDecl := + ⟨1, 1, [indexedTreeType, indexedTreeListType]⟩ + +def indexedTreeChecked : indexedTreeDecl.CheckedBlock := + indexedTreeDecl.checkedBlock?.get (by decide) + +/-- Block-wide generation data for the real indexed mutual declaration. -/ +def indexedTreeGeneration : BlockGenerationChecked indexedTreeDecl := + indexedTreeDecl.identityBlockGeneration?.get (by decide) + +example : indexedTreeChecked.params = [.sort (.succ (.param 0))] := rfl +example : indexedTreeChecked.families.values = + [indexedTreeType, indexedTreeListType] := rfl +example : indexedTreeChecked.families.ordinals = [0, 1] := rfl +example : indexedTreeChecked.families.names = + [``IndexedTree, ``IndexedTreeList] := rfl +example : indexedTreeChecked.families.indices = + [[.const ``Nat []], [.const ``Nat []]] := rfl +example : indexedTreeChecked.families.resultLevels = + [.succ (.param 0), .succ (.param 0)] := rfl +example : indexedTreeChecked.families.constructorNames = + [[``IndexedTree.leaf, ``IndexedTree.node], + [``IndexedTreeList.nil, ``IndexedTreeList.cons]] := rfl +example : indexedTreeChecked.families.recursiveTargets = + [[[], [1]], [[], [0, 1]]] := rfl + +example : indexedTreeGeneration.families.map (·.raw.name) = + [``IndexedTree, ``IndexedTreeList] := rfl +example : indexedTreeGeneration.motiveTypes.length = 2 := rfl +example : indexedTreeGeneration.minorTypes.length = 4 := rfl +example : indexedTreeGeneration.recursors.map (·.name) = + [``IndexedTree.rec, ``IndexedTreeList.rec] := rfl +example : indexedTreeGeneration.generatedRules.length = 4 := rfl + +example : indexedTreeChecked.families.constructors[0][0].resultIndices = + [.const ``Nat.zero []] := rfl +example : indexedTreeChecked.families.constructors[0][1].resultIndices = + [.app (.const ``Nat.succ []) (.bvar 1)] := rfl +example : indexedTreeChecked.families.constructors[1][0].resultIndices = + [.const ``Nat.zero []] := rfl +example : indexedTreeChecked.families.constructors[1][1].resultIndices = + [.app (.const ``Nat.succ []) (.bvar 2)] := rfl +example : indexedTreeChecked.families.constructors[0][1].recursive[0].fieldIndex = 1 := rfl +example : indexedTreeChecked.families.constructors[0][1].recursive[0].targetType = 1 := rfl +example : indexedTreeChecked.families.constructors[0][1].recursive[0].indices = + [.bvar 0] := rfl +example : indexedTreeChecked.families.constructors[1][1].recursive[0].fieldIndex = 1 := rfl +example : indexedTreeChecked.families.constructors[1][1].recursive[0].targetType = 0 := rfl +example : indexedTreeChecked.families.constructors[1][1].recursive[1].fieldIndex = 2 := rfl +example : indexedTreeChecked.families.constructors[1][1].recursive[1].targetType = 1 := rfl + +/-! ## Public block-wide boundary -/ + +example : treeDecl.stage3 = true := rfl +example : indexedTreeDecl.stage3 = true := rfl +example : VEnv.empty.addInduct treeDecl = + VEnv.empty.addInductBlockGeneration treeGeneration := rfl +example : VEnv.empty.addInduct indexedTreeDecl = + VEnv.empty.addInductBlockGeneration indexedTreeGeneration := rfl +example : (VEnv.empty.addInduct treeDecl).isSome = true := rfl +example : (VEnv.empty.addInduct indexedTreeDecl).isSome = true := rfl + +#guard_named_axioms Ix.Theory.Named.VInductDecl.checkedBlock? [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.MutualInductiveFixtures.treeChecked [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.MutualInductiveFixtures.indexedTreeChecked [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CheckedFamilies.values_eq [propext, Quot.sound] + +end Ix.Theory.Named.MutualInductiveFixtures diff --git a/Ix/Theory/Named/NOTICE b/Ix/Theory/Named/NOTICE new file mode 100644 index 000000000..d1c829112 --- /dev/null +++ b/Ix/Theory/Named/NOTICE @@ -0,0 +1,116 @@ +Named kernel specification and implementation proof support +Derived from Lean4Lean (digama0/lean4lean) and the Argument Computer +development line (argumentcomputer/lean4ix), revision +a4188d7c2979378d85c6bb41fdd96c3a48a71371. +Original contributors retain their copyrights. Apache License 2.0; +see LICENSE in this directory. Modified for Ix namespace/import paths +and shared universe semantics. Only the transitive source dependency +closure of the existing kernel/compiler verification was retained. +The standalone checker application, build infrastructure, benchmarks, +and remaining test suite were not imported. + +Original module Local source Original SHA-256 +Lean4Lean.Declaration Ix/Theory/Named/Reference/Declaration.lean cefbf66e2134bec472aa9fd643122a3052bbbfd12ab04e6dbd85fe21e32fb0fa +Lean4Lean.Environment Ix/Theory/Named/Reference/Environment.lean 8de5b7407e225c96ed4116bc46e2e3bad5009b6bfb0cb4f68d4ce2799eff4f11 +Lean4Lean.Environment.Basic Ix/Theory/Named/Reference/Environment/Basic.lean cc39f3a7ae819fed332407c01239dc604a49c8a5dffb379564aade970ff1bcf9 +Lean4Lean.Expr Ix/Theory/Named/Reference/Expr.lean 6cb6f0e5df484973fc282bb86968bb51a1c73ec32d726010b1d7b2133229ac81 +Lean4Lean.ForEachExprV Ix/Theory/Named/Reference/ForEachExprV.lean 260167ef5e882a5bd2482734dd72749b809857dbe99bc2aa32e28c6b4b2fb37e +Lean4Lean.FuelConfig Ix/Theory/Named/Reference/FuelConfig.lean 3ab65df013711059ef867c2683a7516b77cea87a526cf47c1772e9c8a529d35a +Lean4Lean.Inductive.Add Ix/Theory/Named/Reference/Inductive/Add.lean 17dda89d60cac89cba4fc9a4db8233eee8199ce7aee4de337bcd677d076eceb6 +Lean4Lean.Inductive.EliminationTrace Ix/Theory/Named/Reference/Inductive/EliminationTrace.lean d14f7bc2aeb2037701ac12d0217f405b94812fdc441942458656bb543627d095 +Lean4Lean.Inductive.Reduce Ix/Theory/Named/Reference/Inductive/Reduce.lean c6daf4dcec4ffc6c27a64e1054500ec025646d6d15c7c2d61c1323e4928dcc17 +Lean4Lean.Inductive.ValidationTrace Ix/Theory/Named/Reference/Inductive/ValidationTrace.lean 7b4e36920a1286b319237a4db632acfac09e203cb3699835fa7440e31f52bbbd +Lean4Lean.Instantiate Ix/Theory/Named/Reference/Instantiate.lean ae08b31657faf3b54de22688ae287d0bff362c573d6a2dd09f12f417b1975ec2 +Lean4Lean.Level Ix/Theory/Named/Reference/Level.lean 75932c6c1d406cbbf75e60c56b75e4a9b33f743651f5aa50814c7c369edbed76 +Lean4Lean.List Ix/Theory/Named/Reference/List.lean 4ddde2232aff649a50a8d1bec44e9495e2387dd3fcc29833d4f9c67c514431a4 +Lean4Lean.LocalContext Ix/Theory/Named/Reference/LocalContext.lean 5baf7b6b379cbbd3902578c5da9d454558031b52f2af508481bc4647c0d91a1b +Lean4Lean.Primitive Ix/Theory/Named/Reference/Primitive.lean a8407d6fdb55c15225c5535b89a1c647af7b20729adb810b9887372b1a9ea745 +Lean4Lean.PtrEq Ix/Theory/Named/Reference/PtrEq.lean 395e6b6907e548582d9e40e37f5344d6919e597500608c597048e621f27f0aa2 +Lean4Lean.Quot Ix/Theory/Named/Reference/Quot.lean 79c51dae8b2ac0e09537aaabfb32075fe4a9d5dd34a1a78d47a88229a74926c5 +Lean4Lean.Std.Basic Ix/Theory/Named/Std/Basic.lean 885fd60674f7ceec447d20b56f34a2945dccc9d1193041d552162b7220ba6d67 +Lean4Lean.Std.Control Ix/Theory/Named/Std/Control.lean 71dd61bc6bb315f1590ae79e276ada1c0c5c8eb31e67b0f994ed29f92b05fcc2 +Lean4Lean.Std.HashMap Ix/Theory/Named/Std/HashMap.lean 67afa618c36314613b553eb95dafbbf7c5cca01b9e7d8749b4c024de966c37db +Lean4Lean.Std.NodupKeys Ix/Theory/Named/Std/NodupKeys.lean b0a5f079a7f034f0c436de4ca120fd07197775cba26c625d2a2e1c8054ef460e +Lean4Lean.Std.Ord Ix/Theory/Named/Std/Ord.lean 26380558d7e4fd742274d4f4d9396eb2d10feaa0ad945cfd57c0a37b12a04fa4 +Lean4Lean.Std.PersistentHashMap Ix/Theory/Named/Std/PersistentHashMap.lean c619b8962018a8b4fe3afb9fb0ee7c77bb92e75c323fc3f849ed4d8c199a4c59 +Lean4Lean.Std.SMap Ix/Theory/Named/Std/SMap.lean 574b0996076ccb5814ba381f13f288259fbb821abd33c20f9b891bac6c1e1fb0 +Lean4Lean.Std.ToExpr Ix/Theory/Named/Std/ToExpr.lean 5068adbd3d6c0b2d33ec84420efd66e8025dad2b6ed7171b7d10d1453db08e82 +Lean4Lean.Std.VariableBang Ix/Theory/Named/Std/VariableBang.lean 783456566db97120957903fc5beccf549507ed8e1f0fcd76cde4d0175f02a012 +Lean4Lean.Tests.ProjectionExpressibility Ix/Theory/Named/Fixtures/ProjectionExpressibility.lean ee30b121ece1f5ea7ca2bb315b8e02557b75b5ef33a9986edb12b7b7dab76de0 +Lean4Lean.Theory.ConstructorValidityFixtures Ix/Theory/Named/ConstructorValidityFixtures.lean 4743314f7fd0e0323463cc7fd403a68792c5e0dabbdbcaaba9e1289136527cde +Lean4Lean.Theory.Inductive Ix/Theory/Named/Inductive.lean 5cf6f5c19b301396b35d141a90b6b0281ae984e0e64c502d657d6dc15fb5dc57 +Lean4Lean.Theory.InductiveFixtures Ix/Theory/Named/InductiveFixtures.lean 4e663484b880adc47045c4d3760456e3c9192951ff9b0f188eea34eff5adcc55 +Lean4Lean.Theory.Literals Ix/Theory/Named/Literals.lean cfb64f593c6e3643d83d479d29129d0e73522327529143ef41fd62e162d6998a +Lean4Lean.Theory.LocalContext Ix/Theory/Named/LocalContext.lean e31fe96e8ec2e2718c65bd2eb8a4b9c01286d54956d1a4a23ef47b83706c7d1f +Lean4Lean.Theory.Meta Ix/Theory/Named/Meta.lean 37f47b54a8b855ee7f0c4f2a322fb3c7ad319d1749861c386d9b966142f2c8b7 +Lean4Lean.Theory.MutualInductiveFixtures Ix/Theory/Named/MutualInductiveFixtures.lean 907e4cc7a7855eec5b32bf22457baf0eb482b15529e56d8eb2f2369eaaf79414 +Lean4Lean.Theory.NestedInductive Ix/Theory/Named/NestedInductive.lean c88720c864cd7a4c62d44b116779c7ddb93aacd2b9b51c2fea87e4c1afeddfaf +Lean4Lean.Theory.NestedInductiveFixtures Ix/Theory/Named/NestedInductiveFixtures.lean 2b31e2f7691a631007a5a3e4c83227515476c13d812c391e7ab111ceba4b8d99 +Lean4Lean.Theory.Projection Ix/Theory/Named/Projection.lean 9f5108d2192b951a2b8ee05f102fe03bea5c40563f4f10fa27b74c34853884e6 +Lean4Lean.Theory.Quot Ix/Theory/Named/Quot.lean 3742bc54150fb1b410d5807cd64355cc169fd6ea809dbb71662dc9d28a64c617 +Lean4Lean.Theory.SingletonParity Ix/Theory/Named/SingletonParity.lean 018c3ea29f04d2274bd47362ed09d959ab3873abf59f0ad23c79afb03f37eb52 +Lean4Lean.Theory.Typing.Basic Ix/Theory/Named/Typing/Basic.lean 24827047806ee6ffb9af8ed457143cbb6b0c57326557e0f433339840272114a5 +Lean4Lean.Theory.Typing.Env Ix/Theory/Named/Typing/Env.lean 72949fadf8565117631d4ce822a2d44ebd671843e52a70a8617f0c9c85411cfa +Lean4Lean.Theory.Typing.EnvLemmas Ix/Theory/Named/Typing/EnvLemmas.lean bd9f777d16b60d6bfa74e5e4e2dbf2423df1c1478c38970144a849078623e80b +Lean4Lean.Theory.Typing.InductiveCertificate Ix/Theory/Named/Typing/InductiveCertificate.lean ef4b4970be4680efda15c6ed921584f30d174a462c2643c6bb60c43d2248d353 +Lean4Lean.Theory.Typing.InductiveLemmas Ix/Theory/Named/Typing/InductiveLemmas.lean 90bb4f3964705de120018360d7bcf14ab81f05c621fd7c0c0c49539af301fa77 +Lean4Lean.Theory.Typing.InductivePattern Ix/Theory/Named/Typing/InductivePattern.lean 45480de8808c6311c0635ab0afc7fcdbe0c3534558cb1902f7d97d80886c423a +Lean4Lean.Theory.Typing.InductivePatternWF Ix/Theory/Named/Typing/InductivePatternWF.lean 884955ce4111aed9540b7d02ebc2469aa26168a8efa476b2ee1cdd01ee402926 +Lean4Lean.Theory.Typing.Injectivity Ix/Theory/Named/Typing/Injectivity.lean e7233bde95305b46aa1d22d3e13e21aea93f7e3f903eeb46865c226ffe0bba88 +Lean4Lean.Theory.Typing.Lemmas Ix/Theory/Named/Typing/Lemmas.lean f183e28ec58a52dadba983cb53a55537cb9a497d9e8e4f78d2c2ca4cc9427a38 +Lean4Lean.Theory.Typing.Meta Ix/Theory/Named/Typing/Meta.lean 16b0fb88d6719cdd1293108ca2520c1ef2c601819ef5562d0aa7c2d007021d9b +Lean4Lean.Theory.Typing.NestedInductiveLemmas Ix/Theory/Named/Typing/NestedInductiveLemmas.lean f1dcef8e876386abfbebc5a36ca751dafaa75257b7c6c9f506c7447c4be9cd49 +Lean4Lean.Theory.Typing.Pattern Ix/Theory/Named/Typing/Pattern.lean db05a0847000f4d3e2a69f4b053112bfca31dbc2b11082a804052bb44562f4e3 +Lean4Lean.Theory.Typing.QuotLemmas Ix/Theory/Named/Typing/QuotLemmas.lean 63abdab93b43679259fc25df480afa1b2086edc8cf52cca5b08b2f36be847309 +Lean4Lean.Theory.Typing.Strong Ix/Theory/Named/Typing/Strong.lean 266fbcb98287ad2f53c0c7ed11960aaa620471b71d4289def448ec259019d46a +Lean4Lean.Theory.Typing.UniqueTyping Ix/Theory/Named/Typing/UniqueTyping.lean c297cd16f1ec43d6ea4fba930ddd490555cf212d59389f39765beb986dde5a3f +Lean4Lean.Theory.VDecl Ix/Theory/Named/VDecl.lean 94f3032a2aa1e9e71056b1373fb456b242ede7405c88abe593e7f8432f760a66 +Lean4Lean.Theory.VEnv Ix/Theory/Named/VEnv.lean 16c9bb411f7482d5c08585a278b8ad8209eb8deb023bcb77f7bad675825a83a9 +Lean4Lean.Theory.VExpr Ix/Theory/Named/VExpr.lean 469c797a6eaee8883060470e0830dbe56b4b64f7ca91d9ae63304e288849ac8c +Lean4Lean.Theory.VLevel Ix/Theory/Named/VLevel.lean a570dff7d2065ca64acd1bb920628ec332b4e39f18a2dc423cbf3a7bd469ba58 +Lean4Lean.TypeChecker Ix/Theory/Named/Reference/TypeChecker.lean 40f99258932cfa5f2c99a2c2b5f7b59b467c10348cd238b70453f95e61dd7383 +Lean4Lean.Verify.Axioms Ix/Theory/Named/Verify/Axioms.lean 4c890728353a1df1b754b09bec3635b75bbfade362aff83a35005b2f316f5863 +Lean4Lean.Verify.Environment.Basic Ix/Theory/Named/Verify/Environment/Basic.lean 161d0abd5c8f4a8a8a09db2ab3bdcd988171aba2c7dcb4a997bd6a8cb32a89ae +Lean4Lean.Verify.Environment.ConstructorValidation Ix/Theory/Named/Verify/Environment/ConstructorValidation.lean 2ce3772020b93feed5e88a0a27d3dcdcb32801b0aca24c7c1e5fb694d9cf382e +Lean4Lean.Verify.Environment.ConstructorValidityMatrix Ix/Theory/Named/Verify/Environment/ConstructorValidityMatrix.lean 4245035c98958b93c3daefa2efd78f1f0c9779d5bd2a5633db0c2f60958b88b7 +Lean4Lean.Verify.Environment.Elimination Ix/Theory/Named/Verify/Environment/Elimination.lean 89365c4a39346bcd7b1f94b7f568a272e4e02b64ff1b897df38f598079890011 +Lean4Lean.Verify.Environment.EliminationFixturesCommon Ix/Theory/Named/Verify/Environment/EliminationFixturesCommon.lean 1a2dbd72b2538f673c5080abd023c639bee0c200f9d2fd832158f312f726e8b9 +Lean4Lean.Verify.Environment.EliminationFixturesEdges Ix/Theory/Named/Verify/Environment/EliminationFixturesEdges.lean 86aaa7338723cdc0005d3646b68c77099ad8e1c113ac283b9d6048783bda4910 +Lean4Lean.Verify.Environment.EliminationFixturesEq Ix/Theory/Named/Verify/Environment/EliminationFixturesEq.lean 371f2bd76c849048b8c27b741af65214e6345374e09c1658e00eceef0bd5e9d4 +Lean4Lean.Verify.Environment.EliminationFixturesEqNat Ix/Theory/Named/Verify/Environment/EliminationFixturesEqNat.lean 08d2a0352b9a7652ee0c3296b8e6de36e3f3d16b44c77fa962855b355412479a +Lean4Lean.Verify.Environment.EliminationFixturesNat Ix/Theory/Named/Verify/Environment/EliminationFixturesNat.lean 5fc23bf4dacc65a476fa6416fbf548c75b563574aad7d1c670d28df5a27ce541 +Lean4Lean.Verify.Environment.EliminationFixturesOrAnd Ix/Theory/Named/Verify/Environment/EliminationFixturesOrAnd.lean 993466ee37f03b2f7494116d58dca184614f32308b851d0bceb275ddd4c3a79f +Lean4Lean.Verify.Environment.EliminationFixturesSmall Ix/Theory/Named/Verify/Environment/EliminationFixturesSmall.lean 65d5114cba03dbc1e25b5ec644f35d72ac27dc370a376a37536ffb8c9d4e1409 +Lean4Lean.Verify.Environment.IndexedVecCandidate Ix/Theory/Named/Verify/Environment/IndexedVecCandidate.lean ba5fc7e4d21f85c1fbd091bdcdecc51c7c252a00329c6e4ce41cf703cfbfb280 +Lean4Lean.Verify.Environment.IndexedVecConsReplay Ix/Theory/Named/Verify/Environment/IndexedVecConsReplay.lean 5fc8c9d63d58a208f0318894f9ba77d87cff021f2dc413575da4e8e5ae42d738 +Lean4Lean.Verify.Environment.IndexedVecConstructors Ix/Theory/Named/Verify/Environment/IndexedVecConstructors.lean effb4340d8f020607ef34f0bb0195ee5e4217f15385e0c690b354c1c2aa23b04 +Lean4Lean.Verify.Environment.IndexedVecOuterReplay Ix/Theory/Named/Verify/Environment/IndexedVecOuterReplay.lean b15ebd574638b1f728439045f06b85421402dabfcc1df1e92fda97198d7de7e8 +Lean4Lean.Verify.Environment.IndexedVecSemanticReplay Ix/Theory/Named/Verify/Environment/IndexedVecSemanticReplay.lean d2793f18d3052acac81951e18e01d5a5a20c3569cae1d831e3064c251bcf05c9 +Lean4Lean.Verify.Environment.InductiveFixtures Ix/Theory/Named/Verify/Environment/InductiveFixtures.lean 5da7e485923a6e5d9db14361844d48c9628f22b7088072d861f9d24e01acd934 +Lean4Lean.Verify.Environment.Lemmas Ix/Theory/Named/Verify/Environment/Lemmas.lean fe1c3fcec718f780e29af9051b62a906b3f6aa53502ac8740d01f8477ab49576 +Lean4Lean.Verify.Environment.MutualInductiveFixtures Ix/Theory/Named/Verify/Environment/MutualInductiveFixtures.lean cab86779e590746367fb013f95ecb48f0a3f86b06a56ccf1ac43ae7fec78514d +Lean4Lean.Verify.Environment.NestedReplay Ix/Theory/Named/Verify/Environment/NestedReplay.lean c76e7291e064a68f64b3d545de4c887fd8321950d514b485832dce76108bfa28 +Lean4Lean.Verify.Environment.NestedRepresentation Ix/Theory/Named/Verify/Environment/NestedRepresentation.lean 7e5cd82cf41d35a6844d2cbc1f5af67ab5ccad33f81e199984e4017348487b2d +Lean4Lean.Verify.Environment.NestedTransformation Ix/Theory/Named/Verify/Environment/NestedTransformation.lean fd1aefd95d4374e0ae87cff11b10c468b7743c822f0f4cbb1a4f3923baee0b0f +Lean4Lean.Verify.Environment.Normalization Ix/Theory/Named/Verify/Environment/Normalization.lean a9df5c28f597dc88f0cc496a1ad8a2c189ef82fddd194007e2bda7de15c27cdc +Lean4Lean.Verify.Environment.NormalizationMatrix Ix/Theory/Named/Verify/Environment/NormalizationMatrix.lean 0495afd91d4770ec0dc998431828b370c65b4cc1b894994c06b4a50525b44342 +Lean4Lean.Verify.Environment.SingletonParityMatrix Ix/Theory/Named/Verify/Environment/SingletonParityMatrix.lean abc03daf11487f774a89f18506995d3e1f45e817f99dcc361219da73dd586322 +Lean4Lean.Verify.Environment.SingletonParityReplay Ix/Theory/Named/Verify/Environment/SingletonParityReplay.lean 19cf222cf463ad9882c7c2ce278150678310b00141fa712684cb68061d1761c2 +Lean4Lean.Verify.Expr Ix/Theory/Named/Verify/Expr.lean 520da96e974f708a15b738479cea37bdc3458adaca6523975f69b15f7bf52cd4 +Lean4Lean.Verify.Level Ix/Theory/Named/Verify/Level.lean 66f0ffcb59ae63498e8c6fb9cee87fa4003c67f1628fb87ec7661714975a4e02 +Lean4Lean.Verify.LevelStd Ix/Theory/Named/Verify/LevelStd.lean 8aada81455e9c6a88eba0dcce86e30daf34ec9215e26ae4dfc9f396a49b8f57c +Lean4Lean.Verify.LocalContext Ix/Theory/Named/Verify/LocalContext.lean 91718812c4d727d27f8723e039b5e7d490e03a7d8bb4163ffed26ee1df030fe8 +Lean4Lean.Verify.Name Ix/Theory/Named/Verify/Name.lean 8a3ae08cc776deba9354b28010327470f241e4c924c335dff142b3f8ec326c78 +Lean4Lean.Verify.NameGenerator Ix/Theory/Named/Verify/NameGenerator.lean 3d7305876fcfc487c625aaee8c737a4bdb1a80b62f70a4fc3cfeaf3cf39219af +Lean4Lean.Verify.NormLt Ix/Theory/Named/Verify/NormLt.lean 970149d18191ecbd889136507e6dd68ea43b833b6170ab825b69a90b4561e2f5 +Lean4Lean.Verify.QSort Ix/Theory/Named/Verify/QSort.lean 79cbf8f96fb223526690c12333a095aa87ecf7d889b4ba2abacd328609550b9a +Lean4Lean.Verify.TypeChecker Ix/Theory/Named/Verify/TypeChecker.lean 0a8441fa3354c7002b2b4b7491f04eaac94bd137d7eaaf8a1859bae0af6f58be +Lean4Lean.Verify.TypeChecker.Basic Ix/Theory/Named/Verify/TypeChecker/Basic.lean a9cc58cb6b86a6f84b30f41f520f3b9f26a30bdd56885107efc6f3e03b3df8dc +Lean4Lean.Verify.TypeChecker.InferType Ix/Theory/Named/Verify/TypeChecker/InferType.lean ddc9fa7ec80b958169d6515a92a723dbe6042ca4b3cdd7d6cfcc6fb859b1f90f +Lean4Lean.Verify.TypeChecker.IsDefEq Ix/Theory/Named/Verify/TypeChecker/IsDefEq.lean 3b9d09d09029eec24514e9905dd00c7435ba103717e506a46939818a80a39c9b +Lean4Lean.Verify.TypeChecker.Reduce Ix/Theory/Named/Verify/TypeChecker/Reduce.lean ddc6abecc09ad21769b83b857d11bbecd3859be58debab62360fa9d9a5ec31cf +Lean4Lean.Verify.TypeChecker.WHNF Ix/Theory/Named/Verify/TypeChecker/WHNF.lean 745637e76413130151c0fb21401a3ee10cbb420aa8c9de4203afff147c2bd9cd +Lean4Lean.Verify.Typing.ConditionallyTyped Ix/Theory/Named/Verify/Typing/ConditionallyTyped.lean 2df5d00a10e4497b631a4d2a90b93cf811ab13cbfb735234a5815963928c3255 +Lean4Lean.Verify.Typing.Expr Ix/Theory/Named/Verify/Typing/Expr.lean 2f9cb98009b6dcd0d45ec4b967e22756f4af1a32a4d45949ad5b4152b8e416c5 +Lean4Lean.Verify.Typing.Lemmas Ix/Theory/Named/Verify/Typing/Lemmas.lean ac1a2960dc945d5d9b5b998b438da6dd7ad6160b08a646f3e2c0b56cede54669 +Lean4Lean.Verify.VLCtx Ix/Theory/Named/Verify/VLCtx.lean f99f9fb85d79b2f9acd201323cc493f737285c34c064fc2bc68680c595878aa3 diff --git a/Ix/Theory/Named/NestedInductive.lean b/Ix/Theory/Named/NestedInductive.lean new file mode 100644 index 000000000..2b469231a --- /dev/null +++ b/Ix/Theory/Named/NestedInductive.lean @@ -0,0 +1,534 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Inductive + +open Ix.Theory (VLevel) + +/-! +# Nested-inductive flattening (Spec-09B) + +The Theory mirror of the kernel's `ElimNestedInductive` transformation, +following the committed Spec-09A design +(`Ix.Theory.Named/Verify/Environment/NestedRepresentation.lean`): the stored +payload of a nested declaration is the source `VInductDecl`, and nested +support flows through an additive artifact coupling + +1. the flattened mutual block, an ordinary `VInductDecl` handled by the + existing arbitrary-block analyzer, and +2. one auxiliary specification per auxiliary family — the Theory analog of + the kernel's `aux2nested` map. + +`nestedElimination?` computes both from the source declaration plus the +caller-supplied metadata of the previously declared inductives that are +nested into (`NestedTargetBlock`). Keeping the target metadata an explicit +input keeps this analyzer environment-free, exactly like `checked?`; +`NestedTargetBlock.WF` separately ties the supplied copy to a Theory +environment. + +The transformation mirrors the kernel phase for phase: + +- An application `I Ds is` is a nested occurrence when `I` is a family of a + supplied target block, the spine covers at least `I`'s parameters, and + the parametric arguments `Ds` mention a family of the growing flattened + block. Parametric arguments that also mention a constructor-local binder + reject the declaration (the kernel's "parameters cannot contain local + variables"), and matched occurrences are rewritten without descending + into the emitted replacement, exactly like `Expr.replace`. +- One auxiliary family is created per family of `I`'s block, in `all` + order, with `I`'s family and constructor types level-instantiated at the + occurrence's levels and parameter-instantiated at `Ds`; auxiliary + constructor bodies are queued and flattened by the same loop until the + block is stable. +- Auxiliary names are canonical: `(`_nested` ++ familyName).appendIndexAfter i` + with a global counter, matching the kernel's choice whenever the ambient + environment contains no colliding `_nested.*` constant. The Spec-09A + collision probe shows the choice is erased from all final artifacts, and + in-block collisions are rejected downstream by `blockNamesOK` exactly + where the kernel's `checkName` rejects its own collisions. + +Acceptance (`nestedStage3`) is flattening success plus generation +readiness of the flattened block through the unchanged Spec-08 machinery. +No generated recursor, rule, or environment replay is claimed at this +checkpoint; the restoration substitution over generation artifacts is +Spec-09C's obligation. +-/ + +namespace Ix.Theory.Named + +deriving instance DecidableEq for VConstant +deriving instance DecidableEq for VDefEq +deriving instance DecidableEq for VConstVal +deriving instance DecidableEq for VInductiveType +deriving instance DecidableEq for VInductDecl + +/-- Does `e` mention, through a loose bvar, one of the `k` binders directly +below its root? `d` counts binders passed inside `e` itself. -/ +def VExpr.hasLooseBelow (k : Nat) : VExpr → (d : Nat := 0) → Bool + | .bvar i, d => d ≤ i && i - d < k + | .sort _, _ | .const .., _ => false + | .app e1 e2, d => e1.hasLooseBelow k d || e2.hasLooseBelow k d + | .lam e1 e2, d | .forallE e1 e2, d => + e1.hasLooseBelow k d || e2.hasLooseBelow k (d+1) + +/-- Lower every loose bvar of `e` by `n`. Total; meaningful only when no +loose bvar lies below `n`, which callers establish with `hasLooseBelow`. -/ +def VExpr.lowerN (n : Nat) : VExpr → (d : Nat := 0) → VExpr + | .bvar i, d => if i < d then .bvar i else .bvar (i - n) + | .sort l, _ => .sort l + | .const c ls, _ => .const c ls + | .app e1 e2, d => .app (e1.lowerN n d) (e2.lowerN n d) + | .lam e1 e2, d => .lam (e1.lowerN n d) (e2.lowerN n (d+1)) + | .forallE e1 e2, d => .forallE (e1.lowerN n d) (e2.lowerN n (d+1)) + +namespace VInductDecl + +/-- Simultaneous outermost-first parameter substitution: the first list +element replaces the outermost of the `args.length` innermost loose bvars. +The same shape as `instantiateRev` on the implementation side. -/ +def instRevParams : VExpr → List VExpr → VExpr + | C, [] => C + | C, e :: es => instRevParams (C.inst e es.length) es + +/-- Substitute the leading `np`-binder telescope of `ty` simultaneously at +`args` (outermost parameter first), mirroring the kernel's +`instantiateForallParams`. Fails when `ty` exposes fewer than `np` +binders. -/ +def instTelescope (np : Nat) (ty : VExpr) (args : List VExpr) : + Option VExpr := do + guard (args.length == np) + guard ((VExpr.telN np ty).length == np) + return instRevParams (VExpr.dropN np ty) args + +/-- One previously declared mutual block that nested occurrences may point +into. `families` is the complete block in `all` order, in that block's own +universe parameters; a copy is supplied so the analyzer stays +environment-free, and `NestedTargetBlock.WF` ties the copy to an +environment. -/ +structure NestedTargetBlock where + nparams : Nat + families : List VInductiveType + +/-- The supplied target copy agrees with the environment's stored +constants. -/ +structure NestedTargetBlock.WF (env : VEnv) (block : NestedTargetBlock) : + Prop where + families : ∀ f ∈ block.families, + env.constants f.name = some f.toVConstVal.toVConstant + ctors : ∀ f ∈ block.families, ∀ c ∈ f.ctors, + env.constants c.name = some c.toVConstant + +def NestedTargetsWF (env : VEnv) (targets : List NestedTargetBlock) : Prop := + ∀ t ∈ targets, t.WF env + +/-- One auxiliary family created by nested elimination: the Theory analog +of one `aux2nested` binding. `values` are the parametric arguments `Ds`, +open over the block parameters (innermost bvar = last parameter), in +declaration level-world. -/ +structure NestedAuxSpec where + aux : Name + target : Name + levels : List VLevel + values : List VExpr + deriving DecidableEq + +/-- The nested occurrence this auxiliary family abbreviates: `I Ds`. -/ +def NestedAuxSpec.value (spec : NestedAuxSpec) : VExpr := + (VExpr.const spec.target spec.levels).appN spec.values + +/-- The flattening result: the flattened mutual block plus one auxiliary +specification per auxiliary family, in flattened family order. When the +source contains no nested occurrence, `flat` is the source itself and +`specs` is empty. -/ +structure NestedElimination (source : VInductDecl) where + flat : VInductDecl + specs : List NestedAuxSpec + +namespace ElimNested + +/-- Growing flattening state. `types` extends the source families with the +auxiliary families; `specs` aligns with `types.drop ntypes`. -/ +structure State where + types : Array VInductiveType + specs : Array NestedAuxSpec + nextIdx : Nat := 1 + +variable (targets : List NestedTargetBlock) (uvars np : Nat) + +/-- The target block owning family `c`, ignoring names that are currently +part of the flattened block itself (the kernel only recognizes previously +*declared* inductives). -/ +def findTarget? (st : State) (c : Name) : Option NestedTargetBlock := + if st.types.any (·.name == c) then none + else targets.find? fun t => t.families.any (·.name == c) + +/-- Register the auxiliary families for one first-seen nested occurrence +`I Ds` and return the auxiliary family name standing for `I` itself. +`doms` is the discovering constructor's parameter telescope, and `values` +are the parametric arguments in parameter-world. -/ +def registerAux (st : State) (block : NestedTargetBlock) (I : Name) + (ls : List VLevel) (doms values : List VExpr) : + Option (Name × State) := do + let mut st := st + let mut result := none + for J in block.families do + if J.uvars != ls.length then failure + let auxName := (`_nested ++ J.name).appendIndexAfter st.nextIdx + let auxType ← instTelescope block.nparams (J.type.instL ls) values + let mut auxCtors : List VConstVal := [] + for c in J.ctors do + let ctype ← instTelescope block.nparams (c.type.instL ls) values + auxCtors := auxCtors ++ + [⟨⟨uvars, VExpr.forallN doms ctype⟩, c.name.replacePrefix J.name auxName⟩] + let auxFamily : VInductiveType := + { name := auxName, uvars, type := VExpr.forallN doms auxType + ctors := auxCtors } + st := + { types := st.types.push auxFamily + specs := st.specs.push ⟨auxName, J.name, ls, values⟩ + nextIdx := st.nextIdx + 1 } + if J.name == I then result := some auxName + match result with + | some auxName => return (auxName, st) + | none => none + +/-- Rewrite one constructor-body subterm at binder depth `k`, mirroring +`replaceAllNested`: matched occurrences are replaced without descending +into the replacement; unmatched nodes recurse into their children. -/ +def replace (doms : List VExpr) : + VExpr → (k : Nat) → State → Option (VExpr × State) + | e@(.app f a), k, st => do + match rewrite? e k st with + | some result => result + | none => + let (f', st) ← replace doms f k st + let (a', st) ← replace doms a k st + return (.app f' a', st) + | e@(.const ..), k, st => (rewrite? e k st).getD (some (e, st)) + | .lam ty body, k, st => do + let (ty', st) ← replace doms ty k st + let (body', st) ← replace doms body (k+1) st + return (.lam ty' body', st) + | .forallE ty body, k, st => do + let (ty', st) ← replace doms ty k st + let (body', st) ← replace doms body (k+1) st + return (.forallE ty' body', st) + | e, _, st => some (e, st) + where + /-- `some (some ..)` rewrites the node, `some none` is a hard rejection, + `none` leaves the node to the structural recursion. -/ + rewrite? (e : VExpr) (k : Nat) (st : State) : + Option (Option (VExpr × State)) := do + let .const c ls := VExpr.appHead e | none + let args := e.appArgs [] + let block ← findTarget? targets st c + guard (block.nparams ≤ args.length) + guard (0 < block.nparams) + let ds := args.take block.nparams + let names := st.types.toList.map (·.name) + guard (ds.any (·.hasAnyConst names)) + -- the kernel's "nested inductive datatypes parameters cannot contain + -- local variables" rejection + if ds.any (·.hasLooseBelow k) then return none + let values := ds.map (·.lowerN k) + let key := (VExpr.const c ls).appN values + let rest := args.drop block.nparams + let recover (auxName : Name) (st : State) : VExpr × State := + ((VExpr.const auxName (VLevel.params uvars)).appN + (VExpr.bvarRevRange k np ++ rest), st) + match st.specs.find? (·.value == key) with + | some spec => return some (recover spec.aux st) + | none => + match registerAux uvars st block c ls doms values with + | some (auxName, st) => return some (recover auxName st) + | none => return none + +/-- Flatten every constructor of every block family, including the queued +auxiliary families, until the block is stable. `fuel` mirrors the +kernel's `inductiveFuel` bound on the same loop. -/ +def run (fuel : Nat) (i : Nat) (st : State) : Option State := + match fuel with + | 0 => none + | fuel+1 => + if h : i < st.types.size then + let ty := st.types[i] + let step := ty.ctors.foldlM (init := ([], st)) fun (acc, st) c => do + let doms := VExpr.telN np c.type + guard (doms.length == np) + let (body, st) ← replace targets uvars np doms (VExpr.dropN np c.type) 0 st + return (acc ++ [{ c with type := VExpr.forallN doms body }], st) + match step with + | some (ctors, st) => + run fuel (i+1) { st with types := st.types.set! i { ty with ctors } } + | none => none + else some st + +end ElimNested + +/-- Flatten one source declaration against the supplied target blocks. +Returns the flattened block plus the auxiliary specifications; the +identity result (`flat = source`, no specs) is returned when nothing is +nested. -/ +def nestedElimination? (targets : List NestedTargetBlock) + (source : VInductDecl) (fuel : Nat := 1000) : + Option (NestedElimination source) := do + let st ← ElimNested.run targets source.uvars source.nparams fuel 0 + { types := source.types.toArray, specs := #[] } + return { flat := { source with types := st.types.toList } + specs := st.specs.toList } + +/-- The number of auxiliary families, matching the stored +`InductiveVal.numNested` of an accepted nested declaration. -/ +def NestedElimination.numNested {source : VInductDecl} + (elim : NestedElimination source) : Nat := + elim.specs.length + +/-- A flattened declaration accepted by the unchanged arbitrary-block +machinery: the complete Spec-09B validation gate. Positivity, name, level, +anatomy, and generation-shape checking of the flattened block reuse the +Spec-08 analyzers verbatim. -/ +structure NestedBlockChecked (source : VInductDecl) where + elim : NestedElimination source + generation : BlockGenerationChecked elim.flat + +def nestedBlockChecked? (targets : List NestedTargetBlock) + (source : VInductDecl) (fuel : Nat := 1000) : + Option (NestedBlockChecked source) := do + let elim ← nestedElimination? targets source fuel + let generation ← elim.flat.identityBlockGeneration? + return ⟨elim, generation⟩ + +/-- Structural acceptance for a nested declaration. -/ +def nestedStage3 (targets : List NestedTargetBlock) + (source : VInductDecl) (fuel : Nat := 1000) : Bool := + (nestedBlockChecked? targets source fuel).isSome + +/-! ## Restoration (Spec-09C) + +The restoration substitution σ maps the flattened block's generation +artifacts back to the stored metadata surface: auxiliary family constants +become their nested values, auxiliary constructor constants become the +target block's constructors applied to the instantiated value's own +arguments, and auxiliary recursor constants are renamed onto the main +family's `appendIndexAfter` inventory. On an application spine headed by +an auxiliary family or constructor, the first `nparams` spine arguments +are consumed by the value instantiation, mirroring +`ElimNestedInductive.Result.restoreNested`; generated artifacts always +apply auxiliary constants to at least the block parameters (the kernel +asserts exactly this), so the identity fallback on an under-applied +auxiliary head is unreachable from real artifacts and merely keeps σ +total. -/ + +/-- One σ replacement entry. `value` is already in the level world of the +artifact being restored (`instL`-spliced by the caller for recursor-world +artifacts). -/ +structure RestoreEntry where + aux : Name + np : Nat + value : VExpr + deriving DecidableEq + +def findRestoreCtor (entries : List RestoreEntry) (c : Name) : + Option (RestoreEntry × Name) := + entries.findSome? fun entry => + if entry.aux.isPrefixOf c && c != entry.aux then + some (entry, c.replacePrefix entry.aux .anonymous) + else none + +/-- σ on one expression, bottom-up: a replacement fires at the innermost +spine node where an auxiliary head has collected exactly its block-parameter +count, and enclosing applications extend the already-restored value. On +generated artifacts — where auxiliary constants are always applied to at +least the block parameters and never occur inside another auxiliary spine's +arguments — this coincides with `restoreNested`'s top-down +replace-without-descending pass. `recMap` renames auxiliary recursor +constants and is consulted before the constructor-prefix case, exactly like +`restoreNested`'s `auxRec` map. -/ +def restoreExpr (entries : List RestoreEntry) (recMap : List (Name × Name)) : + VExpr → VExpr + | .bvar i => .bvar i + | .sort l => .sort l + | .lam ty body => .lam (restoreExpr entries recMap ty) (restoreExpr entries recMap body) + | .forallE ty body => + .forallE (restoreExpr entries recMap ty) (restoreExpr entries recMap body) + | .app f a => + let e := VExpr.app (restoreExpr entries recMap f) (restoreExpr entries recMap a) + (restoreSpine e).getD e + | e@(.const ..) => (restoreSpine e).getD e + where + /-- Fire one replacement at a completed spine. The head constant is + still unrestored exactly when no inner node completed its parameter + count. -/ + restoreSpine (e : VExpr) : Option VExpr := + match VExpr.appHead e with + | .const c ls => + let args := e.appArgs [] + match recMap.find? (·.1 == c) with + | some (_, newName) => + if args.isEmpty then some (VExpr.const newName ls) else none + | none => + match entries.find? (·.aux == c) with + | some entry => + if args.length == entry.np then + some (instRevParams entry.value args) + else none + | none => + match findRestoreCtor entries c with + | some (entry, suffix) => + if args.length == entry.np then + let value := instRevParams entry.value args + match VExpr.appHead value with + | .const iname ils => + some ((VExpr.const (iname ++ suffix) ils).appN (value.appArgs [])) + | _ => none + else none + | none => none + | _ => none + +namespace NestedBlockChecked + +variable {source : VInductDecl} + +/-- The main family name owning the restored recursor inventory. -/ +def mainName (_nested : NestedBlockChecked source) : Name := + match source.types with + | ty :: _ => ty.name + | [] => .anonymous + +/-- Auxiliary recursor renaming: the `i`-th auxiliary family's recursor +becomes `mainName.rec_(i+1)`, matching `mkAuxRecNameMap`. -/ +def recMap (nested : NestedBlockChecked source) : List (Name × Name) := + nested.elim.specs.mapIdx fun i spec => + (.str spec.aux "rec", ((.str nested.mainName "rec" : Name)).appendIndexAfter (i + 1)) + +/-- σ entries in declaration level-world (constructor-type restorations). -/ +def declEntries (nested : NestedBlockChecked source) : List RestoreEntry := + nested.elim.specs.map fun spec => + ⟨spec.aux, source.nparams, spec.value⟩ + +/-- σ entries spliced into recursor level-world by the elimination +offset. -/ +def recEntries (nested : NestedBlockChecked source) : List RestoreEntry := + nested.elim.specs.map fun spec => + ⟨spec.aux, source.nparams, + spec.value.instL (VLevel.params' source.uvars + (nested.generation.recUvars - source.uvars))⟩ + +/-- σ on a recursor-world artifact. -/ +def restoreRec (nested : NestedBlockChecked source) (e : VExpr) : VExpr := + restoreExpr nested.recEntries nested.recMap e + +/-- The restored recursor inventory: the flattened block's recursors with +auxiliary names renamed and every type restored. Source-family recursors +keep their `.str name "rec"` names. -/ +def recursors (nested : NestedBlockChecked source) : List VConstVal := + nested.generation.recursors.map fun r => + ⟨⟨r.uvars, nested.restoreRec r.type⟩, + ((nested.recMap.find? (·.1 == r.name)).map (·.2)).getD r.name⟩ + +/-- The restored rule inventory, in the flattened block's globally ordered +rule order. -/ +def generatedRules (nested : NestedBlockChecked source) : List VDefEq := + nested.generation.generatedRules.map fun df => + { df with + lhs := nested.restoreRec df.lhs + rhs := nested.restoreRec df.rhs + type := nested.restoreRec df.type } + +end NestedBlockChecked + +end VInductDecl + +/-- The nested transaction: the four-phase shape of +`addInductBlockGeneration` with the *source* families and constructors as +the stored payload and the *restored* recursors and rules as the generated +artifacts. No auxiliary constant enters the environment. -/ +def VEnv.addInductNested {source : VInductDecl} (env : VEnv) + (nested : source.NestedBlockChecked) : Option VEnv := do + let env ← source.blockTypeConstants.foldlM + (fun env type => env.addConst type.name type.toVConstant) env + let env ← source.blockConstructorConstants.foldlM + (fun env constructor => env.addConst constructor.name constructor.toVConstant) env + let env ← nested.recursors.foldlM + (fun env recursor => env.addConst recursor.name recursor.toVConstant) env + return nested.generatedRules.foldl VEnv.addDefEq env + +namespace VInductDecl + +/-- Chained constant well-formedness along an `addConst` fold: each +constant is well formed in the environment already holding every earlier +one. -/ +def NestedConstsWF (env : VEnv) : List VConstVal → Prop + | [] => True + | c :: cs => c.toVConstant.WF env ∧ + ∀ env', env.addConst c.name c.toVConstant = some env' → + NestedConstsWF env' cs + +/-- Chained rule well-formedness along an `addDefEq` fold. -/ +def NestedRulesWF (env : VEnv) : List VDefEq → Prop + | [] => True + | df :: dfs => df.WF env ∧ NestedRulesWF (env.addDefEq df) dfs + +/-- Semantic input to nested preservation: the four transaction phases are +well formed at their exact insertion environments. The phase environments +are determined by the deterministic constant folds, so each later field +takes the earlier folds as hypotheses; a fixture discharges them by +computation. Inhabiting this package from the flattened block's staged +semantic certificate is the σ-transport route recorded by the Spec-09A +design note; fixtures may equally inhabit it from direct checker +executions on the restored artifacts. -/ +structure NestedBlockChecked.WF {source : VInductDecl} + (nested : NestedBlockChecked source) (env : VEnv) : Prop where + types : NestedConstsWF env source.blockTypeConstants + ctors : ∀ {typeEnv}, + source.blockTypeConstants.foldlM + (fun env type => env.addConst type.name type.toVConstant) env = + some typeEnv → + NestedConstsWF typeEnv source.blockConstructorConstants + recs : ∀ {typeEnv ctorEnv}, + source.blockTypeConstants.foldlM + (fun env type => env.addConst type.name type.toVConstant) env = + some typeEnv → + source.blockConstructorConstants.foldlM + (fun env constructor => env.addConst constructor.name constructor.toVConstant) + typeEnv = some ctorEnv → + NestedConstsWF ctorEnv nested.recursors + rules : ∀ {typeEnv ctorEnv recEnv}, + source.blockTypeConstants.foldlM + (fun env type => env.addConst type.name type.toVConstant) env = + some typeEnv → + source.blockConstructorConstants.foldlM + (fun env constructor => env.addConst constructor.name constructor.toVConstant) + typeEnv = some ctorEnv → + nested.recursors.foldlM + (fun env recursor => env.addConst recursor.name recursor.toVConstant) + ctorEnv = some recEnv → + NestedRulesWF recEnv nested.generatedRules + +end VInductDecl + +/-- Exact phase boundaries of a successful nested transaction. -/ +structure VEnv.AddInductNestedTrace {source : VInductDecl} + (env env' : VEnv) (nested : source.NestedBlockChecked) where + typeEnv : VEnv + ctorEnv : VEnv + recEnv : VEnv + addTypes : + source.blockTypeConstants.foldlM + (fun env type => env.addConst type.name type.toVConstant) env = + some typeEnv + addCtors : + source.blockConstructorConstants.foldlM + (fun env constructor => env.addConst constructor.name constructor.toVConstant) + typeEnv = some ctorEnv + addRecs : + nested.recursors.foldlM + (fun env recursor => env.addConst recursor.name recursor.toVConstant) + ctorEnv = some recEnv + addRules : + nested.generatedRules.foldl VEnv.addDefEq recEnv = env' + +end Ix.Theory.Named diff --git a/Ix/Theory/Named/NestedInductiveFixtures.lean b/Ix/Theory/Named/NestedInductiveFixtures.lean new file mode 100644 index 000000000..8ca84b912 --- /dev/null +++ b/Ix/Theory/Named/NestedInductiveFixtures.lean @@ -0,0 +1,329 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.NestedInductive + +/-! +# Nested flattening fixtures (Spec-09B) + +Executable pins for `nestedElimination?` on the two ladder fixtures — a +universe-polymorphic rose tree through `List` and a nested indexed family +through a `PVec`-style vector — plus the nearest structural rejections. +Every family, constructor, auxiliary specification, and acceptance bit is +compared against a hand-written expected descriptor. The kernel +differential for the same shapes lives in +`Ix.Theory.Named/Verify/Environment/NestedTransformation.lean`. +-/ + +namespace Ix.Theory.Named.NestedInductiveFixtures + +open VInductDecl + +/-! ## Target blocks + +Hand-written copies of the nested-into metadata, in each block's own +universe parameters; the Verify differential checks the same shapes +against Lean's stored metadata. -/ + +/-- `List` as a nested target: one family, one parameter. -/ +def listTarget : NestedTargetBlock where + nparams := 1 + families := + [{ name := `List + uvars := 1 + type := .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0))) + ctors := + [⟨⟨1, .forallE (.sort (.succ (.param 0))) + (.app (.const `List [.param 0]) (.bvar 0))⟩, `List.nil⟩, + ⟨⟨1, .forallE (.sort (.succ (.param 0))) + (.forallE (.bvar 0) + (.forallE (.app (.const `List [.param 0]) (.bvar 1)) + (.app (.const `List [.param 0]) (.bvar 2))))⟩, `List.cons⟩] }] + +/-- A `PVec`-style indexed vector as a nested target: one parameter, one +`Nat` index, indices spelled with `Nat.zero`/`Nat.succ`. -/ +def pvecTarget : NestedTargetBlock where + nparams := 1 + families := + [{ name := `PVec + uvars := 0 + type := .forallE (.sort (.succ .zero)) + (.forallE (.const `Nat []) (.sort (.succ .zero))) + ctors := + [⟨⟨0, .forallE (.sort (.succ .zero)) + (.app (.app (.const `PVec []) (.bvar 0)) (.const `Nat.zero []))⟩, + `PVec.nil⟩, + ⟨⟨0, .forallE (.sort (.succ .zero)) + (.forallE (.bvar 0) + (.forallE (.const `Nat []) + (.forallE (.app (.app (.const `PVec []) (.bvar 2)) (.bvar 0)) + (.app (.app (.const `PVec []) (.bvar 3)) + (.app (.const `Nat.succ []) (.bvar 1))))))⟩, + `PVec.cons⟩] }] + +/-! ## Rose tree through `List` -/ + +def roseAux : Lean.Name := (`_nested ++ `List).appendIndexAfter 1 + +/-- `inductive Rose (α : Type u) | node : α → List (Rose α) → Rose α` -/ +def roseSource : VInductDecl where + uvars := 1 + nparams := 1 + types := + [{ name := `Rose + uvars := 1 + type := .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0))) + ctors := + [⟨⟨1, .forallE (.sort (.succ (.param 0))) + (.forallE (.bvar 0) + (.forallE (.app (.const `List [.param 0]) + (.app (.const `Rose [.param 0]) (.bvar 1))) + (.app (.const `Rose [.param 0]) (.bvar 2))))⟩, `Rose.node⟩] }] + +/-- The expected flattened rose block: the rewritten source family plus one +auxiliary family, exactly the shapes pinned against the kernel by the +Spec-09A probes. -/ +def roseFlat : VInductDecl where + uvars := 1 + nparams := 1 + types := + [{ name := `Rose + uvars := 1 + type := .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0))) + ctors := + [⟨⟨1, .forallE (.sort (.succ (.param 0))) + (.forallE (.bvar 0) + (.forallE (.app (.const roseAux [.param 0]) (.bvar 1)) + (.app (.const `Rose [.param 0]) (.bvar 2))))⟩, `Rose.node⟩] }, + { name := roseAux + uvars := 1 + type := .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0))) + ctors := + [⟨⟨1, .forallE (.sort (.succ (.param 0))) + (.app (.const roseAux [.param 0]) (.bvar 0))⟩, roseAux ++ `nil⟩, + ⟨⟨1, .forallE (.sort (.succ (.param 0))) + (.forallE (.app (.const `Rose [.param 0]) (.bvar 0)) + (.forallE (.app (.const roseAux [.param 0]) (.bvar 1)) + (.app (.const roseAux [.param 0]) (.bvar 2))))⟩, + roseAux ++ `cons⟩] }] + +/-- The expected auxiliary specification: `List (Rose α)`, open over the +block parameter. -/ +def roseSpec : NestedAuxSpec where + aux := roseAux + target := `List + levels := [.param 0] + values := [.app (.const `Rose [.param 0]) (.bvar 0)] + +def roseElim? : Option (NestedElimination roseSource) := + nestedElimination? [listTarget] roseSource + +#guard roseElim?.isSome +#guard (roseElim?.map fun elim => elim.flat == roseFlat).getD false +#guard (roseElim?.map fun elim => elim.specs == [roseSpec]).getD false +#guard (roseElim?.map (·.numNested)).getD 0 == 1 +#guard roseFlat.stage3 +#guard nestedStage3 [listTarget] roseSource +-- acceptance behavior of the raw analyzers on the source is unchanged +#guard !roseSource.stage3 + +/-! ## Nested indexed family through `PVec` -/ + +def nvAux : Lean.Name := (`_nested ++ `PVec).appendIndexAfter 1 + +/-- `inductive NV | node : (n : Nat) → PVec NV n → NV` -/ +def nvSource : VInductDecl where + uvars := 0 + nparams := 0 + types := + [{ name := `NV + uvars := 0 + type := .sort (.succ .zero) + ctors := + [⟨⟨0, .forallE (.const `Nat []) + (.forallE (.app (.app (.const `PVec []) (.const `NV [])) + (.bvar 0)) + (.const `NV []))⟩, `NV.node⟩] }] + +/-- The expected flattened indexed block: the auxiliary family keeps the +`Nat` index, its `nil` instantiates the index at `Nat.zero`, and its +`cons` retains sibling recursion through `NV` plus the successor index. -/ +def nvFlat : VInductDecl where + uvars := 0 + nparams := 0 + types := + [{ name := `NV + uvars := 0 + type := .sort (.succ .zero) + ctors := + [⟨⟨0, .forallE (.const `Nat []) + (.forallE (.app (.const nvAux []) (.bvar 0)) + (.const `NV []))⟩, `NV.node⟩] }, + { name := nvAux + uvars := 0 + type := .forallE (.const `Nat []) (.sort (.succ .zero)) + ctors := + [⟨⟨0, .app (.const nvAux []) (.const `Nat.zero [])⟩, nvAux ++ `nil⟩, + ⟨⟨0, .forallE (.const `NV []) + (.forallE (.const `Nat []) + (.forallE (.app (.const nvAux []) (.bvar 0)) + (.app (.const nvAux []) + (.app (.const `Nat.succ []) (.bvar 1)))))⟩, + nvAux ++ `cons⟩] }] + +/-- The expected specification: the closed partial application `PVec NV`; +the index argument stays behind on each occurrence. -/ +def nvSpec : NestedAuxSpec where + aux := nvAux + target := `PVec + levels := [] + values := [.const `NV []] + +def nvElim? : Option (NestedElimination nvSource) := + nestedElimination? [pvecTarget] nvSource + +#guard nvElim?.isSome +#guard (nvElim?.map fun elim => elim.flat == nvFlat).getD false +#guard (nvElim?.map fun elim => elim.specs == [nvSpec]).getD false +#guard nvFlat.stage3 +#guard nestedStage3 [pvecTarget] nvSource +#guard !nvSource.stage3 + +/-! ## Nearest structural rejections -/ + +/-- A parametric argument mentioning a constructor-local binder: +`node : (n : Nat) → List (Loose n) → Loose` — the kernel's "parameters +cannot contain local variables" class. Flattening itself rejects. -/ +def looseSource : VInductDecl where + uvars := 0 + nparams := 0 + types := + [{ name := `Loose + uvars := 0 + type := .sort (.succ .zero) + ctors := + [⟨⟨0, .forallE (.const `Nat []) + (.forallE (.app (.const `List [.zero]) + (.app (.const `Loose []) (.bvar 0))) + (.const `Loose []))⟩, `Loose.node⟩] }] + +#guard (nestedElimination? [listTarget] looseSource).isNone +#guard !nestedStage3 [listTarget] looseSource + +/-- A well-scoped but ill-shaped parametric argument: +`node : Bad → List (Bad Nat.zero) → Bad` flattens, but the auxiliary +constructor then mentions `Bad` applied off the parameter spine, which the +unchanged block analyzer rejects. -/ +def badAppSource : VInductDecl where + uvars := 0 + nparams := 0 + types := + [{ name := `Bad + uvars := 0 + type := .sort (.succ .zero) + ctors := + [⟨⟨0, .forallE (.const `Bad []) + (.forallE (.app (.const `List [.zero]) + (.app (.const `Bad []) (.const `Nat.zero []))) + (.const `Bad []))⟩, `Bad.node⟩] }] + +#guard (nestedElimination? [listTarget] badAppSource).isSome +#guard !nestedStage3 [listTarget] badAppSource + +-- Without the `List` target metadata the occurrence is not recognized, +-- the flattened block is the source itself, and the unchanged analyzer +-- rejects the under-a-foreign-head family mention. +#guard (nestedElimination? [] roseSource).isSome +#guard ((nestedElimination? [] roseSource).map + fun elim => elim.flat == roseSource && elim.specs == []).getD false +#guard !nestedStage3 [] roseSource + +/-- A source family occupying the first canonical auxiliary name collides +with the created auxiliary family; `blockNamesOK` rejects the flattened +block exactly where the kernel's `checkName` rejects its own duplicate +insertion. -/ +def collisionSource : VInductDecl where + uvars := 1 + nparams := 1 + types := + [{ name := `Rose + uvars := 1 + type := .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0))) + ctors := + [⟨⟨1, .forallE (.sort (.succ (.param 0))) + (.forallE (.bvar 0) + (.forallE (.app (.const `List [.param 0]) + (.app (.const `Rose [.param 0]) (.bvar 1))) + (.app (.const `Rose [.param 0]) (.bvar 2))))⟩, `Rose.node⟩] }, + { name := (`_nested ++ `List).appendIndexAfter 1 + uvars := 1 + type := .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0))) + ctors := [] }] + +#guard (nestedElimination? [listTarget] collisionSource).isSome +#guard !nestedStage3 [listTarget] collisionSource + +/-! ## Restoration pins (Spec-09C) + +Structural pins for the restored generation artifacts; the exact +comparison against Lean's stored recursor types and rule RHSs lives in the +Verify differential. -/ + +def roseNested? : Option (NestedBlockChecked roseSource) := + nestedBlockChecked? [listTarget] roseSource + +def nvNested? : Option (NestedBlockChecked nvSource) := + nestedBlockChecked? [pvecTarget] nvSource + +#guard roseNested?.isSome +#guard nvNested?.isSome + +-- the restored recursor inventory: one per source family plus one per +-- auxiliary family, on the main family's `appendIndexAfter` names +#guard (roseNested?.map fun n => n.recursors.map (·.name)).getD [] == + [`Rose.rec, ((.str `Rose "rec" : Lean.Name)).appendIndexAfter 1] +#guard (nvNested?.map fun n => n.recursors.map (·.name)).getD [] == + [`NV.rec, ((.str `NV "rec" : Lean.Name)).appendIndexAfter 1] + +def roseAuxConsts : List Lean.Name := + [roseAux, roseAux ++ `nil, roseAux ++ `cons, .str roseAux "rec"] + +def nvAuxConsts : List Lean.Name := + [nvAux, nvAux ++ `nil, nvAux ++ `cons, .str nvAux "rec"] + +/-- No auxiliary constant survives restoration in any recursor type or +rule component. -/ +def restoredClean {source : VInductDecl} (auxConsts : List Lean.Name) + (nested : NestedBlockChecked source) : Bool := + nested.recursors.all (fun r => !VExpr.hasAnyConst auxConsts r.type) && + nested.generatedRules.all fun df => + !VExpr.hasAnyConst auxConsts df.lhs && + !VExpr.hasAnyConst auxConsts df.rhs && + !VExpr.hasAnyConst auxConsts df.type + +#guard (roseNested?.map (restoredClean roseAuxConsts)).getD false +#guard (nvNested?.map (restoredClean nvAuxConsts)).getD false + +-- the globally flattened rule inventory: one node rule plus the two +-- restored `List`/`PVec` rules +#guard (roseNested?.map fun n => n.generatedRules.length).getD 0 == 3 +#guard (nvNested?.map fun n => n.generatedRules.length).getD 0 == 3 + +-- the nested transaction inserts the source payload and the restored +-- recursors, and no auxiliary constant +def roseNestedEnv? : Option VEnv := do + VEnv.empty.addInductNested (← roseNested?) + +#guard roseNestedEnv?.isSome +#guard (roseNestedEnv?.map fun env => + (env.constants `Rose).isSome && (env.constants `Rose.node).isSome && + (env.constants `Rose.rec).isSome && + (env.constants (((.str `Rose "rec" : Lean.Name)).appendIndexAfter 1)).isSome && + (env.constants roseAux).isNone && + (env.constants (roseAux ++ `cons)).isNone && + (env.constants (.str roseAux "rec")).isNone).getD false + +end Ix.Theory.Named.NestedInductiveFixtures diff --git a/Ix/Theory/Named/Projection.lean b/Ix/Theory/Named/Projection.lean new file mode 100644 index 000000000..b4ed1ed5c --- /dev/null +++ b/Ix/Theory/Named/Projection.lean @@ -0,0 +1,3518 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Typing.InductivePatternWF + +open Ix.Theory (VLevel) + +/-! +# Structure projections + +This module is the consumer-neutral projection boundary. A projection is +not determined by a structure name and field number alone: universe +instantiations, parameters, the constructor telescope, and the generated +recursor/iota package all affect its meaning. `VStructureView` retains that +data from the same checked artifact used by inductive generation. + +Projection terms are encoded with the generated recursor. Earlier +projections occur in the motive of a dependent later projection, so one view +determines both the projected term and its dependent result type. No +projection-function name map or unconstrained metadata witness is involved. +-/ + +namespace Ix.Theory.Named + +open VInductDecl + +/-- Instantiate an outermost-first argument list at a fixed offset. + +The `k` variables below the substituted telescope remain bound. Each +argument is lifted past them before it replaces the then-outermost variable. +This is the operation needed to specialize constructor parameters while +retaining the preceding dependent fields. -/ +def VExpr.instRevAt : VExpr → List VExpr → Nat → VExpr + | e, [], _ => e + | e, a :: as, k => instRevAt (e.inst a (k + as.length)) as k + +theorem VExpr.instRevAt_zero (e : VExpr) (args : List VExpr) : + e.instRevAt args 0 = e.instRev args := by + induction args generalizing e with + | nil => rfl + | cons arg args ih => + simp only [VExpr.instRevAt, VExpr.instRev] + simpa using ih (e := e.inst arg args.length) + +private theorem VExpr.instRevAt_closedN (args : List VExpr) + {C : VExpr} {k : Nat} (hC : C.ClosedN k) : + C.instRevAt args k = C := by + induction args generalizing C with + | nil => rfl + | cons arg args ih => + simp only [VExpr.instRevAt] + rw [hC.instN_eq (by omega)] + exact ih hC + +private theorem VExpr.instRev_forallE_projection + (A B : VExpr) (args : List VExpr) : + VExpr.instRev (.forallE A B) args = + .forallE (VExpr.instRev A args) + (VExpr.instRevAt B args 1) := by + induction args generalizing A B with + | nil => rfl + | cons arg args ih => + simp only [VExpr.instRev, VExpr.inst] + rw [ih] + congr 1 + simp only [VExpr.instRevAt] + rw [show 1 + args.length = args.length + 1 by omega] + +private theorem VExpr.instRevAt_forallE_projection + (A B : VExpr) (args : List VExpr) (k : Nat) : + VExpr.instRevAt (.forallE A B) args k = + .forallE (VExpr.instRevAt A args k) + (VExpr.instRevAt B args (k + 1)) := by + induction args generalizing A B with + | nil => rfl + | cons arg args ih => + simp only [VExpr.instRevAt, VExpr.inst] + rw [ih] + congr 1 + rw [show k + args.length + 1 = k + 1 + args.length by omega] + +private theorem VExpr.instRevAt_forallN_projection + (As : List VExpr) (B : VExpr) (args : List VExpr) (k : Nat) : + VExpr.instRevAt (VExpr.forallN As B) args k = + VExpr.forallN + (As.zipIdx k |>.map fun x => x.1.instRevAt args x.2) + (B.instRevAt args (k + As.length)) := by + induction As generalizing k with + | nil => rfl + | cons A As ih => + simp only [VExpr.forallN, VExpr.instRevAt_forallE_projection, + List.zipIdx, List.map_cons, List.length_cons] + rw [ih] + rw [show k + 1 + As.length = k + (As.length + 1) by omega] + +theorem VExpr.instRev_forallN_projection + (As : List VExpr) (B : VExpr) (args : List VExpr) : + VExpr.instRev (VExpr.forallN As B) args = + VExpr.forallN + (As.zipIdx.map fun x => x.1.instRevAt args x.2) + (B.instRevAt args As.length) := by + cases As with + | nil => simp [VExpr.forallN, VExpr.instRevAt_zero] + | cons A As => + simp only [VExpr.forallN, VExpr.instRev_forallE_projection, + List.zipIdx, List.map_cons, List.length_cons] + rw [VExpr.instRevAt_forallN_projection] + rw [VExpr.instRevAt_zero] + congr 2 + rw [Nat.add_comm] + +/-- Consume a syntactic prefix of dependent `forall` binders, instantiating +them outermost-first. -/ +def VExpr.consumeForalls? : VExpr → List VExpr → Option VExpr + | e, [] => some e + | .forallE _ body, arg :: args => consumeForalls? (body.inst arg) args + | _, _ :: _ => none + +theorem VExpr.consumeForalls?_append (e : VExpr) + (left right : List VExpr) : + e.consumeForalls? (left ++ right) = + (e.consumeForalls? left).bind fun cursor => + cursor.consumeForalls? right := by + induction left generalizing e with + | nil => rfl + | cons arg left ih => + cases e <;> simp [VExpr.consumeForalls?, ih] + +theorem VExpr.instTelN_getElem? (arg : VExpr) (fields : List VExpr) + (k i : Nat) : + (VExpr.instTelN arg fields k)[i]? = + fields[i]?.map fun field => field.inst arg (k + i) := by + induction fields generalizing k i with + | nil => simp [VExpr.instTelN] + | cons field fields ih => + cases i with + | zero => simp [VExpr.instTelN] + | succ i => + simp only [VExpr.instTelN, List.getElem?_cons_succ] + simpa only [Nat.add_assoc, Nat.add_comm, + Nat.add_left_comm] using ih (k + 1) i + +/-- Consuming `args` from a telescope exposes the next original binder with +exactly those arguments substituted. -/ +theorem VExpr.consumeForalls?_forallN_domain + (fields : List VExpr) (result : VExpr) (args : List VExpr) + (hlen : args.length < fields.length) : + ∃ field body, + fields[args.length]? = some field ∧ + VExpr.consumeForalls? (VExpr.forallN fields result) args = + some (.forallE (field.instRevAt args 0) body) := by + induction args generalizing fields result with + | nil => + cases fields with + | nil => simp at hlen + | cons field fields => + exact ⟨field, VExpr.forallN fields result, rfl, rfl⟩ + | cons arg args ih => + cases fields with + | nil => simp at hlen + | cons field fields => + have hlen' : args.length < + (VExpr.instTelN arg fields 0).length := by + simpa [VExpr.instTelN_length] using hlen + obtain ⟨field', body, hfield', hconsume⟩ := + ih (VExpr.instTelN arg fields 0) + (result.inst arg fields.length) hlen' + rw [VExpr.instTelN_getElem?] at hfield' + obtain ⟨original, horiginal, rfl⟩ := Option.map_eq_some_iff.1 hfield' + refine ⟨original, body, by simpa using horiginal, ?_⟩ + simp only [VExpr.forallN, VExpr.consumeForalls?, + VExpr.instN_forallN] + simp only [Nat.zero_add] + simpa only [VExpr.instRevAt, Nat.zero_add] using hconsume + +@[simp] theorem VExpr.instL_instRevAt (e : VExpr) (as : List VExpr) + (k : Nat) : + (e.instRevAt as k).instL ls = + (e.instL ls).instRevAt (as.map (VExpr.instL ls)) k := by + induction as generalizing e with + | nil => rfl + | cons a as ih => + simp only [VExpr.instRevAt, List.map_cons] + simpa only [VExpr.instL_instN, List.length_map] using + ih (e := e.inst a (k + as.length)) + +private theorem VExpr.instL_lamN_projection (ls : List VLevel) : + ∀ (As : List VExpr) (e : VExpr), + (VExpr.lamN As e).instL ls = + VExpr.lamN (As.map (VExpr.instL ls)) (e.instL ls) + | [], _ => rfl + | _ :: As, e => by + simp only [VExpr.lamN, VExpr.instL, List.map_cons] + rw [VExpr.instL_lamN_projection ls As e] + +private theorem VExpr.liftN_lamN_projection (n : Nat) : + ∀ (As : List VExpr) (e : VExpr) (k : Nat), + (VExpr.lamN As e).liftN n k = + VExpr.lamN (VExpr.liftTelN n As k) + (e.liftN n (k + As.length)) + | [], _, _ => rfl + | _ :: As, e, k => by + simp only [VExpr.lamN, VExpr.liftN, VExpr.liftTelN, + List.length_cons] + rw [VExpr.liftN_lamN_projection n As e (k + 1)] + rw [show k + 1 + As.length = k + (As.length + 1) by omega] + +private theorem VExpr.instN_lamN_projection (a : VExpr) : + ∀ (As : List VExpr) (e : VExpr) (k : Nat), + (VExpr.lamN As e).inst a k = + VExpr.lamN (VExpr.instTelN a As k) + (e.inst a (k + As.length)) + | [], _, _ => rfl + | _ :: As, e, k => by + simp only [VExpr.lamN, VExpr.inst, VExpr.instTelN, + List.length_cons] + rw [VExpr.instN_lamN_projection a As e (k + 1)] + rw [show k + 1 + As.length = k + (As.length + 1) by omega] + +private theorem VExpr.liftN_lift_projection (e : VExpr) (n k : Nat) : + e.lift.liftN n (k + 1) = (e.liftN n k).lift := + (VExpr.lift_liftN' e k).symm + +private theorem VExpr.liftN_liftAt_projection + (e : VExpr) (n k i : Nat) : + (e.liftN 1 i).liftN n (k + 1 + i) = + (e.liftN n (k + i)).liftN 1 i := by + symm + simpa only [Nat.add_assoc, Nat.add_comm, Nat.add_left_comm] using + VExpr.liftN_liftN_comm e 1 n i (k + i) (by omega) + +private theorem VExpr.liftTelN_liftAt_projection (As : List VExpr) + (n k i : Nat) : + VExpr.liftTelN n (VExpr.liftTelN 1 As i) (k + 1 + i) = + VExpr.liftTelN 1 (VExpr.liftTelN n As (k + i)) i := by + induction As generalizing i with + | nil => rfl + | cons A As ih => + simp only [VExpr.liftTelN] + rw [VExpr.liftN_liftAt_projection A n k i] + congr 1 + simpa only [Nat.add_assoc] using ih (i + 1) + +private theorem VExpr.liftTelN_lift_projection (As : List VExpr) + (n k : Nat) : + VExpr.liftTelN n (VExpr.liftTelN 1 As 0) (k + 1) = + VExpr.liftTelN 1 (VExpr.liftTelN n As k) 0 := by + simpa using VExpr.liftTelN_liftAt_projection As n k 0 + +private theorem VExpr.instN_liftAt_projection + (e a : VExpr) (k i : Nat) : + (e.liftN 1 i).inst a (k + 1 + i) = + (e.inst a (k + i)).liftN 1 i := by + symm + simpa only [Nat.add_assoc, Nat.add_comm, Nat.add_left_comm] using + VExpr.liftN_instN_lo 1 e a (k + i) i (by omega) + +private theorem VExpr.instTelN_liftAt_projection (As : List VExpr) + (a : VExpr) (k i : Nat) : + VExpr.instTelN a (VExpr.liftTelN 1 As i) (k + 1 + i) = + VExpr.liftTelN 1 (VExpr.instTelN a As (k + i)) i := by + induction As generalizing i with + | nil => rfl + | cons A As ih => + simp only [VExpr.liftTelN, VExpr.instTelN] + rw [VExpr.instN_liftAt_projection A a k i] + congr 1 + simpa only [Nat.add_assoc] using ih (i + 1) + +private theorem VExpr.instTelN_lift_projection (As : List VExpr) + (a : VExpr) (k : Nat) : + VExpr.instTelN a (VExpr.liftTelN 1 As 0) (k + 1) = + VExpr.liftTelN 1 (VExpr.instTelN a As k) 0 := by + simpa using VExpr.instTelN_liftAt_projection As a k 0 + +private theorem VExpr.instN_instRevAt_lift_projection + (e : VExpr) (args : List VExpr) (a : VExpr) (i : Nat) : + ((e.liftN 1 i).instRevAt args (i + 1)).inst a i = + e.instRevAt args i := by + induction args generalizing e with + | nil => exact VExpr.inst_liftN1 e a i + | cons arg args ih => + simp only [VExpr.instRevAt] + rw [show i + 1 + args.length = args.length + 1 + i by omega, + VExpr.instN_liftAt_projection e arg args.length i] + simpa only [Nat.add_comm] using + ih (e := e.inst arg (args.length + i)) + +private theorem VExpr.instTelN_instRevAt_lift_projection + (fields : List VExpr) (args : List VExpr) (a : VExpr) + (start : Nat) : + VExpr.instTelN a + ((VExpr.liftTelN 1 fields start).zipIdx (start + 1) |>.map + fun x => x.1.instRevAt args x.2) + start = + (fields.zipIdx start |>.map + fun x => x.1.instRevAt args x.2) := by + induction fields generalizing start with + | nil => rfl + | cons field fields ih => + simp only [VExpr.liftTelN, List.zipIdx, List.map_cons, + VExpr.instTelN] + rw [VExpr.instN_instRevAt_lift_projection] + congr 1 + simpa only [Nat.add_assoc] using ih (start + 1) + +private theorem VExpr.inst_liftN_top (e a : VExpr) (n : Nat) : + (e.liftN (n + 1)).inst a n = e.liftN n := by + rw [← VExpr.liftN'_liftN' (e := e) (n1 := n) (n2 := 1) + (k1 := 0) (k2 := n) (Nat.zero_le _) (by omega)] + exact VExpr.inst_liftN (e.liftN n) a + +private theorem VExpr.instRevAt_liftN_len (args : List VExpr) + (e : VExpr) (k : Nat) : + (e.liftN (k + args.length)).instRevAt args k = e.liftN k := by + induction args with + | nil => rfl + | cons arg args ih => + simp only [List.length_cons, VExpr.instRevAt] + rw [show k + (args.length + 1) = (k + args.length) + 1 by omega, + VExpr.inst_liftN_top] + exact ih + +private theorem VExpr.instRevAt_bvar_lt_cons (args : List VExpr) + (arg : VExpr) (k i : Nat) (hi : i < k + args.length) : + (VExpr.bvar i).instRevAt (arg :: args) k = + (VExpr.bvar i).instRevAt args k := by + simp only [VExpr.instRevAt] + congr 1 + simp [VExpr.inst, VExpr.instVar, hi] + +private theorem VExpr.map_instRevAt_bvarRevRange + (args : List VExpr) (k : Nat) : + (VExpr.bvarRevRange k args.length).map + (fun e => e.instRevAt args k) = + args.map (VExpr.liftN k) := by + induction args with + | nil => rfl + | cons arg args ih => + simp only [List.length_cons, VExpr.bvarRevRange, + List.map_cons] + congr 1 + · simp only [VExpr.instRevAt] + rw [show (VExpr.bvar (k + args.length)).inst arg + (k + args.length) = arg.liftN (k + args.length) by + simp [VExpr.inst, VExpr.instVar]] + exact VExpr.instRevAt_liftN_len args arg k + · rw [← ih] + apply List.map_congr_left + intro e he + obtain ⟨i, rfl, _, hi⟩ := VExpr.mem_bvarRevRange he + exact VExpr.instRevAt_bvar_lt_cons args arg k i (by omega) + +private theorem VExpr.instRevAt_appN_projection + (f : VExpr) (es : List VExpr) (args : List VExpr) (k : Nat) : + (VExpr.appN f es).instRevAt args k = + VExpr.appN (f.instRevAt args k) + (es.map fun e => e.instRevAt args k) := by + induction args generalizing f es with + | nil => simp [VExpr.instRevAt, List.map_id'] + | cons arg args ih => + simp only [VExpr.instRevAt, VExpr.instN_appN] + rw [ih] + simp only [List.map_map, Function.comp_def] + +private theorem VExpr.map_instRevAt_closedN (args es : List VExpr) + (k : Nat) (hclosed : ∀ e ∈ es, e.ClosedN k) : + es.map (fun e => e.instRevAt args k) = es := by + induction es with + | nil => rfl + | cons e es ih => + simp only [List.map_cons] + rw [VExpr.instRevAt_closedN args (hclosed e (.head _))] + congr 1 + exact ih (fun e he => hclosed e (.tail _ he)) + +private theorem VExpr.map_instN_closedN (a : VExpr) (es : List VExpr) + (k : Nat) (hclosed : ∀ e ∈ es, e.ClosedN k) : + es.map (fun e => e.inst a k) = es := by + induction es with + | nil => rfl + | cons e es ih => + simp only [List.map_cons] + rw [(hclosed e (.head _)).instN_eq (Nat.le_refl _)] + congr 1 + exact ih (fun e he => hclosed e (.tail _ he)) + +private theorem VExpr.map_instN_liftN_top + (es : List VExpr) (a : VExpr) (n : Nat) : + (es.map (VExpr.liftN (n + 1))).map + (fun e => e.inst a n) = + es.map (VExpr.liftN n) := by + rw [List.map_map] + apply List.map_congr_left + intro e _ + exact VExpr.inst_liftN_top e a n + +private theorem VExpr.projectionMinorBody_shape + (constructorName : Name) (levels : List VLevel) + (params : List VExpr) (m : Nat) (typeFn : VExpr) : + ((VExpr.appN (.bvar m) + [VExpr.appN (.const constructorName levels) + (VExpr.bvarRevRange (m + 1) params.length ++ + VExpr.bvarRevRange 0 m)]).instRevAt params (m + 1)).inst + typeFn m = + .app (typeFn.liftN m) + (VExpr.appN (.const constructorName levels) + (params.map (VExpr.liftN m) ++ + VExpr.bvarRevRange 0 m)) := by + have hmotiveR : (VExpr.bvar m).instRevAt params (m + 1) = + .bvar m := VExpr.instRevAt_closedN params (by + exact Nat.lt_succ_self m) + have hconstR : (VExpr.const constructorName levels).instRevAt + params (m + 1) = .const constructorName levels := + VExpr.instRevAt_closedN params (by trivial) + have hfieldsR := VExpr.map_instRevAt_closedN params + (VExpr.bvarRevRange 0 m) (m + 1) + (bvarRevRange_closedN m 0 (m + 1) (by omega)) + have hmotiveI : (VExpr.bvar m).inst typeFn m = + typeFn.liftN m := by simp [VExpr.inst, VExpr.instVar] + have hconstI : (VExpr.const constructorName levels).inst typeFn m = + .const constructorName levels := by rfl + have hparamsI := VExpr.map_instN_liftN_top params typeFn m + have hfieldsI := VExpr.map_instN_closedN typeFn + (VExpr.bvarRevRange 0 m) m + (bvarRevRange_closedN m 0 m (by omega)) + rw [VExpr.instRevAt_appN_projection, hmotiveR] + simp only [List.map_singleton] + rw [VExpr.instRevAt_appN_projection, hconstR, List.map_append, + VExpr.map_instRevAt_bvarRevRange, hfieldsR] + rw [VExpr.instN_appN, hmotiveI] + simp only [List.map_singleton] + rw [VExpr.instN_appN, hconstI, List.map_append, + hparamsI, hfieldsI] + rfl + +private theorem VExpr.projectionMajorTail_shape + (familyName : Name) (levels : List VLevel) + (params : List VExpr) (typeFn : VExpr) : + (((VExpr.forallE + (VExpr.appN (.const familyName levels) + (VExpr.bvarRevRange 2 params.length)) + (.app (.appN (.bvar 2) []) (.bvar 0))).instRevAt + params 2).inst typeFn 1) = + VExpr.forallE + (VExpr.appN (.const familyName levels) + (params.map (VExpr.liftN 1))) + (.app (typeFn.liftN 2) (.bvar 0)) := by + have hconstR : (VExpr.const familyName levels).instRevAt + params 2 = .const familyName levels := + VExpr.instRevAt_closedN params (by trivial) + have hbodyR : + (VExpr.app (VExpr.appN (.bvar 2) []) (.bvar 0)).instRevAt + params 3 = + VExpr.app (VExpr.appN (.bvar 2) []) (.bvar 0) := + VExpr.instRevAt_closedN params (by + change 2 < 3 ∧ 0 < 3 + omega) + rw [VExpr.instRevAt_forallE_projection, + VExpr.instRevAt_appN_projection, hconstR, + VExpr.map_instRevAt_bvarRevRange, hbodyR] + simp only [VExpr.inst] + congr 1 + · rw [VExpr.instN_appN] + have hconstI : (VExpr.const familyName levels).inst typeFn 1 = + .const familyName levels := by rfl + rw [hconstI, VExpr.map_instN_liftN_top] + +theorem VExpr.liftN_instRevAt (e : VExpr) (as : List VExpr) + (i k n : Nat) : + (e.instRevAt as i).liftN n (k + i) = + (e.liftN n (k + i + as.length)).instRevAt + (as.map fun a => a.liftN n k) i := by + induction as generalizing e with + | nil => simp [VExpr.instRevAt] + | cons a as ih => + simp only [VExpr.instRevAt, List.map_cons] + rw [ih] + simp only [List.length_cons, List.length_map] + rw [show k + i + as.length = k + (i + as.length) by omega, + VExpr.liftN_instN_hi] + congr 3 <;> omega + +theorem VExpr.instN_instRevAt (e : VExpr) (as : List VExpr) + (i k : Nat) (a : VExpr) : + (e.instRevAt as i).inst a (k + i) = + (e.inst a (k + i + as.length)).instRevAt + (as.map fun arg => arg.inst a k) i := by + induction as generalizing e with + | nil => simp [VExpr.instRevAt] + | cons arg as ih => + simp only [VExpr.instRevAt, List.map_cons] + rw [ih] + simp only [List.length_cons, List.length_map] + rw [show k + i + as.length = k + (i + as.length) by omega, + VExpr.inst_inst_hi] + congr 3 <;> omega + +/-- A telescope whose entries have the exact retained sort levels. -/ +inductive VEnv.OnSortTel (env : VEnv) (U : Nat) : + List VExpr → List VExpr → List VLevel → Prop where + | nil : OnSortTel env U Γ [] [] + | cons : + env.HasType U Γ A (.sort u) → + OnSortTel env U (A :: Γ) As us → + OnSortTel env U Γ (A :: As) (u :: us) + +private theorem onCtx_levelWFProjection {env : VEnv} {U : Nat} : + ∀ {Γ : List VExpr}, OnCtx Γ (env.IsType U) → + OnCtx Γ fun _ A => A.LevelWF U + | [], _ => trivial + | _ :: _, ⟨hΓ, ⟨_, hA⟩⟩ => + let hΓ' := onCtx_levelWFProjection hΓ + ⟨hΓ', (hA.levelWF hΓ').1⟩ + +/-- Every retained sort selected from a checked sort telescope is a +well-formed universe at the ambient universe bound. -/ +theorem VEnv.OnSortTel.sortWF {env : VEnv} {U : Nat} + : ∀ {Γ : List VExpr} {As : List VExpr} {us : List VLevel}, + OnCtx Γ (env.IsType U) → env.OnSortTel U Γ As us → + ∀ {i : Nat} {u : VLevel}, us[i]? = some u → u.WF U + | _, [], [], _, .nil, _, _, h => by simp at h + | _, _ :: _, _ :: _, hΓ, .cons hA hT, 0, _, h => by + injection h with h + subst h + exact (hA.levelWF (onCtx_levelWFProjection hΓ)).2.2 + | Γ, A :: As, u₀ :: us, hΓ, .cons hA hT, i + 1, u, h => by + exact VEnv.OnSortTel.sortWF (env := env) (U := U) + (Γ := A :: Γ) (As := As) (us := us) + ⟨hΓ, ⟨u₀, hA⟩⟩ hT (by simpa using h) + +private theorem VEnv.OnTel.monoProjection {env env' : VEnv} + (henv : env ≤ env') (H : env.OnTel U Γ As) : env'.OnTel U Γ As := by + induction As generalizing Γ with + | nil => trivial + | cons _ _ ih => + exact ⟨H.1.mono henv, ih H.2⟩ + +theorem VEnv.OnSortTel.mono {env env' : VEnv} (henv : env ≤ env') + (H : env.OnSortTel U Γ As us) : env'.OnSortTel U Γ As us := by + induction H with + | nil => exact .nil + | cons hA _ ih => exact .cons (hA.mono henv) ih + +/-- Forget the retained sort labels, preserving the underlying telescope +well-formedness judgment. -/ +theorem VEnv.OnSortTel.toOnTel {env : VEnv} : + ∀ {U : Nat} {Γ As : List VExpr} {us : List VLevel}, + env.OnSortTel U Γ As us → env.OnTel U Γ As + | _, _, [], [], .nil => trivial + | _, _, _ :: _, _ :: _, .cons hA hT => + ⟨⟨_, hA⟩, VEnv.OnSortTel.toOnTel hT⟩ + +theorem VEnv.OnSortTel.instL {env : VEnv} {U U' : Nat} + (hlevels : ∀ level ∈ levels, level.WF U') : + ∀ {Γ As us}, env.OnSortTel U Γ As us → + env.OnSortTel U' (Γ.map (VExpr.instL levels)) + (As.map (VExpr.instL levels)) + (us.map (VLevel.inst levels)) + | _, [], [], .nil => .nil + | _, _ :: _, _ :: _, .cons hA hT => + .cons (hA.instL hlevels) (VEnv.OnSortTel.instL hlevels hT) + +theorem VEnv.OnSortTel.weakN {env : VEnv} (henv : env.Ordered) + {U n k : Nat} {Γ Γ' : List VExpr} (W : Ctx.LiftN n k Γ Γ') : + ∀ {As us}, env.OnSortTel U Γ As us → + env.OnSortTel U Γ' (VExpr.liftTelN n As k) us + | [], [], .nil => .nil + | _ :: _, _ :: _, .cons hA hT => + .cons (hA.weakN henv W) + (VEnv.OnSortTel.weakN henv W.succ hT) + +private theorem VEnv.OnSortTel.instN {env : VEnv} (henv : env.Ordered) + {U : Nat} {Γ₀ : List VExpr} {e₀ A₀ : VExpr} + (h₀ : env.HasType U Γ₀ e₀ A₀) : + ∀ {As : List VExpr} {us : List VLevel} {k : Nat} + {Γ Γ' : List VExpr}, + Ctx.InstN Γ₀ e₀ A₀ k Γ Γ' → + env.OnSortTel U Γ As us → + env.OnSortTel U Γ' (VExpr.instTelN e₀ As k) us + | [], [], _, _, _, _, .nil => .nil + | _ :: _, _ :: _, _, _, _, W, .cons hA hT => + .cons (hA.instN henv W h₀) + (VEnv.OnSortTel.instN henv h₀ W.succ hT) + +private theorem VExpr.instRevAt_instTelN_cons + (fields : List VExpr) (a : VExpr) (as : List VExpr) : + ((VExpr.instTelN a fields as.length).zipIdx.map fun (field, i) => + VExpr.instRevAt field as i) = + (fields.zipIdx.map fun (field, i) => + VExpr.instRevAt field (a :: as) i) := by + suffices ∀ (start k : Nat), k = as.length + start → + ((VExpr.instTelN a fields k).zipIdx start |>.map + fun (field, i) => VExpr.instRevAt field as i) = + (fields.zipIdx start |>.map fun (field, i) => + VExpr.instRevAt field (a :: as) i) by + simpa using this 0 as.length (by omega) + intro start k hk + induction fields generalizing start k with + | nil => rfl + | cons field fields ih => + simp only [VExpr.instTelN, List.zipIdx, List.map_cons, + VExpr.instRevAt] + rw [hk] + congr 1 + · congr 2 <;> omega + · exact ih (start + 1) (as.length + start + 1) (by omega) + +theorem VExpr.instRevAt_map_instL_zipIdx + (fields : List VExpr) (levels : List VLevel) + (params : List VExpr) (start : Nat := 0) : + ((fields.map (VExpr.instL levels)).zipIdx start |>.map + fun (field, i) => VExpr.instRevAt field params i) = + (fields.zipIdx start |>.map fun (field, i) => + VExpr.instRevAt (field.instL levels) params i) := by + induction fields generalizing start with + | nil => rfl + | cons field fields ih => + simp only [List.map_cons, List.zipIdx] + congr 1 + exact ih (start + 1) + +private theorem VEnv.OnSortTel.instRevParams {env : VEnv} + (henv : env.Ordered) {U : Nat} : + ∀ {Γ params args fields sorts resultLevel}, + env.SpineWF U Γ (VExpr.forallN params (.sort resultLevel)) + args (.sort resultLevel) → + args.length = params.length → + env.OnSortTel U (params.reverse ++ Γ) fields sorts → + env.OnSortTel U Γ + (fields.zipIdx.map fun (field, i) => + VExpr.instRevAt field args i) sorts + | _, [], [], fields, sorts, _, hspine, _, hfields => by + simpa [VExpr.instRevAt] using hfields + | _, [], _ :: _, _, _, _, _, hlen, _ => by simp at hlen + | Γ, param :: params, arg :: args, fields, sorts, resultLevel, + .cons harg hrest, hlen, hfields => by + have hparams : args.length = params.length := by simpa using hlen + have W := Ctx.InstN.consTel (Γ₀ := Γ) (e₀ := arg) + (A₀ := param) params (.zero) + have hfields' : env.OnSortTel U + ((VExpr.instTelN arg params 0).reverse ++ Γ) + (VExpr.instTelN arg fields params.length) sorts := by + apply VEnv.OnSortTel.instN henv harg W + simpa [List.append_assoc] using hfields + have hrest' : env.SpineWF U Γ + (VExpr.forallN (VExpr.instTelN arg params 0) + (.sort resultLevel)) args (.sort resultLevel) := by + simpa [VExpr.instN_forallN, VExpr.inst] using hrest + have hout := VEnv.OnSortTel.instRevParams henv + hrest' (by simpa [VExpr.instTelN_length] using hparams) hfields' + rw [← hparams, VExpr.instRevAt_instTelN_cons] at hout + exact hout + +/-- Extend a well-formed ambient context by a well-formed telescope. -/ +theorem VEnv.OnTel.toOnCtx {env : VEnv} {U : Nat} : + ∀ {As Γ}, env.OnTel U Γ As → OnCtx Γ (env.IsType U) → + OnCtx (As.reverse ++ Γ) (env.IsType U) + | [], _, _, hΓ => by simpa using hΓ + | A :: As, Γ, ⟨hA, hAs⟩, hΓ => by + simpa [List.append_assoc] using + VEnv.OnTel.toOnCtx hAs (Γ := A :: Γ) ⟨hΓ, hA⟩ + +private theorem VEnv.OnSortTel.closedAt {env : VEnv} {U : Nat} + (henv : env.Ordered) : + ∀ {As us Γ}, env.OnSortTel U Γ As us → CtxClosed Γ → + ∀ {i : Nat} {field : VExpr}, As[i]? = some field → + field.ClosedN (Γ.length + i) + | _, _, _, .nil, _, i, _, h => by simp at h + | _ :: _, _ :: _, Γ, .cons hA hAs, hΓ, 0, _, h => by + simp only [List.getElem?_cons_zero] at h + cases h + simpa using hA.closedN henv hΓ + | A :: As, _ :: _, Γ, .cons hA hAs, hΓ, i + 1, field, h => by + simp only [List.getElem?_cons_succ] at h + have hclosed : A.ClosedN Γ.length := hA.closedN henv hΓ + simpa [Nat.add_assoc, Nat.add_comm, Nat.add_left_comm] using + VEnv.OnSortTel.closedAt henv hAs ⟨hΓ, hclosed⟩ h + +private theorem VEnv.OnTel.liftTelN_eq {env : VEnv} {U : Nat} + (henv : env.Ordered) : + ∀ {As Γ}, env.OnTel U Γ As → CtxClosed Γ → ∀ n, + VExpr.liftTelN n As Γ.length = As + | [], _, _, _, _ => rfl + | A :: As, Γ, ⟨hA, hAs⟩, hΓ, n => by + obtain ⟨_, hA⟩ := hA + have hclosed : A.ClosedN Γ.length := hA.closedN henv hΓ + simp only [VExpr.liftTelN, hclosed.liftN_eq (Nat.le_refl _)] + simpa using VEnv.OnTel.liftTelN_eq henv hAs ⟨hΓ, hclosed⟩ n + +private theorem VEnv.OnSortTel.liftTelN_eq {env : VEnv} {U : Nat} + (henv : env.Ordered) : + ∀ {As us Γ}, env.OnSortTel U Γ As us → CtxClosed Γ → ∀ n, + VExpr.liftTelN n As Γ.length = As + | [], [], _, .nil, _, _ => rfl + | A :: As, _ :: us, Γ, .cons hA hAs, hΓ, n => by + have hclosed : A.ClosedN Γ.length := hA.closedN henv hΓ + simp only [VExpr.liftTelN, hclosed.liftN_eq (Nat.le_refl _)] + simpa using VEnv.OnSortTel.liftTelN_eq henv hAs ⟨hΓ, hclosed⟩ n + +/-- The checked, generated description of a nonrecursive structure. + +`generation` supplies the exact family, constructor, recursor, and iota rule +artifacts. The shape fields restrict that general one-family artifact to the +kernel class on which `.proj` is meaningful: no indices, exactly one +constructor, and no recursive constructor arguments. `fieldSorts` records +the motive universe required by each projection; `WF` below ties every entry +to the corresponding dependent constructor field type. -/ +structure VStructureView where + source : VInductDecl + generation : source.GenerationChecked + constructor : NormalizedCtor + constructor_eq : generation.block.ctorPairs = [constructor] + raw_indices_eq : generation.block.rawIndices = [] + checked_indices_eq : generation.block.checked.indices = [] + recursive_eq : constructor.view.recursive = [] + fieldSorts : List VLevel + fieldSorts_length : + fieldSorts.length = (constructor.rawFields source.nparams).length + +namespace VStructureView + +abbrev name (view : VStructureView) : Name := + view.generation.block.sourceType.name + +abbrev constructorName (view : VStructureView) : Name := + view.constructor.raw.name + +def recursorName (view : VStructureView) : Name := + .str view.name "rec" + +abbrev uvars (view : VStructureView) : Nat := view.source.uvars + +abbrev nparams (view : VStructureView) : Nat := view.source.nparams + +abbrev familyType (view : VStructureView) : VExpr := + view.generation.block.sourceType.type + +def constructorParams (view : VStructureView) : List VExpr := + VExpr.telN view.nparams view.constructor.raw.type + +def fields (view : VStructureView) : List VExpr := + view.constructor.rawFields view.nparams + +/-- The instantiated structure type `S.{levels} params`. -/ +def structureType (view : VStructureView) + (levels : List VLevel) (params : List VExpr) : VExpr := + VExpr.appN (.const view.name levels) params + +/-- Specialize declaration universes and constructor parameters, retaining +the preceding field binders of each dependent field. -/ +def specializedFields (view : VStructureView) + (levels : List VLevel) (params : List VExpr) : List VExpr := + view.fields.zipIdx.map fun (field, i) => + VExpr.instRevAt (field.instL levels) params i + +private theorem specializedFieldsAux_liftN + (rawFields : List VExpr) (levels : List VLevel) + (params : List VExpr) (p start n k : Nat) + (hparams : params.length = p) + (hclosed : ∀ (j : Nat) (field : VExpr), + rawFields[j]? = some field → + field.ClosedN (p + start + j)) : + (rawFields.zipIdx start |>.map fun (field, i) => + VExpr.instRevAt (field.instL levels) + (params.map fun param => param.liftN n k) i) = + VExpr.liftTelN n + (rawFields.zipIdx start |>.map fun (field, i) => + VExpr.instRevAt (field.instL levels) params i) + (k + start) := by + induction rawFields generalizing start with + | nil => rfl + | cons field rawFields ih => + have hfield : (field.instL levels).ClosedN (p + start + 0) := + VExpr.ClosedN.instL (ls := levels) (hclosed 0 field (by rfl)) + have hrawLift : + (field.instL levels).liftN n + (k + start + params.length) = field.instL levels := + hfield.liftN_eq (by rw [hparams]; omega) + have hhead := VExpr.liftN_instRevAt + (field.instL levels) params start k n + rw [hrawLift] at hhead + have htail := ih (start := start + 1) + (fun j tailField htailField => by + have := hclosed (j + 1) tailField (by simpa using htailField) + simpa only [Nat.add_assoc, Nat.add_left_comm, + Nat.add_comm] using this) + simp only [List.zipIdx, List.map_cons, VExpr.liftTelN] + rw [← hhead] + exact congrArg + (List.cons (VExpr.liftN n + ((field.instL levels).instRevAt params start) (k + start))) + (by simpa only [Nat.add_assoc] using htail) + +private theorem specializedFieldsAux_instN + (rawFields : List VExpr) (levels : List VLevel) + (params : List VExpr) (p start k : Nat) (a : VExpr) + (hparams : params.length = p) + (hclosed : ∀ (j : Nat) (field : VExpr), + rawFields[j]? = some field → + field.ClosedN (p + start + j)) : + (rawFields.zipIdx start |>.map fun (field, i) => + VExpr.instRevAt (field.instL levels) + (params.map fun param => param.inst a k) i) = + VExpr.instTelN a + (rawFields.zipIdx start |>.map fun (field, i) => + VExpr.instRevAt (field.instL levels) params i) + (k + start) := by + induction rawFields generalizing start with + | nil => rfl + | cons field rawFields ih => + have hfield : (field.instL levels).ClosedN (p + start + 0) := + VExpr.ClosedN.instL (ls := levels) (hclosed 0 field (by rfl)) + have hrawInst : + (field.instL levels).inst a + (k + start + params.length) = field.instL levels := + hfield.instN_eq (by rw [hparams]; omega) + have hhead := VExpr.instN_instRevAt + (field.instL levels) params start k a + rw [hrawInst] at hhead + have htail := ih (start := start + 1) + (fun j tailField htailField => by + have := hclosed (j + 1) tailField (by simpa using htailField) + simpa only [Nat.add_assoc, Nat.add_left_comm, + Nat.add_comm] using this) + simp only [List.zipIdx, List.map_cons, VExpr.instTelN] + rw [← hhead] + exact congrArg + (List.cons (VExpr.inst + ((field.instL levels).instRevAt params start) a (k + start))) + (by simpa only [Nat.add_assoc] using htail) + +/-- Universe arguments supplied to the generated recursor for a projection +whose result type inhabits `Sort fieldSort`. -/ +def projectionLevels (view : VStructureView) + (fieldSort : VLevel) (levels : List VLevel) : List VLevel := + match view.generation.elimination with + | .large => fieldSort :: levels + | .small => levels + +/-- The two expressions generated for one field. `typeFn` is the dependent +field type as a function of the structure value; `projector` is a recursor +program implementing the projection. -/ +structure ProjectionCode where + fieldSort : VLevel + typeFn : VExpr + minor : VExpr + projector : VExpr + +@[ext] theorem ProjectionCode.ext {left right : ProjectionCode} + (fieldSort : left.fieldSort = right.fieldSort) + (typeFn : left.typeFn = right.typeFn) + (minor : left.minor = right.minor) + (projector : left.projector = right.projector) : left = right := by + cases left + cases right + simp_all + +def ProjectionCode.liftN (code : ProjectionCode) + (n k : Nat) : ProjectionCode where + fieldSort := code.fieldSort + typeFn := code.typeFn.liftN n k + minor := code.minor.liftN n k + projector := code.projector.liftN n k + +def ProjectionCode.instN (code : ProjectionCode) + (a : VExpr) (k : Nat) : ProjectionCode where + fieldSort := code.fieldSort + typeFn := code.typeFn.inst a k + minor := code.minor.inst a k + projector := code.projector.inst a k + +def ProjectionCode.instL (code : ProjectionCode) + (ls : List VLevel) : ProjectionCode where + fieldSort := code.fieldSort.inst ls + typeFn := code.typeFn.instL ls + minor := code.minor.instL ls + projector := code.projector.instL ls + +/-- The constructor-headed major used by a projection minor after all fields +have been introduced. -/ +def projectionConstructorApp (view : VStructureView) + (levels : List VLevel) (params fields : List VExpr) : VExpr := + VExpr.appN (.const view.constructorName levels) + (params.map (VExpr.liftN fields.length) ++ + VExpr.bvarRevRange 0 fields.length) + +/-- The one-constructor, nonrecursive minor premise expected by the generated +recursor after parameters and a projection motive have been supplied. -/ +def projectionMinorType (view : VStructureView) + (levels : List VLevel) (params fields : List VExpr) + (typeFn : VExpr) : VExpr := + VExpr.forallN fields + (.app (typeFn.liftN fields.length) + (view.projectionConstructorApp levels params fields)) + +@[simp] theorem projectionLevels_instL (view : VStructureView) + (fieldSort : VLevel) (levels ls : List VLevel) : + (view.projectionLevels fieldSort levels).map (VLevel.inst ls) = + view.projectionLevels (fieldSort.inst ls) + (levels.map (VLevel.inst ls)) := by + unfold projectionLevels + split <;> rfl + +@[simp] theorem structureType_instL (view : VStructureView) + (levels : List VLevel) (params : List VExpr) (ls : List VLevel) : + (view.structureType levels params).instL ls = + view.structureType (levels.map (VLevel.inst ls)) + (params.map (VExpr.instL ls)) := by + simp [structureType, VExpr.instL_appN, VExpr.instL] + +@[simp] theorem structureType_liftN (view : VStructureView) + (levels : List VLevel) (params : List VExpr) (n k : Nat) : + (view.structureType levels params).liftN n k = + view.structureType levels + (params.map fun param => param.liftN n k) := by + simp [structureType, VExpr.liftN_appN, VExpr.liftN] + +@[simp] theorem structureType_instN (view : VStructureView) + (levels : List VLevel) (params : List VExpr) (a : VExpr) (k : Nat) : + (view.structureType levels params).inst a k = + view.structureType levels + (params.map fun param => param.inst a k) := by + simp [structureType, VExpr.instN_appN, VExpr.inst] + +@[simp] theorem specializedFields_instL (view : VStructureView) + (levels : List VLevel) (params : List VExpr) (ls : List VLevel) : + (view.specializedFields levels params).map (VExpr.instL ls) = + view.specializedFields (levels.map (VLevel.inst ls)) + (params.map (VExpr.instL ls)) := by + simp [specializedFields, VExpr.instL_instRevAt, VExpr.instL_instL, Function.comp_def] + +private def projectionCode (view : VStructureView) + (levels : List VLevel) (params allFields : List VExpr) + (structType field : VExpr) (fieldSort : VLevel) (i : Nat) + (previous : List ProjectionCode) : ProjectionCode := + let previousAtMajor := previous.map fun code => + .app code.projector.lift (.bvar 0) + let motiveBody := VExpr.instRevAt + (field.liftN 1 i) previousAtMajor 0 + let typeFn := .lam structType motiveBody + let minor := VExpr.lamN allFields + (.bvar (allFields.length - 1 - i)) + let recursor := .const view.recursorName + (view.projectionLevels fieldSort levels) + let projector := .lam structType <| VExpr.appN recursor <| + params.map (VExpr.liftN 1) ++ + [typeFn.lift, minor.lift, .bvar 0] + { fieldSort, typeFn, minor, projector } + +private theorem projectionCode_liftN (view : VStructureView) + (levels : List VLevel) (params allFields : List VExpr) + (structType field : VExpr) (fieldSort : VLevel) (i : Nat) + (previous : List ProjectionCode) (n k : Nat) + (hprevious : previous.length = i) + (hi : i < allFields.length) : + (projectionCode view levels params allFields structType field + fieldSort i previous).liftN n k = + projectionCode view levels + (params.map fun param => param.liftN n k) + (VExpr.liftTelN n allFields k) + (structType.liftN n k) (field.liftN n (k + i)) fieldSort i + (previous.map fun code => code.liftN n k) := by + have hfieldLift : + (field.liftN 1 i).liftN n (k + 1 + i) = + (field.liftN n (k + i)).liftN 1 i := + VExpr.liftN_liftAt_projection field n k i + have hpreviousLift : + (previous.map fun code => + VExpr.app code.projector.lift (.bvar 0)).map + (fun (e : VExpr) => e.liftN n (k + 1)) = + (previous.map fun code => code.liftN n k).map fun code => + VExpr.app code.projector.lift (.bvar 0) := by + simp [ProjectionCode.liftN, VExpr.liftN, + VExpr.liftN_lift_projection, List.map_map, + Function.comp_def] + have hmotive : + ((field.liftN 1 i).instRevAt + (previous.map fun code => + VExpr.app code.projector.lift (.bvar 0)) 0).liftN n (k + 1) = + ((field.liftN n (k + i)).liftN 1 i).instRevAt + ((previous.map fun code => code.liftN n k).map fun code => + VExpr.app code.projector.lift (.bvar 0)) 0 := by + rw [VExpr.liftN_instRevAt] + rw [List.length_map, hprevious, hfieldLift, hpreviousLift] + have hminorBody : + VExpr.liftN n (.bvar (allFields.length - 1 - i)) + (k + allFields.length) = + .bvar (allFields.length - 1 - i) := by + simp only [VExpr.liftN] + rw [liftVar_lt] + omega + have hminorVar : + liftVar n (allFields.length - 1 - i) + (k + allFields.length) = allFields.length - 1 - i := by + rw [liftVar_lt] + omega + have hminorNestedVar : + liftVar n (liftVar 1 (allFields.length - 1 - i) + allFields.length) (k + 1 + allFields.length) = + liftVar 1 (allFields.length - 1 - i) allFields.length := by + have hinner : liftVar 1 (allFields.length - 1 - i) + allFields.length = allFields.length - 1 - i := + liftVar_lt (by omega) + rw [hinner, liftVar_lt (by omega)] + have hmotiveLift : + (((field.liftN 1 i).instRevAt + (previous.map fun code => + VExpr.app code.projector.lift (.bvar 0)) 0).liftN 1 1).liftN + n (k + 1 + 1) = + (((field.liftN n (k + i)).liftN 1 i).instRevAt + ((previous.map fun code => code.liftN n k).map fun code => + VExpr.app code.projector.lift (.bvar 0)) 0).liftN 1 1 := by + rw [VExpr.liftN_liftAt_projection] + exact congrArg (fun e => e.liftN 1 1) hmotive + apply ProjectionCode.ext + · rfl + · simp [projectionCode, ProjectionCode.liftN, VExpr.liftN, + hmotive] + · simp [projectionCode, ProjectionCode.liftN, + VExpr.liftN_lamN_projection, VExpr.liftTelN_length, + hminorBody] + · simp [projectionCode, ProjectionCode.liftN, VExpr.liftN, + VExpr.liftN_appN, VExpr.liftN_lamN_projection, + VExpr.liftTelN_length, VExpr.liftN_lift_projection, + VExpr.liftTelN_lift_projection, List.map_append, + List.map_map, Function.comp_def, hmotive, hmotiveLift, + hminorNestedVar] + +private theorem projectionCode_instN (view : VStructureView) + (levels : List VLevel) (params allFields : List VExpr) + (structType field : VExpr) (fieldSort : VLevel) (i : Nat) + (previous : List ProjectionCode) (a : VExpr) (k : Nat) + (hprevious : previous.length = i) + (hi : i < allFields.length) : + (projectionCode view levels params allFields structType field + fieldSort i previous).instN a k = + projectionCode view levels + (params.map fun param => param.inst a k) + (VExpr.instTelN a allFields k) + (structType.inst a k) (field.inst a (k + i)) fieldSort i + (previous.map fun code => code.instN a k) := by + have hfieldInst : + (field.liftN 1 i).inst a (k + 1 + i) = + (field.inst a (k + i)).liftN 1 i := + VExpr.instN_liftAt_projection field a k i + have hpreviousInst : + (previous.map fun code => + VExpr.app code.projector.lift (.bvar 0)).map + (fun (e : VExpr) => e.inst a (k + 1)) = + (previous.map fun code => code.instN a k).map fun code => + VExpr.app code.projector.lift (.bvar 0) := by + simp [ProjectionCode.instN, VExpr.inst, VExpr.instVar, + ← VExpr.lift_instN_lo, List.map_map, Function.comp_def] + have hmotive : + ((field.liftN 1 i).instRevAt + (previous.map fun code => + VExpr.app code.projector.lift (.bvar 0)) 0).inst a (k + 1) = + ((field.inst a (k + i)).liftN 1 i).instRevAt + ((previous.map fun code => code.instN a k).map fun code => + VExpr.app code.projector.lift (.bvar 0)) 0 := by + rw [VExpr.instN_instRevAt] + rw [List.length_map, hprevious, hfieldInst, hpreviousInst] + have hminorVar : + VExpr.instVar (allFields.length - 1 - i) a + (k + allFields.length) = + .bvar (allFields.length - 1 - i) := by + simp [VExpr.instVar, show + allFields.length - 1 - i < k + allFields.length by omega] + apply ProjectionCode.ext + · rfl + · simp [projectionCode, ProjectionCode.instN, VExpr.inst, hmotive] + · simp [projectionCode, ProjectionCode.instN, VExpr.inst, + VExpr.instN_lamN_projection, VExpr.instTelN_length, + hminorVar] + · simp [projectionCode, ProjectionCode.instN, VExpr.inst, VExpr.instN_appN, + VExpr.instN_lamN_projection, VExpr.instTelN_length, ← VExpr.lift_instN_lo, List.map_append, + List.map_map, Function.comp_def, hmotive, hminorVar] + +private def projectionCodes.go (view : VStructureView) + (levels : List VLevel) (params : List VExpr) + (allFields : List VExpr) (structType : VExpr) : + List VExpr → List VLevel → Nat → List ProjectionCode → + List ProjectionCode + | field :: fields, fieldSort :: fieldSorts, i, previous => + let code := projectionCode view levels params allFields structType + field fieldSort i previous + code :: projectionCodes.go view levels params allFields structType + fields fieldSorts (i + 1) (previous ++ [code]) + | _, _, _, _ => [] + +private theorem projectionCodes.go_instN (view : VStructureView) + (levels : List VLevel) (params allFields : List VExpr) + (structType : VExpr) (fields : List VExpr) + (fieldSorts : List VLevel) (i : Nat) + (previous : List ProjectionCode) (a : VExpr) (k : Nat) + (hprevious : previous.length = i) + (hfields : i + fields.length = allFields.length) : + (projectionCodes.go view levels params allFields structType + fields fieldSorts i previous).map + (fun code => code.instN a k) = + projectionCodes.go view levels + (params.map fun param => param.inst a k) + (VExpr.instTelN a allFields k) (structType.inst a k) + (VExpr.instTelN a fields (k + i)) fieldSorts i + (previous.map fun code => code.instN a k) := by + induction fields generalizing fieldSorts i previous with + | nil => + cases fieldSorts <;> simp [projectionCodes.go, VExpr.instTelN] + | cons field fields ih => + cases fieldSorts with + | nil => simp [projectionCodes.go] + | cons fieldSort fieldSorts => + have hi : i < allFields.length := by + simp only [List.length_cons] at hfields + omega + have hcode := projectionCode_instN view levels params allFields + structType field fieldSort i previous a k hprevious hi + simp only [projectionCodes.go, List.map_cons, + VExpr.instTelN] + rw [hcode] + congr 1 + have hprevious' : + (previous ++ [projectionCode view levels params allFields + structType field fieldSort i previous]).length = i + 1 := by + simp [hprevious] + have hfields' : i + 1 + fields.length = allFields.length := by + simp only [List.length_cons] at hfields + omega + simpa only [List.map_append, List.map_singleton, + hcode, Nat.add_assoc] using + ih fieldSorts (i + 1) + (previous ++ [projectionCode view levels params allFields + structType field fieldSort i previous]) + hprevious' hfields' + +private theorem projectionCode_instL (view : VStructureView) + (levels : List VLevel) (params allFields : List VExpr) + (structType field : VExpr) (fieldSort : VLevel) (i : Nat) + (previous : List ProjectionCode) (ls : List VLevel) : + (projectionCode view levels params allFields structType field + fieldSort i previous).instL ls = + projectionCode view + (levels.map (VLevel.inst ls)) + (params.map (VExpr.instL ls)) + (allFields.map (VExpr.instL ls)) + (structType.instL ls) (field.instL ls) (fieldSort.inst ls) i + (previous.map fun code => code.instL ls) := by + simp [projectionCode, ProjectionCode.instL, VExpr.instL, + VExpr.instL_instRevAt, VExpr.instL_lamN_projection, + VExpr.instL_appN, VExpr.instL_liftN, + List.map_append, List.map_map, Function.comp_def] + +private theorem projectionCodes.go_instL (view : VStructureView) + (levels : List VLevel) (params allFields : List VExpr) + (structType : VExpr) (fields : List VExpr) + (fieldSorts : List VLevel) (i : Nat) + (previous : List ProjectionCode) (ls : List VLevel) : + (projectionCodes.go view levels params allFields structType + fields fieldSorts i previous).map + (fun code => code.instL ls) = + projectionCodes.go view + (levels.map (VLevel.inst ls)) + (params.map (VExpr.instL ls)) + (allFields.map (VExpr.instL ls)) + (structType.instL ls) + (fields.map (VExpr.instL ls)) + (fieldSorts.map (VLevel.inst ls)) i + (previous.map fun code => code.instL ls) := by + induction fields generalizing fieldSorts i previous with + | nil => simp [projectionCodes.go] + | cons field fields ih => + cases fieldSorts with + | nil => simp [projectionCodes.go] + | cons fieldSort fieldSorts => + simp only [projectionCodes.go, List.map_cons, + projectionCode_instL] + congr 1 + simpa only [List.map_append, List.map_singleton, + projectionCode_instL] using + ih fieldSorts (i + 1) + (previous ++ [projectionCode view levels params allFields + structType field fieldSort i previous]) + +private theorem projectionCodes.go_liftN (view : VStructureView) + (levels : List VLevel) (params allFields : List VExpr) + (structType : VExpr) (fields : List VExpr) + (fieldSorts : List VLevel) (i : Nat) + (previous : List ProjectionCode) (n k : Nat) + (hprevious : previous.length = i) + (hfields : i + fields.length = allFields.length) : + (projectionCodes.go view levels params allFields structType + fields fieldSorts i previous).map + (fun code => code.liftN n k) = + projectionCodes.go view levels + (params.map fun param => param.liftN n k) + (VExpr.liftTelN n allFields k) (structType.liftN n k) + (VExpr.liftTelN n fields (k + i)) fieldSorts i + (previous.map fun code => code.liftN n k) := by + induction fields generalizing fieldSorts i previous with + | nil => + cases fieldSorts <;> simp [projectionCodes.go, VExpr.liftTelN] + | cons field fields ih => + cases fieldSorts with + | nil => simp [projectionCodes.go] + | cons fieldSort fieldSorts => + have hi : i < allFields.length := by + simp only [List.length_cons] at hfields + omega + have hcode := projectionCode_liftN view levels params allFields + structType field fieldSort i previous n k hprevious hi + simp only [projectionCodes.go, List.map_cons, + VExpr.liftTelN] + rw [hcode] + congr 1 + have hprevious' : + (previous ++ [projectionCode view levels params allFields + structType field fieldSort i previous]).length = i + 1 := by + simp [hprevious] + have hfields' : i + 1 + fields.length = allFields.length := by + simp only [List.length_cons] at hfields + omega + simpa only [List.map_append, List.map_singleton, + hcode, Nat.add_assoc] using + ih fieldSorts (i + 1) + (previous ++ [projectionCode view levels params allFields + structType field fieldSort i previous]) + hprevious' hfields' + +/-- All field projections, in constructor-field order. -/ +def projectionCodes (view : VStructureView) + (levels : List VLevel) (params : List VExpr) : List ProjectionCode := + let fields := view.specializedFields levels params + projectionCodes.go view levels params fields + (view.structureType levels params) fields + (view.fieldSorts.map (VLevel.inst levels)) 0 [] + +private theorem projectionCodes.go_length (view : VStructureView) + (levels : List VLevel) (params allFields : List VExpr) + (structType : VExpr) : + ∀ (fields : List VExpr) (fieldSorts : List VLevel) + (i : Nat) (previous : List ProjectionCode), + fields.length = fieldSorts.length → + (projectionCodes.go view levels params allFields structType + fields fieldSorts i previous).length = fields.length + | [], [], _, _, _ => rfl + | [], _ :: _, _, _, h => by simp at h + | _ :: _, [], _, _, h => by simp at h + | field :: fields, fieldSort :: fieldSorts, i, previous, h => by + simp only [List.length_cons] at h ⊢ + simp only [projectionCodes.go, List.length_cons] + exact congrArg Nat.succ <| + projectionCodes.go_length view levels params allFields structType + fields fieldSorts (i + 1) + (previous ++ [projectionCode view levels params allFields + structType field fieldSort i previous]) (Nat.succ.inj h) + +@[simp] theorem projectionCodes_length (view : VStructureView) + (levels : List VLevel) (params : List VExpr) : + (view.projectionCodes levels params).length = + (view.specializedFields levels params).length := by + apply projectionCodes.go_length + simp [VStructureView.specializedFields, VStructureView.fields, + view.fieldSorts_length] + +/-- Semantic arguments substituted while walking to a later dependent +projection field. -/ +def projectionArgs (view : VStructureView) (levels : List VLevel) + (params : List VExpr) (count : Nat) (major : VExpr) : List VExpr := + (view.projectionCodes levels params).take count |>.map fun code => + .app code.projector major + +/-- Rebuild a structure value from all of its canonical generated +projections. This is syntax only: `ProgramsWF.projectionArgsSpine` below +supplies the rule-independent typing evidence, while any equality between +this term and `major` remains an explicit definitional-equality capability. -/ +def etaRebuild (view : VStructureView) (levels : List VLevel) + (params : List VExpr) (major : VExpr) : VExpr := + VExpr.appN (.const view.constructorName levels) + (params ++ view.projectionArgs levels params + (view.specializedFields levels params).length major) + +@[simp] theorem projectionArgs_length (view : VStructureView) + (levels : List VLevel) (params : List VExpr) (count : Nat) + (major : VExpr) (hcount : count ≤ + (view.projectionCodes levels params).length) : + (view.projectionArgs levels params count major).length = count := by + simp only [projectionArgs, List.length_map, List.length_take] + exact Nat.min_eq_left hcount + +theorem projectionArgs_succ (view : VStructureView) + (levels : List VLevel) (params : List VExpr) (count : Nat) + (major : VExpr) {code : ProjectionCode} + (hcode : (view.projectionCodes levels params)[count]? = some code) : + view.projectionArgs levels params (count + 1) major = + view.projectionArgs levels params count major ++ + [.app code.projector major] := by + simp only [projectionArgs, List.take_add_one, hcode, Option.toList_some, + List.map_append, List.map_singleton] + +private theorem projectionCodes.go_get?_typeFn (view : VStructureView) + (levels : List VLevel) (params allFields : List VExpr) + (structType : VExpr) : + ∀ {fields : List VExpr} {fieldSorts : List VLevel} + {i : Nat} {previous : List ProjectionCode} {j : Nat} + {code : ProjectionCode}, + (projectionCodes.go view levels params allFields structType + fields fieldSorts i previous)[j]? = some code → + ∃ field, + fields[j]? = some field ∧ + code.typeFn = .lam structType + ((field.liftN 1 (i + j)).instRevAt + ((previous ++ + (projectionCodes.go view levels params allFields structType + fields fieldSorts i previous).take j).map fun prior => + .app prior.projector.lift (.bvar 0)) 0) := by + intro fields + induction fields with + | nil => + intro fieldSorts i previous j code h + cases fieldSorts <;> simp [projectionCodes.go] at h + | cons field fields ih => + intro fieldSorts i previous j code h + cases fieldSorts with + | nil => simp [projectionCodes.go] at h + | cons fieldSort fieldSorts => + let head := projectionCode view levels params allFields structType + field fieldSort i previous + cases j with + | zero => + change some head = some code at h + injection h with hcode + subst code + refine ⟨field, rfl, ?_⟩ + simp [head, projectionCode] + | succ j => + simp only [projectionCodes.go, List.getElem?_cons_succ] at h + obtain ⟨tailField, htailField, htypeFn⟩ := + ih (fieldSorts := fieldSorts) (i := i + 1) + (previous := previous ++ [head]) h + refine ⟨tailField, by simpa using htailField, ?_⟩ + have hpref : + previous ++ + (projectionCodes.go view levels params allFields structType + (field :: fields) (fieldSort :: fieldSorts) i previous).take + (j + 1) = + (previous ++ [head]) ++ + (projectionCodes.go view levels params allFields structType + fields fieldSorts (i + 1) + (previous ++ [head])).take j := by + simp [head, projectionCodes.go, List.append_assoc] + rw [hpref] + simpa only [Nat.add_assoc, Nat.add_comm, + Nat.add_left_comm] using htypeFn + +/-- The generated type function at field `idx` is the corresponding +specialized constructor field with all earlier generated projectors +substituted at the major premise. -/ +theorem projectionCodes_get?_typeFn (view : VStructureView) + (levels : List VLevel) (params : List VExpr) {idx : Nat} + {code : ProjectionCode} + (hcode : (view.projectionCodes levels params)[idx]? = some code) : + ∃ field, + (view.specializedFields levels params)[idx]? = some field ∧ + code.typeFn = .lam (view.structureType levels params) + ((field.liftN 1 idx).instRevAt + ((view.projectionCodes levels params).take idx |>.map fun prior => + .app prior.projector.lift (.bvar 0)) 0) := by + unfold projectionCodes at hcode ⊢ + simpa using projectionCodes.go_get?_typeFn view levels params + (view.specializedFields levels params) + (view.structureType levels params) hcode + +private theorem projectionCodes.go_get?_program_shape + (view : VStructureView) (levels : List VLevel) + (params allFields : List VExpr) (structType : VExpr) : + ∀ {fields : List VExpr} {fieldSorts : List VLevel} + {i : Nat} {previous : List ProjectionCode} {j : Nat} + {code : ProjectionCode}, + (projectionCodes.go view levels params allFields structType + fields fieldSorts i previous)[j]? = some code → + ∃ fieldSort, + fieldSorts[j]? = some fieldSort ∧ + code.fieldSort = fieldSort ∧ + code.minor = VExpr.lamN allFields + (.bvar (allFields.length - 1 - (i + j))) ∧ + code.projector = .lam structType + (VExpr.appN + (.const view.recursorName + (view.projectionLevels code.fieldSort levels)) + (params.map (VExpr.liftN 1) ++ + [code.typeFn.lift, code.minor.lift, .bvar 0])) := by + intro fields + induction fields with + | nil => + intro fieldSorts i previous j code h + cases fieldSorts <;> simp [projectionCodes.go] at h + | cons field fields ih => + intro fieldSorts i previous j code h + cases fieldSorts with + | nil => simp [projectionCodes.go] at h + | cons fieldSort fieldSorts => + let head := projectionCode view levels params allFields structType + field fieldSort i previous + cases j with + | zero => + change some head = some code at h + injection h with hcode + subst code + simp [head, projectionCode] + | succ j => + simp only [projectionCodes.go, List.getElem?_cons_succ] at h + have hout := ih (fieldSorts := fieldSorts) (i := i + 1) + (previous := previous ++ [head]) h + simpa only [List.getElem?_cons_succ, Nat.add_assoc, Nat.add_comm, + Nat.add_left_comm] using hout + +/-- A selected projection code retains the exact selecting minor and +recursor program emitted by `projectionCodes`. -/ +theorem projectionCodes_get?_program_shape (view : VStructureView) + (levels : List VLevel) (params : List VExpr) {idx : Nat} + {code : ProjectionCode} + (hcode : (view.projectionCodes levels params)[idx]? = some code) : + ∃ fieldSort, + (view.fieldSorts.map (VLevel.inst levels))[idx]? = some fieldSort ∧ + code.fieldSort = fieldSort ∧ + code.minor = VExpr.lamN (view.specializedFields levels params) + (.bvar ((view.specializedFields levels params).length - 1 - idx)) ∧ + code.projector = .lam (view.structureType levels params) + (VExpr.appN + (.const view.recursorName + (view.projectionLevels code.fieldSort levels)) + (params.map (VExpr.liftN 1) ++ + [code.typeFn.lift, code.minor.lift, .bvar 0])) := by + unfold projectionCodes at hcode + simpa using projectionCodes.go_get?_program_shape view levels params + (view.specializedFields levels params) + (view.structureType levels params) hcode + +/-- Applying a generated projection's type function to its major premise +substitutes that major into every earlier generated projector. -/ +theorem projectionCodes_get?_typeFn_beta (view : VStructureView) + (levels : List VLevel) (params : List VExpr) {idx : Nat} + {code : ProjectionCode} + (hcode : (view.projectionCodes levels params)[idx]? = some code) + (major : VExpr) : + ∃ field typeBody, + (view.specializedFields levels params)[idx]? = some field ∧ + code.typeFn = .lam (view.structureType levels params) typeBody ∧ + typeBody.inst major = + field.instRevAt + ((view.projectionCodes levels params).take idx |>.map fun prior => + .app prior.projector major) 0 := by + obtain ⟨field, hfield, htypeFn⟩ := + view.projectionCodes_get?_typeFn levels params hcode + let codes := view.projectionCodes levels params + have hidx : idx < codes.length := + (List.getElem?_eq_some_iff.1 hcode).1 + have htake : (codes.take idx).length = idx := by + simp [List.length_take, Nat.min_eq_left (Nat.le_of_lt hidx)] + have htake' : + ((view.projectionCodes levels params).take idx).length = idx := by + simpa [codes] using htake + refine ⟨field, _, hfield, htypeFn, ?_⟩ + rw [VExpr.instN_instRevAt] + rw [List.length_map, htake'] + simp only [Nat.zero_add, VExpr.inst_liftN1] + congr 1 + induction (view.projectionCodes levels params).take idx with + | nil => rfl + | cons prior previous ih => + simp only [List.map_cons] + rw [ih] + simp only [VExpr.inst, VExpr.inst_lift, VExpr.instVar_zero] + +@[simp] theorem projectionCodes_instL (view : VStructureView) + (levels : List VLevel) (params : List VExpr) (ls : List VLevel) : + (view.projectionCodes levels params).map + (fun code => code.instL ls) = + view.projectionCodes (levels.map (VLevel.inst ls)) + (params.map (VExpr.instL ls)) := by + simp [projectionCodes, projectionCodes.go_instL, + VLevel.inst_inst, List.map_map, Function.comp_def] + +/-- The dependent result type of projection `idx`, applied to `major`. -/ +def projectionType? (view : VStructureView) + (levels : List VLevel) (params : List VExpr) + (idx : Nat) (major : VExpr) : Option VExpr := do + let code ← (view.projectionCodes levels params)[idx]? + return .app code.typeFn major + +/-- The recursor encoding of projection `idx`, applied to `major`. -/ +def project? (view : VStructureView) + (levels : List VLevel) (params : List VExpr) + (idx : Nat) (major : VExpr) : Option VExpr := do + let code ← (view.projectionCodes levels params)[idx]? + return .app code.projector major + +/-- A proof-carrying boundary for the programs generated by +`projectionCodes`. Generation fixes the program syntax, while this +certificate records the remaining semantic fact needed by consumers: every +selected projector is well typed at every well-formed instantiation. + +This is intentionally separate from `VStructureView.WF`. The latter is the +certificate produced by ordinary inductive generation; accepting primitive +projection syntax is a later capability boundary and must not silently add a +structure-eta rule to Theory's definitional equality. -/ +def ProgramsWF (view : VStructureView) (env : VEnv) : Prop := + ∀ {U : Nat} {Γ : List VExpr} {levels : List VLevel} + {params : List VExpr} {idx : Nat} {code : ProjectionCode}, + OnCtx Γ (env.IsType U) → + (∀ level ∈ levels, level.WF U) → + levels.length = view.uvars → + params.length = view.nparams → + (∃ resultLevel, env.SpineWF U Γ (view.familyType.instL levels) + params (.sort resultLevel)) → + (view.projectionCodes levels params)[idx]? = some code → + env.HasType U Γ code.projector + (.forallE (view.structureType levels params) + (.app code.typeFn.lift (.bvar 0))) + +/-- A certified projector is typed by the exact constructor-telescope domain +exposed after substituting all earlier projections. -/ +theorem ProgramsWF.projector_hasType_field + {view : VStructureView} {env : VEnv} + (self : view.ProgramsWF env) (henv : env.WF) + {U : Nat} {Γ : List VExpr} {levels : List VLevel} + {params : List VExpr} {idx : Nat} {code : ProjectionCode} + (hΓ : OnCtx Γ (env.IsType U)) + (hlevels : ∀ level ∈ levels, level.WF U) + (hlevelsLength : levels.length = view.uvars) + (hparamsLength : params.length = view.nparams) + (hparamsSpine : ∃ resultLevel, + env.SpineWF U Γ (view.familyType.instL levels) + params (.sort resultLevel)) + (hcode : (view.projectionCodes levels params)[idx]? = some code) + {major : VExpr} + (hmajor : env.HasType U Γ major (view.structureType levels params)) : + ∃ field typeBody, + (view.specializedFields levels params)[idx]? = some field ∧ + code.typeFn = .lam (view.structureType levels params) typeBody ∧ + env.HasType U Γ (.app code.projector major) + (field.instRevAt (view.projectionArgs levels params idx major) 0) := by + obtain ⟨field, typeBody, hfield, htypeFn, htypeBody⟩ := + view.projectionCodes_get?_typeFn_beta levels params hcode major + have hprojector := self hΓ hlevels hlevelsLength hparamsLength + hparamsSpine hcode + have happ : env.HasType U Γ (.app code.projector major) + (.app code.typeFn major) := by + simpa only [VExpr.inst, VExpr.inst_lift, VExpr.instVar_zero] using + hprojector.app hmajor + rw [htypeFn] at happ + obtain ⟨sortLevel, hredexType⟩ := happ.isType henv hΓ + obtain ⟨A, B, hlam, harg⟩ := hredexType.app_inv henv hΓ + obtain ⟨⟨_, hstructType⟩, _, hbodyType⟩ := + hlam.lam_inv henv hΓ + have hfunTypeEq := hlam.uniqU henv hΓ + (hstructType.lam hbodyType) + obtain ⟨⟨_, hdomainEq⟩, _⟩ := + hfunTypeEq.forallE_inv henv hΓ + have harg' := harg.defeqU_r henv hΓ ⟨_, hdomainEq⟩ + have hbeta : env.IsDefEqU U Γ + (.app (.lam (view.structureType levels params) typeBody) major) + (typeBody.inst major) := + ⟨_, VEnv.IsDefEq.beta hbodyType harg'⟩ + have hout := happ.defeqU_r henv hΓ hbeta + rw [htypeBody] at hout + refine ⟨field, typeBody, hfield, htypeFn, ?_⟩ + simpa [projectionArgs] using hout + +private theorem ProgramsWF.projectionArgsSpineAux + {view : VStructureView} {env : VEnv} + (self : view.ProgramsWF env) (henv : env.WF) + {U : Nat} {Γ : List VExpr} {levels : List VLevel} + {params : List VExpr} + (hΓ : OnCtx Γ (env.IsType U)) + (hlevels : ∀ level ∈ levels, level.WF U) + (hlevelsLength : levels.length = view.uvars) + (hparamsLength : params.length = view.nparams) + (hparamsSpine : ∃ resultLevel, + env.SpineWF U Γ (view.familyType.instL levels) + params (.sort resultLevel)) + {major : VExpr} + (hmajor : env.HasType U Γ major (view.structureType levels params)) + (tailResult : VExpr) : + ∀ {count : Nat}, + count ≤ (view.specializedFields levels params).length → + ∃ cursor, + VExpr.consumeForalls? + (VExpr.forallN (view.specializedFields levels params) tailResult) + (view.projectionArgs levels params count major) = some cursor ∧ + env.SpineWF U Γ + (VExpr.forallN (view.specializedFields levels params) tailResult) + (view.projectionArgs levels params count major) cursor := by + intro count hcount + induction count with + | zero => + exact ⟨_, rfl, .nil⟩ + | succ count ih => + have hcountLt : count < + (view.specializedFields levels params).length := by omega + have hcodeIdx : count < + (view.projectionCodes levels params).length := by + simpa using hcountLt + let code := (view.projectionCodes levels params)[count] + have hcode : + (view.projectionCodes levels params)[count]? = some code := + List.getElem?_eq_getElem hcodeIdx + have hargsLength : + (view.projectionArgs levels params count major).length = count := + view.projectionArgs_length levels params count major + (Nat.le_of_lt hcodeIdx) + obtain ⟨cursor, hconsume, hspine⟩ := + ih (Nat.le_of_lt hcountLt) + obtain ⟨field, semanticBody, hfield, hconsumeDomain⟩ := + VExpr.consumeForalls?_forallN_domain + (view.specializedFields levels params) tailResult + (view.projectionArgs levels params count major) + (by simpa [hargsLength] using hcountLt) + have hcursorShape : cursor = + .forallE + (field.instRevAt + (view.projectionArgs levels params count major) 0) + semanticBody := + Option.some.inj (hconsume.symm.trans hconsumeDomain) + subst cursor + obtain ⟨field', _, hfield', _, hprojectorField⟩ := + self.projector_hasType_field henv hΓ hlevels hlevelsLength + hparamsLength hparamsSpine hcode hmajor + have hfieldEq : field' = field := + Option.some.inj + (hfield'.symm.trans (by simpa [hargsLength] using hfield)) + subst field' + refine ⟨semanticBody.inst (.app code.projector major), ?_, ?_⟩ + · rw [view.projectionArgs_succ levels params count major hcode] + rw [VExpr.consumeForalls?_append, hconsumeDomain] + rfl + · rw [view.projectionArgs_succ levels params count major hcode] + exact hspine.snoc hprojectorField + +/-- All canonical generated projections of a well-typed major form a single +well-typed dependent constructor-field spine. This theorem deliberately +stops at typing: it does not assert structure eta. -/ +theorem ProgramsWF.projectionArgsSpine + {view : VStructureView} {env : VEnv} + (self : view.ProgramsWF env) (henv : env.WF) + {U : Nat} {Γ : List VExpr} {levels : List VLevel} + {params : List VExpr} + (hΓ : OnCtx Γ (env.IsType U)) + (hlevels : ∀ level ∈ levels, level.WF U) + (hlevelsLength : levels.length = view.uvars) + (hparamsLength : params.length = view.nparams) + (hparamsSpine : ∃ resultLevel, + env.SpineWF U Γ (view.familyType.instL levels) + params (.sort resultLevel)) + {major : VExpr} + (hmajor : env.HasType U Γ major (view.structureType levels params)) + (tailResult : VExpr) : + env.SpineWF U Γ + (VExpr.forallN (view.specializedFields levels params) tailResult) + (view.projectionArgs levels params + (view.specializedFields levels params).length major) + (VExpr.instRev tailResult + (view.projectionArgs levels params + (view.specializedFields levels params).length major)) := by + obtain ⟨_, _, hspine⟩ := self.projectionArgsSpineAux henv hΓ hlevels + hlevelsLength hparamsLength hparamsSpine hmajor tailResult + (Nat.le_refl _) + apply hspine.retarget + · exact view.projectionArgs_length levels params + (view.specializedFields levels params).length major (by simp) + +/-- Applying the complete canonical projection spine to a constructor prefix +is well typed. The constructor-prefix premise is kept explicit so this +lemma remains independent of any proposed structure-eta equality rule. -/ +theorem ProgramsWF.etaRebuild_hasType_of_constructorPrefix + {view : VStructureView} {env : VEnv} + (self : view.ProgramsWF env) (henv : env.WF) + {U : Nat} {Γ : List VExpr} {levels : List VLevel} + {params : List VExpr} + (hΓ : OnCtx Γ (env.IsType U)) + (hlevels : ∀ level ∈ levels, level.WF U) + (hlevelsLength : levels.length = view.uvars) + (hparamsLength : params.length = view.nparams) + (hparamsSpine : ∃ resultLevel, + env.SpineWF U Γ (view.familyType.instL levels) + params (.sort resultLevel)) + {major : VExpr} + (hmajor : env.HasType U Γ major (view.structureType levels params)) + (hconstructorPrefix : env.HasType U Γ + (VExpr.appN (.const view.constructorName levels) params) + (VExpr.forallN (view.specializedFields levels params) + ((view.structureType levels params).liftN + (view.specializedFields levels params).length))) : + env.HasType U Γ (view.etaRebuild levels params major) + (view.structureType levels params) := by + have hfields := self.projectionArgsSpine henv hΓ hlevels hlevelsLength + hparamsLength hparamsSpine hmajor + ((view.structureType levels params).liftN + (view.specializedFields levels params).length) + have hrebuild := hfields.hasType_appN hconstructorPrefix + let args := view.projectionArgs levels params + (view.specializedFields levels params).length major + have hargsLength : + args.length = (view.specializedFields levels params).length := + view.projectionArgs_length levels params + (view.specializedFields levels params).length major (by simp) + have hlift : + (view.structureType levels params).liftN + (view.specializedFields levels params).length = + (view.structureType levels params).liftN args.length := + congrArg (view.structureType levels params).liftN hargsLength.symm + have hresult : + VExpr.instRev + ((view.structureType levels params).liftN + (view.specializedFields levels params).length) + args = + view.structureType levels params := by + calc + _ = VExpr.instRev + ((view.structureType levels params).liftN args.length) args := + congrArg (VExpr.instRev · args) hlift + _ = view.structureType levels params := + VExpr.instRev_liftN_len args _ + rw [hresult] at hrebuild + simpa [etaRebuild, VExpr.appN_append] using hrebuild + +/-- Exact registration of the checked structure artifact in a Theory +environment. These are concrete lookups and generated iota rules, not an +oracle supplied by a projection consumer. -/ +structure Registered (view : VStructureView) (env : VEnv) : Prop where + family : env.constants view.name = + some view.generation.block.sourceType.toVConstant + constructor : env.constants view.constructorName = + some view.constructor.raw.toVConstant + recursor : env.constants view.recursorName = + some view.generation.recursor + rules : ∀ rule ∈ view.generation.generatedRules, env.defeqs rule + +/-- The semantic fragment of `GenerationEnv` that remains monotone under an +arbitrary environment extension. Ordering is supplied by the structural-law +caller; exact constant/rule registration is carried separately by +`Registered`. -/ +structure GenerationSemantics (view : VStructureView) (env : VEnv) : Prop where + checked : view.generation.block.checked.WF env + familyTelescope : + env.TelDefEq view.uvars [] + (view.generation.block.rawParams ++ + view.generation.block.rawIndices) + (view.generation.block.checked.params ++ + view.generation.block.checked.indices) + familyResult : + env.IsDefEq view.uvars + (view.generation.block.rawParams ++ + view.generation.block.rawIndices).reverse + view.generation.block.rawResult + (.sort view.generation.block.checked.resultLevel) + (.sort (.succ view.generation.block.checked.resultLevel)) + constructor : view.constructor.WF view.generation.block env + +/-- Semantic well-formedness of one structure view in its registered +environment. The retained sort list is checked against the exact raw +dependent field telescope. -/ +structure WF (view : VStructureView) (env : VEnv) : Prop + extends VStructureView.Registered view env where + generationSemantics : VStructureView.GenerationSemantics view env + parameters : env.OnTel view.uvars [] + view.generation.block.checked.params + parameters_length : + view.generation.block.checked.params.length = view.nparams + fieldTelescope : env.OnSortTel view.uvars + view.generation.block.checked.params.reverse + view.fields view.fieldSorts + smallFields : view.generation.elimination = .small → + ∀ level ∈ view.fieldSorts, level = .zero + +theorem WF.rule_mem (self : VStructureView.WF view env) {df : VDefEq} + (h : df ∈ VInductDecl.GenerationChecked.generatedRules view.generation) : + VEnv.defeqs env df := + self.rules df h + +/-- The semantic capability required by structure-eta consumers. + +`VStructureView.WF` and `ProgramsWF` account for the registered structure +artifact and the typing of its generated projectors. This property records +only the additional equality that those rule-independent certificates do not +derive: rebuilding every canonical projection is definitionally equal to the +original major premise. Keeping it as an explicit environment capability +prevents checker verification from silently extending `VEnv.IsDefEq`. -/ +def _root_.Ix.Theory.Named.VEnv.HasStructureEta (env : VEnv) : Prop := + ∀ (view : VStructureView), view.WF env → view.ProgramsWF env → + ∀ {U : Nat} {Γ : List VExpr} {levels : List VLevel} + {params : List VExpr} {major : VExpr}, + OnCtx Γ (env.IsType U) → + (∀ level ∈ levels, level.WF U) → + levels.length = view.uvars → + params.length = view.nparams → + (∃ resultLevel, env.SpineWF U Γ (view.familyType.instL levels) + params (.sort resultLevel)) → + env.HasType U Γ major (view.structureType levels params) → + env.IsDefEq U Γ (view.etaRebuild levels params major) major + (view.structureType levels params) + +theorem Registered.mono {env env' : VEnv} (henv : env ≤ env') + (self : VStructureView.Registered view env) : + VStructureView.Registered view env' where + family := henv.1 self.family + constructor := henv.1 self.constructor + recursor := henv.1 self.recursor + rules := fun rule hrule => henv.2 (self.rules rule hrule) + +theorem GenerationSemantics.mono {env env' : VEnv} (henv : env ≤ env') + (self : VStructureView.GenerationSemantics view env) : + VStructureView.GenerationSemantics view env' where + checked := self.checked.mono henv + familyTelescope := self.familyTelescope.mono henv + familyResult := self.familyResult.mono henv + constructor := self.constructor.mono henv + +/-- Recover the monotone semantic fragment of a generated structure from the +ordinary generation certificate and the exact successful transaction trace. -/ +theorem GenerationSemantics.ofGenerationTrace {pre env : VEnv} + (hgen : view.generation.WF pre) + (trace : VEnv.AddInductGenerationTrace pre env view.generation) : + VStructureView.GenerationSemantics view env := by + have htypeFinal : trace.typeEnv ≤ env := by + have hctors := + (ctorFold_spec view.generation.block.sourceType.ctors + trace.addCtors).1 + have hrec := VEnv.addConst_le trace.addRec + have hrules : trace.recEnv ≤ env := by + simpa only [trace.addRules] using + (rulesFold_spec view.generation.generatedRules trace.recEnv).1 + exact hctors.trans (hrec.trans hrules) + have hpreFinal := trace.le + refine { + checked := hgen.blockWF.2.mono hpreFinal + familyTelescope := hgen.familyTel.mono hpreFinal + familyResult := hgen.familyResult.mono hpreFinal + constructor := ?_ } + have hconstructor : + view.constructor ∈ view.generation.block.ctorPairs := by + simp [view.constructor_eq] + exact (hgen.ctors trace.typeEnv trace.addType view.constructor + hconstructor).mono htypeFinal + +theorem WF.mono {env env' : VEnv} (henv : env ≤ env') + (self : VStructureView.WF view env) : VStructureView.WF view env' where + toRegistered := self.toRegistered.mono henv + generationSemantics := self.generationSemantics.mono henv + parameters := self.parameters.monoProjection henv + parameters_length := self.parameters_length + fieldTelescope := self.fieldTelescope.mono henv + smallFields := self.smallFields + +/-- Reassemble the standard generated-artifact invariant when an ordered +environment is available. -/ +theorem WF.toGenerationEnv (self : VStructureView.WF view env) + (henv : env.Ordered) : + VInductDecl.GenerationEnv view.generation env where + ord := henv + checked := self.generationSemantics.checked + familyTel := self.generationSemantics.familyTelescope + familyResult := self.generationSemantics.familyResult + ctorWF := by + intro ctor hctor + rw [view.constructor_eq] at hctor + simp only [List.mem_singleton] at hctor + subst ctor + exact self.generationSemantics.constructor + familyConst := self.family + ctorConst := by + intro ctor hctor + rw [view.constructor_eq] at hctor + simp only [List.mem_singleton] at hctor + subst ctor + exact self.constructor + +theorem WF.field_closed (self : VStructureView.WF view env) + (henv : env.Ordered) {i : Nat} {field : VExpr} + (hfield : view.fields[i]? = some field) : + field.ClosedN (view.nparams + i) := by + have hparamsCtx : OnCtx + view.generation.block.checked.params.reverse + (env.IsType view.uvars) := + by simpa using VEnv.OnTel.toOnCtx self.parameters (by trivial) + have hclosed := VEnv.OnSortTel.closedAt henv self.fieldTelescope + (VEnv.CtxWF.closed henv hparamsCtx) hfield + simpa [self.parameters_length] using hclosed + +theorem WF.specializedFields_liftN + (self : VStructureView.WF view env) (henv : env.Ordered) + (levels : List VLevel) (params : List VExpr) + (hparams : params.length = view.nparams) (n k : Nat) : + view.specializedFields levels + (params.map fun param => param.liftN n k) = + VExpr.liftTelN n (view.specializedFields levels params) k := by + simpa [specializedFields] using + specializedFieldsAux_liftN view.fields levels params view.nparams + 0 n k hparams + (fun j field hfield => by + simpa using self.field_closed henv hfield) + +theorem WF.specializedFields_instN + (self : VStructureView.WF view env) (henv : env.Ordered) + (levels : List VLevel) (params : List VExpr) + (hparams : params.length = view.nparams) (a : VExpr) (k : Nat) : + view.specializedFields levels + (params.map fun param => param.inst a k) = + VExpr.instTelN a (view.specializedFields levels params) k := by + simpa [specializedFields] using + specializedFieldsAux_instN view.fields levels params view.nparams + 0 k a hparams + (fun j field hfield => by + simpa using self.field_closed henv hfield) + +private theorem projectionLevels_length (view : VStructureView) + (fieldSort : VLevel) (levels : List VLevel) + (hlevels : levels.length = view.uvars) : + (view.projectionLevels fieldSort levels).length = + view.generation.recUvars := by + unfold projectionLevels + cases h : view.generation.elimination <;> + simp [VInductDecl.GenerationChecked.recUvars, + VInductDecl.ElimMode.recUvars, h, hlevels] + +private theorem projectionLevels_wf (view : VStructureView) + {U : Nat} (fieldSort : VLevel) (levels : List VLevel) + (hfieldSort : fieldSort.WF U) + (hlevels : ∀ level ∈ levels, level.WF U) : + ∀ level ∈ view.projectionLevels fieldSort levels, level.WF U := by + unfold projectionLevels + cases view.generation.elimination <;> simp_all + +private theorem sourceLevels_projectionLevels (view : VStructureView) + (fieldSort : VLevel) (levels : List VLevel) + (hlevels : levels.length = view.uvars) : + view.generation.sourceLevels.map + (VLevel.inst (view.projectionLevels fieldSort levels)) = levels := by + unfold VInductDecl.GenerationChecked.sourceLevels + unfold VInductDecl.ElimMode.sourceLevels projectionLevels + cases h : view.generation.elimination + · + change (VLevel.params' view.uvars 1).map + (VLevel.inst (fieldSort :: levels)) = levels + have hshift : + (VLevel.params' view.uvars 1).map + (VLevel.inst (fieldSort :: levels)) = + (VLevel.params view.uvars).map (VLevel.inst levels) := by + simp [VLevel.params', VLevel.params, List.map_map, + Function.comp_def, VLevel.inst, + List.getD_eq_getElem?_getD] + rw [hshift] + exact VLevel.inst_map_id hlevels + · + change (VLevel.params' view.uvars 0).map + (VLevel.inst levels) = levels + have hzero : VLevel.params' view.uvars 0 = + VLevel.params view.uvars := by + simp [VLevel.params', VLevel.params] + rw [hzero] + exact VLevel.inst_map_id hlevels + +private theorem motiveLevel_projectionLevels (view : VStructureView) + (fieldSort : VLevel) (levels : List VLevel) : + view.generation.motiveLevel.inst + (view.projectionLevels fieldSort levels) = + match view.generation.elimination with + | .large => fieldSort + | .small => .zero := by + unfold VInductDecl.GenerationChecked.motiveLevel + unfold VInductDecl.ElimMode.motiveLevel projectionLevels + cases view.generation.elimination <;> rfl + +private theorem WF.motiveLevel_projectionLevels + (self : VStructureView.WF view env) + (fieldSort : VLevel) (hfieldSort : fieldSort ∈ view.fieldSorts) + (levels : List VLevel) : + view.generation.motiveLevel.inst + (view.projectionLevels (fieldSort.inst levels) levels) = + fieldSort.inst levels := by + rw [VStructureView.motiveLevel_projectionLevels] + cases hmode : view.generation.elimination with + | large => rfl + | small => + rw [self.smallFields hmode fieldSort hfieldSort] + rfl + +@[simp] theorem WF.projectionCodes_liftN + (self : VStructureView.WF view env) (henv : env.Ordered) + (levels : List VLevel) (params : List VExpr) + (hparams : params.length = view.nparams) (n k : Nat) : + (view.projectionCodes levels params).map + (fun code => code.liftN n k) = + view.projectionCodes levels + (params.map fun param => param.liftN n k) := by + unfold projectionCodes + rw [self.specializedFields_liftN henv levels params hparams n k] + rw [← structureType_liftN] + apply projectionCodes.go_liftN + · rfl + · simp + +@[simp] theorem WF.projectionCodes_instN + (self : VStructureView.WF view env) (henv : env.Ordered) + (levels : List VLevel) (params : List VExpr) + (hparams : params.length = view.nparams) (a : VExpr) (k : Nat) : + (view.projectionCodes levels params).map + (fun code => code.instN a k) = + view.projectionCodes levels + (params.map fun param => param.inst a k) := by + unfold projectionCodes + rw [self.specializedFields_instN henv levels params hparams a k] + rw [← structureType_instN] + apply projectionCodes.go_instN + · rfl + · simp + +/-- The exact lower-layer structure-eta descriptor generated by a checked +structure view. Its projector syntax is the deterministic projector program +list already certified by the view; the proof fields are only the three +syntactic naturality laws required by Theory transport. -/ +def WF.toStructEta (self : VStructureView.WF view env) + (henv : env.Ordered) : VStructEta where + uvars := view.uvars + nparams := view.nparams + nfields := view.fields.length + familyName := view.name + familyType := view.familyType + constructorName := view.constructorName + projectors := fun levels params => + (view.projectionCodes levels params).map (·.projector) + projectors_length := by + intro levels params _ _ + simp [VStructureView.specializedFields, VStructureView.fields] + projectors_liftN := by + intro levels params n k hparams + have h := self.projectionCodes_liftN henv levels params hparams n k + simpa [List.map_map, ProjectionCode.liftN, Function.comp_def] using + congrArg (List.map (·.projector)) h + projectors_instN := by + intro levels params a k hparams + have h := self.projectionCodes_instN henv levels params hparams a k + simpa [List.map_map, ProjectionCode.instN, Function.comp_def] using + congrArg (List.map (·.projector)) h + projectors_instL := by + intro levels params ls + have h := projectionCodes_instL view levels params ls + simpa [List.map_map, ProjectionCode.instL, Function.comp_def] using + congrArg (List.map (·.projector)) h + +@[simp] theorem WF.toStructEta_structureType + (self : VStructureView.WF view env) (henv : env.Ordered) + (levels : List VLevel) (params : List VExpr) : + (self.toStructEta henv).structureType levels params = + view.structureType levels params := rfl + +@[simp] theorem WF.toStructEta_rebuild + (self : VStructureView.WF view env) (henv : env.Ordered) + (levels : List VLevel) (params : List VExpr) (major : VExpr) : + (self.toStructEta henv).rebuild levels params major = + view.etaRebuild levels params major := by + simp only [VStructEta.rebuild, VStructEta.projectionArgs, WF.toStructEta, + VStructureView.etaRebuild, VStructureView.projectionArgs] + rw [← view.projectionCodes_length levels params, List.take_length] + simp [List.map_map, Function.comp_def] + +end VStructureView + +namespace VEnv + +/-- Registered checked views supply the former semantic structure-eta +capability. The registry contributes only membership; subject reduction is +recovered from the ordered environment, and the equality itself is the +primitive `IsDefEq.structEta` step. -/ +theorem hasStructureEta_of_registry (henv : env.Ordered) + (registered : ∀ (view : VStructureView) + (hview : view.WF env) (_ : view.ProgramsWF env), + env.structEtas (hview.toStructEta henv)) : + env.HasStructureEta := by + intro view hview programs U Γ levels params major hΓ hlevels + hlevelsLength hparamsLength hparamsSpine hmajor + let rule := hview.toStructEta henv + have hregistered : env.structEtas rule := registered view hview programs + have hruleWF : rule.WF env := henv.structEtaWF hregistered + obtain ⟨resultLevel, hparamsSpine⟩ := hparamsSpine + have hrebuild := hruleWF.rebuild_hasType VEnv.LE.rfl hΓ hlevels + hlevelsLength hparamsLength ⟨resultLevel, hparamsSpine⟩ hmajor + have heta := IsDefEq.structEta hregistered hlevels hlevelsLength + hparamsLength hparamsSpine hmajor hrebuild + simpa [rule] using heta + +private theorem SpineWF.monoProjection {env env' : VEnv} + (henv : env ≤ env') : + ∀ {A es B}, env.SpineWF U Γ A es B → env'.SpineWF U Γ A es B + | _, _, _, h => h.mono henv + +/-- The view-facing direction of `TelDefEq.spine_sort`: arguments checked +against the retained raw telescope also consume its definitionally equal +view telescope. -/ +theorem TelDefEq.spine_sort_view + {env : VEnv} {U : Nat} (henv : env.Ordered) : + ∀ {Γ As As' es l}, env.TelDefEq U Γ As As' → + env.SpineWF U Γ (VExpr.forallN As (.sort l)) es (.sort l) → + es.length = As.length → + env.SpineWF U Γ (VExpr.forallN As' (.sort l)) es (.sort l) + | _, [], [], [], _, _, hspine, _ => by simpa using hspine + | _, [], [], _ :: _, _, _, _, hlen => by simp at hlen + | Γ, A :: As, A' :: As', e :: es, l, ⟨⟨_, hA⟩, hT⟩, + .cons he hrest, hlen => by + have heView : env.HasType U Γ e A' := hA.defeq he + have hTinst := TelDefEq.instN henv he (.zero) hT + have hrest' : env.SpineWF U Γ + (VExpr.forallN (VExpr.instTelN e As 0) (.sort l)) + es (.sort l) := by + simpa [VExpr.instN_forallN, VExpr.inst] using hrest + have hlen' : es.length = As.length := by simpa using hlen + have hlenInst : + es.length = (VExpr.instTelN e As 0).length := by + rw [VExpr.instTelN_length] + exact hlen' + have hout := TelDefEq.spine_sort_view henv + hTinst hrest' hlenInst + refine .cons heView ?_ + simpa [VExpr.instN_forallN, VExpr.inst] using hout + +/-- Parameters accepted by the structure family also consume the stored raw +constructor parameter prefix. This is the semantic bridge used by the +kernel projection checker before it traverses the constructor fields. -/ +theorem _root_.Ix.Theory.Named.VStructureView.WF.constructorParamsSpine + (self : VStructureView.WF view env) (henv : env.Ordered) + {U : Nat} {Γ : List VExpr} (levels : List VLevel) + (hlevels : ∀ level ∈ levels, level.WF U) + (_hlevelsLength : levels.length = view.uvars) + (params : List VExpr) (hparamsLength : params.length = view.nparams) + (paramsSpine : ∃ resultLevel, + env.SpineWF U Γ (view.familyType.instL levels) + params (.sort resultLevel)) + (target : VExpr) : + env.SpineWF U Γ + (VExpr.forallN + (view.constructorParams.map (VExpr.instL levels)) + target) params (VExpr.instRev target params) := by + let S := self.toGenerationEnv henv + obtain ⟨resultLevel, hspine⟩ := paramsSpine + have hrawLength : + view.generation.block.rawParams.length = view.nparams := + view.generation.shape.1 + have hspineShape : env.SpineWF U Γ + (VExpr.forallN + (view.generation.block.rawParams.map (VExpr.instL levels)) + (view.generation.block.rawResult.instL levels)) + params (.sort resultLevel) := by + simpa [VStructureView.familyType, + VInductDecl.NormalizedChecked.rawType_eq, + view.raw_indices_eq, VExpr.instL_forallN, + VExpr.forallN] using hspine + have hparamsRaw : env.SpineWF U Γ + (VExpr.forallN + (view.generation.block.rawParams.map (VExpr.instL levels)) + (.sort .zero)) params (.sort .zero) := by + have hout := hspineShape.retarget + (by simpa [hrawLength] using hparamsLength) (.sort .zero) + rw [VExpr.instRev_closedN params (by trivial)] at hout + exact hout + have hfamilyDefEq := S.rawParams_defeq.instL hlevels + have hrawLift : VExpr.liftTelN Γ.length + (view.generation.block.rawParams.map (VExpr.instL levels)) 0 = + view.generation.block.rawParams.map (VExpr.instL levels) := by + simpa using VEnv.OnTel.liftTelN_eq henv + hfamilyDefEq.raw_onTel (by trivial) Γ.length + have hcheckedLift : VExpr.liftTelN Γ.length + (view.generation.block.checked.params.map (VExpr.instL levels)) 0 = + view.generation.block.checked.params.map (VExpr.instL levels) := by + simpa using VEnv.OnTel.liftTelN_eq henv + (hfamilyDefEq.view_onTel henv) (by trivial) Γ.length + have hfamilyDefEqΓ := hfamilyDefEq.weakN henv + (Ctx.LiftN.zero (n := Γ.length) (Γ := []) Γ) + rw [hrawLift, hcheckedLift] at hfamilyDefEqΓ + simp only [List.append_nil] at hfamilyDefEqΓ + have hparamsChecked : env.SpineWF U Γ + (VExpr.forallN + (view.generation.block.checked.params.map (VExpr.instL levels)) + (.sort .zero)) params (.sort .zero) := + TelDefEq.spine_sort_view henv hfamilyDefEqΓ hparamsRaw + (by simpa [hrawLength] using hparamsLength) + have hconstructorMem : + view.constructor ∈ view.generation.block.ctorPairs := by + simp [view.constructor_eq] + have hconstructorShape := + view.generation.shape.2.2.2.2.2 view.constructor hconstructorMem + have hconstructorDefEq₀ := + ((S.ctorWF view.constructor hconstructorMem).declaredTel.take + view.nparams).instL hlevels + have hconstructorDefEq : env.TelDefEq U [] + (view.constructorParams.map (VExpr.instL levels)) + (view.generation.block.checked.params.map (VExpr.instL levels)) := by + simpa [VStructureView.constructorParams, + VInductDecl.NormalizedCtor.declaredBinders, + VInductDecl.NormalizedCtor.viewBinders, + hconstructorShape.2.2.1, self.parameters_length] using + hconstructorDefEq₀ + have hconstructorRawLift : VExpr.liftTelN Γ.length + (view.constructorParams.map (VExpr.instL levels)) 0 = + view.constructorParams.map (VExpr.instL levels) := by + simpa using VEnv.OnTel.liftTelN_eq henv + hconstructorDefEq.raw_onTel (by trivial) Γ.length + have hconstructorCheckedLift : VExpr.liftTelN Γ.length + (view.generation.block.checked.params.map (VExpr.instL levels)) 0 = + view.generation.block.checked.params.map (VExpr.instL levels) := + hcheckedLift + have hconstructorDefEqΓ := hconstructorDefEq.weakN henv + (Ctx.LiftN.zero (n := Γ.length) (Γ := []) Γ) + rw [hconstructorRawLift, hconstructorCheckedLift] at hconstructorDefEqΓ + simp only [List.append_nil] at hconstructorDefEqΓ + have hout := TelDefEq.spine_sort henv hconstructorDefEqΓ hparamsChecked + (by simpa [VStructureView.constructorParams] using + hparamsLength.trans hconstructorShape.2.2.1.symm) + exact hout.retarget + (by simpa [VStructureView.constructorParams] using + hparamsLength.trans hconstructorShape.2.2.1.symm) target + +/-- Recover the structure-family parameter spine from the corresponding +constructor-parameter prefix. This is the converse consumer bridge needed +when a checker recognizes a fully applied constructor before it knows the +family application carried by its result type. -/ +theorem _root_.Ix.Theory.Named.VStructureView.WF.familyParamsSpine_of_constructor + (self : VStructureView.WF view env) (henv : env.Ordered) + {U : Nat} {Γ : List VExpr} (levels : List VLevel) + (hlevels : ∀ level ∈ levels, level.WF U) + (_hlevelsLength : levels.length = view.uvars) + (params : List VExpr) (hparamsLength : params.length = view.nparams) + {target cursor : VExpr} + (constructorSpine : env.SpineWF U Γ + (VExpr.forallN + (view.constructorParams.map (VExpr.instL levels)) target) + params cursor) + (resultLevel : VLevel) + (hresult : view.generation.block.rawResult = .sort resultLevel) : + env.SpineWF U Γ (view.familyType.instL levels) params + (.sort (resultLevel.inst levels)) := by + let S := self.toGenerationEnv henv + have hrawLength : + view.generation.block.rawParams.length = view.nparams := + view.generation.shape.1 + have hconstructorMem : + view.constructor ∈ view.generation.block.ctorPairs := by + simp [view.constructor_eq] + have hconstructorShape := + view.generation.shape.2.2.2.2.2 view.constructor hconstructorMem + have hconstructorLength : params.length = + (view.constructorParams.map (VExpr.instL levels)).length := by + simpa [VStructureView.constructorParams] using + hparamsLength.trans hconstructorShape.2.2.1.symm + have hparamsConstructor : env.SpineWF U Γ + (VExpr.forallN + (view.constructorParams.map (VExpr.instL levels)) (.sort .zero)) + params (.sort .zero) := by + have hout := constructorSpine.retarget hconstructorLength (.sort .zero) + rw [VExpr.instRev_closedN params (by trivial)] at hout + exact hout + have hfamilyDefEq := S.rawParams_defeq.instL hlevels + have hrawLift : VExpr.liftTelN Γ.length + (view.generation.block.rawParams.map (VExpr.instL levels)) 0 = + view.generation.block.rawParams.map (VExpr.instL levels) := by + simpa using VEnv.OnTel.liftTelN_eq henv + hfamilyDefEq.raw_onTel (by trivial) Γ.length + have hcheckedLift : VExpr.liftTelN Γ.length + (view.generation.block.checked.params.map (VExpr.instL levels)) 0 = + view.generation.block.checked.params.map (VExpr.instL levels) := by + simpa using VEnv.OnTel.liftTelN_eq henv + (hfamilyDefEq.view_onTel henv) (by trivial) Γ.length + have hfamilyDefEqΓ := hfamilyDefEq.weakN henv + (Ctx.LiftN.zero (n := Γ.length) (Γ := []) Γ) + rw [hrawLift, hcheckedLift] at hfamilyDefEqΓ + simp only [List.append_nil] at hfamilyDefEqΓ + have hconstructorDefEq₀ := + ((S.ctorWF view.constructor hconstructorMem).declaredTel.take + view.nparams).instL hlevels + have hconstructorDefEq : env.TelDefEq U [] + (view.constructorParams.map (VExpr.instL levels)) + (view.generation.block.checked.params.map (VExpr.instL levels)) := by + simpa [VStructureView.constructorParams, + VInductDecl.NormalizedCtor.declaredBinders, + VInductDecl.NormalizedCtor.viewBinders, + hconstructorShape.2.2.1, self.parameters_length] using + hconstructorDefEq₀ + have hconstructorRawLift : VExpr.liftTelN Γ.length + (view.constructorParams.map (VExpr.instL levels)) 0 = + view.constructorParams.map (VExpr.instL levels) := by + simpa using VEnv.OnTel.liftTelN_eq henv + hconstructorDefEq.raw_onTel (by trivial) Γ.length + have hconstructorDefEqΓ := hconstructorDefEq.weakN henv + (Ctx.LiftN.zero (n := Γ.length) (Γ := []) Γ) + rw [hconstructorRawLift, hcheckedLift] at hconstructorDefEqΓ + simp only [List.append_nil] at hconstructorDefEqΓ + have hparamsChecked : env.SpineWF U Γ + (VExpr.forallN + (view.generation.block.checked.params.map (VExpr.instL levels)) + (.sort .zero)) params (.sort .zero) := + VEnv.TelDefEq.spine_sort_view henv hconstructorDefEqΓ + hparamsConstructor hconstructorLength + have hrawParamsLength : params.length = + (view.generation.block.rawParams.map (VExpr.instL levels)).length := by + simpa [hrawLength] using hparamsLength + have hparamsRaw : env.SpineWF U Γ + (VExpr.forallN + (view.generation.block.rawParams.map (VExpr.instL levels)) + (.sort .zero)) params (.sort .zero) := + VEnv.TelDefEq.spine_sort henv hfamilyDefEqΓ hparamsChecked + hrawParamsLength + have hout := hparamsRaw.retarget hrawParamsLength + (.sort (resultLevel.inst levels)) + rw [VExpr.instRev_closedN params (by trivial)] at hout + simpa [VStructureView.familyType, + VInductDecl.NormalizedChecked.rawType_eq, + view.raw_indices_eq, hresult, VExpr.instL_forallN, + VExpr.forallN, VExpr.instL] using hout + +theorem _root_.Ix.Theory.Named.VStructureView.WF.specializedFields_onSortTel + (self : VStructureView.WF view env) (henv : env.Ordered) + {U : Nat} {Γ : List VExpr} (levels : List VLevel) + (hlevels : ∀ level ∈ levels, level.WF U) + (_hlevelsLength : levels.length = view.uvars) + (params : List VExpr) (hparamsLength : params.length = view.nparams) + (paramsSpine : ∃ resultLevel, + env.SpineWF U Γ (view.familyType.instL levels) + params (.sort resultLevel)) : + env.OnSortTel U Γ (view.specializedFields levels params) + (view.fieldSorts.map (VLevel.inst levels)) := by + let S := self.toGenerationEnv henv + obtain ⟨resultLevel, hspine⟩ := paramsSpine + have hrawLength : + view.generation.block.rawParams.length = view.nparams := + view.generation.shape.1 + have hspineShape : env.SpineWF U Γ + (VExpr.forallN + (view.generation.block.rawParams.map (VExpr.instL levels)) + (view.generation.block.rawResult.instL levels)) + params (.sort resultLevel) := by + simpa [VStructureView.familyType, + VInductDecl.NormalizedChecked.rawType_eq, + view.raw_indices_eq, VExpr.instL_forallN, + VExpr.forallN] using hspine + have hparamsRaw : env.SpineWF U Γ + (VExpr.forallN + (view.generation.block.rawParams.map (VExpr.instL levels)) + (.sort resultLevel)) params (.sort resultLevel) := by + have hout := hspineShape.retarget + (by simpa [hrawLength] using hparamsLength) + (.sort resultLevel) + rw [VExpr.instRev_closedN params (by trivial)] at hout + exact hout + have hrawChecked := S.rawParams_defeq.instL hlevels + have hrawLift := VEnv.OnTel.liftTelN_eq henv + hrawChecked.raw_onTel (by trivial) Γ.length + have hcheckedLift := VEnv.OnTel.liftTelN_eq henv + (hrawChecked.view_onTel henv) (by trivial) Γ.length + have hrawLift' : VExpr.liftTelN Γ.length + (view.generation.block.rawParams.map (VExpr.instL levels)) 0 = + view.generation.block.rawParams.map (VExpr.instL levels) := by + simpa using hrawLift + have hcheckedLift' : VExpr.liftTelN Γ.length + (view.generation.block.checked.params.map (VExpr.instL levels)) 0 = + view.generation.block.checked.params.map (VExpr.instL levels) := by + simpa using hcheckedLift + have hrawCheckedΓ := hrawChecked.weakN henv + (Ctx.LiftN.zero (n := Γ.length) (Γ := []) Γ) + rw [hrawLift', hcheckedLift'] at hrawCheckedΓ + simp only [List.append_nil] at hrawCheckedΓ + have hparamsChecked : env.SpineWF U Γ + (VExpr.forallN + (view.generation.block.checked.params.map + (VExpr.instL levels)) (.sort resultLevel)) + params (.sort resultLevel) := by + exact TelDefEq.spine_sort_view henv hrawCheckedΓ hparamsRaw + (by simpa [hrawLength] using hparamsLength) + have hfields := self.fieldTelescope.instL hlevels + have hcheckedParams := self.parameters.instL hlevels + have Wparams := Ctx.LiftN.consTel + (view.generation.block.checked.params.map (VExpr.instL levels)) + (Ctx.LiftN.zero (n := Γ.length) (Γ := []) Γ) + rw [hcheckedLift'] at Wparams + have hcheckedCtx : OnCtx + (view.generation.block.checked.params.reverse.map + (VExpr.instL levels)) (env.IsType U) := by + simpa [List.map_reverse] using + VEnv.OnTel.toOnCtx hcheckedParams (by trivial) + have hfieldLift := VEnv.OnSortTel.liftTelN_eq henv hfields + (VEnv.CtxWF.closed henv hcheckedCtx) Γ.length + have hfieldsΓ := VEnv.OnSortTel.weakN henv + (by simpa [List.map_reverse] using Wparams) hfields + simp only [List.length_reverse, List.length_map] at hfieldLift + rw [hfieldLift] at hfieldsΓ + have hspecialized := VEnv.OnSortTel.instRevParams henv + hparamsChecked (by simpa [self.parameters_length] using hparamsLength) + (by simpa [List.map_reverse] using hfieldsΓ) + rw [VExpr.instRevAt_map_instL_zipIdx] at hspecialized + simpa [VStructureView.specializedFields] using hspecialized + +private theorem _root_.Ix.Theory.Named.VStructureView.WF.generationParamsSpine + (self : VStructureView.WF view env) (henv : env.Ordered) + {U : Nat} {Γ : List VExpr} (levels : List VLevel) + (hlevels : ∀ level ∈ levels, level.WF U) + (hlevelsLength : levels.length = view.uvars) + (params : List VExpr) (hparamsLength : params.length = view.nparams) + (paramsSpine : ∃ resultLevel, + env.SpineWF U Γ (view.familyType.instL levels) + params (.sort resultLevel)) + (fieldSort : VLevel) : + env.SpineWF U Γ + (VExpr.forallN + (view.generation.paramsTel.map + (VExpr.instL + (view.projectionLevels fieldSort levels))) + (.sort fieldSort)) params (.sort fieldSort) := by + let S := self.toGenerationEnv henv + obtain ⟨resultLevel, hspine⟩ := paramsSpine + have hrawLength : + view.generation.block.rawParams.length = view.nparams := + view.generation.shape.1 + have hspineShape : env.SpineWF U Γ + (VExpr.forallN + (view.generation.block.rawParams.map (VExpr.instL levels)) + (view.generation.block.rawResult.instL levels)) + params (.sort resultLevel) := by + simpa [VStructureView.familyType, + VInductDecl.NormalizedChecked.rawType_eq, + view.raw_indices_eq, VExpr.instL_forallN, + VExpr.forallN] using hspine + have hparamsRaw : env.SpineWF U Γ + (VExpr.forallN + (view.generation.block.rawParams.map (VExpr.instL levels)) + (.sort fieldSort)) params (.sort fieldSort) := by + have hout := hspineShape.retarget + (by simpa [hrawLength] using hparamsLength) (.sort fieldSort) + rw [VExpr.instRev_closedN params (by trivial)] at hout + exact hout + have hrawChecked := S.rawParams_defeq.instL hlevels + have hrawLift : VExpr.liftTelN Γ.length + (view.generation.block.rawParams.map (VExpr.instL levels)) 0 = + view.generation.block.rawParams.map (VExpr.instL levels) := by + simpa using VEnv.OnTel.liftTelN_eq henv + hrawChecked.raw_onTel (by trivial) Γ.length + have hcheckedLift : VExpr.liftTelN Γ.length + (view.generation.block.checked.params.map (VExpr.instL levels)) 0 = + view.generation.block.checked.params.map (VExpr.instL levels) := by + simpa using VEnv.OnTel.liftTelN_eq henv + (hrawChecked.view_onTel henv) (by trivial) Γ.length + have hrawCheckedΓ := hrawChecked.weakN henv + (Ctx.LiftN.zero (n := Γ.length) (Γ := []) Γ) + rw [hrawLift, hcheckedLift] at hrawCheckedΓ + simp only [List.append_nil] at hrawCheckedΓ + have hparamsChecked : env.SpineWF U Γ + (VExpr.forallN + (view.generation.block.checked.params.map + (VExpr.instL levels)) (.sort fieldSort)) + params (.sort fieldSort) := + TelDefEq.spine_sort_view henv hrawCheckedΓ hparamsRaw + (by simpa [hrawLength] using hparamsLength) + have hgenerationChecked := S.generationParams_defeq.instL hlevels + have hgenerationLift : VExpr.liftTelN Γ.length + (view.generation.block.generationParams.map + (VExpr.instL levels)) 0 = + view.generation.block.generationParams.map + (VExpr.instL levels) := by + simpa using VEnv.OnTel.liftTelN_eq henv + hgenerationChecked.raw_onTel (by trivial) Γ.length + have hcheckedLift₂ : VExpr.liftTelN Γ.length + (view.generation.block.checked.params.map + (VExpr.instL levels)) 0 = + view.generation.block.checked.params.map + (VExpr.instL levels) := hcheckedLift + have hgenerationCheckedΓ := hgenerationChecked.weakN henv + (Ctx.LiftN.zero (n := Γ.length) (Γ := []) Γ) + rw [hgenerationLift, hcheckedLift₂] at hgenerationCheckedΓ + simp only [List.append_nil] at hgenerationCheckedΓ + have hparamsGeneration := TelDefEq.spine_sort henv + hgenerationCheckedΓ hparamsChecked + (by simpa [S.generationParams_length] using hparamsLength) + have hsource := VStructureView.sourceLevels_projectionLevels + view fieldSort levels + hlevelsLength + have hparamsTel : + view.generation.paramsTel.map + (VExpr.instL (view.projectionLevels fieldSort levels)) = + view.generation.block.generationParams.map + (VExpr.instL levels) := by + simp [VInductDecl.GenerationChecked.paramsTel, + List.map_map, Function.comp_def, VExpr.instL_instL, hsource] + rw [hparamsTel] + exact hparamsGeneration + +theorem _root_.Ix.Theory.Named.VStructureView.WF.recursorProjection_hasType + (self : VStructureView.WF view env) (henv : env.Ordered) + {U : Nat} {Γ : List VExpr} (levels : List VLevel) + (hlevels : ∀ level ∈ levels, level.WF U) + (hlevelsLength : levels.length = view.uvars) + (params : List VExpr) (hparamsLength : params.length = view.nparams) + (paramsSpine : ∃ resultLevel, + env.SpineWF U Γ (view.familyType.instL levels) + params (.sort resultLevel)) + (fieldSort : VLevel) + (hfieldSort : fieldSort.WF U) + (hmotiveLevel : + view.generation.motiveLevel.inst + (view.projectionLevels fieldSort levels) = fieldSort) + (_structIsType : env.IsType U Γ + (view.structureType levels params)) + {typeFn minor major : VExpr} + (typeFnType : env.HasType U Γ typeFn + (.forallE (view.structureType levels params) (.sort fieldSort))) + (minorType : env.HasType U Γ minor + (view.projectionMinorType levels params + (view.specializedFields levels params) typeFn)) + (majorType : env.HasType U Γ major + (view.structureType levels params)) : + env.HasType U Γ + (VExpr.appN (.const view.recursorName + (view.projectionLevels fieldSort levels)) + (params ++ [typeFn, minor, major])) + (.app typeFn major) := by + let gen := view.generation + let S := self.toGenerationEnv henv + let pLevels := view.projectionLevels fieldSort levels + let k := gen.block.ctorPairs.length + let ni := gen.idxTel.length + let recRest : VExpr := + VExpr.forallN gen.minorTypes <| + VExpr.forallN (VExpr.liftTelN (k + 1) gen.idxTel 0) <| + .forallE + (VExpr.appN (.const gen.block.sourceType.name gen.sourceLevels) + (VExpr.bvarRevRange (ni + k + 1) view.nparams ++ + VExpr.bvarRevRange 0 ni)) + (.app + (VExpr.appN (.bvar (ni + k + 1)) + (VExpr.bvarRevRange 1 ni)) + (.bvar 0)) + let recTail : VExpr := .forallE gen.motiveType recRest + have hrec : env.HasType U Γ + (.const view.recursorName pLevels) + ((VExpr.forallN gen.paramsTel recTail).instL pLevels) := by + have hout := VEnv.HasType.const (Γ := Γ) self.recursor + (VStructureView.projectionLevels_wf view fieldSort levels + hfieldSort hlevels) + (VStructureView.projectionLevels_length view fieldSort levels + hlevelsLength) + simpa [gen, pLevels, recTail, recRest, k, ni, + VStructureView.recursorName, + VInductDecl.GenerationChecked.recursor, + VInductDecl.GenerationChecked.recType] using hout + have hparams := self.generationParamsSpine henv levels hlevels + hlevelsLength params hparamsLength paramsSpine fieldSort + have hparamsTelLength : params.length = + (gen.paramsTel.map (VExpr.instL pLevels)).length := by + simp [gen, VInductDecl.GenerationChecked.paramsTel, + S.generationParams_length, hparamsLength] + have hparamsFull := hparams.retarget hparamsTelLength + (recTail.instL pLevels) + have hparamsFull' : env.SpineWF U Γ + ((VExpr.forallN gen.paramsTel recTail).instL pLevels) + params (VExpr.instRev (recTail.instL pLevels) params) := by + simpa [VExpr.instL_forallN] using hparamsFull + have hmotiveShape : + VExpr.instRev (recTail.instL pLevels) params = + .forallE + (.forallE (view.structureType levels params) (.sort fieldSort)) + (VExpr.instRevAt (recRest.instL pLevels) params 1) := by + change VExpr.instRev + (.forallE (gen.motiveType.instL pLevels) + (recRest.instL pLevels)) params = _ + have hconst : VExpr.instRev + (.const view.generation.block.sourceType.name levels) params = + .const view.generation.block.sourceType.name levels := + VExpr.instRev_closedN params (by trivial) + have hrange : + (VExpr.bvarRevRange 0 view.source.nparams).map + (VExpr.instRev · params) = params := by + have hparamsLength' : params.length = view.source.nparams := + hparamsLength + rw [← hparamsLength'] + exact VExpr.map_instRev_bvarRevRange params + have hrangeL : + (VExpr.bvarRevRange 0 view.source.nparams).map + (fun x => (x.instL pLevels).instRev params) = params := by + calc + _ = ((VExpr.bvarRevRange 0 view.source.nparams).map + (VExpr.instL pLevels)).map (VExpr.instRev · params) := by + rw [List.map_map] + rfl + _ = params := by + rw [VExpr.bvarRevRange_map_instL] + exact hrange + have hsort : + (VExpr.sort fieldSort).instRevAt params 1 = + .sort fieldSort := + VExpr.instRevAt_closedN params (by trivial) + rw [VExpr.instRev_forallE_projection] + congr 1 + simp [gen, pLevels, + VInductDecl.GenerationChecked.motiveType, + VInductDecl.GenerationChecked.idxTel, + view.raw_indices_eq, VExpr.forallN, VExpr.bvarRevRange, + VExpr.instL, VExpr.instL_appN, + VExpr.instRev_forallE_projection, + VExpr.instRev_appN, Function.comp_def, + hconst, hrangeL, hsort, hmotiveLevel, + VStructureView.structureType, + VStructureView.sourceLevels_projectionLevels view fieldSort levels + hlevelsLength] + rw [hmotiveShape] at hparamsFull' + have hwithMotive := hparamsFull'.snoc typeFnType + have hconstructorMem : + view.constructor ∈ view.generation.block.ctorPairs := by + simp [view.constructor_eq] + have hresultIndices : view.constructor.view.resultIndices = [] := by + apply List.length_eq_zero_iff.1 + rw [S.viewResultIndices_length hconstructorMem] + simp [view.checked_indices_eq] + have hminorShape : + ((VExpr.instRevAt (recRest.instL pLevels) params 1).inst typeFn) = + .forallE (view.projectionMinorType levels params + (view.specializedFields levels params) typeFn) + (.forallE (view.structureType levels params).lift + (.app (typeFn.liftN 2) (.bvar 0))) := by + simp [gen, pLevels, recRest, k, ni, VInductDecl.GenerationChecked.minorTypes, + VInductDecl.GenerationChecked.minorTypesAux, VInductDecl.GenerationChecked.minorType, + VInductDecl.GenerationChecked.idxTel, VInductDecl.NormalizedCtor.fieldsR, + VInductDecl.NormalizedCtor.recArgsR, VInductDecl.NormalizedCtor.resultIndicesR, + VInductDecl.ihsFromRecArgs, VStructureView.projectionMinorType, + VStructureView.projectionConstructorApp, view.constructor_eq, view.raw_indices_eq, + hresultIndices, view.recursive_eq, VExpr.instL_forallN, VExpr.instL_appN, + VExpr.liftTelN_instL, VExpr.instL_instL, VExpr.instN_forallN, VExpr.instTelN, + VExpr.instRevAt_forallN_projection, List.map_append, VExpr.bvarRevRange, List.map_append, + List.map_map, Function.comp_def, + VStructureView.sourceLevels_projectionLevels view fieldSort levels hlevelsLength] + change VExpr.forallE _ _ = VExpr.forallE _ _ + congr 1 + · have hfieldTel := + VExpr.instTelN_instRevAt_lift_projection + ((view.constructor.rawFields view.source.nparams).map + (VExpr.instL levels)) params typeFn 0 + rw [VExpr.instRevAt_map_instL_zipIdx] at hfieldTel + have hfieldTel' : + VExpr.instTelN typeFn + ((VExpr.liftTelN 1 + ((view.constructor.rawFields view.source.nparams).map + (VExpr.instL levels)) 0).zipIdx 1 |>.map + fun x => x.1.instRevAt params x.2) 0 = + view.specializedFields levels params := by + simpa [VStructureView.specializedFields, + VStructureView.fields] using hfieldTel + rw [hfieldTel'] + congr 1 + have hsourceLevels := + VStructureView.sourceLevels_projectionLevels view fieldSort levels + hlevelsLength + change + (VLevel.params' view.source.uvars + view.generation.elimination.offset).map + (VLevel.inst pLevels) = levels at hsourceLevels + have hliftedLength : + (VExpr.liftTelN 1 + ((view.constructor.rawFields view.source.nparams).map + (VExpr.instL levels)) 0).length = + (view.constructor.rawFields view.source.nparams).length := by + rw [VExpr.liftTelN_length] + simp + have hspecializedLength : + (view.specializedFields levels params).length = + (view.constructor.rawFields view.source.nparams).length := by + simp [VStructureView.specializedFields, + VStructureView.fields] + simp only [VExpr.forallN, VExpr.instL, VExpr.bvarRevRange_map_instL, hliftedLength, + hspecializedLength] + rw [hsourceLevels] + have hbody := + VExpr.projectionMinorBody_shape view.constructorName levels + params (view.constructor.rawFields view.source.nparams).length + typeFn + rw [hparamsLength] at hbody + simpa only [Nat.add_comm] using hbody + · have hsourceLevels := + VStructureView.sourceLevels_projectionLevels view fieldSort levels + hlevelsLength + change + (VLevel.params' view.source.uvars + view.generation.elimination.offset).map + (VLevel.inst pLevels) = levels at hsourceLevels + simp only [VExpr.forallN, VExpr.liftTelN, List.zipIdx_nil, List.map_nil, VExpr.instTelN, + VExpr.instL, VExpr.instL_appN, VExpr.bvarRevRange_map_instL, VExpr.instL] + rw [hsourceLevels] + simpa [gen, hparamsLength, VStructureView.structureType] using + (VExpr.projectionMajorTail_shape view.name levels params typeFn) + rw [hminorShape] at hwithMotive + have hwithMinor := hwithMotive.snoc minorType + have hwithMajor : env.SpineWF U Γ + ((VExpr.forallN gen.paramsTel recTail).instL pLevels) + (params ++ [typeFn, minor, major]) (.app typeFn major) := by + have majorType' : env.HasType U Γ major + ((view.structureType levels params).lift.inst minor) := by + rw [VExpr.inst_lift] + exact majorType + have hout := hwithMinor.snoc majorType' + have htypeFnMinor : + (typeFn.liftN 2).inst minor 1 = typeFn.lift := by + rw [← VExpr.liftN_liftN typeFn 1 1, + VExpr.instN_liftAt_projection, VExpr.inst_lift] + have hminorVar : VExpr.instVar 0 minor 1 = .bvar 0 := by + simp [VExpr.instVar] + have hresult : + (((typeFn.liftN 2).app (.bvar 0)).inst minor 1).inst major = + typeFn.app major := by + simp only [VExpr.inst] + rw [htypeFnMinor, VExpr.inst_lift] + rw [hminorVar] + simp only [VExpr.inst] + rw [VExpr.instVar_zero] + rw [hresult] at hout + simpa [List.append_assoc] using hout + exact hwithMajor.hasType_appN hrec + +theorem SpineWF.instNProjection {env : VEnv} {U k : Nat} + {Γ₀ Γ₁ Γ : List VExpr} {e₀ A₀ : VExpr} + (henv : env.Ordered) + (W : Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ) + (h₀ : env.HasType U Γ₀ e₀ A₀) : + ∀ {es : List VExpr} {A B : VExpr}, env.SpineWF U Γ₁ A es B → + env.SpineWF U Γ (A.inst e₀ k) + (es.map fun e => e.inst e₀ k) (B.inst e₀ k) + | _, _, _, h => h.instN henv W h₀ + +/-- A generated projector computes on the matching generated constructor +once the registered rule's capture spine has been checked. This is the +exact iota layer; constructor-head and parameter-prefix alignment are kept +outside this theorem. -/ +theorem _root_.Ix.Theory.Named.VStructureView.WF.projector_constructor_exact + (self : VStructureView.WF view env) (henv : env.WF) + {U : Nat} {Γ : List VExpr} (hΓ : OnCtx Γ (env.IsType U)) + {levels : List VLevel} (hlevels : ∀ level ∈ levels, level.WF U) + (hlevelsLength : levels.length = view.uvars) + {params : List VExpr} (hparamsLength : params.length = view.nparams) + (hparamsSpine : ∃ resultLevel, + env.SpineWF U Γ (view.familyType.instL levels) + params (.sort resultLevel)) + {idx : Nat} {code : VStructureView.ProjectionCode} + (hcode : (view.projectionCodes levels params)[idx]? = some code) + (hprojector : env.HasType U Γ code.projector + (.forallE (view.structureType levels params) + (.app code.typeFn.lift (.bvar 0)))) + {fields : List VExpr} (hfieldsLength : + fields.length = (view.specializedFields levels params).length) + {field : VExpr} (hfield : fields[idx]? = some field) + (hctorType : env.HasType U Γ + (VExpr.appN (.const view.constructorName levels) (params ++ fields)) + (view.structureType levels params)) + (hfieldsSpine : env.SpineWF U Γ + (VExpr.forallN (view.specializedFields levels params) (.sort .zero)) + fields (.sort .zero)) + {B : VExpr} + (hcaps : env.SpineWF U Γ + ((view.generation.rule 0 view.constructor).type.instL + (view.projectionLevels code.fieldSort levels)) + (params ++ [code.typeFn, code.minor] ++ fields) B) : + env.IsDefEqU U Γ + (.app code.projector + (VExpr.appN (.const view.constructorName levels) (params ++ fields))) + field := by + obtain ⟨fieldSort, hfieldSort, hcodeSort, hminorShape, + hprojectorShape⟩ := + view.projectionCodes_get?_program_shape levels params hcode + have hsortTel := self.specializedFields_onSortTel henv.ordered + levels hlevels hlevelsLength params hparamsLength hparamsSpine + have hfieldSortWF : code.fieldSort.WF U := by + rw [hcodeSort] + exact hsortTel.sortWF hΓ hfieldSort + let pLevels := view.projectionLevels code.fieldSort levels + have hpLevelsWF : ∀ level ∈ pLevels, level.WF U := + VStructureView.projectionLevels_wf view code.fieldSort levels + hfieldSortWF hlevels + have hpLevelsLength : pLevels.length = view.generation.recUvars := + VStructureView.projectionLevels_length view code.fieldSort levels + hlevelsLength + have hruleMem : view.generation.rule 0 view.constructor ∈ + view.generation.generatedRules := by + simp [VInductDecl.GenerationChecked.generatedRules, + view.constructor_eq] + have hregistered := self.rule_mem hruleMem + have hruleWF := henv.ordered.defEqWF hregistered + rw [hprojectorShape] at hprojector + obtain ⟨_, ⟨projectorBodyType, hprojectorBody⟩⟩ := + hprojector.lam_inv henv.ordered hΓ + have hprojectorBeta := VEnv.IsDefEq.beta hprojectorBody hctorType + have hprojectorToRule : env.IsDefEqU U Γ + (.app code.projector + (VExpr.appN (.const view.constructorName levels) (params ++ fields))) + (VExpr.appN (.const view.recursorName pLevels) + (params ++ [code.typeFn, code.minor, + VExpr.appN (.const view.constructorName levels) + (params ++ fields)])) := by + refine ⟨projectorBodyType.inst + (VExpr.appN (.const view.constructorName levels) (params ++ fields)), ?_⟩ + rw [hprojectorShape] + simpa [pLevels, VExpr.inst, VExpr.instN_appN, VExpr.inst_lift, + VExpr.instVar_zero, + List.map_append, List.map_map, Function.comp_def] using + hprojectorBeta + let gen := view.generation + let Bs := view.constructor.fieldsR view.source.uvars view.source.nparams + gen.elimination + let m := Bs.length + let rs := view.constructor.recArgsR view.source.uvars gen.elimination + let binders := gen.paramsTel ++ gen.motiveType :: gen.minorTypes ++ + VExpr.liftTelN (gen.block.ctorPairs.length + 1) Bs 0 + let recBase := VExpr.appN + (.const (.str gen.block.sourceType.name "rec") gen.recLevels) + (VExpr.bvarRevRange m (view.source.nparams + + gen.block.ctorPairs.length + 1)) + let idxR := view.constructor.resultIndicesR view.source.uvars + gen.elimination |>.map fun expression => + expression.liftN (gen.block.ctorPairs.length + 1) m + let ctorApp := VExpr.appN + (.const view.constructor.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (m + gen.block.ctorPairs.length + 1) + view.source.nparams ++ VExpr.bvarRevRange 0 m) + let ihs := rs.map fun recursive => + recursive.ruleCall m gen.block.ctorPairs.length recBase + let lhsBody := VExpr.appN recBase (idxR ++ [ctorApp]) + let rhsBody := VExpr.appN + (.bvar (gen.block.ctorPairs.length - 1 - 0 + m)) + (VExpr.bvarRevRange 0 m ++ ihs) + let typeBody := VExpr.appN + (.bvar (gen.block.ctorPairs.length + m)) (idxR ++ [ctorApp]) + have hlhs₀ := hruleWF.1 + change env.HasType gen.recUvars [] (VExpr.lamN binders lhsBody) + (VExpr.forallN binders typeBody) at hlhs₀ + have hrhs₀ := hruleWF.2 + change env.HasType gen.recUvars [] (VExpr.lamN binders rhsBody) + (VExpr.forallN binders typeBody) at hrhs₀ + have hlhs : env.HasType U Γ + ((VExpr.lamN binders lhsBody).instL pLevels) + ((VExpr.forallN binders typeBody).instL pLevels) := + (hlhs₀.instL hpLevelsWF).weak0 henv.ordered + have hrhs : env.HasType U Γ + ((VExpr.lamN binders rhsBody).instL pLevels) + ((VExpr.forallN binders typeBody).instL pLevels) := + (hrhs₀.instL hpLevelsWF).weak0 henv.ordered + rw [VExpr.instL_lamN, VExpr.instL_forallN] at hlhs hrhs + have hcaps' : env.SpineWF U Γ + (VExpr.forallN (binders.map (VExpr.instL pLevels)) + (typeBody.instL pLevels)) + (params ++ [code.typeFn, code.minor] ++ fields) B := by + change env.SpineWF U Γ + ((VExpr.forallN binders typeBody).instL pLevels) + (params ++ [code.typeFn, code.minor] ++ fields) B at hcaps + simpa only [VExpr.instL_forallN] using hcaps + let S := self.toGenerationEnv henv.ordered + have hparamsTelLength : gen.paramsTel.length = view.nparams := by + simp [gen, VInductDecl.GenerationChecked.paramsTel, + S.generationParams_length] + have hspecializedLength : + (view.specializedFields levels params).length = + (view.constructor.rawFields view.nparams).length := by + simp [VStructureView.specializedFields, VStructureView.fields] + have hBsLength : Bs.length = + (view.constructor.rawFields view.nparams).length := by + simpa [Bs] using + (VInductDecl.NormalizedCtor.fieldsR_length + (source := view.source) view.constructor + (mode := gen.elimination)) + have hcapturesLength : + (params ++ [code.typeFn, code.minor] ++ fields).length = + (binders.map (VExpr.instL pLevels)).length := by + simp only [List.length_append, List.length_cons, List.length_nil, + List.length_map, VExpr.liftTelN_length, binders] + rw [hparamsLength, hfieldsLength, hspecializedLength, + hparamsTelLength, gen.minorTypes_length, view.constructor_eq, + hBsLength] + simp + obtain ⟨hlhsTel, lhsType, hlhsBody⟩ := + VEnv.HasType.lamN_wf henv.ordered hΓ hlhs + obtain ⟨hrhsTel, rhsType, hrhsBody⟩ := + VEnv.HasType.lamN_wf henv.ordered hΓ hrhs + have hlhsSpine := hcaps'.retarget hcapturesLength lhsType + have hrhsSpine := hcaps'.retarget hcapturesLength rhsType + have hcollapseL := VEnv.IsDefEq.appN_lamN henv.ordered + hlhsTel hlhsBody hlhsSpine hcapturesLength + have hcollapseR := VEnv.IsDefEq.appN_lamN henv.ordered + hrhsTel hrhsBody hrhsSpine hcapturesLength + have hregisteredRule : env.IsDefEq U Γ + ((view.generation.rule 0 view.constructor).lhs.instL pLevels) + ((view.generation.rule 0 view.constructor).rhs.instL pLevels) + ((view.generation.rule 0 view.constructor).type.instL pLevels) := + .extra hregistered hpLevelsWF hpLevelsLength + have happlied := VEnv.IsDefEq.appN_congr hregisteredRule hcaps + rw [show (view.generation.rule 0 view.constructor).lhs = + VExpr.lamN binders lhsBody from rfl, + show (view.generation.rule 0 view.constructor).rhs = + VExpr.lamN binders rhsBody from rfl, + VExpr.instL_lamN] at happlied + simp only [VExpr.instL_lamN] at happlied + have hiotaBodies : env.IsDefEqU U Γ + (VExpr.instRev (lhsBody.instL pLevels) + (params ++ [code.typeFn, code.minor] ++ fields)) + (VExpr.instRev (rhsBody.instL pLevels) + (params ++ [code.typeFn, code.minor] ++ fields)) := + VEnv.IsDefEqU.trans henv hΓ ⟨_, hcollapseL.symm⟩ + (VEnv.IsDefEqU.trans henv hΓ ⟨_, happlied⟩ ⟨_, hcollapseR⟩) + have hconstructorMem : view.constructor ∈ + view.generation.block.ctorPairs := by + simp [view.constructor_eq] + have hresultIndices : view.constructor.view.resultIndices = [] := by + apply List.length_eq_zero_iff.1 + rw [S.viewResultIndices_length hconstructorMem] + simp [view.checked_indices_eq] + have hfieldsLengthRaw : fields.length = m := by + exact hfieldsLength.trans (hspecializedLength.trans hBsLength.symm) + have hprefixLength : + (params ++ [code.typeFn, code.minor]).length = view.nparams + 2 := by + simp [hparamsLength] + have hcapturesLength' : + (params ++ [code.typeFn, code.minor] ++ fields).length = + view.nparams + 2 + m := by + simp [hparamsLength, hfieldsLengthRaw] + omega + have hsegCommon : + (VExpr.bvarRevRange m (view.nparams + 2)).map + (VExpr.instRev · + (params ++ [code.typeFn, code.minor] ++ fields)) = + params ++ [code.typeFn, code.minor] := by + have h := VExpr.map_instRev_bvarRevRange_seg + (params ++ [code.typeFn, code.minor] ++ fields) + (view.nparams + 2) m (by rw [hcapturesLength']; omega) + rw [← hparamsLength] at h ⊢ + rw [show (params ++ [code.typeFn, code.minor] ++ fields).length - + m - (params.length + 2) = 0 by + simp only [List.length_append, List.length_cons, List.length_nil] + rw [hfieldsLengthRaw] + omega, + List.drop_zero] at h + rw [List.take_append, + show params.length + 2 = + (params ++ [code.typeFn, code.minor]).length by simp, + List.take_length] at h + simpa using h + have hsegParams : + (VExpr.bvarRevRange (m + 2) view.nparams).map + (VExpr.instRev · + (params ++ [code.typeFn, code.minor] ++ fields)) = params := by + have h := VExpr.map_instRev_bvarRevRange_seg + (params ++ [code.typeFn, code.minor] ++ fields) + view.nparams (m + 2) (by rw [hcapturesLength']; omega) + rw [← hparamsLength] at h ⊢ + rw [show (params ++ [code.typeFn, code.minor] ++ fields).length - + (m + 2) - params.length = 0 by + simp only [List.length_append, List.length_cons, List.length_nil] + rw [hfieldsLengthRaw] + omega, + List.drop_zero] at h + simpa using h + have hsegFields : + (VExpr.bvarRevRange 0 m).map + (VExpr.instRev · + (params ++ [code.typeFn, code.minor] ++ fields)) = fields := by + have h := VExpr.map_instRev_bvarRevRange_seg + (params ++ [code.typeFn, code.minor] ++ fields) m 0 + (by rw [hcapturesLength']; omega) + rw [show (params ++ [code.typeFn, code.minor] ++ fields).length - + 0 - m = view.nparams + 2 by rw [hcapturesLength']; omega, + show view.nparams + 2 = + (params ++ [code.typeFn, code.minor]).length by + exact hprefixLength.symm, + List.drop_left] at h + have htake : fields.take m = fields := + List.take_of_length_le (Nat.le_of_eq hfieldsLengthRaw) + rw [htake] at h + exact h + have hsourceLevels := VStructureView.sourceLevels_projectionLevels + view code.fieldSort levels hlevelsLength + have hrecLevels : gen.recLevels.map (VLevel.inst pLevels) = pLevels := by + exact VLevel.inst_map_id hpLevelsLength + have hrecConst : + VExpr.instRev + ((.const (.str gen.block.sourceType.name "rec") gen.recLevels : + VExpr).instL pLevels) + (params ++ [code.typeFn, code.minor] ++ fields) = + .const view.recursorName pLevels := by + rw [VExpr.instRev_closedN _ (by trivial)] + simp only [VExpr.instL] + rw [hrecLevels] + rfl + have hsegCommonL : + (VExpr.bvarRevRange m (view.nparams + 2)).map + (fun expression => VExpr.instRev (expression.instL pLevels) + (params ++ [code.typeFn, code.minor] ++ fields)) = + params ++ [code.typeFn, code.minor] := by + calc + _ = ((VExpr.bvarRevRange m (view.nparams + 2)).map + (VExpr.instL pLevels)).map + (VExpr.instRev · + (params ++ [code.typeFn, code.minor] ++ fields)) := by + rw [List.map_map] + exact List.map_congr_left fun _ _ => rfl + _ = _ := by + rw [VExpr.bvarRevRange_map_instL] + exact hsegCommon + have hsegParamsL : + (VExpr.bvarRevRange (m + 2) view.nparams).map + (fun expression => VExpr.instRev (expression.instL pLevels) + (params ++ [code.typeFn, code.minor] ++ fields)) = params := by + calc + _ = ((VExpr.bvarRevRange (m + 2) view.nparams).map + (VExpr.instL pLevels)).map + (VExpr.instRev · + (params ++ [code.typeFn, code.minor] ++ fields)) := by + rw [List.map_map] + exact List.map_congr_left fun _ _ => rfl + _ = _ := by + rw [VExpr.bvarRevRange_map_instL] + exact hsegParams + have hsegFieldsL : + (VExpr.bvarRevRange 0 m).map + (fun expression => VExpr.instRev (expression.instL pLevels) + (params ++ [code.typeFn, code.minor] ++ fields)) = fields := by + calc + _ = ((VExpr.bvarRevRange 0 m).map + (VExpr.instL pLevels)).map + (VExpr.instRev · + (params ++ [code.typeFn, code.minor] ++ fields)) := by + rw [List.map_map] + exact List.map_congr_left fun _ _ => rfl + _ = _ := by + rw [VExpr.bvarRevRange_map_instL] + exact hsegFields + have hidxRNil : idxR = [] := by + simp [idxR, VInductDecl.NormalizedCtor.resultIndicesR, + hresultIndices] + have hrecBaseShape : + VExpr.instRev (recBase.instL pLevels) + (params ++ [code.typeFn, code.minor] ++ fields) = + VExpr.appN (.const view.recursorName pLevels) + (params ++ [code.typeFn, code.minor]) := by + rw [show recBase = VExpr.appN + (.const (.str gen.block.sourceType.name "rec") gen.recLevels) + (VExpr.bvarRevRange m (view.nparams + 2)) by + unfold recBase + rw [view.constructor_eq] + rfl, + VExpr.instL_appN, VExpr.instRev_appN, hrecConst] + rw [List.map_map] + exact congrArg (VExpr.appN (.const view.recursorName pLevels)) + hsegCommonL + have hctorShape : + VExpr.instRev (ctorApp.instL pLevels) + (params ++ [code.typeFn, code.minor] ++ fields) = + VExpr.appN (.const view.constructorName levels) + (params ++ fields) := by + rw [show ctorApp = VExpr.appN + (.const view.constructorName gen.sourceLevels) + (VExpr.bvarRevRange (m + 2) view.nparams ++ + VExpr.bvarRevRange 0 m) by + unfold ctorApp + rw [view.constructor_eq] + rfl, + VExpr.instL_appN, VExpr.instRev_appN] + rw [VExpr.instRev_closedN _ (by trivial)] + simp only [VExpr.instL] + rw [hsourceLevels] + simp only [List.map_append, List.map_map, Function.comp_def] + rw [hsegParamsL, hsegFieldsL] + have hleftShape : + VExpr.instRev (lhsBody.instL pLevels) + (params ++ [code.typeFn, code.minor] ++ fields) = + VExpr.appN (.const view.recursorName pLevels) + (params ++ [code.typeFn, code.minor, + VExpr.appN (.const view.constructorName levels) + (params ++ fields)]) := by + rw [show lhsBody = VExpr.appN recBase (idxR ++ [ctorApp]) by + rfl, + VExpr.instL_appN, VExpr.instRev_appN, hrecBaseShape, hidxRNil, + List.nil_append] + simp only [List.map_cons, List.map_nil, hctorShape] + rw [← VExpr.appN_append] + simp only [List.append_assoc] + rfl + have hminorCapture : + VExpr.instRev (.bvar m) + (params ++ [code.typeFn, code.minor] ++ fields) = + code.minor := by + have h := VExpr.map_instRev_bvarRevRange_seg + (params ++ [code.typeFn, code.minor] ++ fields) 1 m + (by rw [hcapturesLength']; omega) + rw [show (params ++ [code.typeFn, code.minor] ++ fields).length - + m - 1 = params.length + 1 by + rw [hcapturesLength', hparamsLength] + omega] at h + simpa [VExpr.bvarRevRange] using h + have hrightBodyShape : + rhsBody = VExpr.appN (.bvar m) (VExpr.bvarRevRange 0 m) := by + simp [rhsBody, ihs, rs, gen, + VInductDecl.NormalizedCtor.recArgsR, view.recursive_eq, + view.constructor_eq] + have hrightShape : + VExpr.instRev (rhsBody.instL pLevels) + (params ++ [code.typeFn, code.minor] ++ fields) = + VExpr.appN code.minor fields := by + rw [hrightBodyShape, VExpr.instL_appN, + VExpr.bvarRevRange_map_instL, VExpr.instRev_appN] + simp only [VExpr.instL] + rw [hminorCapture, hsegFields] + rw [hleftShape, hrightShape] at hiotaBodies + obtain ⟨selectedType, hselectedType, -⟩ := + view.projectionCodes_get?_typeFn levels params hcode + have hidxLt : idx < (view.specializedFields levels params).length := + (List.getElem?_eq_some_iff.1 hselectedType).1 + let q := (view.specializedFields levels params).length - 1 - idx + have hqLt : q < (view.specializedFields levels params).length := by + simp only [q] + omega + have hselectedReverse : + (view.specializedFields levels params).reverse[q]? = + some selectedType := by + rw [List.getElem?_reverse hqLt, + show (view.specializedFields levels params).length - 1 - q = idx by + simp only [q] + omega, + hselectedType] + have hselectedCtx : + ((view.specializedFields levels params).reverse ++ Γ)[q]? = + some selectedType := by + rw [List.getElem?_append_left (by simpa using hqLt), + hselectedReverse] + have hminorBodyType : env.HasType U + ((view.specializedFields levels params).reverse ++ Γ) + (.bvar q) (selectedType.liftN (q + 1)) := + .bvar (Lookup.of_getElem? hselectedCtx) + have hminorSpine := hfieldsSpine.retarget hfieldsLength + (selectedType.liftN (q + 1)) + have hminorBetaRaw := VEnv.IsDefEq.appN_lamN henv.ordered + hsortTel.toOnTel hminorBodyType hminorSpine hfieldsLength + have hfieldInst : VExpr.instRev (.bvar q) fields = field := by + have h := VExpr.map_instRev_bvarRevRange_seg fields 1 q + (by rw [hfieldsLength]; exact Nat.add_one_le_iff.2 hqLt) + rw [show fields.length - q - 1 = idx by + rw [hfieldsLength] + simp only [q] + omega] at h + obtain ⟨hidxFields, hfieldGet⟩ := + List.getElem?_eq_some_iff.1 hfield + rw [List.drop_eq_getElem_cons hidxFields, hfieldGet, + List.take_succ_cons] at h + simpa [VExpr.bvarRevRange] using h + have hminorBeta : env.IsDefEqU U Γ + (VExpr.appN code.minor fields) field := by + refine ⟨VExpr.instRev (selectedType.liftN (q + 1)) fields, ?_⟩ + rw [hminorShape] + simpa only [hfieldInst] using hminorBetaRaw + exact VEnv.IsDefEqU.trans henv hΓ hprojectorToRule + (VEnv.IsDefEqU.trans henv hΓ hiotaBodies hminorBeta) + +/-- Environment-indexed projection semantics. + +The universe and parameter spines are explicit. The major premise must have +the exact instantiated structure type, and the result is the unique program +computed by the registered view. -/ +structure TrProj (env : VEnv) (U : Nat) (Γ : List VExpr) + (view : VStructureView) (levels : List VLevel) (params : List VExpr) + (idx : Nat) (major result : VExpr) : Prop where + viewWF : VStructureView.WF view env + levelsWF : ∀ level ∈ levels, level.WF U + levels_length : levels.length = view.uvars + params_length : params.length = view.nparams + paramsSpine : ∃ resultLevel, + env.SpineWF U Γ (view.familyType.instL levels) + params (.sort resultLevel) + majorType : env.HasType U Γ major (view.structureType levels params) + program : ∃ code : VStructureView.ProjectionCode, + (view.projectionCodes levels params)[idx]? = some code ∧ + result = .app code.projector major ∧ + env.HasType U Γ code.projector + (.forallE (view.structureType levels params) + (.app code.typeFn.lift (.bvar 0))) + +/-- The projection-specific output of registered constructor-head inversion. + +This package performs no iota computation. It only aligns a constructor +normal form and one selected runtime argument with the canonical registered +view, and supplies the typed spines needed by +`projector_constructor_exact`. -/ +structure ProjectionConstructorAlignment (env : VEnv) (U : Nat) + (Γ : List VExpr) (view : VStructureView) (levels : List VLevel) + (params : List VExpr) (idx : Nat) + (code : VStructureView.ProjectionCode) + (runtimeConstructorName : Name) (runtimeMajor runtimeField : VExpr) where + constructor_name_eq : runtimeConstructorName = view.constructorName + fields : List VExpr + field : VExpr + fields_length : + fields.length = (view.specializedFields levels params).length + field_get : fields[idx]? = some field + constructorType : env.HasType U Γ + (VExpr.appN (.const view.constructorName levels) (params ++ fields)) + (view.structureType levels params) + fieldsSpine : env.SpineWF U Γ + (VExpr.forallN (view.specializedFields levels params) (.sort .zero)) + fields (.sort .zero) + captures : ∃ B, env.SpineWF U Γ + ((view.generation.rule 0 view.constructor).type.instL + (view.projectionLevels code.fieldSort levels)) + (params ++ [code.typeFn, code.minor] ++ fields) B + major_eq : env.IsDefEqU U Γ runtimeMajor + (VExpr.appN (.const view.constructorName levels) (params ++ fields)) + field_eq : env.IsDefEqU U Γ runtimeField field + +/-- Consume registered-head alignment with the separately proved exact iota +theorem. This keeps the transitional injectivity boundary from hiding the +projection computation itself. -/ +theorem TrProj.projector_constructor_aligned + (self : VEnv.TrProj env U Γ view levels params idx major result) + (henv : env.WF) (hΓ : OnCtx Γ (env.IsType U)) + {code : VStructureView.ProjectionCode} + (hcode : (view.projectionCodes levels params)[idx]? = some code) + (hprojector : env.HasType U Γ code.projector + (.forallE (view.structureType levels params) + (.app code.typeFn.lift (.bvar 0)))) + {runtimeMajor runtimeField : VExpr} + {runtimeConstructorName : Name} + (alignment : ProjectionConstructorAlignment env U Γ view levels params idx + code runtimeConstructorName runtimeMajor runtimeField) : + env.IsDefEqU U Γ (.app code.projector runtimeMajor) runtimeField := by + have hmajorEq := alignment.major_eq.of_r henv hΓ + alignment.constructorType + have hmajorCongr : env.IsDefEqU U Γ + (.app code.projector runtimeMajor) + (.app code.projector + (VExpr.appN (.const view.constructorName levels) + (params ++ alignment.fields))) := + ⟨_, hprojector.appDF hmajorEq⟩ + obtain ⟨captureType, hcaptures⟩ := alignment.captures + have hiota := self.viewWF.projector_constructor_exact henv hΓ + self.levelsWF self.levels_length self.params_length self.paramsSpine + hcode hprojector alignment.fields_length alignment.field_get + alignment.constructorType alignment.fieldsSpine hcaptures + exact VEnv.IsDefEqU.trans henv hΓ hmajorCongr + (VEnv.IsDefEqU.trans henv hΓ hiota alignment.field_eq.symm) + +theorem TrProj.project_eq + (self : VEnv.TrProj env U Γ view levels params idx major result) : + VStructureView.project? view levels params idx major = some result := by + obtain ⟨code, hcode, rfl, -⟩ := self.program + simp [VStructureView.project?, hcode] + +theorem TrProj.type_eq + (self : VEnv.TrProj env U Γ view levels params idx major result) : + ∃ code : VStructureView.ProjectionCode, + VStructureView.projectionType? view levels params idx major = + some (VExpr.app code.typeFn major) := by + obtain ⟨code, hcode, _, -⟩ := self.program + exact ⟨code, by simp [VStructureView.projectionType?, hcode]⟩ + +/-- A fixed checked view, universe/parameter instantiation, field index, and +major determine the projection result syntactically. -/ +theorem TrProj.result_eq + (self : VEnv.TrProj env U Γ view levels params idx major result) + (other : VEnv.TrProj env U Γ view levels params idx major result') : + result = result' := + Option.some.inj (self.project_eq.symm.trans other.project_eq) + +/-- Projection evidence is stable when the registered environment is +extended without changing any existing constants or reduction rules. -/ +theorem TrProj.mono {env env' : VEnv} (henv : env ≤ env') + (self : VEnv.TrProj env U Γ view levels params idx major result) : + VEnv.TrProj env' U Γ view levels params idx major result where + viewWF := self.viewWF.mono henv + levelsWF := self.levelsWF + levels_length := self.levels_length + params_length := self.params_length + paramsSpine := self.paramsSpine.imp fun _ h => h.monoProjection henv + majorType := self.majorType.mono henv + program := self.program.imp fun _ ⟨hcode, hresult, htype⟩ => + ⟨hcode, hresult, htype.mono henv⟩ + +/-- Weakening acts pointwise on the explicit parameters, major, and computed +projection program. -/ +theorem TrProj.weakN (henv : env.Ordered) + (W : Ctx.LiftN n k Γ Γ') + (self : VEnv.TrProj env U Γ view levels params idx major result) : + VEnv.TrProj env U Γ' view levels + (params.map fun param => param.liftN n k) idx + (major.liftN n k) (result.liftN n k) := by + refine { + viewWF := self.viewWF + levelsWF := self.levelsWF + levels_length := self.levels_length + params_length := by simpa using self.params_length + paramsSpine := ?_ + majorType := by simpa using self.majorType.weakN henv W + program := ?_ } + · have hfamilyClosed : (view.familyType.instL levels).ClosedN 0 := by + simpa using (henv.closedC self.viewWF.family).instL + obtain ⟨resultLevel, hspine⟩ := self.paramsSpine + refine ⟨resultLevel, ?_⟩ + have hspine' := hspine.weakN henv W + rw [hfamilyClosed.liftN_eq (Nat.zero_le _)] at hspine' + simpa [VExpr.liftN] using hspine' + · obtain ⟨code, hcode, rfl, htype⟩ := self.program + refine ⟨code.liftN n k, ?_, rfl, ?_⟩ + rw [← self.viewWF.projectionCodes_liftN henv levels params + self.params_length n k] + simp only [List.getElem?_map, hcode, Option.map_some] + simpa [VStructureView.ProjectionCode.liftN, VExpr.liftN, + VExpr.liftN_lift_projection] using htype.weakN henv W + +/-- General context lifting, derived one inserted binder at a time from +`weakN`. -/ +theorem TrProj.weak' (henv : env.Ordered) + (W : Ctx.Lift' l Γ Γ') + (self : VEnv.TrProj env U Γ view levels params idx major result) : + VEnv.TrProj env U Γ' view levels + (params.map fun param => param.lift' l) idx + (major.lift' l) (result.lift' l) := by + generalize hdepth : l.depth = depth + induction depth generalizing l Γ' with + | zero => + have hctx := W.depth_zero hdepth + subst Γ' + simpa [VExpr.lift'_depth_zero (l := l) hdepth] using self + | succ depth ih => + obtain ⟨tail, k, rfl, rfl⟩ := Lift.depth_succ hdepth + obtain ⟨Γ₁, W₁, W₂⟩ := W.of_cons_skip + have h := (ih W₁ Lift.depth_consN).weakN henv W₂ + rw [Lift.consN_skip_eq] + have hlift : ∀ e : VExpr, + e.lift' ((tail.consN k).comp + (Lift.refl.skip.consN k)) = + (e.lift' (tail.consN k)).liftN 1 k := by + intro e + rw [VExpr.lift'_comp, ← Lift.skipN_one, + VExpr.lift'_consN_skipN] + have hparams : + params.map (fun param => param.lift' ((tail.consN k).comp + (Lift.refl.skip.consN k))) = + (params.map fun param => param.lift' (tail.consN k)).map + (fun param => param.liftN 1 k) := by + rw [List.map_map] + exact List.map_congr_left fun param _ => hlift param + rw [hparams, hlift major, hlift result] + exact h + +/-- Substitution acts pointwise on the explicit parameters, major, and +computed projection program. -/ +theorem TrProj.instN (henv : env.Ordered) + (W : Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ) + (h₀ : env.HasType U Γ₀ e₀ A₀) + (self : VEnv.TrProj env U Γ₁ view levels params idx major result) : + VEnv.TrProj env U Γ view levels + (params.map fun param => param.inst e₀ k) idx + (major.inst e₀ k) (result.inst e₀ k) := by + refine { + viewWF := self.viewWF + levelsWF := self.levelsWF + levels_length := self.levels_length + params_length := by simpa using self.params_length + paramsSpine := ?_ + majorType := by simpa using self.majorType.instN henv W h₀ + program := ?_ } + · have hfamilyClosed : (view.familyType.instL levels).ClosedN 0 := by + simpa using (henv.closedC self.viewWF.family).instL + obtain ⟨resultLevel, hspine⟩ := self.paramsSpine + refine ⟨resultLevel, ?_⟩ + have hspine' := hspine.instNProjection henv W h₀ + rw [hfamilyClosed.instN_eq (Nat.zero_le _)] at hspine' + simpa [VExpr.inst] using hspine' + · obtain ⟨code, hcode, rfl, htype⟩ := self.program + refine ⟨code.instN e₀ k, ?_, rfl, ?_⟩ + rw [← self.viewWF.projectionCodes_instN henv levels params + self.params_length e₀ k] + simp only [List.getElem?_map, hcode, Option.map_some] + simpa [VStructureView.ProjectionCode.instN, VExpr.inst, + ← VExpr.lift_instN_lo] using htype.instN henv W h₀ + +/-- Transport projection evidence to a definitionally equal context and a +new major already checked against the same instantiated structure type. -/ +theorem TrProj.defeqDFC (henv : env.Ordered) + (hΓ : env.IsDefEqCtx U Γ₀ Γ₁ Γ₂) + (majorType' : env.HasType U Γ₂ major' + (view.structureType levels params)) + (self : VEnv.TrProj env U Γ₁ view levels params idx major result) : + ∃ result', VEnv.TrProj env U Γ₂ view levels params idx major' result' := by + obtain ⟨code, hcode, -, htype⟩ := self.program + refine ⟨.app code.projector major', { + viewWF := self.viewWF + levelsWF := self.levelsWF + levels_length := self.levels_length + params_length := self.params_length + paramsSpine := self.paramsSpine.imp fun _ h => h.defeqDFC henv hΓ + majorType := majorType' + program := ⟨code, hcode, rfl, + htype.defeqDFC henv hΓ⟩ }⟩ + +/-- Universe instantiation acts pointwise on the explicit structure +universes and parameters, and on the recursor program they determine. -/ +theorem TrProj.instL {ls : List VLevel} + (hls : ∀ level ∈ ls, level.WF U') + (self : VEnv.TrProj env U Γ view levels params idx major result) : + VEnv.TrProj env U' (Γ.map (VExpr.instL ls)) view + (levels.map (VLevel.inst ls)) + (params.map (VExpr.instL ls)) idx + (major.instL ls) (result.instL ls) := by + refine { + viewWF := self.viewWF + levelsWF := ?_ + levels_length := by simpa using self.levels_length + params_length := by simpa using self.params_length + paramsSpine := ?_ + majorType := by simpa using self.majorType.instL hls + program := ?_ } + · intro level hlevel + obtain ⟨sourceLevel, hsourceLevel, rfl⟩ := List.mem_map.1 hlevel + exact VLevel.WF.inst hls + · obtain ⟨resultLevel, hspine⟩ := self.paramsSpine + refine ⟨resultLevel.inst ls, ?_⟩ + simpa [VExpr.instL, VExpr.instL_instL] using hspine.instL hls + · obtain ⟨code, hcode, rfl, htype⟩ := self.program + refine ⟨code.instL ls, ?_, ?_, ?_⟩ + · rw [← VStructureView.projectionCodes_instL] + simp only [List.getElem?_map, hcode, Option.map_some] + · rfl + · simpa [VStructureView.ProjectionCode.instL, VExpr.instL, + VExpr.instL_liftN] using htype.instL hls + +/-- The registered-structure constant-head inversion boundary. + +The four conclusions are the projection-specific eliminators supplied by +constant-head injectivity: a type assigned to a syntactically weakened major +recovers an instantiation below the inserted context; definitionally equal +majors recover the same registered view/instantiation strongly enough for the +generated projector programs to be definitionally equal; a runtime +constructor head recovers the registered constructor name; and that head plus +one selected argument is aligned with the registered constructor and field. +The last conclusion deliberately provides only typed alignment—the iota step +remains the proved `projector_constructor_exact` theorem. + +Its eventual proof uses `IsDefEqU.weakN_iff` together with injectivity of +registered inductive heads. Keeping the boundary in Theory makes the +temporary Spec-16/17 dependency explicit instead of leaving Verify's +structural laws as local holes. -/ +structure RegisteredStructureHeadInversion (env : VEnv) : Prop where + weak'_inv : + ∀ {U : Nat} {Γ Γ' : List VExpr} {view : VStructureView} + {levels : List VLevel} {params : List VExpr} {idx : Nat} + {major result : VExpr} {lift : Lift}, + OnCtx Γ' (env.IsType U) → + Ctx.Lift' lift Γ Γ' → + env.TrProj U Γ' view levels params idx (major.lift' lift) result → + ∃ params' result', + env.TrProj U Γ view levels params' idx major result' + unique : + ∀ {U : Nat} {Γ₁ Γ₂ : List VExpr} + {view₁ view₂ : VStructureView} + {levels₁ levels₂ : List VLevel} {params₁ params₂ : List VExpr} + {idx : Nat} {major₁ major₂ result₁ result₂ : VExpr}, + env.IsDefEqCtx U [] Γ₁ Γ₂ → + env.TrProj U Γ₁ view₁ levels₁ params₁ idx major₁ result₁ → + env.TrProj U Γ₂ view₂ levels₂ params₂ idx major₂ result₂ → + env.IsDefEqU U Γ₁ major₁ major₂ → + env.IsDefEqU U Γ₁ result₁ result₂ + constructor_name_inv : + ∀ {U : Nat} {Γ : List VExpr} {view : VStructureView} + {levels : List VLevel} {params : List VExpr} {idx : Nat} + {major result runtimeMajor : VExpr} + {constructorName : Name} {constructorLevels : List VLevel} + {constructorArgs : List VExpr}, + OnCtx Γ (env.IsType U) → + env.TrProj U Γ view levels params idx major result → + runtimeMajor = VExpr.appN + (.const constructorName constructorLevels) constructorArgs → + env.IsDefEqU U Γ runtimeMajor major → + constructorName = view.constructorName + constructor_inv : + ∀ {U : Nat} {Γ : List VExpr} {view : VStructureView} + {levels : List VLevel} {params : List VExpr} {idx : Nat} + {major result : VExpr} {code : VStructureView.ProjectionCode} + {runtimeMajor runtimeField : VExpr} + {constructorName : Name} {constructorLevels : List VLevel} + {constructorArgs : List VExpr}, + OnCtx Γ (env.IsType U) → + env.TrProj U Γ view levels params idx major result → + (view.projectionCodes levels params)[idx]? = some code → + runtimeMajor = VExpr.appN + (.const constructorName constructorLevels) constructorArgs → + constructorArgs[view.nparams + idx]? = some runtimeField → + env.IsDefEqU U Γ runtimeMajor major → + Nonempty (ProjectionConstructorAlignment env U Γ view levels params idx + code constructorName runtimeMajor runtimeField) + +set_option warn.sorry false in +/-- Public Tier-R registered-head inversion statement. Spec-16/17 discharge +the underlying constant-head theorem; projection structural laws consume only +this stable interface and therefore shed `sorryAx` automatically when it is +proved. -/ +theorem WF.registeredStructureHeadInversion + (self : VEnv.WF env) : RegisteredStructureHeadInversion env := by + sorry + +#guard_named_axioms Ix.Theory.Named.VEnv.WF.registeredStructureHeadInversion [propext, sorryAx, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.TrProj.result_eq [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.TrProj.mono [propext, Quot.sound] + +end VEnv + +end Ix.Theory.Named diff --git a/Ix/Theory/Named/Quot.lean b/Ix/Theory/Named/Quot.lean new file mode 100644 index 000000000..c509fcef0 --- /dev/null +++ b/Ix/Theory/Named/Quot.lean @@ -0,0 +1,27 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.VEnv +import Ix.Theory.Named.Meta + +namespace Ix.Theory.Named + +def eqConst := vconst(type_of% @Eq) +def quotConst := vconst(type_of% @Quot) +def quotMkConst := vconst(type_of% @Quot.mk) +def quotLiftConst := vconst(type_of% @Quot.lift) +def quotIndConst := vconst(type_of% @Quot.ind) +def quotDefEq := vdefeq(α r β f c a => @Quot.lift α r β f c (Quot.mk r a) ≡ f a) + +def VEnv.QuotReady (env : VEnv) : Prop := + env.constants ``Eq = some eqConst + +def VEnv.addQuot (env : VEnv) : Option VEnv := do + let env ← env.addConst ``Quot quotConst + let env ← env.addConst ``Quot.mk quotMkConst + let env ← env.addConst ``Quot.lift quotLiftConst + let env ← env.addConst ``Quot.ind quotIndConst + env.addDefEq quotDefEq diff --git a/Ix/Theory/Named/Reference/Declaration.lean b/Ix/Theory/Named/Reference/Declaration.lean new file mode 100644 index 000000000..5b9ed8970 --- /dev/null +++ b/Ix/Theory/Named/Reference/Declaration.lean @@ -0,0 +1,33 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Lean.Declaration + + +namespace Lean + +/-- +The value of a constant for the purpose of delta reduction: definitions and theorems have one, +opaques do not. This mirrors the C++ `constant_info::has_value()`/`get_value()` pair. + +This is deliberately *not* `ConstantInfo.value?`, whose meaning changed in lean4#12973: it now +excludes theorems, but `constant_info::has_value()` was left untouched by that PR, so the kernel +still delta-unfolds theorems: `type_checker::is_delta` uses this predicate to select candidates, +and `instantiate_value_lparams` uses it before reading their values. +-/ +def ConstantInfo.deltaValue? : ConstantInfo → Option Expr + | .defnInfo {value, ..} => some value + | .thmInfo {value, ..} => some value + | _ => none + +namespace ReducibilityHints + +def lt' : ReducibilityHints → ReducibilityHints → Bool -- lean4#2750 + | _, .opaque => false + | .abbrev, _ => false + | .opaque, _ => true + | _, .abbrev => true + | .regular d₁, .regular d₂ => d₁ < d₂ diff --git a/Ix/Theory/Named/Reference/Environment.lean b/Ix/Theory/Named/Reference/Environment.lean new file mode 100644 index 000000000..8bc74f3f6 --- /dev/null +++ b/Ix/Theory/Named/Reference/Environment.lean @@ -0,0 +1,125 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Reference.TypeChecker +import Ix.Theory.Named.Reference.Quot +import Ix.Theory.Named.Reference.Inductive.Add +import Ix.Theory.Named.Reference.Primitive + +namespace Ix.Theory.Named +open Lean hiding Environment Exception +open TypeChecker Kernel Environment + +open private Lean.Kernel.Environment.add from Lean.Environment + +def checkConstantVal (env : Environment) (v : ConstantVal) (allowPrimitive := false) : M Unit := do + checkName env v.name allowPrimitive + checkDuplicatedUnivParams v.levelParams + checkNoMVarNoFVar env v.name v.type + let sort ← checkType v.type + _ ← ensureSort sort v.type + +def addAxiom (env : Environment) (v : AxiomVal) (check := true) (fuel : FuelConfig := {}) : + Except Exception Environment := do + if check then + _ ← (checkConstantVal env v.toConstantVal).run env + (safety := if v.isUnsafe then .unsafe else .safe) (lparams := v.levelParams) (fuel := fuel) + return env.add (.axiomInfo v) + +def addDefinition (env : Environment) (v : DefinitionVal) + (check := true) (fuel : FuelConfig := {}) : Except Exception Environment := do + if let .unsafe := v.safety then + -- Meta definition can be recursive. + -- So, we check the header, add, and then type check the body. + if check then + _ ← (checkConstantVal env v.toConstantVal).run env + (safety := .unsafe) (lparams := v.levelParams) (fuel := fuel) + let env' := env.add (.axiomInfo { v with isUnsafe := true }) + if check then + checkNoMVarNoFVar env' v.name v.value + M.run env' (safety := .unsafe) (lctx := {}) (lparams := v.levelParams) (fuel := fuel) do + let valType ← TypeChecker.checkType v.value + if !(← isDefEq valType v.type) then + throw <| .declTypeMismatch env' (.defnDecl v) valType + else if check then + M.run env (safety := .safe) (lctx := {}) (lparams := v.levelParams) (fuel := fuel) do + checkConstantVal env v.toConstantVal (← checkPrimitiveDef v) + checkNoMVarNoFVar env v.name v.value + let valType ← TypeChecker.checkType v.value + if !(← isDefEq valType v.type) then + throw <| .declTypeMismatch env (.defnDecl v) valType + return env.add (.defnInfo v) + +def addTheorem (env : Environment) (v : TheoremVal) (check := true) (fuel : FuelConfig := {}) : + Except Exception Environment := do + if check then + -- TODO(Leo): we must add support for handling tasks here + M.run env (safety := .safe) (lctx := {}) (lparams := v.levelParams) (fuel := fuel) do + checkConstantVal env v.toConstantVal + if !(← isProp v.type) then + throw <| .thmTypeIsNotProp env v.name v.type + checkNoMVarNoFVar env v.name v.value + let valType ← TypeChecker.checkType v.value + if !(← isDefEq valType v.type) then + throw <| .declTypeMismatch env (.thmDecl v) valType + return env.add (.thmInfo v) + +def addOpaque (env : Environment) (v : OpaqueVal) (check := true) (fuel : FuelConfig := {}) : + Except Exception Environment := do + if check then + M.run env (safety := .safe) (lctx := {}) (lparams := v.levelParams) (fuel := fuel) do + checkConstantVal env v.toConstantVal + checkNoMVarNoFVar env v.name v.value + let valType ← TypeChecker.checkType v.value + if !(← isDefEq valType v.type) then + throw <| .declTypeMismatch env (.opaqueDecl v) valType + return env.add (.opaqueInfo v) + +def addMutual (env : Environment) (vs : List DefinitionVal) + (check := true) (fuel : FuelConfig := {}) : Except Exception Environment := do + let v₀ :: _ := vs | throw <| .other "invalid empty mutual definition" + if let .safe := v₀.safety then + throw <| .other "invalid mutual definition, declaration is not tagged as unsafe/partial" + if check then + M.run env (safety := v₀.safety) (lctx := {}) (lparams := v₀.levelParams) (fuel := fuel) do + let mut found : NameSet := {} + for v in vs do + if v.safety != v₀.safety then + throw <| .other + "invalid mutual definition, declarations must have the same safety annotation" + -- The whole block is checked under one set of level parameters, so they must agree; + -- lean4#14608 adds the same check to the C++ kernel. + if v.levelParams != v₀.levelParams then + throw <| .other + "invalid mutual definition, declarations must have the same universe level parameters" + if found.contains v.name then + throw <| .other s!"invalid mutual definition, duplicate declaration name '{v.name}'" + found := found.insert v.name + checkConstantVal env v.toConstantVal + let env' := vs.foldl (init := env) fun env' v => + env'.add (.axiomInfo { v with isUnsafe := v₀.safety == .unsafe }) + if check then + M.run env' (safety := v₀.safety) (lctx := {}) (lparams := v₀.levelParams) (fuel := fuel) do + for v in vs do + checkNoMVarNoFVar env' v.name v.value + let valType ← TypeChecker.checkType v.value + if !(← isDefEq valType v.type) then + throw <| .declTypeMismatch env' (.mutualDefnDecl vs) valType + return vs.foldl (fun env' v => env'.add (.defnInfo v)) env + +/-- Type check given declaration and add it to the environment -/ +def addDecl (env : Environment) (decl : Declaration) (check := true) (fuel : FuelConfig := {}) : + Except Exception Environment := do + match decl with + | .axiomDecl v => addAxiom env v check fuel + | .defnDecl v => addDefinition env v check fuel + | .thmDecl v => addTheorem env v check fuel + | .opaqueDecl v => addOpaque env v check fuel + | .mutualDefnDecl v => addMutual env v check fuel + | .quotDecl => addQuot env + | .inductDecl lparams nparams types isUnsafe => + let allowPrimitive ← checkPrimitiveInductive env lparams nparams types isUnsafe + addInductive env lparams nparams types isUnsafe allowPrimitive fuel diff --git a/Ix/Theory/Named/Reference/Environment/Basic.lean b/Ix/Theory/Named/Reference/Environment/Basic.lean new file mode 100644 index 000000000..9ac92b87c --- /dev/null +++ b/Ix/Theory/Named/Reference/Environment/Basic.lean @@ -0,0 +1,226 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Lean.Environment +import Batteries.Tactic.OpenPrivate + +namespace Lean.Kernel.Environment + +def contains (env : Environment) (n : Name) : Bool := + env.constants.contains n + +def get (env : Environment) (n : Name) : Except Exception ConstantInfo := + match env.find? n with + | some ci => pure ci + | none => throw <| .unknownConstant env n + +def checkDuplicatedUnivParams : List Name → Except Exception Unit + | [] => pure () + | p :: ls => do + if p ∈ ls then + throw <| .other + s!"failed to add declaration to environment, duplicate universe level parameter: '{p}'" + checkDuplicatedUnivParams ls + +def checkNoMVar (env : Environment) (n : Name) (e : Expr) : Except Exception Unit := do + if e.hasMVar then + throw <| .declHasMVars env n e + +def checkNoFVar (env : Environment) (n : Name) (e : Expr) : Except Exception Unit := do + if e.hasFVar then + throw <| .declHasFVars env n e + +def checkNoMVarNoFVar (env : Environment) (n : Name) (e : Expr) : Except Exception Unit := do + checkNoMVar env n e + checkNoFVar env n e + +def primitives : NameSet := .ofList [ + ``Bool, ``Bool.false, ``Bool.true, + ``Nat, ``Nat.zero, ``Nat.succ, + ``Nat.add, ``Nat.pred, ``Nat.sub, ``Nat.mul, ``Nat.pow, + ``Nat.gcd, ``Nat.mod, ``Nat.div, ``Nat.beq, ``Nat.ble, + ``Nat.bitwise, ``Nat.land, ``Nat.lor, ``Nat.xor, + ``Nat.shiftLeft, ``Nat.shiftRight, + ``String.ofList, ``Char.ofNat] + +/-- +Returns true iff `constName` is a non-recursive inductive datatype that has only one constructor and no indices. + +Such types have special kernel support (e.g. the eta rule). +This must be in sync with `is_non_rec_structure()`. +-/ +def isNonRecStructure (env : Environment) (constName : Name) : Bool := + match env.find? constName with + | some (.inductInfo { isRec := false, ctors := [_], numIndices := 0, .. }) => true + | _ => false + +/-- A one-constructor, unindexed structure whose constructor and generated +recursor have both reached the host environment. Family metadata is staged +before either artifact is inserted; projection verification may only demand a +registered Theory view at this later boundary. + +Unlike `isNonRecStructure`, projection readiness deliberately does not inspect +`InductiveVal.isRec`: Lean emits primitive projections for recursive structures +too (including nested-recursive structures in the Lean prelude). -/ +def isProjectionReadyStructure (env : Environment) (constName : Name) : Bool := + match env.constants.find?' constName with + | some (.inductInfo { ctors := [ctor], numIndices := 0, .. }) => + match env.constants.find?' ctor, + env.constants.find?' (mkRecName constName) with + | some (.ctorInfo _), some (.recInfo _) => true + | _, _ => false + | _ => false + +theorem isProjectionReadyStructure_false_of_no_ctorInfo + {env : Environment} {name : Name} {info : InductiveVal} + (hfind : env.constants.find?' name = some (.inductInfo info)) + (hnoCtor : ∀ ctor ctorInfo, + env.constants.find?' ctor ≠ some (.ctorInfo ctorInfo)) : + env.isProjectionReadyStructure name = false := by + cases info + rename_i constant numParams numIndices all ctors numNested isRec isUnsafe isReflexive + cases constant + unfold isProjectionReadyStructure + rw [hfind] + cases numIndices with + | succ _ => rfl + | zero => + cases ctors with + | nil => rfl + | cons ctor rest => + cases rest with + | cons _ _ => rfl + | nil => + cases hctor : env.constants.find?' ctor with + | none => simp [hctor] + | some info => + cases info <;> simp_all + +theorem isProjectionReadyStructure_false_of_numIndices_ne + {env : Environment} {name : Name} {info : InductiveVal} + (hfind : env.constants.find?' name = some (.inductInfo info)) + (hindices : info.numIndices ≠ 0) : + env.isProjectionReadyStructure name = false := by + cases info + simp_all [isProjectionReadyStructure] + +theorem isProjectionReadyStructure_false_of_not_found + {env : Environment} {name : Name} + (hfind : env.constants.find?' name = none) : + env.isProjectionReadyStructure name = false := by + simp [isProjectionReadyStructure, hfind] + +def checkName (env : Environment) (n : Name) + (allowPrimitive := false) : Except Exception Unit := do + if env.contains n then + throw <| .alreadyDeclared env n + unless allowPrimitive do + if primitives.contains n then + throw <| .other s!"unexpected use of primitive name {n}" + +open private subsumesInfo Kernel.Environment.mk EnvironmentHeader.mk moduleNames + moduleNameMap parts toEffectiveImport getData? from Lean.Environment + +def empty (mainModule : Name) (trustLevel : UInt32 := 0) : Environment := + Kernel.Environment.mk + (constants := {}) + (quotInit := false) + (diagnostics := {}) + (const2ModIdx := {}) + (extensions := #[]) + (irBaseExts := #[]) + (header := EnvironmentHeader.mk + (mainModule := mainModule) + (trustLevel := trustLevel) + (isModule := false) + (imports := #[]) + (regions := #[]) + (modules := #[]) + (moduleName2Idx := {}) + (importAllModules := #[]) + (moduleData := #[])) + +/-- A minimal kernel environment backed by an explicit constant map. + +This is used by verified staged checks (for example, while an inductive family +has been inserted but its constructors have not). Such states are real kernel +checking stages but are not importable modules, so they intentionally carry no +extensions or module metadata. -/ +def ofConstants (mainModule : Name) (constants : ConstMap) + (quotInit := false) (trustLevel : UInt32 := 0) : Environment := + Kernel.Environment.mk + (constants := constants) + (quotInit := quotInit) + (diagnostics := {}) + (const2ModIdx := {}) + (extensions := #[]) + (irBaseExts := #[]) + (header := EnvironmentHeader.mk + (mainModule := mainModule) + (trustLevel := trustLevel) + (isModule := false) + (imports := #[]) + (regions := #[]) + (modules := #[]) + (moduleName2Idx := {}) + (importAllModules := #[]) + (moduleData := #[])) + +def throwAlreadyImported (s : ImportState) (const2ModIdx : Std.HashMap Name ModuleIdx) + (modIdx : Nat) (cname : Name) : Except Exception α := do + let modName := (moduleNames s)[modIdx]! + let constModName := (moduleNames s)[const2ModIdx[cname]!.toNat]! + throw <| .other + s!"import {modName} failed, environment already contains '{cname}' from {constModName}" + +def finalizeImport (s : ImportState) (imports : Array Import) (mainModule : Name) + (trustLevel : UInt32 := 0) : Except Exception Environment := do + let modules := (moduleNames s).filterMap ((moduleNameMap s)[·]?) + let moduleData ← modules.mapM fun mod => do + let some data := getData? mod .private | + throw <| .other s!"missing data file for module {mod.module}" + return data + let numConsts := moduleData.foldl (init := 0) fun numConsts data => Id.run do + numConsts + data.constants.size + let mut const2ModIdx := .emptyWithCapacity (capacity := numConsts) + let mut constantMap := .emptyWithCapacity (capacity := numConsts) + for h : modIdx in *...moduleData.size do + let data := moduleData[modIdx] + for cname in data.constNames, cinfo in data.constants do + match constantMap.getThenInsertIfNew? cname cinfo with + | (cinfoPrev?, constantMap') => + constantMap := constantMap' + if let some cinfoPrev := cinfoPrev? then + -- Recall that the map has not been modified when `cinfoPrev? = some _`. + if subsumesInfo constantMap cinfo cinfoPrev then + constantMap := constantMap.insert cname cinfo + else if !subsumesInfo constantMap cinfoPrev cinfo then + throwAlreadyImported s const2ModIdx modIdx cname + const2ModIdx := const2ModIdx.insertIfNew cname modIdx + for cname in data.extraConstNames do + const2ModIdx := const2ModIdx.insertIfNew cname modIdx + let mut moduleName2Idx := {} + for _h : idx in [0:modules.size] do + let mod := modules[idx] + moduleName2Idx := moduleName2Idx.insert mod.module idx + + return Kernel.Environment.mk + (constants := SMap.fromHashMap constantMap false) + (quotInit := !imports.isEmpty) -- We assume `Init.Prelude` initializes quotient module + (diagnostics := {}) + (const2ModIdx := const2ModIdx) + (extensions := #[]) + (irBaseExts := #[]) + (header := EnvironmentHeader.mk + (mainModule := mainModule) + (trustLevel := trustLevel) + (isModule := false) + (imports := imports) + (regions := modules.flatMap (parts · |>.map (·.2))) + (modules := modules.map toEffectiveImport) + (moduleName2Idx := moduleName2Idx) + (importAllModules := #[]) + (moduleData := moduleData)) diff --git a/Ix/Theory/Named/Reference/Expr.lean b/Ix/Theory/Named/Reference/Expr.lean new file mode 100644 index 000000000..78a87bd2a --- /dev/null +++ b/Ix/Theory/Named/Reference/Expr.lean @@ -0,0 +1,138 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Lean.Environment + +namespace Lean +namespace Expr + +def prop : Expr := .sort .zero + +/-- Transparent structural equality with the non-strict binder behavior used +by kernel expression comparison. Unlike `Expr.eqv`, this definition is +available to the kernel evaluator, so checker branch proofs do not require an +opaque implementation equation. -/ +def structuralEq : Expr → Expr → Bool + | .bvar i, .bvar j + | .fvar i, .fvar j + | .mvar i, .mvar j + | .sort i, .sort j + | .lit i, .lit j => i == j + | .const name levels, .const name' levels' => + name == name' && levels == levels' + | .app fn arg, .app fn' arg' => + structuralEq fn fn' && structuralEq arg arg' + | .lam _ domain body _, .lam _ domain' body' _ + | .forallE _ domain body _, .forallE _ domain' body' _ => + structuralEq domain domain' && structuralEq body body' + | .letE _ type value body nondep, + .letE _ type' value' body' nondep' => + structuralEq type type' && structuralEq value value' && + structuralEq body body' && nondep == nondep' + | .mdata data expr, .mdata data' expr' => + structuralEq expr expr' && data.entries == data'.entries + | .proj typeName index struct, .proj typeName' index' struct' => + structuralEq struct struct' && typeName == typeName' && index == index' + | _, _ => false + +def arrow (d b : Expr) : Expr := .forallE `a d b .default + +def lam0 (ty e : Expr) : Expr := .lam `_ ty e default + +namespace ReplaceImpl + +unsafe abbrev ReplaceT := StateT (PtrMap Expr Expr) + +@[inline] +unsafe def cacheT [Monad m] (key : Expr) (result : Expr) : ReplaceT m Expr := do + modify (·.insert key result) + pure result + +@[specialize] +unsafe def replaceUnsafeT [Monad m] (f? : Expr → m (Option Expr)) (e : Expr) : ReplaceT m Expr := do + let rec @[specialize] visit (e : Expr) := do + if let some result := (← get).find? e then + return result + match ← f? e with + | some eNew => cacheT e eNew + | none => match e with + | Expr.forallE _ d b _ => cacheT e <| e.updateForallE! (← visit d) (← visit b) + | Expr.lam _ d b _ => cacheT e <| e.updateLambdaE! (← visit d) (← visit b) + | Expr.mdata _ b => cacheT e <| e.updateMData! (← visit b) + | Expr.letE _ t v b nd => cacheT e <| e.updateLet! (← visit t) (← visit v) (← visit b) nd + | Expr.app f a => cacheT e <| e.updateApp! (← visit f) (← visit a) + | Expr.proj _ _ b => cacheT e <| e.updateProj! (← visit b) + | e => pure e + visit e + +@[inline] +unsafe def replaceUnsafe' [Monad m] (f? : Expr → m (Option Expr)) (e : Expr) : m Expr := + (replaceUnsafeT f? e).run' mkPtrMap + +end ReplaceImpl + +/- TODO: use withPtrAddr, withPtrEq to avoid unsafe tricks above. + We also need an invariant at `State` and proofs for the `uget` operations. -/ + +@[specialize] +def replaceNoCacheT [Monad m] (f? : Expr → m (Option Expr)) (e : Expr) : m Expr := do + match ← f? e with + | some eNew => pure eNew + | none => match e with + | .forallE _ d b _ => + return e.updateForallE! (← replaceNoCacheT f? d) (← replaceNoCacheT f? b) + | .lam _ d b _ => + return e.updateLambdaE! (← replaceNoCacheT f? d) (← replaceNoCacheT f? b) + | .mdata _ b => + return e.updateMData! (← replaceNoCacheT f? b) + | .letE _ t v b nd => + return e.updateLet! + (← replaceNoCacheT f? t) (← replaceNoCacheT f? v) (← replaceNoCacheT f? b) nd + | .app f a => + return e.updateApp! (← replaceNoCacheT f? f) (← replaceNoCacheT f? a) + | .proj _ _ b => + return e.updateProj! (← replaceNoCacheT f? b) + | e => return e + +@[implemented_by ReplaceImpl.replaceUnsafe'] +partial def replaceM [Monad m] (f? : Expr → m (Option Expr)) (e : Expr) : m Expr := + e.replaceNoCacheT f? + +def natZero : Expr := .const ``Nat.zero [] +def natSucc : Expr := .const ``Nat.succ [] + +def isConstructorApp?' (env : Kernel.Environment) (e : Expr) : Option Name := do + let .const fn _ := e.getAppFn | none + let .ctorInfo _ ← env.find? fn | none + return fn + +def natLitToConstructor : Nat → Expr + | 0 => natZero + | n+1 => .app natSucc (.lit (.natVal n)) + +def strLitToConstructor (s : String) : Expr := + let char := .const ``Char [] + let listNil := .app (.const ``List.nil [.zero]) char + let listCons := .app (.const ``List.cons [.zero]) char + let stringMk := .const ``String.ofList [] + let charOfNat := .const ``Char.ofNat [] + .app stringMk <| s.toList.foldr (init := listNil) fun c e => -- TODO: use String.foldr + .app (.app listCons <| .app charOfNat (.lit (.natVal c.toNat))) e + +end Expr + +namespace Literal + +def toConstructor : Literal → Expr + | .natVal n => .natLitToConstructor n + | .strVal s => .strLitToConstructor s + +/-- Return the type of a literal value. -/ +def typeName : Literal → Name + | .natVal _ => ``Nat + | .strVal _ => ``String + +end Literal diff --git a/Ix/Theory/Named/Reference/ForEachExprV.lean b/Ix/Theory/Named/Reference/ForEachExprV.lean new file mode 100644 index 000000000..02bc56362 --- /dev/null +++ b/Ix/Theory/Named/Reference/ForEachExprV.lean @@ -0,0 +1,36 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Lean.Expr +import Lean.Util.MonadCache + +/-! +This is the same as `Expr.forEach` but it uses `StateT` instead of `StateRefT` to avoid opaques. +-/ +namespace Lean +variable {ω : Type} {m : Type → Type} [Monad m] + +namespace ForEachExprV +def visit (g : Expr → m Bool) (e : Expr) : MonadStateCacheT Expr Unit m Unit := + checkCache e fun _ => do + if (← g e) then + match e with + | Expr.forallE _ d b _ => do visit g d; visit g b + | Expr.lam _ d b _ => do visit g d; visit g b + | Expr.letE _ t v b _ => do visit g t; visit g v; visit g b + | Expr.app f a => do visit g f; visit g a + | Expr.mdata _ b => visit g b + | Expr.proj _ _ b => visit g b + | _ => pure () + +end ForEachExprV + +/-- Apply `f` to each sub-expression of `e`. If `f t` returns false, then t's children are not visited. -/ +@[inline] def Expr.forEachV' (e : Expr) (f : Expr → m Bool) : m Unit := + (ForEachExprV.visit f e).run + +@[inline] def Expr.forEachV (e : Expr) (f : Expr → m Unit) : m Unit := + e.forEachV' fun e => do f e; pure true diff --git a/Ix/Theory/Named/Reference/FuelConfig.lean b/Ix/Theory/Named/Reference/FuelConfig.lean new file mode 100644 index 000000000..88e8c02a4 --- /dev/null +++ b/Ix/Theory/Named/Reference/FuelConfig.lean @@ -0,0 +1,41 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Lean.Data.Json.FromToJson + +namespace Ix.Theory.Named + +/-- +Bounds for the various fixed-fuel loops inside the kernel. + +Every field is a positive `Nat`; on exhaustion the corresponding loop throws +`.deterministicTimeout` (whnf-family) or `.deepRecursion` (structural / +mutual-recursion loops). + +Defaults are set so mathlib passes. Since lean4#13956 the native kernel bounds +its mutually recursive type-checker entry points using `maxRecDepth`. +The named reference implementation instead keeps separate, explicit fuel for those calls and for loops +that need structural termination witnesses; these counters also provide a +deterministic defensive check against runaway reductions. +-/ +structure FuelConfig where + /-- `whnf'` unfold-loop, non-eager path (`TypeChecker.lean` whnf'). -/ + whnf : Nat := 100000 + /-- `whnf'` unfold-loop, `eagerReduce` path. -/ + whnfEager : Nat := 1000000 + /-- `lazyDeltaReduction.loop`. -/ + lazyDelta : Nat := 1000 + /-- `etaExpand.loop.loop2`. -/ + etaExpand : Nat := 1000 + /-- Starting fuel for `Methods.withFuel` (bounds mutual whnf/isDefEq depth). -/ + recDepth : Nat := 10000 + /-- Shared fuel for the structural loops in `Inductive/Add.lean`. -/ + inductiveFuel : Nat := 1000 + /-- Upper bound, in bytes, on the `Nat` numerals the kernel will accept or compute while + reducing `Nat` literals. Bounds the memory and time a single reduction can consume. The + native kernel spells this bound `LEAN_NAT_MAX_SIZE` and defaults it to the same 128 MB. -/ + natMaxSize : Nat := 134217728 -- 128 MB; a literal so `simp` cannot renormalize it + deriving Repr, Inhabited, Lean.FromJson, Lean.ToJson diff --git a/Ix/Theory/Named/Reference/Inductive/Add.lean b/Ix/Theory/Named/Reference/Inductive/Add.lean new file mode 100644 index 000000000..7a7c29a38 --- /dev/null +++ b/Ix/Theory/Named/Reference/Inductive/Add.lean @@ -0,0 +1,2941 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Batteries.Data.List.Basic +import Ix.Theory.Named.Reference.Environment.Basic +import Ix.Theory.Named.Reference.TypeChecker + +namespace Ix.Theory.Named +open Lean hiding Environment Exception +open Kernel + +open private Lean.Kernel.Environment.add from Lean.Environment + +namespace AddInductive +open TypeChecker + +structure RecInfo where + motive : Expr + minors : Array Expr + indices : Array Expr + major : Expr + deriving Inhabited + +structure InductiveStats where + lctx : LocalContext := {} + levels : List Level + resultLevel : Level + nindices : Array Nat := #[] + indConsts : Array Expr + params : Array Expr + isNotZero : Bool + deriving Inhabited + +/-- Explicit initial state for family validation. Naming this value keeps the +executable producer and its exact-result lemmas independent of the opaque +compiler-generated `Inhabited` instance. -/ +def InductiveStats.initial (levels : List Level) : InductiveStats where + levels := levels + resultLevel := .zero + indConsts := #[] + params := #[] + isNotZero := false + +structure Context where + env : Environment + lctx : LocalContext := {} + lparams : List Name + ngen : NameGenerator := { namePrefix := `_ind_fresh } + safety : DefinitionSafety + allowPrimitive : Bool + fuel : FuelConfig := {} + +/-- Checker context represented by an inductive-add context. Candidate traces +retain the latter so Verify can recover this exact former value. -/ +def Context.toTypeChecker (context : Context) : TypeChecker.Context where + env := context.env + lctx := context.lctx + safety := context.safety + lparams := context.lparams + fuel := context.fuel + +/-- The exact free variable allocated by the next `withLocalDecl` in an +inductive-add context. -/ +def Context.freshFVarId (context : Context) : FVarId := + ⟨context.ngen.curr⟩ + +def Context.freshExpr (context : Context) : Expr := + .fvar context.freshFVarId + +/-- Context seen by the body of the next `withLocalDecl`. Naming this update +lets candidate traces index a Pi body by the actual reader context used by the +producer, rather than retaining an unrelated context as unchecked data. -/ +def Context.pushLocalDecl (context : Context) + (name : Name) (binderInfo : BinderInfo) (type : Expr) : Context := + { context with + lctx := context.lctx.mkLocalDecl context.freshFVarId name type binderInfo + ngen := context.ngen.next } + +abbrev M := ReaderT Context <| Except Exception + +instance : MonadLocalNameGenerator M where + withFreshId f c := f c.ngen.curr { c with ngen := c.ngen.next } + +instance (priority := low) : MonadLift TypeChecker.M M where + monadLift x c := x.run c.env c.safety c.lctx c.lparams (fuel := c.fuel) + +@[simp] theorem liftTypeChecker_apply (x : TypeChecker.M α) (c : Context) : + (liftM x : M α) c = + x.run c.env c.safety c.lctx c.lparams (fuel := c.fuel) := + rfl + +@[simp] theorem liftExcept_apply (x : Except Exception α) (c : Context) : + (liftM x : M α) c = x := + rfl + +instance (priority := low+1) : MonadWithReaderOf LocalContext M where + withReader f x := withReader (fun c => { c with lctx := f c.lctx }) x + +instance : MonadLCtx M where + getLCtx := return (← read).lctx + +@[simp] theorem withLocalDecl_apply + (name : Name) (binderInfo : BinderInfo) (type : Expr) + (k : Expr → M α) (context : Context) : + withLocalDecl name binderInfo type k context = + k context.freshExpr + (context.pushLocalDecl name binderInfo type) := by + rfl + +@[inline] def withEnv (env : Environment) (x : M α) : M α := + withReader (fun c => { c with env }) x + +/-- Run an action under a different universe parameter list. The recursors are checked under +their own parameters, which carry the extra eliminator level the declaration itself lacks. -/ +@[inline] def withLParams (lparams : List Name) (x : M α) : M α := + withReader (fun c => { c with lparams }) x + +/-- Run a closed-metadata action without inheriting validation-local +declarations. All other reader fields, including the staged environment and +fuel, are preserved exactly. -/ +@[inline] def withEmptyLocalContext (x : M α) : M α := + withReader (fun c : Context => { c with lctx := {} }) x + +@[simp] theorem withEmptyLocalContext_apply (x : M α) (context : Context) : + withEmptyLocalContext x context = x { context with lctx := {} } := by + rfl + +def getType (fvar : Expr) : M Expr := + return ((← getLCtx).get! fvar.fvarId!).type + +/-- Transparent binder-annotation peeling used by inductive checking. + +Lean's `Expr.consumeTypeAnnotations` is an opaque partial implementation. A +kernel proof of an exact successful inductive pass cannot reduce through that +helper, so using it directly would require a separate contract axiom for every +annotated binder. This structural mirror covers the same four top-level +annotations and is regression-checked against Lean's helper below at the +candidate boundary. -/ +def consumeTypeAnnotations : (source : Expr) → Expr + | .app (.app (.const name levels) type) default => + if name = ``_root_.optParam then + consumeTypeAnnotations type + else if name = ``_root_.autoParam then + consumeTypeAnnotations type + else + .app (.app (.const name levels) type) default + | .app (.const name levels) type => + if name = ``_root_.outParam then + consumeTypeAnnotations type + else if name = ``_root_.semiOutParam then + consumeTypeAnnotations type + else + .app (.const name levels) type + | source => source +termination_by source => sizeOf source + +/-- Transparent structural equality used only as a fast path before the +normalization-based universe comparison. -/ +def levelStructEq : Level → Level → Bool + | .zero, .zero => true + | .succ u, .succ v => levelStructEq u v + | .max u₁ u₂, .max v₁ v₂ | .imax u₁ u₂, .imax v₁ v₂ => + levelStructEq u₁ v₁ && levelStructEq u₂ v₂ + | .param u, .param v => u == v + | .mvar u, .mvar v => u == v + | _, _ => false + +/-- Transparent sufficient comparison for the common structural universe +cases used by constructor fields. Every universe is at least zero, successor +is monotone, and otherwise exact structural equality is sufficient. Cases +outside this deliberately small relation continue to the standard +normalization-based `Level.geq` comparison below. -/ +def levelStructGe : Level → Level → Bool + | _, .zero => true + | .succ u, .succ v => levelStructGe u v + | u, v => levelStructEq u v + +def checkInductiveTypes + (nparams : Nat) (indTypes : Array InductiveType) + (k : InductiveStats → M α) : M α := do + let rec loopInd dIdx stats : M α := do + if _h : dIdx < indTypes.size then + let indType := indTypes[dIdx] + let env := (← read).env + let type := indType.type + env.checkNoMVarNoFVar indType.name type + _ ← checkType type + let rec loop stats type i nindices fuel k : M α := match fuel with + | 0 => throw .deepRecursion + | fuel+1 => do + if let .forallE name dom body bi := type then + if i < nparams then + if stats.indConsts.isEmpty then + withLocalDecl name bi (consumeTypeAnnotations dom) fun param => do + let stats := { stats with params := stats.params.push param } + let type := body.instantiate1 param + loop stats (← whnf type) (i + 1) nindices fuel k + else + let param := stats.params[i]! + unless ← isDefEq dom (← getType param) do + throw <| .other "parameters of all inductive datatypes must match" + let type := body.instantiate1 param + loop stats (← whnf type) (i + 1) nindices fuel k + else + withLocalDecl name bi (consumeTypeAnnotations dom) fun arg => do + let type := body.instantiate1 arg + loop stats (← whnf type) i (nindices + 1) fuel k + else + if i != nparams then + throw <| .other "number of parameters mismatch in inductive datatype declaration" + k type stats nindices + let fuel := (← readThe Context).fuel.inductiveFuel + loop stats (← whnf type) 0 0 fuel fun type stats nindices => show M α from do + let type ← ensureSort type + let mut stats := stats + let resultLevel := type.sortLevel! + if stats.indConsts.isEmpty then + let lctx := (← read).lctx + stats := { stats with lctx, resultLevel, isNotZero := resultLevel.isNeverZero } + else if !resultLevel.isEquiv stats.resultLevel then + throw <| .other "mutually inductive types must live in the same universe" + stats := { stats with + nindices := stats.nindices.push nindices + indConsts := stats.indConsts.push (.const indType.name stats.levels) } + loopInd (dIdx + 1) stats + else + k <| + assert! stats.levels.length == (← read).lparams.length + assert! stats.nindices.size == indTypes.size + assert! stats.indConsts.size == indTypes.size + assert! stats.params.size == nparams + stats + termination_by indTypes.size - dIdx + loopInd 0 (InductiveStats.initial ((← read).lparams.map .param)) + +/-- Exact singleton result of the family-validation pass when the family type +normalizes directly to a sort. This is the non-telescope producer seam used by +end-to-end candidate certificates: the executable pass selects every retained +statistic, while callers supply only the ordinary checker runs it consumed. -/ +def singletonInductiveStats (context : Context) + (indType : InductiveType) (resultLevel : Level) : InductiveStats where + lctx := context.lctx + levels := context.lparams.map .param + resultLevel := resultLevel + nindices := #[0] + indConsts := #[.const indType.name (context.lparams.map .param)] + params := #[] + isNotZero := resultLevel.isNeverZero + +theorem checkInductiveTypes_singleton_zero_of_whnf_sort + (context : Context) (indType : InductiveType) + (inferred : Expr) (resultLevel : Level) + (k : InductiveStats → M α) + (hfuel : 0 < context.fuel.inductiveFuel) + (hclosed : + context.env.checkNoMVarNoFVar indType.name indType.type = .ok ()) + (hcheck : + TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.checkType indType.type) = + .ok inferred) + (hwhnf : + TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.whnf indType.type) = + .ok (.sort resultLevel)) + (hensure : + TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel + (TypeChecker.ensureSort (.sort resultLevel)) = + .ok (.sort resultLevel)) : + checkInductiveTypes 0 #[indType] k context = + k (singletonInductiveStats context indType resultLevel) context := by + cases hfuel_eq : context.fuel.inductiveFuel with + | zero => omega + | succ fuel => + simp [checkInductiveTypes, checkInductiveTypes.loopInd, checkInductiveTypes.loopInd.loop, + singletonInductiveStats, readThe, MonadReader.read, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, Pure.pure, Except.bind, Except.pure, liftTypeChecker_apply, hclosed, + hcheck, hwhnf, hensure, hfuel_eq, InductiveStats.initial, Expr.sortLevel!] + +/-- Transparent occurrence test for constants in the inductive block. + +Lean's `Expr.find?` is an opaque native traversal. Using it here makes the +kernel's recursive-family and positivity decisions impossible to reduce in an +exact producer theorem without postulating a separate contract for that +traversal. This structural version follows the same expression children and +keeps those decisions computational in the logic as well as at runtime. -/ +def hasIndOcc (indConsts : Array Expr) : Expr → Bool + | .const name _ => indConsts.any fun I => I.constName! == name + | .app fn arg => hasIndOcc indConsts fn || hasIndOcc indConsts arg + | .lam _ domain body _ | .forallE _ domain body _ => + hasIndOcc indConsts domain || hasIndOcc indConsts body + | .letE _ type value body _ => + hasIndOcc indConsts type || hasIndOcc indConsts value || + hasIndOcc indConsts body + | .mdata _ body | .proj _ _ body => hasIndOcc indConsts body + | _ => false + +/-- Return true if declaration is recursive -/ +def isRec (indTypes : Array InductiveType) (indConsts : Array Expr) : Bool := + let rec loop + | .forallE _ dom body _ => hasIndOcc indConsts dom || loop body + | _ => false + indTypes.any fun indType => indType.ctors.any fun ctor => loop ctor.type + +/-- Return true if the given declaration is reflexive. + +Remark: We say an inductive type `T` is reflexive if it +contains at least one constructor that takes as an argument a +function returning `T'` where `T'` is another inductive datatype (possibly equal to `T`) +in the same mutual declaration. -/ +def isReflexive (indTypes : Array InductiveType) (indConsts : Array Expr) : Bool := + let rec loop + | .forallE _ dom body _ => dom.isForall && hasIndOcc indConsts dom || loop body + | _ => false + indTypes.any fun indType => indType.ctors.any fun ctor => loop ctor.type + +def declareInductiveTypes (stats : InductiveStats) (numParams : Nat) + (indTypes : Array InductiveType) (numNested : Nat) (isUnsafe : Bool) : M Environment := + fun c => + let all := indTypes.map (·.name) |>.toList + let infos := indTypes.zipWith (bs := stats.nindices) fun indType numIndices => + { indType with + numParams, numIndices, all, numNested, isUnsafe + levelParams := c.lparams + ctors := indType.ctors.map (·.name) + isRec := isRec indTypes stats.indConsts + isReflexive := isReflexive indTypes stats.indConsts } + infos.foldlM (init := c.env) fun env info => do + env.checkName info.name c.allowPrimitive + return env.add (.inductInfo info) + +/-- The exact kernel family record assembled for a singleton inductive block. +Naming it exposes the value installed by `declareInductiveTypes` without +asking a replay proof to duplicate the producer's record construction. -/ +def singletonDeclaredInfo (stats : InductiveStats) (numParams numIndices : Nat) + (indType : InductiveType) (numNested : Nat) (isUnsafe : Bool) + (context : Context) : InductiveVal := + { indType with + numParams, numIndices, all := [indType.name], numNested, isUnsafe + levelParams := context.lparams + ctors := indType.ctors.map (·.name) + isRec := isRec #[indType] stats.indConsts + isReflexive := isReflexive #[indType] stats.indConsts } + +/-- A successful singleton family declaration installs exactly the family +record assembled by the executable producer. The result equation supplies +the name-check evidence; replay callers provide only the validator's exact +singleton index count. -/ +theorem declareInductiveTypes_singleton_constants + (stats : InductiveStats) (numParams numIndices : Nat) + (indType : InductiveType) (numNested : Nat) (isUnsafe : Bool) + (context : Context) (familyEnv : Environment) + (hnindices : stats.nindices = #[numIndices]) + (hdeclare : + declareInductiveTypes stats numParams #[indType] numNested isUnsafe context = + .ok familyEnv) : + familyEnv.constants = + context.env.constants.insert indType.name + (.inductInfo <| singletonDeclaredInfo stats numParams numIndices + indType numNested isUnsafe context) := by + unfold declareInductiveTypes at hdeclare + rw [hnindices] at hdeclare + cases hcheck : context.env.checkName indType.name context.allowPrimitive with + | error error => + simp [hcheck, Bind.bind, Except.bind, Pure.pure, Except.pure] at hdeclare + | ok _ => + simp [hcheck, Bind.bind, Except.bind, Pure.pure, Except.pure] at hdeclare + exact congrArg Kernel.Environment.constants hdeclare.symm + +/-- A successful singleton family declaration changes only the constant map; +in particular it preserves the kernel's quotient-initialization flag. -/ +theorem declareInductiveTypes_singleton_quotInit + (stats : InductiveStats) (numParams numIndices : Nat) + (indType : InductiveType) (numNested : Nat) (isUnsafe : Bool) + (context : Context) (familyEnv : Environment) + (hnindices : stats.nindices = #[numIndices]) + (hdeclare : + declareInductiveTypes stats numParams #[indType] numNested isUnsafe context = + .ok familyEnv) : + familyEnv.quotInit = context.env.quotInit := by + unfold declareInductiveTypes at hdeclare + rw [hnindices] at hdeclare + cases hcheck : context.env.checkName indType.name context.allowPrimitive with + | error error => + simp [hcheck, Bind.bind, Except.bind, Pure.pure, Except.pure] at hdeclare + | ok _ => + simp [hcheck, Bind.bind, Except.bind, Pure.pure, Except.pure] at hdeclare + subst familyEnv + rfl + +/-- Family declaration observes only the environment, universe parameters, +and primitive-name policy of its reader context. In particular, the local +telescope and fresh-name generator retained by family validation do not alter +the staged environment it produces. -/ +theorem declareInductiveTypes_context_eq + (stats : InductiveStats) (numParams : Nat) + (indTypes : Array InductiveType) (numNested : Nat) + (isUnsafe : Bool) (left right : Context) + (henv : left.env = right.env) + (hlparams : left.lparams = right.lparams) + (hallow : left.allowPrimitive = right.allowPrimitive) : + declareInductiveTypes stats numParams indTypes numNested isUnsafe left = + declareInductiveTypes stats numParams indTypes numNested isUnsafe right := by + unfold declareInductiveTypes + rw [henv, hlparams, hallow] + +def isValidIndAppIdx (stats : InductiveStats) (t : Expr) (i : Nat) : Bool := + t.withApp fun I args => Id.run do + unless I == stats.indConsts[i]! && args.size == stats.params.size + stats.nindices[i]! do + return false + for i in [:stats.params.size] do + if stats.params[i]! != args[i]! then return false + for i in [stats.params.size:args.size] do + if hasIndOcc stats.indConsts args[i]! then return false + true + +def isValidIndApp? (stats : InductiveStats) (t : Expr) : Option Nat := do + for i in [:stats.indConsts.size] do + if isValidIndAppIdx stats t i then + return i + none + +theorem isValidIndApp?_singleton_zero + (stats : InductiveStats) (t : Expr) + (hsize : stats.indConsts.size = 1) + (hvalid : isValidIndAppIdx stats t 0 = true) : + isValidIndApp? stats t = some 0 := by + unfold isValidIndApp? + simp [hsize, hvalid] + +def isRecArg (stats : InductiveStats) (t : Expr) : M (Option Nat) := do + loop t (← readThe Context).fuel.inductiveFuel +where + loop t + | 0 => throw .deepRecursion + | fuel+1 => do + let t ← whnf t + let .forallE name dom body bi := t | return isValidIndApp? stats t + withLocalDecl name bi (consumeTypeAnnotations dom) fun arg => do + loop (body.instantiate1 arg) fuel + +def checkPositivity (stats : InductiveStats) (t : Expr) (ctor : Name) (idx : Nat) : + M Unit := do loop t (← readThe Context).fuel.inductiveFuel where + loop t + | 0 => throw .deepRecursion + | fuel+1 => do + let t ← whnf t + if !hasIndOcc stats.indConsts t then return + if let .forallE name dom body bi := t then + if hasIndOcc stats.indConsts dom then + throw <| .other s!"arg #{idx + 1} of '{ctor}' \ + has a non positive occurrence of the datatypes being declared" + withLocalDecl name bi (consumeTypeAnnotations dom) fun arg => do + loop (body.instantiate1 arg) fuel + else if let none := isValidIndApp? stats t then + throw <| .other s!"arg #{idx + 1} of '{ctor}' \ + has a non valid occurrence of the datatypes being declared" + +/-- Validate the parameter/field telescope and terminal family application of +one constructor. This is factored from the outer traversal so successful +executions can be retained without reproducing compiler-expanded `for` loops. -/ +def checkConstructorType (stats : InductiveStats) (isUnsafe : Bool) + (idx : Nat) (n : Name) (t : Expr) : M Unit := do + loop t 0 (← readThe Context).fuel.inductiveFuel +where + loop t i + | 0 => throw .deepRecursion + | fuel+1 => do + if let .forallE name dom body bi := t then + if let some param := stats.params[i]? then + unless ← isDefEq dom (← getType param) do + throw <| .other + s!"arg #{i + 1} of '{n}' does not match inductive datatype parameters" + loop (body.instantiate1 param) (i + 1) fuel + else + let s ← ensureType dom + -- Equal levels are reflexively admissible, so discharge that common + -- case before consulting the full normalization comparison. + if levelStructGe stats.resultLevel s.sortLevel! then + pure () + else + unless stats.resultLevel.isAlwaysZero || stats.resultLevel.geq s.sortLevel! do + throw <| .other s!"universe level of type_of(arg #{i + 1}) of '{n}' \ + is too big for the corresponding inductive datatype" + if !isUnsafe then + checkPositivity stats dom n i + withLocalDecl name bi (consumeTypeAnnotations dom) fun arg => do + loop (body.instantiate1 arg) (i + 1) fuel + else if !isValidIndAppIdx stats t idx then + throw <| .other s!"invalid return type for '{n}'" + +/-- Validate constructors in source order while retaining the duplicate-name +accumulator as the fold result. -/ +def checkConstructorFold (env : Environment) (stats : InductiveStats) + (isUnsafe : Bool) (idx : Nat) (seen : NameSet) + (ctors : List Constructor) : M NameSet := match ctors with + | [] => pure seen + | ctor :: ctors => do + let n := ctor.name + if seen.contains n then + throw <| .other s!"duplicate constructor name '{n}'" + let seen := seen.insert n + let t := ctor.type + env.checkNoMVarNoFVar n t + -- Constructor metadata has just been established to contain no free + -- variables. Its full closed-type check does not inherit family locals; + -- parameter matching in `checkConstructorType` deliberately does. + _ ← withEmptyLocalContext do checkType t + checkConstructorType stats isUnsafe idx n t + checkConstructorFold env stats isUnsafe idx seen ctors + +/-- The named family recursion of `checkConstructors`. Naming the loop keeps +the executable shell and its validation-trace mirror aligned without depending +on proof terms synthesized by `for` notation. -/ +def checkConstructorsLoop (env : Environment) (stats : InductiveStats) + (isUnsafe : Bool) : Nat → List InductiveType → M Unit + | _, [] => pure () + | idx, indType :: rest => do + _ ← checkConstructorFold env stats isUnsafe idx {} indType.ctors + checkConstructorsLoop env stats isUnsafe (idx + 1) rest + +def checkConstructors (indTypes : Array InductiveType) + (stats : InductiveStats) (isUnsafe : Bool) : M Unit := do + let env ← getEnv + checkConstructorsLoop env stats isUnsafe 0 indTypes.toList + +/-- One observed WHNF node in the executable normalization-candidate pass. +The complete `AddInductive.Context` is retained because Verify must replay the +same environment, safety mode, local context, level parameters, transparency, +and checker fuel before the observation acquires semantic authority. -/ +structure CandidateWhnfStep where + context : Context + source : Expr + result : Expr + +/-- Exact ordinary-checker execution represented by one retained step. -/ +def CandidateWhnfStep.Valid (step : CandidateWhnfStep) : Prop := + TypeChecker.M.run step.context.env step.context.safety + step.context.lctx step.context.lparams step.context.fuel + (TypeChecker.whnf step.source) = + .ok step.result + +/-- The result of evaluating one WHNF step together with the equality that +certifies the observation. -/ +structure CandidateWhnfObservation (context : Context) (source : Expr) where + result : Expr + valid : CandidateWhnfStep.Valid ⟨context, source, result⟩ + +def observeCandidateWhnf (context : Context) (source : Expr) : + Except Exception (CandidateWhnfObservation context source) := + match hrun : + TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.whnf source) with + | .error err => .error err + | .ok result => .ok ⟨result, hrun⟩ + +theorem observeCandidateWhnf_of_run + (context : Context) (source result : Expr) + (hrun : CandidateWhnfStep.Valid ⟨context, source, result⟩) : + observeCandidateWhnf context source = .ok ⟨result, hrun⟩ := by + change + TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.whnf source) = + .ok result at hrun + unfold observeCandidateWhnf + split + · simp_all + · rename_i observed hobserved + have : observed = result := by simp_all + subst observed + rfl + +/-- Recover the state-bearing recursive checker execution erased by +`TypeChecker.M.run`. This is the exact `WhnfRun.run_eq` boundary used by +Verify; no final state is guessed or chosen. -/ +theorem CandidateWhnfStep.innerRun + (step : CandidateWhnfStep) (recursionFuel : Nat) + (hdepth : step.context.fuel.recDepth = recursionFuel + 1) + (hvalid : step.Valid) : + ∃ state : TypeChecker.State, + TypeChecker.Inner.whnf' step.source + (TypeChecker.Methods.withFuel recursionFuel) + step.context.toTypeChecker + ({} : TypeChecker.State) = + .ok (step.result, state) := by + unfold CandidateWhnfStep.Valid at hvalid + unfold TypeChecker.M.run TypeChecker.whnf TypeChecker.RecM.run at hvalid + simp [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, StateT.bind, Except.bind, Bind.bind, + StateT.pure, Except.pure, Pure.pure, + StateT.run', Functor.map, Except.map] at hvalid + rw [hdepth] at hvalid + simp only [TypeChecker.Methods.withFuel, + TypeChecker.Inner.whnf] at hvalid + cases hinner : + TypeChecker.Inner.whnf' step.source + (TypeChecker.Methods.withFuel recursionFuel) + { env := step.context.env + lctx := step.context.lctx + safety := step.context.safety + lparams := step.context.lparams + fuel := step.context.fuel } + ({} : TypeChecker.State) with + | error err => simp [hinner] at hvalid + | ok pair => + rcases pair with ⟨observed, state⟩ + have : observed = step.result := by + simpa [hinner] using hvalid + subst observed + exact ⟨state, by + simpa [Context.toTypeChecker] using hinner⟩ + +/-- One full, non-inference-only type-check observation retained by the +candidate producer. -/ +structure CandidateCheckTypeStep where + context : Context + source : Expr + inferred : Expr + +def CandidateCheckTypeStep.Valid + (step : CandidateCheckTypeStep) : Prop := + TypeChecker.M.run step.context.env step.context.safety + step.context.lctx step.context.lparams step.context.fuel + (TypeChecker.checkType step.source) = + .ok step.inferred + +structure CandidateCheckTypeObservation + (context : Context) (source : Expr) where + inferred : Expr + valid : CandidateCheckTypeStep.Valid ⟨context, source, inferred⟩ + +def observeCandidateCheckType (context : Context) (source : Expr) : + Except Exception (CandidateCheckTypeObservation context source) := + match hrun : + TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.checkType source) with + | .error err => .error err + | .ok inferred => .ok ⟨inferred, hrun⟩ + +theorem observeCandidateCheckType_of_run + (context : Context) (source inferred : Expr) + (hrun : CandidateCheckTypeStep.Valid + ⟨context, source, inferred⟩) : + observeCandidateCheckType context source = + .ok ⟨inferred, hrun⟩ := by + change + TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.checkType source) = + .ok inferred at hrun + unfold observeCandidateCheckType + split + · simp_all + · rename_i observed hobserved + have : observed = inferred := by simp_all + subst observed + rfl + +/-- Recover the state-bearing full-check execution erased by `M.run`. -/ +theorem CandidateCheckTypeStep.innerRun + (step : CandidateCheckTypeStep) (recursionFuel : Nat) + (hdepth : step.context.fuel.recDepth = recursionFuel) + (hvalid : step.Valid) : + ∃ state : TypeChecker.State, + TypeChecker.Inner.inferType step.source false + (TypeChecker.Methods.withFuel recursionFuel) + step.context.toTypeChecker + ({} : TypeChecker.State) = + .ok (step.inferred, state) := by + unfold CandidateCheckTypeStep.Valid at hvalid + unfold TypeChecker.M.run TypeChecker.checkType + TypeChecker.RecM.run at hvalid + simp [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, StateT.bind, Except.bind, Bind.bind, + StateT.pure, Except.pure, Pure.pure, + StateT.run', Functor.map, Except.map] at hvalid + rw [hdepth] at hvalid + cases hinner : + TypeChecker.Inner.inferType step.source false + (TypeChecker.Methods.withFuel recursionFuel) + { env := step.context.env + lctx := step.context.lctx + safety := step.context.safety + lparams := step.context.lparams + fuel := step.context.fuel } + ({} : TypeChecker.State) with + | error err => simp [hinner] at hvalid + | ok pair => + rcases pair with ⟨observed, state⟩ + have : observed = step.inferred := by + simpa [hinner] using hvalid + subst observed + exact ⟨state, by + simpa [Context.toTypeChecker] using hinner⟩ + +/-- One exact successful definitional-equality observation retained by the +candidate producer. The result is fixed to `true`; a negative checker result +is not evidence and aborts candidate construction. -/ +structure CandidateIsDefEqStep where + context : Context + lhs : Expr + rhs : Expr + +def CandidateIsDefEqStep.Valid + (step : CandidateIsDefEqStep) : Prop := + TypeChecker.M.run step.context.env step.context.safety + step.context.lctx step.context.lparams step.context.fuel + (TypeChecker.isDefEq step.lhs step.rhs) = + .ok true + +structure CandidateIsDefEqObservation + (context : Context) (lhs rhs : Expr) : Type where + valid : CandidateIsDefEqStep.Valid ⟨context, lhs, rhs⟩ + +def observeCandidateIsDefEq + (context : Context) (lhs rhs : Expr) : + Except Exception (CandidateIsDefEqObservation context lhs rhs) := + match hrun : + TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.isDefEq lhs rhs) with + | .error err => .error err + | .ok false => + .error (.other "normalization candidate changed a binder domain") + | .ok true => .ok ⟨hrun⟩ + +theorem observeCandidateIsDefEq_of_run + (context : Context) (lhs rhs : Expr) + (hrun : CandidateIsDefEqStep.Valid ⟨context, lhs, rhs⟩) : + observeCandidateIsDefEq context lhs rhs = .ok ⟨hrun⟩ := by + change + TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.isDefEq lhs rhs) = + .ok true at hrun + unfold observeCandidateIsDefEq + split + · simp_all + · simp_all + · rfl + +/-- Recover the state-bearing equality execution erased by `M.run`. -/ +theorem CandidateIsDefEqStep.innerRun + (step : CandidateIsDefEqStep) (recursionFuel : Nat) + (hdepth : step.context.fuel.recDepth = recursionFuel) + (hvalid : step.Valid) : + ∃ state : TypeChecker.State, + TypeChecker.Inner.isDefEq step.lhs step.rhs + (TypeChecker.Methods.withFuel recursionFuel) + step.context.toTypeChecker + ({} : TypeChecker.State) = + .ok (true, state) := by + unfold CandidateIsDefEqStep.Valid at hvalid + unfold TypeChecker.M.run TypeChecker.isDefEq + TypeChecker.RecM.run at hvalid + simp [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, StateT.bind, Except.bind, Bind.bind, + StateT.pure, Except.pure, Pure.pure, + StateT.run', Functor.map, Except.map] at hvalid + rw [hdepth] at hvalid + cases hinner : + TypeChecker.Inner.isDefEq step.lhs step.rhs + (TypeChecker.Methods.withFuel recursionFuel) + { env := step.context.env + lctx := step.context.lctx + safety := step.context.safety + lparams := step.context.lparams + fuel := step.context.fuel } + ({} : TypeChecker.State) with + | error err => simp [hinner] at hvalid + | ok pair => + rcases pair with ⟨observed, state⟩ + have : observed = true := by + simpa [hinner] using hvalid + subst observed + exact ⟨state, by + simpa [Context.toTypeChecker] using hinner⟩ + +/-- Source-indexed retained full-check execution. -/ +structure CandidateCheckTypeRun (source : Expr) where + step : CandidateCheckTypeStep + source_eq : step.source = source + valid : step.Valid + +def buildCandidateCheckType + (source : Expr) : M (CandidateCheckTypeRun source) := do + let context ← readThe Context + match observeCandidateCheckType context source with + | .error err => throw err + | .ok ⟨inferred, valid⟩ => + return ⟨⟨context, source, inferred⟩, rfl, valid⟩ + +/-- Structural certificate for the four top-level binder-domain annotations +peeled by `Expr.consumeTypeAnnotations`. + +The certificate exposes which application argument survives. Verify can +therefore recover a strict translation and free-variable facts for the +consumed domain from the translated raw domain, without assigning semantic +authority to Lean's opaque helper. -/ +inductive CandidateTypeAnnotationTrace : Expr → Expr → Type where + | identity (source : Expr) : + CandidateTypeAnnotationTrace source source + | outParam (levels : List Level) (type : Expr) + (inner : CandidateTypeAnnotationTrace type consumed) : + CandidateTypeAnnotationTrace + (.app (.const ``outParam levels) type) consumed + | semiOutParam (levels : List Level) (type : Expr) + (inner : CandidateTypeAnnotationTrace type consumed) : + CandidateTypeAnnotationTrace + (.app (.const ``semiOutParam levels) type) consumed + | optParam (levels : List Level) (type default : Expr) + (inner : CandidateTypeAnnotationTrace type consumed) : + CandidateTypeAnnotationTrace + (.app (.app (.const ``optParam levels) type) default) consumed + | autoParam (levels : List Level) (type tactic : Expr) + (inner : CandidateTypeAnnotationTrace type consumed) : + CandidateTypeAnnotationTrace + (.app (.app (.const ``autoParam levels) type) tactic) consumed + +namespace CandidateTypeAnnotationTrace + +/-- Transparent structural mirror of the top-level peeling algorithm. -/ +def build : (source : Expr) → Sigma (CandidateTypeAnnotationTrace source) + | .app (.app (.const name levels) type) default => + if hopt : name = ``_root_.optParam then by + subst name + let ⟨consumed, inner⟩ := build type + exact ⟨consumed, .optParam levels type default inner⟩ + else if hauto : name = ``_root_.autoParam then by + subst name + let ⟨consumed, inner⟩ := build type + exact ⟨consumed, .autoParam levels type default inner⟩ + else + ⟨.app (.app (.const name levels) type) default, .identity _⟩ + | .app (.const name levels) type => + if hout : name = ``_root_.outParam then by + subst name + let ⟨consumed, inner⟩ := build type + exact ⟨consumed, .outParam levels type inner⟩ + else if hsemi : name = ``_root_.semiOutParam then by + subst name + let ⟨consumed, inner⟩ := build type + exact ⟨consumed, .semiOutParam levels type inner⟩ + else + ⟨.app (.const name levels) type, .identity _⟩ + | source => ⟨source, .identity source⟩ +termination_by source => sizeOf source + +/-- The structural annotation builder computes the same transparent peeling +used by inductive validation. This deliberately relates two definitions in +this module, not Lean's opaque `Expr.consumeTypeAnnotations`. -/ +theorem build_consumed (source : Expr) : + (build source).1 = consumeTypeAnnotations source := by + fun_induction build source <;> simp_all [consumeTypeAnnotations] + +end CandidateTypeAnnotationTrace + +/-- A structural peeling certificate. Verify assigns semantic authority only +to the trace; compatibility with Lean's opaque helper is retained as a +differential executable check rather than a proof axiom. -/ +structure CandidateTypeAnnotations (source : Expr) where + consumed : Expr + trace : CandidateTypeAnnotationTrace source consumed + +def buildCandidateTypeAnnotations + (source : Expr) : Except Exception (CandidateTypeAnnotations source) := + let ⟨consumed, trace⟩ := CandidateTypeAnnotationTrace.build source + .ok ⟨consumed, trace⟩ + +namespace CandidateTypeAnnotations + +/-- Operational compatibility with this module's transparent annotation +peeling. Semantic consumers still rely on `trace` plus the retained +definitional-equality execution; `Matches` is used to replay the executable +family-validation path exactly. -/ +def Matches (annotations : CandidateTypeAnnotations source) : Prop := + annotations.consumed = consumeTypeAnnotations source + +theorem matches_of_build + (annotations : CandidateTypeAnnotations source) + (hbuild : buildCandidateTypeAnnotations source = .ok annotations) : + annotations.Matches := by + unfold buildCandidateTypeAnnotations at hbuild + cases htrace : CandidateTypeAnnotationTrace.build source with + | mk consumed trace => + simp only [Except.ok.injEq] at hbuild + subst annotations + simpa [Matches, htrace] using + CandidateTypeAnnotationTrace.build_consumed source + +end CandidateTypeAnnotations + +/-- Differential check pinning the transparent implementation to Lean's +opaque helper. This is executable regression evidence, not a logical premise +of the candidate producer. -/ +def candidateTypeAnnotationsAgree (source : Expr) : Bool := + let ⟨consumed, _⟩ := CandidateTypeAnnotationTrace.build source + consumed.equal source.consumeTypeAnnotations + +/-- Context- and source-indexed tree underlying one candidate expression. +The recursive indices are important: a Pi-domain trace uses the exact parent +context, while its body trace uses precisely `Context.pushLocalDecl` with the +structurally certified annotation-consumed domain and the corresponding fresh +free variable. The retained equality run relates that local declaration back +to the raw binder syntax. Thus expression position, annotation handling, and +checker-context provenance are enforced by the type rather than being +invariants of the producer alone. -/ +inductive CandidateExprTrace : Context → Expr → Type where + | terminal (context : Context) (source inferred result : Expr) + (checked : CandidateCheckTypeStep.Valid + ⟨context, source, inferred⟩) + (valid : CandidateWhnfStep.Valid ⟨context, source, result⟩) : + CandidateExprTrace context source + | forallE (context : Context) (source : Expr) + (inferred : Expr) + (name : Name) (domain body : Expr) + (binderInfo : BinderInfo) + (fresh : context.lctx.find? context.freshFVarId = none) + (annotations : CandidateTypeAnnotations domain) + (annotationsEq : CandidateIsDefEqStep.Valid + ⟨context, domain, annotations.consumed⟩) + (checked : CandidateCheckTypeStep.Valid + ⟨context, source, inferred⟩) + (valid : CandidateWhnfStep.Valid + ⟨context, source, .forallE name domain body binderInfo⟩) + (domainCandidate : CandidateExprTrace context domain) + (bodyCandidate : CandidateExprTrace + (context.pushLocalDecl name binderInfo annotations.consumed) + (body.instantiate1 context.freshExpr)) : + CandidateExprTrace context source + +namespace CandidateExprTrace + +/-- The main Pi spine exposed by candidate WHNF was already present in the +stored source syntax at every traversed body position. + +This is the structural precondition needed by mixed generation: it permits +normalization inside binder domains and at the terminal result, but it does +not let WHNF invent or remove the raw binders that generation must emit. -/ +def storedSpine : + {context : Context} → {source : Expr} → + CandidateExprTrace context source → Bool + | _, _, .terminal .. => true + | _, _, .forallE _ source _ name domain body binderInfo _ _ _ _ _ _ + bodyCandidate => + Expr.structuralEq source (.forallE name domain body binderInfo) && + storedSpine bodyCandidate + +/-- Number of stored Pi binders on the main (body) path of a candidate. -/ +def spineLength : + {context : Context} → {source : Expr} → + CandidateExprTrace context source → Nat + | _, _, .terminal .. => 0 + | _, _, .forallE _ _ _ _ _ _ _ _ _ _ _ _ _ bodyCandidate => + bodyCandidate.spineLength + 1 + +/-- The exact full-check observation at the root of a candidate trace. -/ +def rootCheck : + CandidateExprTrace context source → + CandidateCheckTypeObservation context source + | .terminal _ _ inferred _ checked _ => ⟨inferred, checked⟩ + | .forallE _ _ inferred _ _ _ _ _ _ _ checked _ _ _ => + ⟨inferred, checked⟩ + +/-- The exact WHNF result at the root, before recursively normalized domains +and bodies are reassembled into `view`. -/ +def rootWhnf : CandidateExprTrace context source → Expr + | .terminal _ _ _ result _ _ => result + | .forallE _ _ _ name domain body binderInfo _ _ _ _ _ _ _ => + .forallE name domain body binderInfo + +theorem rootWhnf_valid (candidate : CandidateExprTrace context source) : + CandidateWhnfStep.Valid ⟨context, source, candidate.rootWhnf⟩ := by + cases candidate <;> assumption + +/-- Reader context reached after following the complete main Π spine. -/ +def terminalContext : CandidateExprTrace context source → Context + | .terminal context _ _ _ _ _ => context + | .forallE _ _ _ _ _ _ _ _ _ _ _ _ _ bodyCandidate => + bodyCandidate.terminalContext + +theorem terminalContext_lparams + (candidate : CandidateExprTrace context source) : + candidate.terminalContext.lparams = context.lparams := by + induction candidate with + | terminal => rfl + | forallE context source inferred name domain body binderInfo fresh + annotations annotationsEq checked valid domainCandidate bodyCandidate + domain_ih body_ih => + simpa [terminalContext, Context.pushLocalDecl] using body_ih + +/-- Following the main candidate Π spine preserves the kernel environment. -/ +theorem terminalContext_env + (candidate : CandidateExprTrace context source) : + candidate.terminalContext.env = context.env := by + induction candidate with + | terminal => rfl + | forallE context source inferred name domain body binderInfo fresh + annotations annotationsEq checked valid domainCandidate bodyCandidate + domain_ih body_ih => + simpa [terminalContext, Context.pushLocalDecl] using body_ih + +/-- Following the main candidate Π spine preserves the primitive-name +policy used by family declaration. -/ +theorem terminalContext_allowPrimitive + (candidate : CandidateExprTrace context source) : + candidate.terminalContext.allowPrimitive = context.allowPrimitive := by + induction candidate with + | terminal => rfl + | forallE context source inferred name domain body binderInfo fresh + annotations annotationsEq checked valid domainCandidate bodyCandidate + domain_ih body_ih => + simpa [terminalContext, Context.pushLocalDecl] using body_ih + +/-- Following the main candidate Π spine changes only the local context and +name generator; it preserves the checker safety mode. -/ +theorem terminalContext_safety + (candidate : CandidateExprTrace context source) : + candidate.terminalContext.safety = context.safety := by + induction candidate with + | terminal => rfl + | forallE context source inferred name domain body binderInfo fresh + annotations annotationsEq checked valid domainCandidate bodyCandidate + domain_ih body_ih => + simpa [terminalContext, Context.pushLocalDecl] using body_ih + +/-- Following the main candidate Π spine preserves the checker fuel +configuration. -/ +theorem terminalContext_fuel + (candidate : CandidateExprTrace context source) : + candidate.terminalContext.fuel = context.fuel := by + induction candidate with + | terminal => rfl + | forallE context source inferred name domain body binderInfo fresh + annotations annotationsEq checked valid domainCandidate bodyCandidate + domain_ih body_ih => + simpa [terminalContext, Context.pushLocalDecl] using body_ih + +/-- Non-Π result reached after following the complete main Π spine. -/ +def terminalResult : CandidateExprTrace context source → Expr + | .terminal _ _ _ result _ _ => result + | .forallE _ _ _ _ _ _ _ _ _ _ _ _ _ bodyCandidate => + bodyCandidate.terminalResult + +/-- The first `count` local expressions allocated along the main Π spine. +These are exactly the expressions accumulated as inductive parameters when +`count` is the declaration's `nparams`. -/ +def parameterList : + (count : Nat) → CandidateExprTrace context source → List Expr + | 0, _ => [] + | _ + 1, .terminal .. => [] + | count + 1, + .forallE context _ _ _ _ _ _ _ _ _ _ _ _ bodyCandidate => + context.freshExpr :: bodyCandidate.parameterList count + +theorem parameterList_length + (candidate : CandidateExprTrace context source) + (hcount : count ≤ candidate.spineLength) : + (candidate.parameterList count).length = count := by + induction candidate generalizing count with + | terminal => + simp [spineLength] at hcount + subst count + rfl + | forallE context source inferred name domain body binderInfo fresh + annotations annotationsEq checked valid domainCandidate bodyCandidate + domain_ih body_ih => + cases count with + | zero => rfl + | succ count => + simp only [parameterList, List.length_cons] + rw [body_ih] + simpa [spineLength] using hcount + +/-- Every annotation choice on the main Π spine matches the transparent +peeling operation used by `checkInductiveTypes`. This is operational +provenance, separate from the semantic raw/consumed equality stored at each +candidate node. -/ +def validationAnnotations : + CandidateExprTrace context source → Prop + | .terminal .. => True + | .forallE _ _ _ _ _ _ _ _ annotations _ _ _ _ bodyCandidate => + annotations.Matches ∧ bodyCandidate.validationAnnotations + +/-- Replay the inner family-telescope validator from a candidate's exact main +Π spine. The first `remaining` binders extend `stats.params`; every later +binder contributes an index. The theorem is independent of any fixture and +preserves the exact terminal reader context reached by the executable loop. -/ +theorem checkInductiveTypes_loop_of_candidate + (candidate : CandidateExprTrace context source) + (stats : InductiveStats) (nparams i nindices fuel : Nat) + (remaining : Nat) (k : Expr → InductiveStats → Nat → M α) + (hi : i + remaining = nparams) + (hcount : remaining ≤ candidate.spineLength) + (hfuel : candidate.spineLength < fuel) + (hempty : stats.indConsts.isEmpty = true) + (hannotations : candidate.validationAnnotations) + (hterminal : candidate.terminalResult.isForall = false) : + checkInductiveTypes.loopInd.loop nparams stats candidate.rootWhnf + i nindices fuel k context = + k candidate.terminalResult + { stats with + params := stats.params ++ + (candidate.parameterList remaining).toArray } + (nindices + (candidate.spineLength - remaining)) + candidate.terminalContext := by + induction candidate generalizing i nindices fuel remaining stats with + | terminal context source inferred result checked valid => + simp only [spineLength] at hcount hfuel + have hremaining : remaining = 0 := by omega + subst remaining + have hi' : i = nparams := by omega + subst i + cases stats + cases fuel with + | zero => omega + | succ fuel => + cases result <;> + simp_all [rootWhnf, terminalResult, terminalContext, parameterList, + spineLength, checkInductiveTypes.loopInd.loop, Expr.isForall] + | forallE context source inferred name domain body binderInfo fresh + annotations annotationsEq checked valid domainCandidate bodyCandidate + domain_ih body_ih => + rcases hannotations with ⟨hmatch, hbodyAnnotations⟩ + cases remaining with + | zero => + have hi' : i = nparams := by omega + subst i + have hbodyFuel : bodyCandidate.spineLength < fuel - 1 := by + simp only [spineLength] at hfuel + omega + have hbodyCount : 0 ≤ bodyCandidate.spineLength := Nat.zero_le _ + have hvalid := bodyCandidate.rootWhnf_valid + change TypeChecker.M.run _ _ _ _ _ + (TypeChecker.whnf (body.instantiate1 context.freshExpr)) = + .ok bodyCandidate.rootWhnf at hvalid + rw [show fuel = (fuel - 1) + 1 by omega] + simp only [rootWhnf, checkInductiveTypes.loopInd.loop, + Nat.lt_irrefl, if_false, withLocalDecl_apply] + rw [← hmatch] + simp only [ReaderT.bind, Bind.bind, liftTypeChecker_apply] + rw [hvalid] + simp only [Except.bind] + rw [body_ih stats nparams (nindices + 1) (fuel - 1) 0 rfl + hbodyCount hbodyFuel hempty hbodyAnnotations hterminal] + simp [terminalResult, terminalContext, parameterList, spineLength, + Nat.add_comm, Nat.add_assoc] + | succ remaining => + have hil : i < nparams := by omega + have hbodyCount : remaining ≤ bodyCandidate.spineLength := by + simp only [spineLength] at hcount + omega + have hbodyFuel : bodyCandidate.spineLength < fuel - 1 := by + simp only [spineLength] at hfuel + omega + have hvalid := bodyCandidate.rootWhnf_valid + change TypeChecker.M.run _ _ _ _ _ + (TypeChecker.whnf (body.instantiate1 context.freshExpr)) = + .ok bodyCandidate.rootWhnf at hvalid + rw [show fuel = (fuel - 1) + 1 by omega] + simp only [rootWhnf, checkInductiveTypes.loopInd.loop, hil, if_true, + hempty, withLocalDecl_apply] + rw [← hmatch] + simp only [ReaderT.bind, Bind.bind, liftTypeChecker_apply] + rw [hvalid] + simp only [Except.bind] + rw [body_ih { stats with + params := stats.params.push context.freshExpr } + (i + 1) nindices (fuel - 1) remaining (by omega) + hbodyCount hbodyFuel (by simpa using hempty) hbodyAnnotations + hterminal] + simp [terminalResult, terminalContext, parameterList, spineLength] + +/-- Exact singleton statistics selected by a candidate family spine with an +arbitrary parameter/index split. -/ +def singletonCandidateInductiveStats + (indType : InductiveType) + (candidate : CandidateExprTrace context indType.type) + (nparams : Nat) (resultLevel : Level) : InductiveStats where + lctx := candidate.terminalContext.lctx + levels := context.lparams.map .param + resultLevel := resultLevel + nindices := #[candidate.spineLength - nparams] + indConsts := #[.const indType.name (context.lparams.map .param)] + params := (candidate.parameterList nparams).toArray + isNotZero := resultLevel.isNeverZero + +/-- A source-indexed candidate family spine discharges the complete singleton +family-validation pass for any number of parameters and indices. The result +records the same local expressions, terminal context, index count, universe, +and family constant selected by the executable validator. -/ +theorem checkInductiveTypes_singleton_of_candidate + (indType : InductiveType) + (candidate : CandidateExprTrace context indType.type) + (nparams : Nat) (resultLevel : Level) + (k : InductiveStats → M α) + (hclosed : + context.env.checkNoMVarNoFVar indType.name indType.type = .ok ()) + (hcount : nparams ≤ candidate.spineLength) + (hfuel : candidate.spineLength < context.fuel.inductiveFuel) + (hannotations : candidate.validationAnnotations) + (hterminal : candidate.terminalResult = .sort resultLevel) + (hensure : + TypeChecker.M.run candidate.terminalContext.env + candidate.terminalContext.safety candidate.terminalContext.lctx + candidate.terminalContext.lparams candidate.terminalContext.fuel + (TypeChecker.ensureSort (.sort resultLevel)) = + .ok (.sort resultLevel)) : + checkInductiveTypes nparams #[indType] k context = + k (candidate.singletonCandidateInductiveStats + indType nparams resultLevel) candidate.terminalContext := by + have hcheck := candidate.rootCheck.valid + have hwhnf := candidate.rootWhnf_valid + change TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.checkType indType.type) = + .ok candidate.rootCheck.inferred at hcheck + change TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.whnf indType.type) = + .ok candidate.rootWhnf at hwhnf + have hterminalForall : candidate.terminalResult.isForall = false := by + rw [hterminal] + rfl + have hterminalLparams : + candidate.terminalContext.lparams = context.lparams := + candidate.terminalContext_lparams + have hparameterLength := candidate.parameterList_length hcount + unfold checkInductiveTypes + simp only [readThe, MonadReader.read, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, Pure.pure, Except.pure, Except.bind] + rw [checkInductiveTypes.loopInd.eq_1] + have hsize : 0 < #[indType].size := by simp + rw [dif_pos hsize] + rw [show #[indType][0] = indType by rfl] + simp only [readThe, MonadReader.read, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, Pure.pure, Except.pure, Except.bind, + liftTypeChecker_apply, hclosed, hcheck, hwhnf] + rw [candidate.checkInductiveTypes_loop_of_candidate + (stats := InductiveStats.initial (context.lparams.map .param)) + (nparams := nparams) (i := 0) (nindices := 0) + (fuel := context.fuel.inductiveFuel) (remaining := nparams) + (hi := Nat.zero_add nparams) (hcount := hcount) (hfuel := hfuel) + (hempty := rfl) (hannotations := hannotations) + (hterminal := hterminalForall)] + rw [hterminal] + simp only [ReaderT.bind, Bind.bind, liftTypeChecker_apply] + rw [hensure] + simp only [Except.bind] + rw [if_pos (show ((InductiveStats.initial + (List.map Level.param context.lparams)).indConsts).isEmpty = true from + rfl)] + simp only [Expr.sortLevel!, InductiveStats.initial, Nat.zero_add] + simp only [ReaderT.bind, Bind.bind, Except.pure, Except.bind] + rw [checkInductiveTypes.loopInd.eq_1] + have hdone : ¬1 < #[indType].size := by simp + rw [dif_neg hdone] + simp only [readThe, MonadReader.read, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, Pure.pure, Except.pure, Except.bind] + simp [singletonCandidateInductiveStats, hterminalLparams, + hparameterLength] + +/-- Exact successful singleton family-validation execution retained at the +candidate selected by `buildNormalizationCandidate`. + +The executable validator owns the parameter/index split, result universe, +statistics, and terminal reader context. Keeping the universally quantified +continuation equation makes this a decomposition of the real +`checkInductiveTypes` call rather than a fixture-specific success flag. The +semantic interpretation of the retained candidate remains in Verify. -/ +structure FamilyValidationRun + (indType : InductiveType) + {context : Context} + (candidate : CandidateExprTrace context indType.type) where + nparams : Nat + resultLevel : Level + stats : InductiveStats + stats_eq : stats = candidate.singletonCandidateInductiveStats + indType nparams resultLevel + terminal_eq : candidate.terminalResult = Expr.sort resultLevel + run : ∀ {α} (k : InductiveStats → M α), + checkInductiveTypes nparams #[indType] k context = + k stats candidate.terminalContext + +/-- The retained singleton validation run exposes exactly the candidate view +parameter expressions selected by the executable family pass. -/ +def FamilyValidationRun.parameters + (run : FamilyValidationRun indType candidate) : List Expr := + candidate.parameterList run.nparams + +/-- The retained singleton validation run exposes the number of candidate +view indices following the selected parameter prefix. -/ +def FamilyValidationRun.numIndices + (run : FamilyValidationRun indType candidate) : Nat := + candidate.spineLength - run.nparams + +/-- Candidate expression reconstructed from the traced WHNF/Pi tree. -/ +def view : CandidateExprTrace context source → Expr + | .terminal _ _ _ result _ _ => result + | .forallE context _ _ name _ _ binderInfo _ _ _ _ _ domain body => + .forallE name domain.view + (body.view.abstract #[context.freshExpr]) binderInfo + +/-- Preorder list of all retained checker observations. -/ +def steps : CandidateExprTrace context source → List CandidateWhnfStep + | .terminal context source _ result _ _ => [{ context, source, result }] + | .forallE context source _ name domain body binderInfo _ _ _ _ _ + domainCandidate bodyCandidate => + { context, source, + result := .forallE name domain body binderInfo } :: + domainCandidate.steps ++ bodyCandidate.steps + +/-- Preorder list of all retained full-check observations. -/ +def checkSteps : CandidateExprTrace context source → List CandidateCheckTypeStep + | .terminal context source inferred _ _ _ => + [{ context, source, inferred }] + | .forallE context source inferred _ _ _ _ _ _ _ _ _ + domainCandidate bodyCandidate => + { context, source, inferred } :: + domainCandidate.checkSteps ++ bodyCandidate.checkSteps + +/-- Preorder list of all retained binder-domain equality observations. -/ +def isDefEqSteps : + CandidateExprTrace context source → List CandidateIsDefEqStep + | .terminal .. => [] + | .forallE context _ _ _ domain _ _ _ annotations _ _ _ + domainCandidate bodyCandidate => + { context, lhs := domain, rhs := annotations.consumed } :: + domainCandidate.isDefEqSteps ++ bodyCandidate.isDefEqSteps + +/-- Every retained WHNF observation is an exact checker execution. -/ +theorem allValid : (candidate : CandidateExprTrace context source) → + ∀ step ∈ candidate.steps, step.Valid + | .terminal context source _ result _ valid, step, h => by + simp only [steps, List.mem_singleton] at h + subst step + exact valid + | .forallE _ _ _ _ _ _ _ _ _ _ _ valid domain body, step, h => by + simp only [steps, List.mem_cons, List.mem_append] at h + rcases h with (rfl | h) | h + · exact valid + · exact domain.allValid step h + · exact body.allValid step h + +/-- Every retained full-check observation is an exact checker execution. -/ +theorem allChecksValid : (candidate : CandidateExprTrace context source) → + ∀ step ∈ candidate.checkSteps, step.Valid + | .terminal context source inferred _ checked _, step, h => by + simp only [checkSteps, List.mem_singleton] at h + subst step + exact checked + | .forallE _ _ _ _ _ _ _ _ _ _ checked _ domain body, step, h => by + simp only [checkSteps, List.mem_cons, List.mem_append] at h + rcases h with (rfl | h) | h + · exact checked + · exact domain.allChecksValid step h + · exact body.allChecksValid step h + +/-- Every retained binder-domain equality is an exact successful checker +execution. -/ +theorem allIsDefEqValid : (candidate : CandidateExprTrace context source) → + ∀ step ∈ candidate.isDefEqSteps, step.Valid + | .terminal .., step, h => by simp [isDefEqSteps] at h + | .forallE _ _ _ _ _ _ _ _ _ annotationsEq _ _ domain body, + step, h => by + simp only [isDefEqSteps, List.mem_cons, List.mem_append] at h + rcases h with (rfl | h) | h + · exact annotationsEq + · exact domain.allIsDefEqValid step h + · exact body.allIsDefEqValid step h + +end CandidateExprTrace + +/-- Source-indexed trace for one candidate expression. The tree records the +full-check and WHNF observations at every inspected node and retains the exact +positional split through Pi domains and instantiated bodies. Every node +carries the exact checker-run equalities produced by +`observeCandidateCheckType` and `observeCandidateWhnf`; Theory translation and +semantic refinement are intentionally separate. -/ +structure CandidateExpr (source : Expr) where + context : Context + trace : CandidateExprTrace context source + +def CandidateExpr.view (candidate : CandidateExpr source) : Expr := + candidate.trace.view + +def CandidateExpr.steps + (candidate : CandidateExpr source) : List CandidateWhnfStep := + candidate.trace.steps + +def CandidateExpr.checkSteps + (candidate : CandidateExpr source) : + List CandidateCheckTypeStep := + candidate.trace.checkSteps + +def CandidateExpr.isDefEqSteps + (candidate : CandidateExpr source) : + List CandidateIsDefEqStep := + candidate.trace.isDefEqSteps + +theorem CandidateExpr.step_valid + (candidate : CandidateExpr source) + (hstep : step ∈ candidate.steps) : + step.Valid := + candidate.trace.allValid step hstep + +theorem CandidateExpr.checkStep_valid + (candidate : CandidateExpr source) + (hstep : step ∈ candidate.checkSteps) : + step.Valid := + candidate.trace.allChecksValid step hstep + +theorem CandidateExpr.isDefEqStep_valid + (candidate : CandidateExpr source) + (hstep : step ∈ candidate.isDefEqSteps) : + step.Valid := + candidate.trace.allIsDefEqValid step hstep + +/-- Normalize exactly the expression positions inspected by inductive +analysis. Each node is fully checked and then exposed with the ordinary +checker `whnf`; Pi domains retain their raw syntax, while bodies are traversed +under the structurally certified annotation-consumed local declarations used +by kernel checking. Every raw/consumed domain pair is also checked by an exact +successful ordinary-checker `isDefEq` run. The recursion budget is the +configured inductive fuel, while every checker run uses the configured +transparency/fuel. + +The returned trace is only a candidate analysis view and operational +provenance. It is not stored in the kernel environment and acquires semantic +authority only after Verify reconstructs and refines every retained checker +run. -/ +def buildCandidateExpr (e : Expr) : M (CandidateExpr e) := do + let context ← readThe Context + return ⟨context, ← loop context e context.fuel.inductiveFuel⟩ +where + loop (context : Context) (e : Expr) : + Nat → Except Exception (CandidateExprTrace context e) + | 0 => throw .deepRecursion + | fuel + 1 => do + match observeCandidateCheckType context e with + | .error err => throw err + | .ok ⟨inferred, checked⟩ => + match observeCandidateWhnf context e with + | .error err => throw err + | .ok ⟨view, valid⟩ => + match view, valid with + | .forallE name domain body binderInfo, valid => + match hfresh : context.lctx.find? context.freshFVarId with + | some _ => + throw (Exception.other + "normalization candidate generated a duplicate free variable") + | none => + let annotations ← buildCandidateTypeAnnotations domain + let ⟨annotationsEq⟩ ← observeCandidateIsDefEq + context domain annotations.consumed + let domainCandidate ← loop context domain fuel + let bodyContext := + context.pushLocalDecl name binderInfo + annotations.consumed + let bodyCandidate ← loop bodyContext + (body.instantiate1 context.freshExpr) fuel + return .forallE context e inferred name domain body + binderInfo hfresh annotations annotationsEq checked valid + domainCandidate bodyCandidate + | result, valid => + return .terminal context e inferred result checked valid + +/-- One terminal recursive step of `buildCandidateExpr`, with its traversal +budget made explicit. This is the reusable reduction seam for exact producer +fixtures; all semantic evidence remains the ordinary checker executions +stored in the resulting trace. -/ +theorem buildCandidateExpr_loop_of_whnf_nonForall + (context : Context) (e inferred view : Expr) (fuel : Nat) + (hcheck : CandidateCheckTypeStep.Valid + ⟨context, e, inferred⟩) + (hrun : CandidateWhnfStep.Valid ⟨context, e, view⟩) + (hview : view.isForall = false) : + buildCandidateExpr.loop context e (fuel + 1) = + .ok (.terminal context e inferred view hcheck hrun) := by + unfold buildCandidateExpr.loop + rw [observeCandidateCheckType_of_run context e inferred hcheck] + rw [observeCandidateWhnf_of_run context e view hrun] + cases view <;> + simp_all [Expr.isForall, Pure.pure, Except.pure] + +/-- One forall recursive step of `buildCandidateExpr`, exposing the exact +child executions used at the decremented traversal budget. -/ +theorem buildCandidateExpr_loop_of_whnf_forall + (context : Context) (e inferred : Expr) (fuel : Nat) + (name : Name) (domain body : Expr) (binderInfo : BinderInfo) + (hfresh : context.lctx.find? context.freshFVarId = none) + (annotations : CandidateTypeAnnotations domain) + (hannotations : + buildCandidateTypeAnnotations domain = .ok annotations) + (hannotationsEq : CandidateIsDefEqStep.Valid + ⟨context, domain, annotations.consumed⟩) + (hcheck : CandidateCheckTypeStep.Valid + ⟨context, e, inferred⟩) + (hrun : CandidateWhnfStep.Valid + ⟨context, e, .forallE name domain body binderInfo⟩) + (domainCandidate : CandidateExprTrace context domain) + (bodyCandidate : CandidateExprTrace + (context.pushLocalDecl name binderInfo annotations.consumed) + (body.instantiate1 context.freshExpr)) + (hdomain : + buildCandidateExpr.loop context domain fuel = + .ok domainCandidate) + (hbody : + buildCandidateExpr.loop + (context.pushLocalDecl name binderInfo annotations.consumed) + (body.instantiate1 context.freshExpr) fuel = + .ok bodyCandidate) : + buildCandidateExpr.loop context e (fuel + 1) = + .ok (.forallE context e inferred name domain body binderInfo + hfresh annotations hannotationsEq hcheck hrun + domainCandidate bodyCandidate) := by + unfold buildCandidateExpr.loop + simp only [observeCandidateCheckType_of_run context e inferred hcheck, + observeCandidateWhnf_of_run context e + (.forallE name domain body binderInfo) hrun] + split + · simp_all + · simp [Bind.bind, Except.bind, hannotations, + observeCandidateIsDefEq_of_run context domain + annotations.consumed hannotationsEq, + hdomain, hbody, Pure.pure, Except.pure] + +/-- Every annotation choice on a successfully built candidate main spine +comes from the transparent annotation builder used by the ordinary producer. +This recovers validator-replay provenance from the executable traversal +itself; callers do not supply an independent annotation premise. -/ +theorem CandidateExprTrace.validationAnnotations_of_loop + {context : Context} {source : Expr} {fuel : Nat} + {candidate : CandidateExprTrace context source} + (h : buildCandidateExpr.loop context source fuel = .ok candidate) : + candidate.validationAnnotations := by + fun_induction buildCandidateExpr.loop context source fuel <;> + simp_all + case case5 => + simp only [Bind.bind, Except.bind] at h + repeat' split at h + all_goals try simp_all [Functor.map, Except.map] + repeat' split at h + all_goals try simp_all + subst candidate + constructor + · apply CandidateTypeAnnotations.matches_of_build + assumption + · apply_assumption + assumption + case case6 => + simp only [Pure.pure, Except.pure, Except.ok.injEq] at h + subst candidate + trivial + +/-- A successful ordinary candidate-expression call carries the complete +annotation provenance needed to replay family validation. -/ +theorem CandidateExpr.validationAnnotations_of_build + {context : Context} {source : Expr} + {candidate : CandidateExpr source} + (h : buildCandidateExpr source context = .ok candidate) : + candidate.trace.validationAnnotations := by + unfold buildCandidateExpr at h + simp [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, Pure.pure, Except.bind, Except.pure] at h + split at h <;> try simp_all + simp [ReaderT.pure, Pure.pure, Except.pure] at h + subst candidate + apply CandidateExprTrace.validationAnnotations_of_loop + assumption + +/-- The context stored at the root of a successful expression candidate is +the exact reader context in which the ordinary builder was executed. -/ +theorem CandidateExpr.context_eq_of_build + {context : Context} {source : Expr} + {candidate : CandidateExpr source} + (h : buildCandidateExpr source context = .ok candidate) : + candidate.context = context := by + unfold buildCandidateExpr at h + simp [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, Pure.pure, Except.bind, Except.pure] at h + split at h <;> try simp_all + simp [ReaderT.pure, Pure.pure, Except.pure] at h + subst candidate + rfl + +/-- Erase the operational trace and retain only the analysis expression. -/ +def normalizeCandidateExpr (e : Expr) : M Expr := do + return (← buildCandidateExpr e).view + +/-- A successful ordinary WHNF run to a non-Pi expression is exactly one +terminal step of the candidate traversal. This exposes the operational seam +used by Verify without duplicating the `ReaderT`/checker lift plumbing in +every certificate. -/ +theorem buildCandidateExpr_of_whnf_nonForall + (context : Context) (e inferred view : Expr) + (hfuel : 0 < context.fuel.inductiveFuel) + (hcheck : + TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.checkType e) = + .ok inferred) + (hrun : + TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.whnf e) = + .ok view) + (hview : view.isForall = false) : + buildCandidateExpr e context = + .ok ⟨context, .terminal context e inferred view hcheck hrun⟩ := by + cases hf : context.fuel.inductiveFuel with + | zero => omega + | succ fuel => + cases hresult : + TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.whnf e) with + | error err => simp [hresult] at hrun + | ok result => + have : result = view := by simpa [hresult] using hrun + subst result + unfold buildCandidateExpr + unfold buildCandidateExpr.loop + simp [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, Pure.pure, Except.bind, Except.pure, + hf, observeCandidateCheckType_of_run context e inferred hcheck, + observeCandidateWhnf_of_run context e view hrun] + cases view <;> + simp_all [ReaderT.pure, Pure.pure, Except.pure, Expr.isForall] + +theorem normalizeCandidateExpr_of_whnf_nonForall + (context : Context) (e inferred view : Expr) + (hfuel : 0 < context.fuel.inductiveFuel) + (hcheck : + TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.checkType e) = + .ok inferred) + (hrun : + TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.whnf e) = + .ok view) + (hview : view.isForall = false) : + normalizeCandidateExpr e context = .ok view := by + simp [normalizeCandidateExpr, ReaderT.bind, Bind.bind, + ReaderT.pure, Pure.pure, Except.bind, Except.pure, + CandidateExpr.view, CandidateExprTrace.view, + buildCandidateExpr_of_whnf_nonForall context e inferred view + hfuel hcheck hrun hview] + +/-- A dependent list retains the exact source position of each candidate. -/ +inductive CandidateList {α : Type} (F : α → Type) : List α → Type + | nil : CandidateList F [] + | cons : F a → CandidateList F as → CandidateList F (a :: as) + +namespace CandidateList + +def toList (f : (a : α) → F a → β) : + CandidateList F source → List β + | .nil => [] + | .cons head tail => f _ head :: tail.toList f + +/-- Eliminate a source-indexed singleton without a partial list operation. -/ +def singleton : CandidateList F [source] → F source + | .cons head .nil => head + +/-- A source-indexed singleton list is completely determined by its total +singleton projection. This eta law lets retained producer witnesses be +reindexed at staged singleton APIs without inspecting or replacing their +candidate payload. -/ +theorem singleton_eta (candidates : CandidateList F [source]) : + candidates = .cons candidates.singleton .nil := by + cases candidates with + | cons head tail => + cases tail + rfl + +end CandidateList + +/-- Candidate for one constructor; its header is always taken from `source`. -/ +structure CandidateConstructor (source : Constructor) where + type : CandidateExpr source.type + +def CandidateConstructor.view + (candidate : CandidateConstructor source) : Constructor := + { source with type := candidate.type.view } + +/-- Candidate for one family type, computed before raw family insertion. -/ +structure CandidateFamilyType (source : InductiveType) where + type : CandidateExpr source.type + +/-- Complete candidate for one family, with constructor traces computed only +after raw family insertion. -/ +structure CandidateFamily (source : InductiveType) where + familyType : CandidateFamilyType source + constructors : + CandidateList CandidateConstructor source.ctors + +/-- Project the pre-family candidate spine from a complete dependent family +candidate list without erasing source indices or using a parallel list. -/ +def CandidateList.familyTypes : + {sources : List InductiveType} → + CandidateList CandidateFamily sources → + CandidateList CandidateFamilyType sources + | [], .nil => .nil + | _ :: _, .cons family families => + .cons family.familyType families.familyTypes + +def CandidateFamily.view (candidate : CandidateFamily source) : InductiveType := + { source with + type := candidate.familyType.type.view + ctors := candidate.constructors.toList fun _ ctor => ctor.view } + +def normalizeCandidateConstructor + (ctor : Constructor) : M (CandidateConstructor ctor) := do + return ⟨← buildCandidateExpr ctor.type⟩ + +/-- The root context stored by a successful constructor normalization is the +exact post-family reader context used for that traversal. -/ +theorem CandidateConstructor.context_eq_of_normalize + {context : Context} {source : Constructor} + {candidate : CandidateConstructor source} + (h : normalizeCandidateConstructor source context = .ok candidate) : + candidate.type.context = context := by + unfold normalizeCandidateConstructor at h + simp only [ReaderT.bind, Bind.bind] at h + cases hbuild : buildCandidateExpr source.type context with + | error error => + simp [Except.bind, hbuild] at h + | ok type => + simp [Except.bind, ReaderT.pure, Pure.pure, Except.pure, hbuild] at h + subst candidate + exact CandidateExpr.context_eq_of_build hbuild + +def normalizeCandidateFamilyType + (indType : InductiveType) : M (CandidateFamilyType indType) := do + return ⟨← buildCandidateExpr indType.type⟩ + +/-- A successful family-type normalization carries the annotation provenance +of its underlying executable expression traversal. -/ +theorem CandidateFamilyType.validationAnnotations_of_normalize + {context : Context} {source : InductiveType} + {candidate : CandidateFamilyType source} + (h : normalizeCandidateFamilyType source context = .ok candidate) : + candidate.type.trace.validationAnnotations := by + unfold normalizeCandidateFamilyType at h + simp only [ReaderT.bind, Bind.bind] at h + cases hbuild : buildCandidateExpr source.type context with + | error error => + simp [Except.bind, hbuild] at h + | ok type => + simp [Except.bind, ReaderT.pure, Pure.pure, Except.pure, hbuild] at h + subst candidate + exact type.validationAnnotations_of_build hbuild + +/-- The root context stored by a successful family-type normalization is the +exact pre-family reader context used for that traversal. -/ +theorem CandidateFamilyType.context_eq_of_normalize + {context : Context} {source : Lean.InductiveType} + {candidate : CandidateFamilyType source} + (h : normalizeCandidateFamilyType source context = .ok candidate) : + candidate.type.context = context := by + unfold normalizeCandidateFamilyType at h + simp only [ReaderT.bind, Bind.bind] at h + cases hbuild : buildCandidateExpr source.type context with + | error error => + simp [Except.bind, hbuild] at h + | ok type => + simp [Except.bind, ReaderT.pure, Pure.pure, Except.pure, hbuild] at h + subst candidate + exact CandidateExpr.context_eq_of_build hbuild + +def normalizeCandidateConstructorList : + (ctors : List Constructor) → + M (CandidateList CandidateConstructor ctors) + | [] => return .nil + | ctor :: ctors => do + return .cons (← normalizeCandidateConstructor ctor) + (← normalizeCandidateConstructorList ctors) + +def normalizeCandidateFamilyTypeList : + (types : List InductiveType) → + M (CandidateList CandidateFamilyType types) + | [] => return .nil + | indType :: types => do + return .cons (← normalizeCandidateFamilyType indType) + (← normalizeCandidateFamilyTypeList types) + +def normalizeCandidateFamilyList : + {types : List InductiveType} → + CandidateList CandidateFamilyType types → + M (CandidateList CandidateFamily types) + | [], .nil => return .nil + | indType :: _, .cons familyType tail => do + return .cons + { familyType, + constructors := ← + normalizeCandidateConstructorList indType.ctors } + (← normalizeCandidateFamilyList tail) + +/-- Exact successful traversal of an arbitrary source-indexed family-type +list. The dependent indices prevent a proof for one metadata position from +being reused at another position or from silently truncating the source. -/ +inductive CandidateFamilyTypeListProduced (context : Context) : + {sources : List InductiveType} → + CandidateList CandidateFamilyType sources → Prop where + | nil : CandidateFamilyTypeListProduced context .nil + | cons + (head : normalizeCandidateFamilyType source context = .ok candidate) + (tail : CandidateFamilyTypeListProduced context candidates) : + CandidateFamilyTypeListProduced context (.cons candidate candidates) + +/-- A source-indexed family-type traversal determines the complete executable +list result for any length, without a fixture-specific list reduction. -/ +theorem CandidateFamilyTypeListProduced.normalize + {sources : List InductiveType} + {candidates : CandidateList CandidateFamilyType sources} + (run : CandidateFamilyTypeListProduced context candidates) : + normalizeCandidateFamilyTypeList sources context = .ok candidates := by + induction run with + | nil => rfl + | cons head tail ih => + unfold normalizeCandidateFamilyTypeList + simp only [ReaderT.bind, Bind.bind] + rw [head, ih] + rfl + +/-- A successful singleton family-type traversal retains the annotation +provenance of the exact candidate selected at its sole source position. -/ +theorem CandidateFamilyTypeListProduced.singleton_validationAnnotations + {context : Context} {source : Lean.InductiveType} + {candidates : CandidateList CandidateFamilyType [source]} + (run : CandidateFamilyTypeListProduced context candidates) : + candidates.singleton.type.trace.validationAnnotations := by + cases run with + | cons head tail => + cases tail + exact CandidateFamilyType.validationAnnotations_of_normalize head + +/-- A successful singleton family-type traversal stores its exact traversal +context at the candidate root. -/ +theorem CandidateFamilyTypeListProduced.singleton_context_eq + {context : Context} {source : Lean.InductiveType} + {candidates : CandidateList CandidateFamilyType [source]} + (run : CandidateFamilyTypeListProduced context candidates) : + candidates.singleton.type.context = context := by + cases run with + | cons head tail => + cases tail + exact CandidateFamilyType.context_eq_of_normalize head + +/-- Exact successful traversal of an arbitrary source-indexed constructor +list in one post-family context. Every candidate remains indexed by its source +constructor, so ordering, length, and header provenance are preserved by the +type rather than recovered from an erased list equality. -/ +inductive CandidateConstructorListProduced (context : Context) : + {sources : List Constructor} → + CandidateList CandidateConstructor sources → Prop where + | nil : CandidateConstructorListProduced context .nil + | cons + (head : normalizeCandidateConstructor source context = .ok candidate) + (tail : CandidateConstructorListProduced context candidates) : + CandidateConstructorListProduced context (.cons candidate candidates) + +/-- A source-indexed constructor traversal determines the complete executable +list result for any length, with no `zip`, partial lookup, or fixture-specific +cons-chain reduction. -/ +theorem CandidateConstructorListProduced.normalize + {sources : List Constructor} + {candidates : CandidateList CandidateConstructor sources} + (run : CandidateConstructorListProduced context candidates) : + normalizeCandidateConstructorList sources context = .ok candidates := by + induction run with + | nil => rfl + | cons head tail ih => + unfold normalizeCandidateConstructorList + simp only [ReaderT.bind, Bind.bind] + rw [head, ih] + rfl + +/-- A successful singleton constructor traversal stores its exact traversal +context at the candidate root. -/ +theorem CandidateConstructorListProduced.singleton_context_eq + {context : Context} {source : Constructor} + {candidates : CandidateList CandidateConstructor [source]} + (run : CandidateConstructorListProduced context candidates) : + candidates.singleton.type.context = context := by + cases run with + | cons head tail => + cases tail + exact CandidateConstructor.context_eq_of_normalize head + +/-- Exact successful assembly of complete family candidates from an already +source-indexed family-type list. Each constructor traversal is tied to the +corresponding family source, and the tail remains tied to the remaining family +sources. This is the reusable ordered-list boundary needed before mutual-block +staging. -/ +inductive CandidateFamilyListProduced (context : Context) : + {sources : List InductiveType} → + CandidateList CandidateFamilyType sources → + CandidateList CandidateFamily sources → Prop where + | nil : CandidateFamilyListProduced context .nil .nil + | cons + (constructors : CandidateConstructorListProduced + context family.constructors) + (tail : CandidateFamilyListProduced context familyTypes families) : + CandidateFamilyListProduced context + (.cons family.familyType familyTypes) (.cons family families) + +/-- Source-indexed family assembly determines the exact executable family-list +result for arbitrary list lengths. -/ +theorem CandidateFamilyListProduced.normalize + {sources : List InductiveType} + {familyTypes : CandidateList CandidateFamilyType sources} + {families : CandidateList CandidateFamily sources} + (run : CandidateFamilyListProduced context familyTypes families) : + normalizeCandidateFamilyList familyTypes context = .ok families := by + induction run with + | nil => rfl + | cons constructors tail ih => + unfold normalizeCandidateFamilyList + simp only [ReaderT.bind, Bind.bind] + rw [constructors.normalize, ih] + rfl + +/-- Singleton family assembly reuses, without replacement, the family-type +candidate produced in the pre-family environment. -/ +theorem CandidateFamilyListProduced.singleton_familyType + {context : Context} {source : Lean.InductiveType} + {familyTypes : CandidateList CandidateFamilyType [source]} + {families : CandidateList CandidateFamily [source]} + (run : CandidateFamilyListProduced context familyTypes families) : + families.singleton.familyType = familyTypes.singleton := by + cases run with + | cons constructors tail => + cases tail + rfl + +/-- Singleton family assembly exposes the exact source-indexed constructor +traversal retained for its sole family. -/ +theorem CandidateFamilyListProduced.singleton_constructors + {context : Context} {source : Lean.InductiveType} + {familyTypes : CandidateList CandidateFamilyType [source]} + {families : CandidateList CandidateFamily [source]} + (run : CandidateFamilyListProduced context familyTypes families) : + CandidateConstructorListProduced context + families.singleton.constructors := by + cases run with + | cons constructors tail => + cases tail + exact constructors + +/-- A successful singleton family assembly can be reindexed directly by the +family-type payload retained in its assembled result. This dependent eta law +avoids rewriting the input list underneath the execution witness. -/ +theorem CandidateFamilyListProduced.singleton_reindex + {context : Context} {source : Lean.InductiveType} + {familyTypes : CandidateList CandidateFamilyType [source]} + {families : CandidateList CandidateFamily [source]} + (run : CandidateFamilyListProduced context familyTypes families) : + CandidateFamilyListProduced context + (.cons families.singleton.familyType .nil) families := by + cases run with + | cons constructors tail => + cases tail + exact .cons constructors .nil + +/-- Shape-preserving output of the executable normalization-candidate pass. +The dependent family/constructor lists prevent positional provenance from +being silently reused for a different inductive request. Names, ordering, and +record headers come from the indexed source; only expression payloads are +reconstructed from candidate traces. -/ +structure NormalizationCandidate (source : List InductiveType) where + families : CandidateList CandidateFamily source + +def NormalizationCandidate.view + (candidate : NormalizationCandidate source) : List InductiveType := + candidate.families.toList fun _ family => family.view + +/-- One exact family-type traversal result together with the source-indexed +operational witness that produced it. The witness is provenance for later +Verify staging; it carries no Theory semantics. -/ +structure CandidateFamilyTypeListExecution (context : Context) + (sources : List InductiveType) where + candidates : CandidateList CandidateFamilyType sources + produced : CandidateFamilyTypeListProduced context candidates + +/-- Run the existing family-type normalizer while retaining its exact +source-ordered traversal equations. Errors and candidate data are unchanged. -/ +def executeCandidateFamilyTypeList (context : Context) : + (sources : List InductiveType) → + Except Exception (CandidateFamilyTypeListExecution context sources) + | [] => .ok ⟨.nil, .nil⟩ + | source :: sources => + match hhead : normalizeCandidateFamilyType source context with + | .error error => .error error + | .ok head => + match executeCandidateFamilyTypeList context sources with + | .error error => .error error + | .ok tail => .ok { + candidates := .cons head tail.candidates + produced := .cons (by simpa using hhead) tail.produced } + +/-- One exact constructor traversal result together with its source-indexed +operational witness. -/ +structure CandidateConstructorListExecution (context : Context) + (sources : List Constructor) where + candidates : CandidateList CandidateConstructor sources + produced : CandidateConstructorListProduced context candidates + +/-- Run the existing constructor normalizer while retaining its exact +source-ordered traversal equations. -/ +def executeCandidateConstructorList (context : Context) : + (sources : List Constructor) → + Except Exception (CandidateConstructorListExecution context sources) + | [] => .ok ⟨.nil, .nil⟩ + | source :: sources => + match hhead : normalizeCandidateConstructor source context with + | .error error => .error error + | .ok head => + match executeCandidateConstructorList context sources with + | .error error => .error error + | .ok tail => .ok { + candidates := .cons head tail.candidates + produced := .cons (by simpa using hhead) tail.produced } + +/-- One exact family/constructor assembly result together with both dependent +source lists retained by the ordinary traversal. -/ +structure CandidateFamilyListExecution (context : Context) + {sources : List InductiveType} + (familyTypes : CandidateList CandidateFamilyType sources) where + candidates : CandidateList CandidateFamily sources + produced : CandidateFamilyListProduced context familyTypes candidates + +/-- Run the existing family assembler while retaining each constructor-list +execution. This is an operational refinement of +`normalizeCandidateFamilyList`, not an additional acceptance premise. -/ +def executeCandidateFamilyList (context : Context) : + {sources : List InductiveType} → + (familyTypes : CandidateList CandidateFamilyType sources) → + Except Exception (CandidateFamilyListExecution context familyTypes) + | [], .nil => .ok ⟨.nil, .nil⟩ + | source :: _, .cons familyType familyTypes => + match executeCandidateConstructorList context source.ctors with + | .error error => .error error + | .ok constructors => + match executeCandidateFamilyList context familyTypes with + | .error error => .error error + | .ok tail => + let family : CandidateFamily source := { + familyType + constructors := constructors.candidates } + .ok { + candidates := .cons family tail.candidates + produced := by + change CandidateFamilyListProduced context + (.cons family.familyType familyTypes) + (.cons family tail.candidates) + exact .cons constructors.produced tail.produced } + +/-- Detailed operational result of `buildNormalizationCandidate`. + +The ordinary result erases to `candidate`. The remaining fields retain the +validator-selected statistics, intermediate environment, and exact list +traversals already executed by the same call. Verify uses these equations as +staging provenance; all semantic authority still comes from the D1--D4 +interpreters. -/ +structure NormalizationCandidateExecution + (nparams : Nat) (types : List InductiveType) + (numNested : Nat) (isUnsafe : Bool) (candidateContext : Context) where + validationContext : Context + stats : InductiveStats + familyTypes : CandidateFamilyTypeListExecution + { candidateContext with lctx := {} } types + familyEnv : Environment + declareRun : declareInductiveTypes stats nparams types.toArray + numNested isUnsafe validationContext = .ok familyEnv + constructorRun : checkConstructors types.toArray stats isUnsafe + { validationContext with env := familyEnv } = .ok () + families : CandidateFamilyListExecution + { candidateContext with env := familyEnv, lctx := {} } + familyTypes.candidates + +def NormalizationCandidateExecution.candidate + (execution : NormalizationCandidateExecution nparams types numNested + isUnsafe candidateContext) : NormalizationCandidate types := + ⟨execution.families.candidates⟩ + +/-- The post-family half of the detailed ordinary execution. -/ +def buildNormalizationCandidateExecutionAfterValidation + (nparams : Nat) (types : List InductiveType) + (numNested : Nat) (isUnsafe : Bool) (candidateContext : Context) + (stats : InductiveStats) : + M (NormalizationCandidateExecution nparams types numNested isUnsafe + candidateContext) := + fun validationContext => + match executeCandidateFamilyTypeList + { candidateContext with lctx := {} } types with + | .error error => .error error + | .ok familyTypes => + match hdeclare : declareInductiveTypes stats nparams types.toArray + numNested isUnsafe validationContext with + | .error error => .error error + | .ok familyEnv => + match hconstructors : checkConstructors types.toArray stats isUnsafe + { validationContext with env := familyEnv } with + | .error error => .error error + | .ok () => + match executeCandidateFamilyList + { candidateContext with env := familyEnv, lctx := {} } + familyTypes.candidates with + | .error error => .error error + | .ok families => .ok { + validationContext + stats + familyTypes + familyEnv + declareRun := by simpa using hdeclare + constructorRun := by simpa using hconstructors + families } + +/-- A retained successful post-validation execution exposes exactly the +statistics and reader context supplied by the family validator. -/ +theorem NormalizationCandidateExecution.fields_of_afterValidation + (execution : NormalizationCandidateExecution nparams types numNested + isUnsafe candidateContext) + (stats : InductiveStats) (validationContext : Context) + (h : buildNormalizationCandidateExecutionAfterValidation nparams types + numNested isUnsafe candidateContext stats validationContext = + .ok execution) : + execution.stats = stats ∧ + execution.validationContext = validationContext := by + unfold buildNormalizationCandidateExecutionAfterValidation at h + repeat' split at h + all_goals try simp_all + subst execution + exact ⟨rfl, rfl⟩ + +/-- Execute the ordinary singleton/mutual candidate pass while retaining the +exact operational provenance erased by the public candidate result. -/ +def buildNormalizationCandidateExecution + (nparams : Nat) (types : List InductiveType) + (numNested : Nat) (isUnsafe : Bool) (candidateContext : Context) : + Except Exception (NormalizationCandidateExecution nparams types + numNested isUnsafe candidateContext) := + checkInductiveTypes nparams types.toArray (fun stats => + buildNormalizationCandidateExecutionAfterValidation nparams types + numNested isUnsafe candidateContext stats) candidateContext + +/-- Run the generic one-pass candidate producer at the same two environments +as kernel inductive checking: family types in the input environment, then +constructor types after insertion of every raw family constant. + +This repeats the ordinary family/constructor validity checks so a candidate +cannot be obtained from metadata already rejected at those stages. The Theory +analyzer and Verify semantic certificate remain separate downstream gates. -/ +def buildNormalizationCandidate + (nparams : Nat) (types : List InductiveType) + (numNested : Nat) (isUnsafe : Bool) : + M (NormalizationCandidate types) := + -- Family validation retains its parameter/index telescope while invoking + -- the continuation. That context is required by `checkConstructors`, whose + -- parameter checks refer to the free variables recorded in `stats`, but it + -- is not part of the closed metadata being normalized. Snapshot the entry + -- context so both candidate traversals use one stable fresh-name provenance + -- and an empty local context; only the staged kernel environment changes. + fun candidateContext => + let indTypes := types.toArray + checkInductiveTypes nparams indTypes (fun stats => do + let familyTypes ← + withReader (fun _ : Context => { candidateContext with lctx := {} }) do + normalizeCandidateFamilyTypeList types + let familyEnv ← + declareInductiveTypes stats nparams indTypes numNested isUnsafe + withEnv familyEnv do + checkConstructors indTypes stats isUnsafe + let families ← + withReader (fun _ : Context => + { candidateContext with env := familyEnv, lctx := {} }) do + normalizeCandidateFamilyList familyTypes + return ⟨families⟩) candidateContext + +/-- Erase a retained successful execution back to the unchanged public +candidate producer. The family-validation equation supplies the continuation +boundary selected by `checkInductiveTypes`; every later rewrite comes from an +operation already stored in `execution`. -/ +theorem NormalizationCandidateExecution.produces + (execution : NormalizationCandidateExecution nparams types numNested + isUnsafe candidateContext) + (validationRun : ∀ {α} (k : InductiveStats → M α), + checkInductiveTypes nparams types.toArray k candidateContext = + k execution.stats execution.validationContext) : + buildNormalizationCandidate nparams types numNested isUnsafe + candidateContext = .ok execution.candidate := by + unfold buildNormalizationCandidate + rw [validationRun] + simp only [ReaderT.bind, Bind.bind] + rw [show + (withReader (fun _ : Context => + { candidateContext with lctx := {} }) + (normalizeCandidateFamilyTypeList types)) execution.validationContext = + .ok execution.familyTypes.candidates by + change normalizeCandidateFamilyTypeList types + { candidateContext with lctx := {} } = _ + exact execution.familyTypes.produced.normalize] + simp only [Except.bind] + rw [execution.declareRun] + unfold withEnv + change (ReaderT.bind + (checkConstructors types.toArray execution.stats isUnsafe) + (fun _ => ReaderT.bind + (withReader (fun _ : Context => + { candidateContext with + env := execution.familyEnv, lctx := {} }) + (normalizeCandidateFamilyList execution.familyTypes.candidates)) + (fun families => pure + (⟨families⟩ : NormalizationCandidate types)))) + ({ execution.validationContext with + env := execution.familyEnv } : Context) = _ + simp only [ReaderT.bind, Bind.bind] + rw [execution.constructorRun] + simp only [Except.bind] + rw [show + (withReader (fun _ : Context => + { candidateContext with + env := execution.familyEnv, lctx := {} }) + (normalizeCandidateFamilyList execution.familyTypes.candidates)) + { execution.validationContext with env := execution.familyEnv } = + .ok execution.families.candidates by + change normalizeCandidateFamilyList execution.familyTypes.candidates + { candidateContext with env := execution.familyEnv, lctx := {} } = _ + exact execution.families.produced.normalize] + rfl + +#guard_named_axioms Ix.Theory.Named.AddInductive.buildCandidateExpr [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.observeCandidateIsDefEq_of_run [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.buildCandidateExpr_loop_of_whnf_nonForall [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.buildCandidateExpr_loop_of_whnf_forall [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateTypeAnnotationTrace.build [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateTypeAnnotationTrace.build_consumed [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.buildCandidateTypeAnnotations [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateTypeAnnotations.matches_of_build [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.buildCandidateCheckType [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.buildNormalizationCandidate [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateFamilyTypeListProduced.normalize [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateConstructorListProduced.normalize [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateFamilyListProduced.normalize [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateList.singleton [] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateExprTrace.storedSpine [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateExprTrace.spineLength [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateExprTrace.rootWhnf_valid [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateExprTrace.terminalContext_lparams [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateExprTrace.parameterList_length [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateExprTrace.checkInductiveTypes_loop_of_candidate [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateExprTrace.checkInductiveTypes_singleton_of_candidate [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateExprTrace.FamilyValidationRun [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateExprTrace.FamilyValidationRun.parameters [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateExprTrace.FamilyValidationRun.numIndices [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateExpr.step_valid [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateExpr.checkStep_valid [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateExpr.isDefEqStep_valid [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateWhnfStep.innerRun [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateCheckTypeStep.innerRun [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CandidateIsDefEqStep.innerRun [propext, Classical.choice, Quot.sound] + +def declareConstructors (stats : InductiveStats) + (indTypes : Array InductiveType) (isUnsafe : Bool) : M Environment := + fun c => indTypes.foldlM (init := c.env) fun env indType => do + let (_, env) ← indType.ctors.foldlM (init := (0, env)) fun (cidx, env) ctor => do + let type := ctor.type + let rec arity i + | .forallE _ _ body _ => arity (i+1) body + | _ => i + let arity := arity 0 type + env.checkName ctor.name c.allowPrimitive + pure (cidx + 1, env.add <| .ctorInfo { + type, cidx, isUnsafe + levelParams := c.lparams + name := ctor.name + induct := indType.name + numParams := stats.params.size + numFields := assert! arity ≥ stats.params.size; arity - stats.params.size + }) + pure env + +/-- Return true if recursor can map into any universe -/ +def isLargeEliminator (stats : InductiveStats) (indTypes : Array InductiveType) : M Bool := do + if stats.isNotZero then return true + let #[indType] := indTypes | return false + match indType.ctors with + | [] => return true + | [ctor] => + let rec loop type i toCheck + | 0 => throw .deepRecursion + | fuel+1 => do + if let .forallE name dom body bi := type then + withLocalDecl name bi (consumeTypeAnnotations dom) fun arg => do + let mut toCheck := toCheck + if i ≥ stats.params.size then + if !(← ensureType dom).sortLevel!.isAlwaysZero then + toCheck := toCheck.push arg + loop (body.instantiate1 arg) (i + 1) toCheck fuel + else + return toCheck.all type.getAppArgs.contains + loop ctor.type 0 #[] (← readThe Context).fuel.inductiveFuel + | _ => return false + +/-- Search the kernel's elimination-universe name sequence (`u`, `u_1`, …) +for its first entry not already used by the inductive declaration. Among +`lparams.length + 1` distinct candidates at least one is available; the +zero-fuel branch is therefore only a totality fallback. -/ +def getFreshElimParam.loop (lparams : List Name) (u : Name) (i : Nat) : + Nat → Name + | 0 => u + | fuel + 1 => + if lparams.contains u then + loop lparams ((`u).appendIndexAfter i) (i + 1) fuel + else + u + +def getFreshElimParam (lparams : List Name) : Name := + getFreshElimParam.loop lparams `u 1 (lparams.length + 1) + +def getElimLevel (stats : InductiveStats) (indTypes : Array InductiveType) : + M Level := do + unless ← isLargeEliminator stats indTypes do return .zero + let {lparams, ..} ← read + return .param (getFreshElimParam lparams) + +/-- The constructor-shape fragment of the kernel's K-target test. A visible +Pi is accepted only while it belongs to the shared parameter prefix; the +first visible field makes the target ineligible. -/ +def isKTargetCtor (nparams : Nat) : Nat → Expr → Bool + | i, .forallE _ _ body _ => i < nparams && isKTargetCtor nparams (i + 1) body + | _, _ => true + +def isKTarget (stats : InductiveStats) (indTypes : Array InductiveType) : M Bool := do + let #[indType] := indTypes | return false + unless stats.resultLevel.isAlwaysZero do return false + let [ctor] := indType.ctors | return false + return isKTargetCtor stats.params.size 0 ctor.type + +@[inline] def getIIndices (stats : InductiveStats) (t : Expr) : Nat × Array Expr := + ((isValidIndApp? stats t).get!, t.getAppArgs[stats.params.size:]) + +-- FIXME: The function below has been exploded into nested loops as standalone functions +-- because I couldn't get them all to compile together as `let rec`s. +namespace mkRecInfos + +def loopArgs1 (stats : InductiveStats) (type : Expr) (i : Nat) (indices : Array Expr) + (fuel : Nat) (k : Array Expr → M α) : M α := match fuel with + | 0 => throw .deepRecursion + | fuel+1 => do + if let .forallE name dom body bi := type then + if i < stats.params.size then + loopArgs1 stats (← whnf <| body.instantiate1 stats.params[i]!) (i + 1) indices fuel k + else + withLocalDecl name bi (consumeTypeAnnotations dom) fun arg => do + loopArgs1 stats (← whnf <| body.instantiate1 arg) i (indices.push arg) fuel k + else + k indices + +variable (stats : InductiveStats) (indTypes : Array InductiveType) (elimLevel : Level) in +def loopInd1 (dIdx : Nat) (recInfos : Array RecInfo) (k : Array RecInfo → M α) : M α := do + if _h : dIdx < indTypes.size then + let ctx ← readThe Context + loopArgs1 stats (← whnf indTypes[dIdx].type) 0 #[] ctx.fuel.inductiveFuel fun indices => + let tTy := mkAppN (mkAppN stats.indConsts[dIdx]! stats.params) indices + withLocalDecl `t .default (consumeTypeAnnotations tTy) fun major => do + let lctx ← getLCtx + let motiveTy := lctx.mkForall indices <| lctx.mkForall #[major] <| .sort elimLevel + let name := if indTypes.size > 1 then (`motive).appendIndexAfter (dIdx+1) else `motive + withLocalDecl name .default (consumeTypeAnnotations motiveTy) fun motive => do + loopInd1 (dIdx + 1) (recInfos.push { motive, minors := #[], indices, major }) k + else + k recInfos +termination_by indTypes.size - dIdx + +variable (stats : InductiveStats) in +def loopCtorArgs (t : Expr) (k : Expr → Array Expr → Array Expr → M α) : M α := do + loop t 0 #[] #[] (← readThe Context).fuel.inductiveFuel +where + loop t i bu u + | 0 => throw .deepRecursion + | fuel+1 => do + if let .forallE name dom body bi := t then + if let some param := stats.params[i]? then + loop (body.instantiate1 param) (i + 1) bu u fuel + else + withLocalDecl name bi (consumeTypeAnnotations dom) fun arg => do + let bu := bu.push arg + let u := if (← isRecArg stats dom).isSome then u.push arg else u + loop (body.instantiate1 arg) (i + 1) bu u fuel + else k t bu u + +def loopUArgs (ui : Expr) (k : Expr → Array Expr → M α) : M α := do + loop (← whnf (← inferType ui)) #[] (← readThe Context).fuel.inductiveFuel +where + loop uiTy xs + | 0 => throw .deepRecursion + | fuel+1 => do + if let .forallE name dom body bi := uiTy then + withLocalDecl name bi (consumeTypeAnnotations dom) fun arg => do + loop (← whnf <| body.instantiate1 arg) (xs.push arg) fuel + else + k uiTy xs + +variable (stats : InductiveStats) (u : Array Expr) (recInfos : Array RecInfo) in +def loopU (i : Nat) (v : Array Expr) (k : Array Expr → M α) : M α := do + if _h : i < u.size then + let ui := u[i] + let viTy ← loopUArgs ui fun uiTy xs => do + let (itIdx, itIndices) := getIIndices stats uiTy + return (← getLCtx).mkForall xs <| + .app (mkAppN recInfos[itIdx]!.motive itIndices) (mkAppN ui xs) + let vName := ((← getLCtx).get! ui.fvarId!).userName.appendAfter "_ih" + withLocalDecl vName .default (consumeTypeAnnotations viTy) fun vi => do + loopU (i + 1) (v.push vi) k + else + k v +termination_by u.size - i + +variable (stats : InductiveStats) (indTypeName : Name) (dIdx : Nat) in +def loopCtors (recInfos : Array RecInfo) + (ctors : List Constructor) (k : Array RecInfo → M α) : M α := match ctors with + | ctor::ctors => + loopCtorArgs stats ctor.type fun t bu u => do + let (itIdx, itIndices) := getIIndices stats t + let introApp := mkAppN (mkAppN (.const ctor.name stats.levels) stats.params) bu + let motiveApp := Expr.app (mkAppN recInfos[itIdx]!.motive itIndices) introApp + loopU stats u recInfos 0 #[] fun v => do + let lctx ← getLCtx + let minorTy := lctx.mkForall bu <| lctx.mkForall v motiveApp + let minorName := ctor.name.replacePrefix indTypeName .anonymous + withLocalDecl minorName .default (consumeTypeAnnotations minorTy) fun minor => do + let recInfos := recInfos.modify dIdx fun s => { s with minors := s.minors.push minor } + loopCtors recInfos ctors k + | [] => k recInfos + +variable (stats : InductiveStats) (indTypes : Array InductiveType) in +def loopInd2 (dIdx : Nat) (recInfos : Array RecInfo) (k : Array RecInfo → M α) : M α := do + if _h : dIdx < indTypes.size then + let indType := indTypes[dIdx] + let indTypeName := indType.name + loopCtors stats indTypeName dIdx recInfos indType.ctors fun recInfos => + loopInd2 (dIdx + 1) recInfos k + else + k recInfos +termination_by indTypes.size - dIdx + +end mkRecInfos + +def mkRecInfos (stats : InductiveStats) (indTypes : Array InductiveType) + (elimLevel : Level) (k : Array RecInfo → M α) : M α := + mkRecInfos.loopInd1 stats indTypes elimLevel 0 #[] fun recInfos => + mkRecInfos.loopInd2 stats indTypes 0 recInfos k + +def getRecLevels (elimLevel : Level) (levels : List Level) : List Level := + if elimLevel.isParam then elimLevel :: levels else levels + +def getRecLevelParams (elimLevel : Level) (lparams : List Name) : List Name := + if let .param u := elimLevel then u :: lparams else lparams + +def mkRecRules (indTypes : Array InductiveType) (elimLevel : Level) (stats : InductiveStats) + (dIdx : Nat) (motives : Array Expr) (minors : Array Expr) : + StateT Nat M (List RecursorRule) := do + let d := indTypes[dIdx]! + let lvls := getRecLevels elimLevel stats.levels + let mut rules := #[] + for ctor in d.ctors do + let rule ← fun minorIdx => mkRecInfos.loopCtorArgs stats ctor.type fun _ bu u => + let rec loopU i (v : Array Expr) k := do + if _h : i < u.size then + let ui := u[i] + let val ← mkRecInfos.loopUArgs ui fun uiTy xs => do + let (itIdx, itIndices) := getIIndices stats uiTy + let val := .const (mkRecName indTypes[itIdx]!.name) lvls + let val := mkAppN (mkAppN (mkAppN (mkAppN val stats.params) motives) minors) itIndices + return (← getLCtx).mkLambda xs <| val.app (mkAppN ui xs) + loopU (i + 1) (v.push val) k + else + k v + termination_by u.size - i + loopU 0 #[] fun v => do + let lctx ← getLCtx + let rule := { + ctor := ctor.name + nfields := bu.size + rhs := lctx.mkLambda stats.params <| lctx.mkLambda motives <| + lctx.mkLambda minors <| lctx.mkLambda bu <| + mkAppN (mkAppN minors[minorIdx]! bu) v + } + return (rule, minorIdx + 1) + rules := rules.push rule + return rules.toList + +/-- Defensively type-checks the generated recursors. + +`run` installs a recursor and its computation rules without re-checking them. This verifies that +(1) each recursor's type is well typed, and (2) each computation rule is type-preserving: reducing +the recursor applied to a constructor yields a term whose type is the recursor's declared result +type. This catches a recursor whose minor-premise type and reduction rule disagree; checking only +that a rule's right-hand side has *some* type is insufficient, because an under-applied minor +premise is still a well-typed (function) term. -/ +def checkRecursors (indTypes : Array InductiveType) (elimLevel : Level) + (stats : InductiveStats) (motives minors : Array Expr) : M Unit := do + let {lparams, ..} ← read + let lvls := getRecLevels elimLevel stats.levels + withLParams (getRecLevelParams elimLevel lparams) do + for h : dIdx in [:indTypes.size] do + let indType := indTypes[dIdx] + let recName := mkRecName indType.name + let recCi ← (← read).env.get recName + -- (1) The recursor type must be well typed. + _ ← (TypeChecker.checkType recCi.type : TypeChecker.M Expr) + let recPre := mkAppN (mkAppN (mkAppN (.const recName lvls) stats.params) motives) minors + -- (2) Each computation rule must preserve types. + for ctor in indType.ctors do + mkRecInfos.loopCtorArgs stats ctor.type fun t bu _ => do + let (_, itIndices) := getIIndices stats t + let introApp := mkAppN (mkAppN (.const ctor.name stats.levels) stats.params) bu + let lhs := (mkAppN recPre itIndices).app introApp + let expected ← inferType lhs + let reduct ← whnf lhs + let actual ← inferType reduct + unless ← isDefEq actual expected do + throw <| .other s!"generated recursor computation rule for '{ctor.name + }' is not type-preserving" + +def run (nparams : Nat) (types : List InductiveType) (numNested : Nat) : M Environment := do + let isUnsafe := (← read).safety != .safe + let indTypes := types.toArray + let {lparams, ..} ← read + Environment.checkDuplicatedUnivParams lparams + checkInductiveTypes nparams indTypes fun stats => do + withEnv (← declareInductiveTypes stats nparams indTypes numNested isUnsafe) do + checkConstructors indTypes stats isUnsafe + withEnv (← declareConstructors stats indTypes isUnsafe) do + let elimLevel ← getElimLevel stats indTypes + mkRecInfos stats indTypes elimLevel fun recInfos => do + let motives := recInfos.map (·.motive) + let minors := recInfos.flatMap (·.minors) + let numMinors := minors.size + let numMotives := motives.size + let all := indTypes.map (·.name) |>.toList + let lctx ← getLCtx + let k ← isKTarget stats indTypes + let isUnsafe := (← read).safety != .safe + StateT.run' (s := 0) do + let mut env ← getEnv + let {allowPrimitive, ..} ← read + for h : dIdx in [:indTypes.size] do + let indType := indTypes[dIdx] + let info := recInfos[dIdx]! + let ty := + lctx.mkForall stats.params <| + lctx.mkForall motives <| + lctx.mkForall minors <| + lctx.mkForall info.indices <| + lctx.mkForall #[info.major] <| + .app (mkAppN info.motive info.indices) info.major + let rules ← mkRecRules indTypes elimLevel stats dIdx motives minors + let name := mkRecName indType.name + env.checkName name allowPrimitive + env := env.add <| .recInfo { + levelParams := getRecLevelParams elimLevel lparams + type := ty.inferImplicit 1000 false -- note: flag has reversed polarity from C++ + numParams := stats.params.size + numIndices := stats.nindices[dIdx]! + name, all, numMotives, numMinors, rules, k, isUnsafe + } + withEnv env <| checkRecursors indTypes elimLevel stats motives minors + pure env + +end AddInductive + +namespace ElimNestedInductive + +structure Result where + ngen : NameGenerator + nparams : Nat + lctx : LocalContext + params : Array Expr -- the fvars declared in `lctx` + aux2nested : NameMap Expr -- exprs are open over `params`, like the C++ `m_aux2nested` + types : List InductiveType + +instance [MonadStateOf NameGenerator m] : MonadNameGenerator m where + getNGen := get + setNGen := set + +namespace Result + +def getNestedIfAuxCtor (r : Result) (env' : Environment) (c : Name) : Option (Expr × Name) := do + let .ctorInfo { induct, .. } ← env'.find? c | none + return (← r.aux2nested.find? induct, induct) + +def restoreCtorName (r : Result) (env' : Environment) (c : Name) : Name := Id.run do + let (e, name) := (r.getNestedIfAuxCtor env' c).get! + let .const I _ := e.getAppFn | unreachable! + c.replacePrefix name I + +def restoreNested (r : Result) (env' : Environment) (e : Expr) + (auxRec : NameMap Name := {}) : Expr := + Id.run <| StateT.run' (s := { namePrefix := `_nested_fresh : NameGenerator }) do + let pi := e.isForall + let mut e := e + let mut As := #[] + let mut lctx : LocalContext := {} + for _ in [:r.nparams] do + match e with + | .forallE name dom body bi | .lam name dom body bi => + let id := ⟨← mkFreshId⟩ + lctx := lctx.mkLocalDecl id name dom bi + let arg := .fvar id + e := body.instantiate1 arg + As := As.push arg + | _ => unreachable! + e := e.replace fun t => do + if let .const c ls := t then + if let some recName := auxRec.find? c then + return .const recName ls + let .const c _ := t.getAppFn | none + if let some nested := r.aux2nested.find? c then + let args := t.getAppArgs + assert! args.size ≥ r.nparams + return mkAppRange ((nested.abstract r.params).instantiateRev As) r.nparams args.size args + let (nested, auxI_name) ← r.getNestedIfAuxCtor env' c + let args := t.getAppArgs + assert! args.size ≥ r.nparams + let nested' := (nested.abstract r.params).instantiateRev As + nested'.withApp fun I I_args => do + let .const I_c I_ls := I | unreachable! + let c' := .const (c.replacePrefix auxI_name I_c) I_ls + return mkAppRange (mkAppN c' I_args) r.nparams args.size args + return if pi then lctx.mkForall As e else lctx.mkLambda As e + +end Result + +structure State where + ngen : NameGenerator := { namePrefix := `_nested_fresh } + nestedAux : Array (Expr × Name) := {} + lvls : List Level + newTypes : Array InductiveType + nextIdx : Nat := 1 + deriving Inhabited + +abbrev M := ReaderT Environment <| StateT State <| Except Exception + +instance : MonadNameGenerator M where + getNGen := return (← get).ngen + setNGen ngen := modify fun s => { s with ngen } + +-- TODO: remove partial +partial def mkUniqueName (n : Name) : M Name := fun env s => + let rec loop i := + let r := n.appendIndexAfter i + if env.contains r then + loop (i + 1) + else + pure (r, { s with nextIdx := i + 1 }) + loop s.nextIdx + +def illFormed : Exception := + .other "invalid nested inductive datatype, ill-formed declaration" + +def replaceParams (params : Array Expr) (e : Expr) (As : Array Expr) : M Expr := do + assert! As.size == params.size + return (e.abstract As).instantiateRev params + +/-- IF `e` is of the form `I Ds is` where + 1) `I` is a nested inductive datatype (i.e., a previously declared inductive datatype), + 2) the parametric arguments `Ds` do not contain loose bound variables, and do contain inductive datatypes in `m_new_types` +THEN return the `inductive_val` in the `constant_info` associated with `I`. +Otherwise, return none. -/ +def isNestedInductiveApp? (e : Expr) : M (Option InductiveVal) := do + if !e.isApp then return none + let .const fn _ := e.getAppFn | return none + let env ← read + let some (.inductInfo ci) := env.find? fn | return none + let args := e.getAppArgs + if ci.numParams > args.size then return none + let mut isNested := false + let mut looseBVars := false + for i in [0:ci.numParams] do + if args[i]!.hasLooseBVars then + looseBVars := true + let newTypes := (← get).newTypes + if let some _ := args[i]!.find? fun + | .const t _ => newTypes.any fun ty => t == ty.name + | _ => false + then + isNested := true + if !isNested then return none + if looseBVars then + throw <| .other s!"invalid nested inductive datatype '{fn}', \ + nested inductive datatypes parameters cannot contain local variables." + return some ci + +def instantiateForallParams (e : Expr) (hi : Nat) (params : Array Expr) : + Except Exception Expr := do + let mut e := e + for _ in [:hi] do + let .forallE _ _ body _ := e | throw illFormed + e := body + return e.instantiateRevRange 0 hi params + +/-- If `e` is a nested occurrence `I Ds is`, return `Iaux As is` -/ +def replaceIfNested (lctx : LocalContext) (params : Array Expr) (As : Array Expr) (e : Expr) : + M (Option Expr) := do + let some I_val ← isNestedInductiveApp? e | return none + e.withApp fun fn args => do + let .const I_name I_lvls := fn | unreachable! + let I_nparams := I_val.numParams + assert! I_nparams ≤ args.size + let IAs := mkAppRange fn 0 I_nparams args -- `I As` + let Iparams ← replaceParams params IAs As + let st ← get + if let some auxI_name := st.nestedAux.findSome? fun (e, n) => + if e == Iparams then some n else none + then + return mkAppRange (mkAppN (.const auxI_name st.lvls) As) I_nparams args.size args + let mut result := none + let env ← read + for J_name in I_val.all do + let .inductInfo J_info ← env.get J_name | unreachable! + let J := .const J_name I_lvls + let JAs := mkAppRange J 0 I_nparams args + let auxJ_name ← mkUniqueName (`_nested ++ J_name) + let auxJ_type := J_info.type.instantiateLevelParams J_info.levelParams I_lvls + let auxJ_type := lctx.mkForall As <| ← instantiateForallParams auxJ_type I_nparams args + let JAs' ← replaceParams params JAs As + modify fun st => { st with nestedAux := st.nestedAux.push (JAs', auxJ_name) } + if J_name == I_name then + result := some <| + mkAppRange (mkAppN (.const auxJ_name (← get).lvls) As) I_nparams args.size args + let auxJ_ctors ← J_info.ctors.mapM fun J_ctor_name => do + let J_ctor_info ← env.get J_ctor_name + -- auxJ_cnstr_type still has references to `J`, this will be fixed later when we process it. + let auxJ_ctor_name := J_ctor_name.replacePrefix J_name auxJ_name + let auxJ_ctor_type := J_ctor_info.type.instantiateLevelParams J_ctor_info.levelParams I_lvls + let auxJ_ctor_type ← instantiateForallParams auxJ_ctor_type I_nparams args + return { name := auxJ_ctor_name, type := lctx.mkForall As auxJ_ctor_type } + let newType := { name := auxJ_name, type := auxJ_type, ctors := auxJ_ctors } + modify fun st => { st with newTypes := st.newTypes.push newType } + assert! result.isSome + return result + +def replaceAllNested (lctx : LocalContext) (params : Array Expr) (As : Array Expr) (e : Expr) : + M Expr := e.replaceM (replaceIfNested lctx params As) + +def withParams (type : Expr) (nparams : Nat) + (k : LocalContext → Expr → Array Expr → M α) : M α := loop {} type #[] nparams where + loop lctx type params + | 0 => k lctx type params + | i+1 => do + let .forallE name dom body bi := type + | throw <| .other "invalid inductive datatype declaration, incorrect number of parameters" + let id := ⟨← mkFreshId⟩ + let lctx := lctx.mkLocalDecl id name dom bi + let arg := .fvar id + loop lctx (body.instantiate1 arg) (params.push arg) i + +def run (fuel nparams : Nat) (types : List InductiveType) : M Result := do + let I :: _ := types + | throw <| .other s!"invalid empty (mutual) inductive datatype declaration, \ + it must contain at least one inductive type." + withParams I.type nparams fun lctx _ params => do + let rec loop i + | 0 => throw <| .other "deep recursion: ElimNestedInductive.run.loop" + | fuel+1 => do + let s ← get + if _h : i < s.newTypes.size then + let indType := s.newTypes[i] + let ctors ← indType.ctors.mapM fun ctor => do + withParams ctor.type nparams fun lctx ctorType As => do + assert! As.size == nparams + return { ctor with type := lctx.mkForall As (← replaceAllNested lctx params As ctorType) } + modify fun s => { s with newTypes := s.newTypes.set! i { indType with ctors } } + loop (i+1) fuel + else + let aux2nested := s.nestedAux.foldl (fun m (e, n) => m.insert n e) {} + return { + ngen := s.ngen + nparams := params.size + lctx := lctx + params := params + aux2nested := aux2nested + types := s.newTypes.toList } + loop 0 fuel +end ElimNestedInductive + +def mkAuxRecNameMap (env' : Environment) (types : List InductiveType) : + List Name × NameMap Name := Id.run do + let mainType :: _ := types | unreachable! + let ntypes := types.length + let mainName := mainType.name + let some (.inductInfo mainInfo) := env'.find? mainName | unreachable! + let allNames := mainInfo.all + assert! allNames.length > ntypes + let mut oldRecNames := #[] + let mut recMap : NameMap Name := {} + let mut nextIdx := 1 + for indName in allNames.drop ntypes do + let oldRecName := mkRecName indName + let newRecName := (mkRecName mainName).appendIndexAfter nextIdx + nextIdx := nextIdx + 1 + recMap := recMap.insert oldRecName newRecName + oldRecNames := oldRecNames.push oldRecName + return (oldRecNames.toList, recMap) + +def checkNoNestedAux (n : Name) (e : Expr) : Except Exception Unit := do + if (e.find? fun + | .const c _ => (`_nested).isPrefixOf c + | .proj s _ _ => (`_nested).isPrefixOf s + | _ => false).isSome then + throw <| .other s!"invalid declaration '{n}', it uses the reserved prefix '_nested'" + +/-- Checks the occurrence of a datatype being declared at the head of `e`, if there is one. +Returns `true` when the occurrence was checked and `e`'s subterms need not be revisited. -/ +def checkUniformIndOcc (lvls : List Level) (indNames : List Name) (nparams : Nat) + (e : Expr) (offset : Nat) : Except Exception Bool := do + let .const c ls := e.getAppFn | return false + unless indNames.contains c do return false + let args := e.getAppArgs + -- Over-applied: descend, so that occurrences in the indices are checked too. The parameter + -- application itself is visited as a subterm of `e` and checked then. + if args.size > nparams then return false + let ok := args.size == nparams && offset ≥ nparams && ls == lvls + && (List.range nparams).all fun i => args[i]! == .bvar (offset - 1 - i) + unless ok do + throw <| .other s!"invalid occurrence of datatype '{c}' being declared: it must be applied \ + to the parameters and universe levels of the mutual declaration" + return true + +/-- Checks that every occurrence of a datatype being declared in `e` is applied to the +declaration's universe levels and to its parameters, which at binder depth `offset` are the bound +variables `#(offset-1) … #(offset-nparams)`. That those binders really are the parameters is +established later, by the parameter check in `checkConstructors`. -/ +def checkUniformIndOccsIn (lvls : List Level) (indNames : List Name) (nparams : Nat) : + Expr → Nat → Except Exception Unit + | e, offset => do + if ← checkUniformIndOcc lvls indNames nparams e offset then return + match e with + | .forallE _ d b _ | .lam _ d b _ => + checkUniformIndOccsIn lvls indNames nparams d offset + checkUniformIndOccsIn lvls indNames nparams b (offset + 1) + | .letE _ t v b _ => + checkUniformIndOccsIn lvls indNames nparams t offset + checkUniformIndOccsIn lvls indNames nparams v offset + checkUniformIndOccsIn lvls indNames nparams b (offset + 1) + | .app f a => + checkUniformIndOccsIn lvls indNames nparams f offset + checkUniformIndOccsIn lvls indNames nparams a offset + | .mdata _ b => checkUniformIndOccsIn lvls indNames nparams b offset + | .proj _ _ b => checkUniformIndOccsIn lvls indNames nparams b offset + | _ => pure () + +/-- Runs `checkUniformIndOccsIn` over every constructor type of the declaration. + +Later phases inspect the constructor types modulo `whnf`, which can erase an occurrence (as in +`(fun _ => Unit) (T Nat)`), and the parametric arguments of a nested occurrence are dropped from +the auxiliary declaration altogether, so a non-uniform occurrence could escape checking there. +Reduction never creates an occurrence of a datatype being declared, since those are not yet in the +environment, so checking the syntactic occurrences here covers all of them. -/ +def checkUniformIndOccs (lparams : List Name) (nparams : Nat) (types : List InductiveType) : + Except Exception Unit := do + let lvls := lparams.map Level.param + let indNames := types.map (·.name) + for indType in types do + for ctor in indType.ctors do + checkUniformIndOccsIn lvls indNames nparams ctor.type 0 + +def Environment.addInductive (env : Environment) (lparams : List Name) (nparams : Nat) + (types : List InductiveType) (isUnsafe allowPrimitive : Bool) (fuel : FuelConfig := {}) : + Except Exception Environment := do + for indType in types do + env.checkNoMVarNoFVar indType.name indType.type + for ctor in indType.ctors do + env.checkNoMVarNoFVar ctor.name ctor.type + checkNoNestedAux ctor.name ctor.type + checkUniformIndOccs lparams nparams types + let res ← ElimNestedInductive.run fuel.inductiveFuel nparams types env + |>.run' { lvls := lparams.map .param, newTypes := types.toArray } + let numNested := res.aux2nested.size + let safety := if isUnsafe then .unsafe else .safe + let env' ← AddInductive.run nparams res.types numNested + { env, allowPrimitive, lparams, fuel, safety } + if numNested = 0 then return env' + let allIndNames := types.map (·.name) + let (recNames', recNameMap') := mkAuxRecNameMap env' types + (·.2) <$> StateT.run (s := env) do + let processRec recName := do + let newRecName := recNameMap'.getD recName recName + let some (.recInfo recInfo) := env'.find? recName | unreachable! + let newRecType := res.restoreNested env' recInfo.type recNameMap' + let newRules ← recInfo.rules.mapM fun rule => do + let newRhs := res.restoreNested env' rule.rhs recNameMap' + let newCtorName := if newRecName == recName then rule.ctor else + res.restoreCtorName env' rule.ctor + return { rule with ctor := newCtorName, rhs := newRhs } + (← MonadState.get).checkName newRecName allowPrimitive + modify (·.add <| .recInfo { recInfo with + name := newRecName, type := newRecType, all := allIndNames, rules := newRules }) + for indType in types do + let some (.inductInfo ind) := env'.find? indType.name | unreachable! + (← get).checkName ind.name allowPrimitive + modify (·.add <| .inductInfo { ind with all := allIndNames }) + for ctorName in ind.ctors do + let some (.ctorInfo ctor) := env'.find? ctorName | unreachable! + let newType := res.restoreNested env' ctor.type + (← get).checkName ctor.name allowPrimitive + modify (·.add <| .ctorInfo { ctor with type := newType }) + processRec (mkRecName indType.name) + recNames'.forM processRec + TypeChecker.M.run (← get) (safety := safety) (lctx := res.lctx) + (lparams := lparams) (fuel := fuel) do + res.aux2nested.forM fun _ e => do _ ← TypeChecker.checkType e diff --git a/Ix/Theory/Named/Reference/Inductive/EliminationTrace.lean b/Ix/Theory/Named/Reference/Inductive/EliminationTrace.lean new file mode 100644 index 000000000..3295fdb18 --- /dev/null +++ b/Ix/Theory/Named/Reference/Inductive/EliminationTrace.lean @@ -0,0 +1,581 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Reference.Inductive.ValidationTrace + +namespace Ix.Theory.Named +open Lean hiding Environment Exception +open Kernel + +namespace AddInductive +open TypeChecker + +/-- Exact successful traversal of the singleton-constructor branch of +`isLargeEliminator`. Non-parameter fields retain the precise `ensureType` +observation used to decide whether their local is relevant to the terminal +index-occurrence check. -/ +inductive LargeEliminatorLoopTrace (stats : InductiveStats) : + (context : Context) → (source : Expr) → (argIdx : Nat) → + (toCheck : Array Expr) → (fuel : Nat) → (result : Bool) → Type where + | parameter + (context : Context) (fuel argIdx : Nat) (toCheck : Array Expr) + (name : Name) (domain body : Expr) (binderInfo : BinderInfo) + (isParameter : argIdx < stats.params.size) + (tail : LargeEliminatorLoopTrace stats + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) (argIdx + 1) toCheck fuel result) : + LargeEliminatorLoopTrace stats context + (.forallE name domain body binderInfo) argIdx toCheck (fuel + 1) result + | proofField + (context : Context) (fuel argIdx : Nat) (toCheck : Array Expr) + (name : Name) (domain body : Expr) (binderInfo : BinderInfo) + (sortResult : Expr) + (isField : argIdx ≥ stats.params.size) + (ensureType : ConstructorEnsureTypeStep.Valid + ⟨context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain), domain, sortResult⟩) + (isProp : sortResult.sortLevel!.isAlwaysZero = true) + (tail : LargeEliminatorLoopTrace stats + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) (argIdx + 1) toCheck fuel result) : + LargeEliminatorLoopTrace stats context + (.forallE name domain body binderInfo) argIdx toCheck (fuel + 1) result + | dataField + (context : Context) (fuel argIdx : Nat) (toCheck : Array Expr) + (name : Name) (domain body : Expr) (binderInfo : BinderInfo) + (sortResult : Expr) + (isField : argIdx ≥ stats.params.size) + (ensureType : ConstructorEnsureTypeStep.Valid + ⟨context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain), domain, sortResult⟩) + (isProp : sortResult.sortLevel!.isAlwaysZero = false) + (tail : LargeEliminatorLoopTrace stats + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) (argIdx + 1) + (toCheck.push context.freshExpr) fuel result) : + LargeEliminatorLoopTrace stats context + (.forallE name domain body binderInfo) argIdx toCheck (fuel + 1) result + | terminal + (context : Context) (source : Expr) (fuel argIdx : Nat) + (toCheck : Array Expr) (notForall : source.isForall = false) : + LargeEliminatorLoopTrace stats context source argIdx toCheck (fuel + 1) + (toCheck.all source.getAppArgs.contains) + +namespace LargeEliminatorLoopTrace + +def parameterCount + (trace : LargeEliminatorLoopTrace stats context source argIdx toCheck + fuel result) : Nat := + match trace with + | .parameter (tail := tail) .. => tail.parameterCount + 1 + | .proofField (tail := tail) .. => tail.parameterCount + | .dataField (tail := tail) .. => tail.parameterCount + | .terminal .. => 0 + +def proofFieldCount + (trace : LargeEliminatorLoopTrace stats context source argIdx toCheck + fuel result) : Nat := + match trace with + | .parameter (tail := tail) .. => tail.proofFieldCount + | .proofField (tail := tail) .. => tail.proofFieldCount + 1 + | .dataField (tail := tail) .. => tail.proofFieldCount + | .terminal .. => 0 + +def dataFieldCount + (trace : LargeEliminatorLoopTrace stats context source argIdx toCheck + fuel result) : Nat := + match trace with + | .parameter (tail := tail) .. => tail.dataFieldCount + | .proofField (tail := tail) .. => tail.dataFieldCount + | .dataField (tail := tail) .. => tail.dataFieldCount + 1 + | .terminal .. => 0 + +/-- Erasing the retained singleton trace replays the exact ordinary checker +loop, including every `ensureType` call and local declaration. -/ +theorem run + (trace : LargeEliminatorLoopTrace stats context source argIdx toCheck + fuel result) : + isLargeEliminator.loop stats source argIdx toCheck fuel context = + .ok result := by + induction trace with + | parameter context fuel argIdx toCheck name domain body binderInfo + isParameter tail ih => + rw [show fuel + 1 = Nat.succ fuel by omega] + rw [isLargeEliminator.loop.eq_2, withLocalDecl_apply] + have notField : ¬ argIdx ≥ stats.params.size := + Nat.not_le.mpr isParameter + simp only [notField, if_false, Bind.bind] + exact ih + | proofField context fuel argIdx toCheck name domain body binderInfo + sortResult isField ensureStep isProp tail ih => + rw [show fuel + 1 = Nat.succ fuel by omega] + rw [isLargeEliminator.loop.eq_2, withLocalDecl_apply] + simp only [isField, if_true, ReaderT.bind, Bind.bind, + liftTypeChecker_apply] + rw [ensureStep] + simp only [Except.bind, isProp, Bool.not_true, Bool.false_eq_true, if_false] + exact ih + | dataField context fuel argIdx toCheck name domain body binderInfo + sortResult isField ensureStep isProp tail ih => + rw [show fuel + 1 = Nat.succ fuel by omega] + rw [isLargeEliminator.loop.eq_2, withLocalDecl_apply] + simp only [isField, if_true, ReaderT.bind, Bind.bind, + liftTypeChecker_apply] + rw [ensureStep] + simp only [Except.bind, isProp, Bool.not_false, if_true] + exact ih + | terminal context source fuel argIdx toCheck notForall => + cases source <;> + simp_all [isLargeEliminator.loop, ReaderT.pure, Pure.pure, + Except.pure, Expr.isForall] + +/-- Execute the singleton branch once while retaining the exact branch and +checker observations that produced its Boolean result. -/ +def buildExecution (stats : InductiveStats) (context : Context) + (source : Expr) (argIdx : Nat) (toCheck : Array Expr) : + (fuel : Nat) → Except Exception + (Sigma fun result => LargeEliminatorLoopTrace stats context source + argIdx toCheck fuel result) + | 0 => .error .deepRecursion + | fuel + 1 => + match hforall : source.isForall with + | false => .ok ⟨toCheck.all source.getAppArgs.contains, + .terminal context source fuel argIdx toCheck hforall⟩ + | true => + match source with + | .forallE name domain body binderInfo => + let nextContext := context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain) + let nextSource := body.instantiate1 context.freshExpr + if isParameter : argIdx < stats.params.size then + match buildExecution stats nextContext nextSource (argIdx + 1) + toCheck fuel with + | .error error => .error error + | .ok ⟨result, tail⟩ => .ok ⟨result, + .parameter context fuel argIdx toCheck name domain body + binderInfo isParameter tail⟩ + else + match hensure : TypeChecker.M.run nextContext.env nextContext.safety + nextContext.lctx nextContext.lparams nextContext.fuel + (TypeChecker.ensureType domain) with + | .error error => .error error + | .ok sortResult => + if isProp : sortResult.sortLevel!.isAlwaysZero then + match buildExecution stats nextContext nextSource + (argIdx + 1) toCheck fuel with + | .error error => .error error + | .ok ⟨result, tail⟩ => .ok ⟨result, + .proofField context fuel argIdx toCheck name domain body + binderInfo sortResult (Nat.le_of_not_gt isParameter) + hensure isProp tail⟩ + else + match buildExecution stats nextContext nextSource + (argIdx + 1) (toCheck.push context.freshExpr) fuel with + | .error error => .error error + | .ok ⟨result, tail⟩ => .ok ⟨result, + .dataField context fuel argIdx toCheck name domain body + binderInfo sortResult (Nat.le_of_not_gt isParameter) + hensure (by + cases h : sortResult.sortLevel!.isAlwaysZero <;> simp_all) + tail⟩ + | _ => .error <| .other + "large-eliminator source shape disagrees with isForall" + +end LargeEliminatorLoopTrace + +/-- The source identity and exact loop retained when the singleton branch of +`isLargeEliminator` is selected. -/ +structure LargeEliminatorSingletonExecution + (stats : InductiveStats) (indTypes : Array InductiveType) + (context : Context) (result : Bool) where + indType : InductiveType + indTypes_eq : indTypes = #[indType] + ctor : Constructor + ctors_eq : indType.ctors = [ctor] + trace : LargeEliminatorLoopTrace stats context ctor.type 0 #[] + context.fuel.inductiveFuel result + +/-- One exact successful execution of `isLargeEliminator`. The ordinary +equation is always retained; the singleton payload additionally exposes all +field-sort observations made by the executable. -/ +structure LargeEliminatorExecution + (stats : InductiveStats) (indTypes : Array InductiveType) + (context : Context) where + result : Bool + singleton : Option + (LargeEliminatorSingletonExecution stats indTypes context result) + run_eq : isLargeEliminator stats indTypes context = .ok result + +namespace LargeEliminatorExecution + +/-- Execute the ordinary decision and retain a transparent refinement of its +singleton traversal. -/ +def buildExecution (stats : InductiveStats) (indTypes : Array InductiveType) + (context : Context) : + Except Exception (LargeEliminatorExecution stats indTypes context) := + match hrun : isLargeEliminator stats indTypes context with + | .error error => .error error + | .ok result => + if stats.isNotZero then + .ok { result, singleton := none, run_eq := hrun } + else + match htypes : indTypes with + | #[indType] => + match hctors : indType.ctors with + | [ctor] => + match LargeEliminatorLoopTrace.buildExecution stats context + ctor.type 0 #[] context.fuel.inductiveFuel with + | .error error => .error error + | .ok ⟨traceResult, trace⟩ => + if hsame : traceResult = result then + .ok { + result + singleton := some { + indType + indTypes_eq := rfl + ctor + ctors_eq := hctors + trace := hsame ▸ trace } + run_eq := by simpa only [htypes] using hrun } + else + .error <| .other + "large-eliminator trace disagrees with ordinary result" + | _ => .ok { + result + singleton := none + run_eq := by simpa only [htypes] using hrun } + | _ => .ok { + result + singleton := none + run_eq := by simpa only [htypes] using hrun } + +end LargeEliminatorExecution + +/-- Exact `getElimLevel` execution paired with the retained large-elimination +decision that controls it. `level_eq` exposes both the zero and fresh-parameter +branches without unfolding the monadic checker again. -/ +structure ElimLevelExecution + (stats : InductiveStats) (indTypes : Array InductiveType) + (context : Context) where + large : LargeEliminatorExecution stats indTypes context + level : Level + level_eq : level = if large.result then + .param (getFreshElimParam context.lparams) else .zero + run_eq : getElimLevel stats indTypes context = .ok level + +namespace ElimLevelExecution + +/-- Recompose `getElimLevel` from the retained exact large-elimination run. -/ +theorem run_of_large + (large : LargeEliminatorExecution stats indTypes context) : + getElimLevel stats indTypes context = .ok + (if large.result then .param (getFreshElimParam context.lparams) + else .zero) := by + unfold getElimLevel + simp only [ReaderT.bind, Bind.bind] + rw [large.run_eq] + cases large.result <;> rfl + +def buildExecution (stats : InductiveStats) (indTypes : Array InductiveType) + (context : Context) : + Except Exception (ElimLevelExecution stats indTypes context) := do + let large ← LargeEliminatorExecution.buildExecution stats indTypes context + let level := if large.result then + .param (getFreshElimParam context.lparams) else .zero + pure { + large + level + level_eq := rfl + run_eq := run_of_large large } + +theorem level_eq_zero (execution : ElimLevelExecution stats indTypes context) + (small : execution.large.result = false) : + execution.level = .zero := by + rw [execution.level_eq, small] + rfl + +theorem level_eq_param + (execution : ElimLevelExecution stats indTypes context) + (large : execution.large.result = true) : + execution.level = .param (getFreshElimParam context.lparams) := by + rw [execution.level_eq, large] + rfl + +/-- A small eliminator preserves the source universe-level order in recursor +applications. -/ +theorem recLevels_eq_small + (execution : ElimLevelExecution stats indTypes context) + (small : execution.large.result = false) (levels : List Level) : + getRecLevels execution.level levels = levels := by + rw [execution.level_eq_zero small] + rfl + +/-- A large eliminator prepends its fresh elimination level to every source +universe used by recursive calls. -/ +theorem recLevels_eq_large + (execution : ElimLevelExecution stats indTypes context) + (large : execution.large.result = true) (levels : List Level) : + getRecLevels execution.level levels = + .param (getFreshElimParam context.lparams) :: levels := by + rw [execution.level_eq_param large] + rfl + +/-- A small eliminator preserves the stored source level-parameter order. -/ +theorem recLevelParams_eq_small + (execution : ElimLevelExecution stats indTypes context) + (small : execution.large.result = false) (lparams : List Name) : + getRecLevelParams execution.level lparams = lparams := by + rw [execution.level_eq_zero small] + rfl + +/-- A large eliminator stores the fresh elimination parameter before every +source level parameter. -/ +theorem recLevelParams_eq_large + (execution : ElimLevelExecution stats indTypes context) + (large : execution.large.result = true) (lparams : List Name) : + getRecLevelParams execution.level lparams = + getFreshElimParam context.lparams :: lparams := by + rw [execution.level_eq_param large] + rfl + +end ElimLevelExecution + +/-- Exact traversal of the constructor-shape fragment of `isKTarget`. The +trace stops at the first visible non-parameter binder, just as the executable +does; it never treats K eligibility as evidence for large elimination. -/ +inductive KTargetCtorTrace (nparams : Nat) : + (source : Expr) → (argIdx : Nat) → (result : Bool) → Type where + | parameter + (argIdx : Nat) (name : Name) (domain body : Expr) + (binderInfo : BinderInfo) + (isParameter : argIdx < nparams) + (tail : KTargetCtorTrace nparams body (argIdx + 1) result) : + KTargetCtorTrace nparams (.forallE name domain body binderInfo) + argIdx result + | field + (argIdx : Nat) (name : Name) (domain body : Expr) + (binderInfo : BinderInfo) + (isField : argIdx ≥ nparams) : + KTargetCtorTrace nparams (.forallE name domain body binderInfo) + argIdx false + | terminal + (source : Expr) (argIdx : Nat) + (notForall : source.isForall = false) : + KTargetCtorTrace nparams source argIdx true + +namespace KTargetCtorTrace + +def parameterCount + (trace : KTargetCtorTrace nparams source argIdx result) : Nat := + match trace with + | .parameter (tail := tail) .. => tail.parameterCount + 1 + | .field .. => 0 + | .terminal .. => 0 + +/-- The K-target walk stops at the first visible constructor field, so this +count is either zero or one. -/ +def fieldCount + (trace : KTargetCtorTrace nparams source argIdx result) : Nat := + match trace with + | .parameter (tail := tail) .. => tail.fieldCount + | .field .. => 1 + | .terminal .. => 0 + +/-- Erasing the retained constructor trace yields the exact Boolean consumed +by `isKTarget`. -/ +theorem run + (trace : KTargetCtorTrace nparams source argIdx result) : + isKTargetCtor nparams argIdx source = result := by + induction trace with + | parameter argIdx name domain body binderInfo isParameter tail ih => + simp [isKTargetCtor, isParameter, ih] + | field argIdx name domain body binderInfo isField => + have notParameter : ¬ argIdx < nparams := Nat.not_lt.mpr isField + simp [isKTargetCtor, notParameter] + | terminal source argIdx notForall => + cases source <;> simp_all [isKTargetCtor, Expr.isForall] + +/-- Compute the K-target constructor branch while retaining the exact point +where the parameter prefix ends. -/ +def buildExecution (nparams : Nat) : + (source : Expr) → (argIdx : Nat) → + Sigma fun result => KTargetCtorTrace nparams source argIdx result + | .bvar i, argIdx => ⟨true, .terminal (.bvar i) argIdx rfl⟩ + | .fvar id, argIdx => ⟨true, .terminal (.fvar id) argIdx rfl⟩ + | .mvar id, argIdx => ⟨true, .terminal (.mvar id) argIdx rfl⟩ + | .sort level, argIdx => ⟨true, .terminal (.sort level) argIdx rfl⟩ + | .const name levels, argIdx => + ⟨true, .terminal (.const name levels) argIdx rfl⟩ + | .app fn arg, argIdx => ⟨true, .terminal (.app fn arg) argIdx rfl⟩ + | .lam name domain body binderInfo, argIdx => + ⟨true, .terminal (.lam name domain body binderInfo) argIdx rfl⟩ + | .forallE name domain body binderInfo, argIdx => + if isParameter : argIdx < nparams then + let ⟨result, tail⟩ := buildExecution nparams body (argIdx + 1) + ⟨result, .parameter argIdx name domain body binderInfo + isParameter tail⟩ + else + ⟨false, .field argIdx name domain body binderInfo + (Nat.le_of_not_gt isParameter)⟩ + | .letE name type value body nondep, argIdx => + ⟨true, .terminal (.letE name type value body nondep) argIdx rfl⟩ + | .lit literal, argIdx => ⟨true, .terminal (.lit literal) argIdx rfl⟩ + | .mdata data expr, argIdx => + ⟨true, .terminal (.mdata data expr) argIdx rfl⟩ + | .proj typeName idx struct, argIdx => + ⟨true, .terminal (.proj typeName idx struct) argIdx rfl⟩ + +end KTargetCtorTrace + +/-- The singleton-Prop branch data of one exact `isKTarget` execution. -/ +structure KTargetSingletonExecution + (stats : InductiveStats) (indTypes : Array InductiveType) + (result : Bool) where + indType : InductiveType + indTypes_eq : indTypes = #[indType] + resultLevelZero : stats.resultLevel.isAlwaysZero = true + ctor : Constructor + ctors_eq : indType.ctors = [ctor] + trace : KTargetCtorTrace stats.params.size ctor.type 0 result + +/-- One exact successful execution of `isKTarget`. The ordinary monadic +equation is always retained; a singleton candidate additionally exposes the +constructor-prefix trace that decided its flag. -/ +structure KTargetExecution + (stats : InductiveStats) (indTypes : Array InductiveType) + (context : Context) where + result : Bool + singleton : Option (KTargetSingletonExecution stats indTypes result) + run_eq : isKTarget stats indTypes context = .ok result + +namespace KTargetExecution + +def buildExecution (stats : InductiveStats) + (indTypes : Array InductiveType) (context : Context) : + Except Exception (KTargetExecution stats indTypes context) := + match hrun : isKTarget stats indTypes context with + | .error error => .error error + | .ok result => + match _htypes : indTypes with + | #[indType] => + if hzero : stats.resultLevel.isAlwaysZero then + match hctors : indType.ctors with + | [ctor] => + let ⟨traceResult, trace⟩ := + KTargetCtorTrace.buildExecution stats.params.size ctor.type 0 + if hsame : traceResult = result then + .ok { + result + singleton := some { + indType + indTypes_eq := rfl + resultLevelZero := hzero + ctor + ctors_eq := hctors + trace := hsame ▸ trace } + run_eq := hrun } + else + .error <| .other + "K-target trace disagrees with ordinary result" + | _ => .ok { result, singleton := none, run_eq := hrun } + else + .ok { result, singleton := none, run_eq := hrun } + | _ => .ok { result, singleton := none, run_eq := hrun } + +end KTargetExecution + +/-- The normalization/validation execution extended through constructor +declaration and the exact elimination-level and K-target decisions used by +`run`. This is an operational refinement only: erasing the added fields leaves the existing +normalization candidate and checker equations unchanged. -/ +structure NormalizationEliminationExecution + (nparams : Nat) (types : List InductiveType) + (numNested : Nat) (isUnsafe : Bool) (candidateContext : Context) where + normalization : NormalizationCandidateExecution nparams types numNested + isUnsafe candidateContext + constructorEnv : Environment + declareConstructorsRun : + declareConstructors normalization.stats types.toArray isUnsafe + { normalization.validationContext with + env := normalization.familyEnv } = .ok constructorEnv + elimination : ElimLevelExecution normalization.stats types.toArray + { normalization.validationContext with env := constructorEnv } + kTarget : KTargetExecution normalization.stats types.toArray + { normalization.validationContext with env := constructorEnv } + +namespace NormalizationEliminationExecution + +def candidate + (execution : NormalizationEliminationExecution nparams types numNested + isUnsafe candidateContext) : NormalizationCandidate types := + execution.normalization.candidate + +/-- Execute the existing detailed candidate producer, declare the already +validated constructors, and retain both recursor decisions at precisely the +post-constructor context used by `run`. -/ +def buildExecution + (nparams : Nat) (types : List InductiveType) + (numNested : Nat) (isUnsafe : Bool) (candidateContext : Context) : + Except Exception (NormalizationEliminationExecution nparams types + numNested isUnsafe candidateContext) := do + let normalization ← buildNormalizationCandidateExecution nparams types + numNested isUnsafe candidateContext + let constructorContext : Context := + { normalization.validationContext with env := normalization.familyEnv } + match hdeclare : declareConstructors normalization.stats types.toArray + isUnsafe constructorContext with + | .error error => .error error + | .ok constructorEnv => + let eliminationContext : Context := + { normalization.validationContext with env := constructorEnv } + match ElimLevelExecution.buildExecution normalization.stats + types.toArray eliminationContext with + | .error error => .error error + | .ok elimination => + match KTargetExecution.buildExecution normalization.stats + types.toArray eliminationContext with + | .error error => .error error + | .ok kTarget => .ok { + normalization + constructorEnv + declareConstructorsRun := by + simpa [constructorContext] using hdeclare + elimination := by simpa [eliminationContext] using elimination + kTarget := by simpa [eliminationContext] using kTarget } + +/-- The level list supplied to generated recursive calls. -/ +def recLevels + (execution : NormalizationEliminationExecution nparams types numNested + isUnsafe candidateContext) : List Level := + getRecLevels execution.elimination.level execution.normalization.stats.levels + +/-- The level-parameter list stored in generated recursor metadata. -/ +def recLevelParams + (execution : NormalizationEliminationExecution nparams types numNested + isUnsafe candidateContext) : List Name := + getRecLevelParams execution.elimination.level + execution.normalization.validationContext.lparams + +end NormalizationEliminationExecution + +#guard_named_axioms Ix.Theory.Named.AddInductive.KTargetCtorTrace.run [propext] + +#guard_named_axioms Ix.Theory.Named.AddInductive.KTargetExecution.buildExecution [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.LargeEliminatorLoopTrace.run [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.LargeEliminatorExecution.buildExecution [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.ElimLevelExecution.run_of_large [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.ElimLevelExecution.recLevelParams_eq_large [propext, Classical.choice, Quot.sound] + +end AddInductive +end Ix.Theory.Named diff --git a/Ix/Theory/Named/Reference/Inductive/Reduce.lean b/Ix/Theory/Named/Reference/Inductive/Reduce.lean new file mode 100644 index 000000000..8ce22e259 --- /dev/null +++ b/Ix/Theory/Named/Reference/Inductive/Reduce.lean @@ -0,0 +1,120 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Lean.Structure +import Ix.Theory.Named.Reference.Expr +import Ix.Theory.Named.Reference.Environment.Basic + +namespace Ix.Theory.Named +open Lean hiding Environment +open Kernel + +section +variable [Monad m] (env : Environment) + (whnf : Expr → m Expr) (inferType : Expr → m Expr) (isDefEq : Expr → Expr → m Bool) + (isNeverProp : Expr → m Bool) + +def getFirstCtor (dName : Name) : Option Name := do + let some (.inductInfo info) := env.find? dName | none + info.ctors.head? + +def mkNullaryCtor (type : Expr) (nparams : Nat) : Option Expr := + type.withApp fun d args => do + let .const dName ls := d | none + let name ← getFirstCtor env dName + return mkAppRange (.const name ls) 0 nparams args + +/-- When `e` has the type of a K-like inductive, converts it into a constructor application. + +For instance if we have `e : Eq a a`, it is converted into `Eq.refl a` (which it is definitionally +equal to by proof irrelevance). Note that the indices of `e`'s type must match those of the +constructor application (for instance, `e : Eq a b` cannot be converted if `a` and `b` are not +defeq). -/ +def toCtorWhenK (rval : RecursorVal) (e : Expr) : m Expr := do + assert! rval.k + let appType ← whnf (← inferType e) + let .const appTypeI _ := appType.getAppFn | return e + if appTypeI != rval.getMajorInduct then return e + if appType.hasExprMVar then + let appTypeArgs := appType.getAppArgs + for h : i in [rval.numParams:appTypeArgs.size] do + if appTypeArgs[i].hasExprMVar then return e + let some newCtorApp := mkNullaryCtor env appType rval.numParams | return e + -- check that the indices of types of `e` and `newCtorApp` match + unless ← isDefEq appType (← inferType newCtorApp) do return e + return newCtorApp + +def expandEtaStruct (eType e : Expr) : Expr := + eType.withApp fun I args => Id.run do + let .const I ls := I | return e + let some ctor := getFirstCtor env I | return e + let some (.ctorInfo info) := env.find? ctor | unreachable! + let mut result := mkAppRange (.const ctor ls) 0 info.numParams args + for i in [:info.numFields] do + result := .app result (.proj I i e) + pure result + +/-- When `e` is of non-recursive structure type, and that type is not a proposition, converts `e` +into a constructor application using projections. + +For instance if we have `e : α × β`, it is converted into `Prod.mk α β e.1 e.2` (which is +definitionally equal to `e` by struct eta). -/ +def toCtorWhenStruct (inductName : Name) (e : Expr) : m Expr := do + if !env.isNonRecStructure inductName || (e.isConstructorApp?' env).isSome then + return e + let eType ← whnf (← inferType e) + if !eType.getAppFn.isConstOf inductName then return e + -- Lean tests `is_prop eType` and declines to expand when it holds; the named specification instead requires + -- the level to be *never* zero, so an uncertain level declines too (see `divergences.md`). + -- Either way the level comes from a sort-ensuring check, so a non-sort type raises a kernel + -- error rather than reaching an unreachable branch. + unless ← isNeverProp eType do return e + return expandEtaStruct env eType e + +def getRecRuleFor (rval : RecursorVal) (major : Expr) : Option RecursorRule := do + let .const fn _ := major.getAppFn | none + rval.rules.find? (·.ctor == fn) + +/-- Performs recursor reduction on `e` (returning `none` if not applicable). + +For recursor reduction to occur, `e` must be a recursor application where the major premise is +either a complete constructor application, a `Nat` or `String` literal, or of a K- or +structure-like inductive type (in each case it is converted into an equivalent constructor +application). The reduction is done by applying the `RecursorRule.rhs` associated with the +constructor to everything before the indices in the recursor application (its parameters, motives +and minor premises) and then to the fields of the constructor application; any arguments after the +major premise are re-applied to the result. -/ +def inductiveReduceRec [Monad m] (env : Environment) (e : Expr) + (whnf : Expr → m Expr) (inferType : Expr → m Expr) (isDefEq : Expr → Expr → m Bool) + (isNeverProp : Expr → m Bool) : + m (Option Expr) := do + let .const recFn ls := e.getAppFn | return none + let some (.recInfo info) := env.find? recFn | return none + let recArgs := e.getAppArgs + let majorIdx := info.getMajorIdx + let some major := recArgs[majorIdx]? | return none + let mut major := major + if info.k then + major ← toCtorWhenK env whnf inferType isDefEq info major + match ← whnf major with + | .lit (.natVal n) => major := .natLitToConstructor n + | .lit (.strVal s) => major ← whnf (.strLitToConstructor s) + | e => major ← toCtorWhenStruct env whnf inferType isNeverProp info.getMajorInduct e + let some rule := getRecRuleFor info major | return none + let majorArgs := major.getAppArgs + if rule.nfields > majorArgs.size then return none + if ls.length != info.levelParams.length then return none + let mut rhs := rule.rhs.instantiateLevelParams info.levelParams ls + -- get the parameters, motives and minor premises from the recursor application (recursor rules + -- don't need the indices, as these are determined by the constructor and its parameters/fields) + rhs := mkAppRange rhs 0 info.getFirstIndexIdx recArgs + -- get fields from constructor application + rhs := mkAppRange rhs (majorArgs.size - rule.nfields) majorArgs.size majorArgs + if majorIdx + 1 < recArgs.size then + rhs := mkAppRange rhs (majorIdx + 1) recArgs.size recArgs + return rhs + +end diff --git a/Ix/Theory/Named/Reference/Inductive/ValidationTrace.lean b/Ix/Theory/Named/Reference/Inductive/ValidationTrace.lean new file mode 100644 index 000000000..b757d208d --- /dev/null +++ b/Ix/Theory/Named/Reference/Inductive/ValidationTrace.lean @@ -0,0 +1,1600 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Reference.Inductive.Add + +set_option linter.unusedSimpArgs false + +namespace Ix.Theory.Named +open Lean hiding Environment Exception +open Kernel + +namespace AddInductive +open TypeChecker + +/-- The constructor root check runs with no validation-local declarations, +while retaining every other field of the post-family checker context. -/ +def Context.withEmptyLocalContext (context : Context) : Context := + { context with lctx := {} } + +/-- One exact successful `ensureType` execution used for an ordinary +constructor field. -/ +structure ConstructorEnsureTypeStep where + context : Context + source : Expr + result : Expr + +def ConstructorEnsureTypeStep.Valid + (step : ConstructorEnsureTypeStep) : Prop := + TypeChecker.M.run step.context.env step.context.safety + step.context.lctx step.context.lparams step.context.fuel + (TypeChecker.ensureType step.source) = + .ok step.result + +/-- The two successful universe branches of `checkConstructors`: either the +transparent structural comparison succeeds, or the exact fallback comparison +does. Keeping the branch choice prevents a trace from silently replacing the +executable universe test with a stronger premise. -/ +inductive ConstructorUniverseTrace (resultLevel fieldLevel : Level) : Type where + | structural + (valid : levelStructGe resultLevel fieldLevel = true) : + ConstructorUniverseTrace resultLevel fieldLevel + | fallback + (structuralFailed : levelStructGe resultLevel fieldLevel = false) + (valid : (resultLevel.isAlwaysZero || resultLevel.geq fieldLevel) = true) : + ConstructorUniverseTrace resultLevel fieldLevel + +namespace ConstructorUniverseTrace + +/-- A field rejected by both executable universe comparisons cannot have a +successful universe trace. -/ +theorem not_nonempty_of_rejected + (structuralRejected : levelStructGe resultLevel fieldLevel = false) + (fallbackRejected : + (resultLevel.isAlwaysZero || resultLevel.geq fieldLevel) = false) : + ¬ Nonempty (ConstructorUniverseTrace resultLevel fieldLevel) := by + rintro ⟨trace⟩ + cases trace with + | structural valid => simp_all + | fallback _ valid => simp_all + +end ConstructorUniverseTrace + +/-- Complete successful traversal of `checkPositivity.loop`, indexed by the +exact source expression, checker context, and remaining fuel. The recursive +constructor records the precise local declaration used by the executable +traversal; the terminal constructor records the accepted recursive target. -/ +inductive ConstructorPositivityTrace + (stats : InductiveStats) (ctor : Name) (argIdx : Nat) : + (context : Context) → (source : Expr) → (fuel : Nat) → Type where + | absent + (context : Context) (source result : Expr) (fuel : Nat) + (whnf : CandidateWhnfStep.Valid ⟨context, source, result⟩) + (occurs : hasIndOcc stats.indConsts result = false) : + ConstructorPositivityTrace stats ctor argIdx context source (fuel + 1) + | forallE + (context : Context) (source : Expr) (fuel : Nat) + (name : Name) (domain body : Expr) (binderInfo : BinderInfo) + (whnf : CandidateWhnfStep.Valid + ⟨context, source, .forallE name domain body binderInfo⟩) + (occurs : hasIndOcc stats.indConsts + (.forallE name domain body binderInfo) = true) + (domainFree : hasIndOcc stats.indConsts domain = false) + (tail : ConstructorPositivityTrace stats ctor argIdx + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) fuel) : + ConstructorPositivityTrace stats ctor argIdx context source (fuel + 1) + | target + (context : Context) (source result : Expr) (fuel targetIdx : Nat) + (whnf : CandidateWhnfStep.Valid ⟨context, source, result⟩) + (occurs : hasIndOcc stats.indConsts result = true) + (terminal : result.isForall = false) + (valid : isValidIndApp? stats result = some targetIdx) : + ConstructorPositivityTrace stats ctor argIdx context source (fuel + 1) + +namespace ConstructorPositivityTrace + +/-- Observable recursive-target data retained by the positivity traversal. +`binderDepth` counts positive Pi domains traversed before the terminal family +application. -/ +structure Target where + familyIdx : Nat + binderDepth : Nat + deriving DecidableEq, Repr + +/-- Erase proof fields while retaining the exact sibling-family ordinal and +positive-Pi depth selected by the executable positivity run. -/ +def target? : + ConstructorPositivityTrace stats ctor argIdx context source fuel → + Option Target + | .absent _ _ _ _ _ _ => none + | .forallE _ _ _ _ _ _ _ _ _ _ tail => + tail.target?.map fun target => + { target with binderDepth := target.binderDepth + 1 } + | .target _ _ _ _ familyIdx _ _ _ _ => + some { familyIdx, binderDepth := 0 } + +/-- Erasing a positivity trace replays the exact executable traversal. -/ +theorem run + (trace : ConstructorPositivityTrace stats ctor argIdx context source fuel) : + checkPositivity.loop stats ctor argIdx source fuel context = .ok () := by + induction trace with + | absent context source result fuel whnf occurs => + unfold checkPositivity.loop + simp only [ReaderT.bind, Bind.bind, liftTypeChecker_apply] + rw [whnf] + simp only [Except.bind] + rw [occurs] + rfl + | forallE context source fuel name domain body binderInfo whnf occurs + domainFree tail ih => + unfold checkPositivity.loop + simp only [ReaderT.bind, Bind.bind, liftTypeChecker_apply] + rw [whnf] + simp only [Except.bind] + rw [occurs] + simp only [Bool.not_true, Bool.false_eq_true, if_false, + ReaderT.pure, Pure.pure, ReaderT.bind, Bind.bind, + Except.bind, Except.pure] + rw [domainFree] + simp only [Bool.false_eq_true, if_false, withLocalDecl_apply] + exact ih + | target context source result fuel targetIdx whnf occurs terminal valid => + unfold checkPositivity.loop + simp only [ReaderT.bind, Bind.bind, liftTypeChecker_apply] + rw [whnf] + simp only [Except.bind] + rw [occurs] + simp only [Bool.not_true, Bool.false_eq_true, if_false, + ReaderT.pure, Pure.pure, ReaderT.bind, Bind.bind, + Except.bind, Except.pure] + cases result <;> + simp_all [Expr.isForall, ReaderT.pure, Pure.pure, Except.pure] + +/-- Every successful executable positivity traversal decomposes into the +source-indexed trace above. -/ +theorem exists_of_run + (success : checkPositivity.loop stats ctor argIdx source fuel context = + .ok ()) : + Nonempty (ConstructorPositivityTrace stats ctor argIdx + context source fuel) := by + induction fuel generalizing context source with + | zero => + rw [checkPositivity.loop.eq_1] at success + change Except.error Exception.deepRecursion = Except.ok () at success + contradiction + | succ fuel ih => + rw [checkPositivity.loop.eq_2] at success + simp only [ReaderT.bind, Bind.bind, liftTypeChecker_apply] at success + cases hwhnf : TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.whnf source) with + | error err => simp_all [Except.bind] + | ok result => + rw [hwhnf] at success + simp only [Except.bind] at success + cases hocc : hasIndOcc stats.indConsts result with + | false => exact ⟨.absent context source result fuel hwhnf hocc⟩ + | true => + rw [hocc] at success + simp only [Bool.not_true, Bool.false_eq_true, if_false, + ReaderT.pure, Pure.pure, ReaderT.bind, Bind.bind, + Except.bind, Except.pure] at success + cases result + case forallE name domain body binderInfo => + simp only at success + cases hdomain : hasIndOcc stats.indConsts domain with + | false => + rw [hdomain] at success + simp only [Bool.false_eq_true, if_false, + withLocalDecl_apply] at success + obtain ⟨tail⟩ := ih success + exact ⟨.forallE context source fuel name domain body + binderInfo hwhnf hocc hdomain tail⟩ + | true => + rw [hdomain] at success + change Except.error _ = Except.ok () at success + contradiction + all_goals + simp only at success + cases hvalid : isValidIndApp? stats _ with + | none => + rw [hvalid] at success + change Except.error _ = Except.ok () at success + contradiction + | some targetIdx => + exact ⟨.target context source _ fuel targetIdx + hwhnf hocc rfl hvalid⟩ + +/-- Execute the positivity traversal while retaining its exact dependent +trace as data. Unlike `exists_of_run`, this decomposition is transparent and +therefore remains available to later executable alignment audits; erasing the +result with `run` recovers the ordinary checker execution. -/ +def buildExecution (stats : InductiveStats) (ctor : Name) (argIdx : Nat) + (context : Context) (source : Expr) : + (fuel : Nat) → Except Exception + (ConstructorPositivityTrace stats ctor argIdx context source fuel) + | 0 => .error .deepRecursion + | fuel + 1 => + match hwhnf : TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.whnf source) with + | .error error => .error error + | .ok result => + match hoccurs : hasIndOcc stats.indConsts result with + | false => .ok (.absent context source result fuel hwhnf hoccurs) + | true => + match hforall : result.isForall with + | true => + match result with + | .forallE name domain body binderInfo => + match hdomain : hasIndOcc stats.indConsts domain with + | true => .error <| .other + s!"arg #{argIdx + 1} of '{ctor}' has a non positive occurrence of the datatypes being declared" + | false => + match buildExecution stats ctor argIdx + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) fuel with + | .error error => .error error + | .ok tail => .ok (.forallE context source fuel name + domain body binderInfo hwhnf hoccurs hdomain tail) + | _ => .error <| .other + "positivity WHNF shape disagrees with isForall" + | false => + match hvalid : isValidIndApp? stats result with + | none => .error <| .other + s!"arg #{argIdx + 1} of '{ctor}' has a non valid occurrence of the datatypes being declared" + | some targetIdx => .ok (.target context source result fuel + targetIdx hwhnf hoccurs hforall hvalid) + +/-- The transparent decomposition succeeds on every input accepted by the +executable positivity traversal, so retained-trace construction needs no +choice principle. -/ +theorem buildExecution_ok_of_run + (success : checkPositivity.loop stats ctor argIdx source fuel context = + .ok ()) : + ∃ trace, buildExecution stats ctor argIdx context source fuel = + .ok trace := by + induction fuel generalizing context source with + | zero => + rw [checkPositivity.loop.eq_1] at success + change Except.error Exception.deepRecursion = Except.ok () at success + contradiction + | succ fuel ih => + rw [checkPositivity.loop.eq_2] at success + simp only [ReaderT.bind, Bind.bind, liftTypeChecker_apply] at success + unfold buildExecution + split + next error heq => + rw [heq] at success + simp [Except.bind] at success + next result heq => + rw [heq] at success + simp only [Except.bind] at success + split + next hoccurs => exact ⟨_, rfl⟩ + next hoccurs => + rw [hoccurs] at success + simp only [Bool.not_true, Bool.false_eq_true, if_false, + ReaderT.pure, Pure.pure, ReaderT.bind, Bind.bind, + Except.bind, Except.pure] at success + cases result <;> simp only [Expr.isForall] <;> simp only at success + case forallE name domain body binderInfo => + split + next hdomain => + rw [hdomain] at success + change Except.error _ = Except.ok () at success + contradiction + next hdomain => + rw [hdomain] at success + have tailSuccess : + checkPositivity.loop stats ctor argIdx + (body.instantiate1 context.freshExpr) fuel + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) = .ok () := success + obtain ⟨tail, htail⟩ := ih tailSuccess + rw [htail] + exact ⟨_, rfl⟩ + all_goals + split + next hvalid => + rw [hvalid] at success + change Except.error _ = Except.ok () at success + contradiction + next targetIdx hvalid => exact ⟨_, rfl⟩ + +/-- An exact positivity failure, including its diagnostic payload, excludes a +successful trace at precisely that source/context/fuel position. -/ +theorem not_nonempty_of_error + (failure : checkPositivity.loop stats ctor argIdx source fuel context = + .error err) : + ¬ Nonempty (ConstructorPositivityTrace stats ctor argIdx + context source fuel) := by + rintro ⟨trace⟩ + have success := trace.run + rw [failure] at success + contradiction + +end ConstructorPositivityTrace + +/-- Whether positivity was executed or skipped by the exact `isUnsafe` +branch of constructor validation. -/ +inductive ConstructorPositivityModeTrace + (stats : InductiveStats) (isUnsafe : Bool) + (ctor : Name) (argIdx : Nat) (context : Context) (source : Expr) : Type where + | skipped + (isUnsafe_eq : isUnsafe = true) : + ConstructorPositivityModeTrace stats isUnsafe ctor argIdx context source + | safe + (isUnsafe_eq : isUnsafe = false) + (trace : ConstructorPositivityTrace stats ctor argIdx context source + context.fuel.inductiveFuel) : + ConstructorPositivityModeTrace stats isUnsafe ctor argIdx context source + +namespace ConstructorPositivityModeTrace + +/-- Observable recursive target for the exact safe/unsafe positivity branch. +Unsafe validation deliberately exposes no positivity claim. -/ +def target? : + ConstructorPositivityModeTrace + stats isUnsafe ctor argIdx context source → + Option ConstructorPositivityTrace.Target + | .skipped _ => none + | .safe _ trace => trace.target? + +theorem run + (trace : ConstructorPositivityModeTrace + stats isUnsafe ctor argIdx context source) : + (if !isUnsafe then checkPositivity stats source ctor argIdx else pure ()) + context = .ok () := by + cases trace with + | skipped h => simp [h, ReaderT.pure, Pure.pure, Except.pure] + | safe h trace => + simp only [h, Bool.not_false, if_true] + unfold checkPositivity + simpa only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] using trace.run + +/-- Execute the recorded positivity branch and then an exact continuation. -/ +theorem bind_run + (trace : ConstructorPositivityModeTrace + stats isUnsafe ctor argIdx context source) + (next : M α) (result : α) + (nextRun : next context = .ok result) : + (do + if !isUnsafe then checkPositivity stats source ctor argIdx + next) context = .ok result := by + cases trace with + | skipped h => + simp [h, nextRun, ReaderT.bind, Bind.bind, + ReaderT.pure, Pure.pure, Except.bind, Except.pure] + | safe h trace => + have positivityRun : + checkPositivity stats source ctor argIdx context = .ok () := by + unfold checkPositivity + simpa only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] using trace.run + simp [h, positivityRun, nextRun, ReaderT.bind, Bind.bind, + ReaderT.pure, Pure.pure, Except.bind, Except.pure] + +/-- A successful executable positivity branch determines whether validation +was skipped for an unsafe declaration or supplies the full safe trace. -/ +theorem exists_of_run + (success : + (if !isUnsafe then checkPositivity stats source ctor argIdx else pure ()) + context = .ok ()) : + Nonempty (ConstructorPositivityModeTrace + stats isUnsafe ctor argIdx context source) := by + cases hUnsafe : isUnsafe with + | false => + simp only [hUnsafe, Bool.not_false, if_true] at success + unfold checkPositivity at success + simp only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] at success + obtain ⟨trace⟩ := ConstructorPositivityTrace.exists_of_run success + exact ⟨.safe rfl trace⟩ + | true => exact ⟨.skipped rfl⟩ + +/-- Transparently retain the exact safe/unsafe positivity branch selected by +constructor validation. -/ +def buildExecution (stats : InductiveStats) (isUnsafe : Bool) + (ctor : Name) (argIdx : Nat) (context : Context) (source : Expr) : + Except Exception + (ConstructorPositivityModeTrace stats isUnsafe ctor argIdx context source) := + match isUnsafe with + | true => .ok (.skipped rfl) + | false => + match ConstructorPositivityTrace.buildExecution stats ctor argIdx + context source context.fuel.inductiveFuel with + | .error error => .error error + | .ok trace => .ok (.safe rfl trace) + +/-- The retained safe/unsafe branch decomposition succeeds whenever the +executable positivity branch does. -/ +theorem buildExecution_ok_of_run + (success : + (if !isUnsafe then checkPositivity stats source ctor argIdx else pure ()) + context = .ok ()) : + ∃ trace, buildExecution stats isUnsafe ctor argIdx context source = + .ok trace := by + cases isUnsafe with + | true => exact ⟨_, rfl⟩ + | false => + simp only [Bool.not_false, if_true] at success + unfold checkPositivity at success + simp only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] at success + obtain ⟨trace, htrace⟩ := + ConstructorPositivityTrace.buildExecution_ok_of_run success + unfold buildExecution + rw [htrace] + exact ⟨_, rfl⟩ + +/-- Failure of the exact safe/unsafe positivity branch excludes its retained +mode trace without changing the executable diagnostic. -/ +theorem not_nonempty_of_error + (failure : + (if !isUnsafe then checkPositivity stats source ctor argIdx else pure ()) + context = .error err) : + ¬ Nonempty (ConstructorPositivityModeTrace + stats isUnsafe ctor argIdx context source) := by + rintro ⟨trace⟩ + have success := trace.run + rw [failure] at success + contradiction + +end ConstructorPositivityModeTrace + +/-- Exact successful validation of one constructor type from its root through +its parameter prefix, ordinary fields, positivity checks, and terminal family +application. Every recursive index is selected by the executable traversal. -/ +inductive ConstructorTypeValidationTrace + (stats : InductiveStats) (isUnsafe : Bool) + (familyIdx : Nat) (ctor : Name) : + (context : Context) → (source : Expr) → + (argIdx fuel : Nat) → Type where + | parameter + (context : Context) (fuel argIdx : Nat) + (name : Name) (domain body : Expr) (binderInfo : BinderInfo) + (param parameterType : Expr) + (parameterAt : stats.params[argIdx]? = some param) + (parameterTypeRun : getType param context = .ok parameterType) + (defeq : CandidateIsDefEqStep.Valid + ⟨context, domain, parameterType⟩) + (tail : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context (body.instantiate1 param) (argIdx + 1) fuel) : + ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context (.forallE name domain body binderInfo) argIdx (fuel + 1) + | ordinary + (context : Context) (fuel argIdx : Nat) + (name : Name) (domain body : Expr) (binderInfo : BinderInfo) + (sortResult : Expr) + (noParameter : stats.params[argIdx]? = none) + (ensureType : ConstructorEnsureTypeStep.Valid + ⟨context, domain, sortResult⟩) + (universeTrace : ConstructorUniverseTrace + stats.resultLevel sortResult.sortLevel!) + (positivity : ConstructorPositivityModeTrace + stats isUnsafe ctor argIdx context domain) + (tail : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) (argIdx + 1) fuel) : + ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context (.forallE name domain body binderInfo) argIdx (fuel + 1) + | terminal + (context : Context) (source : Expr) (fuel argIdx : Nat) + (terminal : source.isForall = false) + (valid : isValidIndAppIdx stats source familyIdx = true) : + ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source argIdx (fuel + 1) + +namespace ConstructorTypeValidationTrace + +/-- Field-ordered recursive-target observations for one constructor. Parameter +binders are omitted; every ordinary constructor field contributes one slot. -/ +def targets : + ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source argIdx fuel → + List (Option ConstructorPositivityTrace.Target) + | .parameter _ _ _ _ _ _ _ _ _ _ _ _ tail => tail.targets + | .ordinary _ _ _ _ _ _ _ _ _ _ _ positivity tail => + positivity.target? :: tail.targets + | .terminal _ _ _ _ _ _ => [] + +/-- Erasing one constructor-type trace replays the exact inner +`checkConstructorType.loop` execution. -/ +theorem run + (trace : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source argIdx fuel) : + checkConstructorType.loop stats isUnsafe familyIdx ctor source argIdx fuel + context = .ok () := by + induction trace with + | parameter context fuel argIdx name domain body binderInfo param + parameterType parameterAt parameterTypeRun defeq tail ih => + rw [show fuel + 1 = Nat.succ fuel by omega] + rw [checkConstructorType.loop.eq_2] + rw [parameterAt] + simp only [ReaderT.bind, Bind.bind] + rw [parameterTypeRun] + simp only [Except.bind, liftTypeChecker_apply] + rw [defeq] + simp only [if_true, ReaderT.pure, Pure.pure, ReaderT.bind, Bind.bind, + Except.bind, Except.pure] + exact ih + | ordinary context fuel argIdx name domain body binderInfo sortResult noParameter + ensureType universeTrace positivity tail ih => + rw [show fuel + 1 = Nat.succ fuel by omega] + rw [checkConstructorType.loop.eq_2] + rw [noParameter] + simp only [ReaderT.bind, Bind.bind, liftTypeChecker_apply] + rw [ensureType] + simp only [Except.bind] + let next : M PUnit := + withLocalDecl name binderInfo (consumeTypeAnnotations domain) fun arg => + checkConstructorType.loop stats isUnsafe familyIdx ctor + (body.instantiate1 arg) (argIdx + 1) fuel + have nextRun : next context = .ok () := by + simp only [next, withLocalDecl_apply] + exact ih + have restRun : + (do + if !isUnsafe then checkPositivity stats domain ctor argIdx + next) context = .ok () := + positivity.bind_run next () nextRun + cases universeTrace with + | structural valid => + rw [valid] + simp only [if_true, ReaderT.pure, Pure.pure, + ReaderT.bind, Bind.bind, Except.bind, Except.pure] + exact restRun + | fallback structuralFailed valid => + rw [structuralFailed, valid] + simp only [Bool.true_eq_false, Bool.not_true, if_false, + Bool.false_eq_true, + ReaderT.pure, Pure.pure, ReaderT.bind, Bind.bind, + Except.bind, Except.pure] + exact restRun + | terminal context source fuel argIdx terminal valid => + rw [show fuel + 1 = Nat.succ fuel by omega] + cases source <;> + simp_all [checkConstructorType.loop, Expr.isForall, + ReaderT.pure, Pure.pure, Except.pure] + +/-- Every successful one-constructor telescope traversal decomposes into the +exact parameter, field, universe, positivity, and terminal trace. -/ +theorem exists_of_run + (success : + checkConstructorType.loop stats isUnsafe familyIdx ctor source argIdx fuel + context = .ok ()) : + Nonempty (ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source argIdx fuel) := by + induction fuel generalizing context source argIdx with + | zero => + rw [checkConstructorType.loop.eq_1] at success + change Except.error Exception.deepRecursion = Except.ok () at success + contradiction + | succ fuel ih => + rw [show fuel + 1 = Nat.succ fuel by omega] at success + cases source + case forallE name domain body binderInfo => + rw [checkConstructorType.loop.eq_2] at success + simp only at success + cases hparam : stats.params[argIdx]? with + | some param => + rw [hparam] at success + simp only [ReaderT.bind, Bind.bind] at success + cases hget : getType param context with + | error err => simp_all [Except.bind] + | ok parameterType => + rw [hget] at success + simp only [Except.bind, liftTypeChecker_apply] at success + cases hdefeq : TypeChecker.M.run context.env context.safety + context.lctx context.lparams context.fuel + (TypeChecker.isDefEq domain parameterType) with + | error err => simp_all [Except.bind] + | ok equal => + rw [hdefeq] at success + simp only [Except.bind] at success + cases equal with + | false => + change Except.error _ = Except.ok () at success + contradiction + | true => + simp only [if_true, ReaderT.pure, Pure.pure, + ReaderT.bind, Bind.bind, Except.bind, + Except.pure] at success + obtain ⟨tail⟩ := ih success + exact ⟨.parameter context fuel argIdx name domain body + binderInfo param parameterType hparam hget hdefeq tail⟩ + | none => + rw [hparam] at success + simp only [ReaderT.bind, Bind.bind, + liftTypeChecker_apply] at success + cases hensure : TypeChecker.M.run context.env context.safety + context.lctx context.lparams context.fuel + (TypeChecker.ensureType domain) with + | error err => simp_all [Except.bind] + | ok sortResult => + rw [hensure] at success + simp only [Except.bind] at success + have finish + (universeTrace : ConstructorUniverseTrace + stats.resultLevel sortResult.sortLevel!) + (restSuccess : + (do + if !isUnsafe then + checkPositivity stats domain ctor argIdx + withLocalDecl name binderInfo + (consumeTypeAnnotations domain) fun arg => + checkConstructorType.loop stats isUnsafe familyIdx ctor + (body.instantiate1 arg) (argIdx + 1) fuel) + context = .ok ()) : + Nonempty (ConstructorTypeValidationTrace stats isUnsafe + familyIdx ctor context + (.forallE name domain body binderInfo) argIdx (fuel + 1)) := by + cases isUnsafe with + | false => + simp only [Bool.not_false, if_true, + ReaderT.bind, Bind.bind] at restSuccess + cases hpos : checkPositivity stats domain ctor argIdx context with + | error err => simp_all [Except.bind] + | ok typeUnit => + cases typeUnit + rw [hpos] at restSuccess + simp only [Except.bind, withLocalDecl_apply] at restSuccess + have hposLoop : + checkPositivity.loop stats ctor argIdx domain + context.fuel.inductiveFuel context = .ok () := by + unfold checkPositivity at hpos + simpa only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] using hpos + obtain ⟨positivityTrace⟩ := + ConstructorPositivityTrace.exists_of_run hposLoop + obtain ⟨tail⟩ := ih restSuccess + exact ⟨.ordinary context fuel argIdx name domain body + binderInfo sortResult hparam hensure universeTrace + (.safe rfl positivityTrace) tail⟩ + | true => + simp only [Bool.not_true, if_false, + ReaderT.pure, Pure.pure, ReaderT.bind, Bind.bind, + Except.bind, Except.pure, + withLocalDecl_apply] at restSuccess + obtain ⟨tail⟩ := ih restSuccess + exact ⟨.ordinary context fuel argIdx name domain body + binderInfo sortResult hparam hensure universeTrace + (.skipped rfl) tail⟩ + cases hstruct : levelStructGe stats.resultLevel + sortResult.sortLevel! with + | true => + rw [hstruct] at success + simp only [if_true, ReaderT.pure, Pure.pure, + ReaderT.bind, Bind.bind, Except.bind, + Except.pure] at success + exact finish (.structural hstruct) success + | false => + rw [hstruct] at success + simp only [Bool.false_eq_true, if_false] at success + cases hfallback : + (stats.resultLevel.isAlwaysZero || + stats.resultLevel.geq sortResult.sortLevel!) with + | false => + rw [hfallback] at success + change Except.error _ = Except.ok () at success + contradiction + | true => + rw [hfallback] at success + simp only [Bool.true_eq_false, Bool.not_true, if_false, + ReaderT.pure, Pure.pure, ReaderT.bind, Bind.bind, + Except.bind, Except.pure] at success + exact finish (.fallback hstruct hfallback) success + all_goals + unfold checkConstructorType.loop at success + simp only at success + cases hvalid : isValidIndAppIdx stats _ familyIdx with + | false => + rw [hvalid] at success + change Except.error _ = Except.ok () at success + contradiction + | true => + exact ⟨.terminal context _ fuel argIdx rfl hvalid⟩ + +/-- Execute one constructor telescope while retaining the exact parameter, +universe, positivity, and terminal choices made by the ordinary validator. +The returned data is transparent, so later executable gates can inspect the +same trace without selecting it through `Classical.choice`. -/ +def buildExecution (stats : InductiveStats) (isUnsafe : Bool) + (familyIdx : Nat) (ctor : Name) (context : Context) (source : Expr) + (argIdx : Nat) : + (fuel : Nat) → Except Exception + (ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source argIdx fuel) + | 0 => .error .deepRecursion + | fuel + 1 => + match hforall : source.isForall with + | false => + match hvalid : isValidIndAppIdx stats source familyIdx with + | false => .error <| .other s!"invalid return type for '{ctor}'" + | true => .ok (.terminal context source fuel argIdx hforall hvalid) + | true => + match source with + | .forallE name domain body binderInfo => + match hparam : stats.params[argIdx]? with + | some param => + match hget : getType param context with + | .error error => .error error + | .ok parameterType => + match hdefeq : TypeChecker.M.run context.env + context.safety context.lctx context.lparams + context.fuel + (TypeChecker.isDefEq domain parameterType) with + | .error error => .error error + | .ok false => .error <| .other + s!"arg #{argIdx + 1} of '{ctor}' does not match inductive datatype parameters" + | .ok true => + match buildExecution stats isUnsafe familyIdx ctor + context (body.instantiate1 param) (argIdx + 1) + fuel with + | .error error => .error error + | .ok tail => .ok (.parameter context fuel argIdx + name domain body binderInfo param parameterType + hparam hget hdefeq tail) + | none => + match hensure : TypeChecker.M.run context.env context.safety + context.lctx context.lparams context.fuel + (TypeChecker.ensureType domain) with + | .error error => .error error + | .ok sortResult => + let finish (universeTrace : ConstructorUniverseTrace + stats.resultLevel sortResult.sortLevel!) := + match ConstructorPositivityModeTrace.buildExecution + stats isUnsafe ctor argIdx context domain with + | .error error => .error error + | .ok positivity => + match buildExecution stats isUnsafe familyIdx ctor + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) + (argIdx + 1) fuel with + | .error error => .error error + | .ok tail => .ok (.ordinary context fuel argIdx + name domain body binderInfo sortResult hparam + hensure universeTrace positivity tail) + match hstruct : levelStructGe stats.resultLevel + sortResult.sortLevel! with + | true => finish (.structural hstruct) + | false => + match hfallback : stats.resultLevel.isAlwaysZero || + stats.resultLevel.geq sortResult.sortLevel! with + | false => .error <| .other + s!"universe level of type_of(arg #{argIdx + 1}) of '{ctor}' is too big for the corresponding inductive datatype" + | true => finish (.fallback hstruct hfallback) + | _ => .error <| .other + "constructor source shape disagrees with isForall" + +/-- The transparent telescope decomposition succeeds on every constructor +type accepted by the inner executable validator. -/ +theorem buildExecution_ok_of_run + (success : + checkConstructorType.loop stats isUnsafe familyIdx ctor source argIdx fuel + context = .ok ()) : + ∃ trace, buildExecution stats isUnsafe familyIdx ctor context source + argIdx fuel = .ok trace := by + induction fuel generalizing context source argIdx with + | zero => + rw [checkConstructorType.loop.eq_1] at success + change Except.error Exception.deepRecursion = Except.ok () at success + contradiction + | succ fuel ih => + rw [show fuel + 1 = Nat.succ fuel by omega] at success + cases source + case forallE name domain body binderInfo => + rw [checkConstructorType.loop.eq_2] at success + simp only at success + unfold buildExecution + simp only [Expr.isForall] + split + next param hparam => + rw [hparam] at success + simp only [ReaderT.bind, Bind.bind] at success + split + next error heq => + rw [heq] at success + simp [Except.bind] at success + next parameterType heq => + rw [heq] at success + simp only [Except.bind, liftTypeChecker_apply] at success + split + next error heq2 => + rw [heq2] at success + simp [Except.bind] at success + next heq2 => + rw [heq2] at success + simp only [Except.bind] at success + change Except.error _ = Except.ok () at success + contradiction + next heq2 => + rw [heq2] at success + simp only [Except.bind, if_true, ReaderT.pure, Pure.pure, + ReaderT.bind, Bind.bind, Except.pure] at success + obtain ⟨tail, htail⟩ := ih success + rw [htail] + exact ⟨_, rfl⟩ + next hparam => + rw [hparam] at success + simp only [ReaderT.bind, Bind.bind, liftTypeChecker_apply] at success + split + next error heq => + rw [heq] at success + simp [Except.bind] at success + next sortResult heq => + rw [heq] at success + simp only [Except.bind] at success + have finish : + (do + if !isUnsafe then checkPositivity stats domain ctor argIdx + withLocalDecl name binderInfo (consumeTypeAnnotations domain) + fun arg => + checkConstructorType.loop stats isUnsafe familyIdx ctor + (body.instantiate1 arg) (argIdx + 1) fuel) + context = .ok () → + (∃ positivity, + ConstructorPositivityModeTrace.buildExecution stats isUnsafe + ctor argIdx context domain = .ok positivity) ∧ + ∃ tail, + buildExecution stats isUnsafe familyIdx ctor + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) (argIdx + 1) fuel = + .ok tail := by + intro restSuccess + cases isUnsafe with + | false => + simp only [Bool.not_false, if_true, + ReaderT.bind, Bind.bind] at restSuccess + cases hpos : checkPositivity stats domain ctor argIdx + context with + | error err => simp_all [Except.bind] + | ok posUnit => + cases posUnit + rw [hpos] at restSuccess + simp only [Except.bind, + withLocalDecl_apply] at restSuccess + have hpmSuccess : + (if !false then + checkPositivity stats domain ctor argIdx + else pure ()) context = .ok () := by + simpa using hpos + exact ⟨ConstructorPositivityModeTrace.buildExecution_ok_of_run + hpmSuccess, ih restSuccess⟩ + | true => + simp only [Bool.not_true, if_false, + ReaderT.pure, Pure.pure, ReaderT.bind, Bind.bind, + Except.bind, Except.pure, + withLocalDecl_apply] at restSuccess + have hpmSuccess : + (if !true then + checkPositivity stats domain ctor argIdx + else pure ()) context = .ok () := by + simp [ReaderT.pure, Pure.pure, Except.pure] + exact ⟨ConstructorPositivityModeTrace.buildExecution_ok_of_run + hpmSuccess, ih restSuccess⟩ + split + next hstruct => + rw [hstruct] at success + simp only [if_true, ReaderT.pure, Pure.pure, + ReaderT.bind, Bind.bind, Except.bind, Except.pure] at success + obtain ⟨⟨positivity, hpm⟩, tail, htail⟩ := finish success + rw [hpm, htail] + exact ⟨_, rfl⟩ + next hstruct => + rw [hstruct] at success + simp only [Bool.false_eq_true, if_false] at success + split + next hfallback => + rw [hfallback] at success + change Except.error _ = Except.ok () at success + contradiction + next hfallback => + rw [hfallback] at success + simp only [Bool.true_eq_false, Bool.not_true, if_false, + ReaderT.pure, Pure.pure, ReaderT.bind, Bind.bind, + Except.bind, Except.pure] at success + obtain ⟨⟨positivity, hpm⟩, tail, htail⟩ := finish success + rw [hpm, htail] + exact ⟨_, rfl⟩ + all_goals + unfold checkConstructorType.loop at success + simp only at success + unfold buildExecution + simp only [Expr.isForall] + split + next hvalid => + rw [hvalid] at success + change Except.error _ = Except.ok () at success + contradiction + next hvalid => exact ⟨_, rfl⟩ + +/-- Erasing the inner trace also replays the public one-constructor checker, +including its exact context-fuel read. -/ +theorem check_run + (trace : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source 0 context.fuel.inductiveFuel) : + checkConstructorType stats isUnsafe familyIdx ctor source context = .ok () := by + unfold checkConstructorType + simpa only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] using trace.run + +/-- Any exact inner constructor-telescope failure excludes a trace at that +same parameter/field/terminal position and retains the original error value. -/ +theorem not_nonempty_of_error + (failure : + checkConstructorType.loop stats isUnsafe familyIdx ctor source argIdx fuel + context = .error err) : + ¬ Nonempty (ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source argIdx fuel) := by + rintro ⟨trace⟩ + have success := trace.run + rw [failure] at success + contradiction + +end ConstructorTypeValidationTrace + +/-- Source-ordered validation of a constructor list. The `seen` index makes +duplicate-name checks part of the trace and prevents reordering or omission. +Root closedness and full type checking are retained before the recursive type +trace, exactly as in `checkConstructors`. -/ +inductive ConstructorListValidationTrace + (stats : InductiveStats) (isUnsafe : Bool) + (familyIdx : Nat) (context : Context) : + NameSet → List Constructor → Type where + | nil (seen : NameSet) : + ConstructorListValidationTrace stats isUnsafe familyIdx context seen [] + | cons + (seen : NameSet) (head : Constructor) (tail : List Constructor) + (fresh : seen.contains head.name = false) + (closed : context.env.checkNoMVarNoFVar head.name head.type = .ok ()) + (rootCheck : CandidateCheckTypeObservation + context.withEmptyLocalContext head.type) + (typeTrace : ConstructorTypeValidationTrace + stats isUnsafe familyIdx head.name context head.type 0 + context.fuel.inductiveFuel) + (tailTrace : ConstructorListValidationTrace stats isUnsafe familyIdx + context (seen.insert head.name) tail) : + ConstructorListValidationTrace stats isUnsafe familyIdx context + seen (head :: tail) + +namespace ConstructorListValidationTrace + +/-- Constructor-ordered positivity observations for one family. -/ +def targets : + ConstructorListValidationTrace stats isUnsafe familyIdx + context seen constructors → + List (List (Option ConstructorPositivityTrace.Target)) + | .nil _ => [] + | .cons _ _ _ _ _ _ typeTrace tailTrace => + typeTrace.targets :: tailTrace.targets + +/-- Execute the source-ordered constructor fold while retaining its exact +dependent validation trace. Every stored equation is obtained from the same +checker call made by `checkConstructorFold`; no semantic premise participates +in acceptance. -/ +def buildExecution (stats : InductiveStats) (isUnsafe : Bool) + (familyIdx : Nat) (context : Context) : + (seen : NameSet) → (constructors : List Constructor) → + Except Exception + (ConstructorListValidationTrace stats isUnsafe familyIdx context + seen constructors) + | seen, [] => .ok (.nil seen) + | seen, head :: tail => + match hfresh : seen.contains head.name with + | true => .error <| .other s!"duplicate constructor name '{head.name}'" + | false => + match hclosed : context.env.checkNoMVarNoFVar + head.name head.type with + | .error error => .error error + | .ok () => + match hroot : TypeChecker.M.run context.env context.safety {} + context.lparams context.fuel + (TypeChecker.checkType head.type) with + | .error error => .error error + | .ok inferred => + let rootCheck : CandidateCheckTypeObservation + context.withEmptyLocalContext head.type := + ⟨inferred, by + simpa only [CandidateCheckTypeStep.Valid, + Context.withEmptyLocalContext] using hroot⟩ + match ConstructorTypeValidationTrace.buildExecution stats + isUnsafe familyIdx head.name context head.type 0 + context.fuel.inductiveFuel with + | .error error => .error error + | .ok typeTrace => + match buildExecution stats isUnsafe familyIdx context + (seen.insert head.name) tail with + | .error error => .error error + | .ok tailTrace => .ok (.cons seen head tail hfresh + hclosed rootCheck typeTrace tailTrace) + +end ConstructorListValidationTrace + +/-- A transparent presentation of the constructor portion of the executable +validator, discarding only the final duplicate-name accumulator. -/ +def checkConstructorList + (stats : InductiveStats) (isUnsafe : Bool) (familyIdx : Nat) + (context : Context) (seen : NameSet) (ctors : List Constructor) : + Except Exception Unit := do + _ ← checkConstructorFold context.env stats isUnsafe familyIdx seen ctors context + pure () + +/-- For one family, the named list recursion is exactly the array/list shell +of the real constructor validator. -/ +theorem checkConstructors_singleton_eq_checkConstructorList + (indType : InductiveType) (stats : InductiveStats) + (isUnsafe : Bool) (context : Context) : + checkConstructors #[indType] stats isUnsafe context = + checkConstructorList stats isUnsafe 0 context {} indType.ctors := by + unfold checkConstructors + simp only [ReaderT.bind, Bind.bind] + rw [liftTypeChecker_apply] + have hget : + TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel TypeChecker.getEnv = + .ok context.env := by rfl + rw [hget] + simp only [Except.bind] + simp only [checkConstructorsLoop] + unfold checkConstructorList + simp only [ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] + +namespace ConstructorListValidationTrace + +def finalSeen : NameSet → List Constructor → NameSet + | seen, [] => seen + | seen, head :: tail => finalSeen (seen.insert head.name) tail + +/-- Exact inversion for a nonempty source list. In particular the recursive +trace is indexed by the literal source tail and the accumulator obtained from +the literal source head, so omission, insertion, duplication, or reordering +cannot be hidden behind an unindexed traversal. -/ +theorem nonempty_cons_iff_exact_source : + Nonempty (ConstructorListValidationTrace stats isUnsafe familyIdx + context seen (head :: tail)) ↔ + seen.contains head.name = false ∧ + context.env.checkNoMVarNoFVar head.name head.type = .ok () ∧ + Nonempty (CandidateCheckTypeObservation + context.withEmptyLocalContext head.type) ∧ + Nonempty (ConstructorTypeValidationTrace stats isUnsafe familyIdx + head.name context head.type 0 context.fuel.inductiveFuel) ∧ + Nonempty (ConstructorListValidationTrace stats isUnsafe familyIdx + context (seen.insert head.name) tail) := by + constructor + · rintro ⟨trace⟩ + cases trace with + | cons _ _ _ fresh closed rootCheck typeTrace tailTrace => + exact ⟨fresh, closed, ⟨rootCheck⟩, ⟨typeTrace⟩, ⟨tailTrace⟩⟩ + · rintro ⟨fresh, closed, ⟨rootCheck⟩, ⟨typeTrace⟩, ⟨tailTrace⟩⟩ + exact ⟨.cons seen head tail fresh closed rootCheck typeTrace tailTrace⟩ + +/-- A duplicate at the current source position fails before all later +constructor phases, exactly as in the executable fold. -/ +theorem not_nonempty_of_duplicate + (duplicate : seen.contains head.name = true) : + ¬ Nonempty (ConstructorListValidationTrace stats isUnsafe familyIdx + context seen (head :: tail)) := by + intro trace + have fresh := (nonempty_cons_iff_exact_source.mp trace).1 + simp_all + +/-- A closedness error at the current source position excludes the trace before +the root type check or constructor telescope is entered. -/ +theorem not_nonempty_of_closedness_error + (failure : context.env.checkNoMVarNoFVar head.name head.type = .error err) : + ¬ Nonempty (ConstructorListValidationTrace stats isUnsafe familyIdx + context seen (head :: tail)) := by + intro trace + have closed := (nonempty_cons_iff_exact_source.mp trace).2.1 + rw [failure] at closed + contradiction + +/-- A closed-root `checkType` error excludes the trace at that exact source +constructor, before parameter and field validation. -/ +theorem not_nonempty_of_root_error + (failure : TypeChecker.M.run context.env context.safety {} + context.lparams context.fuel (TypeChecker.checkType head.type) = + .error err) : + ¬ Nonempty (ConstructorListValidationTrace stats isUnsafe familyIdx + context seen (head :: tail)) := by + intro trace + obtain ⟨rootCheck⟩ := + (nonempty_cons_iff_exact_source.mp trace).2.2.1 + have success := rootCheck.valid + change TypeChecker.M.run context.withEmptyLocalContext.env + context.withEmptyLocalContext.safety + context.withEmptyLocalContext.lctx + context.withEmptyLocalContext.lparams + context.withEmptyLocalContext.fuel + (TypeChecker.checkType head.type) = .ok rootCheck.inferred at success + simp only [Context.withEmptyLocalContext] at success + rw [failure] at success + contradiction + +/-- An inner parameter, field, universe, positivity, recursive-target, or +terminal-family error excludes the trace at the current constructor. -/ +theorem not_nonempty_of_type_error + (failure : checkConstructorType stats isUnsafe familyIdx + head.name head.type context = .error err) : + ¬ Nonempty (ConstructorListValidationTrace stats isUnsafe familyIdx + context seen (head :: tail)) := by + intro trace + obtain ⟨typeTrace⟩ := + (nonempty_cons_iff_exact_source.mp trace).2.2.2.1 + have success := typeTrace.check_run + rw [failure] at success + contradiction + +/-- Erasing an ordered trace replays the exact stateful constructor fold. -/ +theorem fold_run + (trace : ConstructorListValidationTrace stats isUnsafe familyIdx + context seen ctors) : + checkConstructorFold context.env stats isUnsafe familyIdx seen ctors context = + .ok (finalSeen seen ctors) := by + induction trace with + | nil => rfl + | cons seen head tail fresh closed rootCheck typeTrace tailTrace ih => + have hroot := rootCheck.valid + change TypeChecker.M.run context.withEmptyLocalContext.env + context.withEmptyLocalContext.safety + context.withEmptyLocalContext.lctx + context.withEmptyLocalContext.lparams + context.withEmptyLocalContext.fuel + (TypeChecker.checkType head.type) = + .ok rootCheck.inferred at hroot + simp only [Context.withEmptyLocalContext] at hroot + unfold checkConstructorFold + simp only + rw [fresh] + simp only [Bool.false_eq_true, if_false, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] + rw [closed] + simp only [ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure, liftExcept_apply] + rw [withEmptyLocalContext_apply] + rw [liftTypeChecker_apply] + rw [hroot] + simp only [Except.bind, readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.pure, Pure.pure, Except.pure] + rw [typeTrace.check_run] + change checkConstructorFold context.env stats isUnsafe familyIdx + (seen.insert head.name) tail context = + .ok (finalSeen (seen.insert head.name) tail) + exact ih + +/-- Any successful stateful constructor fold decomposes into the complete +source-ordered list trace; the final accumulator value itself is irrelevant. -/ +theorem exists_of_fold_run + (success : checkConstructorFold context.env stats isUnsafe familyIdx + seen ctors context = .ok result) : + Nonempty (ConstructorListValidationTrace stats isUnsafe familyIdx + context seen ctors) := by + induction ctors generalizing seen result with + | nil => exact ⟨.nil seen⟩ + | cons head tail ih => + unfold checkConstructorFold at success + simp only at success + cases hfresh : seen.contains head.name with + | true => + rw [hfresh] at success + change Except.error _ = Except.ok result at success + contradiction + | false => + rw [hfresh] at success + simp only [Bool.false_eq_true, if_false, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] at success + cases hclosed : context.env.checkNoMVarNoFVar + head.name head.type with + | error err => simp_all [liftExcept_apply, Except.bind] + | ok closedResult => + cases closedResult + rw [hclosed] at success + simp only [liftExcept_apply, Except.bind] at success + rw [withEmptyLocalContext_apply, liftTypeChecker_apply] at success + cases hroot : TypeChecker.M.run context.env context.safety {} + context.lparams context.fuel + (TypeChecker.checkType head.type) with + | error err => simp_all [Except.bind] + | ok inferred => + rw [hroot] at success + simp only [Except.bind] at success + cases htype : checkConstructorType stats isUnsafe familyIdx + head.name head.type context with + | error err => simp_all [Except.bind] + | ok typeResult => + cases typeResult + rw [htype] at success + simp only [Except.bind, ReaderT.pure, Pure.pure, + Except.pure] at success + have htypeLoop : + checkConstructorType.loop stats isUnsafe familyIdx + head.name head.type 0 context.fuel.inductiveFuel + context = .ok () := by + unfold checkConstructorType at htype + simpa only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] using htype + obtain ⟨typeTrace⟩ := + ConstructorTypeValidationTrace.exists_of_run htypeLoop + have rootCheck : CandidateCheckTypeObservation + context.withEmptyLocalContext head.type := + ⟨inferred, by + simpa only [CandidateCheckTypeStep.Valid, + Context.withEmptyLocalContext] using hroot⟩ + change checkConstructorFold context.env stats isUnsafe + familyIdx (seen.insert head.name) tail context = + .ok result at success + obtain ⟨tailTrace⟩ := ih success + exact ⟨.cons seen head tail hfresh hclosed rootCheck + typeTrace tailTrace⟩ + +/-- The transparent list decomposition succeeds on every constructor list +accepted by the executable stateful fold. -/ +theorem buildExecution_ok_of_fold_run + (success : checkConstructorFold context.env stats isUnsafe familyIdx + seen ctors context = .ok result) : + ∃ trace, buildExecution stats isUnsafe familyIdx context seen ctors = + .ok trace := by + induction ctors generalizing seen result with + | nil => exact ⟨_, rfl⟩ + | cons head tail ih => + unfold checkConstructorFold at success + simp only at success + unfold buildExecution + split + next hfresh => + rw [hfresh] at success + change Except.error _ = Except.ok result at success + contradiction + next hfresh => + rw [hfresh] at success + simp only [Bool.false_eq_true, if_false, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] at success + split + next error heq => + rw [heq] at success + simp [liftExcept_apply, Except.bind] at success + next heq => + rw [heq] at success + simp only [liftExcept_apply, Except.bind] at success + rw [withEmptyLocalContext_apply, liftTypeChecker_apply] at success + split + next error heq2 => + rw [heq2] at success + simp [Except.bind] at success + next inferred heq2 => + rw [heq2] at success + simp only [Except.bind] at success + cases htype : checkConstructorType stats isUnsafe familyIdx + head.name head.type context with + | error err => simp_all [Except.bind] + | ok typeResult => + cases typeResult + rw [htype] at success + simp only [Except.bind, ReaderT.pure, Pure.pure, + Except.pure] at success + have htypeLoop : + checkConstructorType.loop stats isUnsafe familyIdx + head.name head.type 0 context.fuel.inductiveFuel + context = .ok () := by + unfold checkConstructorType at htype + simpa only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] using htype + obtain ⟨typeTrace, hT⟩ := + ConstructorTypeValidationTrace.buildExecution_ok_of_run + htypeLoop + rw [hT] + change checkConstructorFold context.env stats isUnsafe + familyIdx (seen.insert head.name) tail context = + .ok result at success + obtain ⟨tailTrace, htl⟩ := ih success + rw [htl] + exact ⟨_, rfl⟩ + +/-- The stateful list fold's exact error value excludes a complete trace for +that same source list and incoming duplicate-name accumulator. -/ +theorem not_nonempty_of_fold_error + (failure : checkConstructorFold context.env stats isUnsafe familyIdx + seen ctors context = .error err) : + ¬ Nonempty (ConstructorListValidationTrace stats isUnsafe familyIdx + context seen ctors) := by + rintro ⟨trace⟩ + have success := trace.fold_run + rw [failure] at success + contradiction + +/-- Erasing an ordered list trace replays the transparent list validator. -/ +theorem run + (trace : ConstructorListValidationTrace stats isUnsafe familyIdx + context seen ctors) : + checkConstructorList stats isUnsafe familyIdx context seen ctors = .ok () := by + unfold checkConstructorList + rw [trace.fold_run] + rfl + +end ConstructorListValidationTrace + +/-! ## Arbitrary mutual-block validation owners -/ + +/-- The exact result selected when the ordinary family validator reaches its +continuation. Retaining the reader context matters: later constructor +validation uses the shared parameters and local declarations installed by +that very run. -/ +structure FamilyValidationBlockResult where + stats : InductiveStats + validationContext : Context + +/-- Observe the successful continuation of `checkInductiveTypes` without +changing any validation branch or error. -/ +def observeFamilyValidationBlock (nparams : Nat) + (indTypes : List InductiveType) (context : Context) : + Except Exception FamilyValidationBlockResult := + checkInductiveTypes nparams indTypes.toArray + (fun stats => fun validationContext => + .ok ⟨stats, validationContext⟩) context + +/-- Complete retained family-validation run for an arbitrary source-ordered +block. + +`run` is the real validator execution, so every later family has already +passed the kernel's definitional parameter comparison and result-level +equivalence phase. The remaining equations expose the terminal invariants +which Lean asserts before invoking the continuation. -/ +structure FamilyValidationBlockRun (nparams : Nat) + (indTypes : List InductiveType) (context : Context) where + result : FamilyValidationBlockResult + run : observeFamilyValidationBlock nparams indTypes context = .ok result + params_size : result.stats.params.size = nparams + nindices_size : result.stats.nindices.size = indTypes.length + indConsts_size : result.stats.indConsts.size = indTypes.length + +namespace FamilyValidationBlockRun + +/-- Execute and retain the ordinary family validator. The explicit terminal +checks mirror its internal assertions and make malformed instrumentation fail +instead of yielding a weaker certificate. -/ +def buildExecution (nparams : Nat) (indTypes : List InductiveType) + (context : Context) : + Except Exception (FamilyValidationBlockRun nparams indTypes context) := + match hrun : observeFamilyValidationBlock nparams indTypes context with + | .error error => .error error + | .ok result => + if hparams : result.stats.params.size = nparams then + if hnindices : result.stats.nindices.size = indTypes.length then + if hconsts : result.stats.indConsts.size = indTypes.length then + .ok { + result + run := hrun + params_size := hparams + nindices_size := hnindices + indConsts_size := hconsts } + else .error (.other "family-validation constant-count invariant failed") + else .error (.other "family-validation index-count invariant failed") + else .error (.other "family-validation parameter-count invariant failed") + +/-- Shared parameters selected by the first family and definitionally checked +against every later family. -/ +def parameters (run : FamilyValidationBlockRun nparams indTypes context) : + Array Expr := + run.result.stats.params + +/-- Common result universe selected by the first family and equivalence- +checked against every later family. -/ +def resultLevel (run : FamilyValidationBlockRun nparams indTypes context) : + Level := + run.result.stats.resultLevel + +end FamilyValidationBlockRun + +/-- Source-indexed constructor traces for every family in a block. The +natural index advances with the source list, so a trace for one family cannot +be reused at another family ordinal. -/ +inductive ConstructorBlockValidationTraces + (stats : InductiveStats) (isUnsafe : Bool) (context : Context) : + Nat → List InductiveType → Type where + | nil {familyIdx : Nat} : + ConstructorBlockValidationTraces stats isUnsafe context familyIdx [] + | cons {familyIdx : Nat} {type : InductiveType} + {types : List InductiveType} + (head : ConstructorListValidationTrace stats isUnsafe familyIdx + context {} type.ctors) + (tail : ConstructorBlockValidationTraces stats isUnsafe context + (familyIdx + 1) types) : + ConstructorBlockValidationTraces stats isUnsafe context familyIdx + (type :: types) + +namespace ConstructorBlockValidationTraces + +/-- Family-, constructor-, and field-ordered recursive-target matrix selected +by the executable arbitrary-block validator. -/ +def targets : + ConstructorBlockValidationTraces stats isUnsafe context familyIdx types → + List (List (List (Option ConstructorPositivityTrace.Target))) + | .nil => [] + | .cons head tail => head.targets :: tail.targets + +/-- Execute each source-indexed list trace in the one shared post-family +context. -/ +def buildExecution (stats : InductiveStats) (isUnsafe : Bool) + (context : Context) : + (familyIdx : Nat) → (types : List InductiveType) → + Except Exception + (ConstructorBlockValidationTraces stats isUnsafe context + familyIdx types) + | _, [] => .ok .nil + | familyIdx, type :: types => + match ConstructorListValidationTrace.buildExecution stats isUnsafe + familyIdx context {} type.ctors with + | .error error => .error error + | .ok head => + match buildExecution stats isUnsafe context (familyIdx + 1) types with + | .error error => .error error + | .ok tail => .ok (.cons head tail) + +end ConstructorBlockValidationTraces + +/-- Complete operational constructor-validation owner for an arbitrary +mutual block. `run` is the actual block call; `traces` retains every +family/constructor/field branch, including cross-family target ordinals and +recursive-Pi paths. -/ +structure ConstructorBlockValidationRun + (indTypes : List InductiveType) (stats : InductiveStats) + (isUnsafe : Bool) (context : Context) where + traces : ConstructorBlockValidationTraces stats isUnsafe context 0 indTypes + run : checkConstructors indTypes.toArray stats isUnsafe context = .ok () + +namespace ConstructorBlockValidationRun + +/-- Execute the real block validator and retain the exact dependent trace +hierarchy for that same source list. -/ +def buildExecution (indTypes : List InductiveType) + (stats : InductiveStats) (isUnsafe : Bool) (context : Context) : + Except Exception + (ConstructorBlockValidationRun indTypes stats isUnsafe context) := + match hrun : checkConstructors indTypes.toArray stats isUnsafe context with + | .error error => .error error + | .ok () => + match ConstructorBlockValidationTraces.buildExecution stats isUnsafe + context 0 indTypes with + | .error error => .error error + | .ok traces => .ok ⟨traces, hrun⟩ + +end ConstructorBlockValidationRun + +/-- The complete retained operational constructor-validation run for one +singleton family. -/ +structure ConstructorValidationRun + (indType : InductiveType) (stats : InductiveStats) + (isUnsafe : Bool) (context : Context) where + trace : ConstructorListValidationTrace stats isUnsafe 0 context {} + indType.ctors + +namespace ConstructorValidationRun + +/-- Transparent decomposition of the ordinary singleton constructor +validator. Successful output is executable data rather than a +`Classical.choice`, which lets subsequent D2/D3 audits compute over the exact +retained branch structure. -/ +def buildExecution (indType : InductiveType) (stats : InductiveStats) + (isUnsafe : Bool) (context : Context) : + Except Exception + (ConstructorValidationRun indType stats isUnsafe context) := + match ConstructorListValidationTrace.buildExecution stats isUnsafe 0 + context {} indType.ctors with + | .error error => .error error + | .ok trace => .ok ⟨trace⟩ + +/-- The transparent singleton decomposition succeeds on every source family +accepted by the real constructor validator. -/ +theorem buildExecution_ok_of_run + (success : checkConstructors #[indType] stats isUnsafe context = .ok ()) : + ∃ validation, buildExecution indType stats isUnsafe context = + .ok validation := by + rw [checkConstructors_singleton_eq_checkConstructorList] at success + unfold checkConstructorList at success + cases hfold : checkConstructorFold context.env stats isUnsafe 0 {} + indType.ctors context with + | error err => simp_all [Functor.map, Except.map] + | ok result => + obtain ⟨trace, htrace⟩ := + ConstructorListValidationTrace.buildExecution_ok_of_fold_run hfold + unfold buildExecution + rw [htrace] + exact ⟨_, rfl⟩ + +/-- Recomposition: retained operational evidence replays the real singleton +`checkConstructors` execution exactly. -/ +theorem run + (validation : ConstructorValidationRun indType stats isUnsafe context) : + checkConstructors #[indType] stats isUnsafe context = .ok () := by + rw [checkConstructors_singleton_eq_checkConstructorList] + exact validation.trace.run + +/-- Decomposition: every successful real singleton `checkConstructors` run +has complete retained operational evidence. -/ +theorem nonempty_of_run + (success : checkConstructors #[indType] stats isUnsafe context = .ok ()) : + Nonempty (ConstructorValidationRun indType stats isUnsafe context) := by + rw [checkConstructors_singleton_eq_checkConstructorList] at success + unfold checkConstructorList at success + cases hfold : checkConstructorFold context.env stats isUnsafe 0 {} + indType.ctors context with + | error err => + simp_all [Functor.map, Except.map] + | ok result => + obtain ⟨trace⟩ := + ConstructorListValidationTrace.exists_of_fold_run hfold + exact ⟨⟨trace⟩⟩ + +/-- Choose the operational shape supplied by a successful run by replaying +the transparent decomposition. The success premise only discharges the +impossible error branch, so the retained evidence is computed by +`buildExecution` rather than selected through `Classical.choice`. -/ +def of_run + (success : checkConstructors #[indType] stats isUnsafe context = .ok ()) : + ConstructorValidationRun indType stats isUnsafe context := + match h : buildExecution indType stats isUnsafe context with + | .ok validation => validation + | .error _ => absurd (buildExecution_ok_of_run success) (by simp [h]) + +/-- Exact decomposition/recomposition contract for singleton constructor +validation. -/ +theorem nonempty_iff_checkConstructors_ok : + Nonempty (ConstructorValidationRun indType stats isUnsafe context) ↔ + checkConstructors #[indType] stats isUnsafe context = .ok () := by + constructor + · rintro ⟨validation⟩ + exact validation.run + · exact nonempty_of_run + +/-- Any phase-specific executable error excludes a successful retained run. -/ +theorem not_nonempty_of_error + (failure : checkConstructors #[indType] stats isUnsafe context = .error err) : + ¬ Nonempty (ConstructorValidationRun indType stats isUnsafe context) := by + intro validation + have success := nonempty_iff_checkConstructors_ok.mp validation + rw [failure] at success + contradiction + +end ConstructorValidationRun + +#guard_named_axioms Ix.Theory.Named.AddInductive.FamilyValidationBlockRun.buildExecution [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.ConstructorBlockValidationRun.buildExecution [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.ConstructorListValidationTrace.nonempty_cons_iff_exact_source [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.ConstructorValidationRun.run [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.ConstructorValidationRun.nonempty_of_run [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.ConstructorValidationRun.of_run [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.ConstructorValidationRun.nonempty_iff_checkConstructors_ok [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.ConstructorValidationRun.not_nonempty_of_error [propext, Classical.choice, Quot.sound] + +end AddInductive +end Ix.Theory.Named diff --git a/Ix/Theory/Named/Reference/Instantiate.lean b/Ix/Theory/Named/Reference/Instantiate.lean new file mode 100644 index 000000000..6319abab3 --- /dev/null +++ b/Ix/Theory/Named/Reference/Instantiate.lean @@ -0,0 +1,39 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Lean.Expr +import Lean.LocalContext +import Lean.Util.InstantiateLevelParams + +namespace Lean +namespace Expr + +/-- Beta-reduces an application `(fun x₁ ... xₙ => b) a₁ ... aₙ aₙ₊₁ ... aₘ` in the two cases where +no substitution is needed: to `b aₙ₊₁ ... aₘ` when `b` has no loose bound variables, and to +`aᵢ aₙ₊₁ ... aₘ` when `b` is the bound variable `xᵢ`. In any other case `e` is returned unchanged — +this is what makes it cheap. -/ +def cheapBetaReduce (e : Expr) : Expr := Id.run do + if !e.isApp then return e + let fn := e.getAppFn + if !fn.isLambda then return e + let args := e.getAppArgs + let rec cont i fn := + if !fn.hasLooseBVars then + mkAppRange fn i args.size args + else if let .bvar n := fn then + assert! n < i + mkAppRange args[i - n - 1]! i args.size args + else + e + let rec loop i fn := + if i < args.size then + match fn with + | .lam _ _ body .. => loop (i + 1) body + | _ => cont i fn + else cont i fn + return loop 0 fn + +end Expr diff --git a/Ix/Theory/Named/Reference/Level.lean b/Ix/Theory/Named/Reference/Level.lean new file mode 100644 index 000000000..b774b0555 --- /dev/null +++ b/Ix/Theory/Named/Reference/Level.lean @@ -0,0 +1,412 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Lean +import Ix.Theory.Named.Reference.List + +namespace Lean.Level + +def forEach [Monad m] (l : Level) (f : Level → m Bool) : m Unit := do + if !(← f l) then return + match l with + | .succ l => l.forEach f + | .max l₁ l₂ | .imax l₁ l₂ => l₁.forEach f; l₂.forEach f + | .zero | .param .. | .mvar .. => pure () + +/-- Returns `some n` if level parameter `n` appears in `l` and `n ∉ ps`. -/ +def getUndefParam (l : Level) (ps : List Name) : Option Name := Id.run do + (·.2) <$> StateT.run (s := none) do + l.forEach fun l => do + if !l.hasParam || (← get).isSome then + return false + if let .param n := l then + if n ∉ ps then + set (some n) + return true + +/-! +## Level normalization + +Based on Yoan Géran, "A Canonical Form for Universe Levels in Impredicative Type Theory" +. +-/ + +namespace Normalize + +local instance namedOrdName : Ord Name := ⟨Name.cmp⟩ + +/-- represents v+n -/ +structure VarNode where + var : Name + offset : Nat + deriving BEq, Ord, Repr + +/-- A key-value pair `vs => { const, var }` in NormLevel represents +the max of `C(vs, const)` and `V(vs, v, n)` for each `v+n ∈ var`, using the `C` and `V` sublevel +functions from . -/ +structure Node where + const : Nat := 0 + var : List VarNode := [] + deriving Repr, Inhabited + +instance : BEq Node where + beq n₁ n₂ := n₁.const == n₂.const && n₁.var == n₂.var +instance : Ord Node where + compare n₁ n₂ := compare n₁.const n₂.const |>.then <| compare n₁.var n₂.var + +def Node.isEmpty (n : Node) : Bool := n.const == 0 && n.var.isEmpty + +def subset (cmp : α → α → Ordering) : List α → List α → Bool + | [], _ => true + | _, [] => false + | x :: xs, y :: ys => + match cmp x y with + | .lt => false + | .eq => subset cmp xs ys + | .gt => subset cmp (x :: xs) ys + +def orderedInsert (cmp : α → α → Ordering) (a : α) : List α → Option (List α) + | [] => some [a] + | b :: l => + match cmp a b with + | .lt => some (a :: b :: l) + | .eq => none + | .gt => (orderedInsert cmp a l).map (b :: ·) + +@[reducible] def NormLevel := Std.TreeMap (List Name) Node compare + deriving Repr + +instance : BEq NormLevel where + beq l₁ l₂ := + (l₁.all fun p n => l₂.get? p == some n) && + (l₂.all fun p n => l₁.get? p == some n) + +def VarNode.addVar (v : Name) (k : Nat) : List VarNode → List VarNode + | [] => [⟨v, k⟩] + | v' :: l => + match Name.cmp v v'.var with + | .lt => ⟨v, k⟩ :: v' :: l + | .eq => ⟨v, v'.offset.max k⟩ :: l + | .gt => v' :: addVar v k l + +def NormLevel.addVar (v : Name) (k : Nat) (path' : List Name) (s : NormLevel) : NormLevel := + s.modify path' fun n => { n with var := VarNode.addVar v k n.var } + +def NormLevel.addNode (v : Name) (k : Nat) (path' : List Name) (s : NormLevel) : NormLevel := + s.alter path' fun + | none => some { var := [⟨v, k⟩] } + | some n => some { n with var := VarNode.addVar v k n.var } + +def NormLevel.addConst (k : Nat) (path : List Name) (acc : NormLevel) : NormLevel := + if k = 0 || k = 1 && !path.isEmpty then acc else + acc.alter path fun + | none => some { const := k } + | some n => some { n with const := k.max n.const } + +def normalizeAux (l : Level) (path : List Name) (k : Nat) (acc : NormLevel) : NormLevel := + match l with + | .zero | .imax _ .zero => acc.addConst k path + | .succ u => normalizeAux u path (k+1) acc + | .max u v => normalizeAux u path k acc |> normalizeAux v path k + | .imax u (.succ v) => normalizeAux u path k acc |> normalizeAux v path (k+1) + | .imax u (.max v w) => normalizeAux (.imax u v) path k acc |> normalizeAux (.imax u w) path k + | .imax u (.imax v w) => normalizeAux (.imax u w) path k acc |> normalizeAux (.imax v w) path k + | .imax u (.param v) => + match orderedInsert Name.cmp v path with + | some path' => acc.addConst k path |>.addNode v k path' |> normalizeAux u path' k + | none => + let acc := if k = 0 then acc else acc.addVar v k path + normalizeAux u path k acc + | .mvar _ | .imax _ (.mvar _) => acc -- unreachable + | .param v => + match orderedInsert Name.cmp v path with + | some path' => acc.addConst k path |>.addNode v k path' + | none => if k = 0 then acc else acc.addVar v k path + +def subsumeVars : List VarNode → List VarNode → List VarNode + | [], _ => [] + | xs, [] => xs + | x :: xs, y :: ys => + match Name.cmp x.var y.var with + | .lt => x :: subsumeVars xs (y :: ys) + | .eq => if x.offset ≤ y.offset then subsumeVars xs ys else x :: subsumeVars xs ys + | .gt => subsumeVars (x :: xs) ys + +/-- Remove from `n₁` the sublevels dominated by `n₂`, whose condition set is a subset of +`n₁`'s: `C(c)` is dominated by `C(c')` when `c ≤ c'` and by `V(x+k)` when `c ≤ k + 1`, and +`V(x+k)` is dominated by `V(x+k')` when `k ≤ k'`. + +`same` says the two sit at the *same* condition set, where a variable may still discharge the +constant but the variables must not discharge themselves. -/ +def Node.subsumeBy (same : Bool) (n₁ n₂ : Node) : Node := + let n₁ := + if n₁.const = 0 || + (same || n₁.const > n₂.const) && + (n₂.var.isEmpty || n₁.const > n₂.var.foldl (·.max ·.offset) 0 + 1) + then n₁ else { n₁ with const := 0 } + if same || n₂.var.isEmpty then n₁ else { n₁ with var := subsumeVars n₁.var n₂.var } + +/-- Remove the parts of the sublevels at `(p₁, n₁)` that are dominated by the sublevels +at `(p₂, n₂)`. -/ +def Node.subsume (p₁ : List Name) (n₁ : Node) (p₂ : List Name) (n₂ : Node) : Node := + if subset compare p₂ p₁ then n₁.subsumeBy (p₁.length == p₂.length) n₂ else n₁ + +/-- Remove the parts of the sublevels at `(p₁, n₁)` dominated by other entries of the map. -/ +def NormLevel.minimize (acc : NormLevel) (p₁ : List Name) (n₁ : Node) : Node := + acc.foldl (init := n₁) (Node.subsume p₁) + +def NormLevel.subsumption (acc : NormLevel) : NormLevel := + acc.foldl (init := acc) fun acc p₁ n₁ => + let n := acc.minimize p₁ n₁ + if n.isEmpty then acc.erase p₁ else acc.insert p₁ n + +def normalize (l : Level) : NormLevel := + Normalize.normalizeAux l [] 0 {} |>.subsumption + +/-- Sublevel comparison, following Theorem 39 of the paper: `l₁ ≤ l₂` iff every sublevel +of `l₁` is dominated by some sublevel of `l₂`, where +`C(E, L) ≤ C(F, K) ↔ F ⊆ E ∧ L ≤ K`, `C(E, L) ≤ V(F, x, K) ↔ F ⊆ E ∧ L ≤ K + 1`, +and `V(E, x, L) ≤ V(F, y, K) ↔ F ⊆ E ∧ x = y ∧ L ≤ K`. + +Each sublevel picks its own dominator, and a node bundles several of them, so it is not +enough to look for a single entry of `l₂` dominating a whole node of `l₁`: for +`imax 2 v ≤ max 2 v` the constant is dominated at `∅` and the variable at `{v}`. Instead +each entry of `l₂` discharges what it can from the sublevels of `n₁` that are still +outstanding, which is the same `subsumeBy` step minimization uses; the node is dominated +once nothing is left, and the fold stops there. -/ +def NormLevel.le (l₁ l₂ : NormLevel) : Bool := + l₁.all fun p₁ n₁ => + -- `none` means nothing is left to discharge, which stops the fold + Option.isNone <| l₂.foldlM (init := n₁) (m := Option) fun n p₂ n₂ => + if subset compare p₂ p₁ then + let n := n.subsumeBy false n₂ + if n.isEmpty then none else some n + else some n + +/-! +Reconstruction of a `Level` from a `NormLevel`. + +The paper's canonical form is a set of sublevels `C(S, k)`, `V(S, v+k)`; it does not address +which such sets are expressible as level expressions. Reifying a sublevel with conditions `S` +requires nesting it under an imax chain `imax (… imax (imax (_) v₁) …) vₙ` where +`{v₁, …, vₙ} = S`, and each edge of such a chain itself contributes the sublevel +`V(S', vᵢ, 0)` where `S'` is the set of conditions up to that point. So a chain order is +admissible only if each such edge contribution is dominated by the canonical form, i.e. +there is some `V(T, vᵢ+k)` with `T ⊆ S'` among the sublevels. Canonical forms produced by +`normalizeAux` always admit at least one such order for every key +(each key is the condition set of some `imax` chain suffix of the input, whose edges put +the required `V` entries at subsets of the key, and subsumption only moves coverage to +smaller sets). + +To make the output canonical, the choice of chain must depend only on the canonical +sublevels, not on incidental map keys (which record which `imax` chains appeared +syntactically in the input). For each key we take the lexicographically least admissible +chain, computed greedily. This is well-defined: domination of `V(S', v, 0)` is monotone +in `S'`, so extending the set of conditions added so far never invalidates other elements, +and a greedy choice never needs to be revisited (checking that the remainder stays +completable before committing to each element). -/ + +/-- Is the edge contribution `V(acc ∪ {a}, a, 0)` dominated by the normal form? +True iff some `V(T, a+k)` with `T ⊆ acc ∪ {a}` is present. -/ +def NormLevel.addable (s : NormLevel) (a : Name) (acc : List Name) : Bool := + s.any fun p n => n.var.any (·.var == a) && subset compare (p.erase a) acc + +/-- Can the elements of `rem` be added to the condition set `acc` one at a time, each +addition being `addable` at that point? Since `addable` is monotone in `acc`, adding any +addable element preserves completability, so a greedy check is complete. -/ +def NormLevel.feasible (s : NormLevel) (acc rem : List Name) : Bool := + go rem.length acc rem +where + go : Nat → List Name → List Name → Bool + | 0, _, rem => rem.isEmpty + | fuel+1, acc, rem => + match rem.find? (s.addable · acc) with + | none => rem.isEmpty + | some a => go fuel ((orderedInsert Name.cmp a acc).getD acc) (rem.erase a) + +/-- The lexicographically least admissible imax chain building the condition set `p`, +listed innermost (last-added) first: at each step, remove the least element that is +`addable` on top of the rest and whose remainder is still completable. +This depends only on the sublevels of `s`, not on its key set, so equal normal forms +reify to equal levels. (The fallback returns the remaining set in sorted order; +it is not reachable for normal forms produced by `normalizeAux`.) -/ +def NormLevel.lexChain (s : NormLevel) : Nat → List Name → List Name + | 0, p => p + | fuel+1, p => + match p.find? fun a => s.addable a (p.erase a) && s.feasible [] (p.erase a) with + | some a => a :: s.lexChain fuel (p.erase a) + | none => p + +structure Tree where + const : Nat + var : List VarNode + child : List (Name × Tree) + deriving Inhabited + +def modifyAt [Inhabited α] (f : α → α) (n : Name) : List (Name × α) → List (Name × α) + | [] => [(n, f default)] + | (x, v) :: l => + match Name.cmp n x with + | .lt => (n, f default) :: (x, v) :: l + | .eq => (x, f v) :: l + | .gt => (x, v) :: modifyAt f n l + +def Tree.modify (path : List Name) (f : Tree → Tree) (t : Tree) : Tree := + match path with + | [] => f t + | a :: p => modify p (t := t) fun t => { t with child := modifyAt f a t.child } + +def NormLevel.toTree (acc : NormLevel) : Tree := + acc.foldl (init := ⟨0, [], []⟩) fun t p n => + let path := acc.lexChain p.length p + -- the edge into this tree node already contributes `V(p, v, 0)` for the innermost + -- chain element `v`, so an explicit `v+0` entry would be redundant + let var := if let v :: _ := path then subsumeVars n.var [⟨v, 0⟩] else n.var + t.modify path fun t => { t with const := n.const, var } + +/-- If the subtree behind an edge labelled `a` holds nothing but the sublevel `V(_, a, k)`, +return `k`. + +Such an edge contributes `imax (a+k) a`, which differs from the plain `a+k` only at `a = 0`, +where the plain form gives `k` instead of `0`. So the guard may be dropped, and the child +written as just `a+k`, whenever the node's constant is at least `k` — and if the constant is +*exactly* `k`, it may then be dropped itself, since `a+k ≥ k`. Without this, `u+1` would reify +to `max 1 (imax (u+1) u)` rather than to itself, and the canonical form would be roughly twice +the size of the input on typical levels. -/ +def Tree.plainOffset? (a : Name) : Tree → Option Nat + | ⟨0, [], []⟩ => some 0 + | ⟨0, [v], []⟩ => if v.var == a then some v.offset else none + | _ => none + +def Tree.reify : Tree → Level + | { const, var, child } => + let l := child.foldr (mkChild const) none + let l := var.foldr (init := l) fun n r => + some (mkMax (addOffset (.param n.var) n.offset) r) + match l with + | none => ofNat const + | some l => + if const == 0 || child.any fun c => plainOffset? c.1 c.2 == some const then l + else max (ofNat const) l +where + mkMax (l : Level) : Option Level → Level + | none => l + | some u => max l u + mkChild (const : Nat) + | (n, t), r => + match plainOffset? n t with + | some k => + if k ≤ const then mkMax (addOffset (.param n) k) r + else mkMax (imax (reify t) (.param n)) r + | none => mkMax (imax (reify t) (.param n)) r + +/-! +### Fast path for levels without an essential `imax` + +Levels arising in practice are almost always built from `zero`, `succ`, `max` and `param` +alone: `mkLevelIMax'` already discharges `imax _ 0`, `imax _ (_+1)`, `imax a a` and +`imax ≤1 _` where the kernel builds levels, and in a census of the 522k level comparisons +performed while checking Lean+Std+Batteries, 99.8% of the levels reaching them were +`imax`-free. + +Such a level's canonical form is flat: its sublevels are `C(∅, K)` and one `V({x}, x, kₓ)` +per parameter, nothing is ever subsumed (a condition set is `∅` or a singleton, and the `C` +node carries no variables), and each `imax` chain is a single edge. So the whole `NormLevel` +can be replaced by a sorted merge, and the tree read off directly. Note that every parameter +occurrence contributes its offset to the constant as well, so `K` dominates every `kₓ` and +the reified children never need their `imax` guard. +-/ + +/-- The map a run of flat data stands for: the constant at the root (absent when zero) and +`V({x}, x, k)` at each singleton key. Note this inserts the keys in sorted order, whereas +`normalizeAux` inserts them in traversal order, so the two build the same entries in +differently balanced trees — everything downstream compares maps entry by entry. -/ +def toNormLevel (c : Nat) (vs : List VarNode) : NormLevel := + vs.foldl (fun s v => s.insert [v.var] ⟨0, [v]⟩) + (if c = 0 then {} else (∅ : NormLevel).insert [] ⟨c, []⟩) + +/-- Collect the largest constant and the largest offset of each parameter, throwing the +`NormLevel` built from what has been collected so far on reaching an `imax`, so that +`normalizeAux` picks up from there rather than retraversing. -/ +def flatAux : Level → Nat → Nat × List VarNode → Except NormLevel (Nat × List VarNode) + | .zero, k, (c, vs) => .ok (Nat.max c k, vs) + | .succ l, k, acc => flatAux l (k+1) acc + | .max a b, k, acc => + match flatAux a k acc with + | .ok acc => flatAux b k acc + | .error s => .error (normalizeAux b [] k s) + | .param x, k, (c, vs) => .ok (Nat.max c k, VarNode.addVar x k vs) + | .mvar _, _, acc => .ok acc + | l@(.imax ..), k, (c, vs) => .error (normalizeAux l [] k (toNormLevel c vs)) + +/-- The tree `toTree` builds for a flat level: the constant at the root, and one child per +parameter holding `V({x}, x, kₓ)` — dropped when `kₓ = 0`, as the edge already provides it. -/ +def flatTree (c : Nat) (vs : List VarNode) : Tree := + ⟨c, [], vs.map fun v => (v.var, ⟨0, if v.offset == 0 then [] else [v], []⟩)⟩ + +end Normalize + +def normalize' (l : Level) : Level := + match Normalize.flatAux l 0 (0, []) with + | .ok (c, vs) => (Normalize.flatTree c vs).reify + | .error s => s.subsumption.toTree.reify + +/-- A transparent structural equality test for levels. Unlike `Level.beq`, +this test has no opaque runtime contract, so its successful branch can be used +directly by the verified checker. -/ +def isStructEq : Level → Level → Bool + | .zero, .zero => true + | .succ u, .succ v => isStructEq u v + | .max u₁ u₂, .max v₁ v₂ + | .imax u₁ u₂, .imax v₁ v₂ => isStructEq u₁ v₁ && isStructEq u₂ v₂ + | .param u, .param v => u == v + | .mvar ⟨u⟩, .mvar ⟨v⟩ => u == v + | _, _ => false + +/-- Core's `isEquiv` is sound but incomplete, so it can be used as a fast path: when it +accepts, the levels really are equivalent, and when it rejects we fall back to the complete +check. Over the 261k level comparisons performed while checking Lean+Std+Batteries this +filter decided every single real equivalence, leaving only the genuinely inequivalent 0.1% +to the fallback — and it is roughly 20× cheaper than normalizing. -/ +def isEquiv' (u v : Level) : Bool := + isEquiv u v || Normalize.normalize u == Normalize.normalize v + +def isEquivList : List Level → List Level → Bool := List.all2 isEquiv' + +/-- Core's `geq` as a fast path, on the same grounds as `isEquiv'`. -/ +def geq' (u v : Level) : Bool := + geq u v || (Normalize.normalize v).le (Normalize.normalize u) + +-- local elab "normalize " l:level : command => do +-- Elab.Command.runTermElabM fun _ => do +-- logInfo m!"{normalize' (← Elab.Term.elabLevel l)}" +-- -- logInfo m!"{repr <| Normalize.normalize (← Elab.Term.elabLevel l) }" + +-- local elab "normalize " l:level " ≤ " l':level : command => do +-- Elab.Command.runTermElabM fun _ => do +-- logInfo m!"{geq' (← Elab.Term.elabLevel l') (← Elab.Term.elabLevel l)}" +-- -- logInfo m!"{repr <| Normalize.normalize (← Elab.Term.elabLevel l)}" +-- -- logInfo m!"{repr <| Normalize.normalize (← Elab.Term.elabLevel l')}" + +-- universe u v w +-- /-- info: max 1 u -/ +-- #guard_msgs (whitespace := lax) in normalize max u 1 +-- /-- info: u -/ +-- #guard_msgs (whitespace := lax) in normalize imax 1 u +-- /-- info: max 1 (imax (u + 1) u) -/ +-- #guard_msgs (whitespace := lax) in normalize u+1 +-- /-- info: imax 2 u -/ +-- #guard_msgs (whitespace := lax) in normalize imax 2 u +-- /-- info: max v (imax (imax u v) w) -/ +-- #guard_msgs (whitespace := lax) in normalize max w (imax (imax u w) v) +-- /-- info: max v (imax (imax u v) w) -/ +-- #guard_msgs (whitespace := lax) in normalize max (imax (imax u v) w) (imax (imax u w) v) +-- /-- info: u -/ +-- #guard_msgs (whitespace := lax) in normalize imax u u +-- /-- info: max 1 (imax (u + 1) u) -/ +-- #guard_msgs (whitespace := lax) in normalize imax u (u+1) +-- /-- info: max 1 (imax (max (v + 1) (imax (u + 1) u)) v) -/ +-- #guard_msgs (whitespace := lax) in normalize imax u v + 1 diff --git a/Ix/Theory/Named/Reference/List.lean b/Ix/Theory/Named/Reference/List.lean new file mode 100644 index 000000000..42447a15f --- /dev/null +++ b/Ix/Theory/Named/Reference/List.lean @@ -0,0 +1,11 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + + +@[specialize] def List.all2 (R : α → α → Bool) : List α → List α → Bool + | a1 :: as1, a2 :: as2 => R a1 a2 && all2 R as1 as2 + | [], [] => true + | _, _ => false diff --git a/Ix/Theory/Named/Reference/LocalContext.lean b/Ix/Theory/Named/Reference/LocalContext.lean new file mode 100644 index 000000000..7417de5e6 --- /dev/null +++ b/Ix/Theory/Named/Reference/LocalContext.lean @@ -0,0 +1,27 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Lean.LocalContext + +namespace Ix.Theory.Named +open Lean + +class MonadLocalNameGenerator (m : Type u → Type v) where + withFreshId : (Name → m α) → m α +export MonadLocalNameGenerator (withFreshId) + +instance [MonadLocalNameGenerator m] : MonadLocalNameGenerator (ReaderT ρ m) where + withFreshId f c := withFreshId (f · c) + +@[inline] def withLocalDecl [Monad m] [MonadLocalNameGenerator m] [MonadWithReaderOf LocalContext m] + (name : Name) (bi : BinderInfo) (ty : Expr) (k : Expr → m α) : m α := + withFreshId fun id => do + withReader (·.mkLocalDecl ⟨id⟩ name ty bi) <| k <| .fvar ⟨id⟩ + +@[inline] def withLetDecl [Monad m] [MonadLocalNameGenerator m] [MonadWithReaderOf LocalContext m] + (name : Name) (ty val : Expr) (k : Expr → m α) : m α := + withFreshId fun id => do + withReader (·.mkLetDecl ⟨id⟩ name ty val) <| k <| .fvar ⟨id⟩ diff --git a/Ix/Theory/Named/Reference/Primitive.lean b/Ix/Theory/Named/Reference/Primitive.lean new file mode 100644 index 000000000..bfed975dd --- /dev/null +++ b/Ix/Theory/Named/Reference/Primitive.lean @@ -0,0 +1,498 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Reference.TypeChecker +import Ix.Theory.Named.Reference.Environment.Basic + +namespace Ix.Theory.Named +namespace Environment +open Lean hiding Environment Exception +open Kernel TypeChecker + +deriving instance ToExpr for LevelMVarId +deriving instance ToExpr for Level +deriving instance ToExpr for MVarId +deriving instance ToExpr for BinderInfo +deriving instance ToExpr for String.Pos.Raw +deriving instance ToExpr for Substring.Raw +deriving instance ToExpr for SourceInfo +deriving instance ToExpr for Syntax +deriving instance ToExpr for DataValue +deriving instance ToExpr for KVMap +deriving instance ToExpr for Expr + +elab (name := microQq) "q(" e:term ")" : term => + return toExpr (← instantiateMVars (← Elab.Term.elabTerm e none)) + +structure Reflection where + type : Expr + ofTrue : Expr + ofFalse : Expr + toDec : Expr + +def Reflection.defn₁ : Reflection where + type := q(fun p b => ∀ {q : Prop}, ((b = true → p) → (¬b = true → ¬p) → q) → q) + ofTrue := q(fun p (H : ∀ {q : Prop}, ((true = true → p) → (¬true = true → ¬p) → q) → q) => + H fun h _ => h rfl) + ofFalse := q(fun p (H : ∀ {q : Prop}, ((false = true → p) → (¬false = true → ¬p) → q) → q) => + H fun _ h => h Bool.noConfusion) + toDec := q(fun p b (H : ∀ {q : Prop}, ((b = true → p) → (¬b = true → ¬p) → q) → q) => + if h : b = true then isTrue (H fun h' _ => h' h) else isFalse (H fun _ h' => h' h)) + +def Reflection.defn₂ : Reflection where + type := q(fun p b => ∀ {q : Prop}, ((b = true → p) → (b = false → ¬p) → q) → q) + ofTrue := q(fun p (H : ∀ {q : Prop}, ((true = true → p) → (true = false → ¬p) → q) → q) => + H fun h _ => h rfl) + ofFalse := q(fun p (H : ∀ {q : Prop}, ((false = true → p) → (false = false → ¬p) → q) → q) => + H fun _ h => h rfl) + toDec := q(fun p b (H : ∀ {q : Prop}, ((b = true → p) → (b = false → ¬p) → q) → q) => + b.casesOn (motive := fun b' => b = b' → Decidable p) + (fun h => isFalse (H fun _ h' => h' h)) (fun h => isTrue (H fun h' _ => h' h)) rfl) + +def Reflection.check (r : Reflection) (fail : ∀ {α}, M α) : M Unit := do + unless ← isDefEq (← checkType r.type) q(Prop → Bool → Prop) do fail + +inductive ConditionImpl where + | bool + | reflectNatNat (asBool : Expr) (reflect : Reflection) (proof : Expr) + +structure Condition where + prop : Expr + dec : Expr + impl : ConditionImpl + +def Condition.natLE : Condition where + prop := q(@LE.le Nat _) + dec := q(Nat.decLe) + impl := .reflectNatNat + (asBool := q(Nat.ble)) + (reflect := .defn₁) + (proof := q(fun n m {q : Prop} (H : _ → _ → q) => + H (@Nat.le_of_ble_eq_true n m) (@Nat.not_le_of_not_ble_eq_true n m))) + +def Condition.natEq : Condition where + prop := q(@Eq Nat) + dec := q(Nat.decEq) + impl := .reflectNatNat + (asBool := q(Nat.beq)) + (reflect := .defn₂) + (proof := q(fun n m {q : Prop} (H : _ → _ → q) => + H (@Nat.eq_of_beq_eq_true n m) (@Nat.ne_of_beq_eq_false n m))) + +def Condition.bool : Condition where + prop := q(fun x : Bool => x = true) + dec := q(fun x => Bool.decEq x true) + impl := .bool + +def Reflection.ite (r : Reflection) : Expr := + .lam0 q(Prop) <| .lam0 q(Bool) <| .lam0 (mkApp2 r.type (.bvar 1) (.bvar 0)) <| + .lam0 q(Type) <| mkApp3 q(@_root_.ite.{1}) (.bvar 0) (.bvar 3) + (mkApp3 r.toDec (.bvar 3) (.bvar 2) (.bvar 1)) + +def Reflection.natDITE (r : Reflection) : Expr := + .lam0 q(Prop) <| .lam0 q(Bool) <| .lam0 (mkApp2 r.type (.bvar 1) (.bvar 0)) <| + mkApp2 q(@dite Nat) (.bvar 2) (mkApp3 r.toDec (.bvar 2) (.bvar 1) (.bvar 0)) + +def Reflection.checkITE (r : Reflection) (fail : ∀ {α}, M α) : M Unit := do + unless ← isDefEq (← checkType r.ite) (.arrow q(Prop) <| .arrow q(Bool) <| + .arrow (mkApp2 r.type (.bvar 1) (.bvar 0)) q(∀ α : Type, α → α → α)) do fail + withLocalDecl `p .default q(Prop) fun p => do + withLocalDecl `H .default (mkApp2 r.type p q(true)) fun H => do + unless ← isDefEq (mkApp3 r.ite p q(true) H) q(fun α : Type => fun a _ : α => a) do fail + withLocalDecl `H .default (mkApp2 r.type p q(false)) fun H => do + unless ← isDefEq (mkApp3 r.ite p q(false) H) q(fun α : Type => fun _ a : α => a) do fail + +def Reflection.checkNatDITE (r : Reflection) (fail : ∀ {α}, M α) : M Unit := do + unless ← isDefEq (← checkType q(Not)) q(Prop → Prop) do fail + unless ← isDefEq (← checkType r.natDITE) (.arrow q(Prop) <| .arrow q(Bool) <| + .arrow (mkApp2 r.type (.bvar 1) (.bvar 0)) <| + .arrow (.arrow (.bvar 2) q(Nat)) <| .arrow (.arrow (mkApp q(Not) (.bvar 3)) q(Nat)) <| + q(Nat)) do fail + unless ← isDefEq (← checkType r.ofTrue) (.arrow q(Prop) <| + .arrow (mkApp2 r.type (.bvar 0) q(true)) (.bvar 1)) do fail + unless ← isDefEq (← checkType r.ofFalse) (.arrow q(Prop) <| + .arrow (mkApp2 r.type (.bvar 0) q(false)) (mkApp q(Not) (.bvar 1))) do fail + withLocalDecl `p .default q(Prop) fun p => do + withLocalDecl `a .default (.arrow p q(Nat)) fun a => do + withLocalDecl `b .default (.arrow (mkApp q(Not) p) q(Nat)) fun b => do + withLocalDecl `H .default (mkApp2 r.type p q(true)) fun H => do + unless ← isDefEq (mkApp5 r.natDITE p q(true) H a b) (mkApp a (mkApp2 r.ofTrue p H)) do fail + withLocalDecl `H .default (mkApp2 r.type p q(false)) fun H => do + unless ← isDefEq (mkApp5 r.natDITE p q(false) H a b) (mkApp b (mkApp2 r.ofFalse p H)) do fail + +def Condition.check (cond : Condition) (fail : ∀ {α}, M α) + (ite := false) (dite := false) : M Unit := do + _ ← checkType cond.dec + match cond.impl with + | .reflectNatNat asBool reflect proof => + unless ← isDefEq (← inferType cond.prop) q(Nat → Nat → Prop) do fail + reflect.check fail + if ite then reflect.checkITE fail + if dite then reflect.checkNatDITE fail + let y := .bvar 0; let x := .bvar 1 + let e := .lam0 q(Nat) <| .lam0 q(Nat) <| mkApp3 reflect.toDec + (mkApp2 cond.prop x y) (mkApp2 asBool x y) (mkApp2 proof x y) + _ ← checkType e + unless ← isDefEq (← inferType asBool) q(Nat → Nat → Bool) do fail + unless ← isProp (← inferType proof) do fail + unless ← isDefEq e cond.dec do fail + | .bool => + unless ← isDefEq (← inferType cond.prop) q(Bool → Prop) do fail + let b := .bvar 0 + if ite then + let natITE := .lam0 q(Bool) <| + mkApp2 q(@_root_.ite Nat) (mkApp cond.prop b) (mkApp cond.dec b) + unless ← isDefEq (← checkType natITE) q(Bool → Nat → Nat → Nat) do fail + unless ← isDefEq (mkApp natITE q(true)) q(fun a _ : Nat => a) do fail + unless ← isDefEq (mkApp natITE q(false)) q(fun _ a : Nat => a) do fail + if dite then throw <| .other "unsupported" + +protected def Condition.ite (cond : Condition) (α : Expr) (args : Array Expr) (t e : Expr) : Expr := + mkApp5 q(@ite.{1}) α (mkAppN cond.prop args) (mkAppN cond.dec args) t e + +protected def Condition.dite (cond : Condition) (args: Array Expr) (t e : Expr) : Expr := + mkApp4 q(@dite Nat) (mkAppN cond.prop args) (mkAppN cond.dec args) + (.lam0 (mkAppN cond.prop args) t) + (.lam0 (mkApp q(Not) (mkAppN cond.prop args)) e) + +protected def Condition.decide (cond : Condition) (args : Array Expr) : Expr := + cond.ite q(Bool) args q(true) q(false) + +def unfoldWellFounded (e : Expr) (fvs : Array Expr) (eq_def : Expr) (fail : ∀ {α}, M α) : M Expr := do + let .app (.app _ lhs) rhs := eq_def.getForallBody.instantiateRev fvs | fail + let orig := lhs.getAppFn + let rhs := rhs.replace fun e' => if e' == orig then some e else none + let .app e1 wfn ← whnf (mkAppN e fvs) | fail + e1.withApp fun accRec args => do + let #[α,r,_,_,n] := args | fail + let .const ``Acc.rec [_, u] := accRec | fail + let .app wf _ := wfn | fail + let L := .lam0 α <| .lam0 (mkApp2 r (.bvar 0) n) (mkApp wf (.bvar 1)) + let wfn' := mkApp4 (.const ``Acc.intro [u]) α r n L + let p ← inferType wfn + unless ← isProp p do fail + unless ← isDefEq p (← checkType wfn') do fail + _ ← checkType rhs + unless ← isDefEq (e1.app wfn') rhs do fail + return (← getLCtx).mkLambda fvs rhs + +def lambdaTelescope (e : Expr) (k : Array Expr → Expr → M α) : M α := loop #[] e where + loop fvars + | .lam x dom body bi => + let d := dom.instantiateRev fvars + withLocalDecl x bi d fun fv => do + let fvars := fvars.push fv + loop fvars body + | e => k fvars (e.instantiateRev fvars) + +def forallTelescope (e : Expr) (k : Array Expr → Expr → M α) : M α := loop #[] e where + loop fvars + | .forallE x dom body bi => + let d := dom.instantiateRev fvars + withLocalDecl x bi d fun fv => do + let fvars := fvars.push fv + loop fvars body + | e => k fvars (e.instantiateRev fvars) + +def unfoldNatWellFounded (e : Expr) (fvs : Array Expr) (eq_def : Expr) (fail : ∀ {α}, M α) : M Expr := do + let succ := mkApp q(Nat.succ) + let defeq1 a b := isDefEq (.arrow q(Nat) a) (.arrow q(Nat) b) + let x := .bvar 0 + let .app (.app _ lhs) rhs := eq_def.getForallBody.instantiateRev fvs | fail + let orig := lhs.getAppFn + let rhs := rhs.replace fun e' => if e' == orig then some e else none + let e1 ← whnfCore (mkAppN e fvs) -- get _unary + let e1 ← unfoldDefinition e1 -- get fix + (← whnfCore e1).withApp fun fix args => do + let .const ``WellFounded.Nat.fix [_, _] := fix | fail + let #[α,motive,f,F,a₀] := args | fail + let fixFn := mkAppN fix #[α,motive,f,F] + withLocalDecl `a .default (← inferType a₀) fun a => do + -- prove |- fix α motive f F a ≡ go α motive f F (eager (f a)) a [proof] + let e1 ← unfoldDefinition (.app fixFn a) -- get fix.go + let e1 ← whnfCore e1 + e1.withApp fun fixGo args => do + let #[α',motive',f',F',fuel,a',_] := args | fail + unless (α, motive, f, F, a) == (α', motive', f', F', a') do fail + let .app eager n := fuel | fail + unless ← isDefEq n (succ (.app f a)) do fail + -- prove |- eager n = if beq n n = true then n else n + unless (← getEnv).contains ``Nat.beq do fail + let c := Condition.bool; c.check (fail) (ite := true) + unless ← defeq1 (mkApp eager x) (c.ite q(Nat) #[mkApp2 (.const ``Nat.beq []) x x] x x) do fail + -- prove |- go α motive f F (succ t) x hfuel ≡ F x fun y hy => go α motive f F t y [proof] + let go' ← unfoldDefinition fixGo -- get fix + lambdaTelescope go' fun fvs go' => do + let #[_,_,_,F,t] := fvs | fail + let .app natRec t' := go' | fail + unless !natRec.containsFVar t.fvarId! && t == t' do fail + _ ← checkType (succ t) + let gor ← whnfCore (.app natRec (succ t)) + lambdaTelescope gor fun fvs gor => do + let #[x,_] := fvs | fail + let .app Fx ih := gor | fail + unless .app F x == Fx do fail + lambdaTelescope ih fun fvs ih => do + let #[y,_] := fvs | fail + let .app ih _ := ih | fail + unless ih == .app (.app natRec t) y do fail + -- prove |- rhs ≡ F x fun y _ => fix α motive f F y + let .forallE _ dom _ _ ← inferType (.app F a₀) | fail + let ih' ← forallTelescope dom fun fvs _ => do + let #[y,_] := fvs | fail + return (← getLCtx).mkLambda fvs (.app fixFn y) + have rhs' := mkApp2 F a₀ ih' + _ ← checkType rhs' + unless ← isDefEq rhs rhs' do fail + return (← getLCtx).mkLambda fvs rhs + +def checkPrimitiveDef (v : DefinitionVal) : M Bool := do + unless v.safety == .safe do return false + let fail {α} : M α := throw <| .other s!"invalid form for primitive def {v.name}" + let tru := q(true) + let fal := q(false) + let zero := q(Nat.zero) + let succ := mkApp q(Nat.succ) + let pred := mkApp q(Nat.pred) + let add := mkApp2 q(Nat.add) + let sub := mkApp2 q(Nat.sub) + let mul := mkApp2 q(Nat.mul) + let mod := mkApp2 q(Nat.mod) + let div := mkApp2 q(Nat.div) + let one := succ zero + let two := succ one + let defeq1 a b := isDefEq (.arrow q(Nat) a) (.arrow q(Nat) b) + let defeq2 a b := defeq1 (.arrow q(Nat) a) (.arrow q(Nat) b) + let x := .bvar 0 + let y := .bvar 1 + let env ← getEnv + match v.name with + | ``Nat.add => + unless env.contains ``Nat && v.levelParams.isEmpty do fail + -- add : Nat → Nat → Nat + unless ← isDefEq v.type q(Nat → Nat → Nat) do fail + let add := mkApp2 v.value + -- add x 0 ≡ x + unless ← defeq1 (add x zero) x do fail + -- add y (succ x) ≡ succ (add y x) + unless ← defeq2 (add y (succ x)) (succ (add y x)) do fail + | ``Nat.pred => + unless env.contains ``Nat && v.levelParams.isEmpty do fail + -- pred : Nat → Nat + unless ← isDefEq v.type q(Nat → Nat) do fail + let pred := mkApp v.value + unless ← isDefEq (pred zero) zero do fail + unless ← defeq1 (pred (succ x)) x do fail + | ``Nat.sub => + unless env.contains ``Nat.pred && v.levelParams.isEmpty do fail + -- sub : Nat → Nat → Nat + unless ← isDefEq v.type q(Nat → Nat → Nat) do fail + let sub := mkApp2 v.value + unless ← defeq1 (sub x zero) x do fail + unless ← defeq2 (sub y (succ x)) (pred (sub y x)) do fail + | ``Nat.mul => + unless env.contains ``Nat.add && v.levelParams.isEmpty do fail + -- mul : Nat → Nat → Nat + unless ← isDefEq v.type q(Nat → Nat → Nat) do fail + let mul := mkApp2 v.value + unless ← defeq1 (mul x zero) zero do fail + unless ← defeq2 (mul y (succ x)) (add (mul y x) y) do fail + | ``Nat.pow => + unless env.contains ``Nat.mul && v.levelParams.isEmpty do fail + -- pow : Nat → Nat → Nat + unless ← isDefEq v.type q(Nat → Nat → Nat) do fail + let pow := mkApp2 v.value + unless ← defeq1 (pow x zero) one do fail + unless ← defeq2 (pow y (succ x)) (mul (pow y x) y) do fail + | ``Nat.mod => + unless env.contains ``Nat.sub && env.contains ``Bool && v.levelParams.isEmpty do fail + -- mod : Nat → Nat → Nat + unless ← isDefEq v.type q(Nat → Nat → Nat) do fail + let mod := mkApp2 v.value + unless ← defeq1 (mod zero x) zero do fail + unless ← isDefEq (← checkType q(@LE.le Nat _)) q(Nat → Nat → Prop) do fail + let le := mkApp2 q(@LE.le Nat _) + unless ← isDefEq (← checkType q(Nat.modCore.go)) + q(∀ n, Nat.succ Nat.zero ≤ n → ∀ fuel x : Nat, Nat.succ x ≤ fuel → Nat) do fail + let go := mkApp5 q(Nat.modCore.go) + let c := Condition.natLE; c.check fail (ite := true) (dite := true) + withLocalDecl `x .default q(Nat) fun x => do + withLocalDecl `y .default q(Nat) fun y => do + let sx := succ x + let e := c.ite q(Nat) #[y, sx] (c.dite #[one, y] + (go y (.bvar 0) (succ sx) sx (mkApp q(Nat.lt_succ_self) sx)) sx) sx + _ ← checkType e + unless ← isDefEq (mod sx y) e do fail + withLocalDecl `hy .default (le one y) fun hy => do + withLocalDecl `fuel .default q(Nat) fun fuel => do + withLocalDecl `h .default (le (succ x) (succ fuel)) fun h => do + let e := c.dite #[y, x] (go y hy fuel (sub x y) + (mkApp6 q(@Nat.div_rec_fuel_lemma) x y fuel hy (.bvar 0) h)) x + _ ← checkType e + unless ← isDefEq (go y hy (succ fuel) x h) e do fail + | ``Nat.div => + unless env.contains ``Nat.sub && env.contains ``Bool && v.levelParams.isEmpty do fail + -- div : Nat → Nat → Nat + unless ← isDefEq v.type q(Nat → Nat → Nat) do fail + let div := mkApp2 v.value + let c := Condition.natLE; c.check fail (dite := true) + unless ← isDefEq (← checkType q(@LE.le Nat _)) q(Nat → Nat → Prop) do fail + let le := mkApp2 q(@LE.le Nat _) + unless ← isDefEq (← checkType q(Nat.div.go)) + q(∀ y, Nat.succ Nat.zero ≤ y → ∀ fuel x : Nat, Nat.succ x ≤ fuel → Nat) do fail + let go := mkApp5 q(Nat.div.go) + withLocalDecl `x .default q(Nat) fun x => do + withLocalDecl `y .default q(Nat) fun y => do + let e := c.dite #[one, y] (go y (.bvar 0) (succ x) x (mkApp q(Nat.lt_succ_self) x)) zero + _ ← checkType e + unless ← isDefEq (div x y) e do fail + withLocalDecl `hy .default (le one y) fun hy => do + withLocalDecl `fuel .default q(Nat) fun fuel => do + withLocalDecl `h .default (le (succ x) (succ fuel)) fun h => do + let e := c.dite #[y, x] (succ (go y hy fuel (sub x y) + (mkApp6 q(@Nat.div_rec_fuel_lemma) x y fuel hy (.bvar 0) h))) zero + _ ← checkType e + unless ← isDefEq (go y hy (succ fuel) x h) e do fail + | ``Nat.gcd => + unless env.contains ``Nat.mod && v.levelParams.isEmpty do fail + -- gcd : Nat → Nat → Nat + unless ← isDefEq v.type q(Nat → Nat → Nat) do fail + withLocalDecl `m .default q(Nat) fun m => do + withLocalDecl `n .default q(Nat) fun n => do + let gcd' ← unfoldNatWellFounded v.value #[m, n] q(type_of% Nat.gcd.eq_def) fail + let gcd' := mkApp2 gcd' + let gcd := mkApp2 v.value + unless ← isDefEq (gcd' zero m) m do fail + unless ← isDefEq (gcd' (succ n) m) (gcd (mod m (succ n)) (succ n)) do fail + | ``Nat.beq => + unless env.contains ``Nat && env.contains ``Bool && v.levelParams.isEmpty do fail + -- beq : Nat → Nat → Bool + unless ← isDefEq v.type q(Nat → Nat → Bool) do fail + let beq := mkApp2 v.value + unless ← isDefEq (beq zero zero) tru do fail + unless ← defeq1 (beq zero (succ x)) fal do fail + unless ← defeq1 (beq (succ x) zero) fal do fail + unless ← defeq2 (beq (succ y) (succ x)) (beq y x) do fail + | ``Nat.ble => + unless env.contains ``Nat && env.contains ``Bool && v.levelParams.isEmpty do fail + -- ble : Nat → Nat → Bool + unless ← isDefEq v.type q(Nat → Nat → Bool) do fail + let ble := mkApp2 v.value + unless ← isDefEq (ble zero zero) tru do fail + unless ← defeq1 (ble zero (succ x)) tru do fail + unless ← defeq1 (ble (succ x) zero) fal do fail + unless ← defeq2 (ble (succ y) (succ x)) (ble y x) do fail + | ``Nat.bitwise => + unless env.contains ``Nat && env.contains ``Bool && v.levelParams.isEmpty do fail + -- bitwise : Nat → Nat → Nat + unless ← isDefEq v.type q((Bool → Bool → Bool) → Nat → Nat → Nat) do fail + withLocalDecl `f .default q(Bool → Bool → Bool) fun f => do + withLocalDecl `n .default q(Nat) fun n => do + withLocalDecl `m .default q(Nat) fun m => do + let bitwise' ← unfoldNatWellFounded v.value #[f, n, m] q(type_of% Nat.bitwise.eq_def) fail + let bitwise := mkApp3 v.value + let c := Condition.natEq; c.check fail (ite := true) + let bc := Condition.bool; bc.check fail (ite := true) + let e := + c.ite q(Nat) #[n, zero] (bc.ite q(Nat) #[mkApp2 f q(false) q(true)] m zero) <| + c.ite q(Nat) #[m, zero] (bc.ite q(Nat) #[mkApp2 f q(true) q(false)] n zero) <| + let n' := div n two + let m' := div m two + let b₁ := c.decide #[mod n two, one] + let b₂ := c.decide #[mod m two, one] + let r := bitwise f n' m' + bc.ite q(Nat) #[mkApp2 f b₁ b₂] (add (add r r) one) (add r r) + _ ← checkType e + unless ← isDefEq (mkApp3 bitwise' f n m) e do fail + | ``Nat.land => + unless env.contains ``Nat.bitwise && v.levelParams.isEmpty do fail + -- land : Nat → Nat → Nat + unless ← isDefEq v.type q(Nat → Nat → Nat) do fail + let .app (.const ``Nat.bitwise []) and := v.value | fail + let and := mkApp2 and + unless ← defeq1 (and fal x) fal do fail + unless ← defeq1 (and tru x) x do fail + | ``Nat.lor => + unless env.contains ``Nat.bitwise && v.levelParams.isEmpty do fail + -- lor : Nat → Nat → Nat + unless ← isDefEq v.type q(Nat → Nat → Nat) do fail + let .app (.const ``Nat.bitwise []) or := v.value | fail + let or := mkApp2 or + unless ← defeq1 (or fal x) x do fail + unless ← defeq1 (or tru x) tru do fail + | ``Nat.xor => + unless env.contains ``Nat.bitwise && v.levelParams.isEmpty do fail + -- xor : Nat → Nat → Nat + unless ← isDefEq v.type q(Nat → Nat → Nat) do fail + let .app (.const ``Nat.bitwise []) xor := v.value | fail + let xor := mkApp2 xor + unless ← isDefEq (xor fal fal) fal do fail + unless ← isDefEq (xor tru fal) tru do fail + unless ← isDefEq (xor fal tru) tru do fail + unless ← isDefEq (xor tru tru) fal do fail + | ``Nat.shiftLeft => + unless env.contains ``Nat.mul && v.levelParams.isEmpty do fail + -- shiftLeft : Nat → Nat → Nat + unless ← isDefEq v.type q(Nat → Nat → Nat) do fail + let shl := mkApp2 v.value + unless ← defeq1 (shl x zero) x do fail + unless ← defeq2 (shl x (succ y)) (shl (mul two x) y) do fail + | ``Nat.shiftRight => + unless env.contains ``Nat.div && v.levelParams.isEmpty do fail + -- shiftRight : Nat → Nat → Nat + unless ← isDefEq v.type q(Nat → Nat → Nat) do fail + let shr := mkApp2 v.value + unless ← defeq1 (shr x zero) x do fail + unless ← defeq2 (shr x (succ y)) (div (shr x y) two) do fail + | ``Char.ofNat => + unless env.contains ``Nat && v.levelParams.isEmpty do fail + -- Char : Type + _ ← ensureType q(Char) + -- @Char.ofNat : Nat → Char + unless ← isDefEq v.type q(Nat → Char) do fail + | ``String.ofList => + unless v.levelParams.isEmpty do fail + -- Char : Type + _ ← ensureType q(Char) + -- List Char : Type + _ ← ensureType q(List Char) + -- @List.nil.{0} Char : List Char + unless ← isDefEq (← checkType q(List.nil (α := Char))) q(List Char) do fail + -- @List.cons.{0} Char : Char → List Char → List Char + unless ← isDefEq (← checkType q(List.cons (α := Char))) q(Char → List Char → List Char) do fail + -- String.ofList : List Char → String + unless ← isDefEq v.type q(List Char → String) do fail + | _ => return false + return true + +def checkPrimitiveInductive (_env : Environment) (lparams : List Name) (nparams : Nat) + (types : List InductiveType) (isUnsafe : Bool) : Except Exception Bool := do + unless !isUnsafe && lparams.isEmpty && nparams == 0 do return false + let [type] := types | return false + unless type.type == .sort (.succ .zero) do return false + let fail {α} : Except Exception α := + throw <| .other s!"invalid form for primitive inductive {type.name}" + match type.name with + | ``Bool => + let [⟨``Bool.false, .const ``Bool []⟩, ⟨``Bool.true, .const ``Bool []⟩] := type.ctors | fail + | ``Nat => + let [ + ⟨``Nat.zero, .const ``Nat []⟩, + ⟨``Nat.succ, .forallE _ (.const ``Nat []) (.const ``Nat []) _⟩ + ] := type.ctors | fail + | _ => return false + return true + +-- Self-test to ensure that the primitives check at compile time +run_meta + let env ← Lean.getEnv + for c in Environment.primitives do + match env.find? c with + | some (.defnInfo v) => + let (.true, _) ← Elab.Term.TermElabM.run (checkPrimitiveDef { v with }) + | throwError "{v.name}" + | some (.inductInfo _) | some (.ctorInfo _) => pure () + | r => throwError "unexpected primitive: {r.map (·.name)}" diff --git a/Ix/Theory/Named/Reference/PtrEq.lean b/Ix/Theory/Named/Reference/PtrEq.lean new file mode 100644 index 000000000..36f715bbd --- /dev/null +++ b/Ix/Theory/Named/Reference/PtrEq.lean @@ -0,0 +1,28 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Lean.Declaration + +namespace Ix.Theory.Named +open Lean + +/-- +Pointer equality is not a safe function, but the kernel uses it anyway for some fast paths. +We cannot use `withPtrEq` because it is not a mere optimization before a true equality test - +the kernel actually has different behavior (will reject some inputs that it would not otherwise) +if identical objects spontaneously get different addresses. + +We use this function to try to isolate uses of pointer equality in the kernel, +and type-restrict it to avoid thorny questions about equality of closures or the like. +-/ +opaque ptrEqExpr (a b : Expr) : Bool := unsafe ptrAddrUnsafe a == ptrAddrUnsafe b + +axiom ptrEqExpr_eq : ptrEqExpr a b → a = b + +/-- See `ptrEqExpr`. -/ +opaque ptrEqConstantInfo (a b : ConstantInfo) : Bool := unsafe ptrAddrUnsafe a == ptrAddrUnsafe b + +axiom ptrEqConstantInfo_eq : ptrEqConstantInfo a b → a = b diff --git a/Ix/Theory/Named/Reference/Quot.lean b/Ix/Theory/Named/Reference/Quot.lean new file mode 100644 index 000000000..15f320647 --- /dev/null +++ b/Ix/Theory/Named/Reference/Quot.lean @@ -0,0 +1,126 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Batteries.Tactic.OpenPrivate +import Ix.Theory.Named.Reference.Environment.Basic +import Ix.Theory.Named.Reference.Expr +import Ix.Theory.Named.Reference.Instantiate +import Ix.Theory.Named.Reference.LocalContext + +namespace Ix.Theory.Named +open Lean hiding Environment Exception +open Kernel + +open private Lean.Kernel.Environment.add markQuotInit from Lean.Environment + +abbrev ExprBuildT (m) := ReaderT LocalContext <| ReaderT NameGenerator m + +def ExprBuildT.run [Monad m] (x : ExprBuildT m α) : m α := x {} {} + +instance : MonadLocalNameGenerator (ExprBuildT m) where + withFreshId x c ngen := x ngen.curr c ngen.next + +def checkEqType (env : Environment) : Except Exception Unit := do + let fail {α} (s : String) : Except Exception α := + throw <| .other s!"failed to initialize quot module, {s}" + let .inductInfo info ← env.get ``Eq | fail "environment does not have 'Eq' type" + let [u] := info.levelParams | fail "unexpected number of universe params at 'Eq' type" + let [eqRefl] := info.ctors | fail "unexpected number of constructors for 'Eq' type" + ExprBuildT.run do + withLocalDecl `α .implicit (.sort (.param u)) fun α => do + if info.type != ((← read).mkForall #[α] <| .arrow α <| .arrow α .prop) then + fail "'Eq' has an expected type" + let info ← env.get eqRefl + let [u] := info.levelParams + | fail "unexpected number of universe params at 'Eq' type constructor" + withLocalDecl `α .implicit (.sort (.param u)) fun α => do + withLocalDecl `a .default α fun a => do + if info.type != ((← read).mkForall #[α, a] <| mkApp3 (.const ``Eq [.param u]) α a a) then + fail "unexpected type for 'Eq' type constructor" + +def Environment.addQuot (env : Environment) : Except Exception Environment := do + if env.quotInit then return env + checkEqType env + env.checkName ``Quot + env.checkName ``Quot.mk + env.checkName ``Quot.lift + env.checkName ``Quot.ind + ExprBuildT.run do + let u := .param `u + withLocalDecl `α .implicit (.sort u) fun α => do + let env ← withLocalDecl `r .default (.arrow α (.arrow α .prop)) fun r => do + -- constant Quot.{u} {α : Sort u} (r : α → α → Prop) : Sort u + let env := env.add <| .quotInfo { + name := ``Quot, kind := .type, levelParams := [`u] + type := (← read).mkForall #[α, r] <| .sort u + } + withLocalDecl `a .default α fun a => do + -- constant Quot.mk.{u} {α : Sort u} (r : α → α → Prop) (a : α) : @Quot.{u} α r + return env.add <| .quotInfo { + name := ``Quot.mk, kind := .ctor, levelParams := [`u] + type := (← read).mkForall #[α, r, a] <| mkApp2 (.const ``Quot [u]) α r + } + withLocalDecl `r .implicit (.arrow α (.arrow α .prop)) fun r => do + let quot_r := mkApp2 (.const ``Quot [u]) α r + withLocalDecl `a .default α fun a => do + let v := .param `v + let env ← withLocalDecl `β .implicit (.sort v) fun β => do + withLocalDecl `f .default (.arrow α β) fun f => do + withLocalDecl `b .default α fun b => do + let rab := mkApp2 r a b + let fa_eq_fb := mkApp3 (.const ``Eq [v]) β (.app f a) (.app f b) + let sanity := (← read).mkForall #[a, b] <| .arrow rab fa_eq_fb + -- constant Quot.lift.{u, v} {α : Sort u} {r : α → α → Prop} {β : Sort v} (f : α → β) : + -- (∀ a b : α, r a b → f a = f b) → @Quot.{u} α r → β + return env.add <| .quotInfo { + name := ``Quot.lift, kind := .lift, levelParams := [`u, `v] + type := (← read).mkForall #[α, r, β, f] <| .arrow sanity <| .arrow quot_r β + } + let quotMk_a := mkApp3 (.const ``Quot.mk [u]) α r a + withLocalDecl `β .implicit (.arrow quot_r .prop) fun β => do + let all_quot := (← read).mkForall #[a] <| .app β quotMk_a + withLocalDecl `q .implicit quot_r fun q => do + -- constant Quot.ind.{u} {α : Sort u} {r : α → α → Prop} {β : @Quot.{u} α r → Prop} : + -- (∀ a : α, β (@Quot.mk.{u} α r a)) → ∀ q : @Quot.{u} α r, β q + let env := env.add <| .quotInfo { + name := ``Quot.ind, kind := .ind, levelParams := [`u] + type := (← read).mkForall #[α, r, β] <| + .forallE `mk all_quot ((← read).mkForall #[q] <| .app β q) .default + } + return markQuotInit env + +/-- Reduces the head application of a quotient eliminator as follows: + +``` +Quot.lift.{u, v} {α : Sort u} {r : α → α → Prop} {β : Sort v} (f : α → β) : + (∀ a b : α, r a b → f a = f b) → @Quot.{u} α r → β + +Quot.lift f h (Quot.mk r a) ... ⟶ f a ... +``` + +``` +Quot.ind.{u} {α : Sort u} {r : α → α → Prop} {β : @Quot.{u} α r → Prop} : + (∀ a : α, β (@Quot.mk.{u} α r a)) → ∀ q : @Quot.{u} α r, β q + +Quot.ind p (Quot.mk r a) ... ⟶ p a ... +``` +-/ +def quotReduceRec [Monad m] (e : Expr) (whnf : Expr → m Expr) : m (Option Expr) := do + let .const fn _ := e.getAppFn | return none + let cont mkPos argPos := do + let args := e.getAppArgs + if h : mkPos < args.size then + let mk ← whnf args[mkPos] + if !mk.isAppOfArity ``Quot.mk 3 then return none + let mut r := Expr.app args[argPos]! mk.appArg! + let elimArity := mkPos + 1 + if elimArity < args.size then + r := mkAppRange r elimArity args.size args + return some r + else return none + if fn == ``Quot.lift then cont 5 3 + else if fn == ``Quot.ind then cont 4 3 + else return none diff --git a/Ix/Theory/Named/Reference/TypeChecker.lean b/Ix/Theory/Named/Reference/TypeChecker.lean new file mode 100644 index 000000000..67027532e --- /dev/null +++ b/Ix/Theory/Named/Reference/TypeChecker.lean @@ -0,0 +1,1074 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Reference.Declaration +import Ix.Theory.Named.Reference.PtrEq +import Ix.Theory.Named.Reference.Level +import Ix.Theory.Named.Reference.Quot +import Ix.Theory.Named.Reference.Inductive.Reduce +import Ix.Theory.Named.Reference.Instantiate +import Ix.Theory.Named.Reference.ForEachExprV +import Ix.Theory.Named.Reference.FuelConfig + +namespace Ix.Theory.Named +open Lean hiding Environment Exception +open Kernel + +abbrev InferCache := ExprMap Expr + +structure TypeChecker.State where + ngen : NameGenerator := { namePrefix := `_kernel_fresh, idx := 0 } + inferTypeI : InferCache := {} + inferTypeC : InferCache := {} + whnfCoreCache : ExprMap Expr := {} + whnfCache : ExprMap Expr := {} + success : Std.HashSet (Expr × Expr) := {} + failure : Std.HashSet (Expr × Expr) := {} + unfold : ExprMap Expr := {} + +structure TypeChecker.Context where + env : Environment + lctx : LocalContext := {} + safety : DefinitionSafety := .safe + eagerReduce := false + lparams : List Name := [] + fuel : FuelConfig := {} + +namespace TypeChecker + +abbrev M := ReaderT Context <| StateT State <| Except Exception + +def M.run (env : Environment) (safety : DefinitionSafety := .safe) + (lctx : LocalContext := {}) (lparams : List Name := []) (fuel : FuelConfig := {}) + (x : M α) : Except Exception α := + x { env, safety, lctx, lparams, fuel } |>.run' {} + +def M.runTermElab (m : M α) (safety := DefinitionSafety.safe) : Elab.Term.TermElabM α := do + ofExceptKernelException <| m.run (env := (← getEnv).toKernelEnv) + (lctx := ← getLCtx) (safety := safety) (lparams := (← get).levelNames) + +instance : MonadLift M Elab.Term.TermElabM := ⟨M.runTermElab⟩ + +def getEnv : M Environment := return (← read).env + +instance : MonadLCtx M where + getLCtx := return (← read).lctx + +instance [Monad m] : MonadNameGenerator (StateT State m) where + getNGen := return (← get).ngen + setNGen ngen := modify fun s => { s with ngen } + +instance : MonadLocalNameGenerator M where + withFreshId x := do x (← mkFreshId) + +instance (priority := low) : MonadWithReaderOf LocalContext M where + withReader f := withReader fun s => { s with lctx := f s.lctx } + +structure Methods where + protected isDefEqCore : Expr → Expr → M Bool + protected whnfCore (e : Expr) (cheapProj := false) : M Expr + protected whnf (e : Expr) : M Expr + protected inferType (e : Expr) (inferOnly : Bool) : M Expr + +abbrev RecM := ReaderT Methods M + +inductive ReductionStatus where + | continue (tn sn : Expr) + | unknown (tn sn : Expr) + | bool (b : Bool) + +namespace Inner + +/-- Reduces `e` to its weak-head normal form. -/ +def whnf (e : Expr) : RecM Expr := fun m => m.whnf e + +@[inline] def withLCtx [MonadWithReaderOf LocalContext m] (lctx : LocalContext) (x : m α) : m α := + withReader (fun _ => lctx) x + +/-- Ensures that `e` is defeq to some `e' := .sort ..`, returning `e'`. If not, throws an error with +`s` (the expression required to be a sort). -/ +def ensureSortCore (e s : Expr) : RecM Expr := do + if e.isSort then return e + let e ← whnf e + if e.isSort then return e + throw <| .typeExpected (← getEnv) (← getLCtx) s + +/-- Ensures that `e` is defeq to some `e' := .forallE ..`, returning `e'`. If not, throws an error +with `s := f a` (the application requiring `f` to be of function type). -/ +def ensureForallCore (e s : Expr) : RecM Expr := do + if e.isForall then return e + let e ← whnf e + if e.isForall then return e + throw <| .funExpected (← getEnv) (← getLCtx) s + +/-- Upper bound on the size in bytes of `n`'s runtime representation, mirroring the kernel's +`lean_nat_size_in_bytes`: a scalar occupies one machine word, and a bignum occupies its GMP limb +count times the limb size. -/ +def natSizeInBytes (n : Nat) : Nat := + let bits := n.log2 + 1 + -- `n` is boxed once it no longer fits in a machine word minus its tag bit. + if bits ≤ 63 then 8 else 8 * ((bits + 63) / 64) + +/-- Rejects a `Nat` numeral whose size exceeds `natMaxSize`. `numBytes` is an upper bound on the +numeral's size, so this may be called before computing it. -/ +def checkNatSize (numBytes : Nat) : RecM Unit := do + if numBytes > (← readThe Context).fuel.natMaxSize then + throw <| .other "the kernel refused a `Nat` numeral because its size exceeds the maximum; \ + raise the `natMaxSize` bound to allow it" + +/-- `Nat.pow` and `Nat.shiftLeft` take their second argument (the exponent, resp. the shift +amount) as a machine `UInt32` in the kernel, so reject one that does not fit rather than +overflowing. -/ +def checkCountArg (count : Nat) (op : String) : RecM Unit := do + if count ≥ UInt32.size then + throw <| .other s!"the kernel refused to evaluate `{op}` because its second argument does \ + not fit in a 32-bit unsigned integer" + +/-- Bounds the numerals entering the kernel (e.g. source literals) by the same limit `reduceNat` +enforces on the ones it computes. -/ +def checkLitSize (l : Literal) : RecM Unit := do + if let .natVal n := l then checkNatSize (natSizeInBytes n) + +/-- `checkNatSize`, conditionally: the size check the binary `Nat` operations that can grow a +numeral without bound run on their result. -/ +def checkNatSizeIf (checkSize : Bool) (numBytes : Nat) : RecM Unit := do + if checkSize then checkNatSize numBytes + +/-- Rejects `base ^ exp` when it would exceed `natMaxSize`, without forming it: the result +occupies at most `natSizeInBytes base * exp` bytes (a `base` of `0` or `1` yields `0`/`1`), and +the bound is compared by division so the product is never formed either. -/ +def checkPowSize (base exp : Nat) : RecM Unit := do + checkCountArg exp "Nat.pow" + if base > 1 && exp != 0 && natSizeInBytes base > (← readThe Context).fuel.natMaxSize / exp then + throw <| .other "the kernel refused to evaluate `Nat.pow` because the result would exceed \ + the maximum numeral size; raise the `natMaxSize` bound to allow it" + +/-- Rejects `v <<< shift` when it would exceed `natMaxSize`, without forming it: the result is +`v * 2 ^ shift`, which occupies about `natSizeInBytes v + shift / 8` bytes. `0 <<< _` is `0`. -/ +def checkShiftLeftSize (v shift : Nat) : RecM Unit := do + if v != 0 then + checkCountArg shift "Nat.shiftLeft" + checkNatSize (natSizeInBytes v + shift / 8 + 1) + +/-- Checks that `l` does not contain any level parameters not found in the context `tc`. -/ +def checkLevel (tc : Context) (l : Level) : Except Exception Unit := do + if let some n2 := l.getUndefParam tc.lparams then + throw <| .other s!"invalid reference to undefined universe level parameter '{n2}'" + +def inferFVar (tc : Context) (name : FVarId) : Except Exception Expr := do + if let some decl := tc.lctx.find? name then + return decl.type + throw <| .other "unknown free variable" + +/-- Infers the type of `.const name ls`. -/ +def inferConstant (tc : Context) (name : Name) (ls : List Level) (inferOnly : Bool) : + Except Exception Expr := do + let e := Expr.const name ls + let info ← tc.env.get name + let ps := info.levelParams + if ps.length != ls.length then + throw <| .other s!"incorrect number of universe levels parameters for '{e + }', #{ps.length} expected, #{ls.length} provided" + if !inferOnly then + if info.isUnsafe && tc.safety != .unsafe then + throw <| .other s!"invalid declaration, it uses unsafe declaration '{e}'" + if let .defnInfo v := info then + if v.safety == .partial && tc.safety == .safe then + throw <| .other + s!"invalid declaration, safe declaration must not contain partial declaration '{e}'" + for l in ls do + checkLevel tc l + return info.instantiateTypeLevelParams ls + +/-- Infers the type of expression `e`. If `inferOnly := false`, this function throws an error +whenever `e` is not typeable according to Lean's algorithmic typing judgment (barring resource +exhaustion: it may also throw `.deterministicTimeout` or `.deepRecursion` on a typeable term). +Setting `inferOnly := true` optimizes to avoid unnecessary checks in the case that `e` is already +known to be well-typed. -/ +def inferType (e : Expr) (inferOnly := true) : RecM Expr := fun m => m.inferType e inferOnly + +/-- Infers the type of lambda expression `e`. -/ +def inferLambda (e : Expr) (inferOnly : Bool) : RecM Expr := loop #[] e where + loop fvars : Expr → RecM Expr + | .lam name dom body bi => do + let d := dom.instantiateRev fvars + if !inferOnly then + _ ← ensureSortCore (← inferType d inferOnly) d + withLocalDecl name bi d fun fv => do + let fvars := fvars.push fv + loop fvars body + | e => do + let r ← inferType (e.instantiateRev fvars) inferOnly + let r := r.cheapBetaReduce + return (← getLCtx).mkForall fvars r + +/-- Infers the type of for-all expression `e`. -/ +def inferForall (e : Expr) (inferOnly : Bool) : RecM Expr := loop #[] #[] e where + loop fvars us : Expr → RecM Expr + | .forallE name dom body bi => do + let d := dom.instantiateRev fvars + let t1 ← ensureSortCore (← inferType d inferOnly) d + let us := us.push t1.sortLevel! + withLocalDecl name bi d fun fv => + loop (fvars.push fv) us body + | e => do + let r ← inferType (e.instantiateRev fvars) inferOnly + let s ← ensureSortCore r e + return .sort <| us.foldr mkLevelIMax' s.sortLevel! + +/-- Whether `t` and `s` have already been found definitionally equal. + +The pair is keyed on the order of the two hashes, so a later query finds the entry whichever way +round it presents the pair. This is a plain set of pairs rather than an equivalence-closure +structure on purpose: `isDefEq` is a sound but incomplete semi-decision procedure and so is not +transitive, and taking the transitive closure of its successes would make its result depend on the +order in which pairs were checked (lean4#14806). -/ +def succeededBefore (success : Std.HashSet (Expr × Expr)) (t s : Expr) : Bool := + if t.hash < s.hash then + success.contains (t, s) + else if t.hash > s.hash then + success.contains (s, t) + else + success.contains (t, s) || success.contains (s, t) + +@[inherit_doc succeededBefore] +def cacheSuccess (t s : Expr) : M Unit := do + let k := if t.hash ≤ s.hash then (t, s) else (s, t) + modify fun st => { st with success := st.success.insert k } + +/-- Returns whether `t` and `s` are definitionally equal according to Lean's algorithmic +definitional equality judgment. + +NOTE: This function does not do any typechecking of its own on `t` and `s`. So, when this is used as +part of a typechecking routine, it is expected that they are already well-typed (that is, that +`checkType t` and `checkType s` did not/would not throw an error). This is what justifies the +internal uses of `inferType` at its default `inferOnly := true`: on a well-typed subterm the fast +path returns the same type the checking path would have. -/ +def isDefEqCore (t s : Expr) : RecM Bool := fun m => m.isDefEqCore t s + +@[inherit_doc isDefEqCore] +def isDefEq (t s : Expr) : RecM Bool := do + -- Syntactically equivalent expressions are definitionally equal without consulting or + -- extending the success cache. Besides avoiding needless work, this keeps exact checker + -- executions compositional when an application argument has precisely the declared domain + -- type. + if t == s then return true + let r ← isDefEqCore t s + if r then + cacheSuccess t s + pure r + +/-- Infers the type of application `e`, assuming that `e` is already well-typed. -/ +def inferApp (e : Expr) : RecM Expr := do + e.withApp fun f args => + let rec loop fType j i : RecM Expr := + if i < args.size then + match fType with + | .forallE _ _ body _ => loop body j (i+1) + | _ => do + let fType := fType.instantiateRevRange j i args + let fType := (← ensureForallCore fType e).bindingBody! + loop fType i (i+1) + else + return fType.instantiateRevRange j args.size args + do loop (← inferType f) 0 0 + +/-- Infers the type of let-expression `e`. -/ +def inferLet (e : Expr) (inferOnly : Bool) : RecM Expr := loop #[] e where + loop fvars : Expr → RecM Expr + | .letE name type val body _ => do + let type := type.instantiateRev fvars + let val := val.instantiateRev fvars + if !inferOnly then + _ ← ensureSortCore (← inferType type inferOnly) type + let valType ← inferType val inferOnly + if !(← isDefEq valType type) then + throw <| .letTypeMismatch (← getEnv) (← getLCtx) name valType type + withLetDecl name type val fun fv => + loop (fvars.push fv) body + | e => do + let r ← inferType (e.instantiateRev fvars) inferOnly + let r := r.cheapBetaReduce + return (← getLCtx).mkForall fvars r + +/-- Gets the universe level of the sort that `e`'s type is defeq to, failing if `e` is not +a type. -/ +def getSortLevel (e : Expr) : RecM Level := do + let .sort u ← ensureSortCore (← inferType e) e | unreachable! + return u + +/-- Checks if `e` is a proposition, that is, if its type is a sort whose level normalizes to +zero. -/ +def isProp (e : Expr) : RecM Bool := return (← getSortLevel e).isAlwaysZero + +/-- Checks if `e` is definitely not a proposition, that is, if its type is a sort whose level +cannot normalize to zero under any instantiation. This is strictly stronger than `!(← isProp e)`, +which also holds for the levels that could go either way. -/ +def isNeverProp (e : Expr) : RecM Bool := return (← getSortLevel e).isNeverZero + +def invalidProj (e : Expr) : RecM α := do + throw <| .invalidProj (← getEnv) (← getLCtx) e + +def inferProjParams (proj : Expr) : List Expr → Expr → RecM Expr + | [], r => pure r + | arg :: args, r => do + let .forallE _ _ body _ ← whnf r | invalidProj proj + inferProjParams proj args (body.instantiate1 arg) + +def inferProjFields (proj : Expr) (typeName : Name) + (struct : Expr) (maybePropType : Bool) : + Nat → Nat → Expr → RecM Expr + | _, 0, r => pure r + | fieldIdx, count + 1, r => do + let .forallE _ dom body _ ← whnf r | invalidProj proj + if body.hasLooseBVars && maybePropType then + -- prop structs cannot have non-prop dependent fields + if !(← isProp dom) then invalidProj proj + inferProjFields proj typeName struct maybePropType (fieldIdx + 1) count + (body.instantiate1 (.proj typeName fieldIdx struct)) + +/-- Infers the type of structure projection `e`. -/ +def inferProj (typeName : Name) (idx : Nat) (struct structType : Expr) : RecM Expr := do + let e := Expr.proj typeName idx struct + let type ← whnf structType + type.withApp fun I args => do + let env ← getEnv + let .const I_name I_levels := I | invalidProj e + if typeName != I_name then invalidProj e + let .inductInfo I_val ← env.get I_name | invalidProj e + let [c] := I_val.ctors | invalidProj e + unless env.isProjectionReadyStructure I_name do invalidProj e + if args.size != I_val.numParams + I_val.numIndices then invalidProj e + let c_info ← env.get c + let .ctorInfo ctorInfo := c_info | invalidProj e + unless idx < ctorInfo.numFields do invalidProj e + let r ← inferProjParams e (args.toList.take I_val.numParams) + (c_info.instantiateTypeLevelParams I_levels) + let maybePropType := !(← getSortLevel type).isNeverZero + let r ← inferProjFields e I_name struct maybePropType 0 idx r + let .forallE _ dom _ _ ← whnf r | invalidProj e + if maybePropType then if !(← isProp dom) then invalidProj e + return dom + +@[inherit_doc inferType] +def inferType' (e : Expr) (inferOnly : Bool) : RecM Expr := do + if e.hasLooseBVars then + throw <| .other + s!"type checker does not support loose bound variables, \ + replace them with free variables before invoking it" + let state ← get + if let some r := (cond inferOnly state.inferTypeI state.inferTypeC)[e]? then + return r + let r ← match e with + | .lit l => + checkLitSize l + if !inferOnly then + match l with + | .natVal _ => _ ← (← getEnv).get ``Nat + | .strVal _ => _ ← (← getEnv).get ``Char.ofNat; _ ← (← getEnv).get ``String.ofList + pure (mkConst l.typeName) + | .mdata _ e => inferType' e inferOnly + | .proj s idx e => inferProj s idx e (← inferType' e inferOnly) + | .fvar n => inferFVar (← readThe Context) n + | .mvar _ => throw <| .other "kernel type checker does not support meta variables" + | .bvar _ => unreachable! + | .sort l => + if !inferOnly then + checkLevel (← readThe Context) l + pure <| .sort (.succ l) + | .const c ls => inferConstant (← readThe Context) c ls inferOnly + | .lam .. => inferLambda e inferOnly + | .forallE .. => inferForall e inferOnly + | .app f a => + if inferOnly then + inferApp e + else + let fType ← ensureForallCore (← inferType' f inferOnly) e + let aType ← inferType' a inferOnly + let dType := fType.bindingDomain! + -- it can be shown that if `e` is typeable as `T`, then `T` is typeable as `Sort l` + -- for some universe level `l`, so this use of `isDefEq` is valid + let ok ← if a.isAppOfArity ``eagerReduce 2 then + withTheReader Context (fun s => {s with eagerReduce := true}) <| + isDefEq dType aType + else + isDefEq dType aType + if !ok then throw <| .appTypeMismatch (← getEnv) (← getLCtx) e fType aType + pure <| fType.bindingBody!.instantiate1 a + | .letE .. => inferLet e inferOnly + modify fun s => cond inferOnly + { s with inferTypeI := s.inferTypeI.insert e r } + { s with inferTypeC := s.inferTypeC.insert e r } + return r + +/-- Reduces `e` to its weak-head normal form, without unfolding definitions. This is a conservative +version of `whnf` (which does unfold definitions), to be used for efficiency purposes. + +Setting `cheapProj` to `true` will cause the struct argument to be reduced "lazily" (using +`whnfCore` rather than `whnf`) when reducing struct projections, and suppresses caching of the +result. This can be a useful optimization if we're checking the definitional equality of two struct +projections of the same projection, where we might save some work by directly checking if the struct +arguments are defeq (rather than eagerly applying a projection). + +The kernel has a companion `cheap_rec` flag doing the same for the major premise of a recursor, but +nothing has set it since lean4#9275 removed the old compiler, so it is omitted here. -/ +def whnfCore (e : Expr) (cheapProj := false) : RecM Expr := + fun m => m.whnfCore e cheapProj + +def reduceRecursor (e : Expr) : RecM (Option Expr) := do + let env ← getEnv + if env.quotInit then + if let some r ← quotReduceRec e whnf then + return r + if let some r ← inductiveReduceRec env e whnf inferType isDefEq isNeverProp then + return r + return none + +/-- Reduces the free variable `e`: to the `whnfCore` of its definition if `e` is a let variable, +and to itself if it is a lambda variable. -/ +def whnfFVar (e : Expr) (cheapProj : Bool) : RecM Expr := do + if let some (.ldecl (value := v) ..) := (← getLCtx).find? e.fvarId! then + return ← whnfCore v cheapProj + return e + +/-- Reduces a projection of `struct` at index `idx` (when `struct` is reducible to a constructor +application). -/ +def reduceProj (idx : Nat) (struct : Expr) (cheapProj : Bool) : RecM (Option Expr) := do + let mut c ← (if cheapProj then whnfCore struct cheapProj else whnf struct) + if let .lit (.strVal s) := c then + c ← whnf (.strLitToConstructor s) + c.withApp fun mk args => do + let .const mkC _ := mk | return none + let env ← getEnv + let .ctorInfo mkInfo ← env.get mkC | return none + return args[mkInfo.numParams + idx]? + +def isLetFVar (lctx : LocalContext) (fvar : FVarId) : Bool := + lctx.find? fvar matches some (.ldecl ..) + +@[inherit_doc whnfCore] +def whnfCore' (e : Expr) (cheapProj := false) : RecM Expr := do + match e with + | .bvar .. | .sort .. | .mvar .. | .forallE .. | .const .. | .lam .. | .lit .. => return e + | .mdata _ e => return ← whnfCore' e cheapProj + | .fvar id => if !isLetFVar (← getLCtx) id then return e + | .app .. | .letE .. | .proj .. => pure () + if let some r := (← get).whnfCoreCache[e]? then + return r + let rec save r := do + if !cheapProj then + modify fun s => { s with whnfCoreCache := s.whnfCoreCache.insert e r } + return r + match e with + | .bvar .. | .sort .. | .mvar .. | .forallE .. | .const .. | .lam .. | .lit .. + | .mdata .. => unreachable! + | .fvar _ => return ← whnfFVar e cheapProj + | .app .. => + -- beta-reduce at the head as much as possible, apply any remaining `rargs` + -- to the resulting expression, and re-run `whnfCore` + e.withAppRev fun f0 rargs => do + -- the head may still be a let variable/binding, projection, or mdata-wrapped expression + let f ← whnfCore f0 cheapProj + if let .lam _ _ body _ := f then + let rec loop m (f : Expr) : RecM Expr := + let rec cont := do + let r := f.instantiateRange (rargs.size - m) rargs.size rargs + let r := r.mkAppRevRange 0 (rargs.size - m) rargs + save <|← whnfCore r cheapProj + if let .lam _ _ body _ := f then + if m < rargs.size then loop (m + 1) body + else cont + else cont + loop 1 body + else if Expr.structuralEq f f0 then + if let some r ← reduceRecursor e then + whnfCore r cheapProj + else + pure e + else + let r := f.mkAppRevRange 0 rargs.size rargs + -- the recursive call re-decomposes `r` and reaches the `f == f0` branch above, so + -- `reduceRecursor` is still applied; adding arguments can only enable further normalization + -- if the head reduced to a partial recursor application + save <|← whnfCore r cheapProj + | .letE _ _ val body _ => + save <|← whnfCore (body.instantiate1 val) cheapProj + | .proj _ idx s => + if let some m ← reduceProj idx s cheapProj then + save <|← whnfCore m cheapProj + else + save e + +/-- Checks if the head of `e` is a constant that can be delta-reduced, applied to the right number +of universe levels, returning its `ConstantInfo` if so. See `ConstantInfo.deltaValue?` for which +constants qualify. -/ +def isDelta (env : Environment) (e : Expr) : Option ConstantInfo := do + if let .const c ls := e.getAppFn then + if let some ci := env.find? c then + if ci.deltaValue?.isSome && ls.length == ci.numLevelParams then + return ci + none + +def instantiateDeltaValue (ci : ConstantInfo) (ls : List Level) : Expr := + ci.deltaValue?.get!.instantiateLevelParams ci.levelParams ls + +/-- If `e` is itself a constant that can be delta-reduced, returns its value with the constant's +level parameters instantiated. Unlike `unfoldDefinition`, this does not look through applications: +`e` must be a `.const`. -/ +def unfoldDefinitionCore (e : Expr) : RecM (Option Expr) := do + let .const _ ls := e | return none + let env ← getEnv + let some d := isDelta env e | return none + unless 0 < ls.length do return some (instantiateDeltaValue d ls) + if let some r := (← get).unfold[e]? then return some r + let r := instantiateDeltaValue d ls + modify fun s => { s with unfold := s.unfold.insert e r } + return some r + +/-- Unfolds the definition at the head of the application `e` (or `e` itself if it is not an +application). -/ +def unfoldDefinition (e : Expr) : RecM (Option Expr) := do + if e.isApp then + let f0 := e.getAppFn + let some f ← unfoldDefinitionCore f0 | return none + let rargs := e.getAppRevArgs + return f.mkAppRevRange 0 rargs.size rargs + else + unfoldDefinitionCore e + +def reduceNative (_env : Environment) (e : Expr) : Except Exception (Option Expr) := do + let .app f (.const c _) := e | return none + if Expr.structuralEq f (.const ``reduceBool []) then + throw <| .other s!"the named specification does not support 'reduceBool {c}' reduction" + else if Expr.structuralEq f (.const ``reduceNat []) then + throw <| .other s!"the named specification does not support 'reduceNat {c}' reduction" + return none + +def rawNatLitExt? (e : Expr) : Option Nat := if e == .natZero then some 0 else e.rawNatLit? + +/-- Reduces the application `f a b` to a Nat literal if `a` and `b` can be reduced to Nat literals. + +`checkSize` bounds the size of the result, for the operations that can grow a numeral without +bound when iterated. Their operands are already bounded, so computing the result before checking +it is cheap. + +Note: `f` should have an (efficient) external implementation. -/ +def reduceBinNatOp (f : Nat → Nat → Nat) (a b : Expr) (checkSize := false) : RecM (Option Expr) := do + let some v1 := rawNatLitExt? (← whnf a) | return none + let some v2 := rawNatLitExt? (← whnf b) | return none + checkNatSizeIf checkSize (natSizeInBytes (f v1 v2)) + return some <| .lit <| .natVal <| f v1 v2 + +/-- Reduces `Nat.pow a b`. Unlike the other binary operations this bounds the result *before* +computing it, since a small exponent already denotes an unrepresentable numeral. -/ +def reducePow (a b : Expr) : RecM (Option Expr) := do + let some base := rawNatLitExt? (← whnf a) | return none + let some exp := rawNatLitExt? (← whnf b) | return none + checkPowSize base exp + return some <| .lit <| .natVal <| Nat.pow base exp + +/-- Reduces `Nat.shiftLeft a b`, bounding the result before computing it. -/ +def reduceShiftLeft (a b : Expr) : RecM (Option Expr) := do + let some v := rawNatLitExt? (← whnf a) | return none + let some shift := rawNatLitExt? (← whnf b) | return none + checkShiftLeftSize v shift + return some <| .lit <| .natVal <| v <<< shift + +/-- Reduces the application `f a b` to a boolean expression if `a` and `b` can be reduced to Nat +literals. + +Note: `f` should have an (efficient) external implementation. -/ +def reduceBinNatPred (f : Nat → Nat → Bool) (a b : Expr) : RecM (Option Expr) := do + let some v1 := rawNatLitExt? (← whnf a) | return none + let some v2 := rawNatLitExt? (← whnf b) | return none + return toExpr <| f v1 v2 + +/-- Reduces `e` to a literal if possible, where the unary operation `Nat.succ` and the binary +operations and predicates with an external implementation may be applied: `Nat.add`, `Nat.sub`, +`Nat.mul`, `Nat.pow`, `Nat.gcd`, `Nat.mod`, `Nat.div`, `Nat.land`, `Nat.lor`, `Nat.xor`, +`Nat.shiftLeft`, `Nat.shiftRight` produce a `Nat` literal, while the predicates `Nat.beq` and +`Nat.ble` produce a `Bool` literal. -/ +def reduceNat (e : Expr) : RecM (Option Expr) := do + let nargs := e.getAppNumArgs + if nargs == 1 then + let f := e.appFn! + if Expr.structuralEq f (.const ``Nat.succ []) then + let some v := rawNatLitExt? (← whnf e.appArg!) | return none + checkNatSize (natSizeInBytes (v + 1)) + return some <| .lit <| .natVal <| v + 1 + else if nargs == 2 then + let .app (.app (.const f _) a) b := e | return none + if f == ``Nat.add then return ← reduceBinNatOp Nat.add a b (checkSize := true) + if f == ``Nat.sub then return ← reduceBinNatOp Nat.sub a b (checkSize := true) + if f == ``Nat.mul then return ← reduceBinNatOp Nat.mul a b (checkSize := true) + if f == ``Nat.pow then return ← reducePow a b + if f == ``Nat.gcd then return ← reduceBinNatOp Nat.gcd a b + if f == ``Nat.mod then return ← reduceBinNatOp Nat.mod a b + if f == ``Nat.div then return ← reduceBinNatOp Nat.div a b + if f == ``Nat.beq then return ← reduceBinNatPred Nat.beq a b + if f == ``Nat.ble then return ← reduceBinNatPred Nat.ble a b + if f == ``Nat.land then return ← reduceBinNatOp Nat.land a b + if f == ``Nat.lor then return ← reduceBinNatOp Nat.lor a b + if f == ``Nat.xor then return ← reduceBinNatOp Nat.xor a b + if f == ``Nat.shiftLeft then return ← reduceShiftLeft a b + if f == ``Nat.shiftRight then return ← reduceBinNatOp Nat.shiftRight a b + return none + +@[inherit_doc whnf] +def whnf' (e : Expr) : RecM Expr := do + -- Do not cache easy cases + match e with + | .bvar .. | .sort .. | .mvar .. | .forallE .. | .lit .. => return e + | .mdata _ e => return ← whnf' e + | .fvar id => + if !isLetFVar (← getLCtx) id then + return e + | .lam .. | .app .. | .const .. | .letE .. | .proj .. => pure () + -- check cache + if let some r := (← get).whnfCache[e]? then + return r + let rec loop t + | 0 => throw .deterministicTimeout + | fuel+1 => do + let env ← getEnv + let t ← whnfCore' t + if let some t ← reduceNative env t then return t + if let some t ← reduceNat t then return t + let some t ← unfoldDefinition t | return t + loop t fuel + let ctx ← readThe Context + let r ← loop e <| if ctx.eagerReduce then ctx.fuel.whnfEager else ctx.fuel.whnf + modify fun s => { s with whnfCache := s.whnfCache.insert e r } + return r + +/-- If `t` and `s` are lambda expressions, checks that their domains are defeq and recurses on the +bodies, substituting in a new free variable for that binder (this substitution is delayed for +efficiency purposes using the `subst` parameter). Otherwise, does a normal defeq check. -/ +def isDefEqLambda (t s : Expr) (subst : Array Expr := #[]) : RecM Bool := + match t, s with + | .lam _ tDom tBody _, .lam name sDom sBody bi => do + let sType ← if tDom == sDom then pure none else + let sType := sDom.instantiateRev subst + let tType := tDom.instantiateRev subst + if !(← isDefEq tType sType) then return false + pure (some sType) + if tBody.hasLooseBVars || sBody.hasLooseBVars then + let sType := sType.getD (sDom.instantiateRev subst) + withLocalDecl name bi sType fun fv => do + isDefEqLambda tBody sBody (subst.push fv) + else + isDefEqLambda tBody sBody (subst.push default) + | t, s => isDefEq (t.instantiateRev subst) (s.instantiateRev subst) + +/-- If `t` and `s` are for-all expressions, checks that their domains are defeq and recurses on the +bodies, substituting in a new free variable for that binder (this substitution is delayed for +efficiency purposes using the `subst` parameter). Otherwise, does a normal defeq check. -/ +def isDefEqForall (t s : Expr) (subst : Array Expr := #[]) : RecM Bool := + match t, s with + | .forallE _ tDom tBody _, .forallE name sDom sBody bi => do + let sType ← if tDom == sDom then pure none else + let sType := sDom.instantiateRev subst + let tType := tDom.instantiateRev subst + if !(← isDefEq tType sType) then return false + pure (some sType) + if tBody.hasLooseBVars || sBody.hasLooseBVars then + let sType := sType.getD (sDom.instantiateRev subst) + withLocalDecl name bi sType fun fv => + isDefEqForall tBody sBody (subst.push fv) + else + isDefEqForall tBody sBody (subst.push default) + | t, s => isDefEq (t.instantiateRev subst) (s.instantiateRev subst) + +/-- Decides definitional equality of `t` and `s` in the cases that can be settled without +reduction, returning `.undef` to defer to the calling function otherwise. + +It returns `.true` if they are α-equivalent or have previously been checked for definitional +equality, and otherwise decides two sorts by level equivalence and two literals by equality, +returning `.false` where these disagree. Two lambdas or two for-alls are handed to +`isDefEqLambda`/`isDefEqForall`, which may return either. All remaining cases — including two +constants, two free variables, two applications and two projections — are deferred. -/ +def quickIsDefEq (t s : Expr) : RecM LBool := do + -- cheap structural check, plus the positive `isDefEq` cache + if t == s || succeededBefore (← get).success t s then return .true + match t, s with + | .lam .., .lam .. => toLBoolM <| isDefEqLambda t s + | .forallE .., .forallE .. => toLBoolM <| isDefEqForall t s + | .sort a1, .sort a2 => pure (a1.isEquiv' a2).toLBool + | .mdata _ a1, .mdata _ a2 => toLBoolM <| isDefEq a1 a2 + | .mvar .., .mvar .. => unreachable! + | .lit a1, .lit a2 => pure (a1 == a2).toLBool + | _, _ => return .undef + +/-- Assuming that `t` and `s` have the same function heads, returns true if they are applications +with definitionally equal arguments (in which case they are defeq), and false otherwise (deferring +further defeq checking to caller). -/ +def isDefEqArgs (t s : Expr) : RecM Bool := do + match t, s with + | .app tf ta, .app sf sa => + if !(← isDefEq ta sa) then return false + isDefEqArgs tf sf + | .app .., _ | _, .app .. => return false + | _, _ => return true + +/-- Assuming `t` and `s` are WHNF, checks if they are defeq on account of `t` being an η-expansion +of `s`. + +Assuming that `s` has a function type `(x : A) → B x`, it η-expands to `fun (x : A) => s x` +(which it is definitionally equal to by the η rule). -/ +def tryEtaExpansionCore (t s : Expr) : RecM Bool := do + if t.isLambda && !s.isLambda then + let .forallE name ty _ bi ← whnf (← inferType s) | return false + isDefEq t (.lam name ty (.app s (.bvar 0)) bi) + else return false + +@[inherit_doc tryEtaExpansionCore] +def tryEtaExpansion (t s : Expr) : RecM Bool := + tryEtaExpansionCore t s <||> tryEtaExpansionCore s t + +/-- One field comparison in the structure-eta fast path. Naming the +callback keeps the executable range loop and its verification aligned without +depending on proof terms synthesized by `for` notation. -/ +def tryEtaStructFieldStep (t : Expr) (induct : Name) (numParams : Nat) + (args : Array Expr) (i : Nat) (_ : i ∈ [numParams:args.size]) + (_ : Option Bool × PUnit) : + RecM (ForInStep (Option Bool × PUnit)) := do + let b ← isDefEq (.proj induct (i - numParams) t) args[i] + if b = true then + pure (.yield ⟨none, PUnit.unit⟩) + else + pure (.done ⟨some false, PUnit.unit⟩) + +/-- Assuming `t` and `s` in WHNF, checks if they are defeq on account of `s` being defeq to the +struct-η-expansion of `t`. + +Assuming that `t` has a non-recursive structure type `S` with constructor `S.mk` and projections +`pᵢ`, it struct-η-expands to `S.mk (p₁ t) ... (pₙ t)` (which it is definitionally equal to by the +struct-η rule). -/ +def tryEtaStructCore (t s : Expr) : RecM Bool := do + let .const f _ := s.getAppFn | return false + let env ← getEnv + let .ctorInfo fInfo ← env.get f | return false + unless s.getAppNumArgs == fInfo.numParams + fInfo.numFields do return false + unless env.isNonRecStructure fInfo.induct do return false + unless ← isDefEq (← inferType t) (← inferType s) do return false + let args := s.getAppArgs + -- since `t` is in WHNF, and assuming it is not a constructor application, this projection + -- cannot reduce (so we are directly checking if `s` is defeq to the struct-η-expansion of `t`) + let r ← forIn' [fInfo.numParams:args.size] ⟨none, PUnit.unit⟩ + (tryEtaStructFieldStep t fInfo.induct fInfo.numParams args) + match r.1 with + | none => return true + | some b => return b + +@[inherit_doc tryEtaStructCore] +def tryEtaStruct (t s : Expr) : RecM Bool := + -- when `t` and `s` are both constructor applications, `isDefEqApp` has already compared their + -- arguments and returned false, and the projections in `tryEtaStructCore` reduce back to those + -- same arguments, so both calls below merely redo that work. The kernel has the same redundancy. + tryEtaStructCore t s <||> tryEtaStructCore s t + +/-- Checks if applications `t` and `s` (should be WHNF) are defeq on account of their function heads +and arguments being defeq. -/ +def isDefEqApp (t s : Expr) : RecM Bool := do + unless t.isApp && s.isApp do return false + t.withApp fun tf tArgs => + s.withApp fun sf sArgs => do + if _h : tArgs.size = sArgs.size then + unless ← isDefEq tf sf do return false + let rec loop i := do + if _h : i < tArgs.size then + unless ← isDefEq tArgs[i] sArgs[i] do return false + loop (i+1) + else return true + loop 0 + else return false + +/-- Checks if `t` and `s` are definitionally equivalent according to proof irrelevance (that is, +they are proofs of the same proposition). -/ +def isDefEqProofIrrel (t s : Expr) : RecM LBool := do + let tType ← inferType t + if !(← isProp tType) then return .undef + toLBoolM <| isDefEq tType (← inferType s) + +def failedBefore (failure : Std.HashSet (Expr × Expr)) (t s : Expr) : Bool := + if t.hash < s.hash then + failure.contains (t, s) + else if t.hash > s.hash then + failure.contains (s, t) + else + failure.contains (t, s) || failure.contains (s, t) + +def cacheFailure (t s : Expr) : M Unit := do + let k := if t.hash ≤ s.hash then (t, s) else (s, t) + modify fun st => { st with failure := st.failure.insert k } + +def tryUnfoldProjApp (e : Expr) : RecM (Option Expr) := do + let f := e.getAppFn + if !f.isProj then return none + let e' ← whnfCore e + return if e' != e then e' else none + +/-- Performs a single step of δ-reduction on `tn`, `sn`, or both (according to optimizations) +followed by weak-head normalization (without further δ-reduction). Returns `.bool` if the resulting +terms are settled by `quickIsDefEq`, or if they are applications of the same defined constant with +defeq args. Otherwise returns `.continue`, indicating to the calling `lazyDeltaReduction` that +δ-reduction is to be continued. + +If neither side has a δ-reducible head, returns `.unknown` with the terms unchanged, leaving further +defeq-checking to `isDefEqCore'`. Note that these are weak-head normal forms with respect to +`cheapProj := true`, so a projection at the head may still be reducible. -/ +def lazyDeltaReductionStep (tn sn : Expr) : RecM ReductionStatus := do + let env ← getEnv + let delta e := do whnfCore (← unfoldDefinition e).get! (cheapProj := true) + let cont tn sn := + return match ← quickIsDefEq tn sn with + | .undef => .continue tn sn + | .true => .bool true + | .false => .bool false + match isDelta env tn, isDelta env sn with + | none, none => return .unknown tn sn + | some _, none => + -- `sn` was normalized with `cheapProj := true`, so a projection at its head may not have been + -- reduced; `tryUnfoldProjApp` retries it with the struct argument fully normalized + if let some sn' ← tryUnfoldProjApp sn then + cont tn sn' + else + cont (← delta tn) sn + | none, some _ => + if let some tn' ← tryUnfoldProjApp tn then + cont tn' sn + else + cont tn (← delta sn) + | some dt, some ds => + let ht := dt.hints + let hs := ds.hints + if ht.lt' hs then + cont tn (← delta sn) + else if hs.lt' ht then + cont (← delta tn) sn + else + if tn.isApp && sn.isApp && ptrEqConstantInfo dt ds && dt.hints.isRegular + && !failedBefore (← get).failure tn sn + then + if Level.isEquivList tn.getAppFn.constLevels! sn.getAppFn.constLevels! then + if ← isDefEqArgs tn sn then + return .bool true + cacheFailure tn sn + cont (← delta tn) (← delta sn) + +@[inline] def isNatZero (t : Expr) : Bool := + t == .natZero || t matches .lit (.natVal 0) + +def isNatSuccOf? : Expr → Option Expr + | .lit (.natVal (n+1)) => return .lit (.natVal n) + | .app (.const ``Nat.succ _) e => return e + | _ => none + +/-- Returns `.true` if `t` and `s` are both zero, either as a literal or as `Nat.zero`. If they are +both successors of natural numbers `t'` and `s'`, either as literals or `Nat.succ` applications, +checks that `t'` and `s'` are definitionally equal. Otherwise, defers to the calling function. -/ +def isDefEqOffset (t s : Expr) : RecM LBool := do + if isNatZero t && isNatZero s then + return .true + match isNatSuccOf? t, isNatSuccOf? s with + | some t', some s' => toLBoolM <| isDefEqCore t' s' + | _, _ => return .undef + +/-- Repeatedly δ-reduces the `cheapProj := true` weak-head normal forms `tn` and `sn` until the +question is settled. Returns `.bool` if: +- they are both zero or both natural number successors (as literals or `Nat.succ` applications) +- one of them can be converted to a natural number/boolean literal +- a `lazyDeltaReductionStep` settles them + +Otherwise returns `.unknown` with the reduced terms, deferring to the calling function. Throws +`.deterministicTimeout` after `FuelConfig.lazyDelta` steps. -/ +def lazyDeltaReduction (tn sn : Expr) : RecM ReductionStatus := do + loop tn sn (← readThe Context).fuel.lazyDelta +where + loop tn sn + | 0 => throw .deterministicTimeout + | fuel+1 => do + let r ← isDefEqOffset tn sn + if r != .undef then return .bool (r == .true) + if !tn.hasFVar && !sn.hasFVar || (← readThe Context).eagerReduce then + if let some tn' ← reduceNat tn then + return .bool (← isDefEqCore tn' sn) + else if let some sn' ← reduceNat sn then + return .bool (← isDefEqCore tn sn') + let env ← getEnv + if let some tn' ← reduceNative env tn then + return .bool (← isDefEqCore tn' sn) + else if let some sn' ← reduceNative env sn then + return .bool (← isDefEqCore tn sn') + match ← lazyDeltaReductionStep tn sn with + | .continue tn sn => loop tn sn fuel + | r => return r + +/-- If `t` is a string literal and `s` is a `String.ofList` application, checks that they are defeq +after expanding `t` into a `String.ofList` application of an explicit character list. Otherwise, +defers to the calling function. -/ +def tryStringLitExpansionCore (t s : Expr) : RecM LBool := do + let .lit (.strVal st) := t | return .undef + let .app sf _ := s | return .undef + unless sf == .const ``String.ofList [] do return .undef + toLBoolM <| isDefEqCore (.strLitToConstructor st) s + +@[inherit_doc tryStringLitExpansionCore] +def tryStringLitExpansion (t s : Expr) : RecM LBool := do + match ← tryStringLitExpansionCore t s with + | .undef => tryStringLitExpansionCore s t + | r => return r + +/-- Checks if `t` and `s` are defeq on account of both being of a unit type (a type with one +constructor without any fields or indices). -/ +def isDefEqUnitLike (t s : Expr) : RecM Bool := do + let tType ← whnf (← inferType t) + let .const I _ := tType.getAppFn | return false + let env ← getEnv + let .inductInfo { isRec := false, ctors := [c], numIndices := 0, .. } ← env.get I + | return false + let .ctorInfo { numFields := 0, .. } ← env.get c | return false + isDefEqCore tType (← inferType s) + +@[inherit_doc isDefEqCore] +def isDefEqCore' (t s : Expr) : RecM Bool := do + let r ← quickIsDefEq t s + if r != .undef then return r == .true + + if (!t.hasFVar || (← readThe Context).eagerReduce) && s.isConstOf ``true then + if (← whnf t).isConstOf ``true then return true + + let tn ← whnfCore t (cheapProj := true) + let sn ← whnfCore s (cheapProj := true) + + if !(ptrEqExpr tn t && ptrEqExpr sn s) then + let r ← quickIsDefEq tn sn + if r != .undef then return r == .true + + let r ← isDefEqProofIrrel tn sn + if r != .undef then return r == .true + + match ← lazyDeltaReduction tn sn with + | .continue .. => unreachable! + | .bool b => return b + | .unknown tn sn => + + match tn, sn with + | .const tf tl, .const sf sl => + if tf == sf && Level.isEquivList tl sl then return true + | .fvar tv, .fvar sv => if tv == sv then return true + | .proj _ ti te, .proj _ si se => + -- optimized by the previous reduction functions using `cheapProj := true` + if ti == si then if ← isDefEq te se then return true + | _, _ => pure () + + -- the previous reduction functions used `cheapProj := true`, so we may not have a complete WHNF + let tnn ← whnfCore tn + let snn ← whnfCore sn + if !(ptrEqExpr tnn tn && ptrEqExpr snn sn) then + -- if projection reduced, need to re-run (as we may not have a WHNF) + return ← isDefEqCore tnn snn + + -- tn and sn are both in WHNF + if ← isDefEqApp tn sn then return true + if ← tryEtaExpansion tn sn then return true + if ← tryEtaStruct tn sn then return true + let r ← tryStringLitExpansion tn sn + if r != .undef then return r == .true + if ← isDefEqUnitLike tn sn then return true + return false + +end Inner + +open Inner + +def Methods.withFuel : Nat → Methods + | 0 => + { isDefEqCore := fun _ _ => throw .deepRecursion + whnfCore := fun _ _ => throw .deepRecursion + whnf := fun _ => throw .deepRecursion + inferType := fun _ _ => throw .deepRecursion } + | n + 1 => + { isDefEqCore := fun t s => isDefEqCore' t s (withFuel n) + whnfCore := fun e p => whnfCore' e p (withFuel n) + whnf := fun e => whnf' e (withFuel n) + inferType := fun e i => inferType' e i (withFuel n) } + +/-- Runs `x` with a limit on the recursion depth, taken from `FuelConfig.recDepth`. -/ +def RecM.run (x : RecM α) : M α := do x (Methods.withFuel (← readThe Context).fuel.recDepth) + +def RecM.runTermElab (x : RecM α) (safety := DefinitionSafety.safe) : Elab.Term.TermElabM α := + x.run.runTermElab safety + +instance : MonadLift RecM Elab.Term.TermElabM := ⟨RecM.runTermElab⟩ + +@[inherit_doc whnf'] +def whnf (e : Expr) : M Expr := (Inner.whnf e).run + +def whnfCore (e : Expr) : M Expr := (Inner.whnfCore e).run + +def unfoldDefinition (e : Expr) : M Expr := return (← (Inner.unfoldDefinition e).run).getD e + +/-- Infers the type of expression `e`. Note that this uses the optimization `inferOnly := true`, and +so should only be used for the purpose of type inference on terms that are known to be well-typed. +To typecheck terms for the first time, use `checkType`. -/ +def inferType (e : Expr) : M Expr := (Inner.inferType e).run + +/-- Infers the type of expression `e` and checks that `e` is well-typed according to Lean's typing +judgment. + +Use `inferType` to infer type alone. -/ +def checkType (e : Expr) : M Expr := (Inner.inferType e (inferOnly := false)).run + +@[inherit_doc isDefEqCore] +def isDefEq (t s : Expr) : M Bool := (Inner.isDefEq t s).run + +@[inherit_doc Inner.isProp] +def isProp (t : Expr) : M Bool := (Inner.isProp t).run + +@[inherit_doc ensureSortCore] +def ensureSort (t : Expr) (s := t) : M Expr := (ensureSortCore t s).run + +@[inherit_doc ensureForallCore] +def ensureForall (t : Expr) (s := t) : M Expr := (ensureForallCore t s).run + +/-- Ensures that `e` is a type/proposition. If it is not, throws an error. -/ +def ensureType (e : Expr) : M Expr := do ensureSort (← inferType e) e + +def etaExpand (e : Expr) : M Expr := + let rec loop fvars + | .lam name dom body bi => do + let d := dom.instantiateRev fvars + withLocalDecl name bi d fun fv => do + let fvars := fvars.push fv + loop fvars body + | it => do + let itType ← whnf <| ← inferType <| it.instantiateRev fvars + if !itType.isForall then return e + let rec loop2 fvars args + | 0, _ => throw .deepRecursion + | fuel + 1, .forallE name dom body bi => do + let d := dom.instantiateRev fvars + withLocalDecl name bi d fun arg => do + let fvars := fvars.push arg + let args := args.push arg + loop2 fvars args fuel <| ← whnf <| body.instantiate1 arg + | _, it => return (← getLCtx).mkLambda fvars (mkAppN it args) + loop2 fvars #[] (← readThe Context).fuel.etaExpand itType + loop #[] e + +-- for testing: + +-- example : "hi" = sorry := by +-- run_tac +-- let env ← Lean.getEnv +-- let lctx ← getLCtx +-- let (_, lhs, _) := (← Elab.Tactic.getMainTarget).eq?.get! +-- logInfo lhs +-- let ty ← show M _ from withReader ({· with env := .empty `h}) (inferType lhs) +-- logInfo ty +-- let sort ← inferType ty +-- logInfo sort diff --git a/Ix/Theory/Named/SingletonParity.lean b/Ix/Theory/Named/SingletonParity.lean new file mode 100644 index 000000000..32a76e7d4 --- /dev/null +++ b/Ix/Theory/Named/SingletonParity.lean @@ -0,0 +1,186 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.InductiveFixtures + +open Ix.Theory (VLevel) + +/-! +# Complete singleton-family parity inputs + +This module is the Theory-side inventory for Spec-07. Each row owns the exact +raw declaration together with the one checked generation artifact consumed by +the kernel differential and environment-replay matrix. `Unit` is represented +honestly by the kernel's polymorphic `PUnit` family; the reducible `Unit` +definition itself is pinned on the Verify side. +-/ + +namespace Ix.Theory.Named.InductiveFixtures + +open VInductDecl + +private def permC07 (constant : VConstant) (levels : List VLevel) : + VConstant := + ⟨constant.uvars, constant.type.instL levels⟩ + +/-! ## Checked artifacts for the pre-existing fixed rows -/ + +def boolChecked : boolDecl.Checked := boolDecl.checked?.get (by decide) + +def boolGenerationChecked : GenerationChecked boolDecl := + (identityGeneration? boolDecl).get (by decide) + +def listChecked : listDecl.Checked := listDecl.checked?.get (by decide) + +def listGenerationChecked : GenerationChecked listDecl := + (identityGeneration? listDecl).get (by decide) + +def optionChecked : optionDecl.Checked := optionDecl.checked?.get (by decide) + +def optionGenerationChecked : GenerationChecked optionDecl := + (identityGeneration? optionDecl).get (by decide) + +def prodChecked : prodDecl.Checked := prodDecl.checked?.get (by decide) + +def prodGenerationChecked : GenerationChecked prodDecl := + (identityGeneration? prodDecl).get (by decide) + +def heqChecked : heqDecl.Checked := heqDecl.checked?.get (by decide) + +def heqGenerationChecked : GenerationChecked heqDecl := + (identityGeneration? heqDecl).get (by decide) + +def orGenerationChecked : GenerationChecked orDecl := + (identityGeneration? orDecl).get (by decide) + +def andGenerationChecked : GenerationChecked andDecl := + (identityGeneration? andDecl).get (by decide) + +/-! ## Fin -/ + +def finType : VInductiveType where + name := ``Fin + uvars := 0 + type := vconst(type_of% @Fin).type + ctors := [⟨vconst(type_of% @Fin.mk), ``Fin.mk⟩] + +def finDecl : VInductDecl := ⟨0, 1, [finType]⟩ + +def finChecked : finDecl.Checked := finDecl.checked?.get (by decide) + +def finGenerationChecked : GenerationChecked finDecl := + (identityGeneration? finDecl).get (by decide) + +example : finDecl.stage3 = true := rfl +example : finChecked.params = [.const ``Nat []] := rfl +example : finChecked.indices = [] := rfl +example : finChecked.resultLevel = .succ .zero := rfl +example : finChecked.elimination = .large := rfl +example : finChecked.kTarget = false := rfl +example : finChecked.constructors.length = 1 := rfl +example : finChecked.constructors[0].fields.length = 2 := rfl +example : finChecked.constructors[0].recursive = [] := rfl +example : finGenerationChecked.recursor = + vconst(type_of% @Fin.rec) := rfl + +/-! ## Vector -/ + +def vectorType : VInductiveType where + name := ``Vector + uvars := 1 + type := vconst(type_of% @Vector).type + ctors := [⟨vconst(type_of% @Vector.mk), ``Vector.mk⟩] + +def vectorDecl : VInductDecl := ⟨1, 2, [vectorType]⟩ + +def vectorChecked : vectorDecl.Checked := vectorDecl.checked?.get (by decide) + +def vectorGenerationChecked : GenerationChecked vectorDecl := + (identityGeneration? vectorDecl).get (by decide) + +example : vectorDecl.stage3 = true := rfl +example : vectorChecked.params = + [.sort (.succ (.param 0)), .const ``Nat []] := rfl +example : vectorChecked.indices = [] := rfl +example : vectorChecked.resultLevel = .succ (.param 0) := rfl +example : vectorChecked.elimination = .large := rfl +example : vectorChecked.kTarget = false := rfl +example : vectorChecked.constructors.length = 1 := rfl +example : vectorChecked.constructors[0].fields.length = 2 := rfl +example : vectorChecked.constructors[0].recursive = [] := rfl +example : vectorGenerationChecked.recursor = + permC07 (vconst(type_of% @Vector.rec)) [.param 1, .param 0] := rfl + +/-! ## One authoritative singleton artifact inventory -/ + +/-- A source-indexed singleton artifact. Merely constructing a row proves +that the public checked analyzer accepted that exact raw declaration and that +all downstream generation data came from the same checked path. -/ +structure SingletonParityArtifact where + label : Name + source : VInductDecl + generation : source.GenerationChecked + +namespace SingletonParityArtifact + +def typeName (artifact : SingletonParityArtifact) : Name := + artifact.generation.block.sourceType.name + +def constructorNames (artifact : SingletonParityArtifact) : List Name := + artifact.generation.block.sourceType.ctors.map (·.name) + +end SingletonParityArtifact + +/-- The fixed Spec-07 positive matrix, in roadmap order. The `Unit` row points +to `PUnit`, matching the actual v4.31 kernel representation rather than +inventing alias-level inductive metadata. -/ +def singletonPositiveArtifacts : List SingletonParityArtifact := + [⟨``Nat, natDecl, natGenerationChecked⟩, + ⟨``Bool, boolDecl, boolGenerationChecked⟩, + ⟨``List, listDecl, listGenerationChecked⟩, + ⟨``Option, optionDecl, optionGenerationChecked⟩, + ⟨``Prod, prodDecl, prodGenerationChecked⟩, + ⟨``Unit, punitDecl, punitGenerationChecked⟩, + ⟨``Empty, emptyDecl, emptyGenerationChecked⟩, + ⟨``Or, orDecl, orGenerationChecked⟩, + ⟨``And, andDecl, andGenerationChecked⟩, + ⟨``Eq, eqDecl, eqGenerationChecked⟩, + ⟨``HEq, heqDecl, heqGenerationChecked⟩, + ⟨``Fin, finDecl, finGenerationChecked⟩, + ⟨``Vector, vectorDecl, vectorGenerationChecked⟩, + ⟨``Acc, accDecl, accGenerationChecked⟩] + +/-- The focused non-identity normalization rows retained alongside the fixed +standard-library matrix. -/ +def singletonNormalizationArtifacts : List SingletonParityArtifact := + [⟨``AliasFormer, aliasFormerRawDecl, aliasFormerGenerationChecked⟩, + ⟨``AliasRec, aliasRecRawDecl, aliasRecGenerationChecked⟩, + ⟨``NormalizationMatrix, normalizationMatrixRawDecl, + normalizationMatrixGenerationChecked⟩, + ⟨``AnnotatedPi, annotatedPiRawDecl, annotatedPiGenerationChecked⟩, + ⟨``AnnotatedParam, annotatedParamRawDecl, + annotatedParamGenerationChecked⟩] + +example : singletonPositiveArtifacts.length = 14 := rfl +example : singletonPositiveArtifacts.map (·.label) = + [``Nat, ``Bool, ``List, ``Option, ``Prod, ``Unit, ``Empty, ``Or, ``And, + ``Eq, ``HEq, ``Fin, ``Vector, ``Acc] := rfl +example : singletonPositiveArtifacts.map (·.typeName) = + [``Nat, ``Bool, ``List, ``Option, ``Prod, ``PUnit, ``Empty, ``Or, ``And, + ``Eq, ``HEq, ``Fin, ``Vector, ``Acc] := rfl +example : singletonNormalizationArtifacts.length = 5 := rfl + +/-! +The inventory roots are trust-sensitive: pin their exact logical closure so +later fixture refactors cannot silently import a broader proof surface. +-/ + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.singletonPositiveArtifacts [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveFixtures.singletonNormalizationArtifacts [propext, Quot.sound] + +end Ix.Theory.Named.InductiveFixtures diff --git a/Ix/Theory/Named/Std/AxiomAudit.lean b/Ix/Theory/Named/Std/AxiomAudit.lean new file mode 100644 index 000000000..816ae3cbb --- /dev/null +++ b/Ix/Theory/Named/Std/AxiomAudit.lean @@ -0,0 +1,130 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Lean.Elab.Command +import Lean.Util.FoldConsts + +/-! +# Exact axiom checks for named-specification proofs + +The checked sets use resolved declaration names, independent of pretty-print +width and dependency traversal order. Both missing and additional axioms fail +the check; a namespace migration cannot silently widen the recorded boundary. +-/ + +open Lean Elab Command + +namespace Ix.Theory.Named.AxiomAudit + +/-- Traverse checked declarations directly. Imported axiom summaries can +omit dependencies of mutually recursive declaration groups. -/ +def directConstants : ConstantInfo → Array Name + | .axiomInfo v => v.type.getUsedConstants + | .defnInfo v => v.type.getUsedConstants ++ v.value.getUsedConstants + | .thmInfo v => v.type.getUsedConstants ++ v.value.getUsedConstants + | .opaqueInfo v => v.type.getUsedConstants ++ v.value.getUsedConstants + | .quotInfo _ => #[] + | .ctorInfo v => v.type.getUsedConstants + | .recInfo v => v.type.getUsedConstants + | .inductInfo v => v.type.getUsedConstants ++ v.ctors + +structure State where + visited : NameSet := {} + names : Array Name := #[] + origins : Array Name := #[] + axioms : Array Name := #[] + +structure Node where + isAxiom : Bool := false + dependencies : Array Name := #[] + +/-- Direct dependencies may be reused across roots in one fixed environment. +Reachability and axiom sets are always computed afresh for each root. -/ +abbrev Cache := NameMap Node + +abbrev M := ReaderT Environment (StateM (State × Cache)) + +partial def visit (name : Name) : M Unit := do + let (state, cache) ← get + unless state.visited.contains name do + let env ← read + let (node, cache) := match cache.find? name with + | some node => (node, cache) + | none => + let node : Node := match env.checked.get.find? name with + | some info => + { isAxiom := (info matches .axiomInfo _) + dependencies := directConstants info } + | none => {} + (node, cache.insert name node) + let state := { state with + visited := state.visited.insert name + names := state.names.push name + axioms := if node.isAxiom then state.axioms.push name else state.axioms + origins := if name != ``sorryAx && node.dependencies.contains ``sorryAx then + state.origins.push name else state.origins } + set (state, cache) + node.dependencies.forM visit + +def collectCached (env : Environment) (root : Name) (cache : Cache) : State × Cache := + ((visit root).run env).run ({}, cache) |>.2 + +def collect (env : Environment) (root : Name) : State := + (collectCached env root {}).1 + +end Ix.Theory.Named.AxiomAudit + +syntax (name := guardNamedAxioms) + "#guard_named_axioms " ident " [" ident,* "]" : command + +elab_rules : command + | `(#guard_named_axioms $root:ident [$axioms:ident,*]) => do + let rootName ← liftCoreM <| realizeGlobalConstNoOverloadWithInfo root + let expected ← axioms.getElems.mapM fun axiomSyntax => + liftCoreM <| realizeGlobalConstNoOverloadWithInfo axiomSyntax + let actual := (Ix.Theory.Named.AxiomAudit.collect (← getEnv) rootName).axioms + unless actual.qsort Name.lt == expected.qsort Name.lt do + let missing := expected.filter (!actual.contains ·) + let additional := actual.filter (!expected.contains ·) + throwError m!"axiom boundary changed for {rootName}\n\ + expected but absent: {missing}\nactual but unlisted: {additional}" + +-- Exercise both rejection directions as well as successful exact checks. +#guard_named_axioms Eq.refl [] +#guard_named_axioms propext [propext] + +-- A constructor depends on its full inductive definition, including siblings. +private inductive AuditFixture : Prop where + | plain + | withAxiom (proof : propext (Iff.rfl : True ↔ True) = rfl) + +#guard_named_axioms AuditFixture.plain [propext] + +run_cmd do + let env ← getEnv + let (first, cache) := Ix.Theory.Named.AxiomAudit.collectCached env + ``AuditFixture.plain {} + let (independent, cache) := Ix.Theory.Named.AxiomAudit.collectCached env ``Eq.refl cache + let (sibling, _) := Ix.Theory.Named.AxiomAudit.collectCached env + ``AuditFixture.withAxiom cache + unless first.axioms == #[``propext] && independent.axioms.isEmpty && + sibling.axioms == #[``propext] do + throwError "cached axiom traversal changed a root's dependency boundary" + +/-- +error: axiom boundary changed for propext +expected but absent: [] +actual but unlisted: [propext] +-/ +#guard_msgs (whitespace := lax) in +#guard_named_axioms propext [] + +/-- +error: axiom boundary changed for Eq.refl +expected but absent: [propext] +actual but unlisted: [] +-/ +#guard_msgs (whitespace := lax) in +#guard_named_axioms Eq.refl [propext] diff --git a/Ix/Theory/Named/Std/Basic.lean b/Ix/Theory/Named/Std/Basic.lean new file mode 100644 index 000000000..076320ac2 --- /dev/null +++ b/Ix/Theory/Named/Std/Basic.lean @@ -0,0 +1,269 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Batteries.CodeAction +import Batteries.Data.Array.Lemmas +import Batteries.Data.HashMap.Basic +import Batteries.Tactic.SeqFocus + +open Std + +/- +These are stdlib-shaped lemmas that live in the root namespace upstream (mathlib, or eventually +batteries/core). Declaring them under root here would make `import Mathlib` and `import Ix.Theory.Named` +conflict. + +So instead, we declare them in `namespace Ix.Theory.Named`. Consumers of this file need an +`open Ix.Theory.Named`, which is what keeps `h.length_eq`-style dot notation working. + +Beware also that `Ix.Theory.Named.List` now exists: an `open (scoped) List` under an `open Ix.Theory.Named` +resolves to it, so such opens are written `_root_.List`. +-/ +namespace Ix.Theory.Named +open List Ix.Theory.Named + +attribute [simp] Option.bind_eq_some_iff List.filterMap_cons + +theorem Option.beq_some_iff [BEq α] {a : Option α} {b : α} : + a == some b ↔ ∃ a', a = some a' ∧ a' == b := by cases a <;> simp + +protected theorem Nat.le_iff_exists_add {a b : Nat} : a ≤ b ↔ ∃ c, b = a + c := + ⟨fun h => ⟨_, (Nat.add_sub_cancel' h).symm⟩, fun ⟨_, h⟩ => h ▸ Nat.le_add_right ..⟩ + +protected theorem Nat.le_iff_exists_add' {a b : Nat} : a ≤ b ↔ ∃ c, b = c + a := by + simp [Nat.add_comm, Nat.le_iff_exists_add] + +protected theorem List.Forall₂.rfl + {R : α → α → Prop} {l : List α} (h : ∀ a ∈ l, R a a) : l.Forall₂ R l := by + induction l with + | nil => constructor + | cons _ _ ih => simp at h; exact .cons h.1 (ih h.2) + +@[simp] theorem List.forall₂_nil_left_iff {l} : Forall₂ R nil l ↔ l = nil := + ⟨fun H => by cases H; rfl, by rintro rfl; exact Forall₂.nil⟩ + +@[simp] theorem List.forall₂_nil_right_iff {l} : Forall₂ R l nil ↔ l = nil := + ⟨fun H => by cases H; rfl, by rintro rfl; exact Forall₂.nil⟩ + +theorem List.forall₂_cons_left_iff {a l u} : + Forall₂ R (a :: l) u ↔ ∃ b u', R a b ∧ Forall₂ R l u' ∧ u = b :: u' := + Iff.intro + (fun h => match u, h with | b :: u', Forall₂.cons h₁ h₂ => ⟨b, u', h₁, h₂, rfl⟩) + (fun h => match u, h with | _, ⟨_, _, h₁, h₂, rfl⟩ => Forall₂.cons h₁ h₂) + +theorem List.forall₂_cons_right_iff {b l u} : + Forall₂ R u (b :: l) ↔ ∃ a u', R a b ∧ Forall₂ R u' l ∧ u = a :: u' := + Iff.intro + (fun h => match u, h with | b :: u', Forall₂.cons h₁ h₂ => ⟨b, u', h₁, h₂, rfl⟩) + (fun h => match u, h with | _, ⟨_, _, h₁, h₂, rfl⟩ => Forall₂.cons h₁ h₂) + +theorem List.Forall₂.imp (H : ∀ a b, R a b → S a b) + {l₁ l₂} (h : Forall₂ R l₁ l₂) : Forall₂ S l₁ l₂ := by + induction h <;> constructor <;> [(apply H; assumption); assumption] + +theorem List.Forall₂.trans (H : ∀ a b c, R a b → S b c → T a c) + {l₁ l₂ l₃} (h₁ : Forall₂ R l₁ l₂) (h₂ : Forall₂ S l₂ l₃) : Forall₂ T l₁ l₃ := by + induction h₁ generalizing l₃ <;> cases h₂ <;> constructor <;> solve_by_elim + +theorem List.Forall₂.and {l₁ l₂} (h₁ : Forall₂ R l₁ l₂) (h₂ : Forall₂ S l₁ l₂) : + Forall₂ (fun x y => R x y ∧ S x y) l₁ l₂ := by induction h₁ <;> simp_all + +theorem List.Forall₂.and_mem {l₁ l₂} (H : Forall₂ R l₁ l₂) : + Forall₂ (fun x y => R x y ∧ x ∈ l₁ ∧ y ∈ l₂) l₁ l₂ := + .trans (fun _ _ _ h1 h2 => ⟨h1.1 ▸ h2.1, h1.2, h2.2⟩) + (.rfl (R := fun x y => x = y ∧ x ∈ l₁) fun _ h => ⟨rfl, h⟩) <| + .trans (T := fun x y => R x y ∧ y ∈ l₂) (fun _ _ _ h1 h2 => by exact ⟨h2.1 ▸ h1, h2.2⟩) H <| + .rfl (R := fun x y => x = y ∧ y ∈ l₂) fun _ h => ⟨rfl, h⟩ + +theorem List.Forall₂.zipWith_l {l₁ l₂} (H : ∀ a b, R a b → S a (f a b)) (h : Forall₂ R l₁ l₂) : + Forall₂ S l₁ (l₁.zipWith f l₂) := by induction h <;> simp [*] + +@[simp] theorem List.forall₂_map_left_iff {f : γ → α} : + ∀ {l u}, Forall₂ R (map f l) u ↔ Forall₂ (fun c b => R (f c) b) l u + | [], _ => by simp only [map, forall₂_nil_left_iff] + | a :: l, _ => by simp only [map, forall₂_cons_left_iff, forall₂_map_left_iff] + +@[simp] theorem List.forall₂_map_right_iff {f : γ → β} : + ∀ {l u}, Forall₂ R l (map f u) ↔ Forall₂ (fun a c => R a (f c)) l u + | _, [] => by simp only [map, forall₂_nil_right_iff] + | _, b :: u => by simp only [map, forall₂_cons_right_iff, forall₂_map_right_iff] + +theorem List.Forall₂.flip : ∀ {a b}, Forall₂ (flip R) b a → Forall₂ R a b + | _, _, .nil => .nil + | _, _, .cons h₁ h₂ => .cons h₁ (flip h₂) + +theorem List.Forall₂.forall_exists_l {l₁ l₂} (h : Forall₂ R l₁ l₂) : ∀ a ∈ l₁, ∃ b ∈ l₂, R a b := by + induction h with simp [*] | cons _ _ ih => exact fun a h => .inr (ih _ h) + +theorem List.Forall₂.forall_exists_r {l₁ l₂} (h : Forall₂ R l₁ l₂) : ∀ b ∈ l₂, ∃ a ∈ l₁, R a b := + h.flip.forall_exists_l + +theorem List.Forall₂.length_eq : ∀ {l₁ l₂}, Forall₂ R l₁ l₂ → length l₁ = length l₂ + | _, _, .nil => rfl + | _, _, .cons _ h₂ => congrArg Nat.succ <| Forall₂.length_eq h₂ + +theorem List.forall₂_eq {l₁ l₂ : List α} : Forall₂ Eq l₁ l₂ ↔ l₁ = l₂ := + ⟨fun h => by induction h <;> simp_all, (· ▸ .rfl fun _ _ => rfl)⟩ + +theorem List.Forall₂.append_of_left : ∀ {l₁ l₂ r₁ r₂}, length l₁ = length l₂ → + (Forall₂ R (l₁ ++ r₁) (l₂ ++ r₂) ↔ Forall₂ R l₁ l₂ ∧ Forall₂ R r₁ r₂) + | [], [], _, _, _ => by simp + | a::l₁, b::l₂, _, _, eq => by simp [append_of_left (Nat.succ_inj.1 eq), and_assoc] + +theorem List.Forall₂.append_of_right {l₁ l₂ r₁ r₂} (H : length r₁ = length r₂) : + Forall₂ R (l₁ ++ r₁) (l₂ ++ r₂) ↔ Forall₂ R l₁ l₂ ∧ Forall₂ R r₁ r₂ := by + refine ⟨fun h => (append_of_left ?_).1 h, fun h => (append_of_left h.1.length_eq).2 h⟩ + simpa [H] using h.length_eq + +theorem List.Forall₂.reverse : Forall₂ R l.reverse l'.reverse ↔ Forall₂ R l l' := by + induction l generalizing l' <;> cases l' <;> simp [List.Forall₂.append_of_right, and_comm, *] + +theorem List.map_id''' {f : α → α} (l : List α) (h : ∀ x ∈ l, f x = x) : map f l = l := by + induction l <;> simp_all + +theorem List.map_fst_lookup {f : α → β} [BEq β] (l : List α) (b : β) : + (l.map (fun a => (f a, a))).lookup b = l.find? fun a => b == f a := by + induction l <;> simp_all [lookup, find?] + +def List.All (P : α → Prop) : List α → Prop + | [] => True + | a::as => P a ∧ All P as + +theorem List.All.imp {P Q : α → Prop} (h : ∀ a, P a → Q a) : ∀ {l : List α}, l.All P → l.All Q + | [] => id + | _::_ => And.imp (h _) (List.All.imp h) + +theorem List.append_eq_append_of_length_le {a b c d : List α} (h : length a ≤ length c) : + a ++ b = c ++ d ↔ ∃ a', c = a ++ a' ∧ b = a' ++ d := by + rw [append_eq_append_iff, or_iff_left_iff_imp] + rintro ⟨c', rfl, rfl⟩ + rw [← Nat.add_zero c.length, length_append, + Nat.add_le_add_iff_left, Nat.le_zero, length_eq_zero_iff] at h + subst h; exact ⟨[], by simp⟩ + +@[simp] theorem List.nodup_reverse {l : List α} : Nodup (reverse l) ↔ Nodup l := + pairwise_reverse.trans <| by simp only [Nodup, Ne, eq_comm] + +theorem List.foldl_congr + (H : ∀ a, ∀ x ∈ l, f a x = g a x) : foldl f a l = foldl g a l := by + induction l generalizing a <;> simp_all + +theorem List.idxOf_eq_length_iff [BEq α] [LawfulBEq α] + {a : α} {l : List α} : idxOf a l = length l ↔ a ∉ l := by + induction l with + | nil => exact iff_of_true rfl not_mem_nil + | cons b l ih => + simp only [length, mem_cons, idxOf_cons] + rw [cond_eq_ite] + split <;> rename_i h <;> simp at h + · exact iff_of_false (by rintro ⟨⟩) fun H => H <| Or.inl h.symm + · simp only [Ne.symm h, false_or] + rw [← ih] + exact Nat.succ_inj + +theorem List.perm_cons_of_mem {l : List α} (h : a ∈ l) : ∃ l', l.Perm (a :: l') := by + obtain ⟨l₁, l₂, rfl⟩ := List.append_of_mem h; exact ⟨_, List.perm_middle⟩ + +instance [BEq α] [LawfulBEq α] : PartialEquivBEq α where + symm h := by simp at *; exact h.symm + trans h1 h2 := by simp at *; exact h1.trans h2 + +theorem beq_comm [BEq α] [PartialEquivBEq α] (a b : α) : (a == b) = (b == a) := + Bool.eq_iff_iff.2 ⟨PartialEquivBEq.symm, PartialEquivBEq.symm⟩ + +theorem List.mapM_eq_some {f : α → Option β} {l : List α} {l' : List β} : + l.mapM f = some l' ↔ List.Forall₂ (f · = some ·) l l' := by + induction l generalizing l' with + | nil => simp only [mapM_nil, pure, Option.some.injEq, forall₂_nil_left_iff, @eq_comm _ l'] + | cons x l ih => + simp [mapM_cons, Bind.bind, pure, Option.bind_eq_some_iff, Option.some.injEq, + forall₂_cons_left_iff, @eq_comm _ l', exists_and_left, ih] + +@[simp] theorem Option.bind_eq_none'' {o : Option α} {f : α → Option β} : + o.bind f = none ↔ ∀ a, o = some a → f a = none := by cases o <;> simp + +@[simp] theorem Option.forall_ne_some {o : Option α} : (∀ a, o ≠ some a) ↔ o = none := by + cases o <;> simp + +@[simp] theorem Option.orElse_eq_none {a : Option α} {b : Unit → Option α} : + a.orElse b = none ↔ a = none ∧ b () = none := by + cases a <;> simp [Option.orElse] + +inductive ReflTransGen (R : α → α → Prop) (a : α) : α → Prop where + | rfl : ReflTransGen R a a + | tail : ReflTransGen R a b → R b c → ReflTransGen R a c + +inductive ReflTransGen' (R : α → α → Prop) (c : α) : α → Prop where + | rfl : ReflTransGen' R c c + | head : R a b → ReflTransGen' R c b → ReflTransGen' R c a + +theorem ReflTransGen.trans + (H1 : ReflTransGen R a b) (H2 : ReflTransGen R b c) : ReflTransGen R a c := by + induction H2 with + | rfl => exact H1 + | tail h1 h2 ih => exact ih.tail h2 + +@[elab_as_elim] theorem ReflTransGen.headIndOn {P : (a : α) → ReflTransGen R a z → Prop} + (rfl : P z .rfl) + (head : ∀ {x y} (h1 : R x y) (h2 : ReflTransGen R y z), + P y h2 → P x (.trans (.tail .rfl h1) h2)) + (H : ReflTransGen R a z) : P a H := by + induction H with + | rfl => exact rfl + | tail h1 h2 ih => exact ih (head h2 .rfl rfl) fun a1 a2 => head a1 (.tail a2 h2) + +instance [BEq α] [PartialEquivBEq α] [BEq β] [PartialEquivBEq β] : PartialEquivBEq (α × β) where + symm := by simp [(· == ·)]; grind [BEq.symm] + trans := by simp [(· == ·)]; grind [BEq.trans] + +instance [BEq α] [EquivBEq α] [BEq β] [EquivBEq β] : EquivBEq (α × β) where + rfl := by simp [(· == ·)] + +instance [BEq α] [Hashable α] [LawfulHashable α] [BEq β] [Hashable β] [LawfulHashable β] : + LawfulHashable (α × β) where + hash_eq a b h := by + simp [(· == ·)] at h + simp [hash, LawfulHashable.hash_eq _ _ h.1, LawfulHashable.hash_eq _ _ h.2] + +instance [BEq α] [PartialEquivBEq α] : PartialEquivBEq (List α) where + symm := by + simp [(· == ·)]; intro a b + induction a generalizing b <;> cases b <;> simp [List.beq]; grind [BEq.symm] + trans := by + simp [(· == ·)]; intro a b c + induction a generalizing b c <;> cases b <;> simp [List.beq] + cases c <;> simp [List.beq]; grind [BEq.trans] + +instance [BEq α] [EquivBEq α] : EquivBEq (List α) where + rfl {a} := by simp [(· == ·)]; induction a <;> simp [List.beq, *] + +end Ix.Theory.Named + +namespace BitVec + +variable (n : Nat) + +instance : TransOrd (BitVec n) := + TransOrd.compareOfLessAndEq_of_antisymm_of_trans_of_total_of_not_le + BitVec.le_antisymm BitVec.le_trans BitVec.le_total BitVec.not_le + +instance : LawfulEqOrd (BitVec n) where + eq_of_compare := compareOfLessAndEq_eq_eq BitVec.le_refl BitVec.not_le |>.mp + +end BitVec + +namespace UInt64 + +variable (n : Nat) + +instance : TransOrd UInt64 := + TransOrd.compareOfLessAndEq_of_antisymm_of_trans_of_total_of_not_le + UInt64.le_antisymm UInt64.le_trans UInt64.le_total UInt64.not_le + +instance : LawfulEqOrd UInt64 where + eq_of_compare := compareOfLessAndEq_eq_eq UInt64.le_refl UInt64.not_le |>.mp + +end UInt64 diff --git a/Ix/Theory/Named/Std/Control.lean b/Ix/Theory/Named/Std/Control.lean new file mode 100644 index 000000000..b515508ac --- /dev/null +++ b/Ix/Theory/Named/Std/Control.lean @@ -0,0 +1,8 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + + +instance [Alternative m] : MonadLift Option m := ⟨fun | none => failure | some a => pure a⟩ diff --git a/Ix/Theory/Named/Std/HashMap.lean b/Ix/Theory/Named/Std/HashMap.lean new file mode 100644 index 000000000..4b24f52a2 --- /dev/null +++ b/Ix/Theory/Named/Std/HashMap.lean @@ -0,0 +1,105 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Std.Data.HashMap.Lemmas +import Lean + +namespace Std.Internal.List + +namespace Const + +def containsKey [BEq α] (a : α) : List (α × β) → Bool + | [] => false + | ⟨k, _⟩ :: l => k == a || containsKey a l + +def replaceEntry [BEq α] (k : α) (v : β) : List (α × β) → List (α × β) + | [] => [] + | ⟨k', v'⟩ :: l => bif k' == k then ⟨k, v⟩ :: l else ⟨k', v'⟩ :: replaceEntry k v l + +def insertEntry [BEq α] (k : α) (v : β) (l : List (α × β)) : List (α × β) := + bif containsKey k l then replaceEntry k v l else (k, v) :: l + +theorem insertEntry_perm_filter [BEq α] [PartialEquivBEq α] + (k : α) (v : β) {l : List (α × β)} (hl : l.Pairwise (fun a b => (a.1 == b.1) = false)) : + (insertEntry k v l).Perm <| (k, v) :: l.filter (¬k == ·.1) := by + unfold insertEntry + cases eq : containsKey k l <;> simp + · rw [List.filter_eq_self.2] + induction l <;> simp_all [containsKey, BEq.comm]; assumption + · induction l <;> simp_all [-List.pairwise_cons, containsKey, replaceEntry, List.filter, BEq.comm] + revert eq; cases h : k == _ <;> intro eq <;> simp_all + rename_i ih + · exact .trans (.cons _ ih) (.swap ..) + · rw [List.filter_eq_self.2] + intro ⟨a, b⟩ h' + have := hl.1 a b h' + refine Decidable.by_contra fun h2 => ?_ + simp at h2 + cases h2.symm.trans (BEq.congr_left h ▸ this) + +end Const + +theorem Const.containsKey_map [BEq α] (l : List ((_ : α) × β)) : + Const.containsKey k (l.map (fun x => (x.1, x.2))) = List.containsKey k l := by + induction l <;> simp [List.containsKey, Const.containsKey, *] + +theorem Const.replaceEntry_map [BEq α] (l : List ((_ : α) × β)) : + Const.replaceEntry k v (l.map (fun x => (x.1, x.2))) = + (List.replaceEntry k v l).map (fun x => (x.1, x.2)) := by + induction l <;> simp [List.replaceEntry, Const.replaceEntry, *] + cases _ == k <;> simp + +theorem Const.insertEntry_map [BEq α] (l : List ((_ : α) × β)) : + Const.insertEntry k v (l.map (fun x => (x.1, x.2))) = + (List.insertEntry k v l).map (fun x => (x.1, x.2)) := by + simp [List.insertEntry, Const.insertEntry, Const.containsKey_map, Const.replaceEntry_map] + cases List.containsKey k l <;> simp + +end Std.Internal.List + +namespace Std.DHashMap +open Std.Internal.List Internal.Raw Internal.Raw₀ + +variable [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] + +theorem insert_perm_insertEntry (m : DHashMap α β) (a b) : + (insert m a b).toList.Perm (insertEntry a b m.toList) := by + simp [toList, toList_eq_toListModel] + exact toListModel_insert (WF.out m.2) + +theorem Const.insert_perm_insertEntry (m : DHashMap α fun _ => β) (a b) : + (Const.toList (insert m a b)).Perm (Const.insertEntry a b (Const.toList m)) := by + simp [Const.toList, Const.toList_eq_toListModel_map, Const.insertEntry_map] + exact (toListModel_insert (WF.out m.2)).map _ + +end Std.DHashMap + +namespace Std.HashMap +open Internal.List + +variable [BEq α] [Hashable α] [LawfulHashable α] + +theorem insert_toList [EquivBEq α] (m : HashMap α β) : + (insert m a b).toList.Perm ((a, b) :: m.toList.filter (¬a == ·.1)) := by + refine (DHashMap.Const.insert_perm_insertEntry m.1 a b).trans ?_ + exact Const.insertEntry_perm_filter _ _ distinct_keys_toList + +theorem getElem?_eq_lookup_toList [LawfulBEq α] (m : HashMap α β) (a : α) : + m[a]? = m.toList.lookup a := by + apply Option.ext fun b => ?_ + rw [← mem_toList_iff_getElem?_eq_some] + have := distinct_keys_toList (m := m); revert this + induction m.toList <;> intro H <;> simp_all [List.lookup] + split <;> simp_all [Prod.ext_iff] + simp [eq_comm] + rw [List.lookup_eq_none_iff.2]; · simp + simp; exact H.1 + +theorem mem_of_getElem? [EquivBEq α] {m : HashMap α β} (h : m[a]? = some b) : a ∈ m := by + refine Decidable.by_contra fun h' => ?_ + cases (getElem?_eq_none h').symm.trans h + +end Std.HashMap diff --git a/Ix/Theory/Named/Std/NodupKeys.lean b/Ix/Theory/Named/Std/NodupKeys.lean new file mode 100644 index 000000000..62418056c --- /dev/null +++ b/Ix/Theory/Named/Std/NodupKeys.lean @@ -0,0 +1,45 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Batteries.Data.List.Perm +import Batteries.Tactic.SeqFocus + +namespace List +def NodupKeys (l : List (α × β)) : Prop := Nodup (l.map (·.1)) + +theorem nodupKeys_iff_pairwise {l : List (α × β)} : + NodupKeys l ↔ Pairwise (fun a b => a.1 ≠ b.1) l := pairwise_map + +theorem NodupKeys.filter {l : List (α × β)} (p : α × β → Bool) + (H : NodupKeys l) : NodupKeys (l.filter p) := pairwise_map.2 <| (pairwise_map.1 H).filter _ + +@[simp] theorem nodupKeys_cons : NodupKeys (a::l) ↔ (∀ a' ∈ l, a.1 ≠ a'.1) ∧ NodupKeys l := by + simp [nodupKeys_iff_pairwise] + +theorem Perm.nodupKeys_iff {l₁ l₂ : List (α × β)} (h : l₁ ~ l₂) : + NodupKeys l₁ ↔ NodupKeys l₂ := (h.map _).nodup_iff + +theorem Perm.lookup_eq [BEq α] [LawfulBEq α] + {l₁ l₂ : List (α × β)} (h : l₁ ~ l₂) (nd : l₁.NodupKeys) : l₁.lookup a = l₂.lookup a := by + induction h with + | nil => rfl + | cons _ _ ih => simp [lookup]; split <;> [rfl; exact ih (pairwise_cons.1 nd).2] + | swap _ h ih => + simp at nd + simp [lookup]; split <;> split <;> rename_i h1 _ h2 <;> try rfl + simp at h1 h2; cases nd.1.1 (by rw [← h1, h2]) + | trans h1 _ ih1 ih2 => exact (ih1 nd).trans (ih2 (h1.nodupKeys_iff.1 nd)) + +theorem NodupKeys.lookup_eq_some [BEq α] [LawfulBEq α] + {l : List (α × β)} (nd : l.NodupKeys) : l.lookup a = some b ↔ (a, b) ∈ l := by + induction l with + | nil => simp + | cons p l ih => + let (a', b') := p + simp [lookup] at nd ⊢; split <;> rename_i h + · simp at h; subst a'; simp [eq_comm] + exact (nomatch nd.1 _ _ · rfl) + · simp at h; simp [h, ih nd.2] diff --git a/Ix/Theory/Named/Std/Ord.lean b/Ix/Theory/Named/Std/Ord.lean new file mode 100644 index 000000000..ba02e49f8 --- /dev/null +++ b/Ix/Theory/Named/Std/Ord.lean @@ -0,0 +1,58 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Init.Data.Order.Ord + +/-! +A device for proving `Std.TransCmp` for comparisons defined by *lexicographic products*, used for +`Lean.Name.cmp` in `Ix.Theory.Named.Verify.Name` and for the level order in `Ix.Theory.Named.Verify.NormLt`. + +Transitivity of a lexicographic product needs more than transitivity of its components: when the +first components compare `.eq` one has to know they compare `.eq` *in both directions* before the +second components can be consulted. Recursive comparisons therefore do not prove `isLE_trans` at a +triple `(a, b, c)` from `isLE_trans` at the sub-triple alone; one needs its rotations too. `Rot` +below packages the three rotations, which is exactly the statement that goes through the induction. +-/ + +namespace Ix.Theory.Named +open Std + +/-- The three rotations of transitivity for a comparison at a triple `a`, `b`, `c`, where +`x = cmp a b`, `y = cmp b c`, `z = cmp a c`. Note that the second and third components are the +first one at the rotated triples `(c, a, b)` and `(b, c, a)`, rewritten with `Ordering.swap`. -/ +def Rot (x y z : Ordering) : Prop := + (x.isLE → y.isLE → z.isLE) ∧ + (z.swap.isLE → x.isLE → y.swap.isLE) ∧ + (y.isLE → z.swap.isLE → x.swap.isLE) + +/-- Lexicographic products satisfy `Rot` if the components do. The second component's rotations +are only required when the first components are all `.eq`; this matters when the second component +is a comparison that is only meaningful where the first component does not already decide, as for +the level order, whose structural component compares unrelated constructors as `.eq`. -/ +theorem Rot.then' : Rot x y z → (x = .eq → y = .eq → z = .eq → Rot x' y' z') → + Rot (x.then x') (y.then y') (z.then z') := by + cases x <;> cases y <;> simp_all [Rot]; cases z <;> simp + +theorem Rot.then {x y z x' y' z' : Ordering} + (R : Rot x y z) (R' : Rot x' y' z') : Rot (x.then x') (y.then y') (z.then z') := + R.then' fun _ _ _ => R' + +/-- Any `TransCmp` gives `Rot` at every triple: the rotations are `isLE_trans` at the rotated +triples, rewritten with `OrientedCmp.eq_swap`. -/ +theorem Rot.of_transCmp {α} {cmp : α → α → Ordering} [TransCmp cmp] (a b c : α) : + Rot (cmp a b) (cmp b c) (cmp a c) := by + refine ⟨fun h₁ h₂ => TransCmp.isLE_trans h₁ h₂, fun h₁ h₂ => ?_, fun h₁ h₂ => ?_⟩ <;> + rw [← OrientedCmp.eq_swap (cmp := cmp)] at * <;> + exact TransCmp.isLE_trans h₁ h₂ + +/-- `Rot` at every triple, plus orientedness, is exactly `TransCmp`. -/ +theorem TransCmp.of_rot {α} {cmp : α → α → Ordering} + (swap : ∀ a b : α, cmp a b = (cmp b a).swap) + (rot : ∀ a b c : α, Rot (cmp a b) (cmp b c) (cmp a c)) : TransCmp cmp where + eq_swap := swap .. + isLE_trans h₁ h₂ := (rot ..).1 h₁ h₂ + +end Ix.Theory.Named diff --git a/Ix/Theory/Named/Std/PersistentHashMap.lean b/Ix/Theory/Named/Std/PersistentHashMap.lean new file mode 100644 index 000000000..b5c3cff01 --- /dev/null +++ b/Ix/Theory/Named/Std/PersistentHashMap.lean @@ -0,0 +1,48 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Lean.Data.SMap +import Std.Data.HashMap.Lemmas +import Ix.Theory.Named.Verify.Axioms + +namespace Lean.PersistentHashMap + +@[simp] theorem toList'_empty [BEq α] [Hashable α] : + (.empty : PersistentHashMap α β).toList' = [] := by + have this n : @Node.toList' α β (.entries ⟨.replicate n .null⟩) = [] := by + simp [Node.toList'] + induction n <;> simp [*, List.replicate_succ] + apply this + +@[simp] theorem toList'_empty' [BEq α] [Hashable α] : + ({} : PersistentHashMap α β).toList' = [] := toList'_empty + +theorem find?_isSome {α β} [BEq α] [Hashable α] + (m : PersistentHashMap α β) (a : α) : m.contains a = (m.find? a).isSome := findAux_isSome .. + +theorem WF.nodupKeys [BEq α] [Hashable α] + [LawfulBEq α] [LawfulHashable α] + {m : PersistentHashMap α β} (h : WF m) : m.toList'.NodupKeys := by + induction h with + | empty => simp; exact .nil + | insert h1 ih => + refine (h1.toList'_insert ..).nodupKeys_iff.2 (List.nodupKeys_cons.2 ⟨?_, ih.filter _⟩) + rintro _ h3 rfl + simpa using (List.mem_filter.1 h3).2 + +variable [BEq α] [LawfulBEq α] [Hashable α] [LawfulHashable α] in +theorem WF.find?_insert {s : PersistentHashMap α β} (h : s.WF) : + (s.insert k v).find? x = if k == x then some v else s.find? x := by + rw [h.insert.find?_eq, h.find?_eq, BEq.comm, + (h.toList'_insert ..).lookup_eq h.insert.nodupKeys, List.lookup] + cases eq : x == k <;> simp + induction s.toList' with + | nil => rfl + | cons kv l ih => + simp [List.filter]; split <;> simp [List.lookup, *] + split <;> [skip; rfl] + rename_i h1 _ h2 + simp at h1 h2; simp [h1, h2] at eq diff --git a/Ix/Theory/Named/Std/SMap.lean b/Ix/Theory/Named/Std/SMap.lean new file mode 100644 index 000000000..592aacf7d --- /dev/null +++ b/Ix/Theory/Named/Std/SMap.lean @@ -0,0 +1,83 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Lean.Data.SMap +import Std.Data.HashMap.Lemmas +import Ix.Theory.Named.Std.HashMap +import Ix.Theory.Named.Std.PersistentHashMap + +namespace Lean.SMap + +variable [BEq α] [Hashable α] +structure WF (s : SMap α β) where + map₂ : s.map₂.WF + stage : s.stage₁ → s.map₂ = .empty + disjoint : s.map₂.contains a → ¬a ∈ s.map₁ + +theorem WF.empty : WF ({} : SMap α β) := ⟨.empty, fun _ => rfl, by simp⟩ + +protected nonrec theorem WF.insert [LawfulBEq α] [LawfulHashable α] {s : SMap α β} + (h : s.WF) (k : α) (v : β) (hn : s.find? k = none) : (s.insert k v).WF := by + unfold insert; split + · refine ⟨h.map₂, h.stage, fun _ h' => ?_⟩ + have := h.stage + simp_all [find?, PersistentHashMap.find?_isSome, PersistentHashMap.WF.empty.find?_eq] + · refine ⟨h.map₂.insert .., nofun, fun h1 => ?_⟩ + simp_all [PersistentHashMap.find?_isSome, h.map₂.find?_insert] + revert ‹_›; split + · simp_all [find?] + · exact h.disjoint ∘ by simp [PersistentHashMap.find?_isSome] + +variable [LawfulBEq α] [LawfulHashable α] in +theorem WF.find?_insert {s : SMap α β} (h : s.WF) : + (s.insert k v).find? x = if k == x then some v else s.find? x := by + unfold insert; split <;> simp [find?] + · exact Std.HashMap.getElem?_insert (α := α) + · rw [h.map₂.find?_insert]; split <;> rfl + +noncomputable def toList' [BEq α] [Hashable α] (m : SMap α β) : + List (α × β) := m.map₂.toList' ++ m.map₁.toList + +open scoped _root_.List in +theorem WF.toList'_insert {α β} [BEq α] [LawfulBEq α] [Hashable α] [LawfulHashable α] + {m : SMap α β} (wf : WF m) (a : α) (b : β) + (h : m.find? a = none) : + (m.insert a b).toList' ~ (a, b) :: m.toList' := by + unfold insert; split <;> simp [toList'] + · have : EquivBEq α := inferInstance; clear ‹LawfulBEq _› + have := wf.stage rfl; simp [find?] at this h; subst this; simp + refine (List.filter_eq_self.2 ?_ ▸ Std.HashMap.insert_toList (α := α) .. :) + rintro ⟨a', b⟩ h + refine Decidable.by_contra fun h2 => ?_ + simp at h2 + have := Std.HashMap.getElem?_eq_some_iff_exists_beq_and_mem_toList.2 ⟨_, h2, h⟩ + exact ‹¬_› (Std.HashMap.mem_of_getElem? this) + · refine .append_right (l₂ := _::_) _ ?_ + refine (List.filter_eq_self.2 ?_ ▸ wf.map₂.toList'_insert .. :) + rintro ⟨a', b⟩ h' + have := wf.map₂.find?_eq a + simp_all [find?]; rintro rfl + exact h.1 _ _ h' rfl + +theorem WF.find?_eq {α β} [BEq α] [Hashable α] [LawfulBEq α] [LawfulHashable α] + {m : SMap α β} (wf : WF m) (a : α) : m.find? a = m.toList'.lookup a := by + simp [find?]; split + · cases wf.stage rfl + simp [toList'] + exact Std.HashMap.getElem?_eq_lookup_toList .. + · simp [toList', List.lookup_append, wf.map₂.find?_eq] + cases List.lookup a .. <;> simp [Std.HashMap.getElem?_eq_lookup_toList] + +theorem WF.find?'_eq_find? {α β} [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] + {m : SMap α β} (wf : WF m) (a : α) : m.find?' a = m.find? a := by + simp [find?, find?']; split; · rfl + rename_i m₁ m₂ + cases e1 : m₁[a]? <;> cases e2 : m₂.find? a <;> simp + cases wf.disjoint (by simp [PersistentHashMap.find?_isSome, e2]) (Std.HashMap.mem_of_getElem? e1) + +theorem find?_isSome {α β} [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α] + (m : SMap α β) (a : α) : m.contains a = (m.find? a).isSome := by + simp [find?, contains]; split <;> simp [← PersistentHashMap.find?_isSome, Bool.or_comm] diff --git a/Ix/Theory/Named/Std/ToExpr.lean b/Ix/Theory/Named/Std/ToExpr.lean new file mode 100644 index 000000000..10f05c232 --- /dev/null +++ b/Ix/Theory/Named/Std/ToExpr.lean @@ -0,0 +1,73 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +/- +Copyright (c) 2023 Kyle Miller. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kyle Miller +-/ +import Lean + +/-! # `ToExpr` instances for Mathlib + +This module should be imported by any module that intends to define `ToExpr` instances. +It provides necessary dependencies (the `Lean.ToLevel` class) and it also overrides the instances +that come from core Lean 4 that do not handle universe polymorphism. +(See the module `Lean.ToExpr` for the instances that are overridden.) + +In addition, we provide some additional `ToExpr` instances for core definitions. +-/ + +set_option autoImplicit true + +namespace Mathlib +open Lean + +deriving instance ToExpr for Int + +deriving instance ToExpr for ULift + +/-- Hand-written instance since `PUnit` is a `Sort` rather than a `Type`. -/ +instance [ToLevel.{u}] : ToExpr PUnit.{u+1} where + toExpr _ := mkConst ``PUnit.unit [toLevel.{u+1}] + toTypeExpr := mkConst ``PUnit [toLevel.{u+1}] + +deriving instance ToExpr for String.Pos.Raw +deriving instance ToExpr for Substring.Raw +deriving instance ToExpr for SourceInfo +deriving instance ToExpr for Syntax.Preresolved +deriving instance ToExpr for Syntax + +open DataValue in +/-- Core of a hand-written `ToExpr` handler for `MData`. +Uses the `KVMap.set*` functions rather than going into the internals +of the `KVMap` data structure. -/ +private def toExprMData (md : MData) : Expr := Id.run do + let mut e := mkConst ``MData.empty + for (k, v) in md do + let k := toExpr k + e := match v with + | ofString v => mkApp3 (mkConst ``KVMap.setString) e k (mkStrLit v) + | ofBool v => mkApp3 (mkConst ``KVMap.setBool) e k (toExpr v) + | ofName v => mkApp3 (mkConst ``KVMap.setName) e k (toExpr v) + | ofNat v => mkApp3 (mkConst ``KVMap.setNat) e k (mkNatLit v) + | ofInt v => mkApp3 (mkConst ``KVMap.setInt) e k (toExpr v) + | ofSyntax v => mkApp3 (mkConst ``KVMap.setSyntax) e k (toExpr v) + return e + +instance : ToExpr MData where + toExpr := toExprMData + toTypeExpr := mkConst ``MData + +deriving instance ToExpr for FVarId +deriving instance ToExpr for MVarId +deriving instance ToExpr for LevelMVarId +deriving instance ToExpr for Level +deriving instance ToExpr for BinderInfo +deriving instance ToExpr for Literal +deriving instance ToExpr for Expr + +end Mathlib diff --git a/Ix/Theory/Named/Std/VariableBang.lean b/Ix/Theory/Named/Std/VariableBang.lean new file mode 100644 index 000000000..6adf62d4d --- /dev/null +++ b/Ix/Theory/Named/Std/VariableBang.lean @@ -0,0 +1,22 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Lean + +macro "variable!" args:(ppSpace colGt bracketedBinder)+ " in" + cmd:ppDedent(command) : command => do + let mut incls := #[] + for a in args do + match a with + | `(bracketedBinder| ($as* $[: $_]?)) + | `(bracketedBinder| {$as* $[: $_]?}) + | `(bracketedBinder| ⦃$as* $[: $_]?⦄) => + for a in as do + match a with | `(_) => pure () | _ => incls := incls.push ⟨a.raw⟩ + | `(bracketedBinder| [$a : $_]) => + match a with | `(_) => pure () | _ => incls := incls.push ⟨a.raw⟩ + | _ => pure () + `(command| variable $args* in include $incls* in $(⟨cmd.raw⟩)) diff --git a/Ix/Theory/Named/Typing/Basic.lean b/Ix/Theory/Named/Typing/Basic.lean new file mode 100644 index 000000000..fe962ac82 --- /dev/null +++ b/Ix/Theory/Named/Typing/Basic.lean @@ -0,0 +1,141 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.VEnv + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named +open Ix.Theory.Named + +inductive Lookup : List VExpr → Nat → VExpr → Prop where + | zero : Lookup (ty::Γ) 0 ty.lift + | succ : Lookup Γ n ty → Lookup (A::Γ) (n+1) ty.lift + +/-- A context-wide predicate, exposing each binder in its preceding context. -/ +def OnCtx (Γ : List VExpr) (P : List VExpr → VExpr → Prop) : Prop := + match Γ with + | [] => True + | A::Γ => OnCtx Γ P ∧ P Γ A + +namespace VEnv + +section +set_option hygiene false +local notation:65 Γ " ⊢ " e " : " A:30 => IsDefEq Γ e e A +local notation:65 Γ " ⊢ " e1 " ≡ " e2 " : " A:30 => IsDefEq Γ e1 e2 A +variable (env : VEnv) (uvars : Nat) + +mutual + +inductive IsDefEq : List VExpr → VExpr → VExpr → VExpr → Prop where + | bvar : Lookup Γ i A → Γ ⊢ .bvar i : A + | symm : Γ ⊢ e ≡ e' : A → Γ ⊢ e' ≡ e : A + | trans : Γ ⊢ e₁ ≡ e₂ : A → Γ ⊢ e₂ ≡ e₃ : A → Γ ⊢ e₁ ≡ e₃ : A + | sortDF : + l.WF uvars → l'.WF uvars → l ≈ l' → + Γ ⊢ .sort l ≡ .sort l' : .sort (.succ l) + | constDF : + env.constants c = some ci → + (∀ l ∈ ls, l.WF uvars) → + (∀ l ∈ ls', l.WF uvars) → + ls.length = ci.uvars → + List.Forall₂ (· ≈ ·) ls ls' → + Γ ⊢ .const c ls ≡ .const c ls' : ci.type.instL ls + | appDF : + Γ ⊢ f ≡ f' : .forallE A B → + Γ ⊢ a ≡ a' : A → + Γ ⊢ .app f a ≡ .app f' a' : B.inst a + | lamDF : + Γ ⊢ A ≡ A' : .sort u → + A::Γ ⊢ body ≡ body' : B → + Γ ⊢ .lam A body ≡ .lam A' body' : .forallE A B + | forallEDF : + Γ ⊢ A ≡ A' : .sort u → + A::Γ ⊢ body ≡ body' : .sort v → + Γ ⊢ .forallE A body ≡ .forallE A' body' : .sort (.imax u v) + | defeqDF : Γ ⊢ A ≡ B : .sort u → Γ ⊢ e1 ≡ e2 : A → Γ ⊢ e1 ≡ e2 : B + | beta : + A::Γ ⊢ e : B → Γ ⊢ e' : A → + Γ ⊢ .app (.lam A e) e' ≡ e.inst e' : B.inst e' + | eta : + Γ ⊢ e : .forallE A B → + Γ ⊢ .lam A (.app e.lift (.bvar 0)) ≡ e : .forallE A B + | structEta : + env.structEtas rule → + (∀ level ∈ levels, level.WF uvars) → + levels.length = rule.uvars → + params.length = rule.nparams → + SpineWF Γ (rule.familyType.instL levels) params (.sort resultLevel) → + Γ ⊢ major : rule.structureType levels params → + Γ ⊢ rule.rebuild levels params major : + rule.structureType levels params → + Γ ⊢ rule.rebuild levels params major ≡ major : + rule.structureType levels params + | proofIrrel : + Γ ⊢ p : .sort .zero → Γ ⊢ h : p → Γ ⊢ h' : p → + Γ ⊢ h ≡ h' : p + | extra : + env.defeqs df → (∀ l ∈ ls, l.WF uvars) → ls.length = df.uvars → + Γ ⊢ df.lhs.instL ls ≡ df.rhs.instL ls : df.type.instL ls + +/-- Typing of an application spine against an iterated pi type: peeling the +expressions of `es` off `A` one instantiation at a time ends at `B`. + +This judgment is mutually inductive with `IsDefEq` so rules whose validity +depends on an exact application spine retain induction hypotheses for every +argument typing derivation. -/ +inductive SpineWF : List VExpr → VExpr → List VExpr → VExpr → Prop where + | nil : SpineWF Γ A [] A + | cons : + IsDefEq Γ e e A₁ → + SpineWF Γ (A₂.inst e) es B → + SpineWF Γ (.forallE A₁ A₂) (e :: es) B + +end + +end + +def HasType (env : VEnv) (U : Nat) (Γ : List VExpr) (e A : VExpr) : Prop := + IsDefEq env U Γ e e A + +def IsType (env : VEnv) (U : Nat) (Γ : List VExpr) (A : VExpr) : Prop := + ∃ u, env.HasType U Γ A (.sort u) + +def IsDefEqU (env : VEnv) (U : Nat) (Γ : List VExpr) (e₁ e₂ : VExpr) := + ∃ A, env.IsDefEq U Γ e₁ e₂ A + +end VEnv + +def VExpr.WF (env : VEnv) (U : Nat) (Γ : List VExpr) (e : VExpr) := env.IsDefEqU U Γ e e + +def VConstant.WF (env : VEnv) (ci : VConstant) : Prop := env.IsType ci.uvars [] ci.type + +def VDefEq.WF (env : VEnv) (df : VDefEq) : Prop := + env.HasType df.uvars [] df.lhs df.type ∧ env.HasType df.uvars [] df.rhs df.type + +/-- Subject-reduction package attached to a registered structure-eta +descriptor. It consumes the exact family parameter spine but contains no +equality premise. -/ +structure VStructEta.WF (rule : VStructEta) (env : VEnv) : Prop where + /-- The retained family declaration is a closed constant type. This is the + syntactic fact which lets an exact parameter-spine certificate survive term + weakening and substitution. -/ + familyType_closed : rule.familyType.ClosedN + rebuild_hasType : + ∀ {env' : VEnv}, env ≤ env' → + ∀ {U : Nat} {Γ : List VExpr} {levels : List VLevel} + {params : List VExpr} {major : VExpr}, + OnCtx Γ (env'.IsType U) → + (∀ level ∈ levels, level.WF U) → + levels.length = rule.uvars → + params.length = rule.nparams → + (∃ resultLevel, + env'.SpineWF U Γ (rule.familyType.instL levels) + params (.sort resultLevel)) → + env'.HasType U Γ major (rule.structureType levels params) → + env'.HasType U Γ (rule.rebuild levels params major) + (rule.structureType levels params) diff --git a/Ix/Theory/Named/Typing/Env.lean b/Ix/Theory/Named/Typing/Env.lean new file mode 100644 index 000000000..b3e009d29 --- /dev/null +++ b/Ix/Theory/Named/Typing/Env.lean @@ -0,0 +1,79 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Typing.Basic +import Ix.Theory.Named.VDecl +import Ix.Theory.Named.Quot +import Ix.Theory.Named.Inductive +import Ix.Theory.Named.NestedInductive + +namespace Ix.Theory.Named + +def VDefVal.WF (env : VEnv) (ci : VDefVal) : Prop := env.HasType ci.uvars [] ci.value ci.type + +/-- Add a block of constants, without their defining equations. -/ +def VEnv.addConsts (env : VEnv) (cis : List VDefVal) : Option VEnv := + cis.foldlM (fun env ci => env.addConst ci.name ci.toVConstant) env + +/-- Add the defining equations of a block, after all of its constants. -/ +def VEnv.addDefEqs (env : VEnv) (cis : List VDefVal) : VEnv := + cis.foldl (fun env ci => env.addDefEq ci.toDefEq) env + +inductive VDecl.WF : VEnv → VDecl → VEnv → Prop where + | axiom : + ci.WF env → + env.addConst ci.name ci.toVConstant = some env' → + VDecl.WF env (.axiom ci) env' + | def : + ci.WF env → + env.addConst ci.name ci.toVConstant = some env' → + VDecl.WF env (.def ci) (env'.addDefEq ci.toDefEq) + | mutualDef : + (∀ ci ∈ cis, ci.toVConstant.WF env) → + env.addConsts cis = some env' → + (∀ ci ∈ cis, ci.WF env') → + VDecl.WF env (.mutualDef cis) (env'.addDefEqs cis) + | opaque : + ci.WF env → + env.addConst ci.name ci.toVConstant = some env' → + VDecl.WF env (.opaque ci) env' + | example : + ci.WF env → + VDecl.WF env (.example ci) env + | quot : + env.QuotReady → + env.addQuot = some env' → + VDecl.WF env .quot env' + | induct {gen : decl.GenerationChecked} : + gen.WF env → + env.addInductGeneration gen = some env' → + VDecl.WF env (.induct decl) env' + | inductBlock {gen : decl.BlockGenerationChecked} : + gen.WF env blockEnv → + env.addInductBlockGeneration gen = some env' → + VDecl.WF env (.induct decl) env' + | inductNested {nested : decl.NestedBlockChecked} : + nested.WF env → + env.addInductNested nested = some env' → + VDecl.WF env (.induct decl) env' + +inductive VEnv.WF' : List VDecl → VEnv → Prop where + | empty : VEnv.WF' [] .empty + | decl {env} : VDecl.WF env d env' → env.WF' ds → env'.WF' (d::ds) + /-- A checked structure-eta descriptor is an environment capability, not a + source declaration. Keep it in the environment history without inventing + a `VDecl`; its subject-reduction certificate is exactly the premise used by + `Ordered.structEta`. -/ + | structEta {env : VEnv} {rule : VStructEta} : rule.WF env → env.WF' ds → + (env.addStructEta rule).WF' ds + +def VEnv.WF (env : VEnv) : Prop := ∃ ds, VEnv.WF' ds env + +/- A normalized inductive history entry carries only the standard Theory +logical baseline; in particular it cannot import Verify's implementation +axioms into `VEnv.WF`. -/ +#guard_named_axioms Ix.Theory.Named.VDecl.WF.induct [propext, Quot.sound] diff --git a/Ix/Theory/Named/Typing/EnvLemmas.lean b/Ix/Theory/Named/Typing/EnvLemmas.lean new file mode 100644 index 000000000..892e68530 --- /dev/null +++ b/Ix/Theory/Named/Typing/EnvLemmas.lean @@ -0,0 +1,120 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Typing.Lemmas +import Ix.Theory.Named.Typing.Env +import Ix.Theory.Named.Typing.QuotLemmas +import Ix.Theory.Named.Typing.InductiveLemmas +import Ix.Theory.Named.Typing.NestedInductiveLemmas + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named + +theorem VEnv.addConsts_le {env env' : VEnv} : ∀ {cis}, env.addConsts cis = some env' → env ≤ env' + | [], h => by cases h; exact .rfl + | _ :: _, h => by + simp [VEnv.addConsts, Option.bind_eq_some_iff] at h + obtain ⟨_, h1, h2⟩ := h + exact (addConst_le h1).trans (addConsts_le h2) + +theorem VEnv.addConst_eq_none {env : VEnv} {name ci} + (h : env.constants name = none) : ∃ env', env.addConst name ci = some env' := by + unfold VEnv.addConst; rw [h]; exact ⟨_, rfl⟩ + +theorem VEnv.addConst_constants_eq {env env' : VEnv} {name ci} + (h : env.addConst name ci = some env') : + env'.constants = fun n => if name = n then some ci else env.constants n := by + unfold VEnv.addConst at h; split at h <;> cases h; rfl + +/-- A block of constants can be added as long as each name is fresh and the block has no +duplicates; the latter is what `addMutual`'s `found` set checks. -/ +theorem VEnv.exists_addConsts {env : VEnv} : ∀ {cis : List VDefVal}, + (∀ ci ∈ cis, env.constants ci.name = none) → (cis.map (·.name)).Nodup → + ∃ env', env.addConsts cis = some env' + | [], _, _ => ⟨_, rfl⟩ + | ci :: cis, hfresh, hnd => by + obtain ⟨env₁, h₁⟩ := VEnv.addConst_eq_none (ci := ci.toVConstant) (hfresh _ (.head _)) + rw [List.map_cons, List.nodup_cons] at hnd + have ⟨env₂, h₂⟩ := VEnv.exists_addConsts (env := env₁) (cis := cis) (fun c hc => ?_) hnd.2 + · exact ⟨env₂, by simp [VEnv.addConsts, h₁]; exact h₂⟩ + · rw [VEnv.addConst_constants_eq h₁] + have : ci.name ≠ c.name := fun h => hnd.1 (List.mem_map.2 ⟨c, hc, h.symm⟩) + simp [this, hfresh c (.tail _ hc)] + +theorem VEnv.addConsts_congr {env : VEnv} : ∀ {cis cis' : List VDefVal}, + List.Forall₂ (fun a b => a.toVConstVal = b.toVConstVal) cis cis' → + env.addConsts cis = env.addConsts cis' + | [], [], _ => rfl + | a :: _, b :: _, .cons h t => by + have h1 : a.name = b.name := congrArg VConstVal.name h + have h2 : a.toVConstant = b.toVConstant := congrArg VConstVal.toVConstant h + show (env.addConst a.name a.toVConstant).bind _ = (env.addConst b.name b.toVConstant).bind _ + rw [h1, h2] + cases env.addConst b.name b.toVConstant + · rfl + · exact VEnv.addConsts_congr t + +theorem VEnv.addConsts_ordered {env env' : VEnv} : ∀ {cis}, Ordered env → + (∀ ci ∈ cis, ci.toVConstant.WF env) → env.addConsts cis = some env' → Ordered env' + | [], h, _, e => by cases e; exact h + | _ :: _, h, hw, e => by + simp [VEnv.addConsts, Option.bind_eq_some_iff] at e + obtain ⟨_, h1, h2⟩ := e + refine VEnv.addConsts_ordered (.const h (hw _ (.head _)) h1) (fun c hc => ?_) h2 + exact (hw c (.tail _ hc)).mono (VEnv.addConst_le h1) + +theorem VEnv.addConsts_constants {env env' : VEnv} : ∀ {cis}, env.addConsts cis = some env' → + ∀ ci ∈ cis, env'.constants ci.name = some ci.toVConstant + | [], _, _, hc => nomatch hc + | _ :: _, e, c, hc => by + simp [VEnv.addConsts, Option.bind_eq_some_iff] at e + obtain ⟨_, h1, h2⟩ := e + cases hc with + | head => exact (VEnv.addConsts_le h2).constants (VEnv.addConst_self h1) + | tail _ hc => exact VEnv.addConsts_constants h2 c hc + +theorem VEnv.addDefEqs_ordered : ∀ {env : VEnv} {cis}, Ordered env → + (∀ ci ∈ cis, env.constants ci.name = some ci.toVConstant) → + (∀ ci ∈ cis, ci.WF env) → Ordered (env.addDefEqs cis) + | _, [], h, _, _ => h + | env, ci :: cis, h, hmem, hw => by + have hci : ci.WF env := hw _ (.head _) + have hord : Ordered (env.addDefEq ci.toDefEq) := by + refine .defeq h ⟨?_, hci⟩ + simp [VDefVal.toDefEq] + rw [← (hci.levelWF ⟨⟩).2.2.instL_id] + exact .const (hmem _ (.head _)) VLevel.id_WF (by simp) + show Ordered ((env.addDefEq ci.toDefEq).addDefEqs cis) + refine VEnv.addDefEqs_ordered hord (fun c hc => ?_) (fun c hc => ?_) + · exact (VEnv.addDefEq_le (df := ci.toDefEq)).constants (hmem c (.tail _ hc)) + · exact (hw c (.tail _ hc)).mono VEnv.addDefEq_le + +theorem VEnv.WF.ordered : WF env → Ordered env + | ⟨ds, H⟩ => by + induction H with + | empty => exact .empty + | decl h _ ih => + cases h with + | «axiom» h1 h2 => exact .const ih h1 h2 + | @«def» env env' ci h1 h2 => + refine .defeq (.const ih (h1.isType ih ⟨⟩) h2) ⟨?_, ?_⟩ + · simp [VDefVal.toDefEq] + rw [← (h1.levelWF ⟨⟩).2.2.instL_id] + exact .const (addConst_self h2) VLevel.id_WF (by simp) + · exact h1.mono (addConst_le h2) + | mutualDef h0 h1 h2 => + exact VEnv.addDefEqs_ordered (VEnv.addConsts_ordered ih h0 h1) + (VEnv.addConsts_constants h1) h2 + | «opaque» h1 h2 => exact .const ih (h1.isType ih ⟨⟩) h2 + | «example» _ => exact ih + | quot h1 h2 => exact addQuot_WF ih h1 h2 + | induct h1 h2 => exact addInductGeneration_WF ih h1 h2 + | inductBlock h1 h2 => exact addInductBlockGeneration_WF ih h1 h2 + | inductNested h1 h2 => exact VEnv.addInductNested_WF ih h1 h2 + | structEta hwf _ ih => exact .structEta ih hwf + +instance : CoeOut (VEnv.WF env) env.Ordered := ⟨(·.ordered)⟩ diff --git a/Ix/Theory/Named/Typing/InductiveCertificate.lean b/Ix/Theory/Named/Typing/InductiveCertificate.lean new file mode 100644 index 000000000..3f0afdf3e --- /dev/null +++ b/Ix/Theory/Named/Typing/InductiveCertificate.lean @@ -0,0 +1,514 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Typing.EnvLemmas +import Ix.Theory.Named.Typing.InductivePattern +import Ix.Theory.Named.Typing.NestedInductiveLemmas + +/-! +# Consumer certificates for completed inductive blocks + +`BlockGenerationCertificate` is the semantic input to the block transaction. +This module packages that input with one successful transaction and a +well-formed dependency environment, then exports the stable consequences a +consumer needs. The package contains only Theory values and proofs: no +implementation metadata, checker state, or normalization execution crosses +this boundary. + +In particular, `BlockCertificate.ruleClosure` derives the closed payload +required by the generated-pattern API from the registered, well-formed iota +rules in the completed environment. A consumer therefore does not need a +second closedness assumption in order to use `IotaPat`. +-/ + +namespace Ix.Theory.Named + +namespace VInductDecl + +/-- One successful proof-carrying block transaction over an explicit +dependency environment. -/ +structure BlockCertificate (source : VInductDecl) (before after : VEnv) where + semantic : source.BlockGenerationCertificate before + success : before.addInductBlockCertified semantic = some after + beforeWF : before.WF + +namespace BlockCertificate + +variable {source : VInductDecl} {before after : VEnv} + +/-- Package the ordinary raw `addInduct` entry point once its accepted block +descriptor and semantic proof are known. This is the compatibility bridge +for consumers that still execute `addInduct`; no second transaction is run. -/ +def ofAddInduct + (generation : source.BlockGenerationChecked) (blockEnv : VEnv) + (hidentity : source.identityBlockGeneration? = some generation) + (hwf : generation.WF before blockEnv) (hbefore : before.WF) + (hadd : before.addInduct source = some after) : + BlockCertificate source before after where + semantic := ⟨generation, blockEnv, hwf⟩ + success := by + unfold VEnv.addInduct at hadd + rw [hidentity] at hadd + exact hadd + beforeWF := hbefore + +/-- The exact generation descriptor retained by a completed block. -/ +abbrev generation (certificate : BlockCertificate source before after) : + source.BlockGenerationChecked := + certificate.semantic.generation + +/-- Recover the four exact insertion phases of the completed block. -/ +theorem trace (certificate : BlockCertificate source before after) : + Nonempty (VEnv.AddInductBlockGenerationTrace before after + certificate.generation) := + VEnv.addInductBlockCertified_trace certificate.success + +/-- The completed transaction is a genuine block declaration step. -/ +theorem declWF (certificate : BlockCertificate source before after) : + VDecl.WF before (.induct source) after := by + apply VDecl.WF.inductBlock certificate.semantic.wf + simpa only [VEnv.addInductBlockCertified_eq_addInductBlockGeneration] using + certificate.success + +/-- Extend the dependency-environment history with the certified block. -/ +theorem afterWF (certificate : BlockCertificate source before after) : + after.WF := by + rcases certificate.beforeWF with ⟨decls, hdecls⟩ + exact ⟨.induct source :: decls, hdecls.decl certificate.declWF⟩ + +/-- A completed block only grows its dependency environment. -/ +theorem envLE (certificate : BlockCertificate source before after) : + before ≤ after := by + rcases certificate.trace with ⟨trace⟩ + exact trace.le + +/-- Compatibility spelling for consumers of the historical +`addInduct_le` growth theorem. -/ +theorem addInduct_le (certificate : BlockCertificate source before after) : + before ≤ after := + certificate.envLE + +/-- Compatibility spelling for the preservation result traditionally +exported as `addInduct_WF`. -/ +theorem addInduct_WF (certificate : BlockCertificate source before after) : + after.WF := + certificate.afterWF + +/-- Recover success through the ordinary raw API when this certificate's +descriptor is the declaration's identity descriptor. -/ +theorem addInduct + (certificate : BlockCertificate source before after) + (hidentity : source.identityBlockGeneration? = + some certificate.generation) : + before.addInduct source = some after := by + unfold VEnv.addInduct + rw [hidentity] + simpa [VEnv.addInductBlockCertified] using certificate.success + +/-- Every source family has its exact stored Theory value in the completed +environment. -/ +theorem familyLookup (certificate : BlockCertificate source before after) + {family : VInductiveType} (hfamily : family ∈ source.types) : + after.constants family.name = some family.toVConstant := by + rcases certificate.trace with ⟨trace⟩ + exact trace.family_lookup hfamily + +/-- Every flattened source constructor has its exact stored Theory value in +the completed environment. -/ +theorem constructorLookup + (certificate : BlockCertificate source before after) + {constructor : VConstVal} + (hconstructor : constructor ∈ source.blockConstructorConstants) : + after.constants constructor.name = some constructor.toVConstant := by + rcases certificate.trace with ⟨trace⟩ + exact trace.ctor_lookup hconstructor + +/-- Every generated family recursor has its exact Theory value in the +completed environment. -/ +theorem recursorLookup + (certificate : BlockCertificate source before after) + {recursor : VConstVal} + (hrecursor : recursor ∈ certificate.generation.recursors) : + after.constants recursor.name = some recursor.toVConstant := by + rcases certificate.trace with ⟨trace⟩ + exact trace.rec_lookup hrecursor + +/-- A source family name was fresh at the dependency boundary. -/ +theorem familyFresh (certificate : BlockCertificate source before after) + {family : VInductiveType} (hfamily : family ∈ source.types) : + before.constants family.name = none := by + rcases certificate.trace with ⟨trace⟩ + exact trace.family_fresh hfamily + +/-- A flattened source constructor name was fresh at the dependency +boundary. -/ +theorem constructorFresh + (certificate : BlockCertificate source before after) + {constructor : VConstVal} + (hconstructor : constructor ∈ source.blockConstructorConstants) : + before.constants constructor.name = none := by + rcases certificate.trace with ⟨trace⟩ + exact trace.ctor_fresh hconstructor + +/-- A generated recursor name was fresh at the dependency boundary. -/ +theorem recursorFresh + (certificate : BlockCertificate source before after) + {recursor : VConstVal} + (hrecursor : recursor ∈ certificate.generation.recursors) : + before.constants recursor.name = none := by + rcases certificate.trace with ⟨trace⟩ + exact trace.rec_fresh hrecursor + +/-- Every generated rule is registered by the completed transaction. -/ +theorem ruleRegistered + (certificate : BlockCertificate source before after) + {rule : VDefEq} + (hrule : rule ∈ certificate.generation.generatedRules) : + after.defeqs rule := by + rcases certificate.trace with ⟨trace⟩ + exact trace.rule_mem hrule + +/-- Every generated rule is well formed in the completed environment. -/ +theorem ruleWF + (certificate : BlockCertificate source before after) + {rule : VDefEq} + (hrule : rule ∈ certificate.generation.generatedRules) : + rule.WF after := + certificate.afterWF.ordered.defEqWF (certificate.ruleRegistered hrule) + +/-- An exact family lookup is unique. This small eliminator is convenient +for consumers that translate their own family representation to a Theory +constant and then compare it with the certificate inventory. -/ +theorem familyLookup_unique + (certificate : BlockCertificate source before after) + {family : VInductiveType} (hfamily : family ∈ source.types) + {constant : VConstant} + (hlookup : after.constants family.name = some constant) : + constant = family.toVConstant := by + exact Option.some.inj (hlookup.symm.trans (certificate.familyLookup hfamily)) + +/-- An exact constructor lookup is unique. -/ +theorem constructorLookup_unique + (certificate : BlockCertificate source before after) + {constructor : VConstVal} + (hconstructor : constructor ∈ source.blockConstructorConstants) + {constant : VConstant} + (hlookup : after.constants constructor.name = some constant) : + constant = constructor.toVConstant := by + exact Option.some.inj + (hlookup.symm.trans (certificate.constructorLookup hconstructor)) + +/-- An exact generated-recursor lookup is unique. -/ +theorem recursorLookup_unique + (certificate : BlockCertificate source before after) + {recursor : VConstVal} + (hrecursor : recursor ∈ certificate.generation.recursors) + {constant : VConstant} + (hlookup : after.constants recursor.name = some constant) : + constant = recursor.toVConstant := by + exact Option.some.inj + (hlookup.symm.trans (certificate.recursorLookup hrecursor)) + +private theorem rule_mem_generatedRules + (generation : source.BlockGenerationChecked) + {i : Nat} {constructor : NormalizedBlockCtor} + (hentry : generation.flatCtors[i]? = some constructor) : + generation.rule i constructor ∈ generation.generatedRules := by + apply List.mem_map.2 + refine ⟨(constructor, i), ?_, rfl⟩ + apply List.mem_of_getElem? (i := i) + rw [List.getElem?_zipIdx, hentry, Option.map_some, Nat.zero_add] + +private theorem closedN_lamN_body : + ∀ {binders : List VExpr} {body : VExpr} {k : Nat}, + (VExpr.lamN binders body).ClosedN k → + body.ClosedN (k + binders.length) + | [], _, _, h => by + simpa only [VExpr.lamN, List.length_nil, Nat.add_zero] using h + | _ :: binders, body, k, h => by + have hbody := closedN_lamN_body (binders := binders) + (body := body) (k := k + 1) h.2 + simpa [Nat.add_assoc, Nat.add_comm, Nat.add_left_comm] using hbody + +private theorem closedN_lamN_replace : + ∀ {binders : List VExpr} {body body' : VExpr} {k : Nat}, + (VExpr.lamN binders body).ClosedN k → + body'.ClosedN (k + binders.length) → + (VExpr.lamN binders body').ClosedN k + | [], _, _, _, _, hbody' => by + simpa only [VExpr.lamN, List.length_nil, Nat.add_zero] using hbody' + | _ :: binders, body, body', k, h, hbody' => by + refine ⟨h.1, closedN_lamN_replace (binders := binders) + (body := body) (body' := body') (k := k + 1) h.2 ?_⟩ + simpa [Nat.add_assoc, Nat.add_comm, Nat.add_left_comm] using hbody' + +private theorem closedN_appN_function : + ∀ {function : VExpr} {arguments : List VExpr} {k : Nat}, + (VExpr.appN function arguments).ClosedN k → function.ClosedN k + | _, [], _, h => by simpa only [VExpr.appN] using h + | function, argument :: arguments, k, h => + (closedN_appN_function (function := function.app argument) + (arguments := arguments) (k := k) h).1 + +private theorem closedN_appN_argument + {function : VExpr} {arguments : List VExpr} {k : Nat} + (hclosed : (VExpr.appN function arguments).ClosedN k) + {argument : VExpr} (hargument : argument ∈ arguments) : + argument.ClosedN k := by + induction arguments generalizing function with + | nil => simp at hargument + | cons head tail ih => + rcases List.mem_cons.1 hargument with heq | htail + · rw [heq] + exact (closedN_appN_function + (function := function.app head) (arguments := tail) + (k := k) hclosed).2 + · exact ih (function := function.app head) hclosed htail + +/-- The successful block transaction supplies the closedness bundle required +by `IotaPat`. Closedness is derived from the registered rules and the +completed environment's ordinary WF history; it is not an additional +consumer assumption. -/ +theorem ruleClosure + (certificate : BlockCertificate source before after) : + certificate.generation.RuleClosure := by + constructor + · intro i constructor hentry + have hmem := rule_mem_generatedRules certificate.generation hentry + exact (certificate.ruleWF hmem).2.closedN + certificate.afterWF.ordered trivial + · intro constructor hconstructor expression hexpression + obtain ⟨i, hentry⟩ := List.mem_iff_getElem?.1 hconstructor + have hmem := rule_mem_generatedRules certificate.generation hentry + have hlhs := (certificate.ruleWF hmem).1.closedN + certificate.afterWF.ordered trivial + rw [certificate.generation.rule_lhs i constructor] at hlhs + have hbody := closedN_lamN_body hlhs + have hexpression' : expression ∈ + certificate.generation.ruleIdx constructor ++ + [certificate.generation.ruleCtorApp constructor] := + List.mem_append.2 (.inl hexpression) + have hclosed : expression.ClosedN + (certificate.generation.ruleBinders constructor).length := by + apply closedN_appN_argument + (function := certificate.generation.recBase + (certificate.generation.ruleFieldCount constructor) + constructor.owner) + (arguments := certificate.generation.ruleIdx constructor ++ + [certificate.generation.ruleCtorApp constructor]) + · simpa only [BlockGenerationChecked.ruleLhsBody, List.length_nil, + Nat.zero_add] using hbody + · exact hexpression' + apply closedN_lamN_replace hlhs + simpa using hclosed + +/-- The exact generated pattern and payload associated with one flattened +rule entry. -/ +theorem recursorPattern + (certificate : BlockCertificate source before after) + {i : Nat} {constructor : NormalizedBlockCtor} + (hentry : certificate.generation.ruleEntry i constructor) : + certificate.generation.IotaPat certificate.ruleClosure + ((certificate.generation.rulePattern constructor).toPattern) + (certificate.generation.ruleRHS certificate.ruleClosure hentry, + certificate.generation.ruleCheck certificate.ruleClosure + (List.mem_of_getElem? hentry)) := + .mk hentry + +/-- Rule-level consumer bundle: exact global position, generated-list +membership, registration, well-formedness, and the corresponding Spec-10 +pattern all come from the same completed block. -/ +structure RecursorRuleFacts + (certificate : BlockCertificate source before after) + (i : Nat) (constructor : NormalizedBlockCtor) : Prop where + entry : certificate.generation.ruleEntry i constructor + member : certificate.generation.rule i constructor ∈ + certificate.generation.generatedRules + registered : after.defeqs (certificate.generation.rule i constructor) + wf : (certificate.generation.rule i constructor).WF after + pattern : certificate.generation.IotaPat certificate.ruleClosure + ((certificate.generation.rulePattern constructor).toPattern) + (certificate.generation.ruleRHS certificate.ruleClosure entry, + certificate.generation.ruleCheck certificate.ruleClosure + (List.mem_of_getElem? entry)) + +/-- Assemble all rule facts without a consumer-supplied semantic premise. -/ +theorem recursorRuleFacts + (certificate : BlockCertificate source before after) + {i : Nat} {constructor : NormalizedBlockCtor} + (hentry : certificate.generation.ruleEntry i constructor) : + certificate.RecursorRuleFacts i constructor := by + have hmember := rule_mem_generatedRules certificate.generation hentry + exact { + entry := hentry + member := hmember + registered := certificate.ruleRegistered hmember + wf := certificate.ruleWF hmember + pattern := certificate.recursorPattern hentry } + +end BlockCertificate + +/-! ## Completed nested transactions -/ + +/-- One successful proof-carrying nested transaction over an explicit +dependency environment. As with `BlockCertificate`, this package contains +only Theory artifacts. -/ +structure NestedBlockCertificate + (source : VInductDecl) (before after : VEnv) where + nested : source.NestedBlockChecked + semantic : nested.WF before + success : before.addInductNested nested = some after + beforeWF : before.WF + +namespace NestedBlockCertificate + +variable {source : VInductDecl} {before after : VEnv} + +/-- Recover the exact four-phase nested transaction trace. -/ +theorem trace (certificate : NestedBlockCertificate source before after) : + Nonempty (VEnv.AddInductNestedTrace before after certificate.nested) := + VEnv.addInductNested_trace certificate.success + +/-- The nested completion is a genuine inductive declaration step. -/ +theorem declWF (certificate : NestedBlockCertificate source before after) : + VDecl.WF before (.induct source) after := + .inductNested certificate.semantic certificate.success + +/-- Extend the dependency-environment history with the nested block. -/ +theorem afterWF (certificate : NestedBlockCertificate source before after) : + after.WF := by + rcases certificate.beforeWF with ⟨decls, hdecls⟩ + exact ⟨.induct source :: decls, hdecls.decl certificate.declWF⟩ + +/-- A completed nested transaction only grows its dependency environment. -/ +theorem envLE (certificate : NestedBlockCertificate source before after) : + before ≤ after := + VEnv.addInductNested_le certificate.success + +/-- Nested analogue of the public block growth result. -/ +theorem addInduct_le + (certificate : NestedBlockCertificate source before after) : + before ≤ after := + certificate.envLE + +/-- Nested analogue of the public block preservation result. -/ +theorem addInduct_WF + (certificate : NestedBlockCertificate source before after) : + after.WF := + certificate.afterWF + +/-- Every stored source family has its exact final value. -/ +theorem familyLookup (certificate : NestedBlockCertificate source before after) + {family : VInductiveType} (hfamily : family ∈ source.types) : + after.constants family.name = some family.toVConstant := by + rcases certificate.trace with ⟨trace⟩ + exact trace.family_lookup hfamily + +/-- Every stored source constructor has its exact final value. -/ +theorem constructorLookup + (certificate : NestedBlockCertificate source before after) + {family : VInductiveType} (hfamily : family ∈ source.types) + {constructor : VConstVal} (hconstructor : constructor ∈ family.ctors) : + after.constants constructor.name = some constructor.toVConstant := by + rcases certificate.trace with ⟨trace⟩ + exact trace.ctor_lookup hfamily hconstructor + +/-- Every restored recursor has its exact final value. -/ +theorem recursorLookup + (certificate : NestedBlockCertificate source before after) + {recursor : VConstVal} (hrecursor : recursor ∈ certificate.nested.recursors) : + after.constants recursor.name = some recursor.toVConstant := by + rcases certificate.trace with ⟨trace⟩ + exact trace.rec_lookup hrecursor + +/-- Every source family name was fresh at the dependency boundary. -/ +theorem familyFresh (certificate : NestedBlockCertificate source before after) + {family : VInductiveType} (hfamily : family ∈ source.types) : + before.constants family.name = none := by + rcases certificate.trace with ⟨trace⟩ + exact trace.family_fresh hfamily + +/-- Every flattened source constructor name was fresh at the dependency +boundary. -/ +theorem constructorFresh + (certificate : NestedBlockCertificate source before after) + {constructor : VConstVal} + (hconstructor : constructor ∈ source.blockConstructorConstants) : + before.constants constructor.name = none := by + rcases certificate.trace with ⟨trace⟩ + exact trace.ctor_fresh hconstructor + +/-- Every restored recursor name was fresh at the dependency boundary. -/ +theorem recursorFresh + (certificate : NestedBlockCertificate source before after) + {recursor : VConstVal} (hrecursor : recursor ∈ certificate.nested.recursors) : + before.constants recursor.name = none := by + rcases certificate.trace with ⟨trace⟩ + exact trace.rec_fresh hrecursor + +/-- Every restored rule is registered in the completed environment. -/ +theorem ruleRegistered + (certificate : NestedBlockCertificate source before after) + {rule : VDefEq} (hrule : rule ∈ certificate.nested.generatedRules) : + after.defeqs rule := by + rcases certificate.trace with ⟨trace⟩ + exact trace.rule_mem hrule + +/-- Every registered restored rule is well formed. -/ +theorem ruleWF + (certificate : NestedBlockCertificate source before after) + {rule : VDefEq} (hrule : rule ∈ certificate.nested.generatedRules) : + rule.WF after := + certificate.afterWF.ordered.defEqWF (certificate.ruleRegistered hrule) + +/-- Exact family lookups are unique. -/ +theorem familyLookup_unique + (certificate : NestedBlockCertificate source before after) + {family : VInductiveType} (hfamily : family ∈ source.types) + {constant : VConstant} + (hlookup : after.constants family.name = some constant) : + constant = family.toVConstant := + Option.some.inj (hlookup.symm.trans (certificate.familyLookup hfamily)) + +/-- Exact constructor lookups are unique. -/ +theorem constructorLookup_unique + (certificate : NestedBlockCertificate source before after) + {family : VInductiveType} (hfamily : family ∈ source.types) + {constructor : VConstVal} (hconstructor : constructor ∈ family.ctors) + {constant : VConstant} + (hlookup : after.constants constructor.name = some constant) : + constant = constructor.toVConstant := + Option.some.inj + (hlookup.symm.trans (certificate.constructorLookup hfamily hconstructor)) + +/-- Exact restored-recursor lookups are unique. -/ +theorem recursorLookup_unique + (certificate : NestedBlockCertificate source before after) + {recursor : VConstVal} (hrecursor : recursor ∈ certificate.nested.recursors) + {constant : VConstant} + (hlookup : after.constants recursor.name = some constant) : + constant = recursor.toVConstant := + Option.some.inj + (hlookup.symm.trans (certificate.recursorLookup hrecursor)) + +end NestedBlockCertificate + +end VInductDecl + +end Ix.Theory.Named + +/-! ## Exact Theory trust guards -/ + +#guard_named_axioms Ix.Theory.Named.VInductDecl.BlockCertificate.afterWF [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.BlockCertificate.ruleClosure [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.BlockCertificate.recursorRuleFacts [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.NestedBlockCertificate.afterWF [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.NestedBlockCertificate.ruleWF [propext, Classical.choice, Quot.sound] diff --git a/Ix/Theory/Named/Typing/InductiveLemmas.lean b/Ix/Theory/Named/Typing/InductiveLemmas.lean new file mode 100644 index 000000000..398afdeec --- /dev/null +++ b/Ix/Theory/Named/Typing/InductiveLemmas.lean @@ -0,0 +1,14628 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Typing.Lemmas +import Ix.Theory.Named.Typing.Env +import Ix.Theory.Named.Typing.Meta +import Ix.Theory.Named.Typing.Strong + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named + +/- Lean 4.31 no longer unfolds these structural recursors implicitly in a +number of `simp`/`simpa` calls below. Keep the compatibility normalization +local to this proof module: all rules only reduce on a visible constructor. -/ +attribute [local simp] VExpr.appN VExpr.bvarRevRange VExpr.forallN VExpr.lamN + VExpr.liftTelN VExpr.liftN VExpr.inst VExpr.instL VLevel.inst + +/-! ## Basic facts about the stage-1 generation helpers -/ + +end Ix.Theory.Named + +namespace Ix.Theory.VLevel + +theorem params'_length : (params' n k).length = n := by simp [params'] + +theorem params'_wf : ∀ l ∈ params' n k, l.WF (n + k) := by + simp only [params', List.mem_map, List.mem_range] + rintro _ ⟨i, hi, rfl⟩; exact Nat.add_lt_add_right hi _ + +theorem params'_one_wf : ∀ l ∈ params' n 1, l.WF (n + 1) := params'_wf + +theorem params_map_inst_params' : + (params n).map (VLevel.inst (params' n k)) = params' n k := + inst_map_id params'_length + +end Ix.Theory.VLevel + +namespace Ix.Theory.Named + +attribute [local simp] VExpr.appN VExpr.bvarRevRange VExpr.forallN VExpr.lamN + VExpr.liftTelN VExpr.liftN VExpr.inst VExpr.instL VLevel.inst + +namespace VExpr + +theorem forallN_append (As Bs : List VExpr) (e : VExpr) : + forallN (As ++ Bs) e = forallN As (forallN Bs e) := by + induction As with + | nil => rfl + | cons A As ih => simp [forallN, ih] + +theorem instL_forallN (ls : List VLevel) (As : List VExpr) (e : VExpr) : + (forallN As e).instL ls = forallN (As.map (instL ls)) (e.instL ls) := by + induction As with + | nil => rfl + | cons A As ih => simp [forallN, instL, ih] + +/-- Substituting a variable for the sole loose variable is a lift. -/ +theorem inst_bvar_of_closedN (h : ClosedN e (k+1)) : + e.inst (.bvar n) k = e.liftN n k := by + induction e generalizing k with simp_all [ClosedN, inst, liftN] + | bvar i => + simp only [instVar] + rcases Nat.lt_trichotomy i k with h' | rfl | h' + · simp [h', liftVar_lt h'] + · simp [liftVar_le (Nat.le_refl _), liftN] + · omega + +theorem ClosedN.appN {f : VExpr} (hf : f.ClosedN k) {as : List VExpr} + (has : ∀ a ∈ as, ClosedN a k) : (appN f as).ClosedN k := by + induction as generalizing f with + | nil => exact hf + | cons a as ih => + exact ih ⟨hf, has _ (.head _)⟩ fun a h => has _ (.tail _ h) + +theorem LevelWF.appN {f : VExpr} (hf : f.LevelWF U) {as : List VExpr} + (has : ∀ a ∈ as, LevelWF U a) : (appN f as).LevelWF U := by + induction as generalizing f with + | nil => exact hf + | cons a as ih => + exact ih ⟨hf, has _ (.head _)⟩ fun a h => has _ (.tail _ h) + +theorem LevelWF.forallN {As : List VExpr} (hAs : ∀ A ∈ As, LevelWF U A) + {e : VExpr} (he : e.LevelWF U) : (forallN As e).LevelWF U := by + induction As with + | nil => exact he + | cons A As ih => + exact ⟨hAs _ (.head _), ih fun A h => hAs _ (.tail _ h)⟩ + +theorem LevelWF.lamN {As : List VExpr} (hAs : ∀ A ∈ As, LevelWF U A) + {e : VExpr} (he : e.LevelWF U) : (lamN As e).LevelWF U := by + induction As with + | nil => exact he + | cons A As ih => + exact ⟨hAs _ (.head _), ih fun A h => hAs _ (.tail _ h)⟩ + +theorem appN_append (f : VExpr) : ∀ (as bs : List VExpr), + f.appN (as ++ bs) = (f.appN as).appN bs + | [], _ => rfl + | a :: as, bs => appN_append (f.app a) as bs + +theorem bvarRevRange_liftN_low : ∀ (m off n : Nat), + (bvarRevRange off m).map (liftN n · 0) = bvarRevRange (n + off) m + | 0, _, _ => rfl + | m+1, off, n => by + show VExpr.bvar _ :: _ = VExpr.bvar _ :: _ + rw [bvarRevRange_liftN_low m off n] + congr 2 + show liftVar n (off + m) 0 = n + off + m + rw [liftVar_le (Nat.zero_le _)]; omega + +theorem bvarRevRange_liftN_high : ∀ (m off n k : Nat), off + m ≤ k → + (bvarRevRange off m).map (liftN n · k) = bvarRevRange off m + | 0, _, _, _, _ => rfl + | m+1, off, n, k, h => by + show VExpr.bvar _ :: _ = VExpr.bvar _ :: _ + rw [bvarRevRange_liftN_high m off n k (by omega)] + congr 2 + exact liftVar_lt (by omega) + +/-- Instantiating at a fresh (lifted-over) position is the identity. -/ +theorem inst_liftN1 : ∀ (e a : VExpr) (k : Nat), (e.liftN 1 k).inst a k = e := by + intro e + induction e with intro a k + | bvar j => + show VExpr.instVar (liftVar 1 j k) a k = .bvar j + unfold liftVar VExpr.instVar + split + · rfl + · next h => + rw [if_neg (by omega), if_neg (by omega)] + congr 1; omega + | sort | const => rfl + | app f b ihf ihb => simp [liftN, inst, ihf, ihb] + | lam A b ihA ihb | forallE A b ihA ihb => simp [liftN, inst, ihA, ihb] + +/-- Lifting a telescope of closed binders acts only on the body. -/ +theorem liftN_forallN_closed : ∀ {As : List VExpr}, (∀ A ∈ As, A.ClosedN 0) → + ∀ (e : VExpr) (n k : Nat), + (forallN As e).liftN n k = forallN As (e.liftN n (k + As.length)) + | [], _, e, n, k => rfl + | A :: As, hAs, e, n, k => by + show VExpr.forallE _ _ = VExpr.forallE _ _ + rw [(hAs _ (.head _)).liftN_eq (Nat.zero_le _), + liftN_forallN_closed (fun A h => hAs _ (.tail _ h)) e n (k+1), + show k+1+As.length = k+(As.length+1) from by omega] + rfl + +/-- Lifting through a dependent telescope. -/ +theorem liftN_forallN (n : Nat) : ∀ (tel : List VExpr) (X : VExpr) (k : Nat), + (forallN tel X).liftN n k = forallN (liftTelN n tel k) (X.liftN n (k + tel.length)) + | [], _, _ => rfl + | A :: tel, X, k => by + show VExpr.forallE _ _ = VExpr.forallE _ _ + rw [liftN_forallN n tel X (k+1), + show k+1+tel.length = k+(tel.length+1) from by omega] + rfl + +theorem liftTelN_length (n : Nat) : ∀ (tel : List VExpr) (k : Nat), + (liftTelN n tel k).length = tel.length + | [], _ => rfl + | _ :: tel, k => by simp [liftTelN, liftTelN_length n tel (k+1)] + +theorem liftTelN_liftTelN (a b : Nat) : ∀ (tel : List VExpr) (k : Nat), + liftTelN b (liftTelN a tel k) k = liftTelN (a+b) tel k + | [], _ => rfl + | A :: tel, k => by + show _ :: _ = _ :: _ + rw [liftN'_liftN_hi, liftTelN_liftTelN a b tel (k+1)] + +theorem liftTelN_getElem? (n : Nat) : ∀ (tel : List VExpr) (k q : Nat), + (liftTelN n tel k)[q]? = tel[q]?.map fun A => A.liftN n (k+q) + | [], _, q => by simp [liftTelN] + | A :: tel, k, 0 => by simp [liftTelN] + | A :: tel, k, q+1 => by + simp only [liftTelN, List.getElem?_cons_succ] + rw [liftTelN_getElem? n tel (k+1) q, show k+1+q = k+(q+1) from by omega] + +theorem liftTelN_take (n : Nat) : ∀ (tel : List VExpr) (k q : Nat), + (liftTelN n tel k).take q = liftTelN n (tel.take q) k + | [], _, q => by simp [liftTelN] + | A :: tel, k, 0 => rfl + | A :: tel, k, q+1 => by + show A.liftN n k :: (liftTelN n tel (k+1)).take q = _ + rw [liftTelN_take n tel (k+1) q] + rfl + +/-- Merging a telescope lift over another when the outer cutoff sits right +past the inner lift. -/ +theorem liftTelN_liftTelN_hi (a b : Nat) : ∀ (tel : List VExpr) (k : Nat), + liftTelN b (liftTelN a tel k) (k + a) = liftTelN (a + b) tel k + | [], _ => rfl + | A :: tel, k => by + show (A.liftN a k).liftN b (k+a) :: _ = _ + rw [liftN'_liftN' (Nat.le_add_right _ _) (by omega), + show k + a + 1 = (k+1) + a from by omega, + liftTelN_liftTelN_hi a b tel (k+1)] + rfl + +/-- `liftTelN_liftTelN_hi` with the outer cutoff generalized, for +syntactic rewriting. -/ +theorem liftTelN_liftTelN_hi' (a b : Nat) (tel : List VExpr) (k : Nat) {cut : Nat} + (hcut : cut = k + a) : + liftTelN b (liftTelN a tel k) cut = liftTelN (a + b) tel k := by + rw [hcut]; exact liftTelN_liftTelN_hi a b tel k + +/-- Merge telescope lifts when the outer cutoff lies anywhere inside the +range opened by the inner lift. This is the shape needed when new ambient +binders are inserted below a recursive argument's own Pi telescope. -/ +theorem liftTelN_liftTelN_mid (a b : Nat) : ∀ (tel : List VExpr) (k cut : Nat), + k ≤ cut → cut ≤ a + k → + liftTelN b (liftTelN a tel k) cut = liftTelN (a+b) tel k + | [], _, _, _, _ => rfl + | A :: tel, k, cut, h₁, h₂ => by + show (A.liftN a k).liftN b cut :: _ = _ + rw [VExpr.liftN'_liftN' h₁ h₂, + liftTelN_liftTelN_mid a b tel (k+1) (cut+1) + (Nat.succ_le_succ h₁) (by omega)] + rfl + +theorem liftTelN_instL (ls : List VLevel) (n : Nat) : ∀ (tel : List VExpr) (k : Nat), + (liftTelN n tel k).map (instL ls) = liftTelN n (tel.map (instL ls)) k + | [], _ => rfl + | A :: tel, k => by + show (A.liftN n k).instL ls :: _ = (A.instL ls).liftN n k :: _ + rw [instL_liftN, liftTelN_instL ls n tel (k+1)] + +/-- Pulling a lift out of the middle of a two-step lift: the outer lift at +the seam between the two inner ones lands on the variables the innermost +lift moved. -/ +theorem liftN_liftN_mid : ∀ (e : VExpr) {j c : Nat} (k d : Nat), c ≤ j → + ((e.liftN 1 j).liftN d c).liftN k (j + d) = (e.liftN (k+1) j).liftN d c := by + intro e + induction e with intro j c k d hc + | bvar i => + show VExpr.bvar (liftVar k (liftVar d (liftVar 1 i j) c) (j+d)) = + .bvar (liftVar d (liftVar (k+1) i j) c) + congr 1 + rcases Nat.lt_or_ge i j with h1 | h1 + · rw [liftVar_lt h1, liftVar_lt (show i < j from h1)] + rcases Nat.lt_or_ge i c with h2 | h2 + · rw [liftVar_lt h2, liftVar_lt (show i < j+d from by omega)] + · rw [liftVar_le h2, liftVar_lt (show d+i < j+d from by omega)] + · rw [liftVar_le h1, liftVar_le h1, + liftVar_le (show c ≤ 1+i from by omega), + liftVar_le (show c ≤ k+1+i from by omega), + liftVar_le (show j+d ≤ d+(1+i) from by omega)] + omega + | sort | const => intros; rfl + | app f a ihf iha => simp [liftN, ihf _ _ hc, iha _ _ hc] + | lam A b ihA ihb | forallE A b ihA ihb => + simp only [liftN] + refine congr (congrArg _ (ihA _ _ hc)) ?_ + have := ihb (j := j+1) (c := c+1) k d (by omega) + rwa [show j+1+d = j+d+1 from by omega] at this + +/-- Telescope form of `liftN_liftN_mid`. The outer lift is inserted at the +seam between the lift below the telescope and the lift that opened the +telescope's original ambient context. -/ +theorem liftTelN_liftN_mid : ∀ (tel : List VExpr) {j c : Nat} (k d : Nat), c ≤ j → + liftTelN k (liftTelN d (liftTelN 1 tel j) c) (j+d) = + liftTelN d (liftTelN (k+1) tel j) c + | [], _, _, _, _, _ => rfl + | A :: tel, j, c, k, d, hc => by + show ((A.liftN 1 j).liftN d c).liftN k (j+d) :: _ = + (A.liftN (k+1) j).liftN d c :: _ + rw [liftN_liftN_mid A k d hc] + congr 1 + rw [show j+d+1 = j+1+d from by omega] + exact liftTelN_liftN_mid tel k d (Nat.succ_le_succ hc) + +/-- Disjoint lifts commute: an outer lift below an inner one slides past +it, pushing the inner cutoff up. -/ +theorem liftN_liftN_comm : ∀ (e : VExpr) (n k c K : Nat), c ≤ K → + (e.liftN k K).liftN n c = (e.liftN n c).liftN k (K + n) := by + intro e + induction e with intro n k c K hc + | bvar i => + show VExpr.bvar (liftVar n (liftVar k i K) c) = .bvar (liftVar k (liftVar n i c) (K+n)) + congr 1 + rcases Nat.lt_or_ge i c with h2 | h2 + · rw [liftVar_lt (show i < K from by omega), liftVar_lt h2, + liftVar_lt (show i < K+n from by omega)] + · rcases Nat.lt_or_ge i K with h1 | h1 + · rw [liftVar_lt h1, liftVar_le h2, + liftVar_lt (show n+i < K+n from by omega)] + · rw [liftVar_le h1, liftVar_le (show c ≤ k+i from by omega), liftVar_le h2, + liftVar_le (show K+n ≤ n+i from by omega)] + omega + | sort | const => intros; rfl + | app f a ihf iha => simp [liftN, ihf _ _ _ _ hc, iha _ _ _ _ hc] + | lam A b ihA ihb | forallE A b ihA ihb => + simp only [liftN] + refine congr (congrArg _ (ihA _ _ _ _ hc)) ?_ + have := ihb n k (c+1) (K+1) (by omega) + rwa [show K+1+n = K+n+1 from by omega] at this + +/-- Generalized form of `liftN_liftN_mid`: expanding an arbitrary lift at +the seam above a disjoint lower lift adds the new width to that arbitrary +lift. -/ +theorem liftN_liftN_midN (e : VExpr) {j c : Nat} + (a k d : Nat) (hc : c ≤ j) : + ((e.liftN a j).liftN d c).liftN k (j+d) = + (e.liftN (a+k) j).liftN d c := by + rw [← liftN_liftN_comm (e.liftN a j) d k c j hc] + rw [liftN'_liftN_hi] + +/-- Telescope form of `liftN_liftN_midN`. -/ +theorem liftTelN_liftN_midN : + ∀ (tel : List VExpr) {j c : Nat} (a k d : Nat), c ≤ j → + liftTelN k (liftTelN d (liftTelN a tel j) c) (j+d) = + liftTelN d (liftTelN (a+k) tel j) c + | [], _, _, _, _, _, _ => rfl + | A :: tel, j, c, a, k, d, hc => by + show ((A.liftN a j).liftN d c).liftN k (j+d) :: _ = _ + rw [liftN_liftN_midN A a k d hc] + congr 1 + rw [show j+d+1 = j+1+d from by omega] + exact liftTelN_liftN_midN tel a k d (Nat.succ_le_succ hc) + +/-- Instantiation under a telescope: the entry at depth `q` instantiates +at `k+q`. -/ +def instTelN (a : VExpr) : List VExpr → Nat → List VExpr + | [], _ => [] + | A :: As, k => A.inst a k :: instTelN a As (k+1) + +attribute [local simp] instTelN + +theorem instTelN_length (a : VExpr) : ∀ (tel : List VExpr) (k : Nat), + (instTelN a tel k).length = tel.length + | [], _ => rfl + | _ :: tel, k => by simp [instTelN, instTelN_length a tel (k+1)] + +theorem instN_forallN (a : VExpr) : ∀ (tel : List VExpr) (X : VExpr) (k : Nat), + (forallN tel X).inst a k = forallN (instTelN a tel k) (X.inst a (k + tel.length)) + | [], _, _ => rfl + | A :: tel, X, k => by + show VExpr.forallE _ _ = VExpr.forallE _ _ + rw [instN_forallN a tel X (k+1), + show k+1+tel.length = k+(tel.length+1) from by omega] + rfl + +/-- Iterated instantiation of a body under `es.length` binders, consuming +the spine outermost-first. -/ +def instRev : VExpr → List VExpr → VExpr + | C, [] => C + | C, e :: es => instRev (C.inst e es.length) es + +theorem instRev_closedN : ∀ (es : List VExpr) {C : VExpr}, C.ClosedN 0 → + instRev C es = C + | [], _, _ => rfl + | e :: es, C, hC => by + show instRev (C.inst e es.length) es = C + rw [hC.instN_eq (Nat.zero_le _)] + exact instRev_closedN es hC + +theorem instRev_bvar_ge : ∀ (es : List VExpr) {i : Nat}, es.length ≤ i → + instRev (.bvar i) es = .bvar (i - es.length) + | [], i, _ => by simp [instRev] + | e :: es, i, h => by + have h' : es.length < i := by + simp only [List.length_cons] at h + omega + show instRev ((VExpr.bvar i).inst e es.length) es = _ + rw [show (VExpr.bvar i).inst e es.length = .bvar (i-1) from by + show VExpr.instVar i e es.length = _ + unfold VExpr.instVar + rw [if_neg (by omega), if_neg (by omega)], + instRev_bvar_ge es (by omega)] + congr 1 + simp only [List.length_cons] + omega + +/-- The spine consumes a fully lifted body without a trace. -/ +theorem instRev_liftN_len : ∀ (es : List VExpr) (X : VExpr), + instRev (X.liftN es.length) es = X + | [], X => by simp [instRev, liftN_zero] + | e :: es, X => by + show instRev ((X.liftN (es.length+1)).inst e es.length) es = X + rw [show X.liftN (es.length+1) = (X.liftN es.length).liftN 1 es.length from + (liftN'_liftN' (Nat.zero_le _) (by omega)).symm, + inst_liftN] + exact instRev_liftN_len es X + +theorem instRev_bvar_lt_cons (es : List VExpr) (e : VExpr) {i : Nat} (hi : i < es.length) : + instRev (.bvar i) (e :: es) = instRev (.bvar i) es := by + show instRev ((VExpr.bvar i).inst e es.length) es = _ + congr 1 + show VExpr.instVar i e es.length = .bvar i + unfold VExpr.instVar + rw [if_pos hi] + +theorem mem_bvarRevRange : ∀ {m off : Nat} {x : VExpr}, x ∈ bvarRevRange off m → + ∃ i, x = .bvar i ∧ off ≤ i ∧ i < off + m + | m+1, off, x, h => by + rcases List.mem_cons.1 h with rfl | h + · exact ⟨off + m, rfl, by omega, by omega⟩ + · obtain ⟨i, rfl, h1, h2⟩ := mem_bvarRevRange h + exact ⟨i, rfl, h1, by omega⟩ + +/-- The last `es.length` variables consume to the spine itself. -/ +theorem map_instRev_bvarRevRange : ∀ (es : List VExpr), + (bvarRevRange 0 es.length).map (instRev · es) = es + | [] => rfl + | e :: es => by + show instRev (.bvar (0 + es.length)) (e :: es) :: + (bvarRevRange 0 es.length).map (instRev · (e :: es)) = e :: es + congr 1 + · show instRev ((VExpr.bvar (0 + es.length)).inst e es.length) es = e + rw [Nat.zero_add, + show (VExpr.bvar es.length).inst e es.length = e.liftN es.length from by + show VExpr.instVar es.length e es.length = _ + unfold VExpr.instVar + rw [if_neg (Nat.lt_irrefl _), if_pos rfl]] + exact instRev_liftN_len es e + · rw [List.map_congr_left fun x hx => ?_, map_instRev_bvarRevRange es] + obtain ⟨i, rfl, -, h2⟩ := mem_bvarRevRange hx + exact instRev_bvar_lt_cons es e (by omega) + +theorem map_instRev_bvarRevRange_ge (es : List VExpr) : ∀ (q off : Nat), + es.length ≤ off → + (bvarRevRange off q).map (instRev · es) = bvarRevRange (off - es.length) q + | 0, _, _ => rfl + | q+1, off, h => by + show instRev (.bvar (off+q)) es :: _ = _ + rw [instRev_bvar_ge es (by omega), map_instRev_bvarRevRange_ge es q off h] + congr 2 + omega + +theorem instRev_appN (es : List VExpr) : ∀ (f : VExpr) (as : List VExpr), + instRev (appN f as) es = appN (instRev f es) (as.map (instRev · es)) := by + induction es with intro f as + | nil => simp [instRev, List.map_id'] + | cons e es ih => + show instRev ((appN f as).inst e es.length) es = _ + rw [instN_appN, ih] + simp [instRev, List.map_map, Function.comp_def] + +theorem instRev_forallE_sort (u : VLevel) : ∀ (es : List VExpr) (D : VExpr), + instRev (.forallE D (.sort u)) es = .forallE (instRev D es) (.sort u) + | [], _ => rfl + | e :: es, D => instRev_forallE_sort u es (D.inst e es.length) + +theorem bvarRevRange_append : ∀ (m k : Nat), + bvarRevRange k m ++ bvarRevRange 0 k = bvarRevRange 0 (k + m) + | 0, k => by simp [bvarRevRange] + | m+1, k => by + show VExpr.bvar (k + m) :: (bvarRevRange k m ++ bvarRevRange 0 k) = _ + rw [bvarRevRange_append m k, show k + (m+1) = (k+m)+1 from rfl] + show _ = VExpr.bvar (0 + (k+m)) :: bvarRevRange 0 (k+m) + rw [Nat.zero_add] + +theorem appHead_appN : ∀ (as : List VExpr) (f : VExpr), (appN f as).appHead = f.appHead + | [], _ => rfl + | a :: as, f => appHead_appN as (f.app a) + +theorem appArgs_appN : ∀ (as acc : List VExpr) (f : VExpr), + (appN f as).appArgs acc = f.appArgs (as ++ acc) + | [], _, _ => rfl + | a :: as, acc, f => by + show (VExpr.appN (f.app a) as).appArgs acc = _ + rw [appArgs_appN as acc (f.app a)] + rfl + +theorem appN_appHead_appArgs : ∀ (e : VExpr) (acc : List VExpr), + appN e.appHead (e.appArgs acc) = appN e acc + | .app f a, acc => by + show appN f.appHead (f.appArgs (a :: acc)) = _ + rw [appN_appHead_appArgs f (a :: acc)] + rfl + | .bvar _, _ | .sort _, _ | .const _ _, _ | .lam _ _, _ | .forallE _ _, _ => rfl + +end VExpr + +/-! ## Anatomy of the stage-3 predicate -/ + +namespace VInductDecl + +/-- A constructor-type telescope splits as fields over the result. -/ +theorem forallN_ctorFields_resultOf : ∀ (e : VExpr), + VExpr.forallN (ctorFields e) e.resultOf = e + | .forallE B rest => congrArg (VExpr.forallE B) (forallN_ctorFields_resultOf rest) + | .bvar _ | .sort _ | .const _ _ | .app _ _ | .lam _ _ => rfl + +/-- Unpack a recursive-field check into its structural content. -/ +theorem isRecField_eq {U T np ni j B} (h : isRecField U T np ni j B = true) : + B = VExpr.appN (.const T (VLevel.params U)) + (VExpr.bvarRevRange j np ++ recFieldIdxs np B) ∧ + (recFieldIdxs np B).length = ni ∧ + ∀ e ∈ recFieldIdxs np B, e.hasConst T = false := by + simp only [isRecField, Bool.and_eq_true, beq_iff_eq, List.all_eq_true] at h + obtain ⟨⟨⟨h1, h2⟩, h3⟩, h4⟩ := h + refine ⟨?_, ?_, fun e he => by simpa [Bool.not_eq_true'] using h4 e he⟩ + · have hB : B = VExpr.appN B.appHead (B.appArgs []) := + (VExpr.appN_appHead_appArgs B []).symm + conv => lhs; rw [hB, h1, + show VExpr.appArgs B [] = + (VExpr.appArgs B []).take np ++ (VExpr.appArgs B []).drop np from + (List.take_append_drop ..).symm, + h3] + rfl + · simp only [recFieldIdxs, List.length_drop, h2] + omega + +/-- Unpack recursive-argument analysis beneath a possibly empty Pi telescope. +The terminal family application is seen under both the preceding constructor +fields (`j`) and the returned recursive binders. -/ +theorem recTarget?_eq {U T np ni j B As idxs} + (h : recTarget? U T np ni j B = some (As, idxs)) : + B = VExpr.forallN As + (VExpr.appN (.const T (VLevel.params U)) + (VExpr.bvarRevRange (j + As.length) np ++ idxs)) ∧ + idxs.length = ni ∧ + (∀ e ∈ idxs, e.hasConst T = false) ∧ + ∀ (q : Nat) (A : VExpr), As[q]? = some A → A.hasConst T = false := by + induction B generalizing j As idxs with + | forallE A rest _ ih => + simp only [recTarget?] at h + split at h + · contradiction + · next hA => + split at h + · next As' idxs' hrest => + simp only [Option.some.injEq, Prod.mk.injEq] at h + obtain ⟨rfl, rfl⟩ := h + obtain ⟨hshape, hlen, hfree, hbinders⟩ := ih hrest + refine ⟨?_, hlen, hfree, ?_⟩ + · show VExpr.forallE A rest = VExpr.forallE A _ + rw [hshape, show j + 1 + As'.length = j + (As'.length + 1) from by omega] + simp only [List.length_cons] + · intro q A' hq + match q, hq with + | 0, hq => + obtain rfl : A = A' := by simpa using hq + cases hAT : A.hasConst T with + | false => rfl + | true => exact (hA hAT).elim + | q+1, hq => exact hbinders q A' (by simpa using hq) + · contradiction + | bvar i | sort i | const i | app i i | lam i i => + simp only [recTarget?] at h + split at h + · next hrec => + simp only [Option.some.injEq, Prod.mk.injEq] at h + obtain ⟨rfl, rfl⟩ := h + obtain ⟨hshape, hlen, hfree⟩ := isRecField_eq hrec + exact ⟨by simpa using hshape, hlen, hfree, by simp⟩ + · contradiction + +/-- Public recursive-argument anatomy, including the stable field and +one-family target indices carried by the descriptor. -/ +theorem recArg?_eq {U T np ni j B r} + (h : recArg? U T np ni j B = some r) : + r.fieldIndex = j ∧ r.targetType = 0 ∧ + B = VExpr.forallN r.binders + (VExpr.appN (.const T (VLevel.params U)) + (VExpr.bvarRevRange (j + r.binders.length) np ++ r.indices)) ∧ + r.indices.length = ni ∧ + (∀ e ∈ r.indices, e.hasConst T = false) ∧ + ∀ (q : Nat) (A : VExpr), r.binders[q]? = some A → A.hasConst T = false := by + unfold recArg? at h + split at h + · next binders indices htarget => + simp only [Option.some.injEq] at h + subst r + obtain ⟨hshape, hlen, hfree, hbinders⟩ := recTarget?_eq htarget + exact ⟨rfl, rfl, hshape, hlen, hfree, hbinders⟩ + · contradiction + +/-- With no Pi binders, recursive-argument recognition is exactly the direct +recursive-field check. -/ +theorem recTarget?_nil {U T np ni j B idxs} + (h : recTarget? U T np ni j B = some ([], idxs)) : + isRecField U T np ni j B = true ∧ idxs = recFieldIdxs np B := by + cases B with + | forallE A rest => + simp only [recTarget?] at h + split at h + · contradiction + · cases hrest : recTarget? U T np ni (j+1) rest with + | none => simp [hrest] at h + | some out => + obtain ⟨As, is⟩ := out + simp [hrest] at h + | bvar i | sort i | const i | app i i | lam i i => + simp only [recTarget?] at h + split at h + · next hrec => + simp only [Option.some.injEq, Prod.mk.injEq] at h + exact ⟨hrec, h.2.symm⟩ + · contradiction + +theorem recArg?_nil {U T np ni j B r} + (h : recArg? U T np ni j B = some r) (hr : r.binders = []) : + isRecField U T np ni j B = true ∧ r.indices = recFieldIdxs np B := by + unfold recArg? at h + split at h + · next binders indices htarget => + simp only [Option.some.injEq] at h + subst r + simp only at hr + subst binders + exact recTarget?_nil htarget + · contradiction + +/-- A direct recursive-field success normalizes to the descriptor with an +empty binder telescope. -/ +theorem recArg?_of_isRecField {U T np ni j B} + (h : isRecField U T np ni j B = true) : + recArg? U T np ni j B = some + { fieldIndex := j, binders := [], targetType := 0, + indices := recFieldIdxs np B } := by + cases B with + | forallE A rest => simp [isRecField, VExpr.appHead] at h + | bvar i | sort i | const i | app i i | lam i i => + simp_all [recArg?, recTarget?] + +theorem stage3Ctor_eq {U T np ni} : ∀ {j₀ : Nat} {e : VExpr}, + stage3Ctor U T np ni j₀ e = true → + e = VExpr.forallN (ctorFields e) + (VExpr.appN (.const T (VLevel.params U)) + (VExpr.bvarRevRange (j₀ + (ctorFields e).length) np ++ + recFieldIdxs np e.resultOf)) ∧ + (recFieldIdxs np e.resultOf).length = ni ∧ + (∀ x ∈ recFieldIdxs np e.resultOf, x.hasConst T = false) ∧ + ∀ q B, (ctorFields e)[q]? = some B → stage3Field U T np ni (j₀ + q) B = true := by + intro j₀ e h + induction e generalizing j₀ with + | forallE B rest _ ih => + rw [show stage3Ctor U T np ni j₀ (.forallE B rest) = + (stage3Field U T np ni j₀ B && stage3Ctor U T np ni (j₀+1) rest) from rfl, + Bool.and_eq_true] at h + have ⟨ih1, ih2, ih3, ih4⟩ := ih h.2 + refine ⟨?_, ih2, ih3, ?_⟩ + · show VExpr.forallE _ _ = VExpr.forallE _ _ + conv => lhs; rw [ih1] + rw [show j₀+1+(ctorFields rest).length = j₀+((ctorFields rest).length+1) from by omega] + rfl + · intro q B' hB' + match q, hB' with + | 0, hB' => + obtain rfl : B = B' := by simpa [ctorFields] using hB' + exact h.1 + | q+1, hB' => + have := ih4 q B' (by simpa [ctorFields] using hB') + rwa [show j₀+1+q = j₀+(q+1) from by omega] at this + | bvar i => + exact ⟨(isRecField_eq h).1, (isRecField_eq h).2.1, (isRecField_eq h).2.2, + fun q B h' => by simp [ctorFields] at h'⟩ + | sort l => + exact ⟨(isRecField_eq h).1, (isRecField_eq h).2.1, (isRecField_eq h).2.2, + fun q B h' => by simp [ctorFields] at h'⟩ + | const c ls => + exact ⟨(isRecField_eq h).1, (isRecField_eq h).2.1, (isRecField_eq h).2.2, + fun q B h' => by simp [ctorFields] at h'⟩ + | app f a _ _ => + exact ⟨(isRecField_eq h).1, (isRecField_eq h).2.1, (isRecField_eq h).2.2, + fun q B h' => by simp [ctorFields] at h'⟩ + | lam A b _ _ => + exact ⟨(isRecField_eq h).1, (isRecField_eq h).2.1, (isRecField_eq h).2.2, + fun q B h' => by simp [ctorFields] at h'⟩ + +/-- Failure of the legacy singleton predicate is exactly failure to produce +the one-family checked descriptor. -/ +theorem checked?_eq_none_iff {decl : VInductDecl} : + decl.checked? = none ↔ decl.singletonStage3 = false := by + unfold singletonStage3 + cases decl.checked? <;> simp + +/-- Successful singleton acceptance retains the descriptor rather than +discarding it. -/ +theorem exists_checked_of_singletonStage3 {decl : VInductDecl} + (h : decl.singletonStage3 = true) : + ∃ checked, decl.checked? = some checked := by + unfold singletonStage3 at h + cases hc : decl.checked? with + | none => simp [hc] at h + | some checked => exact ⟨checked, rfl⟩ + +/-- Public Stage-3 rejection is exactly failure to retain a complete mutual +generation descriptor. -/ +theorem identityBlockGeneration?_eq_none_iff {decl : VInductDecl} : + decl.identityBlockGeneration? = none ↔ decl.stage3 = false := by + unfold stage3 + cases decl.identityBlockGeneration? <;> simp + +/-- Public Stage-3 acceptance retains the exact block descriptor used by the +transaction. -/ +theorem exists_blockGeneration_of_stage3 {decl : VInductDecl} + (h : decl.stage3 = true) : + ∃ generation, decl.identityBlockGeneration? = some generation := by + unfold stage3 at h + cases hgeneration : decl.identityBlockGeneration? with + | none => simp [hgeneration] at h + | some generation => exact ⟨generation, rfl⟩ + +/-- Proof-level constructor-header coherence exported from the computational +normalization-shape check. -/ +def CtorHeaderEq (source view : VConstVal) : Prop := + source.name = view.name ∧ source.uvars = view.uvars + +/-- Proof-level family-header coherence, including constructor order. -/ +def TypeHeaderEq (source view : VInductiveType) : Prop := + source.name = view.name ∧ source.uvars = view.uvars ∧ + List.Forall₂ CtorHeaderEq source.ctors view.ctors + +theorem sameCtorHeaders_iff_forall₂ : ∀ {source view}, + sameCtorHeaders source view = true ↔ + List.Forall₂ CtorHeaderEq source view + | [], [] => ⟨fun _ => .nil, fun _ => rfl⟩ + | [], _ :: _ => by + constructor + · intro h; exact Bool.noConfusion h + · intro h; nomatch h + | _ :: _, [] => by + constructor + · intro h; exact Bool.noConfusion h + · intro h; nomatch h + | source :: sources, view :: views => by + constructor + · intro h + simp only [sameCtorHeaders, Bool.and_eq_true, beq_iff_eq] at h + exact .cons ⟨h.1.1, h.1.2⟩ (sameCtorHeaders_iff_forall₂.1 h.2) + · intro h + obtain ⟨hhead, htail⟩ := List.forall₂_cons.1 h + simp only [sameCtorHeaders, Bool.and_eq_true, beq_iff_eq] + exact ⟨⟨hhead.1, hhead.2⟩, sameCtorHeaders_iff_forall₂.2 htail⟩ + +theorem sameTypeHeaders_iff_forall₂ : ∀ {source view}, + sameTypeHeaders source view = true ↔ + List.Forall₂ TypeHeaderEq source view + | [], [] => ⟨fun _ => .nil, fun _ => rfl⟩ + | [], _ :: _ => by + constructor + · intro h; exact Bool.noConfusion h + · intro h; nomatch h + | _ :: _, [] => by + constructor + · intro h; exact Bool.noConfusion h + · intro h; nomatch h + | source :: sources, view :: views => by + constructor + · intro h + simp only [sameTypeHeaders, Bool.and_eq_true, beq_iff_eq] at h + exact .cons + ⟨h.1.1.1, h.1.1.2, sameCtorHeaders_iff_forall₂.1 h.1.2⟩ + (sameTypeHeaders_iff_forall₂.1 h.2) + · intro h + obtain ⟨hhead, htail⟩ := List.forall₂_cons.1 h + simp only [sameTypeHeaders, Bool.and_eq_true, beq_iff_eq] + exact ⟨⟨⟨hhead.1, hhead.2.1⟩, + sameCtorHeaders_iff_forall₂.2 hhead.2.2⟩, + sameTypeHeaders_iff_forall₂.2 htail⟩ + +/-- Every accepted normalization preserves declaration arity and all +family/constructor identities in order. -/ +theorem Normalization.shape {source : VInductDecl} + (norm : Normalization source) : + source.uvars = norm.view.uvars ∧ + source.nparams = norm.view.nparams ∧ + List.Forall₂ TypeHeaderEq source.types norm.view.types := by + have h := norm.shape_eq + simp only [normalizationShape, Bool.and_eq_true, beq_iff_eq, + sameTypeHeaders_iff_forall₂] at h + exact ⟨h.1.1, h.1.2, h.2⟩ + +/-- The singleton source family paired with the checked singleton analysis +view. This is the raw payload future normalized generation must insert. -/ +theorem NormalizedChecked.source_anatomy {source : VInductDecl} + (block : NormalizedChecked source) : + ∃ raw, + source.types = [raw] ∧ + raw.name = block.checked.type.name ∧ + raw.uvars = block.checked.type.uvars ∧ + List.Forall₂ CtorHeaderEq raw.ctors block.checked.type.ctors := by + have htypes := block.normalization.shape.2.2 + rw [block.source_types_eq, block.checked.types_eq] at htypes + obtain ⟨htype, -⟩ := List.forall₂_cons.1 htypes + exact ⟨block.sourceType, block.source_types_eq, + htype.1, htype.2.1, htype.2.2⟩ + +theorem NormalizedChecked.uvars_eq {source : VInductDecl} + (block : NormalizedChecked source) : + source.uvars = block.normalization.view.uvars := + block.normalization.shape.1 + +theorem NormalizedChecked.nparams_eq {source : VInductDecl} + (block : NormalizedChecked source) : + source.nparams = block.normalization.view.nparams := + block.normalization.shape.2.1 + +/-- Unpack the complete executable layout certificate used by mixed +raw/view generation. -/ +theorem GenerationChecked.shape {source : VInductDecl} + (gen : GenerationChecked source) : + gen.block.rawParams.length = source.nparams ∧ + gen.block.rawParams.length = gen.block.checked.params.length ∧ + gen.block.rawIndices.length = gen.block.checked.indices.length ∧ + gen.block.ctorPairs.length = gen.block.sourceType.ctors.length ∧ + gen.block.ctorPairs.length = gen.block.checked.constructors.length ∧ + ∀ ctor ∈ gen.block.ctorPairs, + ctor.raw.name = ctor.view.value.name ∧ + ctor.raw.uvars = ctor.view.value.uvars ∧ + (VExpr.telN source.nparams ctor.raw.type).length = source.nparams ∧ + (ctor.rawFields source.nparams).length = ctor.view.fields.length := by + have h := gen.shape_eq + simp only [NormalizedChecked.generationShape, + NormalizedCtor.generationShape, Bool.and_eq_true, beq_iff_eq, + List.all_eq_true] at h + obtain ⟨⟨⟨⟨⟨hparams, hparams'⟩, hindices⟩, hraws⟩, hviews⟩, + hctors⟩ := h + refine ⟨hparams, hparams', hindices, hraws, hviews, ?_⟩ + intro ctor hctor + obtain ⟨⟨⟨hname, hU⟩, htel⟩, hfields⟩ := hctors ctor hctor + exact ⟨hname, hU, htel, hfields⟩ + +theorem pairNormalizedCtors_map_raw : + ∀ (raws : List VConstVal) (views : List CheckedCtor), + raws.length = views.length → + (pairNormalizedCtors raws views).map (·.raw) = raws + | [], [], _ => rfl + | raw :: raws, view :: views, h => by + simp only [pairNormalizedCtors, List.map_cons, List.cons.injEq, true_and] + apply pairNormalizedCtors_map_raw + simpa using h + +theorem pairNormalizedCtors_map_view : + ∀ (raws : List VConstVal) (views : List CheckedCtor), + raws.length = views.length → + (pairNormalizedCtors raws views).map (·.view) = views + | [], [], _ => rfl + | raw :: raws, view :: views, h => by + simp only [pairNormalizedCtors, List.map_cons, List.cons.injEq, true_and] + apply pairNormalizedCtors_map_view + simpa using h + +/-- Membership in an identity pairing recovers the single raw constructor +that supplied both sides of the pair. -/ +theorem pairNormalizedCtors_map_self_mem + {U : Nat} {T : Name} {np ni : Nat} : + ∀ {cs : List VConstVal} {ctor : NormalizedCtor}, + ctor ∈ pairNormalizedCtors cs + (cs.map (CheckedCtor.ofDirect U T np ni)) → + ∃ c ∈ cs, ctor = + ⟨c, CheckedCtor.ofDirect U T np ni c⟩ + | [], _, h => by simp [pairNormalizedCtors] at h + | c :: cs, ctor, h => by + simp only [List.map_cons, pairNormalizedCtors, List.mem_cons] at h + rcases h with rfl | h + · exact ⟨c, .head _, rfl⟩ + · obtain ⟨c', hc', rfl⟩ := + pairNormalizedCtors_map_self_mem h + exact ⟨c', .tail _ hc', rfl⟩ + +/-- Every raw constructor occurs in its canonical identity pair. -/ +theorem pairNormalizedCtors_map_self_contains + {U : Nat} {T : Name} {np ni : Nat} : + ∀ {cs : List VConstVal} {c : VConstVal}, + c ∈ cs → + (⟨c, CheckedCtor.ofDirect U T np ni c⟩ : + NormalizedCtor) ∈ + pairNormalizedCtors cs + (cs.map (CheckedCtor.ofDirect U T np ni)) + | _ :: _, _, .head _ => .head _ + | _ :: _, _, .tail _ hc => + .tail _ (pairNormalizedCtors_map_self_contains hc) + +/-- The identity generation path pairs each stored constructor with its own +direct analyzer descriptor. -/ +theorem Checked.identityGeneration_ctor + {source : VInductDecl} (checked : source.Checked) + {ctor : NormalizedCtor} + (hctor : ctor ∈ checked.identityGeneration.block.ctorPairs) : + ∃ c ∈ checked.type.ctors, ctor = + ⟨c, CheckedCtor.ofDirect source.uvars checked.type.name + source.nparams checked.indices.length c⟩ := by + apply pairNormalizedCtors_map_self_mem + simpa only [Checked.identityGeneration, Checked.identityBlock, + NormalizedChecked.ctorPairs, checked.constructors_eq] using hctor + +/-- Positional pairing neither drops nor reorders raw constructors. -/ +theorem GenerationChecked.rawCtors_eq {source : VInductDecl} + (gen : GenerationChecked source) : + gen.block.ctorPairs.map (·.raw) = gen.block.sourceType.ctors := by + apply pairNormalizedCtors_map_raw + exact gen.shape.2.2.2.1.symm.trans gen.shape.2.2.2.2.1 + +/-- Every checked constructor appears in the same paired position. -/ +theorem GenerationChecked.viewCtors_eq {source : VInductDecl} + (gen : GenerationChecked source) : + gen.block.ctorPairs.map (·.view) = gen.block.checked.constructors := by + apply pairNormalizedCtors_map_view + exact gen.shape.2.2.2.1.symm.trans gen.shape.2.2.2.2.1 + +/-! ### Block-generation positional facts -/ + +theorem pairNormalizedFamilies_map_raw : + ∀ (raws : List VInductiveType) (views : List CheckedFamilyData), + raws.length = views.length → + (pairNormalizedFamilies raws views).map (·.raw) = raws + | [], [], _ => rfl + | raw :: raws, view :: views, h => by + simp only [pairNormalizedFamilies, List.map_cons, + List.cons.injEq, true_and] + apply pairNormalizedFamilies_map_raw + simpa using h + +theorem pairNormalizedFamilies_map_view : + ∀ (raws : List VInductiveType) (views : List CheckedFamilyData), + raws.length = views.length → + (pairNormalizedFamilies raws views).map (·.view) = views + | [], [], _ => rfl + | raw :: raws, view :: views, h => by + simp only [pairNormalizedFamilies, List.map_cons, + List.cons.injEq, true_and] + apply pairNormalizedFamilies_map_view + simpa using h + +/-- Unpack the executable positional gate for mutual generation. -/ +theorem BlockGenerationChecked.shape {source : VInductDecl} + (gen : BlockGenerationChecked source) : + gen.block.rawParams.length = source.nparams ∧ + gen.block.rawParams.length = gen.block.checked.params.length ∧ + gen.families.length = source.types.length ∧ + gen.families.length = gen.block.checked.families.data.length ∧ + ∀ family ∈ gen.families, + family.raw.name = family.view.value.name ∧ + family.raw.uvars = family.view.value.uvars ∧ + (family.rawParams source.nparams).length = source.nparams ∧ + (family.rawIndices source.nparams).length = + family.view.indices.length ∧ + family.ctorPairs.length = family.raw.ctors.length ∧ + family.ctorPairs.length = family.view.constructors.length ∧ + ∀ ctor ∈ family.ctorPairs, + ctor.raw.name = ctor.view.value.name ∧ + ctor.raw.uvars = ctor.view.value.uvars ∧ + (VExpr.telN source.nparams ctor.raw.type).length = + source.nparams ∧ + (ctor.rawFields source.nparams).length = ctor.view.fields.length := by + have h := gen.shape_eq + simp only [NormalizedCheckedBlock.blockGenerationShape, + NormalizedFamily.generationShape, NormalizedCtor.generationShape, + Bool.and_eq_true, beq_iff_eq, List.all_eq_true] at h + obtain ⟨h, -⟩ := h + obtain ⟨⟨⟨⟨hparams, hparams'⟩, hraws⟩, hviews⟩, hfamilies⟩ := h + refine ⟨hparams, hparams', hraws, hviews, ?_⟩ + intro family hfamily + obtain ⟨⟨⟨⟨⟨⟨hname, hU⟩, htel⟩, hindices⟩, + hrawCtors⟩, hviewCtors⟩, hctors⟩ := hfamilies family hfamily + refine ⟨hname, hU, htel, hindices, hrawCtors, hviewCtors, ?_⟩ + intro ctor hctor + obtain ⟨⟨⟨hctorName, hctorU⟩, hctorTel⟩, hctorFields⟩ := + hctors ctor hctor + exact ⟨hctorName, hctorU, hctorTel, hctorFields⟩ + +theorem BlockGenerationChecked.family_uvars {source : VInductDecl} + (gen : BlockGenerationChecked source) {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + family.raw.uvars = source.uvars := by + have h := gen.shape_eq + simp only [NormalizedCheckedBlock.blockGenerationShape, + Bool.and_eq_true, beq_iff_eq, List.all_eq_true] at h + exact (h.2 family hfamily).1 + +theorem BlockGenerationChecked.ctor_uvars {source : VInductDecl} + (gen : BlockGenerationChecked source) {family : NormalizedFamily} + (hfamily : family ∈ gen.families) {ctor : NormalizedCtor} + (hctor : ctor ∈ family.ctorPairs) : + ctor.raw.uvars = source.uvars := by + have h := gen.shape_eq + simp only [NormalizedCheckedBlock.blockGenerationShape, + Bool.and_eq_true, beq_iff_eq, List.all_eq_true] at h + exact (h.2 family hfamily).2 ctor hctor + +theorem BlockGenerationChecked.families_map_raw {source : VInductDecl} + (gen : BlockGenerationChecked source) : + gen.families.map (·.raw) = source.types := by + apply pairNormalizedFamilies_map_raw + exact gen.shape.2.2.1.symm.trans gen.shape.2.2.2.1 + +theorem NormalizedFamily.ctorPairs_map_raw + {source : VInductDecl} {gen : BlockGenerationChecked source} + {family : NormalizedFamily} (hfamily : family ∈ gen.families) : + family.ctorPairs.map (·.raw) = family.raw.ctors := by + apply pairNormalizedCtors_map_raw + exact (gen.shape.2.2.2.2 family hfamily).2.2.2.2.1.symm.trans + (gen.shape.2.2.2.2 family hfamily).2.2.2.2.2.1 + +@[simp] theorem NormalizedFamily.blockCtors_map_raw + (family : NormalizedFamily) : + family.blockCtors.map (·.ctor.raw) = family.ctorPairs.map (·.raw) := by + unfold NormalizedFamily.blockCtors + induction family.ctorPairs with + | nil => rfl + | cons ctor ctors ih => + simp only [List.map_cons, List.cons.injEq, true_and] + exact ih + +theorem flatMap_congr_of_mem {α β : Type} (xs : List α) + (f g : α → List β) (h : ∀ x ∈ xs, f x = g x) : + xs.flatMap f = xs.flatMap g := by + induction xs with + | nil => rfl + | cons x xs ih => + simp only [List.flatMap_cons] + rw [h x (.head _), ih (fun y hy => h y (.tail _ hy))] + +theorem map_flatMap_eq {α β γ : Type} (xs : List α) + (f : α → List β) (g : β → γ) : + (xs.flatMap f).map g = xs.flatMap (fun x => (f x).map g) := by + induction xs with + | nil => rfl + | cons x xs ih => + simp only [List.flatMap_cons, List.map_append] + rw [ih] + +theorem BlockGenerationChecked.flatCtors_map_raw + {source : VInductDecl} (gen : BlockGenerationChecked source) : + gen.flatCtors.map (·.ctor.raw) = source.blockConstructorConstants := by + rw [VInductDecl.blockConstructorConstants, ← gen.families_map_raw] + unfold BlockGenerationChecked.flatCtors NormalizedCheckedBlock.flatCtors + rw [map_flatMap_eq] + simp only [List.flatMap_map] + apply flatMap_congr_of_mem + intro family hfamily + rw [family.blockCtors_map_raw] + exact family.ctorPairs_map_raw hfamily + +theorem BlockGenerationChecked.flatCtor_uvars + {source : VInductDecl} (gen : BlockGenerationChecked source) + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) : + constructor.ctor.raw.uvars = source.uvars := by + simp only [BlockGenerationChecked.flatCtors, + NormalizedCheckedBlock.flatCtors, List.mem_flatMap] at hconstructor + obtain ⟨family, hfamily, hconstructor⟩ := hconstructor + simp only [NormalizedFamily.blockCtors, List.mem_map] at hconstructor + obtain ⟨ctor, hctor, rfl⟩ := hconstructor + exact gen.ctor_uvars hfamily hctor + +/-- The validation staging fold is definitionally the family-constant phase +of block generation, modulo the explicit `toVConstVal` map. -/ +theorem blockTypeConstants_foldlM_eq_stageInductiveTypes + (env : VEnv) (source : VInductDecl) : + source.blockTypeConstants.foldlM + (fun env type => env.addConst type.name type.toVConstant) env = + env.stageInductiveTypes source.types := by + unfold VInductDecl.blockTypeConstants VEnv.stageInductiveTypes + induction source.types generalizing env with + | nil => rfl + | cons type types ih => + simp only [List.map_cons, List.foldlM_cons] + apply Option.bind_congr + intro env' _ + exact ih env' + +/-- Identity normalization is computationally the legacy analyzer. -/ +theorem Normalization.identity_checked? (source : VInductDecl) : + (Normalization.identity source).checked? = source.checked? := rfl + +/-- A successful normalized analysis retains the exact normalization that was +analyzed; callers do not need to restate this projection as an unrelated +equality. -/ +theorem Normalization.check?_normalization + {source : VInductDecl} {norm : Normalization source} + {block : NormalizedChecked source} + (h : norm.check? = some block) : + block.normalization = norm := by + unfold Normalization.check? at h + split at h <;> try contradiction + split at h <;> try contradiction + cases h + rfl + +/-- A successful generation analysis is indexed by the same normalization +retained in its checked block. -/ +theorem Normalization.generation?_normalization + {source : VInductDecl} {norm : Normalization source} + {generation : GenerationChecked source} + (h : norm.generation? = some generation) : + generation.block.normalization = norm := by + unfold Normalization.generation? at h + obtain ⟨block, hblock, hgeneration⟩ := + Option.bind_eq_some_iff.mp h + have hnorm := Normalization.check?_normalization hblock + unfold NormalizedChecked.generation? at hgeneration + split at hgeneration + · have hgeneration' := Option.some.inj hgeneration + rw [← hgeneration'] + exact hnorm + · contradiction + +theorem identityChecked?_isSome (source : VInductDecl) : + (identityChecked? source).isSome = source.checked?.isSome := by + obtain ⟨U, np, types⟩ := source + cases types with + | nil => rfl + | cons type types => + cases types with + | nil => + unfold identityChecked? Normalization.check? + split + · next sourceType hsource => + have htype : type = sourceType := by simpa using hsource + subst sourceType + split + · next checked hchecked => + rw [← Normalization.identity_checked?] + exact (congrArg (fun x => x.isSome) hchecked).symm + · next hchecked => + rw [← Normalization.identity_checked?] + exact (congrArg (fun x => x.isSome) hchecked).symm + · next hsource => exact (hsource type rfl).elim + | cons type' types => rfl + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Normalization.shape [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.NormalizedChecked.source_anatomy [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationChecked.shape [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationChecked.rawCtors_eq [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationChecked.viewCtors_eq [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Normalization.check?_normalization [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Normalization.generation?_normalization [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.identityChecked?_isSome [propext, Classical.choice, Quot.sound] + +/- Identity-normalization compatibility is part of the public artifact +boundary. The conversion itself is computational; its proof fields inherit +the standard `Classical.choice` dependency already present in +`Normalization.identity`'s reflexive header check. -/ +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.analyzer_eq [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.identityBlock_generationShape [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.motiveType_eq_legacy [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.minorTypes_eq_legacy [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.recursor_eq_legacy [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.generatedRules_eq_legacy [propext, Classical.choice, Quot.sound] + +/-- The four executable checks retained by a successful descriptor. Keeping +this bridge centralized lets the public proof API expose logical facts without +each consumer re-destructing `stage3Core`. -/ +theorem Checked.analysis_accepted {decl : VInductDecl} (checked : decl.Checked) : + stage3DirectCore decl.uvars decl.nparams checked.type = true ∧ + namesOK checked.type = true ∧ closedOK checked.type = true ∧ + levelsOK decl.uvars checked.type = true := by + cases decl with + | mk U np tys => + have htypes : tys = [checked.type] := checked.types_eq + have hcore := checked.accepted + simp only [stage3Core, htypes, Bool.and_eq_true] at hcore + exact ⟨hcore.1, hcore.2.1, hcore.2.2.1, hcore.2.2.2⟩ + +/-- Every successful descriptor has pairwise-distinct generated type, +constructor, and recursor names. Downstream proofs should consume this fact +instead of unfolding the boolean analyzer. -/ +theorem Checked.names_nodup {decl : VInductDecl} (checked : decl.Checked) : + checked.names.Nodup := by + rw [checked.names_eq] + exact of_decide_eq_true (by simpa [namesOK] using checked.analysis_accepted.2.1) + +/-- A checked family's type metadata has no loose term variables. -/ +theorem Checked.type_closed {decl : VInductDecl} (checked : decl.Checked) : + checked.type.type.ClosedN 0 := by + have hclosed := checked.analysis_accepted.2.2.1 + simp only [closedOK, Bool.and_eq_true, List.all_eq_true] at hclosed + exact of_decide_eq_true hclosed.1 + +/-- Every constructor type retained by a successful descriptor has no loose +term variables. -/ +theorem Checked.ctor_closed {decl : VInductDecl} (checked : decl.Checked) + {ctor : VConstVal} (hctor : ctor ∈ checked.type.ctors) : ctor.type.ClosedN 0 := by + have hclosed := checked.analysis_accepted.2.2.1 + simp only [closedOK, Bool.and_eq_true, List.all_eq_true] at hclosed + exact of_decide_eq_true (hclosed.2 ctor hctor) + +/-- Every universe annotation in a checked family's type is in range. -/ +theorem Checked.type_levelWF {decl : VInductDecl} (checked : decl.Checked) : + checked.type.type.LevelWF decl.uvars := by + have hlevels := checked.analysis_accepted.2.2.2 + simp only [levelsOK, Bool.and_eq_true, List.all_eq_true] at hlevels + exact of_decide_eq_true hlevels.1 + +/-- Every universe annotation in a checked constructor type is in range. -/ +theorem Checked.ctor_levelWF {decl : VInductDecl} (checked : decl.Checked) + {ctor : VConstVal} (hctor : ctor ∈ checked.type.ctors) : + ctor.type.LevelWF decl.uvars := by + have hlevels := checked.analysis_accepted.2.2.2 + simp only [levelsOK, Bool.and_eq_true, List.all_eq_true] at hlevels + exact of_decide_eq_true (hlevels.2 ctor hctor) + +/-- The direct one-family facts carried by the descriptor, including the +new pre-declaration prohibition on self-reference in parameter/index domains. -/ +theorem Checked.direct_anatomy {decl : VInductDecl} (checked : decl.Checked) : + checked.type.uvars = decl.uvars ∧ checked.params.length = decl.nparams ∧ + checked.resultLevel.WF decl.uvars ∧ + (∀ P ∈ checked.params, P.hasConst checked.type.name = false) ∧ + (∀ I ∈ checked.indices, I.hasConst checked.type.name = false) ∧ + ∀ c ∈ checked.type.ctors, c.uvars = decl.uvars ∧ + VExpr.telN decl.nparams c.type = VExpr.telN decl.nparams checked.type.type ∧ + stage3Ctor decl.uvars checked.type.name decl.nparams checked.indices.length 0 + (VExpr.dropN decl.nparams c.type) = true := by + have hdirect := checked.analysis_accepted.1 + simp only [stage3DirectCore, Bool.and_eq_true, beq_iff_eq, + List.all_eq_true] at hdirect + obtain ⟨⟨⟨⟨hU, hparams⟩, hresult⟩, hformer⟩, hctors⟩ := hdirect + simp only [typeFormerOK, Bool.and_eq_true, List.all_eq_true] at hformer + refine ⟨hU, by simpa [checked.params_eq] using hparams, ?_, ?_, ?_, ?_⟩ + · rw [checked.result_eq] at hresult + simpa using hresult + · simpa [checked.params_eq] using hformer.1 + · simpa [checked.indices_eq] using hformer.2 + · intro c hc + simpa [checked.indices_eq, and_assoc] using hctors c hc + +/-- The raw family payload has the source declaration's universe arity. -/ +theorem NormalizedChecked.sourceType_uvars_eq {source : VInductDecl} + (block : NormalizedChecked source) : + block.sourceType.uvars = source.uvars := by + obtain ⟨raw, hsource, _, hrawU, _⟩ := block.source_anatomy + have hraw : raw = block.sourceType := by + simpa using hsource.symm.trans block.source_types_eq + subst raw + exact hrawU.trans + (block.checked.direct_anatomy.1.trans block.uvars_eq.symm) + +/-- The raw and checked family identities agree. -/ +theorem NormalizedChecked.sourceType_name_eq {source : VInductDecl} + (block : NormalizedChecked source) : + block.sourceType.name = block.checked.type.name := by + obtain ⟨raw, hsource, hname, _, _⟩ := block.source_anatomy + have hraw : raw = block.sourceType := by + simpa using hsource.symm.trans block.source_types_eq + subst raw + exact hname + +/-- Every paired raw constructor has the source declaration's universe +arity. -/ +theorem GenerationChecked.ctor_uvars_eq {source : VInductDecl} + (gen : GenerationChecked source) {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) : + ctor.raw.uvars = source.uvars := by + have hview : ctor.view ∈ gen.block.checked.constructors := by + rw [← gen.viewCtors_eq] + exact List.mem_map.2 ⟨ctor, hctor, rfl⟩ + rw [gen.block.checked.constructors_eq] at hview + have hviewU : + ctor.view.value.uvars = gen.block.normalization.view.uvars := by + obtain ⟨c, hc, hcview⟩ := List.mem_map.1 hview + rw [← hcview] + exact (gen.block.checked.direct_anatomy.2.2.2.2.2 c hc).1 + have hpairU := (gen.shape.2.2.2.2.2 ctor hctor).2.1 + exact hpairU.trans (hviewU.trans gen.block.uvars_eq.symm) + +/-- A paired checked constructor is exactly the direct analyzer result for +the corresponding constructor in the normalized view. The statement is +rewritten to the raw block's public header, whose equality is certified by +normalization shape. -/ +theorem GenerationChecked.viewCtor_ofDirect {source : VInductDecl} + (gen : GenerationChecked source) {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) : + ∃ c ∈ gen.block.checked.type.ctors, + ctor.view = CheckedCtor.ofDirect source.uvars + gen.block.sourceType.name source.nparams + gen.block.checked.indices.length c := by + have hview : ctor.view ∈ gen.block.checked.constructors := by + rw [← gen.viewCtors_eq] + exact List.mem_map.2 ⟨ctor, hctor, rfl⟩ + rw [gen.block.checked.constructors_eq] at hview + obtain ⟨c, hc, hcview⟩ := List.mem_map.1 hview + refine ⟨c, hc, ?_⟩ + rw [gen.block.uvars_eq, gen.block.nparams_eq, + gen.block.sourceType_name_eq] + exact hcview.symm + +/-- Re-index declaration well-formedness onto the normalized checked data. -/ +theorem Checked.wf_of_decl {decl : VInductDecl} (checked : decl.Checked) + (hdecl : decl.WF env) : checked.WF env := by + obtain ⟨-, hwf⟩ := hdecl + obtain ⟨htel, hctors⟩ := hwf checked.type (by + rw [checked.types_eq] + exact .head _) + have hsort : sortLevel decl.nparams checked.type = checked.resultLevel := by + simp only [sortLevel, checked.result_eq] + unfold Checked.WF + refine ⟨?_, fun c hc => ?_⟩ + · simpa [checked.params_eq, checked.indices_eq] using htel + · simpa [checked.params_eq, checked.indices_eq, hsort] using hctors c hc + +/-- Normalized semantic evidence reconstructs the legacy declaration-level +`WF` contract. This is the compatibility direction used while clients migrate +to `Checked.WF`. -/ +theorem Checked.to_declWF {decl : VInductDecl} (checked : decl.Checked) + (hchecked : decl.checked? = some checked) (hwf : checked.WF env) : decl.WF env := by + refine ⟨by simp [singletonStage3, hchecked], ?_⟩ + intro ty hty + rw [checked.types_eq] at hty + obtain rfl := List.mem_singleton.1 hty + have hsort : sortLevel decl.nparams checked.type = checked.resultLevel := by + simp only [sortLevel, checked.result_eq] + unfold Checked.WF at hwf + simpa [checked.params_eq, checked.indices_eq, hsort] using hwf + +/-- The environment-indexed declaration contract is exactly the existence of +the analyzer result together with semantic evidence for its normalized data. -/ +theorem wf_iff_exists_checked {decl : VInductDecl} : + decl.WF env ↔ ∃ checked, decl.checked? = some checked ∧ checked.WF env := by + constructor + · intro hdecl + obtain ⟨checked, hchecked⟩ := exists_checked_of_singletonStage3 hdecl.1 + exact ⟨checked, hchecked, checked.wf_of_decl hdecl⟩ + · rintro ⟨checked, hchecked, hwf⟩ + exact checked.to_declWF hchecked hwf + +/- Keep the first exported descriptor invariants on the same accepted Theory +axiom baseline as the transaction API. -/ +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.names_nodup [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.type_closed [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.ctor_closed [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.analysis_accepted [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.type_levelWF [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.ctor_levelWF [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.direct_anatomy [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.wf_of_decl [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.to_declWF [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.wf_iff_exists_checked [propext, Quot.sound] + +/-- Unpack `stage3` for a declaration already known (from `addInduct` +success) to have a singleton type list. -/ +theorem singletonStage3_anatomy {U np ty} + (h : singletonStage3 ⟨U, np, [ty]⟩ = true) : + ty.uvars = U ∧ (VExpr.telN np ty.type).length = np ∧ + (∃ l, VExpr.resultOf (VExpr.dropN np ty.type) = .sort l ∧ l.WF U) ∧ + (∀ I ∈ ctorFields (VExpr.dropN np ty.type), I.hasConst ty.name = false) ∧ + ∀ c ∈ ty.ctors, c.uvars = U ∧ + VExpr.telN np c.type = VExpr.telN np ty.type ∧ + stage3Ctor U ty.name np (ctorFields (VExpr.dropN np ty.type)).length 0 + (VExpr.dropN np c.type) = true := by + obtain ⟨checked, -⟩ := exists_checked_of_singletonStage3 h + have hcore := checked.accepted + simp only [stage3Core, Bool.and_eq_true] at hcore + have hdirect := hcore.1 + simp only [stage3DirectCore, Bool.and_eq_true, beq_iff_eq, + List.all_eq_true] at hdirect + obtain ⟨⟨⟨⟨h1, h2⟩, h3⟩, h4⟩, h6⟩ := hdirect + simp only [typeFormerOK, Bool.and_eq_true, List.all_eq_true] at h4 + refine ⟨h1, h2, ?_, fun I hI => by simpa [Bool.not_eq_true'] using h4.2 I hI, + fun c hc => by simpa [and_assoc] using h6 c hc⟩ + split at h3 + · next l heq => exact ⟨l, heq, by simpa using h3⟩ + · exact Bool.noConfusion h3 + +end VInductDecl + +/-! ## Context and spine lemmas -/ + +theorem Lookup.append {A : VExpr} : ∀ (Δ : List VExpr) {Γ}, + Lookup (Δ ++ A :: Γ) Δ.length (A.liftN (Δ.length + 1)) + | [], _ => .zero + | B :: Δ, Γ => by + simpa [← VExpr.liftN_succ] using (Lookup.append (A := A) Δ (Γ := Γ)).succ (A := B) + +theorem Lookup.append_closed {A : VExpr} (hA : A.ClosedN 0) (Δ : List VExpr) {Γ} : + Lookup (Δ ++ A :: Γ) Δ.length A := by + simpa [hA.liftN_eq (Nat.zero_le _)] using Lookup.append (A := A) Δ (Γ := Γ) + +theorem Lookup.of_getElem? : ∀ {Γ : List VExpr} {i : Nat} {A : VExpr}, + Γ[i]? = some A → Lookup Γ i (A.liftN (i+1)) + | B :: _, 0, A, h => by + obtain rfl : B = A := by simpa using h + exact .zero + | B :: Γ, i+1, A, h => by + have := Lookup.of_getElem? (Γ := Γ) (i := i) (A := A) (by simpa using h) + simpa [← VExpr.liftN_succ] using this.succ (A := B) + +theorem Lookup.of_getElem?_closed {Γ : List VExpr} {i : Nat} {A : VExpr} + (h : Γ[i]? = some A) (hA : A.ClosedN 0) : Lookup Γ i A := by + simpa [hA.liftN_eq (Nat.zero_le _)] using Lookup.of_getElem? h + +/-- Right-associated: the element right past a two-part prefix. -/ +theorem getElem?_rstack3 {α} (Δ mid : List α) (a : α) (Γ : List α) {i : Nat} + (h : i = Δ.length + mid.length) : (Δ ++ (mid ++ a :: Γ))[i]? = some a := by + rw [List.getElem?_append_right (by omega), List.getElem?_append_right (by omega), + show i - Δ.length - mid.length = 0 from by omega] + rfl + +/-- Right-associated: an element inside the middle block. -/ +theorem getElem?_rstack_mid {α} (Δ mid Γ : List α) {i : Nat} + (h1 : Δ.length ≤ i) (h2 : i - Δ.length < mid.length) : + (Δ ++ (mid ++ Γ))[i]? = mid[i - Δ.length]? := by + rw [List.getElem?_append_right h1, List.getElem?_append_left h2] + +/-- The element right past a two-part prefix. -/ +theorem getElem?_stack3 {α} (Δ mid Γ : List α) (a : α) {i : Nat} + (h : i = Δ.length + mid.length) : (Δ ++ mid ++ a :: Γ)[i]? = some a := by + rw [List.append_assoc, List.getElem?_append_right (by omega), + show i - Δ.length = mid.length from by omega, + List.getElem?_append_right (Nat.le_refl _), Nat.sub_self] + rfl + +/-- An element inside the middle block of a three-part context. -/ +theorem getElem?_stack_mid {α} (Δ mid Γ : List α) {i : Nat} + (h1 : Δ.length ≤ i) (h2 : i - Δ.length < mid.length) : + (Δ ++ mid ++ Γ)[i]? = mid[i - Δ.length]? := by + rw [List.append_assoc, List.getElem?_append_right h1, + List.getElem?_append_left h2] + +namespace VEnv + +/-- A typed Theory expression cannot mention a constant absent from its +environment. -/ +theorem HasType.hasConst_false_of_absent + {env : VEnv} {U : Nat} {Γ : List VExpr} + {name : Name} {e A : VExpr} + (henv : env.Ordered) (hΓ : OnCtx Γ (env.IsType U)) + (absent : env.constants name = none) + (typed : env.HasType U Γ e A) : + e.hasConst name = false := by + induction e generalizing Γ A with + | bvar | sort => rfl + | const constant levels => + by_cases equality : constant = name + · subst constant + obtain ⟨ci, present, levelWF, arity⟩ := + typed.const_inv henv hΓ + rw [absent] at present + contradiction + · simp [VExpr.hasConst, equality] + | app function argument functionIH argumentIH => + obtain ⟨domain, body, functionType, argumentType⟩ := + typed.app_inv henv hΓ + simp only [VExpr.hasConst, functionIH hΓ functionType, + argumentIH hΓ argumentType, Bool.false_or] + | lam domain body domainIH bodyIH => + obtain ⟨domainType, bodyWF⟩ := typed.lam_inv henv hΓ + obtain ⟨domainLevel, domainHasType⟩ := domainType + obtain ⟨bodyType, bodyHasType⟩ := bodyWF + have nextContextWF : OnCtx (domain :: Γ) (env.IsType U) := by + change OnCtx Γ (env.IsType U) ∧ env.IsType U Γ domain + exact ⟨hΓ, ⟨domainLevel, domainHasType⟩⟩ + simp only [VExpr.hasConst, domainIH hΓ domainHasType, + bodyIH nextContextWF bodyHasType, Bool.false_or] + | forallE domain body domainIH bodyIH => + obtain ⟨domainType, bodyType⟩ := typed.forallE_inv henv + obtain ⟨domainLevel, domainHasType⟩ := domainType + obtain ⟨bodyLevel, bodyHasType⟩ := bodyType + have nextContextWF : OnCtx (domain :: Γ) (env.IsType U) := by + change OnCtx Γ (env.IsType U) ∧ env.IsType U Γ domain + exact ⟨hΓ, ⟨domainLevel, domainHasType⟩⟩ + simp only [VExpr.hasConst, domainIH hΓ domainHasType, + bodyIH nextContextWF bodyHasType, Bool.false_or] + +/-- The spine `bvarRevRange Δ.length As.length` selects exactly the binders +`As` (reversed into the context past `Δ`), when all of `As` are closed. -/ +theorem hasType_bvarRevRange {env : VEnv} {U : Nat} : + ∀ {As : List VExpr}, (∀ A ∈ As, A.ClosedN 0) → ∀ {Δ Γ₀ : List VExpr}, + List.Forall₂ (env.HasType U (Δ ++ As.reverse ++ Γ₀)) + (VExpr.bvarRevRange Δ.length As.length) As + | [], _, _, _ => .nil + | A :: As, h, Δ, Γ₀ => by + refine .cons (.bvar ?_) ?_ + · have := Lookup.append_closed (h _ (.head _)) (Δ ++ As.reverse) (Γ := Γ₀) + simpa [List.append_assoc] using this + · have := hasType_bvarRevRange (env := env) (U := U) (As := As) + (fun A h' => h _ (.tail _ h')) (Δ := Δ) (Γ₀ := A :: Γ₀) + simpa [List.append_assoc] using this + +theorem ClosedN.forallN_of_all {As : List VExpr} (hAs : ∀ A ∈ As, A.ClosedN 0) + {B : VExpr} (hB : B.ClosedN 0) {k : Nat} : (VExpr.forallN As B).ClosedN k := by + induction As generalizing k with + | nil => exact hB.mono (Nat.zero_le _) + | cons A As ih => + exact ⟨(hAs _ (.head _)).mono (Nat.zero_le _), ih fun A h => hAs _ (.tail _ h)⟩ + +/-- Substituting a variable for the innermost binder of a lifted term is a +smaller lift: the telescope-self-application identity. Unconditional. -/ +theorem _root_.Ix.Theory.Named.VExpr.liftN_succ_inst_bvar (e : VExpr) : + ∀ (s k : Nat), (e.liftN (s+1) (k+1)).inst (.bvar s) k = e.liftN s k := by + induction e with intro s k + | bvar j => + show VExpr.instVar (liftVar (s+1) j (k+1)) (.bvar s) k = .bvar (liftVar s j k) + unfold liftVar + rcases Nat.lt_trichotomy j k with h | rfl | h + · rw [if_pos (Nat.lt_succ_of_lt h), if_pos h] + simp [VExpr.instVar, h] + · rw [if_pos (Nat.lt_succ_self _), if_neg (Nat.lt_irrefl _)] + show VExpr.instVar j (.bvar s) j = _ + rw [show VExpr.instVar j (.bvar s) j = (VExpr.bvar s).liftN j from by + unfold VExpr.instVar; rw [if_neg (Nat.lt_irrefl _), if_pos rfl]] + show VExpr.bvar (liftVar j s) = _ + rw [liftVar_base, Nat.add_comm] + · rw [if_neg (by omega), if_neg (by omega)] + simp only [VExpr.instVar] + rw [if_neg (by omega), if_neg (by omega)] + congr 1; omega + | sort | const => intros; rfl + | app f a ihf iha => simp [VExpr.liftN, VExpr.inst, ihf, iha] + | lam A b ihA ihb | forallE A b ihA ihb => + simp [VExpr.liftN, VExpr.inst, ihA s k, ihb s (k+1)] + +/-- Applying `f : (∀ As, B).liftN Δ.length` to the spine of variables +referring to its own binders, sitting in the context right below `Δ`. +The lift on the type in the hypothesis is what makes the invariant close +over the recursion; no closedness is needed. -/ +theorem HasType.appN_selfSpine {env : VEnv} {U : Nat} : + ∀ {As : List VExpr} {B : VExpr} {Δ Γ : List VExpr} {f}, + env.HasType U (Δ ++ As.reverse ++ Γ) f + ((VExpr.forallN As B).liftN (Δ.length + As.length)) → + env.HasType U (Δ ++ As.reverse ++ Γ) + (f.appN (VExpr.bvarRevRange Δ.length As.length)) (B.liftN Δ.length) + | [], B, Δ, Γ, f, hf => by simpa using hf + | A :: As, B, Δ, Γ, f, hf => by + have harg : env.HasType U (Δ ++ (A :: As).reverse ++ Γ) + (.bvar (Δ.length + As.length)) (A.liftN (Δ.length + As.length + 1)) := by + have := Lookup.append (A := A) (Δ ++ As.reverse) (Γ := Γ) + simp only [List.length_append, List.length_reverse] at this + exact .bvar (by simpa [List.append_assoc, Nat.add_assoc] using this) + have happ := HasType.app hf harg + simp only [List.length_cons, Nat.add_succ] at happ + rw [VExpr.liftN_succ_inst_bvar] at happ + have := HasType.appN_selfSpine (As := As) (B := B) (Δ := Δ) (Γ := A :: Γ) + (f := f.app (.bvar (Δ.length + As.length))) (by simpa [List.append_assoc] using happ) + simpa [List.append_assoc, VExpr.bvarRevRange] using this + +/-- The closed-telescope entry point for `appN_selfSpine`. -/ +theorem HasType.appN_selfSpine' {env : VEnv} {U : Nat} + {As : List VExpr} {B : VExpr} {Δ Γ : List VExpr} {f} + (hcl : (VExpr.forallN As B).ClosedN 0) + (hf : env.HasType U (Δ ++ As.reverse ++ Γ) f (VExpr.forallN As B)) : + env.HasType U (Δ ++ As.reverse ++ Γ) + (f.appN (VExpr.bvarRevRange Δ.length As.length)) (B.liftN Δ.length) := + HasType.appN_selfSpine (by rwa [hcl.liftN_eq (Nat.zero_le _)]) + +/-- Application of a closed non-dependent telescope. -/ +theorem HasType.appN_closed {env : VEnv} {U : Nat} {Γ : List VExpr} : + ∀ {As : List VExpr}, (∀ A ∈ As, A.ClosedN 0) → ∀ {B : VExpr}, B.ClosedN 0 → + ∀ {f args}, env.HasType U Γ f (VExpr.forallN As B) → + List.Forall₂ (env.HasType U Γ) args As → + env.HasType U Γ (f.appN args) B + | [], _, _, _, _, _, hf, .nil => hf + | A :: As, hAs, B, hB, f, a :: as, hf, .cons ha hargs => by + have h2 : env.HasType U Γ (f.app a) ((VExpr.forallN As B).inst a) := .app hf ha + rw [(ClosedN.forallN_of_all (fun A h => hAs _ (.tail _ h)) hB).instN_eq + (Nat.zero_le _)] at h2 + exact appN_closed (fun A h => hAs _ (.tail _ h)) hB (f := f.app a) h2 hargs + +theorem isType_forallN_free {env : VEnv} {U : Nat} {As : List VExpr} + (hAs : ∀ A ∈ As, ∀ Γ, env.IsType U Γ A) + {B : VExpr} (hB : ∀ Γ, env.IsType U Γ B) : ∀ Γ, env.IsType U Γ (VExpr.forallN As B) := by + induction As with + | nil => exact hB + | cons A As ih => + exact fun Γ => (hAs _ (.head _) Γ).forallE + (ih (fun A h => hAs _ (.tail _ h)) (A :: Γ)) + +theorem HasType.lamN {env : VEnv} {U : Nat} : ∀ {As Γ body B}, + OnTel env U Γ As → env.HasType U (As.reverse ++ Γ) body B → + env.HasType U Γ (VExpr.lamN As body) (VExpr.forallN As B) + | [], _, _, _, _, hb => hb + | A :: As, Γ, body, B, ⟨⟨_, hA⟩, hT⟩, hb => + HasType.lam hA (HasType.lamN hT (by simpa [List.append_assoc] using hb)) + +theorem IsType.forallN {env : VEnv} {U : Nat} : ∀ {As Γ B}, + OnTel env U Γ As → env.IsType U (As.reverse ++ Γ) B → + env.IsType U Γ (VExpr.forallN As B) + | [], _, _, _, hB => hB + | A :: As, Γ, B, ⟨hA, hT⟩, hB => + IsType.forallE hA (IsType.forallN hT (by simpa [List.append_assoc] using hB)) + +theorem onTel_of_free {env : VEnv} {U : Nat} : ∀ {As Γ}, + (∀ A ∈ As, ∀ Γ', env.IsType U Γ' A) → OnTel env U Γ As + | [], _, _ => trivial + | _ :: _, _, h => + ⟨h _ (.head _) _, onTel_of_free fun _ h' Γ' => h _ (.tail _ h') Γ'⟩ + +theorem OnTel.append {env : VEnv} {U : Nat} : ∀ {As Bs Γ}, + OnTel env U Γ As → OnTel env U (As.reverse ++ Γ) Bs → OnTel env U Γ (As ++ Bs) + | [], _, _, _, h2 => h2 + | _ :: As, Bs, Γ, ⟨hA, h1⟩, h2 => + ⟨hA, OnTel.append h1 (by simpa [List.append_assoc] using h2)⟩ + +theorem OnTel.of_append {env : VEnv} {U : Nat} : ∀ {As Bs Γ}, + OnTel env U Γ (As ++ Bs) → OnTel env U Γ As ∧ OnTel env U (As.reverse ++ Γ) Bs + | [], _, _, h => ⟨trivial, h⟩ + | A :: As, Bs, Γ, ⟨hA, hT⟩ => by + obtain ⟨h1, h2⟩ := OnTel.of_append (As := As) hT + exact ⟨⟨hA, h1⟩, by simpa [List.append_assoc] using h2⟩ + +/-- Push a context lift under a reversed telescope: inserting binders below +shifts each telescope entry at its own depth. -/ +theorem _root_.Ix.Theory.Named.Ctx.LiftN.consTel {n : Nat} : ∀ (As : List VExpr) {k : Nat} + {Γ Γ' : List VExpr}, Ctx.LiftN n k Γ Γ' → + Ctx.LiftN n (As.length + k) (As.reverse ++ Γ) + ((VExpr.liftTelN n As k).reverse ++ Γ') + | [], k, Γ, Γ', W => by simpa using W + | A :: As, k, Γ, Γ', W => by + have h := Ctx.LiftN.consTel As (Ctx.LiftN.succ (A := A) W) + rw [show As.length + (k+1) = (A :: As).length + k from by simp; omega] at h + simpa [VExpr.liftTelN, List.append_assoc] using h + +/-- Weakening a telescope: inserting binders at depth `k` of the context +shifts each entry at its own depth. -/ +theorem OnTel.weakN {env : VEnv} {U n : Nat} (henv : env.Ordered) : + ∀ {As Γ Γ' k}, Ctx.LiftN n k Γ Γ' → OnTel env U Γ As → + OnTel env U Γ' (VExpr.liftTelN n As k) + | [], _, _, _, _, _ => trivial + | _ :: _, _, _, _, W, ⟨hA, hT⟩ => + ⟨hA.weakN henv W, OnTel.weakN henv W.succ hT⟩ + +/-- Universe instantiation of a telescope. -/ +theorem OnTel.instL {env : VEnv} {U U' : Nat} {ls : List VLevel} + (hls : ∀ l ∈ ls, l.WF U') : + ∀ {As Γ}, OnTel env U Γ As → + OnTel env U' (Γ.map (VExpr.instL ls)) (As.map (VExpr.instL ls)) + | [], _, _ => trivial + | _ :: As, Γ, ⟨hA, hT⟩ => + ⟨hA.instL hls, by simpa using OnTel.instL hls (As := As) (Γ := _ :: Γ) hT⟩ + +/-! ### Spine typing -/ + +theorem SpineWF.hasType_appN {env : VEnv} {U : Nat} {Γ : List VExpr} : + ∀ {es : List VExpr} {A B f : VExpr}, env.SpineWF U Γ A es B → + env.HasType U Γ f A → env.HasType U Γ (f.appN es) B := by + intro es + induction es with intro A B f h hf + | nil => cases h; exact hf + | cons e es ih => + cases h with + | cons he hrest => + exact ih hrest (hf.app he) + +/-- Concatenate two adjacent, well-typed application spines. -/ +theorem SpineWF.append {env : VEnv} {U : Nat} {Γ : List VExpr} : + ∀ {es : List VExpr} {A B : VExpr}, env.SpineWF U Γ A es B → + ∀ {es' : List VExpr} {C : VExpr}, env.SpineWF U Γ B es' C → + env.SpineWF U Γ A (es ++ es') C + | [], _, _, .nil, _, _, h' => h' + | _ :: _, _, _, .cons he hrest, _, _, h' => + .cons he (SpineWF.append hrest h') + +/-- Split a well-typed application spine at an explicit list prefix. -/ +theorem SpineWF.split {env : VEnv} {U : Nat} {Γ : List VExpr} : + ∀ {front suffix : List VExpr} {A B : VExpr}, + env.SpineWF U Γ A (front ++ suffix) B → + ∃ cursor, env.SpineWF U Γ A front cursor ∧ + env.SpineWF U Γ cursor suffix B + | [], suffix, A, B, h => ⟨A, .nil, by simpa using h⟩ + | _ :: front, suffix, _, _, .cons he hrest => by + obtain ⟨cursor, hfront, hsuffix⟩ := SpineWF.split hrest + exact ⟨cursor, .cons he hfront, hsuffix⟩ + +/-- Extend a well-typed application spine by one final argument. -/ +theorem SpineWF.snoc {env : VEnv} {U : Nat} {Γ : List VExpr} {e D C : VExpr} : + ∀ {es : List VExpr} {A : VExpr}, env.SpineWF U Γ A es (.forallE D C) → + env.HasType U Γ e D → env.SpineWF U Γ A (es ++ [e]) (C.inst e) + | [], _, .nil, he => .cons he .nil + | _ :: _, _, .cons ha hrest, he => + .cons ha (SpineWF.snoc hrest he) + +/-- Retarget a spine judgment along a pi with the same domains: the +result is the iterated instantiation of the new codomain. -/ +theorem SpineWF.retarget {env : VEnv} {U : Nat} {Γ : List VExpr} {es : List VExpr} : + ∀ {Δ : List VExpr} {C B : VExpr}, env.SpineWF U Γ (VExpr.forallN Δ C) es B → + es.length = Δ.length → ∀ (C' : VExpr), + env.SpineWF U Γ (VExpr.forallN Δ C') es (VExpr.instRev C' es) := by + induction es with intro Δ C B h hlen C' + | nil => + obtain rfl : Δ = [] := by + cases Δ with + | nil => rfl + | cons _ _ => simp at hlen + cases h + exact .nil + | cons e es ih => + cases Δ with + | nil => simp at hlen + | cons A Δ => + cases h with + | cons he hrest => + have hlen' : es.length = Δ.length := by simpa using hlen + refine .cons he ?_ + rw [VExpr.instN_forallN] at hrest + have := ih hrest (by simp [VExpr.instTelN_length, hlen']) (C'.inst e Δ.length) + show env.SpineWF U Γ ((VExpr.forallN Δ C').inst e) es + (VExpr.instRev (C'.inst e es.length) es) + rw [VExpr.instN_forallN, Nat.zero_add, hlen'] + exact this + +/-- A spine consuming a full telescope and ending in the same sort has +exactly one argument per telescope binder. -/ +theorem SpineWF.forallN_sort_length + {env : VEnv} {U : Nat} {Γ : List VExpr} {l : VLevel} : + ∀ {As es}, env.SpineWF U Γ (VExpr.forallN As (.sort l)) es (.sort l) → + es.length = As.length + | [], [], _ => rfl + | [], _ :: _, h => by cases h + | _ :: _, [], h => by cases h + | A :: As, e :: es, h => by + cases h with + | cons he hrest => + rw [VExpr.instN_forallN] at hrest + have hlen := SpineWF.forallN_sort_length hrest + simpa [VExpr.instTelN_length] using congrArg Nat.succ hlen + +end VEnv + +/-! ## The induction-hypothesis telescope under lifting -/ + +namespace VInductDecl + +/-- `ihsFrom` after the motive-directed lift into the rule context. -/ +def ihsR (m k : Nat) : List (Nat × List VExpr) → Nat → List VExpr + | [], _ => [] + | (j, idxs) :: rs, p => + VExpr.appN (.bvar (k + (m + p))) + ((idxs.map fun e => ((e.liftN 1 j).liftN (m-j+p)).liftN k (m+p)) ++ + [.bvar (m-1-j+p)]) :: + ihsR m k rs (p+1) + +/-- Recursive-field positions are bounded by the field count. -/ +theorem recPairs_lt {U : Nat} {T : Name} {np ni : Nat} : ∀ {Bs : List VExpr} {j₀ : Nat}, + ∀ q ∈ recPairs U T np ni Bs j₀, q.1 < j₀ + Bs.length + | B :: Bs, j₀, q, h => by + unfold recPairs at h + split at h + · rcases List.mem_cons.1 h with rfl | h + · simp + · have := recPairs_lt _ h + simp only [List.length_cons]; omega + · have := recPairs_lt _ h + simp only [List.length_cons]; omega + +theorem recPairs_ge {U : Nat} {T : Name} {np ni : Nat} : ∀ {Bs : List VExpr} {j₀ : Nat}, + ∀ q ∈ recPairs U T np ni Bs j₀, j₀ ≤ q.1 + | _ :: Bs, j₀, q, h => by + unfold recPairs at h + split at h + · rcases List.mem_cons.1 h with rfl | h + · exact Nat.le_refl _ + · exact Nat.le_of_succ_le (recPairs_ge _ h) + · exact Nat.le_of_succ_le (recPairs_ge _ h) + +/-- Recursive positions really hold a recursive field, and the recorded +index arguments are its index arguments. -/ +theorem recPairs_getElem {U : Nat} {T : Name} {np ni : Nat} : ∀ {Bs : List VExpr} {j₀ : Nat}, + ∀ q ∈ recPairs U T np ni Bs j₀, ∃ B, Bs[q.1 - j₀]? = some B ∧ + isRecField U T np ni q.1 B = true ∧ q.2 = recFieldIdxs np B + | B :: Bs, j₀, q, h => by + unfold recPairs at h + split at h + · next heq => + rcases List.mem_cons.1 h with rfl | h + · exact ⟨B, by simp, heq, rfl⟩ + · have h1 := recPairs_ge _ h + obtain ⟨B', hB', hrec, hidx⟩ := recPairs_getElem _ h + refine ⟨B', ?_, hrec, hidx⟩ + rw [show q.1 - j₀ = (q.1 - (j₀+1)) + 1 from by omega] + simpa using hB' + · have h1 := recPairs_ge _ h + obtain ⟨B', hB', hrec, hidx⟩ := recPairs_getElem _ h + refine ⟨B', ?_, hrec, hidx⟩ + rw [show q.1 - j₀ = (q.1 - (j₀+1)) + 1 from by omega] + simpa using hB' + +/-- Every analyzed recursive argument names a field within the source +telescope. -/ +theorem recArgs_lt {U : Nat} {T : Name} {np ni : Nat} : ∀ {Bs : List VExpr} {j₀ : Nat}, + ∀ r ∈ recArgs U T np ni Bs j₀, r.fieldIndex < j₀ + Bs.length + | B :: Bs, j₀, r, h => by + unfold recArgs at h + split at h + · next r₀ hr₀ => + rcases List.mem_cons.1 h with rfl | h + · have hj := (recArg?_eq hr₀).1 + simp only [List.length_cons] + omega + · have := recArgs_lt _ h + simp only [List.length_cons]; omega + · have := recArgs_lt _ h + simp only [List.length_cons]; omega + +/-- Recursive-argument field positions never precede the starting depth. -/ +theorem recArgs_ge {U : Nat} {T : Name} {np ni : Nat} : ∀ {Bs : List VExpr} {j₀ : Nat}, + ∀ r ∈ recArgs U T np ni Bs j₀, j₀ ≤ r.fieldIndex + | _ :: Bs, j₀, r, h => by + unfold recArgs at h + split at h + · next r₀ hr₀ => + rcases List.mem_cons.1 h with rfl | h + · simp [(recArg?_eq hr₀).1] + · exact Nat.le_of_succ_le (recArgs_ge _ h) + · exact Nat.le_of_succ_le (recArgs_ge _ h) + +/-- An analyzed recursive argument is backed by the field at its recorded +position, and re-analysis returns the same normalized descriptor. -/ +theorem recArgs_getElem {U : Nat} {T : Name} {np ni : Nat} : + ∀ {Bs : List VExpr} {j₀ : Nat}, ∀ r ∈ recArgs U T np ni Bs j₀, + ∃ B, Bs[r.fieldIndex - j₀]? = some B ∧ + recArg? U T np ni r.fieldIndex B = some r + | B :: Bs, j₀, r, h => by + unfold recArgs at h + split at h + · next r₀ hr₀ => + rcases List.mem_cons.1 h with rfl | h + · have hj := (recArg?_eq hr₀).1 + subst hj + exact ⟨B, by simp, hr₀⟩ + · have hge := recArgs_ge _ h + obtain ⟨B', hB', hr⟩ := recArgs_getElem _ h + refine ⟨B', ?_, hr⟩ + rw [show r.fieldIndex - j₀ = (r.fieldIndex - (j₀+1)) + 1 from by omega] + simpa using hB' + · have hge := recArgs_ge _ h + obtain ⟨B', hB', hr⟩ := recArgs_getElem _ h + refine ⟨B', ?_, hr⟩ + rw [show r.fieldIndex - j₀ = (r.fieldIndex - (j₀+1)) + 1 from by omega] + simpa using hB' + +theorem ihsFrom_liftN (m k : Nat) : ∀ (rs : List (Nat × List VExpr)), + (∀ q ∈ rs, q.1 < m) → ∀ (p : Nat) (X : VExpr), + (VExpr.forallN (ihsFrom m rs p) X).liftN k (m + p) = + VExpr.forallN (ihsR m k rs p) (X.liftN k (m + p + rs.length)) + | [], _, _, _ => rfl + | (j, idxs) :: rs, hm, p, X => by + show VExpr.forallE _ _ = VExpr.forallE _ _ + congr 1 + · rw [VExpr.liftN_appN, List.map_append, List.map_map, + show ((VExpr.bvar (m+p)).liftN k (m+p)) = .bvar (k+(m+p)) from by + show VExpr.bvar (liftVar k (m+p) (m+p)) = _ + rw [liftVar_le (Nat.le_refl _)], + show ([VExpr.bvar (m-1-j+p)]).map (VExpr.liftN k · (m+p)) = + [VExpr.bvar (m-1-j+p)] from by + show [VExpr.bvar (liftVar k (m-1-j+p) (m+p))] = _ + rw [liftVar_lt (show m-1-j+p < m+p from by + have := hm _ (List.Mem.head _); simp at this; omega)]] + rfl + · rw [show m + p + 1 = m + (p+1) from rfl, + ihsFrom_liftN m k rs (fun q h => hm q (.tail _ h)) (p+1) X, + show m + (p+1) + rs.length = m + p + (rs.length+1) from by omega] + rfl + +/-- `ihsFrom_liftN` with the cutoff generalized, for syntactic rewriting. -/ +theorem ihsFrom_liftN' (m k : Nat) (rs : List (Nat × List VExpr)) + (hm : ∀ q ∈ rs, q.1 < m) (p : Nat) (X : VExpr) {cut : Nat} (hcut : cut = m + p) : + (VExpr.forallN (ihsFrom m rs p) X).liftN k cut = + VExpr.forallN (ihsR m k rs p) (X.liftN k (m + p + rs.length)) := by + rw [hcut]; exact ihsFrom_liftN m k rs hm p X + +theorem ihsR_liftN1 (m k : Nat) : ∀ (rs : List (Nat × List VExpr)) (p c : Nat), c ≤ p → + (∀ q ∈ rs, q.1 < m) → ∀ (X : VExpr), + VExpr.forallN (ihsR m k rs (p+1)) (X.liftN 1 (c + rs.length)) = + (VExpr.forallN (ihsR m k rs p) X).liftN 1 c + | [], p, c, _, _, X => by simp [ihsR, VExpr.forallN] + | (j, idxs) :: rs, p, c, hc, hm, X => by + have hjm : j < m := by have := hm _ (List.Mem.head _); simpa using this + show VExpr.forallE _ _ = VExpr.forallE _ _ + congr 1 + · rw [VExpr.liftN_appN, List.map_append, List.map_map, + show ((VExpr.bvar (k+(m+p))).liftN 1 c) = .bvar (k+(m+(p+1))) from by + show VExpr.bvar (liftVar 1 (k+(m+p)) c) = _ + rw [liftVar_le (by omega)] + congr 1; omega, + show ([VExpr.bvar (m-1-j+p)]).map (VExpr.liftN 1 · c) = + [VExpr.bvar (m-1-j+(p+1))] from by + show [VExpr.bvar (liftVar 1 (m-1-j+p) c)] = _ + rw [liftVar_le (by omega)] + congr 2; omega] + refine congrArg (VExpr.appN _) (congrArg (· ++ [VExpr.bvar (m-1-j+(p+1))]) ?_) + refine List.map_congr_left fun e _ => .symm ?_ + show (((e.liftN 1 j).liftN (m-j+p)).liftN k (m+p)).liftN 1 c = _ + rw [VExpr.liftN_liftN_comm _ 1 k c (m+p) (by omega), + show ((e.liftN 1 j).liftN (m-j+p)).liftN 1 c = + (e.liftN 1 j).liftN (m-j+(p+1)) from by + rw [VExpr.liftN'_liftN' (Nat.zero_le _) (by omega)] + rfl] + rfl + · rw [show c + ((j, idxs) :: rs).length = (c+1) + rs.length from by simp; omega] + exact ihsR_liftN1 m k rs (p+1) (c+1) (by omega) (fun q h => hm q (.tail _ h)) X + +theorem ihsFrom_length (m : Nat) : ∀ (rs : List (Nat × List VExpr)) (p : Nat), + (ihsFrom m rs p).length = rs.length + | [], _ => rfl + | (_, _) :: rs, p => by simp [ihsFrom, ihsFrom_length m rs (p+1)] + +theorem ihsFromRecArgs_length (m : Nat) : ∀ (rs : List RecArg) (p : Nat), + (ihsFromRecArgs m rs p).length = rs.length + | [], _ => rfl + | _ :: rs, p => by simp [ihsFromRecArgs, ihsFromRecArgs_length m rs (p+1)] + +theorem minorTypes_length (U : Nat) (T : Name) (np : Nat) (ty : VInductiveType) : + ∀ (cs : List VConstVal) (i : Nat), (minorTypes U T np ty cs i).length = cs.length + | [], _ => rfl + | _ :: cs, i => by simp [minorTypes, minorTypes_length U T np ty cs (i+1)] + +theorem minorTypesRec_length (U : Nat) (T : Name) (np : Nat) (ty : VInductiveType) : + ∀ (cs : List VConstVal) (i : Nat), (minorTypesRec U T np ty cs i).length = cs.length + | [], _ => rfl + | _ :: cs, i => by + simp [minorTypesRec, minorTypesRec_length U T np ty cs (i+1)] + +theorem bvarRevRange_liftN_ge : ∀ (m off n k : Nat), k ≤ off → + (VExpr.bvarRevRange off m).map (VExpr.liftN n · k) = VExpr.bvarRevRange (n + off) m + | 0, _, _, _, _ => rfl + | m+1, off, n, k, h => by + show VExpr.bvar _ :: _ = VExpr.bvar _ :: _ + rw [bvarRevRange_liftN_ge m off n k h] + congr 2 + show liftVar n (off + m) k = n + off + m + rw [liftVar_le (by omega)]; omega + +theorem bvarRevRange_instL : ∀ (m off : Nat) (ls : List VLevel), + (VExpr.bvarRevRange off m).map (VExpr.instL ls) = VExpr.bvarRevRange off m + | 0, _, _ => rfl + | m+1, off, ls => by + show _ :: _ = _ :: _ + rw [bvarRevRange_instL m off ls] + rfl + +/-- The transported type of an analyzed recursive field is exactly its +generated functional-IH domain telescope followed by the transported family +target. -/ +theorem recArg_minor_fieldType {U : Nat} {T : Name} {np ni : Nat} + {B : VExpr} {r₀ : RecArg} + (hr : recArg? U T np ni r₀.fieldIndex B = some r₀) + (m p : Nat) (hj : r₀.fieldIndex < m) + (mode : ElimMode := .large) : + let ls := mode.sourceLevels U + let r := r₀.instL ls + ((B.instL ls).liftN 1 r.fieldIndex).liftN + (m-r.fieldIndex+p) = + VExpr.forallN (r.minorBinders m p) + (VExpr.appN (.const T ls) + (VExpr.bvarRevRange (m+p+r.binders.length+1) np ++ + r.indices.map fun e => + (e.liftN 1 (r.fieldIndex+r.binders.length)).liftN + (m-r.fieldIndex+p) r.binders.length)) := by + dsimp only + obtain ⟨-, -, hB, -, -, -⟩ := recArg?_eq hr + conv => lhs; rw [hB] + simp only [RecArg.instL, VExpr.instL_forallN, VExpr.instL_appN, + List.map_append, bvarRevRange_instL, + show (VExpr.const T (VLevel.params U)).instL (mode.sourceLevels U) = + .const T (mode.sourceLevels U) from by + simp [VExpr.instL, ElimMode.sourceLevels, + VLevel.params_map_inst_params'], + VExpr.liftN_forallN, VExpr.liftN_appN] + simp only [List.length_map, VExpr.liftTelN_length, Nat.zero_add] + rw [bvarRevRange_liftN_ge _ _ _ _ (Nat.le_refl _), + bvarRevRange_liftN_ge _ _ _ _ (by omega), + show + m - r₀.fieldIndex + p + (1 + (r₀.fieldIndex + r₀.binders.length)) = + m + p + r₀.binders.length + 1 from by omega] + simp only [List.map_map, RecArg.minorBinders] + apply congrArg (VExpr.forallN _) + apply congrArg (VExpr.appN (.const T (mode.sourceLevels U))) + apply congrArg (VExpr.bvarRevRange (m + p + r₀.binders.length + 1) np ++ ·) + apply List.map_congr_left + intro e _ + simp only [Function.comp_apply] + +/-- Rule-context analogue of `recArg_minor_fieldType`: transport a recursive +field beneath the motive and all constructor minors, then beneath the later +constructor fields. -/ +theorem recArg_rule_fieldType {U : Nat} {T : Name} {np ni : Nat} + {B : VExpr} {r₀ : RecArg} + (hr : recArg? U T np ni r₀.fieldIndex B = some r₀) + (m k : Nat) (hj : r₀.fieldIndex < m) + (mode : ElimMode := .large) : + let ls := mode.sourceLevels U + let r := r₀.instL ls + ((B.instL ls).liftN (k+1) r.fieldIndex).liftN + (m-r.fieldIndex) = + VExpr.forallN (r.ruleBinders m k) + (VExpr.appN (.const T ls) + (VExpr.bvarRevRange (m+k+r.binders.length+1) np ++ + r.indices.map fun e => + (e.liftN (k+1) (r.fieldIndex+r.binders.length)).liftN + (m-r.fieldIndex) r.binders.length)) := by + dsimp only + obtain ⟨-, -, hB, -, -, -⟩ := recArg?_eq hr + conv => lhs; rw [hB] + simp only [RecArg.instL, VExpr.instL_forallN, VExpr.instL_appN, + List.map_append, bvarRevRange_instL, + show (VExpr.const T (VLevel.params U)).instL (mode.sourceLevels U) = + .const T (mode.sourceLevels U) from by + simp [VExpr.instL, ElimMode.sourceLevels, + VLevel.params_map_inst_params'], + VExpr.liftN_forallN, VExpr.liftN_appN] + simp only [List.length_map, VExpr.liftTelN_length, Nat.zero_add] + rw [bvarRevRange_liftN_ge _ _ _ _ (Nat.le_refl _), + bvarRevRange_liftN_ge _ _ _ _ (by omega), + show m - r₀.fieldIndex + (k + 1 + (r₀.fieldIndex + r₀.binders.length)) = + m + k + r₀.binders.length + 1 from by omega] + simp only [List.map_map, RecArg.ruleBinders] + apply congrArg (VExpr.forallN _) + apply congrArg (VExpr.appN (.const T (mode.sourceLevels U))) + apply congrArg (VExpr.bvarRevRange (m+k+r₀.binders.length+1) np ++ ·) + apply List.map_congr_left + intro e _ + simp only [Function.comp_apply] + +theorem RecArg.minorBinders_shift (r : RecArg) (m p : Nat) : + r.minorBinders m p = + VExpr.liftTelN p (r.minorBinders m 0) 0 := by + simp only [RecArg.minorBinders, Nat.add_zero] + rw [VExpr.liftTelN_liftTelN] + +/-- The `p` parameter of a minor IH is exactly weakening over the `p` +previous IH binders. -/ +theorem RecArg.minorIH_shift (r : RecArg) (m p : Nat) + (hj : r.fieldIndex < m) : + r.minorIH m p = (r.minorIH m 0).liftN p := by + simp only [RecArg.minorIH, VExpr.liftN_forallN] + rw [← r.minorBinders_shift m p] + rw [show (r.minorBinders m 0).length = r.binders.length by + simp [RecArg.minorBinders, VExpr.liftTelN_length], Nat.zero_add] + apply congrArg (VExpr.forallN _) + rw [VExpr.liftN_appN, List.map_append, List.map_map] + show VExpr.appN _ (_ ++ [_]) = VExpr.appN _ (_ ++ [_]) + congr 1 + · rw [show (VExpr.bvar (m + 0 + r.binders.length)).liftN + p r.binders.length = .bvar (m+p+r.binders.length) from by + simp only [VExpr.liftN] + rw [liftVar_le (by omega)] + congr 1 + omega] + · congr 1 + · apply List.map_congr_left + intro e _ + simp only [Function.comp_apply, Nat.add_zero] + rw [VExpr.liftN'_liftN_hi] + · congr 1 + simp only [Nat.add_zero, VExpr.liftN_appN] + rw [show (VExpr.bvar (m - 1 - r.fieldIndex + r.binders.length)).liftN + p r.binders.length = + .bvar (m - 1 - r.fieldIndex + p + r.binders.length) from by + simp only [VExpr.liftN] + rw [liftVar_le (by omega)] + congr 1 + omega, + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega)] + +/-- Lifting a base minor-IH through the constructor-minor stack produces the +normal form expected of the corresponding recursive call in an iota rule. -/ +theorem RecArg.minorIH_zero_lift_ruleIH (r : RecArg) (m k : Nat) + (hj : r.fieldIndex < m) : + (r.minorIH m 0).liftN k m = r.ruleIH m k := by + simp only [RecArg.minorIH, RecArg.ruleIH, VExpr.liftN_forallN, + VExpr.liftTelN_length, RecArg.minorBinders, RecArg.ruleBinders, Nat.add_zero] + congr 1 + · rw [show m = r.fieldIndex + (m-r.fieldIndex) from by omega] + rw [show r.fieldIndex + (m-r.fieldIndex) - r.fieldIndex = + m-r.fieldIndex from by omega] + rw [VExpr.liftTelN_liftN_mid r.binders k (m-r.fieldIndex) (Nat.zero_le _)] + · congr 1 + rw [VExpr.liftN_appN, List.map_append, List.map_map] + show VExpr.appN _ (_ ++ [_]) = VExpr.appN _ (_ ++ [_]) + congr 1 + · simp only [VExpr.liftN] + rw [liftVar_le (Nat.le_refl _)] + congr 1 + omega + · congr 1 + · apply List.map_congr_left + intro e _ + simp only [Function.comp_apply] + rw [show m + r.binders.length = + (r.fieldIndex + r.binders.length) + (m-r.fieldIndex) from by omega, + VExpr.liftN_liftN_mid e k (m-r.fieldIndex) (by omega)] + · congr 1 + simp only [VExpr.liftN_appN] + rw [show (VExpr.bvar (m - 1 - r.fieldIndex + r.binders.length)).liftN + k (m + r.binders.length) = + .bvar (m - 1 - r.fieldIndex + r.binders.length) from by + simp only [VExpr.liftN] + rw [liftVar_lt (by omega)], + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega)] + +/-- General normalization for a minor-IH already shifted past `p` earlier IH +binders. In rule context it is the base `ruleIH`, weakened by the same `p`. -/ +theorem RecArg.minorIH_lift_ruleIH (r : RecArg) (m k p : Nat) + (hj : r.fieldIndex < m) : + (r.minorIH m p).liftN k (m+p) = (r.ruleIH m k).liftN p := by + rw [r.minorIH_shift m p hj] + rw [← VExpr.liftN_liftN_comm (r.minorIH m 0) p k 0 m (Nat.zero_le _)] + rw [r.minorIH_zero_lift_ruleIH m k hj] + +theorem ruleIHs_length (m k : Nat) : ∀ (rs : List RecArg) (p : Nat), + (ruleIHs m k rs p).length = rs.length + | [], _ => rfl + | _ :: rs, p => by simp [ruleIHs, ruleIHs_length m k rs (p+1)] + +/-- Lifting the generalized minor-IH telescope into a rule context normalizes +each entry to `ruleIHs`; this is the list form of +`RecArg.minorIH_lift_ruleIH`. -/ +theorem ihsFromRecArgs_liftN (m k : Nat) : ∀ (rs : List RecArg), + (∀ r ∈ rs, r.fieldIndex < m) → ∀ (p : Nat) (X : VExpr), + (VExpr.forallN (ihsFromRecArgs m rs p) X).liftN k (m+p) = + VExpr.forallN (ruleIHs m k rs p) (X.liftN k (m+p+rs.length)) + | [], _, _, _ => rfl + | r :: rs, hm, p, X => by + show VExpr.forallE _ _ = VExpr.forallE _ _ + congr 1 + · exact r.minorIH_lift_ruleIH m k p (hm r (.head _)) + · rw [show m+p+1 = m+(p+1) from by omega, + ihsFromRecArgs_liftN m k rs (fun q hq => hm q (.tail _ hq)) (p+1) X, + show m+(p+1)+rs.length = m+p+((r :: rs).length) from by simp; omega] + +theorem ihsFromRecArgs_liftN' (m k : Nat) (rs : List RecArg) + (hm : ∀ r ∈ rs, r.fieldIndex < m) (p : Nat) (X : VExpr) + {cut : Nat} (hcut : cut = m+p) : + (VExpr.forallN (ihsFromRecArgs m rs p) X).liftN k cut = + VExpr.forallN (ruleIHs m k rs p) (X.liftN k (m+p+rs.length)) := by + rw [hcut] + exact ihsFromRecArgs_liftN m k rs hm p X + +/-- Weakening a normalized functional-IH telescope by one binder is the same +as incrementing its starting depth. -/ +theorem ruleIHs_liftN1 (m k : Nat) : ∀ (rs : List RecArg) (p c : Nat), c ≤ p → + ∀ (X : VExpr), + VExpr.forallN (ruleIHs m k rs (p+1)) (X.liftN 1 (c+rs.length)) = + (VExpr.forallN (ruleIHs m k rs p) X).liftN 1 c + | [], p, c, _, X => by simp [ruleIHs, VExpr.forallN] + | r :: rs, p, c, hc, X => by + show VExpr.forallE _ _ = VExpr.forallE _ _ + congr 1 + · rw [VExpr.liftN'_liftN' (Nat.zero_le _) hc] + · rw [show c+(r :: rs).length = (c+1)+rs.length from by simp; omega] + exact ruleIHs_liftN1 m k rs (p+1) (c+1) (by omega) X + +/-- The key normalization: lifting a parameter spine past `off` binders. -/ +theorem recApp'_liftN {U : Nat} {T : Name} {np : Nat} {n k off : Nat} (h : k ≤ off) : + (recApp' U T np off).liftN n k = recApp' U T np (n + off) := by + simp only [recApp', VExpr.liftN_appN] + rw [bvarRevRange_liftN_ge _ _ _ _ h] + rfl + +theorem recApp_liftN {U : Nat} {T : Name} {np : Nat} {n k off : Nat} (h : k ≤ off) : + (recApp U T np off).liftN n k = recApp U T np (n + off) := by + simp only [recApp, VExpr.liftN_appN] + rw [bvarRevRange_liftN_ge _ _ _ _ h] + rfl + +theorem recApp_instL {U : Nat} {T : Name} {np off : Nat} : + (recApp U T np off).instL (VLevel.params' U 1) = recApp' U T np off := by + simp only [recApp, recApp', VExpr.instL_appN] + rw [bvarRevRange_instL] + simp [VExpr.instL, VLevel.params_map_inst_params', ElimMode.sourceLevels, + ElimMode.offset] + +theorem recApp'_congr {U : Nat} {T : Name} {np : Nat} {off off' : Nat} + (h : off = off') : recApp' U T np off = recApp' U T np off' := h ▸ rfl + +theorem _root_.Ix.Theory.Named.VExpr.bvarRevRange_congr {off off' : Nat} (m : Nat) + (h : off = off') : VExpr.bvarRevRange off m = VExpr.bvarRevRange off' m := h ▸ rfl + +theorem _root_.Ix.Theory.Named.VExpr.bvarRevRange_congr' {m m' : Nat} (off : Nat) + (h : m = m') : VExpr.bvarRevRange off m = VExpr.bvarRevRange off m' := h ▸ rfl + +theorem bvarRevRange_closedN : ∀ (m off k : Nat), off + m ≤ k → + ∀ e ∈ VExpr.bvarRevRange off m, e.ClosedN k + | 0, _, _, _, _, h => nomatch h + | m+1, off, k, hk, e, h => by + rcases List.mem_cons.1 h with rfl | h + · exact show off + m < k by omega + · exact bvarRevRange_closedN m off k (by omega) e h + +theorem bvarRevRange_levelWF {Uv : Nat} : ∀ (m off : Nat), + ∀ e ∈ VExpr.bvarRevRange off m, e.LevelWF Uv + | 0, _, _, h => nomatch h + | m+1, off, e, h => by + rcases List.mem_cons.1 h with rfl | h + · trivial + · exact bvarRevRange_levelWF m off e h + +theorem ihsFrom_levelWF {Uv : Nat} {m : Nat} : ∀ (rs : List (Nat × List VExpr)) (p : Nat), + (∀ q ∈ rs, ∀ e ∈ q.2, e.LevelWF Uv) → ∀ e ∈ ihsFrom m rs p, e.LevelWF Uv + | (j, idxs) :: rs, p, hidx, e, h => by + rcases List.mem_cons.1 h with rfl | h + · refine VExpr.LevelWF.appN (f := .bvar (m+p)) trivial fun e he => ?_ + rcases List.mem_append.1 he with he | he + · obtain ⟨e₀, he₀, rfl⟩ := List.mem_map.1 he + exact ((hidx _ (List.Mem.head _) e₀ he₀).liftN).liftN + · rcases List.mem_cons.1 he with rfl | he + · trivial + · cases he + · exact ihsFrom_levelWF rs (p+1) (fun q hq => hidx q (.tail _ hq)) e h + +/-- Closedness of a telescope of closed binders over an open body. -/ +theorem _root_.Ix.Theory.Named.VExpr.ClosedN.forallN_closed_binders : + ∀ {As : List VExpr}, (∀ A ∈ As, A.ClosedN 0) → + ∀ {X : VExpr} {k : Nat}, X.ClosedN (k + As.length) → + (VExpr.forallN As X).ClosedN k + | [], _, _, _, hX => hX + | _ :: As, hAs, X, k, hX => + ⟨(hAs _ (.head _)).mono (Nat.zero_le _), + VExpr.ClosedN.forallN_closed_binders (fun A h => hAs _ (.tail _ h)) + (by rw [show k+1+As.length = k+(As.length+1) from by omega]; exact hX)⟩ + +end VInductDecl + +namespace VExpr + +theorem forallN_telN_dropN : ∀ (n : Nat) (e : VExpr), + forallN (telN n e) (dropN n e) = e + | 0, _ => rfl + | n+1, .forallE A rest => congrArg (VExpr.forallE A) (forallN_telN_dropN n rest) + | _+1, .bvar _ | _+1, .sort _ | _+1, .const _ _ | _+1, .app _ _ | _+1, .lam _ _ => rfl + +theorem resultOf_forallN : ∀ (As : List VExpr) (B : VExpr), + resultOf (forallN As B) = resultOf B + | [], _ => rfl + | _ :: As, B => resultOf_forallN As B + +theorem resultOf_appN_app (f a : VExpr) : + ∀ es, resultOf ((f.app a).appN es) = (f.app a).appN es + | [] => rfl + | b :: bs => resultOf_appN_app (f.app a) b bs + +theorem resultOf_appN_const (T : Name) (ls : List VLevel) : + ∀ es, resultOf ((VExpr.const T ls).appN es) = + (VExpr.const T ls).appN es + | [] => rfl + | a :: as => resultOf_appN_app (VExpr.const T ls) a as + +end VExpr + +namespace VEnv + +theorem OnTel.mono {env env' : VEnv} {U : Nat} (henv : env ≤ env') : + ∀ {As Γ}, OnTel env U Γ As → OnTel env' U Γ As + | [], _, _ => trivial + | _ :: _, _, ⟨hA, hT⟩ => ⟨hA.mono henv, OnTel.mono henv hT⟩ + +/-- A well-formed telescope is pointwise definitionally equal to itself. -/ +theorem OnTel.telDefEq_refl {env : VEnv} {U : Nat} : + ∀ {Γ As}, OnTel env U Γ As → TelDefEq env U Γ As As + | _, [], _ => trivial + | _, _ :: _, ⟨⟨u, hA⟩, hT⟩ => + ⟨⟨u, hA⟩, OnTel.telDefEq_refl hT⟩ + +/-- A structural telescope equality has equal arity. -/ +theorem TelDefEq.length_eq {env : VEnv} {U : Nat} : + ∀ {Γ As As'}, TelDefEq env U Γ As As' → As.length = As'.length + | _, [], [], _ => rfl + | _, _ :: _, _ :: _, ⟨_, hT⟩ => + congrArg Nat.succ (TelDefEq.length_eq hT) + +/-- The raw side of a structural telescope equality is a well-formed +telescope. -/ +theorem TelDefEq.raw_onTel {env : VEnv} {U : Nat} : + ∀ {Γ As As'}, TelDefEq env U Γ As As' → OnTel env U Γ As + | _, [], [], _ => trivial + | _, _ :: _, _ :: _, ⟨⟨_, hA⟩, hT⟩ => + ⟨⟨_, hA.hasType.1⟩, TelDefEq.raw_onTel hT⟩ + +/-- Extend a structural telescope equality by an identical, well-formed +suffix. The suffix is checked in the completed left-hand context, exactly as +required by `TelDefEq`'s raw-context convention. -/ +theorem TelDefEq.append_refl {env : VEnv} {U : Nat} : + ∀ {Γ As As'}, TelDefEq env U Γ As As' → + ∀ {Bs}, OnTel env U (As.reverse ++ Γ) Bs → + TelDefEq env U Γ (As ++ Bs) (As' ++ Bs) + | _, [], [], _, _, hBs => by + simpa using hBs.telDefEq_refl + | Γ, A :: As, A' :: As', ⟨hA, hT⟩, Bs, hBs => by + refine ⟨hA, ?_⟩ + apply TelDefEq.append_refl hT + simpa [List.reverse_cons, List.append_assoc] using hBs + +/-- Structural telescope equality is monotone in the environment. -/ +theorem TelDefEq.mono {env env' : VEnv} {U : Nat} (henv : env ≤ env') : + ∀ {Γ As As'}, TelDefEq env U Γ As As' → TelDefEq env' U Γ As As' + | _, [], [], _ => trivial + | _, _ :: _, _ :: _, ⟨⟨u, hA⟩, hT⟩ => + ⟨⟨u, hA.mono henv⟩, TelDefEq.mono henv hT⟩ + +/-- Instantiate every universe in a structural telescope equality. -/ +theorem TelDefEq.instL {env : VEnv} {U U' : Nat} {ls : List VLevel} + (hls : ∀ l ∈ ls, l.WF U') : + ∀ {Γ As As'}, TelDefEq env U Γ As As' → + TelDefEq env U' (Γ.map (VExpr.instL ls)) + (As.map (VExpr.instL ls)) (As'.map (VExpr.instL ls)) + | _, [], [], _ => trivial + | _, _ :: As, _ :: As', ⟨⟨u, hA⟩, hT⟩ => by + refine ⟨⟨u.inst ls, hA.instL hls⟩, ?_⟩ + simpa using TelDefEq.instL hls hT + +/-- Extend an existing definitionally equal context by a structurally equal +raw/view telescope. -/ +theorem TelDefEq.extendCtx {env : VEnv} {U : Nat} {Γ₀ : List VExpr} : + ∀ {Γ Γ' As As'}, IsDefEqCtx env U Γ₀ Γ Γ' → + TelDefEq env U Γ As As' → + IsDefEqCtx env U Γ₀ (As.reverse ++ Γ) (As'.reverse ++ Γ') + | Γ, Γ', [], [], hΓ, _ => by simpa using hΓ + | Γ, Γ', _ :: As, _ :: As', hΓ, ⟨⟨_, hA⟩, hT⟩ => by + simpa [List.reverse_cons, List.append_assoc] using + TelDefEq.extendCtx (.succ hΓ hA) hT + +/-- The completed raw and view contexts of a structural telescope equality +are definitionally equal over their common prefix. -/ +theorem TelDefEq.ctx {env : VEnv} {U : Nat} {Γ As As' : List VExpr} + (h : TelDefEq env U Γ As As') : + IsDefEqCtx env U Γ (As.reverse ++ Γ) (As'.reverse ++ Γ) := + h.extendCtx .zero + +/-- Any aligned prefix remains a structural telescope equality. -/ +theorem TelDefEq.take {env : VEnv} {U : Nat} : + ∀ (n : Nat) {Γ As As'}, TelDefEq env U Γ As As' → + TelDefEq env U Γ (As.take n) (As'.take n) + | 0, _, _, _, _ => trivial + | _ + 1, _, [], [], _ => trivial + | n + 1, _, _ :: _, _ :: _, ⟨hA, hT⟩ => + ⟨hA, TelDefEq.take n hT⟩ + +/-- Drop an aligned prefix, retaining the exact raw context accumulated by +the removed binders. -/ +theorem TelDefEq.drop {env : VEnv} {U : Nat} : + ∀ (n : Nat) {Γ As As'}, TelDefEq env U Γ As As' → + TelDefEq env U ((As.take n).reverse ++ Γ) + (As.drop n) (As'.drop n) + | 0, _, _, _, h => by simpa using h + | _ + 1, _, [], [], _ => trivial + | n + 1, Γ, A :: As, _ :: As', ⟨_, hT⟩ => by + have h := TelDefEq.drop n hT + simpa [List.take_succ_cons, List.drop_succ_cons, + List.reverse_cons, List.append_assoc] using h + +/-- Recover the pointwise equality at one aligned binder position together +with its exact preceding raw context. -/ +theorem TelDefEq.getElem? {env : VEnv} {U : Nat} : + ∀ {Γ As As'}, TelDefEq env U Γ As As' → + ∀ {n A A'}, As[n]? = some A → As'[n]? = some A' → + ∃ u, env.IsDefEq U ((As.take n).reverse ++ Γ) A A' (.sort u) + | _, [], [], _, n, _, _, hA, _ => by simp at hA + | Γ, _ :: As, _ :: As', ⟨hhead, htail⟩, 0, A, A', hA, hA' => by + simp only [List.getElem?_cons_zero, Option.some.injEq] at hA hA' + subst A + subst A' + simpa using hhead + | Γ, A₀ :: As, A₀' :: As', ⟨_, htail⟩, n + 1, A, A', hA, hA' => by + simp only [List.getElem?_cons_succ] at hA hA' + have h := TelDefEq.getElem? htail hA hA' + simpa [List.take_succ_cons, List.reverse_cons, List.append_assoc] using h + +/-- Weakening preserves structural telescope equality and shifts both +surfaces at their binder-relative cutoffs. -/ +theorem TelDefEq.weakN {env : VEnv} {U n : Nat} (ord : env.Ordered) + {Γ Γ' : List VExpr} {k : Nat} (W : Ctx.LiftN n k Γ Γ') : + ∀ {As As'}, TelDefEq env U Γ As As' → + TelDefEq env U Γ' + (VExpr.liftTelN n As k) (VExpr.liftTelN n As' k) + | [], [], _ => trivial + | _ :: As, _ :: As', ⟨⟨u, hA⟩, hT⟩ => by + refine ⟨⟨u, hA.weakN ord W⟩, ?_⟩ + simpa [VExpr.liftTelN] using TelDefEq.weakN ord W.succ hT + +/-- Substitute one typed term through a structural telescope equality. -/ +theorem TelDefEq.instN {env : VEnv} {U : Nat} (ord : env.Ordered) + {Γ₀ : List VExpr} {e₀ A₀ : VExpr} + (h₀ : env.HasType U Γ₀ e₀ A₀) {k : Nat} {Γ₁ Γ : List VExpr} + (W : Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ) : + ∀ {As As'}, TelDefEq env U Γ₁ As As' → + TelDefEq env U Γ + (VExpr.instTelN e₀ As k) (VExpr.instTelN e₀ As' k) + | [], [], _ => trivial + | _ :: As, _ :: As', ⟨⟨u, hA⟩, hT⟩ => by + refine ⟨⟨u, by simpa using hA.instN ord h₀ W⟩, ?_⟩ + simpa [VExpr.instTelN] using + TelDefEq.instN ord h₀ W.succ hT + +/-- Pointwise telescope equality extends to the corresponding iterated Pi +types once their terminal bodies are definitionally equal in the completed +raw context. This is the construction-side counterpart of `spine_sort`; it +does not require Pi injectivity. -/ +theorem TelDefEq.forallN_defeq {env : VEnv} {U : Nat} : + ∀ {Γ As As' C C' u}, + TelDefEq env U Γ As As' → + env.IsDefEq U (As.reverse ++ Γ) C C' (.sort u) → + ∃ v, env.IsDefEq U Γ + (VExpr.forallN As C) (VExpr.forallN As' C') (.sort v) + | _, [], [], _, _, u, _, hC => ⟨u, hC⟩ + | Γ, A :: As, A' :: As', C, C', u, + ⟨⟨uA, hA⟩, hT⟩, hC => by + have hC' : env.IsDefEq U (As.reverse ++ A :: Γ) + C C' (.sort u) := by + simpa [List.reverse_cons, List.append_assoc] using hC + obtain ⟨v, hbody⟩ := + TelDefEq.forallN_defeq hT hC' + exact ⟨.imax uA v, .forallEDF hA hbody⟩ + +/-- A fully applied spine accepted by the view telescope is also accepted by +the raw telescope. This is the substitution-aware consumer of `TelDefEq`; +it avoids any appeal to whole-Pi injectivity. -/ +theorem TelDefEq.spine_sort {env : VEnv} {U : Nat} (ord : env.Ordered) : + ∀ {Γ As As' es l}, TelDefEq env U Γ As As' → + env.SpineWF U Γ (VExpr.forallN As' (.sort l)) es (.sort l) → + es.length = As.length → + env.SpineWF U Γ (VExpr.forallN As (.sort l)) es (.sort l) + | _, [], [], [], _, _, hsp, _ => by simpa using hsp + | _, [], [], _ :: _, _, _, _, hlen => by simp at hlen + | Γ, A :: As, A' :: As', e :: es, l, ⟨⟨_, hA⟩, hT⟩, + .cons he hrest, hlen => by + have heRaw : env.HasType U Γ e A := hA.defeq' he + have hTinst := TelDefEq.instN ord heRaw (.zero) hT + have hrest' : env.SpineWF U Γ + (VExpr.forallN (VExpr.instTelN e As' 0) (.sort l)) + es (.sort l) := by + simpa [VExpr.instN_forallN] using hrest + have hlen' : es.length = As.length := by simpa using hlen + have hlenInst : + es.length = (VExpr.instTelN e As 0).length := by + rw [VExpr.instTelN_length] + exact hlen' + have hout := TelDefEq.spine_sort ord hTinst hrest' hlenInst + refine .cons heRaw ?_ + simpa [VExpr.instN_forallN] using hout + +/-- Extend a definitionally equal context by the same well-formed telescope +on both sides. -/ +theorem OnTel.extendDefEqCtx {env : VEnv} {U : Nat} {Γ₀ Γ₁ Γ₂ : List VExpr} + (hΓ : IsDefEqCtx env U Γ₀ Γ₁ Γ₂) : + ∀ {As}, OnTel env U Γ₁ As → + IsDefEqCtx env U Γ₀ (As.reverse ++ Γ₁) (As.reverse ++ Γ₂) + | [], _ => by simpa using hΓ + | _ :: As, ⟨⟨_, hA⟩, hT⟩ => by + simpa [List.reverse_cons, List.append_assoc] using + OnTel.extendDefEqCtx (.succ hΓ hA) hT + +/-- Transport a telescope across definitionally equal base contexts. -/ +theorem OnTel.defeqDFC {env : VEnv} {U : Nat} (ord : env.Ordered) + {Γ₀ Γ₁ Γ₂ : List VExpr} (hΓ : IsDefEqCtx env U Γ₀ Γ₁ Γ₂) : + ∀ {As}, OnTel env U Γ₁ As → OnTel env U Γ₂ As + | [], _ => trivial + | _ :: As, ⟨hA, hT⟩ => by + obtain ⟨u, hAt⟩ := hA + exact ⟨⟨u, hAt.defeqDFC ord hΓ⟩, + OnTel.defeqDFC ord (.succ hΓ hAt) hT⟩ + +/-- The view side of a structural telescope equality is itself well formed. +Each tail is transported from the accumulated raw context to the accumulated +view context before recursion continues. -/ +theorem TelDefEq.view_onTel {env : VEnv} {U : Nat} (ord : env.Ordered) : + ∀ {Γ As As'}, TelDefEq env U Γ As As' → OnTel env U Γ As' + | _, [], [], _ => trivial + | _, _ :: _, _ :: _, ⟨⟨u, hA⟩, hT⟩ => + ⟨⟨u, hA.hasType.2⟩, + (TelDefEq.view_onTel ord hT).defeqDFC ord + (.succ .zero hA)⟩ + +/-- Transport a structural telescope equality across definitionally equal +base contexts while retaining its raw/view surfaces. -/ +theorem TelDefEq.defeqDFC {env : VEnv} {U : Nat} (ord : env.Ordered) + {Γ₀ Γ₁ Γ₂ : List VExpr} (hΓ : IsDefEqCtx env U Γ₀ Γ₁ Γ₂) : + ∀ {As As'}, TelDefEq env U Γ₁ As As' → TelDefEq env U Γ₂ As As' + | [], [], _ => trivial + | A :: As, _ :: As', ⟨⟨u, hA⟩, hT⟩ => by + have hA' := hA.defeqDFC ord hΓ + refine ⟨⟨u, hA'⟩, ?_⟩ + exact TelDefEq.defeqDFC ord + (.succ hΓ hA.hasType.1) hT + +/-- Select Lean's kernel-observable parameter surface without losing the +structural equality to the analyzer-owned parameter telescope. Ordinary +parameters keep the raw domain; annotated parameters use the checked domain. +-/ +theorem TelDefEq.generationParams {env : VEnv} {U : Nat} + (ord : env.Ordered) : + ∀ {Γ raw view}, TelDefEq env U Γ raw view → + TelDefEq env U Γ (VInductDecl.generationParams raw view) view + | _, [], [], _ => trivial + | Γ, raw :: raws, view :: views, ⟨⟨u, head⟩, tail⟩ => by + simp only [VInductDecl.generationParams, + VInductDecl.generationParam] + split + · refine ⟨⟨u, head.hasType.2⟩, ?_⟩ + exact (TelDefEq.generationParams ord tail).defeqDFC ord + (.succ (.zero (Γ₀ := Γ)) head) + · exact ⟨⟨u, head⟩, TelDefEq.generationParams ord tail⟩ + +#guard_named_axioms Ix.Theory.Named.VEnv.TelDefEq.generationParams [propext, Quot.sound] + +/-- Transport application-spine typing across definitionally equal +contexts. -/ +theorem SpineWF.defeqDFC {env : VEnv} {U : Nat} (ord : env.Ordered) + {Γ₀ Γ₁ Γ₂ : List VExpr} (hΓ : IsDefEqCtx env U Γ₀ Γ₁ Γ₂) : + ∀ {A es B}, SpineWF env U Γ₁ A es B → SpineWF env U Γ₂ A es B + | _, [], _, .nil => .nil + | _, _ :: _, _, .cons he hT => + .cons (he.defeqDFC ord hΓ) (SpineWF.defeqDFC ord hΓ hT) + +#guard_named_axioms Ix.Theory.Named.VEnv.TelDefEq.raw_onTel [propext] + +#guard_named_axioms Ix.Theory.Named.VEnv.TelDefEq.instL [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.TelDefEq.ctx [propext] + +end VEnv + +namespace VInductDecl +open VEnv + +/-- The exact raw family telescope accepted for generation is well formed in +the pre-environment. -/ +theorem GenerationChecked.WF.rawFamily_onTel {source : VInductDecl} + {gen : GenerationChecked source} {env : VEnv} (h : gen.WF env) : + env.OnTel source.uvars [] + (gen.block.rawParams ++ gen.block.rawIndices) := + h.familyTel.raw_onTel + +/-- The granular family telescope/result contract proves that the stored raw +family constant can be inserted without appealing to whole-`forall` +injectivity. -/ +theorem GenerationChecked.WF.rawFamily_isType {source : VInductDecl} + {gen : GenerationChecked source} {env : VEnv} (h : gen.WF env) : + env.IsType source.uvars [] gen.block.sourceType.type := by + rw [← VExpr.forallN_telN_dropN source.nparams gen.block.sourceType.type, + ← forallN_ctorFields_resultOf + (VExpr.dropN source.nparams gen.block.sourceType.type), + ← VExpr.forallN_append] + have hresult₀ : env.IsType source.uvars + (gen.block.rawParams ++ gen.block.rawIndices).reverse + gen.block.rawResult := + ⟨_, h.familyResult.hasType.1⟩ + have hresult : env.IsType source.uvars + ((gen.block.rawParams ++ gen.block.rawIndices).reverse ++ []) + gen.block.rawResult := by + simpa using hresult₀ + have hout := IsType.forallN h.rawFamily_onTel hresult + simpa [NormalizedChecked.rawParams, NormalizedChecked.rawIndices, + NormalizedChecked.rawResult] using hout + +/-- The exact raw telescope stored in a paired constructor is well formed in +the post-family environment. -/ +theorem NormalizedCtor.WF.rawDeclared_onTel {source : VInductDecl} + {block : NormalizedChecked source} {ctor : NormalizedCtor} {env : VEnv} + (h : ctor.WF block env) : + env.OnTel source.uvars [] (ctor.declaredBinders source.nparams) := + h.declaredTel.raw_onTel + +/-- The granular constructor telescope/result contract proves that the exact +stored raw constructor can be inserted. -/ +theorem NormalizedCtor.WF.rawDeclared_isType {source : VInductDecl} + {block : NormalizedChecked source} {ctor : NormalizedCtor} {env : VEnv} + (h : ctor.WF block env) : + env.IsType source.uvars [] ctor.raw.type := by + rw [← VExpr.forallN_telN_dropN source.nparams ctor.raw.type, + ← forallN_ctorFields_resultOf (VExpr.dropN source.nparams ctor.raw.type), + ← VExpr.forallN_append] + have hresult₀ : env.IsType source.uvars + (ctor.declaredBinders source.nparams).reverse + (ctor.rawResult source.nparams) := + ⟨_, h.declaredResult.hasType.1⟩ + have hresult : env.IsType source.uvars + ((ctor.declaredBinders source.nparams).reverse ++ []) + (ctor.rawResult source.nparams) := by + simpa using hresult₀ + have hout := IsType.forallN h.rawDeclared_onTel hresult + simpa [NormalizedCtor.declaredBinders, NormalizedCtor.rawFields, + NormalizedCtor.rawResult] using hout + +/-- The raw family/field telescope actually emitted in mixed artifacts is +well formed independently of the constructor's stored parameter surface. -/ +theorem NormalizedCtor.WF.rawEmitted_onTel {source : VInductDecl} + {block : NormalizedChecked source} {ctor : NormalizedCtor} {env : VEnv} + (h : ctor.WF block env) : + env.OnTel source.uvars [] (ctor.emittedBinders block) := + h.emittedTel.raw_onTel + +/-- A granular constructor certificate is monotone after its staged family +insertion. -/ +theorem NormalizedCtor.WF.mono {source : VInductDecl} + {block : NormalizedChecked source} {ctor : NormalizedCtor} + {env env' : VEnv} (henv : env ≤ env') (h : ctor.WF block env) : + ctor.WF block env' where + declaredTel := h.declaredTel.mono henv + declaredResult := h.declaredResult.mono henv + emittedTel := h.emittedTel.mono henv + emittedResult := h.emittedResult.mono henv + +/-- Retrieve the insertion-ready proof for any paired raw constructor in the +precise post-family environment named by the block certificate. -/ +theorem GenerationChecked.WF.rawCtor_isType {source : VInductDecl} + {gen : GenerationChecked source} {env envT : VEnv} (h : gen.WF env) + (hadd : env.addConst gen.block.sourceType.name + gen.block.sourceType.toVConstant = some envT) + {ctor : NormalizedCtor} (hctor : ctor ∈ gen.block.ctorPairs) : + envT.IsType source.uvars [] ctor.raw.type := + (h.ctors envT hadd ctor hctor).rawDeclared_isType + +/-! Mutual declaration-stage consequences. -/ + +theorem NormalizedFamily.WF.rawFamily_onTel {source : VInductDecl} + {gen : BlockGenerationChecked source} {family : NormalizedFamily} + {env : VEnv} (h : family.WF gen env) : + env.OnTel source.uvars [] + (family.rawParams source.nparams ++ + family.rawIndices source.nparams) := + h.familyTel.raw_onTel + +theorem NormalizedFamily.WF.rawFamily_isType {source : VInductDecl} + {gen : BlockGenerationChecked source} {family : NormalizedFamily} + {env : VEnv} (h : family.WF gen env) : + env.IsType source.uvars [] family.raw.type := by + rw [← VExpr.forallN_telN_dropN source.nparams family.raw.type, + ← forallN_ctorFields_resultOf + (VExpr.dropN source.nparams family.raw.type), + ← VExpr.forallN_append] + have hresult₀ : env.IsType source.uvars + (family.rawParams source.nparams ++ + family.rawIndices source.nparams).reverse + (family.rawResult source.nparams) := + ⟨_, h.familyResult.hasType.1⟩ + have hresult : env.IsType source.uvars + ((family.rawParams source.nparams ++ + family.rawIndices source.nparams).reverse ++ []) + (family.rawResult source.nparams) := by + simpa using hresult₀ + have hout := IsType.forallN h.rawFamily_onTel hresult + simpa [NormalizedFamily.rawParams, NormalizedFamily.rawIndices, + NormalizedFamily.rawResult] using hout + +theorem NormalizedFamily.WF.mono {source : VInductDecl} + {gen : BlockGenerationChecked source} {family : NormalizedFamily} + {env env' : VEnv} (henv : env ≤ env') (h : family.WF gen env) : + family.WF gen env' where + familyTel := h.familyTel.mono henv + familyResult := h.familyResult.mono henv + +theorem NormalizedBlockCtor.WF.rawDeclared_onTel + {source : VInductDecl} {gen : BlockGenerationChecked source} + {constructor : NormalizedBlockCtor} {env : VEnv} + (h : NormalizedBlockCtor.WF gen constructor env) : + env.OnTel source.uvars [] + (NormalizedBlockCtor.declaredBinders + (source := source) constructor) := + h.declaredTel.raw_onTel + +theorem NormalizedBlockCtor.WF.rawDeclared_isType + {source : VInductDecl} {gen : BlockGenerationChecked source} + {constructor : NormalizedBlockCtor} {env : VEnv} + (h : NormalizedBlockCtor.WF gen constructor env) : + env.IsType source.uvars [] constructor.ctor.raw.type := by + rw [← VExpr.forallN_telN_dropN source.nparams + constructor.ctor.raw.type, + ← forallN_ctorFields_resultOf + (VExpr.dropN source.nparams constructor.ctor.raw.type), + ← VExpr.forallN_append] + have hresult₀ : env.IsType source.uvars + (NormalizedBlockCtor.declaredBinders + (source := source) constructor).reverse + (NormalizedBlockCtor.rawResult + (source := source) constructor) := + ⟨_, h.declaredResult.hasType.1⟩ + have hresult : env.IsType source.uvars + ((NormalizedBlockCtor.declaredBinders + (source := source) constructor).reverse ++ []) + (NormalizedBlockCtor.rawResult + (source := source) constructor) := by + simpa using hresult₀ + have hout := IsType.forallN h.rawDeclared_onTel hresult + simpa [NormalizedBlockCtor.declaredBinders, + NormalizedBlockCtor.rawResult, NormalizedCtor.declaredBinders, + NormalizedCtor.rawFields, NormalizedCtor.rawResult] using hout + +theorem NormalizedBlockCtor.WF.rawEmitted_onTel + {source : VInductDecl} {gen : BlockGenerationChecked source} + {constructor : NormalizedBlockCtor} {env : VEnv} + (h : NormalizedBlockCtor.WF gen constructor env) : + env.OnTel source.uvars [] + (NormalizedBlockCtor.emittedBinders gen constructor) := + h.emittedTel.raw_onTel + +theorem NormalizedBlockCtor.WF.mono + {source : VInductDecl} {gen : BlockGenerationChecked source} + {constructor : NormalizedBlockCtor} {env env' : VEnv} + (henv : env ≤ env') (h : NormalizedBlockCtor.WF gen constructor env) : + NormalizedBlockCtor.WF gen constructor env' where + declaredTel := h.declaredTel.mono henv + declaredResult := h.declaredResult.mono henv + emittedTel := h.emittedTel.mono henv + emittedResult := h.emittedResult.mono henv + owner := h.owner + recursive := fun recursive hrecursive => by + obtain ⟨family, hfamily, hordinal, hfield, hwf⟩ := + h.recursive recursive hrecursive + exact ⟨family, hfamily, hordinal, hfield, + ⟨hwf.1.mono henv, hwf.2.mono henv⟩⟩ + resultSpine := h.resultSpine.mono henv + +theorem BlockGenerationChecked.WF.rawFamily_isType + {source : VInductDecl} {gen : BlockGenerationChecked source} + {env blockEnv : VEnv} (h : gen.WF env blockEnv) + {family : NormalizedFamily} (hfamily : family ∈ gen.families) : + env.IsType source.uvars [] family.raw.type := + (h.families family hfamily).rawFamily_isType + +theorem BlockGenerationChecked.WF.rawCtor_isType + {source : VInductDecl} {gen : BlockGenerationChecked source} + {env blockEnv : VEnv} (h : gen.WF env blockEnv) + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) : + blockEnv.IsType source.uvars [] constructor.ctor.raw.type := + (h.constructors constructor hconstructor).rawDeclared_isType + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationChecked.WF.rawFamily_isType [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationChecked.WF.rawCtor_isType [propext, Quot.sound] + +theorem RecArg.WF.mono {env env' : VEnv} {U : Nat} {l : VLevel} + {Is Γ : List VExpr} {r : RecArg} (henv : env ≤ env') + (h : r.WF U env l Is Γ) : r.WF U env' l Is Γ := + ⟨h.1.mono henv, h.2.mono henv⟩ + +/-- The checked recursive/non-recursive field interpretation is monotone in +the environment. -/ +theorem checkedBlockFieldsWF_mono {env env' : VEnv} {U : Nat} + {resultLevel : VLevel} {familyIndices : List (List VExpr)} + (henv : env ≤ env') : ∀ {fields classifications Γ j}, + checkedBlockFieldsWF env U resultLevel familyIndices + fields classifications Γ j → + checkedBlockFieldsWF env' U resultLevel familyIndices + fields classifications Γ j + | [], [], _, _, _ => trivial + | [], _ :: _, _, _, h => by + simp [checkedBlockFieldsWF] at h + | _ :: _, [], _, _, h => by + simp [checkedBlockFieldsWF] at h + | B :: Bs, none :: classifications, Γ, j, + ⟨⟨u, hB, hlevel⟩, htail⟩ => + ⟨⟨u, hB.mono henv, hlevel⟩, + checkedBlockFieldsWF_mono henv htail⟩ + | B :: Bs, some recursive :: classifications, Γ, j, h => by + simp only [checkedBlockFieldsWF] at h ⊢ + obtain ⟨⟨hfield, hrecursive⟩, htail⟩ := h + refine ⟨⟨hfield, ?_⟩, + checkedBlockFieldsWF_mono henv htail⟩ + cases htarget : familyIndices[recursive.targetType]? with + | none => simp [htarget] at hrecursive + | some indices => + rw [htarget] at hrecursive + exact hrecursive.mono henv + +/-- The erased family-spine semantics is monotone in the environment. -/ +theorem checkedFamilyListsWF_mono {source : VInductDecl} + {params : List VExpr} {env env' : VEnv} {resultLevel : VLevel} + {familyIndices : List (List VExpr)} (henv : env ≤ env') : + ∀ levels indices constructors, + checkedFamilyListsWF source params env resultLevel familyIndices + levels indices constructors → + checkedFamilyListsWF source params env' resultLevel familyIndices + levels indices constructors := by + intro levels + induction levels with + | nil => + intro indices constructors h + cases indices <;> cases constructors <;> + simp_all [checkedFamilyListsWF] + | cons level levels ih => + intro indices constructors h + cases indices with + | nil => simp [checkedFamilyListsWF] at h + | cons index indices => + cases constructors with + | nil => simp [checkedFamilyListsWF] at h + | cons familyConstructors constructors => + simp only [checkedFamilyListsWF] at h ⊢ + obtain ⟨hlevel, hindices, hconstructors, htail⟩ := h + exact ⟨hlevel, hindices.mono henv, + fun constructor hconstructor => + ⟨checkedBlockFieldsWF_mono henv + (hconstructors constructor hconstructor).1, + (hconstructors constructor hconstructor).2.mono henv⟩, + ih indices constructors htail⟩ + +theorem CheckedBlock.WF.mono {source : VInductDecl} + {checked : source.CheckedBlock} {env env' : VEnv} + {resultLevel : VLevel} (henv : env ≤ env') + (h : checked.WF env resultLevel) : + checked.WF env' resultLevel := + checkedFamilyListsWF_mono henv _ _ _ h + +/-- Transport recursive-argument evidence across definitionally equal base +contexts. The private binder telescope is transported first, then the +terminal family-index spine is transported beneath that same telescope. -/ +theorem RecArg.WF.defeqDFC {env : VEnv} {U : Nat} {l : VLevel} + {Is Γ₀ Γ₁ Γ₂ : List VExpr} {r : RecArg} (ord : env.Ordered) + (hΓ : env.IsDefEqCtx U Γ₀ Γ₁ Γ₂) + (h : r.WF U env l Is Γ₁) : r.WF U env l Is Γ₂ := by + have htel := h.1.defeqDFC ord hΓ + have hctx := h.1.extendDefEqCtx hΓ + exact ⟨htel, h.2.defeqDFC ord hctx⟩ + +theorem fieldsWF_mono {U : Nat} {T : Name} {np : Nat} {env env' : VEnv} {l : VLevel} + {Is : List VExpr} (henv : env ≤ env') : ∀ {Γ j Bs}, + fieldsWF U T np env l Is Γ j Bs → fieldsWF U T np env' l Is Γ j Bs + | _, _, [], _ => trivial + | _, _, _ :: _, ⟨hB, hSp, hT⟩ => by + refine ⟨?_, fun hrec => (hSp hrec).mono henv, fieldsWF_mono henv hT⟩ + rcases hB with hrec | hfun | ⟨hnone, u, h, hl⟩ + · exact .inl hrec + · obtain ⟨r, hr, hne, hwf⟩ := hfun + exact .inr (.inl ⟨r, hr, hne, hwf.mono henv⟩) + · exact .inr (.inr ⟨hnone, u, h.mono henv, hl⟩) + +/-- The checked-view semantic certificate is monotone in the environment. -/ +theorem Checked.WF.mono {decl : VInductDecl} {checked : decl.Checked} + {env env' : VEnv} (henv : env ≤ env') (h : checked.WF env) : + checked.WF env' := by + unfold Checked.WF at h ⊢ + refine ⟨h.1.mono henv, fun c hc => ?_⟩ + exact ⟨fieldsWF_mono henv (h.2 c hc).1, (h.2 c hc).2.mono henv⟩ + +/-- Exact syntactic decomposition of an accepted family view into its +parameter telescope, index telescope, and terminal sort. -/ +theorem Checked.type_eq + {source : VInductDecl} (checked : source.Checked) : + checked.type.type = + VExpr.forallN checked.params + (VExpr.forallN checked.indices (.sort checked.resultLevel)) := by + rw [← VExpr.forallN_telN_dropN source.nparams checked.type.type, + ← forallN_ctorFields_resultOf + (VExpr.dropN source.nparams checked.type.type), + checked.result_eq, checked.params_eq, checked.indices_eq] + +/-- The semantic checker contract types the family before that family is +inserted. This is the exact premise needed by the first transaction step. -/ +theorem Checked.WF.family_isType + {source : VInductDecl} {checked : source.Checked} + {env : VEnv} (h : checked.WF env) : + env.IsType source.uvars [] checked.type.type := by + rw [← VExpr.forallN_telN_dropN source.nparams checked.type.type, + ← forallN_ctorFields_resultOf + (VExpr.dropN source.nparams checked.type.type), + checked.result_eq, ← VExpr.forallN_append] + exact IsType.forallN + (by simpa [checked.params_eq, checked.indices_eq] using h.1) + ⟨_, HasType.sort checked.direct_anatomy.2.2.1⟩ + +/-- Once the retained family constant has the checked family type, the +checked constructor-result spine types the exact normalized family +application. This fact depends only on analyzer semantics and the constant's +ordinary typing judgment; callers do not need to restate result typing for +each constructor candidate. -/ +theorem GenerationChecked.checkedResultTarget_hasType + {source : VInductDecl} (gen : GenerationChecked source) + {env : VEnv} (henv : env.Ordered) + (hchecked : gen.block.checked.WF env) + (familyConst : env.HasType source.uvars [] + (.const gen.block.sourceType.name (VLevel.params source.uvars)) + gen.block.checked.type.type) + {ctor : NormalizedCtor} (hctor : ctor ∈ gen.block.ctorPairs) : + env.HasType source.uvars (ctor.viewBinders gen.block).reverse + (ctor.resultTarget gen.block) + (.sort gen.block.checked.resultLevel) := by + have htype := gen.block.checked.type_eq + have hfamily : env.HasType source.uvars + (ctor.view.fields.reverse ++ gen.block.checked.params.reverse) + (VExpr.appN + (.const gen.block.sourceType.name (VLevel.params source.uvars)) + (VExpr.bvarRevRange ctor.view.fields.length + gen.block.checked.params.length)) + (VExpr.forallN + (VExpr.liftTelN ctor.view.fields.length + gen.block.checked.indices 0) + (.sort gen.block.checked.resultLevel)) := by + have hconst : env.HasType source.uvars + (ctor.view.fields.reverse ++ + gen.block.checked.params.reverse ++ []) + (.const gen.block.sourceType.name (VLevel.params source.uvars)) + (VExpr.forallN gen.block.checked.params + (VExpr.forallN gen.block.checked.indices + (.sort gen.block.checked.resultLevel))) := by + simpa only [htype] using familyConst.weak0 henv + have happ := HasType.appN_selfSpine' + (As := gen.block.checked.params) + (B := VExpr.forallN gen.block.checked.indices + (.sort gen.block.checked.resultLevel)) + (Δ := ctor.view.fields.reverse) (Γ := []) + (by simpa only [← htype] using gen.block.checked.type_closed) + hconst + rw [List.length_reverse, VExpr.liftN_forallN] at happ + simpa using happ + have hspine : env.SpineWF source.uvars + (ctor.view.fields.reverse ++ gen.block.checked.params.reverse) + (VExpr.forallN + (VExpr.liftTelN ctor.view.fields.length + gen.block.checked.indices 0) + (.sort gen.block.checked.resultLevel)) + ctor.view.resultIndices + (.sort gen.block.checked.resultLevel) := by + obtain ⟨c, hc, hview⟩ := gen.viewCtor_ofDirect hctor + have h := (hchecked.2 c hc).2 + rw [hview] + simpa [CheckedCtor.ofDirect, gen.block.uvars_eq, + gen.block.nparams_eq] using h + have hresult := hspine.hasType_appN hfamily + rw [← VExpr.appN_append] at hresult + have hparams : gen.block.checked.params.length = source.nparams := + gen.shape.2.1.symm.trans gen.shape.1 + have hfields := (gen.shape.2.2.2.2.2 ctor hctor).2.2.2 + rw [hparams, ← hfields] at hresult + simpa [NormalizedCtor.viewBinders, + NormalizedCtor.resultTarget] using hresult + +/-- A paired checked constructor's stored view type is exactly its analyzed +binder telescope followed by the normalized family result application. -/ +theorem GenerationChecked.viewCtorType_eq + {source : VInductDecl} (gen : GenerationChecked source) + {ctor : NormalizedCtor} (hctor : ctor ∈ gen.block.ctorPairs) : + ctor.view.value.type = + VExpr.forallN (ctor.viewBinders gen.block) + (ctor.resultTarget gen.block) := by + obtain ⟨c, hc, hview⟩ := gen.viewCtor_ofDirect hctor + have hcAn := gen.block.checked.direct_anatomy.2.2.2.2.2 c hc + have htype := VExpr.forallN_telN_dropN + gen.block.normalization.view.nparams c.type + rw [hcAn.2.1, (stage3Ctor_eq hcAn.2.2).1] at htype + have hfields := (gen.shape.2.2.2.2.2 ctor hctor).2.2.2 + have hfields' : + (ctor.rawFields gen.block.normalization.view.nparams).length = + (ctorFields (VExpr.dropN + gen.block.normalization.view.nparams c.type)).length := by + simpa [gen.block.nparams_eq, hview, + CheckedCtor.ofDirect] using hfields + simpa [← VExpr.forallN_append, NormalizedCtor.viewBinders, + NormalizedCtor.resultTarget, hview, hfields', + CheckedCtor.ofDirect, gen.block.uvars_eq, + gen.block.nparams_eq, gen.block.sourceType_name_eq, + gen.block.checked.params_eq, Nat.zero_add] using htype.symm + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.type_eq [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationChecked.viewCtorType_eq [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationChecked.checkedResultTarget_hasType [propext, Quot.sound] + +/-- Final-environment invariant for mixed raw/view generation. It contains +only facts stable after the raw family and constructors have been inserted; +the staged pre-family/post-family split remains in `GenerationChecked.WF`. -/ +theorem GenerationChecked.sourceLevels_wf {source : VInductDecl} + (gen : GenerationChecked source) : + ∀ l ∈ gen.sourceLevels, l.WF gen.recUvars := + VLevel.params'_wf + +@[simp] theorem GenerationChecked.sourceLevels_eq {source : VInductDecl} + (gen : GenerationChecked source) : + gen.sourceLevels = + VLevel.params' source.uvars gen.elimination.offset := rfl + +@[simp] theorem GenerationChecked.recUvars_eq {source : VInductDecl} + (gen : GenerationChecked source) : + gen.recUvars = source.uvars + gen.elimination.offset := rfl + +@[simp] theorem GenerationChecked.recLevels_eq {source : VInductDecl} + (gen : GenerationChecked source) : + gen.recLevels = VLevel.params gen.recUvars := rfl + +theorem GenerationChecked.sourceLevels_length {source : VInductDecl} + (gen : GenerationChecked source) : + gen.sourceLevels.length = source.uvars := + VLevel.params'_length + +theorem GenerationChecked.motiveLevel_wf {source : VInductDecl} + (gen : GenerationChecked source) : + gen.motiveLevel.WF gen.recUvars := by + cases h : gen.elimination <;> + simp [GenerationChecked.motiveLevel, + GenerationChecked.recUvars, ElimMode.recUvars, + ElimMode.offset, ElimMode.motiveLevel, VLevel.WF, h] + +theorem GenerationChecked.recLevels_wf {source : VInductDecl} + (gen : GenerationChecked source) : + ∀ l ∈ gen.recLevels, l.WF gen.recUvars := + VLevel.params_wf + +theorem GenerationChecked.recLevels_length {source : VInductDecl} + (gen : GenerationChecked source) : + gen.recLevels.length = gen.recUvars := + VLevel.params_length + +structure GenerationEnv {source : VInductDecl} + (gen : GenerationChecked source) (env : VEnv) : Prop where + ord : env.Ordered + checked : gen.block.checked.WF env + familyTel : + env.TelDefEq source.uvars [] + (gen.block.rawParams ++ gen.block.rawIndices) + (gen.block.checked.params ++ gen.block.checked.indices) + familyResult : + env.IsDefEq source.uvars + (gen.block.rawParams ++ gen.block.rawIndices).reverse + gen.block.rawResult (.sort gen.block.checked.resultLevel) + (.sort (.succ gen.block.checked.resultLevel)) + ctorWF : + ∀ ctor ∈ gen.block.ctorPairs, ctor.WF gen.block env + familyConst : + env.constants gen.block.sourceType.name = + some gen.block.sourceType.toVConstant + ctorConst : + ∀ ctor ∈ gen.block.ctorPairs, + env.constants ctor.raw.name = some ctor.raw.toVConstant + +/-- Promote a staged generation certificate into the final invariant once the +transaction supplies environment growth and exact raw constant lookups. -/ +theorem GenerationChecked.WF.toGenerationEnv {source : VInductDecl} + {gen : GenerationChecked source} {pre envT env : VEnv} + (h : gen.WF pre) + (hadd : pre.addConst gen.block.sourceType.name + gen.block.sourceType.toVConstant = some envT) + (hlePre : pre ≤ env) (hleT : envT ≤ env) (ord : env.Ordered) + (hfamily : env.constants gen.block.sourceType.name = + some gen.block.sourceType.toVConstant) + (hctors : ∀ ctor ∈ gen.block.ctorPairs, + env.constants ctor.raw.name = some ctor.raw.toVConstant) : + GenerationEnv gen env where + ord := ord + checked := h.blockWF.2.mono hlePre + familyTel := h.familyTel.mono hlePre + familyResult := h.familyResult.mono hlePre + ctorWF := fun ctor hctor => (h.ctors envT hadd ctor hctor).mono hleT + familyConst := hfamily + ctorConst := hctors + +theorem BlockGenerationChecked.sourceLevels_wf {source : VInductDecl} + (gen : BlockGenerationChecked source) : + ∀ l ∈ gen.sourceLevels, l.WF gen.recUvars := + VLevel.params'_wf + +@[simp] theorem BlockGenerationChecked.sourceLevels_eq + {source : VInductDecl} (gen : BlockGenerationChecked source) : + gen.sourceLevels = + VLevel.params' source.uvars gen.elimination.offset := rfl + +@[simp] theorem BlockGenerationChecked.recUvars_eq + {source : VInductDecl} (gen : BlockGenerationChecked source) : + gen.recUvars = source.uvars + gen.elimination.offset := rfl + +@[simp] theorem BlockGenerationChecked.recLevels_eq + {source : VInductDecl} (gen : BlockGenerationChecked source) : + gen.recLevels = VLevel.params gen.recUvars := rfl + +theorem BlockGenerationChecked.sourceLevels_length + {source : VInductDecl} (gen : BlockGenerationChecked source) : + gen.sourceLevels.length = source.uvars := + VLevel.params'_length + +theorem BlockGenerationChecked.motiveLevel_wf + {source : VInductDecl} (gen : BlockGenerationChecked source) : + gen.motiveLevel.WF gen.recUvars := by + cases h : gen.elimination <;> + simp [BlockGenerationChecked.motiveLevel, + BlockGenerationChecked.recUvars, ElimMode.recUvars, + ElimMode.offset, ElimMode.motiveLevel, VLevel.WF, h] + +theorem BlockGenerationChecked.recLevels_wf + {source : VInductDecl} (gen : BlockGenerationChecked source) : + ∀ l ∈ gen.recLevels, l.WF gen.recUvars := + VLevel.params_wf + +theorem BlockGenerationChecked.recLevels_length + {source : VInductDecl} (gen : BlockGenerationChecked source) : + gen.recLevels.length = gen.recUvars := + VLevel.params_length + +/-- Stable final-environment invariant for mutual artifact generation. -/ +structure BlockGenerationEnv {source : VInductDecl} + (gen : BlockGenerationChecked source) (env : VEnv) : Prop where + ord : env.Ordered + resultLevelWF : gen.validated.resultLevel.WF source.uvars + checked : + gen.block.checked.WF env gen.validated.resultLevel + paramsTel : + env.TelDefEq source.uvars [] gen.block.rawParams + gen.block.checked.params + familyWF : ∀ family ∈ gen.families, family.WF gen env + ctorWF : ∀ constructor ∈ gen.flatCtors, + NormalizedBlockCtor.WF gen constructor env + familyConst : ∀ family ∈ gen.families, + env.constants family.raw.name = some family.raw.toVConstant + ctorConst : ∀ constructor ∈ gen.flatCtors, + env.constants constructor.ctor.raw.name = + some constructor.ctor.raw.toVConstant + +theorem BlockGenerationChecked.WF.toBlockGenerationEnv + {source : VInductDecl} {gen : BlockGenerationChecked source} + {pre blockEnv env : VEnv} (h : gen.WF pre blockEnv) + (hlePre : pre ≤ env) (hleBlock : blockEnv ≤ env) + (ord : env.Ordered) + (hfamilies : ∀ family ∈ gen.families, + env.constants family.raw.name = some family.raw.toVConstant) + (hctors : ∀ constructor ∈ gen.flatCtors, + env.constants constructor.ctor.raw.name = + some constructor.ctor.raw.toVConstant) : + BlockGenerationEnv gen env where + ord := ord + resultLevelWF := h.resultLevelWF + checked := h.blockWF.2.mono hlePre + paramsTel := h.paramsTel.mono hlePre + familyWF := fun family hfamily => + (h.families family hfamily).mono hlePre + ctorWF := fun constructor hconstructor => + (h.constructors constructor hconstructor).mono hleBlock + familyConst := hfamilies + ctorConst := hctors + +theorem NormalizedFamily.rawType_eq {source : VInductDecl} + (family : NormalizedFamily) : + family.raw.type = + VExpr.forallN (family.rawParams source.nparams) + (VExpr.forallN (family.rawIndices source.nparams) + (family.rawResult source.nparams)) := by + conv => lhs + rw [← VExpr.forallN_telN_dropN source.nparams family.raw.type, + ← forallN_ctorFields_resultOf + (VExpr.dropN source.nparams family.raw.type)] + rfl + +namespace BlockGenerationEnv + +variable {source : VInductDecl} {gen : BlockGenerationChecked source} + {env : VEnv} (S : BlockGenerationEnv gen env) +include S + +theorem mono {env' : VEnv} (henv : env ≤ env') (ord : env'.Ordered) : + BlockGenerationEnv gen env' where + ord := ord + resultLevelWF := S.resultLevelWF + checked := S.checked.mono henv + paramsTel := S.paramsTel.mono henv + familyWF := fun family hfamily => (S.familyWF family hfamily).mono henv + ctorWF := fun constructor hconstructor => + (S.ctorWF constructor hconstructor).mono henv + familyConst := fun family hfamily => + henv.constants (S.familyConst family hfamily) + ctorConst := fun constructor hconstructor => + henv.constants (S.ctorConst constructor hconstructor) + +theorem generationParams_defeq : + env.TelDefEq source.uvars [] + (generationParams gen.block.rawParams gen.block.checked.params) + gen.block.checked.params := + S.paramsTel.generationParams S.ord + +theorem generationParams_length : + (generationParams gen.block.rawParams + gen.block.checked.params).length = source.nparams := by + exact (generationParams_length_of_eq S.paramsTel.length_eq).trans + gen.shape.1 + +theorem generationParams_ctx : + env.IsDefEqCtx source.uvars [] + (generationParams gen.block.rawParams + gen.block.checked.params).reverse + gen.block.checked.params.reverse := by + simpa using S.generationParams_defeq.ctx + +theorem generationParams_ctx_rec : + env.IsDefEqCtx gen.recUvars [] gen.paramsTel.reverse + (gen.block.checked.params.map + (VExpr.instL gen.sourceLevels)).reverse := by + have h := S.generationParams_defeq.instL + (U' := gen.recUvars) gen.sourceLevels_wf + simpa [BlockGenerationChecked.paramsTel, List.map_reverse] using h.ctx + +theorem rawFamily_onTel {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + env.OnTel source.uvars [] + (family.rawParams source.nparams ++ + family.rawIndices source.nparams) := + (S.familyWF family hfamily).familyTel.raw_onTel + +theorem familyConst_decl {family : NormalizedFamily} + (hfamily : family ∈ gen.families) {Γ : List VExpr} : + env.HasType source.uvars Γ + (.const family.raw.name (VLevel.params source.uvars)) + family.raw.type := by + have hwf : family.raw.toVConstant.WF env := by + show env.IsType family.raw.uvars [] family.raw.type + rw [gen.family_uvars hfamily] + exact (S.familyWF family hfamily).rawFamily_isType + have h := HasType.const0 (S.familyConst family hfamily) hwf + rw [gen.family_uvars hfamily] at h + exact h.weak0 S.ord + +theorem ctorConst_decl {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) {Γ : List VExpr} : + env.HasType source.uvars Γ + (.const constructor.ctor.raw.name (VLevel.params source.uvars)) + constructor.ctor.raw.type := by + have hwf : constructor.ctor.raw.toVConstant.WF env := by + show env.IsType constructor.ctor.raw.uvars [] + constructor.ctor.raw.type + rw [gen.flatCtor_uvars hconstructor] + exact (S.ctorWF constructor hconstructor).rawDeclared_isType + have h := HasType.const0 (S.ctorConst constructor hconstructor) hwf + rw [gen.flatCtor_uvars hconstructor] at h + exact h.weak0 S.ord + +theorem rawParams_defeq {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + env.TelDefEq source.uvars [] + (family.rawParams source.nparams) gen.block.checked.params := by + have h := (S.familyWF family hfamily).familyTel.take source.nparams + have hraw : + (family.rawParams source.nparams ++ + family.rawIndices source.nparams).take source.nparams = + family.rawParams source.nparams := by + let Ps := family.rawParams source.nparams + let Is := family.rawIndices source.nparams + have hlen : Ps.length = source.nparams := + (gen.shape.2.2.2.2 family hfamily).2.2.1 + change (Ps ++ Is).take source.nparams = Ps + rw [← hlen, List.take_append, List.take_length] + simp + have hviewLen : gen.block.checked.params.length = source.nparams := + gen.shape.2.1.symm.trans gen.shape.1 + have hview : + (gen.block.checked.params ++ family.view.indices).take + source.nparams = gen.block.checked.params := by + rw [← hviewLen, List.take_append, List.take_length] + simp + rw [hraw, hview] at h + exact h + +theorem emittedFamilyTel {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + env.TelDefEq source.uvars [] + (family.rawParams source.nparams ++ family.rawIndices source.nparams) + (gen.block.checked.params ++ family.rawIndices source.nparams) := by + have hindices : env.OnTel source.uvars + (family.rawParams source.nparams).reverse + (family.rawIndices source.nparams) := by + simpa using (S.rawFamily_onTel hfamily).of_append.2 + exact (S.rawParams_defeq hfamily).append_refl (by simpa using hindices) + +theorem emittedFamily_onTel {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + env.OnTel source.uvars [] + (gen.block.checked.params ++ family.rawIndices source.nparams) := + (S.emittedFamilyTel hfamily).view_onTel S.ord + +theorem generationFamilyTel {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + env.TelDefEq source.uvars [] + (generationParams gen.block.rawParams gen.block.checked.params ++ + family.rawIndices source.nparams) + (gen.block.checked.params ++ family.rawIndices source.nparams) := by + have hindicesChecked : env.OnTel source.uvars + gen.block.checked.params.reverse (family.rawIndices source.nparams) := by + simpa using (S.emittedFamily_onTel hfamily).of_append.2 + have hindicesGeneration : env.OnTel source.uvars + (generationParams gen.block.rawParams + gen.block.checked.params).reverse + (family.rawIndices source.nparams) := + hindicesChecked.defeqDFC S.ord + (S.generationParams_ctx.symm S.ord) + exact S.generationParams_defeq.append_refl + (by simpa using hindicesGeneration) + +theorem generationFamily_onTel {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + env.OnTel source.uvars [] + (generationParams gen.block.rawParams gen.block.checked.params ++ + family.rawIndices source.nparams) := + (S.generationFamilyTel hfamily).raw_onTel + +theorem emittedFamily_ctx {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + env.IsDefEqCtx source.uvars [] + (family.rawParams source.nparams ++ + family.rawIndices source.nparams).reverse + (gen.block.checked.params ++ + family.rawIndices source.nparams).reverse := by + simpa using (S.emittedFamilyTel hfamily).ctx + +theorem generationFamily_ctx {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + env.IsDefEqCtx source.uvars [] + (generationParams gen.block.rawParams gen.block.checked.params ++ + family.rawIndices source.nparams).reverse + (gen.block.checked.params ++ + family.rawIndices source.nparams).reverse := by + simpa using (S.generationFamilyTel hfamily).ctx + +theorem familyConst_emitted_decl {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + env.HasType source.uvars [] + (.const family.raw.name (VLevel.params source.uvars)) + (VExpr.forallN + (gen.block.checked.params ++ family.rawIndices source.nparams) + (family.rawResult source.nparams)) := by + have hc : env.HasType source.uvars [] + (.const family.raw.name (VLevel.params source.uvars)) + (VExpr.forallN + (family.rawParams source.nparams ++ family.rawIndices source.nparams) + (family.rawResult source.nparams)) := by + rw [VExpr.forallN_append, ← family.rawType_eq] + exact S.familyConst_decl hfamily + obtain ⟨_, htel⟩ := (S.emittedFamilyTel hfamily).forallN_defeq + (by simpa [VEnv.HasType] using + (S.familyWF family hfamily).familyResult.hasType.1) + exact htel.defeq hc + +theorem familyConst_generation_decl {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + env.HasType source.uvars [] + (.const family.raw.name (VLevel.params source.uvars)) + (VExpr.forallN + (generationParams gen.block.rawParams gen.block.checked.params ++ + family.rawIndices source.nparams) + (family.rawResult source.nparams)) := by + have hresultChecked := + (S.familyWF family hfamily).familyResult.defeqDFC S.ord + (S.emittedFamily_ctx hfamily) + have hresultGeneration := hresultChecked.defeqDFC S.ord + ((S.generationFamily_ctx hfamily).symm S.ord) + obtain ⟨_, htel⟩ := (S.generationFamilyTel hfamily).forallN_defeq + (by simpa [VEnv.HasType] using hresultGeneration.hasType.1) + exact htel.defeq' (S.familyConst_emitted_decl hfamily) + +theorem familyApp_hasType {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + env.HasType gen.recUvars + ((gen.idxTel family).reverse ++ gen.paramsTel.reverse) + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (gen.idxTel family).length source.nparams ++ + VExpr.bvarRevRange 0 (gen.idxTel family).length)) + (.sort (gen.validated.resultLevel.inst gen.sourceLevels)) := by + let ls := gen.sourceLevels + have hconst₀ := (S.familyConst_generation_decl hfamily).instL + (U' := gen.recUvars) gen.sourceLevels_wf + have hconst₁ : env.HasType gen.recUvars [] + (.const family.raw.name ls) + (VExpr.forallN (gen.paramsTel ++ gen.idxTel family) + ((family.rawResult source.nparams).instL ls)) := by + simpa [ls, BlockGenerationChecked.paramsTel, + BlockGenerationChecked.idxTel, VExpr.instL_forallN, + VExpr.instL, VLevel.params_map_inst_params'] using hconst₀ + have hclosed : + (VExpr.forallN (gen.paramsTel ++ gen.idxTel family) + ((family.rawResult source.nparams).instL ls)).ClosedN 0 := + (hconst₁.closedN' S.ord.closed trivial).2.2 + have hconst : env.HasType gen.recUvars + ((gen.paramsTel ++ gen.idxTel family).reverse) + (.const family.raw.name ls) + (VExpr.forallN (gen.paramsTel ++ gen.idxTel family) + ((family.rawResult source.nparams).instL ls)) := + hconst₁.weak0 S.ord + have happ := HasType.appN_selfSpine' + (Δ := []) (Γ := []) hclosed (by simpa using hconst) + simp only [List.length_nil, VExpr.liftN_zero, List.nil_append, + List.append_nil] at happ + have hlen : (gen.paramsTel ++ gen.idxTel family).length = + (gen.idxTel family).length + source.nparams := by + simp only [List.length_append, BlockGenerationChecked.paramsTel, + BlockGenerationChecked.idxTel, List.length_map] + rw [S.generationParams_length] + omega + rw [VExpr.bvarRevRange_congr' 0 hlen, + ← VExpr.bvarRevRange_append] at happ + have hresult := (S.familyWF family hfamily).familyResult.instL + (U' := gen.recUvars) gen.sourceLevels_wf + simp only [List.map_reverse] at hresult + have hctxChecked := ((S.emittedFamilyTel hfamily).instL + (U' := gen.recUvars) gen.sourceLevels_wf).ctx + simp only [List.map_nil, List.append_nil] at hctxChecked + have hresultChecked := hresult.defeqDFC S.ord hctxChecked + have hctxGeneration := ((S.generationFamilyTel hfamily).instL + (U' := gen.recUvars) gen.sourceLevels_wf).ctx + simp only [List.map_nil, List.append_nil] at hctxGeneration + have hresultGeneration := hresultChecked.defeqDFC S.ord + (hctxGeneration.symm S.ord) + have hresult' : env.IsDefEq gen.recUvars + ((gen.idxTel family).reverse ++ gen.paramsTel.reverse) + ((family.rawResult source.nparams).instL ls) + (.sort (gen.validated.resultLevel.inst ls)) + (.sort (.succ (gen.validated.resultLevel.inst ls))) := by + simpa [ls, BlockGenerationChecked.paramsTel, + BlockGenerationChecked.idxTel, List.map_reverse, VLevel.inst] using + hresultGeneration + exact hresult'.defeq (by + simpa [List.reverse_append] using happ) + +theorem motive_isType {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + env.IsType gen.recUvars gen.paramsTel.reverse + (gen.motiveType family) := by + have htel₀ := (S.generationFamily_onTel hfamily).instL + (U' := gen.recUvars) gen.sourceLevels_wf + have htel : env.OnTel gen.recUvars [] + (gen.paramsTel ++ gen.idxTel family) := by + simpa [BlockGenerationChecked.paramsTel, + BlockGenerationChecked.idxTel] using htel₀ + have hidx : env.OnTel gen.recUvars gen.paramsTel.reverse + (gen.idxTel family) := by + simpa using htel.of_append.2 + refine IsType.forallN hidx ?_ + exact ⟨_, by + simpa [BlockGenerationChecked.motiveType] using + HasType.forallE (S.familyApp_hasType hfamily) + (HasType.sort gen.motiveLevel_wf)⟩ + +end BlockGenerationEnv + +/-- Exact decomposition of the stored raw family type. -/ +theorem NormalizedChecked.rawType_eq {source : VInductDecl} + (block : NormalizedChecked source) : + block.sourceType.type = + VExpr.forallN block.rawParams + (VExpr.forallN block.rawIndices block.rawResult) := by + conv => lhs + rw [← VExpr.forallN_telN_dropN source.nparams + block.sourceType.type, + ← forallN_ctorFields_resultOf + (VExpr.dropN source.nparams block.sourceType.type)] + rfl + +/-- Exact decomposition of a stored raw constructor type into its declared +parameter/field telescope and terminal result. -/ +theorem NormalizedCtor.rawType_eq {source : VInductDecl} + (ctor : NormalizedCtor) : + ctor.raw.type = + VExpr.forallN (ctor.declaredBinders source.nparams) + (ctor.rawResult source.nparams) := by + conv => lhs + rw [← VExpr.forallN_telN_dropN source.nparams + ctor.raw.type, + ← forallN_ctorFields_resultOf + (VExpr.dropN source.nparams ctor.raw.type)] + rw [NormalizedCtor.declaredBinders, NormalizedCtor.rawFields, + NormalizedCtor.rawResult, ← VExpr.forallN_append] + +namespace GenerationEnv + +variable {source : VInductDecl} {gen : GenerationChecked source} + {env : VEnv} (S : GenerationEnv gen env) +include S +-- `S` is included throughout so every lemma below is reachable as `S.lemma`, +-- including those whose statement and proof never mention it. +set_option linter.unusedSectionVars false + +/-- The final mixed-generation invariant is monotone once the larger +environment is known to remain ordered. -/ +theorem mono {env' : VEnv} (henv : env ≤ env') (ord : env'.Ordered) : + GenerationEnv gen env' where + ord := ord + checked := S.checked.mono henv + familyTel := S.familyTel.mono henv + familyResult := S.familyResult.mono henv + ctorWF := fun ctor hctor => (S.ctorWF ctor hctor).mono henv + familyConst := henv.constants S.familyConst + ctorConst := fun ctor hctor => henv.constants (S.ctorConst ctor hctor) + +/-- The checked result universe is well formed at the source declaration's +universe arity. -/ +theorem resultLevel_WF : + gen.block.checked.resultLevel.WF source.uvars := by + rw [gen.block.uvars_eq] + exact gen.block.checked.direct_anatomy.2.2.1 + +theorem rawFamily_onTel : + env.OnTel source.uvars [] + (gen.block.rawParams ++ gen.block.rawIndices) := + S.familyTel.raw_onTel + +/-- The family parameter prefix is structurally definitionally equal to the +checked locals retained by validation. -/ +theorem rawParams_defeq : + env.TelDefEq source.uvars [] gen.block.rawParams + gen.block.checked.params := by + have h := S.familyTel.take source.nparams + have hraw : + (gen.block.rawParams ++ gen.block.rawIndices).take source.nparams = + gen.block.rawParams := by + rw [← gen.shape.1, List.take_append, List.take_length] + simp + have hviewLen : gen.block.checked.params.length = source.nparams := + gen.shape.2.1.symm.trans gen.shape.1 + have hview : + (gen.block.checked.params ++ gen.block.checked.indices).take + source.nparams = gen.block.checked.params := by + rw [← hviewLen, List.take_append, List.take_length] + simp + rw [hraw, hview] at h + exact h + +/-- Raw and checked parameter contexts denote the same local telescope. -/ +theorem rawParams_ctx : + env.IsDefEqCtx source.uvars [] gen.block.rawParams.reverse + gen.block.checked.params.reverse := + by simpa using S.rawParams_defeq.ctx + +/-- The exact parameter surface emitted in recursor metadata remains +definitionally equal to the analyzer-owned parameters. -/ +theorem generationParams_defeq : + env.TelDefEq source.uvars [] gen.block.generationParams + gen.block.checked.params := by + exact S.rawParams_defeq.generationParams S.ord + +theorem generationParams_ctx : + env.IsDefEqCtx source.uvars [] gen.block.generationParams.reverse + gen.block.checked.params.reverse := + by simpa using S.generationParams_defeq.ctx + +theorem generationParams_length : + gen.block.generationParams.length = source.nparams := by + have hrawView := S.rawParams_defeq.length_eq + exact (VInductDecl.generationParams_length_of_eq hrawView).trans + gen.shape.1 + +theorem generationParams_ctx_rec : + env.IsDefEqCtx (gen.recUvars) [] gen.paramsTel.reverse + (gen.block.checked.params.map + (VExpr.instL (gen.sourceLevels))).reverse := by + have h := S.generationParams_defeq.instL + (U' := gen.recUvars) gen.sourceLevels_wf + simpa [GenerationChecked.paramsTel, List.map_reverse] using h.ctx + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationEnv.generationParams_defeq [propext, Quot.sound] + +/-- Stored constructor fields are well formed over the exact generated +parameter surface after universe transport. -/ +theorem generationFields_onTel_rec {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) : + env.OnTel (gen.recUvars) gen.paramsTel.reverse + (ctor.fieldsR source.uvars source.nparams gen.elimination) := by + have hemitted := (S.ctorWF ctor hctor).rawEmitted_onTel + have hfields₀ := (OnTel.of_append + (As := gen.block.checked.params) hemitted).2 + have hfields₁ := hfields₀.instL + (U' := gen.recUvars) gen.sourceLevels_wf + have hfieldsChecked : env.OnTel (gen.recUvars) + (gen.block.checked.params.map + (VExpr.instL (gen.sourceLevels))).reverse + (ctor.fieldsR source.uvars source.nparams gen.elimination) := by + simpa [NormalizedCtor.fieldsR, List.map_reverse] using hfields₁ + exact hfieldsChecked.defeqDFC S.ord + (S.generationParams_ctx_rec.symm S.ord) + +theorem generationFieldPrefix_ctx_rec {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) (j : Nat) : + env.IsDefEqCtx (gen.recUvars) [] + ((ctor.fieldsR source.uvars source.nparams gen.elimination |>.take j).reverse ++ + gen.paramsTel.reverse) + ((ctor.fieldsR source.uvars source.nparams gen.elimination |>.take j).reverse ++ + (gen.block.checked.params.map + (VExpr.instL (gen.sourceLevels))).reverse) := by + have hfields := S.generationFields_onTel_rec hctor + rw [← List.take_append_drop j + (ctor.fieldsR source.uvars source.nparams gen.elimination)] at hfields + exact (hfields.of_append.1).extendDefEqCtx + S.generationParams_ctx_rec + +/-- The family telescope used by generated artifacts keeps raw index syntax +but uses the checked parameter prefix consumed by validation. -/ +theorem emittedFamilyTel : + env.TelDefEq source.uvars [] + (gen.block.rawParams ++ gen.block.rawIndices) + (gen.block.checked.params ++ gen.block.rawIndices) := by + have hindices : env.OnTel source.uvars gen.block.rawParams.reverse + gen.block.rawIndices := by + simpa using S.rawFamily_onTel.of_append.2 + exact S.rawParams_defeq.append_refl (by simpa using hindices) + +/-- The emitted mixed family telescope is well formed. -/ +theorem emittedFamily_onTel : + env.OnTel source.uvars [] + (gen.block.checked.params ++ gen.block.rawIndices) := + S.emittedFamilyTel.view_onTel S.ord + +/-- Replace the checked parameter prefix by the kernel-observable generation +surface while leaving the stored raw index telescope unchanged. -/ +theorem generationFamilyTel : + env.TelDefEq source.uvars [] + (gen.block.generationParams ++ gen.block.rawIndices) + (gen.block.checked.params ++ gen.block.rawIndices) := by + have hindicesChecked : env.OnTel source.uvars + gen.block.checked.params.reverse gen.block.rawIndices := by + simpa using S.emittedFamily_onTel.of_append.2 + have hindicesGeneration : env.OnTel source.uvars + gen.block.generationParams.reverse gen.block.rawIndices := + hindicesChecked.defeqDFC S.ord + (S.generationParams_ctx.symm S.ord) + exact S.generationParams_defeq.append_refl + (by simpa using hindicesGeneration) + +theorem generationFamily_onTel : + env.OnTel source.uvars [] + (gen.block.generationParams ++ gen.block.rawIndices) := + S.generationFamilyTel.raw_onTel + +theorem generationFamily_ctx : + env.IsDefEqCtx source.uvars [] + (gen.block.generationParams ++ gen.block.rawIndices).reverse + (gen.block.checked.params ++ gen.block.rawIndices).reverse := + by simpa using S.generationFamilyTel.ctx + +/-- Completed raw and emitted family contexts are definitionally equal. -/ +theorem emittedFamily_ctx : + env.IsDefEqCtx source.uvars [] + (gen.block.rawParams ++ gen.block.rawIndices).reverse + (gen.block.checked.params ++ gen.block.rawIndices).reverse := + by simpa using S.emittedFamilyTel.ctx + +theorem rawFamily_isType : + env.IsType source.uvars [] gen.block.sourceType.type := by + rw [gen.block.rawType_eq, ← VExpr.forallN_append] + have hresult₀ : env.IsType source.uvars + (gen.block.rawParams ++ gen.block.rawIndices).reverse + gen.block.rawResult := + ⟨_, S.familyResult.hasType.1⟩ + have hresult : env.IsType source.uvars + ((gen.block.rawParams ++ gen.block.rawIndices).reverse ++ []) + gen.block.rawResult := by + simpa using hresult₀ + exact IsType.forallN S.rawFamily_onTel hresult + +theorem rawCtor_isType {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) : + env.IsType source.uvars [] ctor.raw.type := + (S.ctorWF ctor hctor).rawDeclared_isType + +/-- The raw family constant at declaration universes, in any context. -/ +theorem familyConst_decl {Γ : List VExpr} : + env.HasType source.uvars Γ + (.const gen.block.sourceType.name (VLevel.params source.uvars)) + gen.block.sourceType.type := by + have hwf : gen.block.sourceType.toVConstant.WF env := by + show env.IsType gen.block.sourceType.uvars [] + gen.block.sourceType.type + rw [gen.block.sourceType_uvars_eq] + exact S.rawFamily_isType + have h := HasType.const0 S.familyConst hwf + rw [gen.block.sourceType_uvars_eq] at h + exact h.weak0 S.ord + +/-- A stored raw constructor constant at declaration universes, in any +context. -/ +theorem ctorConst_decl {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) {Γ : List VExpr} : + env.HasType source.uvars Γ + (.const ctor.raw.name (VLevel.params source.uvars)) + ctor.raw.type := by + have hwf : ctor.raw.toVConstant.WF env := by + show env.IsType ctor.raw.uvars [] ctor.raw.type + rw [gen.ctor_uvars_eq hctor] + exact S.rawCtor_isType hctor + have h := HasType.const0 (S.ctorConst ctor hctor) hwf + rw [gen.ctor_uvars_eq hctor] at h + exact h.weak0 S.ord + +/-- The stored constructor constant applied to the checked-parameter/raw-field +self-spine emitted by mixed artifacts. The constructor's declared parameter +prefix need only be definitionally equal to this checked family prefix. -/ +theorem ctorApp_emitted_decl {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) : + env.HasType source.uvars + ((ctor.rawFields source.nparams).reverse ++ + gen.block.checked.params.reverse) + (VExpr.appN + (.const ctor.raw.name (VLevel.params source.uvars)) + (VExpr.bvarRevRange + (ctor.rawFields source.nparams).length source.nparams ++ + VExpr.bvarRevRange 0 + (ctor.rawFields source.nparams).length)) + (ctor.resultTarget gen.block) := by + let E := ctor.emittedBinders gen.block + let V := ctor.viewBinders gen.block + have hc : env.HasType source.uvars [] + (.const ctor.raw.name (VLevel.params source.uvars)) + (VExpr.forallN (ctor.declaredBinders source.nparams) + (ctor.rawResult source.nparams)) := by + rw [← ctor.rawType_eq] + exact S.ctorConst_decl hctor + obtain ⟨_, hdecl⟩ := + (S.ctorWF ctor hctor).declaredTel.forallN_defeq + (by + simpa using (S.ctorWF ctor hctor).declaredResult) + have hview : env.HasType source.uvars [] + (.const ctor.raw.name (VLevel.params source.uvars)) + (VExpr.forallN V (ctor.resultTarget gen.block)) := by + simpa [V] using hdecl.defeq hc + have hresult : env.HasType source.uvars E.reverse + (ctor.resultTarget gen.block) + (.sort gen.block.checked.resultLevel) := by + simpa [E] using + (S.ctorWF ctor hctor).emittedResult.hasType.2 + obtain ⟨_, hemit⟩ := + (S.ctorWF ctor hctor).emittedTel.forallN_defeq + (by simpa [E, VEnv.HasType] using hresult) + have hcE₀ : env.HasType source.uvars [] + (.const ctor.raw.name (VLevel.params source.uvars)) + (VExpr.forallN E (ctor.resultTarget gen.block)) := by + exact hemit.defeq' (by simpa [V] using hview) + have hclosed : + (VExpr.forallN E (ctor.resultTarget gen.block)).ClosedN 0 := + (hcE₀.closedN' S.ord.closed trivial).2.2 + have hcE : env.HasType source.uvars E.reverse + (.const ctor.raw.name (VLevel.params source.uvars)) + (VExpr.forallN E (ctor.resultTarget gen.block)) := + hcE₀.weak0 S.ord + have happ := HasType.appN_selfSpine' + (As := E) (B := ctor.resultTarget gen.block) + (Δ := []) (Γ := []) hclosed (by simpa using hcE) + simp only [List.length_nil, VExpr.liftN_zero, + List.nil_append, List.append_nil] at happ + have hEctx : + E.reverse = + (ctor.rawFields source.nparams).reverse ++ + gen.block.checked.params.reverse := by + simp [E, NormalizedCtor.emittedBinders, + List.reverse_append] + have hElen : + E.length = + (ctor.rawFields source.nparams).length + + source.nparams := by + have hp : gen.block.checked.params.length = source.nparams := + gen.shape.2.1.symm.trans gen.shape.1 + simp [E, NormalizedCtor.emittedBinders, hp] + omega + rw [hEctx, hElen, + ← VExpr.bvarRevRange_append source.nparams + (ctor.rawFields source.nparams).length] at happ + exact happ + +/-- The raw family constant instantiated into recursor universes. -/ +theorem familyConst_rec {Γ : List VExpr} : + env.HasType (gen.recUvars) Γ + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (gen.block.sourceType.type.instL + (gen.sourceLevels)) := by + have h := (S.familyConst_decl (Γ := [])).instL + (U' := gen.recUvars) gen.sourceLevels_wf + rw [show + (VExpr.const gen.block.sourceType.name + (VLevel.params source.uvars)).instL + (gen.sourceLevels) = + .const gen.block.sourceType.name + (gen.sourceLevels) by + simp [VExpr.instL, VLevel.params_map_inst_params']] at h + exact h.weak0 S.ord + +/-- The raw family type decomposed in recursor universes. -/ +theorem rawType_rec_eq : + gen.block.sourceType.type.instL (gen.sourceLevels) = + VExpr.forallN + (gen.block.rawParams.map + (VExpr.instL (gen.sourceLevels))) + (VExpr.forallN gen.idxTel + (gen.block.rawResult.instL + (gen.sourceLevels))) := by + rw [gen.block.rawType_eq, VExpr.instL_forallN, + VExpr.instL_forallN] + rfl + +/-- The family constant may be viewed through the checked-parameter/raw-index +telescope emitted by generated artifacts. -/ +theorem familyConst_emitted_decl : + env.HasType source.uvars [] + (.const gen.block.sourceType.name (VLevel.params source.uvars)) + (VExpr.forallN + (gen.block.checked.params ++ gen.block.rawIndices) + gen.block.rawResult) := by + have hc : env.HasType source.uvars [] + (.const gen.block.sourceType.name (VLevel.params source.uvars)) + (VExpr.forallN + (gen.block.rawParams ++ gen.block.rawIndices) + gen.block.rawResult) := by + rw [VExpr.forallN_append, ← gen.block.rawType_eq] + exact S.familyConst_decl + obtain ⟨_, htel⟩ := S.emittedFamilyTel.forallN_defeq + (by simpa [VEnv.HasType] using S.familyResult.hasType.1) + exact htel.defeq hc + +/-- The family constant viewed through the exact parameter syntax retained +by generated kernel metadata. -/ +theorem familyConst_generation_decl : + env.HasType source.uvars [] + (.const gen.block.sourceType.name (VLevel.params source.uvars)) + (VExpr.forallN + (gen.block.generationParams ++ gen.block.rawIndices) + gen.block.rawResult) := by + have hresultChecked := S.familyResult.defeqDFC S.ord + S.emittedFamily_ctx + have hresultGeneration := hresultChecked.defeqDFC S.ord + (S.generationFamily_ctx.symm S.ord) + obtain ⟨_, htel⟩ := S.generationFamilyTel.forallN_defeq + (by simpa [VEnv.HasType] using hresultGeneration.hasType.1) + exact htel.defeq' S.familyConst_emitted_decl + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationEnv.familyConst_generation_decl [propext, Quot.sound] + +/-- The family applied to its checked-parameter/raw-index self-spine has the +normalized result sort. -/ +theorem familyApp_hasType : + env.HasType (gen.recUvars) + (gen.idxTel.reverse ++ gen.paramsTel.reverse) + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange gen.idxTel.length source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)) + (.sort + (gen.block.checked.resultLevel.inst + (gen.sourceLevels))) := by + let ls := gen.sourceLevels + have hconst₀ := S.familyConst_generation_decl.instL + (U' := gen.recUvars) gen.sourceLevels_wf + have hconst₁ : env.HasType (gen.recUvars) [] + (.const gen.block.sourceType.name ls) + (VExpr.forallN (gen.paramsTel ++ gen.idxTel) + (gen.block.rawResult.instL ls)) := by + simpa [ls, GenerationChecked.paramsTel, + GenerationChecked.idxTel, VExpr.instL_forallN, + VExpr.instL, VLevel.params_map_inst_params'] using hconst₀ + have hcanonicalClosed : + (VExpr.forallN (gen.paramsTel ++ gen.idxTel) + (gen.block.rawResult.instL ls)).ClosedN 0 := by + exact (hconst₁.closedN' S.ord.closed trivial).2.2 + have hconst : env.HasType (gen.recUvars) + ((gen.paramsTel ++ gen.idxTel).reverse) + (.const gen.block.sourceType.name ls) + (VExpr.forallN (gen.paramsTel ++ gen.idxTel) + (gen.block.rawResult.instL ls)) := + hconst₁.weak0 S.ord + have happ := HasType.appN_selfSpine' + (Δ := []) (Γ := []) hcanonicalClosed (by + simpa using hconst) + simp only [List.length_nil, VExpr.liftN_zero, List.nil_append, + List.append_nil] at happ + have hlen : + (gen.paramsTel ++ gen.idxTel).length = + gen.idxTel.length + source.nparams := by + simp only [List.length_append, GenerationChecked.paramsTel, + GenerationChecked.idxTel, List.length_map] + rw [S.generationParams_length] + omega + rw [VExpr.bvarRevRange_congr' 0 hlen, + ← VExpr.bvarRevRange_append] at happ + have hresult := S.familyResult.instL + (U' := gen.recUvars) gen.sourceLevels_wf + simp only [List.map_reverse] at hresult + have hctxChecked := (S.emittedFamilyTel.instL + (U' := gen.recUvars) gen.sourceLevels_wf).ctx + simp only [List.map_nil, List.append_nil] at hctxChecked + have hresultChecked := hresult.defeqDFC S.ord hctxChecked + have hctxGeneration := (S.generationFamilyTel.instL + (U' := gen.recUvars) gen.sourceLevels_wf).ctx + simp only [List.map_nil, List.append_nil] at hctxGeneration + have hresultGeneration := hresultChecked.defeqDFC S.ord + (hctxGeneration.symm S.ord) + have hresult' : env.IsDefEq (gen.recUvars) + (gen.idxTel.reverse ++ gen.paramsTel.reverse) + (gen.block.rawResult.instL ls) + (.sort (gen.block.checked.resultLevel.inst ls)) + (.sort (.succ (gen.block.checked.resultLevel.inst ls))) := by + simpa [ls, GenerationChecked.paramsTel, GenerationChecked.idxTel, + List.map_reverse, VLevel.inst] using hresultGeneration + exact hresult'.defeq (by + simpa [List.reverse_append] using happ) + +/-- The mixed motive is a well-formed type over the checked parameter +context. -/ +theorem motive_isType : + env.IsType (gen.recUvars) gen.paramsTel.reverse + gen.motiveType := by + have htel₀ := S.generationFamily_onTel.instL + (U' := gen.recUvars) gen.sourceLevels_wf + have htel : + env.OnTel (gen.recUvars) [] + (gen.paramsTel ++ gen.idxTel) := by + simpa [GenerationChecked.paramsTel, GenerationChecked.idxTel] using htel₀ + have hidx : env.OnTel (gen.recUvars) gen.paramsTel.reverse + gen.idxTel := by + simpa using htel.of_append.2 + refine IsType.forallN hidx ?_ + exact ⟨_, by + simpa [GenerationChecked.motiveType] using + HasType.forallE S.familyApp_hasType + (HasType.sort gen.motiveLevel_wf)⟩ + +end GenerationEnv + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationEnv.motive_isType [propext, Quot.sound] + +theorem BlockGenerationChecked.motiveTypesAux_length + {source : VInductDecl} (gen : BlockGenerationChecked source) : + ∀ (families : List NormalizedFamily) (i : Nat), + (gen.motiveTypesAux families i).length = families.length + | [], _ => rfl + | _ :: families, i => by + simp only [BlockGenerationChecked.motiveTypesAux, List.length_cons] + rw [gen.motiveTypesAux_length families (i + 1)] + +theorem BlockGenerationChecked.motiveTypes_length + {source : VInductDecl} (gen : BlockGenerationChecked source) : + gen.motiveTypes.length = gen.familyCount := by + unfold BlockGenerationChecked.motiveTypes + exact gen.motiveTypesAux_length gen.families 0 + +/-- Pairing arbitrary stored families with a dependent checked-family spine +does not change the checked ordinal at any surviving position. -/ +theorem CheckedFamilies.pairNormalizedFamilies_getElem?_ordinal + {source : VInductDecl} {params : List VExpr} + {ordinal : Nat} {types : List VInductiveType} + (families : CheckedFamilies source params ordinal types) : + ∀ {raws i family}, + (pairNormalizedFamilies raws families.data)[i]? = some family → + family.view.ordinal = ordinal + i := by + induction families with + | nil => + intro raws i family h + simp [CheckedFamilies.data, pairNormalizedFamilies] at h + | @cons ordinal type types head tail ih => + intro raws i family h + cases raws with + | nil => simp [pairNormalizedFamilies] at h + | cons raw raws => + cases i with + | zero => + simp only [CheckedFamilies.data, pairNormalizedFamilies, + List.getElem?_cons_zero] at h + injection h with hfamily + subst family + rfl + | succ i => + simp only [CheckedFamilies.data, pairNormalizedFamilies, + List.getElem?_cons_succ] at h + have hord := ih h + omega + +/-- Every retained family is found at its checked source ordinal. -/ +theorem BlockGenerationChecked.family_getElem?_ordinal + {source : VInductDecl} (gen : BlockGenerationChecked source) + {family : NormalizedFamily} (hfamily : family ∈ gen.families) : + gen.families[family.view.ordinal]? = some family := by + obtain ⟨i, hi⟩ := List.mem_iff_getElem?.1 hfamily + have hi' : + (pairNormalizedFamilies source.types + gen.block.checked.families.data)[i]? = some family := by + simpa [BlockGenerationChecked.families, + NormalizedCheckedBlock.familyPairs] using hi + have hord := + CheckedFamilies.pairNormalizedFamilies_getElem?_ordinal + gen.block.checked.families hi' + have hord' : family.view.ordinal = i := by + simpa using hord + rwa [hord'] + +theorem BlockGenerationChecked.family_ordinal_lt + {source : VInductDecl} (gen : BlockGenerationChecked source) + {family : NormalizedFamily} (hfamily : family ∈ gen.families) : + family.view.ordinal < gen.familyCount := by + obtain ⟨h, -⟩ := List.getElem?_eq_some_iff.1 + (gen.family_getElem?_ordinal hfamily) + exact h + +/-- Selecting a retained family by its checked ordinal recovers its exact +raw constant name. -/ +@[simp] theorem BlockGenerationChecked.familyNameAt_ordinal + {source : VInductDecl} (gen : BlockGenerationChecked source) + {family : NormalizedFamily} (hfamily : family ∈ gen.families) : + gen.familyNameAt family.view.ordinal = family.raw.name := by + simp [BlockGenerationChecked.familyNameAt, + gen.family_getElem?_ordinal hfamily] + +/-- Positional lookup through the progressively weakened mutual motive +telescope. -/ +theorem BlockGenerationChecked.motiveTypesAux_getElem? + {source : VInductDecl} (gen : BlockGenerationChecked source) : + ∀ (families : List NormalizedFamily) (i q : Nat), + (gen.motiveTypesAux families i)[q]? = + families[q]?.map fun family => + (gen.motiveType family).liftN (i + q) + | [], _, q => by simp [BlockGenerationChecked.motiveTypesAux] + | _ :: _, _, 0 => by simp [BlockGenerationChecked.motiveTypesAux] + | _ :: families, i, q + 1 => by + simp only [BlockGenerationChecked.motiveTypesAux, + List.getElem?_cons_succ] + rw [gen.motiveTypesAux_getElem? families (i + 1) q, + show i + 1 + q = i + (q + 1) by omega] + +theorem BlockGenerationChecked.motiveTypes_getElem?_ordinal + {source : VInductDecl} (gen : BlockGenerationChecked source) + {family : NormalizedFamily} (hfamily : family ∈ gen.families) : + gen.motiveTypes[family.view.ordinal]? = + some ((gen.motiveType family).liftN family.view.ordinal) := by + rw [show gen.motiveTypes = gen.motiveTypesAux gen.families 0 from rfl, + gen.motiveTypesAux_getElem?] + rw [gen.family_getElem?_ordinal hfamily] + simp + +/-- Paired block constructors retain the raw/view field arity certified by +normalization. -/ +theorem BlockGenerationChecked.flatCtor_fields_length + {source : VInductDecl} (gen : BlockGenerationChecked source) + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) : + (constructor.ctor.rawFields source.nparams).length = + constructor.ctor.view.fields.length := by + simp only [BlockGenerationChecked.flatCtors, + NormalizedCheckedBlock.flatCtors, List.mem_flatMap] at hconstructor + obtain ⟨family, hfamily, hconstructor⟩ := hconstructor + simp only [NormalizedFamily.blockCtors, List.mem_map] at hconstructor + obtain ⟨ctor, hctor, rfl⟩ := hconstructor + exact ((gen.shape.2.2.2.2 family hfamily).2.2.2.2.2.2 + ctor hctor).2.2.2 + +/-- Syntactic lifting law for any member of the mutual motive telescope. -/ +theorem BlockGenerationChecked.motiveType_liftN + {source : VInductDecl} (gen : BlockGenerationChecked source) + (family : NormalizedFamily) (n : Nat) : + (gen.motiveType family).liftN n = + VExpr.forallN (VExpr.liftTelN n (gen.idxTel family) 0) + (.forallE + (VExpr.appN + (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (n + (gen.idxTel family).length) + source.nparams ++ + VExpr.bvarRevRange 0 (gen.idxTel family).length)) + (.sort gen.motiveLevel)) := by + rw [show gen.motiveType family = + VExpr.forallN (gen.idxTel family) + (.forallE + (VExpr.appN + (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (gen.idxTel family).length source.nparams ++ + VExpr.bvarRevRange 0 (gen.idxTel family).length)) + (.sort gen.motiveLevel)) from rfl, + VExpr.liftN_forallN] + refine congrArg _ ?_ + show VExpr.forallE _ _ = VExpr.forallE _ _ + refine congr (congrArg _ ?_) rfl + rw [VExpr.liftN_appN, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (by omega), + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega)] + rfl + +/-- Apply a selected mutual motive variable to its target-family indices and +a typed major premise. -/ +theorem BlockGenerationChecked.motiveVarApp_hasType + {source : VInductDecl} (gen : BlockGenerationChecked source) + (family : NormalizedFamily) {env : VEnv} {l : VLevel} + {Γ : List VExpr} {K q : Nat} {idxs : List VExpr} {a : VExpr} + (hM : env.HasType gen.recUvars Γ (.bvar K) + ((gen.motiveType family).liftN (q + K + 1))) + (hidx : env.SpineWF gen.recUvars Γ + (VExpr.forallN + (VExpr.liftTelN (q + K + 1) (gen.idxTel family) 0) + (.sort l)) + idxs (.sort l)) + (hlen : idxs.length = (gen.idxTel family).length) + (ha : env.HasType gen.recUvars Γ a + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (q + K + 1) source.nparams ++ idxs))) : + env.HasType gen.recUvars Γ + (VExpr.appN (.bvar K) (idxs ++ [a])) + (.sort gen.motiveLevel) := by + rw [gen.motiveType_liftN family] at hM + have hshape := hidx.retarget + (by simpa only [VExpr.liftTelN_length] using hlen) + (.forallE + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + (q + K + 1 + (gen.idxTel family).length) + source.nparams ++ + VExpr.bvarRevRange 0 (gen.idxTel family).length)) + (.sort gen.motiveLevel)) + rw [VExpr.instRev_forallE_sort, VExpr.instRev_appN, + VExpr.instRev_closedN _ + (C := .const family.raw.name gen.sourceLevels) trivial, + List.map_append, + VExpr.map_instRev_bvarRevRange_ge _ _ _ (by rw [hlen]; omega), + show q + K + 1 + (gen.idxTel family).length - idxs.length = + q + K + 1 from by rw [hlen]; omega, + VExpr.bvarRevRange_congr' 0 hlen.symm, + VExpr.map_instRev_bvarRevRange] at hshape + rw [hlen] at hshape + have hApp := hshape.hasType_appN hM + rw [VExpr.appN_append] + exact HasType.app hApp (by simpa using ha) + +/-- A recursive raw field, after inserting the full mutual motive telescope +and the constructor-local binders, is the family application expected by its +generated induction hypothesis. -/ +theorem blockMinor_fieldType_of_eq + {source : VInductDecl} {familyName : Name} + {B : VExpr} {r₀ : RecArg} + (hB : B = VExpr.forallN r₀.binders + (VExpr.appN (.const familyName (VLevel.params source.uvars)) + (VExpr.bvarRevRange + (r₀.fieldIndex + r₀.binders.length) source.nparams ++ + r₀.indices))) + (d m p : Nat) (hj : r₀.fieldIndex < m) + (mode : ElimMode := .large) : + let ls := mode.sourceLevels source.uvars + let r := r₀.instL ls + ((B.instL ls).liftN d r.fieldIndex).liftN + (m - r.fieldIndex + p) = + VExpr.forallN + (BlockGenerationChecked.blockMinorBinders d m p r) + (VExpr.appN (.const familyName ls) + (VExpr.bvarRevRange + (m + p + r.binders.length + d) source.nparams ++ + r.indices.map fun e => + (e.liftN d (r.fieldIndex + r.binders.length)).liftN + (m - r.fieldIndex + p) r.binders.length)) := by + dsimp only + conv => lhs; rw [hB] + simp only [RecArg.instL, VExpr.instL_forallN, VExpr.instL_appN, + List.map_append, bvarRevRange_instL, + show (VExpr.const familyName (VLevel.params source.uvars)).instL + (mode.sourceLevels source.uvars) = + .const familyName (mode.sourceLevels source.uvars) from by + simp [VExpr.instL, ElimMode.sourceLevels, + VLevel.params_map_inst_params'], + VExpr.liftN_forallN, VExpr.liftN_appN] + simp only [List.length_map, VExpr.liftTelN_length, Nat.zero_add] + rw [bvarRevRange_liftN_ge _ _ _ _ (Nat.le_refl _), + bvarRevRange_liftN_ge _ _ _ _ (by omega), + show m - r₀.fieldIndex + p + + (d + (r₀.fieldIndex + r₀.binders.length)) = + m + p + r₀.binders.length + d from by omega] + simp only [List.map_map, + BlockGenerationChecked.blockMinorBinders] + apply congrArg (VExpr.forallN _) + apply congrArg (VExpr.appN + (.const familyName (mode.sourceLevels source.uvars))) + apply congrArg + (VExpr.bvarRevRange (m + p + r₀.binders.length + d) + source.nparams ++ ·) + apply List.map_congr_left + intro e _ + simp only [Function.comp_apply] + +/-- Raw mixed fields preserve their arity under recursor-universe +instantiation. -/ +theorem NormalizedCtor.fieldsR_length {source : VInductDecl} + (ctor : NormalizedCtor) {mode : ElimMode} : + (ctor.fieldsR source.uvars source.nparams mode).length = + (ctor.rawFields source.nparams).length := + List.length_map .. + +/-- Pointwise lookup through the raw mixed field universe transport. -/ +theorem NormalizedCtor.fieldsR_getElem? {source : VInductDecl} + {ctor : NormalizedCtor} {q : Nat} {mode : ElimMode} : + (ctor.fieldsR source.uvars source.nparams mode)[q]? = + (ctor.rawFields source.nparams)[q]?.map + (VExpr.instL (mode.sourceLevels source.uvars)) := + List.getElem?_map .. + +/-- Unpack one mixed recursive descriptor to the retained declaration-level +descriptor from the checked view. -/ +theorem NormalizedCtor.recArgsR_mem {source : VInductDecl} + {ctor : NormalizedCtor} {r : RecArg} {mode : ElimMode} + (hr : r ∈ ctor.recArgsR source.uvars mode) : + ∃ r₀, r₀ ∈ ctor.view.recursive ∧ + r = r₀.instL (mode.sourceLevels source.uvars) := by + obtain ⟨r₀, hr₀, rfl⟩ := List.mem_map.1 hr + exact ⟨r₀, hr₀, rfl⟩ + +theorem liftTelN_congr {a a' : Nat} (tel : List VExpr) (k : Nat) + (h : a = a') : + VExpr.liftTelN a tel k = VExpr.liftTelN a' tel k := h ▸ rfl + +theorem BlockGenerationChecked.blockIHsFromRecArgs_length (d m : Nat) : + ∀ (rs : List RecArg) (p : Nat), + (BlockGenerationChecked.blockIHsFromRecArgs d m rs p).length = rs.length + | [], _ => rfl + | _ :: rs, p => by + simp [BlockGenerationChecked.blockIHsFromRecArgs, + BlockGenerationChecked.blockIHsFromRecArgs_length d m rs (p + 1)] + +/-- Rule-context normal form of one mutual induction hypothesis. The +recursive result is routed to the motive selected by `targetType`. -/ +def BlockGenerationChecked.blockRuleIH + (d k m : Nat) (r : RecArg) : VExpr := + let n := r.binders.length + VExpr.forallN + (BlockGenerationChecked.blockRuleBinders (d+k) m r) + (VExpr.appN + (.bvar (d - 1 - r.targetType + k + m + n)) + ((r.indices.map fun e => + (e.liftN (d+k) (r.fieldIndex+n)).liftN + (m-r.fieldIndex) n) ++ + [VExpr.appN (.bvar (m-1-r.fieldIndex+n)) + (VExpr.bvarRevRange 0 n)])) + +/-- Rule-context induction hypotheses, weakened past their preceding +hypothesis binders. -/ +def BlockGenerationChecked.blockRuleIHs (d k m : Nat) : + List RecArg → Nat → List VExpr + | [], _ => [] + | r :: rs, p => + (BlockGenerationChecked.blockRuleIH d k m r).liftN p :: + BlockGenerationChecked.blockRuleIHs d k m rs (p+1) + +theorem BlockGenerationChecked.blockMinorBinders_shift + (d : Nat) (r : RecArg) (m p : Nat) : + BlockGenerationChecked.blockMinorBinders d m p r = + VExpr.liftTelN p + (BlockGenerationChecked.blockMinorBinders d m 0 r) 0 := by + simp only [BlockGenerationChecked.blockMinorBinders, Nat.add_zero] + rw [VExpr.liftTelN_liftTelN] + +/-- The shift parameter of a mutual minor IH is precisely weakening past +the preceding IH binders. -/ +theorem BlockGenerationChecked.blockMinorIH_shift + (d : Nat) (r : RecArg) (m p : Nat) (hj : r.fieldIndex < m) : + BlockGenerationChecked.blockMinorIH d m p r = + (BlockGenerationChecked.blockMinorIH d m 0 r).liftN p := by + simp only [BlockGenerationChecked.blockMinorIH, + VExpr.liftN_forallN] + rw [← BlockGenerationChecked.blockMinorBinders_shift d r m p] + rw [show + (BlockGenerationChecked.blockMinorBinders d m 0 r).length = + r.binders.length by + simp [BlockGenerationChecked.blockMinorBinders, + VExpr.liftTelN_length], + Nat.zero_add] + apply congrArg (VExpr.forallN _) + rw [VExpr.liftN_appN, List.map_append, List.map_map] + show VExpr.appN _ (_ ++ [_]) = VExpr.appN _ (_ ++ [_]) + congr 1 + · rw [show + (VExpr.bvar + (d - 1 - r.targetType + m + 0 + r.binders.length)).liftN + p r.binders.length = + .bvar (d - 1 - r.targetType + m + p + r.binders.length) from by + simp only [VExpr.liftN] + rw [liftVar_le (by omega)] + congr 1 + ac_rfl] + · congr 1 + · apply List.map_congr_left + intro e _ + simp only [Function.comp_apply, Nat.add_zero] + rw [VExpr.liftN'_liftN_hi] + · congr 1 + simp only [Nat.add_zero, VExpr.liftN_appN] + rw [show + (VExpr.bvar + (m - 1 - r.fieldIndex + r.binders.length)).liftN + p r.binders.length = + .bvar (m - 1 - r.fieldIndex + p + r.binders.length) from by + simp only [VExpr.liftN] + rw [liftVar_le (by omega)] + congr 1 + omega, + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega)] + +/-- Lifting a base mutual minor IH through all constructor minors yields the +rule-context normal form. -/ +theorem BlockGenerationChecked.blockMinorIH_zero_lift_ruleIH + (d : Nat) (r : RecArg) (m k : Nat) (hj : r.fieldIndex < m) : + (BlockGenerationChecked.blockMinorIH d m 0 r).liftN k m = + BlockGenerationChecked.blockRuleIH d k m r := by + simp only [BlockGenerationChecked.blockMinorIH, + BlockGenerationChecked.blockRuleIH, + BlockGenerationChecked.blockMinorBinders, + BlockGenerationChecked.blockRuleBinders, + VExpr.liftN_forallN, VExpr.liftTelN_length, Nat.add_zero] + congr 1 + · rw [show m = r.fieldIndex + (m-r.fieldIndex) by omega] + rw [show r.fieldIndex + (m-r.fieldIndex) - r.fieldIndex = + m-r.fieldIndex by omega] + rw [VExpr.liftTelN_liftN_midN r.binders d k + (m-r.fieldIndex) (Nat.zero_le _)] + · congr 1 + rw [VExpr.liftN_appN, List.map_append, List.map_map] + show VExpr.appN _ (_ ++ [_]) = VExpr.appN _ (_ ++ [_]) + congr 1 + · simp only [VExpr.liftN] + rw [liftVar_le (by omega)] + congr 1 + ac_rfl + · congr 1 + · apply List.map_congr_left + intro e _ + simp only [Function.comp_apply] + rw [show m + r.binders.length = + (r.fieldIndex+r.binders.length) + (m-r.fieldIndex) by omega, + VExpr.liftN_liftN_midN e d k (m-r.fieldIndex) (by omega)] + · congr 1 + simp only [VExpr.liftN_appN] + rw [show + (VExpr.bvar + (m - 1 - r.fieldIndex + r.binders.length)).liftN + k (m+r.binders.length) = + .bvar (m - 1 - r.fieldIndex + r.binders.length) from by + simp only [VExpr.liftN] + rw [liftVar_lt (by omega)], + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega)] + +/-- General mutual minor-IH normalization after `p` earlier hypotheses. -/ +theorem BlockGenerationChecked.blockMinorIH_lift_ruleIH + (d : Nat) (r : RecArg) (m k p : Nat) + (hj : r.fieldIndex < m) : + (BlockGenerationChecked.blockMinorIH d m p r).liftN k (m+p) = + (BlockGenerationChecked.blockRuleIH d k m r).liftN p := by + rw [BlockGenerationChecked.blockMinorIH_shift d r m p hj] + rw [← VExpr.liftN_liftN_comm + (BlockGenerationChecked.blockMinorIH d m 0 r) + p k 0 m (Nat.zero_le _)] + rw [BlockGenerationChecked.blockMinorIH_zero_lift_ruleIH d r m k hj] + +theorem BlockGenerationChecked.blockRuleIHs_length (d k m : Nat) : + ∀ (rs : List RecArg) (p : Nat), + (BlockGenerationChecked.blockRuleIHs d k m rs p).length = rs.length + | [], _ => rfl + | _ :: rs, p => by + simp [BlockGenerationChecked.blockRuleIHs, + BlockGenerationChecked.blockRuleIHs_length d k m rs (p+1)] + +/-- Lift the complete mutual minor-IH telescope into rule context. -/ +theorem BlockGenerationChecked.blockIHs_liftN (d m k : Nat) : + ∀ (rs : List RecArg), + (∀ r ∈ rs, r.fieldIndex < m) → ∀ (p : Nat) (X : VExpr), + (VExpr.forallN + (BlockGenerationChecked.blockIHsFromRecArgs d m rs p) X).liftN + k (m+p) = + VExpr.forallN + (BlockGenerationChecked.blockRuleIHs d k m rs p) + (X.liftN k (m+p+rs.length)) + | [], _, _, _ => rfl + | r :: rs, hm, p, X => by + show VExpr.forallE _ _ = VExpr.forallE _ _ + congr 1 + · exact BlockGenerationChecked.blockMinorIH_lift_ruleIH + d r m k p (hm r (.head _)) + · rw [show m+p+1 = m+(p+1) by omega, + BlockGenerationChecked.blockIHs_liftN d m k rs + (fun q hq => hm q (.tail _ hq)) (p+1) X, + show m+(p+1)+rs.length = + m+p+(r :: rs).length from by simp; omega] + +theorem BlockGenerationChecked.blockIHs_liftN' + (d m k : Nat) (rs : List RecArg) + (hm : ∀ r ∈ rs, r.fieldIndex < m) + (p : Nat) (X : VExpr) {cut : Nat} (hcut : cut = m+p) : + (VExpr.forallN + (BlockGenerationChecked.blockIHsFromRecArgs d m rs p) X).liftN + k cut = + VExpr.forallN + (BlockGenerationChecked.blockRuleIHs d k m rs p) + (X.liftN k (m+p+rs.length)) := by + rw [hcut] + exact BlockGenerationChecked.blockIHs_liftN d m k rs hm p X + +theorem BlockGenerationChecked.blockRuleIHs_liftN1 (d k m : Nat) : + ∀ (rs : List RecArg) (p c : Nat), c ≤ p → ∀ (X : VExpr), + VExpr.forallN + (BlockGenerationChecked.blockRuleIHs d k m rs (p+1)) + (X.liftN 1 (c+rs.length)) = + (VExpr.forallN + (BlockGenerationChecked.blockRuleIHs d k m rs p) X).liftN 1 c + | [], p, c, _, X => by + simp [BlockGenerationChecked.blockRuleIHs, VExpr.forallN] + | r :: rs, p, c, hc, X => by + show VExpr.forallE _ _ = VExpr.forallE _ _ + congr 1 + · rw [VExpr.liftN'_liftN' (Nat.zero_le _) hc] + · rw [show c+(r :: rs).length = (c+1)+rs.length from by + simp + omega] + exact BlockGenerationChecked.blockRuleIHs_liftN1 + d k m rs (p+1) (c+1) (by omega) X + +/-- Consume one routed recursive-call term for every mutual rule IH. -/ +theorem hasType_appN_blockRuleIHs + {source : VInductDecl} {gen : BlockGenerationChecked source} + {env : VEnv} {Γ : List VExpr} {d m k : Nat} + {argOf : RecArg → VExpr} {Dfin : VExpr} : + ∀ {rs : List RecArg} {g : VExpr}, + (∀ r ∈ rs, env.HasType gen.recUvars Γ (argOf r) + (BlockGenerationChecked.blockRuleIH d k m r)) → + env.HasType gen.recUvars Γ g + (VExpr.forallN + (BlockGenerationChecked.blockRuleIHs d k m rs 0) + (Dfin.liftN rs.length)) → + env.HasType gen.recUvars Γ (g.appN (rs.map argOf)) Dfin + | [], g, _, hg => by + simpa [BlockGenerationChecked.blockRuleIHs] using hg + | r :: rs, g, hargs, hg => by + have happ := VEnv.HasType.app hg (by + simpa [BlockGenerationChecked.blockRuleIHs] using + hargs r (.head _)) + simp only [List.length_cons] at happ + rw [show Dfin.liftN (rs.length+1) = + (Dfin.liftN rs.length).liftN 1 (0+rs.length) from by + rw [Nat.zero_add, + VExpr.liftN'_liftN' (Nat.zero_le _) (by omega)], + BlockGenerationChecked.blockRuleIHs_liftN1 + d k m rs 0 0 (Nat.le_refl _) (Dfin.liftN rs.length), + VExpr.inst_liftN1] at happ + exact hasType_appN_blockRuleIHs + (rs := rs) (fun q hq => hargs q (.tail _ hq)) happ + +theorem BlockGenerationChecked.minorTypesAux_length + {source : VInductDecl} (gen : BlockGenerationChecked source) : + ∀ (constructors : List NormalizedBlockCtor) (i : Nat), + (gen.minorTypesAux constructors i).length = constructors.length + | [], _ => rfl + | _ :: constructors, i => by + simp [BlockGenerationChecked.minorTypesAux, + gen.minorTypesAux_length constructors (i + 1)] + +theorem BlockGenerationChecked.minorTypes_length + {source : VInductDecl} (gen : BlockGenerationChecked source) : + gen.minorTypes.length = gen.minorCount := by + simpa [BlockGenerationChecked.minorTypes] using + gen.minorTypesAux_length gen.flatCtors 0 + +/-- Positional lookup through the progressively weakened flattened mutual +minor telescope. -/ +theorem BlockGenerationChecked.minorTypesAux_getElem? + {source : VInductDecl} (gen : BlockGenerationChecked source) : + ∀ (constructors : List NormalizedBlockCtor) (i q : Nat), + (gen.minorTypesAux constructors i)[q]? = + constructors[q]?.map fun constructor => + (gen.minorType constructor).liftN (i+q) + | [], _, q => by simp [BlockGenerationChecked.minorTypesAux] + | _ :: _, _, 0 => by simp [BlockGenerationChecked.minorTypesAux] + | _ :: constructors, i, q+1 => by + simp only [BlockGenerationChecked.minorTypesAux, + List.getElem?_cons_succ] + rw [gen.minorTypesAux_getElem? constructors (i+1) q, + show i+1+q = i+(q+1) by omega] + +namespace BlockGenerationEnv + +variable {source : VInductDecl} {gen : BlockGenerationChecked source} + {env : VEnv} (S : BlockGenerationEnv gen env) +include S + +theorem paramsTel_onTel : + env.OnTel gen.recUvars [] gen.paramsTel := by + have h := S.generationParams_defeq.raw_onTel.instL + (U' := gen.recUvars) gen.sourceLevels_wf + simpa [BlockGenerationChecked.paramsTel] using h + +theorem motiveTypesAux_onTel + (families : List NormalizedFamily) + (hsub : ∀ family ∈ families, family ∈ gen.families) + (Δ : List VExpr) (i : Nat) (hΔ : Δ.length = i) : + env.OnTel gen.recUvars (Δ ++ gen.paramsTel.reverse) + (gen.motiveTypesAux families i) := by + induction families generalizing Δ i with + | nil => trivial + | cons family families ih => + exact ⟨by + rw [← hΔ] + exact (S.motive_isType (hsub family (.head _))).weakN S.ord + (.zero Δ), + ih (fun family hfamily => hsub family (.tail _ hfamily)) + (_ :: Δ) (i + 1) (by simp [hΔ])⟩ + +theorem motiveTypes_onTel : + env.OnTel gen.recUvars gen.paramsTel.reverse gen.motiveTypes := by + simpa [BlockGenerationChecked.motiveTypes] using + motiveTypesAux_onTel S gen.families (fun _ h => h) [] 0 rfl + +/-- Constructor fields instantiated for recursor generation are well typed +over the generation parameter telescope. -/ +theorem generationFields_onTel_rec + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) : + env.OnTel gen.recUvars gen.paramsTel.reverse + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination) := by + have hemitted := (S.ctorWF constructor hconstructor).rawEmitted_onTel + have hfields₀ := (VEnv.OnTel.of_append + (As := gen.block.checked.params) hemitted).2 + have hfields₁ := hfields₀.instL + (U' := gen.recUvars) gen.sourceLevels_wf + have hfieldsChecked : env.OnTel gen.recUvars + (gen.block.checked.params.map + (VExpr.instL gen.sourceLevels)).reverse + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination) := by + simpa [NormalizedCtor.fieldsR, List.map_reverse] using hfields₁ + exact hfieldsChecked.defeqDFC S.ord + (S.generationParams_ctx_rec.symm S.ord) + +/-- The checked and generation parameter contexts stay definitionally equal +beneath every instantiated constructor-field prefix. -/ +theorem generationFieldPrefix_ctx_rec + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) (j : Nat) : + env.IsDefEqCtx gen.recUvars [] + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination |>.take j).reverse ++ gen.paramsTel.reverse) + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination |>.take j).reverse ++ + (gen.block.checked.params.map + (VExpr.instL gen.sourceLevels)).reverse) := by + have hfields := S.generationFields_onTel_rec hconstructor + rw [← List.take_append_drop j + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination)] at hfields + exact (hfields.of_append.1).extendDefEqCtx + S.generationParams_ctx_rec + +/-- Definitionally equal raw/view contexts at any mutual constructor-field +prefix. -/ +theorem emittedPrefix_ctx {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) (j : Nat) : + env.IsDefEqCtx source.uvars [] + ((constructor.ctor.rawFields source.nparams |>.take j).reverse ++ + gen.block.checked.params.reverse) + ((constructor.ctor.view.fields.take j).reverse ++ + gen.block.checked.params.reverse) := by + have h := ((S.ctorWF constructor hconstructor).emittedTel.take + (source.nparams + j)).ctx + have hviewLen : + gen.block.checked.params.length = source.nparams := + gen.shape.2.1.symm.trans gen.shape.1 + have hraw : + (gen.block.checked.params ++ + constructor.ctor.rawFields source.nparams).take + (source.nparams + j) = + gen.block.checked.params ++ + (constructor.ctor.rawFields source.nparams).take j := by + rw [← hviewLen] + rw [List.take_append, List.take_of_length_le (by omega)] + simp + have hview : + (gen.block.checked.params ++ constructor.ctor.view.fields).take + (source.nparams + j) = + gen.block.checked.params ++ + constructor.ctor.view.fields.take j := by + rw [← hviewLen] + rw [List.take_append, List.take_of_length_le (by omega)] + simp + simp only [NormalizedBlockCtor.emittedBinders, + NormalizedBlockCtor.viewBinders] at h + rw [hraw, hview] at h + simpa [List.reverse_append] using h + +/-- Pointwise raw/view field-domain equality for a mutual constructor, in +the preceding raw context. -/ +theorem emittedField_defeq {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) + {j : Nat} {B B' : VExpr} + (hB : (constructor.ctor.rawFields source.nparams)[j]? = some B) + (hB' : constructor.ctor.view.fields[j]? = some B') : + ∃ u, env.IsDefEq source.uvars + ((constructor.ctor.rawFields source.nparams |>.take j).reverse ++ + gen.block.checked.params.reverse) + B B' (.sort u) := by + have hviewLen : + gen.block.checked.params.length = source.nparams := + gen.shape.2.1.symm.trans gen.shape.1 + have hraw : + getElem? + (gen.block.checked.params ++ + constructor.ctor.rawFields source.nparams) + (source.nparams + j) = some B := by + rw [List.getElem?_append_right (by rw [hviewLen]; omega), hviewLen] + simpa using hB + have hview : + getElem? + (gen.block.checked.params ++ constructor.ctor.view.fields) + (source.nparams + j) = some B' := by + rw [List.getElem?_append_right (by rw [hviewLen]; omega), hviewLen] + simpa using hB' + obtain ⟨u, h⟩ := + (S.ctorWF constructor hconstructor).emittedTel.getElem? hraw hview + have htake : + (gen.block.checked.params ++ + constructor.ctor.rawFields source.nparams).take + (source.nparams + j) = + gen.block.checked.params ++ + (constructor.ctor.rawFields source.nparams).take j := by + rw [← hviewLen] + rw [List.take_append, List.take_of_length_le (by omega)] + simp + simp only [NormalizedBlockCtor.emittedBinders] at h + rw [htake, List.reverse_append] at h + exact ⟨u, by simpa using h⟩ + +/-- The generated and checked index telescopes of every family are +definitionally equal after universe instantiation. -/ +theorem familyIndexTel_defeq_rec {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + env.TelDefEq gen.recUvars gen.paramsTel.reverse + (gen.idxTel family) + (family.view.indices.map (VExpr.instL gen.sourceLevels)) := by + have h := (S.familyWF family hfamily).familyTel.drop source.nparams + have hrawTake : + (family.rawParams source.nparams ++ + family.rawIndices source.nparams).take source.nparams = + family.rawParams source.nparams := by + let Ps := family.rawParams source.nparams + let Is := family.rawIndices source.nparams + have hlen : Ps.length = source.nparams := + (gen.shape.2.2.2.2 family hfamily).2.2.1 + change (Ps ++ Is).take source.nparams = Ps + rw [← hlen, List.take_append, List.take_length] + simp + have hrawDrop : + (family.rawParams source.nparams ++ + family.rawIndices source.nparams).drop source.nparams = + family.rawIndices source.nparams := by + let Ps := family.rawParams source.nparams + let Is := family.rawIndices source.nparams + have hlen : Ps.length = source.nparams := + (gen.shape.2.2.2.2 family hfamily).2.2.1 + change (Ps ++ Is).drop source.nparams = Is + rw [← hlen, List.drop_append] + simp + have hviewLen : gen.block.checked.params.length = source.nparams := + gen.shape.2.1.symm.trans gen.shape.1 + have hviewDrop : + (gen.block.checked.params ++ family.view.indices).drop + source.nparams = family.view.indices := by + rw [← hviewLen, List.drop_append] + simp + rw [hrawTake, hrawDrop, hviewDrop] at h + simp only [List.append_nil] at h + have hparams : env.IsDefEqCtx source.uvars [] + (family.rawParams source.nparams).reverse + gen.block.checked.params.reverse := by + simpa using (S.rawParams_defeq hfamily).ctx + have hemitted := h.defeqDFC S.ord hparams + have hgeneration := hemitted.defeqDFC S.ord + (S.generationParams_ctx.symm S.ord) + have hrec := hgeneration.instL + (U' := gen.recUvars) gen.sourceLevels_wf + simpa [BlockGenerationChecked.paramsTel, + BlockGenerationChecked.idxTel, List.map_reverse] using hrec + +/-- Transport the analyzer's recursive-argument certificate from checked +source syntax to the generated field and target-family index telescopes, then +weaken it beneath arbitrary motive/local prefixes. -/ +theorem recArg_transport + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) + {r₀ : RecArg} {family : NormalizedFamily} + (hfamily : family ∈ gen.families) + (hsem : r₀.WF source.uvars env gen.validated.resultLevel + family.view.indices + ((constructor.ctor.view.fields.take r₀.fieldIndex).reverse ++ + gen.block.checked.params.reverse)) + (hjlt : r₀.fieldIndex < constructor.ctor.view.fields.length) + (mid : List VExpr) {g : Nat} (hg : mid.length = g) + (As₂ : List VExpr) {d : Nat} (hd : As₂.length = d) : + let r := r₀.instL gen.sourceLevels + let As := VExpr.liftTelN d + (VExpr.liftTelN g r.binders r.fieldIndex) 0 + env.OnTel gen.recUvars + (As₂ ++ ((VExpr.liftTelN g + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).take r.fieldIndex) 0).reverse ++ + (mid ++ gen.paramsTel.reverse))) As ∧ + env.SpineWF gen.recUvars + (As.reverse ++ + (As₂ ++ ((VExpr.liftTelN g + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).take r.fieldIndex) 0).reverse ++ + (mid ++ gen.paramsTel.reverse)))) + (VExpr.forallN + (VExpr.liftTelN + (r.fieldIndex + r.binders.length + g + d) + (gen.idxTel family) 0) + (.sort (gen.validated.resultLevel.inst gen.sourceLevels))) + (r.indices.map fun e => + (e.liftN g (r.fieldIndex + r.binders.length)).liftN d + r.binders.length) + (.sort (gen.validated.resultLevel.inst gen.sourceLevels)) := by + dsimp only + let ls := gen.sourceLevels + have hjraw : + r₀.fieldIndex < + (constructor.ctor.rawFields source.nparams).length := by + rw [gen.flatCtor_fields_length hconstructor] + exact hjlt + have hraw := hsem.defeqDFC S.ord + ((S.emittedPrefix_ctx hconstructor r₀.fieldIndex).symm S.ord) + have htel₁ := hraw.1.instL + (U' := gen.recUvars) gen.sourceLevels_wf + have hsp₁ := hraw.2.instL + (U' := gen.recUvars) gen.sourceLevels_wf + have htelChecked : env.OnTel gen.recUvars + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination |>.take r₀.fieldIndex).reverse ++ + (gen.block.checked.params.map (VExpr.instL ls)).reverse) + (r₀.binders.map (VExpr.instL ls)) := by + simpa [NormalizedCtor.fieldsR, List.map_append, + List.map_reverse, List.map_take] using htel₁ + have hspChecked : env.SpineWF gen.recUvars + ((r₀.binders.map (VExpr.instL ls)).reverse ++ + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination |>.take r₀.fieldIndex).reverse ++ + (gen.block.checked.params.map (VExpr.instL ls)).reverse)) + (VExpr.instL ls + (VExpr.forallN + (VExpr.liftTelN + (r₀.fieldIndex + r₀.binders.length) + family.view.indices 0) + (.sort gen.validated.resultLevel))) + (r₀.indices.map (VExpr.instL ls)) + (VExpr.instL ls (.sort gen.validated.resultLevel)) := by + simpa [List.map_append, List.map_reverse, + NormalizedCtor.fieldsR, List.map_take] using hsp₁ + have hprefix := + S.generationFieldPrefix_ctx_rec hconstructor r₀.fieldIndex + have htelGeneration := htelChecked.defeqDFC S.ord + (hprefix.symm S.ord) + have hfull := htelGeneration.extendDefEqCtx hprefix + have hspGeneration := hspChecked.defeqDFC S.ord (hfull.symm S.ord) + simp only [VExpr.instL_forallN, + VExpr.liftTelN_instL] at htelGeneration hspGeneration + have hjlen : + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).take r₀.fieldIndex).length = r₀.fieldIndex := by + simp only [NormalizedCtor.fieldsR, List.length_take, + List.length_map] + omega + have hidxField := (S.familyIndexTel_defeq_rec hfamily).weakN S.ord + (Ctx.LiftN.zero (Γ := gen.paramsTel.reverse) + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).take r₀.fieldIndex).reverse) + rw [List.length_reverse, hjlen] at hidxField + have hidxPrivate := hidxField.weakN S.ord + (Ctx.LiftN.zero + (Γ := ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).take r₀.fieldIndex).reverse ++ + gen.paramsTel.reverse) + (r₀.binders.map (VExpr.instL ls)).reverse) + simp only [List.length_reverse, List.length_map] at hidxPrivate + rw [VExpr.liftTelN_liftTelN, + VExpr.liftTelN_liftTelN] at hidxPrivate + have hidxLenView := hsem.2.forallN_sort_length + simp only [VExpr.liftTelN_length] at hidxLenView + have hidxLen : + (r₀.indices.map (VExpr.instL ls)).length = + (VExpr.liftTelN + (r₀.fieldIndex + r₀.binders.length) + (gen.idxTel family) 0).length := by + simp only [List.length_map, VExpr.liftTelN_length, + BlockGenerationChecked.idxTel] + exact hidxLenView.trans + (gen.shape.2.2.2.2 family hfamily).2.2.2.1.symm + have hspRaw := + hidxPrivate.spine_sort S.ord hspGeneration hidxLen + have W₁ := Ctx.LiftN.consTel (n := mid.length) + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).take r₀.fieldIndex) + (Ctx.LiftN.zero (Γ := gen.paramsTel.reverse) mid) + rw [hjlen, Nat.add_zero] at W₁ + have htel₂ := htelGeneration.weakN S.ord W₁ + have hsp₂ := hspRaw.weakN S.ord + (Ctx.LiftN.consTel + (r₀.binders.map (VExpr.instL ls)) W₁) + rw [hg] at htel₂ hsp₂ + have W₂ := Ctx.LiftN.zero + (Γ := (VExpr.liftTelN g + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).take r₀.fieldIndex) 0).reverse ++ + (mid ++ gen.paramsTel.reverse)) As₂ (h := hd) + have htel₃ := htel₂.weakN S.ord W₂ + have hsp₃ := hsp₂.weakN S.ord + (Ctx.LiftN.consTel + (VExpr.liftTelN g + (r₀.binders.map (VExpr.instL ls)) r₀.fieldIndex) W₂) + refine ⟨?_, ?_⟩ + · simpa [ls, RecArg.instL, List.append_assoc] using htel₃ + · simp only [List.length_map, VExpr.liftTelN_length, + Nat.add_zero] at hsp₃ + rw [VExpr.liftN_forallN, VExpr.liftN_forallN, + VExpr.liftTelN_liftTelN_hi' + (r₀.fieldIndex + r₀.binders.length) g _ 0 (by omega), + VExpr.liftTelN_liftTelN_mid + (r₀.fieldIndex + r₀.binders.length + g) d _ 0 + r₀.binders.length (Nat.zero_le _) (by omega)] at hsp₃ + rw [show r₀.binders.length + r₀.fieldIndex = + r₀.fieldIndex + r₀.binders.length from Nat.add_comm _ _] at hsp₃ + simpa [ls, RecArg.instL, VExpr.instL, VExpr.liftN, + List.map_map, Function.comp_def, List.append_assoc] using hsp₃ + +theorem recArgMinor_isType {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) {r : RecArg} + (hrmem : r ∈ constructor.ctor.recArgsR source.uvars gen.elimination) + (Δ : List VExpr) (p : Nat) (hΔ : Δ.length = p) : + env.IsType gen.recUvars + (Δ ++ + (VExpr.liftTelN gen.familyCount + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination) 0).reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse)) + (BlockGenerationChecked.blockMinorIH gen.familyCount + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length p r) := by + obtain ⟨r₀, hr₀, rfl⟩ := + NormalizedCtor.recArgsR_mem hrmem + obtain ⟨family, hfamily, hord, ⟨Bview, hBview, hshape⟩, hsem⟩ := + (S.ctorWF constructor hconstructor).recursive r₀ hr₀ + let ls := gen.sourceLevels + let r := r₀.instL ls + let d := gen.familyCount + let Bs := constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination + let m := Bs.length + let j := r₀.fieldIndex + let Fs := VExpr.liftTelN d Bs 0 + let As := BlockGenerationChecked.blockMinorBinders d m p r + let idxs := r.indices.map fun e => + (e.liftN d (r.fieldIndex + r.binders.length)).liftN + (m - r.fieldIndex + p) r.binders.length + let Γ := Δ ++ Fs.reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse) + let q := family.view.ordinal + let K := d - 1 - r.targetType + m + p + r.binders.length + have hjview : r₀.fieldIndex < constructor.ctor.view.fields.length := + (List.getElem?_eq_some_iff.1 hBview).1 + have hjm : j < m := by + have hfields := gen.flatCtor_fields_length hconstructor + simp only [j, m, Bs, NormalizedCtor.fieldsR_length] + omega + have hjraw : + r₀.fieldIndex < + (constructor.ctor.rawFields source.nparams).length := by + simpa [j, m, Bs, NormalizedCtor.fieldsR_length] using hjm + let Braw := + (constructor.ctor.rawFields source.nparams)[r₀.fieldIndex] + have hBraw : + (constructor.ctor.rawFields source.nparams)[r₀.fieldIndex]? = + some Braw := + List.getElem?_eq_getElem hjraw + have hd : gen.motiveTypes.reverse.length = d := by + simp [d, gen.motiveTypes_length] + have hd' : gen.motiveTypes.length = d := by + simpa using hd + have hFsLen : Fs.length = m := by + simp [Fs, m, VExpr.liftTelN_length] + have hstackLen : + (Δ ++ (Fs.drop j).reverse).length = m - j + p := by + simp only [List.length_append, List.length_reverse, + List.length_drop, hFsLen, hΔ] + omega + have ht := S.recArg_transport hconstructor hfamily hsem hjview + gen.motiveTypes.reverse hd + (Δ ++ (Fs.drop j).reverse) hstackLen + simp only [RecArg.instL] at ht + have hctx : + (Δ ++ (Fs.drop j).reverse) ++ + ((VExpr.liftTelN d (Bs.take j) 0).reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse)) = Γ := by + dsimp only [Γ, Fs] + rw [← VExpr.liftTelN_take, List.append_assoc, + ← List.append_assoc + ((VExpr.liftTelN d Bs 0).drop j).reverse, + ← List.reverse_append, List.take_append_drop, + ← List.append_assoc] + dsimp only [j] at ht hctx + have htel : env.OnTel gen.recUvars Γ As := by + rw [hctx] at ht + simpa [r, As, m, j, Bs, RecArg.instL, + BlockGenerationChecked.blockMinorBinders] using ht.1 + have hsp : env.SpineWF gen.recUvars + (As.reverse ++ Γ) + (VExpr.forallN + (VExpr.liftTelN + (m + p + r.binders.length + d) (gen.idxTel family) 0) + (.sort (gen.validated.resultLevel.inst ls))) + idxs + (.sort (gen.validated.resultLevel.inst ls)) := by + rw [hctx] at ht + simpa [r, As, idxs, m, j, Bs, d, ls, RecArg.instL, + BlockGenerationChecked.blockMinorBinders, + List.append_assoc, + show j + r₀.binders.length + d + (m - j + p) = + m + p + r₀.binders.length + d from by omega] using ht.2 + have hF : Γ[m - 1 - j + p]? = + some ((Braw.instL ls).liftN d j) := by + dsimp only [Γ, Fs] + rw [getElem?_stack_mid Δ + (VExpr.liftTelN d Bs 0).reverse + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse) + (i := m - 1 - j + p) (by rw [hΔ]; omega) + (by simp only [hΔ, List.length_reverse, + VExpr.liftTelN_length]; omega), + show m - 1 - j + p - Δ.length = m - 1 - j from by + rw [hΔ] + omega, + List.getElem?_reverse (by rw [hFsLen]; omega), + VExpr.liftTelN_length, + show m - 1 - (m - 1 - j) = j from by omega, + VExpr.liftTelN_getElem?, + NormalizedCtor.fieldsR_getElem?, hBraw] + simp [ls] + have hlu := Lookup.of_getElem? hF + rw [show m - 1 - j + p + 1 = m - j + p from by omega] at hlu + dsimp only [j, r] at hlu + have hf0 := VEnv.HasType.bvar + (env := env) (U := gen.recUvars) hlu + obtain ⟨u, hdom₀⟩ := + S.emittedField_defeq hconstructor hBraw hBview + have hdom₁ := hdom₀.instL + (U' := gen.recUvars) gen.sourceLevels_wf + have hdomChecked : env.IsDefEq gen.recUvars + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination |>.take r₀.fieldIndex).reverse ++ + (gen.block.checked.params.map (VExpr.instL ls)).reverse) + (Braw.instL ls) (Bview.instL ls) ((VExpr.sort u).instL ls) := by + simpa [NormalizedCtor.fieldsR, List.map_append, + List.map_reverse, List.map_take] using hdom₁ + have hprefix := + S.generationFieldPrefix_ctx_rec hconstructor r₀.fieldIndex + have hdomGeneration := hdomChecked.defeqDFC S.ord + (hprefix.symm S.ord) + have hjlen : (Bs.take r₀.fieldIndex).length = + r₀.fieldIndex := by + simp only [Bs, NormalizedCtor.fieldsR, + List.length_take, List.length_map] + omega + have Wmid := Ctx.LiftN.consTel (n := d) + (Bs.take r₀.fieldIndex) + (Ctx.LiftN.zero (n := d) (Γ := gen.paramsTel.reverse) + gen.motiveTypes.reverse (h := hd)) + rw [hjlen, Nat.add_zero] at Wmid + have hdom₂ := hdomGeneration.weakN S.ord Wmid + have Wstack := Ctx.LiftN.zero + (Γ := (VExpr.liftTelN d (Bs.take j) 0).reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse)) + (Δ ++ (Fs.drop j).reverse) (h := hstackLen) + have hdom₃ := hdom₂.weakN S.ord Wstack + rw [hctx] at hdom₃ + have hfView := hdom₃.defeq hf0 + have hfield := blockMinor_fieldType_of_eq hshape d m p + (by simpa [j] using hjm) gen.elimination + simp only [RecArg.instL] at hfield + rw [hfield] at hfView + have hf := hfView.weakN S.ord + (Ctx.LiftN.zero (Γ := Γ) As.reverse) + have hmajor := VEnv.HasType.appN_selfSpine + (env := env) (U := gen.recUvars) + (As := As) + (B := VExpr.appN + (.const family.raw.name ls) + (VExpr.bvarRevRange + (m + p + r.binders.length + d) source.nparams ++ idxs)) + (Δ := []) (Γ := Γ) (by + simpa [As, r, idxs, j, d, ls, RecArg.instL, + List.length_reverse, List.map_map, + Function.comp_def] using hf) + simp only [List.length_nil, VExpr.liftN_zero, + List.nil_append] at hmajor + have hAsLen : As.length = r.binders.length := by + simp [As, BlockGenerationChecked.blockMinorBinders, + VExpr.liftTelN_length] + have hmajor' : env.HasType gen.recUvars (As.reverse ++ Γ) + ((VExpr.bvar (m - 1 - r.fieldIndex + p + As.length)).appN + (VExpr.bvarRevRange 0 As.length)) + (VExpr.appN + (.const family.raw.name ls) + (VExpr.bvarRevRange + (m + p + r.binders.length + d) source.nparams ++ idxs)) := by + simpa [As, d, r, RecArg.instL, + BlockGenerationChecked.blockMinorBinders, + VExpr.liftN, liftVar_le, Nat.add_comm] using hmajor + rw [hAsLen] at hmajor' + have hq : q < d := by + simpa [q, d] using gen.family_ordinal_lt hfamily + have hmot : gen.motiveTypes.reverse[d - 1 - q]? = + some ((gen.motiveType family).liftN q) := by + rw [List.getElem?_reverse (by rw [hd']; omega), + show gen.motiveTypes.length - 1 - (d - 1 - q) = q from by + rw [hd'] + omega, + gen.motiveTypes_getElem?_ordinal hfamily] + have hM0 := getElem?_rstack_mid + (As.reverse ++ (Δ ++ Fs.reverse)) + gen.motiveTypes.reverse gen.paramsTel.reverse + (i := K) + (by + simp only [List.length_append, List.length_reverse, + hAsLen, hΔ, hFsLen] + dsimp only [K, q, d, r] + simp only [RecArg.instL] + omega) + (by + simp only [List.length_append, List.length_reverse, + hAsLen, hΔ, hFsLen, hd] + dsimp only [K, q, d, r] + simp only [RecArg.instL] + omega) + have hdiff : + K - (As.reverse ++ (Δ ++ Fs.reverse)).length = d - 1 - q := by + simp only [List.length_append, List.length_reverse, + hAsLen, hΔ, hFsLen] + dsimp only [K, q, d, r] + simp only [RecArg.instL] + omega + rw [hdiff, hmot] at hM0 + have hMget : (As.reverse ++ Γ)[K]? = + some ((gen.motiveType family).liftN q) := by + simpa [Γ, List.append_assoc] using hM0 + have hMraw := VEnv.HasType.bvar + (env := env) (U := gen.recUvars) + (Lookup.of_getElem? hMget) + have hM : env.HasType gen.recUvars (As.reverse ++ Γ) (.bvar K) + ((gen.motiveType family).liftN (q + K + 1)) := by + simpa [VExpr.liftN_liftN, Nat.add_assoc] using hMraw + have hlen : idxs.length = (gen.idxTel family).length := by + simp only [idxs, List.length_map, + BlockGenerationChecked.idxTel] + have hidx := hsem.2.forallN_sort_length + simp only [VExpr.liftTelN_length] at hidx + simpa [r, RecArg.instL] using hidx.trans + (gen.shape.2.2.2.2 family hfamily).2.2.2.1.symm + have hshift : q + K + 1 = m + p + r.binders.length + d := by + dsimp only [q, K, d, r] + simp only [RecArg.instL] + omega + have hbody := gen.motiveVarApp_hasType family + (q := q) (K := K) + hM (by simpa [hshift] using hsp) hlen + (by simpa [hshift] using hmajor') + refine VEnv.IsType.forallN htel ⟨gen.motiveLevel, ?_⟩ + simpa [BlockGenerationChecked.blockMinorIH, + r, As, idxs, m, d, K, q, Γ, Fs, Bs, + List.append_assoc] using hbody + +/-- The global mutual-IH telescope for one constructor is well formed at +every recursive suffix. -/ +theorem ihs_onTel {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) : + ∀ (rsSuf : List RecArg), + (∀ r ∈ rsSuf, + r ∈ constructor.ctor.recArgsR source.uvars gen.elimination) → + ∀ (Δ : List VExpr) (p : Nat), Δ.length = p → + env.OnTel gen.recUvars + (Δ ++ + (VExpr.liftTelN gen.familyCount + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination) 0).reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse)) + (BlockGenerationChecked.blockIHsFromRecArgs gen.familyCount + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length rsSuf p) + | [], _, _, _, _ => trivial + | r :: rsSuf, hqs, Δ, p, hΔ => + ⟨S.recArgMinor_isType hconstructor + (hqs r (.head _)) Δ p hΔ, + BlockGenerationEnv.ihs_onTel hconstructor rsSuf + (fun q hq => hqs q (.tail _ hq)) + (_ :: Δ) (p + 1) (by simp [hΔ])⟩ + +theorem viewResultIndices_length + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) : + constructor.ctor.view.resultIndices.length = + constructor.familyIndices.length := by + have h := (S.ctorWF constructor hconstructor).resultSpine.forallN_sort_length + simpa only [VExpr.liftTelN_length] using h + +/-- Transport one mutual constructor's checked result spine to the selected +raw family-index telescope and through arbitrary middle/top binders. -/ +theorem result_transport + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) + {family : NormalizedFamily} (hfamily : family ∈ gen.families) + (hindices : family.view.indices = constructor.familyIndices) + (mid : List VExpr) {g : Nat} (hg : mid.length = g) + (As₂ : List VExpr) {d : Nat} (hd : As₂.length = d) : + env.SpineWF gen.recUvars + (As₂ ++ + ((VExpr.liftTelN g + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination) 0).reverse ++ + (mid ++ gen.paramsTel.reverse))) + (VExpr.forallN + (VExpr.liftTelN + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length + g + d) + (gen.idxTel family) 0) + (.sort (gen.validated.resultLevel.inst gen.sourceLevels))) + ((constructor.ctor.resultIndicesR source.uvars gen.elimination).map + fun e => + (e.liftN g + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length).liftN d) + (.sort (gen.validated.resultLevel.inst gen.sourceLevels)) := by + let ls := gen.sourceLevels + have hview := (S.ctorWF constructor hconstructor).resultSpine + rw [← hindices] at hview + have hctx := (S.ctorWF constructor hconstructor).emittedTel.ctx + simp only [NormalizedBlockCtor.emittedBinders, + NormalizedBlockCtor.viewBinders, List.reverse_append, + List.append_nil] at hctx + have hraw := hview.defeqDFC S.ord (hctx.symm S.ord) + have hfields := gen.flatCtor_fields_length hconstructor + rw [← hfields] at hraw + have h1 := hraw.instL + (U' := gen.recUvars) gen.sourceLevels_wf + have h1Checked : env.SpineWF gen.recUvars + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).reverse ++ + (gen.block.checked.params.map (VExpr.instL ls)).reverse) + (VExpr.instL ls + (VExpr.forallN + (VExpr.liftTelN + (constructor.ctor.rawFields source.nparams).length + family.view.indices 0) + (.sort gen.validated.resultLevel))) + (constructor.ctor.view.resultIndices.map (VExpr.instL ls)) + ((VExpr.sort gen.validated.resultLevel).instL ls) := by + simpa [NormalizedCtor.fieldsR, List.map_append, + List.map_reverse] using h1 + have hfieldsCtx := + (S.generationFields_onTel_rec hconstructor).extendDefEqCtx + S.generationParams_ctx_rec + have h1Generation := h1Checked.defeqDFC S.ord + (hfieldsCtx.symm S.ord) + rw [VExpr.instL_forallN, VExpr.liftTelN_instL] at h1Generation + rw [← NormalizedCtor.fieldsR_length + (source := source) (mode := gen.elimination) constructor.ctor] at h1Generation + have hidx := (S.familyIndexTel_defeq_rec hfamily).weakN S.ord + (Ctx.LiftN.zero (Γ := gen.paramsTel.reverse) + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).reverse) + rw [List.length_reverse] at hidx + have hlen : + (constructor.ctor.view.resultIndices.map (VExpr.instL ls)).length = + (VExpr.liftTelN + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length + (gen.idxTel family) 0).length := by + simp only [List.length_map, VExpr.liftTelN_length, + BlockGenerationChecked.idxTel] + exact (S.viewResultIndices_length hconstructor).trans + ((congrArg List.length hindices).symm.trans + (gen.shape.2.2.2.2 family hfamily).2.2.2.1.symm) + have h1Raw := hidx.spine_sort S.ord h1Generation hlen + have W₁ := Ctx.LiftN.consTel (n := mid.length) + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination) + (Ctx.LiftN.zero (Γ := gen.paramsTel.reverse) mid) + rw [Nat.add_zero] at W₁ + have h2 := h1Raw.weakN S.ord W₁ + rw [VExpr.liftN_forallN, hg] at h2 + have h3 := h2.weakN S.ord + (Ctx.LiftN.zero (Γ := _) As₂ (h := hd)) + rw [VExpr.liftN_forallN] at h3 + rw [VExpr.liftTelN_liftTelN_hi' + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length g _ 0 (by omega), + VExpr.liftTelN_liftTelN] at h3 + simpa [ls, NormalizedCtor.resultIndicesR, + VExpr.instL, VExpr.liftN, List.map_map, + Function.comp_def, List.append_assoc] using h3 + +/-- The raw mutual constructor applied to its emitted self-spine at source +universes. -/ +theorem ctorApp_emitted_decl + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) : + env.HasType source.uvars + ((constructor.ctor.rawFields source.nparams).reverse ++ + gen.block.checked.params.reverse) + (VExpr.appN + (.const constructor.ctor.raw.name (VLevel.params source.uvars)) + (VExpr.bvarRevRange + (constructor.ctor.rawFields source.nparams).length + source.nparams ++ + VExpr.bvarRevRange 0 + (constructor.ctor.rawFields source.nparams).length)) + (NormalizedBlockCtor.resultTarget gen constructor) := by + let E := NormalizedBlockCtor.emittedBinders gen constructor + let V := NormalizedBlockCtor.viewBinders gen constructor + have hc : env.HasType source.uvars [] + (.const constructor.ctor.raw.name (VLevel.params source.uvars)) + (VExpr.forallN + (NormalizedBlockCtor.declaredBinders + (source := source) constructor) + (NormalizedBlockCtor.rawResult + (source := source) constructor)) := by + rw [NormalizedBlockCtor.declaredBinders, + NormalizedBlockCtor.rawResult, + ← constructor.ctor.rawType_eq] + exact S.ctorConst_decl hconstructor + obtain ⟨_, hdecl⟩ := + (S.ctorWF constructor hconstructor).declaredTel.forallN_defeq + (by simpa using + (S.ctorWF constructor hconstructor).declaredResult) + have hview : env.HasType source.uvars [] + (.const constructor.ctor.raw.name (VLevel.params source.uvars)) + (VExpr.forallN V + (NormalizedBlockCtor.resultTarget gen constructor)) := by + simpa [V] using hdecl.defeq hc + have hresult : env.HasType source.uvars E.reverse + (NormalizedBlockCtor.resultTarget gen constructor) + (.sort gen.validated.resultLevel) := by + simpa [E] using + (S.ctorWF constructor hconstructor).emittedResult.hasType.2 + obtain ⟨_, hemit⟩ := + (S.ctorWF constructor hconstructor).emittedTel.forallN_defeq + (by simpa [E, VEnv.HasType] using hresult) + have hcE₀ : env.HasType source.uvars [] + (.const constructor.ctor.raw.name (VLevel.params source.uvars)) + (VExpr.forallN E + (NormalizedBlockCtor.resultTarget gen constructor)) := by + exact hemit.defeq' (by simpa [V] using hview) + have hclosed : + (VExpr.forallN E + (NormalizedBlockCtor.resultTarget gen constructor)).ClosedN 0 := + (hcE₀.closedN' S.ord.closed trivial).2.2 + have hcE : env.HasType source.uvars E.reverse + (.const constructor.ctor.raw.name (VLevel.params source.uvars)) + (VExpr.forallN E + (NormalizedBlockCtor.resultTarget gen constructor)) := + hcE₀.weak0 S.ord + have happ := VEnv.HasType.appN_selfSpine' + (As := E) + (B := NormalizedBlockCtor.resultTarget gen constructor) + (Δ := []) (Γ := []) hclosed (by simpa using hcE) + simp only [List.length_nil, VExpr.liftN_zero, + List.nil_append, List.append_nil] at happ + have hEctx : + E.reverse = + (constructor.ctor.rawFields source.nparams).reverse ++ + gen.block.checked.params.reverse := by + simp [E, NormalizedBlockCtor.emittedBinders, + List.reverse_append] + have hElen : + E.length = + (constructor.ctor.rawFields source.nparams).length + + source.nparams := by + have hp : gen.block.checked.params.length = source.nparams := + gen.shape.2.1.symm.trans gen.shape.1 + simp [E, NormalizedBlockCtor.emittedBinders, hp] + omega + rw [hEctx, hElen, + ← VExpr.bvarRevRange_append source.nparams + (constructor.ctor.rawFields source.nparams).length] at happ + exact happ + +/-- The exact emitted mutual-constructor application in recursor universes, +retargeted to its owner family. -/ +theorem ctorApp_emitted_rec + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) + {family : NormalizedFamily} + (hname : family.raw.name = constructor.familyName) : + env.HasType gen.recUvars + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).reverse ++ gen.paramsTel.reverse) + (VExpr.appN + (.const constructor.ctor.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length source.nparams ++ + VExpr.bvarRevRange 0 + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length)) + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length source.nparams ++ + constructor.ctor.resultIndicesR source.uvars gen.elimination)) := by + let ls := gen.sourceLevels + have h := (S.ctorApp_emitted_decl hconstructor).instL + (U' := gen.recUvars) gen.sourceLevels_wf + have hChecked : env.HasType gen.recUvars + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).reverse ++ + (gen.block.checked.params.map (VExpr.instL ls)).reverse) + ((VExpr.appN + (.const constructor.ctor.raw.name (VLevel.params source.uvars)) + (VExpr.bvarRevRange + (constructor.ctor.rawFields source.nparams).length + source.nparams ++ + VExpr.bvarRevRange 0 + (constructor.ctor.rawFields source.nparams).length)).instL ls) + ((NormalizedBlockCtor.resultTarget gen constructor).instL ls) := by + simpa [NormalizedCtor.fieldsR, List.map_append, + List.map_reverse] using h + have hfieldsCtx := + (S.generationFields_onTel_rec hconstructor).extendDefEqCtx + S.generationParams_ctx_rec + have hGeneration := hChecked.defeqDFC S.ord + (hfieldsCtx.symm S.ord) + rw [← NormalizedCtor.fieldsR_length + (source := source) (mode := gen.elimination) constructor.ctor] at hGeneration + simpa [ls, hname, NormalizedCtor.fieldsR, + BlockGenerationChecked.paramsTel, + NormalizedBlockCtor.resultTarget, + NormalizedCtor.resultIndicesR, + VExpr.instL_appN, List.map_append, + bvarRevRange_instL, List.map_reverse, + VExpr.instL, VLevel.params_map_inst_params'] using hGeneration + +/-- Transport the emitted mutual-constructor application beneath arbitrary +middle and top binders. -/ +theorem ctorApp_transport + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) + {family : NormalizedFamily} + (hname : family.raw.name = constructor.familyName) + (mid : List VExpr) {g : Nat} (hg : mid.length = g) + (As₂ : List VExpr) {d : Nat} (hd : As₂.length = d) : + env.HasType gen.recUvars + (As₂ ++ + ((VExpr.liftTelN g + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination) 0).reverse ++ + (mid ++ gen.paramsTel.reverse))) + (VExpr.appN (.const constructor.ctor.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + (d + (g + + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length)) source.nparams ++ + VExpr.bvarRevRange d + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length)) + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + (d + (g + + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length)) source.nparams ++ + (constructor.ctor.resultIndicesR source.uvars gen.elimination).map + fun e => + (e.liftN g + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length).liftN d)) := by + let Bs := constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination + have W₁ := Ctx.LiftN.consTel (n := mid.length) Bs + (Ctx.LiftN.zero (Γ := gen.paramsTel.reverse) mid) + rw [Nat.add_zero] at W₁ + have h₁ := (S.ctorApp_emitted_rec hconstructor hname).weakN + S.ord W₁ + rw [hg] at h₁ + have hmid : env.HasType gen.recUvars + ((VExpr.liftTelN g Bs 0).reverse ++ + (mid ++ gen.paramsTel.reverse)) + (VExpr.appN (.const constructor.ctor.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (g + Bs.length) source.nparams ++ + VExpr.bvarRevRange 0 Bs.length)) + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (g + Bs.length) source.nparams ++ + (constructor.ctor.resultIndicesR source.uvars + gen.elimination).map (VExpr.liftN g · Bs.length))) := by + simpa [Bs, VExpr.liftN_appN, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (Nat.le_refl _), + VExpr.bvarRevRange_liftN_high Bs.length 0 g Bs.length (by omega), + VExpr.liftN] using h₁ + have htop := hmid.weakN S.ord + (Ctx.LiftN.zero (Γ := _) As₂ (h := hd)) + simpa [Bs, VExpr.liftN_appN, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (Nat.zero_le _), + VExpr.liftN, List.map_map, Function.comp_def, + List.append_assoc] using htop + +/-- The raw field telescope of a mutual constructor is well formed after +inserting all block motives. -/ +theorem fields_onTel_minor + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) : + env.OnTel gen.recUvars + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse) + (VExpr.liftTelN gen.familyCount + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination) 0) := by + have hd : gen.motiveTypes.reverse.length = gen.familyCount := by + simp [gen.motiveTypes_length] + have hout := (S.generationFields_onTel_rec hconstructor).weakN S.ord + (Ctx.LiftN.zero (n := gen.familyCount) + (Γ := gen.paramsTel.reverse) gen.motiveTypes.reverse (h := hd)) + simpa using hout + +/-- Every flattened mutual constructor minor is a type over the global +motive telescope. -/ +theorem minor_isType + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) : + env.IsType gen.recUvars + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse) + (gen.minorType constructor) := by + obtain ⟨family, hfamily, howner, hname, hindices⟩ := + (S.ctorWF constructor hconstructor).owner + let d := gen.familyCount + let Bs := constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination + let m := Bs.length + let rs := constructor.ctor.recArgsR source.uvars gen.elimination + let IHs := BlockGenerationChecked.blockIHsFromRecArgs d m rs 0 + let Fs := VExpr.liftTelN d Bs 0 + let Γ := IHs.reverse ++ + (Fs.reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse)) + let q := family.view.ordinal + let K := d - 1 - constructor.owner + m + rs.length + simp only [BlockGenerationChecked.minorType] + refine VEnv.IsType.forallN (by + simpa [Bs, d] using S.fields_onTel_minor hconstructor) ?_ + refine VEnv.IsType.forallN (by + simpa [Bs, m, rs, IHs, Fs, d, List.append_assoc] using + S.ihs_onTel hconstructor rs (fun r hr => hr) [] 0 rfl) ?_ + have hrlen : IHs.reverse.length = rs.length := by + simp [IHs, BlockGenerationChecked.blockIHsFromRecArgs_length] + have hd : gen.motiveTypes.reverse.length = d := by + simp [d, gen.motiveTypes_length] + have hd' : gen.motiveTypes.length = d := by + simpa using hd + have hFsLen : Fs.length = m := by + simp [Fs, m, VExpr.liftTelN_length] + have hq : q < d := by + simpa [q, d] using gen.family_ordinal_lt hfamily + have hmot : gen.motiveTypes.reverse[d - 1 - q]? = + some ((gen.motiveType family).liftN q) := by + rw [List.getElem?_reverse (by rw [hd']; omega), + show gen.motiveTypes.length - 1 - (d - 1 - q) = q from by + rw [hd'] + omega, + gen.motiveTypes_getElem?_ordinal hfamily] + have hM0 := getElem?_rstack_mid + (IHs.reverse ++ Fs.reverse) + gen.motiveTypes.reverse gen.paramsTel.reverse + (i := K) + (by + simp only [List.length_append, List.length_reverse, + hrlen, hFsLen] + dsimp only [K, q, d] + omega) + (by + simp only [List.length_append, List.length_reverse, + hrlen, hFsLen, hd] + dsimp only [K, q, d] + omega) + have hdiff : + K - (IHs.reverse ++ Fs.reverse).length = d - 1 - q := by + simp only [List.length_append, List.length_reverse, + hrlen, hFsLen] + dsimp only [K, q, d] + omega + rw [hdiff, hmot] at hM0 + have hMget : Γ[K]? = + some ((gen.motiveType family).liftN q) := by + simpa [Γ, List.append_assoc] using hM0 + have hMraw := VEnv.HasType.bvar + (env := env) (U := gen.recUvars) + (Lookup.of_getElem? hMget) + have hM : env.HasType gen.recUvars Γ (.bvar K) + ((gen.motiveType family).liftN (q + K + 1)) := by + simpa [VExpr.liftN_liftN, Nat.add_assoc] using hMraw + have hSp := S.result_transport hconstructor hfamily hindices + gen.motiveTypes.reverse hd IHs.reverse hrlen + have hSp' : env.SpineWF gen.recUvars Γ + (VExpr.forallN + (VExpr.liftTelN (m + d + rs.length) + (gen.idxTel family) 0) + (.sort (gen.validated.resultLevel.inst gen.sourceLevels))) + ((constructor.ctor.resultIndicesR source.uvars + gen.elimination).map fun e => + (e.liftN d m).liftN rs.length) + (.sort (gen.validated.resultLevel.inst gen.sourceLevels)) := by + simpa [Γ, Fs, Bs, m, d, List.append_assoc] using hSp + have hlen : + ((constructor.ctor.resultIndicesR source.uvars + gen.elimination).map fun e => + (e.liftN d m).liftN rs.length).length = + (gen.idxTel family).length := by + simp only [List.length_map, NormalizedCtor.resultIndicesR, + BlockGenerationChecked.idxTel] + exact (S.viewResultIndices_length hconstructor).trans + ((congrArg List.length hindices).symm.trans + (gen.shape.2.2.2.2 family hfamily).2.2.2.1.symm) + have hctorApp := S.ctorApp_transport hconstructor hname + gen.motiveTypes.reverse hd IHs.reverse hrlen + have hctorApp' : env.HasType gen.recUvars Γ + (VExpr.appN + (.const constructor.ctor.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (m + d + rs.length) source.nparams ++ + VExpr.bvarRevRange rs.length m)) + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (m + d + rs.length) source.nparams ++ + (constructor.ctor.resultIndicesR source.uvars + gen.elimination).map fun e => + (e.liftN d m).liftN rs.length)) := by + simpa [Γ, Fs, Bs, m, d, IHs, rs, + List.append_assoc, Nat.add_assoc, Nat.add_comm, + Nat.add_left_comm] using hctorApp + have hshift : q + K + 1 = m + d + rs.length := by + dsimp only [q, K, d] + omega + have hbody := gen.motiveVarApp_hasType family + (q := q) (K := K) + hM (by simpa [hshift, Nat.add_assoc, Nat.add_comm, + Nat.add_left_comm] using hSp') hlen + (by simpa [hshift, Nat.add_assoc, Nat.add_comm, + Nat.add_left_comm] using hctorApp') + exact ⟨gen.motiveLevel, by + simpa [Γ, Fs, Bs, m, rs, IHs, d, K, q, + BlockGenerationChecked.blockMinorIH, + List.append_assoc, Nat.add_assoc, Nat.add_comm, + Nat.add_left_comm] using hbody⟩ + +/-- Any suffix of the globally flattened constructor list generates a +well-formed minor telescope at its positional depth. -/ +theorem minorTypesAux_onTel : + ∀ (constructors : List NormalizedBlockCtor), + (∀ constructor ∈ constructors, constructor ∈ gen.flatCtors) → + ∀ (Δ : List VExpr) (i : Nat), Δ.length = i → + env.OnTel gen.recUvars + (Δ ++ (gen.motiveTypes.reverse ++ gen.paramsTel.reverse)) + (gen.minorTypesAux constructors i) + | [], _, _, _, _ => trivial + | constructor :: constructors, hsub, Δ, i, hΔ => + ⟨by + rw [← hΔ] + exact (S.minor_isType + (hsub constructor (.head _))).weakN S.ord (.zero Δ), + BlockGenerationEnv.minorTypesAux_onTel constructors + (fun constructor hconstructor => hsub constructor (.tail _ hconstructor)) + (_ :: Δ) (i + 1) (by simp [hΔ])⟩ + +/-- The complete global constructor-minor telescope is well formed beneath +all mutual motives. -/ +theorem minorTypes_onTel : + env.OnTel gen.recUvars + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse) + gen.minorTypes := by + simpa [BlockGenerationChecked.minorTypes] using + S.minorTypesAux_onTel gen.flatCtors + (fun _ h => h) [] 0 rfl + +theorem idxTel_onTel {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + env.OnTel gen.recUvars gen.paramsTel.reverse + (gen.idxTel family) := by + have h := (S.generationFamily_onTel hfamily).instL + (U' := gen.recUvars) gen.sourceLevels_wf + have h' : env.OnTel gen.recUvars [] + (gen.paramsTel ++ gen.idxTel family) := by + simpa [BlockGenerationChecked.paramsTel, + BlockGenerationChecked.idxTel] using h + simpa using h'.of_append.2 + +/-- Transport a selected family applied to its index self-spine beneath +arbitrary middle and top binders. -/ +theorem familyApp_transport {family : NormalizedFamily} + (hfamily : family ∈ gen.families) + (mid : List VExpr) {g : Nat} (hg : mid.length = g) + (As₂ : List VExpr) {d : Nat} (hd : As₂.length = d) : + env.HasType gen.recUvars + (As₂ ++ + ((VExpr.liftTelN g (gen.idxTel family) 0).reverse ++ + (mid ++ gen.paramsTel.reverse))) + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + (d + (g + (gen.idxTel family).length)) source.nparams ++ + VExpr.bvarRevRange d (gen.idxTel family).length)) + (.sort (gen.validated.resultLevel.inst gen.sourceLevels)) := by + have W₁ := Ctx.LiftN.consTel (n := mid.length) (gen.idxTel family) + (Ctx.LiftN.zero (Γ := gen.paramsTel.reverse) mid) + rw [Nat.add_zero] at W₁ + have h₁ := (S.familyApp_hasType hfamily).weakN S.ord W₁ + rw [hg] at h₁ + have hmid : env.HasType gen.recUvars + ((VExpr.liftTelN g (gen.idxTel family) 0).reverse ++ + (mid ++ gen.paramsTel.reverse)) + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + (g + (gen.idxTel family).length) source.nparams ++ + VExpr.bvarRevRange 0 (gen.idxTel family).length)) + (.sort (gen.validated.resultLevel.inst gen.sourceLevels)) := by + simpa [VExpr.liftN_appN, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (Nat.le_refl _), + VExpr.bvarRevRange_liftN_high + (gen.idxTel family).length 0 g (gen.idxTel family).length + (by omega), + VExpr.liftN] using h₁ + have htop := hmid.weakN S.ord + (Ctx.LiftN.zero (Γ := _) As₂ (h := hd)) + simpa [VExpr.liftN_appN, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (Nat.zero_le _), + VExpr.liftN, List.append_assoc] using htop + +/-- The generated recursor type for every selected mutual family is well +formed over the empty context. -/ +theorem recType_isType {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + env.IsType gen.recUvars [] (gen.recType family) := by + let d := gen.familyCount + let k := gen.minorCount + let q := family.view.ordinal + let Is := gen.idxTel family + let ni := Is.length + let LiftedIs := VExpr.liftTelN (d + k) Is 0 + let A := VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (ni + d + k) source.nparams ++ + VExpr.bvarRevRange 0 ni) + let Base := gen.minorTypes.reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse) + let Γ := A :: (LiftedIs.reverse ++ Base) + let K := d - 1 - q + k + ni + 1 + refine VEnv.IsType.forallN S.paramsTel_onTel ?_ + simp only [List.append_nil] + refine VEnv.IsType.forallN S.motiveTypes_onTel ?_ + refine VEnv.IsType.forallN S.minorTypes_onTel ?_ + have hdk : + (gen.minorTypes.reverse ++ gen.motiveTypes.reverse).length = d + k := by + simp only [List.length_append, List.length_reverse, + gen.minorTypes_length, gen.motiveTypes_length] + dsimp only [d, k] + omega + have hI : env.OnTel gen.recUvars Base LiftedIs := by + have h := (S.idxTel_onTel hfamily).weakN S.ord + (Ctx.LiftN.zero (n := d + k) + (Γ := gen.paramsTel.reverse) + (gen.minorTypes.reverse ++ gen.motiveTypes.reverse) + (h := hdk)) + simpa [Base, LiftedIs, Is, List.append_assoc] using h + refine VEnv.IsType.forallN hI ?_ + have hmaj₀ := S.familyApp_transport hfamily + (gen.minorTypes.reverse ++ gen.motiveTypes.reverse) + (g := d + k) hdk [] (d := 0) rfl + rw [VExpr.bvarRevRange_congr source.nparams + (show 0 + (d + k + Is.length) = ni + d + k by + dsimp only [ni] + omega)] at hmaj₀ + have hmaj : env.HasType gen.recUvars + (LiftedIs.reverse ++ Base) A + (.sort (gen.validated.resultLevel.inst gen.sourceLevels)) := by + simpa [A, Base, LiftedIs, Is, List.append_assoc] using hmaj₀ + refine VEnv.IsType.forallE ⟨_, hmaj⟩ ?_ + have hILen : LiftedIs.length = ni := by + simp [LiftedIs, ni, VExpr.liftTelN_length] + have hq : q < d := by + simpa [q, d] using gen.family_ordinal_lt hfamily + have hd : gen.motiveTypes.length = d := by + simpa [d] using gen.motiveTypes_length + have hmot : gen.motiveTypes.reverse[d - 1 - q]? = + some ((gen.motiveType family).liftN q) := by + rw [List.getElem?_reverse (by rw [hd]; omega), + show gen.motiveTypes.length - 1 - (d - 1 - q) = q from by + rw [hd] + omega, + gen.motiveTypes_getElem?_ordinal hfamily] + have hM0 := getElem?_rstack_mid + ([A] ++ LiftedIs.reverse ++ gen.minorTypes.reverse) + gen.motiveTypes.reverse gen.paramsTel.reverse + (i := K) + (by + simp only [List.length_append, List.length_singleton, + List.length_reverse, hILen, gen.minorTypes_length] + dsimp only [K, q, d, k, ni, Is] + omega) + (by + simp only [List.length_append, List.length_singleton, + List.length_reverse, hILen, gen.minorTypes_length] + rw [hd] + dsimp only [K, q, d, k, ni, Is] + omega) + have hdiff : + K - ([A] ++ LiftedIs.reverse ++ gen.minorTypes.reverse).length = + d - 1 - q := by + simp only [List.length_append, List.length_singleton, + List.length_reverse, hILen, gen.minorTypes_length] + dsimp only [K, q, d, k, ni, Is] + omega + rw [hdiff, hmot] at hM0 + have hMget : Γ[K]? = + some ((gen.motiveType family).liftN q) := by + simpa [Γ, Base, List.append_assoc] using hM0 + have hmlu := Lookup.of_getElem? hMget + rw [show ((gen.motiveType family).liftN q).liftN (K + 1) = + ((gen.motiveType family).liftN (d + k)).liftN (ni + 1) from by + rw [VExpr.liftN_liftN, VExpr.liftN_liftN] + congr 1 + dsimp only [K, q, d, k] + omega, + gen.motiveType_liftN family] at hmlu + have hfun : env.HasType gen.recUvars Γ (.bvar K) + ((VExpr.forallN LiftedIs + (.forallE + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (d + k + ni) source.nparams ++ + VExpr.bvarRevRange 0 ni)) + (.sort gen.motiveLevel))).liftN + (1 + LiftedIs.length)) := by + exact .bvar (by + simpa [Γ, Base, LiftedIs, Is, ni, + VExpr.liftTelN_length, List.append_assoc, + Nat.add_comm, Nat.add_left_comm, Nat.add_assoc] using hmlu) + have hMapp := VEnv.HasType.appN_selfSpine + (As := LiftedIs) + (Δ := [A]) + (Γ := Base) + (f := .bvar K) hfun + have hMapp' : env.HasType gen.recUvars Γ + ((VExpr.bvar K).appN (VExpr.bvarRevRange 1 ni)) + (.forallE (A.liftN 1) (.sort gen.motiveLevel)) := by + simpa [Γ, Base, A, LiftedIs, Is, ni, hILen, + VExpr.liftN, List.append_assoc, + Nat.add_comm, Nat.add_left_comm, Nat.add_assoc] using hMapp + have h0 : Γ[0]? = some A := rfl + have harg := VEnv.HasType.bvar + (env := env) (U := gen.recUvars) + (Lookup.of_getElem? h0) + have harg' : env.HasType gen.recUvars Γ (.bvar 0) (A.liftN 1) := by + simpa [Γ, List.append_assoc] using harg + have happ := VEnv.HasType.app hMapp' harg' + exact ⟨gen.motiveLevel, by + simpa [BlockGenerationChecked.recType, + Γ, Base, A, LiftedIs, Is, ni, d, k, q, K, + VExpr.liftTelN_length, VExpr.inst, List.append_assoc, + Nat.add_comm, Nat.add_left_comm, Nat.add_assoc] using happ⟩ + +/-- Every family-selected mutual recursor constant is well formed in the +environment containing the complete raw block. -/ +theorem recursor_wf {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + (gen.recursor family).WF env := + S.recType_isType hfamily + +theorem recType_levelWF {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + (gen.recType family).LevelWF gen.recUvars := by + obtain ⟨_, h⟩ := S.recType_isType hfamily + exact (h.levelWF trivial).1 + +theorem recType_closedN {family : NormalizedFamily} + (hfamily : family ∈ gen.families) : + (gen.recType family).ClosedN 0 := by + obtain ⟨_, h⟩ := S.recType_isType hfamily + exact VExpr.WF.closedN S.ord ⟨_, h⟩ trivial + +/-- The selected family recursor constant has its generated mutual recursor +type in every local context. -/ +theorem recursor_hasType {family : NormalizedFamily} + (hfamily : family ∈ gen.families) + (hrec : env.constants (.str family.raw.name "rec") = + some (gen.recursor family)) {Γ : List VExpr} : + env.HasType gen.recUvars Γ + (.const (.str family.raw.name "rec") gen.recLevels) + (gen.recType family) := by + have h := VEnv.HasType.const (Γ := Γ) hrec + VLevel.params_wf VLevel.params_length + rw [show (gen.recursor family).uvars = gen.recUvars from rfl, + show (gen.recursor family).type = gen.recType family from rfl] at h + rwa [(S.recType_levelWF hfamily).instL_id] at h + +/-- Applying a selected recursor to the shared parameters, motives, and +flattened minors exposes its index-and-major spine. -/ +theorem recBase_hasType {family : NormalizedFamily} + (hfamily : family ∈ gen.families) + (hrec : env.constants (.str family.raw.name "rec") = + some (gen.recursor family)) + (Δ : List VExpr) : + env.HasType gen.recUvars + (Δ ++ (gen.minorTypes.reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse))) + (gen.recBase Δ.length family.view.ordinal) + ((VExpr.forallN + (VExpr.liftTelN + (gen.familyCount + gen.minorCount) + (gen.idxTel family) 0) + (.forallE + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + ((gen.idxTel family).length + + gen.familyCount + gen.minorCount) + source.nparams ++ + VExpr.bvarRevRange 0 (gen.idxTel family).length)) + (.app + (VExpr.appN + (.bvar + (gen.familyCount - 1 - family.view.ordinal + + gen.minorCount + (gen.idxTel family).length + 1)) + (VExpr.bvarRevRange 1 (gen.idxTel family).length)) + (.bvar 0)))).liftN Δ.length) := by + have hf : env.HasType gen.recUvars + (Δ ++ + (gen.paramsTel ++ gen.motiveTypes ++ gen.minorTypes).reverse ++ []) + (.const (.str family.raw.name "rec") gen.recLevels) + ((VExpr.forallN + (gen.paramsTel ++ gen.motiveTypes ++ gen.minorTypes) + (VExpr.forallN + (VExpr.liftTelN + (gen.familyCount + gen.minorCount) + (gen.idxTel family) 0) + (.forallE + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + ((gen.idxTel family).length + + gen.familyCount + gen.minorCount) + source.nparams ++ + VExpr.bvarRevRange 0 (gen.idxTel family).length)) + (.app + (VExpr.appN + (.bvar + (gen.familyCount - 1 - family.view.ordinal + + gen.minorCount + (gen.idxTel family).length + 1)) + (VExpr.bvarRevRange 1 (gen.idxTel family).length)) + (.bvar 0))))).liftN + (Δ.length + + (gen.paramsTel ++ gen.motiveTypes ++ gen.minorTypes).length)) := by + rw [show VExpr.forallN + (gen.paramsTel ++ gen.motiveTypes ++ gen.minorTypes) + (VExpr.forallN + (VExpr.liftTelN + (gen.familyCount + gen.minorCount) + (gen.idxTel family) 0) + (.forallE + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + ((gen.idxTel family).length + + gen.familyCount + gen.minorCount) + source.nparams ++ + VExpr.bvarRevRange 0 (gen.idxTel family).length)) + (.app + (VExpr.appN + (.bvar + (gen.familyCount - 1 - family.view.ordinal + + gen.minorCount + (gen.idxTel family).length + 1)) + (VExpr.bvarRevRange 1 (gen.idxTel family).length)) + (.bvar 0)))) = gen.recType family from by + rw [VExpr.forallN_append, VExpr.forallN_append] + rfl, + (S.recType_closedN hfamily).liftN_eq (Nat.zero_le _)] + exact S.recursor_hasType hfamily hrec + have hspine := VEnv.HasType.appN_selfSpine + (env := env) (U := gen.recUvars) hf + simp only [List.reverse_append, List.append_nil, List.append_assoc, List.length_append, + gen.minorTypes_length, gen.motiveTypes_length] at hspine + rw [show gen.paramsTel.length = source.nparams from by + simpa [BlockGenerationChecked.paramsTel] using + S.generationParams_length, + VExpr.bvarRevRange_congr' Δ.length + (show source.nparams + (gen.familyCount + gen.minorCount) = + source.nparams + gen.familyCount + gen.minorCount by omega)] at hspine + simpa [BlockGenerationChecked.recBase, + gen.familyNameAt_ordinal hfamily, + List.append_assoc] using hspine + +/-- Applying a selected family recursor to indices and a major premise +returns the correspondingly selected motive application. -/ +theorem recApp_hasType {family : NormalizedFamily} + (hfamily : family ∈ gen.families) + (hrec : env.constants (.str family.raw.name "rec") = + some (gen.recursor family)) + (Δ : List VExpr) {idxs : List VExpr} {a : VExpr} + (hidx : env.SpineWF gen.recUvars + (Δ ++ (gen.minorTypes.reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse))) + (VExpr.forallN + (VExpr.liftTelN + (Δ.length + gen.familyCount + gen.minorCount) + (gen.idxTel family) 0) + (.sort (gen.validated.resultLevel.inst gen.sourceLevels))) + idxs (.sort (gen.validated.resultLevel.inst gen.sourceLevels))) + (hlen : idxs.length = (gen.idxTel family).length) + (ha : env.HasType gen.recUvars + (Δ ++ (gen.minorTypes.reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse))) + a + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + (Δ.length + gen.familyCount + gen.minorCount) + source.nparams ++ idxs))) : + env.HasType gen.recUvars + (Δ ++ (gen.minorTypes.reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse))) + (VExpr.appN (gen.recBase Δ.length family.view.ordinal) + (idxs ++ [a])) + (VExpr.appN + (.bvar + (gen.familyCount - 1 - family.view.ordinal + + gen.minorCount + Δ.length)) + (idxs ++ [a])) := by + have hq := gen.family_ordinal_lt hfamily + have hb := S.recBase_hasType hfamily hrec Δ + rw [VExpr.liftN_forallN, VExpr.liftTelN_liftTelN, + liftTelN_congr _ _ + (show gen.familyCount + gen.minorCount + Δ.length = + Δ.length + gen.familyCount + gen.minorCount by omega)] at hb + have hcod : + (VExpr.forallE + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + ((gen.idxTel family).length + + gen.familyCount + gen.minorCount) + source.nparams ++ + VExpr.bvarRevRange 0 (gen.idxTel family).length)) + (.app + (VExpr.appN + (.bvar + (gen.familyCount - 1 - family.view.ordinal + + gen.minorCount + (gen.idxTel family).length + 1)) + (VExpr.bvarRevRange 1 (gen.idxTel family).length)) + (.bvar 0))).liftN Δ.length + (0 + (VExpr.liftTelN + (gen.familyCount + gen.minorCount) + (gen.idxTel family) 0).length) = + VExpr.forallE + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + ((gen.idxTel family).length + Δ.length + + gen.familyCount + gen.minorCount) + source.nparams ++ + VExpr.bvarRevRange 0 (gen.idxTel family).length)) + (.app + (VExpr.appN + (.bvar + (gen.familyCount - 1 - family.view.ordinal + + gen.minorCount + (gen.idxTel family).length + + Δ.length + 1)) + (VExpr.bvarRevRange 1 (gen.idxTel family).length)) + (.bvar 0)) := by + rw [VExpr.liftTelN_length, Nat.zero_add] + show VExpr.forallE _ _ = VExpr.forallE _ _ + congr 1 + · rw [VExpr.liftN_appN, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (by omega), + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega), + VExpr.bvarRevRange_congr source.nparams + (show Δ.length + + ((gen.idxTel family).length + + gen.familyCount + gen.minorCount) = + (gen.idxTel family).length + Δ.length + + gen.familyCount + gen.minorCount by omega)] + rfl + · show VExpr.app _ _ = VExpr.app _ _ + congr 1 + · rw [VExpr.liftN_appN, + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega)] + show VExpr.appN + (.bvar (liftVar Δ.length + (gen.familyCount - 1 - family.view.ordinal + + gen.minorCount + (gen.idxTel family).length + 1) + ((gen.idxTel family).length + 1))) _ = _ + rw [liftVar_le (by omega)] + congr 1 + ac_rfl + rw [hcod] at hb + have hshape := hidx.retarget + (by simpa only [VExpr.liftTelN_length] using hlen) + (.forallE + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + ((gen.idxTel family).length + Δ.length + + gen.familyCount + gen.minorCount) + source.nparams ++ + VExpr.bvarRevRange 0 (gen.idxTel family).length)) + (.sort gen.motiveLevel)) + rw [VExpr.instRev_forallE_sort, VExpr.instRev_appN, + VExpr.instRev_closedN _ + (C := .const family.raw.name gen.sourceLevels) trivial, + List.map_append, + VExpr.map_instRev_bvarRevRange_ge _ _ _ + (by rw [hlen]; omega), + show (gen.idxTel family).length + Δ.length + + gen.familyCount + gen.minorCount - idxs.length = + Δ.length + gen.familyCount + gen.minorCount from by + rw [hlen] + omega, + VExpr.bvarRevRange_congr' 0 hlen.symm, + VExpr.map_instRev_bvarRevRange] at hshape + rw [hlen] at hshape + have hfull := hshape.snoc ha + simp only [VExpr.inst] at hfull + change env.SpineWF gen.recUvars _ + (VExpr.forallN + (VExpr.liftTelN + (Δ.length + gen.familyCount + gen.minorCount) + (gen.idxTel family) 0) + (VExpr.forallN + [VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + ((gen.idxTel family).length + Δ.length + + gen.familyCount + gen.minorCount) + source.nparams ++ + VExpr.bvarRevRange 0 (gen.idxTel family).length)] + (.sort gen.motiveLevel))) + (idxs ++ [a]) (.sort gen.motiveLevel) at hfull + rw [← VExpr.forallN_append] at hfull + have hfullLen : (idxs ++ [a]).length = + (VExpr.liftTelN + (Δ.length + gen.familyCount + gen.minorCount) + (gen.idxTel family) 0 ++ + [VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + ((gen.idxTel family).length + Δ.length + + gen.familyCount + gen.minorCount) + source.nparams ++ + VExpr.bvarRevRange 0 (gen.idxTel family).length)]).length := by + simp only [List.length_append, List.length_singleton, + VExpr.liftTelN_length, hlen] + have hactual := hfull.retarget hfullLen + (VExpr.app + (VExpr.appN + (.bvar + (gen.familyCount - 1 - family.view.ordinal + + gen.minorCount + (gen.idxTel family).length + + Δ.length + 1)) + (VExpr.bvarRevRange 1 (gen.idxTel family).length)) + (.bvar 0)) + rw [VExpr.forallN_append] at hactual + have happ := hactual.hasType_appN hb + rw [show VExpr.app + (VExpr.appN + (.bvar + (gen.familyCount - 1 - family.view.ordinal + + gen.minorCount + (gen.idxTel family).length + + Δ.length + 1)) + (VExpr.bvarRevRange 1 (gen.idxTel family).length)) + (.bvar 0) = + VExpr.appN + (.bvar + (gen.familyCount - 1 - family.view.ordinal + + gen.minorCount + (gen.idxTel family).length + + Δ.length + 1)) + (VExpr.bvarRevRange 0 ((gen.idxTel family).length + 1)) from by + rw [VExpr.bvarRevRange_congr' 0 + (show (gen.idxTel family).length + 1 = + 1 + (gen.idxTel family).length by omega), + ← VExpr.bvarRevRange_append (gen.idxTel family).length 1] + simpa [VExpr.bvarRevRange, VExpr.appN] using + (VExpr.appN_append + (.bvar + (gen.familyCount - 1 - family.view.ordinal + + gen.minorCount + (gen.idxTel family).length + + Δ.length + 1)) + (VExpr.bvarRevRange 1 (gen.idxTel family).length) + [VExpr.bvar 0]).symm, + VExpr.instRev_appN, + VExpr.instRev_bvar_ge _ (by + simp only [List.length_append, List.length_singleton] + rw [hlen] + omega), + VExpr.bvarRevRange_congr' 0 + (show (gen.idxTel family).length + 1 = + (idxs ++ [a]).length by simp [hlen]), + VExpr.map_instRev_bvarRevRange] at happ + rw [show gen.familyCount - 1 - family.view.ordinal + + gen.minorCount + (gen.idxTel family).length + Δ.length + 1 - + (idxs ++ [a]).length = + gen.familyCount - 1 - family.view.ordinal + + gen.minorCount + Δ.length from by + simp only [List.length_append, List.length_singleton] + rw [hlen] + omega] at happ + simpa [List.length_append, hlen, + BlockGenerationChecked.recBase, + gen.familyNameAt_ordinal hfamily] using happ + +/-- The constructor application appearing in a mutual rule has the selected +owner-family application as its type. -/ +theorem ctorAppRule_hasType + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) + {family : NormalizedFamily} (_hfamily : family ∈ gen.families) + (hname : family.raw.name = constructor.familyName) : + env.HasType gen.recUvars + ((VExpr.liftTelN + (gen.familyCount + gen.minorCount) + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination) 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse))) + (VExpr.appN + (.const constructor.ctor.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length + + gen.familyCount + gen.minorCount) + source.nparams ++ + VExpr.bvarRevRange 0 + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length)) + (VExpr.appN (.const family.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length + + gen.familyCount + gen.minorCount) + source.nparams ++ + (constructor.ctor.resultIndicesR source.uvars + gen.elimination).map fun e => + e.liftN (gen.familyCount + gen.minorCount) + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length)) := by + have h := S.ctorApp_transport hconstructor hname + (gen.minorTypes.reverse ++ gen.motiveTypes.reverse) + (g := gen.familyCount + gen.minorCount) + (by + simp only [List.length_append, List.length_reverse, + gen.minorTypes_length, gen.motiveTypes_length] + omega) + [] (d := 0) rfl + rw [VExpr.bvarRevRange_congr source.nparams + (show + 0 + (gen.familyCount + gen.minorCount + + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length) = + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length + + gen.familyCount + gen.minorCount by omega)] at h + simpa [List.append_assoc] using h + +/-- Parameters, every motive, every flattened minor, and the selected +constructor fields form the binder telescope of a mutual rule. -/ +theorem ruleBinders_onTel + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) : + env.OnTel gen.recUvars [] + (gen.paramsTel ++ gen.motiveTypes ++ gen.minorTypes ++ + VExpr.liftTelN + (gen.familyCount + gen.minorCount) + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination) 0) := by + have hF₀ := S.fields_onTel_minor hconstructor + have hF := hF₀.weakN S.ord + (Ctx.LiftN.zero + (Γ := gen.motiveTypes.reverse ++ gen.paramsTel.reverse) + gen.minorTypes.reverse) + rw [VExpr.liftTelN_liftTelN, + liftTelN_congr _ _ + (show gen.familyCount + gen.minorTypes.reverse.length = + gen.familyCount + gen.minorCount by + simp only [List.length_reverse, gen.minorTypes_length])] at hF + refine OnTel.append + (OnTel.append + (OnTel.append S.paramsTel_onTel + (by simpa only [List.append_nil] using S.motiveTypes_onTel)) + (by simpa only [List.append_nil, List.reverse_append, + List.append_assoc] using S.minorTypes_onTel)) ?_ + simpa only [List.append_nil, List.append_assoc, + List.reverse_append] using hF + +/-- The constructor-headed left side of a mutual iota rule has the owner +motive application as its type. -/ +theorem recRuleApp_hasType + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) + (hrecs : ∀ family ∈ gen.families, + env.constants (.str family.raw.name "rec") = + some (gen.recursor family)) : + env.HasType gen.recUvars + ((VExpr.liftTelN + (gen.familyCount + gen.minorCount) + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination) 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse))) + (VExpr.appN + (gen.recBase + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length constructor.owner) + (((constructor.ctor.resultIndicesR source.uvars + gen.elimination).map fun e => + e.liftN (gen.familyCount + gen.minorCount) + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length) ++ + [VExpr.appN + (.const constructor.ctor.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length + + gen.familyCount + gen.minorCount) + source.nparams ++ + VExpr.bvarRevRange 0 + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length)])) + (VExpr.appN + (.bvar + (gen.familyCount - 1 - constructor.owner + + gen.minorCount + + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length)) + (((constructor.ctor.resultIndicesR source.uvars + gen.elimination).map fun e => + e.liftN (gen.familyCount + gen.minorCount) + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length) ++ + [VExpr.appN + (.const constructor.ctor.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length + + gen.familyCount + gen.minorCount) + source.nparams ++ + VExpr.bvarRevRange 0 + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length)])) := by + obtain ⟨family, hfamily, howner, hname, hindices⟩ := + (S.ctorWF constructor hconstructor).owner + let Bs := constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination + let common := gen.familyCount + gen.minorCount + let mid := gen.minorTypes.reverse ++ gen.motiveTypes.reverse + have hmid : mid.length = common := by + simp only [mid, common, List.length_append, List.length_reverse, + gen.minorTypes_length, gen.motiveTypes_length] + omega + have hSp₀ := S.result_transport hconstructor hfamily hindices + mid hmid [] (d := 0) rfl + have hSp : env.SpineWF gen.recUvars + ((VExpr.liftTelN common Bs 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse))) + (VExpr.forallN + (VExpr.liftTelN (Bs.length + common) + (gen.idxTel family) 0) + (.sort (gen.validated.resultLevel.inst gen.sourceLevels))) + ((constructor.ctor.resultIndicesR source.uvars gen.elimination).map + fun e => e.liftN common Bs.length) + (.sort (gen.validated.resultLevel.inst gen.sourceLevels)) := by + simpa [Bs, common, mid, List.append_assoc, + Nat.add_assoc] using hSp₀ + have hidxLen : + ((constructor.ctor.resultIndicesR source.uvars gen.elimination).map + fun e => e.liftN common Bs.length).length = + (gen.idxTel family).length := by + have hlen := hSp.forallN_sort_length + simpa only [VExpr.liftTelN_length] using hlen + have ha := S.ctorAppRule_hasType hconstructor hfamily hname + have hout := S.recApp_hasType hfamily (hrecs family hfamily) + (VExpr.liftTelN common Bs 0).reverse + (by simpa [common, List.length_reverse, VExpr.liftTelN_length, + Nat.add_assoc] using hSp) + hidxLen + (by simpa only [Bs, common, List.length_reverse, + VExpr.liftTelN_length] using ha) + simpa only [Bs, common, howner, List.length_reverse, + VExpr.liftTelN_length] using hout + +/-- Every recursive field contributes a well-typed rule call to the +recursor selected by that field's certified target family. -/ +theorem blockRuleCall_hasType + {constructor : NormalizedBlockCtor} + (hconstructor : constructor ∈ gen.flatCtors) + (hrecs : ∀ family ∈ gen.families, + env.constants (.str family.raw.name "rec") = + some (gen.recursor family)) + {r : RecArg} + (hr : r ∈ constructor.ctor.recArgsR source.uvars gen.elimination) : + env.HasType gen.recUvars + ((VExpr.liftTelN + (gen.familyCount + gen.minorCount) + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination) 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse))) + (BlockGenerationChecked.blockRuleCall + (gen.familyCount + gen.minorCount) + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length + (gen.recBase + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length r.targetType) r) + (BlockGenerationChecked.blockRuleIH + gen.familyCount gen.minorCount + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length r) := by + obtain ⟨r₀, hr₀, rfl⟩ := NormalizedCtor.recArgsR_mem hr + obtain ⟨family, hfamily, hord, ⟨Bview, hBview, hshape⟩, hsem⟩ := + (S.ctorWF constructor hconstructor).recursive r₀ hr₀ + let ls := gen.sourceLevels + let r := r₀.instL ls + let d := gen.familyCount + let k := gen.minorCount + let common := d+k + let Bs := constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination + let m := Bs.length + let j := r₀.fieldIndex + let Fs := VExpr.liftTelN common Bs 0 + let As := BlockGenerationChecked.blockRuleBinders common m r + let idxs := r.indices.map fun e => + (e.liftN common (r.fieldIndex+r.binders.length)).liftN + (m-r.fieldIndex) r.binders.length + let Γ := Fs.reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse)) + have hjview : r₀.fieldIndex < constructor.ctor.view.fields.length := + (List.getElem?_eq_some_iff.1 hBview).1 + have hjm : j < m := by + have hfields := gen.flatCtor_fields_length hconstructor + simp only [j, m, Bs, NormalizedCtor.fieldsR_length] + omega + have hjraw : r₀.fieldIndex < + (constructor.ctor.rawFields source.nparams).length := by + simpa [j, m, Bs, NormalizedCtor.fieldsR_length] using hjm + let Braw := + (constructor.ctor.rawFields source.nparams)[r₀.fieldIndex] + have hBraw : + (constructor.ctor.rawFields source.nparams)[r₀.fieldIndex]? = + some Braw := + List.getElem?_eq_getElem hjraw + have hcommon : + (gen.minorTypes.reverse ++ gen.motiveTypes.reverse).length = + common := by + simp only [List.length_append, List.length_reverse, + gen.minorTypes_length, gen.motiveTypes_length] + dsimp only [common, d, k] + omega + have hFsLen : Fs.length = m := by + simp [Fs, m, VExpr.liftTelN_length] + have ht := S.recArg_transport hconstructor hfamily hsem hjview + (gen.minorTypes.reverse ++ gen.motiveTypes.reverse) hcommon + (Fs.drop j).reverse (d := m-j) (by + simp only [List.length_reverse, List.length_drop, hFsLen]) + dsimp only [r, j, RecArg.instL] at ht + have hctx : + (Fs.drop j).reverse ++ + ((VExpr.liftTelN common (Bs.take j) 0).reverse ++ + ((gen.minorTypes.reverse ++ gen.motiveTypes.reverse) ++ + gen.paramsTel.reverse)) = Γ := by + dsimp only [Γ, Fs] + rw [← VExpr.liftTelN_take, List.append_assoc, + ← List.append_assoc + (((VExpr.liftTelN common Bs 0).drop j).reverse), + ← List.reverse_append, List.take_append_drop, + ← List.append_assoc] + have htel : env.OnTel gen.recUvars Γ As := by + rw [hctx] at ht + simpa [r, As, m, common, j, RecArg.instL, + BlockGenerationChecked.blockRuleBinders] using ht.1 + have hsp : env.SpineWF gen.recUvars + (As.reverse ++ Γ) + (VExpr.forallN + (VExpr.liftTelN + (m + r.binders.length + common) + (gen.idxTel family) 0) + (.sort (gen.validated.resultLevel.inst ls))) + idxs + (.sort (gen.validated.resultLevel.inst ls)) := by + rw [hctx] at ht + simpa [r, As, idxs, m, common, j, ls, + RecArg.instL, BlockGenerationChecked.blockRuleBinders, + List.append_assoc, + show j + r₀.binders.length + common + (m-j) = + m + r₀.binders.length + common by omega] using ht.2 + have hF : Γ[m-1-j]? = + some ((Braw.instL ls).liftN common j) := by + dsimp only [Γ, Fs] + rw [List.getElem?_append_left + (by + simp only [List.length_reverse, VExpr.liftTelN_length] + omega), + List.getElem?_reverse (by rw [hFsLen]; omega), + VExpr.liftTelN_length, + show m - 1 - (m - 1 - j) = j by omega, + VExpr.liftTelN_getElem?, + NormalizedCtor.fieldsR_getElem?, hBraw] + simp [ls] + have hlu := Lookup.of_getElem? hF + rw [show m-1-j+1 = m-j by omega] at hlu + dsimp only [j, r] at hlu + have hf0 := VEnv.HasType.bvar + (env := env) (U := gen.recUvars) hlu + obtain ⟨u, hdom₀⟩ := + S.emittedField_defeq hconstructor hBraw hBview + have hdom₁ := hdom₀.instL + (U' := gen.recUvars) gen.sourceLevels_wf + have hdomChecked : env.IsDefEq gen.recUvars + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination |>.take r₀.fieldIndex).reverse ++ + (gen.block.checked.params.map (VExpr.instL ls)).reverse) + (Braw.instL ls) (Bview.instL ls) ((VExpr.sort u).instL ls) := by + simpa [NormalizedCtor.fieldsR, List.map_append, + List.map_reverse, List.map_take] using hdom₁ + have hprefix := + S.generationFieldPrefix_ctx_rec hconstructor r₀.fieldIndex + have hdomGeneration := hdomChecked.defeqDFC S.ord + (hprefix.symm S.ord) + have hjlen : (Bs.take r₀.fieldIndex).length = + r₀.fieldIndex := by + simp only [Bs, NormalizedCtor.fieldsR, + List.length_take, List.length_map] + omega + have Wmid := Ctx.LiftN.consTel (n := common) + (Bs.take r₀.fieldIndex) + (Ctx.LiftN.zero (n := common) (Γ := gen.paramsTel.reverse) + (gen.minorTypes.reverse ++ gen.motiveTypes.reverse) + (h := hcommon)) + rw [hjlen, Nat.add_zero] at Wmid + have hdom₂ := hdomGeneration.weakN S.ord Wmid + have Wstack := Ctx.LiftN.zero (n := m-j) + (Γ := (VExpr.liftTelN common (Bs.take j) 0).reverse ++ + ((gen.minorTypes.reverse ++ gen.motiveTypes.reverse) ++ + gen.paramsTel.reverse)) + (Fs.drop j).reverse + (h := by + simp only [List.length_reverse, List.length_drop, hFsLen]) + have hdom₃ := hdom₂.weakN S.ord Wstack + rw [hctx] at hdom₃ + have hfView := hdom₃.defeq hf0 + have hfield := blockMinor_fieldType_of_eq hshape common m 0 + (by simpa [j] using hjm) gen.elimination + simp only [RecArg.instL] at hfield + dsimp only [j] at hfView + simp only [Nat.add_zero] at hfield + rw [hfield] at hfView + have hf := hfView.weakN S.ord + (Ctx.LiftN.zero (Γ := Γ) As.reverse) + have hmajor := VEnv.HasType.appN_selfSpine + (env := env) (U := gen.recUvars) + (As := As) + (B := VExpr.appN (.const family.raw.name ls) + (VExpr.bvarRevRange + (m + r.binders.length + common) source.nparams ++ idxs)) + (Δ := []) (Γ := Γ) (by + simpa [As, r, idxs, j, common, ls, RecArg.instL, + BlockGenerationChecked.blockRuleBinders, + BlockGenerationChecked.blockMinorBinders, + List.length_reverse, List.map_map, + Function.comp_def] using hf) + simp only [List.length_nil, VExpr.liftN_zero, + List.nil_append] at hmajor + have hAsLen : As.length = r.binders.length := by + simp [As, BlockGenerationChecked.blockRuleBinders, + VExpr.liftTelN_length] + have hmajor' : env.HasType gen.recUvars (As.reverse ++ Γ) + ((VExpr.bvar (m-1-r.fieldIndex+As.length)).appN + (VExpr.bvarRevRange 0 As.length)) + (VExpr.appN (.const family.raw.name ls) + (VExpr.bvarRevRange + (m+r.binders.length+common) source.nparams ++ idxs)) := by + simpa [As, common, r, RecArg.instL, + BlockGenerationChecked.blockRuleBinders, + VExpr.liftN, liftVar_le, Nat.add_comm] using hmajor + rw [hAsLen] at hmajor' + have hlen : idxs.length = (gen.idxTel family).length := by + have hidx := hsem.2.forallN_sort_length + simp only [idxs, List.length_map, VExpr.liftTelN_length, + BlockGenerationChecked.idxTel] at hidx ⊢ + simpa [r, RecArg.instL] using hidx.trans + (gen.shape.2.2.2.2 family hfamily).2.2.2.1.symm + have hcall := S.recApp_hasType hfamily (hrecs family hfamily) + (As.reverse ++ Fs.reverse) + (by + simpa [Γ, List.append_assoc, hAsLen, hFsLen, + common, d, k, + Nat.add_comm, Nat.add_left_comm, + Nat.add_assoc] using hsp) + hlen + (by + simpa [Γ, List.append_assoc, hAsLen, hFsLen, + common, d, k, + Nat.add_comm, Nat.add_left_comm, + Nat.add_assoc] using hmajor') + have hbaseLift : + (gen.recBase m r.targetType).liftN r.binders.length = + gen.recBase (m+r.binders.length) r.targetType := by + simp only [BlockGenerationChecked.recBase, VExpr.liftN_appN, VExpr.liftN] + rw [bvarRevRange_liftN_ge _ _ _ _ (Nat.zero_le _)] + apply congrArg (VExpr.appN _) + apply VExpr.bvarRevRange_congr + omega + have hlam := HasType.lamN htel (by + simpa [Γ, Fs, hAsLen, hFsLen, hbaseLift, + List.append_assoc, hord, r, RecArg.instL, + common, d, k, + Nat.add_comm, Nat.add_left_comm, + Nat.add_assoc] using hcall) + have hrecPos : r₀.binders.length + m = m + r₀.binders.length := by + omega + have hfieldPos : + r₀.binders.length + (m-1-r₀.fieldIndex) = + m-1-r₀.fieldIndex+r₀.binders.length := by + omega + have hmotivePos : + r₀.binders.length + + (gen.familyCount - 1 - r₀.targetType + (gen.minorCount+m)) = + gen.familyCount - 1 - r₀.targetType + gen.minorCount + m + + r₀.binders.length := by + ac_rfl + rw [hrecPos, hfieldPos, hmotivePos] at hlam + change env.HasType gen.recUvars Γ + (BlockGenerationChecked.blockRuleCall common m + (gen.recBase m r.targetType) r) + (VExpr.forallN As + (VExpr.appN + (.bvar (d - 1 - r.targetType + k + m + r.binders.length)) + (idxs ++ + [VExpr.appN + (.bvar (m - 1 - r.fieldIndex + r.binders.length)) + (VExpr.bvarRevRange 0 r.binders.length)]))) + have hcallEq : + BlockGenerationChecked.blockRuleCall common m + (gen.recBase m r.targetType) r = + VExpr.lamN As + (VExpr.appN + (gen.recBase (m+r.binders.length) r.targetType) + (idxs ++ + [VExpr.appN + (.bvar (m-1-r.fieldIndex+r.binders.length)) + (VExpr.bvarRevRange 0 r.binders.length)])) := by + simp [BlockGenerationChecked.blockRuleCall, As, idxs, hbaseLift] + rw [hcallEq] + simpa [r, RecArg.instL, List.length_map] using hlam + +/-- The selected flattened mutual minor applied to its fields and routed +recursive calls has the owner's motive as its result. -/ +theorem minorApp_hasType {i : Nat} + {constructor : NormalizedBlockCtor} + (hci : gen.flatCtors[i]? = some constructor) + (hrecs : ∀ family ∈ gen.families, + env.constants (.str family.raw.name "rec") = + some (gen.recursor family)) : + env.HasType gen.recUvars + ((VExpr.liftTelN + (gen.familyCount + gen.minorCount) + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination) 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse))) + (VExpr.appN + (.bvar + (gen.minorCount - 1 - i + + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length)) + (VExpr.bvarRevRange 0 + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length ++ + (constructor.ctor.recArgsR source.uvars gen.elimination).map + fun r => + BlockGenerationChecked.blockRuleCall + (gen.familyCount + gen.minorCount) + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length + (gen.recBase + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length r.targetType) r)) + (VExpr.appN + (.bvar + (gen.familyCount - 1 - constructor.owner + gen.minorCount + + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length)) + (((constructor.ctor.resultIndicesR source.uvars + gen.elimination).map fun e => + e.liftN (gen.familyCount + gen.minorCount) + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length) ++ + [VExpr.appN + (.const constructor.ctor.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + ((constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length + gen.familyCount + + gen.minorCount) + source.nparams ++ + VExpr.bvarRevRange 0 + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination).length)])) := by + obtain ⟨hik, -⟩ := List.getElem?_eq_some_iff.1 hci + have hconstructor := List.mem_of_getElem? hci + obtain ⟨family, hfamily, howner, -, -⟩ := + (S.ctorWF constructor hconstructor).owner + let d := gen.familyCount + let k := gen.minorCount + let common := d+k + let Bs := constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination + let m := Bs.length + let rs := constructor.ctor.recArgsR source.uvars gen.elimination + let Fs := VExpr.liftTelN common Bs 0 + let D := VExpr.appN + (.bvar (d - 1 - constructor.owner + m + rs.length)) + (((constructor.ctor.resultIndicesR source.uvars + gen.elimination).map fun e => + (e.liftN d m).liftN rs.length) ++ + [VExpr.appN + (.const constructor.ctor.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (rs.length+m+d) source.nparams ++ + VExpr.bvarRevRange rs.length m)]) + let Dfin := VExpr.appN + (.bvar (d - 1 - constructor.owner + k + m)) + (((constructor.ctor.resultIndicesR source.uvars + gen.elimination).map fun e => e.liftN common m) ++ + [VExpr.appN + (.const constructor.ctor.raw.name gen.sourceLevels) + (VExpr.bvarRevRange (m+common) source.nparams ++ + VExpr.bvarRevRange 0 m)]) + have hownerLt : constructor.owner < d := by + rw [← howner] + simpa [d] using gen.family_ordinal_lt hfamily + have hrsLt : ∀ r ∈ rs, r.fieldIndex < m := by + intro r hr + obtain ⟨r₀, hr₀, rfl⟩ := + NormalizedCtor.recArgsR_mem (show + r ∈ constructor.ctor.recArgsR source.uvars gen.elimination from + by simpa [rs] using hr) + obtain ⟨family₀, hfamily₀, htarget, + ⟨Bview, hfield, hshape⟩, hsem⟩ := + (S.ctorWF constructor hconstructor).recursive r₀ hr₀ + have hj := (List.getElem?_eq_some_iff.1 hfield).1 + simp only [RecArg.instL] + dsimp only [m, Bs] + rw [NormalizedCtor.fieldsR_length] + rw [gen.flatCtor_fields_length hconstructor] + exact hj + have hik' : i < k := by + simpa [k] using hik + rw [VExpr.appN_append] + have hminorAt : + gen.minorTypes[i]? = + some ((gen.minorType constructor).liftN i) := by + simpa [BlockGenerationChecked.minorTypes, hci] using + (gen.minorTypesAux_getElem? gen.flatCtors 0 i) + have hlu0 : + (Fs.reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse)))[ + k - 1 - i + m]? = + some ((gen.minorType constructor).liftN i) := by + rw [getElem?_rstack_mid _ _ _ + (by simp [Fs, m, VExpr.liftTelN_length] <;> omega) + (by simp [Fs, m, k, gen.minorTypes_length, + VExpr.liftTelN_length] <;> omega), + show k - 1 - i + m - Fs.reverse.length = k - 1 - i by + simp [Fs, m, VExpr.liftTelN_length], + List.getElem?_reverse (by + rw [gen.minorTypes_length] + omega), + show gen.minorTypes.length - 1 - (k - 1 - i) = i by + rw [gen.minorTypes_length] + dsimp only [k] + omega, + hminorAt] + have hlu := Lookup.of_getElem? hlu0 + rw [VExpr.liftN_liftN, + show i + (k - 1 - i + m + 1) = m+k by omega] at hlu + have hminorEq : + (gen.minorType constructor).liftN (m+k) = + (VExpr.forallN Fs + ((VExpr.forallN + (BlockGenerationChecked.blockIHsFromRecArgs d m rs 0) + D).liftN k m)).liftN m := by + simp only [BlockGenerationChecked.minorType] + conv => lhs; rw [VExpr.liftN_forallN, + VExpr.liftTelN_liftTelN, + liftTelN_congr _ _ + (show d + (m+k) = common+m by + dsimp only [common] + omega), + show 0 + (VExpr.liftTelN d Bs 0).length = m by + simp [m, VExpr.liftTelN_length]] + conv => rhs; rw [VExpr.liftN_forallN, + VExpr.liftTelN_liftTelN, + show 0 + Fs.length = m by + simp [Fs, m, VExpr.liftTelN_length], + VExpr.liftN'_liftN_hi, + Nat.add_comm k m] + have hfields := HasType.appN_selfSpine + (env := env) (U := gen.recUvars) + (As := Fs) + (B := (VExpr.forallN + (BlockGenerationChecked.blockIHsFromRecArgs d m rs 0) + D).liftN k m) + (Δ := []) + (Γ := gen.minorTypes.reverse ++ + (gen.motiveTypes.reverse ++ gen.paramsTel.reverse)) + (f := .bvar (k-1-i+m)) + (by + have hb := VEnv.HasType.bvar + (env := env) (U := gen.recUvars) hlu + rw [hminorEq] at hb + simpa [Fs, VExpr.liftTelN_length] using hb) + simp only [List.length_nil, VExpr.liftN_zero] at hfields + rw [BlockGenerationChecked.blockIHs_liftN' + d m k rs hrsLt 0 D (cut := m) rfl] at hfields + have hD : D.liftN k (m+0+rs.length) = + Dfin.liftN rs.length := by + dsimp only [D, Dfin] + rw [VExpr.liftN_appN, VExpr.liftN_appN, + List.map_append, List.map_append, + List.map_map, List.map_map] + show VExpr.appN _ (_ ++ [_]) = VExpr.appN _ (_ ++ [_]) + congr 1 + · show + VExpr.bvar + (liftVar k + (d - 1 - constructor.owner + m + rs.length) + (m+0+rs.length)) = + VExpr.bvar + (liftVar rs.length + (d - 1 - constructor.owner + k + m) 0) + rw [liftVar_le (by omega), liftVar_le (Nat.zero_le _)] + congr 1 + omega + · congr 1 + · apply List.map_congr_left + intro e _ + simp only [Function.comp_apply] + rw [show m+0+rs.length = m+rs.length by omega, + VExpr.liftN_liftN_midN e d k rs.length (Nat.zero_le _)] + · congr 1 + simp only [] + rw [show m+0+rs.length = m+rs.length by omega, + VExpr.liftN_appN, VExpr.liftN_appN, + List.map_append, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (by omega), + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega), + bvarRevRange_liftN_ge _ _ _ _ (Nat.zero_le _), + bvarRevRange_liftN_ge _ _ _ _ (Nat.zero_le _), + VExpr.bvarRevRange_congr source.nparams + (show k + (rs.length+m+d) = + rs.length + (m+common) by + dsimp only [common] + omega), + VExpr.bvarRevRange_congr m + (show rs.length = rs.length+0 by omega)] + rfl + rw [hD] at hfields + have hres := hasType_appN_blockRuleIHs + (env := env) (gen := gen) (d := d) (m := m) (k := k) + (rs := rs) + (argOf := fun r => + BlockGenerationChecked.blockRuleCall common m + (gen.recBase m r.targetType) r) + (Dfin := Dfin) + (fun r hr => by + simpa [d, k, common, m, Bs] using + S.blockRuleCall_hasType hconstructor hrecs + (show r ∈ constructor.ctor.recArgsR source.uvars + gen.elimination by simpa [rs] using hr)) + hfields + simpa [Fs, Bs, m, rs, d, k, common, Dfin, + VExpr.liftTelN_length, Nat.add_assoc] using hres + +/-- Every flattened mutual iota rule is well formed once all family +recursors are available. -/ +theorem rule_WF {i : Nat} + {constructor : NormalizedBlockCtor} + (hci : gen.flatCtors[i]? = some constructor) + (hrecs : ∀ family ∈ gen.families, + env.constants (.str family.raw.name "rec") = + some (gen.recursor family)) : + (gen.rule i constructor).WF env := by + have hconstructor := List.mem_of_getElem? hci + refine ⟨?_, ?_⟩ + · show env.HasType gen.recUvars [] + (VExpr.lamN + (gen.paramsTel ++ gen.motiveTypes ++ gen.minorTypes ++ + VExpr.liftTelN + (gen.familyCount + gen.minorCount) + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination) 0) + _) + (VExpr.forallN _ _) + refine HasType.lamN (S.ruleBinders_onTel hconstructor) ?_ + simp only [List.reverse_append, List.append_nil, + List.append_assoc] + simpa only [Nat.add_assoc] using + S.recRuleApp_hasType hconstructor hrecs + · show env.HasType gen.recUvars [] + (VExpr.lamN + (gen.paramsTel ++ gen.motiveTypes ++ gen.minorTypes ++ + VExpr.liftTelN + (gen.familyCount + gen.minorCount) + (constructor.ctor.fieldsR source.uvars source.nparams + gen.elimination) 0) + _) + (VExpr.forallN _ _) + refine HasType.lamN (S.ruleBinders_onTel hconstructor) ?_ + simp only [List.reverse_append, List.append_nil, + List.append_assoc] + simpa only [Nat.add_assoc] using + S.minorApp_hasType hci hrecs + +end BlockGenerationEnv + +/-- The public inductive-declaration contract is monotone in its prefix +environment. This lets replay fixtures prepend independently verified +declarations without rebuilding the block's Stage-3 derivation. -/ +theorem WF.mono {env env' : VEnv} (henv : env ≤ env') + (H : VInductDecl.WF env decl) : VInductDecl.WF env' decl := by + refine ⟨H.1, fun ty hty => ?_⟩ + obtain ⟨htel, hctors⟩ := H.2 ty hty + exact ⟨htel.mono henv, fun c hc => + ⟨fieldsWF_mono henv (hctors c hc).1, (hctors c hc).2.mono henv⟩⟩ + +/-- Extract the carried index-spine typing of the recursive field at +position `q` from a `fieldsWF` chain. -/ +theorem fieldsWF_spine {U : Nat} {T : Name} {np : Nat} {env : VEnv} {l : VLevel} + {Is : List VExpr} : ∀ {Bs : List VExpr} {Γ₀ : List VExpr} {j₀ : Nat}, + fieldsWF U T np env l Is Γ₀ j₀ Bs → + ∀ q B, Bs[q]? = some B → isRecField U T np Is.length (j₀+q) B = true → + env.SpineWF U ((Bs.take q).reverse ++ Γ₀) + (VExpr.forallN (VExpr.liftTelN (j₀+q) Is 0) (.sort l)) (recFieldIdxs np B) (.sort l) + | [], _, _, _, q, B, hB, _ => by simp at hB + | B' :: Bs, Γ₀, j₀, ⟨_, hSp, hT⟩, 0, B, hB, hrec => by + obtain rfl : B' = B := by simpa using hB + simpa using hSp (by simpa using hrec) + | B' :: Bs, Γ₀, j₀, ⟨_, _, hT⟩, q+1, B, hB, hrec => by + have := fieldsWF_spine hT q B (by simpa using hB) + (by rwa [show j₀+1+q = j₀+(q+1) from by omega]) + rw [show j₀+1+q = j₀+(q+1) from by omega] at this + simpa [List.append_assoc] using this + +/-- Extract the uniform semantic evidence for any analyzed recursive +argument. Direct fields are re-expressed as the empty-telescope case; genuine +recursive Pi fields return the evidence stored by `fieldsWF`. -/ +theorem fieldsWF_recArg {U : Nat} {T : Name} {np : Nat} {env : VEnv} {l : VLevel} + {Is : List VExpr} : ∀ {Bs : List VExpr} {Γ₀ : List VExpr} {j₀ : Nat}, + fieldsWF U T np env l Is Γ₀ j₀ Bs → + ∀ q B r, Bs[q]? = some B → + recArg? U T np Is.length (j₀+q) B = some r → + r.WF U env l Is ((Bs.take q).reverse ++ Γ₀) + | [], _, _, _, q, B, r, hB, _ => by simp at hB + | B' :: Bs, Γ₀, j₀, ⟨hclass, hSp, htail⟩, 0, B, r, hB, hr => by + obtain rfl : B' = B := by simpa using hB + simp only [Nat.add_zero] at hr + rcases hclass with hdirect | hfun | ⟨hnone, -, -, -⟩ + · have hcanon := recArg?_of_isRecField hdirect + rw [hcanon] at hr + obtain rfl := Option.some.inj hr + exact ⟨trivial, by simpa [RecArg.WF] using hSp hdirect⟩ + · obtain ⟨r', hr', -, hwf⟩ := hfun + rw [hr'] at hr + obtain rfl := Option.some.inj hr + simpa using hwf + · rw [hnone] at hr + contradiction + | B' :: Bs, Γ₀, j₀, ⟨_, _, htail⟩, q+1, B, r, hB, hr => by + have h := fieldsWF_recArg htail q B r (by simpa using hB) + (by rwa [show j₀+1+q = j₀+(q+1) from by omega]) + simpa [List.append_assoc] using h + +namespace GenerationEnv + +variable {source : VInductDecl} {gen : GenerationChecked source} + {env : VEnv} (S : GenerationEnv gen env) +include S +-- `S` is included throughout so every lemma below is reachable as `S.lemma`, +-- including those whose statement and proof never mention it. +set_option linter.unusedSectionVars false + +/-- Checked field semantics for one paired constructor, re-indexed onto the +raw block header used by mixed generation. -/ +theorem viewFieldsWF {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) : + fieldsWF source.uvars gen.block.sourceType.name source.nparams env + gen.block.checked.resultLevel gen.block.checked.indices + gen.block.checked.params.reverse 0 ctor.view.fields := by + obtain ⟨c, hc, hview⟩ := gen.viewCtor_ofDirect hctor + have h := (S.checked.2 c hc).1 + rw [hview] + simpa [CheckedCtor.ofDirect, gen.block.uvars_eq, + gen.block.nparams_eq, gen.block.sourceType_name_eq] using h + +/-- Every retained recursive descriptor comes from re-analysis of the field +at its recorded position in the checked view. -/ +theorem viewRecArg_data {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) {r : RecArg} + (hr : r ∈ ctor.view.recursive) : + ∃ B, ctor.view.fields[r.fieldIndex]? = some B ∧ + recArg? source.uvars gen.block.sourceType.name source.nparams + gen.block.checked.indices.length r.fieldIndex B = some r := by + obtain ⟨c, -, hview⟩ := gen.viewCtor_ofDirect hctor + rw [hview] at hr ⊢ + simp only [CheckedCtor.ofDirect] at hr ⊢ + obtain ⟨B, hB, hrec⟩ := recArgs_getElem r hr + exact ⟨B, by simpa using hB, hrec⟩ + +/-- Retained recursive descriptors have the checked family-index arity. -/ +theorem viewRecArg_indices_length {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) {r : RecArg} + (hr : r ∈ ctor.view.recursive) : + r.indices.length = gen.block.checked.indices.length := by + obtain ⟨B, hB, hrec⟩ := S.viewRecArg_data hctor hr + exact (recArg?_eq hrec).2.2.2.1 + +/-- Retained recursive descriptors point inside the normalized field list. -/ +theorem viewRecArg_lt {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) {r : RecArg} + (hr : r ∈ ctor.view.recursive) : + r.fieldIndex < ctor.view.fields.length := by + obtain ⟨c, -, hview⟩ := gen.viewCtor_ofDirect hctor + rw [hview] at hr ⊢ + simp only [CheckedCtor.ofDirect] at hr ⊢ + simpa using recArgs_lt r hr + +/-- The checked-view certificate supplies recursive-argument semantics in +the exact normalized prefix context. -/ +theorem viewRecArg_WF {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) {r : RecArg} + (hr : r ∈ ctor.view.recursive) : + r.WF source.uvars env gen.block.checked.resultLevel + gen.block.checked.indices + ((ctor.view.fields.take r.fieldIndex).reverse ++ + gen.block.checked.params.reverse) := by + obtain ⟨B, hB, hrec⟩ := S.viewRecArg_data hctor hr + have h := fieldsWF_recArg (S.viewFieldsWF hctor) + r.fieldIndex B r hB (by simpa using hrec) + simpa using h + +/-- Definitionally equal raw/view contexts at any constructor-field prefix. +This is the structural bridge used before instantiation and weakening. -/ +theorem emittedPrefix_ctx {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) (j : Nat) : + env.IsDefEqCtx source.uvars [] + ((ctor.rawFields source.nparams |>.take j).reverse ++ + gen.block.checked.params.reverse) + ((ctor.view.fields.take j).reverse ++ + gen.block.checked.params.reverse) := by + have h := ((S.ctorWF ctor hctor).emittedTel.take + (source.nparams + j)).ctx + have hviewLen : + gen.block.checked.params.length = source.nparams := + gen.shape.2.1.symm.trans gen.shape.1 + have hraw : + (gen.block.checked.params ++ ctor.rawFields source.nparams).take + (source.nparams + j) = + gen.block.checked.params ++ + (ctor.rawFields source.nparams).take j := by + rw [← hviewLen] + rw [List.take_append, List.take_of_length_le (by omega)] + simp + have hview : + (gen.block.checked.params ++ ctor.view.fields).take + (source.nparams + j) = + gen.block.checked.params ++ ctor.view.fields.take j := by + rw [← hviewLen] + rw [List.take_append, List.take_of_length_le (by omega)] + simp + simp only [NormalizedCtor.emittedBinders, + NormalizedCtor.viewBinders] at h + rw [hraw, hview] at h + simpa [List.reverse_append] using h + +/-- The pointwise raw/view field-domain equality at a paired position, in +the preceding raw context. -/ +theorem emittedField_defeq {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) {j : Nat} {B B' : VExpr} + (hB : (ctor.rawFields source.nparams)[j]? = some B) + (hB' : ctor.view.fields[j]? = some B') : + ∃ u, env.IsDefEq source.uvars + ((ctor.rawFields source.nparams |>.take j).reverse ++ + gen.block.checked.params.reverse) + B B' (.sort u) := by + have hviewLen : + gen.block.checked.params.length = source.nparams := + gen.shape.2.1.symm.trans gen.shape.1 + have hraw : + getElem? + (gen.block.checked.params ++ ctor.rawFields source.nparams) + (source.nparams + j) = some B := by + rw [List.getElem?_append_right (by + rw [hviewLen] + omega), hviewLen] + simpa using hB + have hview : + getElem? + (gen.block.checked.params ++ ctor.view.fields) + (source.nparams + j) = some B' := by + rw [List.getElem?_append_right (by + rw [hviewLen] + omega), hviewLen] + simpa using hB' + obtain ⟨u, h⟩ := + (S.ctorWF ctor hctor).emittedTel.getElem? hraw hview + have htake : + (gen.block.checked.params ++ ctor.rawFields source.nparams).take + (source.nparams + j) = + gen.block.checked.params ++ + (ctor.rawFields source.nparams).take j := by + rw [← hviewLen] + rw [List.take_append, List.take_of_length_le (by omega)] + simp + simp only [NormalizedCtor.emittedBinders] at h + rw [htake, List.reverse_append] at h + exact ⟨u, by simpa using h⟩ + +/-- Recursive-argument semantics transported from the checked view into the +raw constructor-field prefix that mixed artifacts actually bind. -/ +theorem rawRecArg_WF {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) {r : RecArg} + (hr : r ∈ ctor.view.recursive) : + r.WF source.uvars env gen.block.checked.resultLevel + gen.block.checked.indices + ((ctor.rawFields source.nparams |>.take r.fieldIndex).reverse ++ + gen.block.checked.params.reverse) := by + exact (S.viewRecArg_WF hctor hr).defeqDFC S.ord + ((S.emittedPrefix_ctx hctor r.fieldIndex).symm S.ord) + +/-- The family telescope contract restricted to the index suffix. -/ +theorem rawIndexTel_defeq : + env.TelDefEq source.uvars gen.block.rawParams.reverse + gen.block.rawIndices gen.block.checked.indices := by + have h := S.familyTel.drop source.nparams + have hrawTake : + (gen.block.rawParams ++ gen.block.rawIndices).take source.nparams = + gen.block.rawParams := by + rw [← gen.shape.1, List.take_append, List.take_length] + simp + have hrawDrop : + (gen.block.rawParams ++ gen.block.rawIndices).drop source.nparams = + gen.block.rawIndices := by + rw [← gen.shape.1, List.drop_append] + simp + have hviewLen : + gen.block.checked.params.length = source.nparams := + gen.shape.2.1.symm.trans gen.shape.1 + have hviewDrop : + (gen.block.checked.params ++ gen.block.checked.indices).drop + source.nparams = + gen.block.checked.indices := by + rw [← hviewLen, List.drop_append] + simp + rw [hrawTake, hrawDrop, hviewDrop] at h + simpa using h + +/-- The raw/view index relation transported to the checked parameter base +used by generated artifacts. -/ +theorem emittedIndexTel_defeq : + env.TelDefEq source.uvars gen.block.checked.params.reverse + gen.block.rawIndices gen.block.checked.indices := + S.rawIndexTel_defeq.defeqDFC S.ord S.rawParams_ctx + +/-- The raw/view index telescope relation transported into recursor +universes. -/ +theorem rawIndexTel_defeq_rec : + env.TelDefEq (gen.recUvars) gen.paramsTel.reverse gen.idxTel + (gen.block.checked.indices.map + (VExpr.instL (gen.sourceLevels))) := by + have hdecl := S.emittedIndexTel_defeq.defeqDFC S.ord + (S.generationParams_ctx.symm S.ord) + have h := hdecl.instL + (U' := gen.recUvars) gen.sourceLevels_wf + simpa [GenerationChecked.paramsTel, GenerationChecked.idxTel, + List.map_reverse] using h + +/-- Transport all retained recursive-argument evidence into a mixed +minor/rule context. Raw constructor fields and raw family indices are the +emitted surfaces; recursive classification and index expressions remain the +checked-view data. -/ +theorem recArg_transport {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) {r₀ : RecArg} + (hr₀ : r₀ ∈ ctor.view.recursive) + (mid : List VExpr) {g : Nat} (hg : mid.length = g) + (As₂ : List VExpr) {d : Nat} (hd : As₂.length = d) : + let r := r₀.instL (gen.sourceLevels) + let As := VExpr.liftTelN d + (VExpr.liftTelN g r.binders r.fieldIndex) 0 + env.OnTel (gen.recUvars) + (As₂ ++ ((VExpr.liftTelN g + ((ctor.fieldsR source.uvars source.nparams gen.elimination).take + r.fieldIndex) 0).reverse ++ + (mid ++ gen.paramsTel.reverse))) As ∧ + env.SpineWF (gen.recUvars) + (As.reverse ++ + (As₂ ++ ((VExpr.liftTelN g + ((ctor.fieldsR source.uvars source.nparams gen.elimination).take + r.fieldIndex) 0).reverse ++ + (mid ++ gen.paramsTel.reverse)))) + (VExpr.forallN + (VExpr.liftTelN + (r.fieldIndex + r.binders.length + g + d) gen.idxTel 0) + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels)))) + (r.indices.map fun e => + (e.liftN g (r.fieldIndex + r.binders.length)).liftN d + r.binders.length) + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels))) := by + dsimp only + let ls := gen.sourceLevels + have hjlt : + r₀.fieldIndex < (ctor.rawFields source.nparams).length := by + have hview := S.viewRecArg_lt hctor hr₀ + have hfields := + (gen.shape.2.2.2.2.2 ctor hctor).2.2.2 + omega + have hsem := S.rawRecArg_WF hctor hr₀ + have htel₁ := hsem.1.instL + (U' := gen.recUvars) gen.sourceLevels_wf + have hsp₁ := hsem.2.instL + (U' := gen.recUvars) gen.sourceLevels_wf + have htelChecked : env.OnTel (gen.recUvars) + ((ctor.fieldsR source.uvars source.nparams gen.elimination |>.take + r₀.fieldIndex).reverse ++ + (gen.block.checked.params.map (VExpr.instL ls)).reverse) + (r₀.binders.map (VExpr.instL ls)) := by + simpa [NormalizedCtor.fieldsR, List.map_append, + List.map_reverse, List.map_take] using htel₁ + have hspChecked : env.SpineWF (gen.recUvars) + ((r₀.binders.map (VExpr.instL ls)).reverse ++ + ((ctor.fieldsR source.uvars source.nparams gen.elimination |>.take + r₀.fieldIndex).reverse ++ + (gen.block.checked.params.map (VExpr.instL ls)).reverse)) + (VExpr.instL ls + (VExpr.forallN + (VExpr.liftTelN + (r₀.fieldIndex + r₀.binders.length) + gen.block.checked.indices 0) + (.sort gen.block.checked.resultLevel))) + (r₀.indices.map (VExpr.instL ls)) + (VExpr.instL ls (.sort gen.block.checked.resultLevel)) := by + simpa [List.map_append, List.map_reverse, + NormalizedCtor.fieldsR, List.map_take] using hsp₁ + have hprefix := S.generationFieldPrefix_ctx_rec hctor r₀.fieldIndex + have htelGeneration := htelChecked.defeqDFC S.ord + (hprefix.symm S.ord) + have hfull := htelGeneration.extendDefEqCtx hprefix + have hspGeneration := hspChecked.defeqDFC S.ord (hfull.symm S.ord) + simp only [VExpr.instL_forallN, VExpr.liftTelN_instL] at htelGeneration hspGeneration + have hjlen : + ((ctor.fieldsR source.uvars source.nparams gen.elimination).take + r₀.fieldIndex).length = r₀.fieldIndex := by + simp only [NormalizedCtor.fieldsR, List.length_take, + List.length_map] + omega + have hidxField := S.rawIndexTel_defeq_rec.weakN S.ord + (Ctx.LiftN.zero (Γ := gen.paramsTel.reverse) + ((ctor.fieldsR source.uvars source.nparams gen.elimination).take + r₀.fieldIndex).reverse) + rw [List.length_reverse, hjlen] at hidxField + have hidxPrivate := hidxField.weakN S.ord + (Ctx.LiftN.zero + (Γ := ((ctor.fieldsR source.uvars source.nparams gen.elimination).take + r₀.fieldIndex).reverse ++ gen.paramsTel.reverse) + (r₀.binders.map (VExpr.instL ls)).reverse) + simp only [List.length_reverse, List.length_map] at hidxPrivate + rw [VExpr.liftTelN_liftTelN, + VExpr.liftTelN_liftTelN] at hidxPrivate + have hidxLen : + (r₀.indices.map (VExpr.instL ls)).length = + (VExpr.liftTelN + (r₀.fieldIndex + r₀.binders.length) gen.idxTel 0).length := by + simp only [List.length_map, VExpr.liftTelN_length, + GenerationChecked.idxTel] + exact (S.viewRecArg_indices_length hctor hr₀).trans + gen.shape.2.2.1.symm + have hspRaw := + hidxPrivate.spine_sort S.ord hspGeneration hidxLen + have W₁ := Ctx.LiftN.consTel (n := mid.length) + ((ctor.fieldsR source.uvars source.nparams gen.elimination).take + r₀.fieldIndex) + (Ctx.LiftN.zero (Γ := gen.paramsTel.reverse) mid) + rw [hjlen, Nat.add_zero] at W₁ + have htel₂ := htelGeneration.weakN S.ord W₁ + have hsp₂ := hspRaw.weakN S.ord + (Ctx.LiftN.consTel + (r₀.binders.map (VExpr.instL ls)) W₁) + rw [hg] at htel₂ hsp₂ + have W₂ := Ctx.LiftN.zero + (Γ := (VExpr.liftTelN g + ((ctor.fieldsR source.uvars source.nparams gen.elimination).take + r₀.fieldIndex) 0).reverse ++ + (mid ++ gen.paramsTel.reverse)) As₂ (h := hd) + have htel₃ := htel₂.weakN S.ord W₂ + have hsp₃ := hsp₂.weakN S.ord + (Ctx.LiftN.consTel + (VExpr.liftTelN g + (r₀.binders.map (VExpr.instL ls)) r₀.fieldIndex) W₂) + refine ⟨?_, ?_⟩ + · simpa [ls, RecArg.instL, List.append_assoc] using htel₃ + · simp only [List.length_map, VExpr.liftTelN_length, + Nat.add_zero] at hsp₃ + rw [VExpr.liftN_forallN, VExpr.liftN_forallN, + VExpr.liftTelN_liftTelN_hi' + (r₀.fieldIndex + r₀.binders.length) g _ 0 (by omega), + VExpr.liftTelN_liftTelN_mid + (r₀.fieldIndex + r₀.binders.length + g) d _ 0 + r₀.binders.length (Nat.zero_le _) (by omega)] at hsp₃ + rw [show r₀.binders.length + r₀.fieldIndex = + r₀.fieldIndex + r₀.binders.length from Nat.add_comm _ _] at hsp₃ + simpa [ls, RecArg.instL, VExpr.instL, VExpr.liftN, + List.map_map, Function.comp_def, List.append_assoc] using hsp₃ + +end GenerationEnv + +/-- Syntactic lifting law for the mixed motive. -/ +theorem GenerationChecked.motiveType_liftN {source : VInductDecl} + (gen : GenerationChecked source) (n : Nat) : + gen.motiveType.liftN n = + VExpr.forallN (VExpr.liftTelN n gen.idxTel 0) + (.forallE + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange (n + gen.idxTel.length) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)) + (.sort gen.motiveLevel)) := by + rw [show gen.motiveType = + VExpr.forallN gen.idxTel + (.forallE + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange gen.idxTel.length source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)) + (.sort gen.motiveLevel)) from rfl, + VExpr.liftN_forallN] + refine congrArg _ ?_ + show VExpr.forallE _ _ = VExpr.forallE _ _ + refine congr (congrArg _ ?_) rfl + rw [VExpr.liftN_appN, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (by omega), + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega)] + rfl + +/-- Apply a mixed motive variable to a raw-index spine and a typed family +major. -/ +theorem GenerationChecked.motiveVarApp_hasType {source : VInductDecl} + (gen : GenerationChecked source) {env : VEnv} {l : VLevel} + {Γ : List VExpr} {K : Nat} {idxs : List VExpr} {a : VExpr} + (hM : env.HasType (gen.recUvars) Γ (.bvar K) + (gen.motiveType.liftN (K+1))) + (hidx : env.SpineWF (gen.recUvars) Γ + (VExpr.forallN (VExpr.liftTelN (K+1) gen.idxTel 0) + (.sort l)) + idxs (.sort l)) + (hlen : idxs.length = gen.idxTel.length) + (ha : env.HasType (gen.recUvars) Γ a + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange (K+1) source.nparams ++ idxs))) : + env.HasType (gen.recUvars) Γ + (VExpr.appN (.bvar K) (idxs ++ [a])) (.sort gen.motiveLevel) := by + rw [gen.motiveType_liftN] at hM + have hshape := hidx.retarget + (by simpa only [VExpr.liftTelN_length] using hlen) + (.forallE + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange (K+1 + gen.idxTel.length) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)) + (.sort gen.motiveLevel)) + rw [VExpr.instRev_forallE_sort, VExpr.instRev_appN, + VExpr.instRev_closedN _ + (C := .const gen.block.sourceType.name + (gen.sourceLevels)) trivial, + List.map_append, + VExpr.map_instRev_bvarRevRange_ge _ _ _ (by rw [hlen]; omega), + show K+1+gen.idxTel.length-idxs.length = K+1 from by + rw [hlen, Nat.add_sub_cancel], + VExpr.bvarRevRange_congr' 0 hlen.symm, + VExpr.map_instRev_bvarRevRange] at hshape + rw [hlen] at hshape + have hApp := hshape.hasType_appN hM + rw [VExpr.appN_append] + exact HasType.app hApp (by simpa using ha) + +/-- Mixed minor generation preserves constructor-list arity at every suffix +depth. -/ +theorem GenerationChecked.minorTypesAux_length + {source : VInductDecl} (gen : GenerationChecked source) : + ∀ (ctors : List NormalizedCtor) (i : Nat), + (gen.minorTypesAux ctors i).length = ctors.length + | [], _ => rfl + | _ :: ctors, i => by + simp [GenerationChecked.minorTypesAux, + gen.minorTypesAux_length ctors (i+1)] + +/-- The complete mixed minor telescope has one entry per paired raw/view +constructor. -/ +theorem GenerationChecked.minorTypes_length + {source : VInductDecl} (gen : GenerationChecked source) : + gen.minorTypes.length = gen.block.ctorPairs.length := by + simpa [GenerationChecked.minorTypes] using + gen.minorTypesAux_length gen.block.ctorPairs 0 + +/-- Positional lookup through mixed minor generation. -/ +theorem GenerationChecked.minorTypesAux_getElem? + {source : VInductDecl} (gen : GenerationChecked source) : + ∀ (ctors : List NormalizedCtor) (i q : Nat), + (gen.minorTypesAux ctors i)[q]? = + ctors[q]?.map fun ctor => + VExpr.liftN (i+q) + (GenerationChecked.minorType + (source := source) ctor gen.elimination) + | [], _, q => by simp [GenerationChecked.minorTypesAux] + | _ :: _, _, 0 => by simp [GenerationChecked.minorTypesAux] + | _ :: ctors, i, q+1 => by + simp only [GenerationChecked.minorTypesAux, + List.getElem?_cons_succ] + rw [gen.minorTypesAux_getElem? ctors (i+1) q, + show i+1+q = i+(q+1) by omega] + +namespace GenerationEnv + +variable {source : VInductDecl} {gen : GenerationChecked source} + {env : VEnv} (S : GenerationEnv gen env) +include S +-- `S` is included throughout so every lemma below is reachable as `S.lemma`, +-- including those whose statement and proof never mention it. +set_option linter.unusedSectionVars false + +/-- One mixed generalized induction-hypothesis entry is a type. The bound +field keeps its raw domain; `emittedField_defeq` converts it to the retained +recursive-Pi view only at the semantic application point. -/ +theorem recArgMinor_isType {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) {r : RecArg} + (hrmem : r ∈ ctor.recArgsR source.uvars gen.elimination) + (Δ : List VExpr) (p : Nat) (hΔ : Δ.length = p) : + env.IsType (gen.recUvars) + (Δ ++ + (VExpr.liftTelN 1 + (ctor.fieldsR source.uvars source.nparams gen.elimination) 0).reverse ++ + (gen.motiveType :: gen.paramsTel.reverse)) + (r.minorIH + (ctor.fieldsR source.uvars source.nparams gen.elimination).length p) := by + obtain ⟨r₀, hr₀, rfl⟩ := + NormalizedCtor.recArgsR_mem hrmem + obtain ⟨Bview, hBview, hrec⟩ := + S.viewRecArg_data hctor hr₀ + let ls := gen.sourceLevels + let r := r₀.instL ls + let Bs := ctor.fieldsR source.uvars source.nparams gen.elimination + let m := Bs.length + let j := r₀.fieldIndex + let Fs := VExpr.liftTelN 1 Bs 0 + let As := r.minorBinders m p + let idxs := r.indices.map fun e => + (e.liftN 1 (r.fieldIndex+r.binders.length)).liftN + (m-r.fieldIndex+p) r.binders.length + let Γ := Δ ++ Fs.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse) + have hjm : j < m := by + have hview := S.viewRecArg_lt hctor hr₀ + have hfields := + (gen.shape.2.2.2.2.2 ctor hctor).2.2.2 + simp only [j, m, Bs, NormalizedCtor.fieldsR_length] + omega + have hjraw : + r₀.fieldIndex < (ctor.rawFields source.nparams).length := by + simpa [j, m, Bs, NormalizedCtor.fieldsR_length] using hjm + let Braw := (ctor.rawFields source.nparams)[r₀.fieldIndex] + have hBraw : + (ctor.rawFields source.nparams)[r₀.fieldIndex]? = + some Braw := + List.getElem?_eq_getElem hjraw + have hFsLen : Fs.length = m := by + simp [Fs, m, VExpr.liftTelN_length] + have hstackLen : + (Δ ++ (Fs.drop j).reverse).length = m-j+p := by + simp only [List.length_append, List.length_reverse, + List.length_drop, hFsLen, hΔ] + omega + have ht := S.recArg_transport hctor hr₀ [gen.motiveType] rfl + (Δ ++ (Fs.drop j).reverse) hstackLen + simp only [List.length_singleton, RecArg.instL] at ht + have hctx : + (Δ ++ (Fs.drop j).reverse) ++ + ((VExpr.liftTelN 1 (Bs.take j) 0).reverse ++ + ([gen.motiveType] ++ gen.paramsTel.reverse)) = Γ := by + dsimp only [Γ, Fs] + rw [← VExpr.liftTelN_take, List.append_assoc, + ← List.append_assoc + ((VExpr.liftTelN 1 Bs 0).drop j).reverse, + ← List.reverse_append, List.take_append_drop, + List.singleton_append, ← List.append_assoc] + dsimp only [j] at ht hctx + have htel : env.OnTel (gen.recUvars) Γ As := by + rw [hctx] at ht + simpa [r, As, m, j, Bs, RecArg.instL, + RecArg.minorBinders] using ht.1 + have hsp : env.SpineWF (gen.recUvars) + (As.reverse ++ Γ) + (VExpr.forallN + (VExpr.liftTelN + (m+p+r.binders.length+1) gen.idxTel 0) + (.sort (gen.block.checked.resultLevel.inst ls))) + idxs + (.sort (gen.block.checked.resultLevel.inst ls)) := by + rw [hctx] at ht + simpa [r, As, idxs, m, j, Bs, ls, RecArg.instL, + RecArg.minorBinders, + List.append_assoc, + show j + r₀.binders.length + 1 + (m-j+p) = + m+p+r₀.binders.length+1 from by omega] using ht.2 + have hF : Γ[m-1-j+p]? = + some ((Braw.instL ls).liftN 1 j) := by + dsimp only [Γ, Fs] + rw [getElem?_stack_mid Δ + (VExpr.liftTelN 1 Bs 0).reverse + (gen.motiveType :: gen.paramsTel.reverse) + (i := m-1-j+p) (by rw [hΔ]; omega) + (by simp only [hΔ, List.length_reverse, + VExpr.liftTelN_length]; omega), + show m - 1 - j + p - Δ.length = m - 1 - j from by + rw [hΔ] + omega, + List.getElem?_reverse (by rw [hFsLen]; omega), + VExpr.liftTelN_length, + show m - 1 - (m - 1 - j) = j from by omega, + VExpr.liftTelN_getElem?, + NormalizedCtor.fieldsR_getElem?, hBraw] + simp [ls] + have hlu := Lookup.of_getElem? hF + rw [show m-1-j+p+1 = m-j+p from by omega] at hlu + dsimp only [j, r] at hlu + have hf0 := VEnv.HasType.bvar + (env := env) (U := gen.recUvars) hlu + obtain ⟨u, hdom₀⟩ := + S.emittedField_defeq hctor hBraw hBview + have hdom₁ := hdom₀.instL + (U' := gen.recUvars) gen.sourceLevels_wf + have hdomChecked : env.IsDefEq (gen.recUvars) + ((ctor.fieldsR source.uvars source.nparams gen.elimination |>.take + r₀.fieldIndex).reverse ++ + (gen.block.checked.params.map (VExpr.instL ls)).reverse) + (Braw.instL ls) (Bview.instL ls) ((VExpr.sort u).instL ls) := by + simpa [NormalizedCtor.fieldsR, List.map_append, + List.map_reverse, List.map_take] using hdom₁ + have hprefix := S.generationFieldPrefix_ctx_rec hctor r₀.fieldIndex + have hdomGeneration := hdomChecked.defeqDFC S.ord + (hprefix.symm S.ord) + have hjlen : (Bs.take r₀.fieldIndex).length = + r₀.fieldIndex := by + simp only [Bs, NormalizedCtor.fieldsR, + List.length_take, List.length_map] + omega + have Wmid := Ctx.LiftN.consTel (n := 1) + (Bs.take r₀.fieldIndex) + (Ctx.LiftN.zero (Γ := gen.paramsTel.reverse) + [gen.motiveType]) + rw [hjlen, Nat.add_zero] at Wmid + have hdom₂ := hdomGeneration.weakN S.ord Wmid + have Wstack := Ctx.LiftN.zero + (Γ := (VExpr.liftTelN 1 (Bs.take j) 0).reverse ++ + ([gen.motiveType] ++ gen.paramsTel.reverse)) + (Δ ++ (Fs.drop j).reverse) (h := hstackLen) + have hdom₃ := hdom₂.weakN S.ord Wstack + rw [hctx] at hdom₃ + have hfView := hdom₃.defeq hf0 + have hfield := recArg_minor_fieldType hrec m p + (by simpa [j] using hjm) gen.elimination + simp only [RecArg.instL] at hfield + rw [hfield] at hfView + have hf := hfView.weakN S.ord + (Ctx.LiftN.zero (Γ := Γ) As.reverse) + have hmajor := VEnv.HasType.appN_selfSpine + (env := env) (U := gen.recUvars) + (As := As) + (B := VExpr.appN + (.const gen.block.sourceType.name ls) + (VExpr.bvarRevRange + (m+p+r.binders.length+1) source.nparams ++ idxs)) + (Δ := []) (Γ := Γ) (by + simpa [As, r, idxs, j, ls, RecArg.instL, + List.length_reverse, List.map_map, + Function.comp_def] using hf) + simp only [List.length_nil, VExpr.liftN_zero, + List.nil_append] at hmajor + have hAsLen : As.length = r.binders.length := by + simp [As, RecArg.minorBinders, VExpr.liftTelN_length] + change env.HasType (gen.recUvars) (As.reverse ++ Γ) + ((VExpr.bvar (m-1-r.fieldIndex+p+As.length)).appN + (VExpr.bvarRevRange 0 As.length)) + (VExpr.appN + (.const gen.block.sourceType.name ls) + (VExpr.bvarRevRange + (m+p+r.binders.length+1) source.nparams ++ idxs)) at hmajor + rw [hAsLen] at hmajor + have hMget : + (As.reverse ++ Γ)[m+p+r.binders.length]? = + some gen.motiveType := by + have hM0 := getElem?_rstack3 As.reverse + (Δ ++ Fs.reverse) gen.motiveType gen.paramsTel.reverse + (i := m+p+r.binders.length) + (by + simp [As, RecArg.minorBinders, r, m, Fs, hΔ, + VExpr.liftTelN_length, RecArg.instL] + omega) + simpa [Γ, List.append_assoc] using hM0 + have hM := VEnv.HasType.bvar + (env := env) (U := gen.recUvars) + (Lookup.of_getElem? hMget) + have hlen : idxs.length = gen.idxTel.length := by + simpa [idxs, r, RecArg.instL, + GenerationChecked.idxTel] using + (S.viewRecArg_indices_length hctor hr₀).trans + gen.shape.2.2.1.symm + have hbody := gen.motiveVarApp_hasType + (l := gen.block.checked.resultLevel.inst ls) + hM hsp hlen hmajor + refine IsType.forallN htel ⟨gen.motiveLevel, ?_⟩ + simpa [RecArg.minorIH, r, As, idxs, m, Γ, Fs, Bs, + List.append_assoc] using hbody + +/-- The complete mixed functional-IH telescope is well formed at any suffix +and depth. -/ +theorem ihs_onTel {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) : + ∀ (rsSuf : List RecArg), + (∀ r ∈ rsSuf, r ∈ ctor.recArgsR source.uvars gen.elimination) → + ∀ (Δ : List VExpr) (p : Nat), Δ.length = p → + env.OnTel (gen.recUvars) + (Δ ++ + (VExpr.liftTelN 1 + (ctor.fieldsR source.uvars source.nparams gen.elimination) 0).reverse ++ + (gen.motiveType :: gen.paramsTel.reverse)) + (ihsFromRecArgs + (ctor.fieldsR source.uvars source.nparams gen.elimination).length rsSuf p) + | [], _, _, _, _ => trivial + | r :: rsSuf, hqs, Δ, p, hΔ => + ⟨S.recArgMinor_isType hctor + (hqs r (.head _)) Δ p hΔ, + GenerationEnv.ihs_onTel hctor rsSuf + (fun q hq => hqs q (.tail _ hq)) + (_ :: Δ) (p+1) (by simp [hΔ])⟩ + +/-- The retained constructor result has exactly the checked family-index +arity. -/ +theorem viewResultIndices_length {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) : + ctor.view.resultIndices.length = + gen.block.checked.indices.length := by + obtain ⟨c, hc, hview⟩ := gen.viewCtor_ofDirect hctor + have hstage := + (gen.block.checked.direct_anatomy.2.2.2.2.2 c hc).2.2 + have hlen := (stage3Ctor_eq hstage).2.1 + rw [hview] + rw [gen.block.nparams_eq] + simpa [CheckedCtor.ofDirect] using hlen + +/-- Checked result-index spine semantics for a paired constructor. -/ +theorem viewResultSpine {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) : + env.SpineWF source.uvars + (ctor.view.fields.reverse ++ + gen.block.checked.params.reverse) + (VExpr.forallN + (VExpr.liftTelN ctor.view.fields.length + gen.block.checked.indices 0) + (.sort gen.block.checked.resultLevel)) + ctor.view.resultIndices + (.sort gen.block.checked.resultLevel) := by + obtain ⟨c, hc, hview⟩ := gen.viewCtor_ofDirect hctor + have h := (S.checked.2 c hc).2 + rw [hview] + simpa [CheckedCtor.ofDirect, gen.block.uvars_eq, + gen.block.nparams_eq] using h + +/-- Constructor-result semantics transported to the exact emitted +checked-parameter/raw-field context and raw family-index telescope. -/ +theorem rawResultSpine {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) : + env.SpineWF source.uvars + ((ctor.rawFields source.nparams).reverse ++ + gen.block.checked.params.reverse) + (VExpr.forallN + (VExpr.liftTelN + (ctor.rawFields source.nparams).length + gen.block.rawIndices 0) + (.sort gen.block.checked.resultLevel)) + ctor.view.resultIndices + (.sort gen.block.checked.resultLevel) := by + have hctx := (S.ctorWF ctor hctor).emittedTel.ctx + simp only [NormalizedCtor.emittedBinders, + NormalizedCtor.viewBinders, List.reverse_append, + List.append_nil] at hctx + have hsp := (S.viewResultSpine hctor).defeqDFC S.ord + (hctx.symm S.ord) + have hfields := + (gen.shape.2.2.2.2.2 ctor hctor).2.2.2 + rw [← hfields] at hsp + have hidx := S.emittedIndexTel_defeq.weakN S.ord + (Ctx.LiftN.zero (Γ := gen.block.checked.params.reverse) + (ctor.rawFields source.nparams).reverse) + rw [List.length_reverse] at hidx + have hlen : + ctor.view.resultIndices.length = + (VExpr.liftTelN + (ctor.rawFields source.nparams).length + gen.block.rawIndices 0).length := by + simp only [VExpr.liftTelN_length] + exact (S.viewResultIndices_length hctor).trans + gen.shape.2.2.1.symm + exact hidx.spine_sort S.ord hsp hlen + +/-- Transport the mixed constructor result spine into a minor or rule +context. -/ +theorem result_transport {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) + (mid : List VExpr) {g : Nat} (hg : mid.length = g) + (As₂ : List VExpr) {d : Nat} (hd : As₂.length = d) : + env.SpineWF (gen.recUvars) + (As₂ ++ + ((VExpr.liftTelN g + (ctor.fieldsR source.uvars source.nparams gen.elimination) 0).reverse ++ + (mid ++ gen.paramsTel.reverse))) + (VExpr.forallN + (VExpr.liftTelN + ((ctor.fieldsR source.uvars source.nparams gen.elimination).length + g + d) + gen.idxTel 0) + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels)))) + ((ctor.resultIndicesR source.uvars gen.elimination).map fun e => + (e.liftN g + (ctor.fieldsR source.uvars source.nparams gen.elimination).length).liftN d) + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels))) := by + let ls := gen.sourceLevels + have h1 := (S.rawResultSpine hctor).instL + (U' := gen.recUvars) gen.sourceLevels_wf + have h1Checked : env.SpineWF (gen.recUvars) + ((ctor.fieldsR source.uvars source.nparams gen.elimination).reverse ++ + (gen.block.checked.params.map (VExpr.instL ls)).reverse) + (VExpr.instL ls + (VExpr.forallN + (VExpr.liftTelN (ctor.rawFields source.nparams).length + gen.block.rawIndices 0) + (.sort gen.block.checked.resultLevel))) + (ctor.view.resultIndices.map (VExpr.instL ls)) + ((VExpr.sort gen.block.checked.resultLevel).instL ls) := by + simpa [NormalizedCtor.fieldsR, List.map_append, + List.map_reverse] using h1 + have hfieldsCtx := (S.generationFields_onTel_rec hctor).extendDefEqCtx + S.generationParams_ctx_rec + have h1Generation := h1Checked.defeqDFC S.ord + (hfieldsCtx.symm S.ord) + rw [VExpr.instL_forallN, VExpr.liftTelN_instL, + show + (gen.block.rawIndices.map (VExpr.instL ls)) = + gen.idxTel by rfl] at h1Generation + rw [← NormalizedCtor.fieldsR_length + (source := source) (mode := gen.elimination) ctor] at h1Generation + have W₁ := Ctx.LiftN.consTel (n := mid.length) + (ctor.fieldsR source.uvars source.nparams gen.elimination) + (Ctx.LiftN.zero (Γ := gen.paramsTel.reverse) mid) + rw [Nat.add_zero] at W₁ + have h2 := h1Generation.weakN S.ord W₁ + rw [VExpr.liftN_forallN, hg] at h2 + have h3 := h2.weakN S.ord + (Ctx.LiftN.zero (Γ := _) As₂ (h := hd)) + rw [VExpr.liftN_forallN] at h3 + rw [VExpr.liftTelN_liftTelN_hi' + (ctor.fieldsR source.uvars source.nparams gen.elimination).length + g _ 0 (by omega), + VExpr.liftTelN_liftTelN] at h3 + simpa [ls, NormalizedCtor.resultIndicesR, + VExpr.instL, VExpr.liftN, List.map_map, + Function.comp_def, List.append_assoc] using h3 + +/-- The exact emitted constructor application transported into recursor +universes, before inserting the motive or any induction-hypothesis stack. -/ +theorem ctorApp_emitted_rec {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) : + env.HasType (gen.recUvars) + ((ctor.fieldsR source.uvars source.nparams gen.elimination).reverse ++ + gen.paramsTel.reverse) + (VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (ctor.fieldsR source.uvars source.nparams gen.elimination).length + source.nparams ++ + VExpr.bvarRevRange 0 + (ctor.fieldsR source.uvars source.nparams gen.elimination).length)) + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (ctor.fieldsR source.uvars source.nparams gen.elimination).length + source.nparams ++ + ctor.resultIndicesR source.uvars gen.elimination)) := by + let ls := gen.sourceLevels + have h := (S.ctorApp_emitted_decl hctor).instL + (U' := gen.recUvars) gen.sourceLevels_wf + have hChecked : env.HasType (gen.recUvars) + ((ctor.fieldsR source.uvars source.nparams gen.elimination).reverse ++ + (gen.block.checked.params.map (VExpr.instL ls)).reverse) + ((VExpr.appN + (.const ctor.raw.name (VLevel.params source.uvars)) + (VExpr.bvarRevRange + (ctor.rawFields source.nparams).length source.nparams ++ + VExpr.bvarRevRange 0 + (ctor.rawFields source.nparams).length)).instL ls) + ((ctor.resultTarget gen.block).instL ls) := by + simpa [NormalizedCtor.fieldsR, List.map_append, + List.map_reverse] using h + have hfieldsCtx := (S.generationFields_onTel_rec hctor).extendDefEqCtx + S.generationParams_ctx_rec + have hGeneration := hChecked.defeqDFC S.ord + (hfieldsCtx.symm S.ord) + rw [← NormalizedCtor.fieldsR_length + (source := source) (mode := gen.elimination) ctor] at hGeneration + simpa [ls, NormalizedCtor.fieldsR, + GenerationChecked.paramsTel, + NormalizedCtor.resultTarget, + NormalizedCtor.resultIndicesR, + VExpr.instL_appN, List.map_append, + bvarRevRange_instL, List.map_reverse, + VExpr.instL, VLevel.params_map_inst_params'] using hGeneration + +/-- Transport the emitted constructor application under binders inserted +between parameters and fields, then under an arbitrary top stack. -/ +theorem ctorApp_transport {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) + (mid : List VExpr) {g : Nat} (hg : mid.length = g) + (As₂ : List VExpr) {d : Nat} (hd : As₂.length = d) : + env.HasType (gen.recUvars) + (As₂ ++ + ((VExpr.liftTelN g + (ctor.fieldsR source.uvars source.nparams gen.elimination) 0).reverse ++ + (mid ++ gen.paramsTel.reverse))) + (VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (d + (g + + (ctor.fieldsR source.uvars source.nparams gen.elimination).length)) + source.nparams ++ + VExpr.bvarRevRange d + (ctor.fieldsR source.uvars source.nparams gen.elimination).length)) + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (d + (g + + (ctor.fieldsR source.uvars source.nparams gen.elimination).length)) + source.nparams ++ + (ctor.resultIndicesR source.uvars gen.elimination).map fun e => + (e.liftN g + (ctor.fieldsR source.uvars source.nparams gen.elimination).length).liftN d)) := by + let Bs := ctor.fieldsR source.uvars source.nparams gen.elimination + have W₁ := Ctx.LiftN.consTel (n := mid.length) Bs + (Ctx.LiftN.zero (Γ := gen.paramsTel.reverse) mid) + rw [Nat.add_zero] at W₁ + have h₁ := (S.ctorApp_emitted_rec hctor).weakN S.ord W₁ + rw [hg] at h₁ + have hmid : env.HasType (gen.recUvars) + ((VExpr.liftTelN g Bs 0).reverse ++ + (mid ++ gen.paramsTel.reverse)) + (VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange (g + Bs.length) source.nparams ++ + VExpr.bvarRevRange 0 Bs.length)) + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange (g + Bs.length) source.nparams ++ + (ctor.resultIndicesR source.uvars gen.elimination).map + (VExpr.liftN g · Bs.length))) := by + simpa [Bs, VExpr.liftN_appN, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (Nat.le_refl _), + VExpr.bvarRevRange_liftN_high + Bs.length 0 g Bs.length (by omega), + VExpr.liftN] using h₁ + have htop := hmid.weakN S.ord + (Ctx.LiftN.zero (Γ := _) As₂ (h := hd)) + simpa [Bs, VExpr.liftN_appN, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (Nat.zero_le _), + VExpr.liftN, List.map_map, Function.comp_def, + List.append_assoc] using htop + +/-- Constructor application in the exact mixed minor-premise context. -/ +theorem ctorAppMinor_hasType {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) (Δ : List VExpr) : + env.HasType (gen.recUvars) + (Δ ++ + (VExpr.liftTelN 1 + (ctor.fieldsR source.uvars source.nparams gen.elimination) 0).reverse ++ + (gen.motiveType :: gen.paramsTel.reverse)) + (VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (Δ.length + + (ctor.fieldsR source.uvars source.nparams gen.elimination).length + 1) + source.nparams ++ + VExpr.bvarRevRange Δ.length + (ctor.fieldsR source.uvars source.nparams gen.elimination).length)) + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (Δ.length + + (ctor.fieldsR source.uvars source.nparams gen.elimination).length + 1) + source.nparams ++ + (ctor.resultIndicesR source.uvars gen.elimination).map fun e => + (e.liftN 1 + (ctor.fieldsR source.uvars source.nparams gen.elimination).length).liftN + Δ.length)) := by + have h := S.ctorApp_transport hctor [gen.motiveType] + (g := 1) rfl Δ (d := Δ.length) rfl + rw [VExpr.bvarRevRange_congr source.nparams + (show + Δ.length + + (1 + + (ctor.fieldsR source.uvars source.nparams gen.elimination).length) = + Δ.length + + (ctor.fieldsR source.uvars source.nparams gen.elimination).length + 1 by + omega)] at h + simpa [List.append_assoc] using h + +/-- The exact raw field telescope used by a mixed minor is well formed after +universe instantiation and insertion of the motive. -/ +theorem fields_onTel_minor {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) : + env.OnTel (gen.recUvars) + (gen.motiveType :: gen.paramsTel.reverse) + (VExpr.liftTelN 1 + (ctor.fieldsR source.uvars source.nparams gen.elimination) 0) := by + have hout := (S.generationFields_onTel_rec hctor).weakN S.ord + (Ctx.LiftN.zero (Γ := gen.paramsTel.reverse) + [gen.motiveType]) + simpa using hout + +/-- Every mixed constructor minor is a type over the checked parameter telescope +and mixed motive. Raw field syntax is preserved; recursive classifications +and result indices come from the checked view. -/ +theorem minor_isType {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) : + env.IsType (gen.recUvars) + (gen.motiveType :: gen.paramsTel.reverse) + (GenerationChecked.minorType + (source := source) ctor gen.elimination) := by + let Bs := ctor.fieldsR source.uvars source.nparams gen.elimination + let m := Bs.length + let rs := ctor.recArgsR source.uvars gen.elimination + let IHs := ihsFromRecArgs m rs 0 + let Fs := VExpr.liftTelN 1 Bs 0 + let Γ := IHs.reverse ++ + (Fs.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse)) + simp only [GenerationChecked.minorType] + refine IsType.forallN (by + simpa [Bs] using S.fields_onTel_minor hctor) ?_ + refine IsType.forallN (by + simpa [Bs, m, rs, IHs, Fs, List.append_assoc] using + S.ihs_onTel hctor rs (fun r hr => hr) [] 0 rfl) ?_ + have hrlen : IHs.reverse.length = rs.length := by + simp [IHs, ihsFromRecArgs_length] + have hMget : Γ[m + rs.length]? = + some gen.motiveType := by + have h := getElem?_rstack3 IHs.reverse Fs.reverse + gen.motiveType gen.paramsTel.reverse + (i := m + rs.length) + (by + simp [IHs, Fs, m, ihsFromRecArgs_length, + VExpr.liftTelN_length] + omega) + simpa [Γ] using h + have hM : env.HasType (gen.recUvars) Γ + (.bvar (m + rs.length)) + (gen.motiveType.liftN (m + rs.length + 1)) := + VEnv.HasType.bvar (Lookup.of_getElem? hMget) + have hSp := S.result_transport hctor [gen.motiveType] + (g := 1) rfl IHs.reverse + (d := rs.length) hrlen + rw [show m + 1 + rs.length = + m + rs.length + 1 by omega] at hSp + have hSp' : env.SpineWF (gen.recUvars) Γ + (VExpr.forallN + (VExpr.liftTelN (m + rs.length + 1) + gen.idxTel 0) + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels)))) + ((ctor.resultIndicesR source.uvars gen.elimination).map fun e => + (e.liftN 1 m).liftN rs.length) + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels))) := by + simpa [Γ, Fs, Bs, m, List.append_assoc] using hSp + have hlen : + ((ctor.resultIndicesR source.uvars gen.elimination).map fun e => + (e.liftN 1 m).liftN rs.length).length = + gen.idxTel.length := by + simp only [List.length_map, NormalizedCtor.resultIndicesR, + GenerationChecked.idxTel] + exact (S.viewResultIndices_length hctor).trans + gen.shape.2.2.1.symm + have hctorApp := S.ctorAppMinor_hasType hctor IHs.reverse + rw [hrlen, + VExpr.bvarRevRange_congr source.nparams + (show rs.length + m + 1 = m + rs.length + 1 by omega)] at hctorApp + have hctorApp' : env.HasType (gen.recUvars) Γ + (VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (m + rs.length + 1) source.nparams ++ + VExpr.bvarRevRange rs.length m)) + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (m + rs.length + 1) source.nparams ++ + (ctor.resultIndicesR source.uvars gen.elimination).map fun e => + (e.liftN 1 m).liftN rs.length)) := by + simpa [Γ, Fs, Bs, m, IHs, rs, + List.append_assoc] using hctorApp + have hbody := gen.motiveVarApp_hasType hM hSp' hlen hctorApp' + rw [VExpr.bvarRevRange_congr source.nparams + (show m + rs.length + 1 = + rs.length + m + 1 by omega)] at hbody + exact ⟨gen.motiveLevel, by + simpa [Γ, Fs, Bs, m, rs, IHs, + List.append_assoc] using hbody⟩ + +/-- The checked parameter telescope is well formed in recursor universes. -/ +theorem paramsTel_onTel : + env.OnTel (gen.recUvars) [] gen.paramsTel := by + have h := S.generationFamily_onTel.instL + (U' := gen.recUvars) gen.sourceLevels_wf + have h' : env.OnTel (gen.recUvars) [] + (gen.paramsTel ++ gen.idxTel) := by + simpa [GenerationChecked.paramsTel, + GenerationChecked.idxTel] using h + exact h'.of_append.1 + +/-- The raw index telescope is well formed over the checked parameters. -/ +theorem idxTel_onTel : + env.OnTel (gen.recUvars) + gen.paramsTel.reverse gen.idxTel := by + have h := S.generationFamily_onTel.instL + (U' := gen.recUvars) gen.sourceLevels_wf + have h' : env.OnTel (gen.recUvars) [] + (gen.paramsTel ++ gen.idxTel) := by + simpa [GenerationChecked.paramsTel, + GenerationChecked.idxTel] using h + simpa using h'.of_append.2 + +/-- Any suffix of paired constructors generates a well-formed mixed minor +telescope at its positional depth. -/ +theorem minorTypesAux_onTel : + ∀ (ctors : List NormalizedCtor), + (∀ ctor ∈ ctors, ctor ∈ gen.block.ctorPairs) → + ∀ (Δ : List VExpr) (i : Nat), Δ.length = i → + env.OnTel (gen.recUvars) + (Δ ++ + (gen.motiveType :: gen.paramsTel.reverse)) + (gen.minorTypesAux ctors i) + | [], _, _, _, _ => trivial + | ctor :: ctors, hsub, Δ, i, hΔ => + ⟨by + rw [← hΔ] + exact (S.minor_isType + (hsub ctor (.head _))).weakN S.ord + (.zero Δ), + GenerationEnv.minorTypesAux_onTel ctors + (fun ctor hctor => hsub ctor (.tail _ hctor)) + (_ :: Δ) (i+1) (by simp [hΔ])⟩ + +/-- The complete mixed minor telescope is well formed over parameters and +motive. -/ +theorem minorTypes_onTel : + env.OnTel (gen.recUvars) + (gen.motiveType :: gen.paramsTel.reverse) + gen.minorTypes := by + simpa [GenerationChecked.minorTypes] using + S.minorTypesAux_onTel gen.block.ctorPairs + (fun _ h => h) [] 0 rfl + +/-- Transport the fully applied raw family under binders inserted below its +indices and then under an arbitrary top stack. -/ +theorem familyApp_transport + (mid : List VExpr) {g : Nat} (hg : mid.length = g) + (As₂ : List VExpr) {d : Nat} (hd : As₂.length = d) : + env.HasType (gen.recUvars) + (As₂ ++ + ((VExpr.liftTelN g gen.idxTel 0).reverse ++ + (mid ++ gen.paramsTel.reverse))) + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (d + (g + gen.idxTel.length)) + source.nparams ++ + VExpr.bvarRevRange d gen.idxTel.length)) + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels))) := by + have W₁ := Ctx.LiftN.consTel (n := mid.length) gen.idxTel + (Ctx.LiftN.zero (Γ := gen.paramsTel.reverse) mid) + rw [Nat.add_zero] at W₁ + have h₁ := S.familyApp_hasType.weakN S.ord W₁ + rw [hg] at h₁ + have hmid : env.HasType (gen.recUvars) + ((VExpr.liftTelN g gen.idxTel 0).reverse ++ + (mid ++ gen.paramsTel.reverse)) + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (g + gen.idxTel.length) source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)) + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels))) := by + simpa [VExpr.liftN_appN, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (Nat.le_refl _), + VExpr.bvarRevRange_liftN_high + gen.idxTel.length 0 g gen.idxTel.length (by omega), + VExpr.liftN] using h₁ + have htop := hmid.weakN S.ord + (Ctx.LiftN.zero (Γ := _) As₂ (h := hd)) + simpa [VExpr.liftN_appN, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (Nat.zero_le _), + VExpr.liftN, List.append_assoc] using htop + +/-- The mixed recursor type is well formed. Its telescope retains raw +parameter and index syntax while its motive and minors use the checked +recursive classification. -/ +theorem recType_isType : + env.IsType (gen.recUvars) [] gen.recType := by + refine IsType.forallN S.paramsTel_onTel ?_ + simp only [List.append_nil] + refine IsType.forallE S.motive_isType ?_ + refine IsType.forallN S.minorTypes_onTel ?_ + have hI : env.OnTel (gen.recUvars) + (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse)) + (VExpr.liftTelN (gen.block.ctorPairs.length + 1) + gen.idxTel 0) := by + have h := S.idxTel_onTel.weakN S.ord + (Ctx.LiftN.zero + (n := gen.block.ctorPairs.length + 1) + (Γ := gen.paramsTel.reverse) + (gen.minorTypes.reverse ++ [gen.motiveType]) + (h := by simp [gen.minorTypes_length])) + simpa [List.append_assoc] using h + refine IsType.forallN hI ?_ + have hmaj₀ := S.familyApp_transport + (gen.minorTypes.reverse ++ [gen.motiveType]) + (g := gen.block.ctorPairs.length + 1) + (by simp [gen.minorTypes_length]) + [] (d := 0) rfl + rw [VExpr.bvarRevRange_congr source.nparams + (show + 0 + ((gen.block.ctorPairs.length + 1) + + gen.idxTel.length) = + gen.idxTel.length + gen.block.ctorPairs.length + 1 by omega)] at hmaj₀ + have hmaj : env.HasType (gen.recUvars) + ((VExpr.liftTelN (gen.block.ctorPairs.length + 1) + gen.idxTel 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.idxTel.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)) + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels))) := by + simpa [List.append_assoc] using hmaj₀ + refine IsType.forallE ⟨_, hmaj⟩ ?_ + have hM := getElem?_rstack3 + [VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.idxTel.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)] + ((VExpr.liftTelN (gen.block.ctorPairs.length + 1) + gen.idxTel 0).reverse ++ + gen.minorTypes.reverse) + gen.motiveType gen.paramsTel.reverse + (i := gen.idxTel.length + + gen.block.ctorPairs.length + 1) + (by + simp only [List.length_singleton, List.length_append, + List.length_reverse, VExpr.liftTelN_length, + gen.minorTypes_length] + omega) + have hmlu := Lookup.of_getElem? (by + simpa only [List.singleton_append, + List.append_assoc] using hM) + rw [show gen.motiveType.liftN + (gen.idxTel.length + + gen.block.ctorPairs.length + 1 + 1) = + (gen.motiveType.liftN + (gen.block.ctorPairs.length + 1)).liftN + (gen.idxTel.length + 1) from by + rw [VExpr.liftN_liftN] + congr 1 + omega, + gen.motiveType_liftN] at hmlu + have hfun : env.HasType (gen.recUvars) + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.idxTel.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length) :: + ((VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + gen.idxTel 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse)))) + (.bvar + (gen.idxTel.length + + gen.block.ctorPairs.length + 1)) + ((VExpr.forallN + (VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + gen.idxTel 0) + (.forallE + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.block.ctorPairs.length + 1 + + gen.idxTel.length) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)) + (.sort gen.motiveLevel))).liftN + (1 + (VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + gen.idxTel 0).length)) := by + exact .bvar (by + simpa [List.append_assoc, VExpr.liftTelN_length, + Nat.add_comm] using hmlu) + have hMapp := HasType.appN_selfSpine + (As := VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + gen.idxTel 0) + (Δ := [VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.idxTel.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)]) + (Γ := gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse)) + (f := .bvar + (gen.idxTel.length + + gen.block.ctorPairs.length + 1)) + hfun + have h0 : + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.idxTel.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length) :: + ((VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + gen.idxTel 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))))[0]? = + some (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.idxTel.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)) := rfl + have harg := HasType.bvar + (env := env) (U := gen.recUvars) + (Lookup.of_getElem? h0) + have harg' : env.HasType (gen.recUvars) + ([VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.idxTel.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)] ++ + (VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + gen.idxTel 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + (.bvar 0) + ((VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.block.ctorPairs.length + 1 + + gen.idxTel.length) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)).liftN 1) := by + simpa [List.append_assoc, Nat.add_comm, + Nat.add_left_comm, Nat.add_assoc] using harg + have happ := HasType.app hMapp harg' + exact ⟨_, by + simpa [GenerationChecked.recType, List.append_assoc, + VExpr.liftTelN_length] using happ⟩ + +/-- The mixed generated recursor constant is well formed. -/ +theorem recursor_wf : gen.recursor.WF env := + S.recType_isType + +end GenerationEnv + +theorem minorTypes_getElem? {U : Nat} {T : Name} {np : Nat} {ty : VInductiveType} : + ∀ (cs : List VConstVal) (i₀ q : Nat), + (minorTypes U T np ty cs i₀)[q]? = + cs[q]?.map fun c => VExpr.liftN (i₀+q) (minorType U T np ty c) + | [], _, q => by simp [minorTypes] + | c :: cs, i₀, 0 => by simp [minorTypes] + | c :: cs, i₀, q+1 => by + simp only [minorTypes, List.getElem?_cons_succ] + rw [minorTypes_getElem? cs (i₀+1) q, show i₀+1+q = i₀+(q+1) from by omega] + +theorem minorTypesRec_getElem? {U : Nat} {T : Name} {np : Nat} {ty : VInductiveType} : + ∀ (cs : List VConstVal) (i₀ q : Nat), + (minorTypesRec U T np ty cs i₀)[q]? = + cs[q]?.map fun c => VExpr.liftN (i₀+q) (minorTypeRec U T np ty c) + | [], _, q => by simp [minorTypesRec] + | c :: cs, i₀, 0 => by simp [minorTypesRec] + | c :: cs, i₀, q+1 => by + simp only [minorTypesRec, List.getElem?_cons_succ] + rw [minorTypesRec_getElem? cs (i₀+1) q, + show i₀+1+q = i₀+(q+1) from by omega] + +theorem recApp'_levelWF {U : Nat} {T : Name} {np off : Nat} : + (recApp' U T np off).LevelWF (U+1) := + VExpr.LevelWF.appN (f := .const T (VLevel.params' U 1)) VLevel.params'_one_wf + (bvarRevRange_levelWF _ _) + +theorem idxTel_length {U np : Nat} {ty : VInductiveType} : + (idxTel U np ty).length = (ctorFields (VExpr.dropN np ty.type)).length := + List.length_map .. + +theorem idxTel_levelWF {U np : Nat} {ty : VInductiveType} : + ∀ A ∈ idxTel U np ty, A.LevelWF (U+1) := by + intro A hA + obtain ⟨A₀, -, rfl⟩ := List.mem_map.1 hA + exact VExpr.LevelWF.instL VLevel.params'_one_wf + +theorem ctorIdxs_length {U np : Nat} {c : VConstVal} : + (ctorIdxs U np c).length = + (recFieldIdxs np (VExpr.resultOf (VExpr.dropN np c.type))).length := + List.length_map .. + +theorem ctorIdxs_levelWF {U np : Nat} {c : VConstVal} : + ∀ e ∈ ctorIdxs U np c, e.LevelWF (U+1) := by + intro e he + obtain ⟨e₀, -, rfl⟩ := List.mem_map.1 he + exact VExpr.LevelWF.instL VLevel.params'_one_wf + +theorem recPairsR_lt {U : Nat} {T : Name} {np ni : Nat} {c : VConstVal} : + ∀ q ∈ recPairsR U T np ni c, q.1 < (ctorFields (VExpr.dropN np c.type)).length := by + intro q hq + obtain ⟨⟨j, idxs⟩, hmem, rfl⟩ := List.mem_map.1 hq + simpa using recPairs_lt _ hmem + +theorem recPairsR_idx_levelWF {U : Nat} {T : Name} {np ni : Nat} {c : VConstVal} : + ∀ q ∈ recPairsR U T np ni c, ∀ e ∈ q.2, e.LevelWF (U+1) := by + intro q hq e he + obtain ⟨⟨j, idxs⟩, -, rfl⟩ := List.mem_map.1 hq + obtain ⟨e₀, -, rfl⟩ := List.mem_map.1 he + exact VExpr.LevelWF.instL VLevel.params'_one_wf + +/-- Unpack a recursor-universe recursive position: the underlying field and +its (declaration-universe) index arguments. -/ +theorem recPairsR_mem {U : Nat} {T : Name} {np ni : Nat} {c : VConstVal} {q} + (hq : q ∈ recPairsR U T np ni c) : + ∃ B, (ctorFields (VExpr.dropN np c.type))[q.1]? = some B ∧ + isRecField U T np ni q.1 B = true ∧ + q.2 = (recFieldIdxs np B).map (VExpr.instL (VLevel.params' U 1)) := by + obtain ⟨⟨j, idxs⟩, hmem, rfl⟩ := List.mem_map.1 hq + obtain ⟨B, hB, hrec, hidx⟩ := recPairs_getElem _ hmem + exact ⟨B, by simpa using hB, hrec, by + simpa using congrArg + (List.map (VExpr.instL (ElimMode.large.sourceLevels U))) hidx⟩ + +/-- Recursor-universe transport preserves each recursive argument's source +field position. -/ +theorem recArgsR_lt {U : Nat} {T : Name} {np ni : Nat} {c : VConstVal} : + ∀ r ∈ recArgsR U T np ni c, + r.fieldIndex < (ctorFields (VExpr.dropN np c.type)).length := by + intro r hr + obtain ⟨r₀, hr₀, rfl⟩ := List.mem_map.1 hr + simpa [RecArg.instL] using recArgs_lt _ hr₀ + +/-- Every generated recursive-target index is level-well-formed in the +recursor universe context. -/ +theorem recArgsR_idx_levelWF {U : Nat} {T : Name} {np ni : Nat} {c : VConstVal} : + ∀ r ∈ recArgsR U T np ni c, ∀ e ∈ r.indices, e.LevelWF (U+1) := by + intro r hr e he + obtain ⟨r₀, -, rfl⟩ := List.mem_map.1 hr + obtain ⟨e₀, -, rfl⟩ := List.mem_map.1 he + exact VExpr.LevelWF.instL VLevel.params'_one_wf + +/-- Every generated recursive-Pi domain is level-well-formed after universe +transport. -/ +theorem recArgsR_binder_levelWF {U : Nat} {T : Name} {np ni : Nat} {c : VConstVal} : + ∀ r ∈ recArgsR U T np ni c, ∀ A ∈ r.binders, A.LevelWF (U+1) := by + intro r hr A hA + obtain ⟨r₀, -, rfl⟩ := List.mem_map.1 hr + obtain ⟨A₀, -, rfl⟩ := List.mem_map.1 hA + exact VExpr.LevelWF.instL VLevel.params'_one_wf + +/-- Unpack a recursor-universe recursive descriptor to the source field and +its declaration-universe analysis result. -/ +theorem recArgsR_mem {U : Nat} {T : Name} {np ni : Nat} {c : VConstVal} {r} + (hr : r ∈ recArgsR U T np ni c) : + ∃ r₀ B, r = r₀.instL (VLevel.params' U 1) ∧ + (ctorFields (VExpr.dropN np c.type))[r₀.fieldIndex]? = some B ∧ + recArg? U T np ni r₀.fieldIndex B = some r₀ := by + obtain ⟨r₀, hr₀, rfl⟩ := List.mem_map.1 hr + obtain ⟨B, hB, hrec⟩ := recArgs_getElem _ hr₀ + exact ⟨r₀, B, rfl, by simpa using hB, hrec⟩ + +theorem motiveType_levelWF {U : Nat} {T : Name} {np : Nat} {ty : VInductiveType} : + (motiveType U T np ty).LevelWF (U+1) := by + refine VExpr.LevelWF.forallN idxTel_levelWF ⟨?_, Nat.succ_pos U⟩ + refine VExpr.LevelWF.appN (f := .const T (VLevel.params' U 1)) VLevel.params'_one_wf + fun e h => ?_ + rcases List.mem_append.1 h with h | h + · exact bvarRevRange_levelWF _ _ _ h + · exact bvarRevRange_levelWF _ _ _ h + +theorem motiveType_liftN {U : Nat} {T : Name} {np n : Nat} {ty : VInductiveType} : + (motiveType U T np ty).liftN n = + VExpr.forallN (VExpr.liftTelN n (idxTel U np ty) 0) + (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (n + (idxTel U np ty).length) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (.param 0))) := by + rw [show motiveType U T np ty = VExpr.forallN (idxTel U np ty) + (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (idxTel U np ty).length np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (.param 0))) from rfl, + VExpr.liftN_forallN] + refine congrArg _ ?_ + show VExpr.forallE _ _ = VExpr.forallE _ _ + refine congr (congrArg _ ?_) rfl + rw [VExpr.liftN_appN, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (by omega), + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega)] + rfl + +theorem liftTelN_levelWF {Uv n : Nat} : ∀ {tel : List VExpr} {k : Nat}, + (∀ A ∈ tel, A.LevelWF Uv) → ∀ A ∈ VExpr.liftTelN n tel k, A.LevelWF Uv + | [], _, _, _, h => nomatch h + | _ :: tel, k, hAs, A', h => by + rcases List.mem_cons.1 h with rfl | h + · exact (hAs _ (.head _)).liftN + · exact liftTelN_levelWF (fun A h => hAs _ (.tail _ h)) A' h + +theorem RecArg.minorBinders_levelWF {Uv m p : Nat} {r : RecArg} + (hbind : ∀ A ∈ r.binders, A.LevelWF Uv) : + ∀ A ∈ r.minorBinders m p, A.LevelWF Uv := + liftTelN_levelWF (liftTelN_levelWF hbind) + +theorem RecArg.minorIH_levelWF {Uv m p : Nat} {r : RecArg} + (hbind : ∀ A ∈ r.binders, A.LevelWF Uv) + (hidx : ∀ e ∈ r.indices, e.LevelWF Uv) : + (r.minorIH m p).LevelWF Uv := by + simp only [RecArg.minorIH] + refine VExpr.LevelWF.forallN (r.minorBinders_levelWF hbind) + (VExpr.LevelWF.appN (f := .bvar _) trivial fun e he => ?_) + rcases List.mem_append.1 he with he | he + · obtain ⟨e₀, he₀, rfl⟩ := List.mem_map.1 he + exact ((hidx e₀ he₀).liftN).liftN + · rcases List.mem_cons.1 he with rfl | he + · exact VExpr.LevelWF.appN (f := .bvar _) trivial (bvarRevRange_levelWF _ _) + · cases he + +theorem ihsFromRecArgs_levelWF {Uv m : Nat} : + ∀ (rs : List RecArg) (p : Nat), + (∀ r ∈ rs, ∀ A ∈ r.binders, A.LevelWF Uv) → + (∀ r ∈ rs, ∀ e ∈ r.indices, e.LevelWF Uv) → + ∀ e ∈ ihsFromRecArgs m rs p, e.LevelWF Uv + | [], _, _, _, _, h => nomatch h + | r :: rs, p, hbind, hidx, e, h => by + rcases List.mem_cons.1 h with rfl | h + · exact r.minorIH_levelWF + (hbind r (.head _)) (hidx r (.head _)) + · exact ihsFromRecArgs_levelWF rs (p+1) + (fun q hq => hbind q (.tail _ hq)) + (fun q hq => hidx q (.tail _ hq)) e h + +theorem minorType_levelWF {U : Nat} {T : Name} {np : Nat} {ty : VInductiveType} + {c : VConstVal} : (minorType U T np ty c).LevelWF (U+1) := by + simp only [minorType] + refine VExpr.LevelWF.forallN (liftTelN_levelWF fun B hB => ?_) + (VExpr.LevelWF.forallN (ihsFrom_levelWF _ _ recPairsR_idx_levelWF) ?_) + · obtain ⟨B₀, _, rfl⟩ := List.mem_map.1 hB + exact VExpr.LevelWF.instL VLevel.params'_one_wf + · refine VExpr.LevelWF.appN (f := .bvar _) trivial fun e h => ?_ + rcases List.mem_append.1 h with h | h + · obtain ⟨e₀, he₀, rfl⟩ := List.mem_map.1 h + exact ((ctorIdxs_levelWF _ he₀).liftN).liftN + · rcases List.mem_cons.1 h with rfl | h + · refine VExpr.LevelWF.appN (f := .const c.name (VLevel.params' U 1)) + VLevel.params'_one_wf fun e h => ?_ + rcases List.mem_append.1 h with h | h + · exact bvarRevRange_levelWF _ _ _ h + · exact bvarRevRange_levelWF _ _ _ h + · cases h + +theorem minorTypeRec_levelWF {U : Nat} {T : Name} {np : Nat} {ty : VInductiveType} + {c : VConstVal} : (minorTypeRec U T np ty c).LevelWF (U+1) := by + simp only [minorTypeRec] + refine VExpr.LevelWF.forallN (liftTelN_levelWF fun B hB => ?_) + (VExpr.LevelWF.forallN + (ihsFromRecArgs_levelWF _ _ recArgsR_binder_levelWF recArgsR_idx_levelWF) ?_) + · obtain ⟨B₀, _, rfl⟩ := List.mem_map.1 hB + exact VExpr.LevelWF.instL VLevel.params'_one_wf + · refine VExpr.LevelWF.appN (f := .bvar _) trivial fun e h => ?_ + rcases List.mem_append.1 h with h | h + · obtain ⟨e₀, he₀, rfl⟩ := List.mem_map.1 h + exact ((ctorIdxs_levelWF _ he₀).liftN).liftN + · rcases List.mem_cons.1 h with rfl | h + · refine VExpr.LevelWF.appN (f := .const c.name (VLevel.params' U 1)) + VLevel.params'_one_wf fun e h => ?_ + rcases List.mem_append.1 h with h | h + · exact bvarRevRange_levelWF _ _ _ h + · exact bvarRevRange_levelWF _ _ _ h + · cases h + +theorem minorTypes_levelWF {U : Nat} {T : Name} {np : Nat} {ty : VInductiveType} : + ∀ (cs : List VConstVal) (i : Nat), ∀ e ∈ minorTypes U T np ty cs i, e.LevelWF (U+1) + | _ :: cs, i, e, h => by + rcases List.mem_cons.1 h with rfl | h + · exact minorType_levelWF.liftN + · exact minorTypes_levelWF cs (i+1) e h + +theorem minorTypesRec_levelWF {U : Nat} {T : Name} {np : Nat} {ty : VInductiveType} : + ∀ (cs : List VConstVal) (i : Nat), + ∀ e ∈ minorTypesRec U T np ty cs i, e.LevelWF (U+1) + | _ :: cs, i, e, h => by + rcases List.mem_cons.1 h with rfl | h + · exact minorTypeRec_levelWF.liftN + · exact minorTypesRec_levelWF cs (i+1) e h + +theorem recType_levelWF {U : Nat} {T : Name} {np : Nat} {ty : VInductiveType} : + (recType U T np ty).LevelWF (U+1) := by + refine VExpr.LevelWF.forallN (fun A hA => ?_) + ⟨motiveType_levelWF, VExpr.LevelWF.forallN (minorTypes_levelWF _ _) + (VExpr.LevelWF.forallN (liftTelN_levelWF idxTel_levelWF) + ⟨?_, ?_, trivial⟩)⟩ + · obtain ⟨A₀, _, rfl⟩ := List.mem_map.1 hA + exact VExpr.LevelWF.instL VLevel.params'_one_wf + · refine VExpr.LevelWF.appN (f := .const T (VLevel.params' U 1)) VLevel.params'_one_wf + fun e h => ?_ + rcases List.mem_append.1 h with h | h + · exact bvarRevRange_levelWF _ _ _ h + · exact bvarRevRange_levelWF _ _ _ h + · exact VExpr.LevelWF.appN (f := .bvar _) trivial (bvarRevRange_levelWF _ _) + +theorem recTypeRec_levelWF {U : Nat} {T : Name} {np : Nat} {ty : VInductiveType} : + (recTypeRec U T np ty).LevelWF (U+1) := by + refine VExpr.LevelWF.forallN (fun A hA => ?_) + ⟨motiveType_levelWF, VExpr.LevelWF.forallN (minorTypesRec_levelWF _ _) + (VExpr.LevelWF.forallN (liftTelN_levelWF idxTel_levelWF) + ⟨?_, ?_, trivial⟩)⟩ + · obtain ⟨A₀, _, rfl⟩ := List.mem_map.1 hA + exact VExpr.LevelWF.instL VLevel.params'_one_wf + · refine VExpr.LevelWF.appN (f := .const T (VLevel.params' U 1)) VLevel.params'_one_wf + fun e h => ?_ + rcases List.mem_append.1 h with h | h + · exact bvarRevRange_levelWF _ _ _ h + · exact bvarRevRange_levelWF _ _ _ h + · exact VExpr.LevelWF.appN (f := .bvar _) trivial (bvarRevRange_levelWF _ _) + +/-- Consume the induction-hypothesis telescope with well-typed values. -/ +theorem hasType_appN_ihs {env : VEnv} {U : Nat} {Γ : List VExpr} {m k : Nat} + {argOf : Nat × List VExpr → VExpr} {Dfin : VExpr} : + ∀ {rs : List (Nat × List VExpr)} {g : VExpr}, (∀ q ∈ rs, q.1 < m) → + (∀ q ∈ rs, env.HasType U Γ (argOf q) + (VExpr.appN (.bvar (k + m)) + ((q.2.map fun e => ((e.liftN 1 q.1).liftN (m-q.1)).liftN k m) ++ + [.bvar (m-1-q.1)]))) → + env.HasType U Γ g (VExpr.forallN (ihsR m k rs 0) (Dfin.liftN rs.length)) → + env.HasType U Γ (g.appN (rs.map argOf)) Dfin + | [], g, _, _, hg => by simpa [ihsR] using hg + | (j, idxs) :: rs, g, hm, hargs, hg => by + have happ := VEnv.HasType.app hg (hargs (j, idxs) (.head _)) + simp only [List.length_cons] at happ + rw [show Dfin.liftN (rs.length+1) = + (Dfin.liftN rs.length).liftN 1 (0 + rs.length) from by + rw [Nat.zero_add, VExpr.liftN'_liftN' (Nat.zero_le _) (by omega)], + ihsR_liftN1 m k rs 0 0 (Nat.le_refl _) (fun q hq => hm q (.tail _ hq)) + (Dfin.liftN rs.length), + VExpr.inst_liftN1] at happ + exact hasType_appN_ihs (rs := rs) (fun q hq => hm q (.tail _ hq)) + (fun q hq => hargs q (.tail _ hq)) happ + +/-- Consume a normalized functional-IH telescope with one generated recursive +call per recursive argument. -/ +theorem hasType_appN_ruleIHs {env : VEnv} {U : Nat} {Γ : List VExpr} {m k : Nat} + {argOf : RecArg → VExpr} {Dfin : VExpr} : + ∀ {rs : List RecArg} {g : VExpr}, + (∀ r ∈ rs, env.HasType U Γ (argOf r) (r.ruleIH m k)) → + env.HasType U Γ g + (VExpr.forallN (ruleIHs m k rs 0) (Dfin.liftN rs.length)) → + env.HasType U Γ (g.appN (rs.map argOf)) Dfin + | [], g, _, hg => by simpa [ruleIHs] using hg + | r :: rs, g, hargs, hg => by + have happ := VEnv.HasType.app hg (by + simpa [ruleIHs] using hargs r (.head _)) + simp only [List.length_cons] at happ + rw [show Dfin.liftN (rs.length+1) = + (Dfin.liftN rs.length).liftN 1 (0+rs.length) from by + rw [Nat.zero_add, VExpr.liftN'_liftN' (Nat.zero_le _) (by omega)], + ruleIHs_liftN1 m k rs 0 0 (Nat.le_refl _) (Dfin.liftN rs.length), + VExpr.inst_liftN1] at happ + exact hasType_appN_ruleIHs (rs := rs) + (fun q hq => hargs q (.tail _ hq)) happ + +namespace GenerationEnv + +variable {source : VInductDecl} {gen : GenerationChecked source} + {env : VEnv} (S : GenerationEnv gen env) +include S +-- `S` is included throughout so every lemma below is reachable as `S.lemma`, +-- including those whose statement and proof never mention it. +set_option linter.unusedSectionVars false + +/-! ## Mixed iota-rule preservation -/ + +/-- Syntactic universe well-formedness follows from semantic well-formedness +of the closed mixed recursor type. -/ +theorem recType_levelWF : + gen.recType.LevelWF (gen.recUvars) := by + obtain ⟨_, h⟩ := S.recType_isType + exact (h.levelWF trivial).1 + +/-- The mixed recursor type is closed. -/ +theorem recType_closedN : gen.recType.ClosedN 0 := by + obtain ⟨_, h⟩ := S.recType_isType + exact VExpr.WF.closedN S.ord ⟨_, h⟩ trivial + +/-- The inserted mixed recursor constant at its identity universe list. -/ +theorem recursor_hasType + (hrec : env.constants + (.str gen.block.sourceType.name "rec") = + some gen.recursor) {Γ} : + env.HasType (gen.recUvars) Γ + (.const (.str gen.block.sourceType.name "rec") + (gen.recLevels)) + gen.recType := by + have h := HasType.const (Γ := Γ) hrec + VLevel.params_wf VLevel.params_length + rw [show gen.recursor.uvars = + gen.recUvars from rfl, + show gen.recursor.type = gen.recType from rfl] at h + rwa [S.recType_levelWF.instL_id] at h + +/-- Apply the mixed recursor to parameters, motive, and every constructor +minor. The remaining type binds the indices and major premise. -/ +theorem recBase_hasType + (hrec : env.constants + (.str gen.block.sourceType.name "rec") = + some gen.recursor) + (Δ : List VExpr) : + env.HasType (gen.recUvars) + (Δ ++ (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + (VExpr.appN + (.const (.str gen.block.sourceType.name "rec") + (gen.recLevels)) + (VExpr.bvarRevRange Δ.length + (source.nparams + + gen.block.ctorPairs.length + 1))) + ((VExpr.forallN + (VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + gen.idxTel 0) + (.forallE + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.idxTel.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)) + (.app + (VExpr.appN + (.bvar + (gen.idxTel.length + + gen.block.ctorPairs.length + 1)) + (VExpr.bvarRevRange 1 gen.idxTel.length)) + (.bvar 0)))).liftN Δ.length) := by + have hf : env.HasType (gen.recUvars) + (Δ ++ + (gen.paramsTel ++ + gen.motiveType :: gen.minorTypes).reverse ++ []) + (.const (.str gen.block.sourceType.name "rec") + (gen.recLevels)) + ((VExpr.forallN + (gen.paramsTel ++ + gen.motiveType :: gen.minorTypes) + (VExpr.forallN + (VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + gen.idxTel 0) + (.forallE + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.idxTel.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)) + (.app + (VExpr.appN + (.bvar + (gen.idxTel.length + + gen.block.ctorPairs.length + 1)) + (VExpr.bvarRevRange 1 gen.idxTel.length)) + (.bvar 0))))).liftN + (Δ.length + + (gen.paramsTel ++ + gen.motiveType :: gen.minorTypes).length)) := by + rw [show VExpr.forallN + (gen.paramsTel ++ + gen.motiveType :: gen.minorTypes) + (VExpr.forallN + (VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + gen.idxTel 0) + (.forallE + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.idxTel.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)) + (.app + (VExpr.appN + (.bvar + (gen.idxTel.length + + gen.block.ctorPairs.length + 1)) + (VExpr.bvarRevRange 1 gen.idxTel.length)) + (.bvar 0)))) = + gen.recType from by + rw [VExpr.forallN_append] + rfl, + S.recType_closedN.liftN_eq (Nat.zero_le _)] + exact S.recursor_hasType hrec + have hspine := HasType.appN_selfSpine + (env := env) (U := gen.recUvars) hf + simp only [List.reverse_append, List.reverse_cons, List.append_nil, List.append_assoc, + List.singleton_append, List.length_append, List.length_cons, gen.minorTypes_length] at hspine + rw [show gen.paramsTel.length = source.nparams from by + simp [GenerationChecked.paramsTel, + S.generationParams_length], + VExpr.bvarRevRange_congr' Δ.length + (show source.nparams + + (gen.block.ctorPairs.length + 1) = + source.nparams + + gen.block.ctorPairs.length + 1 by omega)] at hspine + simpa [List.append_assoc] using hspine + +/-- Apply the inserted mixed recursor to a typed index spine and major. -/ +theorem recApp_hasType + (hrec : env.constants + (.str gen.block.sourceType.name "rec") = + some gen.recursor) + (Δ : List VExpr) {idxs : List VExpr} {a : VExpr} + (hidx : env.SpineWF (gen.recUvars) + (Δ ++ (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + (VExpr.forallN + (VExpr.liftTelN + (Δ.length + + gen.block.ctorPairs.length + 1) + gen.idxTel 0) + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels)))) + idxs + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels)))) + (hlen : idxs.length = gen.idxTel.length) + (ha : env.HasType (gen.recUvars) + (Δ ++ (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + a + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (Δ.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + idxs))) : + env.HasType (gen.recUvars) + (Δ ++ (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + (VExpr.appN + (VExpr.appN + (.const (.str gen.block.sourceType.name "rec") + (gen.recLevels)) + (VExpr.bvarRevRange Δ.length + (source.nparams + + gen.block.ctorPairs.length + 1))) + (idxs ++ [a])) + (VExpr.appN + (.bvar + (Δ.length + + gen.block.ctorPairs.length)) + (idxs ++ [a])) := by + have hb := S.recBase_hasType hrec Δ + rw [VExpr.liftN_forallN, + VExpr.liftTelN_liftTelN, + liftTelN_congr _ _ + (show gen.block.ctorPairs.length + 1 + + Δ.length = + Δ.length + gen.block.ctorPairs.length + 1 by omega)] at hb + have hcod : + (VExpr.forallE + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.idxTel.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)) + (.app + (VExpr.appN + (.bvar + (gen.idxTel.length + + gen.block.ctorPairs.length + 1)) + (VExpr.bvarRevRange 1 gen.idxTel.length)) + (.bvar 0))).liftN Δ.length + (0 + (VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + gen.idxTel 0).length) = + VExpr.forallE + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.idxTel.length + Δ.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)) + (.app + (VExpr.appN + (.bvar + (gen.idxTel.length + Δ.length + + gen.block.ctorPairs.length + 1)) + (VExpr.bvarRevRange 1 gen.idxTel.length)) + (.bvar 0)) := by + rw [VExpr.liftTelN_length, Nat.zero_add] + show VExpr.forallE _ _ = VExpr.forallE _ _ + congr 1 + · rw [VExpr.liftN_appN, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (by omega), + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega), + VExpr.bvarRevRange_congr source.nparams + (show Δ.length + + (gen.idxTel.length + + gen.block.ctorPairs.length + 1) = + gen.idxTel.length + Δ.length + + gen.block.ctorPairs.length + 1 by omega)] + rfl + · show VExpr.app _ _ = VExpr.app _ _ + congr 1 + · rw [VExpr.liftN_appN, + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega)] + show VExpr.appN + (.bvar (liftVar Δ.length + (gen.idxTel.length + + gen.block.ctorPairs.length + 1) + (gen.idxTel.length + 1))) _ = _ + rw [liftVar_le (by omega), + show Δ.length + + (gen.idxTel.length + + gen.block.ctorPairs.length + 1) = + gen.idxTel.length + Δ.length + + gen.block.ctorPairs.length + 1 by omega] + rw [hcod] at hb + have hshape := hidx.retarget + (by simpa only [VExpr.liftTelN_length] using hlen) + (.forallE + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.idxTel.length + Δ.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)) + (.sort gen.motiveLevel)) + rw [VExpr.instRev_forallE_sort, VExpr.instRev_appN, + VExpr.instRev_closedN _ + (C := .const gen.block.sourceType.name + (gen.sourceLevels)) trivial, + List.map_append, + VExpr.map_instRev_bvarRevRange_ge _ _ _ + (by rw [hlen]; omega), + show gen.idxTel.length + Δ.length + + gen.block.ctorPairs.length + 1 - idxs.length = + Δ.length + gen.block.ctorPairs.length + 1 from by + rw [hlen] + omega, + VExpr.bvarRevRange_congr' 0 hlen.symm, + VExpr.map_instRev_bvarRevRange] at hshape + rw [hlen] at hshape + have hfull := hshape.snoc ha + simp only [VExpr.inst] at hfull + change env.SpineWF (gen.recUvars) _ + (VExpr.forallN + (VExpr.liftTelN + (Δ.length + gen.block.ctorPairs.length + 1) + gen.idxTel 0) + (VExpr.forallN + [VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.idxTel.length + Δ.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 gen.idxTel.length)] + (.sort gen.motiveLevel))) + (idxs ++ [a]) (.sort gen.motiveLevel) at hfull + rw [← VExpr.forallN_append] at hfull + have hfullLen : (idxs ++ [a]).length = + (VExpr.liftTelN + (Δ.length + + gen.block.ctorPairs.length + 1) + gen.idxTel 0 ++ + [VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (gen.idxTel.length + Δ.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 + gen.idxTel.length)]).length := by + simp only [List.length_append, List.length_singleton, + VExpr.liftTelN_length, hlen] + have hactual := hfull.retarget hfullLen + (VExpr.app + (VExpr.appN + (.bvar + (gen.idxTel.length + Δ.length + + gen.block.ctorPairs.length + 1)) + (VExpr.bvarRevRange 1 gen.idxTel.length)) + (.bvar 0)) + rw [VExpr.forallN_append] at hactual + have happ := hactual.hasType_appN hb + rw [show VExpr.app + (VExpr.appN + (.bvar + (gen.idxTel.length + Δ.length + + gen.block.ctorPairs.length + 1)) + (VExpr.bvarRevRange 1 gen.idxTel.length)) + (.bvar 0) = + VExpr.appN + (.bvar + (gen.idxTel.length + Δ.length + + gen.block.ctorPairs.length + 1)) + (VExpr.bvarRevRange 0 + (gen.idxTel.length + 1)) from by + rw [VExpr.bvarRevRange_congr' 0 + (show gen.idxTel.length + 1 = + 1 + gen.idxTel.length by omega), + ← VExpr.bvarRevRange_append + gen.idxTel.length 1] + simpa [VExpr.bvarRevRange, VExpr.appN] using + (VExpr.appN_append + (.bvar + (gen.idxTel.length + Δ.length + + gen.block.ctorPairs.length + 1)) + (VExpr.bvarRevRange 1 gen.idxTel.length) + [VExpr.bvar 0]).symm, + VExpr.instRev_appN, + VExpr.instRev_bvar_ge _ (by + simp only [List.length_append, + List.length_singleton] + rw [hlen] + omega), + VExpr.bvarRevRange_congr' 0 + (show gen.idxTel.length + 1 = + (idxs ++ [a]).length by simp [hlen]), + VExpr.map_instRev_bvarRevRange] at happ + rw [show gen.idxTel.length + Δ.length + + gen.block.ctorPairs.length + 1 - + (idxs ++ [a]).length = + Δ.length + gen.block.ctorPairs.length from by + simp only [List.length_append, List.length_singleton] + rw [hlen] + omega] at happ + simpa [List.length_append, hlen] using happ + +/-- The mixed motive variable applied to an index spine and major. -/ +theorem motiveApp_hasType + (Δ : List VExpr) {idxs : List VExpr} {a : VExpr} + (hidx : env.SpineWF (gen.recUvars) + (Δ ++ (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + (VExpr.forallN + (VExpr.liftTelN + (Δ.length + + gen.block.ctorPairs.length + 1) + gen.idxTel 0) + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels)))) + idxs + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels)))) + (hlen : idxs.length = gen.idxTel.length) + (ha : env.HasType (gen.recUvars) + (Δ ++ (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + a + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (Δ.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + idxs))) : + env.HasType (gen.recUvars) + (Δ ++ (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + (VExpr.appN + (.bvar + (Δ.length + + gen.block.ctorPairs.length)) + (idxs ++ [a])) + (.sort gen.motiveLevel) := by + have hM := getElem?_rstack3 Δ + gen.minorTypes.reverse gen.motiveType + gen.paramsTel.reverse + (i := Δ.length + gen.block.ctorPairs.length) + (by simp only [List.length_reverse, + gen.minorTypes_length]) + exact gen.motiveVarApp_hasType + (.bvar (Lookup.of_getElem? hM)) + hidx hlen ha + +/-- The raw constructor-headed major in the complete mixed rule context. -/ +theorem ctorAppRule_hasType {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) : + env.HasType (gen.recUvars) + ((VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR source.uvars source.nparams gen.elimination) + 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + (VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)) + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + (ctor.resultIndicesR source.uvars gen.elimination).map fun e => + e.liftN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)) := by + have h := S.ctorApp_transport hctor + (gen.minorTypes.reverse ++ [gen.motiveType]) + (g := gen.block.ctorPairs.length + 1) + (by simp [gen.minorTypes_length]) + [] (d := 0) rfl + rw [VExpr.bvarRevRange_congr source.nparams + (show + 0 + (gen.block.ctorPairs.length + 1 + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length) = + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length + 1 by omega)] at h + simpa [List.append_assoc] using h + +/-- The complete mixed rule binder telescope is well formed. -/ +theorem ruleBinders_onTel {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) : + env.OnTel (gen.recUvars) [] + (gen.paramsTel ++ + gen.motiveType :: gen.minorTypes ++ + VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR source.uvars source.nparams gen.elimination) + 0) := by + have hF₀ := S.fields_onTel_minor hctor + have hF := hF₀.weakN S.ord + (Ctx.LiftN.zero + (Γ := gen.motiveType :: gen.paramsTel.reverse) + gen.minorTypes.reverse) + rw [VExpr.liftTelN_liftTelN, + liftTelN_congr _ _ + (show 1 + gen.minorTypes.reverse.length = + gen.block.ctorPairs.length + 1 by + simp [gen.minorTypes_length, Nat.add_comm])] at hF + refine OnTel.append + (OnTel.append S.paramsTel_onTel ⟨?_, ?_⟩) ?_ + · simpa only [List.append_nil] using S.motive_isType + · simpa only [List.append_nil] using S.minorTypes_onTel + · simpa only [List.append_nil, List.append_assoc, + List.reverse_append, List.reverse_cons, + List.singleton_append] using hF + +/-- The type recorded on every mixed iota rule is itself a type. -/ +theorem ruleType_isType {i : Nat} {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) : + env.IsType (gen.recUvars) [] + ((gen.rule i ctor).type) := by + show env.IsType (gen.recUvars) [] + (VExpr.forallN + (gen.paramsTel ++ + gen.motiveType :: gen.minorTypes ++ + VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR source.uvars source.nparams gen.elimination) + 0) + (VExpr.appN + (.bvar + (gen.block.ctorPairs.length + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)) + (((ctor.resultIndicesR source.uvars gen.elimination).map fun e => + e.liftN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length) ++ + [VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)]))) + refine IsType.forallN (S.ruleBinders_onTel hctor) ?_ + simp only [List.reverse_append, List.reverse_cons, + List.append_nil, List.append_assoc, + List.singleton_append] + have hSp₀ := S.result_transport hctor + (gen.minorTypes.reverse ++ [gen.motiveType]) + (g := gen.block.ctorPairs.length + 1) + (by simp [gen.minorTypes_length]) + [] (d := 0) rfl + have hSp : env.SpineWF (gen.recUvars) + ((VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR source.uvars source.nparams gen.elimination) + 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + (VExpr.forallN + (VExpr.liftTelN + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length + 1) + gen.idxTel 0) + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels)))) + ((ctor.resultIndicesR source.uvars gen.elimination).map fun e => + e.liftN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length) + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels))) := by + simpa [List.append_assoc, Nat.add_assoc] using hSp₀ + have hidxLen : + ((ctor.resultIndicesR source.uvars gen.elimination).map fun e => + e.liftN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length).length = + gen.idxTel.length := by + simp only [List.length_map, + NormalizedCtor.resultIndicesR, + GenerationChecked.idxTel] + exact (S.viewResultIndices_length hctor).trans + gen.shape.2.2.1.symm + have hctorApp := S.ctorAppRule_hasType hctor + have hSp' : env.SpineWF (gen.recUvars) + ((VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR source.uvars source.nparams gen.elimination) + 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + (VExpr.forallN + (VExpr.liftTelN + ((VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination) + 0).reverse.length + + gen.block.ctorPairs.length + 1) + gen.idxTel 0) + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels)))) + ((ctor.resultIndicesR source.uvars gen.elimination).map fun e => + e.liftN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length) + (.sort (gen.block.checked.resultLevel.inst + (gen.sourceLevels))) := by + simpa only [List.length_reverse, + VExpr.liftTelN_length] using hSp + have hctorApp' : env.HasType (gen.recUvars) + ((VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR source.uvars source.nparams gen.elimination) + 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + (VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)) + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + ((VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination) + 0).reverse.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + (ctor.resultIndicesR source.uvars gen.elimination).map fun e => + e.liftN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)) := by + simpa only [List.length_reverse, + VExpr.liftTelN_length] using hctorApp + refine ⟨gen.motiveLevel, ?_⟩ + have hm := S.motiveApp_hasType + (VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR source.uvars source.nparams gen.elimination) + 0).reverse + hSp' hidxLen hctorApp' + rw [List.length_reverse, VExpr.liftTelN_length, + show + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length = + gen.block.ctorPairs.length + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length by omega, + show + gen.block.ctorPairs.length + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + 1 = + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length + 1 by omega] at hm + exact hm + +/-- A retained recursive descriptor generates a well-typed direct or +lambda-valued recursive call in the complete mixed rule context. -/ +theorem ruleCall_hasType {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) + (hrec : env.constants + (.str gen.block.sourceType.name "rec") = + some gen.recursor) + {r : RecArg} + (hr : r ∈ ctor.recArgsR source.uvars gen.elimination) : + env.HasType (gen.recUvars) + ((VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR source.uvars source.nparams gen.elimination) + 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + (r.ruleCall + (ctor.fieldsR source.uvars source.nparams gen.elimination).length + gen.block.ctorPairs.length + (VExpr.appN + (.const + (.str gen.block.sourceType.name "rec") + (gen.recLevels)) + (VExpr.bvarRevRange + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + (source.nparams + + gen.block.ctorPairs.length + 1)))) + (r.ruleIH + (ctor.fieldsR source.uvars source.nparams gen.elimination).length + gen.block.ctorPairs.length) := by + obtain ⟨r₀, hr₀, rfl⟩ := + NormalizedCtor.recArgsR_mem hr + obtain ⟨Bview, hBview, hrecArg⟩ := + S.viewRecArg_data hctor hr₀ + let ls := gen.sourceLevels + let r := r₀.instL ls + let Bs := ctor.fieldsR source.uvars source.nparams gen.elimination + let m := Bs.length + let k := gen.block.ctorPairs.length + let j := r₀.fieldIndex + let Fs := VExpr.liftTelN (k+1) Bs 0 + let As := r.ruleBinders m k + let idxs := r.indices.map fun e => + (e.liftN (k+1) + (r.fieldIndex+r.binders.length)).liftN + (m-r.fieldIndex) r.binders.length + let Γ := Fs.reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse)) + have hjm : j < m := by + have hview := S.viewRecArg_lt hctor hr₀ + have hfields := + (gen.shape.2.2.2.2.2 ctor hctor).2.2.2 + simp only [j, m, Bs, NormalizedCtor.fieldsR_length] + omega + have hjraw : + r₀.fieldIndex < + (ctor.rawFields source.nparams).length := by + simpa [j, m, Bs, + NormalizedCtor.fieldsR_length] using hjm + let Braw := + (ctor.rawFields source.nparams)[r₀.fieldIndex] + have hBraw : + (ctor.rawFields source.nparams)[r₀.fieldIndex]? = + some Braw := + List.getElem?_eq_getElem hjraw + have hFsLen : Fs.length = m := by + simp [Fs, m, VExpr.liftTelN_length] + have ht := S.recArg_transport hctor hr₀ + (gen.minorTypes.reverse ++ [gen.motiveType]) + (g := k+1) (by simp [k, gen.minorTypes_length]) + (Fs.drop j).reverse (d := m-j) (by + simp only [List.length_reverse, List.length_drop, + hFsLen]) + dsimp only [r, j, RecArg.instL] at ht + have hctx : + (Fs.drop j).reverse ++ + ((VExpr.liftTelN (k+1) + (Bs.take j) 0).reverse ++ + ((gen.minorTypes.reverse ++ + [gen.motiveType]) ++ + gen.paramsTel.reverse)) = Γ := by + dsimp only [Γ, Fs] + rw [← VExpr.liftTelN_take, List.append_assoc, + ← List.append_assoc + (((VExpr.liftTelN (k+1) Bs 0).drop j).reverse), + ← List.reverse_append, List.take_append_drop, + List.singleton_append, ← List.append_assoc] + have htel : env.OnTel + (gen.recUvars) Γ As := by + rw [hctx] at ht + simpa [r, As, m, k, j, RecArg.instL, + RecArg.ruleBinders] using ht.1 + have hsp : env.SpineWF (gen.recUvars) + (As.reverse ++ Γ) + (VExpr.forallN + (VExpr.liftTelN + (m+k+r.binders.length+1) + gen.idxTel 0) + (.sort (gen.block.checked.resultLevel.inst ls))) + idxs + (.sort (gen.block.checked.resultLevel.inst ls)) := by + rw [hctx] at ht + simpa [r, As, idxs, m, k, j, ls, + RecArg.instL, RecArg.ruleBinders, + List.append_assoc, + show j + r₀.binders.length + (k+1) + (m-j) = + m+k+r₀.binders.length+1 by omega] using ht.2 + have hF : Γ[m-1-j]? = + some ((Braw.instL ls).liftN (k+1) j) := by + dsimp only [Γ, Fs] + rw [List.getElem?_append_left + (by + simp only [List.length_reverse, + VExpr.liftTelN_length] + omega), + List.getElem?_reverse (by rw [hFsLen]; omega), + VExpr.liftTelN_length, + show m - 1 - (m - 1 - j) = j by omega, + VExpr.liftTelN_getElem?, + NormalizedCtor.fieldsR_getElem?, hBraw] + simp [ls] + have hlu := Lookup.of_getElem? hF + dsimp only [j, r] at hlu + rw [show m-1-r₀.fieldIndex+1 = + m-r₀.fieldIndex by omega] at hlu + have hf0 := VEnv.HasType.bvar + (env := env) (U := gen.recUvars) hlu + obtain ⟨u, hdom₀⟩ := + S.emittedField_defeq hctor hBraw hBview + have hdom₁ := hdom₀.instL + (U' := gen.recUvars) + gen.sourceLevels_wf + have hdomChecked : env.IsDefEq (gen.recUvars) + ((ctor.fieldsR source.uvars source.nparams gen.elimination |>.take + r₀.fieldIndex).reverse ++ + (gen.block.checked.params.map (VExpr.instL ls)).reverse) + (Braw.instL ls) (Bview.instL ls) ((VExpr.sort u).instL ls) := by + simpa [NormalizedCtor.fieldsR, List.map_append, + List.map_reverse, List.map_take] using hdom₁ + have hprefix := S.generationFieldPrefix_ctx_rec hctor r₀.fieldIndex + have hdomGeneration := hdomChecked.defeqDFC S.ord + (hprefix.symm S.ord) + have hjlen : + (Bs.take r₀.fieldIndex).length = + r₀.fieldIndex := by + simp only [Bs, NormalizedCtor.fieldsR, + List.length_take, List.length_map] + omega + have Wmid := Ctx.LiftN.consTel + (n := k+1) + (Bs.take r₀.fieldIndex) + (Ctx.LiftN.zero + (n := k+1) + (Γ := gen.paramsTel.reverse) + (gen.minorTypes.reverse ++ + [gen.motiveType]) + (h := by simp [k, gen.minorTypes_length])) + rw [hjlen, Nat.add_zero] at Wmid + have hdom₂ := hdomGeneration.weakN S.ord Wmid + have Wstack := Ctx.LiftN.zero + (n := m-j) + (Γ := (VExpr.liftTelN (k+1) + (Bs.take j) 0).reverse ++ + ((gen.minorTypes.reverse ++ + [gen.motiveType]) ++ + gen.paramsTel.reverse)) + (Fs.drop j).reverse + (h := by + simp only [List.length_reverse, List.length_drop, + hFsLen]) + have hdom₃ := hdom₂.weakN S.ord Wstack + rw [hctx] at hdom₃ + have hfView := hdom₃.defeq hf0 + have hfield := recArg_rule_fieldType + hrecArg m k (by simpa [j] using hjm) gen.elimination + simp only [RecArg.instL] at hfield + dsimp only [j] at hfView + simp only [Nat.add_zero] at hfView + rw [hfield] at hfView + have hf := hfView.weakN S.ord + (Ctx.LiftN.zero (Γ := Γ) As.reverse) + have hmajor := VEnv.HasType.appN_selfSpine + (env := env) (U := gen.recUvars) + (As := As) + (B := VExpr.appN + (.const gen.block.sourceType.name ls) + (VExpr.bvarRevRange + (m+k+r.binders.length+1) + source.nparams ++ + idxs)) + (Δ := []) (Γ := Γ) (by + simpa [As, r, idxs, j, ls, + RecArg.instL, RecArg.ruleBinders, + List.length_reverse, List.map_map, + Function.comp_def] using hf) + simp only [List.length_nil, VExpr.liftN_zero, + List.nil_append] at hmajor + have hAsLen : + As.length = r.binders.length := by + simp [As, RecArg.ruleBinders, + VExpr.liftTelN_length] + change env.HasType (gen.recUvars) + (As.reverse ++ Γ) + ((VExpr.bvar + (m-1-r.fieldIndex+As.length)).appN + (VExpr.bvarRevRange 0 As.length)) + (VExpr.appN + (.const gen.block.sourceType.name ls) + (VExpr.bvarRevRange + (m+k+r.binders.length+1) + source.nparams ++ + idxs)) at hmajor + rw [hAsLen] at hmajor + have hlen : idxs.length = gen.idxTel.length := by + simpa [idxs, r, RecArg.instL, + GenerationChecked.idxTel] using + (S.viewRecArg_indices_length hctor hr₀).trans + gen.shape.2.2.1.symm + have hcall := S.recApp_hasType hrec + (As.reverse ++ Fs.reverse) + (by + simpa [Γ, List.append_assoc, hAsLen, hFsLen, + Nat.add_comm, Nat.add_left_comm, + Nat.add_assoc] using hsp) + hlen + (by + simpa [Γ, List.append_assoc, hAsLen, hFsLen, + Nat.add_comm, Nat.add_left_comm, + Nat.add_assoc] using hmajor) + have hbaseLift : + (VExpr.appN + (.const + (.str gen.block.sourceType.name "rec") + (gen.recLevels)) + (VExpr.bvarRevRange m + (source.nparams+(k+1)))).liftN + r.binders.length = + VExpr.appN + (.const + (.str gen.block.sourceType.name "rec") + (gen.recLevels)) + (VExpr.bvarRevRange + (m+r.binders.length) + (source.nparams+(1+k))) := by + rw [VExpr.liftN_appN] + simp only [VExpr.liftN] + rw [bvarRevRange_liftN_ge _ _ _ _ + (Nat.zero_le _)] + rw [show source.nparams+(k+1) = + source.nparams+(1+k) by omega] + apply congrArg (VExpr.appN _) + apply VExpr.bvarRevRange_congr + omega + have hbaseRange : + VExpr.appN + ((VExpr.const + (.str gen.block.sourceType.name "rec") + (gen.recLevels)).app + (VExpr.bvar + (source.nparams + + (k + (m + r.binders.length))))) + (VExpr.bvarRevRange + (m+r.binders.length) + (source.nparams+k)) = + VExpr.appN + (.const + (.str gen.block.sourceType.name "rec") + (gen.recLevels)) + (VExpr.bvarRevRange + (m+r.binders.length) + (source.nparams+(1+k))) := by + rw [show source.nparams + (k + (m + r.binders.length)) = + (m+r.binders.length) + (source.nparams+k) by omega, + show source.nparams+(1+k) = (source.nparams+k)+1 by omega] + rfl + have hlam := HasType.lamN htel (by + simpa [Γ, Fs, hAsLen, hFsLen, + List.append_assoc, VExpr.liftN_appN, + bvarRevRange_liftN_ge _ _ _ _ + (Nat.zero_le _), + Nat.add_comm, Nat.add_left_comm, + Nat.add_assoc] using hcall) + simp only [VExpr.liftTelN_length] at hlam + rw [hbaseRange] at hlam + simpa only [RecArg.ruleCall, RecArg.ruleIH, + r, Bs, ls, As, idxs, m, k, Γ, Fs, hbaseLift, + VExpr.liftTelN_length, List.append_assoc, + Nat.add_assoc] using hlam + +/-- The selected mixed constructor minor applied to all raw fields and to one +generated direct or functional recursive call for every retained `RecArg`. -/ +theorem minorApp_hasType {i : Nat} {ctor : NormalizedCtor} + (hci : gen.block.ctorPairs[i]? = some ctor) + (hrec : env.constants + (.str gen.block.sourceType.name "rec") = + some gen.recursor) : + env.HasType (gen.recUvars) + ((VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR source.uvars source.nparams gen.elimination) + 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + (VExpr.appN + (.bvar + (gen.block.ctorPairs.length - 1 - i + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)) + (VExpr.bvarRevRange 0 + (ctor.fieldsR source.uvars source.nparams gen.elimination).length ++ + List.map (fun r => + r.ruleCall + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + gen.block.ctorPairs.length + (VExpr.appN + (.const + (.str gen.block.sourceType.name "rec") + (gen.recLevels)) + (VExpr.bvarRevRange + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + (source.nparams + + gen.block.ctorPairs.length + 1)))) + (ctor.recArgsR source.uvars gen.elimination))) + (VExpr.appN + (.bvar + (gen.block.ctorPairs.length + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)) + (((ctor.resultIndicesR source.uvars gen.elimination).map fun e => + e.liftN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length) ++ + [VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)])) := by + obtain ⟨hik, -⟩ := + List.getElem?_eq_some_iff.1 hci + have hctor := List.mem_of_getElem? hci + let rs := ctor.recArgsR source.uvars gen.elimination + have hrs : + rs = ctor.recArgsR source.uvars gen.elimination := rfl + have hrsLt : ∀ r ∈ rs, + r.fieldIndex < + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length := by + intro r hr + obtain ⟨r₀, hr₀, rfl⟩ := + NormalizedCtor.recArgsR_mem (hrs ▸ hr) + have hview := S.viewRecArg_lt hctor hr₀ + have hfields := + (gen.shape.2.2.2.2.2 ctor hctor).2.2.2 + simp only [RecArg.instL, + NormalizedCtor.fieldsR_length] + omega + rw [VExpr.appN_append] + have hminorAt : + gen.minorTypes[i]? = + some + (VExpr.liftN i + (GenerationChecked.minorType + (source := source) ctor gen.elimination)) := by + simpa [GenerationChecked.minorTypes, hci] using + gen.minorTypesAux_getElem? + gen.block.ctorPairs 0 i + have hlu0 : + ((VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR source.uvars source.nparams gen.elimination) + 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse)))[ + gen.block.ctorPairs.length - 1 - i + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length]? = + some + (VExpr.liftN i + (GenerationChecked.minorType + (source := source) ctor gen.elimination)) := by + rw [getElem?_rstack_mid _ _ _ + (by + simp only [List.length_reverse, + VExpr.liftTelN_length] + omega) + (by + simp only [List.length_reverse, + VExpr.liftTelN_length, + gen.minorTypes_length] + omega), + show + gen.block.ctorPairs.length - 1 - i + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length - + (VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination) + 0).reverse.length = + gen.block.ctorPairs.length - 1 - i by + simp only [List.length_reverse, + VExpr.liftTelN_length] + omega, + List.getElem?_reverse + (by + simp only [gen.minorTypes_length] + omega), + show + gen.minorTypes.length - 1 - + (gen.block.ctorPairs.length - 1 - i) = + i by + simp only [gen.minorTypes_length] + omega, + hminorAt] + have hlu := Lookup.of_getElem? hlu0 + rw [VExpr.liftN_liftN, + show + i + + (gen.block.ctorPairs.length - 1 - i + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + 1) = + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length by omega] at hlu + have hminorEq : + (GenerationChecked.minorType + (source := source) ctor gen.elimination).liftN + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length) = + (VExpr.forallN + (VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination) + 0) + ((VExpr.forallN + (ihsFromRecArgs + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + rs 0) + (VExpr.appN + (.bvar + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + rs.length)) + (((ctor.resultIndicesR source.uvars gen.elimination).map + fun e => + (e.liftN 1 + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length).liftN + rs.length) ++ + [VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (rs.length + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + 1) + source.nparams ++ + VExpr.bvarRevRange rs.length + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)]))).liftN + gen.block.ctorPairs.length + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)).liftN + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length := by + simp only [GenerationChecked.minorType, rs] + conv => lhs; rw [VExpr.liftN_forallN, + VExpr.liftTelN_liftTelN, + liftTelN_congr _ _ + (show + (1 : Nat) + + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length) = + gen.block.ctorPairs.length + 1 + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length by + omega), + show + (0 : Nat) + + (VExpr.liftTelN 1 + (ctor.fieldsR + source.uvars source.nparams gen.elimination) + 0).length = + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length by + simp [VExpr.liftTelN_length]] + conv => rhs; rw [VExpr.liftN_forallN, + VExpr.liftTelN_liftTelN, + show + (0 : Nat) + + (VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination) + 0).length = + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length by + simp [VExpr.liftTelN_length], + VExpr.liftN'_liftN_hi, + Nat.add_comm gen.block.ctorPairs.length + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length] + have hfields := HasType.appN_selfSpine + (env := env) (U := gen.recUvars) + (As := VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR source.uvars source.nparams gen.elimination) + 0) + (B := + (VExpr.forallN + (ihsFromRecArgs + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + rs 0) + (VExpr.appN + (.bvar + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + rs.length)) + (((ctor.resultIndicesR source.uvars gen.elimination).map + fun e => + (e.liftN 1 + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length).liftN + rs.length) ++ + [VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (rs.length + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + 1) + source.nparams ++ + VExpr.bvarRevRange rs.length + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)]))).liftN + gen.block.ctorPairs.length + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length) + (Δ := []) + (Γ := gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse)) + (f := .bvar + (gen.block.ctorPairs.length - 1 - i + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)) + (by + have hb := VEnv.HasType.bvar + (env := env) (U := gen.recUvars) hlu + rw [hminorEq] at hb + simpa [VExpr.liftTelN_length] using hb) + simp only [List.length_nil, + VExpr.liftTelN_length, + VExpr.liftN_zero] at hfields + rw [ihsFromRecArgs_liftN' + (ctor.fieldsR source.uvars source.nparams gen.elimination).length + gen.block.ctorPairs.length rs hrsLt 0 + (VExpr.appN + (.bvar + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + rs.length)) + (((ctor.resultIndicesR source.uvars gen.elimination).map fun e => + (e.liftN 1 + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length).liftN + rs.length) ++ + [VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (rs.length + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + 1) + source.nparams ++ + VExpr.bvarRevRange rs.length + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)])) + (cut := + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length) + rfl] at hfields + have hD : + (VExpr.appN + (.bvar + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + rs.length)) + (((ctor.resultIndicesR source.uvars gen.elimination).map fun e => + (e.liftN 1 + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length).liftN + rs.length) ++ + [VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + (rs.length + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + 1) + source.nparams ++ + VExpr.bvarRevRange rs.length + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)])).liftN + gen.block.ctorPairs.length + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + 0 + rs.length) = + (VExpr.appN + (.bvar + (gen.block.ctorPairs.length + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)) + (((ctor.resultIndicesR source.uvars gen.elimination).map fun e => + e.liftN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length) ++ + [VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)])).liftN + rs.length := by + rw [VExpr.liftN_appN, VExpr.liftN_appN, + List.map_append, List.map_append, + List.map_map, List.map_map] + show VExpr.appN _ (_ ++ [_]) = + VExpr.appN _ (_ ++ [_]) + congr 1 + · show + VExpr.bvar + (liftVar gen.block.ctorPairs.length + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + rs.length) + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + 0 + rs.length)) = + VExpr.bvar + (liftVar rs.length + (gen.block.ctorPairs.length + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length) + 0) + rw [liftVar_le (by omega), + liftVar_le (Nat.zero_le _)] + congr 1 + omega + · congr 1 + · apply List.map_congr_left + intro e _ + simp only [Function.comp_apply] + rw [show + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + 0 + rs.length = + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + rs.length by omega, + VExpr.liftN_liftN_mid e + gen.block.ctorPairs.length rs.length + (Nat.zero_le _)] + · congr 1 + simp only [] + rw [show + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + 0 + rs.length = + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + rs.length by omega] + rw [VExpr.liftN_appN, + VExpr.liftN_appN, + List.map_append, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (by omega), + VExpr.bvarRevRange_liftN_high _ _ _ _ + (by omega), + bvarRevRange_liftN_ge _ _ _ _ + (Nat.zero_le _), + bvarRevRange_liftN_ge _ _ _ _ + (Nat.zero_le _), + VExpr.bvarRevRange_congr source.nparams + (show + gen.block.ctorPairs.length + + (rs.length + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + 1) = + rs.length + + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length + 1) by + omega), + VExpr.bvarRevRange_congr _ + (show rs.length = rs.length + 0 by omega)] + rfl + rw [hD] at hfields + have hres := hasType_appN_ruleIHs + (env := env) (U := gen.recUvars) + (m := + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length) + (k := gen.block.ctorPairs.length) + (rs := rs) + (argOf := fun r => + r.ruleCall + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + gen.block.ctorPairs.length + (VExpr.appN + (.const + (.str gen.block.sourceType.name "rec") + (gen.recLevels)) + (VExpr.bvarRevRange + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + (source.nparams + + gen.block.ctorPairs.length + 1)))) + (fun r hr => + S.ruleCall_hasType hctor hrec (hrs ▸ hr)) + hfields + simpa only [hrs] using hres + +/-- The constructor-headed left side of a mixed iota rule. -/ +theorem recRuleApp_hasType {ctor : NormalizedCtor} + (hctor : ctor ∈ gen.block.ctorPairs) + (hrec : env.constants + (.str gen.block.sourceType.name "rec") = + some gen.recursor) : + env.HasType (gen.recUvars) + ((VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR source.uvars source.nparams gen.elimination) + 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + (VExpr.appN + (VExpr.appN + (.const + (.str gen.block.sourceType.name "rec") + (gen.recLevels)) + (VExpr.bvarRevRange + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + (source.nparams + + gen.block.ctorPairs.length + 1))) + (((ctor.resultIndicesR source.uvars gen.elimination).map fun e => + e.liftN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length) ++ + [VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)])) + (VExpr.appN + (.bvar + (gen.block.ctorPairs.length + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)) + (((ctor.resultIndicesR source.uvars gen.elimination).map fun e => + e.liftN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length) ++ + [VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)])) := by + have hSp₀ := S.result_transport hctor + (gen.minorTypes.reverse ++ [gen.motiveType]) + (g := gen.block.ctorPairs.length + 1) + (by simp [gen.minorTypes_length]) + [] (d := 0) rfl + have hSp : env.SpineWF (gen.recUvars) + ((VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR source.uvars source.nparams gen.elimination) + 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + (VExpr.forallN + (VExpr.liftTelN + ((VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination) + 0).reverse.length + + gen.block.ctorPairs.length + 1) + gen.idxTel 0) + (.sort + (gen.block.checked.resultLevel.inst + (gen.sourceLevels)))) + ((ctor.resultIndicesR source.uvars gen.elimination).map fun e => + e.liftN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length) + (.sort + (gen.block.checked.resultLevel.inst + (gen.sourceLevels))) := by + simpa [List.append_assoc, Nat.add_assoc, + List.length_reverse, VExpr.liftTelN_length] + using hSp₀ + have hidxLen : + ((ctor.resultIndicesR source.uvars gen.elimination).map fun e => + e.liftN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length).length = + gen.idxTel.length := by + simp only [List.length_map, + NormalizedCtor.resultIndicesR, + GenerationChecked.idxTel] + exact (S.viewResultIndices_length hctor).trans + gen.shape.2.2.1.symm + have ha₀ := S.ctorAppRule_hasType hctor + have ha : env.HasType (gen.recUvars) + ((VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR source.uvars source.nparams gen.elimination) + 0).reverse ++ + (gen.minorTypes.reverse ++ + (gen.motiveType :: gen.paramsTel.reverse))) + (VExpr.appN + (.const ctor.raw.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + ((ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + VExpr.bvarRevRange 0 + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)) + (VExpr.appN + (.const gen.block.sourceType.name + (gen.sourceLevels)) + (VExpr.bvarRevRange + ((VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination) + 0).reverse.length + + gen.block.ctorPairs.length + 1) + source.nparams ++ + (ctor.resultIndicesR source.uvars gen.elimination).map fun e => + e.liftN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length)) := by + simpa only [List.length_reverse, + VExpr.liftTelN_length] using ha₀ + have hr := S.recApp_hasType hrec + (VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR source.uvars source.nparams gen.elimination) + 0).reverse + hSp hidxLen ha + rw [List.length_reverse, VExpr.liftTelN_length, + show + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length = + gen.block.ctorPairs.length + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length by + omega, + show + gen.block.ctorPairs.length + + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + 1 = + (ctor.fieldsR + source.uvars source.nparams gen.elimination).length + + gen.block.ctorPairs.length + 1 by + omega] at hr + exact hr + +/-- Every per-constructor mixed iota rule is well formed in an environment +containing the generated recursor constant. -/ +theorem rule_WF {i : Nat} {ctor : NormalizedCtor} + (hci : gen.block.ctorPairs[i]? = some ctor) + (hrec : env.constants + (.str gen.block.sourceType.name "rec") = + some gen.recursor) : + (gen.rule i ctor).WF env := by + have hctor := List.mem_of_getElem? hci + refine ⟨?_, ?_⟩ + · show env.HasType (gen.recUvars) [] + (VExpr.lamN + (gen.paramsTel ++ + gen.motiveType :: gen.minorTypes ++ + VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination) + 0) + _) + (VExpr.forallN _ _) + refine HasType.lamN + (S.ruleBinders_onTel hctor) ?_ + simp only [List.reverse_append, + List.reverse_cons, List.append_nil, + List.append_assoc, List.singleton_append] + exact S.recRuleApp_hasType hctor hrec + · show env.HasType (gen.recUvars) [] + (VExpr.lamN + (gen.paramsTel ++ + gen.motiveType :: gen.minorTypes ++ + VExpr.liftTelN + (gen.block.ctorPairs.length + 1) + (ctor.fieldsR + source.uvars source.nparams gen.elimination) + 0) + _) + (VExpr.forallN _ _) + refine HasType.lamN + (S.ruleBinders_onTel hctor) ?_ + simp only [List.reverse_append, + List.reverse_cons, List.append_nil, + List.append_assoc, List.singleton_append] + exact S.minorApp_hasType hci hrec + +end GenerationEnv + +theorem ctorFieldsR_length {U np : Nat} {c : VConstVal} : + (ctorFieldsR U np c).length = (ctorFields (VExpr.dropN np c.type)).length := + List.length_map .. + +theorem ctorFieldsR_getElem? {U np : Nat} {c : VConstVal} {q : Nat} : + (ctorFieldsR U np c)[q]? = + (ctorFields (VExpr.dropN np c.type))[q]?.map (VExpr.instL (VLevel.params' U 1)) := + List.getElem?_map .. + +/-! ## The post-family environment invariant -/ + +/-- The facts available immediately after inserting a checked family, +before any constructor has been installed. Constructor types may refer to the +family, so this is the precise staging invariant needed to validate them. -/ +structure DirectFamilyEnv (env : VEnv) (U : Nat) (T : Name) + (np : Nat) (l : VLevel) (ty : VInductiveType) : Prop where + ord : env.Ordered + hl : l.WF U + hsort : VExpr.resultOf (VExpr.dropN np ty.type) = .sort l + hlen : (VExpr.telN np ty.type).length = np + hT : env.constants T = some ⟨U, ty.type⟩ + hparams : + OnTel env U [] + (VExpr.telN np ty.type ++ + ctorFields (VExpr.dropN np ty.type)) + +section DirectFamily + +variable {env : VEnv} {U : Nat} {T : Name} {np : Nat} + {l : VLevel} {ty : VInductiveType} + (S : DirectFamilyEnv env U T np l ty) +include S + +theorem DirectFamilyEnv.tyType_eq : + ty.type = + VExpr.forallN (VExpr.telN np ty.type) + (VExpr.forallN + (ctorFields (VExpr.dropN np ty.type)) (.sort l)) := by + conv => lhs + rw [← VExpr.forallN_telN_dropN np ty.type, + ← forallN_ctorFields_resultOf + (VExpr.dropN np ty.type), S.hsort] + +theorem DirectFamilyEnv.tyType_isType : + env.IsType U [] ty.type := by + rw [S.tyType_eq, ← VExpr.forallN_append] + exact IsType.forallN S.hparams ⟨_, HasType.sort S.hl⟩ + +theorem DirectFamilyEnv.tconst_decl {Γ} : + env.HasType U Γ (.const T (VLevel.params U)) ty.type := + (HasType.const0 S.hT S.tyType_isType).weak0 S.ord + +/-- Applying the newly inserted family to its parameter self-spine exposes +the index telescope in any constructor-field context. -/ +theorem DirectFamilyEnv.recAppPi_hasType_decl (Δ : List VExpr) : + env.HasType U (Δ ++ (VExpr.telN np ty.type).reverse) + (recApp U T np Δ.length) + ((VExpr.forallN + (ctorFields (VExpr.dropN np ty.type)) (.sort l)).liftN + Δ.length) := by + have hcl : ty.type.ClosedN 0 := + Ordered.closedC (ci := ⟨U, ty.type⟩) S.ord S.hT + have hf : env.HasType U + (Δ ++ (VExpr.telN np ty.type).reverse ++ []) + (.const T (VLevel.params U)) + ((VExpr.forallN (VExpr.telN np ty.type) + (VExpr.forallN + (ctorFields (VExpr.dropN np ty.type)) (.sort l))).liftN + (Δ.length + (VExpr.telN np ty.type).length)) := by + rw [← S.tyType_eq, hcl.liftN_eq (Nat.zero_le _)] + exact S.tconst_decl + have hout := HasType.appN_selfSpine (env := env) (U := U) hf + rw [S.hlen] at hout + simpa [recApp, List.append_nil] using hout + +/-- Checked constructor fields form a telescope as soon as the family is +available; no constructor lookup is needed for recursive occurrences. -/ +theorem DirectFamilyEnv.fieldsWF_onTel_decl : + ∀ (Bs : List VExpr) (Δd : List VExpr) (j : Nat), + Δd.length = j → + fieldsWF U T np env l + (ctorFields (VExpr.dropN np ty.type)) + (Δd ++ (VExpr.telN np ty.type).reverse) j Bs → + OnTel env U + (Δd ++ (VExpr.telN np ty.type).reverse) Bs + | [], _, _, _, _ => trivial + | B :: Bs, Δd, j, hΔ, ⟨hB, hSp, hrest⟩ => by + refine ⟨?_, DirectFamilyEnv.fieldsWF_onTel_decl + Bs (B :: Δd) (j+1) (by simp [hΔ]) hrest⟩ + rcases hB with hrec | hfun | ⟨-, u, h, -⟩ + · obtain ⟨hBeq, -, -⟩ := isRecField_eq hrec + have hTapp := S.recAppPi_hasType_decl Δd + rw [hΔ, VExpr.liftN_forallN] at hTapp + have hgoal : env.HasType U + (Δd ++ (VExpr.telN np ty.type).reverse) + B (.sort l) := by + rw [hBeq, VExpr.appN_append] + exact (hSp hrec).hasType_appN hTapp + exact ⟨_, hgoal⟩ + · obtain ⟨r, hr, -, hrtel, hrsp⟩ := hfun + obtain ⟨hrj, -, hBeq, -, -, -⟩ := recArg?_eq hr + rw [hBeq] + refine IsType.forallN hrtel ⟨l, ?_⟩ + have hTapp := + S.recAppPi_hasType_decl (r.binders.reverse ++ Δd) + rw [VExpr.liftN_forallN] at hTapp + have hbase : env.HasType U + (r.binders.reverse ++ + (Δd ++ (VExpr.telN np ty.type).reverse)) + (recApp U T np (j + r.binders.length)) + (VExpr.forallN + (VExpr.liftTelN (j + r.binders.length) + (ctorFields (VExpr.dropN np ty.type)) 0) + (.sort l)) := by + simpa [List.append_assoc, hΔ, Nat.add_comm] using hTapp + rw [hrj] at hrsp + simpa [recApp, VExpr.appN_append] using + hrsp.hasType_appN hbase + · exact ⟨u, h⟩ + +/-- The analyzed result spine types the exact family application at the end +of a constructor declaration. -/ +theorem DirectFamilyEnv.ctorResult_hasType_decl + {c : VConstVal} + (hresult : env.SpineWF U + ((ctorFields (VExpr.dropN np c.type)).reverse ++ + (VExpr.telN np ty.type).reverse) + (VExpr.forallN + (VExpr.liftTelN + (ctorFields (VExpr.dropN np c.type)).length + (ctorFields (VExpr.dropN np ty.type)) 0) + (.sort l)) + (recFieldIdxs np + (VExpr.resultOf (VExpr.dropN np c.type))) + (.sort l)) : + env.HasType U + ((ctorFields (VExpr.dropN np c.type)).reverse ++ + (VExpr.telN np ty.type).reverse) + (VExpr.appN (.const T (VLevel.params U)) + (VExpr.bvarRevRange + (ctorFields (VExpr.dropN np c.type)).length np ++ + recFieldIdxs np + (VExpr.resultOf (VExpr.dropN np c.type)))) + (.sort l) := by + have hTapp := S.recAppPi_hasType_decl + ((ctorFields (VExpr.dropN np c.type)).reverse) + rw [List.length_reverse, VExpr.liftN_forallN] at hTapp + rw [VExpr.appN_append] + exact hresult.hasType_appN hTapp + +end DirectFamily + +/-- A checked family insertion constructs the post-family invariant directly +from the checker contract. -/ +theorem Checked.WF.toDirectFamilyEnv + {source : VInductDecl} {checked : source.Checked} + {pre envT : VEnv} (hpre : pre.Ordered) + (h : checked.WF pre) + (hadd : pre.addConst checked.type.name + checked.type.toVConstant = some envT) : + DirectFamilyEnv envT source.uvars checked.type.name + source.nparams checked.resultLevel checked.type where + ord := by + have hfamily : checked.type.toVConstant.WF pre := by + show pre.IsType checked.type.uvars [] checked.type.type + rw [checked.direct_anatomy.1] + exact h.family_isType + exact .const hpre hfamily hadd + hl := checked.direct_anatomy.2.2.1 + hsort := checked.result_eq + hlen := by simpa [checked.params_eq] using + checked.direct_anatomy.2.1 + hT := by + have hout := addConst_self hadd + change envT.constants checked.type.name = + some ⟨checked.type.uvars, checked.type.type⟩ at hout + rw [checked.direct_anatomy.1] at hout + exact hout + hparams := by + simpa [checked.params_eq, checked.indices_eq] using + h.1.mono (addConst_le hadd) + +/-! ## The stage-3 environment invariant -/ + +/-- Everything the piece-typing lemmas need about an environment that +already contains the block's type constant and constructors. -/ +structure Stage3Env (env : VEnv) (U : Nat) (T : Name) (np : Nat) (l : VLevel) + (ty : VInductiveType) : Prop where + ord : env.Ordered + hl : l.WF U + hsort : VExpr.resultOf (VExpr.dropN np ty.type) = .sort l + hlen : (VExpr.telN np ty.type).length = np + hT : env.constants T = some ⟨U, ty.type⟩ + hcs : ∀ c ∈ ty.ctors, env.constants c.name = some ⟨U, c.type⟩ + htel : ∀ c ∈ ty.ctors, VExpr.telN np c.type = VExpr.telN np ty.type + hs3 : ∀ c ∈ ty.ctors, stage3Ctor U T np (ctorFields (VExpr.dropN np ty.type)).length 0 + (VExpr.dropN np c.type) = true + hparams : OnTel env U [] (VExpr.telN np ty.type ++ ctorFields (VExpr.dropN np ty.type)) + hfields : ∀ c ∈ ty.ctors, fieldsWF U T np env l (ctorFields (VExpr.dropN np ty.type)) + (VExpr.telN np ty.type).reverse 0 (ctorFields (VExpr.dropN np c.type)) + hresult : ∀ c ∈ ty.ctors, env.SpineWF U + ((ctorFields (VExpr.dropN np c.type)).reverse ++ (VExpr.telN np ty.type).reverse) + (VExpr.forallN (VExpr.liftTelN (ctorFields (VExpr.dropN np c.type)).length + (ctorFields (VExpr.dropN np ty.type)) 0) (.sort l)) + (recFieldIdxs np (VExpr.resultOf (VExpr.dropN np c.type))) (.sort l) + +variable {env : VEnv} {U : Nat} {T : Name} {np : Nat} {l : VLevel} {ty : VInductiveType} + (S : Stage3Env env U T np l ty) +include S +-- `S` is included throughout so every lemma below is reachable as `S.lemma`, +-- including those whose statement and proof never mention it. +set_option linter.unusedSectionVars false + +theorem Stage3Env.mono {env' : VEnv} (henv : env ≤ env') (ord' : env'.Ordered) : + Stage3Env env' U T np l ty where + ord := ord' + hl := S.hl + hsort := S.hsort + hlen := S.hlen + hT := henv.constants S.hT + hcs := fun c hc => henv.constants (S.hcs c hc) + htel := S.htel + hs3 := S.hs3 + hparams := S.hparams.mono henv + hfields := fun c hc => fieldsWF_mono henv (S.hfields c hc) + hresult := fun c hc => (S.hresult c hc).mono henv + +/-- The block's type split at the parameters and indices. -/ +theorem Stage3Env.tyType_eq : ty.type = + VExpr.forallN (VExpr.telN np ty.type) + (VExpr.forallN (ctorFields (VExpr.dropN np ty.type)) (.sort l)) := by + conv => lhs; rw [← VExpr.forallN_telN_dropN np ty.type, + ← forallN_ctorFields_resultOf (VExpr.dropN np ty.type), S.hsort] + +/-- The type of the block constant is a type. -/ +theorem Stage3Env.tyType_isType : env.IsType U [] ty.type := by + rw [S.tyType_eq, ← VExpr.forallN_append] + exact IsType.forallN S.hparams ⟨_, HasType.sort S.hl⟩ + +/-- The block constant at the declaration universes, in any context. -/ +theorem Stage3Env.tconst_decl {Γ} : + env.HasType U Γ (.const T (VLevel.params U)) ty.type := + (HasType.const0 S.hT S.tyType_isType).weak0 S.ord + +/-- The type of the block constant, instantiated to the recursor universes. -/ +theorem Stage3Env.tyType_instL : + ty.type.instL (VLevel.params' U 1) = + VExpr.forallN (paramsTel U np ty) + (VExpr.forallN (idxTel U np ty) (.sort (l.inst (VLevel.params' U 1)))) := by + conv => lhs; rw [S.tyType_eq] + rw [VExpr.instL_forallN, VExpr.instL_forallN] + rfl + +/-- The block constant at the recursor universes, in any context. -/ +theorem Stage3Env.tconst {Γ} : + env.HasType (U+1) Γ (.const T (VLevel.params' U 1)) (ty.type.instL (VLevel.params' U 1)) := by + have := (S.tconst_decl (Γ := [])).instL (U' := U+1) VLevel.params'_one_wf + rw [show (VExpr.const T (VLevel.params U)).instL (VLevel.params' U 1) = + .const T (VLevel.params' U 1) from by + simp [VExpr.instL, VLevel.params_map_inst_params']] at this + exact this.weak0 S.ord + +/-- The parameter spine applied to the block constant, `Δ` binders past +the parameter telescope (declaration universes): the index pi. -/ +theorem Stage3Env.recAppPi_hasType_decl (Δ : List VExpr) : + env.HasType U (Δ ++ (VExpr.telN np ty.type).reverse) (recApp U T np Δ.length) + ((VExpr.forallN (ctorFields (VExpr.dropN np ty.type)) (.sort l)).liftN Δ.length) := by + have hcl : ty.type.ClosedN 0 := Ordered.closedC (ci := ⟨U, ty.type⟩) S.ord S.hT + have hf : env.HasType U (Δ ++ (VExpr.telN np ty.type).reverse ++ []) + (.const T (VLevel.params U)) + ((VExpr.forallN (VExpr.telN np ty.type) + (VExpr.forallN (ctorFields (VExpr.dropN np ty.type)) (.sort l))).liftN + (Δ.length + (VExpr.telN np ty.type).length)) := by + rw [← S.tyType_eq, hcl.liftN_eq (Nat.zero_le _)] + exact S.tconst_decl + have := HasType.appN_selfSpine (env := env) (U := U) hf + rw [S.hlen] at this + simpa [recApp, List.append_nil] using this + +/-- The parameter spine at the recursor universes: the index pi. -/ +theorem Stage3Env.recAppPi_hasType (Δ : List VExpr) : + env.HasType (U+1) (Δ ++ (paramsTel U np ty).reverse) (recApp' U T np Δ.length) + ((VExpr.forallN (idxTel U np ty) + (.sort (l.inst (VLevel.params' U 1)))).liftN Δ.length) := by + have hcl : (ty.type.instL (VLevel.params' U 1)).ClosedN 0 := + (Ordered.closedC (ci := ⟨U, ty.type⟩) S.ord S.hT).instL + have hf : env.HasType (U+1) (Δ ++ (paramsTel U np ty).reverse ++ []) + (.const T (VLevel.params' U 1)) + ((VExpr.forallN (paramsTel U np ty) + (VExpr.forallN (idxTel U np ty) (.sort (l.inst (VLevel.params' U 1))))).liftN + (Δ.length + (paramsTel U np ty).length)) := by + rw [← S.tyType_instL, hcl.liftN_eq (Nat.zero_le _)] + exact S.tconst + have := HasType.appN_selfSpine (env := env) (U := U+1) hf + rw [show (paramsTel U np ty).length = np from by + simp [paramsTel, List.length_map, S.hlen]] at this + simpa [recApp', List.append_nil] using this + +/-- The block applied to the full parameter-and-index self-spine is a +sort, in the context of the indices over the parameters. -/ +theorem Stage3Env.motiveTApp_hasType : + env.HasType (U+1) ((idxTel U np ty).reverse ++ (paramsTel U np ty).reverse) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (idxTel U np ty).length np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (l.inst (VLevel.params' U 1))) := by + have hcl : (ty.type.instL (VLevel.params' U 1)).ClosedN 0 := + (Ordered.closedC (ci := ⟨U, ty.type⟩) S.ord S.hT).instL + have hf : env.HasType (U+1) + ([] ++ (paramsTel U np ty ++ idxTel U np ty).reverse ++ []) + (.const T (VLevel.params' U 1)) + ((VExpr.forallN (paramsTel U np ty ++ idxTel U np ty) + (.sort (l.inst (VLevel.params' U 1)))).liftN + (List.length ([] : List VExpr) + + (paramsTel U np ty ++ idxTel U np ty).length)) := by + rw [VExpr.forallN_append, ← S.tyType_instL, hcl.liftN_eq (Nat.zero_le _)] + exact S.tconst + have h2 := HasType.appN_selfSpine (env := env) (U := U+1) hf + rw [show List.length ([] : List VExpr) = 0 from rfl, + VExpr.bvarRevRange_congr' 0 (show (paramsTel U np ty ++ idxTel U np ty).length = + (idxTel U np ty).length + np from by + simp [paramsTel, List.length_map, S.hlen]; omega), + ← VExpr.bvarRevRange_append] at h2 + simpa [List.append_nil, List.reverse_append] using h2 + +/-- Field telescopes are well-formed in context, at the declaration +universes: recursive fields by the partial parameter application of the +block followed by the carried index-spine typing. -/ +theorem Stage3Env.fieldsWF_onTel_decl : + ∀ (Bs : List VExpr) (Δd : List VExpr) (j : Nat), Δd.length = j → + fieldsWF U T np env l (ctorFields (VExpr.dropN np ty.type)) + (Δd ++ (VExpr.telN np ty.type).reverse) j Bs → + OnTel env U (Δd ++ (VExpr.telN np ty.type).reverse) Bs + | [], _, _, _, _ => trivial + | B :: Bs, Δd, j, hΔ, ⟨hB, hSp, hT⟩ => by + refine ⟨?_, Stage3Env.fieldsWF_onTel_decl Bs (B :: Δd) (j+1) (by simp [hΔ]) hT⟩ + rcases hB with hrec | hfun | ⟨-, u, h, -⟩ + · obtain ⟨hBeq, -, -⟩ := isRecField_eq hrec + have hTapp := S.recAppPi_hasType_decl Δd + rw [hΔ, VExpr.liftN_forallN] at hTapp + have hgoal : env.HasType U (Δd ++ (VExpr.telN np ty.type).reverse) B (.sort l) := by + rw [hBeq, VExpr.appN_append] + exact (hSp hrec).hasType_appN hTapp + exact ⟨_, hgoal⟩ + · obtain ⟨r, hr, -, hrtel, hrsp⟩ := hfun + obtain ⟨hrj, -, hBeq, -, -, -⟩ := recArg?_eq hr + rw [hBeq] + refine IsType.forallN hrtel ⟨l, ?_⟩ + have hTapp := S.recAppPi_hasType_decl (r.binders.reverse ++ Δd) + rw [VExpr.liftN_forallN] at hTapp + have hbase : env.HasType U + (r.binders.reverse ++ (Δd ++ (VExpr.telN np ty.type).reverse)) + (recApp U T np (j + r.binders.length)) + (VExpr.forallN + (VExpr.liftTelN (j + r.binders.length) + (ctorFields (VExpr.dropN np ty.type)) 0) (.sort l)) := by + simpa [List.append_assoc, hΔ, Nat.add_comm] using hTapp + rw [hrj] at hrsp + simpa [recApp, VExpr.appN_append] using hrsp.hasType_appN hbase + · exact ⟨u, h⟩ + +/-- Field telescopes are well-formed in context, at the recursor +universes. -/ +theorem Stage3Env.fieldsWF_onTel : + ∀ (Bs : List VExpr) (Δd : List VExpr) (j : Nat), Δd.length = j → + fieldsWF U T np env l (ctorFields (VExpr.dropN np ty.type)) + (Δd ++ (VExpr.telN np ty.type).reverse) j Bs → + OnTel env (U+1) + ((Δd ++ (VExpr.telN np ty.type).reverse).map (VExpr.instL (VLevel.params' U 1))) + (Bs.map (VExpr.instL (VLevel.params' U 1))) + | Bs, Δd, j, hΔ, hfields => by + exact (S.fieldsWF_onTel_decl Bs Δd j hΔ hfields).instL VLevel.params'_one_wf + +/-- The constructor's type, split at the parameters. -/ +theorem Stage3Env.ctorType_eq {c : VConstVal} (hc : c ∈ ty.ctors) : + c.type = VExpr.forallN (VExpr.telN np ty.type) + (VExpr.forallN (ctorFields (VExpr.dropN np c.type)) + (VExpr.appN (.const T (VLevel.params U)) + (VExpr.bvarRevRange (0 + (ctorFields (VExpr.dropN np c.type)).length) np ++ + recFieldIdxs np (VExpr.resultOf (VExpr.dropN np c.type))))) := by + conv => lhs; rw [← VExpr.forallN_telN_dropN np c.type, S.htel c hc, + (stage3Ctor_eq (S.hs3 c hc)).1] + +theorem Stage3Env.ctorType_instL {c : VConstVal} (hc : c ∈ ty.ctors) : + c.type.instL (VLevel.params' U 1) = + VExpr.forallN (paramsTel U np ty) + (VExpr.forallN (ctorFieldsR U np c) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (0 + (ctorFields (VExpr.dropN np c.type)).length) np ++ + ctorIdxs U np c))) := by + conv => lhs; rw [S.ctorType_eq hc, VExpr.instL_forallN, VExpr.instL_forallN, + VExpr.instL_appN, List.map_append, bvarRevRange_instL, + show (VExpr.const T (VLevel.params U)).instL (VLevel.params' U 1) = + .const T (VLevel.params' U 1) from by + simp [VExpr.instL, VLevel.params_map_inst_params']] + rfl + +/-- The constructor's result, typed by the partial parameter application +and the carried result-spine typing (declaration universes). -/ +theorem Stage3Env.ctorResult_hasType_decl {c : VConstVal} (hc : c ∈ ty.ctors) : + env.HasType U + ((ctorFields (VExpr.dropN np c.type)).reverse ++ (VExpr.telN np ty.type).reverse) + (VExpr.appN (.const T (VLevel.params U)) + (VExpr.bvarRevRange (0 + (ctorFields (VExpr.dropN np c.type)).length) np ++ + recFieldIdxs np (VExpr.resultOf (VExpr.dropN np c.type)))) + (.sort l) := by + have hTapp := S.recAppPi_hasType_decl ((ctorFields (VExpr.dropN np c.type)).reverse) + rw [List.length_reverse, VExpr.liftN_forallN] at hTapp + rw [VExpr.appN_append, + VExpr.bvarRevRange_congr np (show 0 + (ctorFields (VExpr.dropN np c.type)).length = + (ctorFields (VExpr.dropN np c.type)).length from by omega)] + exact (S.hresult c hc).hasType_appN hTapp + +/-- A constructor's declared type is well-formed (declaration universes). -/ +theorem Stage3Env.ctorType_isType {c : VConstVal} (hc : c ∈ ty.ctors) : + env.IsType U [] c.type := by + rw [S.ctorType_eq hc] + refine IsType.forallN (S.hparams.of_append.1) ?_ + simp only [List.append_nil] + refine IsType.forallN + (S.fieldsWF_onTel_decl _ [] 0 rfl (by simpa using S.hfields c hc)) ?_ + exact ⟨_, by simpa using S.ctorResult_hasType_decl hc⟩ + +/-- The constructor constant at the recursor universes, any context. -/ +theorem Stage3Env.cConst {c : VConstVal} (hc : c ∈ ty.ctors) {Γ} : + env.HasType (U+1) Γ (.const c.name (VLevel.params' U 1)) + (c.type.instL (VLevel.params' U 1)) := by + have h0 := HasType.const0 (S.hcs c hc) (S.ctorType_isType hc) + have := h0.instL (U' := U+1) VLevel.params'_one_wf + rw [show (VExpr.const c.name (VLevel.params U)).instL (VLevel.params' U 1) = + .const c.name (VLevel.params' U 1) from by + simp [VExpr.instL, VLevel.params_map_inst_params']] at this + exact this.weak0 S.ord + +/-- The index telescope is well-formed over the parameters, at the +recursor universes. -/ +theorem Stage3Env.idxTel_onTel : + OnTel env (U+1) (paramsTel U np ty).reverse (idxTel U np ty) := by + have h0 := (S.hparams.of_append.2).instL (U' := U+1) VLevel.params'_one_wf + simpa [paramsTel, idxTel, List.map_reverse] using h0 + +theorem Stage3Env.motive_isType : + env.IsType (U+1) (paramsTel U np ty).reverse (motiveType U T np ty) := by + refine IsType.forallN S.idxTel_onTel ?_ + exact ⟨_, HasType.forallE S.motiveTApp_hasType (HasType.sort (Nat.succ_pos U))⟩ + +/-- Transport a recursive field's index-spine typing into a rule or minor +context: instantiate the universes, insert `mid` (motive, or motive and +minors) at the field's depth `j`, and push a `d`-entry stack underneath. -/ +theorem Stage3Env.spine_transport {c : VConstVal} (hc : c ∈ ty.ctors) + {j : Nat} {B : VExpr} + (hBj : (ctorFields (VExpr.dropN np c.type))[j]? = some B) + (hrec : isRecField U T np (ctorFields (VExpr.dropN np ty.type)).length j B = true) + (mid : List VExpr) {g : Nat} (hg : mid.length = g) + (As₂ : List VExpr) {d : Nat} (hd : As₂.length = d) : + env.SpineWF (U+1) + (As₂ ++ ((VExpr.liftTelN g ((ctorFieldsR U np c).take j) 0).reverse ++ + (mid ++ (paramsTel U np ty).reverse))) + (VExpr.forallN (VExpr.liftTelN (j + g + d) (idxTel U np ty) 0) + (.sort (l.inst (VLevel.params' U 1)))) + (((recFieldIdxs np B).map (VExpr.instL (VLevel.params' U 1))).map + fun e => (e.liftN g j).liftN d) + (.sort (l.inst (VLevel.params' U 1))) := by + have hjlen : ((ctorFieldsR U np c).take j).length = j := by + have h1 := (List.getElem?_eq_some_iff.1 hBj).1 + simp only [ctorFieldsR, List.length_take, List.length_map] + omega + -- (1) declaration-universe spine from the fieldsWF chain + have h0 := fieldsWF_spine (S.hfields c hc) j B (by simpa using hBj) + (by simpa using hrec) + rw [Nat.zero_add] at h0 + -- (2) universe instantiation + have h1 := h0.instL (U' := U+1) VLevel.params'_one_wf + rw [VExpr.instL_forallN, VExpr.liftTelN_instL, + show (((ctorFields (VExpr.dropN np c.type)).take j).reverse ++ + (VExpr.telN np ty.type).reverse).map (VExpr.instL (VLevel.params' U 1)) = + ((ctorFieldsR U np c).take j).reverse ++ (paramsTel U np ty).reverse from by + simp [ctorFieldsR, paramsTel, List.map_reverse, List.map_take]] at h1 + -- (3) insert `mid` at depth `j` + have W₁ := Ctx.LiftN.consTel (n := mid.length) ((ctorFieldsR U np c).take j) + (Ctx.LiftN.zero (Γ := (paramsTel U np ty).reverse) mid) + rw [hjlen, Nat.add_zero] at W₁ + have h2 := h1.weakN S.ord W₁ + rw [VExpr.liftN_forallN, hg] at h2 + -- (4) push the stack underneath + have h3 := h2.weakN S.ord (Ctx.LiftN.zero (Γ := _) As₂ (h := hd)) + rw [VExpr.liftN_forallN] at h3 + rw [VExpr.liftTelN_liftTelN_hi' j g _ 0 (by omega), VExpr.liftTelN_liftTelN, + show (ctorFields (VExpr.dropN np ty.type)).map (VExpr.instL (VLevel.params' U 1)) = + idxTel U np ty from rfl] at h3 + simpa [VExpr.instL, VExpr.liftN, List.map_map, Function.comp_def, + List.append_assoc] using h3 + +/-- Transport all semantic evidence for a recursive argument beneath a Pi +telescope. This is the functional counterpart of `spine_transport`: it +transports both the argument telescope and the terminal family-index spine. +-/ +theorem Stage3Env.recArg_transport {c : VConstVal} (hc : c ∈ ty.ctors) + {r₀ : RecArg} {B : VExpr} + (hB : (ctorFields (VExpr.dropN np c.type))[r₀.fieldIndex]? = some B) + (hr : recArg? U T np (idxTel U np ty).length r₀.fieldIndex B = some r₀) + (mid : List VExpr) {g : Nat} (hg : mid.length = g) + (As₂ : List VExpr) {d : Nat} (hd : As₂.length = d) : + let r := r₀.instL (VLevel.params' U 1) + let As := VExpr.liftTelN d (VExpr.liftTelN g r.binders r.fieldIndex) 0 + OnTel env (U+1) + (As₂ ++ ((VExpr.liftTelN g + ((ctorFieldsR U np c).take r.fieldIndex) 0).reverse ++ + (mid ++ (paramsTel U np ty).reverse))) As ∧ + env.SpineWF (U+1) + (As.reverse ++ + (As₂ ++ ((VExpr.liftTelN g + ((ctorFieldsR U np c).take r.fieldIndex) 0).reverse ++ + (mid ++ (paramsTel U np ty).reverse)))) + (VExpr.forallN + (VExpr.liftTelN + (r.fieldIndex + r.binders.length + g + d) (idxTel U np ty) 0) + (.sort (l.inst (VLevel.params' U 1)))) + (r.indices.map fun e => + (e.liftN g (r.fieldIndex + r.binders.length)).liftN d r.binders.length) + (.sort (l.inst (VLevel.params' U 1))) := by + dsimp only + have hjlt : r₀.fieldIndex < (ctorFields (VExpr.dropN np c.type)).length := + (List.getElem?_eq_some_iff.1 hB).1 + have hsem := fieldsWF_recArg (S.hfields c hc) r₀.fieldIndex B r₀ hB (by + simpa [idxTel_length] using hr) + have htel₁ := hsem.1.instL (U' := U+1) VLevel.params'_one_wf + have hsp₁ := hsem.2.instL (U' := U+1) VLevel.params'_one_wf + have hctx : + (((ctorFields (VExpr.dropN np c.type)).take r₀.fieldIndex).reverse ++ + (VExpr.telN np ty.type).reverse).map (VExpr.instL (VLevel.params' U 1)) = + ((ctorFieldsR U np c).take r₀.fieldIndex).reverse ++ + (paramsTel U np ty).reverse := by + simp [ctorFieldsR, paramsTel, List.map_reverse, List.map_take] + rw [hctx] at htel₁ + rw [List.map_append, List.map_reverse, hctx] at hsp₁ + simp only [VExpr.instL_forallN, VExpr.liftTelN_instL] at htel₁ hsp₁ + have hjlen : ((ctorFieldsR U np c).take r₀.fieldIndex).length = + r₀.fieldIndex := by + simp only [ctorFieldsR, List.length_take, List.length_map] + omega + have W₁ := Ctx.LiftN.consTel (n := mid.length) + ((ctorFieldsR U np c).take r₀.fieldIndex) + (Ctx.LiftN.zero (Γ := (paramsTel U np ty).reverse) mid) + rw [hjlen, Nat.add_zero] at W₁ + have htel₂ := htel₁.weakN S.ord W₁ + have hsp₂ := hsp₁.weakN S.ord + (Ctx.LiftN.consTel + (r₀.binders.map (VExpr.instL (VLevel.params' U 1))) W₁) + rw [hg] at htel₂ hsp₂ + have W₂ := Ctx.LiftN.zero + (Γ := (VExpr.liftTelN g + ((ctorFieldsR U np c).take r₀.fieldIndex) 0).reverse ++ + (mid ++ (paramsTel U np ty).reverse)) As₂ (h := hd) + have htel₃ := htel₂.weakN S.ord W₂ + have hsp₃ := hsp₂.weakN S.ord + (Ctx.LiftN.consTel + (VExpr.liftTelN g + (r₀.binders.map (VExpr.instL (VLevel.params' U 1))) r₀.fieldIndex) W₂) + refine ⟨?_, ?_⟩ + · simpa [RecArg.instL, List.append_assoc] using htel₃ + · simp only [List.length_map, VExpr.liftTelN_length, Nat.add_zero] at hsp₃ + rw [VExpr.liftN_forallN, VExpr.liftN_forallN, + VExpr.liftTelN_liftTelN_hi' (r₀.fieldIndex + r₀.binders.length) g _ 0 + (by omega), + VExpr.liftTelN_liftTelN_mid + (r₀.fieldIndex + r₀.binders.length + g) d _ 0 r₀.binders.length + (Nat.zero_le _) (by omega), + show (ctorFields (VExpr.dropN np ty.type)).map + (VExpr.instL (VLevel.params' U 1)) = idxTel U np ty from rfl] at hsp₃ + rw [show r₀.binders.length + r₀.fieldIndex = + r₀.fieldIndex + r₀.binders.length from Nat.add_comm _ _] at hsp₃ + simpa [RecArg.instL, VExpr.instL, VExpr.liftN, List.map_map, + Function.comp_def, List.append_assoc] using hsp₃ + +omit S in +/-- Apply a motive variable to a typed family-index spine and major premise. +Unlike `Stage3Env.motiveApp_hasType`, this helper is independent of the +recursor's minor-premise stack, so it also applies under a recursive +argument's private Pi telescope. -/ +theorem motiveVarApp_hasType {Γ : List VExpr} {K : Nat} + {idxs : List VExpr} {a : VExpr} + (hM : env.HasType (U+1) Γ (.bvar K) + ((motiveType U T np ty).liftN (K+1))) + (hidx : env.SpineWF (U+1) Γ + (VExpr.forallN (VExpr.liftTelN (K+1) (idxTel U np ty) 0) + (.sort (l.inst (VLevel.params' U 1)))) + idxs (.sort (l.inst (VLevel.params' U 1)))) + (hlen : idxs.length = (idxTel U np ty).length) + (ha : env.HasType (U+1) Γ a + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (K+1) np ++ idxs))) : + env.HasType (U+1) Γ + (VExpr.appN (.bvar K) (idxs ++ [a])) (.sort (.param 0)) := by + rw [motiveType_liftN] at hM + have hshape := hidx.retarget + (by simpa only [VExpr.liftTelN_length] using hlen) + (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (K+1 + (idxTel U np ty).length) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (.param 0))) + rw [VExpr.instRev_forallE_sort, VExpr.instRev_appN, + VExpr.instRev_closedN _ (C := .const T (VLevel.params' U 1)) trivial, + List.map_append, + VExpr.map_instRev_bvarRevRange_ge _ _ _ (by rw [hlen]; omega), + show K+1+(idxTel U np ty).length-idxs.length = K+1 from by + rw [hlen, Nat.add_sub_cancel], + VExpr.bvarRevRange_congr' 0 hlen.symm, + VExpr.map_instRev_bvarRevRange] at hshape + rw [hlen] at hshape + have hApp := hshape.hasType_appN hM + rw [VExpr.appN_append] + exact HasType.app hApp (by simpa using ha) + +/-- One generalized induction-hypothesis entry is a type in the minor +premise context. Recursive Pi arguments become Pi-valued hypotheses whose +body applies both the motive and the recursive field to the same private +self-spine. -/ +theorem Stage3Env.recArgMinor_isType {c : VConstVal} (hc : c ∈ ty.ctors) + {r : RecArg} + (hrmem : r ∈ recArgsR U T np (idxTel U np ty).length c) + (Δ : List VExpr) (p : Nat) (hΔ : Δ.length = p) : + env.IsType (U+1) + (Δ ++ (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)) + (r.minorIH (ctorFieldsR U np c).length p) := by + obtain ⟨r₀, B, rfl, hB, hr₀⟩ := recArgsR_mem hrmem + let r := r₀.instL (VLevel.params' U 1) + let m := (ctorFieldsR U np c).length + let j := r₀.fieldIndex + let Fs := VExpr.liftTelN 1 (ctorFieldsR U np c) 0 + let As := r.minorBinders m p + let idxs := r.indices.map fun e => + (e.liftN 1 (r.fieldIndex+r.binders.length)).liftN (m-r.fieldIndex+p) r.binders.length + let Γ := Δ ++ Fs.reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse) + have hjm : j < m := by + simpa [j, m, RecArg.instL, ctorFieldsR_length] using recArgsR_lt _ hrmem + have hFsLen : Fs.length = m := by simp [Fs, m, VExpr.liftTelN_length] + have hstackLen : (Δ ++ (Fs.drop j).reverse).length = m-j+p := by + simp only [List.length_append, List.length_reverse, List.length_drop, hFsLen, hΔ] + omega + have ht := S.recArg_transport hc hB hr₀ [motiveType U T np ty] rfl + (Δ ++ (Fs.drop j).reverse) hstackLen + simp only [List.length_singleton, RecArg.instL] at ht + have hctx : + (Δ ++ (Fs.drop j).reverse) ++ + ((VExpr.liftTelN 1 ((ctorFieldsR U np c).take j) 0).reverse ++ + ([motiveType U T np ty] ++ (paramsTel U np ty).reverse)) = Γ := by + dsimp only [Γ, Fs] + rw [← VExpr.liftTelN_take, List.append_assoc, + ← List.append_assoc ((VExpr.liftTelN 1 (ctorFieldsR U np c) 0).drop j).reverse, + ← List.reverse_append, List.take_append_drop, List.singleton_append, + ← List.append_assoc] + dsimp only [j] at ht hctx + have htel : OnTel env (U+1) Γ As := by + rw [hctx] at ht + simpa [r, As, m, j, RecArg.instL, + RecArg.minorBinders] using ht.1 + have hsp : env.SpineWF (U+1) (As.reverse ++ Γ) + (VExpr.forallN + (VExpr.liftTelN (m+p+r.binders.length+1) (idxTel U np ty) 0) + (.sort (l.inst (VLevel.params' U 1)))) + idxs (.sort (l.inst (VLevel.params' U 1))) := by + rw [hctx] at ht + simpa [r, As, idxs, m, j, RecArg.instL, + RecArg.minorBinders, List.append_assoc, + show j + r₀.binders.length + 1 + (m-j+p) = + m+p+r₀.binders.length+1 from by omega] using ht.2 + have hF : Γ[m-1-j+p]? = + some ((B.instL (VLevel.params' U 1)).liftN 1 j) := by + dsimp only [Γ, Fs] + rw [getElem?_stack_mid Δ + (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse + (motiveType U T np ty :: (paramsTel U np ty).reverse) + (i := m-1-j+p) (by rw [hΔ]; omega) + (by simp only [hΔ, List.length_reverse, VExpr.liftTelN_length]; omega), + show m - 1 - j + p - Δ.length = m - 1 - j from by rw [hΔ]; omega, + List.getElem?_reverse (by rw [hFsLen]; omega), + VExpr.liftTelN_length, + show m - 1 - (m - 1 - j) = j from by omega, + VExpr.liftTelN_getElem?, ctorFieldsR_getElem?, hB] + simp + have hlu := Lookup.of_getElem? hF + rw [show m-1-j+p+1 = m-j+p from by omega] at hlu + dsimp only [j, r] at hlu + have hfield := recArg_minor_fieldType hr₀ m p (by simpa [j] using hjm) + simp only [RecArg.instL, ElimMode.large_sourceLevels] at hfield + rw [hfield] at hlu + have hf0 := VEnv.HasType.bvar (env := env) (U := U+1) hlu + have hf := hf0.weakN S.ord (Ctx.LiftN.zero (Γ := Γ) As.reverse) + have hmajor := VEnv.HasType.appN_selfSpine (env := env) (U := U+1) + (As := As) (B := VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (m+p+r.binders.length+1) np ++ idxs)) + (Δ := []) (Γ := Γ) (by + simpa [As, r, idxs, j, RecArg.instL, List.length_reverse, + List.map_map, Function.comp_def] using hf) + simp only [List.length_nil, VExpr.liftN_zero, List.nil_append] at hmajor + have hAsLen : As.length = r.binders.length := by + simp [As, RecArg.minorBinders, VExpr.liftTelN_length] + change env.HasType (U+1) (As.reverse ++ Γ) + ((VExpr.bvar (m-1-r.fieldIndex+p+As.length)).appN + (VExpr.bvarRevRange 0 As.length)) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (m+p+r.binders.length+1) np ++ idxs)) at hmajor + rw [hAsLen] at hmajor + have hMget : (As.reverse ++ Γ)[m+p+r.binders.length]? = + some (motiveType U T np ty) := by + have hM0 := getElem?_rstack3 As.reverse (Δ ++ Fs.reverse) + (motiveType U T np ty) (paramsTel U np ty).reverse + (i := m+p+r.binders.length) + (by simp [As, RecArg.minorBinders, r, m, Fs, hΔ, VExpr.liftTelN_length, + RecArg.instL]; omega) + simpa [Γ, List.append_assoc] using hM0 + have hM := VEnv.HasType.bvar (env := env) (U := U+1) + (Lookup.of_getElem? hMget) + have hlen : idxs.length = (idxTel U np ty).length := by + simpa [idxs, r, RecArg.instL] using (recArg?_eq hr₀).2.2.2.1 + have hbody := motiveVarApp_hasType (env := env) (U := U) (T := T) + (np := np) (l := l) (ty := ty) hM hsp hlen hmajor + refine IsType.forallN htel ⟨VLevel.param 0, ?_⟩ + simpa [RecArg.minorIH, r, As, idxs, m, Γ, Fs, List.append_assoc] using hbody + +/-- Well-formedness of the generalized induction-hypothesis telescope. Each +entry is supplied by `recArgMinor_isType`; recursive Pi arguments therefore +contribute one functional IH, not one IH per private binder. -/ +theorem Stage3Env.ihsRec_onTel {c : VConstVal} (hc : c ∈ ty.ctors) : + ∀ (rsSuf : List RecArg), + (∀ r ∈ rsSuf, r ∈ recArgsR U T np (idxTel U np ty).length c) → + ∀ (Δ : List VExpr) (p : Nat), Δ.length = p → + OnTel env (U+1) + (Δ ++ (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)) + (ihsFromRecArgs (ctorFieldsR U np c).length rsSuf p) + | [], _, _, _, _ => trivial + | r :: rsSuf, hqs, Δ, p, hΔ => + ⟨S.recArgMinor_isType hc (hqs r (.head _)) Δ p hΔ, + Stage3Env.ihsRec_onTel hc rsSuf (fun q hq => hqs q (.tail _ hq)) + (_ :: Δ) (p+1) (by simp [hΔ])⟩ + +/-- Transport the constructor result's index-spine typing into a rule or +minor context, like `spine_transport` but at the bottom of the full field +telescope. -/ +theorem Stage3Env.result_transport {c : VConstVal} (hc : c ∈ ty.ctors) + (mid : List VExpr) {g : Nat} (hg : mid.length = g) + (As₂ : List VExpr) {d : Nat} (hd : As₂.length = d) : + env.SpineWF (U+1) + (As₂ ++ ((VExpr.liftTelN g (ctorFieldsR U np c) 0).reverse ++ + (mid ++ (paramsTel U np ty).reverse))) + (VExpr.forallN + (VExpr.liftTelN ((ctorFieldsR U np c).length + g + d) (idxTel U np ty) 0) + (.sort (l.inst (VLevel.params' U 1)))) + ((ctorIdxs U np c).map fun e => (e.liftN g (ctorFieldsR U np c).length).liftN d) + (.sort (l.inst (VLevel.params' U 1))) + := by + have hml : (ctorFieldsR U np c).length = + (ctorFields (VExpr.dropN np c.type)).length := ctorFieldsR_length + -- (1) the carried result spine, universes instantiated + have h1 := (S.hresult c hc).instL (U' := U+1) VLevel.params'_one_wf + rw [VExpr.instL_forallN, VExpr.liftTelN_instL, + show ((ctorFields (VExpr.dropN np c.type)).reverse ++ + (VExpr.telN np ty.type).reverse).map (VExpr.instL (VLevel.params' U 1)) = + (ctorFieldsR U np c).reverse ++ (paramsTel U np ty).reverse from by + simp [ctorFieldsR, paramsTel, List.map_reverse], + liftTelN_congr _ _ hml.symm] at h1 + -- (2) insert `mid` at the bottom of the fields + have W₁ := Ctx.LiftN.consTel (n := mid.length) (ctorFieldsR U np c) + (Ctx.LiftN.zero (Γ := (paramsTel U np ty).reverse) mid) + rw [Nat.add_zero] at W₁ + have h2 := h1.weakN S.ord W₁ + rw [VExpr.liftN_forallN, hg] at h2 + -- (3) push the stack underneath + have h3 := h2.weakN S.ord (Ctx.LiftN.zero (Γ := _) As₂ (h := hd)) + rw [VExpr.liftN_forallN] at h3 + rw [VExpr.liftTelN_liftTelN_hi' (ctorFieldsR U np c).length g _ 0 (by omega), + VExpr.liftTelN_liftTelN, + show (ctorFields (VExpr.dropN np ty.type)).map (VExpr.instL (VLevel.params' U 1)) = + idxTel U np ty from rfl] at h3 + simpa [ctorIdxs, VExpr.instL, VExpr.liftN, List.map_map, Function.comp_def, + List.append_assoc] using h3 + +/-- Well-formedness of the induction-hypothesis telescope of a minor +premise, at any suffix of the recursive positions and any depth. -/ +theorem Stage3Env.ihs_onTel {c : VConstVal} (hc : c ∈ ty.ctors) : + ∀ (rsSuf : List (Nat × List VExpr)), + (∀ q ∈ rsSuf, q ∈ recPairsR U T np (idxTel U np ty).length c) → + ∀ (Δ : List VExpr) (p : Nat), Δ.length = p → + OnTel env (U+1) + (Δ ++ (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)) + (ihsFrom (ctorFieldsR U np c).length rsSuf p) + | [], _, _, _, _ => trivial + | (j, idxs) :: rsSuf, hqs, Δ, p, hΔ => by + have hq := hqs _ (List.Mem.head _) + obtain ⟨B, hBj0, hrec0, hidx0⟩ := recPairsR_mem hq + have hBj : (ctorFields (VExpr.dropN np c.type))[j]? = some B := hBj0 + have hrec : isRecField U T np (ctorFields (VExpr.dropN np ty.type)).length j B = + true := by + have h : isRecField U T np (idxTel U np ty).length j B = true := hrec0 + rwa [idxTel_length] at h + have hidx' : idxs = (recFieldIdxs np B).map (VExpr.instL (VLevel.params' U 1)) := hidx0 + have hjlt : j < (ctorFields (VExpr.dropN np c.type)).length := by + simpa using recPairsR_lt _ hq + have hml : (ctorFieldsR U np c).length = + (ctorFields (VExpr.dropN np c.type)).length := ctorFieldsR_length + have hml2 : (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).length = + (ctorFieldsR U np c).length := VExpr.liftTelN_length .. + have hni : (recFieldIdxs np B).length = (ctorFields (VExpr.dropN np ty.type)).length := + (isRecField_eq hrec).2.1 + refine ⟨?_, Stage3Env.ihs_onTel hc rsSuf (fun q hq' => hqs q (.tail _ hq')) + (_ :: Δ) (p+1) (by simp [hΔ])⟩ + -- the motive variable + have hM := getElem?_stack3 Δ + ((VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse) + (paramsTel U np ty).reverse (motiveType U T np ty) + (i := (ctorFieldsR U np c).length + p) + (by simp only [hΔ, List.length_reverse, VExpr.liftTelN_length]; omega) + have hmlu := Lookup.of_getElem? hM + rw [motiveType_liftN] at hmlu + -- the transported index spine + have hSp := S.spine_transport hc hBj hrec [motiveType U T np ty] (g := 1) rfl + (Δ ++ ((VExpr.liftTelN 1 (ctorFieldsR U np c) 0).drop j).reverse) + (d := (ctorFieldsR U np c).length - j + p) + (by simp only [List.length_append, List.length_reverse, List.length_drop, + hml2, hΔ] + omega) + rw [show (Δ ++ ((VExpr.liftTelN 1 (ctorFieldsR U np c) 0).drop j).reverse) ++ + ((VExpr.liftTelN 1 ((ctorFieldsR U np c).take j) 0).reverse ++ + ([motiveType U T np ty] ++ (paramsTel U np ty).reverse)) = + Δ ++ (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse) from by + rw [← VExpr.liftTelN_take, List.append_assoc, + ← List.append_assoc (((VExpr.liftTelN 1 (ctorFieldsR U np c) 0).drop j).reverse), + ← List.reverse_append, List.take_append_drop, List.singleton_append, + ← List.append_assoc], + liftTelN_congr _ _ (show j + 1 + ((ctorFieldsR U np c).length - j + p) = + (ctorFieldsR U np c).length + p + 1 from by omega), + ← hidx'] at hSp + -- retarget onto the motive's pi and compute the instantiation + have hlen : (idxs.map fun e => (e.liftN 1 j).liftN + ((ctorFieldsR U np c).length - j + p)).length = + (VExpr.liftTelN ((ctorFieldsR U np c).length + p + 1) (idxTel U np ty) 0).length := by + simp only [List.length_map, VExpr.liftTelN_length, hidx', idxTel_length] + exact hni + have hRe := hSp.retarget hlen (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + p + 1 + (idxTel U np ty).length) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (.param 0))) + rw [show VExpr.instRev (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + p + 1 + + (idxTel U np ty).length) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (.param 0))) + (idxs.map fun e => (e.liftN 1 j).liftN ((ctorFieldsR U np c).length - j + p)) = + .forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + p + 1) np ++ + idxs.map fun e => (e.liftN 1 j).liftN ((ctorFieldsR U np c).length - j + p))) + (.sort (.param 0)) from by + rw [VExpr.instRev_forallE_sort, VExpr.instRev_appN, + VExpr.instRev_closedN _ (C := .const T (VLevel.params' U 1)) trivial, + List.map_append, + VExpr.map_instRev_bvarRevRange_ge _ _ _ (by + simp only [List.length_map] + rw [hidx', List.length_map, hni, ← idxTel_length (U := U) (ty := ty)] + omega), + show (ctorFieldsR U np c).length + p + 1 + (idxTel U np ty).length - + (idxs.map fun e => (e.liftN 1 j).liftN + ((ctorFieldsR U np c).length - j + p)).length = + (ctorFieldsR U np c).length + p + 1 from by + simp only [List.length_map] + rw [hidx', List.length_map, hni, ← idxTel_length (U := U) (ty := ty)] + omega, + VExpr.bvarRevRange_congr' 0 (show (idxTel U np ty).length = + (idxs.map fun e => (e.liftN 1 j).liftN + ((ctorFieldsR U np c).length - j + p)).length from by + simp only [List.length_map] + rw [hidx', List.length_map, hni, ← idxTel_length (U := U) (ty := ty)]), + VExpr.map_instRev_bvarRevRange]] at hRe + -- the motive applied to the index arguments + have hApp := hRe.hasType_appN (f := .bvar ((ctorFieldsR U np c).length + p)) (.bvar hmlu) + -- the recursive-field variable + have hF := getElem?_stack_mid Δ + ((VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse) + (motiveType U T np ty :: (paramsTel U np ty).reverse) + (i := (ctorFieldsR U np c).length - 1 - j + p) + (by rw [hΔ]; omega) + (by simp only [hΔ, List.length_reverse, VExpr.liftTelN_length]; omega) + rw [show (ctorFieldsR U np c).length - 1 - j + p - Δ.length = + (ctorFieldsR U np c).length - 1 - j from by rw [hΔ]; omega, + List.getElem?_reverse (by simp only [VExpr.liftTelN_length]; omega), + VExpr.liftTelN_length, + show (ctorFieldsR U np c).length - 1 - + ((ctorFieldsR U np c).length - 1 - j) = j from by omega, + VExpr.liftTelN_getElem?, ctorFieldsR_getElem?, hBj] at hF + simp only [Option.map_some] at hF + have hflu := Lookup.of_getElem? hF + rw [show (((B.instL (VLevel.params' U 1)).liftN 1 (0+j)).liftN + ((ctorFieldsR U np c).length - 1 - j + p + 1)) = + VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + p + 1) np ++ + idxs.map fun e => (e.liftN 1 j).liftN + ((ctorFieldsR U np c).length - j + p)) from by + conv => lhs; rw [(isRecField_eq hrec).1] + rw [VExpr.instL_appN, List.map_append, bvarRevRange_instL, + show (VExpr.const T (VLevel.params U)).instL (VLevel.params' U 1) = + .const T (VLevel.params' U 1) from by + simp [VExpr.instL, VLevel.params_map_inst_params'], + VExpr.liftN_appN, VExpr.liftN_appN, List.map_append, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (by omega), + bvarRevRange_liftN_ge _ _ _ _ (Nat.zero_le _), + VExpr.bvarRevRange_congr np (show (ctorFieldsR U np c).length - 1 - j + p + 1 + + (1 + j) = (ctorFieldsR U np c).length + p + 1 from by omega), + ← hidx', List.map_map] + refine congrArg (VExpr.appN _) (congrArg (VExpr.bvarRevRange _ np ++ ·) ?_) + refine List.map_congr_left fun e _ => ?_ + show (e.liftN 1 (0+j)).liftN ((ctorFieldsR U np c).length - 1 - j + p + 1) = _ + rw [show (0+j) = j from Nat.zero_add j, + show (ctorFieldsR U np c).length - 1 - j + p + 1 = + (ctorFieldsR U np c).length - j + p from by omega]] at hflu + have happ2 : env.HasType (U+1) + (Δ ++ (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)) + (VExpr.appN (.bvar ((ctorFieldsR U np c).length + p)) + ((idxs.map fun e => (e.liftN 1 j).liftN + ((ctorFieldsR U np c).length - j + p)) ++ + [.bvar ((ctorFieldsR U np c).length - 1 - j + p)])) + ((VExpr.sort (.param 0)).inst + (.bvar ((ctorFieldsR U np c).length - 1 - j + p))) := by + rw [VExpr.appN_append] + exact HasType.app hApp (.bvar hflu) + exact ⟨_, happ2⟩ +theorem Stage3Env.ctorAppMin_hasType {c : VConstVal} (hc : c ∈ ty.ctors) + (Δ : List VExpr) : + env.HasType (U+1) + (Δ ++ (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)) + (VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange (Δ.length + (ctorFieldsR U np c).length + 1) np ++ + VExpr.bvarRevRange Δ.length (ctorFieldsR U np c).length)) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (Δ.length + (ctorFieldsR U np c).length + 1) np ++ + (ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN Δ.length)) := by + have hml : (ctorFieldsR U np c).length = + (ctorFields (VExpr.dropN np c.type)).length := ctorFieldsR_length + have hml2 : (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).length = + (ctorFieldsR U np c).length := VExpr.liftTelN_length .. + have hcl : (c.type.instL (VLevel.params' U 1)).ClosedN 0 := + (Ordered.closedC (ci := ⟨U, c.type⟩) S.ord (S.hcs c hc)).instL + -- step A: consume the parameter telescope + have hfA : env.HasType (U+1) + ((Δ ++ (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse ++ + [motiveType U T np ty]) ++ (paramsTel U np ty).reverse ++ []) + (.const c.name (VLevel.params' U 1)) + ((VExpr.forallN (paramsTel U np ty) + (VExpr.forallN (ctorFieldsR U np c) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (0 + (ctorFields (VExpr.dropN np c.type)).length) np ++ + ctorIdxs U np c)))).liftN + ((Δ ++ (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse ++ + [motiveType U T np ty]).length + (paramsTel U np ty).length)) := by + rw [← S.ctorType_instL hc, hcl.liftN_eq (Nat.zero_le _)] + exact S.cConst hc + have hA := HasType.appN_selfSpine (env := env) (U := U+1) hfA + -- step B: consume the field telescope + rw [VExpr.liftN_forallN] at hA + have hBeq : VExpr.forallN + (VExpr.liftTelN ((Δ ++ (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse ++ + [motiveType U T np ty]).length) (ctorFieldsR U np c) 0) + ((VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (0 + (ctorFields (VExpr.dropN np c.type)).length) np ++ + ctorIdxs U np c)).liftN + ((Δ ++ (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse ++ + [motiveType U T np ty]).length) (0 + (ctorFieldsR U np c).length)) = + (VExpr.forallN (VExpr.liftTelN 1 (ctorFieldsR U np c) 0) + ((VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (0 + (ctorFields (VExpr.dropN np c.type)).length) np ++ + ctorIdxs U np c)).liftN 1 (0 + (ctorFieldsR U np c).length))).liftN + (Δ.length + (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).length) := by + conv => rhs; rw [VExpr.liftN_forallN, VExpr.liftTelN_liftTelN, + show (0:Nat) + (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).length = + 0 + (ctorFieldsR U np c).length from by rw [hml2], + VExpr.liftN'_liftN_hi] + rw [liftTelN_congr _ _ (show (1:Nat) + (Δ.length + + (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).length) = + (Δ ++ (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse ++ + [motiveType U T np ty]).length from by + simp only [List.length_append, List.length_reverse, VExpr.liftTelN_length, + List.length_singleton] + omega), + show (1 + (Δ.length + (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).length)) = + (Δ ++ (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse ++ + [motiveType U T np ty]).length from by + simp only [List.length_append, List.length_reverse, VExpr.liftTelN_length, + List.length_singleton] + omega] + rw [hBeq] at hA + have hB := HasType.appN_selfSpine (env := env) (U := U+1) + (Δ := Δ) (Γ := motiveType U T np ty :: (paramsTel U np ty).reverse) + (As := VExpr.liftTelN 1 (ctorFieldsR U np c) 0) + (B := (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (0 + (ctorFields (VExpr.dropN np c.type)).length) np ++ + ctorIdxs U np c)).liftN 1 (0 + (ctorFieldsR U np c).length)) + (by simpa [List.append_assoc, List.append_nil] using hA) + rw [show (paramsTel U np ty).length = np from by + simp [paramsTel, List.length_map, S.hlen], + VExpr.bvarRevRange_congr np (show Δ.length + + ((VExpr.liftTelN 1 (ctorFieldsR U np c) 0).length + 1) = + Δ.length + (ctorFieldsR U np c).length + 1 from by omega), + VExpr.liftTelN_length] at hB + rw [show ((VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (0 + (ctorFields (VExpr.dropN np c.type)).length) np ++ + ctorIdxs U np c)).liftN 1 (0 + (ctorFieldsR U np c).length)).liftN Δ.length = + VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (Δ.length + (ctorFieldsR U np c).length + 1) np ++ + (ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN Δ.length) from by + rw [VExpr.liftN_appN, VExpr.liftN_appN, List.map_append, List.map_append, + Nat.zero_add ((ctorFieldsR U np c).length), + bvarRevRange_liftN_ge _ _ _ _ (by omega), + bvarRevRange_liftN_ge _ _ _ _ (Nat.zero_le _), + VExpr.bvarRevRange_congr np (show Δ.length + + (1 + (0 + (ctorFields (VExpr.dropN np c.type)).length)) = + Δ.length + (ctorFieldsR U np c).length + 1 from by omega), + List.map_map] + rfl] at hB + rw [show VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange (Δ.length + (ctorFieldsR U np c).length + 1) np ++ + VExpr.bvarRevRange Δ.length (ctorFieldsR U np c).length) = + (VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange (Δ.length + (ctorFieldsR U np c).length + 1) np)).appN + (VExpr.bvarRevRange Δ.length (ctorFieldsR U np c).length) from + VExpr.appN_append ..] + exact hB + +/-- The minor premise for a constructor is a type over +`params ++ [motive]`. -/ +theorem Stage3Env.minor_isType {c : VConstVal} (hc : c ∈ ty.ctors) : + env.IsType (U+1) (motiveType U T np ty :: (paramsTel U np ty).reverse) + (minorType U T np ty c) := by + simp only [minorType] + refine IsType.forallN ?_ ?_ + · have h0 := S.fieldsWF_onTel _ [] 0 rfl (by simpa using S.hfields c hc) + have h1 := h0.weakN S.ord (.zero [motiveType U T np ty]) + simpa [ctorFieldsR, List.map_reverse, paramsTel] using h1 + · refine IsType.forallN (S.ihs_onTel hc _ (fun q hq => hq) [] 0 rfl) ?_ + have hml2 : (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).length = + (ctorFieldsR U np c).length := VExpr.liftTelN_length .. + have hrlen : ((ihsFrom (ctorFieldsR U np c).length + (recPairsR U T np (idxTel U np ty).length c) 0).reverse).length = + (recPairsR U T np (idxTel U np ty).length c).length := by + simp [ihsFrom_length] + -- the motive variable + have hM := getElem?_rstack3 ((ihsFrom (ctorFieldsR U np c).length + (recPairsR U T np (idxTel U np ty).length c) 0).reverse) + ((VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse) + (motiveType U T np ty) (paramsTel U np ty).reverse + (i := (ctorFieldsR U np c).length + + (recPairsR U T np (idxTel U np ty).length c).length) + (by simp only [List.length_reverse, ihsFrom_length, VExpr.liftTelN_length]; omega) + have hmlu := Lookup.of_getElem? hM + rw [motiveType_liftN] at hmlu + -- the transported result spine + have hSp := S.result_transport hc [motiveType U T np ty] (g := 1) rfl + ((ihsFrom (ctorFieldsR U np c).length + (recPairsR U T np (idxTel U np ty).length c) 0).reverse) + (d := (recPairsR U T np (idxTel U np ty).length c).length) hrlen + rw [liftTelN_congr _ _ (show (ctorFieldsR U np c).length + 1 + + (recPairsR U T np (idxTel U np ty).length c).length = + (ctorFieldsR U np c).length + + (recPairsR U T np (idxTel U np ty).length c).length + 1 from by omega)] at hSp + have hlen : ((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN + (recPairsR U T np (idxTel U np ty).length c).length).length = + (VExpr.liftTelN ((ctorFieldsR U np c).length + + (recPairsR U T np (idxTel U np ty).length c).length + 1) + (idxTel U np ty) 0).length := by + simp only [List.length_map, VExpr.liftTelN_length, ctorIdxs_length, idxTel_length] + have hc3 := stage3Ctor_eq (S.hs3 c hc) + exact hc3.2.1 + have hRe := hSp.retarget hlen (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + + (recPairsR U T np (idxTel U np ty).length c).length + 1 + + (idxTel U np ty).length) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (.param 0))) + rw [show VExpr.instRev (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + + (recPairsR U T np (idxTel U np ty).length c).length + 1 + + (idxTel U np ty).length) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (.param 0))) + ((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN + (recPairsR U T np (idxTel U np ty).length c).length) = + .forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + + (recPairsR U T np (idxTel U np ty).length c).length + 1) np ++ + (ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN + (recPairsR U T np (idxTel U np ty).length c).length)) + (.sort (.param 0)) from by + rw [VExpr.instRev_forallE_sort, VExpr.instRev_appN, + VExpr.instRev_closedN _ (C := .const T (VLevel.params' U 1)) trivial, + List.map_append, + VExpr.map_instRev_bvarRevRange_ge _ _ _ (by + rw [hlen, VExpr.liftTelN_length]; omega), + show (ctorFieldsR U np c).length + + (recPairsR U T np (idxTel U np ty).length c).length + 1 + + (idxTel U np ty).length - + ((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN + (recPairsR U T np (idxTel U np ty).length c).length).length = + (ctorFieldsR U np c).length + + (recPairsR U T np (idxTel U np ty).length c).length + 1 from by + rw [hlen, VExpr.liftTelN_length] + omega, + VExpr.bvarRevRange_congr' 0 (show (idxTel U np ty).length = + ((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN + (recPairsR U T np (idxTel U np ty).length c).length).length from by + rw [hlen, VExpr.liftTelN_length]), + VExpr.map_instRev_bvarRevRange]] at hRe + -- the motive applied to the result indices + have hApp := hRe.hasType_appN (f := .bvar ((ctorFieldsR U np c).length + + (recPairsR U T np (idxTel U np ty).length c).length)) (.bvar hmlu) + -- the constructor application + have hctor := S.ctorAppMin_hasType hc + ((ihsFrom (ctorFieldsR U np c).length + (recPairsR U T np (idxTel U np ty).length c) 0).reverse) + rw [hrlen] at hctor + rw [VExpr.bvarRevRange_congr np (show + (recPairsR U T np (idxTel U np ty).length c).length + + (ctorFieldsR U np c).length + 1 = + (ctorFieldsR U np c).length + + (recPairsR U T np (idxTel U np ty).length c).length + 1 from by omega)] at hctor + have happ2 : env.HasType (U+1) + ((ihsFrom (ctorFieldsR U np c).length + (recPairsR U T np (idxTel U np ty).length c) 0).reverse ++ + ((VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN (.bvar ((ctorFieldsR U np c).length + + (recPairsR U T np (idxTel U np ty).length c).length)) + (((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN + (recPairsR U T np (idxTel U np ty).length c).length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((recPairsR U T np (idxTel U np ty).length c).length + + (ctorFieldsR U np c).length + 1) np ++ + VExpr.bvarRevRange (recPairsR U T np (idxTel U np ty).length c).length + (ctorFieldsR U np c).length)])) + ((VExpr.sort (.param 0)).inst + (VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((recPairsR U T np (idxTel U np ty).length c).length + + (ctorFieldsR U np c).length + 1) np ++ + VExpr.bvarRevRange (recPairsR U T np (idxTel U np ty).length c).length + (ctorFieldsR U np c).length))) := by + rw [VExpr.appN_append, + VExpr.bvarRevRange_congr np (show + (recPairsR U T np (idxTel U np ty).length c).length + + (ctorFieldsR U np c).length + 1 = + (ctorFieldsR U np c).length + + (recPairsR U T np (idxTel U np ty).length c).length + 1 from by omega)] + exact HasType.app hApp (by simpa [List.append_assoc] using hctor) + exact ⟨_, by simpa [List.append_assoc] using happ2⟩ + +/-- The generalized minor premise is a type over `params ++ [motive]`. +This is the preservation theorem used by `minorTypeRec`; it differs from +`minor_isType` only in the functional-IH telescope supplied for recursive +Pi arguments. -/ +theorem Stage3Env.minor_isTypeRec {c : VConstVal} (hc : c ∈ ty.ctors) : + env.IsType (U+1) (motiveType U T np ty :: (paramsTel U np ty).reverse) + (minorTypeRec U T np ty c) := by + simp only [minorTypeRec] + refine IsType.forallN ?_ ?_ + · have h0 := S.fieldsWF_onTel _ [] 0 rfl (by simpa using S.hfields c hc) + have h1 := h0.weakN S.ord (.zero [motiveType U T np ty]) + simpa [ctorFieldsR, List.map_reverse, paramsTel] using h1 + · refine IsType.forallN (S.ihsRec_onTel hc _ (fun q hq => hq) [] 0 rfl) ?_ + have hml2 : (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).length = + (ctorFieldsR U np c).length := VExpr.liftTelN_length .. + have hrlen : ((ihsFromRecArgs (ctorFieldsR U np c).length + (recArgsR U T np (idxTel U np ty).length c) 0).reverse).length = + (recArgsR U T np (idxTel U np ty).length c).length := by + simp [ihsFromRecArgs_length] + -- the motive variable + have hM := getElem?_rstack3 ((ihsFromRecArgs (ctorFieldsR U np c).length + (recArgsR U T np (idxTel U np ty).length c) 0).reverse) + ((VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse) + (motiveType U T np ty) (paramsTel U np ty).reverse + (i := (ctorFieldsR U np c).length + + (recArgsR U T np (idxTel U np ty).length c).length) + (by simp only [List.length_reverse, ihsFromRecArgs_length, + VExpr.liftTelN_length]; omega) + have hmlu := Lookup.of_getElem? hM + rw [motiveType_liftN] at hmlu + -- the transported result spine + have hSp := S.result_transport hc [motiveType U T np ty] (g := 1) rfl + ((ihsFromRecArgs (ctorFieldsR U np c).length + (recArgsR U T np (idxTel U np ty).length c) 0).reverse) + (d := (recArgsR U T np (idxTel U np ty).length c).length) hrlen + rw [liftTelN_congr _ _ (show (ctorFieldsR U np c).length + 1 + + (recArgsR U T np (idxTel U np ty).length c).length = + (ctorFieldsR U np c).length + + (recArgsR U T np (idxTel U np ty).length c).length + 1 from by omega)] at hSp + have hlen : ((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN + (recArgsR U T np (idxTel U np ty).length c).length).length = + (VExpr.liftTelN ((ctorFieldsR U np c).length + + (recArgsR U T np (idxTel U np ty).length c).length + 1) + (idxTel U np ty) 0).length := by + simp only [List.length_map, VExpr.liftTelN_length, ctorIdxs_length, idxTel_length] + have hc3 := stage3Ctor_eq (S.hs3 c hc) + exact hc3.2.1 + have hRe := hSp.retarget hlen (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + + (recArgsR U T np (idxTel U np ty).length c).length + 1 + + (idxTel U np ty).length) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (.param 0))) + rw [show VExpr.instRev (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + + (recArgsR U T np (idxTel U np ty).length c).length + 1 + + (idxTel U np ty).length) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (.param 0))) + ((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN + (recArgsR U T np (idxTel U np ty).length c).length) = + .forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + + (recArgsR U T np (idxTel U np ty).length c).length + 1) np ++ + (ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN + (recArgsR U T np (idxTel U np ty).length c).length)) + (.sort (.param 0)) from by + rw [VExpr.instRev_forallE_sort, VExpr.instRev_appN, + VExpr.instRev_closedN _ (C := .const T (VLevel.params' U 1)) trivial, + List.map_append, + VExpr.map_instRev_bvarRevRange_ge _ _ _ (by + rw [hlen, VExpr.liftTelN_length]; omega), + show (ctorFieldsR U np c).length + + (recArgsR U T np (idxTel U np ty).length c).length + 1 + + (idxTel U np ty).length - + ((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN + (recArgsR U T np (idxTel U np ty).length c).length).length = + (ctorFieldsR U np c).length + + (recArgsR U T np (idxTel U np ty).length c).length + 1 from by + rw [hlen, VExpr.liftTelN_length] + omega, + VExpr.bvarRevRange_congr' 0 (show (idxTel U np ty).length = + ((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN + (recArgsR U T np (idxTel U np ty).length c).length).length from by + rw [hlen, VExpr.liftTelN_length]), + VExpr.map_instRev_bvarRevRange]] at hRe + -- the motive applied to the result indices + have hApp := hRe.hasType_appN (f := .bvar ((ctorFieldsR U np c).length + + (recArgsR U T np (idxTel U np ty).length c).length)) (.bvar hmlu) + -- the constructor application + have hctor := S.ctorAppMin_hasType hc + ((ihsFromRecArgs (ctorFieldsR U np c).length + (recArgsR U T np (idxTel U np ty).length c) 0).reverse) + rw [hrlen] at hctor + rw [VExpr.bvarRevRange_congr np (show + (recArgsR U T np (idxTel U np ty).length c).length + + (ctorFieldsR U np c).length + 1 = + (ctorFieldsR U np c).length + + (recArgsR U T np (idxTel U np ty).length c).length + 1 from by omega)] at hctor + have happ2 : env.HasType (U+1) + ((ihsFromRecArgs (ctorFieldsR U np c).length + (recArgsR U T np (idxTel U np ty).length c) 0).reverse ++ + ((VExpr.liftTelN 1 (ctorFieldsR U np c) 0).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN (.bvar ((ctorFieldsR U np c).length + + (recArgsR U T np (idxTel U np ty).length c).length)) + (((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN + (recArgsR U T np (idxTel U np ty).length c).length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((recArgsR U T np (idxTel U np ty).length c).length + + (ctorFieldsR U np c).length + 1) np ++ + VExpr.bvarRevRange (recArgsR U T np (idxTel U np ty).length c).length + (ctorFieldsR U np c).length)])) + ((VExpr.sort (.param 0)).inst + (VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((recArgsR U T np (idxTel U np ty).length c).length + + (ctorFieldsR U np c).length + 1) np ++ + VExpr.bvarRevRange (recArgsR U T np (idxTel U np ty).length c).length + (ctorFieldsR U np c).length))) := by + rw [VExpr.appN_append, + VExpr.bvarRevRange_congr np (show + (recArgsR U T np (idxTel U np ty).length c).length + + (ctorFieldsR U np c).length + 1 = + (ctorFieldsR U np c).length + + (recArgsR U T np (idxTel U np ty).length c).length + 1 from by omega)] + exact HasType.app hApp (by simpa [List.append_assoc] using hctor) + exact ⟨_, by simpa [List.append_assoc] using happ2⟩ + +/-- The minor premises, in position, are a telescope over +`params ++ [motive]`. -/ +theorem Stage3Env.minorTypes_onTel : + ∀ (cs' : List VConstVal), (∀ c ∈ cs', c ∈ ty.ctors) → + ∀ (Δ : List VExpr) (i : Nat), Δ.length = i → + OnTel env (U+1) (Δ ++ (motiveType U T np ty :: (paramsTel U np ty).reverse)) + (minorTypes U T np ty cs' i) + | [], _, _, _, _ => trivial + | c :: cs', hsub, Δ, i, hΔ => + ⟨by + rw [← hΔ] + exact (S.minor_isType (hsub c (.head _))).weakN S.ord (.zero Δ), + Stage3Env.minorTypes_onTel cs' (fun c h => hsub c (.tail _ h)) (_ :: Δ) (i+1) + (by simp [hΔ])⟩ + +/-- Generalized minor premises, including functional recursive hypotheses, +form a telescope over `params ++ [motive]`. -/ +theorem Stage3Env.minorTypesRec_onTel : + ∀ (cs' : List VConstVal), (∀ c ∈ cs', c ∈ ty.ctors) → + ∀ (Δ : List VExpr) (i : Nat), Δ.length = i → + OnTel env (U+1) (Δ ++ (motiveType U T np ty :: (paramsTel U np ty).reverse)) + (minorTypesRec U T np ty cs' i) + | [], _, _, _, _ => trivial + | c :: cs', hsub, Δ, i, hΔ => + ⟨by + rw [← hΔ] + exact (S.minor_isTypeRec (hsub c (.head _))).weakN S.ord (.zero Δ), + Stage3Env.minorTypesRec_onTel cs' (fun c h => hsub c (.tail _ h)) (_ :: Δ) (i+1) + (by simp [hΔ])⟩ + +/-- The generated recursor type is well-formed. -/ +theorem Stage3Env.recType_isType : env.IsType (U+1) [] (recType U T np ty) := by + have hP : OnTel env (U+1) [] (paramsTel U np ty) := by + have := S.hparams.of_append.1.instL (U' := U+1) VLevel.params'_one_wf + simpa [paramsTel] using this + refine IsType.forallN hP ?_ + simp only [List.append_nil] + refine IsType.forallE S.motive_isType ?_ + refine IsType.forallN + (by simpa using S.minorTypes_onTel ty.ctors (fun _ h => h) [] 0 rfl) ?_ + have hI : OnTel env (U+1) + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)) + (VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0) := by + have h := S.idxTel_onTel.weakN S.ord + (Ctx.LiftN.zero (n := ty.ctors.length + 1) + (Γ := (paramsTel U np ty).reverse) + ((minorTypes U T np ty ty.ctors).reverse ++ [motiveType U T np ty]) + (h := by simp [minorTypes_length])) + simpa [List.append_assoc] using h + refine IsType.forallN hI ?_ + have hmaj : env.HasType (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (l.inst (VLevel.params' U 1))) := by + have W := Ctx.LiftN.consTel (n := ty.ctors.length + 1) (idxTel U np ty) + (Ctx.LiftN.zero (n := ty.ctors.length + 1) + (Γ := (paramsTel U np ty).reverse) + ((minorTypes U T np ty ty.ctors).reverse ++ [motiveType U T np ty]) + (h := by simp [minorTypes_length])) + have h := S.motiveTApp_hasType.weakN S.ord W + simp only [Nat.add_zero, VExpr.liftN_appN, VExpr.liftN, List.map_append] at h + rw [bvarRevRange_liftN_ge _ _ _ _ (Nat.le_refl _), + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega), + VExpr.bvarRevRange_congr np (show + ty.ctors.length + 1 + (idxTel U np ty).length = + (idxTel U np ty).length + ty.ctors.length + 1 from by omega)] at h + simpa [List.append_assoc] using h + refine IsType.forallE ⟨_, hmaj⟩ ?_ + have hM := getElem?_rstack3 + [VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)] + ((VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0).reverse ++ + (minorTypes U T np ty ty.ctors).reverse) + (motiveType U T np ty) (paramsTel U np ty).reverse + (i := (idxTel U np ty).length + ty.ctors.length + 1) + (by simp only [List.length_singleton, List.length_append, List.length_reverse, + VExpr.liftTelN_length, minorTypes_length]; omega) + have hmlu := Lookup.of_getElem? (by + simpa only [List.singleton_append, List.append_assoc] using hM) + rw [show (motiveType U T np ty).liftN + ((idxTel U np ty).length + ty.ctors.length + 1 + 1) = + ((motiveType U T np ty).liftN (ty.ctors.length + 1)).liftN + ((idxTel U np ty).length + 1) from by + rw [VExpr.liftN_liftN] + congr 1 + omega, + motiveType_liftN] at hmlu + have hfun : env.HasType (U+1) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length) :: + ((VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)))) + (.bvar ((idxTel U np ty).length + ty.ctors.length + 1)) + ((VExpr.forallN + (VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0) + (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (ty.ctors.length + 1 + (idxTel U np ty).length) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (.param 0)))).liftN + (1 + (VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0).length)) := by + exact .bvar (by + simpa [List.append_assoc, VExpr.liftTelN_length, Nat.add_comm] using hmlu) + have hMapp := HasType.appN_selfSpine + (As := VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0) + (Δ := [VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)]) + (Γ := (minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)) + (f := .bvar ((idxTel U np ty).length + ty.ctors.length + 1)) + hfun + have h0 : + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length) :: + ((VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))))[0]? = + some (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) := rfl + have harg := HasType.bvar (env := env) (U := U+1) (Lookup.of_getElem? h0) + have harg' : env.HasType (U+1) + ([VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)] ++ + (VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (.bvar 0) + ((VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (ty.ctors.length + 1 + (idxTel U np ty).length) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)).liftN + [VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)].length) := by + simpa [List.append_assoc, Nat.add_comm, Nat.add_left_comm, Nat.add_assoc] using harg + have happ := HasType.app hMapp harg' + exact ⟨_, by + simpa [List.append_assoc, VExpr.liftTelN_length] using happ⟩ + +/-- The generalized recursor type, whose constructor minors include +functional IHs for recursive Pi arguments, is well-formed. -/ +theorem Stage3Env.recTypeRec_isType : env.IsType (U+1) [] (recTypeRec U T np ty) := by + have hP : OnTel env (U+1) [] (paramsTel U np ty) := by + have := S.hparams.of_append.1.instL (U' := U+1) VLevel.params'_one_wf + simpa [paramsTel] using this + refine IsType.forallN hP ?_ + simp only [List.append_nil] + refine IsType.forallE S.motive_isType ?_ + refine IsType.forallN + (by simpa using S.minorTypesRec_onTel ty.ctors (fun _ h => h) [] 0 rfl) ?_ + have hI : OnTel env (U+1) + ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)) + (VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0) := by + have h := S.idxTel_onTel.weakN S.ord + (Ctx.LiftN.zero (n := ty.ctors.length + 1) + (Γ := (paramsTel U np ty).reverse) + ((minorTypesRec U T np ty ty.ctors).reverse ++ [motiveType U T np ty]) + (h := by simp [minorTypesRec_length])) + simpa [List.append_assoc] using h + refine IsType.forallN hI ?_ + have hmaj : env.HasType (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0).reverse ++ + ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (l.inst (VLevel.params' U 1))) := by + have W := Ctx.LiftN.consTel (n := ty.ctors.length + 1) (idxTel U np ty) + (Ctx.LiftN.zero (n := ty.ctors.length + 1) + (Γ := (paramsTel U np ty).reverse) + ((minorTypesRec U T np ty ty.ctors).reverse ++ [motiveType U T np ty]) + (h := by simp [minorTypesRec_length])) + have h := S.motiveTApp_hasType.weakN S.ord W + simp only [Nat.add_zero, VExpr.liftN_appN, VExpr.liftN, List.map_append] at h + rw [bvarRevRange_liftN_ge _ _ _ _ (Nat.le_refl _), + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega), + VExpr.bvarRevRange_congr np (show + ty.ctors.length + 1 + (idxTel U np ty).length = + (idxTel U np ty).length + ty.ctors.length + 1 from by omega)] at h + simpa [List.append_assoc] using h + refine IsType.forallE ⟨_, hmaj⟩ ?_ + have hM := getElem?_rstack3 + [VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)] + ((VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0).reverse ++ + (minorTypesRec U T np ty ty.ctors).reverse) + (motiveType U T np ty) (paramsTel U np ty).reverse + (i := (idxTel U np ty).length + ty.ctors.length + 1) + (by simp only [List.length_singleton, List.length_append, List.length_reverse, + VExpr.liftTelN_length, minorTypesRec_length]; omega) + have hmlu := Lookup.of_getElem? (by + simpa only [List.singleton_append, List.append_assoc] using hM) + rw [show (motiveType U T np ty).liftN + ((idxTel U np ty).length + ty.ctors.length + 1 + 1) = + ((motiveType U T np ty).liftN (ty.ctors.length + 1)).liftN + ((idxTel U np ty).length + 1) from by + rw [VExpr.liftN_liftN] + congr 1 + omega, + motiveType_liftN] at hmlu + have hfun : env.HasType (U+1) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length) :: + ((VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0).reverse ++ + ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)))) + (.bvar ((idxTel U np ty).length + ty.ctors.length + 1)) + ((VExpr.forallN + (VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0) + (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (ty.ctors.length + 1 + (idxTel U np ty).length) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (.param 0)))).liftN + (1 + (VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0).length)) := by + exact .bvar (by + simpa [List.append_assoc, VExpr.liftTelN_length, Nat.add_comm] using hmlu) + have hMapp := HasType.appN_selfSpine + (As := VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0) + (Δ := [VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)]) + (Γ := (minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)) + (f := .bvar ((idxTel U np ty).length + ty.ctors.length + 1)) + hfun + have h0 : + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length) :: + ((VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0).reverse ++ + ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))))[0]? = + some (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) := rfl + have harg := HasType.bvar (env := env) (U := U+1) (Lookup.of_getElem? h0) + have harg' : env.HasType (U+1) + ([VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)] ++ + (VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0).reverse ++ + ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (.bvar 0) + ((VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (ty.ctors.length + 1 + (idxTel U np ty).length) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)).liftN + [VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)].length) := by + simpa [List.append_assoc, Nat.add_comm, Nat.add_left_comm, Nat.add_assoc] using harg + have happ := HasType.app hMapp harg' + exact ⟨_, by + simpa [List.append_assoc, VExpr.liftTelN_length] using happ⟩ + +theorem Stage3Env.recConstRec_wf : (recConstRec U T np ty).WF env := + S.recTypeRec_isType + +theorem Stage3Env.recConst_wf : (recConst U T np ty).WF env := + S.recType_isType + +/-! ## The iota rules -/ + +/-- The recursor type is closed, by well-formedness. -/ +theorem Stage3Env.recType_closedN : (recType U T np ty).ClosedN 0 := by + obtain ⟨u, h⟩ := S.recType_isType + exact VExpr.WF.closedN S.ord ⟨_, h⟩ trivial + +theorem Stage3Env.recTypeRec_closedN : (recTypeRec U T np ty).ClosedN 0 := by + obtain ⟨u, h⟩ := S.recTypeRec_isType + exact VExpr.WF.closedN S.ord ⟨_, h⟩ trivial + +/-- The recursor constant at its own (identity) universe list. -/ +theorem Stage3Env.recConst_hasType + (hrec : env.constants (.str T "rec") = some (recConst U T np ty)) {Γ} : + env.HasType (U+1) Γ (.const (.str T "rec") (VLevel.params (U+1))) + (recType U T np ty) := by + have := HasType.const (Γ := Γ) hrec VLevel.params_wf VLevel.params_length + rw [show (recConst U T np ty).uvars = U + 1 from rfl, + show (recConst U T np ty).type = recType U T np ty from rfl] at this + rwa [recType_levelWF.instL_id] at this + +theorem Stage3Env.recConstRec_hasType + (hrec : env.constants (.str T "rec") = some (recConstRec U T np ty)) {Γ} : + env.HasType (U+1) Γ (.const (.str T "rec") (VLevel.params (U+1))) + (recTypeRec U T np ty) := by + have := HasType.const (Γ := Γ) hrec VLevel.params_wf VLevel.params_length + rw [show (recConstRec U T np ty).uvars = U + 1 from rfl, + show (recConstRec U T np ty).type = recTypeRec U T np ty from rfl] at this + rwa [recTypeRec_levelWF.instL_id] at this + +/-- The recursor applied to its parameter, motive, and minor-premise +spine. The remaining type is the (lifted) index telescope followed by +the major premise. -/ +theorem Stage3Env.recBase_hasType + (hrec : env.constants (.str T "rec") = some (recConst U T np ty)) + (Δ : List VExpr) : + env.HasType (U+1) + (Δ ++ ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN (.const (.str T "rec") (VLevel.params (U+1))) + (VExpr.bvarRevRange Δ.length (np + ty.ctors.length + 1))) + ((VExpr.forallN (VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0) + (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.app (VExpr.appN (.bvar ((idxTel U np ty).length + ty.ctors.length + 1)) + (VExpr.bvarRevRange 1 (idxTel U np ty).length)) (.bvar 0)))).liftN Δ.length) := by + have hf : env.HasType (U+1) + (Δ ++ (paramsTel U np ty ++ + motiveType U T np ty :: minorTypes U T np ty ty.ctors).reverse ++ []) + (.const (.str T "rec") (VLevel.params (U+1))) + ((VExpr.forallN (paramsTel U np ty ++ + motiveType U T np ty :: minorTypes U T np ty ty.ctors) + (VExpr.forallN (VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0) + (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.app (VExpr.appN (.bvar ((idxTel U np ty).length + ty.ctors.length + 1)) + (VExpr.bvarRevRange 1 (idxTel U np ty).length)) (.bvar 0))))).liftN + (Δ.length + (paramsTel U np ty ++ + motiveType U T np ty :: minorTypes U T np ty ty.ctors).length)) := by + rw [show VExpr.forallN (paramsTel U np ty ++ + motiveType U T np ty :: minorTypes U T np ty ty.ctors) + (VExpr.forallN (VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0) + (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.app (VExpr.appN (.bvar ((idxTel U np ty).length + ty.ctors.length + 1)) + (VExpr.bvarRevRange 1 (idxTel U np ty).length)) (.bvar 0)))) = + recType U T np ty from by rw [VExpr.forallN_append]; rfl, + S.recType_closedN.liftN_eq (Nat.zero_le _)] + exact S.recConst_hasType hrec + have hspine := HasType.appN_selfSpine (env := env) (U := U+1) hf + simp only [List.reverse_append, List.reverse_cons, List.append_nil, List.append_assoc, + List.singleton_append, List.length_append, List.length_cons, minorTypes_length] at hspine + rw [show (paramsTel U np ty).length = np from by + simp [paramsTel, List.length_map, S.hlen], + VExpr.bvarRevRange_congr' Δ.length (show + np + (ty.ctors.length + 1) = np + ty.ctors.length + 1 from by omega)] at hspine + simpa [List.append_assoc] using hspine + +theorem Stage3Env.recBaseRec_hasType + (hrec : env.constants (.str T "rec") = some (recConstRec U T np ty)) + (Δ : List VExpr) : + env.HasType (U+1) + (Δ ++ ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN (.const (.str T "rec") (VLevel.params (U+1))) + (VExpr.bvarRevRange Δ.length (np + ty.ctors.length + 1))) + ((VExpr.forallN (VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0) + (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.app (VExpr.appN (.bvar ((idxTel U np ty).length + ty.ctors.length + 1)) + (VExpr.bvarRevRange 1 (idxTel U np ty).length)) (.bvar 0)))).liftN Δ.length) := by + have hf : env.HasType (U+1) + (Δ ++ (paramsTel U np ty ++ + motiveType U T np ty :: minorTypesRec U T np ty ty.ctors).reverse ++ []) + (.const (.str T "rec") (VLevel.params (U+1))) + ((VExpr.forallN (paramsTel U np ty ++ + motiveType U T np ty :: minorTypesRec U T np ty ty.ctors) + (VExpr.forallN (VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0) + (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.app (VExpr.appN (.bvar ((idxTel U np ty).length + ty.ctors.length + 1)) + (VExpr.bvarRevRange 1 (idxTel U np ty).length)) (.bvar 0))))).liftN + (Δ.length + (paramsTel U np ty ++ + motiveType U T np ty :: minorTypesRec U T np ty ty.ctors).length)) := by + rw [show VExpr.forallN (paramsTel U np ty ++ + motiveType U T np ty :: minorTypesRec U T np ty ty.ctors) + (VExpr.forallN (VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0) + (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.app (VExpr.appN (.bvar ((idxTel U np ty).length + ty.ctors.length + 1)) + (VExpr.bvarRevRange 1 (idxTel U np ty).length)) (.bvar 0)))) = + recTypeRec U T np ty from by rw [VExpr.forallN_append]; rfl, + S.recTypeRec_closedN.liftN_eq (Nat.zero_le _)] + exact S.recConstRec_hasType hrec + have hspine := HasType.appN_selfSpine (env := env) (U := U+1) hf + simp only [List.reverse_append, List.reverse_cons, List.append_nil, List.append_assoc, + List.singleton_append, List.length_append, List.length_cons, minorTypesRec_length] at hspine + rw [show (paramsTel U np ty).length = np from by + simp [paramsTel, List.length_map, S.hlen], + VExpr.bvarRevRange_congr' Δ.length (show + np + (ty.ctors.length + 1) = np + ty.ctors.length + 1 from by omega)] at hspine + simpa [List.append_assoc] using hspine + +/-- Apply the generated recursor to an index spine and its major premise. +This is shared by constructor-headed rule left sides and by the recursive +calls appearing in minor-premise right sides. -/ +theorem Stage3Env.recApp_hasType + (hrec : env.constants (.str T "rec") = some (recConst U T np ty)) + (Δ : List VExpr) {idxs : List VExpr} {a : VExpr} + (hidx : env.SpineWF (U+1) + (Δ ++ ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.forallN + (VExpr.liftTelN (Δ.length + ty.ctors.length + 1) (idxTel U np ty) 0) + (.sort (l.inst (VLevel.params' U 1)))) + idxs (.sort (l.inst (VLevel.params' U 1)))) + (hlen : idxs.length = (idxTel U np ty).length) + (ha : env.HasType (U+1) + (Δ ++ ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) a + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (Δ.length + ty.ctors.length + 1) np ++ idxs))) : + env.HasType (U+1) + (Δ ++ ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN + (VExpr.appN (.const (.str T "rec") (VLevel.params (U+1))) + (VExpr.bvarRevRange Δ.length (np + ty.ctors.length + 1))) + (idxs ++ [a])) + (VExpr.appN (.bvar (Δ.length + ty.ctors.length)) (idxs ++ [a])) := by + have hb := S.recBase_hasType hrec Δ + rw [VExpr.liftN_forallN, VExpr.liftTelN_liftTelN, + liftTelN_congr _ _ (show ty.ctors.length + 1 + Δ.length = + Δ.length + ty.ctors.length + 1 from by omega)] at hb + have hcod : + (VExpr.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.app (VExpr.appN (.bvar ((idxTel U np ty).length + ty.ctors.length + 1)) + (VExpr.bvarRevRange 1 (idxTel U np ty).length)) (.bvar 0))).liftN Δ.length + (0 + (VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0).length) = + VExpr.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + Δ.length + + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.app (VExpr.appN (.bvar ((idxTel U np ty).length + Δ.length + + ty.ctors.length + 1)) + (VExpr.bvarRevRange 1 (idxTel U np ty).length)) (.bvar 0)) := by + rw [VExpr.liftTelN_length, Nat.zero_add] + show VExpr.forallE _ _ = VExpr.forallE _ _ + congr 1 + · rw [VExpr.liftN_appN, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (by omega), + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega), + VExpr.bvarRevRange_congr np (show Δ.length + + ((idxTel U np ty).length + ty.ctors.length + 1) = + (idxTel U np ty).length + Δ.length + ty.ctors.length + 1 from by omega)] + rfl + · show VExpr.app _ _ = VExpr.app _ _ + congr 1 + · rw [VExpr.liftN_appN, + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega)] + show VExpr.appN (.bvar (liftVar Δ.length + ((idxTel U np ty).length + ty.ctors.length + 1) + ((idxTel U np ty).length + 1))) _ = _ + rw [liftVar_le (by omega), show Δ.length + + ((idxTel U np ty).length + ty.ctors.length + 1) = + (idxTel U np ty).length + Δ.length + ty.ctors.length + 1 from by omega] + rw [hcod] at hb + have hshape := hidx.retarget (by simpa only [VExpr.liftTelN_length] using hlen) + (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + Δ.length + + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (.param 0))) + rw [VExpr.instRev_forallE_sort, VExpr.instRev_appN, + VExpr.instRev_closedN _ (C := .const T (VLevel.params' U 1)) trivial, + List.map_append, + VExpr.map_instRev_bvarRevRange_ge _ _ _ (by rw [hlen]; omega), + show (idxTel U np ty).length + Δ.length + ty.ctors.length + 1 - idxs.length = + Δ.length + ty.ctors.length + 1 from by rw [hlen]; omega, + VExpr.bvarRevRange_congr' 0 hlen.symm, + VExpr.map_instRev_bvarRevRange] at hshape + rw [hlen] at hshape + have hfull := hshape.snoc ha + simp only [VExpr.inst] at hfull + change env.SpineWF (U+1) _ + (VExpr.forallN + (VExpr.liftTelN (Δ.length + ty.ctors.length + 1) (idxTel U np ty) 0) + (VExpr.forallN [VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + Δ.length + + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)] + (.sort (.param 0)))) + (idxs ++ [a]) (.sort (.param 0)) at hfull + rw [← VExpr.forallN_append] at hfull + have hfullLen : (idxs ++ [a]).length = + (VExpr.liftTelN (Δ.length + ty.ctors.length + 1) (idxTel U np ty) 0 ++ + [VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + Δ.length + + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)]).length := by + simp only [List.length_append, List.length_singleton, VExpr.liftTelN_length, hlen] + have hactual := hfull.retarget hfullLen + (VExpr.app + (VExpr.appN (.bvar ((idxTel U np ty).length + Δ.length + + ty.ctors.length + 1)) + (VExpr.bvarRevRange 1 (idxTel U np ty).length)) + (.bvar 0)) + rw [VExpr.forallN_append] at hactual + have happ := hactual.hasType_appN hb + rw [show VExpr.app + (VExpr.appN (.bvar ((idxTel U np ty).length + Δ.length + + ty.ctors.length + 1)) + (VExpr.bvarRevRange 1 (idxTel U np ty).length)) + (.bvar 0) = + VExpr.appN (.bvar ((idxTel U np ty).length + Δ.length + + ty.ctors.length + 1)) (VExpr.bvarRevRange 0 ((idxTel U np ty).length + 1)) from by + rw [VExpr.bvarRevRange_congr' 0 (show (idxTel U np ty).length + 1 = + 1 + (idxTel U np ty).length from by omega), + ← VExpr.bvarRevRange_append (idxTel U np ty).length 1] + simpa [VExpr.bvarRevRange, VExpr.appN] using (VExpr.appN_append + (.bvar ((idxTel U np ty).length + Δ.length + ty.ctors.length + 1)) + (VExpr.bvarRevRange 1 (idxTel U np ty).length) [VExpr.bvar 0]).symm, + VExpr.instRev_appN, + VExpr.instRev_bvar_ge _ (by + simp only [List.length_append, List.length_singleton] + rw [hlen] + omega), + VExpr.bvarRevRange_congr' 0 (show (idxTel U np ty).length + 1 = + (idxs ++ [a]).length from by simp [hlen]), + VExpr.map_instRev_bvarRevRange] at happ + rw [show (idxTel U np ty).length + Δ.length + ty.ctors.length + 1 - + (idxs ++ [a]).length = Δ.length + ty.ctors.length from by + simp only [List.length_append, List.length_singleton] + rw [hlen] + omega] at happ + simpa [List.length_append, hlen] using happ + +theorem Stage3Env.recAppRec_hasType + (hrec : env.constants (.str T "rec") = some (recConstRec U T np ty)) + (Δ : List VExpr) {idxs : List VExpr} {a : VExpr} + (hidx : env.SpineWF (U+1) + (Δ ++ ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.forallN + (VExpr.liftTelN (Δ.length + ty.ctors.length + 1) (idxTel U np ty) 0) + (.sort (l.inst (VLevel.params' U 1)))) + idxs (.sort (l.inst (VLevel.params' U 1)))) + (hlen : idxs.length = (idxTel U np ty).length) + (ha : env.HasType (U+1) + (Δ ++ ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) a + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (Δ.length + ty.ctors.length + 1) np ++ idxs))) : + env.HasType (U+1) + (Δ ++ ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN + (VExpr.appN (.const (.str T "rec") (VLevel.params (U+1))) + (VExpr.bvarRevRange Δ.length (np + ty.ctors.length + 1))) + (idxs ++ [a])) + (VExpr.appN (.bvar (Δ.length + ty.ctors.length)) (idxs ++ [a])) := by + have hb := S.recBaseRec_hasType hrec Δ + rw [VExpr.liftN_forallN, VExpr.liftTelN_liftTelN, + liftTelN_congr _ _ (show ty.ctors.length + 1 + Δ.length = + Δ.length + ty.ctors.length + 1 from by omega)] at hb + have hcod : + (VExpr.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.app (VExpr.appN (.bvar ((idxTel U np ty).length + ty.ctors.length + 1)) + (VExpr.bvarRevRange 1 (idxTel U np ty).length)) (.bvar 0))).liftN Δ.length + (0 + (VExpr.liftTelN (ty.ctors.length + 1) (idxTel U np ty) 0).length) = + VExpr.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + Δ.length + + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.app (VExpr.appN (.bvar ((idxTel U np ty).length + Δ.length + + ty.ctors.length + 1)) + (VExpr.bvarRevRange 1 (idxTel U np ty).length)) (.bvar 0)) := by + rw [VExpr.liftTelN_length, Nat.zero_add] + show VExpr.forallE _ _ = VExpr.forallE _ _ + congr 1 + · rw [VExpr.liftN_appN, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (by omega), + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega), + VExpr.bvarRevRange_congr np (show Δ.length + + ((idxTel U np ty).length + ty.ctors.length + 1) = + (idxTel U np ty).length + Δ.length + ty.ctors.length + 1 from by omega)] + rfl + · show VExpr.app _ _ = VExpr.app _ _ + congr 1 + · rw [VExpr.liftN_appN, + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega)] + show VExpr.appN (.bvar (liftVar Δ.length + ((idxTel U np ty).length + ty.ctors.length + 1) + ((idxTel U np ty).length + 1))) _ = _ + rw [liftVar_le (by omega), show Δ.length + + ((idxTel U np ty).length + ty.ctors.length + 1) = + (idxTel U np ty).length + Δ.length + ty.ctors.length + 1 from by omega] + rw [hcod] at hb + have hshape := hidx.retarget (by simpa only [VExpr.liftTelN_length] using hlen) + (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + Δ.length + + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (.param 0))) + rw [VExpr.instRev_forallE_sort, VExpr.instRev_appN, + VExpr.instRev_closedN _ (C := .const T (VLevel.params' U 1)) trivial, + List.map_append, + VExpr.map_instRev_bvarRevRange_ge _ _ _ (by rw [hlen]; omega), + show (idxTel U np ty).length + Δ.length + ty.ctors.length + 1 - idxs.length = + Δ.length + ty.ctors.length + 1 from by rw [hlen]; omega, + VExpr.bvarRevRange_congr' 0 hlen.symm, + VExpr.map_instRev_bvarRevRange] at hshape + rw [hlen] at hshape + have hfull := hshape.snoc ha + simp only [VExpr.inst] at hfull + change env.SpineWF (U+1) _ + (VExpr.forallN + (VExpr.liftTelN (Δ.length + ty.ctors.length + 1) (idxTel U np ty) 0) + (VExpr.forallN [VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + Δ.length + + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)] + (.sort (.param 0)))) + (idxs ++ [a]) (.sort (.param 0)) at hfull + rw [← VExpr.forallN_append] at hfull + have hfullLen : (idxs ++ [a]).length = + (VExpr.liftTelN (Δ.length + ty.ctors.length + 1) (idxTel U np ty) 0 ++ + [VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((idxTel U np ty).length + Δ.length + + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)]).length := by + simp only [List.length_append, List.length_singleton, VExpr.liftTelN_length, hlen] + have hactual := hfull.retarget hfullLen + (VExpr.app + (VExpr.appN (.bvar ((idxTel U np ty).length + Δ.length + + ty.ctors.length + 1)) + (VExpr.bvarRevRange 1 (idxTel U np ty).length)) + (.bvar 0)) + rw [VExpr.forallN_append] at hactual + have happ := hactual.hasType_appN hb + rw [show VExpr.app + (VExpr.appN (.bvar ((idxTel U np ty).length + Δ.length + + ty.ctors.length + 1)) + (VExpr.bvarRevRange 1 (idxTel U np ty).length)) + (.bvar 0) = + VExpr.appN (.bvar ((idxTel U np ty).length + Δ.length + + ty.ctors.length + 1)) (VExpr.bvarRevRange 0 ((idxTel U np ty).length + 1)) from by + rw [VExpr.bvarRevRange_congr' 0 (show (idxTel U np ty).length + 1 = + 1 + (idxTel U np ty).length from by omega), + ← VExpr.bvarRevRange_append (idxTel U np ty).length 1] + simpa [VExpr.bvarRevRange, VExpr.appN] using (VExpr.appN_append + (.bvar ((idxTel U np ty).length + Δ.length + ty.ctors.length + 1)) + (VExpr.bvarRevRange 1 (idxTel U np ty).length) [VExpr.bvar 0]).symm, + VExpr.instRev_appN, + VExpr.instRev_bvar_ge _ (by + simp only [List.length_append, List.length_singleton] + rw [hlen] + omega), + VExpr.bvarRevRange_congr' 0 (show (idxTel U np ty).length + 1 = + (idxs ++ [a]).length from by simp [hlen]), + VExpr.map_instRev_bvarRevRange] at happ + rw [show (idxTel U np ty).length + Δ.length + ty.ctors.length + 1 - + (idxs ++ [a]).length = Δ.length + ty.ctors.length from by + simp only [List.length_append, List.length_singleton] + rw [hlen] + omega] at happ + simpa [List.length_append, hlen] using happ + +/-- The motive variable applied to a well-typed index spine and major +premise has the recursor's elimination sort. -/ +theorem Stage3Env.motiveApp_hasType + (Δ : List VExpr) {idxs : List VExpr} {a : VExpr} + (hidx : env.SpineWF (U+1) + (Δ ++ ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.forallN + (VExpr.liftTelN (Δ.length + ty.ctors.length + 1) (idxTel U np ty) 0) + (.sort (l.inst (VLevel.params' U 1)))) + idxs (.sort (l.inst (VLevel.params' U 1)))) + (hlen : idxs.length = (idxTel U np ty).length) + (ha : env.HasType (U+1) + (Δ ++ ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) a + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (Δ.length + ty.ctors.length + 1) np ++ idxs))) : + env.HasType (U+1) + (Δ ++ ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN (.bvar (Δ.length + ty.ctors.length)) (idxs ++ [a])) + (.sort (.param 0)) := by + have hM := getElem?_rstack3 Δ (minorTypes U T np ty ty.ctors).reverse + (motiveType U T np ty) (paramsTel U np ty).reverse + (i := Δ.length + ty.ctors.length) + (by simp only [List.length_reverse, minorTypes_length]) + have hmlu := Lookup.of_getElem? hM + rw [motiveType_liftN] at hmlu + have hshape := hidx.retarget (by simpa only [VExpr.liftTelN_length] using hlen) + (.forallE (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (Δ.length + ty.ctors.length + 1 + + (idxTel U np ty).length) np ++ + VExpr.bvarRevRange 0 (idxTel U np ty).length)) + (.sort (.param 0))) + rw [VExpr.instRev_forallE_sort, VExpr.instRev_appN, + VExpr.instRev_closedN _ (C := .const T (VLevel.params' U 1)) trivial, + List.map_append, + VExpr.map_instRev_bvarRevRange_ge _ _ _ (by rw [hlen]; omega), + show Δ.length + ty.ctors.length + 1 + (idxTel U np ty).length - idxs.length = + Δ.length + ty.ctors.length + 1 from by rw [hlen]; omega, + VExpr.bvarRevRange_congr' 0 hlen.symm, + VExpr.map_instRev_bvarRevRange] at hshape + rw [hlen] at hshape + have hfull := hshape.snoc ha + simp only [VExpr.inst] at hfull + exact hfull.hasType_appN (.bvar hmlu) + +theorem Stage3Env.motiveAppRec_hasType + (Δ : List VExpr) {idxs : List VExpr} {a : VExpr} + (hidx : env.SpineWF (U+1) + (Δ ++ ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.forallN + (VExpr.liftTelN (Δ.length + ty.ctors.length + 1) (idxTel U np ty) 0) + (.sort (l.inst (VLevel.params' U 1)))) + idxs (.sort (l.inst (VLevel.params' U 1)))) + (hlen : idxs.length = (idxTel U np ty).length) + (ha : env.HasType (U+1) + (Δ ++ ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) a + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (Δ.length + ty.ctors.length + 1) np ++ idxs))) : + env.HasType (U+1) + (Δ ++ ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN (.bvar (Δ.length + ty.ctors.length)) (idxs ++ [a])) + (.sort (.param 0)) := by + have hM := getElem?_rstack3 Δ (minorTypesRec U T np ty ty.ctors).reverse + (motiveType U T np ty) (paramsTel U np ty).reverse + (i := Δ.length + ty.ctors.length) + (by simp only [List.length_reverse, minorTypesRec_length]) + exact motiveVarApp_hasType (env := env) (U := U) (T := T) (np := np) + (l := l) (ty := ty) (.bvar (Lookup.of_getElem? hM)) hidx hlen ha + +/-- The constructor-headed major of an iota rule, in the rule's binder +context (parameter spine past the motive, minors and fields). -/ +theorem Stage3Env.ctorAppRule_hasType {c : VConstVal} (hc : c ∈ ty.ctors) : + env.HasType (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (ctorFieldsR U np c).length)) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + ty.ctors.length + 1) np ++ + (ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length)) := by + have W := Ctx.LiftN.consTel (n := ty.ctors.length) + (VExpr.liftTelN 1 (ctorFieldsR U np c) 0) + (Ctx.LiftN.zero (n := ty.ctors.length) + (Γ := motiveType U T np ty :: (paramsTel U np ty).reverse) + (minorTypes U T np ty ty.ctors).reverse + (h := by simp [minorTypes_length])) + have h := (S.ctorAppMin_hasType hc []).weakN S.ord W + have hlen : (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).length = + (ctorFieldsR U np c).length := VExpr.liftTelN_length .. + rw [hlen] at h + simp only [List.length_nil, Nat.zero_add, VExpr.liftN_appN, VExpr.liftN, List.map_append, + List.map_map] at h + rw [VExpr.liftTelN_liftTelN, + bvarRevRange_liftN_ge _ _ _ _ (by omega), + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega)] at h + simp only [Function.comp_def, VExpr.liftN_zero, Nat.add_zero] at h + have hmap : (ctorIdxs U np c).map (fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN + ty.ctors.length (ctorFieldsR U np c).length) = + (ctorIdxs U np c).map (fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) := by + apply List.map_congr_left + intro e _ + rw [VExpr.liftN'_liftN_hi] + congr 1 + omega + rw [hmap] at h + simpa [List.append_assoc, Nat.add_comm, Nat.add_left_comm, Nat.add_assoc] using h + +theorem Stage3Env.ctorAppRuleRec_hasType {c : VConstVal} (hc : c ∈ ty.ctors) : + env.HasType (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (ctorFieldsR U np c).length)) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + ty.ctors.length + 1) np ++ + (ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length)) := by + have W := Ctx.LiftN.consTel (n := ty.ctors.length) + (VExpr.liftTelN 1 (ctorFieldsR U np c) 0) + (Ctx.LiftN.zero (n := ty.ctors.length) + (Γ := motiveType U T np ty :: (paramsTel U np ty).reverse) + (minorTypesRec U T np ty ty.ctors).reverse + (h := by simp [minorTypesRec_length])) + have h := (S.ctorAppMin_hasType hc []).weakN S.ord W + have hlen : (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).length = + (ctorFieldsR U np c).length := VExpr.liftTelN_length .. + rw [hlen] at h + simp only [List.length_nil, Nat.zero_add, VExpr.liftN_appN, VExpr.liftN, List.map_append, + List.map_map] at h + rw [VExpr.liftTelN_liftTelN, + bvarRevRange_liftN_ge _ _ _ _ (by omega), + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega)] at h + simp only [Function.comp_def, VExpr.liftN_zero, Nat.add_zero] at h + have hmap : (ctorIdxs U np c).map (fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN + ty.ctors.length (ctorFieldsR U np c).length) = + (ctorIdxs U np c).map (fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) := by + apply List.map_congr_left + intro e _ + rw [VExpr.liftN'_liftN_hi] + congr 1 + omega + rw [hmap] at h + simpa [List.append_assoc, Nat.add_comm, Nat.add_left_comm, Nat.add_assoc] using h + +theorem Stage3Env.ruleBinders_onTel {c : VConstVal} (hc : c ∈ ty.ctors) : + OnTel env (U+1) [] + (paramsTel U np ty ++ motiveType U T np ty :: minorTypes U T np ty ty.ctors ++ + VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0) := by + have hP : OnTel env (U+1) [] (paramsTel U np ty) := by + have := S.hparams.of_append.1.instL (U' := U+1) VLevel.params'_one_wf + simpa [paramsTel] using this + have hF : OnTel env (U+1) + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)) + (VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0) := by + have h0 := S.fieldsWF_onTel _ [] 0 rfl (by simpa using S.hfields c hc) + have h1 := h0.weakN S.ord + (.zero ((minorTypes U T np ty ty.ctors).reverse ++ [motiveType U T np ty])) + rw [liftTelN_congr _ _ (show ((minorTypes U T np ty ty.ctors).reverse ++ + [motiveType U T np ty]).length = ty.ctors.length + 1 from by + simp only [List.length_append, List.length_reverse, minorTypes_length, + List.length_singleton])] at h1 + simpa [ctorFieldsR, List.map_reverse, paramsTel, + List.append_assoc] using h1 + refine OnTel.append (OnTel.append hP ⟨?_, ?_⟩) ?_ + · simpa only [List.append_nil] using S.motive_isType + · have := S.minorTypes_onTel ty.ctors (fun _ h => h) [] 0 rfl + simpa only [List.nil_append, List.append_nil] using this + · simpa only [List.append_nil, List.append_assoc, List.reverse_append, + List.reverse_cons, List.singleton_append] using hF + +theorem Stage3Env.ruleBindersRec_onTel {c : VConstVal} (hc : c ∈ ty.ctors) : + OnTel env (U+1) [] + (paramsTel U np ty ++ motiveType U T np ty :: minorTypesRec U T np ty ty.ctors ++ + VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0) := by + have hP : OnTel env (U+1) [] (paramsTel U np ty) := by + have := S.hparams.of_append.1.instL (U' := U+1) VLevel.params'_one_wf + simpa [paramsTel] using this + have hF : OnTel env (U+1) + ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)) + (VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0) := by + have h0 := S.fieldsWF_onTel _ [] 0 rfl (by simpa using S.hfields c hc) + have h1 := h0.weakN S.ord + (.zero ((minorTypesRec U T np ty ty.ctors).reverse ++ [motiveType U T np ty])) + rw [liftTelN_congr _ _ (show ((minorTypesRec U T np ty ty.ctors).reverse ++ + [motiveType U T np ty]).length = ty.ctors.length + 1 from by + simp only [List.length_append, List.length_reverse, minorTypesRec_length, + List.length_singleton])] at h1 + simpa [ctorFieldsR, List.map_reverse, paramsTel, + List.append_assoc] using h1 + refine OnTel.append (OnTel.append hP ⟨?_, ?_⟩) ?_ + · simpa only [List.append_nil] using S.motive_isType + · have := S.minorTypesRec_onTel ty.ctors (fun _ h => h) [] 0 rfl + simpa only [List.nil_append, List.append_nil] using this + · simpa only [List.append_nil, List.append_assoc, List.reverse_append, + List.reverse_cons, List.singleton_append] using hF + +theorem Stage3Env.ruleType_isType {i : Nat} {c : VConstVal} + (hci : ty.ctors[i]? = some c) : + env.IsType (U+1) [] ((rule U T np ty i c).type) := by + have hc := List.mem_of_getElem? hci + show env.IsType (U+1) [] (VExpr.forallN + (paramsTel U np ty ++ motiveType U T np ty :: minorTypes U T np ty ty.ctors ++ + VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0) + (VExpr.appN (.bvar (ty.ctors.length + (ctorFieldsR U np c).length)) + (((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (ctorFieldsR U np c).length)]))) + refine IsType.forallN (S.ruleBinders_onTel hc) ?_ + simp only [List.reverse_append, List.reverse_cons, List.append_nil, List.append_assoc, + List.singleton_append] + have hSp0 := S.result_transport hc + ((minorTypes U T np ty ty.ctors).reverse ++ [motiveType U T np ty]) + (g := ty.ctors.length + 1) (by simp [minorTypes_length]) [] (d := 0) rfl + have hSp : env.SpineWF (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.forallN (VExpr.liftTelN + ((ctorFieldsR U np c).length + ty.ctors.length + 1) + (idxTel U np ty) 0) (.sort (l.inst (VLevel.params' U 1)))) + ((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) + (.sort (l.inst (VLevel.params' U 1))) := by + simpa [List.append_assoc, Nat.add_assoc] using hSp0 + have hidxLen : ((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length).length = + (idxTel U np ty).length := by + simp only [List.length_map, ctorIdxs_length, idxTel_length] + exact (stage3Ctor_eq (S.hs3 c hc)).2.1 + have hctor := S.ctorAppRule_hasType hc + have hSp' : env.SpineWF (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.forallN (VExpr.liftTelN + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse.length + + ty.ctors.length + 1) (idxTel U np ty) 0) + (.sort (l.inst (VLevel.params' U 1)))) + ((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) + (.sort (l.inst (VLevel.params' U 1))) := by + simpa only [List.length_reverse, VExpr.liftTelN_length] using hSp + have hctor' : env.HasType (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (ctorFieldsR U np c).length)) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse.length + + ty.ctors.length + 1) np ++ + (ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length)) := by + simpa only [List.length_reverse, VExpr.liftTelN_length] using hctor + refine ⟨.param 0, ?_⟩ + have hm := S.motiveApp_hasType + (VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse + hSp' hidxLen hctor' + rw [List.length_reverse, VExpr.liftTelN_length, + show (ctorFieldsR U np c).length + ty.ctors.length = + ty.ctors.length + (ctorFieldsR U np c).length from by omega, + show ty.ctors.length + (ctorFieldsR U np c).length + 1 = + (ctorFieldsR U np c).length + ty.ctors.length + 1 from by omega] at hm + exact hm + +theorem Stage3Env.ruleTypeRec_isType {i : Nat} {c : VConstVal} + (hci : ty.ctors[i]? = some c) : + env.IsType (U+1) [] ((ruleRec U T np ty i c).type) := by + have hc := List.mem_of_getElem? hci + show env.IsType (U+1) [] (VExpr.forallN + (paramsTel U np ty ++ motiveType U T np ty :: minorTypesRec U T np ty ty.ctors ++ + VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0) + (VExpr.appN (.bvar (ty.ctors.length + (ctorFieldsR U np c).length)) + (((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (ctorFieldsR U np c).length)]))) + refine IsType.forallN (S.ruleBindersRec_onTel hc) ?_ + simp only [List.reverse_append, List.reverse_cons, List.append_nil, List.append_assoc, + List.singleton_append] + have hSp0 := S.result_transport hc + ((minorTypesRec U T np ty ty.ctors).reverse ++ [motiveType U T np ty]) + (g := ty.ctors.length + 1) (by simp [minorTypesRec_length]) [] (d := 0) rfl + have hSp : env.SpineWF (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.forallN (VExpr.liftTelN + ((ctorFieldsR U np c).length + ty.ctors.length + 1) + (idxTel U np ty) 0) (.sort (l.inst (VLevel.params' U 1)))) + ((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) + (.sort (l.inst (VLevel.params' U 1))) := by + simpa [List.append_assoc, Nat.add_assoc] using hSp0 + have hidxLen : ((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length).length = + (idxTel U np ty).length := by + simp only [List.length_map, ctorIdxs_length, idxTel_length] + exact (stage3Ctor_eq (S.hs3 c hc)).2.1 + have hctor := S.ctorAppRuleRec_hasType hc + have hSp' : env.SpineWF (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.forallN (VExpr.liftTelN + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse.length + + ty.ctors.length + 1) (idxTel U np ty) 0) + (.sort (l.inst (VLevel.params' U 1)))) + ((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) + (.sort (l.inst (VLevel.params' U 1))) := by + simpa only [List.length_reverse, VExpr.liftTelN_length] using hSp + have hctor' : env.HasType (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (ctorFieldsR U np c).length)) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse.length + + ty.ctors.length + 1) np ++ + (ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length)) := by + simpa only [List.length_reverse, VExpr.liftTelN_length] using hctor + refine ⟨.param 0, ?_⟩ + have hm := S.motiveAppRec_hasType + (VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse + hSp' hidxLen hctor' + rw [List.length_reverse, VExpr.liftTelN_length, + show (ctorFieldsR U np c).length + ty.ctors.length = + ty.ctors.length + (ctorFieldsR U np c).length from by omega, + show ty.ctors.length + (ctorFieldsR U np c).length + 1 = + (ctorFieldsR U np c).length + ty.ctors.length + 1 from by omega] at hm + exact hm + +/-- A recursive field gives a well-typed recursive call in the full iota +rule context, carrying that field's own index arguments. -/ +theorem Stage3Env.recCallRule_hasType {c : VConstVal} (hc : c ∈ ty.ctors) + (hrec : env.constants (.str T "rec") = some (recConst U T np ty)) + {q : Nat × List VExpr} + (hq : q ∈ recPairsR U T np (idxTel U np ty).length c) : + env.HasType (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN + (VExpr.appN (.const (.str T "rec") (VLevel.params (U+1))) + (VExpr.bvarRevRange (ctorFieldsR U np c).length + (np + ty.ctors.length + 1))) + ((q.2.map fun e => + (e.liftN (ty.ctors.length + 1) q.1).liftN + ((ctorFieldsR U np c).length - q.1)) ++ + [.bvar ((ctorFieldsR U np c).length - 1 - q.1)])) + (VExpr.appN (.bvar (ty.ctors.length + (ctorFieldsR U np c).length)) + ((q.2.map fun e => + (e.liftN (ty.ctors.length + 1) q.1).liftN + ((ctorFieldsR U np c).length - q.1)) ++ + [.bvar ((ctorFieldsR U np c).length - 1 - q.1)])) := by + obtain ⟨B, hBj, hrecB0, hidx⟩ := recPairsR_mem hq + have hrecB : isRecField U T np + (ctorFields (VExpr.dropN np ty.type)).length q.1 B = true := by + rwa [← idxTel_length (U := U) (ty := ty)] + have hjm : q.1 < (ctorFieldsR U np c).length := by + simpa [ctorFieldsR_length] using recPairsR_lt _ hq + have hml2 : (VExpr.liftTelN (ty.ctors.length + 1) + (ctorFieldsR U np c) 0).length = (ctorFieldsR U np c).length := + VExpr.liftTelN_length .. + have hSp0 := S.spine_transport hc hBj hrecB + ((minorTypes U T np ty ty.ctors).reverse ++ [motiveType U T np ty]) + (g := ty.ctors.length + 1) (by simp [minorTypes_length]) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).drop q.1).reverse + (d := (ctorFieldsR U np c).length - q.1) (by + simp only [List.length_reverse, List.length_drop, hml2]) + rw [show + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).drop q.1).reverse ++ + ((VExpr.liftTelN (ty.ctors.length + 1) + ((ctorFieldsR U np c).take q.1) 0).reverse ++ + (((minorTypes U T np ty ty.ctors).reverse ++ [motiveType U T np ty]) ++ + (paramsTel U np ty).reverse)) = + (VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)) from by + rw [← VExpr.liftTelN_take, List.append_assoc, + ← List.append_assoc + (((VExpr.liftTelN (ty.ctors.length + 1) + (ctorFieldsR U np c) 0).drop q.1).reverse), + ← List.reverse_append, List.take_append_drop, List.singleton_append, + ← List.append_assoc], + liftTelN_congr _ _ (show q.1 + (ty.ctors.length + 1) + + ((ctorFieldsR U np c).length - q.1) = + (ctorFieldsR U np c).length + ty.ctors.length + 1 from by omega), + ← hidx] at hSp0 + have hni : q.2.length = (idxTel U np ty).length := by + rw [hidx, List.length_map, idxTel_length] + exact (isRecField_eq hrecB).2.1 + have hSp : env.SpineWF (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.forallN (VExpr.liftTelN + ((VExpr.liftTelN (ty.ctors.length + 1) + (ctorFieldsR U np c) 0).reverse.length + ty.ctors.length + 1) + (idxTel U np ty) 0) (.sort (l.inst (VLevel.params' U 1)))) + (q.2.map fun e => (e.liftN (ty.ctors.length + 1) q.1).liftN + ((ctorFieldsR U np c).length - q.1)) + (.sort (l.inst (VLevel.params' U 1))) := by + simpa only [List.length_reverse, VExpr.liftTelN_length] using hSp0 + have hSpLen : (q.2.map fun e => + (e.liftN (ty.ctors.length + 1) q.1).liftN + ((ctorFieldsR U np c).length - q.1)).length = + (idxTel U np ty).length := by simpa using hni + have hF : ((VExpr.liftTelN (ty.ctors.length + 1) + (ctorFieldsR U np c) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)))[ + (ctorFieldsR U np c).length - 1 - q.1]? = + some ((B.instL (VLevel.params' U 1)).liftN + (ty.ctors.length + 1) q.1) := by + rw [List.getElem?_append_left + (by simp only [List.length_reverse, VExpr.liftTelN_length]; omega), + List.getElem?_reverse (by simp only [VExpr.liftTelN_length]; omega), + VExpr.liftTelN_length, + show (ctorFieldsR U np c).length - 1 - + ((ctorFieldsR U np c).length - 1 - q.1) = q.1 from by omega, + VExpr.liftTelN_getElem?, ctorFieldsR_getElem?, hBj] + simp only [Option.map_some, Nat.zero_add] + have hflu := Lookup.of_getElem? hF + rw [show (((B.instL (VLevel.params' U 1)).liftN + (ty.ctors.length + 1) q.1).liftN + ((ctorFieldsR U np c).length - 1 - q.1 + 1)) = + VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + ty.ctors.length + 1) np ++ + q.2.map fun e => (e.liftN (ty.ctors.length + 1) q.1).liftN + ((ctorFieldsR U np c).length - q.1)) from by + conv => lhs; rw [(isRecField_eq hrecB).1] + rw [VExpr.instL_appN, List.map_append, bvarRevRange_instL, + show (VExpr.const T (VLevel.params U)).instL (VLevel.params' U 1) = + .const T (VLevel.params' U 1) from by + simp [VExpr.instL, VLevel.params_map_inst_params'], + VExpr.liftN_appN, VExpr.liftN_appN, List.map_append, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (by omega), + bvarRevRange_liftN_ge _ _ _ _ (Nat.zero_le _), + VExpr.bvarRevRange_congr np (show + (ctorFieldsR U np c).length - 1 - q.1 + 1 + + (ty.ctors.length + 1 + q.1) = + (ctorFieldsR U np c).length + ty.ctors.length + 1 from by omega), + ← hidx, List.map_map] + apply congrArg (VExpr.appN (.const T (VLevel.params' U 1))) + apply congrArg (VExpr.bvarRevRange + ((ctorFieldsR U np c).length + ty.ctors.length + 1) np ++ ·) + apply List.map_congr_left + intro e _ + rw [show (ctorFieldsR U np c).length - 1 - q.1 + 1 = + (ctorFieldsR U np c).length - q.1 from by omega] + simp only [Function.comp_apply] + ] at hflu + have ha : env.HasType (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (.bvar ((ctorFieldsR U np c).length - 1 - q.1)) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange + ((VExpr.liftTelN (ty.ctors.length + 1) + (ctorFieldsR U np c) 0).reverse.length + ty.ctors.length + 1) np ++ + q.2.map fun e => (e.liftN (ty.ctors.length + 1) q.1).liftN + ((ctorFieldsR U np c).length - q.1))) := by + simpa only [List.length_reverse, VExpr.liftTelN_length] using + (VEnv.HasType.bvar (env := env) (U := U+1) hflu) + have hr := S.recApp_hasType hrec + (VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse + hSp hSpLen ha + rw [List.length_reverse, VExpr.liftTelN_length, + show (ctorFieldsR U np c).length + ty.ctors.length = + ty.ctors.length + (ctorFieldsR U np c).length from by omega] at hr + exact hr + +/-- A generalized recursive argument yields the direct or lambda-valued +recursive call required by its functional IH in the complete rule context. -/ +theorem Stage3Env.ruleCallRec_hasType {c : VConstVal} (hc : c ∈ ty.ctors) + (hrec : env.constants (.str T "rec") = some (recConstRec U T np ty)) + {r : RecArg} (hr : r ∈ recArgsR U T np (idxTel U np ty).length c) : + env.HasType (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (r.ruleCall (ctorFieldsR U np c).length ty.ctors.length + (VExpr.appN (.const (.str T "rec") (VLevel.params (U+1))) + (VExpr.bvarRevRange (ctorFieldsR U np c).length + (np + ty.ctors.length + 1)))) + (r.ruleIH (ctorFieldsR U np c).length ty.ctors.length) := by + obtain ⟨r₀, B, rfl, hB, hr₀⟩ := recArgsR_mem hr + let r := r₀.instL (VLevel.params' U 1) + let m := (ctorFieldsR U np c).length + let k := ty.ctors.length + let j := r₀.fieldIndex + let Fs := VExpr.liftTelN (k+1) (ctorFieldsR U np c) 0 + let As := r.ruleBinders m k + let idxs := r.indices.map fun e => + (e.liftN (k+1) (r.fieldIndex+r.binders.length)).liftN + (m-r.fieldIndex) r.binders.length + let Γ := Fs.reverse ++ ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)) + have hjm : j < m := by + simpa [j, m, RecArg.instL, ctorFieldsR_length] using recArgsR_lt _ hr + have hFsLen : Fs.length = m := by + simp [Fs, m, VExpr.liftTelN_length] + have ht := S.recArg_transport hc hB hr₀ + ((minorTypesRec U T np ty ty.ctors).reverse ++ [motiveType U T np ty]) + (g := k+1) (by simp [k, minorTypesRec_length]) + (Fs.drop j).reverse (d := m-j) (by + simp only [List.length_reverse, List.length_drop, hFsLen]) + dsimp only [r, j, RecArg.instL] at ht + have hctx : + (Fs.drop j).reverse ++ + ((VExpr.liftTelN (k+1) ((ctorFieldsR U np c).take j) 0).reverse ++ + (((minorTypesRec U T np ty ty.ctors).reverse ++ + [motiveType U T np ty]) ++ (paramsTel U np ty).reverse)) = Γ := by + dsimp only [Γ, Fs] + rw [← VExpr.liftTelN_take, List.append_assoc, + ← List.append_assoc + (((VExpr.liftTelN (k+1) (ctorFieldsR U np c) 0).drop j).reverse), + ← List.reverse_append, List.take_append_drop, List.singleton_append, + ← List.append_assoc] + have htel : OnTel env (U+1) Γ As := by + rw [hctx] at ht + simpa [r, As, m, k, j, RecArg.instL, RecArg.ruleBinders] using ht.1 + have hsp : env.SpineWF (U+1) (As.reverse ++ Γ) + (VExpr.forallN + (VExpr.liftTelN (m+k+r.binders.length+1) (idxTel U np ty) 0) + (.sort (l.inst (VLevel.params' U 1)))) + idxs (.sort (l.inst (VLevel.params' U 1))) := by + rw [hctx] at ht + simpa [r, As, idxs, m, k, j, RecArg.instL, RecArg.ruleBinders, + List.append_assoc, + show j + r₀.binders.length + (k+1) + (m-j) = + m+k+r₀.binders.length+1 from by omega] using ht.2 + have hF : Γ[m-1-j]? = + some ((B.instL (VLevel.params' U 1)).liftN (k+1) j) := by + dsimp only [Γ, Fs] + rw [List.getElem?_append_left + (by simp only [List.length_reverse, VExpr.liftTelN_length]; omega), + List.getElem?_reverse (by rw [hFsLen]; omega), + VExpr.liftTelN_length, + show m - 1 - (m - 1 - j) = j from by omega, + VExpr.liftTelN_getElem?, ctorFieldsR_getElem?, hB] + simp + have hlu := Lookup.of_getElem? hF + dsimp only [j, r] at hlu + rw [show m-1-r₀.fieldIndex+1 = m-r₀.fieldIndex from by omega] + at hlu + have hfield := recArg_rule_fieldType hr₀ m k (by simpa [j] using hjm) + simp only [RecArg.instL, ElimMode.large_sourceLevels] at hfield + rw [hfield] at hlu + have hf0 := VEnv.HasType.bvar (env := env) (U := U+1) hlu + have hf := hf0.weakN S.ord (Ctx.LiftN.zero (Γ := Γ) As.reverse) + have hmajor := VEnv.HasType.appN_selfSpine (env := env) (U := U+1) + (As := As) (B := VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (m+k+r.binders.length+1) np ++ idxs)) + (Δ := []) (Γ := Γ) (by + simpa [As, r, idxs, j, RecArg.instL, RecArg.ruleBinders, + List.length_reverse, List.map_map, Function.comp_def] using hf) + simp only [List.length_nil, VExpr.liftN_zero, List.nil_append] at hmajor + have hAsLen : As.length = r.binders.length := by + simp [As, RecArg.ruleBinders, VExpr.liftTelN_length] + change env.HasType (U+1) (As.reverse ++ Γ) + ((VExpr.bvar (m-1-r.fieldIndex+As.length)).appN + (VExpr.bvarRevRange 0 As.length)) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange (m+k+r.binders.length+1) np ++ idxs)) at hmajor + rw [hAsLen] at hmajor + have hlen : idxs.length = (idxTel U np ty).length := by + simpa [idxs, r, RecArg.instL] using (recArg?_eq hr₀).2.2.2.1 + have hcall := S.recAppRec_hasType hrec (As.reverse ++ Fs.reverse) + (by simpa [Γ, List.append_assoc, hAsLen, hFsLen, Nat.add_comm, + Nat.add_left_comm, Nat.add_assoc] using hsp) + hlen + (by simpa [Γ, List.append_assoc, hAsLen, hFsLen, Nat.add_comm, + Nat.add_left_comm, Nat.add_assoc] using hmajor) + have hbaseLift : + (VExpr.appN (.const (.str T "rec") (VLevel.params (U+1))) + (VExpr.bvarRevRange m (np+(k+1)))).liftN r.binders.length = + VExpr.appN (.const (.str T "rec") (VLevel.params (U+1))) + (VExpr.bvarRevRange (m+r.binders.length) (np+(k+1))) := by + rw [VExpr.liftN_appN] + simp only [VExpr.liftN] + rw [bvarRevRange_liftN_ge _ _ _ _ (Nat.zero_le _)] + apply congrArg (VExpr.appN _) + apply VExpr.bvarRevRange_congr + omega + have hbaseRange : + VExpr.appN + ((VExpr.const (.str T "rec") (VLevel.params (U+1))).app + (VExpr.bvar (np + (k + (m + r.binders.length))))) + (VExpr.bvarRevRange (m+r.binders.length) (np+k)) = + VExpr.appN + (.const (.str T "rec") (VLevel.params (U+1))) + (VExpr.bvarRevRange (m+r.binders.length) (np+(k+1))) := by + rw [show np + (k + (m + r.binders.length)) = + (m+r.binders.length) + (np+k) by omega, + show np+(k+1) = (np+k)+1 by omega] + rfl + have hlam := HasType.lamN htel (by + simpa [Γ, Fs, hAsLen, hFsLen, List.append_assoc, + VExpr.liftN_appN, bvarRevRange_liftN_ge _ _ _ _ (Nat.zero_le _), + Nat.add_comm, Nat.add_left_comm, Nat.add_assoc] using hcall) + rw [hbaseRange] at hlam + simpa only [RecArg.ruleCall, RecArg.ruleIH, r, As, idxs, m, k, Γ, Fs, + hbaseLift, List.append_assoc, Nat.add_assoc] using hlam + + +/-- The right-hand side of an indexed iota rule: the constructor's minor +premise applied to its fields and to one indexed recursive call per recursive +field. -/ +theorem Stage3Env.minorApp_hasType {i : Nat} {c : VConstVal} + (hci : ty.ctors[i]? = some c) + (hrec : env.constants (.str T "rec") = some (recConst U T np ty)) : + env.HasType (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN (.bvar (ty.ctors.length - 1 - i + (ctorFieldsR U np c).length)) + (VExpr.bvarRevRange 0 (ctorFieldsR U np c).length ++ + List.map (fun q => VExpr.appN + (VExpr.appN (.const (.str T "rec") (VLevel.params (U+1))) + (VExpr.bvarRevRange (ctorFieldsR U np c).length + (np + ty.ctors.length + 1))) + ((q.2.map fun e => + (e.liftN (ty.ctors.length + 1) q.1).liftN + ((ctorFieldsR U np c).length - q.1)) ++ + [.bvar ((ctorFieldsR U np c).length - 1 - q.1)])) + (recPairsR U T np (idxTel U np ty).length c))) + (VExpr.appN (.bvar (ty.ctors.length + (ctorFieldsR U np c).length)) + (((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (ctorFieldsR U np c).length)])) := by + obtain ⟨hik, -⟩ := List.getElem?_eq_some_iff.1 hci + have hc := List.mem_of_getElem? hci + let rs := recPairsR U T np (idxTel U np ty).length c + have hrs : rs = recPairsR U T np (idxTel U np ty).length c := rfl + have hrsLt : ∀ q ∈ rs, q.1 < (ctorFieldsR U np c).length := by + intro q hq + simpa [hrs, ctorFieldsR_length] using recPairsR_lt _ hq + rw [VExpr.appN_append] + have hlu0 : ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)))[ + ty.ctors.length - 1 - i + (ctorFieldsR U np c).length]? = + some (VExpr.liftN i (minorType U T np ty c)) := by + rw [getElem?_rstack_mid _ _ _ + (by simp only [List.length_reverse, VExpr.liftTelN_length]; omega) + (by simp only [List.length_reverse, VExpr.liftTelN_length, minorTypes_length] + omega), + show ty.ctors.length - 1 - i + (ctorFieldsR U np c).length - + ((VExpr.liftTelN (ty.ctors.length + 1) + (ctorFieldsR U np c) 0).reverse).length = + ty.ctors.length - 1 - i from by + simp only [List.length_reverse, VExpr.liftTelN_length] + omega, + List.getElem?_reverse (by simp only [minorTypes_length]; omega), + show (minorTypes U T np ty ty.ctors).length - 1 - + (ty.ctors.length - 1 - i) = i from by + simp only [minorTypes_length] + omega, + minorTypes_getElem?, hci, Nat.zero_add] + rfl + have hlu := Lookup.of_getElem? hlu0 + rw [VExpr.liftN_liftN, + show i + (ty.ctors.length - 1 - i + (ctorFieldsR U np c).length + 1) = + (ctorFieldsR U np c).length + ty.ctors.length from by omega] at hlu + have hminorEq : (minorType U T np ty c).liftN + ((ctorFieldsR U np c).length + ty.ctors.length) = + (VExpr.forallN + (VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0) + ((VExpr.forallN (ihsFrom (ctorFieldsR U np c).length rs 0) + (VExpr.appN (.bvar ((ctorFieldsR U np c).length + rs.length)) + (((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN rs.length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange (rs.length + + (ctorFieldsR U np c).length + 1) np ++ + VExpr.bvarRevRange rs.length (ctorFieldsR U np c).length)]))).liftN + ty.ctors.length (ctorFieldsR U np c).length)).liftN + (ctorFieldsR U np c).length := by + simp only [minorType, rs, ElimMode.large_sourceLevels] + conv => lhs; rw [VExpr.liftN_forallN, VExpr.liftTelN_liftTelN, + liftTelN_congr _ _ (show (1:Nat) + + ((ctorFieldsR U np c).length + ty.ctors.length) = + ty.ctors.length + 1 + (ctorFieldsR U np c).length from by omega), + show (0:Nat) + (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).length = + (ctorFieldsR U np c).length from by simp [VExpr.liftTelN_length]] + conv => rhs; rw [VExpr.liftN_forallN, VExpr.liftTelN_liftTelN, + show (0:Nat) + (VExpr.liftTelN (ty.ctors.length + 1) + (ctorFieldsR U np c) 0).length = (ctorFieldsR U np c).length from by + simp [VExpr.liftTelN_length], + VExpr.liftN'_liftN_hi, + Nat.add_comm ty.ctors.length (ctorFieldsR U np c).length] + have hfields := HasType.appN_selfSpine (env := env) (U := U+1) + (As := VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0) + (B := (VExpr.forallN (ihsFrom (ctorFieldsR U np c).length rs 0) + (VExpr.appN (.bvar ((ctorFieldsR U np c).length + rs.length)) + (((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN rs.length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange (rs.length + (ctorFieldsR U np c).length + 1) np ++ + VExpr.bvarRevRange rs.length (ctorFieldsR U np c).length)]))).liftN + ty.ctors.length (ctorFieldsR U np c).length) + (Δ := []) + (Γ := (minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)) + (f := .bvar (ty.ctors.length - 1 - i + (ctorFieldsR U np c).length)) (by + have hb := VEnv.HasType.bvar (env := env) (U := U+1) hlu + rw [hminorEq] at hb + simpa [VExpr.liftTelN_length] using hb) + simp only [List.length_nil, VExpr.liftTelN_length, VExpr.liftN_zero] at hfields + rw [ihsFrom_liftN' (ctorFieldsR U np c).length ty.ctors.length rs + hrsLt 0 + (VExpr.appN (.bvar ((ctorFieldsR U np c).length + rs.length)) + (((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN rs.length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange (rs.length + (ctorFieldsR U np c).length + 1) np ++ + VExpr.bvarRevRange rs.length (ctorFieldsR U np c).length)])) + (cut := (ctorFieldsR U np c).length) rfl] at hfields + have hD : + (VExpr.appN (.bvar ((ctorFieldsR U np c).length + rs.length)) + (((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN rs.length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange (rs.length + (ctorFieldsR U np c).length + 1) np ++ + VExpr.bvarRevRange rs.length (ctorFieldsR U np c).length)])).liftN + ty.ctors.length ((ctorFieldsR U np c).length + 0 + rs.length) = + (VExpr.appN (.bvar (ty.ctors.length + (ctorFieldsR U np c).length)) + (((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (ctorFieldsR U np c).length)])).liftN rs.length := by + rw [VExpr.liftN_appN, VExpr.liftN_appN, List.map_append, List.map_append, + List.map_map, List.map_map] + show VExpr.appN _ (_ ++ [_]) = VExpr.appN _ (_ ++ [_]) + congr 1 + · show VExpr.bvar (liftVar ty.ctors.length + ((ctorFieldsR U np c).length + rs.length) + ((ctorFieldsR U np c).length + 0 + rs.length)) = + VExpr.bvar (liftVar rs.length + (ty.ctors.length + (ctorFieldsR U np c).length) 0) + rw [liftVar_le (by omega), liftVar_le (Nat.zero_le _)] + congr 1 + omega + · congr 1 + · apply List.map_congr_left + intro e _ + simp only [Function.comp_apply] + rw [show (ctorFieldsR U np c).length + 0 + rs.length = + (ctorFieldsR U np c).length + rs.length from by omega, + VExpr.liftN_liftN_mid e ty.ctors.length rs.length (Nat.zero_le _)] + · congr 1 + simp only [] + rw [show (ctorFieldsR U np c).length + 0 + rs.length = + (ctorFieldsR U np c).length + rs.length from by omega] + rw [VExpr.liftN_appN, VExpr.liftN_appN, List.map_append, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (by omega), + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega), + bvarRevRange_liftN_ge _ _ _ _ (Nat.zero_le _), + bvarRevRange_liftN_ge _ _ _ _ (Nat.zero_le _), + VExpr.bvarRevRange_congr np (show ty.ctors.length + + (rs.length + (ctorFieldsR U np c).length + 1) = + rs.length + ((ctorFieldsR U np c).length + ty.ctors.length + 1) from by + omega), + VExpr.bvarRevRange_congr _ (show rs.length = rs.length + 0 from by omega)] + rfl + rw [hD] at hfields + have hres := hasType_appN_ihs (env := env) (U := U+1) + (m := (ctorFieldsR U np c).length) (k := ty.ctors.length) + (rs := rs) + (argOf := fun (j, idxs) => + VExpr.appN + (VExpr.appN (.const (.str T "rec") (VLevel.params (U+1))) + (VExpr.bvarRevRange (ctorFieldsR U np c).length + (np + ty.ctors.length + 1))) + ((idxs.map fun e => (e.liftN (ty.ctors.length + 1) j).liftN + ((ctorFieldsR U np c).length - j)) ++ + [.bvar ((ctorFieldsR U np c).length - 1 - j)])) + hrsLt (fun q hq => by + have hr := S.recCallRule_hasType hc hrec (hrs ▸ hq) + have hargs : (q.2.map fun e => + ((e.liftN 1 q.1).liftN ((ctorFieldsR U np c).length - q.1)).liftN + ty.ctors.length (ctorFieldsR U np c).length) = + q.2.map fun e => (e.liftN (ty.ctors.length + 1) q.1).liftN + ((ctorFieldsR U np c).length - q.1) := by + apply List.map_congr_left + intro e _ + rw [← VExpr.liftN_liftN_mid e ty.ctors.length + ((ctorFieldsR U np c).length - q.1) (Nat.zero_le _), + show q.1 + ((ctorFieldsR U np c).length - q.1) = + (ctorFieldsR U np c).length from by + have := hrsLt q hq + omega] + rw [hargs] + exact hr) + hfields + simpa only [hrs, List.nil_append] using hres + +/-- Generalized iota RHS: apply the selected constructor minor to every field +and then to the direct or functional recursive call generated for each +`RecArg`. -/ +theorem Stage3Env.minorAppRec_hasType {i : Nat} {c : VConstVal} + (hci : ty.ctors[i]? = some c) + (hrec : env.constants (.str T "rec") = some (recConstRec U T np ty)) : + env.HasType (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN (.bvar (ty.ctors.length - 1 - i + (ctorFieldsR U np c).length)) + (VExpr.bvarRevRange 0 (ctorFieldsR U np c).length ++ + List.map (fun r => r.ruleCall (ctorFieldsR U np c).length ty.ctors.length + (VExpr.appN (.const (.str T "rec") (VLevel.params (U+1))) + (VExpr.bvarRevRange (ctorFieldsR U np c).length + (np + ty.ctors.length + 1)))) + (recArgsR U T np (idxTel U np ty).length c))) + (VExpr.appN (.bvar (ty.ctors.length + (ctorFieldsR U np c).length)) + (((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (ctorFieldsR U np c).length)])) := by + obtain ⟨hik, -⟩ := List.getElem?_eq_some_iff.1 hci + have hc := List.mem_of_getElem? hci + let rs := recArgsR U T np (idxTel U np ty).length c + have hrs : rs = recArgsR U T np (idxTel U np ty).length c := rfl + have hrsLt : ∀ r ∈ rs, r.fieldIndex < (ctorFieldsR U np c).length := by + intro r hr + simpa [hrs, ctorFieldsR_length] using recArgsR_lt _ hr + rw [VExpr.appN_append] + have hlu0 : ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)))[ + ty.ctors.length - 1 - i + (ctorFieldsR U np c).length]? = + some (VExpr.liftN i (minorTypeRec U T np ty c)) := by + rw [getElem?_rstack_mid _ _ _ + (by simp only [List.length_reverse, VExpr.liftTelN_length]; omega) + (by simp only [List.length_reverse, VExpr.liftTelN_length, + minorTypesRec_length]; omega), + show ty.ctors.length - 1 - i + (ctorFieldsR U np c).length - + ((VExpr.liftTelN (ty.ctors.length + 1) + (ctorFieldsR U np c) 0).reverse).length = + ty.ctors.length - 1 - i from by + simp only [List.length_reverse, VExpr.liftTelN_length] + omega, + List.getElem?_reverse (by simp only [minorTypesRec_length]; omega), + show (minorTypesRec U T np ty ty.ctors).length - 1 - + (ty.ctors.length - 1 - i) = i from by + simp only [minorTypesRec_length] + omega, + minorTypesRec_getElem?, hci, Nat.zero_add] + rfl + have hlu := Lookup.of_getElem? hlu0 + rw [VExpr.liftN_liftN, + show i + (ty.ctors.length - 1 - i + (ctorFieldsR U np c).length + 1) = + (ctorFieldsR U np c).length + ty.ctors.length from by omega] at hlu + have hminorEq : (minorTypeRec U T np ty c).liftN + ((ctorFieldsR U np c).length + ty.ctors.length) = + (VExpr.forallN + (VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0) + ((VExpr.forallN (ihsFromRecArgs (ctorFieldsR U np c).length rs 0) + (VExpr.appN (.bvar ((ctorFieldsR U np c).length + rs.length)) + (((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN rs.length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange (rs.length + + (ctorFieldsR U np c).length + 1) np ++ + VExpr.bvarRevRange rs.length (ctorFieldsR U np c).length)]))).liftN + ty.ctors.length (ctorFieldsR U np c).length)).liftN + (ctorFieldsR U np c).length := by + simp only [minorTypeRec, rs, ElimMode.large_sourceLevels] + conv => lhs; rw [VExpr.liftN_forallN, VExpr.liftTelN_liftTelN, + liftTelN_congr _ _ (show (1:Nat) + + ((ctorFieldsR U np c).length + ty.ctors.length) = + ty.ctors.length + 1 + (ctorFieldsR U np c).length from by omega), + show (0:Nat) + (VExpr.liftTelN 1 (ctorFieldsR U np c) 0).length = + (ctorFieldsR U np c).length from by simp [VExpr.liftTelN_length]] + conv => rhs; rw [VExpr.liftN_forallN, VExpr.liftTelN_liftTelN, + show (0:Nat) + (VExpr.liftTelN (ty.ctors.length + 1) + (ctorFieldsR U np c) 0).length = (ctorFieldsR U np c).length from by + simp [VExpr.liftTelN_length], + VExpr.liftN'_liftN_hi, + Nat.add_comm ty.ctors.length (ctorFieldsR U np c).length] + have hfields := HasType.appN_selfSpine (env := env) (U := U+1) + (As := VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0) + (B := (VExpr.forallN (ihsFromRecArgs (ctorFieldsR U np c).length rs 0) + (VExpr.appN (.bvar ((ctorFieldsR U np c).length + rs.length)) + (((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN rs.length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange (rs.length + (ctorFieldsR U np c).length + 1) np ++ + VExpr.bvarRevRange rs.length (ctorFieldsR U np c).length)]))).liftN + ty.ctors.length (ctorFieldsR U np c).length) + (Δ := []) + (Γ := (minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse)) + (f := .bvar (ty.ctors.length - 1 - i + (ctorFieldsR U np c).length)) (by + have hb := VEnv.HasType.bvar (env := env) (U := U+1) hlu + rw [hminorEq] at hb + simpa [VExpr.liftTelN_length] using hb) + simp only [List.length_nil, VExpr.liftTelN_length, VExpr.liftN_zero] at hfields + rw [ihsFromRecArgs_liftN' (ctorFieldsR U np c).length ty.ctors.length rs + hrsLt 0 + (VExpr.appN (.bvar ((ctorFieldsR U np c).length + rs.length)) + (((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN rs.length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange (rs.length + (ctorFieldsR U np c).length + 1) np ++ + VExpr.bvarRevRange rs.length (ctorFieldsR U np c).length)])) + (cut := (ctorFieldsR U np c).length) rfl] at hfields + have hD : + (VExpr.appN (.bvar ((ctorFieldsR U np c).length + rs.length)) + (((ctorIdxs U np c).map fun e => + (e.liftN 1 (ctorFieldsR U np c).length).liftN rs.length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange (rs.length + (ctorFieldsR U np c).length + 1) np ++ + VExpr.bvarRevRange rs.length (ctorFieldsR U np c).length)])).liftN + ty.ctors.length ((ctorFieldsR U np c).length + 0 + rs.length) = + (VExpr.appN (.bvar (ty.ctors.length + (ctorFieldsR U np c).length)) + (((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (ctorFieldsR U np c).length)])).liftN rs.length := by + rw [VExpr.liftN_appN, VExpr.liftN_appN, List.map_append, List.map_append, + List.map_map, List.map_map] + show VExpr.appN _ (_ ++ [_]) = VExpr.appN _ (_ ++ [_]) + congr 1 + · show VExpr.bvar (liftVar ty.ctors.length + ((ctorFieldsR U np c).length + rs.length) + ((ctorFieldsR U np c).length + 0 + rs.length)) = + VExpr.bvar (liftVar rs.length + (ty.ctors.length + (ctorFieldsR U np c).length) 0) + rw [liftVar_le (by omega), liftVar_le (Nat.zero_le _)] + congr 1 + omega + · congr 1 + · apply List.map_congr_left + intro e _ + simp only [Function.comp_apply] + rw [show (ctorFieldsR U np c).length + 0 + rs.length = + (ctorFieldsR U np c).length + rs.length from by omega, + VExpr.liftN_liftN_mid e ty.ctors.length rs.length (Nat.zero_le _)] + · congr 1 + simp only [] + rw [show (ctorFieldsR U np c).length + 0 + rs.length = + (ctorFieldsR U np c).length + rs.length from by omega] + rw [VExpr.liftN_appN, VExpr.liftN_appN, List.map_append, List.map_append, + bvarRevRange_liftN_ge _ _ _ _ (by omega), + VExpr.bvarRevRange_liftN_high _ _ _ _ (by omega), + bvarRevRange_liftN_ge _ _ _ _ (Nat.zero_le _), + bvarRevRange_liftN_ge _ _ _ _ (Nat.zero_le _), + VExpr.bvarRevRange_congr np (show ty.ctors.length + + (rs.length + (ctorFieldsR U np c).length + 1) = + rs.length + ((ctorFieldsR U np c).length + ty.ctors.length + 1) from by + omega), + VExpr.bvarRevRange_congr _ (show rs.length = rs.length + 0 from by omega)] + rfl + rw [hD] at hfields + have hres := hasType_appN_ruleIHs (env := env) (U := U+1) + (m := (ctorFieldsR U np c).length) (k := ty.ctors.length) + (rs := rs) + (argOf := fun r => r.ruleCall (ctorFieldsR U np c).length ty.ctors.length + (VExpr.appN (.const (.str T "rec") (VLevel.params (U+1))) + (VExpr.bvarRevRange (ctorFieldsR U np c).length + (np + ty.ctors.length + 1)))) + (fun r hr => S.ruleCallRec_hasType hc hrec (hrs ▸ hr)) hfields + simpa only [hrs] using hres + +/-- The constructor-headed left side of an indexed iota rule. -/ +theorem Stage3Env.recRuleApp_hasType {c : VConstVal} (hc : c ∈ ty.ctors) + (hrec : env.constants (.str T "rec") = some (recConst U T np ty)) : + env.HasType (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN + (VExpr.appN (.const (.str T "rec") (VLevel.params (U+1))) + (VExpr.bvarRevRange (ctorFieldsR U np c).length + (np + ty.ctors.length + 1))) + (((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (ctorFieldsR U np c).length)])) + (VExpr.appN (.bvar (ty.ctors.length + (ctorFieldsR U np c).length)) + (((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (ctorFieldsR U np c).length)])) := by + have hSp0 := S.result_transport hc + ((minorTypes U T np ty ty.ctors).reverse ++ [motiveType U T np ty]) + (g := ty.ctors.length + 1) (by simp [minorTypes_length]) [] (d := 0) rfl + have hSp : env.SpineWF (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.forallN (VExpr.liftTelN + ((VExpr.liftTelN (ty.ctors.length + 1) + (ctorFieldsR U np c) 0).reverse.length + ty.ctors.length + 1) + (idxTel U np ty) 0) (.sort (l.inst (VLevel.params' U 1)))) + ((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) + (.sort (l.inst (VLevel.params' U 1))) := by + simpa [List.append_assoc, Nat.add_assoc, List.length_reverse, + VExpr.liftTelN_length] using hSp0 + have hidxLen : ((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length).length = + (idxTel U np ty).length := by + simp only [List.length_map, ctorIdxs_length, idxTel_length] + exact (stage3Ctor_eq (S.hs3 c hc)).2.1 + have ha0 := S.ctorAppRule_hasType hc + have ha : env.HasType (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypes U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (ctorFieldsR U np c).length)) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange + ((VExpr.liftTelN (ty.ctors.length + 1) + (ctorFieldsR U np c) 0).reverse.length + ty.ctors.length + 1) np ++ + (ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length)) := by + simpa only [List.length_reverse, VExpr.liftTelN_length] using ha0 + have hr := S.recApp_hasType hrec + (VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse + hSp hidxLen ha + rw [List.length_reverse, VExpr.liftTelN_length, + show (ctorFieldsR U np c).length + ty.ctors.length = + ty.ctors.length + (ctorFieldsR U np c).length from by omega, + show ty.ctors.length + (ctorFieldsR U np c).length + 1 = + (ctorFieldsR U np c).length + ty.ctors.length + 1 from by omega] at hr + exact hr + +/-- The constructor-headed left side of a generalized indexed iota rule. -/ +theorem Stage3Env.recRuleAppRec_hasType {c : VConstVal} (hc : c ∈ ty.ctors) + (hrec : env.constants (.str T "rec") = some (recConstRec U T np ty)) : + env.HasType (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN + (VExpr.appN (.const (.str T "rec") (VLevel.params (U+1))) + (VExpr.bvarRevRange (ctorFieldsR U np c).length + (np + ty.ctors.length + 1))) + (((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (ctorFieldsR U np c).length)])) + (VExpr.appN (.bvar (ty.ctors.length + (ctorFieldsR U np c).length)) + (((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) ++ + [VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (ctorFieldsR U np c).length)])) := by + have hSp0 := S.result_transport hc + ((minorTypesRec U T np ty ty.ctors).reverse ++ [motiveType U T np ty]) + (g := ty.ctors.length + 1) (by simp [minorTypesRec_length]) [] (d := 0) rfl + have hSp : env.SpineWF (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.forallN (VExpr.liftTelN + ((VExpr.liftTelN (ty.ctors.length + 1) + (ctorFieldsR U np c) 0).reverse.length + ty.ctors.length + 1) + (idxTel U np ty) 0) (.sort (l.inst (VLevel.params' U 1)))) + ((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length) + (.sort (l.inst (VLevel.params' U 1))) := by + simpa [List.append_assoc, Nat.add_assoc, List.length_reverse, + VExpr.liftTelN_length] using hSp0 + have hidxLen : ((ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length).length = + (idxTel U np ty).length := by + simp only [List.length_map, ctorIdxs_length, idxTel_length] + exact (stage3Ctor_eq (S.hs3 c hc)).2.1 + have ha0 := S.ctorAppRuleRec_hasType hc + have ha : env.HasType (U+1) + ((VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse ++ + ((minorTypesRec U T np ty ty.ctors).reverse ++ + (motiveType U T np ty :: (paramsTel U np ty).reverse))) + (VExpr.appN (.const c.name (VLevel.params' U 1)) + (VExpr.bvarRevRange ((ctorFieldsR U np c).length + ty.ctors.length + 1) np ++ + VExpr.bvarRevRange 0 (ctorFieldsR U np c).length)) + (VExpr.appN (.const T (VLevel.params' U 1)) + (VExpr.bvarRevRange + ((VExpr.liftTelN (ty.ctors.length + 1) + (ctorFieldsR U np c) 0).reverse.length + ty.ctors.length + 1) np ++ + (ctorIdxs U np c).map fun e => + e.liftN (ty.ctors.length + 1) (ctorFieldsR U np c).length)) := by + simpa only [List.length_reverse, VExpr.liftTelN_length] using ha0 + have hr := S.recAppRec_hasType hrec + (VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0).reverse + hSp hidxLen ha + rw [List.length_reverse, VExpr.liftTelN_length, + show (ctorFieldsR U np c).length + ty.ctors.length = + ty.ctors.length + (ctorFieldsR U np c).length from by omega, + show ty.ctors.length + (ctorFieldsR U np c).length + 1 = + (ctorFieldsR U np c).length + ty.ctors.length + 1 from by omega] at hr + exact hr + +/-- Well-formedness of the iota rule for the `i`-th constructor. -/ +theorem Stage3Env.rule_WF {i : Nat} {c : VConstVal} + (hci : ty.ctors[i]? = some c) + (hrec : env.constants (.str T "rec") = some (recConst U T np ty)) : + (rule U T np ty i c).WF env := by + have hc := List.mem_of_getElem? hci + refine ⟨?_, ?_⟩ + · show env.HasType (U+1) [] (VExpr.lamN + (paramsTel U np ty ++ motiveType U T np ty :: minorTypes U T np ty ty.ctors ++ + VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0) _) + (VExpr.forallN _ _) + refine HasType.lamN (S.ruleBinders_onTel hc) ?_ + simp only [List.reverse_append, List.reverse_cons, List.append_nil, + List.append_assoc, List.singleton_append] + exact S.recRuleApp_hasType hc hrec + · show env.HasType (U+1) [] (VExpr.lamN + (paramsTel U np ty ++ motiveType U T np ty :: minorTypes U T np ty ty.ctors ++ + VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0) _) + (VExpr.forallN _ _) + refine HasType.lamN (S.ruleBinders_onTel hc) ?_ + simp only [List.reverse_append, List.reverse_cons, List.append_nil, + List.append_assoc, List.singleton_append] + exact S.minorApp_hasType hci hrec + +/-- Well-formedness of the generalized iota rule for the `i`-th constructor. -/ +theorem Stage3Env.ruleRec_WF {i : Nat} {c : VConstVal} + (hci : ty.ctors[i]? = some c) + (hrec : env.constants (.str T "rec") = some (recConstRec U T np ty)) : + (ruleRec U T np ty i c).WF env := by + have hc := List.mem_of_getElem? hci + refine ⟨?_, ?_⟩ + · show env.HasType (U+1) [] (VExpr.lamN + (paramsTel U np ty ++ motiveType U T np ty :: minorTypesRec U T np ty ty.ctors ++ + VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0) _) + (VExpr.forallN _ _) + refine HasType.lamN (S.ruleBindersRec_onTel hc) ?_ + simp only [List.reverse_append, List.reverse_cons, List.append_nil, + List.append_assoc, List.singleton_append] + exact S.recRuleAppRec_hasType hc hrec + · show env.HasType (U+1) [] (VExpr.lamN + (paramsTel U np ty ++ motiveType U T np ty :: minorTypesRec U T np ty ty.ctors ++ + VExpr.liftTelN (ty.ctors.length + 1) (ctorFieldsR U np c) 0) _) + (VExpr.forallN _ _) + refine HasType.lamN (S.ruleBindersRec_onTel hc) ?_ + simp only [List.reverse_append, List.reverse_cons, List.append_nil, + List.append_assoc, List.singleton_append] + exact S.minorAppRec_hasType hci hrec + +/-- `ctorType_eq` from the per-constructor facts directly (for use before +the constructor is in the environment). -/ +theorem Stage3Env.ctorType_eq' {c : VConstVal} + (htelc : VExpr.telN np c.type = VExpr.telN np ty.type) + (hs3c : stage3Ctor U T np + (ctorFields (VExpr.dropN np ty.type)).length 0 (VExpr.dropN np c.type)) : + c.type = VExpr.forallN (VExpr.telN np ty.type) + (VExpr.forallN (ctorFields (VExpr.dropN np c.type)) + (VExpr.appN (.const T (VLevel.params U)) + (VExpr.bvarRevRange (0 + (ctorFields (VExpr.dropN np c.type)).length) np ++ + recFieldIdxs np (VExpr.resultOf (VExpr.dropN np c.type))))) := by + conv => lhs; rw [← VExpr.forallN_telN_dropN np c.type, htelc, + (stage3Ctor_eq hs3c).1] + +/-- `ctorType_isType` from the per-constructor facts directly. -/ +theorem Stage3Env.ctorType_isType' {c : VConstVal} + (htelc : VExpr.telN np c.type = VExpr.telN np ty.type) + (hs3c : stage3Ctor U T np + (ctorFields (VExpr.dropN np ty.type)).length 0 (VExpr.dropN np c.type)) + (hfc : fieldsWF U T np env l (ctorFields (VExpr.dropN np ty.type)) + (VExpr.telN np ty.type).reverse 0 (ctorFields (VExpr.dropN np c.type))) + (hresultc : env.SpineWF U + ((ctorFields (VExpr.dropN np c.type)).reverse ++ + (VExpr.telN np ty.type).reverse) + (VExpr.forallN (VExpr.liftTelN + (ctorFields (VExpr.dropN np c.type)).length + (ctorFields (VExpr.dropN np ty.type)) 0) (.sort l)) + (recFieldIdxs np (VExpr.resultOf (VExpr.dropN np c.type))) (.sort l)) : + env.IsType U [] c.type := by + rw [S.ctorType_eq' htelc hs3c] + refine IsType.forallN S.hparams.of_append.1 ?_ + simp only [List.append_nil] + refine IsType.forallN + (S.fieldsWF_onTel_decl _ [] 0 rfl (by simpa using hfc)) ?_ + have hTapp := S.recAppPi_hasType_decl + ((ctorFields (VExpr.dropN np c.type)).reverse) + rw [List.length_reverse, VExpr.liftN_forallN] at hTapp + have hres := hresultc.hasType_appN hTapp + exact ⟨_, by + rw [VExpr.appN_append, Nat.zero_add] + exact hres⟩ + +omit S in +/-- In the identity path, each paired constructor receives the granular +raw/view certificate required immediately after the family insertion. -/ +theorem Checked.WF.identityCtorWF + {source : VInductDecl} {checked : source.Checked} + {pre envT : VEnv} (hpre : pre.Ordered) + (h : checked.WF pre) + (hadd : pre.addConst checked.type.name + checked.type.toVConstant = some envT) + {ctor : NormalizedCtor} + (hctor : ctor ∈ + checked.identityGeneration.block.ctorPairs) : + ctor.WF checked.identityGeneration.block envT := by + obtain ⟨c, hc, rfl⟩ := + checked.identityGeneration_ctor hctor + let S := h.toDirectFamilyEnv hpre hadd + have hcAn := checked.direct_anatomy.2.2.2.2.2 c hc + have hfields : fieldsWF source.uvars checked.type.name + source.nparams envT checked.resultLevel + (ctorFields (VExpr.dropN source.nparams checked.type.type)) + (VExpr.telN source.nparams checked.type.type).reverse 0 + (ctorFields (VExpr.dropN source.nparams c.type)) := by + simpa [checked.params_eq, checked.indices_eq] using + fieldsWF_mono (addConst_le hadd) (h.2 c hc).1 + have hfieldTel : OnTel envT source.uvars + (VExpr.telN source.nparams checked.type.type).reverse + (ctorFields (VExpr.dropN source.nparams c.type)) := + S.fieldsWF_onTel_decl _ [] 0 rfl hfields + have hbinders : OnTel envT source.uvars [] + (VExpr.telN source.nparams checked.type.type ++ + ctorFields (VExpr.dropN source.nparams c.type)) := + S.hparams.of_append.1.append (by simpa using hfieldTel) + have htelRefl := hbinders.telDefEq_refl + have hresultSpine : envT.SpineWF source.uvars + ((ctorFields + (VExpr.dropN source.nparams c.type)).reverse ++ + (VExpr.telN source.nparams checked.type.type).reverse) + (VExpr.forallN + (VExpr.liftTelN + (ctorFields + (VExpr.dropN source.nparams c.type)).length + (ctorFields + (VExpr.dropN source.nparams checked.type.type)) 0) + (.sort checked.resultLevel)) + (recFieldIdxs source.nparams + (VExpr.resultOf + (VExpr.dropN source.nparams c.type))) + (.sort checked.resultLevel) := by + simpa [checked.params_eq, checked.indices_eq] using + (h.2 c hc).2.mono (addConst_le hadd) + have hresultTyped := + S.ctorResult_hasType_decl hresultSpine + have hrawResult : + VExpr.resultOf (VExpr.dropN source.nparams c.type) = + VExpr.appN + (.const checked.type.name + (VLevel.params source.uvars)) + (VExpr.bvarRevRange + (ctorFields + (VExpr.dropN source.nparams c.type)).length + source.nparams ++ + recFieldIdxs source.nparams + (VExpr.resultOf + (VExpr.dropN source.nparams c.type))) := by + have hout := congrArg VExpr.resultOf + (stage3Ctor_eq hcAn.2.2).1 + rw [VExpr.resultOf_forallN, + VExpr.resultOf_appN_const] at hout + simpa only [Nat.zero_add] using hout + have hresultDF : envT.IsDefEq source.uvars + ((ctorFields + (VExpr.dropN source.nparams c.type)).reverse ++ + (VExpr.telN source.nparams checked.type.type).reverse) + (VExpr.resultOf (VExpr.dropN source.nparams c.type)) + (VExpr.appN + (.const checked.type.name + (VLevel.params source.uvars)) + (VExpr.bvarRevRange + (ctorFields + (VExpr.dropN source.nparams c.type)).length + source.nparams ++ + recFieldIdxs source.nparams + (VExpr.resultOf + (VExpr.dropN source.nparams c.type)))) + (.sort checked.resultLevel) := by + exact Eq.mpr + (congrArg + (fun e => envT.IsDefEq source.uvars + ((ctorFields + (VExpr.dropN source.nparams c.type)).reverse ++ + (VExpr.telN source.nparams + checked.type.type).reverse) + e + (VExpr.appN + (.const checked.type.name + (VLevel.params source.uvars)) + (VExpr.bvarRevRange + (ctorFields + (VExpr.dropN source.nparams c.type)).length + source.nparams ++ + recFieldIdxs source.nparams + (VExpr.resultOf + (VExpr.dropN source.nparams c.type)))) + (.sort checked.resultLevel)) + hrawResult) + hresultTyped + constructor + · simpa [NormalizedCtor.declaredBinders, + NormalizedCtor.rawFields, + NormalizedCtor.viewBinders, CheckedCtor.ofDirect, + checked.params_eq, hcAn.2.1] using htelRefl + · simpa [NormalizedCtor.declaredBinders, + NormalizedCtor.rawFields, NormalizedCtor.rawResult, + NormalizedCtor.resultTarget, CheckedCtor.ofDirect, + checked.params_eq, hcAn.2.1] using hresultDF + · simpa [NormalizedCtor.emittedBinders, + NormalizedCtor.rawFields, + NormalizedCtor.viewBinders, CheckedCtor.ofDirect, + Checked.identityGeneration, Checked.identityBlock, + NormalizedChecked.rawParams, checked.params_eq] using htelRefl + · simpa [NormalizedCtor.emittedBinders, + NormalizedCtor.rawFields, NormalizedCtor.rawResult, + NormalizedCtor.resultTarget, CheckedCtor.ofDirect, + Checked.identityGeneration, Checked.identityBlock, + NormalizedChecked.rawParams, checked.params_eq] using hresultDF + +omit S in +/-- Every semantically checked direct declaration admits the identity mixed +generation certificate. This is the public bridge from the legacy checker +contract to the normalized transaction. -/ +theorem Checked.WF.identityGeneration + {source : VInductDecl} {checked : source.Checked} + {env : VEnv} (h : checked.WF env) (henv : env.Ordered) : + checked.identityGeneration.WF env := by + have hfamily := h.family_isType + have hnorm : checked.identityGeneration.block.normalization.WF env := by + refine ⟨checked.type, checked.type, checked.types_eq, ?_, ?_, ?_⟩ + · simpa only [Checked.identityGeneration, Checked.identityBlock, + Normalization.identity] using checked.types_eq + · obtain ⟨u, hu⟩ := hfamily + exact ⟨.sort u, hu⟩ + · intro envT hadd + have hadd' : env.addConst checked.type.name + checked.type.toVConstant = some envT := by + simpa only [Checked.identityGeneration, + Checked.identityBlock] using hadd + have hall : ∀ c ∈ checked.type.ctors, + envT.IsDefEqU source.uvars [] c.type c.type := by + intro c hc + have hpair : + (⟨c, CheckedCtor.ofDirect source.uvars + checked.type.name source.nparams + checked.indices.length c⟩ : NormalizedCtor) ∈ + checked.identityGeneration.block.ctorPairs := by + simpa only [Checked.identityGeneration, + Checked.identityBlock, NormalizedChecked.ctorPairs, + checked.constructors_eq] using + (pairNormalizedCtors_map_self_contains hc : + (⟨c, CheckedCtor.ofDirect source.uvars + checked.type.name source.nparams + checked.indices.length c⟩ : + NormalizedCtor) ∈ + pairNormalizedCtors checked.type.ctors + (checked.type.ctors.map + (CheckedCtor.ofDirect source.uvars + checked.type.name source.nparams + checked.indices.length))) + obtain ⟨u, hu⟩ := + (h.identityCtorWF henv hadd' hpair).rawDeclared_isType + exact ⟨.sort u, hu⟩ + have hrel : List.Forall₂ + (fun c c' => envT.IsDefEqU source.uvars [] + c.type c'.type) + checked.type.ctors checked.type.ctors := by + let R : VConstVal → VConstVal → Prop := + fun c c' => envT.IsDefEqU source.uvars [] + c.type c'.type + let rec diagonal : + ∀ cs : List VConstVal, + (∀ c ∈ cs, R c c) → + List.Forall₂ R cs cs + | [], _ => .nil + | c :: cs, hs => + .cons (hs c (.head _)) + (diagonal cs fun c hc => hs c (.tail _ hc)) + exact diagonal checked.type.ctors hall + simpa only [Checked.identityGeneration, + Checked.identityBlock, Normalization.identity] using hrel + refine { + blockWF := ⟨hnorm, h⟩ + familyTel := ?_ + familyResult := ?_ + ctors := ?_ + } + · simpa only [Checked.identityGeneration, + Checked.identityBlock, NormalizedChecked.rawParams, + NormalizedChecked.rawIndices, checked.params_eq, + checked.indices_eq] using h.1.telDefEq_refl + · simpa only [Checked.identityGeneration, + Checked.identityBlock, NormalizedChecked.rawParams, + NormalizedChecked.rawIndices, NormalizedChecked.rawResult, + checked.params_eq, checked.indices_eq, + checked.result_eq] using + (VEnv.IsDefEq.sortDF + checked.direct_anatomy.2.2.1 + checked.direct_anatomy.2.2.1 rfl : + env.IsDefEq source.uvars + (checked.params ++ checked.indices).reverse + (.sort checked.resultLevel) + (.sort checked.resultLevel) + (.sort (.succ checked.resultLevel))) + · intro envT hadd ctor hctor + have hadd' : env.addConst checked.type.name + checked.type.toVConstant = some envT := by + simpa only [Checked.identityGeneration, + Checked.identityBlock] using hadd + exact h.identityCtorWF henv hadd' hctor + +end VInductDecl + +/-! ## `addInduct_WF` -/ + +theorem _root_.List.mem_zipIdx_getElem? {α} : ∀ {l : List α} {n : Nat} {a : α} {i : Nat}, + (a, i) ∈ l.zipIdx n → n ≤ i ∧ l[i - n]? = some a + | b :: l, n, a, i, h => by + simp only [List.zipIdx] at h + rcases List.mem_cons.1 h with h | h + · obtain ⟨rfl, rfl⟩ := Prod.mk.injEq .. ▸ h + simp + · obtain ⟨h1, h2⟩ := List.mem_zipIdx_getElem? h + refine ⟨by omega, ?_⟩ + rw [show i - n = (i - (n+1)) + 1 from by omega] + simpa using h2 + +namespace VInductDecl + +theorem rulesFold_WF : ∀ (dfs : List VDefEq) {env₃ : VEnv}, + env₃.Ordered → (∀ df ∈ dfs, df.WF env₃) → + (dfs.foldl VEnv.addDefEq env₃).Ordered + | [], _, ord, _ => ord + | df :: dfs, env₃, ord, hdfs => by + rw [List.foldl_cons] + exact rulesFold_WF dfs (.defeq ord (hdfs df (.head _))) + (fun df' hdf' => (hdfs df' (.tail _ hdf')).mono VEnv.addDefEq_le) + +/-- Every rule emitted from the paired constructor list is well formed in the +environment containing the mixed recursor. -/ +theorem GenerationEnv.generatedRules_WF + {source : VInductDecl} + {gen : GenerationChecked source} {env : VEnv} + (S : GenerationEnv gen env) + (hrec : env.constants + (.str gen.block.sourceType.name "rec") = + some gen.recursor) : + ∀ df ∈ gen.generatedRules, df.WF env := by + intro df hdf + change df ∈ + gen.block.ctorPairs.zipIdx.map + (fun (ctor, i) => gen.rule i ctor) at hdf + obtain ⟨⟨ctor, i⟩, hmem, rfl⟩ := + List.mem_map.1 hdf + obtain ⟨-, hci⟩ := + List.mem_zipIdx_getElem? hmem + rw [Nat.sub_zero] at hci + exact S.rule_WF hci hrec + +/-- Folding all mixed generated rules preserves environment ordering. -/ +theorem GenerationEnv.generatedRulesFold_ordered + {source : VInductDecl} + {gen : GenerationChecked source} {env : VEnv} + (S : GenerationEnv gen env) + (hrec : env.constants + (.str gen.block.sourceType.name "rec") = + some gen.recursor) : + (gen.generatedRules.foldl + VEnv.addDefEq env).Ordered := + rulesFold_WF gen.generatedRules S.ord + (S.generatedRules_WF hrec) + +/-- Every rule emitted from the flattened mutual constructor list is well +formed in an environment containing every generated family recursor. -/ +theorem BlockGenerationEnv.generatedRules_WF + {source : VInductDecl} + {gen : BlockGenerationChecked source} {env : VEnv} + (S : BlockGenerationEnv gen env) + (hrecs : ∀ family ∈ gen.families, + env.constants (.str family.raw.name "rec") = + some (gen.recursor family)) : + ∀ df ∈ gen.generatedRules, df.WF env := by + intro df hdf + change df ∈ + gen.flatCtors.zipIdx.map + (fun (constructor, i) => gen.rule i constructor) at hdf + obtain ⟨⟨constructor, i⟩, hmem, rfl⟩ := List.mem_map.1 hdf + obtain ⟨-, hci⟩ := List.mem_zipIdx_getElem? hmem + rw [Nat.sub_zero] at hci + exact S.rule_WF hci hrecs + +/-- Folding every generated mutual rule preserves environment ordering. -/ +theorem BlockGenerationEnv.generatedRulesFold_ordered + {source : VInductDecl} + {gen : BlockGenerationChecked source} {env : VEnv} + (S : BlockGenerationEnv gen env) + (hrecs : ∀ family ∈ gen.families, + env.constants (.str family.raw.name "rec") = + some (gen.recursor family)) : + (gen.generatedRules.foldl VEnv.addDefEq env).Ordered := + rulesFold_WF gen.generatedRules S.ord + (S.generatedRules_WF hrecs) + +/-- Folding definitional equations only grows the environment and registers +every equation in the input list. -/ +theorem rulesFold_spec : ∀ (dfs : List VDefEq) (env : VEnv), + env ≤ dfs.foldl VEnv.addDefEq env ∧ + ∀ df ∈ dfs, (dfs.foldl VEnv.addDefEq env).defeqs df + | [], _ => ⟨.rfl, nofun⟩ + | df :: dfs, env => by + rw [List.foldl_cons] + obtain ⟨hle, hmem⟩ := rulesFold_spec dfs (env.addDefEq df) + refine ⟨VEnv.addDefEq_le.trans hle, fun df' hdf' => ?_⟩ + rcases List.mem_cons.1 hdf' with rfl | hdf' + · exact hle.defeqs VEnv.addDefEq_self + · exact hmem df' hdf' + +/-- A successful constructor fold grows the environment, registers every +constructor, and certifies that every constructor name was fresh in the +fold's input environment. -/ +theorem ctorFold_spec : ∀ (cs : List VConstVal) {env₀ env₁ : VEnv}, + List.foldlM (fun env (c : VConstVal) => env.addConst c.name c.toVConstant) env₀ cs = + some env₁ → + env₀ ≤ env₁ ∧ + (∀ c ∈ cs, env₁.constants c.name = some c.toVConstant) ∧ + ∀ c ∈ cs, env₀.constants c.name = none + | [], _, _, hfold => by + cases hfold + exact ⟨.rfl, nofun, nofun⟩ + | c :: cs, env₀, env₁, hfold => by + rw [List.foldlM_cons] at hfold + obtain ⟨env₀', hadd, hrest⟩ := Option.bind_eq_some_iff.1 hfold + obtain ⟨hle, hlook, hfresh⟩ := ctorFold_spec cs hrest + have haddLe := VEnv.addConst_le hadd + refine ⟨haddLe.trans hle, ?_, ?_⟩ + · intro c' hc' + rcases List.mem_cons.1 hc' with rfl | hc' + · exact hle.constants (VEnv.addConst_self hadd) + · exact hlook c' hc' + · intro c' hc' + rcases List.mem_cons.1 hc' with rfl | hc' + · exact VEnv.addConst_fresh hadd + · exact haddLe.constants_none (hfresh c' hc') + +/-- Sequentially inserting constants that are all well formed in the fold's +initial environment preserves ordering. Monotonicity transports the remaining +constant certificates after each insertion. -/ +theorem constFold_ordered : ∀ (cs : List VConstVal) {env₀ env₁ : VEnv}, + env₀.Ordered → + (∀ c ∈ cs, c.toVConstant.WF env₀) → + List.foldlM + (fun env (c : VConstVal) => env.addConst c.name c.toVConstant) + env₀ cs = some env₁ → + env₁.Ordered + | [], _, _, ord, _, hfold => by + cases hfold + exact ord + | c :: cs, env₀, env₁, ord, hwf, hfold => by + rw [List.foldlM_cons] at hfold + obtain ⟨env₀', hadd, hrest⟩ := Option.bind_eq_some_iff.1 hfold + have hle := VEnv.addConst_le hadd + have ord' : env₀'.Ordered := + .const ord (hwf c (.head _)) hadd + exact constFold_ordered cs ord' + (fun c' hc' => (hwf c' (.tail _ hc')).mono hle) hrest + +/-- Adding the constructors of a stage-3 block preserves order and records +their lookups. -/ +theorem ctorFold_WF {U : Nat} {T : Name} {np : Nat} {l : VLevel} {ty : VInductiveType} + (hsort : VExpr.resultOf (VExpr.dropN np ty.type) = .sort l) + (hlen : (VExpr.telN np ty.type).length = np) (hl : l.WF U) : + ∀ (cs' : List VConstVal) {env₀ env₁ : VEnv}, + env₀.Ordered → env₀.constants T = some ⟨U, ty.type⟩ → + VEnv.OnTel env₀ U [] + (VExpr.telN np ty.type ++ ctorFields (VExpr.dropN np ty.type)) → + (∀ c ∈ cs', c.uvars = U ∧ VExpr.telN np c.type = VExpr.telN np ty.type ∧ + stage3Ctor U T np (ctorFields (VExpr.dropN np ty.type)).length 0 + (VExpr.dropN np c.type) ∧ + fieldsWF U T np env₀ l (ctorFields (VExpr.dropN np ty.type)) + (VExpr.telN np ty.type).reverse 0 (ctorFields (VExpr.dropN np c.type)) ∧ + env₀.SpineWF U + ((ctorFields (VExpr.dropN np c.type)).reverse ++ + (VExpr.telN np ty.type).reverse) + (VExpr.forallN (VExpr.liftTelN + (ctorFields (VExpr.dropN np c.type)).length + (ctorFields (VExpr.dropN np ty.type)) 0) (.sort l)) + (recFieldIdxs np (VExpr.resultOf (VExpr.dropN np c.type))) (.sort l)) → + List.foldlM (fun env (c : VConstVal) => env.addConst c.name c.toVConstant) env₀ cs' = + some env₁ → + env₁.Ordered ∧ env₀ ≤ env₁ ∧ + ∀ c ∈ cs', env₁.constants c.name = some ⟨U, c.type⟩ + | [], env₀, env₁, ord, _, _, _, hfold => by + cases hfold + exact ⟨ord, .rfl, nofun⟩ + | c :: cs', env₀, env₁, ord, hT, hpar, hcs, hfold => by + rw [List.foldlM_cons] at hfold + obtain ⟨env₀', hadd, hrest⟩ := Option.bind_eq_some_iff.1 hfold + obtain ⟨hcU, htelc, hs3c, hfc, hresultc⟩ := hcs c (.head _) + have S₀ : Stage3Env env₀ U T np l ⟨⟨⟨ty.uvars, ty.type⟩, ty.name⟩, []⟩ := + ⟨ord, hl, hsort, hlen, hT, nofun, nofun, nofun, hpar, nofun, nofun⟩ + have hwfc : c.toVConstant.WF env₀ := by + show env₀.IsType c.toVConstant.uvars [] c.toVConstant.type + rw [show c.toVConstant.uvars = c.uvars from rfl, hcU] + exact S₀.ctorType_isType' htelc hs3c hfc hresultc + have ord' : env₀'.Ordered := .const ord hwfc hadd + have hle' := VEnv.addConst_le hadd + obtain ⟨ord₁, hle₁, hlook⟩ := ctorFold_WF hsort hlen hl cs' ord' + (hle'.constants hT) (hpar.mono hle') + (fun c' hc' => by + obtain ⟨h1, h2, h3, h4, h5⟩ := hcs c' (.tail _ hc') + exact ⟨h1, h2, h3, fieldsWF_mono hle' h4, h5.mono hle'⟩) + hrest + refine ⟨ord₁, hle'.trans hle₁, fun c' hc' => ?_⟩ + rcases List.mem_cons.1 hc' with rfl | hc' + · have hself := VEnv.addConst_self hadd + rw [show c'.toVConstant = ⟨U, c'.type⟩ from by rw [← hcU]] at hself + exact hle₁.constants hself + · exact hlook c' hc' + +end VInductDecl + +namespace VEnv +open VInductDecl + +/-- Recover the exact intermediate environments from a successful normalized +generation transaction. The data-bearing trace is wrapped in `Nonempty` so +proof consumers can eliminate it without adding a choice axiom merely to +recover bookkeeping states. -/ +theorem addInductGeneration_trace {source : VInductDecl} + {gen : source.GenerationChecked} + (hadd : addInductGeneration env gen = some env') : + Nonempty (AddInductGenerationTrace env env' gen) := by + unfold addInductGeneration at hadd + obtain ⟨typeEnv, addType, hadd⟩ := Option.bind_eq_some_iff.1 hadd + obtain ⟨ctorEnv, addCtors, hadd⟩ := Option.bind_eq_some_iff.1 hadd + obtain ⟨recEnv, addRec, hadd⟩ := Option.bind_eq_some_iff.1 hadd + cases hadd + exact ⟨⟨typeEnv, ctorEnv, recEnv, addType, addCtors, addRec, rfl⟩⟩ + +/-- The normalized core is atomic: failure returns no observable intermediate +environment, while success exposes one complete trace. -/ +theorem addInductGeneration_atomic {source : VInductDecl} + (env : VEnv) (gen : source.GenerationChecked) : + addInductGeneration env gen = none ∨ + ∃ env', addInductGeneration env gen = some env' ∧ + Nonempty (AddInductGenerationTrace env env' gen) := by + cases hadd : addInductGeneration env gen with + | none => exact .inl rfl + | some env' => + exact .inr ⟨env', rfl, addInductGeneration_trace hadd⟩ + +/-- A successful normalized generation transaction only grows its input +environment. -/ +theorem AddInductGenerationTrace.le {source : VInductDecl} + {gen : source.GenerationChecked} + (H : AddInductGenerationTrace env env' gen) : env ≤ env' := by + have htype := addConst_le H.addType + have hctors := (ctorFold_spec gen.block.sourceType.ctors H.addCtors).1 + have hrec := addConst_le H.addRec + have hrules : H.recEnv ≤ env' := by + simpa only [H.addRules] using + (rulesFold_spec gen.generatedRules H.recEnv).1 + exact htype.trans (hctors.trans (hrec.trans hrules)) + +/-- The raw family name was fresh before a successful normalized +transaction. -/ +theorem AddInductGenerationTrace.family_fresh {source : VInductDecl} + {gen : source.GenerationChecked} + (H : AddInductGenerationTrace env env' gen) : + env.constants gen.block.sourceType.name = none := + addConst_fresh H.addType + +/-- The final environment contains the exact raw family constant. -/ +theorem AddInductGenerationTrace.family_lookup {source : VInductDecl} + {gen : source.GenerationChecked} + (H : AddInductGenerationTrace env env' gen) : + env'.constants gen.block.sourceType.name = + some gen.block.sourceType.toVConstant := by + have hctors := (ctorFold_spec gen.block.sourceType.ctors H.addCtors).1 + have hrec := addConst_le H.addRec + have hrules : H.recEnv ≤ env' := by + simpa only [H.addRules] using + (rulesFold_spec gen.generatedRules H.recEnv).1 + exact (hctors.trans (hrec.trans hrules)).constants + (addConst_self H.addType) + +/-- Every raw constructor name was fresh in the transaction's input +environment. -/ +theorem AddInductGenerationTrace.ctor_fresh {source : VInductDecl} + {gen : source.GenerationChecked} + (H : AddInductGenerationTrace env env' gen) + {ctor : VConstVal} (hctor : ctor ∈ gen.block.sourceType.ctors) : + env.constants ctor.name = none := by + have htype := addConst_le H.addType + have hfresh := + (ctorFold_spec gen.block.sourceType.ctors H.addCtors).2.2 ctor hctor + exact htype.constants_none hfresh + +/-- The final environment contains every exact raw constructor constant. -/ +theorem AddInductGenerationTrace.ctor_lookup {source : VInductDecl} + {gen : source.GenerationChecked} + (H : AddInductGenerationTrace env env' gen) + {ctor : VConstVal} (hctor : ctor ∈ gen.block.sourceType.ctors) : + env'.constants ctor.name = some ctor.toVConstant := by + have hlookup := + (ctorFold_spec gen.block.sourceType.ctors H.addCtors).2.1 ctor hctor + have hrec := addConst_le H.addRec + have hrules : H.recEnv ≤ env' := by + simpa only [H.addRules] using + (rulesFold_spec gen.generatedRules H.recEnv).1 + exact (hrec.trans hrules).constants hlookup + +/-- The generated recursor name was fresh before a successful normalized +transaction. -/ +theorem AddInductGenerationTrace.rec_fresh {source : VInductDecl} + {gen : source.GenerationChecked} + (H : AddInductGenerationTrace env env' gen) : + env.constants (.str gen.block.sourceType.name "rec") = none := by + have htype := addConst_le H.addType + have hctors := (ctorFold_spec gen.block.sourceType.ctors H.addCtors).1 + exact (htype.trans hctors).constants_none (addConst_fresh H.addRec) + +/-- The final environment contains the exact mixed generated recursor. -/ +theorem AddInductGenerationTrace.rec_lookup {source : VInductDecl} + {gen : source.GenerationChecked} + (H : AddInductGenerationTrace env env' gen) : + env'.constants (.str gen.block.sourceType.name "rec") = + some gen.recursor := by + have hrules : H.recEnv ≤ env' := by + simpa only [H.addRules] using + (rulesFold_spec gen.generatedRules H.recEnv).1 + exact hrules.constants (addConst_self H.addRec) + +/-- The final environment registers every mixed generated iota rule. -/ +theorem AddInductGenerationTrace.rule_mem {source : VInductDecl} + {gen : source.GenerationChecked} + (H : AddInductGenerationTrace env env' gen) + {df : VDefEq} (hdf : df ∈ gen.generatedRules) : + env'.defeqs df := by + simpa only [H.addRules] using + (rulesFold_spec gen.generatedRules H.recEnv).2 df hdf + +/-! ### Block-wide transaction facts -/ + +/-- Recover every phase boundary from a successful block-wide transaction. -/ +theorem addInductBlockGeneration_trace {source : VInductDecl} + {gen : source.BlockGenerationChecked} + (hadd : addInductBlockGeneration env gen = some env') : + Nonempty (AddInductBlockGenerationTrace env env' gen) := by + unfold addInductBlockGeneration at hadd + obtain ⟨typeEnv, addTypes, hadd⟩ := Option.bind_eq_some_iff.1 hadd + obtain ⟨ctorEnv, addCtors, hadd⟩ := Option.bind_eq_some_iff.1 hadd + obtain ⟨recEnv, addRecs, hadd⟩ := Option.bind_eq_some_iff.1 hadd + cases hadd + exact ⟨⟨typeEnv, ctorEnv, recEnv, addTypes, addCtors, addRecs, rfl⟩⟩ + +/-- The block-wide transaction is atomic at its public `Option` boundary. -/ +theorem addInductBlockGeneration_atomic {source : VInductDecl} + (env : VEnv) (gen : source.BlockGenerationChecked) : + addInductBlockGeneration env gen = none ∨ + ∃ env', addInductBlockGeneration env gen = some env' ∧ + Nonempty (AddInductBlockGenerationTrace env env' gen) := by + cases hadd : addInductBlockGeneration env gen with + | none => exact .inl rfl + | some env' => + exact .inr ⟨env', rfl, addInductBlockGeneration_trace hadd⟩ + +/-- Every phase of a successful block transaction only grows the Theory +environment. -/ +theorem AddInductBlockGenerationTrace.le {source : VInductDecl} + {gen : source.BlockGenerationChecked} + (H : AddInductBlockGenerationTrace env env' gen) : env ≤ env' := by + have htypes := + (ctorFold_spec source.blockTypeConstants H.addTypes).1 + have hctors := + (ctorFold_spec source.blockConstructorConstants H.addCtors).1 + have hrecs := (ctorFold_spec gen.recursors H.addRecs).1 + have hrules : H.recEnv ≤ env' := by + simpa only [H.addRules] using + (rulesFold_spec gen.generatedRules H.recEnv).1 + exact htypes.trans (hctors.trans (hrecs.trans hrules)) + +/-- Every source family name was fresh before a successful block +transaction. -/ +theorem AddInductBlockGenerationTrace.family_fresh + {source : VInductDecl} {gen : source.BlockGenerationChecked} + (H : AddInductBlockGenerationTrace env env' gen) + {type : VInductiveType} (htype : type ∈ source.types) : + env.constants type.name = none := by + have hmem : type.toVConstVal ∈ source.blockTypeConstants := by + exact List.mem_map.2 ⟨type, htype, rfl⟩ + simpa [VInductDecl.blockTypeConstants] using + (ctorFold_spec source.blockTypeConstants H.addTypes).2.2 + type.toVConstVal hmem + +/-- The final environment contains every exact raw family constant. -/ +theorem AddInductBlockGenerationTrace.family_lookup + {source : VInductDecl} {gen : source.BlockGenerationChecked} + (H : AddInductBlockGenerationTrace env env' gen) + {type : VInductiveType} (htype : type ∈ source.types) : + env'.constants type.name = some type.toVConstant := by + have hmem : type.toVConstVal ∈ source.blockTypeConstants := by + exact List.mem_map.2 ⟨type, htype, rfl⟩ + have hlookup := + (ctorFold_spec source.blockTypeConstants H.addTypes).2.1 + type.toVConstVal hmem + have hctors := + (ctorFold_spec source.blockConstructorConstants H.addCtors).1 + have hrecs := (ctorFold_spec gen.recursors H.addRecs).1 + have hrules : H.recEnv ≤ env' := by + simpa only [H.addRules] using + (rulesFold_spec gen.generatedRules H.recEnv).1 + exact (hctors.trans (hrecs.trans hrules)).constants hlookup + +/-- Every flattened raw constructor name was fresh in the transaction's +input environment. -/ +theorem AddInductBlockGenerationTrace.ctor_fresh + {source : VInductDecl} {gen : source.BlockGenerationChecked} + (H : AddInductBlockGenerationTrace env env' gen) + {constructor : VConstVal} + (hconstructor : constructor ∈ source.blockConstructorConstants) : + env.constants constructor.name = none := by + have htypes := + (ctorFold_spec source.blockTypeConstants H.addTypes).1 + have hfresh := + (ctorFold_spec source.blockConstructorConstants H.addCtors).2.2 + constructor hconstructor + exact htypes.constants_none hfresh + +/-- The final environment contains every exact raw constructor constant. -/ +theorem AddInductBlockGenerationTrace.ctor_lookup + {source : VInductDecl} {gen : source.BlockGenerationChecked} + (H : AddInductBlockGenerationTrace env env' gen) + {constructor : VConstVal} + (hconstructor : constructor ∈ source.blockConstructorConstants) : + env'.constants constructor.name = some constructor.toVConstant := by + have hlookup := + (ctorFold_spec source.blockConstructorConstants H.addCtors).2.1 + constructor hconstructor + have hrecs := (ctorFold_spec gen.recursors H.addRecs).1 + have hrules : H.recEnv ≤ env' := by + simpa only [H.addRules] using + (rulesFold_spec gen.generatedRules H.recEnv).1 + exact (hrecs.trans hrules).constants hlookup + +/-- Every generated recursor name was fresh before the block transaction. -/ +theorem AddInductBlockGenerationTrace.rec_fresh + {source : VInductDecl} {gen : source.BlockGenerationChecked} + (H : AddInductBlockGenerationTrace env env' gen) + {recursor : VConstVal} (hrecursor : recursor ∈ gen.recursors) : + env.constants recursor.name = none := by + have htypes := + (ctorFold_spec source.blockTypeConstants H.addTypes).1 + have hctors := + (ctorFold_spec source.blockConstructorConstants H.addCtors).1 + have hfresh := + (ctorFold_spec gen.recursors H.addRecs).2.2 recursor hrecursor + exact (htypes.trans hctors).constants_none hfresh + +/-- The final environment contains every generated recursor. -/ +theorem AddInductBlockGenerationTrace.rec_lookup + {source : VInductDecl} {gen : source.BlockGenerationChecked} + (H : AddInductBlockGenerationTrace env env' gen) + {recursor : VConstVal} (hrecursor : recursor ∈ gen.recursors) : + env'.constants recursor.name = some recursor.toVConstant := by + have hlookup := + (ctorFold_spec gen.recursors H.addRecs).2.1 recursor hrecursor + have hrules : H.recEnv ≤ env' := by + simpa only [H.addRules] using + (rulesFold_spec gen.generatedRules H.recEnv).1 + exact hrules.constants hlookup + +/-- The final environment registers every block-generated iota rule. -/ +theorem AddInductBlockGenerationTrace.rule_mem + {source : VInductDecl} {gen : source.BlockGenerationChecked} + (H : AddInductBlockGenerationTrace env env' gen) + {df : VDefEq} (hdf : df ∈ gen.generatedRules) : + env'.defeqs df := by + simpa only [H.addRules] using + (rulesFold_spec gen.generatedRules H.recEnv).2 df hdf + +/-- Preserve ordering through the all-families phase. -/ +private theorem addInductBlockGeneration_families_ordered + {source : VInductDecl} {gen : source.BlockGenerationChecked} + {blockEnv : VEnv} + (H : AddInductBlockGenerationTrace env env' gen) + (henv : env.Ordered) (hgen : gen.WF env blockEnv) : + H.typeEnv.Ordered := by + apply constFold_ordered source.blockTypeConstants henv ?_ H.addTypes + intro type htype + simp only [VInductDecl.blockTypeConstants, List.mem_map] at htype + obtain ⟨raw, hraw, rfl⟩ := htype + have hraw' : raw ∈ gen.families.map (·.raw) := by + rw [gen.families_map_raw] + exact hraw + obtain ⟨family, hfamily, rfl⟩ := List.mem_map.1 hraw' + show env.IsType family.raw.uvars [] family.raw.type + rw [gen.family_uvars hfamily] + exact hgen.rawFamily_isType hfamily + +/-- Preserve ordering through the globally flattened constructor phase. -/ +private theorem addInductBlockGeneration_constructors_ordered + {source : VInductDecl} {gen : source.BlockGenerationChecked} + {blockEnv : VEnv} + (H : AddInductBlockGenerationTrace env env' gen) + (hgen : gen.WF env blockEnv) (ordT : H.typeEnv.Ordered) : + H.ctorEnv.Ordered := by + have hstage : env.stageInductiveTypes source.types = some blockEnv := + hgen.blockWF.1.1 + rw [← blockTypeConstants_foldlM_eq_stageInductiveTypes env source, + H.addTypes] at hstage + have htypeEnv : H.typeEnv = blockEnv := Option.some.inj hstage + subst blockEnv + apply constFold_ordered source.blockConstructorConstants ordT ?_ H.addCtors + intro ctor hctor + have hctor' : ctor ∈ gen.flatCtors.map (·.ctor.raw) := by + rw [gen.flatCtors_map_raw] + exact hctor + obtain ⟨constructor, hconstructor, rfl⟩ := List.mem_map.1 hctor' + show H.typeEnv.IsType constructor.ctor.raw.uvars [] + constructor.ctor.raw.type + rw [gen.flatCtor_uvars hconstructor] + exact hgen.rawCtor_isType hconstructor + +/-- Assemble the mutual generation invariant once every raw family and every +globally flattened constructor has been inserted. -/ +private theorem addInductBlockGeneration_constructor_generationEnv + {source : VInductDecl} {gen : source.BlockGenerationChecked} + {blockEnv : VEnv} + (H : AddInductBlockGenerationTrace env env' gen) + (hgen : gen.WF env blockEnv) (ordC : H.ctorEnv.Ordered) : + BlockGenerationEnv gen H.ctorEnv := by + obtain ⟨hleET, hfamilyLookup, -⟩ := + ctorFold_spec source.blockTypeConstants H.addTypes + obtain ⟨hleTC, hctorLookup, -⟩ := + ctorFold_spec source.blockConstructorConstants H.addCtors + have hstage : env.stageInductiveTypes source.types = some blockEnv := + hgen.blockWF.1.1 + rw [← blockTypeConstants_foldlM_eq_stageInductiveTypes env source, + H.addTypes] at hstage + have htypeEnv : H.typeEnv = blockEnv := Option.some.inj hstage + rw [htypeEnv] at hleET hfamilyLookup hleTC + have hfamilies : ∀ family ∈ gen.families, + H.ctorEnv.constants family.raw.name = + some family.raw.toVConstant := by + intro family hfamily + apply hleTC.constants + apply hfamilyLookup family.raw.toVConstVal + simp only [VInductDecl.blockTypeConstants, List.mem_map] + refine ⟨family.raw, ?_, rfl⟩ + rw [← gen.families_map_raw] + exact List.mem_map.2 ⟨family, hfamily, rfl⟩ + have hctors : ∀ constructor ∈ gen.flatCtors, + H.ctorEnv.constants constructor.ctor.raw.name = + some constructor.ctor.raw.toVConstant := by + intro constructor hconstructor + apply hctorLookup constructor.ctor.raw + rw [← gen.flatCtors_map_raw] + exact List.mem_map.2 ⟨constructor, hconstructor, rfl⟩ + exact hgen.toBlockGenerationEnv (hleET.trans hleTC) hleTC ordC + hfamilies hctors + +/-- Preserve ordering while inserting the family-indexed recursor list. -/ +private theorem addInductBlockGeneration_recursors_ordered + {source : VInductDecl} {gen : source.BlockGenerationChecked} + (H : AddInductBlockGenerationTrace env env' gen) + (S : BlockGenerationEnv gen H.ctorEnv) : + H.recEnv.Ordered := by + apply constFold_ordered gen.recursors S.ord ?_ H.addRecs + intro recursor hrecursor + simp only [BlockGenerationChecked.recursors, List.mem_map] at hrecursor + obtain ⟨family, hfamily, rfl⟩ := hrecursor + exact S.recursor_wf hfamily + +/-- The recursor fold stores the exact generated recursor selected by every +family ordinal. -/ +private theorem addInductBlockGeneration_recursor_lookup + {source : VInductDecl} {gen : source.BlockGenerationChecked} + (H : AddInductBlockGenerationTrace env env' gen) + {family : NormalizedFamily} (hfamily : family ∈ gen.families) : + H.recEnv.constants (.str family.raw.name "rec") = + some (gen.recursor family) := by + let recursor : VConstVal := + ⟨gen.recursor family, .str family.raw.name "rec"⟩ + have hrecursor : recursor ∈ gen.recursors := + List.mem_map.2 ⟨family, hfamily, rfl⟩ + simpa [recursor] using + (ctorFold_spec gen.recursors H.addRecs).2.1 recursor hrecursor + +/-- Preserve ordering through the block-wide generated-rule fold. -/ +private theorem addInductBlockGeneration_rules_ordered + {source : VInductDecl} {gen : source.BlockGenerationChecked} + (H : AddInductBlockGenerationTrace env env' gen) + (S : BlockGenerationEnv gen H.recEnv) : env'.Ordered := by + have hout := S.generatedRulesFold_ordered + (fun family hfamily => + addInductBlockGeneration_recursor_lookup H hfamily) + simpa only [H.addRules] using hout + +/-- The block-wide transaction preserves ordering through all families, +constructors, family-indexed recursors, and flattened iota rules. -/ +theorem addInductBlockGeneration_WF + {source : VInductDecl} {gen : source.BlockGenerationChecked} + {blockEnv : VEnv} + (henv : env.Ordered) (hgen : gen.WF env blockEnv) + (hadd : addInductBlockGeneration env gen = some env') : + env'.Ordered := by + rcases addInductBlockGeneration_trace hadd with ⟨H⟩ + have ordT : H.typeEnv.Ordered := + addInductBlockGeneration_families_ordered H henv hgen + have ordC : H.ctorEnv.Ordered := + addInductBlockGeneration_constructors_ordered H hgen ordT + have S : BlockGenerationEnv gen H.ctorEnv := + addInductBlockGeneration_constructor_generationEnv H hgen ordC + have ordR : H.recEnv.Ordered := + addInductBlockGeneration_recursors_ordered H S + have hleCR := (ctorFold_spec gen.recursors H.addRecs).1 + have SR : BlockGenerationEnv gen H.recEnv := + S.mono hleCR ordR + exact addInductBlockGeneration_rules_ordered H SR + +/-- Preserve ordering across the raw family insertion, the first generated +component of an inductive transaction. -/ +private theorem addInductGeneration_family_ordered {source : VInductDecl} + {gen : source.GenerationChecked} + (H : AddInductGenerationTrace env env' gen) + (henv : env.Ordered) (hgen : gen.WF env) : H.typeEnv.Ordered := by + have hfamilyWF : gen.block.sourceType.toVConstant.WF env := by + show env.IsType gen.block.sourceType.uvars [] + gen.block.sourceType.type + rw [gen.block.sourceType_uvars_eq] + exact hgen.rawFamily_isType + exact .const henv hfamilyWF H.addType + +/-- Preserve ordering across the complete raw constructor fold. The proof is +uniform in the source list, so the empty-constructor case is the ordinary +zero-step fold rather than a separate preservation path. -/ +private theorem addInductGeneration_constructors_ordered + {source : VInductDecl} {gen : source.GenerationChecked} + (H : AddInductGenerationTrace env env' gen) + (hgen : gen.WF env) (ordT : H.typeEnv.Ordered) : + H.ctorEnv.Ordered := by + have hctorWF : + ∀ c ∈ gen.block.sourceType.ctors, + c.toVConstant.WF H.typeEnv := by + intro c hc + have hc' : c ∈ gen.block.ctorPairs.map (·.raw) := by + rw [gen.rawCtors_eq] + exact hc + obtain ⟨ctor, hctor, rfl⟩ := List.mem_map.1 hc' + show H.typeEnv.IsType ctor.raw.uvars [] ctor.raw.type + rw [gen.ctor_uvars_eq hctor] + exact hgen.rawCtor_isType H.addType hctor + exact constFold_ordered gen.block.sourceType.ctors ordT hctorWF + H.addCtors + +/-- Assemble the mixed-generation invariant after the exact family and +constructor components have been inserted. -/ +private theorem addInductGeneration_constructor_generationEnv + {source : VInductDecl} {gen : source.GenerationChecked} + (H : AddInductGenerationTrace env env' gen) + (hgen : gen.WF env) (ordC : H.ctorEnv.Ordered) : + GenerationEnv gen H.ctorEnv := by + obtain ⟨hleTC, hctorLookup, -⟩ := + ctorFold_spec gen.block.sourceType.ctors H.addCtors + have hlePreT := addConst_le H.addType + have hlePreC := hlePreT.trans hleTC + have hfamily : + H.ctorEnv.constants gen.block.sourceType.name = + some gen.block.sourceType.toVConstant := + hleTC.constants (addConst_self H.addType) + have hctors : + ∀ ctor ∈ gen.block.ctorPairs, + H.ctorEnv.constants ctor.raw.name = + some ctor.raw.toVConstant := by + intro ctor hctor + apply hctorLookup ctor.raw + rw [← gen.rawCtors_eq] + exact List.mem_map.2 ⟨ctor, hctor, rfl⟩ + exact hgen.toGenerationEnv H.addType hlePreC hleTC ordC hfamily + hctors + +/-- Preserve ordering across the single generated recursor component. -/ +private theorem addInductGeneration_recursor_ordered + {source : VInductDecl} {gen : source.GenerationChecked} + (H : AddInductGenerationTrace env env' gen) + (S : GenerationEnv gen H.ctorEnv) : H.recEnv.Ordered := + .const S.ord S.recursor_wf H.addRec + +/-- Preserve ordering across the generated rule fold. Empty families supply +no rules, so this is again the same zero-step component fold used generally. -/ +private theorem addInductGeneration_rules_ordered + {source : VInductDecl} {gen : source.GenerationChecked} + (H : AddInductGenerationTrace env env' gen) + (S : GenerationEnv gen H.recEnv) : env'.Ordered := by + have hout := + S.generatedRulesFold_ordered (addConst_self H.addRec) + simpa only [H.addRules] using hout + +/-- The normalized transaction preserves environment ordering from the +semantic raw/view generation certificate. Stored constants are checked in +their exact raw syntax; the mixed recursor and rules are checked only after +all raw constants are present. -/ +theorem addInductGeneration_WF {source : VInductDecl} + {gen : source.GenerationChecked} + (henv : env.Ordered) (hgen : gen.WF env) + (hadd : addInductGeneration env gen = some env') : + env'.Ordered := by + rcases addInductGeneration_trace hadd with ⟨H⟩ + have ordT : H.typeEnv.Ordered := + addInductGeneration_family_ordered H henv hgen + have ordC : H.ctorEnv.Ordered := + addInductGeneration_constructors_ordered H hgen ordT + have S : GenerationEnv gen H.ctorEnv := + addInductGeneration_constructor_generationEnv H hgen ordC + have ordR : H.recEnv.Ordered := + addInductGeneration_recursor_ordered H S + have hleCR := addConst_le H.addRec + have SR : GenerationEnv gen H.recEnv := + S.mono hleCR ordR + exact addInductGeneration_rules_ordered H SR + +/-- Recover the ordinary normalized transaction trace from the +proof-carrying public entry point. The conclusion contains only Theory data; +the producer that established the certificate is deliberately absent. -/ +theorem addInductCertified_trace {source : VInductDecl} + {certificate : source.GenerationCertificate env} + (hadd : addInductCertified env certificate = some env') : + Nonempty + (AddInductGenerationTrace env env' certificate.generation) := by + apply addInductGeneration_trace + simpa only [addInductCertified_eq_addInductGeneration] using hadd + +/-- The proof-carrying wrapper has the same atomic success/failure behavior as +the underlying normalized transaction. -/ +theorem addInductCertified_atomic {source : VInductDecl} + (env : VEnv) (certificate : source.GenerationCertificate env) : + addInductCertified env certificate = none ∨ + ∃ env', addInductCertified env certificate = some env' ∧ + Nonempty + (AddInductGenerationTrace env env' certificate.generation) := by + simpa only [addInductCertified_eq_addInductGeneration] using + addInductGeneration_atomic env certificate.generation + +/-- Ordering preservation for the public certified transaction. Its +semantic premise is carried by the certificate rather than repeated at every +call site. -/ +theorem addInductCertified_WF {source : VInductDecl} + {certificate : source.GenerationCertificate env} + (henv : env.Ordered) + (hadd : addInductCertified env certificate = some env') : + env'.Ordered := by + apply addInductGeneration_WF henv certificate.wf + simpa only [addInductCertified_eq_addInductGeneration] using hadd + +/-- Recover the exact block-wide transaction phases through the public +proof-carrying entry point. -/ +theorem addInductBlockCertified_trace {source : VInductDecl} + {certificate : source.BlockGenerationCertificate env} + (hadd : addInductBlockCertified env certificate = some env') : + Nonempty + (AddInductBlockGenerationTrace env env' + certificate.generation) := by + apply addInductBlockGeneration_trace + simpa only [addInductBlockCertified_eq_addInductBlockGeneration] using hadd + +/-- The public block certificate wrapper has the same atomic behavior as its +underlying block transaction. -/ +theorem addInductBlockCertified_atomic {source : VInductDecl} + (env : VEnv) (certificate : source.BlockGenerationCertificate env) : + addInductBlockCertified env certificate = none ∨ + ∃ env', addInductBlockCertified env certificate = some env' ∧ + Nonempty + (AddInductBlockGenerationTrace env env' + certificate.generation) := by + simpa only [addInductBlockCertified_eq_addInductBlockGeneration] using + addInductBlockGeneration_atomic env certificate.generation + +/-- Ordering preservation for the public proof-carrying mutual-block +transaction. -/ +theorem addInductBlockCertified_WF {source : VInductDecl} + {certificate : source.BlockGenerationCertificate env} + (henv : env.Ordered) + (hadd : addInductBlockCertified env certificate = some env') : + env'.Ordered := by + apply addInductBlockGeneration_WF henv certificate.wf + simpa only [addInductBlockCertified_eq_addInductBlockGeneration] using hadd + +#guard_named_axioms Ix.Theory.Named.VEnv.addInductBlockGeneration_trace [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInductBlockGeneration_atomic [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.AddInductBlockGenerationTrace.le [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.AddInductBlockGenerationTrace.family_lookup [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.AddInductBlockGenerationTrace.ctor_lookup [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.AddInductBlockGenerationTrace.rec_lookup [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.AddInductBlockGenerationTrace.rule_mem [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInductBlockGeneration_WF [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInductBlockCertified_eq_addInductBlockGeneration [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInductBlockCertified_trace [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInductBlockCertified_atomic [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInductBlockCertified_WF [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInductGeneration_trace [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInductGeneration_atomic [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.AddInductGenerationTrace.le [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.AddInductGenerationTrace.family_lookup [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.AddInductGenerationTrace.ctor_lookup [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.AddInductGenerationTrace.rec_lookup [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.AddInductGenerationTrace.rule_mem [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInductGeneration_WF [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInductCertified_eq_addInductGeneration [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInductCertified_trace [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInductCertified_atomic [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInductCertified_WF [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInduct_eq_addInductBlockGeneration [propext, Classical.choice, Quot.sound] + +/-- Elimination of a successful `addInduct` transaction into its stable +consumer-facing postcondition. -/ +theorem addInduct_success (hadd : addInduct env decl = some env') : + AddInductSuccess env env' decl := by + unfold addInduct at hadd + obtain ⟨generation, hgeneration, hgen⟩ := + Option.bind_eq_some_iff.1 hadd + rcases addInductBlockGeneration_trace hgen with ⟨H⟩ + refine { + generation := ⟨generation, hgeneration⟩ + accepted := by simp [stage3, hgeneration] + le := H.le + type_fresh := ?_ + type_lookup := ?_ + ctor_fresh := ?_ + ctor_lookup := ?_ + rec_fresh := ?_ + rec_lookup := ?_ + rule_mem := ?_ + } + · intro ty hty + exact H.family_fresh hty + · intro ty hty + exact H.family_lookup hty + · intro ty hty c hc + apply H.ctor_fresh + simp only [VInductDecl.blockConstructorConstants, List.mem_flatMap] + exact ⟨ty, hty, hc⟩ + · intro ty hty c hc + apply H.ctor_lookup + simp only [VInductDecl.blockConstructorConstants, List.mem_flatMap] + exact ⟨ty, hty, hc⟩ + · intro generation' hgeneration' recursor hrecursor + have heq : generation = generation' := + Option.some.inj (hgeneration.symm.trans hgeneration') + exact H.rec_fresh (by simpa [heq] using hrecursor) + · intro generation' hgeneration' recursor hrecursor + have heq : generation = generation' := + Option.some.inj (hgeneration.symm.trans hgeneration') + exact H.rec_lookup (by simpa [heq] using hrecursor) + · intro generation' hgeneration' df hdf + have heq : generation = generation' := + Option.some.inj (hgeneration.symm.trans hgeneration') + exact H.rule_mem (by simpa [heq] using hdf) + +/-- Successful inductive addition is monotone. -/ +theorem addInduct_le (hadd : addInduct env decl = some env') : env ≤ env' := + (addInduct_success hadd).le + +/-- Successful environment extension exposes the exact block descriptor that +drove generation, so consumers never need to re-run acceptance analysis. -/ +theorem addInduct_generation (hadd : addInduct env decl = some env') : + ∃ generation, decl.identityBlockGeneration? = some generation := + (addInduct_success hadd).generation + +theorem addInduct_type_fresh (hadd : addInduct env decl = some env') + (hty : ty ∈ decl.types) : env.constants ty.name = none := + (addInduct_success hadd).type_fresh ty hty + +theorem addInduct_type_lookup (hadd : addInduct env decl = some env') + (hty : ty ∈ decl.types) : env'.constants ty.name = some ty.toVConstant := + (addInduct_success hadd).type_lookup ty hty + +theorem addInduct_ctor_fresh (hadd : addInduct env decl = some env') + (hty : ty ∈ decl.types) (hc : c ∈ ty.ctors) : env.constants c.name = none := + (addInduct_success hadd).ctor_fresh ty hty c hc + +theorem addInduct_ctor_lookup (hadd : addInduct env decl = some env') + (hty : ty ∈ decl.types) (hc : c ∈ ty.ctors) : + env'.constants c.name = some c.toVConstant := + (addInduct_success hadd).ctor_lookup ty hty c hc + +theorem addInduct_rec_fresh (hadd : addInduct env decl = some env') + {generation : decl.BlockGenerationChecked} + (hgeneration : decl.identityBlockGeneration? = some generation) + {recursor : VConstVal} (hrecursor : recursor ∈ generation.recursors) : + env.constants recursor.name = none := + (addInduct_success hadd).rec_fresh generation hgeneration recursor hrecursor + +theorem addInduct_rec_lookup (hadd : addInduct env decl = some env') + {generation : decl.BlockGenerationChecked} + (hgeneration : decl.identityBlockGeneration? = some generation) + {recursor : VConstVal} (hrecursor : recursor ∈ generation.recursors) : + env'.constants recursor.name = some recursor.toVConstant := + (addInduct_success hadd).rec_lookup generation hgeneration recursor hrecursor + +theorem addInduct_rule_mem (hadd : addInduct env decl = some env') + {generation : decl.BlockGenerationChecked} + (hgeneration : decl.identityBlockGeneration? = some generation) + (hdf : df ∈ generation.generatedRules) : + env'.defeqs df := + (addInduct_success hadd).rule_mem generation hgeneration df hdf + +/-- `addInduct` is an all-or-nothing transaction: every evaluation either +returns no environment or returns an environment satisfying the complete +success contract. -/ +theorem addInduct_atomic : + addInduct env decl = none ∨ + ∃ env', addInduct env decl = some env' ∧ AddInductSuccess env env' decl := by + cases hadd : addInduct env decl with + | none => exact .inl rfl + | some env' => exact .inr ⟨env', rfl, addInduct_success hadd⟩ + +/-- The Stage-3 guard is an exact early-rejection condition. -/ +theorem addInduct_eq_none_of_stage3_false (h : decl.stage3 = false) : + addInduct env decl = none := by + have hgeneration : decl.identityBlockGeneration? = none := + identityBlockGeneration?_eq_none_iff.2 h + simp [addInduct, hgeneration] + +/-- A pre-existing type name rejects the transaction before any generated +object is observable. -/ +theorem addInduct_eq_none_of_type_present (hty : ty ∈ decl.types) + (hcontains : env.contains ty.name) : addInduct env decl = none := by + cases hadd : addInduct env decl with + | none => rfl + | some env' => + have hfresh := (addInduct_success hadd).type_fresh ty hty + obtain ⟨ci, hci⟩ := hcontains + rw [hci] at hfresh + contradiction + +/-- A pre-existing constructor name rejects the complete transaction. The +proof is stated through the stable success certificate, not the position of +the constructor in the internal `foldlM`. -/ +theorem addInduct_eq_none_of_ctor_present (hty : ty ∈ decl.types) + (hctor : ctor ∈ ty.ctors) (hcontains : env.contains ctor.name) : + addInduct env decl = none := by + cases hadd : addInduct env decl with + | none => rfl + | some env' => + have hfresh := (addInduct_success hadd).ctor_fresh ty hty ctor hctor + obtain ⟨ci, hci⟩ := hcontains + rw [hci] at hfresh + contradiction + +/-- A pre-existing generated recursor name likewise rejects the complete +transaction. -/ +theorem addInduct_eq_none_of_rec_present + {generation : decl.BlockGenerationChecked} + (hgeneration : decl.identityBlockGeneration? = some generation) + (hrecursor : recursor ∈ generation.recursors) + (hcontains : env.contains recursor.name) : addInduct env decl = none := by + cases hadd : addInduct env decl with + | none => rfl + | some env' => + have success := addInduct_success hadd + have hfresh := success.rec_fresh generation hgeneration recursor hrecursor + obtain ⟨ci, hci⟩ := hcontains + rw [hci] at hfresh + contradiction + +theorem addInduct_WF {generation : decl.BlockGenerationChecked} + {blockEnv : VEnv} (henv : Ordered env) + (hgeneration : decl.identityBlockGeneration? = some generation) + (hgen : generation.WF env blockEnv) + (hadd : addInduct env decl = some env') : Ordered env' := by + unfold addInduct at hadd + rw [hgeneration] at hadd + exact addInductBlockGeneration_WF henv hgen hadd + +end VEnv + +/- Mixed raw/view generation must stay within Lean's standard logical +baseline. Guard each proof-critical boundary separately so a dependency change +identifies the first affected layer. -/ +#guard_named_axioms Ix.Theory.Named.VInductDecl.Checked.WF.identityGeneration [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationEnv.minor_isType [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationEnv.minorTypes_onTel [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationEnv.familyApp_transport [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationEnv.recType_isType [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationEnv.recursor_wf [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationEnv.ruleCall_hasType [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationEnv.rule_WF [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationEnv.generatedRules_WF [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationEnv.generatedRulesFold_ordered [propext, Classical.choice, Quot.sound] + +/- The generalized recursive-Pi artifact path must remain within the same +standard logical baseline as the public preservation theorem. These guards +make every proof-critical boundary independently auditable. -/ +#guard_named_axioms Ix.Theory.Named.VInductDecl.Stage3Env.recTypeRec_isType [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Stage3Env.recConstRec_wf [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Stage3Env.ruleCallRec_hasType [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Stage3Env.minorAppRec_hasType [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Stage3Env.recRuleAppRec_hasType [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.Stage3Env.ruleRec_WF [propext, Classical.choice, Quot.sound] + +/- The indexed-inductive preservation proof stays within Lean's standard +logical axiom baseline. Keep this guard adjacent to the theorem so a new +dependency fails during ordinary module compilation. -/ +#guard_named_axioms Ix.Theory.Named.VEnv.addInduct_success [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInduct_generation [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInduct_eq_none_of_ctor_present [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInduct_eq_none_of_rec_present [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.addInduct_WF [propext, Classical.choice, Quot.sound] diff --git a/Ix/Theory/Named/Typing/InductivePattern.lean b/Ix/Theory/Named/Typing/InductivePattern.lean new file mode 100644 index 000000000..b853952d7 --- /dev/null +++ b/Ix/Theory/Named/Typing/InductivePattern.lean @@ -0,0 +1,761 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Typing.InductiveLemmas +import Ix.Theory.Named.Typing.Pattern + +open Ix.Theory (VLevel) + +/-! # Generated iota rules as patterns + +Every iota rule generated for a certified mutual block +(`BlockGenerationChecked.rule`) is a closed defeq between lambda telescopes +whose left body is a `SimplePattern.iota` spine: the owning family's recursor +applied to the shared parameters, all motives, all minors, and the +constructor's result indices, with a constructor-headed major premise. This +module makes that connection exact and proves the generic pattern facts the +Church–Rosser `Params` interface demands of one certified block: + +* `rulePattern` is the `SimplePattern` of one flattened constructor's rule, + and `ruleLhsBody_matches` matches the exact generated left body against it + at the rule's recursor levels. +* `IotaPat` is the block's pattern set, associating each rule's pattern with + an RHS template (the registered right tower applied to the captured common + arguments and fields) and a check list (parameter and result-index + agreement between the recursor spine and the major premise). +* `pat_simple`, `pat_uniq`, `pat_app_l`, `pat_app_l_uniq`, and + `pat_app_uniq` are exactly the `Params` obligations, specialized to + `IotaPat`; their name-freshness inputs come from the certified block's + `blockGeneratedNames` nodup bit, and the major-arity agreement between + same-recursor rules comes from the analyzer's terminal `blockTarget?` + arity equation. + +No open-environment `Params` instance is installed here; the block supplies +the facts, and soundness (`pat_wf`) plus the block-local environment +assembler belong to the pattern-soundness milestone. -/ + +namespace Ix.Theory.Named + +open VExpr + +namespace VExpr + +@[simp] theorem bvarRevRange_length : ∀ (off m : Nat), + (bvarRevRange off m).length = m + | _, 0 => rfl + | off, m+1 => by simp [bvarRevRange, bvarRevRange_length off m] + +end VExpr + +/-- Extending a `HeadConstN` spine by an application spine. -/ +theorem HeadConstN.appN {c : Name} {ls : List VLevel} : + ∀ (as : List VExpr) {n : Nat} {f : VExpr}, HeadConstN c ls n f → + HeadConstN c ls (n + as.length) (VExpr.appN f as) + | [], _, _, h => h + | a :: as, n, f, h => by + have := HeadConstN.appN as (h.app (a := a)) + show HeadConstN c ls (n + (as.length + 1)) (VExpr.appN (f.app a) as) + rwa [(by omega : n + (as.length + 1) = n + 1 + as.length)] + +namespace VInductDecl + +/-! ## Positional facts about the checked pairings -/ + +theorem pairNormalizedFamilies_getElem? : + ∀ (raws : List VInductiveType) (views : List CheckedFamilyData) (t : Nat) + {family : NormalizedFamily}, + (pairNormalizedFamilies raws views)[t]? = some family → + raws[t]? = some family.raw ∧ views[t]? = some family.view + | raw :: raws, view :: views, 0, family => by + intro h + cases h + exact ⟨rfl, rfl⟩ + | raw :: raws, view :: views, t+1, family => by + intro h + simpa using pairNormalizedFamilies_getElem? raws views t + (by simpa [pairNormalizedFamilies] using h) + | [], _, t, _ => by intro h; simp [pairNormalizedFamilies] at h + | _ :: _, [], t, _ => by intro h; simp [pairNormalizedFamilies] at h + +theorem pairNormalizedCtors_getElem? : + ∀ (raws : List VConstVal) (views : List CheckedCtor) (t : Nat) + {ctor : NormalizedCtor}, + (pairNormalizedCtors raws views)[t]? = some ctor → + raws[t]? = some ctor.raw ∧ views[t]? = some ctor.view + | raw :: raws, view :: views, 0, ctor => by + intro h + cases h + exact ⟨rfl, rfl⟩ + | raw :: raws, view :: views, t+1, ctor => by + intro h + simpa using pairNormalizedCtors_getElem? raws views t + (by simpa [pairNormalizedCtors] using h) + | [], _, t, _ => by intro h; simp [pairNormalizedCtors] at h + | _ :: _, [], t, _ => by intro h; simp [pairNormalizedCtors] at h + +/-- The erased family-data spine reads back its exact member facts: ordinal +consecutiveness, the indexing family, the analyzer equations, and the +per-family acceptance bit. -/ +theorem CheckedFamilies.data_getElem? {source : VInductDecl} {params : List VExpr} : + ∀ {ord : Nat} {types : List VInductiveType} + (fs : CheckedFamilies source params ord types) (t : Nat) + {fd : CheckedFamilyData}, + fs.data[t]? = some fd → + ∃ type, types[t]? = some type ∧ fd.ordinal = ord + t ∧ fd.value = type ∧ + fd.indices = ctorFields (VExpr.dropN source.nparams type.type) ∧ + fd.constructors = type.ctors.map (CheckedCtor.ofBlock source) ∧ + blockFamilyCore source params (ord + t) type = true + | _, _, .nil, t, fd => by intro h; simp [CheckedFamilies.data] at h + | ord, _, .cons head tail, 0, fd => by + intro h + cases h + exact ⟨_, rfl, rfl, rfl, head.indices_eq, head.constructors_eq, head.accepted⟩ + | ord, _, .cons head tail, t+1, fd => by + intro h + obtain ⟨type, h1, h2, h3, h4, h5, h6⟩ := + CheckedFamilies.data_getElem? tail t (by simpa [CheckedFamilies.data] using h) + exact ⟨type, by simpa using h1, by omega, h3, h4, h5, + by rw [(by omega : ord + (t + 1) = ord + 1 + t)]; exact h6⟩ + +/-! ## Arity extraction from the analyzer's terminal target check -/ + +theorem blockTarget?_loop_length {U np j : Nat} {names : List Name} + {head : VExpr} {args : List VExpr} : + ∀ (headers : List FamilyHeader) (t : Nat) {target : Nat} {idxs : List VExpr}, + blockTarget?.loop U np j names head args t headers = some (target, idxs) → + t ≤ target ∧ ∃ header, headers[target - t]? = some header ∧ + args.length = np + header.indices ∧ idxs = args.drop np + | [], t, target, idxs => by intro h; simp [blockTarget?.loop] at h + | header :: headers, t, target, idxs => by + intro h + rw [blockTarget?.loop] at h + split at h + · rename_i hcond + cases h + simp only [Bool.and_eq_true, beq_iff_eq] at hcond + exact ⟨Nat.le_refl _, header, by simp, hcond.1.1.2, rfl⟩ + · obtain ⟨hle, header', h1, h2, h3⟩ := + blockTarget?_loop_length headers (t+1) h + refine ⟨Nat.le_of_succ_le hle, header', ?_, h2, h3⟩ + rw [(by omega : target - t = (target - (t+1)) + 1)] + simpa using h1 + +/-- A successful mutual target recognition pins the target's index arity to +its family header. -/ +theorem blockTarget?_length {U np j : Nat} {headers : List FamilyHeader} + {names : List Name} {B : VExpr} {target : Nat} {idxs : List VExpr} + (h : blockTarget? U np j headers names B = some (target, idxs)) : + ∃ header, headers[target]? = some header ∧ + (VExpr.appArgs B []).length = np + header.indices ∧ + idxs = (VExpr.appArgs B []).drop np := by + rw [blockTarget?] at h + obtain ⟨hle, header, h1, h2, h3⟩ := blockTarget?_loop_length headers 0 h + exact ⟨header, by simpa using h1, h2, h3⟩ + +/-- The terminal of an accepted mutual constructor shape is a successful +`blockTarget?` recognition of the owner family, past all fields. -/ +theorem blockStage3Ctor_result {U np : Nat} {headers : List FamilyHeader} + {names : List Name} {owner : Nat} : + ∀ (B : VExpr) (j : Nat), blockStage3Ctor U np headers names owner j B = true → + ∃ idxs, blockTarget? U np (j + (ctorFields B).length) headers names + (VExpr.resultOf B) = some (owner, idxs) := by + intro B + induction B with + (intro j h + simp only [blockStage3Ctor] at h + try (split at h + · rename_i target idxs heq + refine ⟨idxs, ?_⟩ + simp only [ctorFields, List.length_nil, Nat.add_zero, VExpr.resultOf] + rwa [(by simpa using h : target = owner)] at heq + · cases h)) + | forallE A rest _ ihR => + rw [Bool.and_eq_true] at h + obtain ⟨-, h2⟩ := h + obtain ⟨idxs, hidx⟩ := ihR (j+1) h2 + refine ⟨idxs, ?_⟩ + simp only [ctorFields, List.length_cons, VExpr.resultOf] + rwa [(by omega : j + ((ctorFields rest).length + 1) = j + 1 + (ctorFields rest).length)] + +/-! ## Name transport across the normalization boundary -/ + +theorem sameCtorHeaders_names : ∀ {cs cs' : List VConstVal}, + sameCtorHeaders cs cs' = true → cs.map (·.name) = cs'.map (·.name) + | [], [], _ => rfl + | c :: cs, c' :: cs', h => by + simp only [sameCtorHeaders, Bool.and_eq_true, beq_iff_eq] at h + simp only [List.map_cons, h.1.1, sameCtorHeaders_names h.2] + | [], _ :: _, h => by simp [sameCtorHeaders] at h + | _ :: _, [], h => by simp [sameCtorHeaders] at h + +theorem sameTypeHeaders_names : ∀ {tys tys' : List VInductiveType}, + sameTypeHeaders tys tys' = true → + tys.map (·.name) = tys'.map (·.name) ∧ + tys.flatMap (fun ty => ty.ctors.map (·.name)) = + tys'.flatMap (fun ty => ty.ctors.map (·.name)) + | [], [], _ => ⟨rfl, rfl⟩ + | ty :: tys, ty' :: tys', h => by + simp only [sameTypeHeaders, Bool.and_eq_true, beq_iff_eq] at h + have ih := sameTypeHeaders_names h.2 + simp only [List.map_cons, List.flatMap_cons, h.1.1.1, ih.1, ih.2, + sameCtorHeaders_names h.1.2, and_self] + | [], _ :: _, h => by simp [sameTypeHeaders] at h + | _ :: _, [], h => by simp [sameTypeHeaders] at h + +/-- The reserved generated names are unchanged by normalization: they are +computed from family and constructor identities only. -/ +theorem blockGeneratedNames_eq_of_sameTypeHeaders + {tys tys' : List VInductiveType} (h : sameTypeHeaders tys tys' = true) : + blockGeneratedNames tys = blockGeneratedNames tys' := by + obtain ⟨h1, h2⟩ := sameTypeHeaders_names h + have h3 : tys.map (fun ty => (.str ty.name "rec" : Name)) = + tys'.map (fun ty => (.str ty.name "rec" : Name)) := by + have := congrArg (List.map (fun n => (.str n "rec" : Name))) h1 + simpa [List.map_map, Function.comp_def] using this + simp only [blockGeneratedNames, h1, h2, h3] + +namespace BlockGenerationChecked + +variable {source : VInductDecl} (gen : source.BlockGenerationChecked) + +/-! ## Inventory facts from the certified block -/ + +include gen in +/-- The reserved generated names of the raw source are collision-free: the +analyzer certifies the view's inventory, and normalization retains every +identity. -/ +theorem blockGeneratedNames_nodup : + (blockGeneratedNames source.types).Nodup := by + have hshape := gen.block.normalization.shape_eq + simp only [normalizationShape, Bool.and_eq_true, beq_iff_eq] at hshape + rw [blockGeneratedNames_eq_of_sameTypeHeaders hshape.2] + have h := gen.block.checked.names_nodup + rwa [gen.block.checked.names_eq] at h + +/-! ## Named components of one generated iota rule -/ + +/-- Field count of one flattened constructor, as bound by its iota rule. -/ +def ruleFieldCount (constructor : NormalizedBlockCtor) : Nat := + (constructor.ctor.fieldsR source.uvars source.nparams gen.elimination).length + +/-- The result-index spine of one iota rule body, in the rule's binder +context. -/ +def ruleIdx (constructor : NormalizedBlockCtor) : List VExpr := + constructor.ctor.resultIndicesR source.uvars gen.elimination |>.map + fun e => e.liftN (gen.familyCount + gen.minorCount) + (gen.ruleFieldCount constructor) + +/-- The binder telescope shared by both towers of one iota rule. -/ +def ruleBinders (constructor : NormalizedBlockCtor) : List VExpr := + gen.paramsTel ++ gen.motiveTypes ++ gen.minorTypes ++ + VExpr.liftTelN (gen.familyCount + gen.minorCount) + (constructor.ctor.fieldsR source.uvars source.nparams gen.elimination) 0 + +/-- The constructor-headed major premise of one iota rule body. -/ +def ruleCtorApp (constructor : NormalizedBlockCtor) : VExpr := + VExpr.appN (.const constructor.ctor.raw.name gen.sourceLevels) + (VExpr.bvarRevRange + (gen.ruleFieldCount constructor + (gen.familyCount + gen.minorCount)) + source.nparams ++ + VExpr.bvarRevRange 0 (gen.ruleFieldCount constructor)) + +/-- The exact left body of one generated iota rule: the owner's recursor +applied to the common arguments, the constructor's result indices, and the +constructor-headed major premise. -/ +def ruleLhsBody (constructor : NormalizedBlockCtor) : VExpr := + VExpr.appN (gen.recBase (gen.ruleFieldCount constructor) constructor.owner) + (gen.ruleIdx constructor ++ [gen.ruleCtorApp constructor]) + +/-- The generated rule's left side is exactly the shared binder telescope +over the `SimplePattern.iota` spine. -/ +theorem rule_lhs (i : Nat) (constructor : NormalizedBlockCtor) : + (gen.rule i constructor).lhs = + VExpr.lamN (gen.ruleBinders constructor) (gen.ruleLhsBody constructor) := rfl + +/-! ## The pattern of one generated iota rule -/ + +/-- The recursor constant owning one flattened constructor's iota rule. -/ +def ruleRecName (constructor : NormalizedBlockCtor) : Name := + .str (gen.familyNameAt constructor.owner) "rec" + +/-- Major-argument arity of one iota rule: shared parameters, all motives, +all minors, and the constructor's result indices. -/ +def ruleMajorArity (constructor : NormalizedBlockCtor) : Nat := + source.nparams + gen.familyCount + gen.minorCount + + (constructor.ctor.resultIndicesR source.uvars gen.elimination).length + +/-- Argument arity of one iota rule's constructor-headed major premise. -/ +def ruleArgArity (constructor : NormalizedBlockCtor) : Nat := + source.nparams + gen.ruleFieldCount constructor + +/-- The `SimplePattern` of one generated iota rule. -/ +@[reducible] def rulePattern (constructor : NormalizedBlockCtor) : SimplePattern := + .iota (gen.ruleRecName constructor) (gen.ruleMajorArity constructor) + constructor.ctor.raw.name (gen.ruleArgArity constructor) + +/-- The generated left body is matched by the rule's pattern, at exactly the +rule's recursor levels. -/ +theorem ruleLhsBody_matches (constructor : NormalizedBlockCtor) : + ∃ m2, ((gen.rulePattern constructor).toPattern).Matches + (gen.ruleLhsBody constructor) gen.recLevels m2 := by + rw [rulePattern, SimplePattern.toPattern_iota] + have hleft : HeadConstN (gen.ruleRecName constructor) gen.recLevels + (gen.ruleMajorArity constructor) + (VExpr.appN (gen.recBase (gen.ruleFieldCount constructor) constructor.owner) + (gen.ruleIdx constructor)) := by + have h0 : HeadConstN (gen.ruleRecName constructor) gen.recLevels 0 + (.const (gen.ruleRecName constructor) gen.recLevels) := .const + have h1 := (h0.appN (VExpr.bvarRevRange (gen.ruleFieldCount constructor) + (source.nparams + gen.familyCount + gen.minorCount))) + have h2 := h1.appN (as := gen.ruleIdx constructor) + rw [VExpr.bvarRevRange_length] at h2 + have harity : 0 + (source.nparams + gen.familyCount + gen.minorCount) + + (gen.ruleIdx constructor).length = gen.ruleMajorArity constructor := by + simp only [ruleIdx, ruleMajorArity, List.length_map]; omega + rwa [harity] at h2 + have hright : HeadConstN constructor.ctor.raw.name gen.sourceLevels + (gen.ruleArgArity constructor) (gen.ruleCtorApp constructor) := by + have h0 : HeadConstN constructor.ctor.raw.name gen.sourceLevels 0 + (.const constructor.ctor.raw.name gen.sourceLevels) := .const + have h1 := h0.appN (as := VExpr.bvarRevRange + (gen.ruleFieldCount constructor + (gen.familyCount + gen.minorCount)) + source.nparams ++ VExpr.bvarRevRange 0 (gen.ruleFieldCount constructor)) + rw [List.length_append, VExpr.bvarRevRange_length, VExpr.bvarRevRange_length] at h1 + have harity : 0 + (source.nparams + gen.ruleFieldCount constructor) = + gen.ruleArgArity constructor := by simp only [ruleArgArity]; omega + rwa [harity] at h1 + have hbody : gen.ruleLhsBody constructor = + .app (VExpr.appN (gen.recBase (gen.ruleFieldCount constructor) constructor.owner) + (gen.ruleIdx constructor)) + (gen.ruleCtorApp constructor) := by + rw [ruleLhsBody, VExpr.appN_append] + rfl + rw [hbody] + exact RecursorIotaPattern.matches_of hleft hright + +/-! ## Positional anatomy of the flattened constructors -/ + +/-- The checked spine assigns family ordinals positionally. -/ +theorem families_getElem?_ordinal {t : Nat} {family : NormalizedFamily} + (h : gen.families[t]? = some family) : family.view.ordinal = t := by + have h' : (pairNormalizedFamilies source.types + gen.block.checked.families.data)[t]? = some family := h + obtain ⟨-, hview⟩ := pairNormalizedFamilies_getElem? _ _ t h' + obtain ⟨type, -, hord, -, -, -, -⟩ := CheckedFamilies.data_getElem? _ t hview + simpa using hord + +/-- Position `t` of the paired family list is the `t`-th source family. -/ +theorem families_getElem?_raw {t : Nat} {family : NormalizedFamily} + (h : gen.families[t]? = some family) : source.types[t]? = some family.raw := + (pairNormalizedFamilies_getElem? source.types + gen.block.checked.families.data t h).1 + +/-- A family lookup names the owning recursor's family. -/ +theorem familyNameAt_eq {t : Nat} {family : NormalizedFamily} + (h : gen.families[t]? = some family) : + gen.familyNameAt t = family.raw.name := by + simp [familyNameAt, h] + +/-- One flattened constructor decomposes into its owner family lookup and +its position inside that family's pairing. -/ +theorem flatCtors_anatomy {constructor : NormalizedBlockCtor} + (hc : constructor ∈ gen.flatCtors) : + ∃ t family, gen.families[t]? = some family ∧ + constructor.owner = t ∧ + constructor.familyName = family.raw.name ∧ + constructor.familyIndices = family.view.indices ∧ + constructor.ctor ∈ family.ctorPairs := by + have hc' : constructor ∈ gen.families.flatMap (·.blockCtors) := hc + rw [List.mem_flatMap] at hc' + obtain ⟨family, hfamily, hmem⟩ := hc' + obtain ⟨t, ht⟩ := List.mem_iff_getElem?.1 hfamily + simp only [NormalizedFamily.blockCtors, List.mem_map] at hmem + obtain ⟨ctor, hctor, rfl⟩ := hmem + exact ⟨t, family, ht, gen.families_getElem?_ordinal ht, rfl, rfl, hctor⟩ + +/-! ## The analyzer's arity equation for pattern majors -/ + +/-- Every flattened constructor's checked result-index spine has exactly its +owner family's index arity: the analyzer's terminal `blockTarget?` equation +transports through the checked spine. -/ +theorem view_resultIndices_length {constructor : NormalizedBlockCtor} + (hc : constructor ∈ gen.flatCtors) : + constructor.ctor.view.resultIndices.length = + constructor.familyIndices.length := by + obtain ⟨t, family, ht, -, -, hindices, hmem⟩ := gen.flatCtors_anatomy hc + have ht' : (pairNormalizedFamilies source.types + gen.block.checked.families.data)[t]? = some family := ht + obtain ⟨-, hview⟩ := pairNormalizedFamilies_getElem? _ _ t ht' + obtain ⟨vtype, hvty, -, -, hvindices, hvctors, hvcore⟩ := + CheckedFamilies.data_getElem? _ t hview + rw [Nat.zero_add] at hvcore + obtain ⟨s, hs⟩ := List.mem_iff_getElem?.1 hmem + have hs' : (pairNormalizedCtors family.raw.ctors + family.view.constructors)[s]? = some constructor.ctor := hs + obtain ⟨-, hviewCtor⟩ := pairNormalizedCtors_getElem? _ _ s hs' + rw [hvctors, List.getElem?_map] at hviewCtor + obtain ⟨c₀, hc₀, hview_eq⟩ : ∃ c₀, vtype.ctors[s]? = some c₀ ∧ + CheckedCtor.ofBlock _ c₀ = constructor.ctor.view := by + cases h0 : vtype.ctors[s]? with + | none => rw [h0] at hviewCtor; cases hviewCtor + | some c₀ => rw [h0] at hviewCtor; exact ⟨c₀, rfl, by simpa using hviewCtor⟩ + simp only [blockFamilyCore, Bool.and_eq_true, beq_iff_eq, + List.all_eq_true] at hvcore + have hstage := (hvcore.2 c₀ (List.mem_of_getElem? hc₀)).2 + obtain ⟨idxs, htarget⟩ := blockStage3Ctor_result _ 0 hstage + obtain ⟨header, hheader, hlen, -⟩ := blockTarget?_length htarget + rw [familyHeaders, List.getElem?_map, hvty, Option.map_some] at hheader + have hri : constructor.ctor.view.resultIndices = + (VExpr.appArgs (VExpr.resultOf (VExpr.dropN + gen.block.normalization.view.nparams c₀.type)) []).drop + gen.block.normalization.view.nparams := by + rw [← hview_eq]; rfl + rw [hri, hindices, hvindices, List.length_drop, hlen] + cases hheader + show gen.block.normalization.view.nparams + + (ctorFields (VExpr.dropN gen.block.normalization.view.nparams + vtype.type)).length - + gen.block.normalization.view.nparams = + (ctorFields (VExpr.dropN gen.block.normalization.view.nparams + vtype.type)).length + omega + +/-- Pattern major arity through the owner family's index count. -/ +theorem ruleMajorArity_eq {constructor : NormalizedBlockCtor} + (hc : constructor ∈ gen.flatCtors) : + gen.ruleMajorArity constructor = + source.nparams + gen.familyCount + gen.minorCount + + constructor.familyIndices.length := by + simp only [ruleMajorArity, NormalizedCtor.resultIndicesR, List.length_map, + gen.view_resultIndices_length hc] + +/-! ## Name freshness of the generated inventory -/ + +include gen in +private theorem nodup_parts : + (source.types.map (·.name)).Nodup ∧ + (source.types.flatMap fun ty => ty.ctors.map (·.name)).Nodup ∧ + ∀ a ∈ (source.types.flatMap fun ty => ty.ctors.map (·.name)), + ∀ b ∈ source.types.map (fun ty => (.str ty.name "rec" : Name)), a ≠ b := by + have h := gen.blockGeneratedNames_nodup + rw [blockGeneratedNames, List.nodup_append] at h + obtain ⟨hAB, -, hdisj⟩ := h + rw [List.nodup_append] at hAB + exact ⟨hAB.1, hAB.2.1, fun a ha b hb => + hdisj a (List.mem_append.2 (.inr ha)) b hb⟩ + +/-- Family positions are recoverable from raw family names. -/ +theorem families_name_inj {t t' : Nat} {family family' : NormalizedFamily} + (h : gen.families[t]? = some family) (h' : gen.families[t']? = some family') + (hname : family.raw.name = family'.raw.name) : t = t' := by + have h1 := gen.families_getElem?_raw h + have h1' := gen.families_getElem?_raw h' + have hm : (source.types.map (·.name))[t]? = some family.raw.name := by + rw [List.getElem?_map, h1, Option.map_some] + have hm' : (source.types.map (·.name))[t']? = some family.raw.name := by + rw [List.getElem?_map, h1', Option.map_some, hname] + obtain ⟨hlt, -⟩ := List.getElem?_eq_some_iff.1 hm + exact (List.getElem?_inj hlt gen.nodup_parts.1).1 (hm.trans hm'.symm) + +/-- Flattened positions are recoverable from raw constructor names. -/ +theorem flatCtors_name_inj {i i' : Nat} {c c' : NormalizedBlockCtor} + (h : gen.flatCtors[i]? = some c) (h' : gen.flatCtors[i']? = some c') + (hname : c.ctor.raw.name = c'.ctor.raw.name) : i = i' ∧ c = c' := by + have hnodup : ((source.blockConstructorConstants).map (·.name)).Nodup := by + rw [VInductDecl.blockConstructorConstants, List.map_flatMap] + exact gen.nodup_parts.2.1 + have hm : ((source.blockConstructorConstants).map (·.name))[i]? = + some c.ctor.raw.name := by + rw [List.getElem?_map, ← gen.flatCtors_map_raw, List.getElem?_map, h] + rfl + have hm' : ((source.blockConstructorConstants).map (·.name))[i']? = + some c.ctor.raw.name := by + rw [List.getElem?_map, ← gen.flatCtors_map_raw, List.getElem?_map, h', + hname] + rfl + obtain ⟨hlt, -⟩ := List.getElem?_eq_some_iff.1 hm + have hii : i = i' := (List.getElem?_inj hlt hnodup).1 (hm.trans hm'.symm) + subst hii + exact ⟨rfl, Option.some.inj (h.symm.trans h')⟩ + +/-- No family's recursor name collides with any flattened constructor's +name. -/ +theorem recName_ne_ctorName {family : NormalizedFamily} + (hfam : family ∈ gen.families) {constructor : NormalizedBlockCtor} + (hc : constructor ∈ gen.flatCtors) : + (.str family.raw.name "rec" : Name) ≠ constructor.ctor.raw.name := by + have hmemC : constructor.ctor.raw.name ∈ + source.types.flatMap fun ty => ty.ctors.map (·.name) := by + have h1 : constructor.ctor.raw ∈ source.blockConstructorConstants := by + rw [← gen.flatCtors_map_raw] + exact List.mem_map_of_mem hc + rw [VInductDecl.blockConstructorConstants, List.mem_flatMap] at h1 + obtain ⟨ty, hty, hmem⟩ := h1 + rw [List.mem_flatMap] + exact ⟨ty, hty, List.mem_map_of_mem hmem⟩ + have hmemR : (.str family.raw.name "rec" : Name) ∈ + source.types.map (fun ty => (.str ty.name "rec" : Name)) := by + have h1 : family.raw ∈ source.types := by + rw [← gen.families_map_raw] + exact List.mem_map_of_mem hfam + exact List.mem_map_of_mem h1 + intro heq + exact gen.nodup_parts.2.2 _ hmemC _ hmemR heq.symm + +/-- Two flattened constructors with the same owning recursor name share +their owner and their family's index telescope. -/ +theorem ruleRecName_inj {c c' : NormalizedBlockCtor} + (hc : c ∈ gen.flatCtors) (hc' : c' ∈ gen.flatCtors) + (h : gen.ruleRecName c = gen.ruleRecName c') : + c.owner = c'.owner ∧ c.familyIndices = c'.familyIndices := by + obtain ⟨t, family, ht, ho, -, hi, -⟩ := gen.flatCtors_anatomy hc + obtain ⟨t', family', ht', ho', -, hi', -⟩ := gen.flatCtors_anatomy hc' + rw [ruleRecName, ruleRecName, ho, ho', gen.familyNameAt_eq ht, + gen.familyNameAt_eq ht'] at h + have hnames : family.raw.name = family'.raw.name := by + injection h with h1 h2 + have ht2 : t = t' := gen.families_name_inj ht ht' hnames + subst ht2 + cases Option.some.inj (ht.symm.trans ht') + exact ⟨ho.trans ho'.symm, hi.trans hi'.symm⟩ + +/-- Rule distinctness: distinct flattened positions carry distinct +patterns. -/ +theorem rulePattern_inj {i i' : Nat} {c c' : NormalizedBlockCtor} + (h : gen.flatCtors[i]? = some c) (h' : gen.flatCtors[i']? = some c') + (heq : gen.rulePattern c = gen.rulePattern c') : i = i' ∧ c = c' := by + injection heq with h1 h2 h3 h4 + exact gen.flatCtors_name_inj h h' h3 + +/-! ## Rule payloads: RHS templates and agreement checks -/ + +/-- Closedness inputs for one certified block's rule payloads: the towers a +rule's RHS template and checks embed as fixed template constants. Concrete +fixtures discharge this bundle by `decide`; the pattern-soundness milestone +derives it from the staged environment's rule well-formedness. -/ +structure RuleClosure : Prop where + rhs_closed : ∀ ⦃i : Nat⦄ ⦃constructor : NormalizedBlockCtor⦄, + gen.flatCtors[i]? = some constructor → + ((gen.rule i constructor).rhs).ClosedN 0 + idxTower_closed : ∀ ⦃constructor : NormalizedBlockCtor⦄, + constructor ∈ gen.flatCtors → ∀ e ∈ gen.ruleIdx constructor, + (VExpr.lamN (gen.ruleBinders constructor) e).ClosedN 0 + +/-- The template capture list shared by every payload tower: the recursor +side's parameters, motives, and minors, then the major premise's fields. -/ +def captureArgs (constructor : NormalizedBlockCtor) : + List (((gen.rulePattern constructor).toPattern).RHS) := + ((Pattern.varNPaths (.const (gen.ruleRecName constructor)) + (gen.ruleMajorArity constructor)).take + (source.nparams + gen.familyCount + gen.minorCount)).map + (fun path => .var (.inl path)) ++ + ((Pattern.varNPaths (.const constructor.ctor.raw.name) + (gen.ruleArgArity constructor)).drop source.nparams).map + (fun path => .var (.inr path)) + +/-- The RHS template of one rule: the registered right tower applied to the +captured common arguments and fields. -/ +def ruleRHS (hcl : gen.RuleClosure) {i : Nat} {constructor : NormalizedBlockCtor} + (h : gen.flatCtors[i]? = some constructor) : + ((gen.rulePattern constructor).toPattern).RHS := + Pattern.RHS.appN (.fixed ((gen.rule i constructor).rhs) (hcl.rhs_closed h)) + (gen.captureArgs constructor) + +/-- The check list of one rule: the major premise's parameters must agree +with the recursor side's parameters, and the recursor side's index arguments +must agree with the constructor's computed result indices (as fixed index +towers applied to the captures). -/ +def ruleCheck (hcl : gen.RuleClosure) {constructor : NormalizedBlockCtor} + (hc : constructor ∈ gen.flatCtors) : + ((gen.rulePattern constructor).toPattern).Check := + let recPaths := Pattern.varNPaths (.const (gen.ruleRecName constructor)) + (gen.ruleMajorArity constructor) + let ctorPaths := Pattern.varNPaths (.const constructor.ctor.raw.name) + (gen.ruleArgArity constructor) + let common := source.nparams + gen.familyCount + gen.minorCount + let idxChecks := + ((gen.ruleIdx constructor).attach.zip (recPaths.drop common)).foldr + (fun ep rest => + .defeq (.var (.inl ep.2)) + (Pattern.RHS.appN + (.fixed (VExpr.lamN (gen.ruleBinders constructor) ep.1.1) + (hcl.idxTower_closed hc ep.1.1 ep.1.2)) + (gen.captureArgs constructor)) rest) + .true + ((ctorPaths.take source.nparams).zip (recPaths.take source.nparams)).foldr + (fun pr rest => .defeq (.var (.inr pr.1)) (.var (.inl pr.2)) rest) + idxChecks + +/-- Position `i` of the certified block's flattened constructor list. -/ +abbrev ruleEntry (i : Nat) (constructor : NormalizedBlockCtor) : Prop := + gen.flatCtors[i]? = some constructor + +/-- A decidable sufficient condition for `RuleClosure`, discharging concrete +fixtures by evaluation. -/ +theorem RuleClosure.of_all + (h1 : gen.flatCtors.zipIdx.all (fun ic => + decide (((gen.rule ic.2 ic.1).rhs).ClosedN 0)) = true) + (h2 : gen.flatCtors.all (fun c => (gen.ruleIdx c).all fun e => + decide ((VExpr.lamN (gen.ruleBinders c) e).ClosedN 0)) = true) : + gen.RuleClosure := by + constructor + · intro i constructor h + have hmem : (constructor, i) ∈ gen.flatCtors.zipIdx := by + apply List.mem_of_getElem? (i := i) + rw [List.getElem?_zipIdx, h, Option.map_some, Nat.zero_add] + exact of_decide_eq_true (List.all_eq_true.1 h1 _ hmem) + · intro constructor hc e he + exact of_decide_eq_true (List.all_eq_true.1 (List.all_eq_true.1 h2 _ hc) _ he) + +/-- The pattern set of one certified block: each flattened constructor's +rule pattern with its template and checks. -/ +inductive IotaPat (hcl : gen.RuleClosure) : + (p : Pattern) → p.RHS × p.Check → Prop where + | mk {i : Nat} {constructor : NormalizedBlockCtor} + (h : gen.ruleEntry i constructor) : + IotaPat hcl ((gen.rulePattern constructor).toPattern) + (gen.ruleRHS hcl h, gen.ruleCheck hcl (List.mem_of_getElem? h)) + +/-! ## The `Params` obligations for one certified block -/ + +/-- `Params.pat_simple` for the block's pattern set. -/ +theorem IotaPat.pat_simple {hcl : gen.RuleClosure} {p : Pattern} + {r : p.RHS × p.Check} (H : gen.IotaPat hcl p r) : + ∃ sp : SimplePattern, p = sp.toPattern := by + cases H with | mk h => exact ⟨_, rfl⟩ + +/-- Rule recovery: a pattern in the block's set determines its flattened +rule position and constructor. -/ +theorem IotaPat.recover {hcl : gen.RuleClosure} {p : Pattern} + {r : p.RHS × p.Check} (H : gen.IotaPat hcl p r) : + ∃ (i : Nat) (constructor : NormalizedBlockCtor), + gen.flatCtors[i]? = some constructor ∧ + p = (gen.rulePattern constructor).toPattern ∧ + ∀ (i' : Nat) (constructor' : NormalizedBlockCtor), + gen.flatCtors[i']? = some constructor' → + (gen.rulePattern constructor').toPattern = p → + i' = i ∧ constructor' = constructor := by + cases H with | @mk i constructor h => + refine ⟨i, constructor, h, rfl, ?_⟩ + intro i' constructor' h' heq + have := RecursorIotaPattern.inj heq + exact gen.flatCtors_name_inj h' h this.2.2.1 + +/-- `Params.pat_uniq` for the block's pattern set. -/ +theorem IotaPat.pat_uniq {hcl : gen.RuleClosure} {p₁ p₂ p₃ p₄ : Pattern} + {r : p₁.RHS × p₁.Check} {r' : p₂.RHS × p₂.Check} + (H1 : gen.IotaPat hcl p₁ r) (H2 : gen.IotaPat hcl p₂ r') + (H3 : Subpattern p₃ p₁) (H4 : p₂.inter p₃ = some p₄) : + p₁ = p₂ ∧ p₂ = p₃ ∧ r ≍ r' := by + cases H1 with | @mk i c h => + cases H2 with | @mk i' c' h' => + rcases RecursorIotaPattern.subpattern_inv H3 with rfl | ⟨j, hj, rfl⟩ | ⟨j, hj, rfl⟩ + · obtain ⟨hR, hM, hC, hN, rfl⟩ := RecursorIotaPattern.inter_some H4 + obtain ⟨rfl, rfl⟩ := gen.flatCtors_name_inj h' h hC + exact ⟨rfl, rfl, HEq.rfl⟩ + · obtain ⟨hb, hj'⟩ := RecursorIotaPattern.inter_varN_const_some H4 + obtain ⟨-, hIdx⟩ := gen.ruleRecName_inj (List.mem_of_getElem? h) + (List.mem_of_getElem? h') hb + have hM : gen.ruleMajorArity c' = gen.ruleMajorArity c := by + rw [gen.ruleMajorArity_eq (List.mem_of_getElem? h'), + gen.ruleMajorArity_eq (List.mem_of_getElem? h), hIdx] + omega + · obtain ⟨hb, -⟩ := RecursorIotaPattern.inter_varN_const_some H4 + obtain ⟨t', family', ht', ho', -, -, -⟩ := + gen.flatCtors_anatomy (List.mem_of_getElem? h') + have hrec : gen.ruleRecName c' = (.str family'.raw.name "rec" : Name) := by + rw [ruleRecName, ho', gen.familyNameAt_eq ht'] + refine absurd ?_ (gen.recName_ne_ctorName (List.mem_of_getElem? ht') + (List.mem_of_getElem? h)) + rw [← hrec, hb] + +/-- `Params.pat_app_l` for the block's pattern set. -/ +theorem IotaPat.pat_app_l {hcl : gen.RuleClosure} {p : Pattern} + {r : p.RHS × p.Check} {p₁ p₂ p₃ p₄ : Pattern} + (H : gen.IotaPat hcl p r) (h : Subpattern (.app p₁ p₂) p) : + ¬Subpattern (.app p₃ p₄) p₁ := by + cases H with | @mk i c hi => + obtain ⟨rfl, -⟩ := RecursorIotaPattern.app_subpattern h + intro hsub + obtain ⟨j', hj', heq'⟩ := hsub.varN_const_le + cases j' <;> exact absurd heq' (by simp [Pattern.varN]) + +/-- `Params.pat_app_l_uniq` for the block's pattern set. -/ +theorem IotaPat.pat_app_l_uniq {hcl : gen.RuleClosure} {p p' : Pattern} + {r : p.RHS × p.Check} {r' : p'.RHS × p'.Check} {p₁ p₂ p₁' p₂' p₃ : Pattern} + (H : gen.IotaPat hcl p r) (H' : gen.IotaPat hcl p' r') + (h : Subpattern (.app p₁ p₂) p) (h' : Subpattern (.app p₁' p₂') p') + (h₃ : Subpattern (.var p₃) p₁) : p₁'.inter p₃ = none := by + cases H with | @mk i c hi => + cases H' with | @mk i' c' hi' => + obtain ⟨rfl, -⟩ := RecursorIotaPattern.app_subpattern h + obtain ⟨rfl, -⟩ := RecursorIotaPattern.app_subpattern h' + obtain ⟨j, hj, heq⟩ := h₃.varN_const_le + cases j with + | zero => exact absurd heq (by simp [Pattern.varN]) + | succ j'' => + rw [show Pattern.varN (.const (gen.ruleRecName c)) (j'' + 1) = + .var (Pattern.varN (.const (gen.ruleRecName c)) j'') from rfl] at heq + injection heq with heq' + subst heq' + by_cases hname : gen.ruleRecName c' = gen.ruleRecName c + · obtain ⟨-, hIdx⟩ := gen.ruleRecName_inj (List.mem_of_getElem? hi') + (List.mem_of_getElem? hi) hname + have hM : gen.ruleMajorArity c' = gen.ruleMajorArity c := by + rw [gen.ruleMajorArity_eq (List.mem_of_getElem? hi'), + gen.ruleMajorArity_eq (List.mem_of_getElem? hi), hIdx] + rw [hname] + exact Pattern.varN_const_inter_of_ne_arity (by omega) _ _ + · exact Pattern.varN_const_inter_of_ne_name hname _ _ + +/-- `Params.pat_app_uniq` for the block's pattern set. -/ +theorem IotaPat.pat_app_uniq {hcl : gen.RuleClosure} {p p' : Pattern} + {r : p.RHS × p.Check} {r' : p'.RHS × p'.Check} + {p₁ p₂ p₁' p₂' p₃ p₃' : Pattern} + (H : gen.IotaPat hcl p r) (H' : gen.IotaPat hcl p' r') + (h : Subpattern (.app p₁ p₂) p) (h' : Subpattern (.app p₁' p₂') p') + (h₃ : Subpattern p₃ p₁) (h₃' : Subpattern p₃' p₂') : p₃.inter p₃' = none := by + cases H with | @mk i c hi => + cases H' with | @mk i' c' hi' => + obtain ⟨rfl, -⟩ := RecursorIotaPattern.app_subpattern h + obtain ⟨-, rfl⟩ := RecursorIotaPattern.app_subpattern h' + obtain ⟨j, hj, rfl⟩ := h₃.varN_const_le + obtain ⟨j', hj', rfl⟩ := h₃'.varN_const_le + refine Pattern.varN_const_inter_of_ne_name ?_ _ _ + obtain ⟨t, family, ht, ho, -, -, -⟩ := + gen.flatCtors_anatomy (List.mem_of_getElem? hi) + have hrec : gen.ruleRecName c = (.str family.raw.name "rec" : Name) := by + rw [ruleRecName, ho, gen.familyNameAt_eq ht] + rw [hrec] + exact gen.recName_ne_ctorName (List.mem_of_getElem? ht) + (List.mem_of_getElem? hi') + +/-! ## Axiom closures of the generic pattern facts -/ + +#guard_named_axioms Ix.Theory.Named.VInductDecl.BlockGenerationChecked.ruleLhsBody_matches [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.BlockGenerationChecked.view_resultIndices_length [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.BlockGenerationChecked.rulePattern_inj [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.BlockGenerationChecked.IotaPat.pat_simple [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.BlockGenerationChecked.IotaPat.recover [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.BlockGenerationChecked.IotaPat.pat_uniq [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.BlockGenerationChecked.IotaPat.pat_app_l [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.BlockGenerationChecked.IotaPat.pat_app_l_uniq [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.BlockGenerationChecked.IotaPat.pat_app_uniq [propext, Quot.sound] + +end BlockGenerationChecked + +end VInductDecl + +end Ix.Theory.Named diff --git a/Ix/Theory/Named/Typing/InductivePatternWF.lean b/Ix/Theory/Named/Typing/InductivePatternWF.lean new file mode 100644 index 000000000..d01b7305a --- /dev/null +++ b/Ix/Theory/Named/Typing/InductivePatternWF.lean @@ -0,0 +1,943 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Typing.InductivePattern +import Ix.Theory.Named.Typing.UniqueTyping + +open Ix.Theory (VLevel) + +/-! # Pattern soundness for generated iota rules + +The typed β-collapse layer for Spec-10B: applying a lambda tower to a +well-typed argument spine is definitionally equal to the iterated +instantiation of its body (`IsDefEq.appN_lamN`), applications are +congruent along spines (`IsDefEq.appN_congr`, `IsDefEq.appN_defEq` over +`SpineDefEq`), and a matched pattern's captures are exactly the spine +arguments (`varN_matches_paths`). `pat_wf` then proves that a successful +match whose checks hold is definitionally equal to its RHS template — by +applying the registered `addInduct` rule tower to the captured arguments +and β-collapsing both readings. -/ + +namespace Ix.Theory.Named + +open VExpr + +namespace VExpr + +/-- Instantiation pushes under a lambda telescope, mirroring +`instN_forallN`. -/ +theorem instN_lamN (a : VExpr) : ∀ (tel : List VExpr) (X : VExpr) (k : Nat), + (lamN tel X).inst a k = lamN (instTelN a tel k) (X.inst a (k + tel.length)) + | [], _, _ => rfl + | A :: tel, X, k => by + show VExpr.lam _ _ = VExpr.lam _ _ + rw [instN_lamN a tel X (k+1), + show k+1+tel.length = k+(tel.length+1) from by omega] + rfl + +/-- Universe instantiation pushes under a lambda telescope. -/ +theorem instL_lamN (ls : List VLevel) : ∀ (As : List VExpr) (e : VExpr), + (lamN As e).instL ls = lamN (As.map (instL ls)) (e.instL ls) + | [], _ => rfl + | A :: As, e => by + show VExpr.lam _ _ = VExpr.lam _ _ + rw [instL_lamN ls As e] + +end VExpr + +/-- Matching a constant `varN` tower captures exactly the spine arguments: +the `varNPaths` read back the argument list. -/ +theorem Pattern.varN_matches_paths {c : Name} {m1 : List VLevel} : + ∀ (n : Nat) (as : List VExpr) {f : VExpr} {m2}, + (Pattern.varN (.const c) n).Matches (VExpr.appN f as) m1 m2 → + as.length = n → + (Pattern.varNPaths (.const c) n).map m2 = as := by + intro n + induction n with + | zero => + intro as f m2 H hlen + obtain rfl : as = [] := List.length_eq_zero_iff.1 hlen + rfl + | succ n ih => + intro as f m2 H hlen + have hne : as ≠ [] := by rintro rfl; simp at hlen + obtain ⟨as', a, rfl⟩ : ∃ as' a, as = as' ++ [a] := + ⟨as.dropLast, as.getLast hne, (List.dropLast_concat_getLast hne).symm⟩ + rw [VExpr.appN_append] at H + have has : as'.length = n := by simpa using hlen + cases H with + | var h => + show ((Pattern.varNPaths (.const c) n).map some ++ [none]).map _ = + as' ++ [a] + rw [List.map_append, List.map_map] + exact congrArg (· ++ [a]) (ih as' h has) + +/-- Applying an RHS template spine computes to the applied template +values. -/ +theorem Pattern.RHS.appN_apply {p : Pattern} (m1 : List VLevel) + (m2 : p.Path → VExpr) : + ∀ (f : p.RHS) (as : List (p.RHS)), + (Pattern.RHS.appN f as).apply m1 m2 = + VExpr.appN (f.apply m1 m2) (as.map (Pattern.RHS.apply m1 m2)) + | _, [] => rfl + | f, a :: as => by + show (Pattern.RHS.appN (.app f a) as).apply m1 m2 = _ + rw [Pattern.RHS.appN_apply m1 m2 (.app f a) as] + rfl + +/-- A `HeadConstN` spine names its argument list. -/ +theorem HeadConstN.exists_appN {c : Name} {ls : List VLevel} : + ∀ {n : Nat} {e : VExpr}, HeadConstN c ls n e → + ∃ as : List VExpr, e = VExpr.appN (.const c ls) as ∧ as.length = n + | _, _, .const => ⟨[], rfl, rfl⟩ + | _, _, .app (a := a) h => + let ⟨as, he, hl⟩ := h.exists_appN + ⟨as ++ [a], by rw [VExpr.appN_append, ← he]; rfl, by simp [hl]⟩ + +namespace VExpr + +/-- The value of a bound variable under iterated instantiation: the spine +argument at its reverse position. -/ +theorem instRev_bvar_lt : ∀ (es : List VExpr) {i : Nat} (h : i < es.length), + instRev (.bvar i) es = es[es.length - 1 - i]'(by omega) + | e :: es, i, h => by + rcases Nat.lt_or_ge i es.length with h' | h' + · rw [show instRev (.bvar i) (e :: es) = instRev (.bvar i) es from + instRev_bvar_lt_cons es e h', instRev_bvar_lt es h'] + simp only [show (e :: es).length - 1 - i = (es.length - 1 - i) + 1 from by + simp only [List.length_cons]; omega, List.getElem_cons_succ] + · obtain rfl : i = es.length := by + simp only [List.length_cons] at h; omega + show instRev (instVar es.length e es.length) es = _ + rw [show instVar es.length e es.length = liftN es.length e from by + simp [instVar]] + rw [instRev_liftN_len] + simp only [show (e :: es).length - 1 - es.length = 0 from by + simp only [List.length_cons]; omega, List.getElem_cons_zero] + +/-- Iterated instantiation of a reverse bound-variable segment reads back +the corresponding spine segment. -/ +theorem map_instRev_bvarRevRange_seg (es : List VExpr) : + ∀ (q off : Nat), off + q ≤ es.length → + (bvarRevRange off q).map (instRev · es) = + (es.drop (es.length - off - q)).take q := by + intro q + induction q with + | zero => intro off h; simp [VExpr.bvarRevRange] + | succ q ih => + intro off h + show instRev (.bvar (off + q)) es :: (bvarRevRange off q).map (instRev · es) = _ + rw [instRev_bvar_lt es (by omega), ih off (by omega)] + have hd : es.length - off - (q + 1) < es.length := by omega + simp only [show es.length - 1 - (off + q) = es.length - off - (q + 1) from by + omega, show es.length - off - q = (es.length - off - (q + 1)) + 1 from by + omega] + rw [List.drop_eq_getElem_cons hd, List.take_succ_cons] + +end VExpr + +/-! ## Typed β-collapse of applied telescopes -/ + +/-- Instantiating below a reversed telescope, mirroring +`Ctx.LiftN.consTel`. -/ +theorem Ctx.InstN.consTel {Γ₀ : List VExpr} {e₀ A₀ : VExpr} : + ∀ (As : List VExpr) {k : Nat} {Γ Γ' : List VExpr}, + Ctx.InstN Γ₀ e₀ A₀ k Γ Γ' → + Ctx.InstN Γ₀ e₀ A₀ (As.length + k) (As.reverse ++ Γ) + ((VExpr.instTelN e₀ As k).reverse ++ Γ') + | [], k, Γ, Γ', W => by simpa [VExpr.instTelN] using W + | A :: As, k, Γ, Γ', W => by + have h := Ctx.InstN.consTel As (Ctx.InstN.succ (A := A) W) + rw [show As.length + (k+1) = (A :: As).length + k from by simp; omega] at h + simpa [VExpr.instTelN, List.append_assoc] using h + +/-- Instantiating a telescope's context. -/ +theorem VEnv.OnTel.instN {env : VEnv} (henv : env.Ordered) {U : Nat} + {Γ₀ : List VExpr} {e₀ A₀ : VExpr} (h₀ : env.HasType U Γ₀ e₀ A₀) : + ∀ {As : List VExpr} {k : Nat} {Γ Γ' : List VExpr}, + Ctx.InstN Γ₀ e₀ A₀ k Γ Γ' → VEnv.OnTel env U Γ As → + VEnv.OnTel env U Γ' (VExpr.instTelN e₀ As k) + | [], _, _, _, _, _ => trivial + | _ :: _, _, _, _, W, ⟨⟨u, hA⟩, hT⟩ => + ⟨⟨u, hA.instN henv W h₀⟩, VEnv.OnTel.instN henv h₀ W.succ hT⟩ + +/-- Pointwise defeq of two application spines against a peeled pi type. -/ +inductive VEnv.SpineDefEq (env : VEnv) (U : Nat) (Γ : List VExpr) : + VExpr → List VExpr → List VExpr → VExpr → Prop where + | nil : VEnv.SpineDefEq env U Γ A [] [] A + | cons : env.IsDefEq U Γ a a' A₁ → + VEnv.SpineDefEq env U Γ (A₂.inst a) es es' B → + VEnv.SpineDefEq env U Γ (.forallE A₁ A₂) (a :: es) (a' :: es') B + +/-- Iterated application congruence along a pointwise defeq spine. -/ +theorem VEnv.IsDefEq.appN_defEq {env : VEnv} {U : Nat} {Γ : List VExpr} : + ∀ {es es' : List VExpr} {F B X Y : VExpr}, + env.IsDefEq U Γ X Y F → VEnv.SpineDefEq env U Γ F es es' B → + env.IsDefEq U Γ (VExpr.appN X es) (VExpr.appN Y es') B + | [], _, _, _, _, _, h, .nil => h + | a :: _, a' :: _, _, _, X, Y, h, .cons ha hrest => + VEnv.IsDefEq.appN_defEq (X := X.app a) (Y := Y.app a') (h.appDF ha) hrest + +/-- A well-typed spine is a reflexive defeq spine. -/ +theorem VEnv.SpineWF.toSpineDefEq {env : VEnv} {U : Nat} {Γ : List VExpr} : + ∀ {es : List VExpr} {F B : VExpr}, env.SpineWF U Γ F es B → + VEnv.SpineDefEq env U Γ F es es B + | [], _, _, .nil => .nil + | _ :: _, _, _, .cons ha hrest => .cons ha hrest.toSpineDefEq + +/-- Iterated application congruence in the function position. -/ +theorem VEnv.IsDefEq.appN_congr {env : VEnv} {U : Nat} {Γ : List VExpr} + {es : List VExpr} {F B X Y : VExpr} + (h : env.IsDefEq U Γ X Y F) (hs : env.SpineWF U Γ F es B) : + env.IsDefEq U Γ (VExpr.appN X es) (VExpr.appN Y es) B := + h.appN_defEq hs.toSpineDefEq + +/-- A registered equation remains available after environment growth. + +This is the primitive transport operation for consumer-certified extension +rules: `VEnv.LE` transports registration, while the core `.extra` constructor +still requires the exact universe instantiation side conditions. -/ +theorem VEnv.LE.extra {env env' : VEnv} (henv : env ≤ env') {U : Nat} + {Γ : List VExpr} {df : VDefEq} {ls : List VLevel} + (hreg : env.defeqs df) (hlevels : ∀ l ∈ ls, l.WF U) + (hlevelsLength : ls.length = df.uvars) : + env'.IsDefEq U Γ (df.lhs.instL ls) (df.rhs.instL ls) + (df.type.instL ls) := + .extra (henv.defeqs hreg) hlevels hlevelsLength + +/-- Transport a registered equation through environment growth and then +apply it to a well-typed spine. This is the beta-tower consumer boundary: +registration supplies only the tower equality; application congruence and +spine typing remain explicit proof obligations. -/ +theorem VEnv.LE.extra_appN {env env' : VEnv} (henv : env ≤ env') {U : Nat} + {Γ : List VExpr} {df : VDefEq} {ls : List VLevel} {args : List VExpr} + {B : VExpr} (hreg : env.defeqs df) + (hlevels : ∀ l ∈ ls, l.WF U) (hlevelsLength : ls.length = df.uvars) + (hspine : env.SpineWF U Γ (df.type.instL ls) args B) : + env'.IsDefEq U Γ + (VExpr.appN (df.lhs.instL ls) args) + (VExpr.appN (df.rhs.instL ls) args) B := + (henv.extra hreg hlevels hlevelsLength).appN_congr (hspine.mono henv) + +/-- The symmetric applied transport is derived, not a second trusted +extension direction. -/ +theorem VEnv.LE.extra_appN_symm {env env' : VEnv} (henv : env ≤ env') + {U : Nat} {Γ : List VExpr} {df : VDefEq} {ls : List VLevel} + {args : List VExpr} {B : VExpr} (hreg : env.defeqs df) + (hlevels : ∀ l ∈ ls, l.WF U) (hlevelsLength : ls.length = df.uvars) + (hspine : env.SpineWF U Γ (df.type.instL ls) args B) : + env'.IsDefEq U Γ + (VExpr.appN (df.rhs.instL ls) args) + (VExpr.appN (df.lhs.instL ls) args) B := + (henv.extra_appN hreg hlevels hlevelsLength hspine).symm + +/-- Applying a lambda telescope to a full well-typed spine collapses to the +iterated instantiation of its body. -/ +theorem VEnv.IsDefEq.appN_lamN {env : VEnv} (henv : env.Ordered) {U : Nat} : + ∀ {As : List VExpr} {Γ : List VExpr} {body T B : VExpr} {es : List VExpr}, + VEnv.OnTel env U Γ As → + env.HasType U (As.reverse ++ Γ) body T → + env.SpineWF U Γ (VExpr.forallN As T) es B → + es.length = As.length → + env.IsDefEq U Γ (VExpr.appN (VExpr.lamN As body) es) + (VExpr.instRev body es) B + | [], Γ, body, T, B, es, _, hb, hs, hlen => by + obtain rfl : es = [] := List.length_eq_zero_iff.1 hlen + obtain rfl : T = B := hs.nil_inv + exact hb + | A :: As, Γ, body, T, B, e :: es, ⟨⟨u, hA⟩, hT⟩, hb, + .cons he hrest, hlen => by + have hb' : env.HasType U (As.reverse ++ (A :: Γ)) body T := by + simpa [List.append_assoc] using hb + have hlam : env.HasType U (A :: Γ) (VExpr.lamN As body) + (VExpr.forallN As T) := VEnv.HasType.lamN hT hb' + have hbeta := VEnv.IsDefEq.beta hlam he + rw [VExpr.instN_lamN, Nat.zero_add] at hbeta + have hlen2 : es.length = As.length := by simpa using hlen + have hT' : VEnv.OnTel env U Γ (VExpr.instTelN e As 0) := + VEnv.OnTel.instN henv he .zero hT + have hb'' : env.HasType U ((VExpr.instTelN e As 0).reverse ++ Γ) + (body.inst e As.length) (T.inst e As.length) := by + have W := Ctx.InstN.consTel (Γ₀ := Γ) (e₀ := e) (A₀ := A) As .zero + have := hb'.instN henv W he + simpa using this + have hrest' : env.SpineWF U Γ + (VExpr.forallN (VExpr.instTelN e As 0) (T.inst e As.length)) es B := by + rw [VExpr.instN_forallN] at hrest + simpa using hrest + have hlen' : es.length = (VExpr.instTelN e As 0).length := by + rw [VExpr.instTelN_length]; exact hlen2 + have IH := VEnv.IsDefEq.appN_lamN henv hT' hb'' hrest' hlen' + have hstep := VEnv.IsDefEq.appN_congr hbeta hrest + show env.IsDefEq U Γ + (VExpr.appN ((VExpr.lam A (VExpr.lamN As body)).app e) es) + (VExpr.instRev (body.inst e es.length) es) B + rw [hlen2] + exact hstep.trans IH + +/-- Instantiate a terminal definitional equality through a saturated telescope spine. -/ +theorem VEnv.SpineWF.instRev_defeq + {env : VEnv} (henv : env.Ordered) {U : Nat} {Γ : List VExpr} : + ∀ {As : List VExpr} {C C' T : VExpr} {es : List VExpr} {B : VExpr}, + env.SpineWF U Γ (VExpr.forallN As C) es B → + es.length = As.length → + env.IsDefEq U (As.reverse ++ Γ) C C' T → + env.IsDefEq U Γ (VExpr.instRev C es) (VExpr.instRev C' es) + (VExpr.instRev T es) + | [], C, C', T, [], B, hspine, _, hterminal => by + simpa [VExpr.instRev] using hterminal + | [], _, _, _, _ :: _, _, _, hlen, _ => by simp at hlen + | _ :: _, _, _, _, [], _, _, hlen, _ => by simp at hlen + | A :: As, C, C', T, e :: es, B, + .cons he hrest, hlen, hterminal => by + have hlen' : es.length = As.length := by simpa using hlen + have W := Ctx.InstN.consTel (Γ₀ := Γ) (e₀ := e) (A₀ := A) As .zero + have hterminal₀ : env.IsDefEq U (As.reverse ++ A :: Γ) C C' T := by + simpa [List.reverse_cons, List.append_assoc] using hterminal + have hterminal' := hterminal₀.instN henv he W + have hrest' : env.SpineWF U Γ + (VExpr.forallN (VExpr.instTelN e As 0) + (C.inst e As.length)) es B := by + rw [VExpr.instN_forallN] at hrest + simpa using hrest + have hout := VEnv.SpineWF.instRev_defeq henv hrest' + (by simpa [VExpr.instTelN_length] using hlen') hterminal' + simpa [VExpr.instRev, hlen'] using hout + +/-- Iterated inversion of a lambda tower's typing: the telescope is +well-formed and the body is typed under it. -/ +theorem VEnv.HasType.lamN_wf {env : VEnv} {U : Nat} (henv : env.Ordered) : + ∀ {As : List VExpr} {Γ : List VExpr} {body V : VExpr}, + OnCtx Γ (env.IsType U) → + env.HasType U Γ (VExpr.lamN As body) V → + VEnv.OnTel env U Γ As ∧ + ∃ T₀, env.HasType U (As.reverse ++ Γ) body T₀ + | [], Γ, body, V, _, H => ⟨trivial, V, H⟩ + | A :: As, Γ, body, V, hΓ, H => by + obtain ⟨⟨u, hA⟩, W, hrest⟩ := VEnv.HasType.lam_inv henv hΓ H + obtain ⟨hT, T₀, hbody⟩ := + VEnv.HasType.lamN_wf henv (As := As) (Γ := A :: Γ) ⟨hΓ, u, hA⟩ hrest + exact ⟨⟨⟨u, hA⟩, hT⟩, T₀, by simpa [List.append_assoc] using hbody⟩ + +/-- The levels of a `HeadConstN` spine are unique. -/ +theorem HeadConstN.levels_uniq {c : Name} : + ∀ {n : Nat} {e : VExpr} {ls ls' : List VLevel}, + HeadConstN c ls n e → HeadConstN c ls' n e → ls = ls' + | _, _, _, _, .const, .const => rfl + | _, _, _, _, .app h, .app h' => h.levels_uniq h' + +/-- Zip a well-typed spine with pointwise defeqs into a defeq spine. +Reflexive entries need no defeq evidence. -/ +theorem VEnv.SpineWF.defEq_of_pointwise {env : VEnv} (henv : env.WF) + {U : Nat} {Γ : List VExpr} (hΓ : OnCtx Γ (env.IsType U)) : + ∀ {es es' : List VExpr} {F B : VExpr}, + env.SpineWF U Γ F es B → + List.Forall₂ (fun a a' => a = a' ∨ env.IsDefEqU U Γ a a') es es' → + VEnv.SpineDefEq env U Γ F es es' B + | [], [], _, _, .nil, .nil => .nil + | _ :: _, _ :: _, _, _, .cons he hrest, .cons hd htl => by + refine .cons ?_ (hrest.defEq_of_pointwise henv hΓ htl) + rcases hd with rfl | hd + · exact he + · exact VEnv.IsDefEqU.of_l henv hΓ hd he + +/-- Unfold the `OK` predicate through a folded list of defeq checks. -/ +theorem Pattern.Check.OK.of_foldr {p : Pattern} {α : Type _} + {df : VExpr → VExpr → Prop} {m1 : List VLevel} {m2 : p.Path → VExpr} + (f g : α → p.RHS) : + ∀ {xs : List α} {rest : p.Check}, + ((xs.foldr (fun x acc => Pattern.Check.defeq (f x) (g x) acc) + rest).OK df m1 m2) → + (∀ x ∈ xs, df ((f x).apply m1 m2) ((g x).apply m1 m2)) ∧ + rest.OK df m1 m2 + | [], _, h => ⟨nofun, h⟩ + | _ :: xs, rest, h => by + obtain ⟨h1, h2⟩ := h + obtain ⟨h3, h4⟩ := Pattern.Check.OK.of_foldr f g (xs := xs) h2 + refine ⟨fun x hx => ?_, h4⟩ + rcases List.mem_cons.1 hx with rfl | hx + · exact h1 + · exact h3 x hx + +/-- Build a pointwise relation between two mapped lists from their zip. -/ +private theorem forall₂_zip_map {α β : Type _} (F : α → VExpr) (G : β → VExpr) + (R : VExpr → VExpr → Prop) : + ∀ (xs : List α) (ys : List β), xs.length = ys.length → + (∀ p ∈ xs.zip ys, R (F p.1) (G p.2)) → + List.Forall₂ R (xs.map F) (ys.map G) + | [], [], _, _ => .nil + | x :: xs, y :: ys, hlen, hall => + .cons (hall (x, y) (.head _)) + (forall₂_zip_map F G R xs ys (by simpa using hlen) + fun p hp => hall p (.tail _ hp)) + | [], _ :: _, hlen, _ => by simp at hlen + | _ :: _, [], hlen, _ => by simp at hlen + +/-- Universe instantiation fixes a reverse bound-variable range. -/ +theorem VExpr.bvarRevRange_map_instL (ls : List VLevel) : + ∀ (off m : Nat), + (VExpr.bvarRevRange off m).map (VExpr.instL ls) = + VExpr.bvarRevRange off m + | _, 0 => rfl + | off, m+1 => by + simp only [VExpr.bvarRevRange, List.map_cons, VExpr.instL, + VExpr.bvarRevRange_map_instL ls off m] + +/-- A well-formed telescope extends a well-formed context. -/ +theorem VEnv.OnTel.onCtx {env : VEnv} {U : Nat} : + ∀ {As Γ : List VExpr}, OnCtx Γ (env.IsType U) → + VEnv.OnTel env U Γ As → OnCtx (As.reverse ++ Γ) (env.IsType U) + | [], _, hΓ, _ => hΓ + | A :: As, Γ, hΓ, ⟨hA, hT⟩ => by + simpa [List.append_assoc] using + VEnv.OnTel.onCtx (As := As) (Γ := A :: Γ) ⟨hΓ, hA⟩ hT + +/-- Every argument of a well-typed application spine is well-typed. -/ +theorem VEnv.HasType.appN_args_wf {env : VEnv} (henv : env.WF) {U : Nat} + {Γ : List VExpr} (hΓ : OnCtx Γ (env.IsType U)) : + ∀ (n : Nat) (es : List VExpr), es.length = n → ∀ {f B : VExpr}, + env.HasType U Γ (VExpr.appN f es) B → + ∀ e ∈ es, ∃ T, env.HasType U Γ e T := by + intro n + induction n with + | zero => + intro es hlen f B H e he + obtain rfl := List.length_eq_zero_iff.1 hlen + cases he + | succ n ih => + intro es hlen f B H e he + have hne : es ≠ [] := by rintro rfl; simp at hlen + obtain ⟨es', a, rfl⟩ : ∃ es' a, es = es' ++ [a] := + ⟨es.dropLast, es.getLast hne, (List.dropLast_concat_getLast hne).symm⟩ + rw [VExpr.appN_append] at H + have H' : env.HasType U Γ ((VExpr.appN f es').app a) B := H + obtain ⟨A₁, B₁, hf, ha⟩ := H'.app_inv henv hΓ + rcases List.mem_append.1 he with he' | he' + · exact ih es' (by simpa using hlen) hf e he' + · obtain rfl : e = a := by simpa using he' + exact ⟨A₁, ha⟩ + +/-- Iterated inversion of a pi tower's typing: the telescope is well formed +and the body is typed under it. -/ +theorem VEnv.HasType.forallN_wf {env : VEnv} {U : Nat} (henv : env.Ordered) : + ∀ {As : List VExpr} {Γ : List VExpr} {body V : VExpr}, + env.HasType U Γ (VExpr.forallN As body) V → + VEnv.OnTel env U Γ As ∧ ∃ V', env.HasType U (As.reverse ++ Γ) body V' + | [], _, _, V, H => ⟨trivial, V, H⟩ + | A :: As, Γ, body, V, H => by + obtain ⟨⟨u, hA⟩, v, hB⟩ := VEnv.HasType.forallE_inv henv H + obtain ⟨hT, V', hbody⟩ := VEnv.HasType.forallN_wf henv (As := As) hB + exact ⟨⟨⟨u, hA⟩, hT⟩, V', by simpa [List.append_assoc] using hbody⟩ + +private theorem forall₂_refl_or {R : VExpr → VExpr → Prop} : + ∀ (l : List VExpr), List.Forall₂ (fun a a' => a = a' ∨ R a a') l l + | [] => .nil + | _ :: l => .cons (Or.inl rfl) (forall₂_refl_or l) + +private theorem forall₂_append {R : VExpr → VExpr → Prop} : + ∀ {l₁ l₂ l₁' l₂' : List VExpr}, List.Forall₂ R l₁ l₂ → + List.Forall₂ R l₁' l₂' → List.Forall₂ R (l₁ ++ l₁') (l₂ ++ l₂') + | [], [], _, _, .nil, h => h + | _ :: _, _ :: _, _, _, .cons hd htl, h => .cons hd (forall₂_append htl h) + +namespace VInductDecl + +namespace BlockGenerationChecked + +variable {source : VInductDecl} (gen : source.BlockGenerationChecked) + +/-! ## Named shapes of one generated rule -/ + +theorem rule_type (i : Nat) (c : NormalizedBlockCtor) : + (gen.rule i c).type = + VExpr.forallN (gen.ruleBinders c) + (VExpr.appN + (.bvar (gen.familyCount - 1 - c.owner + gen.minorCount + + gen.ruleFieldCount c)) + (gen.ruleIdx c ++ [gen.ruleCtorApp c])) := rfl + +theorem rule_uvars (i : Nat) (c : NormalizedBlockCtor) : + (gen.rule i c).uvars = gen.recUvars := rfl + +theorem paramsTel_length : gen.paramsTel.length = source.nparams := by + show ((generationParams gen.block.rawParams gen.block.checked.params).map + (VExpr.instL gen.sourceLevels)).length = _ + rw [List.length_map] + exact (generationParams_length_of_eq gen.shape.2.1).trans gen.shape.1 + +theorem ruleBinders_length (c : NormalizedBlockCtor) : + (gen.ruleBinders c).length = + source.nparams + gen.familyCount + gen.minorCount + + gen.ruleFieldCount c := by + simp only [ruleBinders, List.length_append, gen.paramsTel_length, + motiveTypes, gen.motiveTypesAux_length, minorTypes, + gen.minorTypesAux_length, VExpr.liftTelN_length, ruleFieldCount] + try omega + +/-- The instantiated left body as one flattened application spine. -/ +theorem ruleLhsBody_instL (c : NormalizedBlockCtor) {m1 : List VLevel} + (hlen1 : m1.length = gen.recUvars) : + (gen.ruleLhsBody c).instL m1 = + VExpr.appN (.const (gen.ruleRecName c) m1) + (VExpr.bvarRevRange (gen.ruleFieldCount c) + (source.nparams + gen.familyCount + gen.minorCount) ++ + (gen.ruleIdx c).map (VExpr.instL m1) ++ + [(gen.ruleCtorApp c).instL m1]) := by + show (VExpr.appN + (VExpr.appN (.const (gen.ruleRecName c) gen.recLevels) + (VExpr.bvarRevRange (gen.ruleFieldCount c) + (source.nparams + gen.familyCount + gen.minorCount))) + (gen.ruleIdx c ++ [gen.ruleCtorApp c])).instL m1 = _ + rw [← VExpr.appN_append, VExpr.instL_appN] + show VExpr.appN (.const (gen.ruleRecName c) + (gen.recLevels.map (VLevel.inst m1))) _ = _ + rw [show gen.recLevels.map (VLevel.inst m1) = m1 from + VLevel.inst_map_id hlen1] + rw [List.map_append, List.map_append, VExpr.bvarRevRange_map_instL, + List.append_assoc] + rfl + +/-- The instantiated major premise of the rule body. -/ +theorem ruleCtorApp_instL (c : NormalizedBlockCtor) (m1 : List VLevel) : + (gen.ruleCtorApp c).instL m1 = + VExpr.appN (.const c.ctor.raw.name + (gen.sourceLevels.map (VLevel.inst m1))) + (VExpr.bvarRevRange + (gen.ruleFieldCount c + (gen.familyCount + gen.minorCount)) + source.nparams ++ + VExpr.bvarRevRange 0 (gen.ruleFieldCount c)) := by + show (VExpr.appN (.const c.ctor.raw.name gen.sourceLevels) _).instL m1 = _ + rw [VExpr.instL_appN, List.map_append, VExpr.bvarRevRange_map_instL, + VExpr.bvarRevRange_map_instL] + rfl + +/-- The captured template values are exactly the shared prefix of the +recursor spine and the field suffix of the major premise. -/ +private theorem captureArgs_apply {c : NormalizedBlockCtor} {m1 : List VLevel} + {g1 : Pattern.Path + (Pattern.varN (.const (gen.ruleRecName c)) (gen.ruleMajorArity c)) → VExpr} + {g2 : Pattern.Path + (Pattern.varN (.const c.ctor.raw.name) (gen.ruleArgArity c)) → VExpr} + {fArgs aArgs : List VExpr} + (hg1 : (Pattern.varNPaths (.const (gen.ruleRecName c)) + (gen.ruleMajorArity c)).map g1 = fArgs) + (hg2 : (Pattern.varNPaths (.const c.ctor.raw.name) + (gen.ruleArgArity c)).map g2 = aArgs) : + (gen.captureArgs c).map + (Pattern.RHS.apply (p := (gen.rulePattern c).toPattern) m1 + (Sum.elim g1 g2)) = + fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams := by + rw [captureArgs, List.map_append, List.map_map, List.map_map] + show List.map g1 (List.take + (source.nparams + gen.familyCount + gen.minorCount) + (Pattern.varNPaths (.const (gen.ruleRecName c)) + (gen.ruleMajorArity c))) ++ + List.map g2 (List.drop source.nparams + (Pattern.varNPaths (.const c.ctor.raw.name) + (gen.ruleArgArity c))) = _ + rw [List.map_take, List.map_drop, hg1, hg2] + +/-- Pattern soundness for one certified block (`pat_wf`): a successful match +of a rule's pattern whose checks hold is definitionally equal to the +instantiated RHS template, derived from the rule defeq registered by +`addInduct` via typed β-collapse. The redex arrives decomposed into its +recursor and constructor spines with spine-form typing, and the major +premise's levels pinned to the rule's source levels; both are exactly what +a verified reduction site holds. -/ +theorem pat_wf {env : VEnv} (henv : env.WF) {univs : Nat} {Γ : List VExpr} + (hΓ : OnCtx Γ (env.IsType univs)) + (hcl : gen.RuleClosure) + {i : Nat} {c : NormalizedBlockCtor} (h : gen.ruleEntry i c) + (hreg : env.defeqs (gen.rule i c)) + (hwf : (gen.rule i c).WF env) + {m1 : List VLevel} {m2} + (hm1 : ∀ l ∈ m1, l.WF univs) (hlen1 : m1.length = gen.recUvars) + {fArgs aArgs : List VExpr} + (hMlen : fArgs.length = gen.ruleMajorArity c) + (hNlen : aArgs.length = gen.ruleArgArity c) + (hm : ((gen.rulePattern c).toPattern).Matches + (.app (VExpr.appN (.const (gen.ruleRecName c) m1) fArgs) + (VExpr.appN (.const c.ctor.raw.name + (gen.sourceLevels.map (VLevel.inst m1))) aArgs)) m1 m2) + (hck : (gen.ruleCheck hcl (List.mem_of_getElem? h)).OK + (env.IsDefEqU univs Γ) m1 m2) + {Frec Ae : VExpr} + (hehead : env.HasType univs Γ (.const (gen.ruleRecName c) m1) Frec) + (hespine : env.SpineWF univs Γ Frec + (fArgs ++ [VExpr.appN (.const c.ctor.raw.name + (gen.sourceLevels.map (VLevel.inst m1))) aArgs]) Ae) + {Fctor Actor : VExpr} + (hctorhead : env.HasType univs Γ + (.const c.ctor.raw.name (gen.sourceLevels.map (VLevel.inst m1))) Fctor) + (hctorspine : env.SpineWF univs Γ Fctor aArgs Actor) + {B : VExpr} + (hcaps : env.SpineWF univs Γ ((gen.rule i c).type.instL m1) + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams) B) : + env.IsDefEqU univs Γ + (.app (VExpr.appN (.const (gen.ruleRecName c) m1) fArgs) + (VExpr.appN (.const c.ctor.raw.name + (gen.sourceLevels.map (VLevel.inst m1))) aArgs)) + ((gen.ruleRHS hcl h).apply m1 m2) := by + have henvo := henv.ordered + have hc := List.mem_of_getElem? h + cases hm with + | @app _ _ _ g1 _ _ f2 g2 h1 h2 => + -- canonical captures + have hg1 : (Pattern.varNPaths (.const (gen.ruleRecName c)) + (gen.ruleMajorArity c)).map g1 = fArgs := + Pattern.varN_matches_paths _ fArgs h1 hMlen + have hg2 : (Pattern.varNPaths (.const c.ctor.raw.name) + (gen.ruleArgArity c)).map g2 = aArgs := + Pattern.varN_matches_paths _ aArgs h2 hNlen + have hcapsVals := gen.captureArgs_apply (m1 := m1) hg1 hg2 + -- length bookkeeping + have hcommon_le : source.nparams + gen.familyCount + gen.minorCount ≤ + gen.ruleMajorArity c := Nat.le_add_right _ _ + have hnp_le : source.nparams ≤ gen.ruleArgArity c := Nat.le_add_right _ _ + have htakelen : (fArgs.take (source.nparams + gen.familyCount + + gen.minorCount)).length = + source.nparams + gen.familyCount + gen.minorCount := by + rw [List.length_take, hMlen]; omega + have hdroplen : (aArgs.drop source.nparams).length = + gen.ruleFieldCount c := by + rw [List.length_drop, hNlen] + show gen.ruleArgArity c - source.nparams = _ + simp only [ruleArgArity]; omega + have hcapslen : (fArgs.take (source.nparams + gen.familyCount + + gen.minorCount) ++ aArgs.drop source.nparams).length = + ((gen.ruleBinders c).map (VExpr.instL m1)).length := by + rw [List.length_append, htakelen, hdroplen, List.length_map, + gen.ruleBinders_length] + -- tower shapes + have htype' : (gen.rule i c).type.instL m1 = + VExpr.forallN ((gen.ruleBinders c).map (VExpr.instL m1)) + ((VExpr.appN + (.bvar (gen.familyCount - 1 - c.owner + gen.minorCount + + gen.ruleFieldCount c)) + (gen.ruleIdx c ++ [gen.ruleCtorApp c])).instL m1) := by + rw [gen.rule_type, VExpr.instL_forallN] + have hlhs' : (gen.rule i c).lhs.instL m1 = + VExpr.lamN ((gen.ruleBinders c).map (VExpr.instL m1)) + ((gen.ruleLhsBody c).instL m1) := by + rw [gen.rule_lhs, VExpr.instL_lamN] + -- tower typing at the working context + have hlhsT : env.HasType univs Γ + (VExpr.lamN ((gen.ruleBinders c).map (VExpr.instL m1)) + ((gen.ruleLhsBody c).instL m1)) + ((gen.rule i c).type.instL m1) := by + rw [← hlhs'] + exact (hwf.1.instL hm1).weak0 henvo + obtain ⟨hTel, T₀, hbody⟩ := VEnv.HasType.lamN_wf henvo hΓ hlhsT + -- β-collapse of the applied left tower + have hcapsF : env.SpineWF univs Γ + (VExpr.forallN ((gen.ruleBinders c).map (VExpr.instL m1)) + ((VExpr.appN + (.bvar (gen.familyCount - 1 - c.owner + gen.minorCount + + gen.ruleFieldCount c)) + (gen.ruleIdx c ++ [gen.ruleCtorApp c])).instL m1)) + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams) B := htype' ▸ hcaps + have hretT0 := (VEnv.SpineWF.retarget hcapsF hcapslen) T₀ + have hcollapseL := VEnv.IsDefEq.appN_lamN henvo hTel hbody hretT0 hcapslen + -- the registered defeq, applied + have hex : env.IsDefEq univs Γ ((gen.rule i c).lhs.instL m1) + ((gen.rule i c).rhs.instL m1) ((gen.rule i c).type.instL m1) := + .extra hreg hm1 hlen1 + rw [hlhs'] at hex + have happlied := VEnv.IsDefEq.appN_congr hex hcaps + -- conclusion-side template computation + have hRHS : Pattern.RHS.apply (p := (gen.rulePattern c).toPattern) m1 + (Sum.elim g1 g2) (gen.ruleRHS hcl h) = + VExpr.appN ((gen.rule i c).rhs.instL m1) + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams) := by + rw [ruleRHS] + simp only [Pattern.RHS.appN_apply, hcapsVals] + rfl + -- typing of the rule type's index spine + obtain ⟨u₀, htypeT⟩ := hlhsT.isType henvo hΓ + rw [htype'] at htypeT + obtain ⟨-, V', htypeBody⟩ := VEnv.HasType.forallN_wf henvo htypeT + have hCtxTel : OnCtx (((gen.ruleBinders c).map (VExpr.instL m1)).reverse ++ Γ) + (env.IsType univs) := VEnv.OnTel.onCtx hΓ hTel + rw [show ((VExpr.appN + (.bvar (gen.familyCount - 1 - c.owner + gen.minorCount + + gen.ruleFieldCount c)) + (gen.ruleIdx c ++ [gen.ruleCtorApp c])).instL m1) = + VExpr.appN (.bvar (gen.familyCount - 1 - c.owner + gen.minorCount + + gen.ruleFieldCount c)) + ((gen.ruleIdx c ++ [gen.ruleCtorApp c]).map (VExpr.instL m1)) from by + rw [VExpr.instL_appN]; rfl] at htypeBody + have hargsWF := VEnv.HasType.appN_args_wf henv hCtxTel _ _ rfl htypeBody + -- check extraction + unfold ruleCheck at hck + obtain ⟨hparams, hidxOK⟩ := Pattern.Check.OK.of_foldr _ _ hck + obtain ⟨hidxs, -⟩ := Pattern.Check.OK.of_foldr _ _ hidxOK + -- per-index tower collapse and check composition + have hidxLink : ∀ x ∈ (gen.ruleIdx c).attach.zip + ((Pattern.varNPaths (.const (gen.ruleRecName c)) + (gen.ruleMajorArity c)).drop + (source.nparams + gen.familyCount + gen.minorCount)), + env.IsDefEqU univs Γ (Sum.elim g1 g2 (Sum.inl x.2)) + (VExpr.instRev (x.1.1.instL m1) + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams)) := by + intro x hx + have hfact := hidxs x hx + simp only [Pattern.RHS.appN_apply, hcapsVals] at hfact + have htower : Pattern.RHS.apply (p := (gen.rulePattern c).toPattern) m1 + (Sum.elim g1 g2) + (.fixed (VExpr.lamN (gen.ruleBinders c) x.1.1) + (hcl.idxTower_closed hc x.1.1 x.1.2)) = + VExpr.lamN ((gen.ruleBinders c).map (VExpr.instL m1)) + (x.1.1.instL m1) := by + show (VExpr.lamN (gen.ruleBinders c) x.1.1).instL m1 = _ + rw [VExpr.instL_lamN] + rw [htower] at hfact + obtain ⟨Tx, hTx⟩ := hargsWF (x.1.1.instL m1) + (by + rw [List.map_append] + exact List.mem_append.2 (.inl (List.mem_map_of_mem x.1.2))) + have hretTx := (VEnv.SpineWF.retarget hcapsF hcapslen) Tx + have hcollapseX := VEnv.IsDefEq.appN_lamN henvo hTel hTx hretTx hcapslen + exact VEnv.IsDefEqU.trans henv hΓ hfact ⟨_, hcollapseX⟩ + -- major premise: constructor spine against its rebuilt form + have hparamsF₂ : List.Forall₂ + (fun a a' => a = a' ∨ env.IsDefEqU univs Γ a a') + aArgs + (fArgs.take source.nparams ++ aArgs.drop source.nparams) := by + have hb := forall₂_zip_map (α := Pattern.Path + (Pattern.varN (.const c.ctor.raw.name) (gen.ruleArgArity c))) + (β := Pattern.Path + (Pattern.varN (.const (gen.ruleRecName c)) (gen.ruleMajorArity c))) + g2 g1 (fun a a' => a = a' ∨ env.IsDefEqU univs Γ a a') + ((Pattern.varNPaths (.const c.ctor.raw.name) + (gen.ruleArgArity c)).take source.nparams) + ((Pattern.varNPaths (.const (gen.ruleRecName c)) + (gen.ruleMajorArity c)).take source.nparams) + (by + rw [List.length_take, List.length_take, + Pattern.varNPaths_length, Pattern.varNPaths_length] + omega) + (fun p hp => Or.inr (hparams p hp)) + rw [List.map_take, List.map_take, hg1, hg2] at hb + have hall := forall₂_append hb + (forall₂_refl_or (R := env.IsDefEqU univs Γ) + (aArgs.drop source.nparams)) + rwa [List.take_append_drop] at hall + have hmajorLink : env.IsDefEqU univs Γ + (VExpr.appN (.const c.ctor.raw.name + (gen.sourceLevels.map (VLevel.inst m1))) aArgs) + (VExpr.appN (.const c.ctor.raw.name + (gen.sourceLevels.map (VLevel.inst m1))) + (fArgs.take source.nparams ++ aArgs.drop source.nparams)) := + ⟨_, VEnv.IsDefEq.appN_defEq hctorhead + (VEnv.SpineWF.defEq_of_pointwise henv hΓ hctorspine hparamsF₂)⟩ + -- the collapsed left spine, computed + have hL : (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams).length = + gen.ruleFieldCount c + + (source.nparams + gen.familyCount + gen.minorCount) := by + rw [List.length_append, htakelen, hdroplen]; omega + have hcapsTake : (fArgs.take (source.nparams + gen.familyCount + + gen.minorCount) ++ aArgs.drop source.nparams).take + (source.nparams + gen.familyCount + gen.minorCount) = + fArgs.take (source.nparams + gen.familyCount + gen.minorCount) := by + rw [List.take_append_of_le_length (by omega : _ ≤ (fArgs.take + (source.nparams + gen.familyCount + gen.minorCount)).length)] + exact List.take_of_length_le (Nat.le_of_eq htakelen) + have hcapsTakeNp : (fArgs.take (source.nparams + gen.familyCount + + gen.minorCount) ++ aArgs.drop source.nparams).take source.nparams = + fArgs.take source.nparams := by + rw [List.take_append_of_le_length (by omega : _ ≤ (fArgs.take + (source.nparams + gen.familyCount + gen.minorCount)).length)] + rw [List.take_take] + congr 1 + omega + have hcapsDrop : (fArgs.take (source.nparams + gen.familyCount + + gen.minorCount) ++ aArgs.drop source.nparams).drop + (source.nparams + gen.familyCount + gen.minorCount) = + aArgs.drop source.nparams := by + have hdl := List.drop_left (l₁ := fArgs.take (source.nparams + gen.familyCount + gen.minorCount)) (l₂ := aArgs.drop source.nparams) + rwa [htakelen] at hdl + have hsegNp : (VExpr.bvarRevRange + (gen.ruleFieldCount c + (gen.familyCount + gen.minorCount)) + source.nparams).map (VExpr.instRev · + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams)) = fArgs.take source.nparams := by + rw [VExpr.map_instRev_bvarRevRange_seg _ source.nparams _ (by omega)] + rw [show (fArgs.take (source.nparams + gen.familyCount + + gen.minorCount) ++ aArgs.drop source.nparams).length - + (gen.ruleFieldCount c + (gen.familyCount + gen.minorCount)) - + source.nparams = 0 from by omega, List.drop_zero] + exact hcapsTakeNp + have hsegFld : (VExpr.bvarRevRange 0 (gen.ruleFieldCount c)).map + (VExpr.instRev · + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams)) = aArgs.drop source.nparams := by + rw [VExpr.map_instRev_bvarRevRange_seg _ (gen.ruleFieldCount c) 0 + (by omega)] + rw [show (fArgs.take (source.nparams + gen.familyCount + + gen.minorCount) ++ aArgs.drop source.nparams).length - 0 - + gen.ruleFieldCount c = + source.nparams + gen.familyCount + gen.minorCount from by omega] + rw [hcapsDrop] + exact List.take_of_length_le (Nat.le_of_eq hdroplen) + have hsegCommon : (VExpr.bvarRevRange (gen.ruleFieldCount c) + (source.nparams + gen.familyCount + gen.minorCount)).map + (VExpr.instRev · + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams)) = + fArgs.take (source.nparams + gen.familyCount + gen.minorCount) := by + rw [VExpr.map_instRev_bvarRevRange_seg _ + (source.nparams + gen.familyCount + gen.minorCount) + (gen.ruleFieldCount c) (by omega)] + rw [show (fArgs.take (source.nparams + gen.familyCount + + gen.minorCount) ++ aArgs.drop source.nparams).length - + gen.ruleFieldCount c - + (source.nparams + gen.familyCount + gen.minorCount) = 0 from by + omega, List.drop_zero] + exact hcapsTake + have hctorImg : VExpr.instRev ((gen.ruleCtorApp c).instL m1) + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams) = + VExpr.appN (.const c.ctor.raw.name + (gen.sourceLevels.map (VLevel.inst m1))) + (fArgs.take source.nparams ++ aArgs.drop source.nparams) := by + rw [gen.ruleCtorApp_instL, VExpr.instRev_appN, + VExpr.instRev_closedN (C := .const c.ctor.raw.name + (gen.sourceLevels.map (VLevel.inst m1))) _ trivial, List.map_append, + hsegNp, hsegFld] + have hcollapsedEq : VExpr.instRev ((gen.ruleLhsBody c).instL m1) + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams) = + VExpr.appN (.const (gen.ruleRecName c) m1) + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + ((gen.ruleIdx c).map (fun x => VExpr.instRev (x.instL m1) + (fArgs.take (source.nparams + gen.familyCount + + gen.minorCount) ++ aArgs.drop source.nparams)) ++ + [VExpr.appN (.const c.ctor.raw.name + (gen.sourceLevels.map (VLevel.inst m1))) + (fArgs.take source.nparams ++ aArgs.drop source.nparams)])) := by + rw [gen.ruleLhsBody_instL c hlen1, VExpr.instRev_appN, + VExpr.instRev_closedN (C := .const (gen.ruleRecName c) m1) _ trivial, + List.map_append, List.map_append, hsegCommon, List.map_map] + rw [show ((gen.ruleCtorApp c).instL m1 :: + ([] : List VExpr)).map (VExpr.instRev · + (fArgs.take (source.nparams + gen.familyCount + + gen.minorCount) ++ aArgs.drop source.nparams)) = + [VExpr.instRev ((gen.ruleCtorApp c).instL m1) + (fArgs.take (source.nparams + gen.familyCount + + gen.minorCount) ++ aArgs.drop source.nparams)] from rfl] + rw [hctorImg] + simp only [Function.comp_def] + rw [List.append_assoc] + -- pointwise defeq between the redex spine and the collapsed spine + have hidxF₂ : List.Forall₂ (fun a a' => a = a' ∨ env.IsDefEqU univs Γ a a') + (fArgs.drop (source.nparams + gen.familyCount + gen.minorCount)) + ((gen.ruleIdx c).map (fun x => VExpr.instRev (x.instL m1) + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams))) := by + have hb := forall₂_zip_map + (α := {x // x ∈ gen.ruleIdx c}) + (β := Pattern.Path (Pattern.varN (.const (gen.ruleRecName c)) + (gen.ruleMajorArity c))) + (fun s => VExpr.instRev (s.1.instL m1) + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams)) + (fun p => Sum.elim g1 g2 (Sum.inl p)) + (fun t v => v = t ∨ env.IsDefEqU univs Γ v t) + (gen.ruleIdx c).attach + ((Pattern.varNPaths (.const (gen.ruleRecName c)) + (gen.ruleMajorArity c)).drop + (source.nparams + gen.familyCount + gen.minorCount)) + (by + rw [List.length_attach, List.length_drop, Pattern.varNPaths_length] + show (gen.ruleIdx c).length = gen.ruleMajorArity c - _ + simp only [ruleIdx, ruleMajorArity, List.length_map] + omega) + (fun p hp => Or.inr (hidxLink p hp)) + have hflip := List.Forall₂.flip hb + have hmapG : ((Pattern.varNPaths (.const (gen.ruleRecName c)) + (gen.ruleMajorArity c)).drop + (source.nparams + gen.familyCount + gen.minorCount)).map + (fun p => Sum.elim g1 g2 (Sum.inl p)) = + fArgs.drop (source.nparams + gen.familyCount + gen.minorCount) := by + show ((Pattern.varNPaths (.const (gen.ruleRecName c)) + (gen.ruleMajorArity c)).drop + (source.nparams + gen.familyCount + gen.minorCount)).map g1 = _ + rw [List.map_drop, hg1] + have hmapF : ((gen.ruleIdx c).attach).map + (fun s => VExpr.instRev (s.1.instL m1) + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams)) = + (gen.ruleIdx c).map (fun x => VExpr.instRev (x.instL m1) + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams)) := by + exact List.attach_map_val + (f := fun x : VExpr => VExpr.instRev (x.instL m1) + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams)) .. + rw [hmapG, hmapF] at hflip + exact hflip + have hbigF₂ : List.Forall₂ (fun a a' => a = a' ∨ env.IsDefEqU univs Γ a a') + (fArgs ++ [VExpr.appN (.const c.ctor.raw.name + (gen.sourceLevels.map (VLevel.inst m1))) aArgs]) + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + ((gen.ruleIdx c).map (fun x => VExpr.instRev (x.instL m1) + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount) ++ + aArgs.drop source.nparams)) ++ + [VExpr.appN (.const c.ctor.raw.name + (gen.sourceLevels.map (VLevel.inst m1))) + (fArgs.take source.nparams ++ aArgs.drop source.nparams)])) := by + have hres := forall₂_append + (forall₂_refl_or (R := env.IsDefEqU univs Γ) + (fArgs.take (source.nparams + gen.familyCount + gen.minorCount))) + (forall₂_append hidxF₂ (.cons (Or.inr hmajorLink) .nil)) + rwa [← List.append_assoc, List.take_append_drop] at hres + -- the redex is defeq to the collapsed left spine + have hE := VEnv.IsDefEq.appN_defEq hehead + (VEnv.SpineWF.defEq_of_pointwise henv hΓ hespine hbigF₂) + rw [← hcollapsedEq, VExpr.appN_append] at hE + -- assemble + rw [hRHS] + exact VEnv.IsDefEqU.trans henv hΓ ⟨_, hE⟩ + (VEnv.IsDefEqU.trans henv hΓ ⟨_, hcollapseL.symm⟩ ⟨_, happlied⟩) + +end BlockGenerationChecked + +end VInductDecl + +end Ix.Theory.Named + +/-! ## Axiom closures + +The typed β-collapse layer is sorry-free. `pat_wf` composes typed defeqs +through `IsDefEqU.of_l`/`IsDefEqU.trans` and therefore carries exactly the +transitional unique-typing closure the Church–Rosser development itself +carries; it sheds `sorryAx` automatically when the Spec-16/17 inversion +milestones land, with no restatement. -/ + +#guard_named_axioms Ix.Theory.Named.VEnv.IsDefEq.appN_lamN [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VEnv.IsDefEq.appN_defEq [propext] + +#guard_named_axioms Ix.Theory.Named.VEnv.LE.extra [propext] + +#guard_named_axioms Ix.Theory.Named.VEnv.LE.extra_appN [propext] + +#guard_named_axioms Ix.Theory.Named.VEnv.LE.extra_appN_symm [propext] + +#guard_named_axioms Ix.Theory.Named.Pattern.varN_matches_paths [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.BlockGenerationChecked.pat_wf [ + propext, + sorryAx, + Classical.choice, + Quot.sound] diff --git a/Ix/Theory/Named/Typing/Injectivity.lean b/Ix/Theory/Named/Typing/Injectivity.lean new file mode 100644 index 000000000..6a69aad14 --- /dev/null +++ b/Ix/Theory/Named/Typing/Injectivity.lean @@ -0,0 +1,43 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Typing.EnvLemmas +import Ix.Theory.Named.Typing.Strong + +/-! +A bunch of important structural theorems which we can't prove :( +-/ + +namespace Ix.Theory.Named +namespace VEnv + +set_option warn.sorry false in +theorem IsDefEqU.sort_inv (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (h1 : env.IsDefEqU U Γ (.sort u) (.sort v)) : u ≈ v := sorry + +set_option warn.sorry false in +theorem IsDefEqU.forallE_inv_stratified (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (h1 : env.IsDefEqU U Γ (.forallE A B) (.forallE A' B')) + (h2 : env.HasTypeStratified U Γ (.forallE A B) V true n) + (h3 : env.HasTypeStratified U Γ (.forallE A' B') V' true n') : + (∃ u, env.IsDefEq U Γ A A' (.sort u) ∧ env.HasTypeStratified U Γ A (.sort u) true n) ∧ + ∃ u, env.IsDefEq U (A::Γ) B B' (.sort u) ∧ + env.HasTypeStratified U (A::Γ) B (.sort u) true n ∧ + env.HasTypeStratified U (A'::Γ) B' (.sort u) true n' := sorry + +theorem IsDefEqU.forallE_inv (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (h1 : env.IsDefEqU U Γ (.forallE A B) (.forallE A' B')) : + (∃ u, env.IsDefEq U Γ A A' (.sort u)) ∧ ∃ u, env.IsDefEq U (A::Γ) B B' (.sort u) := + let ⟨_, eq⟩ := h1 + let ⟨h2, h3⟩ := (eq.strong henv hΓ).hasType' + let ⟨_, h2⟩ := h2.stratify + let ⟨_, h3⟩ := h3.stratify + let ⟨⟨_, a1, _⟩, _, a2, _⟩ := IsDefEqU.forallE_inv_stratified henv hΓ h1 h2 h3 + ⟨⟨_, a1⟩, _, a2⟩ + +set_option warn.sorry false in +theorem IsDefEqU.sort_forallE_inv (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) : + ¬env.IsDefEqU U Γ (.sort u) (.forallE A B) := sorry diff --git a/Ix/Theory/Named/Typing/Lemmas.lean b/Ix/Theory/Named/Typing/Lemmas.lean new file mode 100644 index 000000000..36e44bafb --- /dev/null +++ b/Ix/Theory/Named/Typing/Lemmas.lean @@ -0,0 +1,1115 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Typing.Basic +import Ix.Theory.Named.Std.VariableBang + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named + +open Ix.Theory.Named VExpr + +inductive Ctx.LiftN (n : Nat) : Nat → List VExpr → List VExpr → Prop where + | zero (As) (h : As.length = n := by rfl) : Ctx.LiftN n 0 Γ (As ++ Γ) + | succ : Ctx.LiftN n k Γ Γ' → Ctx.LiftN n (k+1) (A::Γ) (A.liftN n k :: Γ') + +theorem Ctx.LiftN.one : Ctx.LiftN 1 0 Γ (A::Γ) := .zero [_] + +theorem Ctx.LiftN.isSuffix (H : Ctx.LiftN n k Γ Γ') : + ∃ Γ₀ As Δ Δ', + As.length = n ∧ Δ.length = k ∧ Δ'.length = k ∧ + Γ = Δ ++ Γ₀ ∧ Γ' = Δ' ++ As ++ Γ₀ := by + induction H with + | @zero Γ As h => exact ⟨Γ, As, [], [], h, rfl, rfl, rfl, rfl⟩ + | succ _ ih => + obtain ⟨_, _, Δ, Δ', hn, hΔ, hΔ', rfl, rfl⟩ := ih + exact ⟨_, _, _ :: Δ, _ :: Δ', hn, by simp [hΔ], by simp [hΔ'], rfl, rfl⟩ + +theorem Ctx.LiftN.comp (h1 : k1 ≤ k2) (h2 : k2 ≤ n1 + k1) : + Ctx.LiftN n1 k1 Γ₀ Γ₁ → Ctx.LiftN n2 k2 Γ₁ Γ₂ → Ctx.LiftN (n1+n2) k1 Γ₀ Γ₂ + | .zero As h, H2 => by + obtain ⟨Γ₀', _, Δ, Δ', hn, hΔ, hΔ', eq, rfl⟩ := H2.isSuffix + obtain ⟨As, rfl, rfl⟩ := (List.append_eq_append_of_length_le (by rwa [h, hΔ])).1 eq.symm + rw [← List.append_assoc] + exact .zero _ (by simp [← h, hΔ, hΔ', hn, Nat.add_left_comm, Nat.add_comm]) + | .succ H1, .succ H2 => + have h1 := Nat.le_of_succ_le_succ h1 + have h2 := Nat.le_of_succ_le_succ h2 + liftN'_liftN' h1 h2 ▸ (H1.comp h1 h2 H2).succ + +inductive Ctx.Lift' : Lift → List VExpr → List VExpr → Prop where + | refl : Ctx.Lift' .refl Γ Γ + | skip : Ctx.Lift' l Γ Γ' → Ctx.Lift' (.skip l) Γ (A :: Γ') + | cons : Ctx.Lift' l Γ Γ' → Ctx.Lift' (.cons l) (A::Γ) (A.lift' l :: Γ') + +theorem Ctx.liftN_iff_lift' : Ctx.LiftN n k Γ Γ' ↔ Ctx.Lift' (.consN (.skipN .refl n) k) Γ Γ' := by + constructor <;> intro h + · induction h with + | zero As => + subst n + induction As with + | nil => simp [Lift'.refl] + | cons _ _ ih => exact .skip ih + | succ _ ih => rw [← lift'_consN_skipN]; exact .cons ih + · induction k generalizing Γ Γ' with + | zero => + obtain ⟨_, rfl, eq⟩ : ∃ As, Γ' = As ++ Γ ∧ As.length = n := by + induction n generalizing Γ' with + | zero => cases h; exact ⟨[], rfl, rfl⟩ + | succ n ih => let .skip h := h; obtain ⟨_, rfl, rfl⟩ := ih h; exact ⟨_::_, rfl, rfl⟩ + exact .zero _ eq + | succ k ih => let .cons h := h; rw [lift'_consN_skipN]; exact .succ (ih h) + +theorem Ctx.Lift'.comp (H1 : Ctx.Lift' l₁ Γ₁ Γ₂) (H2 : Ctx.Lift' l₂ Γ₂ Γ₃) : + Ctx.Lift' (.comp l₁ l₂) Γ₁ Γ₃ := by + induction H2 generalizing l₁ Γ₁ with + | refl => exact H1 + | skip _ ih => exact .skip (ih H1) + | cons H2 ih => + cases H1 with + | refl => exact .cons H2 + | skip H1 => exact .skip (ih H1) + | cons H1 => rw [← VExpr.lift'_comp]; exact .cons (ih H1) + +theorem Ctx.Lift'.of_cons_skip (H : Ctx.Lift' (Lift.consN (.skip l) k) Γ₁ Γ₃) : + ∃ Γ₂, Ctx.Lift' (Lift.consN l k) Γ₁ Γ₂ ∧ Ctx.LiftN 1 k Γ₂ Γ₃ := by + induction k generalizing Γ₁ Γ₃ with + | zero => let .skip H := H; exact ⟨_, H, .one⟩ + | succ k ih => + let .cons (A := A) H := H + let ⟨_, h1, h2⟩ := ih H + refine ⟨_, .cons h1, ?_⟩ + simp only [Ctx.liftN_iff_lift'] at h2 ⊢ + simpa [← VExpr.lift'_comp, ← Lift.consN_comp] using h2.cons (A := lift' A (.consN l k)) + +theorem Ctx.Lift'.depth_zero (h : l.depth = 0) : Ctx.Lift' l Γ Γ' → Γ = Γ' + | .refl => rfl + | .cons (l := l) H => by rw [VExpr.lift'_depth_zero (l := l) h, H.depth_zero (l := l) h] + +variable (Γ₀ : List VExpr) (e₀ A₀ : VExpr) in +inductive Ctx.InstN : Nat → List VExpr → List VExpr → Prop where + | zero : Ctx.InstN 0 (A₀ :: Γ₀) Γ₀ + | succ : Ctx.InstN k Γ Γ' → Ctx.InstN (k+1) (A::Γ) (A.inst e₀ k :: Γ') + +theorem Lookup.lt (H : Lookup Γ i A) : i < Γ.length := by + induction H with + | zero => apply Nat.succ_pos + | succ _ ih => apply Nat.succ_lt_succ ih + +def Lookup.ofLt (hi : i < Γ.length) : {A // Lookup Γ i A} := + match Γ, i with + | _::_, 0 => ⟨_, .zero⟩ + | _::_, _+1 => let ⟨_, h⟩ := ofLt (Nat.lt_of_succ_lt_succ hi); ⟨_, .succ h⟩ + +theorem Lookup.uniq (hA : Lookup Γ i A) (hB : Lookup Γ i B) : A = B := + match hA, hB with + | .zero, .zero => rfl + | .succ hA, .succ hB => Lookup.uniq hA hB ▸ rfl + +theorem Lookup.weak' (W : Ctx.Lift' ρ Γ Γ') (H : Lookup Γ i A) : + Lookup Γ' (ρ.liftVar i) (A.lift' ρ) := by + induction W generalizing i A with + | refl => simp; exact H + | skip W ih => have' := (ih H).succ; rwa [VExpr.lift_eq_lift', ← VExpr.lift'_comp] at this + | cons W ih => + cases H with + | zero => refine' cast _ Lookup.zero; congr 1; simp [VExpr.lift_eq_lift', ← VExpr.lift'_comp] + | succ H => refine' cast _ (ih H).succ; congr 1; simp [VExpr.lift_eq_lift', ← VExpr.lift'_comp] + +theorem Lookup.weakN (W : Ctx.LiftN n k Γ Γ') (H : Lookup Γ i A) : + Lookup Γ' (liftVar n i k) (A.liftN n k) := by + simp [← Lift.liftVar_consN_skipN, ← VExpr.lift'_consN_skipN] + exact H.weak' (Ctx.liftN_iff_lift'.1 W) + +theorem Lookup.weakU_inv (W : Ctx.Lift' ρ Γ Γ') + (H : Lookup Γ' (ρ.liftVar i) A') : ∃ A, A' = A.lift' ρ ∧ Lookup Γ i A := by + induction W generalizing i A' with + | refl => simpa using H + | @skip ρ W _ _ _ ih => + simp at H; let .succ H := H + obtain ⟨_, rfl, h2⟩ := ih H; refine ⟨_, ?_, h2⟩ + rw [VExpr.lift_eq_lift', ← VExpr.lift'_comp]; rfl + | @cons ρ Γ Δ B W ih => + cases i with + | zero => cases H; exact ⟨_, by simp [VExpr.lift_eq_lift', ← VExpr.lift'_comp], .zero⟩ + | succ i => + let .succ (ty := C) H := H + obtain ⟨C, rfl, h⟩ := ih H + refine ⟨_, ?_, .succ h⟩ + simp [VExpr.lift_eq_lift', ← VExpr.lift'_comp] + +theorem Lookup.weak'_iff (W : Ctx.Lift' ρ Γ Γ') : + Lookup Γ' (ρ.liftVar i) (A.lift' ρ) ↔ Lookup Γ i A := by + refine ⟨fun H => ?_, fun H => H.weak' W⟩ + let ⟨_, h1, h2⟩ := H.weakU_inv W + exact VExpr.lift'_inj.1 h1 ▸ h2 + +theorem Lookup.weakN_iff (W : Ctx.LiftN n k Γ Γ') : + Lookup Γ' (liftVar n i k) (A.liftN n k) ↔ Lookup Γ i A := by + simp [← Lift.liftVar_consN_skipN, ← VExpr.lift'_consN_skipN] + exact weak'_iff (Ctx.liftN_iff_lift'.1 W) + +theorem Lookup.instL : Lookup Γ i A → Lookup (Γ.map (VExpr.instL ls)) i (A.instL ls) + | .zero => instL_liftN ▸ .zero + | .succ h => instL_liftN ▸ .succ h.instL + +theorem OnCtx.lookup (h : OnCtx Γ P) (hL : Lookup Γ n A) + (hP : ∀ {Γ A B}, P Γ A → P (B::Γ) A.lift) : P Γ A := + match hL, h with + | .zero, ⟨h1, h2⟩ => hP h2 + | .succ hL, ⟨h1, h2⟩ => hP (h1.lookup hL hP) + +theorem OnCtx.mono (H : ∀ {Γ A}, P Γ A → Q Γ A) : ∀ {Γ}, OnCtx Γ P → OnCtx Γ Q + | [], h => h + | _::_, ⟨h1, h2⟩ => ⟨mono H h1, H h2⟩ + +def CtxClosed : List VExpr → Prop := (OnCtx · fun Γ A => A.ClosedN Γ.length) + +theorem CtxClosed.lookup (h : CtxClosed Γ) (hL : Lookup Γ n A) : A.ClosedN Γ.length := + match hL, h with + | .zero, ⟨h1, h2⟩ => h2.liftN + | .succ hL, ⟨h1, h2⟩ => (CtxClosed.lookup h1 hL).liftN + +theorem Ctx.LiftN.right (h : CtxClosed Γ) (Γ') : Ctx.LiftN Γ'.length Γ.length Γ (Γ ++ Γ') := + match Γ, h with + | [], _ => by simpa using LiftN.zero (Γ := []) Γ' + | A :: Γ, ⟨h1, h2⟩ => by + simpa [h2.liftN_eq (Nat.le_refl _)] using LiftN.succ (LiftN.right h1 Γ') (A := A) + +theorem VStructEta.WF.mono {rule : VStructEta} {env env' : VEnv} + (henv : env ≤ env') (self : VStructEta.WF rule env) : + VStructEta.WF rule env' where + familyType_closed := self.familyType_closed + rebuild_hasType hle := self.rebuild_hasType (henv.trans hle) + +inductive VObject where + | const (n : Name) (ci : VConstant) + | defeq (df : VDefEq) + +namespace VEnv + +theorem addConst_le {env env' : VEnv} (h : env.addConst n ci = some env') : env ≤ env' := by + unfold addConst at h; split at h <;> cases h + exact ⟨fun _ => by simp; split <;> simp_all, by simp [*], by simp [*]⟩ + +theorem addConst_self {env env' : VEnv} (h : env.addConst n ci = some env') : + env'.constants n = some ci := by + unfold addConst at h; split at h <;> cases h; simp + +/-- A successful insertion certifies that its name was fresh in the input +environment. -/ +theorem addConst_fresh {env env' : VEnv} (h : env.addConst n ci = some env') : + env.constants n = none := by + unfold addConst at h + split at h <;> simp_all + +/-- Environment inclusion is preserved when the same fresh constant is +inserted on both sides. This is the commuting square needed to replay a +declaration after an unrelated prefix environment. -/ +theorem LE.addConst {env₁ env₂ env₁' env₂' : VEnv} (henv : env₁ ≤ env₂) + (h₁ : env₁.addConst n ci = some env₁') + (h₂ : env₂.addConst n ci = some env₂') : env₁' ≤ env₂' := by + unfold VEnv.addConst at h₁ h₂ + split at h₁ <;> cases h₁ + split at h₂ <;> cases h₂ + exact { + constants := fun h => by + simp at h ⊢ + split at h <;> split <;> simp_all + exact henv.constants h + defeqs := henv.defeqs + structEtas := henv.structEtas } + +/-- Absence of a constant pulls back along environment growth. -/ +theorem LE.constants_none {env env' : VEnv} (henv : env ≤ env') + (h : env'.constants n = none) : env.constants n = none := by + cases h₀ : env.constants n with + | none => rfl + | some ci => + have := henv.constants h₀ + rw [h] at this + contradiction + +theorem addDefEq_le {env : VEnv} : env ≤ env.addDefEq df := ⟨id, .inr, id⟩ + +theorem addDefEq_self {env : VEnv} : (env.addDefEq df).defeqs df := .inl rfl + +theorem addStructEta_le {env : VEnv} : env ≤ env.addStructEta rule := + ⟨id, id, .inr⟩ + +theorem addStructEta_self {env : VEnv} : + (env.addStructEta rule).structEtas rule := .inl rfl + +def HasObjects (env : VEnv) : List VObject → Prop + | [] => True + | .const n ci :: ls => env.constants n = some ci ∧ env.HasObjects ls + | .defeq df :: ls => env.defeqs df ∧ env.HasObjects ls + +theorem HasObjects.mono {env env' : VEnv} (henv : env ≤ env') : + ∀ {ls}, HasObjects env ls → HasObjects env' ls + | [] => id + | .const .. :: _ => .imp henv.1 (mono henv) + | .defeq .. :: _ => .imp henv.2 (mono henv) + +theorem HasObjects.const {env env' : VEnv} (hls : env.HasObjects ls) + (h : env.addConst n ci = some env') : env'.HasObjects (.const n ci :: ls) := + ⟨addConst_self h, hls.mono (addConst_le h)⟩ + +theorem HasObjects.defeq {env : VEnv} (hls : env.HasObjects ls) : + (addDefEq env df).HasObjects (.defeq df :: ls) := ⟨addDefEq_self, hls.mono addDefEq_le⟩ + +theorem HasObjects.bind_const {env env' : VEnv} (hls : env.HasObjects ls) + (h : env.addConst n ci >>= f = some env') : + ∃ env1, env1.HasObjects (.const n ci :: ls) ∧ f env1 = some env' := + let ⟨env1, h1, henv1⟩ := Option.bind_eq_some_iff.1 h; ⟨env1, hls.const h1, henv1⟩ + +nonrec theorem HasType.bvar (h : Lookup Γ i A): HasType env U Γ (.bvar i) A := .bvar h +theorem HasType.sort (h : l.WF U) : HasType env U Γ (.sort l) (.sort (.succ l)) := + .sortDF h h rfl +theorem HasType.const + (h1 : env.constants c = some ci) (h2 : ∀ l ∈ ls, l.WF U) (h3 : ls.length = ci.uvars) : + HasType env U Γ (.const c ls) (ci.type.instL ls) := + .constDF h1 h2 h2 h3 (.rfl fun _ _ => rfl) +theorem HasType.app (h1 : HasType env U Γ f (.forallE A B)) (h2 : HasType env U Γ a A) : + HasType env U Γ (.app f a) (B.inst a) := .appDF h1 h2 +theorem HasType.lam (h1 : HasType env U Γ A (.sort u)) (h2 : HasType env U (A::Γ) body B) : + HasType env U Γ (.lam A body) (.forallE A B) := .lamDF h1 h2 +theorem HasType.forallE + (h1 : HasType env U Γ A (.sort u)) (h2 : HasType env U (A::Γ) body (.sort v)) : + HasType env U Γ (.forallE A body) (.sort (.imax u v)) := .forallEDF h1 h2 +theorem IsDefEq.defeq (h1 : IsDefEq env U Γ A B (.sort u)) + (h2 : HasType env U Γ e A) : HasType env U Γ e B := .defeqDF h1 h2 +theorem IsDefEq.defeq' (h1 : IsDefEq env U Γ A B (.sort u)) + (h2 : HasType env U Γ e B) : HasType env U Γ e A := .defeq h1.symm h2 + +theorem IsType.forallE : IsType env U Γ A → IsType env U (A::Γ) body → + IsType env U Γ (.forallE A body) + | ⟨_, h1⟩, ⟨_, h2⟩ => ⟨_, .forallE h1 h2⟩ + +theorem IsDefEq.hasType {env : VEnv} (H : env.IsDefEq U Γ e1 e2 A) : + env.HasType U Γ e1 A ∧ env.HasType U Γ e2 A := ⟨H.trans H.symm, H.symm.trans H⟩ + +theorem IsDefEq.toU {env : VEnv} (H : env.IsDefEq U Γ e1 e2 A) : env.IsDefEqU U Γ e1 e2 := ⟨_, H⟩ + +theorem IsDefEqU.refl {env : VEnv} (h1 : e.WF env U Γ) : env.IsDefEqU U Γ e e := h1 +theorem IsDefEqU.symm {env : VEnv} (h1 : env.IsDefEqU U Γ e₁ e₂) : env.IsDefEqU U Γ e₂ e₁ := + h1.imp fun _ => (·.symm) + +inductive Ordered : VEnv → Prop where + | empty : Ordered ∅ + | const : + Ordered env → ci.WF env → + env.addConst n ci = some env' → Ordered env' + | defeq : Ordered env → df.WF env → Ordered (env.addDefEq df) + | structEta : Ordered env → rule.WF env → Ordered (env.addStructEta rule) + +def OnTypes (env : VEnv) (P : Nat → VExpr → VExpr → Prop) : Prop := + (∀ {n ci}, env.constants n = some ci → ∃ u, P ci.uvars ci.type (.sort u)) ∧ + (∀ {df}, env.defeqs df → P df.uvars df.lhs df.type ∧ P df.uvars df.rhs df.type) + +theorem OnTypes.mono (henv : env' ≤ env) (hP : ∀ {U e A}, P U e A → P' U e A) + (H : OnTypes env P) : OnTypes env' P' := + ⟨fun hci => (H.1 (henv.1 hci)).imp fun _ => hP, fun hdf => (H.2 (henv.2 hdf)).imp hP hP⟩ + +theorem Ordered.induction (motive : VEnv → Nat → VExpr → VExpr → Prop) + (mono : ∀ {env env' U e A}, env ≤ env' → motive env U e A → motive env' U e A) + (type : ∀ {env U e A}, + Ordered env → OnTypes env (motive env) → HasType env U [] e A → motive env U e A) + (H : Ordered env) : OnTypes env (motive env) := by + induction H with + | empty => exact ⟨nofun, nofun⟩ + | const h1 h2 h3 ih => + apply OnTypes.mono .rfl (mono (addConst_le h3)) + unfold addConst at h3; split at h3 <;> cases h3 + refine ⟨fun h => ?_, ih.2⟩ + simp at h; split at h + · cases h + let ⟨_, ht⟩ := h2 + exact ⟨_, type h1 ih ht⟩ + · exact ih.1 h + | defeq h1 h2 ih => + apply OnTypes.mono .rfl (mono addDefEq_le) + refine ⟨ih.1, fun hdf => ?_⟩ + simp [addDefEq] at hdf + obtain rfl | hdf := hdf + · let ⟨hl, hr⟩ := h2 + exact ⟨type h1 ih hl, type h1 ih hr⟩ + · exact ih.2 hdf + | structEta _ _ ih => + exact OnTypes.mono .rfl (mono addStructEta_le) ih + +variable (env : VEnv) (U : Nat) (Γ₀ : List VExpr) in +inductive IsDefEqCtx : List VExpr → List VExpr → Prop + | zero : IsDefEqCtx Γ₀ Γ₀ + | succ : IsDefEqCtx Γ₁ Γ₂ → env.IsDefEq U Γ₁ A₁ A₂ (.sort u) → IsDefEqCtx (A₁ :: Γ₁) (A₂ :: Γ₂) + +theorem IsDefEqCtx.length_eq : IsDefEqCtx env U Γ₀ Γ₁ Γ₂ → Γ₁.length = Γ₂.length + | .zero => rfl + | .succ h _ => congrArg Nat.succ h.length_eq + +theorem IsDefEqCtx.isSuffix : IsDefEqCtx env U Γ₀ Γ₁ Γ₂ → Γ₀ <:+ Γ₁ ∧ Γ₀ <:+ Γ₂ + | .zero => ⟨List.suffix_refl _, List.suffix_refl _⟩ + | .succ h _ => + let ⟨h1, h2⟩ := h.isSuffix + ⟨h1.trans (List.suffix_cons _ _), h2.trans (List.suffix_cons _ _)⟩ + +variable! (H₀ : OnCtx Γ₀ (IsType env U)) in +theorem IsDefEqCtx.isType' : IsDefEqCtx env U Γ₀ Γ₁ Γ₂ → OnCtx Γ₁ (env.IsType U) + | .zero => H₀ + | .succ h1 h2 => ⟨h1.isType', _, h2.hasType.1⟩ + +theorem IsDefEqCtx.isType (H : IsDefEqCtx env U [] Γ₁ Γ₂) : OnCtx Γ₁ (env.IsType U) := + H.isType' (by trivial) + +theorem IsDefEqCtx.refl : ∀ {Γ}, OnCtx Γ (env.IsType U) → IsDefEqCtx env U [] Γ Γ + | [], _ => .zero + | _::_, ⟨h1, _, h2⟩ => .succ (.refl h1) h2 + +variable! (henv : OnTypes env fun _ e A => e.ClosedN ∧ A.ClosedN) in +theorem IsDefEq.closedN' (H : env.IsDefEq U Γ e1 e2 A) (hΓ : CtxClosed Γ) : + e1.ClosedN Γ.length ∧ e2.ClosedN Γ.length ∧ A.ClosedN Γ.length := by + induction H using IsDefEq.rec + (motive_2 := fun _ _ _ _ _ => True) with + | bvar h => exact ⟨h.lt, h.lt, hΓ.lookup h⟩ + | constDF h1 => + let ⟨_, h, _⟩ := henv.1 h1 + exact ⟨trivial, trivial, h.instL.mono (Nat.zero_le _)⟩ + | sortDF => exact ⟨trivial, trivial, trivial⟩ + | symm _ ih => let ⟨h1, h2, h3⟩ := ih hΓ; exact ⟨h2, h1, h3⟩ + | trans _ _ ih1 ih2 => exact ⟨(ih1 hΓ).1, (ih2 hΓ).2.1, (ih1 hΓ).2.2⟩ + | appDF _ _ ih1 ih2 => + let ⟨hf, hf', _, hB⟩ := ih1 hΓ + let ⟨ha, ha', _⟩ := ih2 hΓ + exact ⟨⟨hf, ha⟩, ⟨hf', ha'⟩, hB.inst ha⟩ + | lamDF _ _ ih1 ih2 => + let ⟨hA, hA', _⟩ := ih1 hΓ + let ⟨hb, hb', hB⟩ := ih2 ⟨hΓ, hA⟩ + exact ⟨⟨hA, hb⟩, ⟨hA', hb'⟩, hA, hB⟩ + | forallEDF _ _ ih1 ih2 => + let ⟨hA, hA', _⟩ := ih1 hΓ + let ⟨hb, hb', _⟩ := ih2 ⟨hΓ, hA⟩ + exact ⟨⟨hA, hb⟩, ⟨hA', hb'⟩, trivial⟩ + | defeqDF _ _ ih1 ih2 => exact ⟨(ih2 hΓ).1, (ih2 hΓ).2.1, (ih1 hΓ).2.1⟩ + | beta _ _ ih1 ih2 => + let ⟨he', _, hA⟩ := ih2 hΓ + let ⟨he, _, hB⟩ := ih1 ⟨hΓ, hA⟩ + exact ⟨⟨⟨hA, he⟩, he'⟩, he.inst he', hB.inst he'⟩ + | eta _ ih => + let ⟨he, _, hA, hB⟩ := ih hΓ + exact ⟨⟨hA, he.liftN, Nat.succ_pos _⟩, he, hA, hB⟩ + | structEta _ _ _ _ _ _ _ _ ihMajor ihRebuild => + let ⟨hmajor, _, htype⟩ := ihMajor hΓ + let ⟨hrebuild, _, _⟩ := ihRebuild hΓ + exact ⟨hrebuild, hmajor, htype⟩ + | proofIrrel _ _ _ _ ih2 ih3 => + let ⟨hh, _, _⟩ := ih2 hΓ + let ⟨hh', _, hp⟩ := ih3 hΓ + exact ⟨hh, hh', hp⟩ + | extra h1 _ _ => + let ⟨⟨hl, _⟩, ⟨hr, hA⟩⟩ := henv.2 h1 + exact ⟨ + hl.instL.mono (Nat.zero_le _), + hr.instL.mono (Nat.zero_le _), + hA.instL.mono (Nat.zero_le _)⟩ + | nil | cons => trivial + +theorem Ordered.closed (H : Ordered env) : env.OnTypes fun _ e A => e.ClosedN ∧ A.ClosedN := + H.induction _ (fun _ => id) fun _ ih h => (IsDefEq.closedN' ih h trivial).2 + +theorem Ordered.closedC (H : Ordered env) + (h : env.constants n = some ci) : ci.type.ClosedN := + let ⟨_, h⟩ := H.closed.1 h; h.1 + +theorem IsDefEq.closedN {env : VEnv} (henv : env.Ordered) + (H : env.IsDefEq U Γ e1 e2 A) (hΓ : CtxClosed Γ) : e1.ClosedN Γ.length := + (H.closedN' henv.closed hΓ).1 + +theorem _root_.Ix.Theory.Named.VExpr.WF.closedN {env : VEnv} (henv : env.Ordered) + (H : VExpr.WF env U Γ e) (hΓ : CtxClosed Γ) : e.ClosedN Γ.length := + let ⟨_, h⟩ := H; h.closedN henv hΓ + +variable! (henv : Ordered env) in +theorem IsDefEqCtx.closed (H : CtxClosed Γ₀) : + IsDefEqCtx env U Γ₀ Γ₁ Γ₂ → CtxClosed Γ₁ ∧ CtxClosed Γ₂ + | .zero => ⟨H, H⟩ + | .succ h1 h2 => + have ⟨c1, c2⟩ := h1.closed H + ⟨⟨c1, h2.closedN henv c1⟩, ⟨c2, by simpa [h1.length_eq] using h2.symm.closedN henv c1⟩⟩ + +variable! {env env' : VEnv} (henv : env ≤ env') in +theorem IsDefEq.mono (H : env.IsDefEq U Γ e1 e2 A) : env'.IsDefEq U Γ e1 e2 A := by + induction H using IsDefEq.rec + (motive_2 := fun Γ A es B _ => env'.SpineWF U Γ A es B) with + | bvar h => exact .bvar h + | constDF h1 h2 h3 h4 h5 => exact .constDF (henv.1 h1) h2 h3 h4 h5 + | sortDF h1 h2 h3 => exact .sortDF h1 h2 h3 + | symm _ ih => exact .symm ih + | trans _ _ ih1 ih2 => exact .trans ih1 ih2 + | appDF _ _ ih1 ih2 => exact .appDF ih1 ih2 + | lamDF _ _ ih1 ih2 => exact .lamDF ih1 ih2 + | forallEDF _ _ ih1 ih2 => exact .forallEDF ih1 ih2 + | defeqDF _ _ ih1 ih2 => exact .defeqDF ih1 ih2 + | beta _ _ ih1 ih2 => exact .beta ih1 ih2 + | eta _ ih => exact .eta ih + | structEta hreg hlevels hlevelsLength hparamsLength _ _ _ + ihSpine ihMajor ihRebuild => + exact .structEta (henv.structEtas hreg) hlevels hlevelsLength + hparamsLength ihSpine ihMajor ihRebuild + | proofIrrel _ _ _ ih1 ih2 ih3 => exact .proofIrrel ih1 ih2 ih3 + | extra h1 h2 h3 => exact .extra (henv.2 h1) h2 h3 + | nil => exact .nil + | cons _ _ ihType ihRest => exact .cons ihType ihRest + +theorem HasType.mono {env env' : VEnv} (henv : env ≤ env') : + env.HasType U Γ e A → env'.HasType U Γ e A := IsDefEq.mono henv + +theorem SpineWF.mono {env env' : VEnv} (henv : env ≤ env') {U : Nat} + {Γ : List VExpr} : ∀ {es A B}, env.SpineWF U Γ A es B → + env'.SpineWF U Γ A es B + | [], _, _, .nil => .nil + | _ :: _, _, _, .cons he hrest => + .cons (he.mono henv) (SpineWF.mono henv hrest) + +theorem SpineWF.nil_inv {env : VEnv} (h : env.SpineWF U Γ A [] B) : A = B := by + cases h + rfl + +theorem SpineWF.cons_inv {env : VEnv} + (h : env.SpineWF U Γ A (e :: es) B) : + ∃ A₁ A₂, A = .forallE A₁ A₂ ∧ + env.HasType U Γ e A₁ ∧ env.SpineWF U Γ (A₂.inst e) es B := by + cases h with + | cons he hrest => exact ⟨_, _, rfl, he, hrest⟩ + +theorem IsType.mono {env env' : VEnv} (henv : env ≤ env') : env.IsType U Γ A → env'.IsType U Γ A + | ⟨u, h⟩ => ⟨u, h.mono henv⟩ + +theorem IsDefEqU.mono {env env' : VEnv} (henv : env ≤ env') : + env.IsDefEqU U Γ e1 e2 → env'.IsDefEqU U Γ e1 e2 + | ⟨u, h⟩ => ⟨u, h.mono henv⟩ + +end VEnv + +theorem VExpr.WF.mono {env env' : VEnv} (henv : env ≤ env') {e : VExpr} : + e.WF env U Γ → e.WF env' U Γ + | ⟨u, h⟩ => ⟨u, h.mono henv⟩ + +theorem VConstant.WF.mono {env env' : VEnv} (henv : env ≤ env') {ci : VConstant} : + ci.WF env → ci.WF env' + | ⟨u, h⟩ => ⟨u, h.mono henv⟩ + +theorem VDefEq.WF.mono {env env' : VEnv} (henv : env ≤ env') {df : VDefEq} : df.WF env → df.WF env' + | ⟨h1, h2⟩ => ⟨h1.mono henv, h2.mono henv⟩ + +namespace VEnv + +theorem Ordered.constWF (H : Ordered env) (h : env.constants n = some ci) : ci.WF env := by + induction H with + | empty => cases h + | const _ h2 h3 ih => + refine .mono (addConst_le h3) ?_ + unfold addConst at h3; split at h3 <;> cases h3 + simp at h; split at h + · cases h; exact h2 + · exact ih h + | defeq _ _ ih => exact .mono addDefEq_le (ih h) + | structEta _ _ ih => exact .mono addStructEta_le (ih h) + +theorem Ordered.defEqWF (H : Ordered env) (h : env.defeqs df) : df.WF env := by + induction H with + | empty => cases h + | const _ _ h3 ih => + refine .mono (addConst_le h3) (ih ?_) + unfold addConst at h3; split at h3 <;> cases h3; exact h + | defeq _ _ ih => + refine .mono addDefEq_le ?_ + obtain rfl | h := h + · assumption + · exact ih h + | structEta _ _ ih => exact .mono addStructEta_le (ih h) + +theorem Ordered.structEtaWF (H : Ordered env) (h : env.structEtas rule) : + VStructEta.WF rule env := by + induction H with + | empty => cases h + | const _ _ hadd ih => + refine VStructEta.WF.mono (addConst_le hadd) (ih ?_) + unfold VEnv.addConst at hadd + split at hadd <;> cases hadd + exact h + | defeq _ _ ih => exact VStructEta.WF.mono addDefEq_le (ih h) + | structEta _ hwf ih => + obtain rfl | h := h + · exact VStructEta.WF.mono addStructEta_le hwf + · exact VStructEta.WF.mono addStructEta_le (ih h) + +variable! (henv : Ordered env) in +theorem CtxWF.closed (h : OnCtx Γ (IsType env U)) : CtxClosed Γ := + match Γ, h with + | [], _ => trivial + | _::_, ⟨h1, _, h2⟩ => ⟨closed h1, h2.closedN henv (closed h1)⟩ + +variable {env : VEnv} in +theorem IsDefEq.levelWF (H : env.IsDefEq U Γ e1 e2 A) (W : OnCtx Γ fun _ A => A.LevelWF U) : + e1.LevelWF U ∧ e2.LevelWF U ∧ A.LevelWF U := by + induction H using IsDefEq.rec + (motive_2 := fun _ _ _ _ _ => True) with + | bvar h => + refine ⟨⟨⟩, ⟨⟩, ?_⟩ + induction h with + | zero => exact W.2.liftN + | succ _ ih => exact (ih W.1).liftN + | symm _ ih => let ⟨he, he', hA⟩ := ih W; exact ⟨he', he, hA⟩ + | trans _ _ ih1 ih2 => let ⟨he1, _, hA⟩ := ih1 W; let ⟨_, he3, _⟩ := ih2 W; exact ⟨he1, he3, hA⟩ + | sortDF h1 h2 => exact ⟨h1, h2, h1⟩ + | constDF _ h2 h3 => exact ⟨h2, h3, .instL h2⟩ + | appDF _ _ ih1 ih2 => + let ⟨hf, hf', _, hB⟩ := ih1 W; let ⟨ha, ha', _⟩ := ih2 W + exact ⟨⟨hf, ha⟩, ⟨hf', ha'⟩, hB.inst ha⟩ + | lamDF _ _ ih1 ih2 => + let ⟨hA, hA', _⟩ := ih1 W; let ⟨hb, hb', hB⟩ := ih2 ⟨W, hA⟩ + exact ⟨⟨hA, hb⟩, ⟨hA', hb'⟩, hA, hB⟩ + | forallEDF _ _ ih1 ih2 => + let ⟨hA, hA', hu⟩ := ih1 W; let ⟨hb, hb', hv⟩ := ih2 ⟨W, hA⟩ + exact ⟨⟨hA, hb⟩, ⟨hA', hb'⟩, hu, hv⟩ + | defeqDF _ _ ih1 ih2 => let ⟨_, hB, _⟩ := ih1 W; let ⟨he1, he2, _⟩ := ih2 W; exact ⟨he1, he2, hB⟩ + | beta _ _ ih1 ih2 => + let ⟨he', _, hA⟩ := ih2 W; let ⟨he, _, hB⟩ := ih1 ⟨W, hA⟩ + exact ⟨⟨⟨hA, he⟩, he'⟩, he.inst he', hB.inst he'⟩ + | eta _ ih => let ⟨he, _, hA, hB⟩ := ih W; exact ⟨⟨hA, he.liftN, ⟨⟩⟩, he, hA, hB⟩ + | structEta _ _ _ _ _ _ _ _ ihMajor ihRebuild => + let ⟨hmajor, _, htype⟩ := ihMajor W + let ⟨hrebuild, _, _⟩ := ihRebuild W + exact ⟨hrebuild, hmajor, htype⟩ + | proofIrrel _ _ _ _ ih2 ih3 => + let ⟨hh, _, hp⟩ := ih2 W; let ⟨hh', _, _⟩ := ih3 W + exact ⟨hh, hh', hp⟩ + | extra _ h2 => exact ⟨.instL h2, .instL h2, .instL h2⟩ + | nil | cons => trivial + +theorem HasType.const0 (H : env.constants c = some ci) (wf : ci.WF env) : + HasType env ci.uvars [] (.const c (VLevel.params ci.uvars)) ci.type := by + have := const H (ls := VLevel.params ci.uvars) VLevel.params_wf VLevel.params_length (Γ := []) + have ⟨_, h⟩ := wf + rwa [(IsDefEq.levelWF h trivial).1.instL_id] at this + +theorem IsDefEq.extra0 (H : env.defeqs df) (wf : df.WF env) : + IsDefEq env df.uvars [] df.lhs df.rhs df.type := by + have := extra H (ls := VLevel.params df.uvars) VLevel.params_wf VLevel.params_length (Γ := []) + let ⟨_, h2, h3⟩ := IsDefEq.levelWF wf.2 trivial + rwa [(IsDefEq.levelWF wf.1 trivial).1.instL_id, h2.instL_id, h3.instL_id] at this + +variable! (henv : Ordered env) in +theorem IsDefEq.weakN (W : Ctx.LiftN n k Γ Γ') (H : env.IsDefEq U Γ e1 e2 A) : + env.IsDefEq U Γ' (e1.liftN n k) (e2.liftN n k) (A.liftN n k) := by + induction H using IsDefEq.rec + (motive_2 := fun Γ A es B _ => ∀ {k Γ'}, Ctx.LiftN n k Γ Γ' → + env.SpineWF U Γ' (A.liftN n k) + (es.map fun e => e.liftN n k) (B.liftN n k)) + generalizing k Γ' with + | bvar h => refine .bvar (h.weakN W) + | symm _ ih => exact .symm (ih W) + | trans _ _ ih1 ih2 => exact .trans (ih1 W) (ih2 W) + | sortDF h1 h2 h3 => exact .sortDF h1 h2 h3 + | constDF h1 h2 h3 h4 h5 => + rw [(henv.closedC h1).instL.liftN_eq (Nat.zero_le _)] + exact .constDF h1 h2 h3 h4 h5 + | appDF _ _ ih1 ih2 => exact liftN_inst_hi .. ▸ .appDF (ih1 W) (ih2 W) + | lamDF _ _ ih1 ih2 => exact .lamDF (ih1 W) (ih2 W.succ) + | forallEDF _ _ ih1 ih2 => exact .forallEDF (ih1 W) (ih2 W.succ) + | defeqDF _ _ ih1 ih2 => exact .defeqDF (ih1 W) (ih2 W) + | beta _ _ ih1 ih2 => + exact VExpr.liftN_inst_hi .. ▸ VExpr.liftN_instN_hi .. ▸ .beta (ih1 W.succ) (ih2 W) + | eta _ ih => + have := IsDefEq.eta (ih W) + simp [liftN]; rwa [← lift_liftN'] + | @structEta rule levels _ params _ major hreg hlevels + hlevelsLength hparamsLength _ _ _ + ihSpine ihMajor ihRebuild => + have hparamsSpine := ihSpine W + rw [(henv.structEtaWF hreg).familyType_closed.instL.liftN_eq + (Nat.zero_le _)] at hparamsSpine + have hmajor := ihMajor W + rw [VStructEta.structureType_liftN] at hmajor + have hrebuild := ihRebuild W + rw [VStructEta.rebuild_liftN rule levels params major + hparamsLength n k, VStructEta.structureType_liftN] at hrebuild + have hout := IsDefEq.structEta hreg hlevels hlevelsLength + (by simpa using hparamsLength) hparamsSpine hmajor hrebuild + simpa only [VStructEta.rebuild_liftN rule levels params major + hparamsLength n k, VStructEta.structureType_liftN] using hout + | proofIrrel _ _ _ ih1 ih2 ih3 => exact .proofIrrel (ih1 W) (ih2 W) (ih3 W) + | extra h1 h2 h3 => + have ⟨⟨hA1, _⟩, hA2, hA3⟩ := henv.closed.2 h1 + rw [ + hA1.instL.liftN_eq (Nat.zero_le _), + hA2.instL.liftN_eq (Nat.zero_le _), + hA3.instL.liftN_eq (Nat.zero_le _)] + exact .extra h1 h2 h3 + | nil => exact .nil + | cons _ _ ihType ihRest => + exact .cons (ihType (by assumption)) (by + simpa only [VExpr.liftN_inst_hi] using ihRest (by assumption)) + +variable! (henv : Ordered env) in +theorem HasType.weakN (W : Ctx.LiftN n k Γ Γ') (H : env.HasType U Γ e A) : + env.HasType U Γ' (e.liftN n k) (A.liftN n k) := IsDefEq.weakN henv W H + +theorem SpineWF.weakN {env : VEnv} (henv : env.Ordered) + (W : Ctx.LiftN n k Γ Γ') : + ∀ {es A B}, env.SpineWF U Γ A es B → + env.SpineWF U Γ' (A.liftN n k) + (es.map fun e => e.liftN n k) (B.liftN n k) + | [], _, _, .nil => .nil + | _ :: _, _, _, .cons he hrest => + .cons (he.weakN henv W) (by + simpa only [VExpr.liftN_inst_hi] using SpineWF.weakN henv W hrest) + +variable! (henv : Ordered env) in +theorem IsType.weakN (W : Ctx.LiftN n k Γ Γ') (H : env.IsType U Γ A) : + env.IsType U Γ' (A.liftN n k) := let ⟨_, h⟩ := H; ⟨_, h.weakN henv W⟩ + +variable! (henv : Ordered env) in +theorem IsDefEq.weak (H : env.IsDefEq U Γ e1 e2 A) : + env.IsDefEq U (B::Γ) e1.lift e2.lift A.lift := H.weakN henv .one + +variable! (henv : Ordered env) in +theorem IsDefEqU.weak (H : env.IsDefEqU U Γ e1 e2) : + env.IsDefEqU U (B::Γ) e1.lift e2.lift := let ⟨_, H⟩ := H; ⟨_, H.weak henv⟩ + +variable! (henv : Ordered env) in +theorem IsDefEqU.weakN (W : Ctx.LiftN n k Γ Γ') (H : env.IsDefEqU U Γ e1 e2) : + env.IsDefEqU U Γ' (e1.liftN n k) (e2.liftN n k) := let ⟨_, H⟩ := H; ⟨_, H.weakN henv W⟩ + +variable! (henv : Ordered env) in +theorem IsDefEq.weakR (hΓ : CtxClosed Γ) (H : env.IsDefEq U Γ e1 e2 A) (Γ') : + env.IsDefEq U (Γ ++ Γ') e1 e2 A := by + have ⟨h1, h2, h3⟩ := H.closedN' henv.closed hΓ + simpa [h1.liftN_eq, h2.liftN_eq, h3.liftN_eq] using H.weakN henv (.right hΓ Γ') + +variable! (henv : Ordered env) in +theorem IsDefEq.weak0 (H : env.IsDefEq U [] e1 e2 A) : env.IsDefEq U Γ e1 e2 A := + H.weakR henv (Γ := []) ⟨⟩ _ + +variable! (henv : Ordered env) in +nonrec theorem HasType.weak0 (H : env.HasType U [] e A) : env.HasType U Γ e A := H.weak0 henv + +variable! (henv : Ordered env) in +theorem IsDefEqU.weak0 (H : env.IsDefEqU U [] e1 e2) : env.IsDefEqU U Γ e1 e2 := + let ⟨_, H⟩ := H; ⟨_, H.weak0 henv⟩ + +variable! (henv : Ordered env) in +theorem IsDefEq.weak' (W : Ctx.Lift' l Γ Γ') (H : env.IsDefEq U Γ e1 e2 A) : + env.IsDefEq U Γ' (e1.lift' l) (e2.lift' l) (A.lift' l) := by + generalize e : l.depth = n + induction n generalizing l Γ' with + | zero => simpa [lift'_depth_zero e, W.depth_zero e] using H + | succ n ih => + obtain ⟨l, k, rfl, rfl⟩ := Lift.depth_succ e + have ⟨Γ₁, W1, W2⟩ := W.of_cons_skip + rw [Lift.consN_skip_eq, lift'_comp, lift'_comp, lift'_comp, ← Lift.skipN_one, + lift'_consN_skipN, lift'_consN_skipN, lift'_consN_skipN] + exact (ih W1 Lift.depth_consN).weakN henv W2 + +variable! (henv : Ordered env) in +theorem HasType.weak' (W : Ctx.Lift' l Γ Γ') (H : env.HasType U Γ e A) : + env.HasType U Γ' (e.lift' l) (A.lift' l) := IsDefEq.weak' henv W H + +variable! (henv : Ordered env) in +theorem IsType.weak' (W : Ctx.Lift' l Γ Γ') (H : env.IsType U Γ A) : + env.IsType U Γ' (A.lift' l) := let ⟨_, h⟩ := H; ⟨_, h.weak' henv W⟩ + +variable! (henv : Ordered env) in +theorem IsDefEqU.weak' (W : Ctx.Lift' n Γ Γ') (H : env.IsDefEqU U Γ e1 e2) : + env.IsDefEqU U Γ' (e1.lift' n) (e2.lift' n) := let ⟨_, H⟩ := H; ⟨_, H.weak' henv W⟩ + +theorem IsType.lookup (henv : Ordered env) (h : OnCtx Γ (IsType env U)) (hL : Lookup Γ n A) : + env.IsType U Γ A := h.lookup hL <| .weakN henv .one + +variable! {env : VEnv} {ls : List VLevel} (hls : ∀ l ∈ ls, l.WF U') in +theorem IsDefEq.instL (H : env.IsDefEq U Γ e1 e2 A) : + env.IsDefEq U' (Γ.map (VExpr.instL ls)) (e1.instL ls) (e2.instL ls) (A.instL ls) := by + induction H using IsDefEq.rec + (motive_2 := fun Γ A es B _ => + env.SpineWF U' (Γ.map (VExpr.instL ls)) (A.instL ls) + (es.map (VExpr.instL ls)) (B.instL ls)) with + | bvar h => refine .bvar h.instL + | symm _ ih => exact .symm ih + | trans _ _ ih1 ih2 => exact .trans ih1 ih2 + | sortDF _ _ h3 => + exact .sortDF (VLevel.WF.inst hls) (VLevel.WF.inst hls) (VLevel.inst_congr_l h3) + | @constDF _ _ ls₁ ls₂ _ h1 h2 h3 h4 h5 => + simp [VExpr.instL, VExpr.instL_instL] + exact .constDF h1 (by simp [VLevel.WF.inst hls]) (by simp [VLevel.WF.inst hls]) + (by simp [h4]) (by simpa using h5.imp fun _ _ => VLevel.inst_congr_l) + | appDF _ _ ih1 ih2 => exact VExpr.instL_instN ▸ .appDF ih1 ih2 + | lamDF _ _ ih1 ih2 => exact .lamDF ih1 ih2 + | forallEDF _ _ ih1 ih2 => exact .forallEDF ih1 ih2 + | defeqDF _ _ ih1 ih2 => exact .defeqDF ih1 ih2 + | beta _ _ ih1 ih2 => simpa using .beta ih1 ih2 + | eta _ ih => simpa [VExpr.instL] using .eta ih + | @structEta rule levels _ params _ major hreg hlevels + hlevelsLength hparamsLength _ _ _ + ihSpine ihMajor ihRebuild => + have hlevels' : ∀ level ∈ levels.map (VLevel.inst ls), + level.WF U' := by + intro level hlevel + obtain ⟨source, hsource, heq⟩ := List.mem_map.1 hlevel + rw [← heq] + exact VLevel.WF.inst hls + rw [VStructEta.structureType_instL] at ihMajor + rw [VStructEta.rebuild_instL, + VStructEta.structureType_instL] at ihRebuild + rw [VExpr.instL_instL] at ihSpine + have hout := IsDefEq.structEta hreg hlevels' + (by simpa using hlevelsLength) + (by simpa using hparamsLength) ihSpine ihMajor ihRebuild + simpa only [VStructEta.rebuild_instL, + VStructEta.structureType_instL] using hout + | proofIrrel _ _ _ ih1 ih2 ih3 => exact .proofIrrel ih1 ih2 ih3 + | extra h1 h2 h3 => + simp [VExpr.instL_instL] + exact .extra h1 (by simp [VLevel.WF.inst hls]) (by simp [h3]) + | nil => exact .nil + | cons _ _ ihType ihRest => + exact .cons ihType (by simpa using ihRest) + +theorem HasType.instL {env : VEnv} (hls : ∀ l ∈ ls, l.WF U') (H : env.HasType U Γ e A) : + env.HasType U' (Γ.map (VExpr.instL ls)) (e.instL ls) (A.instL ls) := IsDefEq.instL hls H + +theorem SpineWF.instL {env : VEnv} (hls : ∀ l ∈ ls, l.WF U') : + ∀ {es A B}, env.SpineWF U Γ A es B → + env.SpineWF U' (Γ.map (VExpr.instL ls)) (A.instL ls) + (es.map (VExpr.instL ls)) (B.instL ls) + | [], _, _, .nil => .nil + | _ :: _, _, _, .cons he hrest => + .cons (he.instL hls) (by + simpa using SpineWF.instL hls hrest) + +theorem IsType.instL {env : VEnv} (hls : ∀ l ∈ ls, l.WF U') (H : env.IsType U Γ A) : + env.IsType U' (Γ.map (VExpr.instL ls)) (A.instL ls) := let ⟨_, h⟩ := H; ⟨_, h.instL hls⟩ + +theorem IsDefEqU.instL {env : VEnv} (hls : ∀ l ∈ ls, l.WF U') (H : env.IsDefEqU U Γ e1 e2) : + env.IsDefEqU U' (Γ.map (VExpr.instL ls)) (e1.instL ls) (e2.instL ls) := + let ⟨_, h⟩ := H; ⟨_, h.instL hls⟩ + +theorem _root_.Ix.Theory.Named.OnCtx.instL {env : VEnv} (hls : ∀ l ∈ ls, l.WF U') : + ∀ {Γ}, OnCtx Γ (env.IsType U) → OnCtx (Γ.map (VExpr.instL ls)) (env.IsType U') + | [], _ => trivial + | _::_, ⟨H1, H2⟩ => ⟨H1.instL hls, IsType.instL hls H2⟩ + +variable! (henv : Ordered env) (h₀ : env.HasType U Γ₀ e₀ A₀) in +theorem IsDefEq.instN (W : Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ) (H : env.IsDefEq U Γ₁ e1 e2 A) : + env.IsDefEq U Γ (e1.inst e₀ k) (e2.inst e₀ k) (A.inst e₀ k) := by + induction H using IsDefEq.rec + (motive_2 := fun Γ₁ A es B _ => ∀ {Γ k}, Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ → + env.SpineWF U Γ (A.inst e₀ k) + (es.map fun e => e.inst e₀ k) (B.inst e₀ k)) + generalizing Γ k with + | @bvar _ i ty h => + dsimp [inst] + induction W generalizing i ty with + | zero => + cases h with simp [inst_lift] + | zero => exact h₀ + | succ h => exact .bvar h + | succ _ ih => + cases h with (simp; rw [Nat.add_comm, ← VExpr.liftN_instN_lo (hj := Nat.zero_le _)]) + | zero => exact .bvar .zero + | succ h => exact (ih h).weak henv + | symm _ ih => exact .symm (ih W) + | trans _ _ ih1 ih2 => exact .trans (ih1 W) (ih2 W) + | sortDF h1 h2 h3 => exact .sortDF h1 h2 h3 + | constDF h1 h2 h3 h4 h5 => + rw [(henv.closedC h1).instL.instN_eq (Nat.zero_le _)] + exact .constDF h1 h2 h3 h4 h5 + | appDF _ _ ih1 ih2 => exact VExpr.inst_inst_hi .. ▸ .appDF (ih1 W) (ih2 W) + | lamDF _ _ ih1 ih2 => exact .lamDF (ih1 W) (ih2 W.succ) + | forallEDF _ _ ih1 ih2 => exact .forallEDF (ih1 W) (ih2 W.succ) + | defeqDF _ _ ih1 ih2 => exact .defeqDF (ih1 W) (ih2 W) + | beta _ _ ih1 ih2 => + exact VExpr.inst_inst_hi .. ▸ VExpr.inst_inst_hi .. ▸ .beta (ih1 W.succ) (ih2 W) + | eta _ ih => + have := IsDefEq.eta (ih W) + rw [lift, VExpr.liftN_instN_lo (hj := Nat.zero_le _), Nat.add_comm] at this + simpa [inst] + | @structEta rule levels _ params _ major hreg hlevels + hlevelsLength hparamsLength _ _ _ + ihSpine ihMajor ihRebuild => + have hparamsSpine := ihSpine W + rw [(henv.structEtaWF hreg).familyType_closed.instL.instN_eq + (Nat.zero_le _)] at hparamsSpine + have hmajor := ihMajor W + rw [VStructEta.structureType_instN] at hmajor + have hrebuild := ihRebuild W + rw [VStructEta.rebuild_instN rule levels params major e₀ + hparamsLength k, VStructEta.structureType_instN] at hrebuild + have hout := IsDefEq.structEta hreg hlevels hlevelsLength + (by simpa using hparamsLength) hparamsSpine hmajor hrebuild + simpa only [VStructEta.rebuild_instN rule levels params major e₀ + hparamsLength k, VStructEta.structureType_instN] using hout + | proofIrrel _ _ _ ih1 ih2 ih3 => exact .proofIrrel (ih1 W) (ih2 W) (ih3 W) + | extra h1 h2 h3 => + have ⟨⟨hA1, _⟩, hA2, hA3⟩ := henv.closed.2 h1 + rw [ + hA1.instL.instN_eq (Nat.zero_le _), + hA2.instL.instN_eq (Nat.zero_le _), + hA3.instL.instN_eq (Nat.zero_le _)] + exact .extra h1 h2 h3 + | nil => exact .nil + | cons _ _ ihType ihRest => + exact .cons (ihType (by assumption)) (by + simpa only [VExpr.inst0_inst_hi] using ihRest (by assumption)) + +theorem SpineWF.instN {env : VEnv} (henv : env.Ordered) + (W : Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ) + (h₀ : env.HasType U Γ₀ e₀ A₀) : + ∀ {es A B}, env.SpineWF U Γ₁ A es B → + env.SpineWF U Γ (A.inst e₀ k) + (es.map fun e => e.inst e₀ k) (B.inst e₀ k) + | [], _, _, .nil => .nil + | _ :: _, _, _, .cons he hrest => + .cons (IsDefEq.instN henv h₀ W he) (by + simpa only [VExpr.inst0_inst_hi] using + SpineWF.instN henv W h₀ hrest) + +theorem HasType.instN {env : VEnv} (henv : env.Ordered) (W : Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ) + (H : env.HasType U Γ₁ e A) (h₀ : env.HasType U Γ₀ e₀ A₀) : + env.HasType U Γ (e.inst e₀ k) (A.inst e₀ k) := IsDefEq.instN henv h₀ W H + +theorem IsType.instN {env : VEnv} (henv : env.Ordered) (W : Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ) + (H : env.IsType U Γ₁ A) (h₀ : env.HasType U Γ₀ e₀ A₀) : + env.IsType U Γ (A.inst e₀ k) := let ⟨_, h⟩ := H; ⟨_, h.instN henv W h₀⟩ + +theorem IsDefEqU.instN {env : VEnv} (henv : env.Ordered) (W : Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ) + (H : env.IsDefEqU U Γ₁ e1 e2) (h₀ : env.HasType U Γ₀ e₀ A₀) : + env.IsDefEqU U Γ (e1.inst e₀ k) (e2.inst e₀ k) := let ⟨_, h⟩ := H; ⟨_, h.instN henv h₀ W⟩ + +theorem _root_.Ix.Theory.Named.Ctx.InstN.wf (henv : Ordered env) (W : Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ) + (h₀ : env.HasType U Γ₀ e₀ A₀) (H : OnCtx Γ₁ (env.IsType U)) : + OnCtx Γ₀ (env.IsType U) ∧ OnCtx Γ (env.IsType U) := by + induction W with + | zero => exact ⟨H.1, H.1⟩ + | succ W ih => let ⟨h1, h2⟩ := ih H.1; exact ⟨h1, h2, .instN henv W H.2 h₀⟩ + +theorem IsDefEq.defeqDF_l' (henv : Ordered env) (h1 : env.IsDefEq U Γ A A' (.sort u)) + (h2 : env.IsDefEq U (Δ++A::Γ) e1 e2 B) : env.IsDefEq U (Δ++A'::Γ) e1 e2 B := by + have ⟨_, H1, H2⟩ : ∃ Γ', Ctx.LiftN 1 (Δ.length + 1) (Δ ++ A :: Γ) Γ' ∧ + Ctx.InstN (A' :: Γ) (VExpr.bvar 0) (liftN 1 A) Δ.length Γ' (Δ ++ A' :: Γ) := by + clear h1 h2 + induction Δ with + | nil => exact ⟨_, .succ (.one (A := A')), .zero⟩ + | cons B Δ ih => + have ⟨Γ', h1, h2⟩ := ih + exact ⟨_, .succ h1, by simpa [instN_bvar0] using h2.succ (A := liftN 1 B (Δ.length + 1))⟩ + simpa [instN_bvar0] using + instN henv (h1.weakN henv (.one (A := A')) |>.symm.defeq (.bvar .zero)) H2 (.weakN henv H1 h2) + +theorem IsDefEq.defeqDF_l (henv : Ordered env) (h1 : env.IsDefEq U Γ A A' (.sort u)) + (h2 : env.IsDefEq U (A::Γ) e1 e2 B) : env.IsDefEq U (A'::Γ) e1 e2 B := + .defeqDF_l' (Δ := []) henv h1 h2 + +theorem HasType.defeq_l (henv : Ordered env) (h1 : env.IsDefEq U Γ A A' (.sort u)) + (h2 : env.HasType U (A::Γ) e B) : env.HasType U (A'::Γ) e B := h1.defeqDF_l henv h2 + +theorem IsDefEq.defeqDFC' (henv : Ordered env) (h1 : IsDefEqCtx env U Γ₀ Γ₁ Γ₂) + (h2 : env.IsDefEq U (Δ ++ Γ₁) e₁ e₂ A) : env.IsDefEq U (Δ ++ Γ₂) e₁ e₂ A := by + induction h1 generalizing e₁ e₂ A Δ with + | zero => exact h2 + | @succ _ _ _ A₂ _ _ AA ih => + simpa using ih (Δ := Δ ++ [A₂]) (by simpa using AA.defeqDF_l' henv h2) + +theorem IsDefEq.defeqDFC (henv : Ordered env) (h1 : IsDefEqCtx env U Γ₀ Γ₁ Γ₂) + (h2 : env.IsDefEq U Γ₁ e₁ e₂ A) : env.IsDefEq U Γ₂ e₁ e₂ A := .defeqDFC' (Δ := []) henv h1 h2 + +theorem HasType.defeqDFC (henv : Ordered env) (h1 : IsDefEqCtx env U Γ₀ Γ₁ Γ₂) + (h2 : env.HasType U Γ₁ e A) : env.HasType U Γ₂ e A := IsDefEq.defeqDFC henv h1 h2 + +theorem IsType.defeqDFC (henv : Ordered env) (h1 : IsDefEqCtx env U Γ₀ Γ₁ Γ₂) + (h2 : env.IsType U Γ₁ A) : env.IsType U Γ₂ A := h2.imp fun _ => (·.defeqDFC henv h1) + +theorem IsDefEqU.defeqDFC (henv : Ordered env) (h1 : IsDefEqCtx env U Γ₀ Γ₁ Γ₂) + (h2 : env.IsDefEqU U Γ₁ e₁ e₂) : env.IsDefEqU U Γ₂ e₁ e₂ := + let ⟨_, h2⟩ := h2; ⟨_, h2.defeqDFC henv h1⟩ + +theorem IsDefEqCtx.symm (henv : Ordered env) : + IsDefEqCtx env U Γ₀ Γ₁ Γ₂ → IsDefEqCtx env U Γ₀ Γ₂ Γ₁ + | .zero => .zero + | .succ h1 h2 => .succ (h1.symm henv) (h2.symm.defeqDFC henv h1) + +variable! (henv : Ordered env) + (envIH : env.OnTypes fun U e _ => ∀ A B, e = A.forallE B → + env.IsType U [] A ∧ env.IsType U [A] B) in +theorem IsDefEq.forallE_inv' + (H : env.IsDefEq U Γ e1 e2 V) (eq : e1 = A.forallE B ∨ e2 = A.forallE B) : + env.IsType U Γ A ∧ env.IsType U (A::Γ) B := by + induction H using IsDefEq.rec + (motive_2 := fun _ _ _ _ _ => True) + generalizing A B with + | symm _ ih => exact ih eq.symm + | trans _ _ ih1 ih2 + | proofIrrel _ _ _ _ ih1 ih2 => + obtain eq | eq := eq + · exact ih1 (.inl eq) + · exact ih2 (.inr eq) + | forallEDF h1 h2 => + obtain ⟨⟨⟩⟩ | ⟨⟨⟩⟩ := eq + · exact ⟨⟨_, h1.hasType.1⟩, _, h2.hasType.1⟩ + · exact ⟨⟨_, h1.hasType.2⟩, _, h2.hasType.2.defeq_l henv h1⟩ + | defeqDF _ _ _ ih2 => exact ih2 eq + | @beta _ _ e _ _ h1 h2 ih1 ih2 => + obtain ⟨⟨⟩⟩ | eq := eq + cases e with + | bvar i => + cases i with simp [inst] at eq + | zero => exact ih2 (.inl eq) + | forallE A B => + cases eq + let ⟨A1, A2⟩ := ih1 (.inl rfl) + exact ⟨A1.instN henv .zero h2, A2.instN henv (.succ .zero) h2⟩ + | _ => cases eq + | eta _ ih => + obtain ⟨⟨⟩⟩ | eq := eq + exact ih (.inl eq) + | structEta _ _ _ _ _ _ _ _ ihMajor ihRebuild => + obtain eq | eq := eq + · exact ihRebuild (.inl eq) + · exact ihMajor (.inl eq) + | @extra df ls Γ h1 h2 => + suffices ∀ e, VExpr.instL ls e = VExpr.forallE A B → + (∀ A B, e = VExpr.forallE A B → IsType env df.uvars [] A ∧ IsType env df.uvars [A] B) → + IsType env U Γ A ∧ IsType env U (A :: Γ) B by + have ⟨A1, A2⟩ := envIH.2 h1 + cases eq <;> exact this _ ‹_› ‹_› + intro e eq IH + cases e <;> cases eq; rename_i A B + let ⟨⟨_, A1⟩, v, A2⟩ := IH _ _ rfl + refine ⟨⟨_, (A1.instL h2).weak0 henv⟩, v.inst ls, ?_⟩ + have := (A2.instL h2).weakN henv (.succ (.zero Γ)) + have C1 := (A1.instL h2).closedN henv ⟨⟩ + have C2 := (A2.instL h2).closedN henv ⟨⟨⟩, C1⟩ + rw [C1.liftN_eq (Nat.zero_le _), C2.liftN_eq (by exact Nat.le_refl _)] at this + simpa [liftN] + | nil | cons => trivial + | _ => nomatch eq + +theorem HasType.forallE_inv (henv : Ordered env) (H : env.HasType U Γ (A.forallE B) V) : + env.IsType U Γ A ∧ env.IsType U (A::Γ) B := by + refine H.forallE_inv' henv ?_ (.inl rfl) + exact henv.induction + (fun env U e _ => ∀ A B, e = .forallE A B → IsType env U [] A ∧ IsType env U [A] B) + (fun le H A B eq => (H A B eq).imp (.mono le) (.mono le)) + (fun henv IH H A B eq => H.forallE_inv' henv IH (.inl eq)) + +theorem IsType.forallE_inv (henv : Ordered env) (H : env.IsType U Γ (A.forallE B)) : + env.IsType U Γ A ∧ env.IsType U (A::Γ) B := let ⟨_, h⟩ := H; h.forallE_inv henv + +variable! (henv : Ordered env) in +theorem IsDefEq.sort_inv' + (H : env.IsDefEq U Γ e1 e2 V) (eq : e1 = .sort u ∨ e2 = .sort u) : u.WF U := by + induction H using IsDefEq.rec + (motive_2 := fun _ _ _ _ _ => True) with + | symm _ ih => exact ih eq.symm + | trans _ _ ih1 ih2 + | proofIrrel _ _ _ _ ih1 ih2 => + obtain eq | eq := eq <;> [exact ih1 (.inl eq); exact ih2 (.inr eq)] + | sortDF => obtain ⟨⟨⟩⟩ | ⟨⟨⟩⟩ := eq <;> assumption + | defeqDF _ _ _ ih2 => exact ih2 eq + | @beta _ _ e _ _ h1 _ ih1 ih2 => + obtain ⟨⟨⟩⟩ | eq := eq + cases e with + | bvar i => + cases i with simp [inst] at eq + | zero => exact ih2 (.inl eq) + | _ => cases eq <;> exact ih1 (.inl rfl) + | eta _ ih => + obtain ⟨⟨⟩⟩ | eq := eq + exact ih (.inl eq) + | structEta _ _ _ _ _ _ _ _ ihMajor ihRebuild => + obtain eq | eq := eq + · exact ihRebuild (.inl eq) + · exact ihMajor (.inl eq) + | @extra df ls _ h1 h2 => + suffices ∀ e, VExpr.instL ls e = .sort u → HasType env df.uvars [] e df.type → u.WF U by + have ⟨A1, A2⟩ := henv.defEqWF h1 + cases eq <;> exact this _ ‹_› ‹_› + intro e eq IH + cases e <;> cases eq; rename_i u + exact VLevel.WF.inst h2 + | nil | cons => trivial + | _ => nomatch eq + +theorem IsDefEq.sort_inv_l (henv : Ordered env) (H : env.IsDefEq U Γ (.sort u) e2 V) : u.WF U := + H.sort_inv' henv (.inl rfl) + +theorem IsDefEq.sort_inv_r (henv : Ordered env) (H : env.IsDefEq U Γ e2 (.sort u) V) : u.WF U := + H.sort_inv' henv (.inr rfl) + +theorem HasType.sort_inv (henv : Ordered env) (H : env.HasType U Γ (.sort u) V) : u.WF U := + H.sort_inv_l henv + +theorem IsType.sort_inv (henv : Ordered env) (H : env.IsType U Γ (.sort u)) : u.WF U := + let ⟨_, h⟩ := H; h.sort_inv henv + +variable! (henv : Ordered env) + (envIH : env.OnTypes fun U e A => env.HasType U [] e A ∧ env.IsType U [] A) in +theorem IsDefEq.isType' (hΓ : OnCtx Γ (env.IsType U)) (H : env.IsDefEq U Γ e1 e2 A) : + env.IsType U Γ A := by + induction H using IsDefEq.rec + (motive_2 := fun _ _ _ _ _ => True) with + | bvar h => exact .lookup henv hΓ h + | proofIrrel h1 => exact ⟨_, h1⟩ + | extra h1 h2 => + have ⟨_, _, _, h⟩ := envIH.2 h1 + exact ⟨_, (h.instL h2).weak0 henv⟩ + | sortDF h1 => exact ⟨_, .sort h1⟩ + | constDF h1 h2 => + let ⟨_, h, _⟩ := envIH.1 h1 + exact ⟨_, (h.instL h2).weak0 henv⟩ + | symm _ ih => exact ih hΓ + | trans _ _ ih1 => exact ih1 hΓ + | appDF _ h2 ih1 => exact ((ih1 hΓ).forallE_inv henv).2.instN henv .zero h2.hasType.1 + | lamDF h1 _ _ ih2 => + let ⟨_, h⟩ := ih2 ⟨hΓ, _, h1.hasType.1⟩ + exact ⟨_, .forallE h1.hasType.1 h⟩ + | forallEDF h1 _ ih1 ih2 => + exact ⟨_, .sort ⟨(ih1 hΓ).sort_inv henv, (ih2 ⟨hΓ, _, h1.hasType.1⟩).sort_inv henv⟩⟩ + | defeqDF h1 => exact ⟨_, h1.hasType.2⟩ + | beta _ h2 ih1 ih2 => + have ⟨_, h⟩ := ih2 hΓ + exact (ih1 ⟨hΓ, _, h.hasType.2⟩).instN henv .zero h2 + | eta _ ih => exact ih hΓ + | structEta _ _ _ _ _ _ _ _ ihMajor _ => exact ihMajor hΓ + | nil | cons => trivial + +theorem Ordered.isType (H : Ordered env) : + env.OnTypes fun U e A => env.HasType U [] e A ∧ env.IsType U [] A := + H.induction (fun env U e A => env.HasType U [] e A ∧ env.IsType U [] A) + (fun h1 h2 => h2.imp (.mono h1) (.mono h1)) + (fun henv ih h => ⟨h, h.isType' henv ih trivial⟩) + +theorem IsDefEq.isType (henv : Ordered env) (hΓ : OnCtx Γ (env.IsType U)) + (H : env.IsDefEq U Γ e1 e2 A) : env.IsType U Γ A := H.isType' henv henv.isType hΓ + +theorem IsDefEq.sort_r (henv : Ordered env) + (hΓ : OnCtx Γ (env.IsType U)) (H : env.IsDefEq U Γ e1 e2 (.sort u)) : u.WF U := + (H.isType henv hΓ).sort_inv henv + +theorem IsDefEq.instDF + (henv : Ordered env) (hΓ : OnCtx Γ (env.IsType U)) + (hf : env.IsDefEq U (A::Γ) f f' B) (ha : env.IsDefEq U Γ a a' A) : + env.IsDefEq U Γ (f.inst a) (f'.inst a') (B.inst a) := + have ⟨_, hA⟩ := ha.isType henv hΓ + have ⟨_, hB⟩ := hf.isType henv (Γ := _::_) ⟨hΓ, _, hA⟩ + have H2 {f f' B v} + (hf : env.IsDefEq U (A::Γ) f f' B) + (hi : IsDefEq env U Γ (inst B a) (inst B a') (.sort v)) : + env.IsDefEq U Γ (f.inst a) (f'.inst a') (B.inst a) := + (IsDefEq.beta hf.hasType.1 ha.hasType.1).symm.trans <| + .trans (.appDF (.lamDF hA hf) ha) <| + .defeqDF (.symm hi) (.beta hf.hasType.2 ha.hasType.2) + H2 hf <| H2 hB (HasType.sort (hB.sort_r henv (Γ := _::_) ⟨hΓ, _, hA⟩)) diff --git a/Ix/Theory/Named/Typing/Meta.lean b/Ix/Theory/Named/Typing/Meta.lean new file mode 100644 index 000000000..f17cc9a5a --- /dev/null +++ b/Ix/Theory/Named/Typing/Meta.lean @@ -0,0 +1,54 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Typing.Lemmas + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named +namespace VEnv + +theorem with_addConst {P : Prop} {env env' : VEnv} {cis : List VObject} + (henv : Ordered env) + (hci : env.HasObjects cis → ci.WF env) + (IH : ∀ {env1}, Ordered env1 → + env1.HasObjects (.const n ci :: cis) → f env1 = some env' → P) + (hcis : env.HasObjects cis) + (H : (env.addConst n ci >>= f) = some env') : P := by + let ⟨env1, h1, henv1⟩ := Option.bind_eq_some_iff.1 H + refine IH (.const henv (hci hcis) h1) (hcis.const h1) henv1 + +theorem Lookup.zero' (eq : A.lift = ty') : + Lookup (A::Γ) 0 ty' := eq ▸ .zero +theorem Lookup.succ' (h : Lookup Γ n ty) (eq : ty.lift = ty') : + Lookup (A::Γ) (n+1) ty' := eq ▸ .succ h + +syntax "lookup_tac" : tactic +macro_rules | `(tactic| lookup_tac) => `(tactic| + first + | refine Lookup.zero' ?_; exact rfl + | refine Lookup.succ' (ty := ?_) ?_ ?_ <;> [skip; lookup_tac; exact rfl] +) + +theorem HasType.app' (h1 : HasType env U Γ f (.forallE A B)) (h2 : HasType env U Γ a A) + (eq : B.inst a = B') : HasType env U Γ (.app f a) B' := eq ▸ .appDF h1 h2 +theorem HasType.const' + (h1 : constants env c = some ci) + (h2 : ∀ (l : VLevel), l ∈ ls → VLevel.WF uvars l) + (h3 : List.length ls = ci.uvars) + (eq : .instL ls ci.type = ty') : + HasType env uvars Γ (.const c ls) ty' := eq ▸ .const h1 h2 h3 + +syntax "type_tac" : tactic -- TODO: write an actual tactic +macro_rules | `(tactic| type_tac) => `(tactic| + first + | refine HasType.forallE (u := ?_) (v := ?_) ?_ ?_ <;> [skip; skip; type_tac; type_tac] + | refine HasType.sort ?_; decide + | refine HasType.bvar ?_; lookup_tac + | refine HasType.app' (A := ?_) (B := ?_) ?_ ?_ ?_ <;> [skip; skip; type_tac; type_tac; exact rfl] + | refine HasType.const' (ci := ?_) ?_ ?_ ?_ ?_ <;> [skip; assumption; decide; decide; exact rfl] + | refine HasType.lam (u := ?_) ?_ ?_ <;> [skip; type_tac; type_tac] +) diff --git a/Ix/Theory/Named/Typing/NestedInductiveLemmas.lean b/Ix/Theory/Named/Typing/NestedInductiveLemmas.lean new file mode 100644 index 000000000..1a8ad71d8 --- /dev/null +++ b/Ix/Theory/Named/Typing/NestedInductiveLemmas.lean @@ -0,0 +1,190 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.NestedInductive +import Ix.Theory.Named.Typing.InductiveLemmas + +/-! +# Nested transaction facts and preservation (Spec-09C) + +The `addInductNested` analog of the block-wide transaction lemma suite: +exact phase recovery, atomicity, monotonicity, freshness, lookup and rule +membership through `ctorFold_spec`/`rulesFold_spec`, and `Ordered` +preservation from the `NestedBlockChecked.WF` package. +-/ + +namespace Ix.Theory.Named + +open VInductDecl + +namespace VEnv + +/-- Recover every phase boundary from a successful nested transaction. -/ +theorem addInductNested_trace {source : VInductDecl} + {nested : source.NestedBlockChecked} + (hadd : addInductNested env nested = some env') : + Nonempty (AddInductNestedTrace env env' nested) := by + unfold addInductNested at hadd + obtain ⟨typeEnv, addTypes, hadd⟩ := Option.bind_eq_some_iff.1 hadd + obtain ⟨ctorEnv, addCtors, hadd⟩ := Option.bind_eq_some_iff.1 hadd + obtain ⟨recEnv, addRecs, hadd⟩ := Option.bind_eq_some_iff.1 hadd + cases hadd + exact ⟨⟨typeEnv, ctorEnv, recEnv, addTypes, addCtors, addRecs, rfl⟩⟩ + +/-- The nested transaction is atomic at its public `Option` boundary. -/ +theorem addInductNested_atomic {source : VInductDecl} + (env : VEnv) (nested : source.NestedBlockChecked) : + addInductNested env nested = none ∨ + ∃ env', addInductNested env nested = some env' ∧ + Nonempty (AddInductNestedTrace env env' nested) := by + cases hadd : addInductNested env nested with + | none => exact .inl rfl + | some env' => exact .inr ⟨env', rfl, addInductNested_trace hadd⟩ + +namespace AddInductNestedTrace + +variable {source : VInductDecl} {nested : source.NestedBlockChecked} + +/-- Every phase of a successful nested transaction only grows the Theory +environment. -/ +theorem le (H : AddInductNestedTrace env env' nested) : env ≤ env' := by + have htypes := (ctorFold_spec source.blockTypeConstants H.addTypes).1 + have hctors := (ctorFold_spec source.blockConstructorConstants H.addCtors).1 + have hrecs := (ctorFold_spec nested.recursors H.addRecs).1 + have hrules : H.recEnv ≤ env' := by + simpa only [H.addRules] using + (rulesFold_spec nested.generatedRules H.recEnv).1 + exact htypes.trans (hctors.trans (hrecs.trans hrules)) + +/-- Every source family name was fresh before the transaction. -/ +theorem family_fresh (H : AddInductNestedTrace env env' nested) + {type : VInductiveType} (htype : type ∈ source.types) : + env.constants type.name = none := by + have hmem : type.toVConstVal ∈ source.blockTypeConstants := + List.mem_map.2 ⟨type, htype, rfl⟩ + simpa [VInductDecl.blockTypeConstants] using + (ctorFold_spec source.blockTypeConstants H.addTypes).2.2 + type.toVConstVal hmem + +/-- The final environment stores every exact source family constant. -/ +theorem family_lookup (H : AddInductNestedTrace env env' nested) + {type : VInductiveType} (htype : type ∈ source.types) : + env'.constants type.name = some type.toVConstant := by + have hmem : type.toVConstVal ∈ source.blockTypeConstants := + List.mem_map.2 ⟨type, htype, rfl⟩ + have hlookup := + (ctorFold_spec source.blockTypeConstants H.addTypes).2.1 + type.toVConstVal hmem + have hctors := (ctorFold_spec source.blockConstructorConstants H.addCtors).1 + have hrecs := (ctorFold_spec nested.recursors H.addRecs).1 + have hrules : H.recEnv ≤ env' := by + simpa only [H.addRules] using + (rulesFold_spec nested.generatedRules H.recEnv).1 + exact (hctors.trans (hrecs.trans hrules)).constants hlookup + +/-- Every flattened source constructor name was fresh before the nested +transaction. -/ +theorem ctor_fresh (H : AddInductNestedTrace env env' nested) + {c : VConstVal} (hc : c ∈ source.blockConstructorConstants) : + env.constants c.name = none := by + have htypes := (ctorFold_spec source.blockTypeConstants H.addTypes).1 + have hfresh := + (ctorFold_spec source.blockConstructorConstants H.addCtors).2.2 c hc + exact htypes.constants_none hfresh + +/-- The final environment stores every exact source constructor +constant. -/ +theorem ctor_lookup (H : AddInductNestedTrace env env' nested) + {type : VInductiveType} (htype : type ∈ source.types) + {c : VConstVal} (hc : c ∈ type.ctors) : + env'.constants c.name = some c.toVConstant := by + have hmem : c ∈ source.blockConstructorConstants := + List.mem_flatMap.2 ⟨type, htype, hc⟩ + have hlookup := + (ctorFold_spec source.blockConstructorConstants H.addCtors).2.1 c hmem + have hrecs := (ctorFold_spec nested.recursors H.addRecs).1 + have hrules : H.recEnv ≤ env' := by + simpa only [H.addRules] using + (rulesFold_spec nested.generatedRules H.recEnv).1 + exact (hrecs.trans hrules).constants hlookup + +/-- The final environment stores every restored recursor constant. -/ +theorem rec_lookup (H : AddInductNestedTrace env env' nested) + {recursor : VConstVal} (hrec : recursor ∈ nested.recursors) : + env'.constants recursor.name = some recursor.toVConstant := by + have hlookup := (ctorFold_spec nested.recursors H.addRecs).2.1 recursor hrec + have hrules : H.recEnv ≤ env' := by + simpa only [H.addRules] using + (rulesFold_spec nested.generatedRules H.recEnv).1 + exact hrules.constants hlookup + +/-- Every restored recursor name was fresh before the nested transaction. -/ +theorem rec_fresh (H : AddInductNestedTrace env env' nested) + {recursor : VConstVal} (hrec : recursor ∈ nested.recursors) : + env.constants recursor.name = none := by + have htypes := (ctorFold_spec source.blockTypeConstants H.addTypes).1 + have hctors := + (ctorFold_spec source.blockConstructorConstants H.addCtors).1 + have hfresh := (ctorFold_spec nested.recursors H.addRecs).2.2 recursor hrec + exact (htypes.trans hctors).constants_none hfresh + +/-- The final environment registers every restored rule. -/ +theorem rule_mem (H : AddInductNestedTrace env env' nested) + {df : VDefEq} (hdf : df ∈ nested.generatedRules) : + env'.defeqs df := by + simpa only [H.addRules] using + (rulesFold_spec nested.generatedRules H.recEnv).2 df hdf + +end AddInductNestedTrace + +nonrec theorem addInductNested_le {source : VInductDecl} + {nested : source.NestedBlockChecked} + (hadd : addInductNested env nested = some env') : env ≤ env' := by + obtain ⟨H⟩ := addInductNested_trace hadd + exact H.le + +end VEnv + +/-- A chained constant package folds into `Ordered` preservation. -/ +theorem NestedConstsWF.fold_ordered : + ∀ {cs : List VConstVal} {env env' : VEnv}, + VEnv.Ordered env → NestedConstsWF env cs → + cs.foldlM (fun env c => env.addConst c.name c.toVConstant) env = + some env' → + VEnv.Ordered env' + | [], _, _, h, _, hf => by cases hf; exact h + | c :: cs, env, env', h, hwf, hf => by + rw [List.foldlM_cons] at hf + obtain ⟨env₁, hadd, htail⟩ := Option.bind_eq_some_iff.1 hf + exact NestedConstsWF.fold_ordered (.const h hwf.1 hadd) + (hwf.2 env₁ hadd) htail + +/-- A chained rule package folds into `Ordered` preservation. -/ +theorem NestedRulesWF.fold_ordered : + ∀ {dfs : List VDefEq} {env : VEnv}, + VEnv.Ordered env → NestedRulesWF env dfs → + VEnv.Ordered (dfs.foldl VEnv.addDefEq env) + | [], _, h, _ => h + | df :: dfs, env, h, hwf => by + rw [List.foldl_cons] + exact NestedRulesWF.fold_ordered (.defeq h hwf.1) hwf.2 + +/-- `Ordered` preservation for the nested transaction. -/ +theorem VEnv.addInductNested_WF {source : VInductDecl} + {nested : source.NestedBlockChecked} + (ih : VEnv.Ordered env) (h1 : nested.WF env) + (h2 : addInductNested env nested = some env') : VEnv.Ordered env' := by + unfold addInductNested at h2 + obtain ⟨typeEnv, addTypes, h2⟩ := Option.bind_eq_some_iff.1 h2 + obtain ⟨ctorEnv, addCtors, h2⟩ := Option.bind_eq_some_iff.1 h2 + obtain ⟨recEnv, addRecs, h2⟩ := Option.bind_eq_some_iff.1 h2 + cases h2 + have hT := NestedConstsWF.fold_ordered ih h1.types addTypes + have hC := NestedConstsWF.fold_ordered hT (h1.ctors addTypes) addCtors + have hR := NestedConstsWF.fold_ordered hC (h1.recs addTypes addCtors) addRecs + exact NestedRulesWF.fold_ordered hR (h1.rules addTypes addCtors addRecs) + +end Ix.Theory.Named diff --git a/Ix/Theory/Named/Typing/Pattern.lean b/Ix/Theory/Named/Typing/Pattern.lean new file mode 100644 index 000000000..7927638e6 --- /dev/null +++ b/Ix/Theory/Named/Typing/Pattern.lean @@ -0,0 +1,466 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.VExpr + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named + +open VExpr + +inductive Pattern where + | const (c : Name) + | app (f a : Pattern) + | var (f : Pattern) + +@[reducible] def Pattern.varN (p : Pattern) : Nat → Pattern + | 0 => p + | n+1 => (p.varN n).var + +inductive Subpattern (p : Pattern) : Pattern → Prop where + | refl : Subpattern p p + | appL : Subpattern p f → Subpattern p (.app f a) + | appR : Subpattern p a → Subpattern p (.app f a) + | varL : Subpattern p f → Subpattern p (.var f) + +theorem Subpattern.varN (h : Subpattern p f) : ∀ {n}, Subpattern p (.varN f n) + | 0 => h + | _+1 => .varL (.varN h) + +theorem Subpattern.trans {p₁ p₂ p₃} (H₁ : Subpattern p₁ p₂) (H₂ : Subpattern p₂ p₃) : Subpattern p₁ p₃ := by + induction H₂ with + | refl => exact H₁ + | appL _ ih => exact .appL ih + | appR _ ih => exact .appR ih + | varL _ ih => exact .varL ih + +theorem Subpattern.sizeOf_le {p₁ p₂} (H₁ : Subpattern p₁ p₂) : sizeOf p₁ ≤ sizeOf p₂ := by + induction H₁ <;> simp <;> omega + +theorem Subpattern.antisymm {p₁ p₂} (H₁ : Subpattern p₁ p₂) (H₂ : Subpattern p₂ p₁) : p₂ = p₁ := by + cases id H₂ with + | refl => rfl + | _ h₂ => + have H₁ := H₁.sizeOf_le + have h₂ := h₂.sizeOf_le + simp at H₁; omega + +inductive Arity (p : Pattern) : Nat → Pattern → Prop where + | refl : Arity p 0 p + | app : Arity p n f → Arity p (n+1) (.app f a) + | var : Arity p n f → Arity p (n+1) (.var f) + +theorem Arity.subpattern : Arity p n p' → Subpattern p p' + | .refl => .refl + | .app h => .appL h.subpattern + | .var h => .varL h.subpattern + +def Pattern.inter : Pattern → Pattern → Option Pattern + | .const c, .const c' => if c = c' then some (.const c) else none + | .app f a, .app f' a' => return .app (← f.inter f') (← a.inter a') + | .var f, .var f' => return .var (← f.inter f') + | .app f a, .var f' => return .app (← f.inter f') a + | .var f, .app f' a' => return .app (← f.inter f') a' + | _, _ => none + +theorem Pattern.inter_self (p : Pattern) : p.inter p = some p := by induction p <;> simp [*, inter] + +theorem Pattern.inter_comm (p q : Pattern) : p.inter q = q.inter p := by + induction p generalizing q <;> cases q <;> simp [*, eq_comm, inter] <;> split <;> simp [*] + +inductive Pattern.LE : Pattern → Pattern → Prop where + | refl : LE p p + | var : LE f f' → LE (.var f) (.var f') + | app : LE f f' → LE a a' → LE (.app f a) (.app f' a') + | app_var : LE f f' → LE (.app f a) (.var f') + +@[reducible] def Pattern.Path : Pattern → Type + | .const _ => Empty + | .app f a => f.Path ⊕ a.Path + | .var f => Option f.Path + +inductive Pattern.Matches : (p : Pattern) → VExpr → List VLevel → (p.Path → VExpr) → Prop + | const : Matches (.const c) (.const c ls) ls nofun + | var : Matches f f' f1 g1 → Matches (.var f) (.app f' a') f1 (·.elim a' g1) + | app : Matches f f' f1 g1 → Matches a a' f2 g2 → + Matches (.app f a) (.app f' a') f1 (Sum.elim g1 g2) + +theorem Pattern.Matches.uniq {p : Pattern} {e : VExpr} {m1 m2 m1' m2'} + (H1 : Pattern.Matches p e m1 m2) (H2 : Pattern.Matches p e m1' m2') : m1 = m1' ∧ m2 = m2' := by + induction H1 generalizing m1' with cases H2 + | const => simp + | var _ ih => rename_i h; simp [ih h] + | app _ _ ih1 ih2 => rename_i h2 h1; simp [ih1 h1, ih2 h2] + +def Pattern.OnArgs (P : VExpr → Prop) : Pattern → Prop + | .const .. => True + | .var f => f.OnArgs P + | .app f a => f.OnArgs P ∧ a.OnArgs P ∧ ∀ e m1 m2, a.Matches e m1 m2 → P e + +inductive Pattern.RHS (p : Pattern) where + | fixed (c : VExpr) (_ : c.Closed) + | app (f a : RHS p) + | var (e : p.Path) + +inductive Pattern.Check (p : Pattern) where + | true + | defeq (x y : RHS p) (rest : Check p) + +def Pattern.RHS.apply {p : Pattern} (m1 : List VLevel) (m2 : p.Path → VExpr) : p.RHS → VExpr + | .fixed c _ => c.instL m1 + | .var path => m2 path + | .app f a => .app (f.apply m1 m2) (a.apply m1 m2) + +theorem Pattern.RHS.lift'_apply {p : Pattern} {m1 m2} (r : p.RHS) : + (r.apply m1 m2).lift' ρ = (r.apply m1 fun x => (m2 x).lift' ρ) := by + induction r <;> simp [*, apply, lift', ← instL_lift'] + rw [ClosedN.lift'_eq ‹_› (by trivial)] + +theorem Pattern.RHS.liftN_apply {p : Pattern} {m1 m2} (r : p.RHS) : + (r.apply m1 m2).liftN n k = (r.apply m1 fun x => (m2 x).liftN n k) := by + simp [← lift'_consN_skipN, lift'_apply] + +theorem Pattern.matches_lift' {p : Pattern} {e : VExpr} {m1 m2'} : + p.Matches (e.lift' ρ) m1 m2' ↔ + ∃ m2, p.Matches e m1 m2 ∧ ∀ x, m2' x = (m2 x).lift' ρ := by + constructor + · intro h; generalize eq : e.lift' ρ = e' at h + induction h generalizing e with + | const => cases e <;> cases eq; exact ⟨_, .const, nofun⟩ + | var _ ih => + cases e <;> cases eq + have ⟨_, l1, l2⟩ := ih rfl + refine ⟨_, .var l1, ?_⟩ + rintro (_|_) <;> solve_by_elim + | app _ _ ih1 ih2 => + cases e <;> cases eq + have ⟨_, l1, l2⟩ := ih1 rfl + have ⟨_, r1, r2⟩ := ih2 rfl + refine ⟨_, .app l1 r1, ?_⟩ + rintro (_|_) <;> solve_by_elim + · intro ⟨m2, h1, h2⟩ + induction h1 with + | const => exact (show m2' = _ by ext ⟨⟩) ▸ .const + | var _ ih => + have := (ih (h2 <| some ·)).var (a' := ?_) + rwa [(_ : m2' = _)]; ext (_|_) <;> simp [h2 none] + | app _ _ ih1 ih2 => + have := (ih1 (h2 <| .inl ·)).app (ih2 (h2 <| .inr ·)) + rwa [(_ : m2' = _)]; ext (_|_) <;> rfl + +theorem Pattern.matches_liftN {p : Pattern} {e : VExpr} {m1 m2'} : + p.Matches (e.liftN n k) m1 m2' ↔ ∃ m2, p.Matches e m1 m2 ∧ ∀ x, m2' x = (m2 x).liftN n k := by + simp only [← lift'_consN_skipN]; exact p.matches_lift' + +theorem Pattern.RHS.instN_apply {p : Pattern} {m1 m2} (r : p.RHS) : + (r.apply m1 m2).inst e₀ k = (r.apply m1 fun x => (m2 x).inst e₀ k) := by + induction r <;> simp [*, apply, inst] + rw [(ClosedN.instL ‹_›).instN_eq (Nat.zero_le _)] + +theorem Pattern.matches_instN {p : Pattern} {e : VExpr} {m1 m2} (H : p.Matches e m1 m2) : + p.Matches (e.inst e₀ k) m1 fun x => (m2 x).inst e₀ k := by + induction H with + | const => erw [show (fun _ : Empty => _) = _ by ext ⟨⟩]; exact .const + | var _ ih => + rw [(_ : (fun _ => _) = _)]; exact ih.var + ext (_|_) <;> rfl + | app _ _ ih1 ih2 => + rw [(_ : (fun _ => _) = _)]; exact ih1.app ih2 + ext (_|_) <;> rfl + +/-- Universe instantiation preserves a successful match. The universe +capture is instantiated pointwise and every expression capture is +instantiated by the same level substitution. -/ +theorem Pattern.Matches.instL {p : Pattern} {e : VExpr} {m1 m2} + (H : p.Matches e m1 m2) (ls : List VLevel) : + p.Matches (e.instL ls) (m1.map (VLevel.inst ls)) + fun x => (m2 x).instL ls := by + induction H with + | const => erw [show (fun _ : Empty => _) = _ by ext ⟨⟩]; exact .const + | var _ ih => + rw [(_ : (fun _ => _) = _)]; exact ih.var + ext (_|_) <;> rfl + | app _ _ ih1 ih2 => + rw [(_ : (fun _ => _) = _)]; exact ih1.app ih2 + ext (_|_) <;> rfl + +theorem Pattern.matches_inter {p q : Pattern} {e : VExpr} : + (∃ m1 m2, p.Matches e m1 m2) ∧ (∃ m1 m2, q.Matches e m1 m2) ↔ + (∃ r m1 m2, p.inter q = some r ∧ r.Matches e m1 m2) := by + constructor + · rintro ⟨⟨m1, m2, hp⟩, ⟨m3, m4, hq⟩⟩ + induction hp generalizing q m3 <;> cases hq <;> simp [inter] + · case const.const => exact ⟨_, _, .const⟩ + · case var.var ih _ _ ih' => + have ⟨rf, mf1, mf2, hf1, hf2⟩ := ih _ _ ih' + exact ⟨_, ⟨_, hf1, rfl⟩, _, _, .var hf2⟩ + · case var.app ihf _ _ _ _ _ ha2 ihf' => + have ⟨rf, mf1, mf2, hf1, hf2⟩ := ihf _ _ ihf' + exact ⟨_, ⟨_, hf1, rfl⟩, _, _, .app hf2 ha2⟩ + · case app.var ha2 ihf _ _ _ ihf' => + have ⟨rf, mf1, mf2, hf1, hf2⟩ := ihf _ _ ihf' + exact ⟨_, ⟨_, hf1, rfl⟩, _, _, .app hf2 ha2⟩ + · case app.app ihf iha _ _ _ _ _ iha' ihf' => + have ⟨rf, mf1, mf2, hf1, hf2⟩ := ihf _ _ ihf' + have ⟨ra, ma1, ma2, ha1, ha2⟩ := iha _ _ iha' + exact ⟨_, ⟨_, hf1, _, ha1, rfl⟩, _, _, .app hf2 ha2⟩ + · rintro ⟨r, m1, m2, h1, h2⟩ + induction p generalizing q e r m1 <;> cases q <;> simp [inter] at h1 <;> [ + obtain ⟨rfl, rfl⟩ := h1; obtain ⟨_, wf, _, wa, rfl⟩ := h1; + obtain ⟨_, wf, rfl⟩ := h1; obtain ⟨_, wf, rfl⟩ := h1; obtain ⟨_, wf, rfl⟩ := h1 + ] <;> cases h2 + · exact ⟨⟨_, _, .const⟩, ⟨_, _, .const⟩⟩ + · next ihf iha _ _ _ _ _ _ _ _ _ ha hf => + have ⟨⟨mf1, mf2, hf⟩, ⟨mf1', mf2', hf'⟩⟩ := ihf _ _ _ wf hf + have ⟨⟨ma1, ma2, ha⟩, ⟨ma1', ma2', ha'⟩⟩ := iha _ _ _ wa ha + exact ⟨⟨_, _, .app hf ha⟩, ⟨_, _, .app hf' ha'⟩⟩ + · next ihf _ _ _ _ _ _ _ _ ha hf => + have ⟨⟨mf1, mf2, hf⟩, ⟨mf1', mf2', hf'⟩⟩ := ihf _ _ _ wf hf + exact ⟨⟨_, _, .app hf ha⟩, ⟨_, _, .var hf'⟩⟩ + · next ihf _ _ _ _ _ _ _ _ ha' hf => + have ⟨⟨mf1, mf2, hf⟩, ⟨mf1', mf2', hf'⟩⟩ := ihf _ _ _ wf hf + exact ⟨⟨_, _, .var hf⟩, ⟨_, _, .app hf' ha'⟩⟩ + · next ihf _ _ _ _ _ hf => + have ⟨⟨mf1, mf2, hf⟩, ⟨mf1', mf2', hf'⟩⟩ := ihf _ _ _ wf hf + exact ⟨⟨_, _, .var hf⟩, ⟨_, _, .var hf'⟩⟩ + +theorem Pattern.matches_determ + (h1 : Matches p e m1 m2) (h2 : Matches p e m1' m2') : m1 = m1' ∧ m2 = m2' := by + induction h1 generalizing m1' with + | const => let .const := h2; simp + | app l1 l2 ih1 ih2 => let .app r1 r2 := h2; simp [ih1 r1, ih2 r2] + | var l1 ih1 => let .var r1 := h2; simp [ih1 r1] + +def Pattern.Check.OK (defeq : VExpr → VExpr → Prop) {p : Pattern} + (m1 : List VLevel) (m2 : p.Path → VExpr) : p.Check → Prop + | .true => True + | .defeq a b rest => defeq (RHS.apply m1 m2 a) (RHS.apply m1 m2 b) ∧ rest.OK defeq m1 m2 + +theorem Pattern.Check.OK.map + {df df' : VExpr → VExpr → Prop} {p : Pattern} {ck : p.Check} {m1 m2 m1' m2'} + (h : ∀ a b : p.RHS, + df (a.apply m1 m2) (b.apply m1 m2) → df' (a.apply m1' m2') (b.apply m1' m2')) + (H : ck.OK df m1 m2) : ck.OK df' m1' m2' := by + induction ck <;> simp [OK, *] at H ⊢; cases H; constructor <;> solve_by_elim + +inductive SimplePattern where + | iota (recursor : Name) (major : Nat) (constr : Name) (args : Nat) + | defn (head : Name) + +@[reducible] def SimplePattern.toPattern : SimplePattern → Pattern + | .defn c => .const c + | .iota r m c n => .app (.varN (.const r) m) (.varN (.const c) n) + +/-! ## Shape helpers for generated recursor patterns + +`HeadConstN`, `HeadConst`, `of_varN_matches`, `RecursorIotaPattern`, and +`matches_shape` form the implementation-independent shape layer consumed by +the generated iota patterns of a certified inductive block +(`Theory/Typing/InductivePattern.lean`). They characterize matching against +`Pattern.varN` towers and `SimplePattern.iota` patterns without referring to +any generator data. -/ + +/-- `HeadConstN c ls n e`: `e` is the constant `c` at levels `ls` applied to +exactly `n` arguments. This is the expression shape captured by matching the +pattern `Pattern.varN (.const c) n`. -/ +inductive HeadConstN (c : Name) (ls : List VLevel) : Nat → VExpr → Prop where + | const : HeadConstN c ls 0 (.const c ls) + | app : HeadConstN c ls n f → HeadConstN c ls (n+1) (.app f a) + +/-- `e` is an application spine headed by the constant `c`. -/ +def HeadConst (c : Name) (e : VExpr) : Prop := ∃ ls n, HeadConstN c ls n e + +/-- Matching a `varN` tower of a constant captures exactly a `HeadConstN` +spine whose head levels are the pattern's level assignment. -/ +theorem Pattern.of_varN_matches {c : Name} : + ∀ {n : Nat} {e : VExpr} {m2}, (Pattern.varN (.const c) n).Matches e m1 m2 → + HeadConstN c m1 n e := by + intro n + induction n with + | zero => intro e m2 H; cases H; exact .const + | succ n ih => intro e m2 H; cases H with | var h => exact .app (ih h) + +/-- Every `HeadConstN` spine matches its `varN` tower. -/ +theorem HeadConstN.matches : HeadConstN c ls n e → + ∃ m2, (Pattern.varN (.const c) n).Matches e ls m2 + | .const => ⟨_, .const⟩ + | .app h => let ⟨_, h'⟩ := h.matches; ⟨_, .var h'⟩ + +/-- The capture paths of an `n`-ary `varN` tower in argument order (outermost +application first): matching assigns the `t`-th entry the `t`-th spine +argument. -/ +def Pattern.varNPaths (p : Pattern) : ∀ n, List (Pattern.Path (p.varN n)) + | 0 => [] + | n+1 => (varNPaths p n).map some ++ [none] + +@[simp] theorem Pattern.varNPaths_length (p : Pattern) : + ∀ n, (varNPaths p n).length = n + | 0 => rfl + | n+1 => by + show ((varNPaths p n).map some ++ [none]).length = n + 1 + rw [List.length_append, List.length_map, varNPaths_length p n]; rfl + +/-- The exact pattern of one generated iota rule: the recursor constant +applied to `major` arguments (parameters, motives, minors, and the +constructor's result indices), with a `ctor`-headed major premise carrying +`args` arguments. Definitionally `(SimplePattern.iota recursor major ctor +args).toPattern`. -/ +def RecursorIotaPattern (recursor : Name) (major : Nat) + (ctor : Name) (args : Nat) : Pattern := + .app (.varN (.const recursor) major) (.varN (.const ctor) args) + +theorem SimplePattern.toPattern_iota : + (SimplePattern.iota r m c n).toPattern = RecursorIotaPattern r m c n := rfl + +/-- Match inversion for an iota pattern: the expression is exactly a +recursor-headed spine at the pattern's level assignment whose last argument +is a constructor-headed spine (at unconstrained levels). -/ +theorem RecursorIotaPattern.matches_shape + (H : (RecursorIotaPattern r mj c n).Matches e m1 m2) : + ∃ f a ls, e = .app f a ∧ HeadConstN r m1 mj f ∧ HeadConstN c ls n a := by + cases H with + | app h1 h2 => + exact ⟨_, _, _, rfl, Pattern.of_varN_matches h1, Pattern.of_varN_matches h2⟩ + +/-- Match construction for an iota pattern from the two head spines. -/ +theorem RecursorIotaPattern.matches_of + (h1 : HeadConstN r ls mj f) (h2 : HeadConstN c ls' n a) : + ∃ m2, (RecursorIotaPattern r mj c n).Matches (.app f a) ls m2 := + let ⟨_, hf⟩ := h1.matches + let ⟨_, ha⟩ := h2.matches + ⟨_, .app hf ha⟩ + +/-- Subpatterns of a constant `varN` tower are exactly its shorter towers. -/ +theorem Subpattern.varN_const_le : + ∀ {n}, Subpattern p (Pattern.varN (.const c) n) → + ∃ j, j ≤ n ∧ p = Pattern.varN (.const c) j := by + intro n + induction n with + | zero => intro H; cases H; exact ⟨0, Nat.le_refl _, rfl⟩ + | succ n ih => + intro H + cases H with + | refl => exact ⟨n+1, Nat.le_refl _, rfl⟩ + | varL h => + let ⟨j, hj, hp⟩ := ih h + exact ⟨j, Nat.le_succ_of_le hj, hp⟩ + +/-- Subpattern classification for an iota pattern: the whole pattern, a +prefix of the recursor head, or a prefix of the constructor spine. -/ +theorem RecursorIotaPattern.subpattern_inv + (H : Subpattern p (RecursorIotaPattern r mj c n)) : + p = RecursorIotaPattern r mj c n ∨ + (∃ j, j ≤ mj ∧ p = .varN (.const r) j) ∨ + (∃ j, j ≤ n ∧ p = .varN (.const c) j) := by + cases H with + | refl => exact .inl rfl + | appL h => exact .inr (.inl h.varN_const_le) + | appR h => exact .inr (.inr h.varN_const_le) + +/-- Two constant `varN` towers intersect only when they agree exactly. -/ +theorem Pattern.varN_const_inter_some : + ∀ {n n' p}, (Pattern.varN (.const c) n).inter (Pattern.varN (.const c') n') = some p → + c = c' ∧ n = n' ∧ p = Pattern.varN (.const c) n := by + intro n + induction n with + | zero => + intro n' p h + cases n' with + | zero => + simp [Pattern.inter] at h + exact ⟨h.1, rfl, h.2.symm⟩ + | succ n' => simp [Pattern.inter] at h + | succ n ih => + intro n' p h + cases n' with + | zero => simp [Pattern.inter] at h + | succ n' => + simp only [Pattern.inter, bind, Option.bind_eq_some_iff, Option.pure_def, Option.some.injEq] at h + obtain ⟨q, hq, rfl⟩ := h + obtain ⟨rfl, rfl, rfl⟩ := ih hq + exact ⟨rfl, rfl, rfl⟩ + +theorem Pattern.varN_const_inter_of_ne_name (h : c ≠ c') (n n' : Nat) : + (Pattern.varN (.const c) n).inter (Pattern.varN (.const c') n') = none := by + cases e : (Pattern.varN (.const c) n).inter (Pattern.varN (.const c') n') with + | none => rfl + | some p => exact absurd (varN_const_inter_some e).1 h + +theorem Pattern.varN_const_inter_of_ne_arity (h : n ≠ n') (c c' : Name) : + (Pattern.varN (.const c) n).inter (Pattern.varN (.const c') n') = none := by + cases e : (Pattern.varN (.const c) n).inter (Pattern.varN (.const c') n') with + | none => rfl + | some p => exact absurd (varN_const_inter_some e).2.1 h + +/-- An application pattern intersects a constant `varN` tower only through a +positive tower whose inner tower intersects the function part. -/ +theorem Pattern.app_inter_varN_const_some {f a : Pattern} + (h : (Pattern.app f a).inter (Pattern.varN (.const c) n) = some p) : + ∃ n' q, n = n' + 1 ∧ f.inter (Pattern.varN (.const c) n') = some q ∧ + p = .app q a := by + cases n with + | zero => simp [Pattern.inter] at h + | succ n' => + simp only [Pattern.inter, bind, Option.bind_eq_some_iff, Option.pure_def, Option.some.injEq] at h + obtain ⟨q, hq, rfl⟩ := h + exact ⟨n', q, rfl, hq, rfl⟩ + +/-- Two iota patterns intersect only when they agree exactly. -/ +theorem RecursorIotaPattern.inter_some + (h : (RecursorIotaPattern r mj c n).inter (RecursorIotaPattern r' mj' c' n') = some p) : + r = r' ∧ mj = mj' ∧ c = c' ∧ n = n' ∧ p = RecursorIotaPattern r mj c n := by + simp only [RecursorIotaPattern, Pattern.inter, bind, Option.bind_eq_some_iff, + Option.pure_def, Option.some.injEq] at h + obtain ⟨q1, h1, q2, h2, rfl⟩ := h + obtain ⟨rfl, rfl, rfl⟩ := Pattern.varN_const_inter_some h1 + obtain ⟨rfl, rfl, rfl⟩ := Pattern.varN_const_inter_some h2 + exact ⟨rfl, rfl, rfl, rfl, rfl⟩ + +/-- An iota pattern intersects a constant `varN` tower only at a tower whose +inner arity is the pattern's major arity with the recursor's name. -/ +theorem RecursorIotaPattern.inter_varN_const_some + (h : (RecursorIotaPattern r mj c n).inter (Pattern.varN (.const b) j) = some p) : + b = r ∧ j = mj + 1 := by + obtain ⟨j', q, rfl, hq, rfl⟩ := Pattern.app_inter_varN_const_some h + obtain ⟨rfl, rfl, rfl⟩ := Pattern.varN_const_inter_some hq + exact ⟨rfl, rfl⟩ + +/-- Constant `varN` towers are injective in the head name and the arity. -/ +theorem Pattern.varN_const_inj {c c' : Name} : + ∀ {n n' : Nat}, Pattern.varN (.const c) n = Pattern.varN (.const c') n' → + c = c' ∧ n = n' + | 0, 0, h => by cases h; exact ⟨rfl, rfl⟩ + | 0, n'+1, h => absurd h (by simp [Pattern.varN]) + | n+1, 0, h => absurd h (by simp [Pattern.varN]) + | n+1, n'+1, h => by + injection h with h1 + obtain ⟨rfl, rfl⟩ := Pattern.varN_const_inj h1 + exact ⟨rfl, rfl⟩ + +/-- Iota patterns are injective in all four components. -/ +theorem RecursorIotaPattern.inj + (h : RecursorIotaPattern r mj c n = RecursorIotaPattern r' mj' c' n') : + r = r' ∧ mj = mj' ∧ c = c' ∧ n = n' := by + injection h with h1 h2 + obtain ⟨rfl, rfl⟩ := Pattern.varN_const_inj h1 + obtain ⟨rfl, rfl⟩ := Pattern.varN_const_inj h2 + exact ⟨rfl, rfl, rfl, rfl⟩ + +/-- The only application subpattern of an iota pattern is the pattern +itself. -/ +theorem RecursorIotaPattern.app_subpattern + (H : Subpattern (.app p₁ p₂) (RecursorIotaPattern r mj c n)) : + p₁ = .varN (.const r) mj ∧ p₂ = .varN (.const c) n := by + rcases RecursorIotaPattern.subpattern_inv H with heq | ⟨j, hj, heq⟩ | ⟨j, hj, heq⟩ + · injection heq with h1 h2; exact ⟨h1, h2⟩ + · cases j <;> exact absurd heq (by simp [Pattern.varN]) + · cases j <;> exact absurd heq (by simp [Pattern.varN]) + +/-- Apply an RHS template head to a list of template arguments. -/ +def Pattern.RHS.appN {p : Pattern} (f : p.RHS) : List p.RHS → p.RHS + | [] => f + | a :: as => Pattern.RHS.appN (.app f a) as diff --git a/Ix/Theory/Named/Typing/QuotLemmas.lean b/Ix/Theory/Named/Typing/QuotLemmas.lean new file mode 100644 index 000000000..1f69ed1ba --- /dev/null +++ b/Ix/Theory/Named/Typing/QuotLemmas.lean @@ -0,0 +1,39 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Typing.Env +import Ix.Theory.Named.Typing.Meta + +namespace Ix.Theory.Named +namespace VEnv + +theorem addQuot_WF (henv : Ordered env) (hq : QuotReady env) : + addQuot env = some env' → Ordered env' := by + refine with_addConst (cis := [.const _ eqConst]) (hcis := ⟨hq, ⟨⟩⟩) henv + (fun _ => ⟨_, by type_tac⟩) fun henv => ?_ + refine with_addConst henv (fun ⟨quot, _⟩ => ⟨_, by type_tac⟩) fun henv => ?_ + refine with_addConst henv (fun ⟨_, quot, eq, _⟩ => ⟨_, by type_tac⟩) fun henv => ?_ + refine with_addConst henv (fun ⟨_, _, quot, _⟩ => ⟨_, by type_tac⟩) fun henv => ?_ + rintro ⟨_, _, _, _, eq, _⟩ ⟨⟩; exact .defeq henv ⟨by type_tac, by type_tac⟩ + +section +variable (henv : addQuot env = some env') include henv + +theorem addQuot_objs : env'.HasObjects [.defeq quotDefEq, .const `Quot.ind quotIndConst, + .const `Quot.lift quotLiftConst, .const `Quot.mk quotMkConst, .const `Quot quotConst] := by + let ⟨env, h, henv⟩ := HasObjects.bind_const (ls := []) trivial henv + let ⟨env, h, henv⟩ := HasObjects.bind_const h henv + let ⟨env, h, henv⟩ := HasObjects.bind_const h henv + obtain ⟨env, h, ⟨⟩⟩ := HasObjects.bind_const h henv + exact HasObjects.defeq (df := quotDefEq) h + +theorem addQuot_quot : env'.constants ``Quot = quotConst := (addQuot_objs henv).2.2.2.2.1 +theorem addQuot_quotMk : env'.constants ``Quot.mk = quotMkConst := (addQuot_objs henv).2.2.2.1 +theorem addQuot_quotLift : env'.constants ``Quot.lift = quotLiftConst := (addQuot_objs henv).2.2.1 +theorem addQuot_quotInd : env'.constants ``Quot.ind = quotIndConst := (addQuot_objs henv).2.1 +theorem addQuot_defeq : env'.defeqs quotDefEq := (addQuot_objs henv).1 + +end diff --git a/Ix/Theory/Named/Typing/Strong.lean b/Ix/Theory/Named/Typing/Strong.lean new file mode 100644 index 000000000..f79f27dc7 --- /dev/null +++ b/Ix/Theory/Named/Typing/Strong.lean @@ -0,0 +1,1031 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Typing.Lemmas + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named +open Ix.Theory.Named + +namespace VEnv + +open VExpr + +section +set_option hygiene false +variable (env : VEnv) (uvars : Nat) + +section +local notation:65 Γ " ⊢ " e " : " A:30 => IsDefEqStrong Γ e e A +local notation:65 Γ " ⊢ " e1 " ≡ " e2 " : " A:30 => IsDefEqStrong Γ e1 e2 A + +inductive IsDefEqStrong : List VExpr → VExpr → VExpr → VExpr → Prop where + | bvar : Lookup Γ i A → u.WF uvars → Γ ⊢ A : .sort u → Γ ⊢ .bvar i : A + | symm : Γ ⊢ e ≡ e' : A → Γ ⊢ e' ≡ e : A + | trans : Γ ⊢ e₁ ≡ e₂ : A → Γ ⊢ e₂ ≡ e₃ : A → Γ ⊢ e₁ ≡ e₃ : A + | sortDF : + l.WF uvars → l'.WF uvars → l ≈ l' → + Γ ⊢ .sort l ≡ .sort l' : .sort (.succ l) + | constDF : + env.constants c = some ci → + (∀ l ∈ ls, l.WF uvars) → + (∀ l ∈ ls', l.WF uvars) → + ls.length = ci.uvars → + List.Forall₂ (· ≈ ·) ls ls' → + u.WF uvars → + [] ⊢ ci.type.instL ls ≡ ci.type.instL ls' : .sort u → + Γ ⊢ ci.type.instL ls ≡ ci.type.instL ls' : .sort u → + Γ ⊢ .const c ls ≡ .const c ls' : ci.type.instL ls + | appDF : + u.WF uvars → v.WF uvars → + Γ ⊢ A : .sort u → + A::Γ ⊢ B : .sort v → + Γ ⊢ f ≡ f' : .forallE A B → + Γ ⊢ a ≡ a' : A → + Γ ⊢ B.inst a ≡ B.inst a' : .sort v → + Γ ⊢ .app f a ≡ .app f' a' : B.inst a + | lamDF : + u.WF uvars → v.WF uvars → + Γ ⊢ A ≡ A' : .sort u → + A::Γ ⊢ B : .sort v → + A'::Γ ⊢ B : .sort v → + A::Γ ⊢ body ≡ body' : B → + A'::Γ ⊢ body ≡ body' : B → + Γ ⊢ .lam A body ≡ .lam A' body' : .forallE A B + | forallEDF : + u.WF uvars → v.WF uvars → + Γ ⊢ A ≡ A' : .sort u → + A::Γ ⊢ body ≡ body' : .sort v → + A'::Γ ⊢ body ≡ body' : .sort v → + Γ ⊢ .forallE A body ≡ .forallE A' body' : .sort (.imax u v) + | defeqDF : + u.WF uvars → Γ ⊢ A ≡ B : .sort u → Γ ⊢ e1 ≡ e2 : A → Γ ⊢ e1 ≡ e2 : B + | beta : + u.WF uvars → v.WF uvars → Γ ⊢ A : .sort u → A::Γ ⊢ B : .sort v → + A::Γ ⊢ e : B → Γ ⊢ e' : A → + Γ ⊢ B.inst e' : .sort v → + Γ ⊢ e.inst e' : B.inst e' → + Γ ⊢ .app (.lam A e) e' ≡ e.inst e' : B.inst e' + | eta : + u.WF uvars → v.WF uvars → + Γ ⊢ A : .sort u → + A::Γ ⊢ B : .sort v → + A.lift::A::Γ ⊢ B.liftN 1 1 : .sort v → + Γ ⊢ e : .forallE A B → + A::Γ ⊢ e.lift : .forallE A.lift (B.liftN 1 1) → + A::Γ ⊢ A.lift : .sort u → + Γ ⊢ .lam A (.app e.lift (.bvar 0)) ≡ e : .forallE A B + | structEta : + env.structEtas rule → + (∀ level ∈ levels, level.WF uvars) → + levels.length = rule.uvars → + params.length = rule.nparams → + env.SpineWF uvars Γ (rule.familyType.instL levels) + params (.sort resultLevel) → + u.WF uvars → + Γ ⊢ rule.structureType levels params : .sort u → + Γ ⊢ major : rule.structureType levels params → + Γ ⊢ rule.rebuild levels params major : + rule.structureType levels params → + Γ ⊢ rule.rebuild levels params major ≡ major : + rule.structureType levels params + | proofIrrel : + Γ ⊢ p : .sort .zero → Γ ⊢ h : p → Γ ⊢ h' : p → + Γ ⊢ h ≡ h' : p + | extra : + env.defeqs df → (∀ l ∈ ls, l.WF uvars) → ls.length = df.uvars → + u.WF uvars → + [] ⊢ df.type.instL ls : .sort u → + [] ⊢ df.lhs.instL ls : df.type.instL ls → + [] ⊢ df.rhs.instL ls : df.type.instL ls → + Γ ⊢ df.lhs.instL ls : df.type.instL ls → + Γ ⊢ df.rhs.instL ls : df.type.instL ls → + Γ ⊢ df.lhs.instL ls ≡ df.rhs.instL ls : df.type.instL ls + +end + +section +local notation:65 Γ " ⊢ " e " : " A:30 => HasTypeStrong Γ e A true +local notation:65 Γ " ⊢ " e " :! " A:30 => HasTypeStrong Γ e A false +local notation:65 Γ " ⊢ " e1 " ≡ " e2 " : " A:30 => IsDefEqStrong env uvars Γ e1 e2 A + +/-- +This is functionally a mutual inductive type, +but using a bool index makes it easier to do induction in lean. +The defeq rule is separated from the structural rules for easy inversion. +-/ +inductive HasTypeStrong : List VExpr → VExpr → VExpr → Bool → Prop where + | bvar : Lookup Γ i A → u.WF uvars → Γ ⊢ A : .sort u → Γ ⊢ .bvar i :! A + | sort' : l.WF uvars → l'.WF uvars → l ≈ l' → Γ ⊢ .sort l :! .sort (.succ l') + | const : + env.constants c = some ci → + (∀ l ∈ ls, l.WF uvars) → + ls.length = ci.uvars → + u.WF uvars → + [] ⊢ ci.type.instL ls : .sort u → + Γ ⊢ ci.type.instL ls : .sort u → + Γ ⊢ .const c ls :! ci.type.instL ls + | app : + u.WF uvars → v.WF uvars → + Γ ⊢ A : .sort u → + A::Γ ⊢ B : .sort v → + Γ ⊢ .forallE A B : .sort (.imax u v) → + Γ ⊢ f : .forallE A B → + Γ ⊢ a : A → + Γ ⊢ B.inst a : .sort v → + Γ ⊢ .app f a :! B.inst a + | lam : + u.WF uvars → v.WF uvars → + Γ ⊢ A : .sort u → + A::Γ ⊢ B : .sort v → + A::Γ ⊢ body : B → + Γ ⊢ .forallE A B : .sort (.imax u v) → + Γ ⊢ .lam A body :! .forallE A B + | forallE : + u.WF uvars → v.WF uvars → + Γ ⊢ A : .sort u → + A::Γ ⊢ body : .sort v → + Γ ⊢ .forallE A body :! .sort (.imax u v) + | base : Γ ⊢ e :! A → Γ ⊢ e : A + | defeq : u.WF uvars → Γ ⊢ A ≡ B : .sort u → + Γ ⊢ A : .sort u → Γ ⊢ B : .sort u → Γ ⊢ e : A → Γ ⊢ e : B +end + +end + +theorem IsDefEqStrong.hasType {env : VEnv} + (H : env.IsDefEqStrong U Γ e1 e2 A) : + env.IsDefEqStrong U Γ e1 e1 A ∧ env.IsDefEqStrong U Γ e2 e2 A := + ⟨H.trans H.symm, H.symm.trans H⟩ + +variable! (henv : Ordered env) in +theorem IsDefEqStrong.weakN (W : Ctx.LiftN n k Γ Γ') (H : env.IsDefEqStrong U Γ e1 e2 A) : + env.IsDefEqStrong U Γ' (e1.liftN n k) (e2.liftN n k) (A.liftN n k) := by + induction H generalizing k Γ' with + | bvar h1 h2 _ ih3 => refine .bvar (h1.weakN W) h2 (ih3 W) + | symm _ ih => exact .symm (ih W) + | trans _ _ ih1 ih2 => exact .trans (ih1 W) (ih2 W) + | sortDF h1 h2 h3 => exact .sortDF h1 h2 h3 + | constDF h1 h2 h3 h4 h5 h6 h7 _ _ ih2 => + simp [(henv.closedC h1).instL.liftN_eq (Nat.zero_le _)] at ih2 ⊢ + exact .constDF h1 h2 h3 h4 h5 h6 h7 (ih2 W) + | appDF h1 h2 _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 => + refine liftN_inst_hi .. ▸ .appDF h1 h2 (ih1 W) (ih2 W.succ) (ih3 W) (ih4 W) ?_ + exact liftN_inst_hi .. ▸ liftN_inst_hi .. ▸ ih5 W + | lamDF h1 h2 _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 => + exact .lamDF h1 h2 (ih1 W) (ih2 W.succ) (ih3 W.succ) (ih4 W.succ) (ih5 W.succ) + | forallEDF h1 h2 _ _ _ ih1 ih2 ih3 => exact .forallEDF h1 h2 (ih1 W) (ih2 W.succ) (ih3 W.succ) + | defeqDF h1 _ _ ih1 ih2 => exact .defeqDF h1 (ih1 W) (ih2 W) + | beta h1 h2 _ _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 ih6 => + refine liftN_inst_hi .. ▸ liftN_instN_hi .. ▸ .beta h1 h2 + (ih1 W) (ih2 W.succ) (ih3 W.succ) (ih4 W) + (liftN_instN_hi .. ▸ ih5 W :) + (liftN_instN_hi .. ▸ liftN_instN_hi .. ▸ ih6 W :) + | @eta Γ a u B v e h1 h2 _ _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 ih6 => + have := IsDefEqStrong.eta h1 h2 (ih1 W) (ih2 W.succ) ?_ (ih4 W) ?_ ?_ + · simp [liftN]; rwa [← lift_liftN'] + · specialize ih3 W.succ.succ + have := liftN'_comm B n 1 (k+1) 1 (Nat.le_add_left ..) + rw [Nat.add_comm 1] at this; rwa [← this, ← lift_liftN'] at ih3 + · have ih5 : IsDefEqStrong _ _ _ _ _ (liftN n (lift (forallE ..)) _) := ih5 W.succ + rwa [← lift_liftN', ← lift_liftN'] at ih5 + · have ih6 := ih6 W.succ + rwa [← lift_liftN'] at ih6 + | @structEta rule levels _ params _ u major hreg hlevels + hlevelsLength hparamsLength hparamsSpine hu _ _ _ ihType ihMajor ihRebuild => + have hparamsSpine' := hparamsSpine.weakN henv W + rw [(henv.structEtaWF hreg).familyType_closed.instL.liftN_eq + (Nat.zero_le _)] at hparamsSpine' + have htype := ihType W + rw [VStructEta.structureType_liftN] at htype + have hmajor := ihMajor W + rw [VStructEta.structureType_liftN] at hmajor + have hrebuild := ihRebuild W + rw [VStructEta.rebuild_liftN rule levels params major + hparamsLength n k, VStructEta.structureType_liftN] at hrebuild + have hout := IsDefEqStrong.structEta hreg hlevels hlevelsLength + (by simpa using hparamsLength) hparamsSpine' hu htype hmajor hrebuild + simpa only [VStructEta.rebuild_liftN rule levels params major + hparamsLength n k, VStructEta.structureType_liftN] using hout + | proofIrrel _ _ _ ih1 ih2 ih3 => exact .proofIrrel (ih1 W) (ih2 W) (ih3 W) + | extra h1 h2 h3 h4 h5 h6 h7 _ _ _ _ _ ih4 ih5 => + have ⟨⟨hA1, _⟩, hA2, hA3⟩ := henv.closed.2 h1 + simp [ + hA1.instL.liftN_eq (Nat.zero_le _), + hA2.instL.liftN_eq (Nat.zero_le _), + hA3.instL.liftN_eq (Nat.zero_le _)] at ih4 ih5 ⊢ + exact IsDefEqStrong.extra h1 h2 h3 h4 h5 h6 h7 (ih4 W) (ih5 W) + +theorem IsDefEqStrong.defeq (H : IsDefEqStrong env U Γ e1 e2 A) : env.IsDefEq U Γ e1 e2 A := by + induction H with + | bvar h => exact .bvar h + | symm _ ih => exact .symm ih + | trans _ _ ih1 ih2 => exact .trans ih1 ih2 + | sortDF h1 h2 h3 => exact .sortDF h1 h2 h3 + | constDF h1 h2 h3 h4 h5 => exact .constDF h1 h2 h3 h4 h5 + | appDF _ _ _ _ _ _ _ _ _ ih1 ih2 => exact .appDF ih1 ih2 + | lamDF _ _ _ _ _ _ _ ih1 _ _ ih2 => exact .lamDF ih1 ih2 + | forallEDF _ _ _ _ _ ih1 ih2 => exact .forallEDF ih1 ih2 + | defeqDF _ _ _ ih1 ih2 => exact .defeqDF ih1 ih2 + | beta _ _ _ _ _ _ _ _ _ _ ih1 ih2 => exact .beta ih1 ih2 + | eta _ _ _ _ _ _ _ _ _ _ _ ih => exact .eta ih + | structEta hreg hlevels hlevelsLength hparamsLength hparamsSpine + _ _ _ _ _ ihMajor ihRebuild => + exact .structEta hreg hlevels hlevelsLength hparamsLength + hparamsSpine ihMajor ihRebuild + | proofIrrel _ _ _ ih1 ih2 ih3 => exact .proofIrrel ih1 ih2 ih3 + | extra h1 h2 h3 => exact .extra h1 h2 h3 + +variable! {env env' : VEnv} (henv : env ≤ env') in +theorem IsDefEqStrong.mono + (H : env.IsDefEqStrong U Γ e1 e2 A) : env'.IsDefEqStrong U Γ e1 e2 A := by + induction H with + | bvar h1 h2 _ ih => exact .bvar h1 h2 ih + | symm _ ih => exact .symm ih + | trans _ _ ih1 ih2 => exact .trans ih1 ih2 + | sortDF h1 h2 h3 => exact .sortDF h1 h2 h3 + | constDF h1 h2 h3 h4 h5 h6 _ _ ih1 ih2 => + exact .constDF (henv.1 h1) h2 h3 h4 h5 h6 ih1 ih2 + | appDF h1 h2 _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 => exact .appDF h1 h2 ih1 ih2 ih3 ih4 ih5 + | lamDF h1 h2 _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 => exact .lamDF h1 h2 ih1 ih2 ih3 ih4 ih5 + | forallEDF h1 h2 _ _ _ ih1 ih2 ih3 => exact .forallEDF h1 h2 ih1 ih2 ih3 + | defeqDF h1 _ _ ih1 ih2 => exact .defeqDF h1 ih1 ih2 + | beta h1 h2 _ _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 ih6 => exact .beta h1 h2 ih1 ih2 ih3 ih4 ih5 ih6 + | eta h1 h2 _ _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 ih6 => exact .eta h1 h2 ih1 ih2 ih3 ih4 ih5 ih6 + | structEta hreg hlevels hlevelsLength hparamsLength hparamsSpine + hu _ _ _ ihType ihMajor ihRebuild => + exact .structEta (henv.structEtas hreg) hlevels hlevelsLength + hparamsLength (hparamsSpine.mono henv) hu ihType ihMajor ihRebuild + | proofIrrel _ _ _ ih1 ih2 ih3 => exact .proofIrrel ih1 ih2 ih3 + | extra h1 h2 h3 h4 _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 => + exact .extra (henv.2 h1) h2 h3 h4 ih1 ih2 ih3 ih4 ih5 + +variable! (henv : Ordered env) in +theorem IsDefEqStrong.weak0 (H : env.IsDefEqStrong U [] e1 e2 A) : + env.IsDefEqStrong U Γ e1 e2 A := by + have ⟨h1, h2, h3⟩ := H.defeq.closedN' henv.closed ⟨⟩ + simpa [h1.liftN_eq (Nat.zero_le _), h2.liftN_eq (Nat.zero_le _), + h3.liftN_eq (Nat.zero_le _)] using H.weakN henv (.zero Γ rfl) + +inductive EqUpToLevels (U : Nat) : VExpr → VExpr → Prop + | bvar : EqUpToLevels U (.bvar i) (.bvar i) + | const : (∀ l ∈ ls, l.WF U) → (∀ l ∈ ls', l.WF U) → List.Forall₂ (· ≈ ·) ls ls' → + EqUpToLevels U (.const c ls) (.const c ls') + | sort : l.WF U → l'.WF U → l ≈ l' → EqUpToLevels U (.sort l) (.sort l') + | app : EqUpToLevels U f f' → EqUpToLevels U a a' → EqUpToLevels U (.app f a) (.app f' a') + | lam : EqUpToLevels U A A' → EqUpToLevels U e e' → EqUpToLevels U (.lam A e) (.lam A' e') + | forallE : EqUpToLevels U A A' → EqUpToLevels U B B' → EqUpToLevels U (.forallE A B) (.forallE A' B') + +variable! {env : VEnv} {ls ls' : List VLevel} + (hls : ∀ l ∈ ls, l.WF U) (hls' : ∀ l ∈ ls', l.WF U) (heq : List.Forall₂ (· ≈ ·) ls ls') in +theorem EqUpToLevels.instL (H : env.IsDefEqStrong U' Γ e1 e2 A) : + EqUpToLevels U (e1.instL ls) (e1.instL ls') ∧ EqUpToLevels U (e2.instL ls) (e2.instL ls') := by + induction H with + | bvar => exact ⟨.bvar, .bvar⟩ + | symm _ ih => exact ih.symm + | trans _ _ ih1 ih2 | proofIrrel _ _ _ _ ih1 ih2 + | extra _ _ _ _ _ _ _ _ _ _ ih1 ih2 => exact ⟨ih1.1, ih2.2⟩ + | sortDF h1 h2 h3 => + constructor <;> exact .sort (.inst hls) (.inst hls') (VLevel.inst_congr rfl heq) + | constDF h1 h2 h3 h4 h5 => + constructor <;> exact .const + (List.forall_mem_map.2 fun _ _ => .inst hls) + (List.forall_mem_map.2 fun _ _ => .inst hls') + (List.forall₂_map_left_iff.2 <| List.forall₂_map_right_iff.2 <| + .rfl fun _ _ => VLevel.inst_congr rfl heq) + | appDF _ _ _ _ _ _ _ _ _ ih1 ih2 => exact ⟨.app ih1.1 ih2.1, .app ih1.2 ih2.2⟩ + | lamDF _ _ _ _ _ _ _ ih1 _ _ ih2 => exact ⟨.lam ih1.1 ih2.1, .lam ih1.2 ih2.2⟩ + | forallEDF _ _ _ _ _ ih1 ih2 => exact ⟨.forallE ih1.1 ih2.1, .forallE ih1.2 ih2.2⟩ + | defeqDF _ _ _ _ ih => exact ih + | beta _ _ _ _ _ _ _ _ ih1 _ ih3 ih4 _ ih6 => exact ⟨.app (.lam ih1.1 ih3.1) ih4.1, ih6.2⟩ + | eta _ _ _ _ _ _ _ _ ih1 _ _ ih4 ih5 => exact ⟨.lam ih1.1 (.app ih5.1 .bvar), ih4.1⟩ + | structEta _ _ _ _ _ _ _ _ _ _ ihMajor ihRebuild => + exact ⟨ihRebuild.1, ihMajor.2⟩ + + +variable! {env : VEnv} (W : OnCtx Γ fun _ A => A.LevelWF U) in +theorem EqUpToLevels.refl (H : env.IsDefEqStrong U Γ e1 e2 A) : + EqUpToLevels U e1 e1 ∧ EqUpToLevels U e2 e2 := by + have := IsDefEq.levelWF H.defeq W + exact LevelWF.instL_id this.1 ▸ LevelWF.instL_id this.2.1 ▸ + EqUpToLevels.instL VLevel.params_wf VLevel.params_wf (.rfl fun _ _ => rfl) H + +theorem EqUpToLevels.weakN (H : EqUpToLevels U e e') : + EqUpToLevels U (e.liftN n k) (e'.liftN n k) := by + induction H generalizing k with + | bvar => exact .bvar + | const h1 h2 h3 => exact .const h1 h2 h3 + | sort h1 h2 h3 => exact .sort h1 h2 h3 + | app _ _ ih1 ih2 => exact .app ih1 ih2 + | lam _ _ ih1 ih2 => exact .lam ih1 ih2 + | forallE _ _ ih1 ih2 => exact .forallE ih1 ih2 + +variable! (h₀ : EqUpToLevels U e₀ e₀') in +theorem EqUpToLevels.instN (H : EqUpToLevels U e e') : + EqUpToLevels U (e.inst e₀ k) (e'.inst e₀' k) := by + induction H generalizing k with + | bvar => simp [inst, instVar]; split <;> [exact .bvar; split] <;> [exact h₀.weakN; exact .bvar] + | const h1 h2 h3 => exact .const h1 h2 h3 + | sort h1 h2 h3 => exact .sort h1 h2 h3 + | app _ _ ih1 ih2 => exact .app ih1 ih2 + | lam _ _ ih1 ih2 => exact .lam ih1 ih2 + | forallE _ _ ih1 ih2 => exact .forallE ih1 ih2 + +def EnvStrong (env : VEnv) (U : Nat) (e A : VExpr) : Prop := + env.IsDefEqStrong U [] e e A ∧ + (∃ u, env.IsDefEqStrong U [] A A (.sort u)) ∧ + (∀ U' ls ls', (∀ l ∈ ls, l.WF U') → (∀ l ∈ ls', l.WF U') → List.Forall₂ (· ≈ ·) ls ls' → + env.IsDefEqStrong U' [] (e.instL ls) (e.instL ls') (A.instL ls)) ∧ + ∀ A B, e = A.forallE B → + (∃ u, env.IsDefEqStrong U [] A A (.sort u)) ∧ + (∃ u, env.IsDefEqStrong U [A] B B (.sort u)) + +variable! {env : VEnv} {ls : List VLevel} (hls : ∀ l ∈ ls, l.WF U') in +theorem IsDefEqStrong.instL (H : env.IsDefEqStrong U Γ e1 e2 A) : + env.IsDefEqStrong U' (Γ.map (VExpr.instL ls)) (e1.instL ls) (e2.instL ls) (A.instL ls) := by + induction H with + | bvar h _ _ ih => + exact .bvar h.instL (.inst hls) ih + | constDF h1 h2 h3 h4 h5 _ _ _ ih1 ih2 => + simp [VExpr.instL, VExpr.instL_instL] at ih1 ih2 ⊢ + exact .constDF h1 + (by simp [VLevel.WF.inst hls]) (by simp [VLevel.WF.inst hls]) (by simp [h4]) + (by simpa using h5.imp fun _ _ => VLevel.inst_congr_l) (.inst hls) ih1 ih2 + | symm _ ih => exact .symm ih + | trans _ _ ih1 ih2 => exact .trans ih1 ih2 + | sortDF _ _ h3 => + exact .sortDF (VLevel.WF.inst hls) (VLevel.WF.inst hls) (VLevel.inst_congr_l h3) + | appDF _ _ _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 => + exact instL_instN ▸ .appDF (.inst hls) (.inst hls) + ih1 ih2 ih3 ih4 (instL_instN ▸ instL_instN ▸ ih5) + | lamDF _ _ _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 => + exact .lamDF (.inst hls) (.inst hls) ih1 ih2 ih3 ih4 ih5 + | forallEDF _ _ _ _ _ ih1 ih2 ih3 => + exact .forallEDF (.inst hls) (.inst hls) ih1 ih2 ih3 + | defeqDF _ _ _ ih1 ih2 => + exact .defeqDF (.inst hls) ih1 ih2 + | beta _ _ _ _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 ih6 => + simpa using .beta (.inst hls) (.inst hls) ih1 ih2 ih3 ih4 + (by simpa [VExpr.instL] using ih5) (by simpa [VExpr.instL] using ih6) + | eta _ _ _ _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 ih6 => + simpa [VExpr.instL] using .eta (.inst hls) (.inst hls) ih1 ih2 + (by simpa [VExpr.instL] using ih3) ih4 + (by simpa [VExpr.instL] using ih5) (by simpa [VExpr.instL] using ih6) + | @structEta rule levels _ params _ u major hreg hlevels + hlevelsLength hparamsLength hparamsSpine _ _ _ _ ihType ihMajor ihRebuild => + have hlevels' : ∀ level ∈ levels.map (VLevel.inst ls), + level.WF U' := by + intro level hlevel + obtain ⟨source, _, heq⟩ := List.mem_map.1 hlevel + rw [← heq] + exact VLevel.WF.inst hls + rw [VStructEta.structureType_instL] at ihType ihMajor + rw [VStructEta.rebuild_instL, + VStructEta.structureType_instL] at ihRebuild + have hparamsSpine' := hparamsSpine.instL hls + rw [VExpr.instL_instL] at hparamsSpine' + have hout := IsDefEqStrong.structEta hreg hlevels' + (by simpa using hlevelsLength) (by simpa using hparamsLength) + hparamsSpine' (VLevel.WF.inst hls) ihType ihMajor ihRebuild + simpa only [VStructEta.rebuild_instL, + VStructEta.structureType_instL] using hout + | proofIrrel _ _ _ ih1 ih2 ih3 => + exact .proofIrrel ih1 ih2 ih3 + | extra h1 h2 h3 _ _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 => + simp [VExpr.instL, VExpr.instL_instL] at ih1 ih2 ih3 ih4 ih5 ⊢ + exact .extra h1 (by simp [VLevel.WF.inst hls]) (by simp [h3]) + (.inst hls) ih1 ih2 ih3 ih4 ih5 + +def CtxStrong (env : VEnv) (U Γ) := + OnCtx Γ fun Γ A => ∃ u, env.IsDefEqStrong U Γ A A (.sort u) + +variable! (henv : Ordered env) in +nonrec theorem CtxStrong.lookup {Γ} (H : CtxStrong env U Γ) (h : Lookup Γ i A) : + ∃ u, env.IsDefEqStrong U Γ A A (.sort u) := + H.lookup h fun ⟨_, h⟩ => ⟨_, h.weakN henv .one⟩ + +theorem CtxStrong.defeq {Γ} (H : CtxStrong env U Γ) : OnCtx Γ (env.IsType U) := + H.mono fun ⟨_, h⟩ => ⟨_, h.defeq⟩ + +theorem CtxStrong.levelWF : ∀ {Γ}, CtxStrong env U Γ → OnCtx Γ fun _ A => A.LevelWF U + | [], h => h + | _::_, ⟨h1, _, h2⟩ => ⟨levelWF h1, (h2.defeq.levelWF (levelWF h1)).1⟩ + +variable! (henv : Ordered env) (h₀ : env.IsDefEqStrong U Γ₀ e₀ e₀ A₀) (hΓ₀ : CtxStrong env U Γ₀) in +theorem IsDefEqStrong.instN (W : Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ) (H : env.IsDefEqStrong U Γ₁ e1 e2 A) + (hΓ : CtxStrong env U Γ) : + env.IsDefEqStrong U Γ (e1.inst e₀ k) (e2.inst e₀ k) (A.inst e₀ k) := by + induction H generalizing Γ k with + | @bvar _ i ty _ h _ h2 ih => + dsimp [inst]; clear h2 ih + induction W generalizing i ty with + | zero => + cases h with simp [inst_lift] + | zero => exact h₀ + | succ h => + let ⟨u, hty⟩ := hΓ₀.lookup henv h + exact .bvar h (hty.defeq.sort_r henv hΓ₀.defeq) hty + | succ _ ih => + cases h with (simp; rw [Nat.add_comm, ← liftN_instN_lo (hj := Nat.zero_le _)]) + | zero => + let ⟨u, hty⟩ := hΓ.lookup henv .zero + exact .bvar .zero (hty.defeq.sort_r henv hΓ.defeq) hty + | succ h => exact (ih h hΓ.1).weakN henv .one + | symm _ ih => exact .symm (ih W hΓ) + | trans _ _ ih1 ih2 => exact .trans (ih1 W hΓ) (ih2 W hΓ) + | sortDF h1 h2 h3 => exact .sortDF h1 h2 h3 + | constDF h1 h2 h3 h4 h5 h6 h7 _ _ ih2 => + simp [(henv.closedC h1).instL.instN_eq (Nat.zero_le _)] at ih2 ⊢ + exact .constDF h1 h2 h3 h4 h5 h6 h7 (ih2 W hΓ) + | appDF h1 h2 _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 => + exact inst0_inst_hi .. ▸ .appDF h1 h2 + (ih1 W hΓ) (ih2 W.succ ⟨hΓ, _, ih1 W hΓ⟩) + (ih3 W hΓ) (ih4 W hΓ) (inst0_inst_hi .. ▸ inst0_inst_hi .. ▸ ih5 W hΓ) + | lamDF h1 h2 _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 => + exact + have hΓ' := ⟨hΓ, _, (ih1 W hΓ).hasType.1⟩ + have hΓ'' := ⟨hΓ, _, (ih1 W hΓ).hasType.2⟩ + .lamDF h1 h2 (ih1 W hΓ) (ih2 W.succ hΓ') (ih3 W.succ hΓ'') (ih4 W.succ hΓ') (ih5 W.succ hΓ'') + | forallEDF h1 h2 _ _ _ ih1 ih2 ih3 => + exact .forallEDF h1 h2 (ih1 W hΓ) + (ih2 W.succ ⟨hΓ, _, (ih1 W hΓ).hasType.1⟩) (ih3 W.succ ⟨hΓ, _, (ih1 W hΓ).hasType.2⟩) + | defeqDF h1 _ _ ih1 ih2 => exact .defeqDF h1 (ih1 W hΓ) (ih2 W hΓ) + | beta h1 h2 _ _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 ih6 => + rw [inst0_inst_hi, inst0_inst_hi]; exact + have hΓ' := ⟨hΓ, _, ih1 W hΓ⟩ + .beta h1 h2 + (ih1 W hΓ) (ih2 W.succ hΓ') (ih3 W.succ hΓ') (ih4 W hΓ) + (inst0_inst_hi .. ▸ ih5 W hΓ) (inst0_inst_hi .. ▸ inst0_inst_hi .. ▸ ih6 W hΓ) + | eta h1 h2 _ _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 ih6 => + have := + have hΓ' := ⟨hΓ, _, (ih1 W hΓ).hasType.1⟩ + IsDefEqStrong.eta h1 h2 (ih1 W hΓ) (ih2 W.succ hΓ') + (by + have := ih3 W.succ.succ ⟨hΓ', _, by + rw [← lift_instN_lo]; exact (ih1 W hΓ).hasType.1.weakN henv .one⟩ + rwa [lift_instN_lo, liftN_instN_lo (hj := Nat.le_add_left ..), Nat.add_comm 1]) + (ih4 W hΓ) + (by + have := ih5 W.succ hΓ' + simp only [inst, ← lift_instN_lo] at this + rwa [liftN_instN_lo (hj := Nat.le_add_left ..), Nat.add_comm 1]) + (by simpa [inst, ← lift_instN_lo] using ih6 W.succ hΓ') + rw [lift, liftN_instN_lo (hj := Nat.zero_le _), Nat.add_comm] at this + simpa [inst] + | @structEta rule levels _ params _ u major hreg hlevels + hlevelsLength hparamsLength hparamsSpine hu _ _ _ ihType ihMajor ihRebuild => + have hparamsSpine' := SpineWF.instN henv W h₀.defeq hparamsSpine + rw [(henv.structEtaWF hreg).familyType_closed.instL.instN_eq + (Nat.zero_le _)] at hparamsSpine' + have htype := ihType W hΓ + rw [VStructEta.structureType_instN] at htype + have hmajor := ihMajor W hΓ + rw [VStructEta.structureType_instN] at hmajor + have hrebuild := ihRebuild W hΓ + rw [VStructEta.rebuild_instN rule levels params major e₀ + hparamsLength k, VStructEta.structureType_instN] at hrebuild + have hout := IsDefEqStrong.structEta hreg hlevels hlevelsLength + (by simpa using hparamsLength) hparamsSpine' hu htype hmajor hrebuild + simpa only [VStructEta.rebuild_instN rule levels params major e₀ + hparamsLength k, VStructEta.structureType_instN] using hout + | proofIrrel _ _ _ ih1 ih2 ih3 => exact .proofIrrel (ih1 W hΓ) (ih2 W hΓ) (ih3 W hΓ) + | extra h1 h2 h3 h4 h5 h6 h7 _ _ _ _ _ ih4 ih5 => + have ⟨⟨hA1, _⟩, hA2, hA3⟩ := henv.closed.2 h1 + simp [ + hA1.instL.instN_eq (Nat.zero_le _), + hA2.instL.instN_eq (Nat.zero_le _), + hA3.instL.instN_eq (Nat.zero_le _)] at ih4 ih5 ⊢ + exact .extra h1 h2 h3 h4 h5 h6 h7 (ih4 W hΓ) (ih5 W hΓ) + +theorem IsDefEqStrong.defeqDF_l (henv : Ordered env) (hΓ : CtxStrong env U Γ) + (h1 : env.IsDefEqStrong U Γ A A' (.sort u)) + (h2 : env.IsDefEqStrong U (A::Γ) e1 e2 B) : env.IsDefEqStrong U (A'::Γ) e1 e2 B := by + simpa [instN_bvar0] using + have hu := h1.defeq.sort_r henv hΓ.defeq + have hΓ' := ⟨hΓ, _, h1.hasType.2⟩ + h1.weakN henv (.one (A := A')) + |>.symm.defeqDF hu (.bvar .zero hu (h1.hasType.2.weakN henv .one)) + |>.instN henv hΓ' .zero (h2.weakN henv (.succ (.one (A := A')))) hΓ' + +variable! (henv : Ordered env) (envIH : env.OnTypes (EnvStrong env)) in +theorem IsDefEqStrong.forallE_inv' (hΓ : CtxStrong env U Γ) + (H : env.IsDefEqStrong U Γ e1 e2 V) (eq : e1 = A.forallE B ∨ e2 = A.forallE B) : + (∃ u, env.IsDefEqStrong U Γ A A (.sort u)) ∧ ∃ v, env.IsDefEqStrong U (A::Γ) B B (.sort v) := by + induction H generalizing A B with + | symm _ ih => exact ih hΓ eq.symm + | trans _ _ ih1 ih2 + | proofIrrel _ _ _ _ ih1 ih2 => + obtain eq | eq := eq + · exact ih1 hΓ (.inl eq) + · exact ih2 hΓ (.inr eq) + | forallEDF _ _ h1 h2 => + obtain ⟨⟨⟩⟩ | ⟨⟨⟩⟩ := eq + · exact ⟨⟨_, h1.hasType.1⟩, _, h2.hasType.1⟩ + · exact ⟨⟨_, h1.hasType.2⟩, _, h1.defeqDF_l henv hΓ h2.hasType.2⟩ + | defeqDF _ _ _ _ ih2 => exact ih2 hΓ eq + | @beta _ _ _ _ _ e _ _ _ h1 _ _ h4 _ _ _ _ ih3 ih4 => + obtain ⟨⟨⟩⟩ | eq := eq + cases e with + | bvar i => + cases i with simp [inst] at eq + | zero => exact ih4 hΓ (.inl eq) + | forallE A B => + cases eq + let ⟨⟨_, A1⟩, _, A2⟩ := ih3 ⟨hΓ, _, h1⟩ (.inl rfl) + refine ⟨⟨_, h4.instN henv hΓ .zero A1 hΓ⟩, _, h4.instN henv hΓ (.succ .zero) A2 ?_⟩ + exact ⟨hΓ, _, h4.instN henv hΓ .zero A1 hΓ⟩ + | _ => cases eq + | eta _ _ _ _ _ _ _ _ _ _ _ ih => + obtain ⟨⟨⟩⟩ | eq := eq + exact ih hΓ (.inl eq) + | structEta _ _ _ _ _ _ _ _ _ _ ihMajor ihRebuild => + obtain eq | eq := eq + · exact ihRebuild hΓ (.inl eq) + · exact ihMajor hΓ (.inl eq) + | @extra df ls _ Γ h1 h2 => + suffices ∀ e, VExpr.instL ls e = VExpr.forallE A B → + EnvStrong env df.uvars e df.type → + (∃ u, IsDefEqStrong env U Γ A A (.sort u)) ∧ + (∃ u, IsDefEqStrong env U (A :: Γ) B B (.sort u)) by + have ⟨A1, A2⟩ := envIH.2 h1 + cases eq <;> exact this _ ‹_› ‹_› + intro e eq IH + cases e <;> cases eq; rename_i A B + let ⟨⟨_, A1⟩, v, A2⟩ := IH.2.2.2 _ _ rfl + refine ⟨⟨_, (A1.instL h2).weak0 henv⟩, v.inst ls, ?_⟩ + have := (A2.instL h2).weakN henv (.succ (.zero Γ)) + have C1 := (A1.instL h2).defeq.closedN henv ⟨⟩ + have C2 := (A2.instL h2).defeq.closedN henv ⟨⟨⟩, C1⟩ + rw [C1.liftN_eq (Nat.zero_le _), C2.liftN_eq (by exact Nat.le_refl _)] at this + simpa [liftN] + | _ => nomatch eq + +variable! (henv : Ordered env) (envIH : env.OnTypes (EnvStrong env)) in +theorem IsDefEqStrong.isType' (hΓ : CtxStrong env U Γ) (H : env.IsDefEqStrong U Γ e1 e2 A) : + ∃ u, env.IsDefEqStrong U Γ A A (.sort u) := by + induction H with + | bvar h => exact hΓ.lookup henv h + | symm _ ih => exact ih hΓ + | trans _ _ ih1 => exact ih1 hΓ + | sortDF h1 => exact ⟨_, .sortDF h1 h1 rfl⟩ + | constDF h1 h2 => + let ⟨_, h⟩ := envIH.1 h1 + exact ⟨_, (h.1.instL h2).weak0 henv⟩ + | appDF _ _ _ _ _ h4 _ _ _ ih3 => + let ⟨_, ih3⟩ := ih3 hΓ + have ⟨_, _, ih3⟩ := ih3.forallE_inv' henv envIH hΓ (.inl rfl) + exact ⟨_, h4.hasType.1.instN henv hΓ .zero ih3 hΓ⟩ + | lamDF h1 h2 h3 h4 => exact ⟨_, .forallEDF h1 h2 h3.hasType.1 h4 h4⟩ + | forallEDF h1 h2 => exact ⟨_, .sortDF ⟨h1, h2⟩ ⟨h1, h2⟩ rfl⟩ + | defeqDF _ h2 => exact ⟨_, h2.hasType.2⟩ + | beta _ _ _ h4 _ h6 => exact ⟨_, h6.hasType.1.instN henv hΓ .zero h4 hΓ⟩ + | eta _ _ _ _ _ _ _ _ _ _ _ ih => exact ih hΓ + | @structEta _ _ _ _ _ u _ _ _ _ _ _ _ htype _ _ _ _ _ => + exact ⟨u, htype⟩ + | proofIrrel h1 => exact ⟨_, h1⟩ + | extra h1 h2 => + have ⟨_, h⟩ := (envIH.2 h1).2.2.1 + exact ⟨_, (h.instL h2).weak0 henv⟩ + +theorem IsDefEqStrong.instDF + (henv : Ordered env) (hΓ : CtxStrong env U Γ) (hu : u.WF U) (hv : v.WF U) + (hA : env.IsDefEqStrong U Γ A A (.sort u)) + (hB : env.IsDefEqStrong U (A::Γ) B B (.sort v)) + (hf : env.IsDefEqStrong U (A::Γ) f f' B) + (ha : env.IsDefEqStrong U Γ a a' A) : + env.IsDefEqStrong U Γ (f.inst a) (f'.inst a') (B.inst a) := + have H2 {f f' B v} + (hv : v.WF U) + (hB : env.IsDefEqStrong U (A::Γ) B B (.sort v)) + (hf : env.IsDefEqStrong U (A::Γ) f f' B) + (hi : IsDefEqStrong env U Γ (inst B a) (inst B a') (sort v)) : + env.IsDefEqStrong U Γ (f.inst a) (f'.inst a') (B.inst a) := + have H1 {a f} + (hf : env.IsDefEqStrong U (A::Γ) f f' B) + (ha : IsDefEqStrong env U Γ a a A) : + env.IsDefEqStrong U Γ (.app (.lam A f) a) (f.inst a) (B.inst a) := + IsDefEqStrong.beta hu hv hA hB hf.hasType.1 ha + (ha.hasType.1.instN henv hΓ .zero hB hΓ) + (ha.hasType.1.instN henv hΓ .zero hf.hasType.1 hΓ) + (H1 hf ha.hasType.1).symm.trans <| + .trans (.appDF hu hv hA hB (.lamDF hu hv hA hB hB hf hf) ha hi) <| + .defeqDF hv (.symm hi) (H1 hf.hasType.2 ha.hasType.2) + H2 hv hB hf <| H2 (v := v.succ) hv (.sortDF hv hv rfl) hB (.sortDF hv hv rfl) + +variable! (henv : Ordered env) (envIH : env.OnTypes (EnvStrong env)) + (W : CtxStrong env U Γ) in +theorem EqUpToLevels.defeq (H : env.IsDefEqStrong U Γ e1 e2 A) + (H1 : EqUpToLevels U e1 e1') (H2 : EqUpToLevels U e2 e2') : + env.IsDefEqStrong U Γ e1' e2' A := by + induction H generalizing e1' e2' with + | bvar h1 h2 h3 => let .bvar := H1; let .bvar := H2; exact .bvar h1 h2 h3 + | constDF h1 h2 h3 h4 h5 h6 _ _ ih1 ih2 => + let .const a1 a2 a3 := H1 + let .const b1 b2 b3 := H2 + let ⟨_, c1⟩ := envIH.1 h1 + have c2 := a3.flip.trans (T := (· ≈ ·)) (fun _ _ _ => (·.symm.trans)) <| + h5.trans (T := (· ≈ ·)) (fun _ _ _ => Eq.trans) b3 + have := c1.2.2.1 _ _ _ a2 b2 c2 + exact .defeqDF (.inst a1) (.symm ((c1.2.2.1 _ _ _ a1 a2 a3).weak0 henv)) <| + .constDF h1 a2 b2 (a3.length_eq.symm.trans h4) c2 (.inst a2) this (this.weak0 henv) + | symm _ ih => exact (ih W H2 H1).symm + | trans h1 _ ih1 ih2 => + have H3 := (EqUpToLevels.refl W.levelWF h1).2; exact (ih1 W H1 H3).trans (ih2 W H3 H2) + | sortDF h1 h2 h3 => + let .sort a1 a2 a3 := H1 + let .sort b1 b2 b3 := H2 + exact .defeqDF (by exact a1) + (.symm <| .sortDF (by exact a1) (by exact a2) (VLevel.succ_congr a3)) + (.sortDF a2 b2 (a3.symm.trans <| h3.trans b3)) + | appDF h1 h2 h3 h4 h5 h6 _ ih1 ih2 ih3 ih4 ih5 => + let .app a1 a2 := H1 + let .app b1 b2 := H2 + have := ih4 W a2 (EqUpToLevels.refl W.levelWF h6).2 |>.trans h6.symm + exact .defeqDF h2 (.instDF henv W h1 (by exact h2) h3 (.sortDF h2 h2 rfl) h4 this) <| + .appDF h1 h2 h3 h4 (ih3 W a1 b1) (ih4 W a2 b2) <| + .instDF henv W h1 (by exact h2) h3 (.sortDF h2 h2 rfl) h4 (ih4 W a2 b2) + | lamDF h1 h2 h3 h4 h5 h6 h7 ih1 ih2 ih3 ih4 => + let .lam a1 a2 := H1 + let .lam b1 b2 := H2 + have c1 := ih1 W a1 (EqUpToLevels.refl W.levelWF h3).2 |>.trans h3.symm + have c2 := ih1 W (EqUpToLevels.refl W.levelWF h3).1 b1 + have h4' := c1.symm.defeqDF_l henv W h4 + exact have ih := ih4 ⟨W, _, h3.hasType.1⟩ a2 b2 + .defeqDF (by exact ⟨h1, h2⟩) (.forallEDF h1 h2 c1 h4' h4) <| + .lamDF h1 h2 (ih1 W a1 b1) h4' (.defeqDF_l henv W c2 h4) + (.defeqDF_l henv W c1.symm ih) (.defeqDF_l henv W c2 ih) + | forallEDF h1 h2 h3 _ _ ih1 ih2 => + let .forallE a1 a2 := H1 + let .forallE b1 b2 := H2 + have c1 := ih1 W a1 (EqUpToLevels.refl W.levelWF h3).2 |>.trans h3.symm + have c2 := ih1 W (EqUpToLevels.refl W.levelWF h3).1 b1 + exact .forallEDF h1 h2 (ih1 W a1 b1) + (.defeqDF_l henv W c1.symm <| ih2 ⟨W, _, h3.hasType.1⟩ a2 b2) + (.defeqDF_l henv W c2 <| ih2 ⟨W, _, h3.hasType.1⟩ a2 b2) + | defeqDF h1 h2 _ _ ih => exact .defeqDF h1 h2 (ih W H1 H2) + | beta h1 h2 h3 h4 h5 h6 h7 _ ih1 _ ih3 ih4 ih5 ih6 => + let .app a1 a3 := H1; let .lam a1 a2 := a1 + have c1 := ih1 W a1 (EqUpToLevels.refl W.levelWF h3).2 |>.trans h3.symm + have c2 := ih4 W a3 (EqUpToLevels.refl W.levelWF h6).2 |>.trans h6.symm + have c3 := ih3 ⟨W, _, h3⟩ a2 a2 + have hb := (EqUpToLevels.refl (CtxStrong.levelWF (by exact ⟨W, _, h3⟩)) h4).2 + have c4 := ih5 W (.instN a3 hb) (EqUpToLevels.refl W.levelWF h7).2 + refine .trans ?_ (ih6 W (.instN a3 a2) H2) + refine .defeqDF h2 (.instDF henv W h1 (by exact h2) h3 (.sortDF h2 h2 rfl) h4 c2) ?_ + exact .beta h1 h2 c1.hasType.1 (.defeqDF_l henv W c1.symm h4) + (.defeqDF_l henv W c1.symm c3) (.defeqDF h1 c1.symm c2.hasType.1) + c4.hasType.1 (.instN henv c2.hasType.1 W .zero c3 W) + | eta h1 h2 h3 h4 h5 h6 h7 h8 ih1 ih2 ih3 ih4 ih5 => + let .lam a1 a3 := H1; let .app a2 a3 := a3; let .bvar := a3 + have c1 := ih1 W a1 (EqUpToLevels.refl W.levelWF h3).2 |>.trans h3.symm + have c2 := have W' := ⟨W, _, h3⟩; ih5 W' a2 (EqUpToLevels.refl W'.levelWF h7).2 + have c3 := IsDefEqStrong.appDF h1 h2 h8 h5 c2 (.bvar .zero h1 h8) + rw [instN_bvar0] at c3; specialize c3 h4 + refine .trans + (.symm <| .lamDF h1 h2 c1.symm h4 (.defeqDF_l henv W c1.symm h4) c3.symm + (.defeqDF_l henv W c1.symm c3.symm)) ?_ + exact .trans (.eta h1 h2 h3 h4 h5 h6 h7 h8) (ih4 W (EqUpToLevels.refl W.levelWF h6).1 H2) + | structEta hreg hlevels hlevelsLength hparamsLength hparamsSpine hu htype hmajor + hrebuild _ ihMajor ihRebuild => + have hrebuildRefl := (EqUpToLevels.refl W.levelWF hrebuild).2 + have hmajorRefl := (EqUpToLevels.refl W.levelWF hmajor).1 + exact (ihRebuild W H1 hrebuildRefl).trans <| + (IsDefEqStrong.structEta hreg hlevels hlevelsLength hparamsLength + hparamsSpine hu htype hmajor hrebuild).trans + (ihMajor W hmajorRefl H2) + | proofIrrel h1 _ _ _ ih1 ih2 => exact .proofIrrel h1 (ih1 W H1 H1) (ih2 W H2 H2) + | extra h1 h2 h3 h4 h5 h6 h7 h8 h9 _ ih1 ih2 => + have c1 := ih1 trivial H1 (EqUpToLevels.refl (by trivial) h6).2 + have c2 := ih2 trivial H2 (EqUpToLevels.refl (by trivial) h7).2 + refine .weak0 henv <| c1.trans <| .trans ?_ c2.symm + exact .extra h1 h2 h3 h4 h5 h6 h7 h6 h7 + +variable! (henv : Ordered env) (envIH : env.OnTypes (EnvStrong env)) in +theorem IsDefEq.strong' (hΓ : CtxStrong env U Γ) + (H : env.IsDefEq U Γ e1 e2 A) : env.IsDefEqStrong U Γ e1 e2 A := by + have hctx {Γ} (H : OnCtx Γ fun Γ A => ∃ u, env.IsDefEqStrong U Γ A A (.sort u)) : + OnCtx Γ (env.IsType U) := H.mono fun ⟨_, h⟩ => ⟨_, h.defeq⟩ + induction H using IsDefEq.rec + (motive_2 := fun _ _ _ _ _ => True) with + | bvar h => + let ⟨u, hA⟩ := hΓ.lookup henv h + exact .bvar h (hA.defeq.sort_r henv (hctx hΓ)) hA + | symm _ ih => exact (ih hΓ).symm + | trans _ _ ih1 ih2 => exact (ih1 hΓ).trans (ih2 hΓ) + | sortDF h1 h2 h3 => exact .sortDF h1 h2 h3 + | @constDF _ _ ls₁ ls₂ _ h1 h2 h3 h4 h5 => + let ⟨u, h6⟩ := envIH.1 h1 + have := h6.2.2.1 _ _ _ h2 h3 h5 + exact .constDF h1 h2 h3 h4 h5 (.inst h2) this (this.weak0 henv) + | appDF _ _ ih1 ih2 => + let ⟨_, h3⟩ := (ih1 hΓ).isType' henv envIH hΓ + let ⟨⟨u, hA⟩, ⟨v, hB⟩⟩ := h3.forallE_inv' henv envIH hΓ (.inl rfl) + have hu := hA.defeq.sort_r henv hΓ.defeq + have hΓ' : CtxStrong env U (_::_) := ⟨hΓ, _, hA⟩ + have hv := hB.defeq.sort_r henv hΓ'.defeq + exact .appDF hu hv hA hB (ih1 hΓ) (ih2 hΓ) <| + .instDF (v := v.succ) henv hΓ hu hv hA (.sortDF hv hv rfl) hB (ih2 hΓ) + | lamDF hA _ ih1 ih2 => + have hu := hA.sort_r henv hΓ.defeq + have hΓ' : CtxStrong env U (_::_) := ⟨hΓ, _, (ih1 hΓ).hasType.1⟩ + let ⟨_, hB⟩ := (ih2 hΓ').isType' henv envIH hΓ' + exact .lamDF hu (hB.defeq.sort_r henv hΓ'.defeq) (ih1 hΓ) + hB ((ih1 hΓ).defeqDF_l henv hΓ hB) (ih2 hΓ') ((ih1 hΓ).defeqDF_l henv hΓ (ih2 hΓ')) + | forallEDF hA hb ih1 ih2 => + have hu := hA.sort_r henv hΓ.defeq + have hΓ' : CtxStrong env U (_::_) := ⟨hΓ, _, (ih1 hΓ).hasType.1⟩ + exact .forallEDF hu (hb.sort_r henv hΓ'.defeq) + (ih1 hΓ) (ih2 hΓ') ((ih1 hΓ).defeqDF_l henv hΓ (ih2 hΓ')) + | defeqDF hAB _ ih1 ih2 => + exact .defeqDF (hAB.sort_r henv hΓ.defeq) (ih1 hΓ) (ih2 hΓ) + | beta _ _ ih1 ih2 => + have he' := ih2 hΓ + have ⟨_, hA⟩ := he'.isType' henv envIH hΓ + have hΓ' : CtxStrong env U (_::_) := ⟨hΓ, _, hA⟩ + have he := ih1 hΓ' + have ⟨_, hB⟩ := he.isType' henv envIH hΓ' + exact .beta (hA.defeq.sort_r henv hΓ.defeq) (hB.defeq.sort_r henv hΓ'.defeq) + hA hB he he' (he'.instN henv hΓ .zero hB hΓ) (he'.instN henv hΓ .zero he hΓ) + | eta _ ih => + have he := ih hΓ + let ⟨_, hAB⟩ := he.isType' henv envIH hΓ + let ⟨⟨u, hA⟩, ⟨v, hB⟩⟩ := hAB.forallE_inv' henv envIH hΓ (.inl rfl) + have hΓ' : CtxStrong env U (_::_) := ⟨hΓ, _, hA⟩ + exact .eta (hA.defeq.sort_r henv hΓ.defeq) (hB.defeq.sort_r henv hΓ'.defeq) + hA hB (hB.weakN henv (.succ .one)) he (he.weakN henv .one) (hA.weakN henv .one) + | structEta hreg hlevels hlevelsLength hparamsLength hparamsSpine + _ _ _ ihMajor ihRebuild => + have hmajor := ihMajor hΓ + have hrebuild := ihRebuild hΓ + let ⟨u, htype⟩ := hmajor.isType' henv envIH hΓ + exact .structEta hreg hlevels hlevelsLength hparamsLength + hparamsSpine (htype.defeq.sort_r henv hΓ.defeq) htype hmajor hrebuild + | proofIrrel _ _ _ ih1 ih2 ih3 => exact .proofIrrel (ih1 hΓ) (ih2 hΓ) (ih3 hΓ) + | extra h1 h2 h3 => + let ⟨⟨hl, ⟨_, ht⟩, _⟩, hr, _, _⟩ := envIH.2 h1 + exact .extra h1 h2 h3 (.inst h2) (ht.instL h2) + (hl.instL h2) (hr.instL h2) ((hl.instL h2).weak0 henv) ((hr.instL h2).weak0 henv) + | nil | cons => trivial + +theorem CtxStrong.strong' (henv : Ordered env) (envIH : env.OnTypes (EnvStrong env)) + (hΓ : OnCtx Γ (env.IsType U)) : CtxStrong env U Γ := by + induction Γ with + | nil => trivial + | cons _ _ ih => let ⟨hΓ, _, hA⟩ := hΓ; exact ⟨ih hΓ, _, hA.strong' henv envIH (ih hΓ)⟩ + +theorem Ordered.strong (henv : Ordered env) : OnTypes env (EnvStrong env) := by + refine henv.induction _ (fun le ⟨h1, ⟨_, h2⟩, h3, h4⟩ => ?_) (fun henv IH H => ?_) + · refine ⟨h1.mono le, ⟨_, h2.mono le⟩, ?_, ?_⟩ + · exact fun _ _ _ h4 h5 h6 => (h3 _ _ _ h4 h5 h6).mono le + · exact fun _ _ eq => let ⟨⟨_, h4⟩, ⟨_, h5⟩⟩ := h4 _ _ eq; ⟨⟨_, h4.mono le⟩, ⟨_, h5.mono le⟩⟩ + · have H' := H.strong' henv IH (Γ := []) ⟨⟩ + refine ⟨H', H'.isType' henv IH ⟨⟩, fun _ _ _ h1 h2 h3 => ?_, ?_⟩ + · exact EqUpToLevels.defeq henv IH (by trivial) (.instL h1 H') + (EqUpToLevels.refl (by trivial) (.instL h1 H')).1 (EqUpToLevels.instL h1 h2 h3 H').1 + · exact fun _ _ eq => H'.forallE_inv' henv IH ⟨⟩ (.inl eq) + +theorem CtxStrong.strong (henv : Ordered env) (hΓ : OnCtx Γ (env.IsType U)) : CtxStrong env U Γ := + .strong' henv henv.strong hΓ + +theorem IsDefEq.strong (henv : Ordered env) (hΓ : OnCtx Γ (env.IsType U)) + (H : env.IsDefEq U Γ e1 e2 A) : env.IsDefEqStrong U Γ e1 e2 A := + H.strong' henv henv.strong (.strong henv hΓ) + +variable! (henv : Ordered env) (hΓ : OnCtx Γ (env.IsType U)) in +theorem IsDefEq.eqUpToLevels (H : env.IsDefEq U Γ e1 e2 A) + (H1 : EqUpToLevels U e2 e2') : env.IsDefEq U Γ e1 e2' A := + have W := .strong' henv henv.strong hΓ + have := H.strong henv hΓ + (EqUpToLevels.defeq henv henv.strong W (H.strong henv hΓ) + (EqUpToLevels.refl W.levelWF this).1 H1).defeq + +variable! (henv : Ordered env) (hΓ : OnCtx Γ (env.IsType U')) {ls ls' : List VLevel} + (hls : ∀ l ∈ ls, l.WF U) (hls' : ∀ l ∈ ls', l.WF U) (heq : List.Forall₂ (· ≈ ·) ls ls') in +theorem IsDefEq.instL_r (H : env.IsDefEq U' Γ e1 e2 A) : + env.IsDefEq U (Γ.map (VExpr.instL ls)) (e1.instL ls) (e2.instL ls') (A.instL ls) := + have := H.strong henv hΓ + IsDefEq.eqUpToLevels henv (hΓ.instL hls) (this.instL hls).defeq + (EqUpToLevels.instL hls hls' heq this).2 + +theorem IsDefEqStrong.hasType' {env : VEnv} + (H : env.IsDefEqStrong U Γ e1 e2 A) : + env.HasTypeStrong U Γ e1 A true ∧ env.HasTypeStrong U Γ e2 A true := by + induction H with + | bvar h1 h2 _ ih3 => refine and_self_iff.2 <| .base <| .bvar h1 h2 ih3.1 + | symm _ ih => exact ih.symm + | trans _ _ ih1 ih2 | proofIrrel _ _ _ _ ih1 ih2 => exact ⟨ih1.1, ih2.2⟩ + | sortDF h1 h2 h3 => exact ⟨.base <| .sort' h1 h1 rfl, .base <| .sort' h2 h1 h3.symm⟩ + | constDF h1 h2 h3 h4 h5 h6 h7 h8 ih1 ih2 => + exact ⟨.base <| .const h1 h2 h4 h6 ih1.1 ih2.1, + .defeq h6 h8.symm ih2.2 ih2.1 <| .base <| + .const h1 h3 (h5.length_eq.symm.trans h4) h6 ih1.2 ih2.2⟩ + | appDF h1 h2 h3 h4 h5 h6 h7 ih1 ih2 ih3 ih4 ih5 => + have := HasTypeStrong.base <| .forallE h1 h2 ih1.1 ih2.1 + exact ⟨.base <| .app h1 h2 ih1.1 ih2.1 this ih3.1 ih4.1 ih5.1, + .defeq h2 h7.symm ih5.2 ih5.1 <| .base <| .app h1 h2 ih1.2 ih2.2 this ih3.2 ih4.2 ih5.2⟩ + | lamDF h1 h2 h3 h4 h5 h6 h7 ih1 ih2 ih3 ih4 ih5 => + refine ⟨.base <| .lam h1 h2 ih1.1 ih2.1 ih4.1 ?a, + .defeq (by exact ⟨h1, h2⟩) (.symm <| .forallEDF h1 h2 h3 h4 h5) ?b ?a ?_⟩ + · exact .base <| .forallE h1 h2 ih1.1 ih2.1 + · exact .base <| .forallE h1 h2 ih1.2 ih3.1 + · exact .base <| .lam h1 h2 ih1.2 ih3.1 ih5.2 ?b + | forallEDF h1 h2 h3 h4 h5 ih1 ih2 ih3 => + exact ⟨.base <| .forallE h1 h2 ih1.1 ih2.1, .base <| .forallE h1 h2 ih1.2 ih3.2⟩ + | defeqDF h1 h2 h3 ih1 ih2 => + exact ⟨.defeq h1 h2 ih1.1 ih1.2 ih2.1, .defeq h1 h2 ih1.1 ih1.2 ih2.2⟩ + | beta h1 h2 _ _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 ih6 => + have := HasTypeStrong.base <| .forallE h1 h2 ih1.1 ih2.1 + refine ⟨.base <| .app h1 h2 ih1.1 ih2.1 this ?_ ih4.1 ih5.1, ih6.1⟩ + refine .base <| .lam h1 h2 ih1.1 ih2.1 ih3.1 ?_ + exact .base <| .forallE h1 h2 ih1.1 ih2.1 + | @eta Γ A u B v e h1 h2 _ _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 ih6 => + refine have a1 := .base <| .forallE h1 h2 ih6.1 ih3.1 + have := ih6.1.app h1 h2 ih3.1 a1 ih5.1 (.base <| .bvar .zero h1 ih6.1); ?_ + rw [instN_bvar0] at this; specialize this ih2.1 + refine ⟨.base <| .lam h1 h2 ih1.1 ih2.1 (.base this) ?_, ih4.1⟩ + exact .base <| .forallE h1 h2 ih1.1 ih2.1 + | structEta _ _ _ _ _ _ _ _ _ _ ihMajor ihRebuild => + exact ⟨ihRebuild.1, ihMajor.1⟩ + | extra h1 h2 h3 h4 h5 h6 h7 _ _ _ _ _ ih4 ih5 => exact ⟨ih4.1, ih5.1⟩ + +theorem HasTypeStrong.refl {env : VEnv} + (H : env.HasTypeStrong U Γ e A b) : env.IsDefEqStrong U Γ e e A := by + induction H with + | bvar h1 h2 _ ih1 => refine .bvar h1 h2 ih1 + | sort' h1 h2 h3 => + refine .defeqDF ?_ (.sortDF ?_ ?_ (VLevel.succ_congr h3)) (.sortDF h1 h1 rfl) <;> assumption + | const h1 h2 h3 h4 h5 h6 ih1 ih2 => + exact .constDF h1 h2 h2 h3 (.rfl fun _ _ => rfl) h4 ih1 ih2 + | app h1 h2 h3 h4 _ h5 h6 h7 ih1 ih2 _ ih3 ih4 ih5 => + exact .appDF h1 h2 ih1 ih2 ih3 ih4 ih5 + | lam h1 h2 h3 h4 h5 _ ih1 ih2 ih3 => + exact .lamDF h1 h2 ih1 ih2 ih2 ih3 ih3 + | forallE h1 h2 h3 h4 ih1 ih2 => + exact .forallEDF h1 h2 ih1 ih2 ih2 + | base _ ih => exact ih + | defeq h1 h2 h3 h4 h5 ih1 ih2 ih3 => exact .defeqDF h1 h2 ih3 + +theorem HasTypeStrong.hasType {env : VEnv} + (H : env.HasTypeStrong U Γ e A b) : env.HasType U Γ e A := H.refl.defeq.hasType.1 + +variable! (henv : Ordered env) (hΓ : OnCtx Γ (env.IsType U)) in +theorem HasType.app_inv (H : env.HasType U Γ (.app f a) V) : + ∃ A B, env.HasType U Γ f (.forallE A B) ∧ env.HasType U Γ a A := by + replace H := (H.strong henv hΓ).hasType'.1 + generalize eq : true = b, eq' : f.app a = e' at H + induction H with cases eq + | defeq _ _ _ _ _ _ _ ih => exact ih hΓ rfl eq' + | base H => + subst eq'; let .app _ _ _ _ _ h1 h2 _ := H; exact ⟨_, _, h1.hasType, h2.hasType⟩ + +variable! (henv : Ordered env) (hΓ : OnCtx Γ (env.IsType U)) in +theorem _root_.Ix.Theory.Named.VExpr.WF.app_inv (H : VExpr.WF env U Γ (.app f a)) : + ∃ A B, env.HasType U Γ f (.forallE A B) ∧ env.HasType U Γ a A := + let ⟨_, H⟩ := H; HasType.app_inv henv hΓ H + +variable! (henv : Ordered env) (hΓ : OnCtx Γ (env.IsType U)) in +theorem HasType.lam_inv (H : env.HasType U Γ (.lam A body) V) : + env.IsType U Γ A ∧ body.WF env U (A::Γ) := by + replace H := (H.strong henv hΓ).hasType'.1 + generalize eq : true = b, eq' : A.lam body = e' at H + induction H with cases eq + | defeq _ _ _ _ _ _ _ ih => exact ih hΓ rfl eq' + | base H => subst eq'; let .lam _ _ h1 _ h2 _ := H; exact ⟨⟨_, h1.hasType⟩, _, h2.hasType⟩ + +variable! (henv : Ordered env) (hΓ : OnCtx Γ (env.IsType U)) in +theorem _root_.Ix.Theory.Named.VExpr.WF.lam_inv (H : VExpr.WF env U Γ (.lam A body)) : + env.IsType U Γ A ∧ body.WF env U (A::Γ) := + let ⟨_, H⟩ := H; HasType.lam_inv henv hΓ H + +variable! (henv : Ordered env) (hΓ : OnCtx Γ (env.IsType U)) in +theorem HasType.const_inv (H : env.HasType U Γ (.const c ls) V) : + ∃ ci, env.constants c = some ci ∧ (∀ l ∈ ls, l.WF U) ∧ ls.length = ci.uvars := by + replace H := (H.strong henv hΓ).hasType'.1 + generalize eq : true = b, eq' : VExpr.const c ls = e' at H + induction H with cases eq + | defeq _ _ _ _ _ _ _ ih => exact ih hΓ rfl eq' + | base H => subst eq'; let .const h1 h2 h3 .. := H; exact ⟨_, h1, h2, h3⟩ + +variable! (henv : Ordered env) (hΓ : OnCtx Γ (env.IsType U)) in +theorem _root_.Ix.Theory.Named.VExpr.WF.const_inv (H : VExpr.WF env U Γ (.const c ls)) : + ∃ ci, env.constants c = some ci ∧ (∀ l ∈ ls, l.WF U) ∧ ls.length = ci.uvars := + let ⟨_, H⟩ := H; HasType.const_inv henv hΓ H + +variable! (henv : Ordered env) (hΓ : OnCtx Γ (env.IsType U)) in +theorem HasType.bvar_inv (H : env.HasType U Γ (.bvar i) V) : ∃ A, Lookup Γ i A := by + replace H := (H.strong henv hΓ).hasType'.1 + generalize eq : true = b, eq' : VExpr.bvar i = e' at H + induction H with cases eq + | defeq _ _ _ _ _ _ _ ih => exact ih hΓ rfl eq' + | base H => subst eq'; let .bvar h1 .. := H; exact ⟨_, h1⟩ + +set_option hygiene false +local notation:65 Γ " ⊢ " e " : " A:36 => HasType env U Γ e A +local notation:65 Γ " ⊢ " e1 " ≡ " e2 " : " A:36 => IsDefEq env U Γ e1 e2 A +section +local notation:65 Γ " ⊢ " e " : " A " !! " n:30 => HasTypeStratified Γ e A true n +local notation:65 Γ " ⊢ " e " :! " A " !! " n:30 => HasTypeStratified Γ e A false n + +variable (env : VEnv) (U : Nat) in +inductive HasTypeStratified : List VExpr → VExpr → VExpr → Bool → Nat → Prop where + | bvar : Lookup Γ i A → Γ ⊢ A : .sort u !! n → Γ ⊢ .bvar i :! A !! n+1 + | sort' : l.WF U → l'.WF U → l ≈ l' → Γ ⊢ .sort l :! .sort (.succ l') !! n + | const : + env.constants c = some ci → + (∀ l ∈ ls, l.WF U) → + ls.length = ci.uvars → + Γ ⊢ ci.type.instL ls : .sort u !! n → + Γ ⊢ .const c ls :! ci.type.instL ls !! n+1 + | app : + u.WF U → v.WF U → + Γ ⊢ A : .sort u !! n → + A::Γ ⊢ B : .sort v !! n → + Γ ⊢ f : .forallE A B !! n → + Γ ⊢ a : A !! n → + Γ ⊢ B.inst a : .sort v !! n → + Γ ⊢ .app f a :! B.inst a !! n+1 + | lam : + Γ ⊢ A : .sort u !! n → + A::Γ ⊢ B : .sort v !! n → + A::Γ ⊢ body : B !! n → + Γ ⊢ .forallE A B : .sort (.imax u v) !! n → + Γ ⊢ .lam A body :! .forallE A B !! n+1 + | forallE : + u.WF U → v.WF U → + Γ ⊢ A : .sort u !! n → + A::Γ ⊢ body : .sort v !! n → + Γ ⊢ .forallE A body :! .sort (.imax u v) !! n+1 + | base : Γ ⊢ e :! A !! n → Γ ⊢ e : A !! n + | defeq : u.WF U → Γ ⊢ A ≡ B : .sort u → + Γ ⊢ A : .sort u !! n → Γ ⊢ B : .sort u !! n → Γ ⊢ e : A !! n → Γ ⊢ e : B !! n+1 +end + +theorem HasTypeStratified.hasType (H : env.HasTypeStratified U Γ e A b n) : Γ ⊢ e : A := by + induction H with + | bvar h1 h2 ih1 => refine .bvar h1 + | sort' h1 h2 h3 => + exact IsDefEq.defeq (.sortDF (by exact h1) h2 (VLevel.succ_congr h3)) (.sort h1) + | const h1 h2 h3 => exact .const h1 h2 h3 + | app _ _ _ _ _ _ _ _ _ ih3 ih4 => exact .app ih3 ih4 + | lam _ _ _ _ ih1 _ ih3 => exact .lam ih1 ih3 + | forallE _ _ _ _ ih1 ih2 => exact .forallE ih1 ih2 + | base _ ih => exact ih + | defeq _ h2 _ _ _ _ _ ih3 => exact h2.defeq ih3 + +theorem HasTypeStratified.mono (le : m ≤ n) (H : HasTypeStratified env U Γ e A b m) : + env.HasTypeStratified U Γ e A b n := by + induction H generalizing n with try let n+1 := n; replace le := Nat.le_of_succ_le_succ le + | bvar h1 h2 ih1 => exact .bvar h1 (ih1 le) + | sort' h1 h2 h3 => exact .sort' h1 h2 h3 + | const h1 h2 h3 _ ih1 => exact .const h1 h2 h3 (ih1 le) + | app h1 h2 _ _ _ _ _ ih1 ih2 ih3 ih4 ih5 => + exact .app h1 h2 (ih1 le) (ih2 le) (ih3 le) (ih4 le) (ih5 le) + | lam _ _ _ _ ih1 ih2 ih3 ih4 => exact .lam (ih1 le) (ih2 le) (ih3 le) (ih4 le) + | forallE h1 h2 _ _ ih1 ih2 => exact .forallE h1 h2 (ih1 le) (ih2 le) + | base _ ih => exact .base (ih le) + | defeq h1 h2 _ _ _ ih1 ih2 ih3 => exact .defeq h1 h2 (ih1 le) (ih2 le) (ih3 le) + +theorem HasTypeStrong.stratify (H : HasTypeStrong env U Γ e A b) : + ∃ n, HasTypeStratified env U Γ e A b n := by + generalize true = b at H ⊢ + induction H with + | bvar h1 h2 _ ih1 => let ⟨n, ih1⟩ := ih1; exact ⟨_, .bvar h1 ih1⟩ + | sort' h1 h2 h3 => exact ⟨0, .sort' h1 h2 h3⟩ + | const h1 h2 h3 _ _ _ _ ih1 => let ⟨_, ih1⟩ := ih1; exact ⟨_, .const h1 h2 h3 ih1⟩ + | app h1 h2 _ _ _ _ _ _ ih1 ih2 _ ih3 ih4 ih5 => + let ⟨n₁, ih1⟩ := ih1; let ⟨n₂, ih2⟩ := ih2; let ⟨n₃, ih3⟩ := ih3 + let ⟨n₄, ih4⟩ := ih4; let ⟨n₅, ih5⟩ := ih5 + refine ⟨max n₁ (max n₂ (max n₃ (max n₄ n₅))) + 1, + .app h1 h2 (ih1.mono ?_) (ih2.mono ?_) (ih3.mono ?_) (ih4.mono ?_) (ih5.mono ?_)⟩ <;> omega + | lam _ _ _ _ _ _ ih1 ih2 ih3 ih4 => + let ⟨n₁, ih1⟩ := ih1; let ⟨n₂, ih2⟩ := ih2; let ⟨n₃, ih3⟩ := ih3; let ⟨n₄, ih4⟩ := ih4 + refine ⟨max n₁ (max n₂ (max n₃ n₄)) + 1, + .lam (ih1.mono ?_) (ih2.mono ?_) (ih3.mono ?_) (ih4.mono ?_)⟩ <;> omega + | forallE h1 h2 _ _ ih1 ih2 => + let ⟨n₁, ih1⟩ := ih1; let ⟨n₂, ih2⟩ := ih2 + refine ⟨max n₁ n₂ + 1, .forallE h1 h2 (ih1.mono ?_) (ih2.mono ?_)⟩ <;> omega + | base _ ih => let ⟨_, ih⟩ := ih; exact ⟨_, .base ih⟩ + | defeq h1 h2 _ _ _ ih1 ih2 ih3 => + let ⟨n₁, ih1⟩ := ih1; let ⟨n₂, ih2⟩ := ih2; let ⟨n₃, ih3⟩ := ih3 + refine ⟨max n₁ (max n₂ n₃) + 1, + .defeq h1 h2.defeq (ih1.mono ?_) (ih2.mono ?_) (ih3.mono ?_)⟩ <;> omega + +theorem HasTypeStratified.to_core (H : HasTypeStratified env U Γ e A true n) : + ∃ A', HasTypeStratified env U Γ e A' false n := by + generalize hb : true = b at H + induction H with cases hb + | base h _ => exact ⟨_, h⟩ + | defeq _ _ _ _ _ _ _ ih3 => obtain ⟨A', hA'⟩ := ih3 rfl; exact ⟨A', hA'.mono (Nat.le_succ _)⟩ + +theorem HasTypeStratified.isType (H : HasTypeStratified env U Γ e A b n) : + ∃ u, HasTypeStratified env U Γ A (.sort u) true (n - 1) := by + induction H with + | base _ ih => exact ih + | bvar _ h | const _ _ _ h | app _ _ _ _ _ _ h | lam _ _ _ h | defeq _ _ _ h => exact ⟨_, h⟩ + | @sort' _ l _ _ _ h _ => exact ⟨_, .base (.sort' (l := l.succ) (l' := l.succ) h h rfl)⟩ + | @forallE _ _ u _ _ v h1 h2 => + exact ⟨_, .base (.sort' (l := .imax u v) (l' := .imax u v) ⟨h1, h2⟩ ⟨h1, h2⟩ rfl)⟩ diff --git a/Ix/Theory/Named/Typing/UniqueTyping.lean b/Ix/Theory/Named/Typing/UniqueTyping.lean new file mode 100644 index 000000000..66579ab3b --- /dev/null +++ b/Ix/Theory/Named/Typing/UniqueTyping.lean @@ -0,0 +1,377 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Typing.Injectivity +import Ix.Theory.Named.Typing.Pattern + +open Ix.Theory (VLevel) + +/-! # Unique typing and its consequences. -/ + +namespace Ix.Theory.Named +namespace VEnv + +variable {env : VEnv} {U : Nat} +local notation:65 Γ " ⊢ " e " : " A:36 => HasType env U Γ e A +local notation:65 Γ " ⊢ " e1 " ≡ " e2 " : " A:36 => IsDefEq env U Γ e1 e2 A + +theorem IsDefEq.uniq (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (h1 : Γ ⊢ e₁ ≡ e₂ : A) (h2 : Γ ⊢ e₂ ≡ e₃ : B) : ∃ u, Γ ⊢ A ≡ B : .sort u := by + suffices ∀ {e A B b n₁ n₂ n}, n₁ ≤ n → n₂ ≤ n → + env.HasTypeStratified U Γ e A b n₁ → env.HasTypeStratified U Γ e B b n₂ → + ∃ u, Γ ⊢ A ≡ B : .sort u ∧ ∃ v, u ≈ v ∧ + env.HasTypeStratified U Γ A (.sort u) true (n-1) ∧ + env.HasTypeStratified U Γ B (.sort v) true (n-1) from + let ⟨n₁, h1⟩ := (h1.strong henv hΓ).hasType'.2.stratify + let ⟨n₂, h2⟩ := (h2.strong henv hΓ).hasType'.1.stratify + have ⟨_, h, _⟩ := this (Nat.le_max_left ..) (Nat.le_max_right ..) h1 h2 + ⟨_, h⟩ + clear h1 h2; intro e A B b n₁ n₂ n le₁ le₂ H1 + induction n using WellFounded.induction Nat.lt_wfRel.2 + generalizing n₁ n₂ Γ e A B b with | _ n IH + induction H1 generalizing B n₂ n with + | bvar a1 a2 => + intro (.bvar b1 b2); cases a1.uniq b1 + exact ⟨_, b2.hasType, _, rfl, b2.mono (by omega), b2.mono (by omega)⟩ + | sort' a1 a2 a3 => + intro (.sort' b1 b2 b3) + have := VLevel.succ_congr (b3.symm.trans a3) + exact ⟨_, .symm <| .sortDF b2 a2 this, _, VLevel.succ_congr this, + .base <| .sort' a2 b2 this.symm, .base <| .sort' b2 a2 this⟩ + | const a1 a2 a3 a4 => + intro (.const b1 b2 b3 b4); cases a1.symm.trans b1 + replace le₁ := Nat.sub_le_sub_right le₁ 1 + exact ⟨_, a4.hasType, _, rfl, a4.mono le₁, a4.mono le₁⟩ + | app _ a2 a3 a4 _ a6 a7 _ _ ih3 => + intro (.app _ _ b3 b4 b5 _ b7) + have ⟨_, c1, _, _, c3, c4⟩ := ih3 n IH hΓ (Nat.le_of_succ_le le₁) (Nat.le_of_succ_le le₂) b5 + have ⟨_, _, d3, d4, d5⟩ := IsDefEqU.forallE_inv_stratified henv hΓ ⟨_, c1⟩ c3 c4 + let n+1 := n + replace le₁ := Nat.le_of_succ_le_succ le₁ + replace le₂ := Nat.le_of_succ_le_succ le₂ + have e1 := have ⟨_, h⟩ := a3.hasType.isType henv hΓ; h.sort_inv henv + refine have hΓ₁ := ⟨hΓ, _, a3.hasType⟩ + have ⟨_, h, _⟩ := IH _ (Nat.lt_succ_self _) hΓ₁ le₁ (Nat.le_refl _) a4 d4; ?_ + have e3 := IsDefEqU.sort_inv henv hΓ₁ ⟨_, h⟩ + have e4 := have ⟨_, h⟩ := d4.hasType.isType henv hΓ₁; h.sort_inv henv + refine have hΓ₂ := ⟨hΓ, _, b3.hasType⟩ + have ⟨_, h, _⟩ := IH _ (Nat.lt_succ_self _) hΓ₂ le₂ (Nat.le_refl _) b4 d5; ?_ + have e5 := IsDefEqU.sort_inv henv hΓ₂ ⟨_, h⟩ + exact ⟨_, .defeqDF (.sortDF e4 a2 e3.symm) (d3.instN henv a6.hasType .zero), _, + e3.trans e5.symm, a7.mono le₁, b7.mono le₂⟩ + | lam a1 a2 _ a4 _ _ ih3 => + intro (.lam b1 b2 b3 b4) + have ⟨_, c1, _, c2, c3, c4⟩ := ih3 n IH ⟨hΓ, _, a1.hasType⟩ + (Nat.le_of_succ_le le₁) (Nat.le_of_succ_le le₂) b3 + let n+1 := n + replace le₁ := Nat.le_of_succ_le_succ le₁ + replace le₂ := Nat.le_of_succ_le_succ le₂ + have ⟨_, h, _⟩ := IH _ (Nat.lt_succ_self _) hΓ le₁ le₂ a1 b1 + have e1 := IsDefEqU.sort_inv henv hΓ ⟨_, h⟩ + refine have hΓ' := ⟨hΓ, _, a1.hasType⟩ + have ⟨_, h, _⟩ := IH _ (Nat.lt_succ_self _) hΓ' le₁ (Nat.le_refl _) a2 c3; ?_ + have f1 := h.sort_inv_l henv; have f2 := h.sort_inv_r henv + have e2 := IsDefEqU.sort_inv henv hΓ' ⟨_, h⟩ + have ⟨_, h, _⟩ := IH _ (Nat.lt_succ_self _) hΓ' le₂ (Nat.le_refl _) b2 c4 + have e3 := IsDefEqU.sort_inv henv hΓ' ⟨_, h⟩ + exact ⟨_, .forallEDF a1.hasType (.defeqDF (.symm <| .sortDF f1 f2 e2) c1), + _, VLevel.imax_congr e1 (e2.trans <| c2.trans e3.symm), a4.mono le₁, b4.mono le₂⟩ + | forallE a1 a2 a3 a4 => + intro (.forallE b1 b2 b3 b4) + let n+1 := n + replace le₁ := Nat.le_of_succ_le_succ le₁ + replace le₂ := Nat.le_of_succ_le_succ le₂ + have ⟨_, h, _⟩ := IH _ (Nat.lt_succ_self _) hΓ le₁ le₂ a3 b3 + have e1 := IsDefEqU.sort_inv henv hΓ ⟨_, h⟩ + refine have hΓ' := ⟨hΓ, _, a3.hasType⟩ + have ⟨_, h, _⟩ := IH _ (Nat.lt_succ_self _) hΓ' le₁ le₂ a4 b4; ?_ + have e2 := IsDefEqU.sort_inv henv hΓ' ⟨_, h⟩ + have := VLevel.imax_congr e1 e2 + exact ⟨_, .sortDF ⟨a1, a2⟩ ⟨b1, b2⟩ this, _, rfl, + .base <| .sort' ⟨a1, a2⟩ ⟨a1, a2⟩ rfl, .base <| .sort' ⟨b1, b2⟩ ⟨a1, a2⟩ this.symm⟩ + | @base Γ e A n₁ a1 ih => + intro H2 + replace ih {n'} le := @ih n' (fun y h1 => IH y (Nat.lt_of_lt_of_le h1 le)) hΓ + generalize eq : true = b at H2 + induction H2 with cases eq + | @base _ _ _ n' b1 => exact ih (Nat.le_refl _) le₁ le₂ b1 + | @defeq Γ B' B u n' _ b1 b2 b3 b4 b5 _ _ ih' => + have ⟨u₁, c1, u₂, c2, c3, c4⟩ := ih' a1 hΓ (Nat.le_of_succ_le le₂) ih rfl + let n+1 := n + replace le₂ := Nat.le_of_succ_le_succ le₂ + have ⟨v₁, d1, v₂, _⟩ := IH _ (Nat.lt_succ_self _) hΓ le₂ (Nat.le_refl _) b3 c4 + have e1 := IsDefEqU.sort_inv henv hΓ ⟨_, d1⟩ + have e2 := have ⟨_, h⟩ := c3.hasType.isType henv hΓ; h.sort_inv henv + have eq : u₁ ≈ u := c2.trans e1.symm + exact ⟨_, c1.trans (.defeqDF (.symm <| .sortDF e2 b1 eq) b2), _, eq, c3, b4.mono le₂⟩ + | @defeq Γ A' A u n' _ a1 a2 a3 a4 a5 ih1 ih2 ih' => + intro H2 + have ⟨_, c1, u₂, c2, c3, c4⟩ := ih' _ IH hΓ (Nat.le_of_succ_le le₁) le₂ H2 + let n+1 := n + replace le₁ := Nat.le_of_succ_le_succ le₁ + have ⟨_, d1, _, _⟩ := IH _ (Nat.lt_succ_self _) hΓ le₁ (Nat.le_refl _) a3 c3 + have e1 := IsDefEqU.sort_inv henv hΓ ⟨_, d1⟩ + have e2 := have ⟨_, h⟩ := c3.hasType.isType henv hΓ; h.sort_inv henv + have eq : u ≈ u₂ := e1.trans c2 + exact ⟨_, a2.symm.trans (.defeqDF (.symm <| .sortDF a1 e2 e1) c1), _, eq, a4.mono le₁, c4⟩ + +theorem IsDefEq.uniqU (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (h1 : env.IsDefEq U Γ e₁ e₂ A) (h2 : env.IsDefEq U Γ e₂ e₃ B) : + env.IsDefEqU U Γ A B := let ⟨_, h⟩ := h1.uniq henv hΓ h2; ⟨_, h⟩ + +theorem isDefEq_iff (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) : + env.IsDefEq U Γ e₁ e₂ A ↔ + env.HasType U Γ e₁ A ∧ env.HasType U Γ e₂ A ∧ env.IsDefEqU U Γ e₁ e₂ := by + refine ⟨fun h => ⟨h.hasType.1, h.hasType.2, _, h⟩, fun ⟨_, h2, _, h3⟩ => ?_⟩ + have ⟨_, h⟩ := h3.uniq henv hΓ h2 + exact h.defeqDF h3 + +theorem IsDefEq.trans_r (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (h₁ : env.IsDefEq U Γ e₁ e₂ A) (h₂ : env.IsDefEq U Γ e₂ e₃ B) : + env.IsDefEq U Γ e₁ e₃ B := have ⟨_, h⟩ := h₁.uniq henv hΓ h₂; .trans (.defeqDF h h₁) h₂ + +theorem IsDefEq.trans_l (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (h₁ : env.IsDefEq U Γ e₁ e₂ A) (h₂ : env.IsDefEq U Γ e₂ e₃ B) : + env.IsDefEq U Γ e₁ e₃ A := have ⟨_, h⟩ := h₁.uniq henv hΓ h₂; h₁.trans (.defeqDF (.symm h) h₂) + +theorem IsDefEq.transU_r (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (h₁ : env.IsDefEqU U Γ e₁ e₂) (h₂ : env.IsDefEq U Γ e₂ e₃ A) : + env.IsDefEq U Γ e₁ e₃ A := have ⟨_, h₁⟩ := h₁; .trans_r henv hΓ h₁ h₂ + +theorem IsDefEq.transU_l (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (h₁ : env.IsDefEq U Γ e₁ e₂ A) (h₂ : env.IsDefEqU U Γ e₂ e₃) : + env.IsDefEq U Γ e₁ e₃ A := have ⟨_, h₂⟩ := h₂; .trans_l henv hΓ h₁ h₂ + +theorem IsDefEqU.defeqDF (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (h₁ : env.IsDefEqU U Γ A B) (h₂ : env.IsDefEq U Γ e₁ e₂ A) : + env.IsDefEq U Γ e₁ e₂ B := by + have ⟨_, h₁⟩ := h₁ + have ⟨_, hA⟩ := h₂.isType henv hΓ + exact .defeqDF (hA.trans_l henv hΓ h₁) h₂ + +theorem IsDefEqU.of_l (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (h1 : env.IsDefEqU U Γ e₁ e₂) (h2 : env.HasType U Γ e₁ A) : + env.IsDefEq U Γ e₁ e₂ A := let ⟨_, h⟩ := h1; h2.trans_l henv hΓ h + +theorem HasType.defeqU_l (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (h1 : env.IsDefEqU U Γ e₁ e₂) (h2 : env.HasType U Γ e₁ A) : + env.HasType U Γ e₂ A := (h1.of_l henv hΓ h2).hasType.2 + +theorem IsType.defeqU_l (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (h1 : env.IsDefEqU U Γ A₁ A₂) (h2 : env.IsType U Γ A₁) : + env.IsType U Γ A₂ := h2.imp fun _ h2 => h2.defeqU_l henv hΓ h1 + +theorem IsDefEqU.of_r (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (h1 : env.IsDefEqU U Γ e₁ e₂) (h2 : env.HasType U Γ e₂ A) : + env.IsDefEq U Γ e₁ e₂ A := (h1.symm.of_l henv hΓ h2).symm + +theorem HasType.defeqU_r (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (h1 : env.IsDefEqU U Γ A₁ A₂) (h2 : env.HasType U Γ e A₁) : + env.HasType U Γ e A₂ := h1.defeqDF henv hΓ h2 + +theorem IsDefEqU.trans (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (h1 : env.IsDefEqU U Γ e₁ e₂) (h2 : env.IsDefEqU U Γ e₂ e₃) : + env.IsDefEqU U Γ e₁ e₃ := h1.imp fun _ h1 => let ⟨_, h2⟩ := h2; h1.trans_l henv hΓ h2 + +set_option warn.sorry false in +variable! (henv : VEnv.WF env) (hΓ : OnCtx Γ' (env.IsType U)) in +theorem IsDefEqU.weakN_iff (W : Ctx.LiftN n k Γ Γ') : + env.IsDefEqU U Γ' (e1.liftN n k) (e2.liftN n k) ↔ env.IsDefEqU U Γ e1 e2 := by + refine ⟨fun h => have := henv; have := hΓ; sorry, fun h => h.weakN henv W⟩ + +variable! (henv : VEnv.WF env) (hΓ : OnCtx Γ' (env.IsType U)) in +theorem _root_.Ix.Theory.Named.VExpr.WF.weakN_iff (W : Ctx.LiftN n k Γ Γ') : + VExpr.WF env U Γ' (e.liftN n k) ↔ VExpr.WF env U Γ e := IsDefEqU.weakN_iff henv hΓ W + +theorem IsDefEq.skips (henv : VEnv.WF env) (hΓ : OnCtx Γ' (env.IsType U)) + (W : Ctx.LiftN n k Γ Γ') + (H : env.IsDefEq U Γ' e₁ e₂ A) (h1 : e₁.Skips n k) (h2 : e₂.Skips n k) : + ∃ B, env.IsDefEq U Γ' e₁ e₂ B ∧ B.Skips n k := by + obtain ⟨e₁, rfl⟩ := VExpr.skips_iff_exists.1 h1 + obtain ⟨e₂, rfl⟩ := VExpr.skips_iff_exists.1 h2 + have ⟨_, H⟩ := (IsDefEqU.weakN_iff henv hΓ W).1 ⟨_, H⟩ + exact ⟨_, H.weakN henv W, .liftN⟩ + +variable! (henv : VEnv.WF env) (hΓ' : OnCtx Γ' (env.IsType U)) (hΓ : OnCtx Γ (env.IsType U)) in +theorem IsDefEq.weakN_iff' (W : Ctx.LiftN n k Γ Γ') : + env.IsDefEq U Γ' (e1.liftN n k) (e2.liftN n k) (A.liftN n k) ↔ env.IsDefEq U Γ e1 e2 A := by + refine ⟨fun h => ?_, fun h => h.weakN henv W⟩ + have ⟨_, H⟩ := (IsDefEqU.weakN_iff henv hΓ' W).1 ⟨_, h⟩ + refine IsDefEqU.defeqDF henv hΓ ?_ H + exact (IsDefEqU.weakN_iff henv hΓ' W).1 <| (H.weakN henv W).uniqU henv hΓ' h.symm + +variable! (henv : VEnv.WF env) in +theorem _root_.Ix.Theory.Named.OnCtx.weakN_inv + (W : Ctx.LiftN n k Γ Γ') (H : OnCtx Γ' (env.IsType U)) : OnCtx Γ (env.IsType U) := by + induction W with + | zero As h => + clear h + induction As with + | nil => exact H + | cons A As ih => exact ih H.1 + | succ W ih => + let ⟨H1, _, H2⟩ := H + exact ⟨ih H1, _, (IsDefEq.weakN_iff' henv H1 (ih H1) W).1 H2⟩ + +variable! (henv : VEnv.WF env) (hΓ' : OnCtx Γ' (env.IsType U)) in +theorem IsDefEq.weakN_iff (W : Ctx.LiftN n k Γ Γ') : + env.IsDefEq U Γ' (e1.liftN n k) (e2.liftN n k) (A.liftN n k) ↔ env.IsDefEq U Γ e1 e2 A := + IsDefEq.weakN_iff' henv hΓ' (hΓ'.weakN_inv henv W) W + +variable! (henv : VEnv.WF env) (hΓ' : OnCtx Γ' (env.IsType U)) in +theorem HasType.weakN_iff (W : Ctx.LiftN n k Γ Γ') : + env.HasType U Γ' (e.liftN n k) (A.liftN n k) ↔ env.HasType U Γ e A := + IsDefEq.weakN_iff henv hΓ' W + +variable! (henv : VEnv.WF env) (hΓ' : OnCtx Γ' (env.IsType U)) in +theorem IsType.weakN_iff (W : Ctx.LiftN n k Γ Γ') : + env.IsType U Γ' (A.liftN n k) ↔ env.IsType U Γ A := + exists_congr fun _ => HasType.weakN_iff henv hΓ' W (A := .sort _) + +variable! (henv : VEnv.WF env) (hΓ' : OnCtx Γ' (env.IsType U)) in +theorem HasType.skips (W : Ctx.LiftN n k Γ Γ') + (h1 : env.HasType U Γ' e A) (h2 : e.Skips n k) : ∃ B, env.HasType U Γ' e B ∧ B.Skips n k := + IsDefEq.skips henv hΓ' W h1 h2 h2 + +variable! (henv : VEnv.WF env) (hΓ' : OnCtx Γ' (env.IsType U)) in +theorem IsDefEqU.weak'_iff (W : Ctx.Lift' l Γ Γ') : + env.IsDefEqU U Γ' (e1.lift' l) (e2.lift' l) ↔ env.IsDefEqU U Γ e1 e2 := by + generalize e : l.depth = n + induction n generalizing l Γ' with + | zero => simp [VExpr.lift'_depth_zero e, W.depth_zero e] + | succ n ih => + obtain ⟨l, k, rfl, rfl⟩ := Lift.depth_succ e + have ⟨Γ₁, W1, W2⟩ := W.of_cons_skip + rw [Lift.consN_skip_eq, VExpr.lift'_comp, VExpr.lift'_comp, + ← Lift.skipN_one, VExpr.lift'_consN_skipN, VExpr.lift'_consN_skipN, + weakN_iff henv hΓ' W2, ih (hΓ'.weakN_inv henv W2) W1 Lift.depth_consN] + +variable! (henv : VEnv.WF env) (hΓ' : OnCtx Γ' (env.IsType U)) in +theorem IsDefEq.weak'_iff (W : Ctx.Lift' l Γ Γ') : + env.IsDefEq U Γ' (e1.lift' l) (e2.lift' l) (A.lift' l) ↔ env.IsDefEq U Γ e1 e2 A := by + generalize e : l.depth = n + induction n generalizing l Γ' with + | zero => simp [VExpr.lift'_depth_zero e, W.depth_zero e] + | succ n ih => + obtain ⟨l, k, rfl, rfl⟩ := Lift.depth_succ e + have ⟨Γ₁, W1, W2⟩ := W.of_cons_skip + rw [Lift.consN_skip_eq, VExpr.lift'_comp, VExpr.lift'_comp, VExpr.lift'_comp, + ← Lift.skipN_one, VExpr.lift'_consN_skipN, VExpr.lift'_consN_skipN, VExpr.lift'_consN_skipN, + weakN_iff henv hΓ' W2, ih (hΓ'.weakN_inv henv W2) W1 Lift.depth_consN] + +variable! (henv : VEnv.WF env) (hΓ' : OnCtx Γ' (env.IsType U)) in +theorem HasType.weak'_iff (W : Ctx.Lift' l Γ Γ') : + env.HasType U Γ' (e.lift' l) (A.lift' l) ↔ env.HasType U Γ e A := + IsDefEq.weak'_iff henv hΓ' W + +variable! (henv : VEnv.WF env) (hΓ' : OnCtx Γ' (env.IsType U)) in +theorem IsType.weak'_iff (W : Ctx.Lift' l Γ Γ') : + env.IsType U Γ' (e.lift' l) ↔ env.IsType U Γ e := + exists_congr fun _ => HasType.weak'_iff henv hΓ' W (A := .sort _) + +variable! (henv : VEnv.WF env) (hΓ : OnCtx Γ' (env.IsType U)) in +theorem _root_.Ix.Theory.Named.VExpr.WF.weak'_iff (W : Ctx.Lift' l Γ Γ') : + VExpr.WF env U Γ' (e.lift' l) ↔ VExpr.WF env U Γ e := IsDefEqU.weak'_iff henv hΓ W + +/-! ### Application-spine weakening and inversion -/ + +/-- General context weakening for an application spine. -/ +theorem SpineWF.weak' {env : VEnv} (henv : env.Ordered) + {U : Nat} {lift : Lift} {Γ Γ' : List VExpr} + (W : Ctx.Lift' lift Γ Γ') : + ∀ {es : List VExpr} {A B : VExpr}, env.SpineWF U Γ A es B → + env.SpineWF U Γ' (A.lift' lift) + (es.map fun e => e.lift' lift) (B.lift' lift) := by + intro es + induction es with + | nil => + intro A B h + cases h + exact .nil + | cons e es ih => + intro A B h + obtain ⟨A₁, A₂, rfl, he, hrest⟩ := h.cons_inv + refine .cons (he.weak' henv W) ?_ + have weakened := ih hrest + rwa [VExpr.lift'_inst_hi] at weakened + +/-- Invert weakening of every component of an application-spine judgment +when the enlarged context is well formed. -/ +theorem SpineWF.weakN_inv {env : VEnv} {U n k : Nat} {Γ Γ' : List VExpr} + (henv : env.WF) (hΓ' : OnCtx Γ' (env.IsType U)) + (W : Ctx.LiftN n k Γ Γ') : + ∀ {es : List VExpr} {A B : VExpr}, + env.SpineWF U Γ' (A.liftN n k) + (es.map fun e => e.liftN n k) (B.liftN n k) → + env.SpineWF U Γ A es B := by + intro es + induction es with + | nil => + intro A B h + have hab := VExpr.liftN_inj.1 h.nil_inv + subst B + exact .nil + | cons e es ih => + intro A B h + obtain ⟨A₁', A₂', sourceEq, he, hrest⟩ := h.cons_inv + cases A with + | bvar index => cases sourceEq + | sort level => cases sourceEq + | const name levels => cases sourceEq + | app fn argument => cases sourceEq + | lam domain body => cases sourceEq + | forallE A₁ A₂ => + injection sourceEq with domainEq bodyEq + subst A₁' + subst A₂' + refine .cons ((HasType.weakN_iff henv hΓ' W).1 he) ?_ + rw [← VExpr.liftN_inst_hi] at hrest + exact ih hrest + +/-- Invert a general context lift componentwise across an application-spine +judgment. -/ +theorem SpineWF.weak'_inv {env : VEnv} {U : Nat} {lift : Lift} + {Γ Γ' : List VExpr} + (henv : env.WF) (hΓ' : OnCtx Γ' (env.IsType U)) + (W : Ctx.Lift' lift Γ Γ') : + ∀ {es : List VExpr} {A B : VExpr}, + env.SpineWF U Γ' (A.lift' lift) + (es.map fun e => e.lift' lift) (B.lift' lift) → + env.SpineWF U Γ A es B := by + intro es + induction es with + | nil => + intro A B h + have hab := VExpr.lift'_inj.1 h.nil_inv + subst B + exact .nil + | cons e es ih => + intro A B h + obtain ⟨A₁', A₂', sourceEq, he, hrest⟩ := h.cons_inv + cases A with + | bvar index => cases sourceEq + | sort level => cases sourceEq + | const name levels => cases sourceEq + | app fn argument => cases sourceEq + | lam domain body => cases sourceEq + | forallE A₁ A₂ => + injection sourceEq with domainEq bodyEq + subst A₁' + subst A₂' + refine .cons ((HasType.weak'_iff henv hΓ' W).1 he) ?_ + rw [← VExpr.lift'_inst_hi] at hrest + exact ih hrest + +variable! (henv : VEnv.WF env) in +theorem _root_.Ix.Theory.Named.OnCtx.weak'_inv + (W : Ctx.Lift' ρ Γ Γ') (H : OnCtx Γ' (env.IsType U)) : OnCtx Γ (env.IsType U) := by + generalize e : ρ.depth = n + induction n generalizing ρ Γ' with + | zero => simp [W.depth_zero e, H] + | succ n ih => + obtain ⟨l, k, rfl, rfl⟩ := Lift.depth_succ e + have ⟨Γ₁, W1, W2⟩ := W.of_cons_skip + exact ih W1 (.weakN_inv henv W2 H) (by simp) diff --git a/Ix/Theory/Named/VDecl.lean b/Ix/Theory/Named/VDecl.lean new file mode 100644 index 000000000..f18cb5ff9 --- /dev/null +++ b/Ix/Theory/Named/VDecl.lean @@ -0,0 +1,37 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.VEnv + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named + +structure VConstVal extends VConstant where + name : Name + +structure VDefVal extends VConstVal where + value : VExpr + +def VDefVal.toDefEq (v : VDefVal) : VDefEq := + ⟨v.uvars, .const v.name (VLevel.params v.uvars), v.value, v.type⟩ + +structure VInductiveType extends VConstVal where + ctors : List VConstVal + +structure VInductDecl where + uvars : Nat + nparams : Nat + types : List VInductiveType + +inductive VDecl where + | axiom (_ : VConstVal) + | def (_ : VDefVal) + | opaque (_ : VDefVal) + | example (_ : VDefVal) + | quot + | induct (_ : VInductDecl) + | mutualDef (_ : List VDefVal) diff --git a/Ix/Theory/Named/VEnv.lean b/Ix/Theory/Named/VEnv.lean new file mode 100644 index 000000000..2bcff284c --- /dev/null +++ b/Ix/Theory/Named/VEnv.lean @@ -0,0 +1,214 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.VExpr + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named + +structure VConstant where + uvars : Nat + type : VExpr + +structure VDefEq where + uvars : Nat + lhs : VExpr + rhs : VExpr + type : VExpr + +/-- Syntax of one registered nonrecursive-structure eta rule. + +The projector family is fixed by the checked structure artifact. Its three +naturality fields are syntactic equations, not semantic assumptions; they +are exactly what weakening and substitution need in order to reconstruct the +same registered eta redex. -/ +structure VStructEta where + uvars : Nat + nparams : Nat + nfields : Nat + familyName : Name + familyType : VExpr + constructorName : Name + projectors : List VLevel → List VExpr → List VExpr + projectors_length : ∀ levels params, + levels.length = uvars → params.length = nparams → + (projectors levels params).length = nfields + projectors_liftN : ∀ levels params n k, + params.length = nparams → + (projectors levels params).map (fun projector => + projector.liftN n k) = + projectors levels (params.map fun param => param.liftN n k) + projectors_instN : ∀ levels params a k, + params.length = nparams → + (projectors levels params).map (fun projector => + projector.inst a k) = + projectors levels (params.map fun param => param.inst a k) + projectors_instL : ∀ levels params ls, + (projectors levels params).map (fun projector => + projector.instL ls) = + projectors (levels.map (VLevel.inst ls)) + (params.map (VExpr.instL ls)) + +namespace VStructEta + +/-- The instantiated family type governed by a structure-eta descriptor. -/ +def structureType (rule : VStructEta) (levels : List VLevel) + (params : List VExpr) : VExpr := + VExpr.appN (.const rule.familyName levels) params + +/-- Canonical projected fields of one major premise. -/ +def projectionArgs (rule : VStructEta) (levels : List VLevel) + (params : List VExpr) (major : VExpr) : List VExpr := + (rule.projectors levels params).map fun projector => .app projector major + +/-- Constructor reconstruction contracted by the primitive eta rule. -/ +def rebuild (rule : VStructEta) (levels : List VLevel) + (params : List VExpr) (major : VExpr) : VExpr := + VExpr.appN (.const rule.constructorName levels) + (params ++ rule.projectionArgs levels params major) + +@[simp] theorem structureType_liftN (rule : VStructEta) + (levels : List VLevel) (params : List VExpr) (n k : Nat) : + (rule.structureType levels params).liftN n k = + rule.structureType levels + (params.map fun param => param.liftN n k) := by + unfold structureType + rw [VExpr.liftN_appN] + rfl + +@[simp] theorem structureType_instN (rule : VStructEta) + (levels : List VLevel) (params : List VExpr) (a : VExpr) (k : Nat) : + (rule.structureType levels params).inst a k = + rule.structureType levels + (params.map fun param => param.inst a k) := by + unfold structureType + rw [VExpr.instN_appN] + rfl + +@[simp] theorem structureType_instL (rule : VStructEta) + (levels : List VLevel) (params : List VExpr) (ls : List VLevel) : + (rule.structureType levels params).instL ls = + rule.structureType (levels.map (VLevel.inst ls)) + (params.map (VExpr.instL ls)) := by + unfold structureType + rw [VExpr.instL_appN] + rfl + +@[simp] theorem projectionArgs_length (rule : VStructEta) + (levels : List VLevel) (params : List VExpr) (major : VExpr) + (hlevels : levels.length = rule.uvars) + (hparams : params.length = rule.nparams) : + (rule.projectionArgs levels params major).length = rule.nfields := by + simp [projectionArgs, + rule.projectors_length levels params hlevels hparams] + +@[simp] theorem projectionArgs_liftN (rule : VStructEta) + (levels : List VLevel) (params : List VExpr) (major : VExpr) + (hparams : params.length = rule.nparams) (n k : Nat) : + (rule.projectionArgs levels params major).map + (fun arg => arg.liftN n k) = + rule.projectionArgs levels + (params.map fun param => param.liftN n k) (major.liftN n k) := by + simpa [projectionArgs, VExpr.liftN, List.map_map, Function.comp_def] using + congrArg (List.map fun projector => + projector.app (major.liftN n k)) + (rule.projectors_liftN levels params n k hparams) + +@[simp] theorem projectionArgs_instN (rule : VStructEta) + (levels : List VLevel) (params : List VExpr) (major a : VExpr) + (hparams : params.length = rule.nparams) (k : Nat) : + (rule.projectionArgs levels params major).map + (fun arg => arg.inst a k) = + rule.projectionArgs levels + (params.map fun param => param.inst a k) (major.inst a k) := by + simpa [projectionArgs, VExpr.inst, List.map_map, Function.comp_def] using + congrArg (List.map fun projector => projector.app (major.inst a k)) + (rule.projectors_instN levels params a k hparams) + +@[simp] theorem projectionArgs_instL (rule : VStructEta) + (levels : List VLevel) (params : List VExpr) (major : VExpr) + (ls : List VLevel) : + (rule.projectionArgs levels params major).map + (fun arg => arg.instL ls) = + rule.projectionArgs (levels.map (VLevel.inst ls)) + (params.map (VExpr.instL ls)) (major.instL ls) := by + simpa [projectionArgs, VExpr.instL, List.map_map, Function.comp_def] using + congrArg (List.map fun projector => projector.app (major.instL ls)) + (rule.projectors_instL levels params ls) + +@[simp] theorem rebuild_liftN (rule : VStructEta) + (levels : List VLevel) (params : List VExpr) (major : VExpr) + (hparams : params.length = rule.nparams) (n k : Nat) : + (rule.rebuild levels params major).liftN n k = + rule.rebuild levels (params.map fun param => param.liftN n k) + (major.liftN n k) := by + unfold rebuild + rw [VExpr.liftN_appN, List.map_append, + rule.projectionArgs_liftN levels params major hparams n k] + rfl + +@[simp] theorem rebuild_instN (rule : VStructEta) + (levels : List VLevel) (params : List VExpr) (major a : VExpr) + (hparams : params.length = rule.nparams) (k : Nat) : + (rule.rebuild levels params major).inst a k = + rule.rebuild levels (params.map fun param => param.inst a k) + (major.inst a k) := by + unfold rebuild + rw [VExpr.instN_appN, List.map_append, + rule.projectionArgs_instN levels params major a hparams k] + rfl + +@[simp] theorem rebuild_instL (rule : VStructEta) + (levels : List VLevel) (params : List VExpr) (major : VExpr) + (ls : List VLevel) : + (rule.rebuild levels params major).instL ls = + rule.rebuild (levels.map (VLevel.inst ls)) + (params.map (VExpr.instL ls)) (major.instL ls) := by + unfold rebuild + rw [VExpr.instL_appN, List.map_append, + rule.projectionArgs_instL levels params major ls] + rfl + +end VStructEta + +@[ext] structure VEnv where + constants : Name → Option VConstant + defeqs : VDefEq → Prop + structEtas : VStructEta → Prop + +def VEnv.empty : VEnv where + constants _ := none + defeqs _ := False + structEtas _ := False + +instance : EmptyCollection VEnv := ⟨.empty⟩ + +def VEnv.contains (env : VEnv) (name : Name) := ∃ ci, env.constants name = some ci + +def VEnv.addConst (env : VEnv) (name : Name) (ci : VConstant) : Option VEnv := + match env.constants name with + | some _ => none + | none => some { env with constants := fun n => if name = n then some ci else env.constants n } + +def VEnv.addDefEq (env : VEnv) (df : VDefEq) : VEnv := + { env with defeqs := fun x => x = df ∨ env.defeqs x } + +/-- Register one checked structure-eta descriptor. -/ +def VEnv.addStructEta (env : VEnv) (rule : VStructEta) : VEnv := + { env with structEtas := fun x => x = rule ∨ env.structEtas x } + +structure VEnv.LE (env1 env2 : VEnv) : Prop where + constants : env1.constants n = some a → env2.constants n = some a + defeqs : env1.defeqs df → env2.defeqs df + structEtas : env1.structEtas rule → env2.structEtas rule + +instance : LE VEnv := ⟨VEnv.LE⟩ + +theorem VEnv.LE.rfl {env : VEnv} : env ≤ env := ⟨id, id, id⟩ + +theorem VEnv.LE.trans {a b c : VEnv} (h1 : a ≤ b) (h2 : b ≤ c) : a ≤ c := + ⟨h2.1 ∘ h1.1, h2.2 ∘ h1.2, h2.3 ∘ h1.3⟩ diff --git a/Ix/Theory/Named/VExpr.lean b/Ix/Theory/Named/VExpr.lean new file mode 100644 index 000000000..ad7108342 --- /dev/null +++ b/Ix/Theory/Named/VExpr.lean @@ -0,0 +1,831 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Lean +import Ix.Theory.Named.VLevel + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named +open Ix.Theory.Named + +inductive VExpr where + | bvar (deBruijnIndex : Nat) + | sort (u : VLevel) + | const (declName : Name) (us : List VLevel) + | app (fn arg : VExpr) + | lam (binderType body : VExpr) + | forallE (binderType body : VExpr) + +instance : Inhabited VExpr := ⟨.sort .zero⟩ + +def liftVar (n i : Nat) (k := 0) : Nat := if i < k then i else n + i + +theorem liftVar_lt (h : i < k) : liftVar n i k = i := if_pos h +theorem liftVar_le (h : k ≤ i) : liftVar n i k = n + i := if_neg (Nat.not_lt.2 h) + +theorem liftVar_base : liftVar n i = n + i := liftVar_le (Nat.zero_le _) +@[simp] theorem liftVar_base' : liftVar n i = i + n := Nat.add_comm .. ▸ liftVar_le (Nat.zero_le _) + +@[simp] theorem liftVar_zero : liftVar n 0 (k+1) = 0 := by simp [liftVar] +@[simp] theorem liftVar_succ : liftVar n (i+1) (k+1) = liftVar n i k + 1 := by + simp [liftVar, Nat.succ_lt_succ_iff]; split <;> simp [Nat.add_assoc] + +theorem liftVar_lt_add (self : i < k) : liftVar n i j < k + n := by + simp [liftVar] + split <;> rename_i h + · exact Nat.lt_of_lt_of_le self (Nat.le_add_right ..) + · rw [Nat.add_comm]; exact Nat.add_lt_add_right self _ + +namespace VExpr + +/-- Iterated application, with arguments ordered from left to right. -/ +def appN (f : VExpr) : List VExpr → VExpr + | [] => f + | a :: as => (f.app a).appN as + +variable (n : Nat) in +def liftN : VExpr → (k :_:= 0) → VExpr + | .bvar i, k => .bvar (liftVar n i k) + | .sort u, _ => .sort u + | .const c us, _ => .const c us + | .app fn arg, k => .app (fn.liftN k) (arg.liftN k) + | .lam ty body, k => .lam (ty.liftN k) (body.liftN (k+1)) + | .forallE ty body, k => .forallE (ty.liftN k) (body.liftN (k+1)) + +abbrev lift := liftN 1 + +@[simp] theorem liftN_zero (e : VExpr) (k : Nat) : liftN 0 e k = e := by + induction e generalizing k <;> simp [liftN, liftVar, *] + +theorem liftN'_liftN' {e : VExpr} {n1 n2 k1 k2 : Nat} (h1 : k1 ≤ k2) (h2 : k2 ≤ n1 + k1) : + liftN n2 (liftN n1 e k1) k2 = liftN (n1+n2) e k1 := by + induction e generalizing k1 k2 with simp [liftN, liftVar, Nat.add_assoc, *] + | bvar i => + split <;> rename_i h + · rw [if_pos (Nat.lt_of_lt_of_le h h1)] + · rw [if_neg (mt (fun h => ?_) h), Nat.add_left_comm] + exact (Nat.add_lt_add_iff_left ..).1 (Nat.lt_of_lt_of_le h h2) + | lam _ _ _ IH2 | forallE _ _ _ IH2 => + rw [IH2 (Nat.succ_le_succ h1) (Nat.succ_le_succ h2)] + +theorem liftN'_liftN_lo (e : VExpr) (n k : Nat) : liftN n (liftN k e) k = liftN (n+k) e := by + simpa [Nat.add_comm] using liftN'_liftN' (n1 := k) (n2 := n) (Nat.zero_le _) (Nat.le_refl _) + +theorem liftN'_liftN_hi (e : VExpr) (n1 n2 k : Nat) : + liftN n2 (liftN n1 e k) k = liftN (n1+n2) e k := + liftN'_liftN' (Nat.le_refl _) (Nat.le_add_left ..) + +theorem liftN_liftN (e : VExpr) (n1 n2 : Nat) : liftN n2 (liftN n1 e) = liftN (n1+n2) e := by + simpa using liftN'_liftN' (Nat.zero_le _) (Nat.zero_le _) + +theorem liftN_succ (e : VExpr) (n : Nat) : liftN (n+1) e = lift (liftN n e) := + (liftN_liftN ..).symm + +theorem liftN'_comm (e : VExpr) (n1 n2 k1 k2 : Nat) (h : k2 ≤ k1) : + liftN n2 (liftN n1 e k1) k2 = liftN n1 (liftN n2 e k2) (n2+k1) := by + induction e generalizing k1 k2 with + simp [liftN, liftVar, Nat.add_assoc, Nat.succ_le_succ, *] + | bvar i => + split <;> rename_i h' + · rw [if_pos (c := _ < n2 + k1)]; split + · exact Nat.lt_add_left _ h' + · exact Nat.add_lt_add_left h' _ + · have := mt (Nat.lt_of_lt_of_le · h) h' + rw [if_neg (mt (Nat.lt_of_le_of_lt (Nat.le_add_left _ n1)) this), + if_neg this, if_neg (mt (Nat.add_lt_add_iff_left ..).1 h'), Nat.add_left_comm] + +theorem lift_liftN' (e : VExpr) (k : Nat) : lift (liftN n e k) = liftN n (lift e) (k+1) := + Nat.add_comm .. ▸ liftN'_comm (h := Nat.zero_le _) .. + +theorem sizeOf_liftN (e : VExpr) (k : Nat) : sizeOf e ≤ sizeOf (liftN n e k) := by + induction e generalizing k with simp [liftN, Nat.add_assoc, Nat.add_le_add_iff_left] + | bvar => simp [liftVar]; split <;> simp [Nat.le_add_left] + | _ => rename_i ih1 ih2; exact Nat.add_le_add (ih1 _) (ih2 _) + +@[simp] theorem liftN_default (n k : Nat) : liftN n default k = default := rfl +@[simp] theorem lift_default : lift default = default := rfl + +def ClosedN : VExpr → (k :_:= 0) → Prop + | .bvar i, k => i < k + | .sort .., _ | .const .., _ => True + | .app fn arg, k => fn.ClosedN k ∧ arg.ClosedN k + | .lam ty body, k => ty.ClosedN k ∧ body.ClosedN (k+1) + | .forallE ty body, k => ty.ClosedN k ∧ body.ClosedN (k+1) + +abbrev Closed := ClosedN + +@[simp] theorem ClosedN.default : ClosedN default k := trivial + +theorem ClosedN.mono (h : k ≤ k') (self : ClosedN e k) : ClosedN e k' := by + induction e generalizing k k' with (simp [ClosedN] at self ⊢; try simp [self, *]) + | bvar i => exact Nat.lt_of_lt_of_le self h + | app _ _ ih1 ih2 => exact ⟨ih1 h self.1, ih2 h self.2⟩ + | lam _ _ ih1 ih2 | forallE _ _ ih1 ih2 => + exact ⟨ih1 h self.1, ih2 (Nat.succ_le_succ h) self.2⟩ + +theorem ClosedN.liftN_eq (self : ClosedN e k) (h : k ≤ j) : liftN n e j = e := by + induction e generalizing k j with + (simp [ClosedN] at self; simp [liftN, *]) + | bvar i => exact liftVar_lt (Nat.lt_of_lt_of_le self h) + | app _ _ ih1 ih2 => exact ⟨ih1 self.1 h, ih2 self.2 h⟩ + | lam _ _ ih1 ih2 | forallE _ _ ih1 ih2 => + exact ⟨ih1 self.1 h, ih2 self.2 (Nat.succ_le_succ h)⟩ + +theorem ClosedN.lift_eq (self : ClosedN e) : lift e = e := self.liftN_eq (Nat.zero_le _) + +protected theorem ClosedN.liftN (self : ClosedN e k) : ClosedN (e.liftN n j) (k+n) := by + induction e generalizing k j with + (simp [ClosedN] at self; simp [VExpr.liftN, ClosedN, *]) + | bvar i => exact liftVar_lt_add self + | lam _ _ _ ih2 | forallE _ _ _ ih2 => exact Nat.add_right_comm .. ▸ ih2 self.2 + +theorem ClosedN.liftN_eq_rev (self : ClosedN (liftN n e j) k) (h : k ≤ j) : liftN n e j = e := by + induction e generalizing k j with + (simp [liftN, ClosedN] at self; simp [liftN, *]) + | bvar i => + refine liftVar_lt (Nat.lt_of_lt_of_le ?_ h) + unfold liftVar at self; split at self <;> + [exact self; exact Nat.lt_of_le_of_lt (Nat.le_add_left ..) self] + | app _ _ ih1 ih2 => exact ⟨ih1 self.1 h, ih2 self.2 h⟩ + | lam _ _ ih1 ih2 | forallE _ _ ih1 ih2 => + exact ⟨ih1 self.1 h, ih2 self.2 (Nat.succ_le_succ h)⟩ + +variable (ls : List VLevel) in +def instL : VExpr → VExpr + | .bvar i => .bvar i + | .sort u => .sort (u.inst ls) + | .const c us => .const c (us.map (VLevel.inst ls)) + | .app fn arg => .app fn.instL arg.instL + | .lam ty body => .lam ty.instL body.instL + | .forallE ty body => .forallE ty.instL body.instL + +theorem ClosedN.instL : ∀ {e}, ClosedN e k → ClosedN (e.instL ls) k + | .bvar .., h | .sort .., h | .const .., h => h + | .app .., h | .lam .., h | .forallE .., h => ⟨h.1.instL, h.2.instL⟩ + +theorem ClosedN.instL_rev : ∀ {e}, ClosedN (e.instL ls) k → ClosedN e k + | .bvar .., h | .sort .., h | .const .., h => h + | .app .., h | .lam .., h | .forallE .., h => ⟨h.1.instL_rev, h.2.instL_rev⟩ + +@[simp] theorem instL_default : instL ls default = default := rfl + +@[simp] theorem instL_liftN : (liftN n e k).instL ls = liftN n (e.instL ls) k := by + cases e <;> simp [liftN, instL, instL_liftN] + +theorem instL_instL {e : VExpr} : (e.instL ls).instL ls' = e.instL (ls.map (VLevel.inst ls')) := by + cases e <;> simp [instL, instL_instL, Function.comp_def, VLevel.inst_inst] + +def LevelWF (U : Nat) : VExpr → Prop + | .bvar _ => True + | .sort l => l.WF U + | .const _ ls => ∀ l ∈ ls, l.WF U + | .app e1 e2 | .lam e1 e2 | .forallE e1 e2 => e1.LevelWF U ∧ e2.LevelWF U + +theorem LevelWF.instL_id {e : VExpr} (h : e.LevelWF U) : e.instL (VLevel.params U) = e := by + induction e <;> simp_all [instL, LevelWF, VLevel.inst_id] + case const => exact List.map_id''' _ fun _ h1 => VLevel.inst_id (h _ h1) + +theorem levelWF_liftN : (liftN n e k).LevelWF U ↔ e.LevelWF U := by + induction e generalizing k <;> simp [liftN, LevelWF, *] + +theorem LevelWF.instL (h : ∀ l ∈ ls, l.WF U) : (instL ls e).LevelWF U := by + induction e <;> simp [VExpr.instL, VLevel.WF.inst h, LevelWF, *] + +alias ⟨LevelWF.liftN_rev, LevelWF.liftN⟩ := levelWF_liftN + +def instVar (i : Nat) (e : VExpr) (k := 0) : VExpr := + if i < k then .bvar i else if i = k then liftN k e else .bvar (i - 1) + +@[simp] theorem instVar_zero : instVar 0 e = e := liftN_zero .. +@[simp] theorem instVar_upper : instVar (i+1) e = .bvar i := rfl +@[simp] theorem instVar_lower : instVar 0 e (k+1) = .bvar 0 := by simp [instVar] +@[simp] theorem instVar_succ : instVar (i+1) e (k+1) = (instVar i e k).lift := by + simp [instVar, Nat.succ_lt_succ_iff]; split <;> simp [lift, liftN] + split <;> simp [liftN_liftN, liftN] + have := Nat.lt_of_le_of_ne (Nat.not_lt.1 ‹_›) (Ne.symm ‹_›) + let i+1 := i; rfl + +theorem liftN_instVar_lo (n : Nat) (e : VExpr) (j k : Nat) (hj : k ≤ j) : + liftN n (instVar i e j) k = instVar (liftVar n i k) e (n+j) := by + simp [instVar]; split <;> rename_i h + · rw [if_pos]; · rfl + simp only [liftVar]; split <;> rename_i hk + · exact Nat.lt_add_left _ h + · exact Nat.add_lt_add_left h _ + split <;> rename_i h' + · subst i + rw [liftN'_liftN' (h1 := Nat.zero_le _) (h2 := hj), liftVar_le hj, + if_neg (by simp), if_pos rfl, Nat.add_comm] + · rw [Nat.not_lt] at h; rw [liftVar_le (Nat.le_trans hj h)] + have hk := Nat.lt_of_le_of_ne h (Ne.symm h') + let i+1 := i + have := Nat.add_lt_add_left hk n + rw [if_neg (Nat.lt_asymm this), if_neg (Nat.ne_of_gt this)] + simp only [liftN] + rw [liftVar_le (Nat.le_trans hj <| by exact Nat.le_of_lt_succ hk)]; rfl + +theorem liftN_instVar_hi (i : Nat) (e2 : VExpr) (n k j : Nat) : + liftN n (instVar i e2 j) (k+j) = instVar (liftVar n i (k+j+1)) (liftN n e2 k) j := by + simp [instVar]; split <;> rename_i h + · have := Nat.lt_add_left k h + rw [liftVar_lt <| Nat.lt_succ_of_lt this, if_pos h] + simp [liftN, liftVar_lt this] + split <;> rename_i h' + · subst i + have := Nat.le_add_left j k + simp [liftVar_lt (by exact Nat.lt_succ_of_le this)] + rw [liftN'_comm (h := Nat.zero_le _), Nat.add_comm] + · have hk := Nat.lt_of_le_of_ne (Nat.not_lt.1 h) (Ne.symm h') + let i+1 := i + simp [liftVar, Nat.succ_lt_succ_iff]; split <;> rename_i hi + · simp [liftN, liftVar_lt hi] + · have := Nat.lt_add_left n hk + rw [if_neg (Nat.lt_asymm this), if_neg (Nat.ne_of_gt this)] + simp [liftN]; rw [liftVar_le (Nat.not_lt.1 hi)] + +@[simp] theorem instL_instVar : (instVar i e k).instL ls = instVar i (e.instL ls) k := by + simp [instVar]; split <;> [skip; split] <;> simp [instL, instL_liftN] + +def inst : VExpr → VExpr → (k :_:= 0) → VExpr + | .bvar i, e, k => instVar i e k + | .sort u, _, _ => .sort u + | .const c us, _, _ => .const c us + | .app fn arg, e, k => .app (fn.inst e k) (arg.inst e k) + | .lam ty body, e, k => .lam (ty.inst e k) (body.inst e (k+1)) + | .forallE ty body, e, k => .forallE (ty.inst e k) (body.inst e (k+1)) + +theorem instL_appN (ls : List VLevel) (as : List VExpr) (f : VExpr) : + (appN f as).instL ls = appN (f.instL ls) (as.map (instL ls)) := by + induction as generalizing f with + | nil => rfl + | cons a as ih => simp [appN, instL, ih] + +theorem liftN_appN (n k : Nat) (f : VExpr) : ∀ (as : List VExpr), + (f.appN as).liftN n k = appN (f.liftN n k) (as.map (liftN n · k)) + | [] => rfl + | a :: as => by + show (VExpr.appN (f.app a) as).liftN n k = _ + rw [liftN_appN n k (f.app a) as] + rfl + +theorem instN_appN (a : VExpr) (k : Nat) (f : VExpr) : ∀ (as : List VExpr), + (f.appN as).inst a k = appN (f.inst a k) (as.map (·.inst a k)) + | [] => rfl + | e :: as => by + show (VExpr.appN (f.app e) as).inst a k = _ + rw [instN_appN a k (f.app e) as] + rfl + +@[simp] theorem inst_default : inst default e k = default := rfl + +theorem liftN_instN_lo (n : Nat) (e1 e2 : VExpr) (j k : Nat) (hj : k ≤ j) : + liftN n (e1.inst e2 j) k = (liftN n e1 k).inst e2 (n+j) := by + induction e1 generalizing k j with + simp [liftN, inst, instVar, Nat.add_le_add_iff_right, *] + | bvar i => apply liftN_instVar_lo (hj := hj) + | _ => rfl + +theorem liftN_instN_hi (e1 e2 : VExpr) (n k j : Nat) : + liftN n (e1.inst e2 j) (k+j) = (liftN n e1 (k+j+1)).inst (liftN n e2 k) j := by + induction e1 generalizing j with simp [liftN, inst, instVar, *] + | bvar i => apply liftN_instVar_hi + | _ => rename_i IH; apply IH + +theorem liftN_inst_hi (e1 e2 : VExpr) (n k : Nat) : + liftN n (e1.inst e2) k = (liftN n e1 (k+1)).inst (liftN n e2 k) := liftN_instN_hi .. + +theorem lift_instN_lo (e1 e2 : VExpr) : lift (e1.inst e2 k) = (lift e1).inst e2 (k + 1) := + Nat.add_comm .. ▸ liftN_instN_lo (hj := Nat.zero_le _) .. + +theorem lift_inst_hi (e1 e2 : VExpr) : lift (e1.inst e2) = (liftN 1 e1 1).inst (lift e2) := + liftN_instN_hi .. + +theorem inst_liftN (e1 e2 : VExpr) : (liftN 1 e1 k).inst e2 k = e1 := by + induction e1 generalizing k with simp [liftN, inst, *] + | bvar i => + simp only [liftVar, instVar, Nat.add_comm 1]; split <;> [rfl; rename_i h] + rw [if_neg (mt (Nat.lt_of_le_of_lt (Nat.le_succ _)) h), + if_neg (mt (by rintro rfl; apply Nat.lt_succ_self) h)]; rfl + +theorem inst_liftN' (e1 e2 : VExpr) : (liftN (n+1) e1 k).inst e2 k = liftN n e1 k := by + rw [← liftN'_liftN_hi, inst_liftN] + +theorem inst_lift (e1 e2 : VExpr) : (lift e1).inst e2 = e1 := inst_liftN .. + +protected theorem LevelWF.inst + (h1 : e1.LevelWF U) (h2 : e2.LevelWF U) : (inst e1 e2 k).LevelWF U := by + induction e1 generalizing k <;> simp_all [inst, instVar, LevelWF] + case bvar => split <;> [trivial; split <;> [exact h2.liftN; trivial]] + +def unliftN (e : VExpr) (n k : Nat) : VExpr := + match n with + | 0 => e + | n+1 => unliftN (e.inst default k) n k + +@[simp] theorem unliftN_liftN : unliftN (liftN n e k) n k = e := by + induction n <;> simp [unliftN, inst_liftN', *] + +theorem unliftN_add : unliftN e (n1+n2) k = unliftN (unliftN e n1 k) n2 k := by + induction n1 generalizing e <;> simp [unliftN, Nat.succ_add, *] + +theorem unliftN_succ' : unliftN e (n+1) k = (unliftN e n k).inst default k := by + rw [unliftN_add]; rfl + +theorem liftN_unliftN_hi (h : k2 ≤ k1) : + liftN n1 (unliftN e n2 k2) k1 = unliftN (liftN n1 e (k1+n2)) n2 k2 := by + obtain ⟨k1, rfl⟩ := Nat.le_iff_exists_add'.1 h + induction n2 generalizing e with simp [unliftN] + | succ n2 ih => + rw [ih, Nat.add_right_comm, liftN_instN_hi e default n1 (k1+n2) k2, + Nat.add_right_comm k1]; rfl + +def Skips (e : VExpr) (n k : Nat) : Prop := liftN n (unliftN e n k) k = e + +protected theorem Skips.liftN : Skips (liftN n e k) n k := by simp [Skips] + +theorem skips_iff_exists : Skips e n k ↔ ∃ e', e = liftN n e' k := + ⟨fun h => ⟨_, h.symm⟩, fun ⟨_, h⟩ => h ▸ .liftN⟩ + +theorem Skips.zero : Skips e 0 k := by simp [Skips, unliftN] + +theorem liftN_inj : liftN n e1 k = liftN n e2 k ↔ e1 = e2 := + ⟨fun H => by rw [← unliftN_liftN (e := e1), H, unliftN_liftN], (· ▸ rfl)⟩ + +theorem liftVar_inj : liftVar n i k = liftVar n i' k ↔ i = i' := by + simpa [liftN] using @liftN_inj n (.bvar i) k (.bvar i') + +theorem Skips.of_liftN_hi (self : (liftN n1 e k1).Skips n2 k2) (h : n2 + k2 ≤ k1) : + e.Skips n2 k2 := by + obtain ⟨k1, rfl⟩ := Nat.le_iff_exists_add'.1 h + rwa [Skips, Nat.add_comm n2, ← Nat.add_assoc, ← liftN_unliftN_hi (Nat.le_add_left ..), + liftN'_comm (h := Nat.le_add_left ..), Nat.add_comm, liftN_inj] at self + +theorem skips_add : Skips e (n1+n2) k ↔ ∃ e', Skips e' n1 k ∧ e = liftN n2 e' k := by + simp [skips_iff_exists, ← liftN'_liftN_hi] + exact ⟨fun ⟨_, h⟩ => ⟨_, ⟨_, rfl⟩, h⟩, fun ⟨_, ⟨_, rfl⟩, h⟩ => ⟨_, h⟩⟩ + +def Skips' (n : Nat) : VExpr → (k :_:= 0) → Prop + | .bvar i, k => i < k + n → i < k + | .sort .., _ | .const .., _ => True + | .app fn arg, k => fn.Skips' n k ∧ arg.Skips' n k + | .lam ty body, k => ty.Skips' n k ∧ body.Skips' n (k+1) + | .forallE ty body, k => ty.Skips' n k ∧ body.Skips' n (k+1) + +theorem skips_iff : Skips e n k ↔ Skips' n e k := by + induction n generalizing e with + | zero => simp [Skips, unliftN]; induction e generalizing k <;> simp [Skips', *] + | succ n ih => + simp [skips_add, ih]; clear ih + induction e generalizing k with + | bvar i => + refine ⟨fun ⟨e', h1, h2⟩ => ?_, fun h => ?_⟩ + · cases e' <;> cases h2; simp [Skips', liftVar]; split + · intro; assumption + · next h2 => + rw [Nat.add_comm, ← Nat.add_assoc, Nat.succ_lt_succ_iff] + exact fun h => h2.elim (h1 h) + · simp [Skips'] at h + if h' : i < k + n + 1 then + exact ⟨.bvar i, fun _ => h h', by simp [liftN, liftVar, h h']⟩ + else + have := Nat.not_lt.1 h' + let i+1 := i; rw [Nat.add_lt_add_iff_right] at h' + have := mt (Nat.lt_of_lt_of_le · (Nat.le_add_right ..)) h' + exact ⟨.bvar i, h'.elim, by simp [liftN, liftVar]; rw [if_neg this, Nat.add_comm]⟩ + | sort u => + refine ⟨fun ⟨e', h1, h2⟩ => ?_, fun _ => ⟨.sort u, by simp [Skips', liftN]⟩⟩ + cases e' <;> cases h2; simp [Skips'] + | const c ls => + refine ⟨fun ⟨e', h1, h2⟩ => ?_, fun _ => ⟨.const c ls, by simp [Skips', liftN]⟩⟩ + cases e' <;> cases h2; simp [Skips'] + | app f a fIH aIH => + simp [Skips', ← fIH, ← aIH]; refine ⟨fun ⟨e', h1, h2⟩ => ?_, ?_⟩ + · cases e' <;> cases h2; exact ⟨⟨_, h1.1, rfl⟩, ⟨_, h1.2, rfl⟩⟩ + · rintro ⟨⟨e1, h1, rfl⟩, ⟨e2, h2, rfl⟩⟩; exact ⟨.app .., ⟨h1, h2⟩, rfl⟩ + | forallE f a fIH aIH => + simp [Skips', ← fIH, ← aIH]; refine ⟨fun ⟨e', h1, h2⟩ => ?_, ?_⟩ + · cases e' <;> cases h2; exact ⟨⟨_, h1.1, rfl⟩, ⟨_, h1.2, rfl⟩⟩ + · rintro ⟨⟨e1, h1, rfl⟩, ⟨e2, h2, rfl⟩⟩; exact ⟨.forallE .., ⟨h1, h2⟩, rfl⟩ + | lam f a fIH aIH => + simp [Skips', ← fIH, ← aIH]; refine ⟨fun ⟨e', h1, h2⟩ => ?_, ?_⟩ + · cases e' <;> cases h2; exact ⟨⟨_, h1.1, rfl⟩, ⟨_, h1.2, rfl⟩⟩ + · rintro ⟨⟨e1, h1, rfl⟩, ⟨e2, h2, rfl⟩⟩; exact ⟨.lam .., ⟨h1, h2⟩, rfl⟩ + +theorem of_liftN_eq_liftN (h : liftN n1 e1 (k1+n2+k2) = liftN n2 e2 k2) : + ∃ e', e1 = liftN n2 e' k2 ∧ e2 = liftN n1 e' (k1+k2) := by + have : (liftN n1 e1 (k1+n2+k2)).Skips n2 k2 := h ▸ .liftN + obtain ⟨e', rfl⟩ := skips_iff_exists.1 <| + this.of_liftN_hi (Nat.add_assoc .. ▸ Nat.le_add_left ..) + refine ⟨e', rfl, ?_⟩ + rw [← liftN_inj, ← h, liftN'_comm (n1 := n1) (h := Nat.le_add_left ..), + Nat.add_left_comm, Nat.add_assoc] + +@[simp] theorem instL_instN {e1 e2 : VExpr} : + (e1.inst e2 k).instL ls = (e1.instL ls).inst (e2.instL ls) k := by + induction e1 generalizing k <;> simp [instL, inst, *] + +theorem instL_unliftN : instL ls (unliftN e n k) = unliftN (instL ls e) n k := by + induction n generalizing e with simp [unliftN] + | succ _ ih => rw [ih, instL_instN]; rfl + +theorem Skips.of_instL (self : (instL ls e).Skips n k) : e.Skips n k := by + rw [skips_iff] at self ⊢ + induction e generalizing k <;> simp_all [Skips', instL] + +theorem of_liftN_eq_instL (h : liftN n e1 k = instL ls e2) : + ∃ e', e1 = instL ls e' ∧ e2 = liftN n e' k := by + have : (instL ls e2).Skips n k := h ▸ .liftN + obtain ⟨e', rfl⟩ := skips_iff_exists.1 this.of_instL + refine ⟨e', ?_, rfl⟩ + rw [← liftN_inj, h, instL_liftN] + +theorem ClosedN.instN_eq (self : ClosedN e1 k) (h : k ≤ j) : e1.inst e2 j = e1 := by + conv => lhs; rw [← self.liftN_eq (n := 1) h] + rw [inst_liftN] + +theorem ClosedN.instN (h1 : ClosedN e (k+j+1)) (h2 : ClosedN e2 k) : ClosedN (e.inst e2 j) (k+j) := + match e, h1 with + | .bvar i, h => by + simp [inst, instVar]; split <;> rename_i h1 + · exact Nat.lt_of_lt_of_le h1 (Nat.le_add_left ..) + split <;> rename_i h1' + · exact h2.liftN + · have hk := Nat.lt_of_le_of_ne (Nat.not_lt.1 h1) (Ne.symm h1') + let i+1 := i + exact Nat.lt_of_succ_lt_succ h + | .sort .., h | .const .., h => h + | .app .., h => ⟨h.1.instN h2, h.2.instN h2⟩ + | .lam .., h | .forallE .., h => ⟨h.1.instN h2, h.2.instN (j := j+1) h2⟩ + +theorem ClosedN.inst (h1 : ClosedN e (k+1)) (h2 : ClosedN e2 k) : ClosedN (e.inst e2) k := + h1.instN (j := 0) h2 + +theorem inst_instVar_hi (i : Nat) (e2 e3 : VExpr) (k j : Nat) : + inst (instVar i e2 k) e3 (j+k) = (instVar i e3 (j+k+1)).inst (e2.inst e3 j) k := by + simp [instVar]; split <;> rename_i h + · simp [Nat.lt_succ_of_lt, inst, instVar, h, Nat.lt_of_lt_of_le h (Nat.le_add_left k j)] + split <;> rename_i h' + · subst i + simp [Nat.lt_succ_of_le, Nat.le_add_left, inst, instVar] + rw [liftN_instN_lo k e2 e3 j _ (Nat.zero_le _), Nat.add_comm] + · have hk := Nat.lt_of_le_of_ne (Nat.not_lt.1 h) (Ne.symm h') + let i+1 := i + simp [inst, instVar]; split <;> rename_i hi + · simp [inst, instVar, h, h'] + split <;> rename_i hi' + · subst i + suffices liftN (j+k+1) .. = _ by rw [this]; exact (inst_liftN ..).symm + exact (liftN'_liftN' (Nat.zero_le _) (Nat.le_add_left k j)).symm + · have hk := Nat.lt_of_le_of_ne (Nat.not_lt.1 hi) (Ne.symm hi') + let i+1 := i + simp [inst, instVar] + have := Nat.lt_of_le_of_lt (Nat.le_add_left ..) hk + rw [if_neg (Nat.lt_asymm this), if_neg (Nat.ne_of_gt this)] + +theorem inst_inst_hi (e1 e2 e3 : VExpr) (k j : Nat) : + inst (e1.inst e2 k) e3 (j+k) = (e1.inst e3 (j+k+1)).inst (e2.inst e3 j) k := by + induction e1 generalizing k with simp [inst, instVar, *] + | bvar i => apply inst_instVar_hi + | _ => rename_i IH; apply IH + +theorem inst0_inst_hi (e1 e2 e3 : VExpr) (j : Nat) : + inst (e1.inst e2) e3 j = (e1.inst e3 (j+1)).inst (e2.inst e3 j) := inst_inst_hi .. + +theorem inst_instVar_lo (i : Nat) (e2 e3 : VExpr) (k j : Nat) : + inst (instVar i e2 (k+j+1)) e3 j = + (instVar i (e3.liftN 1 k) j).inst e2 (k+j) := by + simp [instVar]; split <;> rename_i h + · split <;> rename_i h1 + · simp only [inst, instVar, h1, reduceIte] + rw [if_pos (Nat.lt_of_lt_of_le h1 (Nat.le_add_left ..))] + split <;> rename_i h1' + · subst i + simp [inst, instVar]; rw [liftN'_comm (h := Nat.zero_le _), Nat.add_comm] + exact (inst_liftN ..).symm + · have hj := Nat.lt_of_le_of_ne (Nat.not_lt.1 h1) (Ne.symm h1') + let i+1 := i + simp [inst, instVar, h1, h1', Nat.lt_of_succ_lt_succ h] + split <;> rename_i h' + · subst i + have := Nat.lt_succ_of_le (Nat.le_add_left j k) + rw [if_neg (Nat.lt_asymm this), if_neg (Nat.ne_of_gt this)] + simp [inst, instVar] + suffices liftN (k+j+1) .. = _ by rw [this]; exact inst_liftN .. + exact (liftN'_liftN' (Nat.zero_le _) (Nat.le_add_left j k)).symm + · have hk := Nat.lt_of_le_of_ne (Nat.not_lt.1 h) (Ne.symm h') + let i+1 := i + have hk := Nat.lt_of_add_lt_add_right hk + simp [inst, instVar] + have := Nat.lt_of_le_of_lt (Nat.le_add_left ..) hk + rw [if_neg (Nat.lt_asymm this), if_neg (Nat.ne_of_gt this)] + have := Nat.lt_succ_of_lt this + rw [if_neg (Nat.lt_asymm this), if_neg (Nat.ne_of_gt this)] + simp [inst, instVar] + rw [if_neg (Nat.lt_asymm hk), if_neg (Nat.ne_of_gt hk)] + +theorem inst_inst_lo (e1 e2 e3 : VExpr) (k j : Nat) : + inst (e1.inst e2 (k+j+1)) e3 j = + (e1.inst (e3.liftN 1 k) j).inst e2 (k+j) := by + induction e1 generalizing j with simp [inst, instVar, *] + | bvar i => apply inst_instVar_lo + | _ => rename_i IH; exact IH (j+1) + +theorem instN_bvar0 (e : VExpr) (k : Nat) : + inst (e.liftN 1 (k+1)) (.bvar 0) k = e := by + induction e generalizing k with simp [liftN, inst, *] + | bvar i => induction i generalizing k <;> cases k <;> simp [*, lift, liftN] + +end VExpr + +inductive Lift : Type where + | refl : Lift + | skip : Lift → Lift + | cons : Lift → Lift + +namespace Lift + +@[simp] def skipN (l : Lift) : Nat → Lift + | 0 => l + | n+1 => .skip (skipN l n) + +theorem skipN_one : skipN l 1 = .skip l := rfl + +theorem skipN_skipN : skipN (skipN l n) k = skipN l (n + k) := by induction k <;> simp [*] + +@[simp] def consN (l : Lift) : Nat → Lift + | 0 => l + | k+1 => .cons (consN l k) + +theorem consN_consN : consN (.consN l a) b = .consN l (a + b) := by + induction b <;> simp [*] + +@[simp] def comp (l₁ l₂ : Lift) : Lift := + match l₂, l₁ with + | .refl, l₁ => l₁ + | .skip l₂, l₁ => .skip (l₁.comp l₂) + | .cons l₂, .refl => .cons l₂ + | .cons l₂, .skip l₁ => .skip (l₁.comp l₂) + | .cons l₂, .cons l₁ => .cons (l₁.comp l₂) + +@[simp] theorem refl_comp : comp refl l = l := by induction l <;> simp [*] + +theorem consN_comp : consN (.comp l₁ l₂) n = .comp (.consN l₁ n) (.consN l₂ n) := by + induction n <;> simp [*] + +@[simp] def dom : Lift → Nat + | .refl => 0 + | .skip l => l.dom + | .cons l => l.dom + 1 + +@[simp] def size : Lift → Nat + | .refl => 0 + | .skip l => l.size + 1 + | .cons l => l.size + 1 + +@[simp] def depth : Lift → Nat + | .refl => 0 + | .skip l => l.depth + 1 + | .cons l => l.depth + +theorem dom_add_depth : dom l + depth l = size l := by induction l <;> simp! <;> omega + +theorem depth_comp : depth (.comp l₁ l₂) = l₁.depth + l₂.depth := + match l₂, l₁ with + | .refl, _ => rfl + | .skip _, _ => congrArg Nat.succ depth_comp + | .cons _, .refl => (Nat.zero_add _).symm + | .cons _, .skip _ => (congrArg Nat.succ depth_comp).trans (Nat.succ_add ..).symm + | .cons l₂, .cons l₁ => @depth_comp l₁ l₂ + +@[simp] theorem depth_consN : depth (.consN l n) = l.depth := by induction n <;> simp [*] + +@[simp] theorem depth_skipN : depth (.skipN l n) = l.depth + n := by + induction n <;> simp [Nat.add_assoc, *] + +theorem consN_skip_eq : consN (skip l) k = comp (consN l k) (consN (skip refl) k) := by + rw [← consN_comp]; rfl + +theorem depth_succ (H : l.depth = n + 1) : + ∃ l' k, depth l' = n ∧ l = consN (.skip l') k := by + match l with + | .skip l => cases H; exact ⟨l, 0, rfl, rfl⟩ + | .cons l => + obtain ⟨l, k, rfl, ⟨⟩⟩ := depth_succ (l := l) H + exact ⟨l, k+1, rfl, rfl⟩ + +theorem depth_succ' (H : l.depth = n + 1) : + ∃ l' k, depth l' = n ∧ l = comp l' (.consN (.skip refl) k) := by + let ⟨l', k, h1, h2⟩ := depth_succ H + exact ⟨.consN l' k, k, by simp [h1], by rwa [← consN_skip_eq]⟩ + +theorem comp_skipN : comp l₁ (skipN l₂ k) = skipN (comp l₁ l₂) k := by + induction k <;> simp [*] + +theorem skipN_comp_consN : comp (skipN l₁ k) (consN l₂ k) = skipN (comp l₁ l₂) k := by + induction k <;> simp [*] + +@[simp] protected def liftVar : Lift → Nat → Nat + | .refl, n => n + | .skip l, n => l.liftVar n + 1 + | .cons _, 0 => 0 + | .cons l, n+1 => l.liftVar n + 1 + +theorem liftVar_comp : (comp l₁ l₂).liftVar n = l₂.liftVar (l₁.liftVar n) := by + induction l₂ generalizing l₁ n <;> [skip; skip; cases l₁ <;> [skip; skip; cases n]] <;> simp [*] + +theorem liftVar_skipN : (skipN l n).liftVar i = l.liftVar i + n := by + induction n generalizing i with + | zero => rfl + | succ _ ih => simp [ih]; rfl + +theorem liftVar_consN_skipN : (consN (skipN refl n) k).liftVar i = liftVar n i k := by + induction k generalizing i with + | zero => simp [liftVar_skipN] + | succ k ih => + cases i with simp [liftVar, Nat.succ_lt_succ_iff, ih] + | succ i => split <;> rfl + +theorem liftVar_depth_zero (H : depth l = 0) : l.liftVar n = n := by + induction l generalizing n <;> [skip; skip; cases n] <;> simp_all [depth] + +theorem le_liftVar {l : Lift} : n ≤ l.liftVar n := by + induction l generalizing n <;> [skip; skip; cases n] <;> simp_all; grind + +def inter : Lift → Lift → Lift + | refl, l | l, refl => l + | skip l₁, skip l₂ | skip l₁, cons l₂ | cons l₁, skip l₂ => skip (l₁.inter l₂) + | cons l₁, cons l₂ => cons (l₁.inter l₂) + +theorem inter_self : inter l l = l := by induction l <;> simp! [*] + +theorem inter_comm : inter l₁ l₂ = inter l₂ l₁ := by + induction l₁ generalizing l₂ <;> cases l₂ <;> simp! [*] + +theorem inter_assoc : inter (inter l₁ l₂) l₃ = inter l₁ (inter l₂ l₃) := by + induction l₁ generalizing l₂ l₃ <;> cases l₂ <;> cases l₃ <;> simp! [*] + +@[simp] def diff : Lift → Lift → Lift + | refl, _ => refl + | l, refl => l + | skip l₁, skip l₂ | cons l₁, skip l₂ => diff l₁ l₂ + | skip l₁, cons l₂ => skip (diff l₁ l₂) + | cons l₁, cons l₂ => cons (l₁.diff l₂) + +@[simp] theorem diff_refl : diff l refl = l := by cases l <;> simp! + +theorem diff_comp : comp (diff l₁ l₂) l₂ = inter l₁ l₂ := by + induction l₁ generalizing l₂ <;> cases l₂ <;> simp! [*] + +def Fixes : Nat → Lift → Prop + | 0, _ => True + | _, .refl => True + | _+1, .skip _ => False + | n+1, .cons l => Fixes n l + +theorem Fixes.zero : Fixes 0 ρ := by simp [Fixes] + +theorem Fixes.liftVar_eq {ρ : Lift} (H : ρ.Fixes k) (h2 : i < k) : ρ.liftVar i = i := by + induction ρ generalizing i k with + | refl => rfl + | skip => let k+1 := k; cases H + | cons ρ ih => + let k+1 := k + cases i with + | zero => rfl + | succ k => exact congrArg Nat.succ <| ih H (Nat.lt_of_succ_lt_succ h2) + +end Lift + +namespace VExpr + +@[simp] def lift' : VExpr → Lift → VExpr + | .bvar i, k => .bvar (k.liftVar i) + | .sort u, _ => .sort u + | .const c us, _ => .const c us + | .app fn arg, k => .app (fn.lift' k) (arg.lift' k) + | .lam ty body, k => .lam (ty.lift' k) (body.lift' k.cons) + | .forallE ty body, k => .forallE (ty.lift' k) (body.lift' k.cons) + +theorem lift'_consN_skipN : e.lift' (.consN (.skipN .refl n) k) = liftN n e k := Eq.symm <| by + induction e generalizing k <;> simp [liftN, Lift.liftVar_consN_skipN, *] + +theorem lift'_comp {e : VExpr} : e.lift' (.comp l₁ l₂) = (e.lift' l₁).lift' l₂ := Eq.symm <| by + induction e generalizing l₁ l₂ <;> simp [Lift.liftVar_comp, *] + +theorem lift'_depth_zero {e : VExpr} (H : l.depth = 0) : e.lift' l = e := by + induction e generalizing l <;> simp_all [Lift.liftVar_depth_zero] + +@[simp] theorem lift'_refl {e : VExpr} : e.lift' .refl = e := lift'_depth_zero rfl + +theorem lift_eq_lift' {e : VExpr} : e.lift = e.lift' (.skip .refl) := by + rw [lift, ← lift'_consN_skipN]; rfl + +@[simp] theorem instL_lift' : (lift' e ρ).instL ls = lift' (e.instL ls) ρ := by + cases e <;> simp [lift', instL, instL_lift'] + +theorem ClosedN.lift'_eq (self : ClosedN e k) (h : ρ.Fixes k) : lift' e ρ = e := by + induction e generalizing k ρ with (simp [ClosedN] at self; simp [*]) + | bvar i => exact h.liftVar_eq self + | app _ _ ih1 ih2 => exact ⟨ih1 self.1 h, ih2 self.2 h⟩ + | lam _ _ ih1 ih2 | forallE _ _ ih1 ih2 => exact ⟨ih1 self.1 h, ih2 self.2 h⟩ + +def Subst := Nat → VExpr + +def Subst.Depth (σ : Subst) (n n' : Nat) := ∀ i, σ (i + n') = .bvar (i + n) + +def Subst.lift (σ : Subst) : Subst + | 0 => .bvar 0 + | i+1 => (σ i).lift + +def Subst.liftN (σ : Subst) : Nat → Subst + | 0 => σ + | k+1 => (σ.liftN k).lift + +def subst : VExpr → Subst → VExpr + | .bvar i, σ => σ i + | .sort u, _ => .sort u + | .const c us, _ => .const c us + | .app fn arg, σ => .app (fn.subst σ) (arg.subst σ) + | .lam ty body, σ => .lam (ty.subst σ) (body.subst σ.lift) + | .forallE ty body, σ => .forallE (ty.subst σ) (body.subst σ.lift) + +def Subst.lift_r (σ : Subst) (ρ : Lift) : Subst := fun x => (σ x).lift' ρ +def Subst.lift_l (ρ : Lift) (σ : Subst) : Subst := fun x => σ (ρ.liftVar x) + +theorem Subst.lift_l_lift {σ : Subst} {ρ} : (σ.lift_l ρ).lift = σ.lift.lift_l ρ.cons := by + funext i; cases i <;> simp! [lift_l] + +theorem Subst.lift_r_lift {σ : Subst} {ρ} : (σ.lift_r ρ).lift = σ.lift.lift_r ρ.cons := by + funext i; cases i <;> simp! [lift, lift_r, ← lift'_comp, lift_eq_lift'] + +theorem subst_lift' {e : VExpr} : (e.lift' ρ).subst σ = subst e (.lift_l ρ σ) := by + induction e generalizing ρ σ <;> simp! [*, Subst.lift_l_lift]; rfl + +theorem lift'_subst {e : VExpr} : (e.subst σ).lift' ρ = subst e (.lift_r σ ρ) := by + induction e generalizing ρ σ <;> simp! [*, Subst.lift_r, Subst.lift_r_lift] + +def Subst.id : Subst := .bvar +def Subst.head (σ : Subst) : VExpr := σ 0 +def Subst.tail (σ : Subst) : Subst := fun n => σ (n+1) + +theorem Subst.Depth.id : Subst.id.Depth 0 0 := fun _ => rfl + +@[simp] theorem id_lift : Subst.id.lift = Subst.id := by + funext i; cases i <;> simp [Subst.id, Subst.lift, liftN] + +@[simp] theorem subst_id {e : VExpr} : e.subst .id = e := by + induction e <;> simp! [*, id_lift]; rfl + +def Subst.cons (σ : Subst) (e : VExpr) : Subst + | 0 => e + | i+1 => σ i + +abbrev Subst.one (e : VExpr) : Subst := .cons .id e + +theorem Subst.Depth.one : (Subst.one e).Depth 0 1 := .id + +def _root_.Ix.Theory.Named.Lift.inv : Lift → Subst + | .refl => .id + | .skip ρ => ρ.inv.cons default + | .cons ρ => ρ.inv.lift + +theorem lift_l_inv {ρ : Lift} : .lift_l ρ ρ.inv = Subst.id := by + funext i; simp [Subst.lift_l, Subst.id] + induction ρ generalizing i with + | refl => rfl + | skip ρ ih => simp [Lift.inv, Subst.cons, ih] + | cons ρ ih => cases i <;> simp [Lift.inv, Subst.lift, ih, lift_eq_lift'] + +theorem lift'_inj {e e' : VExpr} {ρ : Lift} : e.lift' ρ = e'.lift' ρ ↔ e = e' := + ⟨(by simpa [subst_lift', lift_l_inv] using congrArg (·.subst ρ.inv) ·), (· ▸ rfl)⟩ + +theorem instN_eq (e a : VExpr) : e.inst a k = e.subst (.liftN (.one a) k) := by + induction e generalizing k with simp_all [inst, subst, Subst.liftN] | bvar i + induction k generalizing i <;> cases i <;> simp [Subst.liftN, Subst.lift, Subst.cons, Subst.id, *] + +theorem inst_eq (e a : VExpr) : e.inst a = e.subst (.one a) := instN_eq .. + +def Subst.trunc (σ : Subst) (n n' : Nat) : Subst := + fun i => if n' ≤ i then .bvar (i - n' + n) else σ i + +theorem Subst.lift_r_comm (σ : Subst) (ρ : Lift) (H : Subst.Depth σ 0 n) : + σ.lift_r ρ = .lift_l (ρ.consN n) ((σ.lift_r ρ).trunc 0 n) := by + funext i; simp [Subst.lift_l, Subst.lift_r, Subst.trunc] + have : (ρ.consN n).liftVar i = if n ≤ i then ρ.liftVar (i-n) + n else i := by + clear H; induction n generalizing i <;> [skip; cases i] <;> simp! [*]; split <;> rfl + rw [this]; split <;> simp + have := H (i - n); rw [Nat.sub_add_cancel ‹_›] at this; simp [this] + +theorem lift_r_one (e : VExpr) (ρ : Lift) : + (Subst.one e).lift_r ρ = .lift_l ρ.cons (Subst.one (e.lift' ρ)) := by + refine (Subst.lift_r_comm (Subst.one e) ρ .one).trans ?_; congr 1 + funext i; simp [Subst.trunc] + cases i <;> simp [Subst.one, Subst.cons, Subst.lift_r, Subst.id] + +theorem lift'_inst_hi (e1 e2 : VExpr) (ρ : Lift) : + lift' (e1.inst e2) ρ = (lift' e1 ρ.cons).inst (lift' e2 ρ) := by + simp [subst_lift', lift'_subst, lift_r_one, inst_eq] diff --git a/Ix/Theory/Named/VLevel.lean b/Ix/Theory/Named/VLevel.lean new file mode 100644 index 000000000..e2c9b26bf --- /dev/null +++ b/Ix/Theory/Named/VLevel.lean @@ -0,0 +1,99 @@ +/- +Adapted for Ix: shared universe semantics and additional proof support. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.VLevel +import Ix.Theory.Named.Std.Basic + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named +export Lean (Name) +end Ix.Theory.Named + +namespace Ix.Theory.VLevel +open Ix.Theory.Named + +theorem le_trans {a b c : VLevel} (h1 : a ≤ b) (h2 : b ≤ c) : a ≤ c := + fun _ => Nat.le_trans (h1 _) (h2 _) + +theorem zero_le : zero ≤ a := fun _ => Nat.zero_le _ + +theorem le_succ : a ≤ succ a := fun _ => Nat.le_succ _ + +theorem succ_le_succ (h : a ≤ b) : succ a ≤ succ b := fun _ => Nat.succ_le_succ (h _) + +theorem le_max_left : a ≤ max a b := fun _ => Nat.le_max_left .. +theorem le_max_right : b ≤ max a b := fun _ => Nat.le_max_right .. + +theorem equiv_def' {a b : VLevel} : a ≈ b ↔ a.eval = b.eval := .rfl +theorem equiv_congr_left {a b c : VLevel} (h : a ≈ b) : a ≈ c ↔ b ≈ c := + iff_of_eq (congrArg (· = _) h) + +theorem equiv_congr_right {a b c : VLevel} (h : a ≈ b) : c ≈ a ↔ c ≈ b := + iff_of_eq (congrArg (_ = ·) h) + +theorem succ_congr_iff {a b : VLevel} : succ a ≈ succ b ↔ a ≈ b := by + simp [equiv_def, eval] + +theorem max_congr (h₁ : a₁ ≈ b₁) (h₂ : a₂ ≈ b₂) : max a₁ a₂ ≈ max b₁ b₂ := by + simp_all [equiv_def, eval] + +theorem max_comm : max a b ≈ max b a := by simp [equiv_def, eval, Nat.max_comm] + +theorem LE.max_eq_left (h : b.LE a) : max a b ≈ a := by + simp [equiv_def, eval, Nat.max_eq_left (h _)] + +theorem LE.max_eq_right (h : a.LE b) : max a b ≈ b := by + simp [equiv_def, eval, Nat.max_eq_right (h _)] + +theorem max_self : max a a ≈ a := by simp [equiv_def, eval] + +theorem zero_imax : imax zero a ≈ a := by + simp [equiv_def, eval, natIMax, eq_comm (b := 0)] + +theorem imax_zero : imax a zero ≈ zero := by simp [equiv_def, eval, natIMax] + +theorem imax_eq_zero : imax a b ≈ zero ↔ b ≈ zero := by + simp [equiv_def, eval, natIMax] + refine ⟨fun H ls => ?_, fun H ls hn => nomatch hn (H ls)⟩ + exact Decidable.byContradiction fun h => h (H ls h).2 + +def IsNeverZero (a : VLevel) : Prop := ∀ ls, a.eval ls ≠ 0 + +theorem IsNeverZero.imax_eq_max (h : IsNeverZero b) : imax a b ≈ max a b := by + simp_all [equiv_def, eval, natIMax, IsNeverZero] + +theorem id_WF : ∀ l ∈ (List.range u).map param, l.WF u := by simp [WF] + +variable (ls : List Name) in +def ofLevel : Lean.Level → Option VLevel + | .zero => return .zero + | .succ l => return .succ (← ofLevel l) + | .max l₁ l₂ => return .max (← ofLevel l₁) (← ofLevel l₂) + | .imax l₁ l₂ => return .imax (← ofLevel l₁) (← ofLevel l₂) + | .param n => + let i := ls.idxOf n + if i < ls.length then some (.param i) else none + | .mvar _ => none + +theorem WF.of_ofLevel (h : ofLevel ls l = some l') : l'.WF ls.length := by + induction l generalizing l' with simp [ofLevel, bind] at h + | zero => cases h; trivial + | succ _ ih => obtain ⟨l', h, ⟨⟩⟩ := h; exact @ih l' h + | max _ _ ih1 ih2 | imax _ _ ih1 ih2 => obtain ⟨_, h1, _, h2, ⟨⟩⟩ := h; exact ⟨ih1 h1, ih2 h2⟩ + | param n => exact h.2 ▸ h.1 + +theorem WF.of_mapM_ofLevel (h : List.mapM (VLevel.ofLevel Us) us = some us') + (a) (hl : a ∈ us') : VLevel.WF Us.length a := by + rw [List.mapM_eq_some] at h + have ⟨_, _, h⟩ := h.forall_exists_r _ hl; exact .of_ofLevel h + +end Ix.Theory.VLevel + +/-- The internal universe operation agrees definitionally with Lean's +natural-number operation used by the reference implementation proofs. -/ +@[simp] theorem Ix.Theory.VLevel.natIMax_eq_core (a b : Nat) : + Ix.Theory.VLevel.natIMax a b = Lean.Nat.imax a b := rfl diff --git a/Ix/Theory/Named/Verify/Axioms.lean b/Ix/Theory/Named/Verify/Axioms.lean new file mode 100644 index 000000000..7638841fc --- /dev/null +++ b/Ix/Theory/Named/Verify/Axioms.lean @@ -0,0 +1,516 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Batteries.Tactic.OpenPrivate +import Ix.Theory.Named.Std.Basic +import Ix.Theory.Named.Std.NodupKeys + +namespace Std.TreeMap + +variable {α : Type u} {β : Type v} {cmp : α → α → Ordering} {t : TreeMap α β cmp} + +/-- https://github.com/leanprover/lean4/issues/12798 -/ +axiom all_eq_all_toList {p : α → β → Bool} : + t.all p = t.toList.all fun a => p a.1 a.2 + +/-- https://github.com/leanprover/lean4/issues/12798 -/ +axiom any_eq_any_toList {p : α → β → Bool} : + t.any p = t.toList.any fun a => p a.1 a.2 + +end Std.TreeMap + +open scoped _root_.List +namespace Lean + +noncomputable def PersistentArrayNode.toList' : PersistentArrayNode α → List α := + PersistentArrayNode.rec + (motive_1 := fun _ => List α) (motive_2 := fun _ => List α) (motive_3 := fun _ => List α) + (node := fun _ => id) (leaf := (·.toList)) (fun _ => id) [] (fun _ _ a b => a ++ b) + +namespace PersistentArray + +inductive WF : PersistentArray α → Prop where + | empty : WF .empty + | push : WF arr → WF (arr.push x) + +noncomputable def toList' (arr : PersistentArray α) : List α := + arr.root.toList' ++ arr.tail.toList + +@[simp] theorem toList'_empty : (.empty : PersistentArray α).toList' = [] := rfl + +/-- We cannot prove this because `insertNewLeaf` is partial -/ +@[simp] axiom toList'_push {α} (arr : PersistentArray α) (x : α) : + (arr.push x).toList' = arr.toList' ++ [x] + +@[simp] theorem size_empty : (.empty : PersistentArray α).size = 0 := rfl + +@[simp] theorem size_push {α} (arr : PersistentArray α) (x : α) : + (arr.push x).size = arr.size + 1 := by + simp [push]; split <;> [rfl; (simp [mkNewTail]; split <;> rfl)] + +@[simp] theorem WF.toList'_length (h : WF arr) : arr.toList'.length = arr.size := by + induction h <;> simp [*] + +end PersistentArray + +namespace PersistentHashMap + +noncomputable def Node.toList' : Node α β → List (α × β) := + Node.rec + (motive_1 := fun _ => List (α × β)) (motive_2 := fun _ => List (α × β)) + (motive_3 := fun _ => List (α × β)) (motive_4 := fun _ => List (α × β)) + (entries := fun _ => id) (collision := fun ks xs _ => ks.toList.zip xs.toList) + (mk := fun _ => id) + (nil := []) (cons := fun _ _ l1 l2 => l1 ++ l2) + (entry := fun a b => [(a, b)]) (ref := fun _ => id) (null := []) + +noncomputable def toList' [BEq α] [Hashable α] (m : PersistentHashMap α β) : + List (α × β) := m.root.toList' + +inductive WF [BEq α] [Hashable α] : PersistentHashMap α β → Prop where + | empty : WF .empty + | insert : WF m → WF (m.insert a b) + +/-- We can't prove this because `Lean.PersistentHashMap.insertAux` is opaque -/ +axiom WF.toList'_insert {α β} [BEq α] [Hashable α] + [PartialEquivBEq α] [LawfulHashable α] + {m : PersistentHashMap α β} (_ : WF m) (a : α) (b : β) : + (m.insert a b).toList' ~ (a, b) :: m.toList'.filter (¬a == ·.1) + +/-- We can't prove this because `Lean.PersistentHashMap.findAux` is opaque -/ +axiom WF.find?_eq {α β} [BEq α] [Hashable α] + [PartialEquivBEq α] [LawfulHashable α] + {m : PersistentHashMap α β} (_ : WF m) (a : α) : m.find? a = m.toList'.lookup a + +/-- We can't prove this because `Lean.PersistentHashMap.{findAux, containsAux}` are opaque -/ +axiom findAux_isSome {α β} [BEq α] {node : Node α β} (i : USize) (a : α) : + containsAux node i a = (findAux node i a).isSome + +end PersistentHashMap + +namespace Syntax + +def structEq' : Syntax → Syntax → Bool + | .missing, .missing => true + | .node _ k args, .node _ k' args' => k == k' && + (args.size == args'.size && + (args.toList.attach.zip args'.toList.attach).all fun (a, b) => + have := Array.mem_toList_iff.1 a.2; structEq' a b) + | .atom _ val, .atom _ val' => val == val' + | .ident _ rawVal val preresolved, Syntax.ident _ rawVal' val' preresolved' => + rawVal == rawVal' && val == val' && preresolved == preresolved' + | _, _ => false +termination_by x _ => x + +theorem structEq'_node : + structEq' (.node _x k args) (.node _y k' args') = (k == k' && args.isEqv args' structEq') := by + unfold structEq'; simp; congr 1 + by_cases h : args.size = args'.size <;> [simp [h]; simp [Array.isEqv, h]] + let ⟨args⟩ := args; let ⟨args'⟩ := args'; simp at h ⊢ + have' : ((args.attach.map (·.1)).zip (args'.attach.map (·.1))).all + (fun x => x.1.structEq' x.2) = _ := by + simp only [List.zip_map_left, List.zip_map_right]; simp [Function.comp_def]; rfl + rw [← this]; simp; clear this + induction args generalizing args' <;> cases args' <;> simp at h <;> simp [List.isEqv, *] + +/-- This is a `partial` because it is not obviously terminating. The `structEq'_node` theorem +shows that a definition with the same clauses can be defined manually. -/ +@[simp] axiom structEq_eq : structEq a b = structEq' a b +end Syntax + +namespace Level + +/-! +### A total copy of `Lean.Level.normalize` + +`Lean.Level.normalize` and four of its helpers are `partial def`s, so they are opaque and nothing +can be proved about them. The `Total` namespace below is a clause-by-clause copy of +[Lean's `Lean/Level.lean`](https://github.com/leanprover/lean4/blob/v4.33.1/src/Lean/Level.lean#L319-L404), +under the same names, with the termination proofs supplied. That makes `normalize_eq` below a +purely syntactic trust assumption, checkable by reading the two definitions side by side; +`Ix.Theory.Named.Tests.LevelStd` also checks it on a finite corpus of levels. +-/ +namespace Total + +/-- The structural size of a level, used as the termination measure for `normalize`. -/ +def size : Level → Nat + | .zero | .param _ | .mvar _ => 1 + | .succ l => size l + 1 + | .max l₁ l₂ => size l₁ + size l₂ + 1 + | .imax l₁ l₂ => size l₁ + size l₂ + 2 + +/-- Secondary termination measure for `normalize`: in the `imax` branch it recurses on +`mkLevelMax l₁ l₂`, which has the same `size` as `imax l₁ l₂` but a smaller `tag`. -/ +private def tag (l : Level) : Nat := + match l.getLevelOffset with + | .imax .. => 1 + | _ => 0 + +private theorem tag_le (l : Level) : tag l ≤ 1 := by unfold tag; split <;> omega + +theorem one_le_size (l : Level) : 1 ≤ size l := by cases l <;> simp [size] + +private theorem getOffsetAux_eq (l : Level) (k) : getOffsetAux l k = getOffsetAux l 0 + k := by + induction l generalizing k with + | succ l ih => rw [getOffsetAux, ih (k+1), getOffsetAux, ih 1]; omega + | _ => simp [getOffsetAux] + +theorem size_getLevelOffset (l : Level) : + size l.getLevelOffset + l.getOffset = size l := by + simp only [getOffset] + induction l with | succ l ih => ?_ | _ => rfl + show size l.getLevelOffset + getOffsetAux l 1 = size l + 1 + rw [getOffsetAux_eq l 1]; omega + +end Total +open private accMax mkIMaxAux mkMaxAux skipExplicit isExplicitSubsumedAux + isExplicitSubsumed from Lean.Level + +def Total.mkMaxAux (lvls : Array Level) (extraK : Nat) (i : Nat) + (prev : Level) (prevK : Nat) (result : Level) : Level := + if h : i < lvls.size then + let lvl := lvls[i] + let curr := lvl.getLevelOffset + let currK := lvl.getOffset + if curr == prev then mkMaxAux lvls extraK (i+1) curr currK result + else mkMaxAux lvls extraK (i+1) curr currK (accMax result prev (extraK + prevK)) + else accMax result prev (extraK + prevK) + +/-- Patch for `partial def Lean.Level.mkMaxAux`. -/ +@[simp] axiom mkMaxAux_eq : mkMaxAux = Total.mkMaxAux + +def Total.skipExplicit (lvls : Array Level) (i : Nat) : Nat := + if h : i < lvls.size then + if lvls[i].getLevelOffset.isZero then skipExplicit lvls (i+1) else i + else i + +/-- Patch for `partial def Lean.Level.skipExplicit`. -/ +@[simp] axiom skipExplicit_eq : skipExplicit = Total.skipExplicit + +def Total.isExplicitSubsumedAux (lvls : Array Level) (maxExplicit : Nat) (i : Nat) : Bool := + if h : i < lvls.size then + if lvls[i].getOffset ≥ maxExplicit then true + else isExplicitSubsumedAux lvls maxExplicit (i+1) + else false + +/-- Patch for `partial def Lean.Level.isExplicitSubsumedAux`. -/ +@[simp] axiom isExplicitSubsumedAux_eq : isExplicitSubsumedAux = Total.isExplicitSubsumedAux + +mutual + +/-- A total copy of `partial def Lean.Level.normalize`. -/ +def Total.normalize (l : Level) : Level := + if isAlreadyNormalizedCheap l then l else + let k := l.getOffset + match h : l.getLevelOffset with + | .max l₁ l₂ => + let lvls := getMaxArgsAux l₁ false #[] + let lvls := getMaxArgsAux l₂ false lvls + let lvls := lvls.qsort normLt + let firstNonExplicit := skipExplicit lvls 0 + let i := if isExplicitSubsumed lvls firstNonExplicit then firstNonExplicit + else firstNonExplicit - 1 + let lvl₁ := lvls[i]! + let prev := lvl₁.getLevelOffset + let prevK := lvl₁.getOffset + mkMaxAux lvls k (i+1) prev prevK Level.zero + | .imax l₁ l₂ => + if l₂.isNeverZero then addOffset (normalize (mkLevelMax l₁ l₂)) k + else addOffset (mkIMaxAux (normalize l₁) (normalize l₂)) k + | _ => unreachable! +termination_by (1, 3 * size l + tag l) +decreasing_by all_goals + refine .right _ ?_ + have hsz := size_getLevelOffset l + rw [h] at hsz + simp only [size] at hsz + have := one_le_size l₁ + have := one_le_size l₂ + have := tag_le l₁ + have := tag_le l₂ + first + | omega + | have ht : tag l = 1 := by simp [tag, h] + have e1 : size (mkLevelMax l₁ l₂) = size l₁ + size l₂ + 1 := rfl + have e2 : tag (mkLevelMax l₁ l₂) = 0 := rfl + omega + +def Total.getMaxArgsAux : Level → Bool → Array Level → Array Level + | .max l₁ l₂, norm, lvls => getMaxArgsAux l₂ norm (getMaxArgsAux l₁ norm lvls) + | l, false, lvls => getMaxArgsAux (normalize l) true lvls + | l, true, lvls => lvls.push l +termination_by l b => (if b then 0 else 1, 3 * size l + tag l + 1) +decreasing_by + any_goals cases norm + any_goals first | refine .right _ ?_ | exact .left _ _ (by decide) + all_goals first + | omega + | have e1 : size (Level.max l₁ l₂) = size l₁ + size l₂ + 1 := rfl + have e2 : tag (Level.max l₁ l₂) = 0 := rfl + have := one_le_size l₁ + have := one_le_size l₂ + have := tag_le l₁ + have := tag_le l₂ + omega + +end + +/-- `Lean.Level.normalize` is a `partial def`, so it is opaque; +`Total.normalize` above is a total copy of it. -/ +axiom normalize_eq : normalize = Total.normalize + +def mkData' (h : UInt64) (depth : Nat := 0) (hasMVar hasParam : Bool := false) : Level.Data := + if depth > Nat.pow 2 24 - 1 then panic! "universe level depth is too big" + else + h.toUInt32.toUInt64 + + hasMVar.toUInt64.shiftLeft 32 + + hasParam.toUInt64.shiftLeft 33 + + depth.toUInt64.shiftLeft 40 + +/-- This exists only for the bit-twiddling proofs, it shouldn't appear +in the main results, which use the functions below instead -/ +axiom mkData_eq : @mkData = @mkData' + +def hasParam' : Level → Bool + | .param .. => true + | .zero | .mvar .. => false + | .succ l => l.hasParam' + | .max l₁ l₂ | .imax l₁ l₂ => l₁.hasParam' || l₂.hasParam' + +/-- This was false prior to the fix of lean4#8554; it should now be provable +using `mkData_eq` and friends, but this has not been done yet -/ +@[simp] axiom hasParam_eq (l : Level) : l.hasParam = l.hasParam' + +def hasMVar' : Level → Bool + | .mvar .. => true + | .zero | .param .. => false + | .succ l => l.hasMVar' + | .max l₁ l₂ | .imax l₁ l₂ => l₁.hasMVar' || l₂.hasMVar' + +/-- This was false prior to the fix of lean4#8554; it should now be provable +using `mkData_eq` and friends, but this has not been done yet -/ +@[simp] axiom hasMVar_eq (l : Level) : l.hasMVar = l.hasMVar' + +/-- This is because the `BEq` instance is implemented in C++ -/ +@[instance] axiom instLawfulBEqLevel : LawfulBEq Level + +@[inline] private def mkIMaxCore (u v : Level) (elseK : Unit → Level) : Level := + if v.isNeverZero then mkLevelMax' u v + else if v.isZero then v + else if u.isZero || u matches .succ .zero then v + else if u == v then u + else elseK () + +open private mkLevelIMaxCore from Lean.Level in +/-- Workaround for https://github.com/leanprover/lean4/pull/7631#issuecomment-3289800246 -/ +@[simp] axiom mkLevelIMaxCore_eq (e : Expr) (n : Nat) : mkLevelIMaxCore = mkIMaxCore + +end Level + +namespace Expr + +def mkData' + (h : UInt64) (looseBVarRange : Nat := 0) (approxDepth : UInt32 := 0) + (hasFVar hasExprMVar hasLevelMVar hasLevelParam : Bool := false) + : Expr.Data := + let approxDepth : UInt8 := if approxDepth > 255 then 255 else approxDepth.toUInt8 + assert! (looseBVarRange ≤ Nat.pow 2 20 - 1) + h.toUInt32.toUInt64 + + approxDepth.toUInt64.shiftLeft 32 + + hasFVar.toUInt64.shiftLeft 40 + + hasExprMVar.toUInt64.shiftLeft 41 + + hasLevelMVar.toUInt64.shiftLeft 42 + + hasLevelParam.toUInt64.shiftLeft 43 + + looseBVarRange.toUInt64.shiftLeft 44 + +/-- This exists only for the bit-twiddling proofs, it shouldn't appear +in the main results, which use the functions below instead -/ +axiom mkData_eq : @mkData = @mkData' + +@[inline] def mkAppData' (fData : Data) (aData : Data) : Data := + let depth := max fData.approxDepth.toUInt16 aData.approxDepth.toUInt16 + 1 + let approxDepth := if depth > 255 then 255 else depth.toUInt8 + let looseBVarRange := max fData.looseBVarRange aData.looseBVarRange + let hash := mixHash fData aData + let fData : UInt64 := fData + let aData : UInt64 := aData + assert! looseBVarRange ≤ (Nat.pow 2 20 - 1).toUInt32 + (fData ||| aData) &&& (15 : UInt64) <<< (40 : UInt64) ||| + hash.toUInt32.toUInt64 ||| + approxDepth.toUInt64 <<< (32 : UInt64) ||| + looseBVarRange.toUInt64 <<< (44 : UInt64) + +/-- This exists only for the bit-twiddling proofs, it shouldn't appear +in the main results, which use the functions below instead -/ +axiom mkAppData_eq : @mkAppData = @mkAppData' + +def looseBVarRange' : Expr → Nat + | .bvar i => i + 1 + | .const .. + | .sort _ + | .fvar _ + | .mvar _ + | .lit _ => 0 + | .mdata _ e + | .proj _ _ e => e.looseBVarRange' + | .app e1 e2 => max e1.looseBVarRange' e2.looseBVarRange' + | .lam _ e1 e2 _ + | .forallE _ e1 e2 _ => max e1.looseBVarRange' (e2.looseBVarRange' - 1) + | .letE _ e1 e2 e3 _ => max (max e1.looseBVarRange' e2.looseBVarRange') (e3.looseBVarRange' - 1) + +/-- This was false prior to the fix of lean4#8554; it should now be provable +using `mkData_eq` and friends, but this has not been done yet -/ +@[simp] axiom looseBVarRange_eq (e : Expr) : e.looseBVarRange = e.looseBVarRange' + +/-- This could be an `@[implemented_by]` -/ +@[simp] axiom replace_eq (e : Expr) (f) : e.replace f = e.replaceNoCache f + +def liftLooseBVars' (e : @& Expr) (s d : @& Nat) : Expr := + match e with + | .bvar i => .bvar (if i < s then i else i + d) + | .mdata m e => .mdata m (liftLooseBVars' e s d) + | .proj n i e => .proj n i (liftLooseBVars' e s d) + | .app f a => .app (liftLooseBVars' f s d) (liftLooseBVars' a s d) + | .lam n t b bi => .lam n (liftLooseBVars' t s d) (liftLooseBVars' b (s+1) d) bi + | .forallE n t b bi => .forallE n (liftLooseBVars' t s d) (liftLooseBVars' b (s+1) d) bi + | .letE n t v b bi => + .letE n (liftLooseBVars' t s d) (liftLooseBVars' v s d) (liftLooseBVars' b (s+1) d) bi + | e@(.const ..) + | e@(.sort _) + | e@(.fvar _) + | e@(.mvar _) + | e@(.lit _) => e + +/-- This could be an `@[implemented_by]` -/ +@[simp] axiom liftLooseBVars_eq (e : Expr) (s d) : e.liftLooseBVars s d = e.liftLooseBVars' s d + +def lowerLooseBVars' (e : @& Expr) (s d : @& Nat) : Expr := + if s < d then e else + match e with + | .bvar i => .bvar (if i < s then i else i - d) + | .mdata m e => .mdata m (lowerLooseBVars' e s d) + | .proj n i e => .proj n i (lowerLooseBVars' e s d) + | .app f a => .app (lowerLooseBVars' f s d) (lowerLooseBVars' a s d) + | .lam n t b bi => .lam n (lowerLooseBVars' t s d) (lowerLooseBVars' b (s+1) d) bi + | .forallE n t b bi => .forallE n (lowerLooseBVars' t s d) (lowerLooseBVars' b (s+1) d) bi + | .letE n t v b bi => + .letE n (lowerLooseBVars' t s d) (lowerLooseBVars' v s d) (lowerLooseBVars' b (s+1) d) bi + | e@(.const ..) + | e@(.sort _) + | e@(.fvar _) + | e@(.mvar _) + | e@(.lit _) => e + +/-- This could be an `@[implemented_by]` -/ +@[simp] axiom lowerLooseBVars_eq (e : Expr) (s d) : e.lowerLooseBVars s d = e.lowerLooseBVars' s d + +def instantiate1' (e : Expr) (subst : Expr) (d := 0) : Expr := + match e with + | .bvar i => if i < d then e else if i = d then subst.liftLooseBVars' 0 d else .bvar (i - 1) + | .mdata m e => .mdata m (instantiate1' e subst d) + | .proj s i e => .proj s i (instantiate1' e subst d) + | .app f a => .app (instantiate1' f subst d) (instantiate1' a subst d) + | .lam n t b bi => .lam n (instantiate1' t subst d) (instantiate1' b subst (d+1)) bi + | .forallE n t b bi => .forallE n (instantiate1' t subst d) (instantiate1' b subst (d+1)) bi + | .letE n t v b bi => + .letE n (instantiate1' t subst d) (instantiate1' v subst d) (instantiate1' b subst (d+1)) bi + | .const .. + | .sort _ + | .fvar _ + | .mvar _ + | .lit _ => e + +/-- This could be an `@[implemented_by]` -/ +@[simp] axiom instantiate1_eq (e : Expr) (subst) : e.instantiate1 subst = e.instantiate1' subst + +@[simp] def instantiateList : Expr → List Expr → (k :_:= 0) → Expr + | e, [], _ => e + | e, a :: as, k => instantiateList (instantiate1' e a k) as k + +/-- This could be an `@[implemented_by]` -/ +@[simp] axiom instantiate_eq (e : Expr) (subst) : + e.instantiate subst = e.instantiateList subst.toList + +/-- This could be an `@[implemented_by]` -/ +@[simp] axiom instantiateRev_eq (e : Expr) (subst) : + e.instantiateRev subst = e.instantiate subst.reverse + +/-- This could be an `@[implemented_by]` -/ +@[simp] axiom instantiateRange_eq (e : Expr) (subst) : + e.instantiateRange start stop subst = e.instantiate (subst.extract start stop) + +/-- This could be an `@[implemented_by]` -/ +@[simp] axiom instantiateRevRange_eq (e : Expr) (subst) : + e.instantiateRevRange start stop subst = e.instantiateRev (subst.extract start stop) + +def abstract1 (v : FVarId) : Expr → (k :_:= 0) → Expr + | .bvar i, d => .bvar (if i < d then i else i + 1) + | e@(.fvar v'), d => if v == v' then .bvar d else e + | .mdata m e, d => .mdata m (abstract1 v e d) + | .proj s i e, d => .proj s i (abstract1 v e d) + | .app f a, d => .app (abstract1 v f d) (abstract1 v a d) + | .lam n t b bi, d => .lam n (abstract1 v t d) (abstract1 v b (d+1)) bi + | .forallE n t b bi, d => .forallE n (abstract1 v t d) (abstract1 v b (d+1)) bi + | .letE n t val b bi, d => + .letE n (abstract1 v t d) (abstract1 v val d) (abstract1 v b (d+1)) bi + | e@(.const ..), _ + | e@(.sort _), _ + | e@(.mvar _), _ + | e@(.lit _), _ => e + +@[simp] def abstractList : Expr → List FVarId → (k :_:= 0) → Expr + | e, [], _ => e + | e, a :: as, k => abstractList (abstract1 a e k) as k + +/-- This could be an `@[implemented_by]` -/ +@[simp] axiom abstract_eq (e : Expr) (xs : List FVarId) : + e.abstract ⟨xs.map .fvar⟩ = e.abstractList xs + +/-- This could be an `@[implemented_by]` -/ +@[simp] axiom abstractRange_eq (e : Expr) (n : Nat) (xs : Array Expr) : + e.abstractRange n xs = e.abstract (xs.extract 0 n) + +def hasLooseBVar' : (e : @& Expr) → (bvarIdx : @& Nat) → Bool + | .bvar i, d => i = d + | .mdata _ e, d + | .proj _ _ e, d => hasLooseBVar' e d + | .app f a, d => hasLooseBVar' f d || hasLooseBVar' a d + | .lam _ t b _, d + | .forallE _ t b _, d => hasLooseBVar' t d || hasLooseBVar' b (d+1) + | .letE _ t v b _, d => hasLooseBVar' t d || hasLooseBVar' v d || hasLooseBVar' b (d+1) + | .const .., _ + | .sort _, _ + | .fvar _, _ + | .mvar _, _ + | .lit _, _ => false + +/-- This could be an `@[implemented_by]` -/ +@[simp] axiom hasLooseBVar_eq (e : Expr) (n : Nat) : e.hasLooseBVar n = e.hasLooseBVar' n + +def eqv' : (e1 e2 : Expr) → (strict : Bool := false) → Bool + | .bvar i, .bvar i', _ + | .lit i, .lit i', _ + | .mvar i, .mvar i', _ + | .fvar i, .fvar i', _ + | .sort i, .sort i', _ => i == i' + | .mdata d e, .mdata d' e', st => e.eqv' e' st && d.entries == d'.entries + | .proj s i e, .proj s' i' e', st => e.eqv' e' st && s == s' && i == i' + | .const n ls, .const n' ls', _ => n == n' && ls == ls' + | .app f a, .app f' a', st => f.eqv' f' st && a.eqv' a' st + | .lam n t b bi, .lam n' t' b' bi', st + | .forallE n t b bi, .forallE n' t' b' bi', st => + t.eqv' t' st && b.eqv' b' st && (!st || (n == n' && bi == bi')) + | .letE n t v b nd, .letE n' t' v' b' nd', st => + t.eqv' t' st && v.eqv' v' st && b.eqv' b' st && nd == nd' && (!st || n == n') + | _, _, _ => false + +/-- This could be an `@[implemented_by]` -/ +@[simp] axiom eqv_eq (e1 e2 : Expr) : e1.eqv e2 = e1.eqv' e2 + +/-- This could be an `@[implemented_by]` -/ +@[simp] axiom equal_eq (e1 e2 : Expr) : e1.equal e2 = e1.eqv' e2 (strict := true) + +end Expr diff --git a/Ix/Theory/Named/Verify/Environment/Basic.lean b/Ix/Theory/Named/Verify/Environment/Basic.lean new file mode 100644 index 000000000..3166422c0 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/Basic.lean @@ -0,0 +1,555 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Verify.LocalContext +import Ix.Theory.Named.Typing.EnvLemmas + +namespace Ix.Theory.Named +open Lean hiding Environment Exception +open Kernel + +theorem ConstantInfo.hasValue_eq (ci : ConstantInfo) : ci.hasValue = ci.value?.isSome := by + cases ci <;> rfl + +theorem ConstantInfo.value!_eq (ci : ConstantInfo) : ci.value! = ci.value?.get! := by + cases ci <;> simp [ConstantInfo.value?, ConstantInfo.value!] + +def _root_.Lean.ConstantInfo.safety (ci : ConstantInfo) : DefinitionSafety := + if ci.isUnsafe then .unsafe else if ci.isPartial then .partial else .safe + +variable (safety : DefinitionSafety) (env : VEnv) in +def TrConstant (ci : ConstantInfo) (ci' : VConstant) : Prop := + safety ≤ ci.safety ∧ ci.levelParams.length = ci'.uvars ∧ + TrExprS env ci.levelParams [] ci.type ci'.type + +variable (safety : DefinitionSafety) (env : VEnv) in +def TrConstVal (ci : ConstantInfo) (ci' : VConstVal) : Prop := + TrConstant safety env ci ci'.toVConstant ∧ ci.name = ci'.name + +variable (safety : DefinitionSafety) (env : VEnv) in +def TrDefVal (ci : ConstantInfo) (ci' : VDefVal) : Prop := + TrConstVal safety env ci ci'.toVConstVal ∧ + TrExprS env ci.levelParams [] (ci.value! (allowOpaque := true)) ci'.value + +/-- The step an abstract environment takes when `ci`, modelled by `ci'`, is added. + +At safety levels where the declaration is visible the constant is added; where it is not, the +environment is unchanged, matching `TrEnv'.ignore`. Stating this rather than just `venv ≤ venv'` +is what lets a caller see *which* constant a step added. -/ +def VEnv.AddConst (venv : VEnv) (safety : DefinitionSafety) (ci : ConstantInfo) + (ci' : VConstant) (venv' : VEnv) : Prop := + if safety ≤ ci.safety then + TrConstant safety venv ci ci' ∧ ci'.WF venv ∧ venv.addConst ci.name ci' = some venv' + else + venv' = venv + +theorem VEnv.AddConst.le {venv venv' : VEnv} {ci ci'} + (H : VEnv.AddConst venv safety ci ci' venv') : venv ≤ venv' := by + unfold VEnv.AddConst at H; split at H + · exact addConst_le H.2.2 + · exact H ▸ VEnv.LE.rfl + +/-- As `VEnv.AddConst`, for a definition: the constant is added and then its defining equation, +matching `TrEnv'.defn`. -/ +def VEnv.AddDef (venv : VEnv) (safety : DefinitionSafety) (ci : ConstantInfo) + (ci' : VDefVal) (venv' : VEnv) : Prop := + if safety ≤ ci.safety then + ∃ base, TrDefVal safety venv ci ci' ∧ ci'.WF venv ∧ + venv.addConst ci.name ci'.toVConstant = some base ∧ + venv' = base.addDefEq ci'.toDefEq + else + venv' = venv + +theorem VEnv.AddDef.le {venv venv' : VEnv} {ci ci'} + (H : VEnv.AddDef venv safety ci ci' venv') : venv ≤ venv' := by + unfold VEnv.AddDef at H; split at H + · obtain ⟨base, _, _, hadd, rfl⟩ := H + exact (addConst_le hadd).trans (VEnv.addDefEq_le ..) + · exact H ▸ VEnv.LE.rfl + +def AddQuot1 (name : Name) (kind : QuotKind) (ci' : VConstant) (P : ConstMap → VEnv → Prop) + (m : ConstMap) (env : VEnv) : Prop := + ∃ levelParams type env', + let ci := .quotInfo { name, kind, levelParams, type } + TrConstant .safe env ci ci' ∧ + m.find? name = none ∧ + env.addConst name ci' = some env' ∧ + P (m.insert name ci) env' + +theorem AddQuot1.to_addQuot + (H1 : ∀ m env, P m env → f env = some env') + (m env) (H : AddQuot1 name kind ci' P m env) : + env.addConst name ci' >>= f = some env' := by + let ⟨_, _, _, h1, _, h2, h3⟩ := H + simpa using ⟨_, h2, H1 _ _ h3⟩ + +theorem AddQuot1.le + (H1 : ∀ m env, P m env → env ≤ env₀) + (m env) (H : AddQuot1 name kind ci' P m env) : env ≤ env₀ := + let ⟨_, _, _, _, _, h2, h3⟩ := H + .trans (VEnv.addConst_le h2) (H1 _ _ h3) + +def AddQuot (m₁ m₂ : ConstMap) (env₁ env₂ : VEnv) : Prop := + AddQuot1 ``Quot .type quotConst (m := m₁) (env := env₁) <| + AddQuot1 ``Quot.mk .ctor quotMkConst <| + AddQuot1 ``Quot.lift .lift quotLiftConst <| + AddQuot1 ``Quot.ind .ind quotIndConst (· = m₂ ∧ ·.addDefEq quotDefEq = env₂) + +nonrec theorem AddQuot.to_addQuot (H : AddQuot m₁ m₂ env₁ env₂) : env₁.addQuot = some env₂ := + open AddQuot1 in (to_addQuot <| to_addQuot <| to_addQuot <| to_addQuot (by simp)) _ _ H + +nonrec theorem AddQuot.le (H : AddQuot m₁ m₂ env₁ env₂) : env₁ ≤ env₂ := + open AddQuot1 in (le <| le <| le <| le fun _ _ h => h.2 ▸ VEnv.addDefEq_le) _ _ H + +/-! ## Inductive-environment alignment -/ + +/-- The three kinds of constants emitted by an inductive declaration. The +role is tracked explicitly so an alignment witness cannot stand in a +definition or axiom where the kernel emits inductive metadata. -/ +inductive InductConstantKind where + | induct + | ctor + | recursor + +def InductConstantKind.Matches : InductConstantKind → ConstantInfo → Prop + | .induct, .inductInfo _ => True + | .ctor, .ctorInfo _ => True + | .recursor, .recInfo _ => True + | _, _ => False + +theorem InductConstantKind.Matches.value?_eq_none + {kind : InductConstantKind} {ci : ConstantInfo} + (H : InductConstantKind.Matches kind ci) : ci.value? = none := by + cases kind <;> cases ci <;> simp_all [InductConstantKind.Matches, ConstantInfo.value?] + +/-- One insertion shared by the implementation `ConstMap` and Theory `VEnv`. +The translated constant is checked in the Theory environment immediately +before insertion. -/ +structure AddInductConstant (kind : InductConstantKind) + (m₁ : ConstMap) (env₁ : VEnv) (ci' : VConstVal) + (m₂ : ConstMap) (env₂ : VEnv) where + info : ConstantInfo + kind_eq : kind.Matches info + tr : TrConstVal .safe env₁ info ci' + map_fresh : m₁.find? ci'.name = none + env_add : env₁.addConst ci'.name ci'.toVConstant = some env₂ + map_add : m₂ = m₁.insert ci'.name info + +/-- List-fold alignment for the constructor constants of a block. -/ +inductive AddInductConstants (kind : InductConstantKind) : + ConstMap → VEnv → List VConstVal → ConstMap → VEnv → Type where + | nil : AddInductConstants kind m env [] m env + | cons : + AddInductConstant kind m₁ env₁ ci m₂ env₂ → + AddInductConstants kind m₂ env₂ cis m₃ env₃ → + AddInductConstants kind m₁ env₁ (ci :: cis) m₃ env₃ + +/-- A reusable witness for the defeq tail of an environment transaction. -/ +structure AddDefEqs (env₁ : VEnv) (dfs : List VDefEq) (env₂ : VEnv) : Prop where + fold_eq : dfs.foldl VEnv.addDefEq env₁ = env₂ + +/-- The generated recursor represented as a named Theory constant. -/ +def inductRecVal (decl : VInductDecl) (ty : VInductiveType) : VConstVal := + ⟨VInductDecl.recConstRec decl.uvars ty.name decl.nparams ty, .str ty.name "rec"⟩ + +/-- The exact mixed recursor represented as a named Theory constant. -/ +def inductGenerationRecVal {decl : VInductDecl} + (generation : decl.GenerationChecked) : VConstVal := + ⟨generation.recursor, .str generation.block.sourceType.name "rec"⟩ + +/-- The implementation recursor metadata carries the same K-like reduction +flag retained by Theory generation. Keeping this separate from `TrConstant` +prevents a type-correct recursor with the wrong reduction behavior from +satisfying an inductive alignment trace. -/ +def RecursorKMatches (info : ConstantInfo) (kTarget : Bool) : Prop := + match info with + | .recInfo rec => rec.k = kTarget + | _ => False + +instance (info : ConstantInfo) (kTarget : Bool) : + Decidable (RecursorKMatches info kTarget) := by + cases info <;> simp [RecursorKMatches] <;> infer_instance + +/-- Data-bearing trace of a complete normalized inductive transaction: one +`inductInfo`, the constructor `ctorInfo`s in declaration order, one `recInfo`, +and finally the generated Theory iota equations. The retained generation +certificate owns both the raw declaration inserted into the Theory environment +and the checked normalization view used to generate the recursor and rules. -/ +structure AddInductTrace (m₁ : ConstMap) (env₁ : VEnv) (decl : VInductDecl) + (m₂ : ConstMap) (env₂ : VEnv) where + generation : decl.GenerationChecked + generation_wf : generation.WF env₁ + typeMap : ConstMap + typeEnv : VEnv + ctorMap : ConstMap + ctorEnv : VEnv + recEnv : VEnv + addType : AddInductConstant .induct m₁ env₁ + generation.block.sourceType.toVConstVal typeMap typeEnv + addCtors : AddInductConstants .ctor typeMap typeEnv + generation.block.sourceType.ctors ctorMap ctorEnv + addRec : AddInductConstant .recursor ctorMap ctorEnv + (inductGenerationRecVal generation) m₂ recEnv + recK : RecursorKMatches addRec.info generation.kTarget + addRules : AddDefEqs recEnv generation.generatedRules env₂ + +/-- Proposition-valued environment alignment, preserving the public shape of +the original placeholder while hiding the intermediate transaction states. -/ +def AddInduct (m₁ : ConstMap) (env₁ : VEnv) (decl : VInductDecl) + (m₂ : ConstMap) (env₂ : VEnv) : Prop := + Nonempty (AddInductTrace m₁ env₁ decl m₂ env₂) + +/-- Every implementation recursor stored by a block replay retains the +K-like flag computed by the block generator. -/ +def RecursorMapKMatches (m : ConstMap) (recursors : List VConstVal) + (kTarget : Bool) : Prop := + ∀ recursor ∈ recursors, ∃ info, + m.find? recursor.name = some info ∧ RecursorKMatches info kTarget + +/-- Data-bearing alignment trace for a complete mutual inductive block. +Families, globally flattened constructors, and recursors are each inserted +as a list phase, followed only after all recursors exist by the flattened +rule phase. -/ +structure AddInductBlockTrace + (m₁ : ConstMap) (env₁ : VEnv) (decl : VInductDecl) + (m₂ : ConstMap) (env₂ : VEnv) where + generation : decl.BlockGenerationChecked + blockEnv : VEnv + generation_wf : generation.WF env₁ blockEnv + typeMap : ConstMap + typeEnv : VEnv + ctorMap : ConstMap + ctorEnv : VEnv + recEnv : VEnv + addTypes : AddInductConstants .induct m₁ env₁ + decl.blockTypeConstants typeMap typeEnv + addCtors : AddInductConstants .ctor typeMap typeEnv + decl.blockConstructorConstants ctorMap ctorEnv + addRecs : AddInductConstants .recursor ctorMap ctorEnv + generation.recursors m₂ recEnv + recK : RecursorMapKMatches m₂ generation.recursors generation.kTarget + addRules : AddDefEqs recEnv generation.generatedRules env₂ + +/-- Proposition-valued alignment for a complete mutual block. -/ +def AddInductBlock (m₁ : ConstMap) (env₁ : VEnv) (decl : VInductDecl) + (m₂ : ConstMap) (env₂ : VEnv) : Prop := + Nonempty (AddInductBlockTrace m₁ env₁ decl m₂ env₂) + +/-- Data-bearing alignment trace for a nested inductive declaration: the +source families and constructors are the stored payload, followed by the +restored recursors and restored rules. The implementation map receives +only restored metadata; no auxiliary constant appears in either the map or +the Theory environment. -/ +structure AddInductNestedTrace + (m₁ : ConstMap) (env₁ : VEnv) (decl : VInductDecl) + (m₂ : ConstMap) (env₂ : VEnv) where + nested : decl.NestedBlockChecked + nested_wf : nested.WF env₁ + typeMap : ConstMap + typeEnv : VEnv + ctorMap : ConstMap + ctorEnv : VEnv + recEnv : VEnv + addTypes : AddInductConstants .induct m₁ env₁ + decl.blockTypeConstants typeMap typeEnv + addCtors : AddInductConstants .ctor typeMap typeEnv + decl.blockConstructorConstants ctorMap ctorEnv + addRecs : AddInductConstants .recursor ctorMap ctorEnv + nested.recursors m₂ recEnv + recK : RecursorMapKMatches m₂ nested.recursors nested.generation.kTarget + addRules : AddDefEqs recEnv nested.generatedRules env₂ + +/-- Proposition-valued alignment for a nested declaration. -/ +def AddInductNested (m₁ : ConstMap) (env₁ : VEnv) (decl : VInductDecl) + (m₂ : ConstMap) (env₂ : VEnv) : Prop := + Nonempty (AddInductNestedTrace m₁ env₁ decl m₂ env₂) + +theorem AddInductConstants.to_foldlM : + AddInductConstants kind m₁ env₁ cis m₂ env₂ → + List.foldlM (fun env (ci : VConstVal) => env.addConst ci.name ci.toVConstant) env₁ cis = + some env₂ + | .nil => rfl + | .cons h hrest => by + rw [List.foldlM_cons, h.env_add] + exact hrest.to_foldlM + +theorem AddInductConstant.fresh + (H : AddInductConstant kind m₁ env₁ ci m₂ env₂) : env₁.constants ci.name = none := + VEnv.addConst_fresh H.env_add + +theorem AddInductConstant.lookup + (H : AddInductConstant kind m₁ env₁ ci m₂ env₂) : + env₂.constants ci.name = some ci.toVConstant := + VEnv.addConst_self H.env_add + +theorem AddInductConstant.le + (H : AddInductConstant kind m₁ env₁ ci m₂ env₂) : env₁ ≤ env₂ := + VEnv.addConst_le H.env_add + +theorem AddInductConstants.le : + AddInductConstants kind m₁ env₁ cis m₂ env₂ → env₁ ≤ env₂ + | .nil => .rfl + | .cons h hrest => h.le.trans hrest.le + +theorem AddInductConstants.lookup : + (H : AddInductConstants kind m₁ env₁ cis m₂ env₂) → + ∀ ci ∈ cis, env₂.constants ci.name = some ci.toVConstant + | .nil, _, hmem => nomatch hmem + | .cons h hrest, ci, hmem => by + rcases List.mem_cons.1 hmem with rfl | hmem + · exact hrest.le.constants h.lookup + · exact hrest.lookup ci hmem + +theorem AddInductConstants.fresh : + (H : AddInductConstants kind m₁ env₁ cis m₂ env₂) → + ∀ ci ∈ cis, env₁.constants ci.name = none + | .nil, _, hmem => nomatch hmem + | .cons h hrest, ci, hmem => by + rcases List.mem_cons.1 hmem with rfl | hmem + · exact h.fresh + · exact h.le.constants_none (hrest.fresh ci hmem) + +theorem AddDefEqs.to_add (H : AddDefEqs env₁ dfs env₂) : + dfs.foldl VEnv.addDefEq env₁ = env₂ := H.fold_eq + +theorem AddDefEqs.le (H : AddDefEqs env₁ dfs env₂) : env₁ ≤ env₂ := by + rw [← H.fold_eq] + exact (VInductDecl.rulesFold_spec dfs env₁).1 + +theorem AddDefEqs.lookup (H : AddDefEqs env₁ dfs env₂) + (hdf : df ∈ dfs) : env₂.defeqs df := by + rw [← H.fold_eq] + exact (VInductDecl.rulesFold_spec dfs env₁).2 df hdf + +theorem AddInductTrace.to_addInductGeneration + (H : AddInductTrace m₁ env₁ decl m₂ env₂) : + env₁.addInductGeneration H.generation = some env₂ := by + have hrec : + H.ctorEnv.addConst + (.str H.generation.block.sourceType.name "rec") + H.generation.recursor = some H.recEnv := by + simpa [inductGenerationRecVal] using H.addRec.env_add + simp [VEnv.addInductGeneration, H.addType.env_add, + H.addCtors.to_foldlM, hrec, H.addRules.to_add] + +theorem AddInductBlockTrace.to_addInductBlockGeneration + (H : AddInductBlockTrace m₁ env₁ decl m₂ env₂) : + env₁.addInductBlockGeneration H.generation = some env₂ := by + simp [VEnv.addInductBlockGeneration, H.addTypes.to_foldlM, + H.addCtors.to_foldlM, H.addRecs.to_foldlM, H.addRules.to_add] + +theorem AddInductNestedTrace.to_addInductNested + (H : AddInductNestedTrace m₁ env₁ decl m₂ env₂) : + env₁.addInductNested H.nested = some env₂ := by + simp [VEnv.addInductNested, H.addTypes.to_foldlM, + H.addCtors.to_foldlM, H.addRecs.to_foldlM, H.addRules.to_add] + +/-- Recover the exact certified normalized Theory transaction represented by +an implementation metadata replay. This replaces the old, false-for-aliases +claim that every replay must pass the identity-only `VEnv.addInduct` wrapper. -/ +nonrec theorem AddInduct.to_addInduct + (H : AddInduct m₁ env₁ decl m₂ env₂) : + ∃ generation : decl.GenerationChecked, + generation.WF env₁ ∧ + env₁.addInductGeneration generation = some env₂ := by + rcases H with ⟨H⟩ + exact ⟨H.generation, H.generation_wf, H.to_addInductGeneration⟩ + +nonrec theorem AddInduct.le (H : AddInduct m₁ env₁ decl m₂ env₂) : env₁ ≤ env₂ := by + obtain ⟨generation, -, hadd⟩ := H.to_addInduct + rcases VEnv.addInductGeneration_trace hadd with ⟨trace⟩ + exact trace.le + +/-- Recover the exact block-wide Theory transaction represented by an +implementation metadata replay. -/ +theorem AddInductBlock.to_addInductBlock + (H : AddInductBlock m₁ env₁ decl m₂ env₂) : + ∃ (generation : decl.BlockGenerationChecked) (blockEnv : VEnv), + generation.WF env₁ blockEnv ∧ + env₁.addInductBlockGeneration generation = some env₂ := by + rcases H with ⟨H⟩ + exact ⟨H.generation, H.blockEnv, H.generation_wf, + H.to_addInductBlockGeneration⟩ + +theorem AddInductBlock.le + (H : AddInductBlock m₁ env₁ decl m₂ env₂) : env₁ ≤ env₂ := by + obtain ⟨generation, -, -, hadd⟩ := H.to_addInductBlock + rcases VEnv.addInductBlockGeneration_trace hadd with ⟨trace⟩ + exact trace.le + +/-- Recover the exact nested Theory transaction represented by an +implementation metadata replay. -/ +theorem AddInductNested.to_addInductNested + (H : AddInductNested m₁ env₁ decl m₂ env₂) : + ∃ nested : decl.NestedBlockChecked, + nested.WF env₁ ∧ env₁.addInductNested nested = some env₂ := by + rcases H with ⟨H⟩ + exact ⟨H.nested, H.nested_wf, H.to_addInductNested⟩ + +theorem AddInductNested.le + (H : AddInductNested m₁ env₁ decl m₂ env₂) : env₁ ≤ env₂ := by + obtain ⟨nested, -, hadd⟩ := H.to_addInductNested + exact VEnv.addInductNested_le hadd + +/- The projection relation is now a concrete Theory proposition, so merely +mentioning `TrExprS` no longer contaminates these projection-free roots with +the deferred structural-law sorries. -/ +#guard_named_axioms Ix.Theory.Named.AddInductTrace.to_addInductGeneration [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInduct.to_addInduct [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInduct.le [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductBlockTrace.to_addInductBlockGeneration [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductBlock.to_addInductBlock [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductBlock.le [propext, Classical.choice, Quot.sound] + +/-- Insert a whole block of definitions into the constant map. -/ +def insertDefs (C : ConstMap) (cis : List DefinitionVal) : ConstMap := + cis.foldl (fun C ci => C.insert ci.name (.defnInfo ci)) C + +variable (safety : DefinitionSafety) (env env' : VEnv) in +/-- Translation data for a mutual block: the headers are translated against the environment +before the block is added, the values against the environment that already has every constant +of the block, mirroring the kernel adding them all as axioms first. -/ +def TrDefBlock (cis : List DefinitionVal) (cis' : List VDefVal) : Prop := + List.Forall₂ (fun ci ci' => + TrConstVal safety env (.defnInfo ci) ci'.toVConstVal ∧ + TrExprS env' ci.levelParams [] ci.value ci'.value) cis cis' + +variable (safety : DefinitionSafety) in +inductive TrEnv' : ConstMap → Bool → VEnv → Prop where + | empty : TrEnv' {} false .empty + | ignore : + C.find? ci.name = none → ¬safety ≤ ci.safety → + TrEnv' C Q env → + TrEnv' (C.insert ci.name ci) Q env + | axiom : + TrConstant safety env (.axiomInfo ci) ci' → + C.find? ci.name = none → ci'.WF env → + env.addConst ci.name ci' = some env' → + TrEnv' C Q env → + TrEnv' (C.insert ci.name (.axiomInfo ci)) Q env' + | defn {ci' : VDefVal} : + TrDefVal safety env (.defnInfo ci) ci' → + C.find? ci.name = none → ci'.WF env → + env.addConst ci.name ci'.toVConstant = some env' → + TrEnv' C Q env → + TrEnv' (C.insert ci.name (.defnInfo ci)) Q (env'.addDefEq ci'.toDefEq) + /-- A mutual block, and an unsafe definition as the one-element case. -/ + | mutualDef {cis : List DefinitionVal} {cis' : List VDefVal} : + TrDefBlock safety env env' cis cis' → + -- the block's names are distinct; `addMutual` checks this, as does lean4#14632 + (cis.map (·.name)).Nodup → + (∀ ci ∈ cis, C.find? ci.name = none) → + (∀ ci' ∈ cis', ci'.toVConstant.WF env) → + env.addConsts cis' = some env' → + (∀ ci' ∈ cis', ci'.WF env') → + TrEnv' C Q env → + TrEnv' (insertDefs C cis) Q (env'.addDefEqs cis') + | thm {ci' : VDefVal} : + TrDefVal safety env (.thmInfo ci) ci' → + C.find? ci.name = none → ci'.WF env → + env.HasType ci'.uvars [] ci'.type (.sort .zero) → + env.addConst ci.name ci'.toVConstant = some env' → + TrEnv' C Q env → + TrEnv' (C.insert ci.name (.thmInfo ci)) Q env' + | opaque {ci' : VDefVal} : + TrDefVal safety env (.opaqueInfo ci) ci' → + C.find? ci.name = none → ci'.WF env → + env.addConst ci.name ci'.toVConstant = some env' → + TrEnv' C Q env → + TrEnv' (C.insert ci.name (.opaqueInfo ci)) Q env' + | quot : + env.QuotReady → + AddQuot C C' env env' → + TrEnv' C false env → + TrEnv' C' true env' + /-- Internal kernel-checking stage for one inductive metadata constant. + Lean checks a family and its constructors in environments that exist before + the complete block transaction. Keeping this stage explicit lets the + verified WHNF/defeq checker run in exactly those environments; final + environment replay continues to use the atomic `induct` constructor below. + -/ + | inductStaging {ci' : VConstVal} : + AddInductConstant kind C env ci' C' env' → + ci'.toVConstant.WF env → + TrEnv' C Q env → + TrEnv' C' Q env' + | induct : + AddInduct C env decl C' env' → + TrEnv' C Q env → + TrEnv' C' Q env' + | inductBlock : + AddInductBlock C env decl C' env' → + TrEnv' C Q env → + TrEnv' C' Q env' + | inductNested : + AddInductNested C env decl C' env' → + TrEnv' C Q env → + TrEnv' C' Q env' + /-- Register a Theory structure-eta descriptor without changing the host + constant map. Host eligibility and exact view alignment are retained by + `StructureEtaArtifact`; this history step records only the checked Theory + capability and its subject-reduction certificate. -/ + | structEta : + rule.WF env → + TrEnv' C Q env → + TrEnv' C Q (env.addStructEta rule) + +def TrEnv (safety : DefinitionSafety) (env : Environment) (venv : VEnv) : Prop := + TrEnv' safety env.constants env.quotInit venv + +theorem TrEnv'.wf (H : TrEnv' safety C Q venv) : venv.WF := by + induction H with + | empty => exact ⟨_, .empty⟩ + | ignore _ _ _ ih => exact ih + | «axiom» _ _ h1 h2 _ ih => + have ⟨_, H⟩ := ih + exact ⟨_, H.decl <| .axiom (ci := ⟨_, _⟩) h1 h2⟩ + | defn h1 _ h2 h3 _ ih => + have ⟨_, H⟩ := ih + have := h1.1.2; dsimp [ConstantInfo.name, ConstantInfo.toConstantVal] at this + exact ⟨_, H.decl <| .def h2 (this ▸ h3)⟩ + | mutualDef _ _ _ h2 h3 h4 _ ih => + have ⟨_, H⟩ := ih + exact ⟨_, H.decl <| .mutualDef h2 h3 h4⟩ + | thm h1 _ h2 h3 h4 _ ih => + have ⟨_, H⟩ := ih + have hn := h1.1.2 + dsimp [ConstantInfo.name, ConstantInfo.toConstantVal] at hn + exact ⟨_, (H.decl (.example h2)).decl (.axiom ⟨_, h3⟩ (hn ▸ h4))⟩ + | «opaque» h1 _ h2 h3 _ ih => + have ⟨_, H⟩ := ih + have := h1.1.2; dsimp [ConstantInfo.name, ConstantInfo.toConstantVal] at this + exact ⟨_, H.decl <| .opaque h2 (this ▸ h3)⟩ + | quot h1 h2 _ ih => + have ⟨_, H⟩ := ih + exact ⟨_, H.decl <| .quot h1 h2.to_addQuot⟩ + | inductStaging h1 h2 _ ih => + have ⟨_, H⟩ := ih + exact ⟨_, H.decl <| .axiom h2 h1.env_add⟩ + | induct h1 _ ih => + have ⟨_, H⟩ := ih + obtain ⟨generation, hgen, hadd⟩ := h1.to_addInduct + exact ⟨_, H.decl <| .induct hgen hadd⟩ + | inductBlock h1 _ ih => + have ⟨_, H⟩ := ih + obtain ⟨generation, blockEnv, hgen, hadd⟩ := + h1.to_addInductBlock + exact ⟨_, H.decl <| .inductBlock (blockEnv := blockEnv) hgen hadd⟩ + | inductNested h1 _ ih => + have ⟨_, H⟩ := ih + obtain ⟨nested, hwf, hadd⟩ := h1.to_addInductNested + exact ⟨_, H.decl <| .inductNested hwf hadd⟩ + | structEta hrule _ ih => + have ⟨_, H⟩ := ih + exact ⟨_, H.structEta hrule⟩ + +#guard_named_axioms Ix.Theory.Named.TrEnv'.wf [propext, Classical.choice, Quot.sound] diff --git a/Ix/Theory/Named/Verify/Environment/ConstructorValidation.lean b/Ix/Theory/Named/Verify/Environment/ConstructorValidation.lean new file mode 100644 index 000000000..78ff92f85 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/ConstructorValidation.lean @@ -0,0 +1,9028 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Verify.Environment.Normalization + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named +open Lean hiding Environment Exception +open Kernel + +namespace AddInductive +open TypeChecker + +/-! +# Semantic interpretation of constructor validation + +This module interprets the operational trace retained by +`Inductive.ValidationTrace`. It deliberately sits above both the verified +checker and candidate normalization: validation supplies the accepted kernel +path, while normalization supplies the exact analyzer-owned Theory view. +-/ + +/-- Structural equality of kernel levels is sound after strict translation. -/ +theorem levelStructEq_ofLevel + (equal : levelStructEq lhs rhs = true) + (lhs_tr : VLevel.ofLevel Us lhs = some lhs') + (rhs_tr : VLevel.ofLevel Us rhs = some rhs') : + lhs' = rhs' := by + induction lhs generalizing rhs lhs' rhs' with + | zero => + cases rhs <;> simp_all [levelStructEq, VLevel.ofLevel] + | succ lhs ih => + cases rhs with + | succ rhs => + simp only [levelStructEq] at equal + simp only [VLevel.ofLevel, Option.bind_eq_bind] at lhs_tr rhs_tr + obtain ⟨lhs'', lhs_inner_tr, lhs'_eq⟩ := + Option.bind_eq_some_iff.mp lhs_tr + obtain ⟨rhs'', rhs_inner_tr, rhs'_eq⟩ := + Option.bind_eq_some_iff.mp rhs_tr + change some lhs''.succ = some lhs' at lhs'_eq + change some rhs''.succ = some rhs' at rhs'_eq + injection lhs'_eq with lhs'_eq + injection rhs'_eq with rhs'_eq + subst lhs' + subst rhs' + rw [ih equal lhs_inner_tr rhs_inner_tr] + | zero | max _ _ | imax _ _ | param _ | mvar _ => + simp_all [levelStructEq] + | max lhs₁ lhs₂ ih₁ ih₂ => + cases rhs with + | max rhs₁ rhs₂ => + simp only [levelStructEq, Bool.and_eq_true] at equal + simp only [VLevel.ofLevel, Option.bind_eq_bind] at lhs_tr rhs_tr + obtain ⟨lhs₁', lhs₁_tr, lhsTail⟩ := + Option.bind_eq_some_iff.mp lhs_tr + obtain ⟨lhs₂', lhs₂_tr, lhs'_eq⟩ := + Option.bind_eq_some_iff.mp lhsTail + obtain ⟨rhs₁', rhs₁_tr, rhsTail⟩ := + Option.bind_eq_some_iff.mp rhs_tr + obtain ⟨rhs₂', rhs₂_tr, rhs'_eq⟩ := + Option.bind_eq_some_iff.mp rhsTail + change some (.max lhs₁' lhs₂') = some lhs' at lhs'_eq + change some (.max rhs₁' rhs₂') = some rhs' at rhs'_eq + injection lhs'_eq with lhs'_eq + injection rhs'_eq with rhs'_eq + subst lhs' + subst rhs' + rw [ih₁ equal.1 lhs₁_tr rhs₁_tr, + ih₂ equal.2 lhs₂_tr rhs₂_tr] + | zero | succ _ | imax _ _ | param _ | mvar _ => + simp_all [levelStructEq] + | imax lhs₁ lhs₂ ih₁ ih₂ => + cases rhs with + | imax rhs₁ rhs₂ => + simp only [levelStructEq, Bool.and_eq_true] at equal + simp only [VLevel.ofLevel, Option.bind_eq_bind] at lhs_tr rhs_tr + obtain ⟨lhs₁', lhs₁_tr, lhsTail⟩ := + Option.bind_eq_some_iff.mp lhs_tr + obtain ⟨lhs₂', lhs₂_tr, lhs'_eq⟩ := + Option.bind_eq_some_iff.mp lhsTail + obtain ⟨rhs₁', rhs₁_tr, rhsTail⟩ := + Option.bind_eq_some_iff.mp rhs_tr + obtain ⟨rhs₂', rhs₂_tr, rhs'_eq⟩ := + Option.bind_eq_some_iff.mp rhsTail + change some (.imax lhs₁' lhs₂') = some lhs' at lhs'_eq + change some (.imax rhs₁' rhs₂') = some rhs' at rhs'_eq + injection lhs'_eq with lhs'_eq + injection rhs'_eq with rhs'_eq + subst lhs' + subst rhs' + rw [ih₁ equal.1 lhs₁_tr rhs₁_tr, + ih₂ equal.2 lhs₂_tr rhs₂_tr] + | zero | succ _ | max _ _ | param _ | mvar _ => + simp_all [levelStructEq] + | param lhsName => + cases rhs <;> simp_all [levelStructEq, VLevel.ofLevel] + | mvar lhsId => + simp [VLevel.ofLevel] at lhs_tr + +/-- The transparent fast path used by constructor validation implies the +Theory universe inequality required by `fieldsWF`. -/ +theorem levelStructGe_ofLevel + (greater : levelStructGe result field = true) + (result_tr : VLevel.ofLevel Us result = some result') + (field_tr : VLevel.ofLevel Us field = some field') : + field' ≤ result' := by + induction result generalizing field result' field' with + | zero => + cases field with + | zero => + change some (.zero : VLevel) = some result' at result_tr + change some (.zero : VLevel) = some field' at field_tr + injection result_tr with result_tr + injection field_tr with field_tr + subst result' + subst field' + exact VLevel.le_refl _ + | succ _ | max _ _ | imax _ _ | param _ | mvar _ => + simp_all [levelStructGe, levelStructEq] + | succ result ih => + cases field with + | zero => + change some (.zero : VLevel) = some field' at field_tr + injection field_tr with field_tr + subst field' + exact VLevel.zero_le + | succ field => + simp only [levelStructGe] at greater + simp only [VLevel.ofLevel, Option.bind_eq_bind] at result_tr field_tr + obtain ⟨result'', result_inner_tr, result'_eq⟩ := + Option.bind_eq_some_iff.mp result_tr + obtain ⟨field'', field_inner_tr, field'_eq⟩ := + Option.bind_eq_some_iff.mp field_tr + change some result''.succ = some result' at result'_eq + change some field''.succ = some field' at field'_eq + injection result'_eq with result'_eq + injection field'_eq with field'_eq + subst result' + subst field' + exact VLevel.succ_le_succ + (ih greater result_inner_tr field_inner_tr) + | max _ _ | imax _ _ | param _ | mvar _ => + have equal := levelStructEq_ofLevel (Us := Us) + (by simpa [levelStructGe] using greater) result_tr field_tr + cases equal + exact VLevel.le_refl _ + | max result₁ result₂ ih₁ ih₂ => + cases field with + | zero => + change some (.zero : VLevel) = some field' at field_tr + injection field_tr with field_tr + subst field' + exact VLevel.zero_le + | succ _ | max _ _ | imax _ _ | param _ | mvar _ => + have equal := levelStructEq_ofLevel (Us := Us) + (by simpa [levelStructGe] using greater) result_tr field_tr + cases equal + exact VLevel.le_refl _ + | imax result₁ result₂ ih₁ ih₂ => + cases field with + | zero => + change some (.zero : VLevel) = some field' at field_tr + injection field_tr with field_tr + subst field' + exact VLevel.zero_le + | succ _ | max _ _ | imax _ _ | param _ | mvar _ => + have equal := levelStructEq_ofLevel (Us := Us) + (by simpa [levelStructGe] using greater) result_tr field_tr + cases equal + exact VLevel.le_refl _ + | param resultName => + cases field with + | zero => + change some (.zero : VLevel) = some field' at field_tr + injection field_tr with field_tr + subst field' + exact VLevel.zero_le + | succ _ | max _ _ | imax _ _ | param _ | mvar _ => + have equal := levelStructEq_ofLevel (Us := Us) + (by simpa [levelStructGe] using greater) result_tr field_tr + cases equal + exact VLevel.le_refl _ + | mvar resultId => + simp [VLevel.ofLevel] at result_tr + +/-- The impredicative fallback is exact: a kernel level recognized as zero +translates to Theory's zero level. -/ +theorem ofLevel_eq_zero_of_isZero + (zero : level.isZero = true) + (level_tr : VLevel.ofLevel Us level = some level') : + level' = .zero := by + cases level <;> simp_all [Level.isZero, VLevel.ofLevel] + +/-- Executable universe comparison supported by the semantic proof. + +The structural and impredicative `Prop` branches mirror the ordinary +validator directly. A normalized non-`Prop` comparison is admitted only +when Lean's ordinary `Level.geq` decision and the verified project `geq'` +decision both succeed. The former preserves the kernel-facing acceptance +boundary; the latter supplies the semantic inequality without trusting +Lean's opaque normalizer. -/ +def constructorUniverseSemanticGe (resultLevel fieldLevel : Level) : Bool := + levelStructGe resultLevel fieldLevel || + (resultLevel.isZero || + (resultLevel.geq fieldLevel && resultLevel.geq' fieldLevel)) + +/-- Replay just the universe-bearing part of one constructor telescope. + +The traversal deliberately follows the validator's parameter substitution, +ordinary-field local contexts, annotation consumption, and recursion fuel. +Unlike `checkConstructorType`, its normalized fallback also requires the +proved project comparison above. Running this audit in addition to the +ordinary validator is therefore an executable verified intersection, not a +replacement validator and not a proof-only semantic premise. -/ +def checkConstructorUniverseSemantics (stats : InductiveStats) (t : Expr) : + M Unit := do + loop t 0 (← readThe Context).fuel.inductiveFuel +where + loop (t : Expr) (i : Nat) : Nat → M Unit + | 0 => throw .deepRecursion + | fuel + 1 => do + if let .forallE name domain body binderInfo := t then + if let some parameter := stats.params[i]? then + loop (body.instantiate1 parameter) (i + 1) fuel + else + let sortResult ← ensureType domain + unless constructorUniverseSemanticGe stats.resultLevel + sortResult.sortLevel! do + throw <| .other + "constructor universe lies outside the verified semantic subset" + withLocalDecl name binderInfo (consumeTypeAnnotations domain) fun arg => + loop (body.instantiate1 arg) (i + 1) fuel + +/-- Source-ordered universe audit for every constructor in one singleton +family. Constructor names and types come from the same indexed source list +as `ConstructorListValidationTrace`; all non-universe validation remains in +that retained ordinary trace. -/ +def checkConstructorUniverseListSemantics (stats : InductiveStats) : + List Constructor → M Unit + | [] => pure () + | constructor :: constructors => do + checkConstructorUniverseSemantics stats constructor.type + checkConstructorUniverseListSemantics stats constructors + +/-- The executable semantic decision at the exact universe node retained by +ordinary constructor validation. -/ +def ConstructorUniverseTrace.semantic + (_trace : ConstructorUniverseTrace resultLevel fieldLevel) : Bool := + constructorUniverseSemanticGe resultLevel fieldLevel + +/-- Conjunction of the semantic universe decisions in one exact constructor +telescope. Parameter and terminal nodes contribute no universe obligation. -/ +def ConstructorTypeValidationTrace.universeSemantics + (trace : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source argIdx fuel) : Bool := + match trace with + | .parameter _ _ _ _ _ _ _ _ _ _ _ _ tail => + tail.universeSemantics + | .ordinary _ _ _ _ _ _ _ _ _ _ universeTrace _ tail => + universeTrace.semantic && tail.universeSemantics + | .terminal _ _ _ _ _ _ => true + +/-- Source-ordered conjunction of every constructor telescope's semantic +universe decisions. -/ +def ConstructorListValidationTrace.universeSemantics + (trace : ConstructorListValidationTrace stats isUnsafe familyIdx + context seen constructors) : Bool := + match trace with + | .nil _ => true + | .cons _ _ _ _ _ _ typeTrace tailTrace => + typeTrace.universeSemantics && tailTrace.universeSemantics + +/-- The complete semantic-universe gate attached to one retained ordinary +constructor-validation run. -/ +def ConstructorValidationRun.universeSemantics + (validation : ConstructorValidationRun indType stats isUnsafe context) : + Bool := + validation.trace.universeSemantics + +/-- Recover the state-bearing verified-checker execution erased by the +ordinary constructor validator's `ensureType` observation. -/ +theorem ConstructorEnsureTypeStep.innerRun + (step : ConstructorEnsureTypeStep) (hvalid : step.Valid) : + ∃ state : TypeChecker.State, + TypeChecker.ensureType step.source step.context.toTypeChecker + ({} : TypeChecker.State) = .ok (step.result, state) := by + unfold ConstructorEnsureTypeStep.Valid TypeChecker.M.run at hvalid + cases hrun : TypeChecker.ensureType step.source + { env := step.context.env + lctx := step.context.lctx + safety := step.context.safety + lparams := step.context.lparams + fuel := step.context.fuel } + ({} : TypeChecker.State) with + | error err => + simp [StateT.run', Functor.map, Except.map, hrun] at hvalid + | ok pair => + rcases pair with ⟨result, state⟩ + have result_eq : result = step.result := by + simpa [StateT.run', Functor.map, Except.map, hrun] using hvalid + subst result + exact ⟨state, by simpa [Context.toTypeChecker] using hrun⟩ + +/-- Theory interpretation of the exact `ensureType` execution retained for +an ordinary constructor field. -/ +structure TypeChecker.EnsureTypeRun (env : VEnv) (Us : List Name) + (Δ : VLCtx) (source result : Expr) (source' : VExpr) where + context : VContext + venv_eq : context.venv = env + lparams_eq : context.lparams = Us + vlctx_eq : context.vlctx = Δ + state_wf : VState.WF context {} + source_tr : TrExprS env Us Δ source source' + resultLevel : Level + resultLevel' : VLevel + result_eq : result = .sort resultLevel + resultLevel_tr : VLevel.ofLevel Us resultLevel = some resultLevel' + source_type : env.HasType Us.length Δ.toCtx source' (.sort resultLevel') + run_eq : ∃ state : State, + ensureType source context.toContext ({} : State) = .ok (result, state) + +/-- Attach verified Theory meaning to one retained ordinary-field +`ensureType` step in its exact post-family context. -/ +theorem TypeChecker.EnsureTypeRun.exists_ofConstructorStep + (step : ConstructorEnsureTypeStep) (hvalid : step.Valid) + (contextRun : CandidateContextRun step.context) + (source' : VExpr) + (source_tr : contextRun.context.TrExprS step.source source') : + Nonempty (EnsureTypeRun contextRun.context.venv + contextRun.context.lparams contextRun.context.vlctx + step.source step.result source') := by + obtain ⟨state, run⟩ := step.innerRun hvalid + rw [← contextRun.context_eq] at run + obtain ⟨_, _, _, _, translated, translated_tr, u, u', result_eq, + level_tr, source_type⟩ := + (ensureType.WF source_tr) contextRun.state_wf step.result state run + have translated_def := translated_tr.uniq contextRun.context.Ewf + (.refl contextRun.context.Ewf contextRun.context.Δwf) source_tr + have source_type' := source_type.defeqU_l contextRun.context.Ewf + contextRun.context.Δwf translated_def + exact ⟨{ + context := contextRun.context + venv_eq := rfl + lparams_eq := rfl + vlctx_eq := rfl + state_wf := contextRun.state_wf + source_tr := source_tr + resultLevel := u + resultLevel' := u' + result_eq := result_eq + resultLevel_tr := level_tr + source_type := source_type' + run_eq := ⟨state, run⟩ }⟩ + +/-! +## Exact post-family view alignment + +Constructor validation and constructor-candidate construction deliberately +allocate their ordinary-field free variables from different reader states. +The following audit therefore never compares those identifiers. It follows +the already-retained validation trace and instantiates the exact candidate +view with the *validation* parameter/field expression at each position. +Every comparison consequently happens in the real post-family local context, +while the candidate itself still owns the view being inspected. +-/ + +/-- Number of parameter/ordinary binders consumed by one exact constructor +validation trace. -/ +def ConstructorTypeValidationTrace.spineLength + (trace : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source argIdx fuel) : Nat := + match trace with + | .parameter _ _ _ _ _ _ _ _ _ _ _ _ tail => tail.spineLength + 1 + | .ordinary _ _ _ _ _ _ _ _ _ _ _ _ tail => tail.spineLength + 1 + | .terminal .. => 0 + +/-- Every local declaration entered by a retained positivity traversal uses +an identifier absent from the exact incoming local context. This is an +executable structural condition, not a semantic premise. -/ +def ConstructorPositivityTrace.freshNames + (trace : ConstructorPositivityTrace stats ctor argIdx context source fuel) : + Bool := + match trace with + | .absent .. => true + | .forallE context _ _ _ _ _ _ _ _ _ tail => + (context.lctx.find? context.freshFVarId).isNone && tail.freshNames + | .target .. => true + +def ConstructorPositivityModeTrace.freshNames + (trace : ConstructorPositivityModeTrace stats isUnsafe ctor argIdx + context source) : Bool := + match trace with + | .skipped .. => true + | .safe _ trace => trace.freshNames + +/-- One exact full-check observation together with the syntactic premise used +by the verified checker refinement. Keeping the premise at the operational +boundary avoids reconstructing free-variable membership later from binder +names or from a parallel telescope. -/ +structure ConstructorCheckedExpr (context : Context) (source : Expr) where + fvars : source.FVarsIn + (fun fv => (context.lctx.find? fv).isSome = true) + observation : CandidateCheckTypeObservation context source + +/-- Retain an already verified full-check execution together with a structural +scope proof for its exact source expression. -/ +def ConstructorCheckedExpr.ofRun + (fvars : source.FVarsIn + (fun fv => (context.lctx.find? fv).isSome = true)) + (run : CandidateCheckTypeStep.Valid ⟨context, source, inferred⟩) : + ConstructorCheckedExpr context source := + ⟨fvars, ⟨inferred, run⟩⟩ + +private theorem constructorClosed_hasMVar_false + {env : Kernel.Environment} {name : Name} {source : Expr} + (closed : env.checkNoMVarNoFVar name source = .ok ()) : + source.hasMVar = false := by + unfold Kernel.Environment.checkNoMVarNoFVar + Kernel.Environment.checkNoMVar Kernel.Environment.checkNoFVar at closed + cases hmvars : source.hasMVar + · rfl + · simp [hmvars, Bind.bind, Except.bind] at closed + +private theorem constructorClosed_hasFVar_false + {env : Kernel.Environment} {name : Name} {source : Expr} + (closed : env.checkNoMVarNoFVar name source = .ok ()) : + source.hasFVar = false := by + have hmvars := constructorClosed_hasMVar_false closed + unfold Kernel.Environment.checkNoMVarNoFVar + Kernel.Environment.checkNoMVar Kernel.Environment.checkNoFVar at closed + cases hfvars : source.hasFVar + · rfl + · simp [hmvars, hfvars, Bind.bind, Except.bind, Pure.pure, + Except.pure] at closed + +/-- Constructor metadata accepted by the validator is closed, so its retained +empty-context full-check observation directly supplies the D2 root scope. -/ +def ConstructorCheckedExpr.ofClosedRoot + {context : Context} {name : Name} {source : Expr} + (closed : context.env.checkNoMVarNoFVar name source = .ok ()) + (observation : CandidateCheckTypeObservation + context.withEmptyLocalContext source) : + ConstructorCheckedExpr context.withEmptyLocalContext source where + fvars := fvarsIn_iff.2 ⟨by + intro fv present + have empty := fvarsList_eq_nil.mpr + (constructorClosed_hasFVar_false closed) + rw [empty] at present + contradiction, + fvarsIn_iff_hasMVar.2 (constructorClosed_hasMVar_false closed)⟩ + observation := observation + +/-- Execute a full check only after confirming that every free variable of +the exact source belongs to the retained implementation context. -/ +def checkConstructorAlignedExpr (context : Context) (source : Expr) : + Except Exception (ConstructorCheckedExpr context source) := do + if hfvars : source.fvarsList.all + (fun fv => (context.lctx.find? fv).isSome) = true then + if hmvars : source.hasMVar = false then + have fvars : source.FVarsIn + (fun fv => (context.lctx.find? fv).isSome = true) := + fvarsIn_iff.2 ⟨by + intro fv hfv + have h := List.all_eq_true.mp hfvars fv hfv + exact h, + fvarsIn_iff_hasMVar.2 hmvars⟩ + let observation ← observeCandidateCheckType context source + pure ⟨fvars, observation⟩ + else + throw <| .other "constructor alignment source contains a metavariable" + else + throw <| .other "constructor alignment source escaped its local context" + +/-- Reuse an exact full-check execution to expose the successful result of +the supplemental scope-aware checker boundary. -/ +theorem checkConstructorAlignedExpr.exists_of_run + (hfvars : source.fvarsList.all + (fun fv => (context.lctx.find? fv).isSome) = true) + (hmvars : source.hasMVar = false) + (hrun : CandidateCheckTypeStep.Valid ⟨context, source, inferred⟩) : + ∃ checked : ConstructorCheckedExpr context source, + checkConstructorAlignedExpr context source = .ok checked := by + unfold checkConstructorAlignedExpr + rw [dif_pos hfvars, dif_pos hmvars] + rw [observeCandidateCheckType_of_run context source inferred hrun] + exact ⟨_, rfl⟩ + +/-- Re-executing the supplemental scope/full-check boundary reproduces any +retained successful observation. The only reconstructed fields are proofs, +so proof irrelevance identifies the executable result with the retained +value itself. -/ +theorem ConstructorCheckedExpr.check_eq + (checked : ConstructorCheckedExpr context source) : + checkConstructorAlignedExpr context source = .ok checked := by + have hfvars : source.fvarsList.all + (fun fv => (context.lctx.find? fv).isSome) = true := by + apply List.all_eq_true.mpr + intro fv present + exact (fvarsIn_iff.mp checked.fvars).1 fv present + have hmvars : source.hasMVar = false := + fvarsIn_iff_hasMVar.mp (fvarsIn_iff.mp checked.fvars).2 + unfold checkConstructorAlignedExpr + rw [dif_pos hfvars, dif_pos hmvars] + rw [observeCandidateCheckType_of_run context source + checked.observation.inferred checked.observation.valid] + cases checked + rfl + +/-- Determinism of the erased implementation run pins a retained full-check +observation to any independently proved result of that same exact check. -/ +theorem ConstructorCheckedExpr.inferred_eq_of_run + (checked : ConstructorCheckedExpr context source) + (hrun : CandidateCheckTypeStep.Valid ⟨context, source, inferred⟩) : + checked.observation.inferred = inferred := by + have retained := checked.observation.valid + unfold CandidateCheckTypeStep.Valid at retained hrun + rw [hrun] at retained + exact (Except.ok.inj retained).symm + +/-- Re-executing a retained equality observation returns that same +observation; its sole field is proof-valued. -/ +theorem CandidateIsDefEqObservation.observe_eq + (observation : CandidateIsDefEqObservation context lhs rhs) : + observeCandidateIsDefEq context lhs rhs = .ok observation := by + rw [observeCandidateIsDefEq_of_run context lhs rhs observation.valid] + +/-- The verified implementation equality checker accepts a syntactically +identical pair without inspecting or extending its success cache. -/ +theorem candidateIsDefEqRefl + (context : Context) (source : Expr) : + CandidateIsDefEqStep.Valid ⟨context, source, source⟩ := by + unfold CandidateIsDefEqStep.Valid TypeChecker.M.run TypeChecker.isDefEq + TypeChecker.RecM.run TypeChecker.Inner.isDefEq + simp [readThe, MonadReaderOf.read, ReaderT.read, ReaderT.bind, Bind.bind, + StateT.bind, Except.bind, StateT.pure, Except.pure, Pure.pure, + StateT.run', Functor.map, Except.map] + rfl + +/-- Convert the executable implementation-context scope check to the exact +Theory free-variable premise used by verified `checkType` refinement. -/ +theorem ConstructorCheckedExpr.fvarsIn + (checked : ConstructorCheckedExpr context source) + (contextRun : TypeChecker.CandidateContextRun context) : + source.FVarsIn (· ∈ contextRun.context.vlctx.fvars) := by + apply checked.fvars.mono + intro fv present + have implementationPresent : + ∃ declaration, context.lctx.find? fv = some declaration := by + simpa only [Option.isSome_iff_exists] using present + have verifiedPresent : + ∃ declaration, contextRun.context.lctx.find? fv = some declaration := by + simpa only [contextRun.context_lctx] using implementationPresent + apply contextRun.context.trlctx.find?_eq_some.mp + change ∃ declaration, + contextRun.context.mlctx.lctx.find? fv = some declaration + rw [contextRun.context.lctx_eq] + exact verifiedPresent + +/-- Verified meaning of one full check retained by the alignment audit. Both +Theory endpoints are selected by the checker refinement; callers cannot +substitute an unrelated translation for the exact source position. -/ +structure ConstructorCheckedExpr.Run + (checked : ConstructorCheckedExpr context source) + (contextRun : TypeChecker.CandidateContextRun context) where + source' : VExpr + inferred' : VExpr + check : TypeChecker.CheckTypeRun contextRun.context.venv + contextRun.context.lparams contextRun.context.vlctx source + checked.observation.inferred source' inferred' + +/-- Interpret an aligned full-check observation in its exact verified +post-family context. -/ +theorem ConstructorCheckedExpr.Run.exists + (checked : ConstructorCheckedExpr context source) + (contextRun : TypeChecker.CandidateContextRun context) : + Nonempty (ConstructorCheckedExpr.Run checked contextRun) := by + obtain ⟨source', inferred', ⟨check⟩⟩ := + TypeChecker.CheckTypeRun.exists_ofCandidateStepFVars + ⟨context, source, checked.observation.inferred⟩ + checked.observation.valid contextRun (checked.fvarsIn contextRun) + exact ⟨⟨source', inferred', check⟩⟩ + +/-- A retained full check whose implementation result is syntactically a +sort supplies the exact Theory `IsType` premise needed to extend the local +context. -/ +theorem ConstructorCheckedExpr.Run.isType_of_inferredSort + (run : ConstructorCheckedExpr.Run checked contextRun) + (inferred_eq : checked.observation.inferred = .sort u) : + contextRun.context.IsType run.source' := by + rcases run with ⟨source', inferred', check⟩ + have inferred_tr := check.inferred_tr + rw [inferred_eq] at inferred_tr + cases inferred_tr + exact check.isType + +/-- Reuse a proved scope boundary with the exact full-check observation owned +by another retained execution of the same source expression. -/ +def ConstructorCheckedExpr.withObservation + (scope : ConstructorCheckedExpr context source) + (observation : CandidateCheckTypeObservation context source) : + ConstructorCheckedExpr context source := + ⟨scope.fvars, observation⟩ + +/-- Interpret one retained equality observation at the exact Theory +translations selected by the two aligned full checks. -/ +def ConstructorCheckedExpr.Run.isDefEq + {context : Context} {lhs rhs : Expr} + {lhsCheck : ConstructorCheckedExpr context lhs} + {rhsCheck : ConstructorCheckedExpr context rhs} + {contextRun : TypeChecker.CandidateContextRun context} + (lhsRun : ConstructorCheckedExpr.Run lhsCheck contextRun) + (rhsRun : ConstructorCheckedExpr.Run rhsCheck contextRun) + (observation : CandidateIsDefEqObservation context lhs rhs) : + TypeChecker.IsDefEqRun contextRun.context.venv + contextRun.context.lparams contextRun.context.vlctx lhs rhs + lhsRun.source' rhsRun.source' := + TypeChecker.IsDefEqRun.ofCandidateStep + ⟨context, lhs, rhs⟩ observation.valid contextRun.context + contextRun.context_eq rfl rfl rfl contextRun.state_wf + lhsRun.check.expr_tr rhsRun.check.expr_tr context.fuel.recDepth rfl + +/-- A validation-local verified context pinned to the one post-family Theory +environment and universe-parameter list used by the whole constructor list. -/ +structure ConstructorContextRun (env : VEnv) (Us : List Name) + (context : Context) where + candidate : TypeChecker.CandidateContextRun context + venv_eq : candidate.context.venv = env + lparams_eq : candidate.context.lparams = Us + +def ConstructorContextRun.withEmptyLocalContext + (run : ConstructorContextRun env Us context) : + ConstructorContextRun env Us context.withEmptyLocalContext where + candidate := run.candidate.withEmptyLocalContext + venv_eq := run.venv_eq + lparams_eq := run.lparams_eq + +/-- Extend the actual post-family verified context by exactly one retained +validation local declaration. -/ +def ConstructorContextRun.pushLocalDecl + (run : ConstructorContextRun env Us context) + (name : Name) (binderInfo : BinderInfo) (domain : Expr) + (fresh : context.lctx.find? context.freshFVarId = none) + (domain' : VExpr) + (domain_tr : run.candidate.context.TrExprS domain domain') + (domain_type : run.candidate.context.IsType domain') : + ConstructorContextRun env Us + (context.pushLocalDecl name binderInfo domain) := by + let candidate := run.candidate.pushLocalDecl name binderInfo domain fresh + domain' domain_tr domain_type + refine ⟨candidate, ?_, ?_⟩ + · calc + candidate.context.venv = run.candidate.context.venv := by + simp [candidate] + _ = env := run.venv_eq + · calc + candidate.context.lparams = run.candidate.context.lparams := by + simp [candidate, AddInductive.Context.pushLocalDecl] + _ = Us := run.lparams_eq + +/-- Verified interpretation of one exact retained WHNF operation, sharing the +source translation already selected by its aligned full check. -/ +structure ConstructorWhnfRun + {context : Context} {source : Expr} + {sourceCheck : ConstructorCheckedExpr context source} + (contextRun : TypeChecker.CandidateContextRun context) + (sourceRun : ConstructorCheckedExpr.Run sourceCheck contextRun) + (result : Expr) where + result' : VExpr + result_tr : contextRun.context.TrExprS result result' + whnf : TypeChecker.WhnfRun contextRun.context.venv + contextRun.context.lparams contextRun.context.vlctx source result + sourceRun.source' result' + +theorem ConstructorWhnfRun.exists + {context : Context} {source result : Expr} + {sourceCheck : ConstructorCheckedExpr context source} + (contextRun : TypeChecker.CandidateContextRun context) + (sourceRun : ConstructorCheckedExpr.Run sourceCheck contextRun) + (valid : CandidateWhnfStep.Valid ⟨context, source, result⟩) + (recursionFuel : Nat) + (depth : context.fuel.recDepth = recursionFuel + 1) : + Nonempty (ConstructorWhnfRun contextRun sourceRun result) := by + obtain ⟨result', result_tr, ⟨whnf⟩⟩ := + TypeChecker.WhnfRun.exists_ofCandidateStep + ⟨context, source, result⟩ valid contextRun sourceRun.source' + sourceRun.check.expr_tr recursionFuel depth + exact ⟨⟨result', result_tr, whnf⟩⟩ + +/-- Retained operational evidence for the supplemental nested-positivity +alignment. The ordinary positivity trace continues to own every WHNF, +occurrence decision, and accepted recursive target. This trace adds the full +checks and annotation equality needed to interpret those exact operations in +the verified checker. -/ +inductive ConstructorPositivityAlignmentTrace : + {stats : InductiveStats} → {ctor : Name} → {argIdx : Nat} → + {context : Context} → {source : Expr} → {fuel : Nat} → + ConstructorPositivityTrace stats ctor argIdx context source fuel → Type where + | absent + (sourceCheck : ConstructorCheckedExpr context source) : + ConstructorPositivityAlignmentTrace + (.absent context source result fuel whnfStep occurs) + | forallE + (sourceCheck : ConstructorCheckedExpr context source) + (domainCheck : ConstructorCheckedExpr context domain) + (consumedCheck : ConstructorCheckedExpr context + (consumeTypeAnnotations domain)) + (consumedLevel : Level) + (consumedInferred : consumedCheck.observation.inferred = + .sort consumedLevel) + (fresh : context.lctx.find? context.freshFVarId = none) + (annotations : CandidateIsDefEqObservation context domain + (consumeTypeAnnotations domain)) + (tailTrace : ConstructorPositivityTrace stats ctor argIdx + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) fuel) + (tail : ConstructorPositivityAlignmentTrace tailTrace) : + ConstructorPositivityAlignmentTrace + (.forallE context source fuel name domain body binderInfo whnfStep occurs + domainFree tailTrace) + | target + (sourceCheck : ConstructorCheckedExpr context source) : + ConstructorPositivityAlignmentTrace + (.target context source result fuel targetIdx whnfStep occurs terminal + valid) + +namespace ConstructorPositivityAlignmentTrace + +def build : + (positivityTrace : ConstructorPositivityTrace stats ctor argIdx context + source fuel) → + Except Exception (ConstructorPositivityAlignmentTrace positivityTrace) + | .absent context source _ fuel whnfStep occurs => do + let sourceCheck ← checkConstructorAlignedExpr context source + pure <| .absent sourceCheck + | .forallE context source fuel _ domain _ _ _ occurs + domainFree tailTrace => do + let sourceCheck ← checkConstructorAlignedExpr context source + let domainCheck ← checkConstructorAlignedExpr context domain + let consumedCheck ← checkConstructorAlignedExpr context + (consumeTypeAnnotations domain) + match consumedInferred : consumedCheck.observation.inferred with + | .sort consumedLevel => + if fresh : context.lctx.find? context.freshFVarId = none then + let annotations ← observeCandidateIsDefEq context domain + (consumeTypeAnnotations domain) + let tail ← build tailTrace + pure <| .forallE sourceCheck domainCheck consumedCheck consumedLevel + consumedInferred fresh annotations tailTrace tail + else + throw <| .other "positivity traversal reused a local identifier" + | _ => + throw <| .other + "consumed positivity domain did not check as a type" + | .target context source _ fuel _ _ occurs terminal + valid => do + let sourceCheck ← checkConstructorAlignedExpr context source + pure <| .target sourceCheck + +/-- Executable erasure of the retained positivity alignment. -/ +def check (positivityTrace : ConstructorPositivityTrace stats ctor argIdx + context source fuel) : M Unit := fun _ => + (ConstructorPositivityAlignmentTrace.build positivityTrace).map fun _ => () + +theorem nonempty_of_check + {positivityTrace : ConstructorPositivityTrace stats ctor argIdx context + source fuel} + (success : ConstructorPositivityAlignmentTrace.check positivityTrace + context = .ok ()) : + Nonempty (ConstructorPositivityAlignmentTrace positivityTrace) := by + unfold check at success + cases h : ConstructorPositivityAlignmentTrace.build positivityTrace with + | error error => + rw [h] at success + change Except.error error = Except.ok () at success + contradiction + | ok alignment => exact ⟨alignment⟩ + +end ConstructorPositivityAlignmentTrace + +inductive ConstructorPositivityModeAlignmentTrace : + {stats : InductiveStats} → {isUnsafe : Bool} → {ctor : Name} → + {argIdx : Nat} → {context : Context} → {source : Expr} → + ConstructorPositivityModeTrace stats isUnsafe ctor argIdx context source → + Type where + | skipped : ConstructorPositivityModeAlignmentTrace (.skipped unsafeEq) + | safe (alignment : ConstructorPositivityAlignmentTrace positivityTrace) : + ConstructorPositivityModeAlignmentTrace (.safe unsafeEq positivityTrace) + +namespace ConstructorPositivityModeAlignmentTrace + +def build : + (positivityTrace : ConstructorPositivityModeTrace stats isUnsafe ctor + argIdx context source) → + Except Exception + (ConstructorPositivityModeAlignmentTrace positivityTrace) + | .skipped _ => pure <| .skipped + | .safe _ positivityTrace => do + let alignment ← ConstructorPositivityAlignmentTrace.build + positivityTrace + pure <| .safe alignment + +def check (positivityTrace : ConstructorPositivityModeTrace stats isUnsafe + ctor argIdx context source) : M Unit := fun _ => + (ConstructorPositivityModeAlignmentTrace.build positivityTrace).map fun _ => () + +theorem nonempty_of_check + {positivityTrace : ConstructorPositivityModeTrace stats isUnsafe ctor + argIdx context source} + (success : ConstructorPositivityModeAlignmentTrace.check positivityTrace + context = .ok ()) : + Nonempty (ConstructorPositivityModeAlignmentTrace positivityTrace) := by + unfold check at success + cases h : ConstructorPositivityModeAlignmentTrace.build positivityTrace with + | error error => + rw [h] at success + change Except.error error = Except.ok () at success + contradiction + | ok alignment => exact ⟨alignment⟩ + +end ConstructorPositivityModeAlignmentTrace + +/-- Exact operational alignment between one retained validation telescope and +the analyzer-owned kernel view. The recursive indices instantiate the view +with validation-owned parameters and fields, so no equality between the +producer's and validator's fresh free-variable identifiers is assumed. -/ +inductive ConstructorViewAlignmentTrace : + {stats : InductiveStats} → {isUnsafe : Bool} → {familyIdx : Nat} → + {ctor : Name} → {context : Context} → {source : Expr} → + {argIdx fuel : Nat} → + ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor context + source argIdx fuel → + (view : Expr) → Type where + | parameter + (domainCheck : ConstructorCheckedExpr context domain) + (viewDomainCheck : ConstructorCheckedExpr context viewDomain) + (parameterTypeCheck : ConstructorCheckedExpr context parameterType) + (parameterShape : param = .fvar fv) + (parameterPresent : (context.lctx.find? fv).isSome = true) + (tailTrace : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context (body.instantiate1 param) (argIdx + 1) fuel) + (tail : ConstructorViewAlignmentTrace tailTrace + (viewBody.instantiate1 param)) : + ConstructorViewAlignmentTrace + (.parameter context fuel argIdx name domain body binderInfo param + parameterType parameterAt parameterTypeRun validationDefEq tailTrace) + (.forallE viewName viewDomain viewBody viewBinderInfo) + | ordinary + (domainCheck : ConstructorCheckedExpr context domain) + (viewDomainCheck : ConstructorCheckedExpr context viewDomain) + (viewEquality : CandidateIsDefEqObservation context domain viewDomain) + (consumedCheck : ConstructorCheckedExpr context + (consumeTypeAnnotations domain)) + (positivityTrace : ConstructorPositivityModeTrace stats isUnsafe ctor + argIdx context domain) + (positivityAlignment : + ConstructorPositivityModeAlignmentTrace positivityTrace) + (fresh : context.lctx.find? context.freshFVarId = none) + (annotations : CandidateIsDefEqObservation context domain + (consumeTypeAnnotations domain)) + (tailTrace : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) (argIdx + 1) fuel) + (tail : ConstructorViewAlignmentTrace tailTrace + (viewBody.instantiate1 context.freshExpr)) : + ConstructorViewAlignmentTrace + (.ordinary context fuel argIdx name domain body binderInfo sortResult + noParameter ensureTypeStep universeTrace positivityTrace tailTrace) + (.forallE viewName viewDomain viewBody viewBinderInfo) + | terminal + (sourceCheck : ConstructorCheckedExpr context source) + (viewCheck : ConstructorCheckedExpr context view) + (viewTerminal : view.isForall = false) + (viewValid : isValidIndAppIdx stats view familyIdx = true) : + ConstructorViewAlignmentTrace + (.terminal context source fuel argIdx sourceTerminal sourceValid) view + +namespace ConstructorViewAlignmentTrace + +/-- Execute the exact component audit and retain every successful checker +observation. This is intentionally not a second `checkConstructors` run: the +raw branch shape, parameter selection, universe decision, positivity target, +and raw terminal acceptance remain owned by the ordinary validation trace. -/ +def build : + (validationTrace : ConstructorTypeValidationTrace stats isUnsafe familyIdx + ctor context source argIdx fuel) → + (view : Expr) → + Except Exception (ConstructorViewAlignmentTrace validationTrace view) + | .parameter context parameterFuel parameterArgIdx parameterName domain + parameterBody parameterBinderInfo param parameterType _ + _ _ tailTrace, + .forallE viewName viewDomain viewBody viewBinderInfo => do + let domainCheck ← checkConstructorAlignedExpr context domain + let viewDomainCheck ← checkConstructorAlignedExpr context viewDomain + let parameterTypeCheck ← checkConstructorAlignedExpr context + parameterType + match hparam : param with + | .fvar fv => + if parameterPresent : (context.lctx.find? fv).isSome = true then + let tail ← build tailTrace + (viewBody.instantiate1 param) + pure <| .parameter domainCheck viewDomainCheck parameterTypeCheck + hparam parameterPresent tailTrace tail + else + throw <| .other + "constructor parameter is absent from validation context" + | _ => + throw <| .other "constructor parameter is not a local variable" + | .parameter .., _ => + throw <| .other "candidate and validation constructor telescopes differ" + | .ordinary context ordinaryFuel ordinaryArgIdx name domain body binderInfo + _ _ _ _ positivityTrace tailTrace, + .forallE viewName viewDomain viewBody viewBinderInfo => do + let domainCheck ← checkConstructorAlignedExpr context domain + let viewDomainCheck ← checkConstructorAlignedExpr context viewDomain + let viewEquality ← observeCandidateIsDefEq context domain viewDomain + let consumedCheck ← checkConstructorAlignedExpr context + (consumeTypeAnnotations domain) + let positivityAlignment ← + ConstructorPositivityModeAlignmentTrace.build positivityTrace + if fresh : context.lctx.find? context.freshFVarId = none then + let annotations ← observeCandidateIsDefEq context domain + (consumeTypeAnnotations domain) + let tail ← build tailTrace + (viewBody.instantiate1 context.freshExpr) + pure <| .ordinary domainCheck viewDomainCheck viewEquality consumedCheck + positivityTrace positivityAlignment fresh annotations tailTrace tail + else + throw <| .other "constructor validation reused a local identifier" + | .ordinary .., _ => + throw <| .other "candidate and validation constructor telescopes differ" + | .terminal context source fuel argIdx sourceTerminal sourceValid, view => do + let sourceCheck ← checkConstructorAlignedExpr context source + let viewCheck ← checkConstructorAlignedExpr context view + if viewTerminal : view.isForall = false then + if viewValid : isValidIndAppIdx stats view familyIdx = true then + pure <| .terminal sourceCheck viewCheck viewTerminal viewValid + else + throw <| .other + "candidate view changed the terminal family application" + else + throw <| .other "candidate view has an extra constructor field" + +def check (validationTrace : ConstructorTypeValidationTrace stats isUnsafe + familyIdx ctor context source argIdx fuel) (view : Expr) : M Unit := + fun _ => (build validationTrace view).map fun _ => () + +theorem nonempty_of_check + {validationTrace : ConstructorTypeValidationTrace stats isUnsafe familyIdx + ctor context source argIdx fuel} + (success : check validationTrace view context = .ok ()) : + Nonempty (ConstructorViewAlignmentTrace validationTrace view) := by + unfold check at success + cases h : build validationTrace view with + | error error => + rw [h] at success + change Except.error error = Except.ok () at success + contradiction + | ok alignment => exact ⟨alignment⟩ + +end ConstructorViewAlignmentTrace + +/-- Supplemental checker audit for the exact analyzer-owned view of one +constructor. It follows the validation telescope and instantiates the view +with validation-owned locals; successful execution retains no rewritten +declaration and claims no independent view acceptance. -/ +def ConstructorTypeValidationTrace.checkViewAlignment + (trace : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source argIdx fuel) (view : Expr) : M Unit := + ConstructorViewAlignmentTrace.check trace view + +/-- Source-ordered retained alignment for the complete constructor list. +The source indices make omission, duplication, and reordering unrepresentable. +Each node also retains the closed-root scope check needed to interpret the +validator's original empty-local-context `checkType` observation. -/ +inductive ConstructorCandidateAlignmentTrace + (stats : InductiveStats) (isUnsafe : Bool) (familyIdx : Nat) + (context : Context) : + {seen : NameSet} → {constructors : List Constructor} → + (validationTrace : ConstructorListValidationTrace stats isUnsafe familyIdx + context seen constructors) → + AddInductive.CandidateList AddInductive.CandidateConstructor constructors → + Type where + | nil (seen : NameSet) : + ConstructorCandidateAlignmentTrace stats isUnsafe familyIdx context + (.nil seen) .nil + | cons + {seen : NameSet} {head : Constructor} {tail : List Constructor} + {fresh : seen.contains head.name = false} + {closed : context.env.checkNoMVarNoFVar head.name head.type = .ok ()} + {rootCheck : CandidateCheckTypeObservation + context.withEmptyLocalContext head.type} + {typeTrace : ConstructorTypeValidationTrace stats isUnsafe familyIdx + head.name context head.type 0 context.fuel.inductiveFuel} + {tailTrace : ConstructorListValidationTrace stats isUnsafe familyIdx + context (seen.insert head.name) tail} + {candidate : AddInductive.CandidateConstructor head} + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor tail} + (rootScope : ConstructorCheckedExpr context.withEmptyLocalContext + head.type) + (storedSpine : candidate.type.trace.storedSpine = true) + (spineLength : candidate.type.trace.spineLength = + typeTrace.spineLength) + (candidateDepth : candidate.type.context.fuel.recDepth = + context.fuel.recDepth) + (headAlignment : ConstructorViewAlignmentTrace typeTrace + candidate.type.view) + (tailAlignment : ConstructorCandidateAlignmentTrace stats isUnsafe + familyIdx context tailTrace candidates) : + ConstructorCandidateAlignmentTrace stats isUnsafe familyIdx context + (.cons seen head tail fresh closed rootCheck typeTrace tailTrace) + (.cons candidate candidates) + +namespace ConstructorCandidateAlignmentTrace + +/-- Execute the source-ordered constructor alignment and retain all successful +component checks. The candidate list's dependent source index selects the +same constructor at every recursive position as the validation trace. -/ +def build : + (validationTrace : ConstructorListValidationTrace stats isUnsafe familyIdx + context seen constructors) → + (candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor constructors) → + Except Exception + (ConstructorCandidateAlignmentTrace stats isUnsafe familyIdx context + validationTrace candidates) + | .nil seen, .nil => pure <| .nil seen + | .cons seen head tail _ _ _ typeTrace tailTrace, + .cons candidate candidates => do + let rootScope ← checkConstructorAlignedExpr + context.withEmptyLocalContext head.type + if storedSpine : candidate.type.trace.storedSpine = true then + if spineLength : candidate.type.trace.spineLength = + typeTrace.spineLength then + if candidateDepth : candidate.type.context.fuel.recDepth = + context.fuel.recDepth then + let headAlignment ← ConstructorViewAlignmentTrace.build typeTrace + candidate.type.view + let tailAlignment ← build tailTrace candidates + pure <| .cons rootScope storedSpine spineLength candidateDepth + headAlignment tailAlignment + else + throw <| .other + "candidate and validation checker depths differ" + else + throw <| .other + "candidate and validation constructor lengths differ" + else + throw <| .other + "candidate constructor did not preserve its stored spine" + +def check + (validationTrace : ConstructorListValidationTrace stats isUnsafe familyIdx + context seen constructors) + (candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor constructors) : M Unit := + fun _ => (build validationTrace candidates).map fun _ => () + +theorem nonempty_of_check + {validationTrace : ConstructorListValidationTrace stats isUnsafe familyIdx + context seen constructors} + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor constructors} + (success : check validationTrace candidates context = .ok ()) : + Nonempty (ConstructorCandidateAlignmentTrace stats isUnsafe familyIdx + context validationTrace candidates) := by + unfold check at success + cases h : build validationTrace candidates with + | error error => + rw [h] at success + change Except.error error = Except.ok () at success + contradiction + | ok alignment => exact ⟨alignment⟩ + +/-- A successful alignment audit guarantees the retained builder returns its +trace, so audit owners can replay `build` instead of choosing from +`Nonempty`. -/ +theorem build_ok_of_check + {validationTrace : ConstructorListValidationTrace stats isUnsafe familyIdx + context seen constructors} + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor constructors} + (success : check validationTrace candidates context = .ok ()) : + ∃ alignment, build validationTrace candidates = .ok alignment := by + unfold check at success + cases h : build validationTrace candidates with + | error error => + rw [h] at success + change Except.error error = Except.ok () at success + contradiction + | ok alignment => exact ⟨alignment, rfl⟩ + +end ConstructorCandidateAlignmentTrace + +/-- Source-ordered supplemental alignment audit for every exact constructor +candidate selected by the producer. The dependent list indices rule out +truncation, reordering, duplication, or a view from another source position. +The Boolean gates additionally pin the candidate main-spine length to the +retained validation telescope. -/ +def ConstructorListValidationTrace.checkCandidateAlignment + (trace : ConstructorListValidationTrace stats isUnsafe familyIdx + context seen constructors) + (candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor constructors) : M Unit := + ConstructorCandidateAlignmentTrace.check trace candidates + +/-! +## Verified interpretation of the aligned validation telescope + +The following semantic traces contain only executions refined by the verified +checker. Their indices still carry the ordinary validator's branch choices +and exact candidate view, while `ConstructorContextRun` fixes every recursive +local context to the actual post-family Theory environment. +-/ + +/-- Verified interpretation of every exact WHNF and nested binder visited by +one retained positivity traversal. -/ +inductive ConstructorPositivitySemanticRun + (env : VEnv) (Us : List Name) (whnfFuel : Nat) : + {stats : InductiveStats} → {ctor : Name} → {argIdx : Nat} → + {context : Context} → {source : Expr} → {fuel : Nat} → + (contextRun : ConstructorContextRun env Us context) → + ConstructorPositivityTrace stats ctor argIdx context source fuel → Type where + | absent + {stats : InductiveStats} {ctor : Name} {argIdx : Nat} + {context : Context} {source result : Expr} {fuel : Nat} + {whnfStep : CandidateWhnfStep.Valid ⟨context, source, result⟩} + {occurs : hasIndOcc stats.indConsts result = false} + {sourceCheck : ConstructorCheckedExpr context source} + {contextRun : ConstructorContextRun env Us context} + (depth : context.fuel.recDepth = whnfFuel + 1) + (sourceRun : ConstructorCheckedExpr.Run sourceCheck + contextRun.candidate) + (whnfRun : ConstructorWhnfRun contextRun.candidate sourceRun result) : + ConstructorPositivitySemanticRun env Us whnfFuel contextRun + (.absent context source result fuel whnfStep occurs) + | forallE + {stats : InductiveStats} {ctor : Name} {argIdx : Nat} + {context : Context} {source : Expr} {fuel : Nat} + {name : Name} {domain body : Expr} {binderInfo : BinderInfo} + {whnfStep : CandidateWhnfStep.Valid + ⟨context, source, .forallE name domain body binderInfo⟩} + {occurs : hasIndOcc stats.indConsts + (.forallE name domain body binderInfo) = true} + {domainFree : hasIndOcc stats.indConsts domain = false} + {tailTrace : ConstructorPositivityTrace stats ctor argIdx + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) fuel} + {sourceCheck : ConstructorCheckedExpr context source} + {domainCheck : ConstructorCheckedExpr context domain} + {consumedCheck : ConstructorCheckedExpr context + (consumeTypeAnnotations domain)} + {consumedLevel : Level} + {consumedInferred : consumedCheck.observation.inferred = + .sort consumedLevel} + {fresh : context.lctx.find? context.freshFVarId = none} + {contextRun : ConstructorContextRun env Us context} + (depth : context.fuel.recDepth = whnfFuel + 1) + (sourceRun : ConstructorCheckedExpr.Run sourceCheck + contextRun.candidate) + (whnfRun : ConstructorWhnfRun contextRun.candidate sourceRun + (.forallE name domain body binderInfo)) + (domainRun : ConstructorCheckedExpr.Run domainCheck + contextRun.candidate) + (consumedRun : ConstructorCheckedExpr.Run consumedCheck + contextRun.candidate) + (annotationsRun : TypeChecker.IsDefEqRun + contextRun.candidate.context.venv + contextRun.candidate.context.lparams + contextRun.candidate.context.vlctx domain + (consumeTypeAnnotations domain) domainRun.source' + consumedRun.source') + (consumedType : contextRun.candidate.context.IsType + consumedRun.source') + (tail : ConstructorPositivitySemanticRun env Us whnfFuel + (contextRun.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain) fresh consumedRun.source' + consumedRun.check.expr_tr consumedType) + tailTrace) : + ConstructorPositivitySemanticRun env Us whnfFuel contextRun + (.forallE context source fuel name domain body binderInfo whnfStep + occurs domainFree tailTrace) + | target + {stats : InductiveStats} {ctor : Name} {argIdx : Nat} + {context : Context} {source result : Expr} {fuel targetIdx : Nat} + {whnfStep : CandidateWhnfStep.Valid ⟨context, source, result⟩} + {occurs : hasIndOcc stats.indConsts result = true} + {terminal : result.isForall = false} + {valid : isValidIndApp? stats result = some targetIdx} + {sourceCheck : ConstructorCheckedExpr context source} + {contextRun : ConstructorContextRun env Us context} + (depth : context.fuel.recDepth = whnfFuel + 1) + (sourceRun : ConstructorCheckedExpr.Run sourceCheck + contextRun.candidate) + (whnfRun : ConstructorWhnfRun contextRun.candidate sourceRun result) : + ConstructorPositivitySemanticRun env Us whnfFuel contextRun + (.target context source result fuel targetIdx whnfStep occurs terminal + valid) + +namespace ConstructorPositivitySemanticRun + +theorem nonempty_of_alignment + (contextRun : ConstructorContextRun env Us context) + (depth : context.fuel.recDepth = whnfFuel + 1) + {trace : ConstructorPositivityTrace stats ctor argIdx context source fuel} + (alignment : ConstructorPositivityAlignmentTrace trace) : + Nonempty (ConstructorPositivitySemanticRun env Us whnfFuel contextRun + trace) := by + induction trace with + | absent context source result fuel whnfStep occurs => + cases alignment with + | absent sourceCheck => + obtain ⟨sourceRun⟩ := + ConstructorCheckedExpr.Run.exists sourceCheck contextRun.candidate + obtain ⟨whnfRun⟩ := ConstructorWhnfRun.exists + contextRun.candidate sourceRun whnfStep whnfFuel depth + exact ⟨ConstructorPositivitySemanticRun.absent + (stats := stats) (ctor := ctor) (argIdx := argIdx) + depth sourceRun whnfRun⟩ + | forallE context source fuel name domain body binderInfo whnfStep occurs + domainFree tailTrace ih => + cases alignment with + | forallE sourceCheck domainCheck consumedCheck consumedLevel + consumedInferred fresh annotations _ tailAlignment => + obtain ⟨sourceRun⟩ := + ConstructorCheckedExpr.Run.exists sourceCheck contextRun.candidate + obtain ⟨whnfRun⟩ := ConstructorWhnfRun.exists + contextRun.candidate sourceRun whnfStep whnfFuel depth + obtain ⟨domainRun⟩ := + ConstructorCheckedExpr.Run.exists domainCheck contextRun.candidate + obtain ⟨consumedRun⟩ := + ConstructorCheckedExpr.Run.exists consumedCheck + contextRun.candidate + let annotationsRun := domainRun.isDefEq consumedRun annotations + have consumedType : contextRun.candidate.context.IsType + consumedRun.source' := + consumedRun.isType_of_inferredSort consumedInferred + let tailContext := contextRun.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain) fresh consumedRun.source' + consumedRun.check.expr_tr consumedType + have tailDepth : + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)).fuel.recDepth = + whnfFuel + 1 := by + simpa [AddInductive.Context.pushLocalDecl] using depth + obtain ⟨tail⟩ := ih tailContext tailDepth tailAlignment + exact ⟨ConstructorPositivitySemanticRun.forallE + (stats := stats) (ctor := ctor) (argIdx := argIdx) + (consumedLevel := consumedLevel) + (consumedInferred := consumedInferred) + depth sourceRun whnfRun domainRun consumedRun annotationsRun + consumedType tail⟩ + | target context source result fuel targetIdx whnfStep occurs terminal valid => + cases alignment with + | target sourceCheck => + obtain ⟨sourceRun⟩ := + ConstructorCheckedExpr.Run.exists sourceCheck contextRun.candidate + obtain ⟨whnfRun⟩ := ConstructorWhnfRun.exists + contextRun.candidate sourceRun whnfStep whnfFuel depth + exact ⟨ConstructorPositivitySemanticRun.target + (stats := stats) (ctor := ctor) (argIdx := argIdx) + depth sourceRun whnfRun⟩ + +end ConstructorPositivitySemanticRun + +/-- Verified meaning of the exact safe/unsafe positivity branch retained by +ordinary constructor validation. -/ +inductive ConstructorPositivityModeSemanticRun + (env : VEnv) (Us : List Name) (whnfFuel : Nat) : + {stats : InductiveStats} → {isUnsafe : Bool} → {ctor : Name} → + {argIdx : Nat} → {context : Context} → {source : Expr} → + (contextRun : ConstructorContextRun env Us context) → + ConstructorPositivityModeTrace stats isUnsafe ctor argIdx context source → + Type where + | skipped : + {stats : InductiveStats} → {isUnsafe : Bool} → {ctor : Name} → + {argIdx : Nat} → {context : Context} → {source : Expr} → + {unsafeEq : isUnsafe = true} → + {contextRun : ConstructorContextRun env Us context} → + ConstructorPositivityModeSemanticRun env Us whnfFuel contextRun + (@ConstructorPositivityModeTrace.skipped stats isUnsafe ctor argIdx + context source unsafeEq) + | safe + {stats : InductiveStats} {isUnsafe : Bool} {ctor : Name} + {argIdx : Nat} {context : Context} {source : Expr} + {unsafeEq : isUnsafe = false} + {positivityTrace : ConstructorPositivityTrace stats ctor argIdx context + source context.fuel.inductiveFuel} + {contextRun : ConstructorContextRun env Us context} + (semantic : ConstructorPositivitySemanticRun env Us whnfFuel contextRun + positivityTrace) : + ConstructorPositivityModeSemanticRun env Us whnfFuel contextRun + (@ConstructorPositivityModeTrace.safe stats isUnsafe ctor argIdx + context source unsafeEq positivityTrace) + +namespace ConstructorPositivityModeSemanticRun + +theorem nonempty_of_alignment + (contextRun : ConstructorContextRun env Us context) + (depth : context.fuel.recDepth = whnfFuel + 1) + {trace : ConstructorPositivityModeTrace stats isUnsafe ctor argIdx + context source} + (alignment : ConstructorPositivityModeAlignmentTrace trace) : + Nonempty (ConstructorPositivityModeSemanticRun env Us whnfFuel contextRun + trace) := by + cases alignment with + | @skipped unsafeEq => + exact ⟨ConstructorPositivityModeSemanticRun.skipped + (env := env) (Us := Us) (whnfFuel := whnfFuel) + (stats := stats) (isUnsafe := isUnsafe) (ctor := ctor) + (argIdx := argIdx) (context := context) (source := source) + (unsafeEq := unsafeEq) (contextRun := contextRun)⟩ + | safe positivityAlignment => + obtain ⟨semantic⟩ := + ConstructorPositivitySemanticRun.nonempty_of_alignment contextRun depth + positivityAlignment + exact ⟨.safe semantic⟩ + +end ConstructorPositivityModeSemanticRun + +/-- Verified, componentwise interpretation of one aligned constructor +telescope. The view is followed at validation-owned parameter and field +locals; no equality between candidate and validator fresh identifiers is +required or asserted. -/ +inductive ConstructorViewSemanticRun + (env : VEnv) (Us : List Name) (whnfFuel : Nat) : + {stats : InductiveStats} → {isUnsafe : Bool} → {familyIdx : Nat} → + {ctor : Name} → {context : Context} → {source : Expr} → + {argIdx fuel : Nat} → + (contextRun : ConstructorContextRun env Us context) → + ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor context + source argIdx fuel → + (view : Expr) → Type where + | parameter + {stats : InductiveStats} {isUnsafe : Bool} {familyIdx : Nat} + {ctor : Name} {context : Context} {fuel argIdx : Nat} + {name : Name} {domain body : Expr} {binderInfo : BinderInfo} + {param parameterType : Expr} + {parameterAt : stats.params[argIdx]? = some param} + {parameterTypeGet : getType param context = .ok parameterType} + {validationDefEq : CandidateIsDefEqStep.Valid + ⟨context, domain, parameterType⟩} + {tailTrace : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context (body.instantiate1 param) (argIdx + 1) fuel} + {viewName : Name} {viewDomain viewBody : Expr} + {viewBinderInfo : BinderInfo} + {domainCheck : ConstructorCheckedExpr context domain} + {viewDomainCheck : ConstructorCheckedExpr context viewDomain} + {parameterTypeCheck : ConstructorCheckedExpr context parameterType} + {contextRun : ConstructorContextRun env Us context} + (domainRun : ConstructorCheckedExpr.Run domainCheck + contextRun.candidate) + (viewDomainRun : ConstructorCheckedExpr.Run viewDomainCheck + contextRun.candidate) + (parameterTypeSemantic : ConstructorCheckedExpr.Run parameterTypeCheck + contextRun.candidate) + (validationRun : TypeChecker.IsDefEqRun + contextRun.candidate.context.venv + contextRun.candidate.context.lparams + contextRun.candidate.context.vlctx domain parameterType + domainRun.source' parameterTypeSemantic.source') + (tail : ConstructorViewSemanticRun env Us whnfFuel contextRun tailTrace + (viewBody.instantiate1 param)) : + ConstructorViewSemanticRun env Us whnfFuel contextRun + (.parameter context fuel argIdx name domain body binderInfo param + parameterType parameterAt parameterTypeGet validationDefEq tailTrace) + (.forallE viewName viewDomain viewBody viewBinderInfo) + | ordinary + {stats : InductiveStats} {isUnsafe : Bool} {familyIdx : Nat} + {ctor : Name} {context : Context} {fuel argIdx : Nat} + {name : Name} {domain body : Expr} {binderInfo : BinderInfo} + {sortResult : Expr} {noParameter : stats.params[argIdx]? = none} + {ensureTypeStep : ConstructorEnsureTypeStep.Valid + ⟨context, domain, sortResult⟩} + {universeTrace : ConstructorUniverseTrace stats.resultLevel + sortResult.sortLevel!} + {positivityTrace : ConstructorPositivityModeTrace stats isUnsafe ctor + argIdx context domain} + {tailTrace : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) (argIdx + 1) fuel} + {viewName : Name} {viewDomain viewBody : Expr} + {viewBinderInfo : BinderInfo} + {domainCheck : ConstructorCheckedExpr context domain} + {viewDomainCheck : ConstructorCheckedExpr context viewDomain} + {viewEquality : CandidateIsDefEqObservation context domain viewDomain} + {consumedCheck : ConstructorCheckedExpr context + (consumeTypeAnnotations domain)} + {fresh : context.lctx.find? context.freshFVarId = none} + {contextRun : ConstructorContextRun env Us context} + (domainRun : ConstructorCheckedExpr.Run domainCheck + contextRun.candidate) + (viewDomainRun : ConstructorCheckedExpr.Run viewDomainCheck + contextRun.candidate) + (viewEqualityRun : TypeChecker.IsDefEqRun + contextRun.candidate.context.venv + contextRun.candidate.context.lparams + contextRun.candidate.context.vlctx domain viewDomain + domainRun.source' viewDomainRun.source') + (consumedRun : ConstructorCheckedExpr.Run consumedCheck + contextRun.candidate) + (ensureTypeRun : TypeChecker.EnsureTypeRun + contextRun.candidate.context.venv + contextRun.candidate.context.lparams + contextRun.candidate.context.vlctx domain sortResult + domainRun.source') + (positivity : ConstructorPositivityModeSemanticRun env Us whnfFuel + contextRun positivityTrace) + (annotationsRun : TypeChecker.IsDefEqRun + contextRun.candidate.context.venv + contextRun.candidate.context.lparams + contextRun.candidate.context.vlctx domain + (consumeTypeAnnotations domain) domainRun.source' + consumedRun.source') + (consumedType : contextRun.candidate.context.IsType + consumedRun.source') + (tail : ConstructorViewSemanticRun env Us whnfFuel + (contextRun.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain) fresh consumedRun.source' + consumedRun.check.expr_tr consumedType) + tailTrace (viewBody.instantiate1 context.freshExpr)) : + ConstructorViewSemanticRun env Us whnfFuel contextRun + (.ordinary context fuel argIdx name domain body binderInfo sortResult + noParameter ensureTypeStep universeTrace positivityTrace tailTrace) + (.forallE viewName viewDomain viewBody viewBinderInfo) + | terminal + {stats : InductiveStats} {isUnsafe : Bool} {familyIdx : Nat} + {ctor : Name} {context : Context} {source view : Expr} + {fuel argIdx : Nat} {sourceTerminal : source.isForall = false} + {sourceValid : isValidIndAppIdx stats source familyIdx = true} + {sourceCheck : ConstructorCheckedExpr context source} + {viewCheck : ConstructorCheckedExpr context view} + {contextRun : ConstructorContextRun env Us context} + (sourceRun : ConstructorCheckedExpr.Run sourceCheck + contextRun.candidate) + (viewRun : ConstructorCheckedExpr.Run viewCheck contextRun.candidate) : + ConstructorViewSemanticRun env Us whnfFuel contextRun + (.terminal context source fuel argIdx sourceTerminal sourceValid) view + +namespace ConstructorViewSemanticRun + +theorem nonempty_of_alignment + (contextRun : ConstructorContextRun env Us context) + (depth : context.fuel.recDepth = whnfFuel + 1) + {validationTrace : ConstructorTypeValidationTrace stats isUnsafe familyIdx + ctor context source argIdx fuel} + {view : Expr} + (alignment : ConstructorViewAlignmentTrace validationTrace view) : + Nonempty (ConstructorViewSemanticRun env Us whnfFuel contextRun + validationTrace view) := by + induction validationTrace generalizing view with + | parameter context fuel argIdx name domain body binderInfo param + parameterType parameterAt parameterTypeGet validationDefEq tailTrace ih => + cases alignment with + | parameter domainCheck viewDomainCheck parameterTypeCheck + parameterShape parameterPresent _ tailAlignment => + obtain ⟨domainRun⟩ := + ConstructorCheckedExpr.Run.exists domainCheck contextRun.candidate + obtain ⟨viewDomainRun⟩ := + ConstructorCheckedExpr.Run.exists viewDomainCheck + contextRun.candidate + obtain ⟨parameterTypeSemantic⟩ := + ConstructorCheckedExpr.Run.exists parameterTypeCheck + contextRun.candidate + let validationRun := TypeChecker.IsDefEqRun.ofCandidateStep + ⟨context, domain, parameterType⟩ validationDefEq + contextRun.candidate.context contextRun.candidate.context_eq + rfl rfl rfl contextRun.candidate.state_wf + domainRun.check.expr_tr parameterTypeSemantic.check.expr_tr + context.fuel.recDepth rfl + obtain ⟨tail⟩ := ih contextRun depth tailAlignment + exact ⟨ConstructorViewSemanticRun.parameter + domainRun viewDomainRun parameterTypeSemantic validationRun tail⟩ + | ordinary context fuel argIdx name domain body binderInfo sortResult + noParameter ensureTypeStep universeTrace positivityTrace tailTrace ih => + cases alignment with + | ordinary domainCheck viewDomainCheck viewEquality consumedCheck _ + positivityAlignment fresh annotations _ tailAlignment => + obtain ⟨domainRun⟩ := + ConstructorCheckedExpr.Run.exists domainCheck contextRun.candidate + obtain ⟨viewDomainRun⟩ := + ConstructorCheckedExpr.Run.exists viewDomainCheck + contextRun.candidate + let viewEqualityRun := domainRun.isDefEq viewDomainRun viewEquality + obtain ⟨consumedRun⟩ := + ConstructorCheckedExpr.Run.exists consumedCheck + contextRun.candidate + obtain ⟨ensureTypeRun⟩ := + TypeChecker.EnsureTypeRun.exists_ofConstructorStep + ⟨context, domain, sortResult⟩ ensureTypeStep + contextRun.candidate domainRun.source' domainRun.check.expr_tr + obtain ⟨positivity⟩ := + ConstructorPositivityModeSemanticRun.nonempty_of_alignment + contextRun depth positivityAlignment + let annotationsRun := domainRun.isDefEq consumedRun annotations + have consumedType : contextRun.candidate.context.IsType + consumedRun.source' := by + have annotationDef := annotationsRun.isDefEqU.of_l + contextRun.candidate.context.Ewf + contextRun.candidate.context.Δwf.toCtx + ensureTypeRun.source_type + exact ⟨ensureTypeRun.resultLevel', annotationDef.hasType.2⟩ + let tailContext := contextRun.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain) fresh consumedRun.source' + consumedRun.check.expr_tr consumedType + have tailDepth : + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)).fuel.recDepth = + whnfFuel + 1 := by + simpa [AddInductive.Context.pushLocalDecl] using depth + obtain ⟨tail⟩ := ih tailContext tailDepth tailAlignment + exact ⟨ConstructorViewSemanticRun.ordinary + (viewEquality := viewEquality) + domainRun viewDomainRun viewEqualityRun consumedRun ensureTypeRun + positivity annotationsRun consumedType tail⟩ + | terminal context source fuel argIdx sourceTerminal sourceValid => + cases alignment with + | terminal sourceCheck viewCheck viewTerminal viewValid => + obtain ⟨sourceRun⟩ := + ConstructorCheckedExpr.Run.exists sourceCheck contextRun.candidate + obtain ⟨viewRun⟩ := + ConstructorCheckedExpr.Run.exists viewCheck contextRun.candidate + exact ⟨ConstructorViewSemanticRun.terminal + (stats := stats) (isUnsafe := isUnsafe) + (familyIdx := familyIdx) (ctor := ctor) + sourceRun viewRun⟩ + +end ConstructorViewSemanticRun + +/-! +## Source-ordered post-family constructor semantics + +The alignment trace connects the validator's exact source telescope to the +analyzer-owned candidate view. The semantic list below interprets that trace +alongside the already-produced constructor semantic hierarchy. Its indices +keep source order, raw constructor order, and the exact candidate view fixed; +its payload retains both validation-local checks and Theory telescope/result +evidence selected by the candidate's recursive checker run. +-/ + +/-- Complete post-family meaning for every constructor position selected by +one exact validation/candidate alignment. + +`root` interprets the validator-owned root `checkType` observation (the +supplemental audit contributes only its scope proof). `telescope` interprets +parameter equality, ordinary-field typing, positivity, and the terminal +family application while following the exact candidate view at +validation-owned locals. `spine` exposes the exact Theory view binders and +terminal result selected by the candidate semantic root. -/ +inductive ConstructorPostFamilySemanticListRun + (env : VEnv) (Us : List Name) + (stats : InductiveStats) (isUnsafe : Bool) (familyIdx : Nat) + (context : Context) (contextRun : ConstructorContextRun env Us context) : + {seen : NameSet} → {constructors : List Constructor} → + (validationTrace : ConstructorListValidationTrace stats isUnsafe familyIdx + context seen constructors) → + (candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor constructors) → + {raws : List VConstVal} → + ConstructorCandidateAlignmentTrace stats isUnsafe familyIdx context + validationTrace candidates → + VInductDecl.CandidateConstructorSemanticListRun env Us candidates raws → + Type where + | nil (seen : NameSet) : + ConstructorPostFamilySemanticListRun env Us stats isUnsafe familyIdx + context contextRun (.nil seen) .nil (.nil seen) .nil + | cons + {seen : NameSet} {head : Constructor} {tail : List Constructor} + {freshName : seen.contains head.name = false} + {closed : context.env.checkNoMVarNoFVar head.name head.type = .ok ()} + {rootCheck : CandidateCheckTypeObservation + context.withEmptyLocalContext head.type} + {typeTrace : ConstructorTypeValidationTrace stats isUnsafe familyIdx + head.name context head.type 0 context.fuel.inductiveFuel} + {tailTrace : ConstructorListValidationTrace stats isUnsafe familyIdx + context (seen.insert head.name) tail} + {candidate : AddInductive.CandidateConstructor head} + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor tail} + {raw : VConstVal} {raws : List VConstVal} + {rootScope : ConstructorCheckedExpr context.withEmptyLocalContext + head.type} + {storedSpine : candidate.type.trace.storedSpine = true} + {spineLength : candidate.type.trace.spineLength = + typeTrace.spineLength} + {candidateDepth : candidate.type.context.fuel.recDepth = + context.fuel.recDepth} + {headAlignment : ConstructorViewAlignmentTrace typeTrace + candidate.type.view} + {tailAlignment : ConstructorCandidateAlignmentTrace stats isUnsafe + familyIdx context tailTrace candidates} + {headSemantic : VInductDecl.CandidateConstructorSemanticRun env Us + candidate raw} + {tailSemantic : VInductDecl.CandidateConstructorSemanticListRun env Us + candidates raws} + (root : ConstructorCheckedExpr.Run + (rootScope.withObservation rootCheck) + contextRun.withEmptyLocalContext.candidate) + (telescope : ConstructorViewSemanticRun env Us + headSemantic.type.whnfFuel contextRun typeTrace candidate.type.view) + (spine : ∃ resultType, + TypeChecker.TelResultDefEqEvidence env Us.length [] + (VExpr.telN candidate.type.trace.spineLength raw.type) + (VExpr.telN candidate.type.trace.spineLength + headSemantic.type.view) + (VExpr.dropN candidate.type.trace.spineLength raw.type) + (VExpr.dropN candidate.type.trace.spineLength + headSemantic.type.view) + resultType) + (tailRun : ConstructorPostFamilySemanticListRun env Us stats isUnsafe + familyIdx context contextRun tailTrace candidates + tailAlignment tailSemantic) : + ConstructorPostFamilySemanticListRun env Us stats isUnsafe familyIdx + context contextRun + (.cons seen head tail freshName closed rootCheck typeTrace tailTrace) + (.cons candidate candidates) + (.cons rootScope storedSpine spineLength candidateDepth headAlignment + tailAlignment) + (.cons headSemantic tailSemantic) + +namespace ConstructorPostFamilySemanticListRun + +/-- Interpret an exact source-ordered alignment together with the exact +candidate semantic list produced for the same raw constructor positions. -/ +theorem nonempty_of_alignment + (contextRun : ConstructorContextRun env Us context) + {validationTrace : ConstructorListValidationTrace stats isUnsafe familyIdx + context seen constructors} + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor constructors} + {raws : List VConstVal} + (alignment : ConstructorCandidateAlignmentTrace stats isUnsafe familyIdx + context validationTrace candidates) + (semantics : VInductDecl.CandidateConstructorSemanticListRun env Us + candidates raws) : + Nonempty (ConstructorPostFamilySemanticListRun env Us stats isUnsafe + familyIdx context contextRun validationTrace candidates alignment + semantics) := by + induction alignment generalizing raws with + | nil seen => + cases semantics + exact ⟨.nil seen⟩ + | @cons seen head tail freshName closed rootCheck typeTrace tailTrace + candidate candidates rootScope storedSpine spineLength candidateDepth + headAlignment tailAlignment ih => + cases semantics with + | cons headSemantic tailSemantic => + obtain ⟨root⟩ := ConstructorCheckedExpr.Run.exists + (rootScope.withObservation rootCheck) + contextRun.withEmptyLocalContext.candidate + have depth : context.fuel.recDepth = + headSemantic.type.whnfFuel + 1 := by + calc + context.fuel.recDepth = + candidate.type.context.fuel.recDepth := candidateDepth.symm + _ = headSemantic.type.whnfFuel + 1 := + headSemantic.type.whnfDepth + obtain ⟨telescope⟩ := + ConstructorViewSemanticRun.nonempty_of_alignment contextRun depth + headAlignment + have spine := TypeChecker.CandidateExprSpineRun.evidence + (headSemantic.type.spine storedSpine) + obtain ⟨tailRun⟩ := ih tailSemantic + exact ⟨.cons root telescope spine tailRun⟩ + +end ConstructorPostFamilySemanticListRun + +/-- A retained constructor telescope whose strengthened universe decisions are +all true replays the exact executable universe traversal. This is the +converse of `universeSemantics_of_loop`: the trace supplies the same parameter +choices and `ensureType` results, while the Boolean supplies only the +additional verified comparison at ordinary fields. -/ +theorem ConstructorTypeValidationTrace.universeLoop_of_semantics + (trace : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source argIdx fuel) + (semantic : trace.universeSemantics = true) : + checkConstructorUniverseSemantics.loop stats source argIdx fuel context = + .ok () := by + induction trace with + | parameter context fuel argIdx name domain body binderInfo parameter + parameterType parameterAt parameterTypeRun defeq tail ih => + simp only [universeSemantics] at semantic + rw [show fuel + 1 = Nat.succ fuel by rfl] + rw [checkConstructorUniverseSemantics.loop.eq_2] + rw [parameterAt] + exact ih semantic + | ordinary context fuel argIdx name domain body binderInfo sortResult + noParameter ensureType universeTrace positivity tail ih => + simp only [universeSemantics, Bool.and_eq_true] at semantic + rw [show fuel + 1 = Nat.succ fuel by rfl] + rw [checkConstructorUniverseSemantics.loop.eq_2] + rw [noParameter] + simp only [ReaderT.bind, Bind.bind, liftTypeChecker_apply] + rw [ensureType] + simp only [Except.bind] + have valid : constructorUniverseSemanticGe stats.resultLevel + sortResult.sortLevel! = true := by + simpa only [ConstructorUniverseTrace.semantic] using semantic.1 + rw [valid] + simp only [] + exact ih semantic.2 + | terminal context source fuel argIdx terminal valid => + cases source <;> try rfl + case forallE => + change true = false at terminal + contradiction + +/-- Root form of `universeLoop_of_semantics`, initialized from the same +context fuel as ordinary constructor validation. -/ +theorem ConstructorTypeValidationTrace.universeRun_of_semantics + (trace : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source 0 context.fuel.inductiveFuel) + (semantic : trace.universeSemantics = true) : + checkConstructorUniverseSemantics stats source context = .ok () := by + unfold checkConstructorUniverseSemantics + simpa only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] using trace.universeLoop_of_semantics semantic + +/-- A retained source-ordered constructor list whose strengthened universe +decisions are all true replays the exact executable list audit. -/ +theorem ConstructorListValidationTrace.universeRun_of_semantics + (trace : ConstructorListValidationTrace stats isUnsafe familyIdx + context seen constructors) + (semantic : trace.universeSemantics = true) : + checkConstructorUniverseListSemantics stats constructors context = + .ok () := by + induction trace with + | nil => rfl + | cons seen head tail fresh closed rootCheck typeTrace tailTrace ih => + simp only [universeSemantics, Bool.and_eq_true] at semantic + simp only [checkConstructorUniverseListSemantics, + ReaderT.bind, Bind.bind] + rw [typeTrace.universeRun_of_semantics semantic.1] + simp only [Except.bind] + exact ih semantic.2 + +/-- Impredicative `Prop` makes every strengthened constructor-universe node +true, independently of the field level selected by the retained checker run. -/ +theorem ConstructorUniverseTrace.semantic_of_resultLevel_isZero + (trace : ConstructorUniverseTrace resultLevel fieldLevel) + (zero : resultLevel.isZero = true) : trace.semantic = true := by + simp [ConstructorUniverseTrace.semantic, constructorUniverseSemanticGe, + zero] + +/-- Every universe node in a retained constructor telescope is admitted by +the strengthened audit when the family result is `Prop`. -/ +theorem ConstructorTypeValidationTrace.universeSemantics_of_resultLevel_isZero + (trace : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source argIdx fuel) + (zero : stats.resultLevel.isZero = true) : + trace.universeSemantics = true := by + induction trace with + | parameter context fuel argIdx name domain body binderInfo parameter + parameterType parameterAt parameterTypeRun defeq tail ih => + simpa only [universeSemantics] using ih + | ordinary context fuel argIdx name domain body binderInfo sortResult + noParameter ensureType universeTrace positivity tail ih => + simp only [universeSemantics, Bool.and_eq_true] + exact ⟨universeTrace.semantic_of_resultLevel_isZero zero, ih⟩ + | terminal => rfl + +/-- Source-list form of +`ConstructorTypeValidationTrace.universeSemantics_of_resultLevel_isZero`. -/ +theorem ConstructorListValidationTrace.universeSemantics_of_resultLevel_isZero + (trace : ConstructorListValidationTrace stats isUnsafe familyIdx + context seen constructors) + (zero : stats.resultLevel.isZero = true) : + trace.universeSemantics = true := by + induction trace with + | nil => rfl + | cons seen head tail fresh closed rootCheck typeTrace tailTrace ih => + simp only [universeSemantics, Bool.and_eq_true] + exact ⟨typeTrace.universeSemantics_of_resultLevel_isZero zero, ih⟩ + +/-- A successful executable universe audit marks every universe node in an +arbitrary retained constructor telescope. The proof uses determinism of the +same `ensureType` execution retained by the ordinary trace; it cannot change a +field level or skip a source position. -/ +theorem ConstructorTypeValidationTrace.universeSemantics_of_loop + (trace : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source argIdx fuel) + (success : checkConstructorUniverseSemantics.loop stats source argIdx fuel + context = .ok ()) : + trace.universeSemantics = true := by + induction trace with + | parameter context fuel argIdx name domain body binderInfo parameter + parameterType parameterAt parameterTypeRun defeq tail ih => + simp only [universeSemantics] + rw [show fuel + 1 = Nat.succ fuel by rfl] at success + rw [checkConstructorUniverseSemantics.loop.eq_2] at success + rw [parameterAt] at success + exact ih success + | ordinary context fuel argIdx name domain body binderInfo sortResult + noParameter ensureType universeTrace positivity tail ih => + simp only [universeSemantics, Bool.and_eq_true] + rw [show fuel + 1 = Nat.succ fuel by rfl] at success + rw [checkConstructorUniverseSemantics.loop.eq_2] at success + rw [noParameter] at success + simp only [ReaderT.bind, Bind.bind, liftTypeChecker_apply] at success + rw [ensureType] at success + simp only [Except.bind] at success + cases valid : constructorUniverseSemanticGe stats.resultLevel + sortResult.sortLevel! with + | false => + rw [valid] at success + change Except.error _ = Except.ok () at success + cases success + | true => + rw [valid] at success + simp only [] at success + exact ⟨valid, ih success⟩ + | terminal => rfl + +/-- Root form of `universeSemantics_of_loop`, with the audit initialized from +the exact context fuel just like ordinary constructor validation. -/ +theorem ConstructorTypeValidationTrace.universeSemantics_of_run + (trace : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source 0 context.fuel.inductiveFuel) + (success : checkConstructorUniverseSemantics stats source context = + .ok ()) : + trace.universeSemantics = true := by + apply trace.universeSemantics_of_loop + simpa only [checkConstructorUniverseSemantics, readThe, + MonadReaderOf.read, ReaderT.read, ReaderT.bind, Bind.bind, + ReaderT.pure, Pure.pure, Except.bind, Except.pure] using success + +/-- A successful source-list audit marks every retained constructor position; +the dependent list indices prevent omission, duplication, or reordering. -/ +theorem ConstructorListValidationTrace.universeSemantics_of_run + (trace : ConstructorListValidationTrace stats isUnsafe familyIdx + context seen constructors) + (success : checkConstructorUniverseListSemantics stats constructors + context = .ok ()) : + trace.universeSemantics = true := by + induction trace with + | nil => rfl + | cons seen head tail fresh closed rootCheck typeTrace tailTrace ih => + simp only [universeSemantics, Bool.and_eq_true] + simp only [checkConstructorUniverseListSemantics, + ReaderT.bind, Bind.bind] at success + cases headRun : checkConstructorUniverseSemantics stats head.type + context with + | error error => + rw [headRun] at success + cases success + | ok result => + cases result + rw [headRun] at success + simp only [Except.bind] at success + exact ⟨typeTrace.universeSemantics_of_run headRun, ih success⟩ + +/-- Ordinary constructor validation paired with the executable semantic +universe audit over the identical singleton source list. This record narrows +the accepted package boundary while preserving the ordinary validator result +and every retained non-universe check unchanged. -/ +structure ConstructorSemanticValidationRun + (indType : InductiveType) (stats : InductiveStats) + (isUnsafe : Bool) (context : Context) where + validation : ConstructorValidationRun indType stats isUnsafe context + universeRun : checkConstructorUniverseListSemantics stats indType.ctors + context = .ok () + +namespace ConstructorSemanticValidationRun + +/-- Forgetting the semantic audit replays the exact ordinary validator, so +the strengthened run cannot widen kernel acceptance. -/ +theorem run + (semantic : ConstructorSemanticValidationRun indType stats isUnsafe + context) : + checkConstructors #[indType] stats isUnsafe context = .ok () := + semantic.validation.run + +/-- Every universe-bearing node of the retained ordinary trace passed the +executable verified universe gate. -/ +theorem universeSemantics + (semantic : ConstructorSemanticValidationRun indType stats isUnsafe + context) : + semantic.validation.universeSemantics = true := + semantic.validation.trace.universeSemantics_of_run semantic.universeRun + +end ConstructorSemanticValidationRun + +/-- The semantic universe gate only accepts branches already accepted by the +ordinary constructor validator. It therefore narrows package construction +without widening kernel validation behavior. -/ +theorem ConstructorUniverseTrace.nonempty_of_semanticGe + (valid : constructorUniverseSemanticGe resultLevel fieldLevel = true) : + Nonempty (ConstructorUniverseTrace resultLevel fieldLevel) := by + unfold constructorUniverseSemanticGe at valid + simp only [Bool.or_eq_true, Bool.and_eq_true] at valid + rcases valid with structural | prop | ⟨core, _verified⟩ + · exact ⟨.structural structural⟩ + · cases hstruct : levelStructGe resultLevel fieldLevel with + | true => exact ⟨.structural hstruct⟩ + | false => + exact ⟨.fallback hstruct + (by cases resultLevel <;> simp_all [Level.isZero, Level.isAlwaysZero])⟩ + · cases hstruct : levelStructGe resultLevel fieldLevel with + | true => exact ⟨.structural hstruct⟩ + | false => + exact ⟨.fallback hstruct (by simp [core])⟩ + +/-- The executable semantic subset implies exactly the disjunct required for +a non-recursive field in `VInductDecl.fieldsWF`: either the family is Prop or +the field universe is bounded by the family universe. -/ +theorem constructorUniverseSemanticGe_ofLevel + (valid : constructorUniverseSemanticGe resultLevel fieldLevel = true) + (result_tr : VLevel.ofLevel Us resultLevel = some result') + (field_tr : VLevel.ofLevel Us fieldLevel = some field') : + result' = .zero ∨ field' ≤ result' := by + unfold constructorUniverseSemanticGe at valid + simp only [Bool.or_eq_true, Bool.and_eq_true] at valid + rcases valid with structural | prop | ⟨_core, verified⟩ + · exact .inr (levelStructGe_ofLevel structural result_tr field_tr) + · exact .inl (ofLevel_eq_zero_of_isZero prop result_tr) + · exact .inr (Level.geq'_wf result_tr field_tr verified) + +/-- Agreement between the ordinary and verified normalized comparisons opens +the semantic fallback without weakening the ordinary acceptance boundary. -/ +theorem constructorUniverseSemanticGe_eq_true_of_geq_agreement + (core : resultLevel.geq fieldLevel = true) + (verified : resultLevel.geq' fieldLevel = true) : + constructorUniverseSemanticGe resultLevel fieldLevel = true := by + simp [constructorUniverseSemanticGe, core, verified] + +private def constructorUniverseComparisonSamples : List Level := + [.zero, + .succ .zero, + .succ (.succ .zero), + .param `u, + .param `v, + .succ (.param `u), + .max (.param `u) (.param `v), + .max (.succ (.param `u)) (.param `v), + .imax (.param `u) (.param `v), + .imax (.param `u) (.succ (.param `v)), + .imax (.max (.param `u) (.param `v)) (.succ .zero), + .max (.imax (.param `u) (.param `v)) (.succ (.param `v))] + +/- Differential audit for the mvar-free surface accepted by constructors. +Every pair in this matrix compares Lean v4.31's core decision with the proved +project decision; the samples exercise zero, successor, maximum, impredicative +maximum, parameters, and nested combinations. -/ +#guard constructorUniverseComparisonSamples.all fun resultLevel => + constructorUniverseComparisonSamples.all fun fieldLevel => + resultLevel.geq fieldLevel == resultLevel.geq' fieldLevel + +/- Regression for the former D1 gap: the structural and `Prop` branches both +miss a parameter below a `max`, while core/project normalized comparison +agrees and the verified semantic fallback now accepts it. -/ +private def constructorUniverseNormalizedResult : Level := + .max (.param `u) (.param `v) + +#guard !levelStructGe constructorUniverseNormalizedResult (.param `u) +#guard !constructorUniverseNormalizedResult.isZero +#guard constructorUniverseNormalizedResult.geq (.param `u) +#guard constructorUniverseNormalizedResult.geq' (.param `u) +#guard constructorUniverseSemanticGe constructorUniverseNormalizedResult + (.param `u) + +/- The universe bridge stays within Lean's standard logical basis. In +particular it does not inherit the project's pending sorries, a custom axiom, +or a semantic premise for Lean's opaque `Level.geq` implementation. -/ +#guard_named_axioms Ix.Theory.Named.AddInductive.levelStructEq_ofLevel [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.levelStructGe_ofLevel [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.constructorUniverseSemanticGe_ofLevel [ + propext, + Classical.choice, + Quot.sound, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Std.TreeMap.all_eq_all_toList] + +#guard_named_axioms Ix.Theory.Named.AddInductive.ConstructorUniverseTrace.nonempty_of_semanticGe [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.ConstructorSemanticValidationRun.universeSemantics [propext, Classical.choice, Quot.sound] + +/- The reverse executable bridge and its impredicative-Prop specialization +have the same standard-only closure as the forward universe interpretation. +In particular, replaying a retained validator trace does not inherit a +fixture computation oracle. -/ +#guard_named_axioms Ix.Theory.Named.AddInductive.ConstructorTypeValidationTrace.universeLoop_of_semantics [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.ConstructorTypeValidationTrace.universeRun_of_semantics [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.ConstructorListValidationTrace.universeRun_of_semantics [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.ConstructorUniverseTrace.semantic_of_resultLevel_isZero [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.ConstructorTypeValidationTrace.universeSemantics_of_resultLevel_isZero [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.ConstructorListValidationTrace.universeSemantics_of_resultLevel_isZero [propext, Classical.choice, Quot.sound] + +/-! +## Executable pre-family safety and replay + +The post-family validator introduces a local declaration for every constructor +field. A recursive field's local type mentions the family being defined, so +that declaration cannot be reproduced in the pre-family verifier context. +For the current singleton subset we omit such locals, advance the validator's +fresh-name supply, and permit later checks only when their source expressions +do not mention an omitted identifier. Independent ordinary fields may still +follow recursive fields; genuinely dependent uses remain outside this replay. + +The traces below are outputs of executable builders. Their proof fields are +exact `checkType`, `ensureType`, and `isDefEq` executions; they are operational +evidence, not caller-supplied Theory judgments. +-/ + +/-- Executable fragment on which strict kernel-to-Theory translation has a +syntactically unique endpoint. Projections are excluded because the current +`TrProj` contract determines their result only up to definitional equality; +projection-bearing inductives remain outside the singleton subset until the +projection milestones establish an exact structural API. -/ +def theoryTranslationUnique : Expr → Bool + | .bvar _ + | .fvar _ + | .sort _ + | .const .. + | .mvar .. + | .lit _ => true + | .app fn argument => + theoryTranslationUnique fn && theoryTranslationUnique argument + | .lam _ domain body _ + | .forallE _ domain body _ => + theoryTranslationUnique domain && theoryTranslationUnique body + | .letE _ _ value body _ => + theoryTranslationUnique value && theoryTranslationUnique body + | .mdata _ expression => theoryTranslationUnique expression + | .proj .. => false + +/-- The executable predicate is exactly the structural proposition used by +strict-translation uniqueness. -/ +theorem theoryTranslationUnique_sound + (success : theoryTranslationUnique expression = true) : + TrExprS.IsUnique expression := by + induction expression <;> + simp_all [theoryTranslationUnique, TrExprS.IsUnique] + +/-- Abstracting one free variable preserves the projection-free fragment: +the operation changes only free/bound-variable identities and recursively +retains every expression constructor. -/ +theorem theoryTranslationUnique_abstract1 + (expression : Expr) (id : FVarId) (depth : Nat) : + theoryTranslationUnique (Expr.abstract1 id expression depth) = + theoryTranslationUnique expression := by + induction expression generalizing depth <;> + simp [Expr.abstract1, theoryTranslationUnique, *] + split <;> rfl + +/-- Iterated free-variable abstraction likewise preserves the executable +strict-translation fragment. -/ +theorem theoryTranslationUnique_abstractList + (expression : Expr) (ids : List FVarId) (depth : Nat) : + theoryTranslationUnique (Expr.abstractList expression ids depth) = + theoryTranslationUnique expression := by + induction ids generalizing expression with + | nil => rfl + | cons id ids ih => + simp only [Expr.abstractList, ih, + theoryTranslationUnique_abstract1] + +/-- Array-form abstraction over an explicit list of free variables preserves +the projection-free fragment. -/ +theorem theoryTranslationUnique_abstractFVars + (expression : Expr) (ids : List FVarId) : + theoryTranslationUnique + (expression.abstract ⟨ids.map Expr.fvar⟩) = + theoryTranslationUnique expression := by + rw [Expr.abstract_eq] + exact theoryTranslationUnique_abstractList expression ids 0 + +/-- Every syntax fragment contributing to a recursively reconstructed +candidate view has a unique strict Theory endpoint. Checking recursive body +views as well as their stored abstractions supplies precisely the induction +hypotheses consumed by `CandidateExprRun.view_tr_strict`. -/ +def CandidateExprTrace.viewTranslationUnique : + {context : Context} → {source : Expr} → + CandidateExprTrace context source → Bool + | _, _, .terminal _ _ _ result _ _ => theoryTranslationUnique result + | _, _, .forallE context _ _ _ _ _ _ _ _ _ _ _ domain body => + domain.viewTranslationUnique && + (body.viewTranslationUnique && + theoryTranslationUnique + (body.view.abstract #[context.freshExpr])) + +/-- The recursive check is extensionally the projection-free check on the +reconstructed analyzer view. Its explicit body clause supplies the induction +hypothesis consumed by the strict-view proof. -/ +theorem CandidateExprTrace.viewTranslationUnique_eq + (trace : CandidateExprTrace context source) : + trace.viewTranslationUnique = theoryTranslationUnique trace.view := by + induction trace with + | terminal => rfl + | forallE context source inferred name domain body binderInfo fresh + annotations annotationsEq checked normalized domainTrace bodyTrace + domainIH bodyIH => + simp only [viewTranslationUnique, CandidateExprTrace.view, + theoryTranslationUnique, domainIH, bodyIH] + rw [show #[context.freshExpr] = + ⟨[context.freshFVarId].map Expr.fvar⟩ by rfl, + theoryTranslationUnique_abstractFVars] + simp + +/-- A successful recursive executable check supplies the exact proposition +required by strict candidate-view translation. -/ +theorem CandidateExprTrace.viewTranslationUnique_sound + (trace : CandidateExprTrace context source) + (success : trace.viewTranslationUnique = true) : + TypeChecker.CandidateExprTraceViewIsUnique trace := by + induction trace with + | terminal => exact theoryTranslationUnique_sound success + | forallE context source inferred name domain body binderInfo fresh + annotations annotationsEq checked normalized domainTrace bodyTrace + domainIH bodyIH => + simp only [viewTranslationUnique, Bool.and_eq_true] at success + exact ⟨domainIH success.1, bodyIH success.2.1, + theoryTranslationUnique_sound success.2.2⟩ + +/-- Source-ordered strict-view check for an exact dependent constructor +candidate list. -/ +def CandidateList.viewTranslationUnique : + {constructors : List Constructor} → + CandidateList CandidateConstructor constructors → Bool + | _, .nil => true + | _, .cons candidate candidates => + candidate.type.trace.viewTranslationUnique && + candidates.viewTranslationUnique + +/-- Proof-level source-ordered counterpart of +`CandidateList.viewTranslationUnique`. -/ +def CandidateList.ViewTranslationUnique : + {constructors : List Constructor} → + CandidateList CandidateConstructor constructors → Prop + | _, .nil => True + | _, .cons candidate candidates => + TypeChecker.CandidateExprTraceViewIsUnique candidate.type.trace ∧ + candidates.ViewTranslationUnique + +theorem CandidateList.viewTranslationUnique_sound + (candidates : CandidateList CandidateConstructor constructors) + (success : candidates.viewTranslationUnique = true) : + candidates.ViewTranslationUnique := by + induction candidates with + | nil => trivial + | cons candidate candidates ih => + simp only [viewTranslationUnique, Bool.and_eq_true] at success + exact ⟨candidate.type.trace.viewTranslationUnique_sound success.1, + ih success.2⟩ + +/-- Advance the constructor traversal's fresh-name supply without adding the +family-dependent local declaration for a recursive outer field. -/ +def Context.advanceFresh (context : Context) : Context := + { context with ngen := context.ngen.next } + +/-- Advancing an omitted recursive field changes only the candidate name +generator. The verified checker context and its empty-state certificate remain +the same because `Context.toTypeChecker` deliberately has no name-generator +field. -/ +def advanceCandidateContextRun + (run : TypeChecker.CandidateContextRun context) : + TypeChecker.CandidateContextRun context.advanceFresh := by + refine ⟨run.context, ?_, run.state_wf, ?_⟩ + · simpa [Context.advanceFresh, Context.toTypeChecker] using run.context_eq + · simpa [Context.advanceFresh, NameGenerator.next] using run.namePrefix_ne + +/-- Keep the fixed pre-family Theory environment/universe indices while the +operational traversal advances past an omitted recursive outer field. -/ +def ConstructorContextRun.advanceFresh + (run : ConstructorContextRun env Us context) : + ConstructorContextRun env Us context.advanceFresh where + candidate := advanceCandidateContextRun run.candidate + venv_eq := run.venv_eq + lparams_eq := run.lparams_eq + +/-- Syntactic independence from the recursive outer-field locals omitted by +the pre-family replay context. -/ +def constructorIndependentOf (source : Expr) (removed : List FVarId) : Bool := + source.fvarsList.all fun fv => !removed.contains fv + +/-- Instantiate the analyzer-owned family view with the exact parameter FVars +selected by family validation, leaving the index telescope exposed. -/ +def instantiateFamilyParameters : Expr → List Expr → Except Exception Expr + | familyType, [] => pure familyType + | .forallE _ _ body _, parameter :: parameters => + instantiateFamilyParameters (body.instantiate1 parameter) parameters + | _, _ :: _ => + throw <| .other + "candidate family view has fewer binders than retained parameters" + +/-- Exact successful pre-family `ensureType` observation. -/ +structure ConstructorEnsureTypeObservation + (context : Context) (source : Expr) where + result : Expr + valid : ConstructorEnsureTypeStep.Valid ⟨context, source, result⟩ + +def observeConstructorEnsureType (context : Context) (source : Expr) : + Except Exception (ConstructorEnsureTypeObservation context source) := + match hrun : TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.ensureType source) with + | .error error => .error error + | .ok result => .ok ⟨result, hrun⟩ + +theorem observeConstructorEnsureType_of_run + (run : ConstructorEnsureTypeStep.Valid ⟨context, source, result⟩) : + observeConstructorEnsureType context source = .ok ⟨result, run⟩ := by + change TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.ensureType source) = + .ok result at run + unfold observeConstructorEnsureType + split + · simp_all + · rename_i observed hobserved + have : observed = result := by simp_all + subst observed + rfl + +theorem ConstructorEnsureTypeObservation.observe_eq + (observation : ConstructorEnsureTypeObservation context source) : + observeConstructorEnsureType context source = .ok observation := by + rw [observeConstructorEnsureType_of_run observation.valid] + +/-- One pre-family index argument checked against the corresponding binder of +the parameter-instantiated family view. -/ +structure ConstructorPreFamilyIndexStep + (context : Context) (argument expected : Expr) where + argumentCheck : ConstructorCheckedExpr context argument + expectedCheck : ConstructorCheckedExpr context expected + comparison : CandidateIsDefEqObservation context + argumentCheck.observation.inferred expected + +/-- Source-ordered executable replay of a constructor result/recursive-target +index spine against the analyzer-owned family index telescope. -/ +inductive ConstructorPreFamilyIndexSpineTrace : + (context : Context) → (expected : Expr) → List Expr → Type where + | nil + (context : Context) (expected : Expr) + (expectedCheck : ConstructorCheckedExpr context expected) + (terminal : expected.isForall = false) : + ConstructorPreFamilyIndexSpineTrace context expected [] + | cons + (context : Context) (name : Name) (domain body : Expr) + (binderInfo : BinderInfo) (argument : Expr) (arguments : List Expr) + (expectedCheck : ConstructorCheckedExpr context + (.forallE name domain body binderInfo)) + (step : ConstructorPreFamilyIndexStep context argument domain) + (tail : ConstructorPreFamilyIndexSpineTrace context + (body.instantiate1 argument) arguments) : + ConstructorPreFamilyIndexSpineTrace context + (.forallE name domain body binderInfo) (argument :: arguments) + +namespace ConstructorPreFamilyIndexSpineTrace + +def build : (context : Context) → (expected : Expr) → + (arguments : List Expr) → + Except Exception + (ConstructorPreFamilyIndexSpineTrace context expected arguments) + | context, expected, [] => + if terminal : expected.isForall = false then do + let expectedCheck ← checkConstructorAlignedExpr context expected + pure <| .nil context expected expectedCheck terminal + else + throw <| .other + "constructor target supplies too few family indices" + | context, .forallE name domain body binderInfo, argument :: arguments => do + let telescopeCheck ← checkConstructorAlignedExpr context + (.forallE name domain body binderInfo) + let argumentCheck ← checkConstructorAlignedExpr context argument + let domainCheck ← checkConstructorAlignedExpr context domain + let comparison ← observeCandidateIsDefEq context + argumentCheck.observation.inferred domain + let tail ← build context (body.instantiate1 argument) arguments + pure <| .cons context name domain body binderInfo argument arguments + telescopeCheck ⟨argumentCheck, domainCheck, comparison⟩ tail + | _, _, _ :: _ => + throw <| .other + "constructor target supplies too many family indices" + +end ConstructorPreFamilyIndexSpineTrace + +theorem ConstructorPreFamilyIndexSpineTrace.build_eq + (trace : ConstructorPreFamilyIndexSpineTrace context expected arguments) : + ConstructorPreFamilyIndexSpineTrace.build context expected arguments = + .ok trace := by + induction trace with + | nil expected expectedCheck terminal => + simp only [ConstructorPreFamilyIndexSpineTrace.build] + rw [dif_pos terminal, expectedCheck.check_eq] + rfl + | cons name domain body binderInfo argument arguments + expectedCheck step tail ih => + simp only [ConstructorPreFamilyIndexSpineTrace.build] + rw [expectedCheck.check_eq, step.argumentCheck.check_eq, + step.expectedCheck.check_eq] + simp only [Bind.bind, Except.bind] + rw [step.comparison.observe_eq, ih] + rfl + +/-- The exact full check retained at the root of an index-spine replay. -/ +def ConstructorPreFamilyIndexSpineTrace.expectedCheck : + (trace : ConstructorPreFamilyIndexSpineTrace context expected arguments) → + ConstructorCheckedExpr context expected + | .nil _ _ expectedCheck _ => expectedCheck + | .cons _ _ _ _ _ _ _ expectedCheck _ _ => expectedCheck + +/-! +### Verified pre-family index spines + +The operational trace checks the complete expected family-index telescope at +every recursive position. Its semantic interpretation follows the strict +translation selected at the root, instantiates the translated Pi body with the +translated argument, and therefore constructs an actual Theory `SpineWF` +rather than a pointwise list whose dependencies have been forgotten. +-/ + +/-- Verified meaning of one pre-family index-spine replay. `expected'` is a +strict translation of the exact parameter-instantiated family telescope in the +current context; `arguments'` and `result'` are forced by the retained checker +executions and dependent Pi instantiation. -/ +structure ConstructorPreFamilyIndexSpineSemanticRun + (env : VEnv) (Us : List Name) + (context : Context) (contextRun : ConstructorContextRun env Us context) + {expected : Expr} {arguments : List Expr} + (trace : ConstructorPreFamilyIndexSpineTrace context expected arguments) + (expected' : VExpr) where + expectedInferred' : VExpr + expectedRun : TypeChecker.CheckTypeRun env Us + contextRun.candidate.context.vlctx expected + trace.expectedCheck.observation.inferred expected' expectedInferred' + arguments' : List VExpr + result' : VExpr + arguments_tr : List.Forall₂ + (TrExprS env Us contextRun.candidate.context.vlctx) + arguments arguments' + spine : env.SpineWF Us.length contextRun.candidate.context.vlctx.toCtx + expected' arguments' result' + +namespace ConstructorPreFamilyIndexSpineSemanticRun + +/-- Interpret a spine at a caller-fixed strict translation of its expected +telescope. Recursive calls receive the translated Pi body instantiated with +the exact checker-selected argument translation. -/ +theorem nonempty_at + (contextRun : ConstructorContextRun env Us context) + (trace : ConstructorPreFamilyIndexSpineTrace context expected arguments) + (expected' : VExpr) + (expected_tr : TrExprS env Us + contextRun.candidate.context.vlctx expected expected') : + Nonempty (ConstructorPreFamilyIndexSpineSemanticRun env Us context + contextRun trace expected') := by + induction trace generalizing expected' with + | nil expected expectedCheck terminal => + have expected_tr' : contextRun.candidate.context.TrExprS expected + expected' := by + simpa only [VContext.TrExprS, contextRun.venv_eq, + contextRun.lparams_eq] using expected_tr + obtain ⟨expectedInferred', ⟨expectedRun⟩⟩ := + TypeChecker.CheckTypeRun.exists_ofCandidateStep + ⟨context, expected, expectedCheck.observation.inferred⟩ + expectedCheck.observation.valid contextRun.candidate expected' + expected_tr' + exact ⟨{ + expectedInferred' := expectedInferred' + expectedRun := by + simpa only [ConstructorPreFamilyIndexSpineTrace.expectedCheck, + contextRun.venv_eq, contextRun.lparams_eq] using expectedRun + arguments' := [] + result' := expected' + arguments_tr := .nil + spine := .nil }⟩ + | cons name domain body binderInfo argument arguments telescopeCheck + step tail ih => + obtain ⟨domain', body', rfl, domainType, bodyType, domain_tr, + body_tr⟩ := TypeChecker.TrExprS.forallE_components expected_tr + have telescope_tr' : contextRun.candidate.context.TrExprS + (.forallE name domain body binderInfo) (.forallE domain' body') := by + simpa only [VContext.TrExprS, contextRun.venv_eq, + contextRun.lparams_eq] using expected_tr + obtain ⟨expectedInferred', ⟨expectedRun⟩⟩ := + TypeChecker.CheckTypeRun.exists_ofCandidateStep + ⟨context, .forallE name domain body binderInfo, + telescopeCheck.observation.inferred⟩ + telescopeCheck.observation.valid contextRun.candidate + (.forallE domain' body') telescope_tr' + have domain_tr' : contextRun.candidate.context.TrExprS domain domain' := by + simpa only [VContext.TrExprS, contextRun.venv_eq, + contextRun.lparams_eq] using domain_tr + obtain ⟨domainInferred', ⟨domainCheck⟩⟩ := + TypeChecker.CheckTypeRun.exists_ofCandidateStep + ⟨context, domain, step.expectedCheck.observation.inferred⟩ + step.expectedCheck.observation.valid contextRun.candidate domain' + domain_tr' + let domainRun : ConstructorCheckedExpr.Run step.expectedCheck + contextRun.candidate := ⟨domain', domainInferred', domainCheck⟩ + obtain ⟨argumentRun⟩ := ConstructorCheckedExpr.Run.exists + step.argumentCheck contextRun.candidate + let comparisonRun := TypeChecker.IsDefEqRun.ofCandidateStep + ⟨context, step.argumentCheck.observation.inferred, domain⟩ + step.comparison.valid contextRun.candidate.context + contextRun.candidate.context_eq rfl rfl rfl + contextRun.candidate.state_wf argumentRun.check.inferred_tr + domainRun.check.expr_tr context.fuel.recDepth rfl + have argumentType' : contextRun.candidate.context.HasType + argumentRun.source' domain' := + argumentRun.check.hasType.defeqU_r + contextRun.candidate.context.Ewf + contextRun.candidate.context.Δwf.toCtx comparisonRun.isDefEqU + have argumentType : env.HasType Us.length + contextRun.candidate.context.vlctx.toCtx argumentRun.source' + domain' := by + simpa only [VContext.HasType, contextRun.venv_eq, + contextRun.lparams_eq] using argumentType' + have argument_tr : TrExprS env Us + contextRun.candidate.context.vlctx argument argumentRun.source' := by + simpa only [contextRun.venv_eq, contextRun.lparams_eq] using + argumentRun.check.expr_tr + have henv : VEnv.WF env := by + simpa only [contextRun.venv_eq] using + contextRun.candidate.context.Ewf + have instantiatedBody_tr : TrExprS env Us + contextRun.candidate.context.vlctx + (body.instantiate1 argument) (body'.inst argumentRun.source') := by + simpa only [Expr.instantiate1_eq] using + body_tr.inst henv.ordered argumentType argument_tr + obtain ⟨tailRun⟩ := ih (body'.inst argumentRun.source') + instantiatedBody_tr + exact ⟨{ + expectedInferred' := expectedInferred' + expectedRun := by + simpa only [ConstructorPreFamilyIndexSpineTrace.expectedCheck, + contextRun.venv_eq, contextRun.lparams_eq] using expectedRun + arguments' := argumentRun.source' :: tailRun.arguments' + result' := tailRun.result' + arguments_tr := .cons argument_tr tailRun.arguments_tr + spine := .cons argumentType tailRun.spine }⟩ + +/-- Every successful operational spine trace has a verified interpretation; +the initial strict endpoint is selected by the trace's own root `checkType`. -/ +theorem nonempty + (contextRun : ConstructorContextRun env Us context) + (trace : ConstructorPreFamilyIndexSpineTrace context expected arguments) : + ∃ expected', Nonempty + (ConstructorPreFamilyIndexSpineSemanticRun env Us context contextRun + trace expected') := by + obtain ⟨expectedRun⟩ := ConstructorCheckedExpr.Run.exists + trace.expectedCheck contextRun.candidate + have expected_tr : TrExprS env Us + contextRun.candidate.context.vlctx expected expectedRun.source' := by + simpa only [contextRun.venv_eq, contextRun.lparams_eq] using + expectedRun.check.expr_tr + exact ⟨expectedRun.source', nonempty_at contextRun trace _ expected_tr⟩ + +/-- Strict family translation fixes the expected endpoint chosen by an exact +pre-family index replay, even when the replay context contains a later prefix +of fresh locals. -/ +theorem expected_eq_of_family + {env : VEnv} {Us : List Name} {context : Context} + {contextRun : ConstructorContextRun env Us context} + {expected : Expr} {arguments : List Expr} + {trace : ConstructorPreFamilyIndexSpineTrace context expected arguments} + {expected' : VExpr} + (run : ConstructorPreFamilyIndexSpineSemanticRun env Us context + contextRun trace expected') + {parameterΔ viewΔ : VLCtx} {expectedBase : VExpr} {n : Nat} + (familyTr : TrExprS env Us parameterΔ expected expectedBase) + (unique : TrExprS.IsUnique expected) + (viewLift : VLCtx.FVLift' parameterΔ viewΔ 0 + (.skipN .refl n) 0) + (viewDefEq : VLCtx.IsDefEq env Us.length + contextRun.candidate.context.vlctx viewΔ) + (viewUnique : TrExprS.IsUniqueCtx + contextRun.candidate.context.vlctx viewΔ) : + expected' = expectedBase.liftN n 0 := by + have henv : VEnv.Ordered env := by + simpa only [contextRun.venv_eq] using + contextRun.candidate.context.Ewf.ordered + have viewWF : VLCtx.WF env Us.length viewΔ := + (viewDefEq.symm henv).wf + have familyAtView : TrExprS env Us viewΔ expected + (expectedBase.liftN n 0) := by + simpa only [VExpr.lift'_consN_skipN] using + familyTr.weakFV' henv viewLift viewWF + have retained : TrExprS env Us contextRun.candidate.context.vlctx + expected expected' := run.expectedRun.expr_tr + exact retained.unique' viewUnique unique familyAtView + +/-- Transport the exact pre-family index judgment below an arbitrary later +prefix. This is the proved context weakening used when D4 places the same +spine beneath the remaining constructor fields. -/ +theorem spine_weakPrefix + {expected : Expr} {arguments : List Expr} + {replay : ConstructorPreFamilyIndexSpineTrace context expected arguments} + {expected' : VExpr} + (run : ConstructorPreFamilyIndexSpineSemanticRun env Us context contextRun + replay expected') (Bs : List VExpr) : + env.SpineWF Us.length + (Bs ++ contextRun.candidate.context.vlctx.toCtx) + (expected'.liftN Bs.length 0) + (run.arguments'.map fun argument => + argument.liftN Bs.length 0) + (run.result'.liftN Bs.length 0) := by + have henv : VEnv.WF env := by + simpa only [contextRun.venv_eq] using contextRun.candidate.context.Ewf + exact run.spine.weakN henv.ordered + (Ctx.LiftN.zero (n := Bs.length) + (Γ := contextRun.candidate.context.vlctx.toCtx) Bs) + +end ConstructorPreFamilyIndexSpineSemanticRun + +/-- Pre-family replay of the family-free pieces of one recursive field. Π +domains are checked and introduced normally; the terminal family application +is replaced by an index-spine replay, so the absent family constant is never +looked up. -/ +inductive ConstructorPreFamilyRecursiveTrace + (stats : InductiveStats) (familyIdx : Nat) (familyIndices : Expr) : + (context : Context) → (source : Expr) → (fuel : Nat) → Type where + | forallE + (context : Context) (name : Name) (domain body : Expr) + (binderInfo : BinderInfo) + (domainCheck : ConstructorCheckedExpr context domain) + (ensureType : ConstructorEnsureTypeObservation context domain) + (consumedCheck : ConstructorCheckedExpr context + (consumeTypeAnnotations domain)) + (annotations : CandidateIsDefEqObservation context domain + (consumeTypeAnnotations domain)) + (fresh : context.lctx.find? context.freshFVarId = none) + (tail : ConstructorPreFamilyRecursiveTrace stats familyIdx familyIndices + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) fuel) : + ConstructorPreFamilyRecursiveTrace stats familyIdx familyIndices context + (.forallE name domain body binderInfo) (fuel + 1) + | target + (context : Context) (source : Expr) + (valid : isValidIndAppIdx stats source familyIdx = true) + (spine : ConstructorPreFamilyIndexSpineTrace context familyIndices + (source.getAppArgs.toList.drop stats.params.size)) : + ConstructorPreFamilyRecursiveTrace stats familyIdx familyIndices context + source (fuel + 1) + +namespace ConstructorPreFamilyRecursiveTrace + +def build (stats : InductiveStats) (familyIdx : Nat) + (familyIndices : Expr) : + (context : Context) → (source : Expr) → (fuel : Nat) → + Except Exception + (ConstructorPreFamilyRecursiveTrace stats familyIdx familyIndices + context source fuel) + | _, _, 0 => throw .deepRecursion + | context, .forallE name domain body binderInfo, fuel + 1 => do + let domainCheck ← checkConstructorAlignedExpr context domain + let ensureType ← observeConstructorEnsureType context domain + let consumedCheck ← checkConstructorAlignedExpr context + (consumeTypeAnnotations domain) + let annotations ← observeCandidateIsDefEq context domain + (consumeTypeAnnotations domain) + if fresh : context.lctx.find? context.freshFVarId = none then + let tail ← build stats familyIdx familyIndices + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) fuel + pure <| .forallE context name domain body binderInfo domainCheck + ensureType consumedCheck annotations fresh tail + else + throw <| .other + "pre-family recursive replay reused a local identifier" + | context, source, _ + 1 => do + if valid : isValidIndAppIdx stats source familyIdx = true then + let spine ← ConstructorPreFamilyIndexSpineTrace.build context + familyIndices + (source.getAppArgs.toList.drop stats.params.size) + pure <| .target context source valid spine + else + throw <| .other + "pre-family recursive replay reached a non-family target" + +end ConstructorPreFamilyRecursiveTrace + +theorem ConstructorPreFamilyRecursiveTrace.forallE_build_eq + (domainCheck : ConstructorCheckedExpr context domain) + (ensureType : ConstructorEnsureTypeObservation context domain) + (consumedCheck : ConstructorCheckedExpr context + (consumeTypeAnnotations domain)) + (annotations : CandidateIsDefEqObservation context domain + (consumeTypeAnnotations domain)) + (fresh : context.lctx.find? context.freshFVarId = none) + (tail : ConstructorPreFamilyRecursiveTrace stats familyIdx familyIndices + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) fuel) + (tailRun : ConstructorPreFamilyRecursiveTrace.build stats familyIdx + familyIndices + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) fuel = .ok tail) : + ConstructorPreFamilyRecursiveTrace.build stats familyIdx familyIndices + context (.forallE name domain body binderInfo) (fuel + 1) = + .ok (.forallE context name domain body binderInfo domainCheck ensureType + consumedCheck annotations fresh tail) := by + simp only [ConstructorPreFamilyRecursiveTrace.build] + rw [domainCheck.check_eq, ensureType.observe_eq, consumedCheck.check_eq] + simp only [Bind.bind, Except.bind] + rw [annotations.observe_eq] + simp only [] + rw [dif_pos fresh, tailRun] + rfl + +theorem ConstructorPreFamilyRecursiveTrace.target_build_eq + (terminal : source.isForall = false) + (valid : isValidIndAppIdx stats source familyIdx = true) + (spine : ConstructorPreFamilyIndexSpineTrace context familyIndices + (source.getAppArgs.toList.drop stats.params.size)) : + ConstructorPreFamilyRecursiveTrace.build stats familyIdx familyIndices + context source (fuel + 1) = + .ok (.target context source valid spine) := by + cases source <;> simp only [ConstructorPreFamilyRecursiveTrace.build] + case forallE => simp [Expr.isForall] at terminal + all_goals + rw [dif_pos valid, spine.build_eq] + rfl + +/-! +### Verified pre-family recursive fields + +Recursive outer-field locals are intentionally absent here. Nested Pi binders +inside the field are family-free, so they are checked, interpreted, and pushed +normally. The terminal family application contributes only its already-verified +index spine. +-/ + +/-- Componentwise verified interpretation of a recursive field replay. -/ +inductive ConstructorPreFamilyRecursiveSemanticRun + (env : VEnv) (Us : List Name) + (stats : InductiveStats) (familyIdx : Nat) (familyIndices : Expr) : + {context : Context} → {source : Expr} → {fuel : Nat} → + (contextRun : ConstructorContextRun env Us context) → + ConstructorPreFamilyRecursiveTrace stats familyIdx familyIndices context + source fuel → Type where + | forallE + {context : Context} {name : Name} {domain body : Expr} + {binderInfo : BinderInfo} {fuel : Nat} + {domainCheck : ConstructorCheckedExpr context domain} + {ensureType : ConstructorEnsureTypeObservation context domain} + {consumedCheck : ConstructorCheckedExpr context + (consumeTypeAnnotations domain)} + {annotations : CandidateIsDefEqObservation context domain + (consumeTypeAnnotations domain)} + {fresh : context.lctx.find? context.freshFVarId = none} + {tailTrace : ConstructorPreFamilyRecursiveTrace stats familyIdx + familyIndices + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) fuel} + {contextRun : ConstructorContextRun env Us context} + (domainRun : ConstructorCheckedExpr.Run domainCheck + contextRun.candidate) + (consumedRun : ConstructorCheckedExpr.Run consumedCheck + contextRun.candidate) + (ensureTypeRun : TypeChecker.EnsureTypeRun + contextRun.candidate.context.venv + contextRun.candidate.context.lparams + contextRun.candidate.context.vlctx domain ensureType.result + domainRun.source') + (annotationsRun : TypeChecker.IsDefEqRun + contextRun.candidate.context.venv + contextRun.candidate.context.lparams + contextRun.candidate.context.vlctx domain + (consumeTypeAnnotations domain) domainRun.source' + consumedRun.source') + (consumedType : contextRun.candidate.context.IsType + consumedRun.source') + (tail : ConstructorPreFamilyRecursiveSemanticRun env Us stats familyIdx + familyIndices + (contextRun.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain) fresh consumedRun.source' + consumedRun.check.expr_tr consumedType) + tailTrace) : + ConstructorPreFamilyRecursiveSemanticRun env Us stats familyIdx + familyIndices contextRun + (.forallE context name domain body binderInfo domainCheck ensureType + consumedCheck annotations fresh tailTrace) + | target + {context : Context} {source : Expr} {fuel : Nat} + {valid : isValidIndAppIdx stats source familyIdx = true} + {spineTrace : ConstructorPreFamilyIndexSpineTrace context familyIndices + (source.getAppArgs.toList.drop stats.params.size)} + {contextRun : ConstructorContextRun env Us context} + (expected' : VExpr) + (spine : ConstructorPreFamilyIndexSpineSemanticRun env Us context + contextRun spineTrace expected') : + ConstructorPreFamilyRecursiveSemanticRun env Us stats familyIdx + familyIndices contextRun (.target context source valid spineTrace) + +namespace ConstructorPreFamilyRecursiveSemanticRun + +/-- Interpret every retained nested-binder and terminal-index operation in the +exact verified pre-family context. -/ +theorem nonempty + (contextRun : ConstructorContextRun env Us context) + (trace : ConstructorPreFamilyRecursiveTrace stats familyIdx familyIndices + context source fuel) : + Nonempty (ConstructorPreFamilyRecursiveSemanticRun env Us stats familyIdx + familyIndices contextRun trace) := by + induction trace with + | forallE context name domain body binderInfo domainCheck ensureType + consumedCheck annotations fresh tailTrace ih => + obtain ⟨domainRun⟩ := ConstructorCheckedExpr.Run.exists domainCheck + contextRun.candidate + obtain ⟨consumedRun⟩ := ConstructorCheckedExpr.Run.exists consumedCheck + contextRun.candidate + obtain ⟨ensureTypeRun⟩ := + TypeChecker.EnsureTypeRun.exists_ofConstructorStep + ⟨context, domain, ensureType.result⟩ ensureType.valid + contextRun.candidate domainRun.source' domainRun.check.expr_tr + let annotationsRun := domainRun.isDefEq consumedRun annotations + have consumedType : contextRun.candidate.context.IsType + consumedRun.source' := by + have annotationDef := annotationsRun.isDefEqU.of_l + contextRun.candidate.context.Ewf + contextRun.candidate.context.Δwf.toCtx ensureTypeRun.source_type + exact ⟨ensureTypeRun.resultLevel', annotationDef.hasType.2⟩ + let tailContext := contextRun.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain) fresh consumedRun.source' + consumedRun.check.expr_tr consumedType + obtain ⟨tail⟩ := ih tailContext + exact ⟨.forallE domainRun consumedRun ensureTypeRun annotationsRun + consumedType tail⟩ + | @target traceFuel context source valid spineTrace => + obtain ⟨expected', ⟨spine⟩⟩ := + ConstructorPreFamilyIndexSpineSemanticRun.nonempty contextRun spineTrace + exact ⟨@ConstructorPreFamilyRecursiveSemanticRun.target + env Us stats familyIdx familyIndices traceFuel + context source (traceFuel + 1) valid spineTrace contextRun expected' spine⟩ + +/-- The verified nested Π-binder telescope retained by the recursive field. -/ +def binders + {context : Context} {source : Expr} {fuel : Nat} + {contextRun : ConstructorContextRun env Us context} + {trace : ConstructorPreFamilyRecursiveTrace stats familyIdx familyIndices + context source fuel} + (run : ConstructorPreFamilyRecursiveSemanticRun env Us stats familyIdx + familyIndices contextRun trace) : List VExpr := + match run with + | .forallE _ consumedRun _ _ _ tail => consumedRun.source' :: tail.binders + | .target _ _ => [] + +/-- Translation selected for the analyzer-owned family-index telescope. -/ +def expected' + {context : Context} {source : Expr} {fuel : Nat} + {contextRun : ConstructorContextRun env Us context} + {trace : ConstructorPreFamilyRecursiveTrace stats familyIdx familyIndices + context source fuel} + (run : ConstructorPreFamilyRecursiveSemanticRun env Us stats familyIdx + familyIndices contextRun trace) : VExpr := + match run with + | .forallE _ _ _ _ _ tail => tail.expected' + | .target expected' _ => expected' + +/-- Translated terminal recursive indices, in source order. -/ +def indices' + {context : Context} {source : Expr} {fuel : Nat} + {contextRun : ConstructorContextRun env Us context} + {trace : ConstructorPreFamilyRecursiveTrace stats familyIdx familyIndices + context source fuel} + (run : ConstructorPreFamilyRecursiveSemanticRun env Us stats familyIdx + familyIndices contextRun trace) : List VExpr := + match run with + | .forallE _ _ _ _ _ tail => tail.indices' + | .target _ spine => spine.arguments' + +/-- Translated result type of the terminal recursive index application. -/ +def result' + {context : Context} {source : Expr} {fuel : Nat} + {contextRun : ConstructorContextRun env Us context} + {trace : ConstructorPreFamilyRecursiveTrace stats familyIdx familyIndices + context source fuel} + (run : ConstructorPreFamilyRecursiveSemanticRun env Us stats familyIdx + familyIndices contextRun trace) : VExpr := + match run with + | .forallE _ _ _ _ _ tail => tail.result' + | .target _ spine => spine.result' + +/-- The retained recursive Π domains form a verified Theory telescope in the +exact pre-family context. -/ +theorem onTel + {context : Context} {source : Expr} {fuel : Nat} + {contextRun : ConstructorContextRun env Us context} + {trace : ConstructorPreFamilyRecursiveTrace stats familyIdx familyIndices + context source fuel} + (run : ConstructorPreFamilyRecursiveSemanticRun env Us stats familyIdx + familyIndices contextRun trace) : + env.OnTel Us.length contextRun.candidate.context.vlctx.toCtx run.binders := by + induction run with + | @forallE context name domain body binderInfo fuel domainCheck ensureType + consumedCheck annotations fresh tailTrace branchContextRun domainRun + consumedRun ensureTypeRun annotationsRun consumedType tail ih => + constructor + · simpa only [VContext.IsType, branchContextRun.venv_eq, + branchContextRun.lparams_eq] using consumedType + · simpa only [binders, ConstructorContextRun.pushLocalDecl, + CandidateContextRun.pushLocalDecl_vlctx, VLCtx.toCtx] using ih + | target => trivial + +/-- The terminal recursive indices have the expected analyzer-owned family +index telescope, below all retained nested Π binders. -/ +theorem spine + {context : Context} {source : Expr} {fuel : Nat} + {contextRun : ConstructorContextRun env Us context} + {trace : ConstructorPreFamilyRecursiveTrace stats familyIdx familyIndices + context source fuel} + (run : ConstructorPreFamilyRecursiveSemanticRun env Us stats familyIdx + familyIndices contextRun trace) : + env.SpineWF Us.length + (run.binders.reverse ++ contextRun.candidate.context.vlctx.toCtx) + run.expected' run.indices' run.result' := by + induction run with + | forallE domainRun consumedRun ensureTypeRun annotationsRun consumedType tail ih => + simpa only [binders, expected', indices', result', List.reverse_cons, + List.singleton_append, List.append_assoc, + ConstructorContextRun.pushLocalDecl, + CandidateContextRun.pushLocalDecl_vlctx, VLCtx.toCtx] using ih + | target expectedType spine => + simpa only [binders, expected', indices', result', List.reverse_nil, + List.nil_append] using spine.spine + +/-- Weaken the retained recursive Π telescope over a later prefix of omitted +outer recursive fields. -/ +theorem onTel_weakPrefix + {context : Context} {source : Expr} {fuel : Nat} + {contextRun : ConstructorContextRun env Us context} + {trace : ConstructorPreFamilyRecursiveTrace stats familyIdx familyIndices + context source fuel} + (run : ConstructorPreFamilyRecursiveSemanticRun env Us stats familyIdx + familyIndices contextRun trace) (Bs : List VExpr) : + env.OnTel Us.length + (Bs ++ contextRun.candidate.context.vlctx.toCtx) + (VExpr.liftTelN Bs.length run.binders 0) := by + have henv : VEnv.WF env := by + simpa only [contextRun.venv_eq] using contextRun.candidate.context.Ewf + exact run.onTel.weakN henv.ordered + (Ctx.LiftN.zero (n := Bs.length) + (Γ := contextRun.candidate.context.vlctx.toCtx) Bs) + +/-- Weaken the terminal recursive index judgment below the same omitted outer +field prefix, preserving the nested Π-binder depths. -/ +theorem spine_weakPrefix + {context : Context} {source : Expr} {fuel : Nat} + {contextRun : ConstructorContextRun env Us context} + {trace : ConstructorPreFamilyRecursiveTrace stats familyIdx familyIndices + context source fuel} + (run : ConstructorPreFamilyRecursiveSemanticRun env Us stats familyIdx + familyIndices contextRun trace) (Bs : List VExpr) : + env.SpineWF Us.length + ((VExpr.liftTelN Bs.length run.binders 0).reverse ++ + Bs ++ contextRun.candidate.context.vlctx.toCtx) + (run.expected'.liftN Bs.length run.binders.length) + (run.indices'.map fun index => + index.liftN Bs.length run.binders.length) + (run.result'.liftN Bs.length run.binders.length) := by + have henv : VEnv.WF env := by + simpa only [contextRun.venv_eq] using contextRun.candidate.context.Ewf + simpa only [List.append_assoc, Nat.add_zero] using + run.spine.weakN henv.ordered + (Ctx.LiftN.consTel run.binders + (Ctx.LiftN.zero (n := Bs.length) + (Γ := contextRun.candidate.context.vlctx.toCtx) Bs)) + +end ConstructorPreFamilyRecursiveSemanticRun + +/-- Exact executable pre-family replay for one analyzer-owned constructor view. + +`removed` contains precisely the validation FVars allocated for recursive +outer fields that were not inserted into the pre-family checker context. +`recursiveStarted` records whether such a field has been crossed; later +ordinary fields are admitted exactly when they are independent of `removed`. -/ +inductive ConstructorPreFamilyViewTrace + (stats : InductiveStats) (familyIdx : Nat) (familyIndices : Expr) : + (context : Context) → (view : Expr) → (argIdx : Nat) → + (removed : List FVarId) → (recursiveStarted : Bool) → Type where + | parameter + (context : Context) (argIdx : Nat) (removed : List FVarId) + (recursiveStarted : Bool) + (name : Name) (domain body : Expr) (binderInfo : BinderInfo) + (parameter : Expr) + (parameterAt : stats.params[argIdx]? = some parameter) + (tail : ConstructorPreFamilyViewTrace stats familyIdx familyIndices + context (body.instantiate1 parameter) (argIdx + 1) removed + recursiveStarted) : + ConstructorPreFamilyViewTrace stats familyIdx familyIndices context + (.forallE name domain body binderInfo) argIdx removed recursiveStarted + | ordinary + (context : Context) (argIdx : Nat) (removed : List FVarId) + (recursiveStarted : Bool) + (name : Name) (domain body : Expr) (binderInfo : BinderInfo) + (noParameter : stats.params[argIdx]? = none) + (nonrecursive : hasIndOcc stats.indConsts domain = false) + (independent : constructorIndependentOf domain removed = true) + (domainCheck : ConstructorCheckedExpr context domain) + (ensureType : ConstructorEnsureTypeObservation context domain) + (consumedCheck : ConstructorCheckedExpr context + (consumeTypeAnnotations domain)) + (annotations : CandidateIsDefEqObservation context domain + (consumeTypeAnnotations domain)) + (fresh : context.lctx.find? context.freshFVarId = none) + (tail : ConstructorPreFamilyViewTrace stats familyIdx familyIndices + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) (argIdx + 1) removed + recursiveStarted) : + ConstructorPreFamilyViewTrace stats familyIdx familyIndices context + (.forallE name domain body binderInfo) argIdx removed recursiveStarted + | recursive + (context : Context) (argIdx : Nat) (removed : List FVarId) + (recursiveStarted : Bool) + (name : Name) (domain body : Expr) (binderInfo : BinderInfo) + (noParameter : stats.params[argIdx]? = none) + (isRecursive : hasIndOcc stats.indConsts domain = true) + (independent : constructorIndependentOf domain removed = true) + (field : ConstructorPreFamilyRecursiveTrace stats familyIdx + familyIndices context domain context.fuel.inductiveFuel) + (fresh : context.lctx.find? context.freshFVarId = none) + (tail : ConstructorPreFamilyViewTrace stats familyIdx familyIndices + context.advanceFresh (body.instantiate1 context.freshExpr) + (argIdx + 1) (context.freshFVarId :: removed) true) : + ConstructorPreFamilyViewTrace stats familyIdx familyIndices context + (.forallE name domain body binderInfo) argIdx removed recursiveStarted + | terminal + (context : Context) (source : Expr) (argIdx : Nat) + (removed : List FVarId) (recursiveStarted : Bool) + (valid : isValidIndAppIdx stats source familyIdx = true) + (independent : constructorIndependentOf source removed = true) + (spine : ConstructorPreFamilyIndexSpineTrace context familyIndices + (source.getAppArgs.toList.drop stats.params.size)) : + ConstructorPreFamilyViewTrace stats familyIdx familyIndices context + source argIdx removed recursiveStarted + +namespace ConstructorPreFamilyViewTrace + +def build (stats : InductiveStats) (familyIdx : Nat) + (familyIndices : Expr) : + (context : Context) → (view : Expr) → (argIdx : Nat) → + (removed : List FVarId) → (recursiveStarted : Bool) → (fuel : Nat) → + Except Exception + (ConstructorPreFamilyViewTrace stats familyIdx familyIndices context view + argIdx removed recursiveStarted) + | _, _, _, _, _, 0 => throw .deepRecursion + | context, .forallE name domain body binderInfo, argIdx, removed, + recursiveStarted, fuel + 1 => + match parameterAt : stats.params[argIdx]? with + | some param => do + let tail ← build stats familyIdx familyIndices context + (body.instantiate1 param) (argIdx + 1) removed + recursiveStarted fuel + pure <| .parameter context argIdx removed recursiveStarted name domain + body binderInfo param parameterAt tail + | none => do + match recursive : hasIndOcc stats.indConsts domain with + | false => + if independent : constructorIndependentOf domain removed = true then + let domainCheck ← checkConstructorAlignedExpr context domain + let ensureType ← observeConstructorEnsureType context domain + let consumedCheck ← checkConstructorAlignedExpr context + (consumeTypeAnnotations domain) + let annotations ← observeCandidateIsDefEq context domain + (consumeTypeAnnotations domain) + if fresh : context.lctx.find? context.freshFVarId = none then + let tail ← build stats familyIdx familyIndices + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) (argIdx + 1) + removed recursiveStarted fuel + pure <| .ordinary context argIdx removed recursiveStarted + name domain body binderInfo parameterAt recursive independent domainCheck + ensureType consumedCheck annotations fresh tail + else + throw <| .other + "pre-family ordinary replay reused a local identifier" + else + throw <| .other + "constructor depends on an omitted recursive local" + | true => + if independent : constructorIndependentOf domain removed = true then + let field ← ConstructorPreFamilyRecursiveTrace.build stats + familyIdx familyIndices context domain + context.fuel.inductiveFuel + if fresh : context.lctx.find? context.freshFVarId = none then + let tail ← build stats familyIdx familyIndices + context.advanceFresh + (body.instantiate1 context.freshExpr) (argIdx + 1) + (context.freshFVarId :: removed) true fuel + pure <| .recursive context argIdx removed recursiveStarted + name domain body binderInfo parameterAt recursive independent + field fresh tail + else + throw <| .other + "pre-family recursive replay reused a local identifier" + else + throw <| .other + "constructor depends on an omitted recursive local" + | context, source, argIdx, removed, recursiveStarted, _ + 1 => do + if valid : isValidIndAppIdx stats source familyIdx = true then + if independent : constructorIndependentOf source removed = true then + let spine ← ConstructorPreFamilyIndexSpineTrace.build context + familyIndices + (source.getAppArgs.toList.drop stats.params.size) + pure <| .terminal context source argIdx removed recursiveStarted valid + independent spine + else + throw <| .other + "constructor result depends on an omitted recursive local" + else + throw <| .other + "pre-family replay reached a non-family constructor result" + +end ConstructorPreFamilyViewTrace + +theorem ConstructorPreFamilyViewTrace.terminal_build_eq + (terminal : source.isForall = false) + (valid : isValidIndAppIdx stats source familyIdx = true) + (independent : constructorIndependentOf source removed = true) + (spine : ConstructorPreFamilyIndexSpineTrace context familyIndices + (source.getAppArgs.toList.drop stats.params.size)) : + ConstructorPreFamilyViewTrace.build stats familyIdx familyIndices + context source argIdx removed recursiveStarted (fuel + 1) = + .ok (.terminal context source argIdx removed recursiveStarted valid + independent spine) := by + cases source <;> simp only [ConstructorPreFamilyViewTrace.build] + case forallE => simp [Expr.isForall] at terminal + all_goals + rw [dif_pos valid, dif_pos independent, spine.build_eq] + rfl + +/-! +### Verified pre-family constructor views + +This interpretation follows the exact executable D3 trace. Ordinary fields +are checked and pushed in the pre-family context, recursive outer fields are +omitted while their family-free nested binders and indices are retained, and +the terminal result contributes its verified index spine. +-/ + +/-- Verified meaning of every family-free operation retained by one exact +pre-family constructor-view replay. -/ +inductive ConstructorPreFamilyViewSemanticRun + (env : VEnv) (Us : List Name) + (stats : InductiveStats) (familyIdx : Nat) (familyIndices : Expr) : + {context : Context} → {view : Expr} → {argIdx : Nat} → + {removed : List FVarId} → {recursiveStarted : Bool} → + (contextRun : ConstructorContextRun env Us context) → + ConstructorPreFamilyViewTrace stats familyIdx familyIndices context view + argIdx removed recursiveStarted → Type where + | parameter + {context : Context} {argIdx : Nat} {removed : List FVarId} + {recursiveStarted : Bool} + {name : Name} {domain body : Expr} {binderInfo : BinderInfo} + {parameter : Expr} + {parameterAt : stats.params[argIdx]? = some parameter} + {tailTrace : ConstructorPreFamilyViewTrace stats familyIdx familyIndices + context (body.instantiate1 parameter) (argIdx + 1) removed + recursiveStarted} + {contextRun : ConstructorContextRun env Us context} + (tail : ConstructorPreFamilyViewSemanticRun env Us stats familyIdx + familyIndices contextRun tailTrace) : + ConstructorPreFamilyViewSemanticRun env Us stats familyIdx familyIndices + contextRun + (.parameter context argIdx removed recursiveStarted name domain body + binderInfo parameter parameterAt tailTrace) + | ordinary + {context : Context} {argIdx : Nat} {removed : List FVarId} + {recursiveStarted : Bool} + {name : Name} {domain body : Expr} {binderInfo : BinderInfo} + {noParameter : stats.params[argIdx]? = none} + {nonrecursive : hasIndOcc stats.indConsts domain = false} + {independent : constructorIndependentOf domain removed = true} + {domainCheck : ConstructorCheckedExpr context domain} + {ensureType : ConstructorEnsureTypeObservation context domain} + {consumedCheck : ConstructorCheckedExpr context + (consumeTypeAnnotations domain)} + {annotations : CandidateIsDefEqObservation context domain + (consumeTypeAnnotations domain)} + {fresh : context.lctx.find? context.freshFVarId = none} + {tailTrace : ConstructorPreFamilyViewTrace stats familyIdx familyIndices + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + (body.instantiate1 context.freshExpr) (argIdx + 1) removed + recursiveStarted} + {contextRun : ConstructorContextRun env Us context} + (domainRun : ConstructorCheckedExpr.Run domainCheck + contextRun.candidate) + (consumedRun : ConstructorCheckedExpr.Run consumedCheck + contextRun.candidate) + (ensureTypeRun : TypeChecker.EnsureTypeRun + contextRun.candidate.context.venv + contextRun.candidate.context.lparams + contextRun.candidate.context.vlctx domain ensureType.result + domainRun.source') + (annotationsRun : TypeChecker.IsDefEqRun + contextRun.candidate.context.venv + contextRun.candidate.context.lparams + contextRun.candidate.context.vlctx domain + (consumeTypeAnnotations domain) domainRun.source' + consumedRun.source') + (consumedType : contextRun.candidate.context.IsType + consumedRun.source') + (tail : ConstructorPreFamilyViewSemanticRun env Us stats familyIdx + familyIndices + (contextRun.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain) fresh consumedRun.source' + consumedRun.check.expr_tr consumedType) + tailTrace) : + ConstructorPreFamilyViewSemanticRun env Us stats familyIdx familyIndices + contextRun + (.ordinary context argIdx removed recursiveStarted name domain body + binderInfo noParameter nonrecursive independent domainCheck ensureType + consumedCheck annotations fresh tailTrace) + | recursive + {context : Context} {argIdx : Nat} {removed : List FVarId} + {recursiveStarted : Bool} + {name : Name} {domain body : Expr} {binderInfo : BinderInfo} + {noParameter : stats.params[argIdx]? = none} + {isRecursive : hasIndOcc stats.indConsts domain = true} + {independent : constructorIndependentOf domain removed = true} + {fieldTrace : ConstructorPreFamilyRecursiveTrace stats familyIdx + familyIndices context domain context.fuel.inductiveFuel} + {fresh : context.lctx.find? context.freshFVarId = none} + {tailTrace : ConstructorPreFamilyViewTrace stats familyIdx familyIndices + context.advanceFresh (body.instantiate1 context.freshExpr) + (argIdx + 1) (context.freshFVarId :: removed) true} + {contextRun : ConstructorContextRun env Us context} + (field : ConstructorPreFamilyRecursiveSemanticRun env Us stats familyIdx + familyIndices contextRun fieldTrace) + (tail : ConstructorPreFamilyViewSemanticRun env Us stats familyIdx + familyIndices contextRun.advanceFresh tailTrace) : + ConstructorPreFamilyViewSemanticRun env Us stats familyIdx familyIndices + contextRun + (.recursive context argIdx removed recursiveStarted name domain body + binderInfo noParameter isRecursive independent fieldTrace fresh + tailTrace) + | terminal + {context : Context} {source : Expr} {argIdx : Nat} + {removed : List FVarId} {recursiveStarted : Bool} + {valid : isValidIndAppIdx stats source familyIdx = true} + {independent : constructorIndependentOf source removed = true} + {spineTrace : ConstructorPreFamilyIndexSpineTrace context familyIndices + (source.getAppArgs.toList.drop stats.params.size)} + {contextRun : ConstructorContextRun env Us context} + (expected' : VExpr) + (spine : ConstructorPreFamilyIndexSpineSemanticRun env Us context + contextRun spineTrace expected') : + ConstructorPreFamilyViewSemanticRun env Us stats familyIdx familyIndices + contextRun + (.terminal context source argIdx removed recursiveStarted valid + independent spineTrace) + +namespace ConstructorPreFamilyViewSemanticRun + +/-- Interpret every exact family-free checker observation in a successful +constructor-view replay. -/ +theorem nonempty + (contextRun : ConstructorContextRun env Us context) + (trace : ConstructorPreFamilyViewTrace stats familyIdx familyIndices + context view argIdx removed recursiveStarted) : + Nonempty (ConstructorPreFamilyViewSemanticRun env Us stats familyIdx + familyIndices contextRun trace) := by + induction trace with + | parameter context argIdx removed recursiveStarted name domain body + binderInfo parameter parameterAt tailTrace ih => + obtain ⟨tail⟩ := ih contextRun + exact ⟨.parameter tail⟩ + | ordinary context argIdx removed recursiveStarted name domain body + binderInfo noParameter nonrecursive independent domainCheck ensureType + consumedCheck annotations fresh tailTrace ih => + obtain ⟨domainRun⟩ := ConstructorCheckedExpr.Run.exists domainCheck + contextRun.candidate + obtain ⟨consumedRun⟩ := ConstructorCheckedExpr.Run.exists consumedCheck + contextRun.candidate + obtain ⟨ensureTypeRun⟩ := + TypeChecker.EnsureTypeRun.exists_ofConstructorStep + ⟨context, domain, ensureType.result⟩ ensureType.valid + contextRun.candidate domainRun.source' domainRun.check.expr_tr + let annotationsRun := domainRun.isDefEq consumedRun annotations + have consumedType : contextRun.candidate.context.IsType + consumedRun.source' := by + have annotationDef := annotationsRun.isDefEqU.of_l + contextRun.candidate.context.Ewf + contextRun.candidate.context.Δwf.toCtx ensureTypeRun.source_type + exact ⟨ensureTypeRun.resultLevel', annotationDef.hasType.2⟩ + let tailContext := contextRun.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain) fresh consumedRun.source' + consumedRun.check.expr_tr consumedType + obtain ⟨tail⟩ := ih tailContext + exact ⟨.ordinary domainRun consumedRun ensureTypeRun annotationsRun + consumedType tail⟩ + | recursive context argIdx removed recursiveStarted name domain body + binderInfo noParameter isRecursive independent fieldTrace fresh + tailTrace tailIH => + obtain ⟨field⟩ := + ConstructorPreFamilyRecursiveSemanticRun.nonempty contextRun fieldTrace + obtain ⟨tail⟩ := tailIH contextRun.advanceFresh + exact ⟨.recursive field tail⟩ + | terminal context source argIdx removed recursiveStarted valid independent + spineTrace => + obtain ⟨expected', ⟨spine⟩⟩ := + ConstructorPreFamilyIndexSpineSemanticRun.nonempty contextRun spineTrace + exact ⟨.terminal expected' spine⟩ + +end ConstructorPreFamilyViewSemanticRun + +private theorem drop_eq_cons_of_getElem?_eq_some + {values : List α} {index : Nat} {value : α} + (atIndex : values[index]? = some value) : + values.drop index = value :: values.drop (index + 1) := by + induction values generalizing index with + | nil => simp at atIndex + | cons head tail ih => + cases index with + | zero => + simp at atIndex + simp [atIndex] + | succ index => + simp at atIndex ⊢ + simpa [Nat.add_assoc, Nat.add_comm 1] using ih atIndex + +private theorem eq_length_of_getElem?_eq_none + {values : List α} {index : Nat} + (atIndex : values[index]? = none) + (indexLe : index ≤ values.length) : + index = values.length := by + induction values generalizing index with + | nil => simp_all + | cons head tail ih => + cases index with + | zero => simp at atIndex + | succ index => + simp at atIndex + simp only [List.length_cons, Nat.succ_le_succ_iff] at indexLe + have indexEq : index = tail.length := + Nat.le_antisymm indexLe atIndex + simp [indexEq] + +/-- The exact D3 suffix after consuming every validator-owned parameter. +The context is unchanged because parameter branches instantiate existing +locals rather than pushing constructor fields. -/ +structure ConstructorPreFamilyParameterSuffix + {env : VEnv} {Us : List Name} + {stats : InductiveStats} {familyIndices : Expr} + {context : Context} + {removed : List FVarId} {recursiveStarted : Bool} + {contextRun : ConstructorContextRun env Us context} + (rest : Expr) where + trace : ConstructorPreFamilyViewTrace stats 0 familyIndices + context rest stats.params.size removed recursiveStarted + semantic : ConstructorPreFamilyViewSemanticRun env Us stats 0 + familyIndices contextRun trace + +namespace ConstructorPreFamilyViewSemanticRun + +/-- Strip the exact validator-owned parameter prefix from a verified D3 +view trace. The successful executable instantiation equation excludes an +early terminal, while the indexed parameter lookups exclude an early field. -/ +theorem afterParameters + {env : VEnv} {Us : List Name} + {stats : InductiveStats} {familyIndices : Expr} + {familyName : Name} {levels : List Level} + {context : Context} {view rest : Expr} {argIdx : Nat} + {removed : List FVarId} {recursiveStarted : Bool} + {contextRun : ConstructorContextRun env Us context} + {trace : ConstructorPreFamilyViewTrace stats 0 familyIndices + context view argIdx removed recursiveStarted} + (semantic : ConstructorPreFamilyViewSemanticRun env Us stats 0 + familyIndices contextRun trace) + (indConsts : stats.indConsts = #[.const familyName levels]) + (argIdxLe : argIdx ≤ stats.params.size) + (instantiation : instantiateFamilyParameters view + (stats.params.toList.drop argIdx) = .ok rest) : + Nonempty (ConstructorPreFamilyParameterSuffix + (env := env) (Us := Us) (stats := stats) + (familyIndices := familyIndices) (context := context) + (removed := removed) (recursiveStarted := recursiveStarted) + (contextRun := contextRun) rest) := by + induction semantic generalizing rest with + | @parameter context argIdx removed recursiveStarted name domain body + binderInfo parameter parameterAt tailTrace contextRun tail ih => + have atList : stats.params.toList[argIdx]? = some parameter := by + simpa only [← Array.getElem?_toList] using parameterAt + have dropped := drop_eq_cons_of_getElem?_eq_some atList + rw [dropped] at instantiation + simp only [instantiateFamilyParameters] at instantiation + have argIdxLt : argIdx < stats.params.size := by + by_contra notLt + have argIdxEq : argIdx = stats.params.size := by omega + subst argIdx + simp at parameterAt + exact ih (by omega) instantiation + | @ordinary context argIdx removed recursiveStarted name domain body + binderInfo noParameter nonrecursive independent domainCheck ensureType + consumedCheck annotations fresh tailTrace contextRun domainRun consumedRun + ensureTypeRun annotationsRun consumedType tail => + have noParameterList : stats.params.toList[argIdx]? = none := by + simpa only [← Array.getElem?_toList] using noParameter + have argIdxEq : argIdx = stats.params.toList.length := + eq_length_of_getElem?_eq_none noParameterList (by simpa using argIdxLe) + have sizeEq : stats.params.toList.length = stats.params.size := by simp + rw [sizeEq] at argIdxEq + subst argIdx + have dropEq : stats.params.toList.drop stats.params.size = [] := by + simp + rw [dropEq] at instantiation + have viewEq : (.forallE name domain body binderInfo) = rest := + Except.ok.inj (by + change Except.ok (.forallE name domain body binderInfo) = + Except.ok rest at instantiation + exact instantiation) + subst rest + let suffixTrace := ConstructorPreFamilyViewTrace.ordinary context + stats.params.size removed recursiveStarted name domain body binderInfo + noParameter nonrecursive independent domainCheck ensureType consumedCheck + annotations fresh tailTrace + exact ⟨⟨suffixTrace, + ConstructorPreFamilyViewSemanticRun.ordinary domainRun consumedRun + ensureTypeRun annotationsRun consumedType tail⟩⟩ + | @recursive context argIdx removed recursiveStarted name domain body + binderInfo noParameter isRecursive independent fieldTrace fresh tailTrace + contextRun field tail => + have noParameterList : stats.params.toList[argIdx]? = none := by + simpa only [← Array.getElem?_toList] using noParameter + have argIdxEq : argIdx = stats.params.toList.length := + eq_length_of_getElem?_eq_none noParameterList (by simpa using argIdxLe) + have sizeEq : stats.params.toList.length = stats.params.size := by simp + rw [sizeEq] at argIdxEq + subst argIdx + have dropEq : stats.params.toList.drop stats.params.size = [] := by + simp + rw [dropEq] at instantiation + have viewEq : (.forallE name domain body binderInfo) = rest := + Except.ok.inj (by + change Except.ok (.forallE name domain body binderInfo) = + Except.ok rest at instantiation + exact instantiation) + subst rest + let suffixTrace := ConstructorPreFamilyViewTrace.recursive context + stats.params.size removed recursiveStarted name domain body binderInfo + noParameter isRecursive independent fieldTrace fresh tailTrace + exact ⟨⟨suffixTrace, + ConstructorPreFamilyViewSemanticRun.recursive field tail⟩⟩ + | @terminal context source argIdx removed recursiveStarted valid independent + spineTrace contextRun expected spine => + by_cases argIdxEq : argIdx = stats.params.size + · subst argIdx + have dropEq : stats.params.toList.drop stats.params.size = [] := by + simp + rw [dropEq] at instantiation + have viewEq : source = rest := + Except.ok.inj (by + change Except.ok source = Except.ok rest at instantiation + exact instantiation) + subst rest + let suffixTrace := ConstructorPreFamilyViewTrace.terminal context source + stats.params.size removed recursiveStarted valid independent + spineTrace + exact ⟨⟨suffixTrace, + ConstructorPreFamilyViewSemanticRun.terminal expected spine⟩⟩ + · have argIdxLt : argIdx < stats.params.size := by omega + obtain ⟨parameter, parameterAt⟩ : + ∃ parameter, stats.params.toList[argIdx]? = some parameter := by + have listLt : argIdx < stats.params.toList.length := by + simpa using argIdxLt + exact ⟨stats.params.toList[argIdx], by + simp only [List.getElem?_eq_getElem listLt]⟩ + have dropped := drop_eq_cons_of_getElem?_eq_some parameterAt + rw [dropped] at instantiation + cases source <;> + simp only [instantiateFamilyParameters] at instantiation + case forallE binderName binderType body binderInfo => + unfold isValidIndAppIdx at valid + rw [indConsts] at valid + simp only [Expr.withApp_eq, Expr.getAppFn] at valid + cases headEq : + ((.forallE binderName binderType body binderInfo : Expr) == + .const familyName levels) with + | false => simp [headEq] at valid + | true => + change Expr.eqv + (.forallE binderName binderType body binderInfo) + (.const familyName levels) = true at headEq + rw [Expr.eqv_eq] at headEq + simp [Expr.eqv'] at headEq + all_goals exact nomatch instantiation + +end ConstructorPreFamilyViewSemanticRun + +/-- The exact D2 suffix after consuming every validator-owned parameter. +Both the executable alignment and its verified semantic interpretation are +retained at the first ordinary field or terminal result. -/ +structure ConstructorViewParameterSuffix + {env : VEnv} {Us : List Name} {whnfFuel : Nat} + {stats : InductiveStats} {isUnsafe : Bool} {familyIdx : Nat} + {ctor : Name} {context : Context} + {contextRun : ConstructorContextRun env Us context} + (rest : Expr) where + source : Expr + fuel : Nat + trace : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source stats.params.size fuel + alignment : ConstructorViewAlignmentTrace trace rest + semantic : ConstructorViewSemanticRun env Us whnfFuel contextRun trace rest + +namespace ConstructorViewSemanticRun + +/-- Strip the exact parameter prefix shared by D2's validation trace and +analyzer view alignment. Parameter lookups exclude an early ordinary field; +the alignment's retained non-Pi fact excludes an early terminal. -/ +theorem afterParameters + {env : VEnv} {Us : List Name} {whnfFuel : Nat} + {stats : InductiveStats} {isUnsafe : Bool} {familyIdx : Nat} + {ctor : Name} {context : Context} {source view rest : Expr} + {argIdx fuel : Nat} + {contextRun : ConstructorContextRun env Us context} + {trace : ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context source argIdx fuel} + (alignment : ConstructorViewAlignmentTrace trace view) + (semantic : ConstructorViewSemanticRun env Us whnfFuel contextRun trace + view) + (argIdxLe : argIdx ≤ stats.params.size) + (instantiation : instantiateFamilyParameters view + (stats.params.toList.drop argIdx) = .ok rest) : + ∃ suffix : ConstructorViewParameterSuffix + (env := env) (Us := Us) (whnfFuel := whnfFuel) + (stats := stats) (isUnsafe := isUnsafe) (familyIdx := familyIdx) + (ctor := ctor) (context := context) (contextRun := contextRun) rest, + suffix.trace.universeSemantics = trace.universeSemantics := by + induction trace generalizing view rest with + | parameter context fuel argIdx name domain body binderInfo param + parameterType parameterAt parameterTypeRun validationDefEq tailTrace ih => + cases alignment with + | parameter domainCheck viewDomainCheck parameterTypeCheck + parameterShape parameterPresent _ tailAlignment => + cases semantic with + | parameter domainRun viewDomainRun parameterTypeSemantic validationRun + tail => + have atList : stats.params.toList[argIdx]? = some param := by + simpa only [← Array.getElem?_toList] using parameterAt + have dropped := drop_eq_cons_of_getElem?_eq_some atList + rw [dropped] at instantiation + simp only [instantiateFamilyParameters] at instantiation + obtain ⟨suffix, suffixUniverse⟩ := ih tailAlignment tail (by + have argIdxLt : argIdx < stats.params.size := by + by_contra notLt + have argIdxEq : argIdx = stats.params.size := by omega + subst argIdx + simp at parameterAt + omega) instantiation + exact ⟨suffix, by + simpa only [ConstructorTypeValidationTrace.universeSemantics] + using suffixUniverse⟩ + | ordinary context fuel argIdx name domain body binderInfo sortResult + noParameter ensureTypeStep universeTrace positivityTrace tailTrace ih => + cases alignment with + | @ordinary _ _ viewDomain _ _ _ _ _ _ _ _ _ _ _ _ _ viewName + viewBody viewBinderInfo domainCheck viewDomainCheck viewEquality + consumedCheck _ positivityAlignment fresh annotations _ + tailAlignment => + cases semantic with + | ordinary domainRun viewDomainRun viewEqualityRun consumedRun + ensureTypeRun positivity annotationsRun consumedType tail => + have noParameterList : stats.params.toList[argIdx]? = none := by + simpa only [← Array.getElem?_toList] using noParameter + have argIdxEq : argIdx = stats.params.toList.length := + eq_length_of_getElem?_eq_none noParameterList + (by simpa using argIdxLe) + have sizeEq : stats.params.toList.length = stats.params.size := by + simp + rw [sizeEq] at argIdxEq + subst argIdx + have dropEq : stats.params.toList.drop stats.params.size = [] := by + simp + rw [dropEq] at instantiation + injection instantiation with restEq + subst rest + let suffixTrace := ConstructorTypeValidationTrace.ordinary context + fuel stats.params.size name domain body binderInfo sortResult + noParameter ensureTypeStep universeTrace positivityTrace tailTrace + have suffixAlignment : ConstructorViewAlignmentTrace suffixTrace + (.forallE viewName viewDomain viewBody viewBinderInfo) := + ConstructorViewAlignmentTrace.ordinary domainCheck + viewDomainCheck viewEquality consumedCheck positivityTrace + positivityAlignment fresh annotations tailTrace tailAlignment + have suffixSemantic : ConstructorViewSemanticRun env Us whnfFuel + contextRun suffixTrace + (.forallE viewName viewDomain viewBody viewBinderInfo) := + ConstructorViewSemanticRun.ordinary + (viewEquality := viewEquality) domainRun viewDomainRun + viewEqualityRun consumedRun ensureTypeRun positivity + annotationsRun consumedType tail + exact ⟨⟨_, fuel + 1, suffixTrace, suffixAlignment, + suffixSemantic⟩, rfl⟩ + | terminal context source fuel argIdx sourceTerminal sourceValid => + cases alignment with + | terminal sourceCheck viewCheck viewTerminal viewValid => + cases semantic with + | terminal sourceRun viewRun => + by_cases argIdxEq : argIdx = stats.params.size + · subst argIdx + have dropEq : stats.params.toList.drop stats.params.size = [] := by + simp + rw [dropEq] at instantiation + have viewEq : view = rest := Except.ok.inj instantiation + subst rest + let suffixTrace : ConstructorTypeValidationTrace stats isUnsafe + familyIdx ctor context source stats.params.size (fuel + 1) := + ConstructorTypeValidationTrace.terminal context source fuel + stats.params.size sourceTerminal sourceValid + have suffixAlignment : ConstructorViewAlignmentTrace suffixTrace + view := + ConstructorViewAlignmentTrace.terminal sourceCheck viewCheck + viewTerminal viewValid + have suffixSemantic : ConstructorViewSemanticRun env Us whnfFuel + contextRun suffixTrace view := + ConstructorViewSemanticRun.terminal (isUnsafe := isUnsafe) + (ctor := ctor) sourceRun viewRun + exact ⟨⟨source, fuel + 1, suffixTrace, suffixAlignment, + suffixSemantic⟩, rfl⟩ + · have argIdxLt : argIdx < stats.params.size := by omega + obtain ⟨parameter, parameterAt⟩ : + ∃ parameter, stats.params.toList[argIdx]? = some parameter := by + have listLt : argIdx < stats.params.toList.length := by + simpa using argIdxLt + exact ⟨stats.params.toList[argIdx], by + simp only [List.getElem?_eq_getElem listLt]⟩ + have dropped := drop_eq_cons_of_getElem?_eq_some parameterAt + rw [dropped] at instantiation + cases view <;> + simp_all [instantiateFamilyParameters, Expr.isForall] + +end ConstructorViewSemanticRun + +private def candidateForallDepth : Expr → Nat + | .forallE _ _ body _ => candidateForallDepth body + 1 + | _ => 0 + +private theorem candidateForallDepth_le_instantiate1' + (expression argument : Expr) (depth : Nat) : + candidateForallDepth expression ≤ + candidateForallDepth (expression.instantiate1' argument depth) := by + induction expression generalizing depth <;> + simp [candidateForallDepth, Expr.instantiate1', *] + +private theorem candidateForallDepth_abstract1 + (expression : Expr) (id : FVarId) (depth : Nat) : + candidateForallDepth (Expr.abstract1 id expression depth) = + candidateForallDepth expression := by + induction expression generalizing depth <;> + simp [candidateForallDepth, Expr.abstract1, *] + split <;> rfl + +private theorem candidateForallDepth_abstractList + (expression : Expr) (ids : List FVarId) (depth : Nat) : + candidateForallDepth (Expr.abstractList expression ids depth) = + candidateForallDepth expression := by + induction ids generalizing expression with + | nil => rfl + | cons id ids ih => + simp only [Expr.abstractList, ih, candidateForallDepth_abstract1] + +private theorem candidateForallDepth_abstract + (expression : Expr) (ids : List FVarId) : + candidateForallDepth (expression.abstract ⟨ids.map Expr.fvar⟩) = + candidateForallDepth expression := by + rw [Expr.abstract_eq] + exact candidateForallDepth_abstractList expression ids 0 + +private theorem candidateView_forallDepth + (trace : CandidateExprTrace context source) : + trace.spineLength ≤ candidateForallDepth trace.view := by + induction trace with + | terminal => exact Nat.zero_le _ + | forallE context source inferred name domain body binderInfo fresh + annotations annotationsEq checked normalized domainCandidate + bodyCandidate domainIH bodyIH => + simp only [CandidateExprTrace.view, candidateForallDepth, + CandidateExprTrace.spineLength] + rw [show #[context.freshExpr] = + ⟨[context.freshFVarId].map Expr.fvar⟩ by rfl, + candidateForallDepth_abstract] + omega + +private theorem instantiateFamilyParameters_exists_of_forallDepth + (length : parameters.length ≤ candidateForallDepth source) : + ∃ rest, instantiateFamilyParameters source parameters = .ok rest := by + induction parameters generalizing source with + | nil => exact ⟨source, rfl⟩ + | cons parameter parameters ih => + cases source <;> simp [candidateForallDepth] at length + case forallE name domain body binderInfo => + have bodyLength : parameters.length ≤ candidateForallDepth + (body.instantiate1 parameter) := by + rw [Expr.instantiate1_eq] + exact Nat.le_trans + (by omega : parameters.length ≤ candidateForallDepth body) + (candidateForallDepth_le_instantiate1' body parameter 0) + obtain ⟨rest, restEq⟩ := ih bodyLength + exact ⟨rest, restEq⟩ + +/-- A reconstructed candidate view can instantiate any parameter list no +longer than its retained main Pi spine. This is only a syntactic success +fact; the exact Theory endpoint is supplied separately by strict translation. -/ +theorem CandidateExprTrace.instantiateViewParameters + (trace : CandidateExprTrace context source) + (parameters : List Expr) + (length : parameters.length ≤ trace.spineLength) : + ∃ rest, instantiateFamilyParameters trace.view parameters = .ok rest := by + apply instantiateFamilyParameters_exists_of_forallDepth + exact Nat.le_trans length (candidateView_forallDepth trace) + +namespace ConstructorPreFamilyViewSemanticRun + +/-- Weaken an exact ordinary-field type over any later field prefix. -/ +theorem ordinaryType_weakPrefix + (contextRun : ConstructorContextRun env Us context) + {fieldType : VExpr} + (fieldTypeWF : contextRun.candidate.context.IsType fieldType) + (Bs : List VExpr) : + env.IsType Us.length + (Bs ++ contextRun.candidate.context.vlctx.toCtx) + (fieldType.liftN Bs.length 0) := by + have henv : VEnv.WF env := by + simpa only [contextRun.venv_eq] using contextRun.candidate.context.Ewf + have fieldTypeWF' : env.IsType Us.length + contextRun.candidate.context.vlctx.toCtx fieldType := by + simpa only [VContext.IsType, contextRun.venv_eq, + contextRun.lparams_eq] using fieldTypeWF + exact fieldTypeWF'.weakN henv.ordered + (Ctx.LiftN.zero (n := Bs.length) + (Γ := contextRun.candidate.context.vlctx.toCtx) Bs) + +end ConstructorPreFamilyViewSemanticRun + +/-- Source-ordered D3 traces for the exact dependent constructor candidate +list selected by the producer. -/ +inductive ConstructorPreFamilyListTrace + (stats : InductiveStats) (familyIdx : Nat) (familyIndices : Expr) + (context : Context) : + {constructors : List Constructor} → + AddInductive.CandidateList AddInductive.CandidateConstructor constructors → + Type where + | nil : ConstructorPreFamilyListTrace stats familyIdx familyIndices context + .nil + | cons + (head : ConstructorPreFamilyViewTrace stats familyIdx familyIndices + context candidate.type.view 0 [] false) + (tail : ConstructorPreFamilyListTrace stats familyIdx familyIndices + context candidates) : + ConstructorPreFamilyListTrace stats familyIdx familyIndices context + (.cons candidate candidates) + +namespace ConstructorPreFamilyListTrace + +def build (stats : InductiveStats) (familyIdx : Nat) + (familyIndices : Expr) (context : Context) : + (candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor constructors) → + Except Exception + (ConstructorPreFamilyListTrace stats familyIdx familyIndices context + candidates) + | .nil => pure .nil + | .cons head tail => do + let headTrace ← ConstructorPreFamilyViewTrace.build stats familyIdx + familyIndices context head.type.view 0 [] false + context.fuel.inductiveFuel + let tailTrace ← build stats familyIdx familyIndices context tail + pure <| .cons headTrace tailTrace + +end ConstructorPreFamilyListTrace + +theorem ConstructorPreFamilyListTrace.cons_build_eq + (head : ConstructorPreFamilyViewTrace stats familyIdx familyIndices context + candidate.type.view 0 [] false) + (headRun : ConstructorPreFamilyViewTrace.build stats familyIdx + familyIndices context candidate.type.view 0 [] false + context.fuel.inductiveFuel = .ok head) + (tail : ConstructorPreFamilyListTrace stats familyIdx familyIndices context + candidates) + (tailRun : ConstructorPreFamilyListTrace.build stats familyIdx + familyIndices context candidates = .ok tail) : + ConstructorPreFamilyListTrace.build stats familyIdx familyIndices context + (.cons candidate candidates) = .ok (.cons head tail) := by + simp only [ConstructorPreFamilyListTrace.build] + rw [headRun] + simp only [Bind.bind, Except.bind] + rw [tailRun] + rfl + +/-- Source-ordered verified pre-family meaning for every analyzer-owned +constructor candidate selected by the executable D3 gate. -/ +inductive ConstructorPreFamilyListSemanticRun + (env : VEnv) (Us : List Name) + (stats : InductiveStats) (familyIdx : Nat) (familyIndices : Expr) + (context : Context) (contextRun : ConstructorContextRun env Us context) : + {constructors : List Constructor} → + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor constructors} → + ConstructorPreFamilyListTrace stats familyIdx familyIndices context + candidates → Type where + | nil : ConstructorPreFamilyListSemanticRun env Us stats familyIdx + familyIndices context contextRun (.nil) + | cons + {candidate : AddInductive.CandidateConstructor constructor} + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor constructors} + {headTrace : ConstructorPreFamilyViewTrace stats familyIdx familyIndices + context candidate.type.view 0 [] false} + {tailTrace : ConstructorPreFamilyListTrace stats familyIdx familyIndices + context candidates} + (head : ConstructorPreFamilyViewSemanticRun env Us stats familyIdx + familyIndices contextRun headTrace) + (tail : ConstructorPreFamilyListSemanticRun env Us stats familyIdx + familyIndices context contextRun tailTrace) : + ConstructorPreFamilyListSemanticRun env Us stats familyIdx + familyIndices context contextRun (.cons headTrace tailTrace) + +namespace ConstructorPreFamilyListSemanticRun + +/-- Interpret every constructor position retained by a successful executable +D3 list trace in the same verified pre-family context. -/ +theorem nonempty + (contextRun : ConstructorContextRun env Us context) + (trace : ConstructorPreFamilyListTrace stats familyIdx familyIndices + context candidates) : + Nonempty (ConstructorPreFamilyListSemanticRun env Us stats familyIdx + familyIndices context contextRun trace) := by + induction trace with + | nil => exact ⟨.nil⟩ + | cons headTrace tailTrace ih => + obtain ⟨head⟩ := ConstructorPreFamilyViewSemanticRun.nonempty contextRun + headTrace + obtain ⟨tail⟩ := ih + exact ⟨.cons head tail⟩ + +end ConstructorPreFamilyListSemanticRun + +/-- Exact output of the executable D3 gate. -/ +structure ConstructorPreFamilySafetyTrace + (stats : InductiveStats) (familyView : Expr) + (candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor constructors) + (context : Context) where + translationUnique : + (theoryTranslationUnique familyView && + candidates.viewTranslationUnique) = true + familyIndices : Expr + parameters : instantiateFamilyParameters familyView stats.params.toList = + .ok familyIndices + constructors : ConstructorPreFamilyListTrace stats 0 familyIndices context + candidates + +theorem ConstructorPreFamilySafetyTrace.familyTranslationUnique + (trace : ConstructorPreFamilySafetyTrace stats familyView candidates + context) : + TrExprS.IsUnique familyView := + by + have unique := trace.translationUnique + simp only [Bool.and_eq_true] at unique + exact theoryTranslationUnique_sound unique.1 + +theorem ConstructorPreFamilySafetyTrace.constructorTranslationUnique + (trace : ConstructorPreFamilySafetyTrace stats familyView candidates + context) : + candidates.ViewTranslationUnique := + by + have unique := trace.translationUnique + simp only [Bool.and_eq_true] at unique + exact candidates.viewTranslationUnique_sound unique.2 + +/-- Build the complete D3 trace or return the first structural/checker +failure. -/ +def buildConstructorPreFamilySafety + (stats : InductiveStats) (familyView : Expr) + (candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor constructors) + (context : Context) : + Except Exception + (ConstructorPreFamilySafetyTrace stats familyView candidates context) := + match translationUnique : + theoryTranslationUnique familyView && + candidates.viewTranslationUnique with + | false => throw <| .other + "candidate view contains a projection with no exact Theory endpoint" + | true => + match parameters : + instantiateFamilyParameters familyView stats.params.toList with + | .error error => .error error + | .ok familyIndices => do + let constructors ← ConstructorPreFamilyListTrace.build stats 0 + familyIndices context candidates + pure ⟨translationUnique, familyIndices, parameters, constructors⟩ + +/-! ### Executable pre-family rejection fixtures -/ + +private def preFamilyNegativeStats : InductiveStats where + levels := [] + resultLevel := .zero + nindices := #[0] + indConsts := #[.const `PreFamilyNegative []] + params := #[] + isNotZero := true + +private def preFamilyNegativeContext : Context where + env := Kernel.Environment.ofConstants `_preFamilyNegative + ({} : ConstMap) + lparams := [] + safety := .safe + allowPrimitive := false + +/- The traversal begins from the public gate's initial state. Its first +recursive field is omitted from the pre-family context, while the following +ordinary field is independent of that local and is therefore admissible. -/ +private def preFamilyOrdinaryAfterRecursiveView : Expr := + .forallE `recursive (.const `PreFamilyNegative []) + (.forallE `ordinary (.sort .zero) + (.const `PreFamilyNegative []) .default) + .default + +private def preFamilyOrdinaryAfterRecursiveAccepted : Bool := + match ConstructorPreFamilyViewTrace.build preFamilyNegativeStats 0 + (.sort (.succ .zero)) preFamilyNegativeContext + preFamilyOrdinaryAfterRecursiveView 0 [] false + preFamilyNegativeContext.fuel.inductiveFuel with + | .ok _ => true + | .error _ => false + +#guard preFamilyOrdinaryAfterRecursiveAccepted + +/- The first recursive local is deliberately omitted. Instantiating the next +recursive field exposes that FVar in its domain, so the dependency gate must +reject it before attempting the recursive-field replay. -/ +private def preFamilyRecursiveLocalDependencyView : Expr := + .forallE `recursive (.const `PreFamilyNegative []) + (.forallE `dependent + (.app (.const `PreFamilyNegative []) (.bvar 0)) + (.const `PreFamilyNegative []) .default) + .default + +private def preFamilyRecursiveLocalDependencyRejected : Bool := + match ConstructorPreFamilyViewTrace.build preFamilyNegativeStats 0 + (.sort (.succ .zero)) preFamilyNegativeContext + preFamilyRecursiveLocalDependencyView 0 [] false + preFamilyNegativeContext.fuel.inductiveFuel with + | .error (.other message) => + message == "constructor depends on an omitted recursive local" + | _ => false + +#guard preFamilyRecursiveLocalDependencyRejected + +/-- One executable D3 gate for the complete singleton constructor list. It +both enforces the recursive-suffix/dependency subset and re-runs every +family-free checker operation in the supplied pre-family context. -/ +def checkConstructorPreFamilySafety + (stats : InductiveStats) (familyView : Expr) + (candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor constructors) : M Unit := fun context => + if theoryTranslationUnique familyView && + candidates.viewTranslationUnique then do + let familyIndices ← instantiateFamilyParameters familyView + stats.params.toList + let _ ← ConstructorPreFamilyListTrace.build stats 0 familyIndices context + candidates + pure () + else + throw <| .other + "candidate view contains a projection with no exact Theory endpoint" + +/-- A successful executable D3 gate returns its exact parameter-instantiated +family telescope and source-ordered replay trace. -/ +theorem ConstructorPreFamilyListTrace.nonempty_of_check + (success : checkConstructorPreFamilySafety stats familyView candidates + context = .ok ()) : + Nonempty (ConstructorPreFamilySafetyTrace stats familyView candidates + context) := by + unfold checkConstructorPreFamilySafety at success + cases translationUnique : + theoryTranslationUnique familyView && + candidates.viewTranslationUnique with + | false => simp [translationUnique] at success + | true => + cases parameters : instantiateFamilyParameters familyView + stats.params.toList with + | error error => + simp [translationUnique, parameters, Bind.bind, Except.bind] at success + | ok familyIndices => + cases constructorsRun : ConstructorPreFamilyListTrace.build stats 0 + familyIndices context candidates with + | error error => + simp [translationUnique, parameters, constructorsRun, + Bind.bind, Except.bind] at success + | ok constructors => + exact ⟨⟨translationUnique, familyIndices, parameters, + constructors⟩⟩ + +/-- A successful executable D3 gate guarantees the retained builder returns +its trace, so gate owners can replay `buildConstructorPreFamilySafety` +instead of choosing from `Nonempty`. -/ +theorem buildConstructorPreFamilySafety_ok_of_check + (success : checkConstructorPreFamilySafety stats familyView candidates + context = .ok ()) : + ∃ trace, buildConstructorPreFamilySafety stats familyView candidates + context = .ok trace := by + unfold checkConstructorPreFamilySafety at success + unfold buildConstructorPreFamilySafety + split + next translationUnique => + simp [translationUnique] at success + next translationUnique => + split + next error parameters => + simp [translationUnique, parameters, Bind.bind, Except.bind] at success + next familyIndices parameters => + cases hbuild : ConstructorPreFamilyListTrace.build stats 0 familyIndices + context candidates with + | error error => + simp [translationUnique, parameters, hbuild, + Bind.bind, Except.bind] at success + | ok constructors => + exact ⟨_, rfl⟩ + +end AddInductive + +namespace TypeChecker +open AddInductive + +private theorem abstract1_instantiate_self + (expression : Expr) (id : FVarId) (depth : Nat) : + Closed expression depth → + (expression.abstract1 id depth).instantiate1' (.fvar id) depth = + expression := by + induction expression generalizing depth <;> + simp_all [Closed, Expr.abstract1, Expr.instantiate1', beq_iff_eq] + split <;> + simp_all [Expr.instantiate1', Expr.liftLooseBVars'] + +private theorem abstract_instantiate_self + (expression : Expr) (id : FVarId) (closed : Closed expression) : + (expression.abstract #[.fvar id]).instantiate1 (.fvar id) = + expression := by + rw [show #[Expr.fvar id] = ⟨[id].map Expr.fvar⟩ by rfl] + simp only [Expr.abstract_eq, Expr.abstractList, Expr.instantiate1_eq] + exact abstract1_instantiate_self expression id 0 closed + +/-- A source-ordered list of kernel parameter FVars builds an exact verified +local telescope. The final context is obtained by pushing each parameter in +order; dependency metadata is retained but never guessed by consumers. -/ +inductive CandidateParameterContext : + VLCtx → List Expr → List VExpr → VLCtx → Prop where + | nil : CandidateParameterContext base [] [] base + | cons + (tail : CandidateParameterContext + ((some (fv, deps), .vlam A) :: base) parameters types final) : + CandidateParameterContext base (.fvar fv :: parameters) + (A :: types) final + +/-- A completed analyzer-owned parameter telescope also certifies every +earlier context in that telescope. -/ +theorem CandidateParameterContext.initialWF + (params : CandidateParameterContext base parameters types final) + (finalWF : VLCtx.WF env U final) : + VLCtx.WF env U base := by + induction params with + | nil => exact finalWF + | cons tail ih => exact (ih finalWF).1 + +/-- The source parameter list and analyzer telescope carried by an exact +parameter context have the same number of entries. -/ +theorem CandidateParameterContext.length_eq + (params : CandidateParameterContext base parameters types final) : + parameters.length = types.length := by + induction params with + | nil => rfl + | cons tail ih => simp only [List.length_cons, ih] + +/-- Replay a successful kernel parameter instantiation against the exact +analyzer-owned Theory parameter telescope. Each source Pi body is opened by +the corresponding retained FVar; no whole-Pi injectivity or caller-selected +endpoint is used. -/ +theorem CandidateParameterContext.instantiateForall + (params : CandidateParameterContext base parameters types final) + (henv : VEnv.Ordered env) + (finalWF : VLCtx.WF env Us.length final) + (instantiation : instantiateFamilyParameters source parameters = .ok rest) + (tr : TrExprS env Us base source (VExpr.forallN types result)) : + TrExprS env Us final rest result := by + induction params generalizing source rest with + | nil => + have source_eq : source = rest := Except.ok.inj instantiation + subst rest + simpa only [VExpr.forallN] using tr + | @cons fv deps A base parameters types final tail ih => + cases source <;> simp only [instantiateFamilyParameters] at instantiation + case forallE name domain body binderInfo => + obtain ⟨domain', body', targetEq, domainType, bodyType, domainTr, + bodyTr⟩ := TrExprS.forallE_components tr + simp only [VExpr.forallN, VExpr.forallE.injEq] at targetEq + obtain ⟨rfl, rfl⟩ := targetEq + have nextWF : VLCtx.WF env Us.length + ((some (fv, deps), .vlam A) :: base) := + tail.initialWF finalWF + exact ih finalWF instantiation (by + simpa only [Expr.instantiate1_eq] using + bodyTr.inst_fvar henv nextWF) + all_goals exact nomatch instantiation + +/-- Consume an exact prefix of a strictly translated candidate view and keep +the terminal candidate context tied to the corresponding view telescope. + +The resulting `parameterContext` is the analyzer-owned prefix context. The +final `FVLift'` is over precisely the unconsumed suffix, so a strict +translation in the parameter context can be weakened to the same terminal +context used by pre-family constructor replay. -/ +theorem CandidateExprRun.parameterViewTerminal + {env : VEnv} {Us : List Name} + {candidateContext : AddInductive.Context} {source : Expr} + {trace : AddInductive.CandidateExprTrace candidateContext source} + {Δ : VLCtx} {source' view' inferred' : VExpr} + (run : CandidateExprRun env Us trace Δ source' view' inferred') + (contextRun : CandidateContextRun candidateContext) + (venv_eq : contextRun.context.venv = env) + (lparams_eq : contextRun.context.lparams = Us) + (vlctx_eq : contextRun.context.vlctx = Δ) + (unique : CandidateExprTraceViewIsUnique trace) + (count : Nat) (hcount : count ≤ trace.spineLength) + {viewΔ : VLCtx} + (viewDefEq : VLCtx.IsDefEq env Us.length Δ viewΔ) + (viewContext : TrExprS.IsUniqueCtx Δ viewΔ) + (noBV : Δ.NoBV) : + ∃ (parameterΔ : VLCtx) (rest : Expr) + (terminalRun : CandidateContextRun trace.terminalContext) + (viewTerminal : VLCtx), + instantiateFamilyParameters trace.view (trace.parameterList count) = + .ok rest ∧ + TrExprS env Us parameterΔ rest (VExpr.dropN count view') ∧ + parameterΔ.toCtx = + (VExpr.telN count view').reverse ++ viewΔ.toCtx ∧ + CandidateParameterContext viewΔ (trace.parameterList count) + (VExpr.telN count view') parameterΔ ∧ + parameterΔ.fvars.map Expr.fvar = + (trace.parameterList count).reverse ++ + viewΔ.fvars.map Expr.fvar ∧ + parameterΔ.NoBV ∧ + VLCtx.WF env Us.length parameterΔ ∧ + terminalRun.context.venv = env ∧ + terminalRun.context.lparams = Us ∧ + VLCtx.IsDefEq env Us.length terminalRun.context.vlctx viewTerminal ∧ + TrExprS.IsUniqueCtx terminalRun.context.vlctx viewTerminal ∧ + VLCtx.FVLift' parameterΔ viewTerminal 0 + (.skipN .refl (trace.spineLength - count)) 0 ∧ + viewTerminal.toCtx = + (VExpr.telN (trace.spineLength - count) + (VExpr.dropN count view')).reverse ++ parameterΔ.toCtx := by + induction run generalizing count viewΔ with + | @terminal Δ context source inferred result source' result' inferred' + checked normalized node => + simp only [AddInductive.CandidateExprTrace.spineLength] at hcount + have count_eq : count = 0 := Nat.eq_zero_of_le_zero hcount + subst count + have strict := + CandidateExprRun.view_tr_strict + (CandidateExprRun.terminal node) unique + have henv : VEnv.WF env := by + simpa only [node.check.venv_eq] using node.check.context.Ewf + obtain ⟨moved, movedTr⟩ := strict.defeqDFC henv viewDefEq + have moved_eq : result' = moved := + strict.unique' viewContext unique movedTr + subst moved + obtain ⟨terminalRun, viewTerminal, terminalVenv, terminalLparams, + terminalViewDefEq, terminalViewContext, terminalViewLift, + terminalViewEq⟩ := + (CandidateExprRun.terminal node).terminalContextRunView contextRun + venv_eq lparams_eq vlctx_eq viewDefEq viewContext + exact ⟨viewΔ, result, terminalRun, viewTerminal, rfl, movedTr, rfl, .nil, + by simp [AddInductive.CandidateExprTrace.parameterList], + by simpa only [VLCtx.NoBV, ← viewDefEq.bvars] using noBV, + (viewDefEq.symm henv.ordered).wf, + terminalVenv, terminalLparams, terminalViewDefEq, terminalViewContext, + by simpa only [Nat.sub_zero] using terminalViewLift, + by simpa only [Nat.sub_zero, VExpr.dropN] using terminalViewEq⟩ + | @forallE domain context name binderInfo Δ source inferred body + source' domain' body' inferred' domainView' domainInferred' + storedDomain' bodyΔ storedBody' bodyView' bodyInferred' u v fresh + checked normalized annotations annotationsEq domainCandidate + bodyCandidate node domainRun annotationsRun bodyRun domainType bodyType + bodySource bodyContext domainIH bodyIH => + have currentRun := CandidateExprRun.forallE + (fresh := fresh) (checked := checked) (normalized := normalized) + annotations annotationsEq domainCandidate bodyCandidate node domainRun + annotationsRun bodyRun domainType bodyType bodySource bodyContext + have henv : VEnv.WF env := by + simpa only [node.check.venv_eq] using node.check.context.Ewf + have hΔ : VLCtx.WF env Us.length Δ := by + simpa only [node.check.venv_eq, node.check.lparams_eq, + node.check.vlctx_eq] using node.check.context.Δwf + have allUnique := unique + rcases unique with ⟨domainUnique, bodyUnique, abstractUnique⟩ + cases count with + | zero => + have strict := currentRun.view_tr_strict allUnique + obtain ⟨moved, movedTr⟩ := strict.defeqDFC henv viewDefEq + have moved_eq : VExpr.forallE domainView' bodyView' = moved := + strict.unique' viewContext allUnique.view movedTr + subst moved + obtain ⟨terminalRun, viewTerminal, terminalVenv, terminalLparams, + terminalViewDefEq, terminalViewContext, terminalViewLift, + terminalViewEq⟩ := + currentRun.terminalContextRunView contextRun venv_eq lparams_eq + vlctx_eq viewDefEq viewContext + exact ⟨viewΔ, _, terminalRun, viewTerminal, rfl, movedTr, rfl, .nil, + by simp [AddInductive.CandidateExprTrace.parameterList], + by simpa only [VLCtx.NoBV, ← viewDefEq.bvars] using noBV, + (viewDefEq.symm henv.ordered).wf, + terminalVenv, terminalLparams, terminalViewDefEq, + terminalViewContext, + by simpa only [Nat.sub_zero] using terminalViewLift, + by simpa only [Nat.sub_zero, VExpr.dropN] using terminalViewEq⟩ + | succ count => + simp only [AddInductive.CandidateExprTrace.spineLength, + Nat.succ_le_succ_iff] at hcount + have domainDef : env.IsDefEq Us.length Δ.toCtx + domain' domainView' (.sort u) := + domainRun.evidence.isDefEq.toU.of_l henv hΔ.toCtx domainType + have annotationDef : env.IsDefEq Us.length Δ.toCtx + domain' storedDomain' (.sort u) := + annotationsRun.isDefEqU.of_l henv hΔ.toCtx domainType + have storedToView : env.IsDefEq Us.length Δ.toCtx + storedDomain' domainView' (.sort u) := + annotationDef.symm.trans domainDef + have storedDomain_tr : contextRun.context.TrExprS + annotations.consumed storedDomain' := by + simpa only [VContext.TrExprS, venv_eq, lparams_eq, vlctx_eq] using + annotationsRun.rhs_tr + have storedDomain_type : env.IsType Us.length Δ.toCtx storedDomain' := + ⟨u, annotationDef.hasType.2⟩ + let nextContextRun := contextRun.pushLocalDecl name binderInfo + annotations.consumed fresh storedDomain' storedDomain_tr (by + change contextRun.context.venv.IsType + contextRun.context.lparams.length + contextRun.context.vlctx.toCtx storedDomain' + rw [venv_eq, lparams_eq, vlctx_eq] + exact storedDomain_type) + have nextVenv : nextContextRun.context.venv = env := by + simp only [nextContextRun, CandidateContextRun.pushLocalDecl_venv, + venv_eq] + have nextLparams : nextContextRun.context.lparams = Us := by + simp only [nextContextRun, CandidateContextRun.pushLocalDecl_lparams, + lparams_eq] + have nextVlctx : nextContextRun.context.vlctx = bodyΔ := by + simp only [nextContextRun, CandidateContextRun.pushLocalDecl_vlctx] + rw [vlctx_eq, bodyContext] + let viewBodyΔ : VLCtx := + (some (context.freshFVarId, annotations.consumed.fvarsList), + .vlam domainView') :: viewΔ + have bodyWF := bodyRun.context_wf + rw [bodyContext] at bodyWF + have bodyViewDefEq : VLCtx.IsDefEq env Us.length bodyΔ viewBodyΔ := by + rw [bodyContext] + exact .cons viewDefEq bodyWF.2.1 (.vlam storedToView) + have bodyViewContext : TrExprS.IsUniqueCtx bodyΔ viewBodyΔ := by + rw [bodyContext] + exact viewContext.cons .vlam + have bodyNoBV : bodyΔ.NoBV := by + rw [bodyContext] + simpa only [VLCtx.NoBV, VLCtx.bvars] using noBV + obtain ⟨parameterΔ, rest, terminalRun, viewTerminal, restEq, + restTr, parameterCtx, parameterContext, parameterFVars, + parameterNoBV, parameterWF, terminalVenv, + terminalLparams, terminalViewDefEq, terminalViewContext, + terminalViewLift, terminalViewEq⟩ := + bodyIH nextContextRun nextVenv nextLparams nextVlctx bodyUnique + count hcount bodyViewDefEq bodyViewContext bodyNoBV + have bodyClosed : Closed bodyCandidate.view := by + have closed := (bodyRun.view_tr_strict bodyUnique).closed + simpa only [bodyNoBV] using closed + refine ⟨parameterΔ, rest, terminalRun, viewTerminal, ?_, ?_, ?_, + ?_, ?_, parameterNoBV, parameterWF, terminalVenv, terminalLparams, + ?_, ?_, ?_, ?_⟩ + · simp only [AddInductive.CandidateExprTrace.view, + AddInductive.CandidateExprTrace.parameterList, + instantiateFamilyParameters] + change instantiateFamilyParameters + ((bodyCandidate.view.abstract + #[.fvar context.freshFVarId]).instantiate1 + (.fvar context.freshFVarId)) _ = _ + rw [abstract_instantiate_self _ _ bodyClosed] + exact restEq + · simpa only [VExpr.dropN] using restTr + · simpa only [AddInductive.CandidateExprTrace.spineLength, + VExpr.telN, List.reverse_cons, List.singleton_append, + List.append_assoc, viewBodyΔ, VLCtx.toCtx] using parameterCtx + · simpa only [AddInductive.CandidateExprTrace.parameterList, + VExpr.telN, viewBodyΔ, AddInductive.Context.freshExpr] using + CandidateParameterContext.cons parameterContext + · simpa [AddInductive.CandidateExprTrace.parameterList, + List.reverse_cons, List.singleton_append, List.append_assoc, + viewBodyΔ, VLCtx.fvars, AddInductive.Context.freshExpr] using + parameterFVars + · simpa only [AddInductive.CandidateExprTrace.terminalContext] using + terminalViewDefEq + · simpa only [AddInductive.CandidateExprTrace.terminalContext] using + terminalViewContext + · simpa only [AddInductive.CandidateExprTrace.spineLength, + Nat.succ_sub_succ_eq_sub] using terminalViewLift + · simpa only [AddInductive.CandidateExprTrace.spineLength, + Nat.succ_sub_succ_eq_sub, VExpr.dropN] using terminalViewEq + +end TypeChecker + +namespace VInductDecl + +/-! +## Staged ownership + +The ordinary outer producer deliberately remains unchanged: its successful +equation records kernel validation, while this additive wrapper retains the +verified universe-semantic audit introduced by Spec-01D1 and extended by +Spec-02C. The normalized branch intersects the ordinary core decision with +the proved project comparison, without making bare +`buildNormalizationCandidate` success carry Theory meaning. +-/ + +/-- A staged singleton semantic input together with the exact executable +constructor-universe audit for the same source list, family-validation stats, +and post-family checker context. + +Keeping the ordinary staged input as data preserves the established candidate +and semantic hierarchy. The dependent `universeRun` field prevents an audit +for another family, constructor ordering, parameter split, or environment from +being reused here. -/ +structure StagedNormalizationCandidateUniverseInput + (familyContext constructorContext : AddInductive.Context) + (env : VEnv) (Us : List Name) + {source : InductiveType} + (candidate : AddInductive.NormalizationCandidate [source]) + (rawDecl : VInductDecl) where + staged : StagedNormalizationCandidateSemanticInput familyContext + constructorContext env Us candidate rawDecl + universeRun : + AddInductive.checkConstructorUniverseListSemantics + staged.family.validation.stats source.ctors + { candidate.families.singleton.familyType.type.trace.terminalContext with + env := constructorContext.env } = .ok () + +/-- Pair the retained ordinary validation trace with its source-indexed +universe audit. This is the strengthened D1 validation object; forgetting it +recovers exactly the pre-existing staged owner. -/ +def StagedNormalizationCandidateUniverseInput.semanticValidation + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (input : StagedNormalizationCandidateUniverseInput familyContext + constructorContext env Us candidate rawDecl) : + AddInductive.ConstructorSemanticValidationRun source + input.staged.family.validation.stats false + { candidate.families.singleton.familyType.type.trace.terminalContext with + env := constructorContext.env } where + validation := input.staged.constructorValidation + universeRun := input.universeRun + +/-- Every universe-bearing node in the staged source-ordered validation trace +passes the verified semantic universe gate. -/ +theorem StagedNormalizationCandidateUniverseInput.universeSemantics + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (input : StagedNormalizationCandidateUniverseInput familyContext + constructorContext env Us candidate rawDecl) : + input.staged.constructorValidation.universeSemantics = true := + input.semanticValidation.universeSemantics + +/-- Preserve the existing automatic semantic-hierarchy construction while +retaining the strengthened universe gate in its staged owner. -/ +theorem StagedNormalizationCandidateUniverseInput.exists + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (input : StagedNormalizationCandidateUniverseInput familyContext + constructorContext env Us candidate rawDecl) : + Nonempty (ProducedNormalizationCandidateSemanticRun familyContext + constructorContext env Us candidate rawDecl) := + input.staged.exists + +/-! +## Post-family constructor ownership + +This additive D2 owner keeps D1's universe gate and adds the exact executable +alignment between the retained validation telescope and every analyzer-owned +constructor candidate. It does not replay `checkConstructors` on the view or +claim any pre-family field judgment. +-/ + +/-- The staged semantic/universe owner together with the source-ordered +post-family constructor alignment audit. -/ +structure StagedNormalizationCandidatePostFamilyInput + (familyContext constructorContext : AddInductive.Context) + (env : VEnv) (Us : List Name) + {source : InductiveType} + (candidate : AddInductive.NormalizationCandidate [source]) + (rawDecl : VInductDecl) where + universeInput : StagedNormalizationCandidateUniverseInput familyContext + constructorContext env Us candidate rawDecl + alignment : AddInductive.ConstructorCandidateAlignmentTrace + universeInput.staged.family.validation.stats false 0 + { candidate.families.singleton.familyType.type.trace.terminalContext with + env := constructorContext.env } + universeInput.staged.constructorValidation.trace + candidate.families.singleton.constructors + +/-- Package a successful executable alignment audit into the staged D2 owner. +The direct `alignment` field also permits proof-oriented clients to assemble +the same indexed trace from already-retained checker observations. The +retained trace is computed by replaying the alignment builder; the audit +premise only discharges its impossible error branch. -/ +def StagedNormalizationCandidatePostFamilyInput.ofRun + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (universeInput : StagedNormalizationCandidateUniverseInput familyContext + constructorContext env Us candidate rawDecl) + (alignmentRun : + universeInput.staged.constructorValidation.trace.checkCandidateAlignment + candidate.families.singleton.constructors + { candidate.families.singleton.familyType.type.trace.terminalContext with + env := constructorContext.env } = .ok ()) : + StagedNormalizationCandidatePostFamilyInput familyContext + constructorContext env Us candidate rawDecl where + universeInput := universeInput + alignment := + match h : AddInductive.ConstructorCandidateAlignmentTrace.build + universeInput.staged.constructorValidation.trace + candidate.families.singleton.constructors with + | .ok alignment => alignment + | .error _ => + absurd + (AddInductive.ConstructorCandidateAlignmentTrace.build_ok_of_check + alignmentRun) + (by simp [h]) + +/-- The exact output of D2: the established produced semantic hierarchy plus +the actual post-family validation context, retained source/candidate +alignment, and a positional semantic interpretation of every exact candidate +view binder and terminal result. -/ +structure ProducedNormalizationCandidatePostFamilySemanticRun + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (input : StagedNormalizationCandidatePostFamilyInput familyContext + constructorContext env Us candidate rawDecl) where + produced : ProducedNormalizationCandidateSemanticRun familyContext + constructorContext env Us candidate rawDecl + contextRun : AddInductive.ConstructorContextRun + produced.semantic.family.typeEnv Us + { candidate.families.singleton.familyType.type.trace.terminalContext with + env := constructorContext.env } + alignment : AddInductive.ConstructorCandidateAlignmentTrace + input.universeInput.staged.family.validation.stats false 0 + { candidate.families.singleton.familyType.type.trace.terminalContext with + env := constructorContext.env } + input.universeInput.staged.constructorValidation.trace + candidate.families.singleton.constructors + constructors : AddInductive.ConstructorPostFamilySemanticListRun + produced.semantic.family.typeEnv Us + input.universeInput.staged.family.validation.stats false 0 + { candidate.families.singleton.familyType.type.trace.terminalContext with + env := constructorContext.env } + contextRun input.universeInput.staged.constructorValidation.trace + candidate.families.singleton.constructors alignment + produced.semantic.family.constructors + +/-- Interpret D2's executable alignment in the exact post-family Theory +environment obtained by the retained raw-family insertion. The only +existential selections are checker-produced Theory translations already +encapsulated by `Nonempty`; no caller supplies a view. -/ +theorem StagedNormalizationCandidatePostFamilyInput.exists + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (input : StagedNormalizationCandidatePostFamilyInput familyContext + constructorContext env Us candidate rawDecl) : + Nonempty (ProducedNormalizationCandidatePostFamilySemanticRun input) := by + obtain ⟨familySemantic⟩ := + input.universeInput.staged.semanticInput.family.exists + let semantic : NormalizationCandidateSemanticRun env Us candidate rawDecl := + { raw := input.universeInput.staged.raw + raw_types_eq := input.universeInput.staged.raw_types_eq + uvars_eq := input.universeInput.staged.declaration_uvars_eq + family := familySemantic } + let produced : ProducedNormalizationCandidateSemanticRun familyContext + constructorContext env Us candidate rawDecl := + { semantic := semantic + familyTypesProduced := + input.universeInput.staged.familyTypesProduced + familiesProduced := input.universeInput.staged.familiesProduced } + have typeEnv_eq : familySemantic.typeEnv = + input.universeInput.staged.family.typeEnv := by + exact Option.some.inj <| + familySemantic.addType.symm.trans + input.universeInput.staged.family.addInduct.env_add + obtain ⟨candidateContext, venv_eq, lparams_eq⟩ := + input.universeInput.staged.family.validationContextRun + familySemantic.type + let contextRun : AddInductive.ConstructorContextRun + produced.semantic.family.typeEnv Us + { candidate.families.singleton.familyType.type.trace.terminalContext with + env := constructorContext.env } := + ⟨candidateContext, venv_eq.trans typeEnv_eq.symm, lparams_eq⟩ + let alignment := input.alignment + obtain ⟨constructors⟩ := + AddInductive.ConstructorPostFamilySemanticListRun.nonempty_of_alignment + contextRun alignment produced.semantic.family.constructors + exact ⟨⟨produced, contextRun, alignment, constructors⟩⟩ + +/-! +## Pre-family constructor ownership + +The D3 owner extends the staged D2 package with the output of one executable +pre-family safety gate. Its semantic result is reconstructed from that exact +trace in the verified context reached by family normalization before the raw +family constant is inserted. +-/ + +/-- The staged D2 owner together with the exact executable pre-family safety +trace for the same singleton family view and dependent constructor list. -/ +structure StagedNormalizationCandidatePreFamilyInput + (familyContext constructorContext : AddInductive.Context) + (env : VEnv) (Us : List Name) + {source : InductiveType} + (candidate : AddInductive.NormalizationCandidate [source]) + (rawDecl : VInductDecl) where + postFamilyInput : StagedNormalizationCandidatePostFamilyInput familyContext + constructorContext env Us candidate rawDecl + safety : AddInductive.ConstructorPreFamilySafetyTrace + postFamilyInput.universeInput.staged.family.validation.stats + candidate.families.singleton.familyType.type.view + candidate.families.singleton.constructors + candidate.families.singleton.familyType.type.trace.terminalContext + +/-- Package a successful executable D3 gate into the staged owner. The gate +itself, rather than a caller-supplied Theory premise, selects the retained +parameter-instantiated family telescope and constructor traces. The trace +is computed by replaying the safety builder; the gate premise only +discharges its impossible error branch. -/ +def StagedNormalizationCandidatePreFamilyInput.ofRun + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (postFamilyInput : StagedNormalizationCandidatePostFamilyInput + familyContext constructorContext env Us candidate rawDecl) + (safetyRun : AddInductive.checkConstructorPreFamilySafety + postFamilyInput.universeInput.staged.family.validation.stats + candidate.families.singleton.familyType.type.view + candidate.families.singleton.constructors + candidate.families.singleton.familyType.type.trace.terminalContext = + .ok ()) : + StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us candidate rawDecl where + postFamilyInput := postFamilyInput + safety := + match h : AddInductive.buildConstructorPreFamilySafety + postFamilyInput.universeInput.staged.family.validation.stats + candidate.families.singleton.familyType.type.view + candidate.families.singleton.constructors + candidate.families.singleton.familyType.type.trace.terminalContext with + | .ok safety => safety + | .error _ => + absurd + (AddInductive.buildConstructorPreFamilySafety_ok_of_check safetyRun) + (by simp [h]) + +/-- D3's produced meaning: D2's post-family semantics together with the exact +verified pre-family context and source-ordered family-free replay selected by +the executable safety trace. -/ +structure ProducedNormalizationCandidatePreFamilySemanticRun + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us candidate rawDecl) where + postFamily : ProducedNormalizationCandidatePostFamilySemanticRun + input.postFamilyInput + contextRun : AddInductive.ConstructorContextRun env Us + candidate.families.singleton.familyType.type.trace.terminalContext + constructors : AddInductive.ConstructorPreFamilyListSemanticRun env Us + input.postFamilyInput.universeInput.staged.family.validation.stats 0 + input.safety.familyIndices + candidate.families.singleton.familyType.type.trace.terminalContext + contextRun input.safety.constructors + +/-- Interpret the executable D3 safety trace in the exact verified pre-family +context recovered from the retained family semantic normalization run. -/ +theorem StagedNormalizationCandidatePreFamilyInput.exists + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us candidate rawDecl) : + Nonempty (ProducedNormalizationCandidatePreFamilySemanticRun input) := by + obtain ⟨postFamily⟩ := input.postFamilyInput.exists + have raw_eq : postFamily.produced.semantic.raw = + input.postFamilyInput.universeInput.staged.raw := by + have singleton_eq := + postFamily.produced.semantic.raw_types_eq.symm.trans + input.postFamilyInput.universeInput.staged.raw_types_eq + injection singleton_eq + have familyType : TypeChecker.CandidateExprSemanticRootRun env Us + candidate.families.singleton.familyType.type + input.postFamilyInput.universeInput.staged.raw.type := by + rw [← raw_eq] + exact postFamily.produced.semantic.family.type + obtain ⟨candidateContext, venv_eq, lparams_eq⟩ := + input.postFamilyInput.universeInput.staged.family.preValidationContextRun + familyType + let contextRun : AddInductive.ConstructorContextRun env Us + candidate.families.singleton.familyType.type.trace.terminalContext := + ⟨candidateContext, venv_eq, lparams_eq⟩ + obtain ⟨constructors⟩ := + AddInductive.ConstructorPreFamilyListSemanticRun.nonempty contextRun + input.safety.constructors + exact ⟨⟨postFamily, contextRun, constructors⟩⟩ + +/-- Invert a verified iterated Pi type into its exact source-ordered +telescope and terminal type. -/ +private theorem isType_forallN_inv + (henv : VEnv.Ordered env) : + ∀ {As : List VExpr} {U : Nat} {Γ : List VExpr} {B : VExpr}, + env.IsType U Γ (VExpr.forallN As B) → + env.OnTel U Γ As ∧ + env.IsType U (As.reverse ++ Γ) B + | [], U, Γ, B, h => by + change env.IsType _ _ B at h + exact ⟨trivial, h⟩ + | A :: As, U, Γ, B, h => by + obtain ⟨hA, hrest⟩ := h.forallE_inv henv + obtain ⟨hAs, hB⟩ := isType_forallN_inv henv hrest + exact ⟨⟨hA, hAs⟩, by + simpa [List.reverse_cons, List.append_assoc] using hB⟩ + +/-- The family normalization trace and the analyzer's exact generation +equation derive the checked parameter/index telescope. No checked- or +view-well-formedness premise is accepted. -/ +theorem StagedNormalizationCandidatePreFamilyInput.familyOnTel + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us candidate rawDecl) + (normalization : NormalizationCandidateSemanticRun env Us candidate rawDecl) + (generation : GenerationChecked rawDecl) + (analysis : normalization.root.normalization.generation? = + some generation) : + env.OnTel rawDecl.uvars [] + (generation.block.checked.params ++ generation.block.checked.indices) := by + obtain ⟨_, recursive⟩ := normalization.family.type.recursive + have familyType : env.IsType Us.length [] normalization.family.type.view := + recursive.view_isType_of_terminalSort + input.postFamilyInput.universeInput.staged.family.validation.terminal_eq + have view_eq : normalization.family.type.view = + generation.block.checked.type.type := by + exact (congrArg (fun ty : VInductiveType => ty.type) + (normalization.root.familyViewType_eq analysis)).symm + rw [view_eq, generation.block.checked.type_eq, + ← VExpr.forallN_append] at familyType + have henv : VEnv.Ordered env := by + simpa only [normalization.family.type.venv_eq] using + normalization.family.type.contextRun.context.Ewf.ordered + have htel := (isType_forallN_inv henv familyType).1 + simpa only [normalization.uvars_eq] using htel + +open AddInductive TypeChecker + +private theorem familyTelNForallNLength : + ∀ (As : List VExpr) (B : VExpr), + VExpr.telN As.length (VExpr.forallN As B) = As + | [], _ => rfl + | _ :: As, B => by + simp only [List.length_cons, VExpr.forallN, VExpr.telN, + familyTelNForallNLength As B] + +private theorem familyDropNForallNLength : + ∀ (As : List VExpr) (B : VExpr), + VExpr.dropN As.length (VExpr.forallN As B) = B + | [], _ => rfl + | _ :: As, B => by + simp only [List.length_cons, VExpr.forallN, VExpr.dropN, + familyDropNForallNLength As B] + +/-- Consume the validator-owned parameter prefix of the retained family +candidate and identify both resulting contexts with the analyzer telescope. + +The executable D3 gate selects the residual kernel family expression. Strict +translation uniqueness, the exact generation shape, and dependent analysis +then identify its Theory endpoint and the terminal local context; neither a +view nor a view-context premise is supplied by the caller. -/ +theorem StagedNormalizationCandidatePreFamilyInput.familyParameterTerminal + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us candidate rawDecl) + (normalization : NormalizationCandidateSemanticRun env Us candidate rawDecl) + (generation : GenerationChecked rawDecl) + (analysis : normalization.root.normalization.generation? = + some generation) + (shape : normalization.generationShape = true) : + let trace := candidate.families.singleton.familyType.type.trace + ∃ (parameterΔ : VLCtx) (rest : Expr) + (terminalRun : CandidateContextRun trace.terminalContext) + (viewTerminal : VLCtx), + instantiateFamilyParameters trace.view + (trace.parameterList rawDecl.nparams) = .ok rest ∧ + TrExprS env Us parameterΔ rest + (VExpr.dropN rawDecl.nparams normalization.family.type.view) ∧ + parameterΔ.toCtx = + (VExpr.telN rawDecl.nparams normalization.family.type.view).reverse ∧ + CandidateParameterContext [] (trace.parameterList rawDecl.nparams) + (VExpr.telN rawDecl.nparams normalization.family.type.view) + parameterΔ ∧ + parameterΔ.fvars.map Expr.fvar = + (trace.parameterList rawDecl.nparams).reverse ∧ + parameterΔ.NoBV ∧ + VLCtx.WF env Us.length parameterΔ ∧ + terminalRun.context.venv = env ∧ + terminalRun.context.lparams = Us ∧ + VLCtx.IsDefEq env Us.length terminalRun.context.vlctx viewTerminal ∧ + TrExprS.IsUniqueCtx terminalRun.context.vlctx viewTerminal ∧ + VLCtx.FVLift' parameterΔ viewTerminal 0 + (.skipN .refl generation.block.checked.indices.length) 0 ∧ + viewTerminal.toCtx = + generation.block.checked.indices.reverse ++ parameterΔ.toCtx := by + dsimp only + have familyShape := shape + simp only [NormalizationCandidateSemanticRun.generationShape, + normalizationCandidateGenerationShape, Bool.and_eq_true, + beq_iff_eq] at familyShape + have spineLength_eq : + candidate.families.singleton.familyType.type.trace.spineLength = + (generation.block.checked.params ++ + generation.block.checked.indices).length := by + calc + _ = (VExpr.telN rawDecl.nparams normalization.raw.type ++ + ctorFields (VExpr.dropN rawDecl.nparams normalization.raw.type)).length := + familyShape.1.2 + _ = (generation.block.rawParams ++ + generation.block.rawIndices).length := by + simp only [NormalizedChecked.rawParams, NormalizedChecked.rawIndices, + NormalizationCandidateSemanticRun.root, + normalization.root.sourceType_eq generation] + _ = (generation.block.checked.params ++ + generation.block.checked.indices).length := by + simp only [List.length_append] + rw [generation.shape.2.1, generation.shape.2.2.1] + have parameterLength : generation.block.checked.params.length = + rawDecl.nparams := + generation.block.checked.direct_anatomy.2.1.trans + generation.block.nparams_eq.symm + have hcount : rawDecl.nparams ≤ + candidate.families.singleton.familyType.type.trace.spineLength := by + rw [spineLength_eq] + simpa only [List.length_append, parameterLength] using + Nat.le_add_right rawDecl.nparams + generation.block.checked.indices.length + have unique : CandidateExprTraceViewIsUnique + candidate.families.singleton.familyType.type.trace := by + apply CandidateExprTrace.viewTranslationUnique_sound + rw [CandidateExprTrace.viewTranslationUnique_eq] + have uniqueGate := input.safety.translationUnique + simp only [Bool.and_eq_true] at uniqueGate + exact uniqueGate.1 + obtain ⟨inferred, recursive⟩ := normalization.family.type.recursive + have rootWF : VLCtx.WF env Us.length ([] : VLCtx) := by + simpa only [normalization.family.type.venv_eq, + normalization.family.type.lparams_eq, + normalization.family.type.vlctx_eq] using + normalization.family.type.contextRun.context.Δwf + have henv : VEnv.Ordered env := by + simpa only [normalization.family.type.venv_eq] using + normalization.family.type.contextRun.context.Ewf.ordered + obtain ⟨parameterΔ, rest, terminalRun, viewTerminal, restEq, restTr, + parameterCtx, parameterContext, parameterFVars, parameterNoBV, + parameterWF, terminalVenv, terminalLparams, + terminalViewDefEq, terminalViewContext, terminalViewLift, + terminalViewEq⟩ := + recursive.parameterViewTerminal normalization.family.type.contextRun + normalization.family.type.venv_eq normalization.family.type.lparams_eq + normalization.family.type.vlctx_eq unique rawDecl.nparams hcount + (.refl henv rootWF) .base (by rfl) + have remaining_eq : + candidate.families.singleton.familyType.type.trace.spineLength - + rawDecl.nparams = generation.block.checked.indices.length := by + rw [spineLength_eq, List.length_append, + parameterLength, Nat.add_sub_cancel_left] + have view_eq : normalization.family.type.view = + generation.block.checked.type.type := + (congrArg (fun ty : VInductiveType => ty.type) + (normalization.root.familyViewType_eq analysis)).symm + have viewDrop_eq : + VExpr.dropN rawDecl.nparams normalization.family.type.view = + VExpr.forallN generation.block.checked.indices + (.sort generation.block.checked.resultLevel) := by + rw [view_eq, generation.block.checked.type_eq, ← parameterLength] + exact familyDropNForallNLength _ _ + refine ⟨parameterΔ, rest, terminalRun, viewTerminal, restEq, restTr, ?_, + parameterContext, ?_, parameterNoBV, parameterWF, terminalVenv, + terminalLparams, + terminalViewDefEq, + terminalViewContext, ?_, ?_⟩ + · simpa only [VLCtx.toCtx, List.append_nil] using parameterCtx + · simpa [VLCtx.fvars] using parameterFVars + · simpa only [remaining_eq] using terminalViewLift + · rw [remaining_eq, viewDrop_eq, + familyTelNForallNLength] at terminalViewEq + exact terminalViewEq + +/-- The analyzer-owned family prefix and D3's pre-family terminal context, +with the residual kernel expression fixed to the exact executable safety +trace and every Theory telescope component fixed to dependent analysis. -/ +theorem StagedNormalizationCandidatePreFamilyInput.familyContext + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us candidate rawDecl) + (normalization : NormalizationCandidateSemanticRun env Us candidate rawDecl) + (generation : GenerationChecked rawDecl) + (analysis : normalization.root.normalization.generation? = + some generation) + (shape : normalization.generationShape = true) : + let trace := candidate.families.singleton.familyType.type.trace + ∃ (parameterΔ : VLCtx) + (terminalRun : CandidateContextRun trace.terminalContext) + (viewTerminal : VLCtx), + instantiateFamilyParameters trace.view + (trace.parameterList rawDecl.nparams) = + .ok input.safety.familyIndices ∧ + TrExprS env Us parameterΔ input.safety.familyIndices + (VExpr.forallN generation.block.checked.indices + (.sort generation.block.checked.resultLevel)) ∧ + parameterΔ.toCtx = generation.block.checked.params.reverse ∧ + CandidateParameterContext [] (trace.parameterList rawDecl.nparams) + generation.block.checked.params parameterΔ ∧ + parameterΔ.fvars.map Expr.fvar = + (trace.parameterList rawDecl.nparams).reverse ∧ + parameterΔ.NoBV ∧ + VLCtx.WF env Us.length parameterΔ ∧ + terminalRun.context.venv = env ∧ + terminalRun.context.lparams = Us ∧ + VLCtx.IsDefEq env Us.length terminalRun.context.vlctx viewTerminal ∧ + TrExprS.IsUniqueCtx terminalRun.context.vlctx viewTerminal ∧ + VLCtx.FVLift' parameterΔ viewTerminal 0 + (.skipN .refl generation.block.checked.indices.length) 0 ∧ + viewTerminal.toCtx = + generation.block.checked.indices.reverse ++ + generation.block.checked.params.reverse := by + dsimp only + obtain ⟨parameterΔ, rest, terminalRun, viewTerminal, restEq, restTr, + parameterCtx, parameterContext, parameterFVars, parameterNoBV, + parameterWF, terminalVenv, terminalLparams, + terminalViewDefEq, terminalViewContext, terminalViewLift, + terminalViewEq⟩ := input.familyParameterTerminal normalization + generation analysis shape + let validation := + input.postFamilyInput.universeInput.staged.family.validation + have statsParams : validation.stats.params.toList = + candidate.families.singleton.familyType.type.trace.parameterList + rawDecl.nparams := by + rw [validation.stats_eq] + simp only [CandidateExprTrace.singletonCandidateInductiveStats, + validation, input.postFamilyInput.universeInput.staged.validation_nparams_eq] + have safetyParameters := input.safety.parameters + rw [statsParams] at safetyParameters + have rest_eq : rest = input.safety.familyIndices := by + exact Except.ok.inj (restEq.symm.trans safetyParameters) + subst rest + have parameterLength : generation.block.checked.params.length = + rawDecl.nparams := + generation.block.checked.direct_anatomy.2.1.trans + generation.block.nparams_eq.symm + have view_eq : normalization.family.type.view = + generation.block.checked.type.type := + (congrArg (fun ty : VInductiveType => ty.type) + (normalization.root.familyViewType_eq analysis)).symm + have viewDrop_eq : + VExpr.dropN rawDecl.nparams normalization.family.type.view = + VExpr.forallN generation.block.checked.indices + (.sort generation.block.checked.resultLevel) := by + rw [view_eq, generation.block.checked.type_eq, ← parameterLength] + exact familyDropNForallNLength _ _ + have parameterTel_eq : + VExpr.telN rawDecl.nparams normalization.family.type.view = + generation.block.checked.params := by + rw [view_eq, generation.block.checked.type_eq, ← parameterLength] + exact familyTelNForallNLength _ _ + refine ⟨parameterΔ, terminalRun, viewTerminal, restEq, ?_, ?_, + ?_, parameterFVars, parameterNoBV, parameterWF, terminalVenv, + terminalLparams, + terminalViewDefEq, + terminalViewContext, terminalViewLift, ?_⟩ + · simpa only [viewDrop_eq] using restTr + · simpa only [parameterTel_eq] using parameterCtx + · simpa only [parameterTel_eq] using parameterContext + · simpa only [parameterCtx, parameterTel_eq] using terminalViewEq + +/-- Peeling at most `n` binders cannot expose more than `n` telescope +entries. -/ +private theorem constructorTelN_length_le (n : Nat) (expression : VExpr) : + (VExpr.telN n expression).length ≤ n := by + induction n generalizing expression with + | zero => simp [VExpr.telN] + | succ n ih => cases expression <;> simp [VExpr.telN, ih] + +/-- Strip the validator-owned parameter prefix from one analyzer-selected +constructor and retain the exact D3 suffix together with its strict Theory +translation. + +The source parameter list is fixed by the family validation statistics, the +Theory parameter telescope is fixed by dependent analysis, and the complete +constructor endpoint is fixed by the analyzer-owned raw/view pairing. Thus +the residual endpoint is precisely the stored view fields followed by the +analyzed result target; no whole-Pi injectivity or caller-selected view is +used. -/ +theorem CandidateSemanticNormalizedCtorRun.preFamilySuffix + {env typeEnv : VEnv} {Us : List Name} + {source : VInductDecl} {generation : GenerationChecked source} + {kernelCtor : Constructor} + {candidateCtor : AddInductive.CandidateConstructor kernelCtor} + {rawCtor : VConstVal} + {root : CandidateConstructorSemanticRun typeEnv Us candidateCtor rawCtor} + {ctor : NormalizedCtor} + {stats : InductiveStats} {familyIndices : Expr} + {context : AddInductive.Context} + {contextRun : AddInductive.ConstructorContextRun env Us context} + {d3Trace : AddInductive.ConstructorPreFamilyViewTrace stats 0 + familyIndices context candidateCtor.type.view 0 [] false} + {parameterΔ : VLCtx} {parameters : List Expr} + (genRun : CandidateSemanticNormalizedCtorRun generation.block typeEnv Us + root ctor) + (addType : env ≤ typeEnv) + (parameterContext : CandidateParameterContext [] parameters + generation.block.checked.params parameterΔ) + (parameterWF : VLCtx.WF env Us.length parameterΔ) + (unique : CandidateExprTraceViewIsUnique candidateCtor.type.trace) + (d3 : AddInductive.ConstructorPreFamilyViewSemanticRun env Us stats 0 + familyIndices contextRun d3Trace) + (hctor : ctor ∈ generation.block.ctorPairs) + (parametersEq : stats.params.toList = parameters) + {familyName : Name} {levels : List Level} + (indConsts : stats.indConsts = #[.const familyName levels]) : + ∃ rest, + instantiateFamilyParameters candidateCtor.type.view stats.params.toList = + .ok rest ∧ + Nonempty (AddInductive.ConstructorPreFamilyParameterSuffix + (env := env) (Us := Us) (stats := stats) + (familyIndices := familyIndices) (context := context) + (removed := []) (recursiveStarted := false) + (contextRun := contextRun) rest) ∧ + TrExprS typeEnv Us parameterΔ rest + (VExpr.forallN ctor.view.fields + (ctor.resultTarget generation.block)) := by + have viewTel := genRun.run.viewTel_eq hctor + simp only [CandidateConstructorSemanticRun.root, + NormalizedCtor.viewBinders] at viewTel + have parameterLengthLe : generation.block.checked.params.length ≤ + candidateCtor.type.trace.spineLength := by + apply Nat.le_trans (Nat.le_add_right _ ctor.view.fields.length) + rw [← List.length_append, ← viewTel] + exact constructorTelN_length_le _ _ + have sourceParameterLength : stats.params.toList.length ≤ + candidateCtor.type.trace.spineLength := by + rw [parametersEq, parameterContext.length_eq] + exact parameterLengthLe + obtain ⟨rest, instantiation⟩ := + candidateCtor.type.trace.instantiateViewParameters stats.params.toList + sourceParameterLength + obtain ⟨inferred, recursive⟩ := root.type.recursive + have wholeTr : TrExprS typeEnv Us [] candidateCtor.type.view + (VExpr.forallN generation.block.checked.params + (VExpr.forallN ctor.view.fields + (ctor.resultTarget generation.block))) := by + have strict := recursive.view_tr_strict unique + have viewTypeEq : root.type.view = ctor.view.value.type := by + simpa only [CandidateConstructorSemanticRun.root, + CandidateConstructorRun.view] using + (congrArg (fun value : VConstVal => value.type) genRun.view_eq).symm + rw [viewTypeEq, generation.viewCtorType_eq hctor, + NormalizedCtor.viewBinders] at strict + simpa only [CandidateExpr.view, VExpr.forallN_append] using strict + have parameterWF' : VLCtx.WF typeEnv Us.length parameterΔ := + parameterWF.mono addType + have typeEnvOrdered : typeEnv.Ordered := by + simpa only [root.type.venv_eq] using + root.type.contextRun.context.Ewf.ordered + have suffixTr : TrExprS typeEnv Us parameterΔ rest + (VExpr.forallN ctor.view.fields + (ctor.resultTarget generation.block)) := by + apply parameterContext.instantiateForall typeEnvOrdered parameterWF' + · simpa only [← parametersEq] using instantiation + · exact wholeTr + obtain ⟨suffix⟩ := + d3.afterParameters indConsts (by simp) (by + simpa only [CandidateExpr.view, List.drop_zero] using instantiation) + exact ⟨rest, instantiation, ⟨suffix⟩, suffixTr⟩ + +end VInductDecl + +namespace ConstructorValidation +open AddInductive TypeChecker VEnv + +theorem TrExprS.IsUnique.liftLooseBVars + (unique : TrExprS.IsUnique expression) : + TrExprS.IsUnique (expression.liftLooseBVars' start amount) := by + induction expression generalizing start with + | bvar index => trivial + | fvar | mvar | sort | const | lit => trivial + | app function argument functionIH argumentIH => + exact ⟨functionIH unique.1, argumentIH unique.2⟩ + | lam name domain body binderInfo domainIH bodyIH + | forallE name domain body binderInfo domainIH bodyIH => + exact ⟨domainIH unique.1, bodyIH unique.2⟩ + | letE name type value body nondep typeIH valueIH bodyIH => + exact ⟨valueIH unique.1, bodyIH unique.2⟩ + | mdata data expression ih => exact ih unique + | proj => cases unique + +theorem TrExprS.IsUnique.instantiate1' + (expressionUnique : TrExprS.IsUnique expression) + (argumentUnique : TrExprS.IsUnique argument) : + TrExprS.IsUnique (expression.instantiate1' argument depth) := by + induction expression generalizing depth with + | bvar index => + simp only [Expr.instantiate1'] + split + · trivial + · split + · exact liftLooseBVars argumentUnique + · trivial + | fvar | mvar | sort | const | lit => trivial + | app function argument functionIH argumentIH => + exact ⟨functionIH expressionUnique.1, + argumentIH expressionUnique.2⟩ + | lam name domain body binderInfo domainIH bodyIH + | forallE name domain body binderInfo domainIH bodyIH => + exact ⟨domainIH expressionUnique.1, + bodyIH expressionUnique.2⟩ + | letE name type value body nondep typeIH valueIH bodyIH => + exact ⟨valueIH expressionUnique.1, + bodyIH expressionUnique.2⟩ + | mdata data expression ih => exact ih expressionUnique + | proj => cases expressionUnique + +theorem TrExprS.IsUnique.instantiate1 + (expressionUnique : TrExprS.IsUnique expression) + (argumentUnique : TrExprS.IsUnique argument) : + TrExprS.IsUnique (expression.instantiate1 argument) := by + rw [Expr.instantiate1_eq] + exact instantiate1' expressionUnique argumentUnique + +theorem FVarsIn.consumeTypeAnnotations + (scope : FVarsIn predicate source) : + FVarsIn predicate (AddInductive.consumeTypeAnnotations source) := by + fun_induction AddInductive.consumeTypeAnnotations source <;> + simp_all [FVarsIn] + +theorem instantiateFamilyParameters_unique + (sourceUnique : TrExprS.IsUnique source) + (parametersUnique : ∀ parameter ∈ parameters, + TrExprS.IsUnique parameter) + (run : AddInductive.instantiateFamilyParameters source parameters = + .ok rest) : + TrExprS.IsUnique rest := by + induction parameters generalizing source with + | nil => + have source_eq : source = rest := Except.ok.inj run + simpa only [source_eq] using sourceUnique + | cons parameter parameters ih => + cases source <;> + simp only [AddInductive.instantiateFamilyParameters] at run + case forallE name domain body binderInfo => + exact ih (ConstructorValidation.TrExprS.IsUnique.instantiate1 sourceUnique.2 + (parametersUnique parameter (.head parameters))) + (fun candidate member => + parametersUnique candidate (.tail parameter member)) run + all_goals exact nomatch run + +theorem CandidateParameterContext.parametersUnique + (parameters : CandidateParameterContext base sources types final) : + ∀ source ∈ sources, TrExprS.IsUnique source := by + induction parameters with + | nil => simp + | cons tail ih => + intro source member + simp only [List.mem_cons] at member + rcases member with rfl | member + · trivial + · exact ih source member + +/-- The semantic context invariants shared while D3 and D2 consume the +ordinary prefix of one analyzer-owned constructor view. -/ +structure ConstructorOrdinaryContextState + (env typeEnv : VEnv) (Us : List Name) + (base actual view postActual : VLCtx) (lift : Lift) where + baseWF : VLCtx.WF env Us.length base + actualWF : VLCtx.WF env Us.length actual + postWF : VLCtx.WF typeEnv Us.length postActual + postRelation : VLCtx.IsDefEqFVars typeEnv Us.length actual postActual + viewDefEq : VLCtx.IsDefEq env Us.length actual view + viewUnique : TrExprS.IsUniqueCtx actual view + viewLift : VLCtx.FVLift' base view 0 lift 0 + +/-- Extend the synchronized ordinary-prefix invariant by the exact analyzer +field selected by strict translation. D3 and D2 may retain different +dependency metadata, but use the same operational fresh identifier. -/ +theorem ConstructorOrdinaryContextState.push + {env typeEnv : VEnv} {Us : List Name} + {base actual view postActual : VLCtx} {lift : Lift} + (state : ConstructorOrdinaryContextState env typeEnv Us + base actual view postActual lift) + (henv : VEnv.WF env) (typeEnvWF : VEnv.WF typeEnv) + (addType : env ≤ typeEnv) + {source : Expr} {analyzer actualSource actualConsumed postConsumed : VExpr} + {fieldLevel consumedLevel : VLevel} {fv : FVarId} + {deps actualDeps postDeps : List FVarId} + (sourceUnique : TrExprS.IsUnique source) + (sourceTr : TrExprS typeEnv Us base source analyzer) + (actualTr : TrExprS env Us actual source actualSource) + (actualAnnotations : env.IsDefEqU Us.length actual.toCtx + actualSource actualConsumed) + (actualConsumedType : env.HasType Us.length actual.toCtx actualConsumed + (.sort consumedLevel)) + (analyzerType : env.HasType Us.length base.toCtx analyzer + (.sort fieldLevel)) + (depsSubset : deps ⊆ base.fvars) + (actualTailWF : VLCtx.WF env Us.length + ((some (fv, actualDeps), .vlam actualConsumed) :: actual)) + (actualDeps_eq : actualDeps = deps) + (postTailWF : VLCtx.WF typeEnv Us.length + ((some (fv, postDeps), .vlam postConsumed) :: postActual)) + (consumedEq : typeEnv.IsDefEq Us.length actual.toCtx + actualConsumed postConsumed (.sort consumedLevel)) : + ConstructorOrdinaryContextState env typeEnv Us + ((some (fv, deps), .vlam analyzer) :: base) + ((some (fv, actualDeps), .vlam actualConsumed) :: actual) + ((some (fv, deps), .vlam (analyzer.lift' lift)) :: view) + ((some (fv, postDeps), .vlam postConsumed) :: postActual) + (.consN lift 1) := by + subst actualDeps + have viewWF : VLCtx.WF env Us.length view := + (state.viewDefEq.symm henv.ordered).wf + have analyzerAtView : TrExprS typeEnv Us view source + (analyzer.lift' lift) := + sourceTr.weakFV' typeEnvWF.ordered state.viewLift + (viewWF.mono addType) + have actualSource_eq : actualSource = + analyzer.lift' lift := + (actualTr.mono addType).unique' state.viewUnique sourceUnique + analyzerAtView + have consumedToAnalyzer : env.IsDefEq Us.length actual.toCtx + actualConsumed (analyzer.lift' lift) + (.sort consumedLevel) := by + rw [← actualSource_eq] + exact (actualAnnotations.of_r henv state.actualWF.toCtx + actualConsumedType).symm + have freshBase : fv ∉ base.fvars := by + intro present + have presentView : fv ∈ view.fvars := + state.viewLift.fvars_sublist.subset present + have presentActual : fv ∈ actual.fvars := by + simpa only [state.viewDefEq.fvars] using presentView + exact (actualTailWF.2.1 fv deps rfl).1 presentActual + have baseTailWF : VLCtx.WF env Us.length + ((some (fv, deps), .vlam analyzer) :: base) := + ⟨state.baseWF, + fun _ _ equality => by + cases equality + exact ⟨freshBase, depsSubset⟩, + ⟨fieldLevel, analyzerType⟩⟩ + have nextViewDefEq : VLCtx.IsDefEq env Us.length + ((some (fv, deps), .vlam actualConsumed) :: actual) + ((some (fv, deps), .vlam (analyzer.lift' lift)) :: view) := + .cons state.viewDefEq actualTailWF.2.1 (.vlam consumedToAnalyzer) + exact { + baseWF := baseTailWF + actualWF := actualTailWF + postWF := postTailWF + postRelation := .cons_fvar state.postRelation (.vlam consumedEq) + viewDefEq := nextViewDefEq + viewUnique := state.viewUnique.cons .vlam + viewLift := state.viewLift.cons_fvar (fv, deps) (.vlam analyzer) + depsSubset } + +private theorem vexpr_appArgs_acc (expression : VExpr) (suffix : List VExpr) : + VExpr.appArgs expression suffix = + VExpr.appArgs expression [] ++ suffix := by + induction expression generalizing suffix with + | app function argument ihFunction ihArgument => + simp only [VExpr.appArgs] + rw [ihFunction, ihFunction (suffix := [argument])] + simp + | bvar | sort | const | lam | forallE => simp [VExpr.appArgs] + +private theorem expr_getAppArgsList_acc (expression : Expr) + (suffix : List Expr) : + expression.getAppArgsList suffix = + expression.getAppArgsList [] ++ suffix := by + induction expression generalizing suffix with + | app function argument ihFunction ihArgument => + simp only [Expr.getAppArgsList] + rw [ihFunction, ihFunction (suffix := [argument])] + simp + | bvar | fvar | mvar | sort | const | lit | mdata | proj | lam | forallE | + letE => simp [Expr.getAppArgsList] + +theorem TrExprS.IsUnique.getAppArgsList + (unique : TrExprS.IsUnique expression) : + ∀ argument ∈ expression.getAppArgsList, + TrExprS.IsUnique argument := by + induction expression with + | app function argument functionIH argumentIH => + intro candidate member + rw [Expr.getAppArgsList, expr_getAppArgsList_acc] at member + simp only [List.mem_append, List.mem_singleton] at member + rcases member with member | rfl + · exact functionIH unique.1 candidate member + · exact unique.2 + | bvar | fvar | mvar | sort | const | lit | mdata | proj | lam | forallE | + letE => simp [Expr.getAppArgsList] + +theorem Closed.getAppArgsList + (closed : Closed expression depth) : + ∀ argument ∈ expression.getAppArgsList, + Closed argument depth := by + induction expression with + | app function argument functionIH argumentIH => + intro candidate member + rw [Expr.getAppArgsList, expr_getAppArgsList_acc] at member + simp only [List.mem_append, List.mem_singleton] at member + rcases member with member | rfl + · exact functionIH closed.1 candidate member + · exact closed.2 + | bvar | fvar | mvar | sort | const | lit | mdata | proj | lam | forallE | + letE => simp [Expr.getAppArgsList] + +private theorem vexpr_appHead_appN (head : VExpr) (arguments : List VExpr) : + VExpr.appHead (VExpr.appN head arguments) = VExpr.appHead head := by + induction arguments generalizing head with + | nil => rfl + | cons argument arguments ih => + simp only [VExpr.appN] + rw [ih] + rfl + +private def vexprLiftTel (lift : Lift) : List VExpr → List VExpr + | [] => [] + | domain :: domains => + domain.lift' lift :: vexprLiftTel lift.cons domains + +private theorem vexpr_lift'_forallN (lift : Lift) : + ∀ (domains : List VExpr) (body : VExpr), + (VExpr.forallN domains body).lift' lift = + VExpr.forallN (vexprLiftTel lift domains) + (body.lift' (lift.consN domains.length)) + | [], _ => rfl + | domain :: domains, body => by + simp only [VExpr.forallN, VExpr.lift', vexprLiftTel, + List.length_cons] + rw [vexpr_lift'_forallN lift.cons domains body] + congr 2 + rw [show domains.length + 1 = 1 + domains.length by omega, + ← Lift.consN_consN] + rfl + +private theorem vexprLiftTel_length (lift : Lift) : + ∀ domains : List VExpr, + (vexprLiftTel lift domains).length = domains.length + | [] => rfl + | _ :: domains => by + simp only [vexprLiftTel, List.length_cons, + vexprLiftTel_length lift.cons domains] + +private theorem forall₂_append + (left : List.Forall₂ relation leftSources leftTargets) + (right : List.Forall₂ relation rightSources rightTargets) : + List.Forall₂ relation (leftSources ++ rightSources) + (leftTargets ++ rightTargets) := by + induction left with + | nil => exact right + | cons head tail ih => exact .cons head ih + +private theorem forall₂_length + (run : List.Forall₂ relation sources targets) : + sources.length = targets.length := by + induction run with + | nil => rfl + | cons _ _ ih => exact congrArg Nat.succ ih + +theorem forall₂_translation_unique + (left : List.Forall₂ (TrExprS env Us context) sources leftTargets) + (right : List.Forall₂ (TrExprS env Us context) sources rightTargets) + (unique : ∀ source ∈ sources, TrExprS.IsUnique source) : + leftTargets = rightTargets := by + induction left generalizing rightTargets with + | nil => cases right; rfl + | @cons source leftTarget sources leftTargets leftHead leftTail ih => + cases right with + | cons rightHead rightTail => + rw [leftHead.unique (unique source (.head sources)) rightHead, + ih rightTail (fun candidate member => + unique candidate (.tail source member))] + +theorem forall₂_drop + (run : List.Forall₂ relation sources targets) (n : Nat) : + List.Forall₂ relation (sources.drop n) (targets.drop n) := by + induction n generalizing sources targets with + | zero => simpa using run + | succ n ih => + cases run with + | nil => exact .nil + | cons head tail => simpa using ih tail + +theorem forall₂_tr_weakFV' + {env : VEnv} {Us : List Name} {base full : VLCtx} + {lift : Lift} (henv : VEnv.Ordered env) + (extension : VLCtx.FVLift' base full 0 lift 0) + (fullWF : VLCtx.WF env Us.length full) + (run : List.Forall₂ (TrExprS env Us base) sources targets) : + List.Forall₂ (TrExprS env Us full) sources + (targets.map fun target => target.lift' lift) := by + induction run with + | nil => exact .nil + | cons head tail ih => + exact .cons (by simpa using head.weakFV' henv extension fullWF) ih + +theorem forall₂_tr_mono + (add : env ≤ env') + (run : List.Forall₂ (TrExprS env Us context) sources targets) : + List.Forall₂ (TrExprS env' Us context) sources targets := by + induction run with + | nil => exact .nil + | cons head tail ih => exact .cons (head.mono add) ih + +theorem isValidIndAppIdx_shape + {stats : AddInductive.InductiveStats} {source : Expr} + {familyIdx : Nat} + (valid : AddInductive.isValidIndAppIdx stats source familyIdx = true) : + (source.getAppFn == stats.indConsts[familyIdx]!) = true ∧ + source.getAppArgs.size = + stats.params.size + stats.nindices[familyIdx]! := by + unfold AddInductive.isValidIndAppIdx at valid + rw [Expr.withApp_eq] at valid + simp only [Id.run] at valid + split at valid + · rename_i shape + simpa only [Bool.and_eq_true, beq_iff_eq] using shape + · simp_all + +theorem isValidIndAppIdx_indexArgs_length + {stats : AddInductive.InductiveStats} {source : Expr} + {familyIdx : Nat} + (valid : AddInductive.isValidIndAppIdx stats source familyIdx = true) : + (source.getAppArgs.toList.drop stats.params.size).length = + stats.nindices[familyIdx]! := by + have shape := isValidIndAppIdx_shape valid + rw [List.length_drop, Array.length_toList, shape.2] + omega + +theorem constructorIndependentOf_fvars + {source : Expr} {full base removed : List FVarId} + (scope : FVarsIn (· ∈ full) source) + (independent : AddInductive.constructorIndependentOf source removed = true) + (remaining : ∀ fv, fv ∈ full → fv ∉ removed → fv ∈ base) : + FVarsIn (· ∈ base) source := by + rw [fvarsIn_iff] at scope ⊢ + refine ⟨?_, scope.2⟩ + intro fv member + apply remaining fv (scope.1 fv member) + unfold AddInductive.constructorIndependentOf at independent + simp only [List.all_eq_true] at independent + have omitted := independent fv member + simpa using omitted + +/-- Strict translation preserves a constant-headed application spine and +translates its arguments position-for-position. -/ +theorem TrExprS.constApp_components + {env : VEnv} {Us : List Name} {context : VLCtx} + {source : Expr} {target : VExpr} {name : Name} {levels : List Level} + (run : TrExprS env Us context source target) + (head : source.getAppFn = .const name levels) : + ∃ levels', + VExpr.appHead target = .const name levels' ∧ + List.Forall₂ (TrExprS env Us context) + source.getAppArgsList (VExpr.appArgs target []) := by + induction source generalizing target with + | const sourceName sourceLevels => + cases run with + | const lookup levelsTr arity => + simp only [Expr.getAppFn] at head + obtain ⟨rfl, rfl⟩ := head + exact ⟨_, rfl, .nil⟩ + | app function argument functionIH argumentIH => + cases run with + | app => + rename_i function' domain' body' argument' functionType argumentType + functionTr argumentTr + have functionHead : function.getAppFn = .const name levels := by + simpa only [Expr.getAppFn] using head + obtain ⟨levels', targetHead, argumentsTr⟩ := + functionIH functionTr functionHead + refine ⟨levels', ?_, ?_⟩ + · simpa only [VExpr.appHead] using targetHead + · rw [show VExpr.appArgs (.app function' argument') [] = + VExpr.appArgs function' [] ++ [argument'] by + rw [VExpr.appArgs, vexpr_appArgs_acc]] + simp only [Expr.getAppArgsList] at * + rw [expr_getAppArgsList_acc] + exact forall₂_append argumentsTr (.cons argumentTr .nil) + | bvar => cases run; simp [Expr.getAppFn] at head + | fvar => cases run; simp [Expr.getAppFn] at head + | mvar => cases run + | sort => cases run; simp [Expr.getAppFn] at head + | lam => cases run; simp [Expr.getAppFn] at head + | forallE => cases run; simp [Expr.getAppFn] at head + | letE => cases run; simp [Expr.getAppFn] at head + | lit => cases run; simp [Expr.getAppFn] at head + | mdata => cases run; simp [Expr.getAppFn] at head + | proj => cases run; simp [Expr.getAppFn] at head + +/-- Remove one verified free-variable context extension from a list of strict +translations, retaining the exact lifted endpoints. -/ +theorem TrExprS.forall₂_weakFV_inv + {env : VEnv} {Us : List Name} {base full : VLCtx} + {sources : List Expr} {targets : List VExpr} {n : Lift} + (henv : VEnv.WF env) (fullWF : VLCtx.WF env Us.length full) + (extension : VLCtx.FVLift' base full 0 n 0) + (runs : List.Forall₂ (TrExprS env Us full) sources targets) + (closed : ∀ source ∈ sources, Closed source) + (fvars : ∀ source ∈ sources, FVarsIn (· ∈ base.fvars) source) + (unique : ∀ source ∈ sources, TrExprS.IsUnique source) : + ∃ baseTargets, + List.Forall₂ (TrExprS env Us base) sources baseTargets ∧ + targets = baseTargets.map (fun target => target.lift' (n.consN 0)) := by + induction runs with + | nil => exact ⟨[], .nil, rfl⟩ + | @cons source target sources targets headRun tailRuns ih => + have headClosed := closed source (.head sources) + have headFVars := fvars source (.head sources) + have headUnique := unique source (.head sources) + have tailClosed : ∀ expression ∈ sources, Closed expression := + fun expression member => closed expression (.tail source member) + have tailFVars : ∀ expression ∈ sources, + FVarsIn (· ∈ base.fvars) expression := + fun expression member => fvars expression (.tail source member) + have tailUnique : ∀ expression ∈ sources, + TrExprS.IsUnique expression := + fun expression member => unique expression (.tail source member) + obtain ⟨headBase, headBaseRun⟩ := + headRun.weakFV'_inv henv extension (.refl henv fullWF) + headClosed headFVars + obtain ⟨tailBase, tailBaseRuns, tailEq⟩ := + ih tailClosed tailFVars tailUnique + have headEq : target = headBase.lift' (n.consN 0) := + headRun.unique headUnique + (headBaseRun.weakFV' henv.ordered extension fullWF) + subst target + exact ⟨headBase :: tailBase, .cons headBaseRun tailBaseRuns, by + simp only [List.map_cons, tailEq]⟩ + +/-- Remove a free-variable extension modulo the verified context equality +used by the retained checker run. -/ +theorem TrExprS.forall₂_weakFV_inv_defeq + {env : VEnv} {Us : List Name} {base actual view : VLCtx} + {sources : List Expr} {targets : List VExpr} {n : Lift} + (henv : VEnv.WF env) + (viewDefEq : VLCtx.IsDefEq env Us.length actual view) + (viewUnique : TrExprS.IsUniqueCtx actual view) + (extension : VLCtx.FVLift' base view 0 n 0) + (runs : List.Forall₂ (TrExprS env Us actual) sources targets) + (closed : ∀ source ∈ sources, Closed source) + (fvars : ∀ source ∈ sources, FVarsIn (· ∈ base.fvars) source) + (unique : ∀ source ∈ sources, TrExprS.IsUnique source) : + ∃ baseTargets, + List.Forall₂ (TrExprS env Us base) sources baseTargets ∧ + targets = baseTargets.map (fun target => target.lift' (n.consN 0)) := by + have viewWF : VLCtx.WF env Us.length view := + (viewDefEq.symm henv.ordered).wf + induction runs with + | nil => exact ⟨[], .nil, rfl⟩ + | @cons source target sources targets headRun tailRuns ih => + have headClosed := closed source (.head sources) + have headFVars := fvars source (.head sources) + have headUnique := unique source (.head sources) + have tailClosed : ∀ expression ∈ sources, Closed expression := + fun expression member => closed expression (.tail source member) + have tailFVars : ∀ expression ∈ sources, + FVarsIn (· ∈ base.fvars) expression := + fun expression member => fvars expression (.tail source member) + have tailUnique : ∀ expression ∈ sources, + TrExprS.IsUnique expression := + fun expression member => unique expression (.tail source member) + obtain ⟨headBase, headBaseRun⟩ := + headRun.weakFV'_inv henv extension viewDefEq + headClosed headFVars + obtain ⟨tailBase, tailBaseRuns, tailEq⟩ := + ih tailClosed tailFVars tailUnique + have headEq : target = headBase.lift' (n.consN 0) := + headRun.unique' viewUnique headUnique + (headBaseRun.weakFV' henv.ordered extension viewWF) + subst target + exact ⟨headBase :: tailBase, .cons headBaseRun tailBaseRuns, by + simp only [List.map_cons, tailEq]⟩ + +theorem ConstructorPreFamilyIndexSpineSemanticRun.expected_eq_of_family_lift + {env : VEnv} {Us : List Name} {context : AddInductive.Context} + {contextRun : AddInductive.ConstructorContextRun env Us context} + {expected : Expr} {arguments : List Expr} + {trace : AddInductive.ConstructorPreFamilyIndexSpineTrace context expected + arguments} + {expected' : VExpr} + (run : AddInductive.ConstructorPreFamilyIndexSpineSemanticRun env Us + context contextRun trace expected') + {base view : VLCtx} {expectedBase : VExpr} {lift : Lift} + (familyTr : TrExprS env Us base expected expectedBase) + (unique : TrExprS.IsUnique expected) + (extension : VLCtx.FVLift' base view 0 lift 0) + (viewDefEq : VLCtx.IsDefEq env Us.length + contextRun.candidate.context.vlctx view) + (viewUnique : TrExprS.IsUniqueCtx + contextRun.candidate.context.vlctx view) : + expected' = expectedBase.lift' (lift.consN 0) := by + have henv : VEnv.Ordered env := by + simpa only [contextRun.venv_eq] using + contextRun.candidate.context.Ewf.ordered + have viewWF : VLCtx.WF env Us.length view := + (viewDefEq.symm henv).wf + have familyAtView : TrExprS env Us view expected + (expectedBase.lift' (lift.consN 0)) := + familyTr.weakFV' henv extension viewWF + have retained : TrExprS env Us contextRun.candidate.context.vlctx + expected expected' := run.expectedRun.expr_tr + exact retained.unique' viewUnique unique familyAtView + +theorem VLCtx.FVLift'.toCtxLiftN + {base view : VLCtx} {n : Nat} + (extension : VLCtx.FVLift' base view 0 (.skipN .refl n) 0) : + Ctx.LiftN n 0 base.toCtx view.toCtx := by + exact Ctx.liftN_iff_lift'.2 extension.toCtx + +/-- Transport a spine to the canonical view context and then remove the +free-variable extension from all of its endpoints. -/ +theorem VEnv.SpineWF.fvLift_inv + {env : VEnv} {U n : Nat} {base actual view : VLCtx} + {arguments : List VExpr} {source target : VExpr} + (henv : VEnv.WF env) + (viewDefEq : VLCtx.IsDefEq env U actual view) + (extension : VLCtx.FVLift' base view 0 (.skipN .refl n) 0) + (run : env.SpineWF U actual.toCtx (source.liftN n 0) + (arguments.map fun argument => argument.liftN n 0) + (target.liftN n 0)) : + env.SpineWF U base.toCtx source arguments target := by + have viewWF : VLCtx.WF env U view := + (viewDefEq.symm henv.ordered).wf + have viewRun : env.SpineWF U view.toCtx (source.liftN n 0) + (arguments.map fun argument => argument.liftN n 0) + (target.liftN n 0) := + run.defeqDFC henv.ordered viewDefEq.defeqCtx + exact VEnv.SpineWF.weakN_inv henv viewWF.toCtx + (Ctx.liftN_iff_lift'.2 extension.toCtx) viewRun + +theorem VEnv.SpineWF.fvLift'_inv + {env : VEnv} {U : Nat} {lift : Lift} {base actual view : VLCtx} + {arguments : List VExpr} {source target : VExpr} + (henv : VEnv.WF env) + (viewDefEq : VLCtx.IsDefEq env U actual view) + (extension : VLCtx.FVLift' base view 0 lift 0) + (run : env.SpineWF U actual.toCtx (source.lift' (lift.consN 0)) + (arguments.map fun argument => argument.lift' (lift.consN 0)) + (target.lift' (lift.consN 0))) : + env.SpineWF U base.toCtx source arguments target := by + have viewWF : VLCtx.WF env U view := + (viewDefEq.symm henv.ordered).wf + have viewRun : env.SpineWF U view.toCtx + (source.lift' (lift.consN 0)) + (arguments.map fun argument => argument.lift' (lift.consN 0)) + (target.lift' (lift.consN 0)) := + run.defeqDFC henv.ordered viewDefEq.defeqCtx + exact VEnv.SpineWF.weak'_inv henv viewWF.toCtx extension.toCtx viewRun + +/-- Identify a pre-family index replay with the analyzer family telescope, +remove the family-index locals, and retarget its terminal to the family sort. -/ +theorem ConstructorPreFamilyIndexSpineSemanticRun.baseSpine + {env : VEnv} {Us : List Name} {context : AddInductive.Context} + {contextRun : AddInductive.ConstructorContextRun env Us context} + {expected : Expr} {arguments : List Expr} + {trace : AddInductive.ConstructorPreFamilyIndexSpineTrace context expected + arguments} + {expected' : VExpr} {base view : VLCtx} {indices : List VExpr} + {level : VLevel} {n : Nat} + (run : AddInductive.ConstructorPreFamilyIndexSpineSemanticRun env Us + context contextRun trace expected') + (familyTr : TrExprS env Us base expected + (VExpr.forallN indices (.sort level))) + (expectedUnique : TrExprS.IsUnique expected) + (viewDefEq : VLCtx.IsDefEq env Us.length + contextRun.candidate.context.vlctx view) + (viewUnique : TrExprS.IsUniqueCtx + contextRun.candidate.context.vlctx view) + (extension : VLCtx.FVLift' base view 0 (.skipN .refl n) 0) + (argumentClosed : ∀ argument ∈ arguments, Closed argument) + (argumentFVars : ∀ argument ∈ arguments, + FVarsIn (· ∈ base.fvars) argument) + (argumentUnique : ∀ argument ∈ arguments, + TrExprS.IsUnique argument) + (argumentLength : arguments.length = indices.length) : + ∃ indices', + List.Forall₂ (TrExprS env Us base) arguments indices' ∧ + env.SpineWF Us.length base.toCtx + (VExpr.forallN indices (.sort level)) indices' (.sort level) := by + have henv : VEnv.WF env := by + simpa only [contextRun.venv_eq] using + contextRun.candidate.context.Ewf + have expectedEq : expected' = + (VExpr.forallN indices (.sort level)).liftN n 0 := + run.expected_eq_of_family familyTr expectedUnique extension viewDefEq + viewUnique + subst expected' + obtain ⟨indices', indicesTr, indicesEq⟩ := + TrExprS.forall₂_weakFV_inv_defeq henv viewDefEq viewUnique + extension run.arguments_tr argumentClosed argumentFVars argumentUnique + have translatedLength : run.arguments'.length = + (VExpr.liftTelN n indices 0).length := by + rw [← forall₂_length run.arguments_tr, argumentLength, + VExpr.liftTelN_length] + have liftedSpine : env.SpineWF Us.length + contextRun.candidate.context.vlctx.toCtx + ((VExpr.forallN indices (.sort level)).liftN n 0) + run.arguments' (.sort level) := by + have sourceEq : + (VExpr.forallN indices (.sort level)).liftN n 0 = + VExpr.forallN (VExpr.liftTelN n indices 0) (.sort level) := by + rw [VExpr.liftN_forallN] + rfl + have spine : env.SpineWF Us.length + contextRun.candidate.context.vlctx.toCtx + (VExpr.forallN (VExpr.liftTelN n indices 0) (.sort level)) + run.arguments' run.result' := + sourceEq ▸ run.spine + have retargeted := spine.retarget translatedLength (.sort level) + have sortClosed : (VExpr.sort level).ClosedN 0 := by trivial + have targetEq : (VExpr.sort level).instRev run.arguments' = + .sort level := VExpr.instRev_closedN run.arguments' sortClosed + rw [targetEq] at retargeted + exact Eq.mpr (congrArg (fun source => env.SpineWF Us.length + contextRun.candidate.context.vlctx.toCtx source run.arguments' + (.sort level)) sourceEq) retargeted + simp only [VExpr.lift'_consN_skipN] at indicesEq + rw [indicesEq] at liftedSpine + exact ⟨indices', indicesTr, by + apply VEnv.SpineWF.fvLift_inv henv viewDefEq extension + simpa only [VExpr.liftN] using liftedSpine⟩ + +theorem ConstructorPreFamilyIndexSpineSemanticRun.baseSpine_lift + {env : VEnv} {Us : List Name} {context : AddInductive.Context} + {contextRun : AddInductive.ConstructorContextRun env Us context} + {expected : Expr} {arguments : List Expr} + {trace : AddInductive.ConstructorPreFamilyIndexSpineTrace context expected + arguments} + {expected' : VExpr} {base view : VLCtx} {indices : List VExpr} + {level : VLevel} {lift : Lift} + (run : AddInductive.ConstructorPreFamilyIndexSpineSemanticRun env Us + context contextRun trace expected') + (familyTr : TrExprS env Us base expected + (VExpr.forallN indices (.sort level))) + (expectedUnique : TrExprS.IsUnique expected) + (viewDefEq : VLCtx.IsDefEq env Us.length + contextRun.candidate.context.vlctx view) + (viewUnique : TrExprS.IsUniqueCtx + contextRun.candidate.context.vlctx view) + (extension : VLCtx.FVLift' base view 0 lift 0) + (argumentClosed : ∀ argument ∈ arguments, Closed argument) + (argumentFVars : ∀ argument ∈ arguments, + FVarsIn (· ∈ base.fvars) argument) + (argumentUnique : ∀ argument ∈ arguments, + TrExprS.IsUnique argument) + (argumentLength : arguments.length = indices.length) : + ∃ indices', + List.Forall₂ (TrExprS env Us base) arguments indices' ∧ + env.SpineWF Us.length base.toCtx + (VExpr.forallN indices (.sort level)) indices' (.sort level) := by + have henv : VEnv.WF env := by + simpa only [contextRun.venv_eq] using + contextRun.candidate.context.Ewf + have expectedEq : expected' = + (VExpr.forallN indices (.sort level)).lift' (lift.consN 0) := + expected_eq_of_family_lift run familyTr expectedUnique extension + viewDefEq viewUnique + subst expected' + obtain ⟨indices', indicesTr, indicesEq⟩ := + TrExprS.forall₂_weakFV_inv_defeq henv viewDefEq viewUnique + extension run.arguments_tr argumentClosed argumentFVars argumentUnique + have translatedLength : run.arguments'.length = + (vexprLiftTel (lift.consN 0) indices).length := by + rw [← forall₂_length run.arguments_tr, argumentLength, + vexprLiftTel_length] + have liftedSpine : env.SpineWF Us.length + contextRun.candidate.context.vlctx.toCtx + ((VExpr.forallN indices (.sort level)).lift' (lift.consN 0)) + run.arguments' (.sort level) := by + have sourceEq : + (VExpr.forallN indices (.sort level)).lift' (lift.consN 0) = + VExpr.forallN (vexprLiftTel (lift.consN 0) indices) + (.sort level) := by + rw [vexpr_lift'_forallN] + rfl + have spine : env.SpineWF Us.length + contextRun.candidate.context.vlctx.toCtx + (VExpr.forallN (vexprLiftTel (lift.consN 0) indices) (.sort level)) + run.arguments' run.result' := + sourceEq ▸ run.spine + have retargeted := spine.retarget translatedLength (.sort level) + have sortClosed : (VExpr.sort level).ClosedN 0 := by trivial + have targetEq : (VExpr.sort level).instRev run.arguments' = + .sort level := VExpr.instRev_closedN run.arguments' sortClosed + rw [targetEq] at retargeted + exact Eq.mpr (congrArg (fun source => env.SpineWF Us.length + contextRun.candidate.context.vlctx.toCtx source run.arguments' + (.sort level)) sourceEq) retargeted + rw [indicesEq] at liftedSpine + exact ⟨indices', indicesTr, by + apply VEnv.SpineWF.fvLift'_inv henv viewDefEq extension + simpa only [VExpr.lift'] using liftedSpine⟩ + +/-- Recover the exact analyzer-selected source type and its checker-selected +sort after removing a verified free-variable context extension. -/ +theorem ensureTypeRun_baseType + {env typeEnv : VEnv} {Us : List Name} + {base actual view : VLCtx} {source : Expr} + {source' actual' : VExpr} {fieldLevel : VLevel} {n : Lift} + (henv : VEnv.WF env) (typeEnvOrdered : VEnv.Ordered typeEnv) + (addType : env ≤ typeEnv) + (_baseWF : VLCtx.WF env Us.length base) + (viewDefEq : VLCtx.IsDefEq env Us.length actual view) + (viewUnique : TrExprS.IsUniqueCtx actual view) + (viewLift : VLCtx.FVLift' base view 0 n 0) + (sourceUnique : TrExprS.IsUnique source) + (sourceClosed : Closed source) + (sourceFVars : FVarsIn (· ∈ base.fvars) source) + (sourceTr : TrExprS typeEnv Us base source source') + (actualTr : TrExprS env Us actual source actual') + (actualType : env.HasType Us.length actual.toCtx actual' (.sort fieldLevel)) : + env.HasType Us.length base.toCtx source' (.sort fieldLevel) := by + obtain ⟨base', baseTr⟩ := + actualTr.weakFV'_inv henv viewLift viewDefEq sourceClosed sourceFVars + have baseEq : base' = source' := + (baseTr.mono addType).unique sourceUnique sourceTr + subst base' + have viewWF : VLCtx.WF env Us.length view := + (viewDefEq.symm henv.ordered).wf + have sourceAtView := sourceTr + have sourceAtView' : TrExprS typeEnv Us view source + (source'.lift' (n.consN 0)) := + sourceAtView.weakFV' typeEnvOrdered viewLift (viewWF.mono addType) + have actualEq : actual' = source'.lift' (n.consN 0) := by + exact (actualTr.mono addType).unique' viewUnique sourceUnique sourceAtView' + have sourceTypeAtView : env.HasType Us.length view.toCtx + actual' (.sort fieldLevel) := + actualType.defeqDFC henv.ordered viewDefEq.defeqCtx + rw [actualEq] at sourceTypeAtView + exact (HasType.weak'_iff henv viewWF.toCtx viewLift.toCtx).1 (by + simpa using sourceTypeAtView) + +theorem ensureTypeRun_baseType_mono + {env typeEnv : VEnv} {Us : List Name} + {base actual view : VLCtx} {source : Expr} + {source' actual' : VExpr} {fieldLevel : VLevel} {n : Lift} + (typeEnvWF : VEnv.WF typeEnv) + (addType : env ≤ typeEnv) + (baseWF : VLCtx.WF env Us.length base) + (viewDefEq : VLCtx.IsDefEq env Us.length actual view) + (viewUnique : TrExprS.IsUniqueCtx actual view) + (viewLift : VLCtx.FVLift' base view 0 n 0) + (sourceUnique : TrExprS.IsUnique source) + (sourceClosed : Closed source) + (sourceFVars : FVarsIn (· ∈ base.fvars) source) + (sourceTr : TrExprS typeEnv Us base source source') + (actualTr : TrExprS env Us actual source actual') + (actualType : typeEnv.HasType Us.length actual.toCtx actual' + (.sort fieldLevel)) : + typeEnv.HasType Us.length base.toCtx source' (.sort fieldLevel) := by + exact ensureTypeRun_baseType typeEnvWF + typeEnvWF.ordered VEnv.LE.rfl + (baseWF.mono addType) (viewDefEq.mono addType) viewUnique viewLift + sourceUnique sourceClosed sourceFVars sourceTr (actualTr.mono addType) + actualType + +/-- Recover a family-free base endpoint when the analyzer translation lives +under a different prefix than D3's index-extended replay context. -/ +theorem ensureTypeRun_commonType + {env typeEnv : VEnv} {Us : List Name} + {base full actual view : VLCtx} {source : Expr} + {fullTarget actualTarget : VExpr} {fieldLevel : VLevel} + {fullLift viewLift : Lift} + (henv : VEnv.WF env) (typeEnvWF : VEnv.WF typeEnv) + (addType : env ≤ typeEnv) + (baseWF : VLCtx.WF env Us.length base) + (fullWF : VLCtx.WF typeEnv Us.length full) + (fullExtension : VLCtx.FVLift' base full 0 fullLift 0) + (viewDefEq : VLCtx.IsDefEq env Us.length actual view) + (viewUnique : TrExprS.IsUniqueCtx actual view) + (viewExtension : VLCtx.FVLift' base view 0 viewLift 0) + (sourceUnique : TrExprS.IsUnique source) + (sourceClosed : Closed source) + (sourceFVars : FVarsIn (· ∈ base.fvars) source) + (fullTr : TrExprS typeEnv Us full source fullTarget) + (actualTr : TrExprS env Us actual source actualTarget) + (actualType : env.HasType Us.length actual.toCtx actualTarget + (.sort fieldLevel)) : + ∃ baseTarget, + TrExprS typeEnv Us base source baseTarget ∧ + fullTarget = baseTarget.lift' fullLift ∧ + env.HasType Us.length base.toCtx baseTarget (.sort fieldLevel) := by + obtain ⟨baseTarget, baseTr⟩ := fullTr.weakFV'_inv typeEnvWF + fullExtension (.refl typeEnvWF.ordered fullWF) sourceClosed sourceFVars + have fullEq : fullTarget = baseTarget.lift' fullLift := + fullTr.unique sourceUnique + (baseTr.weakFV' typeEnvWF.ordered fullExtension fullWF) + have baseType := ensureTypeRun_baseType + henv typeEnvWF.ordered + addType baseWF viewDefEq viewUnique viewExtension sourceUnique + sourceClosed sourceFVars baseTr actualTr actualType + exact ⟨baseTarget, baseTr, fullEq, baseType⟩ + +/-- D2 types the exact analyzer-owned field in the synthetic full field +context and relates it to the declaration actually pushed by validation. -/ +theorem analyzerField_postType + {typeEnv : VEnv} {Us : List Name} + {full postActual : VLCtx} {source : Expr} + {analyzer postRaw postView postConsumed : VExpr} + {rawLevel : VLevel} + (typeEnvWF : VEnv.WF typeEnv) + (fullWF : VLCtx.WF typeEnv Us.length full) + (postWF : VLCtx.WF typeEnv Us.length postActual) + (relation : VLCtx.IsDefEqFVars typeEnv Us.length full postActual) + (analyzerTr : TrExprS typeEnv Us full source analyzer) + (postViewTr : TrExprS typeEnv Us postActual source postView) + (postRawType : typeEnv.HasType Us.length postActual.toCtx postRaw + (.sort rawLevel)) + (postRawView : typeEnv.IsDefEqU Us.length postActual.toCtx + postRaw postView) + (postAnnotations : typeEnv.IsDefEqU Us.length postActual.toCtx + postRaw postConsumed) : + typeEnv.HasType Us.length full.toCtx analyzer (.sort rawLevel) ∧ + typeEnv.IsDefEq Us.length full.toCtx analyzer postConsumed + (.sort rawLevel) := by + have postViewType : typeEnv.HasType Us.length postActual.toCtx postView + (.sort rawLevel) := + postRawType.defeqU_l typeEnvWF postWF.toCtx postRawView + have postViewAtFull : typeEnv.HasType Us.length full.toCtx postView + (.sort rawLevel) := + postViewType.defeqDFC typeEnvWF.ordered + (relation.defeqCtx.symm typeEnvWF.ordered) + have analyzerViewU : typeEnv.IsDefEqU Us.length full.toCtx + analyzer postView := + analyzerTr.uniqFVars typeEnvWF relation fullWF postViewTr + have analyzerView := analyzerViewU.of_r typeEnvWF fullWF.toCtx + postViewAtFull + have rawViewAtFull := postRawView.defeqDFC typeEnvWF.ordered + (relation.defeqCtx.symm typeEnvWF.ordered) + have rawConsumedAtFull := postAnnotations.defeqDFC typeEnvWF.ordered + (relation.defeqCtx.symm typeEnvWF.ordered) + have rawTypeAtFull := postRawType.defeqDFC typeEnvWF.ordered + (relation.defeqCtx.symm typeEnvWF.ordered) + have rawConsumed := rawConsumedAtFull.of_l typeEnvWF fullWF.toCtx + rawTypeAtFull + have analyzerConsumed := analyzerView.trans + ((rawViewAtFull.of_l typeEnvWF fullWF.toCtx rawTypeAtFull).symm.trans + rawConsumed) + exact ⟨analyzerView.hasType.1, analyzerConsumed⟩ + +structure AnalyzerPostContextState + (typeEnv : VEnv) (Us : List Name) + (full postActual postView : VLCtx) (viewLift : Lift) where + fullWF : VLCtx.WF typeEnv Us.length full + postWF : VLCtx.WF typeEnv Us.length postActual + viewWF : VLCtx.WF typeEnv Us.length postView + viewDefEq : VLCtx.IsDefEqFVars typeEnv Us.length postActual postView + viewExtension : VLCtx.FVLift' full postView 0 viewLift 0 + +def vlctxCons + (entry : Option (FVarId × List FVarId) × VLocalDecl) + (tail : VLCtx) : VLCtx := + entry :: tail + +theorem VLCtx.IsDefEqFVars.fvars + (relation : VLCtx.IsDefEqFVars env U left right) : + left.fvars = right.fvars := by + induction relation with + | nil => rfl + | cons_bvar relation declaration ih => + change _ = _ + exact ih + | cons_fvar relation declaration ih => + change _ :: _ = _ :: _ + exact congrArg (fun tail => _ :: tail) ih + +theorem VLCtx.IsDefEqFVars.mono + (add : env ≤ env') : + VLCtx.IsDefEqFVars env U left right → + VLCtx.IsDefEqFVars env' U left right := by + intro relation + induction relation with + | nil => exact .nil + | cons_bvar relation declaration ih => + exact .cons_bvar ih (declaration.mono add) + | cons_fvar relation declaration ih => + exact .cons_fvar ih (declaration.mono add) + +theorem VLCtx.IsDefEqFVars.symm + (henv : VEnv.Ordered env) : + VLCtx.IsDefEqFVars env U left right → + VLCtx.IsDefEqFVars env U right left := by + intro relation + induction relation with + | nil => exact .nil + | cons_bvar relation declaration ih => + exact .cons_bvar ih + (declaration.symm.defeqDFC henv relation.defeqCtx) + | cons_fvar relation declaration ih => + exact .cons_fvar ih + (declaration.symm.defeqDFC henv relation.defeqCtx) + +def FullFreshInvariant (context : AddInductive.Context) + (common full : VLCtx) : Prop := + ∀ fv ∈ full.fvars, + fv ∈ common.fvars ∨ context.ngen.Reserves fv + +theorem FullFreshInvariant.fresh + (invariant : FullFreshInvariant context common full) + (freshCommon : context.freshFVarId ∉ common.fvars) : + context.freshFVarId ∉ full.fvars := by + intro present + rcases invariant context.freshFVarId present with common | reserved + · exact freshCommon common + · exact NameGenerator.not_reserves_self reserved + +theorem FullFreshInvariant.push + (invariant : FullFreshInvariant context common full) + {commonDomain fullDomain : VLocalDecl} + {commonDeps fullDeps : List FVarId} : + FullFreshInvariant + (context.pushLocalDecl name binderInfo sourceDomain) + (vlctxCons + (some (context.freshFVarId, commonDeps), commonDomain) common) + (vlctxCons + (some (context.freshFVarId, fullDeps), fullDomain) full) := by + intro fv present + simp only [vlctxCons, VLCtx.fvars_cons_some, List.mem_cons] at present + rcases present with rfl | present + · exact .inl (by simp [vlctxCons]) + · rcases invariant fv present with commonMem | reserved + · exact .inl (by + simp only [vlctxCons, VLCtx.fvars_cons_some, List.mem_cons] + exact .inr commonMem) + · exact .inr (reserved.mono NameGenerator.LE.next) + +theorem FullFreshInvariant.skip + (invariant : FullFreshInvariant context common full) + {fullDomain : VLocalDecl} {fullDeps : List FVarId} : + FullFreshInvariant context.advanceFresh common + (vlctxCons + (some (context.freshFVarId, fullDeps), fullDomain) full) := by + intro fv present + simp only [vlctxCons, VLCtx.fvars_cons_some, List.mem_cons] at present + rcases present with rfl | present + · exact .inr NameGenerator.next_reserves_self + · rcases invariant fv present with common | reserved + · exact .inl common + · exact .inr (reserved.mono NameGenerator.LE.next) + +structure D3FullContextState + (env typeEnv : VEnv) (Us : List Name) + (context : AddInductive.Context) + (common full actual view : VLCtx) (fullLift viewLift : Lift) where + commonWF : VLCtx.WF env Us.length common + commonNoBV : common.NoBV + fullWF : VLCtx.WF typeEnv Us.length full + fullExtension : VLCtx.FVLift' common full 0 fullLift 0 + actualWF : VLCtx.WF env Us.length actual + viewDefEq : VLCtx.IsDefEq env Us.length actual view + viewUnique : TrExprS.IsUniqueCtx actual view + viewExtension : VLCtx.FVLift' common view 0 viewLift 0 + freshInvariant : FullFreshInvariant context common full + +/-- Push one family-free D3 binder through both the analyzer's full context +and D3's index-extended context. -/ +theorem D3FullContextState.push + {env typeEnv : VEnv} {Us : List Name} + {context : AddInductive.Context} + {common full actual view : VLCtx} {fullLift viewLift : Lift} + (state : D3FullContextState env typeEnv Us context + common full actual view fullLift viewLift) + (henv : VEnv.WF env) (typeEnvWF : VEnv.WF typeEnv) + (addType : env ≤ typeEnv) + {source : Expr} + {fullTarget actualSource actualConsumed : VExpr} + {fieldLevel consumedLevel : VLevel} + (sourceUnique : TrExprS.IsUnique source) + (sourceClosed : Closed source) + (sourceFVars : FVarsIn (· ∈ common.fvars) source) + (fullTr : TrExprS typeEnv Us full source fullTarget) + (actualTr : TrExprS env Us actual source actualSource) + (actualType : env.HasType Us.length actual.toCtx actualSource + (.sort fieldLevel)) + (actualAnnotations : env.IsDefEqU Us.length actual.toCtx + actualSource actualConsumed) + (actualConsumedType : env.HasType Us.length actual.toCtx actualConsumed + (.sort consumedLevel)) + (actualTailWF : VLCtx.WF env Us.length + (vlctxCons + (some (context.freshFVarId, + (consumeTypeAnnotations source).fvarsList), + .vlam actualConsumed) actual)) : + ∃ commonTarget, + TrExprS typeEnv Us common source commonTarget ∧ + fullTarget = commonTarget.lift' fullLift ∧ + env.HasType Us.length common.toCtx commonTarget (.sort fieldLevel) ∧ + D3FullContextState env typeEnv Us + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations source)) + (vlctxCons + (some (context.freshFVarId, + (consumeTypeAnnotations source).fvarsList), + .vlam commonTarget) common) + (vlctxCons + (some (context.freshFVarId, + (consumeTypeAnnotations source).fvarsList), + .vlam fullTarget) full) + (vlctxCons + (some (context.freshFVarId, + (consumeTypeAnnotations source).fvarsList), + .vlam actualConsumed) actual) + (vlctxCons + (some (context.freshFVarId, + (consumeTypeAnnotations source).fvarsList), + .vlam (commonTarget.lift' viewLift)) view) + (.consN fullLift 1) (.consN viewLift 1) := by + obtain ⟨commonTarget, commonTr, fullEq, commonType⟩ := + ensureTypeRun_commonType henv typeEnvWF addType state.commonWF + state.fullWF state.fullExtension state.viewDefEq state.viewUnique + state.viewExtension sourceUnique sourceClosed sourceFVars fullTr + actualTr actualType + have viewWF : VLCtx.WF env Us.length view := + (state.viewDefEq.symm henv.ordered).wf + have commonAtView : TrExprS typeEnv Us view source + (commonTarget.lift' viewLift) := + commonTr.weakFV' typeEnvWF.ordered state.viewExtension + (viewWF.mono addType) + have actualSource_eq : actualSource = commonTarget.lift' viewLift := + (actualTr.mono addType).unique' state.viewUnique sourceUnique commonAtView + have consumedToCommon : env.IsDefEq Us.length actual.toCtx + actualConsumed (commonTarget.lift' viewLift) + (.sort consumedLevel) := by + rw [← actualSource_eq] + exact (actualAnnotations.of_r henv state.actualWF.toCtx + actualConsumedType).symm + have depsSubset : (consumeTypeAnnotations source).fvarsList ⊆ + common.fvars := + (FVarsIn.consumeTypeAnnotations sourceFVars |> fvarsIn_iff.mp).1 + have freshCommon : context.freshFVarId ∉ common.fvars := by + intro present + have presentView := state.viewExtension.fvars_sublist.subset present + have presentActual : context.freshFVarId ∈ actual.fvars := by + simpa only [state.viewDefEq.fvars] using presentView + exact (actualTailWF.2.1 _ _ rfl).1 presentActual + have freshFull := state.freshInvariant.fresh freshCommon + have commonTailWF : VLCtx.WF env Us.length + (vlctxCons + (some (context.freshFVarId, + (consumeTypeAnnotations source).fvarsList), + .vlam commonTarget) common) := by + refine ⟨state.commonWF, ?_, ⟨fieldLevel, commonType⟩⟩ + intro fv deps equality + cases equality + exact ⟨freshCommon, depsSubset⟩ + have fullType : typeEnv.HasType Us.length full.toCtx fullTarget + (.sort fieldLevel) := by + rw [fullEq] + exact (commonType.weak' henv.ordered state.fullExtension.toCtx).mono addType + have fullTailWF : VLCtx.WF typeEnv Us.length + (vlctxCons + (some (context.freshFVarId, + (consumeTypeAnnotations source).fvarsList), + .vlam fullTarget) full) := by + refine ⟨state.fullWF, ?_, ⟨fieldLevel, fullType⟩⟩ + intro fv deps equality + cases equality + exact ⟨freshFull, fun fv member => + state.fullExtension.fvars_sublist.subset (depsSubset member)⟩ + have nextViewDefEq : VLCtx.IsDefEq env Us.length + (vlctxCons + (some (context.freshFVarId, + (consumeTypeAnnotations source).fvarsList), + .vlam actualConsumed) actual) + (vlctxCons + (some (context.freshFVarId, + (consumeTypeAnnotations source).fvarsList), + .vlam (commonTarget.lift' viewLift)) view) := by + exact .cons state.viewDefEq actualTailWF.2.1 + (.vlam consumedToCommon) + refine ⟨commonTarget, commonTr, fullEq, commonType, { + commonWF := commonTailWF + commonNoBV := by + simpa only [vlctxCons, VLCtx.NoBV, VLCtx.bvars] using + state.commonNoBV + fullWF := fullTailWF + fullExtension := ?_ + actualWF := actualTailWF + viewDefEq := nextViewDefEq + viewUnique := state.viewUnique.cons .vlam + viewExtension := state.viewExtension.cons_fvar + (context.freshFVarId, + (consumeTypeAnnotations source).fvarsList) + (.vlam commonTarget) depsSubset + freshInvariant := state.freshInvariant.push }⟩ + simpa only [fullEq, vlctxCons, VLocalDecl.lift', + VLocalDecl.depth] using state.fullExtension.cons_fvar + (context.freshFVarId, + (consumeTypeAnnotations source).fvarsList) + (.vlam commonTarget) depsSubset + +/-- Skip one family-dependent outer field in D3's family-free replay while +retaining that exact analyzer field in the full context. -/ +theorem D3FullContextState.skip + {env typeEnv : VEnv} {Us : List Name} + {context : AddInductive.Context} + {common full actual view : VLCtx} {fullLift viewLift : Lift} + (state : D3FullContextState env typeEnv Us context + common full actual view fullLift viewLift) + {source : Expr} {field : VExpr} + (nextFullWF : VLCtx.WF typeEnv Us.length + ((some (context.freshFVarId, + (consumeTypeAnnotations source).fvarsList), + .vlam field) :: full)) : + D3FullContextState env typeEnv Us context.advanceFresh common + ((some (context.freshFVarId, + (consumeTypeAnnotations source).fvarsList), + .vlam field) :: full) + actual view (.skipN fullLift 1) viewLift where + commonWF := state.commonWF + commonNoBV := state.commonNoBV + fullWF := nextFullWF + fullExtension := by + simpa only [VLocalDecl.depth] using state.fullExtension.skip_fvar + (context.freshFVarId, + (consumeTypeAnnotations source).fvarsList) (.vlam field) + actualWF := state.actualWF + viewDefEq := state.viewDefEq + viewUnique := state.viewUnique + viewExtension := state.viewExtension + freshInvariant := state.freshInvariant.skip + +/-- Exact analyzer syntax and pre-family semantic evidence for one recursive +field. The family telescope is tracked at the analyzer's current full +context; nested binders lift it in the ordinary de Bruijn way. -/ +def RecursiveFieldRunResult + (env : VEnv) (Us : List Name) (full : VLCtx) + (familyTarget fieldTarget : VExpr) (level : VLevel) + (familyName : Name) (parameterCount : Nat) : Prop := + ∃ binders indices terminal, + fieldTarget = VExpr.forallN binders terminal ∧ + env.OnTel Us.length full.toCtx binders ∧ + env.SpineWF Us.length + (binders.reverse ++ full.toCtx) + (familyTarget.liftN binders.length 0) indices (.sort level) ∧ + (∃ levels, VExpr.appHead terminal = .const familyName levels) ∧ + (VExpr.appArgs terminal []).drop parameterCount = indices + +/-- Synchronize the recursive D3 replay with the analyzer's exact strict +translation. D3 supplies all family-free typing and the terminal index +spine; strict translation uniqueness fixes the analyzer-owned domains and +terminal arguments componentwise. -/ +theorem recursiveField_exactAnalyzer + {env typeEnv : VEnv} {Us : List Name} + {stats : AddInductive.InductiveStats} {familyIdx : Nat} + {familyIndices : Expr} {context : AddInductive.Context} + {contextRun : AddInductive.ConstructorContextRun env Us context} + {source : Expr} {fuel : Nat} + {trace : AddInductive.ConstructorPreFamilyRecursiveTrace stats familyIdx + familyIndices context source fuel} + (run : AddInductive.ConstructorPreFamilyRecursiveSemanticRun env Us stats + familyIdx familyIndices contextRun trace) + (henv : VEnv.WF env) (typeEnvWF : VEnv.WF typeEnv) + (addType : env ≤ typeEnv) + {common full view : VLCtx} {fullLift viewLift : Lift} + (state : D3FullContextState env typeEnv Us context common full + contextRun.candidate.context.vlctx view fullLift viewLift) + {commonIndices : List VExpr} {familyTarget fieldTarget : VExpr} + {level : VLevel} {familyName : Name} {familyLevels : List Level} + (familyCommonTr : TrExprS env Us common familyIndices + (VExpr.forallN commonIndices (.sort level))) + (familyFullTr : TrExprS typeEnv Us full familyIndices familyTarget) + (familyUnique : TrExprS.IsUnique familyIndices) + (indexLength : commonIndices.length = stats.nindices[familyIdx]!) + (familyHead : stats.indConsts[familyIdx]! = + .const familyName familyLevels) + (sourceUnique : TrExprS.IsUnique source) + (sourceClosed : Closed source) + (sourceFVars : FVarsIn (· ∈ common.fvars) source) + (fullTr : TrExprS typeEnv Us full source fieldTarget) : + RecursiveFieldRunResult env Us full familyTarget fieldTarget level + familyName stats.params.size := by + induction run generalizing common full view fullLift viewLift commonIndices + familyTarget fieldTarget with + | @target _ context source fuel valid spineTrace branchContextRun expected' + spine => + have argumentClosed : ∀ argument ∈ + source.getAppArgs.toList.drop stats.params.size, + Closed argument := by + intro argument member + apply Closed.getAppArgsList sourceClosed + rw [← Expr.getAppArgs_toList] + exact List.mem_of_mem_drop member + have argumentFVars : ∀ argument ∈ + source.getAppArgs.toList.drop stats.params.size, + FVarsIn (· ∈ common.fvars) argument := by + intro argument member + apply FVarsIn.getAppArgsList sourceFVars + rw [← Expr.getAppArgs_toList] + exact List.mem_of_mem_drop member + have argumentUnique : ∀ argument ∈ + source.getAppArgs.toList.drop stats.params.size, + TrExprS.IsUnique argument := by + intro argument member + apply TrExprS.IsUnique.getAppArgsList sourceUnique + rw [← Expr.getAppArgs_toList] + exact List.mem_of_mem_drop member + have argumentLength : + (source.getAppArgs.toList.drop stats.params.size).length = + commonIndices.length := + (isValidIndAppIdx_indexArgs_length valid).trans + indexLength.symm + obtain ⟨baseIndices, baseIndicesTr, baseSpine⟩ := + ConstructorPreFamilyIndexSpineSemanticRun.baseSpine_lift spine + familyCommonTr familyUnique + state.viewDefEq state.viewUnique state.viewExtension + argumentClosed argumentFVars argumentUnique argumentLength + have commonFamilyAtFull : TrExprS typeEnv Us full familyIndices + ((VExpr.forallN commonIndices (.sort level)).lift' fullLift) := by + simpa using (familyCommonTr.mono addType).weakFV' + typeEnvWF.ordered state.fullExtension state.fullWF + have familyEq : familyTarget = + (VExpr.forallN commonIndices (.sort level)).lift' fullLift := + familyFullTr.unique familyUnique commonFamilyAtFull + have shape := isValidIndAppIdx_shape valid + have sourceHead : source.getAppFn = + .const familyName familyLevels := by + rw [familyHead] at shape + change source.getAppFn.eqv (.const familyName familyLevels) = true ∧ + _ at shape + rw [Expr.eqv_eq] at shape + generalize headEq : source.getAppFn = head at shape + cases head <;> simp_all [Expr.eqv'] + obtain ⟨targetLevels, targetHead, allArgumentsTr⟩ := + TrExprS.constApp_components fullTr sourceHead + have droppedArgumentsTr := + forall₂_drop allArgumentsTr stats.params.size + have baseIndicesAtFull : List.Forall₂ + (TrExprS typeEnv Us full) + (source.getAppArgsList.drop stats.params.size) + (baseIndices.map fun index => index.lift' fullLift) := by + have baseMono := forall₂_tr_mono addType baseIndicesTr + have baseWeak := forall₂_tr_weakFV' typeEnvWF.ordered + state.fullExtension state.fullWF baseMono + simpa only [Expr.getAppArgs_toList] using baseWeak + have translatedIndicesEq : + (VExpr.appArgs fieldTarget []).drop stats.params.size = + baseIndices.map fun index => index.lift' fullLift := + forall₂_translation_unique droppedArgumentsTr + baseIndicesAtFull (fun argument member => + TrExprS.IsUnique.getAppArgsList sourceUnique argument + (List.mem_of_mem_drop member)) + have fullSpine : env.SpineWF Us.length full.toCtx + ((VExpr.forallN commonIndices (.sort level)).lift' fullLift) + (baseIndices.map fun index => index.lift' fullLift) + (.sort level) := by + simpa using VEnv.SpineWF.weak' henv.ordered + state.fullExtension.toCtx baseSpine + rw [← familyEq, ← translatedIndicesEq] at fullSpine + exact ⟨[], (VExpr.appArgs fieldTarget []).drop stats.params.size, + fieldTarget, rfl, trivial, by simpa using fullSpine, + ⟨targetLevels, targetHead⟩, rfl⟩ + | @forallE context name domain body binderInfo fuel domainCheck ensureType + consumedCheck annotations fresh tailTrace branchContextRun domainRun + consumedRun ensureTypeRun annotationsRun consumedType tail ih => + obtain ⟨fullDomain, fullBody, rfl, fullDomainType, fullBodyType, + domainTr, bodyTr⟩ := TrExprS.forallE_components fullTr + have actualDomainTr : TrExprS env Us + branchContextRun.candidate.context.vlctx domain + domainRun.source' := by + simpa only [branchContextRun.venv_eq, + branchContextRun.lparams_eq] using domainRun.check.expr_tr + have actualDomainType : env.HasType Us.length + branchContextRun.candidate.context.vlctx.toCtx domainRun.source' + (.sort ensureTypeRun.resultLevel') := by + simpa only [branchContextRun.venv_eq, + branchContextRun.lparams_eq] using ensureTypeRun.source_type + have actualAnnotations : env.IsDefEqU Us.length + branchContextRun.candidate.context.vlctx.toCtx domainRun.source' + consumedRun.source' := by + simpa only [branchContextRun.venv_eq, + branchContextRun.lparams_eq] using annotationsRun.isDefEqU + have consumedTypeCopy := consumedType + obtain ⟨consumedLevel, consumedHasType⟩ := consumedType + have actualConsumedType : env.HasType Us.length + branchContextRun.candidate.context.vlctx.toCtx consumedRun.source' + (.sort consumedLevel) := by + simpa only [branchContextRun.venv_eq, + branchContextRun.lparams_eq] using consumedHasType + let nextContextRun := branchContextRun.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain) fresh consumedRun.source' + consumedRun.check.expr_tr consumedTypeCopy + have actualTailWF : VLCtx.WF env Us.length + (vlctxCons + (some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam consumedRun.source') + branchContextRun.candidate.context.vlctx) := by + have nextWF := nextContextRun.candidate.context.Δwf + rw [nextContextRun.venv_eq, nextContextRun.lparams_eq] at nextWF + simpa only [vlctxCons, nextContextRun, + AddInductive.ConstructorContextRun.pushLocalDecl, + CandidateContextRun.pushLocalDecl_vlctx] using nextWF + obtain ⟨commonDomain, commonDomainTr, fullDomainEq, + commonDomainType, nextState⟩ := + state.push henv typeEnvWF addType sourceUnique.1 sourceClosed.1 + sourceFVars.1 domainTr actualDomainTr actualDomainType + actualAnnotations actualConsumedType actualTailWF + have bodyOpened : TrExprS typeEnv Us + (vlctxCons + (some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam fullDomain) full) + (body.instantiate1 context.freshExpr) fullBody := by + simpa only [vlctxCons, AddInductive.Context.freshExpr, + Expr.instantiate1_eq] using + bodyTr.inst_fvar typeEnvWF.ordered nextState.fullWF + have familyCommonNext : TrExprS env Us + (vlctxCons + (some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam commonDomain) common) + familyIndices + (VExpr.forallN (VExpr.liftTelN 1 commonIndices 0) + (.sort level)) := by + have weakened := familyCommonTr.weakFV henv.ordered + (VLCtx.FVLift.skip_fvar + (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList) + (.vlam commonDomain) .refl) + nextState.commonWF + simpa [vlctxCons, VLocalDecl.depth, + VExpr.liftN_forallN, VExpr.liftN] using weakened + have familyFullNext : TrExprS typeEnv Us + (vlctxCons + (some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam fullDomain) full) + familyIndices (familyTarget.liftN 1 0) := by + have weakened := familyFullTr.weakFV typeEnvWF.ordered + (VLCtx.FVLift.skip_fvar + (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList) + (.vlam fullDomain) .refl) + nextState.fullWF + simpa [vlctxCons, VLocalDecl.depth] using weakened + have nextIndexLength : + (VExpr.liftTelN 1 commonIndices 0).length = + stats.nindices[familyIdx]! := by + simpa only [VExpr.liftTelN_length] using indexLength + have tailUnique : TrExprS.IsUnique + (body.instantiate1 context.freshExpr) := by + apply TrExprS.IsUnique.instantiate1 sourceUnique.2 + simp only [AddInductive.Context.freshExpr] + trivial + have nextFullNoBV : + (vlctxCons + (some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam fullDomain) full).bvars = 0 := + nextState.fullExtension.bvars_eq.trans nextState.commonNoBV + have tailClosed : Closed (body.instantiate1 context.freshExpr) := by + have closed := bodyOpened.closed + rw [nextFullNoBV] at closed + exact closed + have tailFVars : FVarsIn + (· ∈ (vlctxCons + (some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam commonDomain) common).fvars) + (body.instantiate1 context.freshExpr) := by + rw [Expr.instantiate1_eq] + apply FVarsIn.instantiate1 + · exact sourceFVars.2.mono (fun fv member => by + simp only [vlctxCons, VLCtx.fvars_cons_some, + List.mem_cons] + exact .inr member) + · simp [AddInductive.Context.freshExpr, vlctxCons, FVarsIn] + obtain ⟨tailBinders, indices, terminal, tailTargetEq, tailOnTel, + tailSpine, terminalHead, terminalIndices⟩ := + ih nextState familyCommonNext familyFullNext nextIndexLength + tailUnique tailClosed tailFVars bodyOpened + refine ⟨fullDomain :: tailBinders, indices, terminal, ?_, ?_, ?_, + terminalHead, terminalIndices⟩ + · simp only [VExpr.forallN, VExpr.forallE.injEq, true_and] + exact tailTargetEq + · exact ⟨by + rw [fullDomainEq] + exact ⟨ensureTypeRun.resultLevel', by + simpa using commonDomainType.weak' henv.ordered + state.fullExtension.toCtx⟩, + by + simpa only [vlctxCons, VLCtx.toCtx] using tailOnTel⟩ + · simpa only [List.reverse_cons, List.singleton_append, + List.append_assoc, vlctxCons, VLCtx.toCtx, + List.length_cons, VExpr.liftN_liftN, Nat.add_comm] using tailSpine + +private theorem forallN_inj_of_terminal_ne_forall + (leftNot : ∀ domain body, leftTerminal ≠ .forallE domain body) + (rightNot : ∀ domain body, rightTerminal ≠ .forallE domain body) + (equality : VExpr.forallN leftBinders leftTerminal = + VExpr.forallN rightBinders rightTerminal) : + leftBinders = rightBinders ∧ leftTerminal = rightTerminal := by + induction leftBinders generalizing rightBinders with + | nil => + cases rightBinders with + | nil => exact ⟨rfl, equality⟩ + | cons domain binders => + exact (leftNot domain (VExpr.forallN binders rightTerminal) + equality).elim + | cons domain binders ih => + cases rightBinders with + | nil => + exact (rightNot domain (VExpr.forallN binders leftTerminal) + equality.symm).elim + | cons rightDomain rightBinders => + simp only [VExpr.forallN, VExpr.forallE.injEq] at equality + obtain ⟨domainEq, tailEq⟩ := equality + obtain ⟨bindersEq, terminalEq⟩ := + ih tailEq + cases domainEq + cases bindersEq + exact ⟨rfl, terminalEq⟩ + +private theorem terminal_ne_forall_of_appHead_const + (head : VExpr.appHead terminal = .const familyName levels) : + ∀ domain body, terminal ≠ .forallE domain body := by + intro domain body equality + subst terminal + simp only [VExpr.appHead] at head + exact VExpr.noConfusion head + +private theorem hasConst_of_appHead_const + (head : VExpr.appHead terminal = .const familyName levels) : + terminal.hasConst familyName = true := by + induction terminal with + | app function argument functionIH argumentIH => + simp only [VExpr.appHead] at head + simp only [VExpr.hasConst, Bool.or_eq_true] + exact .inl (functionIH head) + | const name levels => + simp only [VExpr.appHead, VExpr.const.injEq] at head + exact (beq_iff_eq).2 head.1 + | bvar | sort | lam | forallE => simp [VExpr.appHead] at head + +private theorem forallN_hasConst_of_terminal + (terminalHasConst : terminal.hasConst familyName = true) : + (VExpr.forallN binders terminal).hasConst familyName = true := by + induction binders with + | nil => exact terminalHasConst + | cons binder binders ih => + simp only [VExpr.forallN, VExpr.hasConst, Bool.or_eq_true] + exact .inr ih + +theorem recArg?_eq_none_of_hasConst_false + (free : field.hasConst familyName = false) : + VInductDecl.recArg? U familyName np ni fieldIndex field = none := by + cases recursiveEq : VInductDecl.recArg? U familyName np ni fieldIndex field with + | none => rfl + | some recursive => + have anatomy := VInductDecl.recArg?_eq recursiveEq + have terminalHead : + VExpr.appHead + (VExpr.appN (.const familyName (VLevel.params U)) + (VExpr.bvarRevRange + (fieldIndex + recursive.binders.length) np ++ + recursive.indices)) = + .const familyName (VLevel.params U) := + VExpr.appHead_appN _ _ + have terminalHasConst := + hasConst_of_appHead_const terminalHead + have recursiveHasConst := + forallN_hasConst_of_terminal + (binders := recursive.binders) terminalHasConst + rw [← anatomy.2.2.1, free] at recursiveHasConst + contradiction + +private theorem drop_bvarRevRange_append + (offset count : Nat) (suffix : List VExpr) : + (VExpr.bvarRevRange offset count ++ suffix).drop count = suffix := by + induction count with + | zero => rfl + | succ count ih => + simp only [VExpr.bvarRevRange, List.cons_append, List.drop_succ_cons] + exact ih + +/-- A recursive-field synchronization certificate is exactly the semantic +payload required by the analyzer's `RecArg` descriptor. -/ +theorem recursiveFieldResult_recArgWF + {env : VEnv} {Us : List Name} {full : VLCtx} + {familyTarget fieldTarget : VExpr} {level : VLevel} + {familyName : Name} {np ni fieldIndex : Nat} + {familyIndices : List VExpr} + (result : RecursiveFieldRunResult env Us full familyTarget + fieldTarget level familyName np) + (familyTargetEq : familyTarget = + VExpr.forallN (VExpr.liftTelN fieldIndex familyIndices 0) + (.sort level)) + (stage : VInductDecl.stage3Field Us.length familyName np ni fieldIndex + fieldTarget = true) : + ∃ recursive, + VInductDecl.recArg? Us.length familyName np ni fieldIndex fieldTarget = + some recursive ∧ + recursive.WF Us.length env level familyIndices full.toCtx := by + obtain ⟨binders, indices, terminal, targetEq, onTel, spine, + ⟨terminalLevels, terminalHead⟩, terminalIndices⟩ := result + have terminalHasConst : terminal.hasConst familyName = true := + hasConst_of_appHead_const terminalHead + have fieldHasConst : fieldTarget.hasConst familyName = true := by + rw [targetEq] + exact forallN_hasConst_of_terminal terminalHasConst + have recursiveSome : + (VInductDecl.recArg? Us.length familyName np ni fieldIndex + fieldTarget).isSome = true := by + simpa only [VInductDecl.stage3Field, fieldHasConst, Bool.not_true, + Bool.or_false] using stage + cases recursiveEq : VInductDecl.recArg? Us.length familyName np ni + fieldIndex fieldTarget with + | none => simp [recursiveEq] at recursiveSome + | some recursive => + have anatomy := VInductDecl.recArg?_eq recursiveEq + let recursiveTerminal := VExpr.appN + (.const familyName (VLevel.params Us.length)) + (VExpr.bvarRevRange (fieldIndex + recursive.binders.length) np ++ + recursive.indices) + have telescopesEq : VExpr.forallN binders terminal = + VExpr.forallN recursive.binders recursiveTerminal := by + exact targetEq.symm.trans anatomy.2.2.1 + have recursiveTerminalHead : + VExpr.appHead recursiveTerminal = + .const familyName (VLevel.params Us.length) := by + exact VExpr.appHead_appN _ _ + obtain ⟨bindersEq, terminalEq⟩ := + forallN_inj_of_terminal_ne_forall + (terminal_ne_forall_of_appHead_const terminalHead) + (terminal_ne_forall_of_appHead_const recursiveTerminalHead) + telescopesEq + have indicesEq : indices = recursive.indices := by + rw [terminalEq, VExpr.appArgs_appN] at terminalIndices + simp only [VExpr.appArgs, List.append_nil] at terminalIndices + rw [drop_bvarRevRange_append] at terminalIndices + exact terminalIndices.symm + refine ⟨recursive, rfl, ?_⟩ + unfold VInductDecl.RecArg.WF + refine ⟨?_, ?_⟩ + · rw [← bindersEq] + exact onTel + · rw [anatomy.1, ← bindersEq, ← indicesEq] + rw [familyTargetEq, VExpr.liftN_forallN, + VExpr.liftTelN_liftTelN] at spine + simpa [VExpr.liftN] using spine + +theorem AnalyzerPostContextState.push + {typeEnv : VEnv} {Us : List Name} + {full postActual postViewContext : VLCtx} {viewLift : Lift} + (state : AnalyzerPostContextState typeEnv Us full postActual + postViewContext viewLift) + (typeEnvWF : VEnv.WF typeEnv) + {source : Expr} {analyzer postRaw postView postConsumed : VExpr} + {rawLevel : VLevel} {fv : FVarId} {postDeps : List FVarId} + (_sourceUnique : TrExprS.IsUnique source) + (_sourceClosed : Closed source) + (analyzerTr : TrExprS typeEnv Us full source analyzer) + (postViewTr : TrExprS typeEnv Us postActual source postView) + (postRawType : typeEnv.HasType Us.length postActual.toCtx postRaw + (.sort rawLevel)) + (postRawView : typeEnv.IsDefEqU Us.length postActual.toCtx + postRaw postView) + (postAnnotations : typeEnv.IsDefEqU Us.length postActual.toCtx + postRaw postConsumed) + (postTailWF : VLCtx.WF typeEnv Us.length + ((some (fv, postDeps), .vlam postConsumed) :: postActual)) : + let deps := (AddInductive.consumeTypeAnnotations source).fvarsList + typeEnv.HasType Us.length full.toCtx analyzer (.sort rawLevel) ∧ + AnalyzerPostContextState typeEnv Us + ((some (fv, deps), .vlam analyzer) :: full) + ((some (fv, postDeps), .vlam postConsumed) :: postActual) + ((some (fv, deps), .vlam (analyzer.lift' viewLift)) :: + postViewContext) + (.consN viewLift 1) := by + dsimp only + have postViewType : typeEnv.HasType Us.length postActual.toCtx postView + (.sort rawLevel) := + postRawType.defeqU_l typeEnvWF state.postWF.toCtx postRawView + have postViewContextWF : VLCtx.WF typeEnv Us.length postViewContext := + state.viewWF + have analyzerAtView : TrExprS typeEnv Us postViewContext source + (analyzer.lift' viewLift) := + analyzerTr.weakFV' typeEnvWF.ordered state.viewExtension + postViewContextWF + have postViewAnalyzer : typeEnv.IsDefEqU Us.length postActual.toCtx + postView (analyzer.lift' viewLift) := + postViewTr.uniqFVars typeEnvWF state.viewDefEq state.postWF + analyzerAtView + have analyzerAtPostType : typeEnv.HasType Us.length postActual.toCtx + (analyzer.lift' viewLift) (.sort rawLevel) := + (postViewAnalyzer.of_l typeEnvWF state.postWF.toCtx postViewType).hasType.2 + have analyzerAtViewType : typeEnv.HasType Us.length postViewContext.toCtx + (analyzer.lift' viewLift) (.sort rawLevel) := + analyzerAtPostType.defeqDFC typeEnvWF.ordered + state.viewDefEq.defeqCtx + have analyzerType : typeEnv.HasType Us.length full.toCtx analyzer + (.sort rawLevel) := + (HasType.weak'_iff typeEnvWF postViewContextWF.toCtx + state.viewExtension.toCtx).1 (by simpa using analyzerAtViewType) + have rawView := postRawView.of_l typeEnvWF state.postWF.toCtx postRawType + have rawConsumed := + postAnnotations.of_l typeEnvWF state.postWF.toCtx postRawType + have analyzerConsumed : typeEnv.IsDefEq Us.length postActual.toCtx + postConsumed (analyzer.lift' viewLift) (.sort rawLevel) := + (rawConsumed.symm.trans rawView).trans + (postViewAnalyzer.of_l typeEnvWF state.postWF.toCtx postViewType) + have depsSubset : (AddInductive.consumeTypeAnnotations source).fvarsList ⊆ + full.fvars := + (FVarsIn.consumeTypeAnnotations analyzerTr.fvarsIn + |> fvarsIn_iff.mp).1 + have freshFull : fv ∉ full.fvars := by + intro present + have presentView := state.viewExtension.fvars_sublist.subset present + have presentPost : fv ∈ postActual.fvars := by + simpa only [ConstructorValidation.VLCtx.IsDefEqFVars.fvars state.viewDefEq] + using presentView + exact (postTailWF.2.1 fv postDeps rfl).1 presentPost + have freshView : fv ∉ postViewContext.fvars := by + intro present + have presentPost : fv ∈ postActual.fvars := by + simpa only [ConstructorValidation.VLCtx.IsDefEqFVars.fvars state.viewDefEq] + using present + exact (postTailWF.2.1 fv postDeps rfl).1 presentPost + have depsSubsetView : + (AddInductive.consumeTypeAnnotations source).fvarsList ⊆ + postViewContext.fvars := fun _ member => + state.viewExtension.fvars_sublist.subset (depsSubset member) + have fullTailWF : VLCtx.WF typeEnv Us.length + ((some (fv, (AddInductive.consumeTypeAnnotations source).fvarsList), + .vlam analyzer) :: full) := + ⟨state.fullWF, + fun _ _ equality => by + cases equality + exact ⟨freshFull, depsSubset⟩, + ⟨rawLevel, analyzerType⟩⟩ + have viewTailWF : VLCtx.WF typeEnv Us.length + ((some (fv, (AddInductive.consumeTypeAnnotations source).fvarsList), + .vlam (analyzer.lift' viewLift)) :: postViewContext) := + ⟨state.viewWF, + fun _ _ equality => by + cases equality + exact ⟨freshView, depsSubsetView⟩, + ⟨rawLevel, analyzerAtViewType⟩⟩ + exact ⟨analyzerType, { + fullWF := fullTailWF + postWF := postTailWF + viewWF := viewTailWF + viewDefEq := .cons_fvar state.viewDefEq (.vlam analyzerConsumed) + viewExtension := state.viewExtension.cons_fvar + (fv, (AddInductive.consumeTypeAnnotations source).fvarsList) + (.vlam analyzer) depsSubset }⟩ + +theorem ordinaryField_baseTypes + {env typeEnv : VEnv} {Us : List Name} + {base actual view postActual : VLCtx} {source : Expr} + {source' actual' postRaw' postView' : VExpr} + {fieldLevel rawLevel resultLevel : VLevel} {n : Lift} + (henv : VEnv.WF env) (typeEnvWF : VEnv.WF typeEnv) + (addType : env ≤ typeEnv) + (baseWF : VLCtx.WF env Us.length base) + (actualWF : VLCtx.WF env Us.length actual) + (postWF : VLCtx.WF typeEnv Us.length postActual) + (postRelation : VLCtx.IsDefEqFVars typeEnv Us.length actual postActual) + (viewDefEq : VLCtx.IsDefEq env Us.length actual view) + (viewUnique : TrExprS.IsUniqueCtx actual view) + (viewLift : VLCtx.FVLift' base view 0 n 0) + (sourceUnique : TrExprS.IsUnique source) + (sourceClosed : Closed source) + (sourceFVars : FVarsIn (· ∈ base.fvars) source) + (sourceTr : TrExprS typeEnv Us base source source') + (actualTr : TrExprS env Us actual source actual') + (actualType : env.HasType Us.length actual.toCtx actual' + (.sort fieldLevel)) + (postViewTr : TrExprS typeEnv Us postActual source postView') + (postRawType : typeEnv.HasType Us.length postActual.toCtx postRaw' + (.sort rawLevel)) + (postRawView : typeEnv.IsDefEqU Us.length postActual.toCtx + postRaw' postView') + (rawBound : resultLevel = .zero ∨ rawLevel ≤ resultLevel) : + env.HasType Us.length base.toCtx source' (.sort fieldLevel) ∧ + fieldLevel ≈ rawLevel ∧ + (resultLevel = .zero ∨ fieldLevel ≤ resultLevel) := by + have baseField := ensureTypeRun_baseType henv + typeEnvWF.ordered addType baseWF viewDefEq viewUnique viewLift + sourceUnique sourceClosed sourceFVars sourceTr actualTr actualType + have postViewType : typeEnv.HasType Us.length postActual.toCtx postView' + (.sort rawLevel) := + postRawType.defeqU_l typeEnvWF postWF.toCtx postRawView + have postViewTypeAtActual : typeEnv.HasType Us.length actual.toCtx + postView' (.sort rawLevel) := + postViewType.defeqDFC typeEnvWF.ordered + (postRelation.defeqCtx.symm typeEnvWF.ordered) + have translatedDefEq : typeEnv.IsDefEqU Us.length actual.toCtx + actual' postView' := + (actualTr.mono addType).uniqFVars typeEnvWF postRelation + (actualWF.mono addType) postViewTr + have actualRawType : typeEnv.HasType Us.length actual.toCtx actual' + (.sort rawLevel) := + (translatedDefEq.of_r typeEnvWF (actualWF.mono addType).toCtx + postViewTypeAtActual).hasType.1 + have baseRaw := ensureTypeRun_baseType_mono typeEnvWF addType baseWF + viewDefEq viewUnique viewLift sourceUnique sourceClosed sourceFVars + sourceTr actualTr actualRawType + have levelEq : fieldLevel ≈ rawLevel := + (baseField.mono addType).uniqU typeEnvWF (baseWF.mono addType).toCtx + baseRaw |>.sort_inv typeEnvWF (baseWF.mono addType).toCtx + refine ⟨baseField, levelEq, ?_⟩ + rcases rawBound with prop | bound + · exact .inl prop + · exact .inr (VLevel.le_trans + (VLevel.le_antisymm_iff.mp levelEq).1 bound) + +theorem ordinaryConsumed_defeq + {env typeEnv : VEnv} {Us : List Name} + {actual postActual : VLCtx} {source _rawSource _rawConsumed _sourceConsumed : Expr} + {actualSource' actualConsumed' postRaw' postView' postConsumed' : VExpr} + (typeEnvWF : VEnv.WF typeEnv) (addType : env ≤ typeEnv) + (actualWF : VLCtx.WF env Us.length actual) + (relation : VLCtx.IsDefEqFVars typeEnv Us.length actual postActual) + (actualSourceTr : TrExprS env Us actual source actualSource') + (actualAnnotations : env.IsDefEqU Us.length actual.toCtx + actualSource' actualConsumed') + (actualConsumedType : env.IsType Us.length actual.toCtx actualConsumed') + (postViewTr : TrExprS typeEnv Us postActual source postView') + (postRawView : typeEnv.IsDefEqU Us.length postActual.toCtx + postRaw' postView') + (postAnnotations : typeEnv.IsDefEqU Us.length postActual.toCtx + postRaw' postConsumed') : + ∃ level, typeEnv.IsDefEq Us.length actual.toCtx + actualConsumed' postConsumed' (.sort level) := by + have actualWF' := actualWF.mono addType + have sourceEq : typeEnv.IsDefEqU Us.length actual.toCtx + actualSource' postView' := + (actualSourceTr.mono addType).uniqFVars typeEnvWF relation actualWF' + postViewTr + have postToActual := relation.defeqCtx.symm typeEnvWF.ordered + have rawView := postRawView.defeqDFC typeEnvWF.ordered postToActual + have rawConsumed := postAnnotations.defeqDFC typeEnvWF.ordered postToActual + have consumedEq := (actualAnnotations.mono addType).symm.trans typeEnvWF + actualWF'.toCtx (sourceEq.trans typeEnvWF actualWF'.toCtx + (rawView.symm.trans typeEnvWF actualWF'.toCtx rawConsumed)) + obtain ⟨level, consumedType⟩ := actualConsumedType + exact ⟨level, consumedEq.of_l typeEnvWF actualWF'.toCtx + (consumedType.mono addType)⟩ + +/-- The terminal kernel sort retained by a candidate recursion translates to +the exact terminal Theory sort exposed by its analyzer-owned view telescope. -/ +theorem CandidateExprRun.terminalLevel_tr + {env : VEnv} {Us : List Name} + {candidateContext : AddInductive.Context} {source : Expr} + {trace : AddInductive.CandidateExprTrace candidateContext source} + {context : VLCtx} {source' view' inferred' : VExpr} + (run : CandidateExprRun env Us trace context source' view' inferred') + {resultLevel : Level} {resultLevel' : VLevel} {binders : List VExpr} + (terminal : trace.terminalResult = .sort resultLevel) + (viewEq : view' = VExpr.forallN binders (.sort resultLevel')) + (lengthEq : trace.spineLength = binders.length) : + VLevel.ofLevel Us resultLevel = some resultLevel' := by + induction run generalizing binders with + | terminal node => + simp only [AddInductive.CandidateExprTrace.spineLength] at lengthEq + have bindersEq : binders = [] := List.eq_nil_of_length_eq_zero + lengthEq.symm + subst binders + simp only [VExpr.forallN] at viewEq + simp only [AddInductive.CandidateExprTrace.terminalResult] at terminal + rw [terminal, viewEq] at node + cases node.whnf.rhs_tr with + | sort levelTr => exact levelTr + | @forallE domain candidateContext name binderInfo context source inferred + body source' domain' body' inferred' domainView' domainInferred' + storedDomain' bodyContext storedBody' bodyView' bodyInferred' u v fresh + checked normalized annotations annotationsEq domainCandidate bodyCandidate + node domainRun annotationsRun bodyRun domainType bodyType bodySource + bodyContextEq domainIH bodyIH => + simp only [AddInductive.CandidateExprTrace.spineLength] at lengthEq + cases binders with + | nil => simp at lengthEq + | cons binder binders => + simp only [VExpr.forallN, VExpr.forallE.injEq] at viewEq + obtain ⟨_, bodyViewEq⟩ := viewEq + apply bodyIH terminal bodyViewEq + simpa only [List.length_cons] using Nat.succ.inj lengthEq + +end ConstructorValidation + + +namespace ConstructorValidation +open AddInductive TypeChecker +-- `VEnv` here resolves to these two namespaces; `_root_.Ix.Theory.Named.VEnv` is +-- shadowed by the enclosing namespace, so name both explicitly. +open _root_.Ix.Theory.Named.ConstructorValidation.VEnv _root_.Ix.Theory.Named.TypeChecker.VEnv + +/-- Every full-context free variable not deliberately omitted by D3 is still +present in the family-free common context. -/ +def FullRemovedInvariant (common full : VLCtx) + (removed : List FVarId) : Prop := + ∀ fv, fv ∈ full.fvars → fv ∉ removed → fv ∈ common.fvars + +theorem FullRemovedInvariant.push + (invariant : FullRemovedInvariant common full removed) : + FullRemovedInvariant + ((some (fv, commonDeps), commonDomain) :: common) + ((some (fv, fullDeps), fullDomain) :: full) removed := by + intro candidate present notRemoved + simp only [VLCtx.fvars_cons_some, List.mem_cons] at present ⊢ + rcases present with rfl | present + · exact .inl rfl + · exact .inr (invariant candidate present notRemoved) + +theorem FullRemovedInvariant.skip + (invariant : FullRemovedInvariant common full removed) + (fresh : fv ∉ full.fvars) : + FullRemovedInvariant common + ((some (fv, fullDeps), fullDomain) :: full) (fv :: removed) := by + intro candidate present notRemoved + simp only [VLCtx.fvars_cons_some, List.mem_cons] at present + rcases present with rfl | present + · exact (notRemoved (by simp)).elim + · exact invariant candidate present (fun old => notRemoved (by simp [old])) + +theorem Context.freshFVarId_eq_of_ngen_eq + {left right : AddInductive.Context} + (equal : left.ngen = right.ngen) : + left.freshFVarId = right.freshFVarId := by + simp only [AddInductive.Context.freshFVarId, equal] + +theorem Context.push_advance_ngen_eq + {left right : AddInductive.Context} + (equal : left.ngen = right.ngen) : + (left.pushLocalDecl name binderInfo domain).ngen = + right.advanceFresh.ngen := by + simp only [AddInductive.Context.pushLocalDecl, + AddInductive.Context.advanceFresh, equal] + +theorem ordinaryConsumed_defeqAt + {env typeEnv : VEnv} {Us : List Name} + {actual postActual : VLCtx} {source : Expr} + {actualSource' actualConsumed' postRaw' postView' postConsumed' : VExpr} + {consumedLevel : VLevel} + (typeEnvWF : VEnv.WF typeEnv) (addType : env ≤ typeEnv) + (actualWF : VLCtx.WF env Us.length actual) + (relation : VLCtx.IsDefEqFVars typeEnv Us.length actual postActual) + (actualSourceTr : TrExprS env Us actual source actualSource') + (actualAnnotations : env.IsDefEqU Us.length actual.toCtx + actualSource' actualConsumed') + (actualConsumedType : env.HasType Us.length actual.toCtx actualConsumed' + (.sort consumedLevel)) + (postViewTr : TrExprS typeEnv Us postActual source postView') + (postRawView : typeEnv.IsDefEqU Us.length postActual.toCtx + postRaw' postView') + (postAnnotations : typeEnv.IsDefEqU Us.length postActual.toCtx + postRaw' postConsumed') : + typeEnv.IsDefEq Us.length actual.toCtx + actualConsumed' postConsumed' (.sort consumedLevel) := by + have actualWF' := actualWF.mono addType + have sourceEq : typeEnv.IsDefEqU Us.length actual.toCtx + actualSource' postView' := + (actualSourceTr.mono addType).uniqFVars typeEnvWF relation actualWF' + postViewTr + have postToActual := relation.defeqCtx.symm typeEnvWF.ordered + have rawView := postRawView.defeqDFC typeEnvWF.ordered postToActual + have rawConsumed := postAnnotations.defeqDFC typeEnvWF.ordered postToActual + have consumedEq := (actualAnnotations.mono addType).symm.trans typeEnvWF + actualWF'.toCtx (sourceEq.trans typeEnvWF actualWF'.toCtx + (rawView.symm.trans typeEnvWF actualWF'.toCtx rawConsumed)) + exact consumedEq.of_l typeEnvWF actualWF'.toCtx + (actualConsumedType.mono addType) + +theorem ConstructorUniverseTrace.bound + {stats : AddInductive.InductiveStats} + {sortResult : Expr} + (trace : AddInductive.ConstructorUniverseTrace stats.resultLevel + sortResult.sortLevel!) + (valid : trace.semantic = true) + (resultLevelTr : VLevel.ofLevel Us stats.resultLevel = some resultLevel) + (ensureTypeRun : TypeChecker.EnsureTypeRun typeEnv Us postContext + source sortResult source') : + resultLevel = .zero ∨ ensureTypeRun.resultLevel' ≤ resultLevel := by + apply AddInductive.constructorUniverseSemanticGe_ofLevel valid resultLevelTr + have sortLevelEq : sortResult.sortLevel! = ensureTypeRun.resultLevel := by + simpa only [Expr.sortLevel!] using + congrArg Expr.sortLevel! ensureTypeRun.result_eq + rw [sortLevelEq] + exact ensureTypeRun.resultLevel_tr + +/-- D3's terminal index replay transported to the analyzer's exact full +context and exact translated result target. -/ +theorem terminal_exactAnalyzer + {env typeEnv : VEnv} {Us : List Name} + {stats : AddInductive.InductiveStats} {familyIdx : Nat} + {familyIndices : Expr} {context : AddInductive.Context} + {contextRun : AddInductive.ConstructorContextRun env Us context} + {source : Expr} + {valid : AddInductive.isValidIndAppIdx stats source familyIdx = true} + {spineTrace : AddInductive.ConstructorPreFamilyIndexSpineTrace context + familyIndices (source.getAppArgs.toList.drop stats.params.size)} + {expected' : VExpr} + (spine : AddInductive.ConstructorPreFamilyIndexSpineSemanticRun env Us + context contextRun spineTrace expected') + (henv : VEnv.WF env) (typeEnvWF : VEnv.WF typeEnv) + (addType : env ≤ typeEnv) + {common full view : VLCtx} {fullLift viewLift : Lift} + (state : D3FullContextState env typeEnv Us context common full + contextRun.candidate.context.vlctx view fullLift viewLift) + {commonIndices : List VExpr} {familyTarget resultTarget : VExpr} + {level : VLevel} {familyName : Name} {familyLevels : List Level} + (familyCommonTr : TrExprS env Us common familyIndices + (VExpr.forallN commonIndices (.sort level))) + (familyFullTr : TrExprS typeEnv Us full familyIndices familyTarget) + (familyUnique : TrExprS.IsUnique familyIndices) + (indexLength : commonIndices.length = stats.nindices[familyIdx]!) + (familyHead : stats.indConsts[familyIdx]! = + .const familyName familyLevels) + (sourceUnique : TrExprS.IsUnique source) + (sourceClosed : Closed source) + (sourceFVars : FVarsIn (· ∈ common.fvars) source) + (fullTr : TrExprS typeEnv Us full source resultTarget) : + env.SpineWF Us.length full.toCtx familyTarget + (VInductDecl.recFieldIdxs stats.params.size resultTarget) + (.sort level) := by + have argumentClosed : ∀ argument ∈ + source.getAppArgs.toList.drop stats.params.size, + Closed argument := by + intro argument member + apply Closed.getAppArgsList sourceClosed + rw [← Expr.getAppArgs_toList] + exact List.mem_of_mem_drop member + have argumentFVars : ∀ argument ∈ + source.getAppArgs.toList.drop stats.params.size, + FVarsIn (· ∈ common.fvars) argument := by + intro argument member + apply FVarsIn.getAppArgsList sourceFVars + rw [← Expr.getAppArgs_toList] + exact List.mem_of_mem_drop member + have argumentUnique : ∀ argument ∈ + source.getAppArgs.toList.drop stats.params.size, + TrExprS.IsUnique argument := by + intro argument member + apply TrExprS.IsUnique.getAppArgsList sourceUnique + rw [← Expr.getAppArgs_toList] + exact List.mem_of_mem_drop member + have argumentLength : + (source.getAppArgs.toList.drop stats.params.size).length = + commonIndices.length := + (isValidIndAppIdx_indexArgs_length valid).trans indexLength.symm + obtain ⟨baseIndices, baseIndicesTr, baseSpine⟩ := + ConstructorPreFamilyIndexSpineSemanticRun.baseSpine_lift + spine familyCommonTr familyUnique state.viewDefEq state.viewUnique + state.viewExtension argumentClosed argumentFVars argumentUnique + argumentLength + have commonFamilyAtFull : TrExprS typeEnv Us full familyIndices + ((VExpr.forallN commonIndices (.sort level)).lift' fullLift) := by + simpa using (familyCommonTr.mono addType).weakFV' + typeEnvWF.ordered state.fullExtension state.fullWF + have familyEq : familyTarget = + (VExpr.forallN commonIndices (.sort level)).lift' fullLift := + familyFullTr.unique familyUnique commonFamilyAtFull + have shape := isValidIndAppIdx_shape valid + have sourceHead : source.getAppFn = .const familyName familyLevels := by + rw [familyHead] at shape + change source.getAppFn.eqv (.const familyName familyLevels) = true ∧ _ + at shape + rw [Expr.eqv_eq] at shape + generalize headEq : source.getAppFn = head at shape + cases head <;> simp_all [Expr.eqv'] + obtain ⟨targetLevels, targetHead, allArgumentsTr⟩ := + TrExprS.constApp_components fullTr sourceHead + have droppedArgumentsTr := + forall₂_drop allArgumentsTr stats.params.size + have baseIndicesAtFull : List.Forall₂ (TrExprS typeEnv Us full) + (source.getAppArgsList.drop stats.params.size) + (baseIndices.map fun index => index.lift' fullLift) := by + have baseMono := forall₂_tr_mono addType baseIndicesTr + have baseWeak := forall₂_tr_weakFV' typeEnvWF.ordered + state.fullExtension state.fullWF baseMono + simpa only [Expr.getAppArgs_toList] using baseWeak + have translatedIndicesEq : + (VExpr.appArgs resultTarget []).drop stats.params.size = + baseIndices.map fun index => index.lift' fullLift := + forall₂_translation_unique droppedArgumentsTr baseIndicesAtFull + (fun argument member => + TrExprS.IsUnique.getAppArgsList sourceUnique argument + (List.mem_of_mem_drop member)) + have fullSpine : env.SpineWF Us.length full.toCtx + ((VExpr.forallN commonIndices (.sort level)).lift' fullLift) + (baseIndices.map fun index => index.lift' fullLift) (.sort level) := by + simpa using VEnv.SpineWF.weak' henv.ordered + state.fullExtension.toCtx baseSpine + rw [← familyEq, ← translatedIndicesEq] at fullSpine + simpa only [VInductDecl.recFieldIdxs] using fullSpine + +def ConstructorFieldsRunResult + (env : VEnv) (Us : List Name) (full : VLCtx) + (familyTarget : VExpr) (level : VLevel) (familyName : Name) + (parameters : Nat) (familyIndices fields : List VExpr) + (fieldIndex : Nat) (resultTarget : VExpr) : Prop := + VInductDecl.fieldsWF Us.length familyName parameters env level familyIndices + full.toCtx fieldIndex fields ∧ + env.SpineWF Us.length (fields.reverse ++ full.toCtx) + (familyTarget.liftN fields.length 0) + (VInductDecl.recFieldIdxs parameters resultTarget) (.sort level) + +/-- Synchronize the exact D3 family-free replay with D2's analyzer-owned +constructor view, deriving every field judgment and the terminal index spine. -/ +theorem constructorFields_exactAnalyzer + {env typeEnv : VEnv} {Us : List Name} + {stats : AddInductive.InductiveStats} {familyIdx : Nat} + {familyIndices : Expr} + {d3Context d2Context : AddInductive.Context} + {d3ContextRun : AddInductive.ConstructorContextRun env Us d3Context} + {d2ContextRun : AddInductive.ConstructorContextRun typeEnv Us d2Context} + {view : Expr} {argIdx : Nat} {removed : List FVarId} + {recursiveStarted : Bool} + {d3Trace : AddInductive.ConstructorPreFamilyViewTrace stats familyIdx + familyIndices d3Context view argIdx removed recursiveStarted} + (d3 : AddInductive.ConstructorPreFamilyViewSemanticRun env Us stats + familyIdx familyIndices d3ContextRun d3Trace) + {isUnsafe : Bool} {ctorName : Name} {rawSource : Expr} + {d2Fuel whnfFuel : Nat} + {d2Trace : AddInductive.ConstructorTypeValidationTrace stats isUnsafe + familyIdx ctorName d2Context rawSource argIdx d2Fuel} + (d2Alignment : AddInductive.ConstructorViewAlignmentTrace d2Trace view) + (d2 : AddInductive.ConstructorViewSemanticRun typeEnv Us whnfFuel + d2ContextRun d2Trace view) + (henv : VEnv.WF env) (typeEnvWF : VEnv.WF typeEnv) + (addType : env ≤ typeEnv) + {common full d3ViewContext analyzerViewContext : VLCtx} + {fullLift viewLift analyzerViewLift : Lift} + (d3State : D3FullContextState env typeEnv Us d3Context common full + d3ContextRun.candidate.context.vlctx d3ViewContext fullLift viewLift) + (analyzerState : AnalyzerPostContextState typeEnv Us full + d2ContextRun.candidate.context.vlctx analyzerViewContext + analyzerViewLift) + (removedInvariant : FullRemovedInvariant common full removed) + (ngenEq : d3Context.ngen = d2Context.ngen) + {commonIndices checkedIndices fields : List VExpr} + {familyTarget resultTarget : VExpr} {level : VLevel} + {familyName : Name} {familyLevels : List Level} + (familyCommonTr : TrExprS env Us common familyIndices + (VExpr.forallN commonIndices (.sort level))) + (familyFullTr : TrExprS typeEnv Us full familyIndices familyTarget) + (familyUnique : TrExprS.IsUnique familyIndices) + (indexLength : commonIndices.length = stats.nindices[familyIdx]!) + (familyHead : stats.indConsts[familyIdx]! = + .const familyName familyLevels) + (familyTargetEq : familyTarget = + VExpr.forallN (VExpr.liftTelN fieldIndex checkedIndices 0) + (.sort level)) + (resultNotForall : ∀ domain body, resultTarget ≠ .forallE domain body) + (resultLevelTr : VLevel.ofLevel Us stats.resultLevel = some level) + (absent : env.constants familyName = none) + (sourceUnique : TrExprS.IsUnique view) + (sourceClosed : Closed view) + (wholeTr : TrExprS typeEnv Us full view + (VExpr.forallN fields resultTarget)) + (parametersDone : stats.params.size ≤ argIdx) + (universeSemantics : d2Trace.universeSemantics = true) + (stageFields : ∀ q field, fields[q]? = some field → + VInductDecl.stage3Field Us.length familyName stats.params.size + checkedIndices.length (fieldIndex + q) field = true) : + ConstructorFieldsRunResult env Us full familyTarget level familyName + stats.params.size checkedIndices fields fieldIndex resultTarget := by + induction d3 generalizing d2Context d2ContextRun rawSource d2Fuel + common full d3ViewContext fullLift viewLift commonIndices familyTarget + analyzerViewContext analyzerViewLift fields fieldIndex with + | @parameter context parameterArgIdx removed recursiveStarted name domain + body binderInfo parameter parameterAt tailTrace contextRun tail ih => + have parameterLt : parameterArgIdx < stats.params.size := by + exact Array.getElem?_eq_some_iff.mp parameterAt |>.1 + omega + | @ordinary context ordinaryArgIdx removed recursiveStarted name domain body + binderInfo noParameter nonrecursive independent domainCheck ensureType + consumedCheck annotations fresh tailTrace contextRun domainRun + consumedRun ensureTypeRun annotationsRun consumedType tail ih => + cases d2 with + | @parameter _ _ _ _ d2Context d2Fuel ordinaryArgIdx name₂ + rawDomain rawBody + binderInfo₂ parameter parameterType parameterAt parameterTypeGet + validationDefEq d2TailTrace viewName viewDomain viewBody + viewBinderInfo domainCheck₂ viewDomainCheck₂ parameterTypeCheck + d2ContextRun domainRun₂ viewDomainRun₂ parameterTypeSemantic + validationRun tail₂ => + rw [noParameter] at parameterAt + contradiction + | @ordinary _ _ _ _ d2Context d2Fuel ordinaryArgIdx name₂ + rawDomain rawBody + binderInfo₂ sortResult noParameter₂ ensureTypeStep universeTrace + positivityTrace d2TailTrace viewName viewDomain viewBody + viewBinderInfo domainCheck₂ viewDomainCheck₂ viewEquality + consumedCheck₂ fresh₂ d2ContextRun domainRun₂ viewDomainRun₂ + viewEqualityRun₂ + consumedRun₂ ensureTypeRun₂ positivity₂ annotationsRun₂ + consumedType₂ tail₂ => + cases d2Alignment with + | ordinary _ _ _ _ _ positivityAlignment _ _ _ tailAlignment => + obtain ⟨fieldTarget, bodyTarget, targetEq, fieldType, + bodyType, fieldTr, bodyTr⟩ := + TrExprS.forallE_components wholeTr + cases fields with + | nil => + exact (resultNotForall fieldTarget bodyTarget targetEq).elim + | cons field fields => + simp only [VExpr.forallN, VExpr.forallE.injEq] at targetEq + obtain ⟨rfl, rfl⟩ := targetEq + have actualFieldTr : TrExprS env Us + contextRun.candidate.context.vlctx domain + domainRun.source' := by + simpa only [contextRun.venv_eq, contextRun.lparams_eq] + using domainRun.check.expr_tr + have actualFieldType : env.HasType Us.length + contextRun.candidate.context.vlctx.toCtx + domainRun.source' (.sort ensureTypeRun.resultLevel') := by + simpa only [contextRun.venv_eq, contextRun.lparams_eq] + using ensureTypeRun.source_type + have actualAnnotations : env.IsDefEqU Us.length + contextRun.candidate.context.vlctx.toCtx + domainRun.source' consumedRun.source' := by + simpa only [contextRun.venv_eq, contextRun.lparams_eq] + using annotationsRun.isDefEqU + obtain ⟨consumedLevel, actualConsumedType⟩ := consumedType + have actualConsumedType' : env.HasType Us.length + contextRun.candidate.context.vlctx.toCtx + consumedRun.source' (.sort consumedLevel) := by + simpa only [contextRun.venv_eq, contextRun.lparams_eq] + using actualConsumedType + let nextD3ContextRun := contextRun.pushLocalDecl name + binderInfo (consumeTypeAnnotations domain) fresh + consumedRun.source' consumedRun.check.expr_tr + ⟨consumedLevel, actualConsumedType⟩ + have actualTailWF : VLCtx.WF env Us.length + ((some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam consumedRun.source') :: + contextRun.candidate.context.vlctx) := by + have nextWF := nextD3ContextRun.candidate.context.Δwf + rw [nextD3ContextRun.venv_eq, + nextD3ContextRun.lparams_eq] at nextWF + exact nextWF + have postViewTr : TrExprS typeEnv Us + d2ContextRun.candidate.context.vlctx domain + viewDomainRun₂.source' := by + simpa only [d2ContextRun.venv_eq, + d2ContextRun.lparams_eq] using + viewDomainRun₂.check.expr_tr + have postRawType : typeEnv.HasType Us.length + d2ContextRun.candidate.context.vlctx.toCtx + domainRun₂.source' (.sort ensureTypeRun₂.resultLevel') := by + simpa only [d2ContextRun.venv_eq, + d2ContextRun.lparams_eq] using ensureTypeRun₂.source_type + have postRawView : typeEnv.IsDefEqU Us.length + d2ContextRun.candidate.context.vlctx.toCtx + domainRun₂.source' viewDomainRun₂.source' := by + simpa only [d2ContextRun.venv_eq, + d2ContextRun.lparams_eq] using + viewEqualityRun₂.isDefEqU + have postAnnotations : typeEnv.IsDefEqU Us.length + d2ContextRun.candidate.context.vlctx.toCtx + domainRun₂.source' consumedRun₂.source' := by + simpa only [d2ContextRun.venv_eq, + d2ContextRun.lparams_eq] using + annotationsRun₂.isDefEqU + let nextD2ContextRun := d2ContextRun.pushLocalDecl name₂ + binderInfo₂ (consumeTypeAnnotations rawDomain) fresh₂ + consumedRun₂.source' consumedRun₂.check.expr_tr + consumedType₂ + have postTailWF : VLCtx.WF typeEnv Us.length + ((some (d2Context.freshFVarId, + (consumeTypeAnnotations rawDomain).fvarsList), + .vlam consumedRun₂.source') :: + d2ContextRun.candidate.context.vlctx) := by + have nextWF := nextD2ContextRun.candidate.context.Δwf + rw [nextD2ContextRun.venv_eq, + nextD2ContextRun.lparams_eq] at nextWF + simpa only [nextD2ContextRun, + AddInductive.ConstructorContextRun.pushLocalDecl, + CandidateContextRun.pushLocalDecl_vlctx] using nextWF + simp only [AddInductive.ConstructorTypeValidationTrace.universeSemantics, + Bool.and_eq_true] at universeSemantics + have sortLevelEq : sortResult.sortLevel! = + ensureTypeRun₂.resultLevel := by + simpa only [Expr.sortLevel!] using + congrArg Expr.sortLevel! ensureTypeRun₂.result_eq + have fieldLevelTr : VLevel.ofLevel Us + sortResult.sortLevel! = + some ensureTypeRun₂.resultLevel' := by + rw [sortLevelEq] + simpa only [d2ContextRun.lparams_eq] using + ensureTypeRun₂.resultLevel_tr + have rawBound := + AddInductive.constructorUniverseSemanticGe_ofLevel + universeSemantics.1 resultLevelTr fieldLevelTr + have freshEq : context.freshFVarId = + d2Context.freshFVarId := + Context.freshFVarId_eq_of_ngen_eq ngenEq + have domainFVars : FVarsIn (· ∈ common.fvars) domain := + constructorIndependentOf_fvars fieldTr.fvarsIn + independent removedInvariant + obtain ⟨commonDomain, commonDomainTr, fieldEq, + commonDomainType, nextD3State⟩ := + d3State.push (name := name) (binderInfo := binderInfo) + henv typeEnvWF addType sourceUnique.1 + sourceClosed.1 domainFVars fieldTr actualFieldTr + actualFieldType actualAnnotations actualConsumedType' + actualTailWF + obtain ⟨postAnalyzerType, nextAnalyzerState⟩ := + analyzerState.push typeEnvWF sourceUnique.1 sourceClosed.1 + fieldTr postViewTr postRawType postRawView + postAnnotations postTailWF + have fieldBaseType : env.HasType Us.length full.toCtx field + (.sort ensureTypeRun.resultLevel') := by + rw [fieldEq] + exact commonDomainType.weak' henv.ordered + d3State.fullExtension.toCtx + have levelEq : ensureTypeRun.resultLevel' ≈ + ensureTypeRun₂.resultLevel' := + (fieldBaseType.mono addType).uniqU typeEnvWF + d3State.fullWF.toCtx postAnalyzerType |>.sort_inv + typeEnvWF d3State.fullWF.toCtx + have fieldBound : level = .zero ∨ + ensureTypeRun.resultLevel' ≤ level := by + rcases rawBound with prop | bound + · exact .inl prop + · exact .inr <| VLevel.le_trans + (VLevel.le_antisymm_iff.mp levelEq).1 bound + have commonFree : commonDomain.hasConst familyName = false := + VEnv.HasType.hasConst_false_of_absent henv.ordered + d3State.commonWF.toCtx absent commonDomainType + have fieldFree : field.hasConst familyName = false := by + rw [fieldEq, VExpr.hasConst_lift'] + exact commonFree + have recNone := recArg?_eq_none_of_hasConst_false + (U := Us.length) (np := stats.params.size) + (ni := checkedIndices.length) (fieldIndex := fieldIndex) + fieldFree + have nextD3State' : D3FullContextState env typeEnv Us + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)) + ((some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam commonDomain) :: common) + ((some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam field) :: full) + nextD3ContextRun.candidate.context.vlctx + ((some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam (commonDomain.lift' viewLift)) :: d3ViewContext) + (.consN fullLift 1) (.consN viewLift 1) := by + exact nextD3State + have nextAnalyzerState' : AnalyzerPostContextState + typeEnv Us + ((some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam field) :: full) + nextD2ContextRun.candidate.context.vlctx + ((some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam (field.lift' analyzerViewLift)) :: + analyzerViewContext) + (.consN analyzerViewLift 1) := by + rw [freshEq] + simpa only [nextD2ContextRun, + AddInductive.ConstructorContextRun.pushLocalDecl, + CandidateContextRun.pushLocalDecl_vlctx] using + nextAnalyzerState + have familyCommonNext : TrExprS env Us + ((some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam commonDomain) :: common) + familyIndices + (VExpr.forallN (VExpr.liftTelN 1 commonIndices 0) + (.sort level)) := by + have weakened := familyCommonTr.weakFV henv.ordered + (VLCtx.FVLift.skip_fvar + (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList) + (.vlam commonDomain) .refl) + nextD3State'.commonWF + simpa [VLocalDecl.depth, VExpr.liftN_forallN, + VExpr.liftN] using weakened + have familyFullNext : TrExprS typeEnv Us + ((some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam field) :: full) + familyIndices (familyTarget.liftN 1 0) := by + have weakened := familyFullTr.weakFV typeEnvWF.ordered + (VLCtx.FVLift.skip_fvar + (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList) + (.vlam field) .refl) + nextD3State'.fullWF + simpa [VLocalDecl.depth] using weakened + have bodyOpened : TrExprS typeEnv Us + ((some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam field) :: full) + (body.instantiate1 context.freshExpr) + (VExpr.forallN fields resultTarget) := by + simpa only [AddInductive.Context.freshExpr, + Expr.instantiate1_eq] using + bodyTr.inst_fvar typeEnvWF.ordered nextD3State'.fullWF + have tailUnique : TrExprS.IsUnique + (body.instantiate1 context.freshExpr) := by + apply TrExprS.IsUnique.instantiate1 sourceUnique.2 + simp only [AddInductive.Context.freshExpr] + trivial + have nextFullNoBV : + VLCtx.bvars (((some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam field) :: full) : VLCtx) = 0 := + nextD3State'.fullExtension.bvars_eq.trans + nextD3State'.commonNoBV + have tailClosed : Closed + (body.instantiate1 context.freshExpr) := by + have closed := bodyOpened.closed + rw [nextFullNoBV] at closed + exact closed + have freshExprEq : d2Context.freshExpr = + context.freshExpr := by + simp only [AddInductive.Context.freshExpr, freshEq] + have tailAlignment' : AddInductive.ConstructorViewAlignmentTrace + d2TailTrace (body.instantiate1 context.freshExpr) := by + rw [← freshExprEq] + exact tailAlignment + have tail₂' : AddInductive.ConstructorViewSemanticRun + typeEnv Us whnfFuel nextD2ContextRun d2TailTrace + (body.instantiate1 context.freshExpr) := by + rw [← freshExprEq] + exact tail₂ + have nextNgenEq : + (context.pushLocalDecl name binderInfo + (consumeTypeAnnotations domain)).ngen = + (d2Context.pushLocalDecl name₂ binderInfo₂ + (consumeTypeAnnotations rawDomain)).ngen := by + simp only [AddInductive.Context.pushLocalDecl, ngenEq] + have nextIndexLength : + (VExpr.liftTelN 1 commonIndices 0).length = + stats.nindices[familyIdx]! := by + simpa only [VExpr.liftTelN_length] using indexLength + have familyTargetNextEq : familyTarget.liftN 1 0 = + VExpr.forallN + (VExpr.liftTelN (fieldIndex + 1) checkedIndices 0) + (.sort level) := by + rw [familyTargetEq, VExpr.liftN_forallN, + VExpr.liftTelN_liftTelN] + simp only [VExpr.liftN] + have stageTail : ∀ q candidate, + fields[q]? = some candidate → + VInductDecl.stage3Field Us.length familyName + stats.params.size checkedIndices.length + (fieldIndex + 1 + q) candidate = true := by + intro q candidate member + have staged := stageFields (q + 1) candidate (by + simpa using member) + simpa only [Nat.add_assoc, Nat.add_comm 1 q] using staged + obtain ⟨tailFields, tailSpine⟩ := ih + (fieldIndex := fieldIndex + 1) tailAlignment' tail₂' + nextD3State' nextAnalyzerState' + (removedInvariant.push) nextNgenEq familyCommonNext + familyFullNext nextIndexLength familyTargetNextEq + tailUnique tailClosed bodyOpened (by omega) + universeSemantics.2 stageTail + unfold ConstructorFieldsRunResult + refine ⟨⟨?_, ?_, ?_⟩, ?_⟩ + · exact .inr (.inr ⟨recNone, ensureTypeRun.resultLevel', + fieldBaseType, fieldBound⟩) + · intro recursive + rw [VInductDecl.recArg?_of_isRecField recursive] at recNone + contradiction + · simpa only [VLCtx.toCtx] using tailFields + · simpa only [List.reverse_cons, List.singleton_append, + List.append_assoc, VLCtx.toCtx, List.length_cons, + VExpr.liftN_liftN, Nat.add_comm] using tailSpine + | terminal sourceRun₂ viewRun₂ => + cases d2Alignment; simp_all [Expr.isForall] + | @recursive context recursiveArgIdx removed recursiveStarted name domain + body binderInfo noParameter isRecursive independent fieldTrace fresh + tailTrace contextRun recursiveRun tail ih => + cases d2 with + | @parameter _ _ _ _ d2Context d2Fuel recursiveArgIdx name₂ + rawDomain rawBody binderInfo₂ parameter parameterType parameterAt + parameterTypeGet validationDefEq d2TailTrace viewName viewDomain + viewBody viewBinderInfo domainCheck₂ viewDomainCheck₂ + parameterTypeCheck d2ContextRun domainRun₂ viewDomainRun₂ + parameterTypeSemantic validationRun tail₂ => + rw [noParameter] at parameterAt + contradiction + | @ordinary _ _ _ _ d2Context d2Fuel recursiveArgIdx name₂ + rawDomain rawBody binderInfo₂ sortResult noParameter₂ + ensureTypeStep universeTrace positivityTrace d2TailTrace viewName + viewDomain viewBody viewBinderInfo domainCheck₂ viewDomainCheck₂ + viewEquality consumedCheck₂ fresh₂ d2ContextRun domainRun₂ + viewDomainRun₂ viewEqualityRun₂ consumedRun₂ ensureTypeRun₂ + positivity₂ annotationsRun₂ consumedType₂ tail₂ => + cases d2Alignment with + | ordinary _ _ _ _ _ positivityAlignment _ _ _ tailAlignment => + obtain ⟨fieldTarget, bodyTarget, targetEq, fieldType, + bodyType, fieldTr, bodyTr⟩ := + TrExprS.forallE_components wholeTr + cases fields with + | nil => + exact (resultNotForall fieldTarget bodyTarget targetEq).elim + | cons field fields => + simp only [VExpr.forallN, VExpr.forallE.injEq] at targetEq + obtain ⟨rfl, rfl⟩ := targetEq + have postViewTr : TrExprS typeEnv Us + d2ContextRun.candidate.context.vlctx domain + viewDomainRun₂.source' := by + simpa only [d2ContextRun.venv_eq, + d2ContextRun.lparams_eq] using + viewDomainRun₂.check.expr_tr + have postRawType : typeEnv.HasType Us.length + d2ContextRun.candidate.context.vlctx.toCtx + domainRun₂.source' (.sort ensureTypeRun₂.resultLevel') := by + simpa only [d2ContextRun.venv_eq, + d2ContextRun.lparams_eq] using ensureTypeRun₂.source_type + have postRawView : typeEnv.IsDefEqU Us.length + d2ContextRun.candidate.context.vlctx.toCtx + domainRun₂.source' viewDomainRun₂.source' := by + simpa only [d2ContextRun.venv_eq, + d2ContextRun.lparams_eq] using + viewEqualityRun₂.isDefEqU + have postAnnotations : typeEnv.IsDefEqU Us.length + d2ContextRun.candidate.context.vlctx.toCtx + domainRun₂.source' consumedRun₂.source' := by + simpa only [d2ContextRun.venv_eq, + d2ContextRun.lparams_eq] using annotationsRun₂.isDefEqU + let nextD2ContextRun := d2ContextRun.pushLocalDecl name₂ + binderInfo₂ (consumeTypeAnnotations rawDomain) fresh₂ + consumedRun₂.source' consumedRun₂.check.expr_tr + consumedType₂ + have postTailWF : VLCtx.WF typeEnv Us.length + ((some (d2Context.freshFVarId, + (consumeTypeAnnotations rawDomain).fvarsList), + .vlam consumedRun₂.source') :: + d2ContextRun.candidate.context.vlctx) := by + have nextWF := nextD2ContextRun.candidate.context.Δwf + rw [nextD2ContextRun.venv_eq, + nextD2ContextRun.lparams_eq] at nextWF + simpa only [nextD2ContextRun, + AddInductive.ConstructorContextRun.pushLocalDecl, + CandidateContextRun.pushLocalDecl_vlctx] using nextWF + simp only [ + AddInductive.ConstructorTypeValidationTrace.universeSemantics, + Bool.and_eq_true] at universeSemantics + have freshEq : context.freshFVarId = + d2Context.freshFVarId := + Context.freshFVarId_eq_of_ngen_eq ngenEq + obtain ⟨postAnalyzerType, nextAnalyzerState⟩ := + analyzerState.push typeEnvWF sourceUnique.1 sourceClosed.1 + fieldTr postViewTr postRawType postRawView + postAnnotations postTailWF + have nextAnalyzerState' : AnalyzerPostContextState + typeEnv Us + ((some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam field) :: full) + nextD2ContextRun.candidate.context.vlctx + ((some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam (field.lift' analyzerViewLift)) :: + analyzerViewContext) + (.consN analyzerViewLift 1) := by + rw [freshEq] + simpa only [nextD2ContextRun, + AddInductive.ConstructorContextRun.pushLocalDecl, + CandidateContextRun.pushLocalDecl_vlctx] using + nextAnalyzerState + have domainFVars : FVarsIn (· ∈ common.fvars) domain := + constructorIndependentOf_fvars fieldTr.fvarsIn + independent removedInvariant + have recursiveResult := recursiveField_exactAnalyzer + recursiveRun henv typeEnvWF addType d3State + familyCommonTr familyFullTr familyUnique indexLength + familyHead sourceUnique.1 sourceClosed.1 domainFVars fieldTr + have stageHead : VInductDecl.stage3Field Us.length + familyName stats.params.size checkedIndices.length + fieldIndex field = true := by + simpa using stageFields 0 field (by simp) + obtain ⟨recursive, recursiveEq, recursiveWF⟩ := + recursiveFieldResult_recArgWF recursiveResult + familyTargetEq stageHead + have nextD3State : D3FullContextState env typeEnv Us + context.advanceFresh common + ((some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam field) :: full) + contextRun.advanceFresh.candidate.context.vlctx + d3ViewContext (.skipN fullLift 1) viewLift := by + simpa only [AddInductive.ConstructorContextRun.advanceFresh, + AddInductive.advanceCandidateContextRun] using + d3State.skip (source := domain) + nextAnalyzerState'.fullWF + have familyFullNext : TrExprS typeEnv Us + ((some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam field) :: full) + familyIndices (familyTarget.liftN 1 0) := by + have weakened := familyFullTr.weakFV typeEnvWF.ordered + (VLCtx.FVLift.skip_fvar + (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList) + (.vlam field) .refl) + nextD3State.fullWF + simpa [VLocalDecl.depth] using weakened + have bodyOpened : TrExprS typeEnv Us + ((some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam field) :: full) + (body.instantiate1 context.freshExpr) + (VExpr.forallN fields resultTarget) := by + simpa only [AddInductive.Context.freshExpr, + Expr.instantiate1_eq] using + bodyTr.inst_fvar typeEnvWF.ordered nextD3State.fullWF + have tailUnique : TrExprS.IsUnique + (body.instantiate1 context.freshExpr) := by + apply TrExprS.IsUnique.instantiate1 sourceUnique.2 + simp only [AddInductive.Context.freshExpr] + trivial + have nextFullNoBV : + VLCtx.bvars (((some (context.freshFVarId, + (consumeTypeAnnotations domain).fvarsList), + .vlam field) :: full) : VLCtx) = 0 := + nextD3State.fullExtension.bvars_eq.trans + nextD3State.commonNoBV + have tailClosed : Closed + (body.instantiate1 context.freshExpr) := by + have closed := bodyOpened.closed + rw [nextFullNoBV] at closed + exact closed + have freshExprEq : d2Context.freshExpr = + context.freshExpr := by + simp only [AddInductive.Context.freshExpr, freshEq] + have tailAlignment' : AddInductive.ConstructorViewAlignmentTrace + d2TailTrace (body.instantiate1 context.freshExpr) := by + rw [← freshExprEq] + exact tailAlignment + have tail₂' : AddInductive.ConstructorViewSemanticRun + typeEnv Us whnfFuel nextD2ContextRun d2TailTrace + (body.instantiate1 context.freshExpr) := by + rw [← freshExprEq] + exact tail₂ + have nextNgenEq : context.advanceFresh.ngen = + (d2Context.pushLocalDecl name₂ binderInfo₂ + (consumeTypeAnnotations rawDomain)).ngen := by + simp only [AddInductive.Context.advanceFresh, + AddInductive.Context.pushLocalDecl, ngenEq] + have freshFull : context.freshFVarId ∉ full.fvars := + (nextAnalyzerState'.fullWF.2.1 _ _ rfl).1 + have familyTargetNextEq : familyTarget.liftN 1 0 = + VExpr.forallN + (VExpr.liftTelN (fieldIndex + 1) checkedIndices 0) + (.sort level) := by + rw [familyTargetEq, VExpr.liftN_forallN, + VExpr.liftTelN_liftTelN] + simp only [VExpr.liftN] + have stageTail : ∀ q candidate, + fields[q]? = some candidate → + VInductDecl.stage3Field Us.length familyName + stats.params.size checkedIndices.length + (fieldIndex + 1 + q) candidate = true := by + intro q candidate member + have staged := stageFields (q + 1) candidate (by + simpa using member) + simpa only [Nat.add_assoc, Nat.add_comm 1 q] using staged + obtain ⟨tailFields, tailSpine⟩ := ih + (fieldIndex := fieldIndex + 1) tailAlignment' tail₂' + nextD3State nextAnalyzerState' + (removedInvariant.skip freshFull) nextNgenEq + familyCommonTr familyFullNext indexLength + familyTargetNextEq tailUnique tailClosed bodyOpened + (by omega) universeSemantics.2 stageTail + have headClassification : + VInductDecl.isRecField Us.length familyName + stats.params.size checkedIndices.length fieldIndex + field = true ∨ + (∃ descriptor, + VInductDecl.recArg? Us.length familyName + stats.params.size checkedIndices.length + fieldIndex field = some descriptor ∧ + descriptor.binders ≠ [] ∧ + descriptor.WF Us.length env level checkedIndices + full.toCtx) ∨ + VInductDecl.recArg? Us.length familyName + stats.params.size checkedIndices.length fieldIndex + field = none ∧ + ∃ fieldLevel, + env.HasType Us.length full.toCtx field + (.sort fieldLevel) ∧ + (level = .zero ∨ fieldLevel ≤ level) := by + by_cases nonempty : recursive.binders ≠ [] + · exact .inr (.inl ⟨recursive, recursiveEq, nonempty, + recursiveWF⟩) + · have empty : recursive.binders = [] := by + simpa using nonempty + exact .inl + (VInductDecl.recArg?_nil recursiveEq empty).1 + have headDirectSpine : + VInductDecl.isRecField Us.length familyName + stats.params.size checkedIndices.length fieldIndex + field = true → + env.SpineWF Us.length full.toCtx + (VExpr.forallN + (VExpr.liftTelN fieldIndex checkedIndices 0) + (.sort level)) + (VInductDecl.recFieldIdxs stats.params.size field) + (.sort level) := by + intro direct + have canonical := + VInductDecl.recArg?_of_isRecField direct + have descriptorEq : recursive = + { fieldIndex := fieldIndex, binders := [], + targetType := 0, + indices := VInductDecl.recFieldIdxs + stats.params.size field } := + Option.some.inj (recursiveEq.symm.trans canonical) + rw [descriptorEq] at recursiveWF + simpa [VInductDecl.RecArg.WF] using recursiveWF.2 + unfold ConstructorFieldsRunResult + refine ⟨⟨headClassification, headDirectSpine, ?_⟩, ?_⟩ + · simpa only [VLCtx.toCtx] using tailFields + · simpa only [List.reverse_cons, List.singleton_append, + List.append_assoc, VLCtx.toCtx, List.length_cons, + VExpr.liftN_liftN, Nat.add_comm] using tailSpine + | terminal sourceRun₂ viewRun₂ => + cases d2Alignment; simp_all [Expr.isForall] + | @terminal context source terminalArgIdx removed recursiveStarted valid + independent spineTrace contextRun expected spine => + have shape := isValidIndAppIdx_shape valid + have sourceHead : source.getAppFn = .const familyName familyLevels := by + rw [familyHead] at shape + change source.getAppFn.eqv (.const familyName familyLevels) = true ∧ _ + at shape + rw [Expr.eqv_eq] at shape + generalize headEq : source.getAppFn = head at shape + cases head <;> simp_all [Expr.eqv'] + cases fields with + | nil => + unfold ConstructorFieldsRunResult + refine ⟨trivial, ?_⟩ + simpa using terminal_exactAnalyzer + (valid := valid) (resultTarget := resultTarget) + spine henv typeEnvWF addType d3State + familyCommonTr familyFullTr familyUnique indexLength familyHead + sourceUnique sourceClosed + (constructorIndependentOf_fvars wholeTr.fvarsIn independent + removedInvariant) + wholeTr + | cons field fields => + cases wholeTr + all_goals simp_all [Expr.getAppFn] + +end ConstructorValidation + +namespace VInductDecl +open AddInductive TypeChecker VEnv + +theorem stagedIndexCount_eq + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {kernelSource : InductiveType} + {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us candidate source) + (normalization : NormalizationCandidateSemanticRun env Us candidate source) + (generation : GenerationChecked source) + (_analysis : normalization.root.normalization.generation? = + some generation) + (shape : normalization.generationShape = true) : + (input.postFamilyInput.universeInput.staged.family.validation.stats + ).nindices[0]! = generation.block.checked.indices.length := by + let validation := input.postFamilyInput.universeInput.staged.family.validation + have familyShape := shape + simp only [NormalizationCandidateSemanticRun.generationShape, + normalizationCandidateGenerationShape, Bool.and_eq_true, + beq_iff_eq] at familyShape + have spineLengthEq : + candidate.families.singleton.familyType.type.trace.spineLength = + (generation.block.checked.params ++ + generation.block.checked.indices).length := by + calc + _ = (VExpr.telN source.nparams normalization.raw.type ++ + ctorFields (VExpr.dropN source.nparams normalization.raw.type)).length := + familyShape.1.2 + _ = (generation.block.rawParams ++ + generation.block.rawIndices).length := by + simp only [NormalizedChecked.rawParams, NormalizedChecked.rawIndices, + NormalizationCandidateSemanticRun.root, + normalization.root.sourceType_eq generation] + _ = (generation.block.checked.params ++ + generation.block.checked.indices).length := by + simp only [List.length_append] + rw [generation.shape.2.1, generation.shape.2.2.1] + have parameterLength : generation.block.checked.params.length = + source.nparams := + generation.block.checked.direct_anatomy.2.1.trans + generation.block.nparams_eq.symm + change validation.stats.nindices[0]! = _ + rw [validation.stats_eq] + simp only [CandidateExprTrace.singletonCandidateInductiveStats] + rw [spineLengthEq, List.length_append, parameterLength, + input.postFamilyInput.universeInput.staged.validation_nparams_eq, + Nat.add_sub_cancel_left] + rfl + +theorem stagedResultLevel_tr + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {kernelSource : InductiveType} + {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us candidate source) + (normalization : NormalizationCandidateSemanticRun env Us candidate source) + (generation : GenerationChecked source) + (analysis : normalization.root.normalization.generation? = + some generation) + (shape : normalization.generationShape = true) : + VLevel.ofLevel Us + input.postFamilyInput.universeInput.staged.family.validation.stats.resultLevel = + some generation.block.checked.resultLevel := by + let validation := input.postFamilyInput.universeInput.staged.family.validation + have familyShape := shape + simp only [NormalizationCandidateSemanticRun.generationShape, + normalizationCandidateGenerationShape, Bool.and_eq_true, + beq_iff_eq] at familyShape + have spineLengthEq : + candidate.families.singleton.familyType.type.trace.spineLength = + (generation.block.checked.params ++ + generation.block.checked.indices).length := by + calc + _ = (VExpr.telN source.nparams normalization.raw.type ++ + ctorFields (VExpr.dropN source.nparams normalization.raw.type)).length := + familyShape.1.2 + _ = (generation.block.rawParams ++ + generation.block.rawIndices).length := by + simp only [NormalizedChecked.rawParams, NormalizedChecked.rawIndices, + NormalizationCandidateSemanticRun.root, + normalization.root.sourceType_eq generation] + _ = (generation.block.checked.params ++ + generation.block.checked.indices).length := by + simp only [List.length_append] + rw [generation.shape.2.1, generation.shape.2.2.1] + have viewEq : normalization.family.type.view = + VExpr.forallN + (generation.block.checked.params ++ generation.block.checked.indices) + (.sort generation.block.checked.resultLevel) := by + have analyzerEq : normalization.family.type.view = + generation.block.checked.type.type := + (congrArg (fun ty : VInductiveType => ty.type) + (normalization.root.familyViewType_eq analysis)).symm + rw [analyzerEq, generation.block.checked.type_eq, + VExpr.forallN_append] + obtain ⟨_, recursive⟩ := normalization.family.type.recursive + have levelTr := ConstructorValidation.CandidateExprRun.terminalLevel_tr recursive + validation.terminal_eq viewEq spineLengthEq + change VLevel.ofLevel Us validation.stats.resultLevel = _ + rw [validation.stats_eq] + simpa only [CandidateExprTrace.singletonCandidateInductiveStats] using levelTr + +end VInductDecl + +namespace VInductDecl +open AddInductive TypeChecker VEnv +open ConstructorValidation + +/-- Derive the checked field and result-spine obligations for one exact +analyzer-owned constructor position after consuming its parameter prefix. -/ +theorem CandidateSemanticNormalizedCtorRun.checkedConstructorWF + {env typeEnv : VEnv} {Us : List Name} + {source : VInductDecl} {generation : GenerationChecked source} + {kernelCtor : Constructor} + {candidateCtor : AddInductive.CandidateConstructor kernelCtor} + {rawCtor : VConstVal} + {root : CandidateConstructorSemanticRun typeEnv Us candidateCtor rawCtor} + {ctor : NormalizedCtor} + {stats : InductiveStats} {familyIndices : Expr} + {d3Context d2Context : AddInductive.Context} + {d3ContextRun : AddInductive.ConstructorContextRun env Us d3Context} + {d2ContextRun : AddInductive.ConstructorContextRun typeEnv Us d2Context} + {d3Trace : AddInductive.ConstructorPreFamilyViewTrace stats 0 + familyIndices d3Context candidateCtor.type.view 0 [] false} + (d3 : AddInductive.ConstructorPreFamilyViewSemanticRun env Us stats 0 + familyIndices d3ContextRun d3Trace) + {d2Trace : AddInductive.ConstructorTypeValidationTrace stats false 0 + kernelCtor.name d2Context kernelCtor.type 0 d2Context.fuel.inductiveFuel} + (d2Alignment : AddInductive.ConstructorViewAlignmentTrace d2Trace + candidateCtor.type.view) + (d2 : AddInductive.ConstructorViewSemanticRun typeEnv Us + root.type.whnfFuel d2ContextRun d2Trace candidateCtor.type.view) + (genRun : CandidateSemanticNormalizedCtorRun generation.block typeEnv Us + root ctor) + (hctor : ctor ∈ generation.block.ctorPairs) + (henv : VEnv.WF env) (typeEnvWF : VEnv.WF typeEnv) + (addType : env ≤ typeEnv) + {parameterΔ d3ViewContext analyzerViewContext : VLCtx} + {viewLift analyzerViewLift : Lift} + {parameters : List Expr} + (parameterContext : CandidateParameterContext [] parameters + generation.block.checked.params parameterΔ) + (parameterWF : VLCtx.WF env Us.length parameterΔ) + (parameterCtx : parameterΔ.toCtx = + generation.block.checked.params.reverse) + (d3State : D3FullContextState env typeEnv Us d3Context + parameterΔ parameterΔ d3ContextRun.candidate.context.vlctx + d3ViewContext .refl viewLift) + (analyzerState : AnalyzerPostContextState typeEnv Us parameterΔ + d2ContextRun.candidate.context.vlctx analyzerViewContext + analyzerViewLift) + (ngenEq : d3Context.ngen = d2Context.ngen) + (parametersEq : stats.params.toList = parameters) + {familyName : Name} {familyLevels : List Level} + (indConsts : stats.indConsts = #[.const familyName familyLevels]) + (familyTr : TrExprS env Us parameterΔ familyIndices + (VExpr.forallN generation.block.checked.indices + (.sort generation.block.checked.resultLevel))) + (familyUnique : TrExprS.IsUnique familyIndices) + (indexLength : generation.block.checked.indices.length = + stats.nindices[0]!) + (resultLevelTr : VLevel.ofLevel Us stats.resultLevel = + some generation.block.checked.resultLevel) + (absent : env.constants familyName = none) + (unique : CandidateExprTraceViewIsUnique candidateCtor.type.trace) + (universeSemantics : d2Trace.universeSemantics = true) + (stageFields : ∀ q field, ctor.view.fields[q]? = some field → + VInductDecl.stage3Field Us.length familyName stats.params.size + generation.block.checked.indices.length q field = true) : + VInductDecl.fieldsWF Us.length familyName stats.params.size env + generation.block.checked.resultLevel + generation.block.checked.indices + generation.block.checked.params.reverse 0 ctor.view.fields ∧ + env.SpineWF Us.length + (ctor.view.fields.reverse ++ + generation.block.checked.params.reverse) + (VExpr.forallN + (VExpr.liftTelN ctor.view.fields.length + generation.block.checked.indices 0) + (.sort generation.block.checked.resultLevel)) + (VInductDecl.recFieldIdxs stats.params.size + (ctor.resultTarget generation.block)) + (.sort generation.block.checked.resultLevel) := by + obtain ⟨rest, instantiation, ⟨d3Suffix⟩, wholeTr⟩ := + genRun.preFamilySuffix addType parameterContext parameterWF unique d3 + hctor parametersEq indConsts + obtain ⟨d2Suffix, suffixUniverse⟩ := + d2.afterParameters d2Alignment (by omega) instantiation + have instantiation' : instantiateFamilyParameters candidateCtor.type.view + parameters = .ok rest := by + rw [← parametersEq] + exact instantiation + have sourceUnique : TrExprS.IsUnique rest := + instantiateFamilyParameters_unique unique.view + (ConstructorValidation.CandidateParameterContext.parametersUnique + parameterContext) instantiation' + have sourceClosed : Closed rest := by + have closed := wholeTr.closed + rw [show parameterΔ.bvars = 0 from by + simpa only [VLCtx.NoBV] using d3State.commonNoBV] at closed + exact closed + have resultNotForall : ∀ domain body, + ctor.resultTarget generation.block ≠ .forallE domain body := by + intro domain body equality + have headEq := congrArg VExpr.appHead equality + simp only [NormalizedCtor.resultTarget, VExpr.appHead_appN, + VExpr.appHead] at headEq + exact VExpr.noConfusion headEq + have familyTargetEq : + VExpr.forallN generation.block.checked.indices + (.sort generation.block.checked.resultLevel) = + VExpr.forallN + (VExpr.liftTelN 0 generation.block.checked.indices 0) + (.sort generation.block.checked.resultLevel) := by + have liftTelNZero : ∀ (indices : List VExpr) (cutoff : Nat), + VExpr.liftTelN 0 indices cutoff = indices := by + intro indices cutoff + induction indices generalizing cutoff with + | nil => rfl + | cons index indices ih => + simp only [VExpr.liftTelN, VExpr.liftN_zero] + rw [ih] + rw [liftTelNZero] + have removedInvariant : FullRemovedInvariant parameterΔ parameterΔ + [] := by + intro fv member _ + exact member + have familyHead : stats.indConsts[0]! = + .const familyName familyLevels := by + rw [indConsts] + rfl + have suffixUniverseSemantics : + d2Suffix.trace.universeSemantics = true := + suffixUniverse.trans universeSemantics + have result := constructorFields_exactAnalyzer + (d3 := d3Suffix.semantic) + (d2Alignment := d2Suffix.alignment) (d2 := d2Suffix.semantic) + henv typeEnvWF addType d3State analyzerState + removedInvariant ngenEq + familyTr (familyTr.mono addType) familyUnique indexLength + familyHead + familyTargetEq resultNotForall resultLevelTr absent sourceUnique + sourceClosed wholeTr (by omega) suffixUniverseSemantics (by + intro q field atIndex + simpa only [Nat.zero_add] using stageFields q field atIndex) + unfold ConstructorFieldsRunResult at result + simpa only [parameterCtx, VExpr.liftN_forallN, VExpr.liftN, + Nat.zero_add] using result + +/-- Traverse the exact source-indexed D3, D2, and analyzer lists in lockstep. +The dependent indices rule out truncation, reordering, or reuse of evidence +from a different constructor position. -/ +theorem CandidateSemanticNormalizedCtorListRun.checkedConstructorsWF + {env typeEnv : VEnv} {Us : List Name} + {source : VInductDecl} {generation : GenerationChecked source} + {stats : InductiveStats} {familyIndices : Expr} + {d3Context d2Context : AddInductive.Context} + {d3ContextRun : AddInductive.ConstructorContextRun env Us d3Context} + {d2ContextRun : AddInductive.ConstructorContextRun typeEnv Us d2Context} + {kernelCtors : List Constructor} + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor kernelCtors} + {d3Trace : AddInductive.ConstructorPreFamilyListTrace stats 0 + familyIndices d3Context candidates} + (d3 : AddInductive.ConstructorPreFamilyListSemanticRun env Us stats 0 + familyIndices d3Context d3ContextRun d3Trace) + {seen : NameSet} + {d2Trace : AddInductive.ConstructorListValidationTrace stats false 0 + d2Context seen kernelCtors} + {alignment : AddInductive.ConstructorCandidateAlignmentTrace stats false 0 + d2Context d2Trace candidates} + {raws : List VConstVal} + {roots : CandidateConstructorSemanticListRun typeEnv Us candidates raws} + (d2 : AddInductive.ConstructorPostFamilySemanticListRun typeEnv Us stats + false 0 d2Context d2ContextRun d2Trace candidates alignment roots) + {ctors : List NormalizedCtor} + (generationRuns : CandidateSemanticNormalizedCtorListRun generation.block + typeEnv Us roots ctors) + (membership : ∀ ctor ∈ ctors, + ctor ∈ generation.block.ctorPairs) + (henv : VEnv.WF env) (typeEnvWF : VEnv.WF typeEnv) + (addType : env ≤ typeEnv) + {parameterΔ d3ViewContext analyzerViewContext : VLCtx} + {viewLift analyzerViewLift : Lift} + {parameters : List Expr} + (parameterContext : CandidateParameterContext [] parameters + generation.block.checked.params parameterΔ) + (parameterWF : VLCtx.WF env Us.length parameterΔ) + (parameterCtx : parameterΔ.toCtx = + generation.block.checked.params.reverse) + (d3State : D3FullContextState env typeEnv Us d3Context + parameterΔ parameterΔ d3ContextRun.candidate.context.vlctx + d3ViewContext .refl viewLift) + (analyzerState : AnalyzerPostContextState typeEnv Us parameterΔ + d2ContextRun.candidate.context.vlctx analyzerViewContext + analyzerViewLift) + (ngenEq : d3Context.ngen = d2Context.ngen) + (parametersEq : stats.params.toList = parameters) + {familyName : Name} {familyLevels : List Level} + (indConsts : stats.indConsts = #[.const familyName familyLevels]) + (familyTr : TrExprS env Us parameterΔ familyIndices + (VExpr.forallN generation.block.checked.indices + (.sort generation.block.checked.resultLevel))) + (familyUnique : TrExprS.IsUnique familyIndices) + (indexLength : generation.block.checked.indices.length = + stats.nindices[0]!) + (resultLevelTr : VLevel.ofLevel Us stats.resultLevel = + some generation.block.checked.resultLevel) + (absent : env.constants familyName = none) + (unique : candidates.ViewTranslationUnique) + (universeSemantics : d2Trace.universeSemantics = true) + (uvarsEq : source.uvars = Us.length) + (familyNameEq : familyName = generation.block.checked.type.name) + (paramsSizeEq : stats.params.size = source.nparams) : + ∀ ctor ∈ ctors, + VInductDecl.fieldsWF Us.length familyName stats.params.size env + generation.block.checked.resultLevel + generation.block.checked.indices + generation.block.checked.params.reverse 0 ctor.view.fields ∧ + env.SpineWF Us.length + (ctor.view.fields.reverse ++ + generation.block.checked.params.reverse) + (VExpr.forallN + (VExpr.liftTelN ctor.view.fields.length + generation.block.checked.indices 0) + (.sort generation.block.checked.resultLevel)) + (VInductDecl.recFieldIdxs stats.params.size + (ctor.resultTarget generation.block)) + (.sort generation.block.checked.resultLevel) := by + induction d3 generalizing seen raws ctors with + | nil => + cases d2 + cases generationRuns + intro ctor member + simp at member + | cons d3Head d3Tail ih => + cases alignment with + | cons rootScope storedSpine spineLength candidateDepth headAlignment + tailAlignment => + cases d2 with + | cons root d2Head spine d2Tail => + cases generationRuns with + | cons generationHead generationTail => + simp only [AddInductive.ConstructorListValidationTrace.universeSemantics, + Bool.and_eq_true] at universeSemantics + intro ctor member + simp only [List.mem_cons] at member + rcases member with rfl | member + · have hctor : ctor ∈ generation.block.ctorPairs := + membership ctor (.head _) + obtain ⟨directCtor, directMember, viewEq⟩ := + generation.viewCtor_ofDirect hctor + have directStage := + (generation.block.checked.direct_anatomy.2.2.2.2.2 + directCtor directMember).2.2 + rw [← generation.block.uvars_eq, + ← generation.block.nparams_eq, + uvarsEq, ← familyNameEq, ← paramsSizeEq] at directStage + have viewEq' : ctor.view = + CheckedCtor.ofDirect Us.length familyName stats.params.size + generation.block.checked.indices.length directCtor := by + simpa only [uvarsEq, paramsSizeEq, + generation.block.sourceType_name_eq, familyNameEq] using viewEq + have stageFields : ∀ q field, + ctor.view.fields[q]? = some field → + VInductDecl.stage3Field Us.length familyName + stats.params.size + generation.block.checked.indices.length q field = true := by + intro q field atIndex + rw [viewEq'] at atIndex + simpa only [Nat.zero_add] using + (VInductDecl.stage3Ctor_eq directStage).2.2.2 q field + (by simpa only [CheckedCtor.ofDirect] using atIndex) + exact generationHead.checkedConstructorWF d3Head + headAlignment d2Head hctor henv typeEnvWF addType + parameterContext parameterWF parameterCtx d3State analyzerState + ngenEq parametersEq indConsts familyTr + familyUnique indexLength resultLevelTr absent unique.1 + universeSemantics.1 stageFields + · exact ih d2Tail generationTail + (fun ctor member => membership ctor (.tail _ member)) unique.2 + universeSemantics.2 ctor member + +/-- The retained D1/D2/D3 producer traces derive the exact analyzer-owned +checked declaration semantics. -/ +theorem StagedNormalizationCandidatePreFamilyInput.checkedWF + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {kernelSource : InductiveType} + {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us candidate source) + (normalization : NormalizationCandidateSemanticRun env Us candidate source) + (generation : GenerationChecked source) + (analysis : normalization.root.normalization.generation? = + some generation) + (shape : normalization.generationShape = true) : + generation.block.checked.WF env := by + let staged := input.postFamilyInput.universeInput.staged + let validation := staged.family.validation + obtain ⟨parameterΔ, terminalRun, viewTerminal, familyInstantiation, + familyTr, parameterCtx, parameterContext, parameterFVars, + parameterNoBV, parameterWF, terminalVenv, terminalLparams, + terminalViewDefEq, terminalViewUnique, terminalViewLift, + terminalViewEq⟩ := + input.familyContext normalization generation analysis shape + have henv : VEnv.WF env := by + simpa only [normalization.family.type.venv_eq] using + normalization.family.type.contextRun.context.Ewf + have rawWF : normalization.raw.toVConstant.WF env := by + show env.IsType normalization.raw.uvars [] normalization.raw.type + simpa only [normalization.family.uvars_eq] using + normalization.family.type.source_isType_of_terminalSort + validation.terminal_eq + have typeEnvWF : VEnv.WF normalization.family.typeEnv := by + obtain ⟨declarations, declarationsWF⟩ := henv + exact ⟨.axiom normalization.raw.toVConstVal :: declarations, + .decl (.axiom rawWF normalization.family.addType) declarationsWF⟩ + have rawEq : normalization.raw = staged.raw := by + have singletonEq : [normalization.raw] = [staged.raw] := + normalization.raw_types_eq.symm.trans staged.raw_types_eq + injection singletonEq + have typeEnvEq : normalization.family.typeEnv = staged.family.typeEnv := by + exact Option.some.inj <| normalization.family.addType.symm.trans (by + simpa only [rawEq] using staged.family.addInduct.env_add) + obtain ⟨validationRun, validationVenv, validationLparams, + validationVlctx⟩ := + staged.family.validationContextRunFromPre terminalRun terminalVenv + terminalLparams + let d3ContextRun : AddInductive.ConstructorContextRun env Us + candidate.families.singleton.familyType.type.trace.terminalContext := + ⟨terminalRun, terminalVenv, terminalLparams⟩ + let d2ContextRun : AddInductive.ConstructorContextRun + normalization.family.typeEnv Us + { candidate.families.singleton.familyType.type.trace.terminalContext with + env := constructorContext.env } := + ⟨validationRun, validationVenv.trans typeEnvEq.symm, + validationLparams⟩ + obtain ⟨d3Constructors⟩ := + AddInductive.ConstructorPreFamilyListSemanticRun.nonempty d3ContextRun + input.safety.constructors + obtain ⟨d2Constructors⟩ := + AddInductive.ConstructorPostFamilySemanticListRun.nonempty_of_alignment + d2ContextRun input.postFamilyInput.alignment + normalization.family.constructors + let generationRuns := normalization.constructorGenerationRuns generation + analysis shape + have terminalWF : VLCtx.WF env Us.length + terminalRun.context.vlctx := by + simpa only [terminalVenv, terminalLparams] using terminalRun.context.Δwf + have validationWF : VLCtx.WF normalization.family.typeEnv Us.length + validationRun.context.vlctx := by + simpa only [validationVenv, ← typeEnvEq, validationLparams] using + validationRun.context.Δwf + have viewTerminalWF : VLCtx.WF env Us.length viewTerminal := + (terminalViewDefEq.symm henv.ordered).wf + have d3State : D3FullContextState env normalization.family.typeEnv Us + candidate.families.singleton.familyType.type.trace.terminalContext + parameterΔ parameterΔ terminalRun.context.vlctx viewTerminal .refl + (.skipN .refl generation.block.checked.indices.length) := { + commonWF := parameterWF + commonNoBV := parameterNoBV + fullWF := parameterWF.mono + (VEnv.addConst_le normalization.family.addType) + fullExtension := .refl + actualWF := terminalWF + viewDefEq := terminalViewDefEq + viewUnique := terminalViewUnique + viewExtension := terminalViewLift + freshInvariant := fun _ present => .inl present } + have analyzerState : AnalyzerPostContextState + normalization.family.typeEnv Us parameterΔ validationRun.context.vlctx + viewTerminal (.skipN .refl + generation.block.checked.indices.length) := { + fullWF := parameterWF.mono + (VEnv.addConst_le normalization.family.addType) + postWF := validationWF + viewWF := viewTerminalWF.mono + (VEnv.addConst_le normalization.family.addType) + viewDefEq := by + rw [validationVlctx] + exact (terminalViewDefEq.mono + (VEnv.addConst_le normalization.family.addType)).toFVars + viewExtension := terminalViewLift } + have parametersEq : validation.stats.params.toList = + candidate.families.singleton.familyType.type.trace.parameterList + source.nparams := by + rw [validation.stats_eq] + simp only [CandidateExprTrace.singletonCandidateInductiveStats, + validation, staged, staged.validation_nparams_eq] + have paramsSizeEq : validation.stats.params.size = source.nparams := by + calc + validation.stats.params.size = + validation.stats.params.toList.length := by simp + _ = (candidate.families.singleton.familyType.type.trace.parameterList + source.nparams).length := congrArg List.length parametersEq + _ = generation.block.checked.params.length := + parameterContext.length_eq + _ = source.nparams := + generation.block.checked.direct_anatomy.2.1.trans + generation.block.nparams_eq.symm + have sourceTypeEq : generation.block.sourceType = normalization.raw := by + simpa only [NormalizationCandidateSemanticRun.root] using + normalization.root.sourceType_eq generation + have familyNameEq : generation.block.sourceType.name = + generation.block.checked.type.name := + generation.block.sourceType_name_eq + have familyLparams : + candidate.families.singleton.familyType.type.context.lparams = Us := by + exact normalization.family.type.contextRun.context_lparams.symm.trans + normalization.family.type.lparams_eq + have indConsts : validation.stats.indConsts = + #[.const generation.block.sourceType.name (Us.map .param)] := by + rw [validation.stats_eq] + simp only [CandidateExprTrace.singletonCandidateInductiveStats, + familyLparams] + congr 2 + exact congrArg (fun name => Expr.const name (Us.map .param)) <| + staged.family.name_eq.trans (congrArg + (fun type : VInductiveType => type.name) + (rawEq.symm.trans sourceTypeEq.symm)) + have familyUnique : TrExprS.IsUnique input.safety.familyIndices := + instantiateFamilyParameters_unique + input.safety.familyTranslationUnique + (ConstructorValidation.CandidateParameterContext.parametersUnique + parameterContext) familyInstantiation + have indexLength : generation.block.checked.indices.length = + validation.stats.nindices[0]! := + (stagedIndexCount_eq input normalization generation analysis + shape).symm + have resultLevelTr : VLevel.ofLevel Us validation.stats.resultLevel = + some generation.block.checked.resultLevel := + stagedResultLevel_tr input normalization generation analysis shape + have absent : env.constants generation.block.sourceType.name = none := by + rw [sourceTypeEq] + exact VEnv.addConst_fresh normalization.family.addType + have pairWF := generationRuns.checkedConstructorsWF + d3Constructors d2Constructors (fun _ member => member) henv typeEnvWF + (VEnv.addConst_le normalization.family.addType) parameterContext + parameterWF parameterCtx d3State analyzerState rfl + parametersEq indConsts familyTr familyUnique indexLength resultLevelTr + absent input.safety.constructorTranslationUnique + input.postFamilyInput.universeInput.universeSemantics + normalization.uvars_eq familyNameEq paramsSizeEq + refine ⟨by + simpa only [generation.block.uvars_eq] using + input.familyOnTel normalization generation analysis, ?_⟩ + intro ctor ctorMember + have directMember : + CheckedCtor.ofDirect generation.block.normalization.view.uvars + generation.block.checked.type.name + generation.block.normalization.view.nparams + generation.block.checked.indices.length ctor ∈ + generation.block.checked.constructors := by + rw [generation.block.checked.constructors_eq] + exact List.mem_map.2 ⟨ctor, ctorMember, rfl⟩ + rw [← generation.viewCtors_eq] at directMember + obtain ⟨pair, pairMember, pairViewEq⟩ := List.mem_map.1 directMember + have result := pairWF pair pairMember + rw [pairViewEq] at result + have viewUvarsEq : generation.block.normalization.view.uvars = Us.length := + generation.block.uvars_eq.symm.trans normalization.uvars_eq + have dropBvarRevRange : ∀ (offset count : Nat) (suffix : List VExpr), + (VExpr.bvarRevRange offset count ++ suffix).drop count = suffix := by + intro offset count suffix + induction count with + | zero => rfl + | succ count ih => + simp only [VExpr.bvarRevRange, List.cons_append, + List.drop_succ_cons] + exact ih + rw [← viewUvarsEq, paramsSizeEq, generation.block.nparams_eq, + familyNameEq] at result + simpa only [ + generation.block.nparams_eq, pairViewEq, + CheckedCtor.ofDirect, NormalizedCtor.resultTarget, + VInductDecl.recFieldIdxs, VExpr.appArgs_appN, VExpr.appArgs, + List.append_nil, dropBvarRevRange] using result + +/-- Re-index the derived checked semantics onto the exact normalization view +selected by dependent analysis. -/ +theorem StagedNormalizationCandidatePreFamilyInput.viewDeclWF + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {kernelSource : InductiveType} + {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us candidate source) + (normalization : NormalizationCandidateSemanticRun env Us candidate source) + (generation : GenerationChecked source) + (analysis : normalization.root.normalization.generation? = + some generation) + (shape : normalization.generationShape = true) : + normalization.root.viewDecl.WF env := by + have checkedWF := input.checkedWF normalization generation analysis shape + have normalizationEq : generation.block.normalization = + normalization.root.normalization := + Normalization.generation?_normalization analysis + have viewEq : generation.block.normalization.view = + normalization.root.viewDecl := by + simpa only [NormalizationCandidateRun.normalization] using + congrArg (fun normalized : Normalization source => normalized.view) + normalizationEq + rw [← viewEq] + exact generation.block.checked.to_declWF generation.block.checked_eq + checkedWF + +/-- Build the semantic generation owner from the retained D1/D2/D3 input, +exact dependent analysis, and the single executable hierarchy shape check. +The analyzer-owned checked semantics are derived from those traces rather +than accepted as a premise. -/ +def GenerationCandidateSemanticRun.ofGenerationShape + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us candidate source) + (normalization : NormalizationCandidateSemanticRun env Us candidate source) + (generation : GenerationChecked source) + (analysis : normalization.root.normalization.generation? = + some generation) + (shape : normalization.generationShape = true) : + GenerationCandidateSemanticRun normalization generation := by + have familyShape := shape + simp only [NormalizationCandidateSemanticRun.generationShape, + normalizationCandidateGenerationShape, Bool.and_eq_true, + beq_iff_eq] at familyShape + have sourceTypeEq : generation.block.sourceType = normalization.raw := by + simpa only [NormalizationCandidateSemanticRun.root] using + normalization.root.sourceType_eq generation + apply GenerationCandidateSemanticShapeRun.run { + analysis := analysis + checked := input.checkedWF normalization generation analysis shape + family := { + storedSpine := familyShape.1.1 + spineLength_eq := by + simpa only [NormalizedChecked.rawParams, + NormalizedChecked.rawIndices, sourceTypeEq] using familyShape.1.2 } + constructors := + CandidateConstructorSemanticGenerationShapeList.ofCheck + normalization.family.constructors familyShape.2 } + +/-- Construct the produced package at the consolidated generation-shape +boundary without accepting a checked- or view-WF premise. -/ +def NormalizationCandidateSemanticRun.producedPackageOfGenerationShape + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (normalization : NormalizationCandidateSemanticRun env Us candidate source) + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us candidate source) + (generation : GenerationChecked source) + (analysis : normalization.root.normalization.generation? = + some generation) + (shape : normalization.generationShape = true) + (context : AddInductive.Context) + (nparams numNested : Nat) (isUnsafe : Bool) + (produced : + AddInductive.buildNormalizationCandidate nparams + [kernelSource] numNested isUnsafe context = .ok candidate) : + ProducedGenerationCandidatePackage env Us := + (GenerationCandidateSemanticRun.ofGenerationShape input normalization + generation analysis shape).producedPackage context nparams numNested + isUnsafe produced + +/-- Interpret one successful shape-producing outer result using the matching +retained D1/D2/D3 semantic input. -/ +def ProducedGenerationShapeCandidate.producedPackage + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {raw : VInductiveType} {numNested : Nat} {isUnsafe : Bool} + {context : AddInductive.Context} + (producedCandidate : ProducedGenerationShapeCandidate source raw + kernelSource numNested isUnsafe context) + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us producedCandidate.candidate source) + (normalization : NormalizationCandidateSemanticRun env Us + producedCandidate.candidate source) + (rawEq : raw = normalization.raw) + (generation : GenerationChecked source) + (analysis : normalization.root.normalization.generation? = + some generation) : + ProducedGenerationCandidatePackage env Us := + normalization.producedPackageOfGenerationShape input generation analysis + (by + simpa only [NormalizationCandidateSemanticRun.generationShape, + rawEq] using producedCandidate.shape) + context source.nparams numNested isUnsafe producedCandidate.produced + +/-- Projection-free D3 candidates determine one exact Theory constructor +view at every source position, independently of the existential checker run +that selected it. -/ +theorem CandidateConstructorSemanticListRun.roots_views_eq + {env : VEnv} {Us : List Name} {constructors : List Constructor} + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor constructors} + {raws : List VConstVal} + (unique : candidates.ViewTranslationUnique) + (left right : CandidateConstructorSemanticListRun env Us candidates raws) : + left.roots.views = right.roots.views := by + induction left with + | nil => + cases right + rfl + | @cons constructor constructors candidate candidates raw raws head tail ih => + cases right with + | cons otherHead otherTail => + obtain ⟨_, headRecursive⟩ := head.type.recursive + obtain ⟨_, otherHeadRecursive⟩ := otherHead.type.recursive + have headViewEq : head.type.view = otherHead.type.view := + TrExprS.unique unique.1.view + (headRecursive.view_tr_strict unique.1) + (otherHeadRecursive.view_tr_strict unique.1) + have tailViewsEq := ih unique.2 otherTail + simp only [CandidateConstructorSemanticListRun.roots, + CandidateConstructorListRun.views, + CandidateConstructorSemanticRun.root, + CandidateConstructorRun.view] + rw [headViewEq, tailViewsEq] + +/-- Transport the positional uniqueness theorem across the uniquely produced +post-family environment and raw constructor list. -/ +theorem CandidateConstructorSemanticListRun.roots_views_eq_of_eq + {leftEnv rightEnv : VEnv} {Us : List Name} + {constructors : List Constructor} + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor constructors} + {leftRaws rightRaws : List VConstVal} + (unique : candidates.ViewTranslationUnique) + (envEq : leftEnv = rightEnv) (rawsEq : leftRaws = rightRaws) + (left : CandidateConstructorSemanticListRun leftEnv Us candidates leftRaws) + (right : CandidateConstructorSemanticListRun rightEnv Us candidates + rightRaws) : + left.roots.views = right.roots.views := by + subst rightEnv + subst rightRaws + exact left.roots_views_eq unique right + +private theorem candidateFamilyView_eq_of_components + {rawLeft rawRight : VInductiveType} + {typeLeft typeRight : VExpr} + {ctorsLeft ctorsRight : List VConstVal} + (rawEq : rawLeft = rawRight) (typeEq : typeLeft = typeRight) + (ctorsEq : ctorsLeft = ctorsRight) : + ({ rawLeft with type := typeLeft, ctors := ctorsLeft } : + VInductiveType) = + { rawRight with type := typeRight, ctors := ctorsRight } := by + cases rawEq + cases typeEq + cases ctorsEq + rfl + +private theorem Normalization.eq_of_view_eq + {source : VInductDecl} {left right : Normalization source} + (viewEq : left.view = right.view) : left = right := by + cases left with + | mk leftView leftShape => + cases right with + | mk rightView rightShape => + cases viewEq + rfl + +/-- The D3 projection-free safety gate makes the complete normalization +selected by the staged semantic owner syntactically unique. In particular, +fixtures may state dependent analysis once for the known normalization and +transport it to whichever semantic hierarchy `input.exists` selects. -/ +theorem StagedNormalizationCandidatePreFamilyInput.normalization_eq + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us candidate source) + (left right : NormalizationCandidateSemanticRun env Us candidate source) : + left.root.normalization = right.root.normalization := by + have rawEq : left.raw = right.raw := by + have singletonEq := left.raw_types_eq.symm.trans right.raw_types_eq + injection singletonEq + have typeEnvEq : left.family.typeEnv = right.family.typeEnv := by + exact Option.some.inj <| left.family.addType.symm.trans <| by + simpa only [rawEq] using right.family.addType + have familyViewEq : left.family.type.view = right.family.type.view := by + obtain ⟨_, leftRecursive⟩ := left.family.type.recursive + obtain ⟨_, rightRecursive⟩ := right.family.type.recursive + exact TrExprS.unique input.safety.familyTranslationUnique + (leftRecursive.view_tr_strict <| + AddInductive.CandidateExprTrace.viewTranslationUnique_sound _ <| by + rw [AddInductive.CandidateExprTrace.viewTranslationUnique_eq] + have gate := input.safety.translationUnique + simp only [Bool.and_eq_true] at gate + exact gate.1) + (rightRecursive.view_tr_strict <| + AddInductive.CandidateExprTrace.viewTranslationUnique_sound _ <| by + rw [AddInductive.CandidateExprTrace.viewTranslationUnique_eq] + have gate := input.safety.translationUnique + simp only [Bool.and_eq_true] at gate + exact gate.1) + have constructorViewsEq : left.family.constructors.roots.views = + right.family.constructors.roots.views := by + exact left.family.constructors.roots_views_eq_of_eq + input.safety.constructorTranslationUnique typeEnvEq + (congrArg VInductiveType.ctors rawEq) right.family.constructors + have familyEq : left.family.root.view = right.family.root.view := by + exact candidateFamilyView_eq_of_components rawEq familyViewEq + constructorViewsEq + have viewDeclEq : left.root.viewDecl = right.root.viewDecl := by + simp only [NormalizationCandidateSemanticRun.root, + NormalizationCandidateRun.viewDecl] + rw [familyEq] + exact Normalization.eq_of_view_eq viewDeclEq + +/-- Choice-free constructor-root interpretation: the semantic root's view is +computed by the deterministic translator under the constructor's strict-view +uniqueness certificate. -/ +def CandidateConstructorSemanticInput.semanticOfUnique + {env : VEnv} {Us : List Name} {source : Constructor} + {candidate : AddInductive.CandidateConstructor source} {raw : VConstVal} + (input : CandidateConstructorSemanticInput env Us candidate raw) + (unique : TypeChecker.CandidateExprTraceViewIsUnique + candidate.type.trace) : + CandidateConstructorSemanticRun env Us candidate raw where + name_eq := input.name_eq + uvars_eq := input.uvars_eq + type := input.type.semanticOfUnique unique + +/-- Choice-free source-ordered interpretation of a complete constructor list +under its source-ordered strict-view certificate. -/ +def CandidateConstructorSemanticListInput.semanticOfUnique + {env : VEnv} {Us : List Name} : + {sources : List Constructor} → + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor sources} → + {raws : List VConstVal} → + CandidateConstructorSemanticListInput env Us candidates raws → + candidates.ViewTranslationUnique → + CandidateConstructorSemanticListRun env Us candidates raws + | _, _, _, .nil, _ => .nil + | _, _, _, .cons head tail, unique => + .cons (head.semanticOfUnique unique.1) (tail.semanticOfUnique unique.2) + +/-- Choice-free family interpretation: the family type and every +post-insertion constructor view are computed by the deterministic +translator. -/ +def CandidateFamilySemanticInput.semanticOfUnique + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.CandidateFamily source} {raw : VInductiveType} + (input : CandidateFamilySemanticInput env Us candidate raw) + (uniqueType : TypeChecker.CandidateExprTraceViewIsUnique + candidate.familyType.type.trace) + (uniqueCtors : candidate.constructors.ViewTranslationUnique) : + CandidateFamilySemanticRun env Us candidate raw where + name_eq := input.name_eq + uvars_eq := input.uvars_eq + type := input.type.semanticOfUnique uniqueType + typeEnv := input.typeEnv + addType := input.addType + constructors := input.constructors.semanticOfUnique uniqueCtors + +/-- Choice-free singleton semantic hierarchy: every normalized Theory view +in the family and constructor list is computed by the deterministic +translator, with `Nonempty` interpretation transferred onto the computed +values. -/ +def NormalizationCandidateSemanticInput.semanticOfUnique + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (input : NormalizationCandidateSemanticInput env Us candidate rawDecl) + (uniqueType : TypeChecker.CandidateExprTraceViewIsUnique + candidate.families.singleton.familyType.type.trace) + (uniqueCtors : + candidate.families.singleton.constructors.ViewTranslationUnique) : + NormalizationCandidateSemanticRun env Us candidate rawDecl where + raw := input.raw + raw_types_eq := input.raw_types_eq + uvars_eq := input.uvars_eq + family := input.family.semanticOfUnique uniqueType uniqueCtors + +/-- The executable D3 gate's uniqueness Bool supplies the family strict-view +certificate consumed by the choice-free semantic assembly. -/ +theorem StagedNormalizationCandidatePreFamilyInput.familyViewUnique + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us candidate rawDecl) : + TypeChecker.CandidateExprTraceViewIsUnique + candidate.families.singleton.familyType.type.trace := by + have h := input.safety.translationUnique + simp only [Bool.and_eq_true] at h + exact AddInductive.CandidateExprTrace.viewTranslationUnique_sound _ + ((AddInductive.CandidateExprTrace.viewTranslationUnique_eq _).trans h.1) + +/-- The executable D3 gate's uniqueness Bool likewise supplies the +constructor-list strict-view certificate. -/ +theorem StagedNormalizationCandidatePreFamilyInput.constructorViewsUnique + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us candidate rawDecl) : + candidate.families.singleton.constructors.ViewTranslationUnique := by + have h := input.safety.translationUnique + simp only [Bool.and_eq_true] at h + exact AddInductive.CandidateList.viewTranslationUnique_sound _ h.2 + +/-- Exact, source-indexed refinement of the public producer package. + +The public `ProducedGenerationCandidatePackage` deliberately erases its +dependent source, normalization, and generation indices. Keeping those +indices in this closure result lets clients choose the `Nonempty` witness and +still recover a package whose projections reduce to the requested source and +generation. -/ +structure ExactProducedGenerationCandidatePackage + {source : VInductDecl} {raw : VInductiveType} + {kernelSource : InductiveType} {numNested : Nat} {isUnsafe : Bool} + {context : AddInductive.Context} + (env : VEnv) (Us : List Name) + (producedCandidate : ProducedGenerationShapeCandidate source raw + kernelSource numNested isUnsafe context) + (generation : GenerationChecked source) where + normalization : NormalizationCandidateSemanticRun env Us + producedCandidate.candidate source + raw_eq : raw = normalization.raw + semantic : GenerationCandidateSemanticRun normalization generation + +/-- Erase only the exact dependent indices retained by the generic closure. +The ordinary producer equation is copied from the same strengthened producer +value; it contributes provenance, not semantic authority. -/ +def ExactProducedGenerationCandidatePackage.package + {source : VInductDecl} {raw : VInductiveType} + {kernelSource : InductiveType} {numNested : Nat} {isUnsafe : Bool} + {context : AddInductive.Context} {env : VEnv} {Us : List Name} + {producedCandidate : ProducedGenerationShapeCandidate source raw + kernelSource numNested isUnsafe context} + {generation : GenerationChecked source} + (exact : ExactProducedGenerationCandidatePackage env Us + producedCandidate generation) : + ProducedGenerationCandidatePackage env Us := + exact.semantic.producedPackage context source.nparams numNested isUnsafe + producedCandidate.produced + +/-- Close one strengthened singleton producer choice-free. The semantic +hierarchy is computed by the deterministic translator under the executable +D3 strict-view gate carried by the staged owner, so the retained package is +data rather than a `Classical.choice` selection from `Nonempty`. -/ +def ProducedGenerationShapeCandidate.exactProducedPackage + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {raw : VInductiveType} {numNested : Nat} {isUnsafe : Bool} + {context : AddInductive.Context} + (producedCandidate : ProducedGenerationShapeCandidate source raw + kernelSource numNested isUnsafe context) + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us producedCandidate.candidate source) + (rawOwnerEq : raw = + input.postFamilyInput.universeInput.staged.raw) + (generation : GenerationChecked source) + (analysis : ∀ normalization : NormalizationCandidateSemanticRun env Us + producedCandidate.candidate source, + normalization.root.normalization.generation? = some generation) : + ExactProducedGenerationCandidatePackage env Us + producedCandidate generation := + let normalization := + input.postFamilyInput.universeInput.staged.semanticInput.semanticOfUnique + input.familyViewUnique input.constructorViewsUnique + { normalization := normalization + raw_eq := rawOwnerEq + semantic := GenerationCandidateSemanticRun.ofGenerationShape input + normalization generation (analysis normalization) + (by + have hraw : normalization.raw = + input.postFamilyInput.universeInput.staged.raw := rfl + simpa only [NormalizationCandidateSemanticRun.generationShape, + rawOwnerEq, hraw] using producedCandidate.shape) } + +/-- Close one strengthened singleton producer from the staged D1--D4 owner +without choosing a semantic hierarchy at the API boundary, while retaining +the exact dependent source and generation indices needed by consumers. -/ +theorem ProducedGenerationShapeCandidate.exactProducedPackage_nonempty + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {raw : VInductiveType} {numNested : Nat} {isUnsafe : Bool} + {context : AddInductive.Context} + (producedCandidate : ProducedGenerationShapeCandidate source raw + kernelSource numNested isUnsafe context) + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us producedCandidate.candidate source) + (rawOwnerEq : raw = + input.postFamilyInput.universeInput.staged.raw) + (generation : GenerationChecked source) + (analysis : ∀ normalization : NormalizationCandidateSemanticRun env Us + producedCandidate.candidate source, + normalization.root.normalization.generation? = some generation) : + Nonempty (ExactProducedGenerationCandidatePackage env Us + producedCandidate generation) := by + obtain ⟨preFamily⟩ := input.exists + let normalization := preFamily.postFamily.produced.semantic + have semanticRawEq : normalization.raw = + input.postFamilyInput.universeInput.staged.raw := by + have singletonEq := normalization.raw_types_eq.symm.trans + input.postFamilyInput.universeInput.staged.raw_types_eq + injection singletonEq + have rawEq := rawOwnerEq.trans semanticRawEq.symm + let semantic := GenerationCandidateSemanticRun.ofGenerationShape input + normalization generation (analysis normalization) (by + simpa only [NormalizationCandidateSemanticRun.generationShape, + rawEq] using producedCandidate.shape) + exact ⟨{ normalization, raw_eq := rawEq, semantic }⟩ + +/-- Close one strengthened singleton producer from the staged D1--D4 owner +without choosing a semantic hierarchy at the API boundary. + +`analysis` is exact for every checker-selected semantic normalization owned by +the staged input. The proof eliminates `input.exists` only into `Nonempty`, +then applies D4 to that exact selected normalization. The ordinary producer +equation and the independent generation-shape gate remain the two fields of +`producedCandidate`; neither supplies Theory meaning by itself. -/ +theorem ProducedGenerationShapeCandidate.producedPackage_nonempty + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {raw : VInductiveType} {numNested : Nat} {isUnsafe : Bool} + {context : AddInductive.Context} + (producedCandidate : ProducedGenerationShapeCandidate source raw + kernelSource numNested isUnsafe context) + (input : StagedNormalizationCandidatePreFamilyInput familyContext + constructorContext env Us producedCandidate.candidate source) + (rawOwnerEq : raw = + input.postFamilyInput.universeInput.staged.raw) + (generation : GenerationChecked source) + (analysis : ∀ normalization : NormalizationCandidateSemanticRun env Us + producedCandidate.candidate source, + normalization.root.normalization.generation? = some generation) : + Nonempty (ProducedGenerationCandidatePackage env Us) := by + obtain ⟨exact⟩ := producedCandidate.exactProducedPackage_nonempty input + rawOwnerEq generation analysis + exact ⟨exact.package⟩ + +end VInductDecl + +namespace VInductDecl + +/- The D4 closure roots intentionally inherit the exact transitional Verify +axiom set already present in the staged semantic inputs. -/ +#guard_named_axioms Ix.Theory.Named.VInductDecl.StagedNormalizationCandidatePreFamilyInput.checkedWF [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.StagedNormalizationCandidatePreFamilyInput.viewDeclWF [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationCandidateSemanticRun.ofGenerationShape [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.NormalizationCandidateSemanticRun.producedPackageOfGenerationShape [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.ProducedGenerationShapeCandidate.producedPackage [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.ProducedGenerationShapeCandidate.exactProducedPackage_nonempty [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.StagedNormalizationCandidatePreFamilyInput.exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.StagedNormalizationCandidatePostFamilyInput.exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +/- The wrapper projections expose their exact existing Verify closure. The +new universe bridge itself remains separately guarded above; staging does not +hide the transitional dependencies already present in the semantic owner. -/ +#guard_named_axioms Ix.Theory.Named.VInductDecl.StagedNormalizationCandidateUniverseInput.semanticValidation [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.StagedNormalizationCandidateUniverseInput.universeSemantics [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.StagedNormalizationCandidateUniverseInput.exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +end VInductDecl +end Ix.Theory.Named diff --git a/Ix/Theory/Named/Verify/Environment/ConstructorValidityMatrix.lean b/Ix/Theory/Named/Verify/Environment/ConstructorValidityMatrix.lean new file mode 100644 index 000000000..1692e8052 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/ConstructorValidityMatrix.lean @@ -0,0 +1,358 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.ConstructorValidityFixtures +import Ix.Theory.Named.Verify.Environment.InductiveFixtures + +/-! +# Spec-05 constructor-validity differential matrix + +The positive half quotes real Lean metadata, runs the ordinary normalization +candidate producer, then runs both strengthened constructor gates at their +actual pre-family and post-family environments. The negative half pairs each failed +source declaration in `Theory.ConstructorValidityFixtures` with hand-built +metadata at the nearest ordinary-producer phase. +-/ + +namespace Ix.Theory.Named.InductiveReplayFixtures +open Lean Meta Elab Term +open Ix.Theory.Named.InductiveFixtures + +/-! ## Actual positive metadata -/ + +def constructorValidityMatrixInfo : ConstantInfo := + kernelInductInfo% ConstructorValidityMatrix + +def constructorValidityMatrixMkInfo : ConstantInfo := + kernelCtorInfo% ConstructorValidityMatrix.mk + +def constructorValidityMatrixRecInfo : ConstantInfo := + kernelRecInfo% ConstructorValidityMatrix.rec + +def constructorValidityMatrixKernelRuleRhs : VExpr := + kernelRecRuleRhs% ConstructorValidityMatrix.rec 0 + +def constructorValidityMatrixKernelCtor : Constructor where + name := constructorValidityMatrixMkInfo.name + type := constructorValidityMatrixMkInfo.type + +def constructorValidityMatrixKernelType : InductiveType where + name := constructorValidityMatrixInfo.name + type := constructorValidityMatrixInfo.type + ctors := [constructorValidityMatrixKernelCtor] + +def propRecursiveBoundaryInfo : ConstantInfo := + kernelInductInfo% PropRecursiveBoundary + +def propRecursiveBoundaryMkInfo : ConstantInfo := + kernelCtorInfo% PropRecursiveBoundary.mk + +def propRecursiveBoundaryRecInfo : ConstantInfo := + kernelRecInfo% PropRecursiveBoundary.rec + +def propRecursiveBoundaryKernelRuleRhs : VExpr := + kernelRecRuleRhs% PropRecursiveBoundary.rec 0 + +def propRecursiveBoundaryKernelCtor : Constructor where + name := propRecursiveBoundaryMkInfo.name + type := propRecursiveBoundaryMkInfo.type + +def propRecursiveBoundaryKernelType : InductiveType where + name := propRecursiveBoundaryInfo.name + type := propRecursiveBoundaryInfo.type + ctors := [propRecursiveBoundaryKernelCtor] + +theorem constructorValidityMatrix_kernel_shape : + (match constructorValidityMatrixInfo with + | .inductInfo info => (info.numParams, info.numIndices) + | _ => (0, 0)) = (2, 0) ∧ + (match constructorValidityMatrixMkInfo with + | .ctorInfo info => info.numFields + | _ => 0) = 6 ∧ + (match constructorValidityMatrixRecInfo with + | .recInfo info => + (info.numParams, info.numIndices, info.numMotives, + info.numMinors, info.rules.length) + | _ => (0, 0, 0, 0, 0)) = (2, 0, 1, 1, 1) := by + exact ⟨rfl, rfl, rfl⟩ + +theorem constructorValidityMatrix_recursive_positions_exact : + constructorValidityMatrixChecked.constructors[0].recursive.map + (fun position => (position.fieldIndex, position.binders.length)) = + [(2, 0), (3, 1)] := rfl + +theorem constructorValidityMatrix_kernel_rule_exact : + constructorValidityMatrixKernelRuleRhs = + constructorValidityMatrixGenerationChecked.generatedRules[0].rhs := rfl + +theorem propRecursiveBoundary_kernel_shape : + (match propRecursiveBoundaryInfo with + | .inductInfo info => (info.numParams, info.numIndices) + | _ => (0, 0)) = (1, 1) ∧ + (match propRecursiveBoundaryMkInfo with + | .ctorInfo info => info.numFields + | _ => 0) = 2 ∧ + (match propRecursiveBoundaryRecInfo with + | .recInfo info => + (info.numParams, info.numIndices, info.numMotives, + info.numMinors, info.rules.length) + | _ => (0, 0, 0, 0, 0)) = (1, 1, 1, 1, 1) := by + exact ⟨rfl, rfl, rfl⟩ + +theorem propRecursiveBoundary_recursive_positions_exact : + propRecursiveBoundaryChecked.constructors[0].recursive.map + (fun position => (position.fieldIndex, position.binders.length)) = + [(1, 1)] := rfl + +theorem propRecursiveBoundary_kernel_rule_exact : + propRecursiveBoundaryKernelRuleRhs = + propRecursiveBoundaryGenerationChecked.generatedRules[0].rhs := rfl + +/-! ## Positive ordinary and strengthened gates -/ + +def constructorValidityMatrixContext : AddInductive.Context where + env := Kernel.Environment.ofConstants `_constructorValidityMatrix + ({} : ConstMap) + lparams := [`u] + safety := .safe + allowPrimitive := false + +def propRecursiveBoundaryContext : AddInductive.Context where + env := Kernel.Environment.ofConstants `_propRecursiveBoundary + ({} : ConstMap) + lparams := [`u] + safety := .safe + allowPrimitive := false + +def singletonCandidateExact (nparams : Nat) (source : InductiveType) + (context : AddInductive.Context) : Bool := + match AddInductive.buildNormalizationCandidate nparams [source] 0 false + context with + | .error _ => false + | .ok candidate => + candidate.families.singleton.familyType.type.view.equal source.type && + candidate.families.singleton.constructors.toList + (fun _ constructor => constructor.type.view) == + source.ctors.map (fun constructor => constructor.type) + +def singletonPreFamilyAccepted (nparams : Nat) (source : InductiveType) + (context : AddInductive.Context) : Bool := + match AddInductive.buildNormalizationCandidate nparams [source] 0 false + context with + | .error _ => false + | .ok candidate => + match AddInductive.checkInductiveTypes nparams #[source] + (fun stats => + AddInductive.checkConstructorPreFamilySafety stats + candidate.families.singleton.familyType.type.view + candidate.families.singleton.constructors) context with + | .ok _ => true + | .error _ => false + +def singletonUniverseAccepted (nparams : Nat) (source : InductiveType) + (context : AddInductive.Context) : Bool := + match AddInductive.checkInductiveTypes nparams #[source] + (fun stats => do + let familyEnv ← AddInductive.declareInductiveTypes stats nparams + #[source] 0 false + AddInductive.withEnv familyEnv do + AddInductive.checkConstructorUniverseListSemantics stats + source.ctors) context with + | .ok _ => true + | .error _ => false + +#guard singletonCandidateExact 2 constructorValidityMatrixKernelType + constructorValidityMatrixContext + +#guard singletonPreFamilyAccepted 2 constructorValidityMatrixKernelType + constructorValidityMatrixContext + +#guard singletonUniverseAccepted 2 constructorValidityMatrixKernelType + constructorValidityMatrixContext + +#guard singletonCandidateExact 1 propRecursiveBoundaryKernelType + propRecursiveBoundaryContext + +#guard singletonPreFamilyAccepted 1 propRecursiveBoundaryKernelType + propRecursiveBoundaryContext + +#guard singletonUniverseAccepted 1 propRecursiveBoundaryKernelType + propRecursiveBoundaryContext + +/-! ## Matching ordinary-producer rejections -/ + +def spec05TypeBoxName : Name := + `Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05TypeBox + +def spec05ProofBoxName : Name := + `Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05ProofBox + +def spec05DepProofBoxName : Name := + `Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05DepProofBox + +def spec05TypeBoxInfo : ConstantInfo := .axiomInfo { + name := spec05TypeBoxName + levelParams := [] + type := .forallE `α (.sort (.succ .zero)) + (.sort (.succ .zero)) .default + isUnsafe := false } + +def spec05ProofBoxInfo : ConstantInfo := .axiomInfo { + name := spec05ProofBoxName + levelParams := [] + type := .forallE `α (.sort (.succ .zero)) (.sort .zero) .default + isUnsafe := false } + +def spec05DepProofBoxInfo : ConstantInfo := .axiomInfo { + name := spec05DepProofBoxName + levelParams := [] + type := .forallE `α (.sort (.succ .zero)) + (.forallE `value (.bvar 0) (.sort .zero) .default) .implicit + isUnsafe := false } + +def spec05NegativeMap : ConstMap := + ((({} : ConstMap).insert spec05TypeBoxName spec05TypeBoxInfo).insert + spec05ProofBoxName spec05ProofBoxInfo).insert + spec05DepProofBoxName spec05DepProofBoxInfo + +def spec05NegativeContext : AddInductive.Context where + env := Kernel.Environment.ofConstants `_spec05Negative spec05NegativeMap + lparams := [] + safety := .safe + allowPrimitive := false + +def spec05UnsafeNegativeContext : AddInductive.Context := + { spec05NegativeContext with safety := .unsafe } + +def spec05NegativeType (name ctorName : Name) (ctorType : Expr) : + InductiveType where + name := name + type := .sort (.succ .zero) + ctors := [{ name := ctorName, type := ctorType }] + +def spec05NestedNegativeName : Name := + `Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05NestedNegative + +def spec05NestedNegativeMkName : Name := + .str spec05NestedNegativeName "mk" + +def spec05NestedNegativeConst : Expr := + .const spec05NestedNegativeName [] + +def spec05NestedNegativeField : Expr := + .forallE `_ + (.forallE `_ spec05NestedNegativeConst (.sort .zero) .default) + spec05NestedNegativeConst .default + +def spec05NestedNegativeType : InductiveType := + spec05NegativeType spec05NestedNegativeName spec05NestedNegativeMkName + (.forallE `field spec05NestedNegativeField + spec05NestedNegativeConst .default) + +def spec05FamilyNonrecursiveName : Name := + `Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05FamilyNonrecursive + +def spec05FamilyNonrecursiveMkName : Name := + .str spec05FamilyNonrecursiveName "mk" + +def spec05FamilyNonrecursiveConst : Expr := + .const spec05FamilyNonrecursiveName [] + +def spec05FamilyNonrecursiveType : InductiveType := + spec05NegativeType spec05FamilyNonrecursiveName + spec05FamilyNonrecursiveMkName + (.forallE `field + (.app (.const spec05TypeBoxName []) spec05FamilyNonrecursiveConst) + spec05FamilyNonrecursiveConst .default) + +def spec05FamilyProofName : Name := + `Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05FamilyProof + +def spec05FamilyProofMkName : Name := + .str spec05FamilyProofName "mk" + +def spec05FamilyProofConst : Expr := + .const spec05FamilyProofName [] + +def spec05FamilyProofType : InductiveType := + spec05NegativeType spec05FamilyProofName spec05FamilyProofMkName + (.forallE `proof + (.app (.const spec05ProofBoxName []) spec05FamilyProofConst) + spec05FamilyProofConst .default) + +def spec05RecursiveDependencyName : Name := + `Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05RecursiveDependency + +def spec05RecursiveDependencyMkName : Name := + .str spec05RecursiveDependencyName "mk" + +def spec05RecursiveDependencyConst : Expr := + .const spec05RecursiveDependencyName [] + +def spec05RecursiveDependencyProof : Expr := + .app (.app (.const spec05DepProofBoxName []) + spec05RecursiveDependencyConst) (.bvar 0) + +def spec05RecursiveDependencyType : InductiveType := + spec05NegativeType spec05RecursiveDependencyName + spec05RecursiveDependencyMkName + (.forallE `recursive spec05RecursiveDependencyConst + (.forallE `proof spec05RecursiveDependencyProof + spec05RecursiveDependencyConst .default) .default) + +def spec05UniverseRejectName : Name := + `Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05UniverseReject + +def spec05UniverseRejectMkName : Name := + .str spec05UniverseRejectName "mk" + +def spec05UniverseRejectConst : Expr := + .const spec05UniverseRejectName [] + +def spec05UniverseRejectType : InductiveType := + spec05NegativeType spec05UniverseRejectName spec05UniverseRejectMkName + (.forallE `α (.sort (.succ .zero)) spec05UniverseRejectConst .default) + +def spec05CandidateError (source : InductiveType) : Option String := + match AddInductive.buildNormalizationCandidate 0 [source] 0 false + spec05NegativeContext with + | .error (.other message) => some message + | _ => none + +#guard spec05CandidateError spec05NestedNegativeType = some + "arg #1 of 'Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05NestedNegative.mk' has a non positive occurrence of the datatypes being declared" + +#guard spec05CandidateError spec05FamilyNonrecursiveType = some + "arg #1 of 'Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05FamilyNonrecursive.mk' has a non valid occurrence of the datatypes being declared" + +#guard spec05CandidateError spec05FamilyProofType = some + "arg #1 of 'Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05FamilyProof.mk' has a non valid occurrence of the datatypes being declared" + +#guard spec05CandidateError spec05RecursiveDependencyType = some + "arg #2 of 'Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05RecursiveDependency.mk' has a non valid occurrence of the datatypes being declared" + +#guard spec05CandidateError spec05UniverseRejectType = some + "universe level of type_of(arg #1) of 'Ix.Theory.Named.InductiveFixtures.KernelDifferential.Spec05UniverseReject.mk' is too big for the corresponding inductive datatype" + +def spec05RecursiveDependencyPreFamilyError : Option String := + match AddInductive.buildNormalizationCandidate 0 + [spec05RecursiveDependencyType] 0 true spec05UnsafeNegativeContext with + | .error _ => none + | .ok candidate => + match AddInductive.checkInductiveTypes 0 + #[spec05RecursiveDependencyType] + (fun stats => + AddInductive.checkConstructorPreFamilySafety stats + candidate.families.singleton.familyType.type.view + candidate.families.singleton.constructors) + spec05UnsafeNegativeContext with + | .error (.other message) => some message + | _ => none + +#guard spec05RecursiveDependencyPreFamilyError = + some "constructor depends on an omitted recursive local" + +end Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/Elimination.lean b/Ix/Theory/Named/Verify/Environment/Elimination.lean new file mode 100644 index 000000000..6ab6559b4 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/Elimination.lean @@ -0,0 +1,244 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Reference.Inductive.EliminationTrace +import Ix.Theory.Named.Inductive + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named +open Lean hiding Environment Exception + +namespace AddInductive + +/-- Compatibility name for Theory's presentation of the Boolean returned by +the ordinary large-eliminator checker. -/ +@[deprecated VInductDecl.ElimMode.ofBool (since := "2026-08-11")] +abbrev checkerElimMode : Bool → VInductDecl.ElimMode := + VInductDecl.ElimMode.ofBool + +/-- Lightweight alignment for an exact `getElimLevel` execution when the +normalization statistics are already pinned independently. This is useful for +the never-zero branch, which returns before inspecting any constructor. -/ +structure CheckerElimLevelRun + {source : VInductDecl} (generation : VInductDecl.GenerationChecked source) + {stats : InductiveStats} {indTypes : Array InductiveType} + {context : Context} + (execution : ElimLevelExecution stats indTypes context) : Type where + sourceUvars_eq : source.uvars = context.lparams.length + mode_eq : generation.elimination = + VInductDecl.ElimMode.ofBool execution.large.result + recUvars_eq : generation.recUvars = + (getRecLevelParams execution.level context.lparams).length + recLevels_eq : + (getRecLevels execution.level stats.levels).mapM + (VLevel.ofLevel (getRecLevelParams execution.level context.lparams)) = + some generation.recLevels + +namespace CheckerElimLevelRun + +/-- Decide the complete mode and level-layout alignment from the retained +ordinary execution. -/ +def build? + {source : VInductDecl} (generation : VInductDecl.GenerationChecked source) + {stats : InductiveStats} {indTypes : Array InductiveType} + {context : Context} + (execution : ElimLevelExecution stats indTypes context) : + Option (CheckerElimLevelRun generation execution) := do + if huvars : source.uvars = context.lparams.length then + if hmode : generation.elimination = + VInductDecl.ElimMode.ofBool execution.large.result then + if hrecUvars : generation.recUvars = + (getRecLevelParams execution.level context.lparams).length then + if hlevels : + (getRecLevels execution.level stats.levels).mapM + (VLevel.ofLevel + (getRecLevelParams execution.level context.lparams)) = + some generation.recLevels then + some { + sourceUvars_eq := huvars + mode_eq := hmode + recUvars_eq := hrecUvars + recLevels_eq := hlevels } + else none + else none + else none + else none + +theorem large_result_iff + (run : CheckerElimLevelRun generation execution) : + execution.large.result = true ↔ + generation.elimination = VInductDecl.ElimMode.large := by + cases hresult : execution.large.result with + | false => + have hmode : generation.elimination = VInductDecl.ElimMode.small := by + simpa [hresult] using run.mode_eq + simp [hmode] + | true => + have hmode : generation.elimination = VInductDecl.ElimMode.large := by + simpa [hresult] using run.mode_eq + simp [hmode] + +theorem small_result_iff + (run : CheckerElimLevelRun generation execution) : + execution.large.result = false ↔ + generation.elimination = VInductDecl.ElimMode.small := by + cases hresult : execution.large.result with + | false => + have hmode : generation.elimination = VInductDecl.ElimMode.small := by + simpa [hresult] using run.mode_eq + simp [hmode] + | true => + have hmode : generation.elimination = VInductDecl.ElimMode.large := by + simpa [hresult] using run.mode_eq + simp [hmode] + +end CheckerElimLevelRun + +/-- Exact alignment between the ordinary K-target execution and the Boolean +retained by Theory generation. This flag is certified independently of the +elimination-mode decision. -/ +structure CheckerKTargetRun + {source : VInductDecl} (generation : VInductDecl.GenerationChecked source) + {stats : InductiveStats} {indTypes : Array InductiveType} + {context : Context} + (execution : KTargetExecution stats indTypes context) : Type where + result_eq : generation.kTarget = execution.result + +namespace CheckerKTargetRun + +def build? + {source : VInductDecl} (generation : VInductDecl.GenerationChecked source) + {stats : InductiveStats} {indTypes : Array InductiveType} + {context : Context} + (execution : KTargetExecution stats indTypes context) : + Option (CheckerKTargetRun generation execution) := + if h : generation.kTarget = execution.result then + some ⟨h⟩ + else + none + +theorem result_true_iff + (run : CheckerKTargetRun generation execution) : + execution.result = true ↔ generation.kTarget = true := by + rw [run.result_eq] + +theorem result_false_iff + (run : CheckerKTargetRun generation execution) : + execution.result = false ↔ generation.kTarget = false := by + rw [run.result_eq] + +end CheckerKTargetRun + +/-- Executable alignment between an exact ordinary elimination run and the +mode/K-target/universe layout consumed by one Theory generation artifact. + +The operational side owns the real `ensureType` observations, selected fresh +level name, K-target decision, recursive-call levels, and stored metadata +parameter order. The equations below are checked data, so a Theory generation +whose mode, K flag, or numeric universe layout disagrees with that run cannot +inhabit this record. -/ +structure CheckerEliminationRun + {source : VInductDecl} (generation : VInductDecl.GenerationChecked source) + {nparams : Nat} {types : List InductiveType} + {numNested : Nat} {isUnsafe : Bool} {candidateContext : Context} + (execution : NormalizationEliminationExecution nparams types numNested + isUnsafe candidateContext) : Type where + nparams_eq : nparams = source.nparams + sourceUvars_eq : source.uvars = + execution.normalization.validationContext.lparams.length + mode_eq : generation.elimination = + VInductDecl.ElimMode.ofBool execution.elimination.large.result + kTarget_eq : generation.kTarget = execution.kTarget.result + recUvars_eq : generation.recUvars = execution.recLevelParams.length + recLevels_eq : execution.recLevels.mapM + (VLevel.ofLevel execution.recLevelParams) = some generation.recLevels + +namespace CheckerEliminationRun + +/-- Build the alignment by deciding every equality rather than asking a +fixture or caller to supply a mode or level permutation. -/ +def build? + {source : VInductDecl} (generation : VInductDecl.GenerationChecked source) + {nparams : Nat} {types : List InductiveType} + {numNested : Nat} {isUnsafe : Bool} {candidateContext : Context} + (execution : NormalizationEliminationExecution nparams types numNested + isUnsafe candidateContext) : + Option (CheckerEliminationRun generation execution) := do + if hparams : nparams = source.nparams then + if huvars : source.uvars = + execution.normalization.validationContext.lparams.length then + if hmode : generation.elimination = + VInductDecl.ElimMode.ofBool execution.elimination.large.result then + if hkTarget : generation.kTarget = execution.kTarget.result then + if hrecUvars : generation.recUvars = + execution.recLevelParams.length then + if hlevels : execution.recLevels.mapM + (VLevel.ofLevel execution.recLevelParams) = + some generation.recLevels then + some { + nparams_eq := hparams + sourceUvars_eq := huvars + mode_eq := hmode + kTarget_eq := hkTarget + recUvars_eq := hrecUvars + recLevels_eq := hlevels } + else none + else none + else none + else none + else none + else none + +theorem large_result_iff + (run : CheckerEliminationRun generation execution) : + execution.elimination.large.result = true ↔ + generation.elimination = VInductDecl.ElimMode.large := by + cases hresult : execution.elimination.large.result with + | false => + have hmode : generation.elimination = VInductDecl.ElimMode.small := by + simpa [hresult] using run.mode_eq + simp [hmode] + | true => + have hmode : generation.elimination = VInductDecl.ElimMode.large := by + simpa [hresult] using run.mode_eq + simp [hmode] + +theorem small_result_iff + (run : CheckerEliminationRun generation execution) : + execution.elimination.large.result = false ↔ + generation.elimination = VInductDecl.ElimMode.small := by + cases hresult : execution.elimination.large.result with + | false => + have hmode : generation.elimination = VInductDecl.ElimMode.small := by + simpa [hresult] using run.mode_eq + simp [hmode] + | true => + have hmode : generation.elimination = VInductDecl.ElimMode.large := by + simpa [hresult] using run.mode_eq + simp [hmode] + +theorem kTarget_result_true_iff + (run : CheckerEliminationRun generation execution) : + execution.kTarget.result = true ↔ generation.kTarget = true := by + rw [run.kTarget_eq] + +theorem kTarget_result_false_iff + (run : CheckerEliminationRun generation execution) : + execution.kTarget.result = false ↔ generation.kTarget = false := by + rw [run.kTarget_eq] + +end CheckerEliminationRun + +#guard_named_axioms Ix.Theory.Named.AddInductive.CheckerEliminationRun.large_result_iff [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CheckerElimLevelRun.large_result_iff [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.AddInductive.CheckerKTargetRun.result_true_iff [propext, Classical.choice, Quot.sound] + +end AddInductive +end Ix.Theory.Named diff --git a/Ix/Theory/Named/Verify/Environment/EliminationFixturesCommon.lean b/Ix/Theory/Named/Verify/Environment/EliminationFixturesCommon.lean new file mode 100644 index 000000000..efacf6f1c --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/EliminationFixturesCommon.lean @@ -0,0 +1,65 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.Environment.Elimination +import Ix.Theory.Named.Verify.Environment.InductiveFixtures + +namespace Ix.Theory.Named.InductiveReplayFixtures +open Lean Meta Elab Term +open Ix.Theory.Named.InductiveFixtures + +universe u + +/-- A source-universe-bearing small eliminator. Or has no source universes, +so this fixture distinguishes "no fresh level" from "no levels at all". -/ +inductive Spec06SmallSource (α : Sort u) : Prop where + | left : Spec06SmallSource α + | right : Spec06SmallSource α + +def recursorShape06 (info : ConstantInfo) : + List Name × Nat × Nat × Nat × Nat × Bool × List (Name × Nat) := + match info with + | .recInfo rec => + (rec.levelParams, rec.numParams, rec.numIndices, rec.numMotives, + rec.numMinors, rec.k, + rec.rules.map fun rule => (rule.ctor, rule.nfields)) + | _ => ([], 0, 0, 0, 0, false, []) + +def spec06KernelEnv : Kernel.Environment := + Kernel.Environment.ofConstants `_spec06 {} + +def spec06Context (lparams : List Name) : AddInductive.Context where + env := spec06KernelEnv + lparams := lparams + safety := .safe + allowPrimitive := false + +example : AddInductive.getFreshElimParam [] = `u := by native_decide +example : AddInductive.getFreshElimParam [`u] = `u_1 := by native_decide +example : AddInductive.getFreshElimParam [`u, `u_1] = `u_2 := by native_decide + +/-- Decidable structural equality for the exact kernel level lists retained by +the fixtures. `Lean.Level` intentionally has no `DecidableEq` instance. -/ +def levelListStructEq06 : List Level → List Level → Bool + | [], [] => true + | u :: us, v :: vs => + Level.isStructEq u v && levelListStructEq06 us vs + | _, _ => false + +theorem levelListStructEq06_eq {us vs : List Level} + (h : levelListStructEq06 us vs) : us = vs := by + induction us generalizing vs with + | nil => cases vs <;> simp_all [levelListStructEq06] + | cons u us ih => + cases vs with + | nil => simp [levelListStructEq06] at h + | cons v vs => + simp only [levelListStructEq06, Bool.and_eq_true] at h + cases Level.isStructEq_eq h.1 + cases ih h.2 + rfl + +end Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/EliminationFixturesEdges.lean b/Ix/Theory/Named/Verify/Environment/EliminationFixturesEdges.lean new file mode 100644 index 000000000..4f9c11fa7 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/EliminationFixturesEdges.lean @@ -0,0 +1,249 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.Environment.EliminationFixturesCommon + +open Ix.Theory (VLevel) + +/-! Exact Spec-06C `Unit`/`PUnit` and `Empty` edge-shape fixtures. + +`Unit` is a reducible alias for `PUnit` on this Lean revision. The alias has +definition metadata but no independent inductive/constructor/recursor records, +so the real one-constructor transaction is checked under `PUnit`. `Empty` +supplies the matching zero-constructor transaction. -/ + +namespace Ix.Theory.Named.InductiveReplayFixtures +open Lean Meta Elab Term +open Ix.Theory.Named.InductiveFixtures + +/-- Translate the declaration-first universe order produced by `vconst` to +the fresh-first order stored by large recursor metadata. -/ +private def permC06C (constant : VConstant) (levels : List VLevel) : + VConstant := + ⟨constant.uvars, constant.type.instL levels⟩ + +/-! ## Unit is exactly the reducible PUnit alias -/ + +def unitAliasInfo06C : DefinitionVal := kernelDefVal% Unit + +/-- Pin every field of the actual `Unit` definition metadata. In particular, +there is no fabricated alias-level recursor for the edge fixture. -/ +example : + (unitAliasInfo06C.name, unitAliasInfo06C.levelParams, + unitAliasInfo06C.type, unitAliasInfo06C.value, + unitAliasInfo06C.hints, unitAliasInfo06C.safety, + unitAliasInfo06C.all) = + (``Unit, [], .sort (.succ .zero), + .const ``PUnit [.succ .zero], .abbrev, .safe, [``Unit]) := rfl + +/-! ## Complete kernel metadata shapes -/ + +def inductiveShape06C (info : ConstantInfo) : + Option (Name × List Name × Nat × Nat × List Name × List Name × Nat × + Bool × Bool × Bool) := + match info with + | .inductInfo induct => some + (induct.name, induct.levelParams, induct.numParams, induct.numIndices, + induct.all, induct.ctors, induct.numNested, induct.isRec, + induct.isUnsafe, induct.isReflexive) + | _ => none + +def constructorShape06C (info : ConstantInfo) : + Option (Name × List Name × Name × Nat × Nat × Nat × Bool) := + match info with + | .ctorInfo ctor => some + (ctor.name, ctor.levelParams, ctor.induct, ctor.cidx, ctor.numParams, + ctor.numFields, ctor.isUnsafe) + | _ => none + +def completeRecursorShape06C (info : ConstantInfo) : + Option (Name × List Name × List Name × Nat × Nat × Nat × Nat × Bool × + Bool × List (Name × Nat)) := + match info with + | .recInfo rec => some + (rec.name, rec.levelParams, rec.all, rec.numParams, rec.numIndices, + rec.numMotives, rec.numMinors, rec.k, rec.isUnsafe, + rec.rules.map fun rule => (rule.ctor, rule.nfields)) + | _ => none + +/-! ## PUnit: one constructor, one minor, one rule -/ + +def punitInfo06C : ConstantInfo := kernelInductInfo% PUnit +def punitCtorInfo06C : ConstantInfo := kernelCtorInfo% PUnit.unit +def punitRecInfo06C : ConstantInfo := kernelRecInfo% PUnit.rec +def punitRuleRhs06C : VExpr := kernelRecRuleRhs% PUnit.rec 0 + +example : inductiveShape06C punitInfo06C = some + (``PUnit, [`u], 0, 0, [``PUnit], [``PUnit.unit], 0, false, false, + false) := rfl +example : punitInfo06C.type = .sort (.param `u) := rfl +example : constructorShape06C punitCtorInfo06C = some + (``PUnit.unit, [`u], ``PUnit, 0, 0, 0, false) := rfl +example : punitCtorInfo06C.type = .const ``PUnit [.param `u] := rfl +example : completeRecursorShape06C punitRecInfo06C = some + (``PUnit.rec, [`u_1, `u], [``PUnit], 0, 0, 1, 1, false, false, + [(``PUnit.unit, 0)]) := rfl + +example : punitChecked.params = [] := rfl +example : punitChecked.indices = [] := rfl +example : punitChecked.constructors.length = 1 := rfl +example : punitChecked.constructors[0].fields = [] := rfl +example : punitChecked.constructors[0].recursive = [] := rfl +example : punitGenerationChecked.block.ctorPairs.length = 1 := rfl +example : punitGenerationChecked.minorTypes = + [.app (.bvar 0) (.const ``PUnit.unit [.param 1])] := rfl +example : punitGenerationChecked.generatedRules.length = 1 := rfl + +/-- The complete generated type agrees with the real recursor, including the +fresh-first `[u_1, u]` metadata order. -/ +example : punitGenerationChecked.recursor = + permC06C (vconst(type_of% @PUnit.rec)) [.param 1, .param 0] := rfl + +/-- Removing the three outer binders exposes motive, sole minor, then major. +The minor has no field or induction-hypothesis binders. -/ +example : VExpr.telN 3 punitGenerationChecked.recType = + [punitGenerationChecked.motiveType, + .app (.bvar 0) (.const ``PUnit.unit [.param 1]), + .const ``PUnit [.param 1]] := rfl + +example : punitRuleRhs06C = + punitGenerationChecked.generatedRules[0].rhs := rfl + +def punitKernelType06C : InductiveType where + name := punitInfo06C.name + type := punitInfo06C.type + ctors := [{ name := punitCtorInfo06C.name, type := punitCtorInfo06C.type }] + +def punitEliminationResult06C := + AddInductive.NormalizationEliminationExecution.buildExecution 0 + [punitKernelType06C] 0 false (spec06Context [`u]) + +theorem punitEliminationResult06C_isOk : + punitEliminationResult06C.isOk = true := by + native_decide + +def punitProducedExecution06C : + { execution // punitEliminationResult06C = .ok execution } := + match h : punitEliminationResult06C with + | .ok execution => ⟨execution, rfl⟩ + | .error _ => by + have hOk := punitEliminationResult06C_isOk + rw [h] at hOk + contradiction + +def punitExecution06C := punitProducedExecution06C.val + +def punitAlignment06C : AddInductive.CheckerEliminationRun + punitGenerationChecked punitExecution06C := + (AddInductive.CheckerEliminationRun.build? punitGenerationChecked + punitExecution06C).get (by native_decide) + +def punitLargeSingleton06C := + punitExecution06C.elimination.large.singleton.get (by native_decide) + +example : punitExecution06C.normalization.stats.params.size = 0 := by + native_decide +example : punitExecution06C.normalization.stats.nindices = #[0] := by + native_decide +example : punitExecution06C.normalization.stats.isNotZero = false := by + native_decide +example : punitExecution06C.elimination.large.result = true := by + native_decide +example : punitLargeSingleton06C.trace.parameterCount = 0 := by + native_decide +example : punitLargeSingleton06C.trace.proofFieldCount = 0 := by + native_decide +example : punitLargeSingleton06C.trace.dataFieldCount = 0 := by + native_decide +example : punitExecution06C.kTarget.result = false := by native_decide +example : punitExecution06C.kTarget.singleton = none := by native_decide +example : punitExecution06C.elimination.level = .param `u_1 := + Level.isStructEq_eq (by native_decide) +example : punitExecution06C.recLevelParams = [`u_1, `u] := by + native_decide +example : punitExecution06C.recLevels = [.param `u_1, .param `u] := + levelListStructEq06_eq (by native_decide) +example : punitGenerationChecked.elimination = .large := + punitAlignment06C.large_result_iff.mp (by native_decide) +example : punitGenerationChecked.kTarget = false := + punitAlignment06C.kTarget_result_false_iff.mp (by native_decide) + +/-! ## Empty: zero constructors, zero minors, zero rules -/ + +def emptyInfo06C : ConstantInfo := kernelInductInfo% Empty +def emptyRecInfo06C : ConstantInfo := kernelRecInfo% Empty.rec + +example : inductiveShape06C emptyInfo06C = some + (``Empty, [], 0, 0, [``Empty], [], 0, false, false, false) := rfl +example : emptyInfo06C.type = .sort (.succ .zero) := rfl +example : completeRecursorShape06C emptyRecInfo06C = some + (``Empty.rec, [`u], [``Empty], 0, 0, 1, 0, false, false, []) := rfl + +example : emptyChecked.params = [] := rfl +example : emptyChecked.indices = [] := rfl +example : emptyChecked.constructors = [] := rfl +example : emptyGenerationChecked.block.ctorPairs = [] := rfl +example : emptyGenerationChecked.minorTypes = [] := rfl +example : emptyGenerationChecked.generatedRules = [] := rfl +example : emptyGenerationChecked.recursor = vconst(type_of% @Empty.rec) := rfl + +/-- Empty elimination has no synthetic constructor minor: motive is followed +immediately by the major. -/ +example : VExpr.telN 2 emptyGenerationChecked.recType = + [emptyGenerationChecked.motiveType, .const ``Empty []] := rfl + +def emptyKernelType06C : InductiveType where + name := emptyInfo06C.name + type := emptyInfo06C.type + ctors := [] + +def emptyEliminationResult06C := + AddInductive.NormalizationEliminationExecution.buildExecution 0 + [emptyKernelType06C] 0 false (spec06Context []) + +theorem emptyEliminationResult06C_isOk : + emptyEliminationResult06C.isOk = true := by + native_decide + +def emptyProducedExecution06C : + { execution // emptyEliminationResult06C = .ok execution } := + match h : emptyEliminationResult06C with + | .ok execution => ⟨execution, rfl⟩ + | .error _ => by + have hOk := emptyEliminationResult06C_isOk + rw [h] at hOk + contradiction + +def emptyExecution06C := emptyProducedExecution06C.val + +def emptyAlignment06C : AddInductive.CheckerEliminationRun + emptyGenerationChecked emptyExecution06C := + (AddInductive.CheckerEliminationRun.build? emptyGenerationChecked + emptyExecution06C).get (by native_decide) + +example : emptyExecution06C.normalization.stats.params.size = 0 := by + native_decide +example : emptyExecution06C.normalization.stats.nindices = #[0] := by + native_decide +example : emptyExecution06C.normalization.stats.isNotZero = true := by + native_decide +example : emptyExecution06C.elimination.large.result = true := by + native_decide +example : emptyExecution06C.elimination.large.singleton = none := by + native_decide +example : emptyExecution06C.kTarget.result = false := by native_decide +example : emptyExecution06C.kTarget.singleton = none := by native_decide +example : emptyExecution06C.elimination.level = .param `u := + Level.isStructEq_eq (by native_decide) +example : emptyExecution06C.recLevelParams = [`u] := by native_decide +example : emptyExecution06C.recLevels = [.param `u] := + levelListStructEq06_eq (by native_decide) +example : emptyGenerationChecked.elimination = .large := + emptyAlignment06C.large_result_iff.mp (by native_decide) +example : emptyGenerationChecked.kTarget = false := + emptyAlignment06C.kTarget_result_false_iff.mp (by native_decide) + +end Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/EliminationFixturesEq.lean b/Ix/Theory/Named/Verify/Environment/EliminationFixturesEq.lean new file mode 100644 index 000000000..11b50dbb2 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/EliminationFixturesEq.lean @@ -0,0 +1,65 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.Environment.EliminationFixturesCommon + +/-! Exact Spec-06B Eq differential fixture. -/ + +namespace Ix.Theory.Named.InductiveReplayFixtures +open Lean Meta Elab Term +open Ix.Theory.Named.InductiveFixtures + +/-! Eq: indexed singleton large elimination and fresh-first order. -/ + +def eqKernelType06 : InductiveType where + name := eqInfo.name + type := eqInfo.type + ctors := [{ name := eqReflInfo.name, type := eqReflInfo.type }] + +def eqEliminationResult06 := + AddInductive.NormalizationEliminationExecution.buildExecution 2 + [eqKernelType06] 0 false (spec06Context eqInfo.levelParams) + +theorem eqEliminationResult06_isOk : eqEliminationResult06.isOk = true := by + native_decide + +def eqProducedExecution06 : + { execution // eqEliminationResult06 = .ok execution } := + match h : eqEliminationResult06 with + | .ok execution => ⟨execution, rfl⟩ + | .error _ => by + have hOk := eqEliminationResult06_isOk + rw [h] at hOk + contradiction + +def eqExecution06 := eqProducedExecution06.val + +def eqAlignment06 : + AddInductive.CheckerEliminationRun eqGenerationChecked eqExecution06 := + (AddInductive.CheckerEliminationRun.build? eqGenerationChecked eqExecution06).get + (by native_decide) + +def eqKTargetSingleton06 := + eqExecution06.kTarget.singleton.get (by native_decide) + +example : eqExecution06.elimination.large.result = true := by native_decide +example : eqExecution06.kTarget.result = true := by native_decide +example : eqKTargetSingleton06.trace.parameterCount = 2 := by native_decide +example : eqKTargetSingleton06.trace.fieldCount = 0 := by native_decide +example : eqExecution06.elimination.level = .param `u := + Level.isStructEq_eq (by native_decide) +example : eqExecution06.recLevelParams = [`u, `u_1] := by native_decide +example : eqExecution06.recLevels = [.param `u, .param `u_1] := + levelListStructEq06_eq (by native_decide) +example : recursorShape06 eqRecInfo = + ([`u, `u_1], 2, 1, 1, 1, true, [(``Eq.refl, 0)]) := rfl +example : eqReflKernelRuleRhs = eqChecked.generatedRules[0].rhs := rfl +example : eqGenerationChecked.elimination = .large := + eqAlignment06.large_result_iff.mp (by native_decide) +example : eqGenerationChecked.kTarget = true := + eqAlignment06.kTarget_result_true_iff.mp (by native_decide) + +end Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/EliminationFixturesEqNat.lean b/Ix/Theory/Named/Verify/Environment/EliminationFixturesEqNat.lean new file mode 100644 index 000000000..ba3fbfbc4 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/EliminationFixturesEqNat.lean @@ -0,0 +1,10 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.Environment.EliminationFixturesEq +import Ix.Theory.Named.Verify.Environment.EliminationFixturesNat + +/-! Umbrella for the exact Spec-06A Eq and Nat differential fixtures. -/ diff --git a/Ix/Theory/Named/Verify/Environment/EliminationFixturesNat.lean b/Ix/Theory/Named/Verify/Environment/EliminationFixturesNat.lean new file mode 100644 index 000000000..5da576adb --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/EliminationFixturesNat.lean @@ -0,0 +1,114 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.Environment.EliminationFixturesCommon + +/-! Exact Spec-06B Nat never-zero elimination and non-K fixture. -/ + +namespace Ix.Theory.Named.InductiveReplayFixtures +open Lean Meta Elab Term +open Ix.Theory.Named.InductiveFixtures + +/-! Nat's nonzero result universe takes the immediate large-elimination +branch. The exact singleton statistics and source levels are pinned directly +so this replay tests that branch without compiling the unrelated full +normalization trace a second time. -/ + +def natKernelType06 : InductiveType where + name := natInfo.name + type := natInfo.type + ctors := [ + { name := natZeroInfo.name, type := natZeroInfo.type }, + { name := natSuccInfo.name, type := natSuccInfo.type }] + +def natNeverZeroStats06 : AddInductive.InductiveStats := + AddInductive.singletonInductiveStats (spec06Context []) natKernelType06 + (.succ .zero) + +def natElimLevelResult06 := + AddInductive.ElimLevelExecution.buildExecution natNeverZeroStats06 + #[natKernelType06] (spec06Context []) + +theorem natElimLevelResult06_isOk : natElimLevelResult06.isOk = true := by + native_decide + +def natProducedElimLevel06 : + { execution // natElimLevelResult06 = .ok execution } := + match h : natElimLevelResult06 with + | .ok execution => ⟨execution, rfl⟩ + | .error _ => by + have hOk := natElimLevelResult06_isOk + rw [h] at hOk + contradiction + +def natElimLevelExecution06 := natProducedElimLevel06.val + +def natElimAlignmentResult06 := + AddInductive.CheckerElimLevelRun.build? natGenerationChecked + natElimLevelExecution06 + +theorem natElimAlignmentResult06_isSome : + natElimAlignmentResult06.isSome = true := by + native_decide + +def natElimAlignment06 : AddInductive.CheckerElimLevelRun + natGenerationChecked natElimLevelExecution06 := + natElimAlignmentResult06.get natElimAlignmentResult06_isSome + +def natKTargetResult06 := + AddInductive.KTargetExecution.buildExecution natNeverZeroStats06 + #[natKernelType06] (spec06Context []) + +theorem natKTargetResult06_isOk : natKTargetResult06.isOk = true := by + native_decide + +def natProducedKTarget06 : + { execution // natKTargetResult06 = .ok execution } := + match h : natKTargetResult06 with + | .ok execution => ⟨execution, rfl⟩ + | .error _ => by + have hOk := natKTargetResult06_isOk + rw [h] at hOk + contradiction + +def natKTargetExecution06 := natProducedKTarget06.val + +def natKTargetAlignmentResult06 := + AddInductive.CheckerKTargetRun.build? natGenerationChecked + natKTargetExecution06 + +theorem natKTargetAlignmentResult06_isSome : + natKTargetAlignmentResult06.isSome = true := by + native_decide + +def natKTargetAlignment06 : AddInductive.CheckerKTargetRun + natGenerationChecked natKTargetExecution06 := + natKTargetAlignmentResult06.get natKTargetAlignmentResult06_isSome + +example : natNeverZeroStats06.isNotZero = true := rfl +example : natNeverZeroStats06.resultLevel = .succ .zero := rfl +example : natKernelType06.type = .sort (.succ .zero) := rfl +example : natChecked.resultLevel = .succ .zero := rfl +example : natElimLevelExecution06.large.result = true := by native_decide +example : natKTargetExecution06.result = false := by native_decide +example : natKTargetExecution06.singleton = none := by native_decide +example : natElimLevelExecution06.level = .param `u := + Level.isStructEq_eq (by native_decide) +example : AddInductive.getRecLevelParams natElimLevelExecution06.level [] = + [`u] := by native_decide +example : AddInductive.getRecLevels natElimLevelExecution06.level [] = + [.param `u] := levelListStructEq06_eq (by native_decide) +example : recursorShape06 natRecInfo = + ([`u], 0, 0, 1, 2, false, + [(``Nat.zero, 0), (``Nat.succ, 1)]) := rfl +example : natZeroKernelRuleRhs = natChecked.generatedRules[0].rhs := rfl +example : natSuccKernelRuleRhs = natChecked.generatedRules[1].rhs := rfl +example : natGenerationChecked.elimination = .large := + natElimAlignment06.large_result_iff.mp (by native_decide) +example : natGenerationChecked.kTarget = false := + natKTargetAlignment06.result_false_iff.mp (by native_decide) + +end Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/EliminationFixturesOrAnd.lean b/Ix/Theory/Named/Verify/Environment/EliminationFixturesOrAnd.lean new file mode 100644 index 000000000..3688cf991 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/EliminationFixturesOrAnd.lean @@ -0,0 +1,137 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.Environment.EliminationFixturesCommon + +/-! Exact Spec-06B Or/And differential fixtures. -/ + +namespace Ix.Theory.Named.InductiveReplayFixtures +open Lean Meta Elab Term +open Ix.Theory.Named.InductiveFixtures + +/-! ## Or: Prop-only elimination -/ + +def orInfo06 : ConstantInfo := kernelInductInfo% Or +def orInlInfo06 : ConstantInfo := kernelCtorInfo% Or.inl +def orInrInfo06 : ConstantInfo := kernelCtorInfo% Or.inr +def orRecInfo06 : ConstantInfo := kernelRecInfo% Or.rec +def orInlKernelRuleRhs06 : VExpr := kernelRecRuleRhs% Or.rec 0 +def orInrKernelRuleRhs06 : VExpr := kernelRecRuleRhs% Or.rec 1 + +def orKernelType06 : InductiveType where + name := orInfo06.name + type := orInfo06.type + ctors := [ + { name := orInlInfo06.name, type := orInlInfo06.type }, + { name := orInrInfo06.name, type := orInrInfo06.type }] + +def orEliminationResult06 := + AddInductive.NormalizationEliminationExecution.buildExecution 2 + [orKernelType06] 0 false (spec06Context orInfo06.levelParams) + +theorem orEliminationResult06_isOk : orEliminationResult06.isOk = true := by + native_decide + +def orProducedExecution06 : + { execution // orEliminationResult06 = .ok execution } := + match h : orEliminationResult06 with + | .ok execution => ⟨execution, rfl⟩ + | .error _ => by + have hOk := orEliminationResult06_isOk + rw [h] at hOk + contradiction + +def orExecution06 := orProducedExecution06.val + +def orGeneration06 : VInductDecl.GenerationChecked orDecl := + (VInductDecl.identityGeneration? orDecl).get (by decide) + +def orAlignment06 : + AddInductive.CheckerEliminationRun orGeneration06 orExecution06 := + (AddInductive.CheckerEliminationRun.build? orGeneration06 orExecution06).get + (by native_decide) + +example : orExecution06.elimination.large.result = false := by native_decide +example : orExecution06.kTarget.result = false := by native_decide +example : orExecution06.kTarget.singleton = none := by native_decide +example : orExecution06.elimination.level = .zero := + Level.isStructEq_eq (by native_decide) +example : orExecution06.recLevelParams = [] := by native_decide +example : orExecution06.recLevels = [] := by native_decide +example : recursorShape06 orRecInfo06 = + ([], 2, 0, 1, 2, false, [(``Or.inl, 1), (``Or.inr, 1)]) := rfl +example : orInlKernelRuleRhs06 = orChecked.generatedRules[0].rhs := rfl +example : orInrKernelRuleRhs06 = orChecked.generatedRules[1].rhs := rfl +example : orGeneration06.elimination = .small := + orAlignment06.small_result_iff.mp (by native_decide) +example : orGeneration06.kTarget = false := + orAlignment06.kTarget_result_false_iff.mp (by native_decide) + +/-! ## And: singleton proof fields permit large elimination -/ + +def andInfo06 : ConstantInfo := kernelInductInfo% And +def andIntroInfo06 : ConstantInfo := kernelCtorInfo% And.intro +def andRecInfo06 : ConstantInfo := kernelRecInfo% And.rec +def andKernelRuleRhs06 : VExpr := kernelRecRuleRhs% And.rec 0 + +def andKernelType06 : InductiveType where + name := andInfo06.name + type := andInfo06.type + ctors := [{ name := andIntroInfo06.name, type := andIntroInfo06.type }] + +def andEliminationResult06 := + AddInductive.NormalizationEliminationExecution.buildExecution 2 + [andKernelType06] 0 false (spec06Context andInfo06.levelParams) + +theorem andEliminationResult06_isOk : andEliminationResult06.isOk = true := by + native_decide + +def andProducedExecution06 : + { execution // andEliminationResult06 = .ok execution } := + match h : andEliminationResult06 with + | .ok execution => ⟨execution, rfl⟩ + | .error _ => by + have hOk := andEliminationResult06_isOk + rw [h] at hOk + contradiction + +def andExecution06 := andProducedExecution06.val + +def andGeneration06 : VInductDecl.GenerationChecked andDecl := + (VInductDecl.identityGeneration? andDecl).get (by decide) + +def andAlignment06 : + AddInductive.CheckerEliminationRun andGeneration06 andExecution06 := + (AddInductive.CheckerEliminationRun.build? andGeneration06 andExecution06).get + (by native_decide) + +def andSingletonExecution06 := + andExecution06.elimination.large.singleton.get (by native_decide) + +def andKTargetSingleton06 := + andExecution06.kTarget.singleton.get (by native_decide) + +example : andExecution06.elimination.large.result = true := by native_decide +example : andExecution06.kTarget.result = false := by native_decide +example : andKTargetSingleton06.trace.parameterCount = 2 := by native_decide +example : andKTargetSingleton06.trace.fieldCount = 1 := by native_decide +example : andExecution06.elimination.level = .param `u := + Level.isStructEq_eq (by native_decide) +example : andExecution06.recLevelParams = [`u] := by native_decide +example : andExecution06.recLevels = [.param `u] := + levelListStructEq06_eq (by native_decide) +example : recursorShape06 andRecInfo06 = + ([`u], 2, 0, 1, 1, false, [(``And.intro, 2)]) := rfl +example : andSingletonExecution06.trace.parameterCount = 2 := by native_decide +example : andSingletonExecution06.trace.proofFieldCount = 2 := by native_decide +example : andSingletonExecution06.trace.dataFieldCount = 0 := by native_decide +example : andKernelRuleRhs06 = andChecked.generatedRules[0].rhs := rfl +example : andGeneration06.elimination = .large := + andAlignment06.large_result_iff.mp (by native_decide) +example : andGeneration06.kTarget = false := + andAlignment06.kTarget_result_false_iff.mp (by native_decide) + +end Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/EliminationFixturesSmall.lean b/Ix/Theory/Named/Verify/Environment/EliminationFixturesSmall.lean new file mode 100644 index 000000000..309021ddf --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/EliminationFixturesSmall.lean @@ -0,0 +1,100 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.Environment.EliminationFixturesCommon + +/-! Exact Spec-06B source-universe small-elimination differential fixture. -/ + +namespace Ix.Theory.Named.InductiveReplayFixtures +open Lean Meta Elab Term +open Ix.Theory.Named.InductiveFixtures + +def smallSourceInfo06 : ConstantInfo := kernelInductInfo% Spec06SmallSource +def smallSourceLeftInfo06 : ConstantInfo := + kernelCtorInfo% Spec06SmallSource.left +def smallSourceRightInfo06 : ConstantInfo := + kernelCtorInfo% Spec06SmallSource.right +def smallSourceRecInfo06 : ConstantInfo := + kernelRecInfo% Spec06SmallSource.rec +def smallSourceLeftRuleRhs06 : VExpr := + kernelRecRuleRhs% Spec06SmallSource.rec 0 +def smallSourceRightRuleRhs06 : VExpr := + kernelRecRuleRhs% Spec06SmallSource.rec 1 + +def smallSourceType06 : VInductiveType where + name := ``Spec06SmallSource + uvars := 1 + type := vconst(type_of% @Spec06SmallSource).type + ctors := [ + ⟨vconst(type_of% @Spec06SmallSource.left), ``Spec06SmallSource.left⟩, + ⟨vconst(type_of% @Spec06SmallSource.right), ``Spec06SmallSource.right⟩] + +def smallSourceDecl06 : VInductDecl := ⟨1, 1, [smallSourceType06]⟩ + +def smallSourceChecked06 : smallSourceDecl06.Checked := + smallSourceDecl06.checked?.get (by decide) + +def smallSourceGeneration06 : + VInductDecl.GenerationChecked smallSourceDecl06 := + (VInductDecl.identityGeneration? smallSourceDecl06).get (by decide) + +def smallSourceKernelType06 : InductiveType where + name := smallSourceInfo06.name + type := smallSourceInfo06.type + ctors := [ + { name := smallSourceLeftInfo06.name, type := smallSourceLeftInfo06.type }, + { name := smallSourceRightInfo06.name, type := smallSourceRightInfo06.type }] + +def smallSourceEliminationResult06 := + AddInductive.NormalizationEliminationExecution.buildExecution 1 + [smallSourceKernelType06] 0 false + (spec06Context smallSourceInfo06.levelParams) + +theorem smallSourceEliminationResult06_isOk : + smallSourceEliminationResult06.isOk = true := by + native_decide + +def smallSourceProducedExecution06 : + { execution // smallSourceEliminationResult06 = .ok execution } := + match h : smallSourceEliminationResult06 with + | .ok execution => ⟨execution, rfl⟩ + | .error _ => by + have hOk := smallSourceEliminationResult06_isOk + rw [h] at hOk + contradiction + +def smallSourceExecution06 := smallSourceProducedExecution06.val + +def smallSourceAlignment06 : AddInductive.CheckerEliminationRun + smallSourceGeneration06 smallSourceExecution06 := + (AddInductive.CheckerEliminationRun.build? smallSourceGeneration06 + smallSourceExecution06).get (by native_decide) + +example : smallSourceInfo06.levelParams = [`u] := rfl +example : smallSourceExecution06.elimination.large.result = false := by + native_decide +example : smallSourceExecution06.kTarget.result = false := by native_decide +example : smallSourceExecution06.kTarget.singleton = none := by native_decide +example : smallSourceExecution06.elimination.level = .zero := + Level.isStructEq_eq (by native_decide) +example : smallSourceExecution06.recLevelParams = [`u] := by native_decide +example : smallSourceExecution06.recLevels = [.param `u] := + levelListStructEq06_eq (by native_decide) +example : recursorShape06 smallSourceRecInfo06 = + ([`u], 1, 0, 1, 2, false, + [(``Spec06SmallSource.left, 0), (``Spec06SmallSource.right, 0)]) := rfl +example : smallSourceChecked06.elimination = .small := + smallSourceAlignment06.small_result_iff.mp (by native_decide) +example : smallSourceGeneration06.kTarget = false := + smallSourceAlignment06.kTarget_result_false_iff.mp (by native_decide) +example : smallSourceGeneration06.recursor = + vconst(type_of% @Spec06SmallSource.rec) := rfl +example : smallSourceLeftRuleRhs06 = + smallSourceGeneration06.generatedRules[0].rhs := rfl +example : smallSourceRightRuleRhs06 = + smallSourceGeneration06.generatedRules[1].rhs := rfl + +end Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/IndexedVecCandidate.lean b/Ix/Theory/Named/Verify/Environment/IndexedVecCandidate.lean new file mode 100644 index 000000000..8d305f876 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/IndexedVecCandidate.lean @@ -0,0 +1,1686 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Verify.Environment.InductiveFixtures + +namespace Ix.Theory.Named.InductiveReplayFixtures +open Lean Meta +open Ix.Theory.Named.InductiveFixtures + +def indexedVecKernelEnv : Kernel.Environment := + Kernel.Environment.ofConstants `_indexedVecCandidate natMap + +def indexedVecFamilyCandidateContext : + Ix.Theory.Named.AddInductive.Context where + env := indexedVecKernelEnv + lparams := indexedVecInfo.levelParams + safety := .safe + allowPrimitive := false + +private theorem indexedVecKernel_lookup_nat : + indexedVecKernelEnv.find? ``Nat = some natInfo := by + change natMap.find?' ``Nat = some natInfo + rw [natMap_wf.find?'_eq_find?, nat_type_map_lookup] + +private theorem indexedVecKernel_lookup_zero : + indexedVecKernelEnv.find? ``Nat.zero = some natZeroInfo := by + change natMap.find?' ``Nat.zero = some natZeroInfo + rw [natMap_wf.find?'_eq_find?, natMap, + natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert] + rfl + +private theorem indexedVecKernel_lookup_succ : + indexedVecKernelEnv.find? ``Nat.succ = some natSuccInfo := by + change natMap.find?' ``Nat.succ = some natSuccInfo + rw [natMap_wf.find?'_eq_find?, nat_succ_map_lookup] + +@[simp] private theorem indexedVecKernel_get_nat : + indexedVecKernelEnv.get ``Nat = .ok natInfo := by + simp only [Kernel.Environment.get, indexedVecKernel_lookup_nat, + Pure.pure, Except.pure] + +@[simp] private theorem indexedVecKernel_get_zero : + indexedVecKernelEnv.get ``Nat.zero = .ok natZeroInfo := by + simp only [Kernel.Environment.get, indexedVecKernel_lookup_zero, + Pure.pure, Except.pure] + +@[simp] private theorem indexedVecKernel_get_succ : + indexedVecKernelEnv.get ``Nat.succ = .ok natSuccInfo := by + simp only [Kernel.Environment.get, indexedVecKernel_lookup_succ, + Pure.pure, Except.pure] + +private def indexedVecParamName : Name := + indexedVecInfo.type.bindingName! + +private def indexedVecIndexName : Name := + indexedVecInfo.type.bindingBody!.bindingName! + +private def indexedVecParamCandidateContext : + Ix.Theory.Named.AddInductive.Context := + indexedVecFamilyCandidateContext.pushLocalDecl + indexedVecParamName .default (.sort (.succ (.param `u))) + +private def indexedVecIndexCandidateContext : + Ix.Theory.Named.AddInductive.Context := + indexedVecParamCandidateContext.pushLocalDecl + indexedVecIndexName .default (.const ``Nat []) + +private def indexedVecInnerKernel : Expr := + .forallE indexedVecIndexName (.const ``Nat []) + (.sort (.succ (.param `u))) .default + +private def indexedVecTerminalKernel : Expr := + .sort (.succ (.param `u)) + +@[simp] private theorem indexedVecInnerKernel_instantiate1 (arg : Expr) : + indexedVecInnerKernel.instantiate1 arg = indexedVecInnerKernel := by + simp [indexedVecInnerKernel, Expr.instantiate1_eq, Expr.instantiate1'] + +@[simp] private theorem indexedVecTerminalKernel_instantiate1 (arg : Expr) : + indexedVecTerminalKernel.instantiate1 arg = + indexedVecTerminalKernel := by + simp [indexedVecTerminalKernel, Expr.instantiate1_eq, + Expr.instantiate1'] + +@[simp] private theorem indexedVecInfo_levelParams : + indexedVecInfo.levelParams = [`u] := rfl + +/-- Reflexive binder-domain equality is an exact successful ordinary-checker +run in any candidate context with positive recursive fuel. -/ +theorem candidateIsDefEqSelfValid + (context : Ix.Theory.Named.AddInductive.Context) (e : Expr) + (fuel : Nat) (hfuel : context.fuel.recDepth = fuel + 1) : + Ix.Theory.Named.AddInductive.CandidateIsDefEqStep.Valid ⟨context, e, e⟩ := by + unfold Ix.Theory.Named.AddInductive.CandidateIsDefEqStep.Valid + unfold Ix.Theory.Named.TypeChecker.M.run Ix.Theory.Named.TypeChecker.isDefEq + Ix.Theory.Named.TypeChecker.RecM.run + simp [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, StateT.bind, Except.bind, Bind.bind, + StateT.pure, Except.pure, Pure.pure, + StateT.run', Functor.map, Except.map] + rw [hfuel] + change Except.map (fun x : Bool × Ix.Theory.Named.TypeChecker.State => x.1) + (Ix.Theory.Named.TypeChecker.Inner.isDefEq e e + (Ix.Theory.Named.TypeChecker.Methods.withFuel (fuel + 1)) + context.toTypeChecker ({} : Ix.Theory.Named.TypeChecker.State)) = .ok true + unfold Ix.Theory.Named.TypeChecker.Inner.isDefEq + rw [if_pos (Expr.eqv_refl e)] + rfl + +def indexedVecTypeCheckerContext + (lctx : LocalContext) : Ix.Theory.Named.TypeChecker.Context where + env := indexedVecKernelEnv + lctx := lctx + lparams := [`u] + +@[simp] private theorem indexedVecFamily_checkLevel : + Ix.Theory.Named.TypeChecker.Inner.checkLevel + indexedVecFamilyCandidateContext.toTypeChecker + (.succ (.param `u)) = .ok () := by + simp [Ix.Theory.Named.TypeChecker.Inner.checkLevel, + indexedVecFamilyCandidateContext, indexedVecInfo, + Ix.Theory.Named.AddInductive.Context.toTypeChecker, + Level.getUndefParam, Level.forEach, + Level.hasParam_eq, Level.hasParam'] + rfl + +@[simp] private theorem indexedVec_checkLevel + (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.Inner.checkLevel + (indexedVecTypeCheckerContext lctx) + (.succ (.param `u)) = .ok () := by + simp [Ix.Theory.Named.TypeChecker.Inner.checkLevel, + indexedVecTypeCheckerContext, + Level.getUndefParam, Level.forEach, + Level.hasParam_eq, Level.hasParam'] + rfl + +@[simp] private theorem indexedVecRecMGet (methods context state) : + (get : Ix.Theory.Named.TypeChecker.RecM Ix.Theory.Named.TypeChecker.State) + methods context state = .ok (state, state) := rfl + +@[simp] private theorem indexedVecRecMReadContext + (methods context state) : + (readThe Ix.Theory.Named.TypeChecker.Context : + Ix.Theory.Named.TypeChecker.RecM Ix.Theory.Named.TypeChecker.Context) + methods context state = .ok (context, state) := rfl + +@[simp] private theorem indexedVecRecMModify + (f : Ix.Theory.Named.TypeChecker.State → Ix.Theory.Named.TypeChecker.State) + (methods context state) : + (modify f : Ix.Theory.Named.TypeChecker.RecM PUnit) + methods context state = .ok (.unit, f state) := rfl + +@[simp] private theorem indexedVecRecMPure + {α} (a : α) (methods context state) : + (pure a : Ix.Theory.Named.TypeChecker.RecM α) + methods context state = .ok (a, state) := rfl + +@[simp] private theorem indexedVecRecMBind + {α β} (x : Ix.Theory.Named.TypeChecker.RecM α) + (f : α → Ix.Theory.Named.TypeChecker.RecM β) + (methods context state) : + (x >>= f) methods context state = + match x methods context state with + | .error e => .error e + | .ok (a, state') => f a methods context state' := by + simp [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + cases h : x methods context state with + | error => rfl + | ok value => cases value; rfl + +@[simp] private theorem indexedVecRecMLiftExceptOk + {α} (a : α) (methods context state) : + (liftM (.ok a : Except Kernel.Exception α) : + Ix.Theory.Named.TypeChecker.RecM α) methods context state = + .ok (a, state) := rfl + +@[simp] private theorem indexedVecGetNGen + (context : Ix.Theory.Named.TypeChecker.Context) + (state : Ix.Theory.Named.TypeChecker.State) : + (getNGen : Ix.Theory.Named.TypeChecker.M NameGenerator) context state = + .ok (state.ngen, state) := rfl + +@[simp] private theorem indexedVecSetNGen + (ngen : NameGenerator) (context : Ix.Theory.Named.TypeChecker.Context) + (state : Ix.Theory.Named.TypeChecker.State) : + (setNGen ngen : Ix.Theory.Named.TypeChecker.M PUnit) context state = + .ok (.unit, { state with ngen }) := rfl + +@[simp] private theorem indexedVecMPure + {α} (a : α) (context : Ix.Theory.Named.TypeChecker.Context) + (state : Ix.Theory.Named.TypeChecker.State) : + (pure a : Ix.Theory.Named.TypeChecker.M α) context state = + .ok (a, state) := rfl + +@[simp] private theorem indexedVecRecMWithReader + {α} (f : LocalContext → LocalContext) + (x : Ix.Theory.Named.TypeChecker.RecM α) + (methods : Ix.Theory.Named.TypeChecker.Methods) + (context : Ix.Theory.Named.TypeChecker.Context) + (state : Ix.Theory.Named.TypeChecker.State) : + (MonadWithReaderOf.withReader (m := Ix.Theory.Named.TypeChecker.RecM) f x) + methods context state = + x methods { context with lctx := f context.lctx } state := rfl + +private theorem indexedVecWithLocalDecl + {α} (name : Name) (bi : BinderInfo) (ty : Expr) + (k : Expr → Ix.Theory.Named.TypeChecker.RecM α) + (methods : Ix.Theory.Named.TypeChecker.Methods) + (context : Ix.Theory.Named.TypeChecker.Context) + (state : Ix.Theory.Named.TypeChecker.State) : + (withLocalDecl (m := Ix.Theory.Named.TypeChecker.RecM) name bi ty k) + methods context state = + k (.fvar ⟨state.ngen.curr⟩) methods + { context with lctx := + (context.lctx.mkLocalDecl ⟨state.ngen.curr⟩ name ty bi) } + { state with ngen := state.ngen.next } := rfl + +@[simp] private theorem indexedVecEnsureSort + (u : Level) (source : Expr) + (methods : Ix.Theory.Named.TypeChecker.Methods) + (context : Ix.Theory.Named.TypeChecker.Context) + (state : Ix.Theory.Named.TypeChecker.State) : + Ix.Theory.Named.TypeChecker.Inner.ensureSortCore (.sort u) source + methods context state = .ok (.sort u, state) := by + rfl + +private theorem indexedVecInferTypeFuel + (n e inferOnly context state) : + Ix.Theory.Named.TypeChecker.Inner.inferType e inferOnly + (Ix.Theory.Named.TypeChecker.Methods.withFuel (n + 1)) context state = + Ix.Theory.Named.TypeChecker.Inner.inferType' e inferOnly + (Ix.Theory.Named.TypeChecker.Methods.withFuel n) context state := rfl + +@[simp] private theorem indexedVecInferTypeSortCore + (n : Nat) (lctx : LocalContext) + (state : Ix.Theory.Named.TypeChecker.State) + (hcache : state.inferTypeC[(.sort (.succ (.param `u)) : Expr)]? = none) : + Ix.Theory.Named.TypeChecker.Inner.inferType' + (.sort (.succ (.param `u))) false + (Ix.Theory.Named.TypeChecker.Methods.withFuel n) + (indexedVecTypeCheckerContext lctx) + state = + .ok (.sort (.succ (.succ (.param `u))), + { state with inferTypeC := + (state.inferTypeC.insert (.sort (.succ (.param `u))) + (.sort (.succ (.succ (.param `u))))) }) := by + unfold Ix.Theory.Named.TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +@[simp] private theorem indexedVecInferTypeSortCachedCore + (n : Nat) (lctx : LocalContext) + (state : Ix.Theory.Named.TypeChecker.State) + (hcache : state.inferTypeC[(.sort (.succ (.param `u)) : Expr)]? = + some (.sort (.succ (.succ (.param `u))))) : + Ix.Theory.Named.TypeChecker.Inner.inferType' + (.sort (.succ (.param `u))) false + (Ix.Theory.Named.TypeChecker.Methods.withFuel n) + (indexedVecTypeCheckerContext lctx) + state = .ok (.sort (.succ (.succ (.param `u))), state) := by + unfold Ix.Theory.Named.TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache] + +@[simp] private theorem indexedVecInferConstantNat + (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.Inner.inferConstant + (indexedVecTypeCheckerContext lctx) ``Nat [] false = + .ok (.sort (.succ .zero)) := by + unfold Ix.Theory.Named.TypeChecker.Inner.inferConstant + simp [indexedVecTypeCheckerContext, indexedVecKernel_get_nat, + natInfo, ConstantInfo.levelParams, ConstantInfo.isUnsafe, + ConstantInfo.instantiateTypeLevelParams, ConstantInfo.toConstantVal, + ConstantVal.instantiateTypeLevelParams, + Expr.instantiateLevelParams_eq, Expr.instantiateLevelParamsCore', + Level.substParams', Bind.bind, Except.bind, + Pure.pure, Except.pure] + +@[simp] theorem indexedVecPreFamilyInferConstantZero + (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.Inner.inferConstant + (indexedVecTypeCheckerContext lctx) ``Nat.zero [] false = + .ok (.const ``Nat []) := by + unfold Ix.Theory.Named.TypeChecker.Inner.inferConstant + simp [indexedVecTypeCheckerContext, indexedVecKernel_get_zero, + natZeroInfo, ConstantInfo.levelParams, ConstantInfo.isUnsafe, + ConstantInfo.instantiateTypeLevelParams, ConstantInfo.toConstantVal, + ConstantVal.instantiateTypeLevelParams, + Expr.instantiateLevelParams_eq, Expr.instantiateLevelParamsCore', + Bind.bind, Except.bind, Pure.pure, Except.pure] + +@[simp] theorem indexedVecPreFamilyInferConstantSucc + (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.Inner.inferConstant + (indexedVecTypeCheckerContext lctx) ``Nat.succ [] false = + .ok (.forallE `n (.const ``Nat []) (.const ``Nat []) .default) := by + unfold Ix.Theory.Named.TypeChecker.Inner.inferConstant + simp [indexedVecTypeCheckerContext, indexedVecKernel_get_succ, + natSuccInfo, ConstantInfo.levelParams, ConstantInfo.isUnsafe, + ConstantInfo.instantiateTypeLevelParams, ConstantInfo.toConstantVal, + ConstantVal.instantiateTypeLevelParams, + Expr.instantiateLevelParams_eq, Expr.instantiateLevelParamsCore', + Bind.bind, Except.bind, Pure.pure, Except.pure] + +@[simp] private theorem indexedVecInferTypeNatCore + (n : Nat) (lctx : LocalContext) + (state : Ix.Theory.Named.TypeChecker.State) + (hcache : state.inferTypeC[(.const ``Nat [] : Expr)]? = none) : + Ix.Theory.Named.TypeChecker.Inner.inferType' (.const ``Nat []) false + (Ix.Theory.Named.TypeChecker.Methods.withFuel n) + (indexedVecTypeCheckerContext lctx) + state = + .ok (.sort (.succ .zero), + { state with inferTypeC := + (state.inferTypeC.insert (.const ``Nat []) + (.sort (.succ .zero))) }) := by + unfold Ix.Theory.Named.TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache, + indexedVecInferConstantNat, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +theorem indexedVecPreFamilyInferTypeFVarCore + (n : Nat) (lctx : LocalContext) + (state : Ix.Theory.Named.TypeChecker.State) (id : FVarId) (type : Expr) + (hcache : state.inferTypeC[(.fvar id : Expr)]? = none) + (hfind : lctx.find? id = + some (.cdecl index id name type bi kind)) : + Ix.Theory.Named.TypeChecker.Inner.inferType' (.fvar id) false + (Ix.Theory.Named.TypeChecker.Methods.withFuel n) + (indexedVecTypeCheckerContext lctx) state = + .ok (type, { state with inferTypeC := + state.inferTypeC.insert (.fvar id) type }) := by + unfold Ix.Theory.Named.TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache, + Ix.Theory.Named.TypeChecker.Inner.inferFVar, + indexedVecTypeCheckerContext, hfind, LocalDecl.type, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +theorem indexedVecPreFamilyInferTypeZeroCore + (n : Nat) (lctx : LocalContext) + (state : Ix.Theory.Named.TypeChecker.State) + (hcache : state.inferTypeC[(.const ``Nat.zero [] : Expr)]? = none) : + Ix.Theory.Named.TypeChecker.Inner.inferType' (.const ``Nat.zero []) false + (Ix.Theory.Named.TypeChecker.Methods.withFuel n) + (indexedVecTypeCheckerContext lctx) state = + .ok (.const ``Nat [], { state with inferTypeC := + (state.inferTypeC.insert (.const ``Nat.zero []) + (.const ``Nat [])) }) := by + unfold Ix.Theory.Named.TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache, + indexedVecPreFamilyInferConstantZero, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +theorem indexedVecPreFamilyInferTypeSuccCore + (n : Nat) (lctx : LocalContext) + (state : Ix.Theory.Named.TypeChecker.State) + (hcache : state.inferTypeC[(.const ``Nat.succ [] : Expr)]? = none) : + Ix.Theory.Named.TypeChecker.Inner.inferType' (.const ``Nat.succ []) false + (Ix.Theory.Named.TypeChecker.Methods.withFuel n) + (indexedVecTypeCheckerContext lctx) state = + .ok (.forallE `n (.const ``Nat []) (.const ``Nat []) .default, + { state with inferTypeC := (state.inferTypeC.insert + (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default)) }) := by + unfold Ix.Theory.Named.TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache, + indexedVecPreFamilyInferConstantSucc, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +private def indexedVecRootSortState : Ix.Theory.Named.TypeChecker.State := + { ({} : Ix.Theory.Named.TypeChecker.State) with inferTypeC := + (({} : Ix.Theory.Named.TypeChecker.State).inferTypeC.insert + (.sort (.succ (.param `u))) + (.sort (.succ (.succ (.param `u))))) } + +@[simp] private theorem indexedVecRootSortCore : + Ix.Theory.Named.TypeChecker.Inner.inferType' + (.sort (.succ (.param `u))) false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 9998) + indexedVecFamilyCandidateContext.toTypeChecker + ({} : Ix.Theory.Named.TypeChecker.State) = + .ok (.sort (.succ (.succ (.param `u))), + indexedVecRootSortState) := by + simpa [indexedVecFamilyCandidateContext, + Ix.Theory.Named.AddInductive.Context.toTypeChecker, + indexedVecInfo, ConstantInfo.levelParams, + ConstantInfo.toConstantVal, indexedVecTypeCheckerContext, + indexedVecRootSortState] using + (indexedVecInferTypeSortCore 9998 ({} : LocalContext) + ({} : Ix.Theory.Named.TypeChecker.State) Std.HashMap.getElem?_empty) + +private def indexedVecParamLctx : LocalContext := + ({} : LocalContext).mkLocalDecl + ⟨indexedVecRootSortState.ngen.curr⟩ indexedVecParamName + (.sort (.succ (.param `u))) .default + +private def indexedVecAfterParamState : Ix.Theory.Named.TypeChecker.State := + { indexedVecRootSortState with + ngen := indexedVecRootSortState.ngen.next } + +private def indexedVecNatState : Ix.Theory.Named.TypeChecker.State := + { indexedVecAfterParamState with + inferTypeC := indexedVecAfterParamState.inferTypeC.insert + (.const ``Nat []) (.sort (.succ .zero)) } + +private def indexedVecIndexLctx : LocalContext := + indexedVecParamLctx.mkLocalDecl + ⟨indexedVecNatState.ngen.curr⟩ indexedVecIndexName + (.const ``Nat []) .default + +private def indexedVecAfterIndexState : Ix.Theory.Named.TypeChecker.State := + { indexedVecNatState with ngen := indexedVecNatState.ngen.next } + +private theorem indexedVecOuterWithLocalDecl + {α} (k : Expr → Ix.Theory.Named.TypeChecker.RecM α) + (methods : Ix.Theory.Named.TypeChecker.Methods) : + (withLocalDecl (m := Ix.Theory.Named.TypeChecker.RecM) + indexedVecParamName .default (.sort (.succ (.param `u))) k) + methods indexedVecFamilyCandidateContext.toTypeChecker + indexedVecRootSortState = + k (.fvar ⟨indexedVecRootSortState.ngen.curr⟩) methods + { indexedVecFamilyCandidateContext.toTypeChecker with + lctx := indexedVecParamLctx } + indexedVecAfterParamState := by + simpa [indexedVecParamLctx, indexedVecAfterParamState, + indexedVecFamilyCandidateContext, + Ix.Theory.Named.AddInductive.Context.toTypeChecker] using + (indexedVecWithLocalDecl indexedVecParamName .default + (.sort (.succ (.param `u))) k methods + indexedVecFamilyCandidateContext.toTypeChecker indexedVecRootSortState) + +private theorem indexedVecInnerWithLocalDecl + {α} (k : Expr → Ix.Theory.Named.TypeChecker.RecM α) + (methods : Ix.Theory.Named.TypeChecker.Methods) : + (withLocalDecl (m := Ix.Theory.Named.TypeChecker.RecM) + indexedVecIndexName .default (.const ``Nat []) k) + methods + { indexedVecFamilyCandidateContext.toTypeChecker with + lctx := indexedVecParamLctx } + indexedVecNatState = + k (.fvar ⟨indexedVecNatState.ngen.curr⟩) methods + { indexedVecFamilyCandidateContext.toTypeChecker with + lctx := indexedVecIndexLctx } + indexedVecAfterIndexState := by + simpa [indexedVecIndexLctx, indexedVecAfterIndexState] using + (indexedVecWithLocalDecl indexedVecIndexName .default + (.const ``Nat []) k methods + { indexedVecFamilyCandidateContext.toTypeChecker with + lctx := indexedVecParamLctx } + indexedVecNatState) + +@[simp] private theorem indexedVecNat_beq_sort : + ((.const ``Nat [] : Expr) == .sort (.succ (.param `u))) = false := by + change Expr.eqv (.const ``Nat []) (.sort (.succ (.param `u))) = false + rw [Expr.eqv_eq] + rfl + +@[simp] private theorem indexedVecAfterIndexState_sort_cache : + indexedVecAfterIndexState.inferTypeC[ + (.sort (.succ (.param `u)) : Expr)]? = + some (.sort (.succ (.succ (.param `u)))) := by + change + (((({} : Ix.Theory.Named.InferCache).insert + (.sort (.succ (.param `u))) + (.sort (.succ (.succ (.param `u))))).insert + (.const ``Nat []) (.sort (.succ .zero)))[ + (.sort (.succ (.param `u)) : Expr)]?) = _ + rw [Std.HashMap.getElem?_insert, indexedVecNat_beq_sort] + exact Std.HashMap.getElem?_insert_self + +@[simp] private theorem indexedVecParamNatCore : + Ix.Theory.Named.TypeChecker.Inner.inferType' (.const ``Nat []) false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 9998) + { indexedVecFamilyCandidateContext.toTypeChecker with + lctx := indexedVecParamLctx } + indexedVecAfterParamState = + .ok (.sort (.succ .zero), indexedVecNatState) := by + simpa [indexedVecFamilyCandidateContext, + Ix.Theory.Named.AddInductive.Context.toTypeChecker, + indexedVecInfo, ConstantInfo.levelParams, + ConstantInfo.toConstantVal, indexedVecTypeCheckerContext, + indexedVecAfterParamState, indexedVecRootSortState, + indexedVecNatState] using + (indexedVecInferTypeNatCore 9998 indexedVecParamLctx + indexedVecAfterParamState (by + simp [indexedVecAfterParamState, indexedVecRootSortState])) + +@[simp] private theorem indexedVecTerminalSortCore : + Ix.Theory.Named.TypeChecker.Inner.inferType' + (.sort (.succ (.param `u))) false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 9998) + { indexedVecFamilyCandidateContext.toTypeChecker with + lctx := indexedVecIndexLctx } + indexedVecAfterIndexState = + .ok (.sort (.succ (.succ (.param `u))), + indexedVecAfterIndexState) := by + simpa [indexedVecFamilyCandidateContext, + Ix.Theory.Named.AddInductive.Context.toTypeChecker, + indexedVecInfo, ConstantInfo.levelParams, + ConstantInfo.toConstantVal, indexedVecTypeCheckerContext, + indexedVecAfterIndexState, indexedVecNatState, + indexedVecAfterParamState, indexedVecRootSortState] using + (indexedVecInferTypeSortCachedCore 9998 indexedVecIndexLctx + indexedVecAfterIndexState indexedVecAfterIndexState_sort_cache) + +private def indexedVecFamilyInferredLevel : Level := + mkLevelIMax' (.succ (.succ (.param `u))) + (mkLevelIMax' (.succ .zero) (.succ (.succ (.param `u)))) + +private theorem indexedVecFamilyInferForall : + Ix.Theory.Named.TypeChecker.Inner.inferForall + (.forallE indexedVecParamName (.sort (.succ (.param `u))) + (.forallE indexedVecIndexName (.const ``Nat []) + (.sort (.succ (.param `u))) .default) .default) + false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 9999) + indexedVecFamilyCandidateContext.toTypeChecker + ({} : Ix.Theory.Named.TypeChecker.State) = + .ok (.sort indexedVecFamilyInferredLevel, + indexedVecAfterIndexState) := by + unfold Ix.Theory.Named.TypeChecker.Inner.inferForall + simp only [Ix.Theory.Named.TypeChecker.Inner.inferForall.loop] + rw [show + (.sort (.succ (.param `u)) : Expr).instantiateRev #[] = + .sort (.succ (.param `u)) by + simp [Expr.instantiateRev_eq, Expr.instantiate_eq]] + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [indexedVecInferTypeFuel 9998] + rw [indexedVecRootSortCore] + simp only + rw [indexedVecEnsureSort] + simp only + rw [indexedVecOuterWithLocalDecl] + rw [show + (.const ``Nat [] : Expr).instantiateRev + (#[] |>.push (.fvar ⟨indexedVecRootSortState.ngen.curr⟩)) = + .const ``Nat [] by + simp [Expr.instantiateRev_eq, Expr.instantiate_eq, + Expr.instantiate1']] + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [indexedVecInferTypeFuel 9998] + rw [indexedVecParamNatCore] + simp only + rw [indexedVecEnsureSort] + simp only + rw [indexedVecInnerWithLocalDecl] + rw [show + (.sort (.succ (.param `u)) : Expr).instantiateRev + ((#[] |>.push + (.fvar ⟨indexedVecRootSortState.ngen.curr⟩)).push + (.fvar ⟨indexedVecNatState.ngen.curr⟩)) = + .sort (.succ (.param `u)) by + simp [Expr.instantiateRev_eq, Expr.instantiate_eq, + Expr.instantiate1']] + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [indexedVecInferTypeFuel 9998] + rw [indexedVecTerminalSortCore] + simp only + rw [indexedVecEnsureSort] + simp [indexedVecFamilyInferredLevel, Expr.sortLevel!, + Pure.pure, ReaderT.pure, + StateT.pure, Except.pure] + +private def indexedVecFamilyCheckedState : Ix.Theory.Named.TypeChecker.State := + { indexedVecAfterIndexState with + inferTypeC := indexedVecAfterIndexState.inferTypeC.insert + (.forallE indexedVecParamName (.sort (.succ (.param `u))) + (.forallE indexedVecIndexName (.const ``Nat []) + (.sort (.succ (.param `u))) .default) .default) + (.sort indexedVecFamilyInferredLevel) } + +private theorem indexedVecFamilyCheckTypeInner : + Ix.Theory.Named.TypeChecker.Inner.inferType indexedVecInfo.type false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 10000) + indexedVecFamilyCandidateContext.toTypeChecker + ({} : Ix.Theory.Named.TypeChecker.State) = + .ok (.sort indexedVecFamilyInferredLevel, + indexedVecFamilyCheckedState) := by + change Ix.Theory.Named.TypeChecker.Inner.inferType' + (.forallE indexedVecParamName (.sort (.succ (.param `u))) + (.forallE indexedVecIndexName (.const ``Nat []) + (.sort (.succ (.param `u))) .default) .default) + false (Ix.Theory.Named.TypeChecker.Methods.withFuel 9999) + indexedVecFamilyCandidateContext.toTypeChecker + ({} : Ix.Theory.Named.TypeChecker.State) = _ + unfold Ix.Theory.Named.TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', + indexedVecFamilyInferForall, indexedVecFamilyCheckedState, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +private theorem indexedVecFamily_whnfM : + Ix.Theory.Named.TypeChecker.M.run + indexedVecFamilyCandidateContext.env + indexedVecFamilyCandidateContext.safety + indexedVecFamilyCandidateContext.lctx + indexedVecFamilyCandidateContext.lparams + indexedVecFamilyCandidateContext.fuel + (Ix.Theory.Named.TypeChecker.whnf indexedVecInfo.type) = + .ok indexedVecInfo.type := by + rfl + +private theorem indexedVecSort_checkTypeM (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.M.run indexedVecKernelEnv .safe lctx [`u] + ({} : FuelConfig) + (Ix.Theory.Named.TypeChecker.checkType (.sort (.succ (.param `u)))) = + .ok (.sort (.succ (.succ (.param `u)))) := by + change Except.map + (fun x : Expr × Ix.Theory.Named.TypeChecker.State => x.1) + (Ix.Theory.Named.TypeChecker.Inner.inferType + (.sort (.succ (.param `u))) false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 10000) + (indexedVecTypeCheckerContext lctx) + ({} : Ix.Theory.Named.TypeChecker.State)) = _ + rw [indexedVecInferTypeFuel 9999] + rw [indexedVecInferTypeSortCore 9999 lctx + ({} : Ix.Theory.Named.TypeChecker.State) Std.HashMap.getElem?_empty] + rfl + +private theorem indexedVecSort_whnfM (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.M.run indexedVecKernelEnv .safe lctx [`u] + ({} : FuelConfig) + (Ix.Theory.Named.TypeChecker.whnf (.sort (.succ (.param `u)))) = + .ok (.sort (.succ (.param `u))) := by + rfl + +private theorem indexedVecNat_checkTypeM (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.M.run indexedVecKernelEnv .safe lctx [`u] + ({} : FuelConfig) + (Ix.Theory.Named.TypeChecker.checkType (.const ``Nat [])) = + .ok (.sort (.succ .zero)) := by + change Except.map + (fun x : Expr × Ix.Theory.Named.TypeChecker.State => x.1) + (Ix.Theory.Named.TypeChecker.Inner.inferType (.const ``Nat []) false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 10000) + (indexedVecTypeCheckerContext lctx) + ({} : Ix.Theory.Named.TypeChecker.State)) = _ + rw [indexedVecInferTypeFuel 9999] + rw [indexedVecInferTypeNatCore 9999 lctx + ({} : Ix.Theory.Named.TypeChecker.State) Std.HashMap.getElem?_empty] + rfl + +private theorem indexedVecUnfoldNat (lctx methods state) : + Ix.Theory.Named.TypeChecker.Inner.unfoldDefinition (.const ``Nat []) + methods (indexedVecTypeCheckerContext lctx) state = + .ok (none, state) := by + change Ix.Theory.Named.TypeChecker.Inner.unfoldDefinitionCore + (.const ``Nat []) methods (indexedVecTypeCheckerContext lctx) state = _ + simp [Ix.Theory.Named.TypeChecker.Inner.unfoldDefinitionCore, + Ix.Theory.Named.TypeChecker.Inner.isDelta, Expr.getAppFn, + indexedVecTypeCheckerContext, indexedVecKernel_lookup_nat, + natInfo, ConstantInfo.deltaValue?, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +private theorem indexedVecWhnfLoopNat (lctx methods state n) : + Ix.Theory.Named.TypeChecker.Inner.whnf'.loop (.const ``Nat []) (n + 1) + methods (indexedVecTypeCheckerContext lctx) state = + .ok (.const ``Nat [], state) := by + unfold Ix.Theory.Named.TypeChecker.Inner.whnf'.loop + simp [indexedVecUnfoldNat] + +private theorem indexedVecNat_whnfM (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.M.run indexedVecKernelEnv .safe lctx [`u] + ({} : FuelConfig) + (Ix.Theory.Named.TypeChecker.whnf (.const ``Nat [])) = + .ok (.const ``Nat []) := by + change Except.map + (fun x : Expr × Ix.Theory.Named.TypeChecker.State => x.1) + (Ix.Theory.Named.TypeChecker.Inner.whnf' (.const ``Nat []) + (Ix.Theory.Named.TypeChecker.Methods.withFuel 9999) + (indexedVecTypeCheckerContext lctx) + ({} : Ix.Theory.Named.TypeChecker.State)) = _ + unfold Ix.Theory.Named.TypeChecker.Inner.whnf' + simp + rw [show + (if (indexedVecTypeCheckerContext lctx).eagerReduce then + (indexedVecTypeCheckerContext lctx).fuel.whnfEager + else (indexedVecTypeCheckerContext lctx).fuel.whnf) = 100000 by rfl] + rw [show 100000 = 99999 + 1 by rfl] + rw [indexedVecWhnfLoopNat] + simp [Functor.map, StateT.map, Except.map] + +private def indexedVecInnerInferredLevel : Level := + mkLevelIMax' (.succ .zero) (.succ (.succ (.param `u))) + +private def indexedVecInnerNatState : Ix.Theory.Named.TypeChecker.State := + { ({} : Ix.Theory.Named.TypeChecker.State) with + inferTypeC := ({} : Ix.Theory.Named.TypeChecker.State).inferTypeC.insert + (.const ``Nat []) (.sort (.succ .zero)) } + +private def indexedVecInnerCheckerLctx : LocalContext := + indexedVecParamCandidateContext.lctx.mkLocalDecl + ⟨indexedVecInnerNatState.ngen.curr⟩ indexedVecIndexName + (.const ``Nat []) .default + +private def indexedVecInnerAfterIndexState : + Ix.Theory.Named.TypeChecker.State := + { indexedVecInnerNatState with + ngen := indexedVecInnerNatState.ngen.next } + +private def indexedVecInnerSortState : Ix.Theory.Named.TypeChecker.State := + { indexedVecInnerAfterIndexState with + inferTypeC := indexedVecInnerAfterIndexState.inferTypeC.insert + (.sort (.succ (.param `u))) + (.sort (.succ (.succ (.param `u)))) } + +private def indexedVecInnerCheckedState : Ix.Theory.Named.TypeChecker.State := + { indexedVecInnerSortState with + inferTypeC := indexedVecInnerSortState.inferTypeC.insert + indexedVecInnerKernel (.sort indexedVecInnerInferredLevel) } + +@[simp] private theorem indexedVecInnerNatCore : + Ix.Theory.Named.TypeChecker.Inner.inferType' (.const ``Nat []) false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 9998) + indexedVecParamCandidateContext.toTypeChecker + ({} : Ix.Theory.Named.TypeChecker.State) = + .ok (.sort (.succ .zero), indexedVecInnerNatState) := by + simpa [indexedVecParamCandidateContext, + indexedVecFamilyCandidateContext, + Ix.Theory.Named.AddInductive.Context.pushLocalDecl, + Ix.Theory.Named.AddInductive.Context.toTypeChecker, + indexedVecInfo, ConstantInfo.levelParams, + ConstantInfo.toConstantVal, indexedVecTypeCheckerContext, + indexedVecInnerNatState] using + (indexedVecInferTypeNatCore 9998 + indexedVecParamCandidateContext.lctx + ({} : Ix.Theory.Named.TypeChecker.State) Std.HashMap.getElem?_empty) + +private theorem indexedVecInnerCheckerWithLocalDecl + {α} (k : Expr → Ix.Theory.Named.TypeChecker.RecM α) + (methods : Ix.Theory.Named.TypeChecker.Methods) : + (withLocalDecl (m := Ix.Theory.Named.TypeChecker.RecM) + indexedVecIndexName .default (.const ``Nat []) k) + methods indexedVecParamCandidateContext.toTypeChecker + indexedVecInnerNatState = + k (.fvar ⟨indexedVecInnerNatState.ngen.curr⟩) methods + { indexedVecParamCandidateContext.toTypeChecker with + lctx := indexedVecInnerCheckerLctx } + indexedVecInnerAfterIndexState := by + simpa [indexedVecInnerCheckerLctx, + indexedVecInnerAfterIndexState, + indexedVecParamCandidateContext, + indexedVecFamilyCandidateContext, + Ix.Theory.Named.AddInductive.Context.pushLocalDecl, + Ix.Theory.Named.AddInductive.Context.toTypeChecker] using + (indexedVecWithLocalDecl indexedVecIndexName .default + (.const ``Nat []) k methods + indexedVecParamCandidateContext.toTypeChecker + indexedVecInnerNatState) + +@[simp] private theorem indexedVecInnerTerminalSortCore : + Ix.Theory.Named.TypeChecker.Inner.inferType' + (.sort (.succ (.param `u))) false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 9998) + { indexedVecParamCandidateContext.toTypeChecker with + lctx := indexedVecInnerCheckerLctx } + indexedVecInnerAfterIndexState = + .ok (.sort (.succ (.succ (.param `u))), + indexedVecInnerSortState) := by + simpa [indexedVecParamCandidateContext, + indexedVecFamilyCandidateContext, + Ix.Theory.Named.AddInductive.Context.pushLocalDecl, + Ix.Theory.Named.AddInductive.Context.toTypeChecker, + indexedVecInfo, ConstantInfo.levelParams, + ConstantInfo.toConstantVal, indexedVecTypeCheckerContext, + indexedVecInnerAfterIndexState, indexedVecInnerNatState, + indexedVecInnerSortState] using + (indexedVecInferTypeSortCore 9998 indexedVecInnerCheckerLctx + indexedVecInnerAfterIndexState (by + simp [indexedVecInnerAfterIndexState, + indexedVecInnerNatState])) + +private theorem indexedVecInnerInferForall : + Ix.Theory.Named.TypeChecker.Inner.inferForall indexedVecInnerKernel false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 9999) + indexedVecParamCandidateContext.toTypeChecker + ({} : Ix.Theory.Named.TypeChecker.State) = + .ok (.sort indexedVecInnerInferredLevel, + indexedVecInnerSortState) := by + unfold indexedVecInnerKernel + unfold Ix.Theory.Named.TypeChecker.Inner.inferForall + simp only [Ix.Theory.Named.TypeChecker.Inner.inferForall.loop] + rw [show (.const ``Nat [] : Expr).instantiateRev #[] = + .const ``Nat [] by + simp [Expr.instantiateRev_eq, Expr.instantiate_eq]] + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [indexedVecInferTypeFuel 9998] + rw [indexedVecInnerNatCore] + simp only + rw [indexedVecEnsureSort] + simp only + rw [indexedVecInnerCheckerWithLocalDecl] + rw [show + (.sort (.succ (.param `u)) : Expr).instantiateRev + (#[] |>.push (.fvar ⟨indexedVecInnerNatState.ngen.curr⟩)) = + .sort (.succ (.param `u)) by + simp [Expr.instantiateRev_eq, Expr.instantiate_eq, + Expr.instantiate1']] + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [indexedVecInferTypeFuel 9998] + rw [indexedVecInnerTerminalSortCore] + simp only + rw [indexedVecEnsureSort] + simp [indexedVecInnerInferredLevel, Expr.sortLevel!, + Pure.pure, ReaderT.pure, StateT.pure, Except.pure] + +private theorem indexedVecInner_checkTypeM : + Ix.Theory.Named.TypeChecker.M.run indexedVecKernelEnv .safe + indexedVecParamCandidateContext.lctx [`u] ({} : FuelConfig) + (Ix.Theory.Named.TypeChecker.checkType indexedVecInnerKernel) = + .ok (.sort indexedVecInnerInferredLevel) := by + change Except.map + (fun x : Expr × Ix.Theory.Named.TypeChecker.State => x.1) + (Ix.Theory.Named.TypeChecker.Inner.inferType indexedVecInnerKernel false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 10000) + indexedVecParamCandidateContext.toTypeChecker + ({} : Ix.Theory.Named.TypeChecker.State)) = _ + change Except.map + (fun x : Expr × Ix.Theory.Named.TypeChecker.State => x.1) + (Ix.Theory.Named.TypeChecker.Inner.inferType' indexedVecInnerKernel false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 9999) + indexedVecParamCandidateContext.toTypeChecker + ({} : Ix.Theory.Named.TypeChecker.State)) = _ + unfold Ix.Theory.Named.TypeChecker.Inner.inferType' + simp [indexedVecInnerKernel, Expr.hasLooseBVars, + Expr.looseBVarRange', + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [show + Ix.Theory.Named.TypeChecker.Inner.inferForall + (.forallE indexedVecIndexName (.const ``Nat []) + (.sort (.succ (.param `u))) .default) + false (Ix.Theory.Named.TypeChecker.Methods.withFuel 9999) + indexedVecParamCandidateContext.toTypeChecker + ({} : Ix.Theory.Named.TypeChecker.State) = + .ok (.sort indexedVecInnerInferredLevel, + indexedVecInnerSortState) by + simpa [indexedVecInnerKernel] using indexedVecInnerInferForall] + rfl + +/-! The constructor pre-family replay runs after family analysis has added its +parameter and index locals, but before `IndexedVec` itself is present in the +kernel environment. The following executions expose the family-free pieces +of the family candidate proof for an arbitrary local context. -/ + +def indexedVecPreFamilyIndexTelescope : Expr := + .forallE indexedVecIndexName (.const ``Nat []) + (.sort (.succ (.param `u))) .default + +theorem indexedVecPreFamilyIndexTelescope_eq : + indexedVecPreFamilyIndexTelescope = indexedVecInfo.type.bindingBody! := by + rfl + +theorem indexedVecPreFamilySortCheckTypeM (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.M.run indexedVecKernelEnv .safe lctx [`u] + ({} : FuelConfig) + (Ix.Theory.Named.TypeChecker.checkType + (.sort (.succ (.param `u)))) = + .ok (.sort (.succ (.succ (.param `u)))) := + indexedVecSort_checkTypeM lctx + +theorem indexedVecPreFamilyNatCheckTypeM (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.M.run indexedVecKernelEnv .safe lctx [`u] + ({} : FuelConfig) + (Ix.Theory.Named.TypeChecker.checkType (.const ``Nat [])) = + .ok (.sort (.succ .zero)) := + indexedVecNat_checkTypeM lctx + +@[simp] private theorem indexedVecInferConstantNatOnly + (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.Inner.inferConstant + (indexedVecTypeCheckerContext lctx) ``Nat [] true = + .ok (.sort (.succ .zero)) := by + unfold Ix.Theory.Named.TypeChecker.Inner.inferConstant + simp [indexedVecTypeCheckerContext, indexedVecKernel_get_nat, natInfo, ConstantInfo.levelParams, + ConstantInfo.instantiateTypeLevelParams, ConstantInfo.toConstantVal, + ConstantVal.instantiateTypeLevelParams, Expr.instantiateLevelParams_eq, + Expr.instantiateLevelParamsCore', Level.substParams', Bind.bind, Except.bind, Pure.pure, + Except.pure] + +private def indexedVecPreFamilyNatInferOnlyState : + Ix.Theory.Named.TypeChecker.State := + { ({} : Ix.Theory.Named.TypeChecker.State) with + inferTypeI := ({} : Ix.Theory.Named.TypeChecker.State).inferTypeI.insert + (.const ``Nat []) (.sort (.succ .zero)) } + +private theorem indexedVecPreFamilyNatInferOnly + (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.Inner.inferType (.const ``Nat []) true + (Ix.Theory.Named.TypeChecker.Methods.withFuel 10000) + (indexedVecTypeCheckerContext lctx) + ({} : Ix.Theory.Named.TypeChecker.State) = + .ok (.sort (.succ .zero), + indexedVecPreFamilyNatInferOnlyState) := by + change Ix.Theory.Named.TypeChecker.Inner.inferType' (.const ``Nat []) true + (Ix.Theory.Named.TypeChecker.Methods.withFuel 9999) + (indexedVecTypeCheckerContext lctx) + ({} : Ix.Theory.Named.TypeChecker.State) = _ + unfold Ix.Theory.Named.TypeChecker.Inner.inferType' + simp [indexedVecPreFamilyNatInferOnlyState, + Expr.hasLooseBVars, Expr.looseBVarRange', + indexedVecInferConstantNatOnly, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + +theorem indexedVecPreFamilyNatEnsureTypeM (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.M.run indexedVecKernelEnv .safe lctx [`u] + ({} : FuelConfig) + (Ix.Theory.Named.TypeChecker.ensureType (.const ``Nat [])) = + .ok (.sort (.succ .zero)) := by + unfold Ix.Theory.Named.TypeChecker.ensureType Ix.Theory.Named.TypeChecker.inferType + Ix.Theory.Named.TypeChecker.ensureSort Ix.Theory.Named.TypeChecker.RecM.run + Ix.Theory.Named.TypeChecker.M.run + simp only [readThe, MonadReaderOf.read, ReaderT.read, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind, + Pure.pure, StateT.pure, Except.pure, StateT.run', + Functor.map, Except.map] + rw [show Ix.Theory.Named.TypeChecker.Inner.inferType (.const ``Nat []) true + (Ix.Theory.Named.TypeChecker.Methods.withFuel 10000) + { env := indexedVecKernelEnv + lctx := lctx + safety := .safe + lparams := [`u] + fuel := ({} : FuelConfig) } + ({} : Ix.Theory.Named.TypeChecker.State) = + .ok (.sort (.succ .zero), + indexedVecPreFamilyNatInferOnlyState) by + simpa [indexedVecTypeCheckerContext] using + indexedVecPreFamilyNatInferOnly lctx] + rfl + +private def indexedVecPreFamilyIndexCheckerLctx + (lctx : LocalContext) : LocalContext := + lctx.mkLocalDecl ⟨indexedVecInnerNatState.ngen.curr⟩ + indexedVecIndexName (.const ``Nat []) .default + +private theorem indexedVecPreFamilyIndexNatCore (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.Inner.inferType' (.const ``Nat []) false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 9998) + (indexedVecTypeCheckerContext lctx) + ({} : Ix.Theory.Named.TypeChecker.State) = + .ok (.sort (.succ .zero), indexedVecInnerNatState) := by + simp [indexedVecInnerNatState] + +private theorem indexedVecPreFamilyIndexWithLocalDecl + (lctx : LocalContext) + {α} (k : Expr → Ix.Theory.Named.TypeChecker.RecM α) + (methods : Ix.Theory.Named.TypeChecker.Methods) : + (withLocalDecl (m := Ix.Theory.Named.TypeChecker.RecM) + indexedVecIndexName .default (.const ``Nat []) k) + methods (indexedVecTypeCheckerContext lctx) + indexedVecInnerNatState = + k (.fvar ⟨indexedVecInnerNatState.ngen.curr⟩) methods + { indexedVecTypeCheckerContext lctx with + lctx := indexedVecPreFamilyIndexCheckerLctx lctx } + indexedVecInnerAfterIndexState := by + simpa [indexedVecPreFamilyIndexCheckerLctx, + indexedVecInnerAfterIndexState, indexedVecTypeCheckerContext] using + (indexedVecWithLocalDecl indexedVecIndexName .default + (.const ``Nat []) k methods (indexedVecTypeCheckerContext lctx) + indexedVecInnerNatState) + +private theorem indexedVecPreFamilyIndexSortCore (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.Inner.inferType' + (.sort (.succ (.param `u))) false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 9998) + { indexedVecTypeCheckerContext lctx with + lctx := indexedVecPreFamilyIndexCheckerLctx lctx } + indexedVecInnerAfterIndexState = + .ok (.sort (.succ (.succ (.param `u))), + indexedVecInnerSortState) := by + simpa [indexedVecPreFamilyIndexCheckerLctx, + indexedVecInnerAfterIndexState, indexedVecInnerNatState, + indexedVecInnerSortState, indexedVecTypeCheckerContext] using + (indexedVecInferTypeSortCore 9998 + (indexedVecPreFamilyIndexCheckerLctx lctx) + indexedVecInnerAfterIndexState (by + simp [indexedVecInnerAfterIndexState, + indexedVecInnerNatState])) + +private theorem indexedVecPreFamilyIndexInferForall + (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.Inner.inferForall + indexedVecPreFamilyIndexTelescope false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 9999) + (indexedVecTypeCheckerContext lctx) + ({} : Ix.Theory.Named.TypeChecker.State) = + .ok (.sort indexedVecInnerInferredLevel, + indexedVecInnerSortState) := by + unfold indexedVecPreFamilyIndexTelescope + unfold Ix.Theory.Named.TypeChecker.Inner.inferForall + simp only [Ix.Theory.Named.TypeChecker.Inner.inferForall.loop] + rw [show (.const ``Nat [] : Expr).instantiateRev #[] = + .const ``Nat [] by + simp [Expr.instantiateRev_eq, Expr.instantiate_eq]] + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [indexedVecInferTypeFuel 9998] + rw [indexedVecPreFamilyIndexNatCore] + simp only + rw [indexedVecEnsureSort] + simp only + rw [indexedVecPreFamilyIndexWithLocalDecl] + rw [show + (.sort (.succ (.param `u)) : Expr).instantiateRev + (#[] |>.push (.fvar ⟨indexedVecInnerNatState.ngen.curr⟩)) = + .sort (.succ (.param `u)) by + simp [Expr.instantiateRev_eq, Expr.instantiate_eq, + Expr.instantiate1']] + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [indexedVecInferTypeFuel 9998] + rw [indexedVecPreFamilyIndexSortCore] + simp only + rw [indexedVecEnsureSort] + simp [indexedVecInnerInferredLevel, Expr.sortLevel!, + Pure.pure, ReaderT.pure, StateT.pure, Except.pure] + +theorem indexedVecPreFamilyIndexTelescopeCheckTypeM + (lctx : LocalContext) : + Ix.Theory.Named.TypeChecker.M.run indexedVecKernelEnv .safe lctx [`u] + ({} : FuelConfig) + (Ix.Theory.Named.TypeChecker.checkType + indexedVecPreFamilyIndexTelescope) = + .ok (.sort indexedVecInnerInferredLevel) := by + change Except.map + (fun x : Expr × Ix.Theory.Named.TypeChecker.State => x.1) + (Ix.Theory.Named.TypeChecker.Inner.inferType' + indexedVecPreFamilyIndexTelescope false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 9999) + (indexedVecTypeCheckerContext lctx) + ({} : Ix.Theory.Named.TypeChecker.State)) = _ + unfold Ix.Theory.Named.TypeChecker.Inner.inferType' + simp [indexedVecPreFamilyIndexTelescope, Expr.hasLooseBVars, + Expr.looseBVarRange', Bind.bind, ReaderT.bind, StateT.bind, + Except.bind] + rw [show + Ix.Theory.Named.TypeChecker.Inner.inferForall + (.forallE indexedVecIndexName (.const ``Nat []) + (.sort (.succ (.param `u))) .default) + false (Ix.Theory.Named.TypeChecker.Methods.withFuel 9999) + (indexedVecTypeCheckerContext lctx) + ({} : Ix.Theory.Named.TypeChecker.State) = + .ok (.sort indexedVecInnerInferredLevel, + indexedVecInnerSortState) by + simpa [indexedVecPreFamilyIndexTelescope] using + indexedVecPreFamilyIndexInferForall lctx] + rfl + +private theorem indexedVecInner_whnfM : + Ix.Theory.Named.TypeChecker.M.run indexedVecKernelEnv .safe + indexedVecParamCandidateContext.lctx [`u] ({} : FuelConfig) + (Ix.Theory.Named.TypeChecker.whnf indexedVecInnerKernel) = + .ok indexedVecInnerKernel := by + rfl + +private theorem indexedVecFamilyCandidateFresh : + indexedVecFamilyCandidateContext.lctx.find? + indexedVecFamilyCandidateContext.freshFVarId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := indexedVecFamilyCandidateContext.freshFVarId) + LocalContext.WF.nil + change + ({ fvarIdToDecl := PersistentHashMap.empty, + decls := PersistentArray.empty, + auxDeclToFullName := Std.TreeMap.empty } : LocalContext).find? + indexedVecFamilyCandidateContext.freshFVarId = none + rw [h] + simp [LocalContext.toList] + +private theorem indexedVecParamCandidateFresh : + indexedVecParamCandidateContext.lctx.find? + indexedVecParamCandidateContext.freshFVarId = none := by + have hroot := indexedVecFamilyCandidateFresh + have hwf : indexedVecParamCandidateContext.lctx.WF := by + change (({} : LocalContext).mkLocalDecl + indexedVecFamilyCandidateContext.freshFVarId + indexedVecParamName (.sort (.succ (.param `u))) .default).WF + exact LocalContext.WF.mkLocalDecl LocalContext.WF.nil (by + simpa [indexedVecFamilyCandidateContext] using hroot) + have h := LocalContext.WF.find?_eq_find?_toList + (fv := indexedVecParamCandidateContext.freshFVarId) hwf + rw [h] + simp only [indexedVecParamCandidateContext, + indexedVecFamilyCandidateContext, + Ix.Theory.Named.AddInductive.Context.pushLocalDecl, + Ix.Theory.Named.AddInductive.Context.freshFVarId] + rw [LocalContext.mkLocalDecl_toList] + rw [show ({} : LocalContext).toList = [] by rfl] + simp [NameGenerator.next, NameGenerator.curr] + intro heq + injection heq with hname + injection hname with hidx + omega + +private theorem indexedVecFamily_checkTypeM : + Ix.Theory.Named.TypeChecker.M.run + indexedVecFamilyCandidateContext.env + indexedVecFamilyCandidateContext.safety + indexedVecFamilyCandidateContext.lctx + indexedVecFamilyCandidateContext.lparams + indexedVecFamilyCandidateContext.fuel + (Ix.Theory.Named.TypeChecker.checkType indexedVecInfo.type) = + .ok (.sort indexedVecFamilyInferredLevel) := by + change Except.map + (fun x : Expr × Ix.Theory.Named.TypeChecker.State => x.1) + (Ix.Theory.Named.TypeChecker.Inner.inferType indexedVecInfo.type false + (Ix.Theory.Named.TypeChecker.Methods.withFuel 10000) + indexedVecFamilyCandidateContext.toTypeChecker + ({} : Ix.Theory.Named.TypeChecker.State)) = _ + rw [indexedVecFamilyCheckTypeInner] + rfl + +private def indexedVecParamAnnotations : + Ix.Theory.Named.AddInductive.CandidateTypeAnnotations + indexedVecTerminalKernel where + consumed := indexedVecTerminalKernel + trace := .identity _ + +private def indexedVecIndexAnnotations : + Ix.Theory.Named.AddInductive.CandidateTypeAnnotations (.const ``Nat []) where + consumed := .const ``Nat [] + trace := .identity _ + +private theorem indexedVecParamAnnotationTrace_build : + Ix.Theory.Named.AddInductive.CandidateTypeAnnotationTrace.build + indexedVecTerminalKernel = + ⟨indexedVecTerminalKernel, .identity _⟩ := by + simp [Ix.Theory.Named.AddInductive.CandidateTypeAnnotationTrace.build, + indexedVecTerminalKernel] + +private theorem indexedVecIndexAnnotationTrace_build : + Ix.Theory.Named.AddInductive.CandidateTypeAnnotationTrace.build + (.const ``Nat []) = ⟨.const ``Nat [], .identity _⟩ := by + simp [Ix.Theory.Named.AddInductive.CandidateTypeAnnotationTrace.build] + +private theorem indexedVecParamAnnotations_build : + Ix.Theory.Named.AddInductive.buildCandidateTypeAnnotations + indexedVecTerminalKernel = .ok indexedVecParamAnnotations := by + unfold Ix.Theory.Named.AddInductive.buildCandidateTypeAnnotations + rw [indexedVecParamAnnotationTrace_build] + rfl + +private theorem indexedVecIndexAnnotations_build : + Ix.Theory.Named.AddInductive.buildCandidateTypeAnnotations + (.const ``Nat []) = .ok indexedVecIndexAnnotations := by + unfold Ix.Theory.Named.AddInductive.buildCandidateTypeAnnotations + rw [indexedVecIndexAnnotationTrace_build] + rfl + +private theorem indexedVecParamAnnotations_match : + indexedVecParamAnnotations.Matches := + Ix.Theory.Named.AddInductive.CandidateTypeAnnotations.matches_of_build + indexedVecParamAnnotations indexedVecParamAnnotations_build + +private theorem indexedVecIndexAnnotations_match : + indexedVecIndexAnnotations.Matches := + Ix.Theory.Named.AddInductive.CandidateTypeAnnotations.matches_of_build + indexedVecIndexAnnotations indexedVecIndexAnnotations_build + +private theorem indexedVecParamAnnotationsEq : + Ix.Theory.Named.AddInductive.CandidateIsDefEqStep.Valid + ⟨indexedVecFamilyCandidateContext, indexedVecTerminalKernel, + indexedVecParamAnnotations.consumed⟩ := by + simpa [indexedVecParamAnnotations] using + (candidateIsDefEqSelfValid indexedVecFamilyCandidateContext + indexedVecTerminalKernel 9999 rfl) + +private theorem indexedVecIndexAnnotationsEq : + Ix.Theory.Named.AddInductive.CandidateIsDefEqStep.Valid + ⟨indexedVecParamCandidateContext, (.const ``Nat []), + indexedVecIndexAnnotations.consumed⟩ := by + simpa [indexedVecIndexAnnotations] using + (candidateIsDefEqSelfValid indexedVecParamCandidateContext + (.const ``Nat []) 9999 rfl) + +private def indexedVecParamDomainCandidateTrace : + Ix.Theory.Named.AddInductive.CandidateExprTrace + indexedVecFamilyCandidateContext indexedVecTerminalKernel := + .terminal indexedVecFamilyCandidateContext indexedVecTerminalKernel + (.sort (.succ (.succ (.param `u)))) indexedVecTerminalKernel + (by + simpa [Ix.Theory.Named.AddInductive.CandidateCheckTypeStep.Valid, + indexedVecFamilyCandidateContext, indexedVecInfo, + ConstantInfo.levelParams, ConstantInfo.toConstantVal, + indexedVecTerminalKernel] using + indexedVecSort_checkTypeM + indexedVecFamilyCandidateContext.lctx) + (by + simpa [Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid, + indexedVecFamilyCandidateContext, indexedVecInfo, + ConstantInfo.levelParams, ConstantInfo.toConstantVal, + indexedVecTerminalKernel] using + indexedVecSort_whnfM indexedVecFamilyCandidateContext.lctx) + +private def indexedVecIndexDomainCandidateTrace : + Ix.Theory.Named.AddInductive.CandidateExprTrace + indexedVecParamCandidateContext (.const ``Nat []) := + .terminal indexedVecParamCandidateContext (.const ``Nat []) + (.sort (.succ .zero)) (.const ``Nat []) + (by + simpa [Ix.Theory.Named.AddInductive.CandidateCheckTypeStep.Valid, + indexedVecParamCandidateContext, + indexedVecFamilyCandidateContext, indexedVecInfo, + ConstantInfo.levelParams, ConstantInfo.toConstantVal, + Ix.Theory.Named.AddInductive.Context.pushLocalDecl] using + indexedVecNat_checkTypeM indexedVecParamCandidateContext.lctx) + (by + simpa [Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid, + indexedVecParamCandidateContext, + indexedVecFamilyCandidateContext, indexedVecInfo, + ConstantInfo.levelParams, ConstantInfo.toConstantVal, + Ix.Theory.Named.AddInductive.Context.pushLocalDecl] using + indexedVecNat_whnfM indexedVecParamCandidateContext.lctx) + +private def indexedVecTerminalCandidateTrace : + Ix.Theory.Named.AddInductive.CandidateExprTrace + indexedVecIndexCandidateContext + (indexedVecTerminalKernel.instantiate1 + indexedVecParamCandidateContext.freshExpr) := + .terminal indexedVecIndexCandidateContext + (indexedVecTerminalKernel.instantiate1 + indexedVecParamCandidateContext.freshExpr) + (.sort (.succ (.succ (.param `u)))) indexedVecTerminalKernel + (by + simpa [Ix.Theory.Named.AddInductive.CandidateCheckTypeStep.Valid, + indexedVecIndexCandidateContext, + indexedVecParamCandidateContext, + indexedVecFamilyCandidateContext, indexedVecInfo, + ConstantInfo.levelParams, ConstantInfo.toConstantVal, + Ix.Theory.Named.AddInductive.Context.pushLocalDecl, + indexedVecTerminalKernel] using + indexedVecSort_checkTypeM indexedVecIndexCandidateContext.lctx) + (by + simpa [Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid, + indexedVecIndexCandidateContext, + indexedVecParamCandidateContext, + indexedVecFamilyCandidateContext, indexedVecInfo, + ConstantInfo.levelParams, ConstantInfo.toConstantVal, + Ix.Theory.Named.AddInductive.Context.pushLocalDecl, + indexedVecTerminalKernel] using + indexedVecSort_whnfM indexedVecIndexCandidateContext.lctx) + +private def indexedVecInnerCandidateTrace : + Ix.Theory.Named.AddInductive.CandidateExprTrace + indexedVecParamCandidateContext + (indexedVecInnerKernel.instantiate1 + indexedVecFamilyCandidateContext.freshExpr) := + .forallE indexedVecParamCandidateContext + (indexedVecInnerKernel.instantiate1 + indexedVecFamilyCandidateContext.freshExpr) + (.sort indexedVecInnerInferredLevel) + indexedVecIndexName (.const ``Nat []) indexedVecTerminalKernel + .default indexedVecParamCandidateFresh indexedVecIndexAnnotations + indexedVecIndexAnnotationsEq + (by + simpa [Ix.Theory.Named.AddInductive.CandidateCheckTypeStep.Valid, + indexedVecParamCandidateContext, + indexedVecFamilyCandidateContext, indexedVecInfo, + ConstantInfo.levelParams, ConstantInfo.toConstantVal, + Ix.Theory.Named.AddInductive.Context.pushLocalDecl, + indexedVecInnerKernel, indexedVecTerminalKernel, + Expr.instantiate1'] using + indexedVecInner_checkTypeM) + (by + simpa [Ix.Theory.Named.AddInductive.CandidateWhnfStep.Valid, + indexedVecParamCandidateContext, + indexedVecFamilyCandidateContext, indexedVecInfo, + ConstantInfo.levelParams, ConstantInfo.toConstantVal, + Ix.Theory.Named.AddInductive.Context.pushLocalDecl, + indexedVecInnerKernel, indexedVecTerminalKernel, + Expr.instantiate1'] using + indexedVecInner_whnfM) + indexedVecIndexDomainCandidateTrace indexedVecTerminalCandidateTrace + +private def indexedVecFamilyCandidateTrace : + Ix.Theory.Named.AddInductive.CandidateExprTrace + indexedVecFamilyCandidateContext indexedVecInfo.type := + .forallE indexedVecFamilyCandidateContext indexedVecInfo.type + (.sort indexedVecFamilyInferredLevel) + indexedVecParamName indexedVecTerminalKernel indexedVecInnerKernel + .default indexedVecFamilyCandidateFresh indexedVecParamAnnotations + indexedVecParamAnnotationsEq + indexedVecFamily_checkTypeM + (by + change Ix.Theory.Named.TypeChecker.M.run + indexedVecFamilyCandidateContext.env + indexedVecFamilyCandidateContext.safety + indexedVecFamilyCandidateContext.lctx + indexedVecFamilyCandidateContext.lparams + indexedVecFamilyCandidateContext.fuel + (Ix.Theory.Named.TypeChecker.whnf indexedVecInfo.type) = + .ok indexedVecInfo.type + exact indexedVecFamily_whnfM) + indexedVecParamDomainCandidateTrace indexedVecInnerCandidateTrace + +def indexedVecFamilyCandidate : + Ix.Theory.Named.AddInductive.CandidateExpr indexedVecInfo.type := + ⟨indexedVecFamilyCandidateContext, indexedVecFamilyCandidateTrace⟩ + +theorem indexedVecFamilyCandidate_view_eq : + indexedVecFamilyCandidate.view = indexedVecInfo.type := by + have habstract (context : Ix.Theory.Named.AddInductive.Context) (e : Expr) : + e.abstract #[context.freshExpr] = + Expr.abstract1 context.freshFVarId e := by + rw [show #[context.freshExpr] = + ⟨[context.freshFVarId].map Expr.fvar⟩ by rfl] + simp only [Expr.abstract_eq, Expr.abstractList] + simp only [indexedVecFamilyCandidate, + Ix.Theory.Named.AddInductive.CandidateExpr.view, + indexedVecFamilyCandidateTrace, + indexedVecInnerCandidateTrace, indexedVecParamDomainCandidateTrace, + indexedVecIndexDomainCandidateTrace, indexedVecTerminalCandidateTrace, + Ix.Theory.Named.AddInductive.CandidateExprTrace.view] + rw [habstract, habstract] + simp [Expr.abstract1, indexedVecTerminalKernel, + indexedVecFamilyCandidateContext, + Ix.Theory.Named.AddInductive.Context.pushLocalDecl, + Ix.Theory.Named.AddInductive.Context.freshFVarId, + NameGenerator.next, NameGenerator.curr, + indexedVecInfo, ConstantInfo.type, ConstantInfo.toConstantVal] + constructor <;> rfl + +/-- Every retained family-type candidate node preserves its kernel source. +This is the structural premise used by the semantic spine interpreter; it is +stronger than the root `view` equality because it covers both Pi domains and +the instantiated body under their exact candidate contexts. -/ +theorem indexedVecFamilyCandidate_identity : + Ix.Theory.Named.TypeChecker.CandidateExprIdentity + indexedVecFamilyCandidate.trace := by + change Ix.Theory.Named.TypeChecker.CandidateExprIdentity + indexedVecFamilyCandidateTrace + unfold indexedVecFamilyCandidateTrace + refine .forallE (name := indexedVecParamName) (binderInfo := .default) + (body := indexedVecInnerKernel) + (annotations := indexedVecParamAnnotations) + indexedVecParamDomainCandidateTrace indexedVecInnerCandidateTrace + ?_ rfl (.terminal rfl) ?_ + · rfl + · unfold indexedVecInnerCandidateTrace + refine .forallE (name := indexedVecIndexName) (binderInfo := .default) + (body := indexedVecTerminalKernel) + (annotations := indexedVecIndexAnnotations) + indexedVecIndexDomainCandidateTrace indexedVecTerminalCandidateTrace + ?_ rfl (.terminal rfl) ?_ + · simpa only [Expr.instantiate1_eq, indexedVecInnerKernel, + indexedVecTerminalKernel] using + indexedVecInnerKernel_instantiate1 + indexedVecFamilyCandidateContext.freshExpr + · exact .terminal (by + simpa only [Expr.instantiate1_eq] using + (indexedVecTerminalKernel_instantiate1 + indexedVecParamCandidateContext.freshExpr).symm) + +private theorem indexedVecParamDomainCandidateTrace_loop (fuel : Nat) : + Ix.Theory.Named.AddInductive.buildCandidateExpr.loop + indexedVecFamilyCandidateContext indexedVecTerminalKernel + (fuel + 1) = .ok indexedVecParamDomainCandidateTrace := by + simpa only [indexedVecParamDomainCandidateTrace] using + Ix.Theory.Named.AddInductive.buildCandidateExpr_loop_of_whnf_nonForall + indexedVecFamilyCandidateContext indexedVecTerminalKernel + (.sort (.succ (.succ (.param `u)))) indexedVecTerminalKernel fuel + indexedVecParamDomainCandidateTrace.rootCheck.valid + indexedVecParamDomainCandidateTrace.rootWhnf_valid rfl + +private theorem indexedVecIndexDomainCandidateTrace_loop (fuel : Nat) : + Ix.Theory.Named.AddInductive.buildCandidateExpr.loop + indexedVecParamCandidateContext (.const ``Nat []) (fuel + 1) = + .ok indexedVecIndexDomainCandidateTrace := by + simpa only [indexedVecIndexDomainCandidateTrace] using + Ix.Theory.Named.AddInductive.buildCandidateExpr_loop_of_whnf_nonForall + indexedVecParamCandidateContext (.const ``Nat []) + (.sort (.succ .zero)) (.const ``Nat []) fuel + indexedVecIndexDomainCandidateTrace.rootCheck.valid + indexedVecIndexDomainCandidateTrace.rootWhnf_valid rfl + +private theorem indexedVecTerminalCandidateTrace_loop (fuel : Nat) : + Ix.Theory.Named.AddInductive.buildCandidateExpr.loop + indexedVecIndexCandidateContext + (indexedVecTerminalKernel.instantiate1 + indexedVecParamCandidateContext.freshExpr) + (fuel + 1) = .ok indexedVecTerminalCandidateTrace := by + simpa only [indexedVecTerminalCandidateTrace] using + Ix.Theory.Named.AddInductive.buildCandidateExpr_loop_of_whnf_nonForall + indexedVecIndexCandidateContext + (indexedVecTerminalKernel.instantiate1 + indexedVecParamCandidateContext.freshExpr) + (.sort (.succ (.succ (.param `u)))) indexedVecTerminalKernel fuel + indexedVecTerminalCandidateTrace.rootCheck.valid + indexedVecTerminalCandidateTrace.rootWhnf_valid rfl + +private theorem indexedVecInnerCandidateTrace_loop : + Ix.Theory.Named.AddInductive.buildCandidateExpr.loop + indexedVecParamCandidateContext + (indexedVecInnerKernel.instantiate1 + indexedVecFamilyCandidateContext.freshExpr) 999 = + .ok indexedVecInnerCandidateTrace := by + rw [show 999 = 998 + 1 by rfl] + simpa only [indexedVecInnerCandidateTrace, + indexedVecIndexCandidateContext] using + (Ix.Theory.Named.AddInductive.buildCandidateExpr_loop_of_whnf_forall + (context := indexedVecParamCandidateContext) + (e := indexedVecInnerKernel.instantiate1 + indexedVecFamilyCandidateContext.freshExpr) + (inferred := .sort indexedVecInnerInferredLevel) + (fuel := 998) (name := indexedVecIndexName) + (domain := .const ``Nat []) (body := indexedVecTerminalKernel) + (binderInfo := .default) (hfresh := indexedVecParamCandidateFresh) + (annotations := indexedVecIndexAnnotations) + (hannotations := indexedVecIndexAnnotations_build) + (hannotationsEq := indexedVecIndexAnnotationsEq) + (hcheck := indexedVecInnerCandidateTrace.rootCheck.valid) + (hrun := indexedVecInnerCandidateTrace.rootWhnf_valid) + (domainCandidate := indexedVecIndexDomainCandidateTrace) + (bodyCandidate := indexedVecTerminalCandidateTrace) + (hdomain := by + simpa using indexedVecIndexDomainCandidateTrace_loop 997) + (hbody := by + simpa [indexedVecIndexCandidateContext, + indexedVecIndexAnnotations] using + indexedVecTerminalCandidateTrace_loop 997)) + +private theorem indexedVecFamilyCandidateTrace_loop : + Ix.Theory.Named.AddInductive.buildCandidateExpr.loop + indexedVecFamilyCandidateContext indexedVecInfo.type + indexedVecFamilyCandidateContext.fuel.inductiveFuel = + .ok indexedVecFamilyCandidateTrace := by + change Ix.Theory.Named.AddInductive.buildCandidateExpr.loop + indexedVecFamilyCandidateContext indexedVecInfo.type (999 + 1) = _ + simpa only [indexedVecFamilyCandidateTrace, + indexedVecParamCandidateContext] using + (Ix.Theory.Named.AddInductive.buildCandidateExpr_loop_of_whnf_forall + (context := indexedVecFamilyCandidateContext) + (e := indexedVecInfo.type) + (inferred := .sort indexedVecFamilyInferredLevel) + (fuel := 999) (name := indexedVecParamName) + (domain := indexedVecTerminalKernel) (body := indexedVecInnerKernel) + (binderInfo := .default) (hfresh := indexedVecFamilyCandidateFresh) + (annotations := indexedVecParamAnnotations) + (hannotations := indexedVecParamAnnotations_build) + (hannotationsEq := indexedVecParamAnnotationsEq) + (hcheck := indexedVecFamilyCandidateTrace.rootCheck.valid) + (hrun := indexedVecFamilyCandidateTrace.rootWhnf_valid) + (domainCandidate := indexedVecParamDomainCandidateTrace) + (bodyCandidate := indexedVecInnerCandidateTrace) + (hdomain := by + simpa using indexedVecParamDomainCandidateTrace_loop 998) + (hbody := by + simpa [indexedVecParamCandidateContext, + indexedVecParamAnnotations, indexedVecTerminalKernel] using + indexedVecInnerCandidateTrace_loop)) + +/-- The executable candidate traversal preserves the real IndexedVec family +telescope and classifies its first binder as a parameter and its second as an +index in the subsequent family-validation pass. -/ +theorem indexedVecFamily_candidateTrace : + Ix.Theory.Named.AddInductive.buildCandidateExpr indexedVecInfo.type + indexedVecFamilyCandidateContext = .ok indexedVecFamilyCandidate := by + unfold Ix.Theory.Named.AddInductive.buildCandidateExpr + simp only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] + rw [indexedVecFamilyCandidateTrace_loop] + rfl + +/-- The executable IndexedVec family candidate retains its parameter and +index binders in order. -/ +theorem indexedVecFamilyCandidate_spineLength : + indexedVecFamilyCandidate.trace.spineLength = 2 := by + rfl + +theorem indexedVecFamilyCandidate_validationAnnotations : + indexedVecFamilyCandidate.trace.validationAnnotations := by + exact ⟨indexedVecParamAnnotations_match, + indexedVecIndexAnnotations_match, trivial⟩ + +theorem indexedVecFamilyCandidate_terminalResult : + indexedVecFamilyCandidate.trace.terminalResult = + .sort (.succ (.param `u)) := by + rfl + +def indexedVecKernelNil : Constructor where + name := indexedVecNilInfo.name + type := indexedVecNilInfo.type + +def indexedVecKernelCons : Constructor where + name := indexedVecConsInfo.name + type := indexedVecConsInfo.type + +def indexedVecKernelType : InductiveType where + name := indexedVecInfo.name + type := indexedVecInfo.type + ctors := [indexedVecKernelNil, indexedVecKernelCons] + +def indexedVecCandidateInductiveStats : + Ix.Theory.Named.AddInductive.InductiveStats := + indexedVecFamilyCandidate.trace.singletonCandidateInductiveStats + indexedVecKernelType 1 (.succ (.param `u)) + +private theorem indexedVecFamily_data_hasExprMVar_false : + indexedVecInfo.type.data.hasExprMVar = false := by + change indexedVecInfo.type.hasExprMVar = false + rw [Expr.hasExprMVar_eq] + rfl + +private theorem indexedVecFamily_data_hasLevelMVar_false : + indexedVecInfo.type.data.hasLevelMVar = false := by + change indexedVecInfo.type.hasLevelMVar = false + rw [Expr.hasLevelMVar_eq] + simp [indexedVecInfo, ConstantInfo.type, ConstantInfo.toConstantVal, + Expr.hasLevelMVar', Level.hasMVar_eq, Level.hasMVar'] + +private theorem indexedVecFamily_data_hasFVar_false : + indexedVecInfo.type.data.hasFVar = false := by + change indexedVecInfo.type.hasFVar = false + rw [Expr.hasFVar_eq] + rfl + +private theorem indexedVecFamily_hasMVar_false : + indexedVecInfo.type.hasMVar = false := by + change (indexedVecInfo.type.data.hasExprMVar || + indexedVecInfo.type.data.hasLevelMVar) = false + rw [indexedVecFamily_data_hasExprMVar_false, + indexedVecFamily_data_hasLevelMVar_false] + rfl + +private theorem indexedVecFamily_hasFVar_false : + indexedVecInfo.type.hasFVar = false := by + exact indexedVecFamily_data_hasFVar_false + +private theorem indexedVecFamily_closed : + indexedVecFamilyCandidateContext.env.checkNoMVarNoFVar + indexedVecKernelType.name indexedVecKernelType.type = .ok () := by + unfold Kernel.Environment.checkNoMVarNoFVar + Kernel.Environment.checkNoMVar Kernel.Environment.checkNoFVar + rw [show indexedVecKernelType.type.hasMVar = false by + simpa [indexedVecKernelType] using indexedVecFamily_hasMVar_false] + rw [show indexedVecKernelType.type.hasFVar = false by + simpa [indexedVecKernelType] using indexedVecFamily_hasFVar_false] + rfl + +private theorem indexedVecTerminal_ensureSortM : + Ix.Theory.Named.TypeChecker.M.run + indexedVecFamilyCandidate.trace.terminalContext.env + indexedVecFamilyCandidate.trace.terminalContext.safety + indexedVecFamilyCandidate.trace.terminalContext.lctx + indexedVecFamilyCandidate.trace.terminalContext.lparams + indexedVecFamilyCandidate.trace.terminalContext.fuel + (Ix.Theory.Named.TypeChecker.ensureSort + (.sort (.succ (.param `u)))) = + .ok (.sort (.succ (.param `u))) := by + rfl + +/-- The real IndexedVec family telescope drives the complete singleton +family-validation pass with one parameter and one index. -/ +theorem indexedVec_checkInductiveTypes + (k : Ix.Theory.Named.AddInductive.InductiveStats → + Ix.Theory.Named.AddInductive.M α) : + Ix.Theory.Named.AddInductive.checkInductiveTypes 1 + #[indexedVecKernelType] k indexedVecFamilyCandidateContext = + k indexedVecCandidateInductiveStats + indexedVecFamilyCandidate.trace.terminalContext := by + change Ix.Theory.Named.AddInductive.checkInductiveTypes 1 + #[indexedVecKernelType] k indexedVecFamilyCandidate.context = + k indexedVecCandidateInductiveStats + indexedVecFamilyCandidate.trace.terminalContext + exact + Ix.Theory.Named.AddInductive.CandidateExprTrace.checkInductiveTypes_singleton_of_candidate + (indType := indexedVecKernelType) + (candidate := indexedVecFamilyCandidate.trace) + (nparams := 1) (resultLevel := .succ (.param `u)) (k := k) + indexedVecFamily_closed (by decide) (by decide) + ⟨indexedVecParamAnnotations_match, + indexedVecIndexAnnotations_match, trivial⟩ + rfl indexedVecTerminal_ensureSortM + +theorem indexedVecCandidateInductiveStats_nindices : + indexedVecCandidateInductiveStats.nindices = #[1] := by + rfl + +theorem indexedVecCandidateInductiveStats_params : + indexedVecCandidateInductiveStats.params = + #[indexedVecFamilyCandidateContext.freshExpr] := by + rfl + +theorem indexedVecCandidateInductiveStats_resultLevel : + indexedVecCandidateInductiveStats.resultLevel = + .succ (.param `u) := by + rfl + +theorem indexedVecCandidateInductiveStats_indConsts : + indexedVecCandidateInductiveStats.indConsts = + #[.const ``IndexedVec [.param `u]] := by + rfl + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.candidateIsDefEqSelfValid [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Syntax.structEq_eq] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVecFamily_candidateTrace [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVec_checkInductiveTypes [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVecCandidateInductiveStats_nindices [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVecCandidateInductiveStats_params [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +end Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/IndexedVecConsReplay.lean b/Ix/Theory/Named/Verify/Environment/IndexedVecConsReplay.lean new file mode 100644 index 000000000..15ccbc80b --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/IndexedVecConsReplay.lean @@ -0,0 +1,2863 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.Environment.IndexedVecConstructors + +namespace Ix.Theory.Named.InductiveReplayFixtures +open Lean Meta +open Ix.Theory.Named.InductiveFixtures + +namespace IndexedVecConsReplay + + + + + + +def replayInsert (state : TypeChecker.State) (e type : Expr) : + TypeChecker.State := + { state with inferTypeC := state.inferTypeC.insert e type } + +open private mkLevelIMaxCore mkLevelMaxCore from Lean.Level in +@[simp] theorem replayMkLevelIMaxSuccParamSelf : + mkLevelIMax' (.succ (.param `u)) (.succ (.param `u)) = + .succ (.param `u) := by + simp [mkLevelIMax', mkLevelIMaxCore, mkLevelMax', mkLevelMaxCore] + +def replayFirstApp (alpha : Expr) : Expr := + .app (.const ``IndexedVec [.param `u]) alpha + +@[simp] theorem replayAppBeqFVar (fn arg : Expr) (id : FVarId) : + ((.app fn arg : Expr) == .fvar id) = false := by + change Expr.eqv (.app fn arg) (.fvar id) = false + rw [Expr.eqv_eq] + rfl + +@[simp] theorem replayFVarBeqApp (id : FVarId) (fn arg : Expr) : + ((.fvar id : Expr) == .app fn arg) = false := by + change Expr.eqv (.fvar id) (.app fn arg) = false + rw [Expr.eqv_eq] + rfl + +@[simp] theorem replayConstBeqApp + (name : Name) (levels : List Level) (fn arg : Expr) : + ((.const name levels : Expr) == .app fn arg) = false := by + change Expr.eqv (.const name levels) (.app fn arg) = false + rw [Expr.eqv_eq] + rfl + +@[simp] theorem replayAppBeqConst + (fn arg : Expr) (name : Name) (levels : List Level) : + ((.app fn arg : Expr) == .const name levels) = false := by + change Expr.eqv (.app fn arg) (.const name levels) = false + rw [Expr.eqv_eq] + rfl + +@[simp] theorem replayIndexedVecConstBeqSucc : + ((.const ``IndexedVec [.param `u] : Expr) == + .const ``Nat.succ []) = false := by + change Expr.eqv (.const ``IndexedVec [.param `u]) + (.const ``Nat.succ []) = false + rw [Expr.eqv_eq] + simp [Expr.eqv'] + +@[simp] theorem replayAlphaBeqN : + ((consAlphaExpr : Expr) == consNExpr) = false := by + change Expr.eqv consAlphaExpr consNExpr = false + rw [Expr.eqv_eq] + simp [Expr.eqv', consAlphaExpr, consNExpr, consAlphaContext, consRootContext, ctorContext, + AddInductive.Context.pushLocalDecl, AddInductive.Context.freshExpr, + AddInductive.Context.freshFVarId, NameGenerator.next, NameGenerator.curr] + +@[simp] theorem replayAlphaIdBeqNId : + ((.fvar consAlphaId : Expr) == .fvar consNId) = false := by + simpa using replayAlphaBeqN + +theorem replayInferFirstAppFVarCore + (fuel : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (id : FVarId) + (hfamily : state.inferTypeC[ + (.const ``IndexedVec [.param `u] : Expr)]? = none) + (halpha : (replayInsert state + (.const ``IndexedVec [.param `u]) indexedVecInfo.type).inferTypeC[ + (.fvar id : Expr)]? = none) + (happ : state.inferTypeC[replayFirstApp (.fvar id)]? = none) + (hfind : lctx.find? id = some (.cdecl index id name + (.sort (.succ (.param `u))) bi kind)) : + TypeChecker.Inner.inferType' (replayFirstApp (.fvar id)) false + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (vecFamilyTail, + replayInsert + (replayInsert + (replayInsert state + (.const ``IndexedVec [.param `u]) indexedVecInfo.type) + (.fvar id) (.sort (.succ (.param `u)))) + (replayFirstApp (.fvar id)) vecFamilyTail) := by + have hfamilyRun := inferTypeFamilyCore fuel lctx state hfamily + have halphaRun := inferTypeFVarCore fuel lctx + (replayInsert state (.const ``IndexedVec [.param `u]) indexedVecInfo.type) + id (.sort (.succ (.param `u))) halpha hfind + have happRun := inferAppCoreOf fuel (tcContext lctx) + state + (replayInsert state (.const ``IndexedVec [.param `u]) indexedVecInfo.type) + (replayInsert + (replayInsert state (.const ``IndexedVec [.param `u]) indexedVecInfo.type) + (.fvar id) (.sort (.succ (.param `u)))) + (.const ``IndexedVec [.param `u]) (.fvar id) + (.sort (.succ (.param `u))) vecFamilyTail `α .default + (by simp [Expr.hasLooseBVars, Expr.looseBVarRange']) + happ + (by simpa [replayInsert, indexedVecInfoTypeShape] using hfamilyRun) + (by simpa [replayInsert] using halphaRun) + (by rfl) + simpa [replayInsert, replayFirstApp, vecFamilyTail, + Expr.instantiate1'] using happRun + +def replaySuccApp (n : Expr) : Expr := + .app (.const ``Nat.succ []) n + +@[simp] theorem replayFirstAppBeqSuccApp (alpha n : Expr) : + (replayFirstApp alpha == replaySuccApp n) = false := by + change Expr.eqv (replayFirstApp alpha) (replaySuccApp n) = false + rw [Expr.eqv_eq] + simp [Expr.eqv', replayFirstApp, replaySuccApp] + +@[simp] theorem replayFirstAppBeqSuccLiteral (alpha n : Expr) : + (replayFirstApp alpha == .app (.const ``Nat.succ []) n) = false := by + simpa [replaySuccApp] using replayFirstAppBeqSuccApp alpha n + +theorem replayInferSuccFVarCore + (fuel : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (id : FVarId) + (hsucc : state.inferTypeC[(.const ``Nat.succ [] : Expr)]? = none) + (hn : (replayInsert state (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default)).inferTypeC[ + (.fvar id : Expr)]? = none) + (happ : state.inferTypeC[replaySuccApp (.fvar id)]? = none) + (hfind : lctx.find? id = some (.cdecl index id name + (.const ``Nat []) bi kind)) : + TypeChecker.Inner.inferType' (replaySuccApp (.fvar id)) false + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (.const ``Nat [], + replayInsert + (replayInsert + (replayInsert state (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default)) + (.fvar id) (.const ``Nat [])) + (replaySuccApp (.fvar id)) (.const ``Nat [])) := by + have hsuccRun := inferTypeSuccCore fuel lctx state hsucc + have hnRun := inferTypeFVarCore fuel lctx + (replayInsert state (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default)) + id (.const ``Nat []) hn hfind + have happRun := inferAppCoreOf fuel (tcContext lctx) + state + (replayInsert state (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default)) + (replayInsert + (replayInsert state (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default)) + (.fvar id) (.const ``Nat [])) + (.const ``Nat.succ []) (.fvar id) (.const ``Nat []) + (.const ``Nat []) `n .default + (by simp [Expr.hasLooseBVars, Expr.looseBVarRange']) + happ + (by simpa [replayInsert] using hsuccRun) + (by simpa [replayInsert] using hnRun) + (by rfl) + simpa [replayInsert, replaySuccApp, Expr.instantiate1_eq, + Expr.instantiate1'] using happRun + +theorem replayInferIndexedVecAppCore + (fuel : Nat) (lctx : LocalContext) + (state stateFn stateArg : TypeChecker.State) + (alpha indexExpr : Expr) + (hclosed : (ctorIndexedVecApp alpha indexExpr).hasLooseBVars = false) + (hcache : state.inferTypeC[ctorIndexedVecApp alpha indexExpr]? = none) + (hfn : TypeChecker.Inner.inferType' (replayFirstApp alpha) false + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (vecFamilyTail, stateFn)) + (harg : TypeChecker.Inner.inferType' indexExpr false + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) stateFn = + .ok (.const ``Nat [], stateArg)) + (heager : indexExpr.isAppOfArity ``eagerReduce 2 = false) : + TypeChecker.Inner.inferType' (ctorIndexedVecApp alpha indexExpr) false + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (.sort (.succ (.param `u)), + replayInsert stateArg (ctorIndexedVecApp alpha indexExpr) + (.sort (.succ (.param `u)))) := by + have h := inferAppCoreOf fuel (tcContext lctx) state stateFn stateArg + (replayFirstApp alpha) indexExpr (.const ``Nat []) + (.sort (.succ (.param `u))) vecIndexName .default + hclosed hcache + (by simpa [replayFirstApp, vecFamilyTail] using hfn) harg heager + simpa [replayInsert, ctorIndexedVecApp, replayFirstApp, + vecFamilyTail, Expr.instantiate1_eq, Expr.instantiate1'] using h + +theorem replayInferTypeCachedCore + (fuel : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (e type : Expr) + (hclosed : e.hasLooseBVars = false) + (hcache : state.inferTypeC[e]? = some type) : + TypeChecker.Inner.inferType' e false + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (type, state) := by + unfold TypeChecker.Inner.inferType' + simp [hclosed, hcache] + +theorem replayInferFirstAppAlphaCachedCore + (fuel : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (alphaId : FVarId) + (halpha : state.inferTypeC[(.fvar alphaId : Expr)]? = + some (.sort (.succ (.param `u)))) + (hfamily : state.inferTypeC[ + (.const ``IndexedVec [.param `u] : Expr)]? = none) + (happ : state.inferTypeC[replayFirstApp (.fvar alphaId)]? = none) : + TypeChecker.Inner.inferType' (replayFirstApp (.fvar alphaId)) false + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (vecFamilyTail, + replayInsert + (replayInsert state + (.const ``IndexedVec [.param `u]) indexedVecInfo.type) + (replayFirstApp (.fvar alphaId)) vecFamilyTail) := by + let familyState := replayInsert state + (.const ``IndexedVec [.param `u]) indexedVecInfo.type + have hfamilyRun := inferTypeFamilyCore fuel lctx state hfamily + have halphaCache : familyState.inferTypeC[(.fvar alphaId : Expr)]? = + some (.sort (.succ (.param `u))) := by + simp only [familyState, replayInsert, Std.HashMap.getElem?_insert] + rw [constBeqFVar] + exact halpha + have halphaRun := replayInferTypeCachedCore fuel lctx familyState + (.fvar alphaId) (.sort (.succ (.param `u))) + (by simp [Expr.hasLooseBVars, Expr.looseBVarRange']) halphaCache + have hrun := inferAppCoreOf fuel (tcContext lctx) + state familyState familyState + (.const ``IndexedVec [.param `u]) (.fvar alphaId) + (.sort (.succ (.param `u))) vecFamilyTail `α .default + (by simp [Expr.hasLooseBVars, Expr.looseBVarRange']) + happ + (by simpa [familyState, replayInsert, indexedVecInfoTypeShape] using + hfamilyRun) + halphaRun (by rfl) + simpa [familyState, replayInsert, replayFirstApp, vecFamilyTail, + Expr.instantiate1'] using hrun + +theorem replayInferTailDomainAlphaCachedCore + (fuel : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (alphaId nId : FVarId) + (halpha : state.inferTypeC[(.fvar alphaId : Expr)]? = + some (.sort (.succ (.param `u)))) + (hfamily : state.inferTypeC[ + (.const ``IndexedVec [.param `u] : Expr)]? = none) + (hfirstApp : state.inferTypeC[ + replayFirstApp (.fvar alphaId)]? = none) + (hn : (replayInsert + (replayInsert state (.const ``IndexedVec [.param `u]) + indexedVecInfo.type) + (replayFirstApp (.fvar alphaId)) vecFamilyTail).inferTypeC[ + (.fvar nId : Expr)]? = none) + (htail : state.inferTypeC[ + ctorIndexedVecApp (.fvar alphaId) (.fvar nId)]? = none) + (hfind : lctx.find? nId = some (.cdecl index nId name + (.const ``Nat []) bi kind)) : + TypeChecker.Inner.inferType' + (ctorIndexedVecApp (.fvar alphaId) (.fvar nId)) false + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (.sort (.succ (.param `u)), + replayInsert + (replayInsert + (replayInsert + (replayInsert state (.const ``IndexedVec [.param `u]) + indexedVecInfo.type) + (replayFirstApp (.fvar alphaId)) vecFamilyTail) + (.fvar nId) (.const ``Nat [])) + (ctorIndexedVecApp (.fvar alphaId) (.fvar nId)) + (.sort (.succ (.param `u)))) := by + let firstState := replayInsert + (replayInsert state (.const ``IndexedVec [.param `u]) + indexedVecInfo.type) + (replayFirstApp (.fvar alphaId)) vecFamilyTail + let nState := replayInsert firstState (.fvar nId) (.const ``Nat []) + have hfirstRun := replayInferFirstAppAlphaCachedCore fuel lctx state + alphaId halpha hfamily hfirstApp + have hnRun := inferTypeFVarCore fuel lctx firstState nId + (.const ``Nat []) hn hfind + have hrun := replayInferIndexedVecAppCore fuel lctx state firstState nState + (.fvar alphaId) (.fvar nId) + (by simp [ctorIndexedVecApp, Expr.hasLooseBVars, + Expr.looseBVarRange']) + htail + (by simpa [firstState] using hfirstRun) + (by simpa [firstState, nState, replayInsert] using hnRun) + (by rfl) + simpa [firstState, nState, replayInsert] using hrun + +theorem replayInferSuccFVarCachedCore + (fuel : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (id : FVarId) + (hsucc : state.inferTypeC[(.const ``Nat.succ [] : Expr)]? = none) + (hn : state.inferTypeC[(.fvar id : Expr)]? = some (.const ``Nat [])) + (happ : state.inferTypeC[replaySuccApp (.fvar id)]? = none) : + TypeChecker.Inner.inferType' (replaySuccApp (.fvar id)) false + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (.const ``Nat [], + replayInsert + (replayInsert state (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default)) + (replaySuccApp (.fvar id)) (.const ``Nat [])) := by + let succState := replayInsert state (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default) + have hsuccRun := inferTypeSuccCore fuel lctx state hsucc + have hnCache : succState.inferTypeC[(.fvar id : Expr)]? = + some (.const ``Nat []) := by + simp only [succState, replayInsert, Std.HashMap.getElem?_insert] + rw [constBeqFVar] + exact hn + have hnRun := replayInferTypeCachedCore fuel lctx succState + (.fvar id) (.const ``Nat []) + (by simp [Expr.hasLooseBVars, Expr.looseBVarRange']) hnCache + have happRun := inferAppCoreOf fuel (tcContext lctx) + state succState succState + (.const ``Nat.succ []) (.fvar id) (.const ``Nat []) + (.const ``Nat []) `n .default + (by simp [Expr.hasLooseBVars, Expr.looseBVarRange']) + happ + (by simpa [succState, replayInsert] using hsuccRun) + hnRun (by rfl) + simpa [succState, replayInsert, replaySuccApp, + Expr.instantiate1_eq, Expr.instantiate1'] using happRun + +theorem replayInferIndexedVecSuccFromCacheCore + (fuel : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (alphaId nId : FVarId) + (hfirst : state.inferTypeC[replayFirstApp (.fvar alphaId)]? = + some vecFamilyTail) + (hsucc : state.inferTypeC[(.const ``Nat.succ [] : Expr)]? = none) + (hn : state.inferTypeC[(.fvar nId : Expr)]? = some (.const ``Nat [])) + (hsuccApp : state.inferTypeC[replaySuccApp (.fvar nId)]? = none) + (hresult : state.inferTypeC[ + ctorIndexedVecApp (.fvar alphaId) (replaySuccApp (.fvar nId))]? = none) : + TypeChecker.Inner.inferType' + (ctorIndexedVecApp (.fvar alphaId) (replaySuccApp (.fvar nId))) false + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (.sort (.succ (.param `u)), + replayInsert + (replayInsert + (replayInsert state (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default)) + (replaySuccApp (.fvar nId)) (.const ``Nat [])) + (ctorIndexedVecApp (.fvar alphaId) + (replaySuccApp (.fvar nId))) + (.sort (.succ (.param `u)))) := by + let succState := replayInsert state (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default) + let succAppState := replayInsert succState + (replaySuccApp (.fvar nId)) (.const ``Nat []) + have hfirstRun := replayInferTypeCachedCore fuel lctx state + (replayFirstApp (.fvar alphaId)) vecFamilyTail + (by simp [replayFirstApp, Expr.hasLooseBVars, + Expr.looseBVarRange']) hfirst + have hsuccRun := replayInferSuccFVarCachedCore fuel lctx state nId + hsucc hn hsuccApp + have h := replayInferIndexedVecAppCore fuel lctx state state succAppState + (.fvar alphaId) (replaySuccApp (.fvar nId)) + (by simp [ctorIndexedVecApp, replaySuccApp, + Expr.hasLooseBVars, Expr.looseBVarRange']) + hresult hfirstRun + (by simpa [succState, succAppState] using hsuccRun) + (by rfl) + simpa [succState, succAppState, replayInsert] using h + +def consHeadFirstAppState : TypeChecker.State := + replayInsert + (replayInsert + (replayInsert ({} : TypeChecker.State) + (.const ``IndexedVec [.param `u]) indexedVecInfo.type) + consAlphaExpr (.sort (.succ (.param `u)))) + (replayFirstApp consAlphaExpr) vecFamilyTail + +def consHeadNState : TypeChecker.State := + replayInsert consHeadFirstAppState consNExpr (.const ``Nat []) + +def consTailDomainFinalState : TypeChecker.State := + replayInsert consHeadNState consTailDomain + (.sort (.succ (.param `u))) + +theorem replayInferConsHeadFirstApp (fuel : Nat) : + TypeChecker.Inner.inferType' (replayFirstApp consAlphaExpr) false + (TypeChecker.Methods.withFuel fuel) + (tcContext consHeadContext.lctx) ({} : TypeChecker.State) = + .ok (vecFamilyTail, consHeadFirstAppState) := by + simpa [consHeadFirstAppState, consAlphaExprShape] using + (replayInferFirstAppFVarCore fuel consHeadContext.lctx + ({} : TypeChecker.State) consAlphaId + (by simp) + (by simp [replayInsert]) + (by simp [replayFirstApp]) + consAlphaFindInHead) + +theorem replayInferConsHeadN (fuel : Nat) : + TypeChecker.Inner.inferType' consNExpr false + (TypeChecker.Methods.withFuel fuel) + (tcContext consHeadContext.lctx) consHeadFirstAppState = + .ok (.const ``Nat [], consHeadNState) := by + simpa [consHeadNState, consNExprShape, replayInsert] using + (inferTypeFVarCore fuel consHeadContext.lctx + consHeadFirstAppState consNId (.const ``Nat []) + (index := 1) (name := consNName) (bi := .implicit) + (kind := .default) + (by simp [consHeadFirstAppState, replayInsert, replayFirstApp]) + consNFindInHead) + +theorem replayInferConsTailDomainCore (fuel : Nat) : + TypeChecker.Inner.inferType' consTailDomain false + (TypeChecker.Methods.withFuel fuel) + (tcContext consHeadContext.lctx) ({} : TypeChecker.State) = + .ok (.sort (.succ (.param `u)), consTailDomainFinalState) := by + simpa [consTailDomainFinalState, consTailDomain, + ctorIndexedVecApp, replayFirstApp] using + (replayInferIndexedVecAppCore fuel consHeadContext.lctx + ({} : TypeChecker.State) consHeadFirstAppState consHeadNState + consAlphaExpr consNExpr + (by simp [ctorIndexedVecApp, consAlphaExprShape, + consNExprShape, Expr.hasLooseBVars, Expr.looseBVarRange']) + (by simp [ctorIndexedVecApp, consAlphaExprShape, consNExprShape]) + (replayInferConsHeadFirstApp fuel) (replayInferConsHeadN fuel) (by rfl)) + +theorem replayInferConsTailDomain : + TypeChecker.Inner.inferType consTailDomain false + (TypeChecker.Methods.withFuel 10000) + (tcContext consHeadContext.lctx) ({} : TypeChecker.State) = + .ok (.sort (.succ (.param `u)), consTailDomainFinalState) := by + change TypeChecker.Inner.inferType' consTailDomain false + (TypeChecker.Methods.withFuel 9999) + (tcContext consHeadContext.lctx) ({} : TypeChecker.State) = _ + exact replayInferConsTailDomainCore 9999 + +theorem replayConsTailDomainCheckTypeM : + TypeChecker.M.run ctorEnv .safe consHeadContext.lctx [`u] + ({} : FuelConfig) (TypeChecker.checkType consTailDomain) = + .ok (.sort (.succ (.param `u))) := by + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType consTailDomain false + (TypeChecker.Methods.withFuel 10000) + (tcContext consHeadContext.lctx) ({} : TypeChecker.State)) = _ + rw [replayInferConsTailDomain] + rfl + +def consTailFirstAppState : TypeChecker.State := + replayInsert + (replayInsert + (replayInsert ({} : TypeChecker.State) + (.const ``IndexedVec [.param `u]) indexedVecInfo.type) + consAlphaExpr (.sort (.succ (.param `u)))) + (replayFirstApp consAlphaExpr) vecFamilyTail + +def consTailSuccState : TypeChecker.State := + replayInsert + (replayInsert + (replayInsert consTailFirstAppState (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default)) + consNExpr (.const ``Nat [])) + (replaySuccApp consNExpr) (.const ``Nat []) + +def consTerminalFinalState : TypeChecker.State := + replayInsert consTailSuccState consTerminal + (.sort (.succ (.param `u))) + +theorem replayInferConsTailFirstApp : + TypeChecker.Inner.inferType' (replayFirstApp consAlphaExpr) false + (TypeChecker.Methods.withFuel 9999) + (tcContext consTailContext.lctx) ({} : TypeChecker.State) = + .ok (vecFamilyTail, consTailFirstAppState) := by + simpa [consTailFirstAppState, consAlphaExprShape] using + (replayInferFirstAppFVarCore 9999 consTailContext.lctx + ({} : TypeChecker.State) consAlphaId + (by simp) + (by simp [replayInsert]) + (by simp [replayFirstApp]) + consAlphaFindInTail) + +theorem replayInferConsTailSucc : + TypeChecker.Inner.inferType' (replaySuccApp consNExpr) false + (TypeChecker.Methods.withFuel 9999) + (tcContext consTailContext.lctx) consTailFirstAppState = + .ok (.const ``Nat [], consTailSuccState) := by + simpa [consTailSuccState, consNExprShape] using + (replayInferSuccFVarCore 9999 consTailContext.lctx + consTailFirstAppState consNId + (by simp [consTailFirstAppState, replayInsert, replayFirstApp]) + (by simp [consTailFirstAppState, replayInsert, + replayFirstApp]) + (by simp [consTailFirstAppState, replayInsert, replaySuccApp]) + consNFindInTail) + +theorem replayInferConsTerminal : + TypeChecker.Inner.inferType consTerminal false + (TypeChecker.Methods.withFuel 10000) + (tcContext consTailContext.lctx) ({} : TypeChecker.State) = + .ok (.sort (.succ (.param `u)), consTerminalFinalState) := by + change TypeChecker.Inner.inferType' consTerminal false + (TypeChecker.Methods.withFuel 9999) + (tcContext consTailContext.lctx) ({} : TypeChecker.State) = _ + simpa [consTerminalFinalState, consTerminal, + ctorIndexedVecApp, replayFirstApp, replaySuccApp] using + (replayInferIndexedVecAppCore 9999 consTailContext.lctx + ({} : TypeChecker.State) consTailFirstAppState consTailSuccState + consAlphaExpr (replaySuccApp consNExpr) + (by simp [ctorIndexedVecApp, replaySuccApp, + Expr.hasLooseBVars, Expr.looseBVarRange']) + (by simp [ctorIndexedVecApp, replaySuccApp]) + replayInferConsTailFirstApp replayInferConsTailSucc + (by rfl)) + +theorem replayConsTerminalCheckTypeM : + TypeChecker.M.run ctorEnv .safe consTailContext.lctx [`u] + ({} : FuelConfig) (TypeChecker.checkType consTerminal) = + .ok (.sort (.succ (.param `u))) := by + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType consTerminal false + (TypeChecker.Methods.withFuel 10000) + (tcContext consTailContext.lctx) ({} : TypeChecker.State)) = _ + rw [replayInferConsTerminal] + rfl + +theorem replayConsTailDomainWhnfM : + TypeChecker.M.run ctorEnv .safe consHeadContext.lctx [`u] + ({} : FuelConfig) (TypeChecker.whnf consTailDomain) = + .ok consTailDomain := by + simpa [consTailDomain, ctorIndexedVecApp] using + (ctorIndexedVecWhnfM consHeadContext.lctx consAlphaExpr consNExpr) + +theorem replayConsTerminalWhnfM : + TypeChecker.M.run ctorEnv .safe consTailContext.lctx [`u] + ({} : FuelConfig) (TypeChecker.whnf consTerminal) = + .ok consTerminal := by + simpa [consTerminal, ctorIndexedVecApp, replaySuccApp] using + (ctorIndexedVecWhnfM consTailContext.lctx consAlphaExpr + (replaySuccApp consNExpr)) + +theorem replayConsRootWhnfM : + TypeChecker.M.run ctorEnv .safe {} [`u] ({} : FuelConfig) + (TypeChecker.whnf indexedVecConsInfo.type) = + .ok indexedVecConsInfo.type := by rfl + +theorem replayConsAfterAlphaWhnfM : + TypeChecker.M.run ctorEnv .safe consAlphaContext.lctx [`u] + ({} : FuelConfig) (TypeChecker.whnf consAfterAlpha) = + .ok consAfterAlpha := by rfl + +theorem replayConsAfterNWhnfM : + TypeChecker.M.run ctorEnv .safe consNContext.lctx [`u] + ({} : FuelConfig) (TypeChecker.whnf consAfterN) = + .ok consAfterN := by rfl + +theorem replayConsAfterHeadWhnfM : + TypeChecker.M.run ctorEnv .safe consHeadContext.lctx [`u] + ({} : FuelConfig) (TypeChecker.whnf consAfterHead) = + .ok consAfterHead := by rfl + +def consAfterHeadCheckTailId : FVarId := + ⟨consTailDomainFinalState.ngen.curr⟩ + +def consAfterHeadCheckLctx : LocalContext := + consHeadContext.lctx.mkLocalDecl consAfterHeadCheckTailId + consTailName consTailDomain .default + +def consAfterHeadCheckState : TypeChecker.State := + { consTailDomainFinalState with + ngen := consTailDomainFinalState.ngen.next } + +@[simp] theorem replayTailDomainBeqFirstApp : + (ctorIndexedVecApp (.fvar consAlphaId) (.fvar consNId) == + replayFirstApp (.fvar consAlphaId)) = false := by + change Expr.eqv + (ctorIndexedVecApp (.fvar consAlphaId) (.fvar consNId)) + (replayFirstApp (.fvar consAlphaId)) = false + rw [Expr.eqv_eq] + simp [Expr.eqv', ctorIndexedVecApp, replayFirstApp] + +@[simp] theorem replayTailDomainBeqSuccApp : + (ctorIndexedVecApp (.fvar consAlphaId) (.fvar consNId) == + replaySuccApp (.fvar consNId)) = false := by + change Expr.eqv + (ctorIndexedVecApp (.fvar consAlphaId) (.fvar consNId)) + (replaySuccApp (.fvar consNId)) = false + rw [Expr.eqv_eq] + simp [Expr.eqv', ctorIndexedVecApp, replaySuccApp] + +@[simp] theorem replayTailDomainBeqTerminal : + (ctorIndexedVecApp (.fvar consAlphaId) (.fvar consNId) == + ctorIndexedVecApp (.fvar consAlphaId) + (replaySuccApp (.fvar consNId))) = false := by + change Expr.eqv + (ctorIndexedVecApp (.fvar consAlphaId) (.fvar consNId)) + (ctorIndexedVecApp (.fvar consAlphaId) + (replaySuccApp (.fvar consNId))) = false + rw [Expr.eqv_eq] + simp [Expr.eqv', ctorIndexedVecApp, replaySuccApp] + +@[simp] theorem replayFirstAppBeqTerminal : + (replayFirstApp (.fvar consAlphaId) == + ctorIndexedVecApp (.fvar consAlphaId) + (replaySuccApp (.fvar consNId))) = false := by + change Expr.eqv (replayFirstApp (.fvar consAlphaId)) + (ctorIndexedVecApp (.fvar consAlphaId) + (replaySuccApp (.fvar consNId))) = false + rw [Expr.eqv_eq] + simp [Expr.eqv', ctorIndexedVecApp, replayFirstApp, replaySuccApp] + +@[simp] theorem replayTailDomainLiteralBeqFirstApp : + (((.const ``IndexedVec [.param `u] : Expr).app (.fvar consAlphaId)).app + (.fvar consNId) == + (.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId)) = false := by + simpa [ctorIndexedVecApp, replayFirstApp] using + replayTailDomainBeqFirstApp + +@[simp] theorem replayTailDomainLiteralBeqSuccApp : + (((.const ``IndexedVec [.param `u] : Expr).app (.fvar consAlphaId)).app + (.fvar consNId) == + (.const ``Nat.succ [] : Expr).app (.fvar consNId)) = false := by + simpa [ctorIndexedVecApp, replaySuccApp] using + replayTailDomainBeqSuccApp + +@[simp] theorem replayFirstAppLiteralBeqSuccApp : + ((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId) == + (.const ``Nat.succ [] : Expr).app (.fvar consNId)) = false := by + simpa only [replayFirstApp, replaySuccApp] using + (replayFirstAppBeqSuccApp (.fvar consAlphaId) (.fvar consNId)) + +@[simp] theorem replayTailDomainLiteralBeqTerminal : + (((.const ``IndexedVec [.param `u] : Expr).app (.fvar consAlphaId)).app + (.fvar consNId) == + ((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId)).app + ((.const ``Nat.succ [] : Expr).app (.fvar consNId))) = false := by + simpa [ctorIndexedVecApp, replaySuccApp] using + replayTailDomainBeqTerminal + +@[simp] theorem replayFirstAppLiteralBeqTerminal : + ((.const ``IndexedVec [.param `u] : Expr).app (.fvar consAlphaId) == + ((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId)).app + ((.const ``Nat.succ [] : Expr).app (.fvar consNId))) = false := by + simpa [ctorIndexedVecApp, replayFirstApp, replaySuccApp] using + replayFirstAppBeqTerminal + +theorem consAfterHeadCheckFirstCache : + consAfterHeadCheckState.inferTypeC[ + replayFirstApp (.fvar consAlphaId)]? = some vecFamilyTail := by + change consTailDomainFinalState.inferTypeC[ + replayFirstApp (.fvar consAlphaId)]? = some vecFamilyTail + unfold consTailDomainFinalState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [show (consTailDomain == replayFirstApp (.fvar consAlphaId)) = + false by + simpa only [consTailDomain, ctorIndexedVecApp, + consAlphaExprShape, consNExprShape] using + replayTailDomainBeqFirstApp] + simp only [Bool.false_eq_true, if_false] + unfold consHeadNState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [show (consNExpr == replayFirstApp (.fvar consAlphaId)) = false by + simp [consNExprShape, replayFirstApp]] + simp only [Bool.false_eq_true, if_false] + unfold consHeadFirstAppState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [show (replayFirstApp consAlphaExpr == + replayFirstApp (.fvar consAlphaId)) = true by simp] + rfl + +theorem consAfterHeadCheckNCache : + consAfterHeadCheckState.inferTypeC[(.fvar consNId : Expr)]? = + some (.const ``Nat []) := by + change consTailDomainFinalState.inferTypeC[(.fvar consNId : Expr)]? = + some (.const ``Nat []) + unfold consTailDomainFinalState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [show (consTailDomain == (.fvar consNId : Expr)) = false by + simpa only [consTailDomain, consAlphaExprShape, consNExprShape] using + (replayAppBeqFVar + ((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId)) (.fvar consNId) consNId)] + simp only [Bool.false_eq_true, if_false] + unfold consHeadNState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [show (consNExpr == (.fvar consNId : Expr)) = true by simp] + rfl + +theorem consAfterHeadCheckSuccMiss : + consAfterHeadCheckState.inferTypeC[(.const ``Nat.succ [] : Expr)]? = + none := by + simp [consAfterHeadCheckState, consTailDomainFinalState, consHeadNState, consHeadFirstAppState, + replayInsert, consTailDomain, replayFirstApp] + +theorem consAfterHeadCheckSuccAppMiss : + consAfterHeadCheckState.inferTypeC[ + replaySuccApp (.fvar consNId)]? = none := by + simp [consAfterHeadCheckState, consTailDomainFinalState, consHeadNState, consHeadFirstAppState, + replayInsert, consTailDomain, replayFirstApp, replaySuccApp] + +theorem consAfterHeadCheckTerminalMiss : + consAfterHeadCheckState.inferTypeC[ + ctorIndexedVecApp (.fvar consAlphaId) + (replaySuccApp (.fvar consNId))]? = none := by + simp [consAfterHeadCheckState, consTailDomainFinalState, consHeadNState, consHeadFirstAppState, + replayInsert, consTailDomain, ctorIndexedVecApp, replayFirstApp, replaySuccApp] + +def consAfterHeadCheckSuccState : TypeChecker.State := + replayInsert consAfterHeadCheckState (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default) + +def consAfterHeadCheckSuccAppState : TypeChecker.State := + replayInsert consAfterHeadCheckSuccState + (replaySuccApp (.fvar consNId)) (.const ``Nat []) + +def consAfterHeadCheckTerminalState : TypeChecker.State := + replayInsert consAfterHeadCheckSuccAppState + (ctorIndexedVecApp (.fvar consAlphaId) + (replaySuccApp (.fvar consNId))) + (.sort (.succ (.param `u))) + +theorem replayInferConsAfterHeadTerminal : + TypeChecker.Inner.inferType' + (ctorIndexedVecApp (.fvar consAlphaId) + (replaySuccApp (.fvar consNId))) false + (TypeChecker.Methods.withFuel 9998) + (tcContext consAfterHeadCheckLctx) consAfterHeadCheckState = + .ok (.sort (.succ (.param `u)), + consAfterHeadCheckTerminalState) := by + simpa [consAfterHeadCheckSuccState, + consAfterHeadCheckSuccAppState, + consAfterHeadCheckTerminalState] using + (replayInferIndexedVecSuccFromCacheCore 9998 consAfterHeadCheckLctx + consAfterHeadCheckState consAlphaId consNId + consAfterHeadCheckFirstCache consAfterHeadCheckSuccMiss + consAfterHeadCheckNCache consAfterHeadCheckSuccAppMiss + consAfterHeadCheckTerminalMiss) + +theorem replayConsAfterHeadCheckTypeM : + TypeChecker.M.run ctorEnv .safe consHeadContext.lctx [`u] + ({} : FuelConfig) (TypeChecker.checkType consAfterHead) = + .ok (.sort (.succ (.param `u))) := by + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType consAfterHead false + (TypeChecker.Methods.withFuel 10000) + (tcContext consHeadContext.lctx) ({} : TypeChecker.State)) = _ + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' consAfterHead false + (TypeChecker.Methods.withFuel 9999) + (tcContext consHeadContext.lctx) ({} : TypeChecker.State)) = _ + unfold consAfterHead consTailDomain TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', TypeChecker.Inner.inferForall, + TypeChecker.Inner.inferForall.loop, Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [show TypeChecker.Inner.inferType' + (.app + (.app (.const ``IndexedVec [.param `u]) (.fvar consAlphaId)) + (.fvar consNId)) false + (TypeChecker.Methods.withFuel 9998) + (tcContext consHeadContext.lctx) ({} : TypeChecker.State) = + .ok (.sort (.succ (.param `u)), consTailDomainFinalState) by + simpa [consTailDomain, ctorIndexedVecApp] using + replayInferConsTailDomainCore 9998] + simp only [ensureSortExact] + rw [withLocalDeclEq] + simp [Expr.instantiate1'] + have hterminal : + TypeChecker.Inner.inferType + (((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId)).app + ((.const ``Nat.succ [] : Expr).app (.fvar consNId))) false + (TypeChecker.Methods.withFuel 9999) + (tcContext consAfterHeadCheckLctx) consAfterHeadCheckState = + .ok (.sort (.succ (.param `u)), + consAfterHeadCheckTerminalState) := by + change TypeChecker.Inner.inferType' + (((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId)).app + ((.const ``Nat.succ [] : Expr).app (.fvar consNId))) false + (TypeChecker.Methods.withFuel 9998) + (tcContext consAfterHeadCheckLctx) consAfterHeadCheckState = _ + simpa [ctorIndexedVecApp, replaySuccApp] using + replayInferConsAfterHeadTerminal + simp only [consAfterHeadCheckLctx, consAfterHeadCheckTailId, + consAfterHeadCheckState, consTailDomain, consAlphaExprShape, + consNExprShape, tcContext] at hterminal + simp only [tcContext] + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [hterminal] + simp only [ensureSortExact] + simp [Expr.sortLevel!, Pure.pure, ReaderT.pure, + StateT.pure, Except.pure] + rfl + +/-! The two-binder suffix beginning at the `head` field. -/ + +def consAfterNHeadDomainState : TypeChecker.State := + replayInsert ({} : TypeChecker.State) consAlphaExpr + (.sort (.succ (.param `u))) + +theorem replayInferConsAfterNHeadDomain (fuel : Nat) : + TypeChecker.Inner.inferType' consAlphaExpr false + (TypeChecker.Methods.withFuel fuel) + (tcContext consNContext.lctx) ({} : TypeChecker.State) = + .ok (.sort (.succ (.param `u)), + consAfterNHeadDomainState) := by + simpa [consAfterNHeadDomainState, replayInsert, + consAlphaExprShape] using + (inferTypeFVarCore fuel consNContext.lctx ({} : TypeChecker.State) + consAlphaId (.sort (.succ (.param `u))) + (index := 0) (name := consAlphaName) (bi := .implicit) + (kind := .default) (by simp) consAlphaFindInN) + +def consAfterNCheckHeadId : FVarId := + ⟨consAfterNHeadDomainState.ngen.curr⟩ + +def consAfterNCheckLctx : LocalContext := + consNContext.lctx.mkLocalDecl consAfterNCheckHeadId + consHeadName consAlphaExpr .default + +def consAfterNCheckState : TypeChecker.State := + { consAfterNHeadDomainState with + ngen := consAfterNHeadDomainState.ngen.next } + +theorem consAfterNCheckHeadFresh : + consNContext.lctx.find? consAfterNCheckHeadId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := consAfterNCheckHeadId) consNContextWF + rw [h] + simp [consAfterNCheckHeadId, consAfterNHeadDomainState, replayInsert, consNContext, + consAlphaContext, consRootContext, ctorContext, consAlphaId, AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshFVarId, LocalContext.mkLocalDecl, LocalContext.toList, + LocalDecl.fvarId, NameGenerator.next, NameGenerator.curr] + intro x hx + change some x ∈ + (PersistentArray.empty : PersistentArray (Option LocalDecl)).toList' at hx + rw [PersistentArray.toList'_empty] at hx + simp at hx + +theorem consAfterNCheckLctxWF : consAfterNCheckLctx.WF := by + simpa [consAfterNCheckLctx] using + (LocalContext.WF.mkLocalDecl consNContextWF consAfterNCheckHeadFresh) + +theorem consAfterNCheckNFind : + consAfterNCheckLctx.find? consNId = + some (.cdecl 1 consNId consNName (.const ``Nat []) + .implicit .default) := by + rw [consAfterNCheckLctxWF.find?_eq_find?_toList] + simp [consAfterNCheckLctx, consAfterNCheckHeadId, consAfterNHeadDomainState, replayInsert, + consNContext, consAlphaContext, consRootContext, ctorContext, consAlphaId, consNId, + AddInductive.Context.pushLocalDecl, AddInductive.Context.freshFVarId, LocalContext.mkLocalDecl, + LocalContext.toList, LocalDecl.fvarId, NameGenerator.next, NameGenerator.curr] + +theorem consAfterNCheckAlphaCache : + consAfterNCheckState.inferTypeC[(.fvar consAlphaId : Expr)]? = + some (.sort (.succ (.param `u))) := by + change consAfterNHeadDomainState.inferTypeC[ + (.fvar consAlphaId : Expr)]? = some (.sort (.succ (.param `u))) + unfold consAfterNHeadDomainState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [show (consAlphaExpr == (.fvar consAlphaId : Expr)) = true by simp] + rfl + +theorem consAfterNCheckFamilyMiss : + consAfterNCheckState.inferTypeC[ + (.const ``IndexedVec [.param `u] : Expr)]? = none := by + simp [consAfterNCheckState, consAfterNHeadDomainState, replayInsert] + +theorem consAfterNCheckFirstAppMiss : + consAfterNCheckState.inferTypeC[ + replayFirstApp (.fvar consAlphaId)]? = none := by + simp [consAfterNCheckState, consAfterNHeadDomainState, + replayInsert, replayFirstApp] + +def consAfterNCheckFirstAppState : TypeChecker.State := + replayInsert + (replayInsert consAfterNCheckState + (.const ``IndexedVec [.param `u]) indexedVecInfo.type) + (replayFirstApp (.fvar consAlphaId)) vecFamilyTail + +theorem consAfterNCheckNMiss : + consAfterNCheckFirstAppState.inferTypeC[(.fvar consNId : Expr)]? = + none := by + simp [consAfterNCheckFirstAppState, consAfterNCheckState, + consAfterNHeadDomainState, replayInsert, replayFirstApp] + +theorem consAfterNCheckTailMiss : + consAfterNCheckState.inferTypeC[ + ctorIndexedVecApp (.fvar consAlphaId) (.fvar consNId)]? = none := by + simp [consAfterNCheckState, consAfterNHeadDomainState, + replayInsert, ctorIndexedVecApp] + +def consAfterNCheckNState : TypeChecker.State := + replayInsert consAfterNCheckFirstAppState + (.fvar consNId) (.const ``Nat []) + +def consAfterNCheckTailDomainState : TypeChecker.State := + replayInsert consAfterNCheckNState + (ctorIndexedVecApp (.fvar consAlphaId) (.fvar consNId)) + (.sort (.succ (.param `u))) + +theorem replayInferConsAfterNTailDomain (fuel : Nat) : + TypeChecker.Inner.inferType' + (ctorIndexedVecApp (.fvar consAlphaId) (.fvar consNId)) false + (TypeChecker.Methods.withFuel fuel) + (tcContext consAfterNCheckLctx) consAfterNCheckState = + .ok (.sort (.succ (.param `u)), + consAfterNCheckTailDomainState) := by + simpa [consAfterNCheckFirstAppState, consAfterNCheckNState, + consAfterNCheckTailDomainState] using + (replayInferTailDomainAlphaCachedCore fuel consAfterNCheckLctx + consAfterNCheckState consAlphaId consNId + consAfterNCheckAlphaCache consAfterNCheckFamilyMiss + consAfterNCheckFirstAppMiss consAfterNCheckNMiss + consAfterNCheckTailMiss consAfterNCheckNFind) + +def consAfterNCheckTailId : FVarId := + ⟨consAfterNCheckTailDomainState.ngen.curr⟩ + +def consAfterNCheckTailLctx : LocalContext := + consAfterNCheckLctx.mkLocalDecl consAfterNCheckTailId + consTailName + (ctorIndexedVecApp (.fvar consAlphaId) (.fvar consNId)) .default + +def consAfterNCheckTailState : TypeChecker.State := + { consAfterNCheckTailDomainState with + ngen := consAfterNCheckTailDomainState.ngen.next } + +theorem consAfterNCheckTailFirstCache : + consAfterNCheckTailState.inferTypeC[ + replayFirstApp (.fvar consAlphaId)]? = some vecFamilyTail := by + change consAfterNCheckTailDomainState.inferTypeC[ + replayFirstApp (.fvar consAlphaId)]? = some vecFamilyTail + unfold consAfterNCheckTailDomainState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [replayTailDomainBeqFirstApp] + simp only [Bool.false_eq_true, if_false] + unfold consAfterNCheckNState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [show ((.fvar consNId : Expr) == + replayFirstApp (.fvar consAlphaId)) = false by + simpa only [replayFirstApp] using + (replayFVarBeqApp consNId + (.const ``IndexedVec [.param `u]) (.fvar consAlphaId))] + simp only [Bool.false_eq_true, if_false] + unfold consAfterNCheckFirstAppState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [beq_self_eq_true] + rfl + +theorem consAfterNCheckTailNCache : + consAfterNCheckTailState.inferTypeC[(.fvar consNId : Expr)]? = + some (.const ``Nat []) := by + change consAfterNCheckTailDomainState.inferTypeC[ + (.fvar consNId : Expr)]? = some (.const ``Nat []) + unfold consAfterNCheckTailDomainState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [show (ctorIndexedVecApp (.fvar consAlphaId) (.fvar consNId) == + (.fvar consNId : Expr)) = false by + simpa only [ctorIndexedVecApp] using + (replayAppBeqFVar + ((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId)) (.fvar consNId) consNId)] + simp only [Bool.false_eq_true, if_false] + unfold consAfterNCheckNState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [beq_self_eq_true] + rfl + +theorem consAfterNCheckTailSuccMiss : + consAfterNCheckTailState.inferTypeC[ + (.const ``Nat.succ [] : Expr)]? = none := by + simp [consAfterNCheckTailState, consAfterNCheckTailDomainState, consAfterNCheckNState, + consAfterNCheckFirstAppState, consAfterNCheckState, consAfterNHeadDomainState, replayInsert, + ctorIndexedVecApp, replayFirstApp] + +theorem consAfterNCheckTailSuccAppMiss : + consAfterNCheckTailState.inferTypeC[ + replaySuccApp (.fvar consNId)]? = none := by + simp [consAfterNCheckTailState, consAfterNCheckTailDomainState, consAfterNCheckNState, + consAfterNCheckFirstAppState, consAfterNCheckState, consAfterNHeadDomainState, replayInsert, + ctorIndexedVecApp, replayFirstApp, replaySuccApp] + +theorem consAfterNCheckTailTerminalMiss : + consAfterNCheckTailState.inferTypeC[ + ctorIndexedVecApp (.fvar consAlphaId) + (replaySuccApp (.fvar consNId))]? = none := by + simp [consAfterNCheckTailState, consAfterNCheckTailDomainState, consAfterNCheckNState, + consAfterNCheckFirstAppState, consAfterNCheckState, consAfterNHeadDomainState, replayInsert, + ctorIndexedVecApp, replayFirstApp, replaySuccApp] + +def consAfterNCheckSuccState : TypeChecker.State := + replayInsert consAfterNCheckTailState (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default) + +def consAfterNCheckSuccAppState : TypeChecker.State := + replayInsert consAfterNCheckSuccState + (replaySuccApp (.fvar consNId)) (.const ``Nat []) + +def consAfterNCheckTerminalState : TypeChecker.State := + replayInsert consAfterNCheckSuccAppState + (ctorIndexedVecApp (.fvar consAlphaId) + (replaySuccApp (.fvar consNId))) + (.sort (.succ (.param `u))) + +theorem replayInferConsAfterNTerminal (fuel : Nat) : + TypeChecker.Inner.inferType' + (ctorIndexedVecApp (.fvar consAlphaId) + (replaySuccApp (.fvar consNId))) false + (TypeChecker.Methods.withFuel fuel) + (tcContext consAfterNCheckTailLctx) consAfterNCheckTailState = + .ok (.sort (.succ (.param `u)), + consAfterNCheckTerminalState) := by + simpa [consAfterNCheckSuccState, consAfterNCheckSuccAppState, + consAfterNCheckTerminalState] using + (replayInferIndexedVecSuccFromCacheCore fuel + consAfterNCheckTailLctx consAfterNCheckTailState + consAlphaId consNId consAfterNCheckTailFirstCache + consAfterNCheckTailSuccMiss consAfterNCheckTailNCache + consAfterNCheckTailSuccAppMiss consAfterNCheckTailTerminalMiss) + +theorem replayConsAfterNCheckTypeM : + TypeChecker.M.run ctorEnv .safe consNContext.lctx [`u] + ({} : FuelConfig) (TypeChecker.checkType consAfterN) = + .ok (.sort (.succ (.param `u))) := by + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType consAfterN false + (TypeChecker.Methods.withFuel 10000) + (tcContext consNContext.lctx) ({} : TypeChecker.State)) = _ + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' consAfterN false + (TypeChecker.Methods.withFuel 9999) + (tcContext consNContext.lctx) ({} : TypeChecker.State)) = _ + unfold consAfterN TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', + TypeChecker.Inner.inferForall, TypeChecker.Inner.inferForall.loop, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [show TypeChecker.Inner.inferType' + (.fvar consAlphaId) false + (TypeChecker.Methods.withFuel 9998) + (tcContext consNContext.lctx) ({} : TypeChecker.State) = + .ok (.sort (.succ (.param `u)), + consAfterNHeadDomainState) by + simpa [consAlphaExprShape] using + replayInferConsAfterNHeadDomain 9998] + simp only [ensureSortExact] + rw [withLocalDeclEq] + simp [Expr.instantiate1'] + have htail : + TypeChecker.Inner.inferType + (((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId)).app (.fvar consNId)) false + (TypeChecker.Methods.withFuel 9999) + (tcContext consAfterNCheckLctx) consAfterNCheckState = + .ok (.sort (.succ (.param `u)), + consAfterNCheckTailDomainState) := by + change TypeChecker.Inner.inferType' + (((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId)).app (.fvar consNId)) false + (TypeChecker.Methods.withFuel 9998) + (tcContext consAfterNCheckLctx) consAfterNCheckState = _ + simpa [ctorIndexedVecApp] using + replayInferConsAfterNTailDomain 9998 + simp only [consAfterNCheckLctx, consAfterNCheckHeadId, + consAfterNCheckState, consAlphaExprShape, tcContext] at htail + simp only [tcContext] + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [htail] + simp only [ensureSortExact] + rw [withLocalDeclEq] + simp only [] + have hterminal : + TypeChecker.Inner.inferType + (((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId)).app + ((.const ``Nat.succ [] : Expr).app (.fvar consNId))) false + (TypeChecker.Methods.withFuel 9999) + (tcContext consAfterNCheckTailLctx) consAfterNCheckTailState = + .ok (.sort (.succ (.param `u)), + consAfterNCheckTerminalState) := by + change TypeChecker.Inner.inferType' + (((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId)).app + ((.const ``Nat.succ [] : Expr).app (.fvar consNId))) false + (TypeChecker.Methods.withFuel 9998) + (tcContext consAfterNCheckTailLctx) consAfterNCheckTailState = _ + simpa [ctorIndexedVecApp, replaySuccApp] using + replayInferConsAfterNTerminal 9998 + simp only [consAfterNCheckTailLctx, consAfterNCheckTailId, + consAfterNCheckTailState, consAfterNCheckLctx, + consAfterNCheckHeadId, consAlphaExprShape, + ctorIndexedVecApp, tcContext] at hterminal + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [hterminal] + simp only [ensureSortExact] + simp [Expr.sortLevel!, Pure.pure, ReaderT.pure, + StateT.pure, Except.pure] + rfl + +/-! The three-binder suffix beginning at the `n` index. -/ + +open private mkLevelIMaxCore mkLevelMaxCore from Lean.Level in +@[simp] theorem replayMkLevelIMaxSuccZeroSuccParam : + mkLevelIMax' (.succ .zero) (.succ (.param `u)) = + .succ (.param `u) := by + simp [mkLevelIMax', mkLevelIMaxCore, mkLevelMax', mkLevelMaxCore, Level.isNeverZero, Level.isZero, + Level.isExplicit, Level.hasMVar', Level.hasParam', Level.getOffset, Level.getOffsetAux] + +def consAfterAlphaNatState : TypeChecker.State := + replayInsert ({} : TypeChecker.State) (.const ``Nat []) + (.sort (.succ .zero)) + +theorem replayInferConsAfterAlphaNat (fuel : Nat) : + TypeChecker.Inner.inferType' (.const ``Nat []) false + (TypeChecker.Methods.withFuel fuel) + (tcContext consAlphaContext.lctx) ({} : TypeChecker.State) = + .ok (.sort (.succ .zero), consAfterAlphaNatState) := by + simpa [consAfterAlphaNatState, replayInsert] using + (inferTypeNatCore fuel consAlphaContext.lctx + ({} : TypeChecker.State) (by simp)) + +def consAfterAlphaCheckNId : FVarId := + ⟨consAfterAlphaNatState.ngen.curr⟩ + +def consAfterAlphaCheckNLctx : LocalContext := + consAlphaContext.lctx.mkLocalDecl consAfterAlphaCheckNId + consNName (.const ``Nat []) .implicit + +def consAfterAlphaCheckNState : TypeChecker.State := + { consAfterAlphaNatState with + ngen := consAfterAlphaNatState.ngen.next } + +theorem consAfterAlphaCheckNFresh : + consAlphaContext.lctx.find? consAfterAlphaCheckNId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := consAfterAlphaCheckNId) consAlphaContextWF + rw [h] + simp [consAfterAlphaCheckNId, consAfterAlphaNatState, replayInsert, consAlphaContext, + consRootContext, ctorContext, AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshFVarId, LocalContext.mkLocalDecl, LocalContext.toList, + LocalDecl.fvarId, NameGenerator.next, NameGenerator.curr] + intro x hx + change some x ∈ + (PersistentArray.empty : PersistentArray (Option LocalDecl)).toList' at hx + rw [PersistentArray.toList'_empty] at hx + simp at hx + +theorem consAfterAlphaCheckNLctxWF : consAfterAlphaCheckNLctx.WF := by + simpa [consAfterAlphaCheckNLctx] using + (LocalContext.WF.mkLocalDecl consAlphaContextWF + consAfterAlphaCheckNFresh) + +theorem consAfterAlphaCheckAlphaFind : + consAfterAlphaCheckNLctx.find? consAlphaId = + some (.cdecl 0 consAlphaId consAlphaName + (.sort (.succ (.param `u))) .implicit .default) := by + rw [consAfterAlphaCheckNLctxWF.find?_eq_find?_toList] + simp [consAfterAlphaCheckNLctx, consAfterAlphaCheckNId, consAfterAlphaNatState, replayInsert, + consAlphaContext, consRootContext, ctorContext, consAlphaId, AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshFVarId, LocalContext.mkLocalDecl, LocalContext.toList, + LocalDecl.fvarId, NameGenerator.next, NameGenerator.curr] + +theorem consAfterAlphaCheckAlphaMiss : + consAfterAlphaCheckNState.inferTypeC[ + (.fvar consAlphaId : Expr)]? = none := by + simp [consAfterAlphaCheckNState, consAfterAlphaNatState, + replayInsert] + +def consAfterAlphaHeadDomainState : TypeChecker.State := + replayInsert consAfterAlphaCheckNState (.fvar consAlphaId) + (.sort (.succ (.param `u))) + +theorem replayInferConsAfterAlphaHeadDomain (fuel : Nat) : + TypeChecker.Inner.inferType' (.fvar consAlphaId) false + (TypeChecker.Methods.withFuel fuel) + (tcContext consAfterAlphaCheckNLctx) consAfterAlphaCheckNState = + .ok (.sort (.succ (.param `u)), + consAfterAlphaHeadDomainState) := by + simpa [consAfterAlphaHeadDomainState, replayInsert] using + (inferTypeFVarCore fuel consAfterAlphaCheckNLctx + consAfterAlphaCheckNState consAlphaId + (.sort (.succ (.param `u))) + consAfterAlphaCheckAlphaMiss consAfterAlphaCheckAlphaFind) + +def consAfterAlphaCheckHeadId : FVarId := + ⟨consAfterAlphaHeadDomainState.ngen.curr⟩ + +def consAfterAlphaCheckHeadLctx : LocalContext := + consAfterAlphaCheckNLctx.mkLocalDecl consAfterAlphaCheckHeadId + consHeadName (.fvar consAlphaId) .default + +def consAfterAlphaCheckHeadState : TypeChecker.State := + { consAfterAlphaHeadDomainState with + ngen := consAfterAlphaHeadDomainState.ngen.next } + +theorem consAfterAlphaCheckHeadFresh : + consAfterAlphaCheckNLctx.find? consAfterAlphaCheckHeadId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := consAfterAlphaCheckHeadId) consAfterAlphaCheckNLctxWF + rw [h] + simp [consAfterAlphaCheckHeadId, consAfterAlphaHeadDomainState, consAfterAlphaCheckNState, + consAfterAlphaCheckNId, consAfterAlphaNatState, replayInsert, consAfterAlphaCheckNLctx, + consAlphaContext, consRootContext, ctorContext, consAlphaId, AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshFVarId, LocalContext.mkLocalDecl, LocalContext.toList, + LocalDecl.fvarId, NameGenerator.next, NameGenerator.curr] + intro x hx + change some x ∈ + (PersistentArray.empty : PersistentArray (Option LocalDecl)).toList' at hx + rw [PersistentArray.toList'_empty] at hx + simp at hx + +theorem consAfterAlphaCheckHeadLctxWF : + consAfterAlphaCheckHeadLctx.WF := by + simpa [consAfterAlphaCheckHeadLctx] using + (LocalContext.WF.mkLocalDecl consAfterAlphaCheckNLctxWF + consAfterAlphaCheckHeadFresh) + +theorem consAfterAlphaCheckNFind : + consAfterAlphaCheckHeadLctx.find? consAfterAlphaCheckNId = + some (.cdecl 1 consAfterAlphaCheckNId consNName + (.const ``Nat []) .implicit .default) := by + rw [consAfterAlphaCheckHeadLctxWF.find?_eq_find?_toList] + simp [consAfterAlphaCheckHeadLctx, consAfterAlphaCheckHeadId, consAfterAlphaHeadDomainState, + consAfterAlphaCheckNState, consAfterAlphaCheckNLctx, consAfterAlphaCheckNId, + consAfterAlphaNatState, replayInsert, consAlphaContext, consRootContext, ctorContext, + consAlphaId, AddInductive.Context.pushLocalDecl, AddInductive.Context.freshFVarId, + LocalContext.mkLocalDecl, LocalContext.toList, LocalDecl.fvarId, NameGenerator.next, + NameGenerator.curr] + +theorem consAfterAlphaCheckAlphaCache : + consAfterAlphaCheckHeadState.inferTypeC[ + (.fvar consAlphaId : Expr)]? = + some (.sort (.succ (.param `u))) := by + change consAfterAlphaHeadDomainState.inferTypeC[ + (.fvar consAlphaId : Expr)]? = + some (.sort (.succ (.param `u))) + unfold consAfterAlphaHeadDomainState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [beq_self_eq_true] + rfl + +@[simp] theorem replayNatConstBeqIndexedVec : + ((.const ``Nat [] : Expr) == + .const ``IndexedVec [.param `u]) = false := by + change Expr.eqv (.const ``Nat []) + (.const ``IndexedVec [.param `u]) = false + rw [Expr.eqv_eq] + rfl + +@[simp] theorem replayAlphaIdBeqAfterAlphaNId : + ((.fvar consAlphaId : Expr) == + .fvar consAfterAlphaCheckNId) = false := by + change Expr.eqv (.fvar consAlphaId) + (.fvar consAfterAlphaCheckNId) = false + rw [Expr.eqv_eq] + simp [Expr.eqv', consAlphaId, consAfterAlphaCheckNId, consAfterAlphaNatState, replayInsert, + consRootContext, ctorContext, AddInductive.Context.freshFVarId, NameGenerator.curr] + +theorem consAfterAlphaCheckFamilyMiss : + consAfterAlphaCheckHeadState.inferTypeC[ + (.const ``IndexedVec [.param `u] : Expr)]? = none := by + simp [consAfterAlphaCheckHeadState, consAfterAlphaHeadDomainState, + consAfterAlphaCheckNState, consAfterAlphaNatState, replayInsert] + +theorem consAfterAlphaCheckFirstAppMiss : + consAfterAlphaCheckHeadState.inferTypeC[ + replayFirstApp (.fvar consAlphaId)]? = none := by + simp [consAfterAlphaCheckHeadState, consAfterAlphaHeadDomainState, + consAfterAlphaCheckNState, consAfterAlphaNatState, + replayInsert, replayFirstApp] + +def consAfterAlphaCheckFirstAppState : TypeChecker.State := + replayInsert + (replayInsert consAfterAlphaCheckHeadState + (.const ``IndexedVec [.param `u]) indexedVecInfo.type) + (replayFirstApp (.fvar consAlphaId)) vecFamilyTail + +theorem consAfterAlphaCheckNMiss : + consAfterAlphaCheckFirstAppState.inferTypeC[ + (.fvar consAfterAlphaCheckNId : Expr)]? = none := by + simp [consAfterAlphaCheckFirstAppState, + consAfterAlphaCheckHeadState, consAfterAlphaHeadDomainState, + consAfterAlphaCheckNState, consAfterAlphaNatState, + replayInsert, replayFirstApp] + +theorem consAfterAlphaCheckTailMiss : + consAfterAlphaCheckHeadState.inferTypeC[ + ctorIndexedVecApp (.fvar consAlphaId) + (.fvar consAfterAlphaCheckNId)]? = none := by + simp [consAfterAlphaCheckHeadState, consAfterAlphaHeadDomainState, + consAfterAlphaCheckNState, consAfterAlphaNatState, + replayInsert, ctorIndexedVecApp] + +def consAfterAlphaCheckNInferState : TypeChecker.State := + replayInsert consAfterAlphaCheckFirstAppState + (.fvar consAfterAlphaCheckNId) (.const ``Nat []) + +def consAfterAlphaCheckTailDomainState : TypeChecker.State := + replayInsert consAfterAlphaCheckNInferState + (ctorIndexedVecApp (.fvar consAlphaId) + (.fvar consAfterAlphaCheckNId)) + (.sort (.succ (.param `u))) + +theorem replayInferConsAfterAlphaTailDomain (fuel : Nat) : + TypeChecker.Inner.inferType' + (ctorIndexedVecApp (.fvar consAlphaId) + (.fvar consAfterAlphaCheckNId)) false + (TypeChecker.Methods.withFuel fuel) + (tcContext consAfterAlphaCheckHeadLctx) + consAfterAlphaCheckHeadState = + .ok (.sort (.succ (.param `u)), + consAfterAlphaCheckTailDomainState) := by + simpa [consAfterAlphaCheckFirstAppState, + consAfterAlphaCheckNInferState, + consAfterAlphaCheckTailDomainState] using + (replayInferTailDomainAlphaCachedCore fuel + consAfterAlphaCheckHeadLctx consAfterAlphaCheckHeadState + consAlphaId consAfterAlphaCheckNId + consAfterAlphaCheckAlphaCache consAfterAlphaCheckFamilyMiss + consAfterAlphaCheckFirstAppMiss consAfterAlphaCheckNMiss + consAfterAlphaCheckTailMiss consAfterAlphaCheckNFind) + +def consAfterAlphaCheckTailId : FVarId := + ⟨consAfterAlphaCheckTailDomainState.ngen.curr⟩ + +def consAfterAlphaCheckTailLctx : LocalContext := + consAfterAlphaCheckHeadLctx.mkLocalDecl consAfterAlphaCheckTailId + consTailName + (ctorIndexedVecApp (.fvar consAlphaId) + (.fvar consAfterAlphaCheckNId)) .default + +def consAfterAlphaCheckTailState : TypeChecker.State := + { consAfterAlphaCheckTailDomainState with + ngen := consAfterAlphaCheckTailDomainState.ngen.next } + +@[simp] theorem replayAfterAlphaNIdBeqAlphaId : + ((.fvar consAfterAlphaCheckNId : Expr) == + .fvar consAlphaId) = false := by + change Expr.eqv (.fvar consAfterAlphaCheckNId) + (.fvar consAlphaId) = false + rw [Expr.eqv_eq] + simp [Expr.eqv', consAlphaId, consAfterAlphaCheckNId, consAfterAlphaNatState, replayInsert, + consRootContext, ctorContext, AddInductive.Context.freshFVarId, NameGenerator.curr] + +@[simp] theorem replayNatConstBeqSucc : + ((.const ``Nat [] : Expr) == .const ``Nat.succ []) = false := by + change Expr.eqv (.const ``Nat []) (.const ``Nat.succ []) = false + rw [Expr.eqv_eq] + rfl + +@[simp] theorem replayIndexedVecAppBeqFirstApp + (alpha index : Expr) : + (ctorIndexedVecApp alpha index == replayFirstApp alpha) = false := by + change Expr.eqv (ctorIndexedVecApp alpha index) + (replayFirstApp alpha) = false + rw [Expr.eqv_eq] + simp [Expr.eqv', ctorIndexedVecApp, replayFirstApp] + +@[simp] theorem replayIndexedVecAppBeqSuccApp + (alpha n : Expr) : + (ctorIndexedVecApp alpha n == replaySuccApp n) = false := by + change Expr.eqv (ctorIndexedVecApp alpha n) + (replaySuccApp n) = false + rw [Expr.eqv_eq] + simp [Expr.eqv', ctorIndexedVecApp, replaySuccApp] + +@[simp] theorem replayIndexedVecAppBeqIndexedVecSucc + (alpha : Expr) (id : FVarId) : + (ctorIndexedVecApp alpha (.fvar id) == + ctorIndexedVecApp alpha (replaySuccApp (.fvar id))) = false := by + change Expr.eqv (ctorIndexedVecApp alpha (.fvar id)) + (ctorIndexedVecApp alpha (replaySuccApp (.fvar id))) = false + rw [Expr.eqv_eq] + simp [Expr.eqv', ctorIndexedVecApp, replaySuccApp] + +@[simp] theorem replayFirstAppBeqIndexedVecSucc + (alpha n : Expr) : + (replayFirstApp alpha == + ctorIndexedVecApp alpha (replaySuccApp n)) = false := by + change Expr.eqv (replayFirstApp alpha) + (ctorIndexedVecApp alpha (replaySuccApp n)) = false + rw [Expr.eqv_eq] + simp [Expr.eqv', ctorIndexedVecApp, replayFirstApp, replaySuccApp] + +@[simp] theorem replayIndexedVecLiteralBeqSuccApp + (alpha n : Expr) : + (((.const ``IndexedVec [.param `u] : Expr).app alpha).app n == + (.const ``Nat.succ [] : Expr).app n) = false := by + simpa [ctorIndexedVecApp, replaySuccApp] using + replayIndexedVecAppBeqSuccApp alpha n + +@[simp] theorem replayFirstAppLiteralGenericBeqSuccApp + (alpha n : Expr) : + ((.const ``IndexedVec [.param `u] : Expr).app alpha == + (.const ``Nat.succ [] : Expr).app n) = false := by + simpa [replayFirstApp, replaySuccApp] using + replayFirstAppBeqSuccApp alpha n + +@[simp] theorem replayIndexedVecLiteralFVarBeqTerminal + (alpha : Expr) (id : FVarId) : + (((.const ``IndexedVec [.param `u] : Expr).app alpha).app + (.fvar id) == + ((.const ``IndexedVec [.param `u] : Expr).app alpha).app + ((.const ``Nat.succ [] : Expr).app (.fvar id))) = false := by + simpa [ctorIndexedVecApp, replaySuccApp] using + replayIndexedVecAppBeqIndexedVecSucc alpha id + +@[simp] theorem replayFirstAppLiteralGenericBeqTerminal + (alpha : Expr) (id : FVarId) : + ((.const ``IndexedVec [.param `u] : Expr).app alpha == + ((.const ``IndexedVec [.param `u] : Expr).app alpha).app + ((.const ``Nat.succ [] : Expr).app (.fvar id))) = false := by + simpa [ctorIndexedVecApp, replayFirstApp, replaySuccApp] using + replayFirstAppBeqIndexedVecSucc alpha (.fvar id) + +theorem consAfterAlphaCheckTailFirstCache : + consAfterAlphaCheckTailState.inferTypeC[ + replayFirstApp (.fvar consAlphaId)]? = some vecFamilyTail := by + change consAfterAlphaCheckTailDomainState.inferTypeC[ + replayFirstApp (.fvar consAlphaId)]? = some vecFamilyTail + unfold consAfterAlphaCheckTailDomainState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [replayIndexedVecAppBeqFirstApp] + simp only [Bool.false_eq_true, if_false] + unfold consAfterAlphaCheckNInferState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [show ((.fvar consAfterAlphaCheckNId : Expr) == + replayFirstApp (.fvar consAlphaId)) = false by + exact replayFVarBeqApp consAfterAlphaCheckNId + (.const ``IndexedVec [.param `u]) (.fvar consAlphaId)] + simp only [Bool.false_eq_true, if_false] + unfold consAfterAlphaCheckFirstAppState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [beq_self_eq_true] + rfl + +theorem consAfterAlphaCheckTailNCache : + consAfterAlphaCheckTailState.inferTypeC[ + (.fvar consAfterAlphaCheckNId : Expr)]? = + some (.const ``Nat []) := by + change consAfterAlphaCheckTailDomainState.inferTypeC[ + (.fvar consAfterAlphaCheckNId : Expr)]? = + some (.const ``Nat []) + unfold consAfterAlphaCheckTailDomainState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [show (ctorIndexedVecApp (.fvar consAlphaId) + (.fvar consAfterAlphaCheckNId) == + (.fvar consAfterAlphaCheckNId : Expr)) = false by + exact replayAppBeqFVar + ((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId)) + (.fvar consAfterAlphaCheckNId) consAfterAlphaCheckNId] + simp only [Bool.false_eq_true, if_false] + unfold consAfterAlphaCheckNInferState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [beq_self_eq_true] + rfl + +theorem consAfterAlphaCheckTailSuccMiss : + consAfterAlphaCheckTailState.inferTypeC[ + (.const ``Nat.succ [] : Expr)]? = none := by + simp [consAfterAlphaCheckTailState, + consAfterAlphaCheckTailDomainState, + consAfterAlphaCheckNInferState, + consAfterAlphaCheckFirstAppState, + consAfterAlphaCheckHeadState, consAfterAlphaHeadDomainState, + consAfterAlphaCheckNState, consAfterAlphaNatState, + replayInsert, ctorIndexedVecApp, replayFirstApp] + +theorem consAfterAlphaCheckTailSuccAppMiss : + consAfterAlphaCheckTailState.inferTypeC[ + replaySuccApp (.fvar consAfterAlphaCheckNId)]? = none := by + simp [consAfterAlphaCheckTailState, + consAfterAlphaCheckTailDomainState, + consAfterAlphaCheckNInferState, + consAfterAlphaCheckFirstAppState, + consAfterAlphaCheckHeadState, consAfterAlphaHeadDomainState, + consAfterAlphaCheckNState, consAfterAlphaNatState, + replayInsert, ctorIndexedVecApp, replayFirstApp, replaySuccApp] + +theorem consAfterAlphaCheckTailTerminalMiss : + consAfterAlphaCheckTailState.inferTypeC[ + ctorIndexedVecApp (.fvar consAlphaId) + (replaySuccApp (.fvar consAfterAlphaCheckNId))]? = none := by + simp [consAfterAlphaCheckTailState, + consAfterAlphaCheckTailDomainState, + consAfterAlphaCheckNInferState, + consAfterAlphaCheckFirstAppState, + consAfterAlphaCheckHeadState, consAfterAlphaHeadDomainState, + consAfterAlphaCheckNState, consAfterAlphaNatState, + replayInsert, ctorIndexedVecApp, replayFirstApp, replaySuccApp] + +def consAfterAlphaCheckSuccState : TypeChecker.State := + replayInsert consAfterAlphaCheckTailState (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default) + +def consAfterAlphaCheckSuccAppState : TypeChecker.State := + replayInsert consAfterAlphaCheckSuccState + (replaySuccApp (.fvar consAfterAlphaCheckNId)) (.const ``Nat []) + +def consAfterAlphaCheckTerminalState : TypeChecker.State := + replayInsert consAfterAlphaCheckSuccAppState + (ctorIndexedVecApp (.fvar consAlphaId) + (replaySuccApp (.fvar consAfterAlphaCheckNId))) + (.sort (.succ (.param `u))) + +theorem replayInferConsAfterAlphaTerminal (fuel : Nat) : + TypeChecker.Inner.inferType' + (ctorIndexedVecApp (.fvar consAlphaId) + (replaySuccApp (.fvar consAfterAlphaCheckNId))) false + (TypeChecker.Methods.withFuel fuel) + (tcContext consAfterAlphaCheckTailLctx) + consAfterAlphaCheckTailState = + .ok (.sort (.succ (.param `u)), + consAfterAlphaCheckTerminalState) := by + simpa [consAfterAlphaCheckSuccState, + consAfterAlphaCheckSuccAppState, + consAfterAlphaCheckTerminalState] using + (replayInferIndexedVecSuccFromCacheCore fuel + consAfterAlphaCheckTailLctx consAfterAlphaCheckTailState + consAlphaId consAfterAlphaCheckNId + consAfterAlphaCheckTailFirstCache + consAfterAlphaCheckTailSuccMiss + consAfterAlphaCheckTailNCache + consAfterAlphaCheckTailSuccAppMiss + consAfterAlphaCheckTailTerminalMiss) + +theorem replayConsAfterAlphaCheckTypeM : + TypeChecker.M.run ctorEnv .safe consAlphaContext.lctx [`u] + ({} : FuelConfig) (TypeChecker.checkType consAfterAlpha) = + .ok (.sort (.succ (.param `u))) := by + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType consAfterAlpha false + (TypeChecker.Methods.withFuel 10000) + (tcContext consAlphaContext.lctx) ({} : TypeChecker.State)) = _ + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' consAfterAlpha false + (TypeChecker.Methods.withFuel 9999) + (tcContext consAlphaContext.lctx) ({} : TypeChecker.State)) = _ + unfold consAfterAlpha TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', + TypeChecker.Inner.inferForall, TypeChecker.Inner.inferForall.loop, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [show TypeChecker.Inner.inferType' + (.const ``Nat []) false + (TypeChecker.Methods.withFuel 9998) + (tcContext consAlphaContext.lctx) ({} : TypeChecker.State) = + .ok (.sort (.succ .zero), consAfterAlphaNatState) by + exact replayInferConsAfterAlphaNat 9998] + simp only [ensureSortExact] + rw [withLocalDeclEq] + simp only [Expr.instantiate1'] + have hhead : + TypeChecker.Inner.inferType (.fvar consAlphaId) false + (TypeChecker.Methods.withFuel 9999) + (tcContext consAfterAlphaCheckNLctx) + consAfterAlphaCheckNState = + .ok (.sort (.succ (.param `u)), + consAfterAlphaHeadDomainState) := by + change TypeChecker.Inner.inferType' (.fvar consAlphaId) false + (TypeChecker.Methods.withFuel 9998) + (tcContext consAfterAlphaCheckNLctx) + consAfterAlphaCheckNState = _ + exact replayInferConsAfterAlphaHeadDomain 9998 + simp only [consAfterAlphaCheckNLctx, consAfterAlphaCheckNId, + consAfterAlphaCheckNState, tcContext] at hhead + simp only [tcContext] + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [hhead] + simp only [ensureSortExact] + rw [withLocalDeclEq] + simp [Expr.instantiate1'] + have htail : + TypeChecker.Inner.inferType + (((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId)).app + (.fvar consAfterAlphaCheckNId)) false + (TypeChecker.Methods.withFuel 9999) + (tcContext consAfterAlphaCheckHeadLctx) + consAfterAlphaCheckHeadState = + .ok (.sort (.succ (.param `u)), + consAfterAlphaCheckTailDomainState) := by + change TypeChecker.Inner.inferType' + (((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId)).app + (.fvar consAfterAlphaCheckNId)) false + (TypeChecker.Methods.withFuel 9998) + (tcContext consAfterAlphaCheckHeadLctx) + consAfterAlphaCheckHeadState = _ + simpa [ctorIndexedVecApp] using + replayInferConsAfterAlphaTailDomain 9998 + simp only [consAfterAlphaCheckHeadLctx, + consAfterAlphaCheckHeadId, consAfterAlphaCheckHeadState, + consAfterAlphaCheckNLctx, consAfterAlphaCheckNId, + tcContext] at htail + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [htail] + simp only [ensureSortExact] + rw [withLocalDeclEq] + simp + have hterminal : + TypeChecker.Inner.inferType + (((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId)).app + ((.const ``Nat.succ [] : Expr).app + (.fvar consAfterAlphaCheckNId))) false + (TypeChecker.Methods.withFuel 9999) + (tcContext consAfterAlphaCheckTailLctx) + consAfterAlphaCheckTailState = + .ok (.sort (.succ (.param `u)), + consAfterAlphaCheckTerminalState) := by + change TypeChecker.Inner.inferType' + (((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consAlphaId)).app + ((.const ``Nat.succ [] : Expr).app + (.fvar consAfterAlphaCheckNId))) false + (TypeChecker.Methods.withFuel 9998) + (tcContext consAfterAlphaCheckTailLctx) + consAfterAlphaCheckTailState = _ + simpa [ctorIndexedVecApp, replaySuccApp] using + replayInferConsAfterAlphaTerminal 9998 + simp only [consAfterAlphaCheckTailLctx, + consAfterAlphaCheckTailId, consAfterAlphaCheckTailState, + consAfterAlphaCheckHeadLctx, consAfterAlphaCheckHeadId, + consAfterAlphaCheckNLctx, consAfterAlphaCheckNId, + ctorIndexedVecApp, tcContext] at hterminal + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [hterminal] + simp only [ensureSortExact] + simp [Expr.sortLevel!, Pure.pure, ReaderT.pure, + StateT.pure, Except.pure] + rfl + +/-! The complete four-binder constructor type. -/ + +def consRootCheckAlphaId : FVarId := + ⟨nilRootSortState.ngen.curr⟩ + +def consRootCheckAlphaLctx : LocalContext := + consRootContext.lctx.mkLocalDecl consRootCheckAlphaId + consAlphaName (.sort (.succ (.param `u))) .implicit + +def consRootCheckAlphaState : TypeChecker.State := + { nilRootSortState with ngen := nilRootSortState.ngen.next } + +theorem replayInferConsRootSort : + TypeChecker.Inner.inferType' + (.sort (.succ (.param `u))) false + (TypeChecker.Methods.withFuel 9998) + (tcContext consRootContext.lctx) ({} : TypeChecker.State) = + .ok (.sort (.succ (.succ (.param `u))), + nilRootSortState) := by + simpa [consRootContext, ctorContext] using nilRootSortCore + +theorem consRootCheckAlphaFresh : + consRootContext.lctx.find? consRootCheckAlphaId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := consRootCheckAlphaId) LocalContext.WF.nil + change + ({ fvarIdToDecl := PersistentHashMap.empty, + decls := PersistentArray.empty, + auxDeclToFullName := Std.TreeMap.empty } : LocalContext).find? + consRootCheckAlphaId = none + rw [h] + simp [LocalContext.toList] + +theorem consRootCheckAlphaLctxWF : consRootCheckAlphaLctx.WF := by + change (({} : LocalContext).mkLocalDecl consRootCheckAlphaId + consAlphaName (.sort (.succ (.param `u))) .implicit).WF + exact LocalContext.WF.mkLocalDecl LocalContext.WF.nil (by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := consRootCheckAlphaId) LocalContext.WF.nil + change + ({ fvarIdToDecl := PersistentHashMap.empty, + decls := PersistentArray.empty, + auxDeclToFullName := Std.TreeMap.empty } : LocalContext).find? + consRootCheckAlphaId = none + rw [h] + simp [LocalContext.toList]) + +theorem consRootCheckAlphaFind : + consRootCheckAlphaLctx.find? consRootCheckAlphaId = + some (.cdecl 0 consRootCheckAlphaId consAlphaName + (.sort (.succ (.param `u))) .implicit .default) := by + rw [consRootCheckAlphaLctxWF.find?_eq_find?_toList] + simp [consRootCheckAlphaLctx, consRootCheckAlphaId, consRootContext, ctorContext, + nilRootSortState, LocalContext.mkLocalDecl, LocalContext.toList, LocalDecl.fvarId, + NameGenerator.curr] + +theorem consRootCheckNatMiss : + consRootCheckAlphaState.inferTypeC[ + (.const ``Nat [] : Expr)]? = none := by + simp [consRootCheckAlphaState, nilRootSortState] + +def consRootCheckNatState : TypeChecker.State := + replayInsert consRootCheckAlphaState (.const ``Nat []) + (.sort (.succ .zero)) + +theorem replayInferConsRootNat (fuel : Nat) : + TypeChecker.Inner.inferType' (.const ``Nat []) false + (TypeChecker.Methods.withFuel fuel) + (tcContext consRootCheckAlphaLctx) consRootCheckAlphaState = + .ok (.sort (.succ .zero), consRootCheckNatState) := by + simpa [consRootCheckNatState, replayInsert] using + (inferTypeNatCore fuel consRootCheckAlphaLctx + consRootCheckAlphaState consRootCheckNatMiss) + +def consRootCheckNId : FVarId := + ⟨consRootCheckNatState.ngen.curr⟩ + +def consRootCheckNLctx : LocalContext := + consRootCheckAlphaLctx.mkLocalDecl consRootCheckNId + consNName (.const ``Nat []) .implicit + +def consRootCheckNState : TypeChecker.State := + { consRootCheckNatState with ngen := consRootCheckNatState.ngen.next } + +theorem consRootCheckNFresh : + consRootCheckAlphaLctx.find? consRootCheckNId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := consRootCheckNId) consRootCheckAlphaLctxWF + rw [h] + simp [consRootCheckNId, consRootCheckNatState, consRootCheckAlphaState, consRootCheckAlphaLctx, + consRootCheckAlphaId, nilRootSortState, replayInsert, consRootContext, ctorContext, + LocalContext.mkLocalDecl, LocalContext.toList, LocalDecl.fvarId, NameGenerator.next, + NameGenerator.curr] + intro x hx + change some x ∈ + (PersistentArray.empty : PersistentArray (Option LocalDecl)).toList' at hx + rw [PersistentArray.toList'_empty] at hx + simp at hx + +theorem consRootCheckNLctxWF : consRootCheckNLctx.WF := by + simpa [consRootCheckNLctx] using + (LocalContext.WF.mkLocalDecl consRootCheckAlphaLctxWF + consRootCheckNFresh) + +theorem consRootCheckAlphaFindInN : + consRootCheckNLctx.find? consRootCheckAlphaId = + some (.cdecl 0 consRootCheckAlphaId consAlphaName + (.sort (.succ (.param `u))) .implicit .default) := by + rw [consRootCheckNLctxWF.find?_eq_find?_toList] + simp [consRootCheckNLctx, consRootCheckNId, consRootCheckNatState, consRootCheckAlphaState, + consRootCheckAlphaLctx, consRootCheckAlphaId, nilRootSortState, replayInsert, consRootContext, + ctorContext, LocalContext.mkLocalDecl, LocalContext.toList, LocalDecl.fvarId, + NameGenerator.next, NameGenerator.curr] + +theorem consRootCheckAlphaMiss : + consRootCheckNState.inferTypeC[ + (.fvar consRootCheckAlphaId : Expr)]? = none := by + simp [consRootCheckNState, consRootCheckNatState, + consRootCheckAlphaState, nilRootSortState, replayInsert] + +def consRootCheckHeadDomainState : TypeChecker.State := + replayInsert consRootCheckNState (.fvar consRootCheckAlphaId) + (.sort (.succ (.param `u))) + +theorem replayInferConsRootHeadDomain (fuel : Nat) : + TypeChecker.Inner.inferType' (.fvar consRootCheckAlphaId) false + (TypeChecker.Methods.withFuel fuel) + (tcContext consRootCheckNLctx) consRootCheckNState = + .ok (.sort (.succ (.param `u)), + consRootCheckHeadDomainState) := by + simpa [consRootCheckHeadDomainState, replayInsert] using + (inferTypeFVarCore fuel consRootCheckNLctx consRootCheckNState + consRootCheckAlphaId (.sort (.succ (.param `u))) + consRootCheckAlphaMiss consRootCheckAlphaFindInN) + +def consRootCheckHeadId : FVarId := + ⟨consRootCheckHeadDomainState.ngen.curr⟩ + +def consRootCheckHeadLctx : LocalContext := + consRootCheckNLctx.mkLocalDecl consRootCheckHeadId + consHeadName (.fvar consRootCheckAlphaId) .default + +def consRootCheckHeadState : TypeChecker.State := + { consRootCheckHeadDomainState with + ngen := consRootCheckHeadDomainState.ngen.next } + +theorem consRootCheckHeadFresh : + consRootCheckNLctx.find? consRootCheckHeadId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := consRootCheckHeadId) consRootCheckNLctxWF + rw [h] + simp [consRootCheckHeadId, consRootCheckHeadDomainState, consRootCheckNState, consRootCheckNId, + consRootCheckNatState, consRootCheckAlphaState, consRootCheckAlphaLctx, consRootCheckAlphaId, + nilRootSortState, replayInsert, consRootCheckNLctx, consRootContext, ctorContext, + LocalContext.mkLocalDecl, LocalContext.toList, LocalDecl.fvarId, NameGenerator.next, + NameGenerator.curr] + intro x hx + change some x ∈ + (PersistentArray.empty : PersistentArray (Option LocalDecl)).toList' at hx + rw [PersistentArray.toList'_empty] at hx + simp at hx + +theorem consRootCheckHeadLctxWF : consRootCheckHeadLctx.WF := by + simpa [consRootCheckHeadLctx] using + (LocalContext.WF.mkLocalDecl consRootCheckNLctxWF + consRootCheckHeadFresh) + +theorem consRootCheckNFind : + consRootCheckHeadLctx.find? consRootCheckNId = + some (.cdecl 1 consRootCheckNId consNName + (.const ``Nat []) .implicit .default) := by + rw [consRootCheckHeadLctxWF.find?_eq_find?_toList] + simp [consRootCheckHeadLctx, consRootCheckHeadId, consRootCheckHeadDomainState, + consRootCheckNState, consRootCheckNLctx, consRootCheckNId, consRootCheckNatState, + consRootCheckAlphaState, consRootCheckAlphaLctx, consRootCheckAlphaId, nilRootSortState, + replayInsert, consRootContext, ctorContext, LocalContext.mkLocalDecl, LocalContext.toList, + LocalDecl.fvarId, NameGenerator.next, NameGenerator.curr] + +@[simp] theorem replayConsRootAlphaIdBeqNId : + ((.fvar consRootCheckAlphaId : Expr) == + .fvar consRootCheckNId) = false := by + change Expr.eqv (.fvar consRootCheckAlphaId) + (.fvar consRootCheckNId) = false + rw [Expr.eqv_eq] + simp [Expr.eqv', consRootCheckAlphaId, consRootCheckNId, + consRootCheckNatState, consRootCheckAlphaState, + nilRootSortState, replayInsert, + NameGenerator.next, NameGenerator.curr] + +@[simp] theorem replayConsRootNIdBeqAlphaId : + ((.fvar consRootCheckNId : Expr) == + .fvar consRootCheckAlphaId) = false := by + change Expr.eqv (.fvar consRootCheckNId) + (.fvar consRootCheckAlphaId) = false + rw [Expr.eqv_eq] + simp [Expr.eqv', consRootCheckAlphaId, consRootCheckNId, + consRootCheckNatState, consRootCheckAlphaState, + nilRootSortState, replayInsert, + NameGenerator.next, NameGenerator.curr] + +theorem consRootCheckAlphaCache : + consRootCheckHeadState.inferTypeC[ + (.fvar consRootCheckAlphaId : Expr)]? = + some (.sort (.succ (.param `u))) := by + change consRootCheckHeadDomainState.inferTypeC[ + (.fvar consRootCheckAlphaId : Expr)]? = + some (.sort (.succ (.param `u))) + unfold consRootCheckHeadDomainState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [beq_self_eq_true] + rfl + +theorem consRootCheckFamilyMiss : + consRootCheckHeadState.inferTypeC[ + (.const ``IndexedVec [.param `u] : Expr)]? = none := by + simp [consRootCheckHeadState, consRootCheckHeadDomainState, + consRootCheckNState, consRootCheckNatState, + consRootCheckAlphaState, nilRootSortState, replayInsert] + +theorem consRootCheckFirstAppMiss : + consRootCheckHeadState.inferTypeC[ + replayFirstApp (.fvar consRootCheckAlphaId)]? = none := by + simp [consRootCheckHeadState, consRootCheckHeadDomainState, + consRootCheckNState, consRootCheckNatState, + consRootCheckAlphaState, nilRootSortState, + replayInsert, replayFirstApp] + +def consRootCheckFirstAppState : TypeChecker.State := + replayInsert + (replayInsert consRootCheckHeadState + (.const ``IndexedVec [.param `u]) indexedVecInfo.type) + (replayFirstApp (.fvar consRootCheckAlphaId)) vecFamilyTail + +theorem consRootCheckNMiss : + consRootCheckFirstAppState.inferTypeC[ + (.fvar consRootCheckNId : Expr)]? = none := by + simp [consRootCheckFirstAppState, consRootCheckHeadState, + consRootCheckHeadDomainState, consRootCheckNState, + consRootCheckNatState, consRootCheckAlphaState, + nilRootSortState, replayInsert, replayFirstApp] + +theorem consRootCheckTailMiss : + consRootCheckHeadState.inferTypeC[ + ctorIndexedVecApp (.fvar consRootCheckAlphaId) + (.fvar consRootCheckNId)]? = none := by + simp [consRootCheckHeadState, consRootCheckHeadDomainState, + consRootCheckNState, consRootCheckNatState, + consRootCheckAlphaState, nilRootSortState, + replayInsert, ctorIndexedVecApp] + +def consRootCheckNInferState : TypeChecker.State := + replayInsert consRootCheckFirstAppState + (.fvar consRootCheckNId) (.const ``Nat []) + +def consRootCheckTailDomainState : TypeChecker.State := + replayInsert consRootCheckNInferState + (ctorIndexedVecApp (.fvar consRootCheckAlphaId) + (.fvar consRootCheckNId)) + (.sort (.succ (.param `u))) + +theorem replayInferConsRootTailDomain (fuel : Nat) : + TypeChecker.Inner.inferType' + (ctorIndexedVecApp (.fvar consRootCheckAlphaId) + (.fvar consRootCheckNId)) false + (TypeChecker.Methods.withFuel fuel) + (tcContext consRootCheckHeadLctx) consRootCheckHeadState = + .ok (.sort (.succ (.param `u)), + consRootCheckTailDomainState) := by + simpa [consRootCheckFirstAppState, consRootCheckNInferState, + consRootCheckTailDomainState] using + (replayInferTailDomainAlphaCachedCore fuel + consRootCheckHeadLctx consRootCheckHeadState + consRootCheckAlphaId consRootCheckNId + consRootCheckAlphaCache consRootCheckFamilyMiss + consRootCheckFirstAppMiss consRootCheckNMiss + consRootCheckTailMiss consRootCheckNFind) + +def consRootCheckTailId : FVarId := + ⟨consRootCheckTailDomainState.ngen.curr⟩ + +def consRootCheckTailLctx : LocalContext := + consRootCheckHeadLctx.mkLocalDecl consRootCheckTailId + consTailName + (ctorIndexedVecApp (.fvar consRootCheckAlphaId) + (.fvar consRootCheckNId)) .default + +def consRootCheckTailState : TypeChecker.State := + { consRootCheckTailDomainState with + ngen := consRootCheckTailDomainState.ngen.next } + +theorem consRootCheckTailFirstCache : + consRootCheckTailState.inferTypeC[ + replayFirstApp (.fvar consRootCheckAlphaId)]? = + some vecFamilyTail := by + change consRootCheckTailDomainState.inferTypeC[ + replayFirstApp (.fvar consRootCheckAlphaId)]? = some vecFamilyTail + unfold consRootCheckTailDomainState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [replayIndexedVecAppBeqFirstApp] + simp only [Bool.false_eq_true, if_false] + unfold consRootCheckNInferState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [show ((.fvar consRootCheckNId : Expr) == + replayFirstApp (.fvar consRootCheckAlphaId)) = false by + exact replayFVarBeqApp consRootCheckNId + (.const ``IndexedVec [.param `u]) + (.fvar consRootCheckAlphaId)] + simp only [Bool.false_eq_true, if_false] + unfold consRootCheckFirstAppState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [beq_self_eq_true] + rfl + +theorem consRootCheckTailNCache : + consRootCheckTailState.inferTypeC[ + (.fvar consRootCheckNId : Expr)]? = some (.const ``Nat []) := by + change consRootCheckTailDomainState.inferTypeC[ + (.fvar consRootCheckNId : Expr)]? = some (.const ``Nat []) + unfold consRootCheckTailDomainState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [show (ctorIndexedVecApp (.fvar consRootCheckAlphaId) + (.fvar consRootCheckNId) == + (.fvar consRootCheckNId : Expr)) = false by + exact replayAppBeqFVar + ((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consRootCheckAlphaId)) + (.fvar consRootCheckNId) consRootCheckNId] + simp only [Bool.false_eq_true, if_false] + unfold consRootCheckNInferState replayInsert + rw [Std.HashMap.getElem?_insert] + rw [beq_self_eq_true] + rfl + +theorem consRootCheckTailSuccMiss : + consRootCheckTailState.inferTypeC[ + (.const ``Nat.succ [] : Expr)]? = none := by + simp [consRootCheckTailState, consRootCheckTailDomainState, + consRootCheckNInferState, consRootCheckFirstAppState, + consRootCheckHeadState, consRootCheckHeadDomainState, + consRootCheckNState, consRootCheckNatState, + consRootCheckAlphaState, nilRootSortState, + replayInsert, ctorIndexedVecApp, replayFirstApp] + +theorem consRootCheckTailSuccAppMiss : + consRootCheckTailState.inferTypeC[ + replaySuccApp (.fvar consRootCheckNId)]? = none := by + simp [consRootCheckTailState, consRootCheckTailDomainState, + consRootCheckNInferState, consRootCheckFirstAppState, + consRootCheckHeadState, consRootCheckHeadDomainState, + consRootCheckNState, consRootCheckNatState, + consRootCheckAlphaState, nilRootSortState, + replayInsert, ctorIndexedVecApp, replayFirstApp, replaySuccApp] + +theorem consRootCheckTailTerminalMiss : + consRootCheckTailState.inferTypeC[ + ctorIndexedVecApp (.fvar consRootCheckAlphaId) + (replaySuccApp (.fvar consRootCheckNId))]? = none := by + simp [consRootCheckTailState, consRootCheckTailDomainState, + consRootCheckNInferState, consRootCheckFirstAppState, + consRootCheckHeadState, consRootCheckHeadDomainState, + consRootCheckNState, consRootCheckNatState, + consRootCheckAlphaState, nilRootSortState, + replayInsert, ctorIndexedVecApp, replayFirstApp, replaySuccApp] + +def consRootCheckSuccState : TypeChecker.State := + replayInsert consRootCheckTailState (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default) + +def consRootCheckSuccAppState : TypeChecker.State := + replayInsert consRootCheckSuccState + (replaySuccApp (.fvar consRootCheckNId)) (.const ``Nat []) + +def consRootCheckTerminalState : TypeChecker.State := + replayInsert consRootCheckSuccAppState + (ctorIndexedVecApp (.fvar consRootCheckAlphaId) + (replaySuccApp (.fvar consRootCheckNId))) + (.sort (.succ (.param `u))) + +theorem replayInferConsRootTerminal (fuel : Nat) : + TypeChecker.Inner.inferType' + (ctorIndexedVecApp (.fvar consRootCheckAlphaId) + (replaySuccApp (.fvar consRootCheckNId))) false + (TypeChecker.Methods.withFuel fuel) + (tcContext consRootCheckTailLctx) consRootCheckTailState = + .ok (.sort (.succ (.param `u)), + consRootCheckTerminalState) := by + simpa [consRootCheckSuccState, consRootCheckSuccAppState, + consRootCheckTerminalState] using + (replayInferIndexedVecSuccFromCacheCore fuel + consRootCheckTailLctx consRootCheckTailState + consRootCheckAlphaId consRootCheckNId + consRootCheckTailFirstCache consRootCheckTailSuccMiss + consRootCheckTailNCache consRootCheckTailSuccAppMiss + consRootCheckTailTerminalMiss) + +open private mkLevelIMaxCore mkLevelMaxCore from Lean.Level in +@[simp] theorem replayMkLevelIMaxSuccSuccParamSuccParam : + mkLevelIMax' (.succ (.succ (.param `u))) + (.succ (.param `u)) = .succ (.succ (.param `u)) := by + simp [mkLevelIMax', mkLevelIMaxCore, mkLevelMax', mkLevelMaxCore, + Level.isNeverZero, Level.isZero, Level.isExplicit, + Level.hasMVar', Level.hasParam', Level.getOffset, + Level.getOffsetAux, Level.getLevelOffset] + +theorem replayConsRootCheckTypeM : + TypeChecker.M.run ctorEnv .safe consRootContext.lctx [`u] + ({} : FuelConfig) (TypeChecker.checkType indexedVecConsInfo.type) = + .ok (.sort (.succ (.succ (.param `u)))) := by + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType indexedVecConsInfo.type false + (TypeChecker.Methods.withFuel 10000) + (tcContext consRootContext.lctx) ({} : TypeChecker.State)) = _ + rw [consInfoTypeShape] + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' consCtorTypeRaw false + (TypeChecker.Methods.withFuel 9999) + (tcContext consRootContext.lctx) ({} : TypeChecker.State)) = _ + unfold consCtorTypeRaw consNTypeRaw consHeadTypeRaw + consTailTypeRaw consTerminalRaw TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', + TypeChecker.Inner.inferForall, TypeChecker.Inner.inferForall.loop, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [replayInferConsRootSort] + simp only [ensureSortExact] + rw [withLocalDeclEq] + simp [Expr.instantiate1'] + have hnat : + TypeChecker.Inner.inferType (.const ``Nat []) false + (TypeChecker.Methods.withFuel 9999) + (tcContext consRootCheckAlphaLctx) consRootCheckAlphaState = + .ok (.sort (.succ .zero), consRootCheckNatState) := by + change TypeChecker.Inner.inferType' (.const ``Nat []) false + (TypeChecker.Methods.withFuel 9998) + (tcContext consRootCheckAlphaLctx) consRootCheckAlphaState = _ + exact replayInferConsRootNat 9998 + simp only [consRootCheckAlphaLctx, consRootCheckAlphaId, + consRootCheckAlphaState, tcContext] at hnat + simp only [tcContext] + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [hnat] + simp only [ensureSortExact] + rw [withLocalDeclEq] + simp [Expr.instantiate1'] + have hhead : + TypeChecker.Inner.inferType (.fvar consRootCheckAlphaId) false + (TypeChecker.Methods.withFuel 9999) + (tcContext consRootCheckNLctx) consRootCheckNState = + .ok (.sort (.succ (.param `u)), + consRootCheckHeadDomainState) := by + change TypeChecker.Inner.inferType' + (.fvar consRootCheckAlphaId) false + (TypeChecker.Methods.withFuel 9998) + (tcContext consRootCheckNLctx) consRootCheckNState = _ + exact replayInferConsRootHeadDomain 9998 + simp only [consRootCheckNLctx, consRootCheckNId, + consRootCheckNState, consRootCheckAlphaLctx, + consRootCheckAlphaId, tcContext] at hhead + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [hhead] + simp only [ensureSortExact] + rw [withLocalDeclEq] + simp + have htail : + TypeChecker.Inner.inferType + (((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consRootCheckAlphaId)).app + (.fvar consRootCheckNId)) false + (TypeChecker.Methods.withFuel 9999) + (tcContext consRootCheckHeadLctx) consRootCheckHeadState = + .ok (.sort (.succ (.param `u)), + consRootCheckTailDomainState) := by + change TypeChecker.Inner.inferType' + (((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consRootCheckAlphaId)).app + (.fvar consRootCheckNId)) false + (TypeChecker.Methods.withFuel 9998) + (tcContext consRootCheckHeadLctx) consRootCheckHeadState = _ + simpa [ctorIndexedVecApp] using + replayInferConsRootTailDomain 9998 + simp only [consRootCheckHeadLctx, consRootCheckHeadId, + consRootCheckHeadState, consRootCheckNLctx, consRootCheckNId, + consRootCheckAlphaLctx, consRootCheckAlphaId, + tcContext] at htail + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [htail] + simp only [ensureSortExact] + rw [withLocalDeclEq] + simp + have hterminal : + TypeChecker.Inner.inferType + (((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consRootCheckAlphaId)).app + ((.const ``Nat.succ [] : Expr).app + (.fvar consRootCheckNId))) false + (TypeChecker.Methods.withFuel 9999) + (tcContext consRootCheckTailLctx) consRootCheckTailState = + .ok (.sort (.succ (.param `u)), + consRootCheckTerminalState) := by + change TypeChecker.Inner.inferType' + (((.const ``IndexedVec [.param `u] : Expr).app + (.fvar consRootCheckAlphaId)).app + ((.const ``Nat.succ [] : Expr).app + (.fvar consRootCheckNId))) false + (TypeChecker.Methods.withFuel 9998) + (tcContext consRootCheckTailLctx) consRootCheckTailState = _ + simpa [ctorIndexedVecApp, replaySuccApp] using + replayInferConsRootTerminal 9998 + simp only [consRootCheckTailLctx, consRootCheckTailId, + consRootCheckTailState, consRootCheckHeadLctx, + consRootCheckHeadId, consRootCheckNLctx, consRootCheckNId, + consRootCheckAlphaLctx, consRootCheckAlphaId, + ctorIndexedVecApp, tcContext] at hterminal + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [hterminal] + simp only [ensureSortExact] + simp [Expr.sortLevel!, Pure.pure, ReaderT.pure, + StateT.pure, Except.pure] + rfl + +/-! ## Source-indexed candidate trace -/ + +def consAlphaAnnotations : + AddInductive.CandidateTypeAnnotations + (.sort (.succ (.param `u))) where + consumed := .sort (.succ (.param `u)) + trace := .identity _ + +def consNatAnnotations : + AddInductive.CandidateTypeAnnotations (.const ``Nat []) where + consumed := .const ``Nat [] + trace := .identity _ + +def consHeadAnnotations : + AddInductive.CandidateTypeAnnotations consAlphaExpr where + consumed := consAlphaExpr + trace := .identity _ + +def consTailAnnotations : + AddInductive.CandidateTypeAnnotations consTailDomain where + consumed := consTailDomain + trace := .identity _ + +theorem consAlphaAnnotationTraceBuild : + AddInductive.CandidateTypeAnnotationTrace.build + (.sort (.succ (.param `u))) = + ⟨.sort (.succ (.param `u)), .identity _⟩ := by + simp [AddInductive.CandidateTypeAnnotationTrace.build] + +theorem consNatAnnotationTraceBuild : + AddInductive.CandidateTypeAnnotationTrace.build (.const ``Nat []) = + ⟨.const ``Nat [], .identity _⟩ := by + simp [AddInductive.CandidateTypeAnnotationTrace.build] + +theorem consHeadAnnotationTraceBuild : + AddInductive.CandidateTypeAnnotationTrace.build consAlphaExpr = + ⟨consAlphaExpr, .identity _⟩ := by + simp [AddInductive.CandidateTypeAnnotationTrace.build, + consAlphaExprShape] + +theorem consTailAnnotationTraceBuild : + AddInductive.CandidateTypeAnnotationTrace.build consTailDomain = + ⟨consTailDomain, .identity _⟩ := by + simp [AddInductive.CandidateTypeAnnotationTrace.build, consTailDomain] + +theorem consAlphaAnnotationsBuild : + AddInductive.buildCandidateTypeAnnotations + (.sort (.succ (.param `u))) = .ok consAlphaAnnotations := by + unfold AddInductive.buildCandidateTypeAnnotations + rw [consAlphaAnnotationTraceBuild] + rfl + +theorem consNatAnnotationsBuild : + AddInductive.buildCandidateTypeAnnotations (.const ``Nat []) = + .ok consNatAnnotations := by + unfold AddInductive.buildCandidateTypeAnnotations + rw [consNatAnnotationTraceBuild] + rfl + +theorem consHeadAnnotationsBuild : + AddInductive.buildCandidateTypeAnnotations consAlphaExpr = + .ok consHeadAnnotations := by + unfold AddInductive.buildCandidateTypeAnnotations + rw [consHeadAnnotationTraceBuild] + rfl + +theorem consTailAnnotationsBuild : + AddInductive.buildCandidateTypeAnnotations consTailDomain = + .ok consTailAnnotations := by + unfold AddInductive.buildCandidateTypeAnnotations + rw [consTailAnnotationTraceBuild] + rfl + +theorem consAlphaAnnotationsEq : + AddInductive.CandidateIsDefEqStep.Valid + ⟨consRootContext, (.sort (.succ (.param `u))), + consAlphaAnnotations.consumed⟩ := by + simpa [consAlphaAnnotations] using + (candidateIsDefEqSelfValid consRootContext + (.sort (.succ (.param `u))) 9999 rfl) + +theorem consNatAnnotationsEq : + AddInductive.CandidateIsDefEqStep.Valid + ⟨consAlphaContext, (.const ``Nat []), + consNatAnnotations.consumed⟩ := by + simpa [consNatAnnotations] using + (candidateIsDefEqSelfValid consAlphaContext + (.const ``Nat []) 9999 rfl) + +theorem consHeadAnnotationsEq : + AddInductive.CandidateIsDefEqStep.Valid + ⟨consNContext, consAlphaExpr, + consHeadAnnotations.consumed⟩ := by + simpa [consHeadAnnotations] using + (candidateIsDefEqSelfValid consNContext consAlphaExpr 9999 rfl) + +theorem consTailAnnotationsEq : + AddInductive.CandidateIsDefEqStep.Valid + ⟨consHeadContext, consTailDomain, + consTailAnnotations.consumed⟩ := by + simpa [consTailAnnotations] using + (candidateIsDefEqSelfValid consHeadContext consTailDomain 9999 rfl) + +theorem consRootCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨consRootContext, indexedVecConsInfo.type, + .sort (.succ (.succ (.param `u)))⟩ := by + simpa [AddInductive.CandidateCheckTypeStep.Valid, + consRootContext, ctorContext] using + replayConsRootCheckTypeM + +theorem consRootWhnfValid : + AddInductive.CandidateWhnfStep.Valid + ⟨consRootContext, indexedVecConsInfo.type, + indexedVecConsInfo.type⟩ := by + simpa [AddInductive.CandidateWhnfStep.Valid, + consRootContext, ctorContext] using replayConsRootWhnfM + +theorem consAfterAlphaCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨consAlphaContext, consAfterAlpha, + .sort (.succ (.param `u))⟩ := by + simpa [AddInductive.CandidateCheckTypeStep.Valid, + consAlphaContext, consRootContext, ctorContext, + AddInductive.Context.pushLocalDecl] using + replayConsAfterAlphaCheckTypeM + +theorem consAfterAlphaWhnfValid : + AddInductive.CandidateWhnfStep.Valid + ⟨consAlphaContext, consAfterAlpha, consAfterAlpha⟩ := by + simpa [AddInductive.CandidateWhnfStep.Valid, + consAlphaContext, consRootContext, ctorContext, + AddInductive.Context.pushLocalDecl] using + replayConsAfterAlphaWhnfM + +theorem consAfterNCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨consNContext, consAfterN, .sort (.succ (.param `u))⟩ := by + simpa [AddInductive.CandidateCheckTypeStep.Valid, + consNContext, consAlphaContext, consRootContext, ctorContext, + AddInductive.Context.pushLocalDecl] using + replayConsAfterNCheckTypeM + +theorem consAfterNWhnfValid : + AddInductive.CandidateWhnfStep.Valid + ⟨consNContext, consAfterN, consAfterN⟩ := by + simpa [AddInductive.CandidateWhnfStep.Valid, + consNContext, consAlphaContext, consRootContext, ctorContext, + AddInductive.Context.pushLocalDecl] using + replayConsAfterNWhnfM + +theorem consAfterHeadCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨consHeadContext, consAfterHead, + .sort (.succ (.param `u))⟩ := by + simpa [AddInductive.CandidateCheckTypeStep.Valid, + consHeadContext, consNContext, consAlphaContext, + consRootContext, ctorContext, AddInductive.Context.pushLocalDecl] using + replayConsAfterHeadCheckTypeM + +theorem consAfterHeadWhnfValid : + AddInductive.CandidateWhnfStep.Valid + ⟨consHeadContext, consAfterHead, consAfterHead⟩ := by + simpa [AddInductive.CandidateWhnfStep.Valid, + consHeadContext, consNContext, consAlphaContext, + consRootContext, ctorContext, AddInductive.Context.pushLocalDecl] using + replayConsAfterHeadWhnfM + +theorem consTerminalCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨consTailContext, consTerminal, + .sort (.succ (.param `u))⟩ := by + simpa [AddInductive.CandidateCheckTypeStep.Valid, + consTailContext, consHeadContext, consNContext, consAlphaContext, + consRootContext, ctorContext, AddInductive.Context.pushLocalDecl] using + replayConsTerminalCheckTypeM + +theorem consTerminalWhnfValid : + AddInductive.CandidateWhnfStep.Valid + ⟨consTailContext, consTerminal, consTerminal⟩ := by + simpa [AddInductive.CandidateWhnfStep.Valid, + consTailContext, consHeadContext, consNContext, consAlphaContext, + consRootContext, ctorContext, AddInductive.Context.pushLocalDecl] using + replayConsTerminalWhnfM + +theorem consAlphaDomainCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨consRootContext, (.sort (.succ (.param `u))), + .sort (.succ (.succ (.param `u)))⟩ := by + simpa [AddInductive.CandidateCheckTypeStep.Valid, + consRootContext, nilCandidateContext, ctorContext] using + nilDomainCheckValid + +theorem consAlphaDomainWhnfValid : + AddInductive.CandidateWhnfStep.Valid + ⟨consRootContext, (.sort (.succ (.param `u))), + .sort (.succ (.param `u))⟩ := by + simpa [AddInductive.CandidateWhnfStep.Valid, + consRootContext, nilCandidateContext, ctorContext] using + nilDomainWhnfValid + +theorem consNatDomainCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨consAlphaContext, (.const ``Nat []), + .sort (.succ .zero)⟩ := by + simpa [AddInductive.CandidateCheckTypeStep.Valid, + consAlphaContext, consRootContext, ctorContext, + AddInductive.Context.pushLocalDecl] using + ctorNatCheckTypeM consAlphaContext.lctx + +theorem consNatDomainWhnfValid : + AddInductive.CandidateWhnfStep.Valid + ⟨consAlphaContext, (.const ``Nat []), (.const ``Nat [])⟩ := by + simpa [AddInductive.CandidateWhnfStep.Valid, + consAlphaContext, consRootContext, ctorContext, + AddInductive.Context.pushLocalDecl] using + ctorNatWhnfM consAlphaContext.lctx + +theorem consHeadDomainCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨consNContext, consAlphaExpr, + .sort (.succ (.param `u))⟩ := by + simpa [AddInductive.CandidateCheckTypeStep.Valid, + consAlphaExprShape, consNContext, consAlphaContext, + consRootContext, ctorContext, AddInductive.Context.pushLocalDecl] using + (ctorFVarCheckTypeM consNContext.lctx consAlphaId + (.sort (.succ (.param `u))) consAlphaFindInN) + +theorem consHeadDomainWhnfValid : + AddInductive.CandidateWhnfStep.Valid + ⟨consNContext, consAlphaExpr, consAlphaExpr⟩ := by + simpa [AddInductive.CandidateWhnfStep.Valid, + consAlphaExprShape, consNContext, consAlphaContext, + consRootContext, ctorContext, AddInductive.Context.pushLocalDecl] using + (ctorFVarWhnfM consNContext.lctx consAlphaId consAlphaFindInN) + +theorem consTailDomainCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨consHeadContext, consTailDomain, + .sort (.succ (.param `u))⟩ := by + simpa [AddInductive.CandidateCheckTypeStep.Valid, + consHeadContext, consNContext, consAlphaContext, + consRootContext, ctorContext, AddInductive.Context.pushLocalDecl] using + replayConsTailDomainCheckTypeM + +theorem consTailDomainWhnfValid : + AddInductive.CandidateWhnfStep.Valid + ⟨consHeadContext, consTailDomain, consTailDomain⟩ := by + simpa [AddInductive.CandidateWhnfStep.Valid, + consHeadContext, consNContext, consAlphaContext, + consRootContext, ctorContext, AddInductive.Context.pushLocalDecl] using + replayConsTailDomainWhnfM + +def consAlphaDomainCandidateTrace : + AddInductive.CandidateExprTrace consRootContext + (.sort (.succ (.param `u))) := + .terminal consRootContext (.sort (.succ (.param `u))) + (.sort (.succ (.succ (.param `u)))) + (.sort (.succ (.param `u))) + consAlphaDomainCheckValid consAlphaDomainWhnfValid + +def consNatDomainCandidateTrace : + AddInductive.CandidateExprTrace consAlphaContext + (.const ``Nat []) := + .terminal consAlphaContext (.const ``Nat []) + (.sort (.succ .zero)) (.const ``Nat []) + consNatDomainCheckValid consNatDomainWhnfValid + +def consHeadDomainCandidateTrace : + AddInductive.CandidateExprTrace consNContext consAlphaExpr := + .terminal consNContext consAlphaExpr + (.sort (.succ (.param `u))) consAlphaExpr + consHeadDomainCheckValid consHeadDomainWhnfValid + +def consTailDomainCandidateTrace : + AddInductive.CandidateExprTrace consHeadContext consTailDomain := + .terminal consHeadContext consTailDomain + (.sort (.succ (.param `u))) consTailDomain + consTailDomainCheckValid consTailDomainWhnfValid + +def consTerminalCandidateTrace : + AddInductive.CandidateExprTrace consTailContext + (consAfterHead.bindingBody!.instantiate1 + consHeadContext.freshExpr) := + .terminal consTailContext + (consAfterHead.bindingBody!.instantiate1 consHeadContext.freshExpr) + (.sort (.succ (.param `u))) consTerminal + (by simpa only [consTerminalShape] using consTerminalCheckValid) + (by simpa only [consTerminalShape] using consTerminalWhnfValid) + +def consAfterHeadCandidateTrace : + AddInductive.CandidateExprTrace consHeadContext + (consAfterN.bindingBody!.instantiate1 consNContext.freshExpr) := + .forallE consHeadContext + (consAfterN.bindingBody!.instantiate1 consNContext.freshExpr) + (.sort (.succ (.param `u))) consTailName consTailDomain + consAfterHead.bindingBody! .default consHeadContextFresh + consTailAnnotations consTailAnnotationsEq + (by simpa only [consAfterHeadShape] using consAfterHeadCheckValid) + (by + simpa [consAfterN, consAfterHead, consTailDomain, + consAlphaExpr, consNExpr, consRootContext, consAlphaContext, + consNContext, ctorContext, AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshExpr, Expr.bindingBody!, + Expr.instantiate1_eq, Expr.instantiate1', + Expr.liftLooseBVars_zero] using + consAfterHeadWhnfValid) + consTailDomainCandidateTrace consTerminalCandidateTrace + +def consAfterNCandidateTrace : + AddInductive.CandidateExprTrace consNContext + (consAfterAlpha.bindingBody!.instantiate1 + consAlphaContext.freshExpr) := + .forallE consNContext + (consAfterAlpha.bindingBody!.instantiate1 + consAlphaContext.freshExpr) + (.sort (.succ (.param `u))) consHeadName consAlphaExpr + consAfterN.bindingBody! .default consNContextFresh + consHeadAnnotations consHeadAnnotationsEq + (by simpa only [consAfterNShape] using consAfterNCheckValid) + (by + simpa [consAfterAlpha, consAfterN, consAlphaExpr, consNExpr, + consRootContext, consAlphaContext, ctorContext, + AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshExpr, Expr.bindingBody!, + Expr.instantiate1_eq, Expr.instantiate1', + Expr.liftLooseBVars_zero] using + consAfterNWhnfValid) + consHeadDomainCandidateTrace consAfterHeadCandidateTrace + +def consAfterAlphaCandidateTrace : + AddInductive.CandidateExprTrace consAlphaContext + (consNTypeRaw.instantiate1 consRootContext.freshExpr) := + .forallE consAlphaContext + (consNTypeRaw.instantiate1 consRootContext.freshExpr) + (.sort (.succ (.param `u))) consNName (.const ``Nat []) + consAfterAlpha.bindingBody! .implicit consAlphaContextFresh + consNatAnnotations consNatAnnotationsEq + (by simpa only [consAfterAlphaShape] using consAfterAlphaCheckValid) + (by + simpa [consNTypeRaw, consHeadTypeRaw, consTailTypeRaw, + consTerminalRaw, consAfterAlpha, consAlphaExpr, + consRootContext, ctorContext, AddInductive.Context.freshExpr, + Expr.bindingBody!, Expr.instantiate1_eq, Expr.instantiate1', + Expr.liftLooseBVars_zero] using + consAfterAlphaWhnfValid) + consNatDomainCandidateTrace consAfterNCandidateTrace + +def consCandidateTrace : + AddInductive.CandidateExprTrace consRootContext + indexedVecConsInfo.type := + .forallE consRootContext indexedVecConsInfo.type + (.sort (.succ (.succ (.param `u)))) consAlphaName + (.sort (.succ (.param `u))) consNTypeRaw .implicit consRootFresh + consAlphaAnnotations consAlphaAnnotationsEq consRootCheckValid + (by + simpa [consInfoTypeShape, consCtorTypeRaw] using consRootWhnfValid) + consAlphaDomainCandidateTrace consAfterAlphaCandidateTrace + +def consCandidate : AddInductive.CandidateExpr indexedVecConsInfo.type := + ⟨consRootContext, consCandidateTrace⟩ + +theorem consCandidate_view_eq : + consCandidate.view = indexedVecConsInfo.type := by + have habstract (context : AddInductive.Context) (e : Expr) : + e.abstract #[context.freshExpr] = + Expr.abstract1 context.freshFVarId e := by + rw [show #[context.freshExpr] = + ⟨[context.freshFVarId].map Expr.fvar⟩ by rfl] + simp only [Expr.abstract_eq, Expr.abstractList] + simp only [consCandidate, AddInductive.CandidateExpr.view, + consCandidateTrace, consAlphaDomainCandidateTrace, + consNatDomainCandidateTrace, consHeadDomainCandidateTrace, + consTailDomainCandidateTrace, consTerminalCandidateTrace, + consAfterHeadCandidateTrace, consAfterNCandidateTrace, + consAfterAlphaCandidateTrace, AddInductive.CandidateExprTrace.view] + rw [habstract, habstract, habstract, habstract] + rw [consInfoTypeShape] + simp [consCtorTypeRaw, consNTypeRaw, consHeadTypeRaw, + consTailTypeRaw, consTerminalRaw, consTerminal, consTailDomain, + consAlphaExpr, consNExpr, + consRootContext, consAlphaContext, ctorContext, + AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshExpr, + AddInductive.Context.freshFVarId, + Expr.abstract1, NameGenerator.next, NameGenerator.curr] + +/-- The retained `cons` candidate preserves all four Pi nodes, their domains, +and the terminal recursive result under the exact instantiated contexts. -/ +theorem consCandidate_identity : + TypeChecker.CandidateExprIdentity consCandidate.trace := by + change TypeChecker.CandidateExprIdentity consCandidateTrace + unfold consCandidateTrace + refine .forallE (name := consAlphaName) (binderInfo := .implicit) + (body := consNTypeRaw) (annotations := consAlphaAnnotations) + consAlphaDomainCandidateTrace consAfterAlphaCandidateTrace + (by simpa [consCtorTypeRaw] using consInfoTypeShape) + rfl (.terminal rfl) ?_ + · unfold consAfterAlphaCandidateTrace + refine .forallE (name := consNName) (binderInfo := .implicit) + (body := consAfterAlpha.bindingBody!) + (annotations := consNatAnnotations) + consNatDomainCandidateTrace consAfterNCandidateTrace + (by rw [consAfterAlphaShape]; rfl) rfl (.terminal rfl) ?_ + · unfold consAfterNCandidateTrace + refine .forallE (name := consHeadName) (binderInfo := .default) + (body := consAfterN.bindingBody!) + (annotations := consHeadAnnotations) + consHeadDomainCandidateTrace consAfterHeadCandidateTrace + (by rw [consAfterNShape]; rfl) rfl (.terminal rfl) ?_ + · unfold consAfterHeadCandidateTrace + refine .forallE (name := consTailName) (binderInfo := .default) + (body := consAfterHead.bindingBody!) + (annotations := consTailAnnotations) + consTailDomainCandidateTrace consTerminalCandidateTrace + (by rw [consAfterHeadShape]; rfl) rfl (.terminal rfl) ?_ + · unfold consTerminalCandidateTrace + exact .terminal (by + simpa only [Expr.instantiate1_eq] using consTerminalShape.symm) + +theorem consAlphaDomainCandidateTraceLoop (fuel : Nat) : + AddInductive.buildCandidateExpr.loop consRootContext + (.sort (.succ (.param `u))) (fuel + 1) = + .ok consAlphaDomainCandidateTrace := by + simpa only [consAlphaDomainCandidateTrace] using + AddInductive.buildCandidateExpr_loop_of_whnf_nonForall + consRootContext (.sort (.succ (.param `u))) + (.sort (.succ (.succ (.param `u)))) + (.sort (.succ (.param `u))) fuel + consAlphaDomainCheckValid consAlphaDomainWhnfValid rfl + +theorem consNatDomainCandidateTraceLoop (fuel : Nat) : + AddInductive.buildCandidateExpr.loop consAlphaContext + (.const ``Nat []) (fuel + 1) = + .ok consNatDomainCandidateTrace := by + simpa only [consNatDomainCandidateTrace] using + AddInductive.buildCandidateExpr_loop_of_whnf_nonForall + consAlphaContext (.const ``Nat []) (.sort (.succ .zero)) + (.const ``Nat []) fuel consNatDomainCheckValid + consNatDomainWhnfValid rfl + +theorem consHeadDomainCandidateTraceLoop (fuel : Nat) : + AddInductive.buildCandidateExpr.loop consNContext consAlphaExpr + (fuel + 1) = .ok consHeadDomainCandidateTrace := by + simpa only [consHeadDomainCandidateTrace] using + AddInductive.buildCandidateExpr_loop_of_whnf_nonForall + consNContext consAlphaExpr (.sort (.succ (.param `u))) + consAlphaExpr fuel consHeadDomainCheckValid + consHeadDomainWhnfValid (by rw [consAlphaExprShape]; rfl) + +theorem consTailDomainCandidateTraceLoop (fuel : Nat) : + AddInductive.buildCandidateExpr.loop consHeadContext consTailDomain + (fuel + 1) = .ok consTailDomainCandidateTrace := by + simpa only [consTailDomainCandidateTrace] using + AddInductive.buildCandidateExpr_loop_of_whnf_nonForall + consHeadContext consTailDomain (.sort (.succ (.param `u))) + consTailDomain fuel consTailDomainCheckValid + consTailDomainWhnfValid (by rfl) + +theorem consTerminalCandidateTraceLoop (fuel : Nat) : + AddInductive.buildCandidateExpr.loop consTailContext + (consAfterHead.bindingBody!.instantiate1 + consHeadContext.freshExpr) (fuel + 1) = + .ok consTerminalCandidateTrace := by + simpa only [consTerminalCandidateTrace] using + AddInductive.buildCandidateExpr_loop_of_whnf_nonForall + consTailContext + (consAfterHead.bindingBody!.instantiate1 consHeadContext.freshExpr) + (.sort (.succ (.param `u))) consTerminal fuel + (by simpa only [consTerminalShape] using consTerminalCheckValid) + (by simpa only [consTerminalShape] using consTerminalWhnfValid) + (by rfl) + +theorem consAfterHeadCandidateTraceLoop : + AddInductive.buildCandidateExpr.loop consHeadContext + (consAfterN.bindingBody!.instantiate1 consNContext.freshExpr) 997 = + .ok consAfterHeadCandidateTrace := by + rw [show 997 = 996 + 1 by rfl] + simpa only [consAfterHeadCandidateTrace, consTailContext, + consTailAnnotations] using + (AddInductive.buildCandidateExpr_loop_of_whnf_forall + (context := consHeadContext) + (e := consAfterN.bindingBody!.instantiate1 consNContext.freshExpr) + (inferred := .sort (.succ (.param `u))) (fuel := 996) + (name := consTailName) (domain := consTailDomain) + (body := consAfterHead.bindingBody!) (binderInfo := .default) + (hfresh := consHeadContextFresh) + (annotations := consTailAnnotations) + (hannotations := consTailAnnotationsBuild) + (hannotationsEq := consTailAnnotationsEq) + (hcheck := by + simpa only [consAfterHeadShape] using consAfterHeadCheckValid) + (hrun := by + simpa [consAfterN, consAfterHead, consTailDomain, + consAlphaExpr, consNExpr, consRootContext, consAlphaContext, + consNContext, ctorContext, AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshExpr, Expr.bindingBody!, + Expr.instantiate1_eq, Expr.instantiate1', + Expr.liftLooseBVars_zero] using + consAfterHeadWhnfValid) + (domainCandidate := consTailDomainCandidateTrace) + (bodyCandidate := consTerminalCandidateTrace) + (hdomain := by + simpa using consTailDomainCandidateTraceLoop 995) + (hbody := by + simpa [consTailContext, consTailAnnotations] using + consTerminalCandidateTraceLoop 995)) + +theorem consAfterNCandidateTraceLoop : + AddInductive.buildCandidateExpr.loop consNContext + (consAfterAlpha.bindingBody!.instantiate1 + consAlphaContext.freshExpr) 998 = + .ok consAfterNCandidateTrace := by + rw [show 998 = 997 + 1 by rfl] + simpa only [consAfterNCandidateTrace, consHeadContext, + consHeadAnnotations] using + (AddInductive.buildCandidateExpr_loop_of_whnf_forall + (context := consNContext) + (e := consAfterAlpha.bindingBody!.instantiate1 + consAlphaContext.freshExpr) + (inferred := .sort (.succ (.param `u))) (fuel := 997) + (name := consHeadName) (domain := consAlphaExpr) + (body := consAfterN.bindingBody!) (binderInfo := .default) + (hfresh := consNContextFresh) + (annotations := consHeadAnnotations) + (hannotations := consHeadAnnotationsBuild) + (hannotationsEq := consHeadAnnotationsEq) + (hcheck := by + simpa only [consAfterNShape] using consAfterNCheckValid) + (hrun := by + simpa [consAfterAlpha, consAfterN, consAlphaExpr, consNExpr, + consRootContext, consAlphaContext, ctorContext, + AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshExpr, Expr.bindingBody!, + Expr.instantiate1_eq, Expr.instantiate1', + Expr.liftLooseBVars_zero] using + consAfterNWhnfValid) + (domainCandidate := consHeadDomainCandidateTrace) + (bodyCandidate := consAfterHeadCandidateTrace) + (hdomain := by + simpa using consHeadDomainCandidateTraceLoop 996) + (hbody := by + simpa [consHeadContext, consHeadAnnotations] using + consAfterHeadCandidateTraceLoop)) + +theorem consAfterAlphaCandidateTraceLoop : + AddInductive.buildCandidateExpr.loop consAlphaContext + (consNTypeRaw.instantiate1 consRootContext.freshExpr) 999 = + .ok consAfterAlphaCandidateTrace := by + rw [show 999 = 998 + 1 by rfl] + simpa only [consAfterAlphaCandidateTrace, consNContext, + consNatAnnotations] using + (AddInductive.buildCandidateExpr_loop_of_whnf_forall + (context := consAlphaContext) + (e := consNTypeRaw.instantiate1 consRootContext.freshExpr) + (inferred := .sort (.succ (.param `u))) (fuel := 998) + (name := consNName) (domain := .const ``Nat []) + (body := consAfterAlpha.bindingBody!) (binderInfo := .implicit) + (hfresh := consAlphaContextFresh) + (annotations := consNatAnnotations) + (hannotations := consNatAnnotationsBuild) + (hannotationsEq := consNatAnnotationsEq) + (hcheck := by + simpa only [consAfterAlphaShape] using consAfterAlphaCheckValid) + (hrun := by + simpa [consNTypeRaw, consHeadTypeRaw, consTailTypeRaw, + consTerminalRaw, consAfterAlpha, consAlphaExpr, + consRootContext, ctorContext, AddInductive.Context.freshExpr, + Expr.bindingBody!, Expr.instantiate1_eq, Expr.instantiate1', + Expr.liftLooseBVars_zero] using + consAfterAlphaWhnfValid) + (domainCandidate := consNatDomainCandidateTrace) + (bodyCandidate := consAfterNCandidateTrace) + (hdomain := by + simpa using consNatDomainCandidateTraceLoop 997) + (hbody := by + simpa [consNContext, consNatAnnotations] using + consAfterNCandidateTraceLoop)) + +theorem consCandidateTraceLoop : + AddInductive.buildCandidateExpr.loop consRootContext + indexedVecConsInfo.type consRootContext.fuel.inductiveFuel = + .ok consCandidateTrace := by + change AddInductive.buildCandidateExpr.loop consRootContext + indexedVecConsInfo.type (999 + 1) = _ + simpa only [consCandidateTrace, consAlphaContext, + consAlphaAnnotations] using + (AddInductive.buildCandidateExpr_loop_of_whnf_forall + (context := consRootContext) (e := indexedVecConsInfo.type) + (inferred := .sort (.succ (.succ (.param `u)))) (fuel := 999) + (name := consAlphaName) + (domain := .sort (.succ (.param `u))) + (body := consNTypeRaw) (binderInfo := .implicit) + (hfresh := consRootFresh) + (annotations := consAlphaAnnotations) + (hannotations := consAlphaAnnotationsBuild) + (hannotationsEq := consAlphaAnnotationsEq) + (hcheck := consRootCheckValid) + (hrun := by + simpa [consInfoTypeShape, consCtorTypeRaw] using consRootWhnfValid) + (domainCandidate := consAlphaDomainCandidateTrace) + (bodyCandidate := consAfterAlphaCandidateTrace) + (hdomain := by + simpa using consAlphaDomainCandidateTraceLoop 998) + (hbody := by + simpa [consAlphaContext, consAlphaAnnotations] using + consAfterAlphaCandidateTraceLoop)) + +theorem consCandidateProduced : + AddInductive.buildCandidateExpr indexedVecConsInfo.type + consRootContext = .ok consCandidate := by + unfold AddInductive.buildCandidateExpr + simp only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] + rw [consCandidateTraceLoop] + rfl + +def indexedVecNilConstructorCandidate : + AddInductive.CandidateConstructor indexedVecKernelNil where + type := nilCandidate + +def indexedVecConsConstructorCandidate : + AddInductive.CandidateConstructor indexedVecKernelCons where + type := consCandidate + +def indexedVecFamilyListCandidate : + AddInductive.CandidateFamily indexedVecKernelType where + familyType := ⟨indexedVecFamilyCandidate⟩ + constructors := + .cons indexedVecNilConstructorCandidate + (.cons indexedVecConsConstructorCandidate .nil) + +def indexedVecNormalizationCandidate : + AddInductive.NormalizationCandidate [indexedVecKernelType] where + families := .cons indexedVecFamilyListCandidate .nil + +/-- Source-indexed evidence for the complete `IndexedVec` family-type list. -/ +theorem indexedVecFamilyTypeListProduced : + AddInductive.CandidateFamilyTypeListProduced + indexedVecFamilyCandidateContext + (.cons indexedVecFamilyListCandidate.familyType .nil) := by + exact .cons (by + unfold AddInductive.normalizeCandidateFamilyType + simp only [ReaderT.bind, Bind.bind] + simp only [indexedVecKernelType] + rw [indexedVecFamily_candidateTrace] + rfl) .nil + +theorem indexedVecFamilyTypeListCandidateProduced : + (withReader (fun c : AddInductive.Context => { c with lctx := {} }) + (AddInductive.normalizeCandidateFamilyTypeList + [indexedVecKernelType])) indexedVecFamilyCandidateContext = + .ok (.cons indexedVecFamilyListCandidate.familyType .nil) := by + change AddInductive.normalizeCandidateFamilyTypeList + [indexedVecKernelType] indexedVecFamilyCandidateContext = _ + exact indexedVecFamilyTypeListProduced.normalize + +/-- The two constructor positions are assembled in source order. The +dependent list indices rule out truncating, swapping, or reusing either +constructor proof. -/ +theorem indexedVecConstructorListProduced : + AddInductive.CandidateConstructorListProduced ctorContext + indexedVecFamilyListCandidate.constructors := by + have hnil : AddInductive.buildCandidateExpr indexedVecNilInfo.type + ctorContext = .ok nilCandidate := by + simpa [nilCandidateContext] using nilCandidateProduced + have hcons : AddInductive.buildCandidateExpr indexedVecConsInfo.type + ctorContext = .ok consCandidate := by + simpa [consRootContext] using consCandidateProduced + exact .cons (by + unfold AddInductive.normalizeCandidateConstructor + simp only [ReaderT.bind, Bind.bind] + simp only [indexedVecKernelNil] + rw [hnil] + rfl) (.cons (by + unfold AddInductive.normalizeCandidateConstructor + simp only [ReaderT.bind, Bind.bind] + simp only [indexedVecKernelCons] + rw [hcons] + rfl) .nil) + +theorem indexedVecConstructorListCandidateProduced : + AddInductive.normalizeCandidateConstructorList + indexedVecKernelType.ctors ctorContext = + .ok indexedVecFamilyListCandidate.constructors := by + exact indexedVecConstructorListProduced.normalize + +/-- Source-indexed evidence for complete family assembly after constructor +normalization. -/ +theorem indexedVecFamilyListProduced : + AddInductive.CandidateFamilyListProduced ctorContext + (.cons indexedVecFamilyListCandidate.familyType .nil) + indexedVecNormalizationCandidate.families := by + exact .cons indexedVecConstructorListProduced .nil + +theorem indexedVecFamilyListCandidateProduced : + AddInductive.normalizeCandidateFamilyList + (.cons indexedVecFamilyListCandidate.familyType .nil) + ctorContext = + .ok indexedVecNormalizationCandidate.families := by + exact indexedVecFamilyListProduced.normalize + +end IndexedVecConsReplay + +end Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/IndexedVecConstructors.lean b/Ix/Theory/Named/Verify/Environment/IndexedVecConstructors.lean new file mode 100644 index 000000000..2af5cf296 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/IndexedVecConstructors.lean @@ -0,0 +1,1803 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.Environment.IndexedVecCandidate + +/-! +# IndexedVec constructor normalization candidates + +Exact ordinary-checker and candidate-producer traces for the real `nil` and +`cons` constructor metadata, staged in the post-family kernel environment. +This module extends the family-validation seam proved in +`IndexedVecCandidate` toward the complete normalization candidate used by the +certified inductive-generation path. +-/ + +namespace Ix.Theory.Named.InductiveReplayFixtures +open Lean Meta +open Ix.Theory.Named.InductiveFixtures + +def ctorEnv : Kernel.Environment := + -- `declareInductiveTypes` preserves the input environment header while + -- inserting the raw family constant. Use that exact staged header so this + -- environment is not merely lookup-equivalent to the producer result. + Kernel.Environment.ofConstants `_indexedVecCandidate indexedVecTypeMap + +def ctorContext : AddInductive.Context where + env := ctorEnv + lparams := [`u] + safety := .safe + allowPrimitive := false + +theorem type_lookup_family : + ctorEnv.find? ``IndexedVec = some indexedVecInfo := by + change indexedVecTypeMap.find?' ``IndexedVec = some indexedVecInfo + rw [indexedVecTypeMap_wf.find?'_eq_find?, indexedVecTypeMap, + natMap_wf.find?_insert] + rfl + +theorem type_lookup_nat : ctorEnv.find? ``Nat = some natInfo := by + change indexedVecTypeMap.find?' ``Nat = some natInfo + rw [indexedVecTypeMap_wf.find?'_eq_find?, indexedVecTypeMap, + natMap_wf.find?_insert, nat_type_map_lookup] + simp +decide + +theorem nat_zero_map_lookup : + natMap.find? ``Nat.zero = some natZeroInfo := by + rw [natMap, natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert] + rfl + +theorem type_lookup_zero : + ctorEnv.find? ``Nat.zero = some natZeroInfo := by + change indexedVecTypeMap.find?' ``Nat.zero = some natZeroInfo + rw [indexedVecTypeMap_wf.find?'_eq_find?, indexedVecTypeMap, + natMap_wf.find?_insert, nat_zero_map_lookup] + simp +decide + +theorem type_lookup_succ : + ctorEnv.find? ``Nat.succ = some natSuccInfo := by + change indexedVecTypeMap.find?' ``Nat.succ = some natSuccInfo + rw [indexedVecTypeMap_wf.find?'_eq_find?, indexedVecTypeMap, + natMap_wf.find?_insert, nat_succ_map_lookup] + simp +decide + +@[simp] theorem type_get_family : + ctorEnv.get ``IndexedVec = .ok indexedVecInfo := by + unfold Kernel.Environment.get + rw [type_lookup_family] + rfl + +@[simp] theorem type_get_nat : ctorEnv.get ``Nat = .ok natInfo := by + unfold Kernel.Environment.get + rw [type_lookup_nat] + rfl + +@[simp] theorem type_get_zero : + ctorEnv.get ``Nat.zero = .ok natZeroInfo := by + unfold Kernel.Environment.get + rw [type_lookup_zero] + rfl + +@[simp] theorem type_get_succ : + ctorEnv.get ``Nat.succ = .ok natSuccInfo := by + unfold Kernel.Environment.get + rw [type_lookup_succ] + rfl + +def tcContext (lctx : LocalContext := {}) : TypeChecker.Context where + env := ctorEnv + lctx := lctx + lparams := [`u] + +@[simp] theorem checkLevelSuccParam (lctx) : + TypeChecker.Inner.checkLevel (tcContext lctx) + (.succ (.param `u)) = .ok () := by + simp [TypeChecker.Inner.checkLevel, tcContext, + Level.getUndefParam, Level.forEach, + Level.hasParam_eq, Level.hasParam'] + rfl + +@[simp] theorem checkLevelParam (lctx) : + TypeChecker.Inner.checkLevel (tcContext lctx) (.param `u) = + .ok () := by + simp [TypeChecker.Inner.checkLevel, tcContext, + Level.getUndefParam, Level.forEach, + Level.hasParam_eq, Level.hasParam'] + rfl + +@[simp] theorem indexedVecInfoLevelParams : + indexedVecInfo.levelParams = [`u] := rfl + +@[simp] theorem indexedVecInfoIsUnsafe : + indexedVecInfo.isUnsafe = false := rfl + +@[simp] theorem indexedVecInfoInstantiate : + indexedVecInfo.instantiateTypeLevelParams [.param `u] = + indexedVecInfo.type := by + rw [ConstantInfo.instantiateTypeLevelParams, + ConstantVal.instantiateTypeLevelParams, + Expr.instantiateLevelParams_eq] + simp [indexedVecInfo, ConstantInfo.type, ConstantInfo.toConstantVal, + Expr.instantiateLevelParamsCore', Level.substParams'] + +@[simp] theorem inferConstantFamily (lctx) : + TypeChecker.Inner.inferConstant (tcContext lctx) ``IndexedVec + [.param `u] false = .ok indexedVecInfo.type := by + unfold TypeChecker.Inner.inferConstant + simp only [tcContext] + rw [type_get_family] + simp only [Bind.bind, Except.bind] + rw [show indexedVecInfo.levelParams.length = 1 by rfl] + simp + rw [show TypeChecker.Inner.checkLevel + ({ env := ctorEnv, lctx := lctx, lparams := [`u] } : + TypeChecker.Context) (.param `u) = .ok () by + simpa [tcContext] using checkLevelParam lctx] + simp [indexedVecInfo, Bind.bind, Except.bind, Pure.pure, Except.pure] + +@[simp] theorem inferConstantNat (lctx) : + TypeChecker.Inner.inferConstant (tcContext lctx) ``Nat [] false = + .ok (.sort (.succ .zero)) := by + unfold TypeChecker.Inner.inferConstant + simp [tcContext, natInfo, + ConstantInfo.levelParams, ConstantInfo.isUnsafe, + ConstantInfo.instantiateTypeLevelParams, ConstantInfo.toConstantVal, + ConstantVal.instantiateTypeLevelParams, + Expr.instantiateLevelParams_eq, Expr.instantiateLevelParamsCore', + Level.substParams', + Bind.bind, Except.bind, Pure.pure, Except.pure] + +@[simp] theorem inferConstantZero (lctx) : + TypeChecker.Inner.inferConstant (tcContext lctx) ``Nat.zero [] false = + .ok (.const ``Nat []) := by + unfold TypeChecker.Inner.inferConstant + simp [tcContext, natZeroInfo, + ConstantInfo.levelParams, ConstantInfo.isUnsafe, + ConstantInfo.instantiateTypeLevelParams, ConstantInfo.toConstantVal, + ConstantVal.instantiateTypeLevelParams, + Expr.instantiateLevelParams_eq, Expr.instantiateLevelParamsCore', + Bind.bind, Except.bind, Pure.pure, Except.pure] + +@[simp] theorem inferConstantSucc (lctx) : + TypeChecker.Inner.inferConstant (tcContext lctx) ``Nat.succ [] false = + .ok (.forallE `n (.const ``Nat []) (.const ``Nat []) .default) := by + unfold TypeChecker.Inner.inferConstant + simp [tcContext, natSuccInfo, + ConstantInfo.levelParams, ConstantInfo.isUnsafe, + ConstantInfo.instantiateTypeLevelParams, ConstantInfo.toConstantVal, + ConstantVal.instantiateTypeLevelParams, + Expr.instantiateLevelParams_eq, Expr.instantiateLevelParamsCore', + Bind.bind, Except.bind, Pure.pure, Except.pure] + +theorem selfDefEq (e : Expr) fuel context state : + TypeChecker.Inner.isDefEq e e (TypeChecker.Methods.withFuel fuel) + context state = .ok (true, state) := by + unfold TypeChecker.Inner.isDefEq + rw [if_pos (Expr.eqv_refl _)] + rfl + +@[simp] theorem constBeqFVar (name : Name) (levels : List Level) + (id : FVarId) : + ((.const name levels : Expr) == .fvar id) = false := by + change Expr.eqv (.const name levels) (.fvar id) = false + rw [Expr.eqv_eq] + rfl + +@[simp] theorem sortBeqFVar (level : Level) (id : FVarId) : + ((.sort level : Expr) == .fvar id) = false := by + change Expr.eqv (.sort level) (.fvar id) = false + rw [Expr.eqv_eq] + rfl + +@[simp] theorem fvarBeqConst (id : FVarId) (name : Name) + (levels : List Level) : + ((.fvar id : Expr) == .const name levels) = false := by + change Expr.eqv (.fvar id) (.const name levels) = false + rw [Expr.eqv_eq] + rfl + +@[simp] theorem sortBeqApp (level : Level) (fn arg : Expr) : + ((.sort level : Expr) == .app fn arg) = false := by + change Expr.eqv (.sort level) (.app fn arg) = false + rw [Expr.eqv_eq] + rfl + +@[simp] theorem indexedVecConstBeqZero : + ((.const ``IndexedVec [.param `u] : Expr) == + .const ``Nat.zero []) = false := by + change Expr.eqv (.const ``IndexedVec [.param `u]) + (.const ``Nat.zero []) = false + rw [Expr.eqv_eq] + rfl + +theorem withLocalDeclEq + {α} (name : Name) (bi : BinderInfo) (ty : Expr) + (k : Expr → TypeChecker.RecM α) + (methods : TypeChecker.Methods) + (context : TypeChecker.Context) + (state : TypeChecker.State) : + (withLocalDecl (m := TypeChecker.RecM) name bi ty k) + methods context state = + k (.fvar ⟨state.ngen.curr⟩) methods + { context with lctx := + context.lctx.mkLocalDecl ⟨state.ngen.curr⟩ name ty bi } + { state with ngen := state.ngen.next } := rfl + +def nilRootSortState : TypeChecker.State := + { ({} : TypeChecker.State) with + inferTypeC := ({} : TypeChecker.State).inferTypeC.insert + (.sort (.succ (.param `u))) + (.sort (.succ (.succ (.param `u)))) } + +def nilAlphaId : FVarId := + ⟨nilRootSortState.ngen.curr⟩ + +def nilAlphaLctx : LocalContext := + ({} : LocalContext).mkLocalDecl nilAlphaId `α + (.sort (.succ (.param `u))) .implicit + +def nilBodyInitialState : TypeChecker.State := + { nilRootSortState with ngen := nilRootSortState.ngen.next } + +theorem nilAlphaFind : + nilAlphaLctx.find? nilAlphaId = + some (.cdecl 0 nilAlphaId `α + (.sort (.succ (.param `u))) .implicit .default) := by + have hfresh : ({} : LocalContext).find? nilAlphaId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := nilAlphaId) LocalContext.WF.nil + change + ({ fvarIdToDecl := PersistentHashMap.empty, + decls := PersistentArray.empty, + auxDeclToFullName := Std.TreeMap.empty } : LocalContext).find? + nilAlphaId = none + rw [h] + simp [LocalContext.toList] + have hwf : nilAlphaLctx.WF := by + change (({} : LocalContext).mkLocalDecl nilAlphaId `α + (.sort (.succ (.param `u))) .implicit).WF + exact LocalContext.WF.mkLocalDecl LocalContext.WF.nil hfresh + rw [hwf.find?_eq_find?_toList] + simp only [nilAlphaLctx] + rw [LocalContext.mkLocalDecl_toList] + rw [show ({} : LocalContext).toList = [] by rfl] + rw [show ({} : LocalContext).decls.size = 0 by rfl] + change + (if nilAlphaId == nilAlphaId then + some (LocalDecl.cdecl 0 nilAlphaId `α + (.sort (.succ (.param `u))) .implicit .default) + else none) = _ + rw [beq_self_eq_true] + rfl + +example : + TypeChecker.Inner.inferFVar (tcContext nilAlphaLctx) nilAlphaId = + .ok (.sort (.succ (.param `u))) := by + unfold TypeChecker.Inner.inferFVar + simp [tcContext, nilAlphaFind, LocalDecl.type, + Pure.pure, Except.pure] + +theorem inferTypeFamilyCore + (fuel : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (hcache : state.inferTypeC[ + (.const ``IndexedVec [.param `u] : Expr)]? = none) : + TypeChecker.Inner.inferType' + (.const ``IndexedVec [.param `u]) false + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (indexedVecInfo.type, + { state with inferTypeC := + (state.inferTypeC.insert + (.const ``IndexedVec [.param `u]) indexedVecInfo.type) }) := by + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache, + inferConstantFamily, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + +theorem inferTypeFVarCore + (fuel : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (id : FVarId) (type : Expr) + (hcache : state.inferTypeC[(.fvar id : Expr)]? = none) + (hfind : lctx.find? id = some (.cdecl index id name type bi kind)) : + TypeChecker.Inner.inferType' (.fvar id) false + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (type, { state with inferTypeC := + (state.inferTypeC.insert (.fvar id) type) }) := by + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache, + TypeChecker.Inner.inferFVar, tcContext, hfind, + LocalDecl.type, Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +theorem inferTypeZeroCore + (fuel : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (hcache : state.inferTypeC[(.const ``Nat.zero [] : Expr)]? = none) : + TypeChecker.Inner.inferType' (.const ``Nat.zero []) false + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (.const ``Nat [], { state with inferTypeC := + (state.inferTypeC.insert (.const ``Nat.zero []) (.const ``Nat [])) }) := by + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache, + inferConstantZero, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + +theorem inferTypeNatCore + (fuel : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (hcache : state.inferTypeC[(.const ``Nat [] : Expr)]? = none) : + TypeChecker.Inner.inferType' (.const ``Nat []) false + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (.sort (.succ .zero), { state with inferTypeC := + (state.inferTypeC.insert (.const ``Nat []) + (.sort (.succ .zero))) }) := by + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache, + inferConstantNat, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + +theorem inferTypeSuccCore + (fuel : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (hcache : state.inferTypeC[(.const ``Nat.succ [] : Expr)]? = none) : + TypeChecker.Inner.inferType' (.const ``Nat.succ []) false + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (.forallE `n (.const ``Nat []) (.const ``Nat []) .default, + { state with inferTypeC := + (state.inferTypeC.insert (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default)) }) := by + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache, + inferConstantSucc, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + +@[simp] theorem ensureForallExact + (name : Name) (domain body : Expr) (bi : BinderInfo) + (source : Expr) (fuel : Nat) (context : TypeChecker.Context) + (state : TypeChecker.State) : + TypeChecker.Inner.ensureForallCore (.forallE name domain body bi) + source (TypeChecker.Methods.withFuel fuel) context state = + .ok (.forallE name domain body bi, state) := by + rfl + +theorem inferAppCoreOf + (fuel : Nat) (context : TypeChecker.Context) + (state stateFn stateArg : TypeChecker.State) + (fn arg domain body : Expr) (name : Name) (bi : BinderInfo) + (hclosed : (.app fn arg : Expr).hasLooseBVars = false) + (hcache : state.inferTypeC[(.app fn arg : Expr)]? = none) + (hfn : TypeChecker.Inner.inferType' fn false + (TypeChecker.Methods.withFuel fuel) context state = + .ok (.forallE name domain body bi, stateFn)) + (harg : TypeChecker.Inner.inferType' arg false + (TypeChecker.Methods.withFuel fuel) context stateFn = + .ok (domain, stateArg)) + (heager : arg.isAppOfArity ``eagerReduce 2 = false) : + TypeChecker.Inner.inferType' (.app fn arg) false + (TypeChecker.Methods.withFuel fuel) context state = + .ok (body.instantiate1 arg, + { stateArg with inferTypeC := + (stateArg.inferTypeC.insert + (.app fn arg) (body.instantiate1 arg)) }) := by + unfold TypeChecker.Inner.inferType' + simp [hclosed, hcache, hfn, harg, heager, selfDefEq, Expr.instantiate1_eq, Bind.bind, + ReaderT.bind, StateT.bind, Except.bind] + +theorem inferTypeForallCore + (fuel : Nat) (context : TypeChecker.Context) + (state finalState : TypeChecker.State) + (name : Name) (domain body result : Expr) (bi : BinderInfo) + (hclosed : (.forallE name domain body bi : Expr).hasLooseBVars = false) + (hcache : state.inferTypeC[(.forallE name domain body bi : Expr)]? = none) + (hforall : TypeChecker.Inner.inferForall + (.forallE name domain body bi) false + (TypeChecker.Methods.withFuel fuel) context state = + .ok (result, finalState)) : + TypeChecker.Inner.inferType' + (.forallE name domain body bi) false + (TypeChecker.Methods.withFuel fuel) context state = + .ok (result, { finalState with inferTypeC := + (finalState.inferTypeC.insert + (.forallE name domain body bi) result) }) := by + unfold TypeChecker.Inner.inferType' + simp [hclosed, hcache, hforall, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + +def nilBodyExpr : Expr := + .app (.app (.const ``IndexedVec [.param `u]) (.fvar nilAlphaId)) + (.const ``Nat.zero []) + +def vecIndexName : Name := + indexedVecInfo.type.bindingBody!.bindingName! + +def vecFamilyTail : Expr := + .forallE vecIndexName (.const ``Nat []) + (.sort (.succ (.param `u))) .default + +theorem indexedVecInfoTypeShape : + indexedVecInfo.type = + .forallE `α (.sort (.succ (.param `u))) vecFamilyTail .default := by + rfl + +@[simp] theorem vecFamilyTailInstantiate (arg : Expr) : + vecFamilyTail.instantiate1 arg = vecFamilyTail := by + simp [vecFamilyTail, Expr.instantiate1_eq, Expr.instantiate1'] + +def nilFamilyState : TypeChecker.State := + { nilBodyInitialState with inferTypeC := + (nilBodyInitialState.inferTypeC.insert + (.const ``IndexedVec [.param `u]) indexedVecInfo.type) } + +def nilAlphaState : TypeChecker.State := + { nilFamilyState with inferTypeC := + (nilFamilyState.inferTypeC.insert (.fvar nilAlphaId) + (.sort (.succ (.param `u)))) } + +def nilFirstApp : Expr := + .app (.const ``IndexedVec [.param `u]) (.fvar nilAlphaId) + +def nilFirstAppState : TypeChecker.State := + { nilAlphaState with inferTypeC := + (nilAlphaState.inferTypeC.insert nilFirstApp vecFamilyTail) } + +def nilZeroState : TypeChecker.State := + { nilFirstAppState with inferTypeC := + (nilFirstAppState.inferTypeC.insert (.const ``Nat.zero []) + (.const ``Nat [])) } + +theorem inferNilFamily : + TypeChecker.Inner.inferType' + (.const ``IndexedVec [.param `u]) false + (TypeChecker.Methods.withFuel 9998) + (tcContext nilAlphaLctx) nilBodyInitialState = + .ok (indexedVecInfo.type, nilFamilyState) := by + simpa [nilFamilyState] using + (inferTypeFamilyCore 9998 nilAlphaLctx nilBodyInitialState (by + simp [nilBodyInitialState, nilRootSortState])) + +theorem inferNilAlpha : + TypeChecker.Inner.inferType' (.fvar nilAlphaId) false + (TypeChecker.Methods.withFuel 9998) + (tcContext nilAlphaLctx) nilFamilyState = + .ok (.sort (.succ (.param `u)), nilAlphaState) := by + simpa [nilAlphaState] using + (inferTypeFVarCore 9998 nilAlphaLctx nilFamilyState nilAlphaId + (.sort (.succ (.param `u))) (index := 0) (name := `α) + (bi := .implicit) (kind := .default) (by + simp [nilFamilyState, nilBodyInitialState, nilRootSortState]) nilAlphaFind) + +theorem inferNilFirstApp : + TypeChecker.Inner.inferType' nilFirstApp false + (TypeChecker.Methods.withFuel 9998) + (tcContext nilAlphaLctx) nilBodyInitialState = + .ok (vecFamilyTail, nilFirstAppState) := by + have h := inferAppCoreOf 9998 (tcContext nilAlphaLctx) + nilBodyInitialState nilFamilyState nilAlphaState + (.const ``IndexedVec [.param `u]) (.fvar nilAlphaId) + (.sort (.succ (.param `u))) vecFamilyTail `α .default + (by + simp [Expr.hasLooseBVars, Expr.looseBVarRange']) + (by + simp [nilBodyInitialState, nilRootSortState]) + (by simpa [indexedVecInfoTypeShape] using inferNilFamily) + inferNilAlpha (by rfl) + simpa [nilFirstApp, nilFirstAppState, vecFamilyTail, + Expr.instantiate1'] using h + +theorem inferNilZero : + TypeChecker.Inner.inferType' (.const ``Nat.zero []) false + (TypeChecker.Methods.withFuel 9998) + (tcContext nilAlphaLctx) nilFirstAppState = + .ok (.const ``Nat [], nilZeroState) := by + simpa [nilZeroState] using + (inferTypeZeroCore 9998 nilAlphaLctx nilFirstAppState (by + simp [nilFirstAppState, nilAlphaState, nilFamilyState, nilBodyInitialState, nilRootSortState, + nilFirstApp])) + +theorem inferNilBodyExists : ∃ finalState, + TypeChecker.Inner.inferType nilBodyExpr false + (TypeChecker.Methods.withFuel 9999) + (tcContext nilAlphaLctx) nilBodyInitialState = + .ok (.sort (.succ (.param `u)), finalState) := by + refine ⟨{ nilZeroState with inferTypeC := + (nilZeroState.inferTypeC.insert nilBodyExpr + (.sort (.succ (.param `u)))) }, ?_⟩ + change TypeChecker.Inner.inferType' nilBodyExpr false + (TypeChecker.Methods.withFuel 9998) + (tcContext nilAlphaLctx) nilBodyInitialState = _ + have h := inferAppCoreOf 9998 (tcContext nilAlphaLctx) + nilBodyInitialState nilFirstAppState nilZeroState + nilFirstApp (.const ``Nat.zero []) (.const ``Nat []) + (.sort (.succ (.param `u))) vecIndexName .default + (by simp [nilFirstApp, Expr.hasLooseBVars, Expr.looseBVarRange']) + (by simp [nilBodyInitialState, nilRootSortState]) + (by simpa [vecFamilyTail] using inferNilFirstApp) + inferNilZero (by rfl) + simpa [nilBodyExpr, nilFirstApp, + Expr.instantiate1_eq, Expr.instantiate1'] using h + +theorem nilOuterWithLocalDecl + {α} (k : Expr → TypeChecker.RecM α) + (methods : TypeChecker.Methods) : + (withLocalDecl (m := TypeChecker.RecM) `α .implicit + (.sort (.succ (.param `u))) k) + methods (tcContext ({} : LocalContext)) nilRootSortState = + k (.fvar nilAlphaId) methods (tcContext nilAlphaLctx) + nilBodyInitialState := by + simpa [nilAlphaId, nilAlphaLctx, nilBodyInitialState, tcContext] using + (withLocalDeclEq `α .implicit (.sort (.succ (.param `u))) k methods + (tcContext ({} : LocalContext)) nilRootSortState) + +@[simp] theorem ensureSortExact + (level : Level) (source : Expr) (fuel : Nat) + (context : TypeChecker.Context) (state : TypeChecker.State) : + TypeChecker.Inner.ensureSortCore (.sort level) source + (TypeChecker.Methods.withFuel fuel) context state = + .ok (.sort level, state) := by + rfl + +theorem nilRootSortCore : + TypeChecker.Inner.inferType' + (.sort (.succ (.param `u))) false + (TypeChecker.Methods.withFuel 9998) + (tcContext ({} : LocalContext)) ({} : TypeChecker.State) = + .ok (.sort (.succ (.succ (.param `u))), nilRootSortState) := by + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', + nilRootSortState, checkLevelSuccParam, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +def nilCtorBodyRaw : Expr := + .app (.app (.const ``IndexedVec [.param `u]) (.bvar 0)) + (.const ``Nat.zero []) + +def nilCtorTypeRaw : Expr := + .forallE `α (.sort (.succ (.param `u))) nilCtorBodyRaw .implicit + +def nilCtorInferredLevel : Level := + mkLevelIMax' (.succ (.succ (.param `u))) (.succ (.param `u)) + +theorem nilInfoTypeShape : indexedVecNilInfo.type = nilCtorTypeRaw := by + rfl + +theorem nilRootInferForallExists : ∃ finalState, + TypeChecker.Inner.inferForall nilCtorTypeRaw false + (TypeChecker.Methods.withFuel 9999) + (tcContext ({} : LocalContext)) ({} : TypeChecker.State) = + .ok (.sort nilCtorInferredLevel, finalState) := by + obtain ⟨finalState, hbody⟩ := inferNilBodyExists + refine ⟨finalState, ?_⟩ + unfold TypeChecker.Inner.inferForall + simp only [TypeChecker.Inner.inferForall.loop, nilCtorTypeRaw] + rw [show + (.sort (.succ (.param `u)) : Expr).instantiateRev #[] = + .sort (.succ (.param `u)) by + simp [Expr.instantiateRev_eq, Expr.instantiate_eq]] + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [show TypeChecker.Inner.inferType + (.sort (.succ (.param `u))) false + (TypeChecker.Methods.withFuel 9999) + (tcContext ({} : LocalContext)) ({} : TypeChecker.State) = + TypeChecker.Inner.inferType' + (.sort (.succ (.param `u))) false + (TypeChecker.Methods.withFuel 9998) + (tcContext ({} : LocalContext)) ({} : TypeChecker.State) by rfl] + rw [nilRootSortCore] + simp only + rw [ensureSortExact] + simp only + rw [nilOuterWithLocalDecl] + simp only [TypeChecker.Inner.inferForall.loop, nilCtorBodyRaw] + rw [show + (((.const ``IndexedVec [.param `u] : Expr).app (.bvar 0)).app + (.const ``Nat.zero [])).instantiateRev + (#[] |>.push (.fvar nilAlphaId)) = nilBodyExpr by + simp [nilBodyExpr, nilAlphaId, Expr.instantiateRev_eq, Expr.instantiate_eq, Expr.instantiate1', + Expr.liftLooseBVars_zero]] + simp only [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [hbody] + simp only + rw [ensureSortExact] + simp [nilCtorInferredLevel, Expr.sortLevel!, Pure.pure, ReaderT.pure, + StateT.pure, Except.pure] + +theorem inferNilRootExists : ∃ finalState, + TypeChecker.Inner.inferType indexedVecNilInfo.type false + (TypeChecker.Methods.withFuel 10000) + (tcContext ({} : LocalContext)) ({} : TypeChecker.State) = + .ok (.sort nilCtorInferredLevel, finalState) := by + obtain ⟨state, hforall⟩ := nilRootInferForallExists + refine ⟨{ state with inferTypeC := + (state.inferTypeC.insert indexedVecNilInfo.type + (.sort nilCtorInferredLevel)) }, ?_⟩ + change TypeChecker.Inner.inferType' indexedVecNilInfo.type false + (TypeChecker.Methods.withFuel 9999) + (tcContext ({} : LocalContext)) ({} : TypeChecker.State) = _ + rw [nilInfoTypeShape] + exact inferTypeForallCore 9999 (tcContext ({} : LocalContext)) + ({} : TypeChecker.State) state `α + (.sort (.succ (.param `u))) nilCtorBodyRaw + (.sort nilCtorInferredLevel) .implicit + (by simp [nilCtorBodyRaw, Expr.hasLooseBVars, + Expr.looseBVarRange']) + (by simp) hforall + +theorem nilRootCheckTypeM : + TypeChecker.M.run ctorEnv .safe {} [`u] {} + (TypeChecker.checkType indexedVecNilInfo.type) = + .ok (.sort nilCtorInferredLevel) := by + obtain ⟨finalState, hroot⟩ := inferNilRootExists + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType indexedVecNilInfo.type false + (TypeChecker.Methods.withFuel 10000) + (tcContext {}) ({} : TypeChecker.State)) = _ + rw [hroot] + rfl + +def nilCandidateContext : AddInductive.Context := ctorContext + +def nilCandidateBodyContext : AddInductive.Context := + nilCandidateContext.pushLocalDecl `α .implicit + (.sort (.succ (.param `u))) + +def nilCandidateBody : Expr := + nilCtorBodyRaw.instantiate1 nilCandidateContext.freshExpr + +theorem nilStandaloneSortCore : + TypeChecker.Inner.inferType' + (.sort (.succ (.param `u))) false + (TypeChecker.Methods.withFuel 9999) + (tcContext ({} : LocalContext)) ({} : TypeChecker.State) = + .ok (.sort (.succ (.succ (.param `u))), nilRootSortState) := by + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', + nilRootSortState, checkLevelSuccParam, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +theorem nilRootWhnfM : + TypeChecker.M.run nilCandidateContext.env nilCandidateContext.safety + nilCandidateContext.lctx nilCandidateContext.lparams + nilCandidateContext.fuel + (TypeChecker.whnf indexedVecNilInfo.type) = + .ok indexedVecNilInfo.type := by + rfl + +theorem nilDomainCheckTypeM : + TypeChecker.M.run nilCandidateContext.env nilCandidateContext.safety + nilCandidateContext.lctx nilCandidateContext.lparams + nilCandidateContext.fuel + (TypeChecker.checkType (.sort (.succ (.param `u)))) = + .ok (.sort (.succ (.succ (.param `u)))) := by + obtain ⟨finalState, hroot⟩ := + show ∃ finalState, + TypeChecker.Inner.inferType (.sort (.succ (.param `u))) false + (TypeChecker.Methods.withFuel 10000) + (tcContext ({} : LocalContext)) ({} : TypeChecker.State) = + .ok (.sort (.succ (.succ (.param `u))), finalState) by + refine ⟨nilRootSortState, ?_⟩ + change TypeChecker.Inner.inferType' + (.sort (.succ (.param `u))) false + (TypeChecker.Methods.withFuel 9999) + (tcContext ({} : LocalContext)) ({} : TypeChecker.State) = _ + exact nilStandaloneSortCore + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType (.sort (.succ (.param `u))) false + (TypeChecker.Methods.withFuel 10000) + (tcContext ({} : LocalContext)) ({} : TypeChecker.State)) = _ + rw [hroot] + rfl + +theorem nilDomainWhnfM : + TypeChecker.M.run nilCandidateContext.env nilCandidateContext.safety + nilCandidateContext.lctx nilCandidateContext.lparams + nilCandidateContext.fuel + (TypeChecker.whnf (.sort (.succ (.param `u)))) = + .ok (.sort (.succ (.param `u))) := by + rfl + +def nilCandidateAlphaId : FVarId := + nilCandidateContext.freshFVarId + +def nilCandidateAlphaLctx : LocalContext := + ({} : LocalContext).mkLocalDecl nilCandidateAlphaId `α + (.sort (.succ (.param `u))) .implicit + +theorem nilCandidateFresh : + ({} : LocalContext).find? nilCandidateAlphaId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := nilCandidateAlphaId) LocalContext.WF.nil + change + ({ fvarIdToDecl := PersistentHashMap.empty, + decls := PersistentArray.empty, + auxDeclToFullName := Std.TreeMap.empty } : LocalContext).find? + nilCandidateAlphaId = none + rw [h] + simp [LocalContext.toList] + +theorem nilCandidateAlphaFind : + nilCandidateAlphaLctx.find? nilCandidateAlphaId = + some (.cdecl 0 nilCandidateAlphaId `α + (.sort (.succ (.param `u))) .implicit .default) := by + have hwf : nilCandidateAlphaLctx.WF := by + change (({} : LocalContext).mkLocalDecl nilCandidateAlphaId `α + (.sort (.succ (.param `u))) .implicit).WF + exact LocalContext.WF.mkLocalDecl LocalContext.WF.nil + nilCandidateFresh + rw [hwf.find?_eq_find?_toList] + simp only [nilCandidateAlphaLctx] + rw [LocalContext.mkLocalDecl_toList] + rw [show ({} : LocalContext).toList = [] by rfl] + rw [show ({} : LocalContext).decls.size = 0 by rfl] + change + (if nilCandidateAlphaId == nilCandidateAlphaId then + some (LocalDecl.cdecl 0 nilCandidateAlphaId `α + (.sort (.succ (.param `u))) .implicit .default) + else none) = _ + rw [beq_self_eq_true] + rfl + +def nilCandidateFirstApp : Expr := + .app (.const ``IndexedVec [.param `u]) (.fvar nilCandidateAlphaId) + +def nilCandidateBodyExpr : Expr := + .app nilCandidateFirstApp (.const ``Nat.zero []) + +theorem nilCandidateBodyShape : + nilCandidateBody = nilCandidateBodyExpr := by + simp [nilCandidateBody, nilCtorBodyRaw, nilCandidateBodyExpr, + nilCandidateFirstApp, nilCandidateAlphaId, + AddInductive.Context.freshExpr, + Expr.instantiate1_eq, Expr.instantiate1'] + +def nilCandidateFamilyState : TypeChecker.State := + { ({} : TypeChecker.State) with inferTypeC := + (({} : TypeChecker.State).inferTypeC.insert + (.const ``IndexedVec [.param `u]) indexedVecInfo.type) } + +def nilCandidateAlphaState : TypeChecker.State := + { nilCandidateFamilyState with inferTypeC := + (nilCandidateFamilyState.inferTypeC.insert + (.fvar nilCandidateAlphaId) (.sort (.succ (.param `u)))) } + +def nilCandidateFirstAppState : TypeChecker.State := + { nilCandidateAlphaState with inferTypeC := + (nilCandidateAlphaState.inferTypeC.insert + nilCandidateFirstApp vecFamilyTail) } + +def nilCandidateZeroState : TypeChecker.State := + { nilCandidateFirstAppState with inferTypeC := + (nilCandidateFirstAppState.inferTypeC.insert + (.const ``Nat.zero []) (.const ``Nat [])) } + +theorem inferNilCandidateFamily : + TypeChecker.Inner.inferType' + (.const ``IndexedVec [.param `u]) false + (TypeChecker.Methods.withFuel 9999) + (tcContext nilCandidateAlphaLctx) ({} : TypeChecker.State) = + .ok (indexedVecInfo.type, nilCandidateFamilyState) := by + simpa [nilCandidateFamilyState] using + (inferTypeFamilyCore 9999 nilCandidateAlphaLctx + ({} : TypeChecker.State) (by simp)) + +theorem inferNilCandidateAlpha : + TypeChecker.Inner.inferType' (.fvar nilCandidateAlphaId) false + (TypeChecker.Methods.withFuel 9999) + (tcContext nilCandidateAlphaLctx) nilCandidateFamilyState = + .ok (.sort (.succ (.param `u)), nilCandidateAlphaState) := by + simpa [nilCandidateAlphaState] using + (inferTypeFVarCore 9999 nilCandidateAlphaLctx + nilCandidateFamilyState nilCandidateAlphaId + (.sort (.succ (.param `u))) + (by simp [nilCandidateFamilyState]) nilCandidateAlphaFind) + +theorem inferNilCandidateFirstApp : + TypeChecker.Inner.inferType' nilCandidateFirstApp false + (TypeChecker.Methods.withFuel 9999) + (tcContext nilCandidateAlphaLctx) ({} : TypeChecker.State) = + .ok (vecFamilyTail, nilCandidateFirstAppState) := by + have h := inferAppCoreOf 9999 (tcContext nilCandidateAlphaLctx) + ({} : TypeChecker.State) nilCandidateFamilyState + nilCandidateAlphaState (.const ``IndexedVec [.param `u]) + (.fvar nilCandidateAlphaId) (.sort (.succ (.param `u))) + vecFamilyTail `α .default + (by simp [Expr.hasLooseBVars, Expr.looseBVarRange']) + (by simp) inferNilCandidateFamily + (by simpa [indexedVecInfoTypeShape] using inferNilCandidateAlpha) + (by rfl) + simpa [nilCandidateFirstApp, nilCandidateFirstAppState, + indexedVecInfoTypeShape, vecFamilyTail, + Expr.instantiate1'] using h + +theorem inferNilCandidateZero : + TypeChecker.Inner.inferType' (.const ``Nat.zero []) false + (TypeChecker.Methods.withFuel 9999) + (tcContext nilCandidateAlphaLctx) nilCandidateFirstAppState = + .ok (.const ``Nat [], nilCandidateZeroState) := by + simpa [nilCandidateZeroState] using + (inferTypeZeroCore 9999 nilCandidateAlphaLctx + nilCandidateFirstAppState (by + simp [nilCandidateFirstAppState, nilCandidateAlphaState, nilCandidateFamilyState, + nilCandidateFirstApp])) + +theorem inferNilCandidateBodyExists : ∃ finalState, + TypeChecker.Inner.inferType nilCandidateBody false + (TypeChecker.Methods.withFuel 10000) + (tcContext nilCandidateAlphaLctx) ({} : TypeChecker.State) = + .ok (.sort (.succ (.param `u)), finalState) := by + refine ⟨{ nilCandidateZeroState with inferTypeC := + (nilCandidateZeroState.inferTypeC.insert nilCandidateBodyExpr + (.sort (.succ (.param `u)))) }, ?_⟩ + change TypeChecker.Inner.inferType' nilCandidateBody false + (TypeChecker.Methods.withFuel 9999) + (tcContext nilCandidateAlphaLctx) ({} : TypeChecker.State) = _ + rw [nilCandidateBodyShape] + have h := inferAppCoreOf 9999 (tcContext nilCandidateAlphaLctx) + ({} : TypeChecker.State) nilCandidateFirstAppState + nilCandidateZeroState nilCandidateFirstApp (.const ``Nat.zero []) + (.const ``Nat []) (.sort (.succ (.param `u))) vecIndexName .default + (by simp [nilCandidateFirstApp, Expr.hasLooseBVars, Expr.looseBVarRange']) + (by simp) inferNilCandidateFirstApp + inferNilCandidateZero (by rfl) + simpa [nilCandidateBodyExpr, nilCandidateFirstApp, + Expr.instantiate1_eq, Expr.instantiate1'] using h + +theorem nilCandidateBodyCheckTypeM : + TypeChecker.M.run nilCandidateBodyContext.env + nilCandidateBodyContext.safety nilCandidateBodyContext.lctx + nilCandidateBodyContext.lparams nilCandidateBodyContext.fuel + (TypeChecker.checkType nilCandidateBody) = + .ok (.sort (.succ (.param `u))) := by + obtain ⟨finalState, hbody⟩ := inferNilCandidateBodyExists + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType nilCandidateBody false + (TypeChecker.Methods.withFuel 10000) + (tcContext nilCandidateAlphaLctx) ({} : TypeChecker.State)) = _ + rw [hbody] + rfl + +theorem nilCandidateBodyGetAppFn : + nilCandidateBody.getAppFn = + .const ``IndexedVec [.param `u] := by + rw [nilCandidateBodyShape] + rfl + +theorem nilRecMBind + {α β} (x : TypeChecker.RecM α) + (f : α → TypeChecker.RecM β) (methods context state) : + (x >>= f) methods context state = + match x methods context state with + | .error e => .error e + | .ok (a, state') => f a methods context state' := by + simp [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + cases h : x methods context state with + | error => rfl + | ok value => cases value; rfl + +@[simp] theorem nilRecMGetEnv (methods context state) : + (liftM TypeChecker.getEnv : + TypeChecker.RecM Kernel.Environment) methods context state = + .ok (context.env, state) := rfl + +@[simp] theorem nilRecMGet (methods context state) : + (get : TypeChecker.RecM TypeChecker.State) + methods context state = .ok (state, state) := rfl + +@[simp] theorem nilRecMPure + {α} (a : α) (methods context state) : + (pure a : TypeChecker.RecM α) methods context state = + .ok (a, state) := rfl + +theorem nilCandidateInductiveReduceRec + (methods : TypeChecker.Methods) (state : TypeChecker.State) : + inductiveReduceRec ctorEnv nilCandidateBody + (fun e => TypeChecker.Inner.whnf e) + (fun e => TypeChecker.Inner.inferType e) + TypeChecker.Inner.isDefEq + TypeChecker.Inner.isNeverProp + methods (tcContext nilCandidateAlphaLctx) state = + .ok (none, state) := by + unfold inductiveReduceRec + rw [nilCandidateBodyGetAppFn] + simp only + rw [type_lookup_family] + rfl + +theorem nilCandidateReduceRecursor + (methods : TypeChecker.Methods) (state : TypeChecker.State) : + TypeChecker.Inner.reduceRecursor nilCandidateBody + methods (tcContext nilCandidateAlphaLctx) state = + .ok (none, state) := by + unfold TypeChecker.Inner.reduceRecursor + have hquot : ctorEnv.quotInit = false := by rfl + simp only [nilRecMBind, nilRecMGetEnv] + rw [show (tcContext nilCandidateAlphaLctx).env = ctorEnv by rfl] + rw [hquot] + simp only [Bool.false_eq_true, if_false, nilRecMBind] + rw [nilCandidateInductiveReduceRec methods state] + rfl + +@[simp] theorem nilCandidateWhnfCoreFamily (n state) : + TypeChecker.Inner.whnfCore + (.const ``IndexedVec [.param `u]) false + (TypeChecker.Methods.withFuel (n + 1)) + (tcContext nilCandidateAlphaLctx) state = + .ok (.const ``IndexedVec [.param `u], state) := by + rfl + +theorem nilCandidateWhnfCoreInitial (n : Nat) : + TypeChecker.Inner.whnfCore' nilCandidateBody false + (TypeChecker.Methods.withFuel (n + 1)) + (tcContext nilCandidateAlphaLctx) ({} : TypeChecker.State) = + .ok (nilCandidateBody, ({} : TypeChecker.State)) := by + rw [nilCandidateBodyShape] + change TypeChecker.Inner.whnfCore' + (.app (.app (.const ``IndexedVec [.param `u]) + (.fvar nilCandidateAlphaId)) (.const ``Nat.zero [])) + false (TypeChecker.Methods.withFuel (n + 1)) + (tcContext nilCandidateAlphaLctx) ({} : TypeChecker.State) = _ + unfold TypeChecker.Inner.whnfCore' + simp only [nilRecMBind, nilRecMGet, Std.HashMap.getElem?_empty] + rw [Expr.withRevApp_eq] + simp only [nilRecMBind] + rw [show + (Expr.app + (Expr.app (Expr.const ``IndexedVec [.param `u]) + (Expr.fvar nilCandidateAlphaId)) + (Expr.const ``Nat.zero [])).getAppFn = + (Expr.const ``IndexedVec [.param `u]) by rfl] + rw [nilCandidateWhnfCoreFamily n ({} : TypeChecker.State)] + simp [nilCandidateBodyExpr, nilCandidateFirstApp, Expr.structuralEq, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + rw [show + .app (.app (.const ``IndexedVec [.param `u]) + (.fvar nilCandidateAlphaId)) (.const ``Nat.zero []) = + nilCandidateBody by + symm; exact nilCandidateBodyShape] + rw [nilCandidateReduceRecursor] + rfl + +@[simp] theorem nilCandidateReduceNative + (env : Kernel.Environment) (methods : TypeChecker.Methods) + (state : TypeChecker.State) : + (liftM (TypeChecker.Inner.reduceNative env nilCandidateBody) : + TypeChecker.RecM (Option Expr)) + methods (tcContext nilCandidateAlphaLctx) state = + .ok (none, state) := by + rw [nilCandidateBodyShape] + simp [TypeChecker.Inner.reduceNative, nilCandidateBodyExpr, + nilCandidateFirstApp, Expr.structuralEq] + +@[simp] theorem nilCandidateReduceNat + (methods : TypeChecker.Methods) (state : TypeChecker.State) : + TypeChecker.Inner.reduceNat nilCandidateBody + methods (tcContext nilCandidateAlphaLctx) state = + .ok (none, state) := by + rw [nilCandidateBodyShape] + simp [TypeChecker.Inner.reduceNat, nilCandidateBodyExpr, nilCandidateFirstApp, + Expr.getAppNumArgs_eq, Expr.getAppArgsRevList] + +theorem nilCandidateIsDeltaFamily : + TypeChecker.Inner.isDelta ctorEnv + (.const ``IndexedVec [.param `u]) = none := by + unfold TypeChecker.Inner.isDelta + rw [show + (Expr.const ``IndexedVec [.param `u]).getAppFn = + Expr.const ``IndexedVec [.param `u] by rfl] + simp only + rw [type_lookup_family] + simp [indexedVecInfo, ConstantInfo.deltaValue?] + +theorem nilCandidateUnfoldFamily + (methods : TypeChecker.Methods) (state : TypeChecker.State) : + TypeChecker.Inner.unfoldDefinitionCore + (.const ``IndexedVec [.param `u]) + methods (tcContext nilCandidateAlphaLctx) state = + .ok (none, state) := by + unfold TypeChecker.Inner.unfoldDefinitionCore + simp only [nilRecMBind, nilRecMGetEnv] + rw [show (tcContext nilCandidateAlphaLctx).env = ctorEnv by rfl] + rw [nilCandidateIsDeltaFamily] + rfl + +theorem nilCandidateUnfoldBody + (methods : TypeChecker.Methods) (state : TypeChecker.State) : + TypeChecker.Inner.unfoldDefinition nilCandidateBody + methods (tcContext nilCandidateAlphaLctx) state = + .ok (none, state) := by + unfold TypeChecker.Inner.unfoldDefinition + rw [show nilCandidateBody.isApp = true by + rw [nilCandidateBodyShape] + rfl] + simp only [if_true] + rw [nilCandidateBodyGetAppFn] + simp only [nilRecMBind] + rw [nilCandidateUnfoldFamily] + rfl + +theorem nilCandidateWhnfLoop : + TypeChecker.Inner.whnf'.loop nilCandidateBody 100000 + (TypeChecker.Methods.withFuel 9999) + (tcContext nilCandidateAlphaLctx) ({} : TypeChecker.State) = + .ok (nilCandidateBody, ({} : TypeChecker.State)) := by + rw [show 100000 = 99999 + 1 by rfl] + unfold TypeChecker.Inner.whnf'.loop + rw [show 9999 = 9998 + 1 by rfl] + simp only [nilRecMBind, nilRecMGetEnv] + rw [nilCandidateWhnfCoreInitial 9998] + simp only [] + rw [nilCandidateReduceNative] + simp only [nilRecMBind] + rw [nilCandidateReduceNat] + simp only [nilRecMBind] + rw [nilCandidateUnfoldBody] + rfl + +theorem nilCandidateBodyWhnfM : + TypeChecker.M.run nilCandidateBodyContext.env + nilCandidateBodyContext.safety nilCandidateBodyContext.lctx + nilCandidateBodyContext.lparams nilCandidateBodyContext.fuel + (TypeChecker.whnf nilCandidateBody) = .ok nilCandidateBody := by + rw [nilCandidateBodyShape] + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.whnf' + (.app (.app (.const ``IndexedVec [.param `u]) + (.fvar nilCandidateAlphaId)) (.const ``Nat.zero [])) + (TypeChecker.Methods.withFuel 9999) + (tcContext nilCandidateAlphaLctx) ({} : TypeChecker.State)) = + .ok (.app (.app (.const ``IndexedVec [.param `u]) + (.fvar nilCandidateAlphaId)) (.const ``Nat.zero [])) + unfold TypeChecker.Inner.whnf' + simp + rw [show + (if (tcContext nilCandidateAlphaLctx).eagerReduce then + (tcContext nilCandidateAlphaLctx).fuel.whnfEager + else (tcContext nilCandidateAlphaLctx).fuel.whnf) = 100000 by rfl] + rw [show + TypeChecker.Inner.whnf'.loop + (.app (.app (.const ``IndexedVec [.param `u]) + (.fvar nilCandidateAlphaId)) (.const ``Nat.zero [])) 100000 + (TypeChecker.Methods.withFuel 9999) + (tcContext nilCandidateAlphaLctx) ({} : TypeChecker.State) = + .ok (.app (.app (.const ``IndexedVec [.param `u]) + (.fvar nilCandidateAlphaId)) (.const ``Nat.zero []), + ({} : TypeChecker.State)) by + simpa [nilCandidateBodyShape, nilCandidateBodyExpr, + nilCandidateFirstApp] using nilCandidateWhnfLoop] + simp [Functor.map, StateT.map, Except.map] + +def nilDomainAnnotations : + AddInductive.CandidateTypeAnnotations + (.sort (.succ (.param `u))) where + consumed := .sort (.succ (.param `u)) + trace := .identity _ + +theorem nilDomainAnnotationTraceBuild : + AddInductive.CandidateTypeAnnotationTrace.build + (.sort (.succ (.param `u))) = + ⟨.sort (.succ (.param `u)), .identity _⟩ := by + simp [AddInductive.CandidateTypeAnnotationTrace.build] + +theorem nilDomainAnnotationsBuild : + AddInductive.buildCandidateTypeAnnotations + (.sort (.succ (.param `u))) = .ok nilDomainAnnotations := by + unfold AddInductive.buildCandidateTypeAnnotations + rw [nilDomainAnnotationTraceBuild] + rfl + +theorem nilDomainAnnotationsEq : + AddInductive.CandidateIsDefEqStep.Valid + ⟨nilCandidateContext, (.sort (.succ (.param `u))), + nilDomainAnnotations.consumed⟩ := by + simpa [nilDomainAnnotations] using + (candidateIsDefEqSelfValid nilCandidateContext + (.sort (.succ (.param `u))) 9999 rfl) + +theorem nilCandidateContextFresh : + nilCandidateContext.lctx.find? + nilCandidateContext.freshFVarId = none := by + change ({} : LocalContext).find? nilCandidateAlphaId = none + exact nilCandidateFresh + +theorem nilRootCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨nilCandidateContext, indexedVecNilInfo.type, + .sort nilCtorInferredLevel⟩ := by + simpa [AddInductive.CandidateCheckTypeStep.Valid, + nilCandidateContext, ctorContext] using nilRootCheckTypeM + +theorem nilRootWhnfValid : + AddInductive.CandidateWhnfStep.Valid + ⟨nilCandidateContext, indexedVecNilInfo.type, + indexedVecNilInfo.type⟩ := by + simpa [AddInductive.CandidateWhnfStep.Valid, + nilCandidateContext, ctorContext] using nilRootWhnfM + +theorem nilDomainCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨nilCandidateContext, (.sort (.succ (.param `u))), + .sort (.succ (.succ (.param `u)))⟩ := by + simpa [AddInductive.CandidateCheckTypeStep.Valid] using + nilDomainCheckTypeM + +theorem nilDomainWhnfValid : + AddInductive.CandidateWhnfStep.Valid + ⟨nilCandidateContext, (.sort (.succ (.param `u))), + .sort (.succ (.param `u))⟩ := by + simpa [AddInductive.CandidateWhnfStep.Valid] using nilDomainWhnfM + +theorem nilBodyCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨nilCandidateBodyContext, nilCandidateBody, + .sort (.succ (.param `u))⟩ := by + simpa [AddInductive.CandidateCheckTypeStep.Valid] using + nilCandidateBodyCheckTypeM + +theorem nilBodyWhnfValid : + AddInductive.CandidateWhnfStep.Valid + ⟨nilCandidateBodyContext, nilCandidateBody, + nilCandidateBody⟩ := by + simpa [AddInductive.CandidateWhnfStep.Valid] using + nilCandidateBodyWhnfM + +def nilDomainCandidateTrace : + AddInductive.CandidateExprTrace nilCandidateContext + (.sort (.succ (.param `u))) := + .terminal nilCandidateContext (.sort (.succ (.param `u))) + (.sort (.succ (.succ (.param `u)))) + (.sort (.succ (.param `u))) + nilDomainCheckValid nilDomainWhnfValid + +def nilBodyCandidateTrace : + AddInductive.CandidateExprTrace nilCandidateBodyContext + (nilCtorBodyRaw.instantiate1 nilCandidateContext.freshExpr) := + .terminal nilCandidateBodyContext + (nilCtorBodyRaw.instantiate1 nilCandidateContext.freshExpr) + (.sort (.succ (.param `u))) nilCandidateBody + (by simpa [nilCandidateBody] using nilBodyCheckValid) + (by simpa [nilCandidateBody] using nilBodyWhnfValid) + +def nilCandidateTrace : + AddInductive.CandidateExprTrace nilCandidateContext + indexedVecNilInfo.type := + .forallE nilCandidateContext indexedVecNilInfo.type + (.sort nilCtorInferredLevel) `α + (.sort (.succ (.param `u))) nilCtorBodyRaw .implicit + nilCandidateContextFresh nilDomainAnnotations + nilDomainAnnotationsEq nilRootCheckValid + (by simpa [nilInfoTypeShape, nilCtorTypeRaw] using nilRootWhnfValid) + nilDomainCandidateTrace nilBodyCandidateTrace + +def nilCandidate : AddInductive.CandidateExpr indexedVecNilInfo.type := + ⟨nilCandidateContext, nilCandidateTrace⟩ + +theorem nilCandidate_view_eq : + nilCandidate.view = indexedVecNilInfo.type := by + have habstract (context : AddInductive.Context) (e : Expr) : + e.abstract #[context.freshExpr] = + Expr.abstract1 context.freshFVarId e := by + rw [show #[context.freshExpr] = + ⟨[context.freshFVarId].map Expr.fvar⟩ by rfl] + simp only [Expr.abstract_eq, Expr.abstractList] + simp only [nilCandidate, AddInductive.CandidateExpr.view, + nilCandidateTrace, nilDomainCandidateTrace, nilBodyCandidateTrace, + AddInductive.CandidateExprTrace.view] + rw [habstract] + rw [nilInfoTypeShape] + simp [nilCandidateBody, nilCtorTypeRaw, nilCtorBodyRaw, + Expr.instantiate1_eq, Expr.instantiate1', Expr.abstract1, + nilCandidateContext, ctorContext, + AddInductive.Context.freshExpr, + AddInductive.Context.freshFVarId, + NameGenerator.curr] + +/-- The retained `nil` candidate is identity-normalizing at its root, domain, +and instantiated result. -/ +theorem nilCandidate_identity : + TypeChecker.CandidateExprIdentity nilCandidate.trace := by + change TypeChecker.CandidateExprIdentity nilCandidateTrace + unfold nilCandidateTrace + refine .forallE (name := `α) (binderInfo := .implicit) + (body := nilCtorBodyRaw) (annotations := nilDomainAnnotations) + nilDomainCandidateTrace nilBodyCandidateTrace + (by simpa [nilCtorTypeRaw] using nilInfoTypeShape) + rfl (.terminal rfl) (.terminal (by rfl)) + +theorem nilDomainCandidateTraceLoop (fuel : Nat) : + AddInductive.buildCandidateExpr.loop nilCandidateContext + (.sort (.succ (.param `u))) (fuel + 1) = + .ok nilDomainCandidateTrace := by + simpa only [nilDomainCandidateTrace] using + AddInductive.buildCandidateExpr_loop_of_whnf_nonForall + nilCandidateContext (.sort (.succ (.param `u))) + (.sort (.succ (.succ (.param `u)))) + (.sort (.succ (.param `u))) fuel + nilDomainCheckValid nilDomainWhnfValid rfl + +theorem nilBodyCandidateTraceLoop (fuel : Nat) : + AddInductive.buildCandidateExpr.loop nilCandidateBodyContext + (nilCtorBodyRaw.instantiate1 nilCandidateContext.freshExpr) + (fuel + 1) = .ok nilBodyCandidateTrace := by + simpa only [nilBodyCandidateTrace] using + AddInductive.buildCandidateExpr_loop_of_whnf_nonForall + nilCandidateBodyContext + (nilCtorBodyRaw.instantiate1 nilCandidateContext.freshExpr) + (.sort (.succ (.param `u))) nilCandidateBody fuel + (by simpa [nilCandidateBody] using nilBodyCheckValid) + (by simpa [nilCandidateBody] using nilBodyWhnfValid) + (by rw [nilCandidateBodyShape]; rfl) + +theorem nilCandidateTraceLoop : + AddInductive.buildCandidateExpr.loop nilCandidateContext + indexedVecNilInfo.type nilCandidateContext.fuel.inductiveFuel = + .ok nilCandidateTrace := by + change AddInductive.buildCandidateExpr.loop nilCandidateContext + indexedVecNilInfo.type (999 + 1) = _ + simpa only [nilCandidateTrace, nilCandidateBodyContext] using + (AddInductive.buildCandidateExpr_loop_of_whnf_forall + (context := nilCandidateContext) + (e := indexedVecNilInfo.type) + (inferred := .sort nilCtorInferredLevel) + (fuel := 999) (name := `α) + (domain := .sort (.succ (.param `u))) + (body := nilCtorBodyRaw) (binderInfo := .implicit) + (hfresh := nilCandidateContextFresh) + (annotations := nilDomainAnnotations) + (hannotations := nilDomainAnnotationsBuild) + (hannotationsEq := nilDomainAnnotationsEq) + (hcheck := nilRootCheckValid) + (hrun := by + simpa [nilInfoTypeShape, nilCtorTypeRaw] using nilRootWhnfValid) + (domainCandidate := nilDomainCandidateTrace) + (bodyCandidate := nilBodyCandidateTrace) + (hdomain := by + simpa using nilDomainCandidateTraceLoop 998) + (hbody := by + simpa [nilCandidateBodyContext, nilDomainAnnotations] using + nilBodyCandidateTraceLoop 998)) + +theorem nilCandidateProduced : + AddInductive.buildCandidateExpr indexedVecNilInfo.type + nilCandidateContext = .ok nilCandidate := by + unfold AddInductive.buildCandidateExpr + simp only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] + rw [nilCandidateTraceLoop] + rfl + +/-! ## `IndexedVec.cons` source and candidate contexts -/ + +def consAlphaName : Name := + indexedVecConsInfo.type.bindingName! + +def consNName : Name := + indexedVecConsInfo.type.bindingBody!.bindingName! + +def consHeadName : Name := + indexedVecConsInfo.type.bindingBody!.bindingBody!.bindingName! + +def consTailName : Name := + indexedVecConsInfo.type.bindingBody!.bindingBody!.bindingBody!.bindingName! + +def consTerminalRaw : Expr := + .app (.app (.const ``IndexedVec [.param `u]) (.bvar 3)) + (.app (.const ``Nat.succ []) (.bvar 2)) + +def consTailTypeRaw : Expr := + .forallE consTailName + (.app (.app (.const ``IndexedVec [.param `u]) (.bvar 2)) (.bvar 1)) + consTerminalRaw .default + +def consHeadTypeRaw : Expr := + .forallE consHeadName (.bvar 1) consTailTypeRaw .default + +def consNTypeRaw : Expr := + .forallE consNName (.const ``Nat []) consHeadTypeRaw .implicit + +def consCtorTypeRaw : Expr := + .forallE consAlphaName (.sort (.succ (.param `u))) + consNTypeRaw .implicit + +theorem consInfoTypeShape : + indexedVecConsInfo.type = consCtorTypeRaw := by + rfl + +def consRootContext : AddInductive.Context := ctorContext + +def consAlphaId : FVarId := consRootContext.freshFVarId + +def consAlphaExpr : Expr := consRootContext.freshExpr + +def consAlphaContext : AddInductive.Context := + consRootContext.pushLocalDecl consAlphaName .implicit + (.sort (.succ (.param `u))) + +def consNId : FVarId := consAlphaContext.freshFVarId + +def consNExpr : Expr := consAlphaContext.freshExpr + +def consNContext : AddInductive.Context := + consAlphaContext.pushLocalDecl consNName .implicit (.const ``Nat []) + +def consHeadId : FVarId := consNContext.freshFVarId + +def consHeadExpr : Expr := consNContext.freshExpr + +def consAfterAlpha : Expr := + .forallE consNName (.const ``Nat []) + (.forallE consHeadName consAlphaExpr + (.forallE consTailName + (.app (.app (.const ``IndexedVec [.param `u]) consAlphaExpr) + (.bvar 1)) + (.app (.app (.const ``IndexedVec [.param `u]) consAlphaExpr) + (.app (.const ``Nat.succ []) (.bvar 2))) + .default) + .default) + .implicit + +def consAfterN : Expr := + .forallE consHeadName consAlphaExpr + (.forallE consTailName + (.app (.app (.const ``IndexedVec [.param `u]) consAlphaExpr) + consNExpr) + (.app (.app (.const ``IndexedVec [.param `u]) consAlphaExpr) + (.app (.const ``Nat.succ []) consNExpr)) + .default) + .default + +def consHeadContext : AddInductive.Context := + consNContext.pushLocalDecl consHeadName .default consAlphaExpr + +def consTailId : FVarId := consHeadContext.freshFVarId + +def consTailExpr : Expr := consHeadContext.freshExpr + +def consTailDomain : Expr := + .app (.app (.const ``IndexedVec [.param `u]) consAlphaExpr) consNExpr + +def consAfterHead : Expr := + .forallE consTailName consTailDomain + (.app (.app (.const ``IndexedVec [.param `u]) consAlphaExpr) + (.app (.const ``Nat.succ []) consNExpr)) + .default + +def consTailContext : AddInductive.Context := + consHeadContext.pushLocalDecl consTailName .default consTailDomain + +def consTerminal : Expr := + .app (.app (.const ``IndexedVec [.param `u]) consAlphaExpr) + (.app (.const ``Nat.succ []) consNExpr) + +@[simp] theorem consLiftLooseBVarsFVar + (id : FVarId) (s d : Nat) : + (Expr.fvar id).liftLooseBVars' s d = .fvar id := by + rfl + +@[simp] theorem consInstantiateFVar + (id : FVarId) (a : Expr) (k : Nat) : + (Expr.fvar id).instantiate1' a k = .fvar id := by + rfl + +theorem consAfterAlphaShape : + consNTypeRaw.instantiate1 consRootContext.freshExpr = + consAfterAlpha := by + simp [consNTypeRaw, consHeadTypeRaw, consTailTypeRaw, consTerminalRaw, consAfterAlpha, + consAlphaExpr, consRootContext, ctorContext, AddInductive.Context.freshExpr, + Expr.instantiate1_eq, Expr.instantiate1'] + +theorem consAfterNShape : + consAfterAlpha.bindingBody!.instantiate1 + consAlphaContext.freshExpr = consAfterN := by + simp [consAfterAlpha, consAfterN, consAlphaExpr, consNExpr, consRootContext, consAlphaContext, + ctorContext, AddInductive.Context.pushLocalDecl, AddInductive.Context.freshExpr, + Expr.bindingBody!, Expr.instantiate1_eq, Expr.instantiate1'] + +theorem consAfterHeadShape : + consAfterN.bindingBody!.instantiate1 + consNContext.freshExpr = consAfterHead := by + simp [consAfterN, consAfterHead, consTailDomain, consAlphaExpr, consNExpr, consRootContext, + consAlphaContext, consNContext, ctorContext, AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshExpr, Expr.bindingBody!, Expr.instantiate1_eq, Expr.instantiate1'] + +theorem consTerminalShape : + consAfterHead.bindingBody!.instantiate1 + consHeadContext.freshExpr = consTerminal := by + simp [consAfterHead, consTerminal, consAlphaExpr, consNExpr, consRootContext, consAlphaContext, + consNContext, consHeadContext, ctorContext, AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshExpr, Expr.bindingBody!, Expr.instantiate1_eq, Expr.instantiate1'] + +@[simp] theorem consAlphaExprShape : + consAlphaExpr = .fvar consAlphaId := by + rfl + +@[simp] theorem consNExprShape : + consNExpr = .fvar consNId := by + rfl + +@[simp] theorem consHeadExprShape : + consHeadExpr = .fvar consHeadId := by + rfl + +@[simp] theorem consTailExprShape : + consTailExpr = .fvar consTailId := by + rfl + +theorem consRootFresh : + consRootContext.lctx.find? consRootContext.freshFVarId = none := by + simpa [consRootContext, nilCandidateContext] using nilCandidateContextFresh + +theorem consAlphaContextWF : consAlphaContext.lctx.WF := by + change (({} : LocalContext).mkLocalDecl + consRootContext.freshFVarId consAlphaName + (.sort (.succ (.param `u))) .implicit).WF + exact LocalContext.WF.mkLocalDecl LocalContext.WF.nil (by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := consRootContext.freshFVarId) LocalContext.WF.nil + change + ({ fvarIdToDecl := PersistentHashMap.empty, + decls := PersistentArray.empty, + auxDeclToFullName := Std.TreeMap.empty } : LocalContext).find? + consRootContext.freshFVarId = none + rw [h] + simp [LocalContext.toList]) + +theorem consAlphaContextFresh : + consAlphaContext.lctx.find? consAlphaContext.freshFVarId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := consAlphaContext.freshFVarId) consAlphaContextWF + rw [h] + simp only [consAlphaContext, consRootContext, ctorContext, + AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshFVarId] + rw [LocalContext.mkLocalDecl_toList] + rw [show ({} : LocalContext).toList = [] by rfl] + simp [NameGenerator.next, NameGenerator.curr] + intro heq + injection heq with hname + injection hname with hidx + omega + +theorem consNContextWF : consNContext.lctx.WF := by + simpa [consNContext, AddInductive.Context.pushLocalDecl] using + (LocalContext.WF.mkLocalDecl consAlphaContextWF consAlphaContextFresh) + +theorem consNContextFresh : + consNContext.lctx.find? consNContext.freshFVarId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := consNContext.freshFVarId) consNContextWF + rw [h] + simp only [consNContext, consAlphaContext, consRootContext, ctorContext, + AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshFVarId] + rw [LocalContext.mkLocalDecl_toList, LocalContext.mkLocalDecl_toList] + rw [show ({} : LocalContext).toList = [] by rfl] + simp [NameGenerator.next, NameGenerator.curr] + constructor <;> intro heq + · injection heq with hname + injection hname with hidx + omega + · injection heq with hname + injection hname with hidx + omega + +theorem consHeadContextWF : consHeadContext.lctx.WF := by + simpa [consHeadContext, AddInductive.Context.pushLocalDecl] using + (LocalContext.WF.mkLocalDecl consNContextWF consNContextFresh) + +theorem consHeadContextFresh : + consHeadContext.lctx.find? consHeadContext.freshFVarId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := consHeadContext.freshFVarId) consHeadContextWF + rw [h] + simp only [consHeadContext, consNContext, consAlphaContext, + consRootContext, ctorContext, AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshFVarId] + rw [LocalContext.mkLocalDecl_toList, LocalContext.mkLocalDecl_toList, + LocalContext.mkLocalDecl_toList] + rw [show ({} : LocalContext).toList = [] by rfl] + simp [NameGenerator.next, NameGenerator.curr] + constructor + · intro heq + injection heq with hname + injection hname with hidx + omega + · constructor <;> intro heq + · injection heq with hname + injection hname with hidx + omega + · injection heq with hname + injection hname with hidx + omega + +theorem consTailContextWF : consTailContext.lctx.WF := by + simpa [consTailContext, AddInductive.Context.pushLocalDecl] using + (LocalContext.WF.mkLocalDecl consHeadContextWF consHeadContextFresh) + +theorem consAlphaFindInHead : + consHeadContext.lctx.find? consAlphaId = + some (.cdecl 0 consAlphaId consAlphaName + (.sort (.succ (.param `u))) .implicit .default) := by + rw [consHeadContextWF.find?_eq_find?_toList] + simp [consHeadContext, consNContext, consAlphaContext, consRootContext, ctorContext, consAlphaId, + AddInductive.Context.pushLocalDecl, AddInductive.Context.freshFVarId, LocalContext.mkLocalDecl, + LocalContext.toList, LocalDecl.fvarId, NameGenerator.next, NameGenerator.curr] + +theorem consAlphaFindInN : + consNContext.lctx.find? consAlphaId = + some (.cdecl 0 consAlphaId consAlphaName + (.sort (.succ (.param `u))) .implicit .default) := by + rw [consNContextWF.find?_eq_find?_toList] + simp [consNContext, consAlphaContext, consRootContext, ctorContext, consAlphaId, + AddInductive.Context.pushLocalDecl, AddInductive.Context.freshFVarId, LocalContext.mkLocalDecl, + LocalContext.toList, LocalDecl.fvarId, NameGenerator.next, NameGenerator.curr] + +theorem consNFindInHead : + consHeadContext.lctx.find? consNId = + some (.cdecl 1 consNId consNName (.const ``Nat []) + .implicit .default) := by + rw [consHeadContextWF.find?_eq_find?_toList] + simp [consHeadContext, consNContext, consAlphaContext, consRootContext, ctorContext, consAlphaId, + consNId, AddInductive.Context.pushLocalDecl, AddInductive.Context.freshFVarId, + LocalContext.mkLocalDecl, LocalContext.toList, LocalDecl.fvarId, NameGenerator.next, + NameGenerator.curr] + +theorem consAlphaFindInTail : + consTailContext.lctx.find? consAlphaId = + some (.cdecl 0 consAlphaId consAlphaName + (.sort (.succ (.param `u))) .implicit .default) := by + rw [consTailContextWF.find?_eq_find?_toList] + simp [consTailContext, consHeadContext, consNContext, consAlphaContext, consRootContext, + ctorContext, consAlphaId, AddInductive.Context.pushLocalDecl, AddInductive.Context.freshFVarId, + LocalContext.mkLocalDecl, LocalContext.toList, LocalDecl.fvarId, NameGenerator.next, + NameGenerator.curr] + +theorem consNFindInTail : + consTailContext.lctx.find? consNId = + some (.cdecl 1 consNId consNName (.const ``Nat []) + .implicit .default) := by + rw [consTailContextWF.find?_eq_find?_toList] + simp [consTailContext, consHeadContext, consNContext, consAlphaContext, consRootContext, + ctorContext, consAlphaId, consNId, AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshFVarId, LocalContext.mkLocalDecl, LocalContext.toList, + LocalDecl.fvarId, NameGenerator.next, NameGenerator.curr] + +/-! ## Reusable post-family atom observations -/ + +theorem ctorNatCheckTypeM (lctx : LocalContext) : + TypeChecker.M.run ctorEnv .safe lctx [`u] ({} : FuelConfig) + (TypeChecker.checkType (.const ``Nat [])) = + .ok (.sort (.succ .zero)) := by + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType (.const ``Nat []) false + (TypeChecker.Methods.withFuel 10000) (tcContext lctx) + ({} : TypeChecker.State)) = _ + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' (.const ``Nat []) false + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + ({} : TypeChecker.State)) = _ + rw [inferTypeNatCore 9999 lctx ({} : TypeChecker.State) + (by simp)] + rfl + +theorem ctorUnfoldNat (lctx methods state) : + TypeChecker.Inner.unfoldDefinition (.const ``Nat []) + methods (tcContext lctx) state = .ok (none, state) := by + change TypeChecker.Inner.unfoldDefinitionCore (.const ``Nat []) + methods (tcContext lctx) state = _ + simp [TypeChecker.Inner.unfoldDefinitionCore, + TypeChecker.Inner.isDelta, Expr.getAppFn, tcContext, + type_lookup_nat, natInfo, ConstantInfo.deltaValue?, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +theorem ctorWhnfLoopNat (lctx methods state n) : + TypeChecker.Inner.whnf'.loop (.const ``Nat []) (n + 1) + methods (tcContext lctx) state = + .ok (.const ``Nat [], state) := by + unfold TypeChecker.Inner.whnf'.loop + simp [ctorUnfoldNat] + +theorem ctorNatWhnfM (lctx : LocalContext) : + TypeChecker.M.run ctorEnv .safe lctx [`u] ({} : FuelConfig) + (TypeChecker.whnf (.const ``Nat [])) = + .ok (.const ``Nat []) := by + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.whnf' (.const ``Nat []) + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + ({} : TypeChecker.State)) = _ + unfold TypeChecker.Inner.whnf' + simp + rw [show + (if (tcContext lctx).eagerReduce then + (tcContext lctx).fuel.whnfEager + else (tcContext lctx).fuel.whnf) = 100000 by rfl] + rw [show 100000 = 99999 + 1 by rfl] + rw [ctorWhnfLoopNat] + simp [Functor.map, StateT.map, Except.map] + +theorem ctorFVarCheckTypeM + (lctx : LocalContext) (id : FVarId) (type : Expr) + (hfind : lctx.find? id = some (.cdecl index id name type bi kind)) : + TypeChecker.M.run ctorEnv .safe lctx [`u] ({} : FuelConfig) + (TypeChecker.checkType (.fvar id)) = .ok type := by + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType (.fvar id) false + (TypeChecker.Methods.withFuel 10000) (tcContext lctx) + ({} : TypeChecker.State)) = _ + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' (.fvar id) false + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + ({} : TypeChecker.State)) = _ + rw [inferTypeFVarCore 9999 lctx ({} : TypeChecker.State) + id type (by simp) hfind] + rfl + +theorem ctorFVarWhnfM + (lctx : LocalContext) (id : FVarId) + (hfind : lctx.find? id = some (.cdecl index id name type bi kind)) : + TypeChecker.M.run ctorEnv .safe lctx [`u] ({} : FuelConfig) + (TypeChecker.whnf (.fvar id)) = .ok (.fvar id) := by + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.whnf' (.fvar id) + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + ({} : TypeChecker.State)) = _ + unfold TypeChecker.Inner.whnf' + simp only [nilRecMBind] + rw [show (getLCtx : TypeChecker.RecM LocalContext) + (TypeChecker.Methods.withFuel 9999) + (tcContext lctx) ({} : TypeChecker.State) = + .ok (lctx, ({} : TypeChecker.State)) by rfl] + simp [TypeChecker.Inner.isLetFVar, hfind] + rfl + +/-! A uniform WHNF observation for opaque applications of the inserted +`IndexedVec` family. -/ + +def ctorIndexedVecApp (alpha index : Expr) : Expr := + .app (.app (.const ``IndexedVec [.param `u]) alpha) index + +@[simp] theorem ctorIndexedVecAppGetAppFn (alpha index : Expr) : + (ctorIndexedVecApp alpha index).getAppFn = + .const ``IndexedVec [.param `u] := by + rfl + +theorem ctorIndexedVecInductiveReduceRec + {m : Type → Type} [Monad m] + (alpha index : Expr) (whnf inferType : Expr → m Expr) + (isDefEq : Expr → Expr → m Bool) (isNeverProp : Expr → m Bool) : + inductiveReduceRec ctorEnv (ctorIndexedVecApp alpha index) + whnf inferType isDefEq isNeverProp = pure none := by + unfold inductiveReduceRec + rw [ctorIndexedVecAppGetAppFn] + simp only + rw [type_lookup_family] + rfl + +theorem ctorIndexedVecReduceRecursor + (lctx : LocalContext) (alpha index : Expr) + (methods : TypeChecker.Methods) (state : TypeChecker.State) : + TypeChecker.Inner.reduceRecursor (ctorIndexedVecApp alpha index) + methods (tcContext lctx) state = + .ok (none, state) := by + unfold TypeChecker.Inner.reduceRecursor + have hquot : ctorEnv.quotInit = false := by rfl + simp only [nilRecMBind, nilRecMGetEnv] + rw [show (tcContext lctx).env = ctorEnv by rfl] + rw [hquot] + simp only [Bool.false_eq_true, if_false, nilRecMBind] + rw [ctorIndexedVecInductiveReduceRec] + rfl + +@[simp] theorem ctorIndexedVecWhnfCoreFamily + (lctx : LocalContext) (n : Nat) (state : TypeChecker.State) : + TypeChecker.Inner.whnfCore + (.const ``IndexedVec [.param `u]) false + (TypeChecker.Methods.withFuel (n + 1)) (tcContext lctx) state = + .ok (.const ``IndexedVec [.param `u], state) := by + rfl + +theorem ctorIndexedVecWhnfCoreInitial + (lctx : LocalContext) (alpha index : Expr) (n : Nat) : + TypeChecker.Inner.whnfCore' (ctorIndexedVecApp alpha index) + false (TypeChecker.Methods.withFuel (n + 1)) + (tcContext lctx) ({} : TypeChecker.State) = + .ok (ctorIndexedVecApp alpha index, ({} : TypeChecker.State)) := by + unfold ctorIndexedVecApp TypeChecker.Inner.whnfCore' + simp only [nilRecMBind, nilRecMGet, Std.HashMap.getElem?_empty] + rw [Expr.withRevApp_eq] + simp only [nilRecMBind] + rw [show + (Expr.app (Expr.app (Expr.const ``IndexedVec [.param `u]) alpha) + index).getAppFn = Expr.const ``IndexedVec [.param `u] by rfl] + rw [ctorIndexedVecWhnfCoreFamily lctx n ({} : TypeChecker.State)] + simp [Expr.structuralEq, Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [show + Expr.app (Expr.app (Expr.const ``IndexedVec [.param `u]) alpha) + index = ctorIndexedVecApp alpha index by rfl] + rw [ctorIndexedVecReduceRecursor] + rfl + +@[simp] theorem ctorIndexedVecReduceNative + (lctx : LocalContext) (env : Kernel.Environment) + (alpha index : Expr) (methods : TypeChecker.Methods) + (state : TypeChecker.State) : + (liftM (TypeChecker.Inner.reduceNative env + (ctorIndexedVecApp alpha index)) : + TypeChecker.RecM (Option Expr)) + methods (tcContext lctx) state = .ok (none, state) := by + cases index <;> + simp [ctorIndexedVecApp, TypeChecker.Inner.reduceNative, Expr.structuralEq] + +@[simp] theorem ctorIndexedVecReduceNat + (lctx : LocalContext) (alpha index : Expr) + (methods : TypeChecker.Methods) (state : TypeChecker.State) : + TypeChecker.Inner.reduceNat (ctorIndexedVecApp alpha index) + methods (tcContext lctx) state = .ok (none, state) := by + simp [ctorIndexedVecApp, TypeChecker.Inner.reduceNat, Expr.getAppNumArgs_eq, + Expr.getAppArgsRevList] + +theorem ctorIndexedVecUnfoldFamily + (lctx : LocalContext) (methods : TypeChecker.Methods) + (state : TypeChecker.State) : + TypeChecker.Inner.unfoldDefinitionCore + (.const ``IndexedVec [.param `u]) methods (tcContext lctx) state = + .ok (none, state) := by + unfold TypeChecker.Inner.unfoldDefinitionCore + simp only [nilRecMBind, nilRecMGetEnv] + rw [show (tcContext lctx).env = ctorEnv by rfl] + rw [nilCandidateIsDeltaFamily] + rfl + +theorem ctorIndexedVecUnfold + (lctx : LocalContext) (alpha index : Expr) + (methods : TypeChecker.Methods) (state : TypeChecker.State) : + TypeChecker.Inner.unfoldDefinition (ctorIndexedVecApp alpha index) + methods (tcContext lctx) state = .ok (none, state) := by + unfold TypeChecker.Inner.unfoldDefinition + rw [show (ctorIndexedVecApp alpha index).isApp = true by rfl] + simp only [if_true] + rw [ctorIndexedVecAppGetAppFn] + simp only [nilRecMBind] + rw [ctorIndexedVecUnfoldFamily] + rfl + +theorem ctorIndexedVecWhnfLoop + (lctx : LocalContext) (alpha index : Expr) : + TypeChecker.Inner.whnf'.loop (ctorIndexedVecApp alpha index) 100000 + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + ({} : TypeChecker.State) = + .ok (ctorIndexedVecApp alpha index, ({} : TypeChecker.State)) := by + rw [show 100000 = 99999 + 1 by rfl] + unfold TypeChecker.Inner.whnf'.loop + rw [show 9999 = 9998 + 1 by rfl] + simp only [nilRecMBind, nilRecMGetEnv] + rw [ctorIndexedVecWhnfCoreInitial lctx alpha index 9998] + simp only [] + rw [ctorIndexedVecReduceNative] + simp only [nilRecMBind] + rw [ctorIndexedVecReduceNat] + simp only [nilRecMBind] + rw [ctorIndexedVecUnfold] + rfl + +theorem ctorIndexedVecWhnfM + (lctx : LocalContext) (alpha index : Expr) : + TypeChecker.M.run ctorEnv .safe lctx [`u] ({} : FuelConfig) + (TypeChecker.whnf (ctorIndexedVecApp alpha index)) = + .ok (ctorIndexedVecApp alpha index) := by + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.whnf' (ctorIndexedVecApp alpha index) + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + ({} : TypeChecker.State)) = _ + unfold ctorIndexedVecApp + unfold TypeChecker.Inner.whnf' + simp + rw [show + (if (tcContext lctx).eagerReduce then + (tcContext lctx).fuel.whnfEager + else (tcContext lctx).fuel.whnf) = 100000 by rfl] + have hloop := ctorIndexedVecWhnfLoop lctx alpha index + simp only [ctorIndexedVecApp] at hloop + rw [hloop] + simp [Functor.map, StateT.map, Except.map] + +end Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/IndexedVecOuterReplay.lean b/Ix/Theory/Named/Verify/Environment/IndexedVecOuterReplay.lean new file mode 100644 index 000000000..6ccc4cd64 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/IndexedVecOuterReplay.lean @@ -0,0 +1,1757 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Verify.Environment.IndexedVecConsReplay +import Ix.Theory.Named.Verify.Name + +/-! +# IndexedVec outer normalization-candidate replay + +Exact post-family declaration and constructor-validation executions for the +real one-parameter, one-index `IndexedVec` metadata. The final theorem closes +the complete `buildNormalizationCandidate` call and retains the ordered +`nil`/`cons` candidate package produced in the staged kernel environments. +-/ + +namespace Ix.Theory.Named.InductiveReplayFixtures +open Lean Meta +open Ix.Theory.Named.InductiveFixtures +open IndexedVecConsReplay + +def indexedVecCtorValidationContext : AddInductive.Context := + { indexedVecFamilyCandidate.trace.terminalContext with env := ctorEnv } + +def indexedVecValidationAlpha : Expr := + indexedVecFamilyCandidateContext.freshExpr + +def indexedVecValidationAlphaId : FVarId := + indexedVecFamilyCandidateContext.freshFVarId + +def indexedVecValidationIndexId : FVarId := + ⟨indexedVecFamilyCandidateContext.ngen.next.curr⟩ + +theorem validationTerminalEnv : + indexedVecFamilyCandidate.trace.terminalContext.env = + indexedVecKernelEnv := by + rfl + +theorem validationTerminalLparams : + indexedVecFamilyCandidate.trace.terminalContext.lparams = [`u] := by + rfl + +theorem validationTerminalAllowPrimitive : + indexedVecFamilyCandidate.trace.terminalContext.allowPrimitive = false := by + rfl + +theorem validationFamilyEnvNotContains : + indexedVecKernelEnv.contains ``IndexedVec = false := by + unfold Kernel.Environment.contains + change natMap.contains ``IndexedVec = false + rw [SMap.find?_isSome, indexedVecType_fresh] + rfl + +theorem validationFamilyEnvCheckName : + indexedVecKernelEnv.checkName ``IndexedVec false = .ok () := by + simp [Kernel.Environment.checkName, validationFamilyEnvNotContains, Kernel.Environment.primitives, + NameSet.ofList, NameSet.contains, Pure.pure, Except.pure] + +theorem indexedVecDeclareRoot : + AddInductive.declareInductiveTypes indexedVecCandidateInductiveStats 1 + #[indexedVecKernelType] 0 false + indexedVecFamilyCandidateContext = + .ok ctorEnv := by + simp [AddInductive.declareInductiveTypes, + indexedVecCandidateInductiveStats_nindices, + indexedVecCandidateInductiveStats_indConsts, + indexedVecKernelType, indexedVecKernelNil, indexedVecKernelCons, + indexedVecInfo, indexedVecNilInfo, indexedVecConsInfo, + ConstantInfo.name, ConstantInfo.type, ConstantInfo.toConstantVal, + ctorEnv, indexedVecKernelEnv, indexedVecFamilyCandidateContext, + indexedVecTypeMap, + AddInductive.isRec, AddInductive.isRec.loop, + AddInductive.isReflexive, AddInductive.isReflexive.loop, + AddInductive.hasIndOcc, Expr.constName!, + Bind.bind, Pure.pure, Except.bind, Except.pure] + have hcheck : + (Kernel.Environment.ofConstants `_indexedVecCandidate natMap).checkName + ``IndexedVec false = .ok () := by + simpa [indexedVecKernelEnv] using validationFamilyEnvCheckName + rw [hcheck] + rfl + +theorem indexedVecDeclareFromTerminal : + AddInductive.declareInductiveTypes indexedVecCandidateInductiveStats 1 + #[indexedVecKernelType] 0 false + indexedVecFamilyCandidate.trace.terminalContext = + .ok ctorEnv := by + calc + _ = AddInductive.declareInductiveTypes + indexedVecCandidateInductiveStats 1 #[indexedVecKernelType] + 0 false indexedVecFamilyCandidateContext := + AddInductive.declareInductiveTypes_context_eq _ _ _ _ _ _ _ + validationTerminalEnv validationTerminalLparams + validationTerminalAllowPrimitive + _ = .ok ctorEnv := indexedVecDeclareRoot + +example : indexedVecCtorValidationContext.env = ctorEnv := by rfl +example : indexedVecCtorValidationContext.lparams = [`u] := by rfl +example : indexedVecCtorValidationContext.safety = .safe := by rfl +example : indexedVecCtorValidationContext.allowPrimitive = false := by rfl +example : indexedVecCtorValidationContext.fuel = ({} : FuelConfig) := by rfl +example : indexedVecCtorValidationContext.ngen = + ({ namePrefix := `_ind_fresh } : NameGenerator).next.next := by rfl + +def indexedVecValidationParamName : Name := + indexedVecInfo.type.bindingName! + +def indexedVecValidationIndexName : Name := + indexedVecInfo.type.bindingBody!.bindingName! + +def indexedVecValidationParamContext : AddInductive.Context := + indexedVecFamilyCandidateContext.pushLocalDecl + indexedVecValidationParamName .default + (.sort (.succ (.param `u))) + +def indexedVecValidationFamilyContext : AddInductive.Context := + indexedVecValidationParamContext.pushLocalDecl + indexedVecValidationIndexName .default (.const ``Nat []) + +theorem indexedVecValidationTerminalContextShape : + indexedVecFamilyCandidate.trace.terminalContext = + indexedVecValidationFamilyContext := by + rfl + +theorem indexedVecCtorValidationContextShape : + indexedVecCtorValidationContext = + { indexedVecValidationFamilyContext with env := ctorEnv } := by + rfl + +theorem indexedVecValidationParamContextWF : + indexedVecValidationParamContext.lctx.WF := by + have hfresh : indexedVecFamilyCandidateContext.lctx.find? + indexedVecFamilyCandidateContext.freshFVarId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := indexedVecFamilyCandidateContext.freshFVarId) + LocalContext.WF.nil + change + ({ fvarIdToDecl := PersistentHashMap.empty, + decls := PersistentArray.empty, + auxDeclToFullName := Std.TreeMap.empty } : LocalContext).find? + indexedVecFamilyCandidateContext.freshFVarId = none + rw [h] + simp [LocalContext.toList] + change (({} : LocalContext).mkLocalDecl + indexedVecFamilyCandidateContext.freshFVarId + indexedVecValidationParamName + (.sort (.succ (.param `u))) .default).WF + exact LocalContext.WF.mkLocalDecl LocalContext.WF.nil hfresh + +theorem indexedVecValidationFamilyContextFresh : + indexedVecValidationParamContext.lctx.find? + indexedVecValidationParamContext.freshFVarId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := indexedVecValidationParamContext.freshFVarId) + indexedVecValidationParamContextWF + rw [h] + simp only [indexedVecValidationParamContext, + indexedVecFamilyCandidateContext, + AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshFVarId] + rw [LocalContext.mkLocalDecl_toList] + rw [show ({} : LocalContext).toList = [] by rfl] + simp [NameGenerator.next, NameGenerator.curr] + intro heq + injection heq with hname + injection hname with hidx + omega + +theorem indexedVecValidationFamilyContextWF : + indexedVecValidationFamilyContext.lctx.WF := by + simpa [indexedVecValidationFamilyContext, + AddInductive.Context.pushLocalDecl] using + (LocalContext.WF.mkLocalDecl indexedVecValidationParamContextWF + indexedVecValidationFamilyContextFresh) + +theorem indexedVecValidationAlphaFind : + indexedVecCtorValidationContext.lctx.find? + indexedVecValidationAlphaId = + some (.cdecl 0 indexedVecValidationAlphaId + indexedVecValidationParamName + (.sort (.succ (.param `u))) .default .default) := by + change indexedVecValidationFamilyContext.lctx.find? + indexedVecValidationAlphaId = _ + rw [indexedVecValidationFamilyContextWF.find?_eq_find?_toList] + simp [indexedVecValidationFamilyContext, + indexedVecValidationParamContext, + indexedVecValidationAlphaId, + indexedVecFamilyCandidateContext, + AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshFVarId, + LocalContext.mkLocalDecl, + LocalContext.toList, LocalDecl.fvarId, + NameGenerator.next, NameGenerator.curr] + +@[simp] theorem indexedVecValidationAlphaShape : indexedVecValidationAlpha = + .fvar indexedVecValidationAlphaId := by rfl + +theorem localContextFindNew + (lctx : LocalContext) (id : FVarId) (name : Name) + (type : Expr) (bi : BinderInfo) (kind : LocalDeclKind) + (hwf : lctx.WF) (hfresh : lctx.find? id = none) : + (lctx.mkLocalDecl id name type bi kind).find? id = + some (.cdecl lctx.decls.size id name type bi kind) := by + have hwf' := LocalContext.WF.mkLocalDecl + (name := name) (ty := type) (bi := bi) (kind := kind) hwf hfresh + rw [hwf'.find?_eq_find?_toList] + rw [LocalContext.mkLocalDecl_toList] + simp [LocalDecl.fvarId] + +theorem localContextFindOld + (lctx : LocalContext) (oldId newId : FVarId) + (newName : Name) (newType : Expr) (newBi : BinderInfo) + (newKind : LocalDeclKind) (oldDecl : LocalDecl) + (hwf : lctx.WF) (hfresh : lctx.find? newId = none) + (hne : oldId ≠ newId) (hold : lctx.find? oldId = some oldDecl) : + (lctx.mkLocalDecl newId newName newType newBi newKind).find? oldId = + some oldDecl := by + have hwf' := LocalContext.WF.mkLocalDecl + (name := newName) (ty := newType) (bi := newBi) + (kind := newKind) hwf hfresh + rw [hwf'.find?_eq_find?_toList] + rw [LocalContext.mkLocalDecl_toList] + simp only [List.find?_cons, LocalDecl.fvarId] + rw [show (oldId == newId) = false by + exact beq_eq_false_iff_ne.mpr hne] + rw [hwf.find?_eq_find?_toList] at hold + simpa only [LocalDecl.fvarId] using hold + +def validationFirstAppState (alphaId : FVarId) : TypeChecker.State := + replayInsert + (replayInsert + (replayInsert ({} : TypeChecker.State) + (.const ``IndexedVec [.param `u]) indexedVecInfo.type) + (.fvar alphaId) (.sort (.succ (.param `u)))) + (replayFirstApp (.fvar alphaId)) vecFamilyTail + +def validationIndexState (alphaId nId : FVarId) : TypeChecker.State := + replayInsert (validationFirstAppState alphaId) + (.fvar nId) (.const ``Nat []) + +def validationIndexedVecState + (alphaId nId : FVarId) : TypeChecker.State := + replayInsert (validationIndexState alphaId nId) + (ctorIndexedVecApp (.fvar alphaId) (.fvar nId)) + (.sort (.succ (.param `u))) + +theorem ctorIndexedVecFVarCheckTypeM + (lctx : LocalContext) (alphaId nId : FVarId) + (hne : alphaId ≠ nId) + (halpha : lctx.find? alphaId = some (.cdecl alphaIndex alphaId + alphaName (.sort (.succ (.param `u))) alphaBi alphaKind)) + (hn : lctx.find? nId = some (.cdecl nIndex nId nName + (.const ``Nat []) nBi nKind)) : + TypeChecker.M.run ctorEnv .safe lctx [`u] ({} : FuelConfig) + (TypeChecker.checkType + (ctorIndexedVecApp (.fvar alphaId) (.fvar nId))) = + .ok (.sort (.succ (.param `u))) := by + have hfirst : + TypeChecker.Inner.inferType' + (replayFirstApp (.fvar alphaId)) false + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + ({} : TypeChecker.State) = + .ok (vecFamilyTail, validationFirstAppState alphaId) := by + simpa [validationFirstAppState] using + (replayInferFirstAppFVarCore 9999 lctx + ({} : TypeChecker.State) alphaId + (by simp) + (by simp [replayInsert]) + (by simp [replayFirstApp]) + halpha) + have hnmiss : + (validationFirstAppState alphaId).inferTypeC[ + (.fvar nId : Expr)]? = none := by + have halphaN : + ((.fvar alphaId : Expr) == .fvar nId) = false := by + change Expr.eqv (.fvar alphaId) (.fvar nId) = false + rw [Expr.eqv_eq] + simp [Expr.eqv', hne] + simp [validationFirstAppState, replayInsert, replayFirstApp, + halphaN] + have hnrun : + TypeChecker.Inner.inferType' (.fvar nId) false + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + (validationFirstAppState alphaId) = + .ok (.const ``Nat [], validationIndexState alphaId nId) := by + simpa [validationIndexState, replayInsert] using + (inferTypeFVarCore 9999 lctx + (validationFirstAppState alphaId) nId (.const ``Nat []) + hnmiss hn) + have htail : + (({} : TypeChecker.State).inferTypeC[ + ctorIndexedVecApp (.fvar alphaId) (.fvar nId)]?) = none := by + simp + have hrun : + TypeChecker.Inner.inferType' + (ctorIndexedVecApp (.fvar alphaId) (.fvar nId)) false + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + ({} : TypeChecker.State) = + .ok (.sort (.succ (.param `u)), + validationIndexedVecState alphaId nId) := by + simpa [validationIndexedVecState] using + (replayInferIndexedVecAppCore 9999 lctx + ({} : TypeChecker.State) (validationFirstAppState alphaId) + (validationIndexState alphaId nId) + (.fvar alphaId) (.fvar nId) + (by simp [ctorIndexedVecApp, Expr.hasLooseBVars, + Expr.looseBVarRange']) + htail hfirst hnrun (by rfl)) + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' + (ctorIndexedVecApp (.fvar alphaId) (.fvar nId)) false + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + ({} : TypeChecker.State)) = _ + rw [hrun] + rfl + +def indexedVecValidationNilResult : Expr := + ctorIndexedVecApp indexedVecValidationAlpha (.const ``Nat.zero []) + +def indexedVecValidationConsAfterParam : Expr := + consNTypeRaw.instantiate1 indexedVecValidationAlpha + +def indexedVecValidationNId : FVarId := + indexedVecCtorValidationContext.freshFVarId + +def indexedVecValidationNExpr : Expr := + indexedVecCtorValidationContext.freshExpr + +@[simp] theorem indexedVecValidationNExprShape : + indexedVecValidationNExpr = .fvar indexedVecValidationNId := by + rfl + +def indexedVecValidationNContext : AddInductive.Context := + indexedVecCtorValidationContext.pushLocalDecl + consNName .implicit (.const ``Nat []) + +def indexedVecValidationConsAfterN : Expr := + .forallE consHeadName indexedVecValidationAlpha + (.forallE consTailName + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) + (ctorIndexedVecApp indexedVecValidationAlpha + (replaySuccApp indexedVecValidationNExpr)) + .default) + .default + +def indexedVecValidationHeadId : FVarId := + indexedVecValidationNContext.freshFVarId + +def indexedVecValidationHeadExpr : Expr := + indexedVecValidationNContext.freshExpr + +@[simp] theorem indexedVecValidationHeadExprShape : + indexedVecValidationHeadExpr = + .fvar indexedVecValidationHeadId := by + rfl + +def indexedVecValidationHeadContext : AddInductive.Context := + indexedVecValidationNContext.pushLocalDecl + consHeadName .default indexedVecValidationAlpha + +def indexedVecValidationConsAfterHead : Expr := + .forallE consTailName + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) + (ctorIndexedVecApp indexedVecValidationAlpha + (replaySuccApp indexedVecValidationNExpr)) + .default + +def indexedVecValidationTailId : FVarId := + indexedVecValidationHeadContext.freshFVarId + +def indexedVecValidationTailContext : AddInductive.Context := + indexedVecValidationHeadContext.pushLocalDecl consTailName .default + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) + +def indexedVecValidationConsResult : Expr := + ctorIndexedVecApp indexedVecValidationAlpha + (replaySuccApp indexedVecValidationNExpr) + +theorem indexedVecValidationNilResultShape : + indexedVecNilInfo.type.bindingBody!.instantiate1 + indexedVecValidationAlpha = + indexedVecValidationNilResult := by + simp [indexedVecNilInfo, ConstantInfo.type, + ConstantInfo.toConstantVal, indexedVecValidationNilResult, + ctorIndexedVecApp, indexedVecValidationAlpha, + indexedVecFamilyCandidateContext, + Expr.bindingBody!, Expr.instantiate1_eq, Expr.instantiate1', + Expr.liftLooseBVars_zero] + +theorem indexedVecValidationConsAfterParamShape : + indexedVecConsInfo.type.bindingBody!.instantiate1 + indexedVecValidationAlpha = + indexedVecValidationConsAfterParam := by + rw [consInfoTypeShape] + rfl + +theorem indexedVecValidationConsAfterParamExplicitShape : + indexedVecValidationConsAfterParam = + .forallE consNName (.const ``Nat []) + (.forallE consHeadName indexedVecValidationAlpha + (.forallE consTailName + (ctorIndexedVecApp indexedVecValidationAlpha (.bvar 1)) + (ctorIndexedVecApp indexedVecValidationAlpha + (replaySuccApp (.bvar 2))) + .default) + .default) + .implicit := by + simp [indexedVecValidationConsAfterParam, consNTypeRaw, + consHeadTypeRaw, consTailTypeRaw, consTerminalRaw, + ctorIndexedVecApp, replaySuccApp, + Expr.instantiate1_eq, Expr.instantiate1'] + +theorem indexedVecValidationConsAfterNShape : + indexedVecValidationConsAfterParam.bindingBody!.instantiate1 + indexedVecValidationNExpr = + indexedVecValidationConsAfterN := by + simp [indexedVecValidationConsAfterParam, consNTypeRaw, + consHeadTypeRaw, consTailTypeRaw, consTerminalRaw, + indexedVecValidationConsAfterN, + indexedVecValidationNExpr, + indexedVecValidationAlpha, + indexedVecCtorValidationContext, + indexedVecValidationTerminalContextShape, + indexedVecValidationFamilyContext, + indexedVecValidationParamContext, + indexedVecFamilyCandidateContext, + ctorIndexedVecApp, replaySuccApp, + AddInductive.Context.freshExpr, + AddInductive.Context.freshFVarId, + Expr.bindingBody!, Expr.instantiate1_eq, Expr.instantiate1', + NameGenerator.curr] + +theorem indexedVecValidationConsAfterHeadShape : + indexedVecValidationConsAfterN.bindingBody!.instantiate1 + indexedVecValidationHeadExpr = + indexedVecValidationConsAfterHead := by + simp [indexedVecValidationConsAfterN, + indexedVecValidationConsAfterHead, + ctorIndexedVecApp, replaySuccApp, + Expr.bindingBody!, Expr.instantiate1_eq, Expr.instantiate1'] + +theorem indexedVecValidationConsResultShape : + indexedVecValidationConsAfterHead.bindingBody!.instantiate1 + indexedVecValidationHeadContext.freshExpr = + indexedVecValidationConsResult := by + simp [indexedVecValidationConsAfterHead, + indexedVecValidationConsResult, + ctorIndexedVecApp, replaySuccApp, + Expr.bindingBody!, Expr.instantiate1_eq, Expr.instantiate1'] + +theorem indexedVecCtorValidationContextWF : + indexedVecCtorValidationContext.lctx.WF := by + simpa [indexedVecCtorValidationContextShape] using + indexedVecValidationFamilyContextWF + +theorem indexedVecCtorValidationContextLctxSize : + indexedVecCtorValidationContext.lctx.decls.size = 2 := by + change indexedVecFamilyCandidate.trace.terminalContext.lctx.decls.size = 2 + rw [indexedVecValidationTerminalContextShape] + simp [indexedVecValidationFamilyContext, + indexedVecValidationParamContext, + indexedVecFamilyCandidateContext, + AddInductive.Context.pushLocalDecl, + LocalContext.mkLocalDecl] + +theorem indexedVecCtorValidationContextFresh : + indexedVecCtorValidationContext.lctx.find? + indexedVecCtorValidationContext.freshFVarId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := indexedVecCtorValidationContext.freshFVarId) + indexedVecCtorValidationContextWF + rw [h] + simp only [indexedVecCtorValidationContext, + indexedVecValidationTerminalContextShape, + indexedVecValidationFamilyContext, + indexedVecValidationParamContext, + indexedVecFamilyCandidateContext, + AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshFVarId] + rw [LocalContext.mkLocalDecl_toList, + LocalContext.mkLocalDecl_toList] + rw [show ({} : LocalContext).toList = [] by rfl] + simp [NameGenerator.next, NameGenerator.curr] + constructor <;> intro heq + · injection heq with hname + injection hname with hidx + omega + · injection heq with hname + injection hname with hidx + omega + +theorem indexedVecValidationNContextWF : + indexedVecValidationNContext.lctx.WF := by + simpa [indexedVecValidationNContext, + AddInductive.Context.pushLocalDecl] using + (LocalContext.WF.mkLocalDecl indexedVecCtorValidationContextWF + indexedVecCtorValidationContextFresh) + +theorem indexedVecValidationNContextFresh : + indexedVecValidationNContext.lctx.find? + indexedVecValidationNContext.freshFVarId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := indexedVecValidationNContext.freshFVarId) + indexedVecValidationNContextWF + rw [h] + simp only [indexedVecValidationNContext, + indexedVecCtorValidationContext, + indexedVecValidationTerminalContextShape, + indexedVecValidationFamilyContext, + indexedVecValidationParamContext, + indexedVecFamilyCandidateContext, + AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshFVarId] + rw [LocalContext.mkLocalDecl_toList, + LocalContext.mkLocalDecl_toList, + LocalContext.mkLocalDecl_toList] + rw [show ({} : LocalContext).toList = [] by rfl] + simp [NameGenerator.next, NameGenerator.curr] + constructor + · intro heq + injection heq with hname + injection hname with hidx + omega + · constructor <;> intro heq + · injection heq with hname + injection hname with hidx + omega + · injection heq with hname + injection hname with hidx + omega + +theorem indexedVecValidationHeadContextWF : + indexedVecValidationHeadContext.lctx.WF := by + simpa [indexedVecValidationHeadContext, + AddInductive.Context.pushLocalDecl] using + (LocalContext.WF.mkLocalDecl indexedVecValidationNContextWF + indexedVecValidationNContextFresh) + +theorem indexedVecValidationAlphaNeN : + indexedVecValidationAlphaId ≠ indexedVecValidationNId := by + change indexedVecValidationAlphaId ≠ + indexedVecCtorValidationContext.freshFVarId + intro heq + have hfresh := indexedVecCtorValidationContextFresh + rw [← heq] at hfresh + rw [indexedVecValidationAlphaFind] at hfresh + contradiction + +theorem indexedVecValidationNNeHead : + indexedVecValidationNId ≠ indexedVecValidationHeadId := by + change indexedVecValidationNId ≠ + indexedVecValidationNContext.freshFVarId + intro heq + have hfresh := indexedVecValidationNContextFresh + rw [← heq] at hfresh + have hnew := localContextFindNew + indexedVecCtorValidationContext.lctx indexedVecValidationNId + consNName (.const ``Nat []) .implicit .default + indexedVecCtorValidationContextWF + indexedVecCtorValidationContextFresh + have hfind : indexedVecValidationNContext.lctx.find? + indexedVecValidationNId = some (.cdecl 2 + indexedVecValidationNId consNName (.const ``Nat []) + .implicit .default) := by + rw [indexedVecCtorValidationContextLctxSize] at hnew + simpa [indexedVecValidationNContext, + indexedVecValidationNId, + AddInductive.Context.pushLocalDecl] using hnew + rw [hfind] at hfresh + contradiction + +theorem indexedVecValidationAlphaFindInN : + indexedVecValidationNContext.lctx.find? + indexedVecValidationAlphaId = + some (.cdecl 0 indexedVecValidationAlphaId + indexedVecValidationParamName + (.sort (.succ (.param `u))) .default .default) := by + have h := localContextFindOld + (lctx := indexedVecCtorValidationContext.lctx) + (oldId := indexedVecValidationAlphaId) + (newId := indexedVecValidationNId) + (newName := consNName) (newType := .const ``Nat []) + (newBi := .implicit) (newKind := .default) + (oldDecl := .cdecl 0 indexedVecValidationAlphaId + indexedVecValidationParamName (.sort (.succ (.param `u))) + .default .default) + indexedVecCtorValidationContextWF + indexedVecCtorValidationContextFresh + indexedVecValidationAlphaNeN indexedVecValidationAlphaFind + simpa [indexedVecValidationNContext, + indexedVecValidationNId, + AddInductive.Context.pushLocalDecl] using h + +theorem indexedVecValidationNFindInHead : + indexedVecValidationHeadContext.lctx.find? + indexedVecValidationNId = + some (.cdecl 2 indexedVecValidationNId consNName + (.const ``Nat []) .implicit .default) := by + have hnew := localContextFindNew + indexedVecCtorValidationContext.lctx indexedVecValidationNId + consNName (.const ``Nat []) .implicit .default + indexedVecCtorValidationContextWF + indexedVecCtorValidationContextFresh + have hold : indexedVecValidationNContext.lctx.find? + indexedVecValidationNId = + some (.cdecl 2 indexedVecValidationNId consNName + (.const ``Nat []) .implicit .default) := by + rw [indexedVecCtorValidationContextLctxSize] at hnew + simpa [indexedVecValidationNContext, + indexedVecValidationNId, + AddInductive.Context.pushLocalDecl] using hnew + have h := localContextFindOld + (lctx := indexedVecValidationNContext.lctx) + (oldId := indexedVecValidationNId) + (newId := indexedVecValidationHeadId) + (newName := consHeadName) + (newType := indexedVecValidationAlpha) + (newBi := .default) (newKind := .default) + (oldDecl := .cdecl 2 indexedVecValidationNId consNName + (.const ``Nat []) .implicit .default) + indexedVecValidationNContextWF indexedVecValidationNContextFresh + indexedVecValidationNNeHead hold + simpa [indexedVecValidationHeadContext, + indexedVecValidationHeadId, + AddInductive.Context.pushLocalDecl] using h + +theorem indexedVecValidationAlphaFindInHead : + indexedVecValidationHeadContext.lctx.find? + indexedVecValidationAlphaId = + some (.cdecl 0 indexedVecValidationAlphaId + indexedVecValidationParamName + (.sort (.succ (.param `u))) .default .default) := by + have hne : indexedVecValidationAlphaId ≠ + indexedVecValidationHeadId := by + change indexedVecValidationAlphaId ≠ + indexedVecValidationNContext.freshFVarId + intro heq + have hfresh := indexedVecValidationNContextFresh + rw [← heq] at hfresh + rw [indexedVecValidationAlphaFindInN] at hfresh + contradiction + have h := localContextFindOld + (lctx := indexedVecValidationNContext.lctx) + (oldId := indexedVecValidationAlphaId) + (newId := indexedVecValidationHeadId) + (newName := consHeadName) + (newType := indexedVecValidationAlpha) + (newBi := .default) (newKind := .default) + (oldDecl := .cdecl 0 indexedVecValidationAlphaId + indexedVecValidationParamName (.sort (.succ (.param `u))) + .default .default) + indexedVecValidationNContextWF indexedVecValidationNContextFresh + hne indexedVecValidationAlphaFindInN + simpa [indexedVecValidationHeadContext, + indexedVecValidationHeadId, + AddInductive.Context.pushLocalDecl] using h + +theorem indexedVecValidationGetTypeAlpha : + AddInductive.getType indexedVecValidationAlpha + indexedVecCtorValidationContext = + .ok (.sort (.succ (.param `u))) := by + unfold AddInductive.getType + simp only [getLCtx, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] + change Except.ok ((indexedVecCtorValidationContext.lctx.get! + indexedVecValidationAlpha.fvarId!).type) = _ + rw [show indexedVecValidationAlpha.fvarId! = + indexedVecValidationAlphaId by + rw [indexedVecValidationAlphaShape] + rfl] + simp [LocalContext.get!, indexedVecValidationAlphaFind, + LocalDecl.type] + +theorem indexedVecValidationParamIsDefEq : + TypeChecker.M.run indexedVecCtorValidationContext.env + indexedVecCtorValidationContext.safety + indexedVecCtorValidationContext.lctx + indexedVecCtorValidationContext.lparams + indexedVecCtorValidationContext.fuel + (TypeChecker.isDefEq (.sort (.succ (.param `u))) + (.sort (.succ (.param `u)))) = .ok true := by + exact candidateIsDefEqSelfValid indexedVecCtorValidationContext + (.sort (.succ (.param `u))) 9999 rfl + +theorem indexedVecNilNoMVarNoFVar : + ctorEnv.checkNoMVarNoFVar indexedVecKernelNil.name + indexedVecKernelNil.type = .ok () := by + have hexpr : indexedVecKernelNil.type.data.hasExprMVar = false := by + change indexedVecNilInfo.type.hasExprMVar = false + rw [Expr.hasExprMVar_eq] + rfl + have hlevel : indexedVecKernelNil.type.data.hasLevelMVar = false := by + change indexedVecNilInfo.type.hasLevelMVar = false + rw [Expr.hasLevelMVar_eq] + simp [indexedVecNilInfo, ConstantInfo.type, + ConstantInfo.toConstantVal, Expr.hasLevelMVar', + Level.hasMVar_eq, Level.hasMVar'] + have hfvar : indexedVecKernelNil.type.data.hasFVar = false := by + change indexedVecNilInfo.type.hasFVar = false + rw [Expr.hasFVar_eq] + rfl + unfold Kernel.Environment.checkNoMVarNoFVar + Kernel.Environment.checkNoMVar Kernel.Environment.checkNoFVar + rw [show indexedVecKernelNil.type.hasMVar = false by + change (indexedVecKernelNil.type.data.hasExprMVar || + indexedVecKernelNil.type.data.hasLevelMVar) = false + rw [hexpr, hlevel] + rfl] + rw [show indexedVecKernelNil.type.hasFVar = false by + exact hfvar] + rfl + +theorem indexedVecConsNoMVarNoFVar : + ctorEnv.checkNoMVarNoFVar indexedVecKernelCons.name + indexedVecKernelCons.type = .ok () := by + have hexpr : indexedVecKernelCons.type.data.hasExprMVar = false := by + change indexedVecConsInfo.type.hasExprMVar = false + rw [Expr.hasExprMVar_eq] + rfl + have hlevel : indexedVecKernelCons.type.data.hasLevelMVar = false := by + change indexedVecConsInfo.type.hasLevelMVar = false + rw [Expr.hasLevelMVar_eq] + simp [indexedVecConsInfo, ConstantInfo.type, + ConstantInfo.toConstantVal, Expr.hasLevelMVar', + Level.hasMVar_eq, Level.hasMVar'] + have hfvar : indexedVecKernelCons.type.data.hasFVar = false := by + change indexedVecConsInfo.type.hasFVar = false + rw [Expr.hasFVar_eq] + rfl + unfold Kernel.Environment.checkNoMVarNoFVar + Kernel.Environment.checkNoMVar Kernel.Environment.checkNoFVar + rw [show indexedVecKernelCons.type.hasMVar = false by + change (indexedVecKernelCons.type.data.hasExprMVar || + indexedVecKernelCons.type.data.hasLevelMVar) = false + rw [hexpr, hlevel] + rfl] + rw [show indexedVecKernelCons.type.hasFVar = false by + exact hfvar] + rfl + +theorem indexedVecValidationNilRootCheckTypeM : + TypeChecker.M.run indexedVecCtorValidationContext.env + indexedVecCtorValidationContext.safety {} + indexedVecCtorValidationContext.lparams + indexedVecCtorValidationContext.fuel + (TypeChecker.checkType indexedVecKernelNil.type) = + .ok (.sort nilCtorInferredLevel) := by + change TypeChecker.M.run ctorEnv .safe {} [`u] ({} : FuelConfig) + (TypeChecker.checkType indexedVecKernelNil.type) = + .ok (.sort nilCtorInferredLevel) + simpa [indexedVecKernelNil] using nilRootCheckTypeM + +theorem indexedVecValidationConsRootCheckTypeM : + TypeChecker.M.run indexedVecCtorValidationContext.env + indexedVecCtorValidationContext.safety {} + indexedVecCtorValidationContext.lparams + indexedVecCtorValidationContext.fuel + (TypeChecker.checkType indexedVecKernelCons.type) = + .ok (.sort (.succ (.succ (.param `u)))) := by + change TypeChecker.M.run ctorEnv .safe {} [`u] ({} : FuelConfig) + (TypeChecker.checkType indexedVecKernelCons.type) = + .ok (.sort (.succ (.succ (.param `u)))) + simpa [indexedVecKernelCons, consRootContext, ctorContext] using + replayConsRootCheckTypeM + +def validationInferOnlyInsert + (state : TypeChecker.State) (e type : Expr) : TypeChecker.State := + { state with inferTypeI := state.inferTypeI.insert e type } + +@[simp] theorem inferConstantFamilyOnly (lctx : LocalContext) : + TypeChecker.Inner.inferConstant (tcContext lctx) ``IndexedVec + [.param `u] true = .ok indexedVecInfo.type := by + unfold TypeChecker.Inner.inferConstant + simp only [tcContext] + rw [type_get_family] + simp [indexedVecInfo, + ConstantInfo.levelParams, ConstantInfo.type, + ConstantInfo.instantiateTypeLevelParams, + ConstantInfo.toConstantVal, + ConstantVal.instantiateTypeLevelParams, + Expr.instantiateLevelParams_eq, + Expr.instantiateLevelParamsCore', Level.substParams', + Bind.bind, Except.bind, Pure.pure, Except.pure] + +@[simp] theorem inferConstantNatOnly (lctx : LocalContext) : + TypeChecker.Inner.inferConstant (tcContext lctx) ``Nat [] true = + .ok (.sort (.succ .zero)) := by + unfold TypeChecker.Inner.inferConstant + simp [tcContext, natInfo, + ConstantInfo.levelParams, ConstantInfo.instantiateTypeLevelParams, + ConstantInfo.toConstantVal, + ConstantVal.instantiateTypeLevelParams, + Expr.instantiateLevelParams_eq, + Expr.instantiateLevelParamsCore', Level.substParams', + Bind.bind, Except.bind, Pure.pure, Except.pure] + +theorem inferTypeNatOnlyCore + (fuel : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (hcache : state.inferTypeI[(.const ``Nat [] : Expr)]? = none) : + TypeChecker.Inner.inferType' (.const ``Nat []) true + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (.sort (.succ .zero), + validationInferOnlyInsert state (.const ``Nat []) + (.sort (.succ .zero))) := by + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache, + validationInferOnlyInsert, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + +theorem inferTypeFamilyOnlyCore + (fuel : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (hcache : state.inferTypeI[ + (.const ``IndexedVec [.param `u] : Expr)]? = none) : + TypeChecker.Inner.inferType' + (.const ``IndexedVec [.param `u]) true + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (indexedVecInfo.type, + validationInferOnlyInsert state + (.const ``IndexedVec [.param `u]) indexedVecInfo.type) := by + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache, + validationInferOnlyInsert, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + +theorem inferTypeFVarOnlyCore + (fuel : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (id : FVarId) (type : Expr) + (hcache : state.inferTypeI[(.fvar id : Expr)]? = none) + (hfind : lctx.find? id = some (.cdecl index id name type bi kind)) : + TypeChecker.Inner.inferType' (.fvar id) true + (TypeChecker.Methods.withFuel fuel) (tcContext lctx) state = + .ok (type, validationInferOnlyInsert state (.fvar id) type) := by + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache, + TypeChecker.Inner.inferFVar, tcContext, hfind, LocalDecl.type, + validationInferOnlyInsert, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + +def validationFamilyOnlyState : TypeChecker.State := + validationInferOnlyInsert ({} : TypeChecker.State) + (.const ``IndexedVec [.param `u]) indexedVecInfo.type + +def validationIndexedVecOnlyState + (alpha index : Expr) : TypeChecker.State := + validationInferOnlyInsert validationFamilyOnlyState + (ctorIndexedVecApp alpha index) (.sort (.succ (.param `u))) + +@[simp] theorem ctorIndexedVecAppGetAppArgs (alpha index : Expr) : + (ctorIndexedVecApp alpha index).getAppArgs = #[alpha, index] := by + rfl + +@[simp] theorem ctorIndexedVecAppGetAppNumArgs (alpha index : Expr) : + (ctorIndexedVecApp alpha index).getAppNumArgs = 2 := by + rfl + +theorem inferTypeIndexedVecOnlyCore + (fuel : Nat) (lctx : LocalContext) (alpha index : Expr) + (hclosed : (ctorIndexedVecApp alpha index).hasLooseBVars = false) : + TypeChecker.Inner.inferType' + (ctorIndexedVecApp alpha index) true + (TypeChecker.Methods.withFuel (fuel + 1)) (tcContext lctx) + ({} : TypeChecker.State) = + .ok (.sort (.succ (.param `u)), + validationIndexedVecOnlyState alpha index) := by + unfold ctorIndexedVecApp at hclosed ⊢ + have hfn : + (((.const ``IndexedVec [.param `u] : Expr).app alpha).app index).getAppFn = + .const ``IndexedVec [.param `u] := by + rfl + have hargs : + (((.const ``IndexedVec [.param `u] : Expr).app alpha).app index).getAppArgs = + #[alpha, index] := by + rfl + unfold TypeChecker.Inner.inferType' + rw [hclosed] + simp [TypeChecker.Inner.inferApp, + TypeChecker.Inner.inferApp.loop, + hfn, hargs, ctorIndexedVecApp, + validationFamilyOnlyState, validationIndexedVecOnlyState, + validationInferOnlyInsert, + inferTypeFamilyOnlyCore, + indexedVecInfoTypeShape, vecFamilyTail, + Expr.instantiate1', + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +theorem ensureTypeMOfInferOnly + (context : AddInductive.Context) (e : Expr) (level : Level) + (finalState : TypeChecker.State) + (hrun : TypeChecker.Inner.inferType e true + (TypeChecker.Methods.withFuel context.fuel.recDepth) + context.toTypeChecker ({} : TypeChecker.State) = + .ok (.sort level, finalState)) : + TypeChecker.M.run context.env context.safety context.lctx + context.lparams context.fuel (TypeChecker.ensureType e) = + .ok (.sort level) := by + unfold TypeChecker.ensureType TypeChecker.inferType + TypeChecker.ensureSort TypeChecker.RecM.run TypeChecker.M.run + simp only [readThe, MonadReaderOf.read, ReaderT.read, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind, + Pure.pure, StateT.pure, Except.pure, StateT.run', + Functor.map, Except.map] + rw [show TypeChecker.Inner.inferType e true + (TypeChecker.Methods.withFuel context.fuel.recDepth) + { env := context.env, lctx := context.lctx, + safety := context.safety, lparams := context.lparams, + fuel := context.fuel } ({} : TypeChecker.State) = + .ok (.sort level, finalState) by + simpa [AddInductive.Context.toTypeChecker] using hrun] + rfl + +def validationNatOnlyState : TypeChecker.State := + validationInferOnlyInsert ({} : TypeChecker.State) + (.const ``Nat []) (.sort (.succ .zero)) + +theorem indexedVecValidationNatInferOnly : + TypeChecker.Inner.inferType (.const ``Nat []) true + (TypeChecker.Methods.withFuel + indexedVecCtorValidationContext.fuel.recDepth) + indexedVecCtorValidationContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (.sort (.succ .zero), validationNatOnlyState) := by + change TypeChecker.Inner.inferType' (.const ``Nat []) true + (TypeChecker.Methods.withFuel 9999) + (tcContext indexedVecCtorValidationContext.lctx) + ({} : TypeChecker.State) = _ + simpa [validationNatOnlyState] using + (inferTypeNatOnlyCore 9999 indexedVecCtorValidationContext.lctx + ({} : TypeChecker.State) (by simp)) + +theorem indexedVecValidationNatEnsureTypeM : + TypeChecker.M.run indexedVecCtorValidationContext.env + indexedVecCtorValidationContext.safety + indexedVecCtorValidationContext.lctx + indexedVecCtorValidationContext.lparams + indexedVecCtorValidationContext.fuel + (TypeChecker.ensureType (.const ``Nat [])) = + .ok (.sort (.succ .zero)) := by + exact ensureTypeMOfInferOnly indexedVecCtorValidationContext + (.const ``Nat []) (.succ .zero) validationNatOnlyState + indexedVecValidationNatInferOnly + +def validationAlphaOnlyState : TypeChecker.State := + validationInferOnlyInsert ({} : TypeChecker.State) + indexedVecValidationAlpha (.sort (.succ (.param `u))) + +theorem indexedVecValidationAlphaInferOnly : + TypeChecker.Inner.inferType indexedVecValidationAlpha true + (TypeChecker.Methods.withFuel + indexedVecValidationNContext.fuel.recDepth) + indexedVecValidationNContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (.sort (.succ (.param `u)), validationAlphaOnlyState) := by + rw [indexedVecValidationAlphaShape] + change TypeChecker.Inner.inferType' + (.fvar indexedVecValidationAlphaId) true + (TypeChecker.Methods.withFuel 9999) + (tcContext indexedVecValidationNContext.lctx) + ({} : TypeChecker.State) = _ + simpa [validationAlphaOnlyState, + indexedVecValidationAlphaShape] using + (inferTypeFVarOnlyCore 9999 indexedVecValidationNContext.lctx + ({} : TypeChecker.State) indexedVecValidationAlphaId + (.sort (.succ (.param `u))) (by simp) + indexedVecValidationAlphaFindInN) + +theorem indexedVecValidationAlphaEnsureTypeM : + TypeChecker.M.run indexedVecValidationNContext.env + indexedVecValidationNContext.safety + indexedVecValidationNContext.lctx + indexedVecValidationNContext.lparams + indexedVecValidationNContext.fuel + (TypeChecker.ensureType indexedVecValidationAlpha) = + .ok (.sort (.succ (.param `u))) := by + exact ensureTypeMOfInferOnly indexedVecValidationNContext + indexedVecValidationAlpha (.succ (.param `u)) + validationAlphaOnlyState indexedVecValidationAlphaInferOnly + +theorem indexedVecValidationTailInferOnly : + TypeChecker.Inner.inferType + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) true + (TypeChecker.Methods.withFuel + indexedVecValidationHeadContext.fuel.recDepth) + indexedVecValidationHeadContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (.sort (.succ (.param `u)), + validationIndexedVecOnlyState indexedVecValidationAlpha + indexedVecValidationNExpr) := by + change TypeChecker.Inner.inferType' + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) true + (TypeChecker.Methods.withFuel 9999) + (tcContext indexedVecValidationHeadContext.lctx) + ({} : TypeChecker.State) = _ + exact inferTypeIndexedVecOnlyCore 9998 + indexedVecValidationHeadContext.lctx + indexedVecValidationAlpha indexedVecValidationNExpr + (by simp [ctorIndexedVecApp, Expr.hasLooseBVars, + Expr.looseBVarRange']) + +theorem indexedVecValidationTailEnsureTypeM : + TypeChecker.M.run indexedVecValidationHeadContext.env + indexedVecValidationHeadContext.safety + indexedVecValidationHeadContext.lctx + indexedVecValidationHeadContext.lparams + indexedVecValidationHeadContext.fuel + (TypeChecker.ensureType + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr)) = + .ok (.sort (.succ (.param `u))) := by + exact ensureTypeMOfInferOnly indexedVecValidationHeadContext + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) (.succ (.param `u)) + (validationIndexedVecOnlyState indexedVecValidationAlpha + indexedVecValidationNExpr) + indexedVecValidationTailInferOnly + +theorem indexedVecValidationStatsParams : + indexedVecCandidateInductiveStats.params = + #[indexedVecValidationAlpha] := by + simpa [indexedVecValidationAlpha] using + indexedVecCandidateInductiveStats_params + +@[simp] theorem validationExprBneSelf (e : Expr) : + (e != e) = false := by + change (!Expr.eqv e e) = false + rw [show Expr.eqv e e = true by exact Expr.eqv_refl e] + rfl + +theorem indexedVecValidationAppIsValidIdx (index : Expr) + (hindex : AddInductive.hasIndOcc + indexedVecCandidateInductiveStats.indConsts index = false) : + AddInductive.isValidIndAppIdx indexedVecCandidateInductiveStats + (ctorIndexedVecApp indexedVecValidationAlpha index) 0 = true := by + have hparam : + (indexedVecValidationAlpha != indexedVecValidationAlpha) = false := by + change (!Expr.eqv indexedVecValidationAlpha + indexedVecValidationAlpha) = false + rw [show Expr.eqv indexedVecValidationAlpha + indexedVecValidationAlpha = true by + exact Expr.eqv_refl indexedVecValidationAlpha] + rfl + have hindex' : AddInductive.hasIndOcc + #[.const ``IndexedVec [.param `u]] index = false := by + simpa [indexedVecCandidateInductiveStats_indConsts] using hindex + simp +decide [AddInductive.isValidIndAppIdx, + indexedVecCandidateInductiveStats_nindices, + indexedVecValidationStatsParams, + indexedVecCandidateInductiveStats_indConsts, + ctorIndexedVecAppGetAppFn, ctorIndexedVecAppGetAppArgs, + hindex'] + +theorem indexedVecValidationAppIsValid (index : Expr) + (hindex : AddInductive.hasIndOcc + indexedVecCandidateInductiveStats.indConsts index = false) : + AddInductive.isValidIndApp? indexedVecCandidateInductiveStats + (ctorIndexedVecApp indexedVecValidationAlpha index) = some 0 := by + exact AddInductive.isValidIndApp?_singleton_zero + indexedVecCandidateInductiveStats + (ctorIndexedVecApp indexedVecValidationAlpha index) + (by simp [indexedVecCandidateInductiveStats_indConsts]) + (indexedVecValidationAppIsValidIdx index hindex) + +theorem indexedVecValidationZeroHasNoIndOcc : + AddInductive.hasIndOcc indexedVecCandidateInductiveStats.indConsts + (.const ``Nat.zero []) = false := by + simp [AddInductive.hasIndOcc, + indexedVecCandidateInductiveStats_indConsts, Expr.constName!] + +theorem indexedVecValidationNHasNoIndOcc : + AddInductive.hasIndOcc indexedVecCandidateInductiveStats.indConsts + indexedVecValidationNExpr = false := by + simp [AddInductive.hasIndOcc, + indexedVecCandidateInductiveStats_indConsts, + indexedVecValidationNExprShape] + +theorem indexedVecValidationSuccNHasNoIndOcc : + AddInductive.hasIndOcc indexedVecCandidateInductiveStats.indConsts + (replaySuccApp indexedVecValidationNExpr) = false := by + simp [AddInductive.hasIndOcc, + indexedVecCandidateInductiveStats_indConsts, + replaySuccApp, Expr.constName!] + +theorem indexedVecValidationNilResultIsValid : + AddInductive.isValidIndAppIdx indexedVecCandidateInductiveStats + indexedVecValidationNilResult 0 = true := by + exact indexedVecValidationAppIsValidIdx (.const ``Nat.zero []) + indexedVecValidationZeroHasNoIndOcc + +theorem indexedVecValidationConsResultIsValid : + AddInductive.isValidIndAppIdx indexedVecCandidateInductiveStats + indexedVecValidationConsResult 0 = true := by + exact indexedVecValidationAppIsValidIdx + (replaySuccApp indexedVecValidationNExpr) + indexedVecValidationSuccNHasNoIndOcc + +theorem indexedVecValidationNatHasNoIndOcc : + AddInductive.hasIndOcc indexedVecCandidateInductiveStats.indConsts + (.const ``Nat []) = false := by + simp [AddInductive.hasIndOcc, + indexedVecCandidateInductiveStats_indConsts, Expr.constName!] + +theorem indexedVecValidationAlphaHasNoIndOcc : + AddInductive.hasIndOcc indexedVecCandidateInductiveStats.indConsts + indexedVecValidationAlpha = false := by + simp [AddInductive.hasIndOcc, + indexedVecCandidateInductiveStats_indConsts, + indexedVecValidationAlphaShape] + +theorem indexedVecValidationTailHasIndOcc : + AddInductive.hasIndOcc indexedVecCandidateInductiveStats.indConsts + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) = true := by + simp [AddInductive.hasIndOcc, + indexedVecCandidateInductiveStats_indConsts, + ctorIndexedVecApp, Expr.constName!] + +theorem indexedVecValidationNatPositivity : + AddInductive.checkPositivity indexedVecCandidateInductiveStats + (.const ``Nat []) indexedVecKernelCons.name 1 + indexedVecCtorValidationContext = .ok () := by + unfold AddInductive.checkPositivity + simp only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, Pure.pure, + Except.bind, Except.pure] + rw [show indexedVecCtorValidationContext.fuel.inductiveFuel = + 999 + 1 by rfl] + unfold AddInductive.checkPositivity.loop + simp only [ReaderT.bind, Bind.bind] + rw [AddInductive.liftTypeChecker_apply] + rw [show TypeChecker.M.run indexedVecCtorValidationContext.env + indexedVecCtorValidationContext.safety + indexedVecCtorValidationContext.lctx + indexedVecCtorValidationContext.lparams + indexedVecCtorValidationContext.fuel + (TypeChecker.whnf (.const ``Nat [])) = + .ok (.const ``Nat []) by + change TypeChecker.M.run ctorEnv .safe + indexedVecCtorValidationContext.lctx [`u] ({} : FuelConfig) + (TypeChecker.whnf (.const ``Nat [])) = .ok (.const ``Nat []) + exact ctorNatWhnfM indexedVecCtorValidationContext.lctx] + simp only [Except.bind] + rw [indexedVecValidationNatHasNoIndOcc] + simp only [Bool.not_false, if_true, + ReaderT.pure, Pure.pure, Except.pure] + +theorem indexedVecValidationAlphaPositivity : + AddInductive.checkPositivity indexedVecCandidateInductiveStats + indexedVecValidationAlpha indexedVecKernelCons.name 2 + indexedVecValidationNContext = .ok () := by + unfold AddInductive.checkPositivity + simp only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, Pure.pure, + Except.bind, Except.pure] + rw [show indexedVecValidationNContext.fuel.inductiveFuel = + 999 + 1 by rfl] + unfold AddInductive.checkPositivity.loop + simp only [ReaderT.bind, Bind.bind] + rw [AddInductive.liftTypeChecker_apply] + rw [show TypeChecker.M.run indexedVecValidationNContext.env + indexedVecValidationNContext.safety + indexedVecValidationNContext.lctx + indexedVecValidationNContext.lparams + indexedVecValidationNContext.fuel + (TypeChecker.whnf indexedVecValidationAlpha) = + .ok indexedVecValidationAlpha by + rw [indexedVecValidationAlphaShape] + change TypeChecker.M.run ctorEnv .safe + indexedVecValidationNContext.lctx [`u] ({} : FuelConfig) + (TypeChecker.whnf (.fvar indexedVecValidationAlphaId)) = + .ok (.fvar indexedVecValidationAlphaId) + exact ctorFVarWhnfM indexedVecValidationNContext.lctx + indexedVecValidationAlphaId indexedVecValidationAlphaFindInN] + simp only [Except.bind] + rw [indexedVecValidationAlphaHasNoIndOcc] + simp only [Bool.not_false, if_true, + ReaderT.pure, Pure.pure, Except.pure] + +theorem indexedVecValidationTailPositivity : + AddInductive.checkPositivity indexedVecCandidateInductiveStats + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) + indexedVecKernelCons.name 3 indexedVecValidationHeadContext = + .ok () := by + unfold AddInductive.checkPositivity + simp only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, Pure.pure, + Except.bind, Except.pure] + rw [show indexedVecValidationHeadContext.fuel.inductiveFuel = + 999 + 1 by rfl] + unfold AddInductive.checkPositivity.loop + simp only [ReaderT.bind, Bind.bind] + rw [AddInductive.liftTypeChecker_apply] + rw [show TypeChecker.M.run indexedVecValidationHeadContext.env + indexedVecValidationHeadContext.safety + indexedVecValidationHeadContext.lctx + indexedVecValidationHeadContext.lparams + indexedVecValidationHeadContext.fuel + (TypeChecker.whnf + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr)) = + .ok (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) by + change TypeChecker.M.run ctorEnv .safe + indexedVecValidationHeadContext.lctx [`u] ({} : FuelConfig) + (TypeChecker.whnf + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr)) = + .ok (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) + exact ctorIndexedVecWhnfM indexedVecValidationHeadContext.lctx + indexedVecValidationAlpha indexedVecValidationNExpr] + simp only [Except.bind] + rw [indexedVecValidationTailHasIndOcc] + simp only [Bool.not_true, Bool.false_eq_true, if_false, Pure.pure] + rw [indexedVecValidationAppIsValid indexedVecValidationNExpr + indexedVecValidationNHasNoIndOcc] + rfl + +theorem indexedVecValidationNilLoopTerminal : + AddInductive.checkConstructorType.loop + indexedVecCandidateInductiveStats false 0 + indexedVecKernelNil.name indexedVecValidationNilResult 1 999 + indexedVecCtorValidationContext = .ok () := by + rw [show 999 = 998 + 1 by rfl] + have hvalid : AddInductive.isValidIndAppIdx + indexedVecCandidateInductiveStats + ((.const ``IndexedVec [.param `u] : Expr).app + (.fvar indexedVecValidationAlphaId) |>.app + (.const ``Nat.zero [])) 0 = + true := by + simpa [indexedVecValidationNilResult, ctorIndexedVecApp, + indexedVecValidationAlphaShape] using + indexedVecValidationNilResultIsValid + unfold indexedVecValidationNilResult ctorIndexedVecApp + unfold AddInductive.checkConstructorType.loop + simp [hvalid, + ReaderT.pure, Pure.pure, Except.pure] + +theorem indexedVecValidationNilLoop : + AddInductive.checkConstructorType.loop + indexedVecCandidateInductiveStats false 0 + indexedVecKernelNil.name indexedVecKernelNil.type 0 + indexedVecCtorValidationContext.fuel.inductiveFuel + indexedVecCtorValidationContext = .ok () := by + rw [show indexedVecCtorValidationContext.fuel.inductiveFuel = + 999 + 1 by rfl] + unfold AddInductive.checkConstructorType.loop + simp only [indexedVecKernelNil, indexedVecNilInfo, + ConstantInfo.name, ConstantInfo.type, ConstantInfo.toConstantVal] + rw [show indexedVecCandidateInductiveStats.params[0]? = + some indexedVecValidationAlpha by + simp [indexedVecValidationStatsParams]] + simp only [ReaderT.bind, Bind.bind] + rw [indexedVecValidationGetTypeAlpha] + simp only [Except.bind] + rw [AddInductive.liftTypeChecker_apply] + rw [indexedVecValidationParamIsDefEq] + simp only [if_true] + simpa [indexedVecValidationNilResult, ctorIndexedVecApp, + indexedVecKernelNil, indexedVecNilInfo, ConstantInfo.name, + ConstantInfo.toConstantVal, + Expr.instantiate1_eq, Expr.instantiate1', + Expr.liftLooseBVars_zero] using indexedVecValidationNilLoopTerminal + +@[simp] theorem indexedVecValidationConsumeNat : + AddInductive.consumeTypeAnnotations (.const ``Nat []) = + .const ``Nat [] := by + simp [AddInductive.consumeTypeAnnotations] + +@[simp] theorem indexedVecValidationConsumeAlpha : + AddInductive.consumeTypeAnnotations indexedVecValidationAlpha = + indexedVecValidationAlpha := by + rw [indexedVecValidationAlphaShape] + simp [AddInductive.consumeTypeAnnotations] + +@[simp] theorem indexedVecValidationConsumeTail : + AddInductive.consumeTypeAnnotations + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) = + ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr := by + simp [ctorIndexedVecApp, AddInductive.consumeTypeAnnotations] + +theorem indexedVecValidationConsLoopTerminal : + AddInductive.checkConstructorType.loop + indexedVecCandidateInductiveStats false 0 + indexedVecKernelCons.name indexedVecValidationConsResult 4 996 + indexedVecValidationTailContext = .ok () := by + rw [show 996 = 995 + 1 by rfl] + have hvalid : AddInductive.isValidIndAppIdx + indexedVecCandidateInductiveStats + (((.const ``IndexedVec [.param `u] : Expr).app + (.fvar indexedVecValidationAlphaId)).app + ((.const ``Nat.succ [] : Expr).app + (.fvar indexedVecValidationNId))) 0 = true := by + simpa [indexedVecValidationConsResult, ctorIndexedVecApp, + replaySuccApp, indexedVecValidationAlphaShape, + indexedVecValidationNExprShape] using + indexedVecValidationConsResultIsValid + unfold indexedVecValidationConsResult ctorIndexedVecApp replaySuccApp + unfold AddInductive.checkConstructorType.loop + simp [hvalid, ReaderT.pure, Pure.pure, Except.pure] + +theorem indexedVecValidationConsLoopTail : + AddInductive.checkConstructorType.loop + indexedVecCandidateInductiveStats false 0 + indexedVecKernelCons.name indexedVecValidationConsAfterHead 3 997 + indexedVecValidationHeadContext = .ok () := by + rw [show 997 = 996 + 1 by rfl] + unfold indexedVecValidationConsAfterHead + unfold AddInductive.checkConstructorType.loop + simp only + rw [show indexedVecCandidateInductiveStats.params[3]? = none by + simp [indexedVecValidationStatsParams]] + simp only [ReaderT.bind, Bind.bind] + rw [AddInductive.liftTypeChecker_apply] + rw [indexedVecValidationTailEnsureTypeM] + simp only [Except.bind] + rw [if_pos (show AddInductive.levelStructGe + indexedVecCandidateInductiveStats.resultLevel + (Expr.sort (.succ (.param `u))).sortLevel! = true from by + simp [Expr.sortLevel!, indexedVecCandidateInductiveStats_resultLevel, + AddInductive.levelStructGe, AddInductive.levelStructEq])] + simp only [↓reduceIte, Bool.not_false, ReaderT.bind, Bind.bind, Except.bind] + rw [indexedVecValidationTailPositivity] + rw [AddInductive.withLocalDecl_apply] + rw [indexedVecValidationConsumeTail] + rw [show (ctorIndexedVecApp indexedVecValidationAlpha + (replaySuccApp indexedVecValidationNExpr)).instantiate1 + indexedVecValidationHeadContext.freshExpr = + indexedVecValidationConsResult by + simp [indexedVecValidationConsResult, ctorIndexedVecApp, + replaySuccApp, Expr.instantiate1_eq, Expr.instantiate1']] + simpa [indexedVecValidationTailContext, + AddInductive.Context.pushLocalDecl, + ReaderT.pure, Pure.pure, Except.pure] using + indexedVecValidationConsLoopTerminal + +theorem indexedVecValidationConsLoopHead : + AddInductive.checkConstructorType.loop + indexedVecCandidateInductiveStats false 0 + indexedVecKernelCons.name indexedVecValidationConsAfterN 2 998 + indexedVecValidationNContext = .ok () := by + rw [show 998 = 997 + 1 by rfl] + unfold indexedVecValidationConsAfterN + unfold AddInductive.checkConstructorType.loop + simp only + rw [show indexedVecCandidateInductiveStats.params[2]? = none by + simp [indexedVecValidationStatsParams]] + simp only [ReaderT.bind, Bind.bind] + rw [AddInductive.liftTypeChecker_apply] + rw [indexedVecValidationAlphaEnsureTypeM] + simp only [Except.bind] + rw [if_pos (show AddInductive.levelStructGe + indexedVecCandidateInductiveStats.resultLevel + (Expr.sort (.succ (.param `u))).sortLevel! = true from by + simp [Expr.sortLevel!, indexedVecCandidateInductiveStats_resultLevel, + AddInductive.levelStructGe, AddInductive.levelStructEq])] + simp only [↓reduceIte, Bool.not_false, ReaderT.bind, Bind.bind, Except.bind] + rw [indexedVecValidationAlphaPositivity] + rw [AddInductive.withLocalDecl_apply] + rw [indexedVecValidationConsumeAlpha] + rw [show + ((.forallE consTailName + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) + (ctorIndexedVecApp indexedVecValidationAlpha + (replaySuccApp indexedVecValidationNExpr)) + .default : Expr).instantiate1 + indexedVecValidationNContext.freshExpr) = + indexedVecValidationConsAfterHead by + simp [indexedVecValidationConsAfterHead, + ctorIndexedVecApp, replaySuccApp, + Expr.instantiate1_eq, Expr.instantiate1']] + simpa [indexedVecValidationHeadContext, + AddInductive.Context.pushLocalDecl, + ReaderT.pure, Pure.pure, Except.pure] using + indexedVecValidationConsLoopTail + +theorem indexedVecValidationConsLoopN : + AddInductive.checkConstructorType.loop + indexedVecCandidateInductiveStats false 0 + indexedVecKernelCons.name indexedVecValidationConsAfterParam 1 999 + indexedVecCtorValidationContext = .ok () := by + rw [show 999 = 998 + 1 by rfl] + rw [indexedVecValidationConsAfterParamExplicitShape] + unfold AddInductive.checkConstructorType.loop + simp only + rw [show indexedVecCandidateInductiveStats.params[1]? = none by + simp [indexedVecValidationStatsParams]] + simp only [ReaderT.bind, Bind.bind] + rw [AddInductive.liftTypeChecker_apply] + rw [indexedVecValidationNatEnsureTypeM] + simp only [Except.bind] + rw [if_pos (show AddInductive.levelStructGe + indexedVecCandidateInductiveStats.resultLevel + (Expr.sort (.succ .zero)).sortLevel! = true from by + simp [Expr.sortLevel!, indexedVecCandidateInductiveStats_resultLevel, + AddInductive.levelStructGe])] + simp only [↓reduceIte, Bool.not_false, ReaderT.bind, Bind.bind, Except.bind] + rw [indexedVecValidationNatPositivity] + rw [AddInductive.withLocalDecl_apply] + rw [indexedVecValidationConsumeNat] + rw [show + ((.forallE consHeadName indexedVecValidationAlpha + (.forallE consTailName + (ctorIndexedVecApp indexedVecValidationAlpha (.bvar 1)) + (ctorIndexedVecApp indexedVecValidationAlpha + (replaySuccApp (.bvar 2))) + .default) + .default : Expr).instantiate1 + indexedVecCtorValidationContext.freshExpr) = + indexedVecValidationConsAfterN by + simp [indexedVecValidationConsAfterN, + ctorIndexedVecApp, replaySuccApp, + indexedVecValidationNExpr, + AddInductive.Context.freshExpr, + Expr.instantiate1_eq, Expr.instantiate1']] + simpa [indexedVecValidationNContext, + AddInductive.Context.pushLocalDecl, + ReaderT.pure, Pure.pure, Except.pure] using + indexedVecValidationConsLoopHead + +theorem indexedVecValidationConsLoop : + AddInductive.checkConstructorType.loop + indexedVecCandidateInductiveStats false 0 + indexedVecKernelCons.name indexedVecKernelCons.type 0 + indexedVecCtorValidationContext.fuel.inductiveFuel + indexedVecCtorValidationContext = .ok () := by + rw [show indexedVecCtorValidationContext.fuel.inductiveFuel = + 999 + 1 by rfl] + rw [show indexedVecKernelCons.type = consCtorTypeRaw by + simpa [indexedVecKernelCons] using consInfoTypeShape] + unfold consCtorTypeRaw + unfold AddInductive.checkConstructorType.loop + simp only + rw [show indexedVecCandidateInductiveStats.params[0]? = + some indexedVecValidationAlpha by + simp [indexedVecValidationStatsParams]] + simp only [ReaderT.bind, Bind.bind] + rw [indexedVecValidationGetTypeAlpha] + simp only [Except.bind] + rw [AddInductive.liftTypeChecker_apply] + rw [indexedVecValidationParamIsDefEq] + simp only [if_true] + simpa [indexedVecValidationConsAfterParam] using + indexedVecValidationConsLoopN + +theorem indexedVecValidationNilUniverseLoopTerminal : + AddInductive.checkConstructorUniverseSemantics.loop + indexedVecCandidateInductiveStats indexedVecValidationNilResult 1 999 + indexedVecCtorValidationContext = .ok () := by + rw [show 999 = 998 + 1 by rfl] + unfold AddInductive.checkConstructorUniverseSemantics.loop + rfl + +theorem indexedVecValidationNilUniverseLoop : + AddInductive.checkConstructorUniverseSemantics.loop + indexedVecCandidateInductiveStats indexedVecKernelNil.type 0 + indexedVecCtorValidationContext.fuel.inductiveFuel + indexedVecCtorValidationContext = .ok () := by + rw [show indexedVecCtorValidationContext.fuel.inductiveFuel = + 999 + 1 by rfl] + unfold AddInductive.checkConstructorUniverseSemantics.loop + simp only [indexedVecKernelNil, indexedVecNilInfo, + ConstantInfo.type, ConstantInfo.toConstantVal] + rw [show indexedVecCandidateInductiveStats.params[0]? = + some indexedVecValidationAlpha by + simp [indexedVecValidationStatsParams]] + simpa [indexedVecValidationNilResult, ctorIndexedVecApp, + indexedVecKernelNil, indexedVecNilInfo, ConstantInfo.toConstantVal, + Expr.instantiate1_eq, Expr.instantiate1', + Expr.liftLooseBVars_zero] using + indexedVecValidationNilUniverseLoopTerminal + +theorem indexedVecValidationConsUniverseLoopTerminal : + AddInductive.checkConstructorUniverseSemantics.loop + indexedVecCandidateInductiveStats indexedVecValidationConsResult 4 996 + indexedVecValidationTailContext = .ok () := by + rw [show 996 = 995 + 1 by rfl] + unfold AddInductive.checkConstructorUniverseSemantics.loop + rfl + +theorem indexedVecValidationConsUniverseLoopTail : + AddInductive.checkConstructorUniverseSemantics.loop + indexedVecCandidateInductiveStats indexedVecValidationConsAfterHead 3 997 + indexedVecValidationHeadContext = .ok () := by + rw [show 997 = 996 + 1 by rfl] + unfold indexedVecValidationConsAfterHead + unfold AddInductive.checkConstructorUniverseSemantics.loop + simp only + rw [show indexedVecCandidateInductiveStats.params[3]? = none by + simp [indexedVecValidationStatsParams]] + simp only [ReaderT.bind, Bind.bind, AddInductive.liftTypeChecker_apply] + rw [indexedVecValidationTailEnsureTypeM] + simp only [Except.bind] + rw [if_pos (show AddInductive.constructorUniverseSemanticGe + indexedVecCandidateInductiveStats.resultLevel + (Expr.sort (.succ (.param `u))).sortLevel! = true from by + simp [Expr.sortLevel!, AddInductive.constructorUniverseSemanticGe, + indexedVecCandidateInductiveStats_resultLevel, + AddInductive.levelStructGe, AddInductive.levelStructEq])] + rw [AddInductive.withLocalDecl_apply] + simpa [indexedVecValidationTailContext, + AddInductive.Context.pushLocalDecl, + AddInductive.consumeTypeAnnotations, + indexedVecValidationConsResult, ctorIndexedVecApp, + replaySuccApp, Expr.instantiate1_eq, Expr.instantiate1'] using + indexedVecValidationConsUniverseLoopTerminal + +theorem indexedVecValidationConsUniverseLoopHead : + AddInductive.checkConstructorUniverseSemantics.loop + indexedVecCandidateInductiveStats indexedVecValidationConsAfterN 2 998 + indexedVecValidationNContext = .ok () := by + rw [show 998 = 997 + 1 by rfl] + unfold indexedVecValidationConsAfterN + unfold AddInductive.checkConstructorUniverseSemantics.loop + simp only + rw [show indexedVecCandidateInductiveStats.params[2]? = none by + simp [indexedVecValidationStatsParams]] + simp only [ReaderT.bind, Bind.bind, AddInductive.liftTypeChecker_apply] + rw [indexedVecValidationAlphaEnsureTypeM] + simp only [Except.bind] + rw [if_pos (show AddInductive.constructorUniverseSemanticGe + indexedVecCandidateInductiveStats.resultLevel + (Expr.sort (.succ (.param `u))).sortLevel! = true from by + simp [Expr.sortLevel!, AddInductive.constructorUniverseSemanticGe, + indexedVecCandidateInductiveStats_resultLevel, + AddInductive.levelStructGe, AddInductive.levelStructEq])] + rw [AddInductive.withLocalDecl_apply] + simpa [indexedVecValidationHeadContext, + AddInductive.Context.pushLocalDecl, + AddInductive.consumeTypeAnnotations, + indexedVecValidationConsAfterHead, + ctorIndexedVecApp, replaySuccApp, + Expr.instantiate1_eq, Expr.instantiate1'] using + indexedVecValidationConsUniverseLoopTail + +theorem indexedVecValidationConsUniverseLoopN : + AddInductive.checkConstructorUniverseSemantics.loop + indexedVecCandidateInductiveStats indexedVecValidationConsAfterParam 1 999 + indexedVecCtorValidationContext = .ok () := by + rw [show 999 = 998 + 1 by rfl] + rw [indexedVecValidationConsAfterParamExplicitShape] + unfold AddInductive.checkConstructorUniverseSemantics.loop + simp only + rw [show indexedVecCandidateInductiveStats.params[1]? = none by + simp [indexedVecValidationStatsParams]] + simp only [ReaderT.bind, Bind.bind, AddInductive.liftTypeChecker_apply] + rw [indexedVecValidationNatEnsureTypeM] + simp only [Except.bind] + rw [if_pos (show AddInductive.constructorUniverseSemanticGe + indexedVecCandidateInductiveStats.resultLevel + (Expr.sort (.succ .zero)).sortLevel! = true from by + simp [Expr.sortLevel!, AddInductive.constructorUniverseSemanticGe, + indexedVecCandidateInductiveStats_resultLevel, + AddInductive.levelStructGe])] + rw [AddInductive.withLocalDecl_apply] + simpa [indexedVecValidationNContext, + AddInductive.Context.pushLocalDecl, + AddInductive.consumeTypeAnnotations, + indexedVecValidationConsAfterN, + ctorIndexedVecApp, replaySuccApp, + indexedVecValidationNExpr, + AddInductive.Context.freshExpr, + Expr.instantiate1_eq, Expr.instantiate1'] using + indexedVecValidationConsUniverseLoopHead + +theorem indexedVecValidationConsUniverseLoop : + AddInductive.checkConstructorUniverseSemantics.loop + indexedVecCandidateInductiveStats indexedVecKernelCons.type 0 + indexedVecCtorValidationContext.fuel.inductiveFuel + indexedVecCtorValidationContext = .ok () := by + rw [show indexedVecCtorValidationContext.fuel.inductiveFuel = + 999 + 1 by rfl] + rw [show indexedVecKernelCons.type = consCtorTypeRaw by + simpa [indexedVecKernelCons] using consInfoTypeShape] + unfold consCtorTypeRaw + unfold AddInductive.checkConstructorUniverseSemantics.loop + simp only + rw [show indexedVecCandidateInductiveStats.params[0]? = + some indexedVecValidationAlpha by + simp [indexedVecValidationStatsParams]] + simpa [indexedVecValidationConsAfterParam] using + indexedVecValidationConsUniverseLoopN + +theorem indexedVecValidationNilUniverseRun : + AddInductive.checkConstructorUniverseSemantics + indexedVecCandidateInductiveStats indexedVecKernelNil.type + indexedVecCtorValidationContext = .ok () := by + unfold AddInductive.checkConstructorUniverseSemantics + simpa only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] using indexedVecValidationNilUniverseLoop + +theorem indexedVecValidationConsUniverseRun : + AddInductive.checkConstructorUniverseSemantics + indexedVecCandidateInductiveStats indexedVecKernelCons.type + indexedVecCtorValidationContext = .ok () := by + unfold AddInductive.checkConstructorUniverseSemantics + simpa only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] using indexedVecValidationConsUniverseLoop + +/-- Both source-ordered `IndexedVec` constructors pass the executable verified +universe gate. -/ +theorem indexedVecValidationCheckConstructorUniverseSemantics : + AddInductive.checkConstructorUniverseListSemantics + indexedVecCandidateInductiveStats indexedVecKernelType.ctors + indexedVecCtorValidationContext = .ok () := by + unfold AddInductive.checkConstructorUniverseListSemantics + simp only [indexedVecKernelType, ReaderT.bind, Bind.bind] + rw [indexedVecValidationNilUniverseRun] + simp only [Except.bind] + unfold AddInductive.checkConstructorUniverseListSemantics + simp only [ReaderT.bind, Bind.bind] + rw [indexedVecValidationConsUniverseRun] + rfl + +theorem indexedVecValidationGetEnvM : + TypeChecker.M.run indexedVecCtorValidationContext.env + indexedVecCtorValidationContext.safety + indexedVecCtorValidationContext.lctx + indexedVecCtorValidationContext.lparams + indexedVecCtorValidationContext.fuel TypeChecker.getEnv = + .ok ctorEnv := by + rfl + +theorem indexedVecValidationEmptyDoesNotContainNil : + (∅ : NameSet).contains indexedVecKernelNil.name = false := by + simp +decide + +theorem indexedVecValidationNilSetDoesNotContainCons : + ((∅ : NameSet).insert indexedVecKernelNil.name).contains + indexedVecKernelCons.name = false := by + simp only [indexedVecKernelNil, indexedVecKernelCons, + indexedVecNilInfo, indexedVecConsInfo, + ConstantInfo.name, ConstantInfo.toConstantVal, + NameSet.contains, NameSet.insert] + rw [Std.TreeSet.contains_insert (t := (∅ : NameSet))] + simp +decide + +set_option linter.unusedSimpArgs false in +theorem indexedVecValidationCheckConstructors : + AddInductive.checkConstructors #[indexedVecKernelType] + indexedVecCandidateInductiveStats false + indexedVecCtorValidationContext = .ok () := by + unfold AddInductive.checkConstructors + simp only [ReaderT.bind, Bind.bind] + rw [AddInductive.liftTypeChecker_apply] + rw [indexedVecValidationGetEnvM] + simp only [Except.bind] + unfold AddInductive.checkConstructorsLoop AddInductive.checkConstructorFold + simp only [indexedVecKernelType, List.toList_toArray, + ReaderT.bind, Bind.bind, Except.bind] + rw [indexedVecValidationEmptyDoesNotContainNil] + simp only [Bool.false_eq_true, if_false, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] + rw [indexedVecNilNoMVarNoFVar] + simp only [ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure, AddInductive.liftExcept_apply] + rw [AddInductive.withEmptyLocalContext_apply] + rw [AddInductive.liftTypeChecker_apply] + rw [indexedVecValidationNilRootCheckTypeM] + unfold AddInductive.checkConstructorType + simp only [Except.bind, readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, Except.pure] + rw [indexedVecValidationNilLoop] + unfold AddInductive.checkConstructorFold + simp only [Except.bind, ReaderT.pure, Pure.pure, Except.pure] + rw [indexedVecValidationNilSetDoesNotContainCons] + simp only [Bool.false_eq_true, if_false, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] + rw [indexedVecConsNoMVarNoFVar] + simp only [ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure, AddInductive.liftExcept_apply] + rw [AddInductive.withEmptyLocalContext_apply] + rw [AddInductive.liftTypeChecker_apply] + rw [indexedVecValidationConsRootCheckTypeM] + unfold AddInductive.checkConstructorType + simp only [Except.bind, readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, Except.pure] + rw [indexedVecValidationConsLoop] + unfold AddInductive.checkConstructorFold + rfl + +/-- The complete one-parameter, one-index IndexedVec request produces the +exact ordered family/nil/cons normalization candidate. -/ +theorem indexedVecNormalizationCandidateProduced : + AddInductive.buildNormalizationCandidate 1 + [indexedVecKernelType] 0 false + indexedVecFamilyCandidateContext = + .ok indexedVecNormalizationCandidate := by + unfold AddInductive.buildNormalizationCandidate + rw [indexedVec_checkInductiveTypes] + simp only [ReaderT.bind, Bind.bind] + rw [show + (withReader (fun _ : AddInductive.Context => + { indexedVecFamilyCandidateContext with lctx := {} }) + (AddInductive.normalizeCandidateFamilyTypeList + [indexedVecKernelType])) indexedVecFamilyCandidate.trace.terminalContext = + .ok (.cons indexedVecFamilyListCandidate.familyType .nil) by + simpa using indexedVecFamilyTypeListCandidateProduced] + simp only [Except.bind] + rw [indexedVecDeclareFromTerminal] + unfold AddInductive.withEnv + change (ReaderT.bind + (AddInductive.checkConstructors #[indexedVecKernelType] + indexedVecCandidateInductiveStats false) + (fun _ => ReaderT.bind + (fun _ : AddInductive.Context => + AddInductive.normalizeCandidateFamilyList + (.cons indexedVecFamilyListCandidate.familyType .nil) + ctorContext) + (fun families => pure + (⟨families⟩ : AddInductive.NormalizationCandidate + [indexedVecKernelType])))) + indexedVecCtorValidationContext = _ + simp only [ReaderT.bind, Bind.bind] + rw [indexedVecValidationCheckConstructors] + simp only [Except.bind] + rw [indexedVecFamilyListCandidateProduced] + rfl + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVecNormalizationCandidateProduced [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +end Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/IndexedVecSemanticReplay.lean b/Ix/Theory/Named/Verify/Environment/IndexedVecSemanticReplay.lean new file mode 100644 index 000000000..1a12306a7 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/IndexedVecSemanticReplay.lean @@ -0,0 +1,3540 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Verify.Environment.IndexedVecOuterReplay + +/-! +# Complete semantic replay of the IndexedVec normalization candidate + +This module connects the exact executable family/`nil`/`cons` candidate +produced by `buildNormalizationCandidate` to its Theory generation +certificate and the E1 kernel-environment replay. Every retained candidate +node is interpreted in its exact pre-family or post-family verifier context; +the final transaction therefore consumes the certificate projected from the +same producer-selected package rather than an independently supplied +well-formedness proof. +-/ + +namespace Ix.Theory.Named.InductiveReplayFixtures +open Lean Meta +open Ix.Theory.Named.InductiveFixtures +open IndexedVecConsReplay + +theorem indexedVecSemanticNatHasPrimitives : VEnv.HasPrimitives natFinalEnv := by + have absent (n : Name) (hlookup : natFinalEnv.constants n = none) : + ¬ natFinalEnv.contains n := by + rintro ⟨ci, hci⟩ + rw [hlookup] at hci + contradiction + refine { + bool := fun h => (absent ``Bool rfl h).elim + boolFalse := fun h => by + change none = some _ at h + contradiction + boolTrue := fun h => by + change none = some _ at h + contradiction + nat := fun _ => ⟨⟨_, rfl⟩, ⟨_, rfl⟩⟩ + natZero := fun h => by + change some natType.ctors[0].toVConstant = some _ at h + exact (Option.some.inj h).symm + natSucc := fun h => by + change some natType.ctors[1].toVConstant = some _ at h + exact (Option.some.inj h).symm + natAdd := fun h => (absent ``Nat.add rfl h).elim + natSub := fun h => (absent ``Nat.sub rfl h).elim + natMul := fun h => (absent ``Nat.mul rfl h).elim + natPow := fun h => (absent ``Nat.pow rfl h).elim + natGcd := fun h => (absent ``Nat.gcd rfl h).elim + natMod := fun h => (absent ``Nat.mod rfl h).elim + natDiv := fun h => (absent ``Nat.div rfl h).elim + natBEq := fun h => (absent ``Nat.beq rfl h).elim + natBLE := fun h => (absent ``Nat.ble rfl h).elim + natLAnd := fun h => (absent ``Nat.land rfl h).elim + natLOr := fun h => (absent ``Nat.lor rfl h).elim + natXor := fun h => (absent ``Nat.xor rfl h).elim + natShiftLeft := fun h => (absent ``Nat.shiftLeft rfl h).elim + natShiftRight := fun h => (absent ``Nat.shiftRight rfl h).elim + charOfNat := fun h => by + change none = some _ at h + contradiction + stringOfList := fun h => by + change none = some _ at h + contradiction } + +theorem indexedVecSemanticNatSafePrimitives : + indexedVecKernelEnv.find? n = some ci → + Kernel.Environment.primitives.contains n → + ci.safety = .safe ∧ ci.levelParams = [] := by + intro hfind hprim + change natMap.find?' n = some ci at hfind + rw [natMap_wf.find?'_eq_find?, natMap, + natCtorMap_wf.find?_insert] at hfind + split at hfind + · rename_i heq + simp at heq + subst n + simp at hfind + subst ci + simp [Kernel.Environment.primitives, NameSet.ofList] at hprim + simp +decide [NameSet.contains] at hprim + · rw [natCtorMap, natZeroMap_wf.find?_insert] at hfind + split at hfind + · rename_i heq + simp at heq + subst n + simp at hfind + subst ci + exact ⟨rfl, rfl⟩ + · rw [natZeroMap, natTypeMap_wf.find?_insert] at hfind + split at hfind + · rename_i heq + simp at heq + subst n + simp at hfind + subst ci + exact ⟨rfl, rfl⟩ + · rw [natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + at hfind + split at hfind + · rename_i heq + simp at heq + subst n + simp at hfind + subst ci + exact ⟨rfl, rfl⟩ + · simp [SMap.find?] at hfind + +theorem indexedVecKernelEnv_noProjectionReady (name : Name) : + indexedVecKernelEnv.isProjectionReadyStructure name = false := by + simp only [indexedVecKernelEnv, + Kernel.Environment.isProjectionReadyStructure, + Kernel.Environment.ofConstants] + simp only [natMap_wf.find?'_eq_find?] + simp only [natMap, natCtorMap_wf.find?_insert] + simp only [natCtorMap, natZeroMap_wf.find?_insert] + simp only [natZeroMap, natTypeMap_wf.find?_insert] + simp only [natTypeMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] + by_cases hRec : ``Nat.rec = name + · subst name + simp [natRecInfo] + · by_cases hSucc : ``Nat.succ = name + · subst name + simp [hRec, natSuccInfo] + · by_cases hZero : ``Nat.zero = name + · subst name + simp [hRec, hSucc, natZeroInfo] + · by_cases hNat : ``Nat = name + · subst name + simp [hRec, hSucc, hZero, natInfo] + · simp [hRec, hSucc, hZero, hNat, SMap.find?] + +theorem indexedVecKernelEnv_noStructureEta (name : Name) : + indexedVecKernelEnv.isNonRecStructure name = false := by + simp only [indexedVecKernelEnv, Kernel.Environment.isNonRecStructure, + Kernel.Environment.ofConstants, Kernel.Environment.find?] + simp only [natMap_wf.find?'_eq_find?] + simp only [natMap, natCtorMap_wf.find?_insert] + simp only [natCtorMap, natZeroMap_wf.find?_insert] + simp only [natZeroMap, natTypeMap_wf.find?_insert] + simp only [natTypeMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] + by_cases hRec : ``Nat.rec = name + · subst name + simp [natRecInfo] + · by_cases hSucc : ``Nat.succ = name + · subst name + simp [hRec, natSuccInfo] + · by_cases hZero : ``Nat.zero = name + · subst name + simp [hRec, hSucc, natZeroInfo] + · by_cases hNat : ``Nat = name + · subst name + simp [hRec, hSucc, hZero, natInfo] + · simp [hRec, hSucc, hZero, hNat, SMap.find?] + +theorem indexedVecTypeEnv_noProjectionReady (name : Name) : + ctorContext.env.isProjectionReadyStructure name = false := by + simp only [ctorContext, ctorEnv, + Kernel.Environment.isProjectionReadyStructure, + Kernel.Environment.ofConstants] + simp only [indexedVecTypeMap_wf.find?'_eq_find?] + simp only [indexedVecTypeMap, natMap_wf.find?_insert] + simp only [natMap, natCtorMap_wf.find?_insert] + simp only [natCtorMap, natZeroMap_wf.find?_insert] + simp only [natZeroMap, natTypeMap_wf.find?_insert] + simp only [natTypeMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] + by_cases hVec : ``IndexedVec = name + · subst name + simp [indexedVecInfo] + · by_cases hRec : ``Nat.rec = name + · subst name + simp [hVec, natRecInfo] + · by_cases hSucc : ``Nat.succ = name + · subst name + simp [hVec, hRec, natSuccInfo] + · by_cases hZero : ``Nat.zero = name + · subst name + simp [hVec, hRec, hSucc, natZeroInfo] + · by_cases hNat : ``Nat = name + · subst name + simp [hVec, hRec, hSucc, hZero, natInfo] + · simp [hVec, hRec, hSucc, hZero, hNat, SMap.find?] + +theorem indexedVecTypeEnv_noStructureEta (name : Name) : + ctorContext.env.isNonRecStructure name = false := by + simp only [ctorContext, ctorEnv, Kernel.Environment.isNonRecStructure, + Kernel.Environment.ofConstants, Kernel.Environment.find?] + simp only [indexedVecTypeMap_wf.find?'_eq_find?] + simp only [indexedVecTypeMap, natMap_wf.find?_insert] + simp only [natMap, natCtorMap_wf.find?_insert] + simp only [natCtorMap, natZeroMap_wf.find?_insert] + simp only [natZeroMap, natTypeMap_wf.find?_insert] + simp only [natTypeMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] + by_cases hVec : ``IndexedVec = name + · subst name + simp [indexedVecInfo] + · by_cases hRec : ``Nat.rec = name + · subst name + simp [hVec, natRecInfo] + · by_cases hSucc : ``Nat.succ = name + · subst name + simp [hVec, hRec, natSuccInfo] + · by_cases hZero : ``Nat.zero = name + · subst name + simp [hVec, hRec, hSucc, natZeroInfo] + · by_cases hNat : ``Nat = name + · subst name + simp [hVec, hRec, hSucc, hZero, natInfo] + · simp [hVec, hRec, hSucc, hZero, hNat, SMap.find?] + +private theorem addConst_constants {env env' : VEnv} {name : Name} + {ci : VConstant} (hadd : env.addConst name ci = some env') (query : Name) : + env'.constants query = + if name = query then some ci else env.constants query := by + unfold VEnv.addConst at hadd + split at hadd <;> cases hadd + rfl + +private theorem structureView_nparams_eq_zero_of_nat + {env : VEnv} {view : VStructureView} (hview : view.WF env) + (hname : ``Nat = view.name) + (hNat : env.constants ``Nat = some natType.toVConstant) : + view.nparams = 0 := by + have hfamily := hview.family + rw [← hname, hNat] at hfamily + have hsourceType : view.generation.block.sourceType.type = natType.type := + congrArg VConstant.type (Option.some.inj hfamily).symm + have hshape := view.generation.shape_eq + simp only [VInductDecl.NormalizedChecked.generationShape, Bool.and_eq_true, + beq_iff_eq] at hshape + have hrawParamsLength := hshape.1.1.1.1.1 + have hNatType : natType.type = .sort (.succ .zero) := rfl + rw [VInductDecl.NormalizedChecked.rawParams, hsourceType, + hNatType] at hrawParamsLength + cases hnp : view.source.nparams with + | zero => simpa using hnp + | succ _ => + rw [hnp] at hrawParamsLength + simp [VExpr.telN] at hrawParamsLength + +private theorem natFinalEnv_structureView_nparams_eq_zero + {view : VStructureView} (hview : view.WF natFinalEnv) : + view.nparams = 0 := by + have hrec := hview.recursor + change natRecEnv.constants view.recursorName = + some view.generation.recursor at hrec + rw [addConst_constants + (show natCtorEnv.addConst ``Nat.rec + (VInductDecl.recConst 0 ``Nat 0 natType) = some natRecEnv from rfl), + addConst_constants + (show natZeroEnv.addConst natType.ctors[1].name + natType.ctors[1].toVConstant = some natCtorEnv from rfl), + addConst_constants + (show natTypeEnv.addConst natType.ctors[0].name + natType.ctors[0].toVConstant = some natZeroEnv from rfl), + addConst_constants + (show VEnv.empty.addConst natType.name natType.toVConstant = + some natTypeEnv from rfl)] at hrec + have hNatName : natType.name = ``Nat := rfl + have hZeroName : natType.ctors[0].name = ``Nat.zero := rfl + have hSuccName : natType.ctors[1].name = ``Nat.succ := rfl + rw [hNatName, hZeroName, hSuccName] at hrec + simp [VEnv.empty, VStructureView.recursorName] at hrec + exact structureView_nparams_eq_zero_of_nat hview hrec.1 + nat_type_env_lookup + +private theorem indexedVecTypeEnv_structureView_nparams_eq_zero + {view : VStructureView} (hview : view.WF indexedVecTypeEnv) : + view.nparams = 0 := by + have hrec := hview.recursor + rw [addConst_constants + (show natFinalEnv.addConst indexedVecType.name + indexedVecType.toVConstant = some indexedVecTypeEnv from rfl)] at hrec + change (if indexedVecType.name = view.recursorName then + some indexedVecType.toVConstant else + natRecEnv.constants view.recursorName) = + some view.generation.recursor at hrec + rw [addConst_constants + (show natCtorEnv.addConst ``Nat.rec + (VInductDecl.recConst 0 ``Nat 0 natType) = some natRecEnv from rfl), + addConst_constants + (show natZeroEnv.addConst natType.ctors[1].name + natType.ctors[1].toVConstant = some natCtorEnv from rfl), + addConst_constants + (show natTypeEnv.addConst natType.ctors[0].name + natType.ctors[0].toVConstant = some natZeroEnv from rfl), + addConst_constants + (show VEnv.empty.addConst natType.name natType.toVConstant = + some natTypeEnv from rfl)] at hrec + have hVecName : indexedVecType.name = ``IndexedVec := rfl + have hNatName : natType.name = ``Nat := rfl + have hZeroName : natType.ctors[0].name = ``Nat.zero := rfl + have hSuccName : natType.ctors[1].name = ``Nat.succ := rfl + rw [hVecName, hNatName, hZeroName, hSuccName] at hrec + simp [VEnv.empty, VStructureView.recursorName] at hrec + exact structureView_nparams_eq_zero_of_nat hview hrec.1 rfl + +private theorem natMap_constructor_numParams + {view : VStructureView} {info : ConstructorVal} + (hzero : view.nparams = 0) + (hfind : natMap.find? view.constructorName = some (.ctorInfo info)) : + info.numParams = view.nparams := by + rw [natMap, natCtorMap_wf.find?_insert] at hfind + split at hfind + · cases hfind + · rw [natCtorMap, natZeroMap_wf.find?_insert] at hfind + split at hfind + · simp [natSuccInfo] at hfind + cases hfind + exact hzero.symm + · rw [natZeroMap, natTypeMap_wf.find?_insert] at hfind + split at hfind + · simp [natZeroInfo] at hfind + cases hfind + exact hzero.symm + · rw [natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + at hfind + split at hfind + · cases hfind + · simp [SMap.find?] at hfind + +def indexedVecSemanticNatVEnvs : VEnvs where + venv _ := natFinalEnv + +theorem indexedVecSemanticNatVEnvsWF : indexedVecSemanticNatVEnvs.WF indexedVecKernelEnv where + tr := by + intro safety + change TrEnv' _ natMap false natFinalEnv + exact nat_trEnv' + hasPrimitives := indexedVecSemanticNatHasPrimitives + safePrimitives := indexedVecSemanticNatSafePrimitives + mono := fun _ => .rfl + projectionReady := { + infer := by + intro name _info _hfind hready + rw [indexedVecKernelEnv_noProjectionReady] at hready + contradiction + constructorNumParams := by + intro view info hview hfind + change natMap.find?' view.constructorName = + some (.ctorInfo info) at hfind + rw [natMap_wf.find?'_eq_find?] at hfind + exact natMap_constructor_numParams + (natFinalEnv_structureView_nparams_eq_zero hview) hfind } + structureEtaReady := StructureEtaReady.of_no_nonRecStructure + indexedVecKernelEnv_noStructureEta + +def indexedVecSemanticAddType : + AddInductConstant .induct natMap natFinalEnv + indexedVecType.toVConstVal indexedVecTypeMap indexedVecTypeEnv where + info := indexedVecInfo + kind_eq := by simp [indexedVecInfo, InductConstantKind.Matches] + tr := indexedVecInfo_tr + map_fresh := by simpa [indexedVecType] using indexedVecType_fresh + env_add := rfl + map_add := rfl + +theorem indexedVecSemanticFamilyPrefixNe : + indexedVecFamilyCandidateContext.ngen.namePrefix ≠ + (({} : TypeChecker.VState).ngen).namePrefix := by + decide + +def indexedVecSemanticFamilyContextRun : + TypeChecker.CandidateContextRun indexedVecFamilyCandidateContext := + TypeChecker.CandidateContextRun.root indexedVecSemanticNatVEnvsWF rfl + indexedVecSemanticFamilyPrefixNe + +theorem indexedVecSemanticFamilySourceTr : + TrExprS natFinalEnv [`u] [] indexedVecInfo.type indexedVecType.type := + indexedVecInfo_tr.1.2.2 + +def indexedVecPreFamilyStage : + TypeChecker.CandidateSemanticStage indexedVecFamilyCandidateContext + natFinalEnv [`u] where + contextRun := indexedVecSemanticFamilyContextRun + venv_eq := rfl + lparams_eq := rfl + vlctx_eq := rfl + +def indexedVecFamilyValidationRun : + AddInductive.CandidateExprTrace.FamilyValidationRun + indexedVecKernelType indexedVecFamilyCandidate.trace where + nparams := 1 + resultLevel := .succ (.param `u) + stats := indexedVecCandidateInductiveStats + stats_eq := rfl + terminal_eq := indexedVecFamilyCandidate_terminalResult + run := indexedVec_checkInductiveTypes + +def indexedVecFamilyStage : + VInductDecl.CandidateFamilyStagedInput + indexedVecFamilyCandidateContext ctorContext natFinalEnv [`u] + indexedVecFamilyListCandidate.familyType indexedVecType + indexedVecPreFamilyStage where + name_eq := rfl + uvars_eq := rfl + type := { + context_eq := rfl + source_tr := indexedVecSemanticFamilySourceTr + whnfFuel := 9999 + whnfDepth := rfl } + validation := indexedVecFamilyValidationRun + typeEnv := indexedVecTypeEnv + addInduct := indexedVecSemanticAddType + projectionReady := { + infer := by + intro name _info _hfind hready + rw [indexedVecTypeEnv_noProjectionReady] at hready + contradiction + constructorNumParams := by + intro view info hview hfind + change indexedVecTypeMap.find?' view.constructorName = + some (.ctorInfo info) at hfind + rw [indexedVecTypeMap_wf.find?'_eq_find?, indexedVecTypeMap, + natMap_wf.find?_insert] at hfind + split at hfind + · cases hfind + · exact natMap_constructor_numParams + (indexedVecTypeEnv_structureView_nparams_eq_zero hview) hfind } + structureEtaReady := StructureEtaReady.of_no_nonRecStructure + indexedVecTypeEnv_noStructureEta + family_lctx_eq := rfl + constructorContext_eq := rfl + quotInit_eq := rfl + name_not_reflected := by decide + name_not_primitive := by + simp [indexedVecType, Kernel.Environment.primitives, + NameSet.ofList] + simp +decide [NameSet.contains] + +def indexedVecSemanticCtorContextRun : + TypeChecker.CandidateContextRun ctorContext := + indexedVecFamilyStage.postContextRun + +theorem indexedVecSemanticNilSourceTr : + TrExprS indexedVecTypeEnv [`u] [] indexedVecNilInfo.type + indexedVecType.ctors[0].type := + indexedVecNilInfo_tr.1.2.2 + +theorem indexedVecSemanticConsIsType : + indexedVecTypeEnv.IsType 1 [] indexedVecType.ctors[1].type := by + have hwf := + (indexedVecChecked.wf_of_decl indexedVecDecl_wf).identityGeneration + nat_env_wf.ordered + have hctor := hwf.rawCtor_isType (envT := indexedVecTypeEnv) rfl + (ctor := indexedVecChecked.identityGeneration.block.ctorPairs[1]) + (by simp) + simpa only [ + show indexedVecDecl.uvars = 1 by rfl, + show indexedVecChecked.identityGeneration.block.ctorPairs[1].raw.type = + indexedVecType.ctors[1].type by rfl] using hctor + +theorem indexedVecSemanticConsSourceTr : + TrExprS indexedVecTypeEnv [`u] [] indexedVecConsInfo.type + indexedVecType.ctors[1].type := by + have hshape : TrTypeExpr indexedVecTypeEnv [`u] [] + indexedVecConsInfo.type indexedVecType.ctors[1].type := by + tr_type_expr_tac + obtain ⟨u, htype⟩ := indexedVecSemanticConsIsType + exact hshape.to_trExprS indexedVecTypeEnv_ordered trivial + ⟨.sort u, htype⟩ + +def indexedVecStagedUniverseInput : + VInductDecl.StagedNormalizationCandidateUniverseInput + indexedVecFamilyCandidateContext ctorContext natFinalEnv [`u] + indexedVecNormalizationCandidate indexedVecDecl where + staged := { + raw := indexedVecType + raw_types_eq := rfl + declaration_uvars_eq := rfl + preFamily := indexedVecPreFamilyStage + family := indexedVecFamilyStage + validation_nparams_eq := rfl + constructorValidation := + AddInductive.ConstructorValidationRun.of_run + indexedVecValidationCheckConstructors + constructors := .cons { + name_eq := rfl + uvars_eq := rfl + type := { + context_eq := rfl + source_tr := indexedVecSemanticNilSourceTr + whnfFuel := 9999 + whnfDepth := rfl } } (.cons { + name_eq := rfl + uvars_eq := rfl + type := { + context_eq := rfl + source_tr := indexedVecSemanticConsSourceTr + whnfFuel := 9999 + whnfDepth := rfl } } .nil) + familyTypesProduced := indexedVecFamilyTypeListProduced + familiesProduced := indexedVecFamilyListProduced } + universeRun := indexedVecValidationCheckConstructorUniverseSemantics + +/-- Generic automatic assembly joins the arbitrary-length operational list +witnesses to the complete retained semantic hierarchy for the two-constructor +fixture. No expected normalized view is an input to this theorem. -/ +theorem indexedVecProducedSemanticHierarchy_exists : + Nonempty (VInductDecl.ProducedNormalizationCandidateSemanticRun + indexedVecFamilyCandidateContext ctorContext natFinalEnv [`u] + indexedVecNormalizationCandidate indexedVecDecl) := + indexedVecStagedUniverseInput.exists + +/-- The automatically assembled hierarchy retains both constructor headers in +the producer's `nil`/`cons` source order. This inspects the semantic result, +not the separately constructed concrete replay below. -/ +theorem indexedVecProducedSemanticHierarchy_constructorHeaders : + ∃ run : VInductDecl.ProducedNormalizationCandidateSemanticRun + indexedVecFamilyCandidateContext ctorContext natFinalEnv [`u] + indexedVecNormalizationCandidate indexedVecDecl, + VInductDecl.sameCtorHeaders indexedVecType.ctors + run.semantic.family.root.constructors.views = true := by + obtain ⟨run⟩ := indexedVecProducedSemanticHierarchy_exists + have hraw : run.semantic.raw = indexedVecType := by + have htypes : [indexedVecType] = [run.semantic.raw] := by + simpa [indexedVecDecl] using run.semantic.raw_types_eq + injection htypes with h + exact h.symm + exact ⟨run, by + simpa only [hraw] using + run.semantic.family.root.constructors.sameHeaders⟩ + +/-! ## Post-family constructor alignment -/ + +private theorem indexedVecValidationSortCheckTypeM (lctx : LocalContext) : + TypeChecker.M.run ctorEnv .safe lctx [`u] ({} : FuelConfig) + (TypeChecker.checkType (.sort (.succ (.param `u)))) = + .ok (.sort (.succ (.succ (.param `u)))) := by + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' + (.sort (.succ (.param `u))) false + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + ({} : TypeChecker.State)) = _ + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', + checkLevelSuccParam, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + rfl + +private def indexedVecValidationZeroState (alphaId : FVarId) : + TypeChecker.State := + replayInsert (validationFirstAppState alphaId) + (.const ``Nat.zero []) (.const ``Nat []) + +private def indexedVecValidationZeroFinalState (alphaId : FVarId) : + TypeChecker.State := + replayInsert (indexedVecValidationZeroState alphaId) + (ctorIndexedVecApp (.fvar alphaId) (.const ``Nat.zero [])) + (.sort (.succ (.param `u))) + +private theorem indexedVecValidationZeroCheckTypeM + (lctx : LocalContext) (alphaId : FVarId) + (halpha : lctx.find? alphaId = some (.cdecl alphaIndex alphaId + alphaName (.sort (.succ (.param `u))) alphaBi alphaKind)) : + TypeChecker.M.run ctorEnv .safe lctx [`u] ({} : FuelConfig) + (TypeChecker.checkType + (ctorIndexedVecApp (.fvar alphaId) (.const ``Nat.zero []))) = + .ok (.sort (.succ (.param `u))) := by + have hfirst : + TypeChecker.Inner.inferType' (replayFirstApp (.fvar alphaId)) false + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + ({} : TypeChecker.State) = + .ok (vecFamilyTail, validationFirstAppState alphaId) := by + simpa [validationFirstAppState] using + (replayInferFirstAppFVarCore 9999 lctx + ({} : TypeChecker.State) alphaId + (by simp) + (by simp [replayInsert]) + (by simp [replayFirstApp]) halpha) + have hzero : + TypeChecker.Inner.inferType' (.const ``Nat.zero []) false + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + (validationFirstAppState alphaId) = + .ok (.const ``Nat [], indexedVecValidationZeroState alphaId) := by + simpa [indexedVecValidationZeroState, replayInsert] using + (inferTypeZeroCore 9999 lctx (validationFirstAppState alphaId) + (by simp [validationFirstAppState, replayInsert, + replayFirstApp])) + have hresult : + TypeChecker.Inner.inferType' + (ctorIndexedVecApp (.fvar alphaId) (.const ``Nat.zero [])) false + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + ({} : TypeChecker.State) = + .ok (.sort (.succ (.param `u)), + indexedVecValidationZeroFinalState alphaId) := by + simpa [indexedVecValidationZeroFinalState] using + (replayInferIndexedVecAppCore 9999 lctx + ({} : TypeChecker.State) (validationFirstAppState alphaId) + (indexedVecValidationZeroState alphaId) (.fvar alphaId) + (.const ``Nat.zero []) + (by simp [ctorIndexedVecApp, Expr.hasLooseBVars, + Expr.looseBVarRange']) + (by simp [ctorIndexedVecApp]) + hfirst hzero (by rfl)) + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' + (ctorIndexedVecApp (.fvar alphaId) (.const ``Nat.zero [])) false + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + ({} : TypeChecker.State)) = _ + rw [hresult] + rfl + +private def indexedVecValidationSuccState + (alphaId nId : FVarId) : TypeChecker.State := + replayInsert + (replayInsert + (replayInsert (validationFirstAppState alphaId) + (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default)) + (.fvar nId) (.const ``Nat [])) + (replaySuccApp (.fvar nId)) (.const ``Nat []) + +private def indexedVecValidationSuccFinalState + (alphaId nId : FVarId) : TypeChecker.State := + replayInsert (indexedVecValidationSuccState alphaId nId) + (ctorIndexedVecApp (.fvar alphaId) (replaySuccApp (.fvar nId))) + (.sort (.succ (.param `u))) + +private theorem indexedVecValidationSuccCheckTypeM + (lctx : LocalContext) (alphaId nId : FVarId) + (hne : alphaId ≠ nId) + (halpha : lctx.find? alphaId = some (.cdecl alphaIndex alphaId + alphaName (.sort (.succ (.param `u))) alphaBi alphaKind)) + (hn : lctx.find? nId = some (.cdecl nIndex nId nName + (.const ``Nat []) nBi nKind)) : + TypeChecker.M.run ctorEnv .safe lctx [`u] ({} : FuelConfig) + (TypeChecker.checkType + (ctorIndexedVecApp (.fvar alphaId) + (replaySuccApp (.fvar nId)))) = + .ok (.sort (.succ (.param `u))) := by + have hfirst : + TypeChecker.Inner.inferType' (replayFirstApp (.fvar alphaId)) false + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + ({} : TypeChecker.State) = + .ok (vecFamilyTail, validationFirstAppState alphaId) := by + simpa [validationFirstAppState] using + (replayInferFirstAppFVarCore 9999 lctx + ({} : TypeChecker.State) alphaId + (by simp) + (by simp [replayInsert]) + (by simp [replayFirstApp]) halpha) + have hsucc : + TypeChecker.Inner.inferType' (replaySuccApp (.fvar nId)) false + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + (validationFirstAppState alphaId) = + .ok (.const ``Nat [], indexedVecValidationSuccState alphaId nId) := by + simpa [indexedVecValidationSuccState] using + (replayInferSuccFVarCore 9999 lctx + (validationFirstAppState alphaId) nId + (by simp [validationFirstAppState, replayInsert, + replayFirstApp]) + (by + simp only [validationFirstAppState, replayInsert, + Std.HashMap.getElem?_insert] + rw [constBeqFVar] + rw [show (replayFirstApp (.fvar alphaId) == + (.fvar nId : Expr)) = false by + simp [replayFirstApp]] + rw [show ((.fvar alphaId : Expr) == .fvar nId) = false by + change Expr.eqv (.fvar alphaId) (.fvar nId) = false + rw [Expr.eqv_eq] + simp [Expr.eqv', hne]] + rw [constBeqFVar] + simp) + (by simp [validationFirstAppState, replayInsert, + replaySuccApp]) + hn) + have hresult : + TypeChecker.Inner.inferType' + (ctorIndexedVecApp (.fvar alphaId) + (replaySuccApp (.fvar nId))) false + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + ({} : TypeChecker.State) = + .ok (.sort (.succ (.param `u)), + indexedVecValidationSuccFinalState alphaId nId) := by + simpa [indexedVecValidationSuccFinalState] using + (replayInferIndexedVecAppCore 9999 lctx + ({} : TypeChecker.State) (validationFirstAppState alphaId) + (indexedVecValidationSuccState alphaId nId) (.fvar alphaId) + (replaySuccApp (.fvar nId)) + (by simp [ctorIndexedVecApp, replaySuccApp, + Expr.hasLooseBVars, Expr.looseBVarRange']) + (by simp [ctorIndexedVecApp, replaySuccApp]) + hfirst hsucc (by rfl)) + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' + (ctorIndexedVecApp (.fvar alphaId) + (replaySuccApp (.fvar nId))) false + (TypeChecker.Methods.withFuel 9999) (tcContext lctx) + ({} : TypeChecker.State)) = _ + rw [hresult] + rfl + +private theorem indexedVecValidationHeadContextFresh : + indexedVecValidationHeadContext.lctx.find? + indexedVecValidationHeadContext.freshFVarId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := indexedVecValidationHeadContext.freshFVarId) + indexedVecValidationHeadContextWF + rw [h] + simp only [indexedVecValidationHeadContext, + indexedVecValidationNContext, indexedVecCtorValidationContext, + indexedVecValidationTerminalContextShape, + indexedVecValidationFamilyContext, + indexedVecValidationParamContext, + indexedVecFamilyCandidateContext, + AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshFVarId] + rw [LocalContext.mkLocalDecl_toList, + LocalContext.mkLocalDecl_toList, + LocalContext.mkLocalDecl_toList, + LocalContext.mkLocalDecl_toList] + rw [show ({} : LocalContext).toList = [] by rfl] + simp +decide + +private theorem indexedVecValidationAlphaFindInTail : + indexedVecValidationTailContext.lctx.find? + indexedVecValidationAlphaId = + some (.cdecl 0 indexedVecValidationAlphaId + indexedVecValidationParamName + (.sort (.succ (.param `u))) .default .default) := by + have hfresh := indexedVecValidationHeadContextFresh + have hne : indexedVecValidationAlphaId ≠ + indexedVecValidationTailId := by + change indexedVecValidationAlphaId ≠ + indexedVecValidationHeadContext.freshFVarId + intro heq + rw [← heq] at hfresh + rw [indexedVecValidationAlphaFindInHead] at hfresh + contradiction + have h := localContextFindOld + (lctx := indexedVecValidationHeadContext.lctx) + (oldId := indexedVecValidationAlphaId) + (newId := indexedVecValidationTailId) + (newName := consTailName) + (newType := ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) + (newBi := .default) (newKind := .default) + (oldDecl := .cdecl 0 indexedVecValidationAlphaId + indexedVecValidationParamName (.sort (.succ (.param `u))) + .default .default) + indexedVecValidationHeadContextWF + hfresh hne + indexedVecValidationAlphaFindInHead + simpa [indexedVecValidationTailContext, + indexedVecValidationTailId, + AddInductive.Context.pushLocalDecl] using h + +private theorem indexedVecValidationNFindInTail : + indexedVecValidationTailContext.lctx.find? + indexedVecValidationNId = + some (.cdecl 2 indexedVecValidationNId consNName + (.const ``Nat []) .implicit .default) := by + have hfresh := indexedVecValidationHeadContextFresh + have hne : indexedVecValidationNId ≠ + indexedVecValidationTailId := by + change indexedVecValidationNId ≠ + indexedVecValidationHeadContext.freshFVarId + intro heq + rw [← heq] at hfresh + rw [indexedVecValidationNFindInHead] at hfresh + contradiction + have h := localContextFindOld + (lctx := indexedVecValidationHeadContext.lctx) + (oldId := indexedVecValidationNId) + (newId := indexedVecValidationTailId) + (newName := consTailName) + (newType := ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) + (newBi := .default) (newKind := .default) + (oldDecl := .cdecl 2 indexedVecValidationNId consNName + (.const ``Nat []) .implicit .default) + indexedVecValidationHeadContextWF + hfresh hne + indexedVecValidationNFindInHead + simpa [indexedVecValidationTailContext, + indexedVecValidationTailId, + AddInductive.Context.pushLocalDecl] using h + +private theorem indexedVecValidationSortCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨indexedVecCtorValidationContext, + .sort (.succ (.param `u)), + .sort (.succ (.succ (.param `u)))⟩ := by + change TypeChecker.M.run ctorEnv .safe + indexedVecCtorValidationContext.lctx [`u] ({} : FuelConfig) + (TypeChecker.checkType (.sort (.succ (.param `u)))) = _ + exact indexedVecValidationSortCheckTypeM + indexedVecCtorValidationContext.lctx + +private theorem indexedVecValidationNatCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨indexedVecCtorValidationContext, .const ``Nat [], + .sort (.succ .zero)⟩ := by + change TypeChecker.M.run ctorEnv .safe + indexedVecCtorValidationContext.lctx [`u] ({} : FuelConfig) + (TypeChecker.checkType (.const ``Nat [])) = _ + exact ctorNatCheckTypeM indexedVecCtorValidationContext.lctx + +private theorem indexedVecValidationAlphaCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨indexedVecValidationNContext, indexedVecValidationAlpha, + .sort (.succ (.param `u))⟩ := by + change TypeChecker.M.run ctorEnv .safe + indexedVecValidationNContext.lctx [`u] ({} : FuelConfig) + (TypeChecker.checkType (.fvar indexedVecValidationAlphaId)) = _ + exact ctorFVarCheckTypeM indexedVecValidationNContext.lctx + indexedVecValidationAlphaId (.sort (.succ (.param `u))) + indexedVecValidationAlphaFindInN + +private theorem indexedVecValidationTailCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨indexedVecValidationHeadContext, + ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr, + .sort (.succ (.param `u))⟩ := by + change TypeChecker.M.run ctorEnv .safe + indexedVecValidationHeadContext.lctx [`u] ({} : FuelConfig) + (TypeChecker.checkType + (ctorIndexedVecApp (.fvar indexedVecValidationAlphaId) + (.fvar indexedVecValidationNId))) = _ + exact ctorIndexedVecFVarCheckTypeM + indexedVecValidationHeadContext.lctx + indexedVecValidationAlphaId indexedVecValidationNId + indexedVecValidationAlphaNeN indexedVecValidationAlphaFindInHead + indexedVecValidationNFindInHead + +private theorem indexedVecValidationNilResultCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨indexedVecCtorValidationContext, indexedVecValidationNilResult, + .sort (.succ (.param `u))⟩ := by + change TypeChecker.M.run ctorEnv .safe + indexedVecCtorValidationContext.lctx [`u] ({} : FuelConfig) + (TypeChecker.checkType + (ctorIndexedVecApp (.fvar indexedVecValidationAlphaId) + (.const ``Nat.zero []))) = _ + exact indexedVecValidationZeroCheckTypeM + indexedVecCtorValidationContext.lctx indexedVecValidationAlphaId + indexedVecValidationAlphaFind + +private theorem indexedVecValidationConsResultCheckValid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨indexedVecValidationTailContext, indexedVecValidationConsResult, + .sort (.succ (.param `u))⟩ := by + change TypeChecker.M.run ctorEnv .safe + indexedVecValidationTailContext.lctx [`u] ({} : FuelConfig) + (TypeChecker.checkType + (ctorIndexedVecApp (.fvar indexedVecValidationAlphaId) + (replaySuccApp (.fvar indexedVecValidationNId)))) = _ + exact indexedVecValidationSuccCheckTypeM + indexedVecValidationTailContext.lctx + indexedVecValidationAlphaId indexedVecValidationNId + indexedVecValidationAlphaNeN + indexedVecValidationAlphaFindInTail + indexedVecValidationNFindInTail + +private def indexedVecCheckedOfValid + (context : AddInductive.Context) (source inferred : Expr) + (fvars : source.FVarsIn + (fun fv => (context.lctx.find? fv).isSome = true)) + (valid : AddInductive.CandidateCheckTypeStep.Valid + ⟨context, source, inferred⟩) : + AddInductive.ConstructorCheckedExpr context source := + .ofRun fvars valid + +private def indexedVecValidationSortChecked : + AddInductive.ConstructorCheckedExpr indexedVecCtorValidationContext + (.sort (.succ (.param `u))) := + indexedVecCheckedOfValid _ _ _ (by + simp [FVarsIn, Level.hasMVar']) + indexedVecValidationSortCheckValid + +private def indexedVecValidationNatChecked : + AddInductive.ConstructorCheckedExpr indexedVecCtorValidationContext + (.const ``Nat []) := + indexedVecCheckedOfValid _ _ _ (by simp [FVarsIn]) + indexedVecValidationNatCheckValid + +private def indexedVecValidationAlphaChecked : + AddInductive.ConstructorCheckedExpr indexedVecValidationNContext + indexedVecValidationAlpha := + indexedVecCheckedOfValid _ _ _ (by + change (indexedVecValidationNContext.lctx.find? + indexedVecValidationAlphaId).isSome = true + rw [indexedVecValidationAlphaFindInN] + rfl) + indexedVecValidationAlphaCheckValid + +private def indexedVecValidationTailChecked : + AddInductive.ConstructorCheckedExpr indexedVecValidationHeadContext + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) := + indexedVecCheckedOfValid _ _ _ (by + simp [ctorIndexedVecApp, indexedVecValidationAlpha, + indexedVecValidationNExpr, AddInductive.Context.freshExpr, + FVarsIn, Level.hasMVar'] + constructor + · change (indexedVecValidationHeadContext.lctx.find? + indexedVecValidationAlphaId).isSome = true + rw [indexedVecValidationAlphaFindInHead] + rfl + · change (indexedVecValidationHeadContext.lctx.find? + indexedVecValidationNId).isSome = true + rw [indexedVecValidationNFindInHead] + rfl) + indexedVecValidationTailCheckValid + +private def indexedVecValidationNilResultChecked : + AddInductive.ConstructorCheckedExpr indexedVecCtorValidationContext + indexedVecValidationNilResult := + indexedVecCheckedOfValid _ _ _ (by + simp [indexedVecValidationNilResult, ctorIndexedVecApp, + indexedVecValidationAlpha, + AddInductive.Context.freshExpr, FVarsIn, Level.hasMVar'] + change (indexedVecCtorValidationContext.lctx.find? + indexedVecValidationAlphaId).isSome = true + rw [indexedVecValidationAlphaFind] + rfl) + indexedVecValidationNilResultCheckValid + +private def indexedVecValidationConsResultChecked : + AddInductive.ConstructorCheckedExpr indexedVecValidationTailContext + indexedVecValidationConsResult := + indexedVecCheckedOfValid _ _ _ (by + simp [indexedVecValidationConsResult, ctorIndexedVecApp, + replaySuccApp, indexedVecValidationAlpha, + indexedVecValidationNExpr, AddInductive.Context.freshExpr, + FVarsIn, Level.hasMVar'] + constructor + · change (indexedVecValidationTailContext.lctx.find? + indexedVecValidationAlphaId).isSome = true + rw [indexedVecValidationAlphaFindInTail] + rfl + · change (indexedVecValidationTailContext.lctx.find? + indexedVecValidationNId).isSome = true + rw [indexedVecValidationNFindInTail] + rfl) + indexedVecValidationConsResultCheckValid + +private theorem indexedVecStagedStats_eq : + indexedVecStagedUniverseInput.staged.family.validation.stats = + indexedVecCandidateInductiveStats := rfl + +private theorem indexedVecValidationPostContext_eq : + { indexedVecNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext with + env := ctorContext.env } = indexedVecCtorValidationContext := rfl + +private def indexedVecTransportValidationTrace + {context context' : AddInductive.Context} + (context_eq : context = context') + {source source' : Expr} (source_eq : source = source') + (trace : AddInductive.ConstructorTypeValidationTrace stats isUnsafe + familyIdx ctor context source argIdx fuel) : + AddInductive.ConstructorTypeValidationTrace stats isUnsafe familyIdx ctor + context' source' argIdx fuel := by + subst context' + subst source' + exact trace + +private def indexedVecTransportViewAlignment + {context context' : AddInductive.Context} + (context_eq : context = context') + {source source' view view' : Expr} + (source_eq : source = source') (view_eq : view = view') + (trace : AddInductive.ConstructorTypeValidationTrace stats isUnsafe + familyIdx ctor context source argIdx fuel) + (alignment : AddInductive.ConstructorViewAlignmentTrace + (indexedVecTransportValidationTrace context_eq source_eq trace) + view') : + AddInductive.ConstructorViewAlignmentTrace trace view := by + subst context' + subst source' + subst view' + exact alignment + +@[simp] private theorem indexedVecTransportValidationTrace_spineLength + {context context' : AddInductive.Context} + (context_eq : context = context') + {source source' : Expr} (source_eq : source = source') + (trace : AddInductive.ConstructorTypeValidationTrace stats isUnsafe + familyIdx ctor context source argIdx fuel) : + (indexedVecTransportValidationTrace context_eq source_eq trace).spineLength = + trace.spineLength := by + subst context' + subst source' + rfl + +private theorem indexedVecValidationNatWhnfSelf : + AddInductive.CandidateWhnfStep.Valid + ⟨indexedVecCtorValidationContext, .const ``Nat [], + .const ``Nat []⟩ := by + change TypeChecker.M.run ctorEnv .safe + indexedVecCtorValidationContext.lctx [`u] ({} : FuelConfig) + (TypeChecker.whnf (.const ``Nat [])) = .ok (.const ``Nat []) + exact ctorNatWhnfM indexedVecCtorValidationContext.lctx + +private theorem indexedVecValidationAlphaWhnfSelf : + AddInductive.CandidateWhnfStep.Valid + ⟨indexedVecValidationNContext, indexedVecValidationAlpha, + indexedVecValidationAlpha⟩ := by + change TypeChecker.M.run ctorEnv .safe + indexedVecValidationNContext.lctx [`u] ({} : FuelConfig) + (TypeChecker.whnf (.fvar indexedVecValidationAlphaId)) = + .ok (.fvar indexedVecValidationAlphaId) + exact ctorFVarWhnfM indexedVecValidationNContext.lctx + indexedVecValidationAlphaId indexedVecValidationAlphaFindInN + +private theorem indexedVecValidationTailWhnfSelf : + AddInductive.CandidateWhnfStep.Valid + ⟨indexedVecValidationHeadContext, + ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr, + ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr⟩ := by + change TypeChecker.M.run ctorEnv .safe + indexedVecValidationHeadContext.lctx [`u] ({} : FuelConfig) + (TypeChecker.whnf + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr)) = + .ok (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) + exact ctorIndexedVecWhnfM indexedVecValidationHeadContext.lctx + indexedVecValidationAlpha indexedVecValidationNExpr + +private theorem indexedVecCandidateWhnfResult_eq + (self : AddInductive.CandidateWhnfStep.Valid + ⟨context, source, source⟩) + (other : AddInductive.CandidateWhnfStep.Valid + ⟨context, source, result⟩) : + result = source := by + unfold AddInductive.CandidateWhnfStep.Valid at self other + rw [self] at other + exact (Except.ok.inj other).symm + +private def indexedVecValidationNatPositivityAlignment + (trace : AddInductive.ConstructorPositivityModeTrace + indexedVecStagedUniverseInput.staged.family.validation.stats false + indexedVecKernelCons.name 1 indexedVecCtorValidationContext + (.const ``Nat [])) : + AddInductive.ConstructorPositivityModeAlignmentTrace trace := by + cases trace with + | skipped unsafeEq => contradiction + | safe unsafeEq positivityTrace => + apply AddInductive.ConstructorPositivityModeAlignmentTrace.safe + cases positivityTrace with + | absent context source result fuel whnf occurs => + have result_eq := indexedVecCandidateWhnfResult_eq + indexedVecValidationNatWhnfSelf whnf + subst result + exact .absent indexedVecValidationNatChecked + | forallE context source fuel name domain body binderInfo whnf occurs + domainFree tail => + have result_eq := indexedVecCandidateWhnfResult_eq + indexedVecValidationNatWhnfSelf whnf + have impossible := congrArg Expr.isForall result_eq + simp [Expr.isForall] at impossible + | target context source result fuel targetIdx whnf occurs terminal valid => + have result_eq := indexedVecCandidateWhnfResult_eq + indexedVecValidationNatWhnfSelf whnf + subst result + rw [indexedVecStagedStats_eq, + indexedVecValidationNatHasNoIndOcc] at occurs + contradiction + +private def indexedVecValidationAlphaPositivityAlignment + (trace : AddInductive.ConstructorPositivityModeTrace + indexedVecStagedUniverseInput.staged.family.validation.stats false + indexedVecKernelCons.name 2 indexedVecValidationNContext + indexedVecValidationAlpha) : + AddInductive.ConstructorPositivityModeAlignmentTrace trace := by + cases trace with + | skipped unsafeEq => contradiction + | safe unsafeEq positivityTrace => + apply AddInductive.ConstructorPositivityModeAlignmentTrace.safe + cases positivityTrace with + | absent context source result fuel whnf occurs => + have result_eq := indexedVecCandidateWhnfResult_eq + indexedVecValidationAlphaWhnfSelf whnf + subst result + exact .absent indexedVecValidationAlphaChecked + | forallE context source fuel name domain body binderInfo whnf occurs + domainFree tail => + have result_eq := indexedVecCandidateWhnfResult_eq + indexedVecValidationAlphaWhnfSelf whnf + have impossible := congrArg Expr.isForall result_eq + simp [indexedVecValidationAlphaShape, Expr.isForall] at impossible + | target context source result fuel targetIdx whnf occurs terminal valid => + have result_eq := indexedVecCandidateWhnfResult_eq + indexedVecValidationAlphaWhnfSelf whnf + subst result + rw [indexedVecStagedStats_eq] at occurs + change AddInductive.hasIndOcc + indexedVecCandidateInductiveStats.indConsts + indexedVecValidationAlpha = true at occurs + rw [indexedVecValidationAlphaHasNoIndOcc] at occurs + contradiction + +private def indexedVecValidationTailPositivityAlignment + (trace : AddInductive.ConstructorPositivityModeTrace + indexedVecStagedUniverseInput.staged.family.validation.stats false + indexedVecKernelCons.name 3 indexedVecValidationHeadContext + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr)) : + AddInductive.ConstructorPositivityModeAlignmentTrace trace := by + cases trace with + | skipped unsafeEq => contradiction + | safe unsafeEq positivityTrace => + apply AddInductive.ConstructorPositivityModeAlignmentTrace.safe + cases positivityTrace with + | absent context source result fuel whnf occurs => + have result_eq := indexedVecCandidateWhnfResult_eq + indexedVecValidationTailWhnfSelf whnf + subst result + rw [indexedVecStagedStats_eq] at occurs + change AddInductive.hasIndOcc + indexedVecCandidateInductiveStats.indConsts + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) = false at occurs + rw [indexedVecValidationTailHasIndOcc] at occurs + contradiction + | forallE context source fuel name domain body binderInfo whnf occurs + domainFree tail => + have result_eq := indexedVecCandidateWhnfResult_eq + indexedVecValidationTailWhnfSelf whnf + have impossible := congrArg Expr.isForall result_eq + simp [ctorIndexedVecApp, Expr.isForall] at impossible + | target context source result fuel targetIdx whnf occurs terminal valid => + have result_eq := indexedVecCandidateWhnfResult_eq + indexedVecValidationTailWhnfSelf whnf + subst result + exact .target indexedVecValidationTailChecked + +private def indexedVecValidationNatAnnotations : + AddInductive.CandidateIsDefEqObservation + indexedVecCtorValidationContext (.const ``Nat []) + (.const ``Nat []) := + ⟨AddInductive.candidateIsDefEqRefl indexedVecCtorValidationContext + (.const ``Nat [])⟩ + +private def indexedVecValidationAlphaAnnotations : + AddInductive.CandidateIsDefEqObservation + indexedVecValidationNContext indexedVecValidationAlpha + indexedVecValidationAlpha := + ⟨AddInductive.candidateIsDefEqRefl indexedVecValidationNContext + indexedVecValidationAlpha⟩ + +private def indexedVecValidationTailAnnotations : + AddInductive.CandidateIsDefEqObservation + indexedVecValidationHeadContext + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) := + ⟨AddInductive.candidateIsDefEqRefl indexedVecValidationHeadContext + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr)⟩ + +/-- The validator and analyzer intentionally allocate the retained `n` field +under different fresh-FVar histories. D2 alignment must therefore be +positional rather than identifier-based. -/ +theorem indexedVecValidationCandidateFieldFVars_ne : + indexedVecValidationNId ≠ consNId := by + simp [indexedVecValidationNId, indexedVecCtorValidationContext, + indexedVecValidationTerminalContextShape, + indexedVecValidationFamilyContext, + indexedVecValidationParamContext, indexedVecFamilyCandidateContext, + consNId, consAlphaContext, consRootContext, ctorContext, + AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshFVarId, + NameGenerator.next, NameGenerator.curr] + +/-- Exact D2 owner for `IndexedVec`. Its validator telescope is transported +only across proved context/source equalities, while every candidate view is +instantiated with the validator-owned locals at the same de Bruijn position. -/ +def indexedVecStagedPostFamilyInput : + VInductDecl.StagedNormalizationCandidatePostFamilyInput + indexedVecFamilyCandidateContext ctorContext natFinalEnv [`u] + indexedVecNormalizationCandidate indexedVecDecl where + universeInput := indexedVecStagedUniverseInput + alignment := by + change AddInductive.ConstructorCandidateAlignmentTrace + indexedVecStagedUniverseInput.staged.family.validation.stats false 0 + indexedVecCtorValidationContext + indexedVecStagedUniverseInput.staged.constructorValidation.trace + (.cons indexedVecNilConstructorCandidate + (.cons indexedVecConsConstructorCandidate .nil)) + generalize htrace : + indexedVecStagedUniverseInput.staged.constructorValidation.trace = trace + cases trace with + | cons seen head constructors fresh closed rootCheck typeTrace tailTrace => + clear htrace + cases hnilRoot : typeTrace with + | parameter context fuel argIdx name domain body binderInfo param + parameterType parameterAt parameterTypeRun parameterDefEq + nilTypeTail => + rw [indexedVecStagedStats_eq, + indexedVecValidationStatsParams] at parameterAt + simp at parameterAt + subst param + change AddInductive.getType indexedVecValidationAlpha + indexedVecCtorValidationContext = .ok parameterType at parameterTypeRun + rw [indexedVecValidationGetTypeAlpha] at parameterTypeRun + injection parameterTypeRun with parameterType_eq + subst parameterType + let nilNormalized := indexedVecTransportValidationTrace + indexedVecValidationPostContext_eq + indexedVecValidationNilResultShape nilTypeTail + let nilNormalizedTrace := nilNormalized + have nilSpine : nilTypeTail.spineLength = + nilNormalizedTrace.spineLength := by + exact (indexedVecTransportValidationTrace_spineLength + indexedVecValidationPostContext_eq + indexedVecValidationNilResultShape nilTypeTail).symm + cases hnilNormalized : nilNormalizedTrace with + | terminal context source fuel argIdx nilTerminal nilValid => + simp [hnilNormalized, + AddInductive.ConstructorTypeValidationTrace.spineLength] at nilSpine + have nilNormalizedAlignment : + AddInductive.ConstructorViewAlignmentTrace + nilNormalizedTrace indexedVecValidationNilResult := by + rw [hnilNormalized] + exact .terminal indexedVecValidationNilResultChecked + indexedVecValidationNilResultChecked nilTerminal nilValid + have nilTailAlignment := indexedVecTransportViewAlignment + indexedVecValidationPostContext_eq + indexedVecValidationNilResultShape + indexedVecValidationNilResultShape nilTypeTail + nilNormalizedAlignment + have nilHeadAlignment : + AddInductive.ConstructorViewAlignmentTrace + (.parameter + { indexedVecNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext with + env := ctorContext.env } + 999 0 `α (.sort (.succ (.param `u))) + nilCtorBodyRaw .implicit indexedVecValidationAlpha + (.sort (.succ (.param `u))) parameterAt + parameterTypeRun parameterDefEq nilTypeTail) + indexedVecNilConstructorCandidate.type.view := by + change AddInductive.ConstructorViewAlignmentTrace + _ nilCandidate.view + rw [nilCandidate_view_eq] + exact .parameter indexedVecValidationSortChecked + indexedVecValidationSortChecked + indexedVecValidationSortChecked rfl + (by simp [indexedVecValidationAlphaFind]) + nilTypeTail nilTailAlignment + cases tailTrace with + | cons seen head constructors consFresh consClosed + consRootCheck consTypeTrace finalTrace => + cases hconsRoot : consTypeTrace with + | parameter context fuel argIdx name domain body binderInfo + param parameterType parameterAt parameterTypeRun + parameterDefEq consAfterParamTrace => + rw [indexedVecStagedStats_eq, + indexedVecValidationStatsParams] at parameterAt + simp at parameterAt + subst param + change AddInductive.getType indexedVecValidationAlpha + indexedVecCtorValidationContext = .ok parameterType at parameterTypeRun + rw [indexedVecValidationGetTypeAlpha] at parameterTypeRun + injection parameterTypeRun with parameterType_eq + subst parameterType + let buildConsHeadAlignment + (tailAlignment : + AddInductive.ConstructorViewAlignmentTrace + consAfterParamTrace + (consNTypeRaw.instantiate1 + indexedVecValidationAlpha)) : + AddInductive.ConstructorViewAlignmentTrace + (.parameter + { indexedVecNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext with + env := ctorContext.env } + 999 0 consAlphaName + (.sort (.succ (.param `u))) consNTypeRaw + .implicit indexedVecValidationAlpha + (.sort (.succ (.param `u))) parameterAt + parameterTypeRun parameterDefEq + consAfterParamTrace) + indexedVecConsConstructorCandidate.type.view := by + change AddInductive.ConstructorViewAlignmentTrace + _ consCandidate.view + rw [consCandidate_view_eq] + exact .parameter indexedVecValidationSortChecked + indexedVecValidationSortChecked + indexedVecValidationSortChecked rfl + (by simp [indexedVecValidationAlphaFind]) + consAfterParamTrace tailAlignment + have consAfterParamSource_eq : + indexedVecConsInfo.type.bindingBody!.instantiate1 + indexedVecValidationAlpha = + indexedVecValidationConsAfterParam := + indexedVecValidationConsAfterParamShape + have consAfterParamExplicit_eq : + indexedVecConsInfo.type.bindingBody!.instantiate1 + indexedVecValidationAlpha = + .forallE consNName (.const ``Nat []) + (.forallE consHeadName + indexedVecValidationAlpha + (.forallE consTailName + (ctorIndexedVecApp + indexedVecValidationAlpha (.bvar 1)) + (ctorIndexedVecApp + indexedVecValidationAlpha + (replaySuccApp (.bvar 2))) .default) + .default) + .implicit := + consAfterParamSource_eq.trans + indexedVecValidationConsAfterParamExplicitShape + let consAfterParamNormalized := + indexedVecTransportValidationTrace + indexedVecValidationPostContext_eq + consAfterParamExplicit_eq consAfterParamTrace + let consAfterParamNormalizedTrace := + consAfterParamNormalized + have consAfterParamSpine : + consAfterParamTrace.spineLength = + consAfterParamNormalizedTrace.spineLength := by + exact (indexedVecTransportValidationTrace_spineLength + indexedVecValidationPostContext_eq + consAfterParamExplicit_eq + consAfterParamTrace).symm + cases hconsAfterParamNormalized : + consAfterParamNormalizedTrace with + | parameter context fuel argIdx name domain body + binderInfo param parameterType parameterAt + parameterTypeRun parameterDefEq tail => + rw [indexedVecStagedStats_eq, + indexedVecValidationStatsParams] at parameterAt + simp at parameterAt + | ordinary context fuel argIdx name domain body + binderInfo sortResult noParameter ensureType + universeTrace natPositivity consAfterNTrace => + simp [hconsAfterParamNormalized, + AddInductive.ConstructorTypeValidationTrace.spineLength] + at consAfterParamSpine + have natPositivityAlignment := + indexedVecValidationNatPositivityAlignment + natPositivity + have afterParamBody_eq : + indexedVecValidationConsAfterParam.bindingBody! = + .forallE consHeadName + indexedVecValidationAlpha + (.forallE consTailName + (ctorIndexedVecApp + indexedVecValidationAlpha (.bvar 1)) + (ctorIndexedVecApp + indexedVecValidationAlpha + (replaySuccApp (.bvar 2))) .default) + .default := by + exact congrArg Expr.bindingBody! + indexedVecValidationConsAfterParamExplicitShape + have afterNSource_eq : + ((Expr.forallE consHeadName + indexedVecValidationAlpha + (Expr.forallE consTailName + (ctorIndexedVecApp + indexedVecValidationAlpha (.bvar 1)) + (ctorIndexedVecApp + indexedVecValidationAlpha + (replaySuccApp (.bvar 2))) .default) + .default).instantiate1 + indexedVecCtorValidationContext.freshExpr) = + indexedVecValidationConsAfterN := by + change + ((Expr.forallE consHeadName + indexedVecValidationAlpha + (Expr.forallE consTailName + (ctorIndexedVecApp + indexedVecValidationAlpha (.bvar 1)) + (ctorIndexedVecApp + indexedVecValidationAlpha + (replaySuccApp (.bvar 2))) .default) + .default).instantiate1 + indexedVecValidationNExpr) = + indexedVecValidationConsAfterN + rw [← afterParamBody_eq] + exact indexedVecValidationConsAfterNShape + have nContext_eq : + indexedVecCtorValidationContext.pushLocalDecl + consNName .implicit + (AddInductive.consumeTypeAnnotations + (.const ``Nat [])) = + indexedVecValidationNContext := by + simp [indexedVecValidationNContext] + let consAfterNNormalized := + indexedVecTransportValidationTrace nContext_eq + afterNSource_eq consAfterNTrace + let consAfterNNormalizedTrace := consAfterNNormalized + have consAfterNSpine : + consAfterNTrace.spineLength = + consAfterNNormalizedTrace.spineLength := by + exact + (indexedVecTransportValidationTrace_spineLength + nContext_eq afterNSource_eq + consAfterNTrace).symm + cases hconsAfterNNormalized : + consAfterNNormalizedTrace with + | parameter context fuel argIdx name domain body + binderInfo param parameterType parameterAt + parameterTypeRun parameterDefEq tail => + rw [indexedVecStagedStats_eq, + indexedVecValidationStatsParams] at parameterAt + simp at parameterAt + | ordinary context fuel argIdx name domain body + binderInfo sortResult noParameter ensureType + universeTrace alphaPositivity + consAfterHeadTrace => + simp [hconsAfterNNormalized, + AddInductive.ConstructorTypeValidationTrace.spineLength] + at consAfterNSpine + have alphaPositivityAlignment := + indexedVecValidationAlphaPositivityAlignment + alphaPositivity + have headContext_eq : + indexedVecValidationNContext.pushLocalDecl + consHeadName .default + (AddInductive.consumeTypeAnnotations + indexedVecValidationAlpha) = + indexedVecValidationHeadContext := by + rw [indexedVecValidationConsumeAlpha] + rfl + let consAfterHeadNormalized := + indexedVecTransportValidationTrace + headContext_eq + indexedVecValidationConsAfterHeadShape + consAfterHeadTrace + let consAfterHeadNormalizedTrace := + consAfterHeadNormalized + have consAfterHeadSpine : + consAfterHeadTrace.spineLength = + consAfterHeadNormalizedTrace.spineLength := by + exact + (indexedVecTransportValidationTrace_spineLength + headContext_eq + indexedVecValidationConsAfterHeadShape + consAfterHeadTrace).symm + cases hconsAfterHeadNormalized : + consAfterHeadNormalizedTrace with + | parameter context fuel argIdx name domain body + binderInfo param parameterType parameterAt + parameterTypeRun parameterDefEq tail => + rw [indexedVecStagedStats_eq, + indexedVecValidationStatsParams] at parameterAt + simp at parameterAt + | ordinary context fuel argIdx name domain body + binderInfo sortResult noParameter ensureType + universeTrace tailPositivity + consResultTrace => + simp [hconsAfterHeadNormalized, + AddInductive.ConstructorTypeValidationTrace.spineLength] + at consAfterHeadSpine + have tailPositivityAlignment := + indexedVecValidationTailPositivityAlignment + tailPositivity + have tailContext_eq : + indexedVecValidationHeadContext.pushLocalDecl + consTailName .default + (AddInductive.consumeTypeAnnotations + (ctorIndexedVecApp + indexedVecValidationAlpha + indexedVecValidationNExpr)) = + indexedVecValidationTailContext := by + rw [indexedVecValidationConsumeTail] + rfl + let consResultNormalized := + indexedVecTransportValidationTrace + tailContext_eq + indexedVecValidationConsResultShape + consResultTrace + let consResultNormalizedTrace := + consResultNormalized + have consResultSpine : + consResultTrace.spineLength = + consResultNormalizedTrace.spineLength := by + exact + (indexedVecTransportValidationTrace_spineLength + tailContext_eq + indexedVecValidationConsResultShape + consResultTrace).symm + cases hconsResultNormalized : + consResultNormalizedTrace with + | terminal context source fuel argIdx + resultTerminal resultValid => + simp [hconsResultNormalized, + AddInductive.ConstructorTypeValidationTrace.spineLength] + at consResultSpine + have consResultNormalizedAlignment : + AddInductive.ConstructorViewAlignmentTrace + consResultNormalizedTrace + indexedVecValidationConsResult := by + rw [hconsResultNormalized] + exact .terminal + indexedVecValidationConsResultChecked + indexedVecValidationConsResultChecked + resultTerminal resultValid + have consResultAlignment := + indexedVecTransportViewAlignment + tailContext_eq + indexedVecValidationConsResultShape + indexedVecValidationConsResultShape + consResultTrace + consResultNormalizedAlignment + have consAfterHeadNormalizedAlignment : + AddInductive.ConstructorViewAlignmentTrace + consAfterHeadNormalizedTrace + indexedVecValidationConsAfterHead := by + rw [hconsAfterHeadNormalized] + exact .ordinary + indexedVecValidationTailChecked + indexedVecValidationTailChecked + (by simpa only + [indexedVecValidationConsumeTail] + using + indexedVecValidationTailAnnotations) + (by simpa only + [indexedVecValidationConsumeTail] + using indexedVecValidationTailChecked) + tailPositivity + tailPositivityAlignment + indexedVecValidationHeadContextFresh + (by simpa only + [indexedVecValidationConsumeTail] + using + indexedVecValidationTailAnnotations) + consResultTrace consResultAlignment + have consAfterHeadAlignment := + indexedVecTransportViewAlignment + headContext_eq + indexedVecValidationConsAfterHeadShape + indexedVecValidationConsAfterHeadShape + consAfterHeadTrace + consAfterHeadNormalizedAlignment + have consAfterNNormalizedAlignment : + AddInductive.ConstructorViewAlignmentTrace + consAfterNNormalizedTrace + indexedVecValidationConsAfterN := by + rw [hconsAfterNNormalized] + exact .ordinary + indexedVecValidationAlphaChecked + indexedVecValidationAlphaChecked + (by simpa only + [indexedVecValidationConsumeAlpha] + using + indexedVecValidationAlphaAnnotations) + (by simpa only + [indexedVecValidationConsumeAlpha] + using indexedVecValidationAlphaChecked) + alphaPositivity + alphaPositivityAlignment + indexedVecValidationNContextFresh + (by simpa only + [indexedVecValidationConsumeAlpha] + using + indexedVecValidationAlphaAnnotations) + consAfterHeadTrace + consAfterHeadAlignment + have consAfterNAlignment := + indexedVecTransportViewAlignment + nContext_eq afterNSource_eq + afterNSource_eq consAfterNTrace + consAfterNNormalizedAlignment + have consAfterParamNormalizedAlignment : + AddInductive.ConstructorViewAlignmentTrace + consAfterParamNormalizedTrace + (.forallE consNName + (.const ``Nat []) + (.forallE consHeadName + indexedVecValidationAlpha + (.forallE consTailName + (ctorIndexedVecApp + indexedVecValidationAlpha + (.bvar 1)) + (ctorIndexedVecApp + indexedVecValidationAlpha + (replaySuccApp (.bvar 2))) + .default) + .default) + .implicit) := by + rw [hconsAfterParamNormalized] + exact .ordinary + indexedVecValidationNatChecked + indexedVecValidationNatChecked + (by simpa only + [indexedVecValidationConsumeNat] + using + indexedVecValidationNatAnnotations) + (by simpa only + [indexedVecValidationConsumeNat] + using indexedVecValidationNatChecked) + natPositivity natPositivityAlignment + indexedVecCtorValidationContextFresh + (by simpa only + [indexedVecValidationConsumeNat] + using + indexedVecValidationNatAnnotations) + consAfterNTrace consAfterNAlignment + have consAfterParamAlignment := + indexedVecTransportViewAlignment + indexedVecValidationPostContext_eq + consAfterParamExplicit_eq + consAfterParamExplicit_eq + consAfterParamTrace + consAfterParamNormalizedAlignment + have consHeadAlignment := + buildConsHeadAlignment + consAfterParamAlignment + change + AddInductive.CandidateCheckTypeObservation + indexedVecCtorValidationContext.withEmptyLocalContext + indexedVecKernelNil.type at rootCheck + change + AddInductive.CandidateCheckTypeObservation + indexedVecCtorValidationContext.withEmptyLocalContext + indexedVecKernelCons.type at consRootCheck + let nilRootScope : + AddInductive.ConstructorCheckedExpr + indexedVecCtorValidationContext.withEmptyLocalContext + indexedVecKernelNil.type := + AddInductive.ConstructorCheckedExpr.ofClosedRoot + closed rootCheck + let consRootScope : + AddInductive.ConstructorCheckedExpr + indexedVecCtorValidationContext.withEmptyLocalContext + indexedVecKernelCons.type := + AddInductive.ConstructorCheckedExpr.ofClosedRoot + consClosed consRootCheck + cases finalTrace with + | nil finalSeen => + exact + AddInductive.ConstructorCandidateAlignmentTrace.cons + nilRootScope + (by + change nilCandidate.trace.storedSpine = true + exact nilCandidate_identity.storedSpine) + (by + change 1 = + nilTypeTail.spineLength + 1 + omega) + (by rfl) nilHeadAlignment <| + AddInductive.ConstructorCandidateAlignmentTrace.cons + consRootScope + (by + change consCandidate.trace.storedSpine = true + exact consCandidate_identity.storedSpine) + (by + change 4 = + consAfterParamTrace.spineLength + 1 + omega) + (by rfl) consHeadAlignment <| + AddInductive.ConstructorCandidateAlignmentTrace.nil + ((∅ : NameSet).insert + indexedVecKernelNil.name |>.insert + indexedVecKernelCons.name) + | terminal context source fuel argIdx terminal + valid => + simp [indexedVecValidationConsAfterHead, + Expr.isForall] at terminal + | terminal context source fuel argIdx terminal valid => + simp [indexedVecValidationConsAfterN, + Expr.isForall] at terminal + | terminal context source fuel argIdx terminal valid => + simp [Expr.isForall] at terminal + | ordinary context fuel argIdx name domain body binderInfo + sortResult noParameter ensureType universeTrace positivity + tail => + rw [indexedVecStagedStats_eq, + indexedVecValidationStatsParams] at noParameter + simp at noParameter + | terminal context source fuel argIdx terminal valid => + have consType_eq : indexedVecKernelCons.type = + consCtorTypeRaw := by + simpa [indexedVecKernelCons] using consInfoTypeShape + rw [consType_eq] at terminal + simp [consCtorTypeRaw, Expr.isForall] at terminal + | ordinary context fuel argIdx name domain body binderInfo sortResult + noParameter ensureType universeTrace positivity nilTypeTail => + rw [indexedVecStagedStats_eq, + indexedVecValidationStatsParams] at noParameter + simp at noParameter + | terminal context source fuel argIdx terminal valid => + simp [indexedVecKernelNil, indexedVecNilInfo, + ConstantInfo.type, ConstantInfo.toConstantVal, + Expr.isForall] at terminal + +/-- The retained `nil`/`cons` validator telescopes, exact analyzer views, field +checks, positivity target, and terminal family applications all admit the D2 +post-family Theory interpretation despite their distinct fresh identifiers. -/ +theorem indexedVecProducedPostFamilySemantic_exists : + Nonempty (VInductDecl.ProducedNormalizationCandidatePostFamilySemanticRun + indexedVecStagedPostFamilyInput) := + indexedVecStagedPostFamilyInput.exists + +/-! ## Pre-family constructor safety + +The D3 replay uses the terminal family-analysis context, whose environment is +still exactly `natMap`. Ordinary constructor fields extend that context; +recursive fields advance only the fresh-name generator so their locals cannot +be used by later fields or the result. -/ + +private def indexedVecPreFamilyContext : AddInductive.Context := + indexedVecFamilyCandidate.trace.terminalContext + +private def indexedVecPreFamilyNContext : AddInductive.Context := + indexedVecPreFamilyContext.pushLocalDecl + consNName .implicit (.const ``Nat []) + +private def indexedVecPreFamilyHeadContext : AddInductive.Context := + indexedVecPreFamilyNContext.pushLocalDecl + consHeadName .default indexedVecValidationAlpha + +private def indexedVecPreFamilyResultContext : AddInductive.Context := + indexedVecPreFamilyHeadContext.advanceFresh + +private theorem indexedVecPreFamilyContext_eq : + indexedVecPreFamilyContext = indexedVecValidationFamilyContext := rfl + +private theorem indexedVecPreFamilyNContext_eq : + indexedVecPreFamilyNContext = + { indexedVecValidationNContext with env := indexedVecKernelEnv } := rfl + +private theorem indexedVecPreFamilyHeadContext_eq : + indexedVecPreFamilyHeadContext = + { indexedVecValidationHeadContext with env := indexedVecKernelEnv } := rfl + +private theorem indexedVecPreFamilySortCheckValid + (context : AddInductive.Context) + (contextEnv : context.env = indexedVecKernelEnv) + (contextSafety : context.safety = .safe) + (contextLparams : context.lparams = [`u]) + (contextFuel : context.fuel = ({} : FuelConfig)) : + AddInductive.CandidateCheckTypeStep.Valid + ⟨context, .sort (.succ (.param `u)), + .sort (.succ (.succ (.param `u)))⟩ := by + unfold AddInductive.CandidateCheckTypeStep.Valid + rw [contextEnv, contextSafety, contextLparams, contextFuel] + exact indexedVecPreFamilySortCheckTypeM context.lctx + +private theorem indexedVecPreFamilyNatCheckValid + (context : AddInductive.Context) + (contextEnv : context.env = indexedVecKernelEnv) + (contextSafety : context.safety = .safe) + (contextLparams : context.lparams = [`u]) + (contextFuel : context.fuel = ({} : FuelConfig)) : + AddInductive.CandidateCheckTypeStep.Valid + ⟨context, .const ``Nat [], .sort (.succ .zero)⟩ := by + unfold AddInductive.CandidateCheckTypeStep.Valid + rw [contextEnv, contextSafety, contextLparams, contextFuel] + exact indexedVecPreFamilyNatCheckTypeM context.lctx + +private theorem indexedVecPreFamilyNatEnsureValid + (context : AddInductive.Context) + (contextEnv : context.env = indexedVecKernelEnv) + (contextSafety : context.safety = .safe) + (contextLparams : context.lparams = [`u]) + (contextFuel : context.fuel = ({} : FuelConfig)) : + AddInductive.ConstructorEnsureTypeStep.Valid + ⟨context, .const ``Nat [], .sort (.succ .zero)⟩ := by + unfold AddInductive.ConstructorEnsureTypeStep.Valid + rw [contextEnv, contextSafety, contextLparams, contextFuel] + exact indexedVecPreFamilyNatEnsureTypeM context.lctx + +private theorem indexedVecPreFamilyTelescopeCheckValid + (context : AddInductive.Context) + (contextEnv : context.env = indexedVecKernelEnv) + (contextSafety : context.safety = .safe) + (contextLparams : context.lparams = [`u]) + (contextFuel : context.fuel = ({} : FuelConfig)) : + AddInductive.CandidateCheckTypeStep.Valid + ⟨context, indexedVecPreFamilyIndexTelescope, + .sort (mkLevelIMax' (.succ .zero) + (.succ (.succ (.param `u))))⟩ := by + unfold AddInductive.CandidateCheckTypeStep.Valid + rw [contextEnv, contextSafety, contextLparams, contextFuel] + exact indexedVecPreFamilyIndexTelescopeCheckTypeM context.lctx + +private theorem indexedVecPreFamilyFVarCheckTypeM + (lctx : LocalContext) (id : FVarId) (type : Expr) + (find : lctx.find? id = + some (.cdecl index id name type bi kind)) : + TypeChecker.M.run indexedVecKernelEnv .safe lctx [`u] + ({} : FuelConfig) (TypeChecker.checkType (.fvar id)) = + .ok type := by + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' (.fvar id) false + (TypeChecker.Methods.withFuel 9999) + (indexedVecTypeCheckerContext lctx) + ({} : TypeChecker.State)) = _ + rw [indexedVecPreFamilyInferTypeFVarCore 9999 lctx + ({} : TypeChecker.State) id type Std.HashMap.getElem?_empty find] + rfl + +private def indexedVecPreFamilyFVarInferOnlyState + (id : FVarId) (type : Expr) : TypeChecker.State := + { ({} : TypeChecker.State) with + inferTypeI := ({} : TypeChecker.State).inferTypeI.insert + (.fvar id) type } + +private theorem indexedVecPreFamilyFVarInferOnly + (lctx : LocalContext) (id : FVarId) (type : Expr) + (find : lctx.find? id = + some (.cdecl index id name type bi kind)) : + TypeChecker.Inner.inferType (.fvar id) true + (TypeChecker.Methods.withFuel 10000) + (indexedVecTypeCheckerContext lctx) + ({} : TypeChecker.State) = + .ok (type, indexedVecPreFamilyFVarInferOnlyState id type) := by + change TypeChecker.Inner.inferType' (.fvar id) true + (TypeChecker.Methods.withFuel 9999) + (indexedVecTypeCheckerContext lctx) + ({} : TypeChecker.State) = _ + unfold TypeChecker.Inner.inferType' + simp [indexedVecPreFamilyFVarInferOnlyState, + Expr.hasLooseBVars, Expr.looseBVarRange', + TypeChecker.Inner.inferFVar, indexedVecTypeCheckerContext, + find, LocalDecl.type, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + +private theorem indexedVecPreFamilyFVarEnsureTypeM + (lctx : LocalContext) (id : FVarId) (level : Level) + (find : lctx.find? id = + some (.cdecl index id name (.sort level) bi kind)) : + TypeChecker.M.run indexedVecKernelEnv .safe lctx [`u] + ({} : FuelConfig) (TypeChecker.ensureType (.fvar id)) = + .ok (.sort level) := by + unfold TypeChecker.ensureType TypeChecker.inferType + TypeChecker.ensureSort TypeChecker.RecM.run TypeChecker.M.run + simp only [readThe, MonadReaderOf.read, ReaderT.read, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind, + Pure.pure, StateT.pure, Except.pure, StateT.run', + Functor.map, Except.map] + rw [show TypeChecker.Inner.inferType (.fvar id) true + (TypeChecker.Methods.withFuel 10000) + { env := indexedVecKernelEnv, lctx := lctx, safety := .safe, + lparams := [`u], fuel := ({} : FuelConfig) } + ({} : TypeChecker.State) = + .ok (.sort level, + indexedVecPreFamilyFVarInferOnlyState id (.sort level)) by + simpa [indexedVecTypeCheckerContext] using + indexedVecPreFamilyFVarInferOnly lctx id (.sort level) find] + rfl + +private theorem indexedVecPreFamilyZeroCheckTypeM + (lctx : LocalContext) : + TypeChecker.M.run indexedVecKernelEnv .safe lctx [`u] + ({} : FuelConfig) (TypeChecker.checkType (.const ``Nat.zero [])) = + .ok (.const ``Nat []) := by + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' (.const ``Nat.zero []) false + (TypeChecker.Methods.withFuel 9999) + (indexedVecTypeCheckerContext lctx) + ({} : TypeChecker.State)) = _ + rw [indexedVecPreFamilyInferTypeZeroCore 9999 lctx + ({} : TypeChecker.State) Std.HashMap.getElem?_empty] + rfl + +private theorem indexedVecPreFamilySuccFVarCheckTypeM + (lctx : LocalContext) (id : FVarId) + (find : lctx.find? id = + some (.cdecl index id name (.const ``Nat []) bi kind)) : + TypeChecker.M.run indexedVecKernelEnv .safe lctx [`u] + ({} : FuelConfig) + (TypeChecker.checkType (replaySuccApp (.fvar id))) = + .ok (.const ``Nat []) := by + let succState := replayInsert ({} : TypeChecker.State) + (.const ``Nat.succ []) + (.forallE `n (.const ``Nat []) (.const ``Nat []) .default) + let argumentState := replayInsert succState (.fvar id) (.const ``Nat []) + have succRun : TypeChecker.Inner.inferType' (.const ``Nat.succ []) false + (TypeChecker.Methods.withFuel 9999) + (indexedVecTypeCheckerContext lctx) ({} : TypeChecker.State) = + .ok (.forallE `n (.const ``Nat []) (.const ``Nat []) .default, + succState) := by + simpa [succState, replayInsert] using + (indexedVecPreFamilyInferTypeSuccCore 9999 lctx + ({} : TypeChecker.State) Std.HashMap.getElem?_empty) + have argumentRun : TypeChecker.Inner.inferType' (.fvar id) false + (TypeChecker.Methods.withFuel 9999) + (indexedVecTypeCheckerContext lctx) succState = + .ok (.const ``Nat [], argumentState) := by + apply indexedVecPreFamilyInferTypeFVarCore + · simp [succState, replayInsert] + · exact find + have appRun := inferAppCoreOf 9999 + (indexedVecTypeCheckerContext lctx) + ({} : TypeChecker.State) succState argumentState + (.const ``Nat.succ []) (.fvar id) (.const ``Nat []) + (.const ``Nat []) `n .default + (by simp [Expr.hasLooseBVars, Expr.looseBVarRange']) + (by simp) succRun argumentRun (by rfl) + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' (replaySuccApp (.fvar id)) false + (TypeChecker.Methods.withFuel 9999) + (indexedVecTypeCheckerContext lctx) + ({} : TypeChecker.State)) = _ + rw [show TypeChecker.Inner.inferType' + (replaySuccApp (.fvar id)) false + (TypeChecker.Methods.withFuel 9999) + (indexedVecTypeCheckerContext lctx) + ({} : TypeChecker.State) = + .ok (.const ``Nat [], + { argumentState with inferTypeC := + (argumentState.inferTypeC.insert + (replaySuccApp (.fvar id)) (.const ``Nat [])) }) by + simpa [replaySuccApp, Expr.instantiate1_eq, + Expr.instantiate1'] using appRun] + rfl + +private theorem indexedVecPreFamilyFVarCheckValid + (context : AddInductive.Context) (id : FVarId) (type : Expr) + (find : context.lctx.find? id = + some (.cdecl index id name type bi kind)) + (contextEnv : context.env = indexedVecKernelEnv) + (contextSafety : context.safety = .safe) + (contextLparams : context.lparams = [`u]) + (contextFuel : context.fuel = ({} : FuelConfig)) : + AddInductive.CandidateCheckTypeStep.Valid + ⟨context, .fvar id, type⟩ := by + unfold AddInductive.CandidateCheckTypeStep.Valid + rw [contextEnv, contextSafety, contextLparams, contextFuel] + exact indexedVecPreFamilyFVarCheckTypeM context.lctx id type find + +private theorem indexedVecPreFamilyFVarEnsureValid + (context : AddInductive.Context) (id : FVarId) (level : Level) + (find : context.lctx.find? id = + some (.cdecl index id name (.sort level) bi kind)) + (contextEnv : context.env = indexedVecKernelEnv) + (contextSafety : context.safety = .safe) + (contextLparams : context.lparams = [`u]) + (contextFuel : context.fuel = ({} : FuelConfig)) : + AddInductive.ConstructorEnsureTypeStep.Valid + ⟨context, .fvar id, .sort level⟩ := by + unfold AddInductive.ConstructorEnsureTypeStep.Valid + rw [contextEnv, contextSafety, contextLparams, contextFuel] + exact indexedVecPreFamilyFVarEnsureTypeM context.lctx id level find + +private theorem indexedVecPreFamilyZeroCheckValid + (context : AddInductive.Context) + (contextEnv : context.env = indexedVecKernelEnv) + (contextSafety : context.safety = .safe) + (contextLparams : context.lparams = [`u]) + (contextFuel : context.fuel = ({} : FuelConfig)) : + AddInductive.CandidateCheckTypeStep.Valid + ⟨context, .const ``Nat.zero [], .const ``Nat []⟩ := by + unfold AddInductive.CandidateCheckTypeStep.Valid + rw [contextEnv, contextSafety, contextLparams, contextFuel] + exact indexedVecPreFamilyZeroCheckTypeM context.lctx + +private theorem indexedVecPreFamilySuccCheckValid + (context : AddInductive.Context) (id : FVarId) + (find : context.lctx.find? id = + some (.cdecl index id name (.const ``Nat []) bi kind)) + (contextEnv : context.env = indexedVecKernelEnv) + (contextSafety : context.safety = .safe) + (contextLparams : context.lparams = [`u]) + (contextFuel : context.fuel = ({} : FuelConfig)) : + AddInductive.CandidateCheckTypeStep.Valid + ⟨context, replaySuccApp (.fvar id), .const ``Nat []⟩ := by + unfold AddInductive.CandidateCheckTypeStep.Valid + rw [contextEnv, contextSafety, contextLparams, contextFuel] + exact indexedVecPreFamilySuccFVarCheckTypeM context.lctx id find + +private theorem indexedVecNormalizationFamilyView_eq : + indexedVecNormalizationCandidate.families.singleton.familyType.type.view = + indexedVecInfo.type := by + change indexedVecFamilyCandidate.view = indexedVecInfo.type + exact indexedVecFamilyCandidate_view_eq + +private theorem indexedVecNormalizationConstructors_eq : + indexedVecNormalizationCandidate.families.singleton.constructors = + .cons indexedVecNilConstructorCandidate + (.cons indexedVecConsConstructorCandidate .nil) := rfl + +private theorem indexedVecNormalizationPreFamilyContext_eq : + indexedVecNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext = + indexedVecPreFamilyContext := rfl + +private theorem indexedVecPreFamilySafetyRun : + AddInductive.checkConstructorPreFamilySafety + indexedVecStagedUniverseInput.staged.family.validation.stats + indexedVecNormalizationCandidate.families.singleton.familyType.type.view + indexedVecNormalizationCandidate.families.singleton.constructors + indexedVecNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext = + .ok () := by + rw [indexedVecStagedStats_eq] + rw [indexedVecNormalizationFamilyView_eq, + indexedVecNormalizationConstructors_eq, + indexedVecNormalizationPreFamilyContext_eq] + change AddInductive.checkConstructorPreFamilySafety + indexedVecCandidateInductiveStats indexedVecInfo.type + (.cons indexedVecNilConstructorCandidate + (.cons indexedVecConsConstructorCandidate .nil)) + indexedVecPreFamilyContext = .ok () + have alphaFind : indexedVecPreFamilyContext.lctx.find? + indexedVecValidationAlphaId = + some (.cdecl 0 indexedVecValidationAlphaId + indexedVecValidationParamName + (.sort (.succ (.param `u))) .default .default) := by + change indexedVecCtorValidationContext.lctx.find? + indexedVecValidationAlphaId = _ + exact indexedVecValidationAlphaFind + have alphaFindN : indexedVecPreFamilyNContext.lctx.find? + indexedVecValidationAlphaId = + some (.cdecl 0 indexedVecValidationAlphaId + indexedVecValidationParamName + (.sort (.succ (.param `u))) .default .default) := by + change indexedVecValidationNContext.lctx.find? + indexedVecValidationAlphaId = _ + exact indexedVecValidationAlphaFindInN + have alphaFindHead : indexedVecPreFamilyHeadContext.lctx.find? + indexedVecValidationAlphaId = + some (.cdecl 0 indexedVecValidationAlphaId + indexedVecValidationParamName + (.sort (.succ (.param `u))) .default .default) := by + change indexedVecValidationHeadContext.lctx.find? + indexedVecValidationAlphaId = _ + exact indexedVecValidationAlphaFindInHead + have nFindHead : indexedVecPreFamilyHeadContext.lctx.find? + indexedVecValidationNId = + some (.cdecl 2 indexedVecValidationNId consNName + (.const ``Nat []) .implicit .default) := by + change indexedVecValidationHeadContext.lctx.find? + indexedVecValidationNId = _ + exact indexedVecValidationNFindInHead + have nFindResult : indexedVecPreFamilyResultContext.lctx.find? + indexedVecValidationNId = + some (.cdecl 2 indexedVecValidationNId consNName + (.const ``Nat []) .implicit .default) := by + simpa [indexedVecPreFamilyResultContext, + AddInductive.Context.advanceFresh] using nFindHead + have baseFresh : indexedVecPreFamilyContext.lctx.find? + indexedVecPreFamilyContext.freshFVarId = none := by + change indexedVecCtorValidationContext.lctx.find? + indexedVecCtorValidationContext.freshFVarId = none + exact indexedVecCtorValidationContextFresh + have nFresh : indexedVecPreFamilyNContext.lctx.find? + indexedVecPreFamilyNContext.freshFVarId = none := by + change indexedVecValidationNContext.lctx.find? + indexedVecValidationNContext.freshFVarId = none + exact indexedVecValidationNContextFresh + have headFresh : indexedVecPreFamilyHeadContext.lctx.find? + indexedVecPreFamilyHeadContext.freshFVarId = none := by + change indexedVecValidationHeadContext.lctx.find? + indexedVecValidationHeadContext.freshFVarId = none + exact indexedVecValidationHeadContextFresh + let baseTelescope : AddInductive.ConstructorCheckedExpr + indexedVecPreFamilyContext indexedVecPreFamilyIndexTelescope := + .ofRun (by + simp [indexedVecPreFamilyIndexTelescope, FVarsIn, + Level.hasMVar']) + (indexedVecPreFamilyTelescopeCheckValid + indexedVecPreFamilyContext rfl rfl rfl rfl) + let headTelescope : AddInductive.ConstructorCheckedExpr + indexedVecPreFamilyHeadContext indexedVecPreFamilyIndexTelescope := + .ofRun (by + simp [indexedVecPreFamilyIndexTelescope, FVarsIn, + Level.hasMVar']) + (indexedVecPreFamilyTelescopeCheckValid + indexedVecPreFamilyHeadContext rfl rfl rfl rfl) + let resultTelescope : AddInductive.ConstructorCheckedExpr + indexedVecPreFamilyResultContext indexedVecPreFamilyIndexTelescope := + .ofRun (by + simp [indexedVecPreFamilyIndexTelescope, FVarsIn, + Level.hasMVar']) + (indexedVecPreFamilyTelescopeCheckValid + indexedVecPreFamilyResultContext rfl rfl rfl rfl) + let baseSort : AddInductive.ConstructorCheckedExpr + indexedVecPreFamilyContext (.sort (.succ (.param `u))) := + .ofRun (by simp [FVarsIn, Level.hasMVar']) + (indexedVecPreFamilySortCheckValid + indexedVecPreFamilyContext rfl rfl rfl rfl) + let headSort : AddInductive.ConstructorCheckedExpr + indexedVecPreFamilyHeadContext (.sort (.succ (.param `u))) := + .ofRun (by simp [FVarsIn, Level.hasMVar']) + (indexedVecPreFamilySortCheckValid + indexedVecPreFamilyHeadContext rfl rfl rfl rfl) + let resultSort : AddInductive.ConstructorCheckedExpr + indexedVecPreFamilyResultContext (.sort (.succ (.param `u))) := + .ofRun (by simp [FVarsIn, Level.hasMVar']) + (indexedVecPreFamilySortCheckValid + indexedVecPreFamilyResultContext rfl rfl rfl rfl) + let baseNat : AddInductive.ConstructorCheckedExpr + indexedVecPreFamilyContext (.const ``Nat []) := + .ofRun (by simp [FVarsIn]) + (indexedVecPreFamilyNatCheckValid + indexedVecPreFamilyContext rfl rfl rfl rfl) + let headNat : AddInductive.ConstructorCheckedExpr + indexedVecPreFamilyHeadContext (.const ``Nat []) := + .ofRun (by simp [FVarsIn]) + (indexedVecPreFamilyNatCheckValid + indexedVecPreFamilyHeadContext rfl rfl rfl rfl) + let resultNat : AddInductive.ConstructorCheckedExpr + indexedVecPreFamilyResultContext (.const ``Nat []) := + .ofRun (by simp [FVarsIn]) + (indexedVecPreFamilyNatCheckValid + indexedVecPreFamilyResultContext rfl rfl rfl rfl) + let zeroChecked : AddInductive.ConstructorCheckedExpr + indexedVecPreFamilyContext (.const ``Nat.zero []) := + .ofRun (by simp [FVarsIn]) + (indexedVecPreFamilyZeroCheckValid + indexedVecPreFamilyContext rfl rfl rfl rfl) + let nChecked : AddInductive.ConstructorCheckedExpr + indexedVecPreFamilyHeadContext indexedVecValidationNExpr := by + rw [indexedVecValidationNExprShape] + exact .ofRun (by + change (indexedVecPreFamilyHeadContext.lctx.find? + indexedVecValidationNId).isSome = true + rw [nFindHead] + rfl) (indexedVecPreFamilyFVarCheckValid + indexedVecPreFamilyHeadContext indexedVecValidationNId + (.const ``Nat []) nFindHead rfl rfl rfl rfl) + let succChecked : AddInductive.ConstructorCheckedExpr + indexedVecPreFamilyResultContext + (replaySuccApp indexedVecValidationNExpr) := by + rw [indexedVecValidationNExprShape] + exact .ofRun (by + simp [replaySuccApp, FVarsIn] + change (indexedVecPreFamilyResultContext.lctx.find? + indexedVecValidationNId).isSome = true + rw [nFindResult] + rfl) (indexedVecPreFamilySuccCheckValid + indexedVecPreFamilyResultContext indexedVecValidationNId + nFindResult rfl rfl rfl rfl) + let zeroComparison : AddInductive.CandidateIsDefEqObservation + indexedVecPreFamilyContext (.const ``Nat []) (.const ``Nat []) := + ⟨candidateIsDefEqSelfValid indexedVecPreFamilyContext + (.const ``Nat []) 9999 rfl⟩ + let nComparison : AddInductive.CandidateIsDefEqObservation + indexedVecPreFamilyHeadContext (.const ``Nat []) (.const ``Nat []) := + ⟨candidateIsDefEqSelfValid indexedVecPreFamilyHeadContext + (.const ``Nat []) 9999 rfl⟩ + let succComparison : AddInductive.CandidateIsDefEqObservation + indexedVecPreFamilyResultContext (.const ``Nat []) (.const ``Nat []) := + ⟨candidateIsDefEqSelfValid indexedVecPreFamilyResultContext + (.const ``Nat []) 9999 rfl⟩ + let nilSpine : AddInductive.ConstructorPreFamilyIndexSpineTrace + indexedVecPreFamilyContext indexedVecPreFamilyIndexTelescope + [.const ``Nat.zero []] := by + unfold indexedVecPreFamilyIndexTelescope + exact .cons indexedVecPreFamilyContext + indexedVecInfo.type.bindingBody!.bindingName! + (.const ``Nat []) (.sort (.succ (.param `u))) .default + (.const ``Nat.zero []) [] baseTelescope + ⟨zeroChecked, baseNat, zeroComparison⟩ + (by + simpa [Expr.instantiate1_eq, Expr.instantiate1'] using + (AddInductive.ConstructorPreFamilyIndexSpineTrace.nil + indexedVecPreFamilyContext + (.sort (.succ (.param `u))) baseSort rfl)) + let recursiveSpine : AddInductive.ConstructorPreFamilyIndexSpineTrace + indexedVecPreFamilyHeadContext indexedVecPreFamilyIndexTelescope + [indexedVecValidationNExpr] := by + unfold indexedVecPreFamilyIndexTelescope + exact .cons indexedVecPreFamilyHeadContext + indexedVecInfo.type.bindingBody!.bindingName! + (.const ``Nat []) (.sort (.succ (.param `u))) .default + indexedVecValidationNExpr [] headTelescope + ⟨nChecked, headNat, nComparison⟩ + (by + simpa [Expr.instantiate1_eq, Expr.instantiate1'] using + (AddInductive.ConstructorPreFamilyIndexSpineTrace.nil + indexedVecPreFamilyHeadContext + (.sort (.succ (.param `u))) headSort rfl)) + let resultSpine : AddInductive.ConstructorPreFamilyIndexSpineTrace + indexedVecPreFamilyResultContext indexedVecPreFamilyIndexTelescope + [replaySuccApp indexedVecValidationNExpr] := by + unfold indexedVecPreFamilyIndexTelescope + exact .cons indexedVecPreFamilyResultContext + indexedVecInfo.type.bindingBody!.bindingName! + (.const ``Nat []) (.sort (.succ (.param `u))) .default + (replaySuccApp indexedVecValidationNExpr) [] resultTelescope + ⟨succChecked, resultNat, succComparison⟩ + (by + simpa [Expr.instantiate1_eq, Expr.instantiate1'] using + (AddInductive.ConstructorPreFamilyIndexSpineTrace.nil + indexedVecPreFamilyResultContext + (.sort (.succ (.param `u))) resultSort rfl)) + have nilArgs : indexedVecValidationNilResult.getAppArgs.toList.drop + indexedVecCandidateInductiveStats.params.size = + [.const ``Nat.zero []] := by + simp [indexedVecValidationNilResult, + indexedVecValidationStatsParams, ctorIndexedVecAppGetAppArgs] + obtain ⟨nilTargetSpine, nilTargetSpineRun⟩ : + ∃ nilTargetSpine : AddInductive.ConstructorPreFamilyIndexSpineTrace + indexedVecPreFamilyContext indexedVecPreFamilyIndexTelescope + (indexedVecValidationNilResult.getAppArgs.toList.drop + indexedVecCandidateInductiveStats.params.size), + AddInductive.ConstructorPreFamilyIndexSpineTrace.build + indexedVecPreFamilyContext indexedVecPreFamilyIndexTelescope + (indexedVecValidationNilResult.getAppArgs.toList.drop + indexedVecCandidateInductiveStats.params.size) = + .ok nilTargetSpine := by + rw [nilArgs] + exact ⟨nilSpine, nilSpine.build_eq⟩ + have nilIndependent : AddInductive.constructorIndependentOf + indexedVecValidationNilResult [] = true := by + simp [AddInductive.constructorIndependentOf] + let nilTerminalTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + indexedVecValidationNilResult 1 [] false := + .terminal indexedVecPreFamilyContext indexedVecValidationNilResult + 1 [] false indexedVecValidationNilResultIsValid nilIndependent + nilTargetSpine + have nilTerminalRun : + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + indexedVecValidationNilResult 1 [] false 999 = + .ok nilTerminalTrace := by + exact AddInductive.ConstructorPreFamilyViewTrace.terminal_build_eq + (fuel := 998) rfl indexedVecValidationNilResultIsValid nilIndependent + nilTargetSpine + obtain ⟨nilTailTrace, nilTailRun⟩ : + ∃ nilTailTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + (nilCtorBodyRaw.instantiate1 indexedVecValidationAlpha) + 1 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + (nilCtorBodyRaw.instantiate1 indexedVecValidationAlpha) + 1 [] false 999 = + .ok nilTailTrace := by + rw [show nilCtorBodyRaw.instantiate1 indexedVecValidationAlpha = + indexedVecValidationNilResult by + simpa [nilInfoTypeShape, nilCtorTypeRaw] using + indexedVecValidationNilResultShape] + exact ⟨nilTerminalTrace, nilTerminalRun⟩ + have parameterAtZero : indexedVecCandidateInductiveStats.params[0]? = + some indexedVecValidationAlpha := by + rw [indexedVecValidationStatsParams] + rfl + obtain ⟨nilRawViewTrace, nilRawViewRun⟩ : + ∃ nilRawViewTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + nilCtorTypeRaw 0 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + nilCtorTypeRaw 0 [] false 1000 = .ok nilRawViewTrace := by + simp only [nilCtorTypeRaw, + AddInductive.ConstructorPreFamilyViewTrace.build] + split + · rename_i parameter parameterAt + rw [parameterAtZero] at parameterAt + cases parameterAt + rw [nilTailRun] + exact ⟨_, rfl⟩ + · rename_i noParameter + rw [parameterAtZero] at noParameter + contradiction + obtain ⟨nilViewTrace, nilViewRun⟩ : + ∃ nilViewTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + indexedVecNilInfo.type 0 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + indexedVecNilInfo.type 0 [] false 1000 = + .ok nilViewTrace := by + rw [nilInfoTypeShape] + exact ⟨nilRawViewTrace, nilRawViewRun⟩ + obtain ⟨nilHeadTrace, nilHeadRun⟩ : + ∃ nilHeadTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + indexedVecNilConstructorCandidate.type.view 0 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + indexedVecNilConstructorCandidate.type.view 0 [] false 1000 = + .ok nilHeadTrace := by + change ∃ nilHeadTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + nilCandidate.view 0 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + nilCandidate.view 0 [] false 1000 = .ok nilHeadTrace + rw [nilCandidate_view_eq] + exact ⟨nilViewTrace, nilViewRun⟩ + let baseNatEnsure : AddInductive.ConstructorEnsureTypeObservation + indexedVecPreFamilyContext (.const ``Nat []) := + ⟨.sort (.succ .zero), indexedVecPreFamilyNatEnsureValid + indexedVecPreFamilyContext rfl rfl rfl rfl⟩ + let baseNatConsumed : AddInductive.ConstructorCheckedExpr + indexedVecPreFamilyContext + (AddInductive.consumeTypeAnnotations (.const ``Nat [])) := by + rw [indexedVecValidationConsumeNat] + exact baseNat + let baseNatAnnotations : AddInductive.CandidateIsDefEqObservation + indexedVecPreFamilyContext (.const ``Nat []) + (AddInductive.consumeTypeAnnotations (.const ``Nat [])) := by + rw [indexedVecValidationConsumeNat] + exact ⟨candidateIsDefEqSelfValid indexedVecPreFamilyContext + (.const ``Nat []) 9999 rfl⟩ + let alphaChecked : AddInductive.ConstructorCheckedExpr + indexedVecPreFamilyNContext indexedVecValidationAlpha := by + rw [indexedVecValidationAlphaShape] + exact .ofRun (by + change (indexedVecPreFamilyNContext.lctx.find? + indexedVecValidationAlphaId).isSome = true + rw [alphaFindN] + rfl) (indexedVecPreFamilyFVarCheckValid + indexedVecPreFamilyNContext indexedVecValidationAlphaId + (.sort (.succ (.param `u))) alphaFindN rfl rfl rfl rfl) + let alphaEnsure : AddInductive.ConstructorEnsureTypeObservation + indexedVecPreFamilyNContext indexedVecValidationAlpha := by + rw [indexedVecValidationAlphaShape] + exact ⟨.sort (.succ (.param `u)), + indexedVecPreFamilyFVarEnsureValid indexedVecPreFamilyNContext + indexedVecValidationAlphaId (.succ (.param `u)) alphaFindN + rfl rfl rfl rfl⟩ + let alphaConsumed : AddInductive.ConstructorCheckedExpr + indexedVecPreFamilyNContext + (AddInductive.consumeTypeAnnotations indexedVecValidationAlpha) := by + rw [indexedVecValidationConsumeAlpha] + exact alphaChecked + let alphaAnnotations : AddInductive.CandidateIsDefEqObservation + indexedVecPreFamilyNContext indexedVecValidationAlpha + (AddInductive.consumeTypeAnnotations indexedVecValidationAlpha) := by + rw [indexedVecValidationConsumeAlpha] + exact ⟨candidateIsDefEqSelfValid indexedVecPreFamilyNContext + indexedVecValidationAlpha 9999 rfl⟩ + have alphaNeRemoved : indexedVecValidationAlphaId ≠ + indexedVecPreFamilyHeadContext.freshFVarId := by + intro equality + have fresh := headFresh + rw [← equality, alphaFindHead] at fresh + contradiction + have nNeRemoved : indexedVecValidationNId ≠ + indexedVecPreFamilyHeadContext.freshFVarId := by + intro equality + have fresh := headFresh + rw [← equality, nFindHead] at fresh + contradiction + have recursiveArgs : + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr).getAppArgs.toList.drop + indexedVecCandidateInductiveStats.params.size = + [indexedVecValidationNExpr] := by + simp [indexedVecValidationStatsParams, ctorIndexedVecAppGetAppArgs] + obtain ⟨recursiveTargetSpine, recursiveTargetSpineRun⟩ : + ∃ recursiveTargetSpine : + AddInductive.ConstructorPreFamilyIndexSpineTrace + indexedVecPreFamilyHeadContext + indexedVecPreFamilyIndexTelescope + ((ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr).getAppArgs.toList.drop + indexedVecCandidateInductiveStats.params.size), + AddInductive.ConstructorPreFamilyIndexSpineTrace.build + indexedVecPreFamilyHeadContext + indexedVecPreFamilyIndexTelescope + ((ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr).getAppArgs.toList.drop + indexedVecCandidateInductiveStats.params.size) = + .ok recursiveTargetSpine := by + rw [recursiveArgs] + exact ⟨recursiveSpine, recursiveSpine.build_eq⟩ + have recursiveTargetValid : AddInductive.isValidIndAppIdx + indexedVecCandidateInductiveStats + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) 0 = true := + indexedVecValidationAppIsValidIdx indexedVecValidationNExpr + indexedVecValidationNHasNoIndOcc + let recursiveFieldTrace : AddInductive.ConstructorPreFamilyRecursiveTrace + indexedVecCandidateInductiveStats 0 indexedVecPreFamilyIndexTelescope + indexedVecPreFamilyHeadContext + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) + indexedVecPreFamilyHeadContext.fuel.inductiveFuel := + .target indexedVecPreFamilyHeadContext + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) + recursiveTargetValid recursiveTargetSpine + have recursiveFieldRun : AddInductive.ConstructorPreFamilyRecursiveTrace.build + indexedVecCandidateInductiveStats 0 indexedVecPreFamilyIndexTelescope + indexedVecPreFamilyHeadContext + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) + indexedVecPreFamilyHeadContext.fuel.inductiveFuel = + .ok recursiveFieldTrace := by + exact AddInductive.ConstructorPreFamilyRecursiveTrace.target_build_eq + (fuel := 999) rfl recursiveTargetValid recursiveTargetSpine + have recursiveIndependent : AddInductive.constructorIndependentOf + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) [] = true := by + simp [AddInductive.constructorIndependentOf] + have resultArgs : indexedVecValidationConsResult.getAppArgs.toList.drop + indexedVecCandidateInductiveStats.params.size = + [replaySuccApp indexedVecValidationNExpr] := by + simp [indexedVecValidationConsResult, + indexedVecValidationStatsParams, ctorIndexedVecAppGetAppArgs] + obtain ⟨resultTargetSpine, resultTargetSpineRun⟩ : + ∃ resultTargetSpine : + AddInductive.ConstructorPreFamilyIndexSpineTrace + indexedVecPreFamilyResultContext + indexedVecPreFamilyIndexTelescope + (indexedVecValidationConsResult.getAppArgs.toList.drop + indexedVecCandidateInductiveStats.params.size), + AddInductive.ConstructorPreFamilyIndexSpineTrace.build + indexedVecPreFamilyResultContext + indexedVecPreFamilyIndexTelescope + (indexedVecValidationConsResult.getAppArgs.toList.drop + indexedVecCandidateInductiveStats.params.size) = + .ok resultTargetSpine := by + rw [resultArgs] + exact ⟨resultSpine, resultSpine.build_eq⟩ + have resultIndependent : AddInductive.constructorIndependentOf + indexedVecValidationConsResult + [indexedVecPreFamilyHeadContext.freshFVarId] = true := by + simp [AddInductive.constructorIndependentOf, + indexedVecValidationConsResult, ctorIndexedVecApp, replaySuccApp, + Expr.fvarsList, + indexedVecValidationAlphaShape, indexedVecValidationNExprShape, + alphaNeRemoved, nNeRemoved] + let resultTerminalTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 indexedVecPreFamilyIndexTelescope + indexedVecPreFamilyResultContext indexedVecValidationConsResult 4 + [indexedVecPreFamilyHeadContext.freshFVarId] true := + .terminal indexedVecPreFamilyResultContext + indexedVecValidationConsResult 4 + [indexedVecPreFamilyHeadContext.freshFVarId] true + indexedVecValidationConsResultIsValid resultIndependent + resultTargetSpine + have resultTerminalRun : AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 indexedVecPreFamilyIndexTelescope + indexedVecPreFamilyResultContext indexedVecValidationConsResult 4 + [indexedVecPreFamilyHeadContext.freshFVarId] true 996 = + .ok resultTerminalTrace := by + exact AddInductive.ConstructorPreFamilyViewTrace.terminal_build_eq + (fuel := 995) rfl indexedVecValidationConsResultIsValid + resultIndependent resultTargetSpine + obtain ⟨consResultTailTrace, consResultTailRun⟩ : + ∃ consResultTailTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope + indexedVecPreFamilyHeadContext.advanceFresh + (indexedVecValidationConsAfterHead.bindingBody!.instantiate1 + indexedVecPreFamilyHeadContext.freshExpr) + 4 [indexedVecPreFamilyHeadContext.freshFVarId] true, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope + indexedVecPreFamilyHeadContext.advanceFresh + (indexedVecValidationConsAfterHead.bindingBody!.instantiate1 + indexedVecPreFamilyHeadContext.freshExpr) + 4 [indexedVecPreFamilyHeadContext.freshFVarId] true 996 = + .ok consResultTailTrace := by + rw [show indexedVecValidationConsAfterHead.bindingBody!.instantiate1 + indexedVecPreFamilyHeadContext.freshExpr = + indexedVecValidationConsResult by + change indexedVecValidationConsAfterHead.bindingBody!.instantiate1 + indexedVecValidationHeadContext.freshExpr = _ + exact indexedVecValidationConsResultShape] + exact ⟨resultTerminalTrace, resultTerminalRun⟩ + obtain ⟨explicitResultTailTrace, explicitResultTailRun⟩ : + ∃ explicitResultTailTrace : + AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope + indexedVecPreFamilyHeadContext.advanceFresh + ((ctorIndexedVecApp indexedVecValidationAlpha + (replaySuccApp indexedVecValidationNExpr)).instantiate1 + indexedVecPreFamilyHeadContext.freshExpr) + 4 [indexedVecPreFamilyHeadContext.freshFVarId] true, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope + indexedVecPreFamilyHeadContext.advanceFresh + ((ctorIndexedVecApp indexedVecValidationAlpha + (replaySuccApp indexedVecValidationNExpr)).instantiate1 + indexedVecPreFamilyHeadContext.freshExpr) + 4 [indexedVecPreFamilyHeadContext.freshFVarId] true 996 = + .ok explicitResultTailTrace := by + change ∃ explicitResultTailTrace : + AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope + indexedVecPreFamilyHeadContext.advanceFresh + (indexedVecValidationConsAfterHead.bindingBody!.instantiate1 + indexedVecPreFamilyHeadContext.freshExpr) + 4 [indexedVecPreFamilyHeadContext.freshFVarId] true, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope + indexedVecPreFamilyHeadContext.advanceFresh + (indexedVecValidationConsAfterHead.bindingBody!.instantiate1 + indexedVecPreFamilyHeadContext.freshExpr) + 4 [indexedVecPreFamilyHeadContext.freshFVarId] true 996 = + .ok explicitResultTailTrace + exact ⟨consResultTailTrace, consResultTailRun⟩ + have noParameterThree : indexedVecCandidateInductiveStats.params[3]? = + none := by + rw [indexedVecValidationStatsParams] + rfl + obtain ⟨consAfterHeadTrace, consAfterHeadRun⟩ : + ∃ consAfterHeadTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyHeadContext + indexedVecValidationConsAfterHead 3 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyHeadContext + indexedVecValidationConsAfterHead 3 [] false 997 = + .ok consAfterHeadTrace := by + simp only [indexedVecValidationConsAfterHead, + AddInductive.ConstructorPreFamilyViewTrace.build] + split + · rename_i parameter parameterAt + rw [noParameterThree] at parameterAt + contradiction + · split + · rename_i nonrecursive + rw [indexedVecValidationTailHasIndOcc] at nonrecursive + contradiction + · rw [dif_pos recursiveIndependent, recursiveFieldRun] + simp only [Bind.bind, Except.bind] + rw [dif_pos headFresh] + rw [explicitResultTailRun] + exact ⟨_, rfl⟩ + obtain ⟨consHeadTailTrace, consHeadTailRun⟩ : + ∃ consHeadTailTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyHeadContext + (indexedVecValidationConsAfterN.bindingBody!.instantiate1 + indexedVecPreFamilyNContext.freshExpr) + 3 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyHeadContext + (indexedVecValidationConsAfterN.bindingBody!.instantiate1 + indexedVecPreFamilyNContext.freshExpr) + 3 [] false 997 = .ok consHeadTailTrace := by + rw [show indexedVecValidationConsAfterN.bindingBody!.instantiate1 + indexedVecPreFamilyNContext.freshExpr = + indexedVecValidationConsAfterHead by + change indexedVecValidationConsAfterN.bindingBody!.instantiate1 + indexedVecValidationNContext.freshExpr = _ + exact indexedVecValidationConsAfterHeadShape] + exact ⟨consAfterHeadTrace, consAfterHeadRun⟩ + obtain ⟨explicitHeadTailTrace, explicitHeadTailRun⟩ : + ∃ explicitHeadTailTrace : + AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope + (indexedVecPreFamilyNContext.pushLocalDecl consHeadName .default + (AddInductive.consumeTypeAnnotations + indexedVecValidationAlpha)) + ((Expr.forallE consTailName + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) + (ctorIndexedVecApp indexedVecValidationAlpha + (replaySuccApp indexedVecValidationNExpr)) + .default).instantiate1 indexedVecPreFamilyNContext.freshExpr) + 3 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope + (indexedVecPreFamilyNContext.pushLocalDecl consHeadName .default + (AddInductive.consumeTypeAnnotations + indexedVecValidationAlpha)) + ((Expr.forallE consTailName + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) + (ctorIndexedVecApp indexedVecValidationAlpha + (replaySuccApp indexedVecValidationNExpr)) + .default).instantiate1 indexedVecPreFamilyNContext.freshExpr) + 3 [] false 997 = .ok explicitHeadTailTrace := by + rw [indexedVecValidationConsumeAlpha] + rw [show Expr.forallE consTailName + (ctorIndexedVecApp indexedVecValidationAlpha + indexedVecValidationNExpr) + (ctorIndexedVecApp indexedVecValidationAlpha + (replaySuccApp indexedVecValidationNExpr)) .default = + indexedVecValidationConsAfterN.bindingBody! by rfl] + exact ⟨consHeadTailTrace, consHeadTailRun⟩ + have noParameterTwo : indexedVecCandidateInductiveStats.params[2]? = + none := by + rw [indexedVecValidationStatsParams] + rfl + obtain ⟨consAfterNTrace, consAfterNRun⟩ : + ∃ consAfterNTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyNContext + indexedVecValidationConsAfterN 2 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyNContext + indexedVecValidationConsAfterN 2 [] false 998 = + .ok consAfterNTrace := by + simp only [indexedVecValidationConsAfterN, + AddInductive.ConstructorPreFamilyViewTrace.build] + split + · rename_i parameter parameterAt + rw [noParameterTwo] at parameterAt + contradiction + · split + · rw [alphaChecked.check_eq, alphaEnsure.observe_eq, + alphaConsumed.check_eq] + rw [dif_pos (by + simp [AddInductive.constructorIndependentOf])] + simp only [Bind.bind, Except.bind] + rw [alphaAnnotations.observe_eq] + simp only [] + rw [dif_pos nFresh] + rw [explicitHeadTailRun] + exact ⟨_, rfl⟩ + · rename_i recursive + rw [indexedVecValidationAlphaHasNoIndOcc] at recursive + contradiction + obtain ⟨consNTailTrace, consNTailRun⟩ : + ∃ consNTailTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyNContext + (indexedVecValidationConsAfterParam.bindingBody!.instantiate1 + indexedVecPreFamilyContext.freshExpr) + 2 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyNContext + (indexedVecValidationConsAfterParam.bindingBody!.instantiate1 + indexedVecPreFamilyContext.freshExpr) + 2 [] false 998 = .ok consNTailTrace := by + rw [show indexedVecValidationConsAfterParam.bindingBody!.instantiate1 + indexedVecPreFamilyContext.freshExpr = + indexedVecValidationConsAfterN by + change indexedVecValidationConsAfterParam.bindingBody!.instantiate1 + indexedVecValidationNExpr = _ + exact indexedVecValidationConsAfterNShape] + exact ⟨consAfterNTrace, consAfterNRun⟩ + obtain ⟨explicitNTailTrace, explicitNTailRun⟩ : + ∃ explicitNTailTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope + (indexedVecPreFamilyContext.pushLocalDecl consNName .implicit + (AddInductive.consumeTypeAnnotations (.const ``Nat []))) + ((Expr.forallE consHeadName indexedVecValidationAlpha + (Expr.forallE consTailName + (ctorIndexedVecApp indexedVecValidationAlpha (.bvar 1)) + (ctorIndexedVecApp indexedVecValidationAlpha + (replaySuccApp (.bvar 2))) .default) + .default).instantiate1 indexedVecPreFamilyContext.freshExpr) + 2 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope + (indexedVecPreFamilyContext.pushLocalDecl consNName .implicit + (AddInductive.consumeTypeAnnotations (.const ``Nat []))) + ((Expr.forallE consHeadName indexedVecValidationAlpha + (Expr.forallE consTailName + (ctorIndexedVecApp indexedVecValidationAlpha (.bvar 1)) + (ctorIndexedVecApp indexedVecValidationAlpha + (replaySuccApp (.bvar 2))) .default) + .default).instantiate1 indexedVecPreFamilyContext.freshExpr) + 2 [] false 998 = .ok explicitNTailTrace := by + rw [indexedVecValidationConsumeNat] + rw [show Expr.forallE consHeadName indexedVecValidationAlpha + (Expr.forallE consTailName + (ctorIndexedVecApp indexedVecValidationAlpha (.bvar 1)) + (ctorIndexedVecApp indexedVecValidationAlpha + (replaySuccApp (.bvar 2))) .default) .default = + indexedVecValidationConsAfterParam.bindingBody! by + rw [indexedVecValidationConsAfterParamExplicitShape] + rfl] + exact ⟨consNTailTrace, consNTailRun⟩ + have noParameterOne : indexedVecCandidateInductiveStats.params[1]? = + none := by + rw [indexedVecValidationStatsParams] + rfl + obtain ⟨consAfterParamTrace, consAfterParamRun⟩ : + ∃ consAfterParamTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + indexedVecValidationConsAfterParam 1 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + indexedVecValidationConsAfterParam 1 [] false 999 = + .ok consAfterParamTrace := by + rw [indexedVecValidationConsAfterParamExplicitShape] + simp only [AddInductive.ConstructorPreFamilyViewTrace.build] + split + · rename_i parameter parameterAt + rw [noParameterOne] at parameterAt + contradiction + · split + · rw [baseNat.check_eq, baseNatEnsure.observe_eq, + baseNatConsumed.check_eq] + rw [dif_pos (by + simp [AddInductive.constructorIndependentOf])] + simp only [Bind.bind, Except.bind] + rw [baseNatAnnotations.observe_eq] + simp only [] + rw [dif_pos baseFresh] + rw [explicitNTailRun] + exact ⟨_, rfl⟩ + · rename_i recursive + rw [indexedVecValidationNatHasNoIndOcc] at recursive + contradiction + obtain ⟨consParameterTailTrace, consParameterTailRun⟩ : + ∃ consParameterTailTrace : + AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + (consNTypeRaw.instantiate1 indexedVecValidationAlpha) + 1 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + (consNTypeRaw.instantiate1 indexedVecValidationAlpha) + 1 [] false 999 = .ok consParameterTailTrace := by + change ∃ consParameterTailTrace : + AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + indexedVecValidationConsAfterParam 1 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + indexedVecValidationConsAfterParam 1 [] false 999 = + .ok consParameterTailTrace + exact ⟨consAfterParamTrace, consAfterParamRun⟩ + obtain ⟨consRawViewTrace, consRawViewRun⟩ : + ∃ consRawViewTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + consCtorTypeRaw 0 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + consCtorTypeRaw 0 [] false 1000 = .ok consRawViewTrace := by + simp only [consCtorTypeRaw, + AddInductive.ConstructorPreFamilyViewTrace.build] + split + · rename_i parameter parameterAt + rw [parameterAtZero] at parameterAt + cases parameterAt + rw [consParameterTailRun] + exact ⟨_, rfl⟩ + · rename_i noParameter + rw [parameterAtZero] at noParameter + contradiction + obtain ⟨consViewTrace, consViewRun⟩ : + ∃ consViewTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + indexedVecConsInfo.type 0 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + indexedVecConsInfo.type 0 [] false 1000 = .ok consViewTrace := by + rw [consInfoTypeShape] + exact ⟨consRawViewTrace, consRawViewRun⟩ + obtain ⟨consHeadTrace, consHeadRun⟩ : + ∃ consHeadTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + indexedVecConsConstructorCandidate.type.view 0 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + indexedVecConsConstructorCandidate.type.view 0 [] false 1000 = + .ok consHeadTrace := by + change ∃ consHeadTrace : AddInductive.ConstructorPreFamilyViewTrace + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + consCandidate.view 0 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + consCandidate.view 0 [] false 1000 = .ok consHeadTrace + rw [consCandidate_view_eq] + exact ⟨consViewTrace, consViewRun⟩ + let consListTrace : AddInductive.ConstructorPreFamilyListTrace + indexedVecCandidateInductiveStats 0 indexedVecPreFamilyIndexTelescope + indexedVecPreFamilyContext + (.cons indexedVecConsConstructorCandidate .nil) := + .cons consHeadTrace .nil + have consListRun : AddInductive.ConstructorPreFamilyListTrace.build + indexedVecCandidateInductiveStats 0 indexedVecPreFamilyIndexTelescope + indexedVecPreFamilyContext + (.cons indexedVecConsConstructorCandidate .nil) = + .ok consListTrace := by + exact AddInductive.ConstructorPreFamilyListTrace.cons_build_eq + consHeadTrace consHeadRun .nil rfl + let constructorListTrace : AddInductive.ConstructorPreFamilyListTrace + indexedVecCandidateInductiveStats 0 indexedVecPreFamilyIndexTelescope + indexedVecPreFamilyContext + (.cons indexedVecNilConstructorCandidate + (.cons indexedVecConsConstructorCandidate .nil)) := + .cons nilHeadTrace consListTrace + have constructorListRun : + AddInductive.ConstructorPreFamilyListTrace.build + indexedVecCandidateInductiveStats 0 + indexedVecPreFamilyIndexTelescope indexedVecPreFamilyContext + (.cons indexedVecNilConstructorCandidate + (.cons indexedVecConsConstructorCandidate .nil)) = + .ok constructorListTrace := by + exact AddInductive.ConstructorPreFamilyListTrace.cons_build_eq + nilHeadTrace nilHeadRun consListTrace consListRun + have parametersRun : AddInductive.instantiateFamilyParameters + indexedVecInfo.type indexedVecCandidateInductiveStats.params.toList = + .ok indexedVecPreFamilyIndexTelescope := by + rw [indexedVecPreFamilyIndexTelescope_eq] + rw [indexedVecValidationStatsParams] + rw [indexedVecInfoTypeShape] + simp [AddInductive.instantiateFamilyParameters, vecFamilyTail, vecIndexName, + Expr.instantiate1_eq, Expr.instantiate1', Pure.pure, Except.pure] + unfold AddInductive.checkConstructorPreFamilySafety + have translationUnique : + (AddInductive.theoryTranslationUnique indexedVecInfo.type && + (AddInductive.CandidateList.cons indexedVecNilConstructorCandidate + (AddInductive.CandidateList.cons indexedVecConsConstructorCandidate + (AddInductive.CandidateList.nil : AddInductive.CandidateList + AddInductive.CandidateConstructor []))).viewTranslationUnique) = + true := by + change (AddInductive.theoryTranslationUnique indexedVecInfo.type && + (nilCandidateTrace.viewTranslationUnique && + (consCandidateTrace.viewTranslationUnique && true))) = true + rw [nilCandidateTrace.viewTranslationUnique_eq, + consCandidateTrace.viewTranslationUnique_eq] + change (AddInductive.theoryTranslationUnique indexedVecInfo.type && + (AddInductive.theoryTranslationUnique nilCandidate.view && + (AddInductive.theoryTranslationUnique consCandidate.view && true))) = + true + rw [nilCandidate_view_eq, consCandidate_view_eq, + indexedVecInfoTypeShape, nilInfoTypeShape, consInfoTypeShape] + simp [AddInductive.theoryTranslationUnique, vecFamilyTail, + nilCtorTypeRaw, nilCtorBodyRaw, consCtorTypeRaw, consNTypeRaw, + consHeadTypeRaw, consTailTypeRaw, consTerminalRaw] + rw [if_pos translationUnique] + rw [parametersRun] + simp only [Bind.bind, Except.bind] + rw [constructorListRun] + rfl + +private def indexedVecStagedPreFamilyInput : + VInductDecl.StagedNormalizationCandidatePreFamilyInput + indexedVecFamilyCandidateContext ctorContext natFinalEnv [`u] + indexedVecNormalizationCandidate indexedVecDecl := + VInductDecl.StagedNormalizationCandidatePreFamilyInput.ofRun + indexedVecStagedPostFamilyInput indexedVecPreFamilySafetyRun + +/-- IndexedVec's ordinary fields are retained, its recursive tail is omitted, +and both constructor-result index spines admit the exact pre-family semantic +replay. -/ +theorem indexedVecProducedPreFamilySemantic_exists : + Nonempty (VInductDecl.ProducedNormalizationCandidatePreFamilySemanticRun + indexedVecStagedPreFamilyInput) := + indexedVecStagedPreFamilyInput.exists + +private def indexedVecReorderedViewType : VInductiveType := + { indexedVecType with + ctors := [indexedVecType.ctors[1], indexedVecType.ctors[0]] } + +private def indexedVecReorderedViewDecl : VInductDecl := + { indexedVecDecl with types := [indexedVecReorderedViewType] } + +/-- Swapping the two otherwise unchanged constructor payloads fails the +computational normalization-shape gate before semantic or generation evidence +can be attached. -/ +theorem indexedVecReorderedView_rejected : + VInductDecl.normalization? indexedVecDecl + indexedVecReorderedViewDecl = none := rfl + +theorem indexedVecSemanticFamilyViewTr : + TrExpr natFinalEnv [`u] [] indexedVecFamilyCandidate.view + indexedVecType.type := by + rw [indexedVecFamilyCandidate_view_eq] + obtain ⟨u, htype⟩ := indexedVecType_wf + exact ⟨_, indexedVecSemanticFamilySourceTr, ⟨_, htype⟩⟩ + +theorem indexedVecSemanticNilViewTr : + TrExpr indexedVecTypeEnv [`u] [] nilCandidate.view + indexedVecType.ctors[0].type := by + rw [nilCandidate_view_eq] + obtain ⟨u, htype⟩ := indexedVecNil_wf + exact ⟨_, indexedVecSemanticNilSourceTr, ⟨_, htype⟩⟩ + +theorem indexedVecSemanticConsViewTr : + TrExpr indexedVecTypeEnv [`u] [] consCandidate.view + indexedVecType.ctors[1].type := by + rw [consCandidate_view_eq] + obtain ⟨u, htype⟩ := indexedVecSemanticConsIsType + exact ⟨_, indexedVecSemanticConsSourceTr, ⟨_, htype⟩⟩ + +def indexedVecSemanticFamilyRootRun : + TypeChecker.CandidateExprRootRun natFinalEnv [`u] + indexedVecFamilyCandidate indexedVecType.type indexedVecType.type where + contextRun := indexedVecSemanticFamilyContextRun + venv_eq := rfl + lparams_eq := rfl + vlctx_eq := rfl + source_tr := indexedVecSemanticFamilySourceTr + view_tr := indexedVecSemanticFamilyViewTr + whnfFuel := 9999 + whnfDepth := rfl + +def indexedVecSemanticNilRootRun : + TypeChecker.CandidateExprRootRun indexedVecTypeEnv [`u] + nilCandidate indexedVecType.ctors[0].type + indexedVecType.ctors[0].type where + contextRun := by + simpa [nilCandidate, nilCandidateContext] using indexedVecSemanticCtorContextRun + venv_eq := rfl + lparams_eq := rfl + vlctx_eq := rfl + source_tr := indexedVecSemanticNilSourceTr + view_tr := indexedVecSemanticNilViewTr + whnfFuel := 9999 + whnfDepth := rfl + +def indexedVecSemanticConsRootRun : + TypeChecker.CandidateExprRootRun indexedVecTypeEnv [`u] + consCandidate indexedVecType.ctors[1].type + indexedVecType.ctors[1].type where + contextRun := by + simpa [consCandidate, consRootContext] using indexedVecSemanticCtorContextRun + venv_eq := rfl + lparams_eq := rfl + vlctx_eq := rfl + source_tr := indexedVecSemanticConsSourceTr + view_tr := indexedVecSemanticConsViewTr + whnfFuel := 9999 + whnfDepth := rfl + +def indexedVecSemanticFamilySemanticRootRun : + TypeChecker.CandidateExprSemanticRootRun natFinalEnv [`u] + indexedVecFamilyCandidate indexedVecType.type := + indexedVecSemanticFamilyRootRun.semanticOfIdentity + indexedVecFamilyCandidate_identity + +def indexedVecSemanticNilSemanticRootRun : + TypeChecker.CandidateExprSemanticRootRun indexedVecTypeEnv [`u] + nilCandidate indexedVecType.ctors[0].type := + indexedVecSemanticNilRootRun.semanticOfIdentity nilCandidate_identity + +def indexedVecSemanticConsSemanticRootRun : + TypeChecker.CandidateExprSemanticRootRun indexedVecTypeEnv [`u] + consCandidate indexedVecType.ctors[1].type := + indexedVecSemanticConsRootRun.semanticOfIdentity consCandidate_identity + +def indexedVecSemanticNilConstructorSemanticRun : + VInductDecl.CandidateConstructorSemanticRun indexedVecTypeEnv [`u] + indexedVecNilConstructorCandidate indexedVecType.ctors[0] where + name_eq := rfl + uvars_eq := rfl + type := indexedVecSemanticNilSemanticRootRun + +def indexedVecSemanticConsConstructorSemanticRun : + VInductDecl.CandidateConstructorSemanticRun indexedVecTypeEnv [`u] + indexedVecConsConstructorCandidate indexedVecType.ctors[1] where + name_eq := rfl + uvars_eq := rfl + type := indexedVecSemanticConsSemanticRootRun + +def indexedVecSemanticNilConstructorRun : + VInductDecl.CandidateConstructorRun indexedVecTypeEnv [`u] + indexedVecNilConstructorCandidate indexedVecType.ctors[0] := + indexedVecSemanticNilConstructorSemanticRun.root + +def indexedVecSemanticConsConstructorRun : + VInductDecl.CandidateConstructorRun indexedVecTypeEnv [`u] + indexedVecConsConstructorCandidate indexedVecType.ctors[1] := + indexedVecSemanticConsConstructorSemanticRun.root + +def indexedVecSemanticConstructorSemanticListRun : + VInductDecl.CandidateConstructorSemanticListRun indexedVecTypeEnv [`u] + indexedVecFamilyListCandidate.constructors indexedVecType.ctors := by + exact .cons indexedVecSemanticNilConstructorSemanticRun + (.cons indexedVecSemanticConsConstructorSemanticRun .nil) + +def indexedVecSemanticConstructorListRun : + VInductDecl.CandidateConstructorListRun indexedVecTypeEnv [`u] + indexedVecFamilyListCandidate.constructors indexedVecType.ctors := + indexedVecSemanticConstructorSemanticListRun.roots + +def indexedVecSemanticFamilySemanticRun : + VInductDecl.CandidateFamilySemanticRun natFinalEnv [`u] + indexedVecFamilyListCandidate indexedVecType where + name_eq := rfl + uvars_eq := rfl + type := indexedVecSemanticFamilySemanticRootRun + typeEnv := indexedVecTypeEnv + addType := rfl + constructors := indexedVecSemanticConstructorSemanticListRun + +def indexedVecSemanticFamilyRun : + VInductDecl.CandidateFamilyRun natFinalEnv [`u] + indexedVecFamilyListCandidate indexedVecType := + indexedVecSemanticFamilySemanticRun.root + +def indexedVecSemanticNormalizationCandidateRun : + VInductDecl.NormalizationCandidateRun natFinalEnv [`u] + indexedVecNormalizationCandidate indexedVecDecl where + raw := indexedVecType + raw_types_eq := rfl + uvars_eq := rfl + family := indexedVecSemanticFamilyRun + +/-- Reconstructing every family and constructor payload leaves the identity +IndexedVec declaration unchanged. -/ +theorem indexedVecSemantic_viewDecl_eq : + indexedVecSemanticNormalizationCandidateRun.viewDecl = + indexedVecDecl := rfl + +/-- The candidate-derived normalization is exactly the analyzer's canonical +identity normalization, not merely propositionally interchangeable with it. -/ +theorem indexedVecSemantic_normalization_eq : + indexedVecSemanticNormalizationCandidateRun.normalization = + indexedVecChecked.identityGeneration.block.normalization := rfl + +theorem indexedVecSemanticFamilySpineRun : + TypeChecker.CandidateExprSpineRun natFinalEnv [`u] + indexedVecFamilyCandidate indexedVecType.type + indexedVecType.type := + indexedVecSemanticFamilySemanticRootRun.spine + indexedVecFamilyCandidate_identity.storedSpine + +theorem indexedVecSemanticNilSpineRun : + TypeChecker.CandidateExprSpineRun indexedVecTypeEnv [`u] + nilCandidate indexedVecType.ctors[0].type + indexedVecType.ctors[0].type := + indexedVecSemanticNilSemanticRootRun.spine + nilCandidate_identity.storedSpine + +theorem indexedVecSemanticConsSpineRun : + TypeChecker.CandidateExprSpineRun indexedVecTypeEnv [`u] + consCandidate indexedVecType.ctors[1].type + indexedVecType.ctors[1].type := + indexedVecSemanticConsSemanticRootRun.spine + consCandidate_identity.storedSpine + +theorem indexedVecSemanticCandidate_generationShape : + VInductDecl.normalizationCandidateGenerationShape indexedVecDecl + indexedVecType indexedVecNormalizationCandidate = true := by + change ((indexedVecFamilyCandidate.trace.storedSpine && true) && + ((nilCandidate.trace.storedSpine && true) && + ((consCandidate.trace.storedSpine && true) && true))) = true + rw [indexedVecFamilyCandidate_identity.storedSpine, + nilCandidate_identity.storedSpine, + consCandidate_identity.storedSpine] + rfl + +/-- The consolidated constructor gate rejects truncation in either direction +before dependent semantic generation is assembled. -/ +theorem indexedVecSemanticCandidate_missingRawShape_rejected : + VInductDecl.candidateConstructorSemanticGenerationShape indexedVecDecl + (.cons indexedVecNilConstructorCandidate .nil) [] = false := + rfl + +theorem indexedVecSemanticCandidate_extraRawShape_rejected : + VInductDecl.candidateConstructorSemanticGenerationShape indexedVecDecl + .nil [indexedVecType.ctors[0]] = false := + rfl + +def indexedVecSemanticProducedGenerationShapeCandidate : + VInductDecl.ProducedGenerationShapeCandidate indexedVecDecl indexedVecType + indexedVecKernelType 0 false indexedVecFamilyCandidateContext where + candidate := indexedVecNormalizationCandidate + produced := indexedVecNormalizationCandidateProduced + shape := indexedVecSemanticCandidate_generationShape + +/-- The strengthened outer gate retains the complete parameter/index and +ordered `nil`/`cons` generation layout in the same produced result. -/ +theorem indexedVecSemanticGenerationShapeCandidate_produced : + VInductDecl.produceGenerationShapeCandidate indexedVecDecl indexedVecType + indexedVecKernelType 0 false indexedVecFamilyCandidateContext = + .ok indexedVecSemanticProducedGenerationShapeCandidate := by + have produced : + AddInductive.buildNormalizationCandidate indexedVecDecl.nparams + [indexedVecKernelType] 0 false indexedVecFamilyCandidateContext = + .ok indexedVecNormalizationCandidate := + indexedVecNormalizationCandidateProduced + simpa only [indexedVecSemanticProducedGenerationShapeCandidate] using + VInductDecl.produceGenerationShapeCandidate_eq_ok + (source := indexedVecDecl) (raw := indexedVecType) + produced indexedVecSemanticCandidate_generationShape + +private theorem indexedVecSemanticCandidate_analysis + (normalization : VInductDecl.NormalizationCandidateSemanticRun + natFinalEnv [`u] indexedVecNormalizationCandidate indexedVecDecl) : + normalization.root.normalization.generation? = + some indexedVecChecked.identityGeneration := by + let reference : VInductDecl.NormalizationCandidateSemanticRun natFinalEnv + [`u] indexedVecNormalizationCandidate indexedVecDecl := { + raw := indexedVecType + raw_types_eq := rfl + uvars_eq := rfl + family := indexedVecSemanticFamilySemanticRun } + rw [indexedVecStagedPreFamilyInput.normalization_eq normalization reference] + rfl + +/-- The parameter/index and two-constructor fixture closes through the same +generic staged-owner theorem without supplying its semantic hierarchy. -/ +theorem indexedVecSemanticExactProducedGenerationCandidatePackage_exists : + Nonempty (VInductDecl.ExactProducedGenerationCandidatePackage + natFinalEnv [`u] indexedVecSemanticProducedGenerationShapeCandidate + indexedVecChecked.identityGeneration) := + indexedVecSemanticProducedGenerationShapeCandidate + |>.exactProducedPackage_nonempty indexedVecStagedPreFamilyInput rfl + indexedVecChecked.identityGeneration indexedVecSemanticCandidate_analysis + +private def + indexedVecSemanticExactProducedGenerationCandidatePackage : + VInductDecl.ExactProducedGenerationCandidatePackage natFinalEnv [`u] + indexedVecSemanticProducedGenerationShapeCandidate + indexedVecChecked.identityGeneration := + indexedVecSemanticProducedGenerationShapeCandidate.exactProducedPackage + indexedVecStagedPreFamilyInput rfl indexedVecChecked.identityGeneration + indexedVecSemanticCandidate_analysis + +def indexedVecSemanticGenerationCandidateSemanticRun : + VInductDecl.GenerationCandidateSemanticRun + indexedVecSemanticExactProducedGenerationCandidatePackage.normalization + indexedVecChecked.identityGeneration := + indexedVecSemanticExactProducedGenerationCandidatePackage.semantic + +def indexedVecSemanticGenerationCandidateRun : + VInductDecl.GenerationCandidateRun + indexedVecSemanticExactProducedGenerationCandidatePackage.normalization.root + indexedVecChecked.identityGeneration := + indexedVecSemanticGenerationCandidateSemanticRun.run + +def indexedVecSemanticGenerationCandidatePackage : + VInductDecl.GenerationCandidatePackage natFinalEnv [`u] := + indexedVecSemanticGenerationCandidateSemanticRun.package + +def indexedVecSemanticProducedGenerationCandidatePackage : + VInductDecl.ProducedGenerationCandidatePackage natFinalEnv [`u] := + indexedVecSemanticExactProducedGenerationCandidatePackage.package + +def indexedVecSemanticGenerationCertificate : + indexedVecDecl.GenerationCertificate natFinalEnv where + generation := indexedVecChecked.identityGeneration + wf := + indexedVecSemanticExactProducedGenerationCandidatePackage.semantic.run.wf + +theorem indexedVecSemantic_addInductCertified : + natFinalEnv.addInductCertified indexedVecSemanticGenerationCertificate = + some indexedVecFinalEnv := by + rfl + +theorem indexedVecSemanticCertified_trace : + Nonempty (VEnv.AddInductGenerationTrace natFinalEnv + indexedVecFinalEnv indexedVecChecked.identityGeneration) := + VEnv.addInductCertified_trace indexedVecSemantic_addInductCertified + +theorem indexedVecSemanticCertified_ordered : + indexedVecFinalEnv.Ordered := + VEnv.addInductCertified_WF nat_env_wf.ordered + indexedVecSemantic_addInductCertified + +def indexedVecSemanticAddInductTraceChecked : + AddInductTrace natMap natFinalEnv indexedVecDecl indexedVecMap + indexedVecFinalEnv := by + refine indexedVecSemanticProducedGenerationCandidatePackage.package.addInductTrace + indexedVecTypeMap indexedVecTypeEnv indexedVecCtorMap + indexedVecCtorEnv indexedVecRecEnv ?_ ?_ ?_ ?_ ⟨rfl⟩ + · exact { + info := indexedVecInfo + kind_eq := by simp [indexedVecInfo, InductConstantKind.Matches] + tr := indexedVecInfo_tr + map_fresh := by + rw [show + indexedVecSemanticProducedGenerationCandidatePackage.package.generation.block.sourceType.name = + ``IndexedVec by rfl] + exact indexedVecType_fresh + env_add := rfl + map_add := rfl } + · refine .cons (m₂ := indexedVecNilMap) + (env₂ := indexedVecNilEnv) ?_ ?_ + · exact { + info := indexedVecNilInfo + kind_eq := by simp [indexedVecNilInfo, InductConstantKind.Matches] + tr := indexedVecNilInfo_tr + map_fresh := by simpa [indexedVecType] using indexedVecNil_fresh + env_add := rfl + map_add := rfl } + · refine .cons ?_ .nil + exact { + info := indexedVecConsInfo + kind_eq := by simp [indexedVecConsInfo, InductConstantKind.Matches] + tr := indexedVecConsInfo_tr + map_fresh := by simpa [indexedVecType] using indexedVecCons_fresh + env_add := rfl + map_add := rfl } + · exact { + info := indexedVecRecInfo + kind_eq := by simp [indexedVecRecInfo, InductConstantKind.Matches] + tr := indexedVecRecInfo_tr + map_fresh := by + rw [show + (inductGenerationRecVal + indexedVecSemanticProducedGenerationCandidatePackage.package.generation).name = + ``IndexedVec.rec by rfl] + exact indexedVecRec_fresh + env_add := rfl + map_add := rfl } + · decide + +theorem indexedVecSemantic_addInduct_checked : + AddInduct natMap natFinalEnv indexedVecDecl indexedVecMap + indexedVecFinalEnv := + ⟨indexedVecSemanticAddInductTraceChecked⟩ + +theorem indexedVecSemantic_trEnv'_checked : + TrEnv' .safe indexedVecMap false indexedVecFinalEnv := + .induct indexedVecSemantic_addInduct_checked nat_trEnv' + +theorem indexedVecSemantic_env_wf_checked : indexedVecFinalEnv.WF := + indexedVecSemantic_trEnv'_checked.wf + +theorem indexedVecSemantic_aligned_checked : + Aligned .safe indexedVecMap indexedVecFinalEnv := + indexedVecSemantic_trEnv'_checked.aligned + +/- +The semantic assembly, executable producer, and final E1 replay intentionally +inherit the existing transitional verifier closure. These guards make +additions to that closure visible at the public roots of this module. +-/ +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVecProducedSemanticHierarchy_exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVecProducedPostFamilySemantic_exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVecProducedPreFamilySemantic_exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVecProducedSemanticHierarchy_constructorHeaders [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVecReorderedView_rejected [propext] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVecSemanticCandidate_missingRawShape_rejected [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVecSemanticCandidate_extraRawShape_rejected [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVecSemanticGenerationShapeCandidate_produced [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVecSemanticExactProducedGenerationCandidatePackage_exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVecSemanticGenerationCandidateSemanticRun [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVecSemanticProducedGenerationCandidatePackage [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVecSemantic_trEnv'_checked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +end Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/InductiveFixtures.lean b/Ix/Theory/Named/Verify/Environment/InductiveFixtures.lean new file mode 100644 index 000000000..0d9cb3079 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/InductiveFixtures.lean @@ -0,0 +1,12189 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Verify.Environment.Lemmas +import Ix.Theory.Named.Verify.Environment.ConstructorValidation +import Ix.Theory.Named.Reference.Inductive.Add +import Ix.Theory.Named.Meta +import Ix.Theory.Named.InductiveFixtures +import Ix.Theory.Named.Typing.Meta + +open Ix.Theory (VLevel) + +/-! End-to-end replay fixtures for inductive environment alignment. + +The Theory fixtures compare generated recursors and iota rules with Lean's +kernel declarations. This module closes the next bridge: it quotes the actual +`ConstantInfo` metadata, translates each metadata type in the precise +intermediate Theory environment, constructs `AddInduct`, and drives the live +`TrEnv'.induct` case. -/ + +namespace Ix.Theory.Named.InductiveReplayFixtures +open Lean Meta Elab Term +open Ix.Theory.Named.InductiveFixtures + +/- These instances are used only by the elaborators below to quote the kernel +metadata returned by `getConstInfo`. -/ +deriving instance ToExpr for ConstantVal +deriving instance ToExpr for InductiveVal +deriving instance ToExpr for ConstructorVal +deriving instance ToExpr for RecursorRule +deriving instance ToExpr for RecursorVal +deriving instance ToExpr for ReducibilityHints +deriving instance ToExpr for DefinitionSafety +deriving instance ToExpr for DefinitionVal + +syntax "kernelInductInfo%" ident : term +syntax "kernelCtorInfo%" ident : term +syntax "kernelRecInfo%" ident : term +syntax "kernelRecRuleRhs%" ident num : term +syntax "kernelDefVal%" ident : term + +elab_rules : term + | `(kernelInductInfo% $n:ident) => do + let name ← realizeGlobalConstNoOverloadWithInfo n + let .inductInfo info ← getConstInfo name + | throwError "expected inductive metadata for {name}" + return mkApp (mkConst ``ConstantInfo.inductInfo) (toExpr info) + | `(kernelCtorInfo% $n:ident) => do + let name ← realizeGlobalConstNoOverloadWithInfo n + let .ctorInfo info ← getConstInfo name + | throwError "expected constructor metadata for {name}" + return mkApp (mkConst ``ConstantInfo.ctorInfo) (toExpr info) + | `(kernelRecInfo% $n:ident) => do + let name ← realizeGlobalConstNoOverloadWithInfo n + let .recInfo info ← getConstInfo name + | throwError "expected recursor metadata for {name}" + return mkApp (mkConst ``ConstantInfo.recInfo) (toExpr info) + | `(kernelDefVal% $n:ident) => do + let name ← realizeGlobalConstNoOverloadWithInfo n + let .defnInfo info ← getConstInfo name + | throwError "expected definition metadata for {name}" + return toExpr info + +/-- Quote one kernel recursor-rule RHS using the rule's own universe-parameter +order. This lets replay fixtures compare implementation metadata with the +Theory equation generator by reduction, including lambdas under recursive Pi +arguments. -/ +elab_rules : term + | `(kernelRecRuleRhs% $n:ident $i:num) => do + let name ← realizeGlobalConstNoOverloadWithInfo n + let .recInfo info ← getConstInfo name + | throwError "expected recursor metadata for {name}" + let some rule := info.rules[i.getNat]? + | throwError "missing recursor rule {i.getNat} for {name}" + let rhs ← Ix.Theory.Named.Meta.expandExpr rule.rhs + let rhs ← Ix.Theory.Named.Meta.ofExpr info.levelParams {} rhs + return toExpr rhs + +/- Construct the representation-only half of a metadata-type translation. +The `to_trExprS` theorem supplies every typing premise from the declaration's +actual Theory `WF` evidence. -/ +syntax "tr_type_expr_tac" : tactic +macro_rules + | `(tactic| tr_type_expr_tac) => `(tactic| + first + | apply TrTypeExpr.bvar; rfl + | apply TrTypeExpr.sort; rfl + | apply TrTypeExpr.const <;> + (first | assumption | rfl | (dsimp; simp [VLevel.params'])) + | apply TrTypeExpr.app <;> tr_type_expr_tac + | apply TrTypeExpr.mdata; tr_type_expr_tac + | apply TrTypeExpr.forallE <;> tr_type_expr_tac) + +local instance : Inhabited VEnv := ⟨.empty⟩ + +/-! ## Nat -/ + +/-- Kernel metadata, captured at elaboration rather than reconstructed by the +fixture. A change in Lean's emitted record is therefore a compile failure. -/ +def natInfo : ConstantInfo := kernelInductInfo% Nat +def natZeroInfo : ConstantInfo := kernelCtorInfo% Nat.zero +def natSuccInfo : ConstantInfo := kernelCtorInfo% Nat.succ +def natRecInfo : ConstantInfo := kernelRecInfo% Nat.rec +def natZeroKernelRuleRhs : VExpr := kernelRecRuleRhs% Nat.rec 0 +def natSuccKernelRuleRhs : VExpr := kernelRecRuleRhs% Nat.rec 1 + +example : natInfo.name = ``Nat := rfl +example : natZeroInfo.name = ``Nat.zero := rfl +example : natSuccInfo.name = ``Nat.succ := rfl +example : natRecInfo.name = ``Nat.rec := rfl +example : natZeroKernelRuleRhs = + natChecked.identityGeneration.generatedRules[0].rhs := rfl +example : natSuccKernelRuleRhs = + natChecked.identityGeneration.generatedRules[1].rhs := rfl + +def natTypeEnv := (VEnv.empty.addConst natType.name natType.toVConstant).get! +def natZeroEnv := + (natTypeEnv.addConst natType.ctors[0].name natType.ctors[0].toVConstant).get! +def natCtorEnv := + (natZeroEnv.addConst natType.ctors[1].name natType.ctors[1].toVConstant).get! +def natRecEnv := + (natCtorEnv.addConst ``Nat.rec (VInductDecl.recConst 0 ``Nat 0 natType)).get! + +theorem natTypeEnv_ordered : natTypeEnv.Ordered := by + refine .const .empty ?_ rfl + exact ⟨.succ (.succ .zero), VEnv.HasType.sort (by decide)⟩ + +theorem natZeroEnv_ordered : natZeroEnv.Ordered := by + refine .const (n := natType.ctors[0].name) (ci := natType.ctors[0].toVConstant) + natTypeEnv_ordered ?_ rfl + have hNat : natTypeEnv.constants ``Nat = some natType.toVConstant := rfl + exact ⟨.succ .zero, by type_tac⟩ + +theorem natSucc_wf : natType.ctors[1].toVConstant.WF natZeroEnv := by + have hNat : natZeroEnv.constants ``Nat = some natType.toVConstant := rfl + refine ⟨.imax (.succ .zero) (.succ .zero), ?_⟩ + refine VEnv.HasType.forallE (u := .succ .zero) (v := .succ .zero) ?_ ?_ + · type_tac + · type_tac + +theorem natCtorEnv_ordered : natCtorEnv.Ordered := by + exact .const (n := natType.ctors[1].name) (ci := natType.ctors[1].toVConstant) + natZeroEnv_ordered natSucc_wf rfl + +/-- `Nat` satisfies the public Stage-3 declaration contract without a fixture +assumption. -/ +theorem natDecl_wf : natDecl.WF VEnv.empty := by + refine ⟨rfl, ?_⟩ + intro ty hty + have hty' : ty = natType := List.mem_singleton.1 (by simpa [natDecl] using hty) + subst ty + refine ⟨?_, ?_⟩ + · change True + trivial + intro c hc + rcases List.mem_cons.1 hc with rfl | hc + · constructor + · change True + trivial + · exact .nil + · have hc' := List.mem_singleton.1 hc + subst c + constructor + · refine ⟨.inl rfl, ?_, trivial⟩ + intro + exact .nil + · exact .nil + +/-- The exact intermediate invariant used to type the generated recursor. -/ +theorem natStage3 : + VInductDecl.Stage3Env natCtorEnv 0 ``Nat 0 (.succ .zero) natType := by + refine { + ord := natCtorEnv_ordered + hl := by decide + hsort := rfl + hlen := rfl + hT := rfl + hcs := ?_ + htel := ?_ + hs3 := ?_ + hparams := ?_ + hfields := ?_ + hresult := ?_ } + · intro c hc + rcases List.mem_cons.1 hc with rfl | hc + · rfl + · have hc' := List.mem_singleton.1 hc + subst c + rfl + · intro c hc + rcases List.mem_cons.1 hc with rfl | hc + · rfl + · have hc' := List.mem_singleton.1 hc + subst c + rfl + · intro c hc + rcases List.mem_cons.1 hc with rfl | hc + · rfl + · have hc' := List.mem_singleton.1 hc + subst c + rfl + · change True + trivial + · intro c hc + rcases List.mem_cons.1 hc with rfl | hc + · change True + trivial + · have hc' := List.mem_singleton.1 hc + subst c + refine ⟨.inl rfl, ?_, trivial⟩ + intro + exact .nil + · intro c hc + rcases List.mem_cons.1 hc with rfl | hc + · exact .nil + · have hc' := List.mem_singleton.1 hc + subst c + exact .nil + +theorem natInfo_tr : + TrConstVal .safe VEnv.empty natInfo natType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + exact .sort rfl + +theorem natZeroInfo_tr : + TrConstVal .safe natTypeEnv natZeroInfo natType.ctors[0] := by + have hNat : natTypeEnv.constants ``Nat = some natType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr natTypeEnv natZeroInfo.levelParams [] + natZeroInfo.type natType.ctors[0].type := by tr_type_expr_tac + exact hshape.to_trExprS natTypeEnv_ordered trivial + ⟨.sort (.succ .zero), by type_tac⟩ + +theorem natSuccInfo_tr : + TrConstVal .safe natZeroEnv natSuccInfo natType.ctors[1] := by + have hNat : natZeroEnv.constants ``Nat = some natType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr natZeroEnv natSuccInfo.levelParams [] + natSuccInfo.type natType.ctors[1].type := by + exact .forallE (.const hNat rfl rfl) (.const hNat rfl rfl) + exact hshape.to_trExprS natZeroEnv_ordered trivial + ⟨.sort (.imax (.succ .zero) (.succ .zero)), by + refine VEnv.HasType.forallE (u := .succ .zero) (v := .succ .zero) ?_ ?_ + · type_tac + · type_tac⟩ + +theorem natRecInfo_tr : + TrConstVal .safe natCtorEnv natRecInfo (inductRecVal natDecl natType) := by + have hNat : natCtorEnv.constants ``Nat = some natType.toVConstant := rfl + have hZero : + natCtorEnv.constants ``Nat.zero = some natType.ctors[0].toVConstant := rfl + have hSucc : + natCtorEnv.constants ``Nat.succ = some natType.ctors[1].toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr natCtorEnv natRecInfo.levelParams [] natRecInfo.type + (inductRecVal natDecl natType).type := by tr_type_expr_tac + obtain ⟨u, hrec⟩ := natStage3.recConst_wf + exact hshape.to_trExprS natCtorEnv_ordered trivial ⟨.sort u, hrec⟩ + +def natTypeMap : ConstMap := ({} : ConstMap).insert ``Nat natInfo +def natZeroMap : ConstMap := natTypeMap.insert ``Nat.zero natZeroInfo +def natCtorMap : ConstMap := natZeroMap.insert ``Nat.succ natSuccInfo +def natMap : ConstMap := natCtorMap.insert ``Nat.rec natRecInfo +def natFinalEnv : VEnv := + (VInductDecl.rules 0 ``Nat 0 natType).foldl VEnv.addDefEq natRecEnv + +theorem natType_fresh : ({} : ConstMap).find? ``Nat = none := by + simp [SMap.find?] + +theorem natTypeMap_wf : natTypeMap.WF := + SMap.WF.empty.insert _ _ natType_fresh + +theorem natZero_fresh : natTypeMap.find? ``Nat.zero = none := by + rw [natTypeMap, SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem natZeroMap_wf : natZeroMap.WF := + natTypeMap_wf.insert _ _ natZero_fresh + +theorem natSucc_fresh : natZeroMap.find? ``Nat.succ = none := by + rw [natZeroMap, natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem natCtorMap_wf : natCtorMap.WF := + natZeroMap_wf.insert _ _ natSucc_fresh + +theorem natRec_fresh : natCtorMap.find? ``Nat.rec = none := by + rw [natCtorMap, natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +/-- A concrete `AddInduct` witness whose implementation side consists of the +actual kernel metadata above and whose Theory side is the Stage-3 Nat +transaction. -/ +theorem nat_addInduct : + AddInduct ({} : ConstMap) VEnv.empty natDecl natMap natFinalEnv := by + refine ⟨{ + generation := natChecked.identityGeneration + generation_wf := + (natChecked.wf_of_decl natDecl_wf).identityGeneration .empty + typeMap := natTypeMap + typeEnv := natTypeEnv + ctorMap := natCtorMap + ctorEnv := natCtorEnv + recEnv := natRecEnv + addType := { + info := natInfo + kind_eq := by simp [natInfo, InductConstantKind.Matches] + tr := natInfo_tr + map_fresh := by + change ({} : ConstMap).find? ``Nat = none + exact natType_fresh + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := natRecInfo + kind_eq := by simp [natRecInfo, InductConstantKind.Matches] + tr := natRecInfo_tr + map_fresh := by + change natCtorMap.find? ``Nat.rec = none + exact natRec_fresh + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ }⟩ + exact .cons { + info := natZeroInfo + kind_eq := by simp [natZeroInfo, InductConstantKind.Matches] + tr := natZeroInfo_tr + map_fresh := by simpa [natType] using natZero_fresh + env_add := rfl + map_add := rfl } + (.cons { + info := natSuccInfo + kind_eq := by simp [natSuccInfo, InductConstantKind.Matches] + tr := natSuccInfo_tr + map_fresh := by + change natZeroMap.find? ``Nat.succ = none + exact natSucc_fresh + env_add := rfl + map_add := rfl } .nil) + +/-- The formerly impossible `TrEnv'.induct` branch, instantiated with a real +Lean declaration transaction. -/ +theorem nat_trEnv' {safety : DefinitionSafety} : + TrEnv' safety natMap false natFinalEnv := + .induct nat_addInduct .empty + +theorem nat_final_matches_addInduct : + VEnv.empty.addInduct natDecl = some natFinalEnv := + rfl + +/-- Theory-only ordering evidence for the Nat dependency environment. This +keeps later inductive preservation proofs entirely within the Theory layer. -/ +theorem natFinalEnv_ordered : natFinalEnv.Ordered := + VEnv.addInductGeneration_WF .empty + ((natChecked.wf_of_decl natDecl_wf).identityGeneration .empty) rfl + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.natFinalEnv_ordered [propext, Classical.choice, Quot.sound] + +theorem nat_env_wf : natFinalEnv.WF := (nat_trEnv' (safety := .safe)).wf + +theorem nat_aligned : Aligned .safe natMap natFinalEnv := (nat_trEnv' (safety := .safe)).aligned + +theorem nat_type_map_lookup : natMap.find? ``Nat = some natInfo := by + rw [natMap, natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, natTypeMap_wf.find?_insert, + natTypeMap, SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + rfl + +theorem nat_type_env_lookup : + natFinalEnv.constants ``Nat = some natType.toVConstant := rfl + +theorem nat_type_lookup_unique : + natInfo.name = ``Nat ∧ + TrConstant .safe natFinalEnv natInfo natType.toVConstant := + nat_aligned.find?_uniq nat_type_map_lookup nat_type_env_lookup + +theorem nat_succ_map_lookup : natMap.find? ``Nat.succ = some natSuccInfo := by + rw [natMap, natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert] + rfl + +theorem nat_succ_env_lookup : natFinalEnv.constants ``Nat.succ = + some natType.ctors[1].toVConstant := rfl + +theorem nat_succ_lookup_unique : + natSuccInfo.name = ``Nat.succ ∧ + TrConstant .safe natFinalEnv natSuccInfo + natType.ctors[1].toVConstant := + nat_aligned.find?_uniq nat_succ_map_lookup nat_succ_env_lookup + +theorem nat_rec_map_lookup : natMap.find? ``Nat.rec = some natRecInfo := by + rw [natMap, natCtorMap_wf.find?_insert] + rfl + +theorem nat_rec_env_lookup : natFinalEnv.constants ``Nat.rec = + some (VInductDecl.recConst 0 ``Nat 0 natType) := rfl + +/-- Lookup uniqueness is tested at the generated recursor, after all iota +rules have been installed. -/ +theorem nat_rec_lookup_unique : + natRecInfo.name = ``Nat.rec ∧ + TrConstant .safe natFinalEnv natRecInfo + (VInductDecl.recConst 0 ``Nat 0 natType) := + nat_aligned.find?_uniq nat_rec_map_lookup nat_rec_env_lookup + +/- This closure is now free of `sorryAx`; the persistent-map contracts come +from proving concrete `SMap` freshness. The fixture introduces no new axiom. -/ +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.nat_trEnv' [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +/-! ## A value-bearing prefix followed by Nat -/ + +/-- A dependency-free definition used to ensure value translation survives a +later inductive metadata transaction. -/ +def ReplaySeed : Type 1 := Type + +def seedKernelDef : DefinitionVal := kernelDefVal% ReplaySeed +def seedInfo : ConstantInfo := .defnInfo seedKernelDef + +def seedVal : VDefVal where + name := ``ReplaySeed + uvars := 0 + type := .sort (.succ (.succ .zero)) + value := .sort (.succ .zero) + +theorem seedInfo_tr : TrDefVal .safe VEnv.empty seedInfo seedVal := by + refine ⟨⟨⟨by decide, rfl, ?_⟩, rfl⟩, ?_⟩ + · exact .sort rfl + · exact .sort rfl + +theorem seedVal_wf : seedVal.WF VEnv.empty := + VEnv.HasType.sort (by decide) + +def seedConstEnv := (VEnv.empty.addConst seedVal.name seedVal.toVConstant).get! +def seedEnv := seedConstEnv.addDefEq seedVal.toDefEq +def seedMap : ConstMap := ({} : ConstMap).insert seedVal.name seedInfo + +theorem seed_fresh : ({} : ConstMap).find? seedVal.name = none := by + simp [seedVal, SMap.find?] + +theorem seed_trEnv' : TrEnv' .safe seedMap false seedEnv := + .defn (ci := seedKernelDef) (ci' := seedVal) seedInfo_tr seed_fresh + seedVal_wf rfl .empty + +theorem seed_map_lookup : seedMap.find? ``ReplaySeed = some seedInfo := by + rw [seedMap, SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + rfl + +theorem seed_le : VEnv.empty ≤ seedEnv := + (VEnv.addConst_le (show VEnv.empty.addConst seedVal.name seedVal.toVConstant = + some seedConstEnv from rfl)).trans VEnv.addDefEq_le + +theorem seedEnv_ordered : seedEnv.Ordered := seed_trEnv'.wf.ordered + +def seedNatTypeEnv := (seedEnv.addConst natType.name natType.toVConstant).get! +def seedNatZeroEnv := + (seedNatTypeEnv.addConst natType.ctors[0].name natType.ctors[0].toVConstant).get! +def seedNatCtorEnv := + (seedNatZeroEnv.addConst natType.ctors[1].name natType.ctors[1].toVConstant).get! +def seedNatRecEnv := + (seedNatCtorEnv.addConst ``Nat.rec (VInductDecl.recConst 0 ``Nat 0 natType)).get! + +theorem natTypeEnv_le_seedNatTypeEnv : natTypeEnv ≤ seedNatTypeEnv := + VEnv.LE.addConst (n := natType.name) (ci := natType.toVConstant) seed_le rfl rfl + +theorem natZeroEnv_le_seedNatZeroEnv : natZeroEnv ≤ seedNatZeroEnv := + VEnv.LE.addConst (n := natType.ctors[0].name) + (ci := natType.ctors[0].toVConstant) natTypeEnv_le_seedNatTypeEnv rfl rfl + +theorem natCtorEnv_le_seedNatCtorEnv : natCtorEnv ≤ seedNatCtorEnv := + VEnv.LE.addConst (n := natType.ctors[1].name) + (ci := natType.ctors[1].toVConstant) natZeroEnv_le_seedNatZeroEnv rfl rfl + +theorem seedNatTypeEnv_ordered : seedNatTypeEnv.Ordered := by + refine .const (n := natType.name) (ci := natType.toVConstant) + seedEnv_ordered ?_ rfl + exact ⟨.succ (.succ .zero), VEnv.HasType.sort (by decide)⟩ + +theorem seedNatZeroEnv_ordered : seedNatZeroEnv.Ordered := by + refine .const (n := natType.ctors[0].name) (ci := natType.ctors[0].toVConstant) + seedNatTypeEnv_ordered ?_ rfl + have hNat : seedNatTypeEnv.constants ``Nat = some natType.toVConstant := rfl + exact ⟨.succ .zero, by type_tac⟩ + +theorem seedNatSucc_wf : natType.ctors[1].toVConstant.WF seedNatZeroEnv := by + have hNat : seedNatZeroEnv.constants ``Nat = some natType.toVConstant := rfl + refine ⟨.imax (.succ .zero) (.succ .zero), ?_⟩ + refine VEnv.HasType.forallE (u := .succ .zero) (v := .succ .zero) ?_ ?_ + · type_tac + · type_tac + +theorem seedNatCtorEnv_ordered : seedNatCtorEnv.Ordered := + .const (n := natType.ctors[1].name) (ci := natType.ctors[1].toVConstant) + seedNatZeroEnv_ordered seedNatSucc_wf rfl + +theorem seedNatInfo_tr : + TrConstVal .safe seedEnv natInfo natType.toVConstVal := + natInfo_tr.mono seed_le + +theorem seedNatZeroInfo_tr : + TrConstVal .safe seedNatTypeEnv natZeroInfo natType.ctors[0] := + natZeroInfo_tr.mono natTypeEnv_le_seedNatTypeEnv + +theorem seedNatSuccInfo_tr : + TrConstVal .safe seedNatZeroEnv natSuccInfo natType.ctors[1] := + natSuccInfo_tr.mono natZeroEnv_le_seedNatZeroEnv + +theorem seedNatRecInfo_tr : + TrConstVal .safe seedNatCtorEnv natRecInfo (inductRecVal natDecl natType) := + natRecInfo_tr.mono natCtorEnv_le_seedNatCtorEnv + +def seedNatTypeMap : ConstMap := seedMap.insert ``Nat natInfo +def seedNatZeroMap : ConstMap := seedNatTypeMap.insert ``Nat.zero natZeroInfo +def seedNatCtorMap : ConstMap := seedNatZeroMap.insert ``Nat.succ natSuccInfo +def seedNatMap : ConstMap := seedNatCtorMap.insert ``Nat.rec natRecInfo +def seedNatFinalEnv : VEnv := + (VInductDecl.rules 0 ``Nat 0 natType).foldl VEnv.addDefEq seedNatRecEnv + +theorem seedMap_wf : seedMap.WF := seed_trEnv'.map_wf + +theorem seedNatType_fresh : seedMap.find? ``Nat = none := by + rw [seedMap, SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [seedVal, SMap.find?] + +theorem seedNatTypeMap_wf : seedNatTypeMap.WF := + seedMap_wf.insert _ _ seedNatType_fresh + +theorem seedNatZero_fresh : seedNatTypeMap.find? ``Nat.zero = none := by + rw [seedNatTypeMap, seedMap_wf.find?_insert, seedMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [seedVal, SMap.find?] + +theorem seedNatZeroMap_wf : seedNatZeroMap.WF := + seedNatTypeMap_wf.insert _ _ seedNatZero_fresh + +theorem seedNatSucc_fresh : seedNatZeroMap.find? ``Nat.succ = none := by + rw [seedNatZeroMap, seedNatTypeMap_wf.find?_insert, seedNatTypeMap, + seedMap_wf.find?_insert, seedMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [seedVal, SMap.find?] + +theorem seedNatCtorMap_wf : seedNatCtorMap.WF := + seedNatZeroMap_wf.insert _ _ seedNatSucc_fresh + +theorem seedNatRec_fresh : seedNatCtorMap.find? ``Nat.rec = none := by + rw [seedNatCtorMap, seedNatZeroMap_wf.find?_insert, seedNatZeroMap, + seedNatTypeMap_wf.find?_insert, seedNatTypeMap, seedMap_wf.find?_insert, + seedMap, SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [seedVal, SMap.find?] + +theorem seedNat_addInduct : + AddInduct seedMap seedEnv natDecl seedNatMap seedNatFinalEnv := by + refine ⟨{ + generation := natChecked.identityGeneration + generation_wf := + (natChecked.wf_of_decl (natDecl_wf.mono seed_le)).identityGeneration + seedEnv_ordered + typeMap := seedNatTypeMap + typeEnv := seedNatTypeEnv + ctorMap := seedNatCtorMap + ctorEnv := seedNatCtorEnv + recEnv := seedNatRecEnv + addType := { + info := natInfo + kind_eq := by simp [natInfo, InductConstantKind.Matches] + tr := seedNatInfo_tr + map_fresh := by + change seedMap.find? ``Nat = none + exact seedNatType_fresh + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := natRecInfo + kind_eq := by simp [natRecInfo, InductConstantKind.Matches] + tr := seedNatRecInfo_tr + map_fresh := by + change seedNatCtorMap.find? ``Nat.rec = none + exact seedNatRec_fresh + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ }⟩ + exact .cons { + info := natZeroInfo + kind_eq := by simp [natZeroInfo, InductConstantKind.Matches] + tr := seedNatZeroInfo_tr + map_fresh := by simpa [natType] using seedNatZero_fresh + env_add := rfl + map_add := rfl } + (.cons { + info := natSuccInfo + kind_eq := by simp [natSuccInfo, InductConstantKind.Matches] + tr := seedNatSuccInfo_tr + map_fresh := by + change seedNatZeroMap.find? ``Nat.succ = none + exact seedNatSucc_fresh + env_add := rfl + map_add := rfl } .nil) + +theorem seedNat_trEnv' : TrEnv' .safe seedNatMap false seedNatFinalEnv := + .induct seedNat_addInduct seed_trEnv' + +theorem seedNat_seed_lookup : seedNatMap.find? ``ReplaySeed = some seedInfo := by + rw [seedNatMap, seedNatCtorMap_wf.find?_insert, seedNatCtorMap, + seedNatZeroMap_wf.find?_insert, seedNatZeroMap, + seedNatTypeMap_wf.find?_insert, seedNatTypeMap, seedMap_wf.find?_insert] + simpa [seedVal] using seed_map_lookup + +/-- A concrete regression for the formerly impossible `TrEnv'.of_value` +inductive branch: the value was inserted before Nat, so this theorem must pull +its lookup back through every Nat metadata insertion. -/ +theorem seed_after_nat_of_value : + TrExpr seedNatFinalEnv seedInfo.levelParams [] seedKernelDef.value + (.const seedInfo.name (VLevel.params seedInfo.levelParams.length)) := + seedNat_trEnv'.of_value (name := ``ReplaySeed) (ci := seedInfo) + (v := seedKernelDef.value) seedNat_seed_lookup (by decide) rfl + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.seed_after_nat_of_value [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +/-! ## Eq -/ + +/- `Eq` exercises parameters, a genuine index, Prop-valued elimination, and +the kernel/generated recursor universe permutation. -/ +def eqInfo : ConstantInfo := kernelInductInfo% Eq +def eqReflInfo : ConstantInfo := kernelCtorInfo% Eq.refl +def eqRecInfo : ConstantInfo := kernelRecInfo% Eq.rec +def eqReflKernelRuleRhs : VExpr := kernelRecRuleRhs% Eq.rec 0 + +example : eqReflKernelRuleRhs = + eqChecked.identityGeneration.generatedRules[0].rhs := rfl + +def eqTypeEnv := (VEnv.empty.addConst eqType.name eqType.toVConstant).get! +def eqCtorEnv := + (eqTypeEnv.addConst eqType.ctors[0].name eqType.ctors[0].toVConstant).get! +def eqRecEnv := + (eqCtorEnv.addConst ``Eq.rec (VInductDecl.recConst 1 ``Eq 2 eqType)).get! + +theorem eqType_wf : eqType.toVConstant.WF VEnv.empty := by + refine ⟨.imax (.succ (.param 0)) + (.imax (.param 0) (.imax (.param 0) (.succ .zero))), ?_⟩ + refine VEnv.HasType.forallE + (u := .succ (.param 0)) + (v := .imax (.param 0) (.imax (.param 0) (.succ .zero))) ?_ ?_ + · exact VEnv.HasType.sort (by decide) + · refine VEnv.HasType.forallE + (u := .param 0) (v := .imax (.param 0) (.succ .zero)) ?_ ?_ + · type_tac + · refine VEnv.HasType.forallE + (u := .param 0) (v := .succ .zero) ?_ ?_ + · type_tac + · exact VEnv.HasType.sort (by decide) + +theorem eqTypeEnv_ordered : eqTypeEnv.Ordered := + .const .empty eqType_wf rfl + +theorem eqDecl_wf : eqDecl.WF VEnv.empty := by + refine ⟨rfl, ?_⟩ + intro ty hty + have hty' : ty = eqType := List.mem_singleton.1 (by simpa [eqDecl] using hty) + subst ty + refine ⟨?_, ?_⟩ + · change VEnv.empty.OnTel 1 [] + [.sort (.param 0), .bvar 0, .bvar 1] + exact ⟨⟨.succ (.param 0), VEnv.HasType.sort (by decide)⟩, + ⟨⟨.param 0, by type_tac⟩, ⟨⟨.param 0, by type_tac⟩, trivial⟩⟩⟩ + · intro c hc + have hc' := List.mem_singleton.1 hc + subst c + constructor + · change True + trivial + · exact .cons (by type_tac) .nil + +theorem eqRefl_wf : eqType.ctors[0].toVConstant.WF eqTypeEnv := by + have hblock := eqDecl_wf.2 eqType (by simp [eqDecl]) + have hctor := hblock.2 eqType.ctors[0] (by simp) + have hle : VEnv.empty ≤ eqTypeEnv := VEnv.addConst_le rfl + have S0 : VInductDecl.Stage3Env eqTypeEnv 1 ``Eq 2 .zero + { eqType with ctors := [] } := { + ord := eqTypeEnv_ordered + hl := by decide + hsort := rfl + hlen := rfl + hT := rfl + hcs := by simp + htel := by simp + hs3 := by simp + hparams := hblock.1.mono hle + hfields := by simp + hresult := by simp } + exact S0.ctorType_isType' rfl rfl + (VInductDecl.fieldsWF_mono hle hctor.1) (hctor.2.mono hle) + +theorem eqCtorEnv_ordered : eqCtorEnv.Ordered := + .const (n := eqType.ctors[0].name) (ci := eqType.ctors[0].toVConstant) + eqTypeEnv_ordered eqRefl_wf rfl + +theorem eqStage3 : + VInductDecl.Stage3Env eqCtorEnv 1 ``Eq 2 .zero eqType := by + have hblock := eqDecl_wf.2 eqType (by simp [eqDecl]) + have hctor := hblock.2 eqType.ctors[0] (by simp) + have h0 : VEnv.empty ≤ eqTypeEnv := + VEnv.addConst_le (show VEnv.empty.addConst eqType.name eqType.toVConstant = + some eqTypeEnv from rfl) + have h1 : eqTypeEnv ≤ eqCtorEnv := + VEnv.addConst_le (show eqTypeEnv.addConst eqType.ctors[0].name + eqType.ctors[0].toVConstant = some eqCtorEnv from rfl) + have hle : VEnv.empty ≤ eqCtorEnv := h0.trans h1 + refine { + ord := eqCtorEnv_ordered + hl := by decide + hsort := rfl + hlen := rfl + hT := rfl + hcs := by + intro c hc + have hc' := List.mem_singleton.1 hc + subst c + rfl + htel := by + intro c hc + have hc' := List.mem_singleton.1 hc + subst c + rfl + hs3 := by + intro c hc + have hc' := List.mem_singleton.1 hc + subst c + rfl + hparams := hblock.1.mono hle + hfields := by + intro c hc + have hc' := List.mem_singleton.1 hc + subst c + exact VInductDecl.fieldsWF_mono hle hctor.1 + hresult := by + intro c hc + have hc' := List.mem_singleton.1 hc + subst c + exact hctor.2.mono hle } + +theorem eqInfo_tr : + TrConstVal .safe VEnv.empty eqInfo eqType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr VEnv.empty eqInfo.levelParams [] eqInfo.type + eqType.type := by tr_type_expr_tac + obtain ⟨u, htype⟩ := eqType_wf + exact hshape.to_trExprS .empty trivial ⟨.sort u, htype⟩ + +theorem eqReflInfo_tr : + TrConstVal .safe eqTypeEnv eqReflInfo eqType.ctors[0] := by + have hEq : eqTypeEnv.constants ``Eq = some eqType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr eqTypeEnv eqReflInfo.levelParams [] eqReflInfo.type + eqType.ctors[0].type := by tr_type_expr_tac + obtain ⟨u, htype⟩ := eqRefl_wf + exact hshape.to_trExprS eqTypeEnv_ordered trivial ⟨.sort u, htype⟩ + +theorem eqRecInfo_tr : + TrConstVal .safe eqCtorEnv eqRecInfo (inductRecVal eqDecl eqType) := by + have hEq : eqCtorEnv.constants ``Eq = some eqType.toVConstant := rfl + have hRefl : eqCtorEnv.constants ``Eq.refl = + some eqType.ctors[0].toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr eqCtorEnv eqRecInfo.levelParams [] eqRecInfo.type + (inductRecVal eqDecl eqType).type := by tr_type_expr_tac + obtain ⟨u, hrec⟩ := eqStage3.recConst_wf + exact hshape.to_trExprS eqCtorEnv_ordered trivial ⟨.sort u, hrec⟩ + +def eqTypeMap : ConstMap := ({} : ConstMap).insert ``Eq eqInfo +def eqCtorMap : ConstMap := eqTypeMap.insert ``Eq.refl eqReflInfo +def eqMap : ConstMap := eqCtorMap.insert ``Eq.rec eqRecInfo +def eqFinalEnv : VEnv := + (VInductDecl.rules 1 ``Eq 2 eqType).foldl VEnv.addDefEq eqRecEnv + +theorem eqType_fresh : ({} : ConstMap).find? ``Eq = none := by + simp [SMap.find?] + +theorem eqTypeMap_wf : eqTypeMap.WF := + SMap.WF.empty.insert _ _ eqType_fresh + +theorem eqRefl_fresh : eqTypeMap.find? ``Eq.refl = none := by + rw [eqTypeMap, SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem eqCtorMap_wf : eqCtorMap.WF := + eqTypeMap_wf.insert _ _ eqRefl_fresh + +theorem eqRec_fresh : eqCtorMap.find? ``Eq.rec = none := by + rw [eqCtorMap, eqTypeMap_wf.find?_insert, eqTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem eq_addInduct : + AddInduct ({} : ConstMap) VEnv.empty eqDecl eqMap eqFinalEnv := by + refine ⟨{ + generation := eqChecked.identityGeneration + generation_wf := + (eqChecked.wf_of_decl eqDecl_wf).identityGeneration .empty + typeMap := eqTypeMap + typeEnv := eqTypeEnv + ctorMap := eqCtorMap + ctorEnv := eqCtorEnv + recEnv := eqRecEnv + addType := { + info := eqInfo + kind_eq := by simp [eqInfo, InductConstantKind.Matches] + tr := eqInfo_tr + map_fresh := by + change ({} : ConstMap).find? ``Eq = none + exact eqType_fresh + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := eqRecInfo + kind_eq := by simp [eqRecInfo, InductConstantKind.Matches] + tr := eqRecInfo_tr + map_fresh := by + change eqCtorMap.find? ``Eq.rec = none + exact eqRec_fresh + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ }⟩ + exact .cons { + info := eqReflInfo + kind_eq := by simp [eqReflInfo, InductConstantKind.Matches] + tr := eqReflInfo_tr + map_fresh := by simpa [eqType] using eqRefl_fresh + env_add := rfl + map_add := rfl } .nil + +/-- Replay actual kernel `Eq` metadata through the formerly empty inductive +environment branch. -/ +theorem eq_trEnv' : TrEnv' .safe eqMap false eqFinalEnv := + .induct eq_addInduct .empty + +theorem eq_final_matches_addInduct : + VEnv.empty.addInduct eqDecl = some eqFinalEnv := + rfl + +theorem eq_env_wf : eqFinalEnv.WF := eq_trEnv'.wf + +theorem eq_aligned : Aligned .safe eqMap eqFinalEnv := eq_trEnv'.aligned + +theorem eq_type_map_lookup : eqMap.find? ``Eq = some eqInfo := by + rw [eqMap, eqCtorMap_wf.find?_insert, eqCtorMap, + eqTypeMap_wf.find?_insert, eqTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + rfl + +theorem eq_type_env_lookup : + eqFinalEnv.constants ``Eq = some eqType.toVConstant := rfl + +theorem eq_type_lookup_unique : + eqInfo.name = ``Eq ∧ + TrConstant .safe eqFinalEnv eqInfo eqType.toVConstant := + eq_aligned.find?_uniq eq_type_map_lookup eq_type_env_lookup + +theorem eq_refl_map_lookup : eqMap.find? ``Eq.refl = some eqReflInfo := by + rw [eqMap, eqCtorMap_wf.find?_insert, eqCtorMap, + eqTypeMap_wf.find?_insert] + rfl + +theorem eq_refl_env_lookup : eqFinalEnv.constants ``Eq.refl = + some eqType.ctors[0].toVConstant := rfl + +theorem eq_refl_lookup_unique : + eqReflInfo.name = ``Eq.refl ∧ + TrConstant .safe eqFinalEnv eqReflInfo + eqType.ctors[0].toVConstant := + eq_aligned.find?_uniq eq_refl_map_lookup eq_refl_env_lookup + +theorem eq_rec_map_lookup : eqMap.find? ``Eq.rec = some eqRecInfo := by + rw [eqMap, eqCtorMap_wf.find?_insert] + rfl + +theorem eq_rec_env_lookup : eqFinalEnv.constants ``Eq.rec = + some (VInductDecl.recConst 1 ``Eq 2 eqType) := rfl + +theorem eq_rec_lookup_unique : + eqRecInfo.name = ``Eq.rec ∧ + TrConstant .safe eqFinalEnv eqRecInfo + (VInductDecl.recConst 1 ``Eq 2 eqType) := + eq_aligned.find?_uniq eq_rec_map_lookup eq_rec_env_lookup + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.eq_trEnv' [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +/-! ## IndexedVec over the replayed Nat prefix -/ + +/- The fixture spells its changing index as `Nat.succ n` (and its base as +`Nat.zero`) so its kernel metadata has exactly the semantically relevant Nat +dependency prefix, rather than the unrelated `OfNat`/`HAdd` instance +implementation generated by notation. The declaration and every metadata +record below are still quoted from and checked against the real kernel +objects. -/ +def indexedVecInfo : ConstantInfo := kernelInductInfo% IndexedVec +def indexedVecNilInfo : ConstantInfo := kernelCtorInfo% IndexedVec.nil +def indexedVecConsInfo : ConstantInfo := kernelCtorInfo% IndexedVec.cons +def indexedVecRecInfo : ConstantInfo := kernelRecInfo% IndexedVec.rec +def indexedVecNilKernelRuleRhs : VExpr := + kernelRecRuleRhs% IndexedVec.rec 0 +def indexedVecConsKernelRuleRhs : VExpr := + kernelRecRuleRhs% IndexedVec.rec 1 + +example : indexedVecNilKernelRuleRhs = + indexedVecChecked.identityGeneration.generatedRules[0].rhs := rfl +example : indexedVecConsKernelRuleRhs = + indexedVecChecked.identityGeneration.generatedRules[1].rhs := rfl + +def indexedVecTypeEnv := + (natFinalEnv.addConst indexedVecType.name indexedVecType.toVConstant).get! +def indexedVecNilEnv := + (indexedVecTypeEnv.addConst indexedVecType.ctors[0].name + indexedVecType.ctors[0].toVConstant).get! +def indexedVecCtorEnv := + (indexedVecNilEnv.addConst indexedVecType.ctors[1].name + indexedVecType.ctors[1].toVConstant).get! +def indexedVecRecEnv := + (indexedVecCtorEnv.addConst ``IndexedVec.rec + (VInductDecl.recConst 1 ``IndexedVec 1 indexedVecType)).get! +theorem indexedVecType_wf : indexedVecType.toVConstant.WF natFinalEnv := by + have hNat : natFinalEnv.constants ``Nat = some natType.toVConstant := rfl + refine ⟨.imax (.succ (.succ (.param 0))) + (.imax (.succ .zero) (.succ (.succ (.param 0)))), ?_⟩ + refine VEnv.HasType.forallE + (u := .succ (.succ (.param 0))) + (v := .imax (.succ .zero) (.succ (.succ (.param 0)))) ?_ ?_ + · exact VEnv.HasType.sort (by decide) + · refine VEnv.HasType.forallE + (u := .succ .zero) (v := .succ (.succ (.param 0))) ?_ ?_ + · type_tac + · exact VEnv.HasType.sort (by decide) + +theorem indexedVecDecl_wf : indexedVecDecl.WF natFinalEnv := by + refine ⟨rfl, ?_⟩ + intro ty hty + have hty' : ty = indexedVecType := + List.mem_singleton.1 (by simpa [indexedVecDecl] using hty) + subst ty + refine ⟨?_, ?_⟩ + · change natFinalEnv.OnTel 1 [] + [.sort (.succ (.param 0)), .const ``Nat []] + have hNat : natFinalEnv.constants ``Nat = some natType.toVConstant := rfl + exact ⟨⟨.succ (.succ (.param 0)), VEnv.HasType.sort (by decide)⟩, + ⟨⟨.succ .zero, by type_tac⟩, trivial⟩⟩ + · intro c hc + rcases List.mem_cons.1 hc with rfl | hc + · constructor + · change True + trivial + · change natFinalEnv.SpineWF 1 + [.sort (.succ (.param 0))] + (.forallE (.const ``Nat []) (.sort (.succ (.param 0)))) + [.const ``Nat.zero []] (.sort (.succ (.param 0))) + have hNat : natFinalEnv.constants ``Nat = some natType.toVConstant := rfl + have hZero : natFinalEnv.constants ``Nat.zero = + some natType.ctors[0].toVConstant := rfl + exact .cons (by type_tac) .nil + · have hc' := List.mem_singleton.1 hc + subst c + constructor + · change VInductDecl.fieldsWF 1 ``IndexedVec 1 natFinalEnv + (VLevel.succ (VLevel.param 0)) [VExpr.const ``Nat []] + [VExpr.sort (VLevel.succ (VLevel.param 0))] 0 + [VExpr.const ``Nat [], VExpr.bvar 1, + VExpr.app (VExpr.app (VExpr.const ``IndexedVec [VLevel.param 0]) + (VExpr.bvar 2)) (VExpr.bvar 1)] + have hNat : natFinalEnv.constants ``Nat = some natType.toVConstant := rfl + constructor + · exact .inr (.inr ⟨rfl, .succ .zero, (by type_tac), + .inr (VLevel.succ_le_succ VLevel.zero_le)⟩) + constructor + · intro h + contradiction + constructor + · exact .inr (.inr ⟨rfl, .succ (.param 0), (by type_tac), + .inr (VLevel.le_refl _)⟩) + constructor + · intro h + contradiction + constructor + · exact .inl rfl + constructor + · intro _ + exact .cons (by type_tac) .nil + · trivial + · change natFinalEnv.SpineWF 1 + [VExpr.app (VExpr.app (VExpr.const ``IndexedVec [VLevel.param 0]) + (VExpr.bvar 2)) (VExpr.bvar 1), + VExpr.bvar 1, VExpr.const ``Nat [], + VExpr.sort (VLevel.succ (VLevel.param 0))] + (VExpr.forallE (VExpr.const ``Nat []) + (VExpr.sort (VLevel.succ (VLevel.param 0)))) + [VExpr.app (VExpr.const ``Nat.succ []) (VExpr.bvar 2)] + (VExpr.sort (VLevel.succ (VLevel.param 0))) + have hNat : natFinalEnv.constants ``Nat = some natType.toVConstant := rfl + have hSucc : natFinalEnv.constants ``Nat.succ = + some natType.ctors[1].toVConstant := rfl + exact .cons (by type_tac) .nil + +theorem natFinalEnv_le_indexedVecTypeEnv : natFinalEnv ≤ indexedVecTypeEnv := + VEnv.addConst_le (show natFinalEnv.addConst indexedVecType.name + indexedVecType.toVConstant = some indexedVecTypeEnv from rfl) + +theorem indexedVecTypeEnv_le_indexedVecNilEnv : + indexedVecTypeEnv ≤ indexedVecNilEnv := + VEnv.addConst_le (show indexedVecTypeEnv.addConst + indexedVecType.ctors[0].name indexedVecType.ctors[0].toVConstant = + some indexedVecNilEnv from rfl) + +theorem indexedVecNilEnv_le_indexedVecCtorEnv : + indexedVecNilEnv ≤ indexedVecCtorEnv := + VEnv.addConst_le (show indexedVecNilEnv.addConst + indexedVecType.ctors[1].name indexedVecType.ctors[1].toVConstant = + some indexedVecCtorEnv from rfl) + +theorem indexedVecTypeEnv_ordered : indexedVecTypeEnv.Ordered := + .const (n := indexedVecType.name) (ci := indexedVecType.toVConstant) + nat_env_wf.ordered indexedVecType_wf rfl + +theorem indexedVecNil_wf : + indexedVecType.ctors[0].toVConstant.WF indexedVecTypeEnv := by + have hblock := indexedVecDecl_wf.2 indexedVecType + (by simp [indexedVecDecl]) + have hctor := hblock.2 indexedVecType.ctors[0] (by simp) + have hle : natFinalEnv ≤ indexedVecTypeEnv := + natFinalEnv_le_indexedVecTypeEnv + have S0 : VInductDecl.Stage3Env indexedVecTypeEnv 1 ``IndexedVec 1 + (.succ (.param 0)) { indexedVecType with ctors := [] } := { + ord := indexedVecTypeEnv_ordered + hl := by decide + hsort := rfl + hlen := rfl + hT := rfl + hcs := by simp + htel := by simp + hs3 := by simp + hparams := hblock.1.mono hle + hfields := by simp + hresult := by simp } + exact S0.ctorType_isType' rfl rfl + (VInductDecl.fieldsWF_mono hle hctor.1) (hctor.2.mono hle) + +theorem indexedVecNilEnv_ordered : indexedVecNilEnv.Ordered := + .const (n := indexedVecType.ctors[0].name) + (ci := indexedVecType.ctors[0].toVConstant) + indexedVecTypeEnv_ordered indexedVecNil_wf rfl + +theorem indexedVecCons_wf : + indexedVecType.ctors[1].toVConstant.WF indexedVecNilEnv := by + have hblock := indexedVecDecl_wf.2 indexedVecType + (by simp [indexedVecDecl]) + have hnil := hblock.2 indexedVecType.ctors[0] (by simp) + have hcons := hblock.2 indexedVecType.ctors[1] (by simp) + have h0 : natFinalEnv ≤ indexedVecTypeEnv := + natFinalEnv_le_indexedVecTypeEnv + have h1 : indexedVecTypeEnv ≤ indexedVecNilEnv := + indexedVecTypeEnv_le_indexedVecNilEnv + have hle : natFinalEnv ≤ indexedVecNilEnv := h0.trans h1 + have S1 : VInductDecl.Stage3Env indexedVecNilEnv 1 ``IndexedVec 1 + (.succ (.param 0)) + { indexedVecType with ctors := [indexedVecType.ctors[0]] } := { + ord := indexedVecNilEnv_ordered + hl := by decide + hsort := rfl + hlen := rfl + hT := rfl + hcs := by + intro c hc + have hc' := List.mem_singleton.1 hc + subst c + rfl + htel := by + intro c hc + have hc' := List.mem_singleton.1 hc + subst c + rfl + hs3 := by + intro c hc + have hc' := List.mem_singleton.1 hc + subst c + rfl + hparams := hblock.1.mono hle + hfields := by + intro c hc + have hc' := List.mem_singleton.1 hc + subst c + exact VInductDecl.fieldsWF_mono hle hnil.1 + hresult := by + intro c hc + have hc' := List.mem_singleton.1 hc + subst c + exact hnil.2.mono hle } + exact S1.ctorType_isType' rfl rfl + (VInductDecl.fieldsWF_mono hle hcons.1) (hcons.2.mono hle) + +theorem indexedVecCtorEnv_ordered : indexedVecCtorEnv.Ordered := + .const (n := indexedVecType.ctors[1].name) + (ci := indexedVecType.ctors[1].toVConstant) + indexedVecNilEnv_ordered indexedVecCons_wf rfl + +theorem indexedVecStage3 : + VInductDecl.Stage3Env indexedVecCtorEnv 1 ``IndexedVec 1 + (.succ (.param 0)) indexedVecType := by + have hblock := indexedVecDecl_wf.2 indexedVecType + (by simp [indexedVecDecl]) + have h0 : natFinalEnv ≤ indexedVecTypeEnv := + natFinalEnv_le_indexedVecTypeEnv + have h1 : indexedVecTypeEnv ≤ indexedVecNilEnv := + indexedVecTypeEnv_le_indexedVecNilEnv + have h2 : indexedVecNilEnv ≤ indexedVecCtorEnv := + indexedVecNilEnv_le_indexedVecCtorEnv + have hle : natFinalEnv ≤ indexedVecCtorEnv := (h0.trans h1).trans h2 + refine { + ord := indexedVecCtorEnv_ordered + hl := by decide + hsort := rfl + hlen := rfl + hT := rfl + hcs := ?_ + htel := ?_ + hs3 := ?_ + hparams := hblock.1.mono hle + hfields := ?_ + hresult := ?_ } + · intro c hc + rcases List.mem_cons.1 hc with rfl | hc + · rfl + · have hc' := List.mem_singleton.1 hc + subst c + rfl + · intro c hc + rcases List.mem_cons.1 hc with rfl | hc + · rfl + · have hc' := List.mem_singleton.1 hc + subst c + rfl + · intro c hc + rcases List.mem_cons.1 hc with rfl | hc + · rfl + · have hc' := List.mem_singleton.1 hc + subst c + rfl + · intro c hc + exact VInductDecl.fieldsWF_mono hle (hblock.2 c hc).1 + · intro c hc + exact (hblock.2 c hc).2.mono hle + +theorem indexedVecInfo_tr : + TrConstVal .safe natFinalEnv indexedVecInfo indexedVecType.toVConstVal := by + have hNat : natFinalEnv.constants ``Nat = some natType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr natFinalEnv indexedVecInfo.levelParams [] + indexedVecInfo.type indexedVecType.type := by tr_type_expr_tac + obtain ⟨u, htype⟩ := indexedVecType_wf + exact hshape.to_trExprS nat_env_wf.ordered trivial ⟨.sort u, htype⟩ + +theorem indexedVecNilInfo_tr : + TrConstVal .safe indexedVecTypeEnv indexedVecNilInfo + indexedVecType.ctors[0] := by + have hNat : indexedVecTypeEnv.constants ``Nat = some natType.toVConstant := rfl + have hZero : indexedVecTypeEnv.constants ``Nat.zero = + some natType.ctors[0].toVConstant := rfl + have hVec : indexedVecTypeEnv.constants ``IndexedVec = + some indexedVecType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr indexedVecTypeEnv indexedVecNilInfo.levelParams [] + indexedVecNilInfo.type indexedVecType.ctors[0].type := by + tr_type_expr_tac + obtain ⟨u, htype⟩ := indexedVecNil_wf + exact hshape.to_trExprS indexedVecTypeEnv_ordered trivial ⟨.sort u, htype⟩ + +theorem indexedVecConsInfo_tr : + TrConstVal .safe indexedVecNilEnv indexedVecConsInfo + indexedVecType.ctors[1] := by + have hNat : indexedVecNilEnv.constants ``Nat = some natType.toVConstant := rfl + have hSucc : indexedVecNilEnv.constants ``Nat.succ = + some natType.ctors[1].toVConstant := rfl + have hVec : indexedVecNilEnv.constants ``IndexedVec = + some indexedVecType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr indexedVecNilEnv indexedVecConsInfo.levelParams [] + indexedVecConsInfo.type indexedVecType.ctors[1].type := by + tr_type_expr_tac + obtain ⟨u, htype⟩ := indexedVecCons_wf + exact hshape.to_trExprS indexedVecNilEnv_ordered trivial ⟨.sort u, htype⟩ + +theorem indexedVecRecInfo_tr : + TrConstVal .safe indexedVecCtorEnv indexedVecRecInfo + (inductRecVal indexedVecDecl indexedVecType) := by + have hNat : indexedVecCtorEnv.constants ``Nat = some natType.toVConstant := rfl + have hZero : indexedVecCtorEnv.constants ``Nat.zero = + some natType.ctors[0].toVConstant := rfl + have hSucc : indexedVecCtorEnv.constants ``Nat.succ = + some natType.ctors[1].toVConstant := rfl + have hVec : indexedVecCtorEnv.constants ``IndexedVec = + some indexedVecType.toVConstant := rfl + have hNil : indexedVecCtorEnv.constants ``IndexedVec.nil = + some indexedVecType.ctors[0].toVConstant := rfl + have hCons : indexedVecCtorEnv.constants ``IndexedVec.cons = + some indexedVecType.ctors[1].toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr indexedVecCtorEnv indexedVecRecInfo.levelParams [] + indexedVecRecInfo.type + (inductRecVal indexedVecDecl indexedVecType).type := by + tr_type_expr_tac + obtain ⟨u, hrec⟩ := indexedVecStage3.recConst_wf + exact hshape.to_trExprS indexedVecCtorEnv_ordered trivial ⟨.sort u, hrec⟩ + +def indexedVecTypeMap : ConstMap := natMap.insert ``IndexedVec indexedVecInfo +def indexedVecNilMap : ConstMap := + indexedVecTypeMap.insert ``IndexedVec.nil indexedVecNilInfo +def indexedVecCtorMap : ConstMap := + indexedVecNilMap.insert ``IndexedVec.cons indexedVecConsInfo +def indexedVecMap : ConstMap := + indexedVecCtorMap.insert ``IndexedVec.rec indexedVecRecInfo +def indexedVecFinalEnv : VEnv := + (VInductDecl.rules 1 ``IndexedVec 1 indexedVecType).foldl + VEnv.addDefEq indexedVecRecEnv + +theorem natMap_wf : natMap.WF := (nat_trEnv' (safety := .safe)).map_wf + +theorem indexedVecType_fresh : natMap.find? ``IndexedVec = none := by + rw [natMap, natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem indexedVecTypeMap_wf : indexedVecTypeMap.WF := + natMap_wf.insert _ _ indexedVecType_fresh + +theorem indexedVecNil_fresh : + indexedVecTypeMap.find? ``IndexedVec.nil = none := by + rw [indexedVecTypeMap, natMap_wf.find?_insert, natMap, + natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem indexedVecNilMap_wf : indexedVecNilMap.WF := + indexedVecTypeMap_wf.insert _ _ indexedVecNil_fresh + +theorem indexedVecCons_fresh : + indexedVecNilMap.find? ``IndexedVec.cons = none := by + rw [indexedVecNilMap, indexedVecTypeMap_wf.find?_insert, + indexedVecTypeMap, natMap_wf.find?_insert, natMap, + natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem indexedVecCtorMap_wf : indexedVecCtorMap.WF := + indexedVecNilMap_wf.insert _ _ indexedVecCons_fresh + +theorem indexedVecRec_fresh : + indexedVecCtorMap.find? ``IndexedVec.rec = none := by + rw [indexedVecCtorMap, indexedVecNilMap_wf.find?_insert, + indexedVecNilMap, indexedVecTypeMap_wf.find?_insert, + indexedVecTypeMap, natMap_wf.find?_insert, natMap, + natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem indexedVec_addInduct : + AddInduct natMap natFinalEnv indexedVecDecl indexedVecMap + indexedVecFinalEnv := by + refine ⟨{ + generation := indexedVecChecked.identityGeneration + generation_wf := + (indexedVecChecked.wf_of_decl indexedVecDecl_wf).identityGeneration + nat_env_wf.ordered + typeMap := indexedVecTypeMap + typeEnv := indexedVecTypeEnv + ctorMap := indexedVecCtorMap + ctorEnv := indexedVecCtorEnv + recEnv := indexedVecRecEnv + addType := { + info := indexedVecInfo + kind_eq := by simp [indexedVecInfo, InductConstantKind.Matches] + tr := indexedVecInfo_tr + map_fresh := by + change natMap.find? ``IndexedVec = none + exact indexedVecType_fresh + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := indexedVecRecInfo + kind_eq := by simp [indexedVecRecInfo, InductConstantKind.Matches] + tr := indexedVecRecInfo_tr + map_fresh := by + change indexedVecCtorMap.find? ``IndexedVec.rec = none + exact indexedVecRec_fresh + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ }⟩ + exact .cons { + info := indexedVecNilInfo + kind_eq := by simp [indexedVecNilInfo, InductConstantKind.Matches] + tr := indexedVecNilInfo_tr + map_fresh := by simpa [indexedVecType] using indexedVecNil_fresh + env_add := rfl + map_add := rfl } + (.cons { + info := indexedVecConsInfo + kind_eq := by simp [indexedVecConsInfo, InductConstantKind.Matches] + tr := indexedVecConsInfo_tr + map_fresh := by + change indexedVecNilMap.find? ``IndexedVec.cons = none + exact indexedVecCons_fresh + env_add := rfl + map_add := rfl } .nil) + +theorem indexedVec_trEnv' : + TrEnv' .safe indexedVecMap false indexedVecFinalEnv := + .induct indexedVec_addInduct nat_trEnv' + +theorem indexedVec_final_matches_addInduct : + natFinalEnv.addInduct indexedVecDecl = some indexedVecFinalEnv := + rfl + +theorem indexedVec_env_wf : indexedVecFinalEnv.WF := indexedVec_trEnv'.wf + +theorem indexedVec_aligned : + Aligned .safe indexedVecMap indexedVecFinalEnv := + indexedVec_trEnv'.aligned + +theorem indexedVec_type_map_lookup : + indexedVecMap.find? ``IndexedVec = some indexedVecInfo := by + rw [indexedVecMap, indexedVecCtorMap_wf.find?_insert, indexedVecCtorMap, + indexedVecNilMap_wf.find?_insert, indexedVecNilMap, + indexedVecTypeMap_wf.find?_insert, indexedVecTypeMap, + natMap_wf.find?_insert] + rfl + +theorem indexedVec_type_env_lookup : + indexedVecFinalEnv.constants ``IndexedVec = + some indexedVecType.toVConstant := rfl + +theorem indexedVec_type_lookup_unique : + indexedVecInfo.name = ``IndexedVec ∧ + TrConstant .safe indexedVecFinalEnv indexedVecInfo + indexedVecType.toVConstant := + indexedVec_aligned.find?_uniq indexedVec_type_map_lookup + indexedVec_type_env_lookup + +theorem indexedVec_cons_map_lookup : + indexedVecMap.find? ``IndexedVec.cons = some indexedVecConsInfo := by + rw [indexedVecMap, indexedVecCtorMap_wf.find?_insert, indexedVecCtorMap, + indexedVecNilMap_wf.find?_insert] + rfl + +theorem indexedVec_cons_env_lookup : + indexedVecFinalEnv.constants ``IndexedVec.cons = + some indexedVecType.ctors[1].toVConstant := rfl + +theorem indexedVec_cons_lookup_unique : + indexedVecConsInfo.name = ``IndexedVec.cons ∧ + TrConstant .safe indexedVecFinalEnv indexedVecConsInfo + indexedVecType.ctors[1].toVConstant := + indexedVec_aligned.find?_uniq indexedVec_cons_map_lookup + indexedVec_cons_env_lookup + +theorem indexedVec_rec_map_lookup : + indexedVecMap.find? ``IndexedVec.rec = some indexedVecRecInfo := by + rw [indexedVecMap, indexedVecCtorMap_wf.find?_insert] + rfl + +theorem indexedVec_rec_env_lookup : + indexedVecFinalEnv.constants ``IndexedVec.rec = + some (VInductDecl.recConst 1 ``IndexedVec 1 indexedVecType) := rfl + +theorem indexedVec_rec_lookup_unique : + indexedVecRecInfo.name = ``IndexedVec.rec ∧ + TrConstant .safe indexedVecFinalEnv indexedVecRecInfo + (VInductDecl.recConst 1 ``IndexedVec 1 indexedVecType) := + indexedVec_aligned.find?_uniq indexedVec_rec_map_lookup + indexedVec_rec_env_lookup + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.indexedVec_trEnv' [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +/-! ## Acc: recursive Pi metadata -/ + +/- `Acc` is the first replay whose recursive constructor argument is a +function. In addition to translating the three kernel constants, this fixture +quotes the actual `RecursorRule.rhs` and compares it definitionally with the +public generalized Theory rule. -/ +def accInfo : ConstantInfo := kernelInductInfo% Acc +def accIntroInfo : ConstantInfo := kernelCtorInfo% Acc.intro +def accRecInfo : ConstantInfo := kernelRecInfo% Acc.rec +def accKernelRuleRhs : VExpr := kernelRecRuleRhs% Acc.rec 0 + +example : (match accIntroInfo with + | .ctorInfo ci => ci.numParams + | _ => 0) = 2 := rfl +example : (match accIntroInfo with + | .ctorInfo ci => ci.numFields + | _ => 0) = 2 := rfl +example : (match accRecInfo with + | .recInfo ci => ci.numParams + | _ => 0) = 2 := rfl +example : (match accRecInfo with + | .recInfo ci => ci.numIndices + | _ => 0) = 1 := rfl +example : (match accRecInfo with + | .recInfo ci => ci.numMotives + | _ => 0) = 1 := rfl +example : (match accRecInfo with + | .recInfo ci => ci.numMinors + | _ => 0) = 1 := rfl +example : (match accRecInfo with + | .recInfo ci => ci.rules.length + | _ => 0) = 1 := rfl +example : (match accRecInfo with + | .recInfo ci => ci.rules[0]?.map (·.ctor) |>.getD .anonymous + | _ => .anonymous) = ``Acc.intro := rfl +example : (match accRecInfo with + | .recInfo ci => ci.rules[0]?.map (·.nfields) |>.getD 0 + | _ => 0) = 2 := rfl + +/-- The actual kernel rule has the same lambda-wrapped functional recursive +call as the public Theory generator, in the kernel recursor's universe order. -/ +example : accKernelRuleRhs = + (VInductDecl.ruleRec 1 ``Acc 2 accType 0 accType.ctors[0]).rhs := rfl +example : accKernelRuleRhs = + accChecked.identityGeneration.generatedRules[0].rhs := rfl + +def accTypeEnv := (VEnv.empty.addConst accType.name accType.toVConstant).get! +def accCtorEnv := + (accTypeEnv.addConst accType.ctors[0].name accType.ctors[0].toVConstant).get! +def accRecEnv := + (accCtorEnv.addConst ``Acc.rec + (VInductDecl.recConstRec 1 ``Acc 2 accType)).get! + +theorem accType_wf : accType.toVConstant.WF VEnv.empty := by + have htel := (accDecl_wf.2 accType (by simp [accDecl])).1 + change VEnv.empty.IsType 1 [] accType.type + rw [show accType.type = VExpr.forallN + [.sort (.param 0), + .forallE (.bvar 0) (.forallE (.bvar 1) (.sort .zero)), + .bvar 1] (.sort .zero) from rfl] + exact VEnv.IsType.forallN htel ⟨.succ .zero, VEnv.HasType.sort (by decide)⟩ + +theorem accTypeEnv_ordered : accTypeEnv.Ordered := + .const .empty accType_wf rfl + +theorem accIntro_wf : accType.ctors[0].toVConstant.WF accTypeEnv := by + have hblock := accDecl_wf.2 accType (by simp [accDecl]) + have hctor := hblock.2 accType.ctors[0] (by simp) + have hle : VEnv.empty ≤ accTypeEnv := VEnv.addConst_le rfl + have S0 : VInductDecl.Stage3Env accTypeEnv 1 ``Acc 2 .zero + { accType with ctors := [] } := { + ord := accTypeEnv_ordered + hl := by decide + hsort := rfl + hlen := rfl + hT := rfl + hcs := by simp + htel := by simp + hs3 := by simp + hparams := hblock.1.mono hle + hfields := by simp + hresult := by simp } + exact S0.ctorType_isType' rfl rfl + (VInductDecl.fieldsWF_mono hle hctor.1) (hctor.2.mono hle) + +theorem accCtorEnv_ordered : accCtorEnv.Ordered := + .const (n := accType.ctors[0].name) (ci := accType.ctors[0].toVConstant) + accTypeEnv_ordered accIntro_wf rfl + +theorem accStage3 : + VInductDecl.Stage3Env accCtorEnv 1 ``Acc 2 .zero accType := by + have hblock := accDecl_wf.2 accType (by simp [accDecl]) + have hctor := hblock.2 accType.ctors[0] (by simp) + have h0 : VEnv.empty ≤ accTypeEnv := VEnv.addConst_le rfl + have h1 : accTypeEnv ≤ accCtorEnv := + VEnv.addConst_le (show accTypeEnv.addConst accType.ctors[0].name + accType.ctors[0].toVConstant = some accCtorEnv from rfl) + have hle : VEnv.empty ≤ accCtorEnv := h0.trans h1 + refine { + ord := accCtorEnv_ordered + hl := by decide + hsort := rfl + hlen := rfl + hT := rfl + hcs := ?_ + htel := ?_ + hs3 := ?_ + hparams := hblock.1.mono hle + hfields := ?_ + hresult := ?_ } + · intro c hc + have hc' := List.mem_singleton.1 hc + subst c + rfl + · intro c hc + have hc' := List.mem_singleton.1 hc + subst c + rfl + · intro c hc + have hc' := List.mem_singleton.1 hc + subst c + rfl + · intro c hc + have hc' := List.mem_singleton.1 hc + subst c + exact VInductDecl.fieldsWF_mono hle hctor.1 + · intro c hc + have hc' := List.mem_singleton.1 hc + subst c + exact hctor.2.mono hle + +theorem accInfo_tr : + TrConstVal .safe VEnv.empty accInfo accType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr VEnv.empty accInfo.levelParams [] accInfo.type + accType.type := by tr_type_expr_tac + obtain ⟨u, htype⟩ := accType_wf + exact hshape.to_trExprS .empty trivial ⟨.sort u, htype⟩ + +theorem accIntroInfo_tr : + TrConstVal .safe accTypeEnv accIntroInfo accType.ctors[0] := by + have hAcc : accTypeEnv.constants ``Acc = some accType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr accTypeEnv accIntroInfo.levelParams [] + accIntroInfo.type accType.ctors[0].type := by tr_type_expr_tac + obtain ⟨u, htype⟩ := accIntro_wf + exact hshape.to_trExprS accTypeEnv_ordered trivial ⟨.sort u, htype⟩ + +theorem accRecInfo_tr : + TrConstVal .safe accCtorEnv accRecInfo (inductRecVal accDecl accType) := by + have hAcc : accCtorEnv.constants ``Acc = some accType.toVConstant := rfl + have hIntro : accCtorEnv.constants ``Acc.intro = + some accType.ctors[0].toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr accCtorEnv accRecInfo.levelParams [] + accRecInfo.type (inductRecVal accDecl accType).type := by tr_type_expr_tac + obtain ⟨u, hrec⟩ := accStage3.recConstRec_wf + exact hshape.to_trExprS accCtorEnv_ordered trivial ⟨.sort u, hrec⟩ + +def accTypeMap : ConstMap := ({} : ConstMap).insert ``Acc accInfo +def accCtorMap : ConstMap := accTypeMap.insert ``Acc.intro accIntroInfo +def accMap : ConstMap := accCtorMap.insert ``Acc.rec accRecInfo +def accFinalEnv : VEnv := + (VInductDecl.rulesRec 1 ``Acc 2 accType).foldl VEnv.addDefEq accRecEnv + +theorem accType_fresh : ({} : ConstMap).find? ``Acc = none := by + simp [SMap.find?] + +theorem accTypeMap_wf : accTypeMap.WF := + SMap.WF.empty.insert _ _ accType_fresh + +theorem accIntro_fresh : accTypeMap.find? ``Acc.intro = none := by + rw [accTypeMap, SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem accCtorMap_wf : accCtorMap.WF := + accTypeMap_wf.insert _ _ accIntro_fresh + +theorem accRec_fresh : accCtorMap.find? ``Acc.rec = none := by + rw [accCtorMap, accTypeMap_wf.find?_insert, accTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem acc_addInduct : + AddInduct ({} : ConstMap) VEnv.empty accDecl accMap accFinalEnv := by + refine ⟨{ + generation := accChecked.identityGeneration + generation_wf := + (accChecked.wf_of_decl accDecl_wf).identityGeneration .empty + typeMap := accTypeMap + typeEnv := accTypeEnv + ctorMap := accCtorMap + ctorEnv := accCtorEnv + recEnv := accRecEnv + addType := { + info := accInfo + kind_eq := by simp [accInfo, InductConstantKind.Matches] + tr := accInfo_tr + map_fresh := by + change ({} : ConstMap).find? ``Acc = none + exact accType_fresh + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := accRecInfo + kind_eq := by simp [accRecInfo, InductConstantKind.Matches] + tr := accRecInfo_tr + map_fresh := by + change accCtorMap.find? ``Acc.rec = none + exact accRec_fresh + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ }⟩ + exact .cons { + info := accIntroInfo + kind_eq := by simp [accIntroInfo, InductConstantKind.Matches] + tr := accIntroInfo_tr + map_fresh := by simpa [accType] using accIntro_fresh + env_add := rfl + map_add := rfl } .nil + +/-- Replay the actual kernel `Acc` metadata through the live inductive +environment branch. -/ +theorem acc_trEnv' : TrEnv' .safe accMap false accFinalEnv := + .induct acc_addInduct .empty + +theorem acc_final_matches_addInduct : + VEnv.empty.addInduct accDecl = some accFinalEnv := + rfl + +theorem acc_env_wf : accFinalEnv.WF := acc_trEnv'.wf + +theorem acc_aligned : Aligned .safe accMap accFinalEnv := acc_trEnv'.aligned + +theorem acc_type_map_lookup : accMap.find? ``Acc = some accInfo := by + rw [accMap, accCtorMap_wf.find?_insert, accCtorMap, + accTypeMap_wf.find?_insert, accTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + rfl + +theorem acc_type_env_lookup : + accFinalEnv.constants ``Acc = some accType.toVConstant := rfl + +theorem acc_type_lookup_unique : + accInfo.name = ``Acc ∧ + TrConstant .safe accFinalEnv accInfo accType.toVConstant := + acc_aligned.find?_uniq acc_type_map_lookup acc_type_env_lookup + +theorem acc_intro_map_lookup : + accMap.find? ``Acc.intro = some accIntroInfo := by + rw [accMap, accCtorMap_wf.find?_insert, accCtorMap, + accTypeMap_wf.find?_insert] + rfl + +theorem acc_intro_env_lookup : + accFinalEnv.constants ``Acc.intro = + some accType.ctors[0].toVConstant := rfl + +theorem acc_intro_lookup_unique : + accIntroInfo.name = ``Acc.intro ∧ + TrConstant .safe accFinalEnv accIntroInfo + accType.ctors[0].toVConstant := + acc_aligned.find?_uniq acc_intro_map_lookup acc_intro_env_lookup + +theorem acc_rec_map_lookup : accMap.find? ``Acc.rec = some accRecInfo := by + rw [accMap, accCtorMap_wf.find?_insert] + rfl + +theorem acc_rec_env_lookup : + accFinalEnv.constants ``Acc.rec = + some (VInductDecl.recConstRec 1 ``Acc 2 accType) := rfl + +theorem acc_rec_lookup_unique : + accRecInfo.name = ``Acc.rec ∧ + TrConstant .safe accFinalEnv accRecInfo + (VInductDecl.recConstRec 1 ``Acc 2 accType) := + acc_aligned.find?_uniq acc_rec_map_lookup acc_rec_env_lookup + +/- This has the same `sorryAx`-free closure as the direct replay roots; the +persistent-map contracts enter through concrete `SMap` freshness proofs. -/ +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.acc_trEnv' [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +/-! ## AliasFormer: non-identity family-result normalization -/ + +/-- The actual reducible alias declaration that precedes `AliasFormer` in the +kernel environment. -/ +def typeFamilyAliasKernelDef : DefinitionVal := + kernelDefVal% TypeFamilyAlias + +def typeFamilyAliasInfo : ConstantInfo := + .defnInfo typeFamilyAliasKernelDef + +def typeFamilyAliasVal : VDefVal where + name := ``TypeFamilyAlias + uvars := (vconst(type_of% @TypeFamilyAlias) : VConstant).uvars + type := (vconst(type_of% @TypeFamilyAlias) : VConstant).type + value := typeFamilyAliasDefEq.rhs + +theorem typeFamilyAliasInfo_tr : + TrDefVal .safe VEnv.empty typeFamilyAliasInfo typeFamilyAliasVal := by + refine ⟨⟨⟨by decide, rfl, ?_⟩, rfl⟩, ?_⟩ + · exact .sort rfl + · exact .sort rfl + +theorem typeFamilyAliasVal_wf : + typeFamilyAliasVal.WF VEnv.empty := + VEnv.HasType.sort (by decide) + +def typeFamilyAliasMap : ConstMap := + ({} : ConstMap).insert ``TypeFamilyAlias typeFamilyAliasInfo + +theorem typeFamilyAliasMap_fresh : + ({} : ConstMap).find? ``TypeFamilyAlias = none := by + simp [SMap.find?] + +/-- Replay the actual alias definition, including its Theory delta rule. -/ +theorem typeFamilyAlias_trEnv' {safety : DefinitionSafety} : + TrEnv' safety typeFamilyAliasMap false typeFamilyAliasEnv := + .defn (ci := typeFamilyAliasKernelDef) (ci' := typeFamilyAliasVal) + (typeFamilyAliasInfo_tr.sf_mono DefinitionSafety.le_safe) + typeFamilyAliasMap_fresh + typeFamilyAliasVal_wf rfl .empty + +def aliasFormerInfo : ConstantInfo := kernelInductInfo% AliasFormer +def aliasFormerMkInfo : ConstantInfo := kernelCtorInfo% AliasFormer.mk +def aliasFormerRecInfo : ConstantInfo := kernelRecInfo% AliasFormer.rec +def aliasFormerKernelRuleRhs : VExpr := + kernelRecRuleRhs% AliasFormer.rec 0 + +example : aliasFormerRawDecl.checked? = none := rfl +example : aliasFormerGenerationChecked.block.sourceType = + aliasFormerRawType := rfl +example : aliasFormerGenerationChecked.block.checked.type = + aliasFormerViewType := rfl +example : aliasFormerKernelRuleRhs = + aliasFormerGenerationChecked.generatedRules[0].rhs := rfl + +def aliasFormerTypeEnv := + (typeFamilyAliasEnv.addConst aliasFormerRawType.name + aliasFormerRawType.toVConstant).get! + +def aliasFormerCtorEnv := + (aliasFormerTypeEnv.addConst aliasFormerRawType.ctors[0].name + aliasFormerRawType.ctors[0].toVConstant).get! + +def aliasFormerRecEnv := + (aliasFormerCtorEnv.addConst ``AliasFormer.rec + aliasFormerGenerationChecked.recursor).get! + +theorem aliasFormerTypeEnv_ordered : aliasFormerTypeEnv.Ordered := by + refine .const (n := aliasFormerRawType.name) + (ci := aliasFormerRawType.toVConstant) + typeFamilyAliasEnv_ordered ?_ rfl + show typeFamilyAliasEnv.IsType + aliasFormerGenerationChecked.block.sourceType.uvars [] + aliasFormerGenerationChecked.block.sourceType.type + rw [aliasFormerGenerationChecked.block.sourceType_uvars_eq] + exact aliasFormerGenerationChecked_wf.rawFamily_isType + +theorem aliasFormerRawCtor_wf : + aliasFormerRawType.ctors[0].toVConstant.WF aliasFormerTypeEnv := by + have hctor : + (⟨aliasFormerRawType.ctors[0], + aliasFormerViewChecked.constructors[0]⟩ : + VInductDecl.NormalizedCtor) ∈ + aliasFormerGenerationChecked.block.ctorPairs := by + exact .head _ + show aliasFormerTypeEnv.IsType + aliasFormerRawType.ctors[0].uvars [] + aliasFormerRawType.ctors[0].type + rw [aliasFormerGenerationChecked.ctor_uvars_eq hctor] + exact aliasFormerGenerationChecked_wf.rawCtor_isType rfl hctor + +theorem aliasFormerCtorEnv_ordered : aliasFormerCtorEnv.Ordered := + .const (n := aliasFormerRawType.ctors[0].name) + (ci := aliasFormerRawType.ctors[0].toVConstant) + aliasFormerTypeEnv_ordered aliasFormerRawCtor_wf rfl + +theorem aliasFormerGenerationEnv : + VInductDecl.GenerationEnv aliasFormerGenerationChecked + aliasFormerCtorEnv := by + apply aliasFormerGenerationChecked_wf.toGenerationEnv + (envT := aliasFormerTypeEnv) + · rfl + · exact (VEnv.addConst_le (show + typeFamilyAliasEnv.addConst aliasFormerRawType.name + aliasFormerRawType.toVConstant = some aliasFormerTypeEnv from rfl)).trans + (VEnv.addConst_le (show + aliasFormerTypeEnv.addConst aliasFormerRawType.ctors[0].name + aliasFormerRawType.ctors[0].toVConstant = + some aliasFormerCtorEnv from rfl)) + · exact VEnv.addConst_le (show + aliasFormerTypeEnv.addConst aliasFormerRawType.ctors[0].name + aliasFormerRawType.ctors[0].toVConstant = + some aliasFormerCtorEnv from rfl) + · exact aliasFormerCtorEnv_ordered + · rfl + · intro ctor hctor + change ctor ∈ + [⟨aliasFormerRawType.ctors[0], + aliasFormerViewChecked.constructors[0]⟩] at hctor + obtain rfl := List.mem_singleton.1 hctor + rfl + +theorem aliasFormerInfo_tr : + TrConstVal .safe typeFamilyAliasEnv aliasFormerInfo + aliasFormerRawType.toVConstVal := by + have hAlias : typeFamilyAliasEnv.constants ``TypeFamilyAlias = + some (vconst(type_of% @TypeFamilyAlias)) := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr typeFamilyAliasEnv + aliasFormerInfo.levelParams [] aliasFormerInfo.type + aliasFormerRawType.type := by + tr_type_expr_tac + obtain ⟨u, htype⟩ := + aliasFormerGenerationChecked_wf.rawFamily_isType + exact hshape.to_trExprS typeFamilyAliasEnv_ordered trivial + ⟨.sort u, htype⟩ + +theorem aliasFormerMkInfo_tr : + TrConstVal .safe aliasFormerTypeEnv aliasFormerMkInfo + aliasFormerRawType.ctors[0] := by + have hFamily : aliasFormerTypeEnv.constants ``AliasFormer = + some aliasFormerRawType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr aliasFormerTypeEnv + aliasFormerMkInfo.levelParams [] aliasFormerMkInfo.type + aliasFormerRawType.ctors[0].type := by + tr_type_expr_tac + obtain ⟨u, htype⟩ := aliasFormerRawCtor_wf + exact hshape.to_trExprS aliasFormerTypeEnv_ordered trivial + ⟨.sort u, htype⟩ + +theorem aliasFormerRecInfo_tr : + TrConstVal .safe aliasFormerCtorEnv aliasFormerRecInfo + (inductGenerationRecVal aliasFormerGenerationChecked) := by + have hFamily : aliasFormerCtorEnv.constants ``AliasFormer = + some aliasFormerRawType.toVConstant := rfl + have hMk : aliasFormerCtorEnv.constants ``AliasFormer.mk = + some aliasFormerRawType.ctors[0].toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr aliasFormerCtorEnv + aliasFormerRecInfo.levelParams [] aliasFormerRecInfo.type + (inductGenerationRecVal aliasFormerGenerationChecked).type := by + tr_type_expr_tac + obtain ⟨u, hrec⟩ := aliasFormerGenerationEnv.recursor_wf + exact hshape.to_trExprS aliasFormerCtorEnv_ordered trivial + ⟨.sort u, hrec⟩ + +def aliasFormerTypeMap : ConstMap := + typeFamilyAliasMap.insert ``AliasFormer aliasFormerInfo + +def aliasFormerCtorMap : ConstMap := + aliasFormerTypeMap.insert ``AliasFormer.mk aliasFormerMkInfo + +def aliasFormerMap : ConstMap := + aliasFormerCtorMap.insert ``AliasFormer.rec aliasFormerRecInfo + +theorem typeFamilyAliasMap_wf : typeFamilyAliasMap.WF := + (typeFamilyAlias_trEnv' (safety := .safe)).map_wf + +theorem aliasFormerType_fresh : + typeFamilyAliasMap.find? ``AliasFormer = none := by + rw [typeFamilyAliasMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem aliasFormerTypeMap_wf : aliasFormerTypeMap.WF := + typeFamilyAliasMap_wf.insert _ _ aliasFormerType_fresh + +theorem aliasFormerMk_fresh : + aliasFormerTypeMap.find? ``AliasFormer.mk = none := by + rw [aliasFormerTypeMap, typeFamilyAliasMap_wf.find?_insert, + typeFamilyAliasMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem aliasFormerCtorMap_wf : aliasFormerCtorMap.WF := + aliasFormerTypeMap_wf.insert _ _ aliasFormerMk_fresh + +theorem aliasFormerRec_fresh : + aliasFormerCtorMap.find? ``AliasFormer.rec = none := by + rw [aliasFormerCtorMap, aliasFormerTypeMap_wf.find?_insert, + aliasFormerTypeMap, typeFamilyAliasMap_wf.find?_insert, + typeFamilyAliasMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +private def aliasFormerAddInductTraceWith + (generation_wf : + aliasFormerGenerationChecked.WF typeFamilyAliasEnv) : + AddInductTrace typeFamilyAliasMap typeFamilyAliasEnv + aliasFormerRawDecl aliasFormerMap aliasFormerFinalEnv := by + refine { + generation := aliasFormerGenerationChecked + generation_wf := generation_wf + typeMap := aliasFormerTypeMap + typeEnv := aliasFormerTypeEnv + ctorMap := aliasFormerCtorMap + ctorEnv := aliasFormerCtorEnv + recEnv := aliasFormerRecEnv + addType := { + info := aliasFormerInfo + kind_eq := by simp [aliasFormerInfo, InductConstantKind.Matches] + tr := aliasFormerInfo_tr + map_fresh := by + change typeFamilyAliasMap.find? ``AliasFormer = none + exact aliasFormerType_fresh + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := aliasFormerRecInfo + kind_eq := by simp [aliasFormerRecInfo, InductConstantKind.Matches] + tr := aliasFormerRecInfo_tr + map_fresh := by + change aliasFormerCtorMap.find? ``AliasFormer.rec = none + exact aliasFormerRec_fresh + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ } + exact .cons { + info := aliasFormerMkInfo + kind_eq := by simp [aliasFormerMkInfo, InductConstantKind.Matches] + tr := aliasFormerMkInfo_tr + map_fresh := by + simpa [aliasFormerRawType] using aliasFormerMk_fresh + env_add := rfl + map_add := rfl } .nil + +theorem aliasFormer_addInduct : + AddInduct typeFamilyAliasMap typeFamilyAliasEnv + aliasFormerRawDecl aliasFormerMap aliasFormerFinalEnv := + ⟨aliasFormerAddInductTraceWith aliasFormerGenerationChecked_wf⟩ + +theorem aliasFormer_trEnv' : + TrEnv' .safe aliasFormerMap false aliasFormerFinalEnv := + .induct aliasFormer_addInduct typeFamilyAlias_trEnv' + +theorem aliasFormer_final_matches_generation : + typeFamilyAliasEnv.addInductGeneration + aliasFormerGenerationChecked = some aliasFormerFinalEnv := + aliasFormer_addInductGeneration + +theorem aliasFormer_env_wf : aliasFormerFinalEnv.WF := + aliasFormer_trEnv'.wf + +theorem aliasFormer_aligned : + Aligned .safe aliasFormerMap aliasFormerFinalEnv := + aliasFormer_trEnv'.aligned + +theorem aliasFormer_type_map_lookup : + aliasFormerMap.find? ``AliasFormer = some aliasFormerInfo := by + rw [aliasFormerMap, aliasFormerCtorMap_wf.find?_insert, + aliasFormerCtorMap, aliasFormerTypeMap_wf.find?_insert, + aliasFormerTypeMap, typeFamilyAliasMap_wf.find?_insert] + rfl + +theorem aliasFormer_type_lookup_unique : + aliasFormerInfo.name = ``AliasFormer ∧ + TrConstant .safe aliasFormerFinalEnv aliasFormerInfo + aliasFormerRawType.toVConstant := + aliasFormer_aligned.find?_uniq aliasFormer_type_map_lookup + aliasFormerFinalEnv_family_lookup + +theorem aliasFormer_mk_map_lookup : + aliasFormerMap.find? ``AliasFormer.mk = some aliasFormerMkInfo := by + rw [aliasFormerMap, aliasFormerCtorMap_wf.find?_insert, + aliasFormerCtorMap, aliasFormerTypeMap_wf.find?_insert] + rfl + +theorem aliasFormer_mk_lookup_unique : + aliasFormerMkInfo.name = ``AliasFormer.mk ∧ + TrConstant .safe aliasFormerFinalEnv aliasFormerMkInfo + aliasFormerRawType.ctors[0].toVConstant := + aliasFormer_aligned.find?_uniq aliasFormer_mk_map_lookup + (aliasFormerFinalEnv_ctor_lookup _ (.head _)) + +theorem aliasFormer_rec_map_lookup : + aliasFormerMap.find? ``AliasFormer.rec = some aliasFormerRecInfo := by + rw [aliasFormerMap, aliasFormerCtorMap_wf.find?_insert] + rfl + +theorem aliasFormer_rec_lookup_unique : + aliasFormerRecInfo.name = ``AliasFormer.rec ∧ + TrConstant .safe aliasFormerFinalEnv aliasFormerRecInfo + aliasFormerGenerationChecked.recursor := + aliasFormer_aligned.find?_uniq aliasFormer_rec_map_lookup + aliasFormerFinalEnv_rec_lookup + +/-! ## AliasRec: non-identity recursive-field normalization -/ + +def recAliasKernelDef : DefinitionVal := kernelDefVal% RecAlias + +def recAliasInfo : ConstantInfo := .defnInfo recAliasKernelDef + +def recAliasVal : VDefVal where + name := ``RecAlias + uvars := (vconst(type_of% @RecAlias) : VConstant).uvars + type := (vconst(type_of% @RecAlias) : VConstant).type + value := recAliasDefEq.rhs + +theorem recAliasInfo_tr : + TrDefVal .safe VEnv.empty recAliasInfo recAliasVal := by + refine ⟨⟨⟨by decide, rfl, ?_⟩, rfl⟩, ?_⟩ + · have hshape : TrTypeExpr VEnv.empty recAliasInfo.levelParams [] + recAliasInfo.type recAliasVal.type := by + tr_type_expr_tac + obtain ⟨u, htype⟩ := recAliasConstant_wf + exact hshape.to_trExprS .empty trivial ⟨.sort u, htype⟩ + · refine .lam ?_ (.sort rfl) (.bvar rfl) + exact ⟨_, VEnv.HasType.sort (by decide)⟩ + +theorem recAliasVal_wf : recAliasVal.WF VEnv.empty := by + exact VEnv.HasType.lam + (VEnv.HasType.sort (by decide)) + (VEnv.HasType.bvar .zero) + +def recAliasMap : ConstMap := + ({} : ConstMap).insert ``RecAlias recAliasInfo + +theorem recAliasMap_fresh : + ({} : ConstMap).find? ``RecAlias = none := by + simp [SMap.find?] + +theorem recAlias_trEnv' {safety : DefinitionSafety} : + TrEnv' safety recAliasMap false recAliasEnv := + .defn (ci := recAliasKernelDef) (ci' := recAliasVal) + (recAliasInfo_tr.sf_mono DefinitionSafety.le_safe) + recAliasMap_fresh recAliasVal_wf rfl .empty + +def aliasRecInfo : ConstantInfo := kernelInductInfo% AliasRec +def aliasRecMkInfo : ConstantInfo := kernelCtorInfo% AliasRec.mk +def aliasRecRecInfo : ConstantInfo := kernelRecInfo% AliasRec.rec +def aliasRecKernelRuleRhs : VExpr := + kernelRecRuleRhs% AliasRec.rec 0 + +example : aliasRecRawDecl.checked? = none := rfl +example : aliasRecGenerationChecked.block.sourceType = aliasRecRawType := rfl +example : aliasRecGenerationChecked.block.checked.type = aliasRecViewType := rfl +example : aliasRecGenerationChecked.block.ctorPairs[0].rawFields 0 = + [aliasRecRawField] := rfl +example : (VInductDecl.ctorFields + aliasRecGenerationChecked.minorTypes[0])[0]? = + some aliasRecRawField := rfl +example : aliasRecKernelRuleRhs = + aliasRecGenerationChecked.generatedRules[0].rhs := rfl + +def aliasRecTypeEnv := + (recAliasEnv.addConst aliasRecRawType.name + aliasRecRawType.toVConstant).get! + +def aliasRecCtorEnv := + (aliasRecTypeEnv.addConst aliasRecRawType.ctors[0].name + aliasRecRawType.ctors[0].toVConstant).get! + +def aliasRecRecEnv := + (aliasRecCtorEnv.addConst ``AliasRec.rec + aliasRecGenerationChecked.recursor).get! + +theorem aliasRecTypeEnv_ordered : aliasRecTypeEnv.Ordered := by + refine .const (n := aliasRecRawType.name) + (ci := aliasRecRawType.toVConstant) + recAliasEnv_ordered ?_ rfl + show recAliasEnv.IsType + aliasRecGenerationChecked.block.sourceType.uvars [] + aliasRecGenerationChecked.block.sourceType.type + rw [aliasRecGenerationChecked.block.sourceType_uvars_eq] + exact aliasRecGenerationChecked_wf.rawFamily_isType + +theorem aliasRecRawCtor_wf : + aliasRecRawType.ctors[0].toVConstant.WF aliasRecTypeEnv := by + have hctor : + (⟨aliasRecRawType.ctors[0], + aliasRecViewChecked.constructors[0]⟩ : + VInductDecl.NormalizedCtor) ∈ + aliasRecGenerationChecked.block.ctorPairs := by + exact .head _ + show aliasRecTypeEnv.IsType aliasRecRawType.ctors[0].uvars [] + aliasRecRawType.ctors[0].type + rw [aliasRecGenerationChecked.ctor_uvars_eq hctor] + exact aliasRecGenerationChecked_wf.rawCtor_isType rfl hctor + +theorem aliasRecCtorEnv_ordered : aliasRecCtorEnv.Ordered := + .const (n := aliasRecRawType.ctors[0].name) + (ci := aliasRecRawType.ctors[0].toVConstant) + aliasRecTypeEnv_ordered aliasRecRawCtor_wf rfl + +theorem aliasRecGenerationEnv : + VInductDecl.GenerationEnv aliasRecGenerationChecked aliasRecCtorEnv := by + apply aliasRecGenerationChecked_wf.toGenerationEnv + (envT := aliasRecTypeEnv) + · rfl + · exact (VEnv.addConst_le (show + recAliasEnv.addConst aliasRecRawType.name + aliasRecRawType.toVConstant = some aliasRecTypeEnv from rfl)).trans + (VEnv.addConst_le (show + aliasRecTypeEnv.addConst aliasRecRawType.ctors[0].name + aliasRecRawType.ctors[0].toVConstant = + some aliasRecCtorEnv from rfl)) + · exact VEnv.addConst_le (show + aliasRecTypeEnv.addConst aliasRecRawType.ctors[0].name + aliasRecRawType.ctors[0].toVConstant = + some aliasRecCtorEnv from rfl) + · exact aliasRecCtorEnv_ordered + · rfl + · intro ctor hctor + change ctor ∈ + [⟨aliasRecRawType.ctors[0], + aliasRecViewChecked.constructors[0]⟩] at hctor + obtain rfl := List.mem_singleton.1 hctor + rfl + +theorem aliasRecInfo_tr : + TrConstVal .safe recAliasEnv aliasRecInfo + aliasRecRawType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr recAliasEnv aliasRecInfo.levelParams [] + aliasRecInfo.type aliasRecRawType.type := by + tr_type_expr_tac + exact hshape.to_trExprS recAliasEnv_ordered trivial + ⟨.sort (.succ (.succ .zero)), VEnv.HasType.sort (by decide)⟩ + +theorem aliasRecMkInfo_tr : + TrConstVal .safe aliasRecTypeEnv aliasRecMkInfo + aliasRecRawType.ctors[0] := by + have hAlias : aliasRecTypeEnv.constants ``RecAlias = + some (vconst(type_of% @RecAlias)) := rfl + have hFamily : aliasRecTypeEnv.constants ``AliasRec = + some aliasRecRawType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr aliasRecTypeEnv + aliasRecMkInfo.levelParams [] aliasRecMkInfo.type + aliasRecRawType.ctors[0].type := by + tr_type_expr_tac + obtain ⟨u, htype⟩ := aliasRecRawCtor_wf + exact hshape.to_trExprS aliasRecTypeEnv_ordered trivial + ⟨.sort u, htype⟩ + +theorem aliasRecRecInfo_tr : + TrConstVal .safe aliasRecCtorEnv aliasRecRecInfo + (inductGenerationRecVal aliasRecGenerationChecked) := by + have hAlias : aliasRecCtorEnv.constants ``RecAlias = + some (vconst(type_of% @RecAlias)) := rfl + have hFamily : aliasRecCtorEnv.constants ``AliasRec = + some aliasRecRawType.toVConstant := rfl + have hMk : aliasRecCtorEnv.constants ``AliasRec.mk = + some aliasRecRawType.ctors[0].toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr aliasRecCtorEnv + aliasRecRecInfo.levelParams [] aliasRecRecInfo.type + (inductGenerationRecVal aliasRecGenerationChecked).type := by + tr_type_expr_tac + obtain ⟨u, hrec⟩ := aliasRecGenerationEnv.recursor_wf + exact hshape.to_trExprS aliasRecCtorEnv_ordered trivial + ⟨.sort u, hrec⟩ + +def aliasRecTypeMap : ConstMap := + recAliasMap.insert ``AliasRec aliasRecInfo + +def aliasRecCtorMap : ConstMap := + aliasRecTypeMap.insert ``AliasRec.mk aliasRecMkInfo + +def aliasRecMap : ConstMap := + aliasRecCtorMap.insert ``AliasRec.rec aliasRecRecInfo + +theorem recAliasMap_wf : recAliasMap.WF := (recAlias_trEnv' (safety := .safe)).map_wf + +theorem aliasRecType_fresh : + recAliasMap.find? ``AliasRec = none := by + rw [recAliasMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem aliasRecTypeMap_wf : aliasRecTypeMap.WF := + recAliasMap_wf.insert _ _ aliasRecType_fresh + +theorem aliasRecMk_fresh : + aliasRecTypeMap.find? ``AliasRec.mk = none := by + rw [aliasRecTypeMap, recAliasMap_wf.find?_insert, recAliasMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem aliasRecCtorMap_wf : aliasRecCtorMap.WF := + aliasRecTypeMap_wf.insert _ _ aliasRecMk_fresh + +theorem aliasRecRec_fresh : + aliasRecCtorMap.find? ``AliasRec.rec = none := by + rw [aliasRecCtorMap, aliasRecTypeMap_wf.find?_insert, + aliasRecTypeMap, recAliasMap_wf.find?_insert, recAliasMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +private def aliasRecAddInductTraceWith + (generation_wf : aliasRecGenerationChecked.WF recAliasEnv) : + AddInductTrace recAliasMap recAliasEnv aliasRecRawDecl + aliasRecMap aliasRecFinalEnv := by + refine { + generation := aliasRecGenerationChecked + generation_wf := generation_wf + typeMap := aliasRecTypeMap + typeEnv := aliasRecTypeEnv + ctorMap := aliasRecCtorMap + ctorEnv := aliasRecCtorEnv + recEnv := aliasRecRecEnv + addType := { + info := aliasRecInfo + kind_eq := by simp [aliasRecInfo, InductConstantKind.Matches] + tr := aliasRecInfo_tr + map_fresh := by + change recAliasMap.find? ``AliasRec = none + exact aliasRecType_fresh + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := aliasRecRecInfo + kind_eq := by simp [aliasRecRecInfo, InductConstantKind.Matches] + tr := aliasRecRecInfo_tr + map_fresh := by + change aliasRecCtorMap.find? ``AliasRec.rec = none + exact aliasRecRec_fresh + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ } + exact .cons { + info := aliasRecMkInfo + kind_eq := by simp [aliasRecMkInfo, InductConstantKind.Matches] + tr := aliasRecMkInfo_tr + map_fresh := by simpa [aliasRecRawType] using aliasRecMk_fresh + env_add := rfl + map_add := rfl } .nil + +theorem aliasRec_addInduct : + AddInduct recAliasMap recAliasEnv aliasRecRawDecl + aliasRecMap aliasRecFinalEnv := + ⟨aliasRecAddInductTraceWith aliasRecGenerationChecked_wf⟩ + +theorem aliasRec_trEnv' : + TrEnv' .safe aliasRecMap false aliasRecFinalEnv := + .induct aliasRec_addInduct recAlias_trEnv' + +theorem aliasRec_final_matches_generation : + recAliasEnv.addInductGeneration aliasRecGenerationChecked = + some aliasRecFinalEnv := + aliasRec_addInductGeneration + +theorem aliasRec_env_wf : aliasRecFinalEnv.WF := + aliasRec_trEnv'.wf + +theorem aliasRec_aligned : + Aligned .safe aliasRecMap aliasRecFinalEnv := + aliasRec_trEnv'.aligned + +theorem aliasRec_type_map_lookup : + aliasRecMap.find? ``AliasRec = some aliasRecInfo := by + rw [aliasRecMap, aliasRecCtorMap_wf.find?_insert, + aliasRecCtorMap, aliasRecTypeMap_wf.find?_insert, + aliasRecTypeMap, recAliasMap_wf.find?_insert] + rfl + +theorem aliasRec_type_lookup_unique : + aliasRecInfo.name = ``AliasRec ∧ + TrConstant .safe aliasRecFinalEnv aliasRecInfo + aliasRecRawType.toVConstant := + aliasRec_aligned.find?_uniq aliasRec_type_map_lookup + aliasRecFinalEnv_family_lookup + +theorem aliasRec_mk_map_lookup : + aliasRecMap.find? ``AliasRec.mk = some aliasRecMkInfo := by + rw [aliasRecMap, aliasRecCtorMap_wf.find?_insert, + aliasRecCtorMap, aliasRecTypeMap_wf.find?_insert] + rfl + +theorem aliasRec_mk_lookup_unique : + aliasRecMkInfo.name = ``AliasRec.mk ∧ + TrConstant .safe aliasRecFinalEnv aliasRecMkInfo + aliasRecRawType.ctors[0].toVConstant := + aliasRec_aligned.find?_uniq aliasRec_mk_map_lookup + (aliasRecFinalEnv_ctor_lookup _ (.head _)) + +theorem aliasRec_rec_map_lookup : + aliasRecMap.find? ``AliasRec.rec = some aliasRecRecInfo := by + rw [aliasRecMap, aliasRecCtorMap_wf.find?_insert] + rfl + +theorem aliasRec_rec_lookup_unique : + aliasRecRecInfo.name = ``AliasRec.rec ∧ + TrConstant .safe aliasRecFinalEnv aliasRecRecInfo + aliasRecGenerationChecked.recursor := + aliasRec_aligned.find?_uniq aliasRec_rec_map_lookup + aliasRecFinalEnv_rec_lookup + +/-! ## Binder annotation candidate fixtures -/ + +/- These four definitions are quoted from the running kernel rather than +reconstructed. The resulting minimal environment is sufficient for the +ordinary checker to delta-reduce every annotation gadget while leaving the +shared `Nat` endpoint opaque. -/ +private def annotationOutParamInfo : ConstantInfo := + .defnInfo (kernelDefVal% outParam) + +private def annotationSemiOutParamInfo : ConstantInfo := + .defnInfo (kernelDefVal% semiOutParam) + +private def annotationOptParamInfo : ConstantInfo := + .defnInfo (kernelDefVal% optParam) + +private def annotationAutoParamInfo : ConstantInfo := + .defnInfo (kernelDefVal% autoParam) + +private def annotationKernelMap : ConstMap := + ((({} : ConstMap).insert ``outParam annotationOutParamInfo).insert + ``semiOutParam annotationSemiOutParamInfo).insert + ``optParam annotationOptParamInfo |>.insert + ``autoParam annotationAutoParamInfo + +private def annotationKernelEnv : Kernel.Environment := + Kernel.Environment.ofConstants `_annotationCandidate annotationKernelMap + +private def annotationCandidateContext : AddInductive.Context where + env := annotationKernelEnv + lparams := [] + safety := .safe + allowPrimitive := false + +private def annotationNatExpr : Expr := .const ``Nat [] + +private def outParamDomain : Expr := + .app (.const ``outParam [.succ .zero]) annotationNatExpr + +private def semiOutParamDomain : Expr := + .app (.const ``semiOutParam [.succ .zero]) annotationNatExpr + +private def optParamDomain : Expr := + .app (.app (.const ``optParam [.succ .zero]) annotationNatExpr) + (.lit (.natVal 0)) + +private def autoParamDomain : Expr := + .app (.app (.const ``autoParam [.succ .zero]) annotationNatExpr) + (.const ``Lean.Syntax.missing []) + +/- Each constructor is inhabited at its precise source and consumed indices; +the guards below additionally ensure the executable structural mirror chooses +that constructor. -/ +private def outParamTrace : + AddInductive.CandidateTypeAnnotationTrace + outParamDomain annotationNatExpr := + .outParam [.succ .zero] annotationNatExpr (.identity _) + +private def semiOutParamTrace : + AddInductive.CandidateTypeAnnotationTrace + semiOutParamDomain annotationNatExpr := + .semiOutParam [.succ .zero] annotationNatExpr (.identity _) + +private def optParamTrace : + AddInductive.CandidateTypeAnnotationTrace + optParamDomain annotationNatExpr := + .optParam [.succ .zero] annotationNatExpr + (.lit (.natVal 0)) (.identity _) + +private def autoParamTrace : + AddInductive.CandidateTypeAnnotationTrace + autoParamDomain annotationNatExpr := + .autoParam [.succ .zero] annotationNatExpr + (.const ``Lean.Syntax.missing []) (.identity _) + +private def annotationTraceTag : + AddInductive.CandidateTypeAnnotationTrace source consumed → Nat + | .identity _ => 0 + | .outParam .. => 1 + | .semiOutParam .. => 2 + | .optParam .. => 3 + | .autoParam .. => 4 + +#guard let ⟨consumed, trace⟩ := + AddInductive.CandidateTypeAnnotationTrace.build outParamDomain + consumed.equal annotationNatExpr && annotationTraceTag trace == 1 + +#guard let ⟨consumed, trace⟩ := + AddInductive.CandidateTypeAnnotationTrace.build semiOutParamDomain + consumed.equal annotationNatExpr && annotationTraceTag trace == 2 + +#guard let ⟨consumed, trace⟩ := + AddInductive.CandidateTypeAnnotationTrace.build optParamDomain + consumed.equal annotationNatExpr && annotationTraceTag trace == 3 + +#guard let ⟨consumed, trace⟩ := + AddInductive.CandidateTypeAnnotationTrace.build autoParamDomain + consumed.equal annotationNatExpr && annotationTraceTag trace == 4 + +private def annotationCandidateAccepted (domain expected : Expr) : Bool := + match AddInductive.buildCandidateTypeAnnotations domain with + | .error _ => false + | .ok annotations => + annotations.consumed.equal expected && + match AddInductive.observeCandidateIsDefEq annotationCandidateContext + domain annotations.consumed with + | .ok _ => true + | .error _ => false + +/- These guards cover the complete binder-annotation seam used by the +candidate producer: the transparent structural implementation remains +differentially equal to Lean's opaque helper, followed by an exact successful +ordinary-checker equality observation. -/ +#guard AddInductive.candidateTypeAnnotationsAgree outParamDomain +#guard AddInductive.candidateTypeAnnotationsAgree semiOutParamDomain +#guard AddInductive.candidateTypeAnnotationsAgree optParamDomain +#guard AddInductive.candidateTypeAnnotationsAgree autoParamDomain +#guard annotationCandidateAccepted outParamDomain annotationNatExpr +#guard annotationCandidateAccepted semiOutParamDomain annotationNatExpr +#guard annotationCandidateAccepted optParamDomain annotationNatExpr +#guard annotationCandidateAccepted autoParamDomain annotationNatExpr + +private def annotationIsDefEq (lhs rhs : Expr) := + TypeChecker.M.run annotationCandidateContext.env + annotationCandidateContext.safety annotationCandidateContext.lctx + annotationCandidateContext.lparams annotationCandidateContext.fuel + (TypeChecker.isDefEq lhs rhs) + +/- A genuinely unequal domain is observed as `.ok false`, not a checker +failure, and the candidate boundary rejects it with the dedicated error. -/ +#guard match annotationIsDefEq (.sort .zero) (.sort (.succ .zero)) with + | .ok false => true + | _ => false + +#guard match AddInductive.observeCandidateIsDefEq annotationCandidateContext + (.sort .zero) (.sort (.succ .zero)) with + | .error (.other message) => + message == "normalization candidate changed a binder domain" + | _ => false + +/-! ## Annotated recursive-Pi candidate -/ + +/-- Exact kernel definition and Theory value used to interpret `outParam` +inside a complete recursive constructor candidate. -/ +private def outParamKernelDef : DefinitionVal := + kernelDefVal% outParam + +private def outParamVal : VDefVal where + name := ``outParam + uvars := (vconst(type_of% @outParam) : VConstant).uvars + type := (vconst(type_of% @outParam) : VConstant).type + value := outParamDefEq.rhs + +private theorem outParamInfo_tr : + TrDefVal .safe VEnv.empty annotationOutParamInfo outParamVal := by + refine ⟨⟨⟨by decide, rfl, ?_⟩, rfl⟩, ?_⟩ + · exact .forallE + ⟨_, VEnv.HasType.sort (by decide)⟩ + ⟨_, VEnv.HasType.sort (by decide)⟩ + (.sort rfl) (.sort rfl) + · exact .lam + ⟨_, VEnv.HasType.sort (by decide)⟩ + (.sort rfl) (.bvar rfl) + +private theorem outParamVal_wf : outParamVal.WF VEnv.empty := by + exact VEnv.HasType.lam + (VEnv.HasType.sort (by decide)) + (VEnv.HasType.bvar .zero) + +private def outParamMap : ConstMap := + ({} : ConstMap).insert ``outParam annotationOutParamInfo + +private theorem outParamMap_fresh : + ({} : ConstMap).find? ``outParam = none := by + simp [SMap.find?] + +private theorem outParam_trEnv' {safety : DefinitionSafety} : + TrEnv' safety outParamMap false outParamEnv := + .defn (ci := outParamKernelDef) (ci' := outParamVal) + (outParamInfo_tr.sf_mono DefinitionSafety.le_safe) + outParamMap_fresh outParamVal_wf rfl .empty + +private theorem outParamMap_wf : outParamMap.WF := + (outParam_trEnv' (safety := .safe)).map_wf + +/-- Public map-well-formedness boundary for replay artifacts whose concrete +dependency map is intentionally kept private to this fixture module. -/ +theorem annotatedReplayInputMap_wf : outParamMap.WF := + outParamMap_wf + +private def outParamKernelEnv : Kernel.Environment := + Kernel.Environment.ofConstants `_annotatedPiCandidate outParamMap + +private theorem outParam_trEnv : + TrEnv .safe outParamKernelEnv outParamEnv := by + simpa [TrEnv, outParamKernelEnv, Kernel.Environment.ofConstants] using + outParam_trEnv' + +private theorem outParam_hasPrimitives : + VEnv.HasPrimitives outParamEnv := by + apply VEnv.HasPrimitives.of_avoids + intro n hn + simp only [VEnv.reflectedPrimitiveNames, List.mem_cons, + List.not_mem_nil, or_false] at hn + rcases hn with rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | + rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | + rfl | rfl | rfl | rfl <;> + rfl + +private theorem outParam_safePrimitives : + outParamKernelEnv.find? n = some ci → + Kernel.Environment.primitives.contains n → + ci.safety = .safe ∧ ci.levelParams = [] := by + intro hfind hprim + change outParamMap.find?' n = some ci at hfind + rw [outParamMap_wf.find?'_eq_find?, outParamMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] at hfind + simp [SMap.find?] at hfind + obtain ⟨rfl, rfl⟩ := hfind + simp [Kernel.Environment.primitives, NameSet.ofList] at hprim + simp +decide [NameSet.contains] at hprim + +private def outParamVEnvs : VEnvs where + venv _ := outParamEnv + +private theorem outParamVEnvs_wf : outParamVEnvs.WF outParamKernelEnv where + tr := by + intro safety + exact outParam_trEnv' + hasPrimitives := outParam_hasPrimitives + safePrimitives := outParam_safePrimitives + mono := fun _ => .rfl + projectionReady := ProjectionReady.of_no_ctorInfo <| by + intro name _info h + change outParamMap.find?' name = some (.ctorInfo _info) at h + rw [outParamMap_wf.find?'_eq_find?] at h + simp only [outParamMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] at h + simp [SMap.find?, annotationOutParamInfo] at h + structureEtaReady := StructureEtaReady.of_no_ctorInfo <| by + intro name _info h + change outParamMap.find?' name = some (.ctorInfo _info) at h + rw [outParamMap_wf.find?'_eq_find?] at h + simp only [outParamMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] at h + simp [SMap.find?, annotationOutParamInfo] at h + +/-! ## Definitionally equal constructor parameters -/ + +def annotatedParamInfo : ConstantInfo := kernelInductInfo% AnnotatedParam +def annotatedParamMkInfo : ConstantInfo := kernelCtorInfo% AnnotatedParam.mk +def annotatedParamRecInfo : ConstantInfo := kernelRecInfo% AnnotatedParam.rec +def annotatedParamKernelRuleRhs : VExpr := + kernelRecRuleRhs% AnnotatedParam.rec 0 + +example : annotatedParamKernelRuleRhs = + annotatedParamGenerationChecked.generatedRules[0].rhs := rfl + +private def annotatedParamKernelCtor : Constructor where + name := annotatedParamMkInfo.name + type := annotatedParamMkInfo.type + +private def annotatedParamKernelType : InductiveType where + name := annotatedParamInfo.name + type := annotatedParamInfo.type + ctors := [annotatedParamKernelCtor] + +private def annotatedParamCandidateContext : AddInductive.Context where + env := outParamKernelEnv + lparams := [] + safety := .safe + allowPrimitive := false + +private def annotatedParamExpectedFamilyView : Expr := + .forallE `alpha (.sort (.succ .zero)) + (.sort (.succ .zero)) .default + +private def annotatedParamExpectedCtorView : Expr := + .forallE `alpha (.sort (.succ .zero)) + (.app (.const ``AnnotatedParam []) (.bvar 0)) .implicit + +/- The successful whole metadata pass reaches `checkConstructors`, compares +the stored `outParam Type` constructor prefix with the checked `Type` family +local by ordinary definitional equality, and retains the checked surface in +both candidate views. -/ +#guard match AddInductive.buildNormalizationCandidate 1 + [annotatedParamKernelType] 0 false annotatedParamCandidateContext with + | .ok candidate => + candidate.families.singleton.familyType.type.view.equal + annotatedParamExpectedFamilyView && + match candidate.families.singleton.constructors with + | .cons constructor .nil => + constructor.type.view.equal annotatedParamExpectedCtorView + | .error _ => false + +/- Keep the constructor type closed and independently well typed while making +its declared parameter domain genuinely different. The fixed result avoids a +premature application-type failure, so rejection is specifically the same +constructor-parameter check exercised by the positive. -/ +private def annotatedParamNonDefEqCtor : Constructor where + name := annotatedParamMkInfo.name + type := .forallE `alpha (.sort .zero) + (.app (.const ``AnnotatedParam []) (.sort .zero)) .implicit + +private def annotatedParamNonDefEqType : InductiveType := + { annotatedParamKernelType with ctors := [annotatedParamNonDefEqCtor] } + +#guard match AddInductive.buildNormalizationCandidate 1 + [annotatedParamNonDefEqType] 0 false annotatedParamCandidateContext with + | .error (.other message) => + message == + "arg #1 of 'Ix.Theory.Named.InductiveFixtures.AnnotatedParam.mk' does not match inductive datatype parameters" + | _ => false + +private theorem annotatedParamRawType_wf : + annotatedParamRawType.toVConstant.WF outParamEnv := by + refine ⟨.imax (.succ (.succ .zero)) (.succ (.succ .zero)), ?_⟩ + change outParamEnv.HasType 0 [] + (.forallE + (.app (.const ``outParam [.succ (.succ .zero)]) + (.sort (.succ .zero))) + (.sort (.succ .zero))) + (.sort (.imax (.succ (.succ .zero)) (.succ (.succ .zero)))) + apply VEnv.HasType.forallE + · apply VEnv.HasType.app + (A := .sort (.succ (.succ .zero))) + (B := .sort (.succ (.succ .zero))) + · simpa [VExpr.instL, VLevel.inst] using VEnv.HasType.const + (env := outParamEnv) (U := 0) (c := ``outParam) + (ci := vconst(type_of% @outParam)) + (ls := [.succ (.succ .zero)]) rfl + (by simp; decide) rfl + · exact VEnv.HasType.sort (by decide) + · exact VEnv.HasType.sort (by decide) + +private theorem annotatedParamInfo_tr : + TrConstVal .safe outParamEnv annotatedParamInfo + annotatedParamRawType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr outParamEnv annotatedParamInfo.levelParams [] + annotatedParamInfo.type annotatedParamRawType.type := by + tr_type_expr_tac + obtain ⟨u, htype⟩ := annotatedParamRawType_wf + exact hshape.to_trExprS outParamEnv_ordered trivial ⟨.sort u, htype⟩ + +private def annotatedParamTypeEnv : VEnv := + (outParamEnv.addConst annotatedParamRawType.name + annotatedParamRawType.toVConstant).get! + +private def annotatedParamTypeMap : ConstMap := + outParamMap.insert ``AnnotatedParam annotatedParamInfo + +private theorem annotatedParamType_fresh : + outParamMap.find? ``AnnotatedParam = none := by + rw [outParamMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +private theorem annotatedParamTypeMap_wf : annotatedParamTypeMap.WF := + outParamMap_wf.insert _ _ annotatedParamType_fresh + +private def annotatedParamAddType : + AddInductConstant .induct outParamMap outParamEnv + annotatedParamRawType.toVConstVal annotatedParamTypeMap + annotatedParamTypeEnv where + info := annotatedParamInfo + kind_eq := by simp [annotatedParamInfo, InductConstantKind.Matches] + tr := annotatedParamInfo_tr + map_fresh := by simpa [annotatedParamRawType] using + annotatedParamType_fresh + env_add := rfl + map_add := rfl + +private def annotatedParamTypeKernelEnv : Kernel.Environment := + Kernel.Environment.ofConstants `_annotatedParamCandidate + annotatedParamTypeMap + +private theorem annotatedParamTypeEnv_ordered : + annotatedParamTypeEnv.Ordered := + .const (n := annotatedParamRawType.name) + (ci := annotatedParamRawType.toVConstant) + outParamEnv_ordered annotatedParamRawType_wf rfl + +private def annotatedParamCtorCandidateContext : AddInductive.Context where + env := annotatedParamTypeKernelEnv + lparams := [] + safety := .safe + allowPrimitive := false + +private theorem annotatedParamType_lookup_outParam : + annotatedParamTypeKernelEnv.find? ``outParam = + some annotationOutParamInfo := by + change annotatedParamTypeMap.find?' ``outParam = + some annotationOutParamInfo + rw [annotatedParamTypeMap_wf.find?'_eq_find?, annotatedParamTypeMap, + outParamMap_wf.find?_insert] + rw [outParamMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] + rfl + +private theorem annotatedParamType_lookup_family : + annotatedParamTypeKernelEnv.find? ``AnnotatedParam = + some annotatedParamInfo := by + change annotatedParamTypeMap.find?' ``AnnotatedParam = + some annotatedParamInfo + rw [annotatedParamTypeMap_wf.find?'_eq_find?, annotatedParamTypeMap, + outParamMap_wf.find?_insert] + rfl + +@[simp] private theorem annotatedParamType_get_outParam : + annotatedParamTypeKernelEnv.get ``outParam = + .ok annotationOutParamInfo := by + unfold Kernel.Environment.get + rw [annotatedParamType_lookup_outParam] + rfl + +@[simp] private theorem annotatedParamType_get_family : + annotatedParamTypeKernelEnv.get ``AnnotatedParam = + .ok annotatedParamInfo := by + unfold Kernel.Environment.get + rw [annotatedParamType_lookup_family] + rfl + +def annotatedPiInfo : ConstantInfo := kernelInductInfo% AnnotatedPi +def annotatedPiMkInfo : ConstantInfo := kernelCtorInfo% AnnotatedPi.mk +def annotatedPiRecInfo : ConstantInfo := kernelRecInfo% AnnotatedPi.rec +def annotatedPiKernelRuleRhs : VExpr := + kernelRecRuleRhs% AnnotatedPi.rec 0 + +example : annotatedPiKernelRuleRhs = + annotatedPiGenerationChecked.generatedRules[0].rhs := rfl + +private def annotatedPiKernelCtor : Constructor where + name := annotatedPiMkInfo.name + type := annotatedPiMkInfo.type + +private def annotatedPiKernelType : InductiveType where + name := annotatedPiInfo.name + type := annotatedPiInfo.type + ctors := [annotatedPiKernelCtor] + +/- The whole-candidate negative keeps the actual AnnotatedPi family and +constructor metadata, but gives the annotation symbol its correct type as an +opaque constant. Ordinary metadata typing can therefore reach the recursive +constructor candidate, while `outParam Prop` is no longer definitionally equal +to the syntactically consumed `Prop`. -/ +private def annotatedPiOpaqueOutParamInfo : ConstantInfo := + .axiomInfo { + name := ``outParam + levelParams := outParamKernelDef.levelParams + type := outParamKernelDef.type + isUnsafe := false } + +private def annotatedPiOpaqueOutParamMap : ConstMap := + ({} : ConstMap).insert ``outParam annotatedPiOpaqueOutParamInfo + +private def annotatedPiOpaqueOutParamEnv : Kernel.Environment := + Kernel.Environment.ofConstants `_annotatedPiOpaqueAnnotation + annotatedPiOpaqueOutParamMap + +private def annotatedPiOpaqueOutParamContext : AddInductive.Context where + env := annotatedPiOpaqueOutParamEnv + lparams := [] + safety := .safe + allowPrimitive := false + +/- This is a complete family/constructor candidate rejection, not the earlier +leaf-level `isDefEq` test. The dedicated message proves failure occurs at the +raw-to-consumed binder equality boundary before any semantic package or +transaction can be assembled. -/ +#guard match AddInductive.buildNormalizationCandidate 0 + [annotatedPiKernelType] 0 false annotatedPiOpaqueOutParamContext with + | .error (.other message) => + message == "normalization candidate changed a binder domain" + | _ => false + +private theorem annotatedPiRawType_wf : + annotatedPiRawType.toVConstant.WF outParamEnv := by + exact ⟨_, VEnv.HasType.sort (by decide)⟩ + +private theorem annotatedPiInfo_tr : + TrConstVal .safe outParamEnv annotatedPiInfo + annotatedPiRawType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr outParamEnv annotatedPiInfo.levelParams [] + annotatedPiInfo.type annotatedPiRawType.type := by + tr_type_expr_tac + obtain ⟨u, htype⟩ := annotatedPiRawType_wf + exact hshape.to_trExprS outParamEnv_ordered trivial ⟨.sort u, htype⟩ + +private def annotatedPiTypeEnv : VEnv := + (outParamEnv.addConst annotatedPiRawType.name + annotatedPiRawType.toVConstant).get! + +private def annotatedPiTypeMap : ConstMap := + outParamMap.insert ``AnnotatedPi annotatedPiInfo + +private theorem annotatedPiType_fresh : + outParamMap.find? ``AnnotatedPi = none := by + rw [outParamMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +private theorem annotatedPiTypeMap_wf : annotatedPiTypeMap.WF := + outParamMap_wf.insert _ _ annotatedPiType_fresh + +private def annotatedPiAddType : + AddInductConstant .induct outParamMap outParamEnv + annotatedPiRawType.toVConstVal annotatedPiTypeMap + annotatedPiTypeEnv where + info := annotatedPiInfo + kind_eq := by simp [annotatedPiInfo, InductConstantKind.Matches] + tr := annotatedPiInfo_tr + map_fresh := by simpa [annotatedPiRawType] using annotatedPiType_fresh + env_add := rfl + map_add := rfl + +private def annotatedPiTypeKernelEnv : Kernel.Environment := + Kernel.Environment.ofConstants `_annotatedPiCandidate annotatedPiTypeMap + +private theorem annotatedPiTypeEnv_ordered : annotatedPiTypeEnv.Ordered := + .const (n := annotatedPiRawType.name) + (ci := annotatedPiRawType.toVConstant) + outParamEnv_ordered annotatedPiRawType_wf rfl + +private def annotatedPiFamilyCandidateContext : AddInductive.Context where + env := outParamKernelEnv + lparams := [] + safety := .safe + allowPrimitive := false + +private def annotatedPiCtorCandidateContext : AddInductive.Context where + env := annotatedPiTypeKernelEnv + lparams := [] + safety := .safe + allowPrimitive := false + +private theorem annotatedPiType_lookup_outParam : + annotatedPiTypeKernelEnv.find? ``outParam = + some annotationOutParamInfo := by + change annotatedPiTypeMap.find?' ``outParam = + some annotationOutParamInfo + rw [annotatedPiTypeMap_wf.find?'_eq_find?, annotatedPiTypeMap, + outParamMap_wf.find?_insert] + rw [outParamMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] + rfl + +private theorem annotatedPiType_lookup_family : + annotatedPiTypeKernelEnv.find? ``AnnotatedPi = + some annotatedPiInfo := by + change annotatedPiTypeMap.find?' ``AnnotatedPi = some annotatedPiInfo + rw [annotatedPiTypeMap_wf.find?'_eq_find?, annotatedPiTypeMap, + outParamMap_wf.find?_insert] + rfl + +@[simp] private theorem annotatedPiType_get_outParam : + annotatedPiTypeKernelEnv.get ``outParam = + .ok annotationOutParamInfo := by + unfold Kernel.Environment.get + rw [annotatedPiType_lookup_outParam] + rfl + +@[simp] private theorem annotatedPiType_get_family : + annotatedPiTypeKernelEnv.get ``AnnotatedPi = + .ok annotatedPiInfo := by + unfold Kernel.Environment.get + rw [annotatedPiType_lookup_family] + rfl + +@[simp] private theorem annotatedPi_checkLevelZero + (context : TypeChecker.Context) : + TypeChecker.Inner.checkLevel context .zero = .ok () := by + simp [TypeChecker.Inner.checkLevel, Level.getUndefParam, Level.forEach, + Level.hasParam_eq, Level.hasParam'] + rfl + +@[simp] private theorem annotatedPi_checkLevelSuccZero + (context : TypeChecker.Context) : + TypeChecker.Inner.checkLevel context (.succ .zero) = .ok () := by + simp [TypeChecker.Inner.checkLevel, Level.getUndefParam, Level.forEach, + Level.hasParam_eq, Level.hasParam'] + rfl + +open private mkLevelIMaxCore mkLevelMaxCore from Lean.Level in +@[simp] private theorem annotatedPi_mkLevelIMaxSuccZero : + mkLevelIMax' (.succ .zero) (.succ .zero) = .succ .zero := by + simp [mkLevelIMax', mkLevelIMaxCore, mkLevelMax', mkLevelMaxCore] + +private theorem annotatedPiExceptPure + {α} (a : α) : + (pure a : Except Kernel.Exception α) = .ok a := rfl + +@[simp] private theorem annotatedPiInferConstantOutParam + (lctx : LocalContext) : + TypeChecker.Inner.inferConstant + ({ env := annotatedPiTypeKernelEnv, lctx := lctx } : + TypeChecker.Context) + ``outParam [.succ .zero] false = + .ok (.forallE `α (.sort (.succ .zero)) + (.sort (.succ .zero)) .default) := by + unfold TypeChecker.Inner.inferConstant + rw [show annotatedPiTypeKernelEnv.get ``outParam = + .ok annotationOutParamInfo by exact annotatedPiType_get_outParam] + simp [annotationOutParamInfo, Bind.bind, Except.bind, + annotatedPiExceptPure, + ConstantInfo.levelParams, ConstantInfo.isUnsafe, + ConstantInfo.instantiateTypeLevelParams, + ConstantInfo.toConstantVal, + ConstantVal.instantiateTypeLevelParams, + Expr.instantiateLevelParams_eq, Expr.instantiateLevelParamsCore', + Level.substParams'] + +@[simp] private theorem annotatedPiInferConstantFamily + (lctx : LocalContext) : + TypeChecker.Inner.inferConstant + ({ env := annotatedPiTypeKernelEnv, lctx := lctx } : + TypeChecker.Context) + ``AnnotatedPi [] false = + .ok (.sort (.succ .zero)) := by + unfold TypeChecker.Inner.inferConstant + rw [show annotatedPiTypeKernelEnv.get ``AnnotatedPi = + .ok annotatedPiInfo by exact annotatedPiType_get_family] + rfl + +@[simp] private theorem annotatedPiInferConstantOutParamCandidate : + TypeChecker.Inner.inferConstant + annotatedPiCtorCandidateContext.toTypeChecker + ``outParam [.succ .zero] false = + .ok (.forallE `α (.sort (.succ .zero)) + (.sort (.succ .zero)) .default) := by + simp [annotatedPiCtorCandidateContext, AddInductive.Context.toTypeChecker] + +@[simp] private theorem annotatedPiEnsureForall + (name dom body bi source methods context state) : + TypeChecker.Inner.ensureForallCore (.forallE name dom body bi) source + methods context state = + .ok (.forallE name dom body bi, state) := by + unfold TypeChecker.Inner.ensureForallCore + rfl + +@[simp] private theorem annotatedPiEnsureSort + (u source methods context state) : + TypeChecker.Inner.ensureSortCore (.sort u) source methods context state = + .ok (.sort u, state) := by + unfold TypeChecker.Inner.ensureSortCore + rfl + +@[simp] private theorem annotatedPiForall_bindingDomain + (name dom body bi) : + (Expr.forallE name dom body bi).bindingDomain! = dom := rfl + +@[simp] private theorem annotatedPiForall_bindingBody + (name dom body bi) : + (Expr.forallE name dom body bi).bindingBody! = body := rfl + +@[simp] private theorem annotatedPiSort_instantiate1' + (u arg) : + (Expr.sort u).instantiate1' arg = .sort u := rfl + +@[simp] private theorem annotatedPiConst_beq_sort + (name levels u) : + ((.const name levels : Expr) == .sort u) = false := by + change Expr.eqv (.const name levels) (.sort u) = false + rw [Expr.eqv_eq] + rfl + +@[simp] private theorem annotatedPiSort_beq_const + (u name levels) : + ((.sort u : Expr) == .const name levels) = false := by + change Expr.eqv (.sort u) (.const name levels) = false + rw [Expr.eqv_eq] + rfl + +@[simp] private theorem annotatedPiApp_beq_const + (fn arg name levels) : + ((.app fn arg : Expr) == .const name levels) = false := by + change Expr.eqv (.app fn arg) (.const name levels) = false + rw [Expr.eqv_eq] + rfl + +@[simp] private theorem annotatedPiForall_beq_const + (binderName dom body bi name levels) : + ((.forallE binderName dom body bi : Expr) == .const name levels) = + false := by + change Expr.eqv (.forallE binderName dom body bi) + (.const name levels) = false + rw [Expr.eqv_eq] + rfl + +@[simp] private theorem annotatedPiOutParam_beq_family : + ((.const ``outParam [.succ .zero] : Expr) == + .const ``AnnotatedPi []) = false := by + change Expr.eqv (.const ``outParam [.succ .zero]) + (.const ``AnnotatedPi []) = false + rw [Expr.eqv_eq] + rfl + +@[simp] private theorem annotatedPiFamilyCacheAfterForall + (cache : InferCache) (name : Name) (dom body result : Expr) + (bi : BinderInfo) : + (((cache.insert (.const ``AnnotatedPi []) (.sort (.succ .zero))).insert + (.forallE name dom body bi) result)[ + (.const ``AnnotatedPi [] : Expr)]?) = + some (.sort (.succ .zero)) := by + rw [Std.HashMap.getElem?_insert, + annotatedPiForall_beq_const] + exact Std.HashMap.getElem?_insert_self + +private def annotatedPiOutParamFnType : Expr := + .forallE `α (.sort (.succ .zero)) + (.sort (.succ .zero)) .default + +@[simp] private theorem annotatedPiOutParamFnType_bindingDomain : + annotatedPiOutParamFnType.bindingDomain! = + .sort (.succ .zero) := rfl + +@[simp] private theorem annotatedPiOutParamFnType_instantiatedBody : + annotatedPiOutParamFnType.bindingBody!.instantiate1 (.sort .zero) = + .sort (.succ .zero) := by + simp [annotatedPiOutParamFnType, Expr.bindingBody!, + Expr.instantiate1_eq, Expr.instantiate1'] + +@[simp] private theorem annotatedPiSort_notEagerReduce : + (Expr.sort .zero).isAppOfArity ``eagerReduce 2 = false := rfl + +private def annotatedPiOutParamFnState : TypeChecker.State := + { ({} : TypeChecker.State) with + inferTypeC := ({} : TypeChecker.State).inferTypeC.insert + (.const ``outParam [.succ .zero]) annotatedPiOutParamFnType } + +private def annotatedPiOutParamArgState : TypeChecker.State := + { annotatedPiOutParamFnState with + inferTypeC := annotatedPiOutParamFnState.inferTypeC.insert + (.sort .zero) (.sort (.succ .zero)) } + +private def annotatedPiWithSuccessCache + (state : TypeChecker.State) (m : Std.HashSet (Expr × Expr)) : + TypeChecker.State := + { state with success := m } + +private theorem annotatedPiIsDefEqSort + (fuel : Nat) + (context : TypeChecker.Context) + (initial : TypeChecker.State) : + TypeChecker.Inner.isDefEq + (.sort (.succ .zero)) (.sort (.succ .zero)) + (TypeChecker.Methods.withFuel fuel) context initial = + .ok (true, initial) := by + unfold TypeChecker.Inner.isDefEq + rw [if_pos (Expr.eqv_refl _)] + rfl + +private def annotatedPiCtorExpectedView : Expr := + match annotatedPiMkInfo.type with + | .forallE outerName (.forallE innerName _ innerBody innerInfo) + outerBody outerInfo => + .forallE outerName + (.forallE innerName (.sort .zero) innerBody innerInfo) + outerBody outerInfo + | source => source + +private def annotatedPiOuterName : Name := + .mkNum + (.mkStr + (.mkStr (.mkStr (.mkStr .anonymous "a") "_@") "_internal") + "_hyg") + 0 + +@[simp] private theorem annotatedPiFamilyType_noLooseBVars : + annotatedPiInfo.type.hasLooseBVars = false := by + rw [show annotatedPiInfo.type = .sort (.succ .zero) by rfl] + simp [Expr.hasLooseBVars, Expr.looseBVarRange'] + +@[simp] private theorem emptyCheckTypeCache_annotatedPiFamily : + (({} : TypeChecker.State).inferTypeC)[annotatedPiInfo.type]? = none := by + exact Std.HashMap.getElem?_empty + +@[simp] private theorem annotatedPiFamily_checkLevel : + TypeChecker.Inner.checkLevel + annotatedPiFamilyCandidateContext.toTypeChecker (.succ .zero) = + .ok () := by + simp [TypeChecker.Inner.checkLevel, Level.getUndefParam, Level.forEach, Level.hasParam_eq, + Level.hasParam'] + rfl + +@[simp] private theorem annotatedPiRecMGet (methods context state) : + (get : TypeChecker.RecM TypeChecker.State) methods context state = + .ok (state, state) := rfl + +@[simp] private theorem annotatedPiRecMReadContext + (methods context state) : + (readThe TypeChecker.Context : TypeChecker.RecM TypeChecker.Context) + methods context state = + .ok (context, state) := rfl + +@[simp] private theorem annotatedPiRecMModify + (f : TypeChecker.State → TypeChecker.State) + (methods context state) : + (modify f : TypeChecker.RecM PUnit) methods context state = + .ok (.unit, f state) := rfl + +@[simp] private theorem annotatedPiRecMPure + {α} (a : α) (methods context state) : + (pure a : TypeChecker.RecM α) methods context state = + .ok (a, state) := rfl + +@[simp] private theorem annotatedPiRecMBind + {α β} (x : TypeChecker.RecM α) + (f : α → TypeChecker.RecM β) (methods context state) : + (x >>= f) methods context state = + match x methods context state with + | .error e => .error e + | .ok (a, state') => f a methods context state' := by + simp [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + cases h : x methods context state with + | error => rfl + | ok value => cases value; rfl + +@[simp] private theorem annotatedPiRecMLiftExceptOk + {α} (a : α) (methods context state) : + (liftM (.ok a : Except Kernel.Exception α) : + TypeChecker.RecM α) methods context state = + .ok (a, state) := rfl + +@[simp] private theorem annotatedPiGetNGen + (context : TypeChecker.Context) (state : TypeChecker.State) : + (getNGen : TypeChecker.M NameGenerator) context state = + .ok (state.ngen, state) := rfl + +@[simp] private theorem annotatedPiSetNGen + (ngen : NameGenerator) (context : TypeChecker.Context) + (state : TypeChecker.State) : + (setNGen ngen : TypeChecker.M PUnit) context state = + .ok (.unit, { state with ngen }) := rfl + +@[simp] private theorem annotatedPiMPure + {α} (a : α) (context : TypeChecker.Context) + (state : TypeChecker.State) : + (pure a : TypeChecker.M α) context state = + .ok (a, state) := rfl + +@[simp] private theorem annotatedPiRecMWithReader + {α} (f : LocalContext → LocalContext) + (x : TypeChecker.RecM α) (methods : TypeChecker.Methods) + (context : TypeChecker.Context) (state : TypeChecker.State) : + (MonadWithReaderOf.withReader (m := TypeChecker.RecM) f x) + methods context state = + x methods { context with lctx := f context.lctx } state := rfl + +private theorem annotatedPiWithLocalDecl + {α} (name : Name) (bi : BinderInfo) (ty : Expr) + (k : Expr → TypeChecker.RecM α) + (methods : TypeChecker.Methods) (context : TypeChecker.Context) + (state : TypeChecker.State) : + (withLocalDecl (m := TypeChecker.RecM) name bi ty k) + methods context state = + k (.fvar ⟨state.ngen.curr⟩) methods + { context with + lctx := context.lctx.mkLocalDecl + ⟨state.ngen.curr⟩ name ty bi } + { state with ngen := state.ngen.next } := rfl + +@[simp] private theorem annotatedPiInferTypeFuel + (n e inferOnly context state) : + TypeChecker.Inner.inferType e inferOnly + (TypeChecker.Methods.withFuel (n + 1)) context state = + TypeChecker.Inner.inferType' e inferOnly + (TypeChecker.Methods.withFuel n) context state := rfl + +@[simp] private theorem annotatedPiInferTypeFamilyCore + (n : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (hcache : + state.inferTypeC[(.const ``AnnotatedPi [] : Expr)]? = none) : + TypeChecker.Inner.inferType' (.const ``AnnotatedPi []) false + (TypeChecker.Methods.withFuel n) + ({ env := annotatedPiTypeKernelEnv, lctx := lctx } : + TypeChecker.Context) + state = + .ok (.sort (.succ .zero), + { state with + inferTypeC := state.inferTypeC.insert + (.const ``AnnotatedPi []) (.sort (.succ .zero)) }) := by + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +@[simp] private theorem annotatedPiInferTypeFamily + (n : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (hcache : + state.inferTypeC[(.const ``AnnotatedPi [] : Expr)]? = none) : + TypeChecker.Inner.inferType (.const ``AnnotatedPi []) false + (TypeChecker.Methods.withFuel (n + 1)) + ({ env := annotatedPiTypeKernelEnv, lctx := lctx } : + TypeChecker.Context) + state = + .ok (.sort (.succ .zero), + { state with + inferTypeC := state.inferTypeC.insert + (.const ``AnnotatedPi []) (.sort (.succ .zero)) }) := + annotatedPiInferTypeFamilyCore n lctx state hcache + +@[simp] private theorem annotatedPiInferTypeFamilyCached + (n : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (hcache : + state.inferTypeC[(.const ``AnnotatedPi [] : Expr)]? = + some (.sort (.succ .zero))) : + TypeChecker.Inner.inferType' (.const ``AnnotatedPi []) false + (TypeChecker.Methods.withFuel n) + ({ env := annotatedPiTypeKernelEnv, lctx := lctx } : + TypeChecker.Context) + state = + .ok (.sort (.succ .zero), state) := by + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +@[simp] private theorem annotatedPiInferTypeFamilyAfterForall + (n : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (name : Name) (dom body result : Expr) (bi : BinderInfo) : + let state' : TypeChecker.State := + { state with + inferTypeC := + (state.inferTypeC.insert + (.const ``AnnotatedPi []) (.sort (.succ .zero))).insert + (.forallE name dom body bi) result } + TypeChecker.Inner.inferType' (.const ``AnnotatedPi []) false + (TypeChecker.Methods.withFuel n) + ({ env := annotatedPiTypeKernelEnv, lctx := lctx } : + TypeChecker.Context) + state' = + .ok (.sort (.succ .zero), state') := by + dsimp only + apply annotatedPiInferTypeFamilyCached + exact annotatedPiFamilyCacheAfterForall + state.inferTypeC name dom body result bi + +private def annotatedPiFamilyCheckTypeState : TypeChecker.State := + { ({} : TypeChecker.State) with + inferTypeC := ({} : TypeChecker.State).inferTypeC.insert + (.sort (.succ .zero)) (.sort (.succ (.succ .zero))) } + +private theorem annotatedPiFamily_checkTypeInner : + TypeChecker.Inner.inferType annotatedPiInfo.type false + (TypeChecker.Methods.withFuel 10000) + annotatedPiFamilyCandidateContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (.sort (.succ (.succ .zero)), + annotatedPiFamilyCheckTypeState) := by + change + TypeChecker.Inner.inferType' (.sort (.succ .zero)) false + (TypeChecker.Methods.withFuel 9999) + annotatedPiFamilyCandidateContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (.sort (.succ (.succ .zero)), + annotatedPiFamilyCheckTypeState) + unfold TypeChecker.Inner.inferType' + simp [annotatedPiFamilyCheckTypeState, Expr.hasLooseBVars, Expr.looseBVarRange', Bind.bind, + ReaderT.bind, StateT.bind, Except.bind] + +private theorem annotatedPiFamily_checkTypeM : + TypeChecker.M.run annotatedPiFamilyCandidateContext.env + annotatedPiFamilyCandidateContext.safety + annotatedPiFamilyCandidateContext.lctx + annotatedPiFamilyCandidateContext.lparams + annotatedPiFamilyCandidateContext.fuel + (TypeChecker.checkType annotatedPiInfo.type) = + .ok (.sort (.succ (.succ .zero))) := by + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType annotatedPiInfo.type false + (TypeChecker.Methods.withFuel 10000) + annotatedPiFamilyCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = + .ok (.sort (.succ (.succ .zero))) + rw [annotatedPiFamily_checkTypeInner] + rfl + +private theorem annotatedPiFamily_whnfM : + TypeChecker.M.run annotatedPiFamilyCandidateContext.env + annotatedPiFamilyCandidateContext.safety + annotatedPiFamilyCandidateContext.lctx + annotatedPiFamilyCandidateContext.lparams + annotatedPiFamilyCandidateContext.fuel + (TypeChecker.whnf annotatedPiInfo.type) = + .ok annotatedPiInfo.type := by rfl + +private theorem annotatedPiCtor_checkTypeM : + TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.checkType annotatedPiMkInfo.type) = + .ok (.sort (.succ .zero)) := by + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType annotatedPiMkInfo.type false + (TypeChecker.Methods.withFuel 10000) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = + .ok (.sort (.succ .zero)) + rw [show annotatedPiMkInfo.type = + .forallE annotatedPiOuterName + (.forallE `p + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) + (.const ``AnnotatedPi []) .default) + (.const ``AnnotatedPi []) .default by rfl] + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' + (.forallE annotatedPiOuterName + (.forallE `p + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) + (.const ``AnnotatedPi []) .default) + (.const ``AnnotatedPi []) .default) + false (TypeChecker.Methods.withFuel 9999) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = + .ok (.sort (.succ .zero)) + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', TypeChecker.Inner.inferType', + TypeChecker.Inner.inferForall, TypeChecker.Inner.inferForall.loop, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + rw [annotatedPiIsDefEqSort 9997] + simp [Expr.instantiate1', annotatedPiWithLocalDecl, annotatedPiCtorCandidateContext, + AddInductive.Context.toTypeChecker, Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [annotatedPiInferTypeFamilyCached (hcache := by + apply annotatedPiFamilyCacheAfterForall)] + simp [Expr.sortLevel!, annotatedPi_mkLevelIMaxSuccZero] + rfl + +private theorem annotatedPiCtor_whnfM : + TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.whnf annotatedPiMkInfo.type) = + .ok annotatedPiMkInfo.type := by rfl + +/-! ## Checker-produced alias normalization certificates -/ + +/-- Minimal kernel environment used to replay family-result WHNF without +bringing unrelated constants or primitives into the certificate. -/ +private def aliasFormerNormalizationKernelEnv : Kernel.Environment := + Kernel.Environment.ofConstants `_aliasFormerNormalization + typeFamilyAliasMap + +private theorem aliasFormerNormalization_trEnv : + TrEnv .safe aliasFormerNormalizationKernelEnv typeFamilyAliasEnv := by + simpa [TrEnv, aliasFormerNormalizationKernelEnv, + Kernel.Environment.ofConstants] using + typeFamilyAlias_trEnv' + +private theorem aliasFormerNormalization_hasPrimitives : + VEnv.HasPrimitives typeFamilyAliasEnv := by + apply VEnv.HasPrimitives.of_avoids + intro n hn + simp only [VEnv.reflectedPrimitiveNames, List.mem_cons, + List.not_mem_nil, or_false] at hn + rcases hn with rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | + rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | + rfl | rfl | rfl | rfl <;> + rfl + +private theorem aliasFormerNormalization_safePrimitives : + aliasFormerNormalizationKernelEnv.find? n = some ci → + Kernel.Environment.primitives.contains n → + ci.safety = .safe ∧ ci.levelParams = [] := by + intro hfind hprim + change typeFamilyAliasMap.find?' n = some ci at hfind + rw [typeFamilyAliasMap_wf.find?'_eq_find?, + typeFamilyAliasMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] at hfind + simp [SMap.find?] at hfind + obtain ⟨rfl, rfl⟩ := hfind + simp [Kernel.Environment.primitives, NameSet.ofList] at hprim + simp +decide [NameSet.contains] at hprim + +private def aliasFormerNormalizationVEnvs : VEnvs where + venv _ := typeFamilyAliasEnv + +private theorem aliasFormerNormalizationVEnvs_wf : + aliasFormerNormalizationVEnvs.WF + aliasFormerNormalizationKernelEnv where + tr := by + intro safety + change TrEnv' _ typeFamilyAliasMap false typeFamilyAliasEnv + exact typeFamilyAlias_trEnv' + hasPrimitives := aliasFormerNormalization_hasPrimitives + safePrimitives := aliasFormerNormalization_safePrimitives + mono := fun _ => .rfl + projectionReady := ProjectionReady.of_no_ctorInfo <| by + intro name _info h + change typeFamilyAliasMap.find?' name = some (.ctorInfo _info) at h + rw [typeFamilyAliasMap_wf.find?'_eq_find?] at h + simp only [typeFamilyAliasMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] at h + simp [SMap.find?, typeFamilyAliasInfo] at h + structureEtaReady := StructureEtaReady.of_no_ctorInfo <| by + intro name _info h + change typeFamilyAliasMap.find?' name = some (.ctorInfo _info) at h + rw [typeFamilyAliasMap_wf.find?'_eq_find?] at h + simp only [typeFamilyAliasMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] at h + simp [SMap.find?, typeFamilyAliasInfo] at h + +private def aliasFormerNormalizationContext : TypeChecker.VContext := + TypeChecker.VContext.mk' aliasFormerNormalizationVEnvs_wf + (fuel := { whnf := 2 }) + +private def aliasFormerNormalizationRawContext : TypeChecker.Context where + env := aliasFormerNormalizationKernelEnv + fuel := { whnf := 2 } + +private def aliasFormerCheckTypeState (state : TypeChecker.State) : + TypeChecker.State := + { state with + inferTypeC := state.inferTypeC.insert + (.const ``TypeFamilyAlias []) + (.sort (.succ (.succ .zero))) } + +/-- Insert the raw AliasFormer family before checking its constructor type. -/ +private def aliasFormerCtorNormalizationAddType : + AddInductConstant .induct typeFamilyAliasMap typeFamilyAliasEnv + aliasFormerRawType.toVConstVal aliasFormerTypeMap + aliasFormerTypeEnv where + info := aliasFormerInfo + kind_eq := by simp [aliasFormerInfo, InductConstantKind.Matches] + tr := aliasFormerInfo_tr + map_fresh := by simpa [aliasFormerRawType] using aliasFormerType_fresh + env_add := rfl + map_add := rfl + +private def aliasFormerCtorNormalizationKernelEnv : Kernel.Environment := + Kernel.Environment.ofConstants `_aliasFormerNormalization + aliasFormerTypeMap + +private def aliasFormerCtorNormalizationRawContext : TypeChecker.Context where + env := aliasFormerCtorNormalizationKernelEnv + fuel := { whnf := 2 } + +private def aliasFormerCtorCheckTypeState (state : TypeChecker.State) : + TypeChecker.State := + { state with + inferTypeC := state.inferTypeC.insert + (.const ``AliasFormer []) + (.const ``TypeFamilyAlias []) } + +/-- Insert only the raw `AliasRec` family into the replay environment. This is +the exact staging at which constructor domains are normalized. -/ +private def aliasRecNormalizationAddType : + AddInductConstant .induct recAliasMap recAliasEnv + aliasRecRawType.toVConstVal aliasRecTypeMap aliasRecTypeEnv where + info := aliasRecInfo + kind_eq := by simp [aliasRecInfo, InductConstantKind.Matches] + tr := aliasRecInfo_tr + map_fresh := by simpa [aliasRecRawType] using aliasRecType_fresh + env_add := rfl + map_add := rfl + +private theorem aliasRecNormalization_trEnv' {safety : DefinitionSafety} : + TrEnv' safety aliasRecTypeMap false aliasRecTypeEnv := + .inductStaging aliasRecNormalizationAddType + ⟨.succ (.succ .zero), VEnv.HasType.sort (by decide)⟩ + recAlias_trEnv' + +private def aliasRecNormalizationKernelEnv : Kernel.Environment := + Kernel.Environment.ofConstants `_aliasRecNormalization aliasRecTypeMap + +private theorem aliasRecNormalization_trEnv : + TrEnv .safe aliasRecNormalizationKernelEnv aliasRecTypeEnv := by + simpa [TrEnv, aliasRecNormalizationKernelEnv, + Kernel.Environment.ofConstants] using + aliasRecNormalization_trEnv' + +private theorem aliasRecNormalization_hasPrimitives : + VEnv.HasPrimitives aliasRecTypeEnv := by + apply VEnv.HasPrimitives.of_avoids + intro n hn + simp only [VEnv.reflectedPrimitiveNames, List.mem_cons, + List.not_mem_nil, or_false] at hn + rcases hn with rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | + rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | + rfl | rfl | rfl | rfl <;> + rfl + +private theorem aliasRecNormalization_safePrimitives : + aliasRecNormalizationKernelEnv.find? n = some ci → + Kernel.Environment.primitives.contains n → + ci.safety = .safe ∧ ci.levelParams = [] := by + intro hfind hprim + change aliasRecTypeMap.find?' n = some ci at hfind + rw [aliasRecTypeMap_wf.find?'_eq_find?, + aliasRecTypeMap, recAliasMap_wf.find?_insert] at hfind + split at hfind + · rename_i heq + simp at heq + subst n + simp [Kernel.Environment.primitives, NameSet.ofList] at hprim + simp +decide [NameSet.contains] at hprim + · rw [recAliasMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] at hfind + simp [SMap.find?] at hfind + obtain ⟨rfl, rfl⟩ := hfind + simp [Kernel.Environment.primitives, NameSet.ofList] at hprim + simp +decide [NameSet.contains] at hprim + +private def aliasRecNormalizationVEnvs : VEnvs where + venv _ := aliasRecTypeEnv + +private theorem aliasRecNormalizationVEnvs_wf : + aliasRecNormalizationVEnvs.WF aliasRecNormalizationKernelEnv where + tr := by + intro safety + change TrEnv' _ aliasRecTypeMap false aliasRecTypeEnv + exact aliasRecNormalization_trEnv' + hasPrimitives := aliasRecNormalization_hasPrimitives + safePrimitives := aliasRecNormalization_safePrimitives + mono := fun _ => .rfl + projectionReady := ProjectionReady.of_no_ctorInfo <| by + intro name _info h + change aliasRecTypeMap.find?' name = some (.ctorInfo _info) at h + rw [aliasRecTypeMap_wf.find?'_eq_find?] at h + simp only [aliasRecTypeMap, recAliasMap_wf.find?_insert] at h + simp only [recAliasMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] at h + by_cases hAliasRec : ``AliasRec = name <;> + by_cases hRecAlias : ``RecAlias = name <;> + simp +decide [hAliasRec, hRecAlias, SMap.find?, aliasRecInfo, + recAliasInfo] at h + structureEtaReady := StructureEtaReady.of_no_ctorInfo <| by + intro name _info h + change aliasRecTypeMap.find?' name = some (.ctorInfo _info) at h + rw [aliasRecTypeMap_wf.find?'_eq_find?] at h + simp only [aliasRecTypeMap, recAliasMap_wf.find?_insert] at h + simp only [recAliasMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] at h + by_cases hAliasRec : ``AliasRec = name <;> + by_cases hRecAlias : ``RecAlias = name <;> + simp +decide [hAliasRec, hRecAlias, SMap.find?, aliasRecInfo, + recAliasInfo] at h + +private def aliasRecNormalizationContext : TypeChecker.VContext := + TypeChecker.VContext.mk' aliasRecNormalizationVEnvs_wf + (fuel := { whnf := 2 }) + +private def aliasRecNormalizationRawContext : TypeChecker.Context where + env := aliasRecNormalizationKernelEnv + fuel := { whnf := 2 } + +private def aliasFormerCandidateContext : AddInductive.Context where + env := aliasFormerNormalizationKernelEnv + lparams := [] + safety := .safe + allowPrimitive := false + fuel := { whnf := 2 } + +private def aliasFormerCtorCandidateContext : AddInductive.Context where + env := aliasFormerCtorNormalizationKernelEnv + lparams := [] + safety := .safe + allowPrimitive := false + fuel := { whnf := 2 } + +/-- Exact kernel request indexed by the singleton normalization candidate. -/ +private def aliasFormerKernelCtor : Constructor where + name := aliasFormerMkInfo.name + type := aliasFormerMkInfo.type + +private def aliasFormerKernelType : InductiveType where + name := aliasFormerInfo.name + type := aliasFormerInfo.type + ctors := [aliasFormerKernelCtor] + +private theorem aliasFormerNormalization_lookup : + aliasFormerNormalizationKernelEnv.find? ``TypeFamilyAlias = + some typeFamilyAliasInfo := by + change typeFamilyAliasMap.find?' ``TypeFamilyAlias = + some typeFamilyAliasInfo + rw [typeFamilyAliasMap_wf.find?'_eq_find?, typeFamilyAliasMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + rfl + +private theorem aliasFormerCtorNormalization_lookup : + aliasFormerCtorNormalizationKernelEnv.find? ``AliasFormer = + some aliasFormerInfo := by + change aliasFormerTypeMap.find?' ``AliasFormer = some aliasFormerInfo + rw [aliasFormerTypeMap_wf.find?'_eq_find?, aliasFormerTypeMap, + typeFamilyAliasMap_wf.find?_insert] + rfl + +private theorem aliasRecNormalization_lookup : + aliasRecNormalizationKernelEnv.find? ``RecAlias = + some recAliasInfo := by + change aliasRecTypeMap.find?' ``RecAlias = some recAliasInfo + rw [aliasRecTypeMap_wf.find?'_eq_find?, aliasRecTypeMap, + recAliasMap_wf.find?_insert, recAliasMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + rfl + +private theorem aliasRecNormalization_family_lookup : + aliasRecNormalizationKernelEnv.find? ``AliasRec = + some aliasRecInfo := by + change aliasRecTypeMap.find?' ``AliasRec = some aliasRecInfo + rw [aliasRecTypeMap_wf.find?'_eq_find?, aliasRecTypeMap, + recAliasMap_wf.find?_insert] + rfl + +private def recAliasWhnfKernelExpr : Expr := + recAliasInfo.instantiateValueLevelParams! [.succ .zero] + +private def aliasRecFieldKernelExpr : Expr := + aliasRecMkInfo.type.bindingDomain! + +private theorem aliasRecFieldKernelExpr_eq : + aliasRecFieldKernelExpr = + .app (.const ``RecAlias [.succ .zero]) + (.const ``AliasRec []) := rfl + +private theorem recAliasWhnfKernelExpr_eq : + recAliasWhnfKernelExpr = + .lam `α (.sort (.succ .zero)) (.bvar 0) .default := by + simp [recAliasWhnfKernelExpr, recAliasInfo, + recAliasKernelDef, ConstantInfo.instantiateValueLevelParams!, + ConstantInfo.levelParams, ConstantInfo.value!, + ConstantInfo.toConstantVal, + Expr.instantiateLevelParams_eq, Expr.instantiateLevelParamsCore', + Level.substParams'] + +private def recAliasUnfoldState (state : TypeChecker.State) : + TypeChecker.State := + { state with + unfold := state.unfold.insert + (.const ``RecAlias [.succ .zero]) + recAliasWhnfKernelExpr } + +/- The following small operational equations expose only the transformer +plumbing needed to kernel-reduce the two concrete WHNF traces. -/ + +@[simp] private theorem normalizationRecMGet (methods context state) : + (get : TypeChecker.RecM TypeChecker.State) methods context state = + .ok (state, state) := rfl + +@[simp] private theorem normalizationRecMReadContext + (methods context state) : + (readThe TypeChecker.Context : TypeChecker.RecM TypeChecker.Context) + methods context state = + .ok (context, state) := rfl + +@[simp] private theorem normalizationRecMModify + (f : TypeChecker.State → TypeChecker.State) + (methods context state) : + (modify f : TypeChecker.RecM PUnit) methods context state = + .ok (.unit, f state) := rfl + +@[simp] private theorem normalizationRecMPure + {α} (a : α) (methods context state) : + (pure a : TypeChecker.RecM α) methods context state = + .ok (a, state) := rfl + +@[simp] private theorem normalizationRecMBind + {α β} (x : TypeChecker.RecM α) + (f : α → TypeChecker.RecM β) (methods context state) : + (x >>= f) methods context state = + match x methods context state with + | .error e => .error e + | .ok (a, state') => f a methods context state' := by + simp [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + cases h : x methods context state with + | error => rfl + | ok value => cases value; rfl + +@[simp] private theorem normalizationRecMGetEnv + (methods context state) : + (liftM TypeChecker.getEnv : + TypeChecker.RecM Kernel.Environment) methods context state = + .ok (context.env, state) := rfl + +@[simp] private theorem normalizationRecMLiftExceptOk + {α} (a : α) (methods context state) : + (liftM (.ok a : Except Kernel.Exception α) : + TypeChecker.RecM α) methods context state = + .ok (a, state) := rfl + +private theorem normalizationExceptPure + {α} (a : α) : + (pure a : Except Kernel.Exception α) = .ok a := rfl + +@[simp] private theorem typeFamilyAlias_noLooseBVars : + (Expr.const ``TypeFamilyAlias []).hasLooseBVars = false := by + simp [Expr.hasLooseBVars, Expr.looseBVarRange'] + +@[simp] private theorem aliasFormer_noLooseBVars : + (Expr.const ``AliasFormer []).hasLooseBVars = false := by + simp [Expr.hasLooseBVars, Expr.looseBVarRange'] + +@[simp] private theorem recAlias_noLooseBVars : + (Expr.const ``RecAlias [.succ .zero]).hasLooseBVars = false := by + simp [Expr.hasLooseBVars, Expr.looseBVarRange'] + +@[simp] private theorem aliasRec_noLooseBVars : + (Expr.const ``AliasRec []).hasLooseBVars = false := by + simp [Expr.hasLooseBVars, Expr.looseBVarRange'] + +@[simp] private theorem aliasRecField_noLooseBVars : + (Expr.app + (.const ``RecAlias [.succ .zero]) + (.const ``AliasRec [])).hasLooseBVars = false := by + simp [Expr.hasLooseBVars, Expr.looseBVarRange'] + +@[simp] private theorem emptyCheckTypeCache_typeFamilyAlias : + (({} : TypeChecker.State).inferTypeC)[ + Expr.const ``TypeFamilyAlias []]? = none := by + exact Std.HashMap.getElem?_empty + +@[simp] private theorem emptyCheckTypeCache_aliasFormer : + (({} : TypeChecker.State).inferTypeC)[ + Expr.const ``AliasFormer []]? = none := by + exact Std.HashMap.getElem?_empty + +@[simp] private theorem emptyCheckTypeCache_recAlias : + (({} : TypeChecker.State).inferTypeC)[ + Expr.const ``RecAlias [.succ .zero]]? = none := by + exact Std.HashMap.getElem?_empty + +@[simp] private theorem emptyCheckTypeCache_aliasRecField : + (({} : TypeChecker.State).inferTypeC)[ + Expr.app + (.const ``RecAlias [.succ .zero]) + (.const ``AliasRec [])]? = none := by + exact Std.HashMap.getElem?_empty + +@[simp] private theorem aliasFormerNormalization_get : + aliasFormerNormalizationRawContext.env.get ``TypeFamilyAlias = + .ok typeFamilyAliasInfo := by + unfold Kernel.Environment.get + change + (match aliasFormerNormalizationKernelEnv.find? ``TypeFamilyAlias with + | some ci => pure ci + | none => throw <| Kernel.Exception.unknownConstant + aliasFormerNormalizationKernelEnv ``TypeFamilyAlias) = + Except.ok typeFamilyAliasInfo + rw [aliasFormerNormalization_lookup] + rfl + +@[simp] private theorem inferConstantTypeFamilyAlias : + TypeChecker.Inner.inferConstant aliasFormerNormalizationRawContext + ``TypeFamilyAlias [] false = + .ok (.sort (.succ (.succ .zero))) := by + unfold TypeChecker.Inner.inferConstant + rw [aliasFormerNormalization_get] + rfl + +@[simp] private theorem aliasFormerCtorNormalization_get : + aliasFormerCtorNormalizationRawContext.env.get ``AliasFormer = + .ok aliasFormerInfo := by + unfold Kernel.Environment.get + change + (match aliasFormerCtorNormalizationKernelEnv.find? ``AliasFormer with + | some ci => pure ci + | none => throw <| Kernel.Exception.unknownConstant + aliasFormerCtorNormalizationKernelEnv ``AliasFormer) = + Except.ok aliasFormerInfo + rw [aliasFormerCtorNormalization_lookup] + rfl + +@[simp] private theorem inferConstantAliasFormer : + TypeChecker.Inner.inferConstant aliasFormerCtorNormalizationRawContext + ``AliasFormer [] false = + .ok (.const ``TypeFamilyAlias []) := by + unfold TypeChecker.Inner.inferConstant + rw [aliasFormerCtorNormalization_get] + rfl + +private theorem checkTypeTypeFamilyAlias : + TypeChecker.Inner.inferType (.const ``TypeFamilyAlias []) false + (TypeChecker.Methods.withFuel 9999) + aliasFormerNormalizationRawContext ({} : TypeChecker.State) = + .ok (.sort (.succ (.succ .zero)), + aliasFormerCheckTypeState {}) := by + change TypeChecker.Inner.inferType' + (.const ``TypeFamilyAlias []) false + (TypeChecker.Methods.withFuel 9998) + aliasFormerNormalizationRawContext ({} : TypeChecker.State) = + .ok (.sort (.succ (.succ .zero)), + aliasFormerCheckTypeState {}) + unfold TypeChecker.Inner.inferType' + simp [aliasFormerCheckTypeState, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +private theorem checkTypeTypeFamilyAliasCandidate : + TypeChecker.Inner.inferType (.const ``TypeFamilyAlias []) false + (TypeChecker.Methods.withFuel 10000) + aliasFormerNormalizationRawContext ({} : TypeChecker.State) = + .ok (.sort (.succ (.succ .zero)), + aliasFormerCheckTypeState {}) := by + change TypeChecker.Inner.inferType' + (.const ``TypeFamilyAlias []) false + (TypeChecker.Methods.withFuel 9999) + aliasFormerNormalizationRawContext ({} : TypeChecker.State) = + .ok (.sort (.succ (.succ .zero)), + aliasFormerCheckTypeState {}) + unfold TypeChecker.Inner.inferType' + simp [aliasFormerCheckTypeState, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +private theorem checkTypeAliasFormer : + TypeChecker.Inner.inferType (.const ``AliasFormer []) false + (TypeChecker.Methods.withFuel 9999) + aliasFormerCtorNormalizationRawContext ({} : TypeChecker.State) = + .ok (.const ``TypeFamilyAlias [], + aliasFormerCtorCheckTypeState {}) := by + change TypeChecker.Inner.inferType' + (.const ``AliasFormer []) false + (TypeChecker.Methods.withFuel 9998) + aliasFormerCtorNormalizationRawContext ({} : TypeChecker.State) = + .ok (.const ``TypeFamilyAlias [], + aliasFormerCtorCheckTypeState {}) + unfold TypeChecker.Inner.inferType' + simp [aliasFormerCtorCheckTypeState, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +private theorem checkTypeAliasFormerCandidate : + TypeChecker.Inner.inferType (.const ``AliasFormer []) false + (TypeChecker.Methods.withFuel 10000) + aliasFormerCtorNormalizationRawContext ({} : TypeChecker.State) = + .ok (.const ``TypeFamilyAlias [], + aliasFormerCtorCheckTypeState {}) := by + change TypeChecker.Inner.inferType' + (.const ``AliasFormer []) false + (TypeChecker.Methods.withFuel 9999) + aliasFormerCtorNormalizationRawContext ({} : TypeChecker.State) = + .ok (.const ``TypeFamilyAlias [], + aliasFormerCtorCheckTypeState {}) + unfold TypeChecker.Inner.inferType' + simp [aliasFormerCtorCheckTypeState, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +@[simp] private theorem aliasRecNormalization_getRecAlias : + aliasRecNormalizationRawContext.env.get ``RecAlias = + .ok recAliasInfo := by + unfold Kernel.Environment.get + change + (match aliasRecNormalizationKernelEnv.find? ``RecAlias with + | some ci => pure ci + | none => throw <| Kernel.Exception.unknownConstant + aliasRecNormalizationKernelEnv ``RecAlias) = + Except.ok recAliasInfo + rw [aliasRecNormalization_lookup] + rfl + +@[simp] private theorem aliasRecNormalization_getFamily : + aliasRecNormalizationRawContext.env.get ``AliasRec = + .ok aliasRecInfo := by + unfold Kernel.Environment.get + change + (match aliasRecNormalizationKernelEnv.find? ``AliasRec with + | some ci => pure ci + | none => throw <| Kernel.Exception.unknownConstant + aliasRecNormalizationKernelEnv ``AliasRec) = + Except.ok aliasRecInfo + rw [aliasRecNormalization_family_lookup] + rfl + +@[simp] private theorem aliasRecNormalization_checkLevelSuccZero : + TypeChecker.Inner.checkLevel aliasRecNormalizationRawContext + (.succ .zero) = .ok () := by + simp [TypeChecker.Inner.checkLevel, Level.getUndefParam, Level.forEach, + Level.hasParam_eq, + Level.hasParam', normalizationExceptPure] + +@[simp] private theorem recAliasInfo_isUnsafe : + recAliasInfo.isUnsafe = false := rfl + +@[simp] private theorem aliasRecNormalization_safety : + aliasRecNormalizationRawContext.safety = .safe := rfl + +@[simp] private theorem recAliasInfo_instantiateType : + recAliasInfo.instantiateTypeLevelParams [.succ .zero] = + .forallE `α (.sort (.succ .zero)) + (.sort (.succ .zero)) .default := by + simp [recAliasInfo, recAliasKernelDef, + ConstantInfo.instantiateTypeLevelParams, + ConstantVal.instantiateTypeLevelParams, + ConstantInfo.toConstantVal, + Expr.instantiateLevelParams_eq, Expr.instantiateLevelParamsCore', + Level.substParams'] + +@[simp] private theorem inferConstantRecAlias : + TypeChecker.Inner.inferConstant aliasRecNormalizationRawContext + ``RecAlias [.succ .zero] false = + .ok (.forallE `α (.sort (.succ .zero)) + (.sort (.succ .zero)) .default) := by + unfold TypeChecker.Inner.inferConstant + rw [aliasRecNormalization_getRecAlias] + simp [recAliasInfo, recAliasKernelDef, Bind.bind, Except.bind, normalizationExceptPure, + ConstantInfo.levelParams, ConstantInfo.isUnsafe, ConstantInfo.instantiateTypeLevelParams, + ConstantInfo.toConstantVal, ConstantVal.instantiateTypeLevelParams, + Expr.instantiateLevelParams_eq, Expr.instantiateLevelParamsCore', Level.substParams'] + +@[simp] private theorem inferConstantAliasRec : + TypeChecker.Inner.inferConstant aliasRecNormalizationRawContext + ``AliasRec [] false = + .ok (.sort (.succ .zero)) := by + unfold TypeChecker.Inner.inferConstant + rw [aliasRecNormalization_getFamily] + rfl + +@[simp] private theorem normalizationWhnfCoreConst + (methods context state n ls) : + (TypeChecker.Inner.whnfCore' (.const n ls) + (cheapProj := false)) + methods context state = + .ok (.const n ls, state) := rfl + +@[simp] private theorem normalizationWhnfCoreSort + (methods context state u) : + (TypeChecker.Inner.whnfCore' (.sort u) + (cheapProj := false)) + methods context state = + .ok (.sort u, state) := rfl + +@[simp] private theorem normalizationWhnfCoreLam + (methods context state name ty body bi) : + (TypeChecker.Inner.whnfCore' (.lam name ty body bi) + (cheapProj := false)) + methods context state = + .ok (.lam name ty body bi, state) := rfl + +@[simp] private theorem normalizationReduceNativeConst + (env methods context state n ls) : + (liftM (TypeChecker.Inner.reduceNative env (.const n ls)) : + TypeChecker.RecM (Option Expr)) methods context state = + .ok (none, state) := rfl + +@[simp] private theorem normalizationReduceNativeSort + (env methods context state u) : + (liftM (TypeChecker.Inner.reduceNative env (.sort u)) : + TypeChecker.RecM (Option Expr)) methods context state = + .ok (none, state) := rfl + +@[simp] private theorem normalizationReduceNativeLam + (env methods context state name ty body bi) : + (liftM (TypeChecker.Inner.reduceNative env (.lam name ty body bi)) : + TypeChecker.RecM (Option Expr)) methods context state = + .ok (none, state) := rfl + +@[simp] private theorem normalizationReduceNatConst + (methods context state n ls) : + TypeChecker.Inner.reduceNat (.const n ls) methods context state = + .ok (none, state) := rfl + +@[simp] private theorem normalizationReduceNatSort + (methods context state u) : + TypeChecker.Inner.reduceNat (.sort u) methods context state = + .ok (none, state) := rfl + +@[simp] private theorem normalizationReduceNatLam + (methods context state name ty body bi) : + TypeChecker.Inner.reduceNat (.lam name ty body bi) + methods context state = + .ok (none, state) := rfl + +@[simp] private theorem normalizationUnfoldSort + (methods context state u) : + TypeChecker.Inner.unfoldDefinition (.sort u) methods context state = + .ok (none, state) := rfl + +@[simp] private theorem normalizationUnfoldLam + (methods context state name ty body bi) : + TypeChecker.Inner.unfoldDefinition (.lam name ty body bi) + methods context state = + .ok (none, state) := rfl + +private theorem unfoldTypeFamilyAlias (methods state) : + TypeChecker.Inner.unfoldDefinition (.const ``TypeFamilyAlias []) + methods aliasFormerNormalizationRawContext state = + .ok (some (.sort (.succ .zero)), state) := by + change + TypeChecker.Inner.unfoldDefinitionCore (.const ``TypeFamilyAlias []) + methods aliasFormerNormalizationRawContext state = + .ok (some (.sort (.succ .zero)), state) + simp [TypeChecker.Inner.unfoldDefinitionCore, TypeChecker.Inner.isDelta, Expr.getAppFn, + aliasFormerNormalizationRawContext, aliasFormerNormalization_lookup, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind, typeFamilyAliasInfo, typeFamilyAliasKernelDef, + ConstantInfo.deltaValue?, TypeChecker.Inner.instantiateDeltaValue, ConstantInfo.numLevelParams, + ConstantInfo.levelParams, ConstantInfo.toConstantVal, Expr.instantiateLevelParams] + +private theorem unfoldAliasFormer (methods state) : + TypeChecker.Inner.unfoldDefinition (.const ``AliasFormer []) + methods aliasFormerCtorNormalizationRawContext state = + .ok (none, state) := by + change + TypeChecker.Inner.unfoldDefinitionCore (.const ``AliasFormer []) + methods aliasFormerCtorNormalizationRawContext state = + .ok (none, state) + simp [TypeChecker.Inner.unfoldDefinitionCore, TypeChecker.Inner.isDelta, + Expr.getAppFn, aliasFormerCtorNormalizationRawContext, + aliasFormerCtorNormalization_lookup, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind, aliasFormerInfo, + ConstantInfo.deltaValue?] + +private theorem unfoldRecAliasInitial (methods) : + TypeChecker.Inner.unfoldDefinition + (.const ``RecAlias [.succ .zero]) + methods aliasRecNormalizationRawContext ({} : TypeChecker.State) = + .ok (some recAliasWhnfKernelExpr, recAliasUnfoldState {}) := by + change + TypeChecker.Inner.unfoldDefinitionCore + (.const ``RecAlias [.succ .zero]) + methods aliasRecNormalizationRawContext ({} : TypeChecker.State) = + .ok (some recAliasWhnfKernelExpr, recAliasUnfoldState {}) + simp [TypeChecker.Inner.unfoldDefinitionCore, TypeChecker.Inner.isDelta, + Expr.getAppFn, aliasRecNormalizationRawContext, + aliasRecNormalization_lookup, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind, recAliasInfo, recAliasKernelDef, + ConstantInfo.deltaValue?, TypeChecker.Inner.instantiateDeltaValue, + ConstantInfo.numLevelParams, + ConstantInfo.instantiateValueLevelParams!, ConstantInfo.levelParams, + ConstantInfo.value!, ConstantInfo.toConstantVal, + Expr.instantiateLevelParams, recAliasWhnfKernelExpr, + recAliasUnfoldState] + +private theorem unfoldRecAliasCoreInitial (methods) : + TypeChecker.Inner.unfoldDefinitionCore + (.const ``RecAlias [.succ .zero]) + methods aliasRecNormalizationRawContext ({} : TypeChecker.State) = + .ok (some recAliasWhnfKernelExpr, recAliasUnfoldState {}) := by + change + TypeChecker.Inner.unfoldDefinition + (.const ``RecAlias [.succ .zero]) + methods aliasRecNormalizationRawContext ({} : TypeChecker.State) = + .ok (some recAliasWhnfKernelExpr, recAliasUnfoldState {}) + exact unfoldRecAliasInitial methods + +private theorem unfoldAliasRecFieldInitial (methods) : + TypeChecker.Inner.unfoldDefinition aliasRecFieldKernelExpr + methods aliasRecNormalizationRawContext ({} : TypeChecker.State) = + .ok (some (.app recAliasWhnfKernelExpr + (.const ``AliasRec [])), recAliasUnfoldState {}) := by + rw [aliasRecFieldKernelExpr_eq] + unfold TypeChecker.Inner.unfoldDefinition + rw [if_pos (show (Expr.app (.const ``RecAlias [.succ .zero]) + (.const ``AliasRec [])).isApp = true from rfl)] + rw [show + (Expr.app (.const ``RecAlias [.succ .zero]) + (.const ``AliasRec [])).getAppFn = + .const ``RecAlias [.succ .zero] by rfl] + simp only [normalizationRecMBind] + rw [unfoldRecAliasCoreInitial] + rw [show + (Expr.app (.const ``RecAlias [.succ .zero]) + (.const ``AliasRec [])).getAppRevArgs = + #[.const ``AliasRec []] by rfl] + simp only [normalizationRecMPure] + rw [Expr.mkAppRevRange_eq + (l₁ := []) (l₂ := [.const ``AliasRec []]) (l₃ := []) + (by simp) (by rfl) (by rfl)] + rfl + +private theorem whnfLoopTypeFamilyAlias (methods state) : + TypeChecker.Inner.whnf'.loop (.const ``TypeFamilyAlias []) 2 + methods aliasFormerNormalizationRawContext state = + .ok (.sort (.succ .zero), state) := by + unfold TypeChecker.Inner.whnf'.loop + simp [unfoldTypeFamilyAlias] + unfold TypeChecker.Inner.whnf'.loop + simp + +private theorem whnfLoopAliasFormer (methods state) : + TypeChecker.Inner.whnf'.loop (.const ``AliasFormer []) 2 + methods aliasFormerCtorNormalizationRawContext state = + .ok (.const ``AliasFormer [], state) := by + unfold TypeChecker.Inner.whnf'.loop + simp [unfoldAliasFormer] + +private theorem whnfLoopRecAlias (methods) : + TypeChecker.Inner.whnf'.loop + (.const ``RecAlias [.succ .zero]) 2 + methods aliasRecNormalizationRawContext ({} : TypeChecker.State) = + .ok (recAliasWhnfKernelExpr, recAliasUnfoldState {}) := by + unfold TypeChecker.Inner.whnf'.loop + simp [unfoldRecAliasInitial] + unfold TypeChecker.Inner.whnf'.loop + rw [recAliasWhnfKernelExpr_eq] + simp + +/-- The actual verified WHNF computation for the raw AliasFormer family +result. -/ +theorem aliasFormerFamily_whnf : + ∃ state : TypeChecker.State, + TypeChecker.Inner.whnf' (.const ``TypeFamilyAlias []) + (TypeChecker.Methods.withFuel 9999) + aliasFormerNormalizationRawContext ({} : TypeChecker.State) = + .ok (.sort (.succ .zero), state) := by + unfold TypeChecker.Inner.whnf' + simp + rw [show (if aliasFormerNormalizationRawContext.eagerReduce then + aliasFormerNormalizationRawContext.fuel.whnfEager + else aliasFormerNormalizationRawContext.fuel.whnf) = 2 by rfl] + rw [whnfLoopTypeFamilyAlias] + simp [Functor.map, StateT.map, Except.map] + +/-- Constructor normalization is staged after inserting the raw family. The +family constant is opaque, so this exact checker run retains the constructor +type unchanged. -/ +theorem aliasFormerCtor_whnf : + ∃ state : TypeChecker.State, + TypeChecker.Inner.whnf' (.const ``AliasFormer []) + (TypeChecker.Methods.withFuel 9999) + aliasFormerCtorNormalizationRawContext + ({} : TypeChecker.State) = + .ok (.const ``AliasFormer [], state) := by + unfold TypeChecker.Inner.whnf' + simp + rw [show (if aliasFormerCtorNormalizationRawContext.eagerReduce then + aliasFormerCtorNormalizationRawContext.fuel.whnfEager + else aliasFormerCtorNormalizationRawContext.fuel.whnf) = 2 by rfl] + rw [whnfLoopAliasFormer] + simp [Functor.map, StateT.map, Except.map] + +/-- The full non-inference-only checker run for the raw AliasFormer family +type. The returned sort is recorded together with the checker's cache update, +rather than supplied as an external Theory premise. -/ +theorem aliasFormerFamily_checkType : + ∃ state : TypeChecker.State, + TypeChecker.Inner.inferType aliasFormerInfo.type false + (TypeChecker.Methods.withFuel 9999) + aliasFormerNormalizationContext.toContext + ({} : TypeChecker.State) = + .ok (.sort (.succ (.succ .zero)), state) := by + exact ⟨aliasFormerCheckTypeState {}, by + simpa [aliasFormerInfo, ConstantInfo.type, + ConstantInfo.toConstantVal, + aliasFormerNormalizationContext, + TypeChecker.VContext.mk', TypeChecker.VContext.mk1, + TypeChecker.MLCtx.lctx, + aliasFormerNormalizationRawContext] using + checkTypeTypeFamilyAlias⟩ + +/-- The exact full checker run for the actual AliasFormer constructor type, +staged after insertion of the raw family. The checker returns the retained +family-type alias rather than silently normalizing it. -/ +theorem aliasFormerCtor_checkType : + ∃ state : TypeChecker.State, + TypeChecker.Inner.inferType aliasFormerMkInfo.type false + (TypeChecker.Methods.withFuel 9999) + aliasFormerCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (.const ``TypeFamilyAlias [], state) := by + exact ⟨aliasFormerCtorCheckTypeState {}, by + simpa [aliasFormerMkInfo, ConstantInfo.type, + ConstantInfo.toConstantVal, + aliasFormerCtorCandidateContext, + AddInductive.Context.toTypeChecker, + aliasFormerCtorNormalizationRawContext] using + checkTypeAliasFormer⟩ + +/-- The actual verified WHNF computation for the reducible `RecAlias` +head at the universe used by `AliasRec.mk`. -/ +theorem recAlias_whnf : + ∃ state : TypeChecker.State, + TypeChecker.Inner.whnf' + (.const ``RecAlias [.succ .zero]) + (TypeChecker.Methods.withFuel 9999) + aliasRecNormalizationRawContext ({} : TypeChecker.State) = + .ok (recAliasWhnfKernelExpr, state) := by + unfold TypeChecker.Inner.whnf' + simp + rw [show (if aliasRecNormalizationRawContext.eagerReduce then + aliasRecNormalizationRawContext.fuel.whnfEager + else aliasRecNormalizationRawContext.fuel.whnf) = 2 by rfl] + rw [whnfLoopRecAlias] + simp [Functor.map, StateT.map, Except.map] + +private theorem aliasFormerFamily_whnfM : + TypeChecker.M.run aliasFormerNormalizationKernelEnv .safe {} [] + { whnf := 2 } (TypeChecker.whnf aliasFormerInfo.type) = + .ok (.sort (.succ .zero)) := by + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.whnf' + (.const ``TypeFamilyAlias []) + (TypeChecker.Methods.withFuel 9999) + aliasFormerNormalizationRawContext ({} : TypeChecker.State)) = + Except.ok (.sort (.succ .zero)) + obtain ⟨state, hrun⟩ := aliasFormerFamily_whnf + rw [hrun] + rfl + +private theorem aliasFormerFamily_checkTypeM : + TypeChecker.M.run aliasFormerNormalizationKernelEnv .safe {} [] + { whnf := 2 } (TypeChecker.checkType aliasFormerInfo.type) = + .ok (.sort (.succ (.succ .zero))) := by + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType aliasFormerInfo.type false + (TypeChecker.Methods.withFuel 10000) + aliasFormerNormalizationRawContext ({} : TypeChecker.State)) = + Except.ok (.sort (.succ (.succ .zero))) + rw [show aliasFormerInfo.type = + .const ``TypeFamilyAlias [] by rfl] + rw [checkTypeTypeFamilyAliasCandidate] + rfl + +private theorem aliasFormerCtor_checkTypeM : + TypeChecker.M.run aliasFormerCtorNormalizationKernelEnv .safe {} [] + { whnf := 2 } (TypeChecker.checkType aliasFormerMkInfo.type) = + .ok (.const ``TypeFamilyAlias []) := by + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType aliasFormerMkInfo.type false + (TypeChecker.Methods.withFuel 10000) + aliasFormerCtorNormalizationRawContext ({} : TypeChecker.State)) = + Except.ok (.const ``TypeFamilyAlias []) + rw [show aliasFormerMkInfo.type = + .const ``AliasFormer [] by rfl] + rw [checkTypeAliasFormerCandidate] + rfl + +private theorem aliasFormerCtor_whnfM : + TypeChecker.M.run aliasFormerCtorNormalizationKernelEnv .safe {} [] + { whnf := 2 } (TypeChecker.whnf aliasFormerMkInfo.type) = + .ok (.const ``AliasFormer []) := by + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.whnf' + (.const ``AliasFormer []) + (TypeChecker.Methods.withFuel 9999) + aliasFormerCtorNormalizationRawContext ({} : TypeChecker.State)) = + Except.ok (.const ``AliasFormer []) + obtain ⟨state, hrun⟩ := aliasFormerCtor_whnf + rw [hrun] + rfl + +private def annotatedPiRawDomainKernel : Expr := + .app (.const ``outParam [.succ .zero]) (.sort .zero) + +private def annotatedPiInnerKernel : Expr := + .forallE `p annotatedPiRawDomainKernel + (.const ``AnnotatedPi []) .default + +@[simp] private theorem annotatedPiRawDomain_getAppFn : + annotatedPiRawDomainKernel.getAppFn = + .const ``outParam [.succ .zero] := rfl + +@[simp] private theorem annotatedPiRawDomain_getAppRevArgs : + annotatedPiRawDomainKernel.getAppRevArgs = #[.sort .zero] := rfl + +private def annotatedPiOutParamWhnfKernelExpr : Expr := + annotationOutParamInfo.instantiateValueLevelParams! [.succ .zero] + +private theorem annotatedPiOutParamWhnfKernelExpr_eq : + annotatedPiOutParamWhnfKernelExpr = + .lam `α (.sort (.succ .zero)) (.bvar 0) .default := by + simp [annotatedPiOutParamWhnfKernelExpr, annotationOutParamInfo, + ConstantInfo.instantiateValueLevelParams!, ConstantInfo.levelParams, ConstantInfo.value!, + ConstantInfo.toConstantVal, Expr.instantiateLevelParams_eq, Expr.instantiateLevelParamsCore', + Level.substParams'] + +private def annotatedPiOutParamUnfoldState (state : TypeChecker.State) : + TypeChecker.State := + { state with + unfold := state.unfold.insert + (.const ``outParam [.succ .zero]) + annotatedPiOutParamWhnfKernelExpr } + +private def annotatedPiDomainBetaKernel : Expr := + .app annotatedPiOutParamWhnfKernelExpr (.sort .zero) + +private def annotatedPiDomainBetaState (state : TypeChecker.State) : + TypeChecker.State := + { state with + whnfCoreCache := state.whnfCoreCache.insert + annotatedPiDomainBetaKernel (.sort .zero) } + +@[simp] private theorem annotatedPiType_quotInit : + annotatedPiTypeKernelEnv.quotInit = false := rfl + +private theorem annotatedPiInductiveReduceRecDomain + {m : Type → Type} [Monad m] + (whnf inferType : Expr → m Expr) + (isDefEq : Expr → Expr → m Bool) (isNeverProp : Expr → m Bool) : + inductiveReduceRec annotatedPiTypeKernelEnv annotatedPiRawDomainKernel + whnf inferType isDefEq isNeverProp = + pure none := by + unfold inductiveReduceRec + rw [show annotatedPiRawDomainKernel.getAppFn = + .const ``outParam [.succ .zero] by rfl] + simp only + rw [annotatedPiType_lookup_outParam] + rfl + +private theorem annotatedPiReduceRecursorDomain + (methods state) : + TypeChecker.Inner.reduceRecursor annotatedPiRawDomainKernel + methods annotatedPiCtorCandidateContext.toTypeChecker state = + .ok (none, state) := by + unfold TypeChecker.Inner.reduceRecursor + simp only [normalizationRecMBind, normalizationRecMGetEnv] + rw [if_neg (show + ¬(annotatedPiCtorCandidateContext.toTypeChecker.env.quotInit = true) by + simp [annotatedPiCtorCandidateContext, + AddInductive.Context.toTypeChecker, annotatedPiType_quotInit])] + simp only [annotatedPiCtorCandidateContext, + AddInductive.Context.toTypeChecker] + rw [annotatedPiInductiveReduceRecDomain] + rfl + +@[simp] private theorem annotatedPiWhnfCoreOutParamConst (n state) : + TypeChecker.Inner.whnfCore + (.const ``outParam [.succ .zero]) false + (TypeChecker.Methods.withFuel (n + 1)) + annotatedPiCtorCandidateContext.toTypeChecker state = + .ok (.const ``outParam [.succ .zero], state) := by + rfl + +private theorem annotatedPiWhnfCoreDomainInitial (n) : + TypeChecker.Inner.whnfCore' annotatedPiRawDomainKernel + (cheapProj := false) + (TypeChecker.Methods.withFuel (n + 1)) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (annotatedPiRawDomainKernel, ({} : TypeChecker.State)) := by + change + TypeChecker.Inner.whnfCore' + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) + (cheapProj := false) + (TypeChecker.Methods.withFuel (n + 1)) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (.app (.const ``outParam [.succ .zero]) (.sort .zero), + ({} : TypeChecker.State)) + unfold TypeChecker.Inner.whnfCore' + simp only [normalizationRecMBind, normalizationRecMGet, Std.HashMap.getElem?_empty] + rw [Expr.withRevApp_eq] + simp only [normalizationRecMBind] + rw [show + (Expr.app (.const ``outParam [.succ .zero]) + (.sort .zero)).getAppFn = + .const ``outParam [.succ .zero] by rfl] + rw [annotatedPiWhnfCoreOutParamConst n ({} : TypeChecker.State)] + simp [Expr.structuralEq, Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [show + .app (.const ``outParam [.succ .zero]) (.sort .zero) = + annotatedPiRawDomainKernel by rfl] + rw [annotatedPiReduceRecursorDomain] + rfl + +private theorem annotatedPiUnfoldOutParamCoreInitial (methods) : + TypeChecker.Inner.unfoldDefinitionCore + (.const ``outParam [.succ .zero]) + methods annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (some annotatedPiOutParamWhnfKernelExpr, + annotatedPiOutParamUnfoldState {}) := by + simp [TypeChecker.Inner.unfoldDefinitionCore, TypeChecker.Inner.isDelta, Expr.getAppFn, + annotatedPiCtorCandidateContext, AddInductive.Context.toTypeChecker, + annotatedPiType_lookup_outParam, Bind.bind, ReaderT.bind, StateT.bind, Except.bind, + annotationOutParamInfo, ConstantInfo.deltaValue?, TypeChecker.Inner.instantiateDeltaValue, + ConstantInfo.numLevelParams, ConstantInfo.instantiateValueLevelParams!, + ConstantInfo.levelParams, ConstantInfo.value!, ConstantInfo.toConstantVal, + Expr.instantiateLevelParams, annotatedPiOutParamWhnfKernelExpr, annotatedPiOutParamUnfoldState] + +private theorem annotatedPiUnfoldDomainInitial (methods) : + TypeChecker.Inner.unfoldDefinition annotatedPiRawDomainKernel + methods annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (some annotatedPiDomainBetaKernel, + annotatedPiOutParamUnfoldState {}) := by + change + TypeChecker.Inner.unfoldDefinition + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) + methods annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (some annotatedPiDomainBetaKernel, + annotatedPiOutParamUnfoldState {}) + unfold TypeChecker.Inner.unfoldDefinition + rw [if_pos (show (Expr.app (.const ``outParam [.succ .zero]) + (.sort .zero)).isApp = true from rfl)] + rw [show + (Expr.app (.const ``outParam [.succ .zero]) + (.sort .zero)).getAppFn = + .const ``outParam [.succ .zero] by rfl] + simp only [normalizationRecMBind] + rw [annotatedPiUnfoldOutParamCoreInitial] + rw [show + (Expr.app (.const ``outParam [.succ .zero]) + (.sort .zero)).getAppRevArgs = #[.sort .zero] by rfl] + simp only [normalizationRecMPure] + rw [Expr.mkAppRevRange_eq + (l₁ := []) (l₂ := [.sort .zero]) (l₃ := []) + (by simp) (by rfl) (by rfl)] + rfl + +@[simp] private theorem annotatedPiWhnfCoreOutParamIdentity (n state) : + TypeChecker.Inner.whnfCore + (.lam `α (.sort (.succ .zero)) (.bvar 0) .default) + false (TypeChecker.Methods.withFuel (n + 1)) + annotatedPiCtorCandidateContext.toTypeChecker state = + .ok (.lam `α (.sort (.succ .zero)) (.bvar 0) .default, + state) := by + rfl + +@[simp] private theorem annotatedPiWhnfCoreDomainSort (n state) : + TypeChecker.Inner.whnfCore (.sort .zero) false + (TypeChecker.Methods.withFuel (n + 1)) + annotatedPiCtorCandidateContext.toTypeChecker state = + .ok (.sort .zero, state) := by + rfl + +@[simp] private theorem annotatedPiSort_mkAppRevRangeZero : + (Expr.sort .zero).mkAppRevRange 0 0 #[.sort .zero] = + .sort .zero := by + rw [Expr.mkAppRevRange_eq + (l₁ := []) (l₂ := []) (l₃ := [.sort .zero]) + (by simp) (by rfl) (by rfl)] + rfl + +private theorem annotatedPiWhnfCoreDomainBeta (n) : + TypeChecker.Inner.whnfCore' annotatedPiDomainBetaKernel + (cheapProj := false) + (TypeChecker.Methods.withFuel (n + 1)) + annotatedPiCtorCandidateContext.toTypeChecker + (annotatedPiOutParamUnfoldState {}) = + .ok (.sort .zero, + annotatedPiDomainBetaState + (annotatedPiOutParamUnfoldState {})) := by + rw [annotatedPiDomainBetaKernel, + annotatedPiOutParamWhnfKernelExpr_eq] + unfold TypeChecker.Inner.whnfCore' + simp only [normalizationRecMBind, normalizationRecMGet, annotatedPiOutParamUnfoldState, + Std.HashMap.getElem?_empty] + rw [Expr.withRevApp_eq] + simp only [normalizationRecMBind] + rw [show + (Expr.app + (.lam `α (.sort (.succ .zero)) (.bvar 0) .default) + (.sort .zero)).getAppFn = + .lam `α (.sort (.succ .zero)) (.bvar 0) .default by rfl] + rw [annotatedPiWhnfCoreOutParamIdentity] + rw [show + (Expr.app + (.lam `α (.sort (.succ .zero)) (.bvar 0) .default) + (.sort .zero)).getAppRevArgs = #[.sort .zero] by rfl] + simp [TypeChecker.Inner.whnfCore'.loop, TypeChecker.Inner.whnfCore'.loop.cont, + TypeChecker.Inner.whnfCore'.save, annotatedPiDomainBetaKernel, + annotatedPiOutParamWhnfKernelExpr_eq, annotatedPiDomainBetaState, Expr.instantiate1', Bind.bind, + ReaderT.bind, StateT.bind, Except.bind] + +@[simp] private theorem annotatedPiReduceNativeDomain + (env methods state) : + (liftM (TypeChecker.Inner.reduceNative env annotatedPiRawDomainKernel) : + TypeChecker.RecM (Option Expr)) + methods annotatedPiCtorCandidateContext.toTypeChecker state = + .ok (none, state) := by + rfl + +@[simp] private theorem annotatedPiReduceNatDomain (methods state) : + TypeChecker.Inner.reduceNat annotatedPiRawDomainKernel + methods annotatedPiCtorCandidateContext.toTypeChecker state = + .ok (none, state) := by + change + TypeChecker.Inner.reduceNat + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) + methods annotatedPiCtorCandidateContext.toTypeChecker state = + .ok (none, state) + simp [TypeChecker.Inner.reduceNat, Expr.getAppNumArgs_eq, + Expr.getAppArgsRevList, Expr.appFn!, Expr.structuralEq] + +private theorem annotatedPiWhnfLoopDomain : + TypeChecker.Inner.whnf'.loop annotatedPiRawDomainKernel 100000 + (TypeChecker.Methods.withFuel 9999) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (.sort .zero, + annotatedPiDomainBetaState + (annotatedPiOutParamUnfoldState {})) := by + rw [show 100000 = 99999 + 1 by rfl] + unfold TypeChecker.Inner.whnf'.loop + rw [show 9999 = 9998 + 1 by rfl] + simp only [normalizationRecMBind, normalizationRecMGetEnv] + rw [annotatedPiWhnfCoreDomainInitial] + simp only [] + rw [annotatedPiReduceNativeDomain] + simp only [normalizationRecMBind] + rw [annotatedPiReduceNatDomain] + simp only [normalizationRecMBind] + rw [annotatedPiUnfoldDomainInitial] + simp only [] + unfold TypeChecker.Inner.whnf'.loop + simp only [normalizationRecMBind, normalizationRecMGetEnv] + rw [annotatedPiWhnfCoreDomainBeta] + simp only [] + rw [normalizationReduceNativeSort] + simp only [normalizationRecMBind] + rw [normalizationReduceNatSort] + simp only [normalizationRecMBind] + rw [normalizationUnfoldSort] + rfl + +private theorem annotatedPiWhnfLoopDomainConcrete : + TypeChecker.Inner.whnf'.loop + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) 100000 + (TypeChecker.Methods.withFuel 9999) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (.sort .zero, + annotatedPiDomainBetaState + (annotatedPiOutParamUnfoldState {})) := by + exact annotatedPiWhnfLoopDomain + +private theorem annotatedPiDomain_checkTypeM : + TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.checkType annotatedPiRawDomainKernel) = + .ok (.sort (.succ .zero)) := by + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType annotatedPiRawDomainKernel false + (TypeChecker.Methods.withFuel 10000) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = + .ok (.sort (.succ .zero)) + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' annotatedPiRawDomainKernel false + (TypeChecker.Methods.withFuel 9999) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = + .ok (.sort (.succ .zero)) + unfold annotatedPiRawDomainKernel TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', TypeChecker.Inner.inferType', Bind.bind, + ReaderT.bind, StateT.bind, Except.bind] + rw [annotatedPiIsDefEqSort 9999] + simp [Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rfl + +private theorem annotatedPiDomain_whnfM : + TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.whnf annotatedPiRawDomainKernel) = + .ok (.sort .zero) := by + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.whnf' annotatedPiRawDomainKernel + (TypeChecker.Methods.withFuel 9999) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = + .ok (.sort .zero) + rw [show annotatedPiRawDomainKernel = + .app (.const ``outParam [.succ .zero]) (.sort .zero) by rfl] + unfold TypeChecker.Inner.whnf' + simp + rw [show + (if annotatedPiCtorCandidateContext.toTypeChecker.eagerReduce then + annotatedPiCtorCandidateContext.toTypeChecker.fuel.whnfEager + else annotatedPiCtorCandidateContext.toTypeChecker.fuel.whnf) = + 100000 by rfl] + rw [annotatedPiWhnfLoopDomainConcrete] + simp [Functor.map, StateT.map, Except.map] + +@[simp] private theorem annotatedPiApp_beq_sort + (fn arg : Expr) (u : Level) : + ((.app fn arg : Expr) == .sort u) = false := by + change Expr.eqv (.app fn arg) (.sort u) = false + rw [Expr.eqv_eq] + rfl + +/-- `quickIsDefEq` only reads the success cache, so whichever answer the cache gives, the state +comes back unchanged and the result is `.true` or `.undef` -- never `.false`, since an application +and a sort are not settled structurally. -/ +private theorem annotatedPiQuickIsDefEqDomainInitial + (methods : TypeChecker.Methods) + (context : TypeChecker.Context) (initial : Std.HashSet (Expr × Expr)) : + ∃ (r : LBool) (m : Std.HashSet (Expr × Expr)), + TypeChecker.Inner.quickIsDefEq + annotatedPiRawDomainKernel (.sort .zero) + methods context ({ success := initial } : TypeChecker.State) = + .ok (r, ({ success := m } : TypeChecker.State)) ∧ + (r = .true ∨ r = .undef) := by + have hb : (annotatedPiRawDomainKernel == Expr.sort .zero) = false := + annotatedPiApp_beq_sort .. + by_cases h : TypeChecker.Inner.succeededBefore initial + annotatedPiRawDomainKernel (.sort .zero) = true + · refine ⟨.true, initial, ?_, Or.inl rfl⟩ + simp [TypeChecker.Inner.quickIsDefEq, hb, h, Bind.bind, ReaderT.bind, StateT.bind, Except.bind, + pure, ReaderT.pure, StateT.pure, Except.pure] + · simp only [Bool.not_eq_true] at h + refine ⟨.undef, initial, ?_, Or.inr rfl⟩ + simp [TypeChecker.Inner.quickIsDefEq, hb, h, Bind.bind, ReaderT.bind, StateT.bind, Except.bind, + pure] + rfl + +@[simp] private theorem annotatedPiWhnfCoreOutParamConstCheap + (fuel : Nat) (m : Std.HashSet (Expr × Expr)) : + TypeChecker.Inner.whnfCore + (.const ``outParam [.succ .zero]) true + (TypeChecker.Methods.withFuel (fuel + 2)) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := m } : TypeChecker.State) = + .ok (.const ``outParam [.succ .zero], + ({ success := m } : TypeChecker.State)) := by + rfl + +private theorem annotatedPiWhnfCoreDomainCheap + (fuel : Nat) (m : Std.HashSet (Expr × Expr)) : + TypeChecker.Inner.whnfCore annotatedPiRawDomainKernel true + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := m } : TypeChecker.State) = + .ok (annotatedPiRawDomainKernel, + ({ success := m } : TypeChecker.State)) := by + change + TypeChecker.Inner.whnfCore' + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) + true (TypeChecker.Methods.withFuel (fuel + 2)) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := m } : TypeChecker.State) = + .ok (.app (.const ``outParam [.succ .zero]) (.sort .zero), + ({ success := m } : TypeChecker.State)) + unfold TypeChecker.Inner.whnfCore' + simp only [normalizationRecMBind, normalizationRecMGet, Std.HashMap.getElem?_empty] + rw [Expr.withRevApp_eq] + simp only [normalizationRecMBind] + rw [show + (Expr.app (.const ``outParam [.succ .zero]) + (.sort .zero)).getAppFn = + .const ``outParam [.succ .zero] by rfl] + rw [annotatedPiWhnfCoreOutParamConstCheap fuel] + simp [Expr.structuralEq, Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [show + .app (.const ``outParam [.succ .zero]) (.sort .zero) = + annotatedPiRawDomainKernel by rfl] + rw [annotatedPiReduceRecursorDomain] + rfl + +@[simp] private theorem annotatedPiInferConstantOutParamCandidateOnly : + TypeChecker.Inner.inferConstant + annotatedPiCtorCandidateContext.toTypeChecker + ``outParam [.succ .zero] true = + .ok annotatedPiOutParamFnType := by + unfold TypeChecker.Inner.inferConstant + simp only [annotatedPiCtorCandidateContext, + AddInductive.Context.toTypeChecker] + rw [show annotatedPiTypeKernelEnv.get ``outParam = + .ok annotationOutParamInfo by exact annotatedPiType_get_outParam] + simp [annotationOutParamInfo, annotatedPiOutParamFnType, Bind.bind, Except.bind, + annotatedPiExceptPure, ConstantInfo.levelParams, ConstantInfo.instantiateTypeLevelParams, + ConstantInfo.toConstantVal, ConstantVal.instantiateTypeLevelParams, + Expr.instantiateLevelParams_eq, Expr.instantiateLevelParamsCore', Level.substParams'] + +private def annotatedPiOutParamInferOnlyState + (m : Std.HashSet (Expr × Expr)) : TypeChecker.State := + { inferTypeI := ({} : InferCache).insert + (.const ``outParam [.succ .zero]) annotatedPiOutParamFnType, + success := m } + +private theorem annotatedPiInferTypeOutParamOnly + (fuel : Nat) (m : Std.HashSet (Expr × Expr)) : + TypeChecker.Inner.inferType + (.const ``outParam [.succ .zero]) true + (TypeChecker.Methods.withFuel (fuel + 2)) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := m } : TypeChecker.State) = + .ok (annotatedPiOutParamFnType, + annotatedPiOutParamInferOnlyState m) := by + change + TypeChecker.Inner.inferType' + (.const ``outParam [.succ .zero]) true + (TypeChecker.Methods.withFuel (fuel + 1)) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := m } : TypeChecker.State) = + .ok (annotatedPiOutParamFnType, + annotatedPiOutParamInferOnlyState m) + unfold TypeChecker.Inner.inferType' + simp [annotatedPiOutParamInferOnlyState, + Expr.hasLooseBVars, Expr.looseBVarRange', + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +private theorem annotatedPiInferAppDomainOnly + (fuel : Nat) (m : Std.HashSet (Expr × Expr)) : + TypeChecker.Inner.inferApp annotatedPiRawDomainKernel + (TypeChecker.Methods.withFuel (fuel + 2)) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := m } : TypeChecker.State) = + .ok (.sort (.succ .zero), + annotatedPiOutParamInferOnlyState m) := by + unfold TypeChecker.Inner.inferApp + rw [Expr.withApp_eq] + rw [annotatedPiRawDomain_getAppFn] + rw [show annotatedPiRawDomainKernel.getAppArgs = + #[.sort .zero] by rfl] + simp only [normalizationRecMBind] + rw [annotatedPiInferTypeOutParamOnly fuel] + simp [TypeChecker.Inner.inferApp.loop, annotatedPiOutParamFnType] + +private def annotatedPiDomainInferOnlyState + (m : Std.HashSet (Expr × Expr)) : TypeChecker.State := + { inferTypeI := + (({} : InferCache).insert + (.const ``outParam [.succ .zero]) annotatedPiOutParamFnType).insert + annotatedPiRawDomainKernel (.sort (.succ .zero)), + success := m } + +private theorem annotatedPiInferTypeDomainOnlyAny + (fuel : Nat) (m : Std.HashSet (Expr × Expr)) : + TypeChecker.Inner.inferType annotatedPiRawDomainKernel true + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := m } : TypeChecker.State) = + .ok (.sort (.succ .zero), annotatedPiDomainInferOnlyState m) := by + change + TypeChecker.Inner.inferType' annotatedPiRawDomainKernel true + (TypeChecker.Methods.withFuel (fuel + 2)) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := m } : TypeChecker.State) = + .ok (.sort (.succ .zero), annotatedPiDomainInferOnlyState m) + rw [show annotatedPiRawDomainKernel = + .app (.const ``outParam [.succ .zero]) (.sort .zero) by rfl] + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', + annotatedPiDomainInferOnlyState, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + rw [show + .app (.const ``outParam [.succ .zero]) (.sort .zero) = + annotatedPiRawDomainKernel by rfl] + rw [annotatedPiInferAppDomainOnly fuel] + simp [annotatedPiOutParamInferOnlyState] + +private theorem annotatedPiInferTypeDomainOnly998 + (m : Std.HashSet (Expr × Expr)) : + TypeChecker.Inner.inferType' + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) true + (TypeChecker.Methods.withFuel 9998) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := m } : TypeChecker.State) = + .ok (.sort (.succ .zero), annotatedPiDomainInferOnlyState m) := by + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', + annotatedPiDomainInferOnlyState, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + rw [show + .app (.const ``outParam [.succ .zero]) (.sort .zero) = + annotatedPiRawDomainKernel by rfl] + rw [show TypeChecker.Inner.inferApp annotatedPiRawDomainKernel + (TypeChecker.Methods.withFuel 9998) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := m } : TypeChecker.State) = + .ok (.sort (.succ .zero), annotatedPiOutParamInferOnlyState m) by + simpa only [Nat.reduceAdd] using + annotatedPiInferAppDomainOnly 9996 m] + simp [annotatedPiOutParamInferOnlyState] + +private def annotatedPiSortOneInferOnlyState + (m : Std.HashSet (Expr × Expr)) : TypeChecker.State := + { annotatedPiDomainInferOnlyState m with + inferTypeI := (annotatedPiDomainInferOnlyState m).inferTypeI.insert + (.sort (.succ .zero)) (.sort (.succ (.succ .zero))) } + +@[simp] private theorem annotatedPiDomainInferOnlyState_sortOneMiss + (m : Std.HashSet (Expr × Expr)) : + (annotatedPiDomainInferOnlyState m).inferTypeI[ + (.sort (.succ .zero) : Expr)]? = none := by + simp [annotatedPiDomainInferOnlyState, annotatedPiRawDomainKernel, annotatedPiOutParamFnType] + +private theorem annotatedPiInferTypeSortOneOnly + (fuel : Nat) (m : Std.HashSet (Expr × Expr)) : + TypeChecker.Inner.inferType (.sort (.succ .zero)) true + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker + (annotatedPiDomainInferOnlyState m) = + .ok (.sort (.succ (.succ .zero)), + annotatedPiSortOneInferOnlyState m) := by + change + TypeChecker.Inner.inferType' (.sort (.succ .zero)) true + (TypeChecker.Methods.withFuel (fuel + 2)) + annotatedPiCtorCandidateContext.toTypeChecker + (annotatedPiDomainInferOnlyState m) = + .ok (.sort (.succ (.succ .zero)), + annotatedPiSortOneInferOnlyState m) + unfold TypeChecker.Inner.inferType' + simp [annotatedPiDomainInferOnlyState_sortOneMiss, + annotatedPiSortOneInferOnlyState, Expr.hasLooseBVars, + Expr.looseBVarRange', Bind.bind, ReaderT.bind, StateT.bind, + Except.bind] + +@[simp] private theorem annotatedPiWhnfSortTwo + (fuel : Nat) (state : TypeChecker.State) : + TypeChecker.Inner.whnf (.sort (.succ (.succ .zero))) + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker state = + .ok (.sort (.succ (.succ .zero)), state) := by + rfl + +private theorem annotatedPiIsPropSortOneFalse + (fuel : Nat) (m : Std.HashSet (Expr × Expr)) : + TypeChecker.Inner.isProp (.sort (.succ .zero)) + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker + (annotatedPiDomainInferOnlyState m) = + .ok (false, annotatedPiSortOneInferOnlyState m) := by + unfold TypeChecker.Inner.isProp TypeChecker.Inner.getSortLevel + simp only [normalizationRecMBind] + rw [annotatedPiInferTypeSortOneOnly fuel] + rfl + +private theorem annotatedPiIsDefEqProofIrrelDomain + (fuel : Nat) (m : Std.HashSet (Expr × Expr)) : + TypeChecker.Inner.isDefEqProofIrrel + annotatedPiRawDomainKernel (.sort .zero) + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := m } : TypeChecker.State) = + .ok (.undef, annotatedPiSortOneInferOnlyState m) := by + unfold TypeChecker.Inner.isDefEqProofIrrel + simp only [normalizationRecMBind] + rw [annotatedPiInferTypeDomainOnlyAny fuel] + simp only [] + rw [annotatedPiIsPropSortOneFalse fuel] + rfl + +private theorem annotatedPiUnfoldOutParamCoreOfMiss + (methods : TypeChecker.Methods) (state : TypeChecker.State) + (hcache : state.unfold[ + (.const ``outParam [.succ .zero] : Expr)]? = none) : + TypeChecker.Inner.unfoldDefinitionCore + (.const ``outParam [.succ .zero]) methods + annotatedPiCtorCandidateContext.toTypeChecker state = + .ok (some annotatedPiOutParamWhnfKernelExpr, + annotatedPiOutParamUnfoldState state) := by + simp [TypeChecker.Inner.unfoldDefinitionCore, + TypeChecker.Inner.isDelta, Expr.getAppFn, + annotatedPiCtorCandidateContext, + AddInductive.Context.toTypeChecker, annotatedPiType_lookup_outParam, + hcache, Bind.bind, ReaderT.bind, StateT.bind, Except.bind, + annotationOutParamInfo, ConstantInfo.deltaValue?, + TypeChecker.Inner.instantiateDeltaValue, + ConstantInfo.numLevelParams, + ConstantInfo.instantiateValueLevelParams!, ConstantInfo.levelParams, + ConstantInfo.value!, ConstantInfo.toConstantVal, + Expr.instantiateLevelParams, annotatedPiOutParamWhnfKernelExpr, + annotatedPiOutParamUnfoldState] + +private theorem annotatedPiUnfoldDomainOfMiss + (methods : TypeChecker.Methods) (state : TypeChecker.State) + (hcache : state.unfold[ + (.const ``outParam [.succ .zero] : Expr)]? = none) : + TypeChecker.Inner.unfoldDefinition annotatedPiRawDomainKernel + methods annotatedPiCtorCandidateContext.toTypeChecker state = + .ok (some annotatedPiDomainBetaKernel, + annotatedPiOutParamUnfoldState state) := by + change + TypeChecker.Inner.unfoldDefinition + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) + methods annotatedPiCtorCandidateContext.toTypeChecker state = + .ok (some annotatedPiDomainBetaKernel, + annotatedPiOutParamUnfoldState state) + unfold TypeChecker.Inner.unfoldDefinition + rw [if_pos (show (Expr.app (.const ``outParam [.succ .zero]) + (.sort .zero)).isApp = true from rfl)] + rw [show + (Expr.app (.const ``outParam [.succ .zero]) + (.sort .zero)).getAppFn = + .const ``outParam [.succ .zero] by rfl] + simp only [normalizationRecMBind] + rw [annotatedPiUnfoldOutParamCoreOfMiss methods state hcache] + rw [show + (Expr.app (.const ``outParam [.succ .zero]) + (.sort .zero)).getAppRevArgs = #[.sort .zero] by rfl] + simp only [normalizationRecMPure] + rw [Expr.mkAppRevRange_eq + (l₁ := []) (l₂ := [.sort .zero]) (l₃ := []) + (by simp) (by rfl) (by rfl)] + rfl + +@[simp] private theorem annotatedPiWhnfCoreIdentityCheap + (fuel : Nat) (state) : + TypeChecker.Inner.whnfCore + (.lam `α (.sort (.succ .zero)) (.bvar 0) .default) + true (TypeChecker.Methods.withFuel (fuel + 2)) + annotatedPiCtorCandidateContext.toTypeChecker state = + .ok (.lam `α (.sort (.succ .zero)) (.bvar 0) .default, + state) := by + rfl + +@[simp] private theorem annotatedPiWhnfCoreSortZeroCheap + (fuel : Nat) (state) : + TypeChecker.Inner.whnfCore (.sort .zero) true + (TypeChecker.Methods.withFuel (fuel + 2)) + annotatedPiCtorCandidateContext.toTypeChecker state = + .ok (.sort .zero, state) := by + rfl + +private theorem annotatedPiWhnfCoreDomainBetaCheap + (fuel : Nat) (m : Std.HashSet (Expr × Expr)) : + TypeChecker.Inner.whnfCore annotatedPiDomainBetaKernel true + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker + (annotatedPiOutParamUnfoldState + (annotatedPiSortOneInferOnlyState m)) = + .ok (.sort .zero, + annotatedPiOutParamUnfoldState + (annotatedPiSortOneInferOnlyState m)) := by + change + TypeChecker.Inner.whnfCore' annotatedPiDomainBetaKernel true + (TypeChecker.Methods.withFuel (fuel + 2)) + annotatedPiCtorCandidateContext.toTypeChecker + (annotatedPiOutParamUnfoldState + (annotatedPiSortOneInferOnlyState m)) = + .ok (.sort .zero, + annotatedPiOutParamUnfoldState + (annotatedPiSortOneInferOnlyState m)) + rw [annotatedPiDomainBetaKernel, + annotatedPiOutParamWhnfKernelExpr_eq] + unfold TypeChecker.Inner.whnfCore' + simp only [normalizationRecMBind, normalizationRecMGet, annotatedPiOutParamUnfoldState, + annotatedPiSortOneInferOnlyState, annotatedPiDomainInferOnlyState, Std.HashMap.getElem?_empty] + rw [Expr.withRevApp_eq] + simp only [normalizationRecMBind] + rw [show + (Expr.app + (.lam `α (.sort (.succ .zero)) (.bvar 0) .default) + (.sort .zero)).getAppFn = + .lam `α (.sort (.succ .zero)) (.bvar 0) .default by rfl] + rw [annotatedPiWhnfCoreIdentityCheap fuel] + rw [show + (Expr.app + (.lam `α (.sort (.succ .zero)) (.bvar 0) .default) + (.sort .zero)).getAppRevArgs = #[.sort .zero] by rfl] + simp [TypeChecker.Inner.whnfCore'.loop, TypeChecker.Inner.whnfCore'.loop.cont, + TypeChecker.Inner.whnfCore'.save, annotatedPiOutParamWhnfKernelExpr_eq, Expr.instantiate1', + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +private theorem annotatedPiQuickIsDefEqSortZeroAny + (methods : TypeChecker.Methods) + (context : TypeChecker.Context) + (initial : TypeChecker.State) : + ∃ m : Std.HashSet (Expr × Expr), + TypeChecker.Inner.quickIsDefEq (.sort .zero) (.sort .zero) + methods context initial = + .ok (.true, annotatedPiWithSuccessCache initial m) := by + -- The two sides are syntactically equal, so the structural check settles it without the cache. + refine ⟨initial.success, ?_⟩ + simp [TypeChecker.Inner.quickIsDefEq, annotatedPiWithSuccessCache, pure, ReaderT.pure, + StateT.pure, Except.pure, Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +private theorem annotatedPiIsDeltaDomain : + TypeChecker.Inner.isDelta annotatedPiTypeKernelEnv + annotatedPiRawDomainKernel = + some annotationOutParamInfo := by + unfold TypeChecker.Inner.isDelta + rw [annotatedPiRawDomain_getAppFn] + simp only + rw [annotatedPiType_lookup_outParam] + simp [annotationOutParamInfo, ConstantInfo.deltaValue?, + ConstantInfo.numLevelParams, ConstantInfo.levelParams, + ConstantInfo.toConstantVal] + +@[simp] private theorem annotatedPiIsDeltaSortZero : + TypeChecker.Inner.isDelta annotatedPiTypeKernelEnv (.sort .zero) = + none := by + rfl + +@[simp] private theorem annotatedPiTryUnfoldProjAppSortZero + (methods state) : + TypeChecker.Inner.tryUnfoldProjApp (.sort .zero) methods + annotatedPiCtorCandidateContext.toTypeChecker state = + .ok (none, state) := by + rfl + +private theorem annotatedPiDeltaDomain + (fuel : Nat) (m : Std.HashSet (Expr × Expr)) : + (TypeChecker.Inner.unfoldDefinition annotatedPiRawDomainKernel >>= + fun e => TypeChecker.Inner.whnfCore e.get! true) + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker + (annotatedPiSortOneInferOnlyState m) = + .ok (.sort .zero, + annotatedPiOutParamUnfoldState + (annotatedPiSortOneInferOnlyState m)) := by + simp only [normalizationRecMBind] + rw [annotatedPiUnfoldDomainOfMiss + (TypeChecker.Methods.withFuel (fuel + 3)) + (annotatedPiSortOneInferOnlyState m) (by + simp [annotatedPiSortOneInferOnlyState, + annotatedPiDomainInferOnlyState])] + simp only + rw [show (some annotatedPiDomainBetaKernel).get! = + annotatedPiDomainBetaKernel by rfl] + rw [annotatedPiWhnfCoreDomainBetaCheap fuel] + +private theorem annotatedPiLazyDeltaStepDomain + (fuel : Nat) (m : Std.HashSet (Expr × Expr)) : + ∃ m' : Std.HashSet (Expr × Expr), + TypeChecker.Inner.lazyDeltaReductionStep + annotatedPiRawDomainKernel (.sort .zero) + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker + (annotatedPiSortOneInferOnlyState m) = + .ok (.bool true, + annotatedPiWithSuccessCache + (annotatedPiOutParamUnfoldState + (annotatedPiSortOneInferOnlyState m)) m') := by + obtain ⟨m', hquick⟩ := annotatedPiQuickIsDefEqSortZeroAny + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker + (annotatedPiOutParamUnfoldState + (annotatedPiSortOneInferOnlyState m)) + refine ⟨m', ?_⟩ + unfold TypeChecker.Inner.lazyDeltaReductionStep + rw [normalizationRecMBind] + rw [normalizationRecMGetEnv] + simp only + rw [show annotatedPiCtorCandidateContext.toTypeChecker.env = + annotatedPiTypeKernelEnv by rfl] + rw [annotatedPiIsDeltaDomain, annotatedPiIsDeltaSortZero] + simp only + rw [normalizationRecMBind] + rw [annotatedPiTryUnfoldProjAppSortZero] + simp only + rw [normalizationRecMBind] + rw [annotatedPiDeltaDomain fuel] + simp only + rw [normalizationRecMBind] + rw [hquick] + rfl + +@[simp] private theorem annotatedPiIsDefEqOffsetDomain + (fuel : Nat) (m : Std.HashSet (Expr × Expr)) : + TypeChecker.Inner.isDefEqOffset + annotatedPiRawDomainKernel (.sort .zero) + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker + (annotatedPiSortOneInferOnlyState m) = + .ok (.undef, annotatedPiSortOneInferOnlyState m) := by + have hzero : + (annotatedPiRawDomainKernel == Expr.natZero) = false := by + rw [show annotatedPiRawDomainKernel = + .app (.const ``outParam [.succ .zero]) (.sort .zero) by rfl] + exact annotatedPiApp_beq_const _ _ _ _ + unfold TypeChecker.Inner.isDefEqOffset + simp [TypeChecker.Inner.isNatZero, TypeChecker.Inner.isNatSuccOf?, annotatedPiRawDomainKernel, + Expr.natZero] + +private theorem annotatedPiLazyDeltaLoopDomain + (fuel : Nat) (m : Std.HashSet (Expr × Expr)) : + ∃ m' : Std.HashSet (Expr × Expr), + TypeChecker.Inner.lazyDeltaReduction.loop + annotatedPiRawDomainKernel (.sort .zero) 1000 + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker + (annotatedPiSortOneInferOnlyState m) = + .ok (.bool true, + annotatedPiWithSuccessCache + (annotatedPiOutParamUnfoldState + (annotatedPiSortOneInferOnlyState m)) m') := by + obtain ⟨m', hstep⟩ := annotatedPiLazyDeltaStepDomain fuel m + refine ⟨m', ?_⟩ + rw [show 1000 = 999 + 1 by rfl] + unfold TypeChecker.Inner.lazyDeltaReduction.loop + rw [normalizationRecMBind] + rw [annotatedPiIsDefEqOffsetDomain fuel] + simp only + rw [show (LBool.undef != LBool.undef) = false by rfl] + simp only [Bool.false_eq_true, if_false] + rw [normalizationRecMBind] + rw [normalizationRecMReadContext] + simp only + rw [show + (!annotatedPiRawDomainKernel.hasFVar && + !(.sort .zero : Expr).hasFVar || + annotatedPiCtorCandidateContext.toTypeChecker.eagerReduce) = true + by + simp [Expr.hasFVar_eq, Expr.hasFVar', + annotatedPiRawDomainKernel]] + simp only [if_true] + rw [normalizationRecMBind] + rw [annotatedPiReduceNatDomain] + simp only + rw [normalizationRecMBind] + rw [normalizationReduceNatSort] + simp only + rw [normalizationRecMBind] + rw [normalizationRecMGetEnv] + simp only + rw [normalizationRecMBind] + rw [annotatedPiReduceNativeDomain] + simp only + rw [normalizationRecMBind] + rw [normalizationReduceNativeSort] + simp only + rw [normalizationRecMBind] + rw [hstep] + rfl + +private theorem annotatedPiLazyDeltaDomain + (fuel : Nat) (m : Std.HashSet (Expr × Expr)) : + ∃ m' : Std.HashSet (Expr × Expr), + TypeChecker.Inner.lazyDeltaReduction + annotatedPiRawDomainKernel (.sort .zero) + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker + (annotatedPiSortOneInferOnlyState m) = + .ok (.bool true, + annotatedPiWithSuccessCache + (annotatedPiOutParamUnfoldState + (annotatedPiSortOneInferOnlyState m)) m') := by + obtain ⟨m', hloop⟩ := annotatedPiLazyDeltaLoopDomain fuel m + refine ⟨m', ?_⟩ + unfold TypeChecker.Inner.lazyDeltaReduction + rw [normalizationRecMBind] + rw [normalizationRecMReadContext] + simp only + change + TypeChecker.Inner.lazyDeltaReduction.loop + annotatedPiRawDomainKernel (.sort .zero) 1000 + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker + (annotatedPiSortOneInferOnlyState m) = _ + exact hloop + +private theorem annotatedPiQuickIsDefEqDomainAny + (fuel : Nat) (m : Std.HashSet (Expr × Expr)) : + ∃ (r : LBool) (m' : Std.HashSet (Expr × Expr)), + TypeChecker.Inner.quickIsDefEq + annotatedPiRawDomainKernel (.sort .zero) + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := m } : TypeChecker.State) = + .ok (r, ({ success := m' } : TypeChecker.State)) ∧ + (r = .true ∨ r = .undef) := + annotatedPiQuickIsDefEqDomainInitial _ _ m + +@[simp] private theorem annotatedPiWhnfCoreSortCheap + (fuel : Nat) (state : TypeChecker.State) : + TypeChecker.Inner.whnfCore (.sort .zero) true + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker state = + .ok (.sort .zero, state) := by + rfl + +private theorem annotatedPiIsDefEqCoreDomain + (fuel : Nat) (initial : Std.HashSet (Expr × Expr) := {}) : + ∃ state : TypeChecker.State, + TypeChecker.Inner.isDefEqCore' + annotatedPiRawDomainKernel (.sort .zero) + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := initial } : TypeChecker.State) = + .ok (true, state) := by + obtain ⟨r, m, hquick, hr⟩ := annotatedPiQuickIsDefEqDomainInitial + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker initial + unfold TypeChecker.Inner.isDefEqCore' + rw [normalizationRecMBind] + rw [hquick] + rcases hr with htrue | hundef + · subst r + simp only + rw [show (LBool.true != LBool.undef) = true by rfl] + simp only [if_true] + exact ⟨({ success := m } : TypeChecker.State), rfl⟩ + · subst r + simp only + rw [show (LBool.undef != LBool.undef) = false by rfl] + simp only [Bool.false_eq_true, if_false] + rw [normalizationRecMBind] + rw [normalizationRecMReadContext] + simp only + rw [show ((.sort .zero : Expr).isConstOf ``true) = false by rfl] + simp only [Bool.and_false, Bool.false_eq_true, if_false] + rw [normalizationRecMBind] + rw [annotatedPiWhnfCoreDomainCheap fuel] + simp only + rw [normalizationRecMBind] + rw [annotatedPiWhnfCoreSortCheap fuel] + simp only + cases hptr : + (!(ptrEqExpr annotatedPiRawDomainKernel annotatedPiRawDomainKernel && + ptrEqExpr (.sort .zero) (.sort .zero))) + · simp only [Bool.false_eq_true, if_false] + rw [normalizationRecMBind] + rw [annotatedPiIsDefEqProofIrrelDomain fuel] + simp only + rw [show (LBool.undef != LBool.undef) = false by rfl] + simp only [Bool.false_eq_true, if_false] + rw [normalizationRecMBind] + obtain ⟨m'', hlazy⟩ := annotatedPiLazyDeltaDomain fuel m + rw [hlazy] + refine ⟨annotatedPiWithSuccessCache + (annotatedPiOutParamUnfoldState + (annotatedPiSortOneInferOnlyState m)) m'', ?_⟩ + rfl + · simp only [if_true] + obtain ⟨r, m', hquick', hr⟩ := + annotatedPiQuickIsDefEqDomainAny fuel m + rw [normalizationRecMBind] + rw [hquick'] + simp only + rcases hr with htrue | hundef + · subst r + rw [show (LBool.true != LBool.undef) = true by rfl] + simp only [if_true] + refine ⟨({ success := m' } : TypeChecker.State), ?_⟩ + rfl + · subst r + rw [show (LBool.undef != LBool.undef) = false by rfl] + simp only [Bool.false_eq_true, if_false] + rw [normalizationRecMBind] + rw [annotatedPiIsDefEqProofIrrelDomain fuel] + simp only + rw [show (LBool.undef != LBool.undef) = false by rfl] + simp only [Bool.false_eq_true, if_false] + rw [normalizationRecMBind] + obtain ⟨m'', hlazy⟩ := annotatedPiLazyDeltaDomain fuel m' + rw [hlazy] + refine ⟨annotatedPiWithSuccessCache + (annotatedPiOutParamUnfoldState + (annotatedPiSortOneInferOnlyState m')) m'', ?_⟩ + rfl + +private theorem annotatedPiDomain_isDefEqInner + (fuel : Nat) (initial : Std.HashSet (Expr × Expr) := {}) : + ∃ state : TypeChecker.State, + TypeChecker.Inner.isDefEq annotatedPiRawDomainKernel (.sort .zero) + (TypeChecker.Methods.withFuel (fuel + 4)) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := initial } : TypeChecker.State) = + .ok (true, state) := by + obtain ⟨state, hcore⟩ := annotatedPiIsDefEqCoreDomain fuel initial + have hcore' : + TypeChecker.Inner.isDefEqCore + annotatedPiRawDomainKernel (.sort .zero) + (TypeChecker.Methods.withFuel (fuel + 4)) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := initial } : TypeChecker.State) = + .ok (true, state) := by + change + TypeChecker.Inner.isDefEqCore' + annotatedPiRawDomainKernel (.sort .zero) + (TypeChecker.Methods.withFuel (fuel + 3)) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := initial } : TypeChecker.State) = + .ok (true, state) + exact hcore + unfold TypeChecker.Inner.isDefEq + rw [show + (annotatedPiRawDomainKernel == (.sort .zero : Expr)) = false by + exact annotatedPiApp_beq_sort _ _ _] + simp only [Bool.false_eq_true, if_false, normalizationRecMBind] + rw [hcore'] + exact ⟨_, rfl⟩ + +private theorem annotatedPiDomain_isDefEqM : + TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.isDefEq annotatedPiRawDomainKernel (.sort .zero)) = + .ok true := by + obtain ⟨state, hrun⟩ := annotatedPiDomain_isDefEqInner 9996 + change + Except.map (fun x : Bool × TypeChecker.State => x.1) + (TypeChecker.Inner.isDefEq + annotatedPiRawDomainKernel (.sort .zero) + (TypeChecker.Methods.withFuel 10000) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = + .ok true + rw [show TypeChecker.Inner.isDefEq + annotatedPiRawDomainKernel (.sort .zero) + (TypeChecker.Methods.withFuel 10000) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State) = .ok (true, state) by + simpa only [Nat.reduceAdd] using hrun] + rfl + +private theorem annotatedPiInner_checkTypeM : + TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.checkType annotatedPiInnerKernel) = + .ok (.sort (.succ .zero)) := by + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType annotatedPiInnerKernel false + (TypeChecker.Methods.withFuel 10000) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = + .ok (.sort (.succ .zero)) + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' annotatedPiInnerKernel false + (TypeChecker.Methods.withFuel 9999) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = + .ok (.sort (.succ .zero)) + unfold annotatedPiInnerKernel TypeChecker.Inner.inferType' + simp [annotatedPiRawDomainKernel, Expr.hasLooseBVars, Expr.looseBVarRange', + TypeChecker.Inner.inferType', TypeChecker.Inner.inferForall, TypeChecker.Inner.inferForall.loop, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [annotatedPiIsDefEqSort 9998] + simp [Expr.instantiate1', annotatedPiWithLocalDecl, annotatedPiCtorCandidateContext, + AddInductive.Context.toTypeChecker, Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + simp [Expr.sortLevel!, annotatedPi_mkLevelIMaxSuccZero] + rfl + +private theorem annotatedPiInner_whnfM : + TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.whnf annotatedPiInnerKernel) = + .ok annotatedPiInnerKernel := by + rfl + +private theorem annotatedPiInner_isDefEqM : + TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.isDefEq annotatedPiInnerKernel + annotatedPiInnerKernel) = .ok true := by + change + Except.map (fun x : Bool × TypeChecker.State => x.1) + (TypeChecker.Inner.isDefEq annotatedPiInnerKernel + annotatedPiInnerKernel (TypeChecker.Methods.withFuel 10000) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = .ok true + unfold TypeChecker.Inner.isDefEq + rw [if_pos (Expr.eqv_refl _)] + rfl + +private theorem annotatedPiConst_checkTypeM (lctx : LocalContext) : + TypeChecker.M.run annotatedPiTypeKernelEnv .safe lctx [] + ({} : FuelConfig) + (TypeChecker.checkType (.const ``AnnotatedPi [])) = + .ok (.sort (.succ .zero)) := by + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType (.const ``AnnotatedPi []) false + (TypeChecker.Methods.withFuel 10000) + ({ env := annotatedPiTypeKernelEnv, lctx := lctx } : + TypeChecker.Context) + ({} : TypeChecker.State)) = + .ok (.sort (.succ .zero)) + rw [annotatedPiInferTypeFamily 9999 lctx + ({} : TypeChecker.State) Std.HashMap.getElem?_empty] + rfl + +private def annotatedPiNormalizationRawContext + (lctx : LocalContext) : TypeChecker.Context := + { env := annotatedPiTypeKernelEnv, lctx := lctx } + +private theorem unfoldAnnotatedPi (lctx methods state) : + TypeChecker.Inner.unfoldDefinition (.const ``AnnotatedPi []) + methods (annotatedPiNormalizationRawContext lctx) state = + .ok (none, state) := by + change + TypeChecker.Inner.unfoldDefinitionCore (.const ``AnnotatedPi []) + methods (annotatedPiNormalizationRawContext lctx) state = + .ok (none, state) + simp [TypeChecker.Inner.unfoldDefinitionCore, TypeChecker.Inner.isDelta, + Expr.getAppFn, annotatedPiNormalizationRawContext, + annotatedPiType_lookup_family, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind, annotatedPiInfo, ConstantInfo.deltaValue?] + +private theorem whnfLoopAnnotatedPi (lctx methods state n) : + TypeChecker.Inner.whnf'.loop (.const ``AnnotatedPi []) (n + 1) + methods (annotatedPiNormalizationRawContext lctx) state = + .ok (.const ``AnnotatedPi [], state) := by + unfold TypeChecker.Inner.whnf'.loop + simp [unfoldAnnotatedPi] + +private theorem annotatedPiConst_whnfM (lctx : LocalContext) : + TypeChecker.M.run annotatedPiTypeKernelEnv .safe lctx [] + ({} : FuelConfig) + (TypeChecker.whnf (.const ``AnnotatedPi [])) = + .ok (.const ``AnnotatedPi []) := by + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.whnf' (.const ``AnnotatedPi []) + (TypeChecker.Methods.withFuel 9999) + (annotatedPiNormalizationRawContext lctx) + ({} : TypeChecker.State)) = + .ok (.const ``AnnotatedPi []) + unfold TypeChecker.Inner.whnf' + simp + rw [show + (if (annotatedPiNormalizationRawContext lctx).eagerReduce then + (annotatedPiNormalizationRawContext lctx).fuel.whnfEager + else (annotatedPiNormalizationRawContext lctx).fuel.whnf) = + 100000 by rfl] + rw [show 100000 = 99999 + 1 by rfl] + rw [whnfLoopAnnotatedPi] + simp [Functor.map, StateT.map, Except.map] + +private def annotatedPiFamilyCandidateStep : + AddInductive.CandidateWhnfStep where + context := annotatedPiFamilyCandidateContext + source := annotatedPiInfo.type + result := annotatedPiInfo.type + +private theorem annotatedPiFamilyCandidateStep_valid : + annotatedPiFamilyCandidateStep.Valid := by + exact annotatedPiFamily_whnfM + +private def annotatedPiFamilyCheckTypeStep : + AddInductive.CandidateCheckTypeStep where + context := annotatedPiFamilyCandidateContext + source := annotatedPiInfo.type + inferred := .sort (.succ (.succ .zero)) + +private theorem annotatedPiFamilyCheckTypeStep_valid : + annotatedPiFamilyCheckTypeStep.Valid := by + exact annotatedPiFamily_checkTypeM + +private def annotatedPiCtorCandidateStep : + AddInductive.CandidateWhnfStep where + context := annotatedPiCtorCandidateContext + source := annotatedPiMkInfo.type + result := annotatedPiMkInfo.type + +private theorem annotatedPiCtorCandidateStep_valid : + annotatedPiCtorCandidateStep.Valid := by + exact annotatedPiCtor_whnfM + +private def annotatedPiCtorCheckTypeStep : + AddInductive.CandidateCheckTypeStep where + context := annotatedPiCtorCandidateContext + source := annotatedPiMkInfo.type + inferred := .sort (.succ .zero) + +private theorem annotatedPiCtorCheckTypeStep_valid : + annotatedPiCtorCheckTypeStep.Valid := by + exact annotatedPiCtor_checkTypeM + +private def annotatedPiInnerCandidateStep : + AddInductive.CandidateWhnfStep where + context := annotatedPiCtorCandidateContext + source := annotatedPiInnerKernel + result := annotatedPiInnerKernel + +private theorem annotatedPiInnerCandidateStep_valid : + annotatedPiInnerCandidateStep.Valid := by + exact annotatedPiInner_whnfM + +private def annotatedPiInnerCheckTypeStep : + AddInductive.CandidateCheckTypeStep where + context := annotatedPiCtorCandidateContext + source := annotatedPiInnerKernel + inferred := .sort (.succ .zero) + +private theorem annotatedPiInnerCheckTypeStep_valid : + annotatedPiInnerCheckTypeStep.Valid := by + exact annotatedPiInner_checkTypeM + +private def annotatedPiDomainCandidateStep : + AddInductive.CandidateWhnfStep where + context := annotatedPiCtorCandidateContext + source := annotatedPiRawDomainKernel + result := .sort .zero + +private theorem annotatedPiDomainCandidateStep_valid : + annotatedPiDomainCandidateStep.Valid := by + exact annotatedPiDomain_whnfM + +private def annotatedPiDomainCheckTypeStep : + AddInductive.CandidateCheckTypeStep where + context := annotatedPiCtorCandidateContext + source := annotatedPiRawDomainKernel + inferred := .sort (.succ .zero) + +private theorem annotatedPiDomainCheckTypeStep_valid : + annotatedPiDomainCheckTypeStep.Valid := by + exact annotatedPiDomain_checkTypeM + +private def annotatedPiDomainAnnotations : + AddInductive.CandidateTypeAnnotations + annotatedPiRawDomainKernel where + consumed := .sort .zero + trace := .outParam [.succ .zero] (.sort .zero) (.identity _) + +private theorem annotatedPiDomainAnnotationsEq : + AddInductive.CandidateIsDefEqStep.Valid + ⟨annotatedPiCtorCandidateContext, + annotatedPiRawDomainKernel, + annotatedPiDomainAnnotations.consumed⟩ := by + exact annotatedPiDomain_isDefEqM + +private def annotatedPiInnerAnnotations : + AddInductive.CandidateTypeAnnotations annotatedPiInnerKernel where + consumed := annotatedPiInnerKernel + trace := .identity _ + +private theorem annotatedPiInnerAnnotationsEq : + AddInductive.CandidateIsDefEqStep.Valid + ⟨annotatedPiCtorCandidateContext, annotatedPiInnerKernel, + annotatedPiInnerAnnotations.consumed⟩ := by + exact annotatedPiInner_isDefEqM + +private def annotatedPiInnerBodyCandidateContext : + AddInductive.Context := + annotatedPiCtorCandidateContext.pushLocalDecl + `p .default annotatedPiDomainAnnotations.consumed + +private def annotatedPiOuterBodyCandidateContext : + AddInductive.Context := + annotatedPiCtorCandidateContext.pushLocalDecl + annotatedPiOuterName .default annotatedPiInnerAnnotations.consumed + +@[simp] private theorem addInductiveWithReader_apply + {alpha : Type} (f : AddInductive.Context → AddInductive.Context) + (x : AddInductive.M alpha) (context : AddInductive.Context) : + (withReader f x) context = x (f context) := rfl + +@[simp] private theorem addInductiveWithLocalReader_apply + {alpha : Type} (f : LocalContext → LocalContext) + (x : AddInductive.M alpha) (context : AddInductive.Context) : + (MonadWithReaderOf.withReader (m := AddInductive.M) f x) context = + x { context with lctx := f context.lctx } := rfl + +private theorem annotatedPiCtorCandidateFresh : + annotatedPiCtorCandidateContext.lctx.find? + annotatedPiCtorCandidateContext.freshFVarId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := annotatedPiCtorCandidateContext.freshFVarId) + LocalContext.WF.nil + change + ({ fvarIdToDecl := PersistentHashMap.empty, + decls := PersistentArray.empty, + auxDeclToFullName := Std.TreeMap.empty } : LocalContext).find? + annotatedPiCtorCandidateContext.freshFVarId = none + rw [h] + simp [LocalContext.toList] + +@[simp] private theorem annotatedPiConst_instantiate1 (arg : Expr) : + (Expr.const ``AnnotatedPi []).instantiate1 arg = + .const ``AnnotatedPi [] := by + simp [Expr.instantiate1_eq, Expr.instantiate1'] + +@[simp] private theorem annotatedPiConst_instantiate1' (arg : Expr) : + (Expr.const ``AnnotatedPi []).instantiate1' arg = + .const ``AnnotatedPi [] := by + rfl + +private def annotatedPiInnerBodyCandidateStep : + AddInductive.CandidateWhnfStep where + context := annotatedPiInnerBodyCandidateContext + source := .const ``AnnotatedPi [] + result := .const ``AnnotatedPi [] + +private theorem annotatedPiInnerBodyCandidateStep_valid : + annotatedPiInnerBodyCandidateStep.Valid := by + exact annotatedPiConst_whnfM _ + +private def annotatedPiInnerBodyCheckTypeStep : + AddInductive.CandidateCheckTypeStep where + context := annotatedPiInnerBodyCandidateContext + source := .const ``AnnotatedPi [] + inferred := .sort (.succ .zero) + +private theorem annotatedPiInnerBodyCheckTypeStep_valid : + annotatedPiInnerBodyCheckTypeStep.Valid := by + exact annotatedPiConst_checkTypeM _ + +private def annotatedPiOuterBodyCandidateStep : + AddInductive.CandidateWhnfStep where + context := annotatedPiOuterBodyCandidateContext + source := .const ``AnnotatedPi [] + result := .const ``AnnotatedPi [] + +private theorem annotatedPiOuterBodyCandidateStep_valid : + annotatedPiOuterBodyCandidateStep.Valid := by + exact annotatedPiConst_whnfM _ + +private def annotatedPiOuterBodyCheckTypeStep : + AddInductive.CandidateCheckTypeStep where + context := annotatedPiOuterBodyCandidateContext + source := .const ``AnnotatedPi [] + inferred := .sort (.succ .zero) + +private theorem annotatedPiOuterBodyCheckTypeStep_valid : + annotatedPiOuterBodyCheckTypeStep.Valid := by + exact annotatedPiConst_checkTypeM _ + +private def annotatedPiDomainCandidateTrace : + AddInductive.CandidateExprTrace annotatedPiCtorCandidateContext + annotatedPiRawDomainKernel := + .terminal annotatedPiCtorCandidateContext + annotatedPiRawDomainKernel (.sort (.succ .zero)) (.sort .zero) + annotatedPiDomainCheckTypeStep_valid + annotatedPiDomainCandidateStep_valid + +private def annotatedPiInnerBodyCandidateTrace : + AddInductive.CandidateExprTrace annotatedPiInnerBodyCandidateContext + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiCtorCandidateContext.freshExpr) := + .terminal annotatedPiInnerBodyCandidateContext + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiCtorCandidateContext.freshExpr) + (.sort (.succ .zero)) + (.const ``AnnotatedPi []) + (by simpa only [annotatedPiInnerBodyCheckTypeStep, + annotatedPiConst_instantiate1] using + annotatedPiInnerBodyCheckTypeStep_valid) + (by simpa only [annotatedPiInnerBodyCandidateStep, + annotatedPiConst_instantiate1] using + annotatedPiInnerBodyCandidateStep_valid) + +private def annotatedPiOuterBodyCandidateTrace : + AddInductive.CandidateExprTrace annotatedPiOuterBodyCandidateContext + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiCtorCandidateContext.freshExpr) := + .terminal annotatedPiOuterBodyCandidateContext + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiCtorCandidateContext.freshExpr) + (.sort (.succ .zero)) + (.const ``AnnotatedPi []) + (by simpa only [annotatedPiOuterBodyCheckTypeStep, + annotatedPiConst_instantiate1] using + annotatedPiOuterBodyCheckTypeStep_valid) + (by simpa only [annotatedPiOuterBodyCandidateStep, + annotatedPiConst_instantiate1] using + annotatedPiOuterBodyCandidateStep_valid) + +private def annotatedPiInnerCandidateTrace : + AddInductive.CandidateExprTrace annotatedPiCtorCandidateContext + annotatedPiInnerKernel := + .forallE annotatedPiCtorCandidateContext annotatedPiInnerKernel + (.sort (.succ .zero)) `p annotatedPiRawDomainKernel + (.const ``AnnotatedPi []) .default annotatedPiCtorCandidateFresh + annotatedPiDomainAnnotations annotatedPiDomainAnnotationsEq + annotatedPiInnerCheckTypeStep_valid + annotatedPiInnerCandidateStep_valid + annotatedPiDomainCandidateTrace annotatedPiInnerBodyCandidateTrace + +private def annotatedPiCtorCandidateTrace : + AddInductive.CandidateExprTrace annotatedPiCtorCandidateContext + annotatedPiMkInfo.type := + .forallE annotatedPiCtorCandidateContext annotatedPiMkInfo.type + (.sort (.succ .zero)) annotatedPiOuterName annotatedPiInnerKernel + (.const ``AnnotatedPi []) .default annotatedPiCtorCandidateFresh + annotatedPiInnerAnnotations annotatedPiInnerAnnotationsEq + annotatedPiCtorCheckTypeStep_valid annotatedPiCtorCandidateStep_valid + annotatedPiInnerCandidateTrace annotatedPiOuterBodyCandidateTrace + +private def annotatedPiFamilyCandidate : + AddInductive.CandidateExpr annotatedPiInfo.type := + ⟨annotatedPiFamilyCandidateContext, + .terminal annotatedPiFamilyCandidateContext annotatedPiInfo.type + (.sort (.succ (.succ .zero))) annotatedPiInfo.type + annotatedPiFamilyCheckTypeStep_valid + annotatedPiFamilyCandidateStep_valid⟩ + +private def annotatedPiCtorCandidate : + AddInductive.CandidateExpr annotatedPiMkInfo.type := + ⟨annotatedPiCtorCandidateContext, annotatedPiCtorCandidateTrace⟩ + +private def annotatedPiConstructorCandidate : + AddInductive.CandidateConstructor annotatedPiKernelCtor := + ⟨annotatedPiCtorCandidate⟩ + +private def annotatedPiFamilyListCandidate : + AddInductive.CandidateFamily annotatedPiKernelType where + familyType := ⟨annotatedPiFamilyCandidate⟩ + constructors := .cons annotatedPiConstructorCandidate .nil + +private def annotatedPiNormalizationCandidate : + AddInductive.NormalizationCandidate [annotatedPiKernelType] where + families := .cons annotatedPiFamilyListCandidate .nil + +private def annotatedPiInductiveStats : AddInductive.InductiveStats where + levels := [] + resultLevel := .succ .zero + nindices := #[0] + indConsts := #[.const ``AnnotatedPi []] + params := #[] + isNotZero := true + +private theorem annotatedPiSortOne_data_hasExprMVar_false : + (Expr.sort (.succ .zero)).data.hasExprMVar = false := by + change (Expr.sort (.succ .zero)).hasExprMVar = false + rw [Expr.hasExprMVar_eq] + rfl + +private theorem annotatedPiSortOne_data_hasLevelMVar_false : + (Expr.sort (.succ .zero)).data.hasLevelMVar = false := by + change (Expr.sort (.succ .zero)).hasLevelMVar = false + rw [Expr.hasLevelMVar_eq] + simp [Expr.hasLevelMVar', Level.hasMVar_eq, Level.hasMVar'] + +private theorem annotatedPiSortOne_data_hasFVar_false : + (Expr.sort (.succ .zero)).data.hasFVar = false := by + change (Expr.sort (.succ .zero)).hasFVar = false + rw [Expr.hasFVar_eq] + rfl + +private theorem annotatedPi_checkInductiveTypes + (k : AddInductive.InductiveStats → AddInductive.M α) : + AddInductive.checkInductiveTypes 0 #[annotatedPiKernelType] k + annotatedPiFamilyCandidateContext = + k annotatedPiInductiveStats annotatedPiFamilyCandidateContext := by + apply AddInductive.checkInductiveTypes_singleton_zero_of_whnf_sort + · decide + · simp [Kernel.Environment.checkNoMVarNoFVar, + Kernel.Environment.checkNoMVar, Kernel.Environment.checkNoFVar, + annotatedPiKernelType, annotatedPiInfo, ConstantInfo.type, + ConstantInfo.toConstantVal, Expr.hasMVar, Expr.hasFVar, + annotatedPiSortOne_data_hasExprMVar_false, + annotatedPiSortOne_data_hasLevelMVar_false, + annotatedPiSortOne_data_hasFVar_false, + Bind.bind, Except.bind, Pure.pure, Except.pure] + · simpa [annotatedPiFamilyCandidateContext, + annotatedPiKernelType] using annotatedPiFamily_checkTypeM + · simpa [annotatedPiFamilyCandidateContext, + annotatedPiKernelType, annotatedPiInfo, ConstantInfo.type, + ConstantInfo.toConstantVal] using annotatedPiFamily_whnfM + · rfl + +private theorem annotatedPiFamilyEnv_not_contains : + outParamKernelEnv.contains ``AnnotatedPi = false := by + unfold Kernel.Environment.contains + change outParamMap.contains ``AnnotatedPi = false + rw [SMap.find?_isSome, annotatedPiType_fresh] + rfl + +private theorem annotatedPiFamilyEnv_checkName : + outParamKernelEnv.checkName ``AnnotatedPi false = .ok () := by + simp [Kernel.Environment.checkName, annotatedPiFamilyEnv_not_contains, + Kernel.Environment.primitives, NameSet.ofList, NameSet.contains, Pure.pure, Except.pure] + +private theorem annotatedPiInner_hasIndOcc : + AddInductive.hasIndOcc #[.const ``AnnotatedPi []] + annotatedPiInnerKernel = true := by + simp [AddInductive.hasIndOcc, annotatedPiInnerKernel, + annotatedPiRawDomainKernel, Expr.constName!] + +private theorem annotatedPi_declareInductiveTypes : + AddInductive.declareInductiveTypes annotatedPiInductiveStats 0 + #[annotatedPiKernelType] 0 false annotatedPiFamilyCandidateContext = + .ok annotatedPiTypeKernelEnv := by + simp [AddInductive.declareInductiveTypes, annotatedPiInductiveStats, + annotatedPiKernelType, annotatedPiKernelCtor, + annotatedPiInfo, annotatedPiMkInfo, ConstantInfo.name, + ConstantInfo.type, ConstantInfo.toConstantVal, + annotatedPiFamilyCandidateContext, annotatedPiTypeKernelEnv, + outParamKernelEnv, annotatedPiTypeMap, + AddInductive.isRec, AddInductive.isRec.loop, + AddInductive.isReflexive, AddInductive.isReflexive.loop, + AddInductive.hasIndOcc, Expr.constName!, + Bind.bind, Pure.pure, Except.bind, Except.pure] + rw [show (Kernel.Environment.ofConstants `_annotatedPiCandidate + outParamMap).checkName ``AnnotatedPi = .ok () by + simpa [outParamKernelEnv] using annotatedPiFamilyEnv_checkName] + rfl + +private theorem annotatedPiCtor_data_hasExprMVar_false : + annotatedPiMkInfo.type.data.hasExprMVar = false := by + change annotatedPiMkInfo.type.hasExprMVar = false + rw [Expr.hasExprMVar_eq] + rfl + +private theorem annotatedPiCtor_data_hasLevelMVar_false : + annotatedPiMkInfo.type.data.hasLevelMVar = false := by + change annotatedPiMkInfo.type.hasLevelMVar = false + rw [Expr.hasLevelMVar_eq] + simp [annotatedPiMkInfo, ConstantInfo.type, + ConstantInfo.toConstantVal, Expr.hasLevelMVar', + Level.hasMVar_eq, Level.hasMVar'] + +private theorem annotatedPiCtor_data_hasFVar_false : + annotatedPiMkInfo.type.data.hasFVar = false := by + change annotatedPiMkInfo.type.hasFVar = false + rw [Expr.hasFVar_eq] + rfl + +private theorem annotatedPiCtor_noMVarNoFVar : + annotatedPiTypeKernelEnv.checkNoMVarNoFVar + annotatedPiMkInfo.name annotatedPiMkInfo.type = .ok () := by + simp [Kernel.Environment.checkNoMVarNoFVar, + Kernel.Environment.checkNoMVar, Kernel.Environment.checkNoFVar, + Expr.hasMVar, Expr.hasFVar, + annotatedPiCtor_data_hasExprMVar_false, + annotatedPiCtor_data_hasLevelMVar_false, + annotatedPiCtor_data_hasFVar_false, + Bind.bind, Except.bind, Pure.pure, Except.pure] + +@[simp] private theorem annotatedPiInferConstantFamilyOnly + (lctx : LocalContext) : + TypeChecker.Inner.inferConstant + ({ env := annotatedPiTypeKernelEnv, lctx := lctx } : + TypeChecker.Context) + ``AnnotatedPi [] true = + .ok (.sort (.succ .zero)) := by + unfold TypeChecker.Inner.inferConstant + rw [show annotatedPiTypeKernelEnv.get ``AnnotatedPi = + .ok annotatedPiInfo by exact annotatedPiType_get_family] + simp [annotatedPiInfo, Bind.bind, Except.bind, + annotatedPiExceptPure, ConstantInfo.levelParams, + ConstantInfo.instantiateTypeLevelParams, + ConstantInfo.toConstantVal, + ConstantVal.instantiateTypeLevelParams, + Expr.instantiateLevelParams_eq, + Expr.instantiateLevelParamsCore_id] + +private theorem annotatedPiInferTypeFamilyOnly + (n : Nat) (lctx : LocalContext) (state : TypeChecker.State) + (hcache : + state.inferTypeI[(.const ``AnnotatedPi [] : Expr)]? = none) : + TypeChecker.Inner.inferType (.const ``AnnotatedPi []) true + (TypeChecker.Methods.withFuel (n + 1)) + ({ env := annotatedPiTypeKernelEnv, lctx := lctx } : + TypeChecker.Context) + state = + .ok (.sort (.succ .zero), + { state with + inferTypeI := state.inferTypeI.insert + (.const ``AnnotatedPi []) (.sort (.succ .zero)) }) := by + change + TypeChecker.Inner.inferType' (.const ``AnnotatedPi []) true + (TypeChecker.Methods.withFuel n) + ({ env := annotatedPiTypeKernelEnv, lctx := lctx } : + TypeChecker.Context) + state = _ + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', hcache, + annotatedPiInferConstantFamilyOnly, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +private def annotatedPiInnerInferOnlyLCtx : LocalContext := + ({} : LocalContext).mkLocalDecl + ⟨({} : TypeChecker.State).ngen.curr⟩ `p + annotatedPiRawDomainKernel .default + +private def annotatedPiInnerInferOnlyState : TypeChecker.State := + { annotatedPiDomainInferOnlyState {} with + ngen := ({} : TypeChecker.State).ngen.next } + +private def annotatedPiFamilyInferOnlyState : TypeChecker.State := + { annotatedPiInnerInferOnlyState with + inferTypeI := annotatedPiInnerInferOnlyState.inferTypeI.insert + (.const ``AnnotatedPi []) (.sort (.succ .zero)) } + +@[simp] private theorem annotatedPiInnerInferOnlyState_family_miss : + annotatedPiInnerInferOnlyState.inferTypeI[ + (.const ``AnnotatedPi [] : Expr)]? = none := by + simp [annotatedPiInnerInferOnlyState, + annotatedPiDomainInferOnlyState, annotatedPiRawDomainKernel, + annotatedPiOutParamFnType] + +@[simp] private theorem annotatedPiInferTypeFamilyAfterDomainOnly : + TypeChecker.Inner.inferType' (.const ``AnnotatedPi []) true + (TypeChecker.Methods.withFuel 9998) + ({ env := annotatedPiTypeKernelEnv, lctx := + annotatedPiInnerInferOnlyLCtx } : + TypeChecker.Context) + annotatedPiInnerInferOnlyState = + .ok (.sort (.succ .zero), annotatedPiFamilyInferOnlyState) := by + change + TypeChecker.Inner.inferType (.const ``AnnotatedPi []) true + (TypeChecker.Methods.withFuel 9999) + ({ env := annotatedPiTypeKernelEnv, lctx := + annotatedPiInnerInferOnlyLCtx } : + TypeChecker.Context) + annotatedPiInnerInferOnlyState = _ + simpa [annotatedPiFamilyInferOnlyState] using + annotatedPiInferTypeFamilyOnly 9998 annotatedPiInnerInferOnlyLCtx + annotatedPiInnerInferOnlyState + annotatedPiInnerInferOnlyState_family_miss + +private theorem annotatedPiInferTypeFamilyAfterDomainOnly_exact : + TypeChecker.Inner.inferType (.const ``AnnotatedPi []) true + (TypeChecker.Methods.withFuel 9999) + { annotatedPiCtorCandidateContext.toTypeChecker with + lctx := annotatedPiCtorCandidateContext.toTypeChecker.lctx.mkLocalDecl + ⟨(annotatedPiDomainInferOnlyState {}).ngen.curr⟩ `p + annotatedPiRawDomainKernel .default } + { annotatedPiDomainInferOnlyState {} with + ngen := (annotatedPiDomainInferOnlyState {}).ngen.next } = + .ok (.sort (.succ .zero), annotatedPiFamilyInferOnlyState) := by + simpa [annotatedPiInnerInferOnlyLCtx, + annotatedPiInnerInferOnlyState, annotatedPiCtorCandidateContext, + annotatedPiDomainInferOnlyState, + AddInductive.Context.toTypeChecker] using + annotatedPiInferTypeFamilyAfterDomainOnly + +private theorem annotatedPiInferTypeFamilyAfterDomainOnly_literal : + TypeChecker.Inner.inferType (.const ``AnnotatedPi []) true + (TypeChecker.Methods.withFuel 9999) + { env := annotatedPiCtorCandidateContext.toTypeChecker.env + lctx := annotatedPiCtorCandidateContext.toTypeChecker.lctx.mkLocalDecl + { name := (annotatedPiDomainInferOnlyState {}).ngen.curr } `p + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) + .default + safety := annotatedPiCtorCandidateContext.toTypeChecker.safety + eagerReduce := + annotatedPiCtorCandidateContext.toTypeChecker.eagerReduce + lparams := annotatedPiCtorCandidateContext.toTypeChecker.lparams + fuel := annotatedPiCtorCandidateContext.toTypeChecker.fuel } + { ngen := (annotatedPiDomainInferOnlyState {}).ngen.next + inferTypeI := (annotatedPiDomainInferOnlyState {}).inferTypeI + inferTypeC := (annotatedPiDomainInferOnlyState {}).inferTypeC + whnfCoreCache := + (annotatedPiDomainInferOnlyState {}).whnfCoreCache + whnfCache := (annotatedPiDomainInferOnlyState {}).whnfCache + success := (annotatedPiDomainInferOnlyState {}).success + failure := (annotatedPiDomainInferOnlyState {}).failure + unfold := (annotatedPiDomainInferOnlyState {}).unfold } = + .ok (.sort (.succ .zero), annotatedPiFamilyInferOnlyState) := by + simpa [annotatedPiRawDomainKernel] using + annotatedPiInferTypeFamilyAfterDomainOnly_exact + +private def annotatedPiInnerInferOnlyFinalState : TypeChecker.State := + { annotatedPiFamilyInferOnlyState with + inferTypeI := annotatedPiFamilyInferOnlyState.inferTypeI.insert + annotatedPiInnerKernel (.sort (.succ .zero)) } + +set_option maxRecDepth 10000 in +private theorem annotatedPiInner_inferTypeInner : + TypeChecker.Inner.inferType annotatedPiInnerKernel true + (TypeChecker.Methods.withFuel 10000) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (.sort (.succ .zero), + annotatedPiInnerInferOnlyFinalState) := by + change + TypeChecker.Inner.inferType' annotatedPiInnerKernel true + (TypeChecker.Methods.withFuel 9999) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State) = _ + unfold annotatedPiInnerKernel TypeChecker.Inner.inferType' + simp [annotatedPiRawDomainKernel, + Expr.hasLooseBVars, Expr.looseBVarRange', + TypeChecker.Inner.inferForall, TypeChecker.Inner.inferForall.loop, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [annotatedPiInferTypeDomainOnly998] + simp only [TypeChecker.Inner.ensureSortCore, Expr.isSort, ↓reduceIte, annotatedPiWithLocalDecl, + annotatedPiRecMPure, Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [annotatedPiInferTypeFamilyAfterDomainOnly_literal] + simp [Expr.sortLevel!, annotatedPiInnerInferOnlyFinalState, + annotatedPiInnerKernel, annotatedPiRawDomainKernel] + +private theorem annotatedPiInner_inferTypeM : + TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.inferType annotatedPiInnerKernel) = + .ok (.sort (.succ .zero)) := by + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType annotatedPiInnerKernel true + (TypeChecker.Methods.withFuel 10000) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = + .ok (.sort (.succ .zero)) + rw [annotatedPiInner_inferTypeInner] + rfl + +private theorem annotatedPiInner_ensureTypeM : + TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.ensureType annotatedPiInnerKernel) = + .ok (.sort (.succ .zero)) := by + unfold TypeChecker.ensureType TypeChecker.inferType + TypeChecker.ensureSort TypeChecker.RecM.run TypeChecker.M.run + simp only [readThe, MonadReaderOf.read, ReaderT.read, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind, + Pure.pure, StateT.pure, Except.pure, StateT.run', + Functor.map, Except.map] + rw [show TypeChecker.Inner.inferType annotatedPiInnerKernel true + (TypeChecker.Methods.withFuel + annotatedPiCtorCandidateContext.fuel.recDepth) + { env := annotatedPiCtorCandidateContext.env + lctx := annotatedPiCtorCandidateContext.lctx + safety := annotatedPiCtorCandidateContext.safety + lparams := annotatedPiCtorCandidateContext.lparams + fuel := annotatedPiCtorCandidateContext.fuel } + ({} : TypeChecker.State) = + .ok (.sort (.succ .zero), + annotatedPiInnerInferOnlyFinalState) by + simpa [annotatedPiCtorCandidateContext, + AddInductive.Context.toTypeChecker] using + annotatedPiInner_inferTypeInner] + rfl + +private theorem annotatedPiCtor_getEnvM : + TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel TypeChecker.getEnv = + .ok annotatedPiTypeKernelEnv := by + rfl + +private theorem annotatedPiRawDomain_hasIndOcc_false : + AddInductive.hasIndOcc annotatedPiInductiveStats.indConsts + annotatedPiRawDomainKernel = false := by + simp [AddInductive.hasIndOcc, annotatedPiInductiveStats, + annotatedPiRawDomainKernel, Expr.constName!] + +private theorem annotatedPiConst_isValidIndAppIdx : + AddInductive.isValidIndAppIdx annotatedPiInductiveStats + (.const ``AnnotatedPi []) 0 = true := by + simp +decide [AddInductive.isValidIndAppIdx, + annotatedPiInductiveStats, Expr.getAppFn, Expr.getAppArgs, + Expr.getAppNumArgs] + +private theorem annotatedPiInner_stats_hasIndOcc : + AddInductive.hasIndOcc annotatedPiInductiveStats.indConsts + annotatedPiInnerKernel = true := by + simpa [annotatedPiInductiveStats] using annotatedPiInner_hasIndOcc + +private theorem annotatedPiConst_hasIndOcc : + AddInductive.hasIndOcc annotatedPiInductiveStats.indConsts + (.const ``AnnotatedPi []) = true := by + simp [AddInductive.hasIndOcc, annotatedPiInductiveStats, + Expr.constName!] + +private theorem annotatedPiConst_isValidIndApp : + AddInductive.isValidIndApp? annotatedPiInductiveStats + (.const ``AnnotatedPi []) = some 0 := by + exact AddInductive.isValidIndApp?_singleton_zero + annotatedPiInductiveStats (.const ``AnnotatedPi []) rfl + annotatedPiConst_isValidIndAppIdx + +private theorem annotatedPi_checkPositivity_terminal : + AddInductive.checkPositivity.loop annotatedPiInductiveStats + annotatedPiMkInfo.name 0 (.const ``AnnotatedPi []) 999 + annotatedPiInnerBodyCandidateContext = .ok () := by + rw [show 999 = 998 + 1 by rfl] + unfold AddInductive.checkPositivity.loop + simp only [ReaderT.bind, Bind.bind] + rw [AddInductive.liftTypeChecker_apply] + rw [show TypeChecker.M.run + annotatedPiInnerBodyCandidateContext.env + annotatedPiInnerBodyCandidateContext.safety + annotatedPiInnerBodyCandidateContext.lctx + annotatedPiInnerBodyCandidateContext.lparams + annotatedPiInnerBodyCandidateContext.fuel + (TypeChecker.whnf (.const ``AnnotatedPi [])) = + .ok (.const ``AnnotatedPi []) by + exact annotatedPiConst_whnfM _] + simp [annotatedPiConst_hasIndOcc, annotatedPiConst_isValidIndApp, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] + +private theorem annotatedPi_checkPositivity : + AddInductive.checkPositivity annotatedPiInductiveStats + annotatedPiInnerKernel annotatedPiMkInfo.name 0 + annotatedPiCtorCandidateContext = .ok () := by + unfold AddInductive.checkPositivity + simp only [readThe, MonadReaderOf.read, ReaderT.read, ReaderT.bind, Bind.bind, Pure.pure, + Except.bind, Except.pure] + rw [show annotatedPiCtorCandidateContext.fuel.inductiveFuel = + 999 + 1 by rfl] + unfold AddInductive.checkPositivity.loop + simp only [ReaderT.bind, Bind.bind] + rw [AddInductive.liftTypeChecker_apply] + rw [annotatedPiInner_whnfM] + simp only [Except.bind] + rw [show AddInductive.hasIndOcc annotatedPiInductiveStats.indConsts + annotatedPiInnerKernel = true by + exact annotatedPiInner_stats_hasIndOcc] + simp only [Bool.not_true, Bool.false_eq_true, if_false, Pure.pure] + unfold annotatedPiInnerKernel + simp only + rw [show AddInductive.hasIndOcc annotatedPiInductiveStats.indConsts + annotatedPiRawDomainKernel = false by + exact annotatedPiRawDomain_hasIndOcc_false] + simp only [Bool.false_eq_true, if_false] + simpa [withLocalDecl, annotatedPiInnerBodyCandidateContext, + withFreshId, MonadLocalNameGenerator.withFreshId, + MonadWithReader.withReader, withTheReader, + AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshExpr, AddInductive.Context.freshFVarId, + AddInductive.consumeTypeAnnotations, annotatedPiDomainAnnotations, + annotatedPiRawDomainKernel, annotatedPiCtorCandidateContext] using + annotatedPi_checkPositivity_terminal + +private theorem annotatedPiInner_ensureTypeM_expanded : + TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.ensureType + (.forallE `p + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) + (.const ``AnnotatedPi []) .default)) = + .ok (.sort (.succ .zero)) := by + simpa [annotatedPiInnerKernel, annotatedPiRawDomainKernel] using + annotatedPiInner_ensureTypeM + +private theorem annotatedPi_checkPositivity_expanded : + AddInductive.checkPositivity annotatedPiInductiveStats + (.forallE `p + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) + (.const ``AnnotatedPi []) .default) + ``AnnotatedPi.mk 0 annotatedPiCtorCandidateContext = .ok () := by + simpa [annotatedPiMkInfo, ConstantInfo.name, + ConstantInfo.toConstantVal, annotatedPiInnerKernel, + annotatedPiRawDomainKernel] using annotatedPi_checkPositivity + +private theorem annotatedPi_checkConstructors_terminal : + AddInductive.checkConstructorType.loop annotatedPiInductiveStats false 0 + ``AnnotatedPi.mk (.const ``AnnotatedPi []) 1 999 + annotatedPiOuterBodyCandidateContext = .ok () := by + rw [show 999 = 998 + 1 by rfl] + unfold AddInductive.checkConstructorType.loop + simp [annotatedPiConst_isValidIndAppIdx, + ReaderT.pure, Pure.pure, Except.pure] + +private theorem annotatedPi_checkConstructors_terminal_expanded : + AddInductive.checkConstructorType.loop annotatedPiInductiveStats false 0 + ``AnnotatedPi.mk (.const ``AnnotatedPi []) 1 999 + ({ env := annotatedPiTypeKernelEnv + lctx := ({} : LocalContext).mkLocalDecl + ⟨({ namePrefix := `_ind_fresh } : NameGenerator).curr⟩ + (.mkNum + (.mkStr + (.mkStr (.mkStr (.mkStr .anonymous "a") "_@") + "_internal") "_hyg") 0) + (.forallE `p + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) + (.const ``AnnotatedPi []) .default) + .default + lparams := [] + ngen := ({ namePrefix := `_ind_fresh } : NameGenerator).next + safety := .safe + allowPrimitive := false } : AddInductive.Context) = .ok () := by + simpa [annotatedPiOuterBodyCandidateContext, + AddInductive.Context.pushLocalDecl, + AddInductive.Context.freshFVarId, + annotatedPiInnerAnnotations, annotatedPiOuterName, + annotatedPiInnerKernel, annotatedPiRawDomainKernel, + annotatedPiCtorCandidateContext] using + annotatedPi_checkConstructors_terminal + +private theorem annotatedPiCtor_noMVarNoFVar_literal : + annotatedPiTypeKernelEnv.checkNoMVarNoFVar + ``AnnotatedPi.mk + (.forallE annotatedPiOuterName annotatedPiInnerKernel + (.const ``AnnotatedPi []) .default) = .ok () := by + simpa [annotatedPiMkInfo, ConstantInfo.name, ConstantInfo.type, + ConstantInfo.toConstantVal, annotatedPiInnerKernel, + annotatedPiRawDomainKernel, annotatedPiOuterName] using + annotatedPiCtor_noMVarNoFVar + +private theorem annotatedPiCtor_noMVarNoFVar_projected : + annotatedPiTypeKernelEnv.checkNoMVarNoFVar + annotatedPiMkInfo.toConstantVal.name annotatedPiMkInfo.type = + .ok () := by + simpa [ConstantInfo.name] using annotatedPiCtor_noMVarNoFVar + +private theorem annotatedPiCtor_checkTypeM_literal : + TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.checkType + (.forallE annotatedPiOuterName annotatedPiInnerKernel + (.const ``AnnotatedPi []) .default)) = + .ok (.sort (.succ .zero)) := by + simpa [annotatedPiMkInfo, ConstantInfo.type, + ConstantInfo.toConstantVal, annotatedPiInnerKernel, + annotatedPiRawDomainKernel, annotatedPiOuterName] using + annotatedPiCtor_checkTypeM + +private theorem annotatedPiCtor_noMVarNoFVar_expanded : + annotatedPiTypeKernelEnv.checkNoMVarNoFVar ``AnnotatedPi.mk + (.forallE + (.mkNum + (.mkStr + (.mkStr (.mkStr (.mkStr .anonymous "a") "_@") + "_internal") "_hyg") 0) + (.forallE `p + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) + (.const ``AnnotatedPi []) .default) + (.const ``AnnotatedPi []) .default) = .ok () := by + simpa [annotatedPiMkInfo, ConstantInfo.name, ConstantInfo.type, + ConstantInfo.toConstantVal, annotatedPiOuterName, + annotatedPiInnerKernel, annotatedPiRawDomainKernel] using + annotatedPiCtor_noMVarNoFVar + +private theorem annotatedPiCtor_checkTypeM_expanded : + TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.checkType + (.forallE + (.mkNum + (.mkStr + (.mkStr (.mkStr (.mkStr .anonymous "a") "_@") + "_internal") "_hyg") 0) + (.forallE `p + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) + (.const ``AnnotatedPi []) .default) + (.const ``AnnotatedPi []) .default)) = + .ok (.sort (.succ .zero)) := by + simpa [annotatedPiMkInfo, ConstantInfo.type, + ConstantInfo.toConstantVal, annotatedPiOuterName, + annotatedPiInnerKernel, annotatedPiRawDomainKernel] using + annotatedPiCtor_checkTypeM + +private theorem annotatedPiCtor_checkTypeM_empty : + TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety {} + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.checkType + (.forallE + (.mkNum + (.mkStr + (.mkStr (.mkStr (.mkStr .anonymous "a") "_@") + "_internal") "_hyg") 0) + (.forallE `p + (.app (.const ``outParam [.succ .zero]) (.sort .zero)) + (.const ``AnnotatedPi []) .default) + (.const ``AnnotatedPi []) .default)) = + .ok (.sort (.succ .zero)) := by + simpa [annotatedPiCtorCandidateContext] using + annotatedPiCtor_checkTypeM_expanded + +private theorem annotatedPi_checkConstructors : + AddInductive.checkConstructors #[annotatedPiKernelType] + annotatedPiInductiveStats false + annotatedPiCtorCandidateContext = .ok () := by + unfold AddInductive.checkConstructors + simp only [ReaderT.bind, Bind.bind] + rw [AddInductive.liftTypeChecker_apply] + rw [annotatedPiCtor_getEnvM] + simp only [Except.bind] + unfold AddInductive.checkConstructorsLoop AddInductive.checkConstructorFold + simp +decide [annotatedPiKernelType, annotatedPiKernelCtor, annotatedPiMkInfo, ConstantInfo.name, + ConstantInfo.type, ConstantInfo.toConstantVal] + rw [annotatedPiCtor_noMVarNoFVar_expanded] + simp only [ReaderT.bind, Bind.bind, Except.bind] + rw [AddInductive.withEmptyLocalContext_apply] + rw [AddInductive.liftTypeChecker_apply] + simp only + rw [annotatedPiCtor_checkTypeM_empty] + simp only [] + unfold AddInductive.checkConstructorType + simp +decide [readThe, MonadReaderOf.read, ReaderT.read, ReaderT.bind, Bind.bind, Pure.pure, + Except.bind, Except.pure] + rw [show annotatedPiCtorCandidateContext.fuel.inductiveFuel = + 999 + 1 by rfl] + unfold AddInductive.checkConstructorType.loop + simp only + rw [show annotatedPiInductiveStats.params[0]? = none by rfl] + simp only + simp only [ReaderT.bind, Bind.bind] + rw [AddInductive.liftTypeChecker_apply] + rw [annotatedPiInner_ensureTypeM_expanded] + simp only [Except.bind] + rw [if_pos (show AddInductive.levelStructGe + annotatedPiInductiveStats.resultLevel + (Expr.sort (.succ .zero)).sortLevel! = true from rfl)] + simp only [Bool.not_false, ↓reduceIte, ReaderT.bind, Bind.bind, Except.bind] + rw [annotatedPi_checkPositivity_expanded] + simp only [] + simp only [AddInductive.withLocalDecl_apply, annotatedPiConst_instantiate1, + AddInductive.Context.pushLocalDecl, AddInductive.Context.freshFVarId, + AddInductive.consumeTypeAnnotations, annotatedPiCtorCandidateContext] + rw [annotatedPi_checkConstructors_terminal_expanded] + unfold AddInductive.checkConstructorsLoop AddInductive.checkConstructorFold + simp [ReaderT.pure, Pure.pure, Except.pure] + +private theorem annotatedPi_checkConstructorUniverseSemantics : + AddInductive.checkConstructorUniverseListSemantics + annotatedPiInductiveStats annotatedPiKernelType.ctors + annotatedPiCtorCandidateContext = .ok () := by + unfold AddInductive.checkConstructorUniverseListSemantics + simp only [annotatedPiKernelType, annotatedPiKernelCtor, + annotatedPiMkInfo, ConstantInfo.type, ConstantInfo.toConstantVal, + ReaderT.bind, Bind.bind] + unfold AddInductive.checkConstructorUniverseSemantics + simp only [readThe, MonadReaderOf.read, ReaderT.read, ReaderT.bind, Bind.bind, Pure.pure, + Except.bind, Except.pure] + rw [show annotatedPiCtorCandidateContext.fuel.inductiveFuel = + 999 + 1 by rfl] + unfold AddInductive.checkConstructorUniverseSemantics.loop + simp only + rw [show annotatedPiInductiveStats.params[0]? = none by rfl] + simp only [ReaderT.bind, Bind.bind, AddInductive.liftTypeChecker_apply] + rw [annotatedPiInner_ensureTypeM_expanded] + simp only [Except.bind] + simp [Expr.sortLevel!, AddInductive.constructorUniverseSemanticGe] + simp only [AddInductive.consumeTypeAnnotations, annotatedPiCtorCandidateContext] + unfold AddInductive.checkConstructorUniverseSemantics.loop + rfl + +private theorem annotatedPiSortAnnotationTrace_build : + AddInductive.CandidateTypeAnnotationTrace.build (.sort .zero) = + ⟨.sort .zero, .identity _⟩ := by + simp [AddInductive.CandidateTypeAnnotationTrace.build] + +private theorem annotatedPiDomainAnnotationTrace_build : + AddInductive.CandidateTypeAnnotationTrace.build + annotatedPiRawDomainKernel = + ⟨.sort .zero, + .outParam [.succ .zero] (.sort .zero) (.identity _)⟩ := by + simp [AddInductive.CandidateTypeAnnotationTrace.build, + annotatedPiRawDomainKernel, annotatedPiSortAnnotationTrace_build] + rw [annotatedPiSortAnnotationTrace_build] + +private theorem annotatedPiInnerAnnotationTrace_build : + AddInductive.CandidateTypeAnnotationTrace.build + annotatedPiInnerKernel = + ⟨annotatedPiInnerKernel, .identity _⟩ := by + simp [AddInductive.CandidateTypeAnnotationTrace.build, + annotatedPiInnerKernel] + +private theorem annotatedPiDomainAnnotations_produced : + AddInductive.buildCandidateTypeAnnotations + annotatedPiRawDomainKernel = + .ok annotatedPiDomainAnnotations := by + unfold AddInductive.buildCandidateTypeAnnotations + rw [annotatedPiDomainAnnotationTrace_build] + rfl + +private theorem annotatedPiInnerAnnotations_produced : + AddInductive.buildCandidateTypeAnnotations annotatedPiInnerKernel = + .ok annotatedPiInnerAnnotations := by + unfold AddInductive.buildCandidateTypeAnnotations + rw [annotatedPiInnerAnnotationTrace_build] + rfl + +private theorem annotatedPiDomainCandidateTrace_loop (fuel : Nat) : + AddInductive.buildCandidateExpr.loop + annotatedPiCtorCandidateContext annotatedPiRawDomainKernel + (fuel + 1) = + .ok annotatedPiDomainCandidateTrace := by + simpa only [annotatedPiDomainCandidateTrace] using + AddInductive.buildCandidateExpr_loop_of_whnf_nonForall + annotatedPiCtorCandidateContext annotatedPiRawDomainKernel + (.sort (.succ .zero)) (.sort .zero) fuel + annotatedPiDomainCheckTypeStep_valid + annotatedPiDomainCandidateStep_valid rfl + +private theorem annotatedPiInnerBodyCandidateTrace_loop (fuel : Nat) : + AddInductive.buildCandidateExpr.loop + annotatedPiInnerBodyCandidateContext + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiCtorCandidateContext.freshExpr) + (fuel + 1) = + .ok annotatedPiInnerBodyCandidateTrace := by + simpa only [annotatedPiInnerBodyCandidateTrace] using + AddInductive.buildCandidateExpr_loop_of_whnf_nonForall + annotatedPiInnerBodyCandidateContext + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiCtorCandidateContext.freshExpr) + (.sort (.succ .zero)) (.const ``AnnotatedPi []) fuel + (by simpa only [annotatedPiInnerBodyCheckTypeStep, + annotatedPiConst_instantiate1] using + annotatedPiInnerBodyCheckTypeStep_valid) + (by simpa only [annotatedPiInnerBodyCandidateStep, + annotatedPiConst_instantiate1] using + annotatedPiInnerBodyCandidateStep_valid) rfl + +private theorem annotatedPiOuterBodyCandidateTrace_loop (fuel : Nat) : + AddInductive.buildCandidateExpr.loop + annotatedPiOuterBodyCandidateContext + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiCtorCandidateContext.freshExpr) + (fuel + 1) = + .ok annotatedPiOuterBodyCandidateTrace := by + simpa only [annotatedPiOuterBodyCandidateTrace] using + AddInductive.buildCandidateExpr_loop_of_whnf_nonForall + annotatedPiOuterBodyCandidateContext + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiCtorCandidateContext.freshExpr) + (.sort (.succ .zero)) (.const ``AnnotatedPi []) fuel + (by simpa only [annotatedPiOuterBodyCheckTypeStep, + annotatedPiConst_instantiate1] using + annotatedPiOuterBodyCheckTypeStep_valid) + (by simpa only [annotatedPiOuterBodyCandidateStep, + annotatedPiConst_instantiate1] using + annotatedPiOuterBodyCandidateStep_valid) rfl + +private theorem annotatedPiInnerCandidateTrace_loop : + AddInductive.buildCandidateExpr.loop + annotatedPiCtorCandidateContext annotatedPiInnerKernel 999 = + .ok annotatedPiInnerCandidateTrace := by + rw [show 999 = 998 + 1 by rfl] + simpa only [annotatedPiInnerCandidateTrace, + annotatedPiInnerBodyCandidateContext] using + (AddInductive.buildCandidateExpr_loop_of_whnf_forall + (context := annotatedPiCtorCandidateContext) + (e := annotatedPiInnerKernel) + (inferred := .sort (.succ .zero)) + (fuel := 998) + (name := `p) + (domain := annotatedPiRawDomainKernel) + (body := .const ``AnnotatedPi []) + (binderInfo := .default) + (hfresh := annotatedPiCtorCandidateFresh) + (annotations := annotatedPiDomainAnnotations) + (hannotations := annotatedPiDomainAnnotations_produced) + (hannotationsEq := annotatedPiDomainAnnotationsEq) + (hcheck := annotatedPiInnerCheckTypeStep_valid) + (hrun := annotatedPiInnerCandidateStep_valid) + (domainCandidate := annotatedPiDomainCandidateTrace) + (bodyCandidate := annotatedPiInnerBodyCandidateTrace) + (hdomain := by + simpa using annotatedPiDomainCandidateTrace_loop 997) + (hbody := by + simpa [annotatedPiInnerBodyCandidateContext] using + annotatedPiInnerBodyCandidateTrace_loop 997)) + +private theorem annotatedPiCtorCandidateTrace_loop : + AddInductive.buildCandidateExpr.loop + annotatedPiCtorCandidateContext annotatedPiMkInfo.type + annotatedPiCtorCandidateContext.fuel.inductiveFuel = + .ok annotatedPiCtorCandidateTrace := by + change AddInductive.buildCandidateExpr.loop + annotatedPiCtorCandidateContext annotatedPiMkInfo.type + (999 + 1) = _ + simpa only [annotatedPiCtorCandidateTrace, + annotatedPiOuterBodyCandidateContext] using + (AddInductive.buildCandidateExpr_loop_of_whnf_forall + (context := annotatedPiCtorCandidateContext) + (e := annotatedPiMkInfo.type) + (inferred := .sort (.succ .zero)) + (fuel := 999) + (name := annotatedPiOuterName) + (domain := annotatedPiInnerKernel) + (body := .const ``AnnotatedPi []) + (binderInfo := .default) + (hfresh := annotatedPiCtorCandidateFresh) + (annotations := annotatedPiInnerAnnotations) + (hannotations := annotatedPiInnerAnnotations_produced) + (hannotationsEq := annotatedPiInnerAnnotationsEq) + (hcheck := annotatedPiCtorCheckTypeStep_valid) + (hrun := annotatedPiCtorCandidateStep_valid) + (domainCandidate := annotatedPiInnerCandidateTrace) + (bodyCandidate := annotatedPiOuterBodyCandidateTrace) + (hdomain := annotatedPiInnerCandidateTrace_loop) + (hbody := by + simpa [annotatedPiOuterBodyCandidateContext] using + annotatedPiOuterBodyCandidateTrace_loop 998)) + +/-- The executable candidate traversal returns the exact nested-forall +AnnotatedPi constructor trace, including both annotation boundaries and the +two recursively extended body contexts. -/ +theorem annotatedPiCtor_candidateTrace : + AddInductive.buildCandidateExpr annotatedPiMkInfo.type + annotatedPiCtorCandidateContext = + .ok annotatedPiCtorCandidate := by + unfold AddInductive.buildCandidateExpr + simp only [readThe, MonadReaderOf.read, ReaderT.read, + ReaderT.bind, Bind.bind, ReaderT.pure, Pure.pure, + Except.bind, Except.pure] + rw [annotatedPiCtorCandidateTrace_loop] + rfl + +/-- The family position is the exact terminal candidate returned in the +pre-family environment. -/ +theorem annotatedPiFamily_candidateTrace : + AddInductive.buildCandidateExpr annotatedPiInfo.type + annotatedPiFamilyCandidateContext = + .ok annotatedPiFamilyCandidate := by + apply AddInductive.buildCandidateExpr_of_whnf_nonForall + · decide + · rfl + +private theorem annotatedPiFamilyTypeListProduced : + AddInductive.CandidateFamilyTypeListProduced + annotatedPiFamilyCandidateContext + (.cons annotatedPiFamilyListCandidate.familyType .nil) := by + exact .cons (by + unfold AddInductive.normalizeCandidateFamilyType + simp only [ReaderT.bind, Bind.bind] + simp only [annotatedPiKernelType] + rw [annotatedPiFamily_candidateTrace] + rfl) .nil + +private theorem annotatedPiFamilyTypeList_candidateTrace : + AddInductive.normalizeCandidateFamilyTypeList + [annotatedPiKernelType] annotatedPiFamilyCandidateContext = + .ok (.cons annotatedPiFamilyListCandidate.familyType .nil) := by + exact annotatedPiFamilyTypeListProduced.normalize + +private theorem annotatedPiConstructorListProduced : + AddInductive.CandidateConstructorListProduced + annotatedPiCtorCandidateContext + annotatedPiFamilyListCandidate.constructors := by + exact .cons (by + unfold AddInductive.normalizeCandidateConstructor + simp only [ReaderT.bind, Bind.bind] + simp only [annotatedPiKernelCtor] + rw [annotatedPiCtor_candidateTrace] + rfl) .nil + +private theorem annotatedPiConstructorList_candidateTrace : + AddInductive.normalizeCandidateConstructorList + annotatedPiKernelType.ctors annotatedPiCtorCandidateContext = + .ok annotatedPiFamilyListCandidate.constructors := by + exact annotatedPiConstructorListProduced.normalize + +private theorem annotatedPiFamilyListProduced : + AddInductive.CandidateFamilyListProduced + annotatedPiCtorCandidateContext + (.cons annotatedPiFamilyListCandidate.familyType .nil) + annotatedPiNormalizationCandidate.families := by + exact .cons annotatedPiConstructorListProduced .nil + +private theorem annotatedPiFamilyList_candidateTrace : + AddInductive.normalizeCandidateFamilyList + (.cons annotatedPiFamilyListCandidate.familyType .nil) + annotatedPiCtorCandidateContext = + .ok annotatedPiNormalizationCandidate.families := by + exact annotatedPiFamilyListProduced.normalize + +/-- The complete positive AnnotatedPi metadata request selects the exact +nested-forall normalization candidate in the real pre-family and post-family +checker environments. -/ +theorem annotatedPiNormalizationCandidate_produced : + AddInductive.buildNormalizationCandidate 0 + [annotatedPiKernelType] 0 false + annotatedPiFamilyCandidateContext = + .ok annotatedPiNormalizationCandidate := by + unfold AddInductive.buildNormalizationCandidate + rw [annotatedPi_checkInductiveTypes] + simp only [ReaderT.bind, Bind.bind] + rw [show + (withReader (fun _ : AddInductive.Context => + { annotatedPiFamilyCandidateContext with lctx := {} }) + (AddInductive.normalizeCandidateFamilyTypeList + [annotatedPiKernelType])) annotatedPiFamilyCandidateContext = + .ok (.cons annotatedPiFamilyListCandidate.familyType .nil) by + change AddInductive.normalizeCandidateFamilyTypeList + [annotatedPiKernelType] + { annotatedPiFamilyCandidateContext with lctx := {} } = _ + rw [show { annotatedPiFamilyCandidateContext with lctx := {} } = + annotatedPiFamilyCandidateContext by rfl] + exact annotatedPiFamilyTypeList_candidateTrace] + simp only [Except.bind] + rw [annotatedPi_declareInductiveTypes] + unfold AddInductive.withEnv + change (ReaderT.bind + (AddInductive.checkConstructors #[annotatedPiKernelType] + annotatedPiInductiveStats false) + (fun _ => ReaderT.bind + (AddInductive.normalizeCandidateFamilyList + (.cons annotatedPiFamilyListCandidate.familyType .nil)) + (fun families => pure + (⟨families⟩ : AddInductive.NormalizationCandidate + [annotatedPiKernelType])))) + ({ annotatedPiFamilyCandidateContext with + env := annotatedPiTypeKernelEnv } : + AddInductive.Context) = _ + rw [show ({ annotatedPiFamilyCandidateContext with + env := annotatedPiTypeKernelEnv } : AddInductive.Context) = + annotatedPiCtorCandidateContext by rfl] + simp only [ReaderT.bind, Bind.bind] + rw [annotatedPi_checkConstructors] + simp only [Except.bind] + rw [annotatedPiFamilyList_candidateTrace] + rfl + +private def aliasFormerFamilyCandidateStep : + AddInductive.CandidateWhnfStep where + context := aliasFormerCandidateContext + source := aliasFormerInfo.type + result := .sort (.succ .zero) + +private theorem aliasFormerFamilyCandidateStep_valid : + aliasFormerFamilyCandidateStep.Valid := by + change + TypeChecker.M.run aliasFormerNormalizationKernelEnv .safe {} [] + { whnf := 2 } (TypeChecker.whnf aliasFormerInfo.type) = + .ok (.sort (.succ .zero)) + exact aliasFormerFamily_whnfM + +private def aliasFormerFamilyCheckTypeStep : + AddInductive.CandidateCheckTypeStep where + context := aliasFormerCandidateContext + source := aliasFormerInfo.type + inferred := .sort (.succ (.succ .zero)) + +private theorem aliasFormerFamilyCheckTypeStep_valid : + aliasFormerFamilyCheckTypeStep.Valid := by + change + TypeChecker.M.run aliasFormerNormalizationKernelEnv .safe {} [] + { whnf := 2 } (TypeChecker.checkType aliasFormerInfo.type) = + .ok (.sort (.succ (.succ .zero))) + exact aliasFormerFamily_checkTypeM + +private def aliasFormerCtorCheckTypeStep : + AddInductive.CandidateCheckTypeStep where + context := aliasFormerCtorCandidateContext + source := aliasFormerMkInfo.type + inferred := .const ``TypeFamilyAlias [] + +private theorem aliasFormerCtorCheckTypeStep_valid : + aliasFormerCtorCheckTypeStep.Valid := by + change + TypeChecker.M.run aliasFormerCtorNormalizationKernelEnv .safe {} [] + { whnf := 2 } (TypeChecker.checkType aliasFormerMkInfo.type) = + .ok (.const ``TypeFamilyAlias []) + exact aliasFormerCtor_checkTypeM + +private def aliasFormerCtorCandidateStep : + AddInductive.CandidateWhnfStep where + context := aliasFormerCtorCandidateContext + source := aliasFormerMkInfo.type + result := .const ``AliasFormer [] + +private theorem aliasFormerCtorCandidateStep_valid : + aliasFormerCtorCandidateStep.Valid := by + change + TypeChecker.M.run aliasFormerCtorNormalizationKernelEnv .safe {} [] + { whnf := 2 } (TypeChecker.whnf aliasFormerMkInfo.type) = + .ok (.const ``AliasFormer []) + exact aliasFormerCtor_whnfM + +private def aliasFormerFamilyCandidate : + AddInductive.CandidateExpr aliasFormerInfo.type := + ⟨aliasFormerCandidateContext, + .terminal aliasFormerCandidateContext aliasFormerInfo.type + (.sort (.succ (.succ .zero))) (.sort (.succ .zero)) + aliasFormerFamilyCheckTypeStep_valid + aliasFormerFamilyCandidateStep_valid⟩ + +private def aliasFormerCtorCandidate : + AddInductive.CandidateExpr aliasFormerMkInfo.type := + ⟨aliasFormerCtorCandidateContext, + .terminal aliasFormerCtorCandidateContext aliasFormerMkInfo.type + (.const ``TypeFamilyAlias []) (.const ``AliasFormer []) + aliasFormerCtorCheckTypeStep_valid + aliasFormerCtorCandidateStep_valid⟩ + +private def aliasFormerConstructorCandidate : + AddInductive.CandidateConstructor aliasFormerKernelCtor := + ⟨aliasFormerCtorCandidate⟩ + +private def aliasFormerFamilyListCandidate : + AddInductive.CandidateFamily aliasFormerKernelType where + familyType := ⟨aliasFormerFamilyCandidate⟩ + constructors := .cons aliasFormerConstructorCandidate .nil + +/-- Exact singleton family/constructor candidate list used to exercise the +generic positional Theory boundary. -/ +private def aliasFormerNormalizationCandidate : + AddInductive.NormalizationCandidate [aliasFormerKernelType] where + families := .cons aliasFormerFamilyListCandidate .nil + +private def aliasFormerInductiveStats : AddInductive.InductiveStats where + levels := [] + resultLevel := .succ .zero + nindices := #[0] + indConsts := #[.const ``AliasFormer []] + params := #[] + isNotZero := true + +private theorem constNil_data_hasExprMVar_false (n : Name) : + (Expr.const n []).data.hasExprMVar = false := by + change (Expr.const n []).hasExprMVar = false + rw [Expr.hasExprMVar_eq] + rfl + +private theorem constNil_data_hasLevelMVar_false (n : Name) : + (Expr.const n []).data.hasLevelMVar = false := by + change (Expr.const n []).hasLevelMVar = false + rw [Expr.hasLevelMVar_eq] + rfl + +private theorem constNil_data_hasFVar_false (n : Name) : + (Expr.const n []).data.hasFVar = false := by + change (Expr.const n []).hasFVar = false + rw [Expr.hasFVar_eq] + rfl + +private theorem aliasFormer_checkInductiveTypes + (k : AddInductive.InductiveStats → AddInductive.M α) : + AddInductive.checkInductiveTypes 0 #[aliasFormerKernelType] k + aliasFormerCandidateContext = + k aliasFormerInductiveStats aliasFormerCandidateContext := by + apply AddInductive.checkInductiveTypes_singleton_zero_of_whnf_sort + · decide + · simp [Kernel.Environment.checkNoMVarNoFVar, + Kernel.Environment.checkNoMVar, Kernel.Environment.checkNoFVar, + aliasFormerKernelType, aliasFormerInfo, ConstantInfo.type, + ConstantInfo.toConstantVal, Expr.hasMVar, Expr.hasFVar, + constNil_data_hasExprMVar_false, + constNil_data_hasLevelMVar_false, constNil_data_hasFVar_false, + Bind.bind, Except.bind, + Pure.pure, Except.pure] + · simpa [aliasFormerCandidateContext, aliasFormerKernelType] using + aliasFormerFamily_checkTypeM + · simpa [aliasFormerCandidateContext, aliasFormerKernelType] using + aliasFormerFamily_whnfM + · rfl + +private theorem aliasFormerNormalization_not_contains : + aliasFormerNormalizationKernelEnv.contains ``AliasFormer = false := by + unfold Kernel.Environment.contains + change typeFamilyAliasMap.contains ``AliasFormer = false + rw [SMap.find?_isSome, aliasFormerType_fresh] + rfl + +private theorem aliasFormerNormalization_checkName : + aliasFormerNormalizationKernelEnv.checkName ``AliasFormer false = + .ok () := by + simp [Kernel.Environment.checkName, aliasFormerNormalization_not_contains, + Kernel.Environment.primitives, NameSet.ofList, NameSet.contains, Pure.pure, Except.pure] + +private theorem aliasFormer_declareInductiveTypes : + AddInductive.declareInductiveTypes aliasFormerInductiveStats 0 + #[aliasFormerKernelType] 0 false aliasFormerCandidateContext = + .ok aliasFormerCtorNormalizationKernelEnv := by + simp [AddInductive.declareInductiveTypes, aliasFormerInductiveStats, + aliasFormerKernelType, aliasFormerKernelCtor, + aliasFormerInfo, aliasFormerMkInfo, ConstantInfo.name, + ConstantInfo.type, ConstantInfo.toConstantVal, + aliasFormerCandidateContext, aliasFormerCtorNormalizationKernelEnv, + aliasFormerNormalizationKernelEnv, aliasFormerTypeMap, + AddInductive.isRec, + AddInductive.isRec.loop, AddInductive.isReflexive, + AddInductive.isReflexive.loop, + Bind.bind, Pure.pure, + Except.bind, Except.pure] + rw [show (Kernel.Environment.ofConstants `_aliasFormerNormalization + typeFamilyAliasMap).checkName ``AliasFormer = .ok () by + simpa [aliasFormerNormalizationKernelEnv] using + aliasFormerNormalization_checkName] + rfl + +private theorem aliasFormerCtor_getEnvM : + TypeChecker.M.run aliasFormerCtorCandidateContext.env + aliasFormerCtorCandidateContext.safety + aliasFormerCtorCandidateContext.lctx + aliasFormerCtorCandidateContext.lparams + aliasFormerCtorCandidateContext.fuel TypeChecker.getEnv = + .ok aliasFormerCtorNormalizationKernelEnv := by + rfl + +private theorem aliasFormerCtor_isValidIndAppIdx : + AddInductive.isValidIndAppIdx aliasFormerInductiveStats + (.const ``AliasFormer []) 0 = true := by + simp +decide [AddInductive.isValidIndAppIdx, + aliasFormerInductiveStats, + Expr.getAppFn, Expr.getAppArgs, Expr.getAppNumArgs] + +private theorem aliasFormerCtor_noMVarNoFVar : + aliasFormerCtorNormalizationKernelEnv.checkNoMVarNoFVar + ``AliasFormer.mk (.const ``AliasFormer []) = .ok () := by + simp [Kernel.Environment.checkNoMVarNoFVar, + Kernel.Environment.checkNoMVar, Kernel.Environment.checkNoFVar, + Expr.hasMVar, Expr.hasFVar, + constNil_data_hasExprMVar_false, + constNil_data_hasLevelMVar_false, constNil_data_hasFVar_false, + Bind.bind, Except.bind, Pure.pure, Except.pure] + +private theorem aliasFormerCtor_checkTypeM_const : + TypeChecker.M.run aliasFormerCtorCandidateContext.env + aliasFormerCtorCandidateContext.safety + aliasFormerCtorCandidateContext.lctx + aliasFormerCtorCandidateContext.lparams + aliasFormerCtorCandidateContext.fuel + (TypeChecker.checkType (.const ``AliasFormer [])) = + .ok (.const ``TypeFamilyAlias []) := by + simpa [aliasFormerMkInfo, ConstantInfo.type, + ConstantInfo.toConstantVal, aliasFormerCtorCandidateContext] using + aliasFormerCtor_checkTypeM + +private theorem aliasFormerCtor_checkTypeM_empty : + TypeChecker.M.run aliasFormerCtorCandidateContext.env + aliasFormerCtorCandidateContext.safety {} + aliasFormerCtorCandidateContext.lparams + aliasFormerCtorCandidateContext.fuel + (TypeChecker.checkType (.const ``AliasFormer [])) = + .ok (.const ``TypeFamilyAlias []) := by + simpa [aliasFormerCtorCandidateContext] using + aliasFormerCtor_checkTypeM_const + +private theorem aliasFormerCtor_checkTypeM_of_empty + (lctx : LocalContext) (hlctx : lctx = {}) : + TypeChecker.M.run aliasFormerCtorCandidateContext.env + aliasFormerCtorCandidateContext.safety lctx + aliasFormerCtorCandidateContext.lparams + aliasFormerCtorCandidateContext.fuel + (TypeChecker.checkType (.const ``AliasFormer [])) = + .ok (.const ``TypeFamilyAlias []) := by + subst lctx + exact aliasFormerCtor_checkTypeM_empty + +private theorem aliasFormer_checkConstructors : + AddInductive.checkConstructors #[aliasFormerKernelType] + aliasFormerInductiveStats false aliasFormerCtorCandidateContext = + .ok () := by + unfold AddInductive.checkConstructors + simp only [ReaderT.bind, Bind.bind] + rw [AddInductive.liftTypeChecker_apply] + rw [aliasFormerCtor_getEnvM] + simp only [Except.bind] + unfold AddInductive.checkConstructorsLoop AddInductive.checkConstructorFold + simp +decide [aliasFormerKernelType, aliasFormerKernelCtor, aliasFormerMkInfo, ConstantInfo.name] + simp +decide [ConstantInfo.type, ConstantInfo.toConstantVal, AddInductive.liftTypeChecker_apply, + aliasFormerCtor_noMVarNoFVar, ReaderT.bind, Bind.bind, Except.bind] + rw [aliasFormerCtor_checkTypeM_of_empty + ({ decls := + { root := PersistentArrayNode.node #[], tail := #[] } } : + LocalContext) rfl] + simp only [] + unfold AddInductive.checkConstructorType + simp only [readThe, MonadReaderOf.read, ReaderT.read, ReaderT.bind, Bind.bind, Pure.pure, + Except.bind, Except.pure] + rw [show aliasFormerCtorCandidateContext.fuel.inductiveFuel = 999 + 1 by + rfl] + unfold AddInductive.checkConstructorType.loop + simp [aliasFormerCtor_isValidIndAppIdx, ReaderT.pure, Pure.pure, + Except.pure, AddInductive.checkConstructorFold, + AddInductive.checkConstructorsLoop] + +private theorem aliasFormer_checkConstructorUniverseSemantics : + AddInductive.checkConstructorUniverseListSemantics + aliasFormerInductiveStats aliasFormerKernelType.ctors + aliasFormerCtorCandidateContext = .ok () := by + unfold AddInductive.checkConstructorUniverseListSemantics + simp only [aliasFormerKernelType, aliasFormerKernelCtor, + aliasFormerMkInfo, ConstantInfo.type, ConstantInfo.toConstantVal, + ReaderT.bind, Bind.bind] + unfold AddInductive.checkConstructorUniverseSemantics + simp only [readThe, MonadReaderOf.read, ReaderT.read, ReaderT.bind, Bind.bind, Pure.pure, + Except.bind, Except.pure] + rw [show aliasFormerCtorCandidateContext.fuel.inductiveFuel = 999 + 1 by + rfl] + unfold AddInductive.checkConstructorUniverseSemantics.loop + rfl + +/-- The generic candidate traversal retains the exact context, input, and +result of the actual AliasFormer family WHNF observation. -/ +theorem aliasFormerFamily_candidateTrace : + AddInductive.buildCandidateExpr aliasFormerInfo.type + aliasFormerCandidateContext = + .ok aliasFormerFamilyCandidate := by + apply AddInductive.buildCandidateExpr_of_whnf_nonForall + · decide + · rfl + +private theorem aliasFormerFamilyTypeListProduced : + AddInductive.CandidateFamilyTypeListProduced aliasFormerCandidateContext + (.cons aliasFormerFamilyListCandidate.familyType .nil) := by + exact .cons (by + unfold AddInductive.normalizeCandidateFamilyType + simp only [ReaderT.bind, Bind.bind] + simp only [aliasFormerKernelType] + rw [aliasFormerFamily_candidateTrace] + rfl) .nil + +private theorem aliasFormerFamilyTypeList_candidateTrace : + AddInductive.normalizeCandidateFamilyTypeList + [aliasFormerKernelType] aliasFormerCandidateContext = + .ok (.cons aliasFormerFamilyListCandidate.familyType .nil) := by + exact aliasFormerFamilyTypeListProduced.normalize + +/-- The post-family constructor position is produced by the same executable +candidate traversal and retains its exact opaque result. -/ +theorem aliasFormerCtor_candidateTrace : + AddInductive.buildCandidateExpr aliasFormerMkInfo.type + aliasFormerCtorCandidateContext = + .ok aliasFormerCtorCandidate := by + apply AddInductive.buildCandidateExpr_of_whnf_nonForall + · decide + · rfl + +private theorem aliasFormerConstructorListProduced : + AddInductive.CandidateConstructorListProduced + aliasFormerCtorCandidateContext + aliasFormerFamilyListCandidate.constructors := by + exact .cons (by + unfold AddInductive.normalizeCandidateConstructor + simp only [ReaderT.bind, Bind.bind] + simp only [aliasFormerKernelCtor] + rw [aliasFormerCtor_candidateTrace] + rfl) .nil + +private theorem aliasFormerConstructorList_candidateTrace : + AddInductive.normalizeCandidateConstructorList + aliasFormerKernelType.ctors aliasFormerCtorCandidateContext = + .ok aliasFormerFamilyListCandidate.constructors := by + exact aliasFormerConstructorListProduced.normalize + +private theorem aliasFormerFamilyListProduced : + AddInductive.CandidateFamilyListProduced aliasFormerCtorCandidateContext + (.cons aliasFormerFamilyListCandidate.familyType .nil) + aliasFormerNormalizationCandidate.families := by + exact .cons aliasFormerConstructorListProduced .nil + +private theorem aliasFormerFamilyList_candidateTrace : + AddInductive.normalizeCandidateFamilyList + (.cons aliasFormerFamilyListCandidate.familyType .nil) + aliasFormerCtorCandidateContext = + .ok aliasFormerNormalizationCandidate.families := by + exact aliasFormerFamilyListProduced.normalize + +theorem aliasFormerNormalizationCandidate_produced : + AddInductive.buildNormalizationCandidate 0 + [aliasFormerKernelType] 0 false aliasFormerCandidateContext = + .ok aliasFormerNormalizationCandidate := by + unfold AddInductive.buildNormalizationCandidate + rw [aliasFormer_checkInductiveTypes] + simp only [ReaderT.bind, Bind.bind] + rw [show + (withReader (fun _ : AddInductive.Context => + { aliasFormerCandidateContext with lctx := {} }) + (AddInductive.normalizeCandidateFamilyTypeList + [aliasFormerKernelType])) aliasFormerCandidateContext = + .ok (.cons aliasFormerFamilyListCandidate.familyType .nil) by + change AddInductive.normalizeCandidateFamilyTypeList + [aliasFormerKernelType] + { aliasFormerCandidateContext with lctx := {} } = _ + rw [show { aliasFormerCandidateContext with lctx := {} } = + aliasFormerCandidateContext by rfl] + exact aliasFormerFamilyTypeList_candidateTrace] + simp only [Except.bind] + rw [aliasFormer_declareInductiveTypes] + unfold AddInductive.withEnv + change (ReaderT.bind + (AddInductive.checkConstructors #[aliasFormerKernelType] + aliasFormerInductiveStats false) + (fun _ => ReaderT.bind + (AddInductive.normalizeCandidateFamilyList + (.cons aliasFormerFamilyListCandidate.familyType .nil)) + (fun families => pure + (⟨families⟩ : AddInductive.NormalizationCandidate + [aliasFormerKernelType])))) + ({ aliasFormerCandidateContext with + env := aliasFormerCtorNormalizationKernelEnv } : + AddInductive.Context) = _ + rw [show ({ aliasFormerCandidateContext with + env := aliasFormerCtorNormalizationKernelEnv } : + AddInductive.Context) = aliasFormerCtorCandidateContext by rfl] + simp only [ReaderT.bind, Bind.bind] + rw [aliasFormer_checkConstructors] + simp only [Except.bind] + rw [aliasFormerFamilyList_candidateTrace] + rfl + +/-- Erasing the retained trace produces the expected AliasFormer analysis +view at the same checker boundary. -/ +theorem aliasFormerFamily_candidate : + AddInductive.normalizeCandidateExpr aliasFormerInfo.type + aliasFormerCandidateContext = + .ok (.sort (.succ .zero)) := by + apply AddInductive.normalizeCandidateExpr_of_whnf_nonForall + · decide + · simpa [aliasFormerCandidateContext] using + aliasFormerFamily_checkTypeM + · simpa [aliasFormerCandidateContext] using + aliasFormerFamily_whnfM + · rfl + +private def aliasRecFieldFnType : Expr := + .forallE `α (.sort (.succ .zero)) + (.sort (.succ .zero)) .default + +@[simp] private theorem aliasRecFieldFnType_isForall : + aliasRecFieldFnType.isForall = true := rfl + +@[simp] private theorem aliasRecFieldFnType_bindingDomain : + aliasRecFieldFnType.bindingDomain! = + .sort (.succ .zero) := rfl + +@[simp] private theorem aliasRecFieldFnType_instantiatedBody : + aliasRecFieldFnType.bindingBody!.instantiate1 + (.const ``AliasRec []) = + .sort (.succ .zero) := by + simp [aliasRecFieldFnType, Expr.bindingBody!, + Expr.instantiate1_eq, + Expr.instantiate1'] + +@[simp] private theorem aliasRecFamily_notEagerReduce : + (Expr.const ``AliasRec []).isAppOfArity ``eagerReduce 2 = + false := rfl + +private def aliasRecFieldFnState (state : TypeChecker.State) : + TypeChecker.State := + { state with + inferTypeC := state.inferTypeC.insert + (.const ``RecAlias [.succ .zero]) aliasRecFieldFnType } + +private def aliasRecFieldArgState (state : TypeChecker.State) : + TypeChecker.State := + { state with + inferTypeC := state.inferTypeC.insert + (.const ``AliasRec []) (.sort (.succ .zero)) } + +private def aliasRecFieldResultState (state : TypeChecker.State) : + TypeChecker.State := + { state with + inferTypeC := state.inferTypeC.insert + aliasRecFieldKernelExpr (.sort (.succ .zero)) } + +@[simp] private theorem aliasRecFieldFnCache_miss : + (({} : Ix.Theory.Named.InferCache).insert + (Expr.const ``RecAlias [.succ .zero]) aliasRecFieldFnType)[ + Expr.const ``AliasRec []]? = none := by + rw [Std.HashMap.getElem?_insert] + have h : + (Expr.const ``RecAlias [.succ .zero] == + Expr.const ``AliasRec []) = false := by + change Expr.eqv + (Expr.const ``RecAlias [.succ .zero]) + (Expr.const ``AliasRec []) = false + rw [Expr.eqv_eq] + rfl + rw [h] + exact Std.HashMap.getElem?_empty + +@[simp] private theorem aliasRecFieldFnState_cache_miss : + (aliasRecFieldFnState {}).inferTypeC[ + Expr.const ``AliasRec []]? = none := by + change + (({} : Ix.Theory.Named.InferCache).insert + (Expr.const ``RecAlias [.succ .zero]) aliasRecFieldFnType)[ + Expr.const ``AliasRec []]? = none + exact aliasRecFieldFnCache_miss + +private theorem isDefEqSort + (context : TypeChecker.Context) + (initial : TypeChecker.State) : + ∃ state : TypeChecker.State, + TypeChecker.Inner.isDefEq + (.sort (.succ .zero)) (.sort (.succ .zero)) + (TypeChecker.Methods.withFuel 9998) context initial = + .ok (true, state) := by + refine ⟨initial, ?_⟩ + unfold TypeChecker.Inner.isDefEq + rw [if_pos (Expr.eqv_refl _)] + rfl + +private theorem inferTypeRecAliasInitial : + TypeChecker.Inner.inferType' + (.const ``RecAlias [.succ .zero]) false + (TypeChecker.Methods.withFuel 9998) + aliasRecNormalizationRawContext ({} : TypeChecker.State) = + .ok (aliasRecFieldFnType, aliasRecFieldFnState {}) := by + unfold TypeChecker.Inner.inferType' + simp [aliasRecFieldFnType, aliasRecFieldFnState, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +private theorem inferTypeAliasRecAfterRecAlias : + TypeChecker.Inner.inferType' + (.const ``AliasRec []) false + (TypeChecker.Methods.withFuel 9998) + aliasRecNormalizationRawContext (aliasRecFieldFnState {}) = + .ok (.sort (.succ .zero), + aliasRecFieldArgState (aliasRecFieldFnState {})) := by + unfold TypeChecker.Inner.inferType' + simp [aliasRecFieldArgState, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + +/-- The exact full checker run for the raw `RecAlias AliasRec` constructor +field in the post-family environment. -/ +theorem aliasRecField_checkType : + ∃ state : TypeChecker.State, + TypeChecker.Inner.inferType aliasRecFieldKernelExpr false + (TypeChecker.Methods.withFuel 9999) + aliasRecNormalizationRawContext ({} : TypeChecker.State) = + .ok (.sort (.succ .zero), state) := by + change ∃ state : TypeChecker.State, + TypeChecker.Inner.inferType' aliasRecFieldKernelExpr false + (TypeChecker.Methods.withFuel 9998) + aliasRecNormalizationRawContext ({} : TypeChecker.State) = + .ok (.sort (.succ .zero), state) + rw [aliasRecFieldKernelExpr_eq] + unfold TypeChecker.Inner.inferType' + simp only [aliasRecField_noLooseBVars, Bool.false_eq_true, if_false, cond, normalizationRecMGet, + Std.HashMap.getElem?_empty, normalizationRecMBind] + rw [inferTypeRecAliasInitial] + simp only + [TypeChecker.Inner.ensureForallCore, + aliasRecFieldFnType_isForall, ↓reduceIte, normalizationRecMPure] + rw [inferTypeAliasRecAfterRecAlias] + obtain ⟨eqState, heq⟩ := + isDefEqSort aliasRecNormalizationRawContext + (aliasRecFieldArgState (aliasRecFieldFnState {})) + dsimp only + rw [if_neg (show ¬((Expr.const ``AliasRec []).isAppOfArity + `eagerReduce 2 = true) from by + simp [aliasRecFamily_notEagerReduce])] + simp only [aliasRecFieldFnType_bindingDomain, + normalizationRecMBind] + rw [heq] + rw [aliasRecFieldFnType_instantiatedBody] + refine ⟨aliasRecFieldResultState eqState, ?_⟩ + simp [aliasRecFieldResultState, aliasRecFieldKernelExpr_eq, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + +/-- The paired full-check/WHNF interpretation of the retained AliasFormer +family node. Both semantic runs are obtained from the candidate's exact +observations in one verified context. -/ +private def aliasFormerFamilyCandidateNodeRun : + TypeChecker.CandidateNodeRun typeFamilyAliasEnv [] [] + aliasFormerCandidateContext aliasFormerInfo.type + (.sort (.succ (.succ .zero))) (.sort (.succ .zero)) + aliasFormerRawType.type aliasFormerViewType.type + (.sort (.succ (.succ .zero))) := by + exact TypeChecker.CandidateNodeRun.ofCandidate + aliasFormerCandidateContext aliasFormerInfo.type + (.sort (.succ (.succ .zero))) (.sort (.succ .zero)) + aliasFormerFamilyCheckTypeStep_valid + aliasFormerFamilyCandidateStep_valid + aliasFormerNormalizationContext (by rfl) + rfl rfl rfl TypeChecker.VState.WF.empty + (.const rfl rfl rfl) (.sort rfl) + (by + have hs : TrExprS typeFamilyAliasEnv [] [] + (.sort (.succ .zero)) (.sort (.succ .zero)) := .sort rfl + exact hs) + 10000 9999 (by rfl) (by rfl) + +/-- Verified family-result normalization leaf for AliasFormer. -/ +def aliasFormerFamilyWhnfRun : + TypeChecker.WhnfRun typeFamilyAliasEnv [] [] + aliasFormerInfo.type (.sort (.succ .zero)) + aliasFormerRawType.type aliasFormerViewType.type := + aliasFormerFamilyCandidateNodeRun.whnf + +/-- Verified full-check certificate for the raw AliasFormer family type. -/ +def aliasFormerFamilyCheckTypeRun : + TypeChecker.CheckTypeRun typeFamilyAliasEnv [] [] + aliasFormerInfo.type (.sort (.succ (.succ .zero))) + aliasFormerRawType.type (.sort (.succ (.succ .zero))) := + aliasFormerFamilyCandidateNodeRun.check + +/-- Recursive semantic interpretation of the exact source-indexed candidate +trace. This terminal fixture is the base case used by the generic Pi +interpreter for larger metadata. -/ +private theorem aliasFormerFamilyCandidateRun : + TypeChecker.CandidateExprRun typeFamilyAliasEnv [] + aliasFormerFamilyCandidate.trace [] + aliasFormerRawType.type aliasFormerViewType.type + (.sort (.succ (.succ .zero))) := + .terminal aliasFormerFamilyCandidateNodeRun + +private theorem aliasFormerCandidatePrefix_ne : + aliasFormerCandidateContext.ngen.namePrefix ≠ + (({} : TypeChecker.VState).ngen).namePrefix := by + decide + +/-- The generic root constructor aligns the actual candidate context with the +verified AliasFormer environment and supplies the empty-state certificate. -/ +private def aliasFormerCandidateContextRun : + TypeChecker.CandidateContextRun aliasFormerCandidateContext := + TypeChecker.CandidateContextRun.root aliasFormerNormalizationVEnvs_wf + rfl aliasFormerCandidatePrefix_ne + +/-- The retained AliasFormer full check now selects its own Theory source and +output translations; no expression translation is supplied by the fixture. -/ +theorem aliasFormerFamily_candidateRun_exists : + ∃ source' view' inferred', + aliasFormerCandidateContextRun.context.TrExprS + aliasFormerInfo.type source' ∧ + Nonempty (TypeChecker.CandidateExprRun + aliasFormerCandidateContextRun.context.venv + aliasFormerCandidateContextRun.context.lparams + aliasFormerFamilyCandidate.trace + aliasFormerCandidateContextRun.context.vlctx + source' view' inferred') := by + apply TypeChecker.CandidateExprRun.exists_ofCandidateFVars + aliasFormerFamilyCandidate.trace aliasFormerCandidateContextRun + (whnfFuel := 9999) + · change ∀ u ∈ ([] : List Level), u.hasMVar' = false + simp + · rfl + +/-- The generic interpreter retains the strict translation of the raw +candidate endpoint. -/ +theorem aliasFormerFamily_candidateSource_tr : + TrExprS typeFamilyAliasEnv [] [] aliasFormerInfo.type + aliasFormerRawType.type := + aliasFormerFamilyCandidateRun.source_tr + +/-- The reconstructed candidate endpoint is also tied back to the concrete +kernel WHNF result, closing the source/view translation pair. -/ +theorem aliasFormerFamily_candidateView_tr : + TrExpr typeFamilyAliasEnv [] [] (.sort (.succ .zero)) + aliasFormerViewType.type := by + simpa [aliasFormerFamilyCandidate, + AddInductive.CandidateExprTrace.view] using + aliasFormerFamilyCandidateRun.view_tr + +private def aliasFormerPreFamilyStage : + TypeChecker.CandidateSemanticStage aliasFormerCandidateContext + typeFamilyAliasEnv [] where + contextRun := aliasFormerCandidateContextRun + venv_eq := rfl + lparams_eq := rfl + vlctx_eq := rfl + +private def aliasFormerFamilyValidationRun : + AddInductive.CandidateExprTrace.FamilyValidationRun + aliasFormerKernelType aliasFormerFamilyCandidate.trace where + nparams := 0 + resultLevel := .succ .zero + stats := aliasFormerInductiveStats + stats_eq := rfl + terminal_eq := rfl + run := aliasFormer_checkInductiveTypes + +private def aliasFormerFamilyStage : + VInductDecl.CandidateFamilyStagedInput aliasFormerCandidateContext + aliasFormerCtorCandidateContext typeFamilyAliasEnv [] + aliasFormerFamilyListCandidate.familyType aliasFormerRawType + aliasFormerPreFamilyStage where + name_eq := rfl + uvars_eq := rfl + type := { + context_eq := rfl + source_tr := aliasFormerFamily_candidateSource_tr + whnfFuel := 9999 + whnfDepth := rfl } + validation := aliasFormerFamilyValidationRun + typeEnv := aliasFormerTypeEnv + addInduct := aliasFormerCtorNormalizationAddType + projectionReady := ProjectionReady.of_no_ctorInfo <| by + intro name _info h + change aliasFormerTypeMap.find?' name = some (.ctorInfo _info) at h + rw [aliasFormerTypeMap_wf.find?'_eq_find?] at h + simp only [aliasFormerTypeMap, typeFamilyAliasMap_wf.find?_insert] at h + simp only [typeFamilyAliasMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] at h + by_cases hAliasFormer : ``AliasFormer = name + · subst name + simp [aliasFormerInfo] at h + · by_cases hTypeFamilyAlias : ``TypeFamilyAlias = name + · subst name + simp [hAliasFormer, typeFamilyAliasInfo] at h + · simp [hAliasFormer, hTypeFamilyAlias, SMap.find?] at h + structureEtaReady := StructureEtaReady.of_no_ctorInfo <| by + intro name _info h + change aliasFormerTypeMap.find?' name = some (.ctorInfo _info) at h + rw [aliasFormerTypeMap_wf.find?'_eq_find?] at h + simp only [aliasFormerTypeMap, typeFamilyAliasMap_wf.find?_insert] at h + simp only [typeFamilyAliasMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] at h + by_cases hAliasFormer : ``AliasFormer = name + · subst name + simp [aliasFormerInfo] at h + · by_cases hTypeFamilyAlias : ``TypeFamilyAlias = name + · subst name + simp [hAliasFormer, typeFamilyAliasInfo] at h + · simp [hAliasFormer, hTypeFamilyAlias, SMap.find?] at h + family_lctx_eq := rfl + constructorContext_eq := rfl + quotInit_eq := rfl + name_not_reflected := by decide + name_not_primitive := by + simp [aliasFormerRawType, Kernel.Environment.primitives, + NameSet.ofList] + simp +decide [NameSet.contains] + +private def aliasFormerCtorCandidateContextRun : + TypeChecker.CandidateContextRun aliasFormerCtorCandidateContext := + aliasFormerFamilyStage.postContextRun + +/-- Family endpoint certificate used by the singleton list assembler. -/ +private def aliasFormerFamilySemanticRootRun : + TypeChecker.CandidateExprSemanticRootRun typeFamilyAliasEnv [] + aliasFormerFamilyCandidate aliasFormerRawType.type + where + contextRun := aliasFormerCandidateContextRun + venv_eq := rfl + lparams_eq := rfl + vlctx_eq := rfl + source_tr := aliasFormerFamily_candidateSource_tr + whnfFuel := 9999 + whnfDepth := rfl + view := aliasFormerViewType.type + recursive := ⟨.sort (.succ (.succ .zero)), + aliasFormerFamilyCandidateRun⟩ + +private def aliasFormerFamilyRootRun : + TypeChecker.CandidateExprRootRun typeFamilyAliasEnv [] + aliasFormerFamilyCandidate aliasFormerRawType.type + aliasFormerViewType.type := + aliasFormerFamilySemanticRootRun.root + +/-- Verified full-check certificate for the actual AliasFormer constructor +type in the post-family environment. -/ +def aliasFormerCtorCheckTypeRun : + TypeChecker.CheckTypeRun aliasFormerTypeEnv [] [] + aliasFormerMkInfo.type (.const ``TypeFamilyAlias []) + aliasFormerRawType.ctors[0].type + (.const ``TypeFamilyAlias []) := by + exact TypeChecker.CheckTypeRun.ofCandidateStep + aliasFormerCtorCheckTypeStep aliasFormerCtorCheckTypeStep_valid + aliasFormerCtorCandidateContextRun.context + aliasFormerCtorCandidateContextRun.context_eq + rfl rfl rfl aliasFormerCtorCandidateContextRun.state_wf + (.const rfl rfl rfl) (.const rfl rfl rfl) + 10000 (by rfl) + +/-- Paired full-check/WHNF interpretation of the retained constructor leaf. +This is the post-family terminal used by the generic constructor-spine +assembler. -/ +private def aliasFormerCtorCandidateNodeRun : + TypeChecker.CandidateNodeRun aliasFormerTypeEnv [] [] + aliasFormerCtorCandidateContext aliasFormerMkInfo.type + (.const ``TypeFamilyAlias []) (.const ``AliasFormer []) + aliasFormerRawType.ctors[0].type + aliasFormerRawType.ctors[0].type + (.const ``TypeFamilyAlias []) := by + exact TypeChecker.CandidateNodeRun.ofCandidate + aliasFormerCtorCandidateContext aliasFormerMkInfo.type + (.const ``TypeFamilyAlias []) (.const ``AliasFormer []) + aliasFormerCtorCheckTypeStep_valid + aliasFormerCtorCandidateStep_valid + aliasFormerCtorCandidateContextRun.context + aliasFormerCtorCandidateContextRun.context_eq + rfl rfl rfl aliasFormerCtorCandidateContextRun.state_wf + aliasFormerCtorCheckTypeRun.expr_tr (.const rfl rfl rfl) + aliasFormerCtorCheckTypeRun.expr_tr + 10000 9999 (by rfl) (by rfl) + +private theorem aliasFormerCtorCandidateRun : + TypeChecker.CandidateExprRun aliasFormerTypeEnv [] + aliasFormerCtorCandidate.trace [] + aliasFormerRawType.ctors[0].type + aliasFormerRawType.ctors[0].type + (.const ``TypeFamilyAlias []) := + .terminal aliasFormerCtorCandidateNodeRun + +/-- Constructor endpoint certificate in the exact post-family context. -/ +private def aliasFormerCtorSemanticRootRun : + TypeChecker.CandidateExprSemanticRootRun aliasFormerTypeEnv [] + aliasFormerCtorCandidate aliasFormerRawType.ctors[0].type + where + contextRun := aliasFormerCtorCandidateContextRun + venv_eq := rfl + lparams_eq := rfl + vlctx_eq := rfl + source_tr := aliasFormerCtorCheckTypeRun.expr_tr + whnfFuel := 9999 + whnfDepth := rfl + view := aliasFormerRawType.ctors[0].type + recursive := ⟨.const ``TypeFamilyAlias [], aliasFormerCtorCandidateRun⟩ + +private def aliasFormerCtorRootRun : + TypeChecker.CandidateExprRootRun aliasFormerTypeEnv [] + aliasFormerCtorCandidate aliasFormerRawType.ctors[0].type + aliasFormerRawType.ctors[0].type := + aliasFormerCtorSemanticRootRun.root + +/-- The actual AliasFormer constructor type is typed by the verified full +checker in the post-family environment. -/ +theorem aliasFormerCtor_hasType_checked : + aliasFormerTypeEnv.HasType 0 [] + aliasFormerRawType.ctors[0].type + (.const ``TypeFamilyAlias []) := + aliasFormerCtorCheckTypeRun.hasType + +/-- The raw AliasFormer family is a Theory type because the verified checker +actually accepted it and inferred a sort. -/ +theorem aliasFormerFamily_isType_checked : + typeFamilyAliasEnv.IsType 0 [] aliasFormerRawType.type := + aliasFormerFamilyCheckTypeRun.isType + +private theorem aliasFormerFamilySpineRun : + TypeChecker.CandidateExprSpineRun typeFamilyAliasEnv [] + aliasFormerFamilyCandidate aliasFormerRawType.type + aliasFormerViewType.type := + aliasFormerFamilySemanticRootRun.spine rfl + +private theorem aliasFormerCtorSpineRun : + TypeChecker.CandidateExprSpineRun aliasFormerTypeEnv [] + aliasFormerCtorCandidate aliasFormerRawType.ctors[0].type + aliasFormerRawType.ctors[0].type := + aliasFormerCtorSemanticRootRun.spine rfl + +/-- Verified delta-normalization leaf for `RecAlias.{1}`. -/ +def recAliasWhnfRun : + TypeChecker.WhnfRun aliasRecTypeEnv [] [] + (.const ``RecAlias [.succ .zero]) + recAliasWhnfKernelExpr + (.const ``RecAlias [.succ .zero]) + (.lam (.sort (.succ .zero)) (.bvar 0)) where + context := aliasRecNormalizationContext + venv_eq := rfl + lparams_eq := rfl + vlctx_eq := rfl + state_wf := TypeChecker.VState.WF.empty + lhs_tr := .const rfl rfl rfl + rhs_tr := by + have hs : TrExprS aliasRecTypeEnv [] [] + recAliasWhnfKernelExpr + (.lam (.sort (.succ .zero)) (.bvar 0)) := by + rw [recAliasWhnfKernelExpr_eq] + exact .lam + ⟨_, VEnv.HasType.sort (by decide)⟩ + (.sort rfl) (.bvar rfl) + exact hs + recursionFuel := 9999 + run_eq := by + simpa [aliasRecNormalizationContext, TypeChecker.VContext.mk', + TypeChecker.VContext.mk1, TypeChecker.MLCtx.lctx, + aliasRecNormalizationRawContext] using + recAlias_whnf + +private theorem recAliasConst_hasType : + aliasRecTypeEnv.HasType 0 [] + (.const ``RecAlias [.succ .zero]) + (.forallE (.sort (.succ .zero)) (.sort (.succ .zero))) := by + have hAlias : aliasRecTypeEnv.constants ``RecAlias = + some (vconst(type_of% @RecAlias)) := rfl + type_tac + +private theorem aliasRecConst_hasType : + aliasRecTypeEnv.HasType 0 [] + (.const ``AliasRec []) (.sort (.succ .zero)) := by + exact .constDF + (VEnv.addConst_self (show + recAliasEnv.addConst aliasRecRawType.name + aliasRecRawType.toVConstant = some aliasRecTypeEnv from rfl)) + (fun _ h => nomatch h) (fun _ h => nomatch h) rfl .nil + +/-- Verified full-check certificate for the actual raw recursive field in the +exact environment produced by inserting `AliasRec`. -/ +def aliasRecFieldCheckTypeRun : + TypeChecker.CheckTypeRun aliasRecTypeEnv [] [] + aliasRecFieldKernelExpr (.sort (.succ .zero)) + aliasRecRawField (.sort (.succ .zero)) where + context := aliasRecNormalizationContext + venv_eq := rfl + lparams_eq := rfl + vlctx_eq := rfl + state_wf := TypeChecker.VState.WF.empty + expr_tr := .app recAliasConst_hasType aliasRecConst_hasType + (.const rfl rfl rfl) (.const rfl rfl rfl) + inferred_tr := .sort rfl + recursionFuel := 9999 + run_eq := by + simpa [aliasRecNormalizationContext, TypeChecker.VContext.mk', + TypeChecker.VContext.mk1, TypeChecker.MLCtx.lctx, + aliasRecNormalizationRawContext] using + aliasRecField_checkType + +/-- The raw recursive field is typed by an exact full checker execution in +the post-family environment. -/ +theorem aliasRecField_hasType_checked : + aliasRecTypeEnv.HasType 0 [] + aliasRecRawField (.sort (.succ .zero)) := + aliasRecFieldCheckTypeRun.hasType + +private theorem aliasRecFieldEvidenceBase : + TypeChecker.DefEqEvidence aliasRecTypeEnv 0 [] + aliasRecRawField (.const ``AliasRec []) (.sort (.succ .zero)) := by + exact .trans + (.app + (.whnf recAliasWhnfRun recAliasConst_hasType) + (.refl aliasRecConst_hasType)) + (.beta (VEnv.HasType.bvar .zero) aliasRecConst_hasType) + +private theorem aliasRecFieldEvidence : + TypeChecker.DefEqEvidence aliasRecTypeEnv 0 [] + aliasRecRawField (.const ``AliasRec []) (.sort (.succ .zero)) := + .trans (.refl aliasRecField_hasType_checked) + aliasRecFieldEvidenceBase + +private theorem aliasRecCtorEvidence : + ∃ A, TypeChecker.DefEqEvidence aliasRecTypeEnv 0 [] + aliasRecRawType.ctors[0].type aliasRecViewCtor.type A := by + exact ⟨.sort (.imax (.succ .zero) (.succ .zero)), + .forallE aliasRecFieldEvidence + (.refl (aliasRecResult_hasType rfl))⟩ + +private def aliasFormerCandidateConstructorSemanticRun : + VInductDecl.CandidateConstructorSemanticRun aliasFormerTypeEnv [] + aliasFormerConstructorCandidate aliasFormerRawType.ctors[0] where + name_eq := rfl + uvars_eq := rfl + type := aliasFormerCtorSemanticRootRun + +private def aliasFormerCandidateConstructorRun : + VInductDecl.CandidateConstructorRun aliasFormerTypeEnv [] + aliasFormerConstructorCandidate aliasFormerRawType.ctors[0] := + aliasFormerCandidateConstructorSemanticRun.root + +private def aliasFormerCandidateConstructorSemanticListRun : + VInductDecl.CandidateConstructorSemanticListRun aliasFormerTypeEnv [] + aliasFormerFamilyListCandidate.constructors + aliasFormerRawType.ctors := by + exact .cons aliasFormerCandidateConstructorSemanticRun .nil + +private def aliasFormerCandidateConstructorListRun : + VInductDecl.CandidateConstructorListRun aliasFormerTypeEnv [] + aliasFormerFamilyListCandidate.constructors + aliasFormerRawType.ctors := + aliasFormerCandidateConstructorSemanticListRun.roots + +private def aliasFormerCandidateFamilySemanticRun : + VInductDecl.CandidateFamilySemanticRun typeFamilyAliasEnv [] + aliasFormerFamilyListCandidate aliasFormerRawType where + name_eq := rfl + uvars_eq := rfl + type := aliasFormerFamilySemanticRootRun + typeEnv := aliasFormerTypeEnv + addType := rfl + constructors := aliasFormerCandidateConstructorSemanticListRun + +private def aliasFormerCandidateFamilyRun : + VInductDecl.CandidateFamilyRun typeFamilyAliasEnv [] + aliasFormerFamilyListCandidate aliasFormerRawType := + aliasFormerCandidateFamilySemanticRun.root + +private def aliasFormerStagedUniverseInput : + VInductDecl.StagedNormalizationCandidateUniverseInput + aliasFormerCandidateContext aliasFormerCtorCandidateContext + typeFamilyAliasEnv [] aliasFormerNormalizationCandidate + aliasFormerRawDecl where + staged := { + raw := aliasFormerRawType + raw_types_eq := rfl + declaration_uvars_eq := rfl + preFamily := aliasFormerPreFamilyStage + family := aliasFormerFamilyStage + validation_nparams_eq := rfl + constructorValidation := + AddInductive.ConstructorValidationRun.of_run aliasFormer_checkConstructors + constructors := .cons { + name_eq := rfl + uvars_eq := rfl + type := { + context_eq := rfl + source_tr := aliasFormerCtorCheckTypeRun.expr_tr + whnfFuel := 9999 + whnfDepth := rfl } } .nil + familyTypesProduced := aliasFormerFamilyTypeListProduced + familiesProduced := aliasFormerFamilyListProduced } + universeRun := aliasFormer_checkConstructorUniverseSemantics + +private theorem aliasFormerConstructorValidationContext_eq : + { aliasFormerNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext with + env := aliasFormerCtorCandidateContext.env } = + aliasFormerCtorCandidateContext := rfl + +private theorem aliasFormerCtorCandidateContext_empty : + aliasFormerCtorCandidateContext.withEmptyLocalContext = + aliasFormerCtorCandidateContext := rfl + +set_option warn.sorry false in +theorem aliasFormerAlignmentRun : + aliasFormerStagedUniverseInput.staged.constructorValidation.trace.checkCandidateAlignment + aliasFormerNormalizationCandidate.families.singleton.constructors + { aliasFormerNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext with + env := aliasFormerCtorCandidateContext.env } = .ok () := by + -- Tier V (Spec-19B, v4.33 reconciliation repair debt): the premerge proof + -- stepped `ConstructorCandidateAlignmentTrace.build` with `rw [build.eq_def]`, + -- which the v4.33 elaborator no longer matches (and eq_def-in-simp loops). + -- The statement is an exact closed checker run and remains true; the + -- stepping proof needs a rework against the new equation-lemma shapes. + sorry + +private def aliasFormerStagedPostFamilyInput : + VInductDecl.StagedNormalizationCandidatePostFamilyInput + aliasFormerCandidateContext aliasFormerCtorCandidateContext + typeFamilyAliasEnv [] aliasFormerNormalizationCandidate + aliasFormerRawDecl := + VInductDecl.StagedNormalizationCandidatePostFamilyInput.ofRun + aliasFormerStagedUniverseInput aliasFormerAlignmentRun + +private theorem aliasFormerPreFamilySafetyRun : + AddInductive.checkConstructorPreFamilySafety + aliasFormerStagedUniverseInput.staged.family.validation.stats + aliasFormerNormalizationCandidate.families.singleton.familyType.type.view + aliasFormerNormalizationCandidate.families.singleton.constructors + aliasFormerNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext = + .ok () := by + change AddInductive.checkConstructorPreFamilySafety + aliasFormerInductiveStats (.sort (.succ .zero)) + (.cons aliasFormerConstructorCandidate .nil) + aliasFormerCandidateContext = .ok () + let sortStep : AddInductive.CandidateCheckTypeStep := + ⟨aliasFormerCandidateContext, .sort (.succ .zero), + .sort (.succ (.succ .zero))⟩ + have sortRun : sortStep.Valid := by + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' (.sort (.succ .zero)) false + (TypeChecker.Methods.withFuel 9999) + aliasFormerCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = _ + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', + annotatedPi_checkLevelSuccZero, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + rfl + have sortHasMVar : (Expr.sort (.succ .zero)).hasMVar = false := by + simp [Expr.hasMVar, annotatedPiSortOne_data_hasExprMVar_false, + annotatedPiSortOne_data_hasLevelMVar_false] + obtain ⟨sortChecked, sortCheckedRun⟩ := + AddInductive.checkConstructorAlignedExpr.exists_of_run + (context := aliasFormerCandidateContext) + (source := .sort (.succ .zero)) + (inferred := .sort (.succ (.succ .zero))) rfl sortHasMVar sortRun + have aliasArgs : + (Expr.const ``AliasFormer []).getAppArgs.toList.drop 0 = [] := by + rfl + have paramsSize : aliasFormerInductiveStats.params.size = 0 := rfl + have targetArgs : + (Expr.const ``AliasFormer []).getAppArgs.toList.drop + aliasFormerInductiveStats.params.size = [] := by + rw [paramsSize] + exact aliasArgs + have inductiveFuel : + aliasFormerCandidateContext.fuel.inductiveFuel = 1000 := rfl + unfold AddInductive.checkConstructorPreFamilySafety + have parametersRun : + AddInductive.instantiateFamilyParameters (.sort (.succ .zero)) + aliasFormerInductiveStats.params.toList = .ok (.sort (.succ .zero)) := by + rfl + have sortTerminal : (Expr.sort (.succ .zero)).isForall = false := rfl + let spineTrace : AddInductive.ConstructorPreFamilyIndexSpineTrace + aliasFormerCandidateContext (.sort (.succ .zero)) [] := + .nil aliasFormerCandidateContext (.sort (.succ .zero)) sortChecked + sortTerminal + have spineRun : + AddInductive.ConstructorPreFamilyIndexSpineTrace.build + aliasFormerCandidateContext (.sort (.succ .zero)) [] = + .ok spineTrace := by + unfold AddInductive.ConstructorPreFamilyIndexSpineTrace.build + rw [dif_pos sortTerminal, sortCheckedRun] + rfl + obtain ⟨targetSpineTrace, targetSpineRun⟩ : + ∃ targetSpineTrace : AddInductive.ConstructorPreFamilyIndexSpineTrace + aliasFormerCandidateContext (.sort (.succ .zero)) + ((Expr.const ``AliasFormer []).getAppArgs.toList.drop + aliasFormerInductiveStats.params.size), + AddInductive.ConstructorPreFamilyIndexSpineTrace.build + aliasFormerCandidateContext (.sort (.succ .zero)) + ((Expr.const ``AliasFormer []).getAppArgs.toList.drop + aliasFormerInductiveStats.params.size) = + .ok targetSpineTrace := by + rw [targetArgs] + exact ⟨spineTrace, spineRun⟩ + have valid : AddInductive.isValidIndAppIdx aliasFormerInductiveStats + (.const ``AliasFormer []) 0 = true := + aliasFormerCtor_isValidIndAppIdx + have independent : AddInductive.constructorIndependentOf + (.const ``AliasFormer []) [] = true := by + rfl + obtain ⟨viewTrace, viewRun⟩ : ∃ viewTrace, + AddInductive.ConstructorPreFamilyViewTrace.build + aliasFormerInductiveStats 0 (.sort (.succ .zero)) + aliasFormerCandidateContext (.const ``AliasFormer []) 0 [] false + aliasFormerCandidateContext.fuel.inductiveFuel = .ok viewTrace := by + rw [inductiveFuel] + rw [show 1000 = 999 + 1 by rfl] + simp only [AddInductive.ConstructorPreFamilyViewTrace.build] + rw [dif_pos valid, dif_pos independent] + rw [targetSpineRun] + exact ⟨_, rfl⟩ + obtain ⟨listTrace, listRun⟩ : ∃ listTrace, + AddInductive.ConstructorPreFamilyListTrace.build + aliasFormerInductiveStats 0 (.sort (.succ .zero)) + aliasFormerCandidateContext + (.cons aliasFormerConstructorCandidate .nil) = .ok listTrace := by + refine ⟨.cons viewTrace .nil, ?_⟩ + simp [AddInductive.ConstructorPreFamilyListTrace.build, + aliasFormerConstructorCandidate, aliasFormerCtorCandidate, + AddInductive.CandidateExpr.view, AddInductive.CandidateExprTrace.view, + viewRun, Bind.bind, Except.bind, Except.pure, Pure.pure] + have translationUnique : + (AddInductive.theoryTranslationUnique (.sort (.succ .zero)) && + (AddInductive.CandidateList.cons aliasFormerConstructorCandidate + (AddInductive.CandidateList.nil : AddInductive.CandidateList + AddInductive.CandidateConstructor [])).viewTranslationUnique) = + true := by + rfl + rw [if_pos translationUnique] + simp [parametersRun, listRun, Bind.bind, Except.bind, + Except.pure, Pure.pure] + +private def aliasFormerStagedPreFamilyInput : + VInductDecl.StagedNormalizationCandidatePreFamilyInput + aliasFormerCandidateContext aliasFormerCtorCandidateContext + typeFamilyAliasEnv [] aliasFormerNormalizationCandidate + aliasFormerRawDecl := + VInductDecl.StagedNormalizationCandidatePreFamilyInput.ofRun + aliasFormerStagedPostFamilyInput aliasFormerPreFamilySafetyRun + +/-- The exact family/constructor producer traversals and verified translations +automatically determine a complete retained AliasFormer hierarchy. -/ +theorem aliasFormerProducedSemanticHierarchy_exists : + Nonempty (VInductDecl.ProducedNormalizationCandidateSemanticRun + aliasFormerCandidateContext aliasFormerCtorCandidateContext + typeFamilyAliasEnv [] aliasFormerNormalizationCandidate + aliasFormerRawDecl) := + aliasFormerStagedUniverseInput.exists + +/-- The retained validator telescope and independently produced candidate +telescope align positionally and admit the complete post-family semantic +interpretation for `AliasFormer`. -/ +theorem aliasFormerProducedPostFamilySemantic_exists : + Nonempty (VInductDecl.ProducedNormalizationCandidatePostFamilySemanticRun + aliasFormerStagedPostFamilyInput) := + aliasFormerStagedPostFamilyInput.exists + +/-- AliasFormer's analyzer-owned constructor candidate passes the executable +pre-family suffix/dependency gate and admits the exact family-free replay. -/ +theorem aliasFormerProducedPreFamilySemantic_exists : + Nonempty (VInductDecl.ProducedNormalizationCandidatePreFamilySemanticRun + aliasFormerStagedPreFamilyInput) := + aliasFormerStagedPreFamilyInput.exists + +def aliasFormerNormalizationCandidateRun : + VInductDecl.NormalizationCandidateRun typeFamilyAliasEnv [] + aliasFormerNormalizationCandidate aliasFormerRawDecl where + raw := aliasFormerRawType + raw_types_eq := rfl + uvars_eq := rfl + family := aliasFormerCandidateFamilyRun + +example : aliasFormerNormalizationCandidateRun.viewDecl = + aliasFormerViewDecl := rfl + +example : aliasFormerNormalizationCandidateRun.normalization.accepted = + true := rfl + +theorem aliasFormerCandidateNormalization_eq : + aliasFormerNormalizationCandidateRun.normalization = + aliasFormerNormalization := rfl + +private def aliasFormerTruncatedViewType : VInductiveType := + { aliasFormerViewType with ctors := [] } + +private def aliasFormerTruncatedViewDecl : VInductDecl := + { aliasFormerViewDecl with types := [aliasFormerTruncatedViewType] } + +/-- A shorter view cannot cross even the computational normalization-shape +gate, so it cannot reach dependent analysis or transaction construction. -/ +theorem aliasFormerTruncatedView_rejected : + VInductDecl.normalization? aliasFormerRawDecl + aliasFormerTruncatedViewDecl = none := rfl + +/-- Complete checker-produced semantic normalization certificate for +AliasFormer. -/ +def aliasFormerNormalizationRun : + VInductDecl.NormalizationRun aliasFormerNormalization + typeFamilyAliasEnv := by + simpa only [aliasFormerCandidateNormalization_eq] using + aliasFormerNormalizationCandidateRun.normalizationRun + +theorem aliasFormerNormalization_wf_checked : + aliasFormerNormalization.WF typeFamilyAliasEnv := + aliasFormerNormalizationRun.wf + +/-- Complete checker-produced semantic normalization certificate for +AliasRec. The field comparison is assembled from verified WHNF, application, +beta, and outer-forall congruence. -/ +def aliasRecNormalizationRun : + VInductDecl.NormalizationRun aliasRecNormalization recAliasEnv := by + refine { + raw := aliasRecRawType + view := aliasRecViewType + source_types_eq := rfl + view_types_eq := rfl + family := ?_ + typeEnv := aliasRecTypeEnv + addType := rfl + constructors := ?_ } + · exact ⟨.sort (.succ (.succ .zero)), + .refl (VEnv.HasType.sort (by decide))⟩ + · exact .cons aliasRecCtorEvidence .nil + +theorem aliasRecNormalization_wf_checked : + aliasRecNormalization.WF recAliasEnv := + aliasRecNormalizationRun.wf + +/-- The paired AliasFormer block with its normalization component supplied by +the checked WHNF path. The view's structural semantics remain the ordinary +Theory `Checked.WF` proof. -/ +theorem aliasFormerBlock_wf_checked : + aliasFormerBlock.WF typeFamilyAliasEnv := by + refine ⟨aliasFormerNormalization_wf_checked, ?_⟩ + change aliasFormerViewChecked.WF typeFamilyAliasEnv + exact aliasFormerViewChecked.wf_of_decl aliasFormerViewDecl_wf + +private theorem aliasFormerFamily_defeq_checked : + typeFamilyAliasEnv.IsDefEq 0 [] + (.const ``TypeFamilyAlias []) (.sort (.succ .zero)) + (.sort (.succ (.succ .zero))) := + aliasFormerFamilyWhnfRun.isDefEq + aliasFormerFamilyCheckTypeRun.hasType + +/-- Combining the constructor's exact full-check result with the verified +family-alias WHNF fixes its Theory sort. -/ +theorem aliasFormerCtor_hasSort_checked : + aliasFormerTypeEnv.HasType 0 [] + aliasFormerRawType.ctors[0].type (.sort (.succ .zero)) := by + have halias := + aliasFormerFamily_defeq_checked.mono (VEnv.addConst_le (show + typeFamilyAliasEnv.addConst aliasFormerRawType.name + aliasFormerRawType.toVConstant = some aliasFormerTypeEnv from rfl)) + exact halias.defeq aliasFormerCtor_hasType_checked + +theorem aliasFormerCtor_isType_checked : + aliasFormerTypeEnv.IsType 0 [] + aliasFormerRawType.ctors[0].type := + ⟨.succ .zero, aliasFormerCtor_hasSort_checked⟩ + +private theorem aliasFormerCandidate_generationShape : + VInductDecl.normalizationCandidateGenerationShape aliasFormerRawDecl + aliasFormerRawType aliasFormerNormalizationCandidate = true := + rfl + +private def aliasFormerProducedGenerationShapeCandidate : + VInductDecl.ProducedGenerationShapeCandidate aliasFormerRawDecl + aliasFormerRawType aliasFormerKernelType 0 false + aliasFormerCandidateContext where + candidate := aliasFormerNormalizationCandidate + produced := aliasFormerNormalizationCandidate_produced + shape := aliasFormerCandidate_generationShape + +/-- The strengthened outer gate returns the exact AliasFormer candidate with +its complete executable generation layout attached. -/ +theorem aliasFormerGenerationShapeCandidate_produced : + VInductDecl.produceGenerationShapeCandidate aliasFormerRawDecl + aliasFormerRawType aliasFormerKernelType 0 false + aliasFormerCandidateContext = + .ok aliasFormerProducedGenerationShapeCandidate := by + have produced : + AddInductive.buildNormalizationCandidate aliasFormerRawDecl.nparams + [aliasFormerKernelType] 0 false aliasFormerCandidateContext = + .ok aliasFormerNormalizationCandidate := + aliasFormerNormalizationCandidate_produced + simpa only [aliasFormerProducedGenerationShapeCandidate] using + VInductDecl.produceGenerationShapeCandidate_eq_ok + (source := aliasFormerRawDecl) (raw := aliasFormerRawType) + produced aliasFormerCandidate_generationShape + +private theorem aliasFormerCandidate_analysis + (normalization : VInductDecl.NormalizationCandidateSemanticRun + typeFamilyAliasEnv [] aliasFormerNormalizationCandidate + aliasFormerRawDecl) : + normalization.root.normalization.generation? = + some aliasFormerGenerationChecked := by + let reference : VInductDecl.NormalizationCandidateSemanticRun + typeFamilyAliasEnv [] aliasFormerNormalizationCandidate + aliasFormerRawDecl := { + raw := aliasFormerRawType + raw_types_eq := rfl + uvars_eq := rfl + family := aliasFormerCandidateFamilySemanticRun } + rw [aliasFormerStagedPreFamilyInput.normalization_eq normalization reference] + rfl + +/-- The staged owner, exact dependent analysis, and strengthened producer +close AliasFormer without exposing a caller-selected semantic hierarchy. -/ +theorem aliasFormerExactProducedGenerationCandidatePackage_exists : + Nonempty (VInductDecl.ExactProducedGenerationCandidatePackage + typeFamilyAliasEnv [] aliasFormerProducedGenerationShapeCandidate + aliasFormerGenerationChecked) := + aliasFormerProducedGenerationShapeCandidate.exactProducedPackage_nonempty + aliasFormerStagedPreFamilyInput rfl aliasFormerGenerationChecked + aliasFormerCandidate_analysis + +private def + aliasFormerExactProducedGenerationCandidatePackage : + VInductDecl.ExactProducedGenerationCandidatePackage typeFamilyAliasEnv [] + aliasFormerProducedGenerationShapeCandidate aliasFormerGenerationChecked := + aliasFormerProducedGenerationShapeCandidate.exactProducedPackage + aliasFormerStagedPreFamilyInput rfl aliasFormerGenerationChecked + aliasFormerCandidate_analysis + +/-- Complete source-indexed candidate certificate for the non-identity +AliasFormer generation transaction. -/ +def aliasFormerGenerationCandidateSemanticRun : + VInductDecl.GenerationCandidateSemanticRun + aliasFormerExactProducedGenerationCandidatePackage.normalization + aliasFormerGenerationChecked := + aliasFormerExactProducedGenerationCandidatePackage.semantic + +def aliasFormerGenerationCandidateRun : + VInductDecl.GenerationCandidateRun + aliasFormerExactProducedGenerationCandidatePackage.normalization.root + aliasFormerGenerationChecked := + aliasFormerGenerationCandidateSemanticRun.run + +/-- The generic dependent package retains the exact AliasFormer kernel +source, candidate trace, reconstructed normalization, successful dependent +analysis, and semantic generation run in one value. -/ +def aliasFormerGenerationCandidatePackage : + VInductDecl.GenerationCandidatePackage typeFamilyAliasEnv [] := + aliasFormerGenerationCandidateSemanticRun.package + +/-- The complete AliasFormer semantic package is selected by the exact +successful whole-call metadata producer, including its pre-family and +post-family checker environments. -/ +def aliasFormerProducedGenerationCandidatePackage : + VInductDecl.ProducedGenerationCandidatePackage typeFamilyAliasEnv [] := + aliasFormerExactProducedGenerationCandidatePackage.package + +/-- Theory-only erasure of the AliasFormer producer package. This is the +consumer-facing value accepted by the public non-identity transaction. -/ +def aliasFormerGenerationCertificate : + aliasFormerRawDecl.GenerationCertificate typeFamilyAliasEnv where + generation := aliasFormerGenerationChecked + wf := aliasFormerExactProducedGenerationCandidatePackage.semantic.run.wf + +/-- The public proof-carrying path exposes AliasFormer's candidate-derived +non-identity generation without exposing its checker package. -/ +theorem aliasFormer_addInductCertified_checked : + typeFamilyAliasEnv.addInductCertified + aliasFormerGenerationCertificate = + some aliasFormerFinalEnv := + aliasFormer_addInductGeneration + +theorem aliasFormerCertified_trace : + Nonempty (VEnv.AddInductGenerationTrace typeFamilyAliasEnv + aliasFormerFinalEnv aliasFormerGenerationChecked) := + VEnv.addInductCertified_trace aliasFormer_addInductCertified_checked + +theorem aliasFormerCertified_ordered : aliasFormerFinalEnv.Ordered := + VEnv.addInductCertified_WF typeFamilyAliasEnv_ordered + aliasFormer_addInductCertified_checked + +/-- Complete checker-side AliasFormer generation run, now derived by the +generic family/constructor spine assembler from the executable singleton +candidate rather than assembled field-by-field by the fixture. -/ +def aliasFormerGenerationRun : + VInductDecl.GenerationRun aliasFormerGenerationChecked + typeFamilyAliasEnv := + aliasFormerProducedGenerationCandidatePackage.package.run.generationRun + +/-- Generation-ready AliasFormer certificate whose raw/view family equality +comes from the verified checker execution rather than the fixture's explicit +delta rule. -/ +theorem aliasFormerGenerationChecked_wf_checked : + aliasFormerGenerationChecked.WF typeFamilyAliasEnv := + aliasFormerGenerationCertificate.wf + +/-- The paired AliasRec block with its field normalization supplied by the +checked WHNF/application/beta certificate. -/ +theorem aliasRecBlock_wf_checked : + aliasRecBlock.WF recAliasEnv := by + refine ⟨aliasRecNormalization_wf_checked, ?_⟩ + change aliasRecViewChecked.WF recAliasEnv + exact aliasRecViewChecked.wf_of_decl aliasRecViewDecl_wf + +/-- Complete checker-side AliasRec generation run. Its constructor telescope +retains the checked compositional alias equality as pointwise evidence. -/ +def aliasRecGenerationRun : + VInductDecl.GenerationRun aliasRecGenerationChecked recAliasEnv := by + refine { + normalization := aliasRecNormalizationRun + checked := aliasRecViewChecked.wf_of_decl aliasRecViewDecl_wf + familyTel := .nil + familyResult := .refl (VEnv.HasType.sort (by decide)) + typeEnv := aliasRecTypeEnv + addType := rfl + constructors := ?_ } + intro ctor hctor + change ctor ∈ + [⟨aliasRecRawType.ctors[0], + aliasRecViewChecked.constructors[0]⟩] at hctor + obtain rfl := List.mem_singleton.1 hctor + have hresult := aliasRecResult_hasType rfl + exact { + declaredTel := .cons aliasRecFieldEvidence .nil + declaredResult := .refl hresult + emittedTel := .cons aliasRecFieldEvidence .nil + emittedResult := .refl hresult } + +/-- Generation-ready AliasRec certificate whose raw field typing comes from +the exact post-family full-check run and whose normalization equality composes +the verified WHNF, application, and beta steps. -/ +theorem aliasRecGenerationChecked_wf_checked : + aliasRecGenerationChecked.WF recAliasEnv := + aliasRecGenerationRun.wf + +/-! ## Annotated recursive-Pi candidate interpretation -/ + +private def annotatedPiRawDomain : VExpr := + .app (.const ``outParam [.succ .zero]) (.sort .zero) + +private def annotatedPiRawInner : VExpr := + .forallE annotatedPiRawDomain (.const ``AnnotatedPi []) + +private def annotatedPiViewInner : VExpr := + .forallE (.sort .zero) (.const ``AnnotatedPi []) + +private theorem annotatedPiFamilyConst_hasType (Γ : List VExpr) : + annotatedPiTypeEnv.HasType 0 Γ + (.const ``AnnotatedPi []) (.sort (.succ .zero)) := by + have hfamily : annotatedPiTypeEnv.constants ``AnnotatedPi = + some annotatedPiRawType.toVConstant := rfl + type_tac + +private theorem annotatedPiRawDomain_hasType (Γ : List VExpr) : + annotatedPiTypeEnv.HasType 0 Γ annotatedPiRawDomain + (.sort (.succ .zero)) := by + have hout : annotatedPiTypeEnv.constants ``outParam = + some (vconst(type_of% @outParam)) := rfl + type_tac + +private theorem annotatedPiForall_hasType + {Γ : List VExpr} {domain body : VExpr} + (domainType : annotatedPiTypeEnv.HasType 0 Γ domain + (.sort (.succ .zero))) + (bodyType : annotatedPiTypeEnv.HasType 0 (domain :: Γ) body + (.sort (.succ .zero))) : + annotatedPiTypeEnv.HasType 0 Γ (.forallE domain body) + (.sort (.succ .zero)) := + .defeqDF + (.sortDF (by decide) (by decide) VLevel.imax_self) + (VEnv.HasType.forallE domainType bodyType) + +private theorem annotatedPiRawInner_hasType (Γ : List VExpr) : + annotatedPiTypeEnv.HasType 0 Γ annotatedPiRawInner + (.sort (.succ .zero)) := by + simpa [annotatedPiRawInner] using annotatedPiForall_hasType + (annotatedPiRawDomain_hasType Γ) + (annotatedPiFamilyConst_hasType (annotatedPiRawDomain :: Γ)) + +private theorem annotatedPiViewInner_hasType (Γ : List VExpr) : + annotatedPiTypeEnv.HasType 0 Γ annotatedPiViewInner + (.sort (.succ .zero)) := by + simpa [annotatedPiViewInner] using annotatedPiForall_hasType + (VEnv.HasType.sort (by decide)) + (annotatedPiFamilyConst_hasType ((.sort .zero) :: Γ)) + +private theorem annotatedPiRawCtor_hasType : + annotatedPiTypeEnv.HasType 0 [] + annotatedPiRawType.ctors[0].type (.sort (.succ .zero)) := by + rw [show annotatedPiRawType.ctors[0].type = + .forallE annotatedPiRawInner (.const ``AnnotatedPi []) by rfl] + simpa using annotatedPiForall_hasType + (annotatedPiRawInner_hasType []) + (annotatedPiFamilyConst_hasType [annotatedPiRawInner]) + +private theorem annotatedPiViewCtor_hasType : + annotatedPiTypeEnv.HasType 0 [] + annotatedPiViewCtor.type (.sort (.succ .zero)) := by + simpa [annotatedPiViewCtor, annotatedPiViewInner] using + annotatedPiForall_hasType (annotatedPiViewInner_hasType []) + (annotatedPiFamilyConst_hasType [annotatedPiViewInner]) + +private theorem annotatedPiCtorSource_tr : + TrExprS annotatedPiTypeEnv [] [] annotatedPiMkInfo.type + annotatedPiRawType.ctors[0].type := by + have hshape : TrTypeExpr annotatedPiTypeEnv [] [] + annotatedPiMkInfo.type annotatedPiRawType.ctors[0].type := by + tr_type_expr_tac + exact hshape.to_trExprS annotatedPiTypeEnv_ordered trivial + ⟨_, annotatedPiRawCtor_hasType⟩ + +private theorem annotatedPiInnerSource_tr : + TrExprS annotatedPiTypeEnv [] [] annotatedPiInnerKernel + annotatedPiRawInner := by + have hshape : TrTypeExpr annotatedPiTypeEnv [] [] + annotatedPiInnerKernel annotatedPiRawInner := by + tr_type_expr_tac + exact hshape.to_trExprS annotatedPiTypeEnv_ordered trivial + ⟨_, annotatedPiRawInner_hasType []⟩ + +private theorem annotatedPiDomainSource_tr : + TrExprS annotatedPiTypeEnv [] [] annotatedPiRawDomainKernel + annotatedPiRawDomain := by + have hshape : TrTypeExpr annotatedPiTypeEnv [] [] + annotatedPiRawDomainKernel annotatedPiRawDomain := by + tr_type_expr_tac + exact hshape.to_trExprS annotatedPiTypeEnv_ordered trivial + ⟨_, annotatedPiRawDomain_hasType []⟩ + +private theorem annotatedPiConstSource_tr (Δ : VLCtx) : + TrExprS annotatedPiTypeEnv [] Δ (Expr.const ``AnnotatedPi []) + (VExpr.const ``AnnotatedPi []) := + .const rfl rfl rfl + +private theorem annotatedPiInstConstSource_tr (Δ : VLCtx) : + TrExprS annotatedPiTypeEnv [] Δ + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiCtorCandidateContext.freshExpr) + (VExpr.const ``AnnotatedPi []) := by + simpa using annotatedPiConstSource_tr Δ + +private theorem annotatedPiFamilyCandidatePrefix_ne : + annotatedPiFamilyCandidateContext.ngen.namePrefix ≠ + (({} : TypeChecker.VState).ngen).namePrefix := by + decide + +private def annotatedPiFamilyCandidateContextRun : + TypeChecker.CandidateContextRun annotatedPiFamilyCandidateContext := + TypeChecker.CandidateContextRun.root outParamVEnvs_wf rfl + annotatedPiFamilyCandidatePrefix_ne + +private theorem annotatedPiFamilySource_tr : + TrExprS outParamEnv [] [] annotatedPiInfo.type + annotatedPiRawType.type := + annotatedPiInfo_tr.1.2.2 + +private def annotatedPiPreFamilyStage : + TypeChecker.CandidateSemanticStage annotatedPiFamilyCandidateContext + outParamEnv [] where + contextRun := annotatedPiFamilyCandidateContextRun + venv_eq := rfl + lparams_eq := rfl + vlctx_eq := rfl + +private def annotatedPiFamilyValidationRun : + AddInductive.CandidateExprTrace.FamilyValidationRun + annotatedPiKernelType annotatedPiFamilyCandidate.trace where + nparams := 0 + resultLevel := .succ .zero + stats := annotatedPiInductiveStats + stats_eq := rfl + terminal_eq := rfl + run := annotatedPi_checkInductiveTypes + +private def annotatedPiFamilyStage : + VInductDecl.CandidateFamilyStagedInput + annotatedPiFamilyCandidateContext annotatedPiCtorCandidateContext + outParamEnv [] annotatedPiFamilyListCandidate.familyType + annotatedPiRawType annotatedPiPreFamilyStage where + name_eq := rfl + uvars_eq := rfl + type := { + context_eq := rfl + source_tr := annotatedPiFamilySource_tr + whnfFuel := 9999 + whnfDepth := rfl } + validation := annotatedPiFamilyValidationRun + typeEnv := annotatedPiTypeEnv + addInduct := annotatedPiAddType + projectionReady := ProjectionReady.of_no_ctorInfo <| by + intro name _info h + change annotatedPiTypeMap.find?' name = some (.ctorInfo _info) at h + rw [annotatedPiTypeMap_wf.find?'_eq_find?] at h + simp only [annotatedPiTypeMap, outParamMap_wf.find?_insert] at h + simp only [outParamMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] at h + by_cases hAnnotatedPi : ``AnnotatedPi = name <;> + by_cases hOutParam : ``outParam = name <;> + simp +decide [hAnnotatedPi, hOutParam, SMap.find?, annotatedPiInfo, + annotationOutParamInfo] at h + structureEtaReady := StructureEtaReady.of_no_ctorInfo <| by + intro name _info h + change annotatedPiTypeMap.find?' name = some (.ctorInfo _info) at h + rw [annotatedPiTypeMap_wf.find?'_eq_find?] at h + simp only [annotatedPiTypeMap, outParamMap_wf.find?_insert] at h + simp only [outParamMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] at h + by_cases hAnnotatedPi : ``AnnotatedPi = name <;> + by_cases hOutParam : ``outParam = name <;> + simp +decide [hAnnotatedPi, hOutParam, SMap.find?, annotatedPiInfo, + annotationOutParamInfo] at h + family_lctx_eq := rfl + constructorContext_eq := rfl + quotInit_eq := rfl + name_not_reflected := by decide + name_not_primitive := by + simp [annotatedPiRawType, Kernel.Environment.primitives, + NameSet.ofList] + simp +decide [NameSet.contains] + +private def annotatedPiCtorCandidateContextRun : + TypeChecker.CandidateContextRun annotatedPiCtorCandidateContext := + annotatedPiFamilyStage.postContextRun + +private def annotatedPiInnerBodyCandidateContextRun : + TypeChecker.CandidateContextRun + annotatedPiInnerBodyCandidateContext := by + simpa [annotatedPiInnerBodyCandidateContext, + annotatedPiDomainAnnotations] using + annotatedPiCtorCandidateContextRun.pushLocalDecl `p .default + (.sort .zero) annotatedPiCtorCandidateFresh (.sort .zero) + (TrExprS.sort rfl) + ⟨.succ .zero, VEnv.HasType.sort (by decide)⟩ + +private def annotatedPiOuterBodyCandidateContextRun : + TypeChecker.CandidateContextRun + annotatedPiOuterBodyCandidateContext := by + have domain_tr : + annotatedPiCtorCandidateContextRun.context.TrExprS + annotatedPiInnerKernel annotatedPiRawInner := by + change + (annotatedPiFamilyStage.postFamily.contextRun.context.TrExprS + annotatedPiInnerKernel annotatedPiRawInner) + change TrExprS + annotatedPiFamilyStage.postFamily.contextRun.context.venv + annotatedPiFamilyStage.postFamily.contextRun.context.lparams + annotatedPiFamilyStage.postFamily.contextRun.context.vlctx + annotatedPiInnerKernel annotatedPiRawInner + rw [annotatedPiFamilyStage.postFamily.venv_eq, + annotatedPiFamilyStage.postFamily.lparams_eq, + annotatedPiFamilyStage.postFamily.vlctx_eq] + change TrExprS annotatedPiTypeEnv [] [] + annotatedPiInnerKernel annotatedPiRawInner + exact annotatedPiInnerSource_tr + simpa [annotatedPiOuterBodyCandidateContext, + annotatedPiInnerAnnotations] using + annotatedPiCtorCandidateContextRun.pushLocalDecl + annotatedPiOuterName .default annotatedPiInnerKernel + annotatedPiCtorCandidateFresh annotatedPiRawInner domain_tr + ⟨.succ .zero, annotatedPiRawInner_hasType []⟩ + +private theorem annotatedPiInnerBodyCandidateContextRun_vlctx : + annotatedPiInnerBodyCandidateContextRun.context.vlctx = + [(some (annotatedPiCtorCandidateContext.freshFVarId, + annotatedPiDomainAnnotations.consumed.fvarsList), + .vlam (.sort .zero))] := by + rfl + +private theorem annotatedPiOuterBodyCandidateContextRun_vlctx : + annotatedPiOuterBodyCandidateContextRun.context.vlctx = + [(some (annotatedPiCtorCandidateContext.freshFVarId, + annotatedPiInnerAnnotations.consumed.fvarsList), + .vlam annotatedPiRawInner)] := by + rfl + +private def annotatedPiFamilyCandidateNodeRun : + TypeChecker.CandidateNodeRun outParamEnv [] [] + annotatedPiFamilyCandidateContext annotatedPiInfo.type + (.sort (.succ (.succ .zero))) annotatedPiInfo.type + annotatedPiRawType.type annotatedPiRawType.type + (.sort (.succ (.succ .zero))) := by + exact TypeChecker.CandidateNodeRun.ofCandidate + annotatedPiFamilyCandidateContext annotatedPiInfo.type + (.sort (.succ (.succ .zero))) annotatedPiInfo.type + annotatedPiFamilyCheckTypeStep_valid + annotatedPiFamilyCandidateStep_valid + annotatedPiFamilyCandidateContextRun.context + annotatedPiFamilyCandidateContextRun.context_eq + rfl rfl rfl annotatedPiFamilyCandidateContextRun.state_wf + (.sort rfl) (.sort rfl) + (TrExprS.sort rfl) + 10000 9999 rfl rfl + +private theorem annotatedPiFamilyCandidateRun : + TypeChecker.CandidateExprRun outParamEnv [] + annotatedPiFamilyCandidate.trace [] + annotatedPiRawType.type annotatedPiRawType.type + (.sort (.succ (.succ .zero))) := + .terminal annotatedPiFamilyCandidateNodeRun + +private def annotatedPiFamilySemanticRootRun : + TypeChecker.CandidateExprSemanticRootRun outParamEnv [] + annotatedPiFamilyCandidate annotatedPiRawType.type + where + contextRun := annotatedPiFamilyCandidateContextRun + venv_eq := rfl + lparams_eq := rfl + vlctx_eq := rfl + source_tr := annotatedPiFamilyCandidateRun.source_tr + whnfFuel := 9999 + whnfDepth := rfl + view := annotatedPiRawType.type + recursive := ⟨.sort (.succ (.succ .zero)), + annotatedPiFamilyCandidateRun⟩ + +private def annotatedPiFamilyRootRun : + TypeChecker.CandidateExprRootRun outParamEnv [] + annotatedPiFamilyCandidate annotatedPiRawType.type + annotatedPiRawType.type := + annotatedPiFamilySemanticRootRun.root + +private theorem annotatedPiFamilySpineRun : + TypeChecker.CandidateExprSpineRun outParamEnv [] + annotatedPiFamilyCandidate annotatedPiRawType.type + annotatedPiRawType.type := + annotatedPiFamilySemanticRootRun.spine rfl + +private def annotatedPiCtorCandidateNodeRun : + TypeChecker.CandidateNodeRun annotatedPiTypeEnv [] [] + annotatedPiCtorCandidateContext annotatedPiMkInfo.type + (.sort (.succ .zero)) annotatedPiMkInfo.type + annotatedPiRawType.ctors[0].type + annotatedPiRawType.ctors[0].type + (.sort (.succ .zero)) := by + exact TypeChecker.CandidateNodeRun.ofCandidate + annotatedPiCtorCandidateContext annotatedPiMkInfo.type + (.sort (.succ .zero)) annotatedPiMkInfo.type + annotatedPiCtorCheckTypeStep_valid annotatedPiCtorCandidateStep_valid + annotatedPiCtorCandidateContextRun.context + annotatedPiCtorCandidateContextRun.context_eq + rfl rfl rfl annotatedPiCtorCandidateContextRun.state_wf + annotatedPiCtorSource_tr (.sort rfl) + annotatedPiCtorSource_tr + 10000 9999 rfl rfl + +private def annotatedPiInnerCandidateNodeRun : + TypeChecker.CandidateNodeRun annotatedPiTypeEnv [] [] + annotatedPiCtorCandidateContext annotatedPiInnerKernel + (.sort (.succ .zero)) annotatedPiInnerKernel + annotatedPiRawInner annotatedPiRawInner + (.sort (.succ .zero)) := by + exact TypeChecker.CandidateNodeRun.ofCandidate + annotatedPiCtorCandidateContext annotatedPiInnerKernel + (.sort (.succ .zero)) annotatedPiInnerKernel + annotatedPiInnerCheckTypeStep_valid annotatedPiInnerCandidateStep_valid + annotatedPiCtorCandidateContextRun.context + annotatedPiCtorCandidateContextRun.context_eq + rfl rfl rfl annotatedPiCtorCandidateContextRun.state_wf + annotatedPiInnerSource_tr (.sort rfl) + annotatedPiInnerSource_tr + 10000 9999 rfl rfl + +private def annotatedPiDomainCandidateNodeRun : + TypeChecker.CandidateNodeRun annotatedPiTypeEnv [] [] + annotatedPiCtorCandidateContext annotatedPiRawDomainKernel + (.sort (.succ .zero)) (.sort .zero) + annotatedPiRawDomain (.sort .zero) + (.sort (.succ .zero)) := by + exact TypeChecker.CandidateNodeRun.ofCandidate + annotatedPiCtorCandidateContext annotatedPiRawDomainKernel + (.sort (.succ .zero)) (.sort .zero) + annotatedPiDomainCheckTypeStep_valid annotatedPiDomainCandidateStep_valid + annotatedPiCtorCandidateContextRun.context + annotatedPiCtorCandidateContextRun.context_eq + rfl rfl rfl annotatedPiCtorCandidateContextRun.state_wf + annotatedPiDomainSource_tr (.sort rfl) + (TrExprS.sort rfl) + 10000 9999 rfl rfl + +private def annotatedPiInnerBodyCandidateNodeRun : + TypeChecker.CandidateNodeRun annotatedPiTypeEnv [] + annotatedPiInnerBodyCandidateContextRun.context.vlctx + annotatedPiInnerBodyCandidateContext + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiCtorCandidateContext.freshExpr) + (.sort (.succ .zero)) (.const ``AnnotatedPi []) + (.const ``AnnotatedPi []) (.const ``AnnotatedPi []) + (.sort (.succ .zero)) := by + exact TypeChecker.CandidateNodeRun.ofCandidate + annotatedPiInnerBodyCandidateContext + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiCtorCandidateContext.freshExpr) + (.sort (.succ .zero)) (.const ``AnnotatedPi []) + (by simpa only [annotatedPiInnerBodyCheckTypeStep, + annotatedPiConst_instantiate1] using + annotatedPiInnerBodyCheckTypeStep_valid) + (by simpa only [annotatedPiInnerBodyCandidateStep, + annotatedPiConst_instantiate1] using + annotatedPiInnerBodyCandidateStep_valid) + annotatedPiInnerBodyCandidateContextRun.context + annotatedPiInnerBodyCandidateContextRun.context_eq + rfl rfl rfl annotatedPiInnerBodyCandidateContextRun.state_wf + (annotatedPiInstConstSource_tr + annotatedPiInnerBodyCandidateContextRun.context.vlctx) + (.sort rfl) + (annotatedPiConstSource_tr + annotatedPiInnerBodyCandidateContextRun.context.vlctx) + 10000 9999 rfl rfl + +private def annotatedPiOuterBodyCandidateNodeRun : + TypeChecker.CandidateNodeRun annotatedPiTypeEnv [] + annotatedPiOuterBodyCandidateContextRun.context.vlctx + annotatedPiOuterBodyCandidateContext + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiCtorCandidateContext.freshExpr) + (.sort (.succ .zero)) (.const ``AnnotatedPi []) + (.const ``AnnotatedPi []) (.const ``AnnotatedPi []) + (.sort (.succ .zero)) := by + exact TypeChecker.CandidateNodeRun.ofCandidate + annotatedPiOuterBodyCandidateContext + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiCtorCandidateContext.freshExpr) + (.sort (.succ .zero)) (.const ``AnnotatedPi []) + (by simpa only [annotatedPiOuterBodyCheckTypeStep, + annotatedPiConst_instantiate1] using + annotatedPiOuterBodyCheckTypeStep_valid) + (by simpa only [annotatedPiOuterBodyCandidateStep, + annotatedPiConst_instantiate1] using + annotatedPiOuterBodyCandidateStep_valid) + annotatedPiOuterBodyCandidateContextRun.context + annotatedPiOuterBodyCandidateContextRun.context_eq + rfl rfl rfl annotatedPiOuterBodyCandidateContextRun.state_wf + (annotatedPiInstConstSource_tr + annotatedPiOuterBodyCandidateContextRun.context.vlctx) + (.sort rfl) + (annotatedPiConstSource_tr + annotatedPiOuterBodyCandidateContextRun.context.vlctx) + 10000 9999 rfl rfl + +private def annotatedPiDomainAnnotationsRun : + TypeChecker.IsDefEqRun annotatedPiTypeEnv [] [] + annotatedPiRawDomainKernel annotatedPiDomainAnnotations.consumed + annotatedPiRawDomain (.sort .zero) := by + exact TypeChecker.IsDefEqRun.ofCandidateStep + ⟨annotatedPiCtorCandidateContext, annotatedPiRawDomainKernel, + annotatedPiDomainAnnotations.consumed⟩ + annotatedPiDomainAnnotationsEq + annotatedPiCtorCandidateContextRun.context + annotatedPiCtorCandidateContextRun.context_eq + rfl rfl rfl annotatedPiCtorCandidateContextRun.state_wf + annotatedPiDomainSource_tr (.sort rfl) 10000 rfl + +private def annotatedPiInnerAnnotationsRun : + TypeChecker.IsDefEqRun annotatedPiTypeEnv [] [] + annotatedPiInnerKernel annotatedPiInnerAnnotations.consumed + annotatedPiRawInner annotatedPiRawInner := by + exact TypeChecker.IsDefEqRun.ofCandidateStep + ⟨annotatedPiCtorCandidateContext, annotatedPiInnerKernel, + annotatedPiInnerAnnotations.consumed⟩ + annotatedPiInnerAnnotationsEq + annotatedPiCtorCandidateContextRun.context + annotatedPiCtorCandidateContextRun.context_eq + rfl rfl rfl annotatedPiCtorCandidateContextRun.state_wf + annotatedPiInnerSource_tr annotatedPiInnerSource_tr 10000 rfl + +private theorem annotatedPiDomainCandidateRun : + TypeChecker.CandidateExprRun annotatedPiTypeEnv [] + annotatedPiDomainCandidateTrace [] annotatedPiRawDomain + (.sort .zero) (.sort (.succ .zero)) := + .terminal annotatedPiDomainCandidateNodeRun + +private theorem annotatedPiInnerBodyCandidateRun : + TypeChecker.CandidateExprRun annotatedPiTypeEnv [] + annotatedPiInnerBodyCandidateTrace + [(some (annotatedPiCtorCandidateContext.freshFVarId, + annotatedPiDomainAnnotations.consumed.fvarsList), + .vlam (.sort .zero))] + (.const ``AnnotatedPi []) (.const ``AnnotatedPi []) + (.sort (.succ .zero)) := by + simpa only [annotatedPiInnerBodyCandidateTrace, + annotatedPiInnerBodyCandidateContextRun_vlctx] using + (TypeChecker.CandidateExprRun.terminal + annotatedPiInnerBodyCandidateNodeRun) + +private theorem annotatedPiOuterBodyCandidateRun : + TypeChecker.CandidateExprRun annotatedPiTypeEnv [] + annotatedPiOuterBodyCandidateTrace + [(some (annotatedPiCtorCandidateContext.freshFVarId, + annotatedPiInnerAnnotations.consumed.fvarsList), + .vlam annotatedPiRawInner)] + (.const ``AnnotatedPi []) (.const ``AnnotatedPi []) + (.sort (.succ .zero)) := by + simpa only [annotatedPiOuterBodyCandidateTrace, + annotatedPiOuterBodyCandidateContextRun_vlctx] using + (TypeChecker.CandidateExprRun.terminal + annotatedPiOuterBodyCandidateNodeRun) + +private theorem annotatedPiInnerCandidateRun : + TypeChecker.CandidateExprRun annotatedPiTypeEnv [] + annotatedPiInnerCandidateTrace [] annotatedPiRawInner + annotatedPiViewInner (.sort (.succ .zero)) := by + exact .forallE annotatedPiDomainAnnotations + annotatedPiDomainAnnotationsEq annotatedPiDomainCandidateTrace + annotatedPiInnerBodyCandidateTrace + annotatedPiInnerCandidateNodeRun annotatedPiDomainCandidateRun + annotatedPiDomainAnnotationsRun annotatedPiInnerBodyCandidateRun + (annotatedPiRawDomain_hasType []) + (annotatedPiFamilyConst_hasType [annotatedPiRawDomain]) + (annotatedPiFamilyConst_hasType [annotatedPiRawDomain]) rfl + +private theorem annotatedPiCtorCandidateRun : + TypeChecker.CandidateExprRun annotatedPiTypeEnv [] + annotatedPiCtorCandidate.trace [] + annotatedPiRawType.ctors[0].type annotatedPiViewCtor.type + (.sort (.succ .zero)) := by + exact .forallE annotatedPiInnerAnnotations + annotatedPiInnerAnnotationsEq annotatedPiInnerCandidateTrace + annotatedPiOuterBodyCandidateTrace + annotatedPiCtorCandidateNodeRun annotatedPiInnerCandidateRun + annotatedPiInnerAnnotationsRun annotatedPiOuterBodyCandidateRun + (annotatedPiRawInner_hasType []) + (annotatedPiFamilyConst_hasType [annotatedPiRawInner]) + (annotatedPiFamilyConst_hasType [annotatedPiRawInner]) rfl + +private def annotatedPiCtorSemanticRootRun : + TypeChecker.CandidateExprSemanticRootRun annotatedPiTypeEnv [] + annotatedPiCtorCandidate annotatedPiRawType.ctors[0].type + where + contextRun := annotatedPiCtorCandidateContextRun + venv_eq := rfl + lparams_eq := rfl + vlctx_eq := rfl + source_tr := annotatedPiCtorCandidateRun.source_tr + whnfFuel := 9999 + whnfDepth := rfl + view := annotatedPiViewCtor.type + recursive := ⟨.sort (.succ .zero), annotatedPiCtorCandidateRun⟩ + +private def annotatedPiCtorRootRun : + TypeChecker.CandidateExprRootRun annotatedPiTypeEnv [] + annotatedPiCtorCandidate annotatedPiRawType.ctors[0].type + annotatedPiViewCtor.type := + annotatedPiCtorSemanticRootRun.root + +private theorem annotatedPiCtorCandidate_storedSpine : + annotatedPiCtorCandidate.trace.storedSpine = true := by + have hsource : annotatedPiMkInfo.type = + .forallE annotatedPiOuterName annotatedPiInnerKernel + (.const ``AnnotatedPi []) .default := rfl + simp only [annotatedPiCtorCandidate, annotatedPiCtorCandidateTrace, + AddInductive.CandidateExprTrace.storedSpine, hsource, + Expr.structuralEq_refl, Bool.true_and] + rfl + +private theorem annotatedPiCtorSpineRun : + TypeChecker.CandidateExprSpineRun annotatedPiTypeEnv [] + annotatedPiCtorCandidate annotatedPiRawType.ctors[0].type + annotatedPiViewCtor.type := + annotatedPiCtorSemanticRootRun.spine + annotatedPiCtorCandidate_storedSpine + +private def annotatedPiCandidateConstructorSemanticRun : + VInductDecl.CandidateConstructorSemanticRun annotatedPiTypeEnv [] + annotatedPiConstructorCandidate annotatedPiRawType.ctors[0] where + name_eq := rfl + uvars_eq := rfl + type := annotatedPiCtorSemanticRootRun + +private def annotatedPiCandidateConstructorRun : + VInductDecl.CandidateConstructorRun annotatedPiTypeEnv [] + annotatedPiConstructorCandidate annotatedPiRawType.ctors[0] := + annotatedPiCandidateConstructorSemanticRun.root + +private def annotatedPiCandidateConstructorSemanticListRun : + VInductDecl.CandidateConstructorSemanticListRun annotatedPiTypeEnv [] + annotatedPiFamilyListCandidate.constructors + annotatedPiRawType.ctors := by + exact .cons annotatedPiCandidateConstructorSemanticRun .nil + +private def annotatedPiCandidateConstructorListRun : + VInductDecl.CandidateConstructorListRun annotatedPiTypeEnv [] + annotatedPiFamilyListCandidate.constructors + annotatedPiRawType.ctors := + annotatedPiCandidateConstructorSemanticListRun.roots + +private def annotatedPiCandidateFamilySemanticRun : + VInductDecl.CandidateFamilySemanticRun outParamEnv [] + annotatedPiFamilyListCandidate annotatedPiRawType where + name_eq := rfl + uvars_eq := rfl + type := annotatedPiFamilySemanticRootRun + typeEnv := annotatedPiTypeEnv + addType := rfl + constructors := annotatedPiCandidateConstructorSemanticListRun + +private def annotatedPiCandidateFamilyRun : + VInductDecl.CandidateFamilyRun outParamEnv [] + annotatedPiFamilyListCandidate annotatedPiRawType := + annotatedPiCandidateFamilySemanticRun.root + +private def annotatedPiStagedUniverseInput : + VInductDecl.StagedNormalizationCandidateUniverseInput + annotatedPiFamilyCandidateContext annotatedPiCtorCandidateContext + outParamEnv [] annotatedPiNormalizationCandidate + annotatedPiRawDecl where + staged := { + raw := annotatedPiRawType + raw_types_eq := rfl + declaration_uvars_eq := rfl + preFamily := annotatedPiPreFamilyStage + family := annotatedPiFamilyStage + validation_nparams_eq := rfl + constructorValidation := + AddInductive.ConstructorValidationRun.of_run annotatedPi_checkConstructors + constructors := .cons { + name_eq := rfl + uvars_eq := rfl + type := { + context_eq := rfl + source_tr := annotatedPiCtorCandidateRun.source_tr + whnfFuel := 9999 + whnfDepth := rfl } } .nil + familyTypesProduced := annotatedPiFamilyTypeListProduced + familiesProduced := annotatedPiFamilyListProduced } + universeRun := annotatedPi_checkConstructorUniverseSemantics + +private def annotatedPiConstructorValidationContext : AddInductive.Context := + { env := annotatedPiCtorCandidateContext.env + lctx := annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext.lctx + lparams := annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext.lparams + ngen := annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext.ngen + safety := annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext.safety + allowPrimitive := annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext.allowPrimitive + fuel := annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext.fuel } + +private theorem annotatedPiConstructorValidationContext_eq : + { annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext with + env := annotatedPiCtorCandidateContext.env } = + annotatedPiCtorCandidateContext := rfl + +private theorem annotatedPiConstructorValidationContext_def_eq : + annotatedPiConstructorValidationContext = + annotatedPiCtorCandidateContext := rfl + +private theorem annotatedPiConstructorValidationContextLiteral_eq : + ({ env := annotatedPiCtorCandidateContext.env + lctx := annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext.lctx + lparams := annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext.lparams + ngen := annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext.ngen + safety := annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext.safety + allowPrimitive := annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext.allowPrimitive + fuel := annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext.fuel } : + AddInductive.Context) = annotatedPiCtorCandidateContext := rfl + +private theorem annotatedPiStagedStats_eq : + annotatedPiStagedUniverseInput.staged.family.validation.stats = + annotatedPiInductiveStats := rfl + +private theorem annotatedPiStagedParams_zero : + annotatedPiStagedUniverseInput.staged.family.validation.stats.params[0]? = + none := by + rw [annotatedPiStagedStats_eq] + rfl + +private theorem annotatedPiKernelCtor_isForall : + annotatedPiKernelCtor.type.isForall = true := rfl + +private theorem annotatedPiKernelType_ctors_eq : + annotatedPiKernelType.ctors = [annotatedPiKernelCtor] := rfl + +private theorem annotatedPiKernelCtor_type_eq : + annotatedPiKernelCtor.type = + .forallE annotatedPiOuterName annotatedPiInnerKernel + (.const ``AnnotatedPi []) .default := rfl + +private theorem annotatedPiConstructorValidationFuel_eq : + annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext.fuel.inductiveFuel = + 1000 := rfl + +private theorem annotatedPiCtorCandidateContext_empty : + annotatedPiCtorCandidateContext.withEmptyLocalContext = + annotatedPiCtorCandidateContext := rfl + +private def annotatedPiViewInnerKernel : Expr := + .forallE `p (.sort .zero) (.const ``AnnotatedPi []) .default + +private def annotatedPiViewCtorKernel : Expr := + .forallE annotatedPiOuterName annotatedPiViewInnerKernel + (.const ``AnnotatedPi []) .default + +private def annotatedPiAlignedViewInnerKernel : Expr := + .forallE `p (.sort .zero) + ((Expr.const ``AnnotatedPi []).abstract + #[annotatedPiCtorCandidateContext.freshExpr]) .default + +private def annotatedPiAlignedViewCtorKernel : Expr := + .forallE annotatedPiOuterName annotatedPiAlignedViewInnerKernel + ((Expr.const ``AnnotatedPi []).abstract + #[annotatedPiCtorCandidateContext.freshExpr]) .default + +@[simp] private theorem annotatedPiConst_abstract_singleton + (context : AddInductive.Context) : + (Expr.const ``AnnotatedPi []).abstract #[context.freshExpr] = + .const ``AnnotatedPi [] := by + rw [show #[context.freshExpr] = + ⟨[context.freshFVarId].map Expr.fvar⟩ by rfl] + simp only [Expr.abstract_eq, Expr.abstractList, Expr.abstract1] + +private theorem annotatedPiAlignedViewInnerKernel_eq : + annotatedPiAlignedViewInnerKernel = annotatedPiViewInnerKernel := by + simp [annotatedPiAlignedViewInnerKernel, annotatedPiViewInnerKernel] + +private theorem annotatedPiAlignedViewCtorKernel_eq : + annotatedPiAlignedViewCtorKernel = annotatedPiViewCtorKernel := by + simp [annotatedPiAlignedViewCtorKernel, annotatedPiViewCtorKernel, + annotatedPiAlignedViewInnerKernel, annotatedPiViewInnerKernel] + +set_option maxRecDepth 10000 in +private theorem annotatedPiDomain_isDefEqInner9999 + (initial : Std.HashSet (Expr × Expr)) : + ∃ state : TypeChecker.State, + TypeChecker.Inner.isDefEq annotatedPiRawDomainKernel (.sort .zero) + (TypeChecker.Methods.withFuel 9999) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := initial } : TypeChecker.State) = + .ok (true, state) := by + simpa only [Nat.reduceAdd] using + annotatedPiDomain_isDefEqInner 9995 initial + +private theorem annotatedPiInnerView_isDefEqForall + (initial : Std.HashSet (Expr × Expr)) : + ∃ state : TypeChecker.State, + TypeChecker.Inner.isDefEqForall + annotatedPiInnerKernel annotatedPiViewInnerKernel #[] + (TypeChecker.Methods.withFuel 9999) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := initial } : TypeChecker.State) = + .ok (true, state) := by + obtain ⟨state, domainRun⟩ := + annotatedPiDomain_isDefEqInner9999 initial + have domainRun' : TypeChecker.Inner.isDefEq + (annotatedPiRawDomainKernel.instantiateRev #[]) + ((.sort .zero : Expr).instantiateRev #[]) + (TypeChecker.Methods.withFuel 9999) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := initial } : TypeChecker.State) = + .ok (true, state) := by + simpa [Expr.instantiateRev] using domainRun + refine ⟨state, ?_⟩ + unfold annotatedPiInnerKernel annotatedPiViewInnerKernel + TypeChecker.Inner.isDefEqForall + rw [show + (annotatedPiRawDomainKernel == (.sort .zero : Expr)) = false by + exact annotatedPiApp_beq_sort _ _ _] + simp only [Bool.false_eq_true, if_false, pure_bind, + normalizationRecMBind] + rw [domainRun'] + simp [TypeChecker.Inner.isDefEqForall, TypeChecker.Inner.isDefEq, Expr.hasLooseBVars, + Expr.looseBVarRange'] + +private theorem annotatedPiInnerView_quickIsDefEq + (initial : Std.HashSet (Expr × Expr)) : + ∃ state : TypeChecker.State, + TypeChecker.Inner.quickIsDefEq + annotatedPiInnerKernel annotatedPiViewInnerKernel + (TypeChecker.Methods.withFuel 9999) + annotatedPiCtorCandidateContext.toTypeChecker + ({ success := initial } : TypeChecker.State) = + .ok (.true, state) := by + by_cases hc : (annotatedPiInnerKernel == annotatedPiViewInnerKernel || + TypeChecker.Inner.succeededBefore initial + annotatedPiInnerKernel annotatedPiViewInnerKernel) = true + -- Settled structurally or by the cache: the state comes back untouched. + · refine ⟨({ success := initial } : TypeChecker.State), ?_⟩ + unfold TypeChecker.Inner.quickIsDefEq + simp [hc, pure, ReaderT.pure, StateT.pure, Except.pure, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + -- Otherwise both sides are for-alls and the match hands off to `isDefEqForall`. + · simp only [Bool.not_eq_true] at hc + obtain ⟨state, forallRun⟩ := annotatedPiInnerView_isDefEqForall initial + refine ⟨state, ?_⟩ + unfold annotatedPiInnerKernel annotatedPiViewInnerKernel at hc forallRun ⊢ + unfold TypeChecker.Inner.quickIsDefEq + simp [hc, Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + unfold toLBoolM + rw [normalizationRecMBind, forallRun] + rfl + +private theorem annotatedPiInnerView_isDefEqInner : + ∃ state : TypeChecker.State, + TypeChecker.Inner.isDefEq + annotatedPiInnerKernel annotatedPiViewInnerKernel + (TypeChecker.Methods.withFuel 10000) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (true, state) := by + obtain ⟨quickState, quickRun⟩ := + annotatedPiInnerView_quickIsDefEq {} + have coreRun : TypeChecker.Inner.isDefEqCore + annotatedPiInnerKernel annotatedPiViewInnerKernel + (TypeChecker.Methods.withFuel 10000) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State) = .ok (true, quickState) := by + change TypeChecker.Inner.isDefEqCore' + annotatedPiInnerKernel annotatedPiViewInnerKernel + (TypeChecker.Methods.withFuel 9999) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State) = .ok (true, quickState) + unfold TypeChecker.Inner.isDefEqCore' + rw [normalizationRecMBind, quickRun] + rfl + unfold TypeChecker.Inner.isDefEq + rw [show + (annotatedPiInnerKernel == annotatedPiViewInnerKernel) = false by + change Expr.eqv + (.forallE `p annotatedPiRawDomainKernel + (.const ``AnnotatedPi []) .default) + (.forallE `p (.sort .zero) + (.const ``AnnotatedPi []) .default) = false + rw [Expr.eqv_eq] + rfl] + simp only [Bool.false_eq_true, if_false, normalizationRecMBind] + rw [coreRun] + exact ⟨_, rfl⟩ + +private theorem annotatedPiInnerView_isDefEq : + AddInductive.CandidateIsDefEqStep.Valid + ⟨annotatedPiConstructorValidationContext, + annotatedPiInnerKernel, annotatedPiAlignedViewInnerKernel⟩ := by + unfold AddInductive.CandidateIsDefEqStep.Valid + rw [annotatedPiConstructorValidationContext_def_eq, + annotatedPiAlignedViewInnerKernel_eq] + obtain ⟨state, run⟩ := annotatedPiInnerView_isDefEqInner + change Except.map (fun x : Bool × TypeChecker.State => x.1) + (TypeChecker.Inner.isDefEq annotatedPiInnerKernel + annotatedPiViewInnerKernel + (TypeChecker.Methods.withFuel 10000) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = .ok true + rw [run] + rfl + +private theorem annotatedPiValidationAlignedCheck_exists + {source candidateSource : Expr} + (source_eq : source = candidateSource) + {checked : AddInductive.ConstructorCheckedExpr + annotatedPiCtorCandidateContext candidateSource} + (run : AddInductive.checkConstructorAlignedExpr + annotatedPiCtorCandidateContext candidateSource = .ok checked) : + ∃ aligned : AddInductive.ConstructorCheckedExpr + annotatedPiConstructorValidationContext source, + AddInductive.checkConstructorAlignedExpr + annotatedPiConstructorValidationContext source = .ok aligned := by + subst candidateSource + rw [annotatedPiConstructorValidationContext_def_eq] + exact ⟨checked, run⟩ + +private def annotatedPiAlignChecked + {context candidateContext : AddInductive.Context} + {source candidateSource : Expr} + (context_eq : context = candidateContext) + (source_eq : source = candidateSource) + (checked : AddInductive.ConstructorCheckedExpr + candidateContext candidateSource) : + AddInductive.ConstructorCheckedExpr context source := by + subst candidateContext + subst candidateSource + exact checked + +private def annotatedPiAlignIsDefEq + {context candidateContext : AddInductive.Context} + {lhs rhs candidateLhs candidateRhs : Expr} + (context_eq : context = candidateContext) + (lhs_eq : lhs = candidateLhs) + (rhs_eq : rhs = candidateRhs) + (observation : AddInductive.CandidateIsDefEqObservation + candidateContext candidateLhs candidateRhs) : + AddInductive.CandidateIsDefEqObservation context lhs rhs := by + subst candidateContext + subst candidateLhs + subst candidateRhs + exact observation + +private theorem annotatedPiViewInnerCheckTypeStep_valid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨annotatedPiCtorCandidateContext, annotatedPiViewInnerKernel, + .sort (.succ .zero)⟩ := by + change TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.checkType annotatedPiViewInnerKernel) = + .ok (.sort (.succ .zero)) + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType annotatedPiViewInnerKernel false + (TypeChecker.Methods.withFuel 10000) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = + .ok (.sort (.succ .zero)) + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' annotatedPiViewInnerKernel false + (TypeChecker.Methods.withFuel 9999) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = + .ok (.sort (.succ .zero)) + unfold annotatedPiViewInnerKernel TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', TypeChecker.Inner.inferType', + TypeChecker.Inner.inferForall, TypeChecker.Inner.inferForall.loop, Expr.instantiate1', + annotatedPiWithLocalDecl, annotatedPiCtorCandidateContext, AddInductive.Context.toTypeChecker, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + simp [Expr.sortLevel!, annotatedPi_mkLevelIMaxSuccZero] + rfl + +private def annotatedPiViewInnerFinalState : TypeChecker.State := + { ({} : TypeChecker.State) with + ngen := ({} : TypeChecker.State).ngen.next + inferTypeC := + ((({} : TypeChecker.State).inferTypeC.insert + (.sort .zero) (.sort (.succ .zero))).insert + (.const ``AnnotatedPi []) (.sort (.succ .zero))).insert + annotatedPiViewInnerKernel (.sort (.succ .zero)) } + +private theorem annotatedPiViewInnerInferType_exists (n : Nat) : + ∃ state : TypeChecker.State, + TypeChecker.Inner.inferType' annotatedPiViewInnerKernel false + (TypeChecker.Methods.withFuel (n + 1)) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (.sort (.succ .zero), state) ∧ + state.inferTypeC[(.const ``AnnotatedPi [] : Expr)]? = + some (.sort (.succ .zero)) := by + refine ⟨annotatedPiViewInnerFinalState, ?_, ?_⟩ + · unfold annotatedPiViewInnerKernel TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', TypeChecker.Inner.inferType', + TypeChecker.Inner.inferForall, TypeChecker.Inner.inferForall.loop, Expr.instantiate1', + annotatedPiWithLocalDecl, annotatedPiCtorCandidateContext, AddInductive.Context.toTypeChecker, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + simp [Expr.sortLevel!, annotatedPi_mkLevelIMaxSuccZero] + rfl + · simpa [annotatedPiViewInnerFinalState, + annotatedPiViewInnerKernel] using + annotatedPiFamilyCacheAfterForall + (({} : TypeChecker.State).inferTypeC.insert + (.sort .zero) (.sort (.succ .zero))) + `p (.sort .zero) (.const ``AnnotatedPi []) + (.sort (.succ .zero)) .default + +private theorem annotatedPiViewCtorCheckTypeStep_valid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨annotatedPiCtorCandidateContext, annotatedPiViewCtorKernel, + .sort (.succ .zero)⟩ := by + change TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.checkType annotatedPiViewCtorKernel) = + .ok (.sort (.succ .zero)) + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType annotatedPiViewCtorKernel false + (TypeChecker.Methods.withFuel 10000) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = + .ok (.sort (.succ .zero)) + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' annotatedPiViewCtorKernel false + (TypeChecker.Methods.withFuel 9999) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = + .ok (.sort (.succ .zero)) + obtain ⟨innerState, hinner, hinnerCache⟩ := + annotatedPiViewInnerInferType_exists 9997 + change TypeChecker.Inner.inferType' annotatedPiViewInnerKernel false + (TypeChecker.Methods.withFuel 9998) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State) = + .ok (.sort (.succ .zero), innerState) at hinner + unfold annotatedPiViewInnerKernel at hinner + let outerState : TypeChecker.State := + { innerState with ngen := innerState.ngen.next } + have houterCache : + outerState.inferTypeC[(.const ``AnnotatedPi [] : Expr)]? = + some (.sort (.succ .zero)) := by + exact hinnerCache + have hfamily : + TypeChecker.Inner.inferType' (.const ``AnnotatedPi []) false + (TypeChecker.Methods.withFuel 9998) + ({ env := annotatedPiCtorCandidateContext.toTypeChecker.env + lctx := annotatedPiCtorCandidateContext.toTypeChecker.lctx.mkLocalDecl + ⟨innerState.ngen.curr⟩ annotatedPiOuterName + annotatedPiViewInnerKernel .default + safety := annotatedPiCtorCandidateContext.toTypeChecker.safety + eagerReduce := annotatedPiCtorCandidateContext.toTypeChecker.eagerReduce + lparams := annotatedPiCtorCandidateContext.toTypeChecker.lparams + fuel := annotatedPiCtorCandidateContext.toTypeChecker.fuel } : + TypeChecker.Context) + outerState = + .ok (.sort (.succ .zero), outerState) := by + simpa [annotatedPiCtorCandidateContext, + AddInductive.Context.toTypeChecker] using + annotatedPiInferTypeFamilyCached 9998 + (annotatedPiCtorCandidateContext.toTypeChecker.lctx.mkLocalDecl + ⟨innerState.ngen.curr⟩ annotatedPiOuterName + annotatedPiViewInnerKernel .default) + outerState houterCache + unfold annotatedPiViewInnerKernel at hfamily + unfold annotatedPiViewCtorKernel TypeChecker.Inner.inferType' + simp [annotatedPiViewInnerKernel, Expr.hasLooseBVars, Expr.looseBVarRange', + TypeChecker.Inner.inferForall, TypeChecker.Inner.inferForall.loop, Expr.instantiate1', + annotatedPiWithLocalDecl, Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rw [hinner] + simp only [TypeChecker.Inner.ensureSortCore, Expr.isSort, ↓reduceIte, Bind.bind, ReaderT.pure, + StateT.pure, Except.pure, Pure.pure] + rw [hfamily] + simp [Expr.sortLevel!, annotatedPi_mkLevelIMaxSuccZero, ReaderT.pure, StateT.pure, Except.pure, + Pure.pure] + rfl + +private theorem annotatedPiSortZeroCheckTypeStep_valid : + AddInductive.CandidateCheckTypeStep.Valid + ⟨annotatedPiCtorCandidateContext, .sort .zero, + .sort (.succ .zero)⟩ := by + unfold AddInductive.CandidateCheckTypeStep.Valid + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType (.sort .zero) false + (TypeChecker.Methods.withFuel 10000) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = + .ok (.sort (.succ .zero)) + change + Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' (.sort .zero) false + (TypeChecker.Methods.withFuel 9999) + annotatedPiCtorCandidateContext.toTypeChecker + ({} : TypeChecker.State)) = + .ok (.sort (.succ .zero)) + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', + annotatedPiCtorCandidateContext, + AddInductive.Context.toTypeChecker, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind] + rfl + +private theorem annotatedPiConsumeRawDomain : + AddInductive.consumeTypeAnnotations annotatedPiRawDomainKernel = + .sort .zero := by + simp [AddInductive.consumeTypeAnnotations, annotatedPiRawDomainKernel] + +private theorem annotatedPiConsumeInner : + AddInductive.consumeTypeAnnotations annotatedPiInnerKernel = + annotatedPiInnerKernel := by + simp [AddInductive.consumeTypeAnnotations, annotatedPiInnerKernel] + +private theorem annotatedPiValidationInnerBodyContext_eq : + annotatedPiConstructorValidationContext.pushLocalDecl + `p .default + (AddInductive.consumeTypeAnnotations annotatedPiRawDomainKernel) = + annotatedPiInnerBodyCandidateContext := by + rw [annotatedPiConstructorValidationContext_def_eq, + annotatedPiConsumeRawDomain] + rfl + +private theorem annotatedPiValidationOuterBodyContext_eq : + annotatedPiConstructorValidationContext.pushLocalDecl + annotatedPiOuterName .default + (AddInductive.consumeTypeAnnotations annotatedPiInnerKernel) = + annotatedPiOuterBodyCandidateContext := by + rw [annotatedPiConstructorValidationContext_def_eq, + annotatedPiConsumeInner] + rfl + +private theorem annotatedPiValidationInnerBody_whnf_eq + {result : Expr} + (run : AddInductive.CandidateWhnfStep.Valid + ⟨({ annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext with + env := annotatedPiCtorCandidateContext.env }).pushLocalDecl + `p .default (AddInductive.consumeTypeAnnotations + annotatedPiRawDomainKernel), + .const ``AnnotatedPi [], result⟩) : + result = .const ``AnnotatedPi [] := by + rw [annotatedPiConstructorValidationContext_eq, + annotatedPiConsumeRawDomain] at run + change TypeChecker.M.run annotatedPiInnerBodyCandidateContext.env + annotatedPiInnerBodyCandidateContext.safety + annotatedPiInnerBodyCandidateContext.lctx + annotatedPiInnerBodyCandidateContext.lparams + annotatedPiInnerBodyCandidateContext.fuel + (TypeChecker.whnf (.const ``AnnotatedPi [])) = .ok result at run + have known := annotatedPiInnerBodyCandidateStep_valid + change TypeChecker.M.run annotatedPiInnerBodyCandidateContext.env + annotatedPiInnerBodyCandidateContext.safety + annotatedPiInnerBodyCandidateContext.lctx + annotatedPiInnerBodyCandidateContext.lparams + annotatedPiInnerBodyCandidateContext.fuel + (TypeChecker.whnf (.const ``AnnotatedPi [])) = + .ok (.const ``AnnotatedPi []) at known + rw [known] at run + exact (Except.ok.inj run).symm + +private theorem constructorTypeValidationTrace_spineLength_zero + {stats : AddInductive.InductiveStats} {isUnsafe : Bool} + {familyIdx : Nat} {ctor : Name} {context : AddInductive.Context} + {source : Expr} {argIdx fuel : Nat} + (trace : AddInductive.ConstructorTypeValidationTrace stats isUnsafe + familyIdx ctor context source argIdx fuel) + (terminal : source.isForall = false) : + trace.spineLength = 0 := by + cases trace <;> + simp_all [AddInductive.ConstructorTypeValidationTrace.spineLength, + Expr.isForall] + +private theorem constructorTypeValidationTrace_eq_terminal + {stats : AddInductive.InductiveStats} {isUnsafe : Bool} + {familyIdx : Nat} {ctor : Name} {context : AddInductive.Context} + {source : Expr} {argIdx fuel : Nat} + (trace : AddInductive.ConstructorTypeValidationTrace stats isUnsafe + familyIdx ctor context source argIdx (fuel + 1)) + (terminal : source.isForall = false) + (valid : AddInductive.isValidIndAppIdx stats source familyIdx = true) : + trace = .terminal context source fuel argIdx terminal valid := by + cases trace with + | parameter context fuel argIdx name domain body binderInfo param + parameterType parameterAt parameterTypeRun defeq tail => + have impossible : + (Expr.forallE name domain body binderInfo).isForall = true := rfl + rw [impossible] at terminal + contradiction + | ordinary context fuel argIdx name domain body binderInfo sortResult + noParameter ensureType universeTrace positivity tail => + have impossible : + (Expr.forallE name domain body binderInfo).isForall = true := rfl + rw [impossible] at terminal + contradiction + | terminal => rfl + +set_option maxHeartbeats 10000000 in +private def annotatedPiStagedPostFamilyInput : + VInductDecl.StagedNormalizationCandidatePostFamilyInput + annotatedPiFamilyCandidateContext annotatedPiCtorCandidateContext + outParamEnv [] annotatedPiNormalizationCandidate + annotatedPiRawDecl where + universeInput := annotatedPiStagedUniverseInput + alignment := by + change AddInductive.ConstructorCandidateAlignmentTrace + annotatedPiStagedUniverseInput.staged.family.validation.stats false 0 + annotatedPiConstructorValidationContext + annotatedPiStagedUniverseInput.staged.constructorValidation.trace + (.cons annotatedPiConstructorCandidate .nil) + generalize htrace : + annotatedPiStagedUniverseInput.staged.constructorValidation.trace = trace + cases trace with + | cons seen head constructors constructorFresh constructorClosed + constructorRootCheck typeTrace listTailTrace => + cases typeTrace with + | parameter context fuel argIdx name domain body binderInfo param + parameterType parameterAt parameterTypeRun defeq tail => + rw [annotatedPiStagedParams_zero] at parameterAt + contradiction + | terminal context source fuel argIdx terminal valid => + rw [annotatedPiKernelCtor_isForall] at terminal + contradiction + | ordinary context fuel argIdx name domain body binderInfo sortResult + noParameter ensureType universeTrace positivity tail => + cases listTailTrace + cases positivity with + | skipped isUnsafe_eq => contradiction + | safe isUnsafe_eq positivityTrace => + cases positivityTrace with + | absent context source result fuel whnf occurs => + change AddInductive.CandidateWhnfStep.Valid + ⟨{ annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext with + env := annotatedPiCtorCandidateContext.env }, + annotatedPiInnerKernel, result⟩ at whnf + rw [annotatedPiConstructorValidationContext_eq] at whnf + change TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.whnf annotatedPiInnerKernel) = + .ok result at whnf + rw [annotatedPiInner_whnfM] at whnf + cases whnf + rw [annotatedPiStagedStats_eq, + annotatedPiInner_stats_hasIndOcc] at occurs + contradiction + | target context source result fuel targetIdx whnf occurs + terminal valid => + change AddInductive.CandidateWhnfStep.Valid + ⟨{ annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext with + env := annotatedPiCtorCandidateContext.env }, + annotatedPiInnerKernel, result⟩ at whnf + rw [annotatedPiConstructorValidationContext_eq] at whnf + change TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.whnf annotatedPiInnerKernel) = + .ok result at whnf + rw [annotatedPiInner_whnfM] at whnf + cases whnf + simp [annotatedPiInnerKernel, Expr.isForall] at terminal + | forallE positivityContext positivitySource positivityFuel + positivityName positivityDomain positivityBody + positivityBinderInfo positivityWhnf positivityOccurs + positivityDomainFree positivityTail => + change AddInductive.CandidateWhnfStep.Valid + ⟨{ annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext with + env := annotatedPiCtorCandidateContext.env }, + annotatedPiInnerKernel, + .forallE positivityName positivityDomain positivityBody + positivityBinderInfo⟩ at positivityWhnf + rw [annotatedPiConstructorValidationContext_eq] at positivityWhnf + change TypeChecker.M.run annotatedPiCtorCandidateContext.env + annotatedPiCtorCandidateContext.safety + annotatedPiCtorCandidateContext.lctx + annotatedPiCtorCandidateContext.lparams + annotatedPiCtorCandidateContext.fuel + (TypeChecker.whnf annotatedPiInnerKernel) = + .ok (.forallE positivityName positivityDomain + positivityBody positivityBinderInfo) at positivityWhnf + rw [annotatedPiInner_whnfM] at positivityWhnf + cases positivityWhnf + have hTailSpine : tail.spineLength = 0 := + constructorTypeValidationTrace_spineLength_zero tail (by + simp only [annotatedPiConst_instantiate1, + Expr.isForall]) + cases positivityTail with + | absent context source result fuel whnf occurs => + have whnf' : AddInductive.CandidateWhnfStep.Valid + ⟨({ annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext with + env := annotatedPiCtorCandidateContext.env }).pushLocalDecl + `p .default (AddInductive.consumeTypeAnnotations + annotatedPiRawDomainKernel), + .const ``AnnotatedPi [], result⟩ := by + simpa only [annotatedPiConst_instantiate1] using whnf + have hresult := + annotatedPiValidationInnerBody_whnf_eq whnf' + subst result + rw [annotatedPiStagedStats_eq, + annotatedPiConst_hasIndOcc] at occurs + contradiction + | forallE context source fuel name domain body binderInfo + whnf occurs domainFree positivityTail => + have whnf' : AddInductive.CandidateWhnfStep.Valid + ⟨({ annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext with + env := annotatedPiCtorCandidateContext.env }).pushLocalDecl + `p .default (AddInductive.consumeTypeAnnotations + annotatedPiRawDomainKernel), + .const ``AnnotatedPi [], + .forallE name domain body binderInfo⟩ := by + simpa only [annotatedPiConst_instantiate1] using whnf + have hresult := + annotatedPiValidationInnerBody_whnf_eq whnf' + contradiction + | target targetContext targetSource targetResult targetFuel + targetIdx targetWhnf targetOccurs targetTerminal + targetValid => + have targetWhnfNormalized : + AddInductive.CandidateWhnfStep.Valid + ⟨({ annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext with + env := annotatedPiCtorCandidateContext.env }).pushLocalDecl + `p .default (AddInductive.consumeTypeAnnotations + annotatedPiRawDomainKernel), + .const ``AnnotatedPi [], targetResult⟩ := by + simpa only [annotatedPiConst_instantiate1] using + targetWhnf + have hresult := + annotatedPiValidationInnerBody_whnf_eq + targetWhnfNormalized + subst targetResult + simp only [annotatedPiConst_instantiate1] at tail ⊢ + cases tail + change AddInductive.CandidateCheckTypeObservation + annotatedPiConstructorValidationContext.withEmptyLocalContext + annotatedPiKernelCtor.type at constructorRootCheck + let rootChecked := + AddInductive.ConstructorCheckedExpr.ofClosedRoot + constructorClosed constructorRootCheck + let innerChecked : AddInductive.ConstructorCheckedExpr + annotatedPiCtorCandidateContext + annotatedPiInnerKernel := + .ofRun (by + simp [annotatedPiInnerKernel, + annotatedPiRawDomainKernel, FVarsIn, + Level.hasMVar']) + annotatedPiInnerCheckTypeStep_valid + let viewInnerChecked : AddInductive.ConstructorCheckedExpr + annotatedPiCtorCandidateContext + annotatedPiViewInnerKernel := + .ofRun (by + simp [annotatedPiViewInnerKernel, FVarsIn, + Level.hasMVar']) + annotatedPiViewInnerCheckTypeStep_valid + let domainChecked : AddInductive.ConstructorCheckedExpr + annotatedPiCtorCandidateContext + annotatedPiRawDomainKernel := + .ofRun (by + simp [annotatedPiRawDomainKernel, FVarsIn, + Level.hasMVar']) + annotatedPiDomainCheckTypeStep_valid + let sortChecked : AddInductive.ConstructorCheckedExpr + annotatedPiCtorCandidateContext (.sort .zero) := + .ofRun (by simp [FVarsIn, Level.hasMVar']) + annotatedPiSortZeroCheckTypeStep_valid + let innerBodyChecked : + AddInductive.ConstructorCheckedExpr + annotatedPiInnerBodyCandidateContext + (.const ``AnnotatedPi []) := + .ofRun (by simp [FVarsIn]) + annotatedPiInnerBodyCheckTypeStep_valid + let outerBodyChecked : + AddInductive.ConstructorCheckedExpr + annotatedPiOuterBodyCandidateContext + (.const ``AnnotatedPi []) := + .ofRun (by simp [FVarsIn]) + annotatedPiOuterBodyCheckTypeStep_valid + let innerChecked' := annotatedPiAlignChecked + annotatedPiConstructorValidationContext_def_eq rfl + innerChecked + let viewInnerChecked' := annotatedPiAlignChecked + annotatedPiConstructorValidationContext_def_eq + annotatedPiAlignedViewInnerKernel_eq viewInnerChecked + let consumedInnerChecked := annotatedPiAlignChecked + annotatedPiConstructorValidationContext_def_eq + annotatedPiConsumeInner innerChecked + let domainChecked' := annotatedPiAlignChecked + annotatedPiConstructorValidationContext_def_eq rfl + domainChecked + let sortChecked' := annotatedPiAlignChecked + annotatedPiConstructorValidationContext_def_eq + annotatedPiConsumeRawDomain sortChecked + let innerBodyChecked' := annotatedPiAlignChecked + annotatedPiValidationInnerBodyContext_eq rfl + innerBodyChecked + let innerBodyPositivityChecked := + annotatedPiAlignChecked + annotatedPiValidationInnerBodyContext_eq + (annotatedPiConst_instantiate1 + annotatedPiConstructorValidationContext.freshExpr) + innerBodyChecked + let outerBodyChecked' := annotatedPiAlignChecked + annotatedPiValidationOuterBodyContext_eq rfl + outerBodyChecked + let outerBodySourceChecked := annotatedPiAlignChecked + annotatedPiValidationOuterBodyContext_eq + (annotatedPiConst_instantiate1 + annotatedPiConstructorValidationContext.freshExpr) + outerBodyChecked + have outerTailView_eq : + ((Expr.const ``AnnotatedPi []).abstract + #[annotatedPiCtorCandidateContext.freshExpr] + |>.instantiate1 + annotatedPiConstructorValidationContext.freshExpr) = + .const ``AnnotatedPi [] := by + simp [annotatedPiConst_abstract_singleton] + let outerBodyViewChecked := annotatedPiAlignChecked + annotatedPiValidationOuterBodyContext_eq + outerTailView_eq outerBodyChecked + let innerAnnotations : + AddInductive.CandidateIsDefEqObservation + annotatedPiCtorCandidateContext + annotatedPiInnerKernel annotatedPiInnerKernel := + ⟨AddInductive.candidateIsDefEqRefl + annotatedPiCtorCandidateContext + annotatedPiInnerKernel⟩ + let innerAnnotations' := annotatedPiAlignIsDefEq + annotatedPiConstructorValidationContext_def_eq rfl + annotatedPiConsumeInner innerAnnotations + let domainAnnotations : + AddInductive.CandidateIsDefEqObservation + annotatedPiCtorCandidateContext + annotatedPiRawDomainKernel (.sort .zero) := + ⟨annotatedPiDomainAnnotationsEq⟩ + let domainAnnotations' := annotatedPiAlignIsDefEq + annotatedPiConstructorValidationContext_def_eq rfl + annotatedPiConsumeRawDomain domainAnnotations + have sortCheckedInferred : + sortChecked'.observation.inferred = + .sort (.succ .zero) := by + apply sortChecked'.inferred_eq_of_run + rw [annotatedPiConstructorValidationContext_def_eq, + annotatedPiConsumeRawDomain] + exact annotatedPiSortZeroCheckTypeStep_valid + have rootStoredSpine : + annotatedPiConstructorCandidate.type.trace.storedSpine = + true := by + change annotatedPiCtorCandidate.trace.storedSpine = true + exact annotatedPiCtorCandidate_storedSpine + have rootDepth : + annotatedPiConstructorCandidate.type.context.fuel.recDepth = + annotatedPiConstructorValidationContext.fuel.recDepth := by + rfl + let constructorTailTrace := + AddInductive.ConstructorTypeValidationTrace.terminal + (stats := annotatedPiStagedUniverseInput.staged.family.validation.stats) + (isUnsafe := false) (familyIdx := 0) + (ctor := annotatedPiKernelCtor.name) + (annotatedPiConstructorValidationContext.pushLocalDecl + annotatedPiOuterName .default + (AddInductive.consumeTypeAnnotations + annotatedPiInnerKernel)) + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiConstructorValidationContext.freshExpr) + 998 1 + (by + simp only [annotatedPiConst_instantiate1, + Expr.isForall]) + (by + simp only [annotatedPiConst_instantiate1] + assumption) + have tail_eq : tail = constructorTailTrace := by + unfold constructorTailTrace + apply constructorTypeValidationTrace_eq_terminal + let positivityTargetTrace := + AddInductive.ConstructorPositivityTrace.target + (stats := annotatedPiStagedUniverseInput.staged.family.validation.stats) + (ctor := annotatedPiKernelCtor.name) + (argIdx := 0) + (annotatedPiConstructorValidationContext.pushLocalDecl + `p .default + (AddInductive.consumeTypeAnnotations + annotatedPiRawDomainKernel)) + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiConstructorValidationContext.freshExpr) + (.const ``AnnotatedPi []) 998 targetIdx + targetWhnf targetOccurs targetTerminal targetValid + let nestedPositivityTrace := + AddInductive.ConstructorPositivityTrace.forallE + (stats := annotatedPiStagedUniverseInput.staged.family.validation.stats) + (ctor := annotatedPiKernelCtor.name) + (argIdx := 0) + annotatedPiConstructorValidationContext + annotatedPiInnerKernel 999 `p + annotatedPiRawDomainKernel + (.const ``AnnotatedPi []) .default positivityWhnf + positivityOccurs positivityDomainFree + positivityTargetTrace + let positivityModeTrace := + AddInductive.ConstructorPositivityModeTrace.safe + isUnsafe_eq nestedPositivityTrace + let headValidationTrace := + AddInductive.ConstructorTypeValidationTrace.ordinary + annotatedPiConstructorValidationContext 999 0 + annotatedPiOuterName annotatedPiInnerKernel + (.const ``AnnotatedPi []) .default sortResult + noParameter ensureType universeTrace + positivityModeTrace constructorTailTrace + have rootSpineLength' : + annotatedPiConstructorCandidate.type.trace.spineLength = + headValidationTrace.spineLength := by + rfl + have positivityTargetAlignment : + AddInductive.ConstructorPositivityAlignmentTrace + positivityTargetTrace := + .target innerBodyPositivityChecked + have positivityAlignment : + AddInductive.ConstructorPositivityModeAlignmentTrace + positivityModeTrace := + .safe <| .forallE innerChecked' domainChecked' + sortChecked' (.succ .zero) + sortCheckedInferred (by + rw [annotatedPiConstructorValidationContext_def_eq] + exact annotatedPiCtorCandidateFresh) + domainAnnotations' positivityTargetTrace + positivityTargetAlignment + have outerTailAlignment : + AddInductive.ConstructorViewAlignmentTrace + constructorTailTrace + ((Expr.const ``AnnotatedPi []).abstract + #[annotatedPiCtorCandidateContext.freshExpr] + |>.instantiate1 + annotatedPiConstructorValidationContext.freshExpr) := by + simp only [annotatedPiConst_abstract_singleton, + annotatedPiConst_instantiate1] + exact .terminal outerBodySourceChecked + outerBodyChecked' + (by simp [Expr.isForall]) (by assumption) + have headAlignment : + AddInductive.ConstructorViewAlignmentTrace + headValidationTrace + annotatedPiConstructorCandidate.type.view := by + change AddInductive.ConstructorViewAlignmentTrace + headValidationTrace + annotatedPiAlignedViewCtorKernel + exact AddInductive.ConstructorViewAlignmentTrace.ordinary + innerChecked' viewInnerChecked' + ⟨annotatedPiInnerView_isDefEq⟩ + consumedInnerChecked + positivityModeTrace + positivityAlignment (by + rw [annotatedPiConstructorValidationContext_def_eq] + exact annotatedPiCtorCandidateFresh) + innerAnnotations' constructorTailTrace + outerTailAlignment + rw [tail_eq] + exact + AddInductive.ConstructorCandidateAlignmentTrace.cons + (seen := ∅) (fresh := constructorFresh) + (closed := constructorClosed) + (rootCheck := constructorRootCheck) + (typeTrace := headValidationTrace) + (tailTrace := + AddInductive.ConstructorListValidationTrace.nil + ((∅ : NameSet).insert + annotatedPiKernelCtor.name)) + (candidate := annotatedPiConstructorCandidate) + (candidates := AddInductive.CandidateList.nil) + rootChecked rootStoredSpine rootSpineLength' + rootDepth headAlignment + (AddInductive.ConstructorCandidateAlignmentTrace.nil + (stats := annotatedPiStagedUniverseInput.staged.family.validation.stats) + (isUnsafe := false) (familyIdx := 0) + (context := annotatedPiConstructorValidationContext) + ((∅ : NameSet).insert + annotatedPiKernelCtor.name)) + +private theorem annotatedPiPreFamilySortZeroCheckTypeStep_valid + (context : AddInductive.Context) + (depth : context.fuel.recDepth = 10000) : + AddInductive.CandidateCheckTypeStep.Valid + ⟨context, .sort .zero, .sort (.succ .zero)⟩ := by + unfold AddInductive.CandidateCheckTypeStep.Valid + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType (.sort .zero) false + (TypeChecker.Methods.withFuel context.fuel.recDepth) + context.toTypeChecker ({} : TypeChecker.State)) = _ + rw [depth] + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' (.sort .zero) false + (TypeChecker.Methods.withFuel 9999) + context.toTypeChecker ({} : TypeChecker.State)) = _ + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', + annotatedPi_checkLevelZero, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + rfl + +private theorem annotatedPiPreFamilySortOneCheckTypeStep_valid + (context : AddInductive.Context) + (depth : context.fuel.recDepth = 10000) : + AddInductive.CandidateCheckTypeStep.Valid + ⟨context, .sort (.succ .zero), + .sort (.succ (.succ .zero))⟩ := by + unfold AddInductive.CandidateCheckTypeStep.Valid + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType (.sort (.succ .zero)) false + (TypeChecker.Methods.withFuel context.fuel.recDepth) + context.toTypeChecker ({} : TypeChecker.State)) = _ + rw [depth] + change Except.map (fun x : Expr × TypeChecker.State => x.1) + (TypeChecker.Inner.inferType' (.sort (.succ .zero)) false + (TypeChecker.Methods.withFuel 9999) + context.toTypeChecker ({} : TypeChecker.State)) = _ + unfold TypeChecker.Inner.inferType' + simp [Expr.hasLooseBVars, Expr.looseBVarRange', + annotatedPi_checkLevelSuccZero, Bind.bind, ReaderT.bind, + StateT.bind, Except.bind] + rfl + +private def annotatedPiPreFamilySortZeroInferState : TypeChecker.State := + { ({} : TypeChecker.State) with + inferTypeI := ({} : TypeChecker.State).inferTypeI.insert + (.sort .zero) (.sort (.succ .zero)) } + +private theorem annotatedPiPreFamilySortZeroInferTypeInner + (context : AddInductive.Context) + (depth : context.fuel.recDepth = 10000) : + TypeChecker.Inner.inferType (.sort .zero) true + (TypeChecker.Methods.withFuel context.fuel.recDepth) + context.toTypeChecker ({} : TypeChecker.State) = + .ok (.sort (.succ .zero), + annotatedPiPreFamilySortZeroInferState) := by + rw [depth] + change TypeChecker.Inner.inferType' (.sort .zero) true + (TypeChecker.Methods.withFuel 9999) context.toTypeChecker + ({} : TypeChecker.State) = _ + unfold TypeChecker.Inner.inferType' + simp [annotatedPiPreFamilySortZeroInferState, + Expr.hasLooseBVars, Expr.looseBVarRange', Bind.bind, + ReaderT.bind, StateT.bind, Except.bind] + +private theorem annotatedPiPreFamilySortZeroEnsureTypeStep_valid + (context : AddInductive.Context) + (depth : context.fuel.recDepth = 10000) : + AddInductive.ConstructorEnsureTypeStep.Valid + ⟨context, .sort .zero, .sort (.succ .zero)⟩ := by + unfold AddInductive.ConstructorEnsureTypeStep.Valid + TypeChecker.ensureType TypeChecker.inferType TypeChecker.ensureSort + TypeChecker.RecM.run TypeChecker.M.run + simp only [readThe, MonadReaderOf.read, ReaderT.read, + Bind.bind, ReaderT.bind, StateT.bind, Except.bind, + Pure.pure, StateT.pure, Except.pure, StateT.run', + Functor.map, Except.map] + rw [show TypeChecker.Inner.inferType (.sort .zero) true + (TypeChecker.Methods.withFuel context.fuel.recDepth) + { env := context.env + lctx := context.lctx + safety := context.safety + lparams := context.lparams + fuel := context.fuel } + ({} : TypeChecker.State) = + .ok (.sort (.succ .zero), + annotatedPiPreFamilySortZeroInferState) by + simpa [AddInductive.Context.toTypeChecker] using + annotatedPiPreFamilySortZeroInferTypeInner context depth] + rfl + +private theorem annotatedPiPreFamilySafetyRun : + AddInductive.checkConstructorPreFamilySafety + annotatedPiStagedUniverseInput.staged.family.validation.stats + annotatedPiNormalizationCandidate.families.singleton.familyType.type.view + annotatedPiNormalizationCandidate.families.singleton.constructors + annotatedPiNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext = + .ok () := by + change AddInductive.checkConstructorPreFamilySafety + annotatedPiInductiveStats (.sort (.succ .zero)) + (.cons annotatedPiConstructorCandidate .nil) + annotatedPiFamilyCandidateContext = .ok () + have consumeSortZero : AddInductive.consumeTypeAnnotations + (.sort .zero) = .sort .zero := by + simp [AddInductive.consumeTypeAnnotations] + let nestedContext := annotatedPiFamilyCandidateContext.pushLocalDecl + `p .default (AddInductive.consumeTypeAnnotations (.sort .zero)) + let resultContext := annotatedPiFamilyCandidateContext.advanceFresh + let rootSortZero : AddInductive.ConstructorCheckedExpr + annotatedPiFamilyCandidateContext (.sort .zero) := + .ofRun (by simp [FVarsIn, Level.hasMVar']) + (annotatedPiPreFamilySortZeroCheckTypeStep_valid + annotatedPiFamilyCandidateContext rfl) + let rootSortOne : AddInductive.ConstructorCheckedExpr + annotatedPiFamilyCandidateContext (.sort (.succ .zero)) := + .ofRun (by simp [FVarsIn, Level.hasMVar']) + (annotatedPiPreFamilySortOneCheckTypeStep_valid + annotatedPiFamilyCandidateContext rfl) + let nestedSortOne : AddInductive.ConstructorCheckedExpr + nestedContext (.sort (.succ .zero)) := + .ofRun (by simp [FVarsIn, Level.hasMVar']) + (annotatedPiPreFamilySortOneCheckTypeStep_valid nestedContext rfl) + let resultSortOne : AddInductive.ConstructorCheckedExpr + resultContext (.sort (.succ .zero)) := + .ofRun (by simp [FVarsIn, Level.hasMVar']) + (annotatedPiPreFamilySortOneCheckTypeStep_valid resultContext rfl) + let rootEnsure : AddInductive.ConstructorEnsureTypeObservation + annotatedPiFamilyCandidateContext (.sort .zero) := + ⟨.sort (.succ .zero), + annotatedPiPreFamilySortZeroEnsureTypeStep_valid + annotatedPiFamilyCandidateContext rfl⟩ + let consumedSortZero : AddInductive.ConstructorCheckedExpr + annotatedPiFamilyCandidateContext + (AddInductive.consumeTypeAnnotations (.sort .zero)) := by + rw [consumeSortZero] + exact rootSortZero + let annotations : AddInductive.CandidateIsDefEqObservation + annotatedPiFamilyCandidateContext (.sort .zero) + (AddInductive.consumeTypeAnnotations (.sort .zero)) := + ⟨by + rw [consumeSortZero] + exact AddInductive.candidateIsDefEqRefl + annotatedPiFamilyCandidateContext (.sort .zero)⟩ + have rootFresh : annotatedPiFamilyCandidateContext.lctx.find? + annotatedPiFamilyCandidateContext.freshFVarId = none := by + have h := LocalContext.WF.find?_eq_find?_toList + (fv := annotatedPiFamilyCandidateContext.freshFVarId) + LocalContext.WF.nil + change + ({ fvarIdToDecl := PersistentHashMap.empty + decls := PersistentArray.empty + auxDeclToFullName := Std.TreeMap.empty } : LocalContext).find? + annotatedPiFamilyCandidateContext.freshFVarId = none + rw [h] + simp [LocalContext.toList] + let rootSpine : AddInductive.ConstructorPreFamilyIndexSpineTrace + annotatedPiFamilyCandidateContext (.sort (.succ .zero)) [] := + .nil annotatedPiFamilyCandidateContext (.sort (.succ .zero)) + rootSortOne rfl + let nestedSpine : AddInductive.ConstructorPreFamilyIndexSpineTrace + nestedContext (.sort (.succ .zero)) [] := + .nil nestedContext (.sort (.succ .zero)) nestedSortOne rfl + let resultSpine : AddInductive.ConstructorPreFamilyIndexSpineTrace + resultContext (.sort (.succ .zero)) [] := + .nil resultContext (.sort (.succ .zero)) resultSortOne rfl + have valid : AddInductive.isValidIndAppIdx annotatedPiInductiveStats + (.const ``AnnotatedPi []) 0 = true := + annotatedPiConst_isValidIndAppIdx + have targetArgs : + (Expr.const ``AnnotatedPi []).getAppArgs.toList.drop + annotatedPiInductiveStats.params.size = [] := by + rfl + obtain ⟨nestedTargetSpine, nestedTargetSpineRun⟩ : + ∃ nestedTargetSpine : + AddInductive.ConstructorPreFamilyIndexSpineTrace nestedContext + (.sort (.succ .zero)) + ((Expr.const ``AnnotatedPi []).getAppArgs.toList.drop + annotatedPiInductiveStats.params.size), + AddInductive.ConstructorPreFamilyIndexSpineTrace.build nestedContext + (.sort (.succ .zero)) + ((Expr.const ``AnnotatedPi []).getAppArgs.toList.drop + annotatedPiInductiveStats.params.size) = + .ok nestedTargetSpine := by + rw [targetArgs] + exact ⟨nestedSpine, nestedSpine.build_eq⟩ + let targetTrace : AddInductive.ConstructorPreFamilyRecursiveTrace + annotatedPiInductiveStats 0 (.sort (.succ .zero)) nestedContext + (.const ``AnnotatedPi []) 999 := + .target nestedContext (.const ``AnnotatedPi []) valid nestedTargetSpine + have targetRun : + AddInductive.ConstructorPreFamilyRecursiveTrace.build + annotatedPiInductiveStats 0 (.sort (.succ .zero)) nestedContext + (.const ``AnnotatedPi []) 999 = .ok targetTrace := by + simp only [AddInductive.ConstructorPreFamilyRecursiveTrace.build] + rw [dif_pos valid, nestedTargetSpineRun] + rfl + obtain ⟨recursiveTailTrace, recursiveTailRun⟩ : + ∃ recursiveTailTrace : + AddInductive.ConstructorPreFamilyRecursiveTrace + annotatedPiInductiveStats 0 (.sort (.succ .zero)) nestedContext + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiFamilyCandidateContext.freshExpr) 999, + AddInductive.ConstructorPreFamilyRecursiveTrace.build + annotatedPiInductiveStats 0 (.sort (.succ .zero)) nestedContext + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiFamilyCandidateContext.freshExpr) 999 = + .ok recursiveTailTrace := by + rw [annotatedPiConst_instantiate1] + exact ⟨targetTrace, targetRun⟩ + let recursiveTrace : AddInductive.ConstructorPreFamilyRecursiveTrace + annotatedPiInductiveStats 0 (.sort (.succ .zero)) + annotatedPiFamilyCandidateContext annotatedPiViewInnerKernel + annotatedPiFamilyCandidateContext.fuel.inductiveFuel := + .forallE annotatedPiFamilyCandidateContext `p (.sort .zero) + (.const ``AnnotatedPi []) .default rootSortZero rootEnsure + consumedSortZero annotations rootFresh (by + simpa only [nestedContext] using recursiveTailTrace) + have recursiveRun : + AddInductive.ConstructorPreFamilyRecursiveTrace.build + annotatedPiInductiveStats 0 (.sort (.succ .zero)) + annotatedPiFamilyCandidateContext annotatedPiViewInnerKernel + annotatedPiFamilyCandidateContext.fuel.inductiveFuel = + .ok recursiveTrace := by + change AddInductive.ConstructorPreFamilyRecursiveTrace.build + annotatedPiInductiveStats 0 (.sort (.succ .zero)) + annotatedPiFamilyCandidateContext annotatedPiViewInnerKernel 1000 = + .ok recursiveTrace + simp only [annotatedPiViewInnerKernel, + AddInductive.ConstructorPreFamilyRecursiveTrace.build] + rw [rootSortZero.check_eq, rootEnsure.observe_eq, + consumedSortZero.check_eq] + simp only [Bind.bind, Except.bind] + rw [annotations.observe_eq] + simp only [] + rw [dif_pos rootFresh] + rw [recursiveTailRun] + rfl + have resultIndependent : AddInductive.constructorIndependentOf + (.const ``AnnotatedPi []) + [annotatedPiFamilyCandidateContext.freshFVarId] = true := by + rfl + obtain ⟨resultTargetSpine, resultTargetSpineRun⟩ : + ∃ resultTargetSpine : + AddInductive.ConstructorPreFamilyIndexSpineTrace resultContext + (.sort (.succ .zero)) + ((Expr.const ``AnnotatedPi []).getAppArgs.toList.drop + annotatedPiInductiveStats.params.size), + AddInductive.ConstructorPreFamilyIndexSpineTrace.build resultContext + (.sort (.succ .zero)) + ((Expr.const ``AnnotatedPi []).getAppArgs.toList.drop + annotatedPiInductiveStats.params.size) = + .ok resultTargetSpine := by + rw [targetArgs] + exact ⟨resultSpine, resultSpine.build_eq⟩ + let terminalTrace : AddInductive.ConstructorPreFamilyViewTrace + annotatedPiInductiveStats 0 (.sort (.succ .zero)) resultContext + (.const ``AnnotatedPi []) 1 + [annotatedPiFamilyCandidateContext.freshFVarId] true := + .terminal resultContext (.const ``AnnotatedPi []) 1 + [annotatedPiFamilyCandidateContext.freshFVarId] true valid + resultIndependent resultTargetSpine + have terminalRun : + AddInductive.ConstructorPreFamilyViewTrace.build + annotatedPiInductiveStats 0 (.sort (.succ .zero)) resultContext + (.const ``AnnotatedPi []) 1 + [annotatedPiFamilyCandidateContext.freshFVarId] true 999 = + .ok terminalTrace := by + simp only [AddInductive.ConstructorPreFamilyViewTrace.build] + rw [dif_pos valid, dif_pos resultIndependent, resultTargetSpineRun] + rfl + obtain ⟨viewTailTrace, viewTailRun⟩ : + ∃ viewTailTrace : AddInductive.ConstructorPreFamilyViewTrace + annotatedPiInductiveStats 0 (.sort (.succ .zero)) resultContext + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiFamilyCandidateContext.freshExpr) + 1 [annotatedPiFamilyCandidateContext.freshFVarId] true, + AddInductive.ConstructorPreFamilyViewTrace.build + annotatedPiInductiveStats 0 (.sort (.succ .zero)) resultContext + ((Expr.const ``AnnotatedPi []).instantiate1 + annotatedPiFamilyCandidateContext.freshExpr) + 1 [annotatedPiFamilyCandidateContext.freshFVarId] true 999 = + .ok viewTailTrace := by + rw [annotatedPiConst_instantiate1] + exact ⟨terminalTrace, terminalRun⟩ + have noParameter : annotatedPiInductiveStats.params[0]? = none := rfl + have recursive : AddInductive.hasIndOcc annotatedPiInductiveStats.indConsts + annotatedPiViewInnerKernel = true := by + simp [AddInductive.hasIndOcc, annotatedPiInductiveStats, + annotatedPiViewInnerKernel, Expr.constName!] + have fieldIndependent : AddInductive.constructorIndependentOf + annotatedPiViewInnerKernel [] = true := by + simp [AddInductive.constructorIndependentOf, + annotatedPiViewInnerKernel] + let viewTrace : AddInductive.ConstructorPreFamilyViewTrace + annotatedPiInductiveStats 0 (.sort (.succ .zero)) + annotatedPiFamilyCandidateContext annotatedPiViewCtorKernel 0 [] false := + .recursive annotatedPiFamilyCandidateContext 0 [] false + annotatedPiOuterName annotatedPiViewInnerKernel + (.const ``AnnotatedPi []) .default noParameter recursive + fieldIndependent recursiveTrace rootFresh (by + simpa only [resultContext] using viewTailTrace) + have viewRun : + AddInductive.ConstructorPreFamilyViewTrace.build + annotatedPiInductiveStats 0 (.sort (.succ .zero)) + annotatedPiFamilyCandidateContext annotatedPiViewCtorKernel 0 [] + false 1000 = .ok viewTrace := by + simp only [annotatedPiViewCtorKernel, + AddInductive.ConstructorPreFamilyViewTrace.build] + split + · rename_i parameter parameterAt + rw [noParameter] at parameterAt + contradiction + · split + · rename_i nonrecursive + rw [recursive] at nonrecursive + contradiction + · rw [dif_pos fieldIndependent] + rw [recursiveRun] + simp only [Bind.bind, Except.bind] + rw [dif_pos rootFresh] + rw [viewTailRun] + rfl + have candidateViewEq : annotatedPiConstructorCandidate.type.view = + annotatedPiViewCtorKernel := by + change annotatedPiCtorCandidate.trace.view = annotatedPiViewCtorKernel + change annotatedPiAlignedViewCtorKernel = annotatedPiViewCtorKernel + exact annotatedPiAlignedViewCtorKernel_eq + obtain ⟨headTrace, headRun⟩ : + ∃ headTrace : AddInductive.ConstructorPreFamilyViewTrace + annotatedPiInductiveStats 0 (.sort (.succ .zero)) + annotatedPiFamilyCandidateContext + annotatedPiConstructorCandidate.type.view 0 [] false, + AddInductive.ConstructorPreFamilyViewTrace.build + annotatedPiInductiveStats 0 (.sort (.succ .zero)) + annotatedPiFamilyCandidateContext + annotatedPiConstructorCandidate.type.view 0 [] false 1000 = + .ok headTrace := by + rw [candidateViewEq] + exact ⟨viewTrace, viewRun⟩ + let listTrace : AddInductive.ConstructorPreFamilyListTrace + annotatedPiInductiveStats 0 (.sort (.succ .zero)) + annotatedPiFamilyCandidateContext + (.cons annotatedPiConstructorCandidate .nil) := + .cons headTrace .nil + have listRun : + AddInductive.ConstructorPreFamilyListTrace.build + annotatedPiInductiveStats 0 (.sort (.succ .zero)) + annotatedPiFamilyCandidateContext + (.cons annotatedPiConstructorCandidate .nil) = .ok listTrace := by + simp only [AddInductive.ConstructorPreFamilyListTrace.build] + rw [show annotatedPiFamilyCandidateContext.fuel.inductiveFuel = 1000 by + rfl, headRun] + rfl + have parametersRun : AddInductive.instantiateFamilyParameters + (.sort (.succ .zero)) annotatedPiInductiveStats.params.toList = + .ok (.sort (.succ .zero)) := by + rfl + unfold AddInductive.checkConstructorPreFamilySafety + have translationUnique : + (AddInductive.theoryTranslationUnique (.sort (.succ .zero)) && + (AddInductive.CandidateList.cons annotatedPiConstructorCandidate + (AddInductive.CandidateList.nil : AddInductive.CandidateList + AddInductive.CandidateConstructor [])).viewTranslationUnique) = + true := by + simp [AddInductive.theoryTranslationUnique, + AddInductive.CandidateList.viewTranslationUnique, + AddInductive.CandidateExprTrace.viewTranslationUnique, + AddInductive.CandidateExprTrace.view, + annotatedPiConstructorCandidate, annotatedPiCtorCandidate, + annotatedPiCtorCandidateTrace, annotatedPiInnerCandidateTrace, + annotatedPiDomainCandidateTrace, annotatedPiInnerBodyCandidateTrace, + annotatedPiOuterBodyCandidateTrace, + annotatedPiConst_abstract_singleton] + rw [if_pos translationUnique] + rw [parametersRun] + simp only [Bind.bind, Except.bind] + rw [listRun] + rfl + +private def annotatedPiStagedPreFamilyInput : + VInductDecl.StagedNormalizationCandidatePreFamilyInput + annotatedPiFamilyCandidateContext annotatedPiCtorCandidateContext + outParamEnv [] annotatedPiNormalizationCandidate annotatedPiRawDecl := + VInductDecl.StagedNormalizationCandidatePreFamilyInput.ofRun + annotatedPiStagedPostFamilyInput annotatedPiPreFamilySafetyRun + +/-- AnnotatedPi's retained validator telescope and candidate telescope admit +the complete post-family semantic interpretation, including the nested +annotation-bearing recursive field. -/ +theorem annotatedPiProducedPostFamilySemantic_exists : + Nonempty (VInductDecl.ProducedNormalizationCandidatePostFamilySemanticRun + annotatedPiStagedPostFamilyInput) := + annotatedPiStagedPostFamilyInput.exists + +/-- AnnotatedPi's recursive outer field is omitted from the pre-family local +context while its nested Pi binder and both family-index spines receive the +exact verified family-free interpretation. -/ +theorem annotatedPiProducedPreFamilySemantic_exists : + Nonempty (VInductDecl.ProducedNormalizationCandidatePreFamilySemanticRun + annotatedPiStagedPreFamilyInput) := + annotatedPiStagedPreFamilyInput.exists + +/-- The exact family/constructor producer traversals and verified translations +automatically determine the complete retained AnnotatedPi hierarchy, including +its nested annotation-consuming constructor trace. -/ +theorem annotatedPiProducedSemanticHierarchy_exists : + Nonempty (VInductDecl.ProducedNormalizationCandidateSemanticRun + annotatedPiFamilyCandidateContext annotatedPiCtorCandidateContext + outParamEnv [] annotatedPiNormalizationCandidate + annotatedPiRawDecl) := + annotatedPiStagedUniverseInput.exists + +def annotatedPiNormalizationCandidateRun : + VInductDecl.NormalizationCandidateRun outParamEnv [] + annotatedPiNormalizationCandidate annotatedPiRawDecl where + raw := annotatedPiRawType + raw_types_eq := rfl + uvars_eq := rfl + family := annotatedPiCandidateFamilyRun + +example : annotatedPiNormalizationCandidateRun.viewDecl = + annotatedPiViewDecl := rfl + +theorem annotatedPiCandidateNormalization_eq : + annotatedPiNormalizationCandidateRun.normalization = + annotatedPiNormalization := rfl + +def annotatedPiNormalizationRun : + VInductDecl.NormalizationRun annotatedPiNormalization outParamEnv := by + simpa only [annotatedPiCandidateNormalization_eq] using + annotatedPiNormalizationCandidateRun.normalizationRun + +theorem annotatedPiNormalization_wf_checked : + annotatedPiNormalization.WF outParamEnv := + annotatedPiNormalizationRun.wf + +theorem annotatedPiBlock_wf_checked : + annotatedPiBlock.WF outParamEnv := by + refine ⟨annotatedPiNormalization_wf_checked, ?_⟩ + exact annotatedPiViewChecked_wf + +private theorem annotatedPiCandidate_generationShape : + VInductDecl.normalizationCandidateGenerationShape annotatedPiRawDecl + annotatedPiRawType annotatedPiNormalizationCandidate = true := by + change ((true && true) && + (annotatedPiCtorCandidate.trace.storedSpine && true && true)) = true + rw [annotatedPiCtorCandidate_storedSpine] + rfl + +private def annotatedPiProducedGenerationShapeCandidate : + VInductDecl.ProducedGenerationShapeCandidate annotatedPiRawDecl + annotatedPiRawType annotatedPiKernelType 0 false + annotatedPiFamilyCandidateContext where + candidate := annotatedPiNormalizationCandidate + produced := annotatedPiNormalizationCandidate_produced + shape := annotatedPiCandidate_generationShape + +/-- The strengthened outer gate retains AnnotatedPi's nested annotation- +normalizing candidate only after its complete raw generation spine passes. -/ +theorem annotatedPiGenerationShapeCandidate_produced : + VInductDecl.produceGenerationShapeCandidate annotatedPiRawDecl + annotatedPiRawType annotatedPiKernelType 0 false + annotatedPiFamilyCandidateContext = + .ok annotatedPiProducedGenerationShapeCandidate := by + have produced : + AddInductive.buildNormalizationCandidate annotatedPiRawDecl.nparams + [annotatedPiKernelType] 0 false annotatedPiFamilyCandidateContext = + .ok annotatedPiNormalizationCandidate := + annotatedPiNormalizationCandidate_produced + simpa only [annotatedPiProducedGenerationShapeCandidate] using + VInductDecl.produceGenerationShapeCandidate_eq_ok + (source := annotatedPiRawDecl) (raw := annotatedPiRawType) + produced annotatedPiCandidate_generationShape + +private theorem annotatedPiCandidate_analysis + (normalization : VInductDecl.NormalizationCandidateSemanticRun + outParamEnv [] annotatedPiNormalizationCandidate + annotatedPiRawDecl) : + normalization.root.normalization.generation? = + some annotatedPiGenerationChecked := by + let reference : VInductDecl.NormalizationCandidateSemanticRun outParamEnv [] + annotatedPiNormalizationCandidate annotatedPiRawDecl := { + raw := annotatedPiRawType + raw_types_eq := rfl + uvars_eq := rfl + family := annotatedPiCandidateFamilySemanticRun } + rw [annotatedPiStagedPreFamilyInput.normalization_eq normalization reference] + rfl + +/-- AnnotatedPi's nested annotation-normalizing candidate closes through the +same generic staged-owner boundary as the ordinary singleton fixture. -/ +theorem annotatedPiExactProducedGenerationCandidatePackage_exists : + Nonempty (VInductDecl.ExactProducedGenerationCandidatePackage + outParamEnv [] annotatedPiProducedGenerationShapeCandidate + annotatedPiGenerationChecked) := + annotatedPiProducedGenerationShapeCandidate.exactProducedPackage_nonempty + annotatedPiStagedPreFamilyInput rfl annotatedPiGenerationChecked + annotatedPiCandidate_analysis + +private def + annotatedPiExactProducedGenerationCandidatePackage : + VInductDecl.ExactProducedGenerationCandidatePackage outParamEnv [] + annotatedPiProducedGenerationShapeCandidate annotatedPiGenerationChecked := + annotatedPiProducedGenerationShapeCandidate.exactProducedPackage + annotatedPiStagedPreFamilyInput rfl annotatedPiGenerationChecked + annotatedPiCandidate_analysis + +/-- Complete source-indexed checker certificate for annotated recursive-Π +generation. This is the first live generation run whose main constructor +spine contains an annotation-normalized recursive function domain. -/ +def annotatedPiGenerationCandidateSemanticRun : + VInductDecl.GenerationCandidateSemanticRun + annotatedPiExactProducedGenerationCandidatePackage.normalization + annotatedPiGenerationChecked := + annotatedPiExactProducedGenerationCandidatePackage.semantic + +def annotatedPiGenerationCandidateRun : + VInductDecl.GenerationCandidateRun + annotatedPiExactProducedGenerationCandidatePackage.normalization.root + annotatedPiGenerationChecked := + annotatedPiGenerationCandidateSemanticRun.run + +/-- Complete dependent producer package for the annotation-bearing recursive +Π candidate. -/ +def annotatedPiGenerationCandidatePackage : + VInductDecl.GenerationCandidatePackage outParamEnv [] := + annotatedPiGenerationCandidateSemanticRun.package + +/-- The complete AnnotatedPi semantic package is selected by the exact +successful whole-call metadata producer, including its nested annotation- +consuming traversal in the post-family environment. -/ +def annotatedPiProducedGenerationCandidatePackage : + VInductDecl.ProducedGenerationCandidatePackage outParamEnv [] := + annotatedPiExactProducedGenerationCandidatePackage.package + +/-- Theory-only erasure consumed by the public certified transaction. -/ +def annotatedPiGenerationCertificate : + annotatedPiRawDecl.GenerationCertificate outParamEnv where + generation := annotatedPiGenerationChecked + wf := annotatedPiExactProducedGenerationCandidatePackage.semantic.run.wf + +def annotatedPiGenerationRun : + VInductDecl.GenerationRun annotatedPiGenerationChecked outParamEnv := + annotatedPiProducedGenerationCandidatePackage.package.run.generationRun + +theorem annotatedPiGenerationChecked_wf_checked : + annotatedPiGenerationChecked.WF outParamEnv := + annotatedPiGenerationCertificate.wf + +def annotatedPiCtorEnv : VEnv := + (annotatedPiTypeEnv.addConst annotatedPiRawType.ctors[0].name + annotatedPiRawType.ctors[0].toVConstant).get! + +def annotatedPiRecEnv : VEnv := + (annotatedPiCtorEnv.addConst ``AnnotatedPi.rec + annotatedPiGenerationChecked.recursor).get! + +def annotatedPiFinalEnv : VEnv := + (outParamEnv.addInductGeneration + annotatedPiGenerationChecked).get (by decide) + +theorem annotatedPi_addInductGeneration : + outParamEnv.addInductGeneration annotatedPiGenerationChecked = + some annotatedPiFinalEnv := rfl + +/-- The public proof-carrying path accepts the non-identity AnnotatedPi view +while computing exactly the established mixed Theory transaction. -/ +theorem annotatedPi_addInductCertified : + outParamEnv.addInductCertified annotatedPiGenerationCertificate = + some annotatedPiFinalEnv := + annotatedPi_addInductGeneration + +theorem annotatedPiCertified_ordered : annotatedPiFinalEnv.Ordered := + VEnv.addInductCertified_WF outParamEnv_ordered + annotatedPi_addInductCertified + +private theorem annotatedPiRawCtor_wf : + annotatedPiRawType.ctors[0].toVConstant.WF + annotatedPiTypeEnv := + ⟨.succ .zero, annotatedPiRawCtor_hasType⟩ + +private theorem annotatedPiCtorEnv_ordered : + annotatedPiCtorEnv.Ordered := + .const (n := annotatedPiRawType.ctors[0].name) + (ci := annotatedPiRawType.ctors[0].toVConstant) + annotatedPiTypeEnv_ordered annotatedPiRawCtor_wf rfl + +private theorem annotatedPiGenerationEnv : + VInductDecl.GenerationEnv annotatedPiGenerationChecked + annotatedPiCtorEnv := by + apply annotatedPiGenerationChecked_wf_checked.toGenerationEnv + (envT := annotatedPiTypeEnv) + · rfl + · exact (VEnv.addConst_le (show + outParamEnv.addConst annotatedPiRawType.name + annotatedPiRawType.toVConstant = some annotatedPiTypeEnv from rfl)).trans + (VEnv.addConst_le (show + annotatedPiTypeEnv.addConst annotatedPiRawType.ctors[0].name + annotatedPiRawType.ctors[0].toVConstant = + some annotatedPiCtorEnv from rfl)) + · exact VEnv.addConst_le (show + annotatedPiTypeEnv.addConst annotatedPiRawType.ctors[0].name + annotatedPiRawType.ctors[0].toVConstant = + some annotatedPiCtorEnv from rfl) + · exact annotatedPiCtorEnv_ordered + · rfl + · intro ctor hctor + change ctor ∈ + [⟨annotatedPiRawType.ctors[0], + annotatedPiViewChecked.constructors[0]⟩] at hctor + obtain rfl := List.mem_singleton.1 hctor + rfl + +private theorem annotatedPiMkInfo_tr : + TrConstVal .safe annotatedPiTypeEnv annotatedPiMkInfo + annotatedPiRawType.ctors[0] := by + exact ⟨⟨by decide, rfl, annotatedPiCtorSource_tr⟩, rfl⟩ + +private theorem annotatedPiRecInfo_tr : + TrConstVal .safe annotatedPiCtorEnv annotatedPiRecInfo + (inductGenerationRecVal annotatedPiGenerationChecked) := by + have hfamily : annotatedPiCtorEnv.constants ``AnnotatedPi = + some annotatedPiRawType.toVConstant := rfl + have hmk : annotatedPiCtorEnv.constants ``AnnotatedPi.mk = + some annotatedPiRawType.ctors[0].toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr annotatedPiCtorEnv + annotatedPiRecInfo.levelParams [] annotatedPiRecInfo.type + (inductGenerationRecVal annotatedPiGenerationChecked).type := by + tr_type_expr_tac + obtain ⟨u, hrec⟩ := annotatedPiGenerationEnv.recursor_wf + exact hshape.to_trExprS annotatedPiCtorEnv_ordered trivial + ⟨.sort u, hrec⟩ + +private def annotatedPiCtorMap : ConstMap := + annotatedPiTypeMap.insert ``AnnotatedPi.mk annotatedPiMkInfo + +private def annotatedPiMap : ConstMap := + annotatedPiCtorMap.insert ``AnnotatedPi.rec annotatedPiRecInfo + +private theorem annotatedPiMk_fresh : + annotatedPiTypeMap.find? ``AnnotatedPi.mk = none := by + rw [annotatedPiTypeMap, outParamMap_wf.find?_insert, + outParamMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +private theorem annotatedPiCtorMap_wf : annotatedPiCtorMap.WF := + annotatedPiTypeMap_wf.insert _ _ annotatedPiMk_fresh + +private theorem annotatedPiRec_fresh : + annotatedPiCtorMap.find? ``AnnotatedPi.rec = none := by + rw [annotatedPiCtorMap, annotatedPiTypeMap_wf.find?_insert, + annotatedPiTypeMap, outParamMap_wf.find?_insert, outParamMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +/-- Complete kernel-metadata replay transaction for `AnnotatedPi`, driven by +the checker-produced non-identity normalization certificate. -/ +def annotatedPiAddInductTraceChecked : + AddInductTrace outParamMap outParamEnv annotatedPiRawDecl + annotatedPiMap annotatedPiFinalEnv := by + refine annotatedPiProducedGenerationCandidatePackage.package.addInductTrace + annotatedPiTypeMap annotatedPiTypeEnv annotatedPiCtorMap + annotatedPiCtorEnv annotatedPiRecEnv annotatedPiAddType ?_ ?_ ?_ ⟨rfl⟩ + · exact .cons { + info := annotatedPiMkInfo + kind_eq := by simp [annotatedPiMkInfo, InductConstantKind.Matches] + tr := annotatedPiMkInfo_tr + map_fresh := by + simpa [annotatedPiRawType] using annotatedPiMk_fresh + env_add := rfl + map_add := rfl } .nil + · exact { + info := annotatedPiRecInfo + kind_eq := by simp [annotatedPiRecInfo, InductConstantKind.Matches] + tr := annotatedPiRecInfo_tr + map_fresh := by + rw [show + (inductGenerationRecVal + annotatedPiProducedGenerationCandidatePackage.package.generation).name = + ``AnnotatedPi.rec by rfl] + exact annotatedPiRec_fresh + env_add := rfl + map_add := rfl } + · decide + +theorem annotatedPi_addInduct_checked : + AddInduct outParamMap outParamEnv annotatedPiRawDecl + annotatedPiMap annotatedPiFinalEnv := + ⟨annotatedPiAddInductTraceChecked⟩ + +theorem annotatedPi_trEnv'_checked : + TrEnv' .safe annotatedPiMap false annotatedPiFinalEnv := + .induct annotatedPi_addInduct_checked outParam_trEnv' + +theorem annotatedPi_env_wf_checked : annotatedPiFinalEnv.WF := + annotatedPi_trEnv'_checked.wf + +theorem annotatedPi_aligned_checked : + Aligned .safe annotatedPiMap annotatedPiFinalEnv := + annotatedPi_trEnv'_checked.aligned + +theorem annotatedPiFinalEnv_trace : + Nonempty (VEnv.AddInductGenerationTrace outParamEnv + annotatedPiFinalEnv annotatedPiGenerationChecked) := + VEnv.addInductGeneration_trace annotatedPi_addInductGeneration + +/-- The public certified wrapper exposes the same trace. This deliberately +stays separate from the minimal Theory-only iota root below: the concrete +certificate remembers its Verify provenance, while the transaction equality +itself admits the smaller Theory proof. -/ +theorem annotatedPiCertified_trace : + Nonempty (VEnv.AddInductGenerationTrace outParamEnv + annotatedPiFinalEnv annotatedPiGenerationChecked) := + VEnv.addInductCertified_trace annotatedPi_addInductCertified + +theorem annotatedPiFinalEnv_family_lookup : + annotatedPiFinalEnv.constants ``AnnotatedPi = + some annotatedPiRawType.toVConstant := by + rcases annotatedPiFinalEnv_trace with ⟨trace⟩ + exact trace.family_lookup + +theorem annotatedPiFinalEnv_ctor_lookup : + annotatedPiFinalEnv.constants ``AnnotatedPi.mk = + some annotatedPiRawType.ctors[0].toVConstant := by + rcases annotatedPiFinalEnv_trace with ⟨trace⟩ + exact trace.ctor_lookup (.head _) + +theorem annotatedPiFinalEnv_rec_lookup : + annotatedPiFinalEnv.constants ``AnnotatedPi.rec = + some annotatedPiGenerationChecked.recursor := by + rcases annotatedPiFinalEnv_trace with ⟨trace⟩ + exact trace.rec_lookup + +theorem annotatedPiFinalEnv_rule_mem : + ∀ df ∈ annotatedPiGenerationChecked.generatedRules, + annotatedPiFinalEnv.defeqs df := by + intro df hdf + rcases annotatedPiFinalEnv_trace with ⟨trace⟩ + exact trace.rule_mem hdf + +theorem annotatedPiFinalEnv_iota_mem : + annotatedPiFinalEnv.defeqs + annotatedPiGenerationChecked.generatedRules[0] := by + apply annotatedPiFinalEnv_rule_mem + exact .head _ + +theorem annotatedPi_iota_rhs_matches_kernel : + annotatedPiKernelRuleRhs = + annotatedPiGenerationChecked.generatedRules[0].rhs := rfl + +theorem annotatedPi_type_map_lookup : + annotatedPiMap.find? ``AnnotatedPi = some annotatedPiInfo := by + rw [annotatedPiMap, annotatedPiCtorMap_wf.find?_insert, + annotatedPiCtorMap, annotatedPiTypeMap_wf.find?_insert] + simp +decide + rw [annotatedPiTypeMap, outParamMap_wf.find?_insert] + simp +decide + +theorem annotatedPi_mk_map_lookup : + annotatedPiMap.find? ``AnnotatedPi.mk = some annotatedPiMkInfo := by + rw [annotatedPiMap, annotatedPiCtorMap_wf.find?_insert, + annotatedPiCtorMap, annotatedPiTypeMap_wf.find?_insert] + rfl + +theorem annotatedPi_rec_map_lookup : + annotatedPiMap.find? ``AnnotatedPi.rec = some annotatedPiRecInfo := by + rw [annotatedPiMap, annotatedPiCtorMap_wf.find?_insert] + rfl + +theorem annotatedPi_type_lookup_unique : + annotatedPiInfo.name = ``AnnotatedPi ∧ + TrConstant .safe annotatedPiFinalEnv annotatedPiInfo + annotatedPiRawType.toVConstant := + annotatedPi_aligned_checked.find?_uniq annotatedPi_type_map_lookup + annotatedPiFinalEnv_family_lookup + +theorem annotatedPi_mk_lookup_unique : + annotatedPiMkInfo.name = ``AnnotatedPi.mk ∧ + TrConstant .safe annotatedPiFinalEnv annotatedPiMkInfo + annotatedPiRawType.ctors[0].toVConstant := + annotatedPi_aligned_checked.find?_uniq annotatedPi_mk_map_lookup + annotatedPiFinalEnv_ctor_lookup + +theorem annotatedPi_rec_lookup_unique : + annotatedPiRecInfo.name = ``AnnotatedPi.rec ∧ + TrConstant .safe annotatedPiFinalEnv annotatedPiRecInfo + annotatedPiGenerationChecked.recursor := + annotatedPi_aligned_checked.find?_uniq annotatedPi_rec_map_lookup + annotatedPiFinalEnv_rec_lookup + +/-! ## Definitionally equal parameter transaction replay -/ + +/-- Consumer-facing certificate for the exact mixed generation value whose +stored parameter is `outParam Type` and whose emitted recursor parameter is +the definitionally equal checked `Type`. -/ +def annotatedParamGenerationCertificate : + annotatedParamRawDecl.GenerationCertificate outParamEnv where + generation := annotatedParamGenerationChecked + wf := annotatedParamGenerationChecked_wf + +def annotatedParamCtorEnv : VEnv := + (annotatedParamTypeEnv.addConst annotatedParamRawType.ctors[0].name + annotatedParamRawType.ctors[0].toVConstant).get! + +def annotatedParamRecEnv : VEnv := + (annotatedParamCtorEnv.addConst ``AnnotatedParam.rec + annotatedParamGenerationChecked.recursor).get! + +def annotatedParamFinalEnv : VEnv := + (outParamEnv.addInductGeneration + annotatedParamGenerationChecked).get (by decide) + +theorem annotatedParam_addInductGeneration : + outParamEnv.addInductGeneration annotatedParamGenerationChecked = + some annotatedParamFinalEnv := rfl + +/-- The public proof-carrying transaction accepts the raw/checked parameter +normalization and computes the same environment as the underlying generation +transaction. -/ +theorem annotatedParam_addInductCertified : + outParamEnv.addInductCertified annotatedParamGenerationCertificate = + some annotatedParamFinalEnv := + annotatedParam_addInductGeneration + +theorem annotatedParamCertified_ordered : + annotatedParamFinalEnv.Ordered := + VEnv.addInductCertified_WF outParamEnv_ordered + annotatedParam_addInductCertified + +private theorem annotatedParamRawCtor_wf : + annotatedParamRawType.ctors[0].toVConstant.WF + annotatedParamTypeEnv := by + change annotatedParamTypeEnv.IsType 0 [] + annotatedParamRawType.ctors[0].type + let ctor : VInductDecl.NormalizedCtor := + ⟨annotatedParamRawType.ctors[0], + annotatedParamViewChecked.constructors[0]⟩ + have hctor : ctor ∈ + annotatedParamGenerationChecked.block.ctorPairs := by + change ctor ∈ [⟨annotatedParamRawType.ctors[0], + annotatedParamViewChecked.constructors[0]⟩] + exact .head _ + simpa [ctor, annotatedParamRawDecl] using + annotatedParamGenerationChecked_wf.rawCtor_isType + (envT := annotatedParamTypeEnv) rfl hctor + +private theorem annotatedParamCtorEnv_ordered : + annotatedParamCtorEnv.Ordered := + .const (n := annotatedParamRawType.ctors[0].name) + (ci := annotatedParamRawType.ctors[0].toVConstant) + annotatedParamTypeEnv_ordered annotatedParamRawCtor_wf rfl + +private theorem annotatedParamGenerationEnv : + VInductDecl.GenerationEnv annotatedParamGenerationChecked + annotatedParamCtorEnv := by + apply annotatedParamGenerationChecked_wf.toGenerationEnv + (envT := annotatedParamTypeEnv) + · rfl + · exact (VEnv.addConst_le (show + outParamEnv.addConst annotatedParamRawType.name + annotatedParamRawType.toVConstant = + some annotatedParamTypeEnv from rfl)).trans + (VEnv.addConst_le (show + annotatedParamTypeEnv.addConst + annotatedParamRawType.ctors[0].name + annotatedParamRawType.ctors[0].toVConstant = + some annotatedParamCtorEnv from rfl)) + · exact VEnv.addConst_le (show + annotatedParamTypeEnv.addConst + annotatedParamRawType.ctors[0].name + annotatedParamRawType.ctors[0].toVConstant = + some annotatedParamCtorEnv from rfl) + · exact annotatedParamCtorEnv_ordered + · rfl + · intro ctor hctor + change ctor ∈ [⟨annotatedParamRawType.ctors[0], + annotatedParamViewChecked.constructors[0]⟩] at hctor + obtain rfl := List.mem_singleton.1 hctor + rfl + +private theorem annotatedParamMkInfo_tr : + TrConstVal .safe annotatedParamTypeEnv annotatedParamMkInfo + annotatedParamRawType.ctors[0] := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr annotatedParamTypeEnv + annotatedParamMkInfo.levelParams [] annotatedParamMkInfo.type + annotatedParamRawType.ctors[0].type := by + tr_type_expr_tac + obtain ⟨u, htype⟩ := annotatedParamRawCtor_wf + exact hshape.to_trExprS annotatedParamTypeEnv_ordered trivial + ⟨.sort u, htype⟩ + +private theorem annotatedParamRecInfo_tr : + TrConstVal .safe annotatedParamCtorEnv annotatedParamRecInfo + (inductGenerationRecVal annotatedParamGenerationChecked) := by + have hfamily : annotatedParamCtorEnv.constants ``AnnotatedParam = + some annotatedParamRawType.toVConstant := rfl + have hmk : annotatedParamCtorEnv.constants ``AnnotatedParam.mk = + some annotatedParamRawType.ctors[0].toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr annotatedParamCtorEnv + annotatedParamRecInfo.levelParams [] annotatedParamRecInfo.type + (inductGenerationRecVal annotatedParamGenerationChecked).type := by + tr_type_expr_tac + obtain ⟨u, hrec⟩ := annotatedParamGenerationEnv.recursor_wf + exact hshape.to_trExprS annotatedParamCtorEnv_ordered trivial + ⟨.sort u, hrec⟩ + +private def annotatedParamCtorMap : ConstMap := + annotatedParamTypeMap.insert ``AnnotatedParam.mk annotatedParamMkInfo + +private def annotatedParamMap : ConstMap := + annotatedParamCtorMap.insert ``AnnotatedParam.rec annotatedParamRecInfo + +private theorem annotatedParamMk_fresh : + annotatedParamTypeMap.find? ``AnnotatedParam.mk = none := by + rw [annotatedParamTypeMap, outParamMap_wf.find?_insert, + outParamMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +private theorem annotatedParamCtorMap_wf : annotatedParamCtorMap.WF := + annotatedParamTypeMap_wf.insert _ _ annotatedParamMk_fresh + +private theorem annotatedParamRec_fresh : + annotatedParamCtorMap.find? ``AnnotatedParam.rec = none := by + rw [annotatedParamCtorMap, annotatedParamTypeMap_wf.find?_insert, + annotatedParamTypeMap, outParamMap_wf.find?_insert, outParamMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +/-- Real `ConstantInfo` replay for the definitionally equal parameter case. +The generation certificate and the emitted recursor/rule payload are the same +values checked against kernel metadata above. -/ +def annotatedParamAddInductTraceChecked : + AddInductTrace outParamMap outParamEnv annotatedParamRawDecl + annotatedParamMap annotatedParamFinalEnv := by + refine { + generation := annotatedParamGenerationChecked + generation_wf := annotatedParamGenerationChecked_wf + typeMap := annotatedParamTypeMap + typeEnv := annotatedParamTypeEnv + ctorMap := annotatedParamCtorMap + ctorEnv := annotatedParamCtorEnv + recEnv := annotatedParamRecEnv + addType := annotatedParamAddType + addCtors := ?_ + addRec := { + info := annotatedParamRecInfo + kind_eq := by + simp [annotatedParamRecInfo, InductConstantKind.Matches] + tr := annotatedParamRecInfo_tr + map_fresh := by + rw [show + (inductGenerationRecVal annotatedParamGenerationChecked).name = + ``AnnotatedParam.rec by rfl] + exact annotatedParamRec_fresh + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ } + exact .cons { + info := annotatedParamMkInfo + kind_eq := by + simp [annotatedParamMkInfo, InductConstantKind.Matches] + tr := annotatedParamMkInfo_tr + map_fresh := by + simpa [annotatedParamRawType] using annotatedParamMk_fresh + env_add := rfl + map_add := rfl } .nil + +theorem annotatedParam_addInduct_checked : + AddInduct outParamMap outParamEnv annotatedParamRawDecl + annotatedParamMap annotatedParamFinalEnv := + ⟨annotatedParamAddInductTraceChecked⟩ + +theorem annotatedParam_trEnv'_checked : + TrEnv' .safe annotatedParamMap false annotatedParamFinalEnv := + .induct annotatedParam_addInduct_checked outParam_trEnv' + +theorem annotatedParam_env_wf_checked : annotatedParamFinalEnv.WF := + annotatedParam_trEnv'_checked.wf + +theorem annotatedParam_aligned_checked : + Aligned .safe annotatedParamMap annotatedParamFinalEnv := + annotatedParam_trEnv'_checked.aligned + +theorem annotatedParamCertified_trace : + Nonempty (VEnv.AddInductGenerationTrace outParamEnv + annotatedParamFinalEnv annotatedParamGenerationChecked) := + VEnv.addInductCertified_trace annotatedParam_addInductCertified + +theorem annotatedParamFinalEnv_family_lookup : + annotatedParamFinalEnv.constants ``AnnotatedParam = + some annotatedParamRawType.toVConstant := by + rcases annotatedParamCertified_trace with ⟨trace⟩ + exact trace.family_lookup + +theorem annotatedParamFinalEnv_ctor_lookup : + annotatedParamFinalEnv.constants ``AnnotatedParam.mk = + some annotatedParamRawType.ctors[0].toVConstant := by + rcases annotatedParamCertified_trace with ⟨trace⟩ + exact trace.ctor_lookup (.head _) + +theorem annotatedParamFinalEnv_rec_lookup : + annotatedParamFinalEnv.constants ``AnnotatedParam.rec = + some annotatedParamGenerationChecked.recursor := by + rcases annotatedParamCertified_trace with ⟨trace⟩ + exact trace.rec_lookup + +theorem annotatedParamFinalEnv_iota_mem : + annotatedParamFinalEnv.defeqs + annotatedParamGenerationChecked.generatedRules[0] := by + rcases annotatedParamCertified_trace with ⟨trace⟩ + exact trace.rule_mem (.head _) + +theorem annotatedParam_iota_rhs_matches_kernel : + annotatedParamKernelRuleRhs = + annotatedParamGenerationChecked.generatedRules[0].rhs := rfl + +theorem annotatedParam_type_map_lookup : + annotatedParamMap.find? ``AnnotatedParam = + some annotatedParamInfo := by + rw [annotatedParamMap, annotatedParamCtorMap_wf.find?_insert, + annotatedParamCtorMap, annotatedParamTypeMap_wf.find?_insert, + annotatedParamTypeMap, outParamMap_wf.find?_insert] + simp +decide + +theorem annotatedParam_mk_map_lookup : + annotatedParamMap.find? ``AnnotatedParam.mk = + some annotatedParamMkInfo := by + rw [annotatedParamMap, annotatedParamCtorMap_wf.find?_insert, + annotatedParamCtorMap, annotatedParamTypeMap_wf.find?_insert] + rfl + +theorem annotatedParam_rec_map_lookup : + annotatedParamMap.find? ``AnnotatedParam.rec = + some annotatedParamRecInfo := by + rw [annotatedParamMap, annotatedParamCtorMap_wf.find?_insert] + rfl + +theorem annotatedParam_type_lookup_unique : + annotatedParamInfo.name = ``AnnotatedParam ∧ + TrConstant .safe annotatedParamFinalEnv annotatedParamInfo + annotatedParamRawType.toVConstant := + annotatedParam_aligned_checked.find?_uniq annotatedParam_type_map_lookup + annotatedParamFinalEnv_family_lookup + +theorem annotatedParam_mk_lookup_unique : + annotatedParamMkInfo.name = ``AnnotatedParam.mk ∧ + TrConstant .safe annotatedParamFinalEnv annotatedParamMkInfo + annotatedParamRawType.ctors[0].toVConstant := + annotatedParam_aligned_checked.find?_uniq annotatedParam_mk_map_lookup + annotatedParamFinalEnv_ctor_lookup + +theorem annotatedParam_rec_lookup_unique : + annotatedParamRecInfo.name = ``AnnotatedParam.rec ∧ + TrConstant .safe annotatedParamFinalEnv annotatedParamRecInfo + annotatedParamGenerationChecked.recursor := + annotatedParam_aligned_checked.find?_uniq annotatedParam_rec_map_lookup + annotatedParamFinalEnv_rec_lookup + +/-- The complete AliasFormer metadata trace with the generation-WF field +supplied by the checker-produced certificate. All computational metadata +witnesses are shared with the existing replay. -/ +def aliasFormerAddInductTraceChecked : + AddInductTrace typeFamilyAliasMap typeFamilyAliasEnv + aliasFormerRawDecl aliasFormerMap aliasFormerFinalEnv := + let replay := + aliasFormerAddInductTraceWith aliasFormerGenerationCertificate.wf + aliasFormerProducedGenerationCandidatePackage.package.addInductTrace + replay.typeMap replay.typeEnv replay.ctorMap replay.ctorEnv replay.recEnv + replay.addType replay.addCtors replay.addRec replay.recK replay.addRules + +theorem aliasFormer_addInduct_checked : + AddInduct typeFamilyAliasMap typeFamilyAliasEnv + aliasFormerRawDecl aliasFormerMap aliasFormerFinalEnv := + ⟨aliasFormerAddInductTraceChecked⟩ + +theorem aliasFormer_trEnv'_checked : + TrEnv' .safe aliasFormerMap false aliasFormerFinalEnv := + .induct aliasFormer_addInduct_checked typeFamilyAlias_trEnv' + +theorem aliasFormer_env_wf_checked : aliasFormerFinalEnv.WF := + aliasFormer_trEnv'_checked.wf + +theorem aliasFormer_aligned_checked : + Aligned .safe aliasFormerMap aliasFormerFinalEnv := + aliasFormer_trEnv'_checked.aligned + +/-- The complete AliasRec metadata trace with the generation-WF field supplied +by the checker-produced normalization certificate. -/ +def aliasRecAddInductTraceChecked : + AddInductTrace recAliasMap recAliasEnv aliasRecRawDecl + aliasRecMap aliasRecFinalEnv := + aliasRecAddInductTraceWith aliasRecGenerationChecked_wf_checked + +theorem aliasRec_addInduct_checked : + AddInduct recAliasMap recAliasEnv aliasRecRawDecl + aliasRecMap aliasRecFinalEnv := + ⟨aliasRecAddInductTraceChecked⟩ + +theorem aliasRec_trEnv'_checked : + TrEnv' .safe aliasRecMap false aliasRecFinalEnv := + .induct aliasRec_addInduct_checked recAlias_trEnv' + +theorem aliasRec_env_wf_checked : aliasRecFinalEnv.WF := + aliasRec_trEnv'_checked.wf + +theorem aliasRec_aligned_checked : + Aligned .safe aliasRecMap aliasRecFinalEnv := + aliasRec_trEnv'_checked.aligned + +/- The operational traces do not reach the pointer-equality contracts. Their +semantic endpoints intentionally inherit Verify's existing checker-refinement +and reflection contracts, including pointer equality. No new axiom or +native-evaluation principle is used. -/ +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerFamily_candidateTrace [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerCtor_candidateTrace [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerFamily_candidate [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerFamily_candidateRun_exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerFamily_candidateSource_tr [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerFamily_candidateView_tr [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerNormalizationCandidateRun [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerCandidateNormalization_eq [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerTruncatedView_rejected [propext] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasRecField_checkType [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasRecField_hasType_checked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerFamily_whnf [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerCtor_whnf [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.recAlias_whnf [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerFamily_checkType [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerCtor_checkType [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerFamily_isType_checked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerCtor_isType_checked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerNormalization_wf_checked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasRecNormalization_wf_checked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerBlock_wf_checked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerProducedSemanticHierarchy_exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerProducedPostFamilySemantic_exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerProducedPreFamilySemantic_exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerGenerationCandidateSemanticRun [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerGenerationCandidateRun [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerGenerationCandidatePackage [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerNormalizationCandidate_produced [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerGenerationShapeCandidate_produced [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerExactProducedGenerationCandidatePackage_exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerProducedGenerationCandidatePackage [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormer_addInductCertified_checked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerGenerationChecked_wf_checked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasRecBlock_wf_checked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasRecGenerationChecked_wf_checked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormerAddInductTraceChecked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormer_trEnv'_checked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasRecAddInductTraceChecked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasRec_trEnv'_checked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +/- Both alias replays have the same `sorryAx`-free Verify closure as the +identity fixtures. The three persistent-map contracts enter through concrete +`ConstMap` freshness proofs. -/ +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormer_trEnv' [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormer_env_wf [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasFormer_aligned [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasRec_trEnv' [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasRec_env_wf [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.aliasRec_aligned [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPiProducedSemanticHierarchy_exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPiProducedPostFamilySemantic_exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPiProducedPreFamilySemantic_exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPiNormalizationCandidateRun [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPiGenerationCandidateSemanticRun [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPiGenerationCandidateRun [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPiGenerationCandidatePackage [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPiCtor_candidateTrace [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPiFamily_candidateTrace [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Syntax.structEq_eq] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPiNormalizationCandidate_produced [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPiGenerationShapeCandidate_produced [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPiExactProducedGenerationCandidatePackage_exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPiProducedGenerationCandidatePackage [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPi_addInductCertified [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPiGenerationChecked_wf_checked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPiAddInductTraceChecked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPi_trEnv'_checked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedPiFinalEnv_iota_mem [propext, Quot.sound] + +/-! The parameter-parity fixture deliberately keeps its operational checker +outcome guard separate from the Theory certificate. These pins make the exact +trust split visible: the semantic transaction stays Theory-small, while real +`ConstantInfo` replay inherits only the already classified Verify frontier. -/ + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedParam_addInductCertified [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedParamAddInductTraceChecked [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedParam_trEnv'_checked [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.annotatedParamFinalEnv_iota_mem [propext, Quot.sound] + +end Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/Lemmas.lean b/Ix/Theory/Named/Verify/Environment/Lemmas.lean new file mode 100644 index 000000000..d82bd8ecb --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/Lemmas.lean @@ -0,0 +1,664 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Std.SMap +import Ix.Theory.Named.Reference.Declaration +import Ix.Theory.Named.Verify.Environment.Basic + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named +open Lean hiding Environment Exception +open Kernel + +theorem TrConstant.sf_mono (hsf : safety ≤ safety') + (H : TrConstant safety' env ci ci') : TrConstant safety env ci ci' := + ⟨safety.le_trans hsf H.1, H.2⟩ + +theorem TrConstVal.sf_mono (hsf : safety ≤ safety') + (H : TrConstVal safety' env ci ci') : TrConstVal safety env ci ci' := + ⟨H.1.sf_mono hsf, H.2⟩ + +theorem TrDefVal.sf_mono (hsf : safety ≤ safety') + (H : TrDefVal safety' env ci ci') : TrDefVal safety env ci ci' := + ⟨H.1.sf_mono hsf, H.2⟩ + +/- The former blanket `TrEnv'.sf_mono` was deleted at the v4.33 +reconciliation: upstream's new `TrEnv'.ignore` constructor makes lowering the +safety mode of an arbitrary translation unsound (a declaration skipped at a +strict mode must be translated, not skipped, at a laxer one). Fixture +environments that need every safety mode now state their `TrEnv'` derivations +parametrically in `safety` instead. -/ + +theorem TrConstant.mono {env env' : VEnv} (henv : env ≤ env') + (H : TrConstant safety env ci ci') : TrConstant safety env' ci ci' := + ⟨H.1, H.2.1, H.2.2.mono henv⟩ + +theorem TrConstVal.mono {env env' : VEnv} (henv : env ≤ env') + (H : TrConstVal safety env ci ci') : TrConstVal safety env' ci ci' := + ⟨H.1.mono henv, H.2⟩ + +theorem TrDefVal.mono {env env' : VEnv} (henv : env ≤ env') + (H : TrDefVal safety env ci ci') : TrDefVal safety env' ci ci' := + ⟨H.1.mono henv, H.2.mono henv⟩ + +variable (safety : DefinitionSafety) in +inductive Aligned : ConstMap → VEnv → Prop where + | empty : Aligned {} .empty + | ignoreConst : Aligned C venv → C.find? n = none → ¬safety ≤ ci.safety → + ci.name = n → Aligned (C.insert n ci) venv + | const : Aligned C venv → C.find? n = none → TrConstant safety venv ci ci' → + venv.addConst n ci' = some venv' → ci.name = n → Aligned (C.insert n ci) venv' + | defeq : Aligned C venv → Aligned C (venv.addDefEq df) + | structEta : Aligned C venv → Aligned C (venv.addStructEta rule) + +theorem Aligned.map_wf (H : Aligned safety C venv) : C.WF := by + induction H with + | empty => exact .empty + | ignoreConst _ h1 _ _ ih + | const _ h1 _ _ _ ih => exact ih.insert _ _ h1 + | defeq _ ih => exact ih + | structEta _ ih => exact ih + +theorem Aligned.find?_iff (H : Aligned safety C venv) : + (∃ ci, C.find? name = some ci ∧ safety ≤ ci.safety) ↔ ∃ ci, venv.constants name = some ci := by + induction H with + | empty => simp [SMap.find?, VEnv.empty] + | ignoreConst H _ h2 _ ih => + simp [H.map_wf.find?_insert]; split <;> [skip; assumption] + rename_i eq1 eq2; subst eq2; simp [← ih, *] + | const H h1 h2 eq _ ih => + simp [H.map_wf.find?_insert] + simp [VEnv.addConst] at eq; split at eq <;> cases eq + split <;> simp_all; exact h2.1 + | defeq _ ih => exact ih + | structEta _ ih => exact ih + +theorem Aligned.addQuot1 {Q : Prop} + (H1 : ∀ c env, Aligned safety c env → P c env → Q) + (C env) (wf : Aligned safety C env) (H2 : AddQuot1 n k ci P C env) : Q := by + let ⟨_, _, _, h1, h2, h3, h4⟩ := H2 + exact H1 _ _ (wf.const h2 (h1.sf_mono DefinitionSafety.le_safe) h3 rfl) h4 + +nonrec theorem Aligned.addQuot (H : AddQuot C₁ C₂ venv₁ venv₂) + (wf : Aligned safety C₁ venv₁) : Aligned safety C₂ venv₂ := by + dsimp [AddQuot] at H + refine (addQuot1 <| addQuot1 <| addQuot1 <| addQuot1 ?_) _ _ wf H + rintro _ _ h ⟨rfl, rfl⟩; exact h.defeq + +theorem AddInductConstant.map_wf + (H : AddInductConstant kind C₁ env₁ ci C₂ env₂) (wf : C₁.WF) : C₂.WF := by + rw [H.map_add] + exact wf.insert _ _ H.map_fresh + +/-- The implementation metadata inserted by one inductive-constant step is +still available at that step's output boundary. -/ +theorem AddInductConstant.map_lookup + (H : AddInductConstant kind C₁ env₁ ci C₂ env₂) + (wf : C₁.WF) : C₂.find? ci.name = some H.info := by + simp [H.map_add, wf.find?_insert] + +/-- An inductive-metadata insertion preserves every lookup already present in +the input map. Freshness rules out the only key at which `insert` could +replace that entry. -/ +theorem AddInductConstant.preserve_map_lookup + (H : AddInductConstant kind C₁ env₁ ci' C₂ env₂) + (wf : C₁.WF) {name : Name} {info : ConstantInfo} + (hlookup : C₁.find? name = some info) : + C₂.find? name = some info := by + rw [H.map_add, wf.find?_insert] + split + · rename_i heq + have hname : ci'.name = name := by simpa using heq + subst name + have hfresh := H.map_fresh + rw [hlookup] at hfresh + contradiction + · exact hlookup + +theorem InductConstantKind.Matches.deltaValue?_eq_none + {kind : InductConstantKind} {ci : ConstantInfo} + (H : InductConstantKind.Matches kind ci) : ci.deltaValue? = none := by + cases kind <;> cases ci <;> + simp_all [InductConstantKind.Matches, ConstantInfo.deltaValue?] + +/-- An inductive metadata insertion cannot introduce a declaration body. +Consequently, any value-bearing entry in the result map was already present +in the input map. -/ +theorem AddInductConstant.old_of_value + (H : AddInductConstant kind C₁ env₁ ci' C₂ env₂) (wf : C₁.WF) + (hout : C₂.find? name = some ci) (hv : ci.deltaValue? = some v) : + C₁.find? name = some ci := by + rw [H.map_add, wf.find?_insert] at hout + split at hout + · cases hout + have hnone := InductConstantKind.Matches.deltaValue?_eq_none H.kind_eq + simp_all + · exact hout + +theorem AddInductConstants.map_wf : + AddInductConstants kind C₁ env₁ cis C₂ env₂ → C₁.WF → C₂.WF + | .nil, wf => wf + | .cons h hrest, wf => hrest.map_wf (h.map_wf wf) + +/-- A whole insertion fold preserves every lookup from its input map. -/ +theorem AddInductConstants.preserve_map_lookup + (H : AddInductConstants kind C₁ env₁ cis C₂ env₂) + (wf : C₁.WF) {name : Name} {info : ConstantInfo} + (hlookup : C₁.find? name = some info) : + C₂.find? name = some info := by + induction H with + | nil => exact hlookup + | cons h hrest ih => + exact ih (h.map_wf wf) (h.preserve_map_lookup wf hlookup) + +/-- Final-map evidence for any member of an inductive metadata fold. The +result retains the exact implementation object, its role tag, and its +translation against the final Theory environment. -/ +theorem AddInductConstants.translated_lookup + (H : AddInductConstants kind C₁ env₁ cis C₂ env₂) + (wf : C₁.WF) {ci : VConstVal} (hmem : ci ∈ cis) : ∃ info, + C₂.find? ci.name = some info ∧ + kind.Matches info ∧ TrConstVal .safe env₂ info ci := by + induction H with + | nil => contradiction + | cons h hrest ih => + rcases List.mem_cons.1 hmem with rfl | hmem + · refine ⟨h.info, ?_, h.kind_eq, ?_⟩ + exact hrest.preserve_map_lookup (h.map_wf wf) (h.map_lookup wf) + exact h.tr.mono (h.le.trans hrest.le) + · exact ih (h.map_wf wf) hmem + +theorem AddInductConstants.old_of_value : + (H : AddInductConstants kind C₁ env₁ cis C₂ env₂) → C₁.WF → + C₂.find? name = some ci → ci.deltaValue? = some v → C₁.find? name = some ci + | .nil, _, hout, _ => hout + | .cons h hrest, wf, hout, hv => + h.old_of_value wf (hrest.old_of_value (h.map_wf wf) hout hv) hv + +/-! ## Final translated metadata inventories -/ + +/-- Final-map and final-environment evidence for the family emitted by a +singleton inductive replay. -/ +theorem AddInductTrace.type_translated_lookup + (H : AddInductTrace C₁ env₁ decl C₂ env₂) (wf : C₁.WF) : + ∃ info, + C₂.find? H.generation.block.sourceType.name = some info ∧ + InductConstantKind.induct.Matches info ∧ + TrConstVal .safe env₂ info H.generation.block.sourceType.toVConstVal := by + refine ⟨H.addType.info, ?_, H.addType.kind_eq, ?_⟩ + · exact H.addRec.preserve_map_lookup + (H.addCtors.map_wf (H.addType.map_wf wf)) + (H.addCtors.preserve_map_lookup (H.addType.map_wf wf) + (H.addType.map_lookup wf)) + · exact H.addType.tr.mono + (H.addType.le.trans <| H.addCtors.le.trans <| + H.addRec.le.trans H.addRules.le) + +/-- Final-map and final-environment evidence for every constructor emitted by +a singleton inductive replay. -/ +theorem AddInductTrace.constructor_translated_lookup + (H : AddInductTrace C₁ env₁ decl C₂ env₂) (wf : C₁.WF) + {constructor : VConstVal} + (hconstructor : constructor ∈ H.generation.block.sourceType.ctors) : + ∃ info, + C₂.find? constructor.name = some info ∧ + InductConstantKind.ctor.Matches info ∧ + TrConstVal .safe env₂ info constructor := by + obtain ⟨info, hlookup, hkind, htr⟩ := + H.addCtors.translated_lookup (H.addType.map_wf wf) hconstructor + exact ⟨info, + H.addRec.preserve_map_lookup (H.addCtors.map_wf (H.addType.map_wf wf)) hlookup, + hkind, htr.mono (H.addRec.le.trans H.addRules.le)⟩ + +/-- Final-map and final-environment evidence for the recursor emitted by a +singleton inductive replay. -/ +theorem AddInductTrace.recursor_translated_lookup + (H : AddInductTrace C₁ env₁ decl C₂ env₂) + (wf : C₁.WF) : ∃ info, + C₂.find? (inductGenerationRecVal H.generation).name = some info ∧ + InductConstantKind.recursor.Matches info ∧ + TrConstVal .safe env₂ info (inductGenerationRecVal H.generation) := by + exact ⟨H.addRec.info, H.addRec.map_lookup + (H.addCtors.map_wf (H.addType.map_wf wf)), H.addRec.kind_eq, + H.addRec.tr.mono (H.addRec.le.trans H.addRules.le)⟩ + +/-- Final translated lookup for every source family in a mutual block. -/ +theorem AddInductBlockTrace.family_translated_lookup + (H : AddInductBlockTrace C₁ env₁ decl C₂ env₂) (wf : C₁.WF) + {family : VInductiveType} (hfamily : family ∈ decl.types) : ∃ info, + C₂.find? family.name = some info ∧ + InductConstantKind.induct.Matches info ∧ + TrConstVal .safe env₂ info family.toVConstVal := by + have hmember : family.toVConstVal ∈ decl.blockTypeConstants := + List.mem_map.2 ⟨family, hfamily, rfl⟩ + obtain ⟨info, hlookup, hkind, htr⟩ := + H.addTypes.translated_lookup wf hmember + exact ⟨info, + H.addRecs.preserve_map_lookup + (H.addCtors.map_wf (H.addTypes.map_wf wf)) + (H.addCtors.preserve_map_lookup (H.addTypes.map_wf wf) hlookup), + hkind, htr.mono (H.addCtors.le.trans <| H.addRecs.le.trans H.addRules.le)⟩ + +/-- Final translated lookup for every flattened constructor in a mutual +block. -/ +theorem AddInductBlockTrace.constructor_translated_lookup + (H : AddInductBlockTrace C₁ env₁ decl C₂ env₂) (wf : C₁.WF) + {constructor : VConstVal} + (hconstructor : constructor ∈ decl.blockConstructorConstants) : ∃ info, + C₂.find? constructor.name = some info ∧ + InductConstantKind.ctor.Matches info ∧ + TrConstVal .safe env₂ info constructor := by + obtain ⟨info, hlookup, hkind, htr⟩ := + H.addCtors.translated_lookup (H.addTypes.map_wf wf) hconstructor + exact ⟨info, + H.addRecs.preserve_map_lookup + (H.addCtors.map_wf (H.addTypes.map_wf wf)) hlookup, + hkind, htr.mono (H.addRecs.le.trans H.addRules.le)⟩ + +/-- Final translated lookup for every generated recursor in a mutual block. -/ +theorem AddInductBlockTrace.recursor_translated_lookup + (H : AddInductBlockTrace C₁ env₁ decl C₂ env₂) (wf : C₁.WF) + {recursor : VConstVal} (hrecursor : recursor ∈ H.generation.recursors) : + ∃ info, + C₂.find? recursor.name = some info ∧ + InductConstantKind.recursor.Matches info ∧ + TrConstVal .safe env₂ info recursor := by + obtain ⟨info, hlookup, hkind, htr⟩ := H.addRecs.translated_lookup + (H.addCtors.map_wf (H.addTypes.map_wf wf)) hrecursor + exact ⟨info, hlookup, hkind, htr.mono H.addRules.le⟩ + +/-- Final translated lookup for every source family in a nested replay. -/ +theorem AddInductNestedTrace.family_translated_lookup + (H : AddInductNestedTrace C₁ env₁ decl C₂ env₂) (wf : C₁.WF) + {family : VInductiveType} (hfamily : family ∈ decl.types) : ∃ info, + C₂.find? family.name = some info ∧ + InductConstantKind.induct.Matches info ∧ + TrConstVal .safe env₂ info family.toVConstVal := by + have hmember : family.toVConstVal ∈ decl.blockTypeConstants := + List.mem_map.2 ⟨family, hfamily, rfl⟩ + obtain ⟨info, hlookup, hkind, htr⟩ := + H.addTypes.translated_lookup wf hmember + exact ⟨info, + H.addRecs.preserve_map_lookup + (H.addCtors.map_wf (H.addTypes.map_wf wf)) + (H.addCtors.preserve_map_lookup (H.addTypes.map_wf wf) hlookup), + hkind, htr.mono (H.addCtors.le.trans <| H.addRecs.le.trans H.addRules.le)⟩ + +/-- Final translated lookup for every source constructor in a nested replay. -/ +theorem AddInductNestedTrace.constructor_translated_lookup + (H : AddInductNestedTrace C₁ env₁ decl C₂ env₂) (wf : C₁.WF) + {constructor : VConstVal} + (hconstructor : constructor ∈ decl.blockConstructorConstants) : ∃ info, + C₂.find? constructor.name = some info ∧ + InductConstantKind.ctor.Matches info ∧ + TrConstVal .safe env₂ info constructor := by + obtain ⟨info, hlookup, hkind, htr⟩ := + H.addCtors.translated_lookup (H.addTypes.map_wf wf) hconstructor + exact ⟨info, + H.addRecs.preserve_map_lookup + (H.addCtors.map_wf (H.addTypes.map_wf wf)) hlookup, + hkind, htr.mono (H.addRecs.le.trans H.addRules.le)⟩ + +/-- Final translated lookup for every restored recursor in a nested replay. -/ +theorem AddInductNestedTrace.recursor_translated_lookup + (H : AddInductNestedTrace C₁ env₁ decl C₂ env₂) (wf : C₁.WF) + {recursor : VConstVal} (hrecursor : recursor ∈ H.nested.recursors) : ∃ info, + C₂.find? recursor.name = some info ∧ + InductConstantKind.recursor.Matches info ∧ + TrConstVal .safe env₂ info recursor := by + obtain ⟨info, hlookup, hkind, htr⟩ := H.addRecs.translated_lookup + (H.addCtors.map_wf (H.addTypes.map_wf wf)) hrecursor + exact ⟨info, hlookup, hkind, htr.mono H.addRules.le⟩ + +theorem AddInduct.map_wf (H : AddInduct C₁ env₁ decl C₂ env₂) + (wf : C₁.WF) : C₂.WF := by + rcases H with ⟨H⟩ + exact H.addRec.map_wf <| H.addCtors.map_wf <| H.addType.map_wf wf + +theorem AddInduct.old_of_value (H : AddInduct C₁ env₁ decl C₂ env₂) + (wf : C₁.WF) (hout : C₂.find? name = some ci) (hv : ci.deltaValue? = some v) : + C₁.find? name = some ci := by + rcases H with ⟨H⟩ + have wfType := H.addType.map_wf wf + have wfCtors := H.addCtors.map_wf wfType + exact H.addType.old_of_value wf + (H.addCtors.old_of_value wfType (H.addRec.old_of_value wfCtors hout hv) hv) hv + +theorem AddInductBlock.map_wf + (H : AddInductBlock C₁ env₁ decl C₂ env₂) + (wf : C₁.WF) : C₂.WF := by + rcases H with ⟨H⟩ + exact H.addRecs.map_wf <| H.addCtors.map_wf <| + H.addTypes.map_wf wf + +theorem AddInductBlock.old_of_value + (H : AddInductBlock C₁ env₁ decl C₂ env₂) + (wf : C₁.WF) (hout : C₂.find? name = some ci) + (hv : ci.deltaValue? = some v) : C₁.find? name = some ci := by + rcases H with ⟨H⟩ + have wfTypes := H.addTypes.map_wf wf + have wfCtors := H.addCtors.map_wf wfTypes + exact H.addTypes.old_of_value wf + (H.addCtors.old_of_value wfTypes + (H.addRecs.old_of_value wfCtors hout hv) hv) hv + +theorem AddInductNested.map_wf + (H : AddInductNested C₁ env₁ decl C₂ env₂) + (wf : C₁.WF) : C₂.WF := by + rcases H with ⟨H⟩ + exact H.addRecs.map_wf <| H.addCtors.map_wf <| + H.addTypes.map_wf wf + +theorem AddInductNested.old_of_value + (H : AddInductNested C₁ env₁ decl C₂ env₂) + (wf : C₁.WF) (hout : C₂.find? name = some ci) + (hv : ci.deltaValue? = some v) : C₁.find? name = some ci := by + rcases H with ⟨H⟩ + have wfTypes := H.addTypes.map_wf wf + have wfCtors := H.addCtors.map_wf wfTypes + exact H.addTypes.old_of_value wf + (H.addCtors.old_of_value wfTypes + (H.addRecs.old_of_value wfCtors hout hv) hv) hv + +theorem Aligned.addInductConstant + (wf : Aligned safety C₁ env₁) + (H : AddInductConstant kind C₁ env₁ ci C₂ env₂) : Aligned safety C₂ env₂ := by + rw [H.map_add] + exact wf.const H.map_fresh (H.tr.1.sf_mono DefinitionSafety.le_safe) + H.env_add H.tr.2 + +theorem Aligned.addInductConstants : + AddInductConstants kind C₁ env₁ cis C₂ env₂ → + Aligned safety C₁ env₁ → Aligned safety C₂ env₂ + | .nil, wf => wf + | .cons h hrest, wf => + Aligned.addInductConstants hrest (wf.addInductConstant h) + +theorem Aligned.addDefEqFold : ∀ (dfs : List VDefEq), + Aligned safety C env → Aligned safety C (dfs.foldl VEnv.addDefEq env) + | [], wf => wf + | _ :: dfs, wf => addDefEqFold dfs wf.defeq + +theorem Aligned.addInduct (H : AddInduct C₁ env₁ decl C₂ env₂) + (wf : Aligned safety C₁ env₁) : Aligned safety C₂ env₂ := by + rcases H with ⟨H⟩ + rw [← H.addRules.to_add] + have wfType := wf.addInductConstant H.addType + have wfCtors := wfType.addInductConstants H.addCtors + have wfRec := wfCtors.addInductConstant H.addRec + exact wfRec.addDefEqFold _ + +theorem Aligned.addInductBlock + (H : AddInductBlock C₁ env₁ decl C₂ env₂) + (wf : Aligned safety C₁ env₁) : Aligned safety C₂ env₂ := by + rcases H with ⟨H⟩ + rw [← H.addRules.to_add] + have wfTypes := wf.addInductConstants H.addTypes + have wfCtors := wfTypes.addInductConstants H.addCtors + have wfRecs := wfCtors.addInductConstants H.addRecs + exact wfRecs.addDefEqFold _ + +theorem Aligned.addInductNested + (H : AddInductNested C₁ env₁ decl C₂ env₂) + (wf : Aligned safety C₁ env₁) : Aligned safety C₂ env₂ := by + rcases H with ⟨H⟩ + rw [← H.addRules.to_add] + have wfTypes := wf.addInductConstants H.addTypes + have wfCtors := wfTypes.addInductConstants H.addCtors + have wfRecs := wfCtors.addInductConstants H.addRecs + exact wfRecs.addDefEqFold _ + +#guard_named_axioms Ix.Theory.Named.Aligned.addInduct [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.Aligned.addInductBlock [propext, Classical.choice, Quot.sound] + +theorem Aligned.addDefEqs {C : ConstMap} : ∀ {cis' : List VDefVal} {venv}, + Aligned safety C venv → Aligned safety C (venv.addDefEqs cis') + | [], _, H => H + | ci :: cis, venv, H => by + show Aligned safety C (VEnv.addDefEqs (venv.addDefEq ci.toDefEq) cis) + exact Aligned.addDefEqs H.defeq + +theorem Aligned.insertDefs : ∀ {cis : List DefinitionVal} {cis' : List VDefVal} {C venv venv'}, + Aligned safety C venv → (cis.map (·.name)).Nodup → + (∀ ci ∈ cis, C.find? ci.name = none) → + List.Forall₂ (fun ci ci' => TrConstVal safety venv (.defnInfo ci) ci'.toVConstVal) cis cis' → + venv.addConsts cis' = some venv' → Aligned safety (insertDefs C cis) venv' + | [], _, _, _, _, H, _, _, hblk, e => by + cases hblk; simp [VEnv.addConsts] at e; cases e; exact H + | ci :: cis, _, C, venv, _, H, hnd, hfr, hblk, e => by + cases hblk with | @cons _ ci' _ _ htr hblk => ?_ + simp [VEnv.addConsts, Option.bind_eq_some_iff] at e + obtain ⟨venv₁, h1, h2⟩ := e + have hname := htr.2 + simp only [ConstantInfo.name, ConstantInfo.toConstantVal] at hname + simp only [List.map_cons, List.nodup_cons, List.mem_map] at hnd + have h1' : venv.addConst ci.name ci'.toVConstant = some venv₁ := by rw [hname]; exact h1 + show Aligned safety + (_root_.Ix.Theory.Named.insertDefs (SMap.insert C ci.name (.defnInfo ci)) cis) _ + refine Aligned.insertDefs (H.const (hfr _ (.head _)) htr.1 h1' rfl) hnd.2 + (fun c hc => ?_) (Ix.Theory.Named.List.Forall₂.imp + (fun _ _ h => h.mono (VEnv.addConst_le h1')) hblk) h2 + rw [H.map_wf.find?_insert] + have : ¬ (ci.name == c.name) = true := by + simp only [beq_iff_eq]; intro h + exact hnd.1 ⟨c, hc, h.symm⟩ + simp [this] + exact hfr c (.tail _ hc) + +theorem TrEnv'.aligned (H : TrEnv' safety C Q venv) : Aligned safety C venv := by + induction H with + | empty => exact .empty + | ignore h1 h2 _ ih => exact ih.ignoreConst h1 h2 rfl + | «axiom» h1 h2 _ h _ ih => exact ih.const h2 h1 h rfl + | thm h1 h2 _ _ h _ ih => exact ih.const h2 h1.1.1 h rfl + | «opaque» h1 h2 _ h _ ih => exact ih.const h2 h1.1.1 h rfl + | defn h1 h2 _ h _ ih => exact (ih.const h2 h1.1.1 h rfl).defeq + | mutualDef hblk hnd hfr _ hadd _ _ ih => + exact Aligned.addDefEqs <| ih.insertDefs hnd hfr + (Ix.Theory.Named.List.Forall₂.imp (fun _ _ h => h.1) hblk) hadd + | quot _ h _ ih => exact ih.addQuot h + | inductStaging h _ _ ih => exact ih.addInductConstant h + | induct h _ ih => exact ih.addInduct h + | inductBlock h _ ih => exact ih.addInductBlock h + | inductNested h _ ih => exact ih.addInductNested h + | structEta _ _ ih => exact ih.structEta + +/- Since the v4.33 reconciliation the `mutualDef` arm routes through +`insertDefs`, whose `SMap` reasoning uses the classified persistent-map +container contracts; the closure is pinned so any further growth is +reviewed. -/ +#guard_named_axioms Ix.Theory.Named.TrEnv'.aligned [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +theorem TrEnv'.map_wf (H : TrEnv' safety C Q venv) : C.WF := H.aligned.map_wf + +theorem Aligned.find? (H : Aligned safety C venv) + (h : C.find? name = some ci) (hs : safety ≤ ci.safety) : + ∃ ci', venv.constants name = some ci' ∧ TrConstant safety venv ci ci' := by + have mono {env₁ env₂} (H : env₁.LE env₂) : + (∃ ci', env₁.constants name = some ci' ∧ TrConstant safety env₁ ci ci') → + (∃ ci', env₂.constants name = some ci' ∧ TrConstant safety env₂ ci ci') + | ⟨_, h1, h2⟩ => ⟨_, H.constants h1, h2.mono H⟩ + induction H with + | empty => simp [SMap.find?] at h + | ignoreConst h1 _ _ _ ih => + rw [h1.map_wf.find?_insert] at h; split at h + · cases h; contradiction + · exact ih h + | const h1 _ h2 h3 _ ih => + have := VEnv.addConst_le h3 + rw [h1.map_wf.find?_insert] at h; split at h + · rename_i h'; cases h; simp at h'; subst h' + simp [VEnv.addConst] at h3; split at h3 <;> cases h3 + simp; rename_i h'; refine h2.mono this + · let ⟨_, h1, h2⟩ := ih h; exact ⟨_, this.constants h1, h2.mono this⟩ + | defeq h1 ih => let ⟨_, h1, h2⟩ := ih h; exact ⟨_, h1, h2.mono VEnv.addDefEq_le⟩ + | structEta h1 ih => + let ⟨_, h1, h2⟩ := ih h + exact ⟨_, h1, h2.mono VEnv.addStructEta_le⟩ + +theorem Aligned.find?_uniq (H : Aligned safety C venv) + (h : C.find? name = some ci) (hs : venv.constants name = some ci') : + ci.name = name ∧ TrConstant safety venv ci ci' := by + induction H with + | empty => simp [SMap.find?] at h + | ignoreConst H h2 h3 _ ih => + simp [H.map_wf.find?_insert] at h; split at h + · rename_i n ci _ h'; subst n h' + simpa [h2, hs] using H.find?_iff (name := ci.name) + · exact ih h hs + | const h1 h5 h2 h3 h4 ih => + have := VEnv.addConst_le h3 + simp [VEnv.addConst] at h3; split at h3 <;> cases h3 + simp [h1.map_wf.find?_insert] at h hs; revert h hs; split + · rintro ⟨⟩ ⟨⟩; rename_i n _ _ _; subst n; exact ⟨h4, h2.mono this⟩ + · intro hs h; let ⟨h1, h2⟩ := ih h hs; exact ⟨h1, h2.mono this⟩ + | defeq h1 ih => let ⟨h1, h2⟩ := ih h hs; exact ⟨h1, h2.mono VEnv.addDefEq_le⟩ + | structEta h1 ih => + let ⟨h1, h2⟩ := ih h hs + exact ⟨h1, h2.mono VEnv.addStructEta_le⟩ + +theorem TrEnv.find?_iff (H : TrEnv safety env venv) : + (∃ ci, env.find? name = some ci ∧ safety ≤ ci.safety) ↔ ∃ ci, venv.constants name = some ci := by + conv => enter [1,1,_,1,1]; apply H.map_wf.find?'_eq_find? + exact H.aligned.find?_iff + +-- theorem TrEnv.contains_iff (H : TrEnv safety env venv) : +-- env.contains name ↔ ∃ oci, venv.constants name = some oci := by +-- simp [← H.find?_iff, Kernel.Environment.find?, H.map_wf.find?'_eq_find?, +-- ← Option.isSome_iff_exists, ← SMap.find?_isSome, Kernel.Environment.contains] + +theorem TrEnv.find? (H : TrEnv safety env venv) + (h : env.find? name = some ci) (hs : safety ≤ ci.safety) : + ∃ ci', venv.constants name = some ci' ∧ TrConstant safety venv ci ci' := + H.aligned.find? (H.map_wf.find?'_eq_find? _ ▸ h) hs + +theorem TrEnv.find?_uniq (H : TrEnv safety env venv) + (h : env.find? name = some ci) (hs : venv.constants name = some ci') : + ci.name = name ∧ TrConstant safety venv ci ci' := + H.aligned.find?_uniq (H.map_wf.find?'_eq_find? _ ▸ h) hs + +theorem VEnv.addDefEqs_le : ∀ {cis' : List VDefVal} {venv : VEnv}, venv ≤ venv.addDefEqs cis' + | [], _ => .rfl + | ci :: cis, venv => by + show venv ≤ VEnv.addDefEqs (venv.addDefEq ci.toDefEq) cis + exact VEnv.addDefEq_le.trans VEnv.addDefEqs_le + +theorem VEnv.addDefEqs_self : ∀ {cis' : List VDefVal} {venv : VEnv} {ci'}, ci' ∈ cis' → + (venv.addDefEqs cis').defeqs ci'.toDefEq + | ci :: cis, venv, _, hc => by + show (VEnv.addDefEqs (venv.addDefEq ci.toDefEq) cis).defeqs _ + cases hc with + | head => exact VEnv.addDefEqs_le.defeqs VEnv.addDefEq_self + | tail _ hc => exact VEnv.addDefEqs_self hc + +theorem insertDefs_find? : ∀ {cis : List DefinitionVal} {C : ConstMap} {name ci}, C.WF → + (∀ d ∈ cis, C.find? d.name = none) → (cis.map (·.name)).Nodup → + (insertDefs C cis).find? name = some ci → + C.find? name = some ci ∨ ∃ d ∈ cis, d.name = name ∧ ConstantInfo.defnInfo d = ci + | [], _, _, _, _, _, _, h => .inl h + | d :: ds, C, name, ci, hC, hfr, hnd, h => by + simp only [List.map_cons, List.nodup_cons, List.mem_map] at hnd + have hfr' : ∀ e ∈ ds, (SMap.insert C d.name (.defnInfo d)).find? e.name = none := by + intro e he + rw [hC.find?_insert] + have : ¬ (d.name == e.name) = true := by + simp only [beq_iff_eq]; intro hh; exact hnd.1 ⟨e, he, hh.symm⟩ + simp [this]; exact hfr e (.tail _ he) + have h : (insertDefs (SMap.insert C d.name (.defnInfo d)) ds).find? name = some ci := h + rcases insertDefs_find? (hC.insert _ _ (hfr _ (.head _))) hfr' hnd.2 h with h | ⟨e, he, h1, h2⟩ + · rw [hC.find?_insert] at h; split at h + · rename_i hb; cases h + exact .inr ⟨d, .head _, by simpa using hb, rfl⟩ + · exact .inl h + · exact .inr ⟨e, .tail _ he, h1, h2⟩ + +theorem TrEnv'.of_value (H : TrEnv' safety C Q venv) (h : C.find? name = some ci) + (hs : safety ≤ ci.safety) (hv : ci.deltaValue? = some v) : + TrExpr venv ci.levelParams [] v (.const ci.name (VLevel.params ci.levelParams.length)) := by + have {C n ci'} (hC : C.WF) : + (SMap.insert C n ci').find? name = some ci → + C.find? name = some ci ∨ n = name ∧ ci' = ci := by + rw [hC.find?_insert]; simp; split <;> simp +contextual [*] + induction H with + | empty => simp [SMap.find?] at h + | ignore h1 h2 H ih => + obtain h | ⟨rfl, rfl⟩ := this H.map_wf h + · exact ih h + · exact (h2 hs).elim + | «axiom» _ _ _ h1 H ih => + obtain h | ⟨rfl, rfl⟩ := this H.map_wf h + · exact (ih h).mono (VEnv.addConst_le h1) + · contradiction + | defn h2 h3 h4 h1 H ih => + have' le := (VEnv.addConst_le h1).trans VEnv.addDefEq_le + obtain h | ⟨rfl, rfl⟩ := this H.map_wf h + · exact (ih h).mono le + · cases hv + have := VEnv.IsDefEq.extra0 VEnv.addDefEq_self <| + (H.defn h2 h3 h4 h1).wf.ordered.defEqWF VEnv.addDefEq_self + let ⟨⟨⟨b1, b2, b3⟩, b4⟩, b5⟩ := h2 + refine ⟨_, b5.mono le, b2.symm ▸ b4.symm ▸ ⟨_, this.symm⟩⟩ + | mutualDef hblk hnd hfr _ hadd _ H ih => + have' le := (VEnv.addConsts_le hadd).trans VEnv.addDefEqs_le + rcases insertDefs_find? H.map_wf hfr hnd h with h | ⟨d, hd, rfl, rfl⟩ + · exact (ih h).mono le + · obtain ⟨d', hd', htr, hval⟩ := Ix.Theory.Named.List.Forall₂.forall_exists_l hblk _ hd + cases hv + have hdefeq := VEnv.IsDefEq.extra0 (VEnv.addDefEqs_self hd') + ((H.mutualDef hblk hnd hfr ‹_› hadd ‹_›).wf.ordered.defEqWF (VEnv.addDefEqs_self hd')) + let ⟨⟨b1, b2, b3⟩, b4⟩ := htr + exact ⟨_, hval.mono VEnv.addDefEqs_le, b2.symm ▸ b4.symm ▸ ⟨_, hdefeq.symm⟩⟩ + | thm h2 h3 h4 h5 h1 H ih => + have' le := VEnv.addConst_le h1 + obtain h | ⟨rfl, rfl⟩ := this H.map_wf h + · exact (ih h).mono le + · cases hv + let ⟨⟨⟨b1, b2, b3⟩, b4⟩, b5⟩ := h2 + dsimp only [ConstantInfo.name, ConstantInfo.levelParams, ConstantInfo.toConstantVal] at b2 b4 ⊢ + have hp := h5.mono le + have hb := h4.mono le + have hc := VEnv.HasType.const0 (VEnv.addConst_self h1) ⟨_, hp⟩ + rw [b4] at hc + refine ⟨_, b5.mono le, b2.symm ▸ b4.symm ▸ ?_⟩ + exact ⟨_, .proofIrrel hp hb hc⟩ + | «opaque» _ _ _ h1 H ih => + obtain h | ⟨rfl, rfl⟩ := this H.map_wf h + · exact (ih h).mono (VEnv.addConst_le h1) + · contradiction + | quot _ h1 H ih => + suffices ∀ {n k ci' P}, (∀ C env, Aligned safety C env → P C env → C.find? name = some ci) → + ∀ C env, Aligned safety C env → AddQuot1 n k ci' P C env → C.find? name = some ci by + refine (ih <| this (this <| this <| this ?_) _ _ H.aligned h1).mono h1.le + rintro _ _ _ ⟨rfl, rfl⟩; exact h + rintro n k ci' P ih C env wf ⟨_, h1, _, h2, h3, h4, h5⟩ + have wf' := wf.const h3 ⟨by cases safety <;> rfl, h2.2⟩ h4 rfl + obtain h | ⟨rfl, rfl⟩ := this wf.map_wf (ih _ _ wf' h5) + · exact h + · contradiction + | inductStaging h1 _ H ih => + exact (ih (h1.old_of_value H.map_wf h hv)).mono h1.le + | induct h1 H ih => + exact (ih (h1.old_of_value H.map_wf h hv)).mono h1.le + | inductBlock h1 H ih => + exact (ih (h1.old_of_value H.map_wf h hv)).mono h1.le + | inductNested h1 H ih => + exact (ih (h1.old_of_value H.map_wf h hv)).mono h1.le + | structEta _ H ih => + exact (ih h).mono VEnv.addStructEta_le + +nonrec theorem TrEnv.of_value (H : TrEnv safety env venv) (h : env.find? name = some ci) + (hs : safety ≤ ci.safety) (hv : ci.deltaValue? = some v) : + TrExpr venv ci.levelParams [] v (.const ci.name (VLevel.params ci.levelParams.length)) := + H.of_value (by rwa [← H.map_wf.find?'_eq_find?]) hs hv diff --git a/Ix/Theory/Named/Verify/Environment/MutualInductiveFixtures.lean b/Ix/Theory/Named/Verify/Environment/MutualInductiveFixtures.lean new file mode 100644 index 000000000..4fa9ec2de --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/MutualInductiveFixtures.lean @@ -0,0 +1,3150 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Verify.Environment.InductiveFixtures +import Ix.Theory.Named.MutualInductiveFixtures + +open Ix.Theory (VLevel) + +/-! +# Mutual generation, preservation, and environment replay + +The real `Tree`/`TreeList` and indexed mutual fixtures instantiate the +arbitrary-block validator traces, generate one recursor per family with one +globally flattened minor/rule inventory, and compare every kernel metadata +record. Their Theory transactions stage all families before constructors, +all constructors before recursors, and all recursors before rules, then replay +the same phases through `TrEnv'.inductBlock` into aligned Verify environments. +-/ + +namespace Ix.Theory.Named.MutualInductiveReplayFixtures + +open Lean Meta Elab Term +open Kernel +open AddInductive +open VInductDecl +open Ix.Theory.Named.MutualInductiveFixtures +open Ix.Theory.Named.InductiveReplayFixtures + +local instance : Inhabited VEnv := ⟨.empty⟩ + +/-- Quote a kernel recursor type using the recursor metadata's own universe +parameter order. -/ +syntax "kernelRecConstant08C%" ident : term +syntax "kernelConstVType08C%" ident : term + +elab_rules : term + | `(kernelRecConstant08C% $n:ident) => do + let name ← realizeGlobalConstNoOverloadWithInfo n + let .recInfo info ← getConstInfo name + | throwError "expected recursor metadata for {name}" + let type ← Ix.Theory.Named.Meta.expandExpr info.type + let type ← Ix.Theory.Named.Meta.ofExpr info.levelParams {} type + return toExpr ({ uvars := info.levelParams.length, type } : VConstant) + +/-- Quote a stored kernel metadata type in that record's own universe order. +Together with the explicit record-field checks below, this observes universe +permutations as well as the translated type expression. -/ +elab_rules : term + | `(kernelConstVType08C% $n:ident) => do + let name ← realizeGlobalConstNoOverloadWithInfo n + let info ← getConstInfo name + let type ← Ix.Theory.Named.Meta.expandExpr info.type + let type ← Ix.Theory.Named.Meta.ofExpr info.levelParams {} type + return toExpr type + +/-! ## Complete block metadata parity -/ + +/-- One block-wide comparison between the retained Theory generation and all +kernel inductive metadata. The nested lists preserve family/constructor +ownership and split each recursor's local rules out of the globally flattened +Theory rule list. -/ +structure MutualKernelBlockRow where + source : VInductDecl + generation : BlockGenerationChecked source + inductInfos : List ConstantInfo + ctorInfos : List (List ConstantInfo) + recInfos : List ConstantInfo + familyTypes : List VExpr + ctorTypes : List (List VExpr) + recTypes : List VExpr + ruleRhs : List (List VExpr) + +namespace MutualKernelBlockRow + +def ctorMatches (row : MutualKernelBlockRow) + (family : NormalizedFamily) (cidx : Nat) (info : ConstantInfo) + (constructor : NormalizedCtor) (storedType : VExpr) : Bool := + match info with + | .ctorInfo ctor => + ctor.name == constructor.raw.name && + ctor.levelParams.length == row.source.uvars && + ctor.induct == family.raw.name && + ctor.cidx == cidx && + ctor.numParams == row.source.nparams && + ctor.numFields == constructor.view.fields.length && + !ctor.isUnsafe && storedType == constructor.raw.type + | _ => false + +def ctorsMatch (row : MutualKernelBlockRow) + (family : NormalizedFamily) : + List ConstantInfo → List NormalizedCtor → List VExpr → Nat → Bool + | [], [], [], _ => true + | info :: infos, constructor :: constructors, storedType :: storedTypes, + cidx => + row.ctorMatches family cidx info constructor storedType && + row.ctorsMatch family infos constructors storedTypes (cidx + 1) + | _, _, _, _ => false + +def recursorMatches (row : MutualKernelBlockRow) + (family : NormalizedFamily) (offset : Nat) + (info : ConstantInfo) (storedType : VExpr) + (rhs : List VExpr) : Bool := + match info with + | .recInfo rec => + rec.name == .str family.raw.name "rec" && + rec.levelParams.length == row.generation.recUvars && + rec.all == row.source.types.map (·.name) && + rec.numParams == row.source.nparams && + rec.numIndices == family.view.indices.length && + rec.numMotives == row.generation.familyCount && + rec.numMinors == row.generation.minorCount && + rec.k == row.generation.kTarget && + !rec.isUnsafe && + rec.rules.map (fun rule => (rule.ctor, rule.nfields)) == + family.ctorPairs.map (fun constructor => + (constructor.raw.name, constructor.view.fields.length)) && + storedType == (row.generation.recursor family).type && + rhs == ((row.generation.generatedRules.drop offset).take + family.ctorPairs.length |>.map (·.rhs)) + | _ => false + +def familiesMatch (row : MutualKernelBlockRow) : + List NormalizedFamily → List ConstantInfo → List (List ConstantInfo) → + List ConstantInfo → List VExpr → List (List VExpr) → List VExpr → + List (List VExpr) → Nat → Bool + | [], [], [], [], [], [], [], [], _ => true + | family :: families, inductInfo :: inductInfos, + ctorInfos :: ctorInfosTail, recInfo :: recInfos, + familyType :: familyTypes, ctorTypes :: ctorTypesTail, + recType :: recTypes, ruleRhs :: ruleRhsTail, offset => + (match inductInfo with + | .inductInfo induct => + induct.name == family.raw.name && + induct.levelParams.length == row.source.uvars && + induct.numParams == row.source.nparams && + induct.numIndices == family.view.indices.length && + induct.all == row.source.types.map (·.name) && + induct.ctors == family.raw.ctors.map (·.name) && + induct.numNested == 0 && + induct.isRec == row.generation.isRec && + induct.isReflexive == row.generation.isReflexive && + !induct.isUnsafe && + familyType == family.raw.type + | _ => false) && + row.ctorsMatch family ctorInfos family.ctorPairs ctorTypes 0 && + row.recursorMatches family offset recInfo recType ruleRhs && + row.familiesMatch families inductInfos ctorInfosTail recInfos + familyTypes ctorTypesTail recTypes ruleRhsTail + (offset + family.ctorPairs.length) + | _, _, _, _, _, _, _, _, _ => false + +/-- Every list must agree position-for-position; no truncated family, +constructor, recursor, or rule inventory can satisfy the comparison. -/ +def agrees (row : MutualKernelBlockRow) : Bool := + row.familiesMatch row.generation.families row.inductInfos row.ctorInfos + row.recInfos row.familyTypes row.ctorTypes row.recTypes row.ruleRhs 0 + +end MutualKernelBlockRow + +/-! ## Executable kernel validation -/ + +def treeKernelInfo : ConstantInfo := kernelInductInfo% Tree +def treeListKernelInfo : ConstantInfo := kernelInductInfo% TreeList +def treeLeafKernelInfo : ConstantInfo := kernelCtorInfo% Tree.leaf +def treeNodeKernelInfo : ConstantInfo := kernelCtorInfo% Tree.node +def treeBranchKernelInfo : ConstantInfo := kernelCtorInfo% Tree.branch +def treeListNilKernelInfo : ConstantInfo := kernelCtorInfo% TreeList.nil +def treeListConsKernelInfo : ConstantInfo := kernelCtorInfo% TreeList.cons +def treeRecKernelInfo : ConstantInfo := kernelRecInfo% Tree.rec +def treeListRecKernelInfo : ConstantInfo := kernelRecInfo% TreeList.rec +def treeRecKernelConstant : VConstant := kernelRecConstant08C% Tree.rec +def treeListRecKernelConstant : VConstant := + kernelRecConstant08C% TreeList.rec +def treeLeafKernelRuleRhs : VExpr := kernelRecRuleRhs% Tree.rec 0 +def treeNodeKernelRuleRhs : VExpr := kernelRecRuleRhs% Tree.rec 1 +def treeBranchKernelRuleRhs : VExpr := kernelRecRuleRhs% Tree.rec 2 +def treeListNilKernelRuleRhs : VExpr := kernelRecRuleRhs% TreeList.rec 0 +def treeListConsKernelRuleRhs : VExpr := kernelRecRuleRhs% TreeList.rec 1 + +example : treeRecKernelConstant = + treeGeneration.recursors[0].toVConstant := rfl +example : treeListRecKernelConstant = + treeGeneration.recursors[1].toVConstant := rfl +example : treeLeafKernelRuleRhs = treeGeneration.generatedRules[0].rhs := rfl +example : treeNodeKernelRuleRhs = treeGeneration.generatedRules[1].rhs := rfl +example : treeBranchKernelRuleRhs = treeGeneration.generatedRules[2].rhs := rfl +example : treeListNilKernelRuleRhs = treeGeneration.generatedRules[3].rhs := rfl +example : treeListConsKernelRuleRhs = treeGeneration.generatedRules[4].rhs := rfl + +def treeKernelType : InductiveType where + name := treeKernelInfo.name + type := treeKernelInfo.type + ctors := [ + ⟨treeLeafKernelInfo.name, treeLeafKernelInfo.type⟩, + ⟨treeNodeKernelInfo.name, treeNodeKernelInfo.type⟩, + ⟨treeBranchKernelInfo.name, treeBranchKernelInfo.type⟩] + +def treeListKernelType : InductiveType where + name := treeListKernelInfo.name + type := treeListKernelInfo.type + ctors := [ + ⟨treeListNilKernelInfo.name, treeListNilKernelInfo.type⟩, + ⟨treeListConsKernelInfo.name, treeListConsKernelInfo.type⟩] + +def treeKernelTypes : List InductiveType := + [treeKernelType, treeListKernelType] + +def treeKernelContext : AddInductive.Context where + env := Kernel.Environment.ofConstants `_mutualTree ({} : ConstMap) + lparams := [`u] + safety := .safe + allowPrimitive := false + +def treeExecutionResult := + AddInductive.buildNormalizationCandidateExecution 1 treeKernelTypes 0 false + treeKernelContext + +theorem treeExecutionResult_isOk : treeExecutionResult.isOk = true := by + native_decide + +def treeProducedExecution : + { execution // treeExecutionResult = .ok execution } := + match h : treeExecutionResult with + | .ok execution => ⟨execution, rfl⟩ + | .error _ => by + have hOk := treeExecutionResult_isOk + rw [h] at hOk + contradiction + +def treeExecution := treeProducedExecution.val + +def treeFamilyValidationResult := + FamilyValidationBlockRun.buildExecution 1 treeKernelTypes treeKernelContext + +theorem treeFamilyValidationResult_isOk : + treeFamilyValidationResult.isOk = true := by + native_decide + +def treeProducedFamilyValidation : + { run // treeFamilyValidationResult = .ok run } := + match h : treeFamilyValidationResult with + | .ok run => ⟨run, rfl⟩ + | .error _ => by + have hOk := treeFamilyValidationResult_isOk + rw [h] at hOk + contradiction + +def treeFamilyValidation := treeProducedFamilyValidation.val + +def treeConstructorContext : AddInductive.Context := + { treeExecution.validationContext with env := treeExecution.familyEnv } + +def treeConstructorValidationResult := + ConstructorBlockValidationRun.buildExecution treeKernelTypes + treeExecution.stats false treeConstructorContext + +theorem treeConstructorValidationResult_isOk : + treeConstructorValidationResult.isOk = true := by + native_decide + +def treeProducedConstructorValidation : + { run // treeConstructorValidationResult = .ok run } := + match h : treeConstructorValidationResult with + | .ok run => ⟨run, rfl⟩ + | .error _ => by + have hOk := treeConstructorValidationResult_isOk + rw [h] at hOk + contradiction + +def treeConstructorValidation := treeProducedConstructorValidation.val + +/-- The real positivity traversal selects both sibling targets and records the +one Pi binder above `Tree.branch`'s recursive occurrence. -/ +theorem treeConstructorTargets_exact : + treeConstructorValidation.traces.targets = + [ + [[none], + [some { familyIdx := 1, binderDepth := 0 }], + [some { familyIdx := 1, binderDepth := 1 }]], + [[], + [some { familyIdx := 0, binderDepth := 0 }, + some { familyIdx := 1, binderDepth := 0 }]] + ] := by + native_decide + +example : treeFamilyValidation.parameters.size = 1 := + treeFamilyValidation.params_size + +example : treeFamilyValidation.result.stats.nindices = #[0, 0] := by + native_decide + +#guard AddInductive.levelStructEq treeFamilyValidation.resultLevel + (.succ (.param `u)) + +#guard AddInductive.levelStructEq treeExecution.stats.resultLevel + (.succ (.param `u)) + +def indexedTreeKernelInfo : ConstantInfo := kernelInductInfo% IndexedTree +def indexedTreeListKernelInfo : ConstantInfo := + kernelInductInfo% IndexedTreeList +def indexedTreeLeafKernelInfo : ConstantInfo := + kernelCtorInfo% IndexedTree.leaf +def indexedTreeNodeKernelInfo : ConstantInfo := + kernelCtorInfo% IndexedTree.node +def indexedTreeListNilKernelInfo : ConstantInfo := + kernelCtorInfo% IndexedTreeList.nil +def indexedTreeListConsKernelInfo : ConstantInfo := + kernelCtorInfo% IndexedTreeList.cons +def indexedTreeRecKernelInfo : ConstantInfo := + kernelRecInfo% IndexedTree.rec +def indexedTreeListRecKernelInfo : ConstantInfo := + kernelRecInfo% IndexedTreeList.rec +def indexedTreeRecKernelConstant : VConstant := + kernelRecConstant08C% IndexedTree.rec +def indexedTreeListRecKernelConstant : VConstant := + kernelRecConstant08C% IndexedTreeList.rec +def indexedTreeLeafKernelRuleRhs : VExpr := + kernelRecRuleRhs% IndexedTree.rec 0 +def indexedTreeNodeKernelRuleRhs : VExpr := + kernelRecRuleRhs% IndexedTree.rec 1 +def indexedTreeListNilKernelRuleRhs : VExpr := + kernelRecRuleRhs% IndexedTreeList.rec 0 +def indexedTreeListConsKernelRuleRhs : VExpr := + kernelRecRuleRhs% IndexedTreeList.rec 1 + +example : indexedTreeRecKernelConstant = + indexedTreeGeneration.recursors[0].toVConstant := rfl +example : indexedTreeListRecKernelConstant = + indexedTreeGeneration.recursors[1].toVConstant := rfl +example : indexedTreeLeafKernelRuleRhs = + indexedTreeGeneration.generatedRules[0].rhs := rfl +example : indexedTreeNodeKernelRuleRhs = + indexedTreeGeneration.generatedRules[1].rhs := rfl +example : indexedTreeListNilKernelRuleRhs = + indexedTreeGeneration.generatedRules[2].rhs := rfl +example : indexedTreeListConsKernelRuleRhs = + indexedTreeGeneration.generatedRules[3].rhs := rfl + +/-- Complete kernel/Theory metadata row for the unindexed mutual block. -/ +def treeKernelBlockRow : MutualKernelBlockRow where + source := treeDecl + generation := treeGeneration + inductInfos := [treeKernelInfo, treeListKernelInfo] + ctorInfos := [ + [treeLeafKernelInfo, treeNodeKernelInfo, treeBranchKernelInfo], + [treeListNilKernelInfo, treeListConsKernelInfo]] + recInfos := [treeRecKernelInfo, treeListRecKernelInfo] + familyTypes := [kernelConstVType08C% Tree, + kernelConstVType08C% TreeList] + ctorTypes := [ + [kernelConstVType08C% Tree.leaf, kernelConstVType08C% Tree.node, + kernelConstVType08C% Tree.branch], + [kernelConstVType08C% TreeList.nil, + kernelConstVType08C% TreeList.cons]] + recTypes := [kernelConstVType08C% Tree.rec, + kernelConstVType08C% TreeList.rec] + ruleRhs := [ + [treeLeafKernelRuleRhs, treeNodeKernelRuleRhs, + treeBranchKernelRuleRhs], + [treeListNilKernelRuleRhs, treeListConsKernelRuleRhs]] + +#guard treeKernelBlockRow.agrees + +/-- Complete kernel/Theory metadata row for the indexed mutual block. -/ +def indexedTreeKernelBlockRow : MutualKernelBlockRow where + source := indexedTreeDecl + generation := indexedTreeGeneration + inductInfos := [indexedTreeKernelInfo, indexedTreeListKernelInfo] + ctorInfos := [ + [indexedTreeLeafKernelInfo, indexedTreeNodeKernelInfo], + [indexedTreeListNilKernelInfo, indexedTreeListConsKernelInfo]] + recInfos := [indexedTreeRecKernelInfo, indexedTreeListRecKernelInfo] + familyTypes := [kernelConstVType08C% IndexedTree, + kernelConstVType08C% IndexedTreeList] + ctorTypes := [ + [kernelConstVType08C% IndexedTree.leaf, + kernelConstVType08C% IndexedTree.node], + [kernelConstVType08C% IndexedTreeList.nil, + kernelConstVType08C% IndexedTreeList.cons]] + recTypes := [kernelConstVType08C% IndexedTree.rec, + kernelConstVType08C% IndexedTreeList.rec] + ruleRhs := [ + [indexedTreeLeafKernelRuleRhs, indexedTreeNodeKernelRuleRhs], + [indexedTreeListNilKernelRuleRhs, indexedTreeListConsKernelRuleRhs]] + +#guard indexedTreeKernelBlockRow.agrees + +def indexedTreeKernelType : InductiveType where + name := indexedTreeKernelInfo.name + type := indexedTreeKernelInfo.type + ctors := [ + ⟨indexedTreeLeafKernelInfo.name, indexedTreeLeafKernelInfo.type⟩, + ⟨indexedTreeNodeKernelInfo.name, indexedTreeNodeKernelInfo.type⟩] + +def indexedTreeListKernelType : InductiveType where + name := indexedTreeListKernelInfo.name + type := indexedTreeListKernelInfo.type + ctors := [ + ⟨indexedTreeListNilKernelInfo.name, indexedTreeListNilKernelInfo.type⟩, + ⟨indexedTreeListConsKernelInfo.name, indexedTreeListConsKernelInfo.type⟩] + +def indexedTreeKernelTypes : List InductiveType := + [indexedTreeKernelType, indexedTreeListKernelType] + +def indexedTreeKernelContext : AddInductive.Context where + env := Kernel.Environment.ofConstants `_mutualIndexedTree natMap + lparams := [`u] + safety := .safe + allowPrimitive := false + +def indexedTreeExecutionResult := + AddInductive.buildNormalizationCandidateExecution 1 indexedTreeKernelTypes + 0 false indexedTreeKernelContext + +theorem indexedTreeExecutionResult_isOk : + indexedTreeExecutionResult.isOk = true := by + native_decide + +def indexedTreeProducedExecution : + { execution // indexedTreeExecutionResult = .ok execution } := + match h : indexedTreeExecutionResult with + | .ok execution => ⟨execution, rfl⟩ + | .error _ => by + have hOk := indexedTreeExecutionResult_isOk + rw [h] at hOk + contradiction + +def indexedTreeExecution := indexedTreeProducedExecution.val + +def indexedTreeFamilyValidationResult := + FamilyValidationBlockRun.buildExecution 1 indexedTreeKernelTypes + indexedTreeKernelContext + +theorem indexedTreeFamilyValidationResult_isOk : + indexedTreeFamilyValidationResult.isOk = true := by + native_decide + +def indexedTreeProducedFamilyValidation : + { run // indexedTreeFamilyValidationResult = .ok run } := + match h : indexedTreeFamilyValidationResult with + | .ok run => ⟨run, rfl⟩ + | .error _ => by + have hOk := indexedTreeFamilyValidationResult_isOk + rw [h] at hOk + contradiction + +def indexedTreeFamilyValidation := indexedTreeProducedFamilyValidation.val + +def indexedTreeConstructorContext : AddInductive.Context := + { indexedTreeExecution.validationContext with + env := indexedTreeExecution.familyEnv } + +def indexedTreeConstructorValidationResult := + ConstructorBlockValidationRun.buildExecution indexedTreeKernelTypes + indexedTreeExecution.stats false indexedTreeConstructorContext + +theorem indexedTreeConstructorValidationResult_isOk : + indexedTreeConstructorValidationResult.isOk = true := by + native_decide + +def indexedTreeProducedConstructorValidation : + { run // indexedTreeConstructorValidationResult = .ok run } := + match h : indexedTreeConstructorValidationResult with + | .ok run => ⟨run, rfl⟩ + | .error _ => by + have hOk := indexedTreeConstructorValidationResult_isOk + rw [h] at hOk + contradiction + +def indexedTreeConstructorValidation := + indexedTreeProducedConstructorValidation.val + +/-- Indexed recursive fields retain sibling ordinals after the ordinary Nat +binder, while nonrecursive fields occupy explicit `none` slots. -/ +theorem indexedTreeConstructorTargets_exact : + indexedTreeConstructorValidation.traces.targets = + [ + [[none], + [none, some { familyIdx := 1, binderDepth := 0 }]], + [[], + [none, + some { familyIdx := 0, binderDepth := 0 }, + some { familyIdx := 1, binderDepth := 0 }]] + ] := by + native_decide + +example : indexedTreeFamilyValidation.parameters.size = 1 := + indexedTreeFamilyValidation.params_size + +example : indexedTreeFamilyValidation.result.stats.nindices = #[1, 1] := by + native_decide + +#guard AddInductive.levelStructEq indexedTreeFamilyValidation.resultLevel + (.succ (.param `u)) + +/-! ## Phase-specific failures -/ + +def treeParameterMismatchType : InductiveType := + { treeListKernelType with + type := .forallE `α (.sort .zero) (.sort (.succ (.param `u))) .default } + +#guard match observeFamilyValidationBlock 1 + [treeKernelType, treeParameterMismatchType] treeKernelContext with + | .error (.other message) => + message == "parameters of all inductive datatypes must match" + | _ => false + +def treeResultUniverseMismatchType : InductiveType := + { treeListKernelType with + type := .forallE `α (.sort (.succ (.param `u))) (.sort .zero) .default } + +#guard match observeFamilyValidationBlock 1 + [treeKernelType, treeResultUniverseMismatchType] treeKernelContext with + | .error (.other message) => + message == "mutually inductive types must live in the same universe" + | _ => false + +/-- Reusing the original family statistics after swapping source owners +reaches constructor validation and fails at the first now-misowned return. -/ +def treeReorderedConstructorResult := + checkConstructors #[treeListKernelType, treeKernelType] + treeExecution.stats false treeConstructorContext + +#guard match treeReorderedConstructorResult with + | .error (.other message) => + message == + "invalid return type for 'Ix.Theory.Named.MutualInductiveFixtures.TreeList.nil'" + | _ => false + +def treeReorderedView : VInductDecl := + { treeDecl with types := [treeListType, treeType] } + +/- Raw/view normalization rejects family reordering before semantic evidence +can be attached to the dependent candidate list. -/ +#guard (normalization? treeDecl treeReorderedView).isNone + +/-! The host elaborator diagnostics independently pin the corresponding +family-validation phases in Lean itself. -/ + +namespace KernelPhases + +universe v + +/-- +error: Invalid mutually inductive types: Parameter `α` has type + Prop +of sort `Type` but is expected to have type + Type v +of sort `Type (v + 1)` +-/ +#guard_msgs (whitespace := lax) in +mutual +inductive ParamA (α : Type v) : Type v +inductive ParamB (α : Prop) : Type v +end + +/-- +error: Invalid mutually inductive types: The resulting type of this declaration + Prop +differs from a preceding one + Type v + +Note: All inductive types declared in the same `mutual` block must belong to the same type universe +-/ +#guard_msgs (whitespace := lax) in +mutual +inductive UniverseA (α : Type v) : Type v +inductive UniverseB (α : Type v) : Prop +end + +end KernelPhases + +/-! ## Tree/TreeList Theory semantics -/ + +def treeBlockEnv : VEnv := + (VEnv.empty.stageInductiveTypes treeDecl.types).get! + +theorem treeStage : + VEnv.empty.stageInductiveTypes treeDecl.types = some treeBlockEnv := by + rfl + +theorem empty_le_treeBlockEnv : VEnv.empty ≤ treeBlockEnv where + constants h := by simp [VEnv.empty] at h + defeqs h := h.elim + structEtas h := h.elim + +theorem treeFamilyTypeWF (type : VInductiveType) + (h : type = treeType ∨ type = treeListType) : + type.type.WF VEnv.empty type.uvars [] := by + rcases h with rfl | rfl <;> + refine ⟨.sort (.imax (.succ (.succ (.param 0))) + (.succ (.succ (.param 0)))), VEnv.HasType.forallE ?_ ?_⟩ <;> + exact VEnv.HasType.sort (by decide) + +theorem treeCtorWF (ctor : VConstVal) + (h : ctor ∈ treeType.ctors ∨ ctor ∈ treeListType.ctors) : + ctor.type.WF treeBlockEnv 1 [] := by + have hTree : treeBlockEnv.constants ``Tree = + some treeType.toVConstant := by rfl + have hTreeList : treeBlockEnv.constants ``TreeList = + some treeListType.toVConstant := by rfl + rcases h with h | h + · simp [treeType] at h + rcases h with (rfl | rfl | rfl) + · exact ⟨_, by type_tac⟩ + · exact ⟨_, by type_tac⟩ + · exact ⟨_, by type_tac⟩ + · simp [treeListType] at h + rcases h with (rfl | rfl) + · exact ⟨_, by type_tac⟩ + · exact ⟨_, by type_tac⟩ + +theorem treeNormalizationBlockWF : + (Normalization.identity treeDecl).BlockWF VEnv.empty treeBlockEnv := by + refine ⟨treeStage, ?_⟩ + change List.Forall₂ _ [treeType, treeListType] [treeType, treeListType] + apply List.Forall₂.cons + · refine ⟨?_, ?_⟩ + · exact VEnv.IsDefEqU.refl (treeFamilyTypeWF treeType (.inl rfl)) + · apply List.Forall₂.rfl + intro ctor h + exact VEnv.IsDefEqU.refl (treeCtorWF ctor (.inl h)) + · apply List.Forall₂.cons + · refine ⟨?_, ?_⟩ + · exact VEnv.IsDefEqU.refl (treeFamilyTypeWF treeListType (.inr rfl)) + · apply List.Forall₂.rfl + intro ctor h + exact VEnv.IsDefEqU.refl (treeCtorWF ctor (.inr h)) + · exact .nil + +theorem treeLeafSemantic : + let constructor := CheckedCtor.ofBlock treeDecl treeType.ctors[0] + checkedBlockFieldsWF VEnv.empty 1 (.succ (.param 0)) [[], []] + constructor.fields constructor.recursiveAt + [.sort (.succ (.param 0))] 0 ∧ + VEnv.empty.SpineWF 1 + (constructor.fields.reverse ++ [.sort (.succ (.param 0))]) + (VExpr.forallN (VExpr.liftTelN constructor.fields.length [] 0) + (.sort (.succ (.param 0)))) + constructor.resultIndices (.sort (.succ (.param 0))) := by + dsimp only + rw [show (CheckedCtor.ofBlock treeDecl treeType.ctors[0]).fields = + [.bvar 0] by rfl] + rw [show (CheckedCtor.ofBlock treeDecl treeType.ctors[0]).recursiveAt = + [none] by rfl] + rw [show (CheckedCtor.ofBlock treeDecl treeType.ctors[0]).resultIndices = + [] by rfl] + exact ⟨⟨⟨.succ (.param 0), .bvar .zero, + .inr (VLevel.le_refl _)⟩, trivial⟩, .nil⟩ + +theorem treeNodeSemantic : + let constructor := CheckedCtor.ofBlock treeDecl treeType.ctors[1] + checkedBlockFieldsWF VEnv.empty 1 (.succ (.param 0)) [[], []] + constructor.fields constructor.recursiveAt + [.sort (.succ (.param 0))] 0 ∧ + VEnv.empty.SpineWF 1 + (constructor.fields.reverse ++ [.sort (.succ (.param 0))]) + (VExpr.forallN (VExpr.liftTelN constructor.fields.length [] 0) + (.sort (.succ (.param 0)))) + constructor.resultIndices (.sort (.succ (.param 0))) := by + dsimp only + rw [show (CheckedCtor.ofBlock treeDecl treeType.ctors[1]).fields = + [.app (.const ``TreeList [.param 0]) (.bvar 0)] by rfl] + rw [show (CheckedCtor.ofBlock treeDecl treeType.ctors[1]).recursiveAt = + [some ({ + fieldIndex := 0 + binders := [] + targetType := 1 + indices := [] } : RecArg)] by rfl] + rw [show (CheckedCtor.ofBlock treeDecl treeType.ctors[1]).resultIndices = + [] by rfl] + exact ⟨⟨⟨rfl, trivial, .nil⟩, trivial⟩, .nil⟩ + +theorem treeBranchSemantic : + let constructor := CheckedCtor.ofBlock treeDecl treeType.ctors[2] + checkedBlockFieldsWF VEnv.empty 1 (.succ (.param 0)) [[], []] + constructor.fields constructor.recursiveAt + [.sort (.succ (.param 0))] 0 ∧ + VEnv.empty.SpineWF 1 + (constructor.fields.reverse ++ [.sort (.succ (.param 0))]) + (VExpr.forallN (VExpr.liftTelN constructor.fields.length [] 0) + (.sort (.succ (.param 0)))) + constructor.resultIndices (.sort (.succ (.param 0))) := by + dsimp only + rw [show (CheckedCtor.ofBlock treeDecl treeType.ctors[2]).fields = + [.forallE (.bvar 0) + (.app (.const ``TreeList [.param 0]) (.bvar 1))] by rfl] + rw [show (CheckedCtor.ofBlock treeDecl treeType.ctors[2]).recursiveAt = + [some ({ + fieldIndex := 0 + binders := [.bvar 0] + targetType := 1 + indices := [] } : RecArg)] by rfl] + rw [show (CheckedCtor.ofBlock treeDecl treeType.ctors[2]).resultIndices = + [] by rfl] + exact ⟨ + ⟨⟨rfl, ⟨⟨⟨.succ (.param 0), .bvar .zero⟩, trivial⟩, .nil⟩⟩, + trivial⟩, + .nil⟩ + +theorem treeListNilSemantic : + let constructor := CheckedCtor.ofBlock treeDecl treeListType.ctors[0] + checkedBlockFieldsWF VEnv.empty 1 (.succ (.param 0)) [[], []] + constructor.fields constructor.recursiveAt + [.sort (.succ (.param 0))] 0 ∧ + VEnv.empty.SpineWF 1 + (constructor.fields.reverse ++ [.sort (.succ (.param 0))]) + (VExpr.forallN (VExpr.liftTelN constructor.fields.length [] 0) + (.sort (.succ (.param 0)))) + constructor.resultIndices (.sort (.succ (.param 0))) := by + dsimp only + rw [show (CheckedCtor.ofBlock treeDecl treeListType.ctors[0]).fields = + [] by rfl] + rw [show (CheckedCtor.ofBlock treeDecl treeListType.ctors[0]).recursiveAt = + [] by rfl] + rw [show (CheckedCtor.ofBlock treeDecl treeListType.ctors[0]).resultIndices = + [] by rfl] + exact ⟨trivial, .nil⟩ + +theorem treeListConsSemantic : + let constructor := CheckedCtor.ofBlock treeDecl treeListType.ctors[1] + checkedBlockFieldsWF VEnv.empty 1 (.succ (.param 0)) [[], []] + constructor.fields constructor.recursiveAt + [.sort (.succ (.param 0))] 0 ∧ + VEnv.empty.SpineWF 1 + (constructor.fields.reverse ++ [.sort (.succ (.param 0))]) + (VExpr.forallN (VExpr.liftTelN constructor.fields.length [] 0) + (.sort (.succ (.param 0)))) + constructor.resultIndices (.sort (.succ (.param 0))) := by + dsimp only + rw [show (CheckedCtor.ofBlock treeDecl treeListType.ctors[1]).fields = + [.app (.const ``Tree [.param 0]) (.bvar 0), + .app (.const ``TreeList [.param 0]) (.bvar 1)] by rfl] + rw [show (CheckedCtor.ofBlock treeDecl treeListType.ctors[1]).recursiveAt = + [some ({ + fieldIndex := 0 + binders := [] + targetType := 0 + indices := [] } : RecArg), + some ({ + fieldIndex := 1 + binders := [] + targetType := 1 + indices := [] } : RecArg)] by rfl] + rw [show (CheckedCtor.ofBlock treeDecl treeListType.ctors[1]).resultIndices = + [] by rfl] + exact ⟨ + ⟨⟨rfl, trivial, .nil⟩, + ⟨⟨rfl, trivial, .nil⟩, trivial⟩⟩, + .nil⟩ + +theorem treeCheckedBlockWF : + treeChecked.WF VEnv.empty (.succ (.param 0)) := by + have hparams : treeChecked.params = + [.sort (.succ (.param 0))] := rfl + have hindices : treeChecked.families.indices = [[], []] := rfl + have hlevels : treeChecked.families.resultLevels = + [.succ (.param 0), .succ (.param 0)] := rfl + have hconstructors : treeChecked.families.constructors = + [treeType.ctors.map (CheckedCtor.ofBlock treeDecl), + treeListType.ctors.map (CheckedCtor.ofBlock treeDecl)] := rfl + rw [CheckedBlock.WF, hindices, hlevels, hconstructors] + rw [hparams] + simp only [checkedFamilyListsWF] + refine ⟨rfl, ?_, ?_, rfl, ?_, ?_⟩ + · exact ⟨⟨_, VEnv.HasType.sort (by decide)⟩, trivial⟩ + · intro ctor h + simp [treeType] at h + rcases h with rfl | rfl | rfl + · exact treeLeafSemantic + · exact treeNodeSemantic + · exact treeBranchSemantic + · exact ⟨⟨_, VEnv.HasType.sort (by decide)⟩, trivial⟩ + · refine ⟨?_, trivial⟩ + intro ctor h + simp [treeListType] at h + rcases h with rfl | rfl + · exact treeListNilSemantic + · exact treeListConsSemantic + +def treeNormalizedBlock : NormalizedCheckedBlock treeDecl where + normalization := Normalization.identity treeDecl + checked := treeChecked + checked_eq := (Option.some_get (x := treeDecl.checkedBlock?) + (by decide)).symm + +def treeValidatedBlock : ValidatedBlock treeDecl where + block := treeNormalizedBlock + resultLevel := .succ (.param 0) + +/-- Exact validation-only semantic package for the unindexed mutual block. -/ +def treeValidationCertificate : ValidationCertificate treeDecl VEnv.empty where + validated := treeValidatedBlock + blockEnv := treeBlockEnv + wf := ⟨treeNormalizationBlockWF, treeCheckedBlockWF⟩ + +/-! ## IndexedTree/IndexedTreeList Theory semantics -/ + +def indexedTreeBlockEnv : VEnv := + (natFinalEnv.stageInductiveTypes indexedTreeDecl.types).get! + +theorem indexedTreeStage : + natFinalEnv.stageInductiveTypes indexedTreeDecl.types = + some indexedTreeBlockEnv := by + rfl + +theorem indexedTreeFamilyTypeWF (type : VInductiveType) + (h : type = indexedTreeType ∨ type = indexedTreeListType) : + type.type.WF natFinalEnv type.uvars [] := by + have hNat : natFinalEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + rcases h with rfl | rfl <;> + exact ⟨_, by type_tac⟩ + +theorem indexedTreeCtorWF (ctor : VConstVal) + (h : ctor ∈ indexedTreeType.ctors ∨ + ctor ∈ indexedTreeListType.ctors) : + ctor.type.WF indexedTreeBlockEnv 1 [] := by + have hNat : indexedTreeBlockEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := by rfl + have hZero : indexedTreeBlockEnv.constants ``Nat.zero = + some InductiveFixtures.natType.ctors[0].toVConstant := by rfl + have hSucc : indexedTreeBlockEnv.constants ``Nat.succ = + some InductiveFixtures.natType.ctors[1].toVConstant := by rfl + have hTree : indexedTreeBlockEnv.constants ``IndexedTree = + some indexedTreeType.toVConstant := by rfl + have hTreeList : indexedTreeBlockEnv.constants ``IndexedTreeList = + some indexedTreeListType.toVConstant := by rfl + rcases h with h | h + · simp [indexedTreeType] at h + rcases h with (rfl | rfl) + · exact ⟨_, by type_tac⟩ + · exact ⟨_, by type_tac⟩ + · simp [indexedTreeListType] at h + rcases h with (rfl | rfl) + · exact ⟨_, by type_tac⟩ + · exact ⟨_, by type_tac⟩ + +theorem indexedTreeNormalizationBlockWF : + (Normalization.identity indexedTreeDecl).BlockWF + natFinalEnv indexedTreeBlockEnv := by + refine ⟨indexedTreeStage, ?_⟩ + change List.Forall₂ _ [indexedTreeType, indexedTreeListType] + [indexedTreeType, indexedTreeListType] + apply List.Forall₂.cons + · refine ⟨?_, ?_⟩ + · exact VEnv.IsDefEqU.refl + (indexedTreeFamilyTypeWF indexedTreeType (.inl rfl)) + · apply List.Forall₂.rfl + intro ctor h + exact VEnv.IsDefEqU.refl (indexedTreeCtorWF ctor (.inl h)) + · apply List.Forall₂.cons + · refine ⟨?_, ?_⟩ + · exact VEnv.IsDefEqU.refl + (indexedTreeFamilyTypeWF indexedTreeListType (.inr rfl)) + · apply List.Forall₂.rfl + intro ctor h + exact VEnv.IsDefEqU.refl (indexedTreeCtorWF ctor (.inr h)) + · exact .nil + +theorem indexedTreeLeafSemantic : + let constructor := CheckedCtor.ofBlock indexedTreeDecl + indexedTreeType.ctors[0] + checkedBlockFieldsWF natFinalEnv 1 (.succ (.param 0)) + [[.const ``Nat []], [.const ``Nat []]] + constructor.fields constructor.recursiveAt + [.sort (.succ (.param 0))] 0 ∧ + natFinalEnv.SpineWF 1 + (constructor.fields.reverse ++ [.sort (.succ (.param 0))]) + (VExpr.forallN + (VExpr.liftTelN constructor.fields.length [.const ``Nat []] 0) + (.sort (.succ (.param 0)))) + constructor.resultIndices (.sort (.succ (.param 0))) := by + dsimp only + rw [show (CheckedCtor.ofBlock indexedTreeDecl + indexedTreeType.ctors[0]).fields = [.bvar 0] by rfl] + rw [show (CheckedCtor.ofBlock indexedTreeDecl + indexedTreeType.ctors[0]).recursiveAt = [none] by rfl] + rw [show (CheckedCtor.ofBlock indexedTreeDecl + indexedTreeType.ctors[0]).resultIndices = [.const ``Nat.zero []] by rfl] + constructor + · exact ⟨⟨.succ (.param 0), .bvar .zero, + .inr (VLevel.le_refl _)⟩, trivial⟩ + · have hNat : natFinalEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + have hZero : natFinalEnv.constants ``Nat.zero = + some InductiveFixtures.natType.ctors[0].toVConstant := rfl + exact .cons (by type_tac) .nil + +theorem indexedTreeNodeSemantic : + let constructor := CheckedCtor.ofBlock indexedTreeDecl + indexedTreeType.ctors[1] + checkedBlockFieldsWF natFinalEnv 1 (.succ (.param 0)) + [[.const ``Nat []], [.const ``Nat []]] + constructor.fields constructor.recursiveAt + [.sort (.succ (.param 0))] 0 ∧ + natFinalEnv.SpineWF 1 + (constructor.fields.reverse ++ [.sort (.succ (.param 0))]) + (VExpr.forallN + (VExpr.liftTelN constructor.fields.length [.const ``Nat []] 0) + (.sort (.succ (.param 0)))) + constructor.resultIndices (.sort (.succ (.param 0))) := by + dsimp only + rw [show (CheckedCtor.ofBlock indexedTreeDecl + indexedTreeType.ctors[1]).fields = + [.const ``Nat [], + (VExpr.const ``IndexedTreeList [.param 0]).app (.bvar 1) + |>.app (.bvar 0)] by rfl] + rw [show (CheckedCtor.ofBlock indexedTreeDecl + indexedTreeType.ctors[1]).recursiveAt = + [none, some ({ + fieldIndex := 1 + binders := [] + targetType := 1 + indices := [.bvar 0] } : RecArg)] by rfl] + rw [show (CheckedCtor.ofBlock indexedTreeDecl + indexedTreeType.ctors[1]).resultIndices = + [(VExpr.const ``Nat.succ []).app (.bvar 1)] by rfl] + have hNat : natFinalEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + have hSucc : natFinalEnv.constants ``Nat.succ = + some InductiveFixtures.natType.ctors[1].toVConstant := rfl + refine ⟨?_, ?_⟩ + · refine ⟨⟨.succ .zero, (by type_tac), + .inr (VLevel.succ_le_succ VLevel.zero_le)⟩, ?_⟩ + exact ⟨⟨rfl, trivial, .cons (by type_tac) .nil⟩, trivial⟩ + · exact .cons (by type_tac) .nil + +theorem indexedTreeListNilSemantic : + let constructor := CheckedCtor.ofBlock indexedTreeDecl + indexedTreeListType.ctors[0] + checkedBlockFieldsWF natFinalEnv 1 (.succ (.param 0)) + [[.const ``Nat []], [.const ``Nat []]] + constructor.fields constructor.recursiveAt + [.sort (.succ (.param 0))] 0 ∧ + natFinalEnv.SpineWF 1 + (constructor.fields.reverse ++ [.sort (.succ (.param 0))]) + (VExpr.forallN + (VExpr.liftTelN constructor.fields.length [.const ``Nat []] 0) + (.sort (.succ (.param 0)))) + constructor.resultIndices (.sort (.succ (.param 0))) := by + dsimp only + rw [show (CheckedCtor.ofBlock indexedTreeDecl + indexedTreeListType.ctors[0]).fields = [] by rfl] + rw [show (CheckedCtor.ofBlock indexedTreeDecl + indexedTreeListType.ctors[0]).recursiveAt = [] by rfl] + rw [show (CheckedCtor.ofBlock indexedTreeDecl + indexedTreeListType.ctors[0]).resultIndices = + [.const ``Nat.zero []] by rfl] + refine ⟨trivial, ?_⟩ + have hNat : natFinalEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + have hZero : natFinalEnv.constants ``Nat.zero = + some InductiveFixtures.natType.ctors[0].toVConstant := rfl + exact .cons (by type_tac) .nil + +theorem indexedTreeListConsSemantic : + let constructor := CheckedCtor.ofBlock indexedTreeDecl + indexedTreeListType.ctors[1] + checkedBlockFieldsWF natFinalEnv 1 (.succ (.param 0)) + [[.const ``Nat []], [.const ``Nat []]] + constructor.fields constructor.recursiveAt + [.sort (.succ (.param 0))] 0 ∧ + natFinalEnv.SpineWF 1 + (constructor.fields.reverse ++ [.sort (.succ (.param 0))]) + (VExpr.forallN + (VExpr.liftTelN constructor.fields.length [.const ``Nat []] 0) + (.sort (.succ (.param 0)))) + constructor.resultIndices (.sort (.succ (.param 0))) := by + dsimp only + rw [show (CheckedCtor.ofBlock indexedTreeDecl + indexedTreeListType.ctors[1]).fields = + [.const ``Nat [], + (VExpr.const ``IndexedTree [.param 0]).app (.bvar 1) + |>.app (.bvar 0), + (VExpr.const ``IndexedTreeList [.param 0]).app (.bvar 2) + |>.app (.bvar 1)] by rfl] + rw [show (CheckedCtor.ofBlock indexedTreeDecl + indexedTreeListType.ctors[1]).recursiveAt = + [none, + some ({ + fieldIndex := 1 + binders := [] + targetType := 0 + indices := [.bvar 0] } : RecArg), + some ({ + fieldIndex := 2 + binders := [] + targetType := 1 + indices := [.bvar 1] } : RecArg)] by rfl] + rw [show (CheckedCtor.ofBlock indexedTreeDecl + indexedTreeListType.ctors[1]).resultIndices = + [(VExpr.const ``Nat.succ []).app (.bvar 2)] by rfl] + have hNat : natFinalEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + have hSucc : natFinalEnv.constants ``Nat.succ = + some InductiveFixtures.natType.ctors[1].toVConstant := rfl + refine ⟨?_, ?_⟩ + · refine ⟨⟨.succ .zero, (by type_tac), + .inr (VLevel.succ_le_succ VLevel.zero_le)⟩, ?_⟩ + refine ⟨⟨rfl, trivial, .cons (by type_tac) .nil⟩, ?_⟩ + exact ⟨⟨rfl, trivial, .cons (by type_tac) .nil⟩, trivial⟩ + · exact .cons (by type_tac) .nil + +theorem indexedTreeCheckedBlockWF : + indexedTreeChecked.WF natFinalEnv (.succ (.param 0)) := by + have hparams : indexedTreeChecked.params = + [.sort (.succ (.param 0))] := rfl + have hindices : indexedTreeChecked.families.indices = + [[.const ``Nat []], [.const ``Nat []]] := rfl + have hlevels : indexedTreeChecked.families.resultLevels = + [.succ (.param 0), .succ (.param 0)] := rfl + have hconstructors : indexedTreeChecked.families.constructors = + [indexedTreeType.ctors.map (CheckedCtor.ofBlock indexedTreeDecl), + indexedTreeListType.ctors.map + (CheckedCtor.ofBlock indexedTreeDecl)] := rfl + rw [CheckedBlock.WF, hindices, hlevels, hconstructors] + rw [hparams] + simp only [checkedFamilyListsWF] + have hNat : natFinalEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + refine ⟨rfl, ?_, ?_, rfl, ?_, ?_⟩ + · exact ⟨⟨_, VEnv.HasType.sort (by decide)⟩, + ⟨⟨_, (by type_tac)⟩, trivial⟩⟩ + · intro ctor h + simp [indexedTreeType] at h + rcases h with rfl | rfl + · exact indexedTreeLeafSemantic + · exact indexedTreeNodeSemantic + · exact ⟨⟨_, VEnv.HasType.sort (by decide)⟩, + ⟨⟨_, (by type_tac)⟩, trivial⟩⟩ + · refine ⟨?_, trivial⟩ + intro ctor h + simp [indexedTreeListType] at h + rcases h with rfl | rfl + · exact indexedTreeListNilSemantic + · exact indexedTreeListConsSemantic + +def indexedTreeNormalizedBlock : NormalizedCheckedBlock indexedTreeDecl where + normalization := Normalization.identity indexedTreeDecl + checked := indexedTreeChecked + checked_eq := (Option.some_get (x := indexedTreeDecl.checkedBlock?) + (by decide)).symm + +def indexedTreeValidatedBlock : ValidatedBlock indexedTreeDecl where + block := indexedTreeNormalizedBlock + resultLevel := .succ (.param 0) + +/-- Exact validation-only semantic package for the indexed mutual block. -/ +def indexedTreeValidationCertificate : + ValidationCertificate indexedTreeDecl natFinalEnv where + validated := indexedTreeValidatedBlock + blockEnv := indexedTreeBlockEnv + wf := ⟨indexedTreeNormalizationBlockWF, indexedTreeCheckedBlockWF⟩ + +/-! ## Certified block-generation semantics -/ + +theorem treeLeafGenerationWF : + NormalizedBlockCtor.WF treeGeneration treeGeneration.flatCtors[0] + treeBlockEnv := by + refine { + declaredTel := ?_ + declaredResult := ?_ + emittedTel := ?_ + emittedResult := ?_ + owner := ?_ + recursive := ?_ + resultSpine := ?_ } + · change treeBlockEnv.TelDefEq 1 [] + [.sort (.succ (.param 0)), .bvar 0] + [.sort (.succ (.param 0)), .bvar 0] + exact (show treeBlockEnv.OnTel 1 [] + [.sort (.succ (.param 0)), .bvar 0] from by + refine ⟨⟨_, VEnv.HasType.sort (by decide)⟩, ?_⟩ + exact ⟨⟨_, by type_tac⟩, trivial⟩).telDefEq_refl + · change treeBlockEnv.IsDefEq 1 + [.bvar 0, .sort (.succ (.param 0))] + (.app (.const ``Tree [.param 0]) (.bvar 1)) + (.app (.const ``Tree [.param 0]) (.bvar 1)) + (.sort (.succ (.param 0))) + have hTree : treeBlockEnv.constants ``Tree = + some treeType.toVConstant := rfl + apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .sort (.succ (.param 0))) + · exact VEnv.HasType.const hTree (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar (.succ .zero) + · change treeBlockEnv.TelDefEq 1 [] + [.sort (.succ (.param 0)), .bvar 0] + [.sort (.succ (.param 0)), .bvar 0] + exact (show treeBlockEnv.OnTel 1 [] + [.sort (.succ (.param 0)), .bvar 0] from by + refine ⟨⟨_, VEnv.HasType.sort (by decide)⟩, ?_⟩ + exact ⟨⟨_, by type_tac⟩, trivial⟩).telDefEq_refl + · change treeBlockEnv.IsDefEq 1 + [.bvar 0, .sort (.succ (.param 0))] + (.app (.const ``Tree [.param 0]) (.bvar 1)) + (.app (.const ``Tree [.param 0]) (.bvar 1)) + (.sort (.succ (.param 0))) + have hTree : treeBlockEnv.constants ``Tree = + some treeType.toVConstant := rfl + apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .sort (.succ (.param 0))) + · exact VEnv.HasType.const hTree (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar (.succ .zero) + · refine ⟨treeGeneration.families[0], ?_, rfl, rfl, rfl⟩ + exact .head _ + · intro recursive hrecursive + change recursive ∈ [] at hrecursive + nomatch hrecursive + · exact treeLeafSemantic.2.mono empty_le_treeBlockEnv + +theorem treeNodeGenerationWF : + NormalizedBlockCtor.WF treeGeneration treeGeneration.flatCtors[1] + treeBlockEnv := by + have hTree : treeBlockEnv.constants ``Tree = + some treeType.toVConstant := rfl + have hTreeList : treeBlockEnv.constants ``TreeList = + some treeListType.toVConstant := rfl + have hbinders : treeBlockEnv.OnTel 1 [] + [.sort (.succ (.param 0)), + .app (.const ``TreeList [.param 0]) (.bvar 0)] := by + refine ⟨⟨_, VEnv.HasType.sort (by decide)⟩, ?_⟩ + refine ⟨⟨.succ (.param 0), ?_⟩, trivial⟩ + apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .sort (.succ (.param 0))) + · exact VEnv.HasType.const hTreeList (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar .zero + have hresult : treeBlockEnv.HasType 1 + [.app (.const ``TreeList [.param 0]) (.bvar 0), + .sort (.succ (.param 0))] + (.app (.const ``Tree [.param 0]) (.bvar 1)) + (.sort (.succ (.param 0))) := by + apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .sort (.succ (.param 0))) + · exact VEnv.HasType.const hTree (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar (.succ .zero) + refine { + declaredTel := ?_ + declaredResult := ?_ + emittedTel := ?_ + emittedResult := ?_ + owner := ?_ + recursive := ?_ + resultSpine := ?_ } + · exact hbinders.telDefEq_refl + · exact hresult + · exact hbinders.telDefEq_refl + · exact hresult + · refine ⟨treeGeneration.families[0], ?_, rfl, rfl, rfl⟩ + exact .head _ + · intro recursive hrecursive + change recursive ∈ [{ + fieldIndex := 0 + binders := [] + targetType := 1 + indices := [] }] at hrecursive + simp only [List.mem_cons, List.not_mem_nil, or_false] at hrecursive + subst recursive + refine ⟨treeGeneration.families[1], ?_, rfl, ?_, ?_⟩ + · exact .tail _ (.head _) + · exact ⟨.app (.const ``TreeList [.param 0]) (.bvar 0), rfl, rfl⟩ + · exact ⟨trivial, .nil⟩ + · exact treeNodeSemantic.2.mono empty_le_treeBlockEnv + +theorem treeBranchGenerationWF : + NormalizedBlockCtor.WF treeGeneration treeGeneration.flatCtors[2] + treeBlockEnv := by + have hTree : treeBlockEnv.constants ``Tree = + some treeType.toVConstant := rfl + have hTreeList : treeBlockEnv.constants ``TreeList = + some treeListType.toVConstant := rfl + have hbinders : treeBlockEnv.OnTel 1 [] + [.sort (.succ (.param 0)), + .forallE (.bvar 0) + (.app (.const ``TreeList [.param 0]) (.bvar 1))] := by + refine ⟨⟨_, VEnv.HasType.sort (by decide)⟩, ?_⟩ + refine ⟨⟨.imax (.succ (.param 0)) (.succ (.param 0)), ?_⟩, + trivial⟩ + apply VEnv.HasType.forallE + · exact VEnv.HasType.bvar .zero + · apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .sort (.succ (.param 0))) + · exact VEnv.HasType.const hTreeList (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar (.succ .zero) + have hresult : treeBlockEnv.HasType 1 + [.forallE (.bvar 0) + (.app (.const ``TreeList [.param 0]) (.bvar 1)), + .sort (.succ (.param 0))] + (.app (.const ``Tree [.param 0]) (.bvar 1)) + (.sort (.succ (.param 0))) := by + apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .sort (.succ (.param 0))) + · exact VEnv.HasType.const hTree (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar (.succ .zero) + refine { + declaredTel := hbinders.telDefEq_refl + declaredResult := hresult + emittedTel := hbinders.telDefEq_refl + emittedResult := hresult + owner := ?_ + recursive := ?_ + resultSpine := treeBranchSemantic.2.mono empty_le_treeBlockEnv } + · refine ⟨treeGeneration.families[0], ?_, rfl, rfl, rfl⟩ + exact .head _ + · intro recursive hrecursive + change recursive ∈ [{ + fieldIndex := 0 + binders := [.bvar 0] + targetType := 1 + indices := [] }] at hrecursive + simp only [List.mem_cons, List.not_mem_nil, or_false] at hrecursive + subst recursive + refine ⟨treeGeneration.families[1], ?_, rfl, ?_, ?_⟩ + · exact .tail _ (.head _) + · exact ⟨.forallE (.bvar 0) + (.app (.const ``TreeList [.param 0]) (.bvar 1)), rfl, rfl⟩ + · exact ⟨⟨⟨_, VEnv.HasType.bvar .zero⟩, trivial⟩, .nil⟩ + +theorem treeListNilGenerationWF : + NormalizedBlockCtor.WF treeGeneration treeGeneration.flatCtors[3] + treeBlockEnv := by + have hTreeList : treeBlockEnv.constants ``TreeList = + some treeListType.toVConstant := rfl + have hbinders : treeBlockEnv.OnTel 1 [] + [.sort (.succ (.param 0))] := + ⟨⟨_, VEnv.HasType.sort (by decide)⟩, trivial⟩ + have hresult : treeBlockEnv.HasType 1 + [.sort (.succ (.param 0))] + (.app (.const ``TreeList [.param 0]) (.bvar 0)) + (.sort (.succ (.param 0))) := by + apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .sort (.succ (.param 0))) + · exact VEnv.HasType.const hTreeList (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar .zero + refine { + declaredTel := hbinders.telDefEq_refl + declaredResult := hresult + emittedTel := hbinders.telDefEq_refl + emittedResult := hresult + owner := ?_ + recursive := ?_ + resultSpine := treeListNilSemantic.2.mono empty_le_treeBlockEnv } + · refine ⟨treeGeneration.families[1], ?_, rfl, rfl, rfl⟩ + exact .tail _ (.head _) + · intro recursive hrecursive + change recursive ∈ [] at hrecursive + nomatch hrecursive + +theorem treeListConsGenerationWF : + NormalizedBlockCtor.WF treeGeneration treeGeneration.flatCtors[4] + treeBlockEnv := by + have hTree : treeBlockEnv.constants ``Tree = + some treeType.toVConstant := rfl + have hTreeList : treeBlockEnv.constants ``TreeList = + some treeListType.toVConstant := rfl + have hbinders : treeBlockEnv.OnTel 1 [] + [.sort (.succ (.param 0)), + .app (.const ``Tree [.param 0]) (.bvar 0), + .app (.const ``TreeList [.param 0]) (.bvar 1)] := by + refine ⟨⟨_, VEnv.HasType.sort (by decide)⟩, ?_⟩ + refine ⟨⟨.succ (.param 0), ?_⟩, ?_⟩ + · apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .sort (.succ (.param 0))) + · exact VEnv.HasType.const hTree (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar .zero + · refine ⟨⟨.succ (.param 0), ?_⟩, trivial⟩ + apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .sort (.succ (.param 0))) + · exact VEnv.HasType.const hTreeList (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar (.succ .zero) + have hresult : treeBlockEnv.HasType 1 + [.app (.const ``TreeList [.param 0]) (.bvar 1), + .app (.const ``Tree [.param 0]) (.bvar 0), + .sort (.succ (.param 0))] + (.app (.const ``TreeList [.param 0]) (.bvar 2)) + (.sort (.succ (.param 0))) := by + apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .sort (.succ (.param 0))) + · exact VEnv.HasType.const hTreeList (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar (.succ (.succ .zero)) + refine { + declaredTel := hbinders.telDefEq_refl + declaredResult := hresult + emittedTel := hbinders.telDefEq_refl + emittedResult := hresult + owner := ?_ + recursive := ?_ + resultSpine := treeListConsSemantic.2.mono empty_le_treeBlockEnv } + · refine ⟨treeGeneration.families[1], ?_, rfl, rfl, rfl⟩ + exact .tail _ (.head _) + · intro recursive hrecursive + change recursive ∈ [{ + fieldIndex := 0 + binders := [] + targetType := 0 + indices := [] }, { + fieldIndex := 1 + binders := [] + targetType := 1 + indices := [] }] at hrecursive + simp only [List.mem_cons, List.not_mem_nil, or_false] at hrecursive + rcases hrecursive with rfl | rfl + · refine ⟨treeGeneration.families[0], ?_, rfl, ?_, ?_⟩ + · exact .head _ + · exact ⟨.app (.const ``Tree [.param 0]) (.bvar 0), rfl, rfl⟩ + · exact ⟨trivial, .nil⟩ + · refine ⟨treeGeneration.families[1], ?_, rfl, ?_, ?_⟩ + · exact .tail _ (.head _) + · exact ⟨.app (.const ``TreeList [.param 0]) (.bvar 1), rfl, rfl⟩ + · exact ⟨trivial, .nil⟩ + +theorem treeBlockGenerationWF : + treeGeneration.WF VEnv.empty treeBlockEnv := by + refine { + blockWF := treeValidationCertificate.wf + resultLevelWF := by decide + paramsTel := ?_ + families := ?_ + constructors := ?_ } + · change VEnv.empty.TelDefEq 1 [] + [.sort (.succ (.param 0))] [.sort (.succ (.param 0))] + exact (show VEnv.empty.OnTel 1 [] + [.sort (.succ (.param 0))] from + ⟨⟨_, VEnv.HasType.sort (by decide)⟩, trivial⟩).telDefEq_refl + · intro family hfamily + have hfamilies : treeGeneration.families = + [treeGeneration.families[0], treeGeneration.families[1]] := rfl + rw [hfamilies] at hfamily + simp only [List.mem_cons, List.not_mem_nil, or_false] at hfamily + rcases hfamily with rfl | rfl + · constructor + · change VEnv.empty.TelDefEq 1 [] + [.sort (.succ (.param 0))] [.sort (.succ (.param 0))] + exact (show VEnv.empty.OnTel 1 [] + [.sort (.succ (.param 0))] from + ⟨⟨_, VEnv.HasType.sort (by decide)⟩, trivial⟩).telDefEq_refl + · change VEnv.empty.IsDefEq 1 [.sort (.succ (.param 0))] + (.sort (.succ (.param 0))) (.sort (.succ (.param 0))) + (.sort (.succ (.succ (.param 0)))) + exact .sortDF (by decide) (by decide) rfl + · constructor + · change VEnv.empty.TelDefEq 1 [] + [.sort (.succ (.param 0))] [.sort (.succ (.param 0))] + exact (show VEnv.empty.OnTel 1 [] + [.sort (.succ (.param 0))] from + ⟨⟨_, VEnv.HasType.sort (by decide)⟩, trivial⟩).telDefEq_refl + · change VEnv.empty.IsDefEq 1 [.sort (.succ (.param 0))] + (.sort (.succ (.param 0))) (.sort (.succ (.param 0))) + (.sort (.succ (.succ (.param 0)))) + exact .sortDF (by decide) (by decide) rfl + · intro constructor hconstructor + have hconstructors : treeGeneration.flatCtors = + [treeGeneration.flatCtors[0], treeGeneration.flatCtors[1], + treeGeneration.flatCtors[2], treeGeneration.flatCtors[3], + treeGeneration.flatCtors[4]] := rfl + rw [hconstructors] at hconstructor + simp only [List.mem_cons, List.not_mem_nil, or_false] at hconstructor + rcases hconstructor with rfl | rfl | rfl | rfl | rfl + · exact treeLeafGenerationWF + · exact treeNodeGenerationWF + · exact treeBranchGenerationWF + · exact treeListNilGenerationWF + · exact treeListConsGenerationWF + +theorem indexedTreeBlock_le : natFinalEnv ≤ indexedTreeBlockEnv := by + have hfold : indexedTreeDecl.blockTypeConstants.foldlM + (fun env type => env.addConst type.name type.toVConstant) natFinalEnv = + some indexedTreeBlockEnv := by + rw [blockTypeConstants_foldlM_eq_stageInductiveTypes] + exact indexedTreeStage + exact (VInductDecl.ctorFold_spec indexedTreeDecl.blockTypeConstants hfold).1 + +theorem indexedTreeLeafGenerationWF : + NormalizedBlockCtor.WF indexedTreeGeneration + indexedTreeGeneration.flatCtors[0] indexedTreeBlockEnv := by + have hTree : indexedTreeBlockEnv.constants ``IndexedTree = + some indexedTreeType.toVConstant := rfl + have hZero : indexedTreeBlockEnv.constants ``Nat.zero = + some InductiveFixtures.natType.ctors[0].toVConstant := rfl + have hbinders : indexedTreeBlockEnv.OnTel 1 [] + [.sort (.succ (.param 0)), .bvar 0] := by + refine ⟨⟨_, VEnv.HasType.sort (by decide)⟩, ?_⟩ + exact ⟨⟨_, VEnv.HasType.bvar .zero⟩, trivial⟩ + have hresult : indexedTreeBlockEnv.HasType 1 + [.bvar 0, .sort (.succ (.param 0))] + (.app + (.app (.const ``IndexedTree [.param 0]) (.bvar 1)) + (.const ``Nat.zero [])) + (.sort (.succ (.param 0))) := by + apply VEnv.HasType.app + (A := .const ``Nat []) + (B := .sort (.succ (.param 0))) + · apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .forallE (.const ``Nat []) (.sort (.succ (.param 0)))) + · exact VEnv.HasType.const hTree (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar (.succ .zero) + · exact VEnv.HasType.const hZero (by simp) rfl + refine { + declaredTel := hbinders.telDefEq_refl + declaredResult := hresult + emittedTel := hbinders.telDefEq_refl + emittedResult := hresult + owner := ?_ + recursive := ?_ + resultSpine := indexedTreeLeafSemantic.2.mono indexedTreeBlock_le } + · refine ⟨indexedTreeGeneration.families[0], ?_, rfl, rfl, rfl⟩ + exact .head _ + · intro recursive hrecursive + change recursive ∈ [] at hrecursive + nomatch hrecursive + +theorem indexedTreeListNilGenerationWF : + NormalizedBlockCtor.WF indexedTreeGeneration + indexedTreeGeneration.flatCtors[2] indexedTreeBlockEnv := by + have hTreeList : indexedTreeBlockEnv.constants ``IndexedTreeList = + some indexedTreeListType.toVConstant := rfl + have hZero : indexedTreeBlockEnv.constants ``Nat.zero = + some InductiveFixtures.natType.ctors[0].toVConstant := rfl + have hbinders : indexedTreeBlockEnv.OnTel 1 [] + [.sort (.succ (.param 0))] := + ⟨⟨_, VEnv.HasType.sort (by decide)⟩, trivial⟩ + have hresult : indexedTreeBlockEnv.HasType 1 + [.sort (.succ (.param 0))] + (.app + (.app (.const ``IndexedTreeList [.param 0]) (.bvar 0)) + (.const ``Nat.zero [])) + (.sort (.succ (.param 0))) := by + apply VEnv.HasType.app + (A := .const ``Nat []) + (B := .sort (.succ (.param 0))) + · apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .forallE (.const ``Nat []) (.sort (.succ (.param 0)))) + · exact VEnv.HasType.const hTreeList (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar .zero + · exact VEnv.HasType.const hZero (by simp) rfl + refine { + declaredTel := hbinders.telDefEq_refl + declaredResult := hresult + emittedTel := hbinders.telDefEq_refl + emittedResult := hresult + owner := ?_ + recursive := ?_ + resultSpine := indexedTreeListNilSemantic.2.mono + indexedTreeBlock_le } + · refine ⟨indexedTreeGeneration.families[1], ?_, rfl, rfl, rfl⟩ + exact .tail _ (.head _) + · intro recursive hrecursive + change recursive ∈ [] at hrecursive + nomatch hrecursive + +theorem indexedTreeNodeGenerationWF : + NormalizedBlockCtor.WF indexedTreeGeneration + indexedTreeGeneration.flatCtors[1] indexedTreeBlockEnv := by + have hNat : indexedTreeBlockEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + have hSucc : indexedTreeBlockEnv.constants ``Nat.succ = + some InductiveFixtures.natType.ctors[1].toVConstant := rfl + have hTree : indexedTreeBlockEnv.constants ``IndexedTree = + some indexedTreeType.toVConstant := rfl + have hTreeList : indexedTreeBlockEnv.constants ``IndexedTreeList = + some indexedTreeListType.toVConstant := rfl + have hbinders : indexedTreeBlockEnv.OnTel 1 [] + [.sort (.succ (.param 0)), .const ``Nat [], + .app + (.app (.const ``IndexedTreeList [.param 0]) (.bvar 1)) + (.bvar 0)] := by + refine ⟨⟨_, VEnv.HasType.sort (by decide)⟩, ?_⟩ + refine ⟨⟨.succ .zero, VEnv.HasType.const hNat (by simp) rfl⟩, ?_⟩ + refine ⟨⟨.succ (.param 0), ?_⟩, trivial⟩ + apply VEnv.HasType.app + (A := .const ``Nat []) + (B := .sort (.succ (.param 0))) + · apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .forallE (.const ``Nat []) (.sort (.succ (.param 0)))) + · exact VEnv.HasType.const hTreeList (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar (.succ .zero) + · exact VEnv.HasType.bvar .zero + have hresult : indexedTreeBlockEnv.HasType 1 + [.app + (.app (.const ``IndexedTreeList [.param 0]) (.bvar 1)) + (.bvar 0), + .const ``Nat [], .sort (.succ (.param 0))] + (.app + (.app (.const ``IndexedTree [.param 0]) (.bvar 2)) + (.app (.const ``Nat.succ []) (.bvar 1))) + (.sort (.succ (.param 0))) := by + apply VEnv.HasType.app + (A := .const ``Nat []) + (B := .sort (.succ (.param 0))) + · apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .forallE (.const ``Nat []) (.sort (.succ (.param 0)))) + · exact VEnv.HasType.const hTree (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar (.succ (.succ .zero)) + · apply VEnv.HasType.app + (A := .const ``Nat []) (B := .const ``Nat []) + · exact VEnv.HasType.const hSucc (by simp) rfl + · exact VEnv.HasType.bvar (.succ .zero) + refine { + declaredTel := hbinders.telDefEq_refl + declaredResult := hresult + emittedTel := hbinders.telDefEq_refl + emittedResult := hresult + owner := ?_ + recursive := ?_ + resultSpine := indexedTreeNodeSemantic.2.mono + indexedTreeBlock_le } + · refine ⟨indexedTreeGeneration.families[0], ?_, rfl, rfl, rfl⟩ + exact .head _ + · intro recursive hrecursive + change recursive ∈ [{ + fieldIndex := 1 + binders := [] + targetType := 1 + indices := [.bvar 0] }] at hrecursive + simp only [List.mem_cons, List.not_mem_nil, or_false] at hrecursive + subst recursive + refine ⟨indexedTreeGeneration.families[1], ?_, rfl, ?_, ?_⟩ + · exact .tail _ (.head _) + · exact ⟨.app + (.app (.const ``IndexedTreeList [.param 0]) (.bvar 1)) + (.bvar 0), rfl, rfl⟩ + · refine ⟨trivial, ?_⟩ + exact .cons (VEnv.HasType.bvar .zero) .nil + +theorem indexedTreeListConsGenerationWF : + NormalizedBlockCtor.WF indexedTreeGeneration + indexedTreeGeneration.flatCtors[3] indexedTreeBlockEnv := by + have hNat : indexedTreeBlockEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + have hSucc : indexedTreeBlockEnv.constants ``Nat.succ = + some InductiveFixtures.natType.ctors[1].toVConstant := rfl + have hTree : indexedTreeBlockEnv.constants ``IndexedTree = + some indexedTreeType.toVConstant := rfl + have hTreeList : indexedTreeBlockEnv.constants ``IndexedTreeList = + some indexedTreeListType.toVConstant := rfl + have hbinders : indexedTreeBlockEnv.OnTel 1 [] + [.sort (.succ (.param 0)), .const ``Nat [], + .app + (.app (.const ``IndexedTree [.param 0]) (.bvar 1)) + (.bvar 0), + .app + (.app (.const ``IndexedTreeList [.param 0]) (.bvar 2)) + (.bvar 1)] := by + refine ⟨⟨_, VEnv.HasType.sort (by decide)⟩, ?_⟩ + refine ⟨⟨.succ .zero, VEnv.HasType.const hNat (by simp) rfl⟩, ?_⟩ + refine ⟨⟨.succ (.param 0), ?_⟩, ?_⟩ + · apply VEnv.HasType.app + (A := .const ``Nat []) + (B := .sort (.succ (.param 0))) + · apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .forallE (.const ``Nat []) (.sort (.succ (.param 0)))) + · exact VEnv.HasType.const hTree (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar (.succ .zero) + · exact VEnv.HasType.bvar .zero + · refine ⟨⟨.succ (.param 0), ?_⟩, trivial⟩ + apply VEnv.HasType.app + (A := .const ``Nat []) + (B := .sort (.succ (.param 0))) + · apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .forallE (.const ``Nat []) (.sort (.succ (.param 0)))) + · exact VEnv.HasType.const hTreeList (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar (.succ (.succ .zero)) + · exact VEnv.HasType.bvar (.succ .zero) + have hresult : indexedTreeBlockEnv.HasType 1 + [.app + (.app (.const ``IndexedTreeList [.param 0]) (.bvar 2)) + (.bvar 1), + .app + (.app (.const ``IndexedTree [.param 0]) (.bvar 1)) + (.bvar 0), + .const ``Nat [], .sort (.succ (.param 0))] + (.app + (.app (.const ``IndexedTreeList [.param 0]) (.bvar 3)) + (.app (.const ``Nat.succ []) (.bvar 2))) + (.sort (.succ (.param 0))) := by + apply VEnv.HasType.app + (A := .const ``Nat []) + (B := .sort (.succ (.param 0))) + · apply VEnv.HasType.app + (A := .sort (.succ (.param 0))) + (B := .forallE (.const ``Nat []) (.sort (.succ (.param 0)))) + · exact VEnv.HasType.const hTreeList (by simp [VLevel.WF]) rfl + · exact VEnv.HasType.bvar (.succ (.succ (.succ .zero))) + · apply VEnv.HasType.app + (A := .const ``Nat []) (B := .const ``Nat []) + · exact VEnv.HasType.const hSucc (by simp) rfl + · exact VEnv.HasType.bvar (.succ (.succ .zero)) + refine { + declaredTel := hbinders.telDefEq_refl + declaredResult := hresult + emittedTel := hbinders.telDefEq_refl + emittedResult := hresult + owner := ?_ + recursive := ?_ + resultSpine := indexedTreeListConsSemantic.2.mono + indexedTreeBlock_le } + · refine ⟨indexedTreeGeneration.families[1], ?_, rfl, rfl, rfl⟩ + exact .tail _ (.head _) + · intro recursive hrecursive + change recursive ∈ [{ + fieldIndex := 1 + binders := [] + targetType := 0 + indices := [.bvar 0] }, { + fieldIndex := 2 + binders := [] + targetType := 1 + indices := [.bvar 1] }] at hrecursive + simp only [List.mem_cons, List.not_mem_nil, or_false] at hrecursive + rcases hrecursive with rfl | rfl + · refine ⟨indexedTreeGeneration.families[0], ?_, rfl, ?_, ?_⟩ + · exact .head _ + · exact ⟨.app + (.app (.const ``IndexedTree [.param 0]) (.bvar 1)) + (.bvar 0), rfl, rfl⟩ + · refine ⟨trivial, ?_⟩ + exact .cons (VEnv.HasType.bvar .zero) .nil + · refine ⟨indexedTreeGeneration.families[1], ?_, rfl, ?_, ?_⟩ + · exact .tail _ (.head _) + · exact ⟨.app + (.app (.const ``IndexedTreeList [.param 0]) (.bvar 2)) + (.bvar 1), rfl, rfl⟩ + · refine ⟨trivial, ?_⟩ + exact .cons (VEnv.HasType.bvar (.succ .zero)) .nil + +theorem indexedTreeBlockGenerationWF : + indexedTreeGeneration.WF natFinalEnv indexedTreeBlockEnv := by + have hNat : natFinalEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + have hparams : natFinalEnv.OnTel 1 [] + [.sort (.succ (.param 0))] := + ⟨⟨_, VEnv.HasType.sort (by decide)⟩, trivial⟩ + have hfamilyTel : natFinalEnv.OnTel 1 [] + [.sort (.succ (.param 0)), .const ``Nat []] := + ⟨⟨_, VEnv.HasType.sort (by decide)⟩, + ⟨⟨.succ .zero, VEnv.HasType.const hNat (by simp) rfl⟩, trivial⟩⟩ + refine { + blockWF := indexedTreeValidationCertificate.wf + resultLevelWF := by decide + paramsTel := hparams.telDefEq_refl + families := ?_ + constructors := ?_ } + · intro family hfamily + have hfamilies : indexedTreeGeneration.families = + [indexedTreeGeneration.families[0], + indexedTreeGeneration.families[1]] := rfl + rw [hfamilies] at hfamily + simp only [List.mem_cons, List.not_mem_nil, or_false] at hfamily + rcases hfamily with rfl | rfl + · constructor + · exact hfamilyTel.telDefEq_refl + · change natFinalEnv.IsDefEq 1 + [.const ``Nat [], .sort (.succ (.param 0))] + (.sort (.succ (.param 0))) (.sort (.succ (.param 0))) + (.sort (.succ (.succ (.param 0)))) + exact .sortDF (by decide) (by decide) rfl + · constructor + · exact hfamilyTel.telDefEq_refl + · change natFinalEnv.IsDefEq 1 + [.const ``Nat [], .sort (.succ (.param 0))] + (.sort (.succ (.param 0))) (.sort (.succ (.param 0))) + (.sort (.succ (.succ (.param 0)))) + exact .sortDF (by decide) (by decide) rfl + · intro constructor hconstructor + have hconstructors : indexedTreeGeneration.flatCtors = + [indexedTreeGeneration.flatCtors[0], + indexedTreeGeneration.flatCtors[1], + indexedTreeGeneration.flatCtors[2], + indexedTreeGeneration.flatCtors[3]] := rfl + rw [hconstructors] at hconstructor + simp only [List.mem_cons, List.not_mem_nil, or_false] at hconstructor + rcases hconstructor with rfl | rfl | rfl | rfl + · exact indexedTreeLeafGenerationWF + · exact indexedTreeNodeGenerationWF + · exact indexedTreeListNilGenerationWF + · exact indexedTreeListConsGenerationWF + +/-- Proof-carrying block generation for `Tree`/`TreeList`. -/ +def treeGenerationCertificate : + treeDecl.BlockGenerationCertificate VEnv.empty where + generation := treeGeneration + blockEnv := treeBlockEnv + wf := treeBlockGenerationWF + +/-- Proof-carrying block generation for the indexed mutual fixture. -/ +def indexedTreeGenerationCertificate : + indexedTreeDecl.BlockGenerationCertificate natFinalEnv where + generation := indexedTreeGeneration + blockEnv := indexedTreeBlockEnv + wf := indexedTreeBlockGenerationWF + +/-- Final Theory environment produced by the certified unindexed block +transaction. -/ +def treeFinalEnv : VEnv := + (VEnv.empty.addInductBlockCertified treeGenerationCertificate).get + (by decide) + +theorem tree_addInductBlockCertified : + VEnv.empty.addInductBlockCertified treeGenerationCertificate = + some treeFinalEnv := by + rfl + +/-- The raw public transaction selects the same retained block descriptor and +produces the same final Theory environment. -/ +theorem tree_addInduct : + VEnv.empty.addInduct treeDecl = some treeFinalEnv := by + rfl + +theorem tree_addInduct_success : + VEnv.AddInductSuccess VEnv.empty treeFinalEnv treeDecl := + VEnv.addInduct_success tree_addInduct + +/-- Exact four-phase trace for the certified unindexed block transaction. -/ +theorem treeCertifiedTrace : + Nonempty (VEnv.AddInductBlockGenerationTrace + VEnv.empty treeFinalEnv treeGeneration) := + VEnv.addInductBlockCertified_trace tree_addInductBlockCertified + +theorem treeFinalEnv_ordered : treeFinalEnv.Ordered := + VEnv.addInduct_WF .empty rfl treeBlockGenerationWF tree_addInduct + +theorem treeFinalEnv_family_lookup {type : VInductiveType} + (htype : type ∈ treeDecl.types) : + treeFinalEnv.constants type.name = some type.toVConstant := by + obtain ⟨trace⟩ := treeCertifiedTrace + exact trace.family_lookup htype + +theorem treeFinalEnv_ctor_lookup {constructor : VConstVal} + (hconstructor : constructor ∈ treeDecl.blockConstructorConstants) : + treeFinalEnv.constants constructor.name = + some constructor.toVConstant := by + obtain ⟨trace⟩ := treeCertifiedTrace + exact trace.ctor_lookup hconstructor + +theorem treeFinalEnv_rec_lookup {recursor : VConstVal} + (hrecursor : recursor ∈ treeGeneration.recursors) : + treeFinalEnv.constants recursor.name = + some recursor.toVConstant := by + obtain ⟨trace⟩ := treeCertifiedTrace + exact trace.rec_lookup hrecursor + +theorem treeFinalEnv_rule_mem {rule : VDefEq} + (hrule : rule ∈ treeGeneration.generatedRules) : + treeFinalEnv.defeqs rule := by + obtain ⟨trace⟩ := treeCertifiedTrace + exact trace.rule_mem hrule + +/-- Final Theory environment produced by the certified indexed block +transaction. -/ +def indexedTreeFinalEnv : VEnv := + (natFinalEnv.addInductBlockCertified indexedTreeGenerationCertificate).get + (by decide) + +theorem indexedTree_addInductBlockCertified : + natFinalEnv.addInductBlockCertified indexedTreeGenerationCertificate = + some indexedTreeFinalEnv := by + rfl + +/-- The indexed mutual block also runs through the same raw public entry +point once its ordinary `Nat` dependency is present. -/ +theorem indexedTree_addInduct : + natFinalEnv.addInduct indexedTreeDecl = some indexedTreeFinalEnv := by + rfl + +theorem indexedTree_addInduct_success : + VEnv.AddInductSuccess natFinalEnv indexedTreeFinalEnv indexedTreeDecl := + VEnv.addInduct_success indexedTree_addInduct + +/-- Exact four-phase trace for the certified indexed block transaction. -/ +theorem indexedTreeCertifiedTrace : + Nonempty (VEnv.AddInductBlockGenerationTrace + natFinalEnv indexedTreeFinalEnv indexedTreeGeneration) := + VEnv.addInductBlockCertified_trace indexedTree_addInductBlockCertified + +theorem indexedTreeFinalEnv_ordered : indexedTreeFinalEnv.Ordered := + VEnv.addInduct_WF natFinalEnv_ordered rfl indexedTreeBlockGenerationWF + indexedTree_addInduct + +theorem indexedTreeFinalEnv_family_lookup {type : VInductiveType} + (htype : type ∈ indexedTreeDecl.types) : + indexedTreeFinalEnv.constants type.name = some type.toVConstant := by + obtain ⟨trace⟩ := indexedTreeCertifiedTrace + exact trace.family_lookup htype + +theorem indexedTreeFinalEnv_ctor_lookup {constructor : VConstVal} + (hconstructor : + constructor ∈ indexedTreeDecl.blockConstructorConstants) : + indexedTreeFinalEnv.constants constructor.name = + some constructor.toVConstant := by + obtain ⟨trace⟩ := indexedTreeCertifiedTrace + exact trace.ctor_lookup hconstructor + +theorem indexedTreeFinalEnv_rec_lookup {recursor : VConstVal} + (hrecursor : recursor ∈ indexedTreeGeneration.recursors) : + indexedTreeFinalEnv.constants recursor.name = + some recursor.toVConstant := by + obtain ⟨trace⟩ := indexedTreeCertifiedTrace + exact trace.rec_lookup hrecursor + +theorem indexedTreeFinalEnv_rule_mem {rule : VDefEq} + (hrule : rule ∈ indexedTreeGeneration.generatedRules) : + indexedTreeFinalEnv.defeqs rule := by + obtain ⟨trace⟩ := indexedTreeCertifiedTrace + exact trace.rule_mem hrule + +/-! ## Verification-environment block replay -/ + +/-! ## Unindexed Verify block replay -/ + +def treeReplayFirstTypeEnv : VEnv := + (VEnv.empty.addConst treeType.name treeType.toVConstant).get! + +def treeReplayTypeEnv : VEnv := + (treeReplayFirstTypeEnv.addConst treeListType.name + treeListType.toVConstant).get! + +def treeReplayLeafEnv : VEnv := + (treeReplayTypeEnv.addConst treeType.ctors[0].name + treeType.ctors[0].toVConstant).get! + +def treeReplayNodeEnv : VEnv := + (treeReplayLeafEnv.addConst treeType.ctors[1].name + treeType.ctors[1].toVConstant).get! + +def treeReplayBranchEnv : VEnv := + (treeReplayNodeEnv.addConst treeType.ctors[2].name + treeType.ctors[2].toVConstant).get! + +def treeReplayNilEnv : VEnv := + (treeReplayBranchEnv.addConst treeListType.ctors[0].name + treeListType.ctors[0].toVConstant).get! + +def treeReplayCtorEnv : VEnv := + (treeReplayNilEnv.addConst treeListType.ctors[1].name + treeListType.ctors[1].toVConstant).get! + +def treeReplayFirstRecEnv : VEnv := + (treeReplayCtorEnv.addConst treeGeneration.recursors[0].name + treeGeneration.recursors[0].toVConstant).get! + +def treeReplayRecEnv : VEnv := + (treeReplayFirstRecEnv.addConst treeGeneration.recursors[1].name + treeGeneration.recursors[1].toVConstant).get! + +example : treeReplayTypeEnv = treeBlockEnv := rfl + +theorem treeReplay_addFirstType : + VEnv.empty.addConst treeType.name treeType.toVConstant = + some treeReplayFirstTypeEnv := rfl + +theorem treeReplay_addSecondType : + treeReplayFirstTypeEnv.addConst treeListType.name + treeListType.toVConstant = some treeReplayTypeEnv := rfl + +theorem treeReplay_addLeaf : + treeReplayTypeEnv.addConst treeType.ctors[0].name + treeType.ctors[0].toVConstant = some treeReplayLeafEnv := rfl + +theorem treeReplay_addNode : + treeReplayLeafEnv.addConst treeType.ctors[1].name + treeType.ctors[1].toVConstant = some treeReplayNodeEnv := rfl + +theorem treeReplay_addBranch : + treeReplayNodeEnv.addConst treeType.ctors[2].name + treeType.ctors[2].toVConstant = some treeReplayBranchEnv := rfl + +theorem treeReplay_addNil : + treeReplayBranchEnv.addConst treeListType.ctors[0].name + treeListType.ctors[0].toVConstant = some treeReplayNilEnv := rfl + +theorem treeReplay_addCons : + treeReplayNilEnv.addConst treeListType.ctors[1].name + treeListType.ctors[1].toVConstant = some treeReplayCtorEnv := rfl + +theorem treeReplay_addFirstRec : + treeReplayCtorEnv.addConst treeGeneration.recursors[0].name + treeGeneration.recursors[0].toVConstant = + some treeReplayFirstRecEnv := rfl + +theorem treeReplay_addSecondRec : + treeReplayFirstRecEnv.addConst treeGeneration.recursors[1].name + treeGeneration.recursors[1].toVConstant = + some treeReplayRecEnv := rfl + +theorem treeTypeConstantWF : treeType.toVConstant.WF VEnv.empty := by + exact treeBlockGenerationWF.rawFamily_isType (.head _) + +theorem treeListTypeConstantWF : + treeListType.toVConstant.WF VEnv.empty := by + exact treeBlockGenerationWF.rawFamily_isType (.tail _ (.head _)) + +theorem treeLeafConstantWF : + treeType.ctors[0].toVConstant.WF treeReplayTypeEnv := by + exact treeBlockGenerationWF.rawCtor_isType (.head _) + +theorem treeNodeConstantWF : + treeType.ctors[1].toVConstant.WF treeReplayTypeEnv := by + exact treeBlockGenerationWF.rawCtor_isType (.tail _ (.head _)) + +theorem treeBranchConstantWF : + treeType.ctors[2].toVConstant.WF treeReplayTypeEnv := by + exact treeBlockGenerationWF.rawCtor_isType + (.tail _ (.tail _ (.head _))) + +theorem treeNilConstantWF : + treeListType.ctors[0].toVConstant.WF treeReplayTypeEnv := by + exact treeBlockGenerationWF.rawCtor_isType + (.tail _ (.tail _ (.tail _ (.head _)))) + +theorem treeConsConstantWF : + treeListType.ctors[1].toVConstant.WF treeReplayTypeEnv := by + exact treeBlockGenerationWF.rawCtor_isType + (.tail _ (.tail _ (.tail _ (.tail _ (.head _))))) + +theorem treeReplayFirstTypeEnv_ordered : + treeReplayFirstTypeEnv.Ordered := by + exact .const .empty treeTypeConstantWF treeReplay_addFirstType + +theorem treeReplayTypeEnv_ordered : treeReplayTypeEnv.Ordered := by + refine .const treeReplayFirstTypeEnv_ordered ?_ + treeReplay_addSecondType + exact treeListTypeConstantWF.mono + (VEnv.addConst_le treeReplay_addFirstType) + +theorem treeReplayTypeEnv_le_leafEnv : + treeReplayTypeEnv ≤ treeReplayLeafEnv := + VEnv.addConst_le treeReplay_addLeaf + +theorem treeReplayLeafEnv_ordered : treeReplayLeafEnv.Ordered := by + exact .const treeReplayTypeEnv_ordered treeLeafConstantWF + treeReplay_addLeaf + +theorem treeReplayLeafEnv_le_nodeEnv : + treeReplayLeafEnv ≤ treeReplayNodeEnv := + VEnv.addConst_le treeReplay_addNode + +theorem treeReplayNodeEnv_ordered : treeReplayNodeEnv.Ordered := by + refine .const treeReplayLeafEnv_ordered ?_ treeReplay_addNode + exact treeNodeConstantWF.mono + treeReplayTypeEnv_le_leafEnv + +theorem treeReplayNodeEnv_le_branchEnv : + treeReplayNodeEnv ≤ treeReplayBranchEnv := + VEnv.addConst_le treeReplay_addBranch + +theorem treeReplayBranchEnv_ordered : treeReplayBranchEnv.Ordered := by + refine .const treeReplayNodeEnv_ordered ?_ treeReplay_addBranch + exact treeBranchConstantWF.mono + (treeReplayTypeEnv_le_leafEnv.trans treeReplayLeafEnv_le_nodeEnv) + +theorem treeReplayBranchEnv_le_nilEnv : + treeReplayBranchEnv ≤ treeReplayNilEnv := + VEnv.addConst_le treeReplay_addNil + +theorem treeReplayNilEnv_ordered : treeReplayNilEnv.Ordered := by + refine .const treeReplayBranchEnv_ordered ?_ treeReplay_addNil + exact treeNilConstantWF.mono + (treeReplayTypeEnv_le_leafEnv.trans + (treeReplayLeafEnv_le_nodeEnv.trans + treeReplayNodeEnv_le_branchEnv)) + +theorem treeReplayNilEnv_le_ctorEnv : + treeReplayNilEnv ≤ treeReplayCtorEnv := + VEnv.addConst_le treeReplay_addCons + +theorem treeReplayCtorEnv_ordered : treeReplayCtorEnv.Ordered := by + refine .const treeReplayNilEnv_ordered ?_ treeReplay_addCons + exact treeConsConstantWF.mono + (treeReplayTypeEnv_le_leafEnv.trans + (treeReplayLeafEnv_le_nodeEnv.trans + (treeReplayNodeEnv_le_branchEnv.trans + treeReplayBranchEnv_le_nilEnv))) + +theorem treeReplayInput_le_ctorEnv : VEnv.empty ≤ treeReplayCtorEnv := by + exact (VEnv.addConst_le treeReplay_addFirstType).trans + ((VEnv.addConst_le treeReplay_addSecondType).trans + (treeReplayTypeEnv_le_leafEnv.trans + (treeReplayLeafEnv_le_nodeEnv.trans + (treeReplayNodeEnv_le_branchEnv.trans + (treeReplayBranchEnv_le_nilEnv.trans + treeReplayNilEnv_le_ctorEnv))))) + +theorem treeReplayBlock_le_ctorEnv : treeBlockEnv ≤ treeReplayCtorEnv := by + exact treeReplayTypeEnv_le_leafEnv.trans + (treeReplayLeafEnv_le_nodeEnv.trans + (treeReplayNodeEnv_le_branchEnv.trans + (treeReplayBranchEnv_le_nilEnv.trans + treeReplayNilEnv_le_ctorEnv))) + +theorem treeReplayGenerationEnv : + BlockGenerationEnv treeGeneration treeReplayCtorEnv := by + apply treeBlockGenerationWF.toBlockGenerationEnv + treeReplayInput_le_ctorEnv treeReplayBlock_le_ctorEnv + treeReplayCtorEnv_ordered + · intro family hfamily + have hfamilies : treeGeneration.families = + [treeGeneration.families[0], treeGeneration.families[1]] := rfl + rw [hfamilies] at hfamily + simp only [List.mem_cons, List.not_mem_nil, or_false] at hfamily + rcases hfamily with rfl | rfl <;> rfl + · intro constructor hconstructor + have hconstructors : treeGeneration.flatCtors = + [treeGeneration.flatCtors[0], treeGeneration.flatCtors[1], + treeGeneration.flatCtors[2], treeGeneration.flatCtors[3], + treeGeneration.flatCtors[4]] := rfl + rw [hconstructors] at hconstructor + simp only [List.mem_cons, List.not_mem_nil, or_false] at hconstructor + rcases hconstructor with rfl | rfl | rfl | rfl | rfl <;> rfl + +theorem treeReplayFirstRecEnv_ordered : + treeReplayFirstRecEnv.Ordered := by + refine .const treeReplayCtorEnv_ordered ?_ treeReplay_addFirstRec + exact treeReplayGenerationEnv.recursor_wf (.head _) + +theorem treeReplayCtorEnv_le_firstRecEnv : + treeReplayCtorEnv ≤ treeReplayFirstRecEnv := + VEnv.addConst_le treeReplay_addFirstRec + +theorem treeReplayRecEnv_ordered : treeReplayRecEnv.Ordered := by + refine .const treeReplayFirstRecEnv_ordered ?_ treeReplay_addSecondRec + exact (treeReplayGenerationEnv.recursor_wf + (.tail _ (.head _))).mono treeReplayCtorEnv_le_firstRecEnv + +theorem treeKernelInfo_tr : + TrConstVal .safe VEnv.empty treeKernelInfo treeType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr VEnv.empty treeKernelInfo.levelParams [] + treeKernelInfo.type treeType.type := by tr_type_expr_tac + exact hshape.to_trExprS .empty trivial + (treeFamilyTypeWF treeType (.inl rfl)) + +theorem treeListKernelInfo_tr : + TrConstVal .safe treeReplayFirstTypeEnv treeListKernelInfo + treeListType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr treeReplayFirstTypeEnv + treeListKernelInfo.levelParams [] treeListKernelInfo.type + treeListType.type := by tr_type_expr_tac + exact hshape.to_trExprS treeReplayFirstTypeEnv_ordered trivial + ((treeFamilyTypeWF treeListType (.inr rfl)).mono + (VEnv.addConst_le (by rfl))) + +theorem treeLeafKernelInfo_tr : + TrConstVal .safe treeReplayTypeEnv treeLeafKernelInfo + treeType.ctors[0] := by + have hTree : treeReplayTypeEnv.constants ``Tree = + some treeType.toVConstant := rfl + have hTreeList : treeReplayTypeEnv.constants ``TreeList = + some treeListType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr treeReplayTypeEnv + treeLeafKernelInfo.levelParams [] treeLeafKernelInfo.type + treeType.ctors[0].type := by tr_type_expr_tac + exact hshape.to_trExprS treeReplayTypeEnv_ordered trivial + (treeCtorWF treeType.ctors[0] (.inl (by simp [treeType]))) + +theorem treeNodeKernelInfo_tr : + TrConstVal .safe treeReplayLeafEnv treeNodeKernelInfo + treeType.ctors[1] := by + have hTree : treeReplayLeafEnv.constants ``Tree = + some treeType.toVConstant := rfl + have hTreeList : treeReplayLeafEnv.constants ``TreeList = + some treeListType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr treeReplayLeafEnv + treeNodeKernelInfo.levelParams [] treeNodeKernelInfo.type + treeType.ctors[1].type := by tr_type_expr_tac + exact hshape.to_trExprS treeReplayLeafEnv_ordered trivial + ((treeCtorWF treeType.ctors[1] (.inl (by simp [treeType]))).mono + treeReplayTypeEnv_le_leafEnv) + +theorem treeBranchKernelInfo_tr : + TrConstVal .safe treeReplayNodeEnv treeBranchKernelInfo + treeType.ctors[2] := by + have hTree : treeReplayNodeEnv.constants ``Tree = + some treeType.toVConstant := rfl + have hTreeList : treeReplayNodeEnv.constants ``TreeList = + some treeListType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr treeReplayNodeEnv + treeBranchKernelInfo.levelParams [] treeBranchKernelInfo.type + treeType.ctors[2].type := by tr_type_expr_tac + exact hshape.to_trExprS treeReplayNodeEnv_ordered trivial + ((treeCtorWF treeType.ctors[2] (.inl (by simp [treeType]))).mono + (treeReplayTypeEnv_le_leafEnv.trans treeReplayLeafEnv_le_nodeEnv)) + +theorem treeListNilKernelInfo_tr : + TrConstVal .safe treeReplayBranchEnv treeListNilKernelInfo + treeListType.ctors[0] := by + have hTree : treeReplayBranchEnv.constants ``Tree = + some treeType.toVConstant := rfl + have hTreeList : treeReplayBranchEnv.constants ``TreeList = + some treeListType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr treeReplayBranchEnv + treeListNilKernelInfo.levelParams [] treeListNilKernelInfo.type + treeListType.ctors[0].type := by tr_type_expr_tac + exact hshape.to_trExprS treeReplayBranchEnv_ordered trivial + ((treeCtorWF treeListType.ctors[0] + (.inr (by simp [treeListType]))).mono + (treeReplayTypeEnv_le_leafEnv.trans + (treeReplayLeafEnv_le_nodeEnv.trans + treeReplayNodeEnv_le_branchEnv))) + +theorem treeListConsKernelInfo_tr : + TrConstVal .safe treeReplayNilEnv treeListConsKernelInfo + treeListType.ctors[1] := by + have hTree : treeReplayNilEnv.constants ``Tree = + some treeType.toVConstant := rfl + have hTreeList : treeReplayNilEnv.constants ``TreeList = + some treeListType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr treeReplayNilEnv + treeListConsKernelInfo.levelParams [] treeListConsKernelInfo.type + treeListType.ctors[1].type := by tr_type_expr_tac + exact hshape.to_trExprS treeReplayNilEnv_ordered trivial + ((treeCtorWF treeListType.ctors[1] + (.inr (by simp [treeListType]))).mono + (treeReplayTypeEnv_le_leafEnv.trans + (treeReplayLeafEnv_le_nodeEnv.trans + (treeReplayNodeEnv_le_branchEnv.trans + treeReplayBranchEnv_le_nilEnv)))) + +theorem treeRecKernelInfo_tr : + TrConstVal .safe treeReplayCtorEnv treeRecKernelInfo + treeGeneration.recursors[0] := by + have hTree : treeReplayCtorEnv.constants ``Tree = + some treeType.toVConstant := rfl + have hTreeList : treeReplayCtorEnv.constants ``TreeList = + some treeListType.toVConstant := rfl + have hLeaf : treeReplayCtorEnv.constants ``Tree.leaf = + some treeType.ctors[0].toVConstant := rfl + have hNode : treeReplayCtorEnv.constants ``Tree.node = + some treeType.ctors[1].toVConstant := rfl + have hBranch : treeReplayCtorEnv.constants ``Tree.branch = + some treeType.ctors[2].toVConstant := rfl + have hNil : treeReplayCtorEnv.constants ``TreeList.nil = + some treeListType.ctors[0].toVConstant := rfl + have hCons : treeReplayCtorEnv.constants ``TreeList.cons = + some treeListType.ctors[1].toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr treeReplayCtorEnv + treeRecKernelInfo.levelParams [] treeRecKernelInfo.type + treeGeneration.recursors[0].type := by tr_type_expr_tac + obtain ⟨u, hrec⟩ := + treeReplayGenerationEnv.recursor_wf (.head _) + exact hshape.to_trExprS treeReplayCtorEnv_ordered trivial + ⟨.sort u, hrec⟩ + +theorem treeListRecKernelInfo_tr : + TrConstVal .safe treeReplayFirstRecEnv treeListRecKernelInfo + treeGeneration.recursors[1] := by + have hTree : treeReplayFirstRecEnv.constants ``Tree = + some treeType.toVConstant := rfl + have hTreeList : treeReplayFirstRecEnv.constants ``TreeList = + some treeListType.toVConstant := rfl + have hLeaf : treeReplayFirstRecEnv.constants ``Tree.leaf = + some treeType.ctors[0].toVConstant := rfl + have hNode : treeReplayFirstRecEnv.constants ``Tree.node = + some treeType.ctors[1].toVConstant := rfl + have hBranch : treeReplayFirstRecEnv.constants ``Tree.branch = + some treeType.ctors[2].toVConstant := rfl + have hNil : treeReplayFirstRecEnv.constants ``TreeList.nil = + some treeListType.ctors[0].toVConstant := rfl + have hCons : treeReplayFirstRecEnv.constants ``TreeList.cons = + some treeListType.ctors[1].toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr treeReplayFirstRecEnv + treeListRecKernelInfo.levelParams [] treeListRecKernelInfo.type + treeGeneration.recursors[1].type := by tr_type_expr_tac + obtain ⟨u, hrec⟩ := + treeReplayGenerationEnv.recursor_wf (.tail _ (.head _)) + exact hshape.to_trExprS treeReplayFirstRecEnv_ordered trivial + ⟨.sort u, hrec.mono treeReplayCtorEnv_le_firstRecEnv⟩ + +def treeReplayFirstTypeMap : ConstMap := + ({} : ConstMap).insert ``Tree treeKernelInfo + +def treeReplayTypeMap : ConstMap := + treeReplayFirstTypeMap.insert ``TreeList treeListKernelInfo + +def treeReplayLeafMap : ConstMap := + treeReplayTypeMap.insert ``Tree.leaf treeLeafKernelInfo + +def treeReplayNodeMap : ConstMap := + treeReplayLeafMap.insert ``Tree.node treeNodeKernelInfo + +def treeReplayBranchMap : ConstMap := + treeReplayNodeMap.insert ``Tree.branch treeBranchKernelInfo + +def treeReplayNilMap : ConstMap := + treeReplayBranchMap.insert ``TreeList.nil treeListNilKernelInfo + +def treeReplayCtorMap : ConstMap := + treeReplayNilMap.insert ``TreeList.cons treeListConsKernelInfo + +def treeReplayFirstRecMap : ConstMap := + treeReplayCtorMap.insert ``Tree.rec treeRecKernelInfo + +def treeReplayMap : ConstMap := + treeReplayFirstRecMap.insert ``TreeList.rec treeListRecKernelInfo + +theorem treeReplayFirstType_fresh : + ({} : ConstMap).find? ``Tree = none := by + simp [SMap.find?] + +theorem treeReplayFirstTypeMap_wf : treeReplayFirstTypeMap.WF := + SMap.WF.empty.insert _ _ treeReplayFirstType_fresh + +theorem treeReplaySecondType_fresh : + treeReplayFirstTypeMap.find? ``TreeList = none := by + rw [treeReplayFirstTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem treeReplayTypeMap_wf : treeReplayTypeMap.WF := + treeReplayFirstTypeMap_wf.insert _ _ treeReplaySecondType_fresh + +theorem treeReplayLeaf_fresh : + treeReplayTypeMap.find? ``Tree.leaf = none := by + rw [treeReplayTypeMap, treeReplayFirstTypeMap_wf.find?_insert, + treeReplayFirstTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem treeReplayLeafMap_wf : treeReplayLeafMap.WF := + treeReplayTypeMap_wf.insert _ _ treeReplayLeaf_fresh + +theorem treeReplayNode_fresh : + treeReplayLeafMap.find? ``Tree.node = none := by + rw [treeReplayLeafMap, treeReplayTypeMap_wf.find?_insert, + treeReplayTypeMap, treeReplayFirstTypeMap_wf.find?_insert, + treeReplayFirstTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem treeReplayNodeMap_wf : treeReplayNodeMap.WF := + treeReplayLeafMap_wf.insert _ _ treeReplayNode_fresh + +theorem treeReplayBranch_fresh : + treeReplayNodeMap.find? ``Tree.branch = none := by + rw [treeReplayNodeMap, treeReplayLeafMap_wf.find?_insert, + treeReplayLeafMap, treeReplayTypeMap_wf.find?_insert, + treeReplayTypeMap, treeReplayFirstTypeMap_wf.find?_insert, + treeReplayFirstTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem treeReplayBranchMap_wf : treeReplayBranchMap.WF := + treeReplayNodeMap_wf.insert _ _ treeReplayBranch_fresh + +theorem treeReplayNil_fresh : + treeReplayBranchMap.find? ``TreeList.nil = none := by + rw [treeReplayBranchMap, treeReplayNodeMap_wf.find?_insert, + treeReplayNodeMap, treeReplayLeafMap_wf.find?_insert, + treeReplayLeafMap, treeReplayTypeMap_wf.find?_insert, + treeReplayTypeMap, treeReplayFirstTypeMap_wf.find?_insert, + treeReplayFirstTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem treeReplayNilMap_wf : treeReplayNilMap.WF := + treeReplayBranchMap_wf.insert _ _ treeReplayNil_fresh + +theorem treeReplayCons_fresh : + treeReplayNilMap.find? ``TreeList.cons = none := by + rw [treeReplayNilMap, treeReplayBranchMap_wf.find?_insert, + treeReplayBranchMap, treeReplayNodeMap_wf.find?_insert, + treeReplayNodeMap, treeReplayLeafMap_wf.find?_insert, + treeReplayLeafMap, treeReplayTypeMap_wf.find?_insert, + treeReplayTypeMap, treeReplayFirstTypeMap_wf.find?_insert, + treeReplayFirstTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem treeReplayCtorMap_wf : treeReplayCtorMap.WF := + treeReplayNilMap_wf.insert _ _ treeReplayCons_fresh + +theorem treeReplayFirstRec_fresh : + treeReplayCtorMap.find? ``Tree.rec = none := by + rw [treeReplayCtorMap, treeReplayNilMap_wf.find?_insert, + treeReplayNilMap, treeReplayBranchMap_wf.find?_insert, + treeReplayBranchMap, treeReplayNodeMap_wf.find?_insert, + treeReplayNodeMap, treeReplayLeafMap_wf.find?_insert, + treeReplayLeafMap, treeReplayTypeMap_wf.find?_insert, + treeReplayTypeMap, treeReplayFirstTypeMap_wf.find?_insert, + treeReplayFirstTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem treeReplayFirstRecMap_wf : treeReplayFirstRecMap.WF := + treeReplayCtorMap_wf.insert _ _ treeReplayFirstRec_fresh + +theorem treeReplaySecondRec_fresh : + treeReplayFirstRecMap.find? ``TreeList.rec = none := by + rw [treeReplayFirstRecMap, treeReplayCtorMap_wf.find?_insert, + treeReplayCtorMap, treeReplayNilMap_wf.find?_insert, + treeReplayNilMap, treeReplayBranchMap_wf.find?_insert, + treeReplayBranchMap, treeReplayNodeMap_wf.find?_insert, + treeReplayNodeMap, treeReplayLeafMap_wf.find?_insert, + treeReplayLeafMap, treeReplayTypeMap_wf.find?_insert, + treeReplayTypeMap, treeReplayFirstTypeMap_wf.find?_insert, + treeReplayFirstTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem treeReplayMap_wf : treeReplayMap.WF := + treeReplayFirstRecMap_wf.insert _ _ treeReplaySecondRec_fresh + +theorem treeReplay_treeRec_lookup : + treeReplayMap.find? ``Tree.rec = some treeRecKernelInfo := by + rw [treeReplayMap, treeReplayFirstRecMap_wf.find?_insert, + treeReplayFirstRecMap, treeReplayCtorMap_wf.find?_insert] + rfl + +theorem treeReplay_treeListRec_lookup : + treeReplayMap.find? ``TreeList.rec = some treeListRecKernelInfo := by + rw [treeReplayMap, treeReplayFirstRecMap_wf.find?_insert] + rfl + +def treeAddInductBlockTrace : + AddInductBlockTrace ({} : ConstMap) VEnv.empty treeDecl + treeReplayMap treeFinalEnv where + generation := treeGeneration + blockEnv := treeBlockEnv + generation_wf := treeBlockGenerationWF + typeMap := treeReplayTypeMap + typeEnv := treeReplayTypeEnv + ctorMap := treeReplayCtorMap + ctorEnv := treeReplayCtorEnv + recEnv := treeReplayRecEnv + addTypes := .cons { + info := treeKernelInfo + kind_eq := by simp [treeKernelInfo, InductConstantKind.Matches] + tr := treeKernelInfo_tr + map_fresh := treeReplayFirstType_fresh + env_add := rfl + map_add := rfl } + (.cons { + info := treeListKernelInfo + kind_eq := by simp [treeListKernelInfo, InductConstantKind.Matches] + tr := treeListKernelInfo_tr + map_fresh := treeReplaySecondType_fresh + env_add := rfl + map_add := rfl } .nil) + addCtors := .cons { + info := treeLeafKernelInfo + kind_eq := by simp [treeLeafKernelInfo, InductConstantKind.Matches] + tr := treeLeafKernelInfo_tr + map_fresh := treeReplayLeaf_fresh + env_add := rfl + map_add := rfl } + (.cons { + info := treeNodeKernelInfo + kind_eq := by simp [treeNodeKernelInfo, InductConstantKind.Matches] + tr := treeNodeKernelInfo_tr + map_fresh := treeReplayNode_fresh + env_add := rfl + map_add := rfl } + (.cons { + info := treeBranchKernelInfo + kind_eq := by simp [treeBranchKernelInfo, InductConstantKind.Matches] + tr := treeBranchKernelInfo_tr + map_fresh := treeReplayBranch_fresh + env_add := rfl + map_add := rfl } + (.cons { + info := treeListNilKernelInfo + kind_eq := by simp [treeListNilKernelInfo, InductConstantKind.Matches] + tr := treeListNilKernelInfo_tr + map_fresh := treeReplayNil_fresh + env_add := rfl + map_add := rfl } + (.cons { + info := treeListConsKernelInfo + kind_eq := by simp [treeListConsKernelInfo, InductConstantKind.Matches] + tr := treeListConsKernelInfo_tr + map_fresh := treeReplayCons_fresh + env_add := rfl + map_add := rfl } .nil)))) + addRecs := .cons { + info := treeRecKernelInfo + kind_eq := by simp [treeRecKernelInfo, InductConstantKind.Matches] + tr := treeRecKernelInfo_tr + map_fresh := treeReplayFirstRec_fresh + env_add := rfl + map_add := rfl } + (.cons { + info := treeListRecKernelInfo + kind_eq := by simp [treeListRecKernelInfo, InductConstantKind.Matches] + tr := treeListRecKernelInfo_tr + map_fresh := treeReplaySecondRec_fresh + env_add := rfl + map_add := rfl } .nil) + recK := by + intro recursor hrecursor + have hrecs : treeGeneration.recursors = + [treeGeneration.recursors[0], treeGeneration.recursors[1]] := rfl + rw [hrecs] at hrecursor + simp only [List.mem_cons, List.not_mem_nil, or_false] at hrecursor + rcases hrecursor with rfl | rfl + · exact ⟨treeRecKernelInfo, treeReplay_treeRec_lookup, by decide⟩ + · exact ⟨treeListRecKernelInfo, treeReplay_treeListRec_lookup, + by decide⟩ + addRules := ⟨rfl⟩ + +theorem treeAddInductBlock : + AddInductBlock ({} : ConstMap) VEnv.empty treeDecl + treeReplayMap treeFinalEnv := + ⟨treeAddInductBlockTrace⟩ + +theorem tree_trEnv' : TrEnv' .safe treeReplayMap false treeFinalEnv := + .inductBlock treeAddInductBlock .empty + +theorem tree_verify_env_wf : treeFinalEnv.WF := tree_trEnv'.wf + +theorem tree_verify_aligned : + Aligned .safe treeReplayMap treeFinalEnv := tree_trEnv'.aligned + +/-! ## Indexed Verify block replay -/ + +def indexedReplayFirstTypeEnv : VEnv := + (natFinalEnv.addConst indexedTreeType.name + indexedTreeType.toVConstant).get! + +def indexedReplayTypeEnv : VEnv := + (indexedReplayFirstTypeEnv.addConst indexedTreeListType.name + indexedTreeListType.toVConstant).get! + +def indexedReplayLeafEnv : VEnv := + (indexedReplayTypeEnv.addConst indexedTreeType.ctors[0].name + indexedTreeType.ctors[0].toVConstant).get! + +def indexedReplayNodeEnv : VEnv := + (indexedReplayLeafEnv.addConst indexedTreeType.ctors[1].name + indexedTreeType.ctors[1].toVConstant).get! + +def indexedReplayNilEnv : VEnv := + (indexedReplayNodeEnv.addConst indexedTreeListType.ctors[0].name + indexedTreeListType.ctors[0].toVConstant).get! + +def indexedReplayCtorEnv : VEnv := + (indexedReplayNilEnv.addConst indexedTreeListType.ctors[1].name + indexedTreeListType.ctors[1].toVConstant).get! + +def indexedReplayFirstRecEnv : VEnv := + (indexedReplayCtorEnv.addConst + indexedTreeGeneration.recursors[0].name + indexedTreeGeneration.recursors[0].toVConstant).get! + +def indexedReplayRecEnv : VEnv := + (indexedReplayFirstRecEnv.addConst + indexedTreeGeneration.recursors[1].name + indexedTreeGeneration.recursors[1].toVConstant).get! + +example : indexedReplayTypeEnv = indexedTreeBlockEnv := rfl + +theorem indexedReplay_addFirstType : + natFinalEnv.addConst indexedTreeType.name + indexedTreeType.toVConstant = some indexedReplayFirstTypeEnv := rfl + +theorem indexedReplay_addSecondType : + indexedReplayFirstTypeEnv.addConst indexedTreeListType.name + indexedTreeListType.toVConstant = some indexedReplayTypeEnv := rfl + +theorem indexedReplay_addLeaf : + indexedReplayTypeEnv.addConst indexedTreeType.ctors[0].name + indexedTreeType.ctors[0].toVConstant = + some indexedReplayLeafEnv := rfl + +theorem indexedReplay_addNode : + indexedReplayLeafEnv.addConst indexedTreeType.ctors[1].name + indexedTreeType.ctors[1].toVConstant = + some indexedReplayNodeEnv := rfl + +theorem indexedReplay_addNil : + indexedReplayNodeEnv.addConst indexedTreeListType.ctors[0].name + indexedTreeListType.ctors[0].toVConstant = + some indexedReplayNilEnv := rfl + +theorem indexedReplay_addCons : + indexedReplayNilEnv.addConst indexedTreeListType.ctors[1].name + indexedTreeListType.ctors[1].toVConstant = + some indexedReplayCtorEnv := rfl + +theorem indexedReplay_addFirstRec : + indexedReplayCtorEnv.addConst + indexedTreeGeneration.recursors[0].name + indexedTreeGeneration.recursors[0].toVConstant = + some indexedReplayFirstRecEnv := rfl + +theorem indexedReplay_addSecondRec : + indexedReplayFirstRecEnv.addConst + indexedTreeGeneration.recursors[1].name + indexedTreeGeneration.recursors[1].toVConstant = + some indexedReplayRecEnv := rfl + +theorem indexedTreeTypeConstantWF : + indexedTreeType.toVConstant.WF natFinalEnv := by + exact indexedTreeBlockGenerationWF.rawFamily_isType (.head _) + +theorem indexedTreeListTypeConstantWF : + indexedTreeListType.toVConstant.WF natFinalEnv := by + exact indexedTreeBlockGenerationWF.rawFamily_isType + (.tail _ (.head _)) + +theorem indexedTreeLeafConstantWF : + indexedTreeType.ctors[0].toVConstant.WF indexedReplayTypeEnv := by + exact indexedTreeBlockGenerationWF.rawCtor_isType (.head _) + +theorem indexedTreeNodeConstantWF : + indexedTreeType.ctors[1].toVConstant.WF indexedReplayTypeEnv := by + exact indexedTreeBlockGenerationWF.rawCtor_isType + (.tail _ (.head _)) + +theorem indexedTreeNilConstantWF : + indexedTreeListType.ctors[0].toVConstant.WF + indexedReplayTypeEnv := by + exact indexedTreeBlockGenerationWF.rawCtor_isType + (.tail _ (.tail _ (.head _))) + +theorem indexedTreeConsConstantWF : + indexedTreeListType.ctors[1].toVConstant.WF + indexedReplayTypeEnv := by + exact indexedTreeBlockGenerationWF.rawCtor_isType + (.tail _ (.tail _ (.tail _ (.head _)))) + +theorem indexedReplayFirstTypeEnv_ordered : + indexedReplayFirstTypeEnv.Ordered := by + exact .const natFinalEnv_ordered indexedTreeTypeConstantWF + indexedReplay_addFirstType + +theorem indexedReplayTypeEnv_ordered : indexedReplayTypeEnv.Ordered := by + refine .const indexedReplayFirstTypeEnv_ordered ?_ + indexedReplay_addSecondType + exact indexedTreeListTypeConstantWF.mono + (VEnv.addConst_le indexedReplay_addFirstType) + +theorem indexedReplayTypeEnv_le_leafEnv : + indexedReplayTypeEnv ≤ indexedReplayLeafEnv := + VEnv.addConst_le indexedReplay_addLeaf + +theorem indexedReplayLeafEnv_ordered : indexedReplayLeafEnv.Ordered := by + exact .const indexedReplayTypeEnv_ordered indexedTreeLeafConstantWF + indexedReplay_addLeaf + +theorem indexedReplayLeafEnv_le_nodeEnv : + indexedReplayLeafEnv ≤ indexedReplayNodeEnv := + VEnv.addConst_le indexedReplay_addNode + +theorem indexedReplayNodeEnv_ordered : indexedReplayNodeEnv.Ordered := by + refine .const indexedReplayLeafEnv_ordered ?_ indexedReplay_addNode + exact indexedTreeNodeConstantWF.mono indexedReplayTypeEnv_le_leafEnv + +theorem indexedReplayNodeEnv_le_nilEnv : + indexedReplayNodeEnv ≤ indexedReplayNilEnv := + VEnv.addConst_le indexedReplay_addNil + +theorem indexedReplayNilEnv_ordered : indexedReplayNilEnv.Ordered := by + refine .const indexedReplayNodeEnv_ordered ?_ indexedReplay_addNil + exact indexedTreeNilConstantWF.mono + (indexedReplayTypeEnv_le_leafEnv.trans + indexedReplayLeafEnv_le_nodeEnv) + +theorem indexedReplayNilEnv_le_ctorEnv : + indexedReplayNilEnv ≤ indexedReplayCtorEnv := + VEnv.addConst_le indexedReplay_addCons + +theorem indexedReplayCtorEnv_ordered : indexedReplayCtorEnv.Ordered := by + refine .const indexedReplayNilEnv_ordered ?_ indexedReplay_addCons + exact indexedTreeConsConstantWF.mono + (indexedReplayTypeEnv_le_leafEnv.trans + (indexedReplayLeafEnv_le_nodeEnv.trans + indexedReplayNodeEnv_le_nilEnv)) + +theorem indexedReplayInput_le_ctorEnv : + natFinalEnv ≤ indexedReplayCtorEnv := by + exact (VEnv.addConst_le indexedReplay_addFirstType).trans + ((VEnv.addConst_le indexedReplay_addSecondType).trans + (indexedReplayTypeEnv_le_leafEnv.trans + (indexedReplayLeafEnv_le_nodeEnv.trans + (indexedReplayNodeEnv_le_nilEnv.trans + indexedReplayNilEnv_le_ctorEnv)))) + +theorem indexedReplayBlock_le_ctorEnv : + indexedTreeBlockEnv ≤ indexedReplayCtorEnv := by + exact indexedReplayTypeEnv_le_leafEnv.trans + (indexedReplayLeafEnv_le_nodeEnv.trans + (indexedReplayNodeEnv_le_nilEnv.trans + indexedReplayNilEnv_le_ctorEnv)) + +theorem indexedReplayGenerationEnv : + BlockGenerationEnv indexedTreeGeneration indexedReplayCtorEnv := by + apply indexedTreeBlockGenerationWF.toBlockGenerationEnv + indexedReplayInput_le_ctorEnv indexedReplayBlock_le_ctorEnv + indexedReplayCtorEnv_ordered + · intro family hfamily + have hfamilies : indexedTreeGeneration.families = + [indexedTreeGeneration.families[0], + indexedTreeGeneration.families[1]] := rfl + rw [hfamilies] at hfamily + simp only [List.mem_cons, List.not_mem_nil, or_false] at hfamily + rcases hfamily with rfl | rfl <;> rfl + · intro constructor hconstructor + have hconstructors : indexedTreeGeneration.flatCtors = + [indexedTreeGeneration.flatCtors[0], + indexedTreeGeneration.flatCtors[1], + indexedTreeGeneration.flatCtors[2], + indexedTreeGeneration.flatCtors[3]] := rfl + rw [hconstructors] at hconstructor + simp only [List.mem_cons, List.not_mem_nil, or_false] at hconstructor + rcases hconstructor with rfl | rfl | rfl | rfl <;> rfl + +theorem indexedReplayFirstRecEnv_ordered : + indexedReplayFirstRecEnv.Ordered := by + refine .const indexedReplayCtorEnv_ordered ?_ + indexedReplay_addFirstRec + exact indexedReplayGenerationEnv.recursor_wf (.head _) + +theorem indexedReplayCtorEnv_le_firstRecEnv : + indexedReplayCtorEnv ≤ indexedReplayFirstRecEnv := + VEnv.addConst_le indexedReplay_addFirstRec + +theorem indexedReplayRecEnv_ordered : indexedReplayRecEnv.Ordered := by + refine .const indexedReplayFirstRecEnv_ordered ?_ + indexedReplay_addSecondRec + exact (indexedReplayGenerationEnv.recursor_wf + (.tail _ (.head _))).mono indexedReplayCtorEnv_le_firstRecEnv + +theorem indexedTreeKernelInfo_tr : + TrConstVal .safe natFinalEnv indexedTreeKernelInfo + indexedTreeType.toVConstVal := by + have hNat : natFinalEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr natFinalEnv indexedTreeKernelInfo.levelParams [] + indexedTreeKernelInfo.type indexedTreeType.type := by + tr_type_expr_tac + exact hshape.to_trExprS natFinalEnv_ordered trivial + (indexedTreeFamilyTypeWF indexedTreeType (.inl rfl)) + +theorem indexedTreeListKernelInfo_tr : + TrConstVal .safe indexedReplayFirstTypeEnv indexedTreeListKernelInfo + indexedTreeListType.toVConstVal := by + have hNat : indexedReplayFirstTypeEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr indexedReplayFirstTypeEnv + indexedTreeListKernelInfo.levelParams [] indexedTreeListKernelInfo.type + indexedTreeListType.type := by tr_type_expr_tac + exact hshape.to_trExprS indexedReplayFirstTypeEnv_ordered trivial + ((indexedTreeFamilyTypeWF indexedTreeListType (.inr rfl)).mono + (VEnv.addConst_le indexedReplay_addFirstType)) + +theorem indexedTreeLeafKernelInfo_tr : + TrConstVal .safe indexedReplayTypeEnv indexedTreeLeafKernelInfo + indexedTreeType.ctors[0] := by + have hNat : indexedReplayTypeEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + have hZero : indexedReplayTypeEnv.constants ``Nat.zero = + some InductiveFixtures.natType.ctors[0].toVConstant := rfl + have hSucc : indexedReplayTypeEnv.constants ``Nat.succ = + some InductiveFixtures.natType.ctors[1].toVConstant := rfl + have hTree : indexedReplayTypeEnv.constants ``IndexedTree = + some indexedTreeType.toVConstant := rfl + have hTreeList : indexedReplayTypeEnv.constants ``IndexedTreeList = + some indexedTreeListType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr indexedReplayTypeEnv + indexedTreeLeafKernelInfo.levelParams [] indexedTreeLeafKernelInfo.type + indexedTreeType.ctors[0].type := by tr_type_expr_tac + exact hshape.to_trExprS indexedReplayTypeEnv_ordered trivial + (indexedTreeCtorWF indexedTreeType.ctors[0] + (.inl (by simp [indexedTreeType]))) + +theorem indexedTreeNodeKernelInfo_tr : + TrConstVal .safe indexedReplayLeafEnv indexedTreeNodeKernelInfo + indexedTreeType.ctors[1] := by + have hNat : indexedReplayLeafEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + have hZero : indexedReplayLeafEnv.constants ``Nat.zero = + some InductiveFixtures.natType.ctors[0].toVConstant := rfl + have hSucc : indexedReplayLeafEnv.constants ``Nat.succ = + some InductiveFixtures.natType.ctors[1].toVConstant := rfl + have hTree : indexedReplayLeafEnv.constants ``IndexedTree = + some indexedTreeType.toVConstant := rfl + have hTreeList : indexedReplayLeafEnv.constants ``IndexedTreeList = + some indexedTreeListType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr indexedReplayLeafEnv + indexedTreeNodeKernelInfo.levelParams [] indexedTreeNodeKernelInfo.type + indexedTreeType.ctors[1].type := by tr_type_expr_tac + exact hshape.to_trExprS indexedReplayLeafEnv_ordered trivial + ((indexedTreeCtorWF indexedTreeType.ctors[1] + (.inl (by simp [indexedTreeType]))).mono + indexedReplayTypeEnv_le_leafEnv) + +theorem indexedTreeListNilKernelInfo_tr : + TrConstVal .safe indexedReplayNodeEnv indexedTreeListNilKernelInfo + indexedTreeListType.ctors[0] := by + have hNat : indexedReplayNodeEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + have hZero : indexedReplayNodeEnv.constants ``Nat.zero = + some InductiveFixtures.natType.ctors[0].toVConstant := rfl + have hSucc : indexedReplayNodeEnv.constants ``Nat.succ = + some InductiveFixtures.natType.ctors[1].toVConstant := rfl + have hTree : indexedReplayNodeEnv.constants ``IndexedTree = + some indexedTreeType.toVConstant := rfl + have hTreeList : indexedReplayNodeEnv.constants ``IndexedTreeList = + some indexedTreeListType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr indexedReplayNodeEnv + indexedTreeListNilKernelInfo.levelParams [] + indexedTreeListNilKernelInfo.type indexedTreeListType.ctors[0].type := by + tr_type_expr_tac + exact hshape.to_trExprS indexedReplayNodeEnv_ordered trivial + ((indexedTreeCtorWF indexedTreeListType.ctors[0] + (.inr (by simp [indexedTreeListType]))).mono + (indexedReplayTypeEnv_le_leafEnv.trans + indexedReplayLeafEnv_le_nodeEnv)) + +theorem indexedTreeListConsKernelInfo_tr : + TrConstVal .safe indexedReplayNilEnv indexedTreeListConsKernelInfo + indexedTreeListType.ctors[1] := by + have hNat : indexedReplayNilEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + have hZero : indexedReplayNilEnv.constants ``Nat.zero = + some InductiveFixtures.natType.ctors[0].toVConstant := rfl + have hSucc : indexedReplayNilEnv.constants ``Nat.succ = + some InductiveFixtures.natType.ctors[1].toVConstant := rfl + have hTree : indexedReplayNilEnv.constants ``IndexedTree = + some indexedTreeType.toVConstant := rfl + have hTreeList : indexedReplayNilEnv.constants ``IndexedTreeList = + some indexedTreeListType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr indexedReplayNilEnv + indexedTreeListConsKernelInfo.levelParams [] + indexedTreeListConsKernelInfo.type indexedTreeListType.ctors[1].type := by + tr_type_expr_tac + exact hshape.to_trExprS indexedReplayNilEnv_ordered trivial + ((indexedTreeCtorWF indexedTreeListType.ctors[1] + (.inr (by simp [indexedTreeListType]))).mono + (indexedReplayTypeEnv_le_leafEnv.trans + (indexedReplayLeafEnv_le_nodeEnv.trans + indexedReplayNodeEnv_le_nilEnv))) + +theorem indexedTreeRecKernelInfo_tr : + TrConstVal .safe indexedReplayCtorEnv indexedTreeRecKernelInfo + indexedTreeGeneration.recursors[0] := by + have hNat : indexedReplayCtorEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + have hZero : indexedReplayCtorEnv.constants ``Nat.zero = + some InductiveFixtures.natType.ctors[0].toVConstant := rfl + have hSucc : indexedReplayCtorEnv.constants ``Nat.succ = + some InductiveFixtures.natType.ctors[1].toVConstant := rfl + have hTree : indexedReplayCtorEnv.constants ``IndexedTree = + some indexedTreeType.toVConstant := rfl + have hTreeList : indexedReplayCtorEnv.constants ``IndexedTreeList = + some indexedTreeListType.toVConstant := rfl + have hLeaf : indexedReplayCtorEnv.constants ``IndexedTree.leaf = + some indexedTreeType.ctors[0].toVConstant := rfl + have hNode : indexedReplayCtorEnv.constants ``IndexedTree.node = + some indexedTreeType.ctors[1].toVConstant := rfl + have hNil : indexedReplayCtorEnv.constants ``IndexedTreeList.nil = + some indexedTreeListType.ctors[0].toVConstant := rfl + have hCons : indexedReplayCtorEnv.constants ``IndexedTreeList.cons = + some indexedTreeListType.ctors[1].toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr indexedReplayCtorEnv + indexedTreeRecKernelInfo.levelParams [] indexedTreeRecKernelInfo.type + indexedTreeGeneration.recursors[0].type := by tr_type_expr_tac + obtain ⟨u, hrec⟩ := indexedReplayGenerationEnv.recursor_wf (.head _) + exact hshape.to_trExprS indexedReplayCtorEnv_ordered trivial + ⟨.sort u, hrec⟩ + +theorem indexedTreeListRecKernelInfo_tr : + TrConstVal .safe indexedReplayFirstRecEnv indexedTreeListRecKernelInfo + indexedTreeGeneration.recursors[1] := by + have hNat : indexedReplayFirstRecEnv.constants ``Nat = + some InductiveFixtures.natType.toVConstant := rfl + have hZero : indexedReplayFirstRecEnv.constants ``Nat.zero = + some InductiveFixtures.natType.ctors[0].toVConstant := rfl + have hSucc : indexedReplayFirstRecEnv.constants ``Nat.succ = + some InductiveFixtures.natType.ctors[1].toVConstant := rfl + have hTree : indexedReplayFirstRecEnv.constants ``IndexedTree = + some indexedTreeType.toVConstant := rfl + have hTreeList : indexedReplayFirstRecEnv.constants ``IndexedTreeList = + some indexedTreeListType.toVConstant := rfl + have hLeaf : indexedReplayFirstRecEnv.constants ``IndexedTree.leaf = + some indexedTreeType.ctors[0].toVConstant := rfl + have hNode : indexedReplayFirstRecEnv.constants ``IndexedTree.node = + some indexedTreeType.ctors[1].toVConstant := rfl + have hNil : indexedReplayFirstRecEnv.constants ``IndexedTreeList.nil = + some indexedTreeListType.ctors[0].toVConstant := rfl + have hCons : indexedReplayFirstRecEnv.constants ``IndexedTreeList.cons = + some indexedTreeListType.ctors[1].toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr indexedReplayFirstRecEnv + indexedTreeListRecKernelInfo.levelParams [] + indexedTreeListRecKernelInfo.type + indexedTreeGeneration.recursors[1].type := by tr_type_expr_tac + obtain ⟨u, hrec⟩ := indexedReplayGenerationEnv.recursor_wf + (.tail _ (.head _)) + exact hshape.to_trExprS indexedReplayFirstRecEnv_ordered trivial + ⟨.sort u, hrec.mono indexedReplayCtorEnv_le_firstRecEnv⟩ + +def indexedReplayFirstTypeMap : ConstMap := + natMap.insert ``IndexedTree indexedTreeKernelInfo + +def indexedReplayTypeMap : ConstMap := + indexedReplayFirstTypeMap.insert ``IndexedTreeList + indexedTreeListKernelInfo + +def indexedReplayLeafMap : ConstMap := + indexedReplayTypeMap.insert ``IndexedTree.leaf indexedTreeLeafKernelInfo + +def indexedReplayNodeMap : ConstMap := + indexedReplayLeafMap.insert ``IndexedTree.node indexedTreeNodeKernelInfo + +def indexedReplayNilMap : ConstMap := + indexedReplayNodeMap.insert ``IndexedTreeList.nil + indexedTreeListNilKernelInfo + +def indexedReplayCtorMap : ConstMap := + indexedReplayNilMap.insert ``IndexedTreeList.cons + indexedTreeListConsKernelInfo + +def indexedReplayFirstRecMap : ConstMap := + indexedReplayCtorMap.insert ``IndexedTree.rec indexedTreeRecKernelInfo + +def indexedReplayMap : ConstMap := + indexedReplayFirstRecMap.insert ``IndexedTreeList.rec + indexedTreeListRecKernelInfo + +theorem indexedReplayFirstType_fresh : + natMap.find? ``IndexedTree = none := by + rw [natMap, natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem indexedReplayFirstTypeMap_wf : indexedReplayFirstTypeMap.WF := + nat_aligned.map_wf.insert _ _ indexedReplayFirstType_fresh + +theorem indexedReplaySecondType_fresh : + indexedReplayFirstTypeMap.find? ``IndexedTreeList = none := by + rw [indexedReplayFirstTypeMap, nat_aligned.map_wf.find?_insert, + natMap, natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem indexedReplayTypeMap_wf : indexedReplayTypeMap.WF := + indexedReplayFirstTypeMap_wf.insert _ _ indexedReplaySecondType_fresh + +theorem indexedReplayLeaf_fresh : + indexedReplayTypeMap.find? ``IndexedTree.leaf = none := by + rw [indexedReplayTypeMap, indexedReplayFirstTypeMap_wf.find?_insert, + indexedReplayFirstTypeMap, nat_aligned.map_wf.find?_insert, + natMap, natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem indexedReplayLeafMap_wf : indexedReplayLeafMap.WF := + indexedReplayTypeMap_wf.insert _ _ indexedReplayLeaf_fresh + +theorem indexedReplayNode_fresh : + indexedReplayLeafMap.find? ``IndexedTree.node = none := by + rw [indexedReplayLeafMap, indexedReplayTypeMap_wf.find?_insert, + indexedReplayTypeMap, indexedReplayFirstTypeMap_wf.find?_insert, + indexedReplayFirstTypeMap, nat_aligned.map_wf.find?_insert, + natMap, natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem indexedReplayNodeMap_wf : indexedReplayNodeMap.WF := + indexedReplayLeafMap_wf.insert _ _ indexedReplayNode_fresh + +theorem indexedReplayNil_fresh : + indexedReplayNodeMap.find? ``IndexedTreeList.nil = none := by + rw [indexedReplayNodeMap, indexedReplayLeafMap_wf.find?_insert, + indexedReplayLeafMap, indexedReplayTypeMap_wf.find?_insert, + indexedReplayTypeMap, indexedReplayFirstTypeMap_wf.find?_insert, + indexedReplayFirstTypeMap, nat_aligned.map_wf.find?_insert, + natMap, natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem indexedReplayNilMap_wf : indexedReplayNilMap.WF := + indexedReplayNodeMap_wf.insert _ _ indexedReplayNil_fresh + +theorem indexedReplayCons_fresh : + indexedReplayNilMap.find? ``IndexedTreeList.cons = none := by + rw [indexedReplayNilMap, indexedReplayNodeMap_wf.find?_insert, + indexedReplayNodeMap, indexedReplayLeafMap_wf.find?_insert, + indexedReplayLeafMap, indexedReplayTypeMap_wf.find?_insert, + indexedReplayTypeMap, indexedReplayFirstTypeMap_wf.find?_insert, + indexedReplayFirstTypeMap, nat_aligned.map_wf.find?_insert, + natMap, natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem indexedReplayCtorMap_wf : indexedReplayCtorMap.WF := + indexedReplayNilMap_wf.insert _ _ indexedReplayCons_fresh + +theorem indexedReplayFirstRec_fresh : + indexedReplayCtorMap.find? ``IndexedTree.rec = none := by + rw [indexedReplayCtorMap, indexedReplayNilMap_wf.find?_insert, + indexedReplayNilMap, indexedReplayNodeMap_wf.find?_insert, + indexedReplayNodeMap, indexedReplayLeafMap_wf.find?_insert, + indexedReplayLeafMap, indexedReplayTypeMap_wf.find?_insert, + indexedReplayTypeMap, indexedReplayFirstTypeMap_wf.find?_insert, + indexedReplayFirstTypeMap, nat_aligned.map_wf.find?_insert, + natMap, natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem indexedReplayFirstRecMap_wf : indexedReplayFirstRecMap.WF := + indexedReplayCtorMap_wf.insert _ _ indexedReplayFirstRec_fresh + +theorem indexedReplaySecondRec_fresh : + indexedReplayFirstRecMap.find? ``IndexedTreeList.rec = none := by + rw [indexedReplayFirstRecMap, indexedReplayCtorMap_wf.find?_insert, + indexedReplayCtorMap, indexedReplayNilMap_wf.find?_insert, + indexedReplayNilMap, indexedReplayNodeMap_wf.find?_insert, + indexedReplayNodeMap, indexedReplayLeafMap_wf.find?_insert, + indexedReplayLeafMap, indexedReplayTypeMap_wf.find?_insert, + indexedReplayTypeMap, indexedReplayFirstTypeMap_wf.find?_insert, + indexedReplayFirstTypeMap, nat_aligned.map_wf.find?_insert, + natMap, natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem indexedReplayMap_wf : indexedReplayMap.WF := + indexedReplayFirstRecMap_wf.insert _ _ indexedReplaySecondRec_fresh + +theorem indexedReplay_treeRec_lookup : + indexedReplayMap.find? ``IndexedTree.rec = + some indexedTreeRecKernelInfo := by + rw [indexedReplayMap, indexedReplayFirstRecMap_wf.find?_insert, + indexedReplayFirstRecMap, indexedReplayCtorMap_wf.find?_insert] + rfl + +theorem indexedReplay_treeListRec_lookup : + indexedReplayMap.find? ``IndexedTreeList.rec = + some indexedTreeListRecKernelInfo := by + rw [indexedReplayMap, indexedReplayFirstRecMap_wf.find?_insert] + rfl + +def indexedTreeAddInductBlockTrace : + AddInductBlockTrace natMap natFinalEnv indexedTreeDecl + indexedReplayMap indexedTreeFinalEnv where + generation := indexedTreeGeneration + blockEnv := indexedTreeBlockEnv + generation_wf := indexedTreeBlockGenerationWF + typeMap := indexedReplayTypeMap + typeEnv := indexedReplayTypeEnv + ctorMap := indexedReplayCtorMap + ctorEnv := indexedReplayCtorEnv + recEnv := indexedReplayRecEnv + addTypes := .cons { + info := indexedTreeKernelInfo + kind_eq := by simp [indexedTreeKernelInfo, + InductConstantKind.Matches] + tr := indexedTreeKernelInfo_tr + map_fresh := indexedReplayFirstType_fresh + env_add := rfl + map_add := rfl } + (.cons { + info := indexedTreeListKernelInfo + kind_eq := by simp [indexedTreeListKernelInfo, + InductConstantKind.Matches] + tr := indexedTreeListKernelInfo_tr + map_fresh := indexedReplaySecondType_fresh + env_add := rfl + map_add := rfl } .nil) + addCtors := .cons { + info := indexedTreeLeafKernelInfo + kind_eq := by simp [indexedTreeLeafKernelInfo, + InductConstantKind.Matches] + tr := indexedTreeLeafKernelInfo_tr + map_fresh := indexedReplayLeaf_fresh + env_add := rfl + map_add := rfl } + (.cons { + info := indexedTreeNodeKernelInfo + kind_eq := by simp [indexedTreeNodeKernelInfo, + InductConstantKind.Matches] + tr := indexedTreeNodeKernelInfo_tr + map_fresh := indexedReplayNode_fresh + env_add := rfl + map_add := rfl } + (.cons { + info := indexedTreeListNilKernelInfo + kind_eq := by simp [indexedTreeListNilKernelInfo, + InductConstantKind.Matches] + tr := indexedTreeListNilKernelInfo_tr + map_fresh := indexedReplayNil_fresh + env_add := rfl + map_add := rfl } + (.cons { + info := indexedTreeListConsKernelInfo + kind_eq := by simp [indexedTreeListConsKernelInfo, + InductConstantKind.Matches] + tr := indexedTreeListConsKernelInfo_tr + map_fresh := indexedReplayCons_fresh + env_add := rfl + map_add := rfl } .nil))) + addRecs := .cons { + info := indexedTreeRecKernelInfo + kind_eq := by simp [indexedTreeRecKernelInfo, + InductConstantKind.Matches] + tr := indexedTreeRecKernelInfo_tr + map_fresh := indexedReplayFirstRec_fresh + env_add := rfl + map_add := rfl } + (.cons { + info := indexedTreeListRecKernelInfo + kind_eq := by simp [indexedTreeListRecKernelInfo, + InductConstantKind.Matches] + tr := indexedTreeListRecKernelInfo_tr + map_fresh := indexedReplaySecondRec_fresh + env_add := rfl + map_add := rfl } .nil) + recK := by + intro recursor hrecursor + have hrecs : indexedTreeGeneration.recursors = + [indexedTreeGeneration.recursors[0], + indexedTreeGeneration.recursors[1]] := rfl + rw [hrecs] at hrecursor + simp only [List.mem_cons, List.not_mem_nil, or_false] at hrecursor + rcases hrecursor with rfl | rfl + · exact ⟨indexedTreeRecKernelInfo, + indexedReplay_treeRec_lookup, by decide⟩ + · exact ⟨indexedTreeListRecKernelInfo, + indexedReplay_treeListRec_lookup, by decide⟩ + addRules := ⟨rfl⟩ + +theorem indexedTreeAddInductBlock : + AddInductBlock natMap natFinalEnv indexedTreeDecl + indexedReplayMap indexedTreeFinalEnv := + ⟨indexedTreeAddInductBlockTrace⟩ + +theorem indexedTree_trEnv' : + TrEnv' .safe indexedReplayMap false indexedTreeFinalEnv := + .inductBlock indexedTreeAddInductBlock nat_trEnv' + +theorem indexedTree_verify_env_wf : indexedTreeFinalEnv.WF := + indexedTree_trEnv'.wf + +theorem indexedTree_verify_aligned : + Aligned .safe indexedReplayMap indexedTreeFinalEnv := + indexedTree_trEnv'.aligned + + + +/-! ## Trust-boundary manifests -/ + +/- The semantic generation and raw public transaction remain inside the +accepted Theory trust baseline. -/ +#guard_named_axioms Ix.Theory.Named.MutualInductiveReplayFixtures.treeBlockGenerationWF [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.MutualInductiveReplayFixtures.indexedTreeBlockGenerationWF [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.MutualInductiveReplayFixtures.treeFinalEnv_ordered [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.MutualInductiveReplayFixtures.indexedTreeFinalEnv_ordered [propext, Classical.choice, Quot.sound] + +/- The implementation metadata replay is now `sorryAx`-free and inherits only +the already classified persistent-map contracts; fixture-local native-decision +axioms are deliberately absent. -/ +#guard_named_axioms Ix.Theory.Named.MutualInductiveReplayFixtures.treeAddInductBlock [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.MutualInductiveReplayFixtures.indexedTreeAddInductBlock [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.MutualInductiveReplayFixtures.tree_verify_aligned [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.MutualInductiveReplayFixtures.indexedTree_verify_aligned [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.MutualInductiveReplayFixtures.treeCheckedBlockWF [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.MutualInductiveReplayFixtures.indexedTreeCheckedBlockWF [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.MutualInductiveReplayFixtures.treeNormalizationBlockWF [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.MutualInductiveReplayFixtures.indexedTreeNormalizationBlockWF [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.MutualInductiveReplayFixtures.treeValidationCertificate [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.MutualInductiveReplayFixtures.indexedTreeValidationCertificate [propext, Classical.choice, Quot.sound] + +end Ix.Theory.Named.MutualInductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/NestedReplay.lean b/Ix/Theory/Named/Verify/Environment/NestedReplay.lean new file mode 100644 index 000000000..e06fb7d68 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/NestedReplay.lean @@ -0,0 +1,3402 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Verify.Environment.SingletonParityReplay +import Ix.Theory.Named.Verify.Environment.NestedTransformation + +/-! +# Nested environment replay (Spec-09C) + +Both ladder fixtures replayed from real stored metadata: the rose tree +over the completed `List` environment and the nested-indexed family over +a staged `PVec` boundary. Each inserts its stored constants through +`AddInductNestedTrace`, proves the `NestedBlockChecked.WF` package by +direct concrete typing derivations over the exact phase environments, +and drives the final map and environment through `TrEnv'.inductNested`, +with `Ordered` derived and the transitional closures guarded. +-/ + +namespace Ix.Theory.Named.NestedReplayFixtures + +open Lean +open Ix.Theory.Named.InductiveReplayFixtures +open Ix.Theory.Named.NestedRepresentation +open Ix.Theory.Named.NestedInductiveFixtures +open VInductDecl + +local instance : Inhabited VEnv := ⟨.empty⟩ +local instance : Inhabited VConstVal := ⟨⟨⟨0, .sort .zero⟩, .anonymous⟩⟩ + +/-! ## The completed List replay as the input boundary -/ + +theorem listTrEnv07 : TrEnv' .safe listMap07 false listFinalEnv07 := + .induct listAddInduct07 .empty + +theorem listFinalOrdered07 : listFinalEnv07.Ordered := + listTrEnv07.wf.ordered + +/-! ## The translated rose source and its nested artifact -/ + +def roseSourceV : VInductDecl where + uvars := 1 + nparams := 1 + types := + [{ name := ``RoseTree + uvars := 1 + type := nestedConstVType09A% RoseTree + ctors := + [⟨⟨1, nestedConstVType09A% RoseTree.node⟩, ``RoseTree.node⟩] }] + +def roseNestedC? : Option (NestedBlockChecked roseSourceV) := + nestedBlockChecked? [listTarget] roseSourceV + +#guard roseNestedC?.isSome + +def roseNestedC : NestedBlockChecked roseSourceV := + roseNestedC?.get (by native_decide) + +/-! ## Stored metadata and phase maps/environments -/ + +def roseInfo09 : ConstantInfo := kernelInductInfo% RoseTree +def roseNodeInfo09 : ConstantInfo := kernelCtorInfo% RoseTree.node +def roseRecInfo09 : ConstantInfo := kernelRecInfo% RoseTree.rec +def roseRec1Info09 : ConstantInfo := kernelRecInfo% RoseTree.rec_1 + +def roseFamilyV : VConstVal := roseSourceV.types[0].toVConstVal +def roseNodeV : VConstVal := roseSourceV.types[0].ctors[0] +def roseRecV : VConstVal := roseNestedC.recursors[0]! +def roseRec1V : VConstVal := roseNestedC.recursors[1]! + +#guard roseRecV.name == ``RoseTree.rec +#guard roseRec1V.name == `Ix.Theory.Named.NestedRepresentation.RoseTree.rec_1 + +def roseTypeMap09 : ConstMap := listMap07.insert ``RoseTree roseInfo09 +def roseCtorMap09 : ConstMap := roseTypeMap09.insert ``RoseTree.node roseNodeInfo09 +def roseRecMap09 : ConstMap := roseCtorMap09.insert ``RoseTree.rec roseRecInfo09 +def roseMap09 : ConstMap := + roseRecMap09.insert `Ix.Theory.Named.NestedRepresentation.RoseTree.rec_1 roseRec1Info09 + +def roseTypeEnv09 : VEnv := + (listFinalEnv07.addConst roseFamilyV.name roseFamilyV.toVConstant).get! +def roseCtorEnv09 : VEnv := + (roseTypeEnv09.addConst roseNodeV.name roseNodeV.toVConstant).get! +-- the recursor and rule phase environments are defined below, over the +-- printed literal inventories + + +/-! ## Printed artifact literals + +The restored recursor types and rule components, printed from the +computed artifact and tied back to it below; the concrete typing +derivations are stated over these literals. -/ + +/-- Printed image of `roseNestedC.recursors[0]!.type`. -/ +def roseRecTypeL : VExpr := + .forallE + (.sort (.succ (.param 1))) + (.forallE + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 0)) + (.sort (.param 0))) + (.forallE + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 1))) + (.sort (.param 0))) + (.forallE + (.forallE + (.bvar 2) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3))) + (.forallE + (.app (.bvar 2) (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.node + [.param 1]) + (.bvar 5)) + (.bvar 2)) + (.bvar 1)))))) + (.forallE + (.app + (.bvar 1) + (.app + (.const `List.nil [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3)))) + (.forallE + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 4)) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5))) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.app + (.bvar 6) + (.app + (.app + (.app + (.const `List.cons [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 8))) + (.bvar 3)) + (.bvar 2))))))) + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5)) + (.app (.bvar 5) (.bvar 0)))))))) + +def roseRec1TypeL : VExpr := + .forallE + (.sort (.succ (.param 1))) + (.forallE + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 0)) + (.sort (.param 0))) + (.forallE + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 1))) + (.sort (.param 0))) + (.forallE + (.forallE + (.bvar 2) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3))) + (.forallE + (.app (.bvar 2) (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.node + [.param 1]) + (.bvar 5)) + (.bvar 2)) + (.bvar 1)))))) + (.forallE + (.app + (.bvar 1) + (.app + (.const `List.nil [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3)))) + (.forallE + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 4)) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5))) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.app + (.bvar 6) + (.app + (.app + (.app + (.const `List.cons [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 8))) + (.bvar 3)) + (.bvar 2))))))) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5))) + (.app (.bvar 4) (.bvar 0)))))))) + +def roseRule0LhsL : VExpr := + .lam + (.sort (.succ (.param 1))) + (.lam + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 0)) + (.sort (.param 0))) + (.lam + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 1))) + (.sort (.param 0))) + (.lam + (.forallE + (.bvar 2) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3))) + (.forallE + (.app (.bvar 2) (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.node + [.param 1]) + (.bvar 5)) + (.bvar 2)) + (.bvar 1)))))) + (.lam + (.app + (.bvar 1) + (.app + (.const `List.nil [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3)))) + (.lam + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 4)) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5))) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.app + (.bvar 6) + (.app + (.app + (.app + (.const `List.cons [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 8))) + (.bvar 3)) + (.bvar 2))))))) + (.lam + (.bvar 5) + (.lam + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 6))) + (.app + (.app + (.app + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.rec + [.param 0, .param 1]) + (.bvar 7)) + (.bvar 6)) + (.bvar 5)) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)) + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.node + [.param 1]) + (.bvar 7)) + (.bvar 1)) + (.bvar 0)))))))))) + +def roseRule0RhsL : VExpr := + .lam + (.sort (.succ (.param 1))) + (.lam + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 0)) + (.sort (.param 0))) + (.lam + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 1))) + (.sort (.param 0))) + (.lam + (.forallE + (.bvar 2) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3))) + (.forallE + (.app (.bvar 2) (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.node + [.param 1]) + (.bvar 5)) + (.bvar 2)) + (.bvar 1)))))) + (.lam + (.app + (.bvar 1) + (.app + (.const `List.nil [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3)))) + (.lam + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 4)) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5))) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.app + (.bvar 6) + (.app + (.app + (.app + (.const `List.cons [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 8))) + (.bvar 3)) + (.bvar 2))))))) + (.lam + (.bvar 5) + (.lam + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 6))) + (.app + (.app + (.app (.bvar 4) (.bvar 1)) + (.bvar 0)) + (.app + (.app + (.app + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.rec_1 + [.param 0, .param 1]) + (.bvar 7)) + (.bvar 6)) + (.bvar 5)) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)) + (.bvar 0)))))))))) + +def roseRule0TypeL : VExpr := + .forallE + (.sort (.succ (.param 1))) + (.forallE + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 0)) + (.sort (.param 0))) + (.forallE + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 1))) + (.sort (.param 0))) + (.forallE + (.forallE + (.bvar 2) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3))) + (.forallE + (.app (.bvar 2) (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.node + [.param 1]) + (.bvar 5)) + (.bvar 2)) + (.bvar 1)))))) + (.forallE + (.app + (.bvar 1) + (.app + (.const `List.nil [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3)))) + (.forallE + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 4)) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5))) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.app + (.bvar 6) + (.app + (.app + (.app + (.const `List.cons [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 8))) + (.bvar 3)) + (.bvar 2))))))) + (.forallE + (.bvar 5) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 6))) + (.app + (.bvar 6) + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.node + [.param 1]) + (.bvar 7)) + (.bvar 1)) + (.bvar 0)))))))))) + +def roseRule1LhsL : VExpr := + .lam + (.sort (.succ (.param 1))) + (.lam + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 0)) + (.sort (.param 0))) + (.lam + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 1))) + (.sort (.param 0))) + (.lam + (.forallE + (.bvar 2) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3))) + (.forallE + (.app (.bvar 2) (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.node + [.param 1]) + (.bvar 5)) + (.bvar 2)) + (.bvar 1)))))) + (.lam + (.app + (.bvar 1) + (.app + (.const `List.nil [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3)))) + (.lam + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 4)) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5))) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.app + (.bvar 6) + (.app + (.app + (.app + (.const `List.cons [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 8))) + (.bvar 3)) + (.bvar 2))))))) + (.app + (.app + (.app + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.rec_1 + [.param 0, .param 1]) + (.bvar 5)) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)) + (.bvar 1)) + (.bvar 0)) + (.app + (.const `List.nil [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5))))))))) + +def roseRule1RhsL : VExpr := + .lam + (.sort (.succ (.param 1))) + (.lam + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 0)) + (.sort (.param 0))) + (.lam + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 1))) + (.sort (.param 0))) + (.lam + (.forallE + (.bvar 2) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3))) + (.forallE + (.app (.bvar 2) (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.node + [.param 1]) + (.bvar 5)) + (.bvar 2)) + (.bvar 1)))))) + (.lam + (.app + (.bvar 1) + (.app + (.const `List.nil [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3)))) + (.lam + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 4)) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5))) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.app + (.bvar 6) + (.app + (.app + (.app + (.const `List.cons [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 8))) + (.bvar 3)) + (.bvar 2))))))) + (.bvar 1)))))) + +def roseRule1TypeL : VExpr := + .forallE + (.sort (.succ (.param 1))) + (.forallE + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 0)) + (.sort (.param 0))) + (.forallE + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 1))) + (.sort (.param 0))) + (.forallE + (.forallE + (.bvar 2) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3))) + (.forallE + (.app (.bvar 2) (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.node + [.param 1]) + (.bvar 5)) + (.bvar 2)) + (.bvar 1)))))) + (.forallE + (.app + (.bvar 1) + (.app + (.const `List.nil [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3)))) + (.forallE + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 4)) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5))) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.app + (.bvar 6) + (.app + (.app + (.app + (.const `List.cons [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 8))) + (.bvar 3)) + (.bvar 2))))))) + (.app + (.bvar 3) + (.app + (.const `List.nil [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5))))))))) + +def roseRule2LhsL : VExpr := + .lam + (.sort (.succ (.param 1))) + (.lam + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 0)) + (.sort (.param 0))) + (.lam + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 1))) + (.sort (.param 0))) + (.lam + (.forallE + (.bvar 2) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3))) + (.forallE + (.app (.bvar 2) (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.node + [.param 1]) + (.bvar 5)) + (.bvar 2)) + (.bvar 1)))))) + (.lam + (.app + (.bvar 1) + (.app + (.const `List.nil [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3)))) + (.lam + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 4)) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5))) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.app + (.bvar 6) + (.app + (.app + (.app + (.const `List.cons [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 8))) + (.bvar 3)) + (.bvar 2))))))) + (.lam + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5)) + (.lam + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 6))) + (.app + (.app + (.app + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.rec_1 + [.param 0, .param 1]) + (.bvar 7)) + (.bvar 6)) + (.bvar 5)) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)) + (.app + (.app + (.app + (.const `List.cons [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 7))) + (.bvar 1)) + (.bvar 0)))))))))) + +def roseRule2RhsL : VExpr := + .lam + (.sort (.succ (.param 1))) + (.lam + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 0)) + (.sort (.param 0))) + (.lam + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 1))) + (.sort (.param 0))) + (.lam + (.forallE + (.bvar 2) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3))) + (.forallE + (.app (.bvar 2) (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.node + [.param 1]) + (.bvar 5)) + (.bvar 2)) + (.bvar 1)))))) + (.lam + (.app + (.bvar 1) + (.app + (.const `List.nil [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3)))) + (.lam + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 4)) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5))) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.app + (.bvar 6) + (.app + (.app + (.app + (.const `List.cons [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 8))) + (.bvar 3)) + (.bvar 2))))))) + (.lam + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5)) + (.lam + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 6))) + (.app + (.app + (.app + (.app (.bvar 2) (.bvar 1)) + (.bvar 0)) + (.app + (.app + (.app + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.rec + [.param 0, .param 1]) + (.bvar 7)) + (.bvar 6)) + (.bvar 5)) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)) + (.bvar 1))) + (.app + (.app + (.app + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.rec_1 + [.param 0, .param 1]) + (.bvar 7)) + (.bvar 6)) + (.bvar 5)) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)) + (.bvar 0)))))))))) + +def roseRule2TypeL : VExpr := + .forallE + (.sort (.succ (.param 1))) + (.forallE + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 0)) + (.sort (.param 0))) + (.forallE + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 1))) + (.sort (.param 0))) + (.forallE + (.forallE + (.bvar 2) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3))) + (.forallE + (.app (.bvar 2) (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree.node + [.param 1]) + (.bvar 5)) + (.bvar 2)) + (.bvar 1)))))) + (.forallE + (.app + (.bvar 1) + (.app + (.const `List.nil [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 3)))) + (.forallE + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 4)) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5))) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.forallE + (.app (.bvar 5) (.bvar 1)) + (.app + (.bvar 6) + (.app + (.app + (.app + (.const `List.cons [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 8))) + (.bvar 3)) + (.bvar 2))))))) + (.forallE + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 5)) + (.forallE + (.app + (.const `List [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 6))) + (.app + (.bvar 5) + (.app + (.app + (.app + (.const `List.cons [.param 1]) + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.RoseTree + [.param 1]) + (.bvar 7))) + (.bvar 1)) + (.bvar 0)))))))))) + +#guard roseRecV.type == roseRecTypeL +#guard roseRec1V.type == roseRec1TypeL +#guard roseNestedC.generatedRules.map (fun df => (df.uvars, df.lhs, df.rhs, df.type)) == + [(2, roseRule0LhsL, roseRule0RhsL, roseRule0TypeL), + (2, roseRule1LhsL, roseRule1RhsL, roseRule1TypeL), + (2, roseRule2LhsL, roseRule2RhsL, roseRule2TypeL)] +#guard roseRecV.uvars == 2 && roseRec1V.uvars == 2 + + +/-! ## Literal inventories -/ + +def roseRecVL : VConstVal := ⟨⟨2, roseRecTypeL⟩, ``RoseTree.rec⟩ +def roseRec1VL : VConstVal := + ⟨⟨2, roseRec1TypeL⟩, `Ix.Theory.Named.NestedRepresentation.RoseTree.rec_1⟩ + +def roseRulesL : List VDefEq := + [⟨2, roseRule0LhsL, roseRule0RhsL, roseRule0TypeL⟩, + ⟨2, roseRule1LhsL, roseRule1RhsL, roseRule1TypeL⟩, + ⟨2, roseRule2LhsL, roseRule2RhsL, roseRule2TypeL⟩] + +theorem roseRecursors_eq : roseNestedC.recursors = [roseRecVL, roseRec1VL] := by + native_decide + +theorem roseRules_eq : roseNestedC.generatedRules = roseRulesL := by + native_decide + +def roseRecEnv09 : VEnv := + (roseCtorEnv09.addConst roseRecVL.name roseRecVL.toVConstant).get! +def roseRec1Env09 : VEnv := + (roseRecEnv09.addConst roseRec1VL.name roseRec1VL.toVConstant).get! +def roseFinalEnv09 : VEnv := + roseRulesL.foldl VEnv.addDefEq roseRec1Env09 + +/-! ## Concrete constant well-formedness -/ + +theorem roseFamilyWF09 : roseFamilyV.toVConstant.WF listFinalEnv07 := + ⟨_, by type_tac⟩ + +theorem roseTypeEnv09_eq : + listFinalEnv07.addConst roseFamilyV.name roseFamilyV.toVConstant = + some roseTypeEnv09 := rfl + +theorem roseTypeOrdered09 : roseTypeEnv09.Ordered := + .const listFinalOrdered07 roseFamilyWF09 roseTypeEnv09_eq + +theorem roseNodeWF09 : roseNodeV.toVConstant.WF roseTypeEnv09 := by + have hList : roseTypeEnv09.constants ``List = + some ⟨1, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))⟩ := rfl + have hRose : roseTypeEnv09.constants ``RoseTree = + some ⟨1, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))⟩ := rfl + exact ⟨_, by type_tac⟩ + + +theorem roseCtorEnv09_eq : + roseTypeEnv09.addConst roseNodeV.name roseNodeV.toVConstant = + some roseCtorEnv09 := rfl + +theorem roseCtorOrdered09 : roseCtorEnv09.Ordered := + .const roseTypeOrdered09 roseNodeWF09 roseCtorEnv09_eq + +set_option maxRecDepth 4000 in +theorem roseRecWF09 : (⟨2, roseRecTypeL⟩ : VConstant).WF roseCtorEnv09 := by + have hList : roseCtorEnv09.constants ``List = + some ⟨1, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))⟩ := rfl + have hRose : roseCtorEnv09.constants ``RoseTree = + some ⟨1, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))⟩ := rfl + have hNode : roseCtorEnv09.constants ``RoseTree.node = + some roseNodeV.toVConstant := rfl + have hNil : roseCtorEnv09.constants ``List.nil = + some ⟨1, .forallE (.sort (.succ (.param 0))) + (.app (.const `List [.param 0]) (.bvar 0))⟩ := rfl + have hCons : roseCtorEnv09.constants ``List.cons = + some ⟨1, .forallE (.sort (.succ (.param 0))) + (.forallE (.bvar 0) + (.forallE (.app (.const `List [.param 0]) (.bvar 1)) + (.app (.const `List [.param 0]) (.bvar 2))))⟩ := rfl + exact ⟨_, by type_tac⟩ + + +theorem roseRecEnv09_eq : + roseCtorEnv09.addConst roseRecVL.name roseRecVL.toVConstant = + some roseRecEnv09 := rfl + +theorem roseRecOrdered09 : roseRecEnv09.Ordered := + .const roseCtorOrdered09 roseRecWF09 roseRecEnv09_eq + +set_option maxRecDepth 4000 in +theorem roseRec1WF09 : (⟨2, roseRec1TypeL⟩ : VConstant).WF roseRecEnv09 := by + have hList : roseRecEnv09.constants ``List = + some ⟨1, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))⟩ := rfl + have hRose : roseRecEnv09.constants ``RoseTree = + some ⟨1, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))⟩ := rfl + have hNode : roseRecEnv09.constants ``RoseTree.node = + some roseNodeV.toVConstant := rfl + have hNil : roseRecEnv09.constants ``List.nil = + some ⟨1, .forallE (.sort (.succ (.param 0))) + (.app (.const `List [.param 0]) (.bvar 0))⟩ := rfl + have hCons : roseRecEnv09.constants ``List.cons = + some ⟨1, .forallE (.sort (.succ (.param 0))) + (.forallE (.bvar 0) + (.forallE (.app (.const `List [.param 0]) (.bvar 1)) + (.app (.const `List [.param 0]) (.bvar 2))))⟩ := rfl + exact ⟨_, by type_tac⟩ + +theorem roseRec1Env09_eq : + roseRecEnv09.addConst roseRec1VL.name roseRec1VL.toVConstant = + some roseRec1Env09 := rfl + +theorem roseRec1Ordered09 : roseRec1Env09.Ordered := + .const roseRecOrdered09 roseRec1WF09 roseRec1Env09_eq + + +/-! ## Rule well-formedness at the rule-phase environment -/ + +section RuleWF + +set_option maxRecDepth 8000 + +/-- The lookup hypotheses shared by every rule component derivation; the +environment argument is any `addDefEq` extension of `roseRec1Env09`, whose +constants agree definitionally. -/ +macro "rose_rule_hyps" e:term : tactic => `(tactic| ( + have hList : VEnv.constants $e ``List = + some ⟨1, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))⟩ := rfl + have hRose : VEnv.constants $e ``RoseTree = + some ⟨1, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))⟩ := rfl + have hNode : VEnv.constants $e ``RoseTree.node = + some roseNodeV.toVConstant := rfl + have hNil : VEnv.constants $e ``List.nil = + some ⟨1, .forallE (.sort (.succ (.param 0))) + (.app (.const `List [.param 0]) (.bvar 0))⟩ := rfl + have hCons : VEnv.constants $e ``List.cons = + some ⟨1, .forallE (.sort (.succ (.param 0))) + (.forallE (.bvar 0) + (.forallE (.app (.const `List [.param 0]) (.bvar 1)) + (.app (.const `List [.param 0]) (.bvar 2))))⟩ := rfl + have hRec : VEnv.constants $e ``RoseTree.rec = + some ⟨2, roseRecTypeL⟩ := rfl + have hRec1 : VEnv.constants $e + `Ix.Theory.Named.NestedRepresentation.RoseTree.rec_1 = + some ⟨2, roseRec1TypeL⟩ := rfl)) + +def roseRuleEnv1 : VEnv := roseRec1Env09.addDefEq roseRulesL[0] +def roseRuleEnv2 : VEnv := roseRuleEnv1.addDefEq roseRulesL[1] + +theorem roseRule0WF09 : roseRulesL[0].WF roseRec1Env09 := by + constructor + · rose_rule_hyps roseRec1Env09; type_tac + · rose_rule_hyps roseRec1Env09; type_tac + +theorem roseRule1WF09 : roseRulesL[1].WF roseRuleEnv1 := by + constructor + · rose_rule_hyps roseRuleEnv1; type_tac + · rose_rule_hyps roseRuleEnv1; type_tac + +theorem roseRule2WF09 : roseRulesL[2].WF roseRuleEnv2 := by + constructor + · rose_rule_hyps roseRuleEnv2; type_tac + · rose_rule_hyps roseRuleEnv2; type_tac + +end RuleWF + + +/-! ## The semantic package -/ + +theorem roseTypesFold_eq : + roseSourceV.blockTypeConstants.foldlM + (fun env c => env.addConst c.name c.toVConstant) listFinalEnv07 = + some roseTypeEnv09 := rfl + +theorem roseCtorsFold_eq : + roseSourceV.blockConstructorConstants.foldlM + (fun env c => env.addConst c.name c.toVConstant) roseTypeEnv09 = + some roseCtorEnv09 := rfl + +theorem roseRecsFold_eq : + roseNestedC.recursors.foldlM + (fun env c => env.addConst c.name c.toVConstant) roseCtorEnv09 = + some roseRec1Env09 := by + rw [roseRecursors_eq]; rfl + +theorem roseNestedWF09 : roseNestedC.WF listFinalEnv07 := by + refine ⟨⟨roseFamilyWF09, fun env' h => ?_⟩, fun {typeEnv} h => ?_, + fun {typeEnv ctorEnv} hT hC => ?_, fun {typeEnv ctorEnv recEnv} hT hC hR => ?_⟩ + · cases Option.some.inj (roseTypeEnv09_eq.symm.trans h) + exact trivial + · cases Option.some.inj (roseTypesFold_eq.symm.trans h) + exact ⟨roseNodeWF09, fun env' h' => by + cases Option.some.inj (roseCtorEnv09_eq.symm.trans h') + exact trivial⟩ + · cases Option.some.inj (roseTypesFold_eq.symm.trans hT) + cases Option.some.inj (roseCtorsFold_eq.symm.trans hC) + rw [roseRecursors_eq] + exact ⟨roseRecWF09, fun env' h' => by + cases Option.some.inj (roseRecEnv09_eq.symm.trans h') + exact ⟨roseRec1WF09, fun env'' h'' => by + cases Option.some.inj (roseRec1Env09_eq.symm.trans h'') + exact trivial⟩⟩ + · cases Option.some.inj (roseTypesFold_eq.symm.trans hT) + cases Option.some.inj (roseCtorsFold_eq.symm.trans hC) + cases Option.some.inj (roseRecsFold_eq.symm.trans hR) + rw [roseRules_eq] + exact ⟨roseRule0WF09, roseRule1WF09, roseRule2WF09, trivial⟩ + + +/-! ## Freshness of the stored insertions -/ + +theorem listMapWF07 : listMap07.WF := + listCtorMapWF07.insert _ _ listRecFresh07 + +theorem roseTypeFresh09 : listMap07.find? ``RoseTree = none := by + rw [listMap07, listCtorMapWF07.find?_insert, listCtorMap07, + listNilMapWF07.find?_insert, listNilMap07, + listTypeMapWF07.find?_insert, listTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem roseTypeMapWF09 : roseTypeMap09.WF := + listMapWF07.insert _ _ roseTypeFresh09 + +theorem roseNodeFresh09 : roseTypeMap09.find? ``RoseTree.node = none := by + rw [roseTypeMap09, listMapWF07.find?_insert, listMap07, + listCtorMapWF07.find?_insert, listCtorMap07, + listNilMapWF07.find?_insert, listNilMap07, + listTypeMapWF07.find?_insert, listTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem roseCtorMapWF09 : roseCtorMap09.WF := + roseTypeMapWF09.insert _ _ roseNodeFresh09 + +theorem roseRecFresh09 : roseCtorMap09.find? ``RoseTree.rec = none := by + rw [roseCtorMap09, roseTypeMapWF09.find?_insert, roseTypeMap09, + listMapWF07.find?_insert, listMap07, + listCtorMapWF07.find?_insert, listCtorMap07, + listNilMapWF07.find?_insert, listNilMap07, + listTypeMapWF07.find?_insert, listTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem roseRecMapWF09 : roseRecMap09.WF := + roseCtorMapWF09.insert _ _ roseRecFresh09 + +theorem roseRec1Fresh09 : + roseRecMap09.find? `Ix.Theory.Named.NestedRepresentation.RoseTree.rec_1 = none := by + rw [roseRecMap09, roseCtorMapWF09.find?_insert, roseCtorMap09, + roseTypeMapWF09.find?_insert, roseTypeMap09, + listMapWF07.find?_insert, listMap07, + listCtorMapWF07.find?_insert, listCtorMap07, + listNilMapWF07.find?_insert, listNilMap07, + listTypeMapWF07.find?_insert, listTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +/-! ## Stored-metadata translations -/ + +theorem roseInfoTr09 : + TrConstVal .safe listFinalEnv07 roseInfo09 roseFamilyV := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr listFinalEnv07 roseInfo09.levelParams [] + roseInfo09.type roseFamilyV.toVConstant.type := by + tr_type_expr_tac + exact shape.to_trExprS listFinalOrdered07 trivial ⟨_, by type_tac⟩ + +theorem roseNodeTr09 : + TrConstVal .safe roseTypeEnv09 roseNodeInfo09 roseNodeV := by + have hList : roseTypeEnv09.constants ``List = + some ⟨1, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))⟩ := rfl + have hRose : roseTypeEnv09.constants ``RoseTree = + some ⟨1, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))⟩ := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr roseTypeEnv09 roseNodeInfo09.levelParams [] + roseNodeInfo09.type roseNodeV.toVConstant.type := by + tr_type_expr_tac + exact shape.to_trExprS roseTypeOrdered09 trivial ⟨_, by type_tac⟩ + +theorem roseRecTr09 : + TrConstVal .safe roseCtorEnv09 roseRecInfo09 roseRecVL := by + have hList : roseCtorEnv09.constants ``List = + some ⟨1, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))⟩ := rfl + have hRose : roseCtorEnv09.constants ``RoseTree = + some ⟨1, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))⟩ := rfl + have hNode : roseCtorEnv09.constants ``RoseTree.node = + some roseNodeV.toVConstant := rfl + have hNil : roseCtorEnv09.constants ``List.nil = + some ⟨1, .forallE (.sort (.succ (.param 0))) + (.app (.const `List [.param 0]) (.bvar 0))⟩ := rfl + have hCons : roseCtorEnv09.constants ``List.cons = + some ⟨1, .forallE (.sort (.succ (.param 0))) + (.forallE (.bvar 0) + (.forallE (.app (.const `List [.param 0]) (.bvar 1)) + (.app (.const `List [.param 0]) (.bvar 2))))⟩ := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr roseCtorEnv09 roseRecInfo09.levelParams [] + roseRecInfo09.type roseRecVL.toVConstant.type := by + tr_type_expr_tac + obtain ⟨u, hty⟩ := roseRecWF09 + exact shape.to_trExprS roseCtorOrdered09 trivial ⟨_, hty⟩ + +theorem roseRec1Tr09 : + TrConstVal .safe roseRecEnv09 roseRec1Info09 roseRec1VL := by + have hList : roseRecEnv09.constants ``List = + some ⟨1, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))⟩ := rfl + have hRose : roseRecEnv09.constants ``RoseTree = + some ⟨1, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))⟩ := rfl + have hNode : roseRecEnv09.constants ``RoseTree.node = + some roseNodeV.toVConstant := rfl + have hNil : roseRecEnv09.constants ``List.nil = + some ⟨1, .forallE (.sort (.succ (.param 0))) + (.app (.const `List [.param 0]) (.bvar 0))⟩ := rfl + have hCons : roseRecEnv09.constants ``List.cons = + some ⟨1, .forallE (.sort (.succ (.param 0))) + (.forallE (.bvar 0) + (.forallE (.app (.const `List [.param 0]) (.bvar 1)) + (.app (.const `List [.param 0]) (.bvar 2))))⟩ := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr roseRecEnv09 roseRec1Info09.levelParams [] + roseRec1Info09.type roseRec1VL.toVConstant.type := by + tr_type_expr_tac + obtain ⟨u, hty⟩ := roseRec1WF09 + exact shape.to_trExprS roseRecOrdered09 trivial ⟨_, hty⟩ + + +/-! ## Recursor K metadata and stored lookups -/ + +theorem roseKTarget09 : roseNestedC.generation.kTarget = false := by + native_decide + +theorem roseRecLookup09 : + roseMap09.find? ``RoseTree.rec = some roseRecInfo09 := by + rw [roseMap09, roseRecMapWF09.find?_insert] + simp [roseRecMap09, roseCtorMapWF09.find?_insert] + +theorem roseRec1Lookup09 : + roseMap09.find? `Ix.Theory.Named.NestedRepresentation.RoseTree.rec_1 = + some roseRec1Info09 := by + rw [roseMap09, roseRecMapWF09.find?_insert] + simp + +theorem roseRecK09 : + RecursorMapKMatches roseMap09 roseNestedC.recursors + roseNestedC.generation.kTarget := by + rw [roseRecursors_eq, roseKTarget09] + intro recursor hmem + rcases List.mem_cons.1 hmem with rfl | hmem + · exact ⟨roseRecInfo09, roseRecLookup09, by decide⟩ + rcases List.mem_cons.1 hmem with rfl | hmem + · exact ⟨roseRec1Info09, roseRec1Lookup09, by decide⟩ + · cases hmem + +/-! ## The nested alignment trace and its `TrEnv'` drive -/ + +def roseTrace09 : + AddInductNestedTrace listMap07 listFinalEnv07 roseSourceV + roseMap09 roseFinalEnv09 where + nested := roseNestedC + nested_wf := roseNestedWF09 + typeMap := roseTypeMap09 + typeEnv := roseTypeEnv09 + ctorMap := roseCtorMap09 + ctorEnv := roseCtorEnv09 + recEnv := roseRec1Env09 + addTypes := .cons + { info := roseInfo09 + kind_eq := trivial + tr := roseInfoTr09 + map_fresh := roseTypeFresh09 + env_add := roseTypeEnv09_eq + map_add := rfl } .nil + addCtors := .cons + { info := roseNodeInfo09 + kind_eq := trivial + tr := roseNodeTr09 + map_fresh := roseNodeFresh09 + env_add := roseCtorEnv09_eq + map_add := rfl } .nil + addRecs := roseRecursors_eq ▸ .cons + { info := roseRecInfo09 + kind_eq := trivial + tr := roseRecTr09 + map_fresh := roseRecFresh09 + env_add := roseRecEnv09_eq + map_add := rfl } (.cons + { info := roseRec1Info09 + kind_eq := trivial + tr := roseRec1Tr09 + map_fresh := roseRec1Fresh09 + env_add := roseRec1Env09_eq + map_add := rfl } .nil) + recK := roseRecK09 + addRules := ⟨by rw [roseRules_eq]; rfl⟩ + +theorem roseAddInductNested09 : + AddInductNested listMap07 listFinalEnv07 roseSourceV + roseMap09 roseFinalEnv09 := + ⟨roseTrace09⟩ + +/-- The rose-tree nested declaration, replayed from real stored metadata +over the completed `List` environment through the nested alignment +constructor. -/ +theorem roseTrEnv09 : TrEnv' .safe roseMap09 false roseFinalEnv09 := + .inductNested roseAddInductNested09 listTrEnv07 + +theorem roseFinalOrdered09 : roseFinalEnv09.Ordered := + roseTrEnv09.wf.ordered + + +/-! ## Round-trip guards + +The stored-metadata surface inserted by the trace is tied to the Theory +artifact inventory, and the final map/environment pair carries the +documented closure (persistent-map contracts plus the compiler-trust axioms +introduced by the `native_decide` observations). -/ + +#guard roseNestedC.elim.numNested == 1 +#guard roseRecV == roseRecVL && roseRec1V == roseRec1VL + +#guard_named_axioms Ix.Theory.Named.NestedReplayFixtures.roseTrEnv09 [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert, + Ix.Theory.Named.NestedReplayFixtures.roseKTarget09._native.native_decide.ax_1_1, + Ix.Theory.Named.NestedReplayFixtures.roseNestedC._native.native_decide.ax_1, + Ix.Theory.Named.NestedReplayFixtures.roseRecursors_eq._native.native_decide.ax_1_1, + Ix.Theory.Named.NestedReplayFixtures.roseRules_eq._native.native_decide.ax_1_1] + +#guard_named_axioms Ix.Theory.Named.NestedReplayFixtures.roseNestedWF09 [ + propext, + Classical.choice, + Quot.sound, + Ix.Theory.Named.NestedReplayFixtures.roseNestedC._native.native_decide.ax_1, + Ix.Theory.Named.NestedReplayFixtures.roseRecursors_eq._native.native_decide.ax_1_1, + Ix.Theory.Named.NestedReplayFixtures.roseRules_eq._native.native_decide.ax_1_1] + + +/-! # The nested-indexed fixture + +`NVTree` nests through the locally declared indexed `PVec`. The base +environment stages the `PVec` family and constructors over the completed +`Nat` replay through `TrEnv'.inductStaging`; the nested trace then inserts +the stored `NVTree` metadata and drives `TrEnv'.inductNested`. -/ + +/-! ## Staged `PVec` base -/ + +def pvecInfo09 : ConstantInfo := kernelInductInfo% PVec +def pvecNilInfo09 : ConstantInfo := kernelCtorInfo% PVec.nil +def pvecConsInfo09 : ConstantInfo := kernelCtorInfo% PVec.cons + +def pvecFamilyVL : VConstVal := + ⟨⟨0, .forallE (.sort (.succ .zero)) + (.forallE (.const `Nat []) (.sort (.succ .zero)))⟩, ``PVec⟩ +def pvecNilVL : VConstVal := ⟨⟨0, nestedConstVType09A% PVec.nil⟩, ``PVec.nil⟩ +def pvecConsVL : VConstVal := ⟨⟨0, nestedConstVType09A% PVec.cons⟩, ``PVec.cons⟩ + +def pvecTypeMap09 : ConstMap := natMap.insert ``PVec pvecInfo09 +def pvecNilMap09 : ConstMap := pvecTypeMap09.insert ``PVec.nil pvecNilInfo09 +def pvecCtorMap09 : ConstMap := pvecNilMap09.insert ``PVec.cons pvecConsInfo09 + +def pvecTypeEnv09 : VEnv := + (natFinalEnv.addConst pvecFamilyVL.name pvecFamilyVL.toVConstant).get! +def pvecNilEnv09 : VEnv := + (pvecTypeEnv09.addConst pvecNilVL.name pvecNilVL.toVConstant).get! +def pvecCtorEnv09 : VEnv := + (pvecNilEnv09.addConst pvecConsVL.name pvecConsVL.toVConstant).get! + +theorem pvecTypeFresh09 : natMap.find? ``PVec = none := by + rw [natMap, natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem pvecTypeMapWF09 : pvecTypeMap09.WF := + natMap_wf.insert _ _ pvecTypeFresh09 + +theorem pvecNilFresh09 : pvecTypeMap09.find? ``PVec.nil = none := by + rw [pvecTypeMap09, natMap_wf.find?_insert, natMap, + natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem pvecNilMapWF09 : pvecNilMap09.WF := + pvecTypeMapWF09.insert _ _ pvecNilFresh09 + +theorem pvecConsFresh09 : pvecNilMap09.find? ``PVec.cons = none := by + rw [pvecNilMap09, pvecTypeMapWF09.find?_insert, pvecTypeMap09, + natMap_wf.find?_insert, natMap, + natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem pvecCtorMapWF09 : pvecCtorMap09.WF := + pvecNilMapWF09.insert _ _ pvecConsFresh09 + +theorem natFinalOrdered09 : natFinalEnv.Ordered := + (nat_trEnv' (safety := .safe)).wf.ordered + +theorem pvecFamilyWF09 : pvecFamilyVL.toVConstant.WF natFinalEnv := by + have hNat : natFinalEnv.constants ``Nat = some ⟨0, .sort (.succ .zero)⟩ := rfl + exact ⟨_, by type_tac⟩ + +theorem pvecTypeEnv09_eq : + natFinalEnv.addConst pvecFamilyVL.name pvecFamilyVL.toVConstant = + some pvecTypeEnv09 := rfl + +theorem pvecTypeOrdered09 : pvecTypeEnv09.Ordered := + .const natFinalOrdered09 pvecFamilyWF09 pvecTypeEnv09_eq + +theorem pvecNilWF09 : pvecNilVL.toVConstant.WF pvecTypeEnv09 := by + have hNat : pvecTypeEnv09.constants ``Nat = some ⟨0, .sort (.succ .zero)⟩ := rfl + have hZero : pvecTypeEnv09.constants ``Nat.zero = some ⟨0, .const `Nat []⟩ := rfl + have hPVec : pvecTypeEnv09.constants ``PVec = some pvecFamilyVL.toVConstant := rfl + exact ⟨_, by type_tac⟩ + +theorem pvecNilEnv09_eq : + pvecTypeEnv09.addConst pvecNilVL.name pvecNilVL.toVConstant = + some pvecNilEnv09 := rfl + +theorem pvecNilOrdered09 : pvecNilEnv09.Ordered := + .const pvecTypeOrdered09 pvecNilWF09 pvecNilEnv09_eq + +theorem pvecConsWF09 : pvecConsVL.toVConstant.WF pvecNilEnv09 := by + have hNat : pvecNilEnv09.constants ``Nat = some ⟨0, .sort (.succ .zero)⟩ := rfl + have hSucc : pvecNilEnv09.constants ``Nat.succ = + some ⟨0, .forallE (.const `Nat []) (.const `Nat [])⟩ := rfl + have hPVec : pvecNilEnv09.constants ``PVec = some pvecFamilyVL.toVConstant := rfl + exact ⟨_, by type_tac⟩ + +theorem pvecConsEnv09_eq : + pvecNilEnv09.addConst pvecConsVL.name pvecConsVL.toVConstant = + some pvecCtorEnv09 := rfl + +theorem pvecCtorOrdered09 : pvecCtorEnv09.Ordered := + .const pvecNilOrdered09 pvecConsWF09 pvecConsEnv09_eq + +theorem pvecInfoTr09 : TrConstVal .safe natFinalEnv pvecInfo09 pvecFamilyVL := by + have hNat : natFinalEnv.constants ``Nat = some ⟨0, .sort (.succ .zero)⟩ := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr natFinalEnv pvecInfo09.levelParams [] + pvecInfo09.type pvecFamilyVL.toVConstant.type := by + tr_type_expr_tac + exact shape.to_trExprS natFinalOrdered09 trivial ⟨_, by type_tac⟩ + +theorem pvecNilTr09 : TrConstVal .safe pvecTypeEnv09 pvecNilInfo09 pvecNilVL := by + have hNat : pvecTypeEnv09.constants ``Nat = some ⟨0, .sort (.succ .zero)⟩ := rfl + have hZero : pvecTypeEnv09.constants ``Nat.zero = some ⟨0, .const `Nat []⟩ := rfl + have hPVec : pvecTypeEnv09.constants ``PVec = some pvecFamilyVL.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr pvecTypeEnv09 pvecNilInfo09.levelParams [] + pvecNilInfo09.type pvecNilVL.toVConstant.type := by + tr_type_expr_tac + exact shape.to_trExprS pvecTypeOrdered09 trivial ⟨_, by type_tac⟩ + +theorem pvecConsTr09 : TrConstVal .safe pvecNilEnv09 pvecConsInfo09 pvecConsVL := by + have hNat : pvecNilEnv09.constants ``Nat = some ⟨0, .sort (.succ .zero)⟩ := rfl + have hSucc : pvecNilEnv09.constants ``Nat.succ = + some ⟨0, .forallE (.const `Nat []) (.const `Nat [])⟩ := rfl + have hPVec : pvecNilEnv09.constants ``PVec = some pvecFamilyVL.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr pvecNilEnv09 pvecConsInfo09.levelParams [] + pvecConsInfo09.type pvecConsVL.toVConstant.type := by + tr_type_expr_tac + exact shape.to_trExprS pvecNilOrdered09 trivial ⟨_, by type_tac⟩ + +/-- The staged `PVec` boundary: family and constructors present, no +recursor or rules — exactly the constants the nested `NVTree` artifacts +reference. -/ +theorem pvecTrEnv09 : TrEnv' .safe pvecCtorMap09 false pvecCtorEnv09 := + .inductStaging (kind := .ctor) + { info := pvecConsInfo09 + kind_eq := trivial + tr := pvecConsTr09 + map_fresh := pvecConsFresh09 + env_add := pvecConsEnv09_eq + map_add := rfl } pvecConsWF09 <| + .inductStaging (kind := .ctor) + { info := pvecNilInfo09 + kind_eq := trivial + tr := pvecNilTr09 + map_fresh := pvecNilFresh09 + env_add := pvecNilEnv09_eq + map_add := rfl } pvecNilWF09 <| + .inductStaging (kind := .induct) + { info := pvecInfo09 + kind_eq := trivial + tr := pvecInfoTr09 + map_fresh := pvecTypeFresh09 + env_add := pvecTypeEnv09_eq + map_add := rfl } pvecFamilyWF09 nat_trEnv' + + +/-! ## The translated NV source and its nested artifact -/ + +def nvSourceV : VInductDecl where + uvars := 0 + nparams := 0 + types := + [{ name := ``NVTree + uvars := 0 + type := nestedConstVType09A% NVTree + ctors := [⟨⟨0, nestedConstVType09A% NVTree.node⟩, ``NVTree.node⟩] }] + +def nvNestedC? : Option (NestedBlockChecked nvSourceV) := + nestedBlockChecked? [NestedTransformation.pvecStoredTarget] nvSourceV + +#guard nvNestedC?.isSome + +def nvNestedC : NestedBlockChecked nvSourceV := + nvNestedC?.get (by native_decide) + +def nvInfo09 : ConstantInfo := kernelInductInfo% NVTree +def nvNodeInfo09 : ConstantInfo := kernelCtorInfo% NVTree.node +def nvRecInfo09 : ConstantInfo := kernelRecInfo% NVTree.rec +def nvRec1Info09 : ConstantInfo := kernelRecInfo% NVTree.rec_1 + +def nvFamilyV : VConstVal := nvSourceV.types[0].toVConstVal +def nvNodeV : VConstVal := nvSourceV.types[0].ctors[0] + +def nvTypeMap09 : ConstMap := pvecCtorMap09.insert ``NVTree nvInfo09 +def nvCtorMap09 : ConstMap := nvTypeMap09.insert ``NVTree.node nvNodeInfo09 +def nvRecMap09 : ConstMap := nvCtorMap09.insert ``NVTree.rec nvRecInfo09 +def nvMap09 : ConstMap := + nvRecMap09.insert `Ix.Theory.Named.NestedRepresentation.NVTree.rec_1 nvRec1Info09 + +def nvTypeEnv09 : VEnv := + (pvecCtorEnv09.addConst nvFamilyV.name nvFamilyV.toVConstant).get! +def nvCtorEnv09 : VEnv := + (nvTypeEnv09.addConst nvNodeV.name nvNodeV.toVConstant).get! + +def nvFamilyTypeL : VExpr := + .sort (.succ (.zero)) + +def nvNodeTypeL : VExpr := + .forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + +def nvRecTypeL : VExpr := + .forallE + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.sort (.param 0))) + (.forallE + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.sort (.param 0)))) + (.forallE + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app + (.app (.bvar 2) (.bvar 1)) + (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.NVTree.node []) + (.bvar 2)) + (.bvar 1)))))) + (.forallE + (.app + (.app (.bvar 1) (.const `Nat.zero [])) + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec.nil []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []))) + (.forallE + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app (.bvar 6) (.bvar 2)) + (.forallE + (.app + (.app (.bvar 6) (.bvar 2)) + (.bvar 1)) + (.app + (.app + (.bvar 7) + (.app + (.const `Nat.succ []) + (.bvar 3))) + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.PVec.cons + []) + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree + [])) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)))))))) + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.app (.bvar 5) (.bvar 0))))))) + +def nvRec1TypeL : VExpr := + .forallE + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.sort (.param 0))) + (.forallE + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.sort (.param 0)))) + (.forallE + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app + (.app (.bvar 2) (.bvar 1)) + (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.NVTree.node []) + (.bvar 2)) + (.bvar 1)))))) + (.forallE + (.app + (.app (.bvar 1) (.const `Nat.zero [])) + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec.nil []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []))) + (.forallE + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app (.bvar 6) (.bvar 2)) + (.forallE + (.app + (.app (.bvar 6) (.bvar 2)) + (.bvar 1)) + (.app + (.app + (.bvar 7) + (.app + (.const `Nat.succ []) + (.bvar 3))) + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.PVec.cons + []) + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree + [])) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)))))))) + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.app + (.app (.bvar 5) (.bvar 1)) + (.bvar 0)))))))) + +def nvRule0LhsL : VExpr := + .lam + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.sort (.param 0))) + (.lam + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.sort (.param 0)))) + (.lam + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app + (.app (.bvar 2) (.bvar 1)) + (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.NVTree.node []) + (.bvar 2)) + (.bvar 1)))))) + (.lam + (.app + (.app (.bvar 1) (.const `Nat.zero [])) + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec.nil []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []))) + (.lam + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app (.bvar 6) (.bvar 2)) + (.forallE + (.app + (.app (.bvar 6) (.bvar 2)) + (.bvar 1)) + (.app + (.app + (.bvar 7) + (.app + (.const `Nat.succ []) + (.bvar 3))) + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.PVec.cons + []) + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree + [])) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)))))))) + (.lam + (.const `Nat []) + (.lam + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.app + (.app + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree.rec + [.param 0]) + (.bvar 6)) + (.bvar 5)) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)) + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.NVTree.node []) + (.bvar 1)) + (.bvar 0))))))))) + +def nvRule0RhsL : VExpr := + .lam + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.sort (.param 0))) + (.lam + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.sort (.param 0)))) + (.lam + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app + (.app (.bvar 2) (.bvar 1)) + (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.NVTree.node []) + (.bvar 2)) + (.bvar 1)))))) + (.lam + (.app + (.app (.bvar 1) (.const `Nat.zero [])) + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec.nil []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []))) + (.lam + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app (.bvar 6) (.bvar 2)) + (.forallE + (.app + (.app (.bvar 6) (.bvar 2)) + (.bvar 1)) + (.app + (.app + (.bvar 7) + (.app + (.const `Nat.succ []) + (.bvar 3))) + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.PVec.cons + []) + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree + [])) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)))))))) + (.lam + (.const `Nat []) + (.lam + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.app + (.app + (.app (.bvar 4) (.bvar 1)) + (.bvar 0)) + (.app + (.app + (.app + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree.rec_1 + [.param 0]) + (.bvar 6)) + (.bvar 5)) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)) + (.bvar 1)) + (.bvar 0))))))))) + +def nvRule0TypeL : VExpr := + .forallE + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.sort (.param 0))) + (.forallE + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.sort (.param 0)))) + (.forallE + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app + (.app (.bvar 2) (.bvar 1)) + (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.NVTree.node []) + (.bvar 2)) + (.bvar 1)))))) + (.forallE + (.app + (.app (.bvar 1) (.const `Nat.zero [])) + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec.nil []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []))) + (.forallE + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app (.bvar 6) (.bvar 2)) + (.forallE + (.app + (.app (.bvar 6) (.bvar 2)) + (.bvar 1)) + (.app + (.app + (.bvar 7) + (.app + (.const `Nat.succ []) + (.bvar 3))) + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.PVec.cons + []) + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree + [])) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)))))))) + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.app + (.bvar 6) + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.NVTree.node []) + (.bvar 1)) + (.bvar 0))))))))) + +def nvRule1LhsL : VExpr := + .lam + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.sort (.param 0))) + (.lam + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.sort (.param 0)))) + (.lam + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app + (.app (.bvar 2) (.bvar 1)) + (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.NVTree.node []) + (.bvar 2)) + (.bvar 1)))))) + (.lam + (.app + (.app (.bvar 1) (.const `Nat.zero [])) + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec.nil []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []))) + (.lam + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app (.bvar 6) (.bvar 2)) + (.forallE + (.app + (.app (.bvar 6) (.bvar 2)) + (.bvar 1)) + (.app + (.app + (.bvar 7) + (.app + (.const `Nat.succ []) + (.bvar 3))) + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.PVec.cons + []) + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree + [])) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)))))))) + (.app + (.app + (.app + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree.rec_1 + [.param 0]) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)) + (.bvar 1)) + (.bvar 0)) + (.const `Nat.zero [])) + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec.nil []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []))))))) + +def nvRule1RhsL : VExpr := + .lam + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.sort (.param 0))) + (.lam + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.sort (.param 0)))) + (.lam + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app + (.app (.bvar 2) (.bvar 1)) + (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.NVTree.node []) + (.bvar 2)) + (.bvar 1)))))) + (.lam + (.app + (.app (.bvar 1) (.const `Nat.zero [])) + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec.nil []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []))) + (.lam + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app (.bvar 6) (.bvar 2)) + (.forallE + (.app + (.app (.bvar 6) (.bvar 2)) + (.bvar 1)) + (.app + (.app + (.bvar 7) + (.app + (.const `Nat.succ []) + (.bvar 3))) + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.PVec.cons + []) + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree + [])) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)))))))) + (.bvar 1))))) + +def nvRule1TypeL : VExpr := + .forallE + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.sort (.param 0))) + (.forallE + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.sort (.param 0)))) + (.forallE + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app + (.app (.bvar 2) (.bvar 1)) + (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.NVTree.node []) + (.bvar 2)) + (.bvar 1)))))) + (.forallE + (.app + (.app (.bvar 1) (.const `Nat.zero [])) + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec.nil []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []))) + (.forallE + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app (.bvar 6) (.bvar 2)) + (.forallE + (.app + (.app (.bvar 6) (.bvar 2)) + (.bvar 1)) + (.app + (.app + (.bvar 7) + (.app + (.const `Nat.succ []) + (.bvar 3))) + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.PVec.cons + []) + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree + [])) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)))))))) + (.app + (.app (.bvar 3) (.const `Nat.zero [])) + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec.nil []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []))))))) + +def nvRule2LhsL : VExpr := + .lam + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.sort (.param 0))) + (.lam + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.sort (.param 0)))) + (.lam + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app + (.app (.bvar 2) (.bvar 1)) + (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.NVTree.node []) + (.bvar 2)) + (.bvar 1)))))) + (.lam + (.app + (.app (.bvar 1) (.const `Nat.zero [])) + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec.nil []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []))) + (.lam + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app (.bvar 6) (.bvar 2)) + (.forallE + (.app + (.app (.bvar 6) (.bvar 2)) + (.bvar 1)) + (.app + (.app + (.bvar 7) + (.app + (.const `Nat.succ []) + (.bvar 3))) + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.PVec.cons + []) + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree + [])) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)))))))) + (.lam + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.lam + (.const `Nat []) + (.lam + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.app + (.app + (.app + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree.rec_1 + [.param 0]) + (.bvar 7)) + (.bvar 6)) + (.bvar 5)) + (.bvar 4)) + (.bvar 3)) + (.app + (.const `Nat.succ []) + (.bvar 1))) + (.app + (.app + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec.cons []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 2)) + (.bvar 1)) + (.bvar 0)))))))))) + +def nvRule2RhsL : VExpr := + .lam + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.sort (.param 0))) + (.lam + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.sort (.param 0)))) + (.lam + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app + (.app (.bvar 2) (.bvar 1)) + (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.NVTree.node []) + (.bvar 2)) + (.bvar 1)))))) + (.lam + (.app + (.app (.bvar 1) (.const `Nat.zero [])) + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec.nil []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []))) + (.lam + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app (.bvar 6) (.bvar 2)) + (.forallE + (.app + (.app (.bvar 6) (.bvar 2)) + (.bvar 1)) + (.app + (.app + (.bvar 7) + (.app + (.const `Nat.succ []) + (.bvar 3))) + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.PVec.cons + []) + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree + [])) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)))))))) + (.lam + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.lam + (.const `Nat []) + (.lam + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.app + (.app + (.app + (.app + (.app (.bvar 3) (.bvar 2)) + (.bvar 1)) + (.bvar 0)) + (.app + (.app + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree.rec + [.param 0]) + (.bvar 7)) + (.bvar 6)) + (.bvar 5)) + (.bvar 4)) + (.bvar 3)) + (.bvar 2))) + (.app + (.app + (.app + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree.rec_1 + [.param 0]) + (.bvar 7)) + (.bvar 6)) + (.bvar 5)) + (.bvar 4)) + (.bvar 3)) + (.bvar 1)) + (.bvar 0)))))))))) + +def nvRule2TypeL : VExpr := + .forallE + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.sort (.param 0))) + (.forallE + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.sort (.param 0)))) + (.forallE + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app + (.app (.bvar 2) (.bvar 1)) + (.bvar 0)) + (.app + (.bvar 4) + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.NVTree.node []) + (.bvar 2)) + (.bvar 1)))))) + (.forallE + (.app + (.app (.bvar 1) (.const `Nat.zero [])) + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec.nil []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []))) + (.forallE + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.forallE + (.app (.bvar 6) (.bvar 2)) + (.forallE + (.app + (.app (.bvar 6) (.bvar 2)) + (.bvar 1)) + (.app + (.app + (.bvar 7) + (.app + (.const `Nat.succ []) + (.bvar 3))) + (.app + (.app + (.app + (.app + (.const + `Ix.Theory.Named.NestedRepresentation.PVec.cons + []) + (.const + `Ix.Theory.Named.NestedRepresentation.NVTree + [])) + (.bvar 4)) + (.bvar 3)) + (.bvar 2)))))))) + (.forallE + (.const `Ix.Theory.Named.NestedRepresentation.NVTree []) + (.forallE + (.const `Nat []) + (.forallE + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 0)) + (.app + (.app + (.bvar 6) + (.app + (.const `Nat.succ []) + (.bvar 1))) + (.app + (.app + (.app + (.app + (.const `Ix.Theory.Named.NestedRepresentation.PVec.cons []) + (.const `Ix.Theory.Named.NestedRepresentation.NVTree [])) + (.bvar 2)) + (.bvar 1)) + (.bvar 0)))))))))) + +def nvRecVL : VConstVal := ⟨⟨1, nvRecTypeL⟩, ``NVTree.rec⟩ +def nvRec1VL : VConstVal := + ⟨⟨1, nvRec1TypeL⟩, `Ix.Theory.Named.NestedRepresentation.NVTree.rec_1⟩ + +def nvRulesL : List VDefEq := + [⟨1, nvRule0LhsL, nvRule0RhsL, nvRule0TypeL⟩, + ⟨1, nvRule1LhsL, nvRule1RhsL, nvRule1TypeL⟩, + ⟨1, nvRule2LhsL, nvRule2RhsL, nvRule2TypeL⟩] + +theorem nvRecursors_eq : nvNestedC.recursors = [nvRecVL, nvRec1VL] := by + native_decide + +theorem nvRules_eq : nvNestedC.generatedRules = nvRulesL := by + native_decide + +def nvRecEnv09 : VEnv := + (nvCtorEnv09.addConst nvRecVL.name nvRecVL.toVConstant).get! +def nvRec1Env09 : VEnv := + (nvRecEnv09.addConst nvRec1VL.name nvRec1VL.toVConstant).get! +def nvFinalEnv09 : VEnv := + nvRulesL.foldl VEnv.addDefEq nvRec1Env09 + + +/-! ## NV constant well-formedness and phase chains -/ + +macro "nv_hyps" e:term : tactic => `(tactic| ( + have hNat : VEnv.constants $e ``Nat = some ⟨0, .sort (.succ .zero)⟩ := rfl + have hZero : VEnv.constants $e ``Nat.zero = some ⟨0, .const `Nat []⟩ := rfl + have hSucc : VEnv.constants $e ``Nat.succ = + some ⟨0, .forallE (.const `Nat []) (.const `Nat [])⟩ := rfl + have hPVec : VEnv.constants $e ``PVec = some pvecFamilyVL.toVConstant := rfl + have hPNil : VEnv.constants $e ``PVec.nil = some pvecNilVL.toVConstant := rfl + have hPCons : VEnv.constants $e ``PVec.cons = some pvecConsVL.toVConstant := rfl)) + +theorem nvFamilyWF09 : nvFamilyV.toVConstant.WF pvecCtorEnv09 := + ⟨_, by type_tac⟩ + +theorem nvTypeEnv09_eq : + pvecCtorEnv09.addConst nvFamilyV.name nvFamilyV.toVConstant = + some nvTypeEnv09 := rfl + +theorem nvTypeOrdered09 : nvTypeEnv09.Ordered := + .const pvecCtorOrdered09 nvFamilyWF09 nvTypeEnv09_eq + +theorem nvNodeWF09 : nvNodeV.toVConstant.WF nvTypeEnv09 := by + nv_hyps nvTypeEnv09 + have hNV : nvTypeEnv09.constants ``NVTree = some ⟨0, .sort (.succ .zero)⟩ := rfl + exact ⟨_, by type_tac⟩ + +theorem nvCtorEnv09_eq : + nvTypeEnv09.addConst nvNodeV.name nvNodeV.toVConstant = + some nvCtorEnv09 := rfl + +theorem nvCtorOrdered09 : nvCtorEnv09.Ordered := + .const nvTypeOrdered09 nvNodeWF09 nvCtorEnv09_eq + +set_option maxRecDepth 4000 in +theorem nvRecWF09 : (⟨1, nvRecTypeL⟩ : VConstant).WF nvCtorEnv09 := by + nv_hyps nvCtorEnv09 + have hNV : nvCtorEnv09.constants ``NVTree = some ⟨0, .sort (.succ .zero)⟩ := rfl + have hNode : nvCtorEnv09.constants ``NVTree.node = + some nvNodeV.toVConstant := rfl + exact ⟨_, by type_tac⟩ + +theorem nvRecEnv09_eq : + nvCtorEnv09.addConst nvRecVL.name nvRecVL.toVConstant = + some nvRecEnv09 := rfl + +theorem nvRecOrdered09 : nvRecEnv09.Ordered := + .const nvCtorOrdered09 nvRecWF09 nvRecEnv09_eq + +set_option maxRecDepth 4000 in +theorem nvRec1WF09 : (⟨1, nvRec1TypeL⟩ : VConstant).WF nvRecEnv09 := by + nv_hyps nvRecEnv09 + have hNV : nvRecEnv09.constants ``NVTree = some ⟨0, .sort (.succ .zero)⟩ := rfl + have hNode : nvRecEnv09.constants ``NVTree.node = + some nvNodeV.toVConstant := rfl + exact ⟨_, by type_tac⟩ + +theorem nvRec1Env09_eq : + nvRecEnv09.addConst nvRec1VL.name nvRec1VL.toVConstant = + some nvRec1Env09 := rfl + +theorem nvRec1Ordered09 : nvRec1Env09.Ordered := + .const nvRecOrdered09 nvRec1WF09 nvRec1Env09_eq + +section NVRuleWF + +set_option maxRecDepth 8000 + +macro "nv_rule_hyps" e:term : tactic => `(tactic| ( + nv_hyps $e + have hNV : VEnv.constants $e ``NVTree = some ⟨0, .sort (.succ .zero)⟩ := rfl + have hNode : VEnv.constants $e ``NVTree.node = some nvNodeV.toVConstant := rfl + have hRec : VEnv.constants $e ``NVTree.rec = some ⟨1, nvRecTypeL⟩ := rfl + have hRec1 : VEnv.constants $e + `Ix.Theory.Named.NestedRepresentation.NVTree.rec_1 = some ⟨1, nvRec1TypeL⟩ := rfl)) + +def nvRuleEnv1 : VEnv := nvRec1Env09.addDefEq nvRulesL[0] +def nvRuleEnv2 : VEnv := nvRuleEnv1.addDefEq nvRulesL[1] + +theorem nvRule0WF09 : nvRulesL[0].WF nvRec1Env09 := by + constructor + · nv_rule_hyps nvRec1Env09; type_tac + · nv_rule_hyps nvRec1Env09; type_tac + +theorem nvRule1WF09 : nvRulesL[1].WF nvRuleEnv1 := by + constructor + · nv_rule_hyps nvRuleEnv1; type_tac + · nv_rule_hyps nvRuleEnv1; type_tac + +theorem nvRule2WF09 : nvRulesL[2].WF nvRuleEnv2 := by + constructor + · nv_rule_hyps nvRuleEnv2; type_tac + · nv_rule_hyps nvRuleEnv2; type_tac + +end NVRuleWF + + +/-! ## NV semantic package -/ + +theorem nvTypesFold_eq : + nvSourceV.blockTypeConstants.foldlM + (fun env c => env.addConst c.name c.toVConstant) pvecCtorEnv09 = + some nvTypeEnv09 := rfl + +theorem nvCtorsFold_eq : + nvSourceV.blockConstructorConstants.foldlM + (fun env c => env.addConst c.name c.toVConstant) nvTypeEnv09 = + some nvCtorEnv09 := rfl + +theorem nvRecsFold_eq : + nvNestedC.recursors.foldlM + (fun env c => env.addConst c.name c.toVConstant) nvCtorEnv09 = + some nvRec1Env09 := by + rw [nvRecursors_eq]; rfl + +theorem nvNestedWF09 : nvNestedC.WF pvecCtorEnv09 := by + refine ⟨⟨nvFamilyWF09, fun env' h => ?_⟩, fun {typeEnv} h => ?_, + fun {typeEnv ctorEnv} hT hC => ?_, fun {typeEnv ctorEnv recEnv} hT hC hR => ?_⟩ + · cases Option.some.inj (nvTypeEnv09_eq.symm.trans h) + exact trivial + · cases Option.some.inj (nvTypesFold_eq.symm.trans h) + exact ⟨nvNodeWF09, fun env' h' => by + cases Option.some.inj (nvCtorEnv09_eq.symm.trans h') + exact trivial⟩ + · cases Option.some.inj (nvTypesFold_eq.symm.trans hT) + cases Option.some.inj (nvCtorsFold_eq.symm.trans hC) + rw [nvRecursors_eq] + exact ⟨nvRecWF09, fun env' h' => by + cases Option.some.inj (nvRecEnv09_eq.symm.trans h') + exact ⟨nvRec1WF09, fun env'' h'' => by + cases Option.some.inj (nvRec1Env09_eq.symm.trans h'') + exact trivial⟩⟩ + · cases Option.some.inj (nvTypesFold_eq.symm.trans hT) + cases Option.some.inj (nvCtorsFold_eq.symm.trans hC) + cases Option.some.inj (nvRecsFold_eq.symm.trans hR) + rw [nvRules_eq] + exact ⟨nvRule0WF09, nvRule1WF09, nvRule2WF09, trivial⟩ + +/-! ## NV freshness and stored-metadata translations -/ + +theorem nvTypeFresh09 : pvecCtorMap09.find? ``NVTree = none := by + rw [pvecCtorMap09, pvecNilMapWF09.find?_insert, pvecNilMap09, + pvecTypeMapWF09.find?_insert, pvecTypeMap09, + natMap_wf.find?_insert, natMap, + natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem nvTypeMapWF09 : nvTypeMap09.WF := + pvecCtorMapWF09.insert _ _ nvTypeFresh09 + +theorem nvNodeFresh09 : nvTypeMap09.find? ``NVTree.node = none := by + rw [nvTypeMap09, pvecCtorMapWF09.find?_insert, pvecCtorMap09, + pvecNilMapWF09.find?_insert, pvecNilMap09, + pvecTypeMapWF09.find?_insert, pvecTypeMap09, + natMap_wf.find?_insert, natMap, + natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem nvCtorMapWF09 : nvCtorMap09.WF := + nvTypeMapWF09.insert _ _ nvNodeFresh09 + +theorem nvRecFresh09 : nvCtorMap09.find? ``NVTree.rec = none := by + rw [nvCtorMap09, nvTypeMapWF09.find?_insert, nvTypeMap09, + pvecCtorMapWF09.find?_insert, pvecCtorMap09, + pvecNilMapWF09.find?_insert, pvecNilMap09, + pvecTypeMapWF09.find?_insert, pvecTypeMap09, + natMap_wf.find?_insert, natMap, + natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem nvRecMapWF09 : nvRecMap09.WF := + nvCtorMapWF09.insert _ _ nvRecFresh09 + +theorem nvRec1Fresh09 : + nvRecMap09.find? `Ix.Theory.Named.NestedRepresentation.NVTree.rec_1 = none := by + rw [nvRecMap09, nvCtorMapWF09.find?_insert, nvCtorMap09, + nvTypeMapWF09.find?_insert, nvTypeMap09, + pvecCtorMapWF09.find?_insert, pvecCtorMap09, + pvecNilMapWF09.find?_insert, pvecNilMap09, + pvecTypeMapWF09.find?_insert, pvecTypeMap09, + natMap_wf.find?_insert, natMap, + natCtorMap_wf.find?_insert, natCtorMap, + natZeroMap_wf.find?_insert, natZeroMap, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem nvInfoTr09 : TrConstVal .safe pvecCtorEnv09 nvInfo09 nvFamilyV := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr pvecCtorEnv09 nvInfo09.levelParams [] + nvInfo09.type nvFamilyV.toVConstant.type := by + tr_type_expr_tac + exact shape.to_trExprS pvecCtorOrdered09 trivial ⟨_, by type_tac⟩ + +theorem nvNodeTr09 : TrConstVal .safe nvTypeEnv09 nvNodeInfo09 nvNodeV := by + nv_hyps nvTypeEnv09 + have hNV : nvTypeEnv09.constants ``NVTree = some ⟨0, .sort (.succ .zero)⟩ := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr nvTypeEnv09 nvNodeInfo09.levelParams [] + nvNodeInfo09.type nvNodeV.toVConstant.type := by + tr_type_expr_tac + exact shape.to_trExprS nvTypeOrdered09 trivial ⟨_, by type_tac⟩ + +theorem nvRecTr09 : TrConstVal .safe nvCtorEnv09 nvRecInfo09 nvRecVL := by + nv_hyps nvCtorEnv09 + have hNV : nvCtorEnv09.constants ``NVTree = some ⟨0, .sort (.succ .zero)⟩ := rfl + have hNode : nvCtorEnv09.constants ``NVTree.node = some nvNodeV.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr nvCtorEnv09 nvRecInfo09.levelParams [] + nvRecInfo09.type nvRecVL.toVConstant.type := by + tr_type_expr_tac + obtain ⟨u, hty⟩ := nvRecWF09 + exact shape.to_trExprS nvCtorOrdered09 trivial ⟨_, hty⟩ + +theorem nvRec1Tr09 : TrConstVal .safe nvRecEnv09 nvRec1Info09 nvRec1VL := by + nv_hyps nvRecEnv09 + have hNV : nvRecEnv09.constants ``NVTree = some ⟨0, .sort (.succ .zero)⟩ := rfl + have hNode : nvRecEnv09.constants ``NVTree.node = some nvNodeV.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr nvRecEnv09 nvRec1Info09.levelParams [] + nvRec1Info09.type nvRec1VL.toVConstant.type := by + tr_type_expr_tac + obtain ⟨u, hty⟩ := nvRec1WF09 + exact shape.to_trExprS nvRecOrdered09 trivial ⟨_, hty⟩ + +/-! ## NV recursor K metadata, trace, and `TrEnv'` drive -/ + +theorem nvKTarget09 : nvNestedC.generation.kTarget = false := by + native_decide + +theorem nvRecLookup09 : nvMap09.find? ``NVTree.rec = some nvRecInfo09 := by + rw [nvMap09, nvRecMapWF09.find?_insert] + simp [nvRecMap09, nvCtorMapWF09.find?_insert] + +theorem nvRec1Lookup09 : + nvMap09.find? `Ix.Theory.Named.NestedRepresentation.NVTree.rec_1 = + some nvRec1Info09 := by + rw [nvMap09, nvRecMapWF09.find?_insert] + simp + +theorem nvRecK09 : + RecursorMapKMatches nvMap09 nvNestedC.recursors + nvNestedC.generation.kTarget := by + rw [nvRecursors_eq, nvKTarget09] + intro recursor hmem + rcases List.mem_cons.1 hmem with rfl | hmem + · exact ⟨nvRecInfo09, nvRecLookup09, by decide⟩ + rcases List.mem_cons.1 hmem with rfl | hmem + · exact ⟨nvRec1Info09, nvRec1Lookup09, by decide⟩ + · cases hmem + +def nvTrace09 : + AddInductNestedTrace pvecCtorMap09 pvecCtorEnv09 nvSourceV + nvMap09 nvFinalEnv09 where + nested := nvNestedC + nested_wf := nvNestedWF09 + typeMap := nvTypeMap09 + typeEnv := nvTypeEnv09 + ctorMap := nvCtorMap09 + ctorEnv := nvCtorEnv09 + recEnv := nvRec1Env09 + addTypes := .cons + { info := nvInfo09 + kind_eq := trivial + tr := nvInfoTr09 + map_fresh := nvTypeFresh09 + env_add := nvTypeEnv09_eq + map_add := rfl } .nil + addCtors := .cons + { info := nvNodeInfo09 + kind_eq := trivial + tr := nvNodeTr09 + map_fresh := nvNodeFresh09 + env_add := nvCtorEnv09_eq + map_add := rfl } .nil + addRecs := nvRecursors_eq ▸ .cons + { info := nvRecInfo09 + kind_eq := trivial + tr := nvRecTr09 + map_fresh := nvRecFresh09 + env_add := nvRecEnv09_eq + map_add := rfl } (.cons + { info := nvRec1Info09 + kind_eq := trivial + tr := nvRec1Tr09 + map_fresh := nvRec1Fresh09 + env_add := nvRec1Env09_eq + map_add := rfl } .nil) + recK := nvRecK09 + addRules := ⟨by rw [nvRules_eq]; rfl⟩ + +theorem nvAddInductNested09 : + AddInductNested pvecCtorMap09 pvecCtorEnv09 nvSourceV + nvMap09 nvFinalEnv09 := + ⟨nvTrace09⟩ + +/-- The nested-indexed declaration, replayed from real stored metadata over +the staged `PVec` boundary through the nested alignment constructor. -/ +theorem nvTrEnv09 : TrEnv' .safe nvMap09 false nvFinalEnv09 := + .inductNested nvAddInductNested09 pvecTrEnv09 + +theorem nvFinalOrdered09 : nvFinalEnv09.Ordered := + nvTrEnv09.wf.ordered + +#guard nvNestedC.elim.numNested == 1 + + +#guard_named_axioms Ix.Theory.Named.NestedReplayFixtures.nvTrEnv09 [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert, + Ix.Theory.Named.NestedReplayFixtures.nvKTarget09._native.native_decide.ax_1_1, + Ix.Theory.Named.NestedReplayFixtures.nvNestedC._native.native_decide.ax_1, + Ix.Theory.Named.NestedReplayFixtures.nvRecursors_eq._native.native_decide.ax_1_1, + Ix.Theory.Named.NestedReplayFixtures.nvRules_eq._native.native_decide.ax_1_1] + +end Ix.Theory.Named.NestedReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/NestedRepresentation.lean b/Ix/Theory/Named/Verify/Environment/NestedRepresentation.lean new file mode 100644 index 000000000..765a88594 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/NestedRepresentation.lean @@ -0,0 +1,718 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Reference.Environment +import Ix.Theory.Named.Inductive +import Ix.Theory.Named.Meta + +open Ix.Theory (VLevel) + +/-! +# Spec-09A: nested-inductive representation audit and decision + +This file is the committed design note and the executable metadata probes +for the nested-inductive representation decision. Every claim below is +pinned by a build-failing probe in this file unless it is explicitly marked +as a forward-looking obligation. This checkpoint changes no acceptance +behavior: the probes only observe the implementation and the existing +Theory analyzers. + +## Audit: how the implementation represents nested inductives + +`Environment.addInductive` (Inductive/Add.lean) runs three phases: + +1. `ElimNestedInductive.run` rewrites the source declaration into a + flattened mutual block: every nested occurrence `I Ds is` whose + parametric arguments `Ds` mention a block family is replaced by + `auxI As is`, where `auxI` is a fresh auxiliary family abstracted over + the block parameters `As`, and one auxiliary family is created for each + family of `I`'s mutual block, with constructor types instantiated at + `Ds` (recursively rewritten). `aux2nested` records `auxI ↦ I Ds`, open + over the block parameters. Auxiliary names are uniquified against the + ambient environment (`mkUniqueName`). +2. `AddInductive.run` checks and generates the flattened block as an + ordinary mutual block, receiving `numNested` (the number of auxiliary + families) as opaque metadata. +3. When `numNested ≠ 0`, a restoration pass rebuilds the final environment + from the *pre-block* environment: source families and constructors are + re-added with every auxiliary constant replaced by its nested + restoration (`Result.restoreNested`), each auxiliary family's recursor + is re-added under the name `(mkRecName mainName).appendIndexAfter i` + with restored type and rules, and finally every `aux2nested` value + `I Ds` is type-checked (the lean4#14577 escape-hatch check, regression + tested in `Tests/NestedInductive.lean`). The auxiliary families, + constructors, and recursor names never enter the final environment. + +The stored metadata therefore has this shape (probes P1, P2): + +- The source `inductInfo` keeps `all` = the source family names only and + carries `numNested` = the number of auxiliary families; stored + constructor types are in restored form (they mention `I Ds`, e.g. + `List (RoseTree α)`). +- The recursor inventory is one recursor per source family plus one per + auxiliary family, all with `all` = source names, and with + `numMotives`/`numMinors` counting the *flattened* block's families and + minors. Auxiliary recursors have rules keyed by constructors of the + previously declared nested inductive (`List.nil`, `List.cons`, ...) with + `nfields` counting the instantiated auxiliary constructor's fields, and + every rule RHS references the restored recursor constants mutually. +- No `_nested.*` constant, and no auxiliary recursor under its original + name, survives into the final environment. + +## Decision: additive artifact type; `VInductDecl` unchanged + +The stored Theory payload for a nested declaration must be the *source* +`VInductDecl` (restored form), because that is what the implementation +stores and what Verify alignment must replay. Storing the flattened block +is unrepresentable: the final `ConstMap` contains neither the auxiliary +families nor their constructors (probe P2), and the stored constructor +types differ from the flattened ones (probe P1). `VInductDecl` needs no +new field: `numNested` is implementation metadata recoverable as the +number of auxiliary specifications, and parity fixtures pin it per row +exactly as they already pin `numNested == 0` for non-nested rows. + +Nested support is an additive checked-block artifact (built in Spec-09B/C), +coupling: + +1. the flattened block as an ordinary `VInductDecl` — probe P4 shows both + target fixtures' flattened blocks are already accepted by the existing + `identityBlockGeneration?` machinery, so flattening reuses the complete + Spec-08 block analyzer and generator unchanged; +2. one auxiliary specification per auxiliary family, in flattened family + order: the auxiliary name, the nested value `I Ds` open over the block + parameters (the Theory analog of `aux2nested`), and the restored + recursor name — plus executable coherence checks tying the flattened + block to the source declaration and to the environment's metadata for + `I` at `Ds`; +3. the restoration substitution σ, a structural constant substitution on + `VExpr` (probe P5, `restoreV09A`): on an application spine headed by an + auxiliary constant, the first `nparams` spine arguments are consumed + and replaced by the instantiated value `I Ds`; auxiliary constructor + constants are renamed by prefix into `I`'s constructors, applied to the + instantiated value's own arguments; auxiliary recursor constants are + renamed (checked *before* the constructor-prefix case, exactly like + `restoreNested`'s `auxRec` map); levels come from the recorded value, + not the auxiliary constant. + +σ has one level-world subtlety (probe P5): specification values live in +declaration level-world, while recursor types and rules live in recursor +level-world, so σ over generation artifacts splices +`value.instL (VLevel.params' uvars elimOffset)`. Constructor types are +restored with the unshifted value. With that splice, σ over the flattened +block's existing `BlockGenerationChecked` artifacts reproduces the stored +kernel metadata *exactly* — every recursor type and every rule RHS of all +three probe fixtures — and no auxiliary constant survives the image. +Probe P2 additionally shows the port's full nested path reproduces Lean's +stored metadata field-for-field, and that the final metadata is +independent of auxiliary-name collisions (the uniquified names are erased +by σ), so Theory may choose canonical auxiliary names as artifact data. + +Rejected alternatives: + +- *Flattened block as stored payload*: contradicts the stored metadata + (P1/P2); Verify alignment would have to invent constants the + implementation never stores. +- *Changing `VInductDecl` fields*: unnecessary — the probes demonstrate + the additive artifact expresses real rose-tree, nested-indexed, and + constant-universe metadata; a payload change would ripple through every + exported Theory API without demonstrated need. +- *A Prop-only pre-flattening relation without an artifact*: the + specifications and σ are data consumed by generation and replay; a + relation alone would force Verify to re-synthesize them. The artifact's + executable coherence checks subsume the relation. + +## Obligations recorded for Spec-09B/09C (not claimed here) + +- 09B: Theory-side flattening and auxiliary-specification validation — + positivity through the existing block analyzer on the flattened block; + executable instantiation checks of auxiliary family/constructor types + against `I`'s metadata at `Ds`; nearest rejection differentials + (ill-typed `Ds` — the lean4#14577 class — wrong specification order, + non-matching instantiation). +- 09C: σ as a total Theory function. The spine rule needs a simultaneous + `instantiateRev`-style multi-substitution for `nparams > 1`: iterating + single `VExpr.inst` is wrong once parameter arguments mention bvars. + Generation, preservation (typing transport along σ: auxiliary constants + behave as definitions `auxI := λ As, I Ds`, so staged flattened-block WF + transports to restored WF given environment lookup facts for `I`'s + families and constructors), insertion order, and replay of real + `Inductive.Add.run` output. +- The kernel's trailing `checkType (I Ds)` becomes a WF premise of the + auxiliary specification, never a trusted escape hatch. +-/ + +namespace Ix.Theory.Named.NestedRepresentation + +open Lean + +/-! ## Probe fixtures + +`RoseTree` is the universe-polymorphic rose tree through `List`; `NVTree` +nests through the locally declared indexed family `PVec` (indices spelled +with `Nat.zero`/`Nat.succ` to keep the probe dependency maps free of +notation instances); `CURose` nests `List` at a constant universe, so its +auxiliary constant carries no block level while the restored `List` +carries level `1` — the level-instantiation case σ must represent. -/ + +inductive RoseTree (α : Type u) : Type u where + | node : α → List (RoseTree α) → RoseTree α + +inductive PVec (α : Type) : Nat → Type where + | nil : PVec α Nat.zero + | cons : α → {n : Nat} → PVec α n → PVec α (Nat.succ n) + +inductive NVTree : Type where + | node : (n : Nat) → PVec NVTree n → NVTree + +inductive CURose : Type 1 where + | node : List CURose → CURose + +/-! ## Quoted stored metadata + +Local pin records keep this file independent of the replay fixture +inventory; a change in Lean's emitted metadata is a compile failure. -/ + +structure InductPins where + name : Name + lparams : List Name + numParams : Nat + numIndices : Nat + all : List Name + ctors : List Name + numNested : Nat + isRec : Bool + isReflexive : Bool + isUnsafe : Bool + deriving ToExpr, BEq + +structure CtorPins where + name : Name + lparams : List Name + induct : Name + cidx : Nat + numParams : Nat + numFields : Nat + deriving ToExpr, BEq + +structure RecPins where + name : Name + lparams : List Name + all : List Name + numParams : Nat + numIndices : Nat + numMotives : Nat + numMinors : Nat + k : Bool + rules : List (Name × Nat) + deriving ToExpr, BEq + +open Elab Term in +elab "nestedInductPins09A%" n:ident : term => do + let name ← realizeGlobalConstNoOverloadWithInfo n + let .inductInfo i ← getConstInfo name | throwError "expected inductive {name}" + return toExpr (InductPins.mk i.name i.levelParams i.numParams i.numIndices + i.all i.ctors i.numNested i.isRec i.isReflexive i.isUnsafe) + +open Elab Term in +elab "nestedCtorPins09A%" n:ident : term => do + let name ← realizeGlobalConstNoOverloadWithInfo n + let .ctorInfo i ← getConstInfo name | throwError "expected constructor {name}" + return toExpr (CtorPins.mk i.name i.levelParams i.induct i.cidx + i.numParams i.numFields) + +open Elab Term in +elab "nestedRecPins09A%" n:ident : term => do + let name ← realizeGlobalConstNoOverloadWithInfo n + let .recInfo i ← getConstInfo name | throwError "expected recursor {name}" + return toExpr (RecPins.mk i.name i.levelParams i.all i.numParams i.numIndices + i.numMotives i.numMinors i.k (i.rules.map fun r => (r.ctor, r.nfields))) + +-- Quote a stored `ConstantInfo.type` in that record's own universe order. +open Elab Term in +elab "nestedConstVType09A%" n:ident : term => do + let name ← realizeGlobalConstNoOverloadWithInfo n + let info ← getConstInfo name + let type ← Ix.Theory.Named.Meta.expandExpr info.type + return toExpr (← Ix.Theory.Named.Meta.ofExpr info.levelParams {} type) + +/-! ## P1: stored-metadata pins + +The source family keeps `all` = source names and counts its auxiliary +families in `numNested`; constructor types are restored; the recursor +inventory reveals the flattened block through `numMotives`/`numMinors` and +through auxiliary recursors whose rules are keyed by the constructors of a +previously declared inductive. -/ + +def roseAux : Name := (`_nested ++ ``List).appendIndexAfter 1 +def nvAux : Name := (`_nested ++ ``PVec).appendIndexAfter 1 + +def roseInductPins : InductPins := nestedInductPins09A% RoseTree +def roseNodePins : CtorPins := nestedCtorPins09A% RoseTree.node +def roseRecPins : RecPins := nestedRecPins09A% RoseTree.rec +def roseRec1Pins : RecPins := nestedRecPins09A% RoseTree.rec_1 + +#guard roseInductPins.numNested == 1 +#guard roseInductPins.all == [``RoseTree] +#guard roseInductPins.ctors == [``RoseTree.node] +#guard roseInductPins.lparams == [`u] && roseInductPins.numParams == 1 +#guard roseInductPins.isRec && !roseInductPins.isReflexive && !roseInductPins.isUnsafe +#guard roseNodePins == + { name := ``RoseTree.node, lparams := [`u], induct := ``RoseTree, cidx := 0, + numParams := 1, numFields := 2 } +#guard roseRecPins == + { name := ``RoseTree.rec, lparams := [`u_1, `u], all := [``RoseTree], numParams := 1, + numIndices := 0, numMotives := 2, numMinors := 3, k := false, + rules := [(``RoseTree.node, 2)] } +#guard roseRec1Pins == + { name := (mkRecName ``RoseTree).appendIndexAfter 1, lparams := [`u_1, `u], + all := [``RoseTree], numParams := 1, numIndices := 0, numMotives := 2, numMinors := 3, + k := false, rules := [(``List.nil, 0), (``List.cons, 2)] } + +/-- The stored constructor type is the restored form: it mentions +`List (RoseTree α)`, not an auxiliary constant. -/ +def roseNodeStoredType : VExpr := nestedConstVType09A% RoseTree.node + +#guard roseNodeStoredType == + .forallE (.sort (.succ (.param 0))) + (.forallE (.bvar 0) + (.forallE (.app (.const ``List [.param 0]) (.app (.const ``RoseTree [.param 0]) (.bvar 1))) + (.app (.const ``RoseTree [.param 0]) (.bvar 2)))) + +def nvInductPins : InductPins := nestedInductPins09A% NVTree +def nvNodePins : CtorPins := nestedCtorPins09A% NVTree.node +def nvRecPins : RecPins := nestedRecPins09A% NVTree.rec +def nvRec1Pins : RecPins := nestedRecPins09A% NVTree.rec_1 + +#guard nvInductPins.numNested == 1 +#guard nvInductPins.all == [``NVTree] && nvInductPins.ctors == [``NVTree.node] +#guard nvNodePins == + { name := ``NVTree.node, lparams := [], induct := ``NVTree, cidx := 0, + numParams := 0, numFields := 2 } +#guard nvRecPins == + { name := ``NVTree.rec, lparams := [`u], all := [``NVTree], numParams := 0, + numIndices := 0, numMotives := 2, numMinors := 3, k := false, + rules := [(``NVTree.node, 2)] } +-- The auxiliary recursor keeps the auxiliary family's index and its rules +-- count the instantiated constructor's fields (`PVec.cons` retains its +-- implicit index field: 3 fields, not 2). +#guard nvRec1Pins == + { name := (mkRecName ``NVTree).appendIndexAfter 1, lparams := [`u], all := [``NVTree], + numParams := 0, numIndices := 1, numMotives := 2, numMinors := 3, k := false, + rules := [(``PVec.nil, 0), (``PVec.cons, 3)] } + +def nvNodeStoredType : VExpr := nestedConstVType09A% NVTree.node + +#guard nvNodeStoredType == + .forallE (.const ``Nat []) + (.forallE (.app (.app (.const ``PVec []) (.const ``NVTree [])) (.bvar 0)) + (.const ``NVTree [])) + +def cuInductPins : InductPins := nestedInductPins09A% CURose +def cuRecPins : RecPins := nestedRecPins09A% CURose.rec +def cuRec1Pins : RecPins := nestedRecPins09A% CURose.rec_1 + +#guard cuInductPins.numNested == 1 && cuInductPins.all == [``CURose] +#guard cuRecPins.rules == [(``CURose.node, 1)] && cuRecPins.numMotives == 2 +#guard cuRec1Pins.rules == [(``List.nil, 0), (``List.cons, 2)] + +/-- The restored constructor type instantiates `List` at the constant level +`1` even though the declaration has no level parameters. -/ +def cuNodeStoredType : VExpr := nestedConstVType09A% CURose.node + +#guard cuNodeStoredType == + .forallE (.app (.const ``List [.succ .zero]) (.const ``CURose [])) + (.const ``CURose []) + +/-! ## Shared probe plumbing -/ + +def sourceType09A (env : Environment) (n : Name) : InductiveType := Id.run do + let some (.inductInfo info) := env.find? n | panic! "expected inductive" + let ctors := info.ctors.map fun c => Id.run do + let some (.ctorInfo ci) := env.find? c | panic! "expected constructor" + return { name := c, type := ci.type : Constructor } + return { name := n, type := info.type, ctors } + +def depMap09A (env : Environment) (ns : List Name) : ConstMap := + ns.foldl (fun m n => m.insert n (env.find? n).get!) {} + +open ElimNestedInductive in +/-- Run the port's flattening phase, returning the flattened block and the +`aux2nested` values abstracted over the block parameters. -/ +def runElim09A (env : Kernel.Environment) (lparams : List Name) (nparams : Nat) + (types : List InductiveType) : + Except Kernel.Exception (List InductiveType × List (Name × Expr)) := do + let res : ElimNestedInductive.Result ← ElimNestedInductive.run 1000 nparams types env + |>.run' { lvls := lparams.map .param, newTypes := types.toArray } + return (res.types, res.aux2nested.toList.map fun (n, e) => (n, e.abstract res.params)) + +/-- Field-for-field stored/ported agreement for the constant kinds a nested +declaration emits. -/ +def sameConst09A (a b : ConstantInfo) : Bool := + a.name == b.name && a.levelParams == b.levelParams && a.type == b.type && + match a, b with + | .recInfo ra, .recInfo rb => + ra.all == rb.all && ra.numParams == rb.numParams && + ra.numIndices == rb.numIndices && ra.numMotives == rb.numMotives && + ra.numMinors == rb.numMinors && ra.k == rb.k && + ra.isUnsafe == rb.isUnsafe && + ra.rules.map (fun r => (r.ctor, r.nfields, r.rhs)) == + rb.rules.map (fun r => (r.ctor, r.nfields, r.rhs)) + | .inductInfo ia, .inductInfo ib => + ia.all == ib.all && ia.numParams == ib.numParams && + ia.numIndices == ib.numIndices && ia.ctors == ib.ctors && + ia.numNested == ib.numNested && ia.isRec == ib.isRec && + ia.isReflexive == ib.isReflexive && ia.isUnsafe == ib.isUnsafe + | .ctorInfo ca, .ctorInfo cb => + ca.induct == cb.induct && ca.cidx == cb.cidx && + ca.numParams == cb.numParams && ca.numFields == cb.numFields && + ca.isUnsafe == cb.isUnsafe + | _, _ => false + +def roseDeps : List Name := [``List, ``List.nil, ``List.cons] +def nvDeps : List Name := + [``Nat, ``Nat.zero, ``Nat.succ, ``PVec, ``PVec.nil, ``PVec.cons] + +def roseRestored : List Name := + [``RoseTree, ``RoseTree.node, mkRecName ``RoseTree, + (mkRecName ``RoseTree).appendIndexAfter 1] +def nvRestored : List Name := + [``NVTree, ``NVTree.node, mkRecName ``NVTree, + (mkRecName ``NVTree).appendIndexAfter 1] +def cuRestored : List Name := + [``CURose, ``CURose.node, mkRecName ``CURose, + (mkRecName ``CURose).appendIndexAfter 1] + +/-! ## P2: the port's nested path reproduces the stored metadata + +`Environment.addInductive`, run on a dependency-only kernel environment, +re-creates exactly the constants Lean stores — including every restored +type and rule RHS — and no auxiliary constant. The final output is +independent of auxiliary-name collisions: pre-seeding `_nested.List_1` +only shifts the uniquified internal names, which restoration erases. -/ + +open Elab in +run_meta do + let env ← getEnv + let checkPort (label : String) (main : Name) (lparams : List Name) (nparams : Nat) + (deps auxNames restored : List Name) (extra : ConstMap → ConstMap) : + MetaM Unit := do + let src := sourceType09A env main + let kenv := Kernel.Environment.ofConstants (`_spec09A ++ main) (extra (depMap09A env deps)) + match Ix.Theory.Named.Environment.addInductive kenv lparams nparams [src] false false with + | .error _ => throwError "{label}: port addInductive failed" + | .ok env' => + for n in restored do + let some stored := env.find? n | throwError "{label}: {n} not stored" + let some ported := env'.find? n | throwError "{label}: {n} missing from port output" + unless sameConst09A stored ported do + throwError "{label}: stored/ported metadata differ at {n}" + for n in auxNames do + unless (env'.find? n).isNone do + throwError "{label}: auxiliary constant {n} leaked into the final environment" + unless (env.find? n).isNone do + throwError "{label}: auxiliary constant {n} present in the ambient environment" + checkPort "rose" ``RoseTree [`u] 1 roseDeps + [roseAux, roseAux ++ `nil, roseAux ++ `cons, mkRecName roseAux, + (mkRecName ``RoseTree).appendIndexAfter 2] roseRestored id + checkPort "nv" ``NVTree [] 0 nvDeps + [nvAux, nvAux ++ `nil, nvAux ++ `cons, mkRecName nvAux, + (mkRecName ``NVTree).appendIndexAfter 2] nvRestored id + checkPort "cu" ``CURose [] 0 roseDeps + [roseAux, mkRecName roseAux] cuRestored id + -- auxiliary-name-collision independence + checkPort "rose-collision" ``RoseTree [`u] 1 roseDeps + [(`_nested ++ ``List).appendIndexAfter 2] roseRestored + (fun m => m.insert roseAux (env.find? ``Nat).get!) + +/-! ## P3: exact flattening pins + +The flattened blocks, translated to binder-erased `VExpr` form. These are +the descriptors the Spec-09B transformation must produce. -/ + +def roseFlatFamilies : List (Name × VExpr) := + [(``RoseTree, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))), + (roseAux, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0))))] + +def roseFlatCtors : List (Name × VExpr) := + [(``RoseTree.node, + .forallE (.sort (.succ (.param 0))) + (.forallE (.bvar 0) + (.forallE (.app (.const roseAux [.param 0]) (.bvar 1)) + (.app (.const ``RoseTree [.param 0]) (.bvar 2))))), + (roseAux ++ `nil, + .forallE (.sort (.succ (.param 0))) (.app (.const roseAux [.param 0]) (.bvar 0))), + (roseAux ++ `cons, + .forallE (.sort (.succ (.param 0))) + (.forallE (.app (.const ``RoseTree [.param 0]) (.bvar 0)) + (.forallE (.app (.const roseAux [.param 0]) (.bvar 1)) + (.app (.const roseAux [.param 0]) (.bvar 2)))))] + +/-- `aux2nested` for the rose tree: `List (RoseTree α)`, open over `α`. -/ +def roseAuxValue : VExpr := + .app (.const ``List [.param 0]) (.app (.const ``RoseTree [.param 0]) (.bvar 0)) + +def nvFlatFamilies : List (Name × VExpr) := + [(``NVTree, .sort (.succ .zero)), + (nvAux, .forallE (.const ``Nat []) (.sort (.succ .zero)))] + +def nvFlatCtors : List (Name × VExpr) := + [(``NVTree.node, + .forallE (.const ``Nat []) + (.forallE (.app (.const nvAux []) (.bvar 0)) (.const ``NVTree []))), + (nvAux ++ `nil, .app (.const nvAux []) (.const ``Nat.zero [])), + (nvAux ++ `cons, + .forallE (.const ``NVTree []) + (.forallE (.const ``Nat []) + (.forallE (.app (.const nvAux []) (.bvar 0)) + (.app (.const nvAux []) (.app (.const ``Nat.succ []) (.bvar 1))))))] + +/-- `aux2nested` for `NVTree`: the closed partial application `PVec NVTree` +(the index argument stays behind on each occurrence). -/ +def nvAuxValue : VExpr := .app (.const ``PVec []) (.const ``NVTree []) + +def cuFlatFamilies : List (Name × VExpr) := + [(``CURose, .sort (.succ (.succ .zero))), + (roseAux, .sort (.succ (.succ .zero)))] + +def cuFlatCtors : List (Name × VExpr) := + [(``CURose.node, .forallE (.const roseAux []) (.const ``CURose [])), + (roseAux ++ `nil, .const roseAux []), + (roseAux ++ `cons, + .forallE (.const ``CURose []) (.forallE (.const roseAux []) (.const roseAux [])))] + +/-- `aux2nested` for `CURose`: the block-level-free auxiliary constant +restores to `List` at the constant level `1`. -/ +def cuAuxValue : VExpr := .app (.const ``List [.succ .zero]) (.const ``CURose []) + +open Elab in +/-- Translate one flattened block and compare it with its pinned shape. -/ +def checkFlat09A (label : String) (main : Name) (lparams : List Name) (nparams : Nat) + (deps : List Name) (families ctors : List (Name × VExpr)) + (auxValues : List (Name × VExpr)) : MetaM (List VInductiveType) := do + let env ← getEnv + let src := sourceType09A env main + let kenv := Kernel.Environment.ofConstants (`_spec09AFlat ++ main) (depMap09A env deps) + let .ok (flatTypes, aux) := runElim09A kenv lparams nparams [src] + | throwError "{label}: flattening failed" + let uvars := lparams.length + let mut vtypes : List VInductiveType := [] + let mut actualFamilies : List (Name × VExpr) := [] + let mut actualCtors : List (Name × VExpr) := [] + for t in flatTypes do + let vty ← Ix.Theory.Named.Meta.ofExpr lparams {} t.type + actualFamilies := actualFamilies ++ [(t.name, vty)] + let mut vctors : List VConstVal := [] + for c in t.ctors do + let vc ← Ix.Theory.Named.Meta.ofExpr lparams {} c.type + actualCtors := actualCtors ++ [(c.name, vc)] + vctors := vctors ++ [{ name := c.name, uvars, type := vc }] + vtypes := vtypes ++ [{ name := t.name, uvars, type := vty, ctors := vctors }] + unless actualFamilies == families do + throwError "{label}: flattened families differ from the pinned shape" + unless actualCtors == ctors do + throwError "{label}: flattened constructors differ from the pinned shape" + let mut actualValues : List (Name × VExpr) := [] + for (n, e) in aux do + actualValues := actualValues ++ [(n, ← Ix.Theory.Named.Meta.ofExpr lparams {} e)] + unless actualValues == auxValues do + throwError "{label}: aux2nested values differ from the pinned shape" + return vtypes + +/-! ## P4: Theory viability, with acceptance behavior unchanged + +The flattened blocks are already inside the supported arbitrary-block +class, while the source declarations remain rejected by every current +analyzer and by the public transaction. -/ + +open Elab in +run_meta do + let checkViability (label : String) (main : Name) (lparams : List Name) + (nparams : Nat) (deps : List Name) (families ctors : List (Name × VExpr)) + (auxValues : List (Name × VExpr)) : MetaM Unit := do + let vtypes ← checkFlat09A label main lparams nparams deps families ctors auxValues + let uvars := lparams.length + let flatDecl : VInductDecl := { uvars, nparams, types := vtypes } + unless flatDecl.stage3 do + throwError "{label}: flattened block rejected by the block analyzer" + unless flatDecl.identityBlockGeneration?.isSome do + throwError "{label}: flattened block is not generation-ready" + let env ← getEnv + let src := sourceType09A env main + let vsrcTy ← Ix.Theory.Named.Meta.ofExpr lparams {} src.type + let mut vctors : List VConstVal := [] + for c in src.ctors do + vctors := vctors ++ [{ name := c.name, uvars, type := ← Ix.Theory.Named.Meta.ofExpr lparams {} c.type }] + let srcTy : VInductiveType := { name := main, uvars, type := vsrcTy, ctors := vctors } + let srcDecl : VInductDecl := { uvars, nparams, types := [srcTy] } + if srcDecl.stage3 then + throwError "{label}: source declaration unexpectedly accepted by stage3" + if srcDecl.checked?.isSome then + throwError "{label}: source declaration unexpectedly accepted by checked?" + if (VEnv.empty.addInduct srcDecl).isSome then + throwError "{label}: source declaration unexpectedly accepted by addInduct" + checkViability "rose" ``RoseTree [`u] 1 roseDeps + roseFlatFamilies roseFlatCtors [(roseAux, roseAuxValue)] + checkViability "nv" ``NVTree [] 0 nvDeps + nvFlatFamilies nvFlatCtors [(nvAux, nvAuxValue)] + checkViability "cu" ``CURose [] 0 roseDeps + cuFlatFamilies cuFlatCtors [(roseAux, cuAuxValue)] + +/-! ## P5: the restoration substitution σ + +`restoreV09A` mirrors `ElimNestedInductive.Result.restoreNested` on +`VExpr`. It is probe-local: the Spec-09C artifact path must define the +total Theory version (with a simultaneous parameter substitution once +`nparams > 1` is in scope; the probe fixtures have `nparams ≤ 1`, where +iterated `VExpr.inst` coincides with it). -/ + +structure AuxSpec09A where + aux : Name + np : Nat + value : VExpr + recName : Name + +def instParams09A (value : VExpr) : List VExpr → VExpr + | [] => value + | [a] => value.inst a + | _ => panic! "the probe fixtures have nparams ≤ 1" + +def findCtorSpec09A (specs : List AuxSpec09A) (c : Name) : Option (AuxSpec09A × Name) := + specs.findSome? fun spec => + if spec.aux.isPrefixOf c && c != spec.aux then + some (spec, c.replacePrefix spec.aux .anonymous) + else none + +/-- σ. The recursor-rename case is checked before the constructor-prefix +case, exactly like `restoreNested`'s `auxRec` map: an auxiliary recursor +name is prefixed by its auxiliary family name and would otherwise be +mangled by the constructor branch. -/ +partial def restoreV09A (specs : List AuxSpec09A) (recMap : List (Name × Name)) : + VExpr → VExpr + | .bvar i => .bvar i + | .sort l => .sort l + | .lam ty body => .lam (restoreV09A specs recMap ty) (restoreV09A specs recMap body) + | .forallE ty body => + .forallE (restoreV09A specs recMap ty) (restoreV09A specs recMap body) + | e@(.app ..) => restoreSpine (VExpr.appHead e) (e.appArgs []) + | e@(.const ..) => restoreSpine e [] + where + restoreSpine (head : VExpr) (args : List VExpr) : VExpr := + let args' := args.map (restoreV09A specs recMap) + match head with + | .const c ls => + match recMap.find? (·.1 == c) with + | some (_, newName) => (VExpr.const newName ls).appN args' + | none => + match specs.find? (·.aux == c) with + | some spec => + (instParams09A spec.value (args'.take spec.np)).appN (args'.drop spec.np) + | none => + match findCtorSpec09A specs c with + | some (spec, suffix) => + let value := instParams09A spec.value (args'.take spec.np) + match VExpr.appHead value with + | .const iname ils => + (VExpr.const (iname ++ suffix) ils).appN + (value.appArgs [] ++ args'.drop spec.np) + | _ => panic! "auxiliary value head is not a constant" + | none => (VExpr.const c ls).appN args' + | h => (restoreV09A specs recMap h).appN args' + +open Elab in +/-- σ over the flattened block's existing generation artifacts reproduces +the stored kernel metadata exactly: recursor names and types, and every +rule RHS in the globally flattened order, with no auxiliary constant in +the image. Constructor types are restored with the declaration-world +value; recursor artifacts use the value spliced by the elimination +offset. -/ +def checkRestore09A (label : String) (main : Name) (lparams : List Name) + (nparams : Nat) (deps : List Name) : MetaM Unit := do + let env ← getEnv + let src := sourceType09A env main + let kenv := Kernel.Environment.ofConstants (`_spec09ARestore ++ main) (depMap09A env deps) + let .ok (flatTypes, aux) := runElim09A kenv lparams nparams [src] + | throwError "{label}: flattening failed" + let uvars := lparams.length + let mut vtypes : List VInductiveType := [] + for t in flatTypes do + let vty ← Ix.Theory.Named.Meta.ofExpr lparams {} t.type + let mut vctors : List VConstVal := [] + for c in t.ctors do + vctors := vctors ++ [{ name := c.name, uvars, type := ← Ix.Theory.Named.Meta.ofExpr lparams {} c.type }] + vtypes := vtypes ++ [{ name := t.name, uvars, type := vty, ctors := vctors }] + let flatDecl : VInductDecl := { uvars, nparams, types := vtypes } + let some gen := flatDecl.identityBlockGeneration? + | throwError "{label}: flattened block is not generation-ready" + let elimOffset := gen.recUvars - uvars + let mut declSpecs : List AuxSpec09A := [] + let mut recSpecs : List AuxSpec09A := [] + let mut recMap : List (Name × Name) := [] + let mut i := 1 + for t in flatTypes.drop 1 do + let some (_, value) := aux.find? (·.1 == t.name) + | throwError "{label}: no aux2nested value for {t.name}" + let v ← Ix.Theory.Named.Meta.ofExpr lparams {} value + let recName := (mkRecName main).appendIndexAfter i + let recValue := v.instL (VLevel.params' uvars elimOffset) + declSpecs := declSpecs ++ [{ aux := t.name, np := nparams, value := v, recName }] + recSpecs := recSpecs ++ [{ aux := t.name, np := nparams, value := recValue, recName }] + recMap := recMap ++ [(mkRecName t.name, recName)] + i := i + 1 + let auxConsts := declSpecs.map (·.aux) ++ recMap.map (·.1) ++ + (flatTypes.drop 1).flatMap (fun t => t.ctors.map (·.name)) + -- declaration-world σ: restored source constructors + for (t, vt) in flatTypes.zip vtypes do + if t.name == main then + for c in vt.ctors do + let some stored := env.find? c.name | throwError "{label}: {c.name} not stored" + let storedType ← Ix.Theory.Named.Meta.ofExpr stored.levelParams {} + (← Ix.Theory.Named.Meta.expandExpr stored.type) + unless restoreV09A declSpecs recMap c.type == storedType do + throwError "{label}: σ(flattened {c.name}) differs from the stored type" + -- recursor-world σ: recursor types, names, and every rule RHS + let expectedNames := [mkRecName main] ++ recSpecs.map (·.recName) + for (r, expected) in gen.recursors.zip expectedNames do + let restoredName := match recMap.find? (·.1 == r.name) with + | some (_, n) => n + | none => r.name + unless restoredName == expected do + throwError "{label}: restored recursor name {restoredName}, expected {expected}" + let some (.recInfo stored) := env.find? expected + | throwError "{label}: stored recursor {expected} missing" + let storedType ← Ix.Theory.Named.Meta.ofExpr stored.levelParams {} + (← Ix.Theory.Named.Meta.expandExpr stored.type) + let restored := restoreV09A recSpecs recMap r.type + unless restored == storedType do + throwError "{label}: σ(recursor type) differs from stored for {expected}" + unless !VExpr.hasAnyConst auxConsts restored do + throwError "{label}: auxiliary constant survives σ in the type of {expected}" + let mut storedRules : List (Name × Expr) := [] + for n in expectedNames do + let some (.recInfo stored) := env.find? n + | throwError "{label}: stored recursor {n} missing" + for rule in stored.rules do + storedRules := storedRules ++ [(rule.ctor, rule.rhs)] + let genRules := gen.generatedRules + unless storedRules.length == genRules.length do + throwError "{label}: {genRules.length} generated rules, {storedRules.length} stored" + let some (.recInfo mainRec) := env.find? (mkRecName main) + | throwError "{label}: stored main recursor missing" + for (df, (ctor, storedRhs)) in genRules.zip storedRules do + let storedRhs ← Ix.Theory.Named.Meta.ofExpr mainRec.levelParams {} + (← Ix.Theory.Named.Meta.expandExpr storedRhs) + let restoredRhs := restoreV09A recSpecs recMap df.rhs + unless restoredRhs == storedRhs do + throwError "{label}: σ(rule rhs) differs from stored for {ctor}" + unless !VExpr.hasAnyConst auxConsts restoredRhs && + !VExpr.hasAnyConst auxConsts (restoreV09A recSpecs recMap df.lhs) do + throwError "{label}: auxiliary constant survives σ in the rule for {ctor}" + +run_meta do + checkRestore09A "rose" ``RoseTree [`u] 1 roseDeps + checkRestore09A "nv" ``NVTree [] 0 nvDeps + checkRestore09A "cu" ``CURose [] 0 roseDeps + +end Ix.Theory.Named.NestedRepresentation diff --git a/Ix/Theory/Named/Verify/Environment/NestedTransformation.lean b/Ix/Theory/Named/Verify/Environment/NestedTransformation.lean new file mode 100644 index 000000000..d32c26af8 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/NestedTransformation.lean @@ -0,0 +1,389 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.Environment.NestedRepresentation +import Ix.Theory.Named.NestedInductiveFixtures + +/-! +# Nested flattening differential (Spec-09B) + +Ties the Theory transformation `nestedElimination?` to the implementation: + +- the hand-written `List` target block in the Theory fixtures is exactly + Lean's stored metadata; +- on the real rose-tree, nested-indexed, and constant-universe fixtures, + the Theory flattening reproduces the port's `ElimNestedInductive` output + family for family, constructor for constructor, and specification for + `aux2nested` binding — including the canonical auxiliary names — and its + auxiliary count equals the stored `numNested`; +- Theory acceptance (`nestedStage3`) agrees with kernel acceptance on the + positives and on the nearest rejections: a parametric argument touching + a constructor-local binder (rejected by flattening itself, with the + kernel's exact error), an off-spine parametric application (rejected by + the unchanged block analyzer where the kernel fails constructor + checking), an in-block collision with the canonical auxiliary name + (rejected by `blockNamesOK` where the kernel's `checkName` rejects the + duplicate insertion), and a missing target declaration. +-/ + +namespace Ix.Theory.Named.NestedTransformation + +open Lean +open Ix.Theory.Named.NestedRepresentation +open Ix.Theory.Named.NestedInductiveFixtures +open VInductDecl + +/-! ## The hand-written `List` target is the stored metadata -/ + +def listNilStoredType : VExpr := nestedConstVType09A% List.nil +def listConsStoredType : VExpr := nestedConstVType09A% List.cons +def listStoredType : VExpr := nestedConstVType09A% List + +#guard listTarget.families.map (·.name) == [``List] +#guard listTarget.nparams == 1 +#guard listTarget.families.map (·.type) == [listStoredType] +#guard listTarget.families.map (·.ctors.map fun c => (c.name, c.uvars, c.type)) == + [[(``List.nil, 1, listNilStoredType), (``List.cons, 1, listConsStoredType)]] + +/-! ## Real-metadata target blocks -/ + +def pvecStoredTarget : NestedTargetBlock where + nparams := 1 + families := + [{ name := ``PVec + uvars := 0 + type := nestedConstVType09A% PVec + ctors := + [⟨⟨0, nestedConstVType09A% PVec.nil⟩, ``PVec.nil⟩, + ⟨⟨0, nestedConstVType09A% PVec.cons⟩, ``PVec.cons⟩] }] + +/-! ## Shared translation plumbing -/ + +open Elab in +/-- Translate a list of kernel `InductiveType`s into a `VInductDecl`. -/ +def toVInductDecl09B (lparams : List Name) (nparams : Nat) + (types : List InductiveType) : MetaM VInductDecl := do + let uvars := lparams.length + let mut vtypes : List VInductiveType := [] + for t in types do + let vty ← Ix.Theory.Named.Meta.ofExpr lparams {} t.type + let mut vctors : List VConstVal := [] + for c in t.ctors do + vctors := vctors ++ [⟨⟨uvars, ← Ix.Theory.Named.Meta.ofExpr lparams {} c.type⟩, c.name⟩] + vtypes := vtypes ++ [{ name := t.name, uvars, type := vty, ctors := vctors }] + return { uvars, nparams, types := vtypes } + +open Elab in +/-- Check that the Theory flattening of one real source declaration equals +the port's flattening, that its specifications are the translated +`aux2nested` bindings, and that its auxiliary count is the stored +`numNested`. -/ +def checkFlattenParity (label : String) (main : Name) (lparams : List Name) + (nparams : Nat) (deps : List Name) (targets : List NestedTargetBlock) : + MetaM Unit := do + let env ← getEnv + let src := sourceType09A env main + let kenv := Kernel.Environment.ofConstants (`_spec09B ++ main) (depMap09A env deps) + let .ok (flatTypes, aux) := runElim09A kenv lparams nparams [src] + | throwError "{label}: port flattening failed" + let sourceV ← toVInductDecl09B lparams nparams [src] + let portFlatV ← toVInductDecl09B lparams nparams flatTypes + let some elim := nestedElimination? targets sourceV + | throwError "{label}: Theory flattening failed" + unless elim.flat == portFlatV do + throwError "{label}: Theory flattened block differs from the port's" + unless elim.specs.length == aux.length do + throwError "{label}: {elim.specs.length} specs vs {aux.length} aux2nested bindings" + for spec in elim.specs do + let some (_, value) := aux.find? (·.1 == spec.aux) + | throwError "{label}: no aux2nested binding for {spec.aux}" + let valueV ← Ix.Theory.Named.Meta.ofExpr lparams {} value + unless spec.value == valueV do + throwError "{label}: spec value for {spec.aux} differs from aux2nested" + let .const target ls := VExpr.appHead valueV + | throwError "{label}: aux2nested head is not a constant" + unless spec.target == target && spec.levels == ls && + spec.values == valueV.appArgs [] do + throwError "{label}: spec decomposition differs for {spec.aux}" + let some (.inductInfo stored) := env.find? main + | throwError "{label}: stored inductive missing" + unless elim.numNested == stored.numNested do + throwError "{label}: numNested {elim.numNested} vs stored {stored.numNested}" + unless nestedStage3 targets sourceV do + throwError "{label}: Theory acceptance rejected an accepted declaration" + +run_meta do + checkFlattenParity "rose" ``RoseTree [`u] 1 roseDeps [listTarget] + checkFlattenParity "nv" ``NVTree [] 0 nvDeps [pvecStoredTarget] + checkFlattenParity "cu" ``CURose [] 0 roseDeps [listTarget] + +/-! ## Rejection differentials + +Each negative is written once at the kernel `Expr` level and once as a +`VInductDecl`; the kernel run and the Theory gate must both reject. -/ + +def natDeps09B (env : Environment) : ConstMap := + depMap09A env [``Nat, ``Nat.zero, ``Nat.succ, ``List, ``List.nil, ``List.cons] + +/-- `inductive Loose0 | node : (n : Nat) → List (Loose0 n) → Loose0` — the +parametric argument mentions the constructor-local `n`. -/ +def looseDecl : Declaration := + .inductDecl [] 0 + [{ name := `Loose0 + type := .sort 1 + ctors := [{ + name := `Loose0.node + type := .forallE `n (.const ``Nat []) + (.forallE `t + (mkApp (mkConst ``List [.zero]) (.app (.const `Loose0 []) (.bvar 0))) + (.const `Loose0 []) .default) .default }] }] + false + +def looseSourceV : VInductDecl where + uvars := 0 + nparams := 0 + types := + [{ name := `Loose0 + uvars := 0 + type := .sort (.succ .zero) + ctors := + [⟨⟨0, .forallE (.const ``Nat []) + (.forallE (.app (.const ``List [.zero]) + (.app (.const `Loose0 []) (.bvar 0))) + (.const `Loose0 []))⟩, `Loose0.node⟩] }] + +/-- `inductive Bad0N | node : Bad0N → List (Bad0N Nat.zero) → Bad0N` — the +parametric argument applies a block family off the parameter spine. -/ +def badAppDecl : Declaration := + .inductDecl [] 0 + [{ name := `Bad0N + type := .sort 1 + ctors := [{ + name := `Bad0N.node + type := .forallE `x (.const `Bad0N []) + (.forallE `t + (mkApp (mkConst ``List [.zero]) + (.app (.const `Bad0N []) (.const ``Nat.zero []))) + (.const `Bad0N []) .default) .default }] }] + false + +def badAppSourceV : VInductDecl where + uvars := 0 + nparams := 0 + types := + [{ name := `Bad0N + uvars := 0 + type := .sort (.succ .zero) + ctors := + [⟨⟨0, .forallE (.const `Bad0N []) + (.forallE (.app (.const ``List [.zero]) + (.app (.const `Bad0N []) (.const ``Nat.zero []))) + (.const `Bad0N []))⟩, `Bad0N.node⟩] }] + +/-- A two-family source whose second family occupies the canonical first +auxiliary name `_nested.List_1`. -/ +def collisionDecl : Declaration := + let rose := fun a => mkApp (mkConst `Rose0 [.param `u]) a + .inductDecl [`u] 1 + [{ name := `Rose0 + type := .forallE `α (.sort (.succ (.param `u))) (.sort (.succ (.param `u))) .default + ctors := [{ + name := `Rose0.node + type := .forallE `α (.sort (.succ (.param `u))) + (.forallE `t (mkApp (mkConst ``List [.param `u]) (rose (.bvar 0))) + (rose (.bvar 1)) .default) .default }] }, + { name := (`_nested ++ ``List).appendIndexAfter 1 + type := .forallE `α (.sort (.succ (.param `u))) (.sort (.succ (.param `u))) .default + ctors := [] }] + false + +def collisionSourceV : VInductDecl where + uvars := 1 + nparams := 1 + types := + [{ name := `Rose0 + uvars := 1 + type := .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0))) + ctors := + [⟨⟨1, .forallE (.sort (.succ (.param 0))) + (.forallE (.bvar 0) + (.forallE (.app (.const ``List [.param 0]) + (.app (.const `Rose0 [.param 0]) (.bvar 1))) + (.app (.const `Rose0 [.param 0]) (.bvar 2))))⟩, `Rose0.node⟩] }, + { name := (`_nested ++ ``List).appendIndexAfter 1 + uvars := 1 + type := .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0))) + ctors := [] }] + +open Elab in +run_meta do + let env ← getEnv + let deps := natDeps09B env + let kenv := Kernel.Environment.ofConstants `_spec09BNeg deps + -- the loose parametric argument rejects in flattening, with the kernel's + -- exact diagnostic + match Ix.Theory.Named.addDecl kenv looseDecl with + | .ok _ => throwError "loose: kernel accepted a local-variable parametric argument" + | .error (.other msg) => + unless msg == "invalid nested inductive datatype 'List', \ + nested inductive datatypes parameters cannot contain local variables." do + throwError "loose: unexpected kernel diagnostic {msg}" + | .error _ => throwError "loose: unexpected kernel error shape" + unless (nestedElimination? [listTarget] looseSourceV).isNone do + throwError "loose: Theory flattening accepted" + unless !nestedStage3 [listTarget] looseSourceV do + throwError "loose: Theory gate accepted" + -- the off-spine parametric application flattens but fails checking + match Ix.Theory.Named.addDecl kenv badAppDecl with + | .ok _ => throwError "badApp: kernel accepted an off-spine parametric application" + | .error _ => pure () + unless (nestedElimination? [listTarget] badAppSourceV).isSome do + throwError "badApp: Theory flattening should succeed" + unless !nestedStage3 [listTarget] badAppSourceV do + throwError "badApp: Theory gate accepted" + -- the canonical-name collision rejects at insertion (kernel) and at + -- `blockNamesOK` (Theory) + match Ix.Theory.Named.addDecl kenv collisionDecl with + | .ok _ => throwError "collision: kernel accepted a duplicate auxiliary name" + | .error _ => pure () + unless (nestedElimination? [listTarget] collisionSourceV).isSome do + throwError "collision: Theory flattening should succeed" + unless !nestedStage3 [listTarget] collisionSourceV do + throwError "collision: Theory gate accepted" + -- a missing target declaration rejects on both sides + let kenvNoList := Kernel.Environment.ofConstants `_spec09BNoList + (depMap09A env [``Nat, ``Nat.zero, ``Nat.succ]) + let roseSrc := sourceType09A env ``RoseTree + match Ix.Theory.Named.Environment.addInductive kenvNoList [`u] 1 [roseSrc] false false with + | .ok _ => throwError "noTarget: kernel accepted without the List declaration" + | .error _ => pure () + let roseV ← toVInductDecl09B [`u] 1 [roseSrc] + unless !nestedStage3 [] roseV do + throwError "noTarget: Theory gate accepted without target metadata" + +/-! ## Restoration parity (Spec-09C) + +The Theory restoration over the flattened block's generation artifacts +reproduces Lean's stored metadata exactly: every restored recursor name, +universe count, and type, and every rule RHS in the globally flattened +order, on all three real fixtures. This runs the product σ +(`NestedBlockChecked.recursors`/`generatedRules`), not the Spec-09A design +probe. -/ + +open Elab in +def checkRestoreParity (label : String) (main : Name) (lparams : List Name) + (nparams : Nat) (targets : List NestedTargetBlock) : MetaM Unit := do + let env ← getEnv + let src := sourceType09A env main + let sourceV ← toVInductDecl09B lparams nparams [src] + let some nested := nestedBlockChecked? targets sourceV + | throwError "{label}: nested acceptance failed" + let expectedNames := [mkRecName main] ++ + nested.elim.specs.mapIdx fun i _ => (mkRecName main).appendIndexAfter (i + 1) + unless nested.recursors.length == expectedNames.length do + throwError "{label}: {nested.recursors.length} restored recursors, \ + expected {expectedNames.length}" + for (r, expected) in nested.recursors.zip expectedNames do + unless r.name == expected do + throwError "{label}: restored recursor name {r.name}, expected {expected}" + let some (.recInfo stored) := env.find? expected + | throwError "{label}: stored recursor {expected} missing" + unless r.uvars == stored.levelParams.length do + throwError "{label}: recursor universe count differs for {expected}" + let storedType ← Ix.Theory.Named.Meta.ofExpr stored.levelParams {} + (← Ix.Theory.Named.Meta.expandExpr stored.type) + unless r.type == storedType do + throwError "{label}: restored recursor type differs from stored for {expected}" + let mut storedRules : List (Name × Expr) := [] + for n in expectedNames do + let some (.recInfo stored) := env.find? n + | throwError "{label}: stored recursor {n} missing" + for rule in stored.rules do + storedRules := storedRules ++ [(rule.ctor, rule.rhs)] + let rules := nested.generatedRules + unless rules.length == storedRules.length do + throwError "{label}: {rules.length} restored rules, stored {storedRules.length}" + let some (.recInfo mainRec) := env.find? (mkRecName main) + | throwError "{label}: stored main recursor missing" + for (df, (ctor, storedRhs)) in rules.zip storedRules do + let storedRhsV ← Ix.Theory.Named.Meta.ofExpr mainRec.levelParams {} + (← Ix.Theory.Named.Meta.expandExpr storedRhs) + unless df.rhs == storedRhsV do + throwError "{label}: restored rule RHS differs from stored for {ctor}" + +run_meta do + checkRestoreParity "rose" ``RoseTree [`u] 1 [listTarget] + checkRestoreParity "nv" ``NVTree [] 0 [pvecStoredTarget] + checkRestoreParity "cu" ``CURose [] 0 [listTarget] + +/-! ## Real-output round-trip (Spec-09C) + +Run the port's complete `Environment.addInductive` on a dependency-only +kernel environment and compare its entire output — not the ambient +elaborator metadata — against the Theory nested artifacts: the stored +payload against the source constants, and every emitted recursor's name, +universe count, type, rule constructors, rule field counts, and rule RHSs +against the restored inventory. Nothing in this comparison is +hand-authored: the left side is real `Inductive.Add.run`-derived output +and the right side is computed by `nestedBlockChecked?`. -/ + +open Elab in +def checkOutputRoundTrip (label : String) (main : Name) (lparams : List Name) + (nparams : Nat) (deps : List Name) (targets : List NestedTargetBlock) : + MetaM Unit := do + let env ← getEnv + let src := sourceType09A env main + let kenv := Kernel.Environment.ofConstants (`_spec09C ++ main) (depMap09A env deps) + let .ok kout := Ix.Theory.Named.Environment.addInductive kenv lparams nparams [src] false false + | throwError "{label}: port addInductive failed" + let sourceV ← toVInductDecl09B lparams nparams [src] + let some nested := nestedBlockChecked? targets sourceV + | throwError "{label}: nested acceptance failed" + -- the stored payload: families and constructors + for tyV in sourceV.types do + let some (.inductInfo out) := kout.find? tyV.name + | throwError "{label}: output family {tyV.name} missing" + let outType ← Ix.Theory.Named.Meta.ofExpr out.levelParams {} (← Ix.Theory.Named.Meta.expandExpr out.type) + unless out.levelParams.length == tyV.uvars && outType == tyV.type do + throwError "{label}: output family metadata differs for {tyV.name}" + unless out.numNested == nested.elim.numNested do + throwError "{label}: output numNested {out.numNested} vs \ + artifact {nested.elim.numNested}" + for cV in tyV.ctors do + let some (.ctorInfo outC) := kout.find? cV.name + | throwError "{label}: output constructor {cV.name} missing" + let outCType ← Ix.Theory.Named.Meta.ofExpr outC.levelParams {} + (← Ix.Theory.Named.Meta.expandExpr outC.type) + unless outC.levelParams.length == cV.uvars && outCType == cV.type do + throwError "{label}: output constructor metadata differs for {cV.name}" + -- the restored recursors and their rules, in inventory order + let mut ruleIdx := 0 + let rules := nested.generatedRules + for r in nested.recursors do + let some (.recInfo out) := kout.find? r.name + | throwError "{label}: output recursor {r.name} missing" + let outType ← Ix.Theory.Named.Meta.ofExpr out.levelParams {} (← Ix.Theory.Named.Meta.expandExpr out.type) + unless out.levelParams.length == r.uvars && outType == r.type do + throwError "{label}: output recursor metadata differs for {r.name}" + unless out.k == nested.generation.kTarget do + throwError "{label}: output recursor K flag differs for {r.name}" + for rule in out.rules do + let some df := rules[ruleIdx]? + | throwError "{label}: more output rules than restored rules" + let outRhs ← Ix.Theory.Named.Meta.ofExpr out.levelParams {} + (← Ix.Theory.Named.Meta.expandExpr rule.rhs) + unless outRhs == df.rhs do + throwError "{label}: output rule RHS differs for {rule.ctor}" + ruleIdx := ruleIdx + 1 + unless ruleIdx == rules.length do + throwError "{label}: {rules.length} restored rules, output consumed {ruleIdx}" + +run_meta do + checkOutputRoundTrip "rose" ``RoseTree [`u] 1 roseDeps [listTarget] + checkOutputRoundTrip "nv" ``NVTree [] 0 nvDeps [pvecStoredTarget] + checkOutputRoundTrip "cu" ``CURose [] 0 roseDeps [listTarget] + +end Ix.Theory.Named.NestedTransformation diff --git a/Ix/Theory/Named/Verify/Environment/Normalization.lean b/Ix/Theory/Named/Verify/Environment/Normalization.lean new file mode 100644 index 000000000..68a92acb1 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/Normalization.lean @@ -0,0 +1,6953 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Verify.TypeChecker +import Ix.Theory.Named.Reference.Inductive.ValidationTrace + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named +open Lean hiding Environment Exception +open Kernel + +namespace TypeChecker + +/-- Compatibility name for the consumer-neutral reflected-primitive list. -/ +@[deprecated Ix.Theory.Named.VEnv.reflectedPrimitiveNames (since := "2026-08-11")] +abbrev reflectedPrimitiveNames : List Name := + Ix.Theory.Named.VEnv.reflectedPrimitiveNames + +/-- Compatibility shim for the consumer-neutral Theory theorem. -/ +@[deprecated Ix.Theory.Named.VEnv.HasPrimitives.of_avoids (since := "2026-08-11")] +theorem VEnv.HasPrimitives.of_avoids + {env : VEnv} + (h : ∀ n ∈ reflectedPrimitiveNames, env.constants n = none) : + env.HasPrimitives := + Ix.Theory.Named.VEnv.HasPrimitives.of_avoids h + +/-- Compatibility shim for the consumer-neutral Theory theorem. -/ +@[deprecated Ix.Theory.Named.VEnv.addConst_other (since := "2026-08-11")] +theorem VEnv.addConst_other + {env env' : VEnv} {name other : Name} {ci : VConstant} + (hadd : env.addConst name ci = some env') + (hne : name ≠ other) : + env'.constants other = env.constants other := + Ix.Theory.Named.VEnv.addConst_other hadd hne + +/-- Compatibility shim for the consumer-neutral Theory theorem. -/ +@[deprecated Ix.Theory.Named.VEnv.HasPrimitives.addConst (since := "2026-08-11")] +theorem VEnv.HasPrimitives.addConst + {env env' : VEnv} {name : Name} {ci : VConstant} + (H : env.HasPrimitives) + (hname : name ∉ reflectedPrimitiveNames) + (hadd : env.addConst name ci = some env') : + env'.HasPrimitives := + Ix.Theory.Named.VEnv.HasPrimitives.addConst H hname hadd + +/-- A verified implementation local context remains verified when the Theory +environment grows. Kernel local declarations and their free-variable names +are unchanged; only their translations and typing derivations are transported +monotonically. -/ +theorem MLCtx.WF.mono + {env env' : VEnv} (henv : env ≤ env') : + ∀ {context : MLCtx} {Us : List Name}, + context.WF env Us → context.WF env' Us + | .nil, _, _ => trivial + | .vlam _ _ _ _ _ _, _, + ⟨tailWF, fresh, type_tr, typeWF⟩ => + ⟨tailWF.mono henv, fresh, type_tr.mono henv, typeWF.mono henv⟩ + | .vlet _ _ _ _ _ _ _, _, + ⟨tailWF, fresh, type_tr, value_tr, valueWF⟩ => + ⟨tailWF.mono henv, fresh, type_tr.mono henv, + value_tr.mono henv, valueWF.mono henv⟩ + +/-- Kernel-side counterpart of `VEnv.HasPrimitives.of_avoids`: if an isolated +constant map contains no hard-coded primitive name, the safety premise needed +by `VContext` is vacuous. -/ +theorem safePrimitives_of_avoids + {env : Environment} {n : Name} {ci : ConstantInfo} + (h : ∀ n, Environment.primitives.contains n → env.find? n = none) : + env.find? n = some ci → + Environment.primitives.contains n → + ci.safety = .safe ∧ ci.levelParams = [] := by + intro hfind hprim + rw [h n hprim] at hfind + contradiction + +/-- Staging one fresh non-primitive inductive family preserves the kernel-side +primitive safety contract. All old lookups are inherited from the input map; +the only new lookup cannot be primitive by hypothesis. -/ +theorem AddInductConstant.safePrimitives + {pre post : Environment} {env typeEnv : VEnv} {raw : VConstVal} + (stage : AddInductConstant .induct pre.constants env raw + post.constants typeEnv) + (preMapWF : pre.constants.WF) + (H : pre.find? n = some ci → + Environment.primitives.contains n → + ci.safety = .safe ∧ ci.levelParams = []) + (hname : Environment.primitives.contains raw.name = false) : + post.find? n = some ci → + Environment.primitives.contains n → + ci.safety = .safe ∧ ci.levelParams = [] := by + intro hfind hprim + have postMapWF := stage.map_wf preMapWF + change post.constants.find?' n = some ci at hfind + rw [postMapWF.find?'_eq_find?, stage.map_add, + preMapWF.find?_insert] at hfind + split at hfind + · rename_i heq + have : raw.name = n := by simpa using heq + subst n + rw [hname] at hprim + contradiction + · apply H + change pre.constants.find?' n = some ci + rw [preMapWF.find?'_eq_find?] + exact hfind + exact hprim + +/-- Evidence that Verify's recursive WHNF procedure returned an exact kernel +expression, together with strict translations of the input and output into one +Theory context. + +The result is not trusted merely because it is supplied by a caller: +`run_eq` records the concrete checker execution, while `rhs_tr` identifies its +Theory meaning. -/ +structure WhnfRun (env : VEnv) (Us : List Name) (Δ : VLCtx) + (lhs rhs : Expr) (lhs' rhs' : VExpr) where + context : VContext + venv_eq : context.venv = env + lparams_eq : context.lparams = Us + vlctx_eq : context.vlctx = Δ + state_wf : VState.WF context {} + lhs_tr : TrExprS env Us Δ lhs lhs' + rhs_tr : TrExprS env Us Δ rhs rhs' + recursionFuel : Nat + run_eq : ∃ state : State, + Inner.whnf' lhs (Methods.withFuel recursionFuel) + context.toContext ({} : State) = .ok (rhs, state) + +/-- Turn one operationally certified candidate step into the existing +state-bearing Verify certificate once the caller supplies the strict +kernel/Theory translations and the corresponding verified context. + +The adapter recovers the final checker state from the stored `M.run` equality; +it does not rerun normalization, choose a result, or assert a semantic +equality. -/ +def WhnfRun.ofCandidateStep + (step : AddInductive.CandidateWhnfStep) + (hvalid : step.Valid) + (context : VContext) + (context_eq : + context.toContext = step.context.toTypeChecker) + (venv_eq : context.venv = env) + (lparams_eq : context.lparams = Us) + (vlctx_eq : context.vlctx = Δ) + (state_wf : VState.WF context {}) + (lhs_tr : TrExprS env Us Δ step.source lhs') + (rhs_tr : TrExprS env Us Δ step.result rhs') + (recursionFuel : Nat) + (hdepth : step.context.fuel.recDepth = recursionFuel + 1) : + WhnfRun env Us Δ step.source step.result lhs' rhs' where + context := context + venv_eq := venv_eq + lparams_eq := lparams_eq + vlctx_eq := vlctx_eq + state_wf := state_wf + lhs_tr := lhs_tr + rhs_tr := rhs_tr + recursionFuel := recursionFuel + run_eq := by + rw [context_eq] + exact step.innerRun recursionFuel hdepth hvalid + +/-- The strict input translation already supplies the Theory well-formedness +needed to type an exact WHNF equality. In particular, a certificate producer +does not need to borrow a typing fact from the normalized declaration it is +trying to construct. -/ +theorem WhnfRun.lhs_wf + (run : WhnfRun env Us Δ lhs rhs lhs' rhs') : + lhs'.WF env Us.length Δ.toCtx := by + have hlhs : run.context.TrExprS lhs lhs' := by + simpa only [VContext.TrExprS, run.venv_eq, run.lparams_eq, + run.vlctx_eq] using run.lhs_tr + have hwf := hlhs.wf run.context.Ewf.ordered run.context.Δwf + simpa only [run.venv_eq, run.lparams_eq, run.vlctx_eq] using hwf + +/-- An exact successful verified WHNF execution is an ordinary Theory +definitional equality. The proof consumes the existing checker-refinement +contract and uses representation uniqueness to identify the translated +result. -/ +theorem WhnfRun.isDefEqU + (run : WhnfRun env Us Δ lhs rhs lhs' rhs') : + env.IsDefEqU Us.length Δ.toCtx lhs' rhs' := by + have hlhs : run.context.TrExprS lhs lhs' := by + simpa only [VContext.TrExprS, run.venv_eq, run.lparams_eq, + run.vlctx_eq] using run.lhs_tr + have hrhs : run.context.TrExpr rhs rhs' := by + have strict : run.context.TrExprS rhs rhs' := by + simpa only [VContext.TrExprS, run.venv_eq, run.lparams_eq, + run.vlctx_eq] using run.rhs_tr + exact strict.trExpr run.context.Ewf run.context.Δwf + obtain ⟨state, hrun⟩ := run.run_eq + obtain ⟨_, _, _, _, _, htr⟩ := + (TypeChecker.Inner.whnf'.WF hlhs + (Methods.withFuel run.recursionFuel) Methods.withFuel.WF) + run.state_wf rhs state hrun + have hdefeq := + htr.uniq run.context.Ewf + (.refl run.context.Ewf run.context.Δwf) hrhs + simpa only [VContext.IsDefEqU, run.venv_eq, run.lparams_eq, + run.vlctx_eq] using hdefeq + +/-- Typed form of `WhnfRun.isDefEqU`. A known type for the input fixes the +otherwise existential type in the WHNF refinement result. -/ +theorem WhnfRun.isDefEq + (run : WhnfRun env Us Δ lhs rhs lhs' rhs') + (hlhs : env.HasType Us.length Δ.toCtx lhs' A) : + env.IsDefEq Us.length Δ.toCtx lhs' rhs' A := by + have henv : VEnv.WF env := by + simpa only [run.venv_eq] using run.context.Ewf + have hΔ : OnCtx Δ.toCtx (env.IsType Us.length) := by + simpa only [run.venv_eq, run.lparams_eq, run.vlctx_eq] using + run.context.Δwf.toCtx + exact run.isDefEqU.of_l henv hΔ hlhs + +/-- Evidence that Verify's full type-checking path inferred an exact kernel +type, with strict Theory translations of both the checked expression and the +returned type. + +`inferOnly := false` is important here: the recorded run checks the expression +rather than trusting a caller-provided `TrExprS` derivation as an inference +cache hit. -/ +structure CheckTypeRun (env : VEnv) (Us : List Name) (Δ : VLCtx) + (expr inferred : Expr) (expr' inferred' : VExpr) where + context : VContext + venv_eq : context.venv = env + lparams_eq : context.lparams = Us + vlctx_eq : context.vlctx = Δ + state_wf : VState.WF context {} + expr_tr : TrExprS env Us Δ expr expr' + inferred_tr : TrExprS env Us Δ inferred inferred' + recursionFuel : Nat + run_eq : ∃ state : State, + Inner.inferType expr false (Methods.withFuel recursionFuel) + context.toContext ({} : State) = .ok (inferred, state) + +/-- Candidate-step adapter for full checking. As with +`WhnfRun.ofCandidateStep`, the operational result and final state come from +the producer; this boundary only attaches an already verified context and +strict translations. -/ +def CheckTypeRun.ofCandidateStep + (step : AddInductive.CandidateCheckTypeStep) + (hvalid : step.Valid) + (context : VContext) + (context_eq : + context.toContext = step.context.toTypeChecker) + (venv_eq : context.venv = env) + (lparams_eq : context.lparams = Us) + (vlctx_eq : context.vlctx = Δ) + (state_wf : VState.WF context {}) + (expr_tr : TrExprS env Us Δ step.source expr') + (inferred_tr : TrExprS env Us Δ step.inferred inferred') + (recursionFuel : Nat) + (hdepth : step.context.fuel.recDepth = recursionFuel) : + CheckTypeRun env Us Δ step.source step.inferred expr' inferred' where + context := context + venv_eq := venv_eq + lparams_eq := lparams_eq + vlctx_eq := vlctx_eq + state_wf := state_wf + expr_tr := expr_tr + inferred_tr := inferred_tr + recursionFuel := recursionFuel + run_eq := by + rw [context_eq] + exact step.innerRun recursionFuel hdepth hvalid + +/-- Recover the strict Theory translations and typing judgment supplied by an +exact retained full-check observation. + +This is the proof-producing counterpart of `CheckTypeRun.ofCandidateStep` for +callers that do not yet have named translations. The only source-side premise +is the free-variable condition required by the verified checker refinement. -/ +theorem candidateCheckTypeStep_exists_translation + (step : AddInductive.CandidateCheckTypeStep) + (hvalid : step.Valid) + (context : VContext) + (context_eq : context.toContext = step.context.toTypeChecker) + (state_wf : VState.WF context {}) + (source_fvars : + step.source.FVarsIn (· ∈ context.vlctx.fvars)) + (recursionFuel : Nat) + (hdepth : step.context.fuel.recDepth = recursionFuel) : + ∃ source' inferred', + context.TrExprS step.source source' ∧ + context.TrExprS step.inferred inferred' ∧ + context.HasType source' inferred' := by + obtain ⟨state, run⟩ := + step.innerRun recursionFuel hdepth hvalid + rw [← context_eq] at run + obtain ⟨_, _, _, _, source', inferred', typing⟩ := + (Inner.checkType.WF source_fvars + (Methods.withFuel recursionFuel) Methods.withFuel.WF) + state_wf step.inferred state run + exact ⟨source', inferred', typing.2.1, typing.2.2.1, + typing.2.2.2⟩ + +/-- An exact successful `checkType` execution supplies the corresponding +Theory typing judgment. Translation uniqueness transports the verifier's +existential result to the precise translations named by the certificate. -/ +theorem CheckTypeRun.hasType + (run : CheckTypeRun env Us Δ expr inferred expr' inferred') : + env.HasType Us.length Δ.toCtx expr' inferred' := by + have hexpr : run.context.TrExprS expr expr' := by + simpa only [VContext.TrExprS, run.venv_eq, run.lparams_eq, + run.vlctx_eq] using run.expr_tr + have hinferred : run.context.TrExprS inferred inferred' := by + simpa only [VContext.TrExprS, run.venv_eq, run.lparams_eq, + run.vlctx_eq] using run.inferred_tr + obtain ⟨state, hrun⟩ := run.run_eq + obtain ⟨_, _, _, _, e, ty, htyping⟩ := + (TypeChecker.Inner.checkType.WF hexpr.fvarsIn + (Methods.withFuel run.recursionFuel) Methods.withFuel.WF) + run.state_wf inferred state hrun + rcases htyping with ⟨_, he, hty, hhasType⟩ + have heq := + he.uniq run.context.Ewf + (.refl run.context.Ewf run.context.Δwf) hexpr + have htyeq := + hty.uniq run.context.Ewf + (.refl run.context.Ewf run.context.Δwf) hinferred + have hout := + (hhasType.defeqU_l run.context.Ewf run.context.Δwf heq).defeqU_r + run.context.Ewf run.context.Δwf htyeq + simpa only [run.venv_eq, run.lparams_eq, run.vlctx_eq] using hout + +/-- A checked expression whose inferred type translates to a sort is a Theory +type. This is the insertion premise needed for raw family and constructor +constants. -/ +theorem CheckTypeRun.isType + (run : CheckTypeRun env Us Δ expr inferred expr' (.sort u)) : + env.IsType Us.length Δ.toCtx expr' := + ⟨u, run.hasType⟩ + +/-- If `checkType` returns a type expression whose own verified WHNF is a +sort, the checked expression is a Theory type. This is the common alias case: +the checker may infer a reducible type constant before `ensureSort` exposes its +sort WHNF. -/ +theorem CheckTypeRun.isType_of_whnf + (run : CheckTypeRun env Us Δ expr inferred expr' inferred') + (typeRun : WhnfRun env Us Δ inferred reduced inferred' (.sort u)) : + env.IsType Us.length Δ.toCtx expr' := by + have henv : VEnv.WF env := by + simpa only [run.venv_eq] using run.context.Ewf + have hΔ : OnCtx Δ.toCtx (env.IsType Us.length) := by + simpa only [run.venv_eq, run.lparams_eq, run.vlctx_eq] using + run.context.Δwf.toCtx + exact ⟨u, run.hasType.defeqU_r henv hΔ typeRun.isDefEqU⟩ + +/-- Evidence for one exact successful checker definitional-equality run, with +strict translations of both kernel endpoints in the same Theory context. -/ +structure IsDefEqRun (env : VEnv) (Us : List Name) (Δ : VLCtx) + (lhs rhs : Expr) (lhs' rhs' : VExpr) where + context : VContext + venv_eq : context.venv = env + lparams_eq : context.lparams = Us + vlctx_eq : context.vlctx = Δ + state_wf : VState.WF context {} + lhs_tr : TrExprS env Us Δ lhs lhs' + rhs_tr : TrExprS env Us Δ rhs rhs' + recursionFuel : Nat + run_eq : ∃ state : State, + Inner.isDefEq lhs rhs (Methods.withFuel recursionFuel) + context.toContext ({} : State) = .ok (true, state) + +/-- Convert the retained candidate equality observation to a state-bearing +Verify certificate. -/ +def IsDefEqRun.ofCandidateStep + (step : AddInductive.CandidateIsDefEqStep) + (hvalid : step.Valid) + (context : VContext) + (context_eq : context.toContext = step.context.toTypeChecker) + (venv_eq : context.venv = env) + (lparams_eq : context.lparams = Us) + (vlctx_eq : context.vlctx = Δ) + (state_wf : VState.WF context {}) + (lhs_tr : TrExprS env Us Δ step.lhs lhs') + (rhs_tr : TrExprS env Us Δ step.rhs rhs') + (recursionFuel : Nat) + (hdepth : step.context.fuel.recDepth = recursionFuel) : + IsDefEqRun env Us Δ step.lhs step.rhs lhs' rhs' where + context := context + venv_eq := venv_eq + lparams_eq := lparams_eq + vlctx_eq := vlctx_eq + state_wf := state_wf + lhs_tr := lhs_tr + rhs_tr := rhs_tr + recursionFuel := recursionFuel + run_eq := by + rw [context_eq] + exact step.innerRun recursionFuel hdepth hvalid + +/-- A successful verified equality run supplies ordinary Theory +definitional equality. -/ +theorem IsDefEqRun.isDefEqU + (run : IsDefEqRun env Us Δ lhs rhs lhs' rhs') : + env.IsDefEqU Us.length Δ.toCtx lhs' rhs' := by + have hlhs : run.context.TrExprS lhs lhs' := by + simpa only [VContext.TrExprS, run.venv_eq, run.lparams_eq, + run.vlctx_eq] using run.lhs_tr + have hrhs : run.context.TrExprS rhs rhs' := by + simpa only [VContext.TrExprS, run.venv_eq, run.lparams_eq, + run.vlctx_eq] using run.rhs_tr + obtain ⟨state, hrun⟩ := run.run_eq + obtain ⟨_, _, _, _, hdefeq⟩ := + (TypeChecker.Inner.isDefEq.WF hlhs hrhs + (Methods.withFuel run.recursionFuel) Methods.withFuel.WF) + run.state_wf true state hrun + simpa only [VContext.IsDefEqU, run.venv_eq, run.lparams_eq, + run.vlctx_eq] using hdefeq (by simp) + +/-- Consuming a certified annotation path cannot introduce a free variable or +level metavariable. -/ +theorem candidateTypeAnnotation_fvarsIn + (trace : AddInductive.CandidateTypeAnnotationTrace source consumed) + (h : source.FVarsIn fvars) : consumed.FVarsIn fvars := by + induction trace with + | identity => exact h + | outParam _ _ _ ih => exact ih h.2 + | semiOutParam _ _ _ ih => exact ih h.2 + | optParam _ _ _ _ ih => exact ih h.1.2 + | autoParam _ _ _ _ ih => exact ih h.1.2 + +/-- Extract a strict translation of the consumed annotation argument from the +strict translation of the raw wrapper application. -/ +theorem candidateTypeAnnotation_exists_translation + (trace : AddInductive.CandidateTypeAnnotationTrace source consumed) + (source_tr : TrExprS env Us Δ source source') : + ∃ consumed', TrExprS env Us Δ consumed consumed' := by + induction trace generalizing source' with + | identity => exact ⟨source', source_tr⟩ + | outParam _ _ _ ih => + let .app _ _ _ type_tr := source_tr + exact ih type_tr + | semiOutParam _ _ _ ih => + let .app _ _ _ type_tr := source_tr + exact ih type_tr + | optParam _ _ _ _ ih => + let .app _ _ fn_tr _ := source_tr + let .app _ _ _ type_tr := fn_tr + exact ih type_tr + | autoParam _ _ _ _ ih => + let .app _ _ fn_tr _ := source_tr + let .app _ _ _ type_tr := fn_tr + exact ih type_tr + +/-- The empty executable checker state is well formed for any verified +context whose free-variable names are already reserved by the kernel name +generator. `VState.WF.empty` is the empty-local-context specialization; +candidate normalization needs this slightly more general form after entering +raw Pi binders. -/ +theorem VState.WF.empty_of_reserves + (context : VContext) + (reserved : ∀ fv ∈ context.vlctx.fvars, + (({} : VState).ngen).Reserves fv) : + VState.WF context {} where + trctx := context.trlctx + ngen_wf := reserved + ectx := ⟨context.vlctx, .refl, context.Δwf, .refl, .empty, reserved⟩ + inferTypeI_wf := .empty + inferTypeC_wf := .empty + whnfCore_wf := .empty + whnf_wf := .empty + unfold_wf _ := by simp + +/-- Positional verified context for an executable normalization candidate. + +The equality pins every checker-visible field (environment, local context, +safety, level parameters, and fuel) to the `AddInductive.Context` retained by +the candidate trace. The state certificate is kept with it because every +retained full-check and WHNF observation starts from the empty checker state. -/ +structure CandidateContextRun + (candidateContext : AddInductive.Context) where + context : VContext + context_eq : context.toContext = candidateContext.toTypeChecker + state_wf : VState.WF context {} + namePrefix_ne : candidateContext.ngen.namePrefix ≠ + (({} : VState).ngen).namePrefix + +/-- Candidate binders and the kernel checker's own temporary names use +different prefixes, so every candidate binder is reserved by a freshly +initialized kernel checker state. -/ +theorem candidateFreshFVarId_reserved + (candidateContext : AddInductive.Context) + (namePrefix_ne : candidateContext.ngen.namePrefix ≠ + (({} : VState).ngen).namePrefix) : + (({} : VState).ngen).Reserves candidateContext.freshFVarId := by + simp [NameGenerator.Reserves, AddInductive.Context.freshFVarId] + intro i h + apply namePrefix_ne + simpa only [NameGenerator.curr, Name.getPrefix] using + congrArg Name.getPrefix h + +/-- Package an already verified checker context at a candidate position. -/ +def CandidateContextRun.ofVContext + (candidateContext : AddInductive.Context) + (context : VContext) + (context_eq : context.toContext = candidateContext.toTypeChecker) + (state_wf : VState.WF context {}) + (namePrefix_ne : candidateContext.ngen.namePrefix ≠ + (({} : VState).ngen).namePrefix) : + CandidateContextRun candidateContext := + ⟨context, context_eq, state_wf, namePrefix_ne⟩ + +/-- Construct the root certificate used by family and constructor candidates. +Their candidate traversal deliberately resets the local context to empty. -/ +def CandidateContextRun.root + {ves : VEnvs} (wf : ves.WF candidateContext.env) + (lctx_eq : candidateContext.lctx = {}) + (namePrefix_ne : candidateContext.ngen.namePrefix ≠ + (({} : VState).ngen).namePrefix) : + CandidateContextRun candidateContext := by + let context := VContext.mk' wf candidateContext.safety + candidateContext.lparams candidateContext.fuel + refine ⟨context, ?_, ?_, namePrefix_ne⟩ + · simp [context, VContext.mk', VContext.mk1, MLCtx.lctx, + AddInductive.Context.toTypeChecker, lctx_eq] + · exact VState.WF.empty + +@[simp] theorem CandidateContextRun.context_env + (run : CandidateContextRun candidateContext) : + run.context.env = candidateContext.env := by + have h := congrArg (fun c : TypeChecker.Context => c.env) run.context_eq + simpa only [AddInductive.Context.toTypeChecker] using h + +@[simp] theorem CandidateContextRun.context_lctx + (run : CandidateContextRun candidateContext) : + run.context.lctx = candidateContext.lctx := by + have h := congrArg (fun c : TypeChecker.Context => c.lctx) run.context_eq + simpa only [AddInductive.Context.toTypeChecker] using h + +@[simp] theorem CandidateContextRun.context_safety + (run : CandidateContextRun candidateContext) : + run.context.safety = candidateContext.safety := by + have h := congrArg (fun c : TypeChecker.Context => c.safety) run.context_eq + simpa only [AddInductive.Context.toTypeChecker] using h + +@[simp] theorem CandidateContextRun.context_lparams + (run : CandidateContextRun candidateContext) : + run.context.lparams = candidateContext.lparams := by + have h := congrArg (fun c : TypeChecker.Context => c.lparams) run.context_eq + simpa only [AddInductive.Context.toTypeChecker] using h + +@[simp] theorem CandidateContextRun.context_fuel + (run : CandidateContextRun candidateContext) : + run.context.fuel = candidateContext.fuel := by + have h := congrArg (fun c : TypeChecker.Context => c.fuel) run.context_eq + simpa only [AddInductive.Context.toTypeChecker] using h + +/-- Reset only the implementation and Theory local contexts while retaining +the exact environment, safety mode, level parameters, fuel, and name +generator owned by a candidate context. Constructor root `checkType` uses +precisely this context before the validation telescope re-enters the retained +family locals. -/ +def CandidateContextRun.withEmptyLocalContext + (run : CandidateContextRun candidateContext) : + CandidateContextRun candidateContext.withEmptyLocalContext := by + let context : VContext := + { run.context with + lctx := {} + mlctx := .nil + mlctx_wf := trivial + lctx_eq := rfl } + have context_eq : context.toContext = + candidateContext.withEmptyLocalContext.toTypeChecker := by + change { run.context.toContext with lctx := {} } = + candidateContext.withEmptyLocalContext.toTypeChecker + rw [run.context_eq] + rfl + refine ⟨context, context_eq, ?_, run.namePrefix_ne⟩ + exact VState.WF.empty_of_reserves context (by + intro fv hfv + change fv ∈ VLCtx.fvars ([] : VLCtx) at hfv + simp at hfv) + +/-- Package a retained full-check observation at an already named strict +Theory source. The verified execution still chooses the inferred Theory +type; the duplicate source translation returned by refinement is discarded, +not identified by syntactic equality. -/ +theorem CheckTypeRun.exists_ofCandidateStep + (step : AddInductive.CandidateCheckTypeStep) + (hvalid : step.Valid) + (contextRun : CandidateContextRun step.context) + (source' : VExpr) + (source_tr : contextRun.context.TrExprS step.source source') : + ∃ inferred', Nonempty + (CheckTypeRun contextRun.context.venv contextRun.context.lparams + contextRun.context.vlctx step.source step.inferred source' inferred') := by + obtain ⟨_, inferred', _, inferred_tr, _⟩ := + candidateCheckTypeStep_exists_translation step hvalid + contextRun.context contextRun.context_eq contextRun.state_wf + source_tr.fvarsIn step.context.fuel.recDepth rfl + exact ⟨inferred', ⟨CheckTypeRun.ofCandidateStep step hvalid + contextRun.context contextRun.context_eq rfl rfl rfl + contextRun.state_wf source_tr inferred_tr + step.context.fuel.recDepth rfl⟩⟩ + +/-- Full-check packaging when only the checker's syntactic free-variable +premise is known. Both strict Theory endpoints are then selected by the +verified refinement of the retained execution. -/ +theorem CheckTypeRun.exists_ofCandidateStepFVars + (step : AddInductive.CandidateCheckTypeStep) + (hvalid : step.Valid) + (contextRun : CandidateContextRun step.context) + (source_fvars : + step.source.FVarsIn (· ∈ contextRun.context.vlctx.fvars)) : + ∃ source' inferred', Nonempty + (CheckTypeRun contextRun.context.venv contextRun.context.lparams + contextRun.context.vlctx step.source step.inferred source' inferred') := by + obtain ⟨source', inferred', source_tr, inferred_tr, _⟩ := + candidateCheckTypeStep_exists_translation step hvalid + contextRun.context contextRun.context_eq contextRun.state_wf + source_fvars step.context.fuel.recDepth rfl + exact ⟨source', inferred', ⟨CheckTypeRun.ofCandidateStep step hvalid + contextRun.context contextRun.context_eq rfl rfl rfl + contextRun.state_wf source_tr inferred_tr + step.context.fuel.recDepth rfl⟩⟩ + +/-- Package one retained WHNF observation and keep the strict Theory +translation selected for its exact kernel result. -/ +theorem WhnfRun.exists_ofCandidateStep + (step : AddInductive.CandidateWhnfStep) + (hvalid : step.Valid) + (contextRun : CandidateContextRun step.context) + (source' : VExpr) + (source_tr : contextRun.context.TrExprS step.source source') + (recursionFuel : Nat) + (hdepth : step.context.fuel.recDepth = recursionFuel + 1) : + ∃ result', contextRun.context.TrExprS step.result result' ∧ + Nonempty (WhnfRun contextRun.context.venv + contextRun.context.lparams contextRun.context.vlctx + step.source step.result source' result') := by + obtain ⟨state, run⟩ := step.innerRun recursionFuel hdepth hvalid + rw [← contextRun.context_eq] at run + obtain ⟨_, _, _, _, _, resultTranslation⟩ := + (Inner.whnf'.WF source_tr + (Methods.withFuel recursionFuel) Methods.withFuel.WF) + contextRun.state_wf step.result state run + obtain ⟨result', result_tr, _⟩ := resultTranslation + exact ⟨result', result_tr, ⟨WhnfRun.ofCandidateStep step hvalid + contextRun.context contextRun.context_eq rfl rfl rfl + contextRun.state_wf source_tr result_tr + recursionFuel hdepth⟩⟩ + +/-- Extend a verified candidate context by precisely the raw local declaration +used by `AddInductive.Context.pushLocalDecl`. + +The caller supplies the strict Theory translation and typing of the *stored* +local-domain expression. Freshness comes from the trace index; reservation is +the independent fact needed to restart each retained checker observation from +the empty kernel checker state. -/ +def CandidateContextRun.pushLocalDecl + (run : CandidateContextRun candidateContext) + (name : Name) (binderInfo : BinderInfo) (domain : Expr) + (fresh : candidateContext.lctx.find? + candidateContext.freshFVarId = none) + (domain' : VExpr) + (domain_tr : run.context.TrExprS domain domain') + (domain_type : run.context.IsType domain') : + CandidateContextRun + (candidateContext.pushLocalDecl name binderInfo domain) := by + let mlctx := run.context.mlctx.vlam candidateContext.freshFVarId + name domain domain' binderInfo + have lctx_eq : run.context.mlctx.lctx = candidateContext.lctx := by + calc + run.context.mlctx.lctx = run.context.lctx := run.context.lctx_eq + _ = candidateContext.lctx := by + simp + have fresh' : run.context.mlctx.lctx.find? + candidateContext.freshFVarId = none := by + rw [lctx_eq] + exact fresh + have mlctx_wf : mlctx.WF run.context.venv run.context.lparams := + ⟨run.context.mlctx_wf, fresh', domain_tr, domain_type⟩ + let context := run.context.withMLC mlctx (wf := ⟨mlctx_wf⟩) + have context_eq : context.toContext = + (candidateContext.pushLocalDecl name binderInfo domain).toTypeChecker := by + change { run.context.toContext with + lctx := run.context.mlctx.lctx.mkLocalDecl + candidateContext.freshFVarId name domain binderInfo } = _ + rw [run.context_eq, lctx_eq] + rfl + refine ⟨context, context_eq, VState.WF.empty_of_reserves context ?_, ?_⟩ + intro fv hfv + change fv ∈ candidateContext.freshFVarId :: + run.context.vlctx.fvars at hfv + simp only [List.mem_cons] at hfv + rcases hfv with rfl | hfv + · exact candidateFreshFVarId_reserved candidateContext run.namePrefix_ne + · exact run.state_wf.ngen_wf fv hfv + simpa [AddInductive.Context.pushLocalDecl, NameGenerator.next] using + run.namePrefix_ne + +@[simp] theorem CandidateContextRun.pushLocalDecl_venv + (run : CandidateContextRun candidateContext) + (domain_tr : run.context.TrExprS domain domain') + (domain_type : run.context.IsType domain') : + (run.pushLocalDecl name binderInfo domain fresh domain' domain_tr + domain_type).context.venv = run.context.venv := by + simp [CandidateContextRun.pushLocalDecl, VContext.withMLC] + +@[simp] theorem CandidateContextRun.pushLocalDecl_lparams + (run : CandidateContextRun candidateContext) + (domain_tr : run.context.TrExprS domain domain') + (domain_type : run.context.IsType domain') : + (run.pushLocalDecl name binderInfo domain fresh domain' domain_tr + domain_type).context.lparams = run.context.lparams := by + simp [CandidateContextRun.pushLocalDecl, VContext.withMLC] + +@[simp] theorem CandidateContextRun.pushLocalDecl_vlctx + (run : CandidateContextRun candidateContext) + (domain_tr : run.context.TrExprS domain domain') + (domain_type : run.context.IsType domain') : + (run.pushLocalDecl name binderInfo domain fresh domain' domain_tr + domain_type).context.vlctx = + (some (candidateContext.freshFVarId, domain.fvarsList), + .vlam domain') :: run.context.vlctx := by + simp [CandidateContextRun.pushLocalDecl, VContext.withMLC] + +/-- The two exact verifier runs attached to one retained candidate node. + +The indices force both runs to use the node's kernel source and observed +results and to agree on its Theory source. This is the atomic input to the +recursive candidate interpreter below: a successful full check supplies the +type at which the successful WHNF execution is interpreted. -/ +structure CandidateNodeRun (env : VEnv) (Us : List Name) (Δ : VLCtx) + (context : AddInductive.Context) + (source inferred result : Expr) + (source' result' inferred' : VExpr) where + check : CheckTypeRun env Us Δ source inferred source' inferred' + whnf : WhnfRun env Us Δ source result source' result' + +/-- Construct an atomic semantic node directly from the two proof-carrying +observations retained by `CandidateExprTrace`. The caller supplies only the +verified context and translations; the operational equalities and erased +checker states come from the candidate observations themselves. -/ +def CandidateNodeRun.ofCandidate + (candidateContext : AddInductive.Context) + (source inferred result : Expr) + (checked : AddInductive.CandidateCheckTypeStep.Valid + ⟨candidateContext, source, inferred⟩) + (normalized : AddInductive.CandidateWhnfStep.Valid + ⟨candidateContext, source, result⟩) + (context : VContext) + (context_eq : context.toContext = candidateContext.toTypeChecker) + (venv_eq : context.venv = env) + (lparams_eq : context.lparams = Us) + (vlctx_eq : context.vlctx = Δ) + (state_wf : VState.WF context {}) + (source_tr : TrExprS env Us Δ source source') + (inferred_tr : TrExprS env Us Δ inferred inferred') + (result_tr : TrExprS env Us Δ result result') + (checkFuel whnfFuel : Nat) + (checkDepth : candidateContext.fuel.recDepth = checkFuel) + (whnfDepth : candidateContext.fuel.recDepth = whnfFuel + 1) : + CandidateNodeRun env Us Δ candidateContext source inferred result + source' result' inferred' where + check := CheckTypeRun.ofCandidateStep + ⟨candidateContext, source, inferred⟩ checked context context_eq + venv_eq lparams_eq vlctx_eq state_wf source_tr inferred_tr + checkFuel checkDepth + whnf := WhnfRun.ofCandidateStep + ⟨candidateContext, source, result⟩ normalized context context_eq + venv_eq lparams_eq vlctx_eq state_wf source_tr result_tr + whnfFuel whnfDepth + +/-- Recover all output translations for one retained candidate node from the +verified checker refinements themselves. + +Unlike `ofCandidate`, this theorem does not ask the caller to identify the +kernel expressions returned by `checkType` and `whnf` with preselected Theory +terms. It extracts strict translations of both results from the two exact +executions, then packages those witnesses in the ordinary paired-node API. +Only the root source translation and matching verified context remain input. -/ +theorem CandidateNodeRun.exists_ofCandidate + (candidateContext : AddInductive.Context) + (source inferred result : Expr) + (checked : AddInductive.CandidateCheckTypeStep.Valid + ⟨candidateContext, source, inferred⟩) + (normalized : AddInductive.CandidateWhnfStep.Valid + ⟨candidateContext, source, result⟩) + (context : VContext) + (context_eq : context.toContext = candidateContext.toTypeChecker) + (state_wf : VState.WF context {}) + (source' : VExpr) (source_tr : context.TrExprS source source') + (checkFuel whnfFuel : Nat) + (checkDepth : candidateContext.fuel.recDepth = checkFuel) + (whnfDepth : candidateContext.fuel.recDepth = whnfFuel + 1) : + ∃ inferred' result', + context.TrExprS inferred inferred' ∧ + context.TrExprS result result' ∧ + Nonempty (CandidateNodeRun context.venv context.lparams context.vlctx + candidateContext source inferred result source' result' inferred') := by + obtain ⟨checkState, checkRun⟩ := + AddInductive.CandidateCheckTypeStep.innerRun + ⟨candidateContext, source, inferred⟩ checkFuel checkDepth checked + rw [← context_eq] at checkRun + obtain ⟨_, _, _, _, checkedSource', inferred', checkedTyping⟩ := + (Inner.checkType.WF source_tr.fvarsIn + (Methods.withFuel checkFuel) Methods.withFuel.WF) + state_wf inferred checkState checkRun + obtain ⟨whnfState, whnfRun⟩ := + AddInductive.CandidateWhnfStep.innerRun + ⟨candidateContext, source, result⟩ whnfFuel whnfDepth normalized + rw [← context_eq] at whnfRun + obtain ⟨_, _, _, _, _, resultTranslation⟩ := + (Inner.whnf'.WF source_tr + (Methods.withFuel whnfFuel) Methods.withFuel.WF) + state_wf result whnfState whnfRun + obtain ⟨result', result_tr, _⟩ := resultTranslation + refine ⟨inferred', result', checkedTyping.2.2.1, result_tr, ⟨?_⟩⟩ + exact CandidateNodeRun.ofCandidate + candidateContext source inferred result checked normalized + context context_eq rfl rfl rfl state_wf source_tr + checkedTyping.2.2.1 + result_tr + checkFuel whnfFuel checkDepth whnfDepth + +/-- Construct a paired candidate node with a caller-selected Theory endpoint +for the retained WHNF result. The exact full-check execution still selects +and strictly translates its inferred type; only the already translated WHNF +endpoint is fixed by the caller. -/ +theorem CandidateNodeRun.exists_ofCandidateAtResult + (candidateContext : AddInductive.Context) + (source inferred result : Expr) + (checked : AddInductive.CandidateCheckTypeStep.Valid + ⟨candidateContext, source, inferred⟩) + (normalized : AddInductive.CandidateWhnfStep.Valid + ⟨candidateContext, source, result⟩) + (context : VContext) + (context_eq : context.toContext = candidateContext.toTypeChecker) + (state_wf : VState.WF context {}) + (source' result' : VExpr) + (source_tr : context.TrExprS source source') + (result_tr : context.TrExprS result result') + (checkFuel whnfFuel : Nat) + (checkDepth : candidateContext.fuel.recDepth = checkFuel) + (whnfDepth : candidateContext.fuel.recDepth = whnfFuel + 1) : + ∃ inferred', Nonempty + (CandidateNodeRun context.venv context.lparams context.vlctx + candidateContext source inferred result source' result' inferred') := by + obtain ⟨_, inferred', _, inferred_tr, _⟩ := + candidateCheckTypeStep_exists_translation + ⟨candidateContext, source, inferred⟩ checked + context context_eq state_wf source_tr.fvarsIn checkFuel checkDepth + exact ⟨inferred', ⟨CandidateNodeRun.ofCandidate + candidateContext source inferred result checked normalized + context context_eq rfl rfl rfl state_wf source_tr inferred_tr + result_tr checkFuel whnfFuel checkDepth whnfDepth⟩⟩ + +/-- Compositional evidence for a normalization comparison. + +Leaves are either reflexive, already typed syntax or exact verified WHNF +executions. `forallE` lifts such evidence through the raw binder context, +which is the constructor-type case needed by the first non-identity replay. -/ +inductive DefEqEvidence (env : VEnv) : + Nat → List VExpr → VExpr → VExpr → VExpr → Prop where + | refl (h : env.HasType U Γ e A) : + DefEqEvidence env U Γ e e A + | whnf (run : WhnfRun env Us Δ lhs rhs lhs' rhs') + (h : env.HasType Us.length Δ.toCtx lhs' A) : + DefEqEvidence env Us.length Δ.toCtx lhs' rhs' A + | app + (fn : DefEqEvidence env U Γ f f' (.forallE A B)) + (arg : DefEqEvidence env U Γ a a' A) : + DefEqEvidence env U Γ (.app f a) (.app f' a') (B.inst a) + | beta + (body : env.HasType U (A :: Γ) e B) + (arg : env.HasType U Γ e' A) : + DefEqEvidence env U Γ + (.app (.lam A e) e') (e.inst e') (B.inst e') + | trans + (left : DefEqEvidence env U Γ lhs mid A) + (right : DefEqEvidence env U Γ mid rhs A) : + DefEqEvidence env U Γ lhs rhs A + | change + (type : env.IsDefEq U Γ A B (.sort u)) + (term : DefEqEvidence env U Γ lhs rhs A) : + DefEqEvidence env U Γ lhs rhs B + | ofDefEq (proof : env.IsDefEq U Γ lhs rhs A) : + DefEqEvidence env U Γ lhs rhs A + | forallE + (domain : DefEqEvidence env U Γ A A' (.sort u)) + (body : DefEqEvidence env U (A :: Γ) B B' (.sort v)) : + DefEqEvidence env U Γ + (.forallE A B) (.forallE A' B') (.sort (.imax u v)) + +/-- Interpret compositional normalization evidence as Theory definitional +equality at its recorded type. -/ +theorem DefEqEvidence.isDefEq : + DefEqEvidence env U Γ lhs rhs A → + env.IsDefEq U Γ lhs rhs A + | .refl h => h + | .whnf run h => run.isDefEq h + | .app fn arg => .appDF fn.isDefEq arg.isDefEq + | .beta body arg => .beta body arg + | .trans left right => .trans left.isDefEq right.isDefEq + | .change type term => .defeqDF type term.isDefEq + | .ofDefEq proof => proof + | .forallE domain body => + .forallEDF domain.isDefEq body.isDefEq + +/-- Interpret one paired candidate node as typed definitional equality. -/ +theorem CandidateNodeRun.evidence + (run : CandidateNodeRun env Us Δ context source inferred result + source' result' inferred') : + DefEqEvidence env Us.length Δ.toCtx source' result' inferred' := + .whnf run.whnf run.check.hasType + +/-- Recursive semantic interpretation of a source-indexed candidate trace. + +At a Pi node the raw domain and the annotation-consumed local domain may have +different strict Theory translations. The retained equality run relates them; +the body is checked in the consumed-domain context and transported back to the +raw Pi context only when forming congruence evidence. -/ +inductive CandidateExprRun (env : VEnv) (Us : List Name) : + {candidateContext : AddInductive.Context} → {source : Expr} → + AddInductive.CandidateExprTrace candidateContext source → + (Δ : VLCtx) → VExpr → VExpr → VExpr → Prop where + | terminal + (node : CandidateNodeRun env Us Δ context source inferred result + source' result' inferred') : + CandidateExprRun env Us + (.terminal context source inferred result checked normalized) + Δ source' result' inferred' + | forallE + (annotations : AddInductive.CandidateTypeAnnotations domain) + (annotationsEq : AddInductive.CandidateIsDefEqStep.Valid + ⟨context, domain, annotations.consumed⟩) + (domainCandidate : AddInductive.CandidateExprTrace context domain) + (bodyCandidate : AddInductive.CandidateExprTrace + (context.pushLocalDecl name binderInfo annotations.consumed) + (body.instantiate1 context.freshExpr)) + (node : CandidateNodeRun env Us Δ context source inferred + (.forallE name domain body binderInfo) + source' (.forallE domain' body') inferred') + (domainRun : CandidateExprRun env Us domainCandidate Δ + domain' domainView' domainInferred') + (annotationsRun : IsDefEqRun env Us Δ + domain annotations.consumed domain' storedDomain') + (bodyRun : CandidateExprRun env Us bodyCandidate bodyΔ + storedBody' bodyView' bodyInferred') + (domainType : env.HasType Us.length Δ.toCtx domain' (.sort u)) + (bodyType : env.HasType Us.length + (domain' :: Δ.toCtx) body' (.sort v)) + (bodySource : env.IsDefEq Us.length (domain' :: Δ.toCtx) + body' storedBody' (.sort v)) + (bodyContext : + bodyΔ = + (some (context.freshFVarId, annotations.consumed.fvarsList), + .vlam storedDomain') :: Δ) : + CandidateExprRun env Us + (.forallE context source inferred name domain body binderInfo fresh + annotations annotationsEq checked normalized + domainCandidate bodyCandidate) + Δ source' (.forallE domainView' bodyView') inferred' + +/-- Structural witness that a retained candidate trace is syntactically +identity-normalizing at every inspected node. + +The witness is deliberately recursive rather than a single root equality: +generation consumes the exposed Pi spine positionally. At Pi nodes it also +records that annotation processing kept the binder domain unchanged. -/ +inductive CandidateExprIdentity : + {candidateContext : AddInductive.Context} → {source : Expr} → + AddInductive.CandidateExprTrace candidateContext source → Prop where + | terminal + (result_eq : result = source) : + CandidateExprIdentity + (.terminal context source inferred result checked normalized) + | forallE + (domainCandidate : AddInductive.CandidateExprTrace context domain) + (bodyCandidate : AddInductive.CandidateExprTrace + (context.pushLocalDecl name binderInfo annotations.consumed) + (body.instantiate1 context.freshExpr)) + (source_eq : source = .forallE name domain body binderInfo) + (consumed_eq : annotations.consumed = domain) + (domainIdentity : CandidateExprIdentity domainCandidate) + (bodyIdentity : CandidateExprIdentity bodyCandidate) : + CandidateExprIdentity + (.forallE context source inferred name domain body binderInfo fresh + annotations annotationsEq checked normalized + domainCandidate bodyCandidate) + +private def candidateExprIdentityBinderInfoEq : + Lean.BinderInfo → Lean.BinderInfo → Bool + | .default, .default + | .implicit, .implicit + | .strictImplicit, .strictImplicit + | .instImplicit, .instImplicit => true + | _, _ => false + +private theorem candidateExprIdentityBinderInfoEq_sound + (left right : Lean.BinderInfo) + (h : candidateExprIdentityBinderInfoEq left right = true) : + left = right := by + cases left <;> cases right <;> + simp_all [candidateExprIdentityBinderInfoEq] + +/-- Transparent structural equality sufficient for identity-normalizing +candidate traces. Metadata nodes are conservatively rejected: the retained +generation spine never needs an opaque metadata equality to justify source +identity. -/ +private def candidateExprIdentityExprEq : Lean.Expr → Lean.Expr → Bool + | .bvar i, .bvar j => i == j + | .fvar i, .fvar j => i == j + | .mvar i, .mvar j => i == j + | .sort u, .sort v => u == v + | .const n us, .const n' us' => n == n' && us == us' + | .app f a, .app f' a' => + candidateExprIdentityExprEq f f' && + candidateExprIdentityExprEq a a' + | .lam n t b bi, .lam n' t' b' bi' => + n == n' && candidateExprIdentityExprEq t t' && + candidateExprIdentityExprEq b b' && + candidateExprIdentityBinderInfoEq bi bi' + | .forallE n t b bi, .forallE n' t' b' bi' => + n == n' && candidateExprIdentityExprEq t t' && + candidateExprIdentityExprEq b b' && + candidateExprIdentityBinderInfoEq bi bi' + | .letE n t v b nd, .letE n' t' v' b' nd' => + n == n' && candidateExprIdentityExprEq t t' && + candidateExprIdentityExprEq v v' && + candidateExprIdentityExprEq b b' && nd == nd' + | .lit a, .lit b => a == b + | .proj n i s, .proj n' i' s' => + n == n' && i == i' && candidateExprIdentityExprEq s s' + | _, _ => false + +private theorem candidateExprIdentityExprEq_sound : + ∀ (left right : Lean.Expr), + candidateExprIdentityExprEq left right = true → left = right := by + intro left right h + induction left generalizing right with + | bvar i => + cases right <;> + simp_all [candidateExprIdentityExprEq, beq_iff_eq] + | fvar i => + cases right <;> + simp_all [candidateExprIdentityExprEq, beq_iff_eq] + | mvar i => + cases right <;> + simp_all [candidateExprIdentityExprEq, beq_iff_eq] + | sort u => + cases right <;> + simp_all [candidateExprIdentityExprEq, beq_iff_eq] + | const n us => + cases right <;> + simp_all [candidateExprIdentityExprEq, beq_iff_eq] + | app fn arg fnIH argIH => + cases right with + | app fn' arg' => + simp only [candidateExprIdentityExprEq, + Bool.and_eq_true] at h + rw [fnIH fn' h.1, argIH arg' h.2] + | _ => simp_all [candidateExprIdentityExprEq] + | lam name type body binderInfo typeIH bodyIH => + cases right with + | lam name' type' body' binderInfo' => + simp only [candidateExprIdentityExprEq, Bool.and_eq_true, + beq_iff_eq] at h + rw [h.1.1.1, typeIH type' h.1.1.2, + bodyIH body' h.1.2, + candidateExprIdentityBinderInfoEq_sound _ _ h.2] + | _ => simp_all [candidateExprIdentityExprEq] + | forallE name type body binderInfo typeIH bodyIH => + cases right with + | forallE name' type' body' binderInfo' => + simp only [candidateExprIdentityExprEq, Bool.and_eq_true, + beq_iff_eq] at h + rw [h.1.1.1, typeIH type' h.1.1.2, + bodyIH body' h.1.2, + candidateExprIdentityBinderInfoEq_sound _ _ h.2] + | _ => simp_all [candidateExprIdentityExprEq] + | letE name type value body nondep typeIH valueIH bodyIH => + cases right with + | letE name' type' value' body' nondep' => + simp only [candidateExprIdentityExprEq, Bool.and_eq_true, + beq_iff_eq] at h + rw [h.1.1.1.1, typeIH type' h.1.1.1.2, + valueIH value' h.1.1.2, bodyIH body' h.1.2, h.2] + | _ => simp_all [candidateExprIdentityExprEq] + | lit literal => + cases right <;> + simp_all [candidateExprIdentityExprEq, beq_iff_eq] + | mdata data expr exprIH => + cases right <;> simp_all [candidateExprIdentityExprEq] + | proj typeName idx struct structIH => + cases right with + | proj typeName' idx' struct' => + simp only [candidateExprIdentityExprEq, Bool.and_eq_true, + beq_iff_eq] at h + rw [h.1.1, h.1.2, structIH struct' h.2] + | _ => simp_all [candidateExprIdentityExprEq] + +/-- Executable sufficient check for the recursive identity witness consumed +by generation. Unlike a root-only equality, it checks every retained domain, +body, annotation result, and terminal node. -/ +def CandidateExprIdentity.check : + {candidateContext : AddInductive.Context} → {source : Lean.Expr} → + AddInductive.CandidateExprTrace candidateContext source → Bool + | _, _, .terminal _ source _ result _ _ => + candidateExprIdentityExprEq result source + | _, _, .forallE _ source _ name domain body binderInfo _ annotations _ _ _ + domainCandidate bodyCandidate => + candidateExprIdentityExprEq source + (.forallE name domain body binderInfo) && + candidateExprIdentityExprEq annotations.consumed domain && + CandidateExprIdentity.check domainCandidate && + CandidateExprIdentity.check bodyCandidate + +/-- Executable sufficient equality check for the terminal expression selected +by a candidate trace. This is useful when the family validator must name its +result universe without unfolding the proof-carrying trace. -/ +def CandidateExprIdentity.terminalCheck + (trace : AddInductive.CandidateExprTrace candidateContext source) + (expected : Lean.Expr) : Bool := + candidateExprIdentityExprEq trace.terminalResult expected + +theorem CandidateExprIdentity.terminalResult_eq_of_check + {trace : AddInductive.CandidateExprTrace candidateContext source} + {expected : Lean.Expr} + (h : CandidateExprIdentity.terminalCheck trace expected = true) : + trace.terminalResult = expected := + candidateExprIdentityExprEq_sound _ _ h + +/-- A successful structural check yields the full recursive identity witness; +the Boolean contributes no semantic authority beyond these proved equalities. -/ +theorem CandidateExprIdentity.of_check + {trace : AddInductive.CandidateExprTrace candidateContext source} + (h : CandidateExprIdentity.check trace = true) : + CandidateExprIdentity trace := by + induction trace with + | terminal context source inferred result checked normalized => + simp only [CandidateExprIdentity.check] at h + exact .terminal (candidateExprIdentityExprEq_sound result source h) + | forallE context source inferred name domain body binderInfo fresh + annotations annotationsEq checked normalized domainCandidate + bodyCandidate domainIH bodyIH => + simp only [CandidateExprIdentity.check, Bool.and_eq_true] at h + exact .forallE domainCandidate bodyCandidate + (candidateExprIdentityExprEq_sound _ _ h.1.1.1) + (candidateExprIdentityExprEq_sound _ _ h.1.1.2) + (domainIH h.1.2) (bodyIH h.2) + +/-- An identity-normalizing trace necessarily preserves the stored main Pi +spine. This turns the recursive identity witness into the Boolean gate used +by the generation assembler. -/ +theorem CandidateExprIdentity.storedSpine + {trace : AddInductive.CandidateExprTrace candidateContext source} + (identity : CandidateExprIdentity trace) : + trace.storedSpine = true := by + induction identity with + | terminal => rfl + | forallE _ _ source_eq _ _ _ _ bodyIH => + simp [AddInductive.CandidateExprTrace.storedSpine, + source_eq, Expr.structuralEq_refl, bodyIH] + +/-- Exact component inversion for a strict translation of a kernel Pi. -/ +theorem TrExprS.forallE_components + (run : TrExprS env Us Δ (.forallE name domain body binderInfo) source') : + ∃ domain' body', + source' = .forallE domain' body' ∧ + env.IsType Us.length Δ.toCtx domain' ∧ + env.IsType Us.length (domain' :: Δ.toCtx) body' ∧ + TrExprS env Us Δ domain domain' ∧ + TrExprS env Us ((none, .vlam domain') :: Δ) body body' := by + cases run with + | forallE domainType bodyType domain_tr body_tr => + exact ⟨_, _, rfl, domainType, bodyType, domain_tr, body_tr⟩ + +/-- Recursively turn every retained candidate observation into verified +normalization evidence, constructing and transporting the exact verified +binder context at each Pi node. -/ +theorem CandidateExprRun.exists_ofCandidate + (trace : AddInductive.CandidateExprTrace candidateContext source) + (candidateRun : CandidateContextRun candidateContext) + (source' : VExpr) + (source_tr : candidateRun.context.TrExprS source source') + (whnfFuel : Nat) + (whnfDepth : candidateContext.fuel.recDepth = whnfFuel + 1) : + ∃ view' inferred', + Nonempty (CandidateExprRun candidateRun.context.venv + candidateRun.context.lparams trace candidateRun.context.vlctx + source' view' inferred') := by + induction trace generalizing source' with + | terminal context source inferred result checked normalized => + obtain ⟨inferred', result', _, _, ⟨node⟩⟩ := + CandidateNodeRun.exists_ofCandidate context source inferred result + checked normalized candidateRun.context candidateRun.context_eq + candidateRun.state_wf source' source_tr + context.fuel.recDepth whnfFuel rfl whnfDepth + exact ⟨result', inferred', ⟨.terminal node⟩⟩ + | forallE context source inferred name domain body binderInfo fresh + annotations annotationsEq checked normalized + domainCandidate bodyCandidate domainIH bodyIH => + obtain ⟨inferred', result', _, result_tr, ⟨node⟩⟩ := + CandidateNodeRun.exists_ofCandidate context source inferred + (.forallE name domain body binderInfo) checked normalized + candidateRun.context candidateRun.context_eq candidateRun.state_wf + source' source_tr context.fuel.recDepth whnfFuel rfl whnfDepth + let .forallE domainType bodyType domain_tr body_tr := result_tr + obtain ⟨u, domainTypeHasType⟩ := domainType + obtain ⟨v, bodyTypeHasType⟩ := bodyType + obtain ⟨domainView', domainInferred', ⟨domainRun⟩⟩ := + domainIH candidateRun _ domain_tr whnfDepth + obtain ⟨storedDomain', storedDomain_tr⟩ := + candidateTypeAnnotation_exists_translation annotations.trace domain_tr + let annotationsRun := IsDefEqRun.ofCandidateStep + ⟨context, domain, annotations.consumed⟩ annotationsEq + candidateRun.context candidateRun.context_eq rfl rfl rfl + candidateRun.state_wf domain_tr storedDomain_tr + context.fuel.recDepth rfl + have henv : VEnv.WF candidateRun.context.venv := + candidateRun.context.Ewf + have hΔ : OnCtx candidateRun.context.vlctx.toCtx + (candidateRun.context.venv.IsType + candidateRun.context.lparams.length) := + candidateRun.context.Δwf.toCtx + have annotationDef := + annotationsRun.isDefEqU.of_l henv hΔ domainTypeHasType + let bodyCandidateRun := candidateRun.pushLocalDecl name binderInfo + annotations.consumed fresh storedDomain' storedDomain_tr + ⟨u, annotationDef.hasType.2⟩ + have bodyVenv : bodyCandidateRun.context.venv = + candidateRun.context.venv := rfl + have bodyLparams : bodyCandidateRun.context.lparams = + candidateRun.context.lparams := rfl + have bodyVlctx : bodyCandidateRun.context.vlctx = + (some (context.freshFVarId, annotations.consumed.fvarsList), + .vlam storedDomain') :: candidateRun.context.vlctx := rfl + have bodyDepth : + (context.pushLocalDecl name binderInfo + annotations.consumed).fuel.recDepth = whnfFuel + 1 := by + simpa [AddInductive.Context.pushLocalDecl] using whnfDepth + have domainContext : VLCtx.IsDefEq + candidateRun.context.venv candidateRun.context.lparams.length + ((none, .vlam _) :: candidateRun.context.vlctx) + ((none, .vlam storedDomain') :: candidateRun.context.vlctx) := + .cons (.refl henv candidateRun.context.Δwf) (by nofun) + (.vlam annotationDef) + obtain ⟨storedBody', storedBody_tr⟩ := + body_tr.defeqDFC henv domainContext + have hRawBody : OnCtx + (_ :: candidateRun.context.vlctx.toCtx) + (candidateRun.context.venv.IsType + candidateRun.context.lparams.length) := + ⟨hΔ, ⟨u, domainTypeHasType⟩⟩ + have bodySource := + (body_tr.uniq henv domainContext storedBody_tr).of_l + henv hRawBody bodyTypeHasType + have bodyΔwf := bodyCandidateRun.context.Δwf + rw [bodyVenv, bodyLparams, bodyVlctx] at bodyΔwf + have instantiatedBody_tr := + storedBody_tr.inst_fvar henv.ordered bodyΔwf + obtain ⟨bodyView', bodyInferred', ⟨bodyRun⟩⟩ := + bodyIH bodyCandidateRun _ (by + change TrExprS bodyCandidateRun.context.venv + bodyCandidateRun.context.lparams bodyCandidateRun.context.vlctx + (body.instantiate1 context.freshExpr) _ + rw [bodyVenv, bodyLparams, bodyVlctx] + simpa only [AddInductive.Context.freshExpr, + Expr.instantiate1_eq] using instantiatedBody_tr) + bodyDepth + refine ⟨.forallE domainView' bodyView', inferred', ⟨?_⟩⟩ + exact .forallE annotations annotationsEq domainCandidate bodyCandidate node + domainRun annotationsRun bodyRun domainTypeHasType bodyTypeHasType + bodySource bodyVlctx + +/-- Interpret a recursively identity-normalizing candidate at the exact +strict Theory translation of its source. + +Unlike `exists_ofCandidate`, whose verified executions select an existential +Theory endpoint, this theorem retains `source'` as the endpoint at every +recursive position. That stronger conclusion is what the generation spine +assembler needs for declarations whose executable normalization is +syntactically the identity. -/ +theorem CandidateExprRun.exists_ofIdentity + (trace : AddInductive.CandidateExprTrace candidateContext source) + (identity : CandidateExprIdentity trace) + (candidateRun : CandidateContextRun candidateContext) + (source' : VExpr) + (source_tr : candidateRun.context.TrExprS source source') + (whnfFuel : Nat) + (whnfDepth : candidateContext.fuel.recDepth = whnfFuel + 1) : + ∃ inferred', Nonempty + (CandidateExprRun candidateRun.context.venv + candidateRun.context.lparams trace candidateRun.context.vlctx + source' source' inferred') := by + induction identity generalizing source' with + | @terminal result source context inferred checked normalized result_eq => + subst result + obtain ⟨inferred', ⟨node⟩⟩ := + CandidateNodeRun.exists_ofCandidateAtResult + context source inferred source checked normalized + candidateRun.context candidateRun.context_eq candidateRun.state_wf + source' source' source_tr source_tr + context.fuel.recDepth whnfFuel rfl whnfDepth + exact ⟨inferred', ⟨.terminal node⟩⟩ + | @forallE context domain name binderInfo source inferred body fresh + annotations annotationsEq checked normalized domainCandidate + bodyCandidate source_eq consumed_eq domainIdentity bodyIdentity + domainIH bodyIH => + subst source + obtain ⟨domain', body', rfl, domainWF, bodyWF, domain_tr, body_tr⟩ := + TypeChecker.TrExprS.forallE_components source_tr + obtain ⟨u, domainType⟩ := domainWF + obtain ⟨v, bodyType⟩ := bodyWF + obtain ⟨inferred', ⟨node⟩⟩ := + CandidateNodeRun.exists_ofCandidateAtResult + context (.forallE name domain body binderInfo) inferred + (.forallE name domain body binderInfo) checked normalized + candidateRun.context candidateRun.context_eq candidateRun.state_wf + (.forallE domain' body') (.forallE domain' body') + source_tr source_tr + context.fuel.recDepth whnfFuel rfl whnfDepth + obtain ⟨domainInferred', ⟨domainRun⟩⟩ := + domainIH candidateRun domain' domain_tr whnfDepth + have consumed_tr : candidateRun.context.TrExprS + annotations.consumed domain' := by + rw [consumed_eq] + exact domain_tr + let annotationsRun := IsDefEqRun.ofCandidateStep + ⟨context, domain, annotations.consumed⟩ annotationsEq + candidateRun.context candidateRun.context_eq rfl rfl rfl + candidateRun.state_wf domain_tr consumed_tr + context.fuel.recDepth rfl + let bodyCandidateRun := candidateRun.pushLocalDecl name binderInfo + annotations.consumed fresh domain' consumed_tr ⟨u, domainType⟩ + have bodyVenv : bodyCandidateRun.context.venv = + candidateRun.context.venv := rfl + have bodyLparams : bodyCandidateRun.context.lparams = + candidateRun.context.lparams := rfl + have bodyVlctx : bodyCandidateRun.context.vlctx = + (some (context.freshFVarId, annotations.consumed.fvarsList), + .vlam domain') :: candidateRun.context.vlctx := rfl + have bodyDepth : + (context.pushLocalDecl name binderInfo + annotations.consumed).fuel.recDepth = + whnfFuel + 1 := by + simpa [AddInductive.Context.pushLocalDecl] using whnfDepth + have bodyDeltaWF := bodyCandidateRun.context.Δwf + rw [bodyVenv, bodyLparams, bodyVlctx] at bodyDeltaWF + have instantiatedBody_tr := + body_tr.inst_fvar candidateRun.context.Ewf.ordered + bodyDeltaWF + obtain ⟨bodyInferred', ⟨bodyRun⟩⟩ := + bodyIH bodyCandidateRun body' (by + change TrExprS bodyCandidateRun.context.venv + bodyCandidateRun.context.lparams bodyCandidateRun.context.vlctx + (body.instantiate1 context.freshExpr) body' + rw [bodyVenv, bodyLparams, bodyVlctx] + simpa only [AddInductive.Context.freshExpr, + Expr.instantiate1_eq] using instantiatedBody_tr) + bodyDepth + refine ⟨inferred', ⟨?_⟩⟩ + exact .forallE annotations annotationsEq domainCandidate bodyCandidate + node domainRun annotationsRun bodyRun domainType bodyType bodyType rfl + +/-- Recover a trace root's strict source translation from its retained full +check. Unlike recursive child nodes, whose source translations are obtained +from the parent Pi translation, a root needs only the checker's syntactic +free-variable premise. -/ +theorem candidateExprTrace_exists_source_translation + (trace : AddInductive.CandidateExprTrace candidateContext source) + (candidateRun : CandidateContextRun candidateContext) + (source_fvars : + source.FVarsIn (· ∈ candidateRun.context.vlctx.fvars)) : + ∃ source', candidateRun.context.TrExprS source source' := by + let checked := trace.rootCheck + obtain ⟨source', _, source_tr, _, _⟩ := + candidateCheckTypeStep_exists_translation + ⟨candidateContext, source, checked.inferred⟩ checked.valid + candidateRun.context candidateRun.context_eq candidateRun.state_wf + source_fvars candidateContext.fuel.recDepth rfl + exact ⟨source', source_tr⟩ + +/-- Recursively certify an annotation-complete candidate trace without asking +the caller for any Theory expression. The retained root full check chooses the +source translation; all output and child translations then come from verified +checker executions, structural annotation traces, and Pi decomposition. -/ +theorem CandidateExprRun.exists_ofCandidateFVars + (trace : AddInductive.CandidateExprTrace candidateContext source) + (candidateRun : CandidateContextRun candidateContext) + (source_fvars : + source.FVarsIn (· ∈ candidateRun.context.vlctx.fvars)) + (whnfFuel : Nat) + (whnfDepth : candidateContext.fuel.recDepth = whnfFuel + 1) : + ∃ source' view' inferred', + candidateRun.context.TrExprS source source' ∧ + Nonempty (CandidateExprRun candidateRun.context.venv + candidateRun.context.lparams trace candidateRun.context.vlctx + source' view' inferred') := by + obtain ⟨source', source_tr⟩ := + candidateExprTrace_exists_source_translation trace candidateRun + source_fvars + obtain ⟨view', inferred', run⟩ := + CandidateExprRun.exists_ofCandidate trace candidateRun source' + source_tr whnfFuel whnfDepth + exact ⟨source', view', inferred', source_tr, run⟩ + +/-- Fold a complete candidate trace into the compositional equality language +consumed by `NormalizationRun` and `GenerationRun`. -/ +theorem CandidateExprRun.evidence + {env : VEnv} {Us : List Name} + {candidateContext : AddInductive.Context} {source : Expr} + {trace : AddInductive.CandidateExprTrace candidateContext source} + {Δ : VLCtx} {source' view' inferred' : VExpr} : + CandidateExprRun env Us trace Δ source' view' inferred' → + DefEqEvidence env Us.length Δ.toCtx source' view' inferred' + | .terminal node => node.evidence + | .forallE _ _ _ _ node domainRun annotationsRun bodyRun domainType + bodyType bodySource bodyContext => by + have henv : VEnv.WF env := by + simpa only [node.check.venv_eq] using node.check.context.Ewf + have hΔ : VLCtx.WF env Us.length Δ := by + simpa only [node.check.venv_eq, node.check.lparams_eq, + node.check.vlctx_eq] using node.check.context.Δwf + have hΓ : OnCtx Δ.toCtx (env.IsType Us.length) := hΔ.toCtx + have domainEvidence := domainRun.evidence + obtain ⟨_, domainTypeEq⟩ := + domainType.uniq henv hΓ domainEvidence.isDefEq + have domainAtSort : DefEqEvidence env Us.length Δ.toCtx + _ _ (.sort _) := + .change domainTypeEq.symm domainEvidence + have annotationDef := + annotationsRun.isDefEqU.of_l henv hΓ domainType + have domainContext : VLCtx.IsDefEq env Us.length + ((none, .vlam _) :: Δ) ((none, .vlam _) :: Δ) := + .cons (.refl henv hΔ) (by nofun) (.vlam annotationDef) + have bodyEvidence := bodyRun.evidence + rw [bodyContext] at bodyEvidence + simp only [VLCtx.toCtx] at bodyEvidence + have bodyStoredType := + bodySource.hasType.2.defeqDFC henv domainContext.defeqCtx + have hBodyΓ : OnCtx (_ :: Δ.toCtx) (env.IsType Us.length) := + ⟨hΓ, ⟨_, annotationDef.hasType.2⟩⟩ + obtain ⟨_, bodyTypeEq⟩ := + bodyStoredType.uniq henv hBodyΓ bodyEvidence.isDefEq + have bodyAtSortStored : DefEqEvidence env Us.length + (_ :: Δ.toCtx) _ _ (.sort _) := + .change bodyTypeEq.symm bodyEvidence + have bodyAtSortRaw := + bodyAtSortStored.isDefEq.defeqDFC henv + (domainContext.symm henv).defeqCtx + have bodyFinal := bodySource.trans bodyAtSortRaw + have piEvidence := DefEqEvidence.forallE domainAtSort + (DefEqEvidence.ofDefEq bodyFinal) + obtain ⟨_, nodeTypeEq⟩ := + node.evidence.isDefEq.uniq henv hΓ (domainType.forallE bodyType) + exact .trans node.evidence (.change nodeTypeEq.symm piEvidence) + +/-- The raw root of a recursively interpreted trace has the strict Theory +translation retained by its paired full-check run. -/ +theorem CandidateExprRun.source_tr + {env : VEnv} {Us : List Name} + {candidateContext : AddInductive.Context} {source : Expr} + {trace : AddInductive.CandidateExprTrace candidateContext source} + {Δ : VLCtx} {source' view' inferred' : VExpr} + (run : CandidateExprRun env Us trace Δ source' view' inferred') : + TrExprS env Us Δ source source' := by + cases run with + | terminal node => exact node.check.expr_tr + | forallE _ _ _ _ node => exact node.check.expr_tr + +/-- Move a weak expression translation between definitionally equal verified +local contexts while retaining its named Theory meaning. -/ +private theorem candidateTrExpr_moveCtx + (henv : VEnv.WF env) + (hctx : VLCtx.IsDefEq env Us.length Δ₁ Δ₂) + (H : TrExpr env Us Δ₁ e e') : + TrExpr env Us Δ₂ e e' := by + obtain ⟨e₂, he₂, hdef⟩ := H + have moved : TrExpr env Us Δ₂ e e₂ := + he₂.defeqDFC' henv hctx + exact moved.defeq henv (hctx.symm henv).wf.toCtx + (hdef.defeqDFC henv hctx.defeqCtx) + +/-- The recursively reconstructed kernel candidate view translates to the +Theory view named by `CandidateExprRun`. + +At Pi nodes the instantiated child body is first abstracted from its exact +free-variable context and then transported from the raw-domain context to the +definitionally equal normalized-domain context. This closes the provenance +loop: the emitted Theory equality is not only between two well-typed terms; +both endpoints are translations of the source-indexed candidate syntax. -/ +theorem CandidateExprRun.view_tr + {env : VEnv} {Us : List Name} + {candidateContext : AddInductive.Context} {source : Expr} + {trace : AddInductive.CandidateExprTrace candidateContext source} + {Δ : VLCtx} {source' view' inferred' : VExpr} + (run : CandidateExprRun env Us trace Δ source' view' inferred') : + TrExpr env Us Δ trace.view view' := by + induction run with + | @terminal Δ context source inferred result source' result' inferred' + checked normalized node => + have henv : VEnv.WF env := by + simpa only [node.whnf.venv_eq] using node.whnf.context.Ewf + have hΔ : VLCtx.WF env Us.length Δ := by + simpa only [node.whnf.venv_eq, node.whnf.lparams_eq, + node.whnf.vlctx_eq] using node.whnf.context.Δwf + simpa only [AddInductive.CandidateExprTrace.view] using + node.whnf.rhs_tr.trExpr henv hΔ + | @forallE domain context name binderInfo Δ source inferred body + source' domain' body' inferred' domainView' domainInferred' + storedDomain' bodyΔ storedBody' bodyView' bodyInferred' u v fresh + checked normalized annotations annotationsEq domainCandidate + bodyCandidate node domainRun annotationsRun bodyRun domainType bodyType + bodySource bodyContext domainIH bodyIH => + have henv : VEnv.WF env := by + simpa only [node.check.venv_eq] using node.check.context.Ewf + have hΔ : VLCtx.WF env Us.length Δ := by + simpa only [node.check.venv_eq, node.check.lparams_eq, + node.check.vlctx_eq] using node.check.context.Δwf + obtain ⟨_, domainTypeEq⟩ := + domainType.uniq henv hΔ.toCtx domainRun.evidence.isDefEq + have domainDef : env.IsDefEq Us.length Δ.toCtx + domain' domainView' (.sort u) := + (DefEqEvidence.change domainTypeEq.symm domainRun.evidence).isDefEq + have annotationDef := + annotationsRun.isDefEqU.of_l henv hΔ.toCtx domainType + have storedToView : env.IsDefEq Us.length Δ.toCtx + storedDomain' domainView' (.sort u) := + annotationDef.symm.trans domainDef + have bodyIH' : TrExpr env Us + ((some (context.freshFVarId, + annotations.consumed.fvarsList), .vlam storedDomain') :: Δ) + bodyCandidate.view bodyView' := by + simpa only [bodyContext] using bodyIH + have bodyAbstract := bodyIH'.abstract VLCtx.Abstract.zero + have hctx : VLCtx.IsDefEq env Us.length + ((none, .vlam storedDomain') :: Δ) + ((none, .vlam domainView') :: Δ) := + .cons (.refl henv hΔ) (by nofun) + (.vlam storedToView) + have bodyMoved := candidateTrExpr_moveCtx henv hctx bodyAbstract + have bodyEvidence := bodyRun.evidence + rw [bodyContext] at bodyEvidence + simp only [VLCtx.toCtx] at bodyEvidence + have annotationContext : VLCtx.IsDefEq env Us.length + ((none, .vlam domain') :: Δ) + ((none, .vlam storedDomain') :: Δ) := + .cons (.refl henv hΔ) (by nofun) (.vlam annotationDef) + have bodyStoredType := + bodySource.hasType.2.defeqDFC henv annotationContext.defeqCtx + have hBodyΓ : OnCtx (storedDomain' :: Δ.toCtx) + (env.IsType Us.length) := + ⟨hΔ.toCtx, ⟨_, annotationDef.hasType.2⟩⟩ + obtain ⟨_, bodyTypeEq⟩ := + bodyStoredType.uniq henv hBodyΓ bodyEvidence.isDefEq + have bodyDefStored : env.IsDefEq Us.length + (storedDomain' :: Δ.toCtx) storedBody' bodyView' (.sort v) := + (DefEqEvidence.change bodyTypeEq.symm bodyEvidence).isDefEq + have bodyDefMoved := + bodyDefStored.defeqDFC henv hctx.defeqCtx + have habstract : + bodyCandidate.view.abstract #[context.freshExpr] = + Expr.abstract1 context.freshFVarId bodyCandidate.view := by + rw [show #[context.freshExpr] = + ⟨[context.freshFVarId].map Expr.fvar⟩ by rfl] + simp only [Expr.abstract_eq, Expr.abstractList] + apply TrExpr.forallE henv hΔ + · exact ⟨_, domainDef.hasType.2⟩ + · exact ⟨_, bodyDefMoved.hasType.2⟩ + · exact domainIH + · simpa only [AddInductive.CandidateExprTrace.view, + habstract] using bodyMoved + +/-- Exact-translation uniqueness for every expression that contributes to a +candidate view. The abstracted-body clause names the syntax stored by +`CandidateExprTrace.view`, while the recursive body clause supports the next +candidate node. Projections are intentionally excluded: their verified +Theory relation is only unique up to definitional equality. -/ +def CandidateExprTraceViewIsUnique : + {context : AddInductive.Context} → {source : Expr} → + AddInductive.CandidateExprTrace context source → Prop + | _, _, .terminal _ _ _ result _ _ => TrExprS.IsUnique result + | _, _, .forallE context _ _ _ _ _ _ _ _ _ _ _ domain body => + CandidateExprTraceViewIsUnique domain ∧ + CandidateExprTraceViewIsUnique body ∧ + TrExprS.IsUnique (body.view.abstract #[context.freshExpr]) + +/-- The recursive uniqueness certificate in particular covers the complete +view reconstructed at this candidate node. -/ +theorem CandidateExprTraceViewIsUnique.view + {context : AddInductive.Context} {source : Expr} + {trace : AddInductive.CandidateExprTrace context source} + (unique : CandidateExprTraceViewIsUnique trace) : + TrExprS.IsUnique trace.view := by + induction trace with + | terminal => exact unique + | forallE context source inferred name domain body binderInfo fresh + annotations annotationsEq checked normalized domainCandidate bodyCandidate + domainIH bodyIH => + exact ⟨domainIH unique.1, unique.2.2⟩ + +/-- A projection-free candidate view retains the strict Theory translation +selected componentwise by its recursive semantic run. + +The ordinary `view_tr` theorem must use weak translation because a projection +endpoint is only definitionally determined. Under the explicit uniqueness +condition, recursive abstraction and context transport select the exact +analyzer-owned expression instead. -/ +theorem CandidateExprRun.view_tr_strict + {env : VEnv} {Us : List Name} + {candidateContext : AddInductive.Context} {source : Expr} + {trace : AddInductive.CandidateExprTrace candidateContext source} + {Δ : VLCtx} {source' view' inferred' : VExpr} + (run : CandidateExprRun env Us trace Δ source' view' inferred') + (unique : CandidateExprTraceViewIsUnique trace) : + TrExprS env Us Δ trace.view view' := by + induction run with + | terminal node => + simpa only [AddInductive.CandidateExprTrace.view] using + node.whnf.rhs_tr + | @forallE domain context name binderInfo Δ source inferred body + source' domain' body' inferred' domainView' domainInferred' + storedDomain' bodyΔ storedBody' bodyView' bodyInferred' u v fresh + checked normalized annotations annotationsEq domainCandidate + bodyCandidate node domainRun annotationsRun bodyRun domainType bodyType + bodySource bodyContext domainIH bodyIH => + rcases unique with ⟨domainUnique, bodyUnique, abstractUnique⟩ + have domainStrict := domainIH domainUnique + have bodyStrict : TrExprS env Us + ((some (context.freshFVarId, + annotations.consumed.fvarsList), .vlam storedDomain') :: Δ) + bodyCandidate.view bodyView' := by + simpa only [bodyContext] using bodyIH bodyUnique + have bodyAbstract := bodyStrict.abstract VLCtx.Abstract.zero + have henv : VEnv.WF env := by + simpa only [node.check.venv_eq] using node.check.context.Ewf + have hΔ : VLCtx.WF env Us.length Δ := by + simpa only [node.check.venv_eq, node.check.lparams_eq, + node.check.vlctx_eq] using node.check.context.Δwf + obtain ⟨_, domainTypeEq⟩ := + domainType.uniq henv hΔ.toCtx domainRun.evidence.isDefEq + have domainDef : env.IsDefEq Us.length Δ.toCtx + domain' domainView' (.sort u) := + (DefEqEvidence.change domainTypeEq.symm domainRun.evidence).isDefEq + have annotationDef := + annotationsRun.isDefEqU.of_l henv hΔ.toCtx domainType + have storedToView : env.IsDefEq Us.length Δ.toCtx + storedDomain' domainView' (.sort u) := + annotationDef.symm.trans domainDef + have hctx : VLCtx.IsDefEq env Us.length + ((none, .vlam storedDomain') :: Δ) + ((none, .vlam domainView') :: Δ) := + .cons (.refl henv hΔ) (by nofun) (.vlam storedToView) + obtain ⟨bodyMoved', bodyMoved⟩ := + bodyAbstract.defeqDFC henv hctx + have habstract : + bodyCandidate.view.abstract #[context.freshExpr] = + Expr.abstract1 context.freshFVarId bodyCandidate.view := by + rw [show #[context.freshExpr] = + ⟨[context.freshFVarId].map Expr.fvar⟩ by rfl] + simp only [Expr.abstract_eq, Expr.abstractList] + have bodyAbstractArray : TrExprS env Us + ((none, .vlam storedDomain') :: Δ) + (bodyCandidate.view.abstract #[context.freshExpr]) bodyView' := by + simpa only [habstract] using bodyAbstract + have bodyMovedArray : TrExprS env Us + ((none, .vlam domainView') :: Δ) + (bodyCandidate.view.abstract #[context.freshExpr]) bodyMoved' := by + simpa only [habstract] using bodyMoved + have bodyMoved_eq : bodyMoved' = bodyView' := by + exact (bodyAbstractArray.unique' + (.cons .base .vlam) abstractUnique bodyMovedArray).symm + subst bodyMoved' + have bodyEvidence := bodyRun.evidence + rw [bodyContext] at bodyEvidence + simp only [VLCtx.toCtx] at bodyEvidence + have annotationContext : VLCtx.IsDefEq env Us.length + ((none, .vlam domain') :: Δ) + ((none, .vlam storedDomain') :: Δ) := + .cons (.refl henv hΔ) (by nofun) (.vlam annotationDef) + have bodyStoredType := + bodySource.hasType.2.defeqDFC henv annotationContext.defeqCtx + have hBodyΓ : OnCtx (storedDomain' :: Δ.toCtx) + (env.IsType Us.length) := + ⟨hΔ.toCtx, ⟨_, annotationDef.hasType.2⟩⟩ + obtain ⟨_, bodyTypeEq⟩ := + bodyStoredType.uniq henv hBodyΓ bodyEvidence.isDefEq + have bodyDefStored : env.IsDefEq Us.length + (storedDomain' :: Δ.toCtx) storedBody' bodyView' (.sort v) := + (DefEqEvidence.change bodyTypeEq.symm bodyEvidence).isDefEq + have bodyDefMoved := + bodyDefStored.defeqDFC henv hctx.defeqCtx + simpa only [AddInductive.CandidateExprTrace.view, habstract] using + TrExprS.forallE + (⟨u, domainDef.hasType.2⟩ : + env.IsType Us.length Δ.toCtx domainView') + (⟨v, bodyDefMoved.hasType.2⟩ : + env.IsType Us.length (domainView' :: Δ.toCtx) bodyView') + domainStrict bodyMoved + +/-- Root-level verified context and translations for an exact executable +candidate expression and an explicitly named Theory view. + +The raw endpoint is a strict translation of the stored kernel source. The +view endpoint translates the exact reconstructed candidate syntax; allowing +the ordinary `TrExpr` relation here accounts for the definitional transport +performed while recursively rebuilding Pi bodies. -/ +structure CandidateExprRootRun (env : VEnv) (Us : List Name) + {source : Expr} (candidate : AddInductive.CandidateExpr source) + (source' view' : VExpr) where + contextRun : CandidateContextRun candidate.context + venv_eq : contextRun.context.venv = env + lparams_eq : contextRun.context.lparams = Us + vlctx_eq : contextRun.context.vlctx = [] + source_tr : TrExprS env Us [] source source' + view_tr : TrExpr env Us [] candidate.view view' + whnfFuel : Nat + whnfDepth : candidate.context.fuel.recDepth = whnfFuel + 1 + +/-- Interpret a root candidate against its explicitly translated endpoints. +The candidate view is not selected from a proof-only existential: the caller +names it and proves that it translates the exact executable view, while the +verified recursive run supplies the equality to the strict raw endpoint. -/ +theorem CandidateExprRootRun.evidence + {env : VEnv} {Us : List Name} {source : Expr} + {candidate : AddInductive.CandidateExpr source} + {source' view' : VExpr} + (run : CandidateExprRootRun env Us candidate source' view') : + ∃ A, DefEqEvidence env Us.length [] source' view' A := by + have source_tr : run.contextRun.context.TrExprS source source' := by + simpa only [VContext.TrExprS, run.venv_eq, run.lparams_eq, + run.vlctx_eq] using run.source_tr + obtain ⟨candidateView', inferred', ⟨candidateRun⟩⟩ := + CandidateExprRun.exists_ofCandidate candidate.trace run.contextRun + source' source_tr run.whnfFuel run.whnfDepth + have henv : VEnv.WF env := by + simpa only [run.venv_eq] using run.contextRun.context.Ewf + have hΔ : VLCtx.WF env Us.length [] := by + simpa only [run.venv_eq, run.lparams_eq, run.vlctx_eq] using + run.contextRun.context.Δwf + have candidateView_tr : + TrExpr env Us [] candidate.view candidateView' := by + simpa only [AddInductive.CandidateExpr.view, run.venv_eq, + run.lparams_eq, run.vlctx_eq] using + candidateRun.view_tr + have viewDef : env.IsDefEqU Us.length [] candidateView' view' := + candidateView_tr.uniq henv (.refl henv hΔ) run.view_tr + have sourceDef : env.IsDefEqU Us.length [] source' candidateView' := by + simpa only [run.venv_eq, run.lparams_eq, run.vlctx_eq, + VLCtx.toCtx] using + candidateRun.evidence.isDefEq.toU + obtain ⟨A, hfinal⟩ := sourceDef.trans henv hΔ.toCtx viewDef + exact ⟨A, .ofDefEq hfinal⟩ + +/-- A root candidate together with the exact recursively interpreted semantic +run selected by its retained checker executions. + +Unlike `CandidateExprRootRun`, this bundle does not stop at whole-expression +equality: it retains the exact inferred type and reconstructed Theory view at +every recursive candidate position. Consequently the same value can supply +both normalization evidence and, when the executable trace preserves its main +Pi spine, the positional telescope/result evidence required by generation. +The view is selected by the verified run rather than supplied independently by +a caller. -/ +structure CandidateExprSemanticRootRun (env : VEnv) (Us : List Name) + {source : Expr} (candidate : AddInductive.CandidateExpr source) + (source' : VExpr) where + contextRun : CandidateContextRun candidate.context + venv_eq : contextRun.context.venv = env + lparams_eq : contextRun.context.lparams = Us + vlctx_eq : contextRun.context.vlctx = [] + source_tr : TrExprS env Us [] source source' + whnfFuel : Nat + whnfDepth : candidate.context.fuel.recDepth = whnfFuel + 1 + view : VExpr + recursive : ∃ inferred, CandidateExprRun env Us candidate.trace [] + source' view inferred + +/-- Forget the retained recursive run and expose the existing root semantic +interface. The reconstructed view translation is derived from that same run, +so it cannot name an unrelated endpoint. -/ +def CandidateExprSemanticRootRun.root + (run : CandidateExprSemanticRootRun env Us candidate source') : + CandidateExprRootRun env Us candidate source' run.view where + contextRun := run.contextRun + venv_eq := run.venv_eq + lparams_eq := run.lparams_eq + vlctx_eq := run.vlctx_eq + source_tr := run.source_tr + view_tr := by + obtain ⟨_, recursive⟩ := run.recursive + simpa only [AddInductive.CandidateExpr.view] using + recursive.view_tr + whnfFuel := run.whnfFuel + whnfDepth := run.whnfDepth + +/-- Automatically construct the retained root semantics from an exact +verified candidate context and strict translation of the stored kernel +source. + +The checker run selects the Theory view and inferred type existentially; the +result records those exact selections. No caller-selected normalization view, +erasure equality, or whole-Pi injectivity principle is used. -/ +theorem CandidateExprSemanticRootRun.exists_ofCandidate + {env : VEnv} {Us : List Name} {source : Expr} + {candidate : AddInductive.CandidateExpr source} {source' : VExpr} + (contextRun : CandidateContextRun candidate.context) + (venv_eq : contextRun.context.venv = env) + (lparams_eq : contextRun.context.lparams = Us) + (vlctx_eq : contextRun.context.vlctx = []) + (source_tr : TrExprS env Us [] source source') + (whnfFuel : Nat) + (whnfDepth : candidate.context.fuel.recDepth = whnfFuel + 1) : + Nonempty (CandidateExprSemanticRootRun env Us candidate source') := by + have contextualSource : + contextRun.context.TrExprS source source' := by + simpa only [VContext.TrExprS, venv_eq, lparams_eq, vlctx_eq] using + source_tr + obtain ⟨view, inferred, ⟨recursive⟩⟩ := + CandidateExprRun.exists_ofCandidate candidate.trace contextRun source' + contextualSource whnfFuel whnfDepth + refine ⟨{ + contextRun := contextRun + venv_eq := venv_eq + lparams_eq := lparams_eq + vlctx_eq := vlctx_eq + source_tr := source_tr + whnfFuel := whnfFuel + whnfDepth := whnfDepth + view := view + recursive := ⟨inferred, ?_⟩ }⟩ + simpa only [venv_eq, lparams_eq, vlctx_eq] using recursive + +/-- The exact pre-run evidence needed to interpret one candidate root without +asking a caller to choose its normalized Theory view. + +This bundle deliberately stops before the recursive semantic run. It contains +only the verified implementation context, its alignment with the requested +Theory environment, the strict translation of the stored source, and the fuel +relation consumed by `CandidateExprRun.exists_ofCandidate`. -/ +structure CandidateExprSemanticRootInput (env : VEnv) (Us : List Name) + {source : Expr} (candidate : AddInductive.CandidateExpr source) + (source' : VExpr) where + contextRun : CandidateContextRun candidate.context + venv_eq : contextRun.context.venv = env + lparams_eq : contextRun.context.lparams = Us + vlctx_eq : contextRun.context.vlctx = [] + source_tr : TrExprS env Us [] source source' + whnfFuel : Nat + whnfDepth : candidate.context.fuel.recDepth = whnfFuel + 1 + +/-- Run the retained checker interpreter on an exact root input. The result is +`Nonempty` because the checker-selected Theory view is semantic evidence rather +than executable metadata; no choice operator or caller-supplied endpoint is +introduced by this boundary. -/ +theorem CandidateExprSemanticRootInput.exists + (input : CandidateExprSemanticRootInput env Us candidate source') : + Nonempty (CandidateExprSemanticRootRun env Us candidate source') := + CandidateExprSemanticRootRun.exists_ofCandidate input.contextRun + input.venv_eq input.lparams_eq input.vlctx_eq input.source_tr + input.whnfFuel input.whnfDepth + +/-- Interpret an identity-normalizing staged root at the strict Theory +translation already owned by its source input. This keeps the endpoint +definitionally fixed without caller-supplied WHNF data or a +`Classical.choice` over the general semantic interpreter. -/ +def CandidateExprSemanticRootInput.semanticOfIdentity + {env : VEnv} {Us : List Name} {source : Expr} + {candidate : AddInductive.CandidateExpr source} {source' : VExpr} + (input : CandidateExprSemanticRootInput env Us candidate source') + (identity : CandidateExprIdentity candidate.trace) : + CandidateExprSemanticRootRun env Us candidate source' where + contextRun := input.contextRun + venv_eq := input.venv_eq + lparams_eq := input.lparams_eq + vlctx_eq := input.vlctx_eq + source_tr := input.source_tr + whnfFuel := input.whnfFuel + whnfDepth := input.whnfDepth + view := source' + recursive := by + have source_tr : input.contextRun.context.TrExprS source source' := by + simpa only [VContext.TrExprS, input.venv_eq, input.lparams_eq, + input.vlctx_eq] using input.source_tr + obtain ⟨inferred, ⟨recursive⟩⟩ := + CandidateExprRun.exists_ofIdentity candidate.trace identity + input.contextRun source' source_tr input.whnfFuel input.whnfDepth + refine ⟨inferred, ?_⟩ + simpa only [input.venv_eq, input.lparams_eq, input.vlctx_eq] using + recursive + +/-- Interpret a staged root at the deterministic translation of its +checker-selected view. For a projection-free view the recursive semantic +run's endpoint is pinned by strict-translation agreement +(`CandidateExprRun.view_tr_strict` plus `TrExprS.trExprS?_eq`), so the +retained `view` field is computed by `trExprS?` and the `Nonempty` +interpretation is transferred onto it; no choice operator selects data. +Unlike `semanticOfIdentity` this covers non-identity normalizations, at the +cost of the executable view-uniqueness certificate. -/ +def CandidateExprSemanticRootInput.semanticOfUnique + {env : VEnv} {Us : List Name} {source : Expr} + {candidate : AddInductive.CandidateExpr source} {source' : VExpr} + (input : CandidateExprSemanticRootInput env Us candidate source') + (unique : CandidateExprTraceViewIsUnique candidate.trace) : + CandidateExprSemanticRootRun env Us candidate source' := + match hview : trExprS? Us [] candidate.trace.view with + | some view => + { contextRun := input.contextRun + venv_eq := input.venv_eq + lparams_eq := input.lparams_eq + vlctx_eq := input.vlctx_eq + source_tr := input.source_tr + whnfFuel := input.whnfFuel + whnfDepth := input.whnfDepth + view := view + recursive := by + obtain ⟨w⟩ := input.exists + obtain ⟨inferred, run⟩ := w.recursive + cases Option.some.inj + (((run.view_tr_strict unique).trExprS?_eq unique.view).symm.trans + hview) + exact ⟨inferred, run⟩ } + | none => + absurd + (show (trExprS? Us [] candidate.trace.view).isSome by + obtain ⟨w⟩ := input.exists + obtain ⟨inferred, run⟩ := w.recursive + exact TrExprS.trExprS?_isSome + ⟨w.view, run.view_tr_strict unique⟩ unique.view) + (by simp [hview]) + +/-- One explicitly verified root stage shared by every candidate expression +interpreted before or after family insertion. + +The stage owns the implementation/Theory context alignment once. Individual +source positions retain only their strict translation, fuel relation, and the +equality identifying the candidate's stored context with this stage. This is +the reusable boundary between staged environment validation and the retained +recursive candidate interpreter. -/ +structure CandidateSemanticStage + (candidateContext : AddInductive.Context) (env : VEnv) (Us : List Name) + where + contextRun : CandidateContextRun candidateContext + venv_eq : contextRun.context.venv = env + lparams_eq : contextRun.context.lparams = Us + vlctx_eq : contextRun.context.vlctx = [] + +/-- Source-position evidence interpreted in one shared candidate stage. + +`context_eq` prevents a verified stage for another producer position from +being reused. The normalized Theory endpoint is deliberately absent: it is +selected only by `CandidateExprSemanticRootInput.exists`. -/ +structure CandidateExprStagedInput + {candidateContext : AddInductive.Context} {env : VEnv} {Us : List Name} + (stage : CandidateSemanticStage candidateContext env Us) + {source : Expr} (candidate : AddInductive.CandidateExpr source) + (source' : VExpr) where + context_eq : candidateContext = candidate.context + source_tr : TrExprS env Us [] source source' + whnfFuel : Nat + whnfDepth : candidate.context.fuel.recDepth = whnfFuel + 1 + +/-- Specialize a shared verified stage to one exact source-indexed candidate +root. This is a pure dependent transport; it neither runs the checker nor +chooses the semantic view. -/ +def CandidateExprStagedInput.rootInput + {candidateContext : AddInductive.Context} {env : VEnv} {Us : List Name} + {source : Expr} {candidate : AddInductive.CandidateExpr source} + {source' : VExpr} + {stage : CandidateSemanticStage candidateContext env Us} + (input : CandidateExprStagedInput stage candidate source') : + CandidateExprSemanticRootInput env Us candidate source' := by + cases input.context_eq + exact { + contextRun := stage.contextRun + venv_eq := stage.venv_eq + lparams_eq := stage.lparams_eq + vlctx_eq := stage.vlctx_eq + source_tr := input.source_tr + whnfFuel := input.whnfFuel + whnfDepth := input.whnfDepth } + +/-- Pointwise checker-produced equality for a pair of binder telescopes. The +tail is checked in the context extended by the raw binder, exactly matching +`VEnv.TelDefEq` and the mixed generator's raw-binder discipline. -/ +inductive TelDefEqEvidence (env : VEnv) (U : Nat) : + List VExpr → List VExpr → List VExpr → Prop where + | nil : TelDefEqEvidence env U Γ [] [] + | cons + (head : DefEqEvidence env U Γ A A' (.sort u)) + (tail : TelDefEqEvidence env U (A :: Γ) As As') : + TelDefEqEvidence env U Γ (A :: As) (A' :: As') + +/-- Interpret pointwise checker evidence as the Theory telescope equality +consumed by `GenerationChecked.WF`. -/ +theorem TelDefEqEvidence.telDefEq : + TelDefEqEvidence env U Γ As As' → + env.TelDefEq U Γ As As' + | .nil => trivial + | .cons head tail => ⟨⟨_, head.isDefEq⟩, tail.telDefEq⟩ + +/-- Pointwise telescope equality followed by equality of the terminal result. + +Keeping these witnesses in one inductive preserves the raw-binder context at +every recursive step. In particular, the result is checked in +`rawBinders.reverse ++ Γ`, exactly the context used by mixed generation. -/ +inductive TelResultDefEqEvidence (env : VEnv) (U : Nat) : + (Γ : List VExpr) → (rawBinders viewBinders : List VExpr) → + (rawResult viewResult resultType : VExpr) → Prop where + | terminal + (result : DefEqEvidence env U Γ rawResult viewResult resultType) : + TelResultDefEqEvidence env U Γ [] [] rawResult viewResult resultType + | forallE + (domain : DefEqEvidence env U Γ rawDomain viewDomain (.sort u)) + (tail : TelResultDefEqEvidence env U (rawDomain :: Γ) + rawBinders viewBinders rawResult viewResult resultType) : + TelResultDefEqEvidence env U Γ + (rawDomain :: rawBinders) (viewDomain :: viewBinders) + rawResult viewResult resultType + +/-- Telescope component of a combined spine/result certificate. -/ +theorem TelResultDefEqEvidence.telescope : + TelResultDefEqEvidence env U Γ rawBinders viewBinders + rawResult viewResult resultType → + TelDefEqEvidence env U Γ rawBinders viewBinders + | .terminal _ => .nil + | .forallE domain tail => .cons domain tail.telescope + +/-- Terminal component, in the context generated by all raw binders. -/ +theorem TelResultDefEqEvidence.result : + TelResultDefEqEvidence env U Γ rawBinders viewBinders + rawResult viewResult resultType → + DefEqEvidence env U (rawBinders.reverse ++ Γ) + rawResult viewResult resultType + | .terminal result => by simpa using result + | .forallE _ tail => by + simpa [List.reverse_cons, List.append_assoc] using tail.result + +theorem TelResultDefEqEvidence.length_eq : + TelResultDefEqEvidence env U Γ rawBinders viewBinders + rawResult viewResult resultType → + rawBinders.length = viewBinders.length + | .terminal _ => rfl + | .forallE _ tail => congrArg Nat.succ tail.length_eq + +/-- Reify a Theory telescope equality as explicit checker-produced evidence. +This direction is useful after telescope operations such as `take`, `drop`, +and context transport have rearranged a candidate certificate. -/ +theorem TelDefEqEvidence.ofTelDefEq : + ∀ {Γ As As'}, env.TelDefEq U Γ As As' → + TelDefEqEvidence env U Γ As As' + | _, [], [], _ => .nil + | _, _ :: _, _ :: _, ⟨⟨_, head⟩, tail⟩ => + .cons (.ofDefEq head) (TelDefEqEvidence.ofTelDefEq tail) + +/-- Retain an exact prefix of a checker-produced telescope certificate. -/ +theorem TelDefEqEvidence.take + (run : TelDefEqEvidence env U Γ As As') (n : Nat) : + TelDefEqEvidence env U Γ (As.take n) (As'.take n) := + .ofTelDefEq (run.telDefEq.take n) + +/-- Transport a checker-produced telescope certificate through environment +growth. -/ +theorem TelDefEqEvidence.mono + (run : TelDefEqEvidence env U Γ As As') (henv : env ≤ env') : + TelDefEqEvidence env' U Γ As As' := + .ofTelDefEq (run.telDefEq.mono henv) + +/-- Combine an independently transformed telescope certificate with its +terminal result certificate. -/ +theorem TelResultDefEqEvidence.ofTelescopeResult + (tel : TelDefEqEvidence env U Γ rawBinders viewBinders) + (result : DefEqEvidence env U (rawBinders.reverse ++ Γ) + rawResult viewResult resultType) : + TelResultDefEqEvidence env U Γ rawBinders viewBinders + rawResult viewResult resultType := by + induction tel with + | nil => exact .terminal (by simpa using result) + | cons head tail ih => + exact .forallE head (ih (by + simpa [List.reverse_cons, List.append_assoc] using result)) + +private theorem candidateDefEqCtx_trans (henv : VEnv.WF env) : + ∀ {Γ₁ Γ₂ Γ₃}, + env.IsDefEqCtx U [] Γ₁ Γ₂ → + env.IsDefEqCtx U [] Γ₂ Γ₃ → + env.IsDefEqCtx U [] Γ₁ Γ₃ + | _, _, _, .zero, h₂₃ => h₂₃ + | _, _, _, .succ h₁₂ head₁₂, .succ h₂₃ head₂₃ => by + have tail := candidateDefEqCtx_trans henv h₁₂ h₂₃ + have head₂₃' := head₂₃.defeqDFC henv (h₁₂.symm henv) + exact .succ tail (VEnv.IsDefEq.trans_l henv h₁₂.isType + head₁₂ head₂₃') + +private theorem candidateTelDefEq_defeqDFC (henv : VEnv.WF env) + (hctx : env.IsDefEqCtx U [] Γ₁ Γ₂) : + ∀ {As As'}, env.TelDefEq U Γ₁ As As' → + env.TelDefEq U Γ₂ As As' + | [], [], _ => trivial + | _ :: _, _ :: _, ⟨⟨u, head⟩, tail⟩ => + ⟨⟨u, head.defeqDFC henv hctx⟩, + candidateTelDefEq_defeqDFC henv + (.succ hctx head.hasType.1) tail⟩ + +private theorem candidateTelDefEq_append + {env : VEnv} {U : Nat} {Γ : List VExpr} : + ∀ {As As' Bs Bs'}, env.TelDefEq U Γ As As' → + env.TelDefEq U (As.reverse ++ Γ) Bs Bs' → + env.TelDefEq U Γ (As ++ Bs) (As' ++ Bs') + | [], [], _, _, _, suffix => by simpa using suffix + | _ :: As, _ :: As', Bs, Bs', ⟨head, tail⟩, suffix => by + exact ⟨head, candidateTelDefEq_append tail (by + simpa [List.reverse_cons, List.append_assoc] using suffix)⟩ + +/-- Replace a constructor candidate's stored parameter prefix by the family's +raw parameter prefix. + +The two raw prefixes need not be syntactically equal: both are related to the +same checked view prefix. The field telescope and terminal result are then +transported through the induced context equality, yielding exactly the mixed +raw/view context emitted by generation. -/ +theorem TelResultDefEqEvidence.replacePrefix + (henv : VEnv.WF env) + (newPrefix : TelDefEqEvidence env U [] newRawPrefix viewPrefix) + (run : TelResultDefEqEvidence env U [] + (oldRawPrefix ++ rawSuffix) (viewPrefix ++ viewSuffix) + rawResult viewResult resultType) + (prefixLength : oldRawPrefix.length = viewPrefix.length) : + TelResultDefEqEvidence env U [] + (newRawPrefix ++ rawSuffix) (viewPrefix ++ viewSuffix) + rawResult viewResult resultType := by + have declaredTel := run.telescope.telDefEq + have oldPrefix : env.TelDefEq U [] oldRawPrefix viewPrefix := by + have hprefix := declaredTel.take oldRawPrefix.length + simpa [prefixLength] using hprefix + have oldSuffix : env.TelDefEq U oldRawPrefix.reverse + rawSuffix viewSuffix := by + have suffix := declaredTel.drop oldRawPrefix.length + simpa [prefixLength] using suffix + have newPrefixTheory := newPrefix.telDefEq + have newPrefixContext : env.IsDefEqCtx U [] + newRawPrefix.reverse viewPrefix.reverse := by + simpa using newPrefixTheory.ctx + have oldPrefixContext : env.IsDefEqCtx U [] + oldRawPrefix.reverse viewPrefix.reverse := by + simpa using oldPrefix.ctx + have prefixContext : env.IsDefEqCtx U [] + newRawPrefix.reverse oldRawPrefix.reverse := + candidateDefEqCtx_trans henv newPrefixContext + (oldPrefixContext.symm henv) + have newSuffix : env.TelDefEq U newRawPrefix.reverse + rawSuffix viewSuffix := + candidateTelDefEq_defeqDFC henv (prefixContext.symm henv) oldSuffix + have emittedTel : env.TelDefEq U [] + (newRawPrefix ++ rawSuffix) (viewPrefix ++ viewSuffix) := + candidateTelDefEq_append newPrefixTheory (by simpa using newSuffix) + have fullContext : env.IsDefEqCtx U [] + ((newRawPrefix ++ rawSuffix).reverse) + ((oldRawPrefix ++ rawSuffix).reverse) := by + have extended := newSuffix.raw_onTel.extendDefEqCtx prefixContext + simpa [List.reverse_append] using extended + have oldResult : DefEqEvidence env U + (oldRawPrefix ++ rawSuffix).reverse + rawResult viewResult resultType := by + simpa using run.result + have emittedResult : DefEqEvidence env U + (newRawPrefix ++ rawSuffix).reverse + rawResult viewResult resultType := + .ofDefEq (oldResult.isDefEq.defeqDFC henv + (fullContext.symm henv)) + exact TelResultDefEqEvidence.ofTelescopeResult + (.ofTelDefEq emittedTel) (by simpa using emittedResult) + +/-- Every recursive candidate run carries the well-formed local context used +by its root checker observation. -/ +theorem CandidateExprRun.context_wf + {env : VEnv} {Us : List Name} + {candidateContext : AddInductive.Context} {source : Expr} + {trace : AddInductive.CandidateExprTrace candidateContext source} + {Δ : VLCtx} {source' view' inferred' : VExpr} + (run : CandidateExprRun env Us trace Δ source' view' inferred') : + VLCtx.WF env Us.length Δ := by + cases run with + | @terminal Δ context source inferred result source' result' inferred' + checked normalized node => + simpa only [node.check.venv_eq, node.check.lparams_eq, + node.check.vlctx_eq] using node.check.context.Δwf + | forallE _ _ _ _ node => + simpa only [node.check.venv_eq, node.check.lparams_eq, + node.check.vlctx_eq] using node.check.context.Δwf + +private theorem candidateTelN_forallN_length : + ∀ (As : List VExpr) (B : VExpr), + VExpr.telN As.length (VExpr.forallN As B) = As + | [], _ => rfl + | _ :: As, B => by + simp only [List.length_cons, VExpr.forallN, VExpr.telN, + candidateTelN_forallN_length As B] + +private theorem candidateDropN_forallN_length : + ∀ (As : List VExpr) (B : VExpr), + VExpr.dropN As.length (VExpr.forallN As B) = B + | [], _ => rfl + | _ :: As, B => by + simp only [List.length_cons, VExpr.forallN, VExpr.dropN, + candidateDropN_forallN_length As B] + +/-- Syntactic terminal marker used only to recover a telescope from a known +`dropN` endpoint. -/ +private def CandidateTerminal : VExpr → Prop + | .forallE _ _ => False + | _ => True + +/-- If dropping `n` binders from a telescope reaches its non-forall terminal, +then taking `n` binders recovers the entire telescope. -/ +private theorem candidateTelN_of_dropN_terminal + {B : VExpr} (hB : CandidateTerminal B) : + ∀ (As : List VExpr) (n : Nat), + VExpr.dropN n (VExpr.forallN As B) = B → + VExpr.telN n (VExpr.forallN As B) = As + | [], n, _ => by + cases B <;> cases n <;> simp_all [CandidateTerminal, + VExpr.forallN, VExpr.dropN, VExpr.telN] + | A :: As, 0, h => by + cases B <;> simp_all [CandidateTerminal, + VExpr.forallN, VExpr.dropN] + | A :: As, n + 1, h => by + simp only [VExpr.forallN, VExpr.telN, + List.cons.injEq, true_and] + exact candidateTelN_of_dropN_terminal hB As n (by + simpa only [VExpr.forallN, VExpr.dropN] using h) + +private theorem candidateTerminal_appN_app (f a : VExpr) : + ∀ args, CandidateTerminal (VExpr.appN (.app f a) args) + | [] => trivial + | b :: args => candidateTerminal_appN_app (.app f a) b args + +private theorem candidateTerminal_appN_const + (name : Name) (levels : List VLevel) : + ∀ args, CandidateTerminal (VExpr.appN (.const name levels) args) + | [] => trivial + | a :: args => candidateTerminal_appN_app (.const name levels) a args + +/-- Recursive worker for candidate spine extraction. + +`rawΔ` follows the contexts generated by the stored raw binders, while `Δ` +is the annotation-consumed context in which the candidate body was checked. +The explicit context equality transports each retained checker judgment back +to the raw side before it is added to the telescope certificate. -/ +private theorem CandidateExprRun.spineEvidenceAux + {env : VEnv} {Us : List Name} + {candidateContext : AddInductive.Context} {source : Expr} + {trace : AddInductive.CandidateExprTrace candidateContext source} + {Δ : VLCtx} {source' view' inferred' : VExpr} + (run : CandidateExprRun env Us trace Δ source' view' inferred') + (aligned : trace.storedSpine = true) + {rawΔ : VLCtx} {rawSource' : VExpr} + (contextEq : VLCtx.IsDefEq env Us.length rawΔ Δ) + (rawSource_tr : TrExprS env Us rawΔ source rawSource') : + ∃ rawBinders viewBinders rawResult viewResult resultType, + rawSource' = VExpr.forallN rawBinders rawResult ∧ + view' = VExpr.forallN viewBinders viewResult ∧ + TelResultDefEqEvidence env Us.length rawΔ.toCtx + rawBinders viewBinders rawResult viewResult resultType ∧ + rawBinders.length = trace.spineLength := by + induction run generalizing rawΔ rawSource' with + | terminal node => + have henv : VEnv.WF env := by + simpa only [node.check.venv_eq] using node.check.context.Ewf + have hRawΔ := contextEq.wf + have rawToSource : env.IsDefEqU Us.length rawΔ.toCtx + rawSource' _ := + rawSource_tr.uniq henv contextEq node.check.expr_tr + have sourceToView := + node.evidence.isDefEq.defeqDFC henv + (contextEq.symm henv).defeqCtx + have final := VEnv.IsDefEq.transU_r henv hRawΔ.toCtx + rawToSource sourceToView + exact ⟨[], [], rawSource', _, _, rfl, rfl, + .terminal (.ofDefEq final), rfl⟩ + | @forallE domain context name binderInfo Δ source inferred body + source' domain' body' inferred' domainView' domainInferred' + storedDomain' bodyΔ storedBody' bodyView' bodyInferred' u v fresh + checked normalized annotations annotationsEq domainCandidate + bodyCandidate node domainRun annotationsRun bodyRun domainType bodyType + bodySource bodyContext domainIH bodyIH => + simp only [AddInductive.CandidateExprTrace.storedSpine, + Bool.and_eq_true] at aligned + obtain ⟨sourceEq, bodyAligned⟩ := aligned + have alignedSource_tr : TrExprS env Us rawΔ + (.forallE name domain body binderInfo) rawSource' := + rawSource_tr.eqv (Expr.structuralEq_eqv sourceEq) + let @TrExprS.forallE _ _ rawDomain rawBody _ _ _ _ _ + rawDomainType rawBodyType rawDomain_tr rawBody_tr := alignedSource_tr + have henv : VEnv.WF env := by + simpa only [node.check.venv_eq] using node.check.context.Ewf + have hΔ : VLCtx.WF env Us.length Δ := by + simpa only [node.check.venv_eq, node.check.lparams_eq, + node.check.vlctx_eq] using node.check.context.Δwf + have hRawΔ := contextEq.wf + have rawToDomainU := + rawDomain_tr.uniq henv contextEq domainRun.source_tr + have domainTypeRaw := + domainType.defeqDFC henv (contextEq.symm henv).defeqCtx + have rawToDomain := + rawToDomainU.of_r henv hRawΔ.toCtx domainTypeRaw + have domainToView := + domainRun.evidence.isDefEq.toU.of_l henv hΔ.toCtx domainType + have domainToViewRaw := + domainToView.defeqDFC henv (contextEq.symm henv).defeqCtx + have head : DefEqEvidence env Us.length rawΔ.toCtx + _ domainView' (.sort u) := + .ofDefEq (rawToDomain.trans domainToViewRaw) + have annotationDef := + annotationsRun.isDefEqU.of_l henv hΔ.toCtx domainType + have annotationDefRaw := + annotationDef.defeqDFC henv (contextEq.symm henv).defeqCtx + have rawToStored := rawToDomain.trans annotationDefRaw + have bodyWF := bodyRun.context_wf + rw [bodyContext] at bodyWF + have rawFresh : + ∀ fv deps, + some (context.freshFVarId, annotations.consumed.fvarsList) = + some (fv, deps) → + fv ∉ rawΔ.fvars ∧ deps ⊆ rawΔ.fvars := by + intro fv deps heq + cases heq + have hfresh := bodyWF.2.1 _ _ rfl + simpa only [contextEq.fvars] using hfresh + let rawBodyΔ : VLCtx := + (some (context.freshFVarId, annotations.consumed.fvarsList), + .vlam rawDomain) :: rawΔ + have bodyContextEqConcrete : VLCtx.IsDefEq env Us.length rawBodyΔ + ((some (context.freshFVarId, annotations.consumed.fvarsList), + .vlam storedDomain') :: Δ) := + .cons contextEq rawFresh (.vlam rawToStored) + have bodyContextEq : VLCtx.IsDefEq env Us.length rawBodyΔ bodyΔ := by + simpa only [bodyContext] using bodyContextEqConcrete + have rawBodyΔwf := bodyContextEq.wf + have rawBodyInst_tr : TrExprS env Us rawBodyΔ + (body.instantiate1 context.freshExpr) rawBody := by + simpa only [AddInductive.Context.freshExpr, + Expr.instantiate1_eq] using + rawBody_tr.inst_fvar henv.ordered rawBodyΔwf + obtain ⟨rawBinders, viewBinders, rawResult, viewResult, + resultType, rawBodyEq, viewBodyEq, tail, tailLength⟩ := + bodyIH bodyAligned bodyContextEq rawBodyInst_tr + refine ⟨rawDomain :: rawBinders, + domainView' :: viewBinders, rawResult, viewResult, resultType, + ?_, ?_, ?_, ?_⟩ + · simp only [VExpr.forallN, rawBodyEq] + · simp only [VExpr.forallN, viewBodyEq] + · exact .forallE head (by + simpa only [rawBodyΔ, VLCtx.toCtx] using tail) + · simpa only [List.length_cons, + AddInductive.CandidateExprTrace.spineLength] using + congrArg Nat.succ tailLength + +/-- Extract exact raw/view telescopes and terminal results from a recursive +candidate run whose WHNF traversal preserved the stored Pi spine. + +The binder count is computed from the source-indexed trace, and `telN`/ +`dropN` name the exact stored raw and reconstructed-view components. This +avoids recovering binder equality from whole-Pi definitional equality and so +does not use the unfinished forall-injectivity theorem. -/ +theorem CandidateExprRun.spineEvidence + {env : VEnv} {Us : List Name} + {candidateContext : AddInductive.Context} {source : Expr} + {trace : AddInductive.CandidateExprTrace candidateContext source} + {Δ : VLCtx} {source' view' inferred' : VExpr} + (run : CandidateExprRun env Us trace Δ source' view' inferred') + (aligned : trace.storedSpine = true) : + ∃ resultType, + TelResultDefEqEvidence env Us.length Δ.toCtx + (VExpr.telN trace.spineLength source') + (VExpr.telN trace.spineLength view') + (VExpr.dropN trace.spineLength source') + (VExpr.dropN trace.spineLength view') resultType := by + have henv : VEnv.WF env := by + cases run with + | terminal node => + simpa only [node.check.venv_eq] using node.check.context.Ewf + | forallE _ _ _ _ node => + simpa only [node.check.venv_eq] using node.check.context.Ewf + obtain ⟨rawBinders, viewBinders, rawResult, viewResult, + resultType, rawEq, viewEq, evidence, rawLength⟩ := + run.spineEvidenceAux aligned + (.refl henv run.context_wf) run.source_tr + have viewLength : viewBinders.length = trace.spineLength := + evidence.length_eq ▸ rawLength + have rawTel : VExpr.telN trace.spineLength source' = rawBinders := by + rw [rawEq, ← rawLength] + exact candidateTelN_forallN_length rawBinders rawResult + have viewTel : VExpr.telN trace.spineLength view' = viewBinders := by + rw [viewEq, ← viewLength] + exact candidateTelN_forallN_length viewBinders viewResult + have rawResultEq : + VExpr.dropN trace.spineLength source' = rawResult := by + rw [rawEq, ← rawLength] + exact candidateDropN_forallN_length rawBinders rawResult + have viewResultEq : + VExpr.dropN trace.spineLength view' = viewResult := by + rw [viewEq, ← viewLength] + exact candidateDropN_forallN_length viewBinders viewResult + simpa only [rawTel, viewTel, rawResultEq, viewResultEq] using + ⟨resultType, evidence⟩ + +/-- Replace only the terminal typing index of a combined certificate. The +telescope and both result endpoints remain definitionally unchanged. -/ +theorem TelResultDefEqEvidence.withResult + (run : TelResultDefEqEvidence env U Γ rawBinders viewBinders + rawResult viewResult resultType) + (result : DefEqEvidence env U (rawBinders.reverse ++ Γ) + rawResult viewResult resultType') : + TelResultDefEqEvidence env U Γ rawBinders viewBinders + rawResult viewResult resultType' := by + induction run with + | terminal _ => exact .terminal (by simpa using result) + | forallE domain tail ih => + exact .forallE domain (ih (by + simpa [List.reverse_cons, List.append_assoc] using result)) + +/-- Fix a candidate terminal equality at a known type of its right endpoint. +This is the bridge from the candidate's checker-inferred type to the precise +sort required by dependent inductive analysis. -/ +theorem TelResultDefEqEvidence.ofRightType + (henv : VEnv.WF env) (hΓ : OnCtx Γ (env.IsType U)) + (run : TelResultDefEqEvidence env U Γ rawBinders viewBinders + rawResult viewResult resultType) + (rightType : env.HasType U (rawBinders.reverse ++ Γ) + viewResult expectedType) : + TelResultDefEqEvidence env U Γ rawBinders viewBinders + rawResult viewResult expectedType := by + have hctx : OnCtx (rawBinders.reverse ++ Γ) (env.IsType U) := + (run.telescope.telDefEq.extendCtx (.refl hΓ)).isType + exact run.withResult (.ofDefEq + (run.result.isDefEq.toU.of_r henv hctx rightType)) + +theorem CandidateExprRun.env_wf + {env : VEnv} {Us : List Name} + {candidateContext : AddInductive.Context} {source : Expr} + {trace : AddInductive.CandidateExprTrace candidateContext source} + {Δ : VLCtx} {source' view' inferred' : VExpr} + (run : CandidateExprRun env Us trace Δ source' view' inferred') : + VEnv.WF env := by + cases run with + | terminal node => + simpa only [node.check.venv_eq] using node.check.context.Ewf + | forallE _ _ _ _ node => + simpa only [node.check.venv_eq] using node.check.context.Ewf + +/-- Recover the exact verified candidate context reached at the end of the +main Pi spine. + +`CandidateExprRun` retains the semantic context at every recursive node, but +its public indices deliberately mention only the translated local context. +Constructor validation, on the other hand, resumes in the implementation +`Context` returned by the family traversal. This projection reconnects the +two without reconstructing a local context from names: starting from the +root `CandidateContextRun`, each Pi case repeats the already-certified +annotation equality and the exact `pushLocalDecl` used by the candidate. -/ +theorem CandidateExprRun.terminalContextRun + {env : VEnv} {Us : List Name} + {candidateContext : AddInductive.Context} {source : Expr} + {trace : AddInductive.CandidateExprTrace candidateContext source} + {Δ : VLCtx} {source' view' inferred' : VExpr} + (run : CandidateExprRun env Us trace Δ source' view' inferred') + (contextRun : CandidateContextRun candidateContext) + (venv_eq : contextRun.context.venv = env) + (lparams_eq : contextRun.context.lparams = Us) + (vlctx_eq : contextRun.context.vlctx = Δ) : + ∃ terminalRun : CandidateContextRun trace.terminalContext, + terminalRun.context.venv = env ∧ + terminalRun.context.lparams = Us := by + induction run with + | terminal node => + exact ⟨by + simpa only [AddInductive.CandidateExprTrace.terminalContext] using + contextRun, + venv_eq, lparams_eq⟩ + | @forallE domain context name binderInfo Δ source inferred body + source' domain' body' inferred' domainView' domainInferred' + storedDomain' bodyΔ storedBody' bodyView' bodyInferred' u v fresh + checked normalized annotations annotationsEq domainCandidate + bodyCandidate node domainRun annotationsRun bodyRun domainType bodyType + bodySource bodyContext domainIH bodyIH => + have storedDomain_tr : contextRun.context.TrExprS + annotations.consumed storedDomain' := by + simpa only [VContext.TrExprS, venv_eq, lparams_eq, vlctx_eq] using + annotationsRun.rhs_tr + have henv : VEnv.WF env := by + simpa only [venv_eq] using contextRun.context.Ewf + have hΔ : OnCtx Δ.toCtx (env.IsType Us.length) := by + simpa only [venv_eq, lparams_eq, vlctx_eq] using + contextRun.context.Δwf.toCtx + have storedDomain_type : env.IsType Us.length Δ.toCtx storedDomain' := by + have annotationDef := annotationsRun.isDefEqU.of_l henv hΔ domainType + exact ⟨u, annotationDef.hasType.2⟩ + let nextContextRun := contextRun.pushLocalDecl name binderInfo + annotations.consumed fresh storedDomain' storedDomain_tr (by + change contextRun.context.venv.IsType + contextRun.context.lparams.length + contextRun.context.vlctx.toCtx storedDomain' + rw [venv_eq, lparams_eq, vlctx_eq] + exact storedDomain_type) + have nextVenv : nextContextRun.context.venv = env := by + simp only [nextContextRun, CandidateContextRun.pushLocalDecl_venv, + venv_eq] + have nextLparams : nextContextRun.context.lparams = Us := by + simp only [nextContextRun, CandidateContextRun.pushLocalDecl_lparams, + lparams_eq] + have nextVlctx : nextContextRun.context.vlctx = bodyΔ := by + simp only [nextContextRun, CandidateContextRun.pushLocalDecl_vlctx] + rw [vlctx_eq, bodyContext] + obtain ⟨terminalRun, terminalVenv, terminalLparams⟩ := + bodyIH nextContextRun nextVenv nextLparams nextVlctx + exact ⟨by + simpa only [AddInductive.CandidateExprTrace.terminalContext] using + terminalRun, + terminalVenv, terminalLparams⟩ + +private theorem candidateFVLift'_comp + (left : VLCtx.FVLift' Δ₁ Δ₂ 0 (.skipN .refl n₁) 0) + (right : VLCtx.FVLift' Δ₂ Δ₃ 0 (.skipN .refl n₂) 0) : + VLCtx.FVLift' Δ₁ Δ₃ 0 (.skipN .refl (n₁ + n₂)) 0 := by + simpa only [Lift.comp_skipN, Lift.comp, Lift.skipN_skipN] using + left.comp right + +/-- Recover the terminal implementation context together with the exact +candidate-view telescope occupying the same local positions. + +The two `VLCtx`s keep the identical free-variable metadata and declaration +kinds. Their declaration types may differ, but strict translation uniqueness +only needs this positional relation. The view-side `toCtx` is definitionally +the reversed telescope selected by the recursive semantic run, followed by +the caller's view-side base context. -/ +theorem CandidateExprRun.terminalContextRunView + {env : VEnv} {Us : List Name} + {candidateContext : AddInductive.Context} {source : Expr} + {trace : AddInductive.CandidateExprTrace candidateContext source} + {Δ : VLCtx} {source' view' inferred' : VExpr} + (run : CandidateExprRun env Us trace Δ source' view' inferred') + (contextRun : CandidateContextRun candidateContext) + (venv_eq : contextRun.context.venv = env) + (lparams_eq : contextRun.context.lparams = Us) + (vlctx_eq : contextRun.context.vlctx = Δ) + {viewΔ : VLCtx} + (viewDefEq : VLCtx.IsDefEq env Us.length Δ viewΔ) + (viewContext : TrExprS.IsUniqueCtx Δ viewΔ) : + ∃ (terminalRun : CandidateContextRun trace.terminalContext) + (viewTerminal : VLCtx), + terminalRun.context.venv = env ∧ + terminalRun.context.lparams = Us ∧ + VLCtx.IsDefEq env Us.length terminalRun.context.vlctx viewTerminal ∧ + TrExprS.IsUniqueCtx terminalRun.context.vlctx viewTerminal ∧ + VLCtx.FVLift' viewΔ viewTerminal 0 + (.skipN .refl trace.spineLength) 0 ∧ + viewTerminal.toCtx = + (VExpr.telN trace.spineLength view').reverse ++ viewΔ.toCtx := by + induction run generalizing viewΔ with + | @terminal Δ context source inferred result source' result' inferred' + checked normalized node => + let terminalRun : CandidateContextRun + (AddInductive.CandidateExprTrace.terminal + context source inferred result checked normalized).terminalContext := by + simpa only [AddInductive.CandidateExprTrace.terminalContext] using + contextRun + refine ⟨terminalRun, viewΔ, venv_eq, lparams_eq, ?_, ?_, .refl, ?_⟩ + · change VLCtx.IsDefEq env Us.length contextRun.context.vlctx viewΔ + rw [vlctx_eq] + exact viewDefEq + · change TrExprS.IsUniqueCtx contextRun.context.vlctx viewΔ + rw [vlctx_eq] + exact viewContext + · simp only [AddInductive.CandidateExprTrace.spineLength, + VExpr.telN, List.reverse_nil, List.nil_append] + | @forallE domain context name binderInfo Δ source inferred body + source' domain' body' inferred' domainView' domainInferred' + storedDomain' bodyΔ storedBody' bodyView' bodyInferred' u v fresh + checked normalized annotations annotationsEq domainCandidate + bodyCandidate node domainRun annotationsRun bodyRun domainType bodyType + bodySource bodyContext domainIH bodyIH => + have storedDomain_tr : contextRun.context.TrExprS + annotations.consumed storedDomain' := by + simpa only [VContext.TrExprS, venv_eq, lparams_eq, vlctx_eq] using + annotationsRun.rhs_tr + have henv : VEnv.WF env := by + simpa only [venv_eq] using contextRun.context.Ewf + have hΔ : OnCtx Δ.toCtx (env.IsType Us.length) := by + simpa only [venv_eq, lparams_eq, vlctx_eq] using + contextRun.context.Δwf.toCtx + have storedDomain_type : env.IsType Us.length Δ.toCtx storedDomain' := by + have annotationDef := annotationsRun.isDefEqU.of_l henv hΔ domainType + exact ⟨u, annotationDef.hasType.2⟩ + have domainDef : env.IsDefEq Us.length Δ.toCtx + domain' domainView' (.sort u) := + domainRun.evidence.isDefEq.toU.of_l henv hΔ domainType + have annotationDef : env.IsDefEq Us.length Δ.toCtx + domain' storedDomain' (.sort u) := + annotationsRun.isDefEqU.of_l henv hΔ domainType + have storedToView : env.IsDefEq Us.length Δ.toCtx + storedDomain' domainView' (.sort u) := + annotationDef.symm.trans domainDef + let nextContextRun := contextRun.pushLocalDecl name binderInfo + annotations.consumed fresh storedDomain' storedDomain_tr (by + change contextRun.context.venv.IsType + contextRun.context.lparams.length + contextRun.context.vlctx.toCtx storedDomain' + rw [venv_eq, lparams_eq, vlctx_eq] + exact storedDomain_type) + have nextVenv : nextContextRun.context.venv = env := by + simp only [nextContextRun, CandidateContextRun.pushLocalDecl_venv, + venv_eq] + have nextLparams : nextContextRun.context.lparams = Us := by + simp only [nextContextRun, CandidateContextRun.pushLocalDecl_lparams, + lparams_eq] + have nextVlctx : nextContextRun.context.vlctx = bodyΔ := by + simp only [nextContextRun, CandidateContextRun.pushLocalDecl_vlctx] + rw [vlctx_eq, bodyContext] + let viewBodyΔ : VLCtx := + (some (context.freshFVarId, annotations.consumed.fvarsList), + .vlam domainView') :: viewΔ + have bodyWF := bodyRun.context_wf + rw [bodyContext] at bodyWF + have bodyViewDefEq : VLCtx.IsDefEq env Us.length bodyΔ viewBodyΔ := by + rw [bodyContext] + exact .cons viewDefEq bodyWF.2.1 (.vlam storedToView) + have bodyViewContext : TrExprS.IsUniqueCtx bodyΔ viewBodyΔ := by + rw [bodyContext] + exact viewContext.cons .vlam + obtain ⟨terminalRun, viewTerminal, terminalVenv, terminalLparams, + terminalViewDefEq, terminalViewContext, terminalViewLift, + terminalViewEq⟩ := + bodyIH nextContextRun nextVenv nextLparams nextVlctx bodyViewDefEq + bodyViewContext + refine ⟨by + simpa only [AddInductive.CandidateExprTrace.terminalContext] using + terminalRun, + viewTerminal, terminalVenv, terminalLparams, terminalViewDefEq, + terminalViewContext, ?_, ?_⟩ + · have headLift : VLCtx.FVLift' viewΔ viewBodyΔ 0 + (.skipN .refl 1) 0 := by + exact VLCtx.FVLift'.skip_fvar + (context.freshFVarId, annotations.consumed.fvarsList) + (.vlam domainView') (.refl : + VLCtx.FVLift' viewΔ viewΔ 0 .refl 0) + simpa only [AddInductive.CandidateExprTrace.spineLength, + Nat.add_comm 1] using + candidateFVLift'_comp headLift terminalViewLift + simpa only [AddInductive.CandidateExprTrace.spineLength, + VExpr.telN, List.reverse_cons, List.singleton_append, + List.append_assoc, viewBodyΔ, VLCtx.toCtx] using terminalViewEq + +/-- Interpret the terminal-sort fact retained by family validation. + +At a terminal node the verified WHNF result translates the exact kernel sort. +At a Pi node the recursively interpreted body is transported from the +annotation-consumed binder context to the candidate-view binder context. Thus +the complete checker-selected candidate view is a Theory type without using a +checked inductive declaration or a caller-supplied view-WF proof. -/ +theorem CandidateExprRun.view_isType_of_terminalSort + {env : VEnv} {Us : List Name} + {candidateContext : AddInductive.Context} {source : Expr} + {trace : AddInductive.CandidateExprTrace candidateContext source} + {Δ : VLCtx} {source' view' inferred' : VExpr} + (run : CandidateExprRun env Us trace Δ source' view' inferred') + (terminal : trace.terminalResult = .sort resultLevel) : + env.IsType Us.length Δ.toCtx view' := by + induction run with + | terminal node => + simp only [AddInductive.CandidateExprTrace.terminalResult] at terminal + rw [terminal] at node + cases node.whnf.rhs_tr with + | sort level_tr => + exact ⟨_, .sort (VLevel.WF.of_ofLevel level_tr)⟩ + | @forallE domain context name binderInfo Δ source inferred body + source' domain' body' inferred' domainView' domainInferred' + storedDomain' bodyΔ storedBody' bodyView' bodyInferred' u v fresh + checked normalized annotations annotationsEq domainCandidate + bodyCandidate node domainRun annotationsRun bodyRun domainType bodyType + bodySource bodyContext domainIH bodyIH => + simp only [AddInductive.CandidateExprTrace.terminalResult] at terminal + have henv : VEnv.WF env := by + simpa only [node.check.venv_eq] using node.check.context.Ewf + have hΔ : VLCtx.WF env Us.length Δ := by + simpa only [node.check.venv_eq, node.check.lparams_eq, + node.check.vlctx_eq] using node.check.context.Δwf + have domainDef : env.IsDefEq Us.length Δ.toCtx + domain' domainView' (.sort u) := + domainRun.evidence.isDefEq.toU.of_l henv hΔ.toCtx domainType + have domainViewType : env.IsType Us.length Δ.toCtx domainView' := + ⟨u, domainDef.hasType.2⟩ + have annotationDef : env.IsDefEq Us.length Δ.toCtx + domain' storedDomain' (.sort u) := + annotationsRun.isDefEqU.of_l henv hΔ.toCtx domainType + have storedToView : env.IsDefEq Us.length Δ.toCtx + storedDomain' domainView' (.sort u) := + annotationDef.symm.trans domainDef + have bodyContextEq : env.IsDefEqCtx Us.length [] + (storedDomain' :: Δ.toCtx) (domainView' :: Δ.toCtx) := + (VLCtx.IsDefEq.cons (.refl henv hΔ) (ofv := none) + (by nofun) (.vlam storedToView)).defeqCtx + have bodyViewTypeStored : env.IsType Us.length + (storedDomain' :: Δ.toCtx) bodyView' := by + simpa only [bodyContext, VLCtx.toCtx] using bodyIH terminal + have bodyViewType : env.IsType Us.length + (domainView' :: Δ.toCtx) bodyView' := by + exact bodyViewTypeStored.defeqDFC henv.ordered bodyContextEq + exact domainViewType.forallE bodyViewType + +/-- Family validation types the checker-selected view first; the retained +candidate equality then transports that fact back to the exact raw Theory +source. This is the declaration-WF fact needed before raw-family insertion. -/ +theorem CandidateExprSemanticRootRun.source_isType_of_terminalSort + {env : VEnv} {Us : List Name} {source : Expr} + {candidate : AddInductive.CandidateExpr source} {source' : VExpr} + (run : CandidateExprSemanticRootRun env Us candidate source') + (terminal : candidate.trace.terminalResult = .sort resultLevel) : + env.IsType Us.length [] source' := by + obtain ⟨_, recursive⟩ := run.recursive + have hview := recursive.view_isType_of_terminalSort terminal + have henv : VEnv.WF env := by + simpa only [run.venv_eq] using run.contextRun.context.Ewf + exact hview.defeqU_l henv trivial recursive.evidence.isDefEq.toU.symm + +/-- Candidate-view parameter binders selected by an exact singleton family +validation run. The split is computed from the retained candidate spine. -/ +def CandidateExprSemanticRootRun.viewParameters + {indType : InductiveType} + {candidate : AddInductive.CandidateExpr indType.type} + (run : CandidateExprSemanticRootRun env Us candidate source') + (validation : AddInductive.CandidateExprTrace.FamilyValidationRun + indType candidate.trace) : List VExpr := + (VExpr.telN candidate.trace.spineLength run.view).take + validation.nparams + +/-- Candidate-view index binders following the validator-selected parameter +prefix. -/ +def CandidateExprSemanticRootRun.viewIndices + {indType : InductiveType} + {candidate : AddInductive.CandidateExpr indType.type} + (run : CandidateExprSemanticRootRun env Us candidate source') + (validation : AddInductive.CandidateExprTrace.FamilyValidationRun + indType candidate.trace) : List VExpr := + (VExpr.telN candidate.trace.spineLength run.view).drop + validation.nparams + +/-- An exact recursive run whose executable main spine preserves the stored +binders. Unlike a whole-expression root equality, this package is strong +enough to expose generation's pointwise binder and terminal-result evidence. -/ +def CandidateExprSpineRun (env : VEnv) (Us : List Name) + {source : Expr} (candidate : AddInductive.CandidateExpr source) + (raw view : VExpr) : Prop := + candidate.trace.storedSpine = true ∧ + ∃ inferred, CandidateExprRun env Us candidate.trace [] raw view inferred + +/-- Retaining the recursive semantic root makes the generation spine a direct +projection once the executable structural gate has succeeded. -/ +theorem CandidateExprSemanticRootRun.spine + (run : CandidateExprSemanticRootRun env Us candidate source') + (storedSpine : candidate.trace.storedSpine = true) : + CandidateExprSpineRun env Us candidate source' run.view := + ⟨storedSpine, run.recursive⟩ + +/-- Turn an exact root translation and a recursive identity witness into the +generation-ready spine package. The root equalities transport the recursive +run out of the verifier's reconstructed context without choosing a different +semantic endpoint. -/ +theorem CandidateExprRootRun.spineOfIdentity + {env : VEnv} {Us : List Name} {source : Expr} + {candidate : AddInductive.CandidateExpr source} {source' : VExpr} + (run : CandidateExprRootRun env Us candidate source' source') + (identity : CandidateExprIdentity candidate.trace) : + CandidateExprSpineRun env Us candidate source' source' := by + refine ⟨identity.storedSpine, ?_⟩ + have source_tr : run.contextRun.context.TrExprS source source' := by + simpa only [VContext.TrExprS, run.venv_eq, run.lparams_eq, + run.vlctx_eq] using run.source_tr + obtain ⟨inferred', ⟨recursive⟩⟩ := + CandidateExprRun.exists_ofIdentity candidate.trace identity + run.contextRun source' source_tr run.whnfFuel run.whnfDepth + refine ⟨inferred', ?_⟩ + simpa only [run.venv_eq, run.lparams_eq, run.vlctx_eq] using recursive + +/-- Retain the exact recursive run selected by an identity-normalizing root. + +All data fields are inherited from the named root and its fixed Theory +endpoint. The existential inferred type remains proof-only, so this constructor +does not use classical choice and does not turn identity into an executable or +semantic oracle. -/ +def CandidateExprRootRun.semanticOfIdentity + {env : VEnv} {Us : List Name} {source : Expr} + {candidate : AddInductive.CandidateExpr source} {source' : VExpr} + (run : CandidateExprRootRun env Us candidate source' source') + (identity : CandidateExprIdentity candidate.trace) : + CandidateExprSemanticRootRun env Us candidate source' where + contextRun := run.contextRun + venv_eq := run.venv_eq + lparams_eq := run.lparams_eq + vlctx_eq := run.vlctx_eq + source_tr := run.source_tr + whnfFuel := run.whnfFuel + whnfDepth := run.whnfDepth + view := source' + recursive := by + have source_tr : run.contextRun.context.TrExprS source source' := by + simpa only [VContext.TrExprS, run.venv_eq, run.lparams_eq, + run.vlctx_eq] using run.source_tr + obtain ⟨inferred, ⟨recursive⟩⟩ := + CandidateExprRun.exists_ofIdentity candidate.trace identity + run.contextRun source' source_tr run.whnfFuel run.whnfDepth + refine ⟨inferred, ?_⟩ + simpa only [run.venv_eq, run.lparams_eq, run.vlctx_eq] using recursive + +theorem CandidateExprSpineRun.evidence + (run : CandidateExprSpineRun env Us candidate raw view) : + ∃ resultType, + TelResultDefEqEvidence env Us.length [] + (VExpr.telN candidate.trace.spineLength raw) + (VExpr.telN candidate.trace.spineLength view) + (VExpr.dropN candidate.trace.spineLength raw) + (VExpr.dropN candidate.trace.spineLength view) resultType := by + obtain ⟨aligned, _, recursive⟩ := run + exact recursive.spineEvidence aligned + +/-- Align extracted candidate components with named raw/view telescope and +result data, then fix the terminal type from a checked right-endpoint typing +judgment. All four alignment premises are syntactic equations. -/ +theorem CandidateExprSpineRun.evidenceAt + (run : CandidateExprSpineRun env Us candidate raw view) + (rawTel : VExpr.telN candidate.trace.spineLength raw = rawBinders) + (viewTel : VExpr.telN candidate.trace.spineLength view = viewBinders) + (rawResult_eq : + VExpr.dropN candidate.trace.spineLength raw = rawResult) + (viewResult_eq : + VExpr.dropN candidate.trace.spineLength view = viewResult) + (rightType : env.HasType Us.length rawBinders.reverse + viewResult expectedType) : + TelResultDefEqEvidence env Us.length [] rawBinders viewBinders + rawResult viewResult expectedType := by + obtain ⟨aligned, _, recursive⟩ := run + obtain ⟨resultType, evidence⟩ := recursive.spineEvidence aligned + have exactEvidence : TelResultDefEqEvidence env Us.length [] + rawBinders viewBinders rawResult viewResult resultType := by + simpa only [rawTel, viewTel, rawResult_eq, viewResult_eq, + VLCtx.toCtx] using evidence + exact exactEvidence.ofRightType recursive.env_wf trivial (by + simpa using rightType) + +end TypeChecker + +namespace VInductDecl + +/-- A one-family normalization candidate validated by compositional verified +normalization evidence at the kernel's two declaration stages. + +The family comparison runs in `env`. Constructor comparisons run in the exact +`typeEnv` obtained by inserting the raw family constant. The positional +`Forall₂` prevents a shorter checker-result list from certifying a declaration. +-/ +structure NormalizationRun {source : VInductDecl} + (norm : Normalization source) (env : VEnv) where + raw : VInductiveType + view : VInductiveType + source_types_eq : source.types = [raw] + view_types_eq : norm.view.types = [view] + family : ∃ A, + TypeChecker.DefEqEvidence env source.uvars [] + raw.type view.type A + typeEnv : VEnv + addType : + env.addConst raw.name raw.toVConstant = some typeEnv + constructors : List.Forall₂ + (fun rawCtor viewCtor => + ∃ A, TypeChecker.DefEqEvidence typeEnv source.uvars [] + rawCtor.type viewCtor.type A) + raw.ctors view.ctors + +/-- Checker-validated family and constructor comparisons establish the +semantic part of the raw/view normalization boundary. -/ +theorem NormalizationRun.wf + (run : NormalizationRun norm env) : norm.WF env := by + refine ⟨run.raw, run.view, run.source_types_eq, run.view_types_eq, ?_, ?_⟩ + · obtain ⟨_, hfamily⟩ := run.family + exact hfamily.isDefEq.toU + · intro envT hadd + have henv : envT = run.typeEnv := by + have : some envT = some run.typeEnv := hadd.symm.trans run.addType + exact Option.some.inj this + subst envT + exact Ix.Theory.Named.List.Forall₂.imp (h := run.constructors) fun _ _ h => by + obtain ⟨_, hctor⟩ := h + exact hctor.isDefEq.toU + +/-- Checker-validated normalization for an arbitrary mutual block. + +Family equalities are interpreted in the common pre-family environment. The +raw family constants are then staged as one exact source-ordered fold, and +all constructor equalities are interpreted in the resulting shared block +environment. -/ +structure NormalizationBlockRun {source : VInductDecl} + (norm : Normalization source) (env blockEnv : VEnv) where + stage : env.stageInductiveTypes source.types = some blockEnv + families : List.Forall₂ + (fun raw view => + (∃ A, TypeChecker.DefEqEvidence env source.uvars [] + raw.type view.type A) ∧ + List.Forall₂ + (fun rawCtor viewCtor => + ∃ A, TypeChecker.DefEqEvidence blockEnv source.uvars [] + rawCtor.type viewCtor.type A) + raw.ctors view.ctors) + source.types norm.view.types + +/-- The verified checker interpretation discharges the complete Theory +mutual-normalization contract without a singleton projection. -/ +theorem NormalizationBlockRun.wf + (run : NormalizationBlockRun norm env blockEnv) : + norm.BlockWF env blockEnv := by + refine ⟨run.stage, ?_⟩ + exact Ix.Theory.Named.List.Forall₂.imp (h := run.families) fun _ _ h => by + refine ⟨h.1.choose_spec.isDefEq.toU, ?_⟩ + exact Ix.Theory.Named.List.Forall₂.imp (h := h.2) fun _ _ hctor => + hctor.choose_spec.isDefEq.toU + +/-- One constructor candidate tied to the corresponding raw Theory constant. +Its expression payload may normalize, but its name, universe arity, and exact +source position remain fixed. -/ +structure CandidateConstructorRun (env : VEnv) (Us : List Name) + {source : Constructor} + (candidate : AddInductive.CandidateConstructor source) + (raw : VConstVal) where + name_eq : source.name = raw.name + uvars_eq : raw.uvars = Us.length + viewType : VExpr + typeRun : TypeChecker.CandidateExprRootRun env Us candidate.type + raw.type viewType + +/-- Replace only the expression payload certified by the constructor run. -/ +def CandidateConstructorRun.view + (run : CandidateConstructorRun env Us candidate raw) : VConstVal := + { raw with type := run.viewType } + +/-- Exact positional certification for a source-indexed constructor list and +the raw Theory constructor list. Unlike `zip`, this type cannot truncate a +longer side or reuse evidence at a different source position. -/ +inductive CandidateConstructorListRun (env : VEnv) (Us : List Name) : + {sources : List Constructor} → + AddInductive.CandidateList AddInductive.CandidateConstructor sources → + List VConstVal → Type where + | nil : CandidateConstructorListRun env Us .nil [] + | cons + (head : CandidateConstructorRun env Us candidate raw) + (tail : CandidateConstructorListRun env Us candidates raws) : + CandidateConstructorListRun env Us + (.cons candidate candidates) (raw :: raws) + +/-- The exact normalized constructor list retained by a positional run. -/ +def CandidateConstructorListRun.views : + CandidateConstructorListRun env Us candidates raws → List VConstVal + | .nil => [] + | .cons head tail => head.view :: tail.views + +/-- Positional certification preserves every constructor header. -/ +theorem CandidateConstructorListRun.sameHeaders + (run : CandidateConstructorListRun env Us candidates raws) : + sameCtorHeaders raws run.views = true := by + induction run with + | nil => rfl + | cons head tail ih => + simp [CandidateConstructorListRun.views, + CandidateConstructorRun.view, sameCtorHeaders, ih] + +/-- Collect the exact checker-produced equality for every positional raw/view +constructor pair. -/ +theorem CandidateConstructorListRun.evidence + (run : CandidateConstructorListRun env Us candidates raws) : + List.Forall₂ + (fun raw view => ∃ A, + TypeChecker.DefEqEvidence env Us.length [] + raw.type view.type A) + raws run.views := by + induction run with + | nil => exact .nil + | cons head tail ih => + exact .cons head.typeRun.evidence ih + +/-- One family candidate certified in the input environment, together with +all of its constructors certified in the exact environment obtained by +inserting the raw family constant. -/ +structure CandidateFamilyRun (env : VEnv) (Us : List Name) + {source : InductiveType} + (candidate : AddInductive.CandidateFamily source) + (raw : VInductiveType) where + name_eq : source.name = raw.name + uvars_eq : raw.uvars = Us.length + viewType : VExpr + typeRun : TypeChecker.CandidateExprRootRun env Us + candidate.familyType.type raw.type viewType + typeEnv : VEnv + addType : env.addConst raw.name raw.toVConstant = some typeEnv + constructors : CandidateConstructorListRun typeEnv Us + candidate.constructors raw.ctors + +/-- Replace only the family and constructor expression payloads named by the +certified candidate runs. -/ +def CandidateFamilyRun.view + (run : CandidateFamilyRun env Us candidate raw) : VInductiveType := + { raw with + type := run.viewType + ctors := run.constructors.views } + +/-- Exact singleton candidate-list certification against one raw Theory +declaration. The singleton kernel-source index rules out partial selection of +a family candidate, and `raw_types_eq` rules out partial selection of a Theory +family. Mutual blocks remain an explicit later generalization. -/ +structure NormalizationCandidateRun (env : VEnv) (Us : List Name) + {source : InductiveType} + (candidate : AddInductive.NormalizationCandidate [source]) + (rawDecl : VInductDecl) where + raw : VInductiveType + raw_types_eq : rawDecl.types = [raw] + uvars_eq : rawDecl.uvars = Us.length + family : CandidateFamilyRun env Us candidate.families.singleton raw + +/-- The Theory declaration obtained from the exact singleton candidate. -/ +def NormalizationCandidateRun.viewDecl + (run : NormalizationCandidateRun env Us candidate rawDecl) : + VInductDecl := + { rawDecl with types := [run.family.view] } + +/-- Candidate-list shape evidence is sufficient to construct the Theory +normalization boundary without `head!`, unchecked `zip`, or an arbitrary view +declaration supplied separately from the candidate. -/ +def NormalizationCandidateRun.normalization + (run : NormalizationCandidateRun env Us candidate rawDecl) : + Normalization rawDecl where + view := run.viewDecl + shape_eq := by + simp only [normalizationShape, NormalizationCandidateRun.viewDecl, + run.raw_types_eq, beq_self_eq_true, Bool.true_and, sameTypeHeaders, + CandidateFamilyRun.view] + simp [run.family.constructors.sameHeaders] + +/-- Assemble the existing semantic normalization certificate from the exact +family and constructor candidate runs. -/ +def NormalizationCandidateRun.normalizationRun + (run : NormalizationCandidateRun env Us candidate rawDecl) : + NormalizationRun run.normalization env where + raw := run.raw + view := run.family.view + source_types_eq := run.raw_types_eq + view_types_eq := rfl + family := by + simpa only [run.uvars_eq, CandidateFamilyRun.view] using + run.family.typeRun.evidence + typeEnv := run.family.typeEnv + addType := run.family.addType + constructors := by + simpa only [run.uvars_eq, CandidateFamilyRun.view] using + run.family.constructors.evidence + +/-- One constructor whose exact recursive candidate semantics are retained, +rather than reconstructed separately for normalization and generation. + +The header remains indexed by the kernel source and raw Theory constant. The +semantic root owns the checker-selected view, its inferred type, and the +recursive run used by both downstream phases. -/ +structure CandidateConstructorSemanticRun (env : VEnv) (Us : List Name) + {source : Constructor} + (candidate : AddInductive.CandidateConstructor source) + (raw : VConstVal) where + name_eq : source.name = raw.name + uvars_eq : raw.uvars = Us.length + type : TypeChecker.CandidateExprSemanticRootRun env Us candidate.type + raw.type + +/-- Project the normalization-facing constructor root without losing its +source or position indices. -/ +def CandidateConstructorSemanticRun.root + (run : CandidateConstructorSemanticRun env Us candidate raw) : + CandidateConstructorRun env Us candidate raw where + name_eq := run.name_eq + uvars_eq := run.uvars_eq + viewType := run.type.view + typeRun := run.type.root + +/-- Exact positional semantic ownership for an arbitrary constructor list. +Every element retains the recursive run selected at that source position; the +list cannot truncate, reorder, or reuse a run for another constructor. -/ +inductive CandidateConstructorSemanticListRun + (env : VEnv) (Us : List Name) : + {sources : List Constructor} → + AddInductive.CandidateList AddInductive.CandidateConstructor sources → + List VConstVal → Type where + | nil : CandidateConstructorSemanticListRun env Us .nil [] + | cons + (head : CandidateConstructorSemanticRun env Us candidate raw) + (tail : CandidateConstructorSemanticListRun env Us candidates raws) : + CandidateConstructorSemanticListRun env Us + (.cons candidate candidates) (raw :: raws) + +/-- Forget only the retained recursive-run payload and recover the existing +normalization-facing positional list. -/ +def CandidateConstructorSemanticListRun.roots : + CandidateConstructorSemanticListRun env Us candidates raws → + CandidateConstructorListRun env Us candidates raws + | .nil => .nil + | .cons head tail => .cons head.root tail.roots + +/-- One family in a mutual normalization candidate. Its type is interpreted +in the common pre-family environment, while every constructor is interpreted +in the single environment obtained after staging the complete raw family +block. -/ +structure CandidateBlockFamilySemanticRun + (env blockEnv : VEnv) (Us : List Name) + {source : InductiveType} + (candidate : AddInductive.CandidateFamily source) + (raw : VInductiveType) where + name_eq : source.name = raw.name + uvars_eq : raw.uvars = Us.length + type : TypeChecker.CandidateExprSemanticRootRun env Us + candidate.familyType.type raw.type + constructors : CandidateConstructorSemanticListRun blockEnv Us + candidate.constructors raw.ctors + +/-- Replace only the expression payloads selected by the retained checker +runs; all family and constructor headers remain raw and source-indexed. -/ +def CandidateBlockFamilySemanticRun.view + (run : CandidateBlockFamilySemanticRun env blockEnv Us candidate raw) : + VInductiveType := + { raw with + type := run.type.view + ctors := run.constructors.roots.views } + +/-- Exact source-order semantic ownership for every family in an arbitrary +block. Both the kernel candidate list and raw Theory list are indices, so +family reordering and truncation are unrepresentable. -/ +inductive CandidateBlockFamilySemanticListRun + (env blockEnv : VEnv) (Us : List Name) : + {sources : List InductiveType} → + AddInductive.CandidateList AddInductive.CandidateFamily sources → + List VInductiveType → Type where + | nil : CandidateBlockFamilySemanticListRun env blockEnv Us .nil [] + | cons + (head : CandidateBlockFamilySemanticRun env blockEnv Us candidate raw) + (tail : CandidateBlockFamilySemanticListRun env blockEnv Us + candidates raws) : + CandidateBlockFamilySemanticListRun env blockEnv Us + (.cons candidate candidates) (raw :: raws) + +/-- Exact normalized family views in source order. -/ +def CandidateBlockFamilySemanticListRun.views : + CandidateBlockFamilySemanticListRun env blockEnv Us candidates raws → + List VInductiveType + | .nil => [] + | .cons head tail => head.view :: tail.views + +/-- Block semantic runs preserve every family and constructor header. -/ +theorem CandidateBlockFamilySemanticListRun.sameHeaders + (run : CandidateBlockFamilySemanticListRun env blockEnv Us + candidates raws) : + sameTypeHeaders raws run.views = true := by + induction run with + | nil => rfl + | cons head tail ih => + simp [CandidateBlockFamilySemanticListRun.views, + CandidateBlockFamilySemanticRun.view, sameTypeHeaders, + head.constructors.roots.sameHeaders, ih] + +/-- Collect the exact family/constructor definitional equalities selected by +the retained semantic checker hierarchy. -/ +theorem CandidateBlockFamilySemanticListRun.evidence + (run : CandidateBlockFamilySemanticListRun env blockEnv Us + candidates raws) : + List.Forall₂ + (fun raw view => + (∃ A, TypeChecker.DefEqEvidence env Us.length [] + raw.type view.type A) ∧ + List.Forall₂ + (fun rawCtor viewCtor => + ∃ A, TypeChecker.DefEqEvidence blockEnv Us.length [] + rawCtor.type viewCtor.type A) + raw.ctors view.ctors) + raws run.views := by + induction run with + | nil => exact .nil + | cons head tail ih => + exact .cons + ⟨head.type.root.evidence, head.constructors.roots.evidence⟩ ih + +/-- Complete semantic ownership for an arbitrary normalization candidate. +The raw staging equation and dependent family list share the same exact raw +declaration; no independently supplied normalized declaration is accepted. -/ +structure NormalizationCandidateBlockSemanticRun + (env blockEnv : VEnv) (Us : List Name) + {sources : List InductiveType} + (candidate : AddInductive.NormalizationCandidate sources) + (rawDecl : VInductDecl) where + uvars_eq : rawDecl.uvars = Us.length + stage : env.stageInductiveTypes rawDecl.types = some blockEnv + families : CandidateBlockFamilySemanticListRun env blockEnv Us + candidate.families rawDecl.types + +/-- Theory declaration selected by the exact block semantic hierarchy. -/ +def NormalizationCandidateBlockSemanticRun.viewDecl + (run : NormalizationCandidateBlockSemanticRun env blockEnv Us + candidate rawDecl) : VInductDecl := + { rawDecl with types := run.families.views } + +/-- Construct the header-preserving Theory normalization boundary selected by +the candidate semantic hierarchy. -/ +def NormalizationCandidateBlockSemanticRun.normalization + (run : NormalizationCandidateBlockSemanticRun env blockEnv Us + candidate rawDecl) : Normalization rawDecl where + view := run.viewDecl + shape_eq := by + simp only [normalizationShape, + NormalizationCandidateBlockSemanticRun.viewDecl, + beq_self_eq_true, Bool.true_and] + exact run.families.sameHeaders + +/-- Project the generic verified normalization run for the same raw block and +shared staged environment. -/ +theorem NormalizationCandidateBlockSemanticRun.normalizationRun + (run : NormalizationCandidateBlockSemanticRun env blockEnv Us + candidate rawDecl) : + NormalizationBlockRun run.normalization env blockEnv where + stage := run.stage + families := by + simpa only [run.uvars_eq, + NormalizationCandidateBlockSemanticRun.normalization, + NormalizationCandidateBlockSemanticRun.viewDecl] using + run.families.evidence + +/-- A singleton-family semantic hierarchy spanning the pre-family candidate, +the exact raw-family insertion, and every post-family constructor candidate. +The normalized expression payloads are selected by retained recursive checker +runs, not by a parallel caller-supplied declaration. -/ +structure CandidateFamilySemanticRun (env : VEnv) (Us : List Name) + {source : InductiveType} + (candidate : AddInductive.CandidateFamily source) + (raw : VInductiveType) where + name_eq : source.name = raw.name + uvars_eq : raw.uvars = Us.length + type : TypeChecker.CandidateExprSemanticRootRun env Us + candidate.familyType.type raw.type + typeEnv : VEnv + addType : env.addConst raw.name raw.toVConstant = some typeEnv + constructors : CandidateConstructorSemanticListRun typeEnv Us + candidate.constructors raw.ctors + +/-- Project the existing normalization-facing family run from the retained +semantic hierarchy. -/ +def CandidateFamilySemanticRun.root + (run : CandidateFamilySemanticRun env Us candidate raw) : + CandidateFamilyRun env Us candidate raw where + name_eq := run.name_eq + uvars_eq := run.uvars_eq + viewType := run.type.view + typeRun := run.type.root + typeEnv := run.typeEnv + addType := run.addType + constructors := run.constructors.roots + +/-- Complete retained semantic ownership for one source-indexed singleton +normalization candidate. This is the generic bridge from translated family and +constructor candidates to `NormalizationCandidateRun`; mutual blocks remain a +later indexed generalization. -/ +structure NormalizationCandidateSemanticRun (env : VEnv) (Us : List Name) + {source : InductiveType} + (candidate : AddInductive.NormalizationCandidate [source]) + (rawDecl : VInductDecl) where + raw : VInductiveType + raw_types_eq : rawDecl.types = [raw] + uvars_eq : rawDecl.uvars = Us.length + family : CandidateFamilySemanticRun env Us candidate.families.singleton raw + +/-- Recover the existing normalization candidate from the retained semantic +hierarchy. -/ +def NormalizationCandidateSemanticRun.root + (run : NormalizationCandidateSemanticRun env Us candidate rawDecl) : + NormalizationCandidateRun env Us candidate rawDecl where + raw := run.raw + raw_types_eq := run.raw_types_eq + uvars_eq := run.uvars_eq + family := run.family.root + +/-- Pre-run semantic evidence for one source-indexed constructor. Its header +is aligned with the raw Theory constant, while the expression input contains +only the verified context and strict source translation needed to let the +retained checker choose the view. -/ +structure CandidateConstructorSemanticInput (env : VEnv) (Us : List Name) + {source : Constructor} + (candidate : AddInductive.CandidateConstructor source) + (raw : VConstVal) where + name_eq : source.name = raw.name + uvars_eq : raw.uvars = Us.length + type : TypeChecker.CandidateExprSemanticRootInput env Us candidate.type + raw.type + +/-- Interpret one constructor input without selecting its view at the call +site. -/ +theorem CandidateConstructorSemanticInput.exists + (input : CandidateConstructorSemanticInput env Us candidate raw) : + Nonempty (CandidateConstructorSemanticRun env Us candidate raw) := by + obtain ⟨type⟩ := input.type.exists + exact ⟨{ + name_eq := input.name_eq + uvars_eq := input.uvars_eq + type := type }⟩ + +/-- Exact source-order semantic inputs for an arbitrary constructor list. +Unlike a pointwise predicate over erased lists, these indices prevent an input +from being reused at another constructor or from silently truncating either +side. -/ +inductive CandidateConstructorSemanticListInput + (env : VEnv) (Us : List Name) : + {sources : List Constructor} → + AddInductive.CandidateList AddInductive.CandidateConstructor sources → + List VConstVal → Type where + | nil : CandidateConstructorSemanticListInput env Us .nil [] + | cons + (head : CandidateConstructorSemanticInput env Us candidate raw) + (tail : CandidateConstructorSemanticListInput env Us candidates raws) : + CandidateConstructorSemanticListInput env Us + (.cons candidate candidates) (raw :: raws) + +/-- Recursively interpret every source-indexed constructor input. -/ +theorem CandidateConstructorSemanticListInput.exists + (input : CandidateConstructorSemanticListInput env Us candidates raws) : + Nonempty (CandidateConstructorSemanticListRun env Us candidates raws) := by + induction input with + | nil => exact ⟨.nil⟩ + | cons head tail ih => + obtain ⟨headRun⟩ := head.exists + obtain ⟨tailRun⟩ := ih + exact ⟨.cons headRun tailRun⟩ + +/-- Pre-run semantic evidence for one family in a shared mutual stage. +Family types use `env`; all constructor types use the same `blockEnv` after +every raw family has been staged. -/ +structure CandidateBlockFamilySemanticInput + (env blockEnv : VEnv) (Us : List Name) + {source : InductiveType} + (candidate : AddInductive.CandidateFamily source) + (raw : VInductiveType) where + name_eq : source.name = raw.name + uvars_eq : raw.uvars = Us.length + type : TypeChecker.CandidateExprSemanticRootInput env Us + candidate.familyType.type raw.type + constructors : CandidateConstructorSemanticListInput blockEnv Us + candidate.constructors raw.ctors + +/-- Interpret one family and its complete constructor list without selecting +a normalized expression at the call site. -/ +theorem CandidateBlockFamilySemanticInput.exists + (input : CandidateBlockFamilySemanticInput env blockEnv Us + candidate raw) : + Nonempty (CandidateBlockFamilySemanticRun env blockEnv Us + candidate raw) := by + obtain ⟨type⟩ := input.type.exists + obtain ⟨constructors⟩ := input.constructors.exists + exact ⟨{ + name_eq := input.name_eq + uvars_eq := input.uvars_eq + type + constructors }⟩ + +/-- Exact source-order semantic inputs for every family in a mutual block. -/ +inductive CandidateBlockFamilySemanticListInput + (env blockEnv : VEnv) (Us : List Name) : + {sources : List InductiveType} → + AddInductive.CandidateList AddInductive.CandidateFamily sources → + List VInductiveType → Type where + | nil : CandidateBlockFamilySemanticListInput env blockEnv Us .nil [] + | cons + (head : CandidateBlockFamilySemanticInput env blockEnv Us candidate raw) + (tail : CandidateBlockFamilySemanticListInput env blockEnv Us + candidates raws) : + CandidateBlockFamilySemanticListInput env blockEnv Us + (.cons candidate candidates) (raw :: raws) + +/-- Interpret every family and constructor input in lockstep. -/ +theorem CandidateBlockFamilySemanticListInput.exists + (input : CandidateBlockFamilySemanticListInput env blockEnv Us + candidates raws) : + Nonempty (CandidateBlockFamilySemanticListRun env blockEnv Us + candidates raws) := by + induction input with + | nil => exact ⟨.nil⟩ + | cons head tail ih => + obtain ⟨headRun⟩ := head.exists + obtain ⟨tailRun⟩ := ih + exact ⟨.cons headRun tailRun⟩ + +/-- Complete verified semantic input for an arbitrary normalization +candidate. The exact raw family list owns both the all-family staging fold and +the dependent family interpretation, ruling out a reordered staging witness. -/ +structure NormalizationCandidateBlockSemanticInput + (env blockEnv : VEnv) (Us : List Name) + {sources : List InductiveType} + (candidate : AddInductive.NormalizationCandidate sources) + (rawDecl : VInductDecl) where + uvars_eq : rawDecl.uvars = Us.length + stage : env.stageInductiveTypes rawDecl.types = some blockEnv + families : CandidateBlockFamilySemanticListInput env blockEnv Us + candidate.families rawDecl.types + +/-- Automatically interpret the complete mutual semantic hierarchy. -/ +theorem NormalizationCandidateBlockSemanticInput.exists + (input : NormalizationCandidateBlockSemanticInput env blockEnv Us + candidate rawDecl) : + Nonempty (NormalizationCandidateBlockSemanticRun env blockEnv Us + candidate rawDecl) := by + obtain ⟨families⟩ := input.families.exists + exact ⟨{ + uvars_eq := input.uvars_eq + stage := input.stage + families }⟩ + +/-- A mutual semantic hierarchy paired with the exact arbitrary-length +producer traversals that selected the same dependent candidate. -/ +structure ProducedNormalizationCandidateBlockSemanticRun + (familyContext constructorContext : AddInductive.Context) + (env blockEnv : VEnv) (Us : List Name) + {sources : List InductiveType} + (candidate : AddInductive.NormalizationCandidate sources) + (rawDecl : VInductDecl) where + semantic : NormalizationCandidateBlockSemanticRun env blockEnv Us + candidate rawDecl + familyTypesProduced : AddInductive.CandidateFamilyTypeListProduced + familyContext candidate.families.familyTypes + familiesProduced : AddInductive.CandidateFamilyListProduced + constructorContext candidate.families.familyTypes candidate.families + +/-- Combine verified semantic inputs with exact producer provenance for the +same source-indexed mutual candidate. -/ +theorem NormalizationCandidateBlockSemanticInput.exists_ofProduced + (input : NormalizationCandidateBlockSemanticInput env blockEnv Us + candidate rawDecl) + (familyTypesProduced : AddInductive.CandidateFamilyTypeListProduced + familyContext candidate.families.familyTypes) + (familiesProduced : AddInductive.CandidateFamilyListProduced + constructorContext candidate.families.familyTypes candidate.families) : + Nonempty (ProducedNormalizationCandidateBlockSemanticRun + familyContext constructorContext env blockEnv Us candidate rawDecl) := by + obtain ⟨semantic⟩ := input.exists + exact ⟨{ + semantic + familyTypesProduced + familiesProduced }⟩ + +/-- One validated singleton family stage derived from a verified entry +candidate context and the exact kernel/Theory family insertion. + +The family validator selects the parameter/index split and terminal sort. The +retained candidate semantics then prove the raw family constant well formed; +that proof extends the entry `TrEnv` and constructs the post-family verifier +context. No independently verified post-family `VEnvs` is an input. -/ +structure CandidateFamilyStagedInput + (familyContext constructorContext : AddInductive.Context) + (env : VEnv) (Us : List Name) + {source : InductiveType} + (candidate : AddInductive.CandidateFamilyType source) + (raw : VInductiveType) + (preFamily : TypeChecker.CandidateSemanticStage familyContext env Us) + where + name_eq : source.name = raw.name + uvars_eq : raw.uvars = Us.length + type : TypeChecker.CandidateExprStagedInput preFamily + candidate.type raw.type + validation : AddInductive.CandidateExprTrace.FamilyValidationRun + source candidate.type.trace + typeEnv : VEnv + addInduct : AddInductConstant .induct familyContext.env.constants env + raw.toVConstVal constructorContext.env.constants typeEnv + /-- The staged family environment has not yet completed a new projection + artifact; any already-complete host structure remains backed by a registered + Theory view. -/ + projectionReady : ProjectionReady constructorContext.env typeEnv + structureEtaReady : StructureEtaReady constructorContext.env typeEnv + family_lctx_eq : familyContext.lctx = {} + constructorContext_eq : constructorContext = + { familyContext with env := constructorContext.env } + quotInit_eq : constructorContext.env.quotInit = + familyContext.env.quotInit + name_not_reflected : raw.name ∉ VEnv.reflectedPrimitiveNames + name_not_primitive : + Environment.primitives.contains raw.name = false + +/-- Family validation plus retained candidate semantics prove the exact raw +Theory constant suitable for insertion. The semantic view remains hidden +under `Nonempty`; elimination is only into this proposition. -/ +theorem CandidateFamilyStagedInput.rawWF + (input : CandidateFamilyStagedInput familyContext constructorContext + env Us candidate raw preFamily) : + raw.toVConstant.WF env := by + obtain ⟨semantic⟩ := input.type.rootInput.exists + show env.IsType raw.uvars [] raw.type + simpa only [input.uvars_eq] using + semantic.source_isType_of_terminalSort input.validation.terminal_eq + +/-- The verifier context after inserting the validated raw family constant. +Primitive reflection and safety are preserved because the new family name is +not a kernel or reflected primitive. -/ +def CandidateFamilyStagedInput.postContext + (input : CandidateFamilyStagedInput familyContext constructorContext + env Us candidate raw preFamily) : TypeChecker.VContext where + env := constructorContext.env + lctx := constructorContext.lctx + lparams := constructorContext.lparams + safety := constructorContext.safety + fuel := constructorContext.fuel + venv := input.typeEnv + hasPrimitives := by + have H : env.HasPrimitives := by + simpa only [preFamily.venv_eq] using + preFamily.contextRun.context.hasPrimitives + exact VEnv.HasPrimitives.addConst H + input.name_not_reflected input.addInduct.env_add + safePrimitives := by + intro n ci + have preMapWF : familyContext.env.constants.WF := by + simpa only [preFamily.contextRun.context_env] using + preFamily.contextRun.context.trenv.map_wf + exact TypeChecker.AddInductConstant.safePrimitives input.addInduct + (n := n) (ci := ci) preMapWF (fun hfind hprim => by + apply preFamily.contextRun.context.safePrimitives + · simpa only [preFamily.contextRun.context_env] using hfind + · exact hprim) + input.name_not_primitive + trenv := by + have preTr : TrEnv' familyContext.safety familyContext.env.constants + familyContext.env.quotInit env := by + simpa only [TrEnv, preFamily.contextRun.context_safety, + preFamily.contextRun.context_env, preFamily.venv_eq] using + preFamily.contextRun.context.trenv + have postTr := TrEnv'.inductStaging input.addInduct input.rawWF preTr + change TrEnv' constructorContext.safety constructorContext.env.constants + constructorContext.env.quotInit input.typeEnv + rw [show constructorContext.safety = familyContext.safety by + rw [input.constructorContext_eq]] + rw [input.quotInit_eq] + exact postTr + projectionReady := input.projectionReady + structureEtaReady := input.structureEtaReady + mlctx := .nil + mlctx_wf := trivial + lctx_eq := by + change ({} : LocalContext) = constructorContext.lctx + rw [input.constructorContext_eq, input.family_lctx_eq] + +/-- The exact post-family candidate context constructed from family +validation, rather than supplied by a second verifier setup. -/ +def CandidateFamilyStagedInput.postContextRun + (input : CandidateFamilyStagedInput familyContext constructorContext + env Us candidate raw preFamily) : + TypeChecker.CandidateContextRun constructorContext := + TypeChecker.CandidateContextRun.ofVContext constructorContext + input.postContext (by rfl) + (TypeChecker.VState.WF.empty_of_reserves input.postContext (by + intro fv hfv + change fv ∈ VLCtx.fvars ([] : VLCtx) at hfv + simp at hfv)) + (by + rw [input.constructorContext_eq] + exact preFamily.contextRun.namePrefix_ne) + +/-- Shared post-family semantic stage consumed by every constructor position. +Its implementation and Theory environments are fixed by the exact family +insertion above. -/ +def CandidateFamilyStagedInput.postFamily + (input : CandidateFamilyStagedInput familyContext constructorContext + env Us candidate raw preFamily) : + TypeChecker.CandidateSemanticStage constructorContext input.typeEnv Us where + contextRun := input.postContextRun + venv_eq := rfl + lparams_eq := by + rw [TypeChecker.CandidateContextRun.context_lparams] + calc + constructorContext.lparams = familyContext.lparams := by + rw [input.constructorContext_eq] + _ = preFamily.contextRun.context.lparams := + preFamily.contextRun.context_lparams.symm + _ = Us := preFamily.lparams_eq + vlctx_eq := rfl + +/-- Recover the exact verified pre-family context at the end of the family +telescope. + +Constructor validation starts from this local telescope after changing only +the kernel/Theory environment to the staged post-family pair. D3 reuses the +pre-change context to replay family-free constructor checks; no local +declaration or fresh identifier is reconstructed. -/ +theorem CandidateFamilyStagedInput.preValidationContextRun + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.CandidateFamilyType source} + {raw : VInductiveType} + {preFamily : TypeChecker.CandidateSemanticStage familyContext env Us} + (_input : CandidateFamilyStagedInput familyContext constructorContext + env Us candidate raw preFamily) + (semantic : TypeChecker.CandidateExprSemanticRootRun env Us + candidate.type raw.type) : + ∃ preRun : TypeChecker.CandidateContextRun + candidate.type.trace.terminalContext, + preRun.context.venv = env ∧ + preRun.context.lparams = Us := by + obtain ⟨inferred, recursive⟩ := semantic.recursive + exact recursive.terminalContextRun semantic.contextRun semantic.venv_eq + semantic.lparams_eq semantic.vlctx_eq + +/-- Rebuild the verified constructor-validation context from the exact +pre-family terminal context run. + +The returned run preserves the implementation local context definitionally; +only the kernel/Theory environment and the primitive evidence are changed to +the staged post-family pair. -/ +theorem CandidateFamilyStagedInput.validationContextRunFromPre + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.CandidateFamilyType source} + {raw : VInductiveType} + {preFamily : TypeChecker.CandidateSemanticStage familyContext env Us} + (input : CandidateFamilyStagedInput familyContext constructorContext + env Us candidate raw preFamily) + (terminalRun : TypeChecker.CandidateContextRun + candidate.type.trace.terminalContext) + (terminalVenv : terminalRun.context.venv = env) + (terminalLparams : terminalRun.context.lparams = Us) : + ∃ validationRun : TypeChecker.CandidateContextRun + { candidate.type.trace.terminalContext with + env := constructorContext.env }, + validationRun.context.venv = input.typeEnv ∧ + validationRun.context.lparams = Us ∧ + validationRun.context.vlctx = terminalRun.context.vlctx := by + have terminalMLWF : terminalRun.context.mlctx.WF env Us := by + simpa only [terminalVenv, terminalLparams] using + terminalRun.context.mlctx_wf + have postMLWF : terminalRun.context.mlctx.WF input.typeEnv Us := + terminalMLWF.mono (VEnv.addConst_le input.addInduct.env_add) + have validationSafety : terminalRun.context.safety = + input.postContext.safety := by + calc + terminalRun.context.safety = + candidate.type.trace.terminalContext.safety := + terminalRun.context_safety + _ = candidate.type.context.safety := + candidate.type.trace.terminalContext_safety + _ = familyContext.safety := by rw [input.type.context_eq] + _ = constructorContext.safety := by rw [input.constructorContext_eq] + _ = input.postContext.safety := rfl + let validationContext : TypeChecker.VContext := + { terminalRun.context with + env := constructorContext.env + venv := input.typeEnv + hasPrimitives := input.postContext.hasPrimitives + safePrimitives := input.postContext.safePrimitives + trenv := by + have postEnv : input.postContext.env = constructorContext.env := rfl + have postVenv : input.postContext.venv = input.typeEnv := rfl + simpa only [validationSafety, postEnv, postVenv] using + input.postContext.trenv + projectionReady := input.postContext.projectionReady + structureEtaReady := input.postContext.structureEtaReady + mlctx_wf := by + simpa only [terminalLparams] using postMLWF } + have validationContextEq : validationContext.toContext = + ({ candidate.type.trace.terminalContext with + env := constructorContext.env } : AddInductive.Context).toTypeChecker := by + calc + validationContext.toContext = + { terminalRun.context.toContext with + env := constructorContext.env } := rfl + _ = { candidate.type.trace.terminalContext.toTypeChecker with + env := constructorContext.env } := + congrArg (fun c : TypeChecker.Context => + { c with env := constructorContext.env }) terminalRun.context_eq + _ = ({ candidate.type.trace.terminalContext with + env := constructorContext.env } : AddInductive.Context).toTypeChecker := + rfl + let validationRun : TypeChecker.CandidateContextRun + { candidate.type.trace.terminalContext with + env := constructorContext.env } := + TypeChecker.CandidateContextRun.ofVContext _ validationContext + validationContextEq + (TypeChecker.VState.WF.empty_of_reserves validationContext (by + intro fv hfv + exact terminalRun.state_wf.ngen_wf fv (by + simpa only [validationContext] using hfv))) + terminalRun.namePrefix_ne + exact ⟨validationRun, rfl, terminalLparams, rfl⟩ + +/-- Rebuild the verified context in which constructor validation actually +runs. + +Family candidates are interpreted before the raw family is inserted, so the +recursive run reaches the correct local telescope in the pre-family Theory +environment. Constructor validation keeps that exact implementation local +context while replacing only the kernel/Theory environment with the staged +post-family pair. Monotonicity of local-context verification justifies that +replacement; no local declaration, free-variable identifier, or binder order +is regenerated. -/ +theorem CandidateFamilyStagedInput.validationContextRun + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.CandidateFamilyType source} + {raw : VInductiveType} + {preFamily : TypeChecker.CandidateSemanticStage familyContext env Us} + (input : CandidateFamilyStagedInput familyContext constructorContext + env Us candidate raw preFamily) + (semantic : TypeChecker.CandidateExprSemanticRootRun env Us + candidate.type raw.type) : + ∃ validationRun : TypeChecker.CandidateContextRun + { candidate.type.trace.terminalContext with + env := constructorContext.env }, + validationRun.context.venv = input.typeEnv ∧ + validationRun.context.lparams = Us := by + obtain ⟨terminalRun, terminalVenv, terminalLparams⟩ := + input.preValidationContextRun semantic + obtain ⟨validationRun, validationVenv, validationLparams, _⟩ := + input.validationContextRunFromPre terminalRun terminalVenv terminalLparams + exact ⟨validationRun, validationVenv, validationLparams⟩ + +/-- One source-indexed constructor interpreted in the shared post-family +stage. Header equality and universe alignment stay attached to the exact raw +constructor position; the expression payload contains no independently +verified context and no caller-selected semantic view. -/ +structure CandidateConstructorStagedInput + {candidateContext : AddInductive.Context} {env : VEnv} {Us : List Name} + (stage : TypeChecker.CandidateSemanticStage candidateContext env Us) + {source : Constructor} + (candidate : AddInductive.CandidateConstructor source) + (raw : VConstVal) where + name_eq : source.name = raw.name + uvars_eq : raw.uvars = Us.length + type : TypeChecker.CandidateExprStagedInput stage candidate.type raw.type + +/-- Forget only the shared-stage presentation and recover the established +constructor semantic input. -/ +def CandidateConstructorStagedInput.semanticInput + {candidateContext : AddInductive.Context} {env : VEnv} {Us : List Name} + {source : Constructor} + {candidate : AddInductive.CandidateConstructor source} + {raw : VConstVal} + {stage : TypeChecker.CandidateSemanticStage candidateContext env Us} + (input : CandidateConstructorStagedInput stage candidate raw) : + CandidateConstructorSemanticInput env Us candidate raw where + name_eq := input.name_eq + uvars_eq := input.uvars_eq + type := input.type.rootInput + +/-- Exact source-order translations for every constructor in one shared +post-family stage. The dependent indices enforce length, order, source, raw +header, and candidate alignment without `zip` or list lookup. -/ +inductive CandidateConstructorStagedListInput + {candidateContext : AddInductive.Context} {env : VEnv} {Us : List Name} + (stage : TypeChecker.CandidateSemanticStage candidateContext env Us) : + {sources : List Constructor} → + AddInductive.CandidateList AddInductive.CandidateConstructor sources → + List VConstVal → Type where + | nil : CandidateConstructorStagedListInput stage .nil [] + | cons + (head : CandidateConstructorStagedInput stage candidate raw) + (tail : CandidateConstructorStagedListInput stage candidates raws) : + CandidateConstructorStagedListInput stage + (.cons candidate candidates) (raw :: raws) + +/-- Convert the staged, source-indexed constructor translations to the +existing recursive semantic-input representation. -/ +def CandidateConstructorStagedListInput.semanticInput + {candidateContext : AddInductive.Context} {env : VEnv} {Us : List Name} + {stage : TypeChecker.CandidateSemanticStage candidateContext env Us} + {sources : List Constructor} + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor sources} + {raws : List VConstVal} + (input : CandidateConstructorStagedListInput stage candidates raws) : + CandidateConstructorSemanticListInput env Us candidates raws := + match input with + | .nil => CandidateConstructorSemanticListInput.nil + | .cons head tail => + CandidateConstructorSemanticListInput.cons + head.semanticInput tail.semanticInput + +/-- Pre-run semantic evidence for a complete singleton family position. The +family type is interpreted in the input environment and its constructor list +in the exact environment obtained by inserting the raw family constant. -/ +structure CandidateFamilySemanticInput (env : VEnv) (Us : List Name) + {source : InductiveType} + (candidate : AddInductive.CandidateFamily source) + (raw : VInductiveType) where + name_eq : source.name = raw.name + uvars_eq : raw.uvars = Us.length + type : TypeChecker.CandidateExprSemanticRootInput env Us + candidate.familyType.type raw.type + typeEnv : VEnv + addType : env.addConst raw.name raw.toVConstant = some typeEnv + constructors : CandidateConstructorSemanticListInput typeEnv Us + candidate.constructors raw.ctors + +/-- Interpret the family root and all post-insertion constructor roots from +their exact pre-run inputs. -/ +theorem CandidateFamilySemanticInput.exists + (input : CandidateFamilySemanticInput env Us candidate raw) : + Nonempty (CandidateFamilySemanticRun env Us candidate raw) := by + obtain ⟨type⟩ := input.type.exists + obtain ⟨constructors⟩ := input.constructors.exists + exact ⟨{ + name_eq := input.name_eq + uvars_eq := input.uvars_eq + type := type + typeEnv := input.typeEnv + addType := input.addType + constructors := constructors }⟩ + +/-- Pre-run semantic evidence for one source-indexed singleton normalization +candidate. The source declaration and candidate list indices rule out an +unrelated raw family or a partial constructor list. -/ +structure NormalizationCandidateSemanticInput (env : VEnv) (Us : List Name) + {source : InductiveType} + (candidate : AddInductive.NormalizationCandidate [source]) + (rawDecl : VInductDecl) where + raw : VInductiveType + raw_types_eq : rawDecl.types = [raw] + uvars_eq : rawDecl.uvars = Us.length + family : CandidateFamilySemanticInput env Us + candidate.families.singleton raw + +/-- Automatically interpret the complete singleton semantic hierarchy from +its verified, source-indexed inputs. -/ +theorem NormalizationCandidateSemanticInput.exists + (input : NormalizationCandidateSemanticInput env Us candidate rawDecl) : + Nonempty (NormalizationCandidateSemanticRun env Us candidate rawDecl) := by + obtain ⟨family⟩ := input.family.exists + exact ⟨{ + raw := input.raw + raw_types_eq := input.raw_types_eq + uvars_eq := input.uvars_eq + family := family }⟩ + +/-- The automatic semantic hierarchy paired with the exact executable +family-type and constructor-list traversals that selected the same dependent +candidate. The two producer contexts are explicit because family types are +checked before raw-family insertion and constructors after it. -/ +structure ProducedNormalizationCandidateSemanticRun + (familyContext constructorContext : AddInductive.Context) + (env : VEnv) (Us : List Name) + {source : InductiveType} + (candidate : AddInductive.NormalizationCandidate [source]) + (rawDecl : VInductDecl) where + semantic : NormalizationCandidateSemanticRun env Us candidate rawDecl + familyTypesProduced : AddInductive.CandidateFamilyTypeListProduced + familyContext + (.cons candidate.families.singleton.familyType .nil) + familiesProduced : AddInductive.CandidateFamilyListProduced + constructorContext + (.cons candidate.families.singleton.familyType .nil) + candidate.families + +/-- Combine exact arbitrary-length producer witnesses with verified semantic +inputs for the same source-indexed singleton candidate. Operational evidence +selects the candidate; only the retained checker interpreter supplies Theory +meaning. -/ +theorem NormalizationCandidateSemanticInput.exists_ofProduced + (input : NormalizationCandidateSemanticInput env Us candidate rawDecl) + (familyTypesProduced : AddInductive.CandidateFamilyTypeListProduced + familyContext + (.cons candidate.families.singleton.familyType .nil)) + (familiesProduced : AddInductive.CandidateFamilyListProduced + constructorContext + (.cons candidate.families.singleton.familyType .nil) + candidate.families) : + Nonempty (ProducedNormalizationCandidateSemanticRun + familyContext constructorContext env Us candidate rawDecl) := by + obtain ⟨semantic⟩ := input.exists + exact ⟨{ + semantic := semantic + familyTypesProduced := familyTypesProduced + familiesProduced := familiesProduced }⟩ + +/-- The complete family-validated semantic input for a produced singleton +candidate. + +Only the entry verifier alignment is supplied. The exact singleton family +validation and raw-family insertion derive the post-family verified stage; +constructor positions then supply strict translations and fuel equalities in +that derived stage. No normalized view, post-family `VEnvs.WF`, semantic run, +declaration-WF proof, or generation package is an input. -/ +structure StagedNormalizationCandidateSemanticInput + (familyContext constructorContext : AddInductive.Context) + (env : VEnv) (Us : List Name) + {source : InductiveType} + (candidate : AddInductive.NormalizationCandidate [source]) + (rawDecl : VInductDecl) where + raw : VInductiveType + raw_types_eq : rawDecl.types = [raw] + declaration_uvars_eq : rawDecl.uvars = Us.length + preFamily : TypeChecker.CandidateSemanticStage familyContext env Us + family : CandidateFamilyStagedInput familyContext constructorContext env Us + candidate.families.singleton.familyType raw preFamily + validation_nparams_eq : family.validation.nparams = rawDecl.nparams + constructorValidation : AddInductive.ConstructorValidationRun + source family.validation.stats false + { candidate.families.singleton.familyType.type.trace.terminalContext with + env := constructorContext.env } + constructors : CandidateConstructorStagedListInput family.postFamily + candidate.families.singleton.constructors raw.ctors + familyTypesProduced : AddInductive.CandidateFamilyTypeListProduced + familyContext + (.cons candidate.families.singleton.familyType .nil) + familiesProduced : AddInductive.CandidateFamilyListProduced + constructorContext + (.cons candidate.families.singleton.familyType .nil) + candidate.families + +/-- The staged owner retains exactly the successful executable constructor +validation that selected its source-indexed constructor list. -/ +theorem StagedNormalizationCandidateSemanticInput.constructorValidation_run + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (input : StagedNormalizationCandidateSemanticInput familyContext + constructorContext env Us candidate rawDecl) : + AddInductive.checkConstructors #[source] input.family.validation.stats + false + { candidate.families.singleton.familyType.type.trace.terminalContext with + env := constructorContext.env } = .ok () := + input.constructorValidation.run + +/-- Project the established semantic-input hierarchy from the consolidated +two-stage owner. This projection remains data-free with respect to checker +semantics: it only rearranges verified stage and translation evidence. -/ +def StagedNormalizationCandidateSemanticInput.semanticInput + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (input : StagedNormalizationCandidateSemanticInput familyContext + constructorContext env Us candidate rawDecl) : + NormalizationCandidateSemanticInput env Us candidate rawDecl where + raw := input.raw + raw_types_eq := input.raw_types_eq + uvars_eq := input.declaration_uvars_eq + family := { + name_eq := input.family.name_eq + uvars_eq := input.family.uvars_eq + type := input.family.type.rootInput + typeEnv := input.family.typeEnv + addType := input.family.addInduct.env_add + constructors := input.constructors.semanticInput } + +/-- Interpret a complete produced singleton candidate from its entry stage and +derived family-validation stage. The result stays in `Nonempty`; in particular, this theorem +does not use choice to expose a semantic run as executable data. -/ +theorem StagedNormalizationCandidateSemanticInput.exists + {familyContext constructorContext : AddInductive.Context} + {env : VEnv} {Us : List Name} {source : InductiveType} + {candidate : AddInductive.NormalizationCandidate [source]} + {rawDecl : VInductDecl} + (input : StagedNormalizationCandidateSemanticInput familyContext + constructorContext env Us candidate rawDecl) : + Nonempty (ProducedNormalizationCandidateSemanticRun + familyContext constructorContext env Us candidate rawDecl) := + input.semanticInput.exists_ofProduced input.familyTypesProduced + input.familiesProduced + +/-- Forget executable list provenance and expose the existing normalization +root selected by the automatic semantic hierarchy. -/ +def ProducedNormalizationCandidateSemanticRun.root + (run : ProducedNormalizationCandidateSemanticRun + familyContext constructorContext env Us candidate rawDecl) : + NormalizationCandidateRun env Us candidate rawDecl := + run.semantic.root + +/-- Checker-produced semantic evidence for one positional raw/view +constructor pair. This has the same four-way declared/emitted split as +`NormalizedCtor.WF`, but keeps every equality in compositional evidence form +until the final Theory boundary. -/ +structure NormalizedCtorRun {source : VInductDecl} + (block : NormalizedChecked source) (ctor : NormalizedCtor) + (env : VEnv) where + declaredTel : TypeChecker.TelDefEqEvidence env source.uvars [] + (ctor.declaredBinders source.nparams) (ctor.viewBinders block) + declaredResult : TypeChecker.DefEqEvidence env source.uvars + (ctor.declaredBinders source.nparams).reverse + (ctor.rawResult source.nparams) (ctor.resultTarget block) + (.sort block.checked.resultLevel) + emittedTel : TypeChecker.TelDefEqEvidence env source.uvars [] + (ctor.emittedBinders block) (ctor.viewBinders block) + emittedResult : TypeChecker.DefEqEvidence env source.uvars + (ctor.emittedBinders block).reverse + (ctor.rawResult source.nparams) (ctor.resultTarget block) + (.sort block.checked.resultLevel) + +theorem NormalizedCtorRun.wf + (run : NormalizedCtorRun block ctor env) : + ctor.WF block env where + declaredTel := run.declaredTel.telDefEq + declaredResult := run.declaredResult.isDefEq + emittedTel := run.emittedTel.telDefEq + emittedResult := run.emittedResult.isDefEq + +/-- Complete checker-side assembler for a generation-ready candidate. + +The exact family insertion state is named once. This lets a producer check +constructor evidence in that state and lets `.wf` discharge the universally +quantified post-family environment in `GenerationChecked.WF` by equality, +without an oracle or an assumed transaction. -/ +structure GenerationRun {source : VInductDecl} + (generation : GenerationChecked source) (env : VEnv) where + normalization : NormalizationRun generation.block.normalization env + checked : generation.block.checked.WF env + familyTel : TypeChecker.TelDefEqEvidence env source.uvars [] + (generation.block.rawParams ++ generation.block.rawIndices) + (generation.block.checked.params ++ generation.block.checked.indices) + familyResult : TypeChecker.DefEqEvidence env source.uvars + (generation.block.rawParams ++ generation.block.rawIndices).reverse + generation.block.rawResult (.sort generation.block.checked.resultLevel) + (.sort (.succ generation.block.checked.resultLevel)) + typeEnv : VEnv + addType : env.addConst generation.block.sourceType.name + generation.block.sourceType.toVConstant = some typeEnv + constructors : + ∀ ctor ∈ generation.block.ctorPairs, + NormalizedCtorRun generation.block ctor typeEnv + +/-- Assemble the complete Theory generation certificate from exact +checker-produced normalization, telescope, result, and constructor evidence. -/ +theorem GenerationRun.wf + (run : GenerationRun generation env) : + generation.WF env := by + refine { + blockWF := ⟨run.normalization.wf, run.checked⟩ + familyTel := run.familyTel.telDefEq + familyResult := run.familyResult.isDefEq + ctors := ?_ } + intro envT hadd ctor hctor + have henv : envT = run.typeEnv := by + have : some envT = some run.typeEnv := hadd.symm.trans run.addType + exact Option.some.inj this + subst envT + exact (run.constructors ctor hctor).wf + +/-- Exact family-spine evidence extracted from the source-indexed singleton +normalization candidate and aligned with the components retained by dependent +inductive analysis. -/ +structure CandidateFamilyGenerationRun + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (normalization : NormalizationCandidateRun env Us candidate source) + (generation : GenerationChecked source) where + spine : TypeChecker.CandidateExprSpineRun env Us + candidate.families.singleton.familyType.type + normalization.raw.type normalization.family.viewType + rawTel : VExpr.telN + candidate.families.singleton.familyType.type.trace.spineLength + normalization.raw.type = + generation.block.rawParams ++ generation.block.rawIndices + rawResult : VExpr.dropN + candidate.families.singleton.familyType.type.trace.spineLength + normalization.raw.type = generation.block.rawResult + viewResult : VExpr.dropN + candidate.families.singleton.familyType.type.trace.spineLength + normalization.family.viewType = + .sort generation.block.checked.resultLevel + +/-- Extract the complete family telescope/result certificate at the exact +components consumed by `GenerationRun`. -/ +theorem CandidateFamilyGenerationRun.evidence + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + {normalization : NormalizationCandidateRun env Us candidate source} + {generation : GenerationChecked source} + (run : CandidateFamilyGenerationRun normalization generation) + (viewType_eq : normalization.family.viewType = + generation.block.checked.type.type) : + TypeChecker.TelResultDefEqEvidence env Us.length [] + (generation.block.rawParams ++ generation.block.rawIndices) + (generation.block.checked.params ++ generation.block.checked.indices) + generation.block.rawResult + (.sort generation.block.checked.resultLevel) + (.sort (.succ generation.block.checked.resultLevel)) := + run.spine.evidenceAt run.rawTel (by + rw [viewType_eq, generation.block.checked.type_eq, + ← VExpr.forallN_append] + apply TypeChecker.candidateTelN_of_dropN_terminal (B := + .sort generation.block.checked.resultLevel) trivial + simpa only [viewType_eq, generation.block.checked.type_eq, + ← VExpr.forallN_append] using run.viewResult) + run.rawResult run.viewResult (by + apply VEnv.HasType.sort + simpa only [← generation.block.uvars_eq, + normalization.uvars_eq] using + generation.block.checked.direct_anatomy.2.2.1) + +/-- Family generation alignment whose spine is projected directly from the +retained semantic hierarchy. Callers provide only the executable structural +gate and the component equations required by `GenerationChecked`; they cannot +substitute a second recursive run or a different normalized view. -/ +structure CandidateFamilySemanticGenerationRun + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (normalization : NormalizationCandidateSemanticRun env Us candidate source) + (generation : GenerationChecked source) where + storedSpine : + candidate.families.singleton.familyType.type.trace.storedSpine = true + rawTel : VExpr.telN + candidate.families.singleton.familyType.type.trace.spineLength + normalization.raw.type = + generation.block.rawParams ++ generation.block.rawIndices + rawResult : VExpr.dropN + candidate.families.singleton.familyType.type.trace.spineLength + normalization.raw.type = generation.block.rawResult + viewResult : VExpr.dropN + candidate.families.singleton.familyType.type.trace.spineLength + normalization.family.type.view = + .sort generation.block.checked.resultLevel + +/-- Minimal structural input for family generation. The retained semantic +root already owns the recursive checker run, while dependent analysis fixes +the raw and checked components. A caller therefore supplies only the +executable stored-spine gate and the total number of binders traversed by that +spine; all telescope and terminal equations are derived below. -/ +structure CandidateFamilySemanticGenerationShape + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (normalization : NormalizationCandidateSemanticRun env Us candidate source) + (generation : GenerationChecked source) where + storedSpine : + candidate.families.singleton.familyType.type.trace.storedSpine = true + spineLength_eq : + candidate.families.singleton.familyType.type.trace.spineLength = + (generation.block.rawParams ++ generation.block.rawIndices).length + +/-- Recover the existing family-generation run from the single retained +semantic owner. -/ +theorem CandidateFamilySemanticGenerationRun.run + (run : CandidateFamilySemanticGenerationRun normalization generation) : + CandidateFamilyGenerationRun normalization.root generation where + spine := normalization.family.type.spine run.storedSpine + rawTel := run.rawTel + rawResult := run.rawResult + viewResult := run.viewResult + +/-- One positional constructor candidate aligned with the raw/view +constructor pair retained by dependent analysis. + +The spine certificate covers the exact stored constructor type. Its declared +telescope will later be transformed into the mixed emitted telescope by +replacing only the constructor's parameter prefix. -/ +structure CandidateNormalizedCtorRun {source : VInductDecl} + (block : NormalizedChecked source) (env : VEnv) (Us : List Name) + {kernelSource : Constructor} + {candidate : AddInductive.CandidateConstructor kernelSource} + {raw : VConstVal} + (root : CandidateConstructorRun env Us candidate raw) + (ctor : NormalizedCtor) where + raw_eq : ctor.raw = raw + view_eq : ctor.view.value = root.view + spine : TypeChecker.CandidateExprSpineRun env Us candidate.type + raw.type root.viewType + rawTel : VExpr.telN candidate.type.trace.spineLength raw.type = + ctor.declaredBinders source.nparams + rawResult : VExpr.dropN candidate.type.trace.spineLength raw.type = + ctor.rawResult source.nparams + viewResult : VExpr.dropN candidate.type.trace.spineLength root.viewType = + ctor.resultTarget block + +/-- Constructor generation alignment owned by the same retained semantic root +used for normalization. The only spine premise is the Boolean structural gate +computed by the candidate trace; the recursive semantic run and view are +projected from `root`. -/ +structure CandidateSemanticNormalizedCtorRun {source : VInductDecl} + (block : NormalizedChecked source) (env : VEnv) (Us : List Name) + {kernelSource : Constructor} + {candidate : AddInductive.CandidateConstructor kernelSource} + {raw : VConstVal} + (root : CandidateConstructorSemanticRun env Us candidate raw) + (ctor : NormalizedCtor) where + raw_eq : ctor.raw = raw + view_eq : ctor.view.value = root.root.view + storedSpine : candidate.type.trace.storedSpine = true + rawTel : VExpr.telN candidate.type.trace.spineLength raw.type = + ctor.declaredBinders source.nparams + rawResult : VExpr.dropN candidate.type.trace.spineLength raw.type = + ctor.rawResult source.nparams + viewResult : VExpr.dropN candidate.type.trace.spineLength root.type.view = + ctor.resultTarget block + +/-- Minimal structural input for one retained constructor root. It is +independent of a caller-selected normalized pair: positional raw/view pairing +is recovered from the successful dependent analysis, and the full component +equations follow from this total stored-binder count. -/ +structure CandidateConstructorSemanticGenerationShape + {source : VInductDecl} (env : VEnv) (Us : List Name) + {kernelSource : Constructor} + {candidate : AddInductive.CandidateConstructor kernelSource} + {raw : VConstVal} + (root : CandidateConstructorSemanticRun env Us candidate raw) where + storedSpine : candidate.type.trace.storedSpine = true + spineLength_eq : candidate.type.trace.spineLength = + (VExpr.telN source.nparams raw.type ++ + ctorFields (VExpr.dropN source.nparams raw.type)).length + +/-- Project the compatibility constructor run without rebuilding or choosing +semantic evidence. -/ +theorem CandidateSemanticNormalizedCtorRun.run + (run : CandidateSemanticNormalizedCtorRun block env Us root ctor) : + CandidateNormalizedCtorRun block env Us root.root ctor where + raw_eq := run.raw_eq + view_eq := run.view_eq + spine := root.type.spine run.storedSpine + rawTel := run.rawTel + rawResult := run.rawResult + viewResult := run.viewResult + +/-- The terminal alignment and the analyzer's exact constructor shape force +the candidate trace to expose the entire checked binder telescope. -/ +theorem CandidateNormalizedCtorRun.viewTel_eq + {source : VInductDecl} {generation : GenerationChecked source} + {env : VEnv} {Us : List Name} + {kernelSource : Constructor} + {candidate : AddInductive.CandidateConstructor kernelSource} + {raw : VConstVal} + {root : CandidateConstructorRun env Us candidate raw} + {ctor : NormalizedCtor} + (run : CandidateNormalizedCtorRun generation.block env Us root ctor) + (hctor : ctor ∈ generation.block.ctorPairs) : + VExpr.telN candidate.type.trace.spineLength root.viewType = + ctor.viewBinders generation.block := by + have viewType_eq : root.viewType = ctor.view.value.type := by + simpa only [CandidateConstructorRun.view] using + (congrArg (fun value : VConstVal => value.type) run.view_eq).symm + have hterminal : + TypeChecker.CandidateTerminal (ctor.resultTarget generation.block) := by + exact TypeChecker.candidateTerminal_appN_const _ _ _ + rw [viewType_eq, generation.viewCtorType_eq hctor] + apply TypeChecker.candidateTelN_of_dropN_terminal hterminal + simpa only [viewType_eq, generation.viewCtorType_eq hctor] using + run.viewResult + +/-- Extract the stored constructor's declared telescope/result evidence. -/ +theorem CandidateNormalizedCtorRun.declaredEvidence + {source : VInductDecl} {generation : GenerationChecked source} + {env : VEnv} {Us : List Name} + {kernelSource : Constructor} + {candidate : AddInductive.CandidateConstructor kernelSource} + {raw : VConstVal} + {root : CandidateConstructorRun env Us candidate raw} + {ctor : NormalizedCtor} + (run : CandidateNormalizedCtorRun generation.block env Us root ctor) + (hctor : ctor ∈ generation.block.ctorPairs) + (rightType : env.HasType Us.length + (ctor.declaredBinders source.nparams).reverse + (ctor.resultTarget generation.block) + (.sort generation.block.checked.resultLevel)) : + TypeChecker.TelResultDefEqEvidence env Us.length [] + (ctor.declaredBinders source.nparams) + (ctor.viewBinders generation.block) + (ctor.rawResult source.nparams) (ctor.resultTarget generation.block) + (.sort generation.block.checked.resultLevel) := by + apply run.spine.evidenceAt run.rawTel (run.viewTel_eq hctor) + run.rawResult run.viewResult rightType + +/-- The checked result spine and the candidate-certified binder telescope +determine a constructor's terminal typing judgment. The family constant is +typed once for the whole block; individual constructor fixtures supply no +additional semantic result oracle. -/ +theorem CandidateNormalizedCtorRun.rightType_ofChecked + {source : VInductDecl} {generation : GenerationChecked source} + {env : VEnv} {Us : List Name} + {kernelSource : Constructor} + {candidate : AddInductive.CandidateConstructor kernelSource} + {raw : VConstVal} + {root : CandidateConstructorRun env Us candidate raw} + {ctor : NormalizedCtor} + (run : CandidateNormalizedCtorRun generation.block env Us root ctor) + (henv : VEnv.WF env) (uvars_eq : source.uvars = Us.length) + (checked : generation.block.checked.WF env) + (familyConst : env.HasType source.uvars [] + (.const generation.block.sourceType.name + (VLevel.params source.uvars)) + generation.block.checked.type.type) + (hctor : ctor ∈ generation.block.ctorPairs) : + env.HasType Us.length + (ctor.declaredBinders source.nparams).reverse + (ctor.resultTarget generation.block) + (.sort generation.block.checked.resultLevel) := by + obtain ⟨_, evidence⟩ := run.spine.evidence + have telescope : TypeChecker.TelDefEqEvidence env Us.length [] + (ctor.declaredBinders source.nparams) + (ctor.viewBinders generation.block) := by + simpa only [run.rawTel, run.viewTel_eq hctor] using evidence.telescope + have hview := generation.checkedResultTarget_hasType + henv.ordered checked familyConst hctor + have hview' : env.HasType Us.length + (ctor.viewBinders generation.block).reverse + (ctor.resultTarget generation.block) + (.sort generation.block.checked.resultLevel) := by + simpa only [uvars_eq] using hview + have hctx : env.IsDefEqCtx Us.length [] + (ctor.declaredBinders source.nparams).reverse + (ctor.viewBinders generation.block).reverse := by + simpa using telescope.telDefEq.ctx + exact hview'.defeqDFC henv.ordered (hctx.symm henv.ordered) + +/-- Produce both constructor paths required by `NormalizedCtorRun`. + +The declared path comes directly from the constructor candidate. The emitted +path replaces the stored constructor parameter prefix by the checked family +parameter prefix used by Lean's recursor generator, transporting fields and +result through the induced definitionally equal context. -/ +theorem CandidateNormalizedCtorRun.normalizedCtorRun + {source : VInductDecl} {generation : GenerationChecked source} + {env : VEnv} {Us : List Name} + {kernelSource : Constructor} + {candidate : AddInductive.CandidateConstructor kernelSource} + {raw : VConstVal} + {root : CandidateConstructorRun env Us candidate raw} + {ctor : NormalizedCtor} + (run : CandidateNormalizedCtorRun generation.block env Us root ctor) + (henv : VEnv.WF env) (uvars_eq : source.uvars = Us.length) + (familyParams : TypeChecker.TelDefEqEvidence env Us.length [] + generation.block.rawParams generation.block.checked.params) + (prefixLength : + (VExpr.telN source.nparams ctor.raw.type).length = + generation.block.checked.params.length) + (hctor : ctor ∈ generation.block.ctorPairs) + (rightType : env.HasType Us.length + (ctor.declaredBinders source.nparams).reverse + (ctor.resultTarget generation.block) + (.sort generation.block.checked.resultLevel)) : + NormalizedCtorRun generation.block ctor env := by + have declared := run.declaredEvidence hctor rightType + have declaredSplit : TypeChecker.TelResultDefEqEvidence env Us.length [] + (VExpr.telN source.nparams ctor.raw.type ++ + ctor.rawFields source.nparams) + (generation.block.checked.params ++ ctor.view.fields) + (ctor.rawResult source.nparams) (ctor.resultTarget generation.block) + (.sort generation.block.checked.resultLevel) := by + simpa only [NormalizedCtor.declaredBinders, + NormalizedCtor.viewBinders] using declared + have checkedParams : TypeChecker.TelDefEqEvidence env Us.length [] + generation.block.checked.params generation.block.checked.params := + .ofTelDefEq <| (familyParams.telDefEq.view_onTel henv.ordered).telDefEq_refl + have emitted := declaredSplit.replacePrefix henv checkedParams prefixLength + exact { + declaredTel := by + simpa only [uvars_eq] using declared.telescope + declaredResult := by + simpa only [uvars_eq, List.append_nil] using declared.result + emittedTel := by + simpa only [uvars_eq, NormalizedCtor.emittedBinders, + NormalizedCtor.viewBinders] using emitted.telescope + emittedResult := by + simpa only [uvars_eq, NormalizedCtor.emittedBinders, + List.append_nil] using emitted.result } + +/-- Dependent positional alignment between every constructor candidate run +and every normalized constructor pair. The indices make unequal lengths, +reordering, and evidence reuse at a different source position impossible. -/ +inductive CandidateNormalizedCtorListRun {source : VInductDecl} + (block : NormalizedChecked source) (env : VEnv) (Us : List Name) : + {kernelSources : List Constructor} → + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor kernelSources} → + {raws : List VConstVal} → + (roots : CandidateConstructorListRun env Us candidates raws) → + List NormalizedCtor → Type where + | nil : CandidateNormalizedCtorListRun block env Us .nil [] + | cons + (head : CandidateNormalizedCtorRun block env Us root ctor) + (tail : CandidateNormalizedCtorListRun block env Us roots ctors) : + CandidateNormalizedCtorListRun block env Us + (.cons root roots) (ctor :: ctors) + +/-- Dependent positional generation alignment over the retained constructor +semantic list. Its projection below is definitionally tied to +`roots.roots`, so source order and the exact normalization views cannot drift +between phases. -/ +inductive CandidateSemanticNormalizedCtorListRun {source : VInductDecl} + (block : NormalizedChecked source) (env : VEnv) (Us : List Name) : + {kernelSources : List Constructor} → + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor kernelSources} → + {raws : List VConstVal} → + (roots : CandidateConstructorSemanticListRun env Us candidates raws) → + List NormalizedCtor → Type where + | nil : CandidateSemanticNormalizedCtorListRun block env Us .nil [] + | cons + (head : CandidateSemanticNormalizedCtorRun block env Us root ctor) + (tail : CandidateSemanticNormalizedCtorListRun block env Us roots ctors) : + CandidateSemanticNormalizedCtorListRun block env Us + (.cons root roots) (ctor :: ctors) + +/-- Source-indexed structural generation inputs for every retained semantic +constructor root. No normalized constructor list occurs in this type, so a +caller cannot choose, reorder, truncate, or duplicate the analyzer's pairs. -/ +inductive CandidateConstructorSemanticGenerationShapeList + (source : VInductDecl) (env : VEnv) (Us : List Name) : + {kernelSources : List Constructor} → + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor kernelSources} → + {raws : List VConstVal} → + (roots : CandidateConstructorSemanticListRun env Us candidates raws) → + Type where + | nil : CandidateConstructorSemanticGenerationShapeList source env Us .nil + | cons + (head : CandidateConstructorSemanticGenerationShape + (source := source) env Us root) + (tail : CandidateConstructorSemanticGenerationShapeList + source env Us roots) : + CandidateConstructorSemanticGenerationShapeList source env Us + (.cons root roots) + +/-- Executable generation-layout check for a complete source-indexed +constructor candidate list. + +The check is intentionally stated against the raw Theory constants retained +by the semantic hierarchy. It accepts exactly when every candidate WHNF trace +preserves the stored main Pi spine and traverses the complete raw constructor +telescope. List-length mismatches are rejected explicitly; no `zip` or +positional lookup can silently truncate either side. -/ +def candidateConstructorSemanticGenerationShape + (source : VInductDecl) : + {kernelSources : List Constructor} → + AddInductive.CandidateList AddInductive.CandidateConstructor + kernelSources → + List VConstVal → Bool + | _, .nil, [] => true + | _, .nil, _ :: _ => false + | _, .cons _ _, [] => false + | _, .cons candidate candidates, raw :: raws => + candidate.type.trace.storedSpine && + candidate.type.trace.spineLength == + (VExpr.telN source.nparams raw.type ++ + ctorFields (VExpr.dropN source.nparams raw.type)).length && + candidateConstructorSemanticGenerationShape source candidates raws + +/-- Executable generation-layout check for a complete singleton +normalization candidate and its raw Theory family. + +This definition is independent of semantic proofs. It checks only the +source-indexed candidate traces against the raw family/constructor telescope +layout that generation would emit. Verify's retained semantic hierarchy +later reindexes the same Boolean onto its exact raw family. -/ +def normalizationCandidateGenerationShape + {kernelSource : InductiveType} + (source : VInductDecl) (raw : VInductiveType) + (candidate : AddInductive.NormalizationCandidate [kernelSource]) : Bool := + let familyTrace := + candidate.families.singleton.familyType.type.trace + (familyTrace.storedSpine && + familyTrace.spineLength == + (VExpr.telN source.nparams raw.type ++ + ctorFields (VExpr.dropN source.nparams raw.type)).length) && + candidateConstructorSemanticGenerationShape source + candidate.families.singleton.constructors raw.ctors + +/-- One executable constructor-list shape check determines every dependent +per-position shape record required by semantic generation. -/ +def CandidateConstructorSemanticGenerationShapeList.ofCheck + {source : VInductDecl} {env : VEnv} {Us : List Name} + {kernelSources : List Constructor} + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor kernelSources} + {raws : List VConstVal} + (roots : CandidateConstructorSemanticListRun env Us candidates raws) + (shape : candidateConstructorSemanticGenerationShape + source candidates raws = true) : + CandidateConstructorSemanticGenerationShapeList source env Us roots := + match roots with + | .nil => .nil + | .cons head tail => by + simp only [candidateConstructorSemanticGenerationShape, + Bool.and_eq_true, beq_iff_eq] at shape + exact .cons { + storedSpine := shape.1.1 + spineLength_eq := shape.1.2 } + (CandidateConstructorSemanticGenerationShapeList.ofCheck + tail shape.2) +termination_by sizeOf roots + +/-- Forget only retained semantic ownership and recover the existing +generation-facing positional list. -/ +def CandidateSemanticNormalizedCtorListRun.run : + (semantic : CandidateSemanticNormalizedCtorListRun + block env Us roots ctors) → + CandidateNormalizedCtorListRun block env Us roots.roots ctors + | .nil => .nil + | .cons head tail => .cons head.run tail.run + +/-- Assemble a `NormalizedCtorRun` for every constructor in an exact +dependent positional list. -/ +theorem CandidateNormalizedCtorListRun.normalizedCtorRuns + {source : VInductDecl} {generation : GenerationChecked source} + {env : VEnv} {Us : List Name} + {kernelSources : List Constructor} + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor kernelSources} + {raws : List VConstVal} + {roots : CandidateConstructorListRun env Us candidates raws} + {ctors : List NormalizedCtor} + (run : CandidateNormalizedCtorListRun generation.block env Us roots ctors) + (henv : VEnv.WF env) (uvars_eq : source.uvars = Us.length) + (familyParams : TypeChecker.TelDefEqEvidence env Us.length [] + generation.block.rawParams generation.block.checked.params) + (checked : generation.block.checked.WF env) + (familyConst : env.HasType source.uvars [] + (.const generation.block.sourceType.name + (VLevel.params source.uvars)) + generation.block.checked.type.type) + (pairMembership : ∀ ctor ∈ ctors, + ctor ∈ generation.block.ctorPairs) + (prefixLengths : ∀ ctor ∈ ctors, + (VExpr.telN source.nparams ctor.raw.type).length = + generation.block.checked.params.length) : + ∀ ctor ∈ ctors, NormalizedCtorRun generation.block ctor env := by + induction run with + | nil => intro ctor hctor; simp at hctor + | cons head tail ih => + intro ctor hctor + simp only [List.mem_cons] at hctor + rcases hctor with rfl | hctor + · exact head.normalizedCtorRun henv uvars_eq familyParams + (prefixLengths _ (.head _)) + (pairMembership _ (.head _)) + (head.rightType_ofChecked henv uvars_eq checked familyConst + (pairMembership _ (.head _))) + · exact ih + (fun ctor hctor => pairMembership ctor (.tail _ hctor)) + (fun ctor hctor => prefixLengths ctor (.tail _ hctor)) + ctor hctor + +/-- Complete source-indexed candidate certificate for one generation-ready +singleton inductive declaration. + +`analysis` records that the candidate-derived normalization produced this exact +dependent generation result. `constructors` then aligns every post-family +candidate run with the corresponding dependent analyzer pair. -/ +structure GenerationCandidateRun + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (normalization : NormalizationCandidateRun env Us candidate source) + (generation : GenerationChecked source) where + analysis : normalization.normalization.generation? = some generation + checked : generation.block.checked.WF env + family : CandidateFamilyGenerationRun normalization generation + constructors : CandidateNormalizedCtorListRun generation.block + normalization.family.typeEnv Us normalization.family.constructors + generation.block.ctorPairs + +/-- Complete generation assembly owned by one retained semantic hierarchy. + +This is the no-parallel-run form of `GenerationCandidateRun`: family and +constructor spines are projections of `normalization`, while `analysis` retains +the exact dependent analyzer result consumed by Theory generation. -/ +structure GenerationCandidateSemanticRun + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (normalization : NormalizationCandidateSemanticRun env Us candidate source) + (generation : GenerationChecked source) where + analysis : normalization.root.normalization.generation? = some generation + checked : generation.block.checked.WF env + family : CandidateFamilySemanticGenerationRun normalization generation + constructors : CandidateSemanticNormalizedCtorListRun generation.block + normalization.family.typeEnv Us normalization.family.constructors + generation.block.ctorPairs + +/-- Complete semantic-generation input with all analyzer-determined component +equations erased. Compared with `GenerationCandidateSemanticRun`, this form +retains only checked semantics plus the executable stored-spine/length shape +for each source-indexed root. Its projection below reconstructs the exact +family and dependent constructor alignment from `analysis`. -/ +structure GenerationCandidateSemanticShapeRun + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (normalization : NormalizationCandidateSemanticRun env Us candidate source) + (generation : GenerationChecked source) where + analysis : normalization.root.normalization.generation? = some generation + checked : generation.block.checked.WF env + family : CandidateFamilySemanticGenerationShape normalization generation + constructors : CandidateConstructorSemanticGenerationShapeList source + normalization.family.typeEnv Us normalization.family.constructors + +/-- One executable structural gate for the complete retained singleton +candidate hierarchy. + +The family check uses the complete raw parameter/index telescope. The +constructor check traverses the source-indexed candidate and raw lists +dependently. This consolidates the former per-fixture family and constructor +proof records into one computation while remaining separate from semantic +authority and dependent analysis. -/ +def NormalizationCandidateSemanticRun.generationShape + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (normalization : NormalizationCandidateSemanticRun env Us candidate source) : + Bool := + normalizationCandidateGenerationShape source normalization.raw candidate + +/-- One successful outer candidate together with the executable structural +gate required before mixed raw/view generation. + +The record carries the exact ordinary producer equation, so the shape check +cannot be reused for a different candidate. It remains operational evidence: +semantic authority is supplied only after Verify interprets the retained +checker executions. -/ +structure ProducedGenerationShapeCandidate + (source : VInductDecl) (raw : VInductiveType) + (kernelSource : InductiveType) (numNested : Nat) (isUnsafe : Bool) + (context : AddInductive.Context) where + candidate : AddInductive.NormalizationCandidate [kernelSource] + produced : + AddInductive.buildNormalizationCandidate source.nparams + [kernelSource] numNested isUnsafe context = .ok candidate + shape : normalizationCandidateGenerationShape source raw candidate = true + +/-- Run the ordinary outer producer and immediately reject candidates whose +retained traces cannot support mixed generation of the supplied raw Theory +family. The successful result retains both exact producer provenance and the +single complete shape proof. -/ +def produceGenerationShapeCandidate + (source : VInductDecl) (raw : VInductiveType) + (kernelSource : InductiveType) (numNested : Nat) (isUnsafe : Bool) + (context : AddInductive.Context) : + Except Exception (ProducedGenerationShapeCandidate source raw kernelSource + numNested isUnsafe context) := + match produced : AddInductive.buildNormalizationCandidate source.nparams + [kernelSource] numNested isUnsafe context with + | .error error => .error error + | .ok candidate => + if shape : normalizationCandidateGenerationShape source raw candidate then + .ok { candidate, produced, shape } + else + .error (.other + "normalization candidate does not preserve the generation spine") + +private theorem produceGenerationShapeCandidate_match_ok + {source : VInductDecl} {raw : VInductiveType} + {kernelSource : InductiveType} {numNested : Nat} {isUnsafe : Bool} + {context : AddInductive.Context} + (result : Except Exception + (AddInductive.NormalizationCandidate [kernelSource])) + (toProduced : ∀ actual, result = .ok actual → + AddInductive.buildNormalizationCandidate source.nparams + [kernelSource] numNested isUnsafe context = .ok actual) + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (result_ok : result = .ok candidate) + (shape : normalizationCandidateGenerationShape source raw candidate = true) : + (match result_eq : result with + | .error error => Except.error error + | .ok actual => + if actualShape : normalizationCandidateGenerationShape source raw actual then + Except.ok (show ProducedGenerationShapeCandidate source raw kernelSource + numNested isUnsafe context from { + candidate := actual + produced := toProduced actual result_eq + shape := actualShape }) + else + Except.error (.other + "normalization candidate does not preserve the generation spine")) = + Except.ok (show ProducedGenerationShapeCandidate source raw kernelSource + numNested isUnsafe context from { + candidate + produced := toProduced candidate result_ok + shape }) := by + subst result + simp [shape] + +/-- A successful ordinary producer equation and successful hierarchy-shape +check determine the exact successful result of the strengthened producer. + +Keeping this dependent-match elimination here avoids repeating proof-carrying +`Except` reasoning in clients. -/ +theorem produceGenerationShapeCandidate_eq_ok + {source : VInductDecl} {raw : VInductiveType} + {kernelSource : InductiveType} {numNested : Nat} {isUnsafe : Bool} + {context : AddInductive.Context} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (produced : + AddInductive.buildNormalizationCandidate source.nparams + [kernelSource] numNested isUnsafe context = .ok candidate) + (shape : normalizationCandidateGenerationShape source raw candidate = true) : + produceGenerationShapeCandidate source raw kernelSource numNested isUnsafe + context = + .ok { candidate, produced, shape } := by + unfold produceGenerationShapeCandidate + exact produceGenerationShapeCandidate_match_ok + (result := AddInductive.buildNormalizationCandidate source.nparams + [kernelSource] numNested isUnsafe context) + (toProduced := fun _ result_eq => result_eq) produced shape + +/-- Project the established generation assembler. Every normalization root, +view, and recursive spine remains definitionally tied to the semantic owner. -/ +def GenerationCandidateSemanticRun.run + (run : GenerationCandidateSemanticRun normalization generation) : + GenerationCandidateRun normalization.root generation where + analysis := run.analysis + checked := run.checked + family := run.family.run + constructors := run.constructors.run + +/-- A retained analyzer result necessarily contains the normalization that was +analyzed. This is derived from `analysis`, rather than supplied by fixtures. -/ +theorem GenerationCandidateRun.normalization_eq + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + {normalization : NormalizationCandidateRun env Us candidate source} + {generation : GenerationChecked source} + (run : GenerationCandidateRun normalization generation) : + generation.block.normalization = normalization.normalization := + Normalization.generation?_normalization run.analysis + +/-- The source-indexed singleton declarations force the analyzer's raw family +to be the exact family retained by a normalization candidate. -/ +theorem NormalizationCandidateRun.sourceType_eq + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (normalization : NormalizationCandidateRun env Us candidate source) + (generation : GenerationChecked source) : + generation.block.sourceType = normalization.raw := by + have h : [generation.block.sourceType] = [normalization.raw] := + generation.block.source_types_eq.symm.trans normalization.raw_types_eq + injection h + +/-- Exact dependent analysis selects the reconstructed family view, including +its constructor list, not merely an expression payload with the same type. -/ +theorem NormalizationCandidateRun.familyViewType_eq + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + {normalization : NormalizationCandidateRun env Us candidate source} + {generation : GenerationChecked source} + (analysis : normalization.normalization.generation? = some generation) : + generation.block.checked.type = normalization.family.view := by + have normalization_eq : generation.block.normalization = + normalization.normalization := + Normalization.generation?_normalization analysis + have hviews := congrArg (fun norm : Normalization source => norm.view.types) + normalization_eq + have htypes : [generation.block.checked.type] = + [normalization.family.view] := by + calc + [generation.block.checked.type] = + generation.block.normalization.view.types := + generation.block.checked.types_eq.symm + _ = normalization.normalization.view.types := hviews + _ = [normalization.family.view] := rfl + injection htypes + +/-- The retained dependent analysis necessarily checks the exact family view +selected by the normalization candidate. This equation is a consequence of +the two singleton declaration indices and `normalization_eq`, not a separate +fixture alignment premise. -/ +theorem GenerationCandidateRun.familyView_eq + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + {normalization : NormalizationCandidateRun env Us candidate source} + {generation : GenerationChecked source} + (run : GenerationCandidateRun normalization generation) : + normalization.family.viewType = generation.block.checked.type.type := by + exact (congrArg (fun ty : VInductiveType => ty.type) + (normalization.familyViewType_eq run.analysis)).symm + +/-- Taking the exact length of the complete stored telescope recovers both +its binder list and its non-forall result. This is the structural bridge from +one numeric trace invariant to generation's named raw components. -/ +private theorem generationTelNForallNLength : + ∀ (As : List VExpr) (B : VExpr), + VExpr.telN As.length (VExpr.forallN As B) = As + | [], _ => rfl + | _ :: As, B => by + simp only [List.length_cons, VExpr.forallN, VExpr.telN, + generationTelNForallNLength As B] + +private theorem generationDropNForallNLength : + ∀ (As : List VExpr) (B : VExpr), + VExpr.dropN As.length (VExpr.forallN As B) = B + | [], _ => rfl + | _ :: As, B => by + simp only [List.length_cons, VExpr.forallN, VExpr.dropN, + generationDropNForallNLength As B] + +private theorem candidateFullTelComponents (np n : Nat) (e : VExpr) + (h : n = + (VExpr.telN np e ++ ctorFields (VExpr.dropN np e)).length) : + VExpr.telN n e = + VExpr.telN np e ++ ctorFields (VExpr.dropN np e) ∧ + VExpr.dropN n e = VExpr.resultOf (VExpr.dropN np e) := by + let As := VExpr.telN np e ++ ctorFields (VExpr.dropN np e) + have he : + VExpr.forallN As (VExpr.resultOf (VExpr.dropN np e)) = e := by + simp only [As, VExpr.forallN_append, + forallN_ctorFields_resultOf, VExpr.forallN_telN_dropN] + let B := VExpr.resultOf (VExpr.dropN np e) + have hAs : n = As.length := h + change VExpr.telN n e = As ∧ VExpr.dropN n e = B + rw [hAs, ← he] + exact ⟨generationTelNForallNLength _ _, + generationDropNForallNLength _ _⟩ + +/-- Derive every family component equation from the minimal structural shape +and the exact dependent analyzer result. -/ +private theorem CandidateFamilySemanticGenerationShape.generationRun + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + {normalization : NormalizationCandidateSemanticRun env Us candidate source} + {generation : GenerationChecked source} + (input : CandidateFamilySemanticGenerationShape + normalization generation) + (analysis : normalization.root.normalization.generation? = + some generation) : + CandidateFamilySemanticGenerationRun normalization generation where + storedSpine := input.storedSpine + rawTel := by + have components := candidateFullTelComponents source.nparams + candidate.families.singleton.familyType.type.trace.spineLength + normalization.raw.type (by + simpa only [NormalizedChecked.rawParams, + NormalizedChecked.rawIndices, + NormalizationCandidateSemanticRun.root, + normalization.root.sourceType_eq generation] using + input.spineLength_eq) + simpa only [NormalizedChecked.rawParams, + NormalizedChecked.rawIndices, + NormalizationCandidateSemanticRun.root, + normalization.root.sourceType_eq generation] using components.1 + rawResult := by + have components := candidateFullTelComponents source.nparams + candidate.families.singleton.familyType.type.trace.spineLength + normalization.raw.type (by + simpa only [NormalizedChecked.rawParams, + NormalizedChecked.rawIndices, + NormalizationCandidateSemanticRun.root, + normalization.root.sourceType_eq generation] using + input.spineLength_eq) + simpa only [NormalizedChecked.rawResult, + NormalizationCandidateSemanticRun.root, + normalization.root.sourceType_eq generation] using components.2 + viewResult := by + let As := generation.block.checked.params ++ + generation.block.checked.indices + have hlength : + candidate.families.singleton.familyType.type.trace.spineLength = + As.length := by + rw [input.spineLength_eq] + simp only [As, List.length_append] + rw [generation.shape.2.1, generation.shape.2.2.1] + have hview : normalization.family.type.view = + generation.block.checked.type.type := by + simpa only [NormalizationCandidateSemanticRun.root, + CandidateFamilySemanticRun.root, CandidateFamilyRun.view] using + (congrArg (fun ty : VInductiveType => ty.type) + (normalization.root.familyViewType_eq analysis)).symm + rw [hview, generation.block.checked.type_eq, hlength] + simpa only [As, VExpr.forallN_append] using + generationDropNForallNLength As + (.sort generation.block.checked.resultLevel) + +/-- Derive one normalized constructor alignment after its positional raw/view +equalities have been recovered from the analyzer-owned pair list. -/ +private theorem CandidateConstructorSemanticGenerationShape.generationRun + {source : VInductDecl} {generation : GenerationChecked source} + {env : VEnv} {Us : List Name} + {kernelSource : Constructor} + {candidate : AddInductive.CandidateConstructor kernelSource} + {raw : VConstVal} + {root : CandidateConstructorSemanticRun env Us candidate raw} + {ctor : NormalizedCtor} + (input : CandidateConstructorSemanticGenerationShape + (source := source) env Us root) + (raw_eq : ctor.raw = raw) + (view_eq : ctor.view.value = root.root.view) + (hctor : ctor ∈ generation.block.ctorPairs) : + CandidateSemanticNormalizedCtorRun generation.block env Us root ctor where + raw_eq := raw_eq + view_eq := view_eq + storedSpine := input.storedSpine + rawTel := by + have components := candidateFullTelComponents source.nparams + candidate.type.trace.spineLength raw.type input.spineLength_eq + simpa only [NormalizedCtor.declaredBinders, + NormalizedCtor.rawFields, raw_eq] using components.1 + rawResult := by + have components := candidateFullTelComponents source.nparams + candidate.type.trace.spineLength raw.type input.spineLength_eq + simpa only [NormalizedCtor.rawResult, raw_eq] using components.2 + viewResult := by + let As := generation.block.checked.params ++ ctor.view.fields + have hlength : candidate.type.trace.spineLength = As.length := by + rw [input.spineLength_eq] + simp only [As, List.length_append] + rw [← raw_eq] + have hshape := generation.shape.2.2.2.2.2 ctor hctor + simp only [NormalizedCtor.rawFields] at hshape + rw [hshape.2.2.1, hshape.2.2.2, + generation.shape.1.symm.trans generation.shape.2.1] + have viewType_eq : root.type.view = ctor.view.value.type := by + exact (congrArg (fun value : VConstVal => value.type) view_eq).symm + rw [viewType_eq, generation.viewCtorType_eq hctor, hlength] + exact generationDropNForallNLength As _ + +/-- Recursively align structural constructor inputs with a pair list whose raw +and checked-value projections are already fixed. -/ +private def + CandidateConstructorSemanticGenerationShapeList.generationRuns + {source : VInductDecl} {generation : GenerationChecked source} + {env : VEnv} {Us : List Name} + {kernelSources : List Constructor} + {candidates : AddInductive.CandidateList + AddInductive.CandidateConstructor kernelSources} + {raws : List VConstVal} + {roots : CandidateConstructorSemanticListRun env Us candidates raws} : + (input : CandidateConstructorSemanticGenerationShapeList + source env Us roots) → + (ctors : List NormalizedCtor) → + (raws_eq : ctors.map (·.raw) = raws) → + (views_eq : ctors.map (fun ctor => ctor.view.value) = + roots.roots.views) → + (membership : ∀ ctor ∈ ctors, + ctor ∈ generation.block.ctorPairs) → + CandidateSemanticNormalizedCtorListRun generation.block env Us roots ctors + | .nil, [], _, _, _ => .nil + | .nil, _ :: _, raws_eq, _, _ => by simp at raws_eq + | .cons _ _, [], raws_eq, _, _ => by simp at raws_eq + | .cons head tail, ctor :: ctors, raws_eq, views_eq, membership => by + simp only [List.map_cons, List.cons.injEq] at raws_eq + simp only [List.map_cons, + CandidateConstructorSemanticListRun.roots, + CandidateConstructorListRun.views, List.cons.injEq] at views_eq + exact .cons + (head.generationRun raws_eq.1 views_eq.1 + (membership ctor (.head _))) + (tail.generationRuns ctors raws_eq.2 views_eq.2 + (fun ctor hctor => membership ctor (.tail _ hctor))) + +/-- Exact analysis determines the complete dependent normalized-constructor +list from source-indexed semantic roots and their minimal structural shapes. -/ +private def + CandidateConstructorSemanticGenerationShapeList.ofAnalysis + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + {normalization : NormalizationCandidateSemanticRun env Us candidate source} + {generation : GenerationChecked source} + (input : CandidateConstructorSemanticGenerationShapeList source + normalization.family.typeEnv Us normalization.family.constructors) + (analysis : normalization.root.normalization.generation? = + some generation) : + CandidateSemanticNormalizedCtorListRun generation.block + normalization.family.typeEnv Us normalization.family.constructors + generation.block.ctorPairs := by + apply input.generationRuns + · simpa only [NormalizationCandidateSemanticRun.root, + normalization.root.sourceType_eq generation] using + generation.rawCtors_eq + · have viewType_eq := normalization.root.familyViewType_eq analysis + calc + generation.block.ctorPairs.map (fun ctor => ctor.view.value) = + generation.block.checked.constructors.map (·.value) := by + simpa only [List.map_map, Function.comp_def] using + congrArg (List.map (·.value)) generation.viewCtors_eq + _ = generation.block.checked.type.ctors := by + rw [generation.block.checked.constructors_eq, List.map_map] + change generation.block.checked.type.ctors.map (fun c => c) = _ + exact List.map_id' generation.block.checked.type.ctors + _ = normalization.family.root.view.ctors := by + simpa only [NormalizationCandidateSemanticRun.root] using + congrArg (fun ty : VInductiveType => ty.ctors) viewType_eq + _ = normalization.family.constructors.roots.views := rfl + · exact fun _ hctor => hctor + +/-- Recover every analyzer-owned constructor pairing from the retained +semantic hierarchy, dependent analysis, and executable structural gate. + +This projection deliberately does not require `Checked.WF`: it exposes only +the exact source/candidate/raw/view alignment needed to derive that semantic +fact in the constructor-validation layer. -/ +def NormalizationCandidateSemanticRun.constructorGenerationRuns + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + (normalization : NormalizationCandidateSemanticRun env Us candidate source) + (generation : GenerationChecked source) + (analysis : normalization.root.normalization.generation? = + some generation) + (shape : normalization.generationShape = true) : + CandidateSemanticNormalizedCtorListRun generation.block + normalization.family.typeEnv Us normalization.family.constructors + generation.block.ctorPairs := by + simp only [NormalizationCandidateSemanticRun.generationShape, + normalizationCandidateGenerationShape, Bool.and_eq_true, + beq_iff_eq] at shape + exact (CandidateConstructorSemanticGenerationShapeList.ofCheck + normalization.family.constructors shape.2).ofAnalysis analysis + +/-- Reconstruct the established semantic-generation run from the reduced +shape boundary. No raw/view pair or component equation is supplied here. -/ +def GenerationCandidateSemanticShapeRun.run + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + {normalization : NormalizationCandidateSemanticRun env Us candidate source} + {generation : GenerationChecked source} + (input : GenerationCandidateSemanticShapeRun normalization generation) : + GenerationCandidateSemanticRun normalization generation where + analysis := input.analysis + checked := input.checked + family := input.family.generationRun input.analysis + constructors := input.constructors.ofAnalysis input.analysis + +/-- Reconstruct well-formedness of the post-family environment from the +retained pre-family context, candidate raw/view equality, checked family view, +and exact raw-family insertion. Fixtures therefore do not supply this semantic +consequence independently. -/ +theorem GenerationCandidateRun.typeEnv_wf + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + {normalization : NormalizationCandidateRun env Us candidate source} + {generation : GenerationChecked source} + (run : GenerationCandidateRun normalization generation) : + VEnv.WF normalization.family.typeEnv := by + have henv : VEnv.WF env := by + simpa only [normalization.family.typeRun.venv_eq] using + normalization.family.typeRun.contextRun.context.Ewf + obtain ⟨_, hfamily⟩ := normalization.family.typeRun.evidence + have hview : env.IsType Us.length [] normalization.family.viewType := by + simpa only [← generation.block.uvars_eq, normalization.uvars_eq, + run.familyView_eq] using run.checked.family_isType + have hraw : env.IsType Us.length [] normalization.raw.type := + VEnv.IsType.defeqU_l henv trivial hfamily.isDefEq.toU.symm hview + have hrawWF : normalization.raw.toVConstant.WF env := by + show env.IsType normalization.raw.uvars [] normalization.raw.type + simpa only [normalization.family.uvars_eq] using hraw + obtain ⟨ds, hds⟩ := henv + exact ⟨.axiom normalization.raw.toVConstVal :: ds, + .decl (.axiom hrawWF normalization.family.addType) hds⟩ + +/-- Type the raw family constant at the analyzer-selected family view in the +post-family environment. The proof combines the exact raw insertion, the +candidate's whole-family equality, and checked family well-formedness once; +constructors can then share this result. -/ +theorem GenerationCandidateRun.familyConst_hasType + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + {normalization : NormalizationCandidateRun env Us candidate source} + {generation : GenerationChecked source} + (run : GenerationCandidateRun normalization generation) : + normalization.family.typeEnv.HasType source.uvars [] + (.const generation.block.sourceType.name + (VLevel.params source.uvars)) + generation.block.checked.type.type := by + have sourceType_eq : generation.block.sourceType = normalization.raw := by + have h : [generation.block.sourceType] = [normalization.raw] := + generation.block.source_types_eq.symm.trans normalization.raw_types_eq + injection h + have hlookup : normalization.family.typeEnv.constants + normalization.raw.name = some normalization.raw.toVConstant := + VEnv.addConst_self normalization.family.addType + have hconstRaw := VEnv.HasType.const0 hlookup + (run.typeEnv_wf.ordered.constWF hlookup) + have hconstRaw' : normalization.family.typeEnv.HasType Us.length [] + (.const normalization.raw.name (VLevel.params Us.length)) + normalization.raw.type := by + simpa only [normalization.family.uvars_eq] using hconstRaw + obtain ⟨_, hfamily⟩ := normalization.family.typeRun.evidence + have hchecked := run.checked.mono + (VEnv.addConst_le normalization.family.addType) + have hviewType : normalization.family.typeEnv.IsType Us.length [] + normalization.family.viewType := by + simpa only [← generation.block.uvars_eq, normalization.uvars_eq, + run.familyView_eq] using hchecked.family_isType + obtain ⟨_, hviewType⟩ := hviewType + have hfamilyExact := + (hfamily.isDefEq.toU.mono + (VEnv.addConst_le normalization.family.addType)).of_r + run.typeEnv_wf trivial hviewType + have hconstView : normalization.family.typeEnv.HasType Us.length [] + (.const normalization.raw.name (VLevel.params Us.length)) + normalization.family.viewType := + hfamilyExact.defeq hconstRaw' + simpa only [normalization.uvars_eq, sourceType_eq, + run.familyView_eq] using hconstView + +/-- Assemble the existing checker-side `GenerationRun` entirely from the +source-indexed normalization candidate and its exact spine certificates. -/ +def GenerationCandidateRun.generationRun + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + {normalization : NormalizationCandidateRun env Us candidate source} + {generation : GenerationChecked source} + (run : GenerationCandidateRun normalization generation) : + GenerationRun generation env := by + have familyEvidence := run.family.evidence run.familyView_eq + have familyParams : TypeChecker.TelDefEqEvidence env Us.length [] + generation.block.rawParams generation.block.checked.params := by + apply TypeChecker.TelDefEqEvidence.ofTelDefEq + simpa [generation.shape.2.1] using + familyEvidence.telescope.telDefEq.take + generation.block.rawParams.length + have familyParamsTypeEnv := familyParams.mono + (VEnv.addConst_le normalization.family.addType) + have sourceType_eq : generation.block.sourceType = normalization.raw := by + have h : [generation.block.sourceType] = [normalization.raw] := + generation.block.source_types_eq.symm.trans normalization.raw_types_eq + injection h + refine { + normalization := by + simpa only [run.normalization_eq] using + normalization.normalizationRun + checked := run.checked + familyTel := by + simpa only [normalization.uvars_eq] using familyEvidence.telescope + familyResult := by + simpa only [normalization.uvars_eq, List.append_nil] using + familyEvidence.result + typeEnv := normalization.family.typeEnv + addType := by + simpa only [sourceType_eq] using normalization.family.addType + constructors := ?_ } + apply run.constructors.normalizedCtorRuns run.typeEnv_wf + normalization.uvars_eq familyParamsTypeEnv + (run.checked.mono (VEnv.addConst_le normalization.family.addType)) + run.familyConst_hasType (fun _ hctor => hctor) + intro ctor hctor + exact (generation.shape.2.2.2.2.2 ctor hctor).2.2.1.trans + (generation.shape.1.symm.trans generation.shape.2.1) + +/-- Public Theory boundary for a complete source-indexed generation +candidate. -/ +theorem GenerationCandidateRun.wf + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + {normalization : NormalizationCandidateRun env Us candidate source} + {generation : GenerationChecked source} + (run : GenerationCandidateRun normalization generation) : + generation.WF env := + run.generationRun.wf + +/-- Complete dependent semantic package for one Verify-side singleton +candidate. + +`kernelSource` and `candidate` retain the exact implementation metadata and +source-indexed operational trace. `normalization` ties that trace to the raw +Theory declaration and its reconstructed view. `generation` is the successful +dependent analysis, and `run` proves that this exact candidate supplies every +semantic obligation consumed by mixed artifact generation. No independently +chosen view can be inserted into this package. -/ +structure GenerationCandidatePackage (env : VEnv) (Us : List Name) where + kernelSource : InductiveType + source : VInductDecl + candidate : AddInductive.NormalizationCandidate [kernelSource] + normalization : NormalizationCandidateRun env Us candidate source + generation : GenerationChecked source + run : GenerationCandidateRun normalization generation + +/-- Package an already assembled source-indexed candidate run without +repeating any of its dependent indices at a call site. -/ +def GenerationCandidateRun.package + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + {normalization : NormalizationCandidateRun env Us candidate source} + {generation : GenerationChecked source} + (run : GenerationCandidateRun normalization generation) : + GenerationCandidatePackage env Us where + kernelSource := kernelSource + source := source + candidate := candidate + normalization := normalization + generation := generation + run := run + +/-- Erase checker and candidate provenance at the consumer boundary. The +result contains only the Theory generation value and its ordinary semantic +certificate, which is the complete input of `VEnv.addInductCertified`. -/ +def GenerationCandidatePackage.certificate + (package : GenerationCandidatePackage env Us) : + package.source.GenerationCertificate env where + generation := package.generation + wf := package.run.wf + +/-- Build the general Verify metadata replay from a candidate package and the +ordinary implementation-to-Theory insertion witnesses. + +The generation value and its semantic proof are not independent inputs: both +are projected from `package`. The remaining arguments concern only constant +map alignment and the exact successful transaction states, so a caller cannot +pair metadata replay with an unrelated normalized view. -/ +def GenerationCandidatePackage.addInductTrace + (package : GenerationCandidatePackage env Us) + {m₁ m₂ : ConstMap} {env₂ : VEnv} + (typeMap : ConstMap) (typeEnv : VEnv) + (ctorMap : ConstMap) (ctorEnv recEnv : VEnv) + (addType : AddInductConstant .induct m₁ env + package.generation.block.sourceType.toVConstVal typeMap typeEnv) + (addCtors : AddInductConstants .ctor typeMap typeEnv + package.generation.block.sourceType.ctors ctorMap ctorEnv) + (addRec : AddInductConstant .recursor ctorMap ctorEnv + (inductGenerationRecVal package.generation) m₂ recEnv) + (recK : RecursorKMatches addRec.info package.generation.kTarget) + (addRules : AddDefEqs recEnv + package.generation.generatedRules env₂) : + AddInductTrace m₁ env package.source m₂ env₂ where + generation := package.generation + generation_wf := package.certificate.wf + typeMap := typeMap + typeEnv := typeEnv + ctorMap := ctorMap + ctorEnv := ctorEnv + recEnv := recEnv + addType := addType + addCtors := addCtors + addRec := addRec + recK := recK + addRules := addRules + +/-- Optional outer provenance for packages obtained by the executable +metadata pass itself. Keeping the exact producer equation separate from the +semantic package makes the trust boundary explicit: computation selects the +candidate, while `GenerationCandidateRun` alone grants it Theory meaning. -/ +structure ProducedGenerationCandidatePackage + (env : VEnv) (Us : List Name) where + package : GenerationCandidatePackage env Us + context : AddInductive.Context + nparams : Nat + numNested : Nat + isUnsafe : Bool + produced : + AddInductive.buildNormalizationCandidate nparams + [package.kernelSource] numNested isUnsafe context = + .ok package.candidate + +/-- Attach exact executable provenance to an already verified singleton +generation run. + +Both premises are indexed by the same kernel source and dependent candidate: +the executable equation therefore cannot be reused for a different semantic +run, reordered constructor list, or caller-selected view. Conversely, the +equation supplies no semantic authority by itself; all Theory meaning remains +in `run`. -/ +def GenerationCandidateRun.producedPackage + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + {normalization : NormalizationCandidateRun env Us candidate source} + {generation : GenerationChecked source} + (run : GenerationCandidateRun normalization generation) + (context : AddInductive.Context) + (nparams numNested : Nat) (isUnsafe : Bool) + (produced : + AddInductive.buildNormalizationCandidate nparams + [kernelSource] numNested isUnsafe context = .ok candidate) : + ProducedGenerationCandidatePackage env Us where + package := run.package + context := context + nparams := nparams + numNested := numNested + isUnsafe := isUnsafe + produced := produced + +/-- Package the no-parallel-run semantic assembler at the existing public +boundary. -/ +def GenerationCandidateSemanticRun.package + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + {normalization : NormalizationCandidateSemanticRun env Us candidate source} + {generation : GenerationChecked source} + (run : GenerationCandidateSemanticRun normalization generation) : + GenerationCandidatePackage env Us := + run.run.package + +/-- Attach the exact successful outer metadata call directly to a retained +semantic-generation owner. -/ +def GenerationCandidateSemanticRun.producedPackage + {env : VEnv} {Us : List Name} + {kernelSource : InductiveType} {source : VInductDecl} + {candidate : AddInductive.NormalizationCandidate [kernelSource]} + {normalization : NormalizationCandidateSemanticRun env Us candidate source} + {generation : GenerationChecked source} + (run : GenerationCandidateSemanticRun normalization generation) + (context : AddInductive.Context) + (nparams numNested : Nat) (isUnsafe : Bool) + (produced : + AddInductive.buildNormalizationCandidate nparams + [kernelSource] numNested isUnsafe context = .ok candidate) : + ProducedGenerationCandidatePackage env Us := + run.run.producedPackage context nparams numNested isUnsafe produced + +/- +The evidence types mention exact verifier executions, so the semantic +interpretation roots below intentionally inherit the same transitional Verify +closure as `WhnfRun.isDefEq`. Exact guards ensure that the generic assembler +does not silently widen it. Theory-only helper closures are guarded by +`Tests.TheoryConsumerSurface` without importing Verify. +-/ + +#guard_named_axioms Ix.Theory.Named.TypeChecker.AddInductConstant.safePrimitives [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateContextRun.context_env [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateContextRun.context_lctx [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateContextRun.context_safety [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateContextRun.context_lparams [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateContextRun.context_fuel [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateExprRun.view_isType_of_terminalSort [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateExprSemanticRootRun.source_isType_of_terminalSort [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateExprSemanticRootRun.viewParameters [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateExprSemanticRootRun.viewIndices [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateFamilyStagedInput [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateFamilyStagedInput.rawWF [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateFamilyStagedInput.postContext [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateFamilyStagedInput.postContextRun [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateFamilyStagedInput.postFamily [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateExprSemanticRootInput.exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateConstructorSemanticListInput.exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.NormalizationCandidateSemanticInput.exists_ofProduced [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.StagedNormalizationCandidateSemanticInput.exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateFamilySemanticGenerationRun.run [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateSemanticNormalizedCtorListRun.run [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationCandidateSemanticRun.run [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationCandidateSemanticShapeRun.run [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.candidateConstructorSemanticGenerationShape [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.normalizationCandidateGenerationShape [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateConstructorSemanticGenerationShapeList.ofCheck [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.NormalizationCandidateSemanticRun.generationShape [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.produceGenerationShapeCandidate [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.produceGenerationShapeCandidate_eq_ok [propext, Classical.choice, Quot.sound] +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationCandidateSemanticRun.package [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationCandidateSemanticRun.producedPackage [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.VState.WF.empty_of_reserves [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentArray.toList'_push, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.candidateFreshFVarId_reserved [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateContextRun.root [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Syntax.structEq_eq] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateContextRun.pushLocalDecl [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.eqv_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentArray.toList'_push, + Lean.Syntax.structEq_eq, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.candidateCheckTypeStep_exists_translation [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.IsDefEqRun.ofCandidateStep [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.IsDefEqRun.isDefEqU [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.candidateTypeAnnotation_fvarsIn [] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.candidateTypeAnnotation_exists_translation [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateExprRun.exists_ofCandidate [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateExprRun.exists_ofCandidateFVars [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.WhnfRun.ofCandidateStep [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CheckTypeRun.ofCandidateStep [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateNodeRun.ofCandidate [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateNodeRun.exists_ofCandidate [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateNodeRun.evidence [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateExprRun.evidence [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateExprRun.source_tr [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateExprRun.view_tr [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateExprRootRun.evidence [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.TelDefEqEvidence.telDefEq [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.TelDefEqEvidence.ofTelDefEq [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.TelResultDefEqEvidence.replacePrefix [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateExprRun.spineEvidence [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.TypeChecker.CandidateExprSpineRun.evidenceAt [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationCandidateRun.normalization_eq [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.NormalizationCandidateRun.sourceType_eq [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.NormalizationCandidateRun.familyViewType_eq [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationCandidateRun.familyView_eq [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationCandidateRun.typeEnv_wf [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationCandidateRun.familyConst_hasType [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateNormalizedCtorRun.rightType_ofChecked [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateNormalizedCtorRun.viewTel_eq [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateNormalizedCtorRun.normalizedCtorRun [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationCandidateRun.wf [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateConstructorListRun.sameHeaders [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateConstructorListRun.evidence [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.NormalizationCandidateRun.normalization [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.NormalizationCandidateRun.normalizationRun [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.NormalizedCtorRun.wf [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationRun.wf [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationCandidateRun.package [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationCandidateRun.producedPackage [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationCandidatePackage.certificate [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.GenerationCandidatePackage.addInductTrace [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.StagedNormalizationCandidateSemanticInput.constructorValidation_run [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.NormalizationBlockRun.wf [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateBlockFamilySemanticListRun.sameHeaders [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateBlockFamilySemanticListRun.evidence [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateBlockFamilySemanticInput.exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.CandidateBlockFamilySemanticListInput.exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.NormalizationCandidateBlockSemanticInput.exists [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.VInductDecl.NormalizationCandidateBlockSemanticInput.exists_ofProduced [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + + +end VInductDecl + +end Ix.Theory.Named diff --git a/Ix/Theory/Named/Verify/Environment/NormalizationMatrix.lean b/Ix/Theory/Named/Verify/Environment/NormalizationMatrix.lean new file mode 100644 index 000000000..4c68f01b9 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/NormalizationMatrix.lean @@ -0,0 +1,749 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Verify.Environment.InductiveFixtures + +open Ix.Theory (VLevel) + +/-! +# Normalization differential matrix + +Executable and semantic replay for the Spec-04 matrix. The fixture deliberately +places reducible aliases at every inductive-analysis boundary, including a +Pi-producing alias around a recursive target. This file observes the actual +kernel declarations, pins the exact analyzer output and its fuel boundary, and +then translates the same declarations into the Theory environment. +-/ + +namespace Ix.Theory.Named.InductiveReplayFixtures + +open Lean Meta +open Ix.Theory.Named.InductiveFixtures + +/-! ## Actual kernel metadata and executable analysis -/ + +def matrixBetaKernelDef : DefinitionVal := kernelDefVal% MatrixBetaAlias +def matrixLetKernelDef : DefinitionVal := kernelDefVal% MatrixLetAlias +def matrixPiKernelDef : DefinitionVal := kernelDefVal% MatrixPiAlias +def matrixIndexKernelDef : DefinitionVal := kernelDefVal% MatrixIndexAlias + +def matrixBetaInfo : ConstantInfo := .defnInfo matrixBetaKernelDef +def matrixLetInfo : ConstantInfo := .defnInfo matrixLetKernelDef +def matrixPiInfo : ConstantInfo := .defnInfo matrixPiKernelDef +def matrixIndexInfo : ConstantInfo := .defnInfo matrixIndexKernelDef + +def matrixRecMap : ConstMap := + typeFamilyAliasMap.insert ``RecAlias recAliasInfo + +def matrixBetaMap : ConstMap := + matrixRecMap.insert ``MatrixBetaAlias matrixBetaInfo + +def matrixLetMap : ConstMap := + matrixBetaMap.insert ``MatrixLetAlias matrixLetInfo + +def matrixPiMap : ConstMap := + matrixLetMap.insert ``MatrixPiAlias matrixPiInfo + +def matrixAliasMap : ConstMap := + matrixPiMap.insert ``MatrixIndexAlias matrixIndexInfo + +theorem matrixRec_fresh : + typeFamilyAliasMap.find? ``RecAlias = none := by + rw [typeFamilyAliasMap, SMap.WF.find?_insert + (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem matrixRecMap_wf : matrixRecMap.WF := + typeFamilyAliasMap_wf.insert _ _ matrixRec_fresh + +theorem matrixBeta_fresh : + matrixRecMap.find? ``MatrixBetaAlias = none := by + rw [matrixRecMap, typeFamilyAliasMap_wf.find?_insert, + typeFamilyAliasMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem matrixBetaMap_wf : matrixBetaMap.WF := + matrixRecMap_wf.insert _ _ matrixBeta_fresh + +theorem matrixLet_fresh : + matrixBetaMap.find? ``MatrixLetAlias = none := by + rw [matrixBetaMap, matrixRecMap_wf.find?_insert, + matrixRecMap, typeFamilyAliasMap_wf.find?_insert, + typeFamilyAliasMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem matrixLetMap_wf : matrixLetMap.WF := + matrixBetaMap_wf.insert _ _ matrixLet_fresh + +theorem matrixPi_fresh : matrixLetMap.find? ``MatrixPiAlias = none := by + rw [matrixLetMap, matrixBetaMap_wf.find?_insert, + matrixBetaMap, matrixRecMap_wf.find?_insert, + matrixRecMap, typeFamilyAliasMap_wf.find?_insert, + typeFamilyAliasMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem matrixPiMap_wf : matrixPiMap.WF := + matrixLetMap_wf.insert _ _ matrixPi_fresh + +theorem matrixIndex_fresh : + matrixPiMap.find? ``MatrixIndexAlias = none := by + rw [matrixPiMap, matrixLetMap_wf.find?_insert, + matrixLetMap, matrixBetaMap_wf.find?_insert, + matrixBetaMap, matrixRecMap_wf.find?_insert, + matrixRecMap, typeFamilyAliasMap_wf.find?_insert, + typeFamilyAliasMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem matrixAliasMap_wf : matrixAliasMap.WF := + matrixPiMap_wf.insert _ _ matrixIndex_fresh + +def matrixKernelEnv : Kernel.Environment := + Kernel.Environment.ofConstants `_normalizationMatrix matrixAliasMap + +def normalizationMatrixInfo : ConstantInfo := + kernelInductInfo% NormalizationMatrix + +def normalizationMatrixMkInfo : ConstantInfo := + kernelCtorInfo% NormalizationMatrix.mk + +def normalizationMatrixRecInfo : ConstantInfo := + kernelRecInfo% NormalizationMatrix.rec + +def normalizationMatrixKernelRuleRhs : VExpr := + kernelRecRuleRhs% NormalizationMatrix.rec 0 + +def normalizationMatrixKernelCtor : Constructor where + name := normalizationMatrixMkInfo.name + type := normalizationMatrixMkInfo.type + +def normalizationMatrixKernelType : InductiveType where + name := normalizationMatrixInfo.name + type := normalizationMatrixInfo.type + ctors := [normalizationMatrixKernelCtor] + +theorem normalizationMatrix_kernel_family_name : + normalizationMatrixInfo.name = ``NormalizationMatrix := rfl + +theorem normalizationMatrix_kernel_ctor_name : + normalizationMatrixMkInfo.name = ``NormalizationMatrix.mk := rfl + +theorem normalizationMatrix_kernel_recursor_name : + normalizationMatrixRecInfo.name = ``NormalizationMatrix.rec := rfl + +theorem normalizationMatrix_kernel_constructor_shape : + (match normalizationMatrixMkInfo with + | .ctorInfo info => (info.numParams, info.numFields) + | _ => (0, 0)) = (1, 8) := rfl + +theorem normalizationMatrix_kernel_recursor_shape : + (match normalizationMatrixRecInfo with + | .recInfo info => + (info.numParams, info.numIndices, info.numMotives, + info.numMinors, info.rules.length) + | _ => (0, 0, 0, 0, 0)) = (1, 1, 1, 1, 1) := rfl + +theorem normalizationMatrix_kernel_rule_exact : + normalizationMatrixKernelRuleRhs = + normalizationMatrixGenerationChecked.generatedRules[0].rhs := rfl + +theorem normalizationMatrix_recursive_positions_exact : + normalizationMatrixViewChecked.constructors[0].recursive.map + (fun position => (position.fieldIndex, position.binders.length)) = + [(4, 0), (5, 1), (6, 0), (7, 0)] := rfl + +def matrixCandidateContext (fuel : Nat) : AddInductive.Context where + env := matrixKernelEnv + lparams := [] + safety := .safe + allowPrimitive := false + fuel := { inductiveFuel := fuel } + +def matrixCandidateAccepted (fuel : Nat) : Bool := + match AddInductive.buildNormalizationCandidate 1 + [normalizationMatrixKernelType] 0 false (matrixCandidateContext fuel) with + | .ok _ => true + | .error _ => false + +def matrixTargetExpr (alpha index : Expr) : Expr := + .app (.app (.const ``NormalizationMatrix []) alpha) + (.app (.const ``MatrixIndexAlias []) index) + +def matrixExpectedFamilyView : Expr := + .forallE `alpha (.sort (.succ .zero)) + (.forallE `index (.sort (.succ .zero)) + (.sort (.succ (.succ .zero))) .default) .default + +def matrixExpectedCtorView : Expr := + .forallE `alpha (.sort (.succ .zero)) + (.forallE `index (.sort (.succ .zero)) + (.forallE `ordinary (.sort .zero) + (.forallE `beta (.sort .zero) + (.forallE `letBound (.sort .zero) + (.forallE `direct (matrixTargetExpr (.bvar 4) (.bvar 3)) + (.forallE `piHidden + (.forallE `proof (.sort .zero) + (matrixTargetExpr (.bvar 6) (.bvar 5)) .default) + (.forallE `betaRecursive + (matrixTargetExpr (.bvar 6) (.bvar 5)) + (.forallE `letRecursive + (matrixTargetExpr (.bvar 7) (.bvar 6)) + (matrixTargetExpr (.bvar 8) (.bvar 7)) .default) + .default) + .default) + .default) + .default) + .default) + .default) + .default) + .implicit + +/-- Binder names are not semantic metadata, so exact executable comparisons +canonicalize only those names and preserve every other expression node. -/ +def matrixCanonicalExpr : Expr → Expr + | .app fn arg => .app (matrixCanonicalExpr fn) (matrixCanonicalExpr arg) + | .forallE _ domain body bi => + .forallE `_ (matrixCanonicalExpr domain) (matrixCanonicalExpr body) bi + | expr => expr + +def matrixCandidateExact (context : AddInductive.Context) : Bool := + match AddInductive.buildNormalizationCandidate 1 + [normalizationMatrixKernelType] 0 false context with + | .ok candidate => + (matrixCanonicalExpr candidate.families.singleton.familyType.type.view).equal + (matrixCanonicalExpr matrixExpectedFamilyView) && + match candidate.families.singleton.constructors with + | .cons constructor .nil => + (matrixCanonicalExpr constructor.type.view).equal + (matrixCanonicalExpr matrixExpectedCtorView) + | .error _ => false + +#guard matrixCandidateExact (matrixCandidateContext 10) +#guard matrixCandidateAccepted 10 +#guard !(matrixCandidateAccepted 9) + +def matrixOpaquePiAliasInfo : ConstantInfo := + .axiomInfo { + name := ``MatrixPiAlias + levelParams := matrixPiKernelDef.levelParams + type := matrixPiKernelDef.type + isUnsafe := false } + +def matrixOpaquePiAliasMap : ConstMap := + matrixAliasMap.insert ``MatrixPiAlias matrixOpaquePiAliasInfo + +def matrixOpaquePiAliasContext : AddInductive.Context where + env := Kernel.Environment.ofConstants `_normalizationMatrixOpaquePi + matrixOpaquePiAliasMap + lparams := [] + safety := .safe + allowPrimitive := false + fuel := { inductiveFuel := 10 } + +#guard !(matrixCandidateExact matrixOpaquePiAliasContext) + +def matrixNonDefEqCtor : Constructor where + name := normalizationMatrixMkInfo.name + type := .forallE `alpha (.sort .zero) + (matrixTargetExpr (.sort .zero) (.sort .zero)) .implicit + +def matrixNonDefEqType : InductiveType := + { normalizationMatrixKernelType with ctors := [matrixNonDefEqCtor] } + +#guard match AddInductive.buildNormalizationCandidate 1 + [matrixNonDefEqType] 0 false (matrixCandidateContext 10) with + | .error (.other message) => + message == + "arg #1 of 'Ix.Theory.Named.InductiveFixtures.NormalizationMatrix.mk' does not match inductive datatype parameters" + | _ => false + +/-! ## Translation of every retained alias -/ + +def matrixBetaVal : VDefVal where + name := ``MatrixBetaAlias + uvars := (vconst(type_of% @MatrixBetaAlias) : VConstant).uvars + type := (vconst(type_of% @MatrixBetaAlias) : VConstant).type + value := matrixBetaAliasDefEq.rhs + +def matrixLetVal : VDefVal where + name := ``MatrixLetAlias + uvars := (vconst(type_of% @MatrixLetAlias) : VConstant).uvars + type := (vconst(type_of% @MatrixLetAlias) : VConstant).type + value := matrixLetAliasDefEq.rhs + +def matrixPiVal : VDefVal where + name := ``MatrixPiAlias + uvars := (vconst(type_of% @MatrixPiAlias) : VConstant).uvars + type := (vconst(type_of% @MatrixPiAlias) : VConstant).type + value := matrixPiAliasDefEq.rhs + +def matrixIndexVal : VDefVal where + name := ``MatrixIndexAlias + uvars := (vconst(type_of% @MatrixIndexAlias) : VConstant).uvars + type := (vconst(type_of% @MatrixIndexAlias) : VConstant).type + value := matrixIndexAliasDefEq.rhs + +theorem matrixRecInfo_tr : + TrDefVal .safe typeFamilyAliasEnv recAliasInfo recAliasVal := + recAliasInfo_tr.mono + ((VEnv.addConst_le (by rfl : + VEnv.empty.addConst ``TypeFamilyAlias + (vconst(type_of% @TypeFamilyAlias)) = + some typeFamilyAliasConstEnv)).trans VEnv.addDefEq_le) + +theorem matrixBetaInfo_tr : + TrDefVal .safe normalizationMatrixRecAliasEnv + matrixBetaInfo matrixBetaVal := by + refine ⟨⟨⟨by decide, rfl, ?_⟩, rfl⟩, ?_⟩ + · have hshape : TrTypeExpr normalizationMatrixRecAliasEnv + matrixBetaInfo.levelParams [] matrixBetaInfo.type matrixBetaVal.type := by + tr_type_expr_tac + exact hshape.to_trExprS normalizationMatrixRecAliasEnv_ordered trivial + ⟨_, by type_tac⟩ + · refine .lam ⟨_, VEnv.HasType.sort (by decide)⟩ (.sort rfl) ?_ + refine .app + (VEnv.HasType.lam + (VEnv.HasType.sort (by decide)) + (VEnv.HasType.bvar .zero)) + (VEnv.HasType.bvar .zero) ?_ (.bvar rfl) + exact .lam ⟨_, VEnv.HasType.sort (by decide)⟩ (.sort rfl) (.bvar rfl) + +theorem matrixLetInfo_tr : + TrDefVal .safe normalizationMatrixBetaAliasEnv + matrixLetInfo matrixLetVal := by + refine ⟨⟨⟨by decide, rfl, ?_⟩, rfl⟩, ?_⟩ + · have hshape : TrTypeExpr normalizationMatrixBetaAliasEnv + matrixLetInfo.levelParams [] matrixLetInfo.type matrixLetVal.type := by + tr_type_expr_tac + exact hshape.to_trExprS normalizationMatrixBetaAliasEnv_ordered trivial + ⟨_, by type_tac⟩ + · refine .lam ⟨_, VEnv.HasType.sort (by decide)⟩ (.sort rfl) ?_ + exact .letE (by type_tac) (.sort rfl) (.bvar rfl) (.bvar rfl) + +theorem matrixPiInfo_tr : + TrDefVal .safe normalizationMatrixLetAliasEnv matrixPiInfo matrixPiVal := by + refine ⟨⟨⟨by decide, rfl, ?_⟩, rfl⟩, ?_⟩ + · have hshape : TrTypeExpr normalizationMatrixLetAliasEnv + matrixPiInfo.levelParams [] matrixPiInfo.type matrixPiVal.type := by + tr_type_expr_tac + exact hshape.to_trExprS normalizationMatrixLetAliasEnv_ordered trivial + ⟨_, by type_tac⟩ + · refine .lam ⟨_, VEnv.HasType.sort (by decide)⟩ (.sort rfl) ?_ + apply TrExprS.forallE + · exact ⟨_, VEnv.HasType.sort (by decide)⟩ + · exact ⟨.param 0, VEnv.HasType.bvar (.succ .zero)⟩ + · exact .sort rfl + · exact .bvar rfl + +theorem matrixIndexInfo_tr : + TrDefVal .safe normalizationMatrixPiAliasEnv + matrixIndexInfo matrixIndexVal := by + have hfamily : normalizationMatrixPiAliasEnv.constants + ``TypeFamilyAlias = some (vconst(type_of% @TypeFamilyAlias)) := rfl + refine ⟨⟨⟨by decide, rfl, ?_⟩, rfl⟩, ?_⟩ + · have hshape : TrTypeExpr normalizationMatrixPiAliasEnv + matrixIndexInfo.levelParams [] matrixIndexInfo.type matrixIndexVal.type := by + tr_type_expr_tac + exact hshape.to_trExprS normalizationMatrixPiAliasEnv_ordered trivial + ⟨_, by type_tac⟩ + · refine .lam ⟨_, by type_tac⟩ ?_ (.bvar rfl) + exact .const rfl rfl rfl + +theorem matrixBetaVal_wf : matrixBetaVal.WF + normalizationMatrixRecAliasEnv := by + type_tac + +theorem matrixLetVal_wf : matrixLetVal.WF + normalizationMatrixBetaAliasEnv := by + type_tac + +theorem matrixPiVal_wf : matrixPiVal.WF + normalizationMatrixLetAliasEnv := by + apply VEnv.HasType.lam + · exact VEnv.HasType.sort (by decide) + · apply VEnv.IsDefEq.defeq + (VEnv.IsDefEq.sortDF + (l := .imax (.succ .zero) (.param 0)) (l' := .param 0) + (by decide) (by decide) (by + rw [VLevel.equiv_def] + intro ls + simp only [VLevel.eval, Nat.zero_add] + let n := ls.getD 0 0 + change Nat.imax 1 n = n + by_cases h : n = 0 + · simp [Nat.imax, h] + · have hn : 1 ≤ n := Nat.one_le_iff_ne_zero.mpr h + simp [Nat.imax, h, Nat.max_eq_right hn])) + exact VEnv.HasType.forallE (VEnv.HasType.sort (by decide)) + (VEnv.HasType.bvar (.succ .zero)) + +theorem matrixIndexVal_wf : matrixIndexVal.WF + normalizationMatrixPiAliasEnv := by + have hfamily : normalizationMatrixPiAliasEnv.constants + ``TypeFamilyAlias = some (vconst(type_of% @TypeFamilyAlias)) := rfl + type_tac + +theorem matrixRec_trEnv' : + TrEnv' .safe matrixRecMap false normalizationMatrixRecAliasEnv := + .defn (ci := recAliasKernelDef) (ci' := recAliasVal) + matrixRecInfo_tr matrixRec_fresh + (recAliasVal_wf.mono + ((VEnv.addConst_le (by rfl : + VEnv.empty.addConst ``TypeFamilyAlias + (vconst(type_of% @TypeFamilyAlias)) = + some typeFamilyAliasConstEnv)).trans VEnv.addDefEq_le)) + rfl typeFamilyAlias_trEnv' + +theorem matrixBeta_trEnv' : + TrEnv' .safe matrixBetaMap false normalizationMatrixBetaAliasEnv := + .defn (ci := matrixBetaKernelDef) (ci' := matrixBetaVal) + matrixBetaInfo_tr matrixBeta_fresh matrixBetaVal_wf rfl matrixRec_trEnv' + +theorem matrixLet_trEnv' : + TrEnv' .safe matrixLetMap false normalizationMatrixLetAliasEnv := + .defn (ci := matrixLetKernelDef) (ci' := matrixLetVal) + matrixLetInfo_tr matrixLet_fresh matrixLetVal_wf rfl matrixBeta_trEnv' + +theorem matrixPi_trEnv' : + TrEnv' .safe matrixPiMap false normalizationMatrixPiAliasEnv := + .defn (ci := matrixPiKernelDef) (ci' := matrixPiVal) + matrixPiInfo_tr matrixPi_fresh matrixPiVal_wf rfl matrixLet_trEnv' + +theorem matrixAlias_trEnv' : + TrEnv' .safe matrixAliasMap false normalizationMatrixAliasEnv := + .defn (ci := matrixIndexKernelDef) (ci' := matrixIndexVal) + matrixIndexInfo_tr matrixIndex_fresh matrixIndexVal_wf rfl matrixPi_trEnv' + +/-! ## Replay of the actual inductive transaction -/ + +def normalizationMatrixTypeEnv : VEnv := + (normalizationMatrixAliasEnv.addConst normalizationMatrixRawType.name + normalizationMatrixRawType.toVConstant).get (by decide) + +def normalizationMatrixCtorEnv : VEnv := + (normalizationMatrixTypeEnv.addConst + normalizationMatrixRawType.ctors[0].name + normalizationMatrixRawType.ctors[0].toVConstant).get (by decide) + +def normalizationMatrixRecEnv : VEnv := + (normalizationMatrixCtorEnv.addConst ``NormalizationMatrix.rec + normalizationMatrixGenerationChecked.recursor).get (by decide) + +theorem normalizationMatrixTypeEnv_ordered : + normalizationMatrixTypeEnv.Ordered := by + refine .const (n := normalizationMatrixRawType.name) + (ci := normalizationMatrixRawType.toVConstant) + normalizationMatrixAliasEnv_ordered ?_ rfl + show normalizationMatrixAliasEnv.IsType + normalizationMatrixGenerationChecked.block.sourceType.uvars [] + normalizationMatrixGenerationChecked.block.sourceType.type + rw [normalizationMatrixGenerationChecked.block.sourceType_uvars_eq] + exact normalizationMatrixGenerationChecked_wf.rawFamily_isType + +theorem normalizationMatrixRawCtor_wf : + normalizationMatrixRawType.ctors[0].toVConstant.WF + normalizationMatrixTypeEnv := by + have hctor : + (⟨normalizationMatrixRawType.ctors[0], + normalizationMatrixViewChecked.constructors[0]⟩ : + VInductDecl.NormalizedCtor) ∈ + normalizationMatrixGenerationChecked.block.ctorPairs := by + exact .head _ + show normalizationMatrixTypeEnv.IsType + normalizationMatrixRawType.ctors[0].uvars [] + normalizationMatrixRawType.ctors[0].type + rw [normalizationMatrixGenerationChecked.ctor_uvars_eq hctor] + exact normalizationMatrixGenerationChecked_wf.rawCtor_isType rfl hctor + +theorem normalizationMatrixCtorEnv_ordered : + normalizationMatrixCtorEnv.Ordered := + .const (n := normalizationMatrixRawType.ctors[0].name) + (ci := normalizationMatrixRawType.ctors[0].toVConstant) + normalizationMatrixTypeEnv_ordered normalizationMatrixRawCtor_wf rfl + +theorem normalizationMatrixGenerationEnv : + VInductDecl.GenerationEnv normalizationMatrixGenerationChecked + normalizationMatrixCtorEnv := by + apply normalizationMatrixGenerationChecked_wf.toGenerationEnv + (envT := normalizationMatrixTypeEnv) + · rfl + · exact (VEnv.addConst_le (show + normalizationMatrixAliasEnv.addConst normalizationMatrixRawType.name + normalizationMatrixRawType.toVConstant = + some normalizationMatrixTypeEnv from rfl)).trans + (VEnv.addConst_le (show + normalizationMatrixTypeEnv.addConst + normalizationMatrixRawType.ctors[0].name + normalizationMatrixRawType.ctors[0].toVConstant = + some normalizationMatrixCtorEnv from rfl)) + · exact VEnv.addConst_le (show + normalizationMatrixTypeEnv.addConst + normalizationMatrixRawType.ctors[0].name + normalizationMatrixRawType.ctors[0].toVConstant = + some normalizationMatrixCtorEnv from rfl) + · exact normalizationMatrixCtorEnv_ordered + · rfl + · intro ctor hctor + change ctor ∈ + [⟨normalizationMatrixRawType.ctors[0], + normalizationMatrixViewChecked.constructors[0]⟩] at hctor + obtain rfl := List.mem_singleton.1 hctor + rfl + +theorem normalizationMatrixInfo_tr : + TrConstVal .safe normalizationMatrixAliasEnv normalizationMatrixInfo + normalizationMatrixRawType.toVConstVal := by + have hTypeFamily : normalizationMatrixAliasEnv.constants + ``TypeFamilyAlias = some (vconst(type_of% @TypeFamilyAlias)) := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr normalizationMatrixAliasEnv + normalizationMatrixInfo.levelParams [] normalizationMatrixInfo.type + normalizationMatrixRawType.type := by + tr_type_expr_tac + obtain ⟨u, htype⟩ := + normalizationMatrixGenerationChecked_wf.rawFamily_isType + exact hshape.to_trExprS normalizationMatrixAliasEnv_ordered trivial + ⟨.sort u, htype⟩ + +theorem normalizationMatrixMkInfo_tr : + TrConstVal .safe normalizationMatrixTypeEnv normalizationMatrixMkInfo + normalizationMatrixRawType.ctors[0] := by + have hTypeFamily : normalizationMatrixTypeEnv.constants + ``TypeFamilyAlias = some (vconst(type_of% @TypeFamilyAlias)) := rfl + have hRecAlias : normalizationMatrixTypeEnv.constants + ``RecAlias = some (vconst(type_of% @RecAlias)) := rfl + have hBetaAlias : normalizationMatrixTypeEnv.constants + ``MatrixBetaAlias = some (vconst(type_of% @MatrixBetaAlias)) := rfl + have hLetAlias : normalizationMatrixTypeEnv.constants + ``MatrixLetAlias = some (vconst(type_of% @MatrixLetAlias)) := rfl + have hPiAlias : normalizationMatrixTypeEnv.constants + ``MatrixPiAlias = some (vconst(type_of% @MatrixPiAlias)) := rfl + have hIndexAlias : normalizationMatrixTypeEnv.constants + ``MatrixIndexAlias = some (vconst(type_of% @MatrixIndexAlias)) := rfl + have hFamily : normalizationMatrixTypeEnv.constants + ``NormalizationMatrix = some normalizationMatrixRawType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr normalizationMatrixTypeEnv + normalizationMatrixMkInfo.levelParams [] normalizationMatrixMkInfo.type + normalizationMatrixRawType.ctors[0].type := by + tr_type_expr_tac + obtain ⟨u, htype⟩ := normalizationMatrixRawCtor_wf + exact hshape.to_trExprS normalizationMatrixTypeEnv_ordered trivial + ⟨.sort u, htype⟩ + +theorem normalizationMatrixRecInfo_tr : + TrConstVal .safe normalizationMatrixCtorEnv normalizationMatrixRecInfo + (inductGenerationRecVal normalizationMatrixGenerationChecked) := by + have hTypeFamily : normalizationMatrixCtorEnv.constants + ``TypeFamilyAlias = some (vconst(type_of% @TypeFamilyAlias)) := rfl + have hIndexAlias : normalizationMatrixCtorEnv.constants + ``MatrixIndexAlias = some (vconst(type_of% @MatrixIndexAlias)) := rfl + have hFamily : normalizationMatrixCtorEnv.constants + ``NormalizationMatrix = some normalizationMatrixRawType.toVConstant := rfl + have hMk : normalizationMatrixCtorEnv.constants + ``NormalizationMatrix.mk = + some normalizationMatrixRawType.ctors[0].toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have hshape : TrTypeExpr normalizationMatrixCtorEnv + normalizationMatrixRecInfo.levelParams [] normalizationMatrixRecInfo.type + (inductGenerationRecVal normalizationMatrixGenerationChecked).type := by + tr_type_expr_tac + obtain ⟨u, hrec⟩ := normalizationMatrixGenerationEnv.recursor_wf + exact hshape.to_trExprS normalizationMatrixCtorEnv_ordered trivial + ⟨.sort u, hrec⟩ + +def normalizationMatrixTypeMap : ConstMap := + matrixAliasMap.insert ``NormalizationMatrix normalizationMatrixInfo + +def normalizationMatrixCtorMap : ConstMap := + normalizationMatrixTypeMap.insert ``NormalizationMatrix.mk + normalizationMatrixMkInfo + +def normalizationMatrixMap : ConstMap := + normalizationMatrixCtorMap.insert ``NormalizationMatrix.rec + normalizationMatrixRecInfo + +theorem normalizationMatrixType_fresh : + matrixAliasMap.find? ``NormalizationMatrix = none := by + rw [matrixAliasMap, matrixPiMap_wf.find?_insert, + matrixPiMap, matrixLetMap_wf.find?_insert, + matrixLetMap, matrixBetaMap_wf.find?_insert, + matrixBetaMap, matrixRecMap_wf.find?_insert, + matrixRecMap, typeFamilyAliasMap_wf.find?_insert, + typeFamilyAliasMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem normalizationMatrixTypeMap_wf : normalizationMatrixTypeMap.WF := + matrixAliasMap_wf.insert _ _ normalizationMatrixType_fresh + +theorem normalizationMatrixMk_fresh : + normalizationMatrixTypeMap.find? ``NormalizationMatrix.mk = none := by + rw [normalizationMatrixTypeMap, matrixAliasMap_wf.find?_insert, + matrixAliasMap, matrixPiMap_wf.find?_insert, + matrixPiMap, matrixLetMap_wf.find?_insert, + matrixLetMap, matrixBetaMap_wf.find?_insert, + matrixBetaMap, matrixRecMap_wf.find?_insert, + matrixRecMap, typeFamilyAliasMap_wf.find?_insert, + typeFamilyAliasMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem normalizationMatrixCtorMap_wf : normalizationMatrixCtorMap.WF := + normalizationMatrixTypeMap_wf.insert _ _ normalizationMatrixMk_fresh + +theorem normalizationMatrixRec_fresh : + normalizationMatrixCtorMap.find? ``NormalizationMatrix.rec = none := by + rw [normalizationMatrixCtorMap, + normalizationMatrixTypeMap_wf.find?_insert, + normalizationMatrixTypeMap, matrixAliasMap_wf.find?_insert, + matrixAliasMap, matrixPiMap_wf.find?_insert, + matrixPiMap, matrixLetMap_wf.find?_insert, + matrixLetMap, matrixBetaMap_wf.find?_insert, + matrixBetaMap, matrixRecMap_wf.find?_insert, + matrixRecMap, typeFamilyAliasMap_wf.find?_insert, + typeFamilyAliasMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +private def normalizationMatrixAddInductTraceWith + (generation_wf : + normalizationMatrixGenerationChecked.WF normalizationMatrixAliasEnv) : + AddInductTrace matrixAliasMap normalizationMatrixAliasEnv + normalizationMatrixRawDecl normalizationMatrixMap + normalizationMatrixFinalEnv := by + refine { + generation := normalizationMatrixGenerationChecked + generation_wf := generation_wf + typeMap := normalizationMatrixTypeMap + typeEnv := normalizationMatrixTypeEnv + ctorMap := normalizationMatrixCtorMap + ctorEnv := normalizationMatrixCtorEnv + recEnv := normalizationMatrixRecEnv + addType := { + info := normalizationMatrixInfo + kind_eq := by simp [normalizationMatrixInfo, InductConstantKind.Matches] + tr := normalizationMatrixInfo_tr + map_fresh := by + change matrixAliasMap.find? ``NormalizationMatrix = none + exact normalizationMatrixType_fresh + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := normalizationMatrixRecInfo + kind_eq := by + simp [normalizationMatrixRecInfo, InductConstantKind.Matches] + tr := normalizationMatrixRecInfo_tr + map_fresh := by + change normalizationMatrixCtorMap.find? + ``NormalizationMatrix.rec = none + exact normalizationMatrixRec_fresh + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ } + exact .cons { + info := normalizationMatrixMkInfo + kind_eq := by + simp [normalizationMatrixMkInfo, InductConstantKind.Matches] + tr := normalizationMatrixMkInfo_tr + map_fresh := by + simpa [normalizationMatrixRawType] using normalizationMatrixMk_fresh + env_add := rfl + map_add := rfl } .nil + +theorem normalizationMatrix_addInduct : + AddInduct matrixAliasMap normalizationMatrixAliasEnv + normalizationMatrixRawDecl normalizationMatrixMap + normalizationMatrixFinalEnv := + ⟨normalizationMatrixAddInductTraceWith + normalizationMatrixGenerationChecked_wf⟩ + +/-- Actual kernel metadata for the complete matrix is aligned with the Theory +environment obtained from the semantically checked normalization transaction. -/ +theorem normalizationMatrix_trEnv' : + TrEnv' .safe normalizationMatrixMap false normalizationMatrixFinalEnv := + .induct normalizationMatrix_addInduct matrixAlias_trEnv' + +theorem normalizationMatrix_final_matches_generation : + normalizationMatrixAliasEnv.addInductGeneration + normalizationMatrixGenerationChecked = + some normalizationMatrixFinalEnv := + normalizationMatrix_addInductGeneration + +theorem normalizationMatrix_env_wf : normalizationMatrixFinalEnv.WF := + normalizationMatrix_trEnv'.wf + +theorem normalizationMatrix_aligned : + Aligned .safe normalizationMatrixMap normalizationMatrixFinalEnv := + normalizationMatrix_trEnv'.aligned + +theorem normalizationMatrix_type_map_lookup : + normalizationMatrixMap.find? ``NormalizationMatrix = + some normalizationMatrixInfo := by + rw [normalizationMatrixMap, normalizationMatrixCtorMap_wf.find?_insert, + normalizationMatrixCtorMap, normalizationMatrixTypeMap_wf.find?_insert, + normalizationMatrixTypeMap, matrixAliasMap_wf.find?_insert] + rfl + +theorem normalizationMatrix_type_lookup_unique : + normalizationMatrixInfo.name = ``NormalizationMatrix ∧ + TrConstant .safe normalizationMatrixFinalEnv normalizationMatrixInfo + normalizationMatrixRawType.toVConstant := + normalizationMatrix_aligned.find?_uniq + normalizationMatrix_type_map_lookup + normalizationMatrixFinalEnv_family_lookup + +theorem normalizationMatrix_mk_map_lookup : + normalizationMatrixMap.find? ``NormalizationMatrix.mk = + some normalizationMatrixMkInfo := by + rw [normalizationMatrixMap, normalizationMatrixCtorMap_wf.find?_insert, + normalizationMatrixCtorMap, normalizationMatrixTypeMap_wf.find?_insert] + rfl + +theorem normalizationMatrix_mk_lookup_unique : + normalizationMatrixMkInfo.name = ``NormalizationMatrix.mk ∧ + TrConstant .safe normalizationMatrixFinalEnv normalizationMatrixMkInfo + normalizationMatrixRawType.ctors[0].toVConstant := + normalizationMatrix_aligned.find?_uniq + normalizationMatrix_mk_map_lookup + normalizationMatrixFinalEnv_ctor_lookup + +theorem normalizationMatrix_rec_map_lookup : + normalizationMatrixMap.find? ``NormalizationMatrix.rec = + some normalizationMatrixRecInfo := by + rw [normalizationMatrixMap, normalizationMatrixCtorMap_wf.find?_insert] + rfl + +theorem normalizationMatrix_rec_lookup_unique : + normalizationMatrixRecInfo.name = ``NormalizationMatrix.rec ∧ + TrConstant .safe normalizationMatrixFinalEnv normalizationMatrixRecInfo + normalizationMatrixGenerationChecked.recursor := + normalizationMatrix_aligned.find?_uniq + normalizationMatrix_rec_map_lookup + normalizationMatrixFinalEnv_rec_lookup + +/-! The semantic generation helpers used above are guarded in +`Theory.Typing.InductiveLemmas`; these two guards pin the separate, now +`sorryAx`-free Verify closure of metadata translation and final environment +replay. -/ + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.normalizationMatrixInfo_tr [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.normalizationMatrix_trEnv' [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +end Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/SingletonParityMatrix.lean b/Ix/Theory/Named/Verify/Environment/SingletonParityMatrix.lean new file mode 100644 index 000000000..f60012ae8 --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/SingletonParityMatrix.lean @@ -0,0 +1,471 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.SingletonParity +import Ix.Theory.Named.Verify.Environment.ConstructorValidityMatrix +import Ix.Theory.Named.Verify.Environment.EliminationFixturesEqNat +import Ix.Theory.Named.Verify.Environment.EliminationFixturesOrAnd +import Ix.Theory.Named.Verify.Environment.EliminationFixturesEdges +import Ix.Theory.Named.Verify.Environment.NormalizationMatrix + +/-! +# Spec-07 complete singleton kernel matrix + +The rows below join the single Theory artifact inventory to Lean's actual +`inductInfo`/`ctorInfo`/`recInfo` records. The executable predicate compares +the retained raw types, names, parameter/index and field counts, universe +order, elimination/K metadata, recursor type, rule count, and every iota RHS. +It also reruns the ordinary normalization producer on the exact kernel source. +-/ + +namespace Ix.Theory.Named.InductiveReplayFixtures + +open Lean Meta Elab Term +open Ix.Theory.Named.InductiveFixtures + +/-! ## Exact conversion of stored kernel types -/ + +syntax "kernelConstVType%" ident : term + +/-- Quote the stored `ConstantInfo.type` using that record's own universe +parameter order. Unlike `vconst(type_of% ...)`, this observes the raw kernel +record directly. -/ +elab_rules : term + | `(kernelConstVType% $n:ident) => do + let name ← realizeGlobalConstNoOverloadWithInfo n + let info ← getConstInfo name + let type ← Ix.Theory.Named.Meta.expandExpr info.type + let type ← Ix.Theory.Named.Meta.ofExpr info.levelParams {} type + return toExpr type + +/-! ## Missing standard-library metadata rows -/ + +def boolInfo07 : ConstantInfo := kernelInductInfo% Bool +def boolFalseInfo07 : ConstantInfo := kernelCtorInfo% Bool.false +def boolTrueInfo07 : ConstantInfo := kernelCtorInfo% Bool.true +def boolRecInfo07 : ConstantInfo := kernelRecInfo% Bool.rec +def boolFalseRuleRhs07 : VExpr := kernelRecRuleRhs% Bool.rec 0 +def boolTrueRuleRhs07 : VExpr := kernelRecRuleRhs% Bool.rec 1 + +def listInfo07 : ConstantInfo := kernelInductInfo% List +def listNilInfo07 : ConstantInfo := kernelCtorInfo% List.nil +def listConsInfo07 : ConstantInfo := kernelCtorInfo% List.cons +def listRecInfo07 : ConstantInfo := kernelRecInfo% List.rec +def listNilRuleRhs07 : VExpr := kernelRecRuleRhs% List.rec 0 +def listConsRuleRhs07 : VExpr := kernelRecRuleRhs% List.rec 1 + +def optionInfo07 : ConstantInfo := kernelInductInfo% Option +def optionNoneInfo07 : ConstantInfo := kernelCtorInfo% Option.none +def optionSomeInfo07 : ConstantInfo := kernelCtorInfo% Option.some +def optionRecInfo07 : ConstantInfo := kernelRecInfo% Option.rec +def optionNoneRuleRhs07 : VExpr := kernelRecRuleRhs% Option.rec 0 +def optionSomeRuleRhs07 : VExpr := kernelRecRuleRhs% Option.rec 1 + +def prodInfo07 : ConstantInfo := kernelInductInfo% Prod +def prodMkInfo07 : ConstantInfo := kernelCtorInfo% Prod.mk +def prodRecInfo07 : ConstantInfo := kernelRecInfo% Prod.rec +def prodRuleRhs07 : VExpr := kernelRecRuleRhs% Prod.rec 0 + +def heqInfo07 : ConstantInfo := kernelInductInfo% HEq +def heqReflInfo07 : ConstantInfo := kernelCtorInfo% HEq.refl +def heqRecInfo07 : ConstantInfo := kernelRecInfo% HEq.rec +def heqRuleRhs07 : VExpr := kernelRecRuleRhs% HEq.rec 0 + +def finInfo07 : ConstantInfo := kernelInductInfo% Fin +def finMkInfo07 : ConstantInfo := kernelCtorInfo% Fin.mk +def finRecInfo07 : ConstantInfo := kernelRecInfo% Fin.rec +def finRuleRhs07 : VExpr := kernelRecRuleRhs% Fin.rec 0 + +def vectorInfo07 : ConstantInfo := kernelInductInfo% Vector +def vectorMkInfo07 : ConstantInfo := kernelCtorInfo% Vector.mk +def vectorRecInfo07 : ConstantInfo := kernelRecInfo% Vector.rec +def vectorRuleRhs07 : VExpr := kernelRecRuleRhs% Vector.rec 0 + +/-! ## Producer dependency contexts -/ + +def finDependencyMap07 : ConstMap := + ((((({} : ConstMap).insert ``Nat natInfo).insert ``LT + (kernelInductInfo% LT)).insert ``LT.mk + (kernelCtorInfo% LT.mk)).insert ``LT.lt + (.defnInfo (kernelDefVal% LT.lt))).insert ``instLTNat + (.defnInfo (kernelDefVal% instLTNat)) + +def vectorDependencyMap07 : ConstMap := + (((({} : ConstMap).insert ``Nat natInfo).insert ``Eq eqInfo).insert ``Array + (kernelInductInfo% Array)).insert ``Array.size + (.defnInfo (kernelDefVal% Array.size)) + +/-! ## Integrated row type -/ + +structure SingletonKernelRow where + artifact : SingletonParityArtifact + inductInfo : ConstantInfo + ctorInfos : List ConstantInfo + recInfo : ConstantInfo + familyType : VExpr + ctorTypes : List VExpr + recType : VExpr + ruleRhs : List VExpr + dependencies : ConstMap := {} + +namespace SingletonKernelRow + +def constructor? : ConstantInfo → Option Constructor + | .ctorInfo ctor => some { name := ctor.name, type := ctor.type } + | _ => none + +def kernelType? (row : SingletonKernelRow) : Option InductiveType := do + let .inductInfo induct := row.inductInfo | none + let ctors ← row.ctorInfos.mapM constructor? + some { name := induct.name, type := induct.type, ctors := ctors } + +def context (row : SingletonKernelRow) : AddInductive.Context where + env := Kernel.Environment.ofConstants + (.str `_singletonParity row.artifact.label.toString) row.dependencies + lparams := row.inductInfo.levelParams + safety := .safe + allowPrimitive := row.artifact.typeName == ``Nat || + row.artifact.typeName == ``Bool + +def producerAccepted (row : SingletonKernelRow) : Bool := + match row.kernelType? with + | none => false + | some kernelType => + match AddInductive.buildNormalizationCandidate + row.artifact.source.nparams [kernelType] 0 false row.context with + | .ok _ => true + | .error _ => false + +def ctorMatches (row : SingletonKernelRow) (info : ConstantInfo) + (raw : VConstVal) (rawType : VExpr) : Bool := + match info with + | .ctorInfo ctor => + ctor.name == raw.name && + ctor.levelParams.length == row.artifact.source.uvars && + ctor.induct == row.artifact.typeName && + ctor.numParams == row.artifact.source.nparams && + ctor.numFields == (VInductDecl.ctorFields + (VExpr.dropN row.artifact.source.nparams raw.type)).length && + !ctor.isUnsafe && rawType == raw.type + | _ => false + +def constructorsMatch (row : SingletonKernelRow) : + List ConstantInfo → List VConstVal → List VExpr → Bool + | [], [], [] => true + | info :: infos, raw :: raws, rawType :: rawTypes => + row.ctorMatches info raw rawType && + row.constructorsMatch infos raws rawTypes + | _, _, _ => false + +def recursorMatches (row : SingletonKernelRow) : Bool := + match row.recInfo with + | .recInfo rec => + rec.name == .str row.artifact.typeName "rec" && + rec.levelParams.length == row.artifact.generation.recUvars && + rec.all == [row.artifact.typeName] && + rec.numParams == row.artifact.source.nparams && + rec.numIndices == + row.artifact.generation.block.checked.indices.length && + rec.numMotives == 1 && + rec.numMinors == + row.artifact.generation.block.sourceType.ctors.length && + rec.k == row.artifact.generation.kTarget && + !rec.isUnsafe && + rec.rules.map (fun rule => (rule.ctor, rule.nfields)) == + row.artifact.generation.block.sourceType.ctors.map (fun ctor => + (ctor.name, (VInductDecl.ctorFields + (VExpr.dropN row.artifact.source.nparams ctor.type)).length)) && + row.recType == row.artifact.generation.recursor.type && + row.ruleRhs == + row.artifact.generation.generatedRules.map (·.rhs) + | _ => false + +/-- Exact fixed-row agreement after binder-name erasure. Universe order is +still observed: `familyType`/`ctorTypes`/`recType` were converted with the +stored metadata parameter lists, not with elaborator-inferred ordering. -/ +def agrees (row : SingletonKernelRow) : Bool := + match row.inductInfo with + | .inductInfo induct => + induct.name == row.artifact.typeName && + induct.levelParams.length == row.artifact.source.uvars && + induct.numParams == row.artifact.source.nparams && + induct.numIndices == + row.artifact.generation.block.checked.indices.length && + induct.all == [row.artifact.typeName] && + induct.ctors == row.artifact.constructorNames && + induct.numNested == 0 && !induct.isUnsafe && + row.familyType == row.artifact.generation.block.sourceType.type && + row.constructorsMatch row.ctorInfos + row.artifact.generation.block.sourceType.ctors row.ctorTypes && + row.recursorMatches + | _ => false + +end SingletonKernelRow + +/-! ## The fixed rows -/ + +def singletonKernelRows : List SingletonKernelRow := + [ { artifact := singletonPositiveArtifacts[0] + inductInfo := natInfo + ctorInfos := [natZeroInfo, natSuccInfo] + recInfo := natRecInfo + familyType := kernelConstVType% Nat + ctorTypes := [kernelConstVType% Nat.zero, kernelConstVType% Nat.succ] + recType := kernelConstVType% Nat.rec + ruleRhs := [natZeroKernelRuleRhs, natSuccKernelRuleRhs] }, + { artifact := singletonPositiveArtifacts[1] + inductInfo := boolInfo07 + ctorInfos := [boolFalseInfo07, boolTrueInfo07] + recInfo := boolRecInfo07 + familyType := kernelConstVType% Bool + ctorTypes := [kernelConstVType% Bool.false, kernelConstVType% Bool.true] + recType := kernelConstVType% Bool.rec + ruleRhs := [boolFalseRuleRhs07, boolTrueRuleRhs07] }, + { artifact := singletonPositiveArtifacts[2] + inductInfo := listInfo07 + ctorInfos := [listNilInfo07, listConsInfo07] + recInfo := listRecInfo07 + familyType := kernelConstVType% List + ctorTypes := [kernelConstVType% List.nil, kernelConstVType% List.cons] + recType := kernelConstVType% List.rec + ruleRhs := [listNilRuleRhs07, listConsRuleRhs07] }, + { artifact := singletonPositiveArtifacts[3] + inductInfo := optionInfo07 + ctorInfos := [optionNoneInfo07, optionSomeInfo07] + recInfo := optionRecInfo07 + familyType := kernelConstVType% Option + ctorTypes := [kernelConstVType% Option.none, + kernelConstVType% Option.some] + recType := kernelConstVType% Option.rec + ruleRhs := [optionNoneRuleRhs07, optionSomeRuleRhs07] }, + { artifact := singletonPositiveArtifacts[4] + inductInfo := prodInfo07 + ctorInfos := [prodMkInfo07] + recInfo := prodRecInfo07 + familyType := kernelConstVType% Prod + ctorTypes := [kernelConstVType% Prod.mk] + recType := kernelConstVType% Prod.rec + ruleRhs := [prodRuleRhs07] }, + { artifact := singletonPositiveArtifacts[5] + inductInfo := punitInfo06C + ctorInfos := [punitCtorInfo06C] + recInfo := punitRecInfo06C + familyType := kernelConstVType% PUnit + ctorTypes := [kernelConstVType% PUnit.unit] + recType := kernelConstVType% PUnit.rec + ruleRhs := [punitRuleRhs06C] }, + { artifact := singletonPositiveArtifacts[6] + inductInfo := emptyInfo06C + ctorInfos := [] + recInfo := emptyRecInfo06C + familyType := kernelConstVType% Empty + ctorTypes := [] + recType := kernelConstVType% Empty.rec + ruleRhs := [] }, + { artifact := singletonPositiveArtifacts[7] + inductInfo := orInfo06 + ctorInfos := [orInlInfo06, orInrInfo06] + recInfo := orRecInfo06 + familyType := kernelConstVType% Or + ctorTypes := [kernelConstVType% Or.inl, kernelConstVType% Or.inr] + recType := kernelConstVType% Or.rec + ruleRhs := [orInlKernelRuleRhs06, orInrKernelRuleRhs06] }, + { artifact := singletonPositiveArtifacts[8] + inductInfo := andInfo06 + ctorInfos := [andIntroInfo06] + recInfo := andRecInfo06 + familyType := kernelConstVType% And + ctorTypes := [kernelConstVType% And.intro] + recType := kernelConstVType% And.rec + ruleRhs := [andKernelRuleRhs06] }, + { artifact := singletonPositiveArtifacts[9] + inductInfo := eqInfo + ctorInfos := [eqReflInfo] + recInfo := eqRecInfo + familyType := kernelConstVType% Eq + ctorTypes := [kernelConstVType% Eq.refl] + recType := kernelConstVType% Eq.rec + ruleRhs := [eqReflKernelRuleRhs] }, + { artifact := singletonPositiveArtifacts[10] + inductInfo := heqInfo07 + ctorInfos := [heqReflInfo07] + recInfo := heqRecInfo07 + familyType := kernelConstVType% HEq + ctorTypes := [kernelConstVType% HEq.refl] + recType := kernelConstVType% HEq.rec + ruleRhs := [heqRuleRhs07] }, + { artifact := singletonPositiveArtifacts[11] + inductInfo := finInfo07 + ctorInfos := [finMkInfo07] + recInfo := finRecInfo07 + familyType := kernelConstVType% Fin + ctorTypes := [kernelConstVType% Fin.mk] + recType := kernelConstVType% Fin.rec + ruleRhs := [finRuleRhs07] + dependencies := finDependencyMap07 }, + { artifact := singletonPositiveArtifacts[12] + inductInfo := vectorInfo07 + ctorInfos := [vectorMkInfo07] + recInfo := vectorRecInfo07 + familyType := kernelConstVType% Vector + ctorTypes := [kernelConstVType% Vector.mk] + recType := kernelConstVType% Vector.rec + ruleRhs := [vectorRuleRhs07] + dependencies := vectorDependencyMap07 }, + { artifact := singletonPositiveArtifacts[13] + inductInfo := accInfo + ctorInfos := [accIntroInfo] + recInfo := accRecInfo + familyType := kernelConstVType% Acc + ctorTypes := [kernelConstVType% Acc.intro] + recType := kernelConstVType% Acc.rec + ruleRhs := [accKernelRuleRhs] } ] + +example : singletonKernelRows.map (·.artifact.label) = + singletonPositiveArtifacts.map (·.label) := rfl + +#guard singletonKernelRows.all (·.agrees) +#guard singletonKernelRows.all (·.producerAccepted) + +/-! ## Consolidated rejection matrix -/ + +/-- One named rejection whose Boolean is computed by the public Theory +analyzer, the ordinary metadata producer, or the environment transaction it +is intended to guard. -/ +structure SingletonNegativeRow where + label : Name + rejected : Bool + +def theoryDeclarationRejected07 (decl : VInductDecl) : Bool := + decl.checked?.isNone && (VEnv.empty.addInduct decl).isNone + +def producerRejected07 (nparams : Nat) (source : InductiveType) + (context : AddInductive.Context) : Bool := + match AddInductive.buildNormalizationCandidate nparams [source] 0 false + context with + | .error _ => true + | .ok _ => false + +def aliasFormerTruncatedViewType07 : VInductiveType := + { aliasFormerViewType with ctors := [] } + +def aliasFormerTruncatedViewDecl07 : VInductDecl := + { aliasFormerViewDecl with types := [aliasFormerTruncatedViewType07] } + +def listReorderedViewType07 : VInductiveType := + { listType with ctors := listType.ctors.reverse } + +def listReorderedViewDecl07 : VInductDecl := + { listDecl with types := [listReorderedViewType07] } + +def recursorKRejected07 (info : ConstantInfo) (expected : Bool) : Bool := + match info with + | .recInfo rec => rec.k != expected + | _ => false + +/-- A large recursor has one fresh universe parameter and a small recursor +has none. Supplying the wrong mode must therefore disagree with the actual +metadata even when the source-universe list itself is otherwise unchanged. -/ +def recursorEliminationRejected07 (info : ConstantInfo) (sourceUvars : Nat) : + VInductDecl.ElimMode → Bool + | VInductDecl.ElimMode.large => + info.levelParams.length != sourceUvars + 1 + | VInductDecl.ElimMode.small => + info.levelParams.length != sourceUvars + +def typeCollisionEnv07 : VEnv := + (VEnv.empty.addConst ``Nat ⟨0, .sort .zero⟩).get (by decide) + +/-- The complete Spec-07 negative matrix. Earlier phase-specific fixtures +retain their exact kernel error messages; this table makes their coverage and +combined acceptance result executable from one public artifact path. -/ +def singletonNegativeRows : List SingletonNegativeRow := + [ ⟨.mkSimple "loose-variables", + theoryDeclarationRejected07 looseIndexDecl⟩, + ⟨.mkSimple "duplicate-constructor-name", + theoryDeclarationRejected07 duplicateCtorDecl⟩, + ⟨.mkSimple "type-constructor-name-alias", + theoryDeclarationRejected07 typeCtorAliasDecl⟩, + ⟨.mkSimple "constructor-recursor-name-alias", + theoryDeclarationRejected07 ctorRecAliasDecl⟩, + ⟨.mkSimple "self-reference-before-family-staging", + theoryDeclarationRejected07 selfParamDecl⟩, + ⟨.mkSimple "bad-parameter-universe", + theoryDeclarationRejected07 badParamLevelDecl⟩, + ⟨.mkSimple "bad-constructor-universe", + theoryDeclarationRejected07 badCtorLevelDecl⟩, + ⟨.mkSimple "non-sort-family-result", + theoryDeclarationRejected07 nonSortResultDecl⟩, + ⟨.mkSimple "wrong-constructor-result-head", + theoryDeclarationRejected07 wrongCtorHeadDecl⟩, + ⟨.mkSimple "wrong-parameter-spine", + theoryDeclarationRejected07 wrongParamSpineDecl⟩, + ⟨.mkSimple "parameter-count-mismatch", + theoryDeclarationRejected07 shortParamDecl⟩, + ⟨.mkSimple "family-universe-count-mismatch", + theoryDeclarationRejected07 badTypeUvarsDecl⟩, + ⟨.mkSimple "constructor-universe-count-mismatch", + theoryDeclarationRejected07 badCtorUvarsDecl⟩, + ⟨.mkSimple "negative-recursive-pi-domain", + theoryDeclarationRejected07 recDomainDecl⟩, + ⟨.mkSimple "changed-recursive-target-parameter", + theoryDeclarationRejected07 recTargetDecl⟩, + ⟨.mkSimple "recursive-index-family-occurrence", + theoryDeclarationRejected07 recIndexDecl⟩, + ⟨.mkSimple "truncated-normalization-view", + (VInductDecl.normalizedGeneration? aliasFormerRawDecl + aliasFormerTruncatedViewDecl07).isNone⟩, + ⟨.mkSimple "reordered-normalization-view", + (VInductDecl.normalizedGeneration? listDecl + listReorderedViewDecl07).isNone⟩, + ⟨.mkSimple "opaque-normalization-view", + !matrixCandidateExact matrixOpaquePiAliasContext⟩, + ⟨.mkSimple "non-defeq-normalization-view", + producerRejected07 1 matrixNonDefEqType (matrixCandidateContext 10)⟩, + ⟨.mkSimple "nested-negativity", + (spec05CandidateError spec05NestedNegativeType).isSome⟩, + ⟨.mkSimple "family-in-nonrecursive-field", + (spec05CandidateError spec05FamilyNonrecursiveType).isSome⟩, + ⟨.mkSimple "family-in-proof-field", + (spec05CandidateError spec05FamilyProofType).isSome⟩, + ⟨.mkSimple "recursive-local-dependency", + (spec05CandidateError spec05RecursiveDependencyType).isSome && + spec05RecursiveDependencyPreFamilyError.isSome⟩, + ⟨.mkSimple "constructor-field-universe-boundary", + (spec05CandidateError spec05UniverseRejectType).isSome⟩, + ⟨.mkSimple "preexisting-type-name", + (typeCollisionEnv07.addInduct natDecl).isNone⟩, + ⟨.mkSimple "preexisting-constructor-name", + (ctorCollisionEnv.addInduct natDecl).isNone⟩, + ⟨.mkSimple "preexisting-recursor-name", + (recCollisionEnv.addInduct natDecl).isNone⟩, + ⟨.mkSimple "eq-wrong-k-target", recursorKRejected07 eqRecInfo false⟩, + ⟨.mkSimple "and-wrong-k-target", recursorKRejected07 andRecInfo06 true⟩, + ⟨.mkSimple "eq-wrong-small-elimination", + recursorEliminationRejected07 eqRecInfo eqDecl.uvars + VInductDecl.ElimMode.small⟩, + ⟨.mkSimple "or-wrong-large-elimination", + recursorEliminationRejected07 orRecInfo06 orDecl.uvars + VInductDecl.ElimMode.large⟩ ] + +example : singletonNegativeRows.length = 32 := rfl + +#guard singletonNegativeRows.all (·.rejected) + +/-! ## Exact trust manifests for the public matrix roots -/ + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.singletonKernelRows [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.singletonNegativeRows [ + propext, + Classical.choice, + Quot.sound, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Level.hasMVar_eq] + +end Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/SingletonParityReplay.lean b/Ix/Theory/Named/Verify/Environment/SingletonParityReplay.lean new file mode 100644 index 000000000..944cc6aab --- /dev/null +++ b/Ix/Theory/Named/Verify/Environment/SingletonParityReplay.lean @@ -0,0 +1,2820 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Verify.Environment.SingletonParityMatrix + +open Ix.Theory (VLevel) + +/-! +# Spec-07 environment replay inventory + +This module is the sole public environment-facing inventory for singleton +parity. A row packages the actual implementation map, the Theory input and +output environments, input alignment/order, and the proof-carrying inductive +transaction. Consequently every row exposes final alignment and orderedness; +mere Theory generation is not enough to inhabit this structure. +-/ + +namespace Ix.Theory.Named.InductiveReplayFixtures + +open Lean +open Ix.Theory.Named.InductiveFixtures + +local instance instInhabitedVEnvParityReplay : Inhabited VEnv := ⟨.empty⟩ + +structure SingletonReplayArtifact where + label : Name + source : VInductDecl + inputMap : ConstMap + inputEnv : VEnv + inputMapWF : inputMap.WF + outputMap : ConstMap + outputEnv : VEnv + inputOrdered : inputEnv.Ordered + transaction : AddInduct inputMap inputEnv source outputMap outputEnv + aligned : Aligned .safe outputMap outputEnv + +namespace SingletonReplayArtifact + +theorem outputAligned (artifact : SingletonReplayArtifact) : + Aligned .safe artifact.outputMap artifact.outputEnv := artifact.aligned + +theorem outputOrdered (artifact : SingletonReplayArtifact) : + artifact.outputEnv.Ordered := by + obtain ⟨generation, generation_wf, transaction⟩ := + artifact.transaction.to_addInduct + exact VEnv.addInductGeneration_WF artifact.inputOrdered generation_wf + transaction + +end SingletonReplayArtifact + +/-! ## Reusable staging facts for fixed-family replays -/ + +/-- The raw family constant retained by a generation certificate is a type in +the replay input environment. -/ +theorem replayRawFamilyWF07 + {source : VInductDecl} {generation : source.GenerationChecked} + {inputEnv : VEnv} (generationWF : generation.WF inputEnv) : + generation.block.sourceType.toVConstant.WF inputEnv := by + show inputEnv.IsType generation.block.sourceType.uvars [] + generation.block.sourceType.type + rw [generation.block.sourceType_uvars_eq] + exact generationWF.rawFamily_isType + +/-- The raw family insertion preserves ordering whenever the retained +generation certificate is well formed in the replay input environment. -/ +theorem replayTypeEnv_ordered07 + {source : VInductDecl} {generation : source.GenerationChecked} + {inputEnv typeEnv : VEnv} + (inputOrdered : inputEnv.Ordered) + (generationWF : generation.WF inputEnv) + (addType : inputEnv.addConst generation.block.sourceType.name + generation.block.sourceType.toVConstant = some typeEnv) : + typeEnv.Ordered := by + refine .const inputOrdered ?_ addType + show inputEnv.IsType generation.block.sourceType.uvars [] + generation.block.sourceType.type + rw [generation.block.sourceType_uvars_eq] + exact generationWF.rawFamily_isType + +/-- Each stored raw constructor is well formed immediately after the raw +family insertion. Later constructor stages use monotonicity to transport +this certificate across earlier constructor insertions. -/ +theorem replayRawCtorWF07 + {source : VInductDecl} {generation : source.GenerationChecked} + {inputEnv typeEnv : VEnv} + (generationWF : generation.WF inputEnv) + (addType : inputEnv.addConst generation.block.sourceType.name + generation.block.sourceType.toVConstant = some typeEnv) + (raw : VConstVal) (hraw : raw ∈ generation.block.sourceType.ctors) : + raw.toVConstant.WF typeEnv := by + have hraw' : raw ∈ generation.block.ctorPairs.map (·.raw) := by + rw [generation.rawCtors_eq] + exact hraw + obtain ⟨ctor, hctor, rfl⟩ := List.mem_map.1 hraw' + show typeEnv.IsType ctor.raw.uvars [] ctor.raw.type + rw [generation.ctor_uvars_eq hctor] + exact generationWF.rawCtor_isType addType hctor + +/-- The complete raw-constructor fold preserves ordering. This includes the +zero-constructor case, where the fold is the identity. -/ +theorem replayCtorEnv_ordered07 + {source : VInductDecl} {generation : source.GenerationChecked} + {inputEnv typeEnv ctorEnv : VEnv} + (generationWF : generation.WF inputEnv) + (addType : inputEnv.addConst generation.block.sourceType.name + generation.block.sourceType.toVConstant = some typeEnv) + (typeOrdered : typeEnv.Ordered) + (addCtors : List.foldlM + (fun env (ctor : VConstVal) => + env.addConst ctor.name ctor.toVConstant) + typeEnv generation.block.sourceType.ctors = some ctorEnv) : + ctorEnv.Ordered := by + have ctorWF : ∀ ctor ∈ generation.block.sourceType.ctors, + ctor.toVConstant.WF typeEnv := by + intro raw hraw + exact replayRawCtorWF07 generationWF addType raw hraw + exact VInductDecl.constFold_ordered + generation.block.sourceType.ctors typeOrdered ctorWF addCtors + +/-- Reconstruct the precise mixed-generation environment after the family and +constructor constants have been inserted. Its recursor certificate is what +turns exact kernel recursor metadata into a `TrConstVal`. -/ +theorem replayGenerationEnv07 + {source : VInductDecl} {generation : source.GenerationChecked} + {inputEnv typeEnv ctorEnv : VEnv} + (generationWF : generation.WF inputEnv) + (addType : inputEnv.addConst generation.block.sourceType.name + generation.block.sourceType.toVConstant = some typeEnv) + (addCtors : List.foldlM + (fun env (ctor : VConstVal) => + env.addConst ctor.name ctor.toVConstant) + typeEnv generation.block.sourceType.ctors = some ctorEnv) + (ctorOrdered : ctorEnv.Ordered) : + VInductDecl.GenerationEnv generation ctorEnv := by + obtain ⟨typeToCtor, ctorLookup, -⟩ := + VInductDecl.ctorFold_spec generation.block.sourceType.ctors addCtors + have inputToType := VEnv.addConst_le addType + have inputToCtor := inputToType.trans typeToCtor + have familyLookup : ctorEnv.constants + generation.block.sourceType.name = + some generation.block.sourceType.toVConstant := + typeToCtor.constants (VEnv.addConst_self addType) + have constructorsLookup : ∀ ctor ∈ generation.block.ctorPairs, + ctorEnv.constants ctor.raw.name = some ctor.raw.toVConstant := by + intro ctor hctor + apply ctorLookup ctor.raw + rw [← generation.rawCtors_eq] + exact List.mem_map.2 ⟨ctor, hctor, rfl⟩ + exact generationWF.toGenerationEnv addType inputToCtor typeToCtor + ctorOrdered familyLookup constructorsLookup + +/-! ## Replays already established by the completed singleton pipeline -/ + +def natReplay07 : SingletonReplayArtifact where + label := ``Nat + source := natDecl + inputMap := {} + inputEnv := .empty + inputMapWF := SMap.WF.empty + outputMap := natMap + outputEnv := natFinalEnv + inputOrdered := .empty + transaction := nat_addInduct + aligned := nat_aligned + +def eqReplay07 : SingletonReplayArtifact where + label := ``Eq + source := eqDecl + inputMap := {} + inputEnv := .empty + inputMapWF := SMap.WF.empty + outputMap := eqMap + outputEnv := eqFinalEnv + inputOrdered := .empty + transaction := eq_addInduct + aligned := eq_aligned + +def accReplay07 : SingletonReplayArtifact where + label := ``Acc + source := accDecl + inputMap := {} + inputEnv := .empty + inputMapWF := SMap.WF.empty + outputMap := accMap + outputEnv := accFinalEnv + inputOrdered := .empty + transaction := acc_addInduct + aligned := acc_aligned + +def aliasFormerReplay07 : SingletonReplayArtifact where + label := ``AliasFormer + source := aliasFormerRawDecl + inputMap := typeFamilyAliasMap + inputEnv := typeFamilyAliasEnv + inputMapWF := typeFamilyAliasMap_wf + outputMap := aliasFormerMap + outputEnv := aliasFormerFinalEnv + inputOrdered := typeFamilyAliasEnv_ordered + transaction := aliasFormer_addInduct_checked + aligned := aliasFormer_aligned_checked + +def aliasRecReplay07 : SingletonReplayArtifact where + label := ``AliasRec + source := aliasRecRawDecl + inputMap := recAliasMap + inputEnv := recAliasEnv + inputMapWF := recAliasMap_wf + outputMap := aliasRecMap + outputEnv := aliasRecFinalEnv + inputOrdered := recAliasEnv_ordered + transaction := aliasRec_addInduct_checked + aligned := aliasRec_aligned_checked + +def normalizationMatrixReplay07 : SingletonReplayArtifact where + label := ``NormalizationMatrix + source := normalizationMatrixRawDecl + inputMap := matrixAliasMap + inputEnv := normalizationMatrixAliasEnv + inputMapWF := matrixAliasMap_wf + outputMap := normalizationMatrixMap + outputEnv := normalizationMatrixFinalEnv + inputOrdered := normalizationMatrixAliasEnv_ordered + transaction := normalizationMatrix_addInduct + aligned := normalizationMatrix_aligned + +def annotatedPiReplay07 : SingletonReplayArtifact where + label := ``AnnotatedPi + source := annotatedPiRawDecl + inputMap := _ + inputEnv := outParamEnv + inputMapWF := annotatedReplayInputMap_wf + outputMap := _ + outputEnv := annotatedPiFinalEnv + inputOrdered := outParamEnv_ordered + transaction := annotatedPi_addInduct_checked + aligned := annotatedPi_aligned_checked + +def annotatedParamReplay07 : SingletonReplayArtifact where + label := ``AnnotatedParam + source := annotatedParamRawDecl + inputMap := _ + inputEnv := outParamEnv + inputMapWF := annotatedReplayInputMap_wf + outputMap := _ + outputEnv := annotatedParamFinalEnv + inputOrdered := outParamEnv_ordered + transaction := annotatedParam_addInduct_checked + aligned := annotatedParam_aligned_checked + +/-! ## Fixed-family replays -/ + +/-! ### Bool -/ + +theorem boolDeclWF07 : boolDecl.WF VEnv.empty := by + refine ⟨rfl, ?_⟩ + intro type htype + have htype' : type = boolType := + List.mem_singleton.1 (by simpa [boolDecl] using htype) + subst type + refine ⟨?_, ?_⟩ + · change True + trivial + · intro ctor hctor + rcases List.mem_cons.1 hctor with rfl | hctor + · constructor + · change True + trivial + · exact .nil + · have hctor' := List.mem_singleton.1 hctor + subst ctor + constructor + · change True + trivial + · exact .nil + +theorem boolGenerationWF07 : boolGenerationChecked.WF VEnv.empty := by + exact (boolChecked.wf_of_decl boolDeclWF07).identityGeneration .empty + +def boolTypeEnv07 : VEnv := + (VEnv.empty.addConst boolType.name boolType.toVConstant).get! + +def boolFalseEnv07 : VEnv := + (boolTypeEnv07.addConst boolType.ctors[0].name + boolType.ctors[0].toVConstant).get! + +def boolCtorEnv07 : VEnv := + (boolFalseEnv07.addConst boolType.ctors[1].name + boolType.ctors[1].toVConstant).get! + +def boolRecEnv07 : VEnv := + (boolCtorEnv07.addConst ``Bool.rec + (inductGenerationRecVal boolGenerationChecked).toVConstant).get! + +def boolFinalEnv07 : VEnv := + boolGenerationChecked.generatedRules.foldl VEnv.addDefEq boolRecEnv07 + +def boolTypeMap07 : ConstMap := + ({} : ConstMap).insert ``Bool boolInfo07 + +def boolFalseMap07 : ConstMap := + boolTypeMap07.insert ``Bool.false boolFalseInfo07 + +def boolCtorMap07 : ConstMap := + boolFalseMap07.insert ``Bool.true boolTrueInfo07 + +def boolMap07 : ConstMap := + boolCtorMap07.insert ``Bool.rec boolRecInfo07 + +theorem boolTypeEnv_ordered07 : boolTypeEnv07.Ordered := + replayTypeEnv_ordered07 .empty boolGenerationWF07 rfl + +theorem boolCtorEnv_ordered07 : boolCtorEnv07.Ordered := + replayCtorEnv_ordered07 boolGenerationWF07 rfl + boolTypeEnv_ordered07 rfl + +theorem boolGenerationEnv07 : + VInductDecl.GenerationEnv boolGenerationChecked boolCtorEnv07 := + replayGenerationEnv07 boolGenerationWF07 rfl rfl + boolCtorEnv_ordered07 + +theorem boolInfoTr07 : + TrConstVal .safe VEnv.empty boolInfo07 boolType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + exact .sort rfl + +theorem boolFalseInfoTr07 : + TrConstVal .safe boolTypeEnv07 boolFalseInfo07 boolType.ctors[0] := by + have hBool : boolTypeEnv07.constants ``Bool = + some boolType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr boolTypeEnv07 boolFalseInfo07.levelParams [] + boolFalseInfo07.type boolType.ctors[0].type := by + tr_type_expr_tac + exact shape.to_trExprS boolTypeEnv_ordered07 trivial + ⟨.sort (.succ .zero), by type_tac⟩ + +theorem boolTrueInfoTr07 : + TrConstVal .safe boolFalseEnv07 boolTrueInfo07 boolType.ctors[1] := by + have hBool : boolFalseEnv07.constants ``Bool = + some boolType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr boolFalseEnv07 boolTrueInfo07.levelParams [] + boolTrueInfo07.type boolType.ctors[1].type := by + tr_type_expr_tac + have falseOrdered : boolFalseEnv07.Ordered := by + refine .const (n := boolType.ctors[0].name) + (ci := boolType.ctors[0].toVConstant) boolTypeEnv_ordered07 ?_ rfl + exact ⟨.succ .zero, by type_tac⟩ + exact shape.to_trExprS falseOrdered trivial + ⟨.sort (.succ .zero), by type_tac⟩ + +theorem boolRecInfoTr07 : + TrConstVal .safe boolCtorEnv07 boolRecInfo07 + (inductGenerationRecVal boolGenerationChecked) := by + have hBool : boolCtorEnv07.constants ``Bool = + some boolType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr boolCtorEnv07 boolRecInfo07.levelParams [] + boolRecInfo07.type + (inductGenerationRecVal boolGenerationChecked).type := by + tr_type_expr_tac + obtain ⟨sort, recursorType⟩ := boolGenerationEnv07.recursor_wf + exact shape.to_trExprS boolCtorEnv_ordered07 trivial + ⟨.sort sort, recursorType⟩ + +theorem boolTypeFresh07 : ({} : ConstMap).find? ``Bool = none := by + simp [SMap.find?] + +theorem boolTypeMapWF07 : boolTypeMap07.WF := + SMap.WF.empty.insert _ _ boolTypeFresh07 + +theorem boolFalseFresh07 : boolTypeMap07.find? ``Bool.false = none := by + rw [boolTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem boolFalseMapWF07 : boolFalseMap07.WF := + boolTypeMapWF07.insert _ _ boolFalseFresh07 + +theorem boolTrueFresh07 : boolFalseMap07.find? ``Bool.true = none := by + rw [boolFalseMap07, boolTypeMapWF07.find?_insert, boolTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem boolCtorMapWF07 : boolCtorMap07.WF := + boolFalseMapWF07.insert _ _ boolTrueFresh07 + +theorem boolRecFresh07 : boolCtorMap07.find? ``Bool.rec = none := by + rw [boolCtorMap07, boolFalseMapWF07.find?_insert, boolFalseMap07, + boolTypeMapWF07.find?_insert, boolTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem boolAddInduct07 : AddInduct ({} : ConstMap) VEnv.empty boolDecl + boolMap07 boolFinalEnv07 := by + refine ⟨{ + generation := boolGenerationChecked + generation_wf := boolGenerationWF07 + typeMap := boolTypeMap07 + typeEnv := boolTypeEnv07 + ctorMap := boolCtorMap07 + ctorEnv := boolCtorEnv07 + recEnv := boolRecEnv07 + addType := { + info := boolInfo07 + kind_eq := by simp [boolInfo07, InductConstantKind.Matches] + tr := boolInfoTr07 + map_fresh := boolTypeFresh07 + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := boolRecInfo07 + kind_eq := by simp [boolRecInfo07, InductConstantKind.Matches] + tr := boolRecInfoTr07 + map_fresh := boolRecFresh07 + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ }⟩ + exact .cons { + info := boolFalseInfo07 + kind_eq := by simp [boolFalseInfo07, InductConstantKind.Matches] + tr := boolFalseInfoTr07 + map_fresh := by simpa [boolType] using boolFalseFresh07 + env_add := rfl + map_add := rfl } + (.cons { + info := boolTrueInfo07 + kind_eq := by simp [boolTrueInfo07, InductConstantKind.Matches] + tr := boolTrueInfoTr07 + map_fresh := by + change boolFalseMap07.find? ``Bool.true = none + exact boolTrueFresh07 + env_add := rfl + map_add := rfl } .nil) + +theorem boolAligned07 : Aligned .safe boolMap07 boolFinalEnv07 := + Aligned.addInduct boolAddInduct07 .empty + +def boolReplay07 : SingletonReplayArtifact where + label := ``Bool + source := boolDecl + inputMap := {} + inputEnv := .empty + inputMapWF := SMap.WF.empty + outputMap := boolMap07 + outputEnv := boolFinalEnv07 + inputOrdered := .empty + transaction := boolAddInduct07 + aligned := boolAligned07 + +/-! ### List -/ + +theorem listCheckedWF07 : listChecked.WF VEnv.empty := by + constructor + · change VEnv.empty.OnTel 1 [] [.sort (.succ (.param 0))] + exact ⟨⟨.succ (.succ (.param 0)), VEnv.HasType.sort (by decide)⟩, + trivial⟩ + · intro ctor hctor + rcases List.mem_cons.1 hctor with rfl | hctor + · constructor + · change True + trivial + · exact .nil + · have hctor' := List.mem_singleton.1 hctor + subst ctor + constructor + · change VInductDecl.fieldsWF 1 ``List 1 VEnv.empty + (.succ (.param 0)) [] [.sort (.succ (.param 0))] 0 + [.bvar 0, + .app (.const ``List [.param 0]) (.bvar 1)] + constructor + · exact .inr (.inr ⟨rfl, .succ (.param 0), by type_tac, + .inr (VLevel.le_refl _)⟩) + constructor + · intro recursive + contradiction + constructor + · exact .inl rfl + constructor + · intro _ + exact .nil + · trivial + · exact .nil + +theorem listGenerationWF07 : listGenerationChecked.WF VEnv.empty := by + exact listCheckedWF07.identityGeneration .empty + +def listTypeEnv07 : VEnv := + (VEnv.empty.addConst listType.name listType.toVConstant).get! + +def listNilEnv07 : VEnv := + (listTypeEnv07.addConst listType.ctors[0].name + listType.ctors[0].toVConstant).get! + +def listCtorEnv07 : VEnv := + (listNilEnv07.addConst listType.ctors[1].name + listType.ctors[1].toVConstant).get! + +def listRecEnv07 : VEnv := + (listCtorEnv07.addConst ``List.rec + (inductGenerationRecVal listGenerationChecked).toVConstant).get! + +def listFinalEnv07 : VEnv := + listGenerationChecked.generatedRules.foldl VEnv.addDefEq listRecEnv07 + +def listTypeMap07 : ConstMap := + ({} : ConstMap).insert ``List listInfo07 + +def listNilMap07 : ConstMap := + listTypeMap07.insert ``List.nil listNilInfo07 + +def listCtorMap07 : ConstMap := + listNilMap07.insert ``List.cons listConsInfo07 + +def listMap07 : ConstMap := + listCtorMap07.insert ``List.rec listRecInfo07 + +theorem listTypeEnv_ordered07 : listTypeEnv07.Ordered := + replayTypeEnv_ordered07 .empty listGenerationWF07 rfl + +theorem listNilEnv_ordered07 : listNilEnv07.Ordered := by + refine .const (n := listType.ctors[0].name) + (ci := listType.ctors[0].toVConstant) listTypeEnv_ordered07 ?_ rfl + exact replayRawCtorWF07 listGenerationWF07 rfl listType.ctors[0] + (.head _) + +theorem listCtorEnv_ordered07 : listCtorEnv07.Ordered := + replayCtorEnv_ordered07 listGenerationWF07 rfl + listTypeEnv_ordered07 rfl + +theorem listGenerationEnv07 : + VInductDecl.GenerationEnv listGenerationChecked listCtorEnv07 := + replayGenerationEnv07 listGenerationWF07 rfl rfl + listCtorEnv_ordered07 + +theorem listInfoTr07 : + TrConstVal .safe VEnv.empty listInfo07 listType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr VEnv.empty listInfo07.levelParams [] + listInfo07.type listType.type := by + tr_type_expr_tac + obtain ⟨sort, familyType⟩ := replayRawFamilyWF07 listGenerationWF07 + exact shape.to_trExprS .empty trivial ⟨.sort sort, familyType⟩ + +theorem listNilInfoTr07 : + TrConstVal .safe listTypeEnv07 listNilInfo07 listType.ctors[0] := by + have hList : listTypeEnv07.constants ``List = + some listType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr listTypeEnv07 listNilInfo07.levelParams [] + listNilInfo07.type listType.ctors[0].type := by + tr_type_expr_tac + obtain ⟨sort, ctorType⟩ := replayRawCtorWF07 listGenerationWF07 rfl + listType.ctors[0] (.head _) + exact shape.to_trExprS listTypeEnv_ordered07 trivial + ⟨.sort sort, ctorType⟩ + +theorem listConsInfoTr07 : + TrConstVal .safe listNilEnv07 listConsInfo07 listType.ctors[1] := by + have hList : listNilEnv07.constants ``List = + some listType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr listNilEnv07 listConsInfo07.levelParams [] + listConsInfo07.type listType.ctors[1].type := by + tr_type_expr_tac + obtain ⟨sort, ctorType⟩ := replayRawCtorWF07 listGenerationWF07 rfl + listType.ctors[1] (.tail _ (.head _)) + have typeToNil : listTypeEnv07 ≤ listNilEnv07 := + VEnv.addConst_le (show listTypeEnv07.addConst + listType.ctors[0].name listType.ctors[0].toVConstant = + some listNilEnv07 from rfl) + exact shape.to_trExprS listNilEnv_ordered07 trivial + ⟨.sort sort, ctorType.mono typeToNil⟩ + +theorem listRecInfoTr07 : + TrConstVal .safe listCtorEnv07 listRecInfo07 + (inductGenerationRecVal listGenerationChecked) := by + have hList : listCtorEnv07.constants ``List = + some listType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr listCtorEnv07 listRecInfo07.levelParams [] + listRecInfo07.type + (inductGenerationRecVal listGenerationChecked).type := by + tr_type_expr_tac + obtain ⟨sort, recursorType⟩ := listGenerationEnv07.recursor_wf + exact shape.to_trExprS listCtorEnv_ordered07 trivial + ⟨.sort sort, recursorType⟩ + +theorem listTypeFresh07 : ({} : ConstMap).find? ``List = none := by + simp [SMap.find?] + +theorem listTypeMapWF07 : listTypeMap07.WF := + SMap.WF.empty.insert _ _ listTypeFresh07 + +theorem listNilFresh07 : listTypeMap07.find? ``List.nil = none := by + rw [listTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem listNilMapWF07 : listNilMap07.WF := + listTypeMapWF07.insert _ _ listNilFresh07 + +theorem listConsFresh07 : listNilMap07.find? ``List.cons = none := by + rw [listNilMap07, listTypeMapWF07.find?_insert, listTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem listCtorMapWF07 : listCtorMap07.WF := + listNilMapWF07.insert _ _ listConsFresh07 + +theorem listRecFresh07 : listCtorMap07.find? ``List.rec = none := by + rw [listCtorMap07, listNilMapWF07.find?_insert, listNilMap07, + listTypeMapWF07.find?_insert, listTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem listAddInduct07 : AddInduct ({} : ConstMap) VEnv.empty listDecl + listMap07 listFinalEnv07 := by + refine ⟨{ + generation := listGenerationChecked + generation_wf := listGenerationWF07 + typeMap := listTypeMap07 + typeEnv := listTypeEnv07 + ctorMap := listCtorMap07 + ctorEnv := listCtorEnv07 + recEnv := listRecEnv07 + addType := { + info := listInfo07 + kind_eq := by simp [listInfo07, InductConstantKind.Matches] + tr := listInfoTr07 + map_fresh := listTypeFresh07 + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := listRecInfo07 + kind_eq := by simp [listRecInfo07, InductConstantKind.Matches] + tr := listRecInfoTr07 + map_fresh := listRecFresh07 + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ }⟩ + exact .cons { + info := listNilInfo07 + kind_eq := by simp [listNilInfo07, InductConstantKind.Matches] + tr := listNilInfoTr07 + map_fresh := by simpa [listType] using listNilFresh07 + env_add := rfl + map_add := rfl } + (.cons { + info := listConsInfo07 + kind_eq := by simp [listConsInfo07, InductConstantKind.Matches] + tr := listConsInfoTr07 + map_fresh := by + change listNilMap07.find? ``List.cons = none + exact listConsFresh07 + env_add := rfl + map_add := rfl } .nil) + +theorem listAligned07 : Aligned .safe listMap07 listFinalEnv07 := + Aligned.addInduct listAddInduct07 .empty + +def listReplay07 : SingletonReplayArtifact where + label := ``List + source := listDecl + inputMap := {} + inputEnv := .empty + inputMapWF := SMap.WF.empty + outputMap := listMap07 + outputEnv := listFinalEnv07 + inputOrdered := .empty + transaction := listAddInduct07 + aligned := listAligned07 + +/-! ### Option -/ + +theorem optionDeclWF07 : optionDecl.WF VEnv.empty := by + refine ⟨rfl, ?_⟩ + intro type htype + have htype' : type = optionType := + List.mem_singleton.1 (by simpa [optionDecl] using htype) + subst type + refine ⟨?_, ?_⟩ + · change VEnv.empty.OnTel 1 [] [.sort (.succ (.param 0))] + exact ⟨⟨.succ (.succ (.param 0)), VEnv.HasType.sort (by decide)⟩, + trivial⟩ + · intro ctor hctor + rcases List.mem_cons.1 hctor with rfl | hctor + · constructor + · change True + trivial + · exact .nil + · have hctor' := List.mem_singleton.1 hctor + subst ctor + constructor + · change VInductDecl.fieldsWF 1 ``Option 1 VEnv.empty + (.succ (.param 0)) [] [.sort (.succ (.param 0))] 0 [.bvar 0] + constructor + · exact .inr (.inr ⟨rfl, .succ (.param 0), by type_tac, + .inr (VLevel.le_refl _)⟩) + constructor + · intro recursive + contradiction + · trivial + · exact .nil + +theorem optionGenerationWF07 : optionGenerationChecked.WF VEnv.empty := by + exact (optionChecked.wf_of_decl optionDeclWF07).identityGeneration .empty + +def optionTypeEnv07 : VEnv := + (VEnv.empty.addConst optionType.name optionType.toVConstant).get! + +def optionNoneEnv07 : VEnv := + (optionTypeEnv07.addConst optionType.ctors[0].name + optionType.ctors[0].toVConstant).get! + +def optionCtorEnv07 : VEnv := + (optionNoneEnv07.addConst optionType.ctors[1].name + optionType.ctors[1].toVConstant).get! + +def optionRecEnv07 : VEnv := + (optionCtorEnv07.addConst ``Option.rec + (inductGenerationRecVal optionGenerationChecked).toVConstant).get! + +def optionFinalEnv07 : VEnv := + optionGenerationChecked.generatedRules.foldl VEnv.addDefEq optionRecEnv07 + +def optionTypeMap07 : ConstMap := + ({} : ConstMap).insert ``Option optionInfo07 + +def optionNoneMap07 : ConstMap := + optionTypeMap07.insert ``Option.none optionNoneInfo07 + +def optionCtorMap07 : ConstMap := + optionNoneMap07.insert ``Option.some optionSomeInfo07 + +def optionMap07 : ConstMap := + optionCtorMap07.insert ``Option.rec optionRecInfo07 + +theorem optionTypeEnv_ordered07 : optionTypeEnv07.Ordered := + replayTypeEnv_ordered07 .empty optionGenerationWF07 rfl + +theorem optionNoneEnv_ordered07 : optionNoneEnv07.Ordered := by + refine .const (n := optionType.ctors[0].name) + (ci := optionType.ctors[0].toVConstant) optionTypeEnv_ordered07 ?_ rfl + exact replayRawCtorWF07 optionGenerationWF07 rfl optionType.ctors[0] + (.head _) + +theorem optionCtorEnv_ordered07 : optionCtorEnv07.Ordered := + replayCtorEnv_ordered07 optionGenerationWF07 rfl + optionTypeEnv_ordered07 rfl + +theorem optionGenerationEnv07 : + VInductDecl.GenerationEnv optionGenerationChecked optionCtorEnv07 := + replayGenerationEnv07 optionGenerationWF07 rfl rfl + optionCtorEnv_ordered07 + +theorem optionInfoTr07 : + TrConstVal .safe VEnv.empty optionInfo07 optionType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr VEnv.empty optionInfo07.levelParams [] + optionInfo07.type optionType.type := by + tr_type_expr_tac + obtain ⟨sort, familyType⟩ := replayRawFamilyWF07 optionGenerationWF07 + exact shape.to_trExprS .empty trivial ⟨.sort sort, familyType⟩ + +theorem optionNoneInfoTr07 : + TrConstVal .safe optionTypeEnv07 optionNoneInfo07 optionType.ctors[0] := by + have hOption : optionTypeEnv07.constants ``Option = + some optionType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr optionTypeEnv07 optionNoneInfo07.levelParams [] + optionNoneInfo07.type optionType.ctors[0].type := by + tr_type_expr_tac + obtain ⟨sort, ctorType⟩ := replayRawCtorWF07 optionGenerationWF07 rfl + optionType.ctors[0] (.head _) + exact shape.to_trExprS optionTypeEnv_ordered07 trivial + ⟨.sort sort, ctorType⟩ + +theorem optionSomeInfoTr07 : + TrConstVal .safe optionNoneEnv07 optionSomeInfo07 optionType.ctors[1] := by + have hOption : optionNoneEnv07.constants ``Option = + some optionType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr optionNoneEnv07 optionSomeInfo07.levelParams [] + optionSomeInfo07.type optionType.ctors[1].type := by + tr_type_expr_tac + obtain ⟨sort, ctorType⟩ := replayRawCtorWF07 optionGenerationWF07 rfl + optionType.ctors[1] (.tail _ (.head _)) + have typeToNone : optionTypeEnv07 ≤ optionNoneEnv07 := + VEnv.addConst_le (show optionTypeEnv07.addConst + optionType.ctors[0].name optionType.ctors[0].toVConstant = + some optionNoneEnv07 from rfl) + exact shape.to_trExprS optionNoneEnv_ordered07 trivial + ⟨.sort sort, ctorType.mono typeToNone⟩ + +theorem optionRecInfoTr07 : + TrConstVal .safe optionCtorEnv07 optionRecInfo07 + (inductGenerationRecVal optionGenerationChecked) := by + have hOption : optionCtorEnv07.constants ``Option = + some optionType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr optionCtorEnv07 optionRecInfo07.levelParams [] + optionRecInfo07.type + (inductGenerationRecVal optionGenerationChecked).type := by + tr_type_expr_tac + obtain ⟨sort, recursorType⟩ := optionGenerationEnv07.recursor_wf + exact shape.to_trExprS optionCtorEnv_ordered07 trivial + ⟨.sort sort, recursorType⟩ + +theorem optionTypeFresh07 : ({} : ConstMap).find? ``Option = none := by + simp [SMap.find?] + +theorem optionTypeMapWF07 : optionTypeMap07.WF := + SMap.WF.empty.insert _ _ optionTypeFresh07 + +theorem optionNoneFresh07 : optionTypeMap07.find? ``Option.none = none := by + rw [optionTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem optionNoneMapWF07 : optionNoneMap07.WF := + optionTypeMapWF07.insert _ _ optionNoneFresh07 + +theorem optionSomeFresh07 : optionNoneMap07.find? ``Option.some = none := by + rw [optionNoneMap07, optionTypeMapWF07.find?_insert, optionTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem optionCtorMapWF07 : optionCtorMap07.WF := + optionNoneMapWF07.insert _ _ optionSomeFresh07 + +theorem optionRecFresh07 : optionCtorMap07.find? ``Option.rec = none := by + rw [optionCtorMap07, optionNoneMapWF07.find?_insert, optionNoneMap07, + optionTypeMapWF07.find?_insert, optionTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem optionAddInduct07 : AddInduct ({} : ConstMap) VEnv.empty optionDecl + optionMap07 optionFinalEnv07 := by + refine ⟨{ + generation := optionGenerationChecked + generation_wf := optionGenerationWF07 + typeMap := optionTypeMap07 + typeEnv := optionTypeEnv07 + ctorMap := optionCtorMap07 + ctorEnv := optionCtorEnv07 + recEnv := optionRecEnv07 + addType := { + info := optionInfo07 + kind_eq := by simp [optionInfo07, InductConstantKind.Matches] + tr := optionInfoTr07 + map_fresh := optionTypeFresh07 + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := optionRecInfo07 + kind_eq := by simp [optionRecInfo07, InductConstantKind.Matches] + tr := optionRecInfoTr07 + map_fresh := optionRecFresh07 + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ }⟩ + exact .cons { + info := optionNoneInfo07 + kind_eq := by simp [optionNoneInfo07, InductConstantKind.Matches] + tr := optionNoneInfoTr07 + map_fresh := by simpa [optionType] using optionNoneFresh07 + env_add := rfl + map_add := rfl } + (.cons { + info := optionSomeInfo07 + kind_eq := by simp [optionSomeInfo07, InductConstantKind.Matches] + tr := optionSomeInfoTr07 + map_fresh := by + change optionNoneMap07.find? ``Option.some = none + exact optionSomeFresh07 + env_add := rfl + map_add := rfl } .nil) + +theorem optionAligned07 : Aligned .safe optionMap07 optionFinalEnv07 := + Aligned.addInduct optionAddInduct07 .empty + +def optionReplay07 : SingletonReplayArtifact where + label := ``Option + source := optionDecl + inputMap := {} + inputEnv := .empty + inputMapWF := SMap.WF.empty + outputMap := optionMap07 + outputEnv := optionFinalEnv07 + inputOrdered := .empty + transaction := optionAddInduct07 + aligned := optionAligned07 + +/-! ### Prod -/ + +theorem prodCheckedWF07 : prodChecked.WF VEnv.empty := by + constructor + · change VEnv.empty.OnTel 2 [] + [.sort (.succ (.param 0)), .sort (.succ (.param 1))] + exact ⟨⟨.succ (.succ (.param 0)), VEnv.HasType.sort (by decide)⟩, + ⟨⟨.succ (.succ (.param 1)), VEnv.HasType.sort (by decide)⟩, + trivial⟩⟩ + · intro ctor hctor + have hctor' := List.mem_singleton.1 hctor + subst ctor + constructor + · change VInductDecl.fieldsWF 2 ``Prod 2 VEnv.empty + (.max (.succ (.param 0)) (.succ (.param 1))) [] + [.sort (.succ (.param 1)), .sort (.succ (.param 0))] 0 + [.bvar 1, .bvar 1] + constructor + · exact .inr (.inr ⟨rfl, .succ (.param 0), by type_tac, + .inr VLevel.le_max_left⟩) + constructor + · intro recursive + contradiction + constructor + · exact .inr (.inr ⟨rfl, .succ (.param 1), by type_tac, + .inr VLevel.le_max_right⟩) + constructor + · intro recursive + contradiction + · trivial + · exact .nil + +theorem prodGenerationWF07 : prodGenerationChecked.WF VEnv.empty := by + exact prodCheckedWF07.identityGeneration .empty + +def prodTypeEnv07 : VEnv := + (VEnv.empty.addConst prodType.name prodType.toVConstant).get! + +def prodCtorEnv07 : VEnv := + (prodTypeEnv07.addConst prodType.ctors[0].name + prodType.ctors[0].toVConstant).get! + +def prodRecEnv07 : VEnv := + (prodCtorEnv07.addConst ``Prod.rec + (inductGenerationRecVal prodGenerationChecked).toVConstant).get! + +def prodFinalEnv07 : VEnv := + prodGenerationChecked.generatedRules.foldl VEnv.addDefEq prodRecEnv07 + +def prodTypeMap07 : ConstMap := + ({} : ConstMap).insert ``Prod prodInfo07 + +def prodCtorMap07 : ConstMap := + prodTypeMap07.insert ``Prod.mk prodMkInfo07 + +def prodMap07 : ConstMap := + prodCtorMap07.insert ``Prod.rec prodRecInfo07 + +theorem prodTypeEnv_ordered07 : prodTypeEnv07.Ordered := + replayTypeEnv_ordered07 .empty prodGenerationWF07 rfl + +theorem prodCtorEnv_ordered07 : prodCtorEnv07.Ordered := + replayCtorEnv_ordered07 prodGenerationWF07 rfl + prodTypeEnv_ordered07 rfl + +theorem prodGenerationEnv07 : + VInductDecl.GenerationEnv prodGenerationChecked prodCtorEnv07 := + replayGenerationEnv07 prodGenerationWF07 rfl rfl + prodCtorEnv_ordered07 + +theorem prodInfoTr07 : + TrConstVal .safe VEnv.empty prodInfo07 prodType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr VEnv.empty prodInfo07.levelParams [] + prodInfo07.type prodType.type := by + tr_type_expr_tac + obtain ⟨sort, familyType⟩ := replayRawFamilyWF07 prodGenerationWF07 + exact shape.to_trExprS .empty trivial ⟨.sort sort, familyType⟩ + +theorem prodCtorInfoTr07 : + TrConstVal .safe prodTypeEnv07 prodMkInfo07 prodType.ctors[0] := by + have hProd : prodTypeEnv07.constants ``Prod = + some prodType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr prodTypeEnv07 prodMkInfo07.levelParams [] + prodMkInfo07.type prodType.ctors[0].type := by + tr_type_expr_tac + obtain ⟨sort, ctorType⟩ := replayRawCtorWF07 prodGenerationWF07 rfl + prodType.ctors[0] (.head _) + exact shape.to_trExprS prodTypeEnv_ordered07 trivial + ⟨.sort sort, ctorType⟩ + +theorem prodRecInfoTr07 : + TrConstVal .safe prodCtorEnv07 prodRecInfo07 + (inductGenerationRecVal prodGenerationChecked) := by + have hProd : prodCtorEnv07.constants ``Prod = + some prodType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr prodCtorEnv07 prodRecInfo07.levelParams [] + prodRecInfo07.type + (inductGenerationRecVal prodGenerationChecked).type := by + tr_type_expr_tac + obtain ⟨sort, recursorType⟩ := prodGenerationEnv07.recursor_wf + exact shape.to_trExprS prodCtorEnv_ordered07 trivial + ⟨.sort sort, recursorType⟩ + +theorem prodTypeFresh07 : ({} : ConstMap).find? ``Prod = none := by + simp [SMap.find?] + +theorem prodTypeMapWF07 : prodTypeMap07.WF := + SMap.WF.empty.insert _ _ prodTypeFresh07 + +theorem prodCtorFresh07 : prodTypeMap07.find? ``Prod.mk = none := by + rw [prodTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem prodCtorMapWF07 : prodCtorMap07.WF := + prodTypeMapWF07.insert _ _ prodCtorFresh07 + +theorem prodRecFresh07 : prodCtorMap07.find? ``Prod.rec = none := by + rw [prodCtorMap07, prodTypeMapWF07.find?_insert, prodTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem prodAddInduct07 : AddInduct ({} : ConstMap) VEnv.empty prodDecl + prodMap07 prodFinalEnv07 := by + refine ⟨{ + generation := prodGenerationChecked + generation_wf := prodGenerationWF07 + typeMap := prodTypeMap07 + typeEnv := prodTypeEnv07 + ctorMap := prodCtorMap07 + ctorEnv := prodCtorEnv07 + recEnv := prodRecEnv07 + addType := { + info := prodInfo07 + kind_eq := by simp [prodInfo07, InductConstantKind.Matches] + tr := prodInfoTr07 + map_fresh := prodTypeFresh07 + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := prodRecInfo07 + kind_eq := by simp [prodRecInfo07, InductConstantKind.Matches] + tr := prodRecInfoTr07 + map_fresh := prodRecFresh07 + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ }⟩ + exact .cons { + info := prodMkInfo07 + kind_eq := by simp [prodMkInfo07, InductConstantKind.Matches] + tr := prodCtorInfoTr07 + map_fresh := by simpa [prodType] using prodCtorFresh07 + env_add := rfl + map_add := rfl } .nil + +theorem prodAligned07 : Aligned .safe prodMap07 prodFinalEnv07 := + Aligned.addInduct prodAddInduct07 .empty + +def prodReplay07 : SingletonReplayArtifact where + label := ``Prod + source := prodDecl + inputMap := {} + inputEnv := .empty + inputMapWF := SMap.WF.empty + outputMap := prodMap07 + outputEnv := prodFinalEnv07 + inputOrdered := .empty + transaction := prodAddInduct07 + aligned := prodAligned07 + +/-! ### And -/ + +theorem andCheckedWF07 : andChecked.WF VEnv.empty := by + constructor + · change VEnv.empty.OnTel 0 [] [.sort .zero, .sort .zero] + exact ⟨⟨.succ .zero, VEnv.HasType.sort trivial⟩, + ⟨⟨.succ .zero, VEnv.HasType.sort trivial⟩, trivial⟩⟩ + · intro ctor hctor + have hctor' := List.mem_singleton.1 hctor + subst ctor + constructor + · change VInductDecl.fieldsWF 0 ``And 2 VEnv.empty .zero [] + [.sort .zero, .sort .zero] 0 [.bvar 1, .bvar 1] + constructor + · exact .inr (.inr ⟨rfl, .zero, by type_tac, .inl rfl⟩) + constructor + · intro recursive + contradiction + constructor + · exact .inr (.inr ⟨rfl, .zero, by type_tac, .inl rfl⟩) + constructor + · intro recursive + contradiction + · trivial + · exact .nil + +theorem andGenerationWF07 : andGenerationChecked.WF VEnv.empty := by + exact andCheckedWF07.identityGeneration .empty + +def andTypeEnv07 : VEnv := + (VEnv.empty.addConst andType.name andType.toVConstant).get! + +def andCtorEnv07 : VEnv := + (andTypeEnv07.addConst andType.ctors[0].name + andType.ctors[0].toVConstant).get! + +def andRecEnv07 : VEnv := + (andCtorEnv07.addConst ``And.rec + (inductGenerationRecVal andGenerationChecked).toVConstant).get! + +def andFinalEnv07 : VEnv := + andGenerationChecked.generatedRules.foldl VEnv.addDefEq andRecEnv07 + +def andTypeMap07 : ConstMap := + ({} : ConstMap).insert ``And andInfo06 + +def andCtorMap07 : ConstMap := + andTypeMap07.insert ``And.intro andIntroInfo06 + +def andMap07 : ConstMap := + andCtorMap07.insert ``And.rec andRecInfo06 + +theorem andTypeEnv_ordered07 : andTypeEnv07.Ordered := + replayTypeEnv_ordered07 .empty andGenerationWF07 rfl + +theorem andCtorEnv_ordered07 : andCtorEnv07.Ordered := + replayCtorEnv_ordered07 andGenerationWF07 rfl + andTypeEnv_ordered07 rfl + +theorem andGenerationEnv07 : + VInductDecl.GenerationEnv andGenerationChecked andCtorEnv07 := + replayGenerationEnv07 andGenerationWF07 rfl rfl + andCtorEnv_ordered07 + +theorem andInfoTr07 : + TrConstVal .safe VEnv.empty andInfo06 andType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr VEnv.empty andInfo06.levelParams [] + andInfo06.type andType.type := by + tr_type_expr_tac + obtain ⟨sort, familyType⟩ := replayRawFamilyWF07 andGenerationWF07 + exact shape.to_trExprS .empty trivial ⟨.sort sort, familyType⟩ + +theorem andCtorInfoTr07 : + TrConstVal .safe andTypeEnv07 andIntroInfo06 andType.ctors[0] := by + have hAnd : andTypeEnv07.constants ``And = + some andType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr andTypeEnv07 andIntroInfo06.levelParams [] + andIntroInfo06.type andType.ctors[0].type := by + tr_type_expr_tac + obtain ⟨sort, ctorType⟩ := replayRawCtorWF07 andGenerationWF07 rfl + andType.ctors[0] (.head _) + exact shape.to_trExprS andTypeEnv_ordered07 trivial + ⟨.sort sort, ctorType⟩ + +theorem andRecInfoTr07 : + TrConstVal .safe andCtorEnv07 andRecInfo06 + (inductGenerationRecVal andGenerationChecked) := by + have hAnd : andCtorEnv07.constants ``And = + some andType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr andCtorEnv07 andRecInfo06.levelParams [] + andRecInfo06.type + (inductGenerationRecVal andGenerationChecked).type := by + tr_type_expr_tac + obtain ⟨sort, recursorType⟩ := andGenerationEnv07.recursor_wf + exact shape.to_trExprS andCtorEnv_ordered07 trivial + ⟨.sort sort, recursorType⟩ + +theorem andTypeFresh07 : ({} : ConstMap).find? ``And = none := by + simp [SMap.find?] + +theorem andTypeMapWF07 : andTypeMap07.WF := + SMap.WF.empty.insert _ _ andTypeFresh07 + +theorem andCtorFresh07 : andTypeMap07.find? ``And.intro = none := by + rw [andTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem andCtorMapWF07 : andCtorMap07.WF := + andTypeMapWF07.insert _ _ andCtorFresh07 + +theorem andRecFresh07 : andCtorMap07.find? ``And.rec = none := by + rw [andCtorMap07, andTypeMapWF07.find?_insert, andTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem andAddInduct07 : AddInduct ({} : ConstMap) VEnv.empty andDecl + andMap07 andFinalEnv07 := by + refine ⟨{ + generation := andGenerationChecked + generation_wf := andGenerationWF07 + typeMap := andTypeMap07 + typeEnv := andTypeEnv07 + ctorMap := andCtorMap07 + ctorEnv := andCtorEnv07 + recEnv := andRecEnv07 + addType := { + info := andInfo06 + kind_eq := by simp [andInfo06, InductConstantKind.Matches] + tr := andInfoTr07 + map_fresh := andTypeFresh07 + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := andRecInfo06 + kind_eq := by simp [andRecInfo06, InductConstantKind.Matches] + tr := andRecInfoTr07 + map_fresh := andRecFresh07 + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ }⟩ + exact .cons { + info := andIntroInfo06 + kind_eq := by simp [andIntroInfo06, InductConstantKind.Matches] + tr := andCtorInfoTr07 + map_fresh := by simpa [andType] using andCtorFresh07 + env_add := rfl + map_add := rfl } .nil + +theorem andAligned07 : Aligned .safe andMap07 andFinalEnv07 := + Aligned.addInduct andAddInduct07 .empty + +def andReplay07 : SingletonReplayArtifact where + label := ``And + source := andDecl + inputMap := {} + inputEnv := .empty + inputMapWF := SMap.WF.empty + outputMap := andMap07 + outputEnv := andFinalEnv07 + inputOrdered := .empty + transaction := andAddInduct07 + aligned := andAligned07 + +/-! ### Or -/ + +theorem orCheckedWF07 : orChecked.WF VEnv.empty := by + constructor + · change VEnv.empty.OnTel 0 [] [.sort .zero, .sort .zero] + exact ⟨⟨.succ .zero, VEnv.HasType.sort trivial⟩, + ⟨⟨.succ .zero, VEnv.HasType.sort trivial⟩, trivial⟩⟩ + · intro ctor hctor + rcases List.mem_cons.1 hctor with rfl | hctor + · constructor + · change VInductDecl.fieldsWF 0 ``Or 2 VEnv.empty .zero [] + [.sort .zero, .sort .zero] 0 [.bvar 1] + constructor + · exact .inr (.inr ⟨rfl, .zero, by type_tac, .inl rfl⟩) + constructor + · intro recursive + contradiction + · trivial + · exact .nil + · have hctor' := List.mem_singleton.1 hctor + subst ctor + constructor + · change VInductDecl.fieldsWF 0 ``Or 2 VEnv.empty .zero [] + [.sort .zero, .sort .zero] 0 [.bvar 0] + constructor + · exact .inr (.inr ⟨rfl, .zero, by type_tac, .inl rfl⟩) + constructor + · intro recursive + contradiction + · trivial + · exact .nil + +theorem orGenerationWF07 : orGenerationChecked.WF VEnv.empty := by + exact orCheckedWF07.identityGeneration .empty + +def orTypeEnv07 : VEnv := + (VEnv.empty.addConst orType.name orType.toVConstant).get! + +def orInlEnv07 : VEnv := + (orTypeEnv07.addConst orType.ctors[0].name + orType.ctors[0].toVConstant).get! + +def orCtorEnv07 : VEnv := + (orInlEnv07.addConst orType.ctors[1].name + orType.ctors[1].toVConstant).get! + +def orRecEnv07 : VEnv := + (orCtorEnv07.addConst ``Or.rec + (inductGenerationRecVal orGenerationChecked).toVConstant).get! + +def orFinalEnv07 : VEnv := + orGenerationChecked.generatedRules.foldl VEnv.addDefEq orRecEnv07 + +def orTypeMap07 : ConstMap := + ({} : ConstMap).insert ``Or orInfo06 + +def orInlMap07 : ConstMap := + orTypeMap07.insert ``Or.inl orInlInfo06 + +def orCtorMap07 : ConstMap := + orInlMap07.insert ``Or.inr orInrInfo06 + +def orMap07 : ConstMap := + orCtorMap07.insert ``Or.rec orRecInfo06 + +theorem orTypeEnv_ordered07 : orTypeEnv07.Ordered := + replayTypeEnv_ordered07 .empty orGenerationWF07 rfl + +theorem orInlEnv_ordered07 : orInlEnv07.Ordered := by + refine .const (n := orType.ctors[0].name) + (ci := orType.ctors[0].toVConstant) orTypeEnv_ordered07 ?_ rfl + exact replayRawCtorWF07 orGenerationWF07 rfl orType.ctors[0] + (.head _) + +theorem orCtorEnv_ordered07 : orCtorEnv07.Ordered := + replayCtorEnv_ordered07 orGenerationWF07 rfl + orTypeEnv_ordered07 rfl + +theorem orGenerationEnv07 : + VInductDecl.GenerationEnv orGenerationChecked orCtorEnv07 := + replayGenerationEnv07 orGenerationWF07 rfl rfl + orCtorEnv_ordered07 + +theorem orInfoTr07 : + TrConstVal .safe VEnv.empty orInfo06 orType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr VEnv.empty orInfo06.levelParams [] + orInfo06.type orType.type := by + tr_type_expr_tac + obtain ⟨sort, familyType⟩ := replayRawFamilyWF07 orGenerationWF07 + exact shape.to_trExprS .empty trivial ⟨.sort sort, familyType⟩ + +theorem orInlInfoTr07 : + TrConstVal .safe orTypeEnv07 orInlInfo06 orType.ctors[0] := by + have hOr : orTypeEnv07.constants ``Or = some orType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr orTypeEnv07 orInlInfo06.levelParams [] + orInlInfo06.type orType.ctors[0].type := by + tr_type_expr_tac + obtain ⟨sort, ctorType⟩ := replayRawCtorWF07 orGenerationWF07 rfl + orType.ctors[0] (.head _) + exact shape.to_trExprS orTypeEnv_ordered07 trivial + ⟨.sort sort, ctorType⟩ + +theorem orInrInfoTr07 : + TrConstVal .safe orInlEnv07 orInrInfo06 orType.ctors[1] := by + have hOr : orInlEnv07.constants ``Or = some orType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr orInlEnv07 orInrInfo06.levelParams [] + orInrInfo06.type orType.ctors[1].type := by + tr_type_expr_tac + obtain ⟨sort, ctorType⟩ := replayRawCtorWF07 orGenerationWF07 rfl + orType.ctors[1] (.tail _ (.head _)) + have typeToInl : orTypeEnv07 ≤ orInlEnv07 := + VEnv.addConst_le (show orTypeEnv07.addConst orType.ctors[0].name + orType.ctors[0].toVConstant = some orInlEnv07 from rfl) + exact shape.to_trExprS orInlEnv_ordered07 trivial + ⟨.sort sort, ctorType.mono typeToInl⟩ + +theorem orRecInfoTr07 : + TrConstVal .safe orCtorEnv07 orRecInfo06 + (inductGenerationRecVal orGenerationChecked) := by + have hOr : orCtorEnv07.constants ``Or = some orType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr orCtorEnv07 orRecInfo06.levelParams [] + orRecInfo06.type + (inductGenerationRecVal orGenerationChecked).type := by + tr_type_expr_tac + obtain ⟨sort, recursorType⟩ := orGenerationEnv07.recursor_wf + exact shape.to_trExprS orCtorEnv_ordered07 trivial + ⟨.sort sort, recursorType⟩ + +theorem orTypeFresh07 : ({} : ConstMap).find? ``Or = none := by + simp [SMap.find?] + +theorem orTypeMapWF07 : orTypeMap07.WF := + SMap.WF.empty.insert _ _ orTypeFresh07 + +theorem orInlFresh07 : orTypeMap07.find? ``Or.inl = none := by + rw [orTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem orInlMapWF07 : orInlMap07.WF := + orTypeMapWF07.insert _ _ orInlFresh07 + +theorem orInrFresh07 : orInlMap07.find? ``Or.inr = none := by + rw [orInlMap07, orTypeMapWF07.find?_insert, orTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem orCtorMapWF07 : orCtorMap07.WF := + orInlMapWF07.insert _ _ orInrFresh07 + +theorem orRecFresh07 : orCtorMap07.find? ``Or.rec = none := by + rw [orCtorMap07, orInlMapWF07.find?_insert, orInlMap07, + orTypeMapWF07.find?_insert, orTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem orAddInduct07 : AddInduct ({} : ConstMap) VEnv.empty orDecl + orMap07 orFinalEnv07 := by + refine ⟨{ + generation := orGenerationChecked + generation_wf := orGenerationWF07 + typeMap := orTypeMap07 + typeEnv := orTypeEnv07 + ctorMap := orCtorMap07 + ctorEnv := orCtorEnv07 + recEnv := orRecEnv07 + addType := { + info := orInfo06 + kind_eq := by simp [orInfo06, InductConstantKind.Matches] + tr := orInfoTr07 + map_fresh := orTypeFresh07 + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := orRecInfo06 + kind_eq := by simp [orRecInfo06, InductConstantKind.Matches] + tr := orRecInfoTr07 + map_fresh := orRecFresh07 + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ }⟩ + exact .cons { + info := orInlInfo06 + kind_eq := by simp [orInlInfo06, InductConstantKind.Matches] + tr := orInlInfoTr07 + map_fresh := by simpa [orType] using orInlFresh07 + env_add := rfl + map_add := rfl } + (.cons { + info := orInrInfo06 + kind_eq := by simp [orInrInfo06, InductConstantKind.Matches] + tr := orInrInfoTr07 + map_fresh := by + change orInlMap07.find? ``Or.inr = none + exact orInrFresh07 + env_add := rfl + map_add := rfl } .nil) + +theorem orAligned07 : Aligned .safe orMap07 orFinalEnv07 := + Aligned.addInduct orAddInduct07 .empty + +def orReplay07 : SingletonReplayArtifact where + label := ``Or + source := orDecl + inputMap := {} + inputEnv := .empty + inputMapWF := SMap.WF.empty + outputMap := orMap07 + outputEnv := orFinalEnv07 + inputOrdered := .empty + transaction := orAddInduct07 + aligned := orAligned07 + +/-! ### HEq -/ + +theorem heqCheckedWF07 : heqChecked.WF VEnv.empty := by + constructor + · change VEnv.empty.OnTel 1 [] + [.sort (.param 0), .bvar 0, .sort (.param 0), .bvar 0] + exact ⟨⟨.succ (.param 0), VEnv.HasType.sort (by decide)⟩, + ⟨⟨.param 0, by type_tac⟩, + ⟨⟨.succ (.param 0), VEnv.HasType.sort (by decide)⟩, + ⟨⟨.param 0, by type_tac⟩, trivial⟩⟩⟩⟩ + · intro ctor hctor + have hctor' := List.mem_singleton.1 hctor + subst ctor + constructor + · change True + trivial + · change VEnv.empty.SpineWF 1 [.bvar 0, .sort (.param 0)] + (.forallE (.sort (.param 0)) + (.forallE (.bvar 0) (.sort .zero))) + [.bvar 1, .bvar 0] (.sort .zero) + exact .cons (by type_tac) <| .cons (by type_tac) .nil + +theorem heqGenerationWF07 : heqGenerationChecked.WF VEnv.empty := by + exact heqCheckedWF07.identityGeneration .empty + +def heqTypeEnv07 : VEnv := + (VEnv.empty.addConst heqType.name heqType.toVConstant).get! + +def heqCtorEnv07 : VEnv := + (heqTypeEnv07.addConst heqType.ctors[0].name + heqType.ctors[0].toVConstant).get! + +def heqRecEnv07 : VEnv := + (heqCtorEnv07.addConst ``HEq.rec + (inductGenerationRecVal heqGenerationChecked).toVConstant).get! + +def heqFinalEnv07 : VEnv := + heqGenerationChecked.generatedRules.foldl VEnv.addDefEq heqRecEnv07 + +def heqTypeMap07 : ConstMap := + ({} : ConstMap).insert ``HEq heqInfo07 + +def heqCtorMap07 : ConstMap := + heqTypeMap07.insert ``HEq.refl heqReflInfo07 + +def heqMap07 : ConstMap := + heqCtorMap07.insert ``HEq.rec heqRecInfo07 + +theorem heqTypeEnv_ordered07 : heqTypeEnv07.Ordered := + replayTypeEnv_ordered07 .empty heqGenerationWF07 rfl + +theorem heqCtorEnv_ordered07 : heqCtorEnv07.Ordered := + replayCtorEnv_ordered07 heqGenerationWF07 rfl + heqTypeEnv_ordered07 rfl + +theorem heqGenerationEnv07 : + VInductDecl.GenerationEnv heqGenerationChecked heqCtorEnv07 := + replayGenerationEnv07 heqGenerationWF07 rfl rfl + heqCtorEnv_ordered07 + +theorem heqInfoTr07 : + TrConstVal .safe VEnv.empty heqInfo07 heqType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr VEnv.empty heqInfo07.levelParams [] + heqInfo07.type heqType.type := by + tr_type_expr_tac + obtain ⟨sort, familyType⟩ := replayRawFamilyWF07 heqGenerationWF07 + exact shape.to_trExprS .empty trivial ⟨.sort sort, familyType⟩ + +theorem heqCtorInfoTr07 : + TrConstVal .safe heqTypeEnv07 heqReflInfo07 heqType.ctors[0] := by + have hHEq : heqTypeEnv07.constants ``HEq = + some heqType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr heqTypeEnv07 heqReflInfo07.levelParams [] + heqReflInfo07.type heqType.ctors[0].type := by + tr_type_expr_tac + obtain ⟨sort, ctorType⟩ := replayRawCtorWF07 heqGenerationWF07 rfl + heqType.ctors[0] (.head _) + exact shape.to_trExprS heqTypeEnv_ordered07 trivial + ⟨.sort sort, ctorType⟩ + +theorem heqRecInfoTr07 : + TrConstVal .safe heqCtorEnv07 heqRecInfo07 + (inductGenerationRecVal heqGenerationChecked) := by + have hHEq : heqCtorEnv07.constants ``HEq = + some heqType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr heqCtorEnv07 heqRecInfo07.levelParams [] + heqRecInfo07.type + (inductGenerationRecVal heqGenerationChecked).type := by + tr_type_expr_tac + obtain ⟨sort, recursorType⟩ := heqGenerationEnv07.recursor_wf + exact shape.to_trExprS heqCtorEnv_ordered07 trivial + ⟨.sort sort, recursorType⟩ + +theorem heqTypeFresh07 : ({} : ConstMap).find? ``HEq = none := by + simp [SMap.find?] + +theorem heqTypeMapWF07 : heqTypeMap07.WF := + SMap.WF.empty.insert _ _ heqTypeFresh07 + +theorem heqCtorFresh07 : heqTypeMap07.find? ``HEq.refl = none := by + rw [heqTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem heqCtorMapWF07 : heqCtorMap07.WF := + heqTypeMapWF07.insert _ _ heqCtorFresh07 + +theorem heqRecFresh07 : heqCtorMap07.find? ``HEq.rec = none := by + rw [heqCtorMap07, heqTypeMapWF07.find?_insert, heqTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem heqAddInduct07 : AddInduct ({} : ConstMap) VEnv.empty heqDecl + heqMap07 heqFinalEnv07 := by + refine ⟨{ + generation := heqGenerationChecked + generation_wf := heqGenerationWF07 + typeMap := heqTypeMap07 + typeEnv := heqTypeEnv07 + ctorMap := heqCtorMap07 + ctorEnv := heqCtorEnv07 + recEnv := heqRecEnv07 + addType := { + info := heqInfo07 + kind_eq := by simp [heqInfo07, InductConstantKind.Matches] + tr := heqInfoTr07 + map_fresh := heqTypeFresh07 + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := heqRecInfo07 + kind_eq := by simp [heqRecInfo07, InductConstantKind.Matches] + tr := heqRecInfoTr07 + map_fresh := heqRecFresh07 + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ }⟩ + exact .cons { + info := heqReflInfo07 + kind_eq := by simp [heqReflInfo07, InductConstantKind.Matches] + tr := heqCtorInfoTr07 + map_fresh := by simpa [heqType] using heqCtorFresh07 + env_add := rfl + map_add := rfl } .nil + +theorem heqAligned07 : Aligned .safe heqMap07 heqFinalEnv07 := + Aligned.addInduct heqAddInduct07 .empty + +def heqReplay07 : SingletonReplayArtifact where + label := ``HEq + source := heqDecl + inputMap := {} + inputEnv := .empty + inputMapWF := SMap.WF.empty + outputMap := heqMap07 + outputEnv := heqFinalEnv07 + inputOrdered := .empty + transaction := heqAddInduct07 + aligned := heqAligned07 + +/-! ### Fin dependency environment -/ + +def finLTInfo07 : ConstantInfo := kernelInductInfo% LT +def finLTMkInfo07 : ConstantInfo := kernelCtorInfo% LT.mk +def finLTLtInfo07 : ConstantInfo := .defnInfo (kernelDefVal% LT.lt) +def finInstLTNatInfo07 : ConstantInfo := + .defnInfo (kernelDefVal% instLTNat) + +def finLTConst07 : VConstVal := ⟨vconst(type_of% @LT), ``LT⟩ +def finLTMkConst07 : VConstVal := ⟨vconst(type_of% @LT.mk), ``LT.mk⟩ +def finLTLtConst07 : VConstVal := ⟨vconst(type_of% @LT.lt), ``LT.lt⟩ +def finInstLTNatConst07 : VConstVal := + ⟨vconst(type_of% @instLTNat), ``instLTNat⟩ + +def finLTMap07 : ConstMap := natTypeMap.insert ``LT finLTInfo07 +def finLTMkMap07 : ConstMap := finLTMap07.insert ``LT.mk finLTMkInfo07 +def finLTLtMap07 : ConstMap := finLTMkMap07.insert ``LT.lt finLTLtInfo07 +def finInputMap07 : ConstMap := + finLTLtMap07.insert ``instLTNat finInstLTNatInfo07 + +def finLTEnv07 : VEnv := + (natTypeEnv.addConst ``LT finLTConst07.toVConstant).get! + +def finLTMkEnv07 : VEnv := + (finLTEnv07.addConst ``LT.mk finLTMkConst07.toVConstant).get! + +def finLTLtEnv07 : VEnv := + (finLTMkEnv07.addConst ``LT.lt finLTLtConst07.toVConstant).get! + +def finInputEnv07 : VEnv := + (finLTLtEnv07.addConst ``instLTNat + finInstLTNatConst07.toVConstant).get! + +theorem natTypeAligned07 : Aligned .safe natTypeMap natTypeEnv := by + exact Aligned.const .empty natType_fresh natInfo_tr.1 rfl natInfo_tr.2 + +theorem finLTConstWF07 : finLTConst07.toVConstant.WF natTypeEnv := by + change natTypeEnv.IsType finLTConst07.uvars [] finLTConst07.type + dsimp [finLTConst07] + refine ⟨.imax (.succ (.succ (.param 0))) + (.succ (.succ (.param 0))), ?_⟩ + refine VEnv.HasType.forallE + (u := .succ (.succ (.param 0))) + (v := .succ (.succ (.param 0))) ?_ ?_ + · exact VEnv.HasType.sort (by decide) + · exact VEnv.HasType.sort (by decide) + +theorem finLTInfoTr07 : + TrConstVal .safe natTypeEnv finLTInfo07 finLTConst07 := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr natTypeEnv finLTInfo07.levelParams [] + finLTInfo07.type finLTConst07.type := by + tr_type_expr_tac + obtain ⟨sort, familyType⟩ := finLTConstWF07 + exact shape.to_trExprS natTypeEnv_ordered trivial + ⟨.sort sort, familyType⟩ + +theorem finLTFresh07 : natTypeMap.find? ``LT = none := by + rw [natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem finLTMapWF07 : finLTMap07.WF := + natTypeMap_wf.insert _ _ finLTFresh07 + +theorem finLTEnv_ordered07 : finLTEnv07.Ordered := + .const (n := ``LT) (ci := finLTConst07.toVConstant) + natTypeEnv_ordered finLTConstWF07 rfl + +theorem finLTAligned07 : Aligned .safe finLTMap07 finLTEnv07 := + Aligned.const natTypeAligned07 finLTFresh07 finLTInfoTr07.1 rfl + finLTInfoTr07.2 + +theorem finLTMkConstWF07 : finLTMkConst07.toVConstant.WF finLTEnv07 := by + have hLT : finLTEnv07.constants ``LT = + some finLTConst07.toVConstant := rfl + change finLTEnv07.IsType finLTMkConst07.uvars [] finLTMkConst07.type + dsimp [finLTMkConst07] + refine ⟨.imax (.succ (.succ (.param 0))) + (.imax + (.imax (.succ (.param 0)) + (.imax (.succ (.param 0)) (.succ .zero))) + (.succ (.param 0))), ?_⟩ + refine VEnv.HasType.forallE + (u := .succ (.succ (.param 0))) + (v := .imax + (.imax (.succ (.param 0)) + (.imax (.succ (.param 0)) (.succ .zero))) + (.succ (.param 0))) ?_ ?_ + · exact VEnv.HasType.sort (by decide) + · refine VEnv.HasType.forallE + (u := .imax (.succ (.param 0)) + (.imax (.succ (.param 0)) (.succ .zero))) + (v := .succ (.param 0)) ?_ ?_ + · refine VEnv.HasType.forallE + (u := .succ (.param 0)) + (v := .imax (.succ (.param 0)) (.succ .zero)) ?_ ?_ + · type_tac + · refine VEnv.HasType.forallE + (u := .succ (.param 0)) (v := .succ .zero) ?_ ?_ + · type_tac + · exact VEnv.HasType.sort (by decide) + · type_tac + +theorem finLTMkInfoTr07 : + TrConstVal .safe finLTEnv07 finLTMkInfo07 finLTMkConst07 := by + have hLT : finLTEnv07.constants ``LT = + some finLTConst07.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr finLTEnv07 finLTMkInfo07.levelParams [] + finLTMkInfo07.type finLTMkConst07.type := by + tr_type_expr_tac + obtain ⟨sort, ctorType⟩ := finLTMkConstWF07 + exact shape.to_trExprS finLTEnv_ordered07 trivial + ⟨.sort sort, ctorType⟩ + +theorem finLTMkFresh07 : finLTMap07.find? ``LT.mk = none := by + rw [finLTMap07, natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem finLTMkMapWF07 : finLTMkMap07.WF := + finLTMapWF07.insert _ _ finLTMkFresh07 + +theorem finLTMkEnv_ordered07 : finLTMkEnv07.Ordered := + .const (n := ``LT.mk) (ci := finLTMkConst07.toVConstant) + finLTEnv_ordered07 finLTMkConstWF07 rfl + +theorem finLTMkAligned07 : Aligned .safe finLTMkMap07 finLTMkEnv07 := + Aligned.const finLTAligned07 finLTMkFresh07 finLTMkInfoTr07.1 rfl + finLTMkInfoTr07.2 + +theorem finLTLtConstWF07 : finLTLtConst07.toVConstant.WF finLTMkEnv07 := by + have hLT : finLTMkEnv07.constants ``LT = + some finLTConst07.toVConstant := rfl + change finLTMkEnv07.IsType finLTLtConst07.uvars [] finLTLtConst07.type + dsimp [finLTLtConst07] + refine ⟨.imax (.succ (.succ (.param 0))) + (.imax (.succ (.param 0)) + (.imax (.succ (.param 0)) + (.imax (.succ (.param 0)) (.succ .zero)))), ?_⟩ + refine VEnv.HasType.forallE + (u := .succ (.succ (.param 0))) + (v := .imax (.succ (.param 0)) + (.imax (.succ (.param 0)) + (.imax (.succ (.param 0)) (.succ .zero)))) ?_ ?_ + · exact VEnv.HasType.sort (by decide) + · refine VEnv.HasType.forallE + (u := .succ (.param 0)) + (v := .imax (.succ (.param 0)) + (.imax (.succ (.param 0)) (.succ .zero))) ?_ ?_ + · type_tac + · refine VEnv.HasType.forallE + (u := .succ (.param 0)) + (v := .imax (.succ (.param 0)) (.succ .zero)) ?_ ?_ + · type_tac + · refine VEnv.HasType.forallE + (u := .succ (.param 0)) (v := .succ .zero) ?_ ?_ + · type_tac + · exact VEnv.HasType.sort (by decide) + +theorem finLTLtInfoTr07 : + TrConstVal .safe finLTMkEnv07 finLTLtInfo07 finLTLtConst07 := by + have hLT : finLTMkEnv07.constants ``LT = + some finLTConst07.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr finLTMkEnv07 finLTLtInfo07.levelParams [] + finLTLtInfo07.type finLTLtConst07.type := by + tr_type_expr_tac + obtain ⟨sort, projectionType⟩ := finLTLtConstWF07 + exact shape.to_trExprS finLTMkEnv_ordered07 trivial + ⟨.sort sort, projectionType⟩ + +theorem finLTLtFresh07 : finLTMkMap07.find? ``LT.lt = none := by + rw [finLTMkMap07, finLTMapWF07.find?_insert, finLTMap07, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem finLTLtMapWF07 : finLTLtMap07.WF := + finLTMkMapWF07.insert _ _ finLTLtFresh07 + +theorem finLTLtEnv_ordered07 : finLTLtEnv07.Ordered := + .const (n := ``LT.lt) (ci := finLTLtConst07.toVConstant) + finLTMkEnv_ordered07 finLTLtConstWF07 rfl + +theorem finLTLtAligned07 : Aligned .safe finLTLtMap07 finLTLtEnv07 := + Aligned.const finLTMkAligned07 finLTLtFresh07 finLTLtInfoTr07.1 rfl + finLTLtInfoTr07.2 + +theorem finInstLTNatConstWF07 : + finInstLTNatConst07.toVConstant.WF finLTLtEnv07 := by + have hNat : finLTLtEnv07.constants ``Nat = + some natType.toVConstant := rfl + have hLT : finLTLtEnv07.constants ``LT = + some finLTConst07.toVConstant := rfl + change finLTLtEnv07.IsType finInstLTNatConst07.uvars [] + finInstLTNatConst07.type + dsimp [finInstLTNatConst07] + refine ⟨.succ .zero, ?_⟩ + type_tac + +theorem finInstLTNatInfoTr07 : + TrConstVal .safe finLTLtEnv07 finInstLTNatInfo07 + finInstLTNatConst07 := by + have hNat : finLTLtEnv07.constants ``Nat = + some natType.toVConstant := rfl + have hLT : finLTLtEnv07.constants ``LT = + some finLTConst07.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr finLTLtEnv07 finInstLTNatInfo07.levelParams [] + finInstLTNatInfo07.type finInstLTNatConst07.type := by + tr_type_expr_tac + obtain ⟨sort, instanceType⟩ := finInstLTNatConstWF07 + exact shape.to_trExprS finLTLtEnv_ordered07 trivial + ⟨.sort sort, instanceType⟩ + +theorem finInstLTNatFresh07 : + finLTLtMap07.find? ``instLTNat = none := by + rw [finLTLtMap07, finLTMkMapWF07.find?_insert, finLTMkMap07, + finLTMapWF07.find?_insert, finLTMap07, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem finInputMapWF07 : finInputMap07.WF := + finLTLtMapWF07.insert _ _ finInstLTNatFresh07 + +theorem finInputEnv_ordered07 : finInputEnv07.Ordered := + .const (n := ``instLTNat) (ci := finInstLTNatConst07.toVConstant) + finLTLtEnv_ordered07 finInstLTNatConstWF07 rfl + +theorem finInputAligned07 : Aligned .safe finInputMap07 finInputEnv07 := + Aligned.const finLTLtAligned07 finInstLTNatFresh07 + finInstLTNatInfoTr07.1 rfl finInstLTNatInfoTr07.2 + +example : finInputMap07 = finDependencyMap07 := rfl + +/-! ### Fin -/ + +theorem finCheckedWF07 : finChecked.WF finInputEnv07 := by + constructor + · change finInputEnv07.OnTel 0 [] [.const ``Nat []] + have hNat : finInputEnv07.constants ``Nat = + some natType.toVConstant := rfl + exact ⟨⟨.succ .zero, by type_tac⟩, trivial⟩ + · intro ctor hctor + have hctor' := List.mem_singleton.1 hctor + subst ctor + constructor + · change VInductDecl.fieldsWF 0 ``Fin 1 finInputEnv07 + (.succ .zero) [] [.const ``Nat []] 0 + [.const ``Nat [], + .app + (.app + (.app + (.app (.const ``LT.lt [.zero]) (.const ``Nat [])) + (.const ``instLTNat [])) + (.bvar 0)) + (.bvar 1)] + have hNat : finInputEnv07.constants ``Nat = + some natType.toVConstant := rfl + have hLT : finInputEnv07.constants ``LT = + some finLTConst07.toVConstant := rfl + have hLTLt : finInputEnv07.constants ``LT.lt = + some finLTLtConst07.toVConstant := rfl + have hInst : finInputEnv07.constants ``instLTNat = + some finInstLTNatConst07.toVConstant := rfl + constructor + · exact .inr (.inr ⟨rfl, .succ .zero, by type_tac, + .inr (VLevel.le_refl _)⟩) + constructor + · intro recursive + contradiction + constructor + · exact .inr (.inr ⟨rfl, .zero, by type_tac, + .inr VLevel.zero_le⟩) + constructor + · intro recursive + contradiction + · trivial + · exact .nil + +theorem finGenerationWF07 : finGenerationChecked.WF finInputEnv07 := by + exact finCheckedWF07.identityGeneration finInputEnv_ordered07 + +def finTypeEnv07 : VEnv := + (finInputEnv07.addConst finType.name finType.toVConstant).get! + +def finCtorEnv07 : VEnv := + (finTypeEnv07.addConst finType.ctors[0].name + finType.ctors[0].toVConstant).get! + +def finRecEnv07 : VEnv := + (finCtorEnv07.addConst ``Fin.rec + (inductGenerationRecVal finGenerationChecked).toVConstant).get! + +def finFinalEnv07 : VEnv := + finGenerationChecked.generatedRules.foldl VEnv.addDefEq finRecEnv07 + +def finTypeMap07 : ConstMap := finInputMap07.insert ``Fin finInfo07 +def finCtorMap07 : ConstMap := finTypeMap07.insert ``Fin.mk finMkInfo07 +def finMap07 : ConstMap := finCtorMap07.insert ``Fin.rec finRecInfo07 + +theorem finTypeEnv_ordered07 : finTypeEnv07.Ordered := + replayTypeEnv_ordered07 finInputEnv_ordered07 finGenerationWF07 rfl + +theorem finCtorEnv_ordered07 : finCtorEnv07.Ordered := + replayCtorEnv_ordered07 finGenerationWF07 rfl finTypeEnv_ordered07 rfl + +theorem finGenerationEnv07 : + VInductDecl.GenerationEnv finGenerationChecked finCtorEnv07 := + replayGenerationEnv07 finGenerationWF07 rfl rfl finCtorEnv_ordered07 + +theorem finInfoTr07 : + TrConstVal .safe finInputEnv07 finInfo07 finType.toVConstVal := by + have hNat : finInputEnv07.constants ``Nat = + some natType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr finInputEnv07 finInfo07.levelParams [] + finInfo07.type finType.type := by + tr_type_expr_tac + obtain ⟨sort, familyType⟩ := replayRawFamilyWF07 finGenerationWF07 + exact shape.to_trExprS finInputEnv_ordered07 trivial + ⟨.sort sort, familyType⟩ + +theorem finCtorInfoTr07 : + TrConstVal .safe finTypeEnv07 finMkInfo07 finType.ctors[0] := by + have hNat : finTypeEnv07.constants ``Nat = + some natType.toVConstant := rfl + have hLT : finTypeEnv07.constants ``LT = + some finLTConst07.toVConstant := rfl + have hLTLt : finTypeEnv07.constants ``LT.lt = + some finLTLtConst07.toVConstant := rfl + have hInst : finTypeEnv07.constants ``instLTNat = + some finInstLTNatConst07.toVConstant := rfl + have hFin : finTypeEnv07.constants ``Fin = + some finType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr finTypeEnv07 finMkInfo07.levelParams [] + finMkInfo07.type finType.ctors[0].type := by + tr_type_expr_tac + obtain ⟨sort, ctorType⟩ := replayRawCtorWF07 finGenerationWF07 rfl + finType.ctors[0] (.head _) + exact shape.to_trExprS finTypeEnv_ordered07 trivial + ⟨.sort sort, ctorType⟩ + +theorem finRecInfoTr07 : + TrConstVal .safe finCtorEnv07 finRecInfo07 + (inductGenerationRecVal finGenerationChecked) := by + have hNat : finCtorEnv07.constants ``Nat = + some natType.toVConstant := rfl + have hLT : finCtorEnv07.constants ``LT = + some finLTConst07.toVConstant := rfl + have hLTLt : finCtorEnv07.constants ``LT.lt = + some finLTLtConst07.toVConstant := rfl + have hInst : finCtorEnv07.constants ``instLTNat = + some finInstLTNatConst07.toVConstant := rfl + have hFin : finCtorEnv07.constants ``Fin = + some finType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr finCtorEnv07 finRecInfo07.levelParams [] + finRecInfo07.type + (inductGenerationRecVal finGenerationChecked).type := by + tr_type_expr_tac + obtain ⟨sort, recursorType⟩ := finGenerationEnv07.recursor_wf + exact shape.to_trExprS finCtorEnv_ordered07 trivial + ⟨.sort sort, recursorType⟩ + +theorem finTypeFresh07 : finInputMap07.find? ``Fin = none := by + rw [finInputMap07, finLTLtMapWF07.find?_insert, finLTLtMap07, + finLTMkMapWF07.find?_insert, finLTMkMap07, + finLTMapWF07.find?_insert, finLTMap07, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem finTypeMapWF07 : finTypeMap07.WF := + finInputMapWF07.insert _ _ finTypeFresh07 + +theorem finCtorFresh07 : finTypeMap07.find? ``Fin.mk = none := by + rw [finTypeMap07, finInputMapWF07.find?_insert, finInputMap07, + finLTLtMapWF07.find?_insert, finLTLtMap07, + finLTMkMapWF07.find?_insert, finLTMkMap07, + finLTMapWF07.find?_insert, finLTMap07, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem finCtorMapWF07 : finCtorMap07.WF := + finTypeMapWF07.insert _ _ finCtorFresh07 + +theorem finRecFresh07 : finCtorMap07.find? ``Fin.rec = none := by + rw [finCtorMap07, finTypeMapWF07.find?_insert, finTypeMap07, + finInputMapWF07.find?_insert, finInputMap07, + finLTLtMapWF07.find?_insert, finLTLtMap07, + finLTMkMapWF07.find?_insert, finLTMkMap07, + finLTMapWF07.find?_insert, finLTMap07, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem finAddInduct07 : AddInduct finInputMap07 finInputEnv07 finDecl + finMap07 finFinalEnv07 := by + refine ⟨{ + generation := finGenerationChecked + generation_wf := finGenerationWF07 + typeMap := finTypeMap07 + typeEnv := finTypeEnv07 + ctorMap := finCtorMap07 + ctorEnv := finCtorEnv07 + recEnv := finRecEnv07 + addType := { + info := finInfo07 + kind_eq := by simp [finInfo07, InductConstantKind.Matches] + tr := finInfoTr07 + map_fresh := finTypeFresh07 + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := finRecInfo07 + kind_eq := by simp [finRecInfo07, InductConstantKind.Matches] + tr := finRecInfoTr07 + map_fresh := finRecFresh07 + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ }⟩ + exact .cons { + info := finMkInfo07 + kind_eq := by simp [finMkInfo07, InductConstantKind.Matches] + tr := finCtorInfoTr07 + map_fresh := by simpa [finType] using finCtorFresh07 + env_add := rfl + map_add := rfl } .nil + +theorem finAligned07 : Aligned .safe finMap07 finFinalEnv07 := + Aligned.addInduct finAddInduct07 finInputAligned07 + +def finReplay07 : SingletonReplayArtifact where + label := ``Fin + source := finDecl + inputMap := finInputMap07 + inputEnv := finInputEnv07 + inputMapWF := finInputMapWF07 + outputMap := finMap07 + outputEnv := finFinalEnv07 + inputOrdered := finInputEnv_ordered07 + transaction := finAddInduct07 + aligned := finAligned07 + +/-! ### Vector dependency environment -/ + +def vectorArrayInfo07 : ConstantInfo := kernelInductInfo% Array +def vectorArraySizeInfo07 : ConstantInfo := + .defnInfo (kernelDefVal% Array.size) + +def vectorArrayConst07 : VConstVal := + ⟨vconst(type_of% @Array), ``Array⟩ + +def vectorArraySizeConst07 : VConstVal := + ⟨vconst(type_of% @Array.size), ``Array.size⟩ + +def vectorEqMap07 : ConstMap := natTypeMap.insert ``Eq eqInfo +def vectorArrayMap07 : ConstMap := + vectorEqMap07.insert ``Array vectorArrayInfo07 +def vectorInputMap07 : ConstMap := + vectorArrayMap07.insert ``Array.size vectorArraySizeInfo07 + +def vectorEqEnv07 : VEnv := + (natTypeEnv.addConst ``Eq eqType.toVConstant).get! + +def vectorArrayEnv07 : VEnv := + (vectorEqEnv07.addConst ``Array vectorArrayConst07.toVConstant).get! + +def vectorInputEnv07 : VEnv := + (vectorArrayEnv07.addConst ``Array.size + vectorArraySizeConst07.toVConstant).get! + +theorem empty_le_natTypeEnv07 : VEnv.empty ≤ natTypeEnv := + VEnv.addConst_le (show VEnv.empty.addConst natType.name + natType.toVConstant = some natTypeEnv from rfl) + +theorem vectorEqTypeWF07 : eqType.toVConstant.WF natTypeEnv := + eqType_wf.mono empty_le_natTypeEnv07 + +theorem vectorEqInfoTr07 : + TrConstVal .safe natTypeEnv eqInfo eqType.toVConstVal := + eqInfo_tr.mono empty_le_natTypeEnv07 + +theorem vectorEqFresh07 : natTypeMap.find? ``Eq = none := by + rw [natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem vectorEqMapWF07 : vectorEqMap07.WF := + natTypeMap_wf.insert _ _ vectorEqFresh07 + +theorem vectorEqEnv_ordered07 : vectorEqEnv07.Ordered := + .const (n := ``Eq) (ci := eqType.toVConstant) + natTypeEnv_ordered vectorEqTypeWF07 rfl + +theorem vectorEqAligned07 : Aligned .safe vectorEqMap07 vectorEqEnv07 := + Aligned.const natTypeAligned07 vectorEqFresh07 vectorEqInfoTr07.1 rfl + vectorEqInfoTr07.2 + +theorem vectorArrayConstWF07 : + vectorArrayConst07.toVConstant.WF vectorEqEnv07 := by + change vectorEqEnv07.IsType vectorArrayConst07.uvars [] + vectorArrayConst07.type + dsimp [vectorArrayConst07] + refine ⟨.imax (.succ (.succ (.param 0))) + (.succ (.succ (.param 0))), ?_⟩ + refine VEnv.HasType.forallE + (u := .succ (.succ (.param 0))) + (v := .succ (.succ (.param 0))) ?_ ?_ + · exact VEnv.HasType.sort (by decide) + · exact VEnv.HasType.sort (by decide) + +theorem vectorArrayInfoTr07 : + TrConstVal .safe vectorEqEnv07 vectorArrayInfo07 + vectorArrayConst07 := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr vectorEqEnv07 vectorArrayInfo07.levelParams [] + vectorArrayInfo07.type vectorArrayConst07.type := by + tr_type_expr_tac + obtain ⟨sort, arrayType⟩ := vectorArrayConstWF07 + exact shape.to_trExprS vectorEqEnv_ordered07 trivial + ⟨.sort sort, arrayType⟩ + +theorem vectorArrayFresh07 : vectorEqMap07.find? ``Array = none := by + rw [vectorEqMap07, natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem vectorArrayMapWF07 : vectorArrayMap07.WF := + vectorEqMapWF07.insert _ _ vectorArrayFresh07 + +theorem vectorArrayEnv_ordered07 : vectorArrayEnv07.Ordered := + .const (n := ``Array) (ci := vectorArrayConst07.toVConstant) + vectorEqEnv_ordered07 vectorArrayConstWF07 rfl + +theorem vectorArrayAligned07 : + Aligned .safe vectorArrayMap07 vectorArrayEnv07 := + Aligned.const vectorEqAligned07 vectorArrayFresh07 + vectorArrayInfoTr07.1 rfl vectorArrayInfoTr07.2 + +theorem vectorArraySizeConstWF07 : + vectorArraySizeConst07.toVConstant.WF vectorArrayEnv07 := by + have hNat : vectorArrayEnv07.constants ``Nat = + some natType.toVConstant := rfl + have hArray : vectorArrayEnv07.constants ``Array = + some vectorArrayConst07.toVConstant := rfl + change vectorArrayEnv07.IsType vectorArraySizeConst07.uvars [] + vectorArraySizeConst07.type + dsimp [vectorArraySizeConst07] + refine ⟨.imax (.succ (.succ (.param 0))) + (.imax (.succ (.param 0)) (.succ .zero)), ?_⟩ + refine VEnv.HasType.forallE + (u := .succ (.succ (.param 0))) + (v := .imax (.succ (.param 0)) (.succ .zero)) ?_ ?_ + · exact VEnv.HasType.sort (by decide) + · refine VEnv.HasType.forallE + (u := .succ (.param 0)) (v := .succ .zero) ?_ ?_ + · type_tac + · type_tac + +theorem vectorArraySizeInfoTr07 : + TrConstVal .safe vectorArrayEnv07 vectorArraySizeInfo07 + vectorArraySizeConst07 := by + have hNat : vectorArrayEnv07.constants ``Nat = + some natType.toVConstant := rfl + have hArray : vectorArrayEnv07.constants ``Array = + some vectorArrayConst07.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr vectorArrayEnv07 + vectorArraySizeInfo07.levelParams [] vectorArraySizeInfo07.type + vectorArraySizeConst07.type := by + tr_type_expr_tac + obtain ⟨sort, sizeType⟩ := vectorArraySizeConstWF07 + exact shape.to_trExprS vectorArrayEnv_ordered07 trivial + ⟨.sort sort, sizeType⟩ + +theorem vectorArraySizeFresh07 : + vectorArrayMap07.find? ``Array.size = none := by + rw [vectorArrayMap07, vectorEqMapWF07.find?_insert, vectorEqMap07, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem vectorInputMapWF07 : vectorInputMap07.WF := + vectorArrayMapWF07.insert _ _ vectorArraySizeFresh07 + +theorem vectorInputEnv_ordered07 : vectorInputEnv07.Ordered := + .const (n := ``Array.size) (ci := vectorArraySizeConst07.toVConstant) + vectorArrayEnv_ordered07 vectorArraySizeConstWF07 rfl + +theorem vectorInputAligned07 : + Aligned .safe vectorInputMap07 vectorInputEnv07 := + Aligned.const vectorArrayAligned07 vectorArraySizeFresh07 + vectorArraySizeInfoTr07.1 rfl vectorArraySizeInfoTr07.2 + +example : vectorInputMap07 = vectorDependencyMap07 := rfl + +/-! ### Vector -/ + +theorem vectorCheckedWF07 : vectorChecked.WF vectorInputEnv07 := by + constructor + · change vectorInputEnv07.OnTel 1 [] + [.sort (.succ (.param 0)), .const ``Nat []] + have hNat : vectorInputEnv07.constants ``Nat = + some natType.toVConstant := rfl + exact ⟨⟨.succ (.succ (.param 0)), VEnv.HasType.sort (by decide)⟩, + ⟨⟨.succ .zero, by type_tac⟩, trivial⟩⟩ + · intro ctor hctor + have hctor' := List.mem_singleton.1 hctor + subst ctor + constructor + · change VInductDecl.fieldsWF 1 ``Vector 2 vectorInputEnv07 + (.succ (.param 0)) [] + [.const ``Nat [], .sort (.succ (.param 0))] 0 + [.app (.const ``Array [.param 0]) (.bvar 1), + .app + (.app + (.app (.const ``Eq [.succ .zero]) (.const ``Nat [])) + (.app + (.app (.const ``Array.size [.param 0]) (.bvar 2)) + (.bvar 0))) + (.bvar 1)] + have hNat : vectorInputEnv07.constants ``Nat = + some natType.toVConstant := rfl + have hEq : vectorInputEnv07.constants ``Eq = + some eqType.toVConstant := rfl + have hArray : vectorInputEnv07.constants ``Array = + some vectorArrayConst07.toVConstant := rfl + have hSize : vectorInputEnv07.constants ``Array.size = + some vectorArraySizeConst07.toVConstant := rfl + constructor + · exact .inr (.inr ⟨rfl, .succ (.param 0), by type_tac, + .inr (VLevel.le_refl _)⟩) + constructor + · intro recursive + contradiction + constructor + · exact .inr (.inr ⟨rfl, .zero, by type_tac, + .inr VLevel.zero_le⟩) + constructor + · intro recursive + contradiction + · trivial + · exact .nil + +theorem vectorGenerationWF07 : + vectorGenerationChecked.WF vectorInputEnv07 := by + exact vectorCheckedWF07.identityGeneration vectorInputEnv_ordered07 + +def vectorTypeEnv07 : VEnv := + (vectorInputEnv07.addConst vectorType.name vectorType.toVConstant).get! + +def vectorCtorEnv07 : VEnv := + (vectorTypeEnv07.addConst vectorType.ctors[0].name + vectorType.ctors[0].toVConstant).get! + +def vectorRecEnv07 : VEnv := + (vectorCtorEnv07.addConst ``Vector.rec + (inductGenerationRecVal vectorGenerationChecked).toVConstant).get! + +def vectorFinalEnv07 : VEnv := + vectorGenerationChecked.generatedRules.foldl VEnv.addDefEq vectorRecEnv07 + +def vectorTypeMap07 : ConstMap := + vectorInputMap07.insert ``Vector vectorInfo07 +def vectorCtorMap07 : ConstMap := + vectorTypeMap07.insert ``Vector.mk vectorMkInfo07 +def vectorMap07 : ConstMap := + vectorCtorMap07.insert ``Vector.rec vectorRecInfo07 + +theorem vectorTypeEnv_ordered07 : vectorTypeEnv07.Ordered := + replayTypeEnv_ordered07 vectorInputEnv_ordered07 vectorGenerationWF07 rfl + +theorem vectorCtorEnv_ordered07 : vectorCtorEnv07.Ordered := + replayCtorEnv_ordered07 vectorGenerationWF07 rfl + vectorTypeEnv_ordered07 rfl + +theorem vectorGenerationEnv07 : + VInductDecl.GenerationEnv vectorGenerationChecked vectorCtorEnv07 := + replayGenerationEnv07 vectorGenerationWF07 rfl rfl + vectorCtorEnv_ordered07 + +theorem vectorInfoTr07 : + TrConstVal .safe vectorInputEnv07 vectorInfo07 + vectorType.toVConstVal := by + have hNat : vectorInputEnv07.constants ``Nat = + some natType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr vectorInputEnv07 vectorInfo07.levelParams [] + vectorInfo07.type vectorType.type := by + tr_type_expr_tac + obtain ⟨sort, familyType⟩ := replayRawFamilyWF07 vectorGenerationWF07 + exact shape.to_trExprS vectorInputEnv_ordered07 trivial + ⟨.sort sort, familyType⟩ + +theorem vectorCtorInfoTr07 : + TrConstVal .safe vectorTypeEnv07 vectorMkInfo07 vectorType.ctors[0] := by + have hNat : vectorTypeEnv07.constants ``Nat = + some natType.toVConstant := rfl + have hEq : vectorTypeEnv07.constants ``Eq = + some eqType.toVConstant := rfl + have hArray : vectorTypeEnv07.constants ``Array = + some vectorArrayConst07.toVConstant := rfl + have hSize : vectorTypeEnv07.constants ``Array.size = + some vectorArraySizeConst07.toVConstant := rfl + have hVector : vectorTypeEnv07.constants ``Vector = + some vectorType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr vectorTypeEnv07 vectorMkInfo07.levelParams [] + vectorMkInfo07.type vectorType.ctors[0].type := by + tr_type_expr_tac + obtain ⟨sort, ctorType⟩ := replayRawCtorWF07 vectorGenerationWF07 rfl + vectorType.ctors[0] (.head _) + exact shape.to_trExprS vectorTypeEnv_ordered07 trivial + ⟨.sort sort, ctorType⟩ + +theorem vectorRecInfoTr07 : + TrConstVal .safe vectorCtorEnv07 vectorRecInfo07 + (inductGenerationRecVal vectorGenerationChecked) := by + have hNat : vectorCtorEnv07.constants ``Nat = + some natType.toVConstant := rfl + have hEq : vectorCtorEnv07.constants ``Eq = + some eqType.toVConstant := rfl + have hArray : vectorCtorEnv07.constants ``Array = + some vectorArrayConst07.toVConstant := rfl + have hSize : vectorCtorEnv07.constants ``Array.size = + some vectorArraySizeConst07.toVConstant := rfl + have hVector : vectorCtorEnv07.constants ``Vector = + some vectorType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr vectorCtorEnv07 vectorRecInfo07.levelParams [] + vectorRecInfo07.type + (inductGenerationRecVal vectorGenerationChecked).type := by + tr_type_expr_tac + obtain ⟨sort, recursorType⟩ := vectorGenerationEnv07.recursor_wf + exact shape.to_trExprS vectorCtorEnv_ordered07 trivial + ⟨.sort sort, recursorType⟩ + +theorem vectorTypeFresh07 : + vectorInputMap07.find? ``Vector = none := by + rw [vectorInputMap07, vectorArrayMapWF07.find?_insert, + vectorArrayMap07, vectorEqMapWF07.find?_insert, vectorEqMap07, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem vectorTypeMapWF07 : vectorTypeMap07.WF := + vectorInputMapWF07.insert _ _ vectorTypeFresh07 + +theorem vectorCtorFresh07 : + vectorTypeMap07.find? ``Vector.mk = none := by + rw [vectorTypeMap07, vectorInputMapWF07.find?_insert, + vectorInputMap07, vectorArrayMapWF07.find?_insert, + vectorArrayMap07, vectorEqMapWF07.find?_insert, vectorEqMap07, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem vectorCtorMapWF07 : vectorCtorMap07.WF := + vectorTypeMapWF07.insert _ _ vectorCtorFresh07 + +theorem vectorRecFresh07 : + vectorCtorMap07.find? ``Vector.rec = none := by + rw [vectorCtorMap07, vectorTypeMapWF07.find?_insert, + vectorTypeMap07, vectorInputMapWF07.find?_insert, + vectorInputMap07, vectorArrayMapWF07.find?_insert, + vectorArrayMap07, vectorEqMapWF07.find?_insert, vectorEqMap07, + natTypeMap_wf.find?_insert, natTypeMap, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem vectorAddInduct07 : + AddInduct vectorInputMap07 vectorInputEnv07 vectorDecl + vectorMap07 vectorFinalEnv07 := by + refine ⟨{ + generation := vectorGenerationChecked + generation_wf := vectorGenerationWF07 + typeMap := vectorTypeMap07 + typeEnv := vectorTypeEnv07 + ctorMap := vectorCtorMap07 + ctorEnv := vectorCtorEnv07 + recEnv := vectorRecEnv07 + addType := { + info := vectorInfo07 + kind_eq := by simp [vectorInfo07, InductConstantKind.Matches] + tr := vectorInfoTr07 + map_fresh := vectorTypeFresh07 + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := vectorRecInfo07 + kind_eq := by simp [vectorRecInfo07, InductConstantKind.Matches] + tr := vectorRecInfoTr07 + map_fresh := vectorRecFresh07 + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ }⟩ + exact .cons { + info := vectorMkInfo07 + kind_eq := by simp [vectorMkInfo07, InductConstantKind.Matches] + tr := vectorCtorInfoTr07 + map_fresh := by simpa [vectorType] using vectorCtorFresh07 + env_add := rfl + map_add := rfl } .nil + +theorem vectorAligned07 : Aligned .safe vectorMap07 vectorFinalEnv07 := + Aligned.addInduct vectorAddInduct07 vectorInputAligned07 + +def vectorReplay07 : SingletonReplayArtifact where + label := ``Vector + source := vectorDecl + inputMap := vectorInputMap07 + inputEnv := vectorInputEnv07 + inputMapWF := vectorInputMapWF07 + outputMap := vectorMap07 + outputEnv := vectorFinalEnv07 + inputOrdered := vectorInputEnv_ordered07 + transaction := vectorAddInduct07 + aligned := vectorAligned07 + +/-! ### Unit/Empty edge cases -/ + +theorem punitGenerationWF07 : punitGenerationChecked.WF VEnv.empty := by + exact (punitChecked.wf_of_decl punitDecl_wf).identityGeneration .empty + +def punitTypeEnv07 : VEnv := + (VEnv.empty.addConst punitType.name punitType.toVConstant).get! + +def punitCtorEnv07 : VEnv := + (punitTypeEnv07.addConst punitType.ctors[0].name + punitType.ctors[0].toVConstant).get! + +def punitRecEnv07 : VEnv := + (punitCtorEnv07.addConst ``PUnit.rec + (inductGenerationRecVal punitGenerationChecked).toVConstant).get! + +def punitFinalEnv07 : VEnv := + punitGenerationChecked.generatedRules.foldl VEnv.addDefEq punitRecEnv07 + +def punitTypeMap07 : ConstMap := + ({} : ConstMap).insert ``PUnit punitInfo06C + +def punitCtorMap07 : ConstMap := + punitTypeMap07.insert ``PUnit.unit punitCtorInfo06C + +def punitMap07 : ConstMap := + punitCtorMap07.insert ``PUnit.rec punitRecInfo06C + +theorem punitTypeEnv_ordered07 : punitTypeEnv07.Ordered := + replayTypeEnv_ordered07 .empty punitGenerationWF07 rfl + +theorem punitCtorEnv_ordered07 : punitCtorEnv07.Ordered := + replayCtorEnv_ordered07 punitGenerationWF07 rfl + punitTypeEnv_ordered07 rfl + +theorem punitGenerationEnv07 : + VInductDecl.GenerationEnv punitGenerationChecked punitCtorEnv07 := + replayGenerationEnv07 punitGenerationWF07 rfl rfl + punitCtorEnv_ordered07 + +theorem punitInfoTr07 : + TrConstVal .safe VEnv.empty punitInfo06C punitType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + exact .sort rfl + +theorem punitCtorInfoTr07 : + TrConstVal .safe punitTypeEnv07 punitCtorInfo06C punitType.ctors[0] := by + have hPUnit : punitTypeEnv07.constants ``PUnit = + some punitType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr punitTypeEnv07 punitCtorInfo06C.levelParams [] + punitCtorInfo06C.type punitType.ctors[0].type := by + tr_type_expr_tac + exact shape.to_trExprS punitTypeEnv_ordered07 trivial + ⟨.sort (.param 0), by type_tac⟩ + +theorem punitRecInfoTr07 : + TrConstVal .safe punitCtorEnv07 punitRecInfo06C + (inductGenerationRecVal punitGenerationChecked) := by + have hPUnit : punitCtorEnv07.constants ``PUnit = + some punitType.toVConstant := rfl + have hUnit : punitCtorEnv07.constants ``PUnit.unit = + some punitType.ctors[0].toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr punitCtorEnv07 punitRecInfo06C.levelParams [] + punitRecInfo06C.type + (inductGenerationRecVal punitGenerationChecked).type := by + tr_type_expr_tac + obtain ⟨sort, recursorType⟩ := punitGenerationEnv07.recursor_wf + exact shape.to_trExprS punitCtorEnv_ordered07 trivial + ⟨.sort sort, recursorType⟩ + +theorem punitTypeFresh07 : ({} : ConstMap).find? ``PUnit = none := by + simp [SMap.find?] + +theorem punitTypeMapWF07 : punitTypeMap07.WF := + SMap.WF.empty.insert _ _ punitTypeFresh07 + +theorem punitCtorFresh07 : punitTypeMap07.find? ``PUnit.unit = none := by + rw [punitTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem punitCtorMapWF07 : punitCtorMap07.WF := + punitTypeMapWF07.insert _ _ punitCtorFresh07 + +theorem punitRecFresh07 : punitCtorMap07.find? ``PUnit.rec = none := by + rw [punitCtorMap07, punitTypeMapWF07.find?_insert, punitTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem punitAddInduct07 : AddInduct ({} : ConstMap) VEnv.empty punitDecl + punitMap07 punitFinalEnv07 := by + refine ⟨{ + generation := punitGenerationChecked + generation_wf := punitGenerationWF07 + typeMap := punitTypeMap07 + typeEnv := punitTypeEnv07 + ctorMap := punitCtorMap07 + ctorEnv := punitCtorEnv07 + recEnv := punitRecEnv07 + addType := { + info := punitInfo06C + kind_eq := by simp [punitInfo06C, InductConstantKind.Matches] + tr := punitInfoTr07 + map_fresh := punitTypeFresh07 + env_add := rfl + map_add := rfl } + addCtors := ?_ + addRec := { + info := punitRecInfo06C + kind_eq := by simp [punitRecInfo06C, InductConstantKind.Matches] + tr := punitRecInfoTr07 + map_fresh := punitRecFresh07 + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ }⟩ + exact .cons { + info := punitCtorInfo06C + kind_eq := by simp [punitCtorInfo06C, InductConstantKind.Matches] + tr := punitCtorInfoTr07 + map_fresh := by simpa [punitType] using punitCtorFresh07 + env_add := rfl + map_add := rfl } .nil + +theorem punitAligned07 : Aligned .safe punitMap07 punitFinalEnv07 := + Aligned.addInduct punitAddInduct07 .empty + +def punitReplay07 : SingletonReplayArtifact where + label := ``Unit + source := punitDecl + inputMap := {} + inputEnv := .empty + inputMapWF := SMap.WF.empty + outputMap := punitMap07 + outputEnv := punitFinalEnv07 + inputOrdered := .empty + transaction := punitAddInduct07 + aligned := punitAligned07 + +theorem emptyGenerationWF07 : emptyGenerationChecked.WF VEnv.empty := by + exact (emptyChecked.wf_of_decl emptyDecl_wf).identityGeneration .empty + +def emptyTypeEnv07 : VEnv := + (VEnv.empty.addConst emptyType.name emptyType.toVConstant).get! + +def emptyRecEnv07 : VEnv := + (emptyTypeEnv07.addConst ``Empty.rec + (inductGenerationRecVal emptyGenerationChecked).toVConstant).get! + +def emptyFinalEnv07 : VEnv := + emptyGenerationChecked.generatedRules.foldl VEnv.addDefEq emptyRecEnv07 + +def emptyTypeMap07 : ConstMap := + ({} : ConstMap).insert ``Empty emptyInfo06C + +def emptyMap07 : ConstMap := + emptyTypeMap07.insert ``Empty.rec emptyRecInfo06C + +theorem emptyTypeEnv_ordered07 : emptyTypeEnv07.Ordered := + replayTypeEnv_ordered07 .empty emptyGenerationWF07 rfl + +theorem emptyGenerationEnv07 : + VInductDecl.GenerationEnv emptyGenerationChecked emptyTypeEnv07 := + replayGenerationEnv07 emptyGenerationWF07 rfl rfl + emptyTypeEnv_ordered07 + +theorem emptyInfoTr07 : + TrConstVal .safe VEnv.empty emptyInfo06C emptyType.toVConstVal := by + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + exact .sort rfl + +theorem emptyRecInfoTr07 : + TrConstVal .safe emptyTypeEnv07 emptyRecInfo06C + (inductGenerationRecVal emptyGenerationChecked) := by + have hEmpty : emptyTypeEnv07.constants ``Empty = + some emptyType.toVConstant := rfl + refine ⟨⟨by decide, rfl, ?_⟩, rfl⟩ + have shape : TrTypeExpr emptyTypeEnv07 emptyRecInfo06C.levelParams [] + emptyRecInfo06C.type + (inductGenerationRecVal emptyGenerationChecked).type := by + tr_type_expr_tac + obtain ⟨sort, recursorType⟩ := emptyGenerationEnv07.recursor_wf + exact shape.to_trExprS emptyTypeEnv_ordered07 trivial + ⟨.sort sort, recursorType⟩ + +theorem emptyTypeFresh07 : ({} : ConstMap).find? ``Empty = none := by + simp [SMap.find?] + +theorem emptyTypeMapWF07 : emptyTypeMap07.WF := + SMap.WF.empty.insert _ _ emptyTypeFresh07 + +theorem emptyRecFresh07 : emptyTypeMap07.find? ``Empty.rec = none := by + rw [emptyTypeMap07, + SMap.WF.find?_insert (s := ({} : ConstMap)) SMap.WF.empty] + simp [SMap.find?] + +theorem emptyAddInduct07 : AddInduct ({} : ConstMap) VEnv.empty emptyDecl + emptyMap07 emptyFinalEnv07 := by + refine ⟨{ + generation := emptyGenerationChecked + generation_wf := emptyGenerationWF07 + typeMap := emptyTypeMap07 + typeEnv := emptyTypeEnv07 + ctorMap := emptyTypeMap07 + ctorEnv := emptyTypeEnv07 + recEnv := emptyRecEnv07 + addType := { + info := emptyInfo06C + kind_eq := by simp [emptyInfo06C, InductConstantKind.Matches] + tr := emptyInfoTr07 + map_fresh := emptyTypeFresh07 + env_add := rfl + map_add := rfl } + addCtors := .nil + addRec := { + info := emptyRecInfo06C + kind_eq := by simp [emptyRecInfo06C, InductConstantKind.Matches] + tr := emptyRecInfoTr07 + map_fresh := emptyRecFresh07 + env_add := rfl + map_add := rfl } + recK := by decide + addRules := ⟨rfl⟩ }⟩ + +theorem emptyAligned07 : Aligned .safe emptyMap07 emptyFinalEnv07 := + Aligned.addInduct emptyAddInduct07 .empty + +def emptyReplay07 : SingletonReplayArtifact where + label := ``Empty + source := emptyDecl + inputMap := {} + inputEnv := .empty + inputMapWF := SMap.WF.empty + outputMap := emptyMap07 + outputEnv := emptyFinalEnv07 + inputOrdered := .empty + transaction := emptyAddInduct07 + aligned := emptyAligned07 + +/-- Every fixed Spec-07 positive row, in exactly the same order as the +Theory/kernel parity matrix. Each entry carries an actual `ConstantInfo` +transaction and final environment alignment, including the real dependency +environments required by `Fin` and `Vector`. -/ +def singletonFixedReplays : List SingletonReplayArtifact := + [natReplay07, boolReplay07, listReplay07, optionReplay07, prodReplay07, + punitReplay07, emptyReplay07, orReplay07, andReplay07, eqReplay07, + heqReplay07, finReplay07, vectorReplay07, accReplay07] + +/-- The focused non-identity normalization rows use the same public replay +artifact as the standard-library matrix. -/ +def singletonNormalizationReplays : + List SingletonReplayArtifact := + [aliasFormerReplay07, aliasRecReplay07, normalizationMatrixReplay07, + annotatedPiReplay07, annotatedParamReplay07] + +/-- The sole public Spec-07 environment replay inventory. -/ +def singletonReplayMatrix : List SingletonReplayArtifact := + singletonFixedReplays ++ singletonNormalizationReplays + +example : singletonFixedReplays.map (·.label) = + singletonPositiveArtifacts.map (·.label) := rfl + +example : singletonFixedReplays.map (·.source) = + singletonPositiveArtifacts.map (·.source) := rfl + +example : singletonNormalizationReplays.map (·.label) = + singletonNormalizationArtifacts.map (·.label) := rfl + +example : singletonNormalizationReplays.map (·.source) = + singletonNormalizationArtifacts.map (·.source) := rfl + +example : singletonReplayMatrix.map (·.source) = + (singletonPositiveArtifacts ++ singletonNormalizationArtifacts).map + (·.source) := rfl + +example : singletonFixedReplays.length = 14 := rfl +example : singletonNormalizationReplays.length = 5 := rfl +example : singletonReplayMatrix.length = 19 := rfl + +/-! ## Exact trust manifests for the public replay seam -/ + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.SingletonReplayArtifact.outputOrdered [propext, Classical.choice, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.singletonFixedReplays [ + propext, + Classical.choice, + Quot.sound, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.singletonNormalizationReplays [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +#guard_named_axioms Ix.Theory.Named.InductiveReplayFixtures.singletonReplayMatrix [ + propext, + sorryAx, + Classical.choice, + Ix.Theory.Named.ptrEqConstantInfo_eq, + Quot.sound, + Lean.Expr.abstractRange_eq, + Lean.Expr.abstract_eq, + Lean.Expr.eqv_eq, + Lean.Expr.hasLooseBVar_eq, + Lean.Expr.instantiate1_eq, + Lean.Expr.instantiateRange_eq, + Lean.Expr.instantiateRevRange_eq, + Lean.Expr.instantiateRev_eq, + Lean.Expr.instantiate_eq, + Lean.Expr.looseBVarRange_eq, + Lean.Expr.lowerLooseBVars_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.Level.isExplicitSubsumedAux_eq, + Lean.Level.normalize_eq, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.Syntax.structEq_eq, + Std.TreeMap.all_eq_all_toList, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] + +end Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Expr.lean b/Ix/Theory/Named/Verify/Expr.lean new file mode 100644 index 000000000..fba52e69d --- /dev/null +++ b/Ix/Theory/Named/Verify/Expr.lean @@ -0,0 +1,1303 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.Basic +import Ix.Theory.Named.Verify.Axioms +import Ix.Theory.Named.Verify.Level +import Ix.Theory.Named.Reference.Expr +import Ix.Theory.Named.Reference.Instantiate +import Batteries.Data.String.Lemmas +import Std.Tactic.BVDecide + +open Ix.Theory.Named + +namespace Lean + +instance : LawfulBEq FVarId where + eq_of_beq := @fun ⟨a⟩ ⟨b⟩ h => by cases LawfulBEq.eq_of_beq (α := Name) h; rfl + rfl := BEq.rfl (α := Name) + +instance : LawfulBEq MVarId where + eq_of_beq := @fun ⟨a⟩ ⟨b⟩ h => by cases LawfulBEq.eq_of_beq (α := Name) h; rfl + rfl := BEq.rfl (α := Name) + +instance : LawfulBEq LBool where + rfl {a} := by cases a <;> rfl + eq_of_beq {a b} := by cases a <;> cases b <;> simp +decide + +instance : LawfulBEq DefinitionSafety where + rfl {a} := by cases a <;> rfl + eq_of_beq {a b} := by cases a <;> cases b <;> simp +decide + +-- we have to define this manually instead of `deriving instance Ord` +-- because the variants are written in the wrong order in core +protected def DefinitionSafety.compare : DefinitionSafety → DefinitionSafety → Ordering + | .unsafe, .unsafe => .eq + | .unsafe, _ => .lt + | _, .unsafe => .gt + | .partial, .partial => .eq + | .partial, _ => .lt + | _, .partial => .gt + | .safe, .safe => .eq + +instance : Ord DefinitionSafety := ⟨DefinitionSafety.compare⟩ + +instance : Std.TransOrd DefinitionSafety where + eq_swap {a b} := by cases a <;> cases b <;> rfl + isLE_trans {a b c} := by cases a <;> cases b <;> simp! [compare] <;> cases c <;> decide + +instance : Std.LawfulBEqOrd DefinitionSafety where + compare_eq_iff_beq {a b} := by cases a <;> cases b <;> decide +instance : Std.LawfulEqOrd DefinitionSafety := Std.LawfulBEqOrd.lawfulEqOrd + +instance : LE DefinitionSafety := leOfOrd +instance : DecidableEq DefinitionSafety := instDecidableEqOfLawfulBEq + +theorem DefinitionSafety.le_trans {a b c : DefinitionSafety} : a ≤ b → b ≤ c → a ≤ c := + Std.TransOrd.isLE_trans + +theorem DefinitionSafety.le_rfl {a : DefinitionSafety} : a ≤ a := Std.ReflCmp.isLE_rfl + +theorem DefinitionSafety.unsafe_le : «unsafe» ≤ a := by cases a <;> rfl +theorem DefinitionSafety.le_safe : a ≤ safe := by cases a <;> rfl +theorem DefinitionSafety.le_antisymm {a b : DefinitionSafety} : a ≤ b → b ≤ a → a = b := by + cases a <;> cases b <;> decide + +namespace Substring + +open private substrEq.loop from Init.Data.String.Basic in +theorem beq_refl (s : Substring.Raw) : s == s := by + simp [(· == ·), Substring.Raw.beq, Substring.Raw.bsize, String.Pos.Raw.substrEq] + let rec loop {s i n} : substrEq.loop s s i i n := by + unfold substrEq.loop; simp [Decidable.or_iff_not_imp_left] + intro h1 + have := Char.utf8Size_pos (String.Pos.Raw.get s i) + exact loop + termination_by n.byteIdx - i.byteIdx + refine ⟨?_, loop⟩ + obtain h | h := Nat.le_or_le s.repair.startPos.byteIdx s.repair.stopPos.byteIdx + · simp only [Nat.add_sub_cancel' h, decide_eq_true_eq] + apply String.Pos.Raw.IsValid.le_rawEndPos + simp [Substring.Raw.repair]; split <;> simp [*] + · simp [Nat.sub_eq_zero_of_le h] + apply String.Pos.Raw.IsValid.le_rawEndPos + simp [Substring.Raw.repair]; split <;> simp [*] + +open private substrEq.loop from Init.Data.String.Basic in +theorem beq_symm {s t : Substring.Raw} : s == t → t == s := by + simp +contextual [(· == ·), Substring.Raw.beq, String.Pos.Raw.substrEq] + simp [Substring.Raw.bsize] + let rec loop {s s' b b' i n} : + substrEq.loop s s' ⟨b + i⟩ ⟨b' + i⟩ ⟨b + n⟩ ↔ + substrEq.loop s' s ⟨b' + i⟩ ⟨b + i⟩ ⟨b' + n⟩ := by + unfold substrEq.loop; simp [beq_comm, Decidable.or_iff_not_imp_left] + refine imp_congr_right fun h1 => and_congr_right fun h2 => ?_ + simp +instances [h2, String.instHAddRawChar, Nat.add_assoc] + have := Char.utf8Size_pos (String.Pos.Raw.get s' ⟨b'+i⟩) + exact Bool.eq_iff_iff.2 loop + termination_by b + n - (b + i) + intro h1 h2 h3 + have loop := @loop (i := 0); simp at loop + simp [loop] + +open private substrEq.loop from Init.Data.String.Basic in +theorem beq_trans {s t : Substring.Raw} : s == t → t == u → s == u := by + simp +contextual [(· == ·), Substring.Raw.beq, String.Pos.Raw.substrEq] + simp [Substring.Raw.bsize] + let ⟨s, ⟨b⟩, e⟩ := s.repair + let ⟨s2, ⟨b2⟩, e2⟩ := t.repair + let ⟨s3, ⟨b3⟩, e3⟩ := u.repair + intro h1 h2 h3 h4 h5 h6 h7 h8 + constructor; · omega + simp [h5] at h4 + let rec loop {s₁ s₂ s₃ b₁ b₂ b₃ i n} : + (h : substrEq.loop s₁ s₂ ⟨b₁ + i⟩ ⟨b₂ + i⟩ ⟨b₁ + n⟩) → + (substrEq.loop s₁ s₃ ⟨b₁ + i⟩ ⟨b₃ + i⟩ ⟨b₁ + n⟩ ↔ + substrEq.loop s₂ s₃ ⟨b₂ + i⟩ ⟨b₃ + i⟩ ⟨b₂ + n⟩) := by + unfold substrEq.loop; simp [Decidable.or_iff_not_imp_left] + refine fun h1 => imp_congr_right fun h => ?_; let ⟨h1, h2⟩ := h1 h + simp [h1]; intro h3; simp +instances [h1, h3, String.instHAddRawChar, Nat.add_assoc] at h2 ⊢ + have := Char.utf8Size_pos (String.Pos.Raw.get s₃ ⟨b₃+i⟩) + refine Bool.eq_iff_iff.2 (loop h2) + termination_by n - i + have loop := @loop (i := 0) (h := h4); simp at loop + simpa [loop] using h8 + +instance : EquivBEq Substring.Raw where + symm := beq_symm + trans := beq_trans + rfl := beq_refl _ + +end Substring + +namespace Syntax + +namespace Preresolved + +protected theorem beq_iff_eq {m n : Preresolved} : m == n ↔ m = n := by + simp only [(· == ·)]; cases m <;> cases n <;> simp! <;> grind only [cases Or] + +instance : LawfulBEq Preresolved where + eq_of_beq := Preresolved.beq_iff_eq.1 + rfl := Preresolved.beq_iff_eq.2 rfl + +end Preresolved + +theorem structEq_refl (s : Syntax) : s.structEq' s := by + unfold structEq' + cases s with + | node i k args => + let ⟨args⟩ := args; simp; intro x _ _ _ + have IH := structEq_refl x + induction args.attach <;> simp + rintro (⟨rfl, ⟨⟩⟩ | h) <;> [skip; solve_by_elim] + simpa using IH + | _ => simp + +attribute [-simp] List.all_eq_true in +theorem structEq_euc {s : Syntax} (H : s.structEq' t) : s.structEq' u = t.structEq' u := by + unfold structEq' at H; split at H + case h_1 => simp + case h_5 => cases H + all_goals + revert H; simp; intros; subst_vars + unfold structEq'; cases u <;> simp <;> rw [Bool.eq_iff_iff] <;> simp [*] + -- all_goals intros; subst_vars + case ident => intros; apply BEq.congr_left ‹_› + case node args₁ _ _ args₂ eq H _ _ args₃ => + rintro rfl eq'; revert H + have IH {a} (h : a ∈ args₁) := @structEq_euc (s := a) + let ⟨args₁⟩ := args₁; let ⟨args₂⟩ := args₂; let ⟨args₃⟩ := args₃ + revert eq eq'; simp at IH ⊢ + simp only [← args₁.length_attach, ← args₂.length_attach, ← args₃.length_attach] + generalize args₁.attach = l₁, args₂.attach = l₂, args₃.attach = l₃ + induction l₁ generalizing l₂ l₃ with cases l₂ <;> simp <;> cases l₃ <;> simp | cons a l₁ ih + intros _ _ H; rw [IH a.2 _ _ H, Bool.eq_iff_iff]; simp [*]; grind +termination_by s + +theorem structEq_symm {s : Syntax} (H : s.structEq' t) : t.structEq' s := + structEq_euc H ▸ structEq_refl _ + +theorem structEq_trans {s : Syntax} (H : s.structEq' t) (H2 : t.structEq' u) : s.structEq' u := + structEq_euc H ▸ H2 + +theorem beq_def : (a == b) = structEq' a b := structEq_eq + +instance : EquivBEq Syntax where + symm := by simp [beq_def]; exact structEq_symm + trans := by simp [beq_def]; exact structEq_trans + rfl := by simp [beq_def]; exact structEq_refl _ + +end Syntax + +namespace DataValue + +instance : EquivBEq DataValue where + symm {a b} := by simp [(· == ·)]; cases a <;> cases b <;> simp! [beq_comm] + trans {a b c} := by + simp [(· == ·)]; cases a <;> cases b <;> simp! [beq_comm] <;> try rintro rfl; simp + cases c <;> simp!; exact BEq.trans + rfl {a} := by simp [(· == ·)]; cases a <;> simp! + +end DataValue + +namespace Expr + +theorem Data.looseBVarRange_le : + (Data.looseBVarRange d).toNat ≤ 2 ^ 20 - 1 := by + rw [Data.looseBVarRange] + suffices (UInt64.shiftRight d 44).toNat ≤ 2 ^ 20 - 1 by simp; omega + show d.toBitVec >>> 44#64 ≤ 0xfffff#64 + rw [BitVec.le_def, BitVec.ushiftRight_ofNat_eq] + simp + have h := BitVec.toNat_ushiftRight_lt d.toBitVec 44 (by omega) + simp at h ⊢ + omega + +private theorem Data.flag_eq_getLsbD (d : Data) (n : UInt64) (hn : n < 64) : + ((d.shiftRight n).land 1 == 1) = d.toBitVec.getLsbD n.toNat := by + apply Bool.eq_iff_iff.2 + simp only [beq_iff_eq] + have hmod : UInt64.mod n 64 = n := UInt64.mod_eq_of_lt hn + constructor + · intro h + have hb := congrArg UInt64.toBitVec h + simp [UInt64.shiftRight, UInt64.land, hmod] at hb + have := congrArg (fun x : BitVec 64 => x.getLsbD 0) hb + simpa using this + · intro h + apply UInt64.toBitVec_inj.mp + simp [UInt64.shiftRight, UInt64.land, hmod] + ext i + by_cases hi : i = 0 + · subst i + simpa using h + · simp [hi] + +private theorem Data.hasFVar_eq_getLsbD (d : Data) : + d.hasFVar = d.toBitVec.getLsbD 40 := by + simpa [Data.hasFVar] using Data.flag_eq_getLsbD d 40 (by decide) + +private theorem Data.hasExprMVar_eq_getLsbD (d : Data) : + d.hasExprMVar = d.toBitVec.getLsbD 41 := by + simpa [Data.hasExprMVar] using Data.flag_eq_getLsbD d 41 (by decide) + +private theorem Data.hasLevelMVar_eq_getLsbD (d : Data) : + d.hasLevelMVar = d.toBitVec.getLsbD 42 := by + simpa [Data.hasLevelMVar] using Data.flag_eq_getLsbD d 42 (by decide) + +private theorem Data.hasLevelParam_eq_getLsbD (d : Data) : + d.hasLevelParam = d.toBitVec.getLsbD 43 := by + simpa [Data.hasLevelParam] using Data.flag_eq_getLsbD d 43 (by decide) + +private def flagAt (fv ev lv lp : Bool) : Nat → Bool + | 0 => fv + | 1 => ev + | 2 => lv + | 3 => lp + | _ => false + +private theorem BitVec.getLsbD_eq_false_of_toNat_lt_two_pow + {x : BitVec w} (h : x.toNat < 2 ^ n) (hn : n ≤ i) : + x.getLsbD i = false := by + rw [BitVec.getLsbD, Nat.testBit_lt_two_pow] + exact Nat.lt_of_lt_of_le h (Nat.pow_le_pow_right (by decide) hn) + +private theorem BitVec.add_shiftLeft_eq_or_of_toNat_lt_two_pow + {x y : BitVec w} (h : x.toNat < 2 ^ n) : + x + (y <<< n) = x ||| (y <<< n) := by + rw [BitVec.add_eq_or_of_and_eq_zero] + ext i hi + simp only [BitVec.getElem_and, BitVec.getElem_zero] + by_cases hin : i < n + · simp [BitVec.getElem_shiftLeft, hin] + · have hx : x[i] = false := by + rw [← BitVec.getLsbD_eq_getElem] + exact BitVec.getLsbD_eq_false_of_toNat_lt_two_pow h (Nat.le_of_not_gt hin) + simp [hx] + +set_option allowUnsafeReducibility true +attribute [local reducible] Data + +private theorem mkData_flags (H : br ≤ 2 ^ 20 - 1) : + (mkData h br d fv ev lv lp).hasFVar = fv ∧ + (mkData h br d fv ev lv lp).hasExprMVar = ev ∧ + (mkData h br d fv ev lv lp).hasLevelMVar = lv ∧ + (mkData h br d fv ev lv lp).hasLevelParam = lp := by + rw [mkData_eq, mkData', if_pos H] + rw [Data.hasFVar_eq_getLsbD, Data.hasExprMVar_eq_getLsbD, + Data.hasLevelMVar_eq_getLsbD, Data.hasLevelParam_eq_getLsbD] + have hh : h.toUInt32.toUInt64.toBitVec ≤ 0xffffffff#64 := + Nat.le_of_lt_succ h.toUInt32.1.1.2 + have hb : ∀ (b : Bool), b.toUInt64.toBitVec ≤ 1#64 := by decide + have hfv := hb fv + have hev := hb ev + have hlv := hb lv + have hlp := hb lp + have hnat : br.toUInt64.toNat = br := by simp; omega + have hbr : br.toUInt64.toBitVec ≤ 0xfffff#64 := (hnat ▸ H :) + let depth : UInt8 := if d > 255 then 255 else d.toUInt8 + have hd : depth.toUInt64.toBitVec ≤ 0xff#64 := Nat.le_of_lt_succ depth.1.1.2 + let data := + h.toUInt32.toUInt64.toBitVec + + depth.toUInt64.toBitVec <<< 32#64 + + fv.toUInt64.toBitVec <<< 40#64 + + ev.toUInt64.toBitVec <<< 41#64 + + lv.toUInt64.toBitVec <<< 42#64 + + lp.toUInt64.toBitVec <<< 43#64 + + br.toUInt64.toBitVec <<< 44#64 + change data.getLsbD 40 = fv ∧ data.getLsbD 41 = ev ∧ + data.getLsbD 42 = lv ∧ data.getLsbD 43 = lp + have hh' : h.toUInt32.toUInt64.toBitVec.toNat < 2 ^ 32 := by + simpa using h.toUInt32.1.1.2 + have hd' : depth.toUInt64.toBitVec.toNat < 2 ^ 8 := by + simpa using depth.1.1.2 + have hb' : ∀ (b : Bool), b.toUInt64.toBitVec.toNat < 2 := by + intro b + cases b <;> decide + have h0 := BitVec.add_shiftLeft_eq_or_of_toNat_lt_two_pow + (x := h.toUInt32.toUInt64.toBitVec) (y := depth.toUInt64.toBitVec) hh' + have hs0 : (depth.toUInt64.toBitVec <<< 32).toNat < 2 ^ 40 := by + rw [BitVec.toNat_shiftLeft, Nat.shiftLeft_eq, Nat.mod_eq_of_lt] + · omega + · omega + have hp0 : + (h.toUInt32.toUInt64.toBitVec ||| depth.toUInt64.toBitVec <<< 32).toNat < + 2 ^ 40 := by + rw [BitVec.toNat_or] + exact Nat.or_lt_two_pow (by omega) hs0 + have h1 := BitVec.add_shiftLeft_eq_or_of_toNat_lt_two_pow + (x := h.toUInt32.toUInt64.toBitVec ||| depth.toUInt64.toBitVec <<< 32) + (y := fv.toUInt64.toBitVec) hp0 + have hs1 : (fv.toUInt64.toBitVec <<< 40).toNat < 2 ^ 41 := by + rw [BitVec.toNat_shiftLeft, Nat.shiftLeft_eq, Nat.mod_eq_of_lt] + · have := hb' fv; omega + · have := hb' fv; omega + have hp1 : + (h.toUInt32.toUInt64.toBitVec ||| depth.toUInt64.toBitVec <<< 32 ||| + fv.toUInt64.toBitVec <<< 40).toNat < 2 ^ 41 := by + rw [BitVec.toNat_or] + exact Nat.or_lt_two_pow (by omega) hs1 + have h2 := BitVec.add_shiftLeft_eq_or_of_toNat_lt_two_pow + (x := h.toUInt32.toUInt64.toBitVec ||| depth.toUInt64.toBitVec <<< 32 ||| + fv.toUInt64.toBitVec <<< 40) + (y := ev.toUInt64.toBitVec) hp1 + have hs2 : (ev.toUInt64.toBitVec <<< 41).toNat < 2 ^ 42 := by + rw [BitVec.toNat_shiftLeft, Nat.shiftLeft_eq, Nat.mod_eq_of_lt] + · have := hb' ev; omega + · have := hb' ev; omega + have hp2 : + (h.toUInt32.toUInt64.toBitVec ||| depth.toUInt64.toBitVec <<< 32 ||| + fv.toUInt64.toBitVec <<< 40 ||| ev.toUInt64.toBitVec <<< 41).toNat < + 2 ^ 42 := by + rw [BitVec.toNat_or] + exact Nat.or_lt_two_pow (by omega) hs2 + have h3 := BitVec.add_shiftLeft_eq_or_of_toNat_lt_two_pow + (x := h.toUInt32.toUInt64.toBitVec ||| depth.toUInt64.toBitVec <<< 32 ||| + fv.toUInt64.toBitVec <<< 40 ||| ev.toUInt64.toBitVec <<< 41) + (y := lv.toUInt64.toBitVec) hp2 + have hs3 : (lv.toUInt64.toBitVec <<< 42).toNat < 2 ^ 43 := by + rw [BitVec.toNat_shiftLeft, Nat.shiftLeft_eq, Nat.mod_eq_of_lt] + · have := hb' lv; omega + · have := hb' lv; omega + have hp3 : + (h.toUInt32.toUInt64.toBitVec ||| depth.toUInt64.toBitVec <<< 32 ||| + fv.toUInt64.toBitVec <<< 40 ||| ev.toUInt64.toBitVec <<< 41 ||| + lv.toUInt64.toBitVec <<< 42).toNat < 2 ^ 43 := by + rw [BitVec.toNat_or] + exact Nat.or_lt_two_pow (by omega) hs3 + have h4 := BitVec.add_shiftLeft_eq_or_of_toNat_lt_two_pow + (x := h.toUInt32.toUInt64.toBitVec ||| depth.toUInt64.toBitVec <<< 32 ||| + fv.toUInt64.toBitVec <<< 40 ||| ev.toUInt64.toBitVec <<< 41 ||| + lv.toUInt64.toBitVec <<< 42) + (y := lp.toUInt64.toBitVec) hp3 + have hs4 : (lp.toUInt64.toBitVec <<< 43).toNat < 2 ^ 44 := by + rw [BitVec.toNat_shiftLeft, Nat.shiftLeft_eq, Nat.mod_eq_of_lt] + · have := hb' lp; omega + · have := hb' lp; omega + have hp4 : + (h.toUInt32.toUInt64.toBitVec ||| depth.toUInt64.toBitVec <<< 32 ||| + fv.toUInt64.toBitVec <<< 40 ||| ev.toUInt64.toBitVec <<< 41 ||| + lv.toUInt64.toBitVec <<< 42 ||| lp.toUInt64.toBitVec <<< 43).toNat < + 2 ^ 44 := by + rw [BitVec.toNat_or] + exact Nat.or_lt_two_pow (by omega) hs4 + have h5 := BitVec.add_shiftLeft_eq_or_of_toNat_lt_two_pow + (x := h.toUInt32.toUInt64.toBitVec ||| depth.toUInt64.toBitVec <<< 32 ||| + fv.toUInt64.toBitVec <<< 40 ||| ev.toUInt64.toBitVec <<< 41 ||| + lv.toUInt64.toBitVec <<< 42 ||| lp.toUInt64.toBitVec <<< 43) + (y := br.toUInt64.toBitVec) hp4 + have hdata : data = + h.toUInt32.toUInt64.toBitVec ||| depth.toUInt64.toBitVec <<< 32 ||| + fv.toUInt64.toBitVec <<< 40 ||| ev.toUInt64.toBitVec <<< 41 ||| + lv.toUInt64.toBitVec <<< 42 ||| lp.toUInt64.toBitVec <<< 43 ||| + br.toUInt64.toBitVec <<< 44 := by + dsimp [data] + rw [h0, h1, h2, h3, h4, h5] + have hhbit : ∀ i, 32 ≤ i → h.toUInt32.toUInt64.toBitVec.getLsbD i = false := + fun _ hi => BitVec.getLsbD_eq_false_of_toNat_lt_two_pow hh' hi + have hdbit : ∀ i, 8 ≤ i → depth.toUInt64.toBitVec.getLsbD i = false := + fun _ hi => BitVec.getLsbD_eq_false_of_toNat_lt_two_pow hd' hi + have hbbit : ∀ (b : Bool) i, 1 ≤ i → b.toUInt64.toBitVec.getLsbD i = false := + fun b _ hi => BitVec.getLsbD_eq_false_of_toNat_lt_two_pow (hb' b) hi + rw [hdata] + simp only [BitVec.getLsbD_or, BitVec.getLsbD_shiftLeft] + simp only [Nat.reduceLT, decide_true, decide_false, Bool.not_true, Bool.not_false, + Bool.true_and, Bool.false_and, Bool.or_false, Nat.reduceSub] + rw [hhbit 40 (by omega), hhbit 41 (by omega), hhbit 42 (by omega), + hhbit 43 (by omega), hdbit 8 (by omega), hdbit 9 (by omega), + hdbit 10 (by omega), hdbit 11 (by omega), hbbit fv 1 (by omega), + hbbit fv 2 (by omega), hbbit fv 3 (by omega), hbbit ev 1 (by omega), + hbbit ev 2 (by omega), hbbit lv 1 (by omega)] + simp only [Bool.false_or] + cases fv <;> cases ev <;> cases lv <;> cases lp <;> decide + +private theorem mkData_hasFVar (H : br ≤ 2 ^ 20 - 1) : + (mkData h br d fv ev lv lp).hasFVar = fv := by + exact (mkData_flags H).1 + +private theorem mkData_hasExprMVar (H : br ≤ 2 ^ 20 - 1) : + (mkData h br d fv ev lv lp).hasExprMVar = ev := by + exact (mkData_flags H).2.1 + +private theorem mkData_hasLevelMVar (H : br ≤ 2 ^ 20 - 1) : + (mkData h br d fv ev lv lp).hasLevelMVar = lv := by + exact (mkData_flags H).2.2.1 + +private theorem mkData_hasLevelParam (H : br ≤ 2 ^ 20 - 1) : + (mkData h br d fv ev lv lp).hasLevelParam = lp := by + exact (mkData_flags H).2.2.2 + +private theorem mkData_flags_of_false (br d h) : + (mkData h br d false false false false).hasFVar = false ∧ + (mkData h br d false false false false).hasExprMVar = false ∧ + (mkData h br d false false false false).hasLevelMVar = false ∧ + (mkData h br d false false false false).hasLevelParam = false := by + by_cases H : br ≤ 2 ^ 20 - 1 + · exact mkData_flags H + · rw [mkData_eq, mkData', if_neg H] + exact ⟨rfl, rfl, rfl, rfl⟩ + +private theorem mkData_hasFVar_of_false (br d h) : + (mkData h br d false false false false).hasFVar = false := + (mkData_flags_of_false br d h).1 + +private theorem mkData_hasExprMVar_of_false (br d h) : + (mkData h br d false false false false).hasExprMVar = false := + (mkData_flags_of_false br d h).2.1 + +private theorem mkData_hasLevelMVar_of_false (br d h) : + (mkData h br d false false false false).hasLevelMVar = false := + (mkData_flags_of_false br d h).2.2.1 + +private theorem mkData_hasLevelParam_of_false (br d h) : + (mkData h br d false false false false).hasLevelParam = false := + (mkData_flags_of_false br d h).2.2.2 + +private theorem mkAppData_flag (i : Nat) (hi : i < 4) : + (mkAppData fData aData).toBitVec.getLsbD (40 + i) = flagAt + (fData.hasFVar || aData.hasFVar) + (fData.hasExprMVar || aData.hasExprMVar) + (fData.hasLevelMVar || aData.hasLevelMVar) + (fData.hasLevelParam || aData.hasLevelParam) i := by + have hm : max fData.looseBVarRange aData.looseBVarRange ≤ + (Nat.pow 2 20 - 1).toUInt32 := by + dsimp +instances [instMaxUInt32, maxOfLe] + split <;> exact Data.looseBVarRange_le + rw [mkAppData_eq, mkAppData', if_pos hm] + generalize (mixHash fData aData).toUInt32 = hash + have : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 := by omega + rcases this with rfl | rfl | rfl | rfl + · simp [flagAt, Data.hasFVar_eq_getLsbD] + · simp [flagAt, Data.hasExprMVar_eq_getLsbD] + · simp [flagAt, Data.hasLevelMVar_eq_getLsbD] + · simp [flagAt, Data.hasLevelParam_eq_getLsbD] + +private theorem mkAppData_hasFVar : + (mkAppData fData aData).hasFVar = (fData.hasFVar || aData.hasFVar) := by + rw [Data.hasFVar_eq_getLsbD] + exact mkAppData_flag 0 (by decide) + +private theorem mkAppData_hasExprMVar : + (mkAppData fData aData).hasExprMVar = (fData.hasExprMVar || aData.hasExprMVar) := by + rw [Data.hasExprMVar_eq_getLsbD] + exact mkAppData_flag 1 (by decide) + +private theorem mkAppData_hasLevelMVar : + (mkAppData fData aData).hasLevelMVar = (fData.hasLevelMVar || aData.hasLevelMVar) := by + rw [Data.hasLevelMVar_eq_getLsbD] + exact mkAppData_flag 2 (by decide) + +private theorem mkAppData_hasLevelParam : + (mkAppData fData aData).hasLevelParam = (fData.hasLevelParam || aData.hasLevelParam) := by + rw [Data.hasLevelParam_eq_getLsbD] + exact mkAppData_flag 3 (by decide) + +private theorem binder_looseBVarRange_le (ty body : Expr) : + max ty.data.looseBVarRange.toNat (body.data.looseBVarRange.toNat - 1) ≤ 2 ^ 20 - 1 := by + have hty := Data.looseBVarRange_le (d := ty.data) + have hbody := Data.looseBVarRange_le (d := body.data) + omega + +private theorem let_looseBVarRange_le (ty val body : Expr) : + max (max ty.data.looseBVarRange.toNat val.data.looseBVarRange.toNat) + (body.data.looseBVarRange.toNat - 1) ≤ 2 ^ 20 - 1 := by + have hty := Data.looseBVarRange_le (d := ty.data) + have hval := Data.looseBVarRange_le (d := val.data) + have hbody := Data.looseBVarRange_le (d := body.data) + omega + +def hasFVar' : Expr → Bool + | .fvar _ => true + | .const .. + | .bvar _ + | .sort _ + | .mvar _ + | .lit _ => false + | .mdata _ e => e.hasFVar' + | .proj _ _ e => e.hasFVar' + | .app e1 e2 + | .lam _ e1 e2 _ + | .forallE _ e1 e2 _ => e1.hasFVar' || e2.hasFVar' + | .letE _ t v b _ => t.hasFVar' || v.hasFVar' || b.hasFVar' + +/-- The cached `hasFVar` bit agrees with structural traversal. -/ +theorem hasFVar_eq (e : Expr) : e.hasFVar = e.hasFVar' := by + change e.data.hasFVar = e.hasFVar' + induction e with + | bvar => simp [Expr.data, hasFVar', mkData_hasFVar_of_false] + | fvar | mvar | sort | const | lit => + simp only [Expr.data, hasFVar'] + apply mkData_hasFVar + omega + | app _ _ ih1 ih2 => + simp only [Expr.data, hasFVar'] + rw [mkAppData_hasFVar, ih1, ih2] + | lam _ ty body _ ihty ihbody | forallE _ ty body _ ihty ihbody => + simp only [Expr.data, mkDataForBinder, hasFVar'] + rw [mkData_hasFVar (binder_looseBVarRange_le ty body), ihty, ihbody] + | letE _ ty val body _ ihty ihval ihbody => + simp only [Expr.data, mkDataForLet, hasFVar'] + rw [mkData_hasFVar (let_looseBVarRange_le ty val body), ihty, ihval, ihbody] + | mdata _ e ih | proj _ _ e ih => + simp only [Expr.data, hasFVar'] + rw [mkData_hasFVar (Data.looseBVarRange_le (d := e.data)), ih] + +def hasExprMVar' : Expr → Bool + | .mvar _ => true + | .const .. + | .bvar _ + | .sort _ + | .fvar _ + | .lit _ => false + | .mdata _ e => e.hasExprMVar' + | .proj _ _ e => e.hasExprMVar' + | .app e1 e2 + | .lam _ e1 e2 _ + | .forallE _ e1 e2 _ => e1.hasExprMVar' || e2.hasExprMVar' + | .letE _ t v b _ => t.hasExprMVar' || v.hasExprMVar' || b.hasExprMVar' + +/-- The cached `hasExprMVar` bit agrees with structural traversal. -/ +@[simp] theorem hasExprMVar_eq (e : Expr) : e.hasExprMVar = e.hasExprMVar' := by + change e.data.hasExprMVar = e.hasExprMVar' + induction e with + | bvar => simp [Expr.data, hasExprMVar', mkData_hasExprMVar_of_false] + | fvar | mvar | sort | const | lit => + simp only [Expr.data, hasExprMVar'] + apply mkData_hasExprMVar + omega + | app _ _ ih1 ih2 => + simp only [Expr.data, hasExprMVar'] + rw [mkAppData_hasExprMVar, ih1, ih2] + | lam _ ty body _ ihty ihbody | forallE _ ty body _ ihty ihbody => + simp only [Expr.data, mkDataForBinder, hasExprMVar'] + rw [mkData_hasExprMVar (binder_looseBVarRange_le ty body), ihty, ihbody] + | letE _ ty val body _ ihty ihval ihbody => + simp only [Expr.data, mkDataForLet, hasExprMVar'] + rw [mkData_hasExprMVar (let_looseBVarRange_le ty val body), ihty, ihval, ihbody] + | mdata _ e ih | proj _ _ e ih => + simp only [Expr.data, hasExprMVar'] + rw [mkData_hasExprMVar (Data.looseBVarRange_le (d := e.data)), ih] + +def hasLevelMVar' : Expr → Bool + | .const _ ls => ls.any (·.hasMVar) + | .sort u => u.hasMVar + | .bvar _ + | .fvar _ + | .mvar _ + | .lit _ => false + | .mdata _ e => e.hasLevelMVar' + | .proj _ _ e => e.hasLevelMVar' + | .app e1 e2 + | .lam _ e1 e2 _ + | .forallE _ e1 e2 _ => e1.hasLevelMVar' || e2.hasLevelMVar' + | .letE _ t v b _ => t.hasLevelMVar' || v.hasLevelMVar' || b.hasLevelMVar' + +/-- The cached `hasLevelMVar` bit agrees with structural traversal. -/ +@[simp] theorem hasLevelMVar_eq (e : Expr) : e.hasLevelMVar = e.hasLevelMVar' := by + change e.data.hasLevelMVar = e.hasLevelMVar' + induction e with + | bvar => simp [Expr.data, hasLevelMVar', mkData_hasLevelMVar_of_false] + | fvar | mvar | sort | const | lit => + simp only [Expr.data, hasLevelMVar'] + apply mkData_hasLevelMVar + omega + | app _ _ ih1 ih2 => + simp only [Expr.data, hasLevelMVar'] + rw [mkAppData_hasLevelMVar, ih1, ih2] + | lam _ ty body _ ihty ihbody | forallE _ ty body _ ihty ihbody => + simp only [Expr.data, mkDataForBinder, hasLevelMVar'] + rw [mkData_hasLevelMVar (binder_looseBVarRange_le ty body), ihty, ihbody] + | letE _ ty val body _ ihty ihval ihbody => + simp only [Expr.data, mkDataForLet, hasLevelMVar'] + rw [mkData_hasLevelMVar (let_looseBVarRange_le ty val body), ihty, ihval, ihbody] + | mdata _ e ih | proj _ _ e ih => + simp only [Expr.data, hasLevelMVar'] + rw [mkData_hasLevelMVar (Data.looseBVarRange_le (d := e.data)), ih] + +def hasLevelParam' : Expr → Bool + | .const _ ls => ls.any (·.hasParam) + | .sort u => u.hasParam + | .bvar _ + | .fvar _ + | .mvar _ + | .lit _ => false + | .mdata _ e => e.hasLevelParam' + | .proj _ _ e => e.hasLevelParam' + | .app e1 e2 + | .lam _ e1 e2 _ + | .forallE _ e1 e2 _ => e1.hasLevelParam' || e2.hasLevelParam' + | .letE _ t v b _ => t.hasLevelParam' || v.hasLevelParam' || b.hasLevelParam' + +/-- The cached `hasLevelParam` bit agrees with structural traversal. -/ +@[simp] theorem hasLevelParam_eq (e : Expr) : e.hasLevelParam = e.hasLevelParam' := by + change e.data.hasLevelParam = e.hasLevelParam' + induction e with + | bvar => simp [Expr.data, hasLevelParam', mkData_hasLevelParam_of_false] + | fvar | mvar | sort | const | lit => + simp only [Expr.data, hasLevelParam'] + apply mkData_hasLevelParam + omega + | app _ _ ih1 ih2 => + simp only [Expr.data, hasLevelParam'] + rw [mkAppData_hasLevelParam, ih1, ih2] + | lam _ ty body _ ihty ihbody | forallE _ ty body _ ihty ihbody => + simp only [Expr.data, mkDataForBinder, hasLevelParam'] + rw [mkData_hasLevelParam (binder_looseBVarRange_le ty body), ihty, ihbody] + | letE _ ty val body _ ihty ihval ihbody => + simp only [Expr.data, mkDataForLet, hasLevelParam'] + rw [mkData_hasLevelParam (let_looseBVarRange_le ty val body), ihty, ihval, ihbody] + | mdata _ e ih | proj _ _ e ih => + simp only [Expr.data, hasLevelParam'] + rw [mkData_hasLevelParam (Data.looseBVarRange_le (d := e.data)), ih] + +end Expr + +namespace Literal +open Expr in +theorem toConstructor_hasLevelParam : + (Literal.toConstructor l).hasLevelParam' = false := by + cases l with simp [Literal.toConstructor] + | natVal n => cases n <;> simp [natLitToConstructor, hasLevelParam', natZero, natSucc] + | strVal s => + simp [strLitToConstructor, hasLevelParam'] + induction s.toList <;> simp_all [hasLevelParam', Level.hasParam'] + +protected theorem beq_iff_eq {m n : Literal} : m == n ↔ m = n := by + cases m <;> cases n <;> simp! [(· == ·)] + +instance : LawfulBEq Literal where + eq_of_beq := Literal.beq_iff_eq.1 + rfl := Literal.beq_iff_eq.2 rfl + +instance : DecidableEq Literal := + fun a b => if h : a == b then .isTrue (by simp_all) else .isFalse (by simp_all) + +@[simp] theorem mkConst_typeName {l : Literal} : .const l.typeName [] = l.type := by + cases l <;> simp [typeName, type, mkConst] + +end Literal + +namespace Expr + +attribute [simp] mkConst mkBVar mkSort mkFVar mkMVar mkMData mkProj mkApp mkLambda mkForall mkLet + updateApp! updateFVar! updateConst! updateSort! updateMData! updateProj! + updateForall! updateForallE! updateLambda! updateLambdaE! updateLetE! updateLet! + +set_option allowUnsafeReducibility true +attribute [local reducible] Data + +theorem mkData_looseBVarRange (H : br ≤ 2^20 - 1) : + (mkData h br d fv ev lv lp).looseBVarRange.toNat = br := by + rw [mkData_eq, mkData', if_pos H]; dsimp only [Data.looseBVarRange, -Nat.reducePow] + have : br.toUInt64.toUInt32.toNat = br := by simp; omega + refine .trans ?_ this; congr 2 + refine UInt64.eq_of_toBitVec_eq ?_ + have : br.toUInt64.toNat = br := by simp; omega + have : br.toUInt64.toBitVec ≤ 0xfffff#64 := (this ▸ H :) + have : h.toUInt32.toUInt64.toBitVec ≤ 0xffffffff#64 := + show h.toUInt32.toNat ≤ 2^32-1 by simp; omega + have : (if d > 255 then 255 else d.toUInt8).toUInt64.toBitVec ≤ 0xff#64 := + Nat.le_of_lt_succ (if d > 255 then 255 else d.toUInt8).1.1.2 + have hb : ∀ (b : Bool), b.toUInt64.toBitVec ≤ 1#64 := by decide + have := hb fv; have := hb ev; have := hb lv; have := hb lp + change + (h.toUInt32.toUInt64.toBitVec + + (if d > 255 then 255 else d.toUInt8).toUInt64.toBitVec <<< 32#64 + + fv.toUInt64.toBitVec <<< 40#64 + + ev.toUInt64.toBitVec <<< 41#64 + + lv.toUInt64.toBitVec <<< 42#64 + + lp.toUInt64.toBitVec <<< 43#64 + + br.toUInt64.toBitVec <<< 44#64) >>> 44#64 = + br.toUInt64.toBitVec + bv_decide + +theorem looseBVarRange_le : looseBVarRange e ≤ 2^20 - 1 := Data.looseBVarRange_le + +theorem _root_.UInt32.max_toNat (a b : UInt32) : (max a b).toNat = max a.toNat b.toNat := by + simp +instances only [instMaxUInt32, maxOfLe, UInt32.le_iff_toNat_le, Nat.instMax]; split <;> rfl + +theorem mkAppData_looseBVarRange : + (mkAppData fData aData).looseBVarRange = max fData.looseBVarRange aData.looseBVarRange := by + have hm : max fData.looseBVarRange aData.looseBVarRange ≤ (Nat.pow 2 20 - 1).toUInt32 := by + dsimp +instances [instMaxUInt32, maxOfLe]; split <;> exact Data.looseBVarRange_le + rw [mkAppData_eq, mkAppData', if_pos hm] + simp [Data.looseBVarRange] at hm + dsimp only [Data.looseBVarRange, -Nat.reducePow] + generalize (max .. : UInt32) = m at * + have : m.toUInt64.toUInt32 = m := by simp [UInt64.toUInt32] + refine .trans ?_ this; congr 2 + generalize (ite (_ > (255 : UInt16)) _ _ : UInt8) = a + generalize HOr.hOr (α := UInt64) fData _ = b + generalize UInt64.toUInt32 _ = c + apply UInt64.eq_of_toBitVec_eq + change m.toUInt64.toBitVec ≤ 0xfffff#64 at hm + have : c.toUInt64.toBitVec ≤ 0xffffffff#64 := Nat.le_of_lt_succ c.1.1.2 + have : a.toUInt64.toBitVec ≤ 0xff#64 := Nat.le_of_lt_succ a.1.1.2 + change + (b.toBitVec &&& 15#64 <<< 40#64 ||| + c.toUInt64.toBitVec ||| + a.toUInt64.toBitVec <<< 32#64 ||| + m.toUInt64.toBitVec <<< 44#64) >>> 44#64 = + m.toUInt64.toBitVec + bv_decide + +def getAppArgsList : Expr → (r : List Expr := []) → List Expr + | .app f a, r => f.getAppArgsList (a :: r) + | _, r => r + +def getAppArgsRevList : Expr → List Expr + | .app f a => a :: f.getAppArgsRevList + | _ => [] + +theorem getAppArgsRevList_reverse : (getAppArgsRevList e).reverse = getAppArgsList e := by + let rec loop {e r} : getAppArgsList e r = (getAppArgsRevList e).reverse ++ r := by + unfold getAppArgsList getAppArgsRevList; split <;> simp; exact loop + simp [loop] + +theorem getAppArgsList_reverse : (getAppArgsList e).reverse = getAppArgsRevList e := by + rw [← getAppArgsRevList_reverse]; simp + +open private getAppNumArgsAux getAppArgsAux mkAppRangeAux from Lean.Expr + +theorem getAppNumArgs_eq : getAppNumArgs e = (getAppArgsRevList e).length := by + let rec loop e n : getAppNumArgsAux e n = (getAppArgsRevList e).length + n := by + unfold getAppNumArgsAux getAppArgsRevList; split <;> simp + rw [loop]; omega + rw [getAppNumArgs, loop]; rfl + +theorem getAppArgs_toList_rev : (getAppArgs e).toList = (getAppArgsRevList e).reverse := by + let rec loop {e l₁ l₂ args} (h1 : args.toList = l₁ ++ l₂) : + l₁.length = (getAppArgsRevList e).length → + (getAppArgsAux e args ((getAppArgsRevList e).length - 1)).toList = + (getAppArgsRevList e).reverse ++ l₂ := by + unfold getAppArgsAux getAppArgsRevList; split + · rename_i f a; intro h2 + obtain rfl | ⟨l₁, u, rfl⟩ := List.eq_nil_or_concat l₁; · cases h2 + simp at h2 ⊢; refine loop ?_ h2 + simp [← h2, h1] + · simp; rintro ⟨⟩; simpa using h1 + simp [getAppArgs, getAppNumArgs_eq] + exact (loop (List.append_nil _).symm (by simp)).trans (by simp) + +theorem getAppArgs_toList : (getAppArgs e).toList = getAppArgsList e := by + rw [getAppArgs_toList_rev, getAppArgsRevList_reverse] + +theorem getAppArgs_eq_rev : getAppArgs e = (getAppArgsRevList e).reverse.toArray := by + simp [← getAppArgs_toList_rev] + +theorem getAppArgs_eq : getAppArgs e = (getAppArgsList e).toArray := by + simp [← getAppArgs_toList] + +open private getAppRevArgsAux from Lean.Expr in +theorem getAppRevArgs_toList : (getAppRevArgs e).toList = getAppArgsRevList e := by + simp [getAppRevArgs, loop] +where + loop {e args} : (getAppRevArgsAux e args).toList = args.toList ++ getAppArgsRevList e := by + unfold getAppRevArgsAux getAppArgsRevList; split <;> [(rw [loop]; simp); simp] + +theorem getAppRevArgs_eq : getAppRevArgs e = (getAppArgsRevList e).toArray := by + simp [← getAppRevArgs_toList] + +@[simp] theorem withApp_eq {e : Expr} : e.withApp f = f e.getAppFn e.getAppArgs := loop where + loop {e arr n} : withAppAux f e arr n = f e.getAppFn (getAppArgsAux e arr n) := by + unfold withAppAux getAppArgsAux; split <;> [exact loop; simp [getAppFn]] + +open private withAppRevAux getAppRevArgsAux from Lean.Expr in +@[simp] theorem withRevApp_eq {e : Expr} : + e.withAppRev f = f e.getAppFn e.getAppRevArgs := loop where + loop {e arr} : withAppRevAux f e arr = f e.getAppFn (getAppRevArgsAux e arr) := by + unfold withAppRevAux getAppRevArgsAux; split <;> [exact loop; simp [getAppFn]] + +@[simp] def mkAppList : Expr → List Expr → Expr + | e, [] => e + | e, a :: as => mkAppList (.app e a) as + +theorem mkAppList_eq_foldl : mkAppList e es = es.foldl .app e := by + induction es generalizing e <;> simp [mkAppList, *] + +@[simp] theorem mkAppList_append : + mkAppList e (es₁ ++ es₂) = mkAppList (mkAppList e es₁) es₂ := by + simp [mkAppList_eq_foldl] + +@[simp] def mkAppRevList : Expr → List Expr → Expr + | e, [] => e + | e, a :: as => .app (mkAppRevList e as) a + +theorem mkAppRevList_eq_foldr : mkAppRevList e es = es.foldr (fun a e => .app e a) e := by + induction es <;> simp [mkAppRevList, *] + +@[simp] theorem mkAppRevList_append : + mkAppRevList e (es₁ ++ es₂) = mkAppRevList (mkAppRevList e es₂) es₁ := by + simp [mkAppRevList_eq_foldr] + +theorem mkAppList_reverse : mkAppList e es.reverse = mkAppRevList e es := by + simp [mkAppList_eq_foldl, mkAppRevList_eq_foldr] + +theorem mkAppRevList_reverse : mkAppRevList e es.reverse = mkAppList e es := by + simp [mkAppList_eq_foldl, mkAppRevList_eq_foldr] + +theorem mkAppRevList_getAppArgsRevList (e) : + mkAppRevList e.getAppFn (getAppArgsRevList e) = e := by + unfold getAppFn getAppArgsRevList; split <;> simp + congr 1; exact mkAppRevList_getAppArgsRevList _ + +theorem mkAppList_getAppArgsList (e) : + mkAppList e.getAppFn (getAppArgsList e) = e := by + rw [← mkAppRevList_reverse, getAppArgsList_reverse, mkAppRevList_getAppArgsRevList] + +theorem mkAppRange_eq (h1 : args.toList = l₁ ++ l₂ ++ l₃) + (h2 : l₁.length = i) (h3 : (l₁ ++ l₂).length = j) : + mkAppRange e i j args = mkAppList e l₂ := loop h1 h2 h3 where + loop {n i e l₁ l₂} (h1 : args.toList = l₁ ++ l₂ ++ l₃) + (h2 : l₁.length = i) (h3 : (l₁ ++ l₂).length = n) : + mkAppRangeAux n args i e = mkAppList e l₂ := by + rw [mkAppRangeAux.eq_def]; split + · simp [Array.getElem!_eq_getD, ← Array.getElem?_toList, h1] + obtain _ | ⟨a, l₂⟩ := l₂; · simp_all + rw [List.getElem?_append_right (by simp [h2])] + simp [h2] + rw [loop (l₁ := l₁ ++ [a]) (l₂ := l₂)] <;> simp [h1, h2, h3] + · simp at h3 + have : l₂.length = 0 := by omega + simp_all + +theorem mkAppRange_eq_rev (h1 : args.toList = l₁ ++ l₂ ++ l₃) + (h2 : l₁.length = i) (h3 : (l₁ ++ l₂).length = j) : + mkAppRange e i j args = mkAppRevList e l₂.reverse := by + rw [mkAppRange_eq h1 h2 h3, mkAppRevList_reverse] + +open private mkAppRevRangeAux from Lean.Expr in +theorem mkAppRevRange_eq(h1 : args.toList = l₁ ++ l₂ ++ l₃) + (h2 : l₁.length = i) (h3 : (l₁ ++ l₂).length = j) : + mkAppRevRange e i j args = mkAppList e l₂.reverse := by + unfold mkAppRevRange + simpa using loop l₁ l₂.reverse l₃ [] (by simpa using h1) h2 (by simpa using h3) +where + loop {start i} (l₁ l₂ l₃ l₄) (h1 : args.toList = l₁ ++ l₂.reverse ++ l₃) + (h2 : l₁.length = start) (h3 : l₁.length + List.length l₂ = i) : + mkAppRevRangeAux args start (mkAppRevList e l₄) i = mkAppList (mkAppRevList e l₄) l₂ := by + rw [mkAppRevRangeAux.eq_def]; split + · have : l₂.length = 0 := by omega + simp_all + · have : 0 < l₂.length := by omega + let a::l₂ := l₂; simp [← Nat.add_assoc] at h3 + let i+1 := i; simp at h3 + simp [Array.getElem!_eq_getD, ← Array.getElem?_toList, h1] + rw [List.getElem?_append_right (by omega)] + simp at h1; simp [← List.append_assoc] at h1 + simp [← h3]; rw [h3] + simpa using loop l₁ l₂ (a :: l₃) (a :: l₄) (by simp [h1]) h2 h3 + +theorem mkAppRevRange_eq_rev (h1 : args.toList = l₁ ++ l₂ ++ l₃) + (h2 : l₁.length = i) (h3 : (l₁ ++ l₂).length = j) : + mkAppRevRange e i j args = mkAppRevList e l₂ := by + rw [mkAppRevRange_eq h1 h2 h3, mkAppList_reverse] + +theorem liftLooseBVars_eq_self : e.looseBVarRange' ≤ s → liftLooseBVars' e s d = e := by + induction e generalizing s <;> + simp +contextual [*, looseBVarRange', liftLooseBVars', Nat.max_le] + omega + +@[simp] theorem liftLooseBVars_zero : liftLooseBVars' e s 0 = e := by + induction e generalizing s <;> simp [*, liftLooseBVars'] + +theorem liftLooseBVars_liftLooseBVars {e : Expr} {n1 n2 k1 k2 : Nat} + (h1 : k1 ≤ k2) (h2 : k2 ≤ n1 + k1) : + liftLooseBVars' (liftLooseBVars' e k1 n1) k2 n2 = liftLooseBVars' e k1 (n1+n2) := by + induction e generalizing k1 k2 with simp [liftLooseBVars', ← Nat.add_assoc, *] + | bvar i => + split <;> rename_i h + · rw [if_pos (Nat.lt_of_lt_of_le h h1)] + · rw [if_neg (by omega), Nat.add_assoc] + +theorem liftLooseBVars_add {e : Expr} {n1 n2 k : Nat} : + liftLooseBVars' (liftLooseBVars' e k n1) k n2 = liftLooseBVars' e k (n1+n2) := by + induction e generalizing k with simp [liftLooseBVars', *] + | bvar i => + split; · rfl + rw [if_neg (by omega), Nat.add_assoc] + +theorem liftLooseBVars_comm (e : Expr) (n1 n2 k1 k2 : Nat) (h : k2 ≤ k1) : + liftLooseBVars' (liftLooseBVars' e k1 n1) k2 n2 = + liftLooseBVars' (liftLooseBVars' e k2 n2) (n2+k1) n1 := by + induction e generalizing k1 k2 with + simp [liftLooseBVars', Nat.add_assoc, Nat.succ_le_succ, *] + | bvar i => + split <;> rename_i h' + · rw [if_pos (c := _ < n2 + k1)]; split + · exact Nat.lt_add_left _ h' + · omega + · have := mt (Nat.lt_of_lt_of_le · h) h' + rw [if_neg (by omega), if_neg this, if_neg (by omega), Nat.add_right_comm] + +theorem liftLooseBVars_looseBVarRange : + (liftLooseBVars' e k n).looseBVarRange' ≤ e.looseBVarRange' + n := by + induction e generalizing k <;> simp [*, looseBVarRange', liftLooseBVars'] <;> grind + +theorem instantiate1'_looseBVarRange + (he : e.looseBVarRange' ≤ n + k + 1) (ha : a.looseBVarRange' ≤ n) : + (instantiate1' e a k).looseBVarRange' ≤ n + k := by + induction e generalizing k with + simp_all [looseBVarRange', instantiate1', Nat.max_le] + | bvar => + split <;> [skip; split] <;> try simp [looseBVarRange'] at he ⊢; omega + have := @liftLooseBVars_looseBVarRange a 0 k; omega + | _ => grind + +theorem instantiateList_looseBVarRange + (he : e.looseBVarRange' ≤ n + k + as.length) (ha : ∀ a ∈ as, a.looseBVarRange' ≤ n) : + (instantiateList e as k).looseBVarRange' ≤ n + k := by + induction as generalizing e with simp_all | cons _ _ ih + apply ih; rw [Nat.add_right_comm]; apply instantiate1'_looseBVarRange <;> omega + +theorem instantiate1'_eq_self : e.looseBVarRange' ≤ k → instantiate1' e a k = e := by + induction e generalizing k <;> + simp +contextual [*, looseBVarRange', instantiate1', Nat.max_le] + omega + +theorem instantiate1_eq_self (H : e.looseBVarRange' = 0) : instantiate1' e a = e := by + simpa using instantiate1'_eq_self (e := e) (Nat.le_zero.2 H) + +theorem instantiateList'_eq_self (h : e.looseBVarRange' ≤ k) : instantiateList e as k = e := by + induction as <;> simp [instantiateList, instantiate1'_eq_self, *] + +theorem instantiateList_eq_self (h : e.looseBVarRange' = 0) : instantiateList e as = e := by + induction as <;> simp [instantiateList, instantiate1_eq_self, *] + +theorem instantiate1'_liftLooseBVars : + instantiate1' (liftLooseBVars' e s (d + 1)) a (s + d) = e.liftLooseBVars' s d := by + induction e generalizing s <;> + simp [*, instantiate1', liftLooseBVars', Nat.add_right_comm _ _ 1] + rename_i i; split; · simp; omega + · rw [if_neg (by omega), if_neg (by omega)]; rfl + +theorem instantiate1'_liftLooseBVars_0 (e1 e2 : Expr) : + instantiate1' (liftLooseBVars' e1 k 1) e2 k = e1 := by + simpa using (instantiate1'_liftLooseBVars (d := 0)).trans liftLooseBVars_zero + +theorem instantiate1'_instantiate1' (e1 e2 e3 j) : + instantiate1' (instantiate1' e1 e2 (j+1)) e3 j = + instantiate1' (instantiate1' e1 (e3.liftLooseBVars' 0 1) j) e2 j := by + induction e1 generalizing j with simp [instantiate1', *] | bvar i + split <;> rename_i h + · split <;> rename_i h1 + · simp [instantiate1', h1] + split <;> rename_i h1' + · subst i + simp [instantiate1']; rw [liftLooseBVars_comm (h := Nat.zero_le _)] + exact (instantiate1'_liftLooseBVars_0 ..).symm + · have hj := Nat.lt_of_le_of_ne (Nat.not_lt.1 h1) (Ne.symm h1') + let i+1 := i + simp [instantiate1', h1, h1', Nat.lt_of_succ_lt_succ h] + split <;> rename_i h' + · subst i + rw [if_neg (by omega), if_neg (by omega)] + simp [instantiate1'] + suffices liftLooseBVars' _ _ (j+1) = _ by + rw [this]; exact instantiate1'_liftLooseBVars_0 .. + exact (liftLooseBVars_liftLooseBVars (Nat.zero_le _) (Nat.le_refl _)).symm + · have hk := Nat.lt_of_le_of_ne (Nat.not_lt.1 h) (Ne.symm h') + let i+1 := i + have hk := Nat.lt_of_add_lt_add_right hk + simp [instantiate1'] + rw [if_neg (by omega), if_neg (by omega), if_neg (by omega), if_neg (by omega)] + simp [instantiate1'] + rw [if_neg (Nat.lt_asymm hk), if_neg (Nat.ne_of_gt hk)] + +@[simp] def instantiateRevList (e : Expr) : List Expr → (k :_:= 0) → Expr + | [], _ => e + | a :: as, k => instantiate1' (instantiateRevList e as k) a k + +theorem instantiateList_eq_foldl : + instantiateList e as k = as.foldl (instantiate1' · · k) e := by + induction as generalizing e <;> simp [*] + +theorem instantiateRevList_eq_foldr : + instantiateRevList e as k = as.foldr (fun a e => instantiate1' e a k) e := by + induction as <;> simp [*] + +theorem instantiateList_append : + instantiateList e (es₁ ++ es₂) k = instantiateList (instantiateList e es₁ k) es₂ k := by + simp [instantiateList_eq_foldl] + +theorem instantiateRevList_append : + instantiateRevList e (es₁ ++ es₂) k = + instantiateRevList (instantiateRevList e es₂ k) es₁ k := by + simp [instantiateRevList_eq_foldr] + +theorem instantiateList_reverse : + instantiateList e as.reverse k = instantiateRevList e as k := by + simp [instantiateList_eq_foldl, instantiateRevList_eq_foldr] + +theorem instantiateRevList_reverse : + instantiateRevList e as.reverse k = instantiateList e as k := by + simp [instantiateList_eq_foldl, instantiateRevList_eq_foldr] + +@[simp] +theorem instantiateRevList_lam : instantiateRevList (.lam n ty body bi) as k = + .lam n (instantiateRevList ty as k) (instantiateRevList body as (k + 1)) bi := by + induction as <;> simp [instantiate1', *] + +@[simp] +theorem instantiateList_lam : instantiateList (.lam n ty body bi) as k = + .lam n (instantiateList ty as k) (instantiateList body as (k + 1)) bi := by + simp [← instantiateRevList_reverse, instantiateRevList_lam] + +@[simp] +theorem instantiateRevList_app : instantiateRevList (.app f a) as k = + .app (instantiateRevList f as k) (instantiateRevList a as k) := by + induction as <;> simp [instantiate1', *] + +@[simp] +theorem instantiateList_app : instantiateList (.app f a) as k = + .app (instantiateList f as k) (instantiateList a as k) := by + simp [← instantiateRevList_reverse, instantiateRevList_app] + +@[simp] +theorem instantiateList_forallE : instantiateList (.forallE n ty body bi) as k = + .forallE n (instantiateList ty as k) (instantiateList body as (k + 1)) bi := by + induction as generalizing ty body <;> simp [instantiate1', *] + +@[simp] +theorem instantiateList_letE : instantiateList (.letE n ty val body nd) as k = + .letE n (instantiateList ty as k) (instantiateList val as k) + (instantiateList body as (k + 1)) nd := by + induction as generalizing ty val body <;> simp [instantiate1', *] + +theorem instantiateList_instantiate1_comm (h : a.looseBVarRange' = 0) : + (instantiateList e as 1).instantiate1' a = + instantiateList (e.instantiate1' a) as := by + induction as generalizing e <;> simp [*] + congr 1; refine (instantiate1'_instantiate1' (j := 0) ..).trans ?_ + rw [liftLooseBVars_eq_self (by simp [h])] + +theorem instantiateRev_push {e : Expr} {subst a} : + instantiateRev e (subst.push a) = instantiateRev (e.instantiate1' a) subst := by + let ⟨subst⟩ := subst; simp [instantiateList] + +theorem abstractList_eq_foldl {e : Expr} {as k} : + abstractList e as k = List.foldl (fun e a => abstract1 a e k) e as := by + induction as generalizing e <;> simp_all + +@[simp] def abstractRevList : Expr → List FVarId → (k :_:= 0) → Expr + | e, [], _ => e + | e, a :: as, k => abstract1 a (abstractRevList e as k) k + +theorem abstractRevList_eq_foldr {e : Expr} {as k} : + abstractRevList e as k = List.foldr (abstract1 · · k) e as := by + induction as <;> simp_all + +theorem abstractList_reverse {e : Expr} {as k} : + abstractList e as.reverse k = abstractRevList e as k := by + simp [abstractList_eq_foldl, abstractRevList_eq_foldr] + +theorem abstractRevList_reverse {e : Expr} {as k} : + abstractRevList e as.reverse k = abstractList e as k := by + simp [abstractList_eq_foldl, abstractRevList_eq_foldr] + +theorem abstractList_append {e : Expr} {as k} : + abstractList e (as ++ as') k = abstractList (abstractList e as k) as' k := by + simp [abstractList_eq_foldl] + +theorem abstract1_comm {e : Expr} {k} (h : a ≠ b) : + abstract1 a (abstract1 b e k) k = + abstract1 b (abstract1 a e k) (k+1) := by + induction e generalizing k with simp_all [abstract1] + | bvar => split <;> [rw [if_pos]; simp [*]] <;> omega + | fvar => split <;> split <;> simp_all [abstract1] + +theorem abstract1_abstractList {e : Expr} {as : List FVarId} {k} (H : a ∉ as) : + abstract1 a (abstractList e as k) k = + abstractList (abstract1 a e k) as (k+1) := by + induction as generalizing e <;> simp_all [abstract1_comm] + +theorem abstract1_abstractList' {e : Expr} {as : List FVarId} {k} (H : (a :: as).Nodup) : + abstract1 a (abstractList e as k) (k + as.length) = + abstractList (abstract1 a e k) as k := by + induction as generalizing e a with + | nil => simp + | cons b as ih => + simp at *; simp [H] at ih + rw [← ih H.2.1, ← Nat.add_assoc, ← abstract1_comm (.symm H.1.1), ih H.1.2, ih H.2.1] + +theorem abstract1_hasLooseBVar (a e k i) : + (abstract1 a e k).hasLooseBVar' (if i < k then i else i+1) = e.hasLooseBVar' i := by + have (i) k : (if i < k then i else i + 1) + 1 = if i + 1 < k + 1 then i + 1 else i + 1 + 1 := by + simp; split <;> rfl + induction e generalizing i k with simp only [hasLooseBVar', abstract1, *] + | bvar j => + by_cases h : j = i <;> simp [h] + split <;> split <;> omega + | fvar b => + split <;> simp [hasLooseBVar'] + split <;> omega + +theorem abstract1_eq_liftLooseBVars (h : (abstract1 a e k).hasLooseBVar' k = false) : + abstract1 a e k = liftLooseBVars' e k 1 := by + induction e generalizing k <;> grind [abstract1, hasLooseBVar', liftLooseBVars'] + +-- theorem abstract1_looseBVarRange_le : +-- (abstract1 a e k).looseBVarRange' ≤ max k e.looseBVarRange' + 1 := by +-- have H {k a b c d} (h1 : a ≤ max k c + 1) (h2 : b ≤ max k d + 1) : +-- max a b ≤ max k (max c d) + 1 := by +-- rw [Nat.max_le]; simp [Nat.max_def]; split <;> split <;> omega +-- have {k a b c d} (h1 : a ≤ max k c + 1) (h2 : b ≤ max (k+1) d + 1) : +-- max a (b - 1) ≤ max k (max c (d - 1)) + 1 := by apply H <;> omega +-- induction e generalizing k with simp [abstract1, looseBVarRange', *] <;> try solve_by_elim +-- | bvar => split <;> omega +-- | fvar => split <;> simp [looseBVarRange'] + +theorem lowerLooseBVars_eq_instantiate (h : e.hasLooseBVar' k = false) : + e.lowerLooseBVars' (k + 1) 1 = instantiate1' e v k := by + induction e generalizing k with simp_all [hasLooseBVar', lowerLooseBVars', instantiate1'] + | bvar j => split <;> [rw [if_pos (by omega)]; rw [if_neg (by omega)]] + +theorem hasLooseBVar_of_ge_looseBVarRange {e : Expr} (h : e.looseBVarRange' ≤ k) : + e.hasLooseBVar' k = false := by + induction e generalizing k with simp_all [hasLooseBVar', looseBVarRange', Nat.max_le] + | bvar j => omega + +theorem abstract1_lower {e : Expr} (h : e.hasLooseBVar' k₁ = false) (hk : k₁ ≤ k₂) : + Expr.abstract1 a (e.lowerLooseBVars' (k₁ + 1) 1) k₂ = + (Expr.abstract1 a e (k₂ + 1)).lowerLooseBVars' (k₁ + 1) 1 := by + induction e generalizing k₁ k₂ with simp_all [abstract1, lowerLooseBVars', hasLooseBVar'] + | bvar i => + split <;> [skip; split] + · rw [if_pos (c := i < k₂ + 1) (by omega), if_pos (by omega)]; simp [*] + · rw [if_pos (c := i < _) (by omega)]; simp [*] + · rw [if_neg (c := i < _) (by omega), if_neg (by omega)]; omega + | fvar b => + split <;> simp [lowerLooseBVars', -right_eq_ite_iff] + rw [if_neg (by omega)] + +variable (red : Bool) (s : Name → Level) in +def instantiateLevelParamsCore' : Expr → Expr + | .const c ls => .const c (ls.map (·.substParams' s red)) + | .sort u => .sort (u.substParams' s red) + | .mdata m e => .mdata m (instantiateLevelParamsCore' e) + | .proj n i e => .proj n i (instantiateLevelParamsCore' e) + | .app f a => .app (instantiateLevelParamsCore' f) (instantiateLevelParamsCore' a) + | .lam n t b bi => .lam n (instantiateLevelParamsCore' t) (instantiateLevelParamsCore' b) bi + | .forallE n t b bi => + .forallE n (instantiateLevelParamsCore' t) (instantiateLevelParamsCore' b) bi + | .letE n t v b bi => .letE n (instantiateLevelParamsCore' t) + (instantiateLevelParamsCore' v) (instantiateLevelParamsCore' b) bi + | e@(.bvar _) + | e@(.fvar _) + | e@(.mvar _) + | e@(.lit _) => e + +theorem instantiateLevelParamsCore_eq_self (h : e.hasLevelParam' = false) : + instantiateLevelParamsCore' red s e = e := by + induction e <;> simp_all [instantiateLevelParamsCore', hasLevelParam', Level.substParams_eq_self] + exact List.map_id''' _ fun _ h' => Level.substParams_eq_self (h _ h') + +theorem instantiateLevelParamsCore_id {e : Expr} : + instantiateLevelParamsCore' false .param e = e := by + induction e <;> simp_all [instantiateLevelParamsCore', Level.substParams_id] + +open private instantiateLevelParamsCore.replaceFn from Lean.Util.InstantiateLevelParams in +theorem instantiateLevelParamsCore_eq : + instantiateLevelParamsCore s e = + instantiateLevelParamsCore' true (fun x => (s x).getD (.param x)) e := by + simp [instantiateLevelParamsCore] + have (e) (H : e.hasLevelParam' = true → + replaceNoCache (instantiateLevelParamsCore.replaceFn s) e = + instantiateLevelParamsCore' true (fun x => (s x).getD (Level.param x)) e) : + replaceNoCache (instantiateLevelParamsCore.replaceFn s) e = + instantiateLevelParamsCore' true (fun x => (s x).getD (Level.param x)) e := by + cases eq : e.hasLevelParam' <;> [skip; exact H eq] + rw [instantiateLevelParamsCore_eq_self eq] + suffices ∀ f, f e = some e → replaceNoCache f e = e by + apply this; simp [instantiateLevelParamsCore.replaceFn, eq] + intro f eq; cases e <;> simp only [replaceNoCache, eq] + induction e <;> ( + refine this _ fun h => ?_ + simp only [replaceNoCache, instantiateLevelParamsCore.replaceFn] + simp [h]; clear this h + simp_all [instantiateLevelParamsCore']) + +open private getParamSubst from Lean.Util.InstantiateLevelParams in +theorem instantiateLevelParams_eq {e ps ls} : + instantiateLevelParams e ps ls = + instantiateLevelParamsCore' (!ps.isEmpty && !ls.isEmpty) + (fun x => ((ps.idxOf? x).bind (ls[·]?)).getD (.param x)) e := by + simp only [instantiateLevelParams]; rw [← Bool.not_or]; + cases eq : ps.isEmpty || ls.isEmpty <;> simp + · clear eq + simp [instantiateLevelParamsCore_eq, List.idxOf?]; congr; ext n; congr + induction ps generalizing ls <;> cases ls <;> simp [getParamSubst] + split <;> simp [*, List.findIdx?_cons]; cases List.findIdx? .. <;> simp + · refine instantiateLevelParamsCore_id.symm.trans ?_; congr; ext n + cases ps <;> simp_all + +theorem eqv_sort {e : Expr} : e == .sort u ↔ e = .sort u := by + conv => lhs; simp [(· == ·)] + cases e <;> simp [eqv'] + +theorem eqv_const {e : Expr} : e == .const c ls ↔ e = .const c ls := by + conv => lhs; simp [(· == ·)] + cases e <;> simp [eqv'] + +theorem structuralEq_const {e : Expr} : + structuralEq e (.const c ls) = true ↔ e = .const c ls := by + cases e <;> simp [structuralEq] + +theorem structuralEq_refl (e : Expr) : structuralEq e e = true := by + induction e <;> simp [structuralEq, *] + +/-- Transparent structural equality implies Lean's non-strict expression +equivalence. This direction deliberately does not identify binder names or +binder information, which `Expr.eqv` also ignores. -/ +theorem structuralEq_eqv {a b : Expr} : + structuralEq a b = true → a == b := by + simp only [(· == ·)] + induction a generalizing b <;> cases b <;> + simp_all [structuralEq, eqv'] + +theorem eqv_refl (e : Expr) : e == e := by + simp [(· == ·)]; induction e <;> simp [eqv', *] + +theorem eqv_euc {e₁ e₂ e₃ : Expr} : e₁ == e₂ → e₁ == e₃ → e₂ == e₃ := by + simp [(· == ·)] + induction e₁ generalizing e₂ e₃ + all_goals + cases e₂ <;> try change false = _ → _; rintro ⟨⟩ + simp [eqv']; intros; subst_vars; try simp [*] + all_goals + revert ‹Expr.eqv' .. = _› + cases e₃ <;> try change false = _ → _; rintro ⟨⟩ + simp +contextual [eqv', *] + simp [BEq.congr_left ‹_›] + +instance : EquivBEq Expr where + symm h := eqv_euc h (eqv_refl _) + trans h1 h2 := eqv_euc (eqv_euc h1 (eqv_refl _)) h2 + rfl := eqv_refl _ + +theorem data_eq {e₁ e₂ : Expr} : e₁ == e₂ → e₁.data = e₂.data := by + simp [(· == ·)]; induction e₁ generalizing e₂ + all_goals + cases e₂ <;> try change false = _ → _; rintro ⟨⟩ + simp [eqv']; intros; subst_vars; try simp [*] + all_goals simp [Expr.data]; grind + +instance : LawfulHashable Expr where + hash_eq e₁ e₂ h := by simp [hash, Expr.hash, data_eq h] + +theorem instantiate1_eqv {e₁ e₂ : Expr} : + e₁ == e₂ → e₁.instantiate1' a k == e₂.instantiate1' a k := by + simp [(· == ·)]; induction e₁ generalizing e₂ k + all_goals + cases e₂ <;> try change false = _ → _; rintro ⟨⟩ + simp [instantiate1', eqv'] + all_goals intros; subst_vars; try simp [*] + split <;> [skip; split] <;> simpa [(· == ·)] using eqv_refl _ + +theorem instantiateList_eqv {e₁ e₂ : Expr} (h : e₁ == e₂) : + e₁.instantiateList as k == e₂.instantiateList as k := by + induction as generalizing e₁ e₂ <;> simp [instantiate1_eqv, *] diff --git a/Ix/Theory/Named/Verify/Level.lean b/Ix/Theory/Named/Verify/Level.lean new file mode 100644 index 000000000..8373b4f29 --- /dev/null +++ b/Ix/Theory/Named/Verify/Level.lean @@ -0,0 +1,3916 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.VLevel +import Ix.Theory.Named.Reference.Level +import Ix.Theory.Named.Verify.Name +import Ix.Theory.Named.Verify.LevelStd +import Ix.Theory.Named.Verify.Axioms +import Std.Tactic.BVDecide +import Std.Data.TreeMap.Lemmas + +open Ix.Theory (VLevel) + +namespace Lean + +namespace Level +open Ix.Theory.Named + +attribute [simp] mkLevelSucc mkLevelMax mkLevelIMax updateSucc! updateMax! updateIMax! + +-- variable (ls : List Name) in +-- def _root_.Ix.Theory.VLevel.toLevel : VLevel → Level +-- | .zero => .zero +-- | .succ l => .succ l.toLevel +-- | .max l₁ l₂ => .max l₁.toLevel l₂.toLevel +-- | .imax l₁ l₂ => .imax l₁.toLevel l₂.toLevel +-- | .param n => match ls.get? n with +-- | some l => .param l +-- | none => .zero + +-- theorem toLevel_inj {ls : List Name} (d : ls.Nodup) +-- {l₁ l₂ : VLevel} (eq : l₁.toLevel ls = l₂.toLevel ls) : l₁ = l₂ := sorry + +@[simp] def getOffset' : Level → Nat + | succ u => getOffset' u + 1 + | _ => 0 + +@[simp] theorem getOffset_eq (u : Level) : u.getOffset = u.getOffset' := go _ 0 where + go (u : Level) (i) : u.getOffsetAux i = u.getOffset' + i := by + unfold getOffsetAux getOffset'; split <;> simp + rw [go]; simp [Nat.add_right_comm, Nat.add_assoc] + +set_option allowUnsafeReducibility true +attribute [local reducible] Data + +theorem mkData_depth (H : d < 2 ^ 24) : (mkData h d hmv hp).depth.toNat = d := by + rw [mkData_eq, mkData', if_neg (Nat.not_lt.2 (Nat.le_sub_one_of_lt H)), Data.depth] + have : d.toUInt64.toUInt32.toNat = d := by simp; omega + refine .trans ?_ this; congr 2 + rw [← UInt64.toBitVec_inj] + have : d.toUInt64.toNat = d := by simp; omega + have : d.toUInt64.toBitVec ≤ 0xffffff#64 := (this ▸ Nat.le_sub_one_of_lt H :) + have : h.toUInt32.toUInt64.toBitVec ≤ 0xffffffff#64 := Nat.le_of_lt_succ h.toUInt32.1.1.2 + have hb : ∀ (b : Bool), b.toUInt64.toBitVec ≤ 1#64 := by decide + have := hb hmv; have := hb hp + change ( + h.toUInt32.toUInt64.toBitVec + + hmv.toUInt64.toBitVec <<< 32#64 + + hp.toUInt64.toBitVec <<< 33#64 + + d.toUInt64.toBitVec <<< 40#64) >>> 40#64 = d.toUInt64.toBitVec + bv_decide + +theorem mkData_hasParam (H : d < 2 ^ 24) : (mkData h d hmv hp).hasParam = hp := by + rw [mkData_eq, mkData', if_neg (Nat.not_lt.2 (Nat.le_sub_one_of_lt H))] + simp [Data.hasParam, (· == ·), ← UInt64.toBitVec_inj] + have : h.toUInt32.toUInt64.toBitVec ≤ 0xffffffff#64 := Nat.le_of_lt_succ h.toUInt32.1.1.2 + have hb : ∀ (b : Bool), b.toUInt64.toBitVec ≤ 1#64 := by decide + have := hb hmv; have := hb hp + let L := (( + h.toUInt32.toUInt64.toBitVec + + hmv.toUInt64.toBitVec <<< 32#64 + + hp.toUInt64.toBitVec <<< 33#64 + + d.toUInt64.toBitVec <<< 40#64) >>> 33#64) &&& 1#64 + change decide (L = 1#64) = hp + rw [show L = hp.toUInt64.toBitVec by bv_decide] + cases hp <;> decide + +theorem mkData_hasMVar (H : d < 2 ^ 24) : (mkData h d hmv hp).hasMVar = hmv := by + rw [mkData_eq, mkData', if_neg (Nat.not_lt.2 (Nat.le_sub_one_of_lt H))] + simp [Data.hasMVar, (· == ·), ← UInt64.toBitVec_inj] + have : h.toUInt32.toUInt64.toBitVec ≤ 0xffffffff#64 := Nat.le_of_lt_succ h.toUInt32.1.1.2 + have hb : ∀ (b : Bool), b.toUInt64.toBitVec ≤ 1#64 := by decide + have := hb hmv; have := hb hp + let L := (( + h.toUInt32.toUInt64.toBitVec + + hmv.toUInt64.toBitVec <<< 32#64 + + hp.toUInt64.toBitVec <<< 33#64 + + d.toUInt64.toBitVec <<< 40#64) >>> 32#64) &&& 1#64 + change decide (L = 1#64) = hmv + rw [show L = hmv.toUInt64.toBitVec by bv_decide] + cases hmv <;> decide + +theorem ofLevel_of_not_hasParam (Us) {l : Level} + (hl : l.hasParam' = false) (hmv : l.hasMVar' = false) : + ∃ u', VLevel.ofLevel Us l = some u' := by + induction l <;> simp_all [hasParam', hasMVar', VLevel.ofLevel, exists_comm] + +def getUndefParam.F (ps : List Name) (l : Level) : StateT (Option Name) Id Bool := do + if !l.hasParam || (← get).isSome then + return false + if let .param n := l then + if n ∉ ps then + set (some n) + return true + +theorem getUndefParam_none {l : Level} (hmv : l.hasMVar' = false) : + l.getUndefParam Us = none → ∃ u', VLevel.ofLevel Us l = some u' := by + suffices ∀ s, ((l.forEach (getUndefParam.F Us)).run s).run.snd = none → s = none ∧ _ from + (this _ · |>.2) + have {l} (hmv : l.hasMVar' = false) {g} + (H : ∀ {s'}, (g.run s').run.snd = none → s' = none ∧ + (((getUndefParam.F Us l).run none).run = (true, none) → + ∃ u', VLevel.ofLevel Us l = some u')) (s) : + ((do if !(← getUndefParam.F Us l) then pure () else g) |>.run s).run.snd = none → + s = none ∧ ∃ u', VLevel.ofLevel Us l = some u' := by + simp; split <;> rename_i h + · simp; revert h + simp [getUndefParam.F]; split <;> [simp; split <;> [split <;> simp; simp]] + rintro rfl; simp at * + exact ofLevel_of_not_hasParam Us ‹_› hmv + · refine fun h' => let ⟨h1, h2⟩ := H h'; have := ?_; ⟨this, h2 ?_⟩ + · revert h h1 + simp [getUndefParam.F]; split <;> [simp; split <;> [split <;> simp; simp]] + · revert h h1; subst s + cases (getUndefParam.F Us l).run none; simp; rintro rfl rfl; rfl + have lt {n a} : n + 1 < a → n < a := by omega + induction l with ( + refine this hmv fun h => ?_; clear this + simp [hasMVar', VLevel.ofLevel, *] at *) + | succ _ ih => + have ⟨h, _, h1⟩ := ih hmv _ h + exact ⟨h, fun _ => ⟨_, _, h1, rfl⟩⟩ + | max _ _ ih1 ih2 | imax _ _ ih1 ih2 => + have ⟨h, _, h2⟩ := ih2 hmv.2 _ h + have ⟨h, _, h1⟩ := ih1 hmv.1 _ h + exact ⟨h, fun _ => ⟨_, _, h1, _, h2, rfl⟩⟩ + | param => + simp [getUndefParam.F, hasParam', List.idxOf_lt_length_iff, *] + split <;> simp [*] + | _ => simp [*] + +variable (s : Name → Level) in +def substParams' (red : Bool) : Level → Level + | .zero => .zero + | .succ v => .succ (substParams' (v.hasParam ∧ red) v) + | .max v₁ v₂ => + let red := (v₁.hasParam ∨ v₂.hasParam) ∧ red + (if red then mkLevelMax' else .max) (substParams' red v₁) (substParams' red v₂) + | .imax v₁ v₂ => + let red := (v₁.hasParam ∨ v₂.hasParam) ∧ red + (if red then mkLevelIMax' else .imax) (substParams' red v₁) (substParams' red v₂) + | .param n => s n + | u => u + +theorem substParams_eq_self {u : Level} (h : u.hasParam' = false) : + substParams' s red u = u := by + induction u generalizing red <;> simp_all [substParams', hasParam'] + +open private substParams.go from Lean.Level in +@[simp] theorem substParams_eq (u : Level) (s : Name → Option Level) : + substParams u s = substParams' (fun x => (s x).getD (.param x)) true u := by + unfold substParams + induction u <;> simp [substParams.go, substParams', hasParam', ← Bool.or_eq_true] <;> + split <;> simp [*, substParams_eq_self] <;> simp_all [substParams_eq_self] + +theorem substParams_id {u : Level} : + substParams' .param false u = u := by induction u <;> simp_all [substParams'] + +local notation "max'" => Max.max + +namespace Normalize + +attribute [local instance] Lean.Level.Normalize.namedOrdName + +local instance : Std.TransCmp (α := Name) compare := inferInstanceAs (Std.TransCmp Name.cmp) +local instance : Std.LawfulBEqCmp (α := Name) compare := + inferInstanceAs (Std.LawfulBEqCmp Name.cmp) +local instance : Std.LawfulBEqCmp (α := List Name) compare := + inferInstanceAs (Std.LawfulBEqCmp (List.compareLex Name.cmp)) + +instance : LawfulBEq VarNode where + rfl {a} := by cases a <;> simp! +instances [instBEqVarNode] + eq_of_beq {a b} := by cases a <;> cases b <;> simp! +instances [instBEqVarNode] + +@[reducible] local instance : Membership (List Name) NormLevel := + inferInstanceAs (Membership _ (Std.TreeMap _ _ compare)) + +@[reducible] local instance : GetElem? NormLevel (List Name) Node (fun m a => a ∈ m) := + inferInstanceAs (GetElem? (Std.TreeMap _ _ compare) ..) + +inductive Extend1 : List α → α → List α → Prop + | mk : Extend1 (l₁ ++ l₂) v (l₁ ++ v :: l₂) + +theorem Extend1.base : Extend1 l v (v::l) := .mk (l₁ := []) +theorem Extend1.cons (H : Extend1 l v l') : Extend1 (a::l) v (a::l') := + let .mk := H; .mk (l₁ := _::_) + +theorem Extend1.mem (H : Extend1 p a p') : b ∈ p' ↔ b = a ∨ b ∈ p := by cases H; simp [or_left_comm] + +theorem Extend1.length (H : Extend1 p a p') : p'.length = p.length + 1 := by + cases H; simp [Nat.add_assoc] + +theorem Extend1.of_mem (h : a ∈ p') : ∃ p, Extend1 p a p' := by + obtain ⟨_, _, rfl, _⟩ := List.eq_append_cons_of_mem h; exact ⟨_, .mk⟩ + +theorem Extend1.orderedInsert (H : orderedInsert cmp v p = some p') : Extend1 p v p' := by + induction p generalizing p' with simp [Normalize.orderedInsert] at H + | nil => exact H ▸ .base + | cons _ _ ih => + split at H <;> [(cases H; exact .base); cases H; skip] + simp at H; obtain ⟨_, H, rfl⟩ := H; exact (ih H).cons + +inductive Extend? : List α → α → List α → Prop + | mk1 : Extend1 l v l' → Extend? l v l' + | mk0 : v ∈ l → Extend? l v l + +theorem Extend?.cons (H : Extend? l v l') : Extend? (a::l) v (a::l') := by + cases H with + | mk1 H => exact .mk1 H.cons + | mk0 H => exact .mk0 (.tail _ H) + +theorem Extend?.mem (H : Extend? p a p') : b ∈ p' ↔ b = a ∨ b ∈ p := by + cases H with + | mk1 H => exact H.mem + | mk0 H => simp; rintro rfl; exact H + +theorem Extend?.orderedInsert [BEq α] [LawfulBEq α] [Std.LawfulBEqCmp (α := α) cmp] : + Extend? p v ((orderedInsert cmp v p).getD p) := by + induction p with simp [Normalize.orderedInsert] + | nil => exact .mk1 .base + | cons _ _ ih => + split + · exact .mk1 .base + · simp_all; exact .mk0 (.head _) + · revert ih; cases Normalize.orderedInsert .. <;> exact .cons + +section +variable (ls : List Name) (ρ : List Nat) in +def evalParam (x : Name) : Nat := + let i := ls.idxOf x; if i < ls.length then ρ[i]?.getD 0 else 0 + +theorem evalParam_eq (hv : ls.idxOf x < ls.length) : + evalParam ls ρ x = ρ[List.idxOf x ls]?.getD 0 := if_pos hv + +variable (ls : List Name) (ρ : List Nat) in +def VarNode.eval (l : VarNode) : Nat := evalParam ls ρ l.var + l.offset + +variable (ls : List Name) (ρ : List Nat) in +def Node.eval (l : Node) : Nat := + l.var.foldl (init := l.const) fun n v => max' n (v.eval ls ρ) + +theorem Node.eval_le : eval ls ρ l ≤ n ↔ + l.const ≤ n ∧ ∀ v ∈ l.var, v.eval ls ρ ≤ n := by + simp [eval, ← List.foldr_reverse]; simp only [← l.var.mem_reverse] + induction l.var.reverse with simp | cons a l + simp [Nat.max_le, and_comm, and_left_comm, *] + +variable (ls : List Name) (ρ : List Nat) in +def allNZ (path : List Name) : Bool := path.all (0 < evalParam ls ρ ·) + +theorem allNZ_cons : allNZ ls ρ (a :: path) ↔ + 0 < evalParam ls ρ a ∧ allNZ ls ρ path := by simp [allNZ] + +theorem allNZ_mono (H : ∀ x ∈ path, x ∈ path') : allNZ ls ρ path' → allNZ ls ρ path := by + simp [allNZ]; grind + +variable (ls : List Name) (ρ : List Nat) in +def evalPath (path : List Name) (n : Nat) : Nat := + if allNZ ls ρ path then n else 0 + +theorem evalPath_cons : evalPath ls ρ (a :: path) n = + evalPath ls ρ path (if 0 < evalParam ls ρ a then n else 0) := by + by_cases h : 0 < evalParam ls ρ a <;> simp [evalPath, allNZ_cons, h] + +theorem evalPath_max : + evalPath ls ρ path (max' m n) = max' (evalPath ls ρ path m) (evalPath ls ρ path n) := by + simp [evalPath]; split <;> simp + +theorem evalPath_mono (h : n ≤ m) : + evalPath ls ρ path n ≤ evalPath ls ρ path m := by + simp [evalPath]; split <;> simp [*] + +theorem evalPath_le : evalPath ls ρ path n ≤ m ↔ (allNZ ls ρ path → n ≤ m) := by + simp [evalPath]; split <;> simp [*] + +variable (ls : List Name) (ρ : List Nat) in +def NormLevel.eval (l : NormLevel) : Nat := + l.foldl (init := 0) fun n a b => max' n (evalPath ls ρ a (b.eval ls ρ)) + +theorem NormLevel.eval_le : eval ls ρ l ≤ n ↔ + ∀ a b, l.get? a = some b → evalPath ls ρ a (b.eval ls ρ) ≤ n := by + simp [eval, Std.TreeMap.foldl_eq_foldl_toList, ← List.foldr_reverse] + simp only [← Std.TreeMap.mem_toList_iff_getElem?_eq_some, ← l.toList.mem_reverse] + induction l.toList.reverse with simp | cons a l; let (a, b) := a + simp [or_imp, forall_and, Nat.max_le, and_comm, *] + +end + +theorem NormLevel.addVar_contains (H : acc.contains x) : (addVar v k path acc).contains x := by + simp_all [addVar, Std.TreeMap.mem_modify] + +theorem NormLevel.addNode_contains (H : acc.contains x) : (addNode v k path acc).contains x := by + simp [addNode, Std.TreeMap.mem_alter] at *; split <;> simp [*] + +theorem NormLevel.addNode_contains_self : (addNode v k path acc).contains path := by + simp [addNode]; split <;> simp + +theorem NormLevel.addConst_contains (H : acc.contains x) : (addConst k path acc).contains x := by + simp [addConst] at *; split <;> simp [H, Std.TreeMap.mem_alter]; split <;> simp + +theorem NormLevel.addConst_contains_self (h : k ≠ 0) (h2 : ¬(k = 1 ∧ path ≠ [])) : + (addConst k path acc).contains path := by + simp [addConst, h, h2]; split <;> simp + +theorem normalizeAux_contains (H : acc.contains x) : (normalizeAux u path k acc).contains x := by + unfold normalizeAux; split + · exact NormLevel.addConst_contains H + · exact NormLevel.addConst_contains H + · exact normalizeAux_contains H + · exact normalizeAux_contains (normalizeAux_contains H) + · exact normalizeAux_contains (normalizeAux_contains H) + · exact normalizeAux_contains (normalizeAux_contains H) + · exact normalizeAux_contains (normalizeAux_contains H) + · split <;> [skip; (dsimp; split)] + · exact normalizeAux_contains (NormLevel.addNode_contains (NormLevel.addConst_contains H)) + · exact normalizeAux_contains H + · exact normalizeAux_contains (NormLevel.addVar_contains H) + · exact H + · exact H + · split <;> [skip; split] + · exact NormLevel.addNode_contains (NormLevel.addConst_contains H) + · exact H + · exact NormLevel.addVar_contains H + +theorem imax_max : Lean.Nat.imax a (max' b c) = max' (Lean.Nat.imax a b) (Lean.Nat.imax a c) := by + simp [Lean.Nat.imax]; symm; split <;> simp [*]; split <;> simp [*, Nat.max_eq_max] + rw [Nat.max_left_comm b, ← Nat.max_assoc, Nat.max_self] + +theorem imax_imax : Lean.Nat.imax a (Lean.Nat.imax b c) = + max' (Lean.Nat.imax a c) (Lean.Nat.imax b c) := by + simp [Lean.Nat.imax]; by_cases h : c = 0 <;> simp [*, Nat.max_eq_max] + rw [Nat.max_left_comm c, Nat.max_self] + +protected theorem Extend?.allNZ (H : Extend? p a p') : allNZ ls ρ p' = allNZ ls ρ (a :: p) := by + rw [Bool.eq_iff_iff]; simp [allNZ, H.mem] + +protected theorem Extend?.evalPath (H : Extend? p a p') : + evalPath ls ρ p' = evalPath ls ρ (a :: p) := by ext n; simp [evalPath, H.allNZ] + +theorem ext_le {n m : Nat} (H : ∀ x, n ≤ x ↔ m ≤ x) : n = m := + Nat.le_antisymm ((H _).2 (Nat.le_refl _)) ((H _).1 (Nat.le_refl _)) + +theorem le_ext_le {n m : Nat} (H : ∀ x, n ≤ x → m ≤ x) : m ≤ n := H _ (Nat.le_refl _) + +/-- Condition sets are strictly sorted, which is what makes `subset` a decision procedure +for inclusion: it is built up by `orderedInsert` from the empty set. -/ +def Sorted (l : List Name) : Prop := l.Pairwise (compare · · = .lt) + +nonrec theorem Sorted.nil : Sorted [] := .nil + +theorem Sorted.of_cons (h : Sorted (a :: l)) : Sorted l := (List.pairwise_cons.1 h).2 + +theorem Sorted.head (h : Sorted (a :: l)) : ∀ b ∈ l, compare a b = .lt := + (List.pairwise_cons.1 h).1 + +theorem Sorted.erase (h : Sorted l) : Sorted (l.erase a) := h.sublist (List.erase_sublist ..) + +theorem Sorted.nodup (h : Sorted l) : l.Nodup := + h.imp <| by rintro _ _ hab rfl; rw [Std.ReflOrd.compare_self] at hab; cases hab + +theorem Sorted.orderedInsert (h : Sorted l) (he : orderedInsert Name.cmp a l = some l') : + Sorted l' := by + induction l generalizing l' with | nil => cases he; exact .cons (by simp) .nil | cons b l ih + simp only [Normalize.orderedInsert] at he + split at he <;> rename_i hab + · cases he + refine .cons (fun c hc => ?_) h + obtain rfl | hc := List.mem_cons.1 hc + · exact hab + · exact Std.TransCmp.lt_trans hab (h.head _ hc) + · cases he + · simp only [Option.map_eq_some_iff] at he + obtain ⟨l'', he, rfl⟩ := he + refine .cons (fun c hc => ?_) (ih h.of_cons he) + -- `c` is either `a`, which is above `b`, or an element of `l` + obtain rfl | hc := (Extend1.orderedInsert he).mem.1 hc + · exact Std.OrientedCmp.lt_of_gt hab + · exact h.head _ hc + +/-- The well-formedness invariant of the `NormLevel` maps produced by `normalizeAux`: +every variable recorded at a key is an element of that key, and every nonempty key `p` +extends another key of the map by a single variable that is recorded at `p`. +The latter is what makes the sublevels expressible by `imax` chains (see the reconstruction +comment in `Ix.Theory.Named.Level`), and it lets `addConst` drop `C(p, 1)` for `p ≠ []`. -/ +def NormLevel.WF (s : NormLevel) : Prop := + ∀ p n, s.get? p = some n → + (p ≠ [] → ∃ v p', Extend1 p' v p ∧ (p' = [] ∨ s.contains p') ∧ ∃ x ∈ n.var, x.var = v) ∧ + (∀ v ∈ n.var, v.var ∈ p) ∧ Sorted p + +theorem NormLevel.WF.of_mem (hm : v ∈ path) (H : WF s) (hp : s.contains path) : + ∃ path₁ path₂ n, (∀ x ∈ path₁, x ∈ path) ∧ + Extend1 path₁ v path₂ ∧ (path₁ = [] ∨ s.contains path₁) ∧ s.get? path₂ = some n ∧ + ∃ x ∈ n.var, x.var = v := by + generalize eq : path.length = n + induction n generalizing path with | zero => simp at eq; subst path; cases hm | succ n ih + have ⟨_, hp'⟩ := Option.isSome_iff_exists.1 (Std.TreeMap.isSome_getElem?_eq_contains.trans hp) + have ⟨_, _, a1, a2, a3⟩ := (H _ _ hp').1 (by rintro rfl; cases hm) + obtain rfl | hm := a1.mem.1 hm + · exact ⟨_, _, _, fun _ h => a1.mem.2 (.inr h), a1, a2, hp', a3⟩ + · -- the parent is in the map, since `v` occurs in it and so it is not the root + have ⟨_, _, _, b1, b2⟩ := ih hm (a2.resolve_left (by rintro rfl; cases hm)) + (by cases a1; simp at eq ⊢; exact Nat.succ_inj.1 eq) + exact ⟨_, _, _, fun _ h => a1.mem.2 (.inr (b1 _ h)), b2⟩ + +theorem NormLevel.WF.sortedOf {s : NormLevel} (wf : s.WF) (H : path = [] ∨ s.contains path) : + Sorted path := by + obtain rfl | h := H + · exact Sorted.nil + · obtain ⟨n, hn⟩ := Option.isSome_iff_exists.1 (Std.TreeMap.isSome_getElem?_eq_contains.trans h) + exact (wf _ _ hn).2.2 + +theorem VarNode.mem_addVar : + (∃ x ∈ VarNode.addVar v k l, x.var = u) ↔ v = u ∨ (∃ x ∈ l, x.var = u) := by + induction l with simp [addVar] | cons x l ih; split <;> simp_all [or_left_comm] + +theorem NormLevel.addVar_wf (hv : v ∈ path) (wf : acc.WF) : + (addVar v k path acc).WF := by + simp [addVar, WF, Std.TreeMap.getElem?_modify, Std.TreeMap.mem_modify] at wf ⊢ + intro p n; split <;> [simp; apply wf] + subst p; rintro _ h rfl; have ⟨a1, a2, a3⟩ := wf _ _ h + refine ⟨fun h => ?_, fun _ h => ?_, a3⟩ + · have ⟨_, _, b1, b2, b3⟩ := a1 h; exact ⟨_, _, b1, b2, VarNode.mem_addVar.2 (.inr b3)⟩ + · obtain eq | ⟨_, h, eq⟩ := VarNode.mem_addVar.1 ⟨_, h, rfl⟩ + · exact eq ▸ hv + · exact eq ▸ a2 _ h + +theorem NormLevel.addNode_wf (H : Extend1 path v path') (hs : Sorted path') + (hacc : path = [] ∨ acc.contains path) (wf : acc.WF) : (addNode v k path' acc).WF := by + simp [addNode, WF, Std.TreeMap.getElem?_alter, Std.TreeMap.mem_alter] at * + intro p n; split + · subst p; split <;> rintro ⟨⟩ <;> simp + · exact ⟨fun _ => ⟨_, _, H, hacc.imp id fun h _ => h, rfl⟩, H.mem.2 (.inl rfl), hs⟩ + · obtain ⟨a1, a2, a3⟩ := wf _ _ ‹_›; refine ⟨fun h => ?_, fun _ h => ?_, a3⟩ + · have ⟨_, _, b1, b2, b3⟩ := a1 h + exact ⟨_, _, b1, b2.imp id fun h _ => h, VarNode.mem_addVar.2 (.inr b3)⟩ + · obtain eq | ⟨_, h, eq⟩ := VarNode.mem_addVar.1 ⟨_, h, rfl⟩ + · exact H.mem.2 (.inl eq.symm) + · exact eq ▸ a2 _ h + · intro h; have ⟨a1, a2, a3⟩ := wf _ _ h; refine ⟨fun h => ?_, a2, a3⟩ + have ⟨_, _, b1, b2, b3⟩ := a1 h; refine ⟨_, _, b1, ?_, b3⟩ + split <;> [split <;> simp; exact b2] + +/-- `WF` survives an update that only adds keys and preserves each node's variables, provided +any key it adds is the root, where the parent condition is vacuous. -/ +theorem NormLevel.WF.update {s s' : NormLevel} (wf : s.WF) + (hk : ∀ q, s.contains q → s'.contains q) + (hv : ∀ p n, s'.get? p = some n → + (∃ n₀, s.get? p = some n₀ ∧ n.var = n₀.var) ∨ (p = [] ∧ n.var = [])) : s'.WF := by + intro p n hn + rcases hv p n hn with ⟨n₀, h₀, hvar⟩ | ⟨rfl, hvar⟩ + · obtain ⟨a1, a2, a3⟩ := wf _ _ h₀ + refine ⟨fun h => ?_, fun v hv => a2 v (hvar ▸ hv), a3⟩ + obtain ⟨v, p', b1, b2, b3⟩ := a1 h + exact ⟨v, p', b1, b2.imp id (hk _), hvar ▸ b3⟩ + · exact ⟨absurd rfl, by simp [hvar], Sorted.nil⟩ + +theorem NormLevel.addConst_wf (hp : path = [] ∨ acc.contains path) (H : acc.WF) : + (addConst k path acc).WF := by + simp only [addConst]; split <;> [exact H; skip] + refine H.update (fun q hq => ?_) fun p n hn => ?_ + · rw [Std.TreeMap.contains_alter]; split <;> [split <;> simp; simp [hq]] + · rw [Std.TreeMap.get?_eq_getElem?, Std.TreeMap.getElem?_alter] at hn + split at hn <;> [rename_i hpe; exact .inl ⟨n, hn, rfl⟩] + cases eq_of_beq (Std.LawfulBEqCmp.compare_eq_iff_beq.1 hpe) + -- `alter` creates a node only at the root, since otherwise `path` is already a key + match hpath : acc[path]?, hp with + | some n', _ => rw [hpath] at hn; cases hn; exact .inl ⟨n', hpath, rfl⟩ + | none, .inl hr => rw [hpath] at hn; cases hn; exact .inr ⟨hr, rfl⟩ + | none, .inr h => simp [Std.TreeMap.mem_iff_isSome_getElem?, hpath] at h + +theorem normalizeAux_wf (H : path = [] ∨ acc.contains path) (wf : acc.WF) : + (normalizeAux u path k acc).WF := by + unfold normalizeAux; split + · exact NormLevel.addConst_wf H wf + · exact NormLevel.addConst_wf H wf + · exact normalizeAux_wf H wf + · exact normalizeAux_wf (H.imp id normalizeAux_contains) (normalizeAux_wf H wf) + · exact normalizeAux_wf (H.imp id normalizeAux_contains) (normalizeAux_wf H wf) + · exact normalizeAux_wf (H.imp id normalizeAux_contains) (normalizeAux_wf H wf) + · exact normalizeAux_wf (H.imp id normalizeAux_contains) (normalizeAux_wf H wf) + · split <;> rename_i eq <;> [skip; (dsimp; split)] + · exact normalizeAux_wf (.inr NormLevel.addNode_contains_self) + (NormLevel.addNode_wf (.orderedInsert eq) ((wf.sortedOf H).orderedInsert eq) + (H.imp id NormLevel.addConst_contains) (NormLevel.addConst_wf H wf)) + · exact normalizeAux_wf H wf + · refine normalizeAux_wf (H.imp id NormLevel.addVar_contains) (NormLevel.addVar_wf ?_ wf) + exact (eq ▸ Extend?.orderedInsert).mem.2 (.inl rfl) + · exact wf + · exact wf + · split <;> rename_i eq <;> [skip; split] + · exact NormLevel.addNode_wf (.orderedInsert eq) ((wf.sortedOf H).orderedInsert eq) + (H.imp id NormLevel.addConst_contains) (NormLevel.addConst_wf H wf) + · exact wf + · exact NormLevel.addVar_wf ((eq ▸ Extend?.orderedInsert).mem.2 (.inl rfl)) wf + +theorem NormLevel.addConst_eval (H : path = [] ∨ acc.contains path) (wf : acc.WF) : + (addConst k path acc).eval ls ρ = max' (acc.eval ls ρ) (evalPath ls ρ path k) := by + simp [addConst]; split <;> rename_i h + · obtain rfl | ⟨rfl, hne⟩ := h + · simp [evalPath] + · -- `C(p, 1)` for `p ≠ []` is already dominated: `WF` puts a variable of `p` at `p`, + -- and along a nonzero path that variable is at least 1 + rw [Nat.max_eq_left]; refine evalPath_le.2 fun nz => le_ext_le fun n le => ?_ + have H := H.resolve_left hne + rw [← Std.TreeMap.isSome_getElem?_eq_contains, Option.isSome_iff_exists] at H + let ⟨v, H⟩ := H; have ⟨_, _, a1, a2, _, a3, rfl⟩ := (wf _ _ H).1 ‹_› + have := (Node.eval_le.1 (evalPath_le.1 (eval_le.1 le _ _ H) nz)).2 _ a3 + simp [allNZ] at nz + exact Nat.le_trans (nz _ (a1.mem.2 (.inl rfl))) (Nat.le_of_add_right_le this) + · refine ext_le fun x => ?_ + simp [eval_le, Nat.max_le, Std.TreeMap.getElem?_alter, evalPath_le, Node.eval_le] + refine ⟨fun H => ⟨fun a b h nz => ?_, fun nz => ?_⟩, fun ⟨H1, H2⟩ a b h nz => ?_⟩ + · have := H a; split at this + · subst a; rw [h] at this + obtain ⟨hc, hv⟩ := this _ rfl nz + exact ⟨Nat.le_trans (Nat.le_max_right ..) hc, hv⟩ + · exact this _ h nz + · have := H path; rw [if_pos rfl] at this; split at this <;> + refine Nat.le_trans ?_ ((this _ rfl nz).1) + · exact Nat.le_refl _ + · exact Nat.le_max_left .. + · split at h + · subst a; split at h <;> cases h <;> [exact ⟨H2 nz, by simp⟩; rename_i n hn] + obtain ⟨hc, hv⟩ := H1 _ _ hn nz + exact ⟨Nat.max_le.2 ⟨H2 nz, hc⟩, hv⟩ + · exact H1 _ _ h nz + +theorem VarNode.addVar_le : (∀ vn ∈ VarNode.addVar v k l, vn.eval ls ρ ≤ x) ↔ + evalParam ls ρ v + k ≤ x ∧ (∀ vn ∈ l, vn.eval ls ρ ≤ x) := by + simp [eval]; induction l with simp [VarNode.addVar] | cons vn l ih; split <;> simp [*] + · simp at *; subst v + rw [← and_assoc, ← Nat.max_le, Nat.add_max_add_left, Nat.max_comm, Nat.max_eq_max] + · rw [and_left_comm] + +theorem NormLevel.addNode_eval : (addNode v k path acc).eval ls ρ = + max' (acc.eval ls ρ) (evalPath ls ρ path (evalParam ls ρ v + k)) := by + refine ext_le fun x => ?_ + simp [addNode, eval_le, Std.TreeMap.getElem?_alter, evalPath_le, Node.eval_le, Nat.max_le] + refine ⟨fun H => ⟨fun a b h nz => ?_, fun nz => ?_⟩, fun ⟨H1, H2⟩ a b h nz => ?_⟩ + · have := H a; split at this + · subst a; simp_all [VarNode.addVar_le] + · exact this _ h nz + · have := H path; simp at this; split at this <;> specialize this _ rfl nz + · simp_all [VarNode.eval] + · simp_all [VarNode.addVar_le] + · split at h + · subst a; split at h <;> cases h + · simp_all [VarNode.eval] + · simp_all [VarNode.addVar_le]; grind + · grind + +theorem NormLevel.addVar_eval (H : acc.contains path) : (addVar v k path acc).eval ls ρ = + max' (acc.eval ls ρ) (evalPath ls ρ path (evalParam ls ρ v + k)) := by + refine ext_le fun x => ?_ + rw [← Std.TreeMap.isSome_getElem?_eq_contains, Option.isSome_iff_exists] at H; let ⟨v, H⟩ := H + simp [addVar, eval_le, Nat.max_le, Std.TreeMap.getElem?_modify, evalPath_le, Node.eval_le, H] + refine ⟨fun H => ⟨fun a b h nz => ?_, fun nz => ?_⟩, fun ⟨H1, H2⟩ a b h nz => ?_⟩ + · have := H a; split at this + · subst a; simp_all [VarNode.addVar_le] + · exact this _ h nz + · have := H path; simp at this; specialize this nz; simp_all [VarNode.addVar_le] + · split at h + · subst a; cases h; simp_all [VarNode.addVar_le]; grind + · grind + +/-- The invariant threaded through `normalizeAux`: the current path is either the root, which +`addConst` creates on demand, or already a key of the map, created by an earlier `addNode`. +`addVar` is only reached in the second case, since it runs only when `path` already contains +the variable being added. -/ +theorem normalizeAux_eval (hu : VLevel.ofLevel ls u = some u') + (H : path = [] ∨ acc.contains path) (wf : acc.WF) : + (normalizeAux u path k acc).eval ls ρ = + max' (acc.eval ls ρ) (evalPath ls ρ path (u'.eval ρ + k)) := by + unfold normalizeAux; split + · cases hu; simp [NormLevel.addConst_eval H wf, VLevel.eval] + · simp [VLevel.ofLevel] at hu; obtain ⟨_, hu, rfl⟩ := hu + simp [VLevel.eval, Lean.Nat.imax, NormLevel.addConst_eval H wf] + · simp [VLevel.ofLevel] at hu; obtain ⟨_, hu, rfl⟩ := hu + rw [normalizeAux_eval hu H wf, Nat.add_succ, ← Nat.succ_add]; rfl + · simp [VLevel.ofLevel] at hu; obtain ⟨_, hu, _, hv, rfl⟩ := hu + rw [normalizeAux_eval hv (H.imp id normalizeAux_contains) (normalizeAux_wf H wf), + normalizeAux_eval hu H wf, Nat.max_assoc, ← evalPath_max, Nat.add_max_add_right]; rfl + · simp [VLevel.ofLevel] at hu; obtain ⟨_, hu, _, ⟨_, hv, rfl⟩, rfl⟩ := hu + rw [normalizeAux_eval hv (H.imp id normalizeAux_contains) (normalizeAux_wf H wf), + normalizeAux_eval hu H wf, Nat.max_assoc, Nat.add_succ, ← Nat.succ_add, + ← evalPath_max, Nat.add_max_add_right]; rfl + · rename_i u v w + simp [VLevel.ofLevel] at hu; obtain ⟨_, hu, _, ⟨_, hv, _, hw, rfl⟩, rfl⟩ := hu + rw [normalizeAux_eval (by simpa [VLevel.ofLevel] using ⟨_, hu, _, hw, rfl⟩) + (H.imp id normalizeAux_contains) (normalizeAux_wf H wf), + normalizeAux_eval (by simpa [VLevel.ofLevel] using ⟨_, hu, _, hv, rfl⟩) H wf, + Nat.max_assoc, ← evalPath_max, Nat.add_max_add_right]; simp [VLevel.eval, imax_max] + · rename_i u v w + simp [VLevel.ofLevel] at hu; obtain ⟨_, hu, _, ⟨_, hv, _, hw, rfl⟩, rfl⟩ := hu + rw [normalizeAux_eval (by simpa [VLevel.ofLevel] using ⟨_, hv, _, hw, rfl⟩) + (H.imp id normalizeAux_contains) (normalizeAux_wf H wf), + normalizeAux_eval (by simpa [VLevel.ofLevel] using ⟨_, hu, _, hw, rfl⟩) H wf, + Nat.max_assoc, ← evalPath_max, Nat.add_max_add_right]; simp [VLevel.eval, imax_imax] + · rename_i u v + simp [VLevel.ofLevel] at hu; obtain ⟨_, hu, _, ⟨hv, rfl⟩, rfl⟩ := hu + have := Extend?.orderedInsert (cmp := Name.cmp) (p := path) (v := v) + split <;> rename_i h <;> simp [h] at this + · rw [normalizeAux_eval hu (.inr NormLevel.addNode_contains_self) + (NormLevel.addNode_wf (.orderedInsert h) ((wf.sortedOf H).orderedInsert h) + (H.imp id NormLevel.addConst_contains) (NormLevel.addConst_wf H wf)), + NormLevel.addNode_eval, NormLevel.addConst_eval H wf, Nat.max_assoc, + Nat.max_assoc, ← evalPath_max, this.evalPath, evalPath_cons, ← evalPath_max, + Nat.add_max_add_right]; congr 2 + simp [VLevel.eval, ← evalParam_eq hv, Lean.Nat.imax] + cases evalParam .. <;> simp [Nat.max_eq_max, Nat.max_comm] + · have hne : path ≠ [] := by rintro rfl; simp [orderedInsert] at h + dsimp; split + · rw [normalizeAux_eval hu H wf] + simp [evalPath]; split <;> [rename_i nz; simp] + have hm := this.mem.2 (.inl rfl) + obtain ⟨p1, p2, w1, a1, a2, a3, a4, z, a5, rfl⟩ := wf.of_mem hm (H.resolve_left hne) + refine ext_le fun n => ?_; simp [Nat.max_le, NormLevel.eval_le]; intro he + have := Node.eval_le.1 (evalPath_le.1 (he _ _ a4) + (allNZ_mono (fun _ h => (a2.mem.1 h).elim (· ▸ hm) (a1 _)) nz)) |>.2 _ a5 + simp [allNZ] at nz; specialize nz _ hm + simp [VLevel.eval, Lean.Nat.imax]; simp [← evalParam_eq hv, VarNode.eval] at this ⊢ + revert this nz; cases evalParam .. <;> simp [Nat.max_eq_max]; omega + · rw [normalizeAux_eval hu (H.imp id NormLevel.addVar_contains) + (NormLevel.addVar_wf (this.mem.2 (.inl rfl)) wf), + NormLevel.addVar_eval (H.resolve_left hne), Nat.max_assoc, ← evalPath_max, Nat.add_max_add_right, + this.evalPath, evalPath_cons, evalPath_cons]; congr 2 + split <;> simp [VLevel.eval, Lean.Nat.imax] + rename_i h; revert h; simp [← evalParam_eq hv] + cases evalParam .. <;> simp [Nat.max_eq_max, Nat.max_comm] + · cases hu + · simp [VLevel.ofLevel] at hu + · rename_i v; simp [VLevel.ofLevel] at hu; obtain ⟨hv, rfl⟩ := hu + have := Extend?.orderedInsert (cmp := Name.cmp) (p := path) (v := v) + split <;> rename_i h <;> simp [h] at this + · rw [NormLevel.addNode_eval, NormLevel.addConst_eval H wf, Nat.max_assoc, + this.evalPath, evalPath_cons, ← evalPath_max] + simp [VLevel.eval, ← evalParam_eq hv]; congr 2; split <;> simp; omega + have hne : path ≠ [] := by rintro rfl; simp [orderedInsert] at h + split + · simp [evalPath]; split <;> [rename_i nz; simp] + have hm := this.mem.2 (.inl rfl) + obtain ⟨p1, p2, w1, a1, a2, a3, a4, z, a5, rfl⟩ := wf.of_mem hm (H.resolve_left hne) + refine ext_le fun n => ?_; simp [Nat.max_le, NormLevel.eval_le]; intro he + have := Node.eval_le.1 (evalPath_le.1 (he _ _ a4) + (allNZ_mono (fun _ h => (a2.mem.1 h).elim (· ▸ hm) (a1 _)) nz)) |>.2 _ a5 + simp [allNZ] at nz; specialize nz _ hm + simp [VLevel.eval]; simp [← evalParam_eq hv, VarNode.eval] at this ⊢ + revert this nz; cases evalParam .. <;> simp; omega + · rw [NormLevel.addVar_eval (H.resolve_left hne), this.evalPath, evalPath_cons, + evalPath_cons] + congr 2; split <;> simp [VLevel.eval, ← evalParam_eq hv] + +theorem subset_length (H : subset cmp l₁ l₂) : l₁.length ≤ l₂.length := by + induction l₂ generalizing l₁ with | nil => cases l₁ <;> simp_all [subset] | cons y l₂ ih + cases l₁ with | nil => simp | cons x l₁ + simp only [subset] at H; split at H + · cases H + · have := ih H; simp only [List.length_cons]; omega + · have := ih H; simp only [List.length_cons] at this ⊢; omega + +theorem subset_mem [BEq α] [LawfulBEq α] [Std.LawfulBEqCmp (α := α) cmp] + (H : subset cmp l₁ l₂) (h : a ∈ l₁) : a ∈ l₂ := by + induction l₂ generalizing l₁ with | nil => cases l₁ <;> simp_all [subset] | cons y l₂ ih + cases l₁ with| nil => cases h | cons x l₁ + simp only [subset] at H; split at H + · cases H + · rename_i h'; rw [Std.LawfulBEqCmp.compare_eq_iff_beq] at h' + cases eq_of_beq h' + rcases List.mem_cons.1 h with rfl | h + · exact .head _ + · exact .tail _ (ih H h) + · exact .tail _ (ih H h) + +theorem subset_eq [BEq α] [LawfulBEq α] [Std.LawfulBEqCmp (α := α) cmp] + (H : subset cmp l₁ l₂) (hl : l₁.length = l₂.length) : l₁ = l₂ := by + induction l₂ generalizing l₁ with | nil => cases l₁ <;> simp_all [subset] | cons y l₂ ih + cases l₁ with | nil => cases hl | cons x l₁ + simp only [subset] at H; simp only [List.length_cons] at hl + split at H + · cases H + · rename_i h'; rw [Std.LawfulBEqCmp.compare_eq_iff_beq] at h' + cases eq_of_beq h'; rw [ih H (by omega)] + · exact absurd (subset_length H) (by simp only [List.length_cons]; omega) + +/-- On sorted lists, `subset` decides inclusion. -/ +theorem subset_of_sorted (h₁ : Sorted l₁) (h₂ : Sorted l₂) (h : ∀ x ∈ l₁, x ∈ l₂) : + subset compare l₁ l₂ := by + induction l₂ generalizing l₁ with + | nil => cases l₁ with | nil => rfl | cons x l₁ => cases h x (.head _) + | cons y l₂ ih + cases l₁ with | nil => rfl | cons x l₁ + simp only [subset] + have hxy := h x (.head _) + split <;> rename_i hc + · -- `x < y` is impossible: `x` is in `y :: l₂`, whose elements are all `≥ y` + obtain rfl | hx := List.mem_cons.1 hxy + · rw [Std.ReflOrd.compare_self] at hc; cases hc + · exact absurd (h₂.head _ hx) (by rw [Std.OrientedCmp.gt_of_lt hc]; simp) + · rw [Std.LawfulBEqCmp.compare_eq_iff_beq] at hc + cases eq_of_beq hc + refine ih h₁.of_cons h₂.of_cons fun z hz => ?_ + obtain rfl | hz' := List.mem_cons.1 (h z (.tail _ hz)) + · exact absurd (h₁.head _ hz) (by rw [Std.ReflOrd.compare_self]; simp) + · exact hz' + · refine ih h₁ h₂.of_cons fun z hz => ?_ + obtain rfl | hz' := List.mem_cons.1 (h z hz) + · obtain rfl | hz := List.mem_cons.1 hz + · exact absurd hc (by rw [Std.ReflOrd.compare_self]; simp) + · exact absurd (h₁.head _ hz) (by + rw [Std.OrientedCmp.gt_of_lt (Std.OrientedCmp.lt_of_gt hc)]; simp) + · exact hz' + +theorem subsumeVars_subset (h : x ∈ subsumeVars vs₁ vs₂) : x ∈ vs₁ := by + induction vs₁ generalizing vs₂ with | nil => simp_all [subsumeVars] | cons a vs₁ ih + induction vs₂ with | nil => simp_all [subsumeVars] | cons b vs₂ ih₂ + simp only [subsumeVars] at h; split at h + · obtain rfl | h := List.mem_cons.1 h + · exact .head _ + · exact .tail _ (ih h) + · split at h <;> [exact .tail _ (ih h); skip] + obtain rfl | h := List.mem_cons.1 h + · exact .head _ + · exact .tail _ (ih h) + · exact ih₂ h + +theorem subsumeVars_dominated (h₁ : x ∈ vs₁) (h₂ : x ∉ subsumeVars vs₁ vs₂) : + ∃ y ∈ vs₂, y.var = x.var ∧ x.offset ≤ y.offset := by + induction vs₁ generalizing vs₂ with | nil => cases h₁ | cons a vs₁ ih + induction vs₂ with | nil => exact absurd h₁ (by simpa [subsumeVars] using h₂) | cons b vs₂ ih₂ + simp only [subsumeVars] at h₂; split at h₂ + · obtain rfl | h₁ := List.mem_cons.1 h₁ + · cases h₂ (.head _) + · have ⟨y, hy, e, le⟩ := ih h₁ fun h => h₂ (.tail _ h) + exact ⟨y, hy, e, le⟩ + · rename_i heq; split at h₂ + · obtain rfl | h₁ := List.mem_cons.1 h₁ + · rw [Std.LawfulBEqCmp.compare_eq_iff_beq] at heq + exact ⟨b, .head _, (eq_of_beq heq).symm, ‹_›⟩ + · have ⟨y, hy, e, le⟩ := ih h₁ h₂ + exact ⟨y, .tail _ hy, e, le⟩ + · obtain rfl | h₁ := List.mem_cons.1 h₁ + · cases h₂ (.head _) + · have ⟨y, hy, e, le⟩ := ih h₁ fun h => h₂ (.tail _ h) + exact ⟨y, .tail _ hy, e, le⟩ + · have ⟨y, hy, e, le⟩ := ih₂ h₂ + exact ⟨y, .tail _ hy, e, le⟩ + +theorem le_foldl_max {vs : List VarNode} + (h : c ≤ vs.foldl (·.max ·.offset) n + 1) : c ≤ n + 1 ∨ ∃ y ∈ vs, c ≤ y.offset + 1 := by + induction vs generalizing n with | nil => exact .inl h | cons x vs ih + obtain h | ⟨y, hy, h⟩ := ih h + · refine (Nat.le_total x.offset n).imp (fun h' => ?_) (fun h' => ⟨x, .head _, ?_⟩) + · simp [Nat.max_eq_left h'] at h; omega + · simp [Nat.max_eq_right h'] at h; omega + · exact .inr ⟨y, .tail _ hy, h⟩ + +theorem Node.const_le_eval {l : Node} : l.const ≤ Node.eval ls ρ l := + (Node.eval_le.1 (Nat.le_refl _)).1 + +theorem Node.var_le_eval {l : Node} (h : x ∈ l.var) : + VarNode.eval ls ρ x ≤ Node.eval ls ρ l := + (Node.eval_le.1 (Nat.le_refl _)).2 _ h + +theorem Node.eval_empty {l : Node} (H : l.isEmpty) : Node.eval ls ρ l = 0 := by + simp [Node.isEmpty] at H; simp [eval, H.1, H.2] + +theorem NormLevel.eval_filter {m : NormLevel} : + NormLevel.eval ls ρ (m.filter fun _ n => !n.isEmpty) = m.eval ls ρ := by + refine ext_le fun x => ?_ + simp only [eval_le, Std.TreeMap.get?_eq_getElem?, Std.TreeMap.getElem?_filter] + refine ⟨fun H a b h => ?_, fun H a b h => ?_⟩ + · by_cases he : b.isEmpty + · simp [evalPath_le, Node.eval_empty he] + · exact H a b (by simp [h, he]) + · exact H _ _ (Option.eq_some_of_pfilter_eq_some h) + +theorem subsumeVars_eval (H : ∀ v ∈ vs₂, VarNode.eval ls ρ v ≤ n) : + (∀ v ∈ subsumeVars vs₁ vs₂, VarNode.eval ls ρ v ≤ n) ↔ + ∀ v ∈ vs₁, VarNode.eval ls ρ v ≤ n := by + refine ⟨fun h v hv => ?_, fun h v hv => h _ (subsumeVars_subset hv)⟩ + by_cases hs : v ∈ subsumeVars vs₁ vs₂ <;> [exact h _ hs; skip] + have ⟨y, hy, e, le⟩ := subsumeVars_dominated hv hs + exact Nat.le_trans (by simp [VarNode.eval, e]; omega) (H _ hy) + +theorem Node.subsumeBy_const_eq {same : Bool} {n₁ n₂ : Node} : + (n₁.subsumeBy same n₂).const = + if n₁.const = 0 || + (same || n₁.const > n₂.const) && + (n₂.var.isEmpty || n₁.const > n₂.var.foldl (·.max ·.offset) 0 + 1) + then n₁.const else 0 := by + simp only [Node.subsumeBy]; split <;> split <;> rfl + +theorem Node.subsumeBy_var_eq {same : Bool} {n₁ n₂ : Node} : + (n₁.subsumeBy same n₂).var = + if same || n₂.var.isEmpty then n₁.var else subsumeVars n₁.var n₂.var := by + simp only [Node.subsumeBy]; split <;> split <;> simp + +theorem Node.subsumeBy_var_subset {same : Bool} + (h : x ∈ (Node.subsumeBy same n₁ n₂).var) : x ∈ n₁.var := by + rw [Node.subsumeBy_var_eq] at h; split at h <;> [exact h; exact subsumeVars_subset h] + +theorem Node.subsumeBy_const_cases {same : Bool} (n₁ n₂ : Node) : + (n₁.subsumeBy same n₂).const = n₁.const ∨ (n₁.subsumeBy same n₂).const = 0 := by + rw [Node.subsumeBy_const_eq]; split <;> [exact .inl rfl; exact .inr rfl] + +theorem Node.subsumeBy_eval_le {same : Bool} : + Node.eval ls ρ (n₁.subsumeBy same n₂) ≤ Node.eval ls ρ n₁ := by + refine Node.eval_le.2 ⟨?_, fun v h => Node.var_le_eval (Node.subsumeBy_var_subset h)⟩ + obtain h | h := Node.subsumeBy_const_cases (same := same) n₁ n₂ + · exact h ▸ Node.const_le_eval + · simp [h] + +/-- If `subsumeBy` dropped the constant, the drop was justified: the constant is dominated +by the constant of `n₂` (only possible when the two keys differ), or by a variable of `n₂`. -/ +theorem Node.subsumeBy_const_drop {same : Bool} + (h : (Node.subsumeBy same n₁ n₂).const ≠ n₁.const) : + same = false ∧ n₁.const ≤ n₂.const ∨ ∃ y ∈ n₂.var, n₁.const ≤ y.offset + 1 := by + rw [Node.subsumeBy_const_eq] at h + split at h <;> [cases h rfl; rename_i hc] + rw [Bool.or_eq_true, not_or] at hc + obtain ⟨-, hc⟩ := hc + rw [Bool.and_eq_true, Decidable.not_and_iff_not_or_not] at hc + obtain hc | hc := hc <;> rw [Bool.or_eq_true, not_or] at hc <;> obtain ⟨h1, h2⟩ := hc + · exact .inl ⟨by simpa using h1, by simpa [Nat.not_lt] using h2⟩ + · have hne : n₂.var ≠ [] := fun e => h1 (by simp [e]) + have h2 : n₁.const ≤ n₂.var.foldl (·.max ·.offset) 0 + 1 := by + simpa [Nat.not_lt] using h2 + obtain h | h := le_foldl_max (c := n₁.const) (n := 0) h2 + · obtain ⟨y, hy⟩ := List.exists_mem_of_ne_nil _ hne + exact .inr ⟨y, hy, by omega⟩ + · exact .inr h + +/-- The domination step is exact against a node bounded by `m`: everything `subsumeBy` +drops from `n₁` is dominated by a sublevel of `n₂`, and `n₂` evaluates to at most `m`. +Domination of the constant by a variable needs that variable to evaluate to at least its +offset plus one, which is why the condition set must be all-nonzero (`hnz`). -/ +theorem Node.subsumeBy_eval_iff {same : Bool} {n₁ n₂ : Node} {m : Nat} + (hnz : ∀ v ∈ n₂.var, 0 < evalParam ls ρ v.var) (h₂ : Node.eval ls ρ n₂ ≤ m) : + Node.eval ls ρ (n₁.subsumeBy same n₂) ≤ m ↔ Node.eval ls ρ n₁ ≤ m := by + have hvar₂ v (hv : v ∈ n₂.var) : VarNode.eval ls ρ v ≤ m := + Nat.le_trans (Node.var_le_eval hv) h₂ + refine ⟨fun h => ?_, fun h => Nat.le_trans Node.subsumeBy_eval_le h⟩ + rw [Node.eval_le] at h ⊢ + refine ⟨?_, fun x hx => ?_⟩ + · by_cases hc : (n₁.subsumeBy same n₂).const = n₁.const + · exact hc ▸ h.1 + obtain ⟨-, hle⟩ | ⟨y, hy, hle⟩ := Node.subsumeBy_const_drop hc + · exact Nat.le_trans hle (Nat.le_trans Node.const_le_eval h₂) + · refine Nat.le_trans ?_ (hvar₂ _ hy) + have := hnz _ hy; simp only [VarNode.eval]; omega + · rw [Node.subsumeBy_var_eq] at h + split at h + · exact h.2 _ hx + · exact (subsumeVars_eval hvar₂).1 h.2 _ hx + +theorem Node.subsume_const_eq : (Node.subsume p₁ n₁ p₂ n₂).const = + if !subset compare p₂ p₁ || + (n₁.const = 0 || + (p₁.length == p₂.length || n₁.const > n₂.const) && + (n₂.var.isEmpty || n₁.const > n₂.var.foldl (·.max ·.offset) 0 + 1)) + then n₁.const else 0 := by + simp only [Node.subsume] + cases hs : subset compare p₂ p₁ <;> + simp only [reduceIte, Bool.not_true, Bool.not_false, Bool.false_or, Bool.true_or] + · rfl + · exact subsumeBy_const_eq + +theorem Node.subsume_var_eq : (Node.subsume p₁ n₁ p₂ n₂).var = + if !subset compare p₂ p₁ || (p₁.length == p₂.length || n₂.var.isEmpty) + then n₁.var else subsumeVars n₁.var n₂.var := by + simp only [Node.subsume] + cases hs : subset compare p₂ p₁ <;> + simp only [reduceIte, Bool.not_true, Bool.not_false, Bool.false_or, Bool.true_or] + · rfl + · exact subsumeBy_var_eq + +theorem Node.subsume_var_subset (h : x ∈ (Node.subsume p₁ n₁ p₂ n₂).var) : x ∈ n₁.var := by + rw [Node.subsume] at h; split at h <;> [exact subsumeBy_var_subset h; exact h] + +theorem Node.subsume_const_cases (p₁ n₁ p₂ n₂) : + (Node.subsume p₁ n₁ p₂ n₂).const = n₁.const ∨ (Node.subsume p₁ n₁ p₂ n₂).const = 0 := by + rw [Node.subsume]; split <;> [exact subsumeBy_const_cases ..; exact .inl rfl] + +theorem Node.subsume_eval_le : + Node.eval ls ρ (Node.subsume p₁ n₁ p₂ n₂) ≤ Node.eval ls ρ n₁ := by + rw [Node.subsume]; split <;> [exact subsumeBy_eval_le; exact Nat.le_refl _] + +/-- If `subsume` dropped the constant, the drop was justified: the constant is dominated +by the constant of `n₂` at a strictly smaller key, or by a variable of `n₂`. -/ +theorem Node.subsume_const_drop (h : (Node.subsume p₁ n₁ p₂ n₂).const ≠ n₁.const) : + subset compare p₂ p₁ ∧ + (p₁.length ≠ p₂.length ∧ n₁.const ≤ n₂.const ∨ ∃ y ∈ n₂.var, n₁.const ≤ y.offset + 1) := by + rw [Node.subsume] at h + split at h <;> [skip; cases h rfl] + refine ⟨‹_›, (subsumeBy_const_drop h).imp_left fun ⟨he, hc⟩ => ⟨fun e => ?_, hc⟩⟩ + simp [e] at he + +/-- If `subsume` changed the variable list, the change was `subsumeVars` against the +variables of `n₂` at a strictly smaller key. -/ +theorem Node.subsume_var_cases (p₁ n₁ p₂ n₂) : + (Node.subsume p₁ n₁ p₂ n₂).var = n₁.var ∨ + (subset compare p₂ p₁ ∧ p₁.length ≠ p₂.length ∧ + (Node.subsume p₁ n₁ p₂ n₂).var = subsumeVars n₁.var n₂.var) := by + rw [Node.subsume_var_eq]; split + · exact .inl rfl + · rename_i hc + rw [Bool.or_eq_true, Bool.or_eq_true, not_or, not_or] at hc + obtain ⟨hs, hlen, -⟩ := hc + have hsub : subset compare p₂ p₁ := by revert hs; cases subset compare p₂ p₁ <;> simp + exact .inr ⟨hsub, fun e => hlen (by simp [e]), rfl⟩ + +theorem NormLevel.minimize_var_subset {acc : NormLevel} + (h : x ∈ (acc.minimize p₁ n₁).var) : x ∈ n₁.var := by + rw [minimize, Std.TreeMap.foldl_eq_foldl_toList] at h + generalize acc.toList = l at h + induction l generalizing n₁ with | nil => exact h | cons a l ih + exact Node.subsume_var_subset (ih h) + +theorem NormLevel.minimize_eval_le {acc : NormLevel} : + Node.eval ls ρ (acc.minimize p₁ n₁) ≤ n₁.eval ls ρ := by + rw [minimize, Std.TreeMap.foldl_eq_foldl_toList] + generalize acc.toList = l + induction l generalizing n₁ with | nil => exact Nat.le_refl _ | cons a l ih + exact Nat.le_trans (ih (n₁ := Node.subsume p₁ n₁ a.1 a.2)) Node.subsume_eval_le + +/-- Minimizing a node against the rest of the map preserves its contribution to the total, +assuming every other entry's contribution is already bounded by `m`. -/ +theorem NormLevel.minimize_eval_iff {acc : NormLevel} {p₁ : List Name} {n₁ : Node} {m : Nat} + (wfa : ∀ p n, acc.get? p = some n → ∀ v ∈ n.var, v.var ∈ p) + (h₁ : acc.get? p₁ = some n₁) + (hacc : ∀ p n, p ≠ p₁ → acc.get? p = some n → evalPath ls ρ p (Node.eval ls ρ n) ≤ m) + (nz : allNZ ls ρ p₁) : + Node.eval ls ρ (acc.minimize p₁ n₁) ≤ m ↔ Node.eval ls ρ n₁ ≤ m := by + have wf₁ := wfa _ _ h₁ + have evalq p₂ n₂ (hne : p₂ ≠ p₁) (h₂ : acc.get? p₂ = some n₂) (hsub : subset compare p₂ p₁) : + Node.eval ls ρ n₂ ≤ m := by + have := hacc _ _ hne h₂ + rw [evalPath_le] at this + exact this (allNZ_mono (fun _ h => subset_mem hsub h) nz) + -- a variable dominated at a different key of the map is bounded by `m` + have domle (x : VarNode) : (∃ p₂ n₂ y, p₂ ≠ p₁ ∧ acc.get? p₂ = some n₂ ∧ + subset compare p₂ p₁ ∧ y ∈ n₂.var ∧ y.var = x.var ∧ x.offset ≤ y.offset) → + VarNode.eval ls ρ x ≤ m := fun ⟨p₂, n₂, y, hne, h₂, hsub, hy, e, le⟩ => by + refine Nat.le_trans ?_ (Nat.le_trans (Node.var_le_eval hy) (evalq _ _ hne h₂ hsub)) + simp only [VarNode.eval, ← e]; omega + refine ⟨fun hf => ?_, fun h => Nat.le_trans minimize_eval_le h⟩ + rw [minimize, Std.TreeMap.foldl_eq_foldl_toList] at hf + have hmem pn (h : pn ∈ acc.toList) : acc.get? pn.1 = some pn.2 := + Std.TreeMap.get?_eq_getElem? .. ▸ Std.TreeMap.mem_toList_iff_getElem?_eq_some.1 h + generalize acc.toList = l at hf hmem + -- fold invariant: vars of the current node come from `n₁`; the constant is intact or + -- justifiably dropped; every original variable is dominated by a current one or elsewhere + suffices ∀ n1, (∀ x ∈ n1.var, x ∈ n₁.var) → + (n1.const = n₁.const ∨ (n1.const = 0 ∧ + ((∃ y ∈ n₁.var, n₁.const ≤ y.offset + 1) ∨ + ∃ p₂ n₂, p₂ ≠ p₁ ∧ acc.get? p₂ = some n₂ ∧ subset compare p₂ p₁ ∧ + n₁.const ≤ Node.eval ls ρ n₂))) → + (∀ x ∈ n₁.var, (∃ y ∈ n1.var, y.var = x.var ∧ x.offset ≤ y.offset) ∨ + ∃ p₂ n₂ y, p₂ ≠ p₁ ∧ acc.get? p₂ = some n₂ ∧ subset compare p₂ p₁ ∧ + y ∈ n₂.var ∧ y.var = x.var ∧ x.offset ≤ y.offset) → + Node.eval ls ρ (List.foldl (fun n1 pn => Node.subsume p₁ n1 pn.1 pn.2) n1 l) ≤ m → + Node.eval ls ρ n₁ ≤ m from + this n₁ (fun _ => id) (.inl rfl) (fun x h => .inl ⟨x, h, rfl, Nat.le_refl _⟩) hf + clear hf + induction l with intro n1 hL hK hJ hf + | nil => + refine Node.eval_le.2 ⟨?_, fun x hx => ?_⟩ + · obtain hK | ⟨-, hK | ⟨p₂, n₂, hne, h₂, hsub, hc⟩⟩ := hK + · exact Nat.le_trans (hK ▸ Node.const_le_eval) hf + · obtain ⟨y, hy, hc⟩ := hK + obtain ⟨y', hy', e, le⟩ | hd := hJ _ hy + · refine Nat.le_trans ?_ (Nat.le_trans (Node.var_le_eval hy') hf) + have : 0 < evalParam ls ρ y'.var := by + simp [allNZ] at nz; exact nz _ (wf₁ _ (hL _ hy')) + simp only [VarNode.eval]; omega + · refine Nat.le_trans ?_ (domle _ hd) + obtain ⟨p₂, n₂, y', hne, h₂, hsub, hy', e, le⟩ := hd + have : 0 < evalParam ls ρ y'.var := by + simp [allNZ] at nz; exact nz _ (subset_mem hsub (wfa _ _ h₂ _ hy')) + simp only [VarNode.eval, ← e]; omega + · exact Nat.le_trans hc (evalq _ _ hne h₂ hsub) + · obtain ⟨y, hy, e, le⟩ | hd := hJ _ hx + · refine Nat.le_trans ?_ (Nat.le_trans (Node.var_le_eval hy) hf) + simp only [VarNode.eval, ← e]; omega + · exact domle _ hd + | cons pn l ih => + simp only [List.mem_cons, forall_eq_or_imp] at hmem + obtain ⟨h₂, hmem'⟩ := hmem + refine ih hmem' _ (fun x h => hL _ (Node.subsume_var_subset h)) ?_ (fun x hx => ?_) hf + · by_cases hc : (Node.subsume p₁ n1 pn.1 pn.2).const = n1.const + · rw [hc]; exact hK + obtain ⟨hsub, hd⟩ := Node.subsume_const_drop hc + obtain heq | hzero := Node.subsume_const_cases p₁ n1 pn.1 pn.2 + · cases hc heq + have hc1 : n1.const = n₁.const := by + rcases hK with h | ⟨h, -⟩ + · exact h + · cases hc (hzero.trans h.symm) + refine .inr ⟨hzero, ?_⟩ + by_cases hpe : pn.1 = p₁ + · subst hpe + have : pn.2 = n₁ := by cases h₁.symm.trans h₂; rfl + subst this + obtain ⟨hne', -⟩ | ⟨y, hy, hle⟩ := hd + · exact absurd rfl hne' + · exact .inl ⟨y, hy, hc1 ▸ hle⟩ + · refine .inr ⟨pn.1, pn.2, hpe, h₂, hsub, ?_⟩ + obtain ⟨-, hle⟩ | ⟨y, hy, hle⟩ := hd + · exact hc1 ▸ Nat.le_trans hle Node.const_le_eval + · refine hc1 ▸ Nat.le_trans hle ?_ + have : 0 < evalParam ls ρ y.var := by + simp [allNZ] at nz + exact nz _ (subset_mem hsub (wfa _ _ h₂ _ hy)) + refine Nat.le_trans ?_ (Node.var_le_eval hy) + simp only [VarNode.eval]; omega + · obtain ⟨y, hy, e, le⟩ | hd := hJ _ hx <;> [skip; exact .inr hd] + obtain hv | ⟨hsub, hlen, hv⟩ := Node.subsume_var_cases p₁ n1 pn.1 pn.2 + · exact .inl ⟨y, hv ▸ hy, e, le⟩ + by_cases hy' : y ∈ (Node.subsume p₁ n1 pn.1 pn.2).var + · exact .inl ⟨y, hy', e, le⟩ + obtain ⟨z, hz, ez, lez⟩ := subsumeVars_dominated hy (hv ▸ hy') + have hpe : pn.1 ≠ p₁ := fun h => hlen (h ▸ rfl) + exact .inr ⟨pn.1, pn.2, z, hpe, h₂, hsub, hz, ez.trans e, Nat.le_trans le lez⟩ + +/-- One step of `subsumption`: the key being minimized is updated, or erased if it drained, +and no other key changes. -/ +theorem NormLevel.subsumption_step_get? (acc : NormLevel) (n₁ : Node) (p₁ p : List Name) : + (if (acc.minimize p₁ n₁).isEmpty then acc.erase p₁ + else acc.insert p₁ (acc.minimize p₁ n₁)).get? p = + if p₁ = p then (if (acc.minimize p₁ n₁).isEmpty then none else some (acc.minimize p₁ n₁)) + else acc.get? p := by + split <;> + simp only [Std.TreeMap.get?_eq_getElem?, Std.TreeMap.getElem?_erase, + Std.TreeMap.getElem?_insert] <;> + split <;> split <;> simp_all + +/-- `subsumption` only shrinks the variable lists, at unchanged keys, so it preserves the +half of `WF` saying that every variable recorded at a key is an element of it. -/ +theorem NormLevel.subsumption_vars {s : NormLevel} (wf : s.WF) : + ∀ p n, s.subsumption.get? p = some n → (∀ v ∈ n.var, v.var ∈ p) ∧ Sorted p := by + rw [subsumption, Std.TreeMap.foldl_eq_foldl_toList] + have hmem pn (h : pn ∈ s.toList) : s.get? pn.1 = some pn.2 := + Std.TreeMap.get?_eq_getElem? .. ▸ Std.TreeMap.mem_toList_iff_getElem?_eq_some.1 h + generalize s.toList = l at hmem + suffices ∀ (l : List (List Name × Node)) (acc : NormLevel), + (∀ pn ∈ l, s.get? pn.1 = some pn.2) → + (∀ p n, acc.get? p = some n → (∀ v ∈ n.var, v.var ∈ p) ∧ Sorted p) → + ∀ p n, (List.foldl (fun acc pn => + let n := acc.minimize pn.1 pn.2 + if n.isEmpty then acc.erase pn.1 else acc.insert pn.1 n) acc l).get? p = some n → + (∀ v ∈ n.var, v.var ∈ p) ∧ Sorted p from this _ _ hmem fun p n h => (wf p n h).2 + clear hmem; intro l + induction l with | nil => exact fun _ _ => id | cons pn l ih + intro acc hl hacc + refine ih _ (fun _ h => hl _ (.tail _ h)) fun p n h => ?_ + rw [subsumption_step_get?] at h + split at h + · split at h <;> [cases h; skip] + cases h; rename_i hp _; subst hp + have := (wf _ _ (hl _ (.head _))).2 + exact ⟨fun v hv => this.1 _ (minimize_var_subset hv), this.2⟩ + · exact hacc _ _ h + +/-- A variable that minimization drops is dropped in favour of one with the same name at a +strictly smaller key. -/ +theorem NormLevel.minimize_var_dominated {acc : NormLevel} {p₁ n₁ x} + (hx : x ∈ n₁.var) (h : x ∉ (acc.minimize p₁ n₁).var) : + ∃ p₂ n₂ y, acc.get? p₂ = some n₂ ∧ y ∈ n₂.var ∧ y.var = x.var ∧ + p₂ ≠ p₁ ∧ ∀ z ∈ p₂, z ∈ p₁ := by + rw [minimize, Std.TreeMap.foldl_eq_foldl_toList] at h + have hmem pn (h : pn ∈ acc.toList) : acc.get? pn.1 = some pn.2 := + Std.TreeMap.get?_eq_getElem? .. ▸ Std.TreeMap.mem_toList_iff_getElem?_eq_some.1 h + generalize acc.toList = l at h hmem + suffices ∀ (l : List (List Name × Node)) (n : Node), + (∀ pn ∈ l, acc.get? pn.1 = some pn.2) → x ∈ n.var → + x ∉ (List.foldl (fun n pn => Node.subsume p₁ n pn.1 pn.2) n l).var → + ∃ p₂ n₂ y, acc.get? p₂ = some n₂ ∧ y ∈ n₂.var ∧ y.var = x.var ∧ + p₂ ≠ p₁ ∧ ∀ z ∈ p₂, z ∈ p₁ from this _ _ hmem hx h + clear hx h hmem; intro l + induction l with + | nil => exact fun n _ hx h => absurd hx h + | cons pn l ih => + intro n hl hx h + by_cases hx' : x ∈ (Node.subsume p₁ n pn.1 pn.2).var + · exact ih _ (fun _ h => hl _ (.tail _ h)) hx' h + · obtain heq | ⟨hsub, hlen, heq⟩ := Node.subsume_var_cases p₁ n pn.1 pn.2 + · rw [heq] at hx'; exact absurd hx hx' + · rw [heq] at hx' + obtain ⟨y, hy, e, -⟩ := subsumeVars_dominated hx hx' + exact ⟨pn.1, pn.2, y, hl _ (.head _), hy, e, + fun he => hlen (by rw [he]), fun _ hz => subset_mem hsub hz⟩ + +/-- `s'` covers `s`: every variable recorded in `s` is still recorded in `s'`, at a subset of +its key. This is all of a map that `Dom`, hence `Feas`, looks at. -/ +def NormLevel.Covers (s' s : NormLevel) : Prop := + ∀ p n x, s.get? p = some n → x ∈ n.var → + ∃ q m y, s'.get? q = some m ∧ y ∈ m.var ∧ y.var = x.var ∧ ∀ z ∈ q, z ∈ p + +/-- `subsumption` only removes variables from a node, and never the last witness for a name: +a removed one is still recorded at a strictly smaller key, possibly after further removals +there. So the subsumed map covers the original, and its entries are entries of it. -/ +theorem NormLevel.subsumption_covers {s : NormLevel} : + (∀ p n, s.subsumption.get? p = some n → + ∃ n₀, s.get? p = some n₀ ∧ ∀ x ∈ n.var, x ∈ n₀.var) ∧ s.subsumption.Covers s := by + rw [subsumption, Std.TreeMap.foldl_eq_foldl_toList] + have hmem pn (h : pn ∈ s.toList) : s.get? pn.1 = some pn.2 := + Std.TreeMap.get?_eq_getElem? .. ▸ Std.TreeMap.mem_toList_iff_getElem?_eq_some.1 h + generalize s.toList = l at hmem + suffices ∀ (l : List (List Name × Node)) (acc : NormLevel), + (∀ pn ∈ l, s.get? pn.1 = some pn.2) → + (∀ p n, acc.get? p = some n → ∃ n₀, s.get? p = some n₀ ∧ ∀ x ∈ n.var, x ∈ n₀.var) → + acc.Covers s → + (∀ p n, (List.foldl (fun acc pn => + let n := acc.minimize pn.1 pn.2 + if n.isEmpty then acc.erase pn.1 else acc.insert pn.1 n) acc l).get? p = some n → + ∃ n₀, s.get? p = some n₀ ∧ ∀ x ∈ n.var, x ∈ n₀.var) ∧ + (List.foldl (fun acc pn => + let n := acc.minimize pn.1 pn.2 + if n.isEmpty then acc.erase pn.1 else acc.insert pn.1 n) acc l).Covers s from + this _ _ hmem (fun p n h => ⟨n, h, fun _ => id⟩) + (fun p n x h hx => ⟨p, n, x, h, hx, rfl, fun _ => id⟩) + clear hmem; intro l + induction l with | nil => exact fun _ _ h1 h2 => ⟨h1, h2⟩ | cons pn l ih + obtain ⟨p₁, n₁⟩ := pn + intro acc hl hsub hcov + have h₁ : s.get? p₁ = some n₁ := hl _ (.head _) + refine ih _ (fun _ h => hl _ (.tail _ h)) (fun p n h => ?_) (fun p n x hp hx => ?_) + · rw [subsumption_step_get?] at h + split at h + · split at h <;> [cases h; skip] + cases h; rename_i hp _; subst hp + exact ⟨n₁, h₁, fun x hx => minimize_var_subset hx⟩ + · exact hsub _ _ h + · obtain ⟨q, m, y, hq, hy, e, hqp⟩ := hcov _ _ _ hp hx + by_cases hqp₁ : q = p₁ + · subst hqp₁ + -- the write lands on the key covering `x`: either the variable survives it, or it is + -- dominated at a smaller key, which this step leaves alone + obtain ⟨n₀, h₀, hy₀⟩ := hsub _ _ hq + cases h₀.symm.trans h₁ + by_cases hmin : y ∈ (acc.minimize q n₁).var + · refine ⟨q, _, y, ?_, hmin, e, hqp⟩ + rw [subsumption_step_get?, if_pos rfl, if_neg] + simp only [Node.isEmpty, Bool.and_eq_true, List.isEmpty_iff, not_and] + rintro - he; simp [he] at hmin + · obtain ⟨p₂, n₂, z, h₂, hz, e₂, hne, hp₂⟩ := minimize_var_dominated (hy₀ _ hy) hmin + exact ⟨p₂, n₂, z, by rw [subsumption_step_get?, if_neg (Ne.symm hne)]; exact h₂, + hz, e₂.trans e, fun w hw => hqp _ (hp₂ _ hw)⟩ + · exact ⟨q, m, y, by rw [subsumption_step_get?, if_neg (Ne.symm hqp₁)]; exact hq, + hy, e, hqp⟩ + +theorem NormLevel.subsumption_eval {s : NormLevel} (wf : s.WF) : + s.subsumption.eval ls ρ = s.eval ls ρ := by + rw [subsumption, Std.TreeMap.foldl_eq_foldl_toList] + have hmem pn (h : pn ∈ s.toList) : s.get? pn.1 = some pn.2 := + Std.TreeMap.get?_eq_getElem? .. ▸ Std.TreeMap.mem_toList_iff_getElem?_eq_some.1 h + have nd : (s.toList.map Prod.fst).Nodup := by simpa using Std.TreeMap.nodup_keys (t := s) + generalize s.toList = l at hmem nd + suffices ∀ (l : List (List Name × Node)) (acc : NormLevel), + (l.map Prod.fst).Nodup → + (∀ p n, (p, n) ∈ l → acc.get? p = some n) → + (∀ p n, acc.get? p = some n → ∀ v ∈ n.var, v.var ∈ p) → + eval ls ρ acc = eval ls ρ s → + eval ls ρ (List.foldl (fun acc pn => + let n := acc.minimize pn.1 pn.2 + if n.isEmpty then acc.erase pn.1 else acc.insert pn.1 n) acc l) = eval ls ρ s from + this _ _ nd (fun _ _ => hmem _) (fun _ _ h => (wf _ _ h).2.1) rfl + clear hmem nd; intro l + induction l with | nil => exact fun acc _ _ _ eq => eq | cons pn l ih + have ⟨p₁, n₁⟩ := pn; intro acc nd hl wfa eq + simp only [List.map_cons, List.nodup_cons] at nd + have h₁ := hl _ _ (.head _) + -- a drained key is erased rather than kept, which is the same for `eval` + have hins := subsumption_step_get? acc n₁ p₁ + have hmin_le m + (H : ∀ a b, (if (acc.minimize p₁ n₁).isEmpty then acc.erase p₁ + else acc.insert p₁ (acc.minimize p₁ n₁)).get? a = some b → + evalPath ls ρ a (Node.eval ls ρ b) ≤ m) + (nz : allNZ ls ρ p₁) : Node.eval ls ρ (acc.minimize p₁ n₁) ≤ m := by + by_cases he : (acc.minimize p₁ n₁).isEmpty + · simp [Node.isEmpty, List.isEmpty_iff] at he; simp [Node.eval, he.1, he.2] + · have hget : (if (acc.minimize p₁ n₁).isEmpty then acc.erase p₁ + else acc.insert p₁ (acc.minimize p₁ n₁)).get? p₁ = some (acc.minimize p₁ n₁) := by + rw [hins p₁, if_pos rfl, if_neg he] + have := H _ _ hget + rw [evalPath_le] at this; exact this nz + refine ih _ nd.2 (fun p n h => ?_) (fun p n h v hv => ?_) ((ext_le fun m => ?_).trans eq) + · have hne : p₁ ≠ p := fun e => nd.1 (by rw [e]; exact List.mem_map_of_mem h) + exact (hins p).trans (if_neg hne) ▸ hl _ _ (.tail _ h) + · rw [hins p] at h; split at h + · split at h <;> [cases h; skip] + cases h; rename_i hp _; subst hp + exact wfa _ _ h₁ _ (minimize_var_subset hv) + · exact wfa _ _ h _ hv + · simp only [eval_le]; constructor <;> intro H p n h + · by_cases hp : p = p₁ + · subst hp; cases h₁.symm.trans h + refine evalPath_le.2 fun nz => ?_ + refine (minimize_eval_iff wfa h₁ (fun q nq hne hq => ?_) nz).1 (hmin_le _ H nz) + exact H _ _ ((hins q).trans (if_neg hne.symm) ▸ hq) + · exact H p n ((hins p).trans (if_neg (Ne.symm hp)) ▸ h) + · rw [hins p] at h; split at h <;> [skip; exact H _ _ h] + split at h <;> [cases h; skip] + cases h; rename_i hp _; subst hp + refine evalPath_le.2 fun nz => ?_ + have := H _ _ h₁; rw [evalPath_le] at this + exact Nat.le_trans minimize_eval_le (this nz) + +theorem normalize_eval (hu : VLevel.ofLevel ls u = some u') : + (normalize u).eval ls ρ = u'.eval ρ := by + simp [normalize] + refine have h1 := ?_; by + rw [NormLevel.subsumption_eval (normalizeAux_wf (by simp) h1)] + exact normalizeAux_eval hu (by simp) h1 + simp [NormLevel.WF] + +theorem normalize_vars_sorted : ∀ p n, (normalize u).get? p = some n → + (∀ v ∈ n.var, v.var ∈ p) ∧ Sorted p := + NormLevel.subsumption_vars (normalizeAux_wf (by simp) (by simp [NormLevel.WF])) + +theorem normalize_vars : ∀ p n, (normalize u).get? p = some n → ∀ v ∈ n.var, v.var ∈ p := + fun _ _ h => (normalize_vars_sorted _ _ h).1 + +theorem normalize_sorted : ∀ p n, (normalize u).get? p = some n → Sorted p := + fun _ _ h => (normalize_vars_sorted _ _ h).2 + +/-- Soundness of `NormLevel.le`, Theorem 39 of the paper: it reports `true` only when every +sublevel of `l₁` is dominated. Each entry of `l₁` is compared against a fold over `l₂`, +where every entry discharges from the node what it can, and the fold stops (returning +`none`) once nothing is left to discharge; so the fold ends in `none` only if the node is +bounded by the total of `l₂`. -/ +theorem NormLevel.le_eval {l₁ l₂ : NormLevel} + (wf₂ : ∀ p n, l₂.get? p = some n → ∀ v ∈ n.var, v.var ∈ p) + (h : l₁.le l₂) : l₁.eval ls ρ ≤ l₂.eval ls ρ := by + refine NormLevel.eval_le.2 fun p₁ n₁ h₁ => evalPath_le.2 fun nz => ?_ + -- an entry of `l₂` at a key below `p₁` is bounded by the total, on a live condition set + have hbd p₂ n₂ (h₂ : l₂.get? p₂ = some n₂) (hsub : subset compare p₂ p₁) : + (∀ v ∈ n₂.var, 0 < evalParam ls ρ v.var) ∧ Node.eval ls ρ n₂ ≤ l₂.eval ls ρ := by + have hnz : allNZ ls ρ p₂ := allNZ_mono (fun _ h => subset_mem hsub h) nz + refine ⟨fun v hv => ?_, ?_⟩ + · simp only [allNZ, List.all_eq_true, decide_eq_true_eq] at hnz + exact hnz _ (wf₂ _ _ h₂ _ hv) + · have := evalPath_le.1 (NormLevel.eval_le.1 (Nat.le_refl (l₂.eval ls ρ)) _ _ h₂) + exact this hnz + rw [NormLevel.le, Std.TreeMap.all_eq_all_toList, List.all_eq_true] at h + have hf := h (p₁, n₁) (Std.TreeMap.mem_toList_iff_getElem?_eq_some.2 (by simpa using h₁)) + simp only [Std.TreeMap.foldlM_eq_foldlM_toList, Option.isNone_iff_eq_none] at hf + have hmem pn (h : pn ∈ l₂.toList) : l₂.get? pn.1 = some pn.2 := + Std.TreeMap.get?_eq_getElem? .. ▸ Std.TreeMap.mem_toList_iff_getElem?_eq_some.1 h + clear h₁ h + generalize l₂.toList = l at hf hmem + induction l generalizing n₁ with + | nil => simp at hf + | cons pn l ih => + simp only [List.foldlM_cons] at hf + simp only [List.mem_cons, forall_eq_or_imp] at hmem + by_cases hs : subset compare pn.1 p₁ + · refine (Node.subsumeBy_eval_iff (same := false) (n₂ := pn.2) + (hbd _ _ hmem.1 hs).1 (hbd _ _ hmem.1 hs).2).1 ?_ + by_cases he : (n₁.subsumeBy false pn.2).isEmpty + · simp [Node.eval_empty he] + · exact ih _ (by simpa [hs, he] using hf) hmem.2 + · exact ih _ (by simpa [hs] using hf) hmem.2 + +theorem Node.eval_congr {a b : Node} (H : a == b) : a.eval ls ρ = b.eval ls ρ := by + simp +instances [instBEqNode] at H; simp [H, eval] + +theorem NormLevel.eval_congr {a b : NormLevel} (H : a == b) : a.eval ls ρ = b.eval ls ρ := by + simp +instances only [instBEqNormLevel, Std.TreeMap.all_eq_all_toList, + Bool.and_eq_true, List.all_eq_true] at H + suffices ∀ {a b : NormLevel}, (∀ x ∈ a.toList, b.get? x.1 == some x.2) → + a.eval ls ρ ≤ b.eval ls ρ from Nat.le_antisymm (this H.1) (this H.2) + clear a b H; intro a b H + simp only [eval, Std.TreeMap.foldl_eq_foldl_toList] + rw [← a.toList.reverse_reverse] at H ⊢; generalize a.toList.reverse = a at H ⊢ + simp only [List.mem_reverse, Std.TreeMap.get?_eq_getElem?, List.foldl_reverse] at H ⊢ + induction a with | nil => exact Nat.zero_le _ | cons p l ih; let (x, y) := p + simp only [List.mem_cons, or_imp, forall_and, forall_eq, List.foldr_cons] at H ⊢ + refine Nat.max_le.2 ⟨ih H.2, ?_⟩ + let ⟨y', h1, h2⟩ := Option.beq_some_iff.1 H.1 + have H := Std.TreeMap.mem_toList_iff_getElem?_eq_some.2 h1 + rw [← b.toList.reverse_reverse] at H ⊢; generalize b.toList.reverse = b at H ⊢ + simp only [List.mem_reverse, List.foldl_reverse] at H ⊢ + induction b with | nil => cases H | cons p l ih; let (x, y) := p + simp; obtain ⟨⟩ | ⟨_, (H : _ ∈ l)⟩ := H + · exact Node.eval_congr h2 ▸ Nat.le_max_right .. + · exact Nat.le_trans (ih H) (Nat.le_max_left ..) + +/-! +### Reconstruction + +The value of a `Tree` is the value of the level it reifies to: a tree node contributes its +own sublevels, and every child contributes under the `imax` guard of the variable labelling +the edge into it. That edge guard is what makes the tree shape meaningful — a node at path +`[a₁, …, aₙ]` (innermost first) is guarded by all of `a₁, …, aₙ` — and it is also what makes +the tree carry sublevels of its own, since `imax x a` is at least `a` when `a ≠ 0`. +-/ + +mutual + +def Tree.eval (ls : List Name) (ρ : List Nat) : Tree → Nat + | ⟨const, var, child⟩ => max' (Node.eval ls ρ ⟨const, var⟩) (Tree.evalChild ls ρ child) + +def Tree.evalChild (ls : List Name) (ρ : List Nat) : List (Name × Tree) → Nat + | [] => 0 + | (a, t) :: l => + max' (Lean.Nat.imax (Tree.eval ls ρ t) (evalParam ls ρ a)) (Tree.evalChild ls ρ l) + +end + +/-- The value of the optional level accumulated by `reify`. -/ +def evalOpt (ρ : Name → Nat) (μ : LMVarId → Nat) : Option Level → Nat + | none => 0 + | some l => Level.eval ρ μ l + +@[simp] theorem evalOpt_none : evalOpt ρ μ none = 0 := rfl +@[simp] theorem evalOpt_some : evalOpt ρ μ (some l) = Level.eval ρ μ l := rfl + +theorem imax_eq_ite : Lean.Nat.imax a b = if b = 0 then 0 else max' a b := rfl + +theorem imax_zero_left : Lean.Nat.imax 0 a = a := by rw [imax_eq_ite]; split <;> omega + +theorem Node.eval_const {var : List VarNode} : + Node.eval ls ρ ⟨c, var⟩ = max' c (Node.eval ls ρ ⟨0, var⟩) := + ext_le fun x => by simp [Node.eval_le, Nat.max_le] + +theorem Node.eval_cons {var : List VarNode} : + Node.eval ls ρ ⟨c, a :: var⟩ = max' (VarNode.eval ls ρ a) (Node.eval ls ρ ⟨c, var⟩) := + ext_le fun x => by simp [Node.eval_le, Nat.max_le, and_left_comm] + +theorem eval_mkMax : + Level.eval ρ μ (Tree.reify.mkMax l o) = max' (Level.eval ρ μ l) (evalOpt ρ μ o) := by + cases o <;> simp [Tree.reify.mkMax, evalOpt, Level.eval] + +theorem eval_addOffset : Level.eval ρ μ (l.addOffset k) = Level.eval ρ μ l + k := by + simp only [Level.addOffset] + induction k generalizing l with + | zero => rfl + | succ k ih => rw [Level.addOffsetAux, ih]; simp [Level.eval]; omega + +theorem eval_ofNat : Level.eval ρ μ (Level.ofNat k) = k := by + induction k with + | zero => rfl + | succ k ih => simp [Level.ofNat, Level.eval, ih] + +theorem eval_varFold (var : List VarNode) (o : Option Level) : + evalOpt (evalParam ls ρ) μ (var.foldr (init := o) fun n r => + some (Tree.reify.mkMax (Level.addOffset (.param n.var) n.offset) r)) = + max' (Node.eval ls ρ ⟨0, var⟩) (evalOpt (evalParam ls ρ) μ o) := by + induction var with | nil => simp [Node.eval] | cons a var ih + simp only [List.foldr_cons, evalOpt_some, eval_mkMax, eval_addOffset, Level.eval, ih, + Node.eval_cons, VarNode.eval]; omega + +/-- The two shapes `plainOffset?` accepts. -/ +theorem Tree.plainOffset?_eq {a : Name} {t : Tree} {k : Nat} (h : plainOffset? a t = some k) : + t = ⟨0, [], []⟩ ∧ k = 0 ∨ t = ⟨0, [⟨a, k⟩], []⟩ := by + unfold plainOffset? at h + split at h + · exact .inl ⟨rfl, by simpa using h.symm⟩ + · rename_i v _ + split at h <;> [skip; cases h] + cases h; rename_i hv + exact .inr (by rw [← eq_of_beq hv]) + · cases h + +/-- Dropping the `imax` guard of a plain child is exact modulo the node's constant: the two +differ only when the edge variable is zero, where the plain form contributes `k ≤ const`. -/ +theorem Tree.plainOffset?_eval {a : Name} {t : Tree} {k const : Nat} + (h : plainOffset? a t = some k) (hk : k ≤ const) : + max' const (Lean.Nat.imax (eval ls ρ t) (evalParam ls ρ a)) = + max' const (evalParam ls ρ a + k) := by + obtain ⟨rfl, rfl⟩ | rfl := plainOffset?_eq h + · rw [show eval ls ρ ⟨0, [], []⟩ = 0 from by simp [eval, evalChild, Node.eval], + imax_zero_left] + omega + · rw [show eval ls ρ ⟨0, [⟨a, k⟩], []⟩ = evalParam ls ρ a + k from by + simp [eval, evalChild, Node.eval, VarNode.eval], imax_eq_ite] + split <;> omega + +/-- A child emitted plainly at exactly the node's constant makes that constant redundant. -/ +theorem Tree.reifyChild_ge {const : Nat} : ∀ child : List (Name × Tree), + (child.any fun c => plainOffset? c.1 c.2 == some const) → + const ≤ evalOpt (evalParam ls ρ) μ (child.foldr (reify.mkChild const) none) + | (n, t) :: child, h => by + rw [List.foldr_cons, reify.mkChild] + simp only [List.any_cons, Bool.or_eq_true, beq_iff_eq] at h + -- either this child is the witness, in which case it is emitted as `n + const`, or the + -- witness is further down and the fold maxes its value in + obtain h | h := h + · rw [h]; dsimp only; rw [if_pos (Nat.le_refl const)] + simp only [evalOpt_some, eval_mkMax, eval_addOffset, Level.eval] + omega + · have ih := reifyChild_ge (ls := ls) (ρ := ρ) (μ := μ) child h + split <;> [split; skip] <;> + simp only [evalOpt_some, eval_mkMax] <;> omega + +mutual + +theorem Tree.reify_eval (t : Tree) : t.reify.eval (evalParam ls ρ) μ = t.eval ls ρ := by + obtain ⟨const, var, child⟩ := t + rw [eval] + simp only [reify] + have h1 := eval_varFold (ls := ls) (ρ := ρ) (μ := μ) var + (child.foldr (reify.mkChild const) none) + have hc := reifyChild_eval (ls := ls) (ρ := ρ) (μ := μ) const child + rw [Node.eval_const (c := const)] + split <;> [rename_i heq; rename_i l heq] + · rw [heq, evalOpt_none] at h1 + rw [eval_ofNat]; omega + · rw [heq, evalOpt_some] at h1 + split <;> rename_i hd + · -- the constant is dropped: either it is zero, or some child already covers it + simp only [Bool.or_eq_true, beq_iff_eq] at hd + rw [h1] + obtain rfl | hd := hd + · omega + · have := Tree.reifyChild_ge (ls := ls) (ρ := ρ) (μ := μ) (const := const) child hd + omega + · simp only [Level.eval, eval_ofNat, h1, Nat.max_eq_max]; omega + +theorem Tree.reifyChild_eval (const : Nat) (child : List (Name × Tree)) : + max' const (evalOpt (evalParam ls ρ) μ (child.foldr (reify.mkChild const) none)) = + max' const (evalChild ls ρ child) := by + match child with + | [] => rfl + | (n, t) :: child => + rw [List.foldr_cons, evalChild, reify.mkChild] + have ht := reify_eval (ls := ls) (ρ := ρ) (μ := μ) t + have ih := reifyChild_eval (ls := ls) (ρ := ρ) (μ := μ) const child + split <;> rename_i k h + · split <;> rename_i hk + · have hp := Tree.plainOffset?_eval (ls := ls) (ρ := ρ) h hk + simp only [evalOpt_some, eval_mkMax, eval_addOffset, Level.eval] at * + omega + · simp only [evalOpt_some, eval_mkMax, Level.eval, ht] at * + omega + · simp only [evalOpt_some, eval_mkMax, Level.eval, ht] at * + omega + +end + +/-- `Tree.At t p t'` says `t'` is the subtree of `t` at path `p`, listed innermost first, +the way `Tree.modify` takes it. -/ +inductive Tree.At : Tree → List Name → Tree → Prop + | nil : At t [] t + | cons (h : At t p t') (hm : (a, t'') ∈ t'.child) : At t (a :: p) t'' + +theorem Tree.eval_eq (t : Tree) : + eval ls ρ t = max' (Node.eval ls ρ ⟨t.const, t.var⟩) (evalChild ls ρ t.child) := by + cases t; rw [eval] + +theorem Tree.At.append (h : At t' q t'') (hm : (a, t') ∈ t.child) : + At t (q ++ [a]) t'' := by + induction h with + | nil => exact .cons .nil hm + | cons _ hm' ih => exact .cons ih hm' + +/-- A path passes through all of its tails. -/ +theorem Tree.At.suffix {t : Tree} : ∀ {path t' q}, At t path t' → q <:+ path → ∃ t'', At t q t'' := by + intro path + induction path with + | nil => intro t' q _ hq; cases List.suffix_nil.1 hq; exact ⟨t, .nil⟩ + | cons a p ih => + intro t' q h hq + obtain rfl | hq := List.suffix_cons_iff.1 hq + · exact ⟨t', h⟩ + · cases h; rename_i t₁ h _; exact ih h hq + +theorem Tree.At.nil_inv (h : At t [] t') : t' = t := by cases h; rfl + +/-- Inverting `At.append`: a nonempty path is a child of the root followed by the rest. -/ +theorem Tree.At.append_inv : ∀ {q t t''}, At t (q ++ [a]) t'' → + ∃ t₁, (a, t₁) ∈ t.child ∧ At t₁ q t'' := by + intro q + induction q with + | nil => + intro t t'' h + cases h; rename_i t₁ h hm + cases h.nil_inv + exact ⟨t'', hm, .nil⟩ + | cons b q ih => + intro t t'' h + simp only [List.cons_append] at h + cases h; rename_i t₁ h hm + obtain ⟨t₂, hm₂, h₂⟩ := ih h + exact ⟨t₂, hm₂, .cons h₂ hm⟩ + +/-- Paths only look at the children, so replacing the root's own data leaves them all in +place; only the empty path sees the difference. -/ +theorem Tree.At.of_child_eq {t u : Tree} (hc : u.child = t.child) : + ∀ {p t'}, At t p t' → At u p t' ∨ (p = [] ∧ t' = t) := by + intro p + induction p with + | nil => intro t' h; exact .inr ⟨rfl, h.nil_inv⟩ + | cons a q ih => + intro t'' h + cases h; rename_i t₁ h hm + obtain h' | ⟨rfl, rfl⟩ := ih h + · exact .inl (.cons h' hm) + · exact .inl (.cons .nil (by rw [hc]; exact hm)) + +theorem Tree.mem_le {l : List (Name × Tree)} (hm : (a, t) ∈ l) : + Lean.Nat.imax (eval ls ρ t) (evalParam ls ρ a) ≤ evalChild ls ρ l := by + induction l with + | nil => cases hm + | cons b l ih => + obtain ⟨b, t'⟩ := b + rw [evalChild] + obtain h | hm := List.mem_cons.1 hm + · cases h; exact Nat.le_max_left .. + · exact Nat.le_trans (ih hm) (Nat.le_max_right ..) + +theorem evalPath_cons_imax : + evalPath ls ρ (a :: p) c ≤ evalPath ls ρ p (Lean.Nat.imax c (evalParam ls ρ a)) := by + rw [evalPath_cons] + exact evalPath_mono <| by + by_cases h : evalParam ls ρ a = 0 <;> + simp [imax_eq_ite, h, Nat.pos_of_ne_zero, Nat.le_max_left] + +theorem evalPath_cons_edge : + evalPath ls ρ (a :: p) (evalParam ls ρ a) ≤ + evalPath ls ρ p (Lean.Nat.imax c (evalParam ls ρ a)) := by + rw [evalPath_cons] + exact evalPath_mono <| by + by_cases h : evalParam ls ρ a = 0 <;> + simp [imax_eq_ite, h, Nat.pos_of_ne_zero, Nat.le_max_right] + +theorem evalPath_append_single (ha : evalParam ls ρ a ≠ 0) : + evalPath ls ρ (p ++ [a]) c = evalPath ls ρ p c := by + simp [evalPath, allNZ, List.all_append, Nat.pos_of_ne_zero ha] + +theorem Tree.At.le (h : At t p t') : + evalPath ls ρ p (eval ls ρ t') ≤ eval ls ρ t := by + induction h with + | nil => simp [evalPath, allNZ] + | cons _ hm ih => + refine Nat.le_trans evalPath_cons_imax (Nat.le_trans (evalPath_mono ?_) ih) + exact Nat.le_trans (mem_le hm) (eval_eq _ ▸ Nat.le_max_right ..) + +theorem Tree.At.edge_le (h : At t (a :: p) t') : + evalPath ls ρ (a :: p) (evalParam ls ρ a) ≤ eval ls ρ t := by + cases h with | @cons _ t'' _ _ h' hm => ?_ + refine Nat.le_trans (evalPath_cons_edge (c := eval ls ρ t')) + (Nat.le_trans (evalPath_mono ?_) h'.le) + exact Nat.le_trans (mem_le hm) (eval_eq t'' ▸ Nat.le_max_right ..) + +mutual + +/-- A tree is bounded by `m` as soon as all the sublevels it reifies to are: the ones +recorded at its nodes, and the `V(p, a, 0)` contributed by the edge into each node. -/ +theorem Tree.eval_le_of (t : Tree) + (h1 : ∀ p t', At t p t' → evalPath ls ρ p (Node.eval ls ρ ⟨t'.const, t'.var⟩) ≤ m) + (h2 : ∀ a p t', At t (a :: p) t' → evalPath ls ρ (a :: p) (evalParam ls ρ a) ≤ m) : + eval ls ρ t ≤ m := by + obtain ⟨const, var, child⟩ := t + rw [eval] + refine Nat.max_le.2 ⟨h1 [] _ .nil, evalChild_le_of child ?_ ?_ ?_⟩ + · exact fun a t' hm p t'' hat => h1 _ _ (hat.append hm) + · exact fun a t' hm b p t'' hat => h2 _ _ _ (hat.append hm) + · exact fun a t' hm => h2 a [] t' (.cons .nil hm) + +theorem Tree.evalChild_le_of : ∀ (l : List (Name × Tree)), + (∀ a t', (a, t') ∈ l → ∀ p t'', At t' p t'' → + evalPath ls ρ (p ++ [a]) (Node.eval ls ρ ⟨t''.const, t''.var⟩) ≤ m) → + (∀ a t', (a, t') ∈ l → ∀ b p t'', At t' (b :: p) t'' → + evalPath ls ρ ((b :: p) ++ [a]) (evalParam ls ρ b) ≤ m) → + (∀ a t', (a, t') ∈ l → evalPath ls ρ [a] (evalParam ls ρ a) ≤ m) → + evalChild ls ρ l ≤ m + | [], _, _, _ => by rw [evalChild]; exact Nat.zero_le _ + | (a, t) :: l, h1, h2, h3 => by + rw [evalChild] + refine Nat.max_le.2 ⟨?_, evalChild_le_of l + (fun a t' hm => h1 a t' (.tail _ hm)) (fun a t' hm => h2 a t' (.tail _ hm)) + (fun a t' hm => h3 a t' (.tail _ hm))⟩ + by_cases ha : evalParam ls ρ a = 0 + · simp [imax_eq_ite, ha] + · have hle : evalParam ls ρ a ≤ m := by + have := h3 a t (.head _) + simpa [evalPath, allNZ, Nat.pos_of_ne_zero ha] using this + have ht : eval ls ρ t ≤ m := + eval_le_of t + (fun p t'' hat => evalPath_append_single ha ▸ h1 a t (.head _) p t'' hat) + (fun b p t'' hat => evalPath_append_single ha ▸ h2 a t (.head _) b p t'' hat) + rw [imax_eq_ite]; split <;> omega + +end + +/-- A tree is bounded by `m` exactly when all the sublevels it reifies to are: the ones +recorded at its nodes, and the one each edge contributes. -/ +theorem Tree.eval_le_iff {t : Tree} {m : Nat} : + eval ls ρ t ≤ m ↔ + (∀ p t', At t p t' → evalPath ls ρ p (Node.eval ls ρ ⟨t'.const, t'.var⟩) ≤ m) ∧ + (∀ a p t', At t (a :: p) t' → evalPath ls ρ (a :: p) (evalParam ls ρ a) ≤ m) := by + refine ⟨fun h => ?_, fun ⟨h1, h2⟩ => eval_le_of t h1 h2⟩ + refine ⟨fun _ t' hp => ?_, fun _ _ _ hp => Nat.le_trans hp.edge_le h⟩ + exact Nat.le_trans (Nat.le_trans (evalPath_mono (eval_eq t' ▸ Nat.le_max_left ..)) hp.le) h + +/-! +### Admissible chains + +Reifying the sublevels at a key `p` means nesting them under an `imax` chain whose variables +are the elements of `p`; the chain contributes the sublevel `V(q, a, 0)` for every one of its +edges, where `q` is the set of conditions from the outside up to and including that edge. The +level is therefore equivalent to the normal form only if every such edge is *dominated* +(`Dom`), and a key is expressible only if its elements can be ordered so that all of them are +(`Feas`). `lexChain` searches for such an order greedily; `feasible` is its lookahead. +-/ + +/-- The edge adding `a` to the conditions `acc` contributes `V(acc ∪ {a}, a, 0)`, which the +normal form dominates when it has some `V(T, a+k)` with `T ⊆ acc ∪ {a}`. -/ +def NormLevel.Dom (s : NormLevel) (a : Name) (acc : List Name) : Prop := + ∃ p n, s.get? p = some n ∧ (∃ x ∈ n.var, x.var = a) ∧ ∀ y ∈ p, y = a ∨ y ∈ acc + +theorem NormLevel.Dom.mono {s : NormLevel} + (h : s.Dom a acc) (hs : ∀ x ∈ acc, x ∈ acc') : s.Dom a acc' := + let ⟨p, n, h1, h2, h3⟩ := h + ⟨p, n, h1, h2, fun y hy => (h3 y hy).imp id (hs _)⟩ + +/-- A dominated edge contributes nothing beyond the normal form. -/ +theorem NormLevel.Dom.le {s : NormLevel} (h : s.Dom a acc) : + evalPath ls ρ (a :: acc) (evalParam ls ρ a) ≤ s.eval ls ρ := by + refine evalPath_le.2 fun nz => ?_ + rw [allNZ_cons] at nz + obtain ⟨p, n, h1, ⟨x, hx, hxa⟩, h3⟩ := h + have hnz : allNZ ls ρ p := by + simp only [allNZ, List.all_eq_true, decide_eq_true_eq] + refine fun y hy => (h3 y hy).elim (fun e => e ▸ nz.1) fun hy => ?_ + simp only [allNZ, List.all_eq_true, decide_eq_true_eq] at nz + exact nz.2 _ hy + refine Nat.le_trans ?_ (Nat.le_trans (Node.var_le_eval hx) + (evalPath_le.1 (NormLevel.eval_le.1 (Nat.le_refl _) _ _ h1) hnz)) + simp only [VarNode.eval, ← hxa]; omega + +theorem NormLevel.addable_sound {s : NormLevel} (h : s.addable a acc) : s.Dom a acc := by + simp only [addable, Std.TreeMap.any_eq_any_toList, List.any_eq_true, Bool.and_eq_true, + beq_iff_eq] at h + obtain ⟨⟨p, n⟩, hm, ⟨x, hx, hxa⟩, hsub⟩ := h + refine ⟨p, n, Std.TreeMap.get?_eq_getElem? .. ▸ Std.TreeMap.mem_toList_iff_getElem?_eq_some.1 hm, + ⟨x, hx, hxa⟩, fun y hy => ?_⟩ + by_cases hya : y = a + · exact .inl hya + · exact .inr (subset_mem hsub ((List.mem_erase_of_ne hya).2 hy)) + +theorem NormLevel.addable_complete {s : NormLevel} (hs : ∀ p n, s.get? p = some n → Sorted p) + (hacc : Sorted acc) (h : s.Dom a acc) : s.addable a acc := by + obtain ⟨p, n, h1, ⟨x, hx, hxa⟩, h3⟩ := h + simp only [addable, Std.TreeMap.any_eq_any_toList, List.any_eq_true, Bool.and_eq_true, + beq_iff_eq] + refine ⟨(p, n), Std.TreeMap.mem_toList_iff_getElem?_eq_some.2 (by simpa using h1), + ⟨x, hx, hxa⟩, subset_of_sorted (hs _ _ h1).erase hacc fun y hy => ?_⟩ + refine (h3 y (List.mem_of_mem_erase hy)).resolve_left fun e => ?_ + exact absurd (e ▸ hy) ((hs _ _ h1).nodup.not_mem_erase) + +/-- The conditions `rem` can be added to `acc` one at a time, each addition dominated. -/ +inductive NormLevel.Feas (s : NormLevel) : List Name → List Name → Prop + | nil {acc} : Feas s acc [] + | cons {acc a rem} : a ∈ rem → s.Dom a acc → Feas s (a :: acc) (rem.erase a) → Feas s acc rem + +theorem NormLevel.Feas.mono {s : NormLevel} (hs : ∀ x ∈ acc, x ∈ acc') + (h : Feas s acc rem) : Feas s acc' rem := by + induction h generalizing acc' with | nil => exact .nil | cons hm hd _ ih + refine .cons hm (hd.mono hs) <| ih fun x hx => ?_ + obtain rfl | hx := List.mem_cons.1 hx + · exact .head _ + · exact .tail _ (hs _ hx) + +/-- Greedy exchange: a dominated element can always be taken first. -/ +theorem NormLevel.Feas.exchange {s : NormLevel} (h : Feas s acc rem) : + ∀ {a}, a ∈ rem → s.Dom a acc → Feas s (a :: acc) (rem.erase a) := by + induction h with | nil => nofun | @cons acc b rem hmb hdb H ih + intro a hm hd + by_cases hab : a = b <;> [(subst hab; exact H); skip] + refine .cons ((List.mem_erase_of_ne (Ne.symm hab)).2 hmb) (hdb.mono fun x hx => .tail _ hx) ?_ + rw [List.erase_comm] + refine ih ((List.mem_erase_of_ne hab).2 hm) (hd.mono fun x hx => .tail _ hx) + |>.mono fun x hx => ?_ + obtain rfl | hx := List.mem_cons.1 hx + · exact .tail _ (.head _) + · obtain rfl | hx := List.mem_cons.1 hx + · exact .head _ + · exact .tail _ (.tail _ hx) + +/-- Peel off the element added last: it is dominated by all the others. -/ +theorem NormLevel.Feas.peel {s : NormLevel} (h : Feas s acc rem) (nd : rem.Nodup) (hne : rem ≠ []) : + ∃ a ∈ rem, s.Dom a (acc ++ rem.erase a) ∧ Feas s acc (rem.erase a) := by + induction h with + | nil => exact absurd rfl hne + | @cons acc b rem hmb hdb H ih => + by_cases he : rem.erase b = [] + · exact ⟨b, hmb, hdb.mono fun x hx => List.mem_append_left _ hx, he ▸ .nil⟩ + obtain ⟨a, hma, hda, hfa⟩ := ih (nd.erase _) he + have hab : a ≠ b := by rintro rfl; exact absurd hma nd.not_mem_erase + have hmb' : b ∈ rem.erase a := (List.mem_erase_of_ne (Ne.symm hab)).2 hmb + refine ⟨a, List.mem_of_mem_erase hma, hda.mono fun x hx => ?_, ?_⟩ + · simp only [List.cons_append, List.mem_cons, List.mem_append] at hx ⊢ + obtain rfl | hx | hx := hx + · exact .inr hmb' + · exact .inl hx + · rw [List.erase_comm] at hx; exact .inr (List.mem_of_mem_erase hx) + · exact .cons hmb' hdb <| by rw [List.erase_comm]; exact hfa + +theorem NormLevel.feasible_go_sound {s : NormLevel} : + ∀ {fuel acc rem}, NormLevel.feasible.go s fuel acc rem → s.Feas acc rem + | 0, _, rem, h => by simp [feasible.go, List.isEmpty_iff] at h; exact h ▸ .nil + | fuel+1, acc, rem, h => by + rw [feasible.go] at h + split at h <;> [(let [] := rem; exact .nil); rename_i a ha] + have hm := List.mem_of_find?_eq_some ha + have hd := addable_sound (List.find?_eq_some_iff_getElem.1 ha).1 + refine .cons hm hd ((feasible_go_sound h).mono fun x hx => ?_) + exact List.mem_cons.2 ((Extend?.orderedInsert (cmp := Name.cmp) (v := a) (p := acc)).mem.1 hx) + +theorem NormLevel.feasible_sound {s : NormLevel} (h : s.feasible acc rem) : s.Feas acc rem := + feasible_go_sound h + +theorem NormLevel.feasible_go_complete {s : NormLevel} (hs : ∀ p n, s.get? p = some n → Sorted p) : + ∀ {fuel acc rem}, rem.length ≤ fuel → Sorted acc → s.Feas acc rem → feasible.go s fuel acc rem + | 0, _, rem, hf, _, _ => by + rw [feasible.go]; cases rem with | nil => rfl | cons => cases hf + | fuel+1, acc, rem, hf, hacc, h => by + rw [feasible.go] + split <;> [rename_i ha; rename_i a ha] + · -- the first element of the chain is addable, so `find?` cannot fail + cases h with | nil => rfl | @cons b _ _ hm hd + exact absurd (addable_complete hs hacc hd) (by simpa using List.find?_eq_none.1 ha _ hm) + · have hm := List.mem_of_find?_eq_some ha + have hd := addable_sound (List.find?_eq_some_iff_getElem.1 ha).1 + have hext := Extend?.orderedInsert (cmp := Name.cmp) (v := a) (p := acc) + refine feasible_go_complete hs ?_ ?_ ((h.exchange hm hd).mono fun x hx => hext.mem.2 ?_) + · rw [List.length_erase_of_mem hm]; omega + · match he : Normalize.orderedInsert Name.cmp a acc with + | none => exact hacc + | some acc' => exact hacc.orderedInsert he + · exact List.mem_cons.1 hx + +theorem NormLevel.feasible_complete {s : NormLevel} (hs : ∀ p n, s.get? p = some n → Sorted p) + (hacc : Sorted acc) (h : s.Feas acc rem) : s.feasible acc rem := + feasible_go_complete hs (Nat.le_refl _) hacc h + +theorem NormLevel.Feas.perm {s : NormLevel} (h : Feas s acc rem) (hp : rem.Perm rem') : + Feas s acc rem' := by + induction h generalizing rem' with + | nil => cases hp.nil_eq; exact .nil + | cons hm hd _ ih => exact .cons (hp.mem_iff.1 hm) hd (ih (hp.erase _)) + +/-- Extend a chain on the inside: the new element's conditions are all the others. -/ +theorem NormLevel.Feas.cons_last {s : NormLevel} (h : Feas s acc rem) (hnm : a ∉ rem) + (hd : s.Dom a (acc ++ rem)) : Feas s acc (a :: rem) := by + induction h with + | nil => exact .cons (.head _) (by simpa using hd) (by simpa using Feas.nil) + | @cons acc b rem hmb hdb _ ih => + have hab : b ≠ a := fun e => hnm (e ▸ hmb) + refine .cons (.tail _ hmb) hdb ?_ + rw [List.erase_cons_tail (by simpa using Ne.symm hab)] + refine ih (fun h => hnm (List.mem_of_mem_erase h)) (hd.mono fun x hx => ?_) + -- everything outside `a` is still there: `b` moved into the accumulator + simp only [List.cons_append, List.mem_cons, List.mem_append] at hx ⊢ + obtain hx | hx := hx + · exact .inr (.inl hx) + · by_cases hxb : x = b + · exact .inl hxb + · exact .inr (.inr ((List.mem_erase_of_ne hxb).2 hx)) + +/-- Every key of a well-formed normal form admits a chain: its `WF` parent is a key with one +condition fewer, and the variable relating them dominates the edge between them. -/ +theorem NormLevel.WF.feas {s : NormLevel} (wf : s.WF) : ∀ {p}, s.contains p → s.Feas [] p := by + intro p + generalize eq : p.length = len + induction len generalizing p with + | zero => cases List.eq_nil_of_length_eq_zero eq; exact fun _ => .nil + | succ len ih => + intro hp + have hne : p ≠ [] := by rintro rfl; cases eq + obtain ⟨n, hn⟩ := Option.isSome_iff_exists.1 (Std.TreeMap.isSome_getElem?_eq_contains.trans hp) + obtain ⟨v, p', h1, h2, x, hx, hxv⟩ := (wf _ _ hn).1 hne + have hperm : p.Perm (v :: p') := by cases h1; exact List.perm_middle + have hnm : v ∉ p' := by + have := (wf.sortedOf (.inr hp)).nodup + rw [hperm.nodup_iff] at this + exact (List.nodup_cons.1 this).1 + refine Feas.perm ?_ hperm.symm + refine Feas.cons_last (h2.elim (fun e => by subst e; exact .nil) (fun h => ih (by + have := h1.length; omega) h)) hnm ⟨p, n, hn, ⟨x, hx, hxv⟩, fun y hy => ?_⟩ + simpa using (h1.mem.1 hy).imp id id + +/-- Domination only reads off variable names and their keys, so a map that covers another +dominates whatever it does. -/ +theorem NormLevel.Dom.mono_map {s s' : NormLevel} (h : s'.Covers s) (hd : s.Dom a acc) : + s'.Dom a acc := by + obtain ⟨p, n, hp, ⟨x, hx, hxa⟩, hcond⟩ := hd + obtain ⟨q, m, y, hq, hy, e, hsub⟩ := h _ _ _ hp hx + exact ⟨q, m, hq, ⟨y, hy, e.trans hxa⟩, fun z hz => hcond _ (hsub _ hz)⟩ + +theorem NormLevel.Feas.mono_map {s s' : NormLevel} (h : s'.Covers s) : + ∀ {acc rem}, s.Feas acc rem → s'.Feas acc rem + | _, _, .nil => .nil + | _, _, .cons hm hd H => .cons hm (hd.mono_map h) (Feas.mono_map h H) + +/-- Every key of the normal form admits a chain. `WF.feas` gives this for the map `normalizeAux` +builds; subsumption keeps it because it covers that map, dropping a variable only in favour of +one with the same name at a smaller key. -/ +theorem normalize_feas : ∀ p, (normalize u).contains p → (normalize u).Feas [] p := by + intro p hp + have wf : (normalizeAux u [] 0 {}).WF := normalizeAux_wf (by simp) (by simp [NormLevel.WF]) + refine NormLevel.Feas.mono_map NormLevel.subsumption_covers.2 (wf.feas ?_) + obtain ⟨n, hn⟩ := Option.isSome_iff_exists.1 (Std.TreeMap.isSome_getElem?_eq_contains.trans hp) + obtain ⟨n₀, h₀, -⟩ := NormLevel.subsumption_covers.1 p n + (by rw [Std.TreeMap.get?_eq_getElem?]; exact hn) + exact Std.TreeMap.isSome_getElem?_eq_contains.symm.trans + (by simp [Std.TreeMap.get?_eq_getElem?] at h₀; simp [h₀]) + +/-- An admissible chain, innermost first: each element is dominated relative to the +conditions outside it. -/ +def NormLevel.Adm (s : NormLevel) : List Name → Prop + | [] => True + | a :: l => s.Dom a l ∧ s.Adm l + +/-- `lexChain` always reorders its input, even in the fallback branch. -/ +theorem NormLevel.lexChain_perm {s : NormLevel} : ∀ {fuel p}, (s.lexChain fuel p).Perm p + | 0, p => by rw [lexChain] + | fuel+1, p => by + rw [lexChain] + split + · rename_i a ha + exact .trans (.cons _ lexChain_perm) + (List.perm_cons_erase (List.mem_of_find?_eq_some ha)).symm + · exact .refl _ + +/-- Whenever a key admits some chain, `lexChain` returns one: it reorders the key, and +every edge of the resulting `imax` chain is dominated. -/ +theorem NormLevel.lexChain_spec {s : NormLevel} (hs : ∀ p n, s.get? p = some n → Sorted p) : + ∀ {fuel p}, p.length ≤ fuel → Sorted p → s.Feas [] p → + (s.lexChain fuel p).Perm p ∧ s.Adm (s.lexChain fuel p) + | 0, p, hf, _, _ => by + rw [lexChain]; cases p with | nil => exact ⟨.refl _, trivial⟩ | cons => cases hf + | fuel+1, p, hf, hp, h => by + rw [lexChain] + split + · rename_i a ha + have hm := List.mem_of_find?_eq_some ha + have hpred := List.find?_eq_some_iff_getElem.1 ha |>.1 + simp only [Bool.and_eq_true] at hpred + have hlen : (p.erase a).length ≤ fuel := by + rw [List.length_erase_of_mem hm]; omega + obtain ⟨hperm, hadm⟩ := + lexChain_spec hs hlen hp.erase (feasible_sound hpred.2) + refine ⟨.trans (.cons _ hperm) (List.perm_cons_erase hm).symm, ?_, hadm⟩ + exact (addable_sound hpred.1).mono fun x hx => hperm.mem_iff.2 hx + · rename_i hnone + -- the chain that exists ends somewhere, and `find?` would have found that element + refine ⟨.refl _, ?_⟩ + match p, h with + | [], _ => exact trivial + | b :: p, h => + obtain ⟨a, hm, hd, hfa⟩ := h.peel hp.nodup (by simp) + have h1 : s.addable a ((b :: p).erase a) := + addable_complete hs hp.erase (by simpa using hd) + have h2 : s.feasible [] ((b :: p).erase a) := feasible_complete hs Sorted.nil hfa + have hnot := List.find?_eq_none.1 hnone _ hm + simp [h1, h2] at hnot + +theorem NormLevel.Adm.suffix {s : NormLevel} : ∀ {l}, s.Adm l → a :: q <:+ l → s.Dom a q + | [], _, h => by simp at h + | b :: l, ⟨h1, h2⟩, h => by + obtain ⟨l', he⟩ := h + match l' with + | [] => cases he; exact h1 + | c :: l' => exact Adm.suffix h2 ⟨l', by cases he; rfl⟩ + +/-! ### Building the tree -/ + +theorem evalPath_le_self : evalPath ls ρ path c ≤ c := by rw [evalPath]; split <;> simp + +theorem evalPath_perm (h : p.Perm p') : evalPath ls ρ p c = evalPath ls ρ p' c := by + simp only [evalPath, show allNZ ls ρ p = allNZ ls ρ p' from Bool.eq_iff_iff.2 + ⟨allNZ_mono fun _ hx => h.symm.mem_iff.1 hx, allNZ_mono fun _ hx => h.mem_iff.1 hx⟩] + +theorem evalPath_singleton : + evalPath ls ρ [a] c = if 0 < evalParam ls ρ a then c else 0 := by simp [evalPath, allNZ] + +theorem evalPath_single : evalPath ls ρ p (evalPath ls ρ [a] c) = evalPath ls ρ (a :: p) c := by + rw [evalPath_singleton, ← evalPath_cons] + +theorem imax_eq_evalPath : Lean.Nat.imax c (evalParam ls ρ a) = + max' (evalPath ls ρ [a] c) (evalPath ls ρ [a] (evalParam ls ρ a)) := by + by_cases h : evalParam ls ρ a = 0 <;> + simp [imax_eq_ite, evalPath_singleton, h, Nat.pos_of_ne_zero] + +/-- `modify` read from the outside in, matching the way `Tree.At` extends a path: the +shallowest element of the path selects a child, and the rest is modified inside it. -/ +theorem Tree.modify_append (path : List Name) (g : Tree → Tree) (b : Name) (t : Tree) : + Tree.modify (path ++ [b]) g t = + { t with child := modifyAt (Tree.modify path g) b t.child } := by + induction path generalizing t g with + | nil => rfl + | cons a p ih => rw [List.cons_append, Tree.modify, ih]; rfl + +/-- All that matters about `modifyAt`: it replaces one entry with key `a` by `f` of it, or +inserts `(a, f default)` somewhere if there is none, and leaves the rest of the list alone. -/ +theorem modifyAt_eq (f : Tree → Tree) (a : Name) (l : List (Name × Tree)) : + ∃ l₁ l₂ y, modifyAt f a l = l₁ ++ (a, f y) :: l₂ ∧ + (l = l₁ ++ l₂ ∧ y = default ∨ l = l₁ ++ (a, y) :: l₂) := by + induction l with + | nil => exact ⟨[], [], default, rfl, .inl ⟨rfl, rfl⟩⟩ + | cons b l ih => + obtain ⟨b, t⟩ := b + match he : Name.cmp a b with + | .lt => exact ⟨[], (b, t) :: l, default, by simp [modifyAt, he], .inl ⟨rfl, rfl⟩⟩ + | .eq => + rw [Std.LawfulBEqCmp.compare_eq_iff_beq (cmp := Name.cmp)] at he + cases eq_of_beq he + exact ⟨[], l, t, by + simp [modifyAt, Std.ReflCmp.compare_self (cmp := Name.cmp)], .inr rfl⟩ + | .gt => + obtain ⟨l₁, l₂, y, h1, h2⟩ := ih + exact ⟨(b, t) :: l₁, l₂, y, by simp [modifyAt, he, h1], + h2.imp (fun ⟨h, hy⟩ => ⟨by simp [h], hy⟩) fun h => by simp [h]⟩ + +theorem mem_modifyAt_self (f : Tree → Tree) (a : Name) (l : List (Name × Tree)) : + ∃ y, (a, f y) ∈ modifyAt f a l := by + obtain ⟨l₁, l₂, y, h, -⟩ := modifyAt_eq f a l + exact ⟨y, by rw [h]; simp⟩ + +/-- The node `modify` writes is there to be found, and its data does not depend on what was +at the path before: the payload of a key is what sits at the end of its chain. -/ +theorem Tree.At_modify_self (path : List Name) (g : Tree → Tree) (t : Tree) : + ∃ t₀, Tree.At (t.modify path g) path (g t₀) := by + suffices ∀ (r : List Name) (g : Tree → Tree) (t : Tree), + ∃ t₀, Tree.At (Tree.modify r.reverse g t) r.reverse (g t₀) by + simpa using this path.reverse g t + clear path g t; intro r + induction r with + | nil => exact fun g t => ⟨t, .nil⟩ + | cons b r ih => + intro g t + rw [List.reverse_cons, Tree.modify_append] + obtain ⟨x, hx⟩ := mem_modifyAt_self (f := Tree.modify r.reverse g) b t.child + obtain ⟨t₀, ht₀⟩ := ih g x + exact ⟨t₀, ht₀.append hx⟩ + +/-- Nothing is lost: an entry either survives `modifyAt` untouched, or is the one it modifies. +(The second case does not need the entry to be the *first* one with its key, so no +duplicate-freedom assumption is needed here or below.) -/ +theorem mem_modifyAt {f : Tree → Tree} {l : List (Name × Tree)} (hm : (c, x) ∈ l) : + (c, x) ∈ modifyAt f a l ∨ (c = a ∧ (c, f x) ∈ modifyAt f a l) := by + obtain ⟨l₁, l₂, y, h, h'⟩ := modifyAt_eq f a l + rw [h] + obtain ⟨rfl, -⟩ | rfl := h' + · obtain hm | hm := List.mem_append.1 hm + · exact .inl (List.mem_append.2 (.inl hm)) + · exact .inl (List.mem_append.2 (.inr (.tail _ hm))) + · obtain hm | hm := List.mem_append.1 hm + · exact .inl (List.mem_append.2 (.inl hm)) + obtain heq | hm := List.mem_cons.1 hm + · simp only [Prod.mk.injEq] at heq; obtain ⟨rfl, rfl⟩ := heq + exact .inr ⟨rfl, List.mem_append.2 (.inr (.head _))⟩ + · exact .inl (List.mem_append.2 (.inr (.tail _ hm))) + +/-- A node written at one path survives a later write at a different path: the write only +replaces the data of the node it lands on, and every other node keeps its own. -/ +theorem Tree.At_modify_of_ne_aux {g : Tree → Tree} (hg : ∀ t, (g t).child = t.child) : + ∀ (r : List Name) {path t t'}, path ≠ r.reverse → At t path t' → + ∃ t'', At (Tree.modify r.reverse g t) path t'' ∧ + t''.const = t'.const ∧ t''.var = t'.var := by + intro r + induction r with + | nil => + intro path t t' hne h + rw [List.reverse_nil, Tree.modify] + obtain h' | ⟨rfl, rfl⟩ := h.of_child_eq (hg t) + · exact ⟨t', h', rfl, rfl⟩ + · exact absurd rfl hne + | cons b r ih => + intro path t t' hne h + rw [List.reverse_cons, Tree.modify_append] + obtain rfl | ⟨q, a, rfl⟩ := List.eq_nil_or_concat path + · cases h.nil_inv; exact ⟨_, .nil, rfl, rfl⟩ + simp only [List.concat_eq_append, List.reverse_cons] at h hne ⊢ + obtain ⟨t₁, hm, h₁⟩ := h.append_inv + obtain hm' | ⟨rfl, hm'⟩ := mem_modifyAt (f := Tree.modify r.reverse g) (a := b) hm + · exact ⟨t', h₁.append hm', rfl, rfl⟩ + · have : q ≠ r.reverse := by rintro rfl; exact hne rfl + obtain ⟨t'', h'', hc, hv⟩ := ih this h₁ + exact ⟨t'', h''.append hm', hc, hv⟩ + +theorem Tree.At_modify_of_ne {g : Tree → Tree} (hg : ∀ t, (g t).child = t.child) + (hne : path ≠ path') (h : At t path t') : + ∃ t'', At (Tree.modify path' g t) path t'' ∧ t''.const = t'.const ∧ t''.var = t'.var := by + have := At_modify_of_ne_aux hg path'.reverse (path := path) (by rwa [List.reverse_reverse]) h + rwa [List.reverse_reverse] at this + +/-- Conversely, nothing appears from nowhere: an entry of `modifyAt` is an entry of the list, +or the modified one, which was an entry or is fresh. -/ +theorem mem_modifyAt_inv {f : Tree → Tree} {l : List (Name × Tree)} + (h : (c, x) ∈ modifyAt f a l) : + (c, x) ∈ l ∨ (c = a ∧ ∃ y, x = f y ∧ (y = default ∨ (a, y) ∈ l)) := by + induction l with + | nil => + simp only [modifyAt, List.mem_singleton, Prod.mk.injEq] at h + obtain ⟨rfl, rfl⟩ := h + exact .inr ⟨rfl, default, rfl, .inl rfl⟩ + | cons b l ih => + obtain ⟨b, t⟩ := b + match he : Name.cmp a b with + | .lt => + simp only [modifyAt, he] at h + obtain h | h := List.mem_cons.1 h + · cases h; exact .inr ⟨rfl, default, rfl, .inl rfl⟩ + · exact .inl h + | .eq => + rw [Std.LawfulBEqCmp.compare_eq_iff_beq (cmp := Name.cmp)] at he + cases eq_of_beq he + simp only [modifyAt, Std.ReflCmp.compare_self (cmp := Name.cmp)] at h + obtain h | h := List.mem_cons.1 h + · cases h; exact .inr ⟨rfl, t, rfl, .inr (.head _)⟩ + · exact .inl (.tail _ h) + | .gt => + simp only [modifyAt, he] at h + obtain h | h := List.mem_cons.1 h + · cases h; exact .inl (.head _) + · exact (ih h).imp (.tail _) fun ⟨rfl, y, hy, h⟩ => ⟨rfl, y, hy, h.imp id (.tail _)⟩ + +theorem Tree.At.of_child_nil (hc : t.child = []) (h : At t p t') : p = [] ∧ t' = t := by + obtain rfl | ⟨q, a, rfl⟩ := List.eq_nil_or_concat p + · exact ⟨rfl, h.nil_inv⟩ + · rw [List.concat_eq_append] at h + obtain ⟨t₁, hm, -⟩ := h.append_inv + rw [hc] at hm; cases hm + +theorem suffix_concat {α} {l₁ l₂ : List α} (h : l₁ <:+ l₂) (a : α) : + l₁ ++ [a] <:+ l₂ ++ [a] := by + obtain ⟨u, rfl⟩ := h; exact ⟨u, by rw [List.append_assoc]⟩ + +/-- Inverting a write: a path of the modified tree either ends at the node just written, or +is a tail of the written path whose node was created empty on the way, or was already there +carrying the same data. -/ +theorem Tree.At_modify_inv_aux {g : Tree → Tree} (hg : ∀ t, (g t).child = t.child) : + ∀ (r : List Name) {path t t'}, At (Tree.modify r.reverse g t) path t' → + (path = r.reverse ∧ ∃ t₀, t' = g t₀) ∨ + (path <:+ r.reverse ∧ t'.const = 0 ∧ t'.var = []) ∨ + (∃ t'', At t path t'' ∧ t'.const = t''.const ∧ t'.var = t''.var) := by + intro r + induction r with + | nil => + intro path t t' h + rw [List.reverse_nil, Tree.modify] at h + obtain h' | ⟨rfl, rfl⟩ := h.of_child_eq (hg t).symm + · exact .inr (.inr ⟨t', h', rfl, rfl⟩) + · exact .inl ⟨rfl, t, rfl⟩ + | cons b r ih => + intro path t t' h + rw [List.reverse_cons, Tree.modify_append] at h + obtain rfl | ⟨q, a, rfl⟩ := List.eq_nil_or_concat path + · cases h.nil_inv; exact .inr (.inr ⟨t, .nil, rfl, rfl⟩) + rw [List.concat_eq_append] at h ⊢ + obtain ⟨t₁, hm, h₁⟩ := h.append_inv + obtain hm | ⟨rfl, y, rfl, hy⟩ := mem_modifyAt_inv hm + · exact .inr (.inr ⟨t', h₁.append hm, rfl, rfl⟩) + · obtain ⟨rfl, t₀, rfl⟩ | ⟨hs, hc, hv⟩ | ⟨t'', h'', hc, hv⟩ := ih h₁ + · exact .inl ⟨by rw [List.reverse_cons], t₀, rfl⟩ + · exact .inr (.inl ⟨by rw [List.reverse_cons]; exact suffix_concat hs _, hc, hv⟩) + · obtain rfl | hy := hy + · obtain ⟨rfl, rfl⟩ := h''.of_child_nil rfl + exact .inr (.inl ⟨by rw [List.reverse_cons]; exact ⟨r.reverse, by simp⟩, hc, hv⟩) + · exact .inr (.inr ⟨t'', h''.append hy, hc, hv⟩) + +theorem Tree.At_modify_inv {g : Tree → Tree} (hg : ∀ t, (g t).child = t.child) + (h : At (Tree.modify path' g t) path t') : + (path = path' ∧ ∃ t₀, t' = g t₀) ∨ + (path <:+ path' ∧ t'.const = 0 ∧ t'.var = []) ∨ + (∃ t'', At t path t'' ∧ t'.const = t''.const ∧ t'.var = t''.var) := by + have := At_modify_inv_aux hg path'.reverse (path := path) + (by rwa [List.reverse_reverse]) (t' := t') + rwa [List.reverse_reverse] at this + +/-- Sorted lists with the same elements are equal, so distinct keys reify to distinct paths: +`lexChain` only permutes a key. -/ +theorem Sorted.perm_eq (h₁ : Sorted l₁) (h₂ : Sorted l₂) (h : l₁.Perm l₂) : l₁ = l₂ := by + induction l₁ generalizing l₂ with + | nil => exact h.nil_eq + | cons a l₁ ih => + match l₂, h₂, h with + | [], _, h => simp at h + | b :: l₂, h₂, h => + have hab : a = b := by + -- each head is at most every element of the other list + obtain rfl | ha := List.mem_cons.1 (h.mem_iff.1 (.head _)) + · rfl + obtain rfl | hb := List.mem_cons.1 (h.symm.mem_iff.1 (.head _)) + · rfl + exact absurd (h₂.head _ ha) (by + rw [Std.OrientedCmp.gt_of_lt (h₁.head _ hb)]; simp) + subst hab + rw [ih h₁.of_cons h₂.of_cons ((List.perm_cons _).1 h)] + +/-- The variables the reconstruction records for the entry `(p, n)`: those of `n`, except the +one the edge into the node already contributes. -/ +def NormLevel.treeVar (s : NormLevel) (p : List Name) (n : Node) : List VarNode := + if let v :: _ := s.lexChain p.length p then subsumeVars n.var [⟨v, 0⟩] else n.var + +/-- The entry `(p, n)` is recorded in `t`: at the end of `p`'s chain sits a node carrying +`n`'s constant and `treeVar p n`. -/ +def NormLevel.WrittenAt (s : NormLevel) (t : Tree) (p : List Name) (n : Node) : Prop := + ∃ t', Tree.At t (s.lexChain p.length p) t' ∧ t'.const = n.const ∧ t'.var = s.treeVar p n + +theorem NormLevel.WrittenAt.write {s : NormLevel} (t : Tree) (p : List Name) (n : Node) : + s.WrittenAt (t.modify (s.lexChain p.length p) + fun t => { t with const := n.const, var := s.treeVar p n }) p n := + let ⟨_, h⟩ := Tree.At_modify_self _ _ t + ⟨_, h, rfl, rfl⟩ + +/-- Distinct keys get distinct chains, since `lexChain` only permutes a sorted key. -/ +theorem NormLevel.lexChain_inj {s : NormLevel} (h₁ : Sorted p) (h₂ : Sorted p') + (h : s.lexChain p.length p = s.lexChain p'.length p') : p = p' := by + refine Sorted.perm_eq h₁ h₂ ((lexChain_perm (s := s) (fuel := p.length) (p := p)).symm.trans ?_) + rw [h]; exact lexChain_perm + +/-- Conversely, everything the tree contains comes from an entry: every nonempty path is a +tail of some key's chain, and the node at the end of a path is either empty scaffolding or +the entry whose chain leads there. -/ +def NormLevel.Accounted (s : NormLevel) (t : Tree) : Prop := + ∀ path t', Tree.At t path t' → + (path ≠ [] → ∃ p n, s.get? p = some n ∧ path <:+ s.lexChain p.length p) ∧ + (t'.const = 0 ∧ t'.var = [] ∨ ∃ p n, s.get? p = some n ∧ + path = s.lexChain p.length p ∧ t'.const = n.const ∧ t'.var = s.treeVar p n) + +/-- The single pass over the map that both directions of soundness read off: after the fold +every entry is recorded at the end of its chain, and everything in the tree is accounted for +by an entry. A write puts its own entry there (`At_modify_self`) and leaves the others alone, +either because it lands on a different path — distinct keys have distinct chains — or because +it lands on the same key, and then writes the same data. -/ +theorem NormLevel.toTree_spec {s : NormLevel} (hsort : ∀ p n, s.get? p = some n → Sorted p) : + s.Accounted (toTree s) ∧ ∀ p n, s.get? p = some n → s.WrittenAt (toTree s) p n := by + rw [toTree, Std.TreeMap.foldl_eq_foldl_toList] + have hmem : ∀ pn : List Name × Node, pn ∈ s.toList ↔ s.get? pn.1 = some pn.2 := fun _ => + Std.TreeMap.mem_toList_iff_getElem?_eq_some.trans (by rw [Std.TreeMap.get?_eq_getElem?]) + have hinit : s.Accounted ⟨0, [], []⟩ := fun path t' h => by + obtain ⟨rfl, rfl⟩ := h.of_child_nil rfl + exact ⟨fun h => absurd rfl h, .inl ⟨rfl, rfl⟩⟩ + suffices ∀ (l : List (List Name × Node)) (t : Tree), + (∀ pn ∈ l, s.get? pn.1 = some pn.2) → s.Accounted t → + s.Accounted (List.foldl (fun t pn => + let path := s.lexChain pn.1.length pn.1 + let var := if let v :: _ := path then subsumeVars pn.2.var [⟨v, 0⟩] else pn.2.var + t.modify path fun t => { t with const := pn.2.const, var }) t l) ∧ + ∀ p n, s.get? p = some n → (s.WrittenAt t p n ∨ (p, n) ∈ l) → + s.WrittenAt (List.foldl (fun t pn => + let path := s.lexChain pn.1.length pn.1 + let var := if let v :: _ := path then subsumeVars pn.2.var [⟨v, 0⟩] else pn.2.var + t.modify path fun t => { t with const := pn.2.const, var }) t l) p n by + have := this _ _ (fun pn h => (hmem pn).1 h) hinit + exact ⟨this.1, fun p n hp => this.2 p n hp (.inr ((hmem (p, n)).2 hp))⟩ + clear hmem hinit; intro l + induction l with + | nil => exact fun _ _ h => ⟨h, fun _ _ _ h => h.resolve_right (by simp)⟩ + | cons pn l ih => + obtain ⟨p', n'⟩ := pn + intro t hl hacc + have hp' : s.get? p' = some n' := hl _ (.head _) + refine (ih _ (fun _ h => hl _ (.tail _ h)) ?_).imp id fun H p n hp h => H p n hp ?_ + · -- nothing unaccounted for appears: the write adds its own node and empty scaffolding + intro path t' h + obtain ⟨rfl, t₀, rfl⟩ | ⟨hs, hc, hv⟩ | ⟨t'', h'', hc, hv⟩ := + Tree.At_modify_inv (g := fun t => + { t with const := n'.const, var := s.treeVar p' n' }) (fun _ => rfl) h + · exact ⟨fun _ => ⟨p', n', hp', List.suffix_refl _⟩, .inr ⟨p', n', hp', rfl, rfl, rfl⟩⟩ + · exact ⟨fun _ => ⟨p', n', hp', hs⟩, .inl ⟨hc, hv⟩⟩ + · exact ⟨(hacc _ _ h'').1, by rw [hc, hv]; exact (hacc _ _ h'').2⟩ + · -- and nothing already written is lost + obtain ⟨t', hat, hc, hv⟩ | h := h + · refine .inl ?_ + by_cases hpp : p = p' + · subst hpp; cases hp.symm.trans hp'; exact .write .. + · obtain ⟨t'', hat', hc', hv'⟩ := Tree.At_modify_of_ne (g := fun t => + { t with const := n'.const, var := s.treeVar p' n' }) (fun _ => rfl) + (fun he => hpp (lexChain_inj (hsort _ _ hp) (hsort _ _ hp') he)) hat + exact ⟨t'', hat', hc' ▸ hc, hv' ▸ hv⟩ + · obtain h | h := List.mem_cons.1 h + · simp only [Prod.mk.injEq] at h + obtain ⟨rfl, rfl⟩ := h + exact .inl (.write ..) + · exact .inr h + +/-- What the reconstruction contributes, as a biconditional: the tree is bounded by `m` +exactly when for every entry the node the tree records for it is, and so is every edge of its +chain. Nothing here is about domination, so no hypothesis on the chains is needed. -/ +theorem NormLevel.toTree_le_iff {s : NormLevel} (hsort : ∀ p n, s.get? p = some n → Sorted p) + {m : Nat} : Tree.eval ls ρ (toTree s) ≤ m ↔ + ∀ p n, s.get? p = some n → + evalPath ls ρ (s.lexChain p.length p) (Node.eval ls ρ ⟨n.const, s.treeVar p n⟩) ≤ m ∧ + ∀ a q, a :: q <:+ s.lexChain p.length p → + evalPath ls ρ (a :: q) (evalParam ls ρ a) ≤ m := by + obtain ⟨hacc, hwr⟩ := toTree_spec hsort + rw [Tree.eval_le_iff] + refine ⟨fun ⟨h1, h2⟩ p n hp => ?_, fun H => ⟨fun path t' hat => ?_, fun a q t' hat => ?_⟩⟩ + · obtain ⟨t', hat, hc, hv⟩ := hwr p n hp + refine ⟨by rw [← hc, ← hv]; exact h1 _ _ hat, fun a q hq => ?_⟩ + obtain ⟨t'', hat''⟩ := hat.suffix hq + exact h2 _ _ _ hat'' + · obtain ⟨-, ⟨hc, hv⟩ | ⟨p, n, hp, rfl, hc, hv⟩⟩ := hacc _ _ hat + · rw [show Node.eval ls ρ ⟨t'.const, t'.var⟩ = 0 from by simp [Node.eval, hc, hv]] + simp [evalPath] + · rw [hc, hv]; exact (H p n hp).1 + · obtain ⟨p, n, hp, hsuf⟩ := (hacc _ _ hat).1 (by simp) + exact (H p n hp).2 _ _ hsuf + +/-- Soundness of the reconstruction: the tree built from a normal form, hence the level it +reifies to, evaluates like the normal form. Below, because the node recorded for an entry +carries a subset of its sublevels and every edge is dominated, `lexChain` emitting only +admissible chains; above, because the one sublevel the node omits, `V(p, v, 0)` for the +innermost element of the chain, is what the edge into it contributes. -/ +theorem NormLevel.toTree_eval {s : NormLevel} (hsort : ∀ p n, s.get? p = some n → Sorted p) + (hfeas : ∀ p, s.contains p → s.Feas [] p) : + Tree.eval ls ρ (toTree s) = s.eval ls ρ := by + refine ext_le fun m => (toTree_le_iff hsort).trans (Iff.trans ?_ NormLevel.eval_le.symm) + refine ⟨fun H p n hp => ?_, fun H p n hp => ⟨?_, fun a q hq => ?_⟩⟩ + · -- the entry is the node the tree records for it, plus the edge into that node + rw [← evalPath_perm (lexChain_perm (s := s) (fuel := p.length) (p := p))] + refine evalPath_le.2 fun nz => ?_ + have h1 := evalPath_le.1 (H p n hp).1 nz + rw [Node.eval_le] at h1 ⊢ + refine ⟨h1.1, ?_⟩ + rw [NormLevel.treeVar] at h1 + split at h1 + · rename_i v q hch + refine (subsumeVars_eval ?_).1 h1.2 + simp only [List.mem_singleton, VarNode.eval] + rintro _ rfl + exact evalPath_le.1 ((H p n hp).2 v q (by rw [hch]; exact List.suffix_refl _)) (hch ▸ nz) + · exact h1.2 + · -- the recorded node is part of the entry + rw [evalPath_perm (lexChain_perm (s := s) (fuel := p.length) (p := p))] + refine Nat.le_trans (evalPath_mono ?_) (H p n hp) + refine Node.eval_le.2 ⟨Node.const_le_eval (l := n), fun v hv => Node.var_le_eval ?_⟩ + revert hv; rw [NormLevel.treeVar]; split + · exact subsumeVars_subset + · exact id + · -- and every edge of the chain is dominated by an entry + have hcon : s.contains p := Std.TreeMap.isSome_getElem?_eq_contains.symm.trans + (by simp [Std.TreeMap.get?_eq_getElem?] at hp; simp [hp]) + obtain ⟨-, hadm⟩ := lexChain_spec hsort (Nat.le_refl _) (hsort _ _ hp) (hfeas _ hcon) + exact Nat.le_trans (hadm.suffix hq).le (NormLevel.eval_le.2 H) + +/-! +### Completeness + +`geq'` and `isEquiv'` are not only sound but complete: `NormLevel.le` detects every semantic +inequality between normal forms, and semantically equal levels have equal normal forms. The +key is a converse to Theorem 39 (`NormLevel.le_eval`): evaluating at a valuation tailored to +a single sublevel shows that a semantic bound forces a syntactic dominator among the +sublevels of the bounding form (`separation`). Completeness of `le` then follows because the +`subsumeBy` fold removes exactly the dominated sublevels, and canonicity because +`subsumption` leaves no sublevel dominated by another slot (`Reduced`), so mutual domination +forces the two maps to be equal. +-/ + +/-- The variable lists of nodes are strictly sorted by variable name. -/ +def VarsSorted (l : List VarNode) : Prop := l.Pairwise (compare ·.var ·.var = .lt) + +theorem VarsSorted.of_cons (h : VarsSorted (v :: l)) : VarsSorted l := (List.pairwise_cons.1 h).2 + +theorem VarsSorted.head (h : VarsSorted (v :: l)) : ∀ x ∈ l, compare v.var x.var = .lt := + (List.pairwise_cons.1 h).1 + +/-- In a sorted variable list, the name determines the entry. -/ +theorem VarsSorted.eq_of_var_eq (h : VarsSorted l) (h₁ : x ∈ l) (h₂ : y ∈ l) + (e : x.var = y.var) : x = y := by + induction l with | nil => cases h₁ | cons v l ih + obtain rfl | h₁' := List.mem_cons.1 h₁ + · obtain rfl | h₂' := List.mem_cons.1 h₂ + · rfl + · have := h.head _ h₂'; rw [e, Std.ReflOrd.compare_self] at this; cases this + · obtain rfl | h₂' := List.mem_cons.1 h₂ + · have := h.head _ h₁'; rw [← e, Std.ReflOrd.compare_self] at this; cases this + · exact ih h.of_cons h₁' h₂' + +theorem VarNode.mem_addVar' (h : x ∈ VarNode.addVar v k l) : x.var = v ∨ x ∈ l := by + induction l with + | nil => simp [addVar] at h; simp [h] + | cons y l ih => + simp only [addVar] at h + split at h + · rcases List.mem_cons.1 h with rfl | h <;> simp [h] + · rcases List.mem_cons.1 h with rfl | h <;> simp [h] + · rcases List.mem_cons.1 h with rfl | h + · simp + · exact (ih h).imp_right (.tail _) + +theorem VarNode.addVar_sorted (h : VarsSorted l) : VarsSorted (VarNode.addVar v k l) := by + induction l with | nil => exact .cons (by simp) .nil | cons x l ih + simp only [addVar] + split <;> rename_i hc + · refine .cons (fun y hy => ?_) h + obtain rfl | hy := List.mem_cons.1 hy + · exact hc + · exact Std.TransCmp.lt_trans hc (h.head _ hy) + · rw [Std.LawfulBEqCmp.compare_eq_iff_beq] at hc + have e := eq_of_beq hc + exact .cons (fun y hy => by rw [e]; exact h.head _ hy) h.of_cons + · refine .cons (fun y hy => ?_) (ih h.of_cons) + obtain e | hy := VarNode.mem_addVar' hy + · rw [e]; exact Std.OrientedCmp.lt_of_gt hc + · exact h.head _ hy + +/-- Every node of the map has its variable list sorted by name. -/ +def NormLevel.SortedVars (s : NormLevel) : Prop := + ∀ p n, s.get? p = some n → VarsSorted n.var + +theorem NormLevel.addVar_sortedVars (h : acc.SortedVars) : + (addVar v k path acc).SortedVars := by + intro p n hn + simp only [addVar, Std.TreeMap.get?_eq_getElem?, Std.TreeMap.getElem?_modify] at hn + split at hn + · obtain ⟨n', hn', rfl⟩ := Option.map_eq_some_iff.1 hn + exact VarNode.addVar_sorted (h path n' (Std.TreeMap.get?_eq_getElem? .. ▸ hn')) + · exact h _ _ (Std.TreeMap.get?_eq_getElem? .. ▸ hn) + +theorem NormLevel.addNode_sortedVars (h : acc.SortedVars) : + (addNode v k path acc).SortedVars := by + intro p n hn + simp only [addNode, Std.TreeMap.get?_eq_getElem?, Std.TreeMap.getElem?_alter] at hn + split at hn + · match e : acc[path]?, hn with + | some n', hn => + cases hn; exact VarNode.addVar_sorted (h path n' (Std.TreeMap.get?_eq_getElem? .. ▸ e)) + | none, hn => cases hn; exact .cons (by simp) .nil + · exact h _ _ (Std.TreeMap.get?_eq_getElem? .. ▸ hn) + +theorem NormLevel.addConst_sortedVars (h : acc.SortedVars) : + (addConst k path acc).SortedVars := by + intro p n hn + simp only [addConst] at hn; split at hn <;> [exact h _ _ hn; skip] + simp only [Std.TreeMap.get?_eq_getElem?, Std.TreeMap.getElem?_alter] at hn + split at hn + · match e : acc[path]?, hn with + | some n', hn => cases hn; exact h path n' (Std.TreeMap.get?_eq_getElem? .. ▸ e) + | none, hn => cases hn; exact .nil + · exact h _ _ (Std.TreeMap.get?_eq_getElem? .. ▸ hn) + +theorem normalizeAux_sortedVars (h : acc.SortedVars) : + (normalizeAux u path k acc).SortedVars := by + unfold normalizeAux; split + · exact NormLevel.addConst_sortedVars h + · exact NormLevel.addConst_sortedVars h + · exact normalizeAux_sortedVars h + · exact normalizeAux_sortedVars (normalizeAux_sortedVars h) + · exact normalizeAux_sortedVars (normalizeAux_sortedVars h) + · exact normalizeAux_sortedVars (normalizeAux_sortedVars h) + · exact normalizeAux_sortedVars (normalizeAux_sortedVars h) + · split <;> [skip; (dsimp; split)] + · exact normalizeAux_sortedVars + (NormLevel.addNode_sortedVars (NormLevel.addConst_sortedVars h)) + · exact normalizeAux_sortedVars h + · exact normalizeAux_sortedVars (NormLevel.addVar_sortedVars h) + · exact h + · exact h + · split <;> [skip; split] + · exact NormLevel.addNode_sortedVars (NormLevel.addConst_sortedVars h) + · exact h + · exact NormLevel.addVar_sortedVars h + +theorem subsumeVars_sublist : ∀ vs₁ vs₂ : List VarNode, List.Sublist (subsumeVars vs₁ vs₂) vs₁ + | [], _ => by simp [subsumeVars] + | _ :: _, [] => by simp [subsumeVars] + | x :: xs, y :: ys => by + simp only [subsumeVars]; split + · exact (subsumeVars_sublist xs (y :: ys)).cons_cons x + · split + · exact (subsumeVars_sublist xs ys).cons x + · exact (subsumeVars_sublist xs ys).cons_cons x + · exact subsumeVars_sublist (x :: xs) ys + +theorem Node.subsume_var_sublist : List.Sublist (Node.subsume p₁ n₁ p₂ n₂).var n₁.var := by + obtain h | ⟨-, -, h⟩ := Node.subsume_var_cases p₁ n₁ p₂ n₂ <;> rw [h] + · exact List.Sublist.refl _ + · exact subsumeVars_sublist .. + +theorem NormLevel.minimize_var_sublist {acc : NormLevel} : + List.Sublist (acc.minimize p₁ n₁).var n₁.var := by + rw [minimize, Std.TreeMap.foldl_eq_foldl_toList] + generalize acc.toList = l + induction l generalizing n₁ with | nil => exact List.Sublist.refl _ | cons a l ih + exact (ih (n₁ := Node.subsume p₁ n₁ a.1 a.2)).trans Node.subsume_var_sublist + +theorem NormLevel.subsumption_sortedVars {s : NormLevel} (hs : s.SortedVars) : + s.subsumption.SortedVars := by + rw [subsumption, Std.TreeMap.foldl_eq_foldl_toList] + have hmem pn (h : pn ∈ s.toList) : s.get? pn.1 = some pn.2 := + Std.TreeMap.get?_eq_getElem? .. ▸ Std.TreeMap.mem_toList_iff_getElem?_eq_some.1 h + generalize s.toList = l at hmem + suffices ∀ (l : List (List Name × Node)) (acc : NormLevel), + (∀ pn ∈ l, s.get? pn.1 = some pn.2) → acc.SortedVars → + ∀ p n, (List.foldl (fun acc pn => + let n := acc.minimize pn.1 pn.2 + if n.isEmpty then acc.erase pn.1 else acc.insert pn.1 n) acc l).get? p = some n → + VarsSorted n.var from this _ _ hmem hs + clear hmem; intro l + induction l with | nil => exact fun _ _ => id | cons pn l ih + intro acc hl hacc + refine ih _ (fun _ h => hl _ (.tail _ h)) fun p n h => ?_ + rw [subsumption_step_get?] at h + split at h + · split at h <;> [cases h; skip] + cases h; rename_i hp _; subst hp + exact (hs _ _ (hl _ (.head _))).sublist minimize_var_sublist + · exact hacc _ _ h + +theorem normalize_sortedVars : (normalize u).SortedVars := + NormLevel.subsumption_sortedVars (normalizeAux_sortedVars fun p n h => by simp at h) + +/-- `subsumption` erases a key rather than leaving an empty node behind. -/ +theorem NormLevel.subsumption_nonempty {s : NormLevel} : + ∀ p n, s.subsumption.get? p = some n → n.isEmpty = false := by + rw [subsumption, Std.TreeMap.foldl_eq_foldl_toList] + suffices ∀ (l : List (List Name × Node)) (acc : NormLevel), + (∀ p n, acc.get? p = some n → n.isEmpty = false ∨ (p, n) ∈ l) → + ∀ p n, (List.foldl (fun acc pn => + let n := acc.minimize pn.1 pn.2 + if n.isEmpty then acc.erase pn.1 else acc.insert pn.1 n) acc l).get? p = some n → + n.isEmpty = false from + this _ _ fun p n h => .inr (Std.TreeMap.mem_toList_iff_getElem?_eq_some.2 + (Std.TreeMap.get?_eq_getElem? .. ▸ h)) + intro l + induction l with + | nil => exact fun acc h p n hn => (h p n hn).resolve_right (by simp) + | cons pn l ih => + intro acc hacc + refine ih _ fun p n h => ?_ + rw [subsumption_step_get?] at h + split at h <;> rename_i hp + · split at h <;> [cases h; skip] + cases h; rename_i he; exact .inl (by simpa using he) + · refine (hacc _ _ h).imp_right fun hm => ?_ + obtain h' | h' := List.mem_cons.1 hm + · exact absurd (congrArg Prod.fst h'.symm) hp + · exact h' + +theorem normalize_nonempty : ∀ p n, (normalize u).get? p = some n → n.isEmpty = false := + NormLevel.subsumption_nonempty + +theorem NormLevel.addVar_keys (h : (addVar v k path acc).contains p) : acc.contains p := by + simpa [addVar, Std.TreeMap.mem_modify] using h + +theorem NormLevel.addNode_keys (h : (addNode v k path acc).contains p) : + p = path ∨ acc.contains p := by + rw [addNode, Std.TreeMap.contains_alter] at h + split at h + · rename_i hc + exact .inl (eq_of_beq (Std.LawfulBEqCmp.compare_eq_iff_beq.1 hc)).symm + · exact .inr h + +theorem NormLevel.addConst_keys (h : (addConst k path acc).contains p) : + p = path ∨ acc.contains p := by + rw [addConst] at h; split at h <;> [exact .inr h; skip] + rw [Std.TreeMap.contains_alter] at h + split at h + · rename_i hc + exact .inl (eq_of_beq (Std.LawfulBEqCmp.compare_eq_iff_beq.1 hc)).symm + · exact .inr h + +/-- All keys of the map built by `normalizeAux` consist of level parameters, which are +in `ls` whenever `ofLevel` succeeds. -/ +theorem normalizeAux_keys (hu : VLevel.ofLevel ls u = some u') + (hpath : ∀ x ∈ path, x ∈ ls) (hacc : ∀ p, acc.contains p → ∀ x ∈ p, x ∈ ls) : + ∀ p, (normalizeAux u path k acc).contains p → ∀ x ∈ p, x ∈ ls := by + unfold normalizeAux; split + · exact fun p h => (NormLevel.addConst_keys h).elim + (fun e x hx => hpath x (e ▸ hx)) (hacc p) + · exact fun p h => (NormLevel.addConst_keys h).elim + (fun e x hx => hpath x (e ▸ hx)) (hacc p) + · simp [VLevel.ofLevel] at hu; obtain ⟨_, hu, rfl⟩ := hu + exact normalizeAux_keys hu hpath hacc + · simp [VLevel.ofLevel] at hu; obtain ⟨_, hu, _, hv, rfl⟩ := hu + exact normalizeAux_keys hv hpath (normalizeAux_keys hu hpath hacc) + · simp [VLevel.ofLevel] at hu; obtain ⟨_, hu, _, ⟨_, hv, rfl⟩, rfl⟩ := hu + exact normalizeAux_keys hv hpath (normalizeAux_keys hu hpath hacc) + · rename_i u v w + simp [VLevel.ofLevel] at hu; obtain ⟨_, hu, _, ⟨_, hv, _, hw, rfl⟩, rfl⟩ := hu + exact normalizeAux_keys (by simpa [VLevel.ofLevel] using ⟨_, hu, _, hw, rfl⟩) hpath + (normalizeAux_keys (by simpa [VLevel.ofLevel] using ⟨_, hu, _, hv, rfl⟩) hpath hacc) + · rename_i u v w + simp [VLevel.ofLevel] at hu; obtain ⟨_, hu, _, ⟨_, hv, _, hw, rfl⟩, rfl⟩ := hu + exact normalizeAux_keys (by simpa [VLevel.ofLevel] using ⟨_, hv, _, hw, rfl⟩) hpath + (normalizeAux_keys (by simpa [VLevel.ofLevel] using ⟨_, hu, _, hw, rfl⟩) hpath hacc) + · rename_i u v + simp [VLevel.ofLevel] at hu; obtain ⟨_, hu, _, ⟨hv, rfl⟩, rfl⟩ := hu + have hvls : v ∈ ls := List.idxOf_lt_length_iff.1 hv + split <;> rename_i h + · refine normalizeAux_keys hu (fun x hx => ?_) fun q hq => ?_ + · exact ((Extend1.orderedInsert h).mem.1 hx).elim (fun e => e.symm ▸ hvls) (hpath x) + · obtain e | hq' := NormLevel.addNode_keys hq + · exact fun x hx => by + rcases (Extend1.orderedInsert h).mem.1 (e ▸ hx) with rfl | hx' + · exact hvls + · exact hpath x hx' + · exact (NormLevel.addConst_keys hq').elim + (fun e x hx => hpath x (e ▸ hx)) (hacc q) + · dsimp; split + · exact normalizeAux_keys hu hpath hacc + · exact normalizeAux_keys hu hpath fun q hq => hacc q (NormLevel.addVar_keys hq) + · exact hacc + · exact hacc + · rename_i v + simp [VLevel.ofLevel] at hu; obtain ⟨hv, rfl⟩ := hu + have hvls : v ∈ ls := List.idxOf_lt_length_iff.1 hv + split <;> rename_i h + · intro q hq + obtain e | hq' := NormLevel.addNode_keys hq + · exact fun x hx => by + rcases (Extend1.orderedInsert h).mem.1 (e ▸ hx) with rfl | hx' + · exact hvls + · exact hpath x hx' + · exact (NormLevel.addConst_keys hq').elim + (fun e x hx => hpath x (e ▸ hx)) (hacc q) + · split + · exact hacc + · exact fun q hq => hacc q (NormLevel.addVar_keys hq) + +theorem normalize_keys (hu : VLevel.ofLevel ls u = some u') : + ∀ p n, (normalize u).get? p = some n → ∀ x ∈ p, x ∈ ls := by + intro p n h + obtain ⟨n₀, h₀, -⟩ := NormLevel.subsumption_covers.1 p n h + have hc : (normalizeAux u [] 0 {}).contains p := + Std.TreeMap.isSome_getElem?_eq_contains.symm.trans + (by simp [Std.TreeMap.get?_eq_getElem?] at h₀; simp [h₀]) + exact normalizeAux_keys hu (by simp) (fun q hq => by simp at hq) p hc + +/-- A single sublevel of the canonical form: `Sub.const p k` is `C(p, k)` and +`Sub.var p x k` is `V(p, x, k)`. -/ +inductive Sub where + | const (p : List Name) (k : Nat) + | var (p : List Name) (x : Name) (k : Nat) + +def Sub.path : Sub → List Name + | .const p _ => p + | .var p _ _ => p + +/-- Domination of sublevels, following Theorem 39: `s.le t` when `t`'s value bounds `s`'s +value under every valuation. The dominator's condition set is a *subset*, so that it fires +whenever the dominated sublevel does; a constant is dominated by `V(F, x, K)` up to `K + 1` +since that sublevel is at least `K + 1` whenever its conditions hold; and a variable +sublevel is only dominated by the same variable at a larger offset. -/ +protected def Sub.le : Sub → Sub → Prop + | .const p k, .const q l => (∀ z ∈ q, z ∈ p) ∧ k ≤ l + | .const p k, .var q _ l => (∀ z ∈ q, z ∈ p) ∧ k ≤ l + 1 + | .var _ _ _, .const _ _ => False + | .var p x k, .var q y l => (∀ z ∈ q, z ∈ p) ∧ x = y ∧ k ≤ l + +protected theorem Sub.le.trans : ∀ {a b c : Sub}, a.le b → b.le c → a.le c + | .const _ _, .const _ _, .const _ _, ⟨s₁, h₁⟩, ⟨s₂, h₂⟩ => + ⟨fun z hz => s₁ _ (s₂ _ hz), Nat.le_trans h₁ h₂⟩ + | .const _ _, .const _ _, .var _ _ _, ⟨s₁, h₁⟩, ⟨s₂, h₂⟩ => + ⟨fun z hz => s₁ _ (s₂ _ hz), by omega⟩ + | .const _ _, .var _ _ _, .const _ _, _, h₂ => h₂.elim + | .const _ _, .var _ _ _, .var _ _ _, ⟨s₁, h₁⟩, ⟨s₂, _, h₂⟩ => + ⟨fun z hz => s₁ _ (s₂ _ hz), by omega⟩ + | .var _ _ _, .const _ _, _, h₁, _ => h₁.elim + | .var _ _ _, .var _ _ _, .const _ _, _, h₂ => h₂.elim + | .var _ _ _, .var _ _ _, .var _ _ _, ⟨s₁, e₁, h₁⟩, ⟨s₂, e₂, h₂⟩ => + ⟨fun z hz => s₁ _ (s₂ _ hz), e₁.trans e₂, Nat.le_trans h₁ h₂⟩ + +theorem subset_antisymm (h₁ : Sorted l₁) (h₂ : Sorted l₂) + (h : ∀ z ∈ l₁, z ∈ l₂) (h' : ∀ z ∈ l₂, z ∈ l₁) : l₁ = l₂ := + subset_eq (subset_of_sorted h₁ h₂ h) <| + Nat.le_antisymm (subset_length (subset_of_sorted h₁ h₂ h)) + (subset_length (subset_of_sorted h₂ h₁ h')) + +protected theorem Sub.le.antisymm : ∀ {a b : Sub}, Sorted a.path → Sorted b.path → + a.le b → b.le a → a = b + | .const p _, .const q _, ha, hb, ⟨s₁, h₁⟩, ⟨s₂, h₂⟩ => by + rw [subset_antisymm (l₁ := p) (l₂ := q) ha hb s₂ s₁, Nat.le_antisymm h₁ h₂] + | .const _ _, .var _ _ _, _, _, _, h₂ => h₂.elim + | .var _ _ _, .const _ _, _, _, h₁, _ => h₁.elim + | .var p _ _, .var q _ _, ha, hb, ⟨s₁, e₁, h₁⟩, ⟨s₂, e₂, h₂⟩ => by + rw [subset_antisymm (l₁ := p) (l₂ := q) ha hb s₂ s₁, e₁, Nat.le_antisymm h₁ h₂] + +/-- The sublevels recorded in a `NormLevel`: `C(p, n.const)` for nonzero constants and +`V(p, x, k)` for each recorded variable. -/ +def NormLevel.HasSub (s : NormLevel) : Sub → Prop + | .const p k => ∃ n, s.get? p = some n ∧ n.const = k ∧ k ≠ 0 + | .var p x k => ∃ n, s.get? p = some n ∧ ⟨x, k⟩ ∈ n.var + +variable (ls : List Name) (ρ : List Nat) in +def Sub.eval : Sub → Nat + | .const p k => evalPath ls ρ p k + | .var p x k => evalPath ls ρ p (evalParam ls ρ x + k) + +theorem NormLevel.HasSub.le_eval {s : NormLevel} : ∀ {t}, s.HasSub t → + t.eval ls ρ ≤ s.eval ls ρ + | .const _ _, ⟨_, hn, hk, _⟩ => + Nat.le_trans (evalPath_mono (hk ▸ Node.const_le_eval)) + (NormLevel.eval_le.1 (Nat.le_refl _) _ _ hn) + | .var _ _ _, ⟨_, hn, hx⟩ => + Nat.le_trans (evalPath_mono (Node.var_le_eval hx)) + (NormLevel.eval_le.1 (Nat.le_refl _) _ _ hn) + +theorem NormLevel.lt_eval {s : NormLevel} : + m < eval ls ρ s ↔ ∃ p n, s.get? p = some n ∧ m < evalPath ls ρ p (Node.eval ls ρ n) := by + refine ⟨fun h => ?_, fun ⟨p, n, hn, hlt⟩ => + Nat.lt_of_lt_of_le hlt (NormLevel.eval_le.1 (Nat.le_refl _) _ _ hn)⟩ + refine Classical.byContradiction fun hc => ?_ + exact absurd (eval_le.2 fun p n hn => Nat.not_lt.1 fun hlt => hc ⟨p, n, hn, hlt⟩) + (Nat.not_le.2 h) + +theorem Node.lt_eval {n : Node} : + m < Node.eval ls ρ n ↔ m < n.const ∨ ∃ v ∈ n.var, m < VarNode.eval ls ρ v := by + refine ⟨fun h => ?_, fun h => ?_⟩ + · refine Classical.byContradiction fun hc => ?_ + rw [not_or] at hc; obtain ⟨h₁, h₂⟩ := hc + refine absurd (Node.eval_le.2 ⟨Nat.not_lt.1 h₁, fun v hv => Nat.not_lt.1 fun hlt => ?_⟩) + (Nat.not_le.2 h) + exact h₂ ⟨v, hv, hlt⟩ + · obtain h | ⟨v, hv, h⟩ := h + · exact Nat.lt_of_lt_of_le h Node.const_le_eval + · exact Nat.lt_of_lt_of_le h (Node.var_le_eval hv) + +theorem lt_evalPath (h : m < evalPath ls ρ p n) : allNZ ls ρ p ∧ m < n := by + rw [evalPath] at h; split at h + · exact ⟨‹_›, h⟩ + · exact absurd h (Nat.not_lt_zero m) + +theorem evalParam_map {f : Name → Nat} (hx : x ∈ ls) : evalParam ls (ls.map f) x = f x := by + have hv : ls.idxOf x < ls.length := List.idxOf_lt_length_iff.2 hx + rw [evalParam_eq hv, List.getElem?_map, List.getElem?_eq_getElem hv] + simp [List.getElem_idxOf] + +theorem evalParam_not_mem (hx : x ∉ ls) : evalParam ls ρ x = 0 := by + simp only [evalParam] + rw [if_neg fun h => hx (List.idxOf_lt_length_iff.1 h)] + +theorem evalParam_map_pos {f : Name → Nat} (h : 0 < evalParam ls (ls.map f) z) : + z ∈ ls ∧ 0 < f z := by + by_cases hz : z ∈ ls + · refine ⟨hz, ?_⟩; rwa [evalParam_map hz] at h + · rw [evalParam_not_mem hz] at h; exact absurd h (Nat.lt_irrefl 0) + +theorem evalParam_map_le {f : Name → Nat} (hb : f z ≤ c) : + evalParam ls (ls.map f) z ≤ c := by + by_cases hz : z ∈ ls + · rw [evalParam_map hz]; exact hb + · rw [evalParam_not_mem hz]; exact Nat.zero_le _ + +theorem foldl_max_le {f : α → Nat} {m : Nat} : ∀ {l : List α} {i : Nat}, + l.foldl (fun r a => max' r (f a)) i ≤ m ↔ i ≤ m ∧ ∀ a ∈ l, f a ≤ m + | [], _ => by simp + | a :: l, i => by simp [foldl_max_le (l := l), Nat.max_le, and_assoc] + +/-- A bound on all the constants and offsets appearing in the map. -/ +def Node.bound (n : Node) : Nat := n.var.foldl (fun r v => max' r v.offset) n.const + +def NormLevel.bound (s : NormLevel) : Nat := s.foldl (fun r _ n => max' r n.bound) 0 + +theorem NormLevel.bound_spec {s : NormLevel} (h : s.get? p = some n) : + n.const ≤ s.bound ∧ ∀ v ∈ n.var, v.offset ≤ s.bound := by + have hmem := Std.TreeMap.mem_toList_iff_getElem?_eq_some.2 (Std.TreeMap.get?_eq_getElem? .. ▸ h) + have hb : n.bound ≤ s.bound := by + rw [bound, Std.TreeMap.foldl_eq_foldl_toList] + exact ((foldl_max_le (f := fun pn : List Name × Node => pn.2.bound)).1 + (Nat.le_refl _)).2 _ hmem + exact (foldl_max_le (f := fun v : VarNode => v.offset)).1 hb + +/-- The separation theorem, a converse to Theorem 39: if the value of `l₁` is bounded by the +value of `l₂` under every valuation, then every sublevel of `l₁` has a syntactic dominator +among the sublevels of `l₂`. The valuation exhibiting the dominator sets every variable of +the sublevel's condition set to `1`, the sublevel's own variable (if any) to a value `N` +larger than every constant and offset of `l₂`, and everything else to `0`: only entries of +`l₂` at condition sets below the sublevel's can contribute, and only a sublevel with the +same variable can reach `N`. -/ +theorem NormLevel.separation {l₁ l₂ : NormLevel} + (hls : ∀ p n, l₁.get? p = some n → ∀ x ∈ p, x ∈ ls) + (wf₁ : ∀ p n, l₁.get? p = some n → ∀ v ∈ n.var, v.var ∈ p) + (h : ∀ ρ, l₁.eval ls ρ ≤ l₂.eval ls ρ) : + ∀ t, l₁.HasSub t → ∃ t', l₂.HasSub t' ∧ t.le t' := by + intro t ht + match t, ht with + | .const p k, ⟨n, hn, hk, hk0⟩ => + have hnz : allNZ ls (ls.map fun z => if z ∈ p then 1 else 0) p := by + simp only [allNZ, List.all_eq_true, decide_eq_true_eq] + intro z hz + rw [evalParam_map (hls _ _ hn _ hz)]; simp [hz] + have h₁ : k ≤ l₁.eval ls (ls.map fun z => if z ∈ p then 1 else 0) := + Nat.le_trans (by simp [Sub.eval, evalPath, hnz]) + (HasSub.le_eval (t := .const p k) ⟨n, hn, hk, hk0⟩) + have h₂ := NormLevel.lt_eval.1 (Nat.lt_of_lt_of_le (by omega : k - 1 < k) + (Nat.le_trans h₁ (h _))) + obtain ⟨q, m, hq, hlt⟩ := h₂ + obtain ⟨hnzq, hlt⟩ := lt_evalPath hlt + have hsub : ∀ z ∈ q, z ∈ p := by + intro z hz + simp only [allNZ, List.all_eq_true, decide_eq_true_eq] at hnzq + have := (evalParam_map_pos (hnzq z hz)).2 + split at this + · assumption + · exact absurd this (Nat.lt_irrefl 0) + obtain hc | ⟨v, hv, hvlt⟩ := Node.lt_eval.1 hlt + · exact ⟨.const q m.const, ⟨m, hq, rfl, by omega⟩, hsub, by omega⟩ + · refine ⟨.var q v.var v.offset, ⟨m, hq, hv⟩, hsub, ?_⟩ + have hev : evalParam ls (ls.map fun z => if z ∈ p then 1 else 0) v.var ≤ 1 := + evalParam_map_le (by split <;> omega) + simp only [VarNode.eval] at hvlt + omega + | .var p x k, ⟨n, hn, hx⟩ => + have hxp : x ∈ p := wf₁ _ _ hn _ hx + have hxls : x ∈ ls := hls _ _ hn _ hxp + have hnz : allNZ ls (ls.map fun z => + if z = x then l₂.bound + k + 2 else if z ∈ p then 1 else 0) p := by + simp only [allNZ, List.all_eq_true, decide_eq_true_eq] + intro z hz + simp only [evalParam_map (hls _ _ hn _ hz)] + split + · omega + · omega + have h₁ : l₂.bound + k + 2 + k ≤ l₁.eval ls (ls.map fun z => + if z = x then l₂.bound + k + 2 else if z ∈ p then 1 else 0) := by + refine Nat.le_trans ?_ (HasSub.le_eval (t := .var p x k) ⟨n, hn, hx⟩) + simp [Sub.eval, evalPath, hnz, evalParam_map hxls] + obtain ⟨q, m, hq, hlt⟩ := NormLevel.lt_eval.1 + (Nat.lt_of_lt_of_le (by omega : l₂.bound + k + 2 + k - 1 < l₂.bound + k + 2 + k) + (Nat.le_trans h₁ (h _))) + obtain ⟨hnzq, hlt⟩ := lt_evalPath hlt + have hsub : ∀ z ∈ q, z ∈ p := by + intro z hz + simp only [allNZ, List.all_eq_true, decide_eq_true_eq] at hnzq + have := (evalParam_map_pos (hnzq z hz)).2 + split at this + · rename_i hz'; subst hz'; exact hxp + · split at this + · assumption + · exact absurd this (Nat.lt_irrefl 0) + obtain hc | ⟨v, hv, hvlt⟩ := Node.lt_eval.1 hlt + · exact absurd hc (by have := (bound_spec hq).1; omega) + · by_cases hvx : v.var = x + · refine ⟨.var q x v.offset, ⟨m, hq, by rw [← hvx]; exact hv⟩, hsub, rfl, ?_⟩ + simp [VarNode.eval, hvx, evalParam_map hxls] at hvlt + omega + · have hoff := (bound_spec hq).2 _ hv + have hev : evalParam ls (ls.map fun z => + if z = x then l₂.bound + k + 2 else if z ∈ p then 1 else 0) v.var ≤ 1 := + evalParam_map_le (by rw [if_neg hvx]; split <;> omega) + simp only [VarNode.eval] at hvlt + exact absurd hvlt (by omega) + +private theorem name_lt_ne {a b : Name} (h : compare a b = .lt) : a ≠ b := by + rintro rfl; rw [Std.ReflOrd.compare_self] at h; cases h + +/-- Exactness of `subsumeVars` on sorted lists: a surviving variable has no dominator +in the subtracted list. -/ +theorem subsumeVars_complete {x y : VarNode} : ∀ {vs₁ vs₂ : List VarNode}, + VarsSorted vs₁ → VarsSorted vs₂ → x ∈ subsumeVars vs₁ vs₂ → y ∈ vs₂ → + y.var = x.var → x.offset ≤ y.offset → False + | [], _, _, _, hx, _ => by simp [subsumeVars] at hx + | _ :: _, [], _, _, _, hy => nomatch hy + | a :: vs₁, b :: vs₂, h₁, h₂, hx, hy => by + intro e hle + simp only [subsumeVars] at hx + split at hx <;> rename_i hab + · rcases List.mem_cons.1 hx with rfl | hx' + · rcases List.mem_cons.1 hy with rfl | hy' + · exact name_lt_ne hab e.symm + · exact name_lt_ne (Std.TransCmp.lt_trans hab (h₂.head _ hy')) e.symm + · exact subsumeVars_complete h₁.of_cons h₂ hx' hy e hle + · have eab : a.var = b.var := eq_of_beq (Std.LawfulBEqCmp.compare_eq_iff_beq.1 hab) + split at hx <;> rename_i hoff + · rcases List.mem_cons.1 hy with rfl | hy' + · exact name_lt_ne (h₁.head _ (subsumeVars_subset hx)) (eab.trans e) + · exact subsumeVars_complete h₁.of_cons h₂.of_cons hx hy' e hle + · rcases List.mem_cons.1 hx with rfl | hx' + · rcases List.mem_cons.1 hy with rfl | hy' + · exact hoff hle + · exact name_lt_ne (h₂.head _ hy') (e.trans eab).symm + · rcases List.mem_cons.1 hy with rfl | hy' + · exact name_lt_ne (h₁.head _ (subsumeVars_subset hx')) (eab.trans e) + · exact subsumeVars_complete h₁.of_cons h₂.of_cons hx' hy' e hle + · rcases List.mem_cons.1 hy with rfl | hy' + · have hbx : compare y.var x.var = .lt := by + have hba := Std.OrientedCmp.lt_of_gt hab + rcases List.mem_cons.1 (subsumeVars_subset hx) with rfl | hxv + · exact hba + · exact Std.TransCmp.lt_trans hba (h₁.head _ hxv) + exact name_lt_ne hbx e + · exact subsumeVars_complete h₁ h₂.of_cons hx hy' e hle + +theorem le_foldl_max_self {vs : List VarNode} : ∀ {n : Nat}, n ≤ vs.foldl (·.max ·.offset) n := by + induction vs with | nil => exact Nat.le_refl _ | cons a vs ih + exact fun {n} => Nat.le_trans (Nat.le_max_left _ _) ih + +theorem foldl_max_ge {vs : List VarNode} (hy : y ∈ vs) : + ∀ {n : Nat}, y.offset ≤ vs.foldl (·.max ·.offset) n := by + induction vs with | nil => cases hy | cons a vs ih + rcases List.mem_cons.1 hy with rfl | hy' + · exact fun {n} => Nat.le_trans (Nat.le_max_right _ _) le_foldl_max_self + · exact fun {n} => ih hy' + +/-- Exactness of the constant part of `subsumeBy`: a dominated constant is dropped. -/ +theorem Node.subsumeBy_const_complete {same : Bool} {n₁ n₂ : Node} + (h : (same = false ∧ n₁.const ≤ n₂.const) ∨ ∃ y ∈ n₂.var, n₁.const ≤ y.offset + 1) : + (n₁.subsumeBy same n₂).const = 0 := by + rw [Node.subsumeBy_const_eq] + split <;> [rename_i hc; rfl] + simp only [Bool.or_eq_true, Bool.and_eq_true, decide_eq_true_eq, List.isEmpty_iff] at hc + obtain hc | ⟨hc1, hc2⟩ := hc + · exact hc + obtain ⟨rfl, hle⟩ | ⟨y, hy, hle⟩ := h + · rcases hc1 with hc1 | hc1 + · cases hc1 + · omega + · rcases hc2 with hc2 | hc2 + · rw [hc2] at hy; cases hy + · have := foldl_max_ge hy (n := 0); omega + +theorem Node.subsumeBy_var_sublist {same : Bool} {n₁ n₂ : Node} : + List.Sublist (n₁.subsumeBy same n₂).var n₁.var := by + rw [Node.subsumeBy_var_eq]; split + · exact List.Sublist.refl _ + · exact subsumeVars_sublist .. + +/-- Exactness of the variable part of `subsumeBy` at a different key: a dominated variable +is dropped. -/ +theorem Node.subsumeBy_var_complete {n₁ n₂ : Node} (h₁ : VarsSorted n₁.var) + (h₂ : VarsSorted n₂.var) (hx : x ∈ (n₁.subsumeBy false n₂).var) (hy : y ∈ n₂.var) + (e : y.var = x.var) (hle : x.offset ≤ y.offset) : False := by + rw [Node.subsumeBy_var_eq] at hx + split at hx <;> rename_i hc + · simp only [Bool.false_or, List.isEmpty_iff] at hc + rw [hc] at hy; cases hy + · exact subsumeVars_complete h₁ h₂ hx hy e hle + +/-- Completeness of the discharging fold in `NormLevel.le`: if every sublevel of `n₁` has a +dominator among the entries of `l` at a subkey of `p₁`, the fold discharges everything and +returns `none`. -/ +theorem NormLevel.le_fold_complete {p₁ : List Name} : + ∀ (l : List (List Name × Node)) (n₁ : Node), VarsSorted n₁.var → + (∀ pn ∈ l, VarsSorted pn.2.var) → n₁.isEmpty = false → + (n₁.const ≠ 0 → ∃ pn ∈ l, subset compare pn.1 p₁ ∧ + (n₁.const ≤ pn.2.const ∨ ∃ y ∈ pn.2.var, n₁.const ≤ y.offset + 1)) → + (∀ x ∈ n₁.var, ∃ pn ∈ l, subset compare pn.1 p₁ ∧ + ∃ y ∈ pn.2.var, y.var = x.var ∧ x.offset ≤ y.offset) → + List.foldlM (m := Option) (fun n pn => + if subset compare pn.1 p₁ then + if (n.subsumeBy false pn.2).isEmpty then none else some (n.subsumeBy false pn.2) + else some n) n₁ l = none + | [], n₁, _, _, hne, hconst, hvar => by + rw [Node.isEmpty, Bool.and_eq_false_iff] at hne + obtain h0 | hv := hne + · obtain ⟨_, h, -⟩ := hconst (by simpa using h0) + cases h + · obtain ⟨x, hx⟩ := List.exists_mem_of_ne_nil _ (by simpa using hv) + obtain ⟨_, h, -⟩ := hvar x hx + cases h + | pn :: l, n₁, hvs₁, hvsl, hne, hconst, hvar => by + simp only [List.foldlM_cons] + split <;> rename_i hs + · by_cases he : (n₁.subsumeBy false pn.2).isEmpty <;> simp [he] + refine le_fold_complete l _ (hvs₁.sublist Node.subsumeBy_var_sublist) + (fun pn h => hvsl _ (.tail _ h)) (by simpa using he) ?_ ?_ + · intro h0 + have hc : (n₁.subsumeBy false pn.2).const = n₁.const := + (Node.subsumeBy_const_cases ..).resolve_right h0 + obtain ⟨pn', hpn', hsub', hdom'⟩ := hconst (hc ▸ h0) + rcases List.mem_cons.1 hpn' with rfl | hpn' + · exact absurd (Node.subsumeBy_const_complete + (hdom'.imp (fun h => ⟨rfl, h⟩) id)) h0 + · exact ⟨pn', hpn', hsub', hc ▸ hdom'⟩ + · intro x hx + obtain ⟨pn', hpn', hsub', y, hy, e, hle⟩ := hvar x (Node.subsumeBy_var_subset hx) + rcases List.mem_cons.1 hpn' with rfl | hpn' + · exact (Node.subsumeBy_var_complete hvs₁ (hvsl _ (.head _)) hx hy e hle).elim + · exact ⟨pn', hpn', hsub', y, hy, e, hle⟩ + · refine le_fold_complete l n₁ hvs₁ (fun pn h => hvsl _ (.tail _ h)) hne + (fun h0 => ?_) (fun x hx => ?_) + · obtain ⟨pn', hpn', hsub', hdom'⟩ := hconst h0 + rcases List.mem_cons.1 hpn' with rfl | hpn' + · exact absurd hsub' hs + · exact ⟨pn', hpn', hsub', hdom'⟩ + · obtain ⟨pn', hpn', hsub', hy⟩ := hvar x hx + rcases List.mem_cons.1 hpn' with rfl | hpn' + · exact absurd hsub' hs + · exact ⟨pn', hpn', hsub', hy⟩ + +/-- Completeness of `NormLevel.le`: per-sublevel domination implies acceptance. -/ +theorem NormLevel.le_complete {l₁ l₂ : NormLevel} + (hvs₁ : l₁.SortedVars) (hvs₂ : l₂.SortedVars) + (hne : ∀ p n, l₁.get? p = some n → n.isEmpty = false) + (hsort₁ : ∀ p n, l₁.get? p = some n → Sorted p) + (hsort₂ : ∀ p n, l₂.get? p = some n → Sorted p) + (hdom : ∀ t, l₁.HasSub t → ∃ t', l₂.HasSub t' ∧ t.le t') : + l₁.le l₂ := by + rw [NormLevel.le, Std.TreeMap.all_eq_all_toList, List.all_eq_true] + rintro ⟨p₁, n₁⟩ hmem + have h₁ := Std.TreeMap.get?_eq_getElem? .. ▸ Std.TreeMap.mem_toList_iff_getElem?_eq_some.1 hmem + simp only [Std.TreeMap.foldlM_eq_foldlM_toList, Option.isNone_iff_eq_none] + have hmem₂ : ∀ q m, l₂.get? q = some m → (q, m) ∈ l₂.toList := fun q m h => + Std.TreeMap.mem_toList_iff_getElem?_eq_some.2 (Std.TreeMap.get?_eq_getElem? .. ▸ h) + refine le_fold_complete l₂.toList n₁ (hvs₁ _ _ h₁) + (fun pn h => hvs₂ _ _ <| Std.TreeMap.get?_eq_getElem? .. ▸ + Std.TreeMap.mem_toList_iff_getElem?_eq_some.1 h) (hne _ _ h₁) (fun h0 => ?_) (fun x hx => ?_) + · obtain ⟨t', ht', hle⟩ := hdom (.const p₁ n₁.const) ⟨n₁, h₁, rfl, h0⟩ + match t', ht', hle with + | .const q _, ⟨m, hq, hc, _⟩, ⟨hsub, hle⟩ => + exact ⟨(q, m), hmem₂ _ _ hq, + subset_of_sorted (hsort₂ _ _ hq) (hsort₁ _ _ h₁) hsub, .inl (hc ▸ hle)⟩ + | .var q yv yk, ⟨m, hq, hyk⟩, ⟨hsub, hle⟩ => + exact ⟨(q, m), hmem₂ _ _ hq, + subset_of_sorted (hsort₂ _ _ hq) (hsort₁ _ _ h₁) hsub, .inr ⟨⟨yv, yk⟩, hyk, hle⟩⟩ + · obtain ⟨t', ht', hle⟩ := hdom (.var p₁ x.var x.offset) ⟨n₁, h₁, hx⟩ + match t', ht', hle with + | .const _ _, _, hle => exact hle.elim + | .var q yv yk, ⟨m, hq, hyk⟩, ⟨hsub, hev, hle⟩ => + exact ⟨(q, m), hmem₂ _ _ hq, + subset_of_sorted (hsort₂ _ _ hq) (hsort₁ _ _ h₁) hsub, ⟨yv, yk⟩, hyk, hev.symm, hle⟩ + +/-- Two key-sorted entry lists with the same entries are equal. -/ +theorem sorted_pairs_eq : ∀ {l₁ l₂ : List (List Name × Node)}, + l₁.Pairwise (compare ·.1 ·.1 = .lt) → l₂.Pairwise (compare ·.1 ·.1 = .lt) → + (∀ x, x ∈ l₁ ↔ x ∈ l₂) → l₁ = l₂ + | [], [], _, _, _ => rfl + | [], _ :: _, _, _, h => nomatch (h _).2 (.head _) + | _ :: _, [], _, _, h => nomatch (h _).1 (.head _) + | a :: l₁, b :: l₂, h₁, h₂, h => by + have head₁ := (List.pairwise_cons.1 h₁).1 + have head₂ := (List.pairwise_cons.1 h₂).1 + cases show a = b by + rcases List.mem_cons.1 ((h a).1 (.head _)) with rfl | ha <;> [rfl; skip] + rcases List.mem_cons.1 ((h b).2 (.head _)) with rfl | hb <;> [rfl; skip] + cases Std.OrientedCmp.not_lt_of_lt (head₁ _ hb) (head₂ _ ha) + refine congrArg (a :: ·) (sorted_pairs_eq (List.pairwise_cons.1 h₁).2 + (List.pairwise_cons.1 h₂).2 fun x => ⟨fun hx => ?_, fun hx => ?_⟩) + · rcases List.mem_cons.1 ((h x).1 (.tail _ hx)) with rfl | hx' + · have := head₁ _ hx; rw [Std.ReflOrd.compare_self] at this; cases this + · exact hx' + · rcases List.mem_cons.1 ((h x).2 (.tail _ hx)) with rfl | hx' + · have := head₂ _ hx; rw [Std.ReflOrd.compare_self] at this; cases this + · exact hx' + +/-! ### The flat fast path + +For a level with no essential `imax`, `normalize` produces a map with the constant at the +root and one single-variable node per parameter (`NormLevel.Flat`), and `toTree` reads that +off directly, so building the `NormLevel` can be skipped entirely. -/ + +/-- The map `normalize` produces for a flat level, pointwise: `C(∅, c)` at the root, and +`V({x}, x, k)` at each singleton key whose parameter is recorded in `vs`. -/ +def flatGet (c : Nat) (vs : List VarNode) : List Name → Option Node + | [] => if c = 0 then none else some ⟨c, []⟩ + | [x] => (vs.find? (·.var == x)).map fun v => ⟨0, [v]⟩ + | _ => none + +def NormLevel.Flat (s : NormLevel) (c : Nat) (vs : List VarNode) : Prop := + ∀ p, s.get? p = flatGet c vs p + +theorem find?_var_eq_some {vs : List VarNode} {x : Name} {v : VarNode} (hvs : VarsSorted vs) : + vs.find? (·.var == x) = some v ↔ v ∈ vs ∧ v.var = x := by + refine ⟨fun h => ⟨List.mem_of_find?_eq_some h, by simpa using List.find?_some h⟩, ?_⟩ + rintro ⟨hv, rfl⟩ + match h : vs.find? (·.var == v.var) with + | none => simp [List.find?_eq_none] at h; exact absurd rfl (h _ hv) + | some w => + have hw := List.mem_of_find?_eq_some h + have hwe : w.var = v.var := by simpa using List.find?_some h + rw [h, hvs.eq_of_var_eq hw hv hwe] + +theorem find?_var_eq_none {vs : List VarNode} {x : Name} : + vs.find? (·.var == x) = none ↔ ∀ v ∈ vs, v.var ≠ x := by + simp [List.find?_eq_none] + +/-- `addVar` raises the offset of the entry for `x`, leaving the rest alone. -/ +theorem VarNode.find?_addVar {vs : List VarNode} {x y : Name} {k : Nat} (hvs : VarsSorted vs) : + (VarNode.addVar x k vs).find? (·.var == y) = + if x = y then some ⟨x, ((vs.find? (·.var == x)).map (·.offset.max k)).getD k⟩ + else vs.find? (·.var == y) := by + induction vs with | nil => split <;> simp [VarNode.addVar, *] | cons w l ih + simp only [VarNode.addVar] + split <;> rename_i hc + · have hnone : (w :: l).find? (·.var == x) = none := by + refine find?_var_eq_none.2 fun v hv => ?_ + obtain rfl | hv := List.mem_cons.1 hv + · exact fun e => name_lt_ne hc e.symm + · exact fun e => name_lt_ne (Std.TransCmp.lt_trans hc (hvs.head _ hv)) e.symm + split <;> rename_i h + · subst h; rw [List.find?_cons_of_pos (by simp), hnone]; rfl + · rw [List.find?_cons_of_neg (by simp [h])] + · have e := eq_of_beq (Std.LawfulBEqCmp.compare_eq_iff_beq.1 hc) + split <;> rename_i h + · subst h; rw [List.find?_cons_of_pos (by simp), List.find?_cons_of_pos (by simp [← e])]; rfl + · rw [List.find?_cons_of_neg (by simp [h]), List.find?_cons_of_neg (by simp [← e, h])] + · have hne := name_lt_ne (Std.OrientedCmp.lt_of_gt hc) + by_cases hy : w.var = y + · rw [if_neg (hy ▸ hne.symm), List.find?_cons_of_pos (by simp [hy]), + List.find?_cons_of_pos (by simp [hy])] + · rw [List.find?_cons_of_neg (by simp [hy]), ih hvs.of_cons, + List.find?_cons_of_neg (by simp [hne]), List.find?_cons_of_neg (by simp [hy])] + +theorem NormLevel.addConst_flat {s : NormLevel} {c k : Nat} {vs : List VarNode} + (h : s.Flat c vs) : (addConst k [] s).Flat (Nat.max c k) vs := by + by_cases hk : k = 0 + · subst hk + rw [show Nat.max c 0 = c from Nat.max_zero c, NormLevel.addConst, if_pos (by simp)] + exact h + · rw [NormLevel.addConst, if_neg (by simp [hk])] + intro p + rw [Std.TreeMap.get?_eq_getElem?, Std.TreeMap.getElem?_alter] + have hmax : ¬Nat.max c k = 0 := by simp only [Nat.max_eq_zero_iff]; simp [hk] + split <;> rename_i he + · cases eq_of_beq (Std.LawfulBEqCmp.compare_eq_iff_beq.1 he) + rw [← Std.TreeMap.get?_eq_getElem?, h []] + simp only [flatGet] + by_cases hc0 : c = 0 + · subst hc0 + rw [if_pos rfl, if_neg hmax, show Nat.max 0 k = k from Nat.zero_max k] + · rw [if_neg hc0, if_neg hmax, show Nat.max c k = Nat.max k c from Nat.max_comm c k] + · rw [← Std.TreeMap.get?_eq_getElem?, h p] + match p with + | [] => cases he Std.ReflOrd.compare_self + | [_] | _ :: _ :: _ => rfl + +theorem NormLevel.addNode_flat {s : NormLevel} {c k : Nat} {vs : List VarNode} {x : Name} + (hvs : VarsSorted vs) (h : s.Flat c vs) : + (addNode x k [x] s).Flat c (VarNode.addVar x k vs) := by + intro p + rw [NormLevel.addNode, Std.TreeMap.get?_eq_getElem?, Std.TreeMap.getElem?_alter] + split <;> rename_i he + · have hp : [x] = p := eq_of_beq (Std.LawfulBEqCmp.compare_eq_iff_beq.1 he) + subst hp + rw [← Std.TreeMap.get?_eq_getElem?, h [x]] + simp only [flatGet] + rw [VarNode.find?_addVar hvs, if_pos rfl] + match hfd : vs.find? (·.var == x) with + | none => rw [hfd]; rfl + | some v => + have hv : v.var = x := by simpa using List.find?_some hfd + rw [hfd] + show some ({ const := 0, var := VarNode.addVar x k [v] } : Node) = _ + rw [show VarNode.addVar x k [v] = [⟨x, v.offset.max k⟩] from by + simp only [VarNode.addVar, hv, Std.ReflCmp.compare_self]]; rfl + · rw [← Std.TreeMap.get?_eq_getElem?, h p] + match p with + | [] => rfl + | [y] => + have hxy : x ≠ y := by rintro rfl; exact he Std.ReflOrd.compare_self + simp only [flatGet] + rw [VarNode.find?_addVar hvs, if_neg hxy] + | _ :: _ :: _ => rfl + +/-- The entries of a flat map: the root carries the constant (and is absent when it is zero), +and every other key is a singleton carrying one variable of `vs`. -/ +private theorem flatGet_eq_some {c : Nat} {vs : List VarNode} {p : List Name} {n : Node} + (h : flatGet c vs p = some n) : + (p = [] ∧ n = ⟨c, []⟩ ∧ c ≠ 0) ∨ ∃ v ∈ vs, p = [v.var] ∧ n = ⟨0, [v]⟩ := by + match p with + | [] => + simp only [flatGet] at h + split at h + · cases h + · exact .inl ⟨rfl, by cases h; rfl, by assumption⟩ + | [x] => + simp only [flatGet, Option.map_eq_some_iff] at h + obtain ⟨v, hv, rfl⟩ := h + have hvx : v.var = x := by simpa using List.find?_some hv + exact .inr ⟨v, List.mem_of_find?_eq_some hv, by rw [hvx], rfl⟩ + | _ :: _ :: _ => simp [flatGet] at h + +/-- `subsumeBy` is the identity when the constant has nothing to lose (it is zero, or the two +sit at the same condition set and the dominator has no variables) and the variables have +nothing to lose (same condition set, or no variables to be dominated by). -/ +private theorem Node.subsumeBy_id {same : Bool} {n₁ n₂ : Node} + (h₁ : n₁.const = 0 ∨ (same ∧ n₂.var = [])) + (h₂ : same ∨ n₂.var = []) : n₁.subsumeBy same n₂ = n₁ := by + simp only [Node.subsumeBy] + rcases h₁ with hz | ⟨hs, hv⟩ + · rcases h₂ with h2 | h2 <;> simp [hz, h2] + · simp [hs, hv] + +private theorem subsume_flat_id {c : Nat} {vs : List VarNode} {p₁ p₂ : List Name} {n₁ n₂ : Node} + (h₁ : flatGet c vs p₁ = some n₁) (h₂ : flatGet c vs p₂ = some n₂) : + Node.subsume p₁ n₁ p₂ n₂ = n₁ := by + rw [Node.subsume] + split <;> [rename_i hsub; rfl] + obtain ⟨rfl, rfl, -⟩ | ⟨v, -, rfl, rfl⟩ := flatGet_eq_some h₂ + · obtain ⟨rfl, rfl, -⟩ | ⟨w, -, rfl, rfl⟩ := flatGet_eq_some h₁ + · exact Node.subsumeBy_id (.inr ⟨rfl, rfl⟩) (.inl rfl) + · exact Node.subsumeBy_id (.inl rfl) (.inr rfl) + · obtain ⟨rfl, rfl, -⟩ | ⟨w, -, rfl, rfl⟩ := flatGet_eq_some h₁ + · exact absurd hsub (by simp [subset]) + · exact Node.subsumeBy_id (.inl rfl) (.inl rfl) + +/-- Nothing in a flat map subsumes anything: a condition set is empty or a singleton, and the +node carrying the constant has no variables. -/ +theorem NormLevel.subsumption_flat {s : NormLevel} {c : Nat} {vs : List VarNode} + (h : s.Flat c vs) : s.subsumption.Flat c vs := by + have hmin : ∀ (acc : NormLevel), acc.Flat c vs → ∀ p₁ n₁, flatGet c vs p₁ = some n₁ → + acc.minimize p₁ n₁ = n₁ := by + intro acc hacc p₁ n₁ h₁ + rw [NormLevel.minimize, Std.TreeMap.foldl_eq_foldl_toList] + have hmem : ∀ pn ∈ acc.toList, flatGet c vs pn.1 = some pn.2 := fun pn hp => + (hacc pn.1).symm.trans (Std.TreeMap.get?_eq_getElem? .. ▸ + Std.TreeMap.mem_toList_iff_getElem?_eq_some.1 hp) + suffices ∀ (l : List (List Name × Node)), (∀ pn ∈ l, flatGet c vs pn.1 = some pn.2) → + List.foldl (fun n pn => Node.subsume p₁ n pn.1 pn.2) n₁ l = n₁ from this _ hmem + intro l; induction l with | nil => intro; rfl | cons pn l ih + intro hl + rw [List.foldl_cons, subsume_flat_id h₁ (hl pn (.head _))] + exact ih fun q hq => hl q (.tail _ hq) + rw [NormLevel.subsumption, Std.TreeMap.foldl_eq_foldl_toList] + have hmem : ∀ pn ∈ s.toList, flatGet c vs pn.1 = some pn.2 := fun pn hp => + (h pn.1).symm.trans (Std.TreeMap.get?_eq_getElem? .. ▸ + Std.TreeMap.mem_toList_iff_getElem?_eq_some.1 hp) + suffices ∀ (l : List (List Name × Node)) (acc : NormLevel), + (∀ pn ∈ l, flatGet c vs pn.1 = some pn.2) → acc.Flat c vs → + (List.foldl (fun acc pn => + let n := acc.minimize pn.1 pn.2 + if n.isEmpty then acc.erase pn.1 else acc.insert pn.1 n) acc l).Flat c vs from + this _ _ hmem h + intro l; induction l with | nil => exact fun _ _ hacc => hacc | cons pn l ih + obtain ⟨p₁, n₁⟩ := pn + refine fun acc hl hacc => ih _ (fun q hq => hl q (.tail _ hq)) fun p => ?_ + have h₁ : flatGet c vs p₁ = some n₁ := hl _ (.head _) + have hne : n₁.isEmpty = false := by + obtain ⟨-, rfl, hc0⟩ | ⟨v, -, -, rfl⟩ := flatGet_eq_some h₁ + · simp [Node.isEmpty, hc0] + · simp [Node.isEmpty] + rw [NormLevel.subsumption_step_get?, hmin acc hacc p₁ n₁ h₁, hne] + simp only [Bool.false_eq_true, if_false] + split <;> rename_i hp + · subst hp; exact h₁.symm + · exact hacc p + +/-- The inserts of `toNormLevel` fill in the singleton keys one at a time: once the starting +map is right at every key not yet due to be written, the fold is right everywhere. -/ +private theorem toNormLevel_fold {c : Nat} {vs : List VarNode} (hvs : VarsSorted vs) : + ∀ (l : List VarNode) (s : NormLevel), (∀ w ∈ l, w ∈ vs) → + (∀ p, (∀ w ∈ l, p ≠ [w.var]) → s.get? p = flatGet c vs p) → + ∀ p, (l.foldl (fun s v => s.insert [v.var] ⟨0, [v]⟩) s).get? p = flatGet c vs p := by + intro l; induction l with | nil => intro s _ hs p; exact hs p (by simp) | cons w l ih + refine fun s hmem hs => ih _ (fun x hx => hmem x (.tail _ hx)) fun p hp => ?_ + rw [Std.TreeMap.get?_eq_getElem?, Std.TreeMap.getElem?_insert] + split <;> rename_i he + · have hpe : [w.var] = p := eq_of_beq (Std.LawfulBEqCmp.compare_eq_iff_beq.1 he) + subst hpe + simp only [flatGet] + rw [(find?_var_eq_some hvs).2 ⟨hmem w (.head _), rfl⟩]; rfl + · rw [← Std.TreeMap.get?_eq_getElem?] + refine hs p fun x hx => ?_ + obtain rfl | hx := List.mem_cons.1 hx + · rintro rfl; exact he Std.ReflOrd.compare_self + · exact hp x hx + +/-- `toNormLevel` really does build a flat map. -/ +theorem toNormLevel_flat {c : Nat} {vs : List VarNode} (hvs : VarsSorted vs) : + (toNormLevel c vs).Flat c vs := by + refine toNormLevel_fold hvs vs _ (fun w hw => hw) fun p hp => ?_ + have hnone : ∀ x, p = [x] → flatGet c vs [x] = none := by + rintro x rfl; simp only [flatGet] + rw [find?_var_eq_none.2 fun v hv he => hp v hv (by rw [he])]; rfl + split <;> rename_i hc + · subst hc + match p with + | [] => rfl + | [x] => rw [hnone x rfl]; rfl + | _ :: _ :: _ => rfl + · rw [Std.TreeMap.get?_eq_getElem?, Std.TreeMap.getElem?_insert] + split <;> rename_i he + · cases eq_of_beq (Std.LawfulBEqCmp.compare_eq_iff_beq.1 he); simp [flatGet, hc] + · match p with + | [] => cases he Std.ReflOrd.compare_self + | [x] => rw [hnone x rfl]; rfl + | _ :: _ :: _ => rfl + +/-! Two maps with the same entries need not be the same tree, so the fallback -- which seeds +`normalizeAux` with `toNormLevel` rather than with the map the general path would have built +-- needs `normalizeAux` and `subsumption` to respect pointwise equality. -/ + +theorem toList_eq_of_get?_eq {A B : NormLevel} (h : ∀ p, A.get? p = B.get? p) : + A.toList = B.toList := by + refine sorted_pairs_eq Std.TreeMap.ordered_keys_toList Std.TreeMap.ordered_keys_toList ?_ + rintro ⟨p, n⟩ + rw [Std.TreeMap.mem_toList_iff_getElem?_eq_some, Std.TreeMap.mem_toList_iff_getElem?_eq_some, + ← Std.TreeMap.get?_eq_getElem?, ← Std.TreeMap.get?_eq_getElem?, h] + +theorem NormLevel.addConst_congr {A B : NormLevel} (h : ∀ p, A.get? p = B.get? p) (k path p) : + (addConst k path A).get? p = (addConst k path B).get? p := by + simp only [Std.TreeMap.get?_eq_getElem?] at h ⊢ + rw [NormLevel.addConst, NormLevel.addConst] + split <;> [exact h p; skip] + rw [Std.TreeMap.getElem?_alter, Std.TreeMap.getElem?_alter] + split <;> rw [h] + +theorem NormLevel.addNode_congr {A B : NormLevel} (h : ∀ p, A.get? p = B.get? p) (x k path p) : + (addNode x k path A).get? p = (addNode x k path B).get? p := by + simp only [Std.TreeMap.get?_eq_getElem?] at h ⊢ + rw [NormLevel.addNode, NormLevel.addNode, Std.TreeMap.getElem?_alter, Std.TreeMap.getElem?_alter] + split <;> rw [h] + +theorem NormLevel.addVar_congr {A B : NormLevel} (h : ∀ p, A.get? p = B.get? p) (x k path p) : + (addVar x k path A).get? p = (addVar x k path B).get? p := by + simp only [Std.TreeMap.get?_eq_getElem?] at h ⊢ + rw [NormLevel.addVar, NormLevel.addVar, Std.TreeMap.getElem?_modify, Std.TreeMap.getElem?_modify] + split <;> rw [h] + +theorem normalizeAux_congr {A B : NormLevel} (h : ∀ p, A.get? p = B.get? p) (u path k) : + ∀ p, (normalizeAux u path k A).get? p = (normalizeAux u path k B).get? p := by + induction u, path, k, A using normalizeAux.induct generalizing B with + | case1 path k acc => simp only [normalizeAux]; exact NormLevel.addConst_congr h k path + | case2 path k acc a => simp only [normalizeAux]; exact NormLevel.addConst_congr h k path + | case3 path k acc u ih => simp only [normalizeAux]; exact ih h + | case4 path k acc u v ih₁ ih₂ => simp only [normalizeAux]; exact ih₂ (ih₁ h) + | case5 path k acc u v ih₁ ih₂ => simp only [normalizeAux]; exact ih₂ (ih₁ h) + | case6 path k acc u v w ih₁ ih₂ => simp only [normalizeAux]; exact ih₂ (ih₁ h) + | case7 path k acc u v w ih₁ ih₂ => simp only [normalizeAux]; exact ih₂ (ih₁ h) + | case8 path k acc u v path' he ih => + simp only [normalizeAux, he] + exact ih (NormLevel.addNode_congr (NormLevel.addConst_congr h k path) v k path') + | case9 path k acc u v he acc1 => + rename_i ih + simp only [normalizeAux, he] + refine ih (B := if k = 0 then B else NormLevel.addVar v k path B) fun p => ?_ + show (if k = 0 then acc else NormLevel.addVar v k path acc).get? p = + (if k = 0 then B else NormLevel.addVar v k path B).get? p + by_cases hk : k = 0 + · simp only [if_pos hk]; exact h p + · simp only [if_neg hk]; exact NormLevel.addVar_congr h v k path p + | case10 path k acc a => simp only [normalizeAux]; exact h + | case11 path k acc a b => simp only [normalizeAux]; exact h + | case12 path k acc v path' he => + simp only [normalizeAux, he] + exact NormLevel.addNode_congr (NormLevel.addConst_congr h k path) v k path' + | case13 path acc v he => simp only [normalizeAux, he, if_pos]; exact h + | case14 path k acc v he hk => + simp only [normalizeAux, he, if_neg hk] + exact NormLevel.addVar_congr h v k path + +theorem NormLevel.subsumption_congr {A B : NormLevel} (h : ∀ p, A.get? p = B.get? p) : + ∀ p, A.subsumption.get? p = B.subsumption.get? p := by + rw [NormLevel.subsumption, NormLevel.subsumption, Std.TreeMap.foldl_eq_foldl_toList, + Std.TreeMap.foldl_eq_foldl_toList, toList_eq_of_get?_eq h] + let +generalize F acc pn := _ + suffices ∀ (l : List (List Name × Node)) (acc₁ acc₂ : NormLevel), + (∀ p, acc₁.get? p = acc₂.get? p) → ∀ p, + (List.foldl F acc₁ l).get? p = (List.foldl F acc₂ l).get? p from + this _ _ _ h + intro l; induction l with | nil => exact fun _ _ h => h | cons pn l ih + refine fun acc₁ acc₂ hacc => ih _ _ fun p => ?_ + have hmin : acc₁.minimize pn.1 pn.2 = acc₂.minimize pn.1 pn.2 := by + rw [NormLevel.minimize, NormLevel.minimize, Std.TreeMap.foldl_eq_foldl_toList, + Std.TreeMap.foldl_eq_foldl_toList, toList_eq_of_get?_eq hacc] + rw [NormLevel.subsumption_step_get?, NormLevel.subsumption_step_get?, hmin] + split <;> [rfl; exact hacc p] + +/-- What a traversal result stands for: collected data still describing the map, or a thrown +map agreeing with it entry for entry (only entry for entry, since the thrown one was rebuilt +from sorted data rather than in traversal order). -/ +def RepAcc : Except NormLevel (Nat × List VarNode) → NormLevel → Prop + | .ok (c, vs), s => VarsSorted vs ∧ s.Flat c vs + | .error s', s => ∀ p, s'.get? p = s.get? p + +/-- The traversal tracks `normalizeAux` step for step. -/ +theorem flatAux_rep (hvs : VarsSorted vs) (h : s.Flat c vs) : + RepAcc (flatAux l k (c, vs)) (normalizeAux l [] k s) := by + induction l generalizing k c vs s with simp only [flatAux, normalizeAux] + | zero => exact ⟨hvs, NormLevel.addConst_flat h⟩ + | succ l ih => exact ih hvs h + | max a b iha ihb => + have ha := iha (k := k) hvs h + cases hfa : flatAux a k (c, vs) with + | ok acc => obtain ⟨c₁, vs₁⟩ := acc; rw [hfa] at ha; exact ihb ha.1 ha.2 + | error s₁ => rw [hfa] at ha; exact normalizeAux_congr ha b [] k + | param => + exact ⟨VarNode.addVar_sorted hvs, NormLevel.addNode_flat hvs (NormLevel.addConst_flat h)⟩ + | imax => exact normalizeAux_congr (fun p => (toNormLevel_flat hvs p).trans (h p).symm) _ [] k + | mvar => exact ⟨hvs, h⟩ + +private theorem compare_nil_cons {x : Name} {l : List Name} : + compare ([] : List Name) (x :: l) = .lt := by + simp [compare, List.compareLex] + +private theorem compare_singleton {x y : Name} : compare [x] [y] = compare x y := by + simp only [compare, List.compareLex] + cases Name.cmp x y <;> rfl + +/-- Adding a name that sorts after everything already there appends at the end. -/ +private theorem modifyAt_append_of_lt (f : Tree → Tree) (a : Name) : ∀ (l : List (Name × Tree)), + (∀ q ∈ l, compare q.1 a = .lt) → modifyAt f a l = l ++ [(a, f default)] + | [], _ => rfl + | (x, t) :: l, hl => by + simp only [modifyAt, Std.OrientedCmp.gt_of_lt (cmp := Name.cmp) (hl (x, t) (.head _))] + rw [modifyAt_append_of_lt f a l fun q hq => hl q (.tail _ hq)]; rfl + +/-- The edge into a singleton key's node already provides `V({x}, x, 0)`. -/ +private theorem subsumeVars_singleton_self (v : VarNode) : + subsumeVars [v] [⟨v.var, 0⟩] = if v.offset == 0 then [] else [v] := by + simp only [subsumeVars, Std.ReflCmp.compare_self] + by_cases h : v.offset = 0 <;> simp [h] <;> omega + +/-- Folding the singleton keys of a flat map appends one child per parameter, in name order. -/ +private theorem flat_toTree_fold (s : NormLevel) (a : Nat) (b : List VarNode) : + ∀ (vs : List VarNode) (ch : List (Name × Tree)), + VarsSorted vs → + (∀ v ∈ vs, s.lexChain 1 [v.var] = [v.var]) → + (∀ v ∈ vs, ∀ q ∈ ch, compare q.1 v.var = .lt) → + List.foldl (fun t pn => + let path := s.lexChain (List.length pn.1) pn.1 + let var := if let v :: _ := path then subsumeVars pn.2.var [⟨v, 0⟩] else pn.2.var + Tree.modify path (fun t => { t with const := pn.2.const, var }) t) + ⟨a, b, ch⟩ (vs.map fun v => ([v.var], (⟨0, [v]⟩ : Node))) + = ⟨a, b, ch ++ vs.map fun v => (v.var, (⟨0, if v.offset == 0 then [] else [v], []⟩ : Tree))⟩ + | [], ch, _, _, _ => by simp + | v :: vs, ch, hvs, hlex, hch => by + have hch' : ∀ w ∈ vs, ∀ q ∈ ch ++ [(v.var, + (⟨0, if v.offset == 0 then [] else [v], []⟩ : Tree))], compare q.1 w.var = .lt := by + intro w hw q hq + rcases List.mem_append.1 hq with hq | hq + · exact hch w (.tail _ hw) q hq + · rw [List.mem_singleton] at hq; subst hq; exact hvs.head _ hw + have ih := flat_toTree_fold s a b vs _ hvs.of_cons (fun w hw => hlex w (.tail _ hw)) hch' + simp only [List.map_cons, List.foldl_cons, List.length_cons, List.length_nil, + hlex v (.head _), Tree.modify, subsumeVars_singleton_self] + rw [modifyAt_append_of_lt _ _ ch (fun q hq => hch v (.head _) q hq)] + exact ih.trans (by simp) + +/-- The entry list of a flat map, in key order. -/ +private theorem NormLevel.Flat.toList {s : NormLevel} {c : Nat} {vs : List VarNode} + (hvs : VarsSorted vs) (h : s.Flat c vs) : + s.toList = (if c = 0 then [] else [([], (⟨c, []⟩ : Node))]) ++ + vs.map fun v => ([v.var], (⟨0, [v]⟩ : Node)) := by + refine sorted_pairs_eq Std.TreeMap.ordered_keys_toList ?_ fun ⟨p, n⟩ => ?_ + · refine List.pairwise_append.2 ⟨?_, ?_, ?_⟩ + · by_cases hc0 : c = 0 <;> simp [hc0] + · rw [List.pairwise_map] + exact hvs.imp fun {u w} huw => by rw [compare_singleton]; exact huw + · intro x hx y hy + by_cases hc0 : c = 0 + · simp [hc0] at hx + · rw [if_neg hc0, List.mem_singleton] at hx + subst hx + obtain ⟨w, -, rfl⟩ := List.mem_map.1 hy + exact compare_nil_cons + · rw [Std.TreeMap.mem_toList_iff_getElem?_eq_some, ← Std.TreeMap.get?_eq_getElem?, h p] + constructor <;> intro hp + · obtain ⟨rfl, rfl, hc0⟩ | ⟨v, hv, rfl, rfl⟩ := flatGet_eq_some hp + · exact List.mem_append_left _ (by rw [if_neg hc0]; exact List.mem_singleton.2 rfl) + · exact List.mem_append_right _ (List.mem_map.2 ⟨v, hv, rfl⟩) + · obtain hp | hp := List.mem_append.1 hp + · split at hp <;> [cases hp; rename_i hc] + rw [List.mem_singleton] at hp; cases hp; simp [flatGet, hc] + · obtain ⟨v, hv, he⟩ := List.mem_map.1 hp + cases he; simp only [flatGet] + rw [(find?_var_eq_some hvs).2 ⟨hv, rfl⟩]; rfl + +/-- A singleton key's chain is forced: its one element is `addable` on the empty set, thanks +to the entry itself, and nothing remains to be completed. -/ +private theorem NormLevel.Flat.lexChain_singleton {s : NormLevel} {c : Nat} {vs : List VarNode} + (hvs : VarsSorted vs) (h : s.Flat c vs) {v : VarNode} (hv : v ∈ vs) : + s.lexChain 1 [v.var] = [v.var] := by + have haddable : s.addable v.var [] := by + rw [NormLevel.addable, Std.TreeMap.any_eq_any_toList, List.any_eq_true] + refine ⟨([v.var], ⟨0, [v]⟩), Std.TreeMap.mem_toList_iff_getElem?_eq_some.2 ?_, ?_⟩ + · rw [← Std.TreeMap.get?_eq_getElem?, h [v.var]] + simp only [flatGet] + rw [(find?_var_eq_some hvs).2 ⟨hv, rfl⟩]; rfl + · simp [subset] + rw [NormLevel.lexChain, List.find?_cons_of_pos] + · simp [NormLevel.lexChain] + · simp only [List.erase_cons_head, haddable, Bool.true_and]; rfl + +/-- Reading the tree off a flat map: every key is a singleton, so its `lexChain` is forced, +and the fold just adds one child per parameter in name order. -/ +theorem NormLevel.Flat.toTree {s : NormLevel} {c : Nat} {vs : List VarNode} + (hvs : VarsSorted vs) (h : s.Flat c vs) : s.toTree = flatTree c vs := by + rw [NormLevel.toTree, Std.TreeMap.foldl_eq_foldl_toList, NormLevel.Flat.toList hvs h] + have key := flat_toTree_fold s c [] vs [] hvs + (fun v => NormLevel.Flat.lexChain_singleton hvs h) (by simp) + by_cases hc0 : c = 0 + · subst hc0; rw [if_pos rfl]; exact key + · rw [if_neg hc0]; exact key + +/-- The sublevels of a single node keyed at `p`. -/ +def Node.HasSub (p : List Name) (n : Node) : Sub → Prop + | .const q k => p = q ∧ n.const = k ∧ k ≠ 0 + | .var q x k => p = q ∧ ⟨x, k⟩ ∈ n.var + +theorem NormLevel.hasSub_iff {s : NormLevel} {t} : + s.HasSub t ↔ ∃ p n, s.get? p = some n ∧ Node.HasSub p n t := by + match t with + | .const p k => + constructor + · rintro ⟨n, hn, hk, hk0⟩; exact ⟨p, n, hn, rfl, hk, hk0⟩ + · rintro ⟨q, n, hn, rfl, hk, hk0⟩; exact ⟨n, hn, hk, hk0⟩ + | .var p x k => + constructor + · rintro ⟨n, hn, hx⟩; exact ⟨p, n, hn, rfl, hx⟩ + · rintro ⟨q, n, hn, rfl, hx⟩; exact ⟨n, hn, hx⟩ + +theorem Node.subsume_hasSub : ∀ {t}, Node.HasSub p₁ (Node.subsume p₁ n p₂ n₂) t → + Node.HasSub p₁ n t + | .const _ k, ⟨rfl, hck, hk0⟩ => by + refine ⟨rfl, ?_, hk0⟩ + obtain h | h := Node.subsume_const_cases p₁ n p₂ n₂ + · rw [← h]; exact hck + · rw [h] at hck; exact absurd hck.symm hk0 + | .var _ _ _, ⟨rfl, hxk⟩ => ⟨rfl, Node.subsume_var_subset hxk⟩ + +theorem NormLevel.minimize_hasSub {acc : NormLevel} {t} + (h : Node.HasSub p₁ (acc.minimize p₁ n₁) t) : Node.HasSub p₁ n₁ t := by + rw [minimize, Std.TreeMap.foldl_eq_foldl_toList] at h + generalize acc.toList = l at h + induction l generalizing n₁ with + | nil => exact h + | cons a l ih => exact Node.subsume_hasSub (ih h) + +/-- A `subsumption` step only removes sublevels. -/ +theorem NormLevel.subsumption_step_hasSub {acc : NormLevel} {p₁ : List Name} {n₁ : Node} + (h₁ : acc.get? p₁ = some n₁) {t} + (h : NormLevel.HasSub (if (acc.minimize p₁ n₁).isEmpty then acc.erase p₁ + else acc.insert p₁ (acc.minimize p₁ n₁)) t) : acc.HasSub t := by + rw [hasSub_iff] at h ⊢ + obtain ⟨p, n, hp, hn⟩ := h + rw [subsumption_step_get?] at hp + split at hp <;> rename_i hpe + · split at hp <;> [cases hp; skip] + cases hp; subst hpe + exact ⟨p₁, n₁, h₁, minimize_hasSub hn⟩ + · exact ⟨p, n, hp, hn⟩ + +/-- Exactness of minimization, fold form: a sublevel surviving the subtraction of every entry +in `l` is not (strictly) dominated by any of their sublevels — domination forces equality. -/ +theorem NormLevel.minimize_exact_aux {acc : NormLevel} {p₁ : List Name} {n₁ : Node} + (hsort : ∀ p n, acc.get? p = some n → Sorted p) (hvsa : acc.SortedVars) + (h₁ : acc.get? p₁ = some n₁) (hs₁ : Sorted p₁) (hvs₁ : VarsSorted n₁.var) : + ∀ (l : List (List Name × Node)) (n : Node), + (∀ pn ∈ l, acc.get? pn.1 = some pn.2) → + (∀ x ∈ n.var, x ∈ n₁.var) → (n.const ≠ 0 → n.const = n₁.const) → VarsSorted n.var → + ∀ t, Node.HasSub p₁ (l.foldl (fun n pn => Node.subsume p₁ n pn.1 pn.2) n) t → + Node.HasSub p₁ n t ∧ + ∀ pn ∈ l, ∀ t', Node.HasSub pn.1 pn.2 t' → t.le t' → t = t' + | [], _, _, _, _, _, _, ht => ⟨ht, fun _ h => nomatch h⟩ + | (p₂, n₂) :: l, n, hl, hnvar, hnconst, hvs, t, ht => by + simp only [List.foldl_cons] at ht + have h₂ : acc.get? p₂ = some n₂ := hl _ (.head _) + have hvs₂ : VarsSorted n₂.var := hvsa _ _ h₂ + have hs₂ : Sorted p₂ := hsort _ _ h₂ + obtain ⟨ht', hrest⟩ := minimize_exact_aux hsort hvsa h₁ hs₁ hvs₁ l + (Node.subsume p₁ n p₂ n₂) (fun pn h => hl _ (.tail _ h)) + (fun x hx => hnvar _ (Node.subsume_var_subset hx)) + (fun h0 => by + obtain hc | hc := Node.subsume_const_cases p₁ n p₂ n₂ + · rw [hc]; exact hnconst (hc ▸ h0) + · exact absurd hc h0) + (hvs.sublist Node.subsume_var_sublist) t ht + refine ⟨Node.subsume_hasSub ht', ?_⟩ + rintro pn hpn t' ht'' hle + rcases List.mem_cons.1 hpn with rfl | hpn + · obtain ⟨q, k⟩ | ⟨q, x, k⟩ := t <;> obtain ⟨q', k'⟩ | ⟨q', y, k'⟩ := t' + · -- const dominated by const + obtain ⟨rfl, hck, hk0⟩ := ht' + obtain ⟨rfl, hck', hk0'⟩ := ht'' + obtain ⟨hsub, hlek⟩ := hle + have hgate : subset compare p₂ p₁ := subset_of_sorted hs₂ hs₁ hsub + have hsu : Node.subsume p₁ n p₂ n₂ = n.subsumeBy (p₁.length == p₂.length) n₂ := by + rw [Node.subsume, if_pos hgate] + by_cases hlen : p₁.length = p₂.length + · have hqq : p₂ = p₁ := subset_eq hgate hlen.symm + have hn₂ : n₂ = n₁ := by + rw [hqq] at h₂; cases h₂.symm.trans h₁; rfl + have hkc : n.const = k := by + obtain hc | hc := Node.subsume_const_cases p₁ n p₂ n₂ + · rw [← hc]; exact hck + · rw [hc] at hck; exact absurd hck.symm hk0 + have hne0 : n.const ≠ 0 := fun h0 => hk0 (hkc.symm.trans h0) + rw [hqq, show k = k' from by rw [← hck', hn₂, ← hnconst hne0, hkc]] + · have hbeq : (p₁.length == p₂.length) = false := by simpa using hlen + rw [hsu, hbeq] at hck + have hkc : n.const = k := by + obtain hc | hc := Node.subsumeBy_const_cases (same := false) n n₂ + · rw [← hc]; exact hck + · rw [hc] at hck; exact absurd hck.symm hk0 + refine absurd hck ?_ + rw [Node.subsumeBy_const_complete (n₁ := n) (n₂ := n₂) + (.inl ⟨rfl, by rw [hkc, hck']; exact hlek⟩)] + exact fun h => hk0 h.symm + · -- const dominated by a variable + obtain ⟨rfl, hck, hk0⟩ := ht' + obtain ⟨rfl, hyk⟩ := ht'' + obtain ⟨hsub, hlek⟩ := hle + have hgate : subset compare p₂ p₁ := subset_of_sorted hs₂ hs₁ hsub + have hsu : Node.subsume p₁ n p₂ n₂ = n.subsumeBy (p₁.length == p₂.length) n₂ := by + rw [Node.subsume, if_pos hgate] + rw [hsu] at hck + have hkc : n.const = k := by + obtain hc | hc := Node.subsumeBy_const_cases (same := p₁.length == p₂.length) n n₂ + · rw [← hc]; exact hck + · rw [hc] at hck; exact absurd hck.symm hk0 + refine absurd hck ?_ + rw [Node.subsumeBy_const_complete (n₁ := n) (n₂ := n₂) + (.inr ⟨⟨y, k'⟩, hyk, by rw [hkc]; exact hlek⟩)] + exact fun h => hk0 h.symm + · exact hle.elim + · -- variable dominated by a variable + obtain ⟨rfl, hxk⟩ := ht' + obtain ⟨rfl, hyk⟩ := ht'' + obtain ⟨hsub, rfl, hlek⟩ := hle + have hgate : subset compare p₂ p₁ := subset_of_sorted hs₂ hs₁ hsub + have hsu : Node.subsume p₁ n p₂ n₂ = n.subsumeBy (p₁.length == p₂.length) n₂ := by + rw [Node.subsume, if_pos hgate] + by_cases hlen : p₁.length = p₂.length + · have hqq : p₂ = p₁ := subset_eq hgate hlen.symm + have hn₂ : n₂ = n₁ := by + rw [hqq] at h₂; cases h₂.symm.trans h₁; rfl + have hk : (⟨x, k⟩ : VarNode) = ⟨x, k'⟩ := + hvs₁.eq_of_var_eq (hnvar _ (Node.subsume_var_subset hxk)) (hn₂ ▸ hyk) rfl + rw [hqq, show k = k' from congrArg VarNode.offset hk] + · have hbeq : (p₁.length == p₂.length) = false := by simpa using hlen + rw [hsu, hbeq] at hxk + exact (Node.subsumeBy_var_complete hvs hvs₂ hxk hyk rfl hlek).elim + · exact hrest _ hpn _ ht'' hle + +theorem NormLevel.minimize_exact {acc : NormLevel} {p₁ : List Name} {n₁ : Node} + (hsort : ∀ p n, acc.get? p = some n → Sorted p) (hvsa : acc.SortedVars) + (h₁ : acc.get? p₁ = some n₁) : + ∀ t t', Node.HasSub p₁ (acc.minimize p₁ n₁) t → acc.HasSub t' → t.le t' → t = t' := by + intro t t' ht ht' hle + rw [minimize, Std.TreeMap.foldl_eq_foldl_toList] at ht + have hmem pn (h : pn ∈ acc.toList) : acc.get? pn.1 = some pn.2 := + Std.TreeMap.get?_eq_getElem? .. ▸ Std.TreeMap.mem_toList_iff_getElem?_eq_some.1 h + obtain ⟨-, hexact⟩ := minimize_exact_aux hsort hvsa h₁ (hsort _ _ h₁) (hvsa _ _ h₁) + acc.toList n₁ hmem (fun _ => id) (fun _ => rfl) (hvsa _ _ h₁) t ht + obtain ⟨p₂, n₂, hp₂, hn₂⟩ := hasSub_iff.1 ht' + exact hexact (p₂, n₂) (Std.TreeMap.mem_toList_iff_getElem?_eq_some.2 + (Std.TreeMap.get?_eq_getElem? .. ▸ hp₂)) _ hn₂ hle + +/-- A normal form is reduced when no sublevel is dominated by another: domination between +recorded sublevels forces them to be the same sublevel. -/ +def NormLevel.Reduced (s : NormLevel) : Prop := + ∀ t t', s.HasSub t → s.HasSub t' → t.le t' → t = t' + +/-- `subsumption` produces a reduced map: every entry is minimized against the (current) +whole map, minimization removes exactly the dominated sublevels, and later steps only +shrink the map, which cannot introduce new domination. -/ +theorem NormLevel.subsumption_reduced {s : NormLevel} + (hsort : ∀ p n, s.get? p = some n → Sorted p) (hvsa : s.SortedVars) : + s.subsumption.Reduced := by + have hmem pn (h : pn ∈ s.toList) : s.get? pn.1 = some pn.2 := + Std.TreeMap.get?_eq_getElem? .. ▸ Std.TreeMap.mem_toList_iff_getElem?_eq_some.1 h + have nd : (s.toList.map Prod.fst).Nodup := by simpa using Std.TreeMap.nodup_keys (t := s) + rw [Reduced, subsumption, Std.TreeMap.foldl_eq_foldl_toList] + suffices ∀ (l : List (List Name × Node)) (acc : NormLevel), + (l.map Prod.fst).Nodup → + (∀ pn ∈ l, acc.get? pn.1 = some pn.2) → + (∀ p n, acc.get? p = some n → Sorted p) → acc.SortedVars → + (∀ p n, acc.get? p = some n → p ∉ l.map Prod.fst → + ∀ t t', Node.HasSub p n t → acc.HasSub t' → t.le t' → t = t') → + ∀ t t', (List.foldl (fun acc pn => + let n := acc.minimize pn.1 pn.2 + if n.isEmpty then acc.erase pn.1 else acc.insert pn.1 n) acc l).HasSub t → + (List.foldl (fun acc pn => + let n := acc.minimize pn.1 pn.2 + if n.isEmpty then acc.erase pn.1 else acc.insert pn.1 n) acc l).HasSub t' → + t.le t' → t = t' from + this _ _ nd hmem hsort hvsa fun p n hp hnp => absurd + (List.mem_map_of_mem (f := Prod.fst) (Std.TreeMap.mem_toList_iff_getElem?_eq_some.2 + (Std.TreeMap.get?_eq_getElem? .. ▸ hp))) hnp + clear hmem nd hsort hvsa; intro l + induction l with + | nil => + intro acc _ _ _ _ hred t t' ht ht' hle + obtain ⟨p, n, hp, hnt⟩ := hasSub_iff.1 ht + exact hred p n hp (by simp) t t' hnt ht' hle + | cons pn l ih => + obtain ⟨p₂, n₂⟩ := pn + intro acc nd hl hsorta hvsacc hred + simp only [List.map_cons, List.nodup_cons] at nd + have h₂ : acc.get? p₂ = some n₂ := hl _ (.head _) + simp only [List.foldl_cons] + have hstep := subsumption_step_get? acc n₂ p₂ + refine ih _ nd.2 (fun pn' h => ?_) (fun p n h => ?_) (fun p n h => ?_) + (fun p n hp hnp t t' hnt ht' hle => ?_) + · have hne : p₂ ≠ pn'.1 := fun e => nd.1 (e ▸ List.mem_map_of_mem (f := Prod.fst) h) + rw [hstep, if_neg hne] + exact hl _ (.tail _ h) + · rw [hstep] at h; split at h <;> rename_i hpe + · split at h <;> [cases h; skip] + cases h; exact hpe ▸ hsorta _ _ h₂ + · exact hsorta _ _ h + · rw [hstep] at h; split at h <;> rename_i hpe + · split at h <;> [cases h; skip] + cases h + exact (hvsacc _ _ h₂).sublist minimize_var_sublist + · exact hvsacc _ _ h + · rw [hstep] at hp; split at hp <;> rename_i hpe + · split at hp <;> [cases hp; skip] + cases hp; subst hpe + exact minimize_exact hsorta hvsacc h₂ t t' hnt (subsumption_step_hasSub h₂ ht') hle + · refine hred p n hp ?_ t t' hnt (subsumption_step_hasSub h₂ ht') hle + simp only [List.map_cons, List.mem_cons, not_or] + exact ⟨fun e => hpe e.symm, hnp⟩ + +theorem normalize_reduced : (normalize u).Reduced := by + refine NormLevel.subsumption_reduced ?_ (normalizeAux_sortedVars fun _ _ => by simp) + exact fun p n h => (normalizeAux_wf (by simp) (by simp [NormLevel.WF]) p n h).2.2 + +/-! Canonicity: two reduced normal forms with the same semantics have the same sublevels, +and hence are equal maps. -/ + +instance : LawfulBEq Node where + rfl {a} := by cases a <;> simp! +instances [instBEqNode] + eq_of_beq {a b} h := by + cases a; cases b + simp! +instances [instBEqNode] at h + simp [h.1, h.2] + +theorem VarsSorted.eq_of_mem_iff : ∀ {l₁ l₂ : List VarNode}, VarsSorted l₁ → VarsSorted l₂ → + (∀ x, x ∈ l₁ ↔ x ∈ l₂) → l₁ = l₂ + | [], [], _, _, _ => rfl + | [], _ :: _, _, _, h => nomatch (h _).2 (.head _) + | _ :: _, [], _, _, h => nomatch (h _).1 (.head _) + | a :: l₁, b :: l₂, h₁, h₂, h => by + cases show a = b by + rcases List.mem_cons.1 ((h a).1 (.head _)) with rfl | ha <;> [rfl; skip] + rcases List.mem_cons.1 ((h b).2 (.head _)) with rfl | hb <;> [rfl; skip] + exact absurd (h₂.head _ ha) (by rw [Std.OrientedCmp.gt_of_lt (h₁.head _ hb)]; simp) + refine congrArg (a :: ·) (VarsSorted.eq_of_mem_iff h₁.of_cons h₂.of_cons + fun x => ⟨fun hx => ?_, fun hx => ?_⟩) + · rcases List.mem_cons.1 ((h x).1 (.tail _ hx)) with rfl | hx' + · exact absurd (h₁.head _ hx) (by rw [Std.ReflOrd.compare_self]; simp) + · exact hx' + · rcases List.mem_cons.1 ((h x).2 (.tail _ hx)) with rfl | hx' + · exact absurd (h₂.head _ hx) (by rw [Std.ReflOrd.compare_self]; simp) + · exact hx' + +theorem NormLevel.HasSub.path_sorted {s : NormLevel} + (hsort : ∀ p n, s.get? p = some n → Sorted p) : ∀ {t}, s.HasSub t → Sorted t.path + | .const _ _, ⟨_, hn, _⟩ => hsort _ _ hn + | .var _ _ _, ⟨_, hn, _⟩ => hsort _ _ hn + +/-- In reduced maps, mutual per-sublevel domination pins the sublevels to be equal: the +dominator of a sublevel is itself dominated by a sublevel of the first map, which by +reducedness is the sublevel we started from, and antisymmetry finishes. -/ +theorem NormLevel.Reduced.hasSub_iff_hasSub {A B : NormLevel} + (rA : A.Reduced) (rB : B.Reduced) + (sortA : ∀ p n, A.get? p = some n → Sorted p) + (sortB : ∀ p n, B.get? p = some n → Sorted p) + (hAB : ∀ t, A.HasSub t → ∃ t', B.HasSub t' ∧ t.le t') + (hBA : ∀ t, B.HasSub t → ∃ t', A.HasSub t' ∧ t.le t') : + ∀ t, A.HasSub t ↔ B.HasSub t := by + suffices ∀ {A B : NormLevel}, A.Reduced → + (∀ p n, A.get? p = some n → Sorted p) → (∀ p n, B.get? p = some n → Sorted p) → + (∀ t, A.HasSub t → ∃ t', B.HasSub t' ∧ t.le t') → + (∀ t, B.HasSub t → ∃ t', A.HasSub t' ∧ t.le t') → + ∀ t, A.HasSub t → B.HasSub t from + fun t => ⟨this rA sortA sortB hAB hBA t, this rB sortB sortA hBA hAB t⟩ + clear rA rB sortA sortB hAB hBA + intro A B rA sortA sortB hAB hBA t ht + obtain ⟨t', ht', hle⟩ := hAB t ht + obtain ⟨t'', ht'', hle'⟩ := hBA t' ht' + cases rA t t'' ht ht'' (hle.trans hle') + exact (hle.antisymm (HasSub.path_sorted sortA ht) (HasSub.path_sorted sortB ht') hle').symm ▸ ht' + +/-- Two reduced normal forms with the same sublevels are equal as `NormLevel`s. -/ +theorem NormLevel.eq_of_hasSub_iff {A B : NormLevel} + (hvsA : A.SortedVars) (hvsB : B.SortedVars) + (hneA : ∀ p n, A.get? p = some n → n.isEmpty = false) + (hneB : ∀ p n, B.get? p = some n → n.isEmpty = false) + (h : ∀ t, A.HasSub t ↔ B.HasSub t) : A == B := by + suffices ∀ {A B : NormLevel}, A.SortedVars → B.SortedVars → + (∀ p n, A.get? p = some n → n.isEmpty = false) → + (∀ t, A.HasSub t ↔ B.HasSub t) → + ∀ p n, A.get? p = some n → B.get? p = some n by + have h1 := @this A B hvsA hvsB hneA h + have h2 := @this B A hvsB hvsA hneB fun t => (h t).symm + simp +instances only [instBEqNormLevel, Std.TreeMap.all_eq_all_toList, + Bool.and_eq_true, List.all_eq_true] + constructor <;> rintro ⟨p, n⟩ hpn + · have := h1 p n (Std.TreeMap.get?_eq_getElem? .. ▸ + Std.TreeMap.mem_toList_iff_getElem?_eq_some.1 hpn) + rw [Std.TreeMap.get?_eq_getElem?] at this + simp [this] + · have := h2 p n (Std.TreeMap.get?_eq_getElem? .. ▸ + Std.TreeMap.mem_toList_iff_getElem?_eq_some.1 hpn) + rw [Std.TreeMap.get?_eq_getElem?] at this + simp [this] + clear hvsA hvsB hneA hneB h + intro A B hvsA hvsB hneA h p n hp + have hne := hneA _ _ hp + rw [Node.isEmpty, Bool.and_eq_false_iff] at hne + have hBp : ∃ m, B.get? p = some m := by + obtain h0 | hv := hne + · obtain ⟨m, hm, -⟩ := (h (.const p n.const)).1 ⟨n, hp, rfl, by simpa using h0⟩ + exact ⟨m, hm⟩ + · obtain ⟨x, hx⟩ := List.exists_mem_of_ne_nil _ (by simpa using hv) + obtain ⟨m, hm, -⟩ := (h (.var p x.var x.offset)).1 ⟨n, hp, hx⟩ + exact ⟨m, hm⟩ + obtain ⟨m, hm⟩ := hBp + have hconst : n.const = m.const := by + by_cases h0 : n.const = 0 + · by_cases h0' : m.const = 0 + · rw [h0, h0'] + · obtain ⟨n', hn', hc, -⟩ := (h (.const p m.const)).2 ⟨m, hm, rfl, h0'⟩ + cases hn'.symm.trans hp + exact absurd (h0 ▸ hc).symm h0' + · obtain ⟨m', hm', hc, -⟩ := (h (.const p n.const)).1 ⟨n, hp, rfl, h0⟩ + cases hm'.symm.trans hm + exact hc.symm + have hvar : n.var = m.var := by + refine VarsSorted.eq_of_mem_iff (hvsA _ _ hp) (hvsB _ _ hm) + fun x => ⟨fun hx => ?_, fun hx => ?_⟩ + · obtain ⟨m', hm', hx'⟩ := (h (.var p x.var x.offset)).1 ⟨n, hp, hx⟩ + cases hm'.symm.trans hm + exact hx' + · obtain ⟨n', hn', hx'⟩ := (h (.var p x.var x.offset)).2 ⟨m, hm, hx⟩ + cases hn'.symm.trans hp + exact hx' + obtain ⟨nc, nv⟩ := n + obtain ⟨mc, mv⟩ := m + cases hconst; cases hvar + exact hm + +/-- Semantically equal levels have `BEq`-equal normal forms. -/ +theorem normalize_complete (hu : VLevel.ofLevel ls u = some u') + (hv : VLevel.ofLevel ls v = some v') : normalize u == normalize v ↔ u' ≈ v' := by + refine .trans ⟨fun h ls => ?_, fun h => ?_⟩ VLevel.equiv_def.symm + · rw [← normalize_eval hu, NormLevel.eval_congr h, normalize_eval hv] + have h₁ : ∀ ρ, (normalize u).eval ls ρ ≤ (normalize v).eval ls ρ := fun ρ => by + rw [normalize_eval hu, normalize_eval hv, h ρ]; exact Nat.le_refl _ + have h₂ : ∀ ρ, (normalize v).eval ls ρ ≤ (normalize u).eval ls ρ := fun ρ => by + rw [normalize_eval hu, normalize_eval hv, h ρ]; exact Nat.le_refl _ + exact NormLevel.eq_of_hasSub_iff normalize_sortedVars normalize_sortedVars + normalize_nonempty normalize_nonempty + (NormLevel.Reduced.hasSub_iff_hasSub normalize_reduced normalize_reduced + normalize_sorted normalize_sorted + (NormLevel.separation (normalize_keys hu) normalize_vars h₁) + (NormLevel.separation (normalize_keys hv) normalize_vars h₂)) + +/-! `BEq`-equal maps have equal `toList`s, and the reconstruction depends on the map only +through `toList`, so equal normal forms reify to syntactically equal levels. (`TreeMap` +equality itself does not follow from `==`: the internal tree shape depends on insertion +order.) -/ + +theorem NormLevel.toList_eq {A B : NormLevel} (h : A == B) : A.toList = B.toList := by + simp +instances only [instBEqNormLevel, Std.TreeMap.all_eq_all_toList, + Bool.and_eq_true, List.all_eq_true] at h + refine sorted_pairs_eq Std.TreeMap.ordered_keys_toList Std.TreeMap.ordered_keys_toList + fun x => ⟨fun hx => ?_, fun hx => ?_⟩ + · have := h.1 x hx + rw [beq_iff_eq, Std.TreeMap.get?_eq_getElem?] at this + exact Std.TreeMap.mem_toList_iff_getElem?_eq_some.2 this + · have := h.2 x hx + rw [beq_iff_eq, Std.TreeMap.get?_eq_getElem?] at this + exact Std.TreeMap.mem_toList_iff_getElem?_eq_some.2 this + +theorem NormLevel.addable_congr {A B : NormLevel} (h : A.toList = B.toList) : + A.addable a acc = B.addable a acc := by + rw [addable, addable, Std.TreeMap.any_eq_any_toList, Std.TreeMap.any_eq_any_toList, h] + +theorem NormLevel.feasible_go_congr {A B : NormLevel} (h : A.toList = B.toList) : + ∀ fuel acc rem, NormLevel.feasible.go A fuel acc rem = NormLevel.feasible.go B fuel acc rem + | 0, _, _ => rfl + | fuel+1, acc, rem => by + simp only [feasible.go] + rw [show (fun a => A.addable a acc) = fun a => B.addable a acc from + funext fun a => addable_congr h] + cases rem.find? fun a => B.addable a acc with + | none => rfl + | some a => exact feasible_go_congr h fuel _ _ + +theorem NormLevel.feasible_congr {A B : NormLevel} (h : A.toList = B.toList) : + A.feasible acc rem = B.feasible acc rem := by + simp only [feasible]; exact feasible_go_congr h .. + +theorem NormLevel.lexChain_congr {A B : NormLevel} (h : A.toList = B.toList) : + ∀ fuel p, A.lexChain fuel p = B.lexChain fuel p + | 0, _ => rfl + | fuel+1, p => by + simp only [lexChain] + rw [show (fun a => A.addable a (p.erase a) && A.feasible [] (p.erase a)) + = fun a => B.addable a (p.erase a) && B.feasible [] (p.erase a) from + funext fun a => by rw [addable_congr h, feasible_congr h]] + cases p.find? fun a => B.addable a (p.erase a) && B.feasible [] (p.erase a) with + | none => rfl + | some a => exact congrArg (a :: ·) (lexChain_congr h fuel _) + +/-- The reconstruction depends only on the entry list of the map. -/ +theorem NormLevel.toTree_congr {A B : NormLevel} (h : A.toList = B.toList) : + A.toTree = B.toTree := by + rw [toTree, toTree, Std.TreeMap.foldl_eq_foldl_toList, Std.TreeMap.foldl_eq_foldl_toList, h] + congr 1 + funext t pn + rw [lexChain_congr h] + +/-- The fast path is transparent: it computes the same tree the general path does. -/ +theorem normalize'_eq (l : Level) : normalize' l = (normalize l).toTree.reify := by + rw [normalize'] + have hrep := flatAux_rep (l := l) (k := 0) (c := 0) (vs := []) (s := {}) .nil + (fun p => by match p with | [] | [_] | _::_::_ => simp [flatGet]) + match hf : flatAux l 0 (0, []) with + | .ok (c, vs) => + rw [hf] at hrep + obtain ⟨hvs, hflat⟩ := hrep + rw [normalize, (NormLevel.subsumption_flat hflat).toTree hvs] + | .error s => + rw [hf] at hrep; dsimp only + rw [normalize, NormLevel.toTree_congr (toList_eq_of_get?_eq (NormLevel.subsumption_congr hrep))] + +end Normalize + +theorem isStructEq_eq {u v : Level} (h : isStructEq u v) : u = v := by + induction u generalizing v with + | zero => cases v <;> simp_all [isStructEq] + | succ u ih => + cases v <;> simp [isStructEq] at h + exact congrArg Level.succ (ih h) + | max u₁ u₂ ih₁ ih₂ => + cases v <;> simp [isStructEq] at h + cases ih₁ h.1 + cases ih₂ h.2 + rfl + | imax u₁ u₂ ih₁ ih₂ => + cases v <;> simp [isStructEq] at h + cases ih₁ h.1 + cases ih₂ h.2 + rfl + | param u => + cases v <;> simp_all [isStructEq] + | mvar u => + rcases u with ⟨u⟩ + cases v <;> simp_all [isStructEq] + +theorem isStructEq_iff_eq {u v : Level} : isStructEq u v ↔ u = v := by + constructor + · exact isStructEq_eq + · rintro rfl + induction u <;> simp_all [isStructEq] + +theorem isEquiv'_wf (h : isEquiv' u v) + (hu : VLevel.ofLevel ls u = some u') (hv : VLevel.ofLevel ls v = some v') : u' ≈ v' := by + simp only [isEquiv', Bool.or_eq_true] at h + obtain h | h := h + · exact isEquiv_wf h hu hv + · refine VLevel.equiv_def.2 fun ρ => ?_ + rw [← Normalize.normalize_eval (ρ := ρ) hu, ← Normalize.normalize_eval (ρ := ρ) hv] + exact Normalize.NormLevel.eval_congr h + +/-- Soundness of reification: the level `normalize'` reconstructs evaluates like the input +everywhere. Reification is `toTree` followed by `reify`, and both preserve the value: the +tree's `imax` chains contribute nothing the normal form does not already have, since every +key admits a chain (`normalize_feas`) and `lexChain` then picks an admissible one, and +nothing is lost, since every entry is recorded at the end of its chain. -/ +theorem normalize'_eval (hu : VLevel.ofLevel ls u = some u') : + Level.eval (Normalize.evalParam ls ρ) μ (normalize' u) = u'.eval ρ := by + open Normalize in + rw [normalize'_eq, Tree.reify_eval, NormLevel.toTree_eval normalize_sorted normalize_feas] + exact normalize_eval hu + +theorem geq'_wf (hu : VLevel.ofLevel ls u = some u') (hv : VLevel.ofLevel ls v = some v') + (h : geq' u v) : v' ≤ u' := by + simp only [geq', Bool.or_eq_true] at h + obtain h | h := h + · exact geq_wf h hu hv + · intro ρ + rw [← Normalize.normalize_eval (ρ := ρ) hv, ← Normalize.normalize_eval (ρ := ρ) hu] + exact Normalize.NormLevel.le_eval Normalize.normalize_vars h + +theorem isEquivList_wf (H : Level.isEquivList us vs) : + List.mapM (VLevel.ofLevel Us) us = some us' → + List.mapM (VLevel.ofLevel Us) vs = some vs' → us'.Forall₂ (· ≈ ·) vs' := by + simp [Level.isEquivList] at H; revert us' vs' + induction us generalizing vs with cases vs <;> simp [List.all2] at H <;> simp | cons u us ih + rename_i v vs; rintro _ _ u' hu us' hus rfl v' hv vs' hvs rfl + exact .cons (isEquiv'_wf H.1 hu hv) (ih H.2 hus hvs) + +/-- Canonicity of `normalize'`: semantically equal levels reconstruct to syntactically equal +levels. The normal forms are `BEq`-equal, hence have the same entry list, and the +reconstruction (`lexChain` and the tree fold) depends on the map only through its entry +list. -/ +theorem normalize'_complete (hu : VLevel.ofLevel ls u = some u') + (hv : VLevel.ofLevel ls v = some v') : normalize' u = normalize' v ↔ u' ≈ v' := by + refine ⟨fun h => ?_, fun h => ?_⟩ + · refine VLevel.equiv_def.2 fun ρ => ?_ + rw [← normalize'_eval (μ := fun _ => 0) hu, ← normalize'_eval hv, h] + · rw [Normalize.normalize'_eq, Normalize.normalize'_eq] + rw [← Normalize.normalize_complete hu hv] at h + rw [Normalize.NormLevel.toTree_congr (Normalize.NormLevel.toList_eq h)] + +/-- Completeness of `isEquiv'`: semantically equal levels have equal normal forms. Both +normal forms are reduced (`subsumption_reduced`), mutually dominate each other's sublevels +(`separation`), and reduced forms with the same sublevels are the same map. -/ +theorem isEquiv'_complete (hu : VLevel.ofLevel ls u = some u') + (hv : VLevel.ofLevel ls v = some v') : isEquiv' u v ↔ u' ≈ v' := by + simp only [isEquiv', Bool.or_eq_true, Normalize.normalize_complete hu hv] + exact ⟨fun h => h.elim (fun h => isEquiv_wf h hu hv) id, .inr⟩ + +/-- Completeness of `geq'`: every valid semantic inequality is accepted. Every sublevel of +`normalize v` is semantically bounded by `normalize u`, hence syntactically dominated by one +of its sublevels (`separation`), which is exactly what the discharging fold in +`NormLevel.le` checks for. -/ +theorem geq'_complete (hu : VLevel.ofLevel ls u = some u') + (hv : VLevel.ofLevel ls v = some v') : geq' u v ↔ v' ≤ u' := by + open Normalize in + refine ⟨geq'_wf hu hv, fun h => ?_⟩ + simp only [geq', Bool.or_eq_true] + refine .inr ?_ + refine NormLevel.le_complete normalize_sortedVars normalize_sortedVars normalize_nonempty + normalize_sorted normalize_sorted ?_ + refine NormLevel.separation (normalize_keys hv) normalize_vars fun ρ => ?_ + rw [normalize_eval hv, normalize_eval hu] + exact h ρ diff --git a/Ix/Theory/Named/Verify/LevelStd.lean b/Ix/Theory/Named/Verify/LevelStd.lean new file mode 100644 index 000000000..e05736b3c --- /dev/null +++ b/Ix/Theory/Named/Verify/LevelStd.lean @@ -0,0 +1,549 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Batteries.Tactic.OpenPrivate +import Ix.Theory.Named.VLevel +import Ix.Theory.Named.Verify.QSort +import Ix.Theory.Named.Verify.NormLt + +open Ix.Theory (VLevel) + +open private go in Lean.Level.geq +open private accMax mkIMaxAux isExplicitSubsumed from Lean.Level + +namespace Lean.Level + +open Ix.Theory.Named + +/-! +Semantic soundness of the universe-level operations in Lean's standard library. +`normalize` is an opaque `partial def`, so `Ix.Theory.Named.Verify.Axioms` assumes it +equals the total copy `Lean.Level.Total.normalize` defined there; the semantic +behavior of that copy is `eval_normalize` below, which is still open. The exact +correspondence between `geqCore` below and the private recursion used by +`Lean.Level.geq` is proved. +-/ + +variable (ρ : Name → Nat) (μ : LMVarId → Nat) in +def eval : Level → Nat + | .zero => 0 + | .param n => ρ n + | .mvar n => μ n + | .succ l => eval l + 1 + | .max l₁ l₂ => Nat.max (eval l₁) (eval l₂) + | .imax l₁ l₂ => Nat.imax (eval l₁) (eval l₂) + +private def offset : Level → Nat + | .succ l => offset l + 1 + | _ => 0 + +private theorem getOffsetAux_eq_offset : + l.getOffsetAux k = offset l + k := by + induction l generalizing k with + | succ l ih => simp only [Level.getOffsetAux, offset, ih]; omega + | _ => simp [Level.getOffsetAux, offset] + +private theorem getOffset_eq_offset : l.getOffset = offset l := by + simp [Level.getOffset, getOffsetAux_eq_offset] + +theorem eval_getLevelOffset : + eval ρ μ l = eval ρ μ l.getLevelOffset + l.getOffset := by + induction l with | succ l ih => ?_ | _ => rfl + simp only [eval, Level.getLevelOffset, getOffset_eq_offset, offset, ih] + omega + +theorem fallback_sound + (h : (u.getLevelOffset = v.getLevelOffset ∨ v.getLevelOffset.isZero = true) ∧ + v.getOffset ≤ u.getOffset) : + eval ρ μ v ≤ eval ρ μ u := by + rw [eval_getLevelOffset, eval_getLevelOffset (l := u)] + rcases h with ⟨hv | hv, hk⟩ + · rw [← hv]; omega + · have hv : v.getLevelOffset = .zero := by + generalize hbase : v.getLevelOffset = base at hv + cases base <;> simp_all [Level.isZero] + simp [hv, eval] + omega + +def geqCore : Level → Level → Bool + -- Keep this in the same source-shaped form as `go`'s `u == v || ...` prefix. + -- The apparently redundant `|| true` is therefore deliberate. + | u, .zero => u == .zero || true + | u, .max v₁ v₂ => u == .max v₁ v₂ || (geqCore u v₁ && geqCore u v₂) + | .max u₁ u₂, .imax v₁ v₂ => + (.max u₁ u₂ : Level) == .imax v₁ v₂ || + (geqCore u₁ (.imax v₁ v₂) || geqCore u₂ (.imax v₁ v₂) || + (geqCore (.max u₁ u₂) v₁ && geqCore (.max u₁ u₂) v₂)) + | .max u₁ u₂, v => + let u := .max u₁ u₂ + u == v || (geqCore u₁ v || geqCore u₂ v || + ((u.getLevelOffset == v.getLevelOffset || v.getLevelOffset.isZero) && + u.getOffset ≥ v.getOffset)) + | .imax u₁ u₂, v => (.imax u₁ u₂ : Level) == v || geqCore u₂ v + | .succ u, .succ v => (.succ u : Level) == .succ v || geqCore u v + | u, .imax v₁ v₂ => u == .imax v₁ v₂ || (geqCore u v₁ && geqCore u v₂) + | u, v => u == v || + ((u.getLevelOffset == v.getLevelOffset || v.getLevelOffset.isZero) && + u.getOffset ≥ v.getOffset) + termination_by u v => (u, v) + +private theorem geqCore_eq_go : geqCore u v = go u v := by + fun_induction go with | _ u v + cases u <;> cases v <;> simp_all [geqCore, go] + +theorem geqCore_sound (h : geqCore u v) : eval ρ μ v ≤ eval ρ μ u := by + induction u, v using geqCore.induct with + simp only [geqCore, Bool.or_eq_true, Bool.and_eq_true, beq_iff_eq, + decide_eq_true_eq] at h + | case1 => simp [eval] + | case2 _ _ _ ih₂ ih₁ => + rcases h with rfl | ⟨h₁, h₂⟩ + · exact Nat.le_refl _ + · exact (Nat.max_le).2 ⟨ih₂ h₁, ih₁ h₂⟩ + | case3 u₁ u₂ v₁ v₂ ih₄ ih₃ ih₂ ih₁ => + rcases h with heq | (h | h) | ⟨h₁, h₂⟩ + · exact Nat.le_of_eq (congrArg (eval ρ μ) heq).symm + · exact Nat.le_trans (ih₄ h) (Nat.le_max_left ..) + · exact Nat.le_trans (ih₃ h) (Nat.le_max_right ..) + · simp only [eval, Nat.imax] + split + · exact Nat.zero_le _ + · exact (Nat.max_le).2 ⟨ih₂ h₁, ih₁ h₂⟩ + | case4 u₁ u₂ v _ _ _ ih₂ ih₁ => + rcases h with rfl | h + · exact Nat.le_refl _ + · rcases h with (h | h) | h + · exact Nat.le_trans (ih₂ h) (Nat.le_max_left ..) + · exact Nat.le_trans (ih₁ h) (Nat.le_max_right ..) + · exact fallback_sound h + | case5 u₁ u₂ v _ _ ih => + rcases h with rfl | h + · exact Nat.le_refl _ + · simp only [eval, Nat.imax] + have hv := ih h + split <;> rename_i hz + · simpa [hz] using hv + · exact Nat.le_trans hv (Nat.le_max_right ..) + | case6 u v ih => + rcases h with heq | h + · exact Nat.le_of_eq (congrArg (eval ρ μ) heq).symm + · simpa [eval] using Nat.add_le_add_right (ih h) 1 + | case7 u v₁ v₂ _ _ ih₂ ih₁ => + rcases h with rfl | ⟨h₁, h₂⟩ + · exact Nat.le_refl _ + · simp only [eval, Nat.imax] + split + · exact Nat.zero_le _ + · exact (Nat.max_le).2 ⟨ih₂ h₁, ih₁ h₂⟩ + | case8 => + rcases h with heq | h + · exact Nat.le_of_eq (congrArg (eval ρ μ) heq).symm + · exact fallback_sound h + +/-! +### Soundness of `normalize` + +The proof is by strong induction on `Total.size`. The mutual recursion with +`getMaxArgsAux` is untangled by observing that `getMaxArgsAux l true` recurses only +structurally, and `getMaxArgsAux l false` calls `normalize` only on levels of size at +most `size l`, so both can be handled by standalone lemmas parameterized by the +induction hypothesis for `normalize`. + +The `max` branch sorts the collected arguments with `qsort normLt` and then drops +dominated entries: `mkMaxAux` drops an entry when the next one has the same level base +(relying on offsets being sorted within a base class), and the explicit (constant) +entries in the sorted prefix are dropped when subsumed by the largest explicit or by +some offset to its right. All of this is justified by a single consequence of +sortedness: entries with equal `getLevelOffset` occur in order of `getOffset` +(`explicit` entries all have base `zero`, so this also orders the explicit prefix). +That fact, together with the fact that `qsort` permutes the array, are the only +properties of sorting used; they are `qsort_perm_toList` and `pairwise_qsort_normLt` +below, currently unproved because `Array.qsort` has no specification in the standard +library. +-/ + +theorem le_ext_le {n m : Nat} (H : ∀ x, n ≤ x → m ≤ x) : m ≤ n := H _ (Nat.le_refl _) + +theorem nat_ext_le {n m : Nat} (H : ∀ x, n ≤ x ↔ m ≤ x) : n = m := + Nat.le_antisymm ((H _).2 (Nat.le_refl _)) ((H _).1 (Nat.le_refl _)) + +theorem eval_addOffset : eval ρ μ (addOffset l k) = eval ρ μ l + k := by + suffices ∀ k l, eval ρ μ (addOffsetAux k l) = eval ρ μ l + k from this .. + intro k; induction k with intro l + | zero => rfl + | succ k ih => rw [addOffsetAux, ih]; simp [eval, mkLevelSucc]; omega + +theorem isZero_iff : isZero l ↔ l = .zero := by cases l <;> simp [Level.isZero] + +theorem isNeverZero_sound (h : l.isNeverZero = true) : 0 < eval ρ μ l := by + induction l with + | zero | param | mvar => simp [isNeverZero] at h + | succ l => simp [eval] + | max l₁ l₂ ih₁ ih₂ => + simp only [isNeverZero, Bool.or_eq_true] at h + simp only [eval, Nat.max_eq_max] + obtain h | h := h + · have := ih₁ h; omega + · have := ih₂ h; omega + | imax l₁ l₂ _ ih₂ => + simp only [isNeverZero] at h + have := ih₂ h + simp only [eval, Nat.imax, Nat.max_eq_max]; split <;> omega + +theorem eval_accMax : eval ρ μ (accMax r p k) = Nat.max (eval ρ μ r) (eval ρ μ p + k) := by + rw [accMax]; split <;> rename_i h + · rw [isZero_iff.1 h, eval_addOffset]; simp [eval] + · simp [mkLevelMax, eval, eval_addOffset] + +theorem eval_mkIMaxAux : + eval ρ μ (mkIMaxAux a b) = Nat.imax (eval ρ μ a) (eval ρ μ b) := by + unfold mkIMaxAux; split + · simp [eval, Nat.imax] + · simp only [eval, Nat.imax]; split <;> [omega; simp] + · simp only [eval, Nat.imax, Nat.max_eq_max]; split <;> [omega; rw [Nat.max_eq_right (by omega)]] + · split <;> rename_i h + · cases eq_of_beq h; simp only [Nat.imax, Nat.max_eq_max] + split <;> [omega; rw [Nat.max_self]] + · simp [mkLevelIMax, eval] + +/-- The maximum of the evaluations of a list of levels. -/ +def evalList (ρ : Name → Nat) (μ : LMVarId → Nat) (ls : List Level) : Nat := + ls.foldr (fun l n => Nat.max (eval ρ μ l) n) 0 + +theorem evalList_le_iff : evalList ρ μ ls ≤ n ↔ ∀ l ∈ ls, eval ρ μ l ≤ n := by + induction ls with + | nil => simp [evalList, Nat.zero_le] + | cons l ls ih => + show Nat.max (eval ρ μ l) (evalList ρ μ ls) ≤ n ↔ _ + rw [Nat.max_eq_max, Nat.max_le, ih]; simp + +theorem le_evalList (h : l ∈ ls) : eval ρ μ l ≤ evalList ρ μ ls := + evalList_le_iff.1 (Nat.le_refl _) _ h + +theorem evalList_perm (h : ls₁.Perm ls₂) : evalList ρ μ ls₁ = evalList ρ μ ls₂ := by + refine nat_ext_le fun _ => ?_; simp only [evalList_le_iff, h.mem_iff] + +theorem evalList_append : evalList ρ μ (ls₁ ++ ls₂) = + Nat.max (evalList ρ μ ls₁) (evalList ρ μ ls₂) := by + induction ls₁ with | nil => simp [evalList] | cons l ls ih + show Nat.max _ (evalList ρ μ (ls ++ ls₂)) = Nat.max (Nat.max _ (evalList ρ μ ls)) _ + rw [ih]; simp only [Nat.max_eq_max]; rw [Nat.max_assoc] + +/-- `Array.qsort` returns a permutation of its input (`Array.qsort_perm`). -/ +theorem qsort_perm (as : Array Level) : (as.qsort normLt).toList.Perm as.toList := + Array.perm_iff_toList_perm.1 (Array.qsort_perm normLt 0 (as.size - 1) as) + +/-- Entries with equal level base come out of `qsort normLt` ordered by offset: a +consequence of sortedness (`Array.qsort_sorted`), since `normLt` compares levels with +equal bases by offset. -/ +theorem pairwise_qsort (as : Array Level) : + (as.qsort normLt).toList.Pairwise fun a b => + a.getLevelOffset = b.getLevelOffset → a.getOffset ≤ b.getOffset := by + rw [List.pairwise_iff_getElem] + intro i j hi hj hij hb + simp only [Array.getElem_toList] at hb + simpa [normLt_same_base hb.symm] using + Array.qsort_sorted normLt normLt_asymm normLt_le_trans as i j hij hj + +theorem offset_le_eval : l.getOffset ≤ eval ρ μ l := by + rw [eval_getLevelOffset]; omega + +theorem eval_of_isZero (h : l.getLevelOffset.isZero) : eval ρ μ l = l.getOffset := by + rw [eval_getLevelOffset, isZero_iff.1 h]; simp [eval] + +theorem skipExplicit_spec {lvls : Array Level} : i ≤ lvls.size → + i ≤ Total.skipExplicit lvls i ∧ Total.skipExplicit lvls i ≤ lvls.size ∧ + ∀ j (_ : j < lvls.size), i ≤ j → j < Total.skipExplicit lvls i → + lvls[j].getLevelOffset.isZero := by + fun_induction Total.skipExplicit lvls i with + | case1 i hi hz ih => + intro h + obtain ⟨ih1, ih2, ih3⟩ := ih (by omega) + refine ⟨by omega, ih2, fun j hj hij hlt => ?_⟩ + rcases Nat.eq_or_lt_of_le hij with rfl | hij' + · exact hz + · exact ih3 j hj (by omega) hlt + | case2 i hi hz => exact fun h => ⟨Nat.le_refl _, by omega, fun j hj hij hlt => by omega⟩ + | case3 i hi => exact fun h => ⟨Nat.le_refl _, by omega, fun j hj hij hlt => by omega⟩ + +theorem isExplicitSubsumedAux_spec {lvls : Array Level} : + Total.isExplicitSubsumedAux lvls mx i = true ↔ + ∃ j, i ≤ j ∧ ∃ (_ : j < lvls.size), mx ≤ lvls[j].getOffset := by + fun_induction Total.isExplicitSubsumedAux lvls mx i with + | case1 i hi hge => simpa using ⟨i, Nat.le_refl _, hi, by omega⟩ + | case2 i hi hlt ih => + rw [ih] + constructor <;> rintro ⟨j, hij, hj, hle⟩ <;> refine ⟨j, ?_, hj, hle⟩ + · omega + · obtain rfl | h := Nat.eq_or_lt_of_le hij <;> omega + | case3 i hi => simp; rintro j hij hj; omega + +theorem eval_mkMaxAux {lvls : Array Level} + (hs : ∀ (i j : Nat) (hi : i < lvls.size) (hj : j < lvls.size), i < j → + lvls[i].getLevelOffset = lvls[j].getLevelOffset → lvls[i].getOffset ≤ lvls[j].getOffset) + (hfuel : lvls.size ≤ i + fuel) + (hi0 : 0 < i) (hile : i ≤ lvls.size) + (hp : ∀ h : i - 1 < lvls.size, prev = lvls[i-1].getLevelOffset ∧ prevK = lvls[i-1].getOffset) : + eval ρ μ (Total.mkMaxAux lvls extraK i prev prevK result) = + Nat.max (eval ρ μ result) (evalList ρ μ (lvls.toList.drop (i-1)) + extraK) := by + induction fuel generalizing i result prev prevK with + | zero => + have hie : i = lvls.size := by omega + obtain ⟨hp, hpk⟩ := hp (by omega) + rw [Total.mkMaxAux.eq_def, dif_neg (by omega), eval_accMax] + have hlast : i - 1 < lvls.size := by omega + have hdrop : lvls.toList.drop (i-1) = [lvls[i-1]] := by + rw [List.drop_eq_getElem_cons (by simp only [Array.length_toList]; omega)] + simp [hie]; omega + have : eval ρ μ lvls[i-1] = eval ρ μ prev + prevK := by + rw [eval_getLevelOffset (l := lvls[i-1]), hp, hpk] + rw [hdrop] + simp only [evalList, List.foldr_cons, List.foldr_nil, this, Nat.max_eq_max] + omega + | succ fuel ih => + rw [Total.mkMaxAux.eq_def] + split <;> rename_i hlt + · obtain ⟨hp, hpk⟩ := hp (by omega) + have hlast : i - 1 < lvls.size := by omega + have hdrop : lvls.toList.drop (i-1) = lvls[i-1] :: lvls.toList.drop i := by + rw [List.drop_eq_getElem_cons (by simp only [Array.length_toList]; omega)] + simp; congr 1; omega + have heval : eval ρ μ lvls[i-1] = eval ρ μ prev + prevK := by + rw [eval_getLevelOffset (l := lvls[i-1]), hp, hpk] + have hmem : eval ρ μ lvls[i] ≤ evalList ρ μ (lvls.toList.drop i) := + le_evalList (by rw [List.drop_eq_getElem_cons (by simp only [Array.length_toList]; omega)]; exact .head _) + dsimp only + split <;> rename_i hbeq + · -- equal bases: drop the previous entry + rw [ih (i := i+1) (by omega) (by omega) (by omega) (fun h => by simp)] + have hb : lvls[i].getLevelOffset = prev := eq_of_beq hbeq + have hk : prevK ≤ lvls[i].getOffset := by + rw [hpk]; exact hs (i-1) i hlast hlt (by omega) (by rw [hb, hp]) + have hle : eval ρ μ lvls[i-1] ≤ evalList ρ μ (lvls.toList.drop i) := by + refine Nat.le_trans ?_ hmem + rw [heval, eval_getLevelOffset (l := lvls[i]), hb, hp] + omega + rw [Nat.add_sub_cancel, hdrop] + have : evalList ρ μ (lvls[i-1] :: lvls.toList.drop i) = + evalList ρ μ (lvls.toList.drop i) := by + exact Nat.max_eq_right hle + rw [this] + · -- new base: accumulate the previous entry + rw [ih (i := i+1) (by omega) (by omega) (by omega) (fun h => by simp)] + rw [Nat.add_sub_cancel, eval_accMax, hdrop] + simp only [evalList, List.foldr_cons, Nat.max_eq_max, heval] + omega + · have hie : i = lvls.size := by omega + obtain ⟨hp, hpk⟩ := hp (by omega) + rw [eval_accMax] + have hlast : i - 1 < lvls.size := by omega + have hdrop : lvls.toList.drop (i-1) = [lvls[i-1]] := by + rw [List.drop_eq_getElem_cons (by simp only [Array.length_toList]; omega)] + simp [hie]; omega + have : eval ρ μ lvls[i-1] = eval ρ μ prev + prevK := by + rw [eval_getLevelOffset (l := lvls[i-1]), hp, hpk] + rw [hdrop] + simp only [evalList, List.foldr_cons, List.foldr_nil, this, Nat.max_eq_max] + omega + +theorem size_lt_getMaxArgsAux_true : + lvls.size < (Total.getMaxArgsAux l true lvls).size := by + induction l generalizing lvls with + | max _ _ ih₁ ih₂ => exact Total.getMaxArgsAux.eq_def .. ▸ Nat.lt_trans ih₁ ih₂ + | _ => rw [Total.getMaxArgsAux.eq_def]; simp + +theorem size_lt_getMaxArgsAux_false : + lvls.size < (Total.getMaxArgsAux l false lvls).size := by + induction l generalizing lvls with + | max _ _ ih₁ ih₂ => exact Total.getMaxArgsAux.eq_def .. ▸ Nat.lt_trans ih₁ ih₂ + | _ => exact Total.getMaxArgsAux.eq_def .. ▸ size_lt_getMaxArgsAux_true .. + +theorem evalList_getMaxArgsAux_true : + evalList ρ μ (Total.getMaxArgsAux l true lvls).toList = + Nat.max (evalList ρ μ lvls.toList) (eval ρ μ l) := by + induction l generalizing lvls with + | max l₁ l₂ ih₁ ih₂ => + rw [Total.getMaxArgsAux, ih₂, ih₁] + simp only [eval, Nat.max_eq_max]; omega + | _ => + rw [Total.getMaxArgsAux.eq_def, Array.toList_push, evalList_append] + simp only [evalList, List.foldr_cons, List.foldr_nil, Nat.max_eq_max]; omega + +theorem evalList_getMaxArgsAux_false {l : Level} + (IH : ∀ u, Total.size u ≤ Total.size l → eval ρ μ (Total.normalize u) = eval ρ μ u) : + evalList ρ μ (Total.getMaxArgsAux l false lvls).toList = + Nat.max (evalList ρ μ lvls.toList) (eval ρ μ l) := by + induction l generalizing lvls with + | max l₁ l₂ ih₁ ih₂ => + rw [Total.getMaxArgsAux.eq_def] + show evalList ρ μ (Total.getMaxArgsAux l₂ false (Total.getMaxArgsAux l₁ false lvls)).toList = _ + rw [ih₂ (fun u hu => IH u (by simp only [Total.size] at *; omega)), + ih₁ (fun u hu => IH u (by simp only [Total.size] at *; omega))] + simp only [eval, Nat.max_eq_max]; omega + | _ => rw [Total.getMaxArgsAux.eq_def, evalList_getMaxArgsAux_true, IH _ (Nat.le_refl _)] + +/-- Dropping a dominated prefix does not change the maximum. -/ +theorem evalList_drop_eq {ls : List Level} (hstart : start ≤ ls.length) + (hdom : ∀ j (hj : j < ls.length), j < start → + eval ρ μ ls[j] ≤ evalList ρ μ (ls.drop start)) : + evalList ρ μ (ls.drop start) = evalList ρ μ ls := by + refine nat_ext_le fun n => ?_ + simp only [evalList_le_iff] + constructor <;> intro H l hl + · obtain ⟨j, hj, rfl⟩ := List.mem_iff_getElem.1 hl + by_cases hjs : j < start + · exact Nat.le_trans (hdom j hj hjs) (evalList_le_iff.2 H) + · refine H _ (List.mem_iff_getElem.2 ⟨j - start, by simp; omega, ?_⟩) + rw [List.getElem_drop]; congr 1; omega + · exact H l (List.drop_subset _ _ hl) + +/-- The level base of a level that is not already normalized is a `max` or an `imax`. -/ +theorem base_of_not_cheap {l : Level} (h : ¬l.isAlreadyNormalizedCheap = true) : + (∃ a b, l.getLevelOffset = .max a b) ∨ (∃ a b, l.getLevelOffset = .imax a b) := by + induction l with + | succ l ih => apply ih; simpa [isAlreadyNormalizedCheap] using h + | max l₁ l₂ => exact .inl ⟨_, _, rfl⟩ + | imax l₁ l₂ => exact .inr ⟨_, _, rfl⟩ + | _ => simp [isAlreadyNormalizedCheap] at h + +theorem eval_normalize_total {l : Level} : eval ρ μ (Total.normalize l) = eval ρ μ l := by + generalize hn : Total.size l = n + induction n using Nat.strongRecOn generalizing l with | _ n IH + subst hn + rw [Total.normalize.eq_def] + split <;> [rfl; rename_i hcheap] + have hsz := Total.size_getLevelOffset l + split <;> [rename_i l₁ l₂ hbase; rename_i l₁ l₂ hbase; skip] + · -- max + rw [eval_getLevelOffset (l := l), hbase] + rw [hbase] at hsz; simp only [Total.size] at hsz + have hs₁ := Total.one_le_size l₁ + have hs₂ := Total.one_le_size l₂ + have IH₁ u (hu : Total.size u ≤ Total.size l₁) : eval ρ μ (Total.normalize u) = eval ρ μ u := + IH _ (by omega) rfl + have IH₂ u (hu : Total.size u ≤ Total.size l₂) : eval ρ μ (Total.normalize u) = eval ρ μ u := + IH _ (by omega) rfl + extract_lets k lvls₁ L1 L i₀ i lvl₁ prev prevK + have hevalL1 : evalList ρ μ L1.toList = Nat.max (eval ρ μ l₁) (eval ρ μ l₂) := by + rw [evalList_getMaxArgsAux_false IH₂, evalList_getMaxArgsAux_false IH₁] + simp [evalList, Nat.max_eq_max] + have hL1pos : 0 < L1.size := + Nat.lt_trans (size_lt_getMaxArgsAux_false (lvls := #[])) size_lt_getMaxArgsAux_false + have hperm : L.toList.Perm L1.toList := qsort_perm L1 + have hpair : List.Pairwise _ L.toList := pairwise_qsort L1 + have hLsize : L.size = L1.size := by + simpa [Array.length_toList] using hperm.length_eq + have hLpos : 0 < L.size := hLsize ▸ hL1pos + have hevalL : evalList ρ μ L.toList = Nat.max (eval ρ μ l₁) (eval ρ μ l₂) := by + rw [evalList_perm hperm, hevalL1] + have hs : ∀ (i j : Nat) (hi : i < L.size) (hj : j < L.size), i < j → + L[i].getLevelOffset = L[j].getLevelOffset → L[i].getOffset ≤ L[j].getOffset := by + intro i j hi hj hij hb + have := (List.pairwise_iff_getElem.1 hpair) i j (by simpa using hi) (by simpa using hj) hij + simpa using this (by simpa using hb) + obtain ⟨-, hskle, hskz⟩ := skipExplicit_spec (lvls := L) (i := 0) (Nat.zero_le _) + -- the start index and its bound + have main i (hi : i < L.size) + (hdom : ∀ j (hj : j < L.size), j < i → eval ρ μ L[j] ≤ evalList ρ μ (L.toList.drop i)) : + eval ρ μ (Total.mkMaxAux L (l.getOffset) (i+1) L[i]!.getLevelOffset L[i]!.getOffset + Level.zero) = Nat.max (eval ρ μ l₁) (eval ρ μ l₂) + l.getOffset := by + rw [getElem!_pos L i hi, + eval_mkMaxAux hs (fuel := L.size) (by omega) (by omega) (by omega) (fun _ => by simp), + Nat.add_sub_cancel, evalList_drop_eq (by rw [Array.length_toList]; omega) hdom, hevalL] + simp [eval, Nat.max_eq_max] + subst i lvl₁ prevK prev; split <;> rename_i hsub + · -- explicits subsumed: start at the first non-explicit + rw [isExplicitSubsumed] at hsub + split at hsub <;> [cases hsub; let (eq := eq) i'+1 := i₀]; subst i₀ + simp only [isExplicitSubsumedAux_eq, isExplicitSubsumedAux_spec] at hsub + obtain ⟨j, hij, hjs, hmax⟩ := hsub; dsimp at hmax + refine main (i'+1) (by omega) fun m hm hmi => ?_ + -- every dropped explicit is at most the witness entry + have hzm : L[m].getLevelOffset.isZero := hskz m hm (Nat.zero_le _) (eq ▸ hmi) + have hz₁ := hskz i' (by omega) (Nat.zero_le _) (by omega) + have h2 : L[m].getOffset ≤ L[i'].getOffset := by + rcases Nat.eq_or_lt_of_le (Nat.le_pred_of_lt hmi) with h | h + · subst h; exact Nat.le_refl _ + · exact hs m i' hm (by omega) h (by rw [isZero_iff.1 hzm, isZero_iff.1 hz₁]) + have h3 : L[i'].getOffset ≤ eval ρ μ L[j] := + Nat.le_trans (getElem!_pos L i' (by omega) ▸ hmax) offset_le_eval + refine eval_of_isZero hzm ▸ Nat.le_trans (Nat.le_trans h2 h3) (le_evalList ?_) + refine List.mem_iff_getElem.2 ⟨j - (i' + 1), ?_, ?_⟩ + · simp only [List.length_drop, Array.length_toList]; omega + · rw [List.getElem_drop]; simp only [Array.getElem_toList]; congr 1; omega + · -- keep the largest explicit + cases eq : i₀ with | zero => exact main 0 (by omega) (fun m hm hmi => by omega) | succ i' + have hstart : i' < L.size := by omega + refine main i' hstart fun m hm hmi => ?_ + have hzm : L[m].getLevelOffset.isZero := hskz m hm (Nat.zero_le _) (by omega) + have hz₁ : L[i'].getLevelOffset.isZero := + hskz i' hstart (Nat.zero_le _) (by omega) + refine eval_of_isZero hzm ▸ Nat.le_trans (hs m i' hm hstart (by omega) ?_) ?_ + · rw [isZero_iff.1 hzm, isZero_iff.1 hz₁] + refine eval_of_isZero hz₁ ▸ le_evalList ?_ + rw [List.drop_eq_getElem_cons (by omega)]; exact .head _ + · -- imax + rw [eval_getLevelOffset (l := l), hbase] + rw [hbase] at hsz; simp only [Total.size] at hsz + have hs₁ := Total.one_le_size l₁ + have hs₂ := Total.one_le_size l₂ + split <;> rename_i hnz + · rw [eval_addOffset, IH (Total.size (mkLevelMax l₁ l₂)) + (by simp only [mkLevelMax, Total.size]; omega) rfl] + have := isNeverZero_sound (ρ := ρ) (μ := μ) hnz + simp only [mkLevelMax, eval, Nat.imax] + rw [if_neg (by omega)] + · rw [eval_addOffset, eval_mkIMaxAux, IH _ (by omega) rfl, IH _ (by omega) rfl]; rfl + · grind [base_of_not_cheap] + +theorem eval_normalize {ρ μ l} : eval ρ μ l.normalize = eval ρ μ l := by + rw [normalize_eq]; exact eval_normalize_total + +theorem geq_eq_core : geq u v = geqCore (normalize u) (normalize v) := by + simp [geq, geqCore_eq_go] + +theorem isEquiv_sound (h : isEquiv u v) : eval ρ μ u = eval ρ μ v := by + simp only [Level.isEquiv, Bool.or_eq_true, beq_iff_eq] at h + rcases h with rfl | h <;> [rfl; skip] + rw [← eval_normalize (l := u), ← eval_normalize (l := v), h] + +theorem geq_sound (h : geq u v) : eval ρ μ v ≤ eval ρ μ u := by + rw [geq_eq_core] at h + rw [← eval_normalize (l := u), ← eval_normalize (l := v)] + exact geqCore_sound h + +theorem eval_ofLevel (h : VLevel.ofLevel Us l = some l') : + l'.eval ns = eval (fun n => ns.getD (Us.idxOf n) 0) μ l := by + induction l generalizing l' with + | zero => simp [VLevel.ofLevel] at h; cases h; rfl + | succ l ih => + simp [VLevel.ofLevel, bind] at h + obtain ⟨l', hl, rfl⟩ := h + simp [VLevel.eval, eval, ih hl] + | max l₁ l₂ ih₁ ih₂ | imax l₁ l₂ ih₁ ih₂ => + simp [VLevel.ofLevel, bind] at h + obtain ⟨l₁', hl₁, l₂', hl₂, rfl⟩ := h + simp only [VLevel.eval, eval, ih₁ hl₁, ih₂ hl₂] + all_goals rfl + | param n => + simp [VLevel.ofLevel] at h + obtain ⟨hidx, rfl⟩ := h + simp [VLevel.eval, eval] + | mvar n => simp [VLevel.ofLevel] at h + +theorem isEquiv_wf (h : isEquiv u v) + (hu : VLevel.ofLevel Us u = some u') (hv : VLevel.ofLevel Us v = some v') : u' ≈ v' := by + refine VLevel.equiv_def.2 fun ns => ?_ + rw [eval_ofLevel (μ := fun _ => 0) hu, eval_ofLevel (μ := fun _ => 0) hv] + exact isEquiv_sound h + +theorem geq_wf (h : geq u v) + (hu : VLevel.ofLevel Us u = some u') (hv : VLevel.ofLevel Us v = some v') : v' ≤ u' := by + intro ns + rw [eval_ofLevel (μ := fun _ => 0) hv, eval_ofLevel (μ := fun _ => 0) hu] + exact geq_sound h + +end Lean.Level diff --git a/Ix/Theory/Named/Verify/LocalContext.lean b/Ix/Theory/Named/Verify/LocalContext.lean new file mode 100644 index 000000000..3abc5fa70 --- /dev/null +++ b/Ix/Theory/Named/Verify/LocalContext.lean @@ -0,0 +1,337 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.PersistentHashMap +import Ix.Theory.Named.Verify.Expr +import Ix.Theory.Named.Verify.Typing.Expr +import Ix.Theory.Named.Verify.Typing.Lemmas + +open Ix.Theory.Named + +namespace Lean.LocalContext + +noncomputable def toList (lctx : LocalContext) : List LocalDecl := + lctx.decls.toList'.reverse.filterMap id + +noncomputable def fvars (lctx : LocalContext) : List FVarId := + lctx.toList.map (·.fvarId) + +def mkBindingList1 (isLambda : Bool) (lctx : LocalContext) + (xs : List FVarId) (x : FVarId) (b : Expr) : Expr := + match lctx.find? x with + | some (.cdecl _ _ n ty bi _) => + let ty := ty.abstractList xs + if isLambda then + .lam n ty b bi + else + .forallE n ty b bi + | some (.ldecl _ _ n ty val nonDep _) => + if b.hasLooseBVar' 0 then + let ty := ty.abstractList xs + let val := val.abstractList xs + .letE n ty val b nonDep + else + b.lowerLooseBVars' 1 1 + | none => panic! "unknown free variable" + +def mkBindingList (isLambda : Bool) (lctx : LocalContext) (xs : List FVarId) (b : Expr) : Expr := + core (b.abstractList xs) +where + core := go xs.reverse + go : List FVarId → Expr → Expr + | [], b => b + | x :: xs, b => go xs (mkBindingList1 isLambda lctx xs.reverse x b) + +theorem mkBinding_eq : + mkBinding isLambda lctx ⟨xs.map .fvar⟩ b = mkBindingList isLambda lctx xs b := by + simp only [mkBinding, List.getElem_toArray, Expr.abstractRange_eq, Expr.hasLooseBVar_eq, + Expr.abstract_eq, ← Array.take_eq_extract, List.take_toArray, Bool.and_false, + ← List.map_take, List.getElem_map, Expr.lowerLooseBVars_eq] + dsimp only [Array.size] + simp only [List.getElem_eq_getElem?_get, Option.get_eq_getD (fallback := default)] + change Nat.foldRev _ (fun i x => + mkBindingList1 isLambda lctx (xs.take i) (xs[i]?.getD default)) .. = mkBindingList.go .. + rw [List.length_map]; generalize eq : xs.length = n + generalize b.abstractList xs = b + induction n generalizing xs b with + | zero => let [] := xs; simp [mkBindingList.go] + | succ n ih => + obtain rfl | ⟨xs, a, rfl⟩ := List.eq_nil_or_concat xs; · cases eq + simp at eq ⊢; subst eq + simp +contextual only [Nat.le_of_lt, List.take_append_of_le_length, + List.getElem?_append_left, mkBindingList.go, ih]; simp + +theorem mkBindingList1_abstract {xs : List FVarId} + (hx : lctx.find? x = some decl) (nd : (a :: xs).Nodup) : + (mkBindingList1 isLambda lctx xs x b).abstract1 a xs.length = + mkBindingList1 isLambda lctx (a :: xs) x (b.abstract1 a (xs.length + 1)) := by + have (e:_) := Nat.zero_add _ ▸ Expr.abstract1_abstractList' (k := 0) (e := e) nd + simp [mkBindingList1, hx]; cases decl with simp + | cdecl _ _ _ ty => split <;> simp [Expr.abstract1, Expr.abstract1, this] + | ldecl => + have := Expr.abstract1_hasLooseBVar a b (xs.length + 1) 0 + simp at this; simp [this]; clear this + split + · simp [Expr.abstract1, Expr.abstract1, this] + · rename_i h; simp at h + rw [Expr.abstract1_lower h (Nat.zero_le _)] + +theorem mkBindingList_core_cons {xs : List FVarId} {b : Expr} + (hx : ∀ x ∈ xs, ∃ decl, lctx.find? x = some decl) (nd : (a :: xs).Nodup) : + mkBindingList.core isLambda lctx (a :: xs) (b.abstract1 a xs.length) = + mkBindingList1 isLambda lctx [] a + ((mkBindingList.core isLambda lctx xs b).abstract1 a) := by + obtain ⟨xs, rfl⟩ : ∃ xs', List.reverse xs' = xs := ⟨_, List.reverse_reverse _⟩ + simp [mkBindingList.core] at * + induction xs generalizing b with + | nil => simp [mkBindingList.go] + | cons c xs ih => + simp at hx nd ih + let ⟨decl, eq⟩ := hx.1 + simp [mkBindingList.go] + rw [← xs.length_reverse, ← mkBindingList1_abstract eq (by simp [*])] + simp [ih hx.2 nd.1.2 nd.2.2] + +@[simp] theorem mkBindingList_nil : mkBindingList isLambda lctx [] b = b := rfl + +theorem mkBindingList_cons + (hx : ∀ x ∈ xs, ∃ decl, lctx.find? x = some decl) (nd : (a :: xs).Nodup) : + mkBindingList isLambda lctx (a :: xs) b = + mkBindingList1 isLambda lctx [] a ((mkBindingList isLambda lctx xs b).abstract1 a) := by + simp [mkBindingList] + rw [← Expr.abstract1_abstractList' nd] + rw [Nat.zero_add, mkBindingList_core_cons hx nd] + +theorem mkBindingList_eq_fold + (hx : ∀ x ∈ xs, ∃ decl, lctx.find? x = some decl) (nd : xs.Nodup) : + mkBindingList isLambda lctx xs b = + xs.foldr (fun a e => mkBindingList1 isLambda lctx [] a (e.abstract1 a)) b := by + induction xs <;> simp_all [mkBindingList_cons] + +theorem mkBindingList1_congr (H : lctx₁.find? x = lctx₂.find? x) : + mkBindingList1 isLambda lctx₁ xs x b = mkBindingList1 isLambda lctx₂ xs x b := by + simp [mkBindingList1, H] + +theorem mkBindingList_congr + (H : ∀ x ∈ xs, lctx₁.find? x = lctx₂.find? x) : + mkBindingList isLambda lctx₁ xs b = mkBindingList isLambda lctx₂ xs b := by + obtain ⟨xs, rfl⟩ : ∃ xs', List.reverse xs' = xs := ⟨_, List.reverse_reverse _⟩ + simp [mkBindingList, mkBindingList.core] at * + generalize b.abstractList _ = b + induction xs generalizing b <;> simp_all [mkBindingList.go] + simp [mkBindingList1_congr H.1] + +inductive WF : LocalContext → Prop + | nil : WF ⟨.empty, .empty, .empty⟩ + | cons : + d.fvarId = fv → map.find? fv = none → d.index = arr.size → + WF ⟨map, arr, fvmap⟩ → + WF ⟨map.insert fv d, arr.push d, fvmap⟩ + +theorem WF.map_wf {lctx : LocalContext} : lctx.WF → lctx.fvarIdToDecl.WF + | .nil => .empty + | .cons _ _ _ h2 => .insert h2.map_wf + +theorem WF.decls_wf {lctx : LocalContext} : lctx.WF → lctx.decls.WF + | .nil => .empty + | .cons _ _ _ h2 => .push h2.decls_wf + +attribute [-simp] List.filterMap_reverse in +open scoped _root_.List in +theorem WF.map_toList : WF lctx → + lctx.fvarIdToDecl.toList' ~ lctx.toList.map fun d => (d.fvarId, d) + | .nil => by simp [LocalContext.toList] + | .cons h1 h2 _ h4 => by + subst h1; simp [LocalContext.toList] + refine h4.map_wf.toList'_insert _ _ |>.trans (.cons _ ?_) + rw [List.filter_eq_self.2]; · exact h4.map_toList + simp; rintro _ b h rfl + have := (h4.map_wf.find?_eq _).symm.trans h2 + simp [List.lookup_eq_none_iff] at this + exact this _ _ h rfl + +theorem WF.find?_eq_find?_toList (H : WF lctx) : + lctx.find? fv = lctx.toList.find? (fv == ·.fvarId) := by + rw [LocalContext.find?, H.map_wf.find?_eq, + H.map_toList.lookup_eq H.map_wf.nodupKeys, List.map_fst_lookup] + +theorem WF.nodup : WF lctx → (lctx.toList.map (·.fvarId)).Nodup + | .nil => .nil + | .cons h1 h2 h3 h4 => by + have := h4.nodup + have := h4.find?_eq_find?_toList.symm.trans h2 + simp_all [toList] + simpa [eq_comm] using this + +protected theorem WF.mkLocalDecl + (h1 : WF lctx) (h2 : lctx.find? fv = none) : WF (lctx.mkLocalDecl fv name ty bi kind) := + .cons rfl h2 rfl h1 + +protected theorem WF.mkLetDecl + (h1 : WF lctx) (h2 : lctx.find? fv = none) : WF (lctx.mkLetDecl fv name ty val bi kind) := + .cons rfl h2 rfl h1 + +@[simp] theorem mkLocalDecl_toList {lctx : LocalContext} : + (lctx.mkLocalDecl fv name ty bi kind).toList = + .cdecl lctx.decls.size fv name ty bi kind :: lctx.toList := by + simp [mkLocalDecl, toList] + +@[simp] theorem mkLetDecl_toList {lctx : LocalContext} : + (lctx.mkLetDecl fv name ty val bi kind).toList = + .ldecl lctx.decls.size fv name ty val bi kind :: lctx.toList := by + simp [mkLetDecl, toList] + +end Lean.LocalContext + +namespace Ix.Theory.Named + +open Lean +open scoped _root_.List + +attribute [-simp] List.filterMap_reverse + +variable (env : VEnv) (Us : List Name) (Δ : VLCtx) in +inductive TrLocalDecl : LocalDecl → VLocalDecl → Prop + | vlam : TrExprS env Us Δ ty ty' → env.IsType Us.length Δ.toCtx ty' → + TrLocalDecl (.cdecl n fv name ty bi kind) (.vlam ty') + | vlet : + TrExprS env Us Δ ty ty' → TrExprS env Us Δ val val' → + env.HasType Us.length Δ.toCtx val' ty' → + TrLocalDecl (.ldecl n fv name ty val bi kind) (.vlet ty' val') + +theorem TrLocalDecl.wf : TrLocalDecl env Us Δ d d' → d'.WF env Us.length Δ.toCtx + | .vlam _ h | .vlet _ _ h => h + +def _root_.Lean.LocalDecl.deps : LocalDecl → List FVarId + | .cdecl (type := t) .. => t.fvarsList + | .ldecl (type := t) (value := v) .. => t.fvarsList ++ v.fvarsList + +theorem TrLocalDecl.deps_wf : TrLocalDecl env Us Δ d d' → d.deps ⊆ Δ.fvars + | .vlam h _ => h.fvarsList + | .vlet h1 h2 _ => by simp [LocalDecl.deps, h1.fvarsList, h2.fvarsList] + +variable (env : VEnv) (Us : List Name) in +inductive TrLCtx' : List LocalDecl → VLCtx → Prop + | nil : TrLCtx' [] [] + | cons : + TrLCtx' ds Δ → TrLocalDecl env Us Δ d d' → + TrLCtx' (d :: ds) ((some (d.fvarId, d.deps), d') :: Δ) + +def TrLCtx (env : VEnv) (Us : List Name) (lctx : LocalContext) (Δ : VLCtx) : Prop := + lctx.WF ∧ TrLCtx' env Us lctx.toList Δ + +theorem TrLCtx.nil {env : VEnv} {Us : List Name} : TrLCtx env Us {} [] := ⟨.nil, .nil⟩ + +theorem TrLCtx'.noBV : TrLCtx' env Us ds Δ → Δ.NoBV + | .nil => rfl + | .cons h _ => h.noBV + +theorem TrLCtx'.forall₂ : + TrLCtx' env Us ds Δ → ds.Forall₂ Δ (R := fun d d' => d'.1 = some (d.fvarId, d.deps)) + | .nil => by simp + | .cons h _ => by simp; exact h.forall₂ + +theorem TrLCtx'.fvars_eq (H : TrLCtx' env Us ds Δ) : ds.map (·.fvarId) = Δ.fvars := by + simp [VLCtx.fvars] + induction H with + | nil => rfl + | cons h1 _ ih => simp [← ih] + +theorem TrLCtx.fvars_eq (H : TrLCtx env Us lctx Δ) : lctx.fvars = Δ.fvars := + H.2.fvars_eq + +theorem TrLCtx'.find?_eq_some (H : TrLCtx' env Us ds Δ) : + (∃ d, ds.find? (fv == ·.fvarId) = some d) ↔ fv ∈ Δ.fvars := by + rw [← Option.isSome_iff_exists, List.find?_isSome] + induction H with simp + | @cons _ _ d d' _ _ ih => simp [← ih] + +theorem TrLCtx'.find?_isSome (H : TrLCtx' env Us ds Δ) : + (ds.find? (fv == ·.fvarId)).isSome = (Δ.find? (.inr fv)).isSome := by + rw [Bool.eq_iff_iff, Option.isSome_iff_exists, Option.isSome_iff_exists, + H.find?_eq_some, VLCtx.find?_eq_some] + +theorem TrLCtx.find?_isSome (H : TrLCtx env Us lctx Δ) : + (lctx.find? fv).isSome = (Δ.find? (.inr fv)).isSome := by + rw [H.1.find?_eq_find?_toList, H.2.find?_isSome] + +theorem TrLCtx.find?_eq_some (H : TrLCtx env Us lctx Δ) : + (∃ d, lctx.find? fv = some d) ↔ fv ∈ Δ.fvars := by + rw [H.1.find?_eq_find?_toList, H.2.find?_eq_some] + +theorem TrLCtx.find?_eq_none (H : TrLCtx env Us lctx Δ) : + lctx.find? fv = none ↔ ¬fv ∈ Δ.fvars := by simp [← H.find?_eq_some] + +theorem TrLCtx.contains (H : TrLCtx env Us lctx Δ) : lctx.contains fv ↔ fv ∈ Δ.fvars := by + rw [LocalContext.contains, PersistentHashMap.find?_isSome, Option.isSome_iff_exists] + exact H.find?_eq_some + +theorem TrLCtx'.wf : TrLCtx' env Us ds Δ → (ds.map (·.fvarId)).Nodup → Δ.WF env Us.length + | .nil, _ => ⟨⟩ + | .cons h1 h2, .cons H1 H2 => by + refine ⟨h1.wf H2, fun _ _ => ?_, h2.wf⟩ + rintro ⟨⟩; exact ⟨by simpa [← h1.find?_eq_some] using H1, h2.deps_wf⟩ + +theorem TrLCtx.wf (H : TrLCtx env Us lctx Δ) : Δ.WF env Us.length := H.2.wf H.1.nodup + +def _root_.Lean.LocalDecl.value' : LocalDecl → Expr + | .ldecl (value := v) .. => v + | .cdecl (fvarId := fv) .. => .fvar fv + +theorem TrLCtx'.find?_of_mem (henv : env.WF) (H : TrLCtx' env Us ds Δ) + (nd : (ds.map (·.fvarId)).Nodup) (hm : decl ∈ ds) : + ∃ e A, Δ.find? (.inr decl.fvarId) = some (e, A) ∧ + FVarsBelow Δ (.fvar decl.fvarId) decl.value' ∧ FVarsBelow Δ (.fvar decl.fvarId) decl.type ∧ + TrExprS env Us Δ decl.value' e ∧ TrExprS env Us Δ decl.type A := by + have := H.wf nd + match H with + | .nil => cases hm + | .cons (ds := ds) h1 h2 => + simp [VLCtx.find?, VLCtx.next] + obtain _ | ⟨_, hm : decl ∈ ds⟩ := hm + · simp [and_assoc] + cases h2 with + | vlam h2 h3 => + refine ⟨.rfl, ?_, .fvar <| by simp [VLCtx.find?, VLCtx.next, LocalDecl.fvarId]; rfl, ?_⟩ + · intro P hP he; exact fvarsIn_iff.2 ⟨hP.2 he, h2.fvarsIn.mono fun _ _ => ⟨⟩⟩ + · exact h2.weakFV henv (.skip_fvar _ _ .refl) this + | vlet h2 h3 => + refine ⟨?_, ?_, ?_, ?_⟩ + · intro P hP he; have := hP.2 he; simp [LocalDecl.deps, or_imp, forall_and] at this + exact fvarsIn_iff.2 ⟨this.2, h3.fvarsIn.mono fun _ _ => ⟨⟩⟩ + · intro P hP he; have := hP.2 he; simp [LocalDecl.deps, or_imp, forall_and] at this + exact fvarsIn_iff.2 ⟨this.1, h2.fvarsIn.mono fun _ _ => ⟨⟩⟩ + · simpa [LocalDecl.value', VLocalDecl.value, VLocalDecl.depth] using + h3.weakFV henv (.skip_fvar _ _ .refl) this + · simpa [LocalDecl.type, VLocalDecl.type, VLocalDecl.depth] using + h2.weakFV henv (.skip_fvar _ _ .refl) this + · simp at nd; rw [if_neg (by simpa using Ne.symm (nd.1 _ hm))]; simp + have ⟨_, _, h1, h2, h3, h4, h5⟩ := h1.find?_of_mem henv nd.2 hm + refine ⟨_, _, ⟨_, _, h1, rfl, rfl⟩, fun _ h => h2 _ h.1, fun _ h => h3 _ h.1, ?_, ?_⟩ + · simpa using h4.weakFV henv (.skip_fvar _ _ .refl) this + · simpa using h5.weakFV henv (.skip_fvar _ _ .refl) this + +theorem TrLCtx.find?_of_mem (henv : env.WF) (H : TrLCtx env Us lctx Δ) + (hm : decl ∈ lctx.toList) : + ∃ e A, Δ.find? (.inr decl.fvarId) = some (e, A) ∧ + FVarsBelow Δ (.fvar decl.fvarId) decl.value' ∧ FVarsBelow Δ (.fvar decl.fvarId) decl.type ∧ + TrExprS env Us Δ decl.value' e ∧ TrExprS env Us Δ decl.type A := + H.2.find?_of_mem henv H.1.nodup hm + +theorem TrLCtx.mkLocalDecl + (h1 : TrLCtx env Us lctx Δ) (h2 : lctx.find? fv = none) (h3 : TrExprS env Us Δ ty ty') + (h4 : env.IsType Us.length Δ.toCtx ty') : + TrLCtx env Us (lctx.mkLocalDecl fv name ty bi kind) + ((some (fv, ty.fvarsList), .vlam ty') :: Δ) := + ⟨h1.1.mkLocalDecl h2, by simpa using .cons h1.2 (.vlam h3 h4)⟩ + +theorem TrLCtx.mkLetDecl + (h1 : TrLCtx env Us lctx Δ) (h2 : lctx.find? fv = none) + (h3 : TrExprS env Us Δ ty ty') (h4 : TrExprS env Us Δ val val') + (h5 : env.HasType Us.length Δ.toCtx val' ty') : + TrLCtx env Us (lctx.mkLetDecl fv name ty val bi kind) + ((some (fv, ty.fvarsList ++ val.fvarsList), .vlet ty' val') :: Δ) := + ⟨h1.1.mkLetDecl h2, by simpa using .cons h1.2 (.vlet h3 h4 h5)⟩ diff --git a/Ix/Theory/Named/Verify/Name.lean b/Ix/Theory/Named/Verify/Name.lean new file mode 100644 index 000000000..650926aa9 --- /dev/null +++ b/Ix/Theory/Named/Verify/Name.lean @@ -0,0 +1,96 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Lean.Data.NameMap.Basic +import Ix.Theory.Named.Std.Ord +import Std.Data.TreeSet.Lemmas + +/-! +Order properties of `Lean.Name.cmp` and `Lean.Name.quickCmp`. +-/ + +namespace Lean + +namespace Name +open _root_.Std Ix.Theory.Named + +theorem cmp_eq_swap {a b : Name} : a.cmp b = (b.cmp a).swap := by + induction a generalizing b with obtain _|⟨b₁,b₂⟩|⟨b₁,b₂⟩ := b <;> simp [cmp] + | str a₁ a₂ ih | num a₁ a₂ ih => + rw [ih]; cases b₁.cmp a₁ <;> simp [← OrientedOrd.eq_swap] + +instance : TransCmp cmp := by + refine TransCmp.of_rot (fun _ _ => cmp_eq_swap) fun a b c => ?_ + induction a generalizing b c with + | anonymous => + obtain _|⟨b₁,b₂⟩|⟨b₁,b₂⟩ := b <;> obtain _|⟨c₁,c₂⟩|⟨c₁,c₂⟩ := c <;> simp [cmp, Rot] + | str a₁ a₂ ih | num a₁ a₂ ih => + obtain _|⟨b₁,b₂⟩|⟨b₁,b₂⟩ := b <;> obtain _|⟨c₁,c₂⟩|⟨c₁,c₂⟩ := c <;> + first + | exact (ih ..).then (Rot.of_transCmp ..) + | simp [cmp, Rot] + +instance : LawfulBEqCmp cmp where + compare_eq_iff_beq {a b} := by + simp; refine ⟨?_, fun h => h ▸ ReflCmp.compare_self⟩ + induction a generalizing b with obtain _|⟨b₁,b₂⟩|⟨b₁,b₂⟩ := b <;> simp [cmp] + | str a₁ a₂ ih | num a₁ a₂ ih => + refine ?_ ∘ Ordering.then_eq_eq.1 + simp +contextual; exact fun h _ => ih h + +instance : TransCmp quickCmp where + eq_swap {a b} := by + simp [quickCmp] + rw [OrientedOrd.eq_swap] + cases compare b.hash a.hash <;> simp + induction a generalizing b with obtain _|⟨b₁,b₂⟩|⟨b₁,b₂⟩ := b <;> simp [quickCmpAux] + | str a₁ a₂ ih | num a₁ a₂ ih => + rw [OrientedOrd.eq_swap] + cases compare b₂ a₂ <;> simp [ih] + isLE_trans {a b c} := by + have {α} [Ord α] [TransOrd α] {a₁ b₁ c₁ : α} {a₂ b₂ c₂} + (H : (quickCmpAux a₂ b₂).isLE → (quickCmpAux b₂ c₂).isLE → (quickCmpAux a₂ c₂).isLE) : + ((compare a₁ b₁).then (quickCmpAux a₂ b₂)).isLE → + ((compare b₁ c₁).then (quickCmpAux b₂ c₂)).isLE → + ((compare a₁ c₁).then (quickCmpAux a₂ c₂)).isLE := by + simp [Ordering.isLE_then_iff_and] + intro h1 h2 h3 h4 + refine ⟨TransCmp.isLE_trans h1 h3, ?_⟩ + refine h2.elim (fun h2 => .inl <| TransCmp.lt_of_lt_of_isLE h2 h3) fun h2 => ?_ + refine h4.elim (fun h4 => .inl <| TransCmp.lt_of_isLE_of_lt h1 h4) fun h4 => .inr (H h2 h4) + apply this + induction a generalizing b c with + obtain _|⟨b₁,b₂⟩|⟨b₁,b₂⟩ := b <;> simp [quickCmpAux] at * <;> + obtain _|⟨c₁,c₂⟩|⟨c₁,c₂⟩ := c <;> simp [quickCmpAux] at * + | str a₁ a₂ ih | num a₁ a₂ ih => apply this ih + +instance : LawfulBEqCmp quickCmp where + compare_eq_iff_beq {a b} := by + simp; refine ⟨fun h => ?_, fun h => h ▸ ReflCmp.compare_self⟩ + replace h := (Ordering.then_eq_eq.1 h).2; revert h + induction a generalizing b with obtain _|⟨b₁,b₂⟩|⟨b₁,b₂⟩ := b <;> simp [quickCmpAux] + | str a₁ a₂ ih | num a₁ a₂ ih => + refine ?_ ∘ Ordering.then_eq_eq.1 + simp +contextual; exact fun _ => ih + +end Name + +namespace NameSet +open _root_.Std + +theorem contains_insert {s : NameSet} {a b : Name} : + (s.insert a).contains b = (a == b || s.contains b) := by + have key : (Name.quickCmp a b == Ordering.eq) = (a == b) := by + have := @LawfulBEqCmp.compare_eq_iff_beq _ _ Name.quickCmp _ a b + cases h : Name.quickCmp a b <;> simp_all + have h : (s.insert a).contains b + = (Name.quickCmp a b == Ordering.eq || s.contains b) := + Std.TreeSet.contains_insert (t := s) (k := a) (a := b) + rw [h, key] + +@[simp] theorem contains_empty {a : Name} : (∅ : NameSet).contains a = false := rfl + +end NameSet diff --git a/Ix/Theory/Named/Verify/NameGenerator.lean b/Ix/Theory/Named/Verify/NameGenerator.lean new file mode 100644 index 000000000..5e96e8e48 --- /dev/null +++ b/Ix/Theory/Named/Verify/NameGenerator.lean @@ -0,0 +1,34 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Reference.Expr +import Ix.Theory.Named.VExpr + +namespace Lean.NameGenerator + +def Reserves (ngen : NameGenerator) (fv : FVarId) : Prop := + ∀ i, fv = ⟨.num ngen.namePrefix i⟩ → i < ngen.idx + +theorem next_reserves_self {ngen : NameGenerator} : + ngen.next.Reserves ⟨ngen.curr⟩ := by rintro _ ⟨⟩; exact Nat.lt_succ_self _ + +theorem not_reserves_self {ngen : NameGenerator} : + ¬ngen.Reserves ⟨ngen.curr⟩ := fun h => Nat.lt_irrefl _ (h _ rfl) + +protected inductive LE : NameGenerator → NameGenerator → Prop where + | le : i ≤ j → NameGenerator.LE ⟨pfx, i⟩ ⟨pfx, j⟩ + +instance : LE NameGenerator := ⟨NameGenerator.LE⟩ + +theorem LE.rfl {ngen : NameGenerator} : ngen ≤ ngen := ⟨Nat.le_refl _⟩ + +theorem LE.trans {ngen₁ ngen₂ ngen₃ : NameGenerator} : ngen₁ ≤ ngen₂ → ngen₂ ≤ ngen₃ → ngen₁ ≤ ngen₃ + | ⟨h₁⟩, ⟨h₂⟩ => ⟨Nat.le_trans h₁ h₂⟩ + +theorem Reserves.mono : ngen ≤ ngen' → Reserves ngen fv → Reserves ngen' fv + | ⟨h₁⟩ => fun H _ hi => Nat.lt_of_lt_of_le (H _ hi) h₁ + +theorem LE.next {ngen : NameGenerator} : ngen ≤ ngen.next := ⟨Nat.le_succ _⟩ diff --git a/Ix/Theory/Named/Verify/NormLt.lean b/Ix/Theory/Named/Verify/NormLt.lean new file mode 100644 index 000000000..dd097405a --- /dev/null +++ b/Ix/Theory/Named/Verify/NormLt.lean @@ -0,0 +1,370 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Lean.Level +import Ix.Theory.Named.Verify.Name +import Ix.Theory.Named.Std.Ord +import Ix.Theory.Named.Verify.Axioms + +/-! +`Lean.Level.normLt` is the order used to sort the arguments of a `max` in +`Lean.Level.normalize`. This file shows it is a strict weak order, which is what the +`Array.qsort` specification requires. + +The proof identifies `normLt` with an `Ordering`-valued comparison `normCmp`, which compares +levels by (base, offset) lexicographically, bases being compared structurally. `normCmp` is +then given the `Std` order instances (`ReflCmp`, `TransCmp`, `LawfulEqCmp`), from which the +strict weak order properties `normLt` needs follow. Transitivity uses `Ix.Theory.Named.Rot`, the +lexicographic-product device shared with `Name.cmp` in `Ix.Theory.Named.Verify.Name`. +-/ + +open Std Ix.Theory.Named + +namespace Lean.Level + +instance : LawfulBEq LMVarId where + eq_of_beq := @fun ⟨a⟩ ⟨b⟩ h => by cases LawfulBEq.eq_of_beq (α := Name) h; rfl + rfl := BEq.rfl (α := Name) + +/-- The structural size of a level. -/ +private def size : Level → Nat + | .zero | .param _ | .mvar _ => 1 + | .succ l => size l + 1 + | .max a b | .imax a b => size a + size b + 1 + +private theorem size_max {a b : Level} : size (.max a b) = size a + size b + 1 := rfl +private theorem size_imax {a b : Level} : size (.imax a b) = size a + size b + 1 := rfl + +private theorem one_le_size : ∀ l : Level, 1 ≤ size l + | .zero | .param _ | .mvar _ => Nat.le_refl _ + | .succ l => Nat.le_succ_of_le (one_le_size l) + | .max a b | .imax a b => by have := one_le_size a; simp only [size]; omega + +private theorem size_getLevelOffset_le : ∀ l : Level, size l.getLevelOffset ≤ size l + | .succ l => Nat.le_trans (size_getLevelOffset_le l) (Nat.le_succ _) + | .zero | .param _ | .mvar _ | .max .. | .imax .. => Nat.le_refl _ + +/-- Structural comparison of level *bases* (levels that are not `succ`s). +Sub-levels are compared by `normCmp`, i.e. base first, then offset. -/ +def baseCmp : Level → Level → Ordering + | .max a b, .max c d => + ((baseCmp a.getLevelOffset c.getLevelOffset).then (compare a.getOffset c.getOffset)).then + ((baseCmp b.getLevelOffset d.getLevelOffset).then (compare b.getOffset d.getOffset)) + | .imax a b, .imax c d => + ((baseCmp a.getLevelOffset c.getLevelOffset).then (compare a.getOffset c.getOffset)).then + ((baseCmp b.getLevelOffset d.getLevelOffset).then (compare b.getOffset d.getOffset)) + | .param n₁, .param n₂ => Name.cmp n₁ n₂ + | .mvar n₁, .mvar n₂ => Name.cmp n₁.name n₂.name + | l₁, l₂ => compare l₁.ctorToNat l₂.ctorToNat +termination_by l₁ l₂ => size l₁ + size l₂ +decreasing_by + all_goals + first + | (exact Nat.lt_of_le_of_lt + (Nat.add_le_add (size_getLevelOffset_le _) (size_getLevelOffset_le _)) + (by simp only [size]; omega)) + +/-- Comparison of levels: base first, then offset. -/ +def normCmp (l₁ l₂ : Level) : Ordering := + (baseCmp l₁.getLevelOffset l₂.getLevelOffset).then (compare l₁.getOffset l₂.getOffset) + +/-- The same-constructor part of `baseCmp`. It is `.eq` when the constructors differ, in which +case the `ctorToNat` comparison of `baseCmp_eq` already decides the comparison. -/ +private def structCmp : Level → Level → Ordering + | .max a b, .max c d => (normCmp a c).then (normCmp b d) + | .imax a b, .imax c d => (normCmp a c).then (normCmp b d) + | .param n₁, .param n₂ => Name.cmp n₁ n₂ + | .mvar n₁, .mvar n₂ => Name.cmp n₁.name n₂.name + | _, _ => .eq + +/-- `baseCmp` is the lexicographic product of the constructor tags with `structCmp`. -/ +private theorem baseCmp_eq : ∀ l₁ l₂ : Level, + baseCmp l₁ l₂ = (compare l₁.ctorToNat l₂.ctorToNat).then (structCmp l₁ l₂) := by + intro l₁ l₂ + cases l₁ <;> cases l₂ <;> simp [baseCmp, structCmp, normCmp, ctorToNat] + +private theorem baseCmp_swap : ∀ l₁ l₂ : Level, baseCmp l₂ l₁ = (baseCmp l₁ l₂).swap := by + intro l₁ l₂ + induction l₁, l₂ using baseCmp.induct with + | case1 a b c d ih₁ ih₂ | case2 a b c d ih₁ ih₂ => + rw [baseCmp, baseCmp] + simp only [Ordering.swap_then] + rw [← ih₁, ← ih₂, + ← OrientedCmp.eq_swap (cmp := compare (α := Nat)) (a := c.getOffset) (b := a.getOffset), + ← OrientedCmp.eq_swap (cmp := compare (α := Nat)) (a := d.getOffset) (b := b.getOffset)] + | case3 n₁ n₂ | case4 n₁ n₂ => + rw [baseCmp, baseCmp]; exact OrientedCmp.eq_swap + | case5 l₁ l₂ h₁ h₂ h₃ h₄ => + rw [baseCmp, baseCmp] + · exact OrientedCmp.eq_swap + all_goals grind + +theorem normCmp_swap (l₁ l₂ : Level) : normCmp l₂ l₁ = (normCmp l₁ l₂).swap := by + rw [normCmp, normCmp, Ordering.swap_then, ← baseCmp_swap, + ← OrientedCmp.eq_swap (cmp := compare (α := Nat))] + +private theorem normCmp_rot_of {a b c : Level} + (h : Rot (baseCmp a.getLevelOffset b.getLevelOffset) + (baseCmp b.getLevelOffset c.getLevelOffset) (baseCmp a.getLevelOffset c.getLevelOffset)) : + Rot (normCmp a b) (normCmp b c) (normCmp a c) := + h.then (Rot.of_transCmp a.getOffset b.getOffset c.getOffset) + +private theorem baseCmp_rot : ∀ l₁ l₂ l₃ : Level, + Rot (baseCmp l₁ l₂) (baseCmp l₂ l₃) (baseCmp l₁ l₃) := by + suffices key : ∀ n l₁ l₂ l₃, size l₁ + size l₂ + size l₃ ≤ n → + Rot (baseCmp l₁ l₂) (baseCmp l₂ l₃) (baseCmp l₁ l₃) from + fun l₁ l₂ l₃ => key _ l₁ l₂ l₃ (Nat.le_refl _) + intro n + induction n with + | zero => + intro l₁ l₂ l₃ h + have := one_le_size l₁; have := one_le_size l₂; have := one_le_size l₃ + omega + | succ n ih => + intro l₁ l₂ l₃ hn + rw [baseCmp_eq, baseCmp_eq, baseCmp_eq] + refine (Rot.of_transCmp ..).then' fun e₁ e₂ e₃ => ?_ + -- the sub-level comparisons are on strictly smaller levels + have small : ∀ x y z : Level, size x + size y + size z < size l₁ + size l₂ + size l₃ → + Rot (normCmp x y) (normCmp y z) (normCmp x z) := by + intro x y z hxyz + refine normCmp_rot_of (ih _ _ _ ?_) + have := size_getLevelOffset_le x + have := size_getLevelOffset_le y + have := size_getLevelOffset_le z + omega + -- all three constructor tags agree, so all three levels share a constructor + have hc₁ : l₁.ctorToNat = l₂.ctorToNat := Nat.compare_eq_eq.1 e₁ + have hc₂ : l₂.ctorToNat = l₃.ctorToNat := Nat.compare_eq_eq.1 e₂ + clear e₁ e₂ e₃ hn + cases l₁ <;> cases l₂ <;> cases hc₁ <;> cases l₃ <;> cases hc₂ <;> simp only [structCmp] + · exact ⟨fun _ _ => rfl, fun _ _ => rfl, fun _ _ => rfl⟩ + · exact ⟨fun _ _ => rfl, fun _ _ => rfl, fun _ _ => rfl⟩ + · refine Rot.then (small _ _ _ ?_) (small _ _ _ ?_) <;> (simp only [size_max]; omega) + · refine Rot.then (small _ _ _ ?_) (small _ _ _ ?_) <;> (simp only [size_imax]; omega) + · exact Rot.of_transCmp .. + · exact Rot.of_transCmp .. + +theorem normCmp_rot (a b c : Level) : Rot (normCmp a b) (normCmp b c) (normCmp a c) := + normCmp_rot_of (baseCmp_rot ..) + +instance : TransCmp baseCmp := TransCmp.of_rot (fun a b => baseCmp_swap b a) baseCmp_rot +instance : TransCmp normCmp := TransCmp.of_rot (fun a b => normCmp_swap b a) normCmp_rot + +private theorem getOffsetAux_eq : ∀ (l : Level) (k), l.getOffsetAux k = l.getOffset + k := by + intro l + induction l with + | succ l ih => + intro k + show l.getOffsetAux (k+1) = l.getOffsetAux 1 + k + rw [ih (k+1), ih 1]; omega + | _ => intro k; simp [getOffsetAux, getOffset] + +private theorem getOffset_succ {l : Level} : (Level.succ l).getOffset = l.getOffset + 1 := by + show l.getOffsetAux 1 = _ + rw [getOffsetAux_eq] + +private theorem getLevelOffset_succ {l : Level} : + (Level.succ l).getLevelOffset = l.getLevelOffset := rfl + +/-! ### Reflexivity and antisymmetry -/ + +private theorem baseCmp_refl : ∀ l : Level, baseCmp l l = .eq := by + suffices key : ∀ n l, size l ≤ n → baseCmp l l = .eq from fun l => key _ l (Nat.le_refl _) + intro n + induction n with + | zero => intro l h; have := one_le_size l; omega + | succ n ih => + intro l hn + have hnorm x (hx : size x ≤ n) : normCmp x x = .eq := by + rw [normCmp, ih _ (Nat.le_trans (size_getLevelOffset_le x) hx), Nat.compare_eq_eq.2 rfl]; rfl + rw [baseCmp_eq, Nat.compare_eq_eq.2 rfl] + show structCmp l l = .eq + cases l with simp only [structCmp] + | max a b | imax a b => + rw [hnorm a, hnorm b]; rfl + all_goals simp only [size] at hn ⊢; omega + | _ => exact LawfulBEqCmp.compare_eq_iff_beq.2 (beq_self_eq_true _) + +theorem normCmp_refl (l : Level) : normCmp l l = .eq := by + rw [normCmp, baseCmp_refl, Nat.compare_eq_eq.2 rfl]; rfl + +instance : ReflCmp baseCmp where compare_self := baseCmp_refl _ +instance : ReflCmp normCmp where compare_self := normCmp_refl _ + +/-- A level is determined by its base and its offset. -/ +private theorem level_ext : ∀ {l₁ l₂ : Level}, l₁.getLevelOffset = l₂.getLevelOffset → + l₁.getOffset = l₂.getOffset → l₁ = l₂ := by + intro l₁ + induction l₁ with + | succ a ih => + intro l₂ + cases l₂ with + | succ b => + intro h₁ h₂ + rw [getLevelOffset_succ, getLevelOffset_succ] at h₁ + rw [getOffset_succ, getOffset_succ] at h₂ + exact congrArg Level.succ (ih h₁ (by omega)) + | _ => intro h₁ h₂; rw [getOffset_succ] at h₂; simp [getOffset, getOffsetAux] at h₂ + | _ => + intro l₂ + cases l₂ with + | succ b => intro h₁ h₂; rw [getOffset_succ] at h₂; simp [getOffset, getOffsetAux] at h₂ + | _ => intro h₁ h₂; exact h₁ + +private theorem getLevelOffset_ne_succ : ∀ (l a : Level), l.getLevelOffset ≠ .succ a := by + intro l + induction l with + | succ b ih => exact ih + | _ => intro a h; cases h + +theorem eq_of_normCmp_eq : ∀ {l₁ l₂ : Level}, normCmp l₁ l₂ = .eq → l₁ = l₂ := by + suffices key : ∀ n (l₁ l₂ : Level), size l₁ + size l₂ ≤ n → normCmp l₁ l₂ = .eq → l₁ = l₂ from + fun {l₁ l₂} h => key _ l₁ l₂ (Nat.le_refl _) h + intro n + induction n with + | zero => intro l₁ l₂ h; have := one_le_size l₁; have := one_le_size l₂; omega + | succ n ih => + intro l₁ l₂ hn h + rw [normCmp, baseCmp_eq] at h + obtain ⟨h₁, hoff⟩ := Ordering.then_eq_eq.1 h + obtain ⟨hc, hs⟩ := Ordering.then_eq_eq.1 h₁ + refine level_ext ?_ (Nat.compare_eq_eq.1 hoff) + have hb₁ := size_getLevelOffset_le l₁ + have hb₂ := size_getLevelOffset_le l₂ + have hns₁ := getLevelOffset_ne_succ l₁ + have hns₂ := getLevelOffset_ne_succ l₂ + clear h h₁ hoff + generalize l₁.getLevelOffset = b₁ at * + generalize l₂.getLevelOffset = b₂ at * + replace hc := Nat.compare_eq_eq.1 hc + cases b₁ <;> cases b₂ <;> try simp only [ctorToNat, Nat.reduceEqDiff] at hc + · rfl + · exact absurd rfl (hns₁ _) + · obtain ⟨e₁, e₂⟩ := Ordering.then_eq_eq.1 hs + rw [ih _ _ _ e₁, ih _ _ _ e₂] <;> (simp only [size_max] at hb₁ hb₂ ⊢; omega) + · obtain ⟨e₁, e₂⟩ := Ordering.then_eq_eq.1 hs + rw [ih _ _ _ e₁, ih _ _ _ e₂] <;> (simp only [size_imax] at hb₁ hb₂ ⊢; omega) + · simp only [structCmp] at hs + rw [eq_of_beq (LawfulBEqCmp.compare_eq_iff_beq.1 hs)] + · rename_i x y + have : x.name = y.name := eq_of_beq (LawfulBEqCmp.compare_eq_iff_beq.1 hs) + cases x; cases y; simp_all + +instance : LawfulEqCmp normCmp where eq_of_compare := eq_of_normCmp_eq + +/-! ### `normLt` in terms of `normCmp` -/ + +private theorem compare_beq_lt (a b : Nat) : (compare a b == Ordering.lt) = decide (a < b) := by + apply Bool.eq_iff_iff.2; simp [Nat.compare_eq_lt] + +private theorem base_max {a b : Level} : (Level.max a b).getLevelOffset = .max a b := rfl +private theorem base_imax {a b : Level} : (Level.imax a b).getLevelOffset = .imax a b := rfl +private theorem off_max {a b : Level} : (Level.max a b).getOffset = 0 := rfl +private theorem off_imax {a b : Level} : (Level.imax a b).getOffset = 0 := rfl + +/-- `normLtAux` accumulates the `succ`s into the offsets and then runs `normCmp`. -/ +private theorem normLtAux_eq : ∀ (l₁ : Level) (k₁ : Nat) (l₂ : Level) (k₂ : Nat), + normLtAux l₁ k₁ l₂ k₂ = + ((baseCmp l₁.getLevelOffset l₂.getLevelOffset).then + (compare (l₁.getOffset + k₁) (l₂.getOffset + k₂)) == .lt) := by + intro l₁ k₁ l₂ k₂ + induction l₁, k₁, l₂, k₂ using normLtAux.induct with + | case1 l₁ k₁ l₂ k₂ ih => + rw [normLtAux, ih] + simp only [getLevelOffset_succ, getOffset_succ] + rw [show l₁.getOffset + 1 + k₁ = l₁.getOffset + (k₁ + 1) by omega] + | case2 l₁ k₁ l₂ k₂ hns ih => + rw [normLtAux, ih] + simp only [getLevelOffset_succ, getOffset_succ] + rw [show l₂.getOffset + 1 + k₂ = l₂.getOffset + (k₂ + 1) by omega] + exact hns + | case3 a b k₁ c d k₂ hbeq | case6 a b k₁ c d k₂ hbeq => + -- the two levels are syntactically equal: the offsets decide + rw [normLtAux, if_pos hbeq, Bool.eq_iff_iff] + cases eq_of_beq hbeq + show _ ↔ ((baseCmp _ _).then (compare (0 + k₁) (0 + k₂)) == _) + rw [baseCmp_refl] + simp only [decide_eq_true_eq, Ordering.then, Nat.zero_add, beq_iff_eq, Nat.compare_eq_lt] + | case4 a b k₁ c d k₂ hbeq hne ih | case7 a b k₁ c d k₂ hbeq hne ih => + -- the heads differ, so the head comparison decides + rw [normLtAux, if_neg (by simpa using hbeq), if_pos hne, ih] + have hne' : a ≠ c := by simpa using hne + have hac : normCmp a c ≠ .eq := fun h => hne' (eq_of_normCmp_eq h) + simp only [base_max, base_imax, off_max, off_imax, Nat.add_zero, Nat.zero_add] + rw [baseCmp] + show ((normCmp a c) == _) = (((normCmp a c).then (normCmp b d)).then (compare k₁ k₂) == _) + cases h : normCmp a c <;> simp_all [Ordering.then] + | case5 a b k₁ c d k₂ hbeq hne ih | case8 a b k₁ c d k₂ hbeq hne ih => + -- the heads agree, so the tail comparison decides + rw [normLtAux, if_neg (by simpa using hbeq), if_neg hne, ih] + have hac : a = c := by simpa using hne + subst hac + have hne' : b ≠ d := by rintro rfl; exact absurd (by simp) hbeq + have hbd : normCmp b d ≠ .eq := fun h => hne' (eq_of_normCmp_eq h) + simp only [base_max, base_imax, off_max, off_imax, Nat.add_zero, Nat.zero_add] + rw [baseCmp] + show ((normCmp b d) == _) = (((normCmp a a).then (normCmp b d)).then (compare k₁ k₂) == _) + rw [normCmp_refl] + cases h : normCmp b d <;> simp_all [Ordering.then] + | case9 n₁ k₁ n₂ k₂ hbeq => + rw [normLtAux, if_pos hbeq, Bool.eq_iff_iff] + cases eq_of_beq hbeq + show _ ↔ ((baseCmp (Level.param n₁) (Level.param n₁)).then (compare (0 + k₁) (0 + k₂)) == _) + rw [baseCmp_refl] + simp only [decide_eq_true_eq, Ordering.then, Nat.zero_add, beq_iff_eq, Nat.compare_eq_lt] + | case11 n₁ k₁ n₂ k₂ hbeq => + rw [normLtAux, if_pos hbeq, Bool.eq_iff_iff] + cases eq_of_beq hbeq + show _ ↔ ((baseCmp (Level.mvar n₁) (Level.mvar n₁)).then (compare (0 + k₁) (0 + k₂)) == _) + rw [baseCmp_refl] + simp only [decide_eq_true_eq, Ordering.then, Nat.zero_add, beq_iff_eq, Nat.compare_eq_lt] + | case10 n₁ k₁ n₂ k₂ hbeq => + rw [normLtAux, if_neg hbeq] + show _ = ((baseCmp (Level.param n₁) (Level.param n₂)).then + (compare (0 + k₁) (0 + k₂)) == _) + rw [baseCmp] + have : Name.cmp n₁ n₂ ≠ .eq := fun h => + hbeq (LawfulBEqCmp.compare_eq_iff_beq.1 h) + cases h : Name.cmp n₁ n₂ <;> simp_all [Name.lt, Ordering.then] + | case12 n₁ k₁ n₂ k₂ hbeq => + rw [normLtAux, if_neg hbeq] + show _ = ((baseCmp (Level.mvar n₁) (Level.mvar n₂)).then + (compare (0 + k₁) (0 + k₂)) == _) + rw [baseCmp] + have : Name.cmp n₁.name n₂.name ≠ .eq := fun h => hbeq (by + have := eq_of_beq (LawfulBEqCmp.compare_eq_iff_beq.1 h) + cases n₁; cases n₂; simp_all) + cases h : Name.cmp n₁.name n₂.name <;> simp_all [Name.lt, Ordering.then] + | case13 l₁ k₁ l₂ k₂ hs₁ hs₂ hmax himax hpar hmvar hbeq + | case14 l₁ k₁ l₂ k₂ hs₁ hs₂ hmax himax hpar hmvar hbeq => + -- neither level is a `succ` and their constructors differ, so the tags decide + rw [normLtAux, baseCmp_eq] + · cases l₁ <;> cases l₂ <;> + simp_all [structCmp, ctorToNat, Ordering.then, compare_beq_lt, getLevelOffset, getOffset, + getOffsetAux] <;> grind + all_goals assumption + +theorem normLt_eq (l₁ l₂ : Level) : normLt l₁ l₂ = (normCmp l₁ l₂ == .lt) := by + rw [normLt, normLtAux_eq, normCmp, Nat.add_zero, Nat.add_zero] + +/-! ### `normLt` is a strict weak order -/ + +theorem normLt_asymm {a b : Level} (h : normLt a b) : ¬normLt b a := by + simp only [normLt_eq, beq_iff_eq] at h ⊢ + exact OrientedCmp.not_lt_of_lt h + +theorem normLt_le_trans {a b c : Level} (h₁ : ¬normLt b a) (h₂ : ¬normLt c b) : ¬normLt c a := by + simp only [normLt_eq, beq_iff_eq, ← Ordering.isGE_iff_ne_lt] at h₁ h₂ ⊢ + exact TransCmp.isGE_trans h₂ h₁ + +/-- On levels with equal bases, `normLt` compares the offsets. -/ +theorem normLt_same_base {l₁ l₂ : Level} (h : l₁.getLevelOffset = l₂.getLevelOffset) : + normLt l₁ l₂ = decide (l₁.getOffset < l₂.getOffset) := by + rw [normLt_eq, normCmp, h, baseCmp_refl] + simp only [Ordering.then, compare_beq_lt] + +end Lean.Level diff --git a/Ix/Theory/Named/Verify/QSort.lean b/Ix/Theory/Named/Verify/QSort.lean new file mode 100644 index 000000000..18dfdbb3f --- /dev/null +++ b/Ix/Theory/Named/Verify/QSort.lean @@ -0,0 +1,398 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +/- +Copyright (c) 2025 Lean FRO, LLC. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kim Morrison +-/ +module +public import Init.Data.Array.Basic +import all Init.Data.Array.QSort.Basic + +/-! +# Verification of `Array.qsort` + +Adapted from the verification in leanprover/lean4#14658 +(tests/elab/grind_qsort.lean on the `qsort_verification` branch). + +This file contains a verification of the `Array.qsort` function, +using the `grind` tactic. + +The theorems are: +* `size_qsort : (qsort as lt lo hi).size = as.size` +* `qsort_perm : qsort as lt lo hi ~ as` + +And when `lt` is antisymmetric and `¬ lt a b` is transitive, we have: +* `qsort_sorted' : lo ≤ i < j ≤ hi → ¬ lt (as.qsort lt lo hi)[j] (as.qsort lt lo hi)[i]` +* `qsort_sorted : i < j → ¬ lt (as.qsort lt)[j] (as.qsort lt)[i]` + +(There is not currently a public theorem that `(qsort as lt lo hi)[i] = as[i]` when `i < lo` or `hi < i`.) + +-/ +namespace Array + +open List Vector + +-- These attributes still need to be moved to the standard library. + +-- set_option trace.grind.ematch.pattern true in +-- attribute [grind] Vector.getElem?_eq_getElem -- This one requires some consideration! -- Probably not need, see Vector.Perm.extract' below. + +-- Hmm, we don't seem to have the Array analogues of these! +attribute [grind =] Vector.toArray_perm_iff +attribute [grind =] Vector.perm_toArray_iff + +attribute [grind .] Vector.swap_perm + +attribute [grind .] List.Perm.refl +attribute [grind .] Array.Perm.refl +attribute [grind .] Vector.Perm.refl + +-- attribute [grind] Array.Perm.extract +-- attribute [grind] Vector.Perm.extract + +-- These are just the patterns resulting from `grind`, but the behaviour should be explained! +grind_pattern List.Perm.trans => l₁ ~ l₂, l₁ ~ l₃ +grind_pattern Array.Perm.trans => xs ~ ys, xs ~ zs +grind_pattern Vector.Perm.trans => xs ~ ys, xs ~ zs + +/-- Variant of `List.Perm.take` specifying the permutation is constant after `i` elementwise. -/ +theorem _root_.List.Perm.take_of_getElem {l₁ l₂ : List α} (h : l₁ ~ l₂) {i : Nat} + (w : ∀ j, i ≤ j → (_ : j < l₁.length) → l₁[j] = l₂[j]'(by have := h.length_eq; omega)) : + l₁.take i ~ l₂.take i := by + apply h.take_of_getElem? + intro j hij + by_cases h_length₁ : j < l₁.length + <;> have h_length₂ := h.length_eq ▸ h_length₁ + <;> grind + +/-- Variant of `List.Perm.drop` specifying the permutation is constant before `i` elementwise. -/ +theorem _root_.List.Perm.drop_of_getElem {l₁ l₂ : List α} (h : l₁ ~ l₂) {i : Nat} + (w : ∀ j, j < i → (_ : j < l₁.length) → l₁[j] = l₂[j]'(by have := h.length_eq; omega)) : + l₁.drop i ~ l₂.drop i := by + apply h.drop_of_getElem? + intro j hij + by_cases h_length₁ : j < l₁.length + <;> have h_length₂ := h.length_eq ▸ h_length₁ + <;> grind + +private theorem getElem_mk {l : List α} {i : Nat} (h : i < l.length) : + (Array.mk l)[i]'(by simpa using h) = l[i] := by + rw [← Array.getElem_toList] + +theorem _root_.Array.Perm.extract' {xs ys : Array α} (h : xs ~ ys) {lo hi : Nat} + (wlo : ∀ i, i < lo → (_ : i < xs.size) → xs[i] = ys[i]'(by have := h.size_eq; omega)) + (whi : ∀ i, hi ≤ i → (_ : i < xs.size) → xs[i] = ys[i]'(by have := h.size_eq; omega)) : + xs.extract lo hi ~ ys.extract lo hi := by + rcases xs with ⟨xs⟩ + rcases ys with ⟨ys⟩ + simp_all only [perm_iff_toList_perm, List.extract_toArray] + apply List.Perm.take_of_getElem + (w := fun i h₁ h₂ => by + rw [List.getElem_drop, List.getElem_drop, ← getElem_mk, ← getElem_mk] + exact whi (lo + i) (by omega) (by grind)) + apply List.Perm.drop_of_getElem + (w := fun i h₁ h₂ => by + rw [← getElem_mk, ← getElem_mk] + exact wlo i h₁ (by grind)) + simpa using List.perm_iff_toArray_perm.mpr h + +theorem _root_.Vector.Perm.extract' {xs ys : Vector α n} (h : xs ~ ys) {lo hi : Nat} + (wlo : ∀ i, i < lo → (_ : i < n) → xs[i] = ys[i]) (whi : ∀ i, hi ≤ i → (_ : i < n) → xs[i] = ys[i]) : + xs.extract lo hi ~ ys.extract lo hi := by + rcases xs with ⟨xs, rfl⟩ + rcases ys with ⟨ys, h⟩ + exact ⟨Array.Perm.extract' h.toArray (by simpa using wlo) (by simpa using whi)⟩ + +attribute [grind .] Array.Perm.extract' +attribute [grind .] Vector.Perm.extract' + +variable (lt : α → α → Bool) (lo hi : Nat) + +@[simp, grind =] public theorem size_qsort (as : Array α) : + (qsort as lt lo hi).size = as.size := by + grind [qsort] + +private theorem qpartition_loop_perm (as : Vector α n) + (hhi : hi < n) (ilo : lo ≤ i) (ik : i ≤ k) (w : k ≤ hi) : + (qpartition.loop lt lo hi hhi pivot as i k).2 ~ as := by + fun_induction qpartition.loop with grind + +@[local grind .] +private theorem qpartition_perm + (as : Vector α n) (w : lo ≤ hi) (hlo : lo < n) (hhi : hi < n) : + (qpartition as lt lo hi).2 ~ as := by + unfold qpartition + refine Vector.Perm.trans (qpartition_loop_perm ..) ?_ + repeat' first + | split + | grind + | refine Vector.Perm.trans (Vector.swap_perm ..) ?_ + +private theorem qsort_sort_perm + (as : Vector α n) (w : lo ≤ hi) (hlo : lo < n) (hhi : hi < n) : + qsort.sort lt as lo hi w hlo hhi ~ as := by + fun_induction qsort.sort with grind + +grind_pattern qsort_sort_perm => qsort.sort lt as lo hi w hlo hhi + +public theorem qsort_perm (as : Array α) : qsort as lt lo hi ~ as := by + grind [qsort] + +private theorem getElem_qpartition_loop_snd_of_lt_lo + (hhi : hi < n) (as : Vector α n) (i k : Nat) (ilo : lo ≤ i) (ik : i ≤ k) (w : k ≤ hi) (w' : lo ≤ hi) + (l : Nat) (h : l < lo) : (qpartition.loop lt lo hi hhi pivot as i k).2[l] = as[l] := by + fun_induction qpartition.loop <;> grind + +private theorem getElem_qpartition_snd_of_lt_lo (as : Vector α n) + (hhi : hi < n) (w : lo ≤ hi) + (k : Nat) (h : k < lo) : (qpartition as lt lo hi).2[k] = as[k] := by + grind [qpartition, getElem_qpartition_loop_snd_of_lt_lo] + +@[local grind =] private theorem getElem_qsort_sort_of_lt_lo + (as : Vector α n) + (hlo : lo < n) (hhi : hi < n) (w : lo ≤ hi) + (i : Nat) (h : i < lo) : (qsort.sort lt as lo hi)[i] = as[i] := by + fun_induction qsort.sort with grind [getElem_qpartition_snd_of_lt_lo] + +private theorem getElem_qpartition_loop_snd_of_hi_lt + (hhi : hi < n) (as : Vector α n) (i k) + (ilo : lo ≤ i) (ik : i ≤ k) (w : k ≤ hi) (z : i ≤ hi) + (l : Nat) (h : hi < l) (h' : l < n) : (qpartition.loop lt lo hi hhi pivot as i k).2[l] = as[l] := by + fun_induction qpartition.loop <;> grind + +private theorem getElem_qpartition_snd_of_hi_lt (as : Vector α n) + (hhi : hi < n) (w : lo ≤ hi) + (k : Nat) (h : hi < k) (h' : k < n) : (qpartition as lt lo hi).2[k] = as[k] := by + grind [qpartition, getElem_qpartition_loop_snd_of_hi_lt] + +@[local grind =] private theorem getElem_qsort_sort_of_hi_lt + (as : Vector α n) (w : lo ≤ hi) + (hlo : lo < n) (hhi : hi < n) (w : lo ≤ hi) + (i : Nat) (h : hi < i) (h' : i < n) : (qsort.sort lt as lo hi)[i] = as[i] := by + fun_induction qsort.sort with grind [getElem_qpartition_snd_of_hi_lt] + +private theorem extract_qsort_sort_perm (as : Vector α n) (lt : α → α → Bool) + (hlo := by grind) (hhi := by grind) (w : lo ≤ hi := by grind) : + ((qsort.sort lt as lo hi).extract lo (hi + 1)) ~ (as.extract lo (hi + 1)) := by + grind + +private theorem getElem_qsort_sort_mem + (as : Vector α n) (hhi : hi < n) (i : Nat) (h : i < n) (_ : lo ≤ i) (_ : i ≤ hi) : + (qsort.sort lt as lo hi)[i] ∈ as.extract lo (hi + 1) := by + rw [← (extract_qsort_sort_perm lo hi as lt).mem_iff, Vector.mem_extract_iff_getElem] + exact ⟨i - lo, by grind⟩ + +private theorem qpartition_loop_spec₁ + (hhi : hi < n) (ilo : lo ≤ i) (ik : i ≤ k) (w : k < n) (khi : k ≤ hi) + (as : Vector α n) (hpivot : pivot = as[hi]) + (q : ∀ l, (hk₁ : lo ≤ l) → (hk₂ : l < i) → lt as[l] as[hi]) (mid as') + (w_mid : mid = (qpartition.loop lt lo hi hhi pivot as i k).fst.1) (hmid : mid < n) + (w_as : as' = (qpartition.loop lt lo hi hhi pivot as i k).2) : + ∀ l, (h₁ : lo ≤ l) → (h₂ : l < mid) → lt as'[l] as'[mid] := by + fun_induction qpartition.loop with unfold qpartition.loop at w_mid w_as + | case1 + | case2 => apply_assumption <;> grind + | case3 => grind + +private theorem qpartition_loop_spec₂ + (hhi : hi < n) (ilo : lo ≤ i) (ik : i ≤ k) (w : k < n) (khi : k ≤ hi) + (as : Vector α n) (hpivot : pivot = as[hi]) + (q : ∀ l, (hk₁ : i ≤ l) → (hk₂ : l < k) → !lt as[l] as[hi]) (mid as') + (w_mid : mid = (qpartition.loop lt lo hi hhi pivot as i k).fst.1) (hmid : mid < n) + (w_as : as' = (qpartition.loop lt lo hi hhi pivot as i k).2) : + ∀ l, (h₁ : mid < l) → (h₂ : l ≤ hi) → lt as'[l] as'[mid] = false := by + fun_induction qpartition.loop with grind + +/-- +All elements in the active range before the pivot, are less than the pivot. +-/ +private theorem qpartition_spec₁ + (hhi : hi < n) (w : lo ≤ hi) + (as : Vector α n) (mid as') + (w_mid : mid = (qpartition as lt lo hi).fst.1) (hmid : mid < n) + (w_as : as' = (qpartition as lt lo hi).2) : + ∀ i, (h₁ : lo ≤ i) → (h₂ : i < mid) → lt as'[i] as'[mid] := by + grind [qpartition, qpartition_loop_spec₁] + +/-- +All elements in the active range after the pivot, are greater than or equal to the pivot. +-/ +private theorem qpartition_spec₂ + (hhi : hi < n) (w : lo ≤ hi) + (as : Vector α n) (mid as') + (w_mid : mid = (qpartition as lt lo hi).fst.1) (hmid : mid < n) + (w_as : as' = (qpartition as lt lo hi).2) : + ∀ i, (h₁ : mid < i) → (h₂ : i ≤ hi) → lt as'[i] as'[mid] = false := by + grind [qpartition, qpartition_loop_spec₂] + +/-! +We now need to deal with a corner case: +we need to show that `qpartition` only returns a value `≥ hi` when `hi ≤ lo` +(and hence the slice of the array between `lo` and `hi` (inclusive) is trivially already sorted). + +We prove two preliminary lemmas about `qpartition.loop`. +-/ + +/-- If we already have `i < k`, then we're sure to return something less than `hi`. -/ +private theorem qpartition_loop_lt_hi₁ + (ilo : lo ≤ i) (ik : i < k) (w : k ≤ hi) (z : k ≤ hi) (ik' : i ≤ k) : + (qpartition.loop lt lo hi hhi pivot as i k).1.val < hi := by + fun_induction qpartition.loop with grind + +/-- +Otherwise, if there is some position `k' ≥ k` which is greater than or equal to the pivot, +then when we reach that we'll be sure `i < k`, and hence the previous lemma will apply, +and so we're sure to return something less than `hi`. + -/ +private theorem qpartition_loop_lt_hi₂ + {as : Vector α n} (ilo : lo ≤ i) (ik : i ≤ k) (w : k < n) (z : k ≤ hi) + (q : ∃ (k' : Nat) (hj' : k' < n), k' ≥ k ∧ k' < hi ∧ ¬ lt as[k'] pivot) : + (qpartition.loop lt lo hi hhi pivot as i k).1.val < hi := by + fun_induction qpartition.loop with + | case1 => + -- It would be nice if a more aggressive mode in `grind` would do this. + apply_assumption <;> grind + | case2 => grind [qpartition_loop_lt_hi₁] + | case3 => grind + +/-- The only way `qpartition` returns a pivot position `≥ hi` is if `hi ≤ lo`. -/ +private theorem qpartition_fst_lt_hi (lt_asymm : ∀ {a b}, lt a b → ¬ lt b a) + (as : Vector α n) (hhi : hi < n) (w : lo < hi) : (qpartition as lt lo hi).fst.1 < hi := by + apply qpartition_loop_lt_hi₂ lt lo hi + · grind + · exact ⟨(lo + hi)/2, by grind⟩ + +private theorem qsort_sort_spec + (lt_asymm : ∀ {a b}, lt a b → ¬ lt b a) + (le_trans : ∀ {a b c}, ¬ lt b a → ¬ lt c b → ¬ lt c a) + (as : Vector α n) (lo hi : Nat) (hhi : hi < n) (w : lo ≤ hi) + (as' : Vector α n) (w_as : as' = qsort.sort lt as lo hi) : + ∀ i, (h₁ : lo ≤ i) → (h₂ : i < hi) → ¬ lt (as')[i + 1] as'[i] := by + unfold qsort.sort at w_as + split at w_as <;> rename_i w₁ + · -- The interesting case, where `lo < hi`. + intro i h₁ h₂ + -- Decompose `qpartition as lt lo hi` into `mid` (the pivot) and `as'` (the partitioned array). + split at w_as <;> rename_i mid hmid as' w₂ + split at w_as <;> rename_i w₃ + · -- If the pivot was at least `hi`, then we get a contradiction from `lo < hi`. + simp only [Prod.ext_iff, Subtype.ext_iff] at w₂ + obtain ⟨rfl, rfl⟩ := w₂ + have := qpartition_fst_lt_hi lt lo hi lt_asymm as hhi w₁ + grind + · -- Now we know `lo ≤ mid < hi`. + subst w_as + if p₁ : i < mid then + -- If `i < mid`, then the second stage of sorting is only + -- moving elements above where we're looking. + rw [getElem_qsort_sort_of_lt_lo (i := i)] + rw [getElem_qsort_sort_of_lt_lo (i := i + 1)] + -- And so we can apply the theorem recursively replacing `hi` with `mid`. + apply qsort_sort_spec lt_asymm le_trans as' lo mid + -- The remaining arithmetic side conditions are easily resolved. + all_goals grind + else + replace p₁ : mid ≤ i := by grind + -- If `mid ≤ i`, we need to consider two cases. + if p₃ : mid = i then + -- The tricky case, where `mid = i`. + subst i + -- On the right hand side, the index is below the range where the second stage of sorting is happening, + -- so we can drop that sort. + rw [getElem_qsort_sort_of_lt_lo (i := mid)] + -- The `mid` element of `qsort.sort lt as' lo mid ⋯` + -- is *some* element `lo + k` of `as'` in the range `lo ≤ lo + k ≤ mid`. + have z := getElem_qsort_sort_mem lt lo mid as' ?_ mid ?_ ?_ ?_ + rw [Vector.mem_extract_iff_getElem] at z + obtain ⟨k, hk, z⟩ := z + rw [← z] + clear z + -- Similarly, the `mid + 1` element on the left hand side + -- is some element `mid + 1 + k'` of `qsort.sort lt as' lo mid ⋯` + -- in the range `mid + 1 ≤ mid + 1 + k' ≤ hi` + have z := getElem_qsort_sort_mem lt (mid + 1) hi + (qsort.sort lt as' lo mid ?_ ?_) ?_ (mid + 1) ?_ ?_ ?_ + rw [Vector.mem_extract_iff_getElem] at z + obtain ⟨k', hk', z⟩ := z + rw [← z] + clear z + -- And then the first stage sort on the left hand side can't have any effect, + -- as it only moves elements between `lo` and `mid` inclusive. + rw [getElem_qsort_sort_of_hi_lt] + · by_cases p : lo + k = mid + · -- Now if `lo + k = mid`, + -- the element `as'[mid + 1 + k']` is in the top part of the partitioned array, + -- and `as[lo + k]` is the pivot, so we get the inequality from the specification of `qpartition`. + grind [qpartition_spec₂] + · -- Otherwise, we use transitivity: + -- `as[lo + k']` is in the bottom part, so is strictly less than the pivot, + -- while `as'[mid + 1 + k']` is in the top, so greater than or equal to the pivot. + apply le_trans (b := as'[mid]) + · grind [qpartition_spec₁] + · grind [qpartition_spec₂] + -- Various arithmetic side conditions remain from the rewriting, + -- but are now all easy to resolve. + all_goals grind + else + -- If `i < mid`, we can apply the theorem recursively replacing + -- `as` with `qsort.sort lt as' lo mid ⋯` and `lo` with `mid + 1`. + apply qsort_sort_spec lt_asymm le_trans _ _ _ (w_as := rfl) <;> grind + · -- Just an arithmetical contradiction. + grind + +/-- +The slice of `as.qsort lt lo hi` from `lo` to `hi` (inclusive) is sorted. + +This variant states that adjacent elements are non-decreasing. +See `qsort_sorted'` for a variant about arbitrary pairs of indices. +-/ +public theorem qsort_sorted₁' (lt : α → α → Bool) (lt_asymm : ∀ {a b}, lt a b → ¬ lt b a) + (le_trans : ∀ {a b c}, ¬ lt b a → ¬ lt c b → ¬ lt c a) + (as : Array α) (lo hi : Nat) (i) (h₁ : lo ≤ i) (h₂ : i < hi) (h₃ : i + 1 < as.size) : + ¬ lt ((as.qsort lt lo hi)[i + 1]'(by grind)) ((as.qsort lt lo hi)[i]'(by grind)) := by + unfold qsort + split <;> rename_i w + · grind + · apply qsort_sort_spec lt lt_asymm le_trans (w_as := rfl) <;> grind + +/-- +`Array.qsort` returns a sorted array, i.e. adjacent elements are non-decreasing. + +See `qsort_sorted` for a variant about arbitrary pairs of indices. +-/ +public theorem qsort_sorted₁ (lt : α → α → Bool) (lt_asymm : ∀ {a b}, lt a b → ¬ lt b a) + (le_trans : ∀ {a b c}, ¬ lt b a → ¬ lt c b → ¬ lt c a) (as : Array α) + (i) (h : i + 1 < (qsort as lt).size) : + ¬ lt (as.qsort lt)[i + 1] (as.qsort lt)[i] := by + have := qsort_sorted₁' lt lt_asymm le_trans + grind + +/-- The slice of `as.qsort lt lo hi` from `lo` to `hi` (inclusive) is sorted. -/ +public theorem qsort_sorted' (lt : α → α → Bool) (lt_asymm : ∀ {a b}, lt a b → ¬ lt b a) + (le_trans : ∀ {a b c}, ¬ lt b a → ¬ lt c b → ¬ lt c a) + (as : Array α) (lo hi : Nat) (i j) (h₁ : lo ≤ i) (h₂ : i < j) (h₃ : j ≤ hi) (h₄ : j < as.size) : + ¬ lt ((as.qsort lt lo hi)[j]'(by grind)) ((as.qsort lt lo hi)[i]'(by grind)) := by + induction j with + | zero => grind + | succ j ih => + if p : i = j then + subst p + apply qsort_sorted₁' <;> grind + else + apply le_trans (b := (as.qsort lt lo hi)[j]'(by grind)) + · grind + · apply qsort_sorted₁' <;> grind + +public theorem qsort_sorted (lt : α → α → Bool) (lt_asymm : ∀ {a b}, lt a b → ¬ lt b a) + (le_trans : ∀ {a b c}, ¬ lt b a → ¬ lt c b → ¬ lt c a) (as : Array α) : + ∀ i j, (h₁ : i < j) → (h₂ : j < (qsort as lt).size) → + ¬ lt (as.qsort lt)[j] (as.qsort lt)[i] := by + have := qsort_sorted' lt lt_asymm le_trans + grind + +end Array diff --git a/Ix/Theory/Named/Verify/TypeChecker.lean b/Ix/Theory/Named/Verify/TypeChecker.lean new file mode 100644 index 000000000..04ed1b2a3 --- /dev/null +++ b/Ix/Theory/Named/Verify/TypeChecker.lean @@ -0,0 +1,236 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.TypeChecker.InferType +import Ix.Theory.Named.Verify.TypeChecker.WHNF +import Ix.Theory.Named.Verify.TypeChecker.IsDefEq + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named + +open Lean hiding Environment Exception +open Kernel + +structure VEnvs where + venv : DefinitionSafety → VEnv + +structure VEnvs.WF (env : Environment) (ves : VEnvs) where + tr : TrEnv safety env (ves.venv safety) + hasPrimitives : VEnv.HasPrimitives (ves.venv safety) + safePrimitives : env.find? n = some ci → + Environment.primitives.contains n → ci.safety = .safe ∧ ci.levelParams = [] + mono : safety ≤ safety' → ves.venv safety' ≤ ves.venv safety + projectionReady : ProjectionReady env (ves.venv safety) + structureEtaReady : StructureEtaReady env (ves.venv safety) + +/-- Assemble a `VEnvs` from a pointwise existential. `DefinitionSafety` has three elements, so +this is a finite case split rather than an appeal to choice -- the name records what it replaces. -/ +theorem VEnvs.axiom_of_choice {P : DefinitionSafety → VEnv → Prop} (H : ∀ sf, ∃ x, P sf x) : + ∃ x : VEnvs, ∀ sf, P sf (x.venv sf) := by + have ⟨x1, _⟩ := H .safe; have ⟨x2, _⟩ := H .partial; have ⟨x3, _⟩ := H .unsafe + exact ⟨⟨fun | .safe => x1 | .partial => x2 | .unsafe => x3⟩, by rintro ⟨⟩ <;> assumption⟩ + +/-- A model of `env` at a *single* safety level, which is all the type checker consumes. + +`VEnvs.WF` bundles one of these at every level, but not every environment the checker runs +against admits that: while a `partial` mutual block is being checked its members are present +as axioms tagged `safe` (an `AxiomVal` cannot be tagged `partial`), and their types were only +checked at `partial`, so there is no `safe`-level model of that environment. -/ +structure VEnvAt (env : Environment) (safety : DefinitionSafety) (venv : VEnv) : Prop where + tr : TrEnv safety env venv + hasPrimitives : VEnv.HasPrimitives venv + safePrimitives : env.find? n = some ci → + Environment.primitives.contains n → ci.safety = .safe ∧ ci.levelParams = [] + projectionReady : ProjectionReady env venv + structureEtaReady : StructureEtaReady env venv + +theorem VEnvs.WF.toVEnvAt {env : Environment} {ves : VEnvs} (wf : ves.WF env) + (safety : DefinitionSafety) : VEnvAt env safety (ves.venv safety) where + tr := wf.tr + hasPrimitives := wf.hasPrimitives + safePrimitives := wf.safePrimitives + projectionReady := wf.projectionReady + structureEtaReady := wf.structureEtaReady + +namespace TypeChecker +open Inner + +theorem Methods.withFuel.WF : ∀ {n}, (withFuel n).WF + | 0 => + { isDefEqCore _ _ := .throw + whnfCore _ := .throw + whnf _ := .throw + inferType _ _ := .throw } + | n + 1 => + have := withFuel.WF (n := n) + { isDefEqCore h1 h2 := isDefEqCore'.WF h1 h2 _ this + whnfCore h1 := whnfCore'.WF h1 _ this + whnf h1 := whnf'.WF h1 _ this + inferType h1 h2 := inferType'.WF h1 h2 _ this } + +theorem RecM.WF.run {x : RecM α} (H : x.WF c s Q) : (RecM.run x).WF c s Q := + H _ Methods.withFuel.WF + +def VContext.mk1 {env : Environment} {safety : DefinitionSafety} {venv : VEnv} + (wf : VEnvAt env safety venv) (lparams : List Name := []) + (fuel : FuelConfig := {}) : VContext where + env; safety; lparams; fuel; venv + hasPrimitives := wf.hasPrimitives + safePrimitives := wf.safePrimitives + trenv := wf.tr + projectionReady := wf.projectionReady + structureEtaReady := wf.structureEtaReady + mlctx := .nil + mlctx_wf := trivial + lctx_eq := rfl + +def VContext.mk' {env : Environment} {ves : VEnvs} (wf : ves.WF env) + (safety : DefinitionSafety := .safe) (lparams : List Name := []) + (fuel : FuelConfig := {}) : VContext := .mk1 (wf.toVEnvAt safety) lparams fuel + +theorem VState.WF.empty1 {env : Environment} {safety : DefinitionSafety} {venv : VEnv} + {wf : VEnvAt env safety venv} {lparams : List Name} {fuel : FuelConfig} : + VState.WF (.mk1 wf lparams fuel) {} where + trctx := .nil + ngen_wf := nofun + ectx := ⟨[], .refl, trivial, .refl, .empty, nofun⟩ + inferTypeI_wf := .empty + inferTypeC_wf := .empty + whnfCore_wf := .empty + whnf_wf := .empty + unfold_wf _ := by simp + +theorem VState.WF.empty {env : Environment} {ves : VEnvs} {wf : ves.WF env} + {safety : DefinitionSafety} {lparams : List Name} {fuel : FuelConfig} : + VState.WF (.mk' wf safety lparams fuel) {} := by + unfold VContext.mk'; exact .empty1 + +theorem M.WF.run1 {env : Environment} {venv : VEnv} (wf : VEnvAt env safety venv) + {x : M α} {Q} (H : x.WF (.mk1 wf lparams fuel) {} fun a _ => Q a) : + (M.run env safety {} lparams fuel x).WF Q := by + intro a eq + simp [M.run, Functor.map, Except.map] at eq + split at eq <;> cases eq; rename_i eq + let ⟨_, _, _, _, H⟩ := H .empty1 _ _ eq + exact H + +theorem M.WF.run {env : Environment} {ves : VEnvs} (wf : ves.WF env) + {x : M α} {Q} (H : x.WF (.mk' wf safety lparams fuel) {} fun a _ => Q a) : + (M.run env safety {} lparams fuel x).WF Q := by + unfold VContext.mk' at H; exact M.WF.run1 _ H + +/-- Loop invariant rule for `for x in xs do ...`. `Inv` is indexed by the list still to be +processed, so the conclusion `Inv []` records that every element was handled. The body must +`yield`; a loop that can `break` is out of scope (none of the kernel's loops do). -/ +theorem M.WF.forIn {c : VContext} {f : α → β → M (ForInStep β)} + {Inv : List α → β → VState → Prop} + (H : ∀ v vs b s, Inv (v :: vs) b s → + (f v b).WF c s fun r s' => ∃ b', r = .yield b' ∧ Inv vs b' s') : + ∀ {vs : List α} {b : β} {s : VState}, Inv vs b s → + (forIn vs b f).WF c s fun b' s' => Inv [] b' s' + | [], _, _, h => .pure h + | v :: vs, b, s, h => by + rw [List.forIn_cons] + refine (H v vs b s h).bind fun r s' _ hr => ?_ + obtain ⟨b', rfl, hinv⟩ := hr + exact M.WF.forIn H hinv + +theorem M.WF.bindThrow {c : VContext} {s : VState} {x : M α} {f : α → M β} {Q} + (h : x.WF c s fun _ _ => False) : (x >>= f).WF c s Q := + h.bind fun _ _ _ hf => hf.elim + +/-- Loop rule for `addMutual`'s header loop, whose accumulator is the set of names seen so +far: each iteration rejects a name already in the set, so the whole block is duplicate-free. -/ +theorem M.WF.forInFresh {c : VContext} {Q : Lean.DefinitionVal → β → Prop} + {f : Lean.DefinitionVal → NameSet → M (ForInStep NameSet)} + (H : ∀ v found s, (f v found).WF c s fun r _ => + found.contains v.name = false ∧ (∃ b, Q v b) ∧ r = .yield (found.insert v.name)) : + ∀ {vs : List Lean.DefinitionVal} {found : NameSet} {s : VState}, + (ForIn.forIn vs found f).WF c s fun _ _ => + (∃ bs, List.Forall₂ Q vs bs) ∧ (vs.map (·.name)).Nodup ∧ + ∀ v ∈ vs, found.contains v.name = false + | [], _, _ => .pure ⟨⟨[], .nil⟩, by simp, by simp⟩ + | v :: vs, found, s => by + rw [List.forIn_cons] + refine (H v found s).bind fun r s' _ h => ?_ + obtain ⟨hfresh, ⟨b, hb⟩, rfl⟩ := h + refine (M.WF.forInFresh H (vs := vs) (found := found.insert v.name)).mono + fun _ _ _ h => ?_ + obtain ⟨⟨bs, hbs⟩, hnd, hmem⟩ := h + refine ⟨⟨b :: bs, .cons hb hbs⟩, ?_, ?_⟩ + · rw [List.map_cons, List.nodup_cons] + refine ⟨fun hm => ?_, hnd⟩ + obtain ⟨w, hw, hwn⟩ := List.mem_map.1 hm + have := hmem w hw + rw [NameSet.contains_insert, hwn] at this + simp at this + · intro w hw + cases hw with + | head => exact hfresh + | tail _ hw => + have := hmem w hw + rw [NameSet.contains_insert] at this + exact (by simpa using this : _ ∧ _).2 + +/-- Loop rule for a loop whose elements are already related to a list `cis`, so each iteration +may use the datum paired with the element it processes; each refines its `ci` to a `ci'` +related by `R`. -/ +theorem M.WF.forInForall₂ {c : VContext} {f : α → Unit → M (ForInStep Unit)} + {P : α → β → Prop} {R : β → β → Prop} {Q : α → β → Prop} + (H : ∀ v ci s, P v ci → (f v ()).WF c s fun r _ => + (∃ ci', R ci ci' ∧ Q v ci') ∧ r = .yield ()) : + ∀ {vs : List α} {cis : List β} {s : VState}, List.Forall₂ P vs cis → + (ForIn.forIn vs () f).WF c s fun _ _ => + ∃ cis', List.Forall₂ R cis cis' ∧ List.Forall₂ Q vs cis' := by + intro vs cis s h + induction h generalizing s with + | nil => exact .pure ⟨[], .nil, .nil⟩ + | @cons v ci vs cis hd tl ih => + rw [List.forIn_cons] + refine (H v ci s hd).bind fun r s' _ h => ?_ + obtain ⟨⟨ci', hR, hQ⟩, rfl⟩ := h + refine (ih (s := s')).mono fun _ _ _ h => ?_ + obtain ⟨cis', h1, h2⟩ := h + exact ⟨ci' :: cis', .cons hR h1, .cons hQ h2⟩ + +nonrec theorem whnf.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + M.WF c s (whnf e) fun e₁ _ => c.TrExpr e₁ e' := + (whnf.WF he).run.mono fun _ _ _ h => h.2 + +nonrec theorem inferType.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + M.WF c s (inferType e) fun ty _ => ∃ ty', c.TrTyping e ty e' ty' := + (inferType.WF he).run + +nonrec theorem checkType.WF {c : VContext} {s : VState} (h1 : e.FVarsIn (· ∈ c.vlctx.fvars)) : + M.WF c s (checkType e) fun ty _ => ∃ e' ty', c.TrTyping e ty e' ty' := + (checkType.WF h1).run + +nonrec theorem isDefEq.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + M.WF c s (isDefEq e₁ e₂) fun b _ => b → c.IsDefEqU e₁' e₂' := + (isDefEq.WF he₁ he₂).run + +nonrec theorem isProp.WF {c : VContext} {s : VState} + (he : c.TrExprS e e') : (isProp e).WF c s fun b _ => b → c.HasType e' (.sort .zero) := + (isProp.WF he).run + +nonrec theorem ensureSort.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + M.WF c s (ensureSort e e₀) fun e1 _ => c.TrExpr e1 e' ∧ ∃ u, e1 = .sort u := + (ensureSortCore.WF he).run.mono fun _ _ _ h => ⟨h.2.1, h.1⟩ + +nonrec theorem ensureForall.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + M.WF c s (ensureForall e) fun e1 _ => + c.TrExpr e1 e' ∧ ∃ name ty body bi, e1 = .forallE name ty body bi := + (ensureForallCore.WF he).run.mono fun _ _ _ h => h.2 + +nonrec theorem ensureType.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + M.WF c s (ensureType e) fun e1 _ => ∃ e', c.TrExprS e e' ∧ ∃ u u', e1 = .sort u ∧ + VLevel.ofLevel c.lparams u = some u' ∧ c.HasType e' (.sort u') := by + refine (inferType.WF he).bind fun _ _ _ ⟨_, _, a1, a2, a3⟩ => ?_ + refine (ensureSort.WF a2).mono fun _ _ _ ⟨⟨_, b1, b2⟩, b3⟩ => ?_ + obtain ⟨_, rfl⟩ := b3; let .sort b1 := b1 + exact ⟨_, a1, _, _, rfl, b1, a3.defeqU_r c.Ewf c.Δwf b2.symm⟩ diff --git a/Ix/Theory/Named/Verify/TypeChecker/Basic.lean b/Ix/Theory/Named/Verify/TypeChecker/Basic.lean new file mode 100644 index 000000000..175f3c5fb --- /dev/null +++ b/Ix/Theory/Named/Verify/TypeChecker/Basic.lean @@ -0,0 +1,1176 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.Environment.Lemmas +import Ix.Theory.Named.Verify.Typing.ConditionallyTyped +import Ix.Theory.Named.Reference.TypeChecker + +open Ix.Theory (VLevel) + +namespace Except + +def WF (x : Except ε α) (Q : α → Prop) : Prop := ∀ a, x = .ok a → Q a + +theorem WF.bind {x : Except ε α} {f : α → Except ε β} {Q R} + (h1 : x.WF Q) (h2 : ∀ a, Q a → (f a).WF R) : (x >>= f).WF R := by + intro b + simp [(· >>= ·), Except.bind] + split; · simp + exact h2 _ (h1 _ rfl) _ + +theorem WF.pure {Q} (H : Q a) : + (pure a : Except ε α).WF Q := by rintro _ ⟨⟩; exact H + +theorem WF.map {x : Except ε α} {f : α → β} {Q R} + (h1 : x.WF Q) (h2 : ∀ a, Q a → R (f a)) : (f <$> x).WF R := by + rw [map_eq_pure_bind] + exact h1.bind fun _ h => .pure (h2 _ h) + +theorem WF.mono {x : Except ε α} {Q R} + (h1 : x.WF Q) (h2 : ∀ a, Q a → R a) : x.WF R := by + simpa using h1.bind fun _ h => .pure (h2 _ h) + +theorem WF.throw {Q} : (throw e : Except ε α).WF Q := nofun + +theorem WF.le {Q R} {x : Except ε α} + (h1 : x.WF Q) (H : ∀ a, Q a → R a) : + x.WF R := fun _ e => H _ (h1 _ e) + +theorem WF.pureBind {f : β → Except ε α} {Q} + {x : β} (H : WF (f x) Q) : ((Pure.pure x : Except ε β) >>= f).WF Q := H + +end Except + +namespace Ix.Theory.Named +open Ix.Theory.Named +open Lean hiding Environment Exception +open Kernel +open scoped _root_.List + +namespace DefEqCache + +variable {env : VEnv} {Us : List Name} {Δ : VLCtx} + +/-- The relation the positive `isDefEq` cache records: both kernel expressions translate to +definitionally equal `VExpr`s. + +Unlike the union-find manager it replaces, this relation is deliberately *not* closed under +transitivity or congruence. `isDefEq` is a sound but incomplete semi-decision procedure and so is +not transitive, and closing over its successes would make the checker's answers depend on the order +in which pairs were presented (lean4#14806). -/ +def IsDefEqE (env : VEnv) (Us : List Name) (Δ : VLCtx) (e₁ e₂ : Expr) : Prop := + ∃ e', TrExprS env Us Δ e₁ e' ∧ TrExpr env Us Δ e₂ e' + +theorem IsDefEqE.symm (H : IsDefEqE env Us Δ e₁ e₂) : IsDefEqE env Us Δ e₂ e₁ := + let ⟨_, h1, _, h2, h3⟩ := H; ⟨_, h2, _, h1, h3.symm⟩ + +/-- The cache is keyed by a `BEq` hash set, so a lookup may present expressions that are only +`Expr.eqv`-equal to the ones recorded. -/ +theorem IsDefEqE.eqv (H : IsDefEqE env Us Δ e₁ e₂) (h₁ : e₁ == e₁') (h₂ : e₂ == e₂') : + IsDefEqE env Us Δ e₁' e₂' := + let ⟨_, h1, _, h2, h3⟩ := H; ⟨_, h1.eqv h₁, _, h2.eqv h₂, h3⟩ + +variable! (henv : env.WF) (W : VLCtx.FVLift' Δ Δ' 0 n 0) (hΔ : VLCtx.WF env Us.length Δ') in +theorem IsDefEqE.weak' (H : IsDefEqE env Us Δ e₁ e₂) : IsDefEqE env Us Δ' e₁ e₂ := + let ⟨_, h1, h2⟩ := H; ⟨_, h1.weakFV' henv W hΔ, h2.weakFV' henv W hΔ⟩ + +theorem IsDefEqE.uniq (henv : env.WF) (hΔ : Δ.WF env Us.length) + (he₁ : TrExprS env Us Δ e₁ e₁') (he₂ : TrExprS env Us Δ e₂ e₂') + (eq : IsDefEqE env Us Δ e₁ e₂) : env.IsDefEqU Us.length Δ.toCtx e₁' e₂' := + let ⟨_, h1, _, h2, h3⟩ := eq + (he₁.uniq henv (.refl henv hΔ) h1).trans henv hΔ <| + h3.symm.trans henv hΔ (h2.uniq henv (.refl henv hΔ) he₂) + +variable (env Us Δ) in +/-- Well-formedness of the positive `isDefEq` cache: every pair it answers `true` for really is a +pair of definitionally equal expressions. -/ +def WF (m : Std.HashSet (Expr × Expr)) : Prop := + ∀ ⦃p : Expr × Expr⦄, p ∈ m → IsDefEqE env Us Δ p.1 p.2 + +theorem WF.empty : WF env Us Δ {} := by simp [WF] + +theorem WF.insert (wf : WF env Us Δ m) (H : IsDefEqE env Us Δ e₁ e₂) : + WF env Us Δ (m.insert (e₁, e₂)) := by + intro p hp + rcases Std.HashSet.mem_insert.1 hp with h | h + · obtain ⟨q₁, q₂⟩ := p + have h' : ((e₁ == q₁) && (e₂ == q₂)) = true := h + rw [Bool.and_eq_true] at h' + exact H.eqv h'.1 h'.2 + · exact wf h + +theorem WF.contains (wf : WF env Us Δ m) (h : m.contains (e₁, e₂)) : + IsDefEqE env Us Δ e₁ e₂ := wf (Std.HashSet.contains_iff_mem.1 h) + +variable! (henv : env.WF) (W : VLCtx.FVLift' Δ Δ' 0 n 0) (hΔ : VLCtx.WF env Us.length Δ') in +theorem WF.weak' (wf : WF env Us Δ m) : WF env Us Δ' m := + fun _ h => (wf h).weak' henv W hΔ + +end DefEqCache + +/-- Exact alignment between one host structure record and the registered +Theory artifact used to interpret primitive projections. The positional +metadata is retained explicitly because ordinary constant translation checks +types but does not identify the kernel's parameter/constructor roles. -/ +structure ProjectionArtifact (env : Environment) (name : Name) + (info : InductiveVal) (venv : VEnv) where + view : VStructureView + name_eq : view.name = name + viewWF : view.WF venv + constructorInfo : ConstructorVal + constructor_find : env.find? view.constructorName = + some (.ctorInfo constructorInfo) + constructor_numParams_eq : constructorInfo.numParams = view.nparams + constructor_numFields_eq : constructorInfo.numFields = view.fields.length + levelParams_length : info.levelParams.length = view.uvars + numParams_eq : info.numParams = view.nparams + numIndices_eq : info.numIndices = 0 + ctors_eq : info.ctors = [view.constructorName] + rawResult_sort : ∃ resultLevel, + view.generation.block.rawResult = .sort resultLevel + programsWF : view.ProgramsWF venv + +/-- Host/Theory coherence needed by primitive projections. + +Inference obtains one complete registered artifact from ready family +metadata. Reduction additionally relies on the positional host fact that a +constructor's cached `numParams` agrees with the registered Theory view; +ordinary constant translation checks the constructor type but does not +identify which leading binders the host metadata classifies as parameters. -/ +structure ProjectionReady (env : Environment) (venv : VEnv) : Prop where + infer : ∀ name info, env.find? name = some (.inductInfo info) → + env.isProjectionReadyStructure name = true → + Nonempty (ProjectionArtifact env name info venv) + constructorNumParams : ∀ (view : VStructureView) (info : ConstructorVal), + view.WF venv → + env.find? view.constructorName = some (.ctorInfo info) → + info.numParams = view.nparams + +/-- Exact host/Theory alignment for one constructor/family pair accepted by +the runtime structure-eta heuristics. The underlying projection artifact +supplies the registered Theory view and its typed projector programs; the two +equalities identify that artifact with the precise host constructor lookup +which triggered the heuristic. -/ +structure StructureEtaArtifact (env : Environment) (familyName : Name) + (familyInfo : InductiveVal) (constructorName : Name) + (constructorInfo : ConstructorVal) (venv : VEnv) where + projection : ProjectionArtifact env familyName familyInfo venv + constructor_name_eq : projection.view.constructorName = constructorName + constructor_info_eq : projection.constructorInfo = constructorInfo + /-- The ordered registry proof fixes the exact descriptor generated from + the checked view. It contains no equality oracle: the associated + subject-reduction package is recovered by `Ordered.structEtaWF`. -/ + etaOrdered : venv.Ordered + etaRegistered : venv.structEtas + (projection.viewWF.toStructEta etaOrdered) + +/-- Host-metadata coherence required whenever the executable checker accepts +a family/constructor pair as a nonrecursive structure. This deliberately +contains no Theory equality: `VEnv.HasStructureEta` is the separate semantic +capability consumed by the verification theorem. -/ +structure StructureEtaReady (env : Environment) (venv : VEnv) : Prop where + resolve : ∀ familyName familyInfo constructorName constructorInfo, + env.find? familyName = some (.inductInfo familyInfo) → + env.find? constructorName = some (.ctorInfo constructorInfo) → + env.isNonRecStructure familyName = true → + Nonempty (StructureEtaArtifact env familyName familyInfo + constructorName constructorInfo venv) + +/-- Resolve the family artifact named by a constructor lookup after the +runtime nonrecursive-structure test has succeeded. -/ +theorem StructureEtaReady.resolveConstructor + (self : StructureEtaReady env venv) + (hctor : env.find? constructorName = some (.ctorInfo constructorInfo)) + (hnonrec : env.isNonRecStructure constructorInfo.induct = true) : + ∃ familyInfo, + env.find? constructorInfo.induct = some (.inductInfo familyInfo) ∧ + Nonempty (StructureEtaArtifact env constructorInfo.induct familyInfo + constructorName constructorInfo venv) := by + have hshape := hnonrec + unfold Kernel.Environment.isNonRecStructure at hshape + generalize hfamily : env.find? constructorInfo.induct = found at hshape + cases found with + | none => simp at hshape + | some info => cases info with + | inductInfo familyInfo => + exact ⟨familyInfo, rfl, + self.resolve _ _ _ _ hfamily hctor hnonrec⟩ + | axiomInfo _ => simp at hshape + | defnInfo _ => simp at hshape + | thmInfo _ => simp at hshape + | opaqueInfo _ => simp at hshape + | quotInfo _ => simp at hshape + | ctorInfo _ => simp at hshape + | recInfo _ => simp at hshape + +/-- Consume the exact registered descriptor retained by a resolved host +structure artifact. Reconstruction typing comes from the registry's +`VStructEta.WF` certificate; the equality is precisely the primitive Theory +rule. -/ +theorem StructureEtaArtifact.eta + (self : StructureEtaArtifact env familyName familyInfo constructorName + constructorInfo venv) + {U : Nat} {Γ : List VExpr} {levels : List VLevel} + {params : List VExpr} {major : VExpr} + (hΓ : OnCtx Γ (venv.IsType U)) + (hlevels : ∀ level ∈ levels, level.WF U) + (hlevelsLength : levels.length = self.projection.view.uvars) + (hparamsLength : params.length = self.projection.view.nparams) + (hparamsSpine : ∃ resultLevel, + venv.SpineWF U Γ + (self.projection.view.familyType.instL levels) + params (.sort resultLevel)) + (hmajor : venv.HasType U Γ major + (self.projection.view.structureType levels params)) : + venv.IsDefEq U Γ + (self.projection.view.etaRebuild levels params major) major + (self.projection.view.structureType levels params) := by + let rule := self.projection.viewWF.toStructEta self.etaOrdered + have hruleWF : rule.WF venv := + self.etaOrdered.structEtaWF self.etaRegistered + obtain ⟨resultLevel, hparamsSpine⟩ := hparamsSpine + have hrebuild := hruleWF.rebuild_hasType VEnv.LE.rfl hΓ hlevels + hlevelsLength hparamsLength ⟨resultLevel, hparamsSpine⟩ hmajor + have heta := VEnv.IsDefEq.structEta self.etaRegistered hlevels + hlevelsLength hparamsLength hparamsSpine hmajor hrebuild + simpa [rule] using heta + +/-- Environments which contain no constructor metadata satisfy projection +readiness vacuously. This is the common staging case for validation fixtures: +families may already be present, but their constructors have not been +installed yet. -/ +theorem ProjectionReady.of_no_ctorInfo + (hnoCtor : ∀ name info, + env.find? name ≠ some (.ctorInfo info)) : + ProjectionReady env venv where + infer name _info hfind hready := by + have hfalse := + Kernel.Environment.isProjectionReadyStructure_false_of_no_ctorInfo + hfind hnoCtor + rw [hfalse] at hready + contradiction + constructorNumParams _view info _hview hfind := + (hnoCtor _ info hfind).elim + +/-- Environments with no constructor metadata also satisfy structure-eta +readiness vacuously. -/ +theorem StructureEtaReady.of_no_ctorInfo + (hnoCtor : ∀ name info, + env.find? name ≠ some (.ctorInfo info)) : + StructureEtaReady env venv where + resolve _ _ constructorName constructorInfo _ hctor _ := + (hnoCtor constructorName constructorInfo hctor).elim + +/-- A convenient negative readiness witness for staging/indexed environments +where the host recognizes no eta-eligible structure family. -/ +theorem StructureEtaReady.of_no_nonRecStructure + (hnone : ∀ name, env.isNonRecStructure name = false) : + StructureEtaReady env venv where + resolve familyName _ _ _ _ _ hnonrec := by + rw [hnone familyName] at hnonrec + contradiction + +namespace TypeChecker + +inductive MLCtx where + | nil : MLCtx + | vlam (id : FVarId) (name : Name) (ty : Expr) (ty' : VExpr) (bi : BinderInfo) : MLCtx → MLCtx + | vlet (id : FVarId) (name : Name) (ty v : Expr) (ty' v' : VExpr) : MLCtx → MLCtx + +@[simp] def MLCtx.vlctx : MLCtx → VLCtx + | .nil => [] + | .vlam id _ ty ty' _ c => (some (id, ty.fvarsList), .vlam ty') :: c.vlctx + | .vlet id _ ty v ty' v' c => (some (id, ty.fvarsList ++ v.fvarsList), .vlet ty' v') :: c.vlctx + +def MLCtx.lctx : MLCtx → LocalContext + | .nil => {} + | .vlam id name ty _ bi c => c.lctx.mkLocalDecl id name ty bi + | .vlet id name ty val _ _ c => c.lctx.mkLetDecl id name ty val + +@[simp] def MLCtx.length : MLCtx → Nat + | .nil => 0 + | .vlam _ _ _ _ _ c + | .vlet _ _ _ _ _ _ c => c.length + 1 + +def MLCtx.decls : MLCtx → List LocalDecl + | .nil => {} + | .vlam x name ty _ bi c => .cdecl c.length x name ty bi .default :: c.decls + | .vlet x name ty v _ _ c => .ldecl c.length x name ty v false default :: c.decls + +@[simp] def MLCtx.fvarRevList (c : MLCtx) (n) (hn : n ≤ c.length) : List FVarId := + match n, c, hn with + | 0, _, _ => [] + | n+1, .vlam id _ _ _ _ c, h + | n+1, .vlet id _ _ _ _ _ c, h => id :: c.fvarRevList n (Nat.le_of_succ_le_succ h) +termination_by structural n + +@[simp] theorem MLCtx.fvarRevList_length {c n hn} : (MLCtx.fvarRevList c n hn).length = n := by + induction n generalizing c <;> [simp; cases c <;> simp [*] at hn ⊢] + +@[simp] def MLCtx.letValList (c : MLCtx) (n) (hn : n ≤ c.length) : List (Option (Expr × Expr)) := + match n, c, hn with + | 0, _, _ => [] + | n+1, .vlam _ _ _ _ _ c, h => none :: c.letValList n (Nat.le_of_succ_le_succ h) + | n+1, .vlet _ _ ty v _ _ c, h => some (ty, v) :: c.letValList n (Nat.le_of_succ_le_succ h) +termination_by structural n + +@[simp] theorem MLCtx.letValList_length {c n hn} : (MLCtx.letValList c n hn).length = n := by + induction n generalizing c <;> [simp; cases c <;> simp [*] at hn ⊢] + +@[simp] def MLCtx.dropN (c : MLCtx) (n) (hn : n ≤ c.length) : MLCtx := + match n, c, hn with + | 0, c, _ => c + | n+1, .vlam _ _ _ _ _ c, h + | n+1, .vlet _ _ _ _ _ _ c, h => c.dropN n (Nat.le_of_succ_le_succ h) +termination_by structural n + +def MLCtx.WF (env : VEnv) (Us : List Name) : MLCtx → Prop + | .nil => True + | .vlam fv _ ty ty' _ c => + c.WF env Us ∧ c.lctx.find? fv = none ∧ + TrExprS env Us c.vlctx ty ty' ∧ + env.IsType Us.length c.vlctx.toCtx ty' + | .vlet fv _ ty v ty' v' c => + c.WF env Us ∧ c.lctx.find? fv = none ∧ + TrExprS env Us c.vlctx ty ty' ∧ TrExprS env Us c.vlctx v v' ∧ + env.HasType Us.length c.vlctx.toCtx v' ty' + +theorem MLCtx.WF.tr : ∀ {c : MLCtx}, c.WF env Us → TrLCtx env Us c.lctx c.vlctx + | .nil, _ => ⟨.nil, .nil⟩ + | .vlam .., ⟨h1, h2, h3, h4⟩ => .mkLocalDecl h1.tr h2 h3 h4 + | .vlet .., ⟨h1, h2, h3, h4, h5⟩ => .mkLetDecl h1.tr h2 h3 h4 h5 + +theorem MLCtx.WF.dropN {c : MLCtx} (n hn) : c.WF env Us → (c.dropN n hn).WF env Us := + match n, c, hn with + | 0, _, _ => id + | n+1, .vlam .., h + | n+1, .vlet .., h => fun H => H.1.dropN n (Nat.le_of_succ_le_succ h) + +theorem MLCtx.dropN_fvars_subset {c : MLCtx} (n hn) : (c.dropN n hn).vlctx.fvars ⊆ c.vlctx.fvars := + match n, c, hn with + | 0, _, _ => fun _ => id + | n+1, .vlam .., h + | n+1, .vlet .., h => + List.subset_cons_of_subset _ <| dropN_fvars_subset n (Nat.le_of_succ_le_succ h) + +theorem MLCtx.noBV (c : MLCtx) : c.vlctx.NoBV := by + induction c <;> trivial + +structure VContext extends Context where + venv : VEnv + hasPrimitives : VEnv.HasPrimitives venv + safePrimitives : env.find? n = some ci → + Environment.primitives.contains n → ci.safety = .safe ∧ ci.levelParams = [] + trenv : TrEnv safety env venv + projectionReady : ProjectionReady env venv + structureEtaReady : StructureEtaReady env venv + mlctx : MLCtx + mlctx_wf : mlctx.WF venv lparams + lctx_eq : mlctx.lctx = lctx + +@[simp] abbrev VContext.lctx' (c : VContext) := c.mlctx.lctx +@[simp] abbrev VContext.vlctx (c : VContext) := c.mlctx.vlctx + +theorem VContext.trlctx (c : VContext) : TrLCtx c.venv c.lparams c.lctx' c.vlctx := c.mlctx_wf.tr +theorem VContext.Ewf (c : VContext) : VEnv.WF c.venv := c.trenv.wf +theorem VContext.Δwf (c : VContext) : c.vlctx.WF c.venv c.lparams.length := c.trlctx.wf + +nonrec abbrev VContext.TrExprS (c : VContext) : Expr → VExpr → Prop := + TrExprS c.venv c.lparams c.vlctx +nonrec abbrev VContext.TrExpr (c : VContext) : Expr → VExpr → Prop := + TrExpr c.venv c.lparams c.vlctx +nonrec abbrev VContext.IsType (c : VContext) : VExpr → Prop := + c.venv.IsType c.lparams.length c.vlctx.toCtx +nonrec abbrev VContext.HasType (c : VContext) : VExpr → VExpr → Prop := + c.venv.HasType c.lparams.length c.vlctx.toCtx +nonrec abbrev VContext.IsDefEqU (c : VContext) : VExpr → VExpr → Prop := + c.venv.IsDefEqU c.lparams.length c.vlctx.toCtx +nonrec abbrev VContext.TrLCtx (c : VContext) : Prop := + TrLCtx c.venv c.lparams c.lctx' c.vlctx +nonrec abbrev VContext.FVarsBelow (c : VContext) : Expr → Expr → Prop := + FVarsBelow c.vlctx +nonrec abbrev VContext.TrTyping (c : VContext) : Expr → Expr → VExpr → VExpr → Prop := + TrTyping c.venv c.lparams c.vlctx + +class VContext.MLCWF (c : VContext) (m : MLCtx) : Prop where + wf : m.WF c.venv c.lparams + +instance (c : VContext) : c.MLCWF c.mlctx := ⟨c.mlctx_wf⟩ + +structure VState extends State where + +def _root_.Ix.Theory.Named.InferCache.WF (c : VContext) (s : VState) (m : InferCache) : Prop := + ∀ ⦃e ty : Expr⦄, m[e]? = some ty → ConditionallyHasType s.ngen c.venv c.lparams c.vlctx e ty + +theorem _root_.Ix.Theory.Named.InferCache.WF.empty : InferCache.WF c s {} := fun _ => by simp + +def WHNFCache.WF (c : VContext) (s : VState) (m : InferCache) : Prop := + ∀ ⦃e ty : Expr⦄, m[e]? = some ty → ConditionallyWHNF s.ngen c.venv c.lparams c.vlctx e ty + +theorem WHNFCache.WF.empty : WHNFCache.WF c s {} := fun _ => by simp + +def UnfoldCache.WF (c : VContext) (m : ExprMap Expr) : Prop := + ∀ ⦃e e' : Expr⦄, m[e]? = some e' → ∃ n ls ci, e = .const n ls ∧ + c.env.find? n = some ci ∧ e' = Inner.instantiateDeltaValue ci ls + +class VState.WF (c : VContext) (s : VState) where + trctx : c.TrLCtx + ngen_wf : ∀ fv ∈ c.vlctx.fvars, s.ngen.Reserves fv + ectx : ∃ Δ' n, Δ'.WF c.venv c.lparams.length ∧ c.vlctx.FVLift' Δ' 0 n 0 ∧ + DefEqCache.WF c.venv c.lparams Δ' s.success ∧ ∀ fv ∈ Δ'.fvars, s.ngen.Reserves fv + inferTypeI_wf : s.inferTypeI.WF c s + inferTypeC_wf : s.inferTypeC.WF c s + whnfCore_wf : WHNFCache.WF c s s.whnfCoreCache + whnf_wf : WHNFCache.WF c s s.whnfCache + unfold_wf : UnfoldCache.WF c s.unfold + +theorem VState.WF.find?_eq_none {id} + (wf : VState.WF c s) (H : ¬s.ngen.Reserves id) : c.lctx'.find? id = none := + wf.trctx.find?_eq_none.2 fun h => H (wf.ngen_wf _ h) + +def VState.LE (s₁ s₂ : VState) : Prop := + s₁.ngen ≤ s₂.ngen + +instance : LE VState := ⟨VState.LE⟩ + +theorem VState.LE.rfl {s : VState} : s ≤ s := NameGenerator.LE.rfl + +theorem VState.LE.trans {s₁ s₂ s₃ : VState} (h₁ : s₁ ≤ s₂) (h₂ : s₂ ≤ s₃) : s₁ ≤ s₃ := + NameGenerator.LE.trans h₁ h₂ + +theorem VState.LE.reservesV {s₁ s₂ : VState} (h : s₁ ≤ s₂) {{fv}} : + s₁.ngen.Reserves fv → s₂.ngen.Reserves fv := + (·.mono h) + +theorem VState.LE.reserves {s₁ s₂ : VState} (h : s₁ ≤ s₂) {{e}} : + FVarsIn s₁.ngen.Reserves e → FVarsIn s₂.ngen.Reserves e := + (·.mono h.reservesV) + +def M.WF (c : VContext) (vs : VState) (x : M α) (Q : α → VState → Prop) : Prop := + vs.WF c → ∀ a s', x c.toContext vs.toState = .ok (a, s') → + ∃ vs', vs'.toState = s' ∧ vs ≤ vs' ∧ vs'.WF c ∧ Q a vs' + +theorem M.WF.bind {c : VContext} {s : VState} {x : M α} {f : α → M β} {Q R} + (h1 : x.WF c s Q) + (h2 : ∀ a s', s ≤ s' → Q a s' → (f a).WF c s' R) : + (x >>= f).WF c s R := by + intro wf₁ a vs₁ + simp [(· >>= ·), ReaderT.bind, StateT.bind, Except.bind] + split; · simp + intro h; rename_i v eq + obtain ⟨vs₂, eq1, le1, wf₂, h1⟩ := h1 wf₁ _ _ eq + obtain ⟨vs₃, rfl, le2, wf₃, h2⟩ := h2 _ _ le1 h1 wf₂ _ _ (eq1 ▸ h) + exact ⟨_, rfl, le1.trans le2, wf₃, h2⟩ + +theorem M.WF.pure {c : VContext} {s : VState} {Q} (H : Q a s) : + (pure a : M α).WF c s Q := by rintro h _ _ ⟨⟩; exact ⟨_, rfl, .rfl, h, H⟩ + +theorem M.WF.map {c : VContext} {s : VState} {x : M α} {f : α → β} {Q R} + (h1 : x.WF c s Q) (h2 : ∀ a s', s ≤ s' → Q a s' → R (f a) s') : (f <$> x).WF c s R := by + rw [map_eq_pure_bind] + exact h1.bind fun _ _ le h => .pure (h2 _ _ le h) + +theorem M.WF.mono {c : VContext} {s : VState} {x : M α} {Q R} + (h1 : x.WF c s Q) (h2 : ∀ a s', s ≤ s' → Q a s' → R a s') : x.WF c s R := by + simpa using h1.bind fun _ _ a1 a2 => .pure (h2 _ _ a1 a2) + +theorem M.WF.throw {c : VContext} {s : VState} {Q} : (throw e : M α).WF c s Q := nofun + +theorem M.WF.le {c : VContext} {s : VState} {Q R} {x : M α} + (h1 : x.WF c s Q) (H : ∀ a s', s ≤ s' → Q a s' → R a s') : + x.WF c s R := fun wf _ _ e => + let ⟨_, a1, a2, a3, a4⟩ := h1 wf _ _ e + ⟨_, a1, a2, a3, H _ _ a2 a4⟩ + +structure Methods.WF (m : Methods) where + isDefEqCore : c.TrExprS e₁ e₁' → c.TrExprS e₂ e₂' → + (m.isDefEqCore e₁ e₂).WF c s fun b _ => b → c.IsDefEqU e₁' e₂' + whnfCore : c.TrExprS e e' → + (m.whnfCore e cheapProj).WF c s fun e₁ _ => c.FVarsBelow e e₁ ∧ c.TrExpr e₁ e' + whnf : c.TrExprS e e' → + (m.whnf e).WF c s fun e₁ _ => c.FVarsBelow e e₁ ∧ c.TrExpr e₁ e' + inferType : e.FVarsIn (· ∈ c.vlctx.fvars) → + (inferOnly = true → ∃ e', c.TrExprS e e') → + (m.inferType e inferOnly).WF c s fun ty _ => ∃ e' ty', c.TrTyping e ty e' ty' + +def RecM.WF (c : VContext) (s : VState) (x : RecM α) (Q : α → VState → Prop) : Prop := + ∀ m, m.WF → M.WF c s (x m) Q + +theorem M.WF.liftExcept {c : VContext} {s : VState} {x : Except Exception α} {Q} (h : x.WF Q) : + M.WF c s (liftM x) fun a _ => Q a := by + rintro wf _ _ eq + cases x <;> cases eq + exact ⟨s, rfl, .rfl, wf, h _ rfl⟩ + +theorem M.WF.lift {c : VContext} {s : VState} {x : M α} {Q} (h : x.WF c s Q) : + RecM.WF c s x Q := fun _ _ => h + +instance : Coe (M.WF c s x Q) (RecM.WF c s x Q) := ⟨M.WF.lift⟩ + +theorem RecM.WF.bind {c : VContext} {s : VState} {x : RecM α} {f : α → RecM β} {Q R} + (h1 : x.WF c s Q) (h2 : ∀ a s', s ≤ s' → Q a s' → (f a).WF c s' R) : (x >>= f).WF c s R := + fun _ h => M.WF.bind (h1 _ h) fun _ _ h1' h2' => h2 _ _ h1' h2' _ h + +theorem RecM.WF.bind_le {c : VContext} {s : VState} {x : RecM α} {f : α → RecM β} {Q R} + (h1 : x.WF c s Q) (hs : s₀ ≤ s) + (h2 : ∀ a s', s₀ ≤ s' → Q a s' → (f a).WF c s' R) : (x >>= f).WF c s R := + RecM.WF.bind h1 fun _ _ h => h2 _ _ (hs.trans h) + +theorem RecM.WF.pure {c : VContext} {s : VState} {Q} (H : Q a s) : (pure a : RecM α).WF c s Q := + fun _ _ => .pure H + +theorem RecM.WF.map {c : VContext} {s : VState} {x : RecM α} {f : α → β} {Q R} + (h1 : x.WF c s Q) (h2 : ∀ a s', s ≤ s' → Q a s' → R (f a) s') : (f <$> x).WF c s R := by + rw [map_eq_pure_bind] + exact h1.bind fun _ _ le h => .pure (h2 _ _ le h) + +theorem RecM.WF.mono {c : VContext} {s : VState} {x : RecM α} {Q R} + (h1 : x.WF c s Q) (h2 : ∀ a s', s ≤ s' → Q a s' → R a s') : x.WF c s R := by + rw [← id_map x]; exact h1.map h2 + +theorem RecM.WF.throw {c : VContext} {s : VState} {Q} : (throw e : RecM α).WF c s Q := nofun + +theorem RecM.WF.le {c : VContext} {s : VState} {Q R} {x : RecM α} + (h1 : x.WF c s Q) (H : ∀ a s', s ≤ s' → Q a s' → R a s') : + x.WF c s R := fun _ h => (h1 _ h).le H + +theorem RecM.WF.pureBind {c : VContext} {s : VState} {f : β → RecM α} {Q} + {x : β} (H : WF c s (f x) Q) : ((Pure.pure x : RecM β) >>= f).WF c s Q := H + +theorem get.WF {c : VContext} {s : VState} : + M.WF c s get fun a s' => s.toState = a ∧ s = s' := by + rintro wf _ _ ⟨⟩; exact ⟨_, rfl, .rfl, wf, rfl, rfl⟩ + +theorem RecM.WF.get {c : VContext} {s : VState} {f : State → RecM α} {Q} + (H : WF c s (f s.toState) Q) : (get >>= f).WF c s Q := H + +theorem getEnv.WF {c : VContext} {s : VState} : + M.WF c s getEnv fun a s' => c.env = a ∧ s = s' := by + rintro wf _ _ ⟨⟩; exact ⟨_, rfl, .rfl, wf, rfl, rfl⟩ + +theorem RecM.WF.getEnv {c : VContext} {s : VState} {f : Environment → RecM α} {Q} + (H : WF c s (f c.env) Q) : (liftM getEnv >>= f).WF c s Q := H + +theorem getLCtx.WF {c : VContext} {s : VState} : + M.WF c s getLCtx fun a s' => c.lctx' = a ∧ s = s' := by + rintro wf _ _ ⟨⟩; exact ⟨_, rfl, .rfl, wf, c.lctx_eq, rfl⟩ + +theorem RecM.WF.getLCtx {c : VContext} {s : VState} {f : LocalContext → RecM α} {Q} + (H : WF c s (f c.lctx') Q) : (getLCtx >>= f).WF c s Q := + getLCtx.WF.lift.bind <| by rintro _ _ _ ⟨rfl, rfl⟩; exact H + +theorem RecM.WF.readThe {c : VContext} {s : VState} {f : Context → RecM α} {Q} + (H : WF c s (f c.toContext) Q) : (readThe Context >>= f).WF c s Q := H + +theorem getNGen.WF {c : VContext} {s : VState} : + M.WF c s getNGen fun a s' => s.ngen = a ∧ s = s' := by + rintro wf _ _ ⟨⟩; exact ⟨_, rfl, .rfl, wf, rfl, rfl⟩ + +theorem M.WF.getNGen {c : VContext} {s : VState} {f : NameGenerator → M α} {Q} + (H : WF c s (f s.ngen) Q) : (getNGen >>= f).WF c s Q := H + +theorem RecM.WF.getNGen {c : VContext} {s : VState} {f : NameGenerator → RecM α} {Q} + (H : WF c s (f s.ngen) Q) : (getNGen >>= f).WF c s Q := H + +theorem RecM.WF.stateWF {c : VContext} {s : VState} {x : RecM α} {Q} + (H : s.WF c → WF c s x Q) : WF c s x Q := + fun _ h wf => H wf _ h wf + +@[simp] theorem toLBool_true {b : Bool} : b.toLBool = .true ↔ b = true := by + cases b <;> simp [Bool.toLBool] + +theorem RecM.WF.toLBoolM {Q : VState → Prop} {x : RecM Bool} + (H : x.WF c s fun b s => b → Q s) : (toLBoolM x).WF c s fun b s => b = .true → Q s := + H.bind fun _ _ _ H => .pure fun h => H (by simpa using h) + +def VContext.withMLC (c : VContext) (m : MLCtx) [wf : c.MLCWF m] : VContext := + { c with + mlctx := m + mlctx_wf := wf.1 + lctx := m.lctx + lctx_eq := rfl } + +@[simp] theorem VContext.withMLC_self (c : VContext) : c.withMLC c.mlctx = c := by + simp [withMLC, c.lctx_eq] + +def VState.next (s : VState) : VState := { s with ngen := s.ngen.next } + +protected theorem RecM.WF.withLocalDecl {c : VContext} {m} [cwf : c.MLCWF m] + {s : VState} {f : Expr → RecM α} {Q name ty ty' bi} + (hty : (c.withMLC m).TrExprS ty ty') + (hty' : (c.withMLC m).IsType ty') + (hs : s₀ ≤ s) + (H : ∀ id cwf' s', s₀ ≤ s' → ¬s.ngen.Reserves id → + WF (c.withMLC (.vlam id name ty ty' bi m) (wf := cwf')) s' (f (.fvar id)) Q) : + (withLocalDecl name bi ty f).WF (c.withMLC m) s Q := by + intro _ mwf wf a s' e + let id := s.ngen.curr + have h0 := s.ngen.next_reserves_self + have h1 := s.ngen.not_reserves_self + have le : s ≤ s.next := .next + have h1' := wf.find?_eq_none h1 + let m' := m.vlam ⟨id⟩ name ty ty' bi + have cwf' : c.MLCWF m' := ⟨cwf.1, h1', hty, hty'⟩ + have : VState.WF (c.withMLC m') s.next := + have trctx := wf.trctx.mkLocalDecl h1' hty hty' + have hic {ic} (H : InferCache.WF (c.withMLC m) s ic) : InferCache.WF (c.withMLC m') s.next ic := + fun _ _ h => ((H h).fresh c.Ewf.ordered trctx.wf).mono le + have hwc {wc} (H : WHNFCache.WF (c.withMLC m) s wc) : WHNFCache.WF (c.withMLC m') s.next wc := + fun _ _ h => ((H h).fresh c.Ewf trctx.wf).mono le + { ngen_wf := by + simp [m', VContext.withMLC]; exact ⟨h0, fun _ h => le.reservesV (wf.ngen_wf _ h)⟩ + ectx := by + let ⟨_, _, a1, a2, a3, a4⟩ := wf.ectx + refine + have b1 := ⟨a1, ?_, hty'.weak' c.Ewf.ordered a2.toCtx⟩ + ⟨_, _, b1, a2.cons_fvar _ _ hty.fvarsList, a3.weak' c.Ewf (.skip_fvar _ _ .refl) b1, + fun _ h => by obtain _ | ⟨_, h⟩ := h <;> [exact h0; exact (a4 _ h).mono le]⟩ + rintro _ _ ⟨⟩; exact ⟨mt (a4 _) h1, hty.fvarsList.trans a2.fvars_sublist.subset⟩ + trctx, inferTypeI_wf := hic wf.inferTypeI_wf, inferTypeC_wf := hic wf.inferTypeC_wf + whnfCore_wf := hwc wf.whnfCore_wf, whnf_wf := hwc wf.whnf_wf, unfold_wf := wf.unfold_wf } + let ⟨s', hs1, hs2, wf', hs4⟩ := H _ _ _ (hs.trans le) h1 _ mwf this a s' e + refine have le' := le.trans hs2; ⟨s', hs1, le', ?_, hs4⟩ + have hic {ic} (H : InferCache.WF (c.withMLC m') s' ic) : + InferCache.WF (c.withMLC m) s' ic := fun _ _ h => (H h).weakN_inv c.Ewf wf'.trctx.wf + have hwc {wc} (H : WHNFCache.WF (c.withMLC m') s' wc) : + WHNFCache.WF (c.withMLC m) s' wc := fun _ _ h => (H h).weakN_inv c.Ewf wf'.trctx.wf + let ⟨_, _, a1, a2, a3⟩ := wf'.ectx + exact { + ngen_wf := (by simpa [VContext.withMLC] using wf'.ngen_wf :).2 + ectx := ⟨_, _, a1, .comp (.skip_fvar _ _ .refl) a2, a3⟩ + trctx := wf.trctx + inferTypeI_wf := hic wf'.inferTypeI_wf, inferTypeC_wf := hic wf'.inferTypeC_wf + whnfCore_wf := hwc wf'.whnfCore_wf, whnf_wf := hwc wf'.whnf_wf, unfold_wf := wf'.unfold_wf + } + +protected theorem RecM.WF.withLetDecl {c : VContext} {m} [cwf : c.MLCWF m] + {s : VState} {f : Expr → RecM α} {Q name ty ty'} + (hty : (c.withMLC m).TrExprS ty ty') + (hval : (c.withMLC m).TrExprS val val') + (hval' : (c.withMLC m).HasType val' ty') + (hs : s₀ ≤ s) + (H : ∀ id cwf' s', s₀ ≤ s' → ¬s.ngen.Reserves id → + WF (c.withMLC (.vlet id name ty val ty' val' m) (wf := cwf')) s' (f (.fvar id)) Q) : + (withLetDecl name ty val f).WF (c.withMLC m) s Q := by + intro _ mwf wf a s' e + let id := s.ngen.curr + have h0 := s.ngen.next_reserves_self + have h1 := s.ngen.not_reserves_self + have le : s ≤ s.next := .next + have h1' := wf.find?_eq_none h1 + let m' := m.vlet ⟨id⟩ name ty val ty' val' + have cwf' : c.MLCWF m' := ⟨cwf.1, h1', hty, hval, hval'⟩ + have : VState.WF (c.withMLC m') s.next := + have trctx := wf.trctx.mkLetDecl h1' hty hval hval' + have hic {ic} (H : InferCache.WF (c.withMLC m) s ic) : + InferCache.WF (c.withMLC m') s.next ic := + fun _ _ h => ((H h).fresh c.Ewf.ordered trctx.wf).mono le + have hwc {wc} (H : WHNFCache.WF (c.withMLC m) s wc) : WHNFCache.WF (c.withMLC m') s.next wc := + fun _ _ h => ((H h).fresh c.Ewf trctx.wf).mono le + { ngen_wf := by + simp [m', VContext.withMLC]; exact ⟨h0, fun _ h => le.reservesV (wf.ngen_wf _ h)⟩ + ectx := by + let ⟨_, _, a1, a2, a3, a4⟩ := wf.ectx + have hv := List.append_subset.2 ⟨hty.fvarsList, hval.fvarsList⟩ + refine + have b1 := ⟨a1, ?_, hval'.weak' c.Ewf.ordered a2.toCtx⟩ + ⟨_, _, b1, a2.cons_fvar _ _ hv, a3.weak' c.Ewf (.skip_fvar _ _ .refl) b1, + fun _ h => by obtain _ | ⟨_, h⟩ := h <;> [exact h0; exact (a4 _ h).mono le]⟩ + rintro _ _ ⟨⟩; exact ⟨mt (a4 _) h1, hv.trans a2.fvars_sublist.subset⟩ + trctx, inferTypeI_wf := hic wf.inferTypeI_wf, inferTypeC_wf := hic wf.inferTypeC_wf + whnfCore_wf := hwc wf.whnfCore_wf, whnf_wf := hwc wf.whnf_wf, unfold_wf := wf.unfold_wf } + let ⟨s', hs1, hs2, wf', hs4⟩ := H _ _ _ (hs.trans le) h1 _ mwf this a s' e + refine ⟨s', hs1, le.trans hs2, ?_, hs4⟩ + have hic {ic} (H : InferCache.WF (c.withMLC m') s' ic) : + InferCache.WF (c.withMLC m) s' ic := fun _ _ h => (H h).weakN_inv c.Ewf wf'.trctx.wf + have hwc {wc} (H : WHNFCache.WF (c.withMLC m') s' wc) : + WHNFCache.WF (c.withMLC m) s' wc := fun _ _ h => (H h).weakN_inv c.Ewf wf'.trctx.wf + let ⟨_, _, a1, a2, a3⟩ := wf'.ectx + exact { + ngen_wf := (by simpa [VContext.withMLC] using wf'.ngen_wf :).2 + ectx := ⟨_, _, a1, .comp (.skip_fvar _ _ .refl) a2, a3⟩ + trctx := wf.trctx + inferTypeI_wf := hic wf'.inferTypeI_wf, inferTypeC_wf := hic wf'.inferTypeC_wf + whnfCore_wf := hwc wf'.whnfCore_wf, whnf_wf := hwc wf'.whnf_wf, unfold_wf := wf'.unfold_wf + } + +@[simp] def MLCtx.mkForall (c : MLCtx) (n) (hn : n ≤ c.length) (e : Expr) : Expr := + match n, c, hn with + | 0, _, _ => e + | n+1, .vlam x name ty _ bi c, h => + c.mkForall n (Nat.le_of_succ_le_succ h) (.forallE name ty (.abstract1 x e) bi) + | n+1, .vlet x name ty val _ _ c, h => + c.mkForall n (Nat.le_of_succ_le_succ h) <| + let e' := Expr.abstract1 x e + if e'.hasLooseBVar' 0 then + .letE name ty val e' false + else + e'.lowerLooseBVars' 1 1 +termination_by structural n + +def AllAbove (Δ : VLCtx) (P : FVarId → Prop) (fv : FVarId) : Prop := fv ∈ Δ.fvars → P fv + +theorem AllAbove.wf (H : Δ.FVWF) : IsFVarUpSet (AllAbove Δ P) Δ ↔ IsFVarUpSet P Δ := + IsFVarUpSet.congr H fun _ h => by simp [h, AllAbove] + +@[simp] def MLCtx.mkForall' (c : MLCtx) (n) (hn : n ≤ c.length) (e : VExpr) : VExpr := + match n, c, hn, e with + | 0, _, _, e => e + | n+1, .vlam _ _ _ ty _ c, h, e => c.mkForall' n (Nat.le_of_succ_le_succ h) (.forallE ty e) + | n+1, .vlet _ _ _ _ _ _ c, h, e => c.mkForall' n (Nat.le_of_succ_le_succ h) e +termination_by structural n + +@[simp] def MLCtx.mkLambda (c : MLCtx) (n) (hn : n ≤ c.length) (e : Expr) : Expr := + match n, c, hn, e with + | 0, _, _, e => e + | n+1, .vlam x name ty _ bi c, h, e => + c.mkLambda n (Nat.le_of_succ_le_succ h) (.lam name ty (.abstract1 x e) bi) + | n+1, .vlet x name ty val _ _ c, h, e => + c.mkLambda n (Nat.le_of_succ_le_succ h) <| + let e' := Expr.abstract1 x e + if e'.hasLooseBVar' 0 then + .letE name ty val e' false + else + e'.lowerLooseBVars' 1 1 +termination_by structural n + +@[simp] def MLCtx.mkLambda' (c : MLCtx) (n) (hn : n ≤ c.length) (e : VExpr) : VExpr := + match n, c, hn, e with + | 0, _, _, e => e + | n+1, .vlam _ _ _ ty _ c, h, e => c.mkLambda' n (Nat.le_of_succ_le_succ h) (.lam ty e) + | n+1, .vlet _ _ _ _ _ _ c, h, e => c.mkLambda' n (Nat.le_of_succ_le_succ h) e +termination_by structural n + +-- HACK: MLCtx.mkLet equation generation fails if this is inlined +def MLCtx.mkLetArg (x : FVarId) (name : Name) + (ty val : Expr) (nd : Option Bool) (e : Expr) : Expr := + let e' := Expr.abstract1 x e + if e'.hasLooseBVar' 0 then + .letE name ty val e' (nd.getD false) + else if let some nd := nd then + .letE name ty val e' nd + else + e'.lowerLooseBVars' 1 1 + +@[simp] def MLCtx.mkLet (c : MLCtx) (n) (hn : n ≤ c.length) + (nds : List (Option Bool)) (eq : nds.length = n) (e : Expr) (asForall := false) : Expr := + match n, c, hn, nds, eq, e with + | 0, _, _, _, _, e => e + | n+1, .vlam x name ty _ bi c, h, _ :: nds, eq, e => + c.mkLet n (Nat.le_of_succ_le_succ h) nds (Nat.succ_inj.1 eq) <| + if asForall then .forallE name ty (.abstract1 x e) bi else .lam name ty (.abstract1 x e) bi + | n+1, .vlet x name ty val _ _ c, h, nd :: nds, eq, e => + c.mkLet n (Nat.le_of_succ_le_succ h) nds (Nat.succ_inj.1 eq) <| mkLetArg x name ty val nd e +termination_by structural n + +variable! (henv : VEnv.WF env) in +theorem MLCtx.WF.mkForall_trS {c : MLCtx} (wf : c.WF env Us) + (H1 : TrExprS env Us c.vlctx e e') + (H2 : env.IsType Us.length c.vlctx.toCtx e') (n hn) : + TrExprS env Us (c.dropN n hn).vlctx (c.mkForall n hn e) (c.mkForall' n hn e') ∧ + env.IsType Us.length (c.dropN n hn).vlctx.toCtx (c.mkForall' n hn e') := by + induction n generalizing c e e' with + | zero => exact ⟨H1, H2⟩ + | succ n ih => + match c with + | .vlam x name ty ty' bi c => + let ⟨h1, _, h3, h4⟩ := wf + refine ih h1 ?_ (.forallE h4 H2) _ + exact .forallE h4 H2 h3 (.abstract .zero H1) + | .vlet x name ty val ty' val' c => + let ⟨h1, _, h3, h4, h5⟩ := wf + refine ih h1 ?_ H2 _; dsimp; split + · exact .letE h5 h3 h4 (.abstract .zero H1) + · rename_i h; simp at h + rw [Expr.lowerLooseBVars_eq_instantiate h (v := val)] + exact .inst_let henv (.abstract .zero H1) h4 + +theorem mkForall_hasType {c : MLCtx} + (hus : us.Forall₂ (VLevel.ofLevel Us · = some ·) us') + (hΔ : c.vlctx.SortList env Us.length us') + (hu : VLevel.ofLevel Us u = some u') + (H2 : env.HasType Us.length c.vlctx.toCtx e' (.sort u')) (n hn) + (hus : us.length = n) : + ∃ u₀', VLevel.ofLevel Us (List.foldl (fun x y => mkLevelIMax' y x) u us) = some u₀' ∧ + env.HasType Us.length (c.dropN n hn).vlctx.toCtx (c.mkForall' n hn e') (.sort u₀') := by + subst hus + induction hus generalizing c e' u u' with + | nil => exact ⟨_, hu, H2⟩ + | cons h _ ih => + match c, hΔ with + | .vlam x name ty ty' bi c, .cons hΔ hu₁ => + have ⟨_, h5, h6⟩ := ofLevel_mkLevelIMax' h hu + refine ih hΔ h5 ?_ (Nat.le_of_succ_le_succ hn) + refine .defeq (.sortDF ?_ (.of_ofLevel h5) h6.symm) (hu₁.forallE H2) + exact ⟨.of_ofLevel h, .of_ofLevel hu⟩ + +theorem MLCtx.WF.mkForall'_congr {c : MLCtx} (wf : c.WF env Us) + (H : env.IsDefEq Us.length c.vlctx.toCtx e₁ e₂ (.sort u)) (n hn) : + ∃ u, env.IsDefEq Us.length (c.dropN n hn).vlctx.toCtx + (c.mkForall' n hn e₁) (c.mkForall' n hn e₂) (.sort u) := by + induction n generalizing c e₁ e₂ u with + | zero => exact ⟨_, H⟩ + | succ n ih => + match c with + | .vlam .. => let ⟨_, h⟩ := wf.2.2.2; exact ih wf.1 (.forallEDF h H) _ + | .vlet .. => exact ih wf.1 H _ + +theorem MLCtx.WF.mkForall_tr (henv : VEnv.WF env) {c : MLCtx} (wf : c.WF env Us) + (H1 : TrExpr env Us c.vlctx e e') + (H2 : env.IsType Us.length c.vlctx.toCtx e') (n hn) : + TrExpr env Us (c.dropN n hn).vlctx (c.mkForall n hn e) (c.mkForall' n hn e') ∧ + env.IsType Us.length (c.dropN n hn).vlctx.toCtx (c.mkForall' n hn e') := by + let ⟨_, H1, eq⟩ := H1 + let ⟨_, H2⟩ := H2 + have eq := eq.of_r henv wf.tr.wf H2 + have ⟨_, H⟩ := wf.mkForall'_congr eq n hn + have := wf.mkForall_trS henv H1 ⟨_, eq.hasType.1⟩ n hn + exact ⟨⟨_, this.1, _, H⟩, this.2.defeqU_l henv (wf.dropN n hn).tr.wf ⟨_, H⟩⟩ + +variable! (henv : VEnv.WF env) in +theorem MLCtx.WF.mkLambda_trS {c : MLCtx} (wf : c.WF env Us) + (H1 : TrExprS env Us c.vlctx e e') + (H2 : env.HasType Us.length c.vlctx.toCtx e' ty') (n hn) : + TrExprS env Us (c.dropN n hn).vlctx (c.mkLambda n hn e) (c.mkLambda' n hn e') ∧ + env.HasType Us.length (c.dropN n hn).vlctx.toCtx + (c.mkLambda' n hn e') (c.mkForall' n hn ty') := by + induction n generalizing c e e' ty' with + | zero => exact ⟨H1, H2⟩ + | succ n ih => + match c with + | .vlam x name ty ty' bi c => + let ⟨h1, _, h3, _, h4⟩ := wf + refine ih h1 ?_ (.lam h4 H2) _ + exact .lam ⟨_, h4⟩ h3 (.abstract .zero H1) + | .vlet x name ty val ty' val' c => + let ⟨h1, _, h3, h4, h5⟩ := wf + refine ih h1 ?_ H2 _; dsimp; split + · exact .letE h5 h3 h4 (.abstract .zero H1) + · rename_i h; simp at h + rw [Expr.lowerLooseBVars_eq_instantiate h (v := val)] + exact .inst_let henv (.abstract .zero H1) h4 + +theorem MLCtx.WF.mkLambda'_congr {c : MLCtx} (wf : c.WF env Us) + (H : env.IsDefEq Us.length c.vlctx.toCtx e₁ e₂ ty) (n hn) : + env.IsDefEq Us.length (c.dropN n hn).vlctx.toCtx + (c.mkLambda' n hn e₁) (c.mkLambda' n hn e₂) (c.mkForall' n hn ty) := by + induction n generalizing c e₁ e₂ ty with + | zero => exact H + | succ n ih => + match c with + | .vlam .. => let ⟨_, h⟩ := wf.2.2.2; exact ih wf.1 (.lamDF h H) _ + | .vlet .. => exact ih wf.1 H _ + +theorem MLCtx.WF.mkLambda_tr (henv : VEnv.WF env) {c : MLCtx} (wf : c.WF env Us) + (H1 : TrExpr env Us c.vlctx e e') + (H2 : env.HasType Us.length c.vlctx.toCtx e' ty') (n hn) : + TrExpr env Us (c.dropN n hn).vlctx (c.mkLambda n hn e) (c.mkLambda' n hn e') ∧ + env.HasType Us.length (c.dropN n hn).vlctx.toCtx + (c.mkLambda' n hn e') (c.mkForall' n hn ty') := by + let ⟨_, H1, eq⟩ := H1 + have eq := eq.of_r henv wf.tr.wf H2 + have H := wf.mkLambda'_congr eq n hn + have := wf.mkLambda_trS henv H1 eq.hasType.1 n hn + exact ⟨⟨_, this.1, _, H⟩, this.2.defeqU_l henv (wf.dropN n hn).tr.wf ⟨_, H⟩⟩ + +variable! (henv : VEnv.WF env) in +theorem MLCtx.WF.mkLet_trS {c : MLCtx} (wf : c.WF env Us) + (H1 : TrExprS env Us c.vlctx e e') + (H2 : env.HasType Us.length c.vlctx.toCtx e' ty') (n hn nds hnds) : + TrExprS env Us (c.dropN n hn).vlctx (c.mkLet n hn nds hnds e) (c.mkLambda' n hn e') ∧ + env.HasType Us.length (c.dropN n hn).vlctx.toCtx + (c.mkLambda' n hn e') (c.mkForall' n hn ty') := by + induction n generalizing c e e' ty' nds with + | zero => exact ⟨H1, H2⟩ + | succ n ih => + match c with + | .vlam x name ty ty' bi c => + let ⟨h1, _, h3, _, h4⟩ := wf; let _ :: _ := nds + refine ih h1 ?_ (.lam h4 H2) .. + exact .lam ⟨_, h4⟩ h3 (.abstract .zero H1) + | .vlet x name ty val ty' val' c => + let ⟨h1, _, h3, h4, h5⟩ := wf; let _ :: _ := nds + refine ih h1 ?_ H2 ..; dsimp [mkLetArg]; split <;> [skip; split] + · exact .letE h5 h3 h4 (.abstract .zero H1) + · exact .letE h5 h3 h4 (.abstract .zero H1) + · rename_i h _ _; simp at h + rw [Expr.lowerLooseBVars_eq_instantiate h (v := val)] + exact .inst_let henv (.abstract .zero H1) h4 + +theorem MLCtx.fvarRevList_prefix (c : MLCtx) + {n hn} : c.fvarRevList n hn <+: c.vlctx.fvars := by + induction n generalizing c with + | zero => simp + | succ n ih => match c with | .vlam .. | .vlet .. => simp [ih] + +theorem MLCtx.WF.fvars_nodup : ∀ {c : MLCtx}, c.WF env Us → c.vlctx.fvars.Nodup + | .nil, _ => .nil + | .vlam _ _ _ _ _ c, ⟨h1, h2, _⟩ + | .vlet _ _ _ _ _ _ c, ⟨h1, h2, _⟩ => by simp [h1.fvars_nodup, h1.tr.find?_eq_none.1 h2] + +theorem MLCtx.WF.fvarRevList_nodup {c : MLCtx} (wf : c.WF env Us) + (n hn) : (c.fvarRevList n hn).Nodup := + c.fvarRevList_prefix.sublist.nodup wf.fvars_nodup + +theorem MLCtx.WF.decls_size {c : MLCtx} (wf : c.WF env Us) : + c.lctx.decls.size = c.length := by + rw [← wf.tr.1.decls_wf.toList'_length] + induction c with + | nil => rfl + | vlam _ _ _ _ _ _ ih => simp [lctx, LocalContext.mkLocalDecl, ih wf.1] + | vlet _ _ _ _ _ _ _ ih => simp [lctx, LocalContext.mkLetDecl, ih wf.1] + +theorem MLCtx.WF.toList_eq {c : MLCtx} (wf : c.WF env Us) : + c.lctx.toList = c.decls := by + simp [LocalContext.toList] + induction c with + | nil => rfl + | vlam _ _ _ _ _ _ ih => simp [lctx, LocalContext.mkLocalDecl, decls, ih wf.1, wf.1.decls_size] + | vlet _ _ _ _ _ _ _ ih => simp [lctx, LocalContext.mkLetDecl, decls, ih wf.1, wf.1.decls_size] + +theorem MLCtx.WF.find?_eq {c : MLCtx} (wf : c.WF env Us) : + c.lctx.find? x = c.decls.find? (x == ·.fvarId) := by + simp [wf.tr.1.find?_eq_find?_toList, wf.toList_eq] + +inductive MLCtx.PartialForall : MLCtx → Nat → List FVarId → Expr → Prop where + | nil : PartialForall c 0 [] e + | vlam : PartialForall c n fvs (.forallE x ty (.abstract1 fv e) bi) → + PartialForall (c.vlam fv x ty ty' bi) (n+1) (fv :: fvs) e + | vlet : PartialForall c n fvs ( + let e' := Expr.abstract1 fv e + if e'.hasLooseBVar' 0 then .letE x ty v e' false + else e'.lowerLooseBVars' 1 1) → + PartialForall (c.vlet fv x ty v ty' v') (n+1) (fv :: fvs) e + | skip : e.looseBVarRange' = 0 → e' = Expr.abstract1 fv e → e'.hasLooseBVar' 0 = false → + PartialForall c n fvs e → + PartialForall (c.vlet fv x ty v ty' v') (n+1) fvs e + +theorem MLCtx.PartialForall.full : MLCtx.PartialForall c n (c.fvarRevList n hn) e := by + induction n generalizing c e with + | zero => exact .nil + | succ n ih => match c with | .vlam .. | .vlet .. => constructor; apply ih + +theorem MLCtx.PartialForall.sublist (H : MLCtx.PartialForall c n l e) : l <+ c.vlctx.fvars := by + induction H with + | nil => simp + | vlam _ ih | vlet _ ih => simp [ih] + | skip _ _ _ _ ih => exact ih.trans (List.sublist_cons_self ..) + +theorem MLCtx.WF.mkForall_partial {c : MLCtx} (wf : c.WF env Us) (n hn) + (harr : arr.toList.reverse = l.map .fvar) (hp : MLCtx.PartialForall c n l e) : + c.lctx.mkForall arr e = c.mkForall n hn e := by + have := congrArg (Array.mk ·.reverse) harr; simp at this + rw [LocalContext.mkForall, this, ← List.map_reverse, LocalContext.mkBinding_eq, + LocalContext.mkBindingList_eq_fold, List.foldr_reverse] + · clear harr this + induction hp with + | nil => simp + | vlam hp ih | vlet hp ih => + simp + refine (List.foldl_congr fun _ y h => ?_).trans <| + .trans (congrFun (congrArg _ ?_) _) (ih wf.1 _) + · refine LocalContext.mkBindingList1_congr ?_ + rw [wf.find?_eq, wf.1.find?_eq, decls, List.find?, (?_ : (y == _) = false)] + simp [LocalDecl.fvarId]; rintro ⟨⟩ + have := (List.cons_sublist_cons.2 hp.sublist).nodup wf.fvars_nodup; simp_all + · simp [LocalContext.mkBindingList1, wf.find?_eq, decls, LocalDecl.fvarId] + | skip h1 h2 h3 hp ih => + subst h2; simp [h3] + rw [Expr.lowerLooseBVars_eq_instantiate h3 (v := default), + Expr.abstract1_eq_liftLooseBVars h3, Expr.liftLooseBVars_eq_self (by simp [h1]), + Expr.instantiate1_eq_self h1] + refine (List.foldl_congr fun _ y h => ?_).trans (ih wf.1 _) + refine LocalContext.mkBindingList1_congr ?_ + rw [wf.find?_eq, wf.1.find?_eq, decls, List.find?, (?_ : (y == _) = false)] + simp [LocalDecl.fvarId]; rintro ⟨⟩ + have := (List.cons_sublist_cons.2 hp.sublist).nodup wf.fvars_nodup; simp_all + · intro _ h + exact wf.tr.find?_eq_some.2 (hp.sublist.subset (List.mem_reverse.1 h)) + · exact List.nodup_reverse.2 (hp.sublist.nodup wf.fvars_nodup) + +theorem MLCtx.WF.mkForall_eq {c : MLCtx} (wf : c.WF env Us) (n hn) + (harr : arr.toList.reverse = (c.fvarRevList n hn).map .fvar) : + c.lctx.mkForall arr e = c.mkForall n hn e := mkForall_partial wf n hn harr .full + +theorem MLCtx.WF.mkLambda_eq {c : MLCtx} (wf : c.WF env Us) (n hn) + (harr : arr.toList.reverse = (c.fvarRevList n hn).map .fvar) : + c.lctx.mkLambda arr e = c.mkLambda n hn e := by + have := congrArg (Array.mk ·.reverse) harr; simp at this + rw [LocalContext.mkLambda, this, ← List.map_reverse, LocalContext.mkBinding_eq, + LocalContext.mkBindingList_eq_fold, List.foldr_reverse] + · clear harr this + induction n generalizing c e with + | zero => simp + | succ n ih => + match c with + | .vlam .. | .vlet .. => + simp + refine (List.foldl_congr fun _ y h => ?_).trans <| + .trans (congrFun (congrArg _ ?_) _) (ih wf.1 _) + · refine LocalContext.mkBindingList1_congr ?_ + rw [wf.find?_eq, wf.1.find?_eq, decls, List.find?, (?_ : (y == _) = false)] + simp [LocalDecl.fvarId]; rintro ⟨⟩ + have := wf.fvarRevList_nodup (n+1) hn; simp_all + · simp [LocalContext.mkBindingList1, wf.find?_eq, decls, LocalDecl.fvarId] + · intro _ h + exact wf.tr.find?_eq_some.2 ((MLCtx.fvarRevList_prefix ..).subset (List.mem_reverse.1 h)) + · exact List.nodup_reverse.2 (wf.fvarRevList_nodup ..) + +namespace Inner + +/-- A successful host-environment lookup returns exactly the constant found +at the requested name. Kept in the common checker layer so both inference +and WHNF reduction can consume the same lookup certificate. -/ +theorem envGet.WF {c : VContext} : + (c.env.get name).WF fun ci => c.env.find? name = some ci := by + simp [Environment.get]; split <;> [refine .pure ‹_›; exact .throw] + +theorem whnf.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + RecM.WF c s (whnf e) fun e₁ _ => c.FVarsBelow e e₁ ∧ c.TrExpr e₁ e' := + fun _ wf => wf.whnf he + +theorem isDefEqCore.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + RecM.WF c s (isDefEqCore e₁ e₂) fun b _ => b → c.IsDefEqU e₁' e₂' := + fun _ wf => wf.isDefEqCore he₁ he₂ + +theorem cacheSuccess.WF {c : VContext} {s : VState} (he₁ : c.TrExprS e₁ e') (he₂ : c.TrExpr e₂ e') : + RecM.WF c s (cacheSuccess e₁ e₂) fun _ _ => True := by + rintro _ mwf wf _ _ ⟨⟩ + let ⟨_, _, a1, a2, ewf, a4⟩ := wf.ectx + refine ⟨{ s with toState := _ }, rfl, .rfl, { wf with ectx := ⟨_, _, a1, a2, ?_, a4⟩ }, trivial⟩ + -- The insertion is keyed on the two hashes, so either order can be the one recorded. + have H : DefEqCache.IsDefEqE c.venv c.lparams _ e₁ e₂ := + ⟨_, he₁.weakFV' c.Ewf a2 a1, he₂.weakFV' c.Ewf a2 a1⟩ + simp only []; split <;> [exact ewf.insert H; exact ewf.insert H.symm] + +theorem isDefEq.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + RecM.WF c s (isDefEq e₁ e₂) fun b _ => b → c.IsDefEqU e₁' e₂' := by + unfold isDefEq + split + · rename_i heq + exact .pure fun _ => (he₁.eqv heq).uniq c.Ewf (.refl c.Ewf c.Δwf) he₂ + simp only [] + refine (isDefEqCore.WF he₁ he₂).bind fun b _ _ hb => ?_ + simp; split + · exact (cacheSuccess.WF he₁ ⟨_, he₂, (hb ‹_›).symm⟩).map fun _ _ _ _ => hb + · exact .pure hb + +theorem inferType.WF' {c : VContext} {s : VState} (h1 : e.FVarsIn (· ∈ c.vlctx.fvars)) + (hinf : inferOnly = true → ∃ e', c.TrExprS e e') : + RecM.WF c s (inferType e inferOnly) fun ty _ => ∃ e' ty', c.TrTyping e ty e' ty' := + fun _ wf => wf.inferType h1 hinf + +theorem inferType.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + RecM.WF c s (inferType e true) fun ty _ => ∃ ty', c.TrTyping e ty e' ty' := by + refine .stateWF fun wf => ?_ + refine (inferType.WF' he.fvarsIn fun _ => ⟨_, he⟩).le + fun _ _ _ ⟨_, _, h1, h2, h3, h4⟩ => ⟨_, h1, he, h3, ?_⟩ + have := h2.uniq c.Ewf (.refl c.Ewf c.Δwf) he + exact h4.defeqU_l c.Ewf c.Δwf this + +theorem inferType.WF_uniq {c : VContext} {s : VState} + (he : c.TrExprS e e') (hty : c.HasType e' ty') : + RecM.WF c s (inferType e true) fun ty _ => c.TrExpr ty ty' := + (inferType.WF he).le fun _ _ _ ⟨_, _, _, h1, h2⟩ => + ⟨_, h1, h2.uniqU c.Ewf c.Δwf hty⟩ + +theorem checkType.WF {c : VContext} {s : VState} (h1 : e.FVarsIn (· ∈ c.vlctx.fvars)) : + RecM.WF c s (inferType e false) fun ty _ => ∃ e' ty', c.TrTyping e ty e' ty' := + inferType.WF' h1 nofun + +theorem whnfCore.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + RecM.WF c s (whnfCore e cheapProj) fun e₁ _ => c.FVarsBelow e e₁ ∧ c.TrExpr e₁ e' := + fun _ wf => wf.whnfCore he + +theorem isDelta_is_some : isDelta env e = some ci ↔ + ∃ n, env.find? n = some ci ∧ (∃ v, ci.deltaValue? = some v) ∧ + ∃ ls, e.getAppFn = .const n ls ∧ ls.length = ci.numLevelParams := by + simp only [isDelta] + split <;> [split <;> [split; skip]; skip] <;> + simp_all [Option.isSome_iff_exists] <;> grind + +def UnfoldDefinition.WF (c : VContext) (e e₀ : Expr) (e' : VExpr) : Option Expr → Prop + | some e₁ => c.FVarsBelow e e₁ ∧ c.TrExpr e₁ e' + | none => ∀ {{n ci v ls}}, c.env.find? n = some ci → ci.deltaValue? = some v → + e₀ = .const n ls → ls.length = ci.numLevelParams → False + +theorem unfoldDefinitionCore.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + RecM.WF c s (unfoldDefinitionCore e) fun oe _ => UnfoldDefinition.WF c e e e' oe := by + dsimp [unfoldDefinitionCore] + split <;> [refine .getEnv ?_; (rename_i H; exact .pure fun _ _ _ _ _ _ h => nomatch H _ _ h)] + split; rotate_left + · rename_i H; refine .pure ?_; rintro _ _ _ _ h1 h2 ⟨⟩ hlen + cases H _ (isDelta_is_some.2 ⟨_, h1, ⟨_, h2⟩, _, rfl, hlen⟩) + rename_i n ls oci ci h1 + obtain ⟨_, h3, ⟨_, h4⟩, _, ⟨⟩, hlen⟩ := isDelta_is_some.1 h1 + have : UnfoldDefinition.WF c (.const n ls) (.const n ls) e' + (some (instantiateDeltaValue ci ls)) := by + let .const a1 a2 a3 := he + have ⟨rfl, b1, b2, b3⟩ := c.trenv.find?_uniq h3 a1 + simp [instantiateDeltaValue, h4] + have c1 := c.trenv.of_value h3 b1 h4 |>.instL c.Ewf (by trivial) a2 (b2.trans a3.symm) + have := c1.weakFV c.Ewf (.from_nil c.mlctx.noBV) c.Δwf + rw [c1.wf.closedN c.Ewf trivial |>.liftN_eq (Nat.zero_le _)] at this + simp [VExpr.instL] at this; rw [VLevel.inst_map_id] at this + · exact ⟨fun _ _ _ => c1.fvarsIn.mono nofun, this⟩ + · exact (List.mapM_eq_some.1 a2).length_eq.symm.trans <| a3.trans b2.symm + split <;> [rename_i h5; exact .pure this] + refine .pureBind <| .get ?_ + split <;> [rename_i eq; skip] + · refine .stateWF fun wf => .pure ?_ + obtain ⟨_, _, _, ⟨⟩, a1, rfl⟩ := wf.unfold_wf eq + cases h3.symm.trans a1; exact this + · refine .bind (Q := fun _ _ => True) ?_ fun _ _ _ _ => .pure this + rintro _ mwf wf _ _ ⟨⟩ + refine ⟨{ s with toState := _ }, rfl, .rfl, { wf with unfold_wf := ?_ }, ⟨⟩⟩ + intro e e'; simp only [Std.HashMap.getElem?_insert] + split <;> [rintro ⟨⟩; exact (wf.unfold_wf ·)] + rename_i eq; rw [BEq.comm, Expr.eqv_const] at eq + exact ⟨_, _, _, eq, h3, rfl⟩ + +theorem unfoldDefinition.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + RecM.WF c s (unfoldDefinition e) fun oe _ => UnfoldDefinition.WF c e e.getAppFn e' oe := by + simp [unfoldDefinition]; split; rotate_left + · rename_i h; refine (unfoldDefinitionCore.WF he).mono fun _ _ _ H => ?_ + rwa [show e.getAppFn = e by revert h; unfold Expr.isApp Expr.getAppFn; split <;> simp] + have ⟨f', stk⟩ := AppStack.build (e.mkAppList_getAppArgsList ▸ he) + refine (unfoldDefinitionCore.WF stk.tr).bind fun oe _ _ H => ?_ + cases oe <;> [exact .pure H; refine .pure ?_] + have ⟨h1, h2⟩ := H + rw [Expr.mkAppRevRange_eq (l₁ := []) (l₂ := e.getAppArgsRevList) (l₃ := []) + (by simp [Expr.getAppRevArgs_toList]) (by rfl) (by simp [Expr.getAppRevArgs_eq])] + simp only [Expr.getAppArgsRevList_reverse]; constructor + · exact (e.mkAppList_getAppArgsList ▸ h1.mkAppList :) + · exact h2.rebuild_mkAppList c.Ewf c.Δwf stk.tr (e.mkAppList_getAppArgsList ▸ he :) + +theorem ensureSortCore.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + RecM.WF c s (ensureSortCore e e₀) fun e1 _ => + (∃ u, e1 = .sort u) ∧ c.TrExpr e1 e' ∧ c.FVarsBelow e e1 := by + simp [ensureSortCore]; split + · let .sort _ := e + exact .pure ⟨⟨_, rfl⟩, he.trExpr c.Ewf c.Δwf, .rfl⟩ + refine (whnf.WF he).bind fun e _ _ ⟨hb, he⟩ => ?_; split + · let .sort _ := e + exact .pure ⟨⟨_, rfl⟩, he, hb⟩ + exact .getEnv <| .getLCtx .throw + +theorem getSortLevel.WF + (he : c.TrExprS e e') : (getSortLevel e).WF c s fun l _ => + ∃ u', VLevel.ofLevel c.lparams l = some u' ∧ c.HasType e' (.sort u') := by + refine (inferType.WF he).bind fun ty _ le ⟨ty', _, _, h1, h2⟩ => ?_ + refine (ensureSortCore.WF h1).bind fun ty _ le h => ?_ + obtain ⟨⟨u, rfl⟩, ⟨ty₂, h3, h4⟩, _⟩ := h + let .sort hu := h3 + exact .pure ⟨_, hu, h2.defeqU_r c.Ewf c.Δwf h4.symm⟩ + +theorem isProp.WF + (he : c.TrExprS e e') : (isProp e).WF c s fun b _ => + b → c.HasType e' (.sort .zero) := by + refine (getSortLevel.WF he).bind fun l _ le ⟨u', hu, h⟩ => .pure fun H => ?_ + exact h.defeqU_r c.Ewf c.Δwf + ⟨_, .sortDF (.of_ofLevel hu) trivial (ofLevel_isAlwaysZero hu H)⟩ diff --git a/Ix/Theory/Named/Verify/TypeChecker/InferType.lean b/Ix/Theory/Named/Verify/TypeChecker/InferType.lean new file mode 100644 index 000000000..a16d77f19 --- /dev/null +++ b/Ix/Theory/Named/Verify/TypeChecker/InferType.lean @@ -0,0 +1,1082 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Ix.Theory.Named.Verify.TypeChecker.Reduce + +open Ix.Theory (VLevel) + +open Ix.Theory.Named + +namespace Ix.Theory.Named.TypeChecker.Inner +open Lean hiding Environment Exception +open Kernel + +theorem ensureForallCore.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + RecM.WF c s (ensureForallCore e e₀) fun e1 _ => c.FVarsBelow e e1 ∧ + c.TrExpr e1 e' ∧ ∃ name ty body bi, e1 = .forallE name ty body bi := by + simp [ensureForallCore]; split + · let .forallE .. := e + exact .pure ⟨.rfl, he.trExpr c.Ewf c.Δwf, _, _, _, _, rfl⟩ + refine (whnf.WF he).bind fun e _ _ ⟨hb, he⟩ => ?_; split + · let .forallE .. := e + exact .pure ⟨hb, he, _, _, _, _, rfl⟩ + exact .getEnv <| .getLCtx .throw + +theorem ensureForallCore.WF' {c : VContext} {s : VState} (he : c.TrExpr e e') : + RecM.WF c s (ensureForallCore e e₀) fun e1 _ => c.FVarsBelow e e1 ∧ + c.TrExpr e1 e' ∧ ∃ name ty body bi, e1 = .forallE name ty body bi := + let ⟨_, he, eq⟩ := he + (ensureForallCore.WF he).mono fun _ _ _ ⟨h1, h2, h3⟩ => + ⟨h1, h2.defeq c.Ewf c.Δwf eq, h3⟩ + +theorem checkLevel.WF {c : VContext} (H : l.hasMVar' = false) : + (checkLevel c.toContext l).WF fun _ => ∃ u', VLevel.ofLevel c.lparams l = some u' := by + simp [checkLevel]; split <;> [exact .throw; refine .pure ?_] + exact Level.getUndefParam_none H (by rename_i h; simpa using h) + +theorem inferFVar.WF {c : VContext} : + (inferFVar c.toContext name).WF fun ty => ∃ e' ty', c.TrTyping (.fvar name) ty e' ty' := by + simp [inferFVar, ← c.lctx_eq]; split <;> [refine .pure ?_; exact .throw] + rename_i decl h + rw [c.trlctx.1.find?_eq_find?_toList] at h + have := List.find?_some h; simp at this; subst this + let ⟨e', ty', h1, _, h2, _, h3⟩ := + c.trlctx.find?_of_mem c.Ewf (List.mem_of_find?_eq_some h) + exact ⟨_, _, h2, .fvar h1, h3, c.Δwf.find?_wf c.Ewf h1⟩ + +theorem inferConstant.WF {c : VContext} + (H : ∀ l ∈ ls, l.hasMVar' = false) + (hinf : inferOnly = true → ∃ e', c.TrExprS (.const name ls) e') : + (inferConstant c.toContext name ls inferOnly).WF fun ty => + ∃ e' ty', c.TrTyping (.const name ls) ty e' ty' := by + simp [inferConstant]; refine envGet.WF.bind fun ci eq1 => ?_ + have : (ls.foldlM (fun b a => checkLevel c.toContext a) PUnit.unit).WF fun _ => + ∃ ls', ls.Forall₂ (VLevel.ofLevel c.lparams · = some ·) ls' := by + clear hinf + induction ls with + | nil => exact .pure ⟨_, .nil⟩ + | cons l ls ih => + simp at H + refine (checkLevel.WF H.1).bind fun ⟨⟩ ⟨_, h1⟩ => ?_ + exact (ih H.2).le fun _ ⟨_, h2⟩ => ⟨_, .cons h1 h2⟩ + split <;> [rename_i h1; exact .throw] + have main {e'} (he : c.TrExprS (.const name ls) e') : ∃ e' ty', + c.TrTyping (.const name ls) (ci.instantiateTypeLevelParams ls) e' ty' := by + let .const h4 H' eq := id he + have ⟨_, _, h5, h6⟩ := c.trenv.find?_uniq eq1 h4 + have H := List.mapM_eq_some.1 H' + have s0 := h6.instL c.Ewf (Δ := []) trivial H' (h5.trans eq.symm) + have s1 := s0.weakFV c.Ewf (.from_nil c.mlctx.noBV) c.Δwf + rw [(c.Ewf.ordered.closedC h4).instL.liftN_eq (Nat.le_refl _)] at s1 + have ⟨_, s1, s2⟩ := s1 + refine ⟨_, _, ?_, he, s1, .defeqU_r c.Ewf c.Δwf s2.symm ?_⟩ + · intro _ _ _; exact s0.fvarsIn.mono nofun + · exact .const h4 (.of_mapM_ofLevel H') (H.length_eq.symm.trans eq) + split + · split <;> [exact .throw; rename_i h2] + generalize eq1 : _ <$> (_ : Except Exception _) = F + generalize eq2 : (fun ty : Expr => _) = P + suffices ci.isPartial = false ∨ c.safety ≠ .safe → F.WF P by + split <;> [skip; exact this (.inl (ConstantInfo.isPartial.eq_2 _ ‹_›))] + split <;> [exact .throw; apply this] + rename_i h; simpa [Decidable.or_iff_not_imp_left, ConstantInfo.isPartial] using h + subst eq1 eq2; intro h3 + refine this.map fun _ ⟨_, H⟩ => ?_ + have ⟨_, h4, _, h5, h6⟩ := c.trenv.find? eq1 <| by + revert h2 h3 + simp [ConstantInfo.safety] + split <;> simp +contextual [*] + split <;> simp [DefinitionSafety.le_safe, *] + cases c.safety <;> decide + have eq := h1.symm.trans h5 + exact main (.const h4 (List.mapM_eq_some.2 H) eq) + · simp_all; let ⟨_, h⟩ := hinf; refine .pure (main h) + +theorem inferLambda.loop.WF {c : VContext} {e₀ : Expr} + {m} [mwf : c.MLCWF m] {n} (hn : n ≤ m.length) + (hdrop : m.dropN n hn = c.mlctx) + (harr : arr.toList.reverse = (m.fvarRevList n hn).map .fvar) + (he₀ : e₀ = m.mkLambda n hn ei) + (hei : e.instantiateList ((m.fvarRevList n hn).map .fvar) = ei) + (hbelow : ∀ P, IsFVarUpSet P c.vlctx → FVarsIn P e₀ → + IsFVarUpSet (AllAbove c.vlctx P) m.vlctx ∧ FVarsIn (AllAbove c.vlctx P) ei ∧ + ∀ ty, FVarsIn (AllAbove c.vlctx P) ty → FVarsIn (AllAbove c.vlctx P) (m.mkForall n hn ty)) + (hr : e.FVarsIn (· ∈ m.vlctx.fvars)) + (hinf : inferOnly = true → ∃ e', (c.withMLC m).TrExprS ei e') : + (inferLambda.loop inferOnly arr e).WF (c.withMLC m) s fun ty _ => + ∃ e' ty', c.TrTyping e₀ ty e' ty' := by + unfold inferLambda.loop + generalize eqfvs : (m.fvarRevList n hn).map Expr.fvar = fvs at * + simp [harr, -bind_pure_comp]; split + · rename_i name dom body bi + generalize eqF : withLocalDecl (m := RecM) _ _ _ _ = F + generalize eqP : (fun ty x => ∃ _, _) = P + rw [Expr.instantiateList_lam] at hei; subst ei + have main {s₁} (le₁ : s ≤ s₁) {dom'} + (domty : (c.withMLC m).venv.IsType + (c.withMLC m).lparams.length (c.withMLC m).vlctx.toCtx dom') + (hdom : (c.withMLC m).TrExprS (dom.instantiateList fvs) dom') + (hbody : inferOnly = true → ∃ body', + TrExprS c.venv c.lparams ((none, .vlam dom') :: m.vlctx) + (body.instantiateList fvs 1) body') : + F.WF (c.withMLC m) s₁ P := by + refine .stateWF fun wf => ?_ + have hdom' := hdom.trExpr c.Ewf mwf.1.tr.wf + subst eqF eqP + refine .withLocalDecl hdom domty le₁ fun a mwf' s' le₂ res => ?_ + have eq := @Expr.instantiateList_instantiate1_comm body fvs (.fvar a) (by trivial) + refine inferLambda.loop.WF (Nat.succ_le_succ hn) (by simp [hdrop]) + (by simp [← eqfvs, harr]) ?_ (by simp; rfl) ?_ (hr.2.mono fun _ => .tail _) ?_ + · rw [he₀, eqfvs, ← eq]; simp; congr 2 + refine (FVarsIn.abstract_instantiate1 ((hr.2.instantiateList ?_ _).mono ?_)).symm + · simp [← eqfvs, FVarsIn]; exact m.fvarRevList_prefix.subset + · rintro _ h rfl; exact (mwf'.1.tr.wf.2.1 _ _ rfl).1 h + · intro P hP he + have ⟨h1, h2, h3⟩ := hbelow _ hP he + refine ⟨⟨h1, fun _ => (fvarsIn_iff.1 h2.1).1⟩, ?_, fun ty hty => h3 _ ⟨h2.1, hty.abstract1⟩⟩ + rw [eqfvs, ← eq] + refine h2.2.instantiate1 fun h => ?_ + exact res.elim (wf.ngen_wf _ (m.dropN_fvars_subset n hn (hdrop ▸ h))) + · intro h; let ⟨_, hbody⟩ := hbody h + exact eqfvs.symm ▸ eq ▸ ⟨_, hbody.inst_fvar c.Ewf.ordered mwf'.1.tr.wf⟩ + split + · subst inferOnly + refine (checkType.WF ?_).bind fun uv _ le ⟨dom', uv', _, h1, h2, h3⟩ => ?_ + · apply hr.1.instantiateList; simp [← eqfvs]; exact m.fvarRevList_prefix.subset + refine (ensureSortCore.WF h2).bind_le le fun _ _ le ⟨h4, h5, _⟩ => ?_ + obtain ⟨_, rfl⟩ := h4; let ⟨_, .sort _, h5⟩ := h5 + have domty := h3.defeqU_r c.Ewf mwf.1.tr.wf.toCtx h5.symm + have domty' : (c.withMLC m).IsType dom' := ⟨_, domty⟩ + exact main le domty' h1 nofun + · simp_all; let ⟨_, h1⟩ := hinf + have .lam (ty' := dom') (body' := body') domty hdom hbody := h1 + exact main .rfl domty hdom _ hbody + · subst ei + refine (inferType.WF' ?_ hinf).bind fun ty _ _ ⟨e', ty', hb, h1, h2, h3⟩ => ?_ + · apply hr.instantiateList; simp [← eqfvs]; exact m.fvarRevList_prefix.subset + refine .stateWF fun wf => .getLCtx <| .pure ?_ + have ⟨_, h2', e2⟩ := h2.trExpr c.Ewf.ordered wf.trctx.wf + |>.cheapBetaReduce c.Ewf wf.trctx.wf m.noBV + have h3 := h3.defeqU_r c.Ewf mwf.1.tr.wf.toCtx e2.symm + let ⟨h1', h2''⟩ := mwf.1.mkLambda_trS c.Ewf h1 h3 n hn + have h3' := (mwf.1.mkForall_trS c.Ewf h2' (h3.isType c.Ewf mwf.1.tr.wf.toCtx) n hn).1 + simp [hdrop] at h1' h2'' h3' + refine mwf.1.mkForall_eq _ _ (eqfvs ▸ harr) ▸ + ⟨_, _, fun P hP he => ?_, he₀ ▸ h1', h3', h2''⟩ + have ⟨c1, c2, c3⟩ := hbelow _ hP he + have := c3 _ <| FVarsBelow.cheapBetaReduce (m.noBV ▸ h2.closed) _ c1 <| hb _ c1 c2 + exact this.mp (fun _ => id) h3'.fvarsIn + +theorem inferLambda.WF + (h1 : e.FVarsIn (· ∈ c.vlctx.fvars)) + (hinf : inferOnly = true → ∃ e', c.TrExprS e e') : + (inferLambda e inferOnly).WF c s fun ty _ => ∃ e' ty', c.TrTyping e ty e' ty' := by + refine .stateWF fun wf => ?_ + refine (c.withMLC_self ▸ inferLambda.loop.WF (Nat.zero_le _) rfl rfl rfl rfl ?_ h1) hinf + exact fun P hP he => ⟨(AllAbove.wf wf.trctx.wf.fvwf).2 hP, he.mono fun _ h _ => h, fun _ => id⟩ + +theorem inferForall.loop.WF {c : VContext} {e₀ : Expr} + {m} [mwf : c.MLCWF m] {n} (hn : n ≤ m.length) + (hdrop : m.dropN n hn = c.mlctx) + (harr : arr.toList.reverse = (m.fvarRevList n hn).map .fvar) + (he₀ : e₀ = m.mkForall n hn ei) + (hei : e.instantiateList ((m.fvarRevList n hn).map .fvar) = ei) + (hr : e.FVarsIn (· ∈ m.vlctx.fvars)) + (hus : us.toList.reverse.Forall₂ (VLevel.ofLevel c.lparams · = some ·) us') + (hΔ : m.vlctx.SortList c.venv c.lparams.length us') + (hlen : us'.length = n) + (hinf : inferOnly = true → ∃ e', (c.withMLC m).TrExprS ei e') : + (inferForall.loop inferOnly arr us e).WF (c.withMLC m) s fun ty _ => + ∃ e' u, c.TrTyping e₀ ty e' (.sort u) := by + unfold inferForall.loop + generalize eqfvs : (m.fvarRevList n hn).map Expr.fvar = fvs at * + simp [harr, -bind_pure_comp]; split + · rename_i name dom body bi + rw [Expr.instantiateList_forallE] at hei; subst ei + refine (inferType.WF' ?_ ?_).bind fun uv _ le ⟨dom', uv', _, h1, h2, h3⟩ => ?_ + · apply hr.1.instantiateList; simp [← eqfvs]; exact m.fvarRevList_prefix.subset + · intro h; let ⟨_, .forallE _ _ h _⟩ := hinf h; exact ⟨_, h⟩ + refine (ensureSortCore.WF h2).bind_le le fun _ _ le ⟨h4, h5, _⟩ => ?_ + obtain ⟨_, rfl⟩ := h4; let ⟨_, .sort h4, h5⟩ := h5 + refine .stateWF fun wf => ?_ + have domty := h3.defeqU_r c.Ewf mwf.1.tr.wf.toCtx h5.symm + have domty' : (c.withMLC m).IsType dom' := ⟨_, domty⟩ + refine .withLocalDecl h1 domty' le fun a mwf' s' le₂ res => ?_ + have eq := @Expr.instantiateList_instantiate1_comm body fvs (.fvar a) (by trivial) + refine inferForall.loop.WF (Nat.succ_le_succ hn) (by simp [hdrop]) + (by simp [eqfvs, harr]) ?_ (by simp [eqfvs]; rfl) (hr.2.mono fun _ => .tail _) + (by simpa using ⟨h4, hus⟩) (.cons hΔ domty) (by simp [hlen]) ?_ + · simp [he₀, ← eq]; congr 2 + refine (FVarsIn.abstract_instantiate1 ((hr.2.instantiateList ?_ _).mono ?_)).symm + · simp [← eqfvs, FVarsIn]; exact m.fvarRevList_prefix.subset + · rintro _ h rfl; exact (mwf'.1.tr.wf.2.1 _ _ rfl).1 h + · intro h; let ⟨_, .forallE (body' := body') _ _ hdom₁ hbody₁⟩ := hinf h + refine have hΔ := .refl c.Ewf mwf.1.tr.wf; have H := hdom₁.uniq c.Ewf hΔ h1; ?_ + have H := H.of_r c.Ewf mwf.1.tr.wf.toCtx domty + have ⟨_, hbody₂⟩ := hbody₁.defeqDFC c.Ewf <| .cons hΔ (ofv := none) nofun (.vlam H) + exact eq ▸ ⟨_, hbody₂.inst_fvar c.Ewf.ordered mwf'.1.tr.wf⟩ + · subst ei; refine (inferType.WF' ?_ hinf).bind fun ty _ _ ⟨e', ty', _, h1, h2, h3⟩ => ?_ + · apply hr.instantiateList; simp [← eqfvs]; exact m.fvarRevList_prefix.subset + refine (ensureSortCore.WF h2).bind fun _ _ le₂ ⟨h4, h5, _⟩ => ?_ + obtain ⟨_, rfl⟩ := h4; let ⟨_, .sort (u' := u') h4, h5⟩ := h5 + obtain ⟨us, rfl⟩ : ∃ l, ⟨List.reverse l⟩ = us := ⟨us.toList.reverse, by simp⟩ + simp [Expr.sortLevel!] at hus ⊢ + have h3 := h3.defeqU_r c.Ewf mwf.1.tr.wf.toCtx h5.symm + let ⟨h1', h2'⟩ := mwf.1.mkForall_trS c.Ewf h1 ⟨_, h3⟩ n hn + have ⟨_, h3', h4'⟩ := mkForall_hasType hus hΔ h4 h3 n hn (hus.length_eq.trans hlen) + simp [hdrop] at h1' h2' h4' + refine have h := .sort h3'; .pure ⟨_, _, fun _ _ _ => h.fvarsIn, he₀ ▸ h1', h, h4'⟩ + +theorem inferForall.WF + (hr : e.FVarsIn (· ∈ c.vlctx.fvars)) + (hinf : inferOnly = true → ∃ e', c.TrExprS e e') : + (inferForall e inferOnly).WF c s fun ty _ => ∃ e' u, c.TrTyping e ty e' (.sort u) := + (c.withMLC_self ▸ inferForall.loop.WF (Nat.zero_le _) rfl rfl rfl rfl hr .nil .nil rfl) hinf + +theorem inferApp.loop.WF {c : VContext} {s : VState} + {ll lm lr : List _} + (stk : AppStack c.venv c.lparams c.vlctx (.mkAppRevList e lm) e' lr) + (hbelow : FVarsBelow c.vlctx e fType) + (hfty : c.TrExpr (fType.instantiateList lm) fty') (hety : c.HasType e' fty') + (hargs : args = ll ++ lm.reverse ++ lr) + (hj : j = ll.length) (hi : i = ll.length + lm.length) : + RecM.WF c s (inferApp.loop e₀ ⟨args⟩ fType j i) fun ty _ => + ∃ e₁' ty', c.TrTyping (e.mkAppRevList lm |>.mkAppList lr) ty e₁' ty' := by + subst i j; rw [inferApp.loop.eq_def] + simp [hargs, Expr.instantiateList_reverse] + have henv := c.Ewf; have hΔ := c.Δwf + cases lr with simp + | cons a lr => + let .app hf' ha' hf ha stk := stk + have uf := hf'.uniqU henv hΔ hety + split + · rw [Expr.instantiateList_forallE] at hfty + let ⟨_, .forallE _ _ hty hbody, h3⟩ := hfty + have ⟨⟨_, uA⟩, _, uB⟩ := h3.trans henv hΔ uf.symm |>.forallE_inv henv hΔ + refine inferApp.loop.WF (lm := a::lm) stk ?_ ?_ (.app hf' ha') (by simp) rfl rfl + · exact fun _ hP he => (hbelow _ hP he).2 + have ha0 := c.mlctx.noBV ▸ ha.closed + simp [← Expr.instantiateList_instantiate1_comm ha0.looseBVarRange_zero] + exact .inst henv hΔ (ha'.defeqU_r henv hΔ ⟨_, uA.symm⟩) ⟨_, hbody, _, uB⟩ (ha.trExpr henv hΔ) + · simp [Nat.add_sub_cancel_left, Expr.instantiateRevList_reverse] + refine (ensureForallCore.WF' hfty).bind fun _ _ _ ⟨hb, ⟨_, h2, h3⟩, eq⟩ => ?_ + obtain ⟨name, ty, body, bi, rfl⟩ := eq; simp [Expr.bindingBody!] + let .forallE _ _ hty hbody := h2 + have ⟨⟨_, uA⟩, _, uB⟩ := h3.trans henv hΔ uf.symm |>.forallE_inv henv hΔ + refine inferApp.loop.WF (ll := ll ++ lm.reverse) (lm := [a]) stk ?_ ?_ + (.app hf' ha') (by simp) (by simp) (by simp) + · intro _ hP he + have ⟨he, hlm⟩ := FVarsIn.appRevList.1 he + exact (hb _ hP <| (hbelow _ hP he).instantiateList hlm).2 + exact .inst henv hΔ (ha'.defeqU_r henv hΔ ⟨_, uA.symm⟩) ⟨_, hbody, _, uB⟩ (ha.trExpr henv hΔ) + | nil => + rw [← List.length_reverse, List.take_length, Expr.instantiateRevList_reverse] + have ⟨_, hfty, h2⟩ := hfty + refine .pure ⟨_, _, fun _ hP he => ?_, stk.tr, hfty, hety.defeqU_r henv hΔ h2.symm⟩ + have ⟨he, hlm⟩ := FVarsIn.appRevList.1 he + exact (hbelow _ hP he).instantiateList hlm + +theorem inferApp.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + RecM.WF c s (inferApp e) fun ty _ => ∃ ty', c.TrTyping e ty e' ty' := by + rw [inferApp, Expr.withApp_eq, Expr.getAppArgs_eq] + have ⟨_, he'⟩ := AppStack.build <| e.mkAppList_getAppArgsList ▸ he + refine (inferType.WF he'.tr).bind fun ty _ _ ⟨ty', hb, _, hty', ety⟩ => ?_ + have henv := c.Ewf; have hΔ := c.Δwf + refine (inferApp.loop.WF (ll := []) (lm := []) he' hb + (hty'.trExpr henv hΔ) ety rfl rfl rfl).le + fun _ _ _ ⟨_, _, hb, h1, h2, h3⟩ => ?_ + have := (e.mkAppList_getAppArgsList ▸ h1).uniq henv (.refl henv hΔ) he + exact ⟨_, e.mkAppList_getAppArgsList ▸ hb, he, h2, h3.defeqU_l henv hΔ this⟩ + +theorem inferLet.loop.WF {c : VContext} {e₀ : Expr} + {m} [mwf : c.MLCWF m] {n} (hn : n ≤ m.length) (nds hnds) + (hdrop : m.dropN n hn = c.mlctx) + (harr : arr.toList.reverse = (m.fvarRevList n hn).map .fvar) + (he₀ : e₀ = m.mkLet n hn nds hnds ei) + (hei : e.instantiateList ((m.fvarRevList n hn).map .fvar) = ei) + (hbelow : ∀ P, IsFVarUpSet P c.vlctx → FVarsIn P e₀ → + IsFVarUpSet (AllAbove c.vlctx P) m.vlctx ∧ FVarsIn (AllAbove c.vlctx P) ei ∧ + ∀ ty, FVarsIn (AllAbove c.vlctx P) ty → FVarsIn (AllAbove c.vlctx P) (m.mkForall n hn ty)) + (hr : e.FVarsIn (· ∈ m.vlctx.fvars)) + (hinf : inferOnly = true → ∃ e', (c.withMLC m).TrExprS ei e') : + (inferLet.loop inferOnly arr e).WF (c.withMLC m) s fun ty _ => + ∃ e' ty', c.TrTyping e₀ ty e' ty' := by + generalize eqfvs : (m.fvarRevList n hn).map Expr.fvar = fvs at * + unfold inferLet.loop + simp [harr, -bind_pure_comp]; split + · rename_i name dom val body nd + generalize eqF : withLetDecl (m := RecM) _ _ _ _ = F + generalize eqP : (fun ty x => ∃ _, _) = P + rw [Expr.instantiateList_letE] at hei; subst ei + have main {s₁} (le₁ : s ≤ s₁) {dom' val'} + (hdom : (c.withMLC m).TrExprS (dom.instantiateList fvs) dom') + (hval : (c.withMLC m).TrExprS (val.instantiateList fvs) val') + (valty : (c.withMLC m).venv.HasType + (c.withMLC m).lparams.length (c.withMLC m).vlctx.toCtx val' dom') + (hbody : inferOnly = true → ∃ body', + TrExprS c.venv c.lparams ((none, .vlet dom' val') :: m.vlctx) + (body.instantiateList fvs 1) body') : + F.WF (c.withMLC m) s₁ P := by + refine .stateWF fun wf => ?_ + have hdom' := hdom.trExpr c.Ewf mwf.1.tr.wf + subst eqF eqP + refine .withLetDecl hdom hval valty le₁ fun a mwf' s' le₂ res => ?_ + have eq := @Expr.instantiateList_instantiate1_comm body fvs (.fvar a) (by trivial) + refine inferLet.loop.WF (Nat.succ_le_succ hn) (some nd :: nds) + (by simp [hnds]) (by simp [hdrop]) (by simp [← eqfvs, harr]) + ?_ (by simp; rfl) ?_ (hr.2.2.mono fun _ => .tail _) ?_ + · rw [he₀, eqfvs, ← eq]; simp [MLCtx.mkLetArg]; congr 2 + refine (FVarsIn.abstract_instantiate1 ((hr.2.2.instantiateList ?_ _).mono ?_)).symm + · simp [← eqfvs, FVarsIn]; exact m.fvarRevList_prefix.subset + · rintro _ h rfl; exact (mwf'.1.tr.wf.2.1 _ _ rfl).1 h + · intro P hP he + have ⟨h1, h2, h3⟩ := hbelow _ hP he + refine ⟨⟨h1, fun _ => ?_⟩, ?_, fun ty hty => h3 _ ?_⟩ + · simp [or_imp, forall_and] + exact ⟨(fvarsIn_iff.1 h2.1).1, (fvarsIn_iff.1 h2.2.1).1⟩ + · rw [eqfvs, ← eq] + refine h2.2.2.instantiate1 fun h => ?_ + exact res.elim (wf.ngen_wf _ (m.dropN_fvars_subset n hn (hdrop ▸ h))) + · simp; split <;> rename_i h + · exact ⟨h2.1, h2.2.1, hty.abstract1⟩ + · rw [Expr.lowerLooseBVars_eq_instantiate (v := .sort .zero) (by simpa using h)] + exact hty.abstract1.instantiate1 rfl + · intro h; let ⟨_, hbody⟩ := hbody h + exact eqfvs.symm ▸ eq ▸ ⟨_, hbody.inst_fvar c.Ewf.ordered mwf'.1.tr.wf⟩ + split + · subst inferOnly + refine (checkType.WF ?_).bind fun uv _ le ⟨dom', uv', _, h1, h2, h3⟩ => ?_ + · apply hr.1.instantiateList; simp [← eqfvs]; exact m.fvarRevList_prefix.subset + refine (ensureSortCore.WF h2).bind + fun _ _ le₂ ⟨h4, h5, _⟩ => ?_ + obtain ⟨_, rfl⟩ := h4; let ⟨_, .sort _, h5⟩ := h5; have le := le.trans le₂ + refine (checkType.WF ?_).bind_le le fun ty _ le ⟨val', ty', _, h4, h5, h6⟩ => ?_ + · apply hr.2.1.instantiateList; simp [← eqfvs]; exact m.fvarRevList_prefix.subset + refine (isDefEq.WF h5 h1).bind_le le fun b _ le h7 => ?_ + cases b <;> simp + · exact .getEnv <| .getLCtx .throw + have valty := h6.defeqU_r c.Ewf mwf.1.tr.wf.toCtx (h7 rfl) + exact main le h1 h4 valty nofun + · simp_all; let ⟨_, h1⟩ := hinf + have .letE (ty' := dom') (body' := body') valty hdom hval hbody := h1 + exact main .rfl hdom hval valty _ hbody + · subst ei; refine (inferType.WF' ?_ hinf).bind fun ty _ _ ⟨e', ty', hb, h1, h2, h3⟩ => ?_ + · apply hr.instantiateList; simp [← eqfvs]; exact m.fvarRevList_prefix.subset + refine .stateWF fun wf => .getLCtx <| .pure ?_ + have ⟨_, hty, e2⟩ := h2.trExpr c.Ewf.ordered wf.trctx.wf + |>.cheapBetaReduce c.Ewf wf.trctx.wf m.noBV + have h3 := h3.defeqU_r c.Ewf mwf.1.tr.wf.toCtx e2.symm + let ⟨h1', h2'⟩ := mwf.1.mkLet_trS c.Ewf h1 h3 n hn nds hnds + have h3' := (mwf.1.mkForall_trS c.Ewf hty (h3.isType c.Ewf mwf.1.tr.wf.toCtx) n hn).1 + simp [hdrop] at h1' h2' h3' + erw [mwf.1.mkForall_eq _ _ (eqfvs ▸ harr)] + refine ⟨_, _, fun P hP he => ?_, he₀ ▸ h1', h3', h2'⟩ + have ⟨c1, c2, c3⟩ := hbelow _ hP he + have := c3 _ <| FVarsBelow.cheapBetaReduce (m.noBV ▸ h2.closed) _ c1 <| hb _ c1 c2 + refine this.mp (fun _ => id) h3'.fvarsIn +termination_by e + +theorem inferLet.WF + (hr : e.FVarsIn (· ∈ c.vlctx.fvars)) + (hinf : inferOnly = true → ∃ e', c.TrExprS e e') : + (inferLet e inferOnly).WF c s fun ty _ => + ∃ e' ty', c.TrTyping e ty e' ty' := by + refine .stateWF fun wf => ?_ + refine (c.withMLC_self ▸ inferLet.loop.WF (Nat.zero_le _) [] rfl rfl rfl rfl rfl ?_ hr) hinf + exact fun P hP he => ⟨(AllAbove.wf wf.trctx.wf.fvwf).2 hP, he.mono fun _ h _ => h, fun _ => id⟩ + +theorem AppStack.toSpineWF {c : VContext} + (H : AppStack c.venv c.lparams c.vlctx f f' args) + (hf : c.HasType f' (VExpr.forallN As C)) + (hlen : args.length = As.length) : + ∃ args', args.Forall₂ (c.TrExprS · ·) args' ∧ + c.venv.SpineWF c.lparams.length c.vlctx.toCtx + (VExpr.forallN As C) args' (VExpr.instRev C args') ∧ + c.TrExprS (f.mkAppList args) (VExpr.appN f' args') := by + induction args generalizing f f' As C with + | nil => + cases As with + | nil => + let .head hfull := H + exact ⟨[], .nil, .nil, by simpa⟩ + | cons _ _ => simp at hlen + | cons arg args ih => + cases As with + | nil => simp at hlen + | cons A As => + let .app hfun harg hf' harg' Hrest := H + have htypes := hf.uniqU c.Ewf c.Δwf hfun + have ⟨⟨_, hA⟩, _⟩ := htypes.forallE_inv c.Ewf c.Δwf + have hargA := harg.defeqU_r c.Ewf c.Δwf ⟨_, hA.symm⟩ + have hlen' : args.length = As.length := by simpa using hlen + have htailType : c.HasType (.app f' _) ((VExpr.forallN As C).inst _) := + hf.app hargA + rw [VExpr.instN_forallN] at htailType + obtain ⟨args', hargs', hspine, hfull⟩ := + ih Hrest htailType (by simpa [VExpr.instTelN_length] using hlen') + refine ⟨_ :: args', .cons harg' hargs', .cons hargA ?_, ?_⟩ + have hlenArgsAs : args'.length = As.length := + hargs'.length_eq.symm.trans hlen' + rw [VExpr.instN_forallN] + simpa [VExpr.instRev, hlenArgsAs] using hspine + simpa [Expr.mkAppList, VExpr.appN] using hfull + +theorem invalidProj.WF {c : VContext} {s : VState} : + (invalidProj e : RecM α).WF c s Q := by + unfold invalidProj + exact .getEnv <| .getLCtx .throw + +theorem inferProjParams.WF {c : VContext} {s : VState} + (hargs : args.Forall₂ (c.TrExprS · ·) args') + (hrBelow : c.FVarsBelow proj r) + (hargsBelow : ∀ arg ∈ args, c.FVarsBelow proj arg) + (hr : c.TrExpr r R) + (hspine : c.venv.SpineWF c.lparams.length c.vlctx.toCtx + R args' T) : + (inferProjParams proj args r).WF c s fun out _ => + c.FVarsBelow proj out ∧ c.TrExpr out T := by + induction hargs generalizing r R s with + | nil => + simp [inferProjParams] at hspine ⊢ + exact hspine.nil_inv ▸ .pure ⟨hrBelow, hr⟩ + | @cons arg arg' args args' harg hargs ih => + simp only [inferProjParams] + have hargBelow := hargsBelow arg (by simp) + have hargsBelow' : ∀ arg ∈ args, c.FVarsBelow proj arg := by + intro arg harg + exact hargsBelow arg (by simp [harg]) + obtain ⟨A, B, rfl, hargType, hrest⟩ := hspine.cons_inv + obtain ⟨r', hrS, hrEq⟩ := hr + refine (whnf.WF hrS).bind fun out s' _ + ⟨houtBelow, ⟨out', hout, houtEq⟩⟩ => ?_ + have houtEq := houtEq.trans c.Ewf c.Δwf hrEq + cases out with + | forallE name dom body bi => + let .forallE hdomTy hbodyTy hdom hbody := hout + have hforallEq := houtEq.forallE_inv c.Ewf c.Δwf + obtain ⟨⟨_, hdomEq⟩, _, hbodyEq⟩ := hforallEq + have hargType' := hargType.defeqU_r c.Ewf c.Δwf + ⟨_, hdomEq.symm⟩ + have hnext : c.TrExpr (body.instantiate1 arg) (B.inst arg') := by + simpa only [Expr.instantiate1_eq] using + (.inst c.Ewf c.Δwf hargType' + ⟨_, hbody, _, hbodyEq⟩ (harg.trExpr c.Ewf c.Δwf)) + have hnextBelow : c.FVarsBelow proj (body.instantiate1 arg) := by + intro P hP hproj + have houtFVars := (hrBelow.trans houtBelow) P hP hproj + simpa only [Expr.instantiate1_eq] using + houtFVars.2.instantiate1 (hargBelow P hP hproj) + exact ih hnextBelow hargsBelow' hnext hrest + | bvar | fvar | mvar | sort | const | app | lam | letE | lit | + mdata | proj => exact invalidProj.WF + +theorem inferProjFields.WF {c : VContext} {s : VState} + {view : VStructureView} {levels : List VLevel} + {params : List VExpr} {major : VExpr} {tailResult cursor : VExpr} + (hstruct : c.TrExprS struct major) + (hview : view.WF c.venv) + (hlevels : ∀ level ∈ levels, level.WF c.lparams.length) + (hlevelsLength : levels.length = view.uvars) + (hparamsLength : params.length = view.nparams) + (hparamsSpine : ∃ resultLevel, + c.venv.SpineWF c.lparams.length c.vlctx.toCtx + (view.familyType.instL levels) params (.sort resultLevel)) + (hprograms : view.ProgramsWF c.venv) + (hname : view.name = typeName) + (hmajor : c.HasType major (view.structureType levels params)) + (hrBelow : c.FVarsBelow proj r) + (hstructBelow : c.FVarsBelow proj struct) + (hbound : fieldIdx + count < + (view.specializedFields levels params).length) + (hr : c.TrExpr r cursor) + (hcursor : VExpr.consumeForalls? + (VExpr.forallN (view.specializedFields levels params) tailResult) + (view.projectionArgs levels params fieldIdx major) = some cursor) : + (inferProjFields proj typeName struct maybePropType fieldIdx count r).WF + c s fun out _ => + ∃ cursor', + VExpr.consumeForalls? + (VExpr.forallN (view.specializedFields levels params) tailResult) + (view.projectionArgs levels params (fieldIdx + count) major) = + some cursor' ∧ + c.FVarsBelow proj out ∧ c.TrExpr out cursor' := by + induction count generalizing s r cursor fieldIdx with + | zero => + simp only [inferProjFields, Nat.add_zero] + exact .pure ⟨cursor, hcursor, hrBelow, hr⟩ + | succ count ih => + simp only [inferProjFields] + have hfieldIdx : fieldIdx < + (view.specializedFields levels params).length := by omega + have hcodeIdx : fieldIdx < + (view.projectionCodes levels params).length := by + simpa using hfieldIdx + let code := (view.projectionCodes levels params)[fieldIdx] + have hcode : + (view.projectionCodes levels params)[fieldIdx]? = some code := + List.getElem?_eq_getElem hcodeIdx + have hargsLength : + (view.projectionArgs levels params fieldIdx major).length = + fieldIdx := + view.projectionArgs_length levels params fieldIdx major + (Nat.le_of_lt hcodeIdx) + obtain ⟨field, semanticBody, hfield, hconsume⟩ := + VExpr.consumeForalls?_forallN_domain + (view.specializedFields levels params) tailResult + (view.projectionArgs levels params fieldIdx major) + (by simpa [hargsLength] using hfieldIdx) + rw [hargsLength] at hfield + have hcursorShape : cursor = + .forallE + (field.instRevAt + (view.projectionArgs levels params fieldIdx major) 0) + semanticBody := + Option.some.inj (hcursor.symm.trans hconsume) + subst cursor + obtain ⟨field', typeBody, hfield', htypeFn, + hprojectorField⟩ := + hprograms.projector_hasType_field c.Ewf c.Δwf hlevels + hlevelsLength hparamsLength hparamsSpine hcode hmajor + have hfieldEq : field' = field := + Option.some.inj (hfield'.symm.trans hfield) + subst field' + have hprojector := hprograms c.Δwf hlevels hlevelsLength + hparamsLength hparamsSpine hcode + have hprojSem : c.venv.TrProj c.lparams.length c.vlctx.toCtx + view levels params fieldIdx major (.app code.projector major) := { + viewWF := hview + levelsWF := hlevels + levels_length := hlevelsLength + params_length := hparamsLength + paramsSpine := hparamsSpine + majorType := hmajor + program := ⟨code, hcode, rfl, hprojector⟩ } + have hprojStrict : c.TrExprS (.proj typeName fieldIdx struct) + (.app code.projector major) := + .proj hstruct ⟨view, levels, params, hname, hprojSem⟩ + obtain ⟨r', hrS, hrEq⟩ := hr + refine (whnf.WF hrS).bind fun out nextState _ + ⟨houtBelow, ⟨out', hout, houtEq⟩⟩ => ?_ + have houtEq := houtEq.trans c.Ewf c.Δwf hrEq + cases out with + | forallE name dom body bi => + let .forallE hdomTy hbodyTy hdom hbody := hout + have hforallEq := houtEq.forallE_inv c.Ewf c.Δwf + obtain ⟨⟨_, hdomEq⟩, _, hbodyEq⟩ := hforallEq + have hprojectorField' := hprojectorField.defeqU_r + c.Ewf c.Δwf ⟨_, hdomEq.symm⟩ + have hnext : c.TrExpr + (body.instantiate1 (.proj typeName fieldIdx struct)) + (semanticBody.inst (.app code.projector major)) := by + simpa only [Expr.instantiate1_eq] using + (.inst c.Ewf c.Δwf hprojectorField' + ⟨_, hbody, _, hbodyEq⟩ + (hprojStrict.trExpr c.Ewf.ordered c.Δwf)) + have hnextBelow : c.FVarsBelow proj + (body.instantiate1 (.proj typeName fieldIdx struct)) := by + intro P hP hproj + have houtFVars := (hrBelow.trans houtBelow) P hP hproj + have hfieldProj : FVarsIn P + (.proj typeName fieldIdx struct) := by + simpa [FVarsIn] using hstructBelow P hP hproj + simpa only [Expr.instantiate1_eq] using + houtFVars.2.instantiate1 hfieldProj + have hconsumeNext : VExpr.consumeForalls? + (VExpr.forallN (view.specializedFields levels params) tailResult) + (view.projectionArgs levels params (fieldIdx + 1) major) = + some (semanticBody.inst (.app code.projector major)) := by + rw [view.projectionArgs_succ levels params fieldIdx major hcode] + rw [VExpr.consumeForalls?_append, hconsume] + rfl + have hbound' : fieldIdx + 1 + count < + (view.specializedFields levels params).length := by omega + have hrec (recState : VState) := + ih (s := recState) hnextBelow hbound' hnext hconsumeNext + simp only + split + · refine (isProp.WF hdom).bind fun _ propState _ _ => ?_ + split + · exact invalidProj.WF + · simpa only [pure_bind, Nat.add_assoc, Nat.add_left_comm, + Nat.add_comm] using hrec propState + · simpa only [pure_bind, Nat.add_assoc, Nat.add_left_comm, + Nat.add_comm] using hrec nextState + | bvar | fvar | mvar | sort | const | app | lam | letE | lit | + mdata | proj => exact invalidProj.WF + +theorem inferProj.WF + (heBelow : c.FVarsBelow e ety) + (he : c.TrExprS e e') (hty : c.TrExprS ety ety') + (hasty : c.HasType e' ety') : + (inferProj st i e ety).WF c s fun ty _ => + ∃ proj' ty', c.TrTyping (.proj st i e) ty proj' ty' := by + unfold inferProj + refine (whnf.WF hty).bind fun type _ _ ⟨htypeBelow, htype⟩ => ?_ + have hprojBelowType : c.FVarsBelow (.proj st i e) type := by + intro P hP hproj + exact htypeBelow P hP (heBelow P hP (by simpa [FVarsIn] using hproj)) + obtain ⟨type', htypeS, htypeEq⟩ := htype + rw [Expr.withApp_eq] + have ⟨family', hstack⟩ := AppStack.build + (type.mkAppList_getAppArgsList ▸ htypeS) + simp only + split + · rename_i familyName familyLevels hfamilyShape + refine .getEnv ?_ + split + · exact invalidProj.WF + · rename_i hname + refine (M.WF.liftExcept envGet.WF).lift.bind fun ci _ _ hfind => ?_ + split + · rename_i info + split + · rename_i constructor hctors + split + · rename_i hready + split + · exact invalidProj.WF + · rename_i hargs + obtain ⟨artifact⟩ := + c.projectionReady.infer familyName info hfind hready + have hhead := hstack.tr + rw [hfamilyShape] at hhead + let .const (us' := levels') hfamilyConst hlevelsMap + hlevelsLength := hhead + have hviewFamily := artifact.viewWF.family + rw [artifact.name_eq] at hviewFamily + rw [hviewFamily] at hfamilyConst + cases hfamilyConst + have hlevelsWF : ∀ level ∈ levels', + level.WF c.lparams.length := + VLevel.WF.of_mapM_ofLevel hlevelsMap + have hlevelsSourceLength : levels'.length = + artifact.view.generation.block.sourceType.uvars := + (List.mapM_eq_some.1 hlevelsMap).length_eq.symm.trans + hlevelsLength + have hlevelsLength' : levels'.length = artifact.view.uvars := by + exact hlevelsSourceLength.trans + artifact.view.generation.block.sourceType_uvars_eq + have hargsSize : type.getAppArgs.size = + info.numParams + info.numIndices := by + simpa using hargs + have hargsLength : type.getAppArgsList.length = + artifact.view.nparams := by + rw [← Expr.getAppArgs_toList] + simp [hargsSize, + artifact.numParams_eq, artifact.numIndices_eq] + have hfamilyType : c.HasType (.const familyName levels') + (artifact.view.familyType.instL levels') := by + exact VEnv.HasType.const hviewFamily hlevelsWF + hlevelsSourceLength + have hfamilyTypeShape : c.HasType (.const familyName levels') + (VExpr.forallN + (artifact.view.generation.block.rawParams.map + (VExpr.instL levels')) + (artifact.view.generation.block.rawResult.instL + levels')) := by + simpa [VStructureView.familyType, + VInductDecl.NormalizedChecked.rawType_eq, + artifact.view.raw_indices_eq, + VExpr.instL_forallN, VExpr.forallN] using hfamilyType + have hargsRawLength : type.getAppArgsList.length = + (artifact.view.generation.block.rawParams.map + (VExpr.instL levels')).length := by + simpa [artifact.view.generation.shape.1] using hargsLength + obtain ⟨params', hparamsTr, hparamsSpineRaw, htypeFull⟩ := + AppStack.toSpineWF hstack hfamilyTypeShape hargsRawLength + rw [type.mkAppList_getAppArgsList] at htypeFull + have htypeAppliedEq := htypeFull.uniq c.Ewf + (.refl c.Ewf c.Δwf) htypeS + have hmajorType : c.HasType e' + (artifact.view.structureType levels' params') := by + apply hasty.defeqU_r c.Ewf c.Δwf + have := (htypeAppliedEq.trans c.Ewf c.Δwf htypeEq).symm + simpa [VStructureView.structureType, + artifact.name_eq] using this + have hparamsLength : params'.length = + artifact.view.nparams := + hparamsTr.length_eq.symm.trans hargsLength + have hparamsRawLength : params'.length = + (artifact.view.generation.block.rawParams.map + (VExpr.instL levels')).length := + hparamsTr.length_eq.symm.trans hargsRawLength + have hparamsSpine : ∃ resultLevel, + c.venv.SpineWF c.lparams.length c.vlctx.toCtx + (artifact.view.familyType.instL levels') params' + (.sort resultLevel) := by + obtain ⟨resultLevel, hresultLevel⟩ := artifact.rawResult_sort + refine ⟨resultLevel.inst levels', ?_⟩ + rw [hresultLevel] at hparamsSpineRaw + rw [VExpr.instRev_closedN params' (by trivial)] at hparamsSpineRaw + simpa [VStructureView.familyType, + VInductDecl.NormalizedChecked.rawType_eq, + artifact.view.raw_indices_eq, hresultLevel, + VExpr.instL_forallN, VExpr.forallN, + VExpr.instRev, VExpr.instL] using hparamsSpineRaw + have hconstructorName : + constructor = artifact.view.constructorName := by + have := hctors.symm.trans artifact.ctors_eq + simpa using this + refine (M.WF.liftExcept envGet.WF).lift.bind + fun c_info _ _ hctorFind => ?_ + cases c_info with + | ctorInfo ctorInfo => + simp only + split + · rename_i hidxHost + have hctorInfoEq : + ctorInfo = artifact.constructorInfo := by + rw [hconstructorName, artifact.constructor_find] at hctorFind + exact ConstantInfo.ctorInfo.inj + (Option.some.inj hctorFind.symm) + have hiFields : i < + (artifact.view.specializedFields levels' params').length := by + rw [hctorInfoEq, + artifact.constructor_numFields_eq] at hidxHost + simpa [VStructureView.specializedFields, + VStructureView.fields] using hidxHost + have hviewConstructor : c.venv.constants constructor = + some artifact.view.constructor.raw.toVConstant := by + simpa [hconstructorName] using + artifact.viewWF.toRegistered.constructor + obtain ⟨_, hctorTr⟩ := + c.trenv.find?_uniq hctorFind hviewConstructor + have hrawCtorUvars : + artifact.view.constructor.raw.uvars = + artifact.view.uvars := by + exact artifact.view.generation.ctor_uvars_eq + (by simp [artifact.view.constructor_eq]) + have hctorLevelLength : + ctorInfo.levelParams.length = familyLevels.length := + hctorTr.2.1.trans <| hrawCtorUvars.trans <| + hlevelsLength'.symm.trans + (List.mapM_eq_some.1 hlevelsMap).length_eq.symm + have hctorType₀ := hctorTr.2.2.instL c.Ewf + (Us := c.lparams) (ls' := levels') (Δ := []) + trivial hlevelsMap hctorLevelLength + have hctorType := hctorType₀.weakFV c.Ewf + (.from_nil c.mlctx.noBV) c.Δwf + rw [(c.Ewf.ordered.closedC + hviewConstructor).instL.liftN_eq + (Nat.le_refl _)] at hctorType + let ctorTail := VExpr.forallN + (artifact.view.fields.map (VExpr.instL levels')) + ((artifact.view.constructor.rawResult + artifact.view.nparams).instL levels') + rw [artifact.view.constructor.rawType_eq] at hctorType + have hinstantiate : + ((.ctorInfo ctorInfo : ConstantInfo) + |>.instantiateTypeLevelParams familyLevels) = + ctorInfo.type.instantiateLevelParams + ctorInfo.levelParams familyLevels := rfl + have hctorTypeShape : c.TrExpr + ((.ctorInfo ctorInfo : ConstantInfo) + |>.instantiateTypeLevelParams familyLevels) + (VExpr.forallN + (artifact.view.constructorParams.map + (VExpr.instL levels')) ctorTail) := by + simpa [ConstantInfo.instantiateTypeLevelParams, + ConstantVal.instantiateTypeLevelParams, + ConstantInfo.type, ConstantInfo.toConstantVal, ctorTail, + VInductDecl.NormalizedCtor.declaredBinders, + VStructureView.nparams, + VStructureView.constructorParams, + VStructureView.fields, + VExpr.instL_forallN, VExpr.forallN_append, + List.map_append] using hctorType + have hctorTypeBelow : c.FVarsBelow (.proj st i e) + ((.ctorInfo ctorInfo : ConstantInfo) + |>.instantiateTypeLevelParams familyLevels) := by + intro P _ _ + simpa [ConstantInfo.instantiateTypeLevelParams, + ConstantVal.instantiateTypeLevelParams, + ConstantInfo.type, ConstantInfo.toConstantVal] using + hctorType₀.fvarsIn.mono nofun + have hparamArgsEq : + List.take info.numParams type.getAppArgs.toList = + type.getAppArgsList := by + simp [Expr.getAppArgs_toList, artifact.numParams_eq, + ← hargsLength] + have hparamArgsTr : + (List.take info.numParams + type.getAppArgs.toList).Forall₂ + (c.TrExprS · ·) params' := by + simpa [hparamArgsEq] using hparamsTr + have hparamArgsBelow : ∀ arg ∈ + List.take info.numParams type.getAppArgs.toList, + c.FVarsBelow (.proj st i e) arg := by + intro arg harg P hP hproj + apply FVarsIn.getAppArgsList + (hprojBelowType P hP hproj) + simpa [hparamArgsEq] using harg + have hctorParamsSpine := + artifact.viewWF.constructorParamsSpine c.Ewf.ordered + levels' hlevelsWF hlevelsLength' params' hparamsLength + hparamsSpine ctorTail + refine (inferProjParams.WF hparamArgsTr hctorTypeBelow + hparamArgsBelow hctorTypeShape hctorParamsSpine).bind + fun r _ _ hr => ?_ + obtain ⟨hrBelow, hr⟩ := hr + let tailResult := + ((artifact.view.constructor.rawResult + artifact.view.nparams).instL levels').instRevAt + params' artifact.view.fields.length + have hctorTailInst : ctorTail.instRev params' = + VExpr.forallN + (artifact.view.specializedFields levels' params') + tailResult := by + simp [ctorTail, tailResult, + VExpr.instRev_forallN_projection, + VStructureView.specializedFields, + VExpr.instRevAt_map_instL_zipIdx] + rw [hctorTailInst] at hr + refine (getSortLevel.WF htypeS).bind + fun sortLevel nextState _ _ => ?_ + have hstructBelow : c.FVarsBelow (.proj st i e) e := by + intro P _ hproj + simpa [FVarsIn] using hproj + have hcursorZero : VExpr.consumeForalls? + (VExpr.forallN + (artifact.view.specializedFields levels' params') + tailResult) + (artifact.view.projectionArgs levels' params' 0 e') = + some (VExpr.forallN + (artifact.view.specializedFields levels' params') + tailResult) := by + rfl + refine (inferProjFields.WF he artifact.viewWF hlevelsWF + hlevelsLength' hparamsLength hparamsSpine + artifact.programsWF artifact.name_eq hmajorType + hrBelow hstructBelow (by simpa using hiFields) hr + hcursorZero).bind + fun r _ _ hr => ?_ + obtain ⟨cursor, hcursor, hrBelow, hr⟩ := hr + have hcursor' : VExpr.consumeForalls? + (VExpr.forallN + (artifact.view.specializedFields levels' params') + tailResult) + (artifact.view.projectionArgs levels' params' i e') = + some cursor := by + simpa using hcursor + have hcodeIdx : i < + (artifact.view.projectionCodes levels' params').length := by + simpa using hiFields + let code := + (artifact.view.projectionCodes levels' params')[i] + have hcode : + (artifact.view.projectionCodes levels' params')[i]? = + some code := + List.getElem?_eq_getElem hcodeIdx + have hprojectionArgsLength : + (artifact.view.projectionArgs levels' params' i e').length = + i := + artifact.view.projectionArgs_length levels' params' i e' + (Nat.le_of_lt hcodeIdx) + obtain ⟨field, semanticBody, hfield, hconsume⟩ := + VExpr.consumeForalls?_forallN_domain + (artifact.view.specializedFields levels' params') + tailResult + (artifact.view.projectionArgs levels' params' i e') + (by simpa [hprojectionArgsLength] using hiFields) + rw [hprojectionArgsLength] at hfield + have hcursorShape : cursor = + .forallE + (field.instRevAt + (artifact.view.projectionArgs levels' params' i e') 0) + semanticBody := + Option.some.inj (hcursor'.symm.trans hconsume) + subst cursor + have hprograms : artifact.view.ProgramsWF c.venv := + artifact.programsWF + obtain ⟨field', typeBody, hfield', htypeFn, + hprojectorField⟩ := + hprograms.projector_hasType_field + c.Ewf c.Δwf hlevelsWF hlevelsLength' hparamsLength + hparamsSpine hcode hmajorType + have hfieldEq : field' = field := + Option.some.inj (hfield'.symm.trans hfield) + subst field' + have hprojector := hprograms c.Δwf hlevelsWF + hlevelsLength' hparamsLength hparamsSpine hcode + have hprojSem : c.venv.TrProj c.lparams.length + c.vlctx.toCtx artifact.view levels' params' i e' + (.app code.projector e') := { + viewWF := artifact.viewWF + levelsWF := hlevelsWF + levels_length := hlevelsLength' + params_length := hparamsLength + paramsSpine := hparamsSpine + majorType := hmajorType + program := ⟨code, hcode, rfl, hprojector⟩ } + have hst : st = familyName := by + simpa using hname + have hprojStrict : c.TrExprS (.proj st i e) + (.app code.projector e') := + .proj he ⟨artifact.view, levels', params', + artifact.name_eq.trans hst.symm, hprojSem⟩ + obtain ⟨r', hrS, hrEq⟩ := hr + refine (whnf.WF hrS).bind fun out _ _ + ⟨houtBelow, ⟨out', hout, houtEq⟩⟩ => ?_ + have houtEq := houtEq.trans c.Ewf c.Δwf hrEq + cases out with + | forallE name dom body bi => + let .forallE hdomTy hbodyTy hdom hbody := hout + have hforallEq := houtEq.forallE_inv c.Ewf c.Δwf + obtain ⟨⟨_, hdomEq⟩, _, hbodyEq⟩ := hforallEq + have hprojectorField' := hprojectorField.defeqU_r + c.Ewf c.Δwf ⟨_, hdomEq.symm⟩ + have hdomBelow : c.FVarsBelow (.proj st i e) dom := by + intro P hP hproj + exact ((hrBelow.trans houtBelow) P hP hproj).1 + have hresult : ∃ proj' ty', + c.TrTyping (.proj st i e) dom proj' ty' := + ⟨.app code.projector e', _, hdomBelow, hprojStrict, + hdom, hprojectorField'⟩ + simp only + split + · refine (isProp.WF hdom).bind fun _ _ _ _ => ?_ + split + · exact invalidProj.WF + · exact .pure hresult + · exact .pure hresult + | bvar | fvar | mvar | sort | const | app | lam | letE | + lit | mdata | proj => exact invalidProj.WF + · exact invalidProj.WF + | axiomInfo | defnInfo | thmInfo | opaqueInfo | quotInfo | + inductInfo | recInfo => exact invalidProj.WF + · exact invalidProj.WF + · exact invalidProj.WF + · exact invalidProj.WF + · exact invalidProj.WF + +theorem literal_is_primitive (H : n = ``Nat ∨ n = ``Char.ofNat ∨ n = ``String.ofList) : + Environment.primitives.contains n := by + simp [Environment.primitives, NameSet.ofList] + obtain rfl|rfl|rfl := H <;> simp +decide [NameSet.contains] + +theorem infer_literal {c : VContext} (H : c.venv.ContainsLits l) : + c.TrTyping (.lit l) l.type (.trLiteral l) (.const l.typeName []) := by + refine + have := TrExprS.trLiteral c.Ewf c.hasPrimitives l H + ⟨fun _ _ _ => .litType, this.1, ?_, this.2⟩ + rw [← Literal.mkConst_typeName] + have ⟨_, h⟩ := this.2.isType c.Ewf c.Δwf + have ⟨_, h1, _, h3⟩ := h.const_inv c.Ewf c.Δwf + exact .const h1 rfl h3 + +theorem infer_sort {c : VContext} (H : VLevel.ofLevel c.lparams u = some u') : + c.TrTyping (.sort u) (.sort u.succ) (.sort u') (.sort u'.succ) := by + refine ⟨fun _ _ _ => (?a).fvarsIn, .sort H, ?a, .sort (.of_ofLevel H)⟩ + exact .sort <| by simpa [VLevel.ofLevel] + +theorem inferType'.WF + (h1 : e.FVarsIn (· ∈ c.vlctx.fvars)) + (hinf : inferOnly = true → ∃ e', c.TrExprS e e') : + (inferType' e inferOnly).WF c s fun ty _ => ∃ e' ty', c.TrTyping e ty e' ty' := by + unfold inferType'; lift_lets; intro F F1 + split <;> [exact .throw; refine .get <| .get ?_] + split + · rename_i h; refine .stateWF fun wf => .pure ?_ + generalize hic : cond .. = ic at h + have : ic.WF c s := by + subst ic; cases inferOnly <;> [exact wf.inferTypeC_wf; exact wf.inferTypeI_wf] + exact (this h).2.2.2.2 h1 + generalize hP : (fun _ (_ : VState) => _) = P + have hF {ty e' ty' s} (H : c.TrTyping e ty e' ty') : (F ty).WF c s P := by + rintro _ mwf wf a s' ⟨⟩ + refine let s' := _; ⟨s', rfl, ?_⟩ + have hic {ic} (hic : InferCache.WF c s ic) : InferCache.WF c s (ic.insert e ty) := by + intro _ _ h + rw [Std.HashMap.getElem?_insert] at h; split at h <;> [cases h; exact hic h] + rename_i eq + refine .mk c.mlctx.noBV (.eqv H eq BEq.rfl) (.eqv eq ?_) ?_ + · exact H.2.1.fvarsIn.mono wf.ngen_wf + · exact H.2.2.1.fvarsIn.mono wf.ngen_wf + subst P; revert s'; cases inferOnly <;> (dsimp -zeta; intro s'; refine ⟨.rfl, ?_, _, _, H⟩) + · exact { wf with inferTypeC_wf := hic wf.inferTypeC_wf } + · exact { wf with inferTypeI_wf := hic wf.inferTypeI_wf } + split + · extract_lets G1 + refine (checkLitSize.WF (Q := fun _ s' => s' = _) rfl).bind fun _ _ _ h => ?_ + subst h; split <;> [split; skip] + · refine .getEnv <| (M.WF.liftExcept envGet.WF).lift.bind fun _ _ _ h => ?_ + have ⟨_, h, _⟩ := c.trenv.find? h <| + (c.safePrimitives h (literal_is_primitive (.inl rfl))).1 ▸ DefinitionSafety.le_safe + exact hF (infer_literal ⟨_, h⟩) + · refine .getEnv <| (M.WF.liftExcept envGet.WF).lift.bind fun _ _ _ h1 => ?_ + refine .getEnv <| (M.WF.liftExcept envGet.WF).lift.bind fun _ _ _ h2 => ?_ + have ⟨_, h1, _⟩ := c.trenv.find? h1 <| + (c.safePrimitives h1 (literal_is_primitive (.inr (.inl rfl)))).1 ▸ DefinitionSafety.le_safe + have ⟨_, h2, _⟩ := c.trenv.find? h2 <| + (c.safePrimitives h2 (literal_is_primitive (.inr (.inr rfl)))).1 ▸ DefinitionSafety.le_safe + exact hF (infer_literal ⟨⟨_, h1⟩, ⟨_, h2⟩⟩) + · rename_i h; have ⟨_, h⟩ := hinf (by simpa using h) + have := h.lit_has_type + simp [G1]; exact hF (infer_literal this) + · refine (inferType'.WF (by exact h1) ?_).bind fun _ _ _ ⟨_, _, hb, h1, h⟩ => ?_ + · exact fun h => let ⟨_, .mdata h⟩ := hinf h; ⟨_, h⟩ + exact hF ⟨hb, .mdata h1, h⟩ + · refine (inferType'.WF (by exact h1) ?_).bind fun _ _ _ ⟨_, _, hb, h1, h2, h3⟩ => ?_ + · exact fun h => let ⟨_, .proj h ..⟩ := hinf h; ⟨_, h⟩ + exact (inferProj.WF hb h1 h2 h3).bind fun ty _ _ ⟨_, ty', h⟩ => hF h + · exact .readThe <| (M.WF.liftExcept inferFVar.WF).lift.bind fun _ _ _ ⟨_, _, h⟩ => hF h + · exact .throw + · rename_i h _; simp [Expr.hasLooseBVars, Expr.looseBVarRange'] at h + · split <;> rename_i h + · refine .readThe <| (M.WF.liftExcept (checkLevel.WF h1)).lift.bind fun _ _ _ ⟨_, h⟩ => ?_ + exact hF (infer_sort h) + · let ⟨_, .sort h⟩ := hinf (by simpa using h) + exact hF (infer_sort h) + · refine .readThe <| + (M.WF.liftExcept (inferConstant.WF h1 hinf)).lift.bind fun _ _ _ ⟨_, _, h⟩ => hF h + · exact (inferLambda.WF h1 hinf).bind fun _ _ _ ⟨_, _, h⟩ => hF h + · exact (inferForall.WF h1 hinf).bind fun _ _ _ ⟨_, _, h⟩ => hF h + · split <;> rename_i h + · let ⟨_, h⟩ := hinf h; exact (inferApp.WF h).bind fun _ _ _ ⟨_, h⟩ => hF h + refine (inferType'.WF h1.1 ?_).bind fun _ _ _ ⟨_, _, hfb, hf1, hf2, hf3⟩ => ?_ + · exact fun h => let ⟨_, .app _ _ h _⟩ := hinf h; ⟨_, h⟩ + refine .stateWF fun wf => ?_ + refine (ensureForallCore.WF hf2).bind fun _ _ _ H => ?_ + obtain ⟨hb, h2, name, dType, body, bi, rfl⟩ := H + let ⟨_, .forallE (ty' := dType') hl1 hl2 hl3 hl4, hl5⟩ := h2 + extract_lets _ G1 + refine (inferType'.WF h1.2 ?_).bind fun aType _ _ ⟨_, aType', hab, ha1, ha2, ha3⟩ => ?_ + · exact fun h => let ⟨_, .app _ _ _ h⟩ := hinf h; ⟨_, h⟩ + extract_lets G2 + suffices ∀ {s b} (H : b = true → c.IsDefEqU dType' aType'), RecM.WF c s (G2 b) P by + split + · refine .bind ?_ (Q := fun b _ => b = true → c.IsDefEqU dType' aType') fun b _ _ => this + intro _ mwf wf _ _ eq + let c' := { c with eagerReduce := true } + have ⟨_, h1, h2, h3, h4⟩ := isDefEq.WF (c := c') hl3 ha2 _ mwf { wf with } _ _ eq + exact ⟨_, h1, h2, { h3 with }, h4⟩ + · exact (isDefEq.WF hl3 ha2).bind fun b _ _ => this + subst G2; dsimp; rintro s ⟨⟩ H + · exact .getEnv <| .getLCtx .throw + simp [G1, Expr.bindingBody!] + have hf3 := hf3.defeqU_r c.Ewf c.Δwf hl5.symm + have ha3 := ha3.defeqU_r c.Ewf c.Δwf (H rfl).symm + subst hP; refine hF ⟨?_, .app hf3 ha3 hf1 ha1, hl4.inst c.Ewf ha3 ha1, .app hf3 ha3⟩ + exact fun _ hP he => (hfb.trans hb _ hP he.1).2.instantiate1 he.2 + · exact (inferLet.WF h1 hinf).bind fun _ _ _ ⟨_, _, h⟩ => hF h + +#guard_named_axioms Ix.Theory.Named.TypeChecker.Inner.inferProj.WF [ + propext, + sorryAx, + Classical.choice, + Quot.sound, + Lean.Expr.instantiate1_eq, + Lean.Expr.mkAppData_eq, + Lean.Expr.mkData_eq, + Lean.Expr.replace_eq, + Lean.Level.hasMVar_eq, + Lean.Level.hasParam_eq, + Lean.Level.instLawfulBEqLevel, + Lean.PersistentArray.toList'_push, + Lean.PersistentHashMap.findAux_isSome, + Lean.PersistentHashMap.WF.find?_eq, + Lean.PersistentHashMap.WF.toList'_insert] diff --git a/Ix/Theory/Named/Verify/TypeChecker/IsDefEq.lean b/Ix/Theory/Named/Verify/TypeChecker/IsDefEq.lean new file mode 100644 index 000000000..50ae5f28b --- /dev/null +++ b/Ix/Theory/Named/Verify/TypeChecker/IsDefEq.lean @@ -0,0 +1,1228 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.TypeChecker.Reduce +import Ix.Theory.Named.Verify.TypeChecker.InferType + +open Ix.Theory (VLevel) + +open Ix.Theory.Named + +namespace Ix.Theory.Named.TypeChecker.Inner +open Lean hiding Environment Exception + +theorem isDefEqLambda.WF {c : VContext} {s : VState} + {m} [mwf : c.MLCWF m] + {fvs : List Expr} (hsubst : subst.toList.reverse = fvs) + (he₁ : (c.withMLC m).TrExprS (e₁.instantiateList fvs) ei₁') + (he₂ : (c.withMLC m).TrExprS (e₂.instantiateList fvs) ei₂') : + RecM.WF (c.withMLC m) s (isDefEqLambda e₁ e₂ subst) fun b _ => + b → (c.withMLC m).IsDefEqU ei₁' ei₂' := by + unfold isDefEqLambda; let c' := c.withMLC m + split <;> [rename_i n₁ d₁ b₁ bi₁ n₂ d₂ b₂ bi₂; (simp [hsubst]; exact isDefEq.WF he₁ he₂)] + extract_lets F di₁ di₂; unfold di₁ di₂ + simp at he₁ he₂ + let .lam (ty' := t₁') (body' := b₁') ⟨_, a1⟩ a2 a3 := he₁ + let .lam (ty' := t₂') (body' := b₂') b1 b2 b3 := he₂ + suffices ∀ {x s} + (_ : match x with + | none => d₁ == d₂ + | some x => x = d₂.instantiateList fvs), + c'.IsDefEqU t₁' t₂' → + (F x).WF c' s fun b _ => b → c'.IsDefEqU (t₁'.lam b₁') (t₂'.lam b₂') by + split <;> rename_i h + · refine .pureBind <| this ‹_› ?_ + exact a2.eqv (Expr.instantiateList_eqv h) |>.uniq c'.Ewf (.refl c'.Ewf c'.Δwf) b2 + simp [hsubst] + refine (isDefEq.WF a2 b2).bind fun b _ _ h1 => ?_ + split <;> [exact .pure nofun; rename_i h] + simp at h; exact this rfl (h1 h) + intros x s hx tt + have tt' := tt.of_l c'.Ewf c'.Δwf a1 + have ⟨b₁'', a3', eq⟩ := a3.defeqDFC' c'.Ewf <| .cons (.refl c'.Ewf c'.Δwf) (by nofun) (.vlam tt') + unfold F; split <;> rename_i h + · extract_lets d₂' + have : d₂' = d₂.instantiateList fvs := by split at hx <;> [simp [d₂', hsubst]; exact hx] + clear_value d₂'; subst this + refine .withLocalDecl b2 b1 .rfl fun v mwf' _ _ _ => ?_ + have b3' := b3.inst_fvar c.Ewf mwf'.1.tr.wf + have a3'' := a3'.inst_fvar c.Ewf mwf'.1.tr.wf + rw [Expr.instantiateList_instantiate1_comm (by rfl), ← Expr.instantiateList] at a3'' b3' + refine isDefEqLambda.WF (mwf := mwf') (fvs := .fvar v :: fvs) (by simp [hsubst]) a3'' b3' + |>.mono fun _ _ _ h hb => ?_ + have ⟨_, bb⟩ := eq.symm.trans c'.Ewf mwf'.1.tr.wf.toCtx (h hb) + exact ⟨_, .symm <| .lamDF tt'.symm <| bb.symm⟩ + · simp [Expr.hasLooseBVars] at h + refine .stateWF fun wf => ?_ + have {bᵢ : Expr} {bᵢ'} (h : bᵢ.looseBVarRange' = 0) + (a3 : TrExprS c'.venv c'.lparams ((none, .vlam t₂') :: c'.vlctx) + (bᵢ.instantiateList fvs 1) bᵢ') : + ∃ e', (c.withMLC m).TrExprS (bᵢ.instantiateList (default :: fvs)) e' ∧ + c'.venv.IsDefEqU c'.lparams.length (t₂' :: c'.vlctx.toCtx) bᵢ' e'.lift := by + simp; rw [← Expr.instantiateList_instantiate1_comm (by rfl)] + let v : FVarId := ⟨s.ngen.curr⟩ + have hΔ : VLCtx.WF c'.venv c.lparams.length ((some (v, []), .vlam t₂') :: m.vlctx) := by + refine ⟨mwf.1.tr.wf, ?_, b1⟩ + rintro _ _ ⟨⟩; simp; exact fun h => s.ngen.not_reserves_self (wf.ngen_wf _ h) + have := a3.inst_fvar c.Ewf.ordered hΔ + have eq {e} (hb : bᵢ.looseBVarRange' = 0) (he : e.looseBVarRange' = 0) : + (bᵢ.instantiateList fvs 1).instantiate1' e = bᵢ.instantiateList fvs 1 := by + rw [Expr.instantiate1'_eq_self]; rw [Expr.instantiateList'_eq_self] <;> simp [*] + rw [eq h rfl, ← eq (e := .fvar v) h rfl] + let ⟨_, H⟩ := this.weakFV_inv c.Ewf (.skip_fvar _ _ .refl) (.refl c.Ewf hΔ) + (m.noBV ▸ this.closed) (by rw [eq h rfl]; exact a3.fvarsIn) + refine ⟨_, H, this.uniq c'.Ewf (.refl c'.Ewf hΔ) <| H.weakFV c'.Ewf (.skip_fvar _ _ .refl) hΔ⟩ + let ⟨_, a4, a5⟩ := this h.1 a3' + let ⟨_, b4, b5⟩ := this h.2 b3 + exact isDefEqLambda.WF (fvs := default :: fvs) (by simp [hsubst]) a4 b4 + |>.mono fun _ _ _ h hb => + have hΓ := ⟨c'.Δwf, b1⟩ + have ⟨_, bb⟩ := eq.symm.trans c'.Ewf hΓ a5 + |>.trans c'.Ewf hΓ ((h hb).weak c'.Ewf (B := t₂')) |>.trans c'.Ewf hΓ b5.symm + ⟨_, .symm <| .lamDF tt'.symm bb.symm⟩ + +theorem isDefEqForall.WF {c : VContext} {s : VState} + {m} [mwf : c.MLCWF m] + {fvs : List Expr} (hsubst : subst.toList.reverse = fvs) + (he₁ : (c.withMLC m).TrExprS (e₁.instantiateList fvs) ei₁') + (he₂ : (c.withMLC m).TrExprS (e₂.instantiateList fvs) ei₂') : + RecM.WF (c.withMLC m) s (isDefEqForall e₁ e₂ subst) fun b _ => + b → (c.withMLC m).IsDefEqU ei₁' ei₂' := by + unfold isDefEqForall; let c' := c.withMLC m + split <;> [rename_i n₁ d₁ b₁ bi₁ n₂ d₂ b₂ bi₂; (simp [hsubst]; exact isDefEq.WF he₁ he₂)] + extract_lets F di₁ di₂; unfold di₁ di₂ + simp at he₁ he₂ + let .forallE (ty' := t₁') (body' := b₁') ⟨_, a1⟩ _ a2 a3 := he₁ + let .forallE (ty' := t₂') (body' := b₂') b1 ⟨_, bT⟩ b2 b3 := he₂ + suffices ∀ {x s} + (_ : match x with + | none => d₁ == d₂ + | some x => x = d₂.instantiateList fvs), + c'.IsDefEqU t₁' t₂' → + (F x).WF c' s fun b _ => b → c'.IsDefEqU (t₁'.forallE b₁') (t₂'.forallE b₂') by + split <;> rename_i h + · refine .pureBind <| this ‹_› ?_ + exact a2.eqv (Expr.instantiateList_eqv h) |>.uniq c'.Ewf (.refl c'.Ewf c'.Δwf) b2 + simp [hsubst] + refine (isDefEq.WF a2 b2).bind fun b _ _ h1 => ?_ + split <;> [exact .pure nofun; rename_i h] + simp at h; exact this rfl (h1 h) + intros x s hx tt + have tt' := tt.of_l c'.Ewf c'.Δwf a1 + have ⟨b₁'', a3', eq⟩ := a3.defeqDFC' c'.Ewf <| .cons (.refl c'.Ewf c'.Δwf) (by nofun) (.vlam tt') + unfold F; split <;> rename_i h + · extract_lets d₂' + have : d₂' = d₂.instantiateList fvs := by split at hx <;> [simp [d₂', hsubst]; exact hx] + clear_value d₂'; subst this + refine .withLocalDecl b2 b1 .rfl fun v mwf' _ _ _ => ?_ + have b3' := b3.inst_fvar c.Ewf mwf'.1.tr.wf + have a3'' := a3'.inst_fvar c.Ewf mwf'.1.tr.wf + rw [Expr.instantiateList_instantiate1_comm (by rfl), ← Expr.instantiateList] at a3'' b3' + refine isDefEqForall.WF (mwf := mwf') (fvs := .fvar v :: fvs) (by simp [hsubst]) a3'' b3' + |>.mono fun _ _ _ h hb => ?_ + have bb := eq.symm.trans c'.Ewf mwf'.1.tr.wf.toCtx (h hb) |>.of_r c'.Ewf mwf'.1.tr.wf.toCtx bT + exact ⟨_, .symm <| .forallEDF tt'.symm <| bb.symm⟩ + · simp [Expr.hasLooseBVars] at h + refine .stateWF fun wf => ?_ + have {bᵢ : Expr} {bᵢ'} (h : bᵢ.looseBVarRange' = 0) + (a3 : TrExprS c'.venv c'.lparams ((none, .vlam t₂') :: c'.vlctx) + (bᵢ.instantiateList fvs 1) bᵢ') : + ∃ e', (c.withMLC m).TrExprS (bᵢ.instantiateList (default :: fvs)) e' ∧ + c'.venv.IsDefEqU c'.lparams.length (t₂' :: c'.vlctx.toCtx) bᵢ' e'.lift := by + simp; rw [← Expr.instantiateList_instantiate1_comm (by rfl)] + let v : FVarId := ⟨s.ngen.curr⟩ + have hΔ : VLCtx.WF c'.venv c.lparams.length ((some (v, []), .vlam t₂') :: m.vlctx) := by + refine ⟨mwf.1.tr.wf, ?_, b1⟩ + rintro _ _ ⟨⟩; simp; exact fun h => s.ngen.not_reserves_self (wf.ngen_wf _ h) + have := a3.inst_fvar c.Ewf.ordered hΔ + have eq {e} (hb : bᵢ.looseBVarRange' = 0) (he : e.looseBVarRange' = 0) : + (bᵢ.instantiateList fvs 1).instantiate1' e = bᵢ.instantiateList fvs 1 := by + rw [Expr.instantiate1'_eq_self]; rw [Expr.instantiateList'_eq_self] <;> simp [*] + rw [eq h rfl, ← eq (e := .fvar v) h rfl] + let ⟨_, H⟩ := this.weakFV_inv c.Ewf (.skip_fvar _ _ .refl) (.refl c.Ewf hΔ) + (m.noBV ▸ this.closed) (by rw [eq h rfl]; exact a3.fvarsIn) + refine ⟨_, H, this.uniq c'.Ewf (.refl c'.Ewf hΔ) <| H.weakFV c'.Ewf (.skip_fvar _ _ .refl) hΔ⟩ + let ⟨_, a4, a5⟩ := this h.1 a3' + let ⟨_, b4, b5⟩ := this h.2 b3 + exact isDefEqForall.WF (fvs := default :: fvs) (by simp [hsubst]) a4 b4 + |>.mono fun _ _ _ h hb => + have hΓ := ⟨c'.Δwf, b1⟩ + have bb := eq.symm.trans c'.Ewf hΓ a5 |>.trans c'.Ewf hΓ ((h hb).weak c'.Ewf (B := t₂')) + |>.trans c'.Ewf hΓ b5.symm |>.of_r c'.Ewf hΓ bT + ⟨_, .symm <| .forallEDF tt'.symm bb.symm⟩ + +theorem quickIsDefEq.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + RecM.WF c s (quickIsDefEq e₁ e₂) fun b _ => b = .true → c.IsDefEqU e₁' e₂' := by + unfold quickIsDefEq + refine .stateWF fun wf => .get ?_ + split <;> [rename_i hcache; skip] + · refine .pure fun _ => ?_ + rcases Bool.or_eq_true_iff.1 hcache with h | h + · exact (he₁.eqv h).uniq c.Ewf (.refl c.Ewf c.Δwf) he₂ + -- The cache is keyed on the two hashes, so the recorded pair may be either way round. + let ⟨_, _, a1, a2, ewf, _⟩ := wf.ectx + refine (VEnv.IsDefEqU.weak'_iff c.Ewf a1 a2.toCtx).1 <| + DefEqCache.IsDefEqE.uniq c.Ewf a1 + (he₁.weakFV' c.Ewf a2 a1) (he₂.weakFV' c.Ewf a2 a1) ?_ + unfold succeededBefore at h + split at h; · exact ewf.contains h + split at h; · exact (ewf.contains h).symm + rcases Bool.or_eq_true_iff.1 h with h | h + · exact ewf.contains h + · exact (ewf.contains h).symm + split + · exact .toLBoolM <| c.withMLC_self ▸ + isDefEqLambda.WF (subst := #[]) (fvs := []) rfl (c.withMLC_self ▸ he₁) (c.withMLC_self ▸ he₂) + · exact .toLBoolM <| c.withMLC_self ▸ + isDefEqForall.WF (subst := #[]) (fvs := []) rfl (c.withMLC_self ▸ he₁) (c.withMLC_self ▸ he₂) + · have .sort hu := he₁; have .sort hv := he₂ + refine .pure fun h => ⟨_, .sortDF (.of_ofLevel hu) (.of_ofLevel hv) ?_⟩ + exact Level.isEquiv'_wf (toLBool_true.1 h) hu hv + · let .mdata he₁ := he₁; let .mdata he₂ := he₂ + exact .toLBoolM <| isDefEq.WF he₁ he₂ + · cases he₁ + · rename_i a1 a2 _; refine .pure fun h => ?_ + simp at h; subst h; exact he₁.uniq c.Ewf (.refl c.Ewf c.Δwf) he₂ + · exact .pure nofun + +theorem isDefEqArgs.WF {c : VContext} {s : VState} + (H : ∃ e₁', c.TrExprS e₁.getAppFn e₁' ∧ ∃ e₂', c.TrExprS e₂.getAppFn e₂' ∧ c.IsDefEqU e₁' e₂') + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + RecM.WF c s (isDefEqArgs e₁ e₂) fun b _ => b → c.IsDefEqU e₁' e₂' := by + unfold isDefEqArgs; split <;> (unfold Expr.getAppFn at H) + · let .app a1 a2 a3 a4 := he₁ + let .app b1 b2 b3 b4 := he₂ + refine (isDefEq.WF a4 b4).bind fun _ _ _ h2 => ?_; extract_lets + split <;> [exact .pure nofun; rename_i hb2] + refine (isDefEqArgs.WF H a3 b3).mono fun _ _ _ h1 hb1 => ?_ + simp at hb2 + exact ⟨_, .appDF ((h1 hb1).of_l c.Ewf c.Δwf a1) ((h2 hb2).of_l c.Ewf c.Δwf a2)⟩ + · exact .pure nofun + · exact .pure nofun + · refine .pure fun _ => ?_ + simp [*] at H; let ⟨_, h1, _, h2, h3⟩ := H + have a1 := he₁.uniq c.Ewf (.refl c.Ewf c.Δwf) h1 + have a2 := he₂.uniq c.Ewf (.refl c.Ewf c.Δwf) h2 + exact a1.trans c.Ewf c.Δwf h3 |>.trans c.Ewf c.Δwf a2.symm + +theorem tryEtaExpansionCore.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + RecM.WF c s (tryEtaExpansionCore e₁ e₂) fun b _ => b → c.IsDefEqU e₁' e₂' := by + unfold tryEtaExpansionCore; split <;> [skip; exact .pure nofun] + refine (inferType.WF he₂).bind fun _ _ _ ⟨ty₁, a1, a2, a3, a4⟩ => ?_ + refine (whnf.WF a3).bind fun _ _ _ ⟨b1, _, b2, b3⟩ => ?_ + split <;> [skip; exact .pure nofun] + let .forallE (ty' := ty') c1 c2 c3 c4 := b2 + replace a4 := a4.defeqU_r c.Ewf c.Δwf b3.symm + -- have := b2.uniq c.Ewf (.refl c.Ewf c.Δwf) (.forallE c1 c2 c3 c4) + refine (isDefEq.WF he₁ (.lam c1 c3 (.app (a4.weak c.Ewf) (.bvar .zero) ( + Expr.liftLooseBVars_eq_self (c.mlctx.noBV ▸ a2.closed).looseBVarRange_le ▸ + a2.weakBV c.Ewf (.skip (.vlam ty') .refl)) (.bvar rfl)))).mono fun _ _ _ h hb => ?_ + exact (h hb).trans c.Ewf c.Δwf ⟨_, .eta a4⟩ + +theorem tryEtaExpansion.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + RecM.WF c s (tryEtaExpansion e₁ e₂) fun b _ => b → c.IsDefEqU e₁' e₂' := by + simp [tryEtaExpansion, orM, toBool] + refine (tryEtaExpansionCore.WF he₁ he₂).bind fun _ _ _ h => ?_ + split <;> [exact .pure fun _ => h rfl; skip] + exact (tryEtaExpansionCore.WF he₂ he₁).mono fun _ _ _ h hb => (h hb).symm + +private theorem AppStack.toSpineWF_of_isType {c : VContext} + (H : AppStack c.venv c.lparams c.vlctx f f' args) + (hf : c.HasType f' (VExpr.forallN As (.sort resultLevel))) + (hfull : c.TrExprS (f.mkAppList args) full') + (hfullType : c.venv.IsType c.lparams.length c.vlctx.toCtx full') : + ∃ args', args.Forall₂ (c.TrExprS · ·) args' ∧ + c.venv.SpineWF c.lparams.length c.vlctx.toCtx + (VExpr.forallN As (.sort resultLevel)) args' (.sort resultLevel) ∧ + c.TrExprS (f.mkAppList args) (VExpr.appN f' args') := by + induction args generalizing f f' As full' with + | nil => + let .head hhead := H + cases As with + | nil => + refine ⟨[], .nil, .nil, ?_⟩ + change c.TrExprS f f' + exact hhead + | cons A As => + obtain ⟨sortLevel, hfullSort⟩ := hfullType + have hheadEq := hhead.uniq c.Ewf (.refl c.Ewf c.Δwf) hfull + have hheadSort := hfullSort.defeqU_l c.Ewf c.Δwf hheadEq.symm + have htypes := hf.uniqU c.Ewf c.Δwf hheadSort + exact False.elim <| + VEnv.IsDefEqU.sort_forallE_inv c.Ewf c.Δwf htypes.symm + | cons arg args ih => + let .app hfun harg hhead hargTr Hrest := H + cases As with + | nil => + have htypes := hf.uniqU c.Ewf c.Δwf hfun + exact False.elim <| + VEnv.IsDefEqU.sort_forallE_inv c.Ewf c.Δwf htypes + | cons A As => + have htypes := hf.uniqU c.Ewf c.Δwf hfun + obtain ⟨⟨_, hdomain⟩, _, _hcodomain⟩ := + htypes.forallE_inv c.Ewf c.Δwf + have hargA := harg.defeqU_r c.Ewf c.Δwf ⟨_, hdomain.symm⟩ + have htailType := hf.app hargA + rw [VExpr.instN_forallN] at htailType + obtain ⟨args', hargs, hspine, htailFull⟩ := + ih Hrest htailType (by simpa [Expr.mkAppList] using hfull) + hfullType + refine ⟨_ :: args', .cons hargTr hargs, .cons hargA ?_, ?_⟩ + · rw [VExpr.instN_forallN] + rw [Nat.zero_add] + rw [(show (VExpr.sort resultLevel).ClosedN 0 by trivial).instN_eq + (e2 := _) (Nat.zero_le As.length)] + exact hspine + · simpa [Expr.mkAppList, VExpr.appN] using htailFull + +private theorem forall₂_of_getElem? {R : α → β → Prop} : + ∀ {xs : List α} {ys : List β}, + xs.length = ys.length → + (∀ (i : Nat) (x : α) (y : β), + xs[i]? = some x → ys[i]? = some y → R x y) → + List.Forall₂ R xs ys + | [], [], _, _ => .nil + | [], _ :: _, hlen, _ => by simp at hlen + | _ :: _, [], hlen, _ => by simp at hlen + | x :: xs, y :: ys, hlen, h => by + refine .cons (h 0 x y (by simp) (by simp)) ?_ + apply forall₂_of_getElem? (Nat.succ.inj hlen) + intro i x' y' hx hy + exact h (i + 1) x' y' (by simpa using hx) (by simpa using hy) + + +theorem tryEtaStructCore.WF_of_structureEta {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + RecM.WF c s (tryEtaStructCore e₁ e₂) fun b _ => b → c.IsDefEqU e₁' e₂' := by + unfold tryEtaStructCore + split <;> [skip; exact .pure nofun] + refine .getEnv ?_ + refine (M.WF.liftExcept envGet.WF).lift.bind fun _ci _ _ hfind => ?_ + split <;> [skip; exact .pure nofun] + extract_lets F1 + split <;> [skip; exact .pure nofun] + rename_i hostHead ctorName ctorLevels hhead state hstate hostInfo ctorInfo + hargs + split <;> [skip; exact .pure nofun] + rename_i hnonrec + refine (inferType.WF he₁).bind fun _ _ _ + ⟨ty₁', _aBelow, _aTerm, aType, aTyped⟩ => ?_ + refine (inferType.WF he₂).bind fun _ _ _ + ⟨ty₂', _bBelow, _bTerm, bType, bTyped⟩ => ?_ + refine (isDefEq.WF aType bType).bind fun _ _ _ htypes => ?_ + split <;> [skip; exact .pure nofun] + rename_i htypesTrue + unfold F1 + obtain ⟨familyInfo, hfamily, ⟨artifact⟩⟩ := + c.structureEtaReady.resolveConstructor hfind hnonrec + have ⟨head', hstack⟩ := AppStack.build <| + e₂.mkAppList_getAppArgsList ▸ he₂ + have hheadTr := hstack.tr + rw [hhead] at hheadTr + let .const (us' := levels) hconst hlevelsMap hlevelsHostLength := hheadTr + have hviewConstructor := artifact.projection.viewWF.constructor + rw [artifact.constructor_name_eq] at hviewConstructor + rw [hviewConstructor] at hconst + cases hconst + have hlevelsWF : ∀ level ∈ levels, + level.WF c.lparams.length := + VLevel.WF.of_mapM_ofLevel hlevelsMap + have hrawCtorUvars : artifact.projection.view.constructor.raw.uvars = + artifact.projection.view.uvars := + artifact.projection.view.generation.ctor_uvars_eq + (by simp [artifact.projection.view.constructor_eq]) + have hlevelsLength : levels.length = artifact.projection.view.uvars := + (List.mapM_eq_some.1 hlevelsMap).length_eq.symm.trans <| + hlevelsHostLength.trans hrawCtorUvars + have hctorHead : c.HasType (.const ctorName levels) + (artifact.projection.view.constructor.raw.type.instL levels) := + VEnv.HasType.const hviewConstructor hlevelsWF + (hlevelsLength.trans hrawCtorUvars.symm) + let ctorBinders := + (artifact.projection.view.constructor.declaredBinders + artifact.projection.view.nparams).map (VExpr.instL levels) + let ctorResult := + (artifact.projection.view.constructor.rawResult + artifact.projection.view.nparams).instL levels + have hctorHeadShape : c.HasType (.const ctorName levels) + (VExpr.forallN ctorBinders ctorResult) := by + rw [artifact.projection.view.constructor.rawType_eq] at hctorHead + simpa [ctorBinders, ctorResult, VExpr.instL_forallN] using hctorHead + have hhostArgsLength : e₂.getAppArgsList.length = + ctorInfo.numParams + ctorInfo.numFields := by + simpa [Expr.getAppNumArgs_eq, ← Expr.getAppArgsList_reverse] using hargs + have hnumParams : ctorInfo.numParams = artifact.projection.view.nparams := by + exact (congrArg ConstructorVal.numParams + artifact.constructor_info_eq).symm.trans + artifact.projection.constructor_numParams_eq + have hnumFields : ctorInfo.numFields = artifact.projection.view.fields.length := by + exact (congrArg ConstructorVal.numFields + artifact.constructor_info_eq).symm.trans + artifact.projection.constructor_numFields_eq + have hconstructorMem : artifact.projection.view.constructor ∈ + artifact.projection.view.generation.block.ctorPairs := by + simp [artifact.projection.view.constructor_eq] + have hconstructorShape := + artifact.projection.view.generation.shape.2.2.2.2.2 + artifact.projection.view.constructor hconstructorMem + have hhostBinderLength : e₂.getAppArgsList.length = ctorBinders.length := by + simpa [ctorBinders, VInductDecl.NormalizedCtor.declaredBinders, + VStructureView.fields, hnumParams, hnumFields, + hconstructorShape.2.2.1] using hhostArgsLength + obtain ⟨args', hargsTr, hargsSpine, hfullTr⟩ := + AppStack.toSpineWF hstack hctorHeadShape hhostBinderLength + rw [e₂.mkAppList_getAppArgsList] at hfullTr + have hargsLength : args'.length = artifact.projection.view.nparams + + artifact.projection.view.fields.length := + hargsTr.length_eq.symm.trans <| by + simpa [hnumParams, hnumFields] using hhostArgsLength + let params := args'.take artifact.projection.view.nparams + let fields := args'.drop artifact.projection.view.nparams + have hargsSplit : args' = params ++ fields := by + simp [params, fields] + have hparamsLength : params.length = artifact.projection.view.nparams := by + simp [params, hargsLength] + have hfieldsLength : fields.length = artifact.projection.view.fields.length := by + simp [fields, hargsLength] + have hargsSpineSplit := hargsSpine + rw [hargsSplit] at hargsSpineSplit + obtain ⟨paramCursor, hparamRaw, hfieldsRaw⟩ := + hargsSpineSplit.split + let ctorTail := VExpr.forallN + (artifact.projection.view.fields.map (VExpr.instL levels)) ctorResult + have hparamCtor : c.venv.SpineWF c.lparams.length c.vlctx.toCtx + (VExpr.forallN + (artifact.projection.view.constructorParams.map + (VExpr.instL levels)) ctorTail) + params paramCursor := by + simpa [ctorBinders, ctorTail, + VInductDecl.NormalizedCtor.declaredBinders, + VStructureView.constructorParams, VStructureView.fields, + List.map_append, VExpr.forallN_append] using hparamRaw + obtain ⟨resultLevel, hrawResult⟩ := artifact.projection.rawResult_sort + have hparamsSpine := + artifact.projection.viewWF.familyParamsSpine_of_constructor + c.Ewf.ordered levels hlevelsWF hlevelsLength params hparamsLength + hparamCtor resultLevel hrawResult + have hdeclResult₀ := + artifact.projection.viewWF.generationSemantics.constructor.declaredResult + have hdeclResult₁ := hdeclResult₀.instL hlevelsWF + have hdeclResult : c.venv.IsDefEq c.lparams.length + ctorBinders.reverse ctorResult + ((VInductDecl.NormalizedCtor.resultTarget + artifact.projection.view.generation.block + artifact.projection.view.constructor).instL levels) + ((VExpr.sort + artifact.projection.view.generation.block.checked.resultLevel).instL + levels) := by + simpa [ctorBinders, ctorResult, List.map_reverse] using hdeclResult₁ + have hdeclTel₀ := + artifact.projection.viewWF.generationSemantics.constructor.declaredTel + have hdeclTel := hdeclTel₀.instL hlevelsWF + have hctorOnTel : c.venv.OnTel c.lparams.length [] ctorBinders := by + simpa [ctorBinders] using hdeclTel.raw_onTel + have hctorCtxClosed : CtxClosed ctorBinders.reverse := + VEnv.CtxWF.closed c.Ewf.ordered <| by + simpa using hctorOnTel.toOnCtx (by trivial) + have hdeclResultΓ := hdeclResult.weakR c.Ewf.ordered hctorCtxClosed + c.vlctx.toCtx + have hargsTelLength : args'.length = ctorBinders.length := + hargsTr.length_eq.symm.trans hhostBinderLength + have hresultEq := hargsSpine.instRev_defeq c.Ewf.ordered + hargsTelLength hdeclResultΓ + let S := artifact.projection.viewWF.toGenerationEnv c.Ewf.ordered + have hresultIndices : + artifact.projection.view.constructor.view.resultIndices = [] := by + apply List.length_eq_zero_iff.1 + rw [S.viewResultIndices_length hconstructorMem] + simp [artifact.projection.view.checked_indices_eq] + have hrange := VExpr.map_instRev_bvarRevRange_seg args' + artifact.projection.view.nparams artifact.projection.view.fields.length + (by omega) + have hrange' : + (VExpr.bvarRevRange + (artifact.projection.view.constructor.rawFields + artifact.projection.view.source.nparams).length + artifact.projection.view.source.nparams).map (VExpr.instRev · args') = + params := by + simpa [VStructureView.fields, hargsLength, params] using hrange + have htarget : + ((VInductDecl.NormalizedCtor.resultTarget + artifact.projection.view.generation.block + artifact.projection.view.constructor).instL levels).instRev args' = + artifact.projection.view.structureType levels params := by + simp only [VInductDecl.NormalizedCtor.resultTarget, VExpr.instL_appN, VExpr.instL, + VExpr.instRev_appN, VExpr.bvarRevRange_map_instL, hresultIndices, List.append_nil] + rw [VLevel.inst_map_id hlevelsLength] + rw [VExpr.instRev_closedN args' (by trivial)] + rw [hrange'] + rfl + rw [htarget] at hresultEq + have hcanonicalRaw := hargsSpine.hasType_appN hctorHeadShape + have hcanonical : c.HasType + ((VExpr.const ctorName levels).appN args') + (artifact.projection.view.structureType levels params) := + hcanonicalRaw.defeqU_r c.Ewf c.Δwf ⟨_, hresultEq⟩ + have hfullEq := hfullTr.uniq c.Ewf (.refl c.Ewf c.Δwf) he₂ + have hbStruct := hcanonical.defeqU_l c.Ewf c.Δwf hfullEq + have hty₂Struct := bTyped.uniqU c.Ewf c.Δwf hbStruct + have hty₁Struct := VEnv.IsDefEqU.trans c.Ewf c.Δwf + (htypes htypesTrue) hty₂Struct + have haStruct := aTyped.defeqU_r c.Ewf c.Δwf hty₁Struct + have heta := artifact.eta c.Δwf hlevelsWF hlevelsLength + hparamsLength ⟨_, hparamsSpine⟩ haStruct + have hF1Size : F1.size = args'.length := by + calc + F1.size = F1.toList.length := by simp + _ = e₂.getAppArgsList.length := by simp [F1, Expr.getAppArgs_toList] + _ = args'.length := hargsTr.length_eq + have hfieldData : ∀ (j : Nat), j < fields.length → + ∃ code : VStructureView.ProjectionCode, + (artifact.projection.view.projectionCodes levels params)[j]? = + some code ∧ + c.TrExprS (.proj ctorInfo.induct j e₁) + (.app code.projector e₁') ∧ + ∀ (hi : ctorInfo.numParams + j < F1.size), + c.TrExprS F1[ctorInfo.numParams + j] fields[j] := by + intro j hj + have hcodeIdx : j < + (artifact.projection.view.projectionCodes levels params).length := by + simpa [VStructureView.specializedFields, hfieldsLength] using hj + let code := (artifact.projection.view.projectionCodes levels params)[j] + have hcode : + (artifact.projection.view.projectionCodes levels params)[j]? = + some code := List.getElem?_eq_getElem hcodeIdx + have hprojector := artifact.projection.programsWF c.Δwf hlevelsWF + hlevelsLength hparamsLength ⟨_, hparamsSpine⟩ hcode + have hprojSem : c.venv.TrProj c.lparams.length c.vlctx.toCtx + artifact.projection.view levels params j e₁' + (.app code.projector e₁') := { + viewWF := artifact.projection.viewWF + levelsWF := hlevelsWF + levels_length := hlevelsLength + params_length := hparamsLength + paramsSpine := ⟨_, hparamsSpine⟩ + majorType := haStruct + program := ⟨code, hcode, rfl, hprojector⟩ } + have hprojTr : c.TrExprS (.proj ctorInfo.induct j e₁) + (.app code.projector e₁') := + .proj he₁ ⟨artifact.projection.view, levels, params, + artifact.projection.name_eq, hprojSem⟩ + refine ⟨code, hcode, hprojTr, ?_⟩ + intro hi + have hselectedList : + e₂.getAppArgsList[ctorInfo.numParams + j]? = + some F1[ctorInfo.numParams + j] := by + rw [← Expr.getAppArgs_toList] + simp [F1] + obtain ⟨translated, htranslated, htr⟩ := + Ix.Theory.Named.List.Forall₂.getElem?_left hargsTr hselectedList + have hfieldGet : args'[ctorInfo.numParams + j]? = some fields[j] := by + rw [hargsSplit, List.getElem?_append_right] + · simp [hnumParams, hparamsLength] + · simp [hnumParams, hparamsLength] + have : translated = fields[j] := + Option.some.inj (htranslated.symm.trans hfieldGet) + subst translated + exact htr + rw [Std.Legacy.Range.forIn'_eq_forIn'_range'] + simp only [Std.Legacy.Range.size, Nat.add_sub_cancel, Nat.div_one] + let FieldEq := fun (j : Nat) => ∃ (field : VExpr) + (code : VStructureView.ProjectionCode), + fields[j]? = some field ∧ + (artifact.projection.view.projectionCodes levels params)[j]? = some code ∧ + c.IsDefEqU (.app code.projector e₁') field + let etaStep (indices : List Nat) + (hlow : ∀ i, i ∈ indices → ctorInfo.numParams ≤ i) + (hhigh : ∀ i, i ∈ indices → i < F1.size) : + (i : Nat) → i ∈ indices → Option Bool × PUnit → + RecM (ForInStep (Option Bool × PUnit)) := + fun i hi r => tryEtaStructFieldStep e₁ ctorInfo.induct + ctorInfo.numParams F1 i + ⟨hlow i hi, hhigh i hi, by + change (i - ctorInfo.numParams) % 1 = 0 + exact Nat.mod_one _⟩ r + have etaLoopWF : ∀ (all indices : List Nat) + (hsuffix : ∃ pre, pre ++ indices = all) + (hlow : ∀ i, i ∈ all → ctorInfo.numParams ≤ i) + (hhigh : ∀ i, i ∈ all → i < F1.size) {st : VState}, + RecM.WF c st + (List.forIn'.loop all (etaStep all hlow hhigh) indices + ⟨none, PUnit.unit⟩ hsuffix) + fun r _ => + (r.1 = none → + ∀ i, i ∈ indices → FieldEq (i - ctorInfo.numParams)) ∧ + r.1 ≠ some true := by + intro all indices hsuffix hlow hhigh st + induction indices generalizing st with + | nil => + simp only [List.forIn'.loop] + exact .pure (by simp) + | cons i indices ih => + simp only [List.forIn'.loop] + simp only [etaStep, tryEtaStructFieldStep] + obtain ⟨pre, hprefix⟩ := hsuffix + have hiAll : i ∈ all := by + rw [← hprefix] + simp + have hlo := hlow i hiAll + have hhi := hhigh i hiAll + have hj : i - ctorInfo.numParams < fields.length := by + rw [hF1Size, hargsLength, ← hnumParams, + ← hfieldsLength] at hhi + omega + obtain ⟨code, hcode, hprojTr, hargTr⟩ := + hfieldData (i - ctorInfo.numParams) hj + have hiEq : ctorInfo.numParams + (i - ctorInfo.numParams) = i := + Nat.add_sub_of_le hlo + have hargBound : + ctorInfo.numParams + (i - ctorInfo.numParams) < F1.size := by + omega + have hargTr' : c.TrExprS F1[i] fields[i - ctorInfo.numParams] := by + simpa [hiEq] using hargTr hargBound + simp only [bind_assoc] + refine (isDefEq.WF hprojTr hargTr').bind fun b next _ hb => ?_ + by_cases hbtrue : b = true + · simp only [hbtrue, if_pos, pure_bind] + have hcur : FieldEq (i - ctorInfo.numParams) := + ⟨fields[i - ctorInfo.numParams], code, + List.getElem?_eq_getElem hj, hcode, hb hbtrue⟩ + have hsuffixTail : ∃ pre, pre ++ indices = all := by + refine ⟨pre ++ [i], ?_⟩ + simpa [List.append_assoc] using hprefix + have htail : RecM.WF c next + (List.forIn'.loop all (etaStep all hlow hhigh) indices + ⟨none, PUnit.unit⟩ hsuffixTail) + (fun r _ => + (r.1 = none → + ∀ k, k ∈ i :: indices → + FieldEq (k - ctorInfo.numParams)) ∧ + r.1 ≠ some true) := + (ih hsuffixTail (st := next)).mono + (fun r _ _ hrest => ⟨fun hnone k hk => by + rw [List.mem_cons] at hk + rcases hk with rfl | hk + · exact hcur + · exact hrest.1 hnone k hk, + hrest.2⟩) + simpa only [etaStep, tryEtaStructFieldStep, pure_bind] using htail + · simp only [hbtrue] + exact .pure (by simp) + have hparamsLe : ctorInfo.numParams ≤ F1.size := by + rw [hF1Size, hargsLength, ← hnumParams] + omega + have hlowRange : ∀ i, + i ∈ List.range' ctorInfo.numParams + (F1.size - ctorInfo.numParams) → + ctorInfo.numParams ≤ i := by + intro i hi + rcases List.mem_range'.mp hi with ⟨j, hj, rfl⟩ + omega + have hhighRange : ∀ i, + i ∈ List.range' ctorInfo.numParams + (F1.size - ctorInfo.numParams) → + i < F1.size := by + intro i hi + rcases List.mem_range'.mp hi with ⟨j, hj, rfl⟩ + omega + have etaForInWF : ∀ {st : VState}, RecM.WF c st + (List.forIn' + (List.range' ctorInfo.numParams (F1.size - ctorInfo.numParams)) + ⟨none, PUnit.unit⟩ + (etaStep + (List.range' ctorInfo.numParams + (F1.size - ctorInfo.numParams)) hlowRange hhighRange)) + (fun r _ => (r.1 = none → ∀ i, + i ∈ List.range' ctorInfo.numParams + (F1.size - ctorInfo.numParams) → + FieldEq (i - ctorInfo.numParams)) ∧ + r.1 ≠ some true) := by + intro st + exact etaLoopWF + (List.range' ctorInfo.numParams (F1.size - ctorInfo.numParams)) + (List.range' ctorInfo.numParams (F1.size - ctorInfo.numParams)) + ⟨[], by simp⟩ hlowRange hhighRange + change RecM.WF c _ + (do + let r ← List.forIn' + (List.range' ctorInfo.numParams (F1.size - ctorInfo.numParams)) + ⟨none, PUnit.unit⟩ + (etaStep + (List.range' ctorInfo.numParams + (F1.size - ctorInfo.numParams)) hlowRange hhighRange) + match r.1 with + | none => pure true + | some a => pure a) + (fun b _ => b = true → c.IsDefEqU e₁' e₂') + refine etaForInWF.bind fun r next _ hr => ?_ + cases hr₁ : r.1 with + | some b => + simp only + refine .pure fun hbtrue => False.elim <| + hr.2 (hr₁.trans (congrArg some hbtrue)) + | none => + simp only + refine .pure fun _ => ?_ + have hrangeCount : + F1.size - ctorInfo.numParams = fields.length := by + rw [hF1Size, hargsLength, hnumParams, hfieldsLength] + omega + have hfieldEq : ∀ j, j < fields.length → FieldEq j := by + intro j hj + have hjmem : ctorInfo.numParams + j ∈ + List.range' ctorInfo.numParams + (F1.size - ctorInfo.numParams) := by + apply List.mem_range'.2 + exact ⟨j, by simpa [hrangeCount] using hj, by simp⟩ + simpa using hr.1 hr₁ (ctorInfo.numParams + j) hjmem + let projections := artifact.projection.view.projectionArgs levels params + (artifact.projection.view.specializedFields levels params).length e₁' + have hprojectionCount : + (artifact.projection.view.specializedFields levels params).length = + fields.length := by + simpa [VStructureView.specializedFields] using hfieldsLength.symm + have hprojectionsLength : projections.length = fields.length := by + dsimp [projections] + rw [artifact.projection.view.projectionArgs_length levels params + (artifact.projection.view.specializedFields levels params).length + e₁' (by simp)] + exact hprojectionCount + have hpointwise : List.Forall₂ + (fun a a' => a = a' ∨ c.IsDefEqU a a') projections fields := by + apply forall₂_of_getElem? hprojectionsLength + intro j projection field hprojection hfield + have hj := (List.getElem?_eq_some_iff.mp hfield).1 + obtain ⟨field', code, hfield', hcode, hdefeq⟩ := hfieldEq j hj + have hprojection' : projections[j]? = + some (.app code.projector e₁') := by + dsimp [projections, VStructureView.projectionArgs] + have hjspec : j < + (artifact.projection.view.specializedFields levels params).length := + hprojectionCount.symm ▸ hj + rw [List.getElem?_map, List.getElem?_take_of_lt hjspec, hcode] + rfl + have hpEq : projection = .app code.projector e₁' := + Option.some.inj (hprojection.symm.trans hprojection') + have hfEq : field = field' := + Option.some.inj (hfield.symm.trans hfield') + subst projection + subst field + exact .inr hdefeq + let tailResult := VExpr.instRevAt ctorResult params + artifact.projection.view.fields.length + have hctorTailShape : VExpr.instRev ctorTail params = + VExpr.forallN + (artifact.projection.view.specializedFields levels params) + tailResult := by + simp only [ctorTail, tailResult, VExpr.instRev_forallN_projection, + VStructureView.specializedFields, List.length_map] + rw [VExpr.instRevAt_map_instL_zipIdx] + have hconstructorParamsLength : params.length = + artifact.projection.view.constructorParams.length := by + exact hparamsLength.trans <| by + simpa [VStructureView.constructorParams] using + hconstructorShape.2.2.1.symm + have hctorHeadPrefix : c.HasType (.const ctorName levels) + (VExpr.forallN + (artifact.projection.view.constructorParams.map + (VExpr.instL levels)) ctorTail) := by + simpa [ctorBinders, ctorTail, + VInductDecl.NormalizedCtor.declaredBinders, + VStructureView.constructorParams, VStructureView.fields, + List.map_append, VExpr.forallN_append] using hctorHeadShape + have hprefixSpine := hparamCtor.retarget + (by simpa using hconstructorParamsLength) ctorTail + rw [hctorTailShape] at hprefixSpine + have hprefixType := hprefixSpine.hasType_appN hctorHeadPrefix + have hprojectionSpine := + VStructureView.ProgramsWF.projectionArgsSpine + artifact.projection.programsWF + c.Ewf c.Δwf hlevelsWF hlevelsLength hparamsLength + ⟨_, hparamsSpine⟩ haStruct tailResult + have hprojectionDefEq := hprojectionSpine.defEq_of_pointwise + c.Ewf c.Δwf (by simpa [projections] using hpointwise) + have happEq := VEnv.IsDefEq.appN_defEq hprefixType + hprojectionDefEq + have happEqU : c.IsDefEqU + (artifact.projection.view.etaRebuild levels params e₁') + ((VExpr.const ctorName levels).appN args') := by + refine ⟨VExpr.instRev tailResult projections, ?_⟩ + simpa [VStructureView.etaRebuild, VExpr.appN_append, + artifact.constructor_name_eq, hargsSplit] using happEq + exact VEnv.IsDefEqU.trans c.Ewf c.Δwf + ⟨artifact.projection.view.structureType levels params, heta.symm⟩ + (VEnv.IsDefEqU.trans c.Ewf c.Δwf happEqU hfullEq) + + +theorem tryEtaStructCore.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + RecM.WF c s (tryEtaStructCore e₁ e₂) fun b _ => b → c.IsDefEqU e₁' e₂' := + tryEtaStructCore.WF_of_structureEta he₁ he₂ + +theorem tryEtaStruct.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + RecM.WF c s (tryEtaStruct e₁ e₂) fun b _ => b → c.IsDefEqU e₁' e₂' := by + simp [tryEtaStruct, orM, toBool] + refine (tryEtaStructCore.WF he₁ he₂).bind fun _ _ _ h => ?_ + split <;> [exact .pure fun _ => h rfl; skip] + exact (tryEtaStructCore.WF he₂ he₁).mono fun _ _ _ h hb => (h hb).symm + +theorem isDefEqApp.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + RecM.WF c s (isDefEqApp e₁ e₂) fun b _ => b → c.IsDefEqU e₁' e₂' := by + unfold isDefEqApp; split <;> [skip; exact .pure nofun] + rw [Expr.withApp_eq, Expr.withApp_eq] + split <;> [rename_i eq; exact .pure nofun] + have ⟨_, he₁'⟩ := AppStack.build <| e₁.mkAppList_getAppArgsList ▸ he₁ + have ⟨_, he₂'⟩ := AppStack.build <| e₂.mkAppList_getAppArgsList ▸ he₂ + refine (isDefEq.WF he₁'.tr he₂'.tr).bind fun _ _ _ h => ?_ + split <;> [skip; exact .pure nofun] + let rec loop.WF {s args₁ args₂ f₁ f₂ f₁' f₂' eq i} (l₁ r₁ l₂ r₂) + (h₁ : args₁.toList = l₁ ++ r₁) (hi₁ : l₁.length = i) + (h₂ : args₂.toList = l₂ ++ r₂) (hi₂ : l₂.length = i) + (he₁ : AppStack c.venv c.lparams c.vlctx (.mkAppList f₁ l₁) f₁' r₁) + (he₂ : AppStack c.venv c.lparams c.vlctx (.mkAppList f₂ l₂) f₂' r₂) + (H1 : c.IsDefEqU f₁' f₂') : + RecM.WF c s (loop args₁ args₂ eq i) fun b _ => b → + ∀ e₁', c.TrExprS (f₁.mkAppList args₁.toList) e₁' → + ∀ e₂', c.TrExprS (f₂.mkAppList args₂.toList) e₂' → c.IsDefEqU e₁' e₂' := by + unfold loop; split <;> rename_i h + · have hr₁ : r₁.length > 0 := by simp [← Array.length_toList, h₁] at h; omega + have hr₂ : r₂.length > 0 := by simp [eq, ← Array.length_toList, h₂] at h; omega + let .app (a := a₁) (as := r₁) a1 a2 a3 a4 a5 := he₁ + let .app (a := a₂) (as := r₂) b1 b2 b3 b4 b5 := he₂ + simp [ + show args₁[i] = a₁ by cases args₁; cases h₁; simp [hi₁], + show args₂[i] = a₂ by cases args₂; cases h₂; simp [hi₂]] + refine (isDefEq.WF a4 b4).bind fun _ _ _ h => ?_ + split <;> [skip; exact .pure nofun] + have H := (H1.of_l c.Ewf c.Δwf a1).appDF <| (h ‹_›).of_l c.Ewf c.Δwf a2 + exact loop.WF (l₁ ++ [a₁]) r₁ (l₂ ++ [a₂]) r₂ + (by simp [h₁]) (by simp [hi₁]) (by simp [h₂]) (by simp [hi₂]) + (by simp [a5]) (by simp [b5]) ⟨_, H⟩ + · have hr₁ : r₁.length = 0 := by simp [← Array.length_toList, h₁] at h; omega + have hr₂ : r₂.length = 0 := by simp [eq, ← Array.length_toList, h₂] at h; omega + simp at hr₁ hr₂; subst r₁ r₂; simp at h₁ h₂; subst l₁ l₂ + refine .pure fun _ _ h1 _ h2 => ?_ + have u1 := h1.uniq c.Ewf (.refl c.Ewf c.Δwf) he₁.tr + have u2 := h2.uniq c.Ewf (.refl c.Ewf c.Δwf) he₂.tr + exact u1.trans c.Ewf c.Δwf H1 |>.trans c.Ewf c.Δwf u2.symm + refine loop.WF [] _ [] _ (i := 0) (by simp [Expr.getAppArgs_toList]) rfl + (by simp [Expr.getAppArgs_toList]) rfl he₁' he₂' (h ‹_›) |>.mono fun _ _ _ h2 hb => ?_ + simp [Expr.getAppArgs_toList, Expr.mkAppList_getAppArgsList] at h2 + exact h2 hb _ he₁ _ he₂ + +theorem isDefEqProofIrrel.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + RecM.WF c s (isDefEqProofIrrel e₁ e₂) fun b _ => b = .true → c.IsDefEqU e₁' e₂' := by + unfold isDefEqProofIrrel + refine (inferType.WF he₁).bind fun _ _ _ ⟨_, a1, a2, a3, a4⟩ => ?_ + refine (isProp.WF a3).bind fun _ _ _ h1 => ?_ + split <;> [exact .pure nofun; skip] + rename_i h; simp at h + refine (inferType.WF he₂).bind fun _ _ _ ⟨_, b1, b2, b3, b4⟩ => .toLBoolM ?_ + refine (isDefEq.WF a3 b3).mono fun _ _ _ h2 hb => ?_ + exact ⟨_, .proofIrrel (h1 h) a4 (b4.defeqU_r c.Ewf c.Δwf (h2 hb).symm)⟩ + +theorem cacheFailure.WF {c : VContext} {s : VState} : + (cacheFailure e₁ e₂).WF c s fun _ _ => True := by + rintro wf _ _ ⟨⟩ + exact ⟨{ s with toState := _ }, rfl, .rfl, { wf with }, ⟨⟩⟩ + +theorem tryUnfoldProjApp.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + (tryUnfoldProjApp e).WF c s fun oe _ => + ∀ e₁, oe = some e₁ → c.FVarsBelow e e₁ ∧ c.TrExpr e₁ e' := by + unfold tryUnfoldProjApp; extract_lets f + split <;> [exact .pure nofun; skip] + refine (whnfCore.WF he).bind fun _ _ _ h => ?_ + refine .pure fun _ => ?_ + split <;> rintro ⟨⟩; exact h + +def _root_.Ix.Theory.Named.TypeChecker.ReductionStatus.WF + (c : VContext) (e₁' e₂' : VExpr) (allowContinue := false) : ReductionStatus → Prop + | .continue e₁ e₂ => allowContinue ∧ c.TrExpr e₁ e₁' ∧ c.TrExpr e₂ e₂' + | .unknown e₁ e₂ => c.TrExpr e₁ e₁' ∧ c.TrExpr e₂ e₂' + | .bool b => b → c.IsDefEqU e₁' e₂' + +theorem _root_.Ix.Theory.Named.TypeChecker.ReductionStatus.WF.defeq + (h1 : c.IsDefEqU e₁' e₁'') (h2 : c.IsDefEqU e₂' e₂'') + (H : ReductionStatus.WF c e₁' e₂' ac r) : ReductionStatus.WF c e₁'' e₂'' ac r := + match r, H with + | .continue .., ⟨a1, a2, a3⟩ => + ⟨a1, a2.defeq c.Ewf c.Δwf h1, a3.defeq c.Ewf c.Δwf h2⟩ + | .unknown .., ⟨a2, a3⟩ => + ⟨a2.defeq c.Ewf c.Δwf h1, a3.defeq c.Ewf c.Δwf h2⟩ + | .bool _, h => fun hb => h1.symm.trans c.Ewf c.Δwf (h hb) |>.trans c.Ewf c.Δwf h2 + +theorem lazyDeltaReductionStep.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + (lazyDeltaReductionStep e₁ e₂).WF c s fun r _ => r.WF c e₁' e₂' true := by + unfold lazyDeltaReductionStep + refine .getEnv ?_; extract_lets delta cont F1 F2 + have hdelta {s e e' ci} (he : c.TrExprS e e') (H : isDelta c.env e = some ci) : + (delta e).WF c s fun r _ => c.TrExpr r e' := by + let ⟨n, h1, ⟨_, h2⟩, ls, h3, _⟩ := isDelta_is_some.1 H + have ⟨_, stk⟩ := AppStack.build (e.mkAppList_getAppArgsList ▸ he) + have .const a1 a2 a3 := h3 ▸ stk.tr + have ⟨b1, b2, b3, b4⟩ := c.trenv.find?_uniq h1 a1 + refine (unfoldDefinition.WF he).bind fun oe _ _ H => ?_ + obtain _ | e' := oe; · cases H h1 h2 h3 (a3.trans b3.symm) + have ⟨_, _, c1, c2⟩ := H + exact (whnfCore.WF c1).mono fun x _ _ h => h.2.defeq c.Ewf c.Δwf c2 + have hcont {s e₁ e₂} (he₁ : c.TrExpr e₁ e₁') (he₂ : c.TrExpr e₂ e₂') : + (cont e₁ e₂).WF c s fun r _ => r.WF c e₁' e₂' true := by + let ⟨_, se₁, de₁⟩ := he₁; let ⟨_, se₂, de₂⟩ := he₂ + refine (quickIsDefEq.WF se₁ se₂).bind fun _ _ _ h => .pure ?_; split + · exact ⟨rfl, he₁, he₂⟩ + · intro; exact de₁.symm.trans c.Ewf c.Δwf (h rfl) |>.trans c.Ewf c.Δwf de₂ + · nofun + split + · exact .pure ⟨he₁.trExpr c.Ewf c.Δwf, he₂.trExpr c.Ewf c.Δwf⟩ + · refine (tryUnfoldProjApp.WF he₂).bind fun _ _ _ h => ?_; split + · exact hcont (he₁.trExpr c.Ewf c.Δwf) (h _ rfl).2 + · exact (hdelta he₁ ‹_›).bind fun _ _ _ h => hcont h (he₂.trExpr c.Ewf c.Δwf) + · refine (tryUnfoldProjApp.WF he₁).bind fun _ _ _ h => ?_; split + · exact hcont (h _ rfl).2 (he₂.trExpr c.Ewf c.Δwf) + · exact (hdelta he₂ ‹_›).bind fun _ _ _ h => hcont (he₁.trExpr c.Ewf c.Δwf) h + rename_i dt dt' hd1 hd2; extract_lets ht hs; split <;> [skip; split] + · exact (hdelta he₂ ‹_›).bind fun _ _ _ h => hcont (he₁.trExpr c.Ewf c.Δwf) h + · exact (hdelta he₁ ‹_›).bind fun _ _ _ h => hcont h (he₂.trExpr c.Ewf c.Δwf) + have hF1 {s} : (F1 ⟨⟩).WF c s fun r _ => r.WF c e₁' e₂' true := + (hdelta he₁ ‹_›).bind fun _ _ _ h1 => (hdelta he₂ ‹_›).bind fun _ _ _ h2 => hcont h1 h2 + refine .get ?_; split <;> [skip; exact hF1] + split <;> [skip; exact cacheFailure.WF.lift.bind fun _ _ _ _ => hF1] + rename_i h1 h2; simp at h1 + cases ptrEqConstantInfo_eq h1.1.1.2 + have ⟨n₁, b1₁, ⟨_, b2₁⟩, ls₁, b3₁, _⟩ := isDelta_is_some.1 hd1 + have ⟨n₂, b1₂, ⟨_, b2₂⟩, ls₂, b3₂, _⟩ := isDelta_is_some.1 hd2 + simp [b3₁, b3₂, Expr.constLevels!] at h2 + have ⟨_, stk₁⟩ := AppStack.build (e₁.mkAppList_getAppArgsList ▸ he₁) + have ⟨_, stk₂⟩ := AppStack.build (e₂.mkAppList_getAppArgsList ▸ he₂) + have .const (us' := ls₁) c1₁ c2₁ c3₁ := b3₁ ▸ stk₁.tr + have .const (us' := ls₂) c1₂ c2₂ c3₂ := b3₂ ▸ stk₂.tr + cases (c.trenv.find?_uniq b1₁ c1₁).1 + cases (c.trenv.find?_uniq b1₂ c1₂).1 + cases c1₁.symm.trans c1₂ + have := VEnv.IsDefEq.constDF c1₁ + (Γ := c.vlctx.toCtx) (.of_mapM_ofLevel c2₁) (.of_mapM_ofLevel c2₂) + ((List.mapM_eq_some.1 c2₁).length_eq.symm.trans c3₁) + (Level.isEquivList_wf h2 c2₁ c2₂) + refine (isDefEqArgs.WF ⟨_, stk₁.tr, _, stk₂.tr, _, this⟩ he₁ he₂).bind fun _ _ _ h => ?_ + split <;> [skip; exact cacheFailure.WF.lift.bind fun _ _ _ _ => hF1] + exact .pure fun _ => h ‹_› + +theorem isNatZero_wf {c : VContext} (H : isNatZero e) (h : c.TrExprS e e') : e' = .natZero := by + have h1 : c.TrExprS (.lit (.natVal 0)) e' := by + simp [isNatZero] at H; obtain H|H := H + · have := h.eqv H; exact .lit (this.nat_of_natZero c.Ewf c.hasPrimitives) this + · split at H <;> [exact h; cases H] + have := TrExprS.lit_has_type (l := .natVal 0) h1 + exact h1.unique (by trivial) (TrExprS.natLit c.hasPrimitives this 0).1 + +theorem isNatSuccOf?_wf {c : VContext} (H : isNatSuccOf? e = some e₁) + (h : c.TrExprS e e') : ∃ x, c.TrExprS e₁ x ∧ e' = .app .natSucc x := by + unfold isNatSuccOf? at H; split at H <;> cases H + · rename_i n + have := TrExprS.lit_has_type (l := .natVal (n+1)) h + refine ⟨_, (TrExprS.natLit c.hasPrimitives this n).1, ?_⟩ + exact h.unique (by trivial) (TrExprS.natLit c.hasPrimitives this (n+1)).1 + · let .app a1 a2 a3 a4 := h + let .const b1 b2 b3 := a3 + cases c.hasPrimitives.natSucc b1 + simp at b3; subst b3; simp at b2; subst b2 + exact ⟨_, a4, rfl⟩ + +theorem isDefEqOffset.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + (isDefEqOffset e₁ e₂).WF c s fun b _ => b = .true → c.IsDefEqU e₁' e₂' := by + unfold isDefEqOffset; split + · rename_i h; simp at h + cases isNatZero_wf h.1 he₁; cases isNatZero_wf h.2 he₂ + exact .pure fun _ => .refl <| he₁.wf c.Ewf c.Δwf + · split <;> [skip; exact .pure nofun] + obtain ⟨_, a1, rfl⟩ := isNatSuccOf?_wf ‹_› he₁ + obtain ⟨_, b1, rfl⟩ := isNatSuccOf?_wf ‹_› he₂ + refine .toLBoolM <| (isDefEqCore.WF a1 b1).mono fun _ _ _ h hb => ?_ + let ⟨_, de'⟩ := he₁.wf c.Ewf c.Δwf + let ⟨_, _, c1, c2⟩ := de'.hasType.1.app_inv c.Ewf c.Δwf + exact ⟨_, c1.appDF <| (h hb).of_l c.Ewf c.Δwf c2⟩ + +theorem lazyDeltaReduction.loop.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + (lazyDeltaReduction.loop e₁ e₂ n).WF c s fun r _ => r.WF c e₁' e₂' := by + induction n generalizing s e₁ e₂ e₁' e₂' with | zero => exact .throw | succ n ih + unfold loop; extract_lets F1 + refine (isDefEqOffset.WF he₁ he₂).bind fun _ _ _ h => ?_; split + · exact .pure fun hb => h (by simpa using hb) + suffices hF1 : ∀ {s}, (F1 ⟨⟩).WF c s fun r _ => r.WF c e₁' e₂' by + refine .readThe ?_; split <;> [skip; exact hF1] + refine (reduceNat.WF he₁).bind fun _ _ _ h => ?_; split + · have ⟨_, a1, a2⟩ := (h _ rfl).2 + refine (isDefEqCore.WF a1 he₂).bind fun _ _ _ h => .pure fun hb => ?_ + exact a2.symm.trans c.Ewf c.Δwf (h hb) + refine (reduceNat.WF he₂).bind fun _ _ _ h => ?_; split + · have ⟨_, a1, a2⟩ := (h _ rfl).2 + refine (isDefEqCore.WF he₁ a1).bind fun _ _ _ h => .pure fun hb => ?_ + exact (h hb).trans c.Ewf c.Δwf a2 + exact hF1 + intro s; unfold F1; refine .getEnv ?_ + refine (M.WF.liftExcept reduceNative.WF).lift.bind fun _ _ _ h => ?_ + split <;> [cases h _ rfl; skip] + refine (M.WF.liftExcept reduceNative.WF).lift.bind fun _ _ _ h => ?_ + split <;> [cases h _ rfl; skip] + refine (lazyDeltaReductionStep.WF he₁ he₂).bind fun r _ _ h => ?_ + obtain r|r|r := r + · let ⟨_, ⟨_, a1, a2⟩, ⟨_, b1, b2⟩⟩ := h + exact (ih a1 b1).mono fun _ _ _ h => h.defeq a2 b2 + · exact .pure h + · exact .pure h + +theorem tryStringLitExpansionCore.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + RecM.WF c s (tryStringLitExpansionCore e₁ e₂) fun b _ => b = .true → c.IsDefEqU e₁' e₂' := by + unfold tryStringLitExpansionCore; iterate 3 split <;> [skip; exact .pure nofun] + let .lit _ he₁ := he₁ + exact .toLBoolM <| isDefEqCore.WF he₁ he₂ + +theorem tryStringLitExpansion.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + RecM.WF c s (tryStringLitExpansion e₁ e₂) fun b _ => b = .true → c.IsDefEqU e₁' e₂' := by + refine (tryStringLitExpansionCore.WF he₁ he₂).bind fun _ _ _ h => ?_ + split <;> [skip; exact .pure h] + exact (tryStringLitExpansionCore.WF he₂ he₁).mono fun _ _ _ h hb => (h hb).symm + +theorem isDefEqUnitLike.WF_of_structureEta {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + RecM.WF c s (isDefEqUnitLike e₁ e₂) + fun b _ => b = .true → c.IsDefEqU e₁' e₂' := by + unfold isDefEqUnitLike + refine (inferType.WF he₁).bind fun _ _ _ + ⟨ty₁', _aBelow, _aTerm, aType, aTyped⟩ => ?_ + refine (whnf.WF aType).bind fun normalizedType _ _ + ⟨_aWhnfBelow, tType', tTypeTr, tTypeEq⟩ => ?_ + split <;> [skip; exact .pure nofun] + rename_i _ familyName hostLevels hhead + refine .getEnv ?_ + refine (M.WF.liftExcept envGet.WF).lift.bind fun _ _ _ hfamily => ?_ + split <;> [skip; exact .pure nofun] + rename_i _ familyDeclName familyLevelParams familyRawType hostNumParams + familyAll ctorName familyNumNested familyUnsafe familyReflexive + refine (M.WF.liftExcept envGet.WF).lift.bind fun _ _ _ hctor => ?_ + split <;> [skip; exact .pure nofun] + rename_i _ ctorDeclName ctorLevelParams ctorRawType ctorInduct ctorIndex + ctorNumParams ctorUnsafe + refine (inferType.WF he₂).bind fun _ _ _ + ⟨ty₂', _bBelow, _bTerm, bType, bTyped⟩ => ?_ + refine (isDefEqCore.WF tTypeTr bType).mono fun _ _ _ h hb => ?_ + let familyInfo : InductiveVal := { + name := familyDeclName + levelParams := familyLevelParams + type := familyRawType + numParams := hostNumParams + numIndices := 0 + all := familyAll + ctors := [ctorName] + numNested := familyNumNested + isRec := false + isUnsafe := familyUnsafe + isReflexive := familyReflexive } + let constructorInfo : ConstructorVal := { + name := ctorDeclName + levelParams := ctorLevelParams + type := ctorRawType + induct := ctorInduct + cidx := ctorIndex + numParams := ctorNumParams + numFields := 0 + isUnsafe := ctorUnsafe } + have hnonrec : c.env.isNonRecStructure familyName = true := by + unfold Kernel.Environment.isNonRecStructure + rw [hfamily] + rfl + obtain ⟨artifact⟩ := c.structureEtaReady.resolve familyName familyInfo ctorName + constructorInfo hfamily hctor hnonrec + have ⟨head', hstack⟩ := AppStack.build <| + normalizedType.mkAppList_getAppArgsList ▸ tTypeTr + have hheadTr := hstack.tr + rw [hhead] at hheadTr + let .const (us' := levels) hconst hlevelsMap hlevelsHostLength := hheadTr + have hviewFamily := artifact.projection.viewWF.family + rw [artifact.projection.name_eq] at hviewFamily + rw [hviewFamily] at hconst + cases hconst + have hlevelsWF : ∀ level ∈ levels, + level.WF c.lparams.length := + VLevel.WF.of_mapM_ofLevel hlevelsMap + have hsourceUvars : + artifact.projection.view.generation.block.sourceType.uvars = + artifact.projection.view.uvars := + artifact.projection.view.generation.block.sourceType_uvars_eq + have hlevelsLength : levels.length = artifact.projection.view.uvars := + (List.mapM_eq_some.1 hlevelsMap).length_eq.symm.trans + (hlevelsHostLength.trans hsourceUvars) + have hfamilyHead : c.HasType (.const familyName levels) + (artifact.projection.view.familyType.instL levels) := + VEnv.HasType.const hviewFamily hlevelsWF + (hlevelsLength.trans hsourceUvars.symm) + obtain ⟨resultLevel, hrawResult⟩ := artifact.projection.rawResult_sort + let rawParams := artifact.projection.view.generation.block.rawParams.map + (VExpr.instL levels) + have hfamilyHeadShape : c.HasType (.const familyName levels) + (VExpr.forallN rawParams (.sort (resultLevel.inst levels))) := by + simpa [rawParams, VStructureView.familyType, + VInductDecl.NormalizedChecked.rawType_eq, + artifact.projection.view.raw_indices_eq, hrawResult, + VExpr.instL_forallN, VExpr.forallN, VExpr.instL] using hfamilyHead + have htTypeIsType : c.venv.IsType c.lparams.length c.vlctx.toCtx tType' := + (aTyped.isType c.Ewf.ordered c.Δwf).defeqU_l c.Ewf c.Δwf + tTypeEq.symm + have normalizedTypeTr : c.TrExprS + (normalizedType.getAppFn.mkAppList normalizedType.getAppArgsList) + tType' := by + rw [normalizedType.mkAppList_getAppArgsList] + exact tTypeTr + obtain ⟨params, _hparamsTr, hparamsSpine, hfullTr⟩ := + AppStack.toSpineWF_of_isType + (f := normalizedType.getAppFn) + (args := normalizedType.getAppArgsList) + (full' := tType') hstack hfamilyHeadShape + normalizedTypeTr htTypeIsType + have hparamsLength : params.length = artifact.projection.view.nparams := + hparamsSpine.forallN_sort_length.trans <| by + simpa [rawParams] using + artifact.projection.view.generation.shape.1 + have hfamilyShape : artifact.projection.view.familyType.instL levels = + VExpr.forallN rawParams (.sort (resultLevel.inst levels)) := by + simp [rawParams, VStructureView.familyType, + VInductDecl.NormalizedChecked.rawType_eq, + artifact.projection.view.raw_indices_eq, hrawResult, + VExpr.instL_forallN, VExpr.forallN, VExpr.instL] + have hparamsFamily : c.venv.SpineWF c.lparams.length c.vlctx.toCtx + (artifact.projection.view.familyType.instL levels) params + (.sort (resultLevel.inst levels)) := by + rw [hfamilyShape] + exact hparamsSpine + have hfullStruct : c.TrExprS normalizedType + (artifact.projection.view.structureType levels params) := by + rw [← normalizedType.mkAppList_getAppArgsList] + simpa [VStructureView.structureType, + artifact.projection.name_eq] using hfullTr + have hfullEq := hfullStruct.uniq c.Ewf (.refl c.Ewf c.Δwf) tTypeTr + have hstructTy₁ := VEnv.IsDefEqU.trans c.Ewf c.Δwf hfullEq tTypeEq + have hstructTy₂ := VEnv.IsDefEqU.trans c.Ewf c.Δwf hfullEq (h hb) + have haStruct := aTyped.defeqU_r c.Ewf c.Δwf hstructTy₁.symm + have hbStruct := bTyped.defeqU_r c.Ewf c.Δwf hstructTy₂.symm + have heta₁ := artifact.eta c.Δwf hlevelsWF hlevelsLength + hparamsLength ⟨_, hparamsFamily⟩ haStruct + have heta₂ := artifact.eta c.Δwf hlevelsWF hlevelsLength + hparamsLength ⟨_, hparamsFamily⟩ hbStruct + have hfieldsLength : artifact.projection.view.fields.length = 0 := by + calc + artifact.projection.view.fields.length = + artifact.projection.constructorInfo.numFields := + artifact.projection.constructor_numFields_eq.symm + _ = constructorInfo.numFields := + congrArg ConstructorVal.numFields artifact.constructor_info_eq + _ = 0 := rfl + have hfields : artifact.projection.view.fields = [] := + List.length_eq_zero_iff.mp hfieldsLength + have hrebuild : + artifact.projection.view.etaRebuild levels params e₁' = + artifact.projection.view.etaRebuild levels params e₂' := by + simp [VStructureView.etaRebuild, VStructureView.projectionArgs, + VStructureView.specializedFields, hfields] + rw [hrebuild] at heta₁ + exact VEnv.IsDefEqU.trans c.Ewf c.Δwf ⟨_, heta₁.symm⟩ ⟨_, heta₂⟩ + +theorem isDefEqUnitLike.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + RecM.WF c s (isDefEqUnitLike e₁ e₂) fun b _ => b = .true → c.IsDefEqU e₁' e₂' := + isDefEqUnitLike.WF_of_structureEta he₁ he₂ + +theorem isDefEqCore'.WF {c : VContext} {s : VState} + (he₁ : c.TrExprS e₁ e₁') (he₂ : c.TrExprS e₂ e₂') : + RecM.WF c s (isDefEqCore' e₁ e₂) fun b _ => b = true → c.IsDefEqU e₁' e₂' := by + unfold isDefEqCore'; extract_lets F1 + refine (quickIsDefEq.WF he₁ he₂).bind fun _ _ _ h => ?_ + split <;> [exact .pure fun hb => h (by simpa using hb); skip] + refine .readThe ?_ + suffices ∀ {s}, RecM.WF c s (F1 ⟨⟩) fun b _ => b = true → c.IsDefEqU e₁' e₂' by + split <;> [rename_i h1; exact this] + refine (whnf.WF he₁).bind fun _ _ _ ⟨_, _, a1, a2⟩ => ?_ + split <;> [rename_i h2; exact this] + refine .pure fun _ => ?_ + simp [Expr.isConstOf] at h1 h2 + split at h1 <;> simp at h1; cases h1.2; split at h2 <;> simp at h2; cases h2 + let .const b1 b2 b3 := he₂ + let .const c1 c2 c3 := a1 + cases c.hasPrimitives.boolTrue b1 + cases c.hasPrimitives.boolTrue c1 + simp at b3 c3; subst b3 c3; simp at b2 c2; subst b2 c2 + exact a2.symm + intro; unfold F1 + refine (whnfCore.WF he₁).bind fun _ _ _ ⟨_, e₁', a1, a2⟩ => ?_ + refine (whnfCore.WF he₂).bind fun _ _ _ ⟨_, e₂', b1, b2⟩ => ?_ + extract_lets F2 + refine .mono (Q := fun b _ => b = true → c.IsDefEqU e₁' e₂') ?_ fun _ _ _ h hb => + a2.symm.trans c.Ewf c.Δwf (h (by simpa using hb)) |>.trans c.Ewf c.Δwf b2 + suffices ∀ {s}, RecM.WF c s (F2 ⟨⟩) fun b _ => b = true → c.IsDefEqU e₁' e₂' by + split <;> [skip; exact this] + refine (quickIsDefEq.WF a1 b1).bind fun _ _ _ h => ?_ + split <;> [skip; exact this] + exact .pure fun hb => h (by simpa using hb) + intro; unfold F2 + refine (isDefEqProofIrrel.WF a1 b1).bind fun _ _ _ h => ?_ + split + · exact .pure fun hb => h (by simpa using hb) + refine (lazyDeltaReduction.loop.WF a1 b1).readThe.bind fun _ _ _ h => ?_; split + · cases h.1 + · exact .pure h + have ⟨⟨e₁', c1, c4⟩, ⟨e₂', d1, d4⟩⟩ := h + refine .mono (Q := fun b _ => b = true → c.IsDefEqU e₁' e₂') ?_ fun _ _ _ h hb => + c4.symm.trans c.Ewf c.Δwf (h (by simpa using hb)) |>.trans c.Ewf c.Δwf d4 + extract_lets F3 + suffices ∀ {s}, RecM.WF c s (F3 ⟨⟩) fun b _ => b = true → c.IsDefEqU e₁' e₂' by + split + · split <;> [rename_i h2; exact this] + refine .pure fun _ => ?_ + simp at h2; cases h2.1 + have .const c1 c2 c3 := c1; have .const d1 d2 d3 := d1 + cases d1.symm.trans c1 + have := VEnv.IsDefEq.constDF c1 + (Γ := c.vlctx.toCtx) (.of_mapM_ofLevel c2) (.of_mapM_ofLevel d2) + ((List.mapM_eq_some.1 c2).length_eq.symm.trans c3) + (Level.isEquivList_wf h2.2 c2 d2) + exact this.toU + · split <;> [rename_i h; exact this] + simp at h; subst h + exact .pure fun _ => c1.uniq c.Ewf (.refl c.Ewf c.Δwf) d1 + · split <;> [rename_i h2; exact this] + have .proj c1 c2 := c1; have .proj d1 d2 := d1 + refine (isDefEq.WF c1 d1).bind fun _ _ _ h => ?_ + split <;> [skip; exact this] + simp at h2; subst h2; clear h + exact .pure fun _ => c2.uniq c.Ewf (.refl c.Δwf) d2 (h ‹_›) + · exact this + intro; unfold F3 + refine (whnfCore.WF c1).bind fun _ _ _ ⟨_, e₁'', c5, c6⟩ => ?_ + refine (whnfCore.WF d1).bind fun _ _ _ ⟨_, e₂'', d5, d6⟩ => ?_ + split + · exact (isDefEqCore.WF c5 d5).mono fun _ _ _ h hb => + c6.symm.trans c.Ewf c.Δwf (h (by simpa using hb)) |>.trans c.Ewf c.Δwf d6 + refine (isDefEqApp.WF c1 d1).bind fun _ _ _ h => ?_ + split <;> [exact .pure fun _ => h ‹_›; skip] + refine (tryEtaExpansion.WF c1 d1).bind fun _ _ _ h => ?_ + split <;> [exact .pure fun _ => h ‹_›; skip] + refine (tryEtaStruct.WF c1 d1).bind fun _ _ _ h => ?_ + split <;> [exact .pure fun _ => h ‹_›; skip] + refine (tryStringLitExpansion.WF c1 d1).bind fun _ _ _ h => ?_ + split <;> [exact .pure fun hb => h (by simpa using hb); skip] + refine (isDefEqUnitLike.WF c1 d1).bind fun _ _ _ h => ?_ + split <;> [exact .pure fun _ => h ‹_›; skip] + exact .pure nofun diff --git a/Ix/Theory/Named/Verify/TypeChecker/Reduce.lean b/Ix/Theory/Named/Verify/TypeChecker/Reduce.lean new file mode 100644 index 000000000..82daec369 --- /dev/null +++ b/Ix/Theory/Named/Verify/TypeChecker/Reduce.lean @@ -0,0 +1,221 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.TypeChecker.Basic + +namespace Ix.Theory.Named.TypeChecker.Inner +open Lean hiding Environment Exception +open Kernel + +theorem reduceNative.WF : + (reduceNative env e).WF fun oe => ∀ e₁, oe = some e₁ → False := by + unfold reduceNative; split <;> [skip; exact .pure nofun] + split <;> [exact .throw; skip]; split <;> [exact .throw; exact .pure nofun] + +theorem rawNatLitExt?.WF {c : VContext} (H : rawNatLitExt? e = some n) (he : c.TrExprS e e') : + c.venv.contains ``Nat ∧ e' = .natLit n := by + have : c.TrExprS (.lit (.natVal n)) e' := by + unfold rawNatLitExt? at H; split at H <;> rename_i h + · cases H; have := he.eqv h; exact .lit (this.nat_of_natZero c.Ewf c.hasPrimitives) this + · unfold Expr.rawNatLit? at H; split at H <;> cases H; exact he + have hn := this.lit_has_type + exact ⟨hn, this.unique (by trivial) (TrExprS.natLit c.hasPrimitives hn n).1⟩ + +/-- `checkNatSize` either throws or leaves the state alone, so it is transparent to any +postcondition that already holds. -/ +theorem checkNatSize.WF {c : VContext} {s : VState} {Q : Unit → VState → Prop} (H : Q () s) : + RecM.WF c s (checkNatSize n) Q := by + unfold checkNatSize; exact .readThe (by split <;> [exact .throw; exact .pure H]) + +@[inherit_doc checkNatSize.WF] +theorem checkCountArg.WF {c : VContext} {s : VState} {Q : Unit → VState → Prop} (H : Q () s) : + RecM.WF c s (checkCountArg n op) Q := by + unfold checkCountArg; split <;> [exact .throw; exact .pure H] + +@[inherit_doc checkNatSize.WF] +theorem checkLitSize.WF {c : VContext} {s : VState} {Q : Unit → VState → Prop} (H : Q () s) : + RecM.WF c s (checkLitSize l) Q := by + unfold checkLitSize; split <;> [exact checkNatSize.WF H; exact .pure H] + +@[inherit_doc checkNatSize.WF] +theorem checkNatSizeIf.WF {c : VContext} {s : VState} {Q : Unit → VState → Prop} (H : Q () s) : + RecM.WF c s (checkNatSizeIf checkSize n) Q := by + unfold checkNatSizeIf; split <;> [exact checkNatSize.WF H; exact .pure H] + +@[inherit_doc checkNatSize.WF] +theorem checkPowSize.WF {c : VContext} {s : VState} {Q : Unit → VState → Prop} (H : Q () s) : + RecM.WF c s (checkPowSize base exp) Q := by + unfold checkPowSize + exact (checkCountArg.WF H).bind fun _ _ _ H => + .readThe (by split <;> [exact .throw; exact .pure H]) + +@[inherit_doc checkNatSize.WF] +theorem checkShiftLeftSize.WF {c : VContext} {s : VState} {Q : Unit → VState → Prop} (H : Q () s) : + RecM.WF c s (checkShiftLeftSize v shift) Q := by + unfold checkShiftLeftSize + split <;> [exact (checkCountArg.WF H).bind fun _ _ _ H => checkNatSize.WF H; exact .pure H] + +/-- The shared skeleton of the binary `Nat` reductions: reduce both operands to literals, run a +size `check` that can only throw, and return the literal `f v1 v2`. -/ +theorem reduceBinNatOpCore.WF {c : VContext} {check : Nat → Nat → RecM Unit} + (hcheck : ∀ {v1 v2 s'} {Q : Unit → VState → Prop}, Q () s' → RecM.WF c s' (check v1 v2) Q) + (he : c.TrExprS (.app (.app (.const fc ls) a) b) e') + (hprim : Environment.primitives.contains fc) + (heval : c.venv.ReflectsNatNatNat fc f) : + RecM.WF c s (do + let some v1 := rawNatLitExt? (← whnf a) | return none + let some v2 := rawNatLitExt? (← whnf b) | return none + check v1 v2 + return some <| .lit <| .natVal <| f v1 v2) + fun oe _ => ∀ e₁, oe = some e₁ → + c.FVarsBelow (.app (.app (.const fc ls) a) b) e₁ ∧ c.TrExpr e₁ e' := by + let .app hb1 hb2 hf hb := he + let .app ha1 ha2 hf ha := hf + let .const h1 h2 h3 := hf + refine (whnf.WF ha).bind fun a₁ _ _ ⟨a1, _, a2, a3⟩ => ?_ + split <;> [rename_i v1 h; exact .pure nofun] + obtain ⟨hn, rfl⟩ := rawNatLitExt?.WF h a2 + refine (whnf.WF hb).bind fun b₁ _ _ ⟨b1, _, b2, b3⟩ => ?_ + split <;> [rename_i v2 h; exact .pure nofun] + cases (rawNatLitExt?.WF h b2).2 + refine (hcheck (Q := fun _ _ => True) trivial).bind fun _ _ _ _ => ?_ + refine .pure ?_; rintro _ ⟨⟩; refine ⟨fun _ _ _ => trivial, ?_⟩ + have ⟨ci, c1, _⟩ := c.trenv.find?_iff.2 ⟨_, h1⟩ + have ⟨_, c3⟩ := c.safePrimitives c1 hprim + have ⟨_, d1, d2, d3⟩ := c.trenv.find?_uniq c1 h1 + simp [c3] at d2; simp [← d2] at h3; simp [h3] at h2; subst h2 + refine ⟨_, (TrExprS.natLit c.hasPrimitives hn _).1, ?_⟩ + have := heval ⟨_, h1⟩ v1 v2 |>.instL (U' := c.lparams.length) (ls := []) nofun + simp [VExpr.instL] at this + refine this.weak0 c.Ewf (Γ := c.vlctx.toCtx) |>.symm.trans c.Ewf c.Δwf ?_ + have a3 := a3.of_r c.Ewf c.Δwf ha2 + have b3 := b3.of_r c.Ewf c.Δwf hb2 + have := ha1.appDF a3 |>.toU.of_r c.Ewf c.Δwf hb1 + exact ⟨_, .appDF this b3⟩ + +theorem reduceBinNatOp.WF {c : VContext} + (he : c.TrExprS (.app (.app (.const fc ls) a) b) e') + (hprim : Environment.primitives.contains fc) + (heval : c.venv.ReflectsNatNatNat fc f) : + RecM.WF c s (reduceBinNatOp f a b checkSize) fun oe _ => ∀ e₁, oe = some e₁ → + c.FVarsBelow (.app (.app (.const fc ls) a) b) e₁ ∧ c.TrExpr e₁ e' := by + unfold reduceBinNatOp + exact reduceBinNatOpCore.WF (fun H => checkNatSizeIf.WF H) he hprim heval + +theorem reducePow.WF {c : VContext} + (he : c.TrExprS (.app (.app (.const fc ls) a) b) e') + (hprim : Environment.primitives.contains fc) + (heval : c.venv.ReflectsNatNatNat fc Nat.pow) : + RecM.WF c s (reducePow a b) fun oe _ => ∀ e₁, oe = some e₁ → + c.FVarsBelow (.app (.app (.const fc ls) a) b) e₁ ∧ c.TrExpr e₁ e' := by + unfold reducePow + exact reduceBinNatOpCore.WF (fun H => checkPowSize.WF H) he hprim heval + +theorem reduceShiftLeft.WF {c : VContext} + (he : c.TrExprS (.app (.app (.const fc ls) a) b) e') + (hprim : Environment.primitives.contains fc) + (heval : c.venv.ReflectsNatNatNat fc (· <<< ·)) : + RecM.WF c s (reduceShiftLeft a b) fun oe _ => ∀ e₁, oe = some e₁ → + c.FVarsBelow (.app (.app (.const fc ls) a) b) e₁ ∧ c.TrExpr e₁ e' := by + unfold reduceShiftLeft + exact reduceBinNatOpCore.WF (fun H => checkShiftLeftSize.WF H) he hprim heval + +theorem reduceBinNatPred.WF {c : VContext} + (he : c.TrExprS (.app (.app (.const fc ls) a) b) e') + (hprim : Environment.primitives.contains fc) + (heval : c.venv.ReflectsNatNatBool fc f) : + RecM.WF c s (reduceBinNatPred f a b) fun oe _ => ∀ e₁, oe = some e₁ → + c.FVarsBelow (.app (.app (.const fc ls) a) b) e₁ ∧ c.TrExpr e₁ e' := by + let .app hb1 hb2 hf hb := he + let .app ha1 ha2 hf ha := hf + let .const h1 h2 h3 := hf + unfold reduceBinNatPred + refine (whnf.WF ha).bind fun a₁ _ _ ⟨a1, _, a2, a3⟩ => ?_ + split <;> [rename_i v1 h; exact .pure nofun]; cases (rawNatLitExt?.WF h a2).2 + refine (whnf.WF hb).bind fun b₁ _ _ ⟨b1, _, b2, b3⟩ => ?_ + split <;> [rename_i v2 h; exact .pure nofun]; cases (rawNatLitExt?.WF h b2).2 + refine .pure ?_; rintro _ ⟨⟩; refine ⟨fun _ _ _ => .boolLit, ?_⟩ + have ⟨ci, c1, _⟩ := c.trenv.find?_iff.2 ⟨_, h1⟩ + have ⟨_, c3⟩ := c.safePrimitives c1 hprim + have ⟨_, d1, d2, d3⟩ := c.trenv.find?_uniq c1 h1 + simp [c3] at d2; simp [← d2] at h3; simp [h3] at h2; subst h2 + have := heval ⟨_, h1⟩ v1 v2 |>.instL (U' := c.lparams.length) (ls := []) nofun + simp [VExpr.instL] at this + refine ⟨_, (TrExprS.boolLit c.hasPrimitives ?_ _).1, ?_⟩ + · let ⟨_, H⟩ := this + exact VExpr.WF.boolLit_has_type c.Ewf c.hasPrimitives (Γ := []) trivial ⟨_, H.hasType.2⟩ + refine this.weak0 c.Ewf (Γ := c.vlctx.toCtx) |>.symm.trans c.Ewf c.Δwf ?_ + have a3 := a3.of_r c.Ewf c.Δwf ha2 + have b3 := b3.of_r c.Ewf c.Δwf hb2 + have := ha1.appDF a3 |>.toU.of_r c.Ewf c.Δwf hb1 + exact ⟨_, .appDF this b3⟩ + +theorem reduceNat.WF {c : VContext} (he : c.TrExprS e e') : + RecM.WF c s (reduceNat e) fun oe _ => ∀ e₁, oe = some e₁ → + c.FVarsBelow e e₁ ∧ c.TrExpr e₁ e' := by + generalize hP : (fun oe => _) = P + refine let prims := _; have hprims : Environment.primitives = .ofList prims := rfl; ?_ + replace hprims {a} : Environment.primitives.contains a ↔ a ∈ prims := by + simp [hprims, NameSet.contains, NameSet.ofList] + unfold reduceNat; extract_lets nargs F1 fn + cases h1 : nargs == 1 <;> simp only [Bool.false_eq_true, ↓reduceIte] + · cases nargs == 2 <;> [exact hP ▸ .pure nofun; simp only [↓reduceIte]] + split <;> [rename_i f ls a b; exact hP ▸ .pure nofun] + have hfun checkSize {g fc G} (hprim : fc ∈ prims) + (heval : c.venv.ReflectsNatNatNat fc g) (hG : RecM.WF c s G P) : + RecM.WF c s (do if f == fc then {return ← reduceBinNatOp g a b checkSize}; G) P := by + split <;> [rename_i h; exact hG] + simp at h ⊢; subst h + exact hP ▸ reduceBinNatOp.WF he (hprims.2 hprim) heval + have hpow {fc G} (hprim : fc ∈ prims) + (heval : c.venv.ReflectsNatNatNat fc Nat.pow) (hG : RecM.WF c s G P) : + RecM.WF c s (do if f == fc then {return ← reducePow a b}; G) P := by + split <;> [rename_i h; exact hG] + simp at h ⊢; subst h + exact hP ▸ reducePow.WF he (hprims.2 hprim) heval + have hshl {fc G} (hprim : fc ∈ prims) + (heval : c.venv.ReflectsNatNatNat fc (· <<< ·)) (hG : RecM.WF c s G P) : + RecM.WF c s (do if f == fc then {return ← reduceShiftLeft a b}; G) P := by + split <;> [rename_i h; exact hG] + simp at h ⊢; subst h + exact hP ▸ reduceShiftLeft.WF he (hprims.2 hprim) heval + have hpred {g fc G} (hprim : fc ∈ prims) + (heval : c.venv.ReflectsNatNatBool fc g) (hG : RecM.WF c s G P) : + RecM.WF c s (do if f == fc then {return ← reduceBinNatPred g a b}; G) P := by + split <;> [rename_i h; exact hG] + simp at h ⊢; subst h + exact hP ▸ reduceBinNatPred.WF he (hprims.2 hprim) heval + apply hfun true (by simp [prims]) c.hasPrimitives.natAdd + apply hfun true (by simp [prims]) c.hasPrimitives.natSub + apply hfun true (by simp [prims]) c.hasPrimitives.natMul + apply hpow (by simp [prims]) c.hasPrimitives.natPow + apply hfun false (by simp [prims]) c.hasPrimitives.natGcd + apply hfun false (by simp [prims]) c.hasPrimitives.natMod + apply hfun false (by simp [prims]) c.hasPrimitives.natDiv + apply hpred (by simp [prims]) c.hasPrimitives.natBEq + apply hpred (by simp [prims]) c.hasPrimitives.natBLE + apply hfun false (by simp [prims]) c.hasPrimitives.natLAnd + apply hfun false (by simp [prims]) c.hasPrimitives.natLOr + apply hfun false (by simp [prims]) c.hasPrimitives.natXor + apply hshl (by simp [prims]) c.hasPrimitives.natShiftLeft + apply hfun false (by simp [prims]) c.hasPrimitives.natShiftRight + exact hP ▸ .pure nofun + · split <;> [rename_i h2; exact hP ▸ .pure nofun] + simp [nargs, Expr.getAppNumArgs_eq] at h1; subst fn + let .app f a := e; simp [Expr.appFn!, Expr.structuralEq_const] at h2 ⊢; subst h2 + let .app ha1 ha2 hf ha := he + let .const h1 h2 h3 := hf + refine (whnf.WF ha).bind fun a₁ _ _ ⟨a1, _, a2, a3⟩ => ?_ + split <;> [rename_i n h; exact hP ▸ .pure nofun] + obtain ⟨hn, rfl⟩ := rawNatLitExt?.WF h a2 + refine hP ▸ (checkNatSize.WF (Q := fun _ _ => True) trivial).map fun _ _ _ _ => ?_ + rintro _ ⟨⟩; refine ⟨fun _ _ _ => trivial, ?_⟩ + have ⟨ci, c1, _⟩ := c.trenv.find?_iff.2 ⟨_, h1⟩ + have ⟨c2, c3⟩ := c.safePrimitives c1 <| hprims.2 (by simp [prims]) + have ⟨d1, d2, d3⟩ := c.trenv.find?_uniq c1 h1; cases h2 + refine have ⟨p1, p2⟩ := TrExprS.natLit c.hasPrimitives hn _; ⟨_, p1, ?_⟩ + refine p2.toU.symm.trans c.Ewf c.Δwf ?_ + exact ⟨_, ha1.appDF <| a3.of_r c.Ewf c.Δwf ha2⟩ diff --git a/Ix/Theory/Named/Verify/TypeChecker/WHNF.lean b/Ix/Theory/Named/Verify/TypeChecker/WHNF.lean new file mode 100644 index 000000000..ab307a02f --- /dev/null +++ b/Ix/Theory/Named/Verify/TypeChecker/WHNF.lean @@ -0,0 +1,275 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.TypeChecker.Reduce + +namespace Ix.Theory.Named.TypeChecker.Inner +open Lean hiding Environment Exception + +set_option warn.sorry false in +theorem reduceRecursor.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + RecM.WF c s (reduceRecursor e) fun oe _ => + ∀ e₁, oe = some e₁ → c.FVarsBelow e e₁ ∧ c.TrExpr e₁ e' := sorry + +theorem whnfFVar.WF {c : VContext} {s : VState} (he : c.TrExprS (.fvar fv) e') : + RecM.WF c s (whnfFVar (.fvar fv) cheapProj) fun e₁ _ => + c.FVarsBelow (.fvar fv) e₁ ∧ c.TrExpr e₁ e' := by + refine .getLCtx ?_ + simp [Expr.fvarId!]; split <;> [skip; exact .pure ⟨.rfl, he.trExpr c.Ewf c.Δwf⟩] + rename_i decl h + rw [c.trlctx.1.find?_eq_find?_toList] at h + have := List.find?_some h; simp at this; subst this + let ⟨e', ty', h1, h2, _, h3, _⟩ := + c.trlctx.find?_of_mem c.Ewf (List.mem_of_find?_eq_some h) + refine (whnfCore.WF h3).mono fun _ _ _ ⟨h4, h5⟩ => ?_ + refine ⟨h2.trans h4, h5.defeq c.Ewf c.Δwf ?_⟩ + refine (TrExprS.fvar h1).uniq c.Ewf ?_ he + exact .refl c.Ewf c.Δwf + +theorem reduceProj.WF {c : VContext} {s : VState} (he : c.TrExprS (.proj n i e) e') : + RecM.WF c s (reduceProj i e cheapProj) fun oe _ => + ∀ e₁, oe = some e₁ → c.FVarsBelow (.proj n i e) e₁ ∧ c.TrExpr e₁ e' := by + let .proj (e' := major) heMajor hproj := he + obtain ⟨view, levels, params, _hviewName, hsemantic⟩ := hproj + obtain ⟨code, hcode, hresult, hprojector⟩ := hsemantic.program + have finish {normal : Expr} {state : VState} + (hbelow : c.FVarsBelow e normal) + (htr : c.TrExpr normal major) : + RecM.WF c state + (normal.withApp fun mk args => do + let .const mkC _ := mk | return none + let env ← getEnv + let .ctorInfo mkInfo ← env.get mkC | return none + return args[mkInfo.numParams + i]?) (fun oe _ => + ∀ e₁, oe = some e₁ → + c.FVarsBelow (.proj n i e) e₁ ∧ c.TrExpr e₁ e') := by + rw [Expr.withApp_eq] + split + · rename_i mkC hostLevels hheadShape + obtain ⟨runtimeMajor, hnormalS, hnormalEq⟩ := htr + have ⟨runtimeHead, hstack⟩ := AppStack.build + (normal.mkAppList_getAppArgsList ▸ hnormalS) + have hhead := hstack.tr + rw [hheadShape] at hhead + let .const (us' := runtimeLevels) _hconst _hlevelsMap + _hlevelsLength := hhead + obtain ⟨runtimeArgs, hargsTr, hfull⟩ := hstack.argsTranslation + rw [normal.mkAppList_getAppArgsList] at hfull + have hfullEq := hfull.uniq c.Ewf (.refl c.Ewf c.Δwf) hnormalS + have hmajorEq := hfullEq.trans c.Ewf c.Δwf hnormalEq + refine .getEnv ?_ + refine (M.WF.liftExcept envGet.WF).lift.bind fun _ci _ _ hfind => ?_ + split + · rename_i mkInfo + refine .pure ?_ + intro selected hselected + have hconstructorName : mkC = view.constructorName := + c.Ewf.registeredStructureHeadInversion.constructor_name_inv + c.Δwf hsemantic rfl hmajorEq + have hnumParams : mkInfo.numParams = view.nparams := + c.projectionReady.constructorNumParams view mkInfo + hsemantic.viewWF (by + rw [← hconstructorName] + exact hfind) + have hselectedList : + normal.getAppArgsList[mkInfo.numParams + i]? = some selected := by + rw [← Expr.getAppArgs_toList, Array.getElem?_toList] + exact hselected + obtain ⟨runtimeField, hfieldGet, hfieldTr⟩ := + Ix.Theory.Named.List.Forall₂.getElem?_left hargsTr hselectedList + have hfieldGetCanonical : + runtimeArgs[view.nparams + i]? = some runtimeField := by + rw [← hnumParams] + exact hfieldGet + obtain ⟨alignment⟩ := + c.Ewf.registeredStructureHeadInversion.constructor_inv + c.Δwf hsemantic hcode rfl hfieldGetCanonical hmajorEq + have hiota := hsemantic.projector_constructor_aligned + c.Ewf c.Δwf hcode hprojector alignment + have hmajorTyped := hmajorEq.of_r c.Ewf c.Δwf hsemantic.majorType + have hprojectorCongr : c.IsDefEqU + (.app code.projector + (VExpr.appN (.const mkC runtimeLevels) runtimeArgs)) + (.app code.projector major) := + ⟨_, hprojector.appDF hmajorTyped⟩ + have hfieldTarget : c.IsDefEqU runtimeField e' := by + rw [hresult] + exact hiota.symm.trans c.Ewf c.Δwf hprojectorCongr + refine ⟨?_, ⟨runtimeField, hfieldTr, hfieldTarget⟩⟩ + intro P hP hprojFv + exact FVarsIn.getAppArgsList (hbelow P hP hprojFv) + (List.mem_of_getElem? hselectedList) + · exact .pure nofun + · exact .pure nofun + unfold reduceProj + split + · refine (whnfCore.WF heMajor).bind fun normal _ _ hnormal => ?_ + split + · obtain ⟨literalMajor, hliteralS, hliteralEq⟩ := hnormal.2 + let .lit _ hconstructorS := hliteralS + refine (whnf.WF hconstructorS).bind fun expanded _ _ hexpanded => ?_ + have hbelow' : c.FVarsBelow e expanded := + FVarsBelow.trans (fun _ _ _ => FVarsIn.strLitToConstructor) + hexpanded.1 + have htr' := hexpanded.2.defeq c.Ewf c.Δwf hliteralEq + exact RecM.WF.pureBind (finish hbelow' htr') + · exact RecM.WF.pureBind (finish hnormal.1 hnormal.2) + · refine (whnf.WF heMajor).bind fun normal _ _ hnormal => ?_ + split + · obtain ⟨literalMajor, hliteralS, hliteralEq⟩ := hnormal.2 + let .lit _ hconstructorS := hliteralS + refine (whnf.WF hconstructorS).bind fun expanded _ _ hexpanded => ?_ + have hbelow' : c.FVarsBelow e expanded := + FVarsBelow.trans (fun _ _ _ => FVarsIn.strLitToConstructor) + hexpanded.1 + have htr' := hexpanded.2.defeq c.Ewf c.Δwf hliteralEq + exact RecM.WF.pureBind (finish hbelow' htr') + · exact RecM.WF.pureBind (finish hnormal.1 hnormal.2) + +theorem whnfCore'.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + RecM.WF c s (whnfCore' e cheapProj) fun e₁ _ => + c.FVarsBelow e e₁ ∧ c.TrExpr e₁ e' := by + unfold whnfCore'; extract_lets F + let full := (· matches Expr.fvar _ | .app .. | .letE .. | .proj ..) + generalize hP : (fun e₁ (_ : VState) => _) = P + have hid {s} : RecM.WF c s (pure e) P := hP ▸ .pure ⟨.rfl, he.trExpr c.Ewf c.Δwf⟩ + suffices hF : full e → RecM.WF c s (F ⟨⟩) P by + split + any_goals exact hid + any_goals exact hF rfl + · let .mdata he := he + exact hP ▸ whnfCore'.WF he + · refine .getLCtx ?_; split <;> [exact hid; exact hF rfl] + simp [F]; refine fun hfull => .get ?_; split + · rename_i r eq; refine .stateWF fun wf => hP ▸ .pure ?_ + have ⟨_, h1, h2, h3⟩ := (wf.whnfCore_wf eq).2.2.2.2 he.fvarsIn + refine ⟨h1, h3.defeq c.Ewf c.Δwf ?_⟩ + exact h2.uniq c.Ewf (.refl c.Ewf c.Δwf) he + have hsave {e₁ s} (h1 : c.FVarsBelow e e₁) (h2 : c.TrExpr e₁ e') : + (save e cheapProj e₁).WF c s P := by + simp [save] + split <;> [skip; exact hP ▸ .pure ⟨h1, h2⟩] + rintro _ mwf wf a s' ⟨⟩ + refine let s' := _; ⟨s', rfl, ?_⟩ + have hic {ic} (hic : WHNFCache.WF c s ic) : WHNFCache.WF c s (ic.insert e e₁) := by + intro _ _ h + rw [Std.HashMap.getElem?_insert] at h; split at h <;> [cases h; exact hic h] + rename_i eq + refine .mk c.mlctx.noBV (.eqv h1 eq BEq.rfl) (he.eqv eq) h2 (.eqv eq ?_) ?_ --_ (.eqv h2 eq BEq.rfl) (.eqv eq ?_) ?_ + · exact he.fvarsIn.mono wf.ngen_wf + · exact h2.fvarsIn.mono wf.ngen_wf + exact hP ▸ ⟨.rfl, { wf with whnfCore_wf := hic wf.whnfCore_wf }, h1, h2⟩ + split <;> cases hfull + · exact hP ▸ whnfFVar.WF he + · rename_i fn arg _; generalize eq : fn.app arg = e at * + have ⟨_, stk⟩ := AppStack.build <| e.mkAppList_getAppArgsList ▸ he + refine (whnfCore.WF stk.tr).bind fun _ s _ ⟨h1, h2⟩ => ?_ + split <;> [rename_i name dom body bi _; split] + · let rec loop.WF {e e' i rargs f} (H : LambdaBodyN i e' f) (hi : i ≤ rargs.size) : + ∃ n f', LambdaBodyN n e' f' ∧ n ≤ rargs.size ∧ + loop e cheapProj rargs i f = loop.cont e cheapProj rargs n f' := by + unfold loop; split + · split + · refine loop.WF (by simpa [Nat.add_comm] using H.add (.succ .zero)) ‹_› + · exact ⟨_, _, H, hi, rfl⟩ + · exact ⟨_, _, H, hi, rfl⟩ + refine + let ⟨i, f, h3, h4, eq⟩ := loop.WF (e' := .lam name dom body bi) (.succ .zero) <| by + simp [← eq, Expr.getAppRevArgs_eq, Expr.getAppArgsRevList] + eq ▸ ?_; clear eq + simp [Expr.getAppRevArgs_eq] at h4 ⊢ + obtain ⟨l₁, l₂, h5, rfl⟩ : ∃ l₁ l₂, e.getAppArgsRevList = l₁ ++ l₂ ∧ l₂.length = i := + ⟨_, _, (List.take_append_drop (e.getAppArgsRevList.length - i) ..).symm, by simp; omega⟩ + simp [loop.cont, h5, List.take_of_length_le] + rw [Expr.mkAppRevRange_eq_rev (l₁ := []) (l₂ := l₁) (l₃ := l₂) (by simp) (by rfl) (by rfl)] + have br := BetaReduce.inst_reduce (l₁ := l₂.reverse) + [] (by simpa using h3) (Expr.instantiateList_append ..) (h := by + have := h5 ▸ (c.mlctx.noBV ▸ he.closed).getAppArgsRevList + simp [or_imp, forall_and] at this ⊢ + exact this.2) |>.mkAppRevList (es := l₁) + simp [← Expr.mkAppRevList_reverse, ← Expr.mkAppRevList_append, ← h5] at br + have := h2.rebuild_mkAppRevList c.Ewf c.Δwf stk.tr <| + e.mkAppRevList_getAppArgsRevList ▸ he + have ⟨_, a1, a2⟩ := this.beta c.Ewf c.Δwf br + refine (whnfCore.WF a1).bind fun _ _ _ ⟨b1, b2⟩ => ?_ + have hb := e.mkAppRevList_getAppArgsRevList ▸ h1.mkAppRevList + exact hsave (hb.trans (.betaReduce br) |>.trans b1) <| + b2.defeq c.Ewf c.Δwf a2 + · refine (reduceRecursor.WF he).bind fun _ _ _ h => ?_ + split <;> [skip; exact hid] + let ⟨h1, _, h2, eq⟩ := h _ rfl + refine hP ▸ (whnfCore.WF h2).mono fun _ _ _ ⟨h3, h4⟩ => ?_ + exact ⟨h1.trans h3, h4.defeq c.Ewf c.Δwf eq⟩ + · rw [Expr.mkAppRevRange_eq_rev (l₁ := []) (l₃ := []) + (by simp [Expr.getAppRevArgs_toList]; rfl) (by rfl) (by simp [Expr.getAppRevArgs_eq])] + have {e e₁ : Expr} (hb : c.FVarsBelow e e₁) {es e₀' e'} + (hes : c.TrExprS (e.mkAppRevList es) e₀') (he : c.TrExprS e e') (he₁ : c.TrExpr e₁ e') : + c.FVarsBelow (e.mkAppRevList es) (e₁.mkAppRevList es) ∧ + c.TrExpr (e₁.mkAppRevList es) e₀' := by + induction es generalizing e₁ e₀' e' with + | nil => + refine ⟨hb, he₁.defeq c.Ewf c.Δwf ?_⟩ + exact he.uniq c.Ewf (.refl c.Ewf c.Δwf) hes + | cons _ _ ih => + have .app h1 h2 h3 h4 := hes + have ⟨h5, h6⟩ := ih hb h3 he he₁ + exact ⟨fun _ hP he => ⟨h5 _ hP he.1, he.2⟩, + .app c.Ewf c.Δwf h1 h2 h6 (h4.trExpr c.Ewf c.Δwf)⟩ + have eq := e.mkAppRevList_getAppArgsRevList + let ⟨h3, _, h4, eq⟩ := eq ▸ this h1 (eq ▸ he) stk.tr h2 + refine (whnfCore.WF h4).bind fun _ _ _ ⟨h5, h6⟩ => ?_ + refine hsave (h3.trans h5) (h6.defeq c.Ewf c.Δwf eq) + · let .letE h1 h2 h3 h4 := he + refine (whnfCore.WF (h4.inst_let c.Ewf.ordered h3)).bind fun _ _ _ ⟨h1, h2⟩ => ?_ + exact hsave (.trans (fun _ _ he => he.2.2.instantiate1 he.2.1) h1) h2 + · refine (reduceProj.WF he).bind fun _ _ _ H => ?_ + split + · let ⟨h1, _, h2, eq⟩ := H _ rfl + refine (whnfCore.WF h2).bind fun _ _ _ ⟨h3, h4⟩ => ?_ + exact hsave (h1.trans h3) (h4.defeq c.Ewf c.Δwf eq) + · exact hsave .rfl (he.trExpr c.Ewf c.Δwf) + +theorem whnf'.WF {c : VContext} {s : VState} (he : c.TrExprS e e') : + RecM.WF c s (whnf' e) fun e₁ _ => c.FVarsBelow e e₁ ∧ c.TrExpr e₁ e' := by + unfold whnf'; extract_lets F + generalize hP : (fun e₁ (_ : VState) => _) = P + have hid {s} : RecM.WF c s (pure e) P := hP ▸ .pure ⟨.rfl, he.trExpr c.Ewf c.Δwf⟩ + suffices hF : RecM.WF c s (F ()) P by + split + any_goals exact hid + any_goals exact hF + · let .mdata he := he + exact hP ▸ whnf'.WF he + · refine .getLCtx ?_; split <;> [exact hid; exact hF] + simp [F]; refine .get ?_; split + · rename_i r eq; refine .stateWF fun wf => hP ▸ .pure ?_ + have ⟨_, h1, h2, h3⟩ := (wf.whnf_wf eq).2.2.2.2 he.fvarsIn + refine ⟨h1, h3.defeq c.Ewf c.Δwf ?_⟩ + exact h2.uniq c.Ewf (.refl c.Ewf c.Δwf) he + have {e e' s n} (he : c.TrExprS e e') : (loop e n).WF c s fun e₁ _ => + c.FVarsBelow e e₁ ∧ c.TrExpr e₁ e' := by + induction n generalizing s e e' with | zero => exact .throw | succ n ih => ?_ + refine .getEnv <| (whnfCore'.WF he).bind fun e₁ s _ ⟨h1, _, he₁, eq⟩ => ?_ + refine (M.WF.liftExcept reduceNative.WF).lift.bind fun _ _ _ h3 => ?_ + split <;> [cases h3 _ rfl; skip] + refine (reduceNat.WF he₁).bind fun _ _ _ h3 => ?_; split + · exact .pure ⟨.trans h1 (h3 _ rfl).1, (h3 _ rfl).2.defeq c.Ewf c.Δwf eq⟩ + refine (unfoldDefinition.WF he₁).bind fun _ _ _ H => ?_ + split <;> [skip; exact .pure ⟨h1, _, he₁, eq⟩] + have ⟨a1, _, a2, eq'⟩ := H + refine (ih a2).mono fun _ _ _ ⟨b1, b2⟩ => ?_ + exact ⟨h1.trans <| a1.trans b1, b2.defeq c.Ewf c.Δwf <| eq'.trans c.Ewf c.Δwf eq⟩ + refine .readThe <| (this he).bind fun e₁ s _ ⟨h1, h2⟩ => ?_ + rintro _ mwf wf a s' ⟨⟩ + refine let s' := _; ⟨s', rfl, ?_⟩ + have hic {ic} (hic : WHNFCache.WF c s ic) : WHNFCache.WF c s (ic.insert e e₁) := by + intro _ _ h + rw [Std.HashMap.getElem?_insert] at h; split at h <;> [cases h; exact hic h] + rename_i eq + refine .mk c.mlctx.noBV (.eqv h1 eq BEq.rfl) (he.eqv eq) h2 (.eqv eq ?_) ?_ + · exact he.fvarsIn.mono wf.ngen_wf + · exact h2.fvarsIn.mono wf.ngen_wf + exact hP ▸ ⟨.rfl, { wf with whnf_wf := hic wf.whnf_wf }, h1, h2⟩ diff --git a/Ix/Theory/Named/Verify/Typing/ConditionallyTyped.lean b/Ix/Theory/Named/Verify/Typing/ConditionallyTyped.lean new file mode 100644 index 000000000..80f91f5e1 --- /dev/null +++ b/Ix/Theory/Named/Verify/Typing/ConditionallyTyped.lean @@ -0,0 +1,150 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.Typing.Lemmas + +namespace Ix.Theory.Named +open VEnv Lean + +def ConditionallyTyped + (ngen : NameGenerator) (env : VEnv) (Us : List Name) (Δ : VLCtx) (e : Expr) : Prop := + Closed e ∧ FVarsIn ngen.Reserves e ∧ (FVarsIn (· ∈ Δ.fvars) e → ∃ e', TrExprS env Us Δ e e') + +theorem ConditionallyTyped.mk {Δ : VLCtx} + (noBV : Δ.NoBV) (r : FVarsIn ngen.Reserves e) (H : TrExprS env Us Δ e e') : + ConditionallyTyped ngen env Us Δ e := ⟨noBV ▸ H.closed, r, fun _ => ⟨_, H⟩⟩ + +theorem ConditionallyTyped.mono (H : ngen₁ ≤ ngen₂) : + ConditionallyTyped ngen₁ env Us Δ e → ConditionallyTyped ngen₂ env Us Δ e + | ⟨h1, h2, h3⟩ => ⟨h1, h2.mono fun _ h => h.mono H, h3⟩ + +theorem ConditionallyTyped.weakN_inv + (henv : VEnv.WF env) (hΔ : VLCtx.WF env Us.length ((some fv, d) :: Δ)) + (H : ConditionallyTyped ngen env Us ((some fv, d) :: Δ) e) : + ConditionallyTyped ngen env Us Δ e := by + refine ⟨H.1, H.2.1, fun H2 => ?_⟩ + have := H.2.2 + have ⟨e', h⟩ := H.2.2 H2.fvars_cons + exact TrExprS.weakFV_inv henv (.skip_fvar _ _ .refl) (.refl henv hΔ) h H.1 H2 + +theorem ConditionallyTyped.fresh + (henv : Ordered env) (hΔ : VLCtx.WF env Us.length ((some (⟨ngen.curr⟩, deps), d) :: Δ)) + (H : ConditionallyTyped ngen env Us Δ e) : + ConditionallyTyped ngen env Us ((some (⟨ngen.curr⟩, deps), d) :: Δ) e := by + refine have ⟨H1, H2, H3⟩ := H; ⟨H1, H2, fun H4 => ?_⟩ + refine have ⟨_, h⟩ := H3 (H4.mp ?_ H2); ⟨_, h.weakFV henv (.skip_fvar _ _ .refl) hΔ⟩ + intro _ h1 h2; simp at h1; rcases h1 with rfl | h1 + · cases Nat.lt_irrefl _ (h2 _ rfl) + · exact h1 + +def ConditionallyHasType + (ngen : NameGenerator) (env : VEnv) (Us : List Name) (Δ : VLCtx) (e A : Expr) : Prop := + Closed e ∧ FVarsIn ngen.Reserves e ∧ Closed A ∧ FVarsIn ngen.Reserves A ∧ + (FVarsIn (· ∈ Δ.fvars) e → ∃ e' A', TrTyping env Us Δ e A e' A') + +theorem ConditionallyHasType.mk {Δ : VLCtx} + (noBV : Δ.NoBV) (he : TrTyping env Us Δ e A e' A') + (re : FVarsIn ngen.Reserves e) (rA : FVarsIn ngen.Reserves A) : + ConditionallyHasType ngen env Us Δ e A := by + refine ⟨noBV ▸ he.2.1.closed, re, noBV ▸ he.2.2.1.closed, rA, fun _ => ⟨_, _, he.1, he.2⟩⟩ + +theorem ConditionallyHasType.typed : + ConditionallyHasType ngen env Us Δ e A → ConditionallyTyped ngen env Us Δ e + | ⟨c1, f1, _, _, H⟩ => ⟨c1, f1, fun h => let ⟨_, _, _, h, _⟩ := H h; ⟨_, h⟩⟩ + +theorem ConditionallyHasType.mono (H : ngen₁ ≤ ngen₂) : + ConditionallyHasType ngen₁ env Us Δ e A → ConditionallyHasType ngen₂ env Us Δ e A + | ⟨c1, f1, c2, f2, h'⟩ => ⟨c1, f1.mono fun _ h => h.mono H, c2, f2.mono fun _ h => h.mono H, h'⟩ + +theorem ConditionallyHasType.weakN_inv + (henv : VEnv.WF env) (hΔ : VLCtx.WF env Us.length ((some fv, d) :: Δ)) + (H : ConditionallyHasType ngen env Us ((some fv, d) :: Δ) e A) : + ConditionallyHasType ngen env Us Δ e A := by + have ⟨c1, f1, c2, f2, H⟩ := H + refine ⟨c1, f1, c2, f2, fun H4 => ?_⟩ + have ⟨e', A', h1, h2, h3, h4⟩ := H H4.fvars_cons + have W : VLCtx.FVLift Δ ((some fv, d) :: Δ) 0 (0 + d.depth) 0 := .skip_fvar _ _ .refl + have ⟨e'', he⟩ := TrExprS.weakFV_inv henv W (.refl henv hΔ) h2 c1 H4 + have ee := h2.uniq henv (.refl henv hΔ) <| he.weakFV henv W hΔ + have := hΔ.2.1 _ _ rfl + have : IsFVarUpSet (· ∈ VLCtx.fvars Δ) ((some fv, d) :: Δ) := + ⟨.fvars hΔ.1.fvwf, (hΔ.2.1 _ _ rfl).1.elim⟩ + have ⟨_, hA⟩ := TrExprS.weakFV_inv henv W (.refl henv hΔ) h3 c2 <| h1 _ this H4 + have AA := h3.uniq henv (.refl henv hΔ) <| hA.weakFV henv W hΔ + have h4 := h4.defeqU_r henv hΔ.toCtx AA |>.defeqU_l henv hΔ.toCtx ee + have h4 := (HasType.weakN_iff henv hΔ.toCtx W.toCtx).1 h4 + refine ⟨_, _, fun P hP he' => ?_, he, hA, h4⟩ + exact h1 _ + ⟨(IsFVarUpSet.and_fvars hΔ.1.fvwf).1 hP, fun h => (hΔ.2.1 _ _ rfl).1.elim h.2⟩ + (he'.mp (fun _ => .intro) he.fvarsIn) |>.mono fun _ => (·.1) + +theorem ConditionallyHasType.fresh + (henv : Ordered env) + (hΔ : VLCtx.WF env Us.length ((some (⟨ngen.curr⟩, deps), d) :: Δ)) + (H : ConditionallyHasType ngen env Us Δ e A) : + ConditionallyHasType ngen env Us ((some (⟨ngen.curr⟩, deps), d) :: Δ) e A := by + refine have ⟨c1, f1, c2, f2, H⟩ := H; ⟨c1, f1, c2, f2, fun H4 => ?_⟩ + have ⟨_, _, h1, h2, h3, h4⟩ := H (H4.mp ?_ f1) + · have W : VLCtx.FVLift Δ ((some (⟨ngen.curr⟩, deps), d) :: Δ) 0 (0 + d.depth) 0 := + .skip_fvar _ _ .refl + exact ⟨_, _, fun P hP => h1 _ hP.1, + h2.weakFV henv W hΔ, h3.weakFV henv W hΔ, h4.weakN henv W.toCtx⟩ + · intro _ h1 h2; simp at h1; rcases h1 with rfl | h1 + · cases Nat.lt_irrefl _ (h2 _ rfl) + · exact h1 + +def ConditionallyWHNF + (ngen : NameGenerator) (env : VEnv) (Us : List Name) (Δ : VLCtx) (e e₁ : Expr) : Prop := + Closed e ∧ FVarsIn ngen.Reserves e ∧ Closed e₁ ∧ FVarsIn ngen.Reserves e₁ ∧ + (FVarsIn (· ∈ Δ.fvars) e → ∃ e', + FVarsBelow Δ e e₁ ∧ TrExprS env Us Δ e e' ∧ TrExpr env Us Δ e₁ e') + +theorem ConditionallyWHNF.mk {Δ : VLCtx} + (noBV : Δ.NoBV) (hb : FVarsBelow Δ e e₁) + (he : TrExprS env Us Δ e e') (he₁ : TrExpr env Us Δ e₁ e') + (re : FVarsIn ngen.Reserves e) (re₁ : FVarsIn ngen.Reserves e₁) : + ConditionallyWHNF ngen env Us Δ e e₁ := by + refine ⟨noBV ▸ he.closed, re, noBV ▸ he₁.closed, re₁, fun _ => ⟨_, hb, he, he₁⟩⟩ + +theorem ConditionallyWHNF.mono (H : ngen₁ ≤ ngen₂) : + ConditionallyWHNF ngen₁ env Us Δ e A → ConditionallyWHNF ngen₂ env Us Δ e A + | ⟨c1, f1, c2, f2, h'⟩ => ⟨c1, f1.mono fun _ h => h.mono H, c2, f2.mono fun _ h => h.mono H, h'⟩ + +theorem ConditionallyWHNF.weakN_inv + (henv : VEnv.WF env) (hΔ : VLCtx.WF env Us.length ((some fv, d) :: Δ)) + (H : ConditionallyWHNF ngen env Us ((some fv, d) :: Δ) e e₁) : + ConditionallyWHNF ngen env Us Δ e e₁ := by + have ⟨c1, f1, c2, f2, H⟩ := H + refine ⟨c1, f1, c2, f2, fun H4 => ?_⟩ + have ⟨e', h1, h2, _, h3, h4⟩ := H H4.fvars_cons + have W : VLCtx.FVLift Δ ((some fv, d) :: Δ) 0 (0 + d.depth) 0 := .skip_fvar _ _ .refl + have ⟨e'', he⟩ := TrExprS.weakFV_inv henv W (.refl henv hΔ) h2 c1 H4 + have ee := h2.uniq henv (.refl henv hΔ) <| he.weakFV henv W hΔ + have := hΔ.2.1 _ _ rfl + have : IsFVarUpSet (· ∈ VLCtx.fvars Δ) ((some fv, d) :: Δ) := + ⟨.fvars hΔ.1.fvwf, (hΔ.2.1 _ _ rfl).1.elim⟩ + have ⟨_, he₁⟩ := TrExprS.weakFV_inv henv W (.refl henv hΔ) h3 c2 <| h1 _ this H4 + have ee₁ := h3.uniq henv (.refl henv hΔ) <| he₁.weakFV henv W hΔ + have h4 := ee₁.symm.trans henv hΔ.toCtx h4 |>.trans henv hΔ.toCtx ee + have h4 := (IsDefEqU.weakN_iff henv hΔ.toCtx W.toCtx).1 h4 + refine ⟨_, fun P hP he' => ?_, he, _, he₁, h4⟩ + exact h1 _ + ⟨(IsFVarUpSet.and_fvars hΔ.1.fvwf).1 hP, fun h => (hΔ.2.1 _ _ rfl).1.elim h.2⟩ + (he'.mp (fun _ => .intro) he.fvarsIn) |>.mono fun _ => (·.1) + +theorem ConditionallyWHNF.fresh + (henv : env.WF) + (hΔ : VLCtx.WF env Us.length ((some (⟨ngen.curr⟩, deps), d) :: Δ)) + (H : ConditionallyWHNF ngen env Us Δ e e₁) : + ConditionallyWHNF ngen env Us ((some (⟨ngen.curr⟩, deps), d) :: Δ) e e₁ := by + refine have ⟨c1, f1, c2, f2, H⟩ := H; ⟨c1, f1, c2, f2, fun H4 => ?_⟩ + have ⟨_, h1, h2, h3⟩ := H (H4.mp ?_ f1) + · have W : VLCtx.FVLift Δ ((some (⟨ngen.curr⟩, deps), d) :: Δ) 0 (0 + d.depth) 0 := + .skip_fvar _ _ .refl + exact ⟨_, fun P hP => h1 _ hP.1, h2.weakFV henv W hΔ, h3.weakFV henv W hΔ⟩ + · intro _ h1 h2; simp at h1; rcases h1 with rfl | h1 + · cases Nat.lt_irrefl _ (h2 _ rfl) + · exact h1 diff --git a/Ix/Theory/Named/Verify/Typing/Expr.lean b/Ix/Theory/Named/Verify/Typing/Expr.lean new file mode 100644 index 000000000..da146d752 --- /dev/null +++ b/Ix/Theory/Named/Verify/Typing/Expr.lean @@ -0,0 +1,146 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Typing.Basic +import Ix.Theory.Named.Literals +import Ix.Theory.Named.Projection +import Ix.Theory.Named.Verify.NameGenerator +import Ix.Theory.Named.Verify.VLCtx +import Ix.Theory.Named.Verify.Axioms + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named +open Lean + +def Closed : Expr → (k :_:= 0) → Prop + | .bvar i, k => i < k + | .fvar _, _ | .sort .., _ | .const .., _ | .lit .., _ => True + | .app f a, k => Closed f k ∧ Closed a k + | .lam _ d b _, k + | .forallE _ d b _, k => Closed d k ∧ Closed b (k+1) + | .letE _ d v b _, k => Closed d k ∧ Closed v k ∧ Closed b (k+1) + | .proj _ _ e, k | .mdata _ e, k => Closed e k + | .mvar .., _ => False + +nonrec abbrev _root_.Lean.Expr.Closed := @Closed + +/-- This is very inefficient, only use for spec purposes -/ +def _root_.Lean.Expr.fvarsList : Expr → List FVarId + | .bvar _ | .sort .. | .const .. | .lit .. | .mvar .. => [] + | .fvar fv => [fv] + | .app f a => f.fvarsList ++ a.fvarsList + | .lam _ d b _ + | .forallE _ d b _ => d.fvarsList ++ b.fvarsList + | .letE _ d v b _ => d.fvarsList ++ v.fvarsList ++ b.fvarsList + | .proj _ _ e | .mdata _ e => e.fvarsList + +variable (fvars : FVarId → Prop) in +def FVarsIn : Expr → Prop + | .bvar _ => True + | .fvar fv => fvars fv + | .sort u => u.hasMVar' = false + | .const _ us => ∀ u ∈ us, u.hasMVar' = false + | .lit .. => True + | .app f a => FVarsIn f ∧ FVarsIn a + | .lam _ d b _ + | .forallE _ d b _ => FVarsIn d ∧ FVarsIn b + | .letE _ d v b _ => FVarsIn d ∧ FVarsIn v ∧ FVarsIn b + | .proj _ _ e | .mdata _ e => FVarsIn e + | .mvar .. => False + +nonrec abbrev _root_.Lean.Expr.FVarsIn := @FVarsIn + +def VLCtx.FVWF : VLCtx → Prop + | [] => True + | (ofv, _) :: (Δ : VLCtx) => + VLCtx.FVWF Δ ∧ (∀ fv deps, ofv = some (fv, deps) → fv ∉ Δ.fvars ∧ deps ⊆ Δ.fvars) + +variable (env : VEnv) (U : Nat) in +def VLCtx.WF : VLCtx → Prop + | [] => True + | (ofv, d) :: (Δ : VLCtx) => + VLCtx.WF Δ ∧ (∀ fv deps, ofv = some (fv, deps) → fv ∉ Δ.fvars ∧ deps ⊆ Δ.fvars) ∧ + VLocalDecl.WF env U Δ.toCtx d + +theorem VLCtx.WF.fvwf : ∀ {Δ}, VLCtx.WF env U Δ → Δ.FVWF + | [], h => h + | _ :: _, ⟨h1, h2, _⟩ => ⟨h1.fvwf, h2⟩ + +/-- Verify compatibility surface for Theory's environment-indexed projection +semantics. The view, universe instantiation, and parameter spine are hidden +from existing expression-translation consumers, but each witness is fully +constrained by `VEnv.TrProj`; no metadata is existentially invented. -/ +def TrProj (env : VEnv) (U : Nat) (Γ : List VExpr) + (structName : Name) (idx : Nat) (e result : VExpr) : Prop := + ∃ view levels params, + view.name = structName ∧ + env.TrProj U Γ view levels params idx e result + +variable (env : VEnv) (Us : List Name) in +inductive TrExprS : VLCtx → Expr → VExpr → Prop + | bvar : Δ.find? (.inl i) = some (e, A) → TrExprS Δ (.bvar i) e + | fvar : Δ.find? (.inr fv) = some (e, A) → TrExprS Δ (.fvar fv) e + | sort : VLevel.ofLevel Us u = some u' → TrExprS Δ (.sort u) (.sort u') + | const : + env.constants c = some ci → + us.mapM (VLevel.ofLevel Us) = some us' → + us.length = ci.uvars → + TrExprS Δ (.const c us) (.const c us') + | app : + env.HasType Us.length Δ.toCtx f' (.forallE A B) → + env.HasType Us.length Δ.toCtx a' A → + TrExprS Δ f f' → TrExprS Δ a a' → TrExprS Δ (.app f a) (.app f' a') + | lam : + env.IsType Us.length Δ.toCtx ty' → + TrExprS Δ ty ty' → TrExprS ((none, .vlam ty') :: Δ) body body' → + TrExprS Δ (.lam name ty body bi) (.lam ty' body') + | forallE : + env.IsType Us.length Δ.toCtx ty' → + env.IsType Us.length (ty' :: Δ.toCtx) body' → + TrExprS Δ ty ty' → TrExprS ((none, .vlam ty') :: Δ) body body' → + TrExprS Δ (.forallE name ty body bi) (.forallE ty' body') + | letE : + env.HasType Us.length Δ.toCtx val' ty' → + TrExprS Δ ty ty' → TrExprS Δ val val' → + TrExprS ((none, .vlet ty' val') :: Δ) body body' → + TrExprS Δ (.letE name ty val body nd) body' + | lit : env.ContainsLits l → TrExprS Δ l.toConstructor e → TrExprS Δ (.lit l) e + | mdata : TrExprS Δ e e' → TrExprS Δ (.mdata d e) e' + | proj : TrExprS Δ e e' → + TrProj env Us.length Δ.toCtx s i e' e'' → + TrExprS Δ (.proj s i e) e'' + +def TrExpr (env : VEnv) (Us : List Name) (Δ : VLCtx) (e : Expr) (e' : VExpr) : Prop := + ∃ e₂, TrExprS env Us Δ e e₂ ∧ env.IsDefEqU Us.length Δ.toCtx e₂ e' + +/-- Deterministic shadow of `TrExprS`: compute the strict Theory translation +of an expression syntactically. Every semantic premise of `TrExprS` only +validates a translation, it never selects between candidates, so on the +`TrExprS.IsUnique` fragment this function returns exactly the translation of +any derivation (`TrExprS.trExprS?_eq`). The function checks nothing +semantic: it is meaningful only through that agreement theorem. The pushed +`vlet` type is a dummy because `TrExprS` never reads it — `VLCtx.find?` +returns a let's value, and the type component is existentially discarded. -/ +def trExprS? (Us : List Name) : VLCtx → Expr → Option VExpr + | Δ, .bvar i => (Δ.find? (.inl i)).map (·.1) + | Δ, .fvar fv => (Δ.find? (.inr fv)).map (·.1) + | _, .sort u => (VLevel.ofLevel Us u).map .sort + | _, .const c us => (us.mapM (VLevel.ofLevel Us)).map (VExpr.const c) + | Δ, .app f a => do return .app (← trExprS? Us Δ f) (← trExprS? Us Δ a) + | Δ, .lam _ ty body _ => do + let ty' ← trExprS? Us Δ ty + return .lam ty' (← trExprS? Us ((none, .vlam ty') :: Δ) body) + | Δ, .forallE _ ty body _ => do + let ty' ← trExprS? Us Δ ty + return .forallE ty' (← trExprS? Us ((none, .vlam ty') :: Δ) body) + | Δ, .letE _ _ val body _ => do + let val' ← trExprS? Us Δ val + trExprS? Us ((none, .vlet (.sort .zero) val') :: Δ) body + | _, .lit l => some (.trLiteral l) + | Δ, .mdata _ e => trExprS? Us Δ e + | _, .proj .. => none + | _, .mvar .. => none diff --git a/Ix/Theory/Named/Verify/Typing/Lemmas.lean b/Ix/Theory/Named/Verify/Typing/Lemmas.lean new file mode 100644 index 000000000..7e4160ac5 --- /dev/null +++ b/Ix/Theory/Named/Verify/Typing/Lemmas.lean @@ -0,0 +1,2636 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Std.AxiomAudit +import Batteries.Data.String.Lemmas +import Ix.Theory.Named.Verify.Typing.Expr +import Ix.Theory.Named.Verify.Expr +import Ix.Theory.Named.Typing.Strong +import Ix.Theory.Named.Typing.UniqueTyping +import Ix.Theory.Named.Reference.Instantiate + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named +open Ix.Theory.Named VEnv Lean +open scoped _root_.List + +theorem fvarsIn_iff : FVarsIn P e ↔ (∀ fv ∈ e.fvarsList, P fv) ∧ FVarsIn (fun _ => True) e := by + induction e <;> simp [FVarsIn, Expr.fvarsList, *] <;> grind + +theorem fvarsIn_iff_hasMVar : FVarsIn (fun _ => True) e ↔ e.hasMVar = false := by + rw [Expr.hasMVar, ← Expr.hasExprMVar, ← Expr.hasLevelMVar]; simp + induction e <;> simp [FVarsIn, Expr.hasExprMVar', Expr.hasLevelMVar', and_assoc, and_left_comm, *] + +theorem fvarsList_eq_nil {e : Expr} : e.fvarsList = [] ↔ e.hasFVar = false := by + rw [Expr.hasFVar_eq] + induction e <;> simp [Expr.fvarsList, Expr.hasFVar', and_assoc, *] + +theorem FVarsIn.mp (H : ∀ fv, P fv → Q fv → R fv) : + ∀ {e}, FVarsIn P e → FVarsIn Q e → FVarsIn R e + | .bvar _, l, _ | .sort .., l, _ | .const .., l, _ | .lit .., l, _ => l + | .fvar _, l, r => H _ l r + | .app .., ⟨l1, l2⟩, ⟨r1, r2⟩ + | .lam .., ⟨l1, l2⟩, ⟨r1, r2⟩ + | .forallE .., ⟨l1, l2⟩, ⟨r1, r2⟩ => ⟨l1.mp H r1, l2.mp H r2⟩ + | .letE .., ⟨l1, l2, l3⟩, ⟨r1, r2, r3⟩ => ⟨l1.mp H r1, l2.mp H r2, l3.mp H r3⟩ + | .proj _ _ e, l, r | .mdata _ e, l, r => l.mp (e := e) H r + +theorem FVarsIn.mono (H : ∀ fv, P fv → Q fv) (h : FVarsIn P e) : FVarsIn Q e := + h.mp (fun _ h _ => H _ h) h + +theorem Closed.mono (H : k ≤ k') : ∀ {e}, Closed e k → Closed e k' + | .bvar _, h => Nat.lt_of_lt_of_le h H + | .fvar _, h | .sort .., h | .const .., h | .lit .., h => h + | .app .., ⟨h1, h2⟩ => ⟨h1.mono H, h2.mono H⟩ + | .lam .., ⟨h1, h2⟩ + | .forallE .., ⟨h1, h2⟩ => ⟨h1.mono H, h2.mono (Nat.succ_le_succ H)⟩ + | .letE .., ⟨h1, h2, h3⟩ => ⟨h1.mono H, h2.mono H, h3.mono (Nat.succ_le_succ H)⟩ + | .proj _ _ e, h | .mdata _ e, h => h.mono (e := e) H + +theorem FVarsIn.natLitToConstructor : FVarsIn P (.natLitToConstructor n) := by + cases n <;> simp [FVarsIn, Expr.natLitToConstructor, Expr.natZero, Expr.natSucc] + +theorem Closed.natLitToConstructor : Closed (.natLitToConstructor n) k := by + cases n <;> simp [Closed, Expr.natLitToConstructor, Expr.natZero, Expr.natSucc] + +theorem FVarsIn.strLitToConstructor : FVarsIn P (.strLitToConstructor s) := by + simp [FVarsIn, Expr.strLitToConstructor] + induction s.toList <;> simp [*, FVarsIn, Level.hasMVar'] + +theorem Closed.strLitToConstructor : Closed (.strLitToConstructor s) k := by + simp [Closed, Expr.strLitToConstructor] + induction s.toList <;> simp [*, Closed] + +theorem FVarsIn.toConstructor : ∀ {l : Literal}, FVarsIn P l.toConstructor + | .natVal _ => .natLitToConstructor + | .strVal _ => .strLitToConstructor + +theorem FVarsIn.litType {l : Literal} : FVarsIn P l.type := by + cases l <;> simp [FVarsIn, Literal.type] + +theorem Closed.toConstructor : ∀ {l : Literal}, Closed l.toConstructor k + | .natVal _ => .natLitToConstructor + | .strVal _ => .strLitToConstructor + +theorem toConstructor : ∀ {l : Literal}, Closed l.toConstructor k + | .natVal _ => .natLitToConstructor + | .strVal _ => .strLitToConstructor + +theorem Closed.litType {l : Literal} : Closed l.type k := by cases l <;> trivial + +theorem FVarsIn.fvars_cons : + FVarsIn (· ∈ VLCtx.fvars Δ) e → FVarsIn (· ∈ VLCtx.fvars ((ofv, d) :: Δ)) e := + FVarsIn.mono fun a h => by cases ofv <;> simp [h] + +theorem FVarsIn.abstract_instantiate1 (h : FVarsIn (· ≠ v) e) : + (Expr.instantiate1' e (.fvar v) k).abstract1 v k = e := by + induction e generalizing k with simp_all [Expr.instantiate1', Expr.abstract1, FVarsIn] + | bvar i => + split <;> [skip; split] + · simp [Expr.abstract1, *] + · simp [Expr.abstract1, Expr.liftLooseBVars', *] + · obtain _|i := i <;> simp [Expr.abstract1] <;> omega + | fvar v' => exact Ne.symm h + +theorem FVarsIn.abstract_eq_self (h : FVarsIn (· ≠ v) e) (hc : Closed e k) : + e.abstract1 v k = e := by + induction e generalizing k <;> simp_all [FVarsIn, Closed, Expr.abstract1] + exact Ne.symm h + +theorem FVarsIn.liftLooseBVars (h : FVarsIn P e) : FVarsIn P (Expr.liftLooseBVars' e s d) := by + induction e generalizing s <;> simp_all [FVarsIn, Expr.liftLooseBVars'] + +theorem FVarsIn.instantiate1_go (h1 : FVarsIn P e) (h2 : FVarsIn P a) : + FVarsIn P (Expr.instantiate1' e a k) := by + induction e generalizing k <;> simp_all [FVarsIn, Expr.instantiate1'] + (repeat' split) <;> simp [*, FVarsIn.liftLooseBVars, FVarsIn] + +theorem FVarsIn.instantiate1 (h1 : FVarsIn P e) (h2 : FVarsIn P a) : + FVarsIn P (Expr.instantiate1' e a) := h1.instantiate1_go h2 + +theorem FVarsIn.instantiateList (h1 : FVarsIn P e) (h2 : ∀ a ∈ as, FVarsIn P a) (k := 0) : + FVarsIn P (Expr.instantiateList e as k) := by + induction as generalizing e <;> simp_all [Expr.instantiateList, FVarsIn.instantiate1_go] + +theorem FVarsIn.abstract1 (h1 : FVarsIn P e) : + FVarsIn P (Expr.abstract1 a e k) := by + induction e generalizing k <;> simp_all [FVarsIn, Expr.abstract1] + split <;> simp [FVarsIn, *] + +theorem FVarsIn.appRevList : + FVarsIn P (f.mkAppRevList es) ↔ FVarsIn P f ∧ ∀ e ∈ es, FVarsIn P e := by + induction es <;> simp [FVarsIn, and_comm, and_left_comm, *] + +theorem Closed.abstract1 (h1 : Closed e k) : + Closed (Expr.abstract1 a e k) (k+1) := by + induction e generalizing k with simp_all [Closed, Expr.abstract1] + | bvar => omega + | fvar => split <;> simp [Closed] + +theorem Closed.getAppFn {e} (h : Closed e) : Closed e.getAppFn := by + unfold Expr.getAppFn; split + · exact Closed.getAppFn h.1 + · exact h + +theorem Closed.getAppArgsRevList {e} (h : Closed e) + {{a}} (ha : a ∈ e.getAppArgsRevList) : Closed a := by + revert a; unfold Expr.getAppArgsRevList; split <;> simp + exact ⟨h.2, Closed.getAppArgsRevList h.1⟩ + +theorem Closed.getAppArgsList {e} (h : Closed e) + {{a}} (ha : a ∈ e.getAppArgsList) : Closed a := + h.getAppArgsRevList (by simpa [← Expr.getAppArgsList_reverse]) + +theorem FVarsIn.getAppArgsRevList {e} (h : FVarsIn P e) + {{a}} (ha : a ∈ e.getAppArgsRevList) : FVarsIn P a := by + revert a + unfold Expr.getAppArgsRevList + split <;> simp + exact ⟨h.2, FVarsIn.getAppArgsRevList h.1⟩ + +theorem FVarsIn.getAppArgsList {e} (h : FVarsIn P e) + {{a}} (ha : a ∈ e.getAppArgsList) : FVarsIn P a := + h.getAppArgsRevList (by simpa [← Expr.getAppArgsList_reverse]) + +theorem Closed.looseBVarRange_le : Closed e k → e.looseBVarRange' ≤ k := by + induction e generalizing k <;> + simp +contextual [*, Closed, Expr.looseBVarRange', Nat.max_le] + exact id + +theorem Closed.looseBVarRange_zero (H : Closed e) : e.looseBVarRange' = 0 := by + simpa using H.looseBVarRange_le + +theorem VLocalDecl.WF.hasType : ∀ {d}, VLocalDecl.WF env U (VLCtx.toCtx Δ) d → + env.HasType U (VLCtx.toCtx ((ofv, d) :: Δ)) d.value d.type + | .vlam _, _ => .bvar .zero + | .vlet .., hA => hA + +theorem VLocalDecl.is_liftN {Δ : VLCtx} : + ∀ {d}, Ctx.LiftN (VLocalDecl.depth d) 0 Δ.toCtx (VLCtx.toCtx ((ofv, d) :: Δ)) + | .vlam _ => .one + | .vlet .. => .zero [] + +namespace VLCtx + +variable! (henv : Ordered env) in +theorem WF.find?_wf {Δ : VLCtx} (hΔ : WF env U Δ) (H : Δ.find? v = some (e, A)) : + env.HasType U Δ.toCtx e A := by + let (ofv, d') :: Δ := Δ + unfold find? at H; split at H + · cases H; exact hΔ.2.2.hasType + · simp at H + obtain ⟨d'', n', H, rfl, rfl⟩ := H + obtain h3 := hΔ.1.find?_wf H + exact h3.weakN henv VLocalDecl.is_liftN + +theorem WF.toCtx : ∀ {Δ}, WF env U Δ → OnCtx Δ.toCtx (env.IsType U) + | [], _ => ⟨⟩ + | (_, .vlam _) :: _, ⟨hΔ, _, hA⟩ => ⟨hΔ.toCtx, hA⟩ + | (_, .vlet ..) :: _, ⟨hΔ, _, _⟩ => hΔ.toCtx + +/-- A verified local context remains well formed when the global environment +is extended. -/ +theorem WF.mono (henv : env ≤ env') : ∀ {Δ}, WF env U Δ → WF env' U Δ + | [], _ => trivial + | (_, d) :: Δ, ⟨hΔ, hfvars, hd⟩ => + ⟨hΔ.mono henv, hfvars, by + cases d with + | vlam => exact hd.mono henv + | vlet => exact hd.mono henv⟩ + +instance : Coe (WF env U Δ) (OnCtx Δ.toCtx (env.IsType U)) := ⟨(·.toCtx)⟩ + +theorem WF.fvars_nodup : ∀ {Δ}, WF env U Δ → Δ.fvars.Nodup + | [], _ => .nil + | (none, _) :: Δ, ⟨hΔ, _, _⟩ => fvars_nodup (Δ := Δ) hΔ + | (some (fv, _), _) :: Δ, ⟨hΔ, h, _⟩ => by + suffices fv ∉ fvars Δ from (fvars_nodup hΔ).cons (fun _ h (e:fv=_) => this (e ▸ h)) + exact (h _ _ rfl).1 + +theorem liftVar_zero : liftVar 0 k v = v := by cases v <;> simp [liftVar] + +inductive FVLift' : VLCtx → VLCtx → Nat → Lift → Nat → Prop + | refl : FVLift' Δ Δ 0 .refl 0 + | skip_fvar (fv d) : FVLift' Δ Δ' 0 n 0 → + FVLift' Δ ((some fv, d) :: Δ') 0 (n.skipN d.depth) 0 + | cons_fvar (fv d) : fv.2 ⊆ Δ.fvars → FVLift' Δ Δ' 0 n 0 → + FVLift' ((some fv, d) :: Δ) ((some fv, d.lift' n) :: Δ') 0 (.consN n d.depth) 0 + | cons_bvar (d) : FVLift' Δ Δ' dk n k → + FVLift' ((none, d) :: Δ) ((none, d.lift' (n.consN k)) :: Δ') (dk + 1) n (k + d.depth) + +protected theorem FVLift'.toCtx (W : FVLift' Δ Δ' dk n k) : + Ctx.Lift' (n.consN k) Δ.toCtx Δ'.toCtx := by + induction W with + | refl => exact .refl + | skip_fvar _ d _ ih => match d with + | .vlet .. => exact ih + | .vlam A => exact .skip ih + | cons_fvar _ d _ _ ih => match d with + | .vlet .. => exact ih + | .vlam A => exact .cons ih + | cons_bvar d _ ih => match d with + | .vlet .. => exact ih + | .vlam A => exact .cons ih + +theorem FVLift'.comp (H1 : FVLift' Δ₁ Δ₂ 0 n₁ 0) (H2 : FVLift' Δ₂ Δ₃ dk n₂ k) : + FVLift' Δ₁ Δ₃ dk (n₁.comp n₂) k := by + induction H2 generalizing n₁ Δ₁ with + | refl => exact H1 + | skip_fvar _ _ _ ih => simpa [Lift.comp_skipN] using (ih H1).skip_fvar _ _ + | cons_fvar _ d h1 _ ih => cases H1 with + | refl => simpa using (ih .refl).cons_fvar _ _ h1 + | skip_fvar _ _ h2 => simpa [Lift.skipN_comp_consN] using (ih h2).skip_fvar _ (d.lift' _) + | cons_fvar _ d h1 h2 => + simpa [← Lift.consN_comp, ← VLocalDecl.lift'_comp] using (ih h2).cons_fvar _ d h1 + | cons_bvar d h1 ih => cases H1 with | refl => simpa using h1.cons_bvar _ + +theorem FVLift'.from_nil : ∀ {Δ : VLCtx}, Δ.NoBV → FVLift' [] Δ 0 (.skipN .refl Δ.toCtx.length) 0 + | [], _ => .refl + | (some _, .vlam _) :: _, H => .skip_fvar _ _ (.from_nil H) + | (some _, .vlet _ _) :: _, H => .skip_fvar _ _ (.from_nil H) + +theorem FVLift'.fvars_sublist (W : FVLift' Δ Δ' dk n k) : Δ.fvars <+ Δ'.fvars := by + induction W with + | refl => exact .refl _ + | skip_fvar _ _ _ ih => exact .cons _ ih + | cons_fvar _ _ _ _ ih => exact .cons_cons _ ih + | cons_bvar _ _ ih => exact ih + +theorem FVLift'.bvars_eq (W : FVLift' Δ Δ' dk n k) : Δ'.bvars = Δ.bvars := by + induction W with + | refl => rfl + | skip_fvar _ _ _ ih => exact ih + | cons_fvar _ _ _ _ ih => exact ih + | cons_bvar _ _ ih => exact congrArg Nat.succ ih + +variable! (henv : VEnv.WF env) in +theorem FVLift'.wf (W : FVLift' Δ Δ' dk n k) (hΔ' : Δ'.WF env U) : Δ.WF env U := by + induction W with + | refl => exact hΔ' + | skip_fvar _ _ _ ih => exact ih hΔ'.1 + | cons_fvar _ _ hd W ih => + let ⟨hΔ', h1, h2⟩ := hΔ' + refine ⟨ih hΔ', ?_, (VLocalDecl.weak'_iff henv hΔ'.toCtx W.toCtx).1 h2⟩ + rintro _ _ ⟨⟩; exact ⟨fun h => (h1 _ _ rfl).1 <| W.fvars_sublist.subset h, hd⟩ + | cons_bvar _ W ih => + let ⟨hΔ', _, h2⟩ := hΔ' + exact ⟨ih hΔ', nofun, (VLocalDecl.weak'_iff henv hΔ'.toCtx W.toCtx).1 h2⟩ + +protected theorem FVLift'.find? (W : FVLift' Δ Δ' dk n k) (hΔ' : Δ'.WF env U) + (H : find? Δ v = some (e, A)) : + find? Δ' v = some (e.lift' (n.consN k), A.lift' (n.consN k)) := by + induction W generalizing v e A with + | refl => simp [H] + | skip_fvar fv' _ W ih => + let (fv', deps) := fv'; simp [find?] + cases v with simp [next] + | inl => + refine ⟨_, _, ih hΔ'.1 H, ?_⟩ + simp [← VExpr.lift'_consN_skipN, ← VExpr.lift'_comp, Lift.comp_skipN] + | inr fv => + cases eq : fv' == fv <;> simp + · refine ⟨_, _, ih hΔ'.1 H, ?_⟩ + simp [← VExpr.lift'_consN_skipN, ← VExpr.lift'_comp, Lift.comp_skipN] + · refine ((List.pairwise_cons.1 hΔ'.fvars_nodup).1 fv' ?_ rfl).elim + exact W.fvars_sublist.subset ((beq_iff_eq ..).1 eq ▸ find?_eq_some.1 ⟨_, H⟩) + | cons_fvar fv' d _ W ih => + let (fv', deps) := fv'; revert H; simp [find?] + obtain i | fv := v <;> simp [next] <;> + [skip; cases eq : fv' == fv <;> simp] <;> + [(rintro _ _ H rfl rfl; refine ⟨_, _, ih hΔ'.1 H, ?_⟩); + (rintro _ _ H rfl rfl; refine ⟨_, _, ih (v := .inr fv) hΔ'.1 H, ?_⟩); + rintro rfl rfl] <;> + open VLocalDecl in + cases d <;> simp [value, type, depth, lift', VExpr.lift, + ← VExpr.lift'_consN_skipN, ← VExpr.lift'_comp] + | cons_bvar d _ ih => + simp [find?] at H ⊢ + obtain ⟨_|i⟩ | fv := v <;> simp [next] at H ⊢ <;> + [(obtain ⟨rfl, rfl⟩ := H); + (obtain ⟨e, A, H, rfl, rfl⟩ := H + refine ⟨_, _, ih (v := .inl i) hΔ'.1 H, ?_⟩); + (obtain ⟨e, A, H, rfl, rfl⟩ := H + refine ⟨_, _, ih (v := .inr fv) hΔ'.1 H, ?_⟩)] <;> + open VLocalDecl in + cases d <;> simp [value, type, depth, lift', VExpr.lift, + ← VExpr.lift'_consN_skipN, ← VExpr.lift'_comp] + +inductive FVLift : VLCtx → VLCtx → Nat → Nat → Nat → Prop + | refl : FVLift Δ Δ 0 0 0 + | skip_fvar (fv d) : FVLift Δ Δ' 0 n 0 → FVLift Δ ((some fv, d) :: Δ') 0 (n + d.depth) 0 + | cons_bvar (d) : FVLift Δ Δ' dk n k → + FVLift ((none, d) :: Δ) ((none, d.liftN n k) :: Δ') (dk + 1) n (k + d.depth) + +theorem FVLift.toFVLift' (W : FVLift Δ Δ' dk n k) : FVLift' Δ Δ' dk (.skipN .refl n) k := by + induction W with + | refl => exact .refl + | skip_fvar fv d _ ih => simpa [Lift.skipN_skipN] using ih.skip_fvar fv d + | cons_bvar d _ ih => + simpa [← VLocalDecl.lift'_consN_skipN, Lift.skipN_skipN] using ih.cons_bvar d + +protected theorem FVLift.toCtx (W : FVLift Δ Δ' dk n k) : Ctx.LiftN n k Δ.toCtx Δ'.toCtx := + Ctx.liftN_iff_lift'.2 W.toFVLift'.toCtx + +theorem FVLift.from_nil : ∀ {Δ : VLCtx}, Δ.NoBV → FVLift [] Δ 0 Δ.toCtx.length 0 + | [], _ => .refl + | (some _, .vlam _) :: _, H => .skip_fvar _ _ (.from_nil H) + | (some _, .vlet _ _) :: _, H => .skip_fvar _ _ (.from_nil H) + +variable! (henv : VEnv.WF env) in +theorem FVLift.wf (W : FVLift Δ Δ' dk n k) (hΔ' : Δ'.WF env U) : Δ.WF env U := + W.toFVLift'.wf henv hΔ' + +theorem FVLift.fvars_suffix (W : FVLift Δ Δ' dk n k) : Δ.fvars <:+ Δ'.fvars := by + induction W with + | refl => exact List.suffix_refl _ + | skip_fvar _ _ _ ih => exact ih.trans (List.suffix_cons ..) + | cons_bvar _ _ ih => exact ih + +protected theorem FVLift.find? (W : FVLift Δ Δ' dk n k) (hΔ' : Δ'.WF env U) + (H : find? Δ v = some (e, A)) : find? Δ' v = some (e.liftN n k, A.liftN n k) := by + simpa [VExpr.lift'_consN_skipN] using W.toFVLift'.find? hΔ' H + +inductive BVLift : (Δ Δ' : VLCtx) → (dn dk n k : Nat) → Prop + | refl : BVLift Δ Δ 0 0 0 0 + | skip (d) : BVLift Δ Δ' dn 0 n 0 → BVLift Δ ((none, d) :: Δ') (dn + 1) 0 (n + d.depth) 0 + | cons (d) : BVLift Δ Δ' dn dk n k → + BVLift ((none, d) :: Δ) ((none, d.liftN n k) :: Δ') dn (dk + 1) n (k + d.depth) + +theorem BVLift.toCtx (W : BVLift Δ Δ' dn dk n k) : Ctx.LiftN n k Δ.toCtx Δ'.toCtx := by + induction W with + | refl => exact .zero [] + | @skip _ Δ' _ _ d _ ih => + match d with + | .vlet .. => exact ih + | .vlam A => + generalize hΓ' : VLCtx.toCtx Δ' = Γ' at ih + let .zero As eq := ih + simp [VLCtx.toCtx, hΓ'] + exact .zero (A :: As) (eq ▸ rfl) + | cons d _ ih => + match d with + | .vlet .. => exact ih + | .vlam A => exact .succ ih + +variable! (henv : VEnv.WF env) in +theorem BVLift.wf (W : BVLift Δ Δ' dn dk n k) (hΔ' : Δ'.WF env U) : Δ.WF env U := by + induction W with + | refl => exact hΔ' + | skip _ _ ih => exact ih hΔ'.1 + | cons _ W ih => + let ⟨hΔ', _, h2⟩ := hΔ' + exact ⟨ih hΔ', nofun, (VLocalDecl.weakN_iff henv hΔ'.toCtx W.toCtx).1 h2⟩ + +theorem BVLift.fvars_eq (W : BVLift Δ Δ' dn dk n k) : Δ.fvars = Δ'.fvars := by + induction W with + | refl => rfl + | skip _ _ ih => exact ih + | cons _ _ ih => exact ih + +protected theorem BVLift.find? (W : BVLift Δ Δ' dn dk n k) (H : find? Δ v = some (e, A)) : + find? Δ' (liftVar dn dk v) = some (e.liftN n k, A.liftN n k) := by + induction W generalizing v e A with + | refl => simp [H, liftVar_zero] + | @skip _ Δ' _ fv' _ W ih => + obtain v | fv := v <;> simp [find?, liftVar, next] <;> + exact ⟨_, _, ih H, by simp [VExpr.liftN_liftN]⟩ + | cons d _ ih => + obtain (_ | v) | fv := v <;> simp [liftVar] <;> + [ (simp [find?, next] at H ⊢; simp [← H]); + split <;> ( + rename_i h + simp [Nat.add_right_comm _ 1, find?, next] at H ⊢ + obtain ⟨e, A, H, rfl, rfl⟩ := H + have := ih H + simp [liftVar, h] at this + refine ⟨_, _, this, ?_⟩); + ( simp [find?, next] at H ⊢ + obtain ⟨e, A, H, rfl, rfl⟩ := H + refine ⟨_, _, ih H, ?_⟩ )] <;> + open VLocalDecl in + cases d <;> simp [VExpr.lift_liftN', liftN, value, type, depth, VExpr.liftN] + +variable (Δ₀ : VLCtx) (e₀ A₀ : VExpr) in +inductive InstN : Nat → Nat → VLCtx → VLCtx → Prop where + | zero : InstN 0 0 ((none, .vlam A₀) :: Δ₀) Δ₀ + | succ : InstN dk k Γ Γ' → InstN (dk + 1) (k + d.depth) ((none, d)::Γ) ((none, d.inst e₀ k) :: Γ') + +protected theorem InstN.toCtx (W : InstN Δ₀ e₀ A₀ dk k Δ₁ Δ) : + Ctx.InstN Δ₀.toCtx e₀ A₀ k Δ₁.toCtx Δ.toCtx := by + induction W with + | zero => exact .zero + | @succ _ _ _ _ d _ ih => + match d with + | .vlet .. => exact ih + | .vlam A => exact .succ ih + +variable! (henv : Ordered env) (h₀ : env.HasType U (toCtx Δ₀) e₀ A₀) in +theorem InstN.wf (W : InstN Δ₀ e₀ A₀ dk k Δ₁ Δ) (hΔ' : Δ₁.WF env U) : Δ.WF env U := by + induction W with + | zero => exact hΔ'.1 + | succ W ih => let ⟨hΔ', _, h2⟩ := hΔ'; exact ⟨ih hΔ', nofun, h2.instN henv W.toCtx h₀⟩ + +theorem InstN.fvars_eq (W : InstN Δ₀ e₀ A₀ dk k Δ₁ Δ) : + Δ₁.fvars = Δ₀.fvars ∧ Δ.fvars = Δ₀.fvars := by + induction W with + | zero => exact ⟨rfl, rfl⟩ + | succ _ ih => exact ih + +variable (Δ₀ : VLCtx) (e₀ A₀ : VExpr) in +inductive InstLet : Nat → Nat → VLCtx → VLCtx → Prop where + | zero : InstLet 0 0 ((none, .vlet A₀ e₀) :: Δ₀) Δ₀ + | succ : InstLet dk k Γ Γ' → InstLet (dk + 1) (k + d.depth) ((none, d)::Γ) ((none, d) :: Γ') + +protected theorem InstLet.toCtx (W : InstLet Δ₀ e₀ A₀ dk k Δ₁ Δ) : Δ₁.toCtx = Δ.toCtx := by + induction W with + | zero => rfl + | @succ _ _ _ _ d _ ih => + match d with + | .vlet .. => exact ih + | .vlam _ => exact congrArg (_::·) ih + +theorem InstLet.wf (W : InstLet Δ₀ e₀ A₀ dk k Δ₁ Δ) (hΔ' : Δ₁.WF env U) : Δ.WF env U := by + induction W with + | zero => exact hΔ'.1 + | succ W ih => let ⟨hΔ', _, h2⟩ := hΔ'; exact ⟨ih hΔ', nofun, W.toCtx ▸ h2⟩ + +theorem InstLet.fvars_eq (W : InstLet Δ₀ e₀ A₀ dk k Δ₁ Δ) : + Δ₁.fvars = Δ₀.fvars ∧ Δ.fvars = Δ₀.fvars := by + induction W with + | zero => exact ⟨rfl, rfl⟩ + | succ _ ih => exact ih + +variable (Δ₀ : VLCtx) (v₀ : FVarId) (d₀ : VLocalDecl) in +inductive Abstract : Nat → Nat → VLCtx → VLCtx → Prop where + | zero : Abstract 0 0 ((some (v₀, deps), d₀) :: Δ₀) ((none, d₀) :: Δ₀) + | succ : Abstract dk k Γ Γ' → Abstract (dk + 1) (k + d.depth) ((none, d) :: Γ) ((none, d) :: Γ') + +protected theorem Abstract.toCtx (W : Abstract Δ₀ v₀ d₀ dk k Δ₁ Δ) : Δ₁.toCtx = Δ.toCtx := by + induction W with + | zero => cases d₀ <;> rfl + | @succ _ _ _ _ d _ ih => + match d with + | .vlet .. => exact ih + | .vlam A => exact congrArg (_ :: ·) ih + +theorem Abstract.wf (W : Abstract Δ₀ v₀ d₀ dk k Δ₁ Δ) (hΔ' : Δ₁.WF env U) : Δ.WF env U := by + induction W with + | zero => exact ⟨hΔ'.1, nofun, hΔ'.2.2⟩ + | succ W ih => let ⟨hΔ', _, h2⟩ := hΔ'; exact ⟨ih hΔ', nofun, W.toCtx ▸ h2⟩ + +theorem Abstract.fvars_eq (W : Abstract Δ₀ v₀ d₀ dk k Δ₁ Δ) : + Δ₁.fvars = v₀ :: Δ₀.fvars ∧ Δ.fvars = Δ₀.fvars := by + induction W with + | zero => exact ⟨rfl, rfl⟩ + | succ _ ih => exact ih + +theorem Abstract.find?_self (W : Abstract Δ₀ v₀ d₀ dk k Δ₁ Δ) : + Δ₁.find? (.inr v₀) = some (d₀.value.liftN k, d₀.type.liftN k) := by + induction W with simp [find?, next] + | succ _ ih => exact ⟨_, _, ih, by simp [VExpr.liftN_liftN]⟩ + +protected theorem Abstract.find? (W : Abstract Δ₀ v₀ d₀ dk k Δ₁ Δ) (h : v ≠ .inr v₀) : + Δ.find? v = Δ₁.find? (clear% h; match v with + | .inl i => if i < dk then .inl i else if i = dk then .inr v₀ else .inl (i - 1) + | .inr v' => .inr v') := by + induction W generalizing v with + | zero => + obtain (_|i)|v := v <;> simp [find?, next] + cases eq : v₀ == v; · simp + · simp at h eq; cases h eq.symm + | @succ dk k _ _ _ _ ih => + obtain (_|i)|v := v <;> simp [find?, next] + · have := @ih (.inl i) nofun; revert this + by_cases h : i < dk <;> simp +contextual [h] + by_cases h : i = dk <;> simp +contextual [h] + obtain _|i := i <;> [omega; simp] + · simp [ih h] + +theorem instL_eq_map (Δ : VLCtx) : Δ.instL ls = Δ.map (fun (ofv, d) => (ofv, d.instL ls)) := by + induction Δ <;> simp [instL, *] + +@[simp] theorem instL_toCtx (Δ : VLCtx) : (Δ.instL ls).toCtx = Δ.toCtx.map (·.instL ls) := by + induction Δ with + | nil => rfl + | cons head => obtain ⟨_, _|_⟩ := head <;> rw [instL, VLocalDecl.instL] <;> simp [toCtx, *] + +variable! (hls : ∀ l ∈ (ls : List _), VLevel.WF U l) in +protected theorem WF.instL : ∀ {Δ}, VLCtx.WF env ls.length Δ → + VLCtx.WF env U (Δ.instL ls) + | [], _ => ⟨⟩ + | (_, d) :: Δ, ⟨h1, h2, h3⟩ => + ⟨h1.instL, by simpa [instL_eq_map, fvars, Function.comp_def] using h2, + by simpa using h3.instL hls⟩ + +theorem find?_instL : find? Δ v = some (e, A) → + find? (Δ.instL ls) v = some (e.instL ls, A.instL ls) := by + induction Δ generalizing v e A with + | nil => nofun + | cons d Δ ih => + simp [find?, instL]; split <;> simp + · rintro rfl rfl; cases d.2 <;> exact ⟨rfl, by simp [VLocalDecl.instL, VLocalDecl.type]⟩ + · rintro e A h rfl rfl + exact ⟨_, _, ih h, by cases d.2 <;> simp [VLocalDecl.instL, VLocalDecl.depth]⟩ + +variable (env : VEnv) (U) in +inductive SortList : VLCtx → List VLevel → Prop + | nil : SortList Δ [] + | cons : SortList Δ ls → env.HasType U Δ.toCtx A (.sort u) → + SortList ((some fv, .vlam A) :: Δ) (u :: ls) + +end VLCtx + +theorem TrProj.weak' (henv : env.Ordered) (W : Ctx.Lift' n Γ Γ') + (H : TrProj env U Γ s i e e') : + TrProj env U Γ' s i (e.lift' n) (e'.lift' n) := by + obtain ⟨view, levels, params, hname, hproj⟩ := H + exact ⟨view, levels, params.map (fun param => param.lift' n), + hname, hproj.weak' henv W⟩ + +theorem TrProj.weakN (henv : env.Ordered) (W : Ctx.LiftN n k Γ Γ') + (H : TrProj env U Γ s i e e') : + TrProj env U Γ' s i (e.liftN n k) (e'.liftN n k) := by + simpa [VExpr.lift'_consN_skipN] using + H.weak' henv (Ctx.liftN_iff_lift'.1 W) + +/-! ## Replaying closed metadata types -/ + +variable (env : VEnv) (Us : List Name) in +/-- The syntax-directed fragment used by kernel declaration types. Unlike +`TrExprS`, this relation records only the representation translation; the +typing premises are recovered from a well-formed Theory expression by +`TrTypeExpr.to_trExprS`. Keeping the two concerns separate lets replay +fixtures reuse the declaration's real `WF` proof instead of re-running a +fragile type-synthesis tactic at every application and pi node. -/ +inductive TrTypeExpr : VLCtx → Expr → VExpr → Prop where + | bvar : Δ.find? (.inl i) = some (e, A) → TrTypeExpr Δ (.bvar i) e + | sort : VLevel.ofLevel Us u = some u' → TrTypeExpr Δ (.sort u) (.sort u') + | const : + env.constants c = some ci → + us.mapM (VLevel.ofLevel Us) = some us' → + us.length = ci.uvars → + TrTypeExpr Δ (.const c us) (.const c us') + | app : TrTypeExpr Δ f f' → TrTypeExpr Δ a a' → + TrTypeExpr Δ (.app f a) (.app f' a') + | mdata : TrTypeExpr Δ e e' → TrTypeExpr Δ (.mdata data e) e' + | forallE : TrTypeExpr Δ ty ty' → + TrTypeExpr ((none, .vlam ty') :: Δ) body body' → + TrTypeExpr Δ (.forallE name ty body bi) (.forallE ty' body') + +/-- Add the `TrExprS` typing premises to a structural metadata translation. +The source grammar is deliberately restricted to the forms that can occur in +the normalized types of constants emitted by the inductive compiler. -/ +theorem TrTypeExpr.to_trExprS + (H : TrTypeExpr env Us Δ e e') + (henv : env.Ordered) + (hΔ : OnCtx Δ.toCtx (env.IsType Us.length)) + (hwf : e'.WF env Us.length Δ.toCtx) : TrExprS env Us Δ e e' := by + induction H with + | bvar h => exact .bvar h + | sort h => exact .sort h + | const h1 h2 h3 => exact .const h1 h2 h3 + | app _ _ ihf iha => + obtain ⟨A, B, htf, hta⟩ := hwf.app_inv henv hΔ + exact .app htf hta (ihf hΔ ⟨_, htf⟩) (iha hΔ ⟨_, hta⟩) + | mdata _ ih => exact .mdata (ih hΔ hwf) + | forallE _ _ ihty ihbody => + obtain ⟨_, hwf⟩ := hwf + obtain ⟨hty, hbody⟩ := VEnv.HasType.forallE_inv henv hwf + obtain ⟨u, hty⟩ := hty + obtain ⟨v, hbody⟩ := hbody + exact .forallE ⟨u, hty⟩ ⟨v, hbody⟩ + (ihty hΔ ⟨_, hty⟩) (ihbody ⟨hΔ, ⟨u, hty⟩⟩ ⟨_, hbody⟩) + +#guard_named_axioms Ix.Theory.Named.TrTypeExpr.to_trExprS [propext, Classical.choice, Quot.sound] + +variable! (henv : Ordered env) in +theorem TrExprS.weakFV' (W : VLCtx.FVLift' Δ Δ' dk n k) (hΔ' : Δ'.WF env Us.length) + (H : TrExprS env Us Δ e e') : TrExprS env Us Δ' e (e'.lift' (n.consN k)) := by + induction H generalizing Δ' dk k with + | bvar h1 => exact .bvar (W.find? hΔ' h1) + | fvar h1 => exact .fvar (W.find? hΔ' h1) + | sort h1 => exact .sort h1 + | const h1 h2 h3 => exact .const h1 h2 h3 + | app h1 h2 _ _ ih1 ih2 => + exact .app (h1.weak' henv W.toCtx) (h2.weak' henv W.toCtx) (ih1 W hΔ') (ih2 W hΔ') + | lam h1 _ _ ih1 ih2 => + have h1 := h1.weak' henv W.toCtx + exact .lam h1 (ih1 W hΔ') (ih2 (W.cons_bvar _) ⟨hΔ', nofun, h1⟩) + | forallE h1 h2 _ _ ih1 ih2 => + have h1 := h1.weak' henv W.toCtx + have h2 := h2.weak' henv W.toCtx.cons + exact .forallE h1 h2 (ih1 W hΔ') (ih2 (W.cons_bvar _) ⟨hΔ', nofun, h1⟩) + | letE h1 _ _ _ ih1 ih2 ih3 => + have h1 := h1.weak' henv W.toCtx + exact .letE h1 (ih1 W hΔ') (ih2 W hΔ') (ih3 (W.cons_bvar _) ⟨hΔ', nofun, h1⟩) + | lit h1 _ ih => exact .lit h1 (ih W hΔ') + | mdata _ ih => exact .mdata (ih W hΔ') + | proj _ h2 ih => exact .proj (ih W hΔ') (h2.weak' henv W.toCtx) + +variable! (henv : WF env) in +theorem TrExpr.weakFV' (W : VLCtx.FVLift' Δ Δ' dk n k) (hΔ' : Δ'.WF env Us.length) + (H : TrExpr env Us Δ e e') : TrExpr env Us Δ' e (e'.lift' (n.consN k)) := + let ⟨_, H1, H2⟩ := H + ⟨_, H1.weakFV' henv W hΔ', H2.weak' henv W.toCtx⟩ + +variable! (henv : Ordered env) in +theorem TrExprS.weakFV (W : VLCtx.FVLift Δ Δ' dk n k) (hΔ' : Δ'.WF env Us.length) + (H : TrExprS env Us Δ e e') : TrExprS env Us Δ' e (e'.liftN n k) := by + simpa [VExpr.lift'_consN_skipN] using H.weakFV' henv W.toFVLift' hΔ' + +variable! (henv : WF env) in +theorem TrExpr.weakFV (W : VLCtx.FVLift Δ Δ' dk n k) (hΔ' : Δ'.WF env Us.length) + (H : TrExpr env Us Δ e e') : TrExpr env Us Δ' e (e'.liftN n k) := + let ⟨_, H1, H2⟩ := H + ⟨_, H1.weakFV henv W hΔ', H2.weakN henv W.toCtx⟩ + +variable! (henv : Ordered env) in +theorem TrExprS.weakBV (W : VLCtx.BVLift Δ Δ' dn dk n k) + (H : TrExprS env Us Δ e e') : TrExprS env Us Δ' (e.liftLooseBVars' dk dn) (e'.liftN n k) := by + induction H generalizing Δ' dk k with + | bvar h1 => exact .bvar (W.find? h1) + | fvar h1 => exact .fvar (W.find? h1) + | sort h1 => exact .sort h1 + | const h1 h2 h3 => exact .const h1 h2 h3 + | app h1 h2 _ _ ih1 ih2 => + exact .app (h1.weakN henv W.toCtx) (h2.weakN henv W.toCtx) (ih1 W) (ih2 W) + | lam h1 _ _ ih1 ih2 => + exact .lam (h1.weakN henv W.toCtx) (ih1 W) (ih2 (W.cons _)) + | forallE h1 h2 _ _ ih1 ih2 => + exact .forallE (h1.weakN henv W.toCtx) (h2.weakN henv W.toCtx.succ) (ih1 W) (ih2 (W.cons _)) + | letE h1 _ _ _ ih1 ih2 ih3 => + exact .letE (h1.weakN henv W.toCtx) (ih1 W) (ih2 W) (ih3 (W.cons _)) + | lit h1 _ ih => + refine .lit h1 (Expr.liftLooseBVars_eq_self ?_ ▸ ih W :) + exact Closed.toConstructor.looseBVarRange_le + | mdata _ ih => exact .mdata (ih W) + | proj _ h2 ih => exact .proj (ih W) (h2.weakN henv W.toCtx) + +variable! (henv : WF env) in +theorem TrExpr.weakBV (W : VLCtx.BVLift Δ Δ' dn dk n k) + (H : TrExpr env Us Δ e e') : TrExpr env Us Δ' (e.liftLooseBVars' dk dn) (e'.liftN n k) := + let ⟨_, H1, H2⟩ := H + ⟨_, H1.weakBV henv W, H2.weakN henv W.toCtx⟩ + +variable! (henv : VEnv.WF env) (hΓ' : OnCtx Γ' (env.IsType U)) in +theorem HasType.skips (W : Ctx.LiftN n k Γ Γ') + (h1 : env.HasType U Γ' e A) (h2 : e.Skips n k) : ∃ B, env.HasType U Γ' e B ∧ B.Skips n k := + IsDefEq.skips henv hΓ' W h1 h2 h2 + +theorem TrProj.weak'_inv (henv : VEnv.WF env) (hΓ' : OnCtx Γ' (env.IsType U)) + (W : Ctx.Lift' l Γ Γ') : + TrProj env U Γ' s i (e.lift' l) e' → + ∃ e', TrProj env U Γ s i e e' := by + rintro ⟨view, levels, params, hname, hproj⟩ + obtain ⟨params', result, hresult⟩ := + henv.registeredStructureHeadInversion.weak'_inv hΓ' W hproj + exact ⟨result, view, levels, params', hname, hresult⟩ + +theorem TrProj.defeqDFC (henv : VEnv.WF env) (hΓ : env.IsDefEqCtx U [] Γ₁ Γ₂) + (he : env.IsDefEqU U Γ₁ e₁ e₂) (H : TrProj env U Γ₁ s i e₁ e') : + ∃ e', TrProj env U Γ₂ s i e₂ e' := by + obtain ⟨view, levels, params, hname, hproj⟩ := H + have he₂ : env.HasType U Γ₂ e₂ + (view.structureType levels params) := + (hproj.majorType.defeqU_l henv hΓ.isType he).defeqDFC + henv.ordered hΓ + obtain ⟨result, hresult⟩ := + hproj.defeqDFC henv.ordered hΓ he₂ + exact ⟨result, view, levels, params, hname, hresult⟩ + +theorem TrProj.mono {env env' : VEnv} (henv : env ≤ env') + (H : TrProj env U Γ s i e e') : TrProj env' U Γ s i e e' := by + obtain ⟨view, levels, params, hname, hproj⟩ := H + exact ⟨view, levels, params, hname, hproj.mono henv⟩ + +variable! {env env' : VEnv} (henv : env ≤ env') in +theorem TrExprS.mono (H : TrExprS env Us Δ e e') : TrExprS env' Us Δ e e' := by + induction H with + | bvar h1 => exact .bvar h1 + | fvar h1 => exact .fvar h1 + | sort h1 => exact .sort h1 + | const h1 h2 h3 => exact .const (henv.1 h1) h2 h3 + | app h1 h2 _ _ ih1 ih2 => exact .app (h1.mono henv) (h2.mono henv) ih1 ih2 + | lam h1 _ _ ih1 ih2 => exact .lam (h1.mono henv) ih1 ih2 + | forallE h1 h2 _ _ ih1 ih2 => exact .forallE (h1.mono henv) (h2.mono henv) ih1 ih2 + | letE h1 _ _ _ ih1 ih2 ih3 => exact .letE (h1.mono henv) ih1 ih2 ih3 + | lit h1 _ ih => refine .lit (h1.mono henv) ih + | mdata _ ih => exact .mdata ih + | proj _ h2 ih => exact .proj ih (h2.mono henv) + +variable! {env env' : VEnv} (henv : env ≤ env') in +theorem TrExpr.mono (H : TrExpr env Us Δ e e') : TrExpr env' Us Δ e e' := + let ⟨_, H1, H2⟩ := H; ⟨_, H1.mono henv, H2.mono henv⟩ + +variable! (env : VEnv) (U : Nat) in +inductive VLCtx.IsDefEq : VLCtx → VLCtx → Prop + | nil : VLCtx.IsDefEq [] [] + | cons {Δ₁ Δ₂ : VLCtx} : + VLCtx.IsDefEq Δ₁ Δ₂ → + (∀ fv deps, ofv = some (fv, deps) → fv ∉ Δ₁.fvars ∧ deps ⊆ Δ₁.fvars) → + VLocalDecl.IsDefEq env U Δ₁.toCtx d₁ d₂ → + VLCtx.IsDefEq ((ofv, d₁) :: Δ₁) ((ofv, d₂) :: Δ₂) + +variable! (henv : Ordered env) in +theorem VLCtx.IsDefEq.refl : ∀ {Δ}, VLCtx.WF env U Δ → VLCtx.IsDefEq env U Δ Δ + | [], _ => .nil + | (_, _) :: _, ⟨h1, h2, h3⟩ => .cons (IsDefEq.refl h1) h2 (.refl henv h1.toCtx h3) + +theorem VLCtx.IsDefEq.defeqCtx : VLCtx.IsDefEq env U Δ₁ Δ₂ → env.IsDefEqCtx U [] Δ₁.toCtx Δ₂.toCtx + | .nil => .zero + | .cons h1 _ (.vlam h2) => .succ h1.defeqCtx h2 + | .cons h1 _ (.vlet ..) => h1.defeqCtx + +theorem VLCtx.IsDefEq.fvars : VLCtx.IsDefEq env U Δ₁ Δ₂ → Δ₁.fvars = Δ₂.fvars + | .nil => by simp + | .cons (ofv := none) h1 h2 _ => h1.fvars + | .cons (ofv := some fv) h1 h2 _ => by simp [h1.fvars] + +/-- Definitionally equal verified local contexts have bound-variable entries +in exactly the same positions. -/ +theorem VLCtx.IsDefEq.bvars : VLCtx.IsDefEq env U Δ₁ Δ₂ → Δ₁.bvars = Δ₂.bvars + | .nil => rfl + | .cons (ofv := none) h1 _ _ => by + simp only [VLCtx.bvars, h1.bvars] + | .cons (ofv := some _) h1 _ _ => by + simp only [VLCtx.bvars, h1.bvars] + +theorem VLCtx.IsDefEq.wf : VLCtx.IsDefEq env U Δ₁ Δ₂ → VLCtx.WF env U Δ₁ + | .nil => ⟨⟩ + | .cons h1 h2 h3 => ⟨h1.wf, h2, h3.wf⟩ + +theorem VLCtx.IsDefEq.mono (henv : env ≤ env') : + VLCtx.IsDefEq env U Δ₁ Δ₂ → VLCtx.IsDefEq env' U Δ₁ Δ₂ + | .nil => .nil + | .cons h₁ h₂ h₃ => .cons (h₁.mono henv) h₂ (h₃.mono henv) + +/-- Definitionally equal verified contexts whose free-variable slots use the +same identifiers. Dependency metadata may differ because normalization can +change the syntactic free-variable list of a definitionally equal local type; +that metadata is irrelevant to Theory lookup and typing. -/ +inductive VLCtx.IsDefEqFVars (env : VEnv) (U : Nat) : VLCtx → VLCtx → Prop + | nil : IsDefEqFVars env U [] [] + | cons_bvar : + IsDefEqFVars env U Δ₁ Δ₂ → + VLocalDecl.IsDefEq env U Δ₁.toCtx d₁ d₂ → + IsDefEqFVars env U ((none, d₁) :: Δ₁) ((none, d₂) :: Δ₂) + | cons_fvar : + IsDefEqFVars env U Δ₁ Δ₂ → + VLocalDecl.IsDefEq env U Δ₁.toCtx d₁ d₂ → + IsDefEqFVars env U + ((some (fv, deps₁), d₁) :: Δ₁) + ((some (fv, deps₂), d₂) :: Δ₂) + +theorem VLCtx.IsDefEq.toFVars : + VLCtx.IsDefEq env U Δ₁ Δ₂ → VLCtx.IsDefEqFVars env U Δ₁ Δ₂ + | .nil => .nil + | .cons (ofv := none) h₁ _ h₃ => .cons_bvar h₁.toFVars h₃ + | .cons (ofv := some _) h₁ _ h₃ => .cons_fvar h₁.toFVars h₃ + +theorem VLCtx.IsDefEqFVars.defeqCtx : + VLCtx.IsDefEqFVars env U Δ₁ Δ₂ → + env.IsDefEqCtx U [] Δ₁.toCtx Δ₂.toCtx + | .nil => .zero + | .cons_bvar h₁ (.vlam h₂) => .succ h₁.defeqCtx h₂ + | .cons_bvar h₁ (.vlet ..) => h₁.defeqCtx + | .cons_fvar h₁ (.vlam h₂) => .succ h₁.defeqCtx h₂ + | .cons_fvar h₁ (.vlet ..) => h₁.defeqCtx + +theorem VLCtx.IsDefEqFVars.find?_uniq (henv : VEnv.WF env) + (hΔ : VLCtx.IsDefEqFVars env U Δ₁ Δ₂) + (H₁ : Δ₁.find? v = some (e₁, A₁)) + (H₂ : Δ₂.find? v = some (e₂, A₂)) : + env.IsDefEqU U Δ₁.toCtx A₁ A₂ ∧ + env.IsDefEq U Δ₁.toCtx e₁ e₂ A₁ := by + induction hΔ generalizing v e₁ e₂ A₁ A₂ with + | nil => simp [VLCtx.find?] at H₁ + | cons_bvar hΔ hd ih => + revert H₁ H₂ + simp only [VLCtx.find?] + split + · rintro ⟨⟩ ⟨⟩ + cases hd with + | vlam h => exact ⟨⟨_, h.weak henv⟩, .bvar .zero⟩ + | vlet h₁ h₂ => exact ⟨⟨_, h₂⟩, h₁⟩ + · simp + rintro d₁' n₁' H₁' rfl rfl d₂' n₂' H₂' rfl rfl + obtain ⟨h₂, h₃⟩ := ih H₁' H₂' + cases hd with + | vlam => exact ⟨h₂.weakN henv .one, h₃.weak henv⟩ + | vlet => simpa [VLocalDecl.depth] using ⟨h₂, h₃⟩ + | @cons_fvar Δ₁ Δ₂ d₁ d₂ fv deps₁ deps₂ hΔ hd ih => + revert H₁ H₂ + simp only [VLCtx.find?] + cases same : fv == (match v with | .inl _ => fv | .inr fv' => fv') <;> + simp only [VLCtx.next] + all_goals + cases v with + | inl i => + simp + rintro d₁' n₁' H₁' rfl rfl d₂' n₂' H₂' rfl rfl + obtain ⟨h₂, h₃⟩ := ih H₁' H₂' + cases hd with + | vlam => exact ⟨h₂.weakN henv .one, h₃.weak henv⟩ + | vlet => simpa [VLocalDecl.depth] using ⟨h₂, h₃⟩ + | inr fv' => + simp only [same] + split + · rintro ⟨⟩ ⟨⟩ + cases hd with + | vlam h => exact ⟨⟨_, h.weak henv⟩, .bvar .zero⟩ + | vlet h₁ h₂ => exact ⟨⟨_, h₂⟩, h₁⟩ + · simp + rintro d₁' n₁' H₁' rfl rfl d₂' n₂' H₂' rfl rfl + obtain ⟨h₂, h₃⟩ := ih H₁' H₂' + cases hd with + | vlam => exact ⟨h₂.weakN henv .one, h₃.weak henv⟩ + | vlet => simpa [VLocalDecl.depth] using ⟨h₂, h₃⟩ + +variable! (henv : Ordered env) in +theorem VLCtx.IsDefEq.symm : VLCtx.IsDefEq env U Δ₁ Δ₂ → VLCtx.IsDefEq env U Δ₂ Δ₁ + | .nil => .nil + | .cons h1 h2 h3 => + .cons h1.symm (by simpa [h1.fvars] using h2) (h3.symm.defeqDFC henv h1.defeqCtx) + +variable! (henv : VEnv.WF env) in +theorem VLCtx.IsDefEq.find?_uniq (hΔ : VLCtx.IsDefEq env U Δ₁ Δ₂) + (H1 : Δ₁.find? v = some (e₁, A₁)) (H2 : Δ₂.find? v = some (e₂, A₂)) : + env.IsDefEqU U Δ₁.toCtx A₁ A₂ ∧ env.IsDefEq U Δ₁.toCtx e₁ e₂ A₁ := by + let .cons hΔ h1 h2 := hΔ + match h2 with + | .vlam (type₁ := A₁) (type₂ := A₂) h2 => + revert H1 H2; unfold VLCtx.find?; split + · rintro ⟨⟩ ⟨⟩; exact ⟨⟨_, h2.weak henv⟩, .bvar .zero⟩ + · simp + rintro d₁' n₁' H1' rfl rfl d₂' n₂' H2' rfl rfl + obtain ⟨h2, h3⟩ := find?_uniq hΔ H1' H2' + exact ⟨h2.weakN henv .one, h3.weak henv⟩ + | .vlet h3 h4 => + revert H1 H2; unfold VLCtx.find?; split + · rintro ⟨⟩ ⟨⟩; exact ⟨⟨_, h4⟩, h3⟩ + · simp + rintro d₁' n₁' H1' rfl rfl d₂' n₂' H2' rfl rfl + simpa [VLocalDecl.depth, VLCtx.toCtx] using find?_uniq hΔ H1' H2' + +theorem VLCtx.IsDefEq.find?_defeqDFC (hΔ : VLCtx.IsDefEq env U Δ₁ Δ₂) + (H : Δ₁.find? v = some (e₁, A₁)) : + ∃ e₂ A₂, Δ₂.find? v = some (e₂, A₂) := by + let .cons hΔ _ _ := hΔ + revert H; unfold VLCtx.find?; split + · exact fun _ => ⟨_, _, rfl⟩ + · simp; rintro e A H rfl rfl + obtain ⟨_, _, H⟩ := find?_defeqDFC hΔ H + exact ⟨_, _, _, _, H, rfl, rfl⟩ + +theorem TrExprS.closed (H : TrExprS env Us Δ e e') : Closed e Δ.bvars := by + induction H with + | @bvar e A Δ i h1 => + simp [Closed] + induction Δ generalizing i e A with + | nil => cases h1 + | cons d Δ ih => + match d, i with + | (none, _), 0 => exact Nat.succ_pos _ + | (none, _), _ + 1 => + simp [VLCtx.find?, VLCtx.next, bind] at h1 + obtain ⟨_, _, h1, rfl, rfl⟩ := h1 + exact Nat.succ_lt_succ (ih h1) + | (some _, _), _ => + simp [VLCtx.find?, VLCtx.next, bind] at h1 + obtain ⟨_, _, h1, rfl, rfl⟩ := h1 + exact ih h1 + | fvar | sort | const | lit | mdata => trivial + | app _ _ _ _ ih1 ih2 + | lam _ _ _ ih1 ih2 + | forallE _ _ _ _ ih1 ih2 => exact ⟨ih1, ih2⟩ + | letE _ _ _ _ ih1 ih2 ih3 => exact ⟨ih1, ih2, ih3⟩ + | proj _ _ ih => exact ih + +theorem ofLevel_hasMVar (h : VLevel.ofLevel ls l = some l') : l.hasMVar' = false := by + induction l generalizing l' with simp [VLevel.ofLevel, bind, Level.hasMVar'] at h ⊢ + | succ _ ih => obtain ⟨l', h, ⟨⟩⟩ := h; exact ih h + | max _ _ ih1 ih2 | imax _ _ ih1 ih2 => obtain ⟨_, h1, _, h2, ⟨⟩⟩ := h; exact ⟨ih1 h1, ih2 h2⟩ + +theorem TrExprS.fvarsIn (H : TrExprS env Us Δ e e') : FVarsIn (· ∈ Δ.fvars) e := by + induction H with + | fvar h1 => exact VLCtx.find?_eq_some.1 ⟨_, h1⟩ + | sort h => exact ofLevel_hasMVar h + | const _ h => + rw [List.mapM_eq_some] at h + intro _ hl + have ⟨_, _, h⟩ := h.forall_exists_l _ hl + exact ofLevel_hasMVar h + | bvar | lit | mdata => trivial + | app _ _ _ _ ih1 ih2 + | lam _ _ _ ih1 ih2 + | forallE _ _ _ _ ih1 ih2 => exact ⟨ih1, ih2⟩ + | letE _ _ _ _ ih1 ih2 ih3 => exact ⟨ih1, ih2, ih3⟩ + | proj _ _ ih => exact ih + +theorem TrExprS.fvarsList (H : TrExprS env Us Δ e e') : e.fvarsList ⊆ Δ.fvars := + (fvarsIn_iff.1 H.fvarsIn).1 + +theorem TrExpr.closed (H : TrExpr env Us Δ e e') : Closed e Δ.bvars := + let ⟨_, H, _⟩ := H; H.closed + +theorem TrExpr.fvarsIn (H : TrExpr env Us Δ e e') : FVarsIn (· ∈ Δ.fvars) e := + let ⟨_, H, _⟩ := H; H.fvarsIn + +theorem TrExpr.fvarsList (H : TrExpr env Us Δ e e') : e.fvarsList ⊆ Δ.fvars := + (fvarsIn_iff.1 H.fvarsIn).1 + +theorem TrProj.wf (H1 : TrProj env U Γ s i e e') + (_H2 : VExpr.WF env U Γ e) : VExpr.WF env U Γ e' := by + obtain ⟨view, levels, params, _hname, hproj⟩ := H1 + obtain ⟨code, _hcode, rfl, hprojector⟩ := hproj.program + exact ⟨_, hprojector.app hproj.majorType⟩ + +theorem TrExpr.wf (H : TrExpr env Us Δ e e') : VExpr.WF env Us.length Δ.toCtx e' := + let ⟨_, _, _, H⟩ := H; ⟨_, H.hasType.2⟩ + +variable! (henv : Ordered env) {Us : List Name} (hΔ : VLCtx.WF env Us.length Δ) in +theorem TrExprS.wf (H : TrExprS env Us Δ e e') : VExpr.WF env Us.length Δ.toCtx e' := by + induction H with + | bvar h1 | fvar h1 => exact ⟨_, hΔ.find?_wf henv h1⟩ + | sort h1 => exact ⟨_, HasType.sort (.of_ofLevel h1)⟩ + | const h1 h2 h3 => exact ⟨_, + HasType.const h1 (.of_mapM_ofLevel h2) ((List.mapM_eq_some.1 h2).length_eq.symm.trans h3)⟩ + | app h1 h2 => exact ⟨_, h1.app h2⟩ + | lam h1 _ _ _ ih2 => + have ⟨_, h1'⟩ := h1 + have ⟨_, h2'⟩ := ih2 ⟨hΔ, nofun, h1⟩ + refine ⟨_, h1'.lam h2'⟩ + | forallE h1 h2 => have ⟨_, h1'⟩ := h1; have ⟨_, h2'⟩ := h2; exact ⟨_, h1'.forallE h2'⟩ + | letE h1 _ _ _ _ _ ih3 => exact ih3 ⟨hΔ, nofun, h1⟩ + | lit _ _ ih | mdata _ ih => exact ih hΔ + | proj _ h2 ih => exact h2.wf (ih hΔ) + +variable! (henv : Ordered env) {Us : List Name} (hΔ : VLCtx.WF env Us.length Δ) in +theorem TrExprS.trExpr (H : TrExprS env Us Δ e e') : TrExpr env Us Δ e e' := + ⟨_, H, H.wf henv hΔ⟩ + +theorem TrExpr.defeq (henv : VEnv.WF env) (hΔ : OnCtx Δ.toCtx (env.IsType Us.length)) + (h1 : TrExpr env Us Δ e e₁) (h2 : env.IsDefEqU Us.length Δ.toCtx e₁ e₂) : + TrExpr env Us Δ e e₂ := let ⟨_, H, h1⟩ := h1; ⟨_, H, h1.trans henv hΔ h2⟩ + +theorem TrExpr.app (henv : VEnv.WF env) (hΔ : OnCtx Δ.toCtx (env.IsType Us.length)) + (h1 : env.HasType Us.length Δ.toCtx f' (.forallE A B)) + (h2 : env.HasType Us.length Δ.toCtx a' A) + (h3 : TrExpr env Us Δ f f') + (h4 : TrExpr env Us Δ a a') : + TrExpr env Us Δ (.app f a) (.app f' a') := + let ⟨_, s3, h3⟩ := h3 + let ⟨_, s4, h4⟩ := h4 + have h3 := h3.of_r henv hΔ h1 + have h4 := h4.of_r henv hΔ h2 + ⟨_, .app h3.hasType.1 h4.hasType.1 s3 s4, _, h3.appDF h4⟩ + +variable! (henv : VEnv.WF env) (hΓ : IsDefEqCtx env U [] Γ₁ Γ₂) in +theorem TrProj.uniq (H1 : TrProj env U Γ₁ s₁ i e₁ e₁') + (H2 : TrProj env U Γ₂ s₂ i e₂ e₂') + (H : env.IsDefEqU U Γ₁ e₁ e₂) : + env.IsDefEqU U Γ₁ e₁' e₂' := by + obtain ⟨view₁, levels₁, params₁, _hname₁, hproj₁⟩ := H1 + obtain ⟨view₂, levels₂, params₂, _hname₂, hproj₂⟩ := H2 + exact henv.registeredStructureHeadInversion.unique + hΓ hproj₁ hproj₂ H + +variable! (henv : VEnv.WF env) {Us : List Name} (hΔ : VLCtx.IsDefEq env Us.length Δ₁ Δ₂) in +theorem TrExprS.uniq (H1 : TrExprS env Us Δ₁ e e₁) (H2 : TrExprS env Us Δ₂ e e₂) : + env.IsDefEqU Us.length Δ₁.toCtx e₁ e₂ := by + induction H1 generalizing Δ₂ e₂ with + | bvar l1 => let .bvar r1 := H2; exact ⟨_, (hΔ.find?_uniq henv l1 r1).2⟩ + | fvar l1 => let .fvar r1 := H2; exact ⟨_, (hΔ.find?_uniq henv l1 r1).2⟩ + | sort l1 => + let .sort r1 := H2; cases l1.symm.trans r1; exact ⟨_, HasType.sort (.of_ofLevel l1)⟩ + | const l1 l2 l3 => + let .const r1 r2 r3 := H2; cases l1.symm.trans r1; cases l2.symm.trans r2 + exact (TrExprS.const l1 l2 l3).wf henv hΔ.wf + | app l1 l2 _ _ ih3 ih4 => + let .app _ _ r3 r4 := H2 + exact ⟨_, .appDF + (ih3 hΔ r3 |>.of_l henv hΔ.wf.toCtx l1) + (ih4 hΔ r4 |>.of_l henv hΔ.wf.toCtx l2)⟩ + | lam l1 _ _ ih2 ih3 => + let ⟨_, l1⟩ := l1; let .lam _ r2 r3 := H2 + have hA := ih2 hΔ r2 |>.of_l henv hΔ.wf.toCtx l1 + have ⟨_, hb⟩ := ih3 (hΔ.cons nofun <| .vlam hA) r3 + exact ⟨_, .lamDF hA hb⟩ + | forallE l1 l2 _ _ ih3 ih4 => + let ⟨_, l1'⟩ := l1; let ⟨_, l2⟩ := l2; let .forallE _ _ r3 r4 := H2 + have hA := ih3 hΔ r3 |>.of_l henv hΔ.wf.toCtx l1' + have hB := ih4 (hΔ.cons nofun <| .vlam hA) r4 |>.of_l (Γ := _::_) henv ⟨hΔ.wf.toCtx, l1⟩ l2 + exact ⟨_, .forallEDF hA hB⟩ + | letE l1 _ _ _ ih2 ih3 ih4 => + have hΓ := hΔ.wf.toCtx + let .letE _ r2 r3 r4 := H2 + have ⟨_, hb⟩ := l1.isType henv hΓ + refine ih4 (hΔ.cons nofun ?_) r4 + exact .vlet (ih3 hΔ r3 |>.of_l henv hΓ l1) (ih2 hΔ r2 |>.of_l henv hΓ hb) + | lit _ _ ih1 => let .lit _ r2 := H2; exact ih1 hΔ r2 + | mdata _ ih1 => let .mdata r1 := H2; exact ih1 hΔ r1 + | proj _ l2 ih1 => let .proj r1 r2 := H2; exact l2.uniq henv hΔ.defeqCtx r2 (ih1 hΔ r1) + +variable! (henv : VEnv.WF env) {Us : List Name} + (hΔ : VLCtx.IsDefEqFVars env Us.length Δ₁ Δ₂) + (hΔwf : VLCtx.WF env Us.length Δ₁) in +/-- Translation congruence across contexts with the same free-variable +identifiers but potentially different dependency metadata. -/ +theorem TrExprS.uniqFVars + (H₁ : TrExprS env Us Δ₁ e e₁) (H₂ : TrExprS env Us Δ₂ e e₂) : + env.IsDefEqU Us.length Δ₁.toCtx e₁ e₂ := by + induction H₁ generalizing Δ₂ e₂ with + | bvar l₁ => + let .bvar r₁ := H₂ + exact ⟨_, (hΔ.find?_uniq henv l₁ r₁).2⟩ + | fvar l₁ => + let .fvar r₁ := H₂ + exact ⟨_, (hΔ.find?_uniq henv l₁ r₁).2⟩ + | sort l₁ => + let .sort r₁ := H₂ + cases l₁.symm.trans r₁ + exact ⟨_, HasType.sort (.of_ofLevel l₁)⟩ + | const l₁ l₂ l₃ => + let .const r₁ r₂ r₃ := H₂ + cases l₁.symm.trans r₁ + cases l₂.symm.trans r₂ + exact (TrExprS.const l₁ l₂ l₃).wf henv hΔwf + | app l₁ l₂ _ _ ih₃ ih₄ => + let .app _ _ r₃ r₄ := H₂ + exact ⟨_, .appDF + (ih₃ hΔ hΔwf r₃ |>.of_l henv hΔwf.toCtx l₁) + (ih₄ hΔ hΔwf r₄ |>.of_l henv hΔwf.toCtx l₂)⟩ + | lam l₁ _ _ ih₂ ih₃ => + let ⟨u, l₁'⟩ := l₁ + let .lam _ r₂ r₃ := H₂ + have hA := ih₂ hΔ hΔwf r₂ |>.of_l henv hΔwf.toCtx l₁' + have ⟨_, hb⟩ := ih₃ (.cons_bvar hΔ (.vlam hA)) + ⟨hΔwf, nofun, ⟨u, l₁'⟩⟩ r₃ + exact ⟨_, .lamDF hA hb⟩ + | forallE l₁ l₂ _ _ ih₃ ih₄ => + let ⟨_, l₁'⟩ := l₁ + let ⟨_, l₂⟩ := l₂ + let .forallE _ _ r₃ r₄ := H₂ + have hA := ih₃ hΔ hΔwf r₃ |>.of_l henv hΔwf.toCtx l₁' + have hbody := ih₄ (.cons_bvar hΔ (.vlam hA)) + ⟨hΔwf, nofun, l₁⟩ r₄ + have hbodyCtx : OnCtx (_ :: _) (env.IsType Us.length) := + ⟨hΔwf.toCtx, l₁⟩ + have hB := hbody.of_l henv hbodyCtx l₂ + exact ⟨_, .forallEDF hA hB⟩ + | letE l₁ _ _ _ ih₂ ih₃ ih₄ => + have hΓ := hΔwf.toCtx + let .letE _ r₂ r₃ r₄ := H₂ + have ⟨_, hb⟩ := l₁.isType henv hΓ + refine ih₄ (.cons_bvar hΔ ?_) ⟨hΔwf, nofun, l₁⟩ r₄ + exact .vlet + (ih₃ hΔ hΔwf r₃ |>.of_l henv hΓ l₁) + (ih₂ hΔ hΔwf r₂ |>.of_l henv hΓ hb) + | lit _ _ ih₁ => + let .lit _ r₂ := H₂ + exact ih₁ hΔ hΔwf r₂ + | mdata _ ih₁ => + let .mdata r₁ := H₂ + exact ih₁ hΔ hΔwf r₁ + | proj _ l₂ ih₁ => + let .proj r₁ r₂ := H₂ + exact l₂.uniq henv hΔ.defeqCtx r₂ (ih₁ hΔ hΔwf r₁) + +variable! (henv : VEnv.WF env) {Us : List Name} (hΔ : VLCtx.IsDefEq env Us.length Δ₁ Δ₂) in +theorem TrExpr.uniq (H1 : TrExpr env Us Δ₁ e e₁) (H2 : TrExpr env Us Δ₂ e e₂) : + env.IsDefEqU Us.length Δ₁.toCtx e₁ e₂ := by + let ⟨_, H1, eq1⟩ := H1 + let ⟨_, H2, eq2⟩ := H2 + exact eq1.symm.trans henv hΔ.wf <| (H1.uniq henv hΔ H2).trans henv hΔ.wf <| + eq2.defeqDFC henv (hΔ.defeqCtx.symm henv) + +variable! (henv : VEnv.WF env) {Us : List Name} (hΔ : VLCtx.IsDefEq env Us.length Δ₁ Δ₂) in +theorem TrExprS.defeqDFC (H : TrExprS env Us Δ₁ e e₁) : ∃ e₂, TrExprS env Us Δ₂ e e₂ := by + induction H generalizing Δ₂ with + | bvar h1 => have ⟨_, _, h1⟩ := hΔ.find?_defeqDFC h1; exact ⟨_, .bvar h1⟩ + | fvar h1 => have ⟨_, _, h1⟩ := hΔ.find?_defeqDFC h1; exact ⟨_, .fvar h1⟩ + | sort h1 => exact ⟨_, .sort h1⟩ + | const h1 h2 h3 => exact ⟨_, .const h1 h2 h3⟩ + | app h1 h2 h3 h4 ih3 ih4 => + let ⟨_, h3'⟩ := ih3 hΔ + let ⟨_, h4'⟩ := ih4 hΔ + have h1 := h1.defeqDFC henv hΔ.defeqCtx + have h2 := h2.defeqDFC henv hΔ.defeqCtx + have h1 := h1.defeqU_l henv (hΔ.symm henv).wf (h3'.uniq henv (hΔ.symm henv) h3).symm + have h2 := h2.defeqU_l henv (hΔ.symm henv).wf (h4'.uniq henv (hΔ.symm henv) h4).symm + exact ⟨_, .app h1 h2 h3' h4'⟩ + | lam h1 h2 h3 ih2 ih3 => + let ⟨_, h1'⟩ := h1 + let ⟨_, h2'⟩ := ih2 hΔ + have h1 := h1.defeqDFC henv hΔ.defeqCtx + have h1 := h1.defeqU_l henv (hΔ.symm henv).wf (h2'.uniq henv (hΔ.symm henv) h2).symm + have ht := (h2.uniq henv hΔ h2').of_l henv hΔ.wf h1' + let ⟨_, h3'⟩ := ih3 (hΔ.cons nofun <| .vlam ht) + exact ⟨_, .lam h1 h2' h3'⟩ + | forallE h1 h2 h3 h4 ih3 ih4 => + let ⟨_, h1'⟩ := h1 + let ⟨_, h2'⟩ := h2 + let ⟨_, h3'⟩ := ih3 hΔ + have ht := (h3.uniq henv hΔ h3').of_l henv hΔ.wf h1' + have hΔ' := hΔ.cons (ofv := none) nofun (.vlam ht) + let ⟨_, h4'⟩ := ih4 hΔ' + have h1 := h1.defeqDFC henv hΔ.defeqCtx + have h2 := h2.defeqDFC henv (hΔ.defeqCtx.succ ht) + have h1 := h1.defeqU_l henv (hΔ.symm henv).wf (h3'.uniq henv (hΔ.symm henv) h3).symm + have h2 := h2.defeqU_l henv (hΔ'.symm henv).wf (h4'.uniq henv (hΔ'.symm henv) h4).symm + exact ⟨_, .forallE h1 h2 h3' h4'⟩ + | letE h1 h2 h3 h4 ih2 ih3 ih4 => + let ⟨_, h2'⟩ := ih2 hΔ + let ⟨_, h3'⟩ := ih3 hΔ + have ⟨_, h0⟩ := h1.isType henv hΔ.wf + have t0 := (h2.uniq henv hΔ h2').of_l henv hΔ.wf h0 + have t1 := (h3.uniq henv hΔ h3').of_l henv hΔ.wf h1 + have t2 := (h2'.uniq henv (hΔ.symm henv) h2).symm + have t3 := (h3'.uniq henv (hΔ.symm henv) h3).symm + have hΔ' := hΔ.cons (ofv := none) nofun (.vlet t1 t0) + let ⟨_, h4'⟩ := ih4 hΔ' + have h0 := h0.defeqDFC henv hΔ.defeqCtx + have h0 := h0.defeqU_l henv (hΔ.symm henv).wf t2 + have h1 := h1.defeqDFC henv hΔ.defeqCtx + have h1 := h1.defeqU_l henv (hΔ.symm henv).wf t3 + have h1 := h1.defeqU_r henv (hΔ.symm henv).wf t2 + exact ⟨_, .letE h1 h2' h3' h4'⟩ + | lit h1 _ ih1 => let ⟨_, h2⟩ := ih1 hΔ; exact ⟨_, .lit h1 h2⟩ + | mdata _ ih1 => let ⟨_, h1⟩ := ih1 hΔ; exact ⟨_, .mdata h1⟩ + | proj h1 h2 ih1 => + let ⟨_, h1'⟩ := ih1 hΔ + let ⟨_, h2⟩ := h2.defeqDFC henv hΔ.defeqCtx (h1.uniq henv hΔ h1') + exact ⟨_, .proj h1' h2⟩ + +variable! (henv : VEnv.WF env) {Us : List Name} (hΔ : VLCtx.IsDefEq env Us.length Δ₁ Δ₂) in +theorem TrExprS.defeqDFC' (H : TrExprS env Us Δ₁ e e') : TrExpr env Us Δ₂ e e' := by + let ⟨_, H'⟩ := H.defeqDFC henv hΔ + refine ⟨_, H', H'.uniq henv (hΔ.symm henv) H⟩ + +theorem TrExpr.lam (henv : VEnv.WF env) (hΔ : VLCtx.WF env Us.length Δ) + (h1 : env.IsType Us.length Δ.toCtx ty') + (h2 : TrExpr env Us Δ ty ty') + (h3 : TrExpr env Us ((none, .vlam ty') :: Δ) body body') : + TrExpr env Us Δ (.lam name ty body bi) (.lam ty' body') := + let ⟨_, h1⟩ := h1 + let ⟨_, s2, h2⟩ := h2 + let ⟨_, s3, _, h3⟩ := h3 + have := h2.symm.of_l henv hΔ h1 + have hΔΔ := .cons (.refl henv hΔ) (ofv := none) nofun (.vlam this) + let ⟨_, s3'⟩ := s3.defeqDFC henv hΔΔ + let ⟨_, h3'⟩ := s3.uniq henv hΔΔ s3' + ⟨_, .lam ⟨_, this.hasType.2⟩ s2 s3', _, + .symm <| .lamDF this <| h3.symm.trans_l henv hΔΔ.wf.toCtx h3'⟩ + +theorem TrExpr.forallE (henv : VEnv.WF env) (hΔ : VLCtx.WF env Us.length Δ) + (h1 : env.IsType Us.length Δ.toCtx ty') + (h2 : env.IsType Us.length (ty' :: Δ.toCtx) body') + (h3 : TrExpr env Us Δ ty ty') + (h4 : TrExpr env Us ((none, .vlam ty') :: Δ) body body') : + TrExpr env Us Δ (.forallE name ty body bi) (.forallE ty' body') := + let ⟨_, h1⟩ := h1 + let ⟨_, h2⟩ := h2 + let ⟨_, s3, h3⟩ := h3 + let ⟨_, s4, _, h4⟩ := h4 + have := h3.symm.of_l henv hΔ h1 + have hΔΔ := .cons (.refl henv hΔ) (ofv := none) nofun (.vlam this) + let ⟨_, s4'⟩ := s4.defeqDFC henv hΔΔ + let ⟨_, h4'⟩ := s4.uniq henv hΔΔ s4' + have h4 := h4.trans_r henv hΔΔ.wf h2 |>.symm.trans_l henv hΔΔ.wf h4' + have h5 := h4.hasType.2.defeq_l henv this + ⟨_, .forallE ⟨_, this.hasType.2⟩ ⟨_, h5⟩ s3 s4', _, .symm <| .forallEDF this h4⟩ + +theorem TrExpr.letE (henv : VEnv.WF env) (hΔ : VLCtx.WF env Us.length Δ) + (h1 : env.HasType Us.length Δ.toCtx val' ty') + (h2 : TrExpr env Us Δ ty ty') + (h3 : TrExpr env Us Δ val val') + (h4 : TrExpr env Us ((none, .vlet ty' val') :: Δ) body body') : + TrExpr env Us Δ (.letE name ty val body nd) body' := + have ⟨_, h0⟩ := h1.isType henv hΔ + let ⟨_, s2, h2⟩ := h2 + let ⟨_, s3, h3⟩ := h3 + let ⟨_, s4, _, h4⟩ := h4 + have h1' := h1.defeqU_r henv hΔ h2.symm |>.defeqU_l henv hΔ h3.symm + have h2' := h2.symm.of_l henv hΔ h0 + have h3' := h3.symm.of_l henv hΔ h1 + have hΔΔ := VLCtx.IsDefEq.cons (.refl henv hΔ) (ofv := none) nofun (.vlet h3' h2') + let ⟨_, s4'⟩ := s4.defeqDFC henv hΔΔ + let ⟨_, h4'⟩ := s4.uniq henv hΔΔ s4' + ⟨_, .letE h1' s2 s3 s4', _, h4'.symm.trans_l henv hΔ h4⟩ + +theorem TrExpr.lit (h1 : env.ContainsLits l) + (h : TrExpr env Us Δ l.toConstructor e') : TrExpr env Us Δ (.lit l) e' := + let ⟨_, s2, h2⟩ := h; ⟨_, .lit h1 s2, h2⟩ + +theorem TrExpr.mdata (h : TrExpr env Us Δ e e') : TrExpr env Us Δ (.mdata d e) e' := + let ⟨_, s2, h2⟩ := h; ⟨_, .mdata s2, h2⟩ + +theorem TrExpr.proj {env Us Δ e e' s i e''} (henv : VEnv.WF env) (hΔ : VLCtx.WF env Us.length Δ) + (H : TrExpr env Us Δ e e') + (H2 : TrProj env Us.length Δ.toCtx s i e' e'') : + TrExpr env Us Δ (.proj s i e) e'' := + let ⟨_, s2, h2⟩ := H + have ⟨_, H2'⟩ := H2.defeqDFC henv (.refl hΔ) h2.symm + ⟨_, .proj s2 H2', H2'.uniq henv (.refl hΔ) H2 h2⟩ + +theorem TrExprS.weakFV'_inv (henv : VEnv.WF env) + (W : VLCtx.FVLift' Δ Δ₂ dk n k) (hΔ : VLCtx.IsDefEq env Us.length Δ₁ Δ₂) + (H : TrExprS env Us Δ₁ e e') (hc : Closed e dk) (hv : FVarsIn (· ∈ VLCtx.fvars Δ) e) : + ∃ e', TrExprS env Us Δ e e' := by + induction H generalizing Δ Δ₂ dk k with + | @bvar e A Δ₁ i h1 => + suffices ∃ p, Δ.find? (.inl i) = some p from let ⟨_, h⟩ := this; ⟨_, .bvar h⟩ + simp [Closed] at hc + induction W generalizing i e A Δ₁ with | @cons_bvar _ Δ₂ _ _ _ d _ ih => ?_ | _ => cases hc + obtain ⟨d, Δ₂, rfl, hΔ₁⟩ : ∃ d Δ₁', Δ₁ = (none, d) :: Δ₁' ∧ + VLCtx.IsDefEq env Us.length Δ₁' Δ₂ := by cases d <;> cases hΔ <;> exact ⟨_, _, rfl, ‹_›⟩ + simp [VLCtx.find?] at h1 ⊢ + rcases i with _ | i <;> simp [VLCtx.next] at h1 ⊢ + obtain ⟨_, _, h1, _⟩ := h1 + have ⟨_, h1⟩ := ih h1 hΔ₁ (Nat.lt_of_succ_lt_succ hc) hv + exact ⟨_, _, _, _, h1, rfl, rfl⟩ + | @fvar _ _ _ fv => let ⟨_, h⟩ := VLCtx.find?_eq_some.2 hv; exact ⟨_, .fvar h⟩ + | sort h1 => exact ⟨_, .sort h1⟩ + | const h1 h2 h3 => exact ⟨_, .const h1 h2 h3⟩ + | app h1 h2 hf ha ih1 ih2 => + have hΔ₁ := hΔ.wf; have hΔ₂ := (hΔ.symm henv).wf + let ⟨f₁, ih1⟩ := ih1 W hΔ hc.1 hv.1 + let ⟨a₁, ih2⟩ := ih2 W hΔ hc.2 hv.2 + have h1 := h1.defeqU_l henv hΔ₁.toCtx <| hf.uniq henv hΔ (ih1.weakFV' henv W hΔ₂) + have h2 := h2.defeqU_l henv hΔ₁.toCtx <| ha.uniq henv hΔ (ih2.weakFV' henv W hΔ₂) + have := VExpr.WF.weak'_iff henv hΔ₂.toCtx W.toCtx (e := f₁.app a₁) + have := this.1 ⟨_, (h1.app h2).defeqDFC henv hΔ.defeqCtx⟩ + have ⟨_, _, h1, h2⟩ := this.app_inv henv (W.wf henv hΔ₂).toCtx + exact ⟨_, .app h1 h2 ih1 ih2⟩ + | lam h1 ht _ ih1 ih2 => + let ⟨_, h1⟩ := h1 + have hΔ₁ := hΔ.wf; have hΔ₂ := (hΔ.symm henv).wf + let ⟨ty₁, ih1⟩ := ih1 W hΔ hc.1 hv.1 + have htt := ht.uniq henv hΔ (ih1.weakFV' henv W hΔ₂) |>.of_l henv hΔ₁.toCtx h1 + have ⟨_, ih2⟩ := ih2 (W.cons_bvar (.vlam _)) + (hΔ.cons (ofv := none) nofun <| .vlam htt) hc.2 hv.2.fvars_cons + have h1 := HasType.weak'_iff (A := .sort _) henv hΔ₂.toCtx W.toCtx + |>.1 (htt.hasType.2.defeqDFC henv hΔ.defeqCtx) + exact ⟨_, .lam ⟨_, h1⟩ ih1 ih2⟩ + | forallE h1 h2 ht hb ih1 ih2 => + let ⟨_, h1⟩ := h1; let ⟨_, h2⟩ := h2 + have hΔ₁ := hΔ.wf; have hΔ₂ := (hΔ.symm henv).wf + let ⟨ty₁, ih1⟩ := ih1 W hΔ hc.1 hv.1 + have htt := ht.uniq henv hΔ (ih1.weakFV' henv W hΔ₂) |>.of_l henv hΔ₁.toCtx h1 + have hΔ' := hΔ.cons (ofv := none) nofun <| .vlam htt + have ⟨_, ih2⟩ := ih2 (W.cons_bvar (.vlam _)) hΔ' hc.2 hv.2.fvars_cons + have h1' := htt.hasType.2.defeqDFC henv hΔ.defeqCtx + have h1 := HasType.weak'_iff (A := .sort _) henv hΔ₂.toCtx W.toCtx |>.1 h1' + have hΔ₂' : VLCtx.WF _ _ ((none, .vlam _) :: _) := ⟨hΔ₂, nofun, _, h1'⟩ + have h2 := (HasType.weak'_iff (A := .sort _) henv hΔ₂'.toCtx (W.cons_bvar (.vlam _)).toCtx).1 <| + hb.uniq henv hΔ' (ih2.weakFV' henv (W.cons_bvar _) hΔ₂') + |>.of_l (Γ := _::_) henv ⟨hΔ₁.toCtx, _, htt.hasType.1⟩ h2 + |>.hasType.2.defeqDFC henv (.succ hΔ.defeqCtx htt) + exact ⟨_, .forallE ⟨_, h1⟩ ⟨_, h2⟩ ih1 ih2⟩ + | letE h1 ht ha _ ih1 ih2 ih3 => + have hΔ₁ := hΔ.wf; have hΔ₂ := (hΔ.symm henv).wf + let ⟨ty₁, ih1⟩ := ih1 W hΔ hc.1 hv.1 + let ⟨val₁, ih2⟩ := ih2 W hΔ hc.2.1 hv.2.1 + have hvv := ha.uniq henv hΔ (ih2.weakFV' henv W hΔ₂) |>.of_l henv hΔ₁.toCtx h1 + let ⟨_, h2⟩ := h1.isType henv hΔ₁.toCtx + have htt := ht.uniq henv hΔ (ih1.weakFV' henv W hΔ₂) |>.of_l henv hΔ₁.toCtx h2 + have ⟨_, ih3⟩ := ih3 (W.cons_bvar (.vlet ..)) + (hΔ.cons nofun <| .vlet hvv htt) hc.2.2 hv.2.2.fvars_cons + have h1 := HasType.weak'_iff henv hΔ₂.toCtx W.toCtx + |>.1 ((htt.defeqDF hvv).hasType.2.defeqDFC henv hΔ.defeqCtx) + exact ⟨_, .letE h1 ih1 ih2 ih3⟩ + | lit h1 _ ih => let ⟨_, ih⟩ := ih W hΔ .toConstructor .toConstructor; exact ⟨_, .lit h1 ih⟩ + | mdata _ ih => let ⟨_, ih⟩ := ih W hΔ hc hv; exact ⟨_, .mdata ih⟩ + | proj h1 h2 ih => + have hΔ₂ := (hΔ.symm henv).wf + let ⟨_, ih⟩ := ih W hΔ hc hv + have htt := h1.uniq henv hΔ (ih.weakFV' henv W hΔ₂) + have ⟨_, h2⟩ := h2.defeqDFC henv hΔ.defeqCtx htt + have ⟨_, h2⟩ := h2.weak'_inv henv hΔ₂.toCtx W.toCtx + exact ⟨_, .proj ih h2⟩ + +theorem TrExprS.weakFV_inv (henv : VEnv.WF env) + (W : VLCtx.FVLift Δ Δ₂ dk n k) (hΔ : VLCtx.IsDefEq env Us.length Δ₁ Δ₂) + (H : TrExprS env Us Δ₁ e e') (hc : Closed e dk) (hv : FVarsIn (· ∈ VLCtx.fvars Δ) e) : + ∃ e', TrExprS env Us Δ e e' := H.weakFV'_inv henv W.toFVLift' hΔ hc hv + +variable! (henv : Ordered env) (h₀ : TrExprS env Us Δ₀ e₀ e₀') in +theorem TrExprS.instN_var (W : VLCtx.InstN Δ₀ e₀' A₀ dk k Δ₁ Δ) (H : Δ₁.find? v = some (e', A)) : + TrExprS env Us Δ (Expr.instantiate1' (VLCtx.varToExpr v) e₀ dk) (e'.inst e₀' k) := by + induction W generalizing v e' A with + | zero => + obtain (_|i)|fv := v <;> simp [VLCtx.varToExpr, Expr.instantiate1', Expr.liftLooseBVars_zero] + · cases H; simp [VLocalDecl.value, VExpr.inst]; exact h₀ + · simp [VLCtx.find?, VLCtx.next] at H + obtain ⟨e, A, H, rfl, rfl⟩ := H + simp [VLocalDecl.depth, VExpr.inst_liftN] + exact .bvar H + · simp [VLCtx.find?, VLCtx.next] at H + obtain ⟨e, A, H, rfl, rfl⟩ := H + simp [VLocalDecl.depth, VExpr.inst_liftN] + exact .fvar H + | @succ _ k _ _ d _ ih => + obtain (_|i)|fv := v <;> simp [VLCtx.varToExpr, Expr.instantiate1'] + · cases H + cases d <;> exact .bvar <| by simp [VLocalDecl.value, VExpr.inst, VLocalDecl.depth]; rfl + · simp [VLCtx.find?, VLCtx.next] at H + obtain ⟨e, A, H, rfl, rfl⟩ := H + have := ih H; revert this + simp [VLCtx.varToExpr, Expr.instantiate1']; split <;> [skip; split] + · intro | .bvar h => ?_ + exact .bvar <| by + simp [VLCtx.find?, VLCtx.next] + refine ⟨_, _, h, ?_, rfl⟩ + cases d <;> simp [VLocalDecl.depth, VLocalDecl.inst, VExpr.lift_instN_lo] + · intro H + have := Expr.liftLooseBVars_add ▸ H.weakBV henv (.skip (d.inst e₀' k) .refl) + cases d <;> simpa [← VExpr.lift_instN_lo, VExpr.liftN_zero, + VLocalDecl.inst, VLocalDecl.depth] using this + · obtain _|i := i; · omega + intro | .bvar h => ?_ + exact .bvar <| by + simp [VLCtx.find?, VLCtx.next] + refine ⟨_, _, h, ?_, rfl⟩ + cases d <;> simp [VLocalDecl.depth, VLocalDecl.inst, VExpr.lift_instN_lo] + · simp [VLCtx.find?, VLCtx.next] at H + obtain ⟨e, A, H, rfl, rfl⟩ := H + have .fvar h := ih H + exact .fvar <| by + simp [VLCtx.find?, VLCtx.next] + refine ⟨_, _, h, ?_, rfl⟩ + cases d <;> simp [VLocalDecl.depth, VLocalDecl.inst, VExpr.lift_instN_lo] + +theorem TrProj.instN (henv : env.Ordered) + (h₀ : env.HasType U Γ₀ e₀ A₀) + (W : Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ) + (H : TrProj env U Γ₁ s i e e') : + TrProj env U Γ s i (e.inst e₀ k) (e'.inst e₀ k) := by + obtain ⟨view, levels, params, hname, hproj⟩ := H + exact ⟨view, levels, params.map (fun param => param.inst e₀ k), + hname, hproj.instN henv W h₀⟩ + +variable! (henv : Ordered env) (h₀ : TrExprS env Us Δ₀ e₀ e₀') + (t₀ : env.HasType Us.length Δ₀.toCtx e₀' A₀) in +theorem TrExprS.instN (W : VLCtx.InstN Δ₀ e₀' A₀ dk k Δ₁ Δ) (H : TrExprS env Us Δ₁ e e') : + TrExprS env Us Δ (Expr.instantiate1' e e₀ dk) (e'.inst e₀' k) := by + induction H generalizing Δ dk k with + | bvar h1 | fvar h1 => exact instN_var henv h₀ W h1 + | sort h1 => exact .sort h1 + | const h1 h2 h3 => exact .const h1 h2 h3 + | app h1 h2 _ _ ih1 ih2 => + exact .app (h1.instN henv W.toCtx t₀) (h2.instN henv W.toCtx t₀) (ih1 W) (ih2 W) + | lam h1 _ _ ih1 ih2 => + exact .lam (h1.instN henv W.toCtx t₀) (ih1 W) (ih2 (W.succ (d := .vlam _))) + | forallE h1 h2 _ _ ih1 ih2 => + exact .forallE (h1.instN henv W.toCtx t₀) (h2.instN henv W.toCtx.succ t₀) + (ih1 W) (ih2 (W.succ (d := .vlam _))) + | letE h1 _ _ _ ih1 ih2 ih3 => + exact .letE (h1.instN henv W.toCtx t₀) (ih1 W) (ih2 W) (ih3 (W.succ (d := .vlet ..))) + | lit h1 _ ih => + refine .lit h1 (Expr.instantiate1'_eq_self ?_ ▸ ih W :) + exact Closed.toConstructor.looseBVarRange_le + | mdata _ ih => exact .mdata (ih W) + | proj _ h2 ih => exact .proj (ih W) (h2.instN henv t₀ W.toCtx) + +theorem TrExprS.inst {Δ : VLCtx} (henv : Ordered env) + (t₀ : env.HasType Us.length Δ.toCtx e₀' A₀) + (H : TrExprS env Us ((none, .vlam A₀) :: Δ) e e') + (h₀ : TrExprS env Us Δ e₀ e₀') : + TrExprS env Us Δ (e.instantiate1' e₀) (e'.inst e₀') := + h₀.instN henv t₀ .zero H + +theorem TrExpr.inst (henv : VEnv.WF env) (hΔ : VLCtx.WF env Us.length Δ) + (t₀ : env.HasType Us.length Δ.toCtx e₀' A₀) + (H : TrExpr env Us ((none, .vlam A₀) :: Δ) e e') + (h₀ : TrExpr env Us Δ e₀ e₀') : + TrExpr env Us Δ (e.instantiate1' e₀) (e'.inst e₀') := + have ⟨_, h0⟩ := t₀.isType henv hΔ + have ⟨_, s1, _, h1⟩ := H + have ⟨_, s2, h2⟩ := h₀ + have h2' := h2.symm.of_l henv hΔ t₀ + have hΔΔ := VLCtx.IsDefEq.cons (.refl henv hΔ) (ofv := none) nofun (.vlam h0) + let ⟨_, s1'⟩ := s1.defeqDFC henv hΔΔ + let ⟨_, h1'⟩ := s1.uniq henv hΔΔ s1' + ⟨_, .inst henv h2'.hasType.2 s1' s2, _, + .instDF henv hΔ (h1'.symm.trans_l henv hΔΔ.wf.toCtx h1) h2'.symm⟩ + +variable! (henv : Ordered env) (h₀ : TrExprS env Us Δ₀ e₀ e₀') in +theorem TrExprS.instN_let_var (W : VLCtx.InstLet Δ₀ e₀' A₀ dk k Δ₁ Δ) + (H : Δ₁.find? v = some (e', A)) : + TrExprS env Us Δ (Expr.instantiate1' (VLCtx.varToExpr v) e₀ dk) e' := by + induction W generalizing v e' A with + | zero => + obtain (_|i)|fv := v <;> simp [VLCtx.varToExpr, Expr.instantiate1', Expr.liftLooseBVars_zero] + · cases H; simp [VLocalDecl.value]; exact h₀ + · simp [VLCtx.find?, VLCtx.next] at H + obtain ⟨e, A, H, rfl, rfl⟩ := H + simp [VLocalDecl.depth] + exact .bvar H + · simp [VLCtx.find?, VLCtx.next] at H + obtain ⟨e, A, H, rfl, rfl⟩ := H + simp [VLocalDecl.depth] + exact .fvar H + | @succ _ k _ _ d _ ih => + obtain (_|i)|fv := v <;> simp [VLCtx.varToExpr, Expr.instantiate1'] + · cases H + cases d <;> exact .bvar <| by simp [VLocalDecl.value]; rfl + · simp [VLCtx.find?, VLCtx.next] at H + obtain ⟨e, A, H, rfl, rfl⟩ := H + have := ih H; revert this + simp [VLCtx.varToExpr, Expr.instantiate1']; split <;> [skip; split] + · intro | .bvar h => ?_ + exact .bvar <| by + simp [VLCtx.find?, VLCtx.next] + refine ⟨_, _, h, ?_, rfl⟩ + cases d <;> simp [VLocalDecl.depth] + · intro H + have := Expr.liftLooseBVars_add ▸ H.weakBV henv (.skip d .refl) + cases d <;> simpa [VLocalDecl.depth] using this + · obtain _|i := i; · omega + intro | .bvar h => ?_ + exact .bvar <| by + simp [VLCtx.find?, VLCtx.next] + refine ⟨_, _, h, ?_, rfl⟩ + cases d <;> simp [VLocalDecl.depth] + · simp [VLCtx.find?, VLCtx.next] at H + obtain ⟨e, A, H, rfl, rfl⟩ := H + have .fvar h := ih H + exact .fvar <| by + simp [VLCtx.find?, VLCtx.next] + refine ⟨_, _, h, ?_, rfl⟩ + cases d <;> simp [VLocalDecl.depth] + +variable! (henv : Ordered env) (h₀ : TrExprS env Us Δ₀ e₀ e₀') in +theorem TrExprS.instN_let (W : VLCtx.InstLet Δ₀ e₀' A₀ dk k Δ₁ Δ) (H : TrExprS env Us Δ₁ e e') : + TrExprS env Us Δ (Expr.instantiate1' e e₀ dk) e' := by + induction H generalizing Δ dk k with + | bvar h1 | fvar h1 => exact instN_let_var henv h₀ W h1 + | sort h1 => exact .sort h1 + | const h1 h2 h3 => exact .const h1 h2 h3 + | app h1 h2 _ _ ih1 ih2 => + exact .app (W.toCtx ▸ h1) (W.toCtx ▸ h2) (ih1 W) (ih2 W) + | lam h1 _ _ ih1 ih2 => + exact .lam (W.toCtx ▸ h1) (ih1 W) (ih2 (W.succ (d := .vlam _))) + | forallE h1 h2 _ _ ih1 ih2 => + exact .forallE (W.toCtx ▸ h1) (W.toCtx ▸ h2) + (ih1 W) (ih2 (W.succ (d := .vlam _))) + | letE h1 _ _ _ ih1 ih2 ih3 => + exact .letE (W.toCtx ▸ h1) (ih1 W) (ih2 W) (ih3 (W.succ (d := .vlet ..))) + | lit h1 _ ih => + refine .lit h1 (Expr.instantiate1'_eq_self ?_ ▸ ih W :) + exact Closed.toConstructor.looseBVarRange_le + | mdata _ ih => exact .mdata (ih W) + | proj _ h2 ih => exact .proj (ih W) (W.toCtx ▸ h2) + +theorem TrExprS.inst_let {Δ : VLCtx} (henv : Ordered env) + (H : TrExprS env Us ((none, .vlet A₀ e₀') :: Δ) e e') + (h₀ : TrExprS env Us Δ e₀ e₀') : + TrExprS env Us Δ (e.instantiate1' e₀) e' := + h₀.instN_let henv .zero H + +theorem TrExpr.inst_let (henv : VEnv.WF env) (hΔ : VLCtx.WF env Us.length Δ) + (t₀ : env.HasType Us.length Δ.toCtx e₀' A₀) + (H : TrExpr env Us ((none, .vlet A₀ e₀') :: Δ) e e') + (h₀ : TrExpr env Us Δ e₀ e₀') : + TrExpr env Us Δ (e.instantiate1' e₀) e' := + have ⟨_, h0⟩ := t₀.isType henv hΔ + have ⟨_, s1, _, h1⟩ := H + have ⟨_, s2, h2⟩ := h₀ + have h2' := h2.symm.of_l henv hΔ t₀ + have hΔΔ := VLCtx.IsDefEq.cons (.refl henv hΔ) (ofv := none) nofun (.vlet h2' h0) + let ⟨_, s1'⟩ := s1.defeqDFC henv hΔΔ + let ⟨_, h1'⟩ := s1.uniq henv hΔΔ s1' + ⟨_, .inst_let henv s1' s2, _, h1'.symm.trans_l henv hΔ h1⟩ + +theorem ofLevel_mkLevelMax' + (h1 : VLevel.ofLevel Us u = some u') (h2 : VLevel.ofLevel Us v = some v') : + ∃ w, VLevel.ofLevel Us (mkLevelMax' u v) = some w ∧ w ≈ .max u' v' := by + let subsumes (u v : Level) : Bool := + if v.isExplicit && u.getOffset ≥ v.getOffset then true + else match u with + | Level.max u₁ u₂ => v == u₁ || v == u₂ + | _ => false + let mkLevelMaxCore (u v : Level) := + if u == v then u + else if u.isZero then v + else if v.isZero then u + else if subsumes u v then u + else if subsumes v u then v + else if u.getLevelOffset == v.getLevelOffset then + if u.getOffset ≥ v.getOffset then u else v + else + .max u v + change ∃ w, VLevel.ofLevel Us (mkLevelMaxCore u v) = some w ∧ w ≈ .max u' v' + have le {u v u' v'} (h : subsumes u v) + (hu : VLevel.ofLevel Us u = some u') + (hv : VLevel.ofLevel Us v = some v') : v'.LE u' := by + simp [subsumes] at h + obtain ⟨h1, h2⟩ | h := h + · clear subsumes mkLevelMaxCore + induction v generalizing u u' v' with simp [VLevel.ofLevel] at hv h2 ⊢ + | zero => subst v'; exact VLevel.zero_le + | succ _ ih => + obtain ⟨_, hv, rfl⟩ := hv + generalize eq : u.getOffset' = n at h2 + unfold Level.getOffset' at eq; split at eq <;> subst eq <;> [skip; cases h2] + simp [VLevel.ofLevel] at hu; obtain ⟨_, hu, rfl⟩ := hu + simp [Level.isExplicit] at h1 + exact VLevel.succ_le_succ (ih hu hv h1.2 (Nat.le_of_succ_le_succ h2)) + | _ => cases h1 + · split at h <;> [skip; cases h] + simp [VLevel.ofLevel] at hu; obtain ⟨_, hu1, _, hu2, rfl⟩ := hu + simp at h; obtain rfl | rfl := h + · cases hv.symm.trans hu1 + exact VLevel.le_max_left + · cases hv.symm.trans hu2 + exact VLevel.le_max_right + simp only [mkLevelMaxCore]; split + · simp_all; exact VLevel.max_self.symm + split + · let .zero := u; simp [VLevel.ofLevel] at h1; subst u' + exact ⟨_, h2, VLevel.zero_le.max_eq_right.symm⟩ + split + · let .zero := v; simp [VLevel.ofLevel] at h2; subst v' + exact ⟨_, h1, VLevel.zero_le.max_eq_left.symm⟩ + split + · exact ⟨_, h1, (le ‹_› h1 h2).max_eq_left.symm⟩ + split + · exact ⟨_, h2, (le ‹_› h2 h1).max_eq_right.symm⟩ + split + · rename_i h + simp at h ⊢ + let rec lem1 {v : Level} {u' v'} + (hu : VLevel.ofLevel Us v.getLevelOffset = some u') + (hv : VLevel.ofLevel Us v = some v') : u'.LE v' := by + unfold Level.getLevelOffset at hu; split at hu + · simp [VLevel.ofLevel] at hv; obtain ⟨_, hv, rfl⟩ := hv + exact VLevel.le_trans (lem1 hu hv) VLevel.le_succ + · cases hu.symm.trans hv; exact VLevel.le_refl _ + let rec lem2 {u v : Level} {u' v'} + (h1 : u.getLevelOffset = v.getLevelOffset) + (h2 : u.getOffset' ≤ v.getOffset') + (hu : VLevel.ofLevel Us u = some u') + (hv : VLevel.ofLevel Us v = some v') : u'.LE v' := by + revert h1 h2; unfold Level.getLevelOffset Level.getOffset' + split <;> simp <;> split <;> (try simp) + · simp [VLevel.ofLevel] at hu; obtain ⟨_, hu, rfl⟩ := hu + simp [VLevel.ofLevel] at hv; obtain ⟨_, hv, rfl⟩ := hv + exact (VLevel.succ_le_succ <| lem2 · · hu hv) + · rintro rfl; exact lem1 (v := .succ _) hu hv + · rintro rfl; cases hu.symm.trans hv; exact VLevel.le_refl _ + split <;> rename_i h3 + · exact ⟨_, h1, (lem2 h.symm h3 h2 h1).max_eq_left.symm⟩ + · exact ⟨_, h2, (lem2 h (Nat.le_of_not_le h3) h1 h2).max_eq_right.symm⟩ + simp [VLevel.ofLevel]; exact ⟨_, ⟨_, h1, _, h2, rfl⟩, rfl⟩ + +theorem ofLevel_isNeverZero (h : VLevel.ofLevel Us u = some u') (H : u.isNeverZero) : + u'.IsNeverZero := by + induction u generalizing u' with simp [Level.isNeverZero, VLevel.ofLevel] at H h <;> intro ls + | succ => + obtain ⟨_, h1, rfl⟩ := h + exact Nat.succ_ne_zero _ + | max _ _ ih1 ih2 => + obtain ⟨_, h1, _, h2, rfl⟩ := h + intro h + rw [VLevel.eval, ← Nat.le_zero, Nat.max_le] at h; simp at h + exact H.elim (ih1 h1 · _ h.1) (ih2 h2 · _ h.2) + | imax _ _ ih1 ih2 => + obtain ⟨_, h1, _, h2, rfl⟩ := h + simp [VLevel.eval, Lean.Nat.imax, ih2 h2 H ls] + +theorem ofLevel_isAlwaysZero (h : VLevel.ofLevel Us u = some u') (H : u.isAlwaysZero) : + u' ≈ .zero := by + induction u generalizing u' with + simp [Level.isAlwaysZero, VLevel.ofLevel] at H h <;> subst_vars <;> + refine VLevel.equiv_def.2 fun ls => ?_ + | zero => rfl + | max _ _ ih1 ih2 => + obtain ⟨_, h1, _, h2, rfl⟩ := h + simp [VLevel.eval, VLevel.equiv_def.1 (ih1 h1 H.1) ls, VLevel.equiv_def.1 (ih2 h2 H.2) ls] + | imax _ _ _ ih2 => + obtain ⟨_, _, _, h2, rfl⟩ := h + simp [VLevel.eval, Lean.Nat.imax, VLevel.equiv_def.1 (ih2 h2 H) ls] + +theorem ofLevel_mkLevelIMax' + (h1 : VLevel.ofLevel Us u = some u') (h2 : VLevel.ofLevel Us v = some v') : + ∃ w, VLevel.ofLevel Us (mkLevelIMax' u v) = some w ∧ w ≈ .imax u' v' := by + let mkLevelIMaxCore (u v : Level) := + if v.isNeverZero then mkLevelMax' u v + else if v.isZero then v + else if u.isZero then v + else if u == v then u + else .imax u v + change ∃ w, VLevel.ofLevel Us (mkLevelIMaxCore u v) = some w ∧ w ≈ .imax u' v' + simp only [mkLevelIMaxCore]; split + · have ⟨_, a1, a2⟩ := ofLevel_mkLevelMax' h1 h2 + exact ⟨_, a1, .trans a2 (ofLevel_isNeverZero h2 ‹_›).imax_eq_max.symm⟩ + split + · let .zero := v; simp [VLevel.ofLevel] at h2; subst v' + exact ⟨.zero, rfl, rfl⟩ + split + · let .zero := u; simp [VLevel.ofLevel] at h1; subst u' + exact ⟨_, h2, VLevel.zero_imax.symm⟩ + split + · simp_all; exact VLevel.imax_self.symm + simp [VLevel.ofLevel]; exact ⟨_, ⟨_, h1, _, h2, rfl⟩, rfl⟩ + +variable! {ls : List VLevel} (hls : ∀ l ∈ ls, l.WF U') in +theorem TrProj.instL (H : TrProj env U Γ s i e e') : + TrProj env U' (Γ.map (VExpr.instL ls)) s i + (e.instL ls) (e'.instL ls) := by + obtain ⟨view, levels, params, hname, hproj⟩ := H + exact ⟨view, levels.map (VLevel.inst ls), + params.map (VExpr.instL ls), hname, hproj.instL hls⟩ + +/-- The structural interface of Verify's projection translation. The bundle +keeps the seven laws available as one coherent capability while the named +theorems above remain the compatibility surface for existing callers. -/ +structure TrProj.StructuralLaws (env : VEnv) : Prop where + weakening : ∀ {U n Γ Γ' s i e e'}, + Ctx.Lift' n Γ Γ' → TrProj env U Γ s i e e' → + TrProj env U Γ' s i (e.lift' n) (e'.lift' n) + inverseWeakening : ∀ {U l Γ Γ' s i e e'}, + OnCtx Γ' (env.IsType U) → Ctx.Lift' l Γ Γ' → + TrProj env U Γ' s i (e.lift' l) e' → + ∃ result, TrProj env U Γ s i e result + contextDefEq : ∀ {U Γ₁ Γ₂ s i e₁ e₂ result}, + env.IsDefEqCtx U [] Γ₁ Γ₂ → env.IsDefEqU U Γ₁ e₁ e₂ → + TrProj env U Γ₁ s i e₁ result → + ∃ result', TrProj env U Γ₂ s i e₂ result' + wellFormed : ∀ {U Γ s i e result}, + TrProj env U Γ s i e result → VExpr.WF env U Γ e → + VExpr.WF env U Γ result + unique : ∀ {U Γ₁ Γ₂ s₁ s₂ i e₁ e₂ result₁ result₂}, + env.IsDefEqCtx U [] Γ₁ Γ₂ → + TrProj env U Γ₁ s₁ i e₁ result₁ → + TrProj env U Γ₂ s₂ i e₂ result₂ → + env.IsDefEqU U Γ₁ e₁ e₂ → + env.IsDefEqU U Γ₁ result₁ result₂ + termSubstitution : ∀ {U Γ₀ Γ₁ Γ s i e e' e₀ A₀ k}, + env.HasType U Γ₀ e₀ A₀ → Ctx.InstN Γ₀ e₀ A₀ k Γ₁ Γ → + TrProj env U Γ₁ s i e e' → + TrProj env U Γ s i (e.inst e₀ k) (e'.inst e₀ k) + universeInstantiation : ∀ {U U' Γ s i e e'} {ls : List VLevel}, + (∀ level ∈ ls, level.WF U') → U = ls.length → + TrProj env U Γ s i e e' → + TrProj env U' (Γ.map (VExpr.instL ls)) s i + (e.instL ls) (e'.instL ls) + +/-- Every well-formed environment supplies the complete projection structural +interface. -/ +theorem TrProj.structuralLaws (henv : VEnv.WF env) : + TrProj.StructuralLaws env where + weakening W H := H.weak' henv.ordered W + inverseWeakening hΓ' W H := H.weak'_inv henv hΓ' W + contextDefEq hΓ he H := H.defeqDFC henv hΓ he + wellFormed H he := H.wf he + unique hΓ H1 H2 he := H1.uniq henv hΓ H2 he + termSubstitution h₀ W H := H.instN henv.ordered h₀ W + universeInstantiation hls _ H := H.instL hls + +/-! +The guards below pin both the proved laws and the inherited Tier-R boundary. +In particular, they distinguish local proof closure from the remaining public +registered-head inversion dependency. +-/ + +#guard_named_axioms Ix.Theory.Named.TrProj.weak' [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TrProj.weak'_inv [propext, sorryAx, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TrProj.defeqDFC [ + propext, + sorryAx, + Classical.choice, + Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TrProj.wf [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TrProj.uniq [propext, sorryAx, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TrProj.instN [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TrProj.instL [propext, Quot.sound] + +#guard_named_axioms Ix.Theory.Named.TrProj.structuralLaws [ + propext, + sorryAx, + Classical.choice, + Quot.sound] + +section + +variable (henv : VEnv.WF env) {Us ps : List Name} {ls : List Level} {ls' : List VLevel} + (hΔ : VLCtx.WF env ls'.length Δ) + (Hls : ls.mapM (VLevel.ofLevel Us) = some ls') + (eq : ps.length = ls.length) + +include Hls eq + +section +variable (eqF : (fun x => ((List.idxOf? x ps).bind fun x => ls[x]?).getD (Level.param x)) = F) +include eqF + +attribute [-simp] Bool.forall_bool in +theorem substParams_wf (red) (H : VLevel.ofLevel ps u = some u') : + ∃ u₁, VLevel.ofLevel Us (u.substParams' F red) = some u₁ ∧ u₁ ≈ u'.inst ls' := by + induction u generalizing u' red with simp_all [VLevel.ofLevel, Level.substParams'] + | zero => subst u'; rfl + | succ _ ih => + obtain ⟨_, H, rfl⟩ := H + exact let ⟨_, h1, h2⟩ := ih _ H; ⟨_, ⟨_, h1, rfl⟩, VLevel.succ_congr h2⟩ + | max _ _ ih1 ih2 => + obtain ⟨_, H1, _, H2, rfl⟩ := H + generalize (_ && _) = red' + let ⟨_, a1, a2⟩ := ih1 (red := red') H1 + let ⟨_, b1, b2⟩ := ih2 (red := red') H2 + split + · have ⟨w, c1, c2⟩ := ofLevel_mkLevelMax' a1 b1 + exact ⟨_, c1, .trans c2 <| VLevel.max_congr a2 b2⟩ + · simp [VLevel.ofLevel] + exact ⟨_, ⟨_, a1, _, b1, rfl⟩, VLevel.max_congr a2 b2⟩ + | imax _ _ ih1 ih2 => + obtain ⟨_, H1, _, H2, rfl⟩ := H + generalize (_ && _) = red' + let ⟨_, a1, a2⟩ := ih1 (red := red') H1 + let ⟨_, b1, b2⟩ := ih2 (red := red') H2 + split + · have ⟨w, c1, c2⟩ := ofLevel_mkLevelIMax' a1 b1 + exact ⟨_, c1, .trans c2 <| VLevel.imax_congr a2 b2⟩ + · simp [VLevel.ofLevel] + exact ⟨_, ⟨_, a1, _, b1, rfl⟩, VLevel.imax_congr a2 b2⟩ + | param x => + obtain ⟨H, rfl⟩ := H; subst eqF; simp + have := List.idxOf_eq_getD_idxOf? x ps; unfold Option.getD at this; revert this + split <;> simp [*, Nat.ne_of_lt, VLevel.inst]; rintro rfl; clear ‹_› eq + generalize List.idxOf x ps = n at * + rw [List.mapM_eq_some] at Hls + induction Hls generalizing n with + | nil => cases H + | cons Hl _ ih => + obtain _|n := n <;> simp + · exact ⟨_, Hl, rfl⟩ + · exact ih _ (Nat.lt_of_succ_lt_succ H) + +theorem substParams_wf_list (red) {us us' : List _} (H : us.mapM (VLevel.ofLevel ps) = some us') : + ∃ us₁, (us.map (Level.substParams' F red)).mapM (VLevel.ofLevel Us) = some us₁ ∧ + List.Forall₂ (· ≈ ·) us₁ (us'.map (·.inst ls')) := by + induction us generalizing us' with simp_all + | cons u us ih => + obtain ⟨_, H1, _, H2, rfl⟩ := H + have ⟨_, h1, h2⟩ := ih H2 + have ⟨_, h3, h4⟩ := substParams_wf Hls eq eqF red H1 + refine ⟨_, ⟨_, h3, _, h1, rfl⟩, .cons h4 h2⟩ + +end + +include henv hΔ + +theorem TrExprS.instL (H : TrExprS env ps Δ e e') : + TrExpr env Us (Δ.instL ls') (e.instantiateLevelParams ps ls) (e'.instL ls') := by + simp [Expr.instantiateLevelParams_eq] + generalize (_ && _) = red, eqF : (fun x : Name => _) = F + have Hls' := VLevel.WF.of_mapM_ofLevel Hls + have eq' := eq.trans (List.mapM_eq_some.1 Hls).length_eq + induction H with + | bvar h1 => exact (bvar (VLCtx.find?_instL h1)).trExpr henv (hΔ.instL Hls') + | fvar h1 => exact (fvar (VLCtx.find?_instL h1)).trExpr henv (hΔ.instL Hls') + | sort h1 => + simp [Expr.instantiateLevelParamsCore'] + have ⟨_, a1, a2⟩ := substParams_wf Hls eq eqF red h1 + exact ⟨_, .sort a1, _, .sortDF (.of_ofLevel a1) (.inst Hls') a2⟩ + | const h1 h2 h3 => + have ⟨_, a1, a2⟩ := substParams_wf_list Hls eq eqF red h2 + refine ⟨_, .const h1 a1 (by simp [h3]), _, .constDF h1 (.of_mapM_ofLevel a1) ?_ ?_ a2⟩ + · simp; exact fun _ _ => .inst Hls' + · simp [← (List.mapM_eq_some.1 a1).length_eq, h3] + | app h1 h2 _ _ ih1 ih2 => + exact .app henv (hΔ.instL Hls') + (VLCtx.instL_toCtx _ ▸ h1.instL Hls') + (VLCtx.instL_toCtx _ ▸ h2.instL Hls') (ih1 hΔ) (ih2 hΔ) + | lam h1 h2 _ ih1 ih2 => + exact .lam henv (hΔ.instL Hls') + (VLCtx.instL_toCtx _ ▸ h1.instL Hls') (ih1 hΔ) (ih2 ⟨hΔ, nofun, eq' ▸ h1⟩) + | forallE h1 h2 _ _ ih1 ih2 => + exact .forallE henv (hΔ.instL Hls') + (VLCtx.instL_toCtx _ ▸ h1.instL Hls') + (VLCtx.instL_toCtx _ ▸ h2.instL Hls') (ih1 hΔ) (ih2 ⟨hΔ, nofun, eq' ▸ h1⟩) + | letE h1 _ _ _ ih1 ih2 ih3 => + exact .letE henv (hΔ.instL Hls') + (VLCtx.instL_toCtx _ ▸ h1.instL Hls') (ih1 hΔ) (ih2 hΔ) (ih3 ⟨hΔ, nofun, eq' ▸ h1⟩) + | lit h1 _ ih => + refine .lit h1 (Expr.instantiateLevelParamsCore_eq_self ?_ ▸ ih hΔ :) + exact Literal.toConstructor_hasLevelParam + | mdata _ ih => exact .mdata (ih hΔ) + | proj _ h2 ih => + exact .proj henv (hΔ.instL Hls') (ih hΔ) + (VLCtx.instL_toCtx _ ▸ h2.instL Hls') + +theorem TrExpr.instL (H : TrExpr env ps Δ e e') : + TrExpr env Us (Δ.instL ls') (e.instantiateLevelParams ps ls) (e'.instL ls') := + let ⟨_, s1, h1⟩ := H + have Hls' := .of_mapM_ofLevel Hls + (s1.instL henv hΔ Hls eq).defeq henv (hΔ.instL Hls') (VLCtx.instL_toCtx _ ▸ h1.instL Hls') + +end + +theorem TrExprS.abstract (W : VLCtx.Abstract Δ₀ v₀ d₀ dk k Δ₁ Δ) (H : TrExprS env Us Δ₁ e e') : + TrExprS env Us Δ (e.abstract1 v₀ dk) e' := by + induction H generalizing dk k Δ with + | bvar h1 => + exact .bvar <| (W.find? (by nofun)).trans <| by + simp; split <;> [skip; rw [if_neg (by omega), if_neg (by omega)]] <;> exact h1 + | @fvar _ _ _ fv h1 => + if h : fv = v₀ then + rw [h, W.find?_self] at h1; cases h1 + rw [Expr.abstract1, if_pos (by simp [h])] + exact .bvar <| (W.find? (by nofun)).trans (by simpa using W.find?_self) + else + have := W.find? (v := .inr fv) (by rintro ⟨⟩; trivial) + simp at this + rw [Expr.abstract1, if_neg] + · exact .fvar (this.trans h1) + · simp; rintro rfl; trivial + | sort h1 => exact .sort h1 + | const h1 h2 h3 => exact .const h1 h2 h3 + | app h1 h2 _ _ ih1 ih2 => exact .app (W.toCtx ▸ h1) (W.toCtx ▸ h2) (ih1 W) (ih2 W) + | lam h1 _ _ ih1 ih2 => exact .lam (W.toCtx ▸ h1) (ih1 W) (ih2 W.succ) + | forallE h1 h2 _ _ ih1 ih2 => + exact .forallE (W.toCtx ▸ h1) (W.toCtx ▸ h2) (ih1 W) (ih2 W.succ) + | letE h1 _ _ _ ih1 ih2 ih3 => + exact .letE (W.toCtx ▸ h1) (ih1 W) (ih2 W) (ih3 W.succ) + | lit h1 _ ih => + exact .lit h1 (FVarsIn.toConstructor.abstract_eq_self .toConstructor ▸ ih W) + | mdata _ ih => exact .mdata (ih W) + | proj _ h2 ih => exact .proj (ih W) (W.toCtx ▸ h2) + +theorem TrExpr.abstract (W : VLCtx.Abstract Δ₀ v₀ d₀ dk k Δ₁ Δ) (H : TrExpr env Us Δ₁ e e') : + TrExpr env Us Δ (e.abstract1 v₀ dk) e' := + let ⟨_, s, h⟩ := H; ⟨_, s.abstract W, W.toCtx ▸ h⟩ + +def TrExprS.IsUnique : Expr → Prop + | .bvar _ + | .fvar _ + | .sort _ + | .const .. + | .mvar .. + | .lit _ => True + | .app f a => IsUnique f ∧ IsUnique a + | .lam _ t b _ => IsUnique t ∧ IsUnique b + | .forallE _ t b _ => IsUnique t ∧ IsUnique b + | .letE _ _ v b _ => IsUnique v ∧ IsUnique b + | .mdata _ e => IsUnique e + | .proj .. => False + +theorem TrExprS.IsUnique.natLitToConstructor : ∀ {n : Nat}, IsUnique (.natLitToConstructor n) + | 0 => ⟨⟩ + | _+1 => ⟨⟨⟩, ⟨⟩⟩ + +theorem TrExprS.IsUnique.strLitToConstructor {s : String} : IsUnique (.strLitToConstructor s) := by + refine ⟨⟨⟩, ?_⟩ + induction s.toList with simp + | nil => exact ⟨⟨⟩, ⟨⟩⟩ + | cons _ _ ih => exact ⟨⟨⟨⟨⟩, ⟨⟩⟩, ⟨⟨⟩, ⟨⟩⟩⟩, ih⟩ + +theorem TrExprS.IsUnique.toConstructor : ∀ {l : Literal}, IsUnique l.toConstructor + | .natVal _ => .natLitToConstructor + | .strVal _ => .strLitToConstructor + +inductive TrExprS.IsUniqueDecl : VLocalDecl → VLocalDecl → Prop + | vlam : IsUniqueDecl (.vlam ty) (.vlam ty') + | vlet : IsUniqueDecl (.vlet ty val) (.vlet ty' val) + +inductive TrExprS.IsUniqueCtx : VLCtx → VLCtx → Prop + | base : IsUniqueCtx Δ Δ + | cons : IsUniqueCtx Δ₁ Δ₂ → IsUniqueDecl d₁ d₂ → IsUniqueCtx ((ofv, d₁) :: Δ₁) ((ofv, d₂) :: Δ₂) + +theorem TrExprS.IsUniqueCtx.find?_uniq (hΔ : IsUniqueCtx Δ₁ Δ₂) + (H1 : Δ₁.find? v = some (e₁, A₁)) (H2 : Δ₂.find? v = some (e₂, A₂)) : e₁ = e₂ := by + induction hΔ generalizing v e₁ e₂ A₁ A₂ with + | base => cases H1.symm.trans H2; rfl + | @cons _ _ _ _ ofv _ hd ih => + revert H1 H2; simp [VLCtx.find?]; split + · rintro ⟨⟩ ⟨⟩; cases hd <;> rfl + · simp; rintro _ _ h1 rfl rfl _ _ h2 rfl rfl + congr 1 + · cases hd <;> rfl + · exact ih h1 h2 + +theorem TrExprS.unique' (hΔ : IsUniqueCtx Δ₁ Δ₂) (H : IsUnique e) + (H1 : TrExprS env Us Δ₁ e e₁) (H2 : TrExprS env Us Δ₂ e e₂) : e₁ = e₂ := by + induction H1 generalizing Δ₂ e₂ with cases H2 + | bvar => exact hΔ.find?_uniq ‹_› ‹_› + | fvar => exact hΔ.find?_uniq ‹_› ‹_› + | sort h1 + | const _ h1 => cases h1.symm.trans ‹_›; rfl + | app _ _ _ _ ih1 ih2 => cases ih1 hΔ H.1 ‹_›; cases ih2 hΔ H.2 ‹_›; rfl + | lam _ _ _ ih1 ih2 + | forallE _ _ _ _ ih1 ih2 => cases ih1 hΔ H.1 ‹_›; cases ih2 (hΔ.cons .vlam) H.2 ‹_›; rfl + | letE _ _ _ _ _ ih1 ih2 => cases ih1 hΔ H.1 ‹_›; cases ih2 (hΔ.cons .vlet) H.2 ‹_›; rfl + | lit _ _ ih => exact ih hΔ .toConstructor ‹_› + | mdata _ ih => exact ih hΔ H ‹_› + | proj => cases H + +theorem TrExprS.unique (H : IsUnique e) + (H1 : TrExprS env Us Δ e e₁) (H2 : TrExprS env Us Δ e e₂) : e₁ = e₂ := H1.unique' .base H H2 + +/-- A successful lookup transfers along value-preserving context alignment: +the found value is identical and only the (discarded) type component may +differ. -/ +theorem TrExprS.IsUniqueCtx.find?_transfer (hΔ : IsUniqueCtx Δ₁ Δ₂) + (H : Δ₁.find? v = some (e, A)) : ∃ A₂, Δ₂.find? v = some (e, A₂) := by + induction hΔ generalizing v e A with + | base => exact ⟨A, H⟩ + | @cons Δ₁' Δ₂' d₁ d₂ ofv _ hd ih => + revert H; simp only [VLCtx.find?]; split + next heq => + simp only [Option.some.injEq, Prod.mk.injEq] + rintro ⟨rfl, rfl⟩ + cases hd <;> exact ⟨_, rfl, rfl⟩ + next v' heq => + rintro h + simp only [Bind.bind, Option.bind_eq_some_iff] at h + obtain ⟨⟨e₁, A₁⟩, h1, h2⟩ := h + simp only [Option.some.injEq, Prod.mk.injEq] at h2 + obtain ⟨rfl, rfl⟩ := h2 + obtain ⟨A₂, h₂⟩ := ih h1 + have hdep : d₁.depth = d₂.depth := by cases hd <;> rfl + refine ⟨VExpr.liftN d₂.depth A₂, ?_⟩ + simp only [Bind.bind, Option.bind_eq_some_iff] + exact ⟨(e₁, A₂), h₂, by rw [hdep]⟩ + +/-- Every strict translation of an unfolded natural-number literal is the +canonical numeral: the constructor spine pins the Theory value +syntactically. -/ +theorem TrExprS.natLitToConstructor_eq : + ∀ {n : Nat} {w}, TrExprS env Us Δ (Expr.natLitToConstructor n) w → + w = VExpr.natLit n + | 0, w, h => by + have h : TrExprS env Us Δ (.const ``Nat.zero []) w := h + cases h with + | const h1 h2 h3 => + obtain rfl : _ = ([] : List VLevel) := by simpa using h2.symm + rfl + | n+1, w, h => by + have h : TrExprS env Us Δ (.app (.const ``Nat.succ []) (.lit (.natVal n))) w := h + cases h with + | app h1 h2 hf ha => + cases hf with + | const hf1 hf2 hf3 => + obtain rfl : _ = ([] : List VLevel) := by simpa using hf2.symm + cases ha with + | lit ha1 ha2 => + cases natLitToConstructor_eq ha2 + rfl + +/-- Every strict translation of an unfolded character-list literal is the +canonical Theory list. -/ +theorem TrExprS.strLitToConstructor_chars_eq : + ∀ {cs : List Char} {w}, + TrExprS env Us Δ + (cs.foldr (init := .app (.const ``List.nil [.zero]) (.const ``Char [])) + fun c e => + .app (.app (.app (.const ``List.cons [.zero]) (.const ``Char [])) + (.app (.const ``Char.ofNat []) (.lit (.natVal c.toNat)))) e) w → + w = VExpr.listCharLit cs + | [], w, h => by + cases h with + | app h1 h2 hf ha => + cases hf with + | const hf1 hf2 hf3 => + simp [VLevel.ofLevel] at hf2 + obtain rfl := hf2 + cases ha with + | const ha1 ha2 ha3 => + obtain rfl : _ = ([] : List VLevel) := by simpa using ha2.symm + rfl + | c :: cs, w, h => by + cases h with + | app h1 h2 hf ha => + cases strLitToConstructor_chars_eq ha + cases hf with + | app hg1 hg2 hgf hga => + cases hgf with + | app hh1 hh2 hhf hha => + cases hhf with + | const hi1 hi2 hi3 => + simp [VLevel.ofLevel] at hi2 + obtain rfl := hi2 + cases hha with + | const hj1 hj2 hj3 => + obtain rfl : _ = ([] : List VLevel) := by simpa using hj2.symm + cases hga with + | app hk1 hk2 hkf hka => + cases hkf with + | const hl1 hl2 hl3 => + obtain rfl : _ = ([] : List VLevel) := by simpa using hl2.symm + cases hka with + | lit hm1 hm2 => + cases natLitToConstructor_eq hm2 + rfl + +/-- Every strict translation of a literal's constructor unfolding is the +canonical `VExpr.trLiteral` value. -/ +theorem TrExprS.toConstructor_eq {l : Literal} {w} + (h : TrExprS env Us Δ l.toConstructor w) : w = VExpr.trLiteral l := by + match l with + | .natVal n => exact natLitToConstructor_eq h + | .strVal s => + have h : TrExprS env Us Δ (.app (.const ``String.ofList []) + (s.toList.foldr (init := .app (.const ``List.nil [.zero]) (.const ``Char [])) + fun c e => + .app (.app (.app (.const ``List.cons [.zero]) (.const ``Char [])) + (.app (.const ``Char.ofNat []) (.lit (.natVal c.toNat)))) e)) w := h + cases h with + | app h1 h2 hf ha => + cases strLitToConstructor_chars_eq ha + cases hf with + | const hf1 hf2 hf3 => + obtain rfl : _ = ([] : List VLevel) := by simpa using hf2.symm + rfl + +/-- The Verify traversal of `Literal.toConstructor` and the direct Theory +encoding form one ready, well-formed literal value. -/ +theorem TrExprS.toConstructor_ready {l : Literal} {w} + (hready : env.PreludeReady) (hcontains : env.ContainsLits l) + (h : TrExprS env Us Δ l.toConstructor w) : + w = VExpr.trLiteral l ∧ VExpr.WF env U [] w := by + have heq := h.toConstructor_eq + refine ⟨heq, ?_⟩ + rw [heq] + exact hready.trLiteral_wf l hcontains + +/-- The deterministic translator agrees with every strict-translation +derivation over any value-preserving context alignment: on the `IsUnique` +fragment, `trExprS?` computes exactly the derivation's Theory value. This +is the replay engine for choice-free semantic packaging — a `Nonempty` +translation witness plus this agreement pins the computed value. -/ +theorem TrExprS.trExprS?_eq' (hΔ : IsUniqueCtx Δ₁ Δ₂) + (H : TrExprS env Us Δ₁ e e') (hu : IsUnique e) : + trExprS? Us Δ₂ e = some e' := by + induction H generalizing Δ₂ with + | bvar h1 => + obtain ⟨A₂, h2⟩ := hΔ.find?_transfer h1 + simp [trExprS?, h2] + | fvar h1 => + obtain ⟨A₂, h2⟩ := hΔ.find?_transfer h1 + simp [trExprS?, h2] + | sort h1 => simp [trExprS?, h1] + | const h1 h2 h3 => simp [trExprS?, h2] + | app h1 h2 _ _ ih1 ih2 => + simp [trExprS?, ih1 hΔ hu.1, ih2 hΔ hu.2] + | lam h1 _ _ ih1 ih2 => + simp [trExprS?, ih1 hΔ hu.1, ih2 (hΔ.cons .vlam) hu.2] + | forallE h1 h2 _ _ ih1 ih2 => + simp [trExprS?, ih1 hΔ hu.1, ih2 (hΔ.cons .vlam) hu.2] + | letE h1 _ _ _ ih1 ih2 ih3 => + simp [trExprS?, ih2 hΔ hu.1, ih3 (hΔ.cons .vlet) hu.2] + | lit h1 h2 ih => + cases h2.toConstructor_eq + simp [trExprS?] + | mdata _ ih => simpa [trExprS?] using ih hΔ hu + | proj h1 h2 => cases hu + +/-- Deterministic-translator agreement in a fixed context. -/ +theorem TrExprS.trExprS?_eq (H : TrExprS env Us Δ e e') (hu : IsUnique e) : + trExprS? Us Δ e = some e' := + H.trExprS?_eq' .base hu + +/-- Executable totality on the unique fragment: any translation witness +guarantees the deterministic translator succeeds. -/ +theorem TrExprS.trExprS?_isSome (hex : ∃ e', TrExprS env Us Δ e e') + (hu : IsUnique e) : (trExprS? Us Δ e).isSome := by + obtain ⟨e', H⟩ := hex + simp [H.trExprS?_eq hu] + +/-- Replay transfer: an existential translation witness holds of the computed +translation itself. Choice-free packagers pin their Theory data with this: +compute by `trExprS?`, then transfer the `Nonempty`-level witness onto the +computed value. -/ +theorem TrExprS.of_trExprS?_eq (hex : ∃ e', TrExprS env Us Δ e e') + (hu : IsUnique e) (h : trExprS? Us Δ e = some v) : + TrExprS env Us Δ e v := by + obtain ⟨e', H⟩ := hex + cases Option.some.inj ((H.trExprS?_eq hu).symm.trans h) + exact H + +theorem TrExprS.boolFalse (henv : env.HasPrimitives) (H : env.contains ``Bool) : + TrExprS env Us Δ (toExpr false) .boolFalse ∧ + env.HasType Us.length Δ.toCtx .boolFalse .bool := by + let ⟨⟨_, H⟩, _⟩ := henv.bool H + cases henv.boolFalse H + exact ⟨.const H rfl rfl, .const H nofun rfl⟩ + +theorem TrExprS.boolTrue (henv : env.HasPrimitives) (H : env.contains ``Bool) : + TrExprS env Us Δ (toExpr true) .boolTrue ∧ + env.HasType Us.length Δ.toCtx .boolTrue .bool := by + let ⟨_, _, H⟩ := henv.bool H + cases henv.boolTrue H + exact ⟨.const H rfl rfl, .const H nofun rfl⟩ + +theorem TrExprS.boolLit (henv : env.HasPrimitives) (H : env.contains ``Bool) (b : Bool) : + TrExprS env Us Δ (toExpr b) (.boolLit b) ∧ + env.HasType Us.length Δ.toCtx (.boolLit b) .bool := by + match b with + | false => exact TrExprS.boolFalse henv H + | true => exact TrExprS.boolTrue henv H + +theorem FVarsIn.boolLit {b : Bool} : FVarsIn P (toExpr b) := by cases b <;> exact nofun + +theorem TrExprS.lit_has_type (H : TrExprS env Us Δ (.lit l) e') : env.ContainsLits l := + let .lit H _ := H; H + +theorem TrExprS.nat_of_natZero (wf : env.Ordered) (henv : env.HasPrimitives) + (H : TrExprS env Us Δ .natZero e') : env.contains ``Nat := by + let .const H .. := H + have ⟨_, H⟩ := henv.natZero H ▸ wf.constWF H + have ⟨_, H, _⟩ := H.const_inv wf (by trivial) + exact ⟨_, H⟩ + +theorem TrExprS.natZero (henv : env.HasPrimitives) (H : env.contains ``Nat) : + TrExprS env Us Δ .natZero .natZero ∧ env.HasType Us.length Δ.toCtx .natZero .nat := by + let ⟨⟨_, H⟩, _⟩ := henv.nat H + cases henv.natZero H + exact ⟨.const H rfl rfl, .const H nofun rfl⟩ + +theorem TrExprS.natSucc (henv : env.HasPrimitives) (H : env.contains ``Nat) : + TrExprS env Us Δ .natSucc .natSucc ∧ + env.HasType Us.length Δ.toCtx .natSucc (.forallE .nat .nat) := by + let ⟨_, _, H⟩ := henv.nat H + cases henv.natSucc H + exact ⟨.const H rfl rfl, .const H nofun rfl⟩ + +theorem TrExprS.natLit (henv : env.HasPrimitives) (H : env.contains ``Nat) (n) : + TrExprS env Us Δ (.lit (.natVal n)) (.natLit n) ∧ + env.HasType Us.length Δ.toCtx (.natLit n) .nat := by + induction n with + | zero => exact let ⟨h1, h2⟩ := natZero henv H; ⟨.lit H h1, h2⟩ + | succ n ih => exact let ⟨h1, h2⟩ := natSucc henv H; ⟨.lit H (.app h2 ih.2 h1 ih.1), .app h2 ih.2⟩ + +theorem TrExprS.stringOfList (henv : env.HasPrimitives) (H : env.contains ``String.ofList) : + TrExprS env Us Δ (.const ``String.ofList []) .stringOfList ∧ + env.HasType Us.length Δ.toCtx .stringOfList (.forallE .listChar .string) := by + let ⟨_, H⟩ := H + cases (henv.stringOfList H).1 + exact ⟨.const H rfl rfl, .const H nofun rfl⟩ + +theorem TrExprS.charOfNat (henv : env.HasPrimitives) (H : env.contains ``Char.ofNat) : + TrExprS env Us Δ (.const ``Char.ofNat []) .charOfNat ∧ + env.HasType Us.length Δ.toCtx .charOfNat (.forallE .nat .char) := by + let ⟨_, H⟩ := H + cases henv.charOfNat H + exact ⟨.const H rfl rfl, .const H nofun rfl⟩ + +theorem TrExprS.listChar (wf : env.Ordered) (henv : env.HasPrimitives) + (H : env.contains ``String.ofList) : + TrExprS env Us Δ (.app (.const ``List [.zero]) (.const ``Char [])) .listChar ∧ + env.IsType Us.length Δ.toCtx .listChar := by + let ⟨_, H⟩ := H + let ⟨_, H, _⟩ := henv.stringOfList H + let ⟨_, H⟩ := H.isType wf (by trivial) + refine ⟨?_, _, (H.instL (ls := []) nofun).weak0 wf⟩ + let ⟨_, _, A, B⟩ := H.app_inv wf trivial + let ⟨_, A1, _, A3⟩ := A.const_inv wf trivial + let ⟨_, B1, _, B3⟩ := B.const_inv wf trivial + exact .app ((A.instL (ls := []) nofun).weak0 wf) ((B.instL (ls := []) nofun).weak0 wf) + (.const A1 rfl A3) (.const B1 rfl B3) + +theorem TrExprS.listCharNil (wf : env.Ordered) (henv : env.HasPrimitives) + (H : env.contains ``String.ofList) : + TrExprS env Us Δ (.app (.const ``List.nil [.zero]) (.const ``Char [])) .listCharNil ∧ + env.HasType Us.length Δ.toCtx .listCharNil .listChar := by + let ⟨_, H⟩ := H + let ⟨_, H, _⟩ := henv.stringOfList H + refine ⟨?_, (H.instL (ls := []) nofun).weak0 wf⟩ + let ⟨_, _, A, B⟩ := H.app_inv wf trivial + let ⟨_, A1, _, A3⟩ := A.const_inv wf trivial + let ⟨_, B1, _, B3⟩ := B.const_inv wf trivial + exact .app ((A.instL (ls := []) nofun).weak0 wf) ((B.instL (ls := []) nofun).weak0 wf) + (.const A1 rfl A3) (.const B1 rfl B3) + +theorem TrExprS.listCharCons (wf : env.Ordered) (henv : env.HasPrimitives) + (H : env.contains ``String.ofList) : + TrExprS env Us Δ (.app (.const ``List.cons [.zero]) (.const ``Char [])) .listCharCons ∧ + env.HasType Us.length Δ.toCtx .listCharCons + (.forallE .char <| .forallE .listChar .listChar) := by + let ⟨_, H⟩ := H + let ⟨_, _, H⟩ := henv.stringOfList H + refine ⟨?_, (H.instL (ls := []) nofun).weak0 wf⟩ + let ⟨_, _, A, B⟩ := H.app_inv wf trivial + let ⟨_, A1, _, A3⟩ := A.const_inv wf trivial + let ⟨_, B1, _, B3⟩ := B.const_inv wf trivial + exact .app ((A.instL (ls := []) nofun).weak0 wf) ((B.instL (ls := []) nofun).weak0 wf) + (.const A1 rfl A3) (.const B1 rfl B3) + +theorem TrExprS.listCharLit (wf : env.Ordered) (henv : env.HasPrimitives) + (H : env.ContainsLits (.strVal l)) (s : List Char) : + TrExprS env Us Δ (s.foldr + (init := .app (.const ``List.nil [.zero]) (.const ``Char [])) + (fun c e => .app (.app + (.app (.const ``List.cons [.zero]) (.const ``Char [])) + (.app (.const ``Char.ofNat []) (.lit (.natVal c.toNat)))) e)) (.listCharLit s) ∧ + env.HasType Us.length Δ.toCtx (.listCharLit s) .listChar := by + induction s with + | nil => exact TrExprS.listCharNil wf henv H.2 + | cons x _ ih => + have a := TrExprS.listCharCons wf henv H.2 (Us := Us) (Δ := Δ) + have b := TrExprS.charOfNat henv H.1 (Us := Us) (Δ := Δ) + have c := TrExprS.natLit henv (henv.nat_of_charOfNat wf H.1) (Us := Us) (Δ := Δ) (n := x.toNat) + have d1 := b.1.app b.2 c.2 c.1; have d2 := b.2.app c.2 + have e1 := a.1.app a.2 d2 d1; have e2 := a.2.app d2 + exact ⟨e1.app e2 ih.2 ih.1, e2.app ih.2⟩ + +theorem TrExprS.trLiteral (wf : env.Ordered) (henv : env.HasPrimitives) + (l) (H : env.ContainsLits l) : + TrExprS env Us Δ (.lit l) (.trLiteral l) ∧ + env.HasType Us.length Δ.toCtx (.trLiteral l) (.const l.typeName []) := by + match l with + | .natVal n => exact TrExprS.natLit henv H _ + | .strVal s => + have a := TrExprS.stringOfList henv H.2 (Us := Us) (Δ := Δ) + have b := TrExprS.listCharLit wf henv H (Us := Us) (Δ := Δ) s.toList + exact ⟨.lit H (.app a.2 b.2 a.1 (String.foldr_eq .. ▸ b.1)), a.2.app b.2⟩ + +def VLCtx.Closed : VLCtx → Prop + | [] => True + | (none, _) :: _ => False + | (some _, d) :: (Δ : VLCtx) => Δ.Closed ∧ d.ClosedN Δ.bvars + +def IsFVarUpSet (P : FVarId → Prop) : VLCtx → Prop + | [] => True + | (none, _) :: Δ => IsFVarUpSet P Δ + | (some (fv, deps), _) :: (Δ : VLCtx) => + IsFVarUpSet P Δ ∧ + (P fv → ∀ fv' ∈ deps, P fv') + +theorem IsFVarUpSet.congr : ∀ {Δ}, VLCtx.FVWF Δ → + (H : ∀ fv ∈ VLCtx.fvars Δ, P fv ↔ Q fv) → IsFVarUpSet P Δ ↔ IsFVarUpSet Q Δ + | [], _, _ => .rfl + | (none, _) :: Δ, ⟨h1, _⟩, H => congr (Δ := Δ) h1 H + | (some (fv, deps), d) :: (Δ : VLCtx), ⟨h1, h2⟩, H => by + refine and_congr (congr h1 fun fv h => H _ (.tail _ h)) (imp_congr (H _ (.head _)) ?_) + refine forall_congr' fun fv => imp_congr_right fun h => ?_ + exact H _ (by simp [(h2 _ _ rfl).2 h]) + +theorem IsFVarUpSet.and_fvars (H : VLCtx.FVWF Δ) : + IsFVarUpSet P Δ ↔ IsFVarUpSet (fun fv => P fv ∧ fv ∈ Δ.fvars) Δ := + IsFVarUpSet.congr H fun _ h => (and_iff_left h).symm + +theorem IsFVarUpSet.trivial : ∀ {Δ}, IsFVarUpSet (fun _ => True) Δ + | [] => ⟨⟩ + | (none, _) :: Δ => trivial (Δ := Δ) + | (some _, _) :: _ => ⟨trivial, fun _ _ _ => ⟨⟩⟩ + +theorem IsFVarUpSet.fvars (H : VLCtx.FVWF Δ) : IsFVarUpSet (· ∈ Δ.fvars) Δ := + (IsFVarUpSet.congr H fun _ => iff_true_intro).2 trivial + +def AllAbove (Δ : VLCtx) (P : FVarId → Prop) (fv : FVarId) : Prop := fv ∈ Δ.fvars → P fv + +theorem AllAbove.wf (H : Δ.FVWF) : IsFVarUpSet (AllAbove Δ P) Δ ↔ IsFVarUpSet P Δ := + IsFVarUpSet.congr H fun _ h => by simp [h, AllAbove] + +def FVarsBelow (Δ e e') := ∀ P, IsFVarUpSet P Δ → FVarsIn P e → FVarsIn P e' + +theorem FVarsBelow.rfl : FVarsBelow Δ e e := fun _ _ => id + +theorem FVarsBelow.trans (H1 : FVarsBelow Δ e₁ e₂) (H2 : FVarsBelow Δ e₂ e₃) : + FVarsBelow Δ e₁ e₃ := fun _ h => H2 _ h ∘ H1 _ h + +def TrTyping (env : VEnv) (Us : List Name) (Δ : VLCtx) (e A : Expr) (e' A' : VExpr) : Prop := + FVarsBelow Δ e A ∧ TrExprS env Us Δ e e' ∧ TrExprS env Us Δ A A' ∧ + env.HasType Us.length Δ.toCtx e' A' + +theorem FVarsIn.mkAppRevList : + FVarsIn P (e.mkAppRevList es) ↔ FVarsIn P e ∧ ∀ a ∈ es, FVarsIn P a := by + induction es <;> simp [FVarsIn, and_comm, and_left_comm, *] + +theorem FVarsIn.mkAppList : + FVarsIn P (e.mkAppList es) ↔ FVarsIn P e ∧ ∀ a ∈ es, FVarsIn P a := by + simp [← Expr.mkAppRevList_reverse, FVarsIn.mkAppRevList] + +theorem FVarsBelow.mkAppList (H : FVarsBelow Δ e₁ e₂) : + FVarsBelow Δ (e₁.mkAppList es) (e₂.mkAppList es) := by + simp [FVarsBelow, FVarsIn.mkAppList] at H ⊢; grind + +theorem FVarsBelow.mkAppRevList (H : FVarsBelow Δ e₁ e₂) : + FVarsBelow Δ (e₁.mkAppRevList es) (e₂.mkAppRevList es) := by + simpa [← Expr.mkAppList_reverse] using H.mkAppList + +inductive LambdaBodyN : Nat → Expr → Expr → Prop + | zero : LambdaBodyN 0 e e + | succ : LambdaBodyN n body e → LambdaBodyN (n+1) (.lam i ty body bi) e + +theorem LambdaBodyN.closed (H : LambdaBodyN n e1 e2) : e1.Closed k → e2.Closed (k + n) := by + induction H generalizing k with + | zero => exact id + | succ _ ih => exact fun h => (Nat.add_right_comm .. ▸ ih h.2 :) + +theorem LambdaBodyN.add (H1 : LambdaBodyN m e1 e2) (H2 : LambdaBodyN n e2 e3) : + LambdaBodyN (n + m) e1 e3 := by + induction H1 with + | zero => exact H2 + | succ _ ih => exact .succ (ih H2) + +theorem LambdaBodyN.instantiateList (H1 : LambdaBodyN n e1 e2) : + LambdaBodyN n (e1.instantiateList es k) (e2.instantiateList es (k+n)) := by + induction H1 generalizing k with + | zero => exact .zero + | succ _ ih => + rw [Expr.instantiateList_lam] + refine .succ ?_ + conv => arg 3; rw [← Nat.add_assoc, Nat.add_right_comm] + exact ih + +theorem LambdaBodyN.instantiateRevList (H1 : LambdaBodyN n e1 e2) : + LambdaBodyN n (e1.instantiateRevList es k) (e2.instantiateRevList es (k+n)) := by + simp only [← Expr.instantiateList_reverse]; exact H1.instantiateList + +inductive BetaReduce : Expr → Expr → Prop + | refl : BetaReduce e e + | trans : BetaReduce e₁ e₂ → BetaReduce e₂ e₃ → BetaReduce e₁ e₃ + | app : BetaReduce f f' → BetaReduce (.app f a) (.app f' a) + | beta : e.looseBVarRange' = 0 → BetaReduce (.app (.lam i ty body bi) e) (body.instantiate1' e) + +theorem BetaReduce.mkAppRevList (H : BetaReduce f f') : + BetaReduce (f.mkAppRevList es) (f'.mkAppRevList es) := by + induction es with + | nil => exact H + | cons _ _ ih => exact .app ih + +theorem BetaReduce.mkAppList (H : BetaReduce f f') : + BetaReduce (f.mkAppList es) (f'.mkAppList es) := by + induction es generalizing f f' with + | nil => exact H + | cons _ _ ih => exact ih (.app H) + +theorem BetaReduce.instantiateList (H : BetaReduce f f') : + BetaReduce (f.instantiateList es) (f'.instantiateList es) := by + induction H with + | refl => exact .refl + | trans _ _ ih1 ih2 => exact ih1.trans ih2 + | app _ ih => simp only [Expr.instantiateList_app]; exact .app ih + | beta h => + simp only [Expr.instantiateList_app, Expr.instantiateList_lam] + rw [← Expr.instantiateList_instantiate1_comm h] + refine' cast _ (BetaReduce.beta _); congr 2 + · rw [Expr.instantiateList_eq_self h] + · rwa [Expr.instantiateList_eq_self h] + +theorem BetaReduce.instantiateRevList (H : BetaReduce f f') : + BetaReduce (f.instantiateRevList es) (f'.instantiateRevList es) := by + simp only [← Expr.instantiateList_reverse]; exact H.instantiateList + +theorem FVarsBelow.betaReduce (H : BetaReduce e e') : FVarsBelow Δ e e' := by + intro P hP he + induction H with + | refl => exact he + | trans _ _ ih1 ih2 => exact ih2 (ih1 he) + | app _ ih => exact ⟨ih he.1, he.2⟩ + | beta => exact he.1.2.instantiate1 he.2 + +theorem BetaReduce.inst_reduce {l₁ : List Expr} {fn e₀ : Expr} + (h : ∀ x ∈ l₁, x.Closed) (l₂) + (h1 : LambdaBodyN l₁.length e₀ fn) + (hr : fn.instantiateList (l₁.reverse ++ l₂) = r) : + BetaReduce ((e₀.instantiateList l₂).mkAppList l₁) r := by + subst r + induction l₁ generalizing e₀ fn l₂ with + | nil => let .zero := h1; exact .refl + | cons a l ih => + let .succ (body := body) h1 := h1 + rw [Expr.instantiateList_lam]; simp at h ⊢ + have h' := h.1.looseBVarRange_zero + refine .trans (.mkAppList (.beta h')) ?_ + exact Expr.instantiateList_instantiate1_comm h' ▸ ih h.2 (a::l₂) h1 + +theorem BetaReduce.cheapBetaReduce (hc : e.Closed) : BetaReduce e e.cheapBetaReduce := by + simp [Expr.cheapBetaReduce] + split; · exact .refl + split; · exact .refl + let rec loop {e' i fn args} (H : LambdaBodyN i e' fn) (hi : i ≤ args.size) : + ∃ n fn', LambdaBodyN n e' fn' ∧ n ≤ args.size ∧ + Expr.cheapBetaReduce.loop e args i fn = Expr.cheapBetaReduce.cont e args n fn' := by + unfold Expr.cheapBetaReduce.loop; split + · split + · exact loop (by simpa [Nat.add_comm] using H.add (.succ .zero)) ‹_› + · exact ⟨_, _, H, Nat.le_of_lt ‹_›, rfl⟩ + · exact ⟨_, _, H, hi, rfl⟩ + refine let ⟨i, fn, h1, h2, eq⟩ := loop .zero (Nat.zero_le _); eq ▸ ?_; clear eq + simp [Expr.getAppArgs_eq] at h2 ⊢ + obtain ⟨l₁, l₂, rfl, eq⟩ : ∃ l₁ l₂, l₁.length = i ∧ e.getAppArgsList = l₁ ++ l₂ := + ⟨_, _, List.length_take_of_le (by simp [h2]), (List.take_append_drop ..).symm⟩ + have eqr := congrArg List.reverse eq; simp at eqr + have hl₁ : ∀ x ∈ l₁, x.Closed := by + have := eqr ▸ hc.getAppArgsList; simp [or_imp, forall_and] at this + exact this.1 + unfold Expr.cheapBetaReduce.cont; split <;> rename_i h3 + · simp [Expr.hasLooseBVars] at h3 + rw [Expr.mkAppRange_eq (l₂ := l₂) (l₃ := []) (by simp [eq]) rfl (by simp [← eq])] + rw [← e.mkAppList_getAppArgsList, eqr]; simp + refine .mkAppList <| .inst_reduce hl₁ [] h1 (Expr.instantiateList_eq_self h3) + split <;> [rename_i n; exact .refl] + have hc := h1.closed hc.getAppFn + simp [Closed] at hc; rw [if_pos hc] + rw [Expr.mkAppRange_eq (l₂ := l₂) (l₃ := []) (by simp [eq]) rfl (by simp [← eq])] + conv => lhs; rw [← e.mkAppList_getAppArgsList] + simp [eqr] + refine .mkAppList <| .inst_reduce hl₁ [] h1 ?_ + rw [List.getElem?_append_left (by omega), Nat.sub_right_comm, ← List.getElem?_reverse hc] + suffices ∀ l₁, (∀ x ∈ l₁, x.Closed) → ∀ n < l₁.length, + (Expr.bvar n).instantiateList l₁ = l₁[n]?.getD default by + simpa [Expr.liftLooseBVars_zero] using this l₁.reverse (by simpa using hl₁) n (by simp [hc]) + intro l₁ hl₁ n lt + induction l₁ generalizing n with + | nil => cases lt + | cons a l ih => + simp at hl₁ + obtain _ | n := n <;> simp [Expr.instantiate1'] + · exact Expr.instantiateList_eq_self hl₁.1.looseBVarRange_zero + · exact ih hl₁.2 _ (Nat.lt_of_succ_lt_succ lt) + +theorem TrExpr.beta (H : TrExpr env Us Δ e e') + (henv : VEnv.WF env) (hΓ : VLCtx.WF env Us.length Δ) + (H : BetaReduce e e₂) : TrExpr env Us Δ e₂ e' := by + induction H generalizing e' with + | refl => exact H + | trans _ _ ih1 ih2 => exact ih2 (ih1 H) + | app _ ih => + let ⟨_, .app hf ha tf ta, _, df⟩ := H + have ⟨_, _, hf', ha'⟩ := df.hasType.1.app_inv henv hΓ + exact ((ih ⟨_, tf, _, hf'⟩).app henv hΓ hf' ha' (ta.trExpr henv hΓ)).defeq henv hΓ ⟨_, df⟩ + | beta => + let ⟨_, .app hf ha tf ta, _, df⟩ := H + let .lam hA tA tb := tf + have ⟨⟨_, hA⟩, _, hb⟩ := hf.lam_inv henv hΓ + have ht := hf.uniqU henv hΓ (hA.lam hb) + have ⟨⟨_, Ae⟩, _, be⟩ := ht.forallE_inv henv hΓ + have hΓΓ := VLCtx.IsDefEq.cons (.refl henv hΓ) (ofv := none) nofun (.vlam Ae.symm) + have ⟨_, tb'⟩ := tb.defeqDFC henv hΓΓ + have beta := hb.beta (Ae.defeq ha) + have be' := (tb.uniq henv hΓΓ tb').of_l henv hΓΓ.wf hb + have hi := be'.instDF henv hΓ (.defeq Ae ha) + exact ⟨_, .inst henv ha tb' ta, _, beta.trans_l henv hΓ hi |>.symm.trans_l henv hΓ df⟩ + +theorem FVarsBelow.cheapBetaReduce (he : e.Closed) : FVarsBelow Δ e e.cheapBetaReduce := + .betaReduce (.cheapBetaReduce he) + +theorem TrExpr.cheapBetaReduce (H : TrExpr env Us Δ e e') + (henv : VEnv.WF env) (hΓ : VLCtx.WF env Us.length Δ) + (noBV : Δ.NoBV) : TrExpr env Us Δ e.cheapBetaReduce e' := + H.beta henv hΓ <| .cheapBetaReduce <| noBV ▸ H.closed.mono (by simp) + +theorem TrExprS.uninstantiateN + (W : VLCtx.Abstract Δ₀ v₀ d₀ dk k Δ₁ Δ) + (H : TrExprS env Us Δ₁ (Expr.instantiate1' e (.fvar v₀) dk) e') + (sc : FVarsIn (· ≠ v₀) e) : + TrExprS env Us Δ e e' := by + have := H.abstract W + rwa [sc.abstract_instantiate1] at this + +theorem TrExpr.uninstantiateN + (W : VLCtx.Abstract Δ₀ v₀ d₀ dk k Δ₁ Δ) + (H : TrExpr env Us Δ₁ (Expr.instantiate1' e (.fvar v₀) dk) e') + (sc : FVarsIn (· ≠ v₀) e) : + TrExpr env Us Δ e e' := + let ⟨_, s, h⟩ := H; ⟨_, s.uninstantiateN W sc, W.toCtx ▸ h⟩ + +theorem TrExprS.uninstantiate + (H : TrExprS env Us ((some (v, deps), d) :: Δ) (e.instantiate1' (.fvar v)) e') + (sc : FVarsIn (· ≠ v) e) : + TrExprS env Us ((none, d) :: Δ) e e' := H.uninstantiateN .zero sc + +theorem TrExpr.uninstantiate + (H : TrExpr env Us ((some (v, deps), d) :: Δ) (e.instantiate1' (.fvar v)) e') + (sc : FVarsIn (· ≠ v) e) : + TrExpr env Us ((none, d) :: Δ) e e' := H.uninstantiateN .zero sc + +theorem TrExprS.inst_fvar {Δ : VLCtx} (henv : Ordered env) + (hΔ : VLCtx.WF env Us.length ((some (a, deps), d) :: Δ)) + (H : TrExprS env Us ((none, d) :: Δ) e e') : + TrExprS env Us ((some (a, deps), d) :: Δ) (e.instantiate1' (.fvar a)) e' := by + refine + have W := .skip_fvar (a, deps) d .refl + have := H.weakFV henv (.cons_bvar _ W) ⟨hΔ, nofun, hΔ.2.2.weakN henv W.toCtx⟩ + ?_ + have hf := TrExprS.fvar (env := env) (Us := Us) (fv := a) (Δ := (some (a, deps), d) :: Δ) <| by + simp [VLCtx.find?, VLCtx.next]; exact ⟨rfl, rfl⟩ + match d with + | .vlam A₀ => + have := this.inst henv (.bvar .zero) (Δ := (some (a, deps), .vlam _) :: Δ) hf + rwa [VLocalDecl.depth, VExpr.instN_bvar0] at this + | .vlet A₀ e₀ => + simp [VLocalDecl.depth, VLocalDecl.liftN] at this + exact this.inst_let henv hf + +theorem TrExpr.rebuild_mkAppRevList (henv : env.WF) (hΔ : Δ.WF env Us.length) + (he : TrExprS env Us Δ e e') (h1 : TrExprS env Us Δ (e.mkAppRevList as) ea') + (h2 : TrExpr env Us Δ e₁ e') : TrExpr env Us Δ (e₁.mkAppRevList as) ea' := by + induction as generalizing ea' with + | nil => exact h2.defeq henv hΔ (he.uniq henv (.refl henv hΔ) h1) + | cons a as ih => + let .app a1 a2 a3 a4 := h1 + have := ih a3 + exact .app henv hΔ a1 a2 (ih a3) (a4.trExpr henv hΔ) + +theorem TrExpr.rebuild_mkAppList (henv : env.WF) (hΔ : Δ.WF env Us.length) + (he : TrExprS env Us Δ e e') (h1 : TrExprS env Us Δ (e.mkAppList as) ea') + (h2 : TrExpr env Us Δ e₁ e') : TrExpr env Us Δ (e₁.mkAppList as) ea' := by + rw [← Expr.mkAppRevList_reverse] at h1 ⊢ + exact h2.rebuild_mkAppRevList henv hΔ he h1 + +theorem TrExprS.eqv (H : TrExprS env Us Δ e₁ e') : e₁ == e₂ → TrExprS env Us Δ e₂ e' := by + simp [(· == ·)] + induction H generalizing e₂ <;> (cases e₂ <;> try change false = _ → _; rintro ⟨⟩) + all_goals simp [Expr.eqv']; grind [TrExprS] + +theorem TrExpr.eqv (H : TrExpr env Us Δ e₁ e') (h : e₁ == e₂) : TrExpr env Us Δ e₂ e' := + let ⟨_, h1, h2⟩ := H; ⟨_, h1.eqv h, h2⟩ + +theorem fvarsList_eqv {e₁ e₂ : Expr} : e₁ == e₂ → e₁.fvarsList = e₂.fvarsList := by + simp [(· == ·)] + induction e₁ generalizing e₂ <;> (cases e₂ <;> try change false = _ → _; rintro ⟨⟩) + all_goals simp [Expr.eqv']; intros; subst_vars; simp [Expr.fvarsList] + all_goals grind + +theorem FVarsIn.eqv : e₁ == e₂ → FVarsIn P e₁ → FVarsIn P e₂ := by + simp [(· == ·)] + induction e₁ generalizing e₂ <;> (cases e₂ <;> try change false = _ → _; rintro ⟨⟩) + all_goals simp [Expr.eqv']; intros; subst_vars; revert ‹FVarsIn ..›; simp [FVarsIn] + all_goals grind + +theorem FVarsBelow.eqv (H : FVarsBelow Δ e₁ ty₁) + (eq : e₁ == e₂) (eq' : ty₁ == ty₂) : FVarsBelow Δ e₂ ty₂ := + fun _ hP he => .eqv eq' (H _ hP (.eqv (BEq.symm eq) he)) + +theorem TrTyping.eqv (H : TrTyping env Us Δ e₁ ty₁ e' ty') + (eq : e₁ == e₂) (eq' : ty₁ == ty₂) : TrTyping env Us Δ e₂ ty₂ e' ty' := + let ⟨h1, h2, h3, h4⟩ := H + ⟨.eqv h1 eq eq', h2.eqv eq, h3.eqv eq', h4⟩ + +variable (env : VEnv) (Us : List Name) (Δ : VLCtx) in +inductive AppStack : Expr → VExpr → List Expr → Prop where + | head : TrExprS env Us Δ f f' → AppStack f f' [] + | app : + env.HasType Us.length Δ.toCtx f' (.forallE A B) → + env.HasType Us.length Δ.toCtx a' A → + TrExprS env Us Δ f f' → + TrExprS env Us Δ a a' → + AppStack (.app f a) (.app f' a') as → + AppStack f f' (a :: as) + +theorem AppStack.build_rev {e : Expr} : + ∀ {as}, TrExprS env Us Δ (e.mkAppRevList as) e' → + AppStack env Us Δ (e.mkAppRevList as) e' as' → + ∃ e', AppStack env Us Δ e e' (as.reverseAux as') + | [], _, H2 => ⟨_, H2⟩ + | _ :: as, .app h1 h2 h3 h4, H2 => + AppStack.build_rev (as := as) h3 (.app h1 h2 h3 h4 H2) + +theorem AppStack.tr : AppStack env Us Δ e e' as → TrExprS env Us Δ e e' + | .head H | .app _ _ H _ _ => H + +theorem AppStack.append {e : Expr} (H : AppStack env Us Δ (e.mkAppList as) e' bs) : + ∃ e', AppStack env Us Δ e e' (as ++ bs) := by + rw [← Expr.mkAppRevList_reverse] at H + simpa using AppStack.build_rev H.tr H + +theorem AppStack.build {e : Expr} (H : TrExprS env Us Δ (e.mkAppList as) e') : + ∃ e', AppStack env Us Δ e e' as := by simpa using AppStack.append (.head H) + +/-- Recover the pointwise strict translations of an application spine and +rebuild the complete translated application. Unlike the checker-facing +`AppStack.toSpineWF`, this purely syntactic projection needs no expected +function type and is therefore available to WHNF reduction. -/ +theorem AppStack.argsTranslation + (H : AppStack env Us Δ f f' args) : + ∃ args', args.Forall₂ (TrExprS env Us Δ) args' ∧ + TrExprS env Us Δ (f.mkAppList args) (VExpr.appN f' args') := by + induction H with + | head h => exact ⟨[], .nil, by simpa⟩ + | app hfun harg hf ha H ih => + obtain ⟨args', hargs, hfull⟩ := ih + refine ⟨_ :: args', .cons ha hargs, ?_⟩ + simpa [Expr.mkAppList, VExpr.appN] using hfull + +/-- A successful lookup on the left side of a pointwise list relation has a +related lookup at the same position on the right. -/ +theorem List.Forall₂.getElem?_left + {α : Type u} {β : Type v} {R : α → β → Prop} + {xs : List α} {ys : List β} {i : Nat} {x : α} + (H : List.Forall₂ R xs ys) (hx : xs[i]? = some x) : + ∃ y : β, ys[i]? = some y ∧ R x y := by + induction H generalizing i with + | nil => simp at hx + | cons hxy _ ih => + cases i with + | zero => + simp at hx + subst x + exact ⟨_, rfl, hxy⟩ + | succ i => simpa using ih (i := i) (by simpa using hx) + +#guard_named_axioms Ix.Theory.Named.TrExprS.toConstructor_ready [propext, Classical.choice, Quot.sound] diff --git a/Ix/Theory/Named/Verify/VLCtx.lean b/Ix/Theory/Named/Verify/VLCtx.lean new file mode 100644 index 000000000..7d42cb195 --- /dev/null +++ b/Ix/Theory/Named/Verify/VLCtx.lean @@ -0,0 +1,88 @@ +/- +Adapted for Ix: namespace, imports, and shared universe semantics. +SPDX-License-Identifier: Apache-2.0 +Source attribution and revision: Ix/Theory/Named/NOTICE. +-/ + +import Ix.Theory.Named.Verify.Expr +import Ix.Theory.Named.LocalContext + +open Ix.Theory (VLevel) + +namespace Ix.Theory.Named + +open Lean (FVarId Expr) + +@[reducible] def VLCtx := List (Option (FVarId × List FVarId) × VLocalDecl) + +namespace VLCtx + +def bvars : VLCtx → Nat + | [] => 0 + | (none, _) :: Δ => bvars Δ + 1 + | (some _, _) :: Δ => bvars Δ + +abbrev NoBV (Δ : VLCtx) : Prop := Δ.bvars = 0 + +def next : Option (FVarId × List FVarId) → Nat ⊕ FVarId → Option (Nat ⊕ FVarId) + | none, .inl 0 => none + | none, .inl (n+1) => some (.inl n) + | some _, .inl n => some (.inl n) + | none, .inr fv' => some (.inr fv') + | some (fv, _), .inr fv' => if fv == fv' then none else some (.inr fv') + +def find? : VLCtx → Nat ⊕ FVarId → Option (VExpr × VExpr) + | [], _ => none + | (ofv, d) :: Δ, v => + match next ofv v with + | none => some (d.value, d.type) + | some v => do let (e, A) ← find? Δ v; some (e.liftN d.depth, A.liftN d.depth) + +def liftVar (n k : Nat) : Nat ⊕ FVarId → Nat ⊕ FVarId + | .inl i => .inl (if i < k then i else i + n) + | .inr fv => .inr fv + +def varToExpr : Nat ⊕ FVarId → Expr + | .inl i => .bvar i + | .inr fv => .fvar fv + +def vlamName : VLCtx → Nat → Option (Option (FVarId × List FVarId)) + | [], _ => none + | (_, .vlet ..) :: Δ, i + | (_, .vlam ..) :: Δ, i+1 => vlamName Δ i + | (ofv, .vlam ..) :: _, 0 => some ofv + +def fvars (Δ : VLCtx) : List FVarId := Δ.filterMap (·.1.map (·.1)) + +@[simp] theorem fvars_nil : fvars [] = [] := rfl +@[simp] theorem fvars_cons_none {Δ : VLCtx} : fvars ((none, d) :: Δ) = fvars Δ := rfl +@[simp] theorem fvars_cons_some {Δ : VLCtx} : + fvars ((some fv, d) :: Δ) = fv.1 :: fvars Δ := rfl + +def toCtx : VLCtx → List VExpr + | [] => [] + | (_, .vlam ty) :: Δ => ty :: VLCtx.toCtx Δ + | (_, .vlet _ _) :: Δ => VLCtx.toCtx Δ + +def instL (Δ : VLCtx) (ls : List VLevel) : VLCtx := + match Δ with + | [] => [] + | (ofv, d) :: Δ => (ofv, d.instL ls) :: instL Δ ls + +theorem find?_eq_some : (∃ x, Δ.find? (.inr fv) = some x) ↔ fv ∈ fvars Δ := by + induction Δ with simp [find?] | cons d Δ ih + match d with + | (none, _) => simp [next, ← ih]; grind + | (some (fv', _), _) => + simp [next]; rw [@eq_comm _ fv] + by_cases h : fv' == fv <;> simp [h] <;> simp at h <;> simp [h]; grind + +theorem vlamName_mem_fvars : + ∀ {Δ : VLCtx} {i}, Δ.vlamName i = some (some fv) → fv.1 ∈ fvars Δ + | (none, .vlet ..) :: Δ, _, h + | (none, .vlam ..) :: Δ, _+1, h => vlamName_mem_fvars (Δ := Δ) h + | (some _, .vlet ..) :: Δ, _, h + | (some _, .vlam ..) :: Δ, _+1, h => .tail _ <| vlamName_mem_fvars (Δ := Δ) h + | (some _fv, .vlam ..) :: _, 0, rfl => .head _ + +end VLCtx diff --git a/Ix/Theory/PORTING.md b/Ix/Theory/PORTING.md new file mode 100644 index 000000000..4a013e8b8 --- /dev/null +++ b/Ix/Theory/PORTING.md @@ -0,0 +1,11 @@ +# Port notice + +This directory contains the selected Lean4Ix consistency model and its required +syntax, model, and certificate modules, relocated to `Ix.Theory`. It does not +contain the entire Lean4Ix or Ix.Theory.Named repositories. + +`LICENSE`, `LICENSE-MIT`, `LICENSE-APACHE`, and `NOTICE` are retained from the +source distribution. Historical source paths in those texts describe that +distribution. The current file selection, source hashes, con-leche attribution, +and namespace modifications are recorded in `Tests/Theory/ImportManifest.lean` +and the per-file headers. See `docs/theory.md` for the import boundary. diff --git a/Ix/Theory/Quot.lean b/Ix/Theory/Quot.lean new file mode 100644 index 000000000..f3c93b249 --- /dev/null +++ b/Ix/Theory/Quot.lean @@ -0,0 +1,239 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Store + +/-! # Canonical quotient declarations + +The quotient computation rules are selected by declaration content rather +than ambient names. This file records the exact anonymous kernel types of +`Eq`, `Quot`, `Quot.mk`, `Quot.lift`, and `Quot.ind`, parameterized by their +content-addressed references. +-/ + +namespace Ix.Theory +namespace Store + +/-- References used by the primitive quotient interface, including the +equality family appearing in the type of `Quot.lift`. -/ +structure QuotRefs (β : Type u) where + eq : ConstRef β + type : ConstRef β + ctor : ConstRef β + lift : ConstRef β + ind : ConstRef β + deriving DecidableEq + +/-- The two blocks whose contents make up the canonical equality and quotient +interface. -/ +structure QuotBlocks (β : Type u) where + equality : β + quotient : β + deriving DecidableEq + +namespace QuotBlocks + +/-- Positional references exposed by the canonical two-block layout. -/ +def refs (blocks : QuotBlocks β) : QuotRefs β where + eq := .member blocks.equality 0 + type := .member blocks.quotient 0 + ctor := .member blocks.quotient 1 + lift := .member blocks.quotient 2 + ind := .member blocks.quotient 3 + +end QuotBlocks + +namespace Quotient + +/-- Binary relation on the most recently bound sort. -/ +def relationType : VExpr β := + .forallE (.bvar 0) (.forallE (.bvar 1) (.sort .zero)) + +/-- Exact kernel type of the equality family. -/ +def eqType : VExpr β := + .forallE (.sort (.param 0)) + (.forallE (.bvar 0) (.forallE (.bvar 1) (.sort .zero))) + +/-- Exact kernel type of the reflexivity constructor at `eq`. -/ +def eqCtorType (eq : ConstRef β) : VExpr β := + .forallE (.sort (.param 0)) + (.forallE (.bvar 0) + (VExpr.appN (.const eq [.param 0]) [.bvar 1, .bvar 0, .bvar 0])) + +/-- Canonical equality-family member, including its nested reflexivity +constructor. -/ +def eqConstant (eq : ConstRef β) : Const β := + .induct 1 2 1 eqType + [{ + uvars := 1 + nparams := 2 + nfields := 0 + type := eqCtorType eq + safety := .safe + }] + .safe + +/-- Canonical one-member equality block at `block`. -/ +def eqBlock (block : β) : Block β := + { members := [eqConstant (.member block 0)] } + +/-- Exact kernel type of the quotient family. -/ +def typeType : VExpr β := + .forallE (.sort (.param 0)) + (.forallE relationType (.sort (.param 0))) + +/-- Exact kernel type of the quotient constructor. -/ +def ctorType (refs : QuotRefs β) : VExpr β := + .forallE (.sort (.param 0)) + (.forallE relationType + (.forallE (.bvar 1) + (VExpr.appN (.const refs.type [.param 0]) [.bvar 2, .bvar 1]))) + +/-- Exact kernel type of quotient lifting. -/ +def liftType (refs : QuotRefs β) : VExpr β := + .forallE (.sort (.param 0)) + (.forallE relationType + (.forallE (.sort (.param 1)) + (.forallE (.forallE (.bvar 2) (.bvar 1)) + (.forallE + (.forallE (.bvar 3) + (.forallE (.bvar 4) + (.forallE + (VExpr.appN (.bvar 4) [.bvar 1, .bvar 0]) + (VExpr.appN (.const refs.eq [.param 1]) + [.bvar 4, .app (.bvar 3) (.bvar 2), + .app (.bvar 3) (.bvar 1)])))) + (.forallE + (VExpr.appN (.const refs.type [.param 0]) [.bvar 4, .bvar 3]) + (.bvar 3)))))) + +/-- Exact kernel type of quotient induction. -/ +def indType (refs : QuotRefs β) : VExpr β := + .forallE (.sort (.param 0)) + (.forallE relationType + (.forallE + (.forallE + (VExpr.appN (.const refs.type [.param 0]) [.bvar 1, .bvar 0]) + (.sort .zero)) + (.forallE + (.forallE (.bvar 2) + (.app (.bvar 1) + (VExpr.appN (.const refs.ctor [.param 0]) + [.bvar 3, .bvar 2, .bvar 0]))) + (.forallE + (VExpr.appN (.const refs.type [.param 0]) [.bvar 3, .bvar 2]) + (.app (.bvar 2) (.bvar 0)))))) + +def typeConstant (_refs : QuotRefs β) : Const β := + .quot .type 1 typeType + +def ctorConstant (refs : QuotRefs β) : Const β := + .quot .ctor 1 (ctorType refs) + +def liftConstant (refs : QuotRefs β) : Const β := + .quot .lift 2 (liftType refs) + +def indConstant (refs : QuotRefs β) : Const β := + .quot .ind 1 (indType refs) + +/-- Canonical four-member quotient block at the selected block addresses. -/ +def quotBlock (blocks : QuotBlocks β) : Block β := + let refs := blocks.refs + { members := [typeConstant refs, ctorConstant refs, + liftConstant refs, indConstant refs] } + +/-- The canonical quotient block is syntactically closed. -/ +theorem quotBlock_closed (blocks : QuotBlocks β) : + (quotBlock blocks).Closed := by + simp [Block.Closed, quotBlock, typeConstant, ctorConstant, liftConstant, + indConstant, Const.Closed, typeType, ctorType, liftType, indType, + relationType, VExpr.appN, VExpr.Closed, VExpr.ClosedN] + +/-- Canonical quotient blocks contain no nested inductive-family members. -/ +theorem quotBlock_no_inductive {blocks : QuotBlocks β} {member uvars + nparams nindices : Nat} {type : VExpr β} {ctors : List (Ctor β)} + {safety : Safety} + (found : (quotBlock blocks).members[member]? = some + (.induct uvars nparams nindices type ctors safety)) : False := by + cases member with + | zero => simp [quotBlock, typeConstant] at found + | succ member => + cases member with + | zero => simp [quotBlock, ctorConstant] at found + | succ member => + cases member with + | zero => simp [quotBlock, liftConstant] at found + | succ member => + cases member with + | zero => simp [quotBlock, indConstant] at found + | succ member => simp [quotBlock] at found + +end Quotient + +/-- Exact content certificate for the canonical equality and quotient blocks. -/ +structure QuotBlocksCanonical (store : Store β) + (blocks : QuotBlocks β) : Prop where + equality : store.blocks blocks.equality = + some (Quotient.eqBlock blocks.equality) + quotient : store.blocks blocks.quotient = + some (Quotient.quotBlock blocks) + +/-- Exact canonical block content is stable under store inclusion. -/ +theorem QuotBlocksCanonical.mono {left right : Store β} + (subset : left ⊆ right) {blocks : QuotBlocks β} + (canonical : left.QuotBlocksCanonical blocks) : + right.QuotBlocksCanonical blocks where + equality := subset _ _ canonical.equality + quotient := subset _ _ canonical.quotient + +/-- The exact declarations selected by the primitive quotient rules. -/ +structure QuotReady (store : Store β) (refs : QuotRefs β) : Prop where + eq : store.lookup refs.eq = some (Quotient.eqConstant refs.eq) + type : store.lookup refs.type = some (Quotient.typeConstant refs) + ctor : store.lookup refs.ctor = some (Quotient.ctorConstant refs) + lift : store.lookup refs.lift = some (Quotient.liftConstant refs) + ind : store.lookup refs.ind = some (Quotient.indConstant refs) + +/-- Discover the unique canonical equality block by content. -/ +def eqBlock? [DecidableEq β] (store : Store β) : Option β := + match store.dom.filter fun block => + store.blocks block == some (Quotient.eqBlock block) with + | [block] => some block + | _ => none + +/-- Discover the unique canonical quotient block relative to `equality`. -/ +def quotBlock? [DecidableEq β] (store : Store β) + (equality : β) : Option β := + match store.dom.filter fun quotient => + let blocks : QuotBlocks β := { equality, quotient } + store.blocks quotient == some (Quotient.quotBlock blocks) with + | [block] => some block + | _ => none + +/-- Discover both uniquely designated blocks of the quotient interface. -/ +def quotBlocks? [DecidableEq β] (store : Store β) : Option (QuotBlocks β) := do + let equality ← store.eqBlock? + let quotient ← store.quotBlock? equality + pure { equality, quotient } + +/-- Exact canonical block content exposes the rule-facing declaration +inventory. -/ +theorem QuotBlocksCanonical.ready [DecidableEq β] {store : Store β} + {blocks : QuotBlocks β} (canonical : store.QuotBlocksCanonical blocks) : + store.QuotReady blocks.refs := by + constructor + · simp [Store.lookup, QuotBlocks.refs, Quotient.eqBlock, + canonical.equality] + · simp [Store.lookup, QuotBlocks.refs, Quotient.quotBlock, + canonical.quotient] + · simp [Store.lookup, QuotBlocks.refs, Quotient.quotBlock, + canonical.quotient] + · simp [Store.lookup, QuotBlocks.refs, Quotient.quotBlock, + canonical.quotient] + · simp [Store.lookup, QuotBlocks.refs, Quotient.quotBlock, + canonical.quotient] + +end Store +end Ix.Theory diff --git a/Ix/Theory/Ref.lean b/Ix/Theory/Ref.lean new file mode 100644 index 000000000..5ab55aeb6 --- /dev/null +++ b/Ix/Theory/Ref.lean @@ -0,0 +1,23 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +namespace Ix.Theory + +/-- A reference to a constant contained in a content-addressed block. -/ +inductive ConstRef (β : Type u) where + /-- The `i`th top-level member of block `b`. -/ + | member (b : β) (i : Nat) + /-- The `c`th constructor of the `i`th inductive member of block `b`. -/ + | ctor (b : β) (i c : Nat) +deriving DecidableEq, Hashable + +namespace ConstRef + +/-- The block containing a referenced member or constructor. -/ +@[simp] def block : ConstRef β → β + | .member b _ | .ctor b _ _ => b + +end ConstRef +end Ix.Theory diff --git a/Ix/Theory/Rename.lean b/Ix/Theory/Rename.lean new file mode 100644 index 000000000..362aa4701 --- /dev/null +++ b/Ix/Theory/Rename.lean @@ -0,0 +1,308 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Expr + +/-! # Address renaming for anonymous expressions + +The shared syntax operation is independent of inductive generation. +-/ + +namespace Ix.Theory + +/-- A dependency-free bijection used to transport abstract block addresses. +The Theory intentionally does not import Lean's metadata equivalence API. -/ +structure AddressEquiv (β : Type u) (γ : Type v) where + toFun : β → γ + invFun : γ → β + left_inv : ∀ block, invFun (toFun block) = block + right_inv : ∀ block, toFun (invFun block) = block + +namespace AddressEquiv + +instance : CoeFun (AddressEquiv β γ) fun _ => β → γ := + ⟨AddressEquiv.toFun⟩ + +@[simp] theorem inv_apply_apply (mapping : AddressEquiv β γ) (block : β) : + mapping.invFun (mapping block) = block := + mapping.left_inv block + +@[simp] theorem apply_inv_apply (mapping : AddressEquiv β γ) (block : γ) : + mapping (mapping.invFun block) = block := + mapping.right_inv block + +theorem injective (mapping : AddressEquiv β γ) : Function.Injective mapping := by + intro left right equality + simpa using congrArg mapping.invFun equality + +/-- Identity address bijection. -/ +def refl (β : Type u) : AddressEquiv β β where + toFun := id + invFun := id + left_inv := fun _ => rfl + right_inv := fun _ => rfl + +/-- Reverse an address bijection. -/ +def symm (mapping : AddressEquiv β γ) : AddressEquiv γ β where + toFun := mapping.invFun + invFun := mapping + left_inv := mapping.right_inv + right_inv := mapping.left_inv + +/-- Compose address bijections. -/ +def trans (first : AddressEquiv β γ) + (second : AddressEquiv γ δ) : AddressEquiv β δ where + toFun := second ∘ first + invFun := first.invFun ∘ second.invFun + left_inv := by + intro block + simp + right_inv := by + intro block + simp + +@[simp] theorem symm_apply (mapping : AddressEquiv β γ) (block : γ) : + mapping.symm block = mapping.invFun block := rfl + +@[simp] theorem trans_apply (first : AddressEquiv β γ) + (second : AddressEquiv γ δ) (block : β) : + (first.trans second) block = second (first block) := rfl + +@[simp] theorem beq_apply (mapping : AddressEquiv β γ) + [DecidableEq β] [DecidableEq γ] (left right : β) : + (mapping left == mapping right) = (left == right) := by + rw [Bool.eq_iff_iff] + simp only [beq_iff_eq, mapping.injective.eq_iff] + +@[simp] theorem bne_apply (mapping : AddressEquiv β γ) + [DecidableEq β] [DecidableEq γ] (left right : β) : + (mapping left != mapping right) = (left != right) := by + simp only [bne, beq_apply] + +end AddressEquiv + +namespace ConstRef + +/-- Rename the block address carried by a constant reference. -/ +def rename (mapping : β → γ) : ConstRef β → ConstRef γ + | .member block index => .member (mapping block) index + | .ctor block index ctorIndex => .ctor (mapping block) index ctorIndex + +@[simp] theorem rename_id (ref : ConstRef β) : + ref.rename id = ref := by + cases ref <;> rfl + +@[simp] theorem rename_comp (second : γ → δ) (first : β → γ) + (ref : ConstRef β) : + (ref.rename first).rename second = ref.rename (second ∘ first) := by + cases ref <;> rfl + +@[simp] theorem rename_block (mapping : β → γ) (ref : ConstRef β) : + (ref.rename mapping).block = mapping ref.block := by + cases ref <;> rfl + +/-- Renaming references along an address bijection is injective. -/ +theorem rename_injective (mapping : AddressEquiv β γ) : + Function.Injective (ConstRef.rename mapping) := by + have inverse : Function.LeftInverse + (ConstRef.rename mapping.invFun) (ConstRef.rename mapping) := by + intro ref + cases ref <;> simp [ConstRef.rename] + exact inverse.injective + +@[simp] theorem rename_eq_rename_iff (mapping : AddressEquiv β γ) + (left right : ConstRef β) : + left.rename mapping = right.rename mapping ↔ left = right := + (rename_injective mapping).eq_iff + +@[simp] theorem rename_inverse (mapping : AddressEquiv β γ) + (ref : ConstRef β) : + (ref.rename mapping).rename mapping.invFun = ref := by + cases ref <;> simp [ConstRef.rename] + +end ConstRef + +namespace VExpr + +/-- Rename every block address in an expression. Binder indices and universe +levels are unchanged. -/ +def rename (mapping : β → γ) : VExpr β → VExpr γ + | .bvar index => .bvar index + | .sort level => .sort level + | .const ref levels => .const (ref.rename mapping) levels + | .app function argument => + .app (function.rename mapping) (argument.rename mapping) + | .lam domain body => .lam (domain.rename mapping) (body.rename mapping) + | .forallE domain body => + .forallE (domain.rename mapping) (body.rename mapping) + | .proj ref field major => + .proj (ref.rename mapping) field (major.rename mapping) + | .natLit value => .natLit value + +@[simp] theorem rename_id (expression : VExpr β) : + expression.rename id = expression := by + induction expression with + | const ref levels => cases ref <;> rfl + | proj ref field major ih => cases ref <;> simp [rename, ih] + | _ => simp_all [rename] + +@[simp] theorem rename_comp (second : γ → δ) (first : β → γ) + (expression : VExpr β) : + (expression.rename first).rename second = + expression.rename (second ∘ first) := by + induction expression with + | const ref levels => cases ref <;> rfl + | proj ref field major ih => cases ref <;> simp [rename, ih] + | _ => simp_all [rename] + +@[simp] theorem rename_inverse (mapping : AddressEquiv β γ) + (expression : VExpr β) : + (expression.rename mapping).rename mapping.invFun = expression := by + induction expression <;> + simp_all only [rename, ConstRef.rename_inverse] + +/-- Expression renaming along an address bijection is injective. -/ +theorem rename_injective (mapping : AddressEquiv β γ) : + Function.Injective (VExpr.rename mapping) := by + have inverse : Function.LeftInverse + (VExpr.rename mapping.invFun) (VExpr.rename mapping) := + VExpr.rename_inverse mapping + exact inverse.injective + +@[simp] theorem rename_appN (mapping : β → γ) (function : VExpr β) + (arguments : List (VExpr β)) : + (function.appN arguments).rename mapping = + (function.rename mapping).appN (arguments.map (VExpr.rename mapping)) := by + induction arguments generalizing function with + | nil => rfl + | cons argument arguments ih => + simp [VExpr.appN, VExpr.rename, ih] + +@[simp] theorem rename_liftN (mapping : β → γ) (expression : VExpr β) + (count cutoff : Nat) : + (expression.liftN count cutoff).rename mapping = + (expression.rename mapping).liftN count cutoff := by + induction expression generalizing cutoff <;> simp_all [VExpr.liftN, rename] + +@[simp] theorem rename_instL (mapping : β → γ) (expression : VExpr β) + (levels : List VLevel) : + (expression.instL levels).rename mapping = + (expression.rename mapping).instL levels := by + induction expression <;> simp_all [VExpr.instL, rename] + +@[simp] theorem rename_inst (mapping : β → γ) (expression argument : VExpr β) + (cutoff : Nat) : + (expression.inst argument cutoff).rename mapping = + (expression.rename mapping).inst (argument.rename mapping) cutoff := by + induction expression generalizing cutoff with + | bvar index => + by_cases below : index < cutoff + · simp [VExpr.inst, VExpr.instVar, below, rename] + · by_cases equal : index = cutoff + · simp [VExpr.inst, VExpr.instVar, equal, rename] + · simp [VExpr.inst, VExpr.instVar, below, equal, rename] + | _ => simp_all [VExpr.inst, rename] + +@[simp] theorem rename_lamN (mapping : β → γ) + (binders : List (VExpr β)) (body : VExpr β) : + (VExpr.lamN binders body).rename mapping = + VExpr.lamN (binders.map (VExpr.rename mapping)) (body.rename mapping) := by + induction binders with + | nil => rfl + | cons binder binders ih => simp [VExpr.lamN, rename, ih] + +@[simp] theorem rename_forallN (mapping : β → γ) + (binders : List (VExpr β)) (body : VExpr β) : + (VExpr.forallN binders body).rename mapping = + VExpr.forallN (binders.map (VExpr.rename mapping)) + (body.rename mapping) := by + induction binders with + | nil => rfl + | cons binder binders ih => simp [VExpr.forallN, rename, ih] + +@[simp] theorem rename_liftTelN (mapping : β → γ) (count : Nat) : + ∀ (binders : List (VExpr β)) (cutoff : Nat), + (VExpr.liftTelN count binders cutoff).map (VExpr.rename mapping) = + VExpr.liftTelN count + (binders.map (VExpr.rename mapping)) cutoff + | [], _ => rfl + | binder :: binders, cutoff => by + simp [VExpr.liftTelN, rename_liftTelN mapping count binders] + +@[simp] theorem rename_telN (mapping : β → γ) (count : Nat) + (expression : VExpr β) : + (expression.rename mapping).telN count = + (expression.telN count).map (VExpr.rename mapping) := by + induction count generalizing expression with + | zero => rfl + | succ count ih => + cases expression with + | forallE domain body => + simp only [rename, VExpr.telN, List.map_cons] + rw [ih] + | _ => rfl + +@[simp] theorem rename_dropN (mapping : β → γ) (count : Nat) + (expression : VExpr β) : + (expression.rename mapping).dropN count = + (expression.dropN count).rename mapping := by + induction count generalizing expression with + | zero => rfl + | succ count ih => + cases expression <;> simp [rename, VExpr.dropN, ih] + +@[simp] theorem rename_resultOf (mapping : β → γ) (expression : VExpr β) : + (expression.rename mapping).resultOf = + expression.resultOf.rename mapping := by + induction expression <;> simp_all [rename, VExpr.resultOf] + +@[simp] theorem rename_appArgs (mapping : β → γ) (expression : VExpr β) + (accumulator : List (VExpr β)) : + (expression.rename mapping).appArgs + (accumulator.map (VExpr.rename mapping)) = + (expression.appArgs accumulator).map (VExpr.rename mapping) := by + induction expression generalizing accumulator with + | app function argument functionIH argumentIH => + simp only [rename, VExpr.appArgs] + simpa only [List.map_cons] using functionIH (argument :: accumulator) + | _ => rfl + +@[simp] theorem rename_appHead (mapping : β → γ) (expression : VExpr β) : + (expression.rename mapping).appHead = + expression.appHead.rename mapping := by + induction expression <;> simp_all [rename, VExpr.appHead] + +@[simp] theorem rename_instTelN (mapping : β → γ) (argument : VExpr β) : + ∀ (binders : List (VExpr β)) (cutoff : Nat), + (VExpr.instTelN argument binders cutoff).map (VExpr.rename mapping) = + VExpr.instTelN (argument.rename mapping) + (binders.map (VExpr.rename mapping)) cutoff + | [], _ => rfl + | binder :: binders, cutoff => by + simp [VExpr.instTelN, rename_instTelN mapping argument binders] + +@[simp] theorem rename_instRev (mapping : β → γ) (expression : VExpr β) + (arguments : List (VExpr β)) : + (VExpr.instRev expression arguments).rename mapping = + VExpr.instRev (expression.rename mapping) + (arguments.map (VExpr.rename mapping)) := by + induction arguments generalizing expression with + | nil => rfl + | cons argument arguments ih => + simp [VExpr.instRev, ih] + +@[simp] theorem rename_instRevAt (mapping : β → γ) (expression : VExpr β) + (arguments : List (VExpr β)) (cutoff : Nat) : + (VExpr.instRevAt expression arguments cutoff).rename mapping = + VExpr.instRevAt (expression.rename mapping) + (arguments.map (VExpr.rename mapping)) cutoff := by + induction arguments generalizing expression with + | nil => rfl + | cons argument arguments ih => + simp [VExpr.instRevAt, ih] + +end VExpr + +end Ix.Theory diff --git a/Ix/Theory/Std/Basic.lean b/Ix/Theory/Std/Basic.lean new file mode 100644 index 000000000..326c00da1 --- /dev/null +++ b/Ix/Theory/Std/Basic.lean @@ -0,0 +1,31 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Batteries.Data.List.Basic +import Init.Omega + +namespace Ix.Theory + +protected theorem List.Forall₂.rfl + {R : α → α → Prop} {xs : List α} (h : ∀ x ∈ xs, R x x) : xs.Forall₂ R xs := by + induction xs with + | nil => exact .nil + | cons x xs ih => + simp only [List.mem_cons, forall_eq_or_imp] at h + exact .cons h.1 (ih h.2) + +protected theorem List.Forall₂.length_eq + {R : α → β → Prop} {left : List α} {right : List β} + (related : List.Forall₂ R left right) : left.length = right.length := by + induction related with + | nil => rfl + | cons _ _ ih => exact congrArg Nat.succ ih +theorem List.map_id_mem {f : α → α} (xs : List α) (h : ∀ x ∈ xs, f x = x) : + xs.map f = xs := by + induction xs with + | nil => rfl + | cons x xs ih => simp only [List.mem_cons, forall_eq_or_imp] at h; simp [h.1, ih h.2] + +end Ix.Theory diff --git a/Ix/Theory/Store.lean b/Ix/Theory/Store.lean new file mode 100644 index 000000000..4365de0d0 --- /dev/null +++ b/Ix/Theory/Store.lean @@ -0,0 +1,116 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Const + +namespace Ix.Theory + +/-- A finite view of a content-addressed constant store. -/ +structure Store (β : Type u) where + dom : List β + nodup : dom.Nodup + blocks : β → Option (Block β) + mem_dom : ∀ b, (blocks b).isSome ↔ b ∈ dom + +namespace Store + +/-- Look up an ordinary block member. Constructor references use `lookupCtor`. -/ +def lookup (store : Store β) : ConstRef β → Option (Const β) + | .member block index => do + let contents ← store.blocks block + contents.members[index]? + | .ctor .. => none + +/-- Look up a constructor nested under an inductive block member. -/ +def lookupCtor (store : Store β) : ConstRef β → Option (Ctor β) + | .member .. => none + | .ctor block member ctor => do + let contents ← store.blocks block + let constant ← contents.members[member]? + match constant with + | .induct _ _ _ _ ctors _ => ctors[ctor]? + | _ => none + +/-- Flattened constructor-rule position of a nested constructor reference. -/ +def ctorRuleIndex? (store : Store β) : ConstRef β → Option Nat + | .member .. => none + | .ctor block member ctor => do + let some contents := store.blocks block | none + let some constant := contents.members[member]? | none + match constant with + | .induct _ _ _ _ ctors _ => + if ctor < ctors.length then + some ((contents.members.take member).foldl + (fun offset entry => offset + entry.ctorCount) 0 + ctor) + else + none + | _ => none + +/-- Classify either an ordinary member or a nested constructor reference. -/ +def kind (store : Store β) (ref : ConstRef β) : Option ConstKind := + match store.lookup ref with + | some constant => some constant.kind + | none => (store.lookupCtor ref).map fun _ => .ctor + +/-- Universe arity of an ordinary member or nested constructor. -/ +def uvars (store : Store β) (ref : ConstRef β) : Option Nat := + match store.lookup ref with + | some constant => some constant.uvars + | none => (store.lookupCtor ref).map Ctor.uvars + +/-- Type of an ordinary member or nested constructor. -/ +def type (store : Store β) (ref : ConstRef β) : Option (VExpr β) := + match store.lookup ref with + | some constant => some constant.type + | none => (store.lookupCtor ref).map Ctor.type + +/-- Distinct dependencies of a block, excluding self references. -/ +def deps [DecidableEq β] (store : Store β) (block : β) : List β := + (((store.blocks block).map Block.refs).getD []).map ConstRef.block + |>.filter (· != block) + |>.eraseDups + +/-- `block` directly refers to `dependency`, and the dependency is present. -/ +def DependsOn [DecidableEq β] (store : Store β) (block dependency : β) : Prop := + dependency ∈ store.deps block ∧ dependency ∈ store.dom + +/-- Reflexive transitive dependency closure of one block. -/ +inductive Closure [DecidableEq β] (store : Store β) (root : β) : β → Prop where + | refl : store.Closure root root + | step : store.Closure root block → store.DependsOn block dependency → + store.Closure root dependency + +/-- Dependencies point strictly backward in some well-founded order. -/ +def Acyclic [DecidableEq β] (store : Store β) : Prop := + WellFounded (flip store.DependsOn) + +/-- Keep exactly the blocks satisfying `keep`. This is a proof-facing view; +executable consumers may supply their own finite closure computation. -/ +noncomputable def restrict (store : Store β) (keep : β → Prop) : Store β := by + classical + exact { + dom := store.dom.filter keep + nodup := store.nodup.filter _ + blocks := fun block => if keep block then store.blocks block else none + mem_dom := by + intro block + by_cases h : keep block <;> simp [h, store.mem_dom] + } + +/-- Pointwise inclusion of partial block maps. -/ +protected def Subset (left right : Store β) : Prop := + ∀ block contents, left.blocks block = some contents → + right.blocks block = some contents + +instance : HasSubset (Store β) := ⟨Store.Subset⟩ + +/-- Return a block identifier only when the requested content occurs exactly once. -/ +def designated [DecidableEq β] (store : Store β) (canon : Block β) : Option β := + match store.dom.filter fun block => store.blocks block == some canon with + | [block] => some block + | _ => none + +end Store +end Ix.Theory diff --git a/Ix/Theory/VLevel.lean b/Ix/Theory/VLevel.lean new file mode 100644 index 000000000..fe27e5159 --- /dev/null +++ b/Ix/Theory/VLevel.lean @@ -0,0 +1,128 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Std.Basic + +/-! +# Semantic universe levels + +`VLevel` gives the implementation-independent meaning of universe levels used +by Theory. Its equivalence relation is extensional equality under every +valuation, so it deliberately forgets representation identity and sharing. +-/ + +namespace Ix.Theory + +inductive VLevel where + | zero : VLevel + | succ : VLevel → VLevel + | max : VLevel → VLevel → VLevel + | imax : VLevel → VLevel → VLevel + | param : Nat → VLevel +deriving DecidableEq, Hashable + +namespace VLevel + +/-- The natural-number semantics of universe `imax`. -/ +def natIMax (a b : Nat) : Nat := if b = 0 then 0 else Nat.max a b + +instance : Inhabited VLevel := ⟨.zero⟩ + +variable (n : Nat) in +def WF : VLevel → Prop + | .zero => True + | .succ l => l.WF + | .max l₁ l₂ => l₁.WF ∧ l₂.WF + | .imax l₁ l₂ => l₁.WF ∧ l₂.WF + | .param i => i < n + +instance decidable_WF : ∀ {l}, Decidable (WF n l) + | .zero => instDecidableTrue + | .succ l => @decidable_WF _ l + | .max .. | .imax .. => @instDecidableAnd _ _ decidable_WF decidable_WF + | .param _ => Nat.decLt .. + +variable (ls : List Nat) in +def eval : VLevel → Nat + | .zero => 0 + | .succ l => l.eval + 1 + | .max l₁ l₂ => l₁.eval.max l₂.eval + | .imax l₁ l₂ => natIMax l₁.eval l₂.eval + | .param i => ls.getD i 0 + +protected def LE (a b : VLevel) : Prop := ∀ ls, a.eval ls ≤ b.eval ls + +instance : LE VLevel := ⟨VLevel.LE⟩ + +theorem le_refl (a : VLevel) : a ≤ a := fun _ => Nat.le_refl _ + +protected def Equiv (a b : VLevel) : Prop := a.eval = b.eval + +instance : HasEquiv VLevel := ⟨VLevel.Equiv⟩ + +theorem equiv_def {a b : VLevel} : a ≈ b ↔ ∀ ls, a.eval ls = b.eval ls := funext_iff + +theorem le_antisymm_iff {a b : VLevel} : a ≈ b ↔ a ≤ b ∧ b ≤ a := + equiv_def.trans <| (forall_congr' fun _ => Nat.le_antisymm_iff).trans forall_and + +theorem succ_congr {a b : VLevel} (h : a ≈ b) : succ a ≈ succ b := by + simpa [equiv_def, eval] using h + +theorem imax_congr (h₁ : a₁ ≈ b₁) (h₂ : a₂ ≈ b₂) : imax a₁ a₂ ≈ imax b₁ b₂ := by + simp_all [equiv_def, eval] + +theorem imax_self : imax a a ≈ a := by + simp [equiv_def, eval, natIMax, eq_comm (b := 0)] + +variable (ls : List VLevel) in +def inst : VLevel → VLevel + | .zero => .zero + | .succ l => .succ l.inst + | .max l₁ l₂ => .max l₁.inst l₂.inst + | .imax l₁ l₂ => .imax l₁.inst l₂.inst + | .param i => ls.getD i .zero + +theorem inst_inst {l : VLevel} : (l.inst ls).inst ls' = l.inst (ls.map (inst ls')) := by + induction l <;> simp [inst, *, List.getD_eq_getElem?_getD, List.getElem?_map] + case param n => cases ls[n]? <;> simp [inst] + +def params (n : Nat) : List VLevel := (List.range n).map .param + +@[simp] theorem params_length {n : Nat} : (params n).length = n := by simp [params] + +theorem params_wf {n : Nat} : ∀ ⦃l⦄, l ∈ params n → l.WF n := by simp [params, WF] + +theorem inst_id {l : VLevel} (h : l.WF u) : l.inst (params u) = l := by + induction l <;> simp_all [params, inst, WF, List.getD_eq_getElem?_getD] + +theorem inst_map_id (h : ls.length = n) : (params n).map (inst ls) = ls := by + subst n; simp [params]; apply List.ext_get (by simp) + intro i _ _; simp [inst]; rw [List.getElem?_eq_getElem]; rfl + +theorem eval_inst {l : VLevel} : (l.inst ls).eval ns = l.eval (ls.map (eval ns)) := by + induction l <;> simp [eval, inst, *, List.getD_eq_getElem?_getD] + case param n => cases ls[n]? <;> simp [eval] + +theorem WF.inst {l : VLevel} (H : ∀ l ∈ ls, l.WF n) : (l.inst ls).WF n := by + induction l with + | zero => trivial + | succ _ ih => exact ih + | max _ _ ih1 ih2 | imax _ _ ih1 ih2 => exact ⟨ih1, ih2⟩ + | param i => + simp [VLevel.inst, List.getD_eq_getElem?_getD] + cases e : ls[i]? with + | none => trivial + | some => exact H _ (List.mem_of_getElem? e) + +theorem inst_congr {l : VLevel} (h1 : l ≈ l') (h2 : List.Forall₂ (·≈·) ls ls') : + l.inst ls ≈ l'.inst ls' := by + simp [equiv_def, eval_inst, ← equiv_def.1 h1] + intro ns; congr 1 + induction h2 with + | nil => rfl + | cons h2 => simp [*, equiv_def.1 h2] + +theorem inst_congr_l {l : VLevel} (h1 : l ≈ l') : l.inst ls ≈ l'.inst ls := + inst_congr h1 <| Ix.Theory.List.Forall₂.rfl fun _ _ => rfl diff --git a/Models/SetTheory/IxSetTheoryModel.lean b/Models/SetTheory/IxSetTheoryModel.lean new file mode 100644 index 000000000..a60e6561b --- /dev/null +++ b/Models/SetTheory/IxSetTheoryModel.lean @@ -0,0 +1,6 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import IxSetTheoryModel.Audit diff --git a/Models/SetTheory/IxSetTheoryModel/Audit.lean b/Models/SetTheory/IxSetTheoryModel/Audit.lean new file mode 100644 index 000000000..31fe85af3 --- /dev/null +++ b/Models/SetTheory/IxSetTheoryModel/Audit.lean @@ -0,0 +1,52 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Lean.Elab.Command +import Lean.Util.FoldConsts +import IxSetTheoryModel.Carneiro + +/-! +# Checked dependency boundary of the concrete set model + +The traversal reads declaration types, bodies, and constructor fields directly. +It does not depend on imported axiom summaries or the kernel's named calculus. +-/ + +namespace IxSetTheoryModel.Audit + +open Lean Elab Command + +private def directConstants (info : ConstantInfo) : Array Name := + info.type.getUsedConstants ++ match info with + | .thmInfo value => value.value.getUsedConstants + | .defnInfo value => value.value.getUsedConstants + | .opaqueInfo value => value.value.getUsedConstants + | .inductInfo value => value.ctors.toArray + | _ => #[] + +private partial def closure (env : Environment) (pending : List Name) + (seen : NameSet := {}) : NameSet := + match pending with + | [] => seen + | name :: rest => + if seen.contains name then closure env rest seen + else match env.checked.get.find? name with + | some info => closure env ((directConstants info).toList ++ rest) (seen.insert name) + | none => closure env rest (seen.insert name) + +run_cmd do + let env ← getEnv + let root := ``carneiro_implies_ix + let dependencies := closure env [root] + let actual := dependencies.toList.toArray.filter fun name => + match env.checked.get.find? name with + | some (.axiomInfo _) => true + | _ => false + let expected := #[``propext, ``Classical.choice, ``Quot.sound] + unless actual.qsort Name.lt == expected.qsort Name.lt do + throwError m!"set-theory model axiom boundary changed:\n{actual.qsort Name.lt}" + logInfo "Set-theory model full dependency audit passed: standard Lean axioms only" + +end IxSetTheoryModel.Audit diff --git a/Models/SetTheory/IxSetTheoryModel/Carneiro.lean b/Models/SetTheory/IxSetTheoryModel/Carneiro.lean new file mode 100644 index 000000000..2bf8d3ade --- /dev/null +++ b/Models/SetTheory/IxSetTheoryModel/Carneiro.lean @@ -0,0 +1,181 @@ +/- +Ported from con-leche (86cd20a65660d757cedc81561a44579099b565d0). +Source attribution and original path: Models/SetTheory/NOTICE. +Modifications Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0) +Changes: namespaces and imports adapted to Ix; documentation updated. +-/ + +import Mathlib.SetTheory.Cardinal.Regular +import Mathlib.SetTheory.ZFC.VonNeumann +import Mathlib.SetTheory.ZFC.Cardinal +import Ix.Theory.Model.SetTheory.Core + +/-! +# A concrete model of Ix's set-theory interface + +A strictly increasing countable sequence of inaccessible cardinals gives +`Ix.Theory.Model.SetTheory ZFSet.{u}`, with `univChain n := V_ (κ n).ord`. +Mathlib supplies the set operations, including images of arbitrary Lean +functions via `Classical.allZFSetDefinable`. The proof below establishes +Tarski's universe clauses for each inaccessible stage of the von Neumann +hierarchy and assembles the instance. + +This separate package is the only part of the construction that imports +Mathlib. `Audit.lean` checks the model-existence theorem's full dependency +graph against Lean's `propext`, `Classical.choice`, and `Quot.sound` axioms. +The inaccessible-cardinal assumption remains a theorem hypothesis. +-/ + +universe u + +open Cardinal Order ZFSet + +namespace IxSetTheoryModel + +/-- A strictly increasing countable sequence of strongly inaccessible +cardinals. This remains an explicit hypothesis; this package does not assert +the existence of inaccessible cardinals. -/ +def OmegaInaccessibles : Prop := + ∃ κ : ℕ → Cardinal.{u}, StrictMono κ ∧ ∀ n, (κ n).IsInaccessible + +/-! ### `V_ κ` is a Grothendieck universe in Tarski's form, for `κ` inaccessible -/ + +variable {κ : Cardinal.{u}} + +/-- A subset of `V_ κ.ord` of cardinality `< κ` has rank `< κ.ord`: +regularity of `κ` bounds the least strict upper bound of `< κ` many +ordinals below `κ.ord`. -/ +theorem rank_lt_ord_of_card_lt (hκ : κ.IsRegular) {y : ZFSet.{u}} + (hy : y ⊆ V_ κ.ord) (hc : y.card < κ) : y.rank < κ.ord := by + let e : y ≃ Shrink.{u} y := equivShrink _ + let f : Shrink.{u} y → Ordinal.{u} := fun z => rank (e.symm z).1 + have hlt : ⨆ z, f z + 1 < κ.ord := + Ordinal.iSup_add_one_lt_of_lt_cof (by rwa [hκ.cof_ord]) fun z => + mem_vonNeumann.mp (hy (e.symm z).2) + refine lt_of_le_of_lt ?_ hlt + rw [rank_le_iff] + intro z hz + have := Ordinal.lt_iSup_add_one f (e ⟨z, hz⟩) + simpa [f, e] using this + +/-- Below an inaccessible, the beth function stays below it. -/ +theorem preBeth_lt_of_lt_ord (hκ : κ.IsInaccessible) : + ∀ {a : Ordinal.{u}}, a < κ.ord → preBeth a < κ := by + intro a + induction a using Ordinal.limitRecOn with + | zero => intro _; rw [preBeth_zero]; exact hκ.pos + | add_one a ih => + intro h + rw [preBeth_add_one] + exact hκ.isStrongLimit.isStrongPrelimit (ih ((lt_add_one a).trans h)) + | limit a ha ih => + intro h + rw [preBeth_limit ha.isSuccPrelimit] + refine Cardinal.lift_iSup_lt_of_lt_cof_ord ?_ fun b => ih b b.2 (b.2.trans h) + rw [hκ.isRegular.lift.cof_ord, mk_Iio_ordinal, lift_lift, lift_lt] + exact lt_ord.mp h + +/-- `|V_ κ| = κ` for `κ` inaccessible. -/ +theorem card_vonNeumann_ord (hκ : κ.IsInaccessible) : card (V_ κ.ord) = κ := by + rw [card_vonNeumann] + refine le_antisymm ?_ (le_preBeth_ord κ) + rw [preBeth_limit (isSuccLimit_ord hκ.aleph0_lt.le).isSuccPrelimit] + exact ciSup_le' fun b => (preBeth_lt_of_lt_ord hκ b.2).le + +/-- A subset of `V_ κ` that is not a member has the cardinality of +`V_ κ`. -/ +theorem card_eq_of_not_rank_lt (hκ : κ.IsInaccessible) {y : ZFSet.{u}} + (hy : y ⊆ V_ κ.ord) (hr : ¬ y.rank < κ.ord) : card y = card (V_ κ.ord) := by + refine le_antisymm (card_mono hy) ?_ + rw [card_vonNeumann_ord hκ] + exact not_lt.mp fun h => hr (rank_lt_ord_of_card_lt hκ.isRegular hy h) + +/-- Equal cardinality gives Ix's meta-level equinumerosity: a global +function `ZFSet → ZFSet` that restricts to a bijection. -/ +theorem equinumerous_of_card_eq {y s : ZFSet.{u}} (h : card y = card s) : + Ix.Theory.Model.Equinumerous (· ∈ ·) y s := by + obtain ⟨e'⟩ := Cardinal.eq.mp h + let e : y ≃ s := (equivShrink _).trans (e'.trans (equivShrink _).symm) + classical + refine ⟨fun z => if hz : z ∈ y then (e ⟨z, hz⟩).1 else z, ?_, ?_, ?_⟩ + · intro z hz + simp only [hz, ↓reduceDIte] + exact (e ⟨z, hz⟩).2 + · intro z z' hz hz' hzz' + simp only [hz, hz', ↓reduceDIte] at hzz' + have := e.injective (Subtype.ext hzz') + exact congrArg Subtype.val this + · intro w hw + refine ⟨(e.symm ⟨w, hw⟩).1, (e.symm ⟨w, hw⟩).2, ?_⟩ + simp only [(e.symm ⟨w, hw⟩).2, ↓reduceDIte, Subtype.coe_eta, Equiv.apply_symm_apply] + +/-- **`V_ κ` is a Grothendieck universe in Tarski's form** (Ix's +`IsTGUniverse`) for every inaccessible `κ`. -/ +theorem isTGUniverse_vonNeumann (hκ : κ.IsInaccessible) : + Ix.Theory.Model.IsTGUniverse (· ∈ ·) (V_ κ.ord) := by + have hlim : IsSuccLimit κ.ord := isSuccLimit_ord hκ.aleph0_lt.le + refine ⟨?_, ?_, ?_, ?_⟩ + · -- transitivity + intro y z (hy : y ∈ V_ κ.ord) (hz : z ∈ y) + exact isTransitive_vonNeumann _ y hy hz + · -- subsets of members are members + intro y z (hy : y ∈ V_ κ.ord) hzy + exact mem_vonNeumann_of_subset (fun w hw => hzy w hw) hy + · -- power sets stay inside + intro y (hy : y ∈ V_ κ.ord) + refine ⟨powerset y, ?_, fun z hz => mem_powerset.mpr fun w hw => hz w hw⟩ + show powerset y ∈ V_ κ.ord + rw [mem_vonNeumann, rank_powerset] + exact hlim.succ_lt (mem_vonNeumann.mp hy) + · -- Tarski's clause: a subset is a member or equinumerous + intro y hy + have hy' : y ⊆ V_ κ.ord := fun w hw => hy w hw + by_cases hr : y.rank < κ.ord + · exact Or.inr (mem_vonNeumann.mpr hr) + · exact Or.inl (equinumerous_of_card_eq (card_eq_of_not_rank_lt hκ hy' hr)) + +/-! ### The `Ix.Theory.Model.SetTheory` instance on `ZFSet` -/ + +set_option warn.classDefReducibility false in +/-- Ix's set theory on Mathlib's `ZFSet.{u}`, from any strictly +increasing sequence of inaccessibles: the ZF⁻ fields are Mathlib's +(`ZFSet.ext`, pairs, `⋃₀`, `powerset`, `mem_wf`, `image` under +`Classical.allZFSetDefinable`), and `univChain n := V_ (κ n).ord`. -/ +noncomputable def setTheoryOfChain (κ : ℕ → Cardinal.{u}) (hmono : StrictMono κ) + (hinacc : ∀ n, (κ n).IsInaccessible) : Ix.Theory.Model.SetTheory ZFSet.{u} where + Mem := (· ∈ ·) + ext h := ZFSet.ext h + upair a b := {a, b} + mem_upair := mem_pair + sUnion := ZFSet.sUnion + mem_sUnion := mem_sUnion + power := powerset + mem_power := mem_powerset + regularity x h := by + obtain ⟨y, hy, hmin⟩ := mem_wf.has_min {y | y ∈ x} h + exact ⟨y, hy, fun ⟨z, hzy, hzx⟩ => hmin z hzx hzy⟩ + image f := @ZFSet.image f (Classical.allZFSetDefinable _) + mem_image := by + intro f a z + rw [@mem_image f (Classical.allZFSetDefinable _)] + exact exists_congr fun w => and_congr_right fun _ => eq_comm + univChain n := V_ (κ n).ord + univChain_mem n := vonNeumann_mem_of_lt (ord_lt_ord.mpr (hmono (Nat.lt_succ_self n))) + univChain_tg n := isTGUniverse_vonNeumann (hinacc n) + +set_option warn.classDefReducibility false in +/-- Ix's set theory on `ZFSet.{u}` under Carneiro's hypothesis. -/ +noncomputable def setTheoryOfCarneiro (h : OmegaInaccessibles.{u}) : Ix.Theory.Model.SetTheory ZFSet.{u} := + setTheoryOfChain (Classical.choose h) (Classical.choose_spec h).1 (Classical.choose_spec h).2 + +/-- **Carneiro's hypothesis implies Ix's.** `ω` strongly inaccessible +cardinals give a model of +Ix's `SetTheory` interface — on Mathlib's `ZFSet.{u}`, with the +universe chain `V_ (κ n).ord`. -/ +theorem carneiro_implies_ix : + OmegaInaccessibles.{u} → Nonempty (Σ V : Type (u + 1), Ix.Theory.Model.SetTheory V) := + fun h => ⟨⟨ZFSet.{u}, setTheoryOfCarneiro h⟩⟩ + + +end IxSetTheoryModel diff --git a/Models/SetTheory/LICENSE-CON-LECHE b/Models/SetTheory/LICENSE-CON-LECHE new file mode 100644 index 000000000..4c3f8ab0f --- /dev/null +++ b/Models/SetTheory/LICENSE-CON-LECHE @@ -0,0 +1,70 @@ +Apache License 2.0 (Apache) +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy of this License; and + +2. You must cause any modified files to carry prominent notices stating that You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. diff --git a/Models/SetTheory/NOTICE b/Models/SetTheory/NOTICE new file mode 100644 index 000000000..049f8aeb6 --- /dev/null +++ b/Models/SetTheory/NOTICE @@ -0,0 +1,11 @@ +Set-theory model construction + +Copied from con-leche revision 86cd20a65660d757cedc81561a44579099b565d0, +bridge/lean4lean-model/ConLecheBridge/Carneiro.lean. +Original SHA-256: +389b5fff365cf47a5040e6eac540d6ddedde4b95d9f4176c3c08f675f586c1c4 + +The countable-inaccessibles hypothesis was transcribed by con-leche from +Mario Carneiro's Lean4LeanModel/Consistency.lean. Namespaces, imports, and +documentation were adapted for Ix; the mathematical construction is retained. +Con-leche's Apache-2.0 license is preserved in LICENSE-CON-LECHE. diff --git a/Models/SetTheory/README.md b/Models/SetTheory/README.md new file mode 100644 index 000000000..e74498555 --- /dev/null +++ b/Models/SetTheory/README.md @@ -0,0 +1,56 @@ +# Set-theory model + +This separate Lake package constructs `Ix.Theory.Model.SetTheory` on Mathlib's +`ZFSet`, assuming a strictly increasing countable sequence of strongly +inaccessible cardinals. Its universe chain is `V_ (κ n).ord`. + +`IxSetTheoryModel.setTheoryOfChain` assembles the instance from a given chain. +`setTheoryOfCarneiro` selects a chain from `OmegaInaccessibles`, and +`carneiro_implies_ix` proves: + +```lean +OmegaInaccessibles.{u} → + Nonempty (Σ V : Type (u + 1), Ix.Theory.Model.SetTheory V) +``` + +The construction includes the interface's Lean-level replacement scheme: +Mathlib's `Classical.allZFSetDefinable` supplies images of arbitrary functions +`ZFSet → ZFSet`. The audit traverses the theorem's checked types, bodies, and +constructor fields and permits exactly `propext`, `Classical.choice`, and +`Quot.sound`. Inaccessible cardinals remain an explicit +hypothesis, rather than an added Lean axiom. + +The package imports the actual Ix interface by a path dependency on the +repository root. Mathlib is confined to this package; ordinary Ix and +`Ix.Theory` builds do not depend on it. This construction supplies the +set-theoretic assumption used by the [consistency model](../../docs/theory.md). +The connection from the production checker to the certified interface is a +separate proof obligation. A converse from the interface to inaccessible +cardinals is not proved here. + +## Build + +From `Models/SetTheory`: + +```sh +lake exe cache get Mathlib.SetTheory.Cardinal.Regular Mathlib.SetTheory.ZFC.VonNeumann Mathlib.SetTheory.ZFC.Cardinal +lake build --wfail +``` + +Lean and Mathlib use release `v4.33.1`; `lake-manifest.json` pins all resolved +dependencies. The cache command retrieves the three imported Mathlib modules +and their dependencies. The build checks the axiom guard. The separate +`set-theory-model.yml` workflow runs these commands when the model, its interface, +or package configuration changes. + +From the repository root, `lake run check-kernel --with-model` includes this +build alongside the kernel proofs, foundation audits, and host regressions. + +## Provenance + +`IxSetTheoryModel/Carneiro.lean` is copied from con-leche revision +`86cd20a65660d757cedc81561a44579099b565d0`. The original path and source +SHA-256 are recorded in [NOTICE](NOTICE). +Namespaces, imports, and documentation are adapted for Ix; the mathematical +construction is retained. Con-leche's Apache-2.0 license is preserved in +`LICENSE-CON-LECHE`. diff --git a/Models/SetTheory/lake-manifest.json b/Models/SetTheory/lake-manifest.json new file mode 100644 index 000000000..65a60778b --- /dev/null +++ b/Models/SetTheory/lake-manifest.json @@ -0,0 +1,123 @@ +{"version": "1.2.0", + "packagesDir": ".lake/packages", + "packages": + [{"type": "path", + "scope": "", + "name": "ix", + "manifestFile": "lake-manifest.json", + "inherited": false, + "dir": "../..", + "configFile": "lakefile.lean"}, + {"url": "https://github.com/leanprover-community/mathlib4", + "type": "git", + "subDir": null, + "scope": "", + "rev": "0df444a360eaa60ab8c11dca51a86af692955474", + "name": "mathlib", + "manifestFile": "lake-manifest.json", + "inputRev": "v4.33.1", + "inherited": false, + "configFile": "lakefile.lean"}, + {"url": "https://github.com/leanprover-community/batteries", + "type": "git", + "subDir": null, + "scope": "", + "rev": "4488d40d070b9700d4d5a6aa342f0d40c31b2a2d", + "name": "batteries", + "manifestFile": "lake-manifest.json", + "inputRev": "v4.33.0", + "inherited": true, + "configFile": "lakefile.toml"}, + {"url": "https://github.com/leanprover/lean4-cli", + "type": "git", + "subDir": null, + "scope": "", + "rev": "6130a47896ce867c6a4a55373441e59e565bad0f", + "name": "Cli", + "manifestFile": "lake-manifest.json", + "inputRev": "v4.33.0", + "inherited": true, + "configFile": "lakefile.toml"}, + {"url": "https://github.com/argumentcomputer/Blake3.lean", + "type": "git", + "subDir": null, + "scope": "", + "rev": "78f5bc4b22de1172af8a5d91e7039128084fad3a", + "name": "Blake3", + "manifestFile": "lake-manifest.json", + "inputRev": "78f5bc4b22de1172af8a5d91e7039128084fad3a", + "inherited": true, + "configFile": "lakefile.lean"}, + {"url": "https://github.com/argumentcomputer/LSpec", + "type": "git", + "subDir": null, + "scope": "", + "rev": "ab4d5eb461941837f48eb891be755c8c73e89fdd", + "name": "LSpec", + "manifestFile": "lake-manifest.json", + "inputRev": "ab4d5eb461941837f48eb891be755c8c73e89fdd", + "inherited": true, + "configFile": "lakefile.toml"}, + {"url": "https://github.com/leanprover-community/plausible", + "type": "git", + "subDir": null, + "scope": "", + "rev": "b7eb3304aeae834b12dda98993a37f6a41f6f0bb", + "name": "plausible", + "manifestFile": "lake-manifest.json", + "inputRev": "v4.33.0", + "inherited": true, + "configFile": "lakefile.toml"}, + {"url": "https://github.com/leanprover-community/LeanSearchClient", + "type": "git", + "subDir": null, + "scope": "leanprover-community", + "rev": "5f4d51b81cbd3f6b32b156bfad9056621a040404", + "name": "LeanSearchClient", + "manifestFile": "lake-manifest.json", + "inputRev": "main", + "inherited": true, + "configFile": "lakefile.toml"}, + {"url": "https://github.com/leanprover-community/import-graph", + "type": "git", + "subDir": null, + "scope": "leanprover-community", + "rev": "16f02aa7642864af59f1ff0e384a015994db9118", + "name": "importGraph", + "manifestFile": "lake-manifest.json", + "inputRev": "main", + "inherited": true, + "configFile": "lakefile.toml"}, + {"url": "https://github.com/leanprover-community/ProofWidgets4", + "type": "git", + "subDir": null, + "scope": "leanprover-community", + "rev": "4be2e3d5087eeb272cf5a8853b8f9dd025ef5957", + "name": "proofwidgets", + "manifestFile": "lake-manifest.json", + "inputRev": "main", + "inherited": true, + "configFile": "lakefile.lean"}, + {"url": "https://github.com/leanprover-community/aesop", + "type": "git", + "subDir": null, + "scope": "leanprover-community", + "rev": "3448c0bcc5ce01b2d1546e483ec3620e32df3d0e", + "name": "aesop", + "manifestFile": "lake-manifest.json", + "inputRev": "master", + "inherited": true, + "configFile": "lakefile.toml"}, + {"url": "https://github.com/leanprover-community/quote4", + "type": "git", + "subDir": null, + "scope": "leanprover-community", + "rev": "92c15be17b7caf78c2ad767ec40f89052d908d81", + "name": "Qq", + "manifestFile": "lake-manifest.json", + "inputRev": "master", + "inherited": true, + "configFile": "lakefile.toml"}], + "name": "«ix-set-theory-model»", + "lakeDir": ".lake", + "fixedToolchain": false} diff --git a/Models/SetTheory/lakefile.toml b/Models/SetTheory/lakefile.toml new file mode 100644 index 000000000..6d6734249 --- /dev/null +++ b/Models/SetTheory/lakefile.toml @@ -0,0 +1,16 @@ +name = "ix-set-theory-model" +defaultTargets = ["IxSetTheoryModel"] + +# Keep Mathlib confined to this package. Its release matches Ix's toolchain. +# Resolve shared dependencies from Mathlib's pinned manifest first. +[[require]] +name = "mathlib" +git = "https://github.com/leanprover-community/mathlib4" +rev = "v4.33.1" + +[[require]] +name = "ix" +path = "../.." + +[[lean_lib]] +name = "IxSetTheoryModel" diff --git a/Models/SetTheory/lean-toolchain b/Models/SetTheory/lean-toolchain new file mode 100644 index 000000000..a8afa7d1b --- /dev/null +++ b/Models/SetTheory/lean-toolchain @@ -0,0 +1 @@ +leanprover/lean4:v4.33.1 diff --git a/README.md b/README.md index 5cd891617..37832c63a 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,20 @@ Ix consists of the following core components: - Integration with the [iroh p2p network](https://www.iroh.computer/) so that different ix users can easily share `ixon` data between themselves. +### Kernel certification + +`Ix.Kernel` includes audited implementation proofs and a certified receipt +interface with relative consistency theorems under an explicit set-theory +assumption. Full consistency of the production `checkEnvAnon` checker remains +open. See the [kernel verification guide](docs/kernel-verification.md) for the +theorem boundaries and remaining obligations, and +[certified checking](docs/certified-checking.md) for runnable examples. + +Run `lake run check-kernel` to validate the implementation proofs, consistency +refinements, theory foundation, certified host adapters, and kernel unit tests. +Add `--with-model` to include the separate Mathlib `ZFSet` construction of the +set-theory instance under the inaccessible-cardinal hypothesis. + ## Benchmarks Benchmarks (compiler, kernel, and zk-prover backends) are tracked at diff --git a/Tests/Certified/CLI.lean b/Tests/Certified/CLI.lean new file mode 100644 index 000000000..b96ed8668 --- /dev/null +++ b/Tests/Certified/CLI.lean @@ -0,0 +1,264 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Lean + +/-! Native command regressions ported from the frozen source and claim CLI drivers. +JSON is retained for the existing command protocol and differential evidence. -/ + +open Lean System + +namespace Tests.Certified.CLI + +private def readJson (path : FilePath) : IO Json := do + IO.ofExcept (Json.parse (← IO.FS.readFile path)) + +private def writeJson (path : FilePath) (value : Json) : IO Unit := + IO.FS.writeFile path (value.pretty ++ "\n") + +private def field (value : Json) (key : String) : Json := value.getObjValD key +private def array (value : Json) : Array Json := value.getArr?.toOption.get! +private def string (value : Json) : String := value.getStr?.toOption.get! +private def set := Json.setObjVal! + +private def directories (path : FilePath) : IO (Array FilePath) := do + let entries ← path.readDir + let mut result := #[] + for entry in entries do + if ← entry.path.isDir then result := result.push entry.path + return result.qsort (fun a b => a.toString < b.toString) + +private def name (path : FilePath) : String := path.fileName.get! +private def truncate (bytes : ByteArray) := bytes.extract 0 (bytes.size / 2) +private def corrupt (bytes : ByteArray) := bytes.set! 0 (bytes[0]! ^^^ 255) +private def zeros : Json := .str (String.ofList (List.replicate 64 '0')) + +private def check (executable : String) (directory : FilePath) (source : ByteArray) + (request : Json) (expected : Bool) (mode : Option String := none) + (envelope : ByteArray := ByteArray.empty) : IO Json := do + IO.FS.createDirAll directory + let sourcePath := directory / "source.ixe" + let requestPath := directory / "request.json" + IO.FS.writeBinFile sourcePath source + writeJson requestPath request + let (args, success) ← match mode with + | some mode => pure (#[mode, sourcePath.toString, requestPath.toString], + Json.mkObj [("accepted", .bool true), ("mode", .str mode)]) + | none => do + let envelopePath := directory / "envelope.bin" + IO.FS.writeBinFile envelopePath envelope + pure (#[sourcePath.toString, envelopePath.toString, requestPath.toString], + Json.mkObj [("accepted", .bool true), ("address", field request "address")]) + let result ← IO.Process.output { cmd := "timeout", args := #["60", executable] ++ args } + IO.FS.writeFile (directory / "stdout.txt") result.stdout + IO.FS.writeFile (directory / "stderr.txt") result.stderr + unless result.exitCode == 0 || result.exitCode == 1 do + throw (IO.userError s!"abnormal exit {directory}: {result.exitCode}\n{result.stderr}") + let accepted := result.exitCode == 0 + unless accepted == expected do + throw (IO.userError s!"unexpected acceptance {directory}: {accepted}\n{result.stderr}") + if accepted then + unless (← IO.ofExcept (Json.parse result.stdout)) == success do + throw (IO.userError s!"incorrect success output {directory}: {result.stdout}") + else unless result.stdout.trimAscii.isEmpty do + throw (IO.userError s!"rejected command printed a success result {directory}: {result.stdout}") + return Json.mkObj [("accepted", .bool accepted), ("exitCode", toJson result.exitCode.toNat)] + +private def acceptedCount (records : Array Json) : Nat := + (records.filter (fun record => field record "accepted" == .bool true)).size + +private def finish (output : FilePath) (records : Array Json) (fields : List (String × Json)) : IO Unit := do + writeJson (output / "results.json") (.arr records) + let report := Json.mkObj (fields ++ [ + ("rejected", toJson (records.size - acceptedCount records)), ("unexpectedErrors", toJson (0 : Nat))]) + writeJson (output / "summary.json") report + IO.println report.compress + +private def sourceTests (executable : String) (inputs output : FilePath) : IO Unit := do + let cases ← directories inputs + unless cases.size == 42 do throw (IO.userError "expected the 42-case source corpus") + let mut records := #[] + for directory in cases do + let bytes ← IO.FS.readBinFile (directory / "source.ixe") + let request ← readJson (directory / "request.json") + let objects := array (field request "objects") + let mut variants := #[ + ("valid", bytes, request, true), + ("missing-target", bytes, set request "objects" (.arr (objects.filter (· != field request "target"))), false), + ("duplicate-object", bytes, set request "objects" (.arr (objects.push objects[0]!)), false), + ("wrong-false-pin", bytes, set request "falseType" (field request "target"), false), + ("malformed-address", bytes, set request "falseElim" (.str "01"), false), + ("truncated-source", truncate bytes, request, false), + ("invalid-header", corrupt bytes, request, false)] + if field request "natType" != .null then + variants := variants.push ("missing-nat-pin", bytes, set request "natType" .null, false) + for (scenario, source, requested, expected) in variants do + for mode in #["proof", "store"] do + let record ← check executable (output / name directory / scenario / mode) + source requested expected (some mode) + records := records.push (set (set (set record "case" (.str (name directory))) + "scenario" (.str scenario)) "mode" (.str mode)) + let record ← check executable (output / name directory / "unsupported-mode" / "infer-only") + bytes request false (some "infer-only") + records := records.push (set (set (set record "case" (.str (name directory))) + "scenario" (.str "unsupported-mode")) "mode" (.str "infer-only")) + unless acceptedCount records == 84 do throw (IO.userError "missing source success coverage") + finish output records [("cases", toJson cases.size), ("proofAccepted", toJson (42 : Nat)), + ("storesAccepted", toJson (42 : Nat))] + +private def claimTests (executable : String) (inputs output : FilePath) : IO Unit := do + let cases ← directories inputs + let required := #["cycle-A-assuming-B", "cycle-B-assuming-A", "cycle-closed-AB", "cycle-closed-BA", + "cycle-own-assumption", "contains-padding", "contains-as-logical"] + unless cases.size ≥ 1500 && required.all (fun x => (cases.map name).contains x) do + throw (IO.userError "expected complete claim, cycle, catalog and structural coverage") + let mut records := #[] + for directory in cases do + let source ← IO.FS.readBinFile (directory / "source.ixe") + let envelope ← IO.FS.readBinFile (directory / "envelope.bin") + let request ← readJson (directory / "request.json") + let expected := field (← readJson (directory / "expected.json")) "accepted" == .bool true + let record ← check executable (output / name directory / "original") source request expected none envelope + records := records.push (set (set (set record "case" (.str (name directory))) + "scenario" (.str "original")) "kind" (field request "kind")) + unless expected do continue + let mut variants := #[ + ("wrong-digest", source, envelope, set request "address" zeros), + ("short-address", source, envelope, set request "address" (.str "01")), + ("unsupported-kind", source, envelope, set request "kind" (.str "infer-only")), + ("changed-envelope", source, corrupt envelope, request), + ("trailing-envelope", source, envelope.push 0, request), + ("truncated-envelope", source, truncate envelope, request), + ("truncated-source", truncate source, envelope, request)] + if field request "kind" == .str "logical" then + let objects := array (field request "objects") + variants := variants.push ("duplicate-object", source, envelope, + set request "objects" (.arr (objects.push objects[0]!))) + variants := variants.push ("missing-leaves", source, envelope, set request "leaves" (.arr #[])) + if field request "axioms" != .null then + variants := variants.push ("omitted-logical-axioms", source, envelope, set request "axioms" .null) + for key in #["subjects", "members", "frontier", "axioms"] do + if field request key != .null then + variants := variants.push (s!"trailing-{key}", source, envelope, + set request key (.str (string (field request key) ++ "00"))) + let leaves := array (field request "leaves") + let leaf := leaves[0]! + variants := variants.push ("trailing-leaf-claim", source, envelope, + set request "leaves" (.arr (leaves.set! 0 + (set leaf "claim" (.str (string (field leaf "claim") ++ "00")))))) + for (scenario, changedSource, changedEnvelope, changedRequest) in variants do + let record ← check executable (output / name directory / scenario) + changedSource changedRequest false none changedEnvelope + records := records.push (set (set (set record "case" (.str (name directory))) + "scenario" (.str scenario)) "kind" (field changedRequest "kind")) + unless acceptedCount records ≥ 500 do throw (IO.userError "missing claim nonvacuity coverage") + finish output records [("cases", toJson cases.size), ("accepted", toJson (acceptedCount records))] + +private def modelVariants (request : Json) : Array (String × Json) := Id.run do + let models := array (field request "models") + let model := models[0]! + let recursors := array (field model "recursors") + let targets := array (field model "targets") + let withoutModels := match request with + | .obj fields => Json.obj (fields.erase "models") + | _ => request + let mut variants := #[("missing-models", withoutModels), + ("malformed-models", set request "models" (.bool true))] + let edits := #[ + ("unknown-model-source", "source", zeros), ("short-model-address", "source", .str "01"), + ("recursor-order", "recursors", .arr recursors.reverse), + ("target-order", "targets", .arr targets.reverse), + ("missing-target", "targets", .arr targets.pop), + ("circular-target", "targets", .arr (targets.set! (targets.size - 1) recursors.back!)), + ("unknown-target", "targets", .arr (targets.set! (targets.size - 1) zeros)), + ("unknown-recursor", "recursors", .arr (recursors.set! (recursors.size - 1) zeros)), + ("missing-recursor", "recursors", .arr (recursors.extract 1 recursors.size))] + for (scenario, key, value) in edits do + variants := variants.push (scenario, set request "models" (.arr (models.set! 0 (set model key value)))) + let rulesArray := array (field model "proofs") + unless rulesArray.isEmpty do + let rules := rulesArray[0]! + let proofs := array (field rules "proofs") + for scenario in #["wrong-equation-owner", "wrong-equation-proof", "wrong-equality-family", + "wrong-reflexivity", "wrong-equality-eliminator", "malformed-proof-list"] do + let changed := if scenario == "wrong-equation-owner" then set rules "owner" recursors.back! + else if scenario == "malformed-proof-list" then set rules "proofs" (.bool true) + else Id.run do + let index := proofs.findIdx? (· != .null) |>.get! + let key := if scenario == "wrong-equation-proof" then "proof" + else if scenario == "wrong-equality-family" then "equality" + else if scenario == "wrong-reflexivity" then "reflexivity" else "eliminator" + return set rules "proofs" (.arr (proofs.set! index (set proofs[index]! key targets[0]!))) + let changedModel := set model "proofs" (.arr (rulesArray.set! 0 changed)) + variants := variants.push (scenario, set request "models" (.arr (models.set! 0 changedModel))) + return variants + +private def positives := #["mutual-unequal", "mutual-permuted", "auxiliary-merged", "nested-parameter", + "nested-recursive-Pi", "propositional-equations"] + +private def modeledTests (sourceExe claimExe : String) (inputs output : FilePath) : IO Unit := do + let sourceCases ← directories (inputs / "source") + let claimCases ← directories (inputs / "claims") + let mut records := #[] + let mut acceptedSources := #[] + for directory in sourceCases do + let source ← IO.FS.readBinFile (directory / "source.ixe") + let request ← readJson (directory / "request.json") + let expected ← readJson (directory / "expected.json") + if field expected "proof" == .bool true && field expected "store" == .bool true then + acceptedSources := acceptedSources.push (name directory) + for mode in #["proof", "store"] do + let accept := field expected mode == .bool true + let mut variants := #[("original", source, request, accept)] + if accept then + variants := variants ++ (modelVariants request).map (fun (scenario, changed) => + (scenario, source, changed, false)) + variants := variants.push ("truncated-source", truncate source, request, false) + for (scenario, changedSource, changedRequest, accept) in variants do + let record ← check sourceExe (output / "source" / name directory / scenario / mode) + changedSource changedRequest accept (some mode) + records := records.push (set (set (set (set record "kind" (.str "source")) + "case" (.str (name directory))) "scenario" (.str scenario)) "mode" (.str mode)) + unless sourceCases.size == 44 && acceptedSources.qsort (· < ·) == positives.qsort (· < ·) do + throw (IO.userError "missing complete modeled source controls") + let mut acceptedClaims := #[] + for directory in claimCases do + let source ← IO.FS.readBinFile (directory / "source.ixe") + let envelope ← IO.FS.readBinFile (directory / "envelope.bin") + let request ← readJson (directory / "request.json") + let expected := field (← readJson (directory / "expected.json")) "accepted" == .bool true + let mut variants := #[("original", envelope, request, expected)] + if expected then + acceptedClaims := acceptedClaims.push (name directory) + variants := variants ++ (modelVariants request).map (fun (scenario, changed) => + (scenario, envelope, changed, false)) + variants := variants.push ("changed-public-bytes", corrupt envelope, request, false) + variants := variants.push ("trailing-public-bytes", envelope.push 0, request, false) + for (scenario, changedEnvelope, changedRequest, accept) in variants do + let record ← check claimExe (output / "claims" / name directory / scenario / "claim") + source changedRequest accept none changedEnvelope + records := records.push (set (set (set (set record "kind" (.str "claims")) + "case" (.str (name directory))) "scenario" (.str scenario)) "mode" .null) + let requiredClaims := positives.flatMap (fun caseName => + #["check", "environment", "shared-model"].map (fun kind => s!"{caseName}-{kind}")) + unless claimCases.size == 74 && acceptedClaims.qsort (· < ·) == requiredClaims.qsort (· < ·) do + throw (IO.userError "missing complete modeled claim controls") + finish output records [("sourceCases", toJson sourceCases.size), ("claimCases", toJson claimCases.size), + ("accepted", toJson (acceptedCount records))] + +end Tests.Certified.CLI + +def main (args : List String) : IO UInt32 := do + match args with + | [kind, sourceExe, claimExe, inputs, output] => + match kind with + | "source" => Tests.Certified.CLI.sourceTests sourceExe inputs output + | "claims" => Tests.Certified.CLI.claimTests claimExe inputs output + | "modeled" => Tests.Certified.CLI.modeledTests sourceExe claimExe inputs output + | _ => throw (IO.userError s!"unknown corpus: {kind}") + return 0 + | _ => + IO.eprintln "usage: certified-cli-tests (source|claims|modeled) SOURCE_EXE CLAIM_EXE INPUTS OUTPUTS" + return 2 diff --git a/Tests/Certified/Check.lean b/Tests/Certified/Check.lean new file mode 100644 index 000000000..25ac2055e --- /dev/null +++ b/Tests/Certified/Check.lean @@ -0,0 +1,144 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Lean +import Tests.Certified.ImportManifest + +/-! Independent provenance and byte/command regressions for the maintained +certified adapters. All historical inputs come from the local frozen archive. -/ + +open Lean System Tests.Certified.ImportManifest + +namespace Tests.Certified.Check + +private def need (condition : Bool) (message : String) : IO Unit := + unless condition do throw (IO.userError message) + +private def run (cmd : String) (args : Array String) : IO IO.Process.Output := do + let result ← IO.Process.output { cmd, args } + need (result.exitCode == 0) s!"{cmd} failed ({result.exitCode}): {result.stdout}{result.stderr}" + return result + +private def sha256 (path : FilePath) : IO String := do + return ((← run "sha256sum" #["--", path.toString]).stdout.splitOn " ").headD "" + +private def files (directory : FilePath) : IO (Array String) := do + let mut result := #[] + for path in ← directory.walkDir do + unless ← path.isDir do + result := result.push ((path.toString.drop (directory.toString.length + 1)).toString) + return result.qsort (· < ·) + +private def compareTree (expected actual : FilePath) (jsonEquivalent : Bool := false) : IO Unit := do + let expectedFiles ← files expected + let actualFiles ← files actual + need (expectedFiles == actualFiles) s!"fixture inventory differs: {expected} / {actual}" + for relative in expectedFiles do + let old ← IO.FS.readBinFile (expected / relative) + let current ← IO.FS.readBinFile (actual / relative) + if old == current then continue + if jsonEquivalent && (FilePath.mk relative).extension == some "json" then + let old ← IO.ofExcept (Json.parse (String.fromUTF8! old)) + let current ← IO.ofExcept (Json.parse (String.fromUTF8! current)) + need (old == current) s!"JSON protocol value differs: {relative}" + else throw (IO.userError s!"fixture bytes differ: {relative} ({expected} / {actual})") + +private def unpack (directory : FilePath) : IO Unit := do + need ((← sha256 archive) == archiveSha256) "frozen adapter archive identity changed" + IO.FS.createDirAll directory + let _ ← run "tar" #["-xzf", archive, "--no-same-owner", "--no-same-permissions", "-C", directory.toString] + for row in selected do + need ((← sha256 (directory / "adapters" / row.source)) == row.sourceSha256) + s!"frozen adapter source identity changed: {row.source}" + if let some target := row.target then + need ((← sha256 target) == row.importedSha256) + s!"maintained adapter changed; review import manifest: {target}" + IO.println s!"Certified adapter provenance passed: {selected.size} frozen sources, {(selected.filter (·.target.isSome)).size} maintained ports." + +private def nativeCases := #[ + ("feature", "certified-feature-tests", none), + ("ordinary", "certified-ordinary-tests", none), + ("source", "certified-source-tests", some "inputs"), + ("fidelity", "certified-fidelity-tests", some "fidelity-inputs"), + ("claim", "certified-claim-tests", some "claim-inputs"), + ("modeled", "certified-modeled-tests", some "modeled-inputs")] + +/-- The historical host reports also recorded the separate VM pilot's +refusals. Project away exactly those two fields; all host results, record +order, source/envelope bytes and command behavior still have to match. -/ +private def hostReport (contents : String) : Except String (Array Json) := do + (contents.splitOn "\n" |>.filter (!·.isEmpty)).toArray.mapM fun line => do + let .obj fields ← Json.parse line | throw "expected a JSON object in a host test report" + return .obj (fields.erase "pilotDeclined" |>.erase "pilotDeclines") + +private def compareNative (frozen actual : FilePath) : IO Unit := do + for (label, _, _) in nativeCases do + let expected ← IO.FS.readFile (frozen / s!"evidence/c7/{label}-tests.txt") + let current ← IO.FS.readFile (actual / s!"{label}-tests.txt") + if #["feature", "ordinary", "modeled"].contains label then + need ((← IO.ofExcept (hostReport expected)) == (← IO.ofExcept (hostReport current))) + s!"native host report differs: {label}" + else + need (expected == current) s!"native report differs: {label}" + if #["source", "fidelity", "claim", "modeled"].contains label then + need ((← IO.FS.readFile (frozen / s!"evidence/c7/{label}-loader.txt")) == + (← IO.FS.readFile (actual / s!"{label}-loader.txt"))) s!"loader report differs: {label}" + for (old, current) in #[ + ("c5/inputs", "inputs"), ("c6/fidelity-inputs", "fidelity-inputs"), + ("c7/claim-inputs", "claim-inputs"), ("c7/modeled-inputs", "modeled-inputs")] do + compareTree (frozen / "evidence" / old) (actual / current) + IO.println "Native reports and all generated source/envelope fixtures match frozen adapter bytes." + +private def compareCLI (frozen actual : FilePath) : IO Unit := do + for (old, current) in #[ + ("c5/cli-inputs", "cli-source"), ("c7/claim-cli-inputs", "cli-claims"), + ("c7/modeled-cli-inputs", "cli-modeled")] do + -- The new Lean driver prints JSON differently. Compare parsed protocol + -- values; source, envelope, stdout and stderr remain byte-exact checks. + compareTree (frozen / "evidence" / old) (actual / current) true + IO.println "All CLI scenarios match the frozen inventory, protocol values, source/envelope bytes and process results." + +private def runNative (frozen output : FilePath) : IO Unit := do + for (label, executable, directory) in nativeCases do + let args := directory.toArray.map (fun item => (output / FilePath.mk item).toString) + let result ← run s!".lake/build/bin/{executable}" args + IO.FS.writeFile (output / s!"{label}-tests.txt") result.stdout + IO.FS.writeFile (output / s!"{label}-loader.txt") result.stderr + IO.println s!"Native {label} tests passed." + compareNative frozen output + +private def runCLI (frozen output : FilePath) : IO Unit := do + for (kind, inputs, target) in #[ + ("source", "inputs", "cli-source"), ("claims", "claim-inputs", "cli-claims"), + ("modeled", "modeled-inputs", "cli-modeled")] do + let result ← run ".lake/build/bin/certified-cli-tests" #[kind, + ".lake/build/bin/certified-check", ".lake/build/bin/certified-claim-check", + (output / inputs).toString, (output / target).toString] + IO.print s!"CLI {kind}: {result.stdout}" + compareCLI frozen output + +def main (args : List String) : IO UInt32 := do + match args with + | [] => + IO.FS.withTempDir fun directory => do + let frozen := directory / "frozen" + let output := directory / "actual" + unpack frozen + IO.FS.createDirAll output + runNative frozen output + runCLI frozen output + IO.println "Certified adapter checks passed." + return 0 + | ["--compare", frozen, actual] => + compareNative frozen actual + compareCLI frozen actual + return 0 + | _ => + IO.eprintln "usage: certified-adapter-tests [--compare FROZEN_DIRECTORY ACTUAL_DIRECTORY]" + return 2 + +end Tests.Certified.Check + +def main := Tests.Certified.Check.main diff --git a/Tests/Certified/Claims.lean b/Tests/Certified/Claims.lean new file mode 100644 index 000000000..b9ee1f2d5 --- /dev/null +++ b/Tests/Certified/Claims.lean @@ -0,0 +1,375 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Certified.FeatureCases +import Tests.Certified.Source +import Ix.Certified.ClaimSuggest +import Ix.Certified.ClaimCommand +import Tests.Theory.Claims + +/-! Native tests of the versioned claim byte boundary. All hints are built +outside the checker and every outcome goes through `acceptsClaimBytes`. -/ + +namespace Tests.Certified.Claims + +open Ix.Theory Ix.Theory.Certified Ix.Theory.Model Ix.Certified Serialize +open Lean (toJson) + +set_option maxRecDepth 32768 +set_option maxHeartbeats 32000000 + +def fuel : Nat := 6400 + +def tree : List Address → Ix.AssumptionTree + | [] => .padding + | [a] => .leaf a + | a :: rest => .node (.leaf a) (tree rest) + +def optionalRoot (addresses : List Address) : Option Address := + if addresses.isEmpty then none else some (treeRoot (tree addresses)) + +def optionalBytes (addresses : List Address) : Option ByteArray := + if addresses.isEmpty then none else some (treeBytes (tree addresses)) + +structure Fixture where + name : String + source : Case + envelope : Envelope + hint : LogicalHint + witness : LogicalWitness + +def Fixture.bytes (f : Fixture) : ByteArray := envelopeBytes f.envelope +def Fixture.address (f : Fixture) : Address := Address.blake3 f.bytes +def Fixture.accepted (f : Fixture) : Bool := + acceptsClaimBytes.{0} fuel (Source.source f.source) f.address f.bytes (.logical f.witness) + +/-- Untrusted test producer computes the logical-use tree from the checked +leaf manifest. The public acceptance call independently recomputes it. -/ +def finish? (name : String) (c : Case) (claim : Ix.Claim) (hint : LogicalHint) : Option Fixture := do + let envelope : Envelope := ⟨Protocol.current, c.profile, claim, none⟩ + let source := Source.source c + let witness ← suggestLogical? fuel source envelope hint + let (snapshot, _) ← readSnapshot? fuel source hint.selection {} + let signature ← readSignature? c.profile snapshot.decodedObjects + let store ← readStore? fuel snapshot.decodedObjects snapshot.decodedNaturals + let leaves ← witness.leaves.mapM (readLeafInput? fuel snapshot.decodedObjects) + let batch ← checkBatch?.{0,0} fuel signature store witness.frontier + (leaves.map (fun leaf => leaf.prepared.node signature)) + let axioms := hint.selection.objects.filter fun address => + (subjectReferences? snapshot.decodedObjects address).any fun refs => + !refs.isEmpty && refs.all batch.logicalUses.contains + let axiomTree := optionalBytes axioms + return ⟨name, c, { envelope with logicalAxioms := optionalRoot axioms }, + { hint with axiomTree }, { witness with axiomTree }⟩ + +def single? (c : Case) : Option Fixture := + let claim : Ix.Claim := .check c.target none + finish? (c.name ++ "-check") c claim + ⟨Source.selection c, [⟨claim, none, none⟩], none, none, none, none, []⟩ + +def environment? (c : Case) : Option Fixture := + let subjects := tree (Source.subjects c) + let claim : Ix.Claim := .checkEnv (treeRoot subjects) none + finish? (c.name ++ "-environment") c claim + ⟨Source.selection c, [⟨claim, some (treeBytes subjects), none⟩], + some (treeBytes subjects), none, none, none, []⟩ + +def rejectsEnvelope (f : Fixture) (envelope : Envelope) : Bool := + let bytes := envelopeBytes envelope + !acceptsClaimBytes.{0} fuel (Source.source f.source) (Address.blake3 bytes) bytes (.logical f.witness) + +def hostile (f : Fixture) : Bool := Id.run do + let source := Source.source f.source + let check := fun w => acceptsClaimBytes.{0} fuel source f.address f.bytes (.logical w) + if !f.accepted then return false + if acceptsClaimBytes.{0} 0 source f.address f.bytes (.logical f.witness) then return false + if acceptsClaimBytes.{0} fuel source f.source.target f.bytes (.logical f.witness) then return false + let trailing := f.bytes.push 0 + if acceptsClaimBytes.{0} fuel source (Address.blake3 trailing) trailing (.logical f.witness) then return false + for protocol in [ + { Protocol.current with format := 2 }, { Protocol.current with codec := 2 }, + { Protocol.current with checker := Protocol.current.checker + 1 }, { Protocol.current with policy := 2 }, + { Protocol.current with aggregation := 2 }] do + if !rejectsEnvelope f { f.envelope with protocol } then return false + if !rejectsEnvelope f { f.envelope with profile := { f.envelope.profile with + falseType := f.source.profile.falseElim, falseElim := f.source.profile.falseType } } then return false + if check { f.witness with leaves := [] } then return false + if check { f.witness with leaves := f.witness.leaves.map fun leaf => { leaf with declarations := [] } } then + return false + if check { f.witness with selection := { f.witness.selection with + objects := f.source.target :: f.witness.selection.objects } } then return false + if check { f.witness with selection := { f.witness.selection with + objects := f.witness.selection.objects.filter (· != f.source.target) } } then return false + let forged := treeBytes (.leaf f.source.target) + if check { f.witness with frontierTree := some forged } then return false + if check { f.witness with axiomTree := some forged } then return false + if check { f.witness with leaves := f.witness.leaves.map fun leaf => + { leaf with + claim := .contains (treeRoot (.leaf f.source.target)) f.source.target, + subjects := none } } then return false + if !rejectsEnvelope f { f.envelope with claim := .eval f.source.target f.source.target none } then + return false + if f.envelope.logicalAxioms.isSome && !rejectsEnvelope f { f.envelope with logicalAxioms := none } then + return false + return true + +def diamondCase? : Option Case := + let T := (Tests.Theory.Checker.identityType (.param 0)).erase + let store := Tests.Theory.Claims.store fun n c => if n = 14 then + .defn 1 .theorem T (.app (.lam T (.bvar 0)) (.const (.member 12 0) [.param 0])) .safe else c + make? "diamond" Tests.Theory.Certified.primitives + ⟨store, 1, .const (.member 15 0) [.param 0], T⟩ + +def checkLeaf (subject : Address) (frontier : List Address) : LeafHint := + ⟨.check subject (optionalRoot frontier), none, optionalBytes frontier⟩ + +def envLeaf (subjects frontier : List Address) : LeafHint := + ⟨.checkEnv (treeRoot (tree subjects)) (optionalRoot frontier), + some (treeBytes (tree subjects)), optionalBytes frontier⟩ + +def diamond? (openFrontier : Bool := false) (catalog : Bool := false) : Option Fixture := do + let c ← diamondCase? + let a ← reference c.references (.member 12 0) + let b ← reference c.references (.member 13 0) + let d ← reference c.references (.member 14 0) + let e ← reference c.references (.member 15 0) + let specs := if openFrontier then [([b], [a]), ([d], [a]), ([e], [b, d])] + else [([a], []), ([b], [a]), ([d], [a]), ([e], [b, d])] + let leaves := specs.map fun (subjects, frontier) => + if catalog then envLeaf subjects frontier else checkLeaf subjects[0]! frontier + let content := tree (specs.flatMap Prod.fst) + let frontier := if openFrontier then [a] else [] + let members := tree (leaves.filterMap fun leaf => environmentRoot? leaf.claim) + let claim := if catalog then .catalog (treeRoot members) (treeRoot content) (optionalRoot frontier) + else .checkEnv (treeRoot content) (optionalRoot frontier) + finish? s!"diamond-{openFrontier}-{catalog}" c claim + ⟨Source.selection c, leaves, some (treeBytes content), + if catalog then some (treeBytes members) else none, optionalBytes frontier, none, []⟩ + +def diamondTests (f : Fixture) : Bool := Id.run do + if !f.accepted then return false + let source := Source.source f.source + let check := fun w => acceptsClaimBytes.{0} fuel source f.address f.bytes (.logical w) + if check { f.witness with leaves := f.witness.leaves.reverse } then return false + if check { f.witness with leaves := f.witness.leaves.drop 1 } then return false + if !check { f.witness with leaves := f.witness.leaves ++ f.witness.leaves } then return false + if check { f.witness with frontier := [] } && (claimFrontier f.envelope.claim).isSome then return false + if check { f.witness with members := f.witness.subjects } && f.witness.members.isSome then return false + return true + +def warm (f : Fixture) : Bool := Id.run do + let source := Source.source f.source + let step := Ix.Kernel.certifiedClaimStep.{0} fuel source f.address f.bytes (.logical f.witness) + let .ok _ cold := step.run Ix.Kernel.initialCertifiedState | return false + let start := { cold with checker := Source.poisonedChecker f.source } + let .ok _ cached := step.run start | return false + let size := f.witness.selection.objects.length + f.witness.selection.naturals.length + if cached.inputCache.hits != size || cached.inputCache.misses != size then return false + let .error _ failed := (Ix.Kernel.certifiedClaimStep.{0} fuel source f.address f.bytes + (.logical { f.witness with leaves := [] })).run cached | return false + if failed.inputCache.hits != cached.inputCache.hits || failed.inputCache.misses != cached.inputCache.misses then + return false + let changed := { source with consts := source.consts.erase f.source.target } + let .error _ absent := (Ix.Kernel.certifiedClaimStep.{0} fuel changed f.address f.bytes (.logical f.witness)).run failed + | return false + if absent.inputCache.hits != cached.inputCache.hits || absent.inputCache.misses != cached.inputCache.misses then + return false + let .ok _ retry := step.run absent | return false + return retry.inputCache.hits == 2 * size && retry.inputCache.misses == size && retry.checker.inferOnly + +structure WireCase where + name : String + source : Case + envelope : Envelope + hint : ClaimCommand.Hint + expected : Bool := true + +def WireCase.bytes (w : WireCase) : ByteArray := envelopeBytes w.envelope +def WireCase.request (w : WireCase) : ClaimCommand.Request := ⟨Address.blake3 w.bytes, w.hint⟩ +def WireCase.passes (w : WireCase) : Bool := + (ClaimCommand.run.{0} fuel (Source.source w.source) w.bytes w.request).isOk == w.expected + +def Fixture.wire (f : Fixture) : WireCase := ⟨f.name, f.source, f.envelope, .logical f.hint, true⟩ + +def rawFalseAlias (index : UInt64) : Ixon.MutConst := .defn { + kind := .thm, safety := .safe, lvls := 0, typ := .ref 0 #[], value := .recur index #[] } + +/-- Self-block indices make this a finite, canonically hashed source cycle; +there is no assumed collision or circular hash-address construction. -/ +def cycleCase : Case := + let block := Fixtures.encode ⟨.muts #[rawFalseAlias 1, rawFalseAlias 0], #[], #[Fixtures.profile.falseType], #[]⟩ + let a := Fixtures.encode ⟨.dPrj ⟨0, block.1⟩, #[], #[], #[]⟩ + let b := Fixtures.encode ⟨.dPrj ⟨1, block.1⟩, #[], #[], #[]⟩ + ⟨"cycle", Fixtures.profile, a.1, Fixtures.prelude ++ [block, a, b], [], + [(.member 20 0, a.1), (.member 20 1, b.1)], [(20, block.1)]⟩ + +def cycleWires? : Option (List WireCase) := do + let c := cycleCase + let a ← reference c.references (.member 20 0) + let b ← reference c.references (.member 20 1) + let left := checkLeaf a [b] + let right := checkLeaf b [a] + let one := fun leaf => LogicalHint.mk (Source.selection c) [leaf] none none leaf.frontierTree none [] + let subjects := tree [a, b] + let envelope : Envelope := ⟨Protocol.current, c.profile, .checkEnv (treeRoot subjects) none, none⟩ + let both := fun leaves => LogicalHint.mk (Source.selection c) leaves (some (treeBytes subjects)) none none none [] + return [ + (← finish? "cycle-A-assuming-B" c left.claim (one left)).wire, + (← finish? "cycle-B-assuming-A" c right.claim (one right)).wire, + ⟨"cycle-closed-AB", c, envelope, .logical (both [left, right]), false⟩, + ⟨"cycle-closed-BA", c, envelope, .logical (both [right, left]), false⟩, + ⟨"cycle-own-assumption", c, ⟨Protocol.current, c.profile, (checkLeaf a [a]).claim, none⟩, + .logical (one (checkLeaf a [a])), false⟩] + +def containsWires (c : Case) : List WireCase := + let content : Ix.AssumptionTree := .node (.leaf c.target) (.node .padding (.leaf c.profile.falseType)) + let claim := Ix.Claim.contains (treeRoot content) c.target + let envelope : Envelope := ⟨Protocol.current, c.profile, claim, none⟩ + [⟨"contains-padding", c, envelope, .contains (treeBytes content), true⟩, + ⟨"contains-absent", c, { envelope with claim := .contains (treeRoot content) c.profile.falseElim }, + .contains (treeBytes content), false⟩, + ⟨"contains-wrong-root", c, { envelope with claim := .contains c.target c.target }, + .contains (treeBytes content), false⟩, + ⟨"contains-trailing-tree", c, envelope, .contains ((treeBytes content).push 0), false⟩, + ⟨"contains-logical-axioms", c, { envelope with logicalAxioms := some (treeRoot content) }, + .contains (treeBytes content), false⟩, + ⟨"contains-as-logical", c, envelope, + .logical ⟨Source.selection c, [⟨claim, none, none⟩], none, none, none, none, []⟩, false⟩] + +def revealCtor (ctor : Ixon.Constructor) : Ix.RevealConstructorInfo := + ⟨some ctor.isUnsafe, some ctor.lvls, some ctor.cidx, some ctor.params, some ctor.fields, + some (expressionAddress ctor.typ)⟩ + +def revealCtors (ctors : Array Ixon.Constructor) : Option (Array (UInt64 × Ix.RevealConstructorInfo)) := + some (ctors.toList.zipIdx.map fun (ctor, index) => (index.toUInt64, revealCtor ctor)).toArray + +def revealRules (rules : Array Ixon.RecursorRule) : Option (Array Ix.RevealRecursorRule) := + some (rules.toList.zipIdx.map fun (rule, index) => + ⟨index.toUInt64, rule.fields, expressionAddress rule.rhs⟩).toArray + +def revealMut : Ixon.MutConst → Ix.RevealMutConstInfo + | .defn d => .defn (some d.kind) (some d.safety) (some d.lvls) + (some (expressionAddress d.typ)) (some (expressionAddress d.value)) + | .indc d => .indc (some d.isUnsafe) (some d.lvls) (some d.params) (some d.indices) + (some (expressionAddress d.typ)) (revealCtors d.ctors) + | .recr d => .recr (some d.k) (some d.isUnsafe) (some d.lvls) (some d.params) (some d.indices) + (some d.motives) (some d.minors) (some (expressionAddress d.typ)) (revealRules d.rules) + +def revealInfo : Ixon.ConstantInfo → Ix.RevealConstantInfo + | .defn d => .defn (some d.kind) (some d.safety) (some d.lvls) + (some (expressionAddress d.typ)) (some (expressionAddress d.value)) + | .recr d => .recr (some d.k) (some d.isUnsafe) (some d.lvls) (some d.params) (some d.indices) + (some d.motives) (some d.minors) (some (expressionAddress d.typ)) (revealRules d.rules) + | .axio d => .axio (some d.isUnsafe) (some d.lvls) (some (expressionAddress d.typ)) + | .quot d => .quot (some d.kind) (some d.lvls) (some (expressionAddress d.typ)) + | .cPrj d => .cPrj (some d.idx) (some d.cidx) (some d.block) + | .iPrj d => .iPrj (some d.idx) (some d.block) + | .rPrj d => .rPrj (some d.idx) (some d.block) + | .dPrj d => .dPrj (some d.idx) (some d.block) + | .muts ds => .muts (ds.toList.zipIdx.map fun (d, index) => (index.toUInt64, revealMut d)).toArray + +def corruptInfo : Ix.RevealConstantInfo → Ix.RevealConstantInfo + | .defn k s u t b => .defn k s ((u.getD 0 + 1) |> some) t b + | .recr k s u p i m n t r => .recr k s u p i (some (m.getD 0 + 1)) n t r + | .axio s u t => .axio (some (!(s.getD false))) u t + | .quot k u t => .quot k (some (u.getD 0 + 1)) t + | .cPrj i c b => .cPrj i (some (c.getD 0 + 1)) b + | .iPrj i b => .iPrj (some (i.getD 0 + 1)) b + | .rPrj i b => .rPrj (some (i.getD 0 + 1)) b + | .dPrj i b => .dPrj (some (i.getD 0 + 1)) b + | .muts ds => .muts (ds.push (100000, .defn none none none none none)) + +def revealWires? (c : Case) : Option (List WireCase) := do + let mut result := [] + for ((address, bytes), index) in c.blobs.zipIdx do + let object ← decodeObject? address bytes + let info := revealInfo object.val.info + let opening : Ixon.Comm := ⟨Address.blake3 "C6 reveal secret".toUTF8, address⟩ + let envelope : Envelope := ⟨Protocol.current, c.profile, .reveal opening.commit info, none⟩ + result := result ++ [ + ⟨s!"{c.name}-reveal-{index}", c, envelope, .reveal ⟨opening⟩, true⟩, + ⟨s!"{c.name}-reveal-altered-{index}", c, + { envelope with claim := .reveal opening.commit (corruptInfo info) }, .reveal ⟨opening⟩, false⟩, + ⟨s!"{c.name}-reveal-secret-{index}", c, envelope, + .reveal ⟨{ opening with secret := c.target }⟩, false⟩] + return result + +def hexJson (bytes : Option ByteArray) : Lean.Json := toJson (bytes.map hexOfBytes) + +def leafJson (hint : LeafHint) : Lean.Json := Lean.Json.mkObj [ + ("claim", toJson (hexOfBytes (Ix.Claim.ser hint.claim))), + ("subjects", hexJson hint.subjects), ("frontier", hexJson hint.frontierTree)] + +def requestJson (request : ClaimCommand.Request) : Lean.Json := + let fields := match request.hint with + | .logical hint => [ + ("kind", toJson "logical"), + ("objects", toJson (hint.selection.objects.map fun a => hexOfBytes a.hash)), + ("naturals", toJson (hint.selection.naturals.map fun a => hexOfBytes a.hash)), + ("leaves", toJson (hint.leaves.map leafJson)), + ("subjects", hexJson hint.subjects), ("members", hexJson hint.members), + ("frontier", hexJson hint.frontierTree), ("axioms", hexJson hint.axiomTree)] ++ + (if hint.models.isEmpty then [] else [("models", toJson (hint.models.map ModelHint.json))]) + | .contains bytes => [("kind", toJson "contains"), ("tree", toJson (hexOfBytes bytes))] + | .reveal witness => [("kind", toJson "reveal"), + ("secret", toJson (hexOfBytes witness.opening.secret.hash)), + ("payload", toJson (hexOfBytes witness.opening.payload.hash))] + Lean.Json.mkObj (("address", toJson (hexOfBytes request.address.hash)) :: fields) + +def writeWire (directory : System.FilePath) (w : WireCase) : IO Unit := do + let directory := directory / w.name + IO.FS.createDirAll directory + let .ok source := Ixon.serEnv (Source.source w.source) + | throw (IO.userError s!"cannot serialize {w.name}") + IO.FS.writeBinFile (directory / "source.ixe") source + IO.FS.writeBinFile (directory / "envelope.bin") w.bytes + IO.FS.writeFile (directory / "request.json") ((requestJson w.request).pretty ++ "\n") + IO.FS.writeFile (directory / "expected.json") + ((Lean.Json.mkObj [("accepted", toJson w.expected)]).compress ++ "\n") + +def runWire (directory : Option System.FilePath) (w : WireCase) : IO Unit := do + unless w.passes do throw (IO.userError s!"claim command outcome failed: {w.name}") + let .ok request := ClaimCommand.readRequest (requestJson w.request) + | throw (IO.userError s!"claim request parsing failed: {w.name}") + unless (ClaimCommand.run.{0} fuel (Source.source w.source) w.bytes request).isOk == w.expected do + throw (IO.userError s!"claim parsed request outcome failed: {w.name}") + if let some directory := directory then writeWire directory w + +def run (directory : Option System.FilePath) : IO Unit := do + let mut count := 0 + let mut axioms := 0 + let mut revelations := 0 + for c in Source.cases do + for (label, produce) in [("check", single?), ("environment", environment?)] do + let some f := produce c | throw (IO.userError s!"claim producer declined {c.name}/{label}") + unless f.accepted do throw (IO.userError s!"claim rejected {f.name}") + unless hostile f do throw (IO.userError s!"claim mutation escaped {f.name}") + unless warm f do throw (IO.userError s!"claim cache rollback failed {f.name}") + runWire directory f.wire + count := count + 1 + if f.envelope.logicalAxioms.isSome then axioms := axioms + 1 + let some reveals := revealWires? c | throw (IO.userError s!"reveal producer declined {c.name}") + for w in reveals do runWire directory w + revelations := revelations + reveals.length + for openFrontier in [false, true] do + for catalog in [false, true] do + let some f := diamond? openFrontier catalog | throw (IO.userError s!"diamond producer declined {openFrontier}/{catalog}") + unless diamondTests f do throw (IO.userError s!"diamond failed {f.name}") + runWire directory f.wire + let some cycles := cycleWires? | throw (IO.userError "cycle producer declined") + for w in cycles do runWire directory w + let some cycleReveals := revealWires? cycleCase | throw (IO.userError "cycle reveal producer declined") + for w in cycleReveals do runWire directory w + revelations := revelations + cycleReveals.length + for w in containsWires cycleCase do runWire directory w + IO.println <| (Lean.Json.mkObj [ + ("sourceClaimsAccepted", toJson count), ("nonemptyAxiomRoots", toJson axioms), + ("sourceMutationScenarios", toJson count), ("diamondScenarios", toJson (4 : Nat)), + ("warmClaimScenarios", toJson count), ("cycleScenarios", toJson cycles.length), + ("containsScenarios", toJson (containsWires cycleCase).length), + ("revealScenarios", toJson revelations), + ("unexpectedErrors", toJson (0 : Nat))]).compress + +end Tests.Certified.Claims diff --git a/Tests/Certified/ClaimsMain.lean b/Tests/Certified/ClaimsMain.lean new file mode 100644 index 000000000..ab82d621b --- /dev/null +++ b/Tests/Certified/ClaimsMain.lean @@ -0,0 +1,13 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Certified.Claims + +def main (args : List String) : IO UInt32 := do + if args.length > 1 then + IO.eprintln "usage: certified-claim-tests [OUTPUT_DIRECTORY]" + return 2 + Tests.Certified.Claims.run (args.head?.map System.FilePath.mk) + return 0 diff --git a/Tests/Certified/FeatureCases.lean b/Tests/Certified/FeatureCases.lean new file mode 100644 index 000000000..52341ee1e --- /dev/null +++ b/Tests/Certified/FeatureCases.lean @@ -0,0 +1,284 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Certified.Serialize +import Ix.Certified.Corpus +import Tests.Theory.Standard +import Tests.Theory.Quotient +import Tests.Theory.Structure +import Tests.Theory.Natural + +/-! Canonical serialized acceptance corpus. All certificates are generated +from decoded stores, and all outcomes run the independent serialized gate. -/ + +namespace Tests.Certified.Features + +open Ix.Theory Ix.Theory.Model Ix.Theory.Certified Ix.Certified Serialize +open Tests.Theory.Certified (primitives) +open Lean (toJson) + +set_option maxRecDepth 32768 +set_option maxHeartbeats 32000000 + +abbrev std := Tests.Theory.Standard.input +abbrev quo := Tests.Theory.Quotient.input +abbrev struc := Tests.Theory.Structure.input +abbrev natural := Tests.Theory.Natural.input + +structure Scenario where + name : String + input : ProofInput Nat + signature : PrimitiveSignature Nat := primitives + +def positive : List Scenario := [ + ⟨"propext", Tests.Theory.Standard.propextInput, primitives⟩, + ⟨"choice-Prop", std 0 (.const Tests.Theory.Standard.choiceRef [.zero]) + (Tests.Theory.Standard.choiceSpec.type.instL [.zero]), primitives⟩, + ⟨"choice-universe", std 1 (Tests.Theory.Standard.choiceRefl (.param 0)) + (Tests.Theory.Standard.choiceReflType (.param 0)), primitives⟩, + ⟨"choice-Type", std 0 (Tests.Theory.Standard.choiceRefl (.succ .zero)) + (Tests.Theory.Standard.choiceReflType (.succ .zero)), primitives⟩, + ⟨"choice-Type1", std 0 (Tests.Theory.Standard.choiceRefl (.succ (.succ .zero))) + (Tests.Theory.Standard.choiceReflType (.succ (.succ .zero))), primitives⟩, + ⟨"Eq-K", std 0 Tests.Theory.Standard.kProof Tests.Theory.Standard.kProposition, primitives⟩, + ⟨"Quot-sound", quo 1 (.const Tests.Theory.Quotient.refs.sound [.param 0]) + (Certified.Quotient.soundType Tests.Theory.Quotient.refs), primitives⟩, + ⟨"Quot-ind", quo 1 (.const Tests.Theory.Quotient.refs.ind [.param 0]) + (Certified.Quotient.indType Tests.Theory.Quotient.refs), primitives⟩, + ⟨"Quot-lift-universe", Tests.Theory.Quotient.betaInput, primitives⟩, + ⟨"Quot-lift-Prop", quo 0 (Tests.Theory.Quotient.betaProof .zero) + (Tests.Theory.Quotient.betaProposition .zero), primitives⟩, + ⟨"Quot-lift-Type", quo 0 (Tests.Theory.Quotient.betaProof (.succ .zero)) + (Tests.Theory.Quotient.betaProposition (.succ .zero)), primitives⟩, + ⟨"Quot-proof-universe", quo 1 (Tests.Theory.Quotient.propLiftProof (.param 0)) + (Tests.Theory.Quotient.propLiftProposition (.param 0)), primitives⟩, + ⟨"Quot-proof-Prop", quo 0 (Tests.Theory.Quotient.propLiftProof .zero) + (Tests.Theory.Quotient.propLiftProposition .zero), primitives⟩, + ⟨"Box-dependent-projection", Tests.Theory.Structure.boxInput, primitives⟩, + ⟨"Box-eta-Prop", struc Tests.Theory.Structure.box 0 (Tests.Theory.Structure.etaProof .zero) + (Tests.Theory.Structure.etaProposition .zero), primitives⟩, + ⟨"Box-eta-universe", struc Tests.Theory.Structure.box 1 (Tests.Theory.Structure.etaProof (.param 0)) + (Tests.Theory.Structure.etaProposition (.param 0)), primitives⟩, + ⟨"Pair-projection", struc Tests.Theory.Structure.pair 0 Tests.Theory.Structure.pairProof + Tests.Theory.Structure.boxProposition, primitives⟩, + ⟨"Box-field-Prop", struc Tests.Theory.Structure.box 0 (Tests.Theory.Structure.fieldProof .zero) + (Tests.Theory.Structure.fieldProposition .zero), primitives⟩, + ⟨"Box-field-Type", struc Tests.Theory.Structure.box 0 (Tests.Theory.Structure.fieldProof (.succ .zero)) + (Tests.Theory.Structure.fieldProposition (.succ .zero)), primitives⟩, + ⟨"Box-field-universe", struc Tests.Theory.Structure.box 1 (Tests.Theory.Structure.fieldProof (.param 0)) + (Tests.Theory.Structure.fieldProposition (.param 0)), primitives⟩, + ⟨"Pair-eta-Prop", struc Tests.Theory.Structure.pair 0 (Tests.Theory.Structure.pairEtaProof .zero) + (Tests.Theory.Structure.pairEtaProposition .zero), primitives⟩, + ⟨"Pair-eta-universe", struc Tests.Theory.Structure.pair 1 (Tests.Theory.Structure.pairEtaProof (.param 0)) + (Tests.Theory.Structure.pairEtaProposition (.param 0)), primitives⟩, + ⟨"Nat-zero", natural Tests.Theory.Natural.zero 0, Tests.Theory.Natural.profile⟩, + ⟨"Nat-literal", natural (Tests.Theory.Natural.numeral 4) 4, Tests.Theory.Natural.profile⟩, + ⟨"Nat-succ", natural (Tests.Theory.Natural.succ (.natLit 8)) 9, Tests.Theory.Natural.profile⟩, + ⟨"Nat-definition", natural (.const (.member 302 0) []) 3, Tests.Theory.Natural.profile⟩, + ⟨"Nat-add-zero", natural (Tests.Theory.Natural.add (.natLit 0) (.natLit 0)) 0, Tests.Theory.Natural.profile⟩, + ⟨"Nat-add", Tests.Theory.Natural.sampleInput, Tests.Theory.Natural.profile⟩, + ⟨"Nat-mul", natural (Tests.Theory.Natural.mul (.natLit 2) (.natLit 3)) 6, Tests.Theory.Natural.profile⟩, + ⟨"Nat-mul-zero", natural (Tests.Theory.Natural.mul (.natLit 5) (.natLit 0)) 0, Tests.Theory.Natural.profile⟩ +] + +#guard positive.length = 30 + +def witness? (c : Case) : Option (PreparedInput × ProofWitness Address) := do + let p ← prepare? 6400 c.profile c.target c.blobs c.literals + let w ← Ix.Theory.Certificate.proofWitness? 6400 p.signature p.input + return (p, w) + +def accepted (c : Case) : Bool := + (witness? c).any fun (_, w) => acceptsSerialized.{0} 6400 c.profile c.target c.blobs w c.literals + +#guard positive.all fun s => (make? s.name s.signature s.input).any accepted + +structure Mutation where + name : String + original : ProofInput Nat + altered : ProofInput Nat + signature : PrimitiveSignature Nat := primitives + +def mutatedSources : List Mutation := + [Tests.Theory.Standard.falseAxiom, Tests.Theory.Standard.unsafeAxiom, + Tests.Theory.Standard.wrongArity].zipIdx.map (fun (alter, i) => + ⟨s!"bad-standard-{i}", Tests.Theory.Standard.propextInput, + { Tests.Theory.Standard.propextInput with store := Tests.Theory.Standard.store alter }, primitives⟩) ++ + [Tests.Theory.Quotient.wrongKind, Tests.Theory.Quotient.wrongArity, + Tests.Theory.Quotient.falseSound, Tests.Theory.Quotient.unsafeSound, + Tests.Theory.Quotient.wrongLift].zipIdx.map (fun (alter, i) => + ⟨s!"bad-quotient-{i}", Tests.Theory.Quotient.betaInput, + { Tests.Theory.Quotient.betaInput with store := Tests.Theory.Quotient.store alter }, primitives⟩) ++ + [Tests.Theory.Structure.wrongFieldCount, Tests.Theory.Structure.wrongRecursor].zipIdx.map (fun (alter, i) => + ⟨s!"bad-structure-{i}", Tests.Theory.Structure.boxInput, + { Tests.Theory.Structure.boxInput with store := Tests.Theory.Structure.store Tests.Theory.Structure.box alter }, primitives⟩) ++ + [Tests.Theory.Natural.unsafeNat, Tests.Theory.Natural.forgedRule, + Tests.Theory.Natural.changedAdd].zipIdx.map (fun (alter, i) => + ⟨s!"bad-natural-{i}", Tests.Theory.Natural.sampleInput, + { Tests.Theory.Natural.sampleInput with store := Tests.Theory.Natural.store alter }, Tests.Theory.Natural.profile⟩) ++ + [(.member 200 0, 2), (.member 100 0, 1)].zipIdx.map (fun ((r, field), i) => + ⟨s!"bad-projection-{i}", Tests.Theory.Structure.boxInput, + { Tests.Theory.Structure.boxInput with proof := (Tests.Theory.Structure.badProjectionProof r field).erase }, primitives⟩) ++ + [⟨"bad-Nat-answer", Tests.Theory.Natural.sampleInput, + { Tests.Theory.Natural.sampleInput with proposition := + (Tests.Theory.Natural.proposition (Tests.Theory.Natural.add (.natLit 2) (.natLit 3)) (.natLit 6)).erase }, + Tests.Theory.Natural.profile⟩] + +/-- The altered bytes are validly hashed. A certificate is obtained for the +original complete declarations at these addresses and first validated on the +control input. Reusing it on the altered authenticated input must fail. -/ +def rejectsSource (m : Mutation) : Bool := Id.run do + let some c := make? m.name m.signature m.altered | return false + let some p := repaired? c m.original | return false + let some w := Ix.Theory.Certificate.proofWitness? 6400 p.signature p.input | return false + return acceptsCertified.{0,0} 6400 p.signature p.input w && + !acceptsSerialized.{0} 6400 c.profile c.target c.blobs w c.literals + +#guard mutatedSources.length = 16 +#guard mutatedSources.all rejectsSource + + +structure WitnessMutation where + scenario : Nat + name : String + alter : ProofWitness Address → ProofWitness Address + +def mapDeclarations (f : DeclarationWitness Address → DeclarationWitness Address) + (w : ProofWitness Address) : ProofWitness Address := + { w with declarations := w.declarations.map f } + +def witnessMutations : List WitnessMutation := [ + ⟨0, "standard-before-prerequisites", fun w => { w with declarations := w.declarations.reverse }⟩, + ⟨8, "quotient-missing-type", mapDeclarations fun d => match d with + | .quotient q => .quotient { q with types := q.types.drop 1 } + | other => other⟩, + ⟨8, "quotient-wrong-rule", mapDeclarations fun d => match d with + | .quotient q => .quotient { q with liftRule := q.indRule } + | other => other⟩, + ⟨8, "quotient-wrong-owner", mapDeclarations fun d => match d with + | .quotient q => .quotient { q with refs := { q.refs with ctor := q.refs.type } } + | other => other⟩, + ⟨13, "structure-without-facts", mapDeclarations fun d => match d with + | .structure s => .ordinary s.facts.block + | other => other⟩, + ⟨13, "structure-wrong-field-sorts", mapDeclarations fun d => match d with + | .structure s => .structure { s with facts := { s.facts with description := + { s.facts.description with fields := s.facts.description.fields.map fun f => { f with level := .zero } } } } + | other => other⟩, + ⟨13, "structure-dependent-rule-order", mapDeclarations fun d => match d with + | .structure s => .structure { s with iota := s.iota.reverse } + | other => other⟩, + ⟨27, "natural-without-facts", mapDeclarations fun d => match d with + | .natural n => .ordinary n + | other => other⟩ +] + +def rejectsWitness (m : WitnessMutation) : Bool := Id.run do + let some s := positive[m.scenario]? | return false + let some c := make? s.name s.signature s.input | return false + let some (_, w) := witness? c | return false + return acceptsSerialized.{0} 6400 c.profile c.target c.blobs w c.literals && + !acceptsSerialized.{0} 6400 c.profile c.target c.blobs (m.alter w) c.literals + +#guard witnessMutations.all rejectsWitness + +def naturalCase := make? "Nat-add" Tests.Theory.Natural.profile Tests.Theory.Natural.sampleInput + +def missingPin (c : Case) : Case := { c with profile := { c.profile with natType := none } } +def wrongPin (c : Case) : Case := + { c with profile := { c.profile with natType := reference c.references (.member 100 0) } } + +def rejectsProfile (alter : Case → Case) : Bool := Id.run do + let some c := naturalCase | return false + let some (_, w) := witness? c | return false + let changed := alter c + return !acceptsSerialized.{0} 6400 changed.profile changed.target changed.blobs w changed.literals + +#guard [missingPin, wrongPin].all rejectsProfile + +structure IngressMutation where + name : String + altered : Option Case + +def ingressMutations : List IngressMutation := [ + ⟨"missing-literal-blob", naturalCase.map fun c => { c with literals := [] }⟩, + ⟨"literal-bytes-under-old-hash", naturalCase.map fun c => + { c with literals := c.literals.map fun (a, bytes) => (a, bytes.push 1) }⟩, + ⟨"noncanonical-literal-new-hash", naturalCase.bind fun c => rehash? c (fun bytes => bytes.push 0)⟩, + ⟨"duplicate-literal-blob", naturalCase.map fun c => { c with literals := c.literals ++ c.literals }⟩, + ⟨"literal-in-constant-domain", naturalCase.map fun c => { c with blobs := c.blobs ++ c.literals, literals := [] }⟩, + ⟨"constant-in-literal-domain", naturalCase.bind fun c => do + let eq ← reference c.references (.member 100 0) + rehash? c id fun a source => + if a = c.target then match source.info with + | .defn d => { source with + refs := source.refs.push eq + info := .defn { d with + typ := mapLiterals source.refs.size.toUInt64 d.typ + value := mapLiterals source.refs.size.toUInt64 d.value } } + | _ => source + else source⟩, + ⟨"projection-wrapper-wrong-kind", do + let c ← make? "Box-dependent-projection" primitives Tests.Theory.Structure.boxInput + let owner ← reference c.references (.member 200 0) + rehash? c id fun a source => if a = owner then match source.info with + | .iPrj p => { source with info := .rPrj ⟨p.idx, p.block⟩ } + | _ => source + else source⟩ +] + +def rejectsIngress (m : IngressMutation) : Bool := + m.altered.any fun c => (prepare? 6400 c.profile c.target c.blobs c.literals).isNone + +#guard ingressMutations.length = 7 +#guard ingressMutations.all rejectsIngress + +def writeCase (directory : System.FilePath) (c : Case) : IO Unit := do + let directory := directory / c.name + IO.FS.createDirAll directory + for (a, bytes) in c.blobs do + IO.FS.writeBinFile (directory / s!"{hexOfBytes a.hash}.ixon") bytes + for (a, bytes) in c.literals do + IO.FS.writeBinFile (directory / s!"{hexOfBytes a.hash}.nat") bytes + let metadata := Lean.Json.mkObj [ + ("target", toJson (hexOfBytes c.target.hash)), + ("falseType", toJson (hexOfBytes c.profile.falseType.hash)), + ("falseElim", toJson (hexOfBytes c.profile.falseElim.hash)), + ("natType", toJson (c.profile.natType.map fun a => hexOfBytes a.hash)), + ("constants", toJson (c.blobs.map fun (a, _) => hexOfBytes a.hash)), + ("naturals", toJson (c.literals.map fun (a, _) => hexOfBytes a.hash))] + IO.FS.writeFile (directory / "target.json") (metadata.pretty ++ "\n") + +def run (directory : Option System.FilePath) : IO Unit := do + for s in positive do + let some c := make? s.name s.signature s.input | throw (IO.userError s!"fixture construction failed: {s.name}") + unless accepted c do throw (IO.userError s!"serialized acceptance failed: {s.name}") + if let some directory := directory then writeCase directory c + for m in mutatedSources do + unless rejectsSource m do throw (IO.userError s!"source rejection failed: {m.name}") + if let some directory := directory then + let some c := make? m.name m.signature m.altered | throw (IO.userError s!"mutation construction failed: {m.name}") + writeCase directory c + for m in witnessMutations do + unless rejectsWitness m do throw (IO.userError s!"witness rejection failed: {m.name}") + for (name, alter) in [("missing-Nat-pin", missingPin), ("wrong-Nat-pin", wrongPin)] do + unless rejectsProfile alter do throw (IO.userError s!"profile rejection failed: {name}") + if let some directory := directory then + let some c := naturalCase | throw (IO.userError "natural fixture construction failed") + writeCase directory { (alter c) with name } + for m in ingressMutations do + unless rejectsIngress m do throw (IO.userError s!"ingress rejection failed: {m.name}") + if let some directory := directory then + let some c := m.altered | throw (IO.userError s!"ingress mutation construction failed: {m.name}") + writeCase directory { c with name := m.name } + IO.println <| (Lean.Json.mkObj [ + ("serializedAccepted", toJson positive.length), + ("sourceRejectedWithValidWitness", toJson mutatedSources.length), + ("forgedWitnessRejected", toJson witnessMutations.length), + ("profileRejected", toJson (2 : Nat)), + ("ingressRejected", toJson ingressMutations.length), + ("unexpectedErrors", toJson (0 : Nat))]).compress + +end Tests.Certified.Features diff --git a/Tests/Certified/Features.lean b/Tests/Certified/Features.lean new file mode 100644 index 000000000..4b1be4415 --- /dev/null +++ b/Tests/Certified/Features.lean @@ -0,0 +1,13 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Certified.FeatureCases + +def main (args : List String) : IO UInt32 := do + if args.length > 1 then + IO.eprintln "usage: certified-feature-tests [OUTPUT_DIRECTORY]" + return 2 + Tests.Certified.Features.run (args.head?.map System.FilePath.mk) + return 0 diff --git a/Tests/Certified/Fidelity.lean b/Tests/Certified/Fidelity.lean new file mode 100644 index 000000000..ff5a89e65 --- /dev/null +++ b/Tests/Certified/Fidelity.lean @@ -0,0 +1,132 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Certified.Source +import Ix.Certified.SourceMeaning + +namespace Tests.Certified.Fidelity + +open Ix.Theory Ix.Theory.Certified Ix.Certified Ix.Kernel Serialize +open Lean (toJson) + +set_option maxRecDepth 32768 +set_option maxHeartbeats 32000000 + +def whole (source : Ixon.Constant) : Ixon.Constant := + match source.info with + | .defn value => { source with + info := .defn { value with typ := .share 0, value := .share 1 }, + sharing := #[value.typ, value.value] } + | _ => source + +def base := whole Fixtures.identity + +def nested : Ixon.Constant := + match base.info with + | .defn value => { base with + info := .defn { value with typ := .share 2, value := .share 3 }, + sharing := base.sharing ++ #[.share 0, .share 1] } + | _ => base + +def permuted : Ixon.Constant := + match base.info with + | .defn value => { base with + info := .defn { value with typ := .share 1, value := .share 0 }, + sharing := #[Corpus.idBody 0, Corpus.idType 0] } + | _ => base + +def underBinders : Ixon.Constant := + match base.info with + | .defn value => { base with + info := .defn { value with + typ := .leanAll (.sort 0) (.share 0), value := .leanLam (.sort 0) (.share 1) }, + sharing := #[.leanAll (.var 0) (.var 1), .leanLam (.var 0) (.var 0)] } + | _ => base + +def input (name : String) (source : Ixon.Constant) (dependencies : List Ixon.Constant := []) : Case := + let c := Corpus.one name source dependencies + { name, profile := Fixtures.profile, target := c.target, blobs := c.blobs, + literals := [], references := [], blocks := c.blobs.map fun (a, _) => (0, a) } + +def positive : List Case := [ + input "sharing-whole-identity" base, + input "sharing-nested-identity" nested, + input "sharing-permuted-identity" permuted, + input "sharing-under-binders" underBinders, + input "sharing-beta-statement" (whole Corpus.betaStatement), + input "sharing-eta-statement" (whole Corpus.etaStatement), + input "sharing-dependent-polymorphic" (whole (Corpus.dependent 1 (.var 0))), + input "sharing-defined-polymorphic-use" (whole (Corpus.polyUse .defn true)) [Corpus.polyId .defn] +] + +#guard positive.length = 8 +#guard positive.all Source.accepted +#guard positive.all Source.storeAccepted +#guard positive.all Source.ixeAccepted +#guard positive.all Source.warmAndRollback + +def negative : List Case := [ + input "sharing-out-of-bounds" { base with sharing := #[.share 999, Corpus.idBody 0] }, + input "sharing-self-cycle" { base with sharing := #[.share 0, Corpus.idBody 0] }, + input "sharing-mutual-cycle" { base with sharing := #[.share 1, .share 0] }, + input "sharing-altered-statement" { base with + sharing := #[.ref 0 #[], Corpus.idBody 0], refs := #[Fixtures.falseObject.1] }, + input "sharing-linear-body" { base with + sharing := #[Corpus.idType 0, .lam .linear (.sort 0) (.leanLam (.var 0) (.var 0))] }, + input "sharing-missing-universe" { base with univs := #[] }, + input "sharing-free-variable" { base with sharing := #[Corpus.idType 0, .var 0] }, + input "sharing-missing-reference" { base with sharing := #[Corpus.idType 0, .ref 999 #[]] } +] + +/-- The control certificate checks the complete original declaration at the +altered address. It is then submitted to the actual authenticated source +gate, so witness-search refusal cannot explain these rejections. -/ +def validControlRejected (c : Case) : Bool := Id.run do + let some objects := decodeObjects? c.blobs | return false + let original := objects.map fun (a, value) => (a, if a = c.target then base else value) + let some signature := readSignature? c.profile original | return false + let some prepared := readProofInput? 6400 original c.target | return false + let some witness := Ix.Theory.Certificate.proofWitness? 6400 signature prepared | return false + return acceptsCertified.{0,0} 6400 signature prepared witness && + !acceptsCertifiedSource.{0} 6400 (Source.source c) c.profile c.target (Source.selection c) witness && + !acceptsCertifiedStoreSource.{0} 6400 (Source.source c) c.profile [c.target] (Source.selection c) witness.declarations + +#guard negative.length = 8 +#guard negative.all validControlRejected + +/-- Equal expanded statements do not identify their different wire encodings. +The current bytes must still authenticate under the exact requested address. -/ +def distinctEncoding : Bool := Id.run do + let first := input "first" base + let second := input "second" permuted + if first.target = second.target then return false + let some a := prepare? 6400 first.profile first.target first.blobs | return false + let some b := prepare? 6400 second.profile second.target second.blobs | return false + if a.input.proposition != b.input.proposition then return false + let some witness := suggestSource? 6400 (Source.source first) first.profile first.target (Source.selection first) + | return false + let some value := (Source.source second).consts.get? second.target | return false + let changed := { (Source.source first) with + consts := (Source.source first).consts.insert first.target value } + return !acceptsCertifiedSource.{0} 6400 changed first.profile first.target (Source.selection first) witness + +#guard distinctEncoding + +def run (directory : Option System.FilePath) : IO Unit := do + for c in positive do + unless Source.accepted c && Source.storeAccepted c && Source.ixeAccepted c && Source.warmAndRollback c do + throw (IO.userError s!"source fidelity acceptance failed: {c.name}") + if let some directory := directory then Source.writeCase directory c + for c in negative do + unless validControlRejected c do throw (IO.userError s!"source fidelity mutation escaped: {c.name}") + if let some directory := directory then Source.writeCase directory c + unless distinctEncoding do throw (IO.userError "semantic equality was used as byte identity") + IO.println <| (Lean.Json.mkObj [ + ("proofAccepted", toJson positive.length), ("storesAccepted", toJson positive.length), + ("ixeAccepted", toJson positive.length), ("warmProofScenarios", toJson positive.length), + ("validControlSourceMutationsRejected", toJson negative.length), + ("distinctEncodingRejected", toJson distinctEncoding), ("unexpectedErrors", toJson (0 : Nat))]).compress + +end Tests.Certified.Fidelity diff --git a/Tests/Certified/FidelityMain.lean b/Tests/Certified/FidelityMain.lean new file mode 100644 index 000000000..3076e1835 --- /dev/null +++ b/Tests/Certified/FidelityMain.lean @@ -0,0 +1,13 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Certified.Fidelity + +def main (args : List String) : IO UInt32 := do + if args.length > 1 then + IO.eprintln "usage: certified-fidelity-tests [OUTPUT_DIRECTORY]" + return 2 + Tests.Certified.Fidelity.run (args.head?.map System.FilePath.mk) + return 0 diff --git a/Tests/Certified/ImportManifest.lean b/Tests/Certified/ImportManifest.lean new file mode 100644 index 000000000..64ebc47de --- /dev/null +++ b/Tests/Certified/ImportManifest.lean @@ -0,0 +1,82 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +/-! Frozen certified adapter identities. The archive includes the original +patches, source identities, reports and differential corpora. Maintained code +uses Ix.Theory and Ix.Kernel; five audits now traverse full checked declarations. +The historical VM pilot, packet encoder, VM test and native profiling counters +are preserved in the archive for a later change. Host tests omit the +pilot-specific assertions; maintained hashes record all local adaptations. -/ + +namespace Tests.Certified.ImportManifest + +def archive : String := "Tests/Fixtures/Certified/c7-handoff.tar.gz" +def archiveSha256 : String := "94d7b43e2942f9a9f5c3407964905beddb834b8223ce9eb52d8df494fb17faf0" +def ixBaseRevision : String := "3e7586a7adb0c6f3cd3ff5854da832701288ec90" + +structure SourceFile where + source : String + target : Option String + sourceSha256 : String + importedSha256 : String + deriving Repr + +def selected : Array SourceFile := #[ + ⟨"Ix/Certified/Audit.lean", some "Ix/Certified/Audit.lean", "06635e487e0db2bbe57f110245c0dc6e2a9c633ebfe074797567e114da8aec52", "793ea6e189ab20f68120c58d4d5483c08124715c67ffa511424c1522e249080f"⟩, + ⟨"Ix/Certified/Bytes.lean", some "Ix/Certified/Bytes.lean", "5b01039400a830a3f80420cb011cb76c6a52511b901905f4bf7f811e19986328", "278a9cd43907eeb885e1db5c672b227d59a15713f73317941df09a3d5ca61046"⟩, + ⟨"Ix/Certified/ClaimAccept.lean", some "Ix/Certified/ClaimAccept.lean", "0c179bd4ea2c1a9d8a3b239ffe2feb60b88038f8f9f09c5d9fd8e1e83011f981", "198d8b9b436b937c311bf7ae7e47d4523bdc80022447e42dcc03a131fb01ef38"⟩, + ⟨"Ix/Certified/ClaimAudit.lean", some "Ix/Certified/ClaimAudit.lean", "8916803f12bb53e22fa1c94318a6b2a736ec9634ffae811f710b579d3d278bf4", "363f6524397b56c2225d2bdc5cadee29522959d043cfa8a5ace854078a5c30bb"⟩, + ⟨"Ix/Certified/ClaimCheck.lean", some "Ix/Certified/ClaimCheck.lean", "e4d598badcb189897a339e187b9529658e2ef75445645e1463704cc6b349a196", "8f8f6c94581ed08c8b8b00d3157bd351e7aed29cd8aa53e491cb53dc48e8845f"⟩, + ⟨"Ix/Certified/ClaimCommand.lean", some "Ix/Certified/ClaimCommand.lean", "8f360b0d0a586ab15e3b8bb3a375f5dc56ad6157344295a67673d876bf8650eb", "e053cb7bab6e1ca19e36de92bcb44c60be9764fc30ab160b92224f4703a4fb4d"⟩, + ⟨"Ix/Certified/ClaimInput.lean", some "Ix/Certified/ClaimInput.lean", "d2d0df3ef446690600e843c84683a8f2056e1d8a843a523fc57b550763963f32", "861b0cfd3a1dac9c683dbd1b71088065a00d9a47be9ddbdce304722271027451"⟩, + ⟨"Ix/Certified/ClaimMain.lean", some "Ix/Certified/ClaimMain.lean", "ed1f1b0edaec4e0928d90a814f35673a8989e55037c9cc023a6e0e287c7574e7", "ed1f1b0edaec4e0928d90a814f35673a8989e55037c9cc023a6e0e287c7574e7"⟩, + ⟨"Ix/Certified/ClaimMeaning.lean", some "Ix/Certified/ClaimMeaning.lean", "78edb69d209768d8010b28b16d334fd2fa8bc6225490f9b2bbe5fd66cc906f36", "5f2550f0235fe84927d654a360a4298f426e7ca2e7911245c84f91dbd11eef6f"⟩, + ⟨"Ix/Certified/ClaimSuggest.lean", some "Ix/Certified/ClaimSuggest.lean", "5f83e6b65542661c4efacd6389c5c6ca0a0eeaffb2d0d24cb58168be514a10f6", "285ed4b14540aa47afa67e28f20bc44e7fdce93fd94d0742971711f15df8336b"⟩, + ⟨"Ix/Certified/Command.lean", some "Ix/Certified/Command.lean", "f9f8a32be2761dc97bf7215585cde40b0e310153482707a570f7c1a106252abe", "c6803f699ed2edd6508e2db73b55f4abb163f6425bcf6b755da11b885991ad17"⟩, + ⟨"Ix/Certified/Corpus.lean", some "Ix/Certified/Corpus.lean", "fc1d66c81430461bed9e0045e6a26a1e01c85de90854eccea2343f24fe5dde97", "fc1d66c81430461bed9e0045e6a26a1e01c85de90854eccea2343f24fe5dde97"⟩, + ⟨"Ix/Certified/Envelope.lean", some "Ix/Certified/Envelope.lean", "da0269803ff6f48c6d0347627d57a7f7699c0ea06e54f959406e3d8570a326f1", "da0269803ff6f48c6d0347627d57a7f7699c0ea06e54f959406e3d8570a326f1"⟩, + ⟨"Ix/Certified/Fixtures.lean", some "Ix/Certified/Fixtures.lean", "fe7a503a0e436a2ecf924b48d9b66ed3f6ef901c7bcbe6098853c62c7748675a", "f85f883f498cdee55ab674876dd1fb19bae5dbe8aa729118cffd29ab626c8b25"⟩, + ⟨"Ix/Certified/Ingress.lean", some "Ix/Certified/Ingress.lean", "1fe00c0a512ae94fddd2847196f1bafa415ff2c452412f0e69e9e060883dd0cf", "afd9e82827fed62d453c3979fbfdb1238f83573f19af3b6728574f97ff1a86a4"⟩, + ⟨"Ix/Certified/Ixon.lean", some "Ix/Certified/Ixon.lean", "9b7f0d64f08c03ed3a215ff5b23e136bd85c71f953e9198fb940b597d7b54410", "5e37eaa6b5cb872edad7954cde413706f30996950c71d21417174f8166377bcb"⟩, + ⟨"Ix/Certified/Main.lean", some "Ix/Certified/Main.lean", "c571357f5e1120efbe23c7db4906fc3eeaebffccf2956e1899212494800d38a5", "c571357f5e1120efbe23c7db4906fc3eeaebffccf2956e1899212494800d38a5"⟩, + ⟨"Ix/Certified/ModelHints.lean", some "Ix/Certified/ModelHints.lean", "74fd6763d56ac5c8aa9af4507172879739c2dc681969a3f342aadbc30ab27837", "de098f48fc03893643c8de08c3bf2bed315f91767c71e9e229816784abb8f124"⟩, + ⟨"Ix/Certified/ModeledAudit.lean", some "Ix/Certified/ModeledAudit.lean", "55101ebfee84fd89e4c99f08d88aac7edb7000302ef6d022a8c639e331fb3ec2", "c37969f6a69e11d62547bc91b685695cd08f002c115db70a05069f34fe111de1"⟩, + ⟨"Ix/Certified/Native.lean", none, "7b617483cbeef932b9caaf6ca3e118be93e5621aaf1ca6bb8132821b336b1c82", ""⟩, + ⟨"Ix/Certified/Packet.lean", none, "a694ff77c95d6fe3bd31b99e28597769604b901926d4e0a3dfea7802fb57711d", ""⟩, + ⟨"Ix/Certified/Reveal.lean", some "Ix/Certified/Reveal.lean", "2d31e94a72208eddbd52e51ff819131430e374403e8411a9fd90b909f965af2b", "2d31e94a72208eddbd52e51ff819131430e374403e8411a9fd90b909f965af2b"⟩, + ⟨"Ix/Certified/SourceAudit.lean", some "Ix/Certified/SourceAudit.lean", "fb916b0e7eede60d3d225d7fd8045acf175dc327e83c8705379fd01a52c7866f", "c945ca007d28ad54bcceb25c5d3dafd38029ea2280b7f22a7e85d57f637ecbb6"⟩, + ⟨"Ix/Certified/SourceExpr.lean", some "Ix/Certified/SourceExpr.lean", "f4de024bc68cb991bbad57bbe0d1c147cfe32329fb9a34dc91a4dbde1177f805", "03417c4f256de2158e71407560d60abeaf77bbacd00533654c67c9c5b4f63334"⟩, + ⟨"Ix/Certified/SourceMeaning.lean", some "Ix/Certified/SourceMeaning.lean", "69f916348389e4e7d93b67b3d2c3d8abf8e30b89b417de828620627c6012209a", "f96eedf1b7d46564f6c3abf0bf3450bffb5ffca85a68071f567d75837c84ee14"⟩, + ⟨"Ix/Certified/SourceStore.lean", some "Ix/Certified/SourceStore.lean", "7b963da22cb0d2199ce4bf02353cfff0a6e8a3d571a82e19b3c9edb99336d545", "390d260608e9f4f638758751306e1cf06d4a129379a4ebcf902f5873969d832d"⟩, + ⟨"Ix/Certified/Store.lean", some "Ix/Certified/Store.lean", "23cd4434c7e1f1784a438bdeecd66378bda1ad280f28f452cb6d51443dd7e3be", "86394770b2c96176e7478a6cdd64bc0a1057b30968b4301030ffdb59751ad733"⟩, + ⟨"Ix/Certified/Suggest.lean", some "Ix/Certified/Suggest.lean", "cec35a4826b6b6d55bbcf62563d99d192bcb511a25c9d00be7d4341c64189d53", "214b6a2652c46ee93720a8a6f017268f7f06173cfa2e146ce6c067f08b0fa861"⟩, + ⟨"Ix/Certified/TcAudit.lean", some "Ix/Certified/TcAudit.lean", "a0c0177157fa1a4a891f80e3aa437901a48c2c3814bc66cb26eeaaf5808f6862", "912cae64335baa42740fffea5e26035f4a90bd107fe2e17c603d3071a623e00a"⟩, + ⟨"Ix/Certified/Trees.lean", some "Ix/Certified/Trees.lean", "cf96fa2ee3ea6263c62c630c848b8f90449d8380a454ba291ed5e7e67011c3a5", "cf96fa2ee3ea6263c62c630c848b8f90449d8380a454ba291ed5e7e67011c3a5"⟩, + ⟨"Ix/IxVM/Certified/Accept.lean", none, "8c35df5d77369c3bc77747fa872399da1b9709fdfb53f28623809cea60637e71", ""⟩, + ⟨"Ix/IxVM/Certified/Checker.lean", none, "7da05836aa7701570a6cc35c0dd856ff0ce1b09cfbce58cbec942e9a066c76a3", ""⟩, + ⟨"Ix/IxVM/Certified/Expr.lean", none, "63906b989d6ade2f3a8b72c3e84c68e887a857227a9f3bacf6a94a6c873a9a1b", ""⟩, + ⟨"Ix/IxVM/Certified/Levels.lean", none, "c59aea708f0ef66dc61d32754fa3956e597268514917546cd9ce48a1cce71be4", ""⟩, + ⟨"Ix/IxVM/Certified/Read.lean", none, "6e25a90521af8c0dc1ba9dfb9c849a2dd4e95854484688461f1962c85f1bc650", ""⟩, + ⟨"Ix/IxVM/Certified/Types.lean", none, "a5bc3333435eee693e5c2af7b444c4ea5de8ca56f9aed37dde6e80f23690a87a", ""⟩, + ⟨"Ix/Tc/Certified.lean", some "Ix/Kernel/Certified.lean", "ef7be3a4a372fe1c781c4d3e407fd14c55ba0f9289392ab7e8ab5806972ad90b", "cf7d2b617336af369fa5678bcfede46c6e4a9a06f91d5334c8b7b8a6a3b85f2c"⟩, + ⟨"Ix/Tc/CertifiedClaims.lean", some "Ix/Kernel/CertifiedClaims.lean", "ef79fbdc511c9ae90b729e639f0d57a54f68e40caab1f66d84ebba35501a30a6", "fa6d66105ade421b155c76165f7ad77c900313120e8436ce563b16e0f2451421"⟩, + ⟨"Tests/Certified/Claims.lean", some "Tests/Certified/Claims.lean", "1a678f07b5645c1f88ff07028793663c37ab74164759953dab5a2a3e311c6a22", "1b358ef41f6b701ca1dad5f309f65e9931a2fe120aa5e22f4feae7500535db49"⟩, + ⟨"Tests/Certified/ClaimsMain.lean", some "Tests/Certified/ClaimsMain.lean", "01f900c1a61940910c7131fc42a3757d2f7ea1a508f55668f6d54da8ee6eb136", "01f900c1a61940910c7131fc42a3757d2f7ea1a508f55668f6d54da8ee6eb136"⟩, + ⟨"Tests/Certified/FeatureCases.lean", some "Tests/Certified/FeatureCases.lean", "d68e7731b4864cdf823bdcf94af492158bf8f9a4da3d5dcfc659cf8582795591", "2478888475a43b4d4e07f4d07910f765788870568b96ec282afba6153e44cdeb"⟩, + ⟨"Tests/Certified/Features.lean", some "Tests/Certified/Features.lean", "65b70e67a51698b4921c6592419658e8b528ac544cdd1b0fedc84a69effa9470", "65b70e67a51698b4921c6592419658e8b528ac544cdd1b0fedc84a69effa9470"⟩, + ⟨"Tests/Certified/Fidelity.lean", some "Tests/Certified/Fidelity.lean", "7d8fdea26a63bc94ace55943f6c062340ab3b263867ecd21705c70b288784946", "b64958afa2dfbdb90de98f6a0908a8aa18c78f944bcfd6060db317636c8c4f9a"⟩, + ⟨"Tests/Certified/FidelityMain.lean", some "Tests/Certified/FidelityMain.lean", "47dbf7899235312d4ea171879940cf6eb98028eccbbdaeaa86b8a41a6449bb17", "47dbf7899235312d4ea171879940cf6eb98028eccbbdaeaa86b8a41a6449bb17"⟩, + ⟨"Tests/Certified/ModelSerialize.lean", some "Tests/Certified/ModelSerialize.lean", "d470f6a95bc48ccc65f141d90abb99d2211ba82b9fa3b9d13e42f7698c755f2b", "86f064fcdd3940bd1385463a6ad65e2a455e5de8950eb025ffbae0397c8cd3e2"⟩, + ⟨"Tests/Certified/Modeled.lean", some "Tests/Certified/Modeled.lean", "772dcd81eee47d73bbd40b25d068ce4e2f209df6fe22740db456c0b5e6e6353a", "ef4a5257a39154724a8c0c7d0c105aff571cec119353e14309e236f577cc1eb7"⟩, + ⟨"Tests/Certified/ModeledAdversarial.lean", some "Tests/Certified/ModeledAdversarial.lean", "6147577fa6afdf0b2f05813462c99c32846443717eaea86cfbf6547d42b82e42", "ec7a790339751ba9c0e4760de959faa8eaa4682c50d3afd06d8cacfe57df2dcf"⟩, + ⟨"Tests/Certified/ModeledMain.lean", some "Tests/Certified/ModeledMain.lean", "459171f71e7d845d1aadead559b1224a07b4d86e9be7ff97f5182fd1a88b974d", "459171f71e7d845d1aadead559b1224a07b4d86e9be7ff97f5182fd1a88b974d"⟩, + ⟨"Tests/Certified/Ordinary.lean", some "Tests/Certified/Ordinary.lean", "59cbe0be253cda1854481989f9d02d44624db0fa3493dd7dac9cdf68480e0684", "804eca674eb8e663429a74de6710dc612d110c4dbc892da8fa6e6f08a4ac52ba"⟩, + ⟨"Tests/Certified/Serialize.lean", some "Tests/Certified/Serialize.lean", "ef9286ca27377f034c97e8549e91484e67612aeb83dc0baf1926dff640d07f74", "593acdcb84d7d350bc258df2ad841233e946bf123583f844b516a45669a71af2"⟩, + ⟨"Tests/Certified/Source.lean", some "Tests/Certified/Source.lean", "78a98301be394278ad90d55599aa1deba55f9463b735626c4cc1965a01200ce0", "703c39031d8211f9d156039e03ede0a5e81adb2b955932596421b2fa6686a31a"⟩, + ⟨"Tests/Certified/SourceMain.lean", some "Tests/Certified/SourceMain.lean", "5a07d03d9f3e37b5e6e3fd84af66241c2e530054fa01c8157a8f938172c84d58", "5a07d03d9f3e37b5e6e3fd84af66241c2e530054fa01c8157a8f938172c84d58"⟩, + ⟨"Tests/Certified/VM.lean", none, "7699c996e3238243314318ef63c7e4436bce81264420d9db231b19535552a828", ""⟩ +] + +end Tests.Certified.ImportManifest diff --git a/Tests/Certified/ModelSerialize.lean b/Tests/Certified/ModelSerialize.lean new file mode 100644 index 000000000..6f7709f0b --- /dev/null +++ b/Tests/Certified/ModelSerialize.lean @@ -0,0 +1,113 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Certified.Serialize +import Ix.Certified.ModelHints + +/-! General test serializer for complete mutual blocks, including recursors +and every public member/constructor projection. All model declarations are +ordinary source objects in the same authenticated .ixe environment. -/ + +namespace Tests.Certified.ModelSerialize + +open Ix.Theory Ix.Theory.Certified Ix.Certified Serialize + +def mutualInfo (self : Nat) (resolve : ConstRef Nat → Option Address) (source : Const Nat) : + Write Ixon.MutConst := do + match ← constantInfo self resolve source with + | .muts #[.indc family] => return .indc family + | .defn definition => return .defn definition + | .recr recursor => return .recr recursor + | _ => failure + +def block (self : Nat) (resolve : ConstRef Nat → Option Address) (source : Block Nat) : + Option (Ixon.Constant × ConstantBlobs) := do + let action : Write Ixon.ConstantInfo := match source.members with + | [member] => constantInfo self resolve member + | [] => failure + | members => return .muts (← members.mapM (mutualInfo self resolve)).toArray + let (info, tables) ← action {} + return (⟨info, #[], tables.refs, tables.univs⟩, tables.literals) + +def memberProjection? (kind : ConstKind) (index : Nat) (address : Address) : Option Ixon.Constant := + match kind with + | .induct => some ⟨.iPrj ⟨index.toUInt64, address⟩, #[], #[], #[]⟩ + | .recursor => some ⟨.rPrj ⟨index.toUInt64, address⟩, #[], #[], #[]⟩ + | .defn _ => some ⟨.dPrj ⟨index.toUInt64, address⟩, #[], #[], #[]⟩ + | _ => none + +/-- The finite domain is in source dependency order; unused empty slots are +omitted. Every nonempty member and source field is serialized literally. -/ +def make? (name : String) (signature : PrimitiveSignature Nat) (input : ProofInput Nat) : Option Case := do + if 900 ∈ input.store.dom then none else do + let mut blobs : ConstantBlobs := [] + let mut literals : ConstantBlobs := [] + let mut references : List (ConstRef Nat × Address) := [] + let mut blocks : List (Nat × Address) := [] + for b in input.store.dom do + let source ← input.store.blocks b + if !source.members.isEmpty then + let (raw, extra) ← block b (reference references) source + let encoded := Fixtures.encode raw + blobs := blobs ++ [encoded] + literals := literals ++ extra + blocks := blocks ++ [(b, encoded.1)] + for (member, index) in source.members.zipIdx do + let address ← match raw.info with + | .muts _ => do + let projection ← memberProjection? member.kind index encoded.1 + let projection := Fixtures.encode projection + blobs := blobs ++ [projection] + pure projection.1 + | _ => pure encoded.1 + references := references ++ [(.member b index, address)] + match member with + | .induct _ _ _ _ ctors _ => + for ctor in List.range ctors.length do + let projection := Fixtures.encode ⟨.cPrj ⟨index.toUInt64, ctor.toUInt64, encoded.1⟩, #[], #[], #[]⟩ + blobs := blobs ++ [projection] + references := references ++ [(.ctor b index ctor, projection.1)] + | _ => pure () + let (raw, extra) ← constant 900 (reference references) + (.defn input.universes .theorem input.proposition input.proof .safe) + let target := Fixtures.encode raw + let falseType ← reference references signature.falseType + let falseElim ← reference references signature.falseElim + let natType ← signature.natType.mapM (reference references) + return ⟨name, ⟨falseType, falseElim, natType⟩, target.1, uniqueBlobs (blobs ++ [target]), + uniqueBlobs (literals ++ extra), references ++ [(.member 900 0, target.1)], blocks ++ [(900, target.1)]⟩ + +def hint? (c : Case) (candidate : Certificate.Modeled.Candidate Nat) : Option ModelHint := do + let source ← ((c.blocks.find? (fun (key, _) => key = candidate.source)).map Prod.snd) + let recursors ← candidate.recursors.mapM (reference c.references) + let targets ← candidate.models.mapM (reference c.references) + let proofs ← candidate.proofs.mapM fun (owner, proofs) => do + let owner ← reference c.references owner + let proofs ← proofs.mapM fun proof => proof.mapM fun proof => do + let .const term _ := proof.proof | none + return (⟨← reference c.references proof.equality, ← reference c.references proof.reflexivity, + ← reference c.references proof.recursor, ← reference c.references term⟩ : ModelProofHint) + return (⟨owner, proofs⟩ : ModelRuleHints) + return ⟨source, recursors, targets, proofs⟩ + +/-- Reconstruct every original mutual member at the mutated wire addresses. +This deliberately unauthenticated control is used only to obtain a complete, +valid certificate before attacking the authenticated acceptance gate. -/ +def repaired? (c : Case) (original : ProofInput Nat) : Option PreparedInput := do + let prepared ← prepare? 6400 c.profile c.target c.blobs c.literals + let mapping := fun b => ((c.blocks.find? fun (key, _) => key = b).map Prod.snd).getD c.target + let originals := c.blocks.map fun (b, a) => (a, (if b = 900 then + some (⟨[.defn original.universes .theorem original.proposition original.proof .safe]⟩ : Block Nat) + else original.store.blocks b).map (fun source => + (⟨source.members.map (renameConstant mapping)⟩ : Block Address))) + let old := prepared.input.store + let store : Store Address := { + dom := old.dom, nodup := old.nodup, + blocks := fun a => (old.blocks a).map fun block => ((lookup originals a).join).getD block, + mem_dom := by intro a; simp [old.mem_dom] } + let .defn _ _ proposition proof _ ← store.lookup (.member c.target 0) | none + return { prepared with input := { prepared.input with store, proposition, proof } } + +end Tests.Certified.ModelSerialize diff --git a/Tests/Certified/Modeled.lean b/Tests/Certified/Modeled.lean new file mode 100644 index 000000000..6afd6f9a5 --- /dev/null +++ b/Tests/Certified/Modeled.lean @@ -0,0 +1,140 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Certified.FeatureCases +import Tests.Certified.ModelSerialize +import Tests.Certified.Claims +import Ix.Certified.Command +import Tests.Theory.ModeledNested +import Tests.Theory.ModeledPermutation +import Tests.Theory.ModeledEquations + +/-! Modeled inductive tests exercise canonical mutual Ixon blocks, lazy .ixe loading, +checked model declarations, public TcM execution and versioned claims. -/ + +namespace Tests.Certified.Modeled + +open Ix.Theory Ix.Theory.Certified Ix.Certified Ix.Kernel Serialize +open Tests.Theory.Certified (primitives) +open Lean (toJson) + +set_option maxRecDepth 32768 +set_option maxHeartbeats 64000000 + +def fuel := 6400 + +structure Scenario where + name : String + input : ProofInput Nat + candidate : Certificate.Modeled.Candidate Nat + +def scenarios : List Scenario := [ + ⟨"mutual-unequal", Tests.Theory.Modeled.input, Tests.Theory.Modeled.candidate⟩, + ⟨"mutual-permuted", Tests.Theory.ModeledPermutation.input, Tests.Theory.ModeledPermutation.candidate⟩, + ⟨"auxiliary-merged", Tests.Theory.ModeledPermutation.mergedInput, Tests.Theory.ModeledPermutation.mergedCandidate⟩, + ⟨"nested-parameter", Tests.Theory.ModeledNested.input false, Tests.Theory.ModeledNested.candidate⟩, + ⟨"nested-recursive-Pi", Tests.Theory.ModeledNested.input true, Tests.Theory.ModeledNested.candidate⟩, + ⟨"propositional-equations", Tests.Theory.ModeledEquations.input, Tests.Theory.ModeledEquations.candidate⟩] + +structure Fixture where + scenario : Scenario + source : Case + model : ModelHint + +def fixture? (scenario : Scenario) : Option Fixture := do + let source ← ModelSerialize.make? scenario.name primitives scenario.input + let model ← ModelSerialize.hint? source scenario.candidate + return ⟨scenario, source, model⟩ + +def Fixture.request (f : Fixture) : Command.Request := + ⟨f.source.profile, f.source.target, Source.subjects f.source, Source.selection f.source, [f.model]⟩ + +def requestJson (request : Command.Request) : Lean.Json := Lean.Json.mkObj [ + ("falseType", toJson (hexOfBytes request.profile.falseType.hash)), + ("falseElim", toJson (hexOfBytes request.profile.falseElim.hash)), + ("natType", toJson (request.profile.natType.map (hexOfBytes ·.hash))), + ("target", toJson (hexOfBytes request.target.hash)), + ("subjects", toJson (request.subjects.map (hexOfBytes ·.hash))), + ("objects", toJson (request.selection.objects.map (hexOfBytes ·.hash))), + ("naturals", toJson (request.selection.naturals.map (hexOfBytes ·.hash))), + ("models", toJson (request.models.map ModelHint.json))] + +def sourceAccepted (f : Fixture) : Bool := Id.run do + let some (_, env) := Source.loadedIxe? f.source | return false + let .ok request := Command.readRequest (requestJson f.request) | return false + return (Command.run.{0} "proof" fuel env request).isOk && (Command.run.{0} "store" fuel env request).isOk + +/-- Poisoned legacy caches do not skip either a model declaration or an +equation proof. Failed model checks leave the actual byte cache unchanged. -/ +def warmAndRollback (f : Fixture) : Bool := Id.run do + let some (_, env) := Source.loadedIxe? f.source | return false + let c := f.source + let count := c.blobs.length + c.literals.length + let some witness := suggestSource? fuel env c.profile c.target (Source.selection c) [f.model] | return false + let .ok _ cold := (certifiedStep.{0} fuel env c.profile c.target (Source.selection c) witness).run initialCertifiedState + | return false + if cold.inputCache.misses != count || cold.inputCache.hits != 0 then return false + let start := { cold with checker := Source.poisonedChecker c } + let .ok _ warm := (certifiedStep.{0} fuel env c.profile c.target (Source.selection c) witness).run start | return false + if warm.inputCache.hits != count || warm.inputCache.misses != count then return false + let bad := { witness with declarations := witness.declarations.map fun declaration => match declaration with + | .modeled model => .modeled { model with equations := model.equations.map (fun proofs => + proofs.map (fun proof => { proof with proof := .conversion .refl })) } + | declaration => declaration } + let .error _ failed := (certifiedStep.{0} fuel env c.profile c.target (Source.selection c) bad).run warm | return false + if failed.inputCache.hits != warm.inputCache.hits || failed.inputCache.misses != warm.inputCache.misses then return false + let .error _ exhausted := (certifiedStep.{0} 0 env c.profile c.target (Source.selection c) witness).run failed | return false + if exhausted.inputCache.hits != warm.inputCache.hits || exhausted.inputCache.misses != warm.inputCache.misses then return false + let .ok _ retry := (certifiedStep.{0} fuel env c.profile c.target (Source.selection c) witness).run exhausted | return false + return retry.inputCache.hits == 2 * count && retry.inputCache.misses == count + +def checkClaim? (f : Fixture) : Option Claims.Fixture := + Claims.finish? s!"{f.scenario.name}-check" f.source (.check f.source.target none) + ⟨Source.selection f.source, [⟨.check f.source.target none, none, none⟩], none, none, none, none, [f.model]⟩ + +def environmentClaim? (f : Fixture) : Option Claims.Fixture := do + let subjects := Claims.tree (Source.subjects f.source) + let claim := Ix.Claim.checkEnv (treeRoot subjects) none + Claims.finish? s!"{f.scenario.name}-environment" f.source claim + ⟨Source.selection f.source, [⟨claim, some (treeBytes subjects), none⟩], + some (treeBytes subjects), none, none, none, [f.model]⟩ + +def sharedClaim? (f : Fixture) : Option Claims.Fixture := do + let model ← f.model.targets.getLast? + let subjects := Claims.tree [model, f.source.target] + let claim := Ix.Claim.checkEnv (treeRoot subjects) none + Claims.finish? s!"{f.scenario.name}-shared-model" f.source claim + ⟨Source.selection f.source, [⟨.check model none, none, none⟩, ⟨.check f.source.target none, none, none⟩], + some (treeBytes subjects), none, none, none, [f.model]⟩ + +def claimCases? (f : Fixture) : Option (List Claims.WireCase) := do + let fixtures ← [checkClaim? f, environmentClaim? f, sharedClaim? f].mapM id + return fixtures.flatMap fun fixture => [ + ⟨fixture.name, fixture.source, fixture.envelope, .logical fixture.hint, true⟩, + ⟨s!"{fixture.name}-old-checker", fixture.source, + { fixture.envelope with protocol := { fixture.envelope.protocol with checker := 1 } }, + .logical fixture.hint, false⟩] + +def writeSource (directory : System.FilePath) (f : Fixture) : IO Unit := do + let directory := directory / f.scenario.name + IO.FS.createDirAll directory + let some (bytes, _) := Source.loadedIxe? f.source | throw (IO.userError "cannot write modeled .ixe") + IO.FS.writeBinFile (directory / "source.ixe") bytes + IO.FS.writeFile (directory / "request.json") ((requestJson f.request).pretty ++ "\n") + IO.FS.writeFile (directory / "expected.json") + ((Lean.Json.mkObj [("proof", toJson true), ("store", toJson true)]).compress ++ "\n") + +def run (directory : Option System.FilePath := none) : IO Unit := do + for scenario in scenarios do + let some f := fixture? scenario | throw (IO.userError s!"modeled serialization failed: {scenario.name}") + unless sourceAccepted f do throw (IO.userError s!"modeled source acceptance failed: {scenario.name}") + unless warmAndRollback f do throw (IO.userError s!"modeled cache or rollback failed: {scenario.name}") + let some claims := claimCases? f | throw (IO.userError s!"modeled claim construction failed: {scenario.name}") + for claim in claims do Claims.runWire (directory.map (· / "claims")) claim + if let some directory := directory then writeSource (directory / "source") f + IO.println <| (Lean.Json.mkObj [("case", toJson scenario.name), ("source", toJson true), + ("cacheRollback", toJson true), ("claims", toJson claims.length)]).compress + +end Tests.Certified.Modeled diff --git a/Tests/Certified/ModeledAdversarial.lean b/Tests/Certified/ModeledAdversarial.lean new file mode 100644 index 000000000..adae735f6 --- /dev/null +++ b/Tests/Certified/ModeledAdversarial.lean @@ -0,0 +1,232 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Certified.Modeled + +namespace Tests.Certified.ModeledAdversarial + +open Ix.Theory Ix.Theory.Certified Ix.Certified Ix.Kernel Serialize Modeled +open Lean (toJson) + +set_option maxRecDepth 32768 +set_option maxHeartbeats 64000000 + +def changeBlock (input : ProofInput Nat) (key : Nat) (alter : Block Nat → Block Nat) : ProofInput Nat := + let old := input.store + { input with store := { + dom := old.dom, nodup := old.nodup, + blocks := fun b => (old.blocks b).map fun block => if b = key then alter block else block, + mem_dom := by intro b; simp [old.mem_dom] } } + +def changeMembers (input : ProofInput Nat) (key : Nat) (alter : Const Nat → Const Nat) : ProofInput Nat := + changeBlock input key fun block => ⟨block.members.map alter⟩ + +def changeRecursor (tag : String) : Const Nat → Const Nat + | .recursor u p i m n t rs k s => match tag with + | "global-rule-order" => .recursor u p i m n t rs.reverse k s + | "omitted-global-rule" => .recursor u p i m n t (rs.take 2) k s + | "rule-fields" => .recursor u p i m n t (rs.map fun r => { r with nfields := r.nfields + 1 }) k s + | "rule-rhs" => .recursor u p i m n t (rs.map fun r => { r with rhs := .sort .zero }) k s + | "rule-telescope" => .recursor u p i m n t (rs.map fun r => { r with rhs := match r.rhs with + | .lam _ body => .lam (.sort .zero) body | body => body }) k s + | "k" => .recursor u p i m n t rs true s + | "unsafe-recursor" => .recursor u p i m n t rs k .unsafe + | "parameters" => .recursor u (p + 1) i m n t rs k s + | "indices" => .recursor u p (i + 1) m n t rs k s + | "motives" => .recursor u p i (m + 1) n t rs k s + | "minors" => .recursor u p i m (n + 1) t rs k s + | "levels" => .recursor (u + 1) p i m n t rs k s + | "level-permutation" => .recursor u p i m n (t.instL [.param 1, .param 0]) rs k s + | _ => .recursor u p i m n t rs k s + | c => c + +def changeFamily (tag : String) : Const Nat → Const Nat + | .induct u p i t cs s => match tag with + | "constructor-order" => .induct u p i t cs.reverse s + | "constructor-fields" => .induct u p i t (cs.map fun c => { c with nfields := c.nfields + 1 }) s + | "constructor-levels" => .induct u p i t (cs.map fun c => { c with uvars := c.uvars + 1 }) s + | "constructor-parameters" => .induct u p i t (cs.map fun c => { c with nparams := c.nparams + 1 }) s + | "unsafe-family" => .induct u p i t cs .unsafe + | "unsafe-constructor" => .induct u p i t (cs.map fun c => { c with safety := .unsafe }) s + | _ => .induct u p i t cs s + | c => c + +structure SourceMutation where + name : String + original : Scenario + input : ProofInput Nat + +def mutualCase : Scenario := ⟨"mutual-unequal", Tests.Theory.Modeled.input, Tests.Theory.Modeled.candidate⟩ +def nested : Scenario := ⟨"nested-parameter", Tests.Theory.ModeledNested.input false, Tests.Theory.ModeledNested.candidate⟩ +def recursivePi : Scenario := ⟨"nested-recursive-Pi", Tests.Theory.ModeledNested.input true, Tests.Theory.ModeledNested.candidate⟩ +def propositional : Scenario := ⟨"propositional-equations", Tests.Theory.ModeledEquations.input, Tests.Theory.ModeledEquations.candidate⟩ + +def sourceMutations : List SourceMutation := + (["global-rule-order", "omitted-global-rule", "rule-fields", "rule-rhs", "rule-telescope", + "k", "unsafe-recursor", "parameters", "indices", "motives", "minors", "levels"].map fun tag => + ⟨s!"mutual-{tag}", mutualCase, changeMembers mutualCase.input 31 (changeRecursor tag)⟩) ++ + (["constructor-order", "constructor-fields", "constructor-levels", "constructor-parameters", + "unsafe-family", "unsafe-constructor"].map fun tag => + ⟨s!"mutual-{tag}", mutualCase, changeMembers mutualCase.input 30 (changeFamily tag)⟩) ++ [ + ⟨"mutual-family-type", mutualCase, changeMembers mutualCase.input 30 fun c => match c with + | .induct u p i _ cs s => .induct u p i (.sort (.succ (.succ .zero))) cs s | c => c⟩, + ⟨"mutual-recursor-order", mutualCase, changeBlock mutualCase.input 31 fun b => ⟨b.members.reverse⟩⟩, + ⟨"unproved-model-axiom", mutualCase, changeMembers mutualCase.input 40 fun c => .axiom c.uvars c.type .safe⟩, + ⟨"changed-model-body", mutualCase, changeMembers mutualCase.input 40 fun c => match c with + | .defn n k t _ s => .defn n k t (.lam Tests.Theory.Modeled.natType (.const (.ctor 20 0 0) [])) s + | c => c⟩, + ⟨"circular-model", mutualCase, changeMembers mutualCase.input 40 fun c => match c with + | .defn n k t _ s => .defn n k t (.const (.ctor 30 1 0) []) s | c => c⟩, + ⟨"nested-level-permutation", nested, changeMembers nested.input 41 (changeRecursor "level-permutation")⟩, + ⟨"nested-rule-fields", nested, changeMembers nested.input 41 (changeRecursor "rule-fields")⟩, + ⟨"recursive-Pi-level-permutation", recursivePi, + changeMembers recursivePi.input 41 (changeRecursor "level-permutation")⟩, + ⟨"recursive-Pi-rule-telescope", recursivePi, + changeMembers recursivePi.input 41 (changeRecursor "rule-telescope")⟩, + ⟨"unproved-equation-axiom", propositional, + changeMembers propositional.input 70 fun c => .axiom c.uvars c.type .safe⟩, + ⟨"unrelated-equation-proof", propositional, + changeMembers propositional.input 70 fun c => match c with + | .defn n k t _ s => .defn n k t (.const (.member 40 0) []) s + | c => c⟩] + +def mutationFixture? (m : SourceMutation) : Option Fixture := + fixture? { m.original with name := m.name, input := m.input } + +def control? (f : Fixture) (original : ProofInput Nat) : Option (PreparedInput × ProofWitness Address) := do + let prepared ← ModelSerialize.repaired? f.source original + let objects ← decodeObjects? f.source.blobs + let models ← modelCandidates? objects prepared.input.store [f.model] + let witness ← Certificate.proofWitness? fuel prepared.signature prepared.input models + return (prepared, witness) + +/-- A complete semantic certificate succeeds on the original declarations at +the new addresses, then fails on the actual authenticated altered source. +The same declaration certificate is submitted to the claim acceptance root. -/ +def rejectsSource (f : Fixture) (original : ProofInput Nat) : Bool := Id.run do + let some (prepared, witness) := control? f original | return false + unless acceptsCertified.{0,0} fuel prepared.signature prepared.input witness do return false + let some (_, env) := Source.loadedIxe? f.source | return false + if acceptsCertifiedSource.{0} fuel env f.source.profile f.source.target (Source.selection f.source) witness then + return false + if acceptsCertifiedStoreSource.{0} fuel env f.source.profile (Source.subjects f.source) + (Source.selection f.source) witness.declarations then return false + let claim := Ix.Claim.check f.source.target none + let envelope : Envelope := ⟨Protocol.current, f.source.profile, claim, none⟩ + let leaf : LeafWitness := ⟨claim, none, none, [], witness.declarations⟩ + let logical : LogicalWitness := ⟨Source.selection f.source, [leaf], none, none, none, [], none⟩ + let bytes := envelopeBytes envelope + return !acceptsCertifiedClaim.{0} fuel env (Address.blake3 bytes) bytes (.logical logical) + +def witnessMutations : List (String × (Ix.Theory.Certified.Modeled.Witness Address → + Ix.Theory.Certified.Modeled.Witness Address)) := [ + ("recursor-order", fun w => { w with recursors := w.recursors.reverse }), + ("companion-order", fun w => { w with companions := w.companions.reverse }), + ("missing-companion", fun w => { w with companions := w.companions.drop 1 }), + ("missing-equation", fun w => { w with equations := w.equations.drop 1 }), + ("self-model", fun w => { w with companions := w.companions.map fun c => { c with model := c.header.ref } }), + ("unrelated-left-side", fun w => { w with companions := w.companions.map fun c => + { c with rules := c.rules.map fun r => { r with lhs := r.rhs } } }), + ("forged-conversion", fun w => { w with equations := w.equations.map (fun ps => + ps.map fun p => { p with proof := .conversion .refl }) })] + +def rejectsWitnesses (f : Fixture) : Bool := Id.run do + let env := Source.source f.source + let some witness := suggestSource? fuel env f.source.profile f.source.target (Source.selection f.source) [f.model] + | return false + return witnessMutations.all fun (_, alter) => + let witness := { witness with declarations := witness.declarations.map fun declaration => match declaration with + | .modeled model => .modeled (alter model) | declaration => declaration } + !acceptsCertifiedSource.{0} fuel env f.source.profile f.source.target (Source.selection f.source) witness && + !acceptsCertifiedStoreSource.{0} fuel env f.source.profile (Source.subjects f.source) + (Source.selection f.source) witness.declarations + +def negativeWire (f : Fixture) : Claims.WireCase := + let claim := Ix.Claim.check f.source.target none + ⟨f.scenario.name, f.source, ⟨Protocol.current, f.source.profile, claim, none⟩, + .logical ⟨Source.selection f.source, [⟨claim, none, none⟩], none, none, none, none, [f.model]⟩, false⟩ + +def writeNegative (directory : System.FilePath) (f : Fixture) : IO Unit := do + let directory := directory / f.scenario.name + IO.FS.createDirAll directory + let some (bytes, _) := Source.loadedIxe? f.source | throw (IO.userError "cannot write mutated .ixe") + IO.FS.writeBinFile (directory / "source.ixe") bytes + IO.FS.writeFile (directory / "request.json") ((Modeled.requestJson f.request).pretty ++ "\n") + IO.FS.writeFile (directory / "expected.json") + ((Lean.Json.mkObj [("proof", toJson false), ("store", toJson false)]).compress ++ "\n") + +def ingressNames : List String := ["family-order", "constructor-index", "family-wrapper-kind", + "family-wrapper-slot", "recursor-wrapper-kind", "recursor-wrapper-slot", "constructor-wrapper-slot", + "missing-universe-table", "missing-reference-table"] + +/-- These mutations preserve fresh hashes and update every dependent address. +Malformed mutual projections and tables must fail even before model search. -/ +def ingressFixture? (name : String) : Option Fixture := do + let original ← fixture? mutualCase + let family ← reference original.source.references (.member 30 0) + let recursor ← reference original.source.references (.member 31 0) + let constructor ← reference original.source.references (.ctor 30 1 0) + let source ← rehash? original.source id fun address source => + if address = original.model.source && name = "family-order" then + match source.info with + | .muts members => { source with info := .muts members.reverse } + | _ => source + else if address = original.model.source && name = "constructor-index" then + match source.info with + | .muts members => { source with info := .muts (members.map fun member => match member with + | .indc family => .indc { family with ctors := family.ctors.map fun ctor => { ctor with cidx := ctor.cidx + 1 } } + | member => member) } + | _ => source + else if address = family && name = "family-wrapper-kind" then + match source.info with + | .iPrj p => { source with info := .rPrj ⟨p.idx, p.block⟩ } | _ => source + else if address = family && name = "family-wrapper-slot" then + match source.info with + | .iPrj p => { source with info := .iPrj { p with idx := 999 } } | _ => source + else if address = recursor && name = "recursor-wrapper-kind" then + match source.info with + | .rPrj p => { source with info := .dPrj ⟨p.idx, p.block⟩ } | _ => source + else if address = recursor && name = "recursor-wrapper-slot" then + match source.info with + | .rPrj p => { source with info := .rPrj { p with idx := 999 } } | _ => source + else if address = constructor && name = "constructor-wrapper-slot" then + match source.info with + | .cPrj p => { source with info := .cPrj { p with cidx := 999 } } | _ => source + else if address = original.source.target && name = "missing-universe-table" then { source with univs := #[] } + else if address = original.source.target && name = "missing-reference-table" then { source with refs := #[] } + else source + let name := s!"ingress-{name}" + let model ← ModelSerialize.hint? source mutualCase.candidate + return ⟨{ mutualCase with name }, { source with name }, model⟩ + +def run (directory : Option System.FilePath := none) : IO Unit := do + for scenario in scenarios do + let some f := fixture? scenario | throw (IO.userError s!"modeled witness fixture failed: {scenario.name}") + unless rejectsWitnesses f do throw (IO.userError s!"modeled forged witness escaped: {scenario.name}") + for m in sourceMutations do + let some f := mutationFixture? m | throw (IO.userError s!"modeled mutation serialization failed: {m.name}") + unless rejectsSource f m.original.input do throw (IO.userError s!"modeled valid-control source mutation escaped: {m.name}") + let some (_, env) := Source.loadedIxe? f.source | throw (IO.userError s!"modeled mutation load failed: {m.name}") + if (Command.run.{0} "proof" fuel env f.request).isOk || (Command.run.{0} "store" fuel env f.request).isOk then + throw (IO.userError s!"modeled mutated command accepted: {m.name}") + Claims.runWire (directory.map (· / "claims")) (negativeWire f) + if let some directory := directory then writeNegative (directory / "source") f + IO.println <| (Lean.Json.mkObj [("sourceMutation", toJson m.name), ("controlAccepted", toJson true), + ("sourceRejected", toJson true), ("claimRejected", toJson true)]).compress + for name in ingressNames do + let some f := ingressFixture? name | throw (IO.userError s!"modeled ingress construction failed: {name}") + unless (prepare? fuel f.source.profile f.source.target f.source.blobs f.source.literals).isNone do + throw (IO.userError s!"modeled malformed mutual source decoded: {name}") + let some (_, env) := Source.loadedIxe? f.source | throw (IO.userError s!"modeled ingress .ixe load failed: {name}") + if (Command.run.{0} "proof" fuel env f.request).isOk || (Command.run.{0} "store" fuel env f.request).isOk then + throw (IO.userError s!"modeled malformed mutual command accepted: {name}") + Claims.runWire (directory.map (· / "claims")) (negativeWire f) + if let some directory := directory then writeNegative (directory / "source") f + IO.println <| (Lean.Json.mkObj [("sourceMutations", toJson sourceMutations.length), + ("forgedWitnesses", toJson (scenarios.length * witnessMutations.length)), + ("ingressMutations", toJson ingressNames.length), + ("unexpectedErrors", toJson (0 : Nat))]).compress + +end Tests.Certified.ModeledAdversarial diff --git a/Tests/Certified/ModeledMain.lean b/Tests/Certified/ModeledMain.lean new file mode 100644 index 000000000..45dedc808 --- /dev/null +++ b/Tests/Certified/ModeledMain.lean @@ -0,0 +1,11 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Certified.ModeledAdversarial + +def main (args : List String) : IO Unit := do + let directory := (args.head?).map System.FilePath.mk + Tests.Certified.Modeled.run directory + Tests.Certified.ModeledAdversarial.run directory diff --git a/Tests/Certified/Ordinary.lean b/Tests/Certified/Ordinary.lean new file mode 100644 index 000000000..d68a9e49e --- /dev/null +++ b/Tests/Certified/Ordinary.lean @@ -0,0 +1,236 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Corpus +import Tests.Theory.OrdinaryAcceptance + +/-! Actual canonical Ixon bytes for ordinary source blocks and closed proofs. +The serializer is test infrastructure. Witness generation receives only the +decoded store and original target, and the serialized acceptance gate reruns +all independent checks. -/ + +namespace Tests.Certified.Ordinary + +open Ix.Theory Ix.Theory.Model Ix.Theory.Certified Ix.Theory.Certified.Ordinary +open Ix.Certified Ix.Certified.Fixtures +open Tests.Theory.OrdinaryAcceptance +open Lean (toJson) + +set_option maxRecDepth 8192 +set_option maxHeartbeats 8000000 + +local instance : DecidableEq Address := Ix.Certified.addressDecidableEq + +structure Tables where + refs : Array Address := #[] + univs : Array Ixon.Univ := #[] + +abbrev Write := StateT Tables Option + +def univ : VLevel → Ixon.Univ + | .zero => .zero + | .succ u => .succ (univ u) + | .max u v => .max (univ u) (univ v) + | .imax u v => .imax (univ u) (univ v) + | .param i => .var i.toUInt64 + +def level (u : VLevel) : Write UInt64 := do + let s ← get + set { s with univs := s.univs.push (univ u) } + return s.univs.size.toUInt64 + +def address (a : Address) : Write UInt64 := do + let s ← get + set { s with refs := s.refs.push a } + return s.refs.size.toUInt64 + +def expression (self : Nat) (resolve : ConstRef Nat → Option Address) : + VExpr Nat → Write Ixon.Expr + | .sort u => return .sort (← level u) + | .bvar i => return .var i.toUInt64 + | .const r us => do + let us ← us.mapM level + match r with + | .member block member => + if block = self then return .recur member.toUInt64 us.toArray + else return .ref (← address (← resolve r)) us.toArray + | .ctor .. => return .ref (← address (← resolve r)) us.toArray + | .app f a => return .app (← expression self resolve f) (← expression self resolve a) + | .lam A b => return .leanLam (← expression self resolve A) (← expression self resolve b) + | .forallE A B => return .leanAll (← expression self resolve A) (← expression self resolve B) + | .proj .. | .natLit .. => failure + +def constructor (self : Nat) (resolve : ConstRef Nat → Option Address) + (ctor : Ctor Nat) (index : Nat) : Write Ixon.Constructor := do + return { + isUnsafe := ctor.safety != .safe, lvls := ctor.uvars.toUInt64, + cidx := index.toUInt64, params := ctor.nparams.toUInt64, fields := ctor.nfields.toUInt64, + typ := ← expression self resolve ctor.type } + +def constantInfo (self : Nat) (resolve : ConstRef Nat → Option Address) : + Const Nat → Write Ixon.ConstantInfo + | .induct n p i type ctors safety => do + let typ ← expression self resolve type + let ctors ← ctors.zipIdx.mapM fun (ctor, index) => constructor self resolve ctor index + return .muts #[.indc { + isUnsafe := safety != .safe, + lvls := n.toUInt64, params := p.toUInt64, indices := i.toUInt64, typ, ctors := ctors.toArray }] + | .recursor n p i m b type rules k safety => do + let typ ← expression self resolve type + let rules ← rules.mapM fun rule => do + return ({ + fields := rule.nfields.toUInt64, + rhs := ← expression self resolve rule.rhs } : Ixon.RecursorRule) + return .recr { + k, isUnsafe := safety != .safe, lvls := n.toUInt64, + params := p.toUInt64, indices := i.toUInt64, motives := m.toUInt64, + minors := b.toUInt64, typ, rules := rules.toArray } + | .defn n _ type body .safe => do + return .defn { + kind := .thm, safety := .safe, lvls := n.toUInt64, + typ := ← expression self resolve type, value := ← expression self resolve body } + | _ => failure + +def constant (self : Nat) (resolve : ConstRef Nat → Option Address) (source : Const Nat) : + Option Ixon.Constant := do + let (info, tables) ← constantInfo self resolve source {} + return ⟨info, #[], tables.refs, tables.univs⟩ + +structure Case extends Corpus.Case where + source : Address + recursor : Address + mode : Inductive.ElimMode + +def make? (name : String) (shape : Shape Nat) (mode : Inductive.ElimMode) + (body proposition : AExpr Nat) (mutateRec : Const Nat → Const Nat := id) + (mutateFamily : Ixon.Constant → Ixon.Constant := id) : Option Case := do + let family := mutateFamily (← constant 100 (fun _ => none) (shape.source 100)) + let source := encode family + let projection := encode ⟨.iPrj ⟨0, source.1⟩, #[], #[], #[]⟩ + let ctors := (List.range shape.constructors.length).map fun index => + encode ⟨.cPrj ⟨0, index.toUInt64, source.1⟩, #[], #[], #[]⟩ + let resolve := fun r => match r with + | .member 100 0 => some projection.1 + | .ctor 100 0 index => (ctors[index]?).map Prod.fst + | _ => none + let recursor ← constant 101 resolve (mutateRec (shape.recursorSource 100 101 mode)) + let recursor := encode recursor + let resolve := fun r => if r = .member 101 0 then some recursor.1 else resolve r + let target ← constant 200 resolve (.defn 0 .theorem proposition.erase body.erase .safe) + let target := encode target + return { + name, target := target.1, + blobs := prelude ++ [source, projection] ++ ctors ++ [recursor, target], + source := source.1, recursor := recursor.1, mode } + +def acceptedCase (c : Case) : Bool := + match prepare? 2000 profile c.target c.blobs with + | none => false + | some prepared => (Ix.Theory.Certificate.proofWitness? 2000 prepared.signature prepared.input).any + (acceptsSerialized.{0} 2000 profile c.target c.blobs) + +def positive : List (Option Case) := [ + make? "Nat-iota" Tests.Theory.Ordinary.natShape .large identity (proposition natComputed), + make? "List-distinct-universes" Tests.Theory.Ordinary.listShape .large identity (proposition listComputed), + make? "indexed-iota" Tests.Theory.Ordinary.indexedShape .large identity (proposition indexedComputed), + make? "function-field-iota" functionalShape .large identity (proposition functionalComputed), + make? "multiple-Prop-small" Tests.Theory.Ordinary.manyProp .small smallProof identityType +] + +#guard positive.length = 5 +#guard positive.all (fun c => c.any acceptedCase) + +def corruptRule : Const Nat → Const Nat + | .recursor n p i m b type (_ :: rules) k safety => + .recursor n p i m b type (⟨0, .sort .zero⟩ :: rules) k safety + | other => other + +def unsafeRecursor : Const Nat → Const Nat + | .recursor n p i m b type rules k _ => .recursor n p i m b type rules k .unsafe + | other => other + +def enableK : Const Nat → Const Nat + | .recursor n p i m b type rules _ safety => .recursor n p i m b type rules true safety + | other => other + +def replaceBlock (store : Store Address) (address : Address) (block : Block Address) : Store Address where + dom := store.dom + nodup := store.nodup + blocks a := (store.blocks a).map fun old => if a = address then block else old + mem_dom a := by simp [store.mem_dom] + +/-- Construct a valid certificate with the original canonical recursor at the +new address, then submit it against the authenticated altered source. This +tests the acceptance boundary independently of producer refusal. -/ +def rejectsWithValidWitness (c : Case) : Bool := Id.run do + let some prepared := prepare? 2000 profile c.target c.blobs | return false + let some raw := prepared.input.store.lookup (.member c.source 0) | return false + let some shape := Ix.Theory.Certificate.Ordinary.description? 2000 + prepared.signature.environment c.source raw | return false + let repaired := replaceBlock prepared.input.store c.recursor + ⟨[shape.recursorSource c.source c.recursor c.mode]⟩ + let input := { prepared.input with store := repaired } + let some witness := Ix.Theory.Certificate.proofWitness? 2000 prepared.signature input | return false + return acceptsCertified.{0,0} 2000 prepared.signature input witness && + !acceptsSerialized.{0} 2000 profile c.target c.blobs witness + +#guard [corruptRule, unsafeRecursor, enableK].all fun mutation => + (make? "forged-recursor" Tests.Theory.Ordinary.natShape .large identity + (proposition natComputed) mutation).any rejectsWithValidWitness + +def wrongPosition (source : Ixon.Constant) : Ixon.Constant := + match source.info with + | .muts #[.indc family] => { source with info := .muts #[.indc + { family with ctors := family.ctors.map fun ctor => { ctor with cidx := ctor.cidx + 1 } }] } + | _ => source + +#guard (make? "wrong-constructor-position" Tests.Theory.Ordinary.natShape .large identity + (proposition natComputed) id wrongPosition).any fun c => + (prepare? 2000 profile c.target c.blobs).isNone + +def writeCase (directory : System.FilePath) (c : Case) : IO Unit := do + let directory := directory / c.name + IO.FS.createDirAll directory + for (address, bytes) in c.blobs do + IO.FS.writeBinFile (directory / s!"{hexOfBytes address.hash}.ixon") bytes + let metadata := Lean.Json.mkObj [ + ("target", toJson (hexOfBytes c.target.hash)), + ("falseType", toJson (hexOfBytes profile.falseType.hash)), + ("falseElim", toJson (hexOfBytes profile.falseElim.hash))] + IO.FS.writeFile (directory / "target.json") (metadata.pretty ++ "\n") + +def run (directory : Option System.FilePath) : IO Unit := do + let mut accepted := 0 + for candidate in positive do + let some c := candidate | throw (IO.userError "serialized fixture construction failed") + unless acceptedCase c do throw (IO.userError s!"serialized acceptance failed: {c.name}") + accepted := accepted + 1 + if let some directory := directory then writeCase directory c + let mut rejected := 0 + for (name, mutation) in [("changed-rule", corruptRule), ("unsafe-recursor", unsafeRecursor), + ("unsupported-K", enableK)] do + let some c := make? name Tests.Theory.Ordinary.natShape .large identity + (proposition natComputed) mutation | throw (IO.userError "malformed fixture construction failed") + unless rejectsWithValidWitness c do throw (IO.userError s!"malformed source accepted: {name}") + rejected := rejected + 1 + if let some directory := directory then writeCase directory c + let some wrong := make? "wrong-constructor-position" Tests.Theory.Ordinary.natShape .large identity + (proposition natComputed) id wrongPosition | throw (IO.userError "position fixture construction failed") + unless (prepare? 2000 profile wrong.target wrong.blobs).isNone do + throw (IO.userError "wrong constructor position accepted") + if let some directory := directory then writeCase directory wrong + IO.println <| (Lean.Json.mkObj [ + ("serializedAccepted", toJson accepted), ("serializedRejected", toJson rejected), + ("ingressRejected", toJson (1 : Nat)), + ("unexpectedErrors", toJson (0 : Nat))]).compress + +end Tests.Certified.Ordinary + +def main (args : List String) : IO UInt32 := do + if args.length > 1 then + IO.eprintln "usage: certified-ordinary-tests [OUTPUT_DIRECTORY]" + return 2 + Tests.Certified.Ordinary.run (args.head?.map System.FilePath.mk) + return 0 diff --git a/Tests/Certified/Serialize.lean b/Tests/Certified/Serialize.lean new file mode 100644 index 000000000..5289854c6 --- /dev/null +++ b/Tests/Certified/Serialize.lean @@ -0,0 +1,243 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Fixtures + +/-! Test-only serializer for singleton source stores. It preserves every +source kind and field, emits real content-addressed projection wrappers, and +places natural literal bytes in their separate authenticated blob domain. -/ + +namespace Tests.Certified.Serialize + +open Ix.Theory Ix.Theory.Certified Ix.Certified Ix.Certified.Fixtures + +structure Tables where + refs : Array Address := #[] + univs : Array Ixon.Univ := #[] + literals : ConstantBlobs := [] + +abbrev Write := StateT Tables Option + +def univ : VLevel → Ixon.Univ + | .zero => .zero + | .succ u => .succ (univ u) + | .max u v => .max (univ u) (univ v) + | .imax u v => .imax (univ u) (univ v) + | .param i => .var i.toUInt64 + +def level (u : VLevel) : Write UInt64 := do + let s ← get + set { s with univs := s.univs.push (univ u) } + return s.univs.size.toUInt64 + +def address (a : Address) : Write UInt64 := do + let s ← get + set { s with refs := s.refs.push a } + return s.refs.size.toUInt64 + +def expression (self : Nat) (resolve : ConstRef Nat → Option Address) : + VExpr Nat → Write Ixon.Expr + | .sort u => return .sort (← level u) + | .bvar i => return .var i.toUInt64 + | .const r us => do + let us ← us.mapM level + match r with + | .member block member => + if block = self then return .recur member.toUInt64 us.toArray + else return .ref (← address (← resolve r)) us.toArray + | .ctor .. => return .ref (← address (← resolve r)) us.toArray + | .app f a => return .app (← expression self resolve f) (← expression self resolve a) + | .lam A b => return .leanLam (← expression self resolve A) (← expression self resolve b) + | .forallE A B => return .leanAll (← expression self resolve A) (← expression self resolve B) + | .proj r i s => return .prj (← address (← resolve r)) i.toUInt64 (← expression self resolve s) + | .natLit n => do + let bytes := ByteArray.mk n.toBytesLE + let a := Address.blake3 bytes + modify fun s => { s with literals := s.literals ++ [(a, bytes)] } + return .nat (← address a) + +def safety : Safety → Ix.DefinitionSafety + | .safe => .safe + | .unsafe => .unsaf + | .partial => .part + +def kind : Ix.Theory.DefKind → Ix.DefKind + | .definition => .defn + | .theorem => .thm + | .opaque => .opaq + +def quotKind : Ix.Theory.QuotKind → Ix.QuotKind + | .type => .type + | .ctor => .ctor + | .lift => .lift + | .ind => .ind + +def constantInfo (self : Nat) (resolve : ConstRef Nat → Option Address) : + Const Nat → Write Ixon.ConstantInfo + | .induct n p i type ctors s => do + let typ ← expression self resolve type + let ctors ← ctors.zipIdx.mapM fun (ctor, index) => do + return ({ + isUnsafe := ctor.safety != .safe, lvls := ctor.uvars.toUInt64, + cidx := index.toUInt64, params := ctor.nparams.toUInt64, fields := ctor.nfields.toUInt64, + typ := ← expression self resolve ctor.type } : Ixon.Constructor) + return .muts #[.indc { + isUnsafe := s != .safe, lvls := n.toUInt64, + params := p.toUInt64, indices := i.toUInt64, typ, ctors := ctors.toArray }] + | .recursor n p i m b type rules k s => do + let typ ← expression self resolve type + let rules ← rules.mapM fun rule => do + return ({ + fields := rule.nfields.toUInt64, + rhs := ← expression self resolve rule.rhs } : Ixon.RecursorRule) + return .recr { + k, isUnsafe := s != .safe, lvls := n.toUInt64, + params := p.toUInt64, indices := i.toUInt64, motives := m.toUInt64, + minors := b.toUInt64, typ, rules := rules.toArray } + | .defn n k type body s => do + return .defn { + kind := kind k, safety := safety s, lvls := n.toUInt64, + typ := ← expression self resolve type, value := ← expression self resolve body } + | .axiom n type s => do + return .axio { isUnsafe := s != .safe, lvls := n.toUInt64, typ := ← expression self resolve type } + | .quot k n type => do + return .quot { kind := quotKind k, lvls := n.toUInt64, typ := ← expression self resolve type } + +def constant (self : Nat) (resolve : ConstRef Nat → Option Address) (source : Const Nat) : + Option (Ixon.Constant × ConstantBlobs) := do + let (info, tables) ← constantInfo self resolve source {} + return (⟨info, #[], tables.refs, tables.univs⟩, tables.literals) + +structure Case where + name : String + profile : Profile + target : Address + blobs : ConstantBlobs + literals : ConstantBlobs + references : List (ConstRef Nat × Address) + blocks : List (Nat × Address) + +def reference (references : List (ConstRef Nat × Address)) (r : ConstRef Nat) : Option Address := + (references.find? fun (key, _) => key = r).map Prod.snd + +def uniqueBlobs (blobs : ConstantBlobs) : ConstantBlobs := + blobs.foldl (fun acc entry => if (lookup acc entry.1).isSome then acc else acc ++ [entry]) [] + +/-- Source domains are supplied in dependency order. Each fixture must provide +all declarations; witness construction receives only the decoded result. -/ +def make? (name : String) (signature : PrimitiveSignature Nat) (input : ProofInput Nat) : + Option Case := do + if signature.falseType != .member 10 0 || signature.falseElim != .member 11 0 || 900 ∈ input.store.dom then + none + else do + let mut blobs := prelude + let mut literals := [] + let mut references := [(.member 10 0, falseObject.1), (.member 11 0, falseElimObject.1)] + let mut blocks := [(10, falseBlockObject.1), (11, falseElimObject.1)] + for b in input.store.dom.filter (fun b => b != 10 && b != 11) do + let source ← input.store.lookup (.member b 0) + let (raw, extra) ← constant b (reference references) source + let encoded := encode raw + blobs := blobs ++ [encoded] + literals := literals ++ extra + blocks := blocks ++ [(b, encoded.1)] + match source with + | .induct _ _ _ _ ctors _ => + let projection := encode ⟨.iPrj ⟨0, encoded.1⟩, #[], #[], #[]⟩ + blobs := blobs ++ [projection] + references := references ++ [(.member b 0, projection.1)] + for i in List.range ctors.length do + let ctor := encode ⟨.cPrj ⟨0, i.toUInt64, encoded.1⟩, #[], #[], #[]⟩ + blobs := blobs ++ [ctor] + references := references ++ [(.ctor b 0 i, ctor.1)] + | _ => references := references ++ [(.member b 0, encoded.1)] + let (raw, extra) ← constant 900 (reference references) + (.defn input.universes .theorem input.proposition input.proof .safe) + let target := encode raw + let natType ← match signature.natType with + | none => some none + | some r => (reference references r).map some + return { + name, profile := { profile with natType }, target := target.1, + blobs := blobs ++ [target], literals := uniqueBlobs (literals ++ extra), + references := references ++ [(.member 900 0, target.1)], blocks := blocks ++ [(900, target.1)] } + +def renameConstant (mapping : Nat → Address) : Const Nat → Const Address + | .axiom n type s => .axiom n (type.rename mapping) s + | .defn n k type body s => .defn n k (type.rename mapping) (body.rename mapping) s + | .quot k n type => .quot k n (type.rename mapping) + | .induct n p i type ctors s => .induct n p i (type.rename mapping) + (ctors.map fun c => ⟨c.uvars, c.nparams, c.nfields, c.type.rename mapping, c.safety⟩) s + | .recursor n p i m b type rules k s => .recursor n p i m b (type.rename mapping) + (rules.map fun r => ⟨r.nfields, r.rhs.rename mapping⟩) k s + +/-- A control input with original declarations under the altered source's +addresses. It intentionally bypasses authentication only to obtain an +independently valid certificate to attack the real serialized gate. -/ +def repaired? (c : Case) (original : ProofInput Nat) : Option PreparedInput := do + let prepared ← prepare? 6400 c.profile c.target c.blobs c.literals + let mapping := fun b => ((c.blocks.find? fun (key, _) => key = b).map Prod.snd).getD c.target + let originals := c.blocks.map fun (b, a) => (a, (if b = 900 then + some (Const.defn original.universes .theorem original.proposition original.proof .safe) + else original.store.lookup (.member b 0)).map (fun source => + (⟨[renameConstant mapping source]⟩ : Block Address))) + let old := prepared.input.store + let store : Store Address := { + dom := old.dom, nodup := old.nodup, + blocks := fun a => (old.blocks a).map fun block => ((lookup originals a).join).getD block, + mem_dom := by intro a; simp [old.mem_dom] } + let target ← store.lookup (.member c.target 0) + return { prepared with input := { prepared.input with store, proposition := target.type } } + +end Tests.Certified.Serialize + +namespace Tests.Certified.Serialize + +open Ix.Theory Ix.Theory.Certified Ix.Certified Ix.Certified.Fixtures + +/-- Rehash an entire fixture after changing one raw blob or constant. Every +reference and projection wrapper is updated in dependency order, so tests of +canonical encoding and field ownership do not merely fail at an old hash. -/ +def rehash? (c : Case) (alterLiteral : ByteArray → ByteArray := id) + (alterConstant : Address → Ixon.Constant → Ixon.Constant := fun _ source => source) : Option Case := do + let mut changed : List (Address × Address) := [] + let mut literals := [] + for (a, bytes) in c.literals do + let bytes := alterLiteral bytes + let a' := Address.blake3 bytes + changed := changed ++ [(a, a')] + literals := literals ++ [(a', bytes)] + let mut blobs := [] + for (a, bytes) in c.blobs do + let source ← decodeObject? a bytes + let rewrite := fun a => (lookup changed a).getD a + let info := match source.val.info with + | .iPrj p => .iPrj { p with block := rewrite p.block } + | .rPrj p => .rPrj { p with block := rewrite p.block } + | .dPrj p => .dPrj { p with block := rewrite p.block } + | .cPrj p => .cPrj { p with block := rewrite p.block } + | info => info + let source := alterConstant a { source.val with info, refs := source.val.refs.map rewrite } + let encoded := encode source + changed := changed ++ [(a, encoded.1)] + blobs := blobs ++ [encoded] + let rewrite := fun a => (lookup changed a).getD a + return { c with + profile := { + falseType := rewrite c.profile.falseType, falseElim := rewrite c.profile.falseElim, + natType := c.profile.natType.map rewrite }, + target := rewrite c.target, blobs, literals, + references := c.references.map fun (r, a) => (r, rewrite a), + blocks := c.blocks.map fun (b, a) => (b, rewrite a) } + +def mapLiterals (index : UInt64) : Ixon.Expr → Ixon.Expr + | .nat _ => .nat index + | .app f a => .app (mapLiterals index f) (mapLiterals index a) + | .lam u A b => .lam u (mapLiterals index A) (mapLiterals index b) + | .all u c A B => .all u c (mapLiterals index A) (mapLiterals index B) + | .prj owner field value => .prj owner field (mapLiterals index value) + | other => other + +end Tests.Certified.Serialize diff --git a/Tests/Certified/Source.lean b/Tests/Certified/Source.lean new file mode 100644 index 000000000..270227d4b --- /dev/null +++ b/Tests/Certified/Source.lean @@ -0,0 +1,272 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Certified.FeatureCases +import Ix.Kernel.Certified +import Ix.Certified.Suggest + +/-! Actual lazy Ixon environments, empty/cached TcM runs, full declaration +subjects and hostile cache/state/selection inputs. -/ + +namespace Tests.Certified.Source + +open Ix.Theory Ix.Theory.Certified Ix.Certified Ix.Kernel Serialize +open Lean (toJson) + +set_option maxRecDepth 32768 +set_option maxHeartbeats 32000000 + +def cases : List Case := + (Corpus.positive.map fun c => { + name := c.name, profile := Fixtures.profile, + target := c.target, blobs := c.blobs, literals := [], references := [], + blocks := c.blobs.map fun (a, _) => (0, a) }) ++ + Features.positive.filterMap fun s => make? s.name s.signature s.input + +#guard cases.length = 42 + +def selection (c : Case) : InputSelection := ⟨c.blobs.map Prod.fst, c.literals.map Prod.fst⟩ + +/-- All constants are real offset windows with no materialized cache. -/ +def source (c : Case) : Ixon.Env := Id.run do + let mut env : Ixon.Env := { main := some c.target } + for (a, bytes) in c.blobs do + let backing := (ByteArray.mk #[17, 19]) ++ bytes ++ (ByteArray.mk #[23]) + env := { env with consts := env.consts.insert a (Ixon.LazyConstant.ofSlice backing 2 bytes.size) } + for (a, bytes) in c.literals do env := { env with blobs := env.blobs.insert a bytes } + return env + +def subjects (c : Case) : List Address := + c.blocks.map Prod.snd + +def accepted (c : Case) : Bool := + let env := source c + (suggestSource? 6400 env c.profile c.target (selection c)).any fun w => + acceptsCertifiedSource.{0} 6400 env c.profile c.target (selection c) w + +def storeAccepted (c : Case) : Bool := + let env := source c + (suggestStore? 6400 env c.profile (subjects c) (selection c)).any fun w => + acceptsCertifiedStoreSource.{0} 6400 env c.profile (subjects c) (selection c) w + +#guard cases.all accepted +#guard cases.all storeAccepted + +/-- A forged legacy equivalence, type cache, block verdict and inference-only +flag are carried through the actual TcM state. Certified checking is full and +independent of each of these fields. -/ +def poisonedChecker (c : Case) : TcState .anon := + let state := TcState.ofEnvAnon {} + let lhs : KExpr .anon := KExpr.mkNatLit 0 + let rhs : KExpr .anon := KExpr.mkConst ⟨c.profile.falseType, ()⟩ #[] + { state with + inferOnly := true, eagerReduce := true, recFuel := 0, noAccel := false, + equivManager := state.equivManager.addEquiv ⟨lhs.addr, state.ctxId, 0, 0⟩ ⟨rhs.addr, state.ctxId, 0, 0⟩, + env := { state.env with + inferCache := state.env.inferCache.insert (lhs.addr, state.ctxId) rhs, + inferOnlyCache := state.env.inferOnlyCache.insert (lhs.addr, state.ctxId) rhs, + whnfCache := state.env.whnfCache.insert (lhs.addr, state.ctxId) rhs, + defEqCache := state.env.defEqCache.insert (lhs.addr, rhs.addr, state.ctxId) true, + blockCheckResults := state.env.blockCheckResults.insert ⟨c.target, ()⟩ (.ok ()) } } + +def warmAndRollback (c : Case) : Bool := Id.run do + let env := source c + let count := c.blobs.length + c.literals.length + let some w := suggestSource? 6400 env c.profile c.target (selection c) | return false + let .ok _ cold := (certifiedStep.{0} 6400 env c.profile c.target (selection c) w).run initialCertifiedState | return false + if cold.inputCache.misses != count || cold.inputCache.hits != 0 then return false + let start := { cold with checker := poisonedChecker c } + let .ok _ warm := (certifiedStep.{0} 6400 env c.profile c.target (selection c) w).run start | return false + if warm.inputCache.hits != count || warm.inputCache.misses != count then return false + if !warm.checker.inferOnly || warm.checker.recFuel != 0 then return false + let bad := { w with proofWitness := .sort } + let .error _ failed := (certifiedStep.{0} 6400 env c.profile c.target (selection c) bad).run warm | return false + if failed.inputCache.hits != warm.inputCache.hits || failed.inputCache.misses != warm.inputCache.misses then return false + let .error _ exhausted := (certifiedStep.{0} 0 env c.profile c.target (selection c) w).run failed | return false + if exhausted.inputCache.hits != warm.inputCache.hits || exhausted.inputCache.misses != warm.inputCache.misses then return false + let .ok _ retry := (certifiedStep.{0} 6400 env c.profile c.target (selection c) w).run exhausted | return false + return retry.inputCache.hits == 2 * count && retry.inputCache.misses == count + +#guard cases.all warmAndRollback + +def loadedIxe? (c : Case) : Option (ByteArray × Ixon.Env) := do + let bytes ← (Ixon.serEnv (source c)).toOption + let parts ← (Ixon.deEnvVerifiedLazy bytes).toOption + return (bytes, parts.env) + +def ixeAccepted (c : Case) : Bool := Id.run do + let some (_, env) := loadedIxe? c | return false + let some target := env.consts.get? c.target | return false + if target.cache.isSome || target.buf.size ≤ target.len then return false + let some w := suggestSource? 6400 env c.profile c.target (selection c) | return false + return acceptsCertifiedSource.{0} 6400 env c.profile c.target (selection c) w + +#guard cases.all ixeAccepted + +def extraMalformed : Address := Address.blake3 "C5-unvisited-malformed-constant".toUTF8 + +def lazyOnlySelected (c : Case) : Bool := Id.run do + let raw := source c + let env := { raw with + consts := raw.consts.insert extraMalformed + (Ixon.LazyConstant.ofSlice (ByteArray.mk #[255, 254]) 0 2) } + let some w := suggestSource? 6400 env c.profile c.target (selection c) | return false + return env.consts.size > c.blobs.length && + acceptsCertifiedSource.{0} 6400 env c.profile c.target (selection c) w && + !acceptsCertifiedSource.{0} 6400 env c.profile c.target + ⟨extraMalformed :: (selection c).objects, (selection c).naturals⟩ w + +#guard cases.all lazyOnlySelected + +def materializedCacheIgnored (c : Case) : Bool := Id.run do + let raw := source c + let some entry := raw.consts.get? c.target | return false + let bogus : Ixon.Constant := ⟨.axio ⟨false, 0, .sort 0⟩, #[], #[], #[.zero]⟩ + let env := { raw with consts := raw.consts.insert c.target { entry with cache := some bogus } } + let some w := suggestSource? 6400 env c.profile c.target (selection c) | return false + return acceptsCertifiedSource.{0} 6400 env c.profile c.target (selection c) w + +#guard cases.all materializedCacheIgnored + +/-- Warm byte caches must not hide changed source bytes, windows, pins, +missing dependencies, or a modified selection. -/ +def hostileSource (c : Case) : Bool := Id.run do + let env := source c + let some w := suggestSource? 6400 env c.profile c.target (selection c) | return false + let .ok _ warm := (certifiedStep.{0} 6400 env c.profile c.target (selection c) w).run initialCertifiedState | return false + let some entry := env.consts.get? c.target | return false + let changed := { env with + consts := env.consts.insert c.target + (Ixon.LazyConstant.ofSlice (entry.rawBytes.push 0) 0 (entry.rawBytes.size + 1)) } + let .error _ _ := (certifiedStep.{0} 6400 changed c.profile c.target (selection c) w).run warm | return false + let outside := { env with consts := env.consts.insert c.target { entry with off := entry.buf.size + 1 } } + let .error _ _ := (certifiedStep.{0} 6400 outside c.profile c.target (selection c) w).run warm | return false + let truncated := { env with consts := env.consts.insert c.target { entry with len := entry.buf.size + 1 } } + let .error _ _ := (certifiedStep.{0} 6400 truncated c.profile c.target (selection c) w).run warm | return false + let missing := { (selection c) with objects := (selection c).objects.filter (· != c.target) } + let .error _ _ := (certifiedStep.{0} 6400 env c.profile c.target missing w).run warm | return false + let duplicate := { (selection c) with objects := c.target :: (selection c).objects } + let .error _ _ := (certifiedStep.{0} 6400 env c.profile c.target duplicate w).run warm | return false + let badProfile := { c.profile with falseType := c.profile.falseElim, falseElim := c.profile.falseType } + let .error _ _ := (certifiedStep.{0} 6400 env badProfile c.target (selection c) w).run warm | return false + let reordered := { (selection c) with objects := (selection c).objects.reverse, naturals := (selection c).naturals.reverse } + let .ok _ _ := (certifiedStep.{0} 6400 env c.profile c.target reordered w).run warm | return false + return true + +#guard cases.all hostileSource + +def rejectsMutation (m : Features.Mutation) : Bool := Id.run do + let some c := make? m.name m.signature m.altered | return false + let some p := repaired? c m.original | return false + let some w := Ix.Theory.Certificate.proofWitness? 6400 p.signature p.input | return false + return acceptsCertified.{0,0} 6400 p.signature p.input w && + !acceptsCertifiedSource.{0} 6400 (source c) c.profile c.target (selection c) w + +#guard Features.mutatedSources.all rejectsMutation + + +def rejectsStoreMutation (m : Features.Mutation) : Bool := Id.run do + let some c := make? m.name m.signature m.altered | return false + let some p := repaired? c m.original | return false + let some w := Ix.Theory.Certificate.proofWitness? 6400 p.signature p.input | return false + return acceptsStoreCertified.{0,0} 6400 p.signature p.input.store [.member c.target 0] w.declarations && + !acceptsCertifiedStoreSource.{0} 6400 (source c) c.profile [c.target] (selection c) w.declarations + +#guard Features.mutatedSources.all rejectsStoreMutation + +def rejectsIngressMutation (m : Features.IngressMutation) : Bool := + m.altered.any fun c => + match readSnapshot? 6400 (source c) (selection c) {} with + | none => true + | some (snapshot, _) => (snapshot.prepare? 6400 c.profile c.target).isNone + +#guard Features.ingressMutations.all rejectsIngressMutation + +def naturalState : Bool := Id.run do + let some c := Features.naturalCase | return false + let env := source c + let some w := suggestSource? 6400 env c.profile c.target (selection c) | return false + let .ok _ warm := (certifiedStep.{0} 6400 env c.profile c.target (selection c) w).run initialCertifiedState | return false + for alter in [Features.missingPin, Features.wrongPin] do + let other := alter c + let .error _ _ := (certifiedStep.{0} 6400 env other.profile c.target (selection c) w).run warm | return false + let some (literal, bytes) := c.literals.head? | return false + let changed := { env with blobs := env.blobs.insert literal (bytes.push 1) } + let .error _ _ := (certifiedStep.{0} 6400 changed c.profile c.target (selection c) w).run warm | return false + let missing := { env with blobs := env.blobs.erase literal } + let .error _ _ := (certifiedStep.{0} 6400 missing c.profile c.target (selection c) w).run warm | return false + let some recursor := reference c.references (.member 201 0) | return false + let thin := { (selection c) with objects := (selection c).objects.filter (· != recursor) } + let assumed := { env with assumptions := env.assumptions.insert recursor } + let .error _ _ := (certifiedStep.{0} 6400 assumed c.profile c.target thin w).run warm | return false + let reversed := { w with declarations := w.declarations.reverse } + let .error _ _ := (certifiedStep.{0} 6400 env c.profile c.target (selection c) reversed).run warm | return false + return true + +#guard naturalState + +def storeRollback (c : Case) : Bool := Id.run do + let env := source c + let some w := suggestStore? 6400 env c.profile (subjects c) (selection c) | return false + let .ok _ cold := (certifiedStoreStep.{0} 6400 env c.profile (subjects c) (selection c) w).run initialCertifiedState | return false + let start := { cold with checker := poisonedChecker c } + let .ok _ warm := (certifiedStoreStep.{0} 6400 env c.profile (subjects c) (selection c) w).run start | return false + let .error _ failed := (certifiedStoreStep.{0} 6400 env c.profile (subjects c) (selection c) []).run warm | return false + if failed.inputCache.hits != warm.inputCache.hits || failed.inputCache.misses != warm.inputCache.misses then return false + let .ok _ retry := (certifiedStoreStep.{0} 6400 env c.profile (subjects c) (selection c) w).run failed | return false + return retry.inputCache.misses == cold.inputCache.misses + +#guard cases.all storeRollback + +def emptyBlockRejected : Bool := Id.run do + let empty := Fixtures.encode ⟨.muts #[], #[], #[], #[]⟩ + let c : Case := { + name := "empty-source-block", profile := Fixtures.profile, target := empty.1, + blobs := Fixtures.prelude ++ [empty], literals := [], references := [], blocks := [] } + return !acceptsCertifiedStoreSource.{0} 6400 (source c) c.profile [empty.1] (selection c) [] + +#guard emptyBlockRejected + +def writeCase (directory : System.FilePath) (c : Case) : IO Unit := do + let some (bytes, _) := loadedIxe? c | throw (IO.userError s!"cannot serialize source case: {c.name}") + let directory := directory / c.name + IO.FS.createDirAll directory + IO.FS.writeBinFile (directory / "source.ixe") bytes + let request := Lean.Json.mkObj [ + ("target", toJson (hexOfBytes c.target.hash)), + ("subjects", toJson ((subjects c).map fun a => hexOfBytes a.hash)), + ("falseType", toJson (hexOfBytes c.profile.falseType.hash)), + ("falseElim", toJson (hexOfBytes c.profile.falseElim.hash)), + ("natType", toJson (c.profile.natType.map fun a => hexOfBytes a.hash)), + ("objects", toJson ((selection c).objects.map fun a => hexOfBytes a.hash)), + ("naturals", toJson ((selection c).naturals.map fun a => hexOfBytes a.hash))] + IO.FS.writeFile (directory / "request.json") (request.pretty ++ "\n") + +def run (directory : Option System.FilePath) : IO Unit := do + for c in cases do + for (name, test) in [("proof", accepted), ("declarations", storeAccepted), + ("cache-and-rollback", warmAndRollback), ("ixe", ixeAccepted), + ("lazy-selection", lazyOnlySelected), ("materialized-cache", materializedCacheIgnored), + ("hostile-source", hostileSource), ("store-rollback", storeRollback)] do + unless test c do throw (IO.userError s!"{name} failed: {c.name}") + if let some directory := directory then writeCase directory c + for m in Features.mutatedSources do + unless rejectsMutation m && rejectsStoreMutation m do + throw (IO.userError s!"source mutation escaped a TcM gate: {m.name}") + for m in Features.ingressMutations do + unless rejectsIngressMutation m do throw (IO.userError s!"ingress mutation escaped: {m.name}") + unless naturalState && emptyBlockRejected do throw (IO.userError "profile/dependency or empty-block rejection failed") + IO.println <| (Lean.Json.mkObj [ + ("proofAccepted", toJson cases.length), ("storesAccepted", toJson cases.length), + ("ixeAccepted", toJson cases.length), ("warmProofScenarios", toJson cases.length), + ("warmStoreScenarios", toJson cases.length), ("lazySelectionScenarios", toJson cases.length), + ("materializedCacheScenarios", toJson cases.length), ("hostileSourceScenarios", toJson cases.length), + ("proofSourceMutationsRejected", toJson Features.mutatedSources.length), + ("storeSourceMutationsRejected", toJson Features.mutatedSources.length), + ("ingressMutationsRejected", toJson Features.ingressMutations.length), + ("naturalStateScenario", toJson true), ("emptyBlockRejected", toJson true), + ("unexpectedErrors", toJson (0 : Nat))]).compress + +end Tests.Certified.Source diff --git a/Tests/Certified/SourceMain.lean b/Tests/Certified/SourceMain.lean new file mode 100644 index 000000000..78bb696fc --- /dev/null +++ b/Tests/Certified/SourceMain.lean @@ -0,0 +1,13 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Certified.Source + +def main (args : List String) : IO UInt32 := do + if args.length > 1 then + IO.eprintln "usage: certified-source-tests [OUTPUT_DIRECTORY]" + return 2 + Tests.Certified.Source.run (args.head?.map System.FilePath.mk) + return 0 diff --git a/Tests/Certified/foundation.txt b/Tests/Certified/foundation.txt new file mode 100644 index 000000000..5bd8291bd --- /dev/null +++ b/Tests/Certified/foundation.txt @@ -0,0 +1,4243 @@ +ROOT Ix.Certified.decodeObject? +(address : Address) → (bytes : ByteArray) → Option (DecodedObject address bytes) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.decodeNatural? +(address : Address) → + (bytes : ByteArray) → + Option + { value // + address.hash.size = 32 ∧ + Address.blake3 bytes = address ∧ Nat.fromBytesLE bytes.data = value ∧ { data := value.toBytesLE } = bytes } +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.readSignature? +Profile → Objects → Option (Theory.Certified.PrimitiveSignature Address) +AXIOMS [propext, Quot.sound] +ROOT Ix.Certified.prepare? +Nat → Profile → Address → ConstantBlobs → optParam ConstantBlobs [] → Option PreparedInput +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.acceptsSerialized_prepared +∀ {fuel : Nat} {profile : Profile} {target : Address} {blobs literalBlobs : ConstantBlobs} + {witness : Theory.Certified.ProofWitness Address}, + acceptsSerialized fuel profile target blobs witness literalBlobs = true → + ∃ prepared, + prepare? fuel profile target blobs literalBlobs = some prepared ∧ + Theory.Certified.acceptsCertified fuel prepared.signature prepared.input witness = true +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.accepted_serialized_has_model +∀ {fuel : Nat} {profile : Profile} {target : Address} {blobs literalBlobs : ConstantBlobs} + {witness : Theory.Certified.ProofWitness Address}, + acceptsSerialized fuel profile target blobs witness literalBlobs = true → + ∀ (V : Type v) [inst : Theory.Model.SetTheory V] (levels : List Nat) (env : Nat → V), + ∃ prepared, + prepare? fuel profile target blobs literalBlobs = some prepared ∧ + ∃ result, + Theory.Certified.checkProofCertified fuel prepared.signature prepared.input witness = some result ∧ + ∃ constants, + prepared.signature.Compatible result.environment.entries constants ∧ + Theory.Model.WellDenoted constants levels env result.proof.val ∧ + Theory.Model.WellDenoted constants levels env result.proposition.val ∧ + Theory.Model.SetTheory.Mem (Theory.Model.interp constants levels env result.proof.val) + (Theory.Model.interp constants levels env result.proposition.val) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.no_serialized_proof_of_False +∀ {fuel : Nat} {profile : Profile} {target : Address} {blobs literalBlobs : ConstantBlobs} + {witness : Theory.Certified.ProofWitness Address} (V : Type v) [Theory.Model.SetTheory V], + isFalseStatement fuel profile target blobs literalBlobs = true → + acceptsSerialized fuel profile target blobs witness literalBlobs = true → False +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.constantBytes? +Ixon.Env → Address → Option ByteArray +AXIOMS [propext, Quot.sound] +ROOT Ix.Certified.decodeObject_complete +∀ {address : Address} {bytes : ByteArray} (value : DecodedObject address bytes), + decodeObject? address bytes = some value +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.decodeNatural_complete +∀ {address : Address} {bytes : ByteArray} (value : DecodedNatural address bytes), + decodeNatural? address bytes = some value +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.SourceSnapshot.prepare_eq +∀ {source : Ixon.Env} (fuel : Nat) (profile : Profile) (target : Address) (snapshot : SourceSnapshot source), + prepare? fuel profile target snapshot.blobs snapshot.literalBlobs = + SourceSnapshot.prepare? fuel profile target snapshot +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.SourceSnapshot.prepareStore_eq +∀ {source : Ixon.Env} (fuel : Nat) (profile : Profile) (subjects : List Address) (snapshot : SourceSnapshot source), + prepareStore? fuel profile subjects snapshot.blobs snapshot.literalBlobs = + SourceSnapshot.prepareStore? fuel profile subjects snapshot +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.SourceSnapshot.objects_from_source +∀ {source : Ixon.Env} (snapshot : SourceSnapshot source) {address : Address} {bytes : ByteArray}, + (address, bytes) ∈ snapshot.blobs → constantBytes? source address = some bytes +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.SourceSnapshot.naturals_from_source +∀ {source : Ixon.Env} (snapshot : SourceSnapshot source) {address : Address} {bytes : ByteArray}, + (address, bytes) ∈ snapshot.literalBlobs → source.getBlob? address = some bytes +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.SourceReceipt.serialized +∀ {source : Ixon.Env} {fuel : Nat} {profile : Profile} {target : Address} + {witness : Theory.Certified.ProofWitness Address} (receipt : SourceReceipt source fuel profile target witness), + acceptsSerialized fuel profile target receipt.snapshot.blobs witness receipt.snapshot.literalBlobs = true +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.StoreReceipt.serialized +∀ {source : Ixon.Env} {fuel : Nat} {profile : Profile} {subjects : List Address} + {witness : List (Theory.Certified.DeclarationWitness Address)} + (receipt : StoreReceipt source fuel profile subjects witness), + acceptsSerializedStore fuel profile subjects receipt.snapshot.blobs witness receipt.snapshot.literalBlobs = true +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.accepted_serialized_store_has_model +∀ {fuel : Nat} {profile : Profile} {subjects : List Address} {blobs literalBlobs : ConstantBlobs} + {witness : List (Theory.Certified.DeclarationWitness Address)}, + acceptsSerializedStore fuel profile subjects blobs witness literalBlobs = true → + ∀ (V : Type v) [inst : Theory.Model.SetTheory V], + ∃ prepared, + prepareStore? fuel profile subjects blobs literalBlobs = some prepared ∧ + ∃ result, + Theory.Certified.checkStoreCertified fuel prepared.signature prepared.store prepared.targets witness = + some result ∧ + ∃ constants, + prepared.signature.Compatible result.environment.entries constants ∧ + ∀ (r : Theory.ConstRef Address), + r ∈ prepared.targets → + ∃ entry, + result.environment.entries r = some entry ∧ + Theory.Certified.EntrySource prepared.signature prepared.store r entry ∧ + ∀ (levels : List Nat), + levels.length = entry.universes → + ∀ (env : Nat → V), + Theory.Model.WellDenoted constants levels env entry.type ∧ + Theory.Model.SetTheory.Mem (constants r levels) + (Theory.Model.interp constants levels env entry.type) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.TcM.checkCertified_success +∀ {m : Kernel.Mode} {fuel : Nat} {source : Ixon.Env} {profile : Profile} {target : Address} {selection : InputSelection} + {witness : Theory.Certified.ProofWitness Address} {cache nextCache : InputCache} {before after : Kernel.TcState m} + {receipt : SourceReceipt source fuel profile target witness}, + EStateM.run (Kernel.TcM.checkCertified fuel source profile target selection witness cache) before = + EStateM.Result.ok (receipt, nextCache) after → + checkSource? fuel source profile target selection witness cache = some (receipt, nextCache) ∧ after = before +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.TcM.checkCertified_failure +∀ {m : Kernel.Mode} {fuel : Nat} {source : Ixon.Env} {profile : Profile} {target : Address} {selection : InputSelection} + {witness : Theory.Certified.ProofWitness Address} {cache : InputCache} {before after : Kernel.TcState m} + {error : Kernel.TcError m}, + EStateM.run (Kernel.TcM.checkCertified fuel source profile target selection witness cache) before = + EStateM.Result.error error after → + checkSource? fuel source profile target selection witness cache = none ∧ after = before +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.TcM.checkStoreCertified_success +∀ {m : Kernel.Mode} {fuel : Nat} {source : Ixon.Env} {profile : Profile} {subjects : List Address} + {selection : InputSelection} {witness : List (Theory.Certified.DeclarationWitness Address)} + {cache nextCache : InputCache} {before after : Kernel.TcState m} + {receipt : StoreReceipt source fuel profile subjects witness}, + EStateM.run (Kernel.TcM.checkStoreCertified fuel source profile subjects selection witness cache) before = + EStateM.Result.ok (receipt, nextCache) after → + checkSourceStore? fuel source profile subjects selection witness cache = some (receipt, nextCache) ∧ after = before +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.certifiedStep_success +∀ {fuel : Nat} {source : Ixon.Env} {profile : Profile} {target : Address} {selection : InputSelection} + {witness : Theory.Certified.ProofWitness Address} {before after : Kernel.CertifiedState} + {receipt : SourceReceipt source fuel profile target witness}, + (Kernel.certifiedStep fuel source profile target selection witness).run before = EStateM.Result.ok receipt after → + checkSource? fuel source profile target selection witness before.inputCache = some (receipt, after.inputCache) ∧ + after.checker = before.checker +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.certifiedStep_failure +∀ {fuel : Nat} {source : Ixon.Env} {profile : Profile} {target : Address} {selection : InputSelection} + {witness : Theory.Certified.ProofWitness Address} {before after : Kernel.CertifiedState} + {error : Kernel.TcError Kernel.Mode.anon}, + (Kernel.certifiedStep fuel source profile target selection witness).run before = EStateM.Result.error error after → + after = before +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.certifiedStoreStep_success +∀ {fuel : Nat} {source : Ixon.Env} {profile : Profile} {subjects : List Address} {selection : InputSelection} + {witness : List (Theory.Certified.DeclarationWitness Address)} {before after : Kernel.CertifiedState} + {receipt : StoreReceipt source fuel profile subjects witness}, + (Kernel.certifiedStoreStep fuel source profile subjects selection witness).run before = + EStateM.Result.ok receipt after → + checkSourceStore? fuel source profile subjects selection witness before.inputCache = + some (receipt, after.inputCache) ∧ + after.checker = before.checker +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.certifiedStoreStep_failure +∀ {fuel : Nat} {source : Ixon.Env} {profile : Profile} {subjects : List Address} {selection : InputSelection} + {witness : List (Theory.Certified.DeclarationWitness Address)} {before after : Kernel.CertifiedState} + {error : Kernel.TcError Kernel.Mode.anon}, + (Kernel.certifiedStoreStep fuel source profile subjects selection witness).run before = + EStateM.Result.error error after → + after = before +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.initialCertifiedState +Kernel.CertifiedState +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.accepted_tc_has_model +∀ {fuel : Nat} {source : Ixon.Env} {profile : Profile} {target : Address} {selection : InputSelection} + {witness : Theory.Certified.ProofWitness Address}, + Kernel.acceptsCertifiedSource fuel source profile target selection witness = true → + ∀ (V : Type v) [inst : Theory.Model.SetTheory V] (levels : List Nat) (env : Nat → V), + ∃ receipt, + Kernel.runCertified fuel source profile target selection witness = some receipt ∧ + (∀ (address : Address) (bytes : ByteArray), + (address, bytes) ∈ receipt.snapshot.blobs → constantBytes? source address = some bytes) ∧ + (∀ (address : Address) (bytes : ByteArray), + (address, bytes) ∈ receipt.snapshot.literalBlobs → source.getBlob? address = some bytes) ∧ + ∃ constants, + receipt.prepared.signature.Compatible receipt.proof.environment.entries constants ∧ + Theory.Model.WellDenoted constants levels env receipt.proof.proof.val ∧ + Theory.Model.WellDenoted constants levels env receipt.proof.proposition.val ∧ + Theory.Model.SetTheory.Mem (Theory.Model.interp constants levels env receipt.proof.proof.val) + (Theory.Model.interp constants levels env receipt.proof.proposition.val) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.accepted_tc_store_has_model +∀ {fuel : Nat} {source : Ixon.Env} {profile : Profile} {subjects : List Address} {selection : InputSelection} + {witness : List (Theory.Certified.DeclarationWitness Address)}, + Kernel.acceptsCertifiedStoreSource fuel source profile subjects selection witness = true → + ∀ (V : Type v) [inst : Theory.Model.SetTheory V], + ∃ receipt, + Kernel.runCertifiedStore fuel source profile subjects selection witness = some receipt ∧ + (∀ (address : Address) (bytes : ByteArray), + (address, bytes) ∈ receipt.snapshot.blobs → constantBytes? source address = some bytes) ∧ + (∀ (address : Address) (bytes : ByteArray), + (address, bytes) ∈ receipt.snapshot.literalBlobs → source.getBlob? address = some bytes) ∧ + ∃ constants, + receipt.prepared.signature.Compatible receipt.result.environment.entries constants ∧ + ∀ (r : Theory.ConstRef Address), + r ∈ receipt.prepared.targets → + ∃ entry, + receipt.result.environment.entries r = some entry ∧ + Theory.Certified.EntrySource receipt.prepared.signature receipt.prepared.store r entry ∧ + ∀ (levels : List Nat), + levels.length = entry.universes → + ∀ (env : Nat → V), + Theory.Model.WellDenoted constants levels env entry.type ∧ + Theory.Model.SetTheory.Mem (constants r levels) + (Theory.Model.interp constants levels env entry.type) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.no_tc_proof_of_False +∀ {source : Ixon.Env} {fuel : Nat} {profile : Profile} {target : Address} + {witness : Theory.Certified.ProofWitness Address} (receipt : SourceReceipt source fuel profile target witness) + (V : Type v) [Theory.Model.SetTheory V], + receipt.prepared.input.proposition = receipt.prepared.signature.falseExpr → False +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.Command.run_success +∀ {mode : String} {fuel : Nat} {source : Ixon.Env} {request : Command.Request}, + Command.run mode fuel source request = Except.ok () → + (mode = "proof" ∧ + ∃ witness, + Kernel.acceptsCertifiedSource fuel source request.profile request.target request.selection witness = true) ∨ + mode = "store" ∧ + ∃ witness, + Kernel.acceptsCertifiedStoreSource fuel source request.profile request.subjects request.selection witness = + true +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.resolveReference_iff +∀ {objects : Objects} {address : Address} {ref : Theory.ConstRef Address}, + resolveReference? objects address = some ref ↔ ReferenceMeaning objects address ref +AXIOMS [propext, Quot.sound] +ROOT Ix.Certified.readLevel_value +∀ (level : Ixon.Univ) (levels : List Nat), Theory.VLevel.eval levels (readLevel level) = sourceLevelValue levels level +AXIOMS [propext] +ROOT Ix.Certified.readExpr_sound +∀ {fuel : Nat} {objects : Objects} {naturals : Naturals} {block : Address} {source : Ixon.Constant} + {expression : Ixon.Expr} {reading : Theory.VExpr Address}, + readExpr? fuel objects naturals block source expression = some reading → + ExprReading objects naturals block source expression reading +AXIOMS [propext, Quot.sound] +ROOT Ix.Certified.ExprReading.unique +∀ {objects : Objects} {naturals : Naturals} {block : Address} {source : Ixon.Constant} {expression : Ixon.Expr} + {left right : Theory.VExpr Address}, + ExprReading objects naturals block source expression left → + ExprReading objects naturals block source expression right → left = right +AXIOMS [propext, Quot.sound] +ROOT Ix.Certified.readExpr_fuel_independent +∀ {fuel₁ fuel₂ : Nat} {objects : Objects} {naturals : Naturals} {block : Address} {source : Ixon.Constant} + {expression : Ixon.Expr} {left right : Theory.VExpr Address}, + readExpr? fuel₁ objects naturals block source expression = some left → + readExpr? fuel₂ objects naturals block source expression = some right → left = right +AXIOMS [propext, Quot.sound] +ROOT Ix.Certified.readBlock_sourceHeader +∀ {fuel : Nat} {objects : Objects} {naturals : Naturals} {block : Address} {source : Ixon.Constant} + {result : Theory.Block Address} {ref : Theory.ConstRef Address} {universes : Nat} {type : Theory.VExpr Address}, + readBlock? fuel objects naturals block source = some result → + blockHeader? result ref = some (universes, type) → + ∃ raw, rawHeader? source ref = some (universes, raw) ∧ ExprReading objects naturals block source raw type +AXIOMS [propext, Quot.sound] +ROOT Ix.Certified.readStore_sourceHeader +∀ {fuel : Nat} {objects : Objects} {naturals : Naturals} {store : Theory.Store Address} {ref : Theory.ConstRef Address} + {entry : Theory.Model.ConstantEntry Address}, + readStore? fuel objects naturals = some store → + Theory.Certified.SourceHeader store ref entry → + ∃ source raw, + lookup objects ref.block = some source ∧ + rawHeader? source ref = some (entry.universes, raw) ∧ + ExprReading objects naturals ref.block source raw entry.type.erase +AXIOMS [propext, Quot.sound] +ROOT Ix.Certified.SourceSnapshot.object_bytes +∀ {source : Ixon.Env} (snapshot : SourceSnapshot source) {address : Address} {value : Ixon.Constant}, + lookup snapshot.decodedObjects address = some value → ∃ bytes, ObjectBytes source address bytes value +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.SourceSnapshot.natural_bytes +∀ {source : Ixon.Env} (snapshot : SourceSnapshot source) {address : Address} {value : Nat}, + lookup snapshot.decodedNaturals address = some value → ∃ bytes, NaturalBytes source address bytes value +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.readSignature_sound +∀ {profile : Profile} {objects : Objects} {signature : Theory.Certified.PrimitiveSignature Address}, + readSignature? profile objects = some signature → SignatureReading profile objects signature +AXIOMS [propext, Quot.sound] +ROOT Ix.Certified.StoreReceipt.subject_meaning +∀ {source : Ixon.Env} {fuel : Nat} {profile : Profile} {subjects : List Address} + {witness : List (Theory.Certified.DeclarationWitness Address)} + (receipt : StoreReceipt source fuel profile subjects witness) {ref : Theory.ConstRef Address}, + ref ∈ receipt.prepared.targets → + ∃ entry, + receipt.result.environment.entries ref = some entry ∧ + SourceDeclarationReading source receipt.snapshot.decodedObjects receipt.snapshot.decodedNaturals ref entry ∧ + ∀ (V : Type v) [inst : Theory.Model.SetTheory V] (constants : Theory.Model.Assignment Address V), + receipt.prepared.signature.Compatible receipt.result.environment.entries constants → + ∀ (levels : List Nat), + levels.length = entry.universes → + ∀ (env : Nat → V), + Theory.Model.WellDenoted constants levels env entry.type ∧ + Theory.Model.SetTheory.Mem (constants ref levels) + (Theory.Model.interp constants levels env entry.type) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.SourceReceipt.proposition_meaning +∀ {source : Ixon.Env} {fuel : Nat} {profile : Profile} {target : Address} + {witness : Theory.Certified.ProofWitness Address} (receipt : SourceReceipt source fuel profile target witness), + SignatureReading profile receipt.snapshot.decodedObjects receipt.prepared.signature ∧ + ∃ ref bytes declaration rawType, + ReferenceMeaning receipt.snapshot.decodedObjects target ref ∧ + ObjectBytes source ref.block bytes declaration ∧ + rawHeader? declaration ref = some (receipt.prepared.input.universes, rawType) ∧ + ExprReading receipt.snapshot.decodedObjects receipt.snapshot.decodedNaturals ref.block declaration rawType + receipt.proof.proposition.val.erase ∧ + ∀ (V : Type v) [inst : Theory.Model.SetTheory V] (constants : Theory.Model.Assignment Address V), + receipt.prepared.signature.Compatible receipt.proof.environment.entries constants → + ∀ (levels : List Nat) (env : Nat → V), + Theory.Model.WellDenoted constants levels env receipt.proof.proposition.val ∧ + Theory.Model.SetTheory.Mem + (Theory.Model.interp constants levels env receipt.proof.proposition.val) + (Theory.Model.SetTheory.univ 0) ∧ + Theory.Model.SetTheory.Mem (Theory.Model.interp constants levels env receipt.proof.proof.val) + (Theory.Model.interp constants levels env receipt.proof.proposition.val) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.StoreReceipt.subject_coverage +∀ {source : Ixon.Env} {fuel : Nat} {profile : Profile} {subjects : List Address} + {witness : List (Theory.Certified.DeclarationWitness Address)} + (receipt : StoreReceipt source fuel profile subjects witness) (ref : Theory.ConstRef Address), + ref ∈ receipt.prepared.targets ↔ + ∃ subject, subject ∈ subjects ∧ ∃ refs, SubjectReading receipt.snapshot.decodedObjects subject refs ∧ ref ∈ refs +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.TreeOpening.membership +∀ {fuel : Nat} {root : Address} {bytes : ByteArray} (opening : TreeOpening fuel root bytes) {target : Address}, + target ∈ treeLeaves opening.tree → TreeMembership root target +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.TreeOpening.unique +∀ {fuel : Nat} {root : Address} {bytes : ByteArray} {a b : TreeOpening fuel root bytes}, a.tree = b.tree +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.treeLeaves_join +∀ (left right : AssumptionTree) (address : Address), + address ∈ treeLeaves (left.node right) ↔ address ∈ treeLeaves left ∨ address ∈ treeLeaves right +AXIOMS [propext] +ROOT Ix.Certified.EnvelopeReading.unique +∀ {address : Address} {bytes : ByteArray} {a b : EnvelopeReading address bytes}, a.envelope = b.envelope +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.readEnvelope? +Nat → (address : Address) → (bytes : ByteArray) → Option (EnvelopeReading address bytes) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.readTree? +(fuel : Nat) → (root : Address) → (bytes : ByteArray) → Option (TreeOpening fuel root bytes) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.LogicalChecking.original_subject +∀ {source : Ixon.Env} {snapshot : SourceSnapshot source} {fuel : Nat} {envelope : Envelope} {witness : LogicalWitness} + (checked : LogicalChecking snapshot fuel envelope witness) {ref : Theory.ConstRef Address}, + ref ∈ checked.subjects → + ∃ entry, + checked.batch.receipt.checked.result.entries ref = some entry ∧ + SourceDeclarationReading source snapshot.decodedObjects snapshot.decodedNaturals ref entry +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.LogicalChecking.original_frontier +∀ {source : Ixon.Env} {snapshot : SourceSnapshot source} {fuel : Nat} {envelope : Envelope} {witness : LogicalWitness} + (checked : LogicalChecking snapshot fuel envelope witness) {ref : Theory.ConstRef Address}, + ref ∈ checked.frontierRefs → + ∃ entry, + checked.batch.receipt.frontier.interface.entries ref = some entry ∧ + SourceDeclarationReading source snapshot.decodedObjects snapshot.decodedNaturals ref entry +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.LogicalChecking.logical_policy +∀ {source : Ixon.Env} {snapshot : SourceSnapshot source} {fuel : Nat} {envelope : Envelope} {witness : LogicalWitness} + (checked : LogicalChecking snapshot fuel envelope witness) {ref : Theory.ConstRef Address}, + ref ∈ checked.axiomRefs → + ∃ entry, + Theory.Certified.Standard.EntrySource checked.store ref entry ∨ + Theory.Certified.Quotient.EntrySource checked.store ref entry +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.LogicalChecking.closed_frontier +∀ {source : Ixon.Env} {snapshot : SourceSnapshot source} {fuel : Nat} {envelope : Envelope} {witness : LogicalWitness} + (checked : LogicalChecking snapshot fuel envelope witness), + claimFrontier envelope.claim = none → checked.batch.receipt.frontier.refs = [] +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.LogicalReceipt.subject_meaning +∀ {source : Ixon.Env} {fuel : Nat} {envelope : Envelope} {witness : LogicalWitness} + (receipt : LogicalReceipt source fuel envelope witness) (V : Type v) [inst : Theory.Model.SetTheory V] + (constants : Theory.Model.Assignment Address V), + receipt.checked.signature.Compatible receipt.checked.batch.receipt.frontier.interface.entries constants → + ∃ constants', + receipt.checked.signature.Compatible receipt.checked.batch.receipt.checked.result.entries constants' ∧ + Theory.Model.Assignment.AgreesOn receipt.checked.batch.receipt.frontier.interface.entries constants constants' ∧ + ∀ (ref : Theory.ConstRef Address), + ref ∈ receipt.checked.subjects → + ∃ entry, + SourceDeclarationReading source receipt.snapshot.decodedObjects receipt.snapshot.decodedNaturals ref + entry ∧ + ∀ (levels : List Nat), + levels.length = entry.universes → + ∀ (env : Nat → V), + Theory.Model.WellDenoted constants' levels env entry.type ∧ + Theory.Model.SetTheory.Mem (constants' ref levels) + (Theory.Model.interp constants' levels env entry.type) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.LogicalReceipt.closed_subject_meaning +∀ {source : Ixon.Env} {fuel : Nat} {envelope : Envelope} {witness : LogicalWitness} + (receipt : LogicalReceipt source fuel envelope witness), + claimFrontier envelope.claim = none → + ∀ (V : Type v) [inst : Theory.Model.SetTheory V], + ∃ constants, + receipt.checked.signature.Compatible receipt.checked.batch.receipt.checked.result.entries constants ∧ + ∀ (ref : Theory.ConstRef Address), + ref ∈ receipt.checked.subjects → + ∃ entry, + SourceDeclarationReading source receipt.snapshot.decodedObjects receipt.snapshot.decodedNaturals ref + entry ∧ + ∀ (levels : List Nat), + levels.length = entry.universes → + ∀ (env : Nat → V), + Theory.Model.WellDenoted constants levels env entry.type ∧ + Theory.Model.SetTheory.Mem (constants ref levels) + (Theory.Model.interp constants levels env entry.type) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.LogicalReceipt.no_False +∀ {source : Ixon.Env} {fuel : Nat} {envelope : Envelope} {witness : LogicalWitness} + (receipt : LogicalReceipt source fuel envelope witness), + claimFrontier envelope.claim = none → + ∀ {ref : Theory.ConstRef Address}, + ref ∈ receipt.checked.subjects → + receipt.checked.store.type ref = some receipt.checked.signature.falseExpr → + ∀ (V : Type v) [Theory.Model.SetTheory V], False +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.RevealReceipt.meaning +∀ {source : Ixon.Env} {commitment : Address} {info : RevealConstantInfo} {witness : RevealWitness} + (receipt : RevealReceipt source commitment info witness), RevealMeaning source commitment info +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.ClaimReceipt.meaning +∀ {source : Ixon.Env} {fuel : Nat} {address : Address} {bytes : ByteArray} + (receipt : ClaimReceipt source fuel address bytes), SemanticClaimMeaning source fuel receipt.reading.envelope +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.accepted_claim_meaning +∀ {fuel : Nat} {source : Ixon.Env} {address : Address} {bytes : ByteArray} {witness : ClaimWitness}, + acceptsClaimBytes fuel source address bytes witness = true → + ∃ receipt cache, + checkClaimBytes? fuel source address bytes witness { } = some (receipt, cache) ∧ + Address.blake3 bytes = address ∧ + envelopeBytes receipt.reading.envelope = bytes ∧ SemanticClaimMeaning source fuel receipt.reading.envelope +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.evaluation_not_semantic +∀ {source : Ixon.Env} {fuel : Nat} {envelope : Envelope} {input output : Address} {frontier : Option Address}, + envelope.claim = Claim.eval input output frontier → ¬SemanticClaimMeaning source fuel envelope +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.membership_not_leaf +∀ {fuel : Nat} {root target : Address} {bytes : Option ByteArray}, + readSubjectView? fuel (Claim.contains root target) bytes = none +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.revelation_not_leaf +∀ {fuel : Nat} {commitment : Address} {info : RevealConstantInfo} {bytes : Option ByteArray}, + readSubjectView? fuel (Claim.reveal commitment info) bytes = none +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.TcM.checkClaimCertified_success +∀ {m : Kernel.Mode} {fuel : Nat} {source : Ixon.Env} {address : Address} {bytes : ByteArray} {witness : ClaimWitness} + {cache nextCache : InputCache} {before after : Kernel.TcState m} {receipt : ClaimReceipt source fuel address bytes}, + EStateM.run (Kernel.TcM.checkClaimCertified fuel source address bytes witness cache) before = + EStateM.Result.ok (receipt, nextCache) after → + checkClaimBytes? fuel source address bytes witness cache = some (receipt, nextCache) ∧ after = before +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.certifiedClaimStep_failure +∀ {fuel : Nat} {source : Ixon.Env} {address : Address} {bytes : ByteArray} {witness : ClaimWitness} + {before after : Kernel.CertifiedState} {error : Kernel.TcError Kernel.Mode.anon}, + (Kernel.certifiedClaimStep fuel source address bytes witness).run before = EStateM.Result.error error after → + after = before +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.certifiedClaimStep_success +∀ {fuel : Nat} {source : Ixon.Env} {address : Address} {bytes : ByteArray} {witness : ClaimWitness} + {before after : Kernel.CertifiedState} {receipt : ClaimReceipt source fuel address bytes}, + (Kernel.certifiedClaimStep fuel source address bytes witness).run before = EStateM.Result.ok receipt after → + checkClaimBytes? fuel source address bytes witness before.inputCache = some (receipt, after.inputCache) ∧ + after.checker = before.checker +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.accepted_tc_claim_meaning +∀ {fuel : Nat} {source : Ixon.Env} {address : Address} {bytes : ByteArray} {witness : ClaimWitness}, + Kernel.acceptsCertifiedClaim fuel source address bytes witness = true → + ∃ receipt, + Kernel.runCertifiedClaim fuel source address bytes witness = some receipt ∧ + Address.blake3 bytes = address ∧ + envelopeBytes receipt.reading.envelope = bytes ∧ SemanticClaimMeaning source fuel receipt.reading.envelope +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.ClaimCommand.readRequest +Lean.Json → Except String ClaimCommand.Request +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.ClaimCommand.run_success +∀ {fuel : Nat} {source : Ixon.Env} {bytes : ByteArray} {request : ClaimCommand.Request}, + ClaimCommand.run fuel source bytes request = Except.ok () → + ∃ witness, Kernel.acceptsCertifiedClaim fuel source request.address bytes witness = true +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.ClaimCommand.run_meaning +∀ {fuel : Nat} {source : Ixon.Env} {bytes : ByteArray} {request : ClaimCommand.Request}, + ClaimCommand.run fuel source bytes request = Except.ok () → + ∃ receipt, + Address.blake3 bytes = request.address ∧ + envelopeBytes receipt.reading.envelope = bytes ∧ SemanticClaimMeaning source fuel receipt.reading.envelope +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Theory.Certified.Modeled.check?_sound +∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {entries : Theory.Model.Environment β} {store : Theory.Store β} + {witness : Theory.Certified.Modeled.Witness β} + {result : Theory.Certified.CheckedClaim (Theory.Certified.Modeled.Checked entries store witness)}, + Theory.Certified.Modeled.check? fuel entries store witness = some result → + Theory.Certified.Modeled.Checked entries store witness +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Theory.Certified.Modeled.checkEquation?_sound +∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {entries : Theory.Model.Environment β} + {rule : Theory.Certified.Signature.Rule β} {witness : Theory.Certified.Modeled.EquationWitness β} + {result : Theory.Certified.CheckedClaim (Theory.Certified.Modeled.CheckedEquation entries rule)}, + Theory.Certified.Modeled.checkEquation? fuel entries rule witness = some result → + Theory.Certified.Modeled.CheckedEquation entries rule +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Theory.Certified.Modeled.assignment_realizes +∀ {β : Type u} [inst : DecidableEq β] {V : Type v} [inst_1 : Theory.Model.SetTheory V] + {entries : Theory.Model.Environment β} {companions : List (Theory.Certified.Modeled.Companion β)}, + Theory.Certified.Modeled.CheckedCompanions entries companions → + entries.WF → + ∀ (constants : Theory.Model.Assignment β V), + Theory.Model.Realizes constants entries → + Theory.Model.Realizes (Theory.Certified.Modeled.assignment constants companions) + (Theory.Certified.Modeled.environment entries companions) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Theory.Certified.Modeled.assignment_agrees +∀ {β : Type u} [inst : DecidableEq β] {V : Type v} {entries : Theory.Model.Environment β} + {companions : List (Theory.Certified.Modeled.Companion β)}, + Theory.Certified.Modeled.CheckedCompanions entries companions → + ∀ (constants : Theory.Model.Assignment β V), + Theory.Model.Assignment.AgreesOn entries constants (Theory.Certified.Modeled.assignment constants companions) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Theory.Certified.checkModeledExtension? +{β : Type u} → + [DecidableEq β] → + Nat → + {signature : Theory.Certified.PrimitiveSignature β} → + {store : Theory.Store β} → + (state : Theory.Certified.CheckedInterface signature) → + Theory.Certified.Modeled.Witness β → Option (Theory.Certified.CheckedExtension signature store state) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Theory.Certificate.Modeled.witness? +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Theory.Model.Environment β → + Theory.Store β → + β → + List (Theory.ConstRef β) → + List (Theory.ConstRef β) → + (optParam (Theory.ConstRef β → Nat → Option (Theory.Certificate.Modeled.ProofHint β)) fun x x_1 => + none) → + Option (Theory.Certified.Modeled.Witness β) +AXIOMS [propext, Quot.sound] +ROOT Ix.Theory.Certificate.sourceGroup? +{β : Type u} → + [DecidableEq β] → + Theory.Store β → + Theory.ConstRef β → + optParam (Option (Theory.ConstRef β)) none → + optParam (List (Theory.Certificate.Modeled.Candidate β)) [] → Option (Theory.Certificate.SourceGroup β) +AXIOMS [propext, Quot.sound] +ROOT Ix.Theory.Certificate.proofWitness? +{β : Type u} → + [DecidableEq β] → + Nat → + Theory.Certified.PrimitiveSignature β → + Theory.Certified.ProofInput β → + optParam (List (Theory.Certificate.Modeled.Candidate β)) [] → Option (Theory.Certified.ProofWitness β) +AXIOMS [propext, Quot.sound] +ROOT Ix.Certified.modelCandidate? +Objects → Theory.Store Address → ModelHint → Option (Theory.Certificate.Modeled.Candidate Address) +AXIOMS [propext, Quot.sound] +ROOT Ix.Certified.modelCandidates? +Objects → Theory.Store Address → List ModelHint → Option (List (Theory.Certificate.Modeled.Candidate Address)) +AXIOMS [propext, Quot.sound] +ROOT Ix.Certified.ModelHint.readOptional +Lean.Json → Except String (List ModelHint) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.ModelHint.read +Lean.Json → Except String ModelHint +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.suggestSource? +Nat → + Ixon.Env → + Profile → Address → InputSelection → optParam (List ModelHint) [] → Option (Theory.Certified.ProofWitness Address) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.suggestStore? +Nat → + Ixon.Env → + Profile → + List Address → + InputSelection → optParam (List ModelHint) [] → Option (List (Theory.Certified.DeclarationWitness Address)) +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.Command.readRequest +Lean.Json → Except String Command.Request +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Certified.suggestLogical? +Nat → Ixon.Env → Envelope → LogicalHint → Option LogicalWitness +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.acceptsCertifiedSource +Nat → Ixon.Env → Profile → Address → InputSelection → Theory.Certified.ProofWitness Address → Bool +AXIOMS [propext, Classical.choice, Quot.sound] +ROOT Ix.Kernel.acceptsCertifiedStoreSource +Nat → Ixon.Env → Profile → List Address → InputSelection → List (Theory.Certified.DeclarationWitness Address) → Bool +AXIOMS [propext, Classical.choice, Quot.sound] +PREMISE Ix.Certified.InputCache.mk +List CachedObject → List CachedNatural → Nat → Nat → InputCache +PREMISE Ix.Certified.CachedObject.mk +(address : Address) → (bytes : ByteArray) → DecodedObject address bytes → CachedObject +PREMISE Ix.Certified.CachedNatural.mk +(address : Address) → (bytes : ByteArray) → DecodedNatural address bytes → CachedNatural +PREMISE Ix.Certified.DecodedObject +Address → ByteArray → Type +DEFINITION +fun address bytes => + { value // + address.hash.size = 32 ∧ + Address.blake3 bytes = address ∧ + Ixon.runGetExact Ixon.getConstant bytes = Except.ok value ∧ Ixon.serConstant value = bytes } +PREMISE Ix.Certified.DecodedNatural +Address → ByteArray → Type +DEFINITION +fun address bytes => + { value // + address.hash.size = 32 ∧ + Address.blake3 bytes = address ∧ Nat.fromBytesLE bytes.data = value ∧ { data := value.toBytesLE } = bytes } +PREMISE Ix.Certified.SourceObject.mk +{source : Ixon.Env} → + (toCachedObject : CachedObject) → + constantBytes? source toCachedObject.address = some toCachedObject.bytes → SourceObject source +PREMISE Ix.Certified.SourceNatural.mk +{source : Ixon.Env} → + (toCachedNatural : CachedNatural) → + source.getBlob? toCachedNatural.address = some toCachedNatural.bytes → SourceNatural source +PREMISE Ix.Certified.SourceReceipt.mk +{source : Ixon.Env} → + {fuel : Nat} → + {profile : Profile} → + {target : Address} → + {witness : Theory.Certified.ProofWitness Address} → + (snapshot : SourceSnapshot source) → + (prepared : PreparedInput) → + SourceSnapshot.prepare? fuel profile target snapshot = some prepared → + (proof : Theory.Certified.CheckedProof prepared.signature prepared.input) → + Theory.Certified.checkProofCertified fuel prepared.signature prepared.input witness = some proof → + SourceReceipt source fuel profile target witness +PREMISE Ix.Certified.StoreReceipt.mk +{source : Ixon.Env} → + {fuel : Nat} → + {profile : Profile} → + {subjects : List Address} → + {witness : List (Theory.Certified.DeclarationWitness Address)} → + (snapshot : SourceSnapshot source) → + (prepared : PreparedStore) → + SourceSnapshot.prepareStore? fuel profile subjects snapshot = some prepared → + (result : Theory.Certified.CheckedStore prepared.signature prepared.store prepared.targets) → + Theory.Certified.checkStoreCertified fuel prepared.signature prepared.store prepared.targets witness = + some result → + StoreReceipt source fuel profile subjects witness +PREMISE Ix.Certified.InputSelection.mk +List Address → List Address → InputSelection +PREMISE Ix.Certified.subjectReferences? +Objects → Address → Option (List (Theory.ConstRef Address)) +DEFINITION +fun objects subject => do + let source ← lookup objects subject + match source.info with + | Ixon.ConstantInfo.muts members => + if members.isEmpty = true then none + else + pure + (List.flatMap + (fun x => + match x with + | (member, index) => + Theory.ConstRef.member subject index :: + match member with + | Ixon.MutConst.indc family => + List.map (fun x => Theory.ConstRef.ctor subject index x) (List.range family.ctors.size) + | x => []) + members.toList.zipIdx) + | x => do + let __do_lift ← resolveReference? objects subject + pure [__do_lift] +PREMISE Ix.Kernel.CertifiedState.mk +Kernel.TcState Kernel.Mode.anon → InputCache → Kernel.CertifiedState +PREMISE Ix.Certified.MemberMeaning +Objects → Address → UInt64 → Ixon.MutConst → Prop +DEFINITION +fun objects block index member => + ∃ source members, + lookup objects block = some source ∧ + source.info = Ixon.ConstantInfo.muts members ∧ members[index.toNat]? = some member +PREMISE Ix.Certified.ReferenceTarget +Objects → Address → Ixon.Constant → Theory.ConstRef Address → Prop +DEFINITION +fun objects address source ref => + match source.info with + | Ixon.ConstantInfo.defn a => ref = Theory.ConstRef.member address 0 + | Ixon.ConstantInfo.recr a => ref = Theory.ConstRef.member address 0 + | Ixon.ConstantInfo.axio a => ref = Theory.ConstRef.member address 0 + | Ixon.ConstantInfo.quot a => ref = Theory.ConstRef.member address 0 + | Ixon.ConstantInfo.iPrj projection => + (∃ family, MemberMeaning objects projection.block projection.idx (Ixon.MutConst.indc family)) ∧ + ref = Theory.ConstRef.member projection.block projection.idx.toNat + | Ixon.ConstantInfo.rPrj projection => + (∃ recursor, MemberMeaning objects projection.block projection.idx (Ixon.MutConst.recr recursor)) ∧ + ref = Theory.ConstRef.member projection.block projection.idx.toNat + | Ixon.ConstantInfo.dPrj projection => + (∃ definition, MemberMeaning objects projection.block projection.idx (Ixon.MutConst.defn definition)) ∧ + ref = Theory.ConstRef.member projection.block projection.idx.toNat + | Ixon.ConstantInfo.cPrj projection => + (∃ family ctor, + MemberMeaning objects projection.block projection.idx (Ixon.MutConst.indc family) ∧ + family.ctors[projection.cidx.toNat]? = some ctor) ∧ + ref = Theory.ConstRef.ctor projection.block projection.idx.toNat projection.cidx.toNat + | Ixon.ConstantInfo.muts a => False +PREMISE Ix.Certified.ReferenceMeaning +Objects → Address → Theory.ConstRef Address → Prop +DEFINITION +fun objects address ref => ∃ source, lookup objects address = some source ∧ ReferenceTarget objects address source ref +PREMISE Ix.Certified.LevelsReading.nil +∀ {source : Ixon.Constant}, LevelsReading source [] [] +PREMISE Ix.Certified.LevelsReading.cons +∀ {source : Ixon.Constant} {index : UInt64} {indices : List UInt64} {level : Ixon.Univ} {levels : List Theory.VLevel}, + source.univs[index.toNat]? = some level → + LevelsReading source indices levels → LevelsReading source (index :: indices) (readLevel level :: levels) +PREMISE Ix.Certified.ExprReading.var +∀ {objects : Objects} {naturals : Naturals} {block : Address} {source : Ixon.Constant} (index : UInt64), + ExprReading objects naturals block source (Ixon.Expr.var index) (Theory.VExpr.bvar index.toNat) +PREMISE Ix.Certified.ExprReading.sort +∀ {objects : Objects} {naturals : Naturals} {block : Address} {source : Ixon.Constant} {index : UInt64} + {level : Ixon.Univ}, + source.univs[index.toNat]? = some level → + ExprReading objects naturals block source (Ixon.Expr.sort index) (Theory.VExpr.sort (readLevel level)) +PREMISE Ix.Certified.ExprReading.ref +∀ {objects : Objects} {naturals : Naturals} {block : Address} {source : Ixon.Constant} {index : UInt64} + {indices : Array UInt64} {address : Address} {ref : Theory.ConstRef Address} {levels : List Theory.VLevel}, + source.refs[index.toNat]? = some address → + ReferenceMeaning objects address ref → + LevelsReading source indices.toList levels → + ExprReading objects naturals block source (Ixon.Expr.ref index indices) (Theory.VExpr.const ref levels) +PREMISE Ix.Certified.ExprReading.recur +∀ {objects : Objects} {naturals : Naturals} {block : Address} {source : Ixon.Constant} {index : UInt64} + {indices : Array UInt64} {levels : List Theory.VLevel}, + LevelsReading source indices.toList levels → + ExprReading objects naturals block source (Ixon.Expr.recur index indices) + (Theory.VExpr.const (Theory.ConstRef.member block index.toNat) levels) +PREMISE Ix.Certified.ExprReading.app +∀ {objects : Objects} {naturals : Naturals} {block : Address} {source : Ixon.Constant} {f a : Ixon.Expr} + {fr ar : Theory.VExpr Address}, + ExprReading objects naturals block source f fr → + ExprReading objects naturals block source a ar → ExprReading objects naturals block source (f.app a) (fr.app ar) +PREMISE Ix.Certified.ExprReading.lam +∀ {objects : Objects} {naturals : Naturals} {block : Address} {source : Ixon.Constant} {A b : Ixon.Expr} + {Ar br : Theory.VExpr Address}, + ExprReading objects naturals block source A Ar → + ExprReading objects naturals block source b br → + ExprReading objects naturals block source (Ixon.Expr.lam Ixon.Uses.many A b) (Ar.lam br) +PREMISE Ix.Certified.ExprReading.all +∀ {objects : Objects} {naturals : Naturals} {block : Address} {source : Ixon.Constant} {A B : Ixon.Expr} + {Ar Br : Theory.VExpr Address}, + ExprReading objects naturals block source A Ar → + ExprReading objects naturals block source B Br → + ExprReading objects naturals block source (Ixon.Expr.all Ixon.Uses.many Ixon.Owned.shared A B) (Ar.forallE Br) +PREMISE Ix.Certified.ExprReading.prj +∀ {objects : Objects} {naturals : Naturals} {block : Address} {source : Ixon.Constant} {owner field : UInt64} + {value : Ixon.Expr} {address : Address} {ownerSource : Ixon.Constant} {projection : Ixon.InductiveProj} + {ref : Theory.ConstRef Address} {reading : Theory.VExpr Address}, + source.refs[owner.toNat]? = some address → + lookup objects address = some ownerSource → + ownerSource.info = Ixon.ConstantInfo.iPrj projection → + ReferenceMeaning objects address ref → + ExprReading objects naturals block source value reading → + ExprReading objects naturals block source (Ixon.Expr.prj owner field value) + (Theory.VExpr.proj ref field.toNat reading) +PREMISE Ix.Certified.ExprReading.nat +∀ {objects : Objects} {naturals : Naturals} {block : Address} {source : Ixon.Constant} {index : UInt64} + {address : Address} {value : Nat}, + source.refs[index.toNat]? = some address → + lookup naturals address = some value → + ExprReading objects naturals block source (Ixon.Expr.nat index) (Theory.VExpr.natLit value) +PREMISE Ix.Certified.ExprReading.share +∀ {objects : Objects} {naturals : Naturals} {block : Address} {source : Ixon.Constant} {index : UInt64} + {shared : Ixon.Expr} {reading : Theory.VExpr Address}, + source.sharing[index.toNat]? = some shared → + ExprReading objects naturals block source shared reading → + ExprReading objects naturals block source (Ixon.Expr.share index) reading +PREMISE Ix.Certified.rawHeader? +Ixon.Constant → Theory.ConstRef Address → Option (Nat × Ixon.Expr) +DEFINITION +fun source x => + match x with + | Theory.ConstRef.member b index => + match source.info with + | Ixon.ConstantInfo.muts members => Option.map memberRawHeader members[index]? + | Ixon.ConstantInfo.defn value => if index = 0 then some (value.lvls.toNat, value.typ) else none + | Ixon.ConstantInfo.recr value => if index = 0 then some (value.lvls.toNat, value.typ) else none + | Ixon.ConstantInfo.axio value => if index = 0 then some (value.lvls.toNat, value.typ) else none + | Ixon.ConstantInfo.quot value => if index = 0 then some (value.lvls.toNat, value.typ) else none + | x => none + | Theory.ConstRef.ctor b index field => + match source.info with + | Ixon.ConstantInfo.muts members => do + let __x ← members[index]? + match __x with + | Ixon.MutConst.indc family => do + let ctor ← family.ctors[field]? + pure (ctor.lvls.toNat, ctor.typ) + | x => none + | x => none +PREMISE Ix.Certified.ObjectBytes +Ixon.Env → Address → ByteArray → Ixon.Constant → Prop +DEFINITION +fun source address bytes value => + constantBytes? source address = some bytes ∧ + address.hash.size = 32 ∧ + Address.blake3 bytes = address ∧ + Ixon.runGetExact Ixon.getConstant bytes = Except.ok value ∧ Ixon.serConstant value = bytes +PREMISE Ix.Certified.NaturalBytes +Ixon.Env → Address → ByteArray → Nat → Prop +DEFINITION +fun source address bytes value => + source.getBlob? address = some bytes ∧ + address.hash.size = 32 ∧ + Address.blake3 bytes = address ∧ Nat.fromBytesLE bytes.data = value ∧ { data := value.toBytesLE } = bytes +PREMISE Ix.Certified.SignatureReading +Profile → Objects → Theory.Certified.PrimitiveSignature Address → Prop +DEFINITION +fun profile objects signature => + ReferenceMeaning objects profile.falseType signature.falseType ∧ + ReferenceMeaning objects profile.falseElim signature.falseElim ∧ + match profile.natType, signature.natType with + | none, none => True + | some address, some ref => ReferenceMeaning objects address ref + | x, x_1 => False +PREMISE Ix.Certified.SourceDeclarationReading +Ixon.Env → Objects → Naturals → Theory.ConstRef Address → Theory.Model.ConstantEntry Address → Prop +DEFINITION +fun source objects naturals ref entry => + ∃ bytes declaration rawType, + ObjectBytes source ref.block bytes declaration ∧ + lookup objects ref.block = some declaration ∧ + rawHeader? declaration ref = some (entry.universes, rawType) ∧ + ExprReading objects naturals ref.block declaration rawType entry.type.erase +PREMISE Ix.Certified.SubjectReading +Objects → Address → List (Theory.ConstRef Address) → Prop +DEFINITION +fun objects subject refs => + ∃ source, + lookup objects subject = some source ∧ + match source.info with + | Ixon.ConstantInfo.muts members => + members.isEmpty = false ∧ + refs = + List.flatMap + (fun x => + match x with + | (member, index) => + Theory.ConstRef.member subject index :: + match member with + | Ixon.MutConst.indc family => + List.map (fun x => Theory.ConstRef.ctor subject index x) (List.range family.ctors.size) + | x => []) + members.toList.zipIdx + | x => ∃ ref, ReferenceMeaning objects subject ref ∧ refs = [ref] +PREMISE Ix.Certified.Protocol.current +Protocol +DEFINITION +{ checker := 2 } +PREMISE Ix.Certified.envelopeMagic +ByteArray +DEFINITION +"IX-CERTIFIED-CLAIM".toUTF8 ++ { data := #[0] } +PREMISE Ix.Certified.putEnvelope +Envelope → Ixon.PutM Unit +DEFINITION +fun envelope => do + Ixon.putBytes envelopeMagic + Ixon.putTag0 { size := envelope.protocol.format } + Ixon.putTag0 { size := envelope.protocol.codec } + Ixon.putTag0 { size := envelope.protocol.checker } + Ixon.putTag0 { size := envelope.protocol.policy } + Ixon.putTag0 { size := envelope.protocol.aggregation } + Ixon.Serialize.put envelope.profile.falseType + Ixon.Serialize.put envelope.profile.falseElim + Claim.putOptAddr envelope.profile.natType + envelope.claim.put + Claim.putOptAddr envelope.logicalAxioms +PREMISE Ix.Certified.getEnvelope +Ixon.GetM Envelope +DEFINITION +do + let __do_lift ← Ixon.getBytes envelopeMagic.size + have __do_jp : Unit → Ixon.GetM Envelope := fun __r => do + let __do_lift ← Ixon.getTag0 + have format : UInt64 := __do_lift.size + let __do_lift ← Ixon.getTag0 + have codec : UInt64 := __do_lift.size + let __do_lift ← Ixon.getTag0 + have checker : UInt64 := __do_lift.size + let __do_lift ← Ixon.getTag0 + have policy : UInt64 := __do_lift.size + let __do_lift ← Ixon.getTag0 + have aggregation : UInt64 := __do_lift.size + let falseType ← Ixon.Serialize.get + let falseElim ← Ixon.Serialize.get + let natType ← Claim.getOptAddr + let claim ← Claim.get + let logicalAxioms ← Claim.getOptAddr + pure + { + protocol := + { format := format, codec := codec, checker := checker, policy := policy, aggregation := aggregation }, + profile := { falseType := falseType, falseElim := falseElim, natType := natType }, claim := claim, + logicalAxioms := logicalAxioms } + if (__do_lift != envelopeMagic) = true then do + let __r ← throw "invalid certified claim magic" + __do_jp __r + else __do_jp () +PREMISE Ix.Certified.EnvelopeReading.mk +{address : Address} → + {bytes : ByteArray} → + (envelope : Envelope) → + address.hash.size = 32 → + Address.blake3 bytes = address → + Ixon.runGetExact getEnvelope bytes = Except.ok envelope → + envelopeBytes envelope = bytes → envelope.protocol = Protocol.current → EnvelopeReading address bytes +PREMISE Ix.Certified.TreeOpening.mk +{fuel : Nat} → + {root : Address} → + {bytes : ByteArray} → + (tree : AssumptionTree) → + Ixon.runGetExact (getTree fuel) bytes = Except.ok tree → + treeBytes tree = bytes → + treeRoot tree = root → + (∀ (address : Address), address ∈ treeLeaves tree → address.hash.size = 32) → TreeOpening fuel root bytes +PREMISE Ix.Certified.OptionalTreeOpening.mk +{fuel : Nat} → + {root : Option Address} → + {bytes : Option ByteArray} → + (leaves : List Address) → + (match root, bytes with + | none, none => leaves = [] + | some root, some bytes => ∃ opening, treeLeaves opening.tree = leaves + | x, x_1 => False) → + OptionalTreeOpening fuel root bytes +PREMISE Ix.Certified.SubjectView.mk +{fuel : Nat} → + {claim : Claim} → + {bytes : Option ByteArray} → + (addresses : List Address) → + (match claim with + | Claim.check address assumptions => bytes = none ∧ addresses = [address] + | Claim.checkEnv root assumptions => ∃ raw, bytes = some raw ∧ ∃ opening, treeLeaves opening.tree = addresses + | x => False) → + SubjectView fuel claim bytes +PREMISE Ix.Certified.PreparedLeaf.mk +{fuel : Nat} → + {objects : Objects} → + {witness : LeafWitness} → + (subjectView : SubjectView fuel witness.claim witness.subjects) → + (subjectRefs : List (Theory.ConstRef Address)) → + List.flatMapM (subjectReferences? objects) subjectView.addresses = some subjectRefs → + (frontierView : OptionalTreeOpening fuel (claimFrontier witness.claim) witness.frontierTree) → + (frontierRefs : List (Theory.ConstRef Address)) → + List.flatMapM (subjectReferences? objects) frontierView.leaves = some frontierRefs → + (∀ (ref : Theory.ConstRef Address), + ref ∈ List.map (fun x => x.ref) witness.frontier ↔ ref ∈ frontierRefs) → + PreparedLeaf fuel objects witness +PREMISE Ix.Certified.ContentView.mk +{fuel : Nat} → + {claim : Claim} → + {subjects members : Option ByteArray} → + {objects : Objects} → + {leaves : List (LeafInput fuel objects)} → + (addresses : List Address) → + (∀ (address : Address), address ∈ addresses ↔ address ∈ leafAddresses leaves) → + (match claim with + | Claim.check address assumptions => subjects = none ∧ members = none ∧ addresses = [address] + | Claim.checkEnv root assumptions => + members = none ∧ ∃ raw, subjects = some raw ∧ ∃ opening, treeLeaves opening.tree = addresses + | Claim.catalog memberRoot contentRoot assumptions => + ∃ rawContent rawMembers, + subjects = some rawContent ∧ + members = some rawMembers ∧ + ∃ content membership, + treeLeaves content.tree = addresses ∧ + ∃ roots, + List.mapM (fun leaf => environmentRoot? leaf.witness.claim) leaves = some roots ∧ + ∀ (root : Address), root ∈ treeLeaves membership.tree ↔ root ∈ roots + | x => False) → + ContentView fuel claim subjects members leaves +PREMISE Ix.Certified.ownedReferences +Theory.Certified.PrimitiveSignature Address → List (Theory.ConstRef Address) → List (Theory.ConstRef Address) +DEFINITION +fun signature refs => (List.filter (fun ref => ref != signature.falseType && ref != signature.falseElim) refs).eraseDups +PREMISE Ix.Certified.claimFrontier +Claim → Option Address +DEFINITION +fun x => + match x with + | Claim.check const frontier => frontier + | Claim.checkEnv root frontier => frontier + | Claim.catalog members content frontier => frontier + | Claim.eval input output frontier => frontier + | x => none +PREMISE Ix.Certified.LogicalChecking.mk +{source : Ixon.Env} → + {snapshot : SourceSnapshot source} → + {fuel : Nat} → + {envelope : Envelope} → + {witness : LogicalWitness} → + (List.map Prod.fst (snapshot.blobs ++ snapshot.literalBlobs)).Nodup → + (signature : Theory.Certified.PrimitiveSignature Address) → + readSignature? envelope.profile snapshot.decodedObjects = some signature → + (store : Theory.Store Address) → + readStore? fuel snapshot.decodedObjects snapshot.decodedNaturals = some store → + (leaves : List (LeafInput fuel snapshot.decodedObjects)) → + List.mapM (readLeafInput? fuel snapshot.decodedObjects) witness.leaves = some leaves → + (content : ContentView fuel envelope.claim witness.subjects witness.members leaves) → + (subjects : List (Theory.ConstRef Address)) → + List.flatMapM (subjectReferences? snapshot.decodedObjects) content.addresses = + some subjects → + (frontier : + OptionalTreeOpening fuel (claimFrontier envelope.claim) witness.frontierTree) → + (frontierRefs : List (Theory.ConstRef Address)) → + List.flatMapM (subjectReferences? snapshot.decodedObjects) frontier.leaves = + some frontierRefs → + (batch : + Theory.Certified.CheckedBatch fuel signature store + (List.map (fun leaf => leaf.prepared.node signature) leaves)) → + Theory.Certified.checkBatch? fuel signature store witness.frontier + (List.map (fun leaf => leaf.prepared.node signature) leaves) = + some batch → + (∀ (ref : Theory.ConstRef Address), + ref ∈ Theory.Certified.nodeSubjects batch.nodes ↔ + ref ∈ ownedReferences signature subjects) → + (∀ (ref : Theory.ConstRef Address), + ref ∈ batch.receipt.frontier.refs ↔ ref ∈ frontierRefs) → + (axioms : + OptionalTreeOpening fuel envelope.logicalAxioms witness.axiomTree) → + (axiomRefs : List (Theory.ConstRef Address)) → + List.flatMapM (subjectReferences? snapshot.decodedObjects) + axioms.leaves = + some axiomRefs → + (∀ (ref : Theory.ConstRef Address), + ref ∈ batch.logicalUses ↔ ref ∈ axiomRefs) → + LogicalChecking snapshot fuel envelope witness +PREMISE Ix.Certified.LogicalReceipt.mk +{source : Ixon.Env} → + {fuel : Nat} → + {envelope : Envelope} → + {witness : LogicalWitness} → + (snapshot : SourceSnapshot source) → + LogicalChecking snapshot fuel envelope witness → + envelope.protocol = Protocol.current → LogicalReceipt source fuel envelope witness +PREMISE Ix.Certified.SubjectsInterpreted +{source : Ixon.Env} → + {fuel : Nat} → + {envelope : Envelope} → + {witness : LogicalWitness} → + LogicalReceipt source fuel envelope witness → + (V : Type v) → [Theory.Model.SetTheory V] → Theory.Model.Assignment Address V → Prop +DEFINITION +fun {source} {fuel} {envelope} {witness} receipt V [Theory.Model.SetTheory V] constants => + ∀ (ref : Theory.ConstRef Address), + ref ∈ receipt.checked.subjects → + ∃ entry, + SourceDeclarationReading source receipt.snapshot.decodedObjects receipt.snapshot.decodedNaturals ref entry ∧ + ∀ (levels : List Nat), + levels.length = entry.universes → + ∀ (env : Nat → V), + Theory.Model.WellDenoted constants levels env entry.type ∧ + Theory.Model.SetTheory.Mem (constants ref levels) + (Theory.Model.interp constants levels env entry.type) +PREMISE Ix.Certified.LogicalMeaning +Ixon.Env → Nat → Envelope → Prop +DEFINITION +fun source fuel envelope => + ∃ witness receipt, + SignatureReading envelope.profile receipt.snapshot.decodedObjects receipt.checked.signature ∧ + (∀ (ref : Theory.ConstRef Address), + ref ∈ receipt.checked.frontierRefs → + ∃ entry, + receipt.checked.batch.receipt.frontier.interface.entries ref = some entry ∧ + SourceDeclarationReading source receipt.snapshot.decodedObjects receipt.snapshot.decodedNaturals ref + entry) ∧ + (∀ (ref : Theory.ConstRef Address), + ref ∈ receipt.checked.axiomRefs → + ∃ entry, + Theory.Certified.Standard.EntrySource receipt.checked.store ref entry ∨ + Theory.Certified.Quotient.EntrySource receipt.checked.store ref entry) ∧ + (∀ (V : Type v) [inst : Theory.Model.SetTheory V] (constants : Theory.Model.Assignment Address V), + receipt.checked.signature.Compatible receipt.checked.batch.receipt.frontier.interface.entries constants → + ∃ constants', + receipt.checked.signature.Compatible receipt.checked.batch.receipt.checked.result.entries constants' ∧ + Theory.Model.Assignment.AgreesOn receipt.checked.batch.receipt.frontier.interface.entries constants + constants' ∧ + SubjectsInterpreted receipt V constants') ∧ + (claimFrontier envelope.claim = none → + ∀ (V : Type v) [inst : Theory.Model.SetTheory V], + ∃ constants, + receipt.checked.signature.Compatible receipt.checked.batch.receipt.checked.result.entries constants ∧ + SubjectsInterpreted receipt V constants) +PREMISE Ix.Certified.RevealMatches +RevealConstantInfo → Ixon.ConstantInfo → Prop +DEFINITION +fun expected actual => + match expected, actual with + | RevealConstantInfo.defn kind safety levels type value, Ixon.ConstantInfo.defn actual => + Selected kind actual.kind ∧ + Selected safety actual.safety ∧ + Selected levels actual.lvls ∧ + Selected type (expressionAddress actual.typ) ∧ Selected value (expressionAddress actual.value) + | RevealConstantInfo.recr k safety levels params indices motives minors type rules, Ixon.ConstantInfo.recr actual => + Selected k actual.k ∧ + Selected safety actual.isUnsafe ∧ + Selected levels actual.lvls ∧ + Selected params actual.params ∧ + Selected indices actual.indices ∧ + Selected motives actual.motives ∧ + Selected minors actual.minors ∧ + Selected type (expressionAddress actual.typ) ∧ RulesMatch rules actual.rules + | RevealConstantInfo.axio safety levels type, Ixon.ConstantInfo.axio actual => + Selected safety actual.isUnsafe ∧ Selected levels actual.lvls ∧ Selected type (expressionAddress actual.typ) + | RevealConstantInfo.quot kind levels type, Ixon.ConstantInfo.quot actual => + Selected kind actual.kind ∧ Selected levels actual.lvls ∧ Selected type (expressionAddress actual.typ) + | RevealConstantInfo.cPrj index constructor block, Ixon.ConstantInfo.cPrj actual => + Selected index actual.idx ∧ Selected constructor actual.cidx ∧ Selected block actual.block + | RevealConstantInfo.iPrj index block, Ixon.ConstantInfo.iPrj actual => + Selected index actual.idx ∧ Selected block actual.block + | RevealConstantInfo.rPrj index block, Ixon.ConstantInfo.rPrj actual => + Selected index actual.idx ∧ Selected block actual.block + | RevealConstantInfo.dPrj index block, Ixon.ConstantInfo.dPrj actual => + Selected index actual.idx ∧ Selected block actual.block + | RevealConstantInfo.muts components, Ixon.ConstantInfo.muts actual => ComponentsMatch components actual + | x, x_1 => False +PREMISE Ix.Certified.ConstructorMatches +RevealConstructorInfo → Ixon.Constructor → Prop +DEFINITION +fun expected actual => + Selected expected.isUnsafe actual.isUnsafe ∧ + Selected expected.lvls actual.lvls ∧ + Selected expected.cidx actual.cidx ∧ + Selected expected.params actual.params ∧ + Selected expected.fields actual.fields ∧ Selected expected.typ (expressionAddress actual.typ) +PREMISE Ix.Certified.ConstructorsMatch +Option (Array (UInt64 × RevealConstructorInfo)) → Array Ixon.Constructor → Prop +DEFINITION +fun expected actual => + match expected with + | none => True + | some constructors => + ∀ (pair : UInt64 × RevealConstructorInfo), + pair ∈ constructors.toList → + match actual[pair.fst.toNat]? with + | none => False + | some constructor => ConstructorMatches pair.snd constructor +PREMISE Ix.Certified.MutMatches +RevealMutConstInfo → Ixon.MutConst → Prop +DEFINITION +fun expected actual => + match expected, actual with + | RevealMutConstInfo.defn kind safety levels type value, Ixon.MutConst.defn actual => + Selected kind actual.kind ∧ + Selected safety actual.safety ∧ + Selected levels actual.lvls ∧ + Selected type (expressionAddress actual.typ) ∧ Selected value (expressionAddress actual.value) + | RevealMutConstInfo.indc safety levels params indices type ctors, Ixon.MutConst.indc actual => + Selected safety actual.isUnsafe ∧ + Selected levels actual.lvls ∧ + Selected params actual.params ∧ + Selected indices actual.indices ∧ + Selected type (expressionAddress actual.typ) ∧ ConstructorsMatch ctors actual.ctors + | RevealMutConstInfo.recr k safety levels params indices motives minors type rules, Ixon.MutConst.recr actual => + Selected k actual.k ∧ + Selected safety actual.isUnsafe ∧ + Selected levels actual.lvls ∧ + Selected params actual.params ∧ + Selected indices actual.indices ∧ + Selected motives actual.motives ∧ + Selected minors actual.minors ∧ + Selected type (expressionAddress actual.typ) ∧ RulesMatch rules actual.rules + | x, x_1 => False +PREMISE Ix.Certified.ComponentsMatch +Array (UInt64 × RevealMutConstInfo) → Array Ixon.MutConst → Prop +DEFINITION +fun expected actual => + ∀ (pair : UInt64 × RevealMutConstInfo), + pair ∈ expected.toList → + match actual[pair.fst.toNat]? with + | none => False + | some member => MutMatches pair.snd member +PREMISE Ix.Certified.RulesMatch +Option (Array RevealRecursorRule) → Array Ixon.RecursorRule → Prop +DEFINITION +fun expected actual => + match expected with + | none => True + | some rules => + ∀ (rule : RevealRecursorRule), + rule ∈ rules.toList → + match actual[rule.ruleIdx.toNat]? with + | none => False + | some actual => rule.fields = actual.fields ∧ rule.rhs = expressionAddress actual.rhs +PREMISE Ix.Certified.RevealMeaning +Ixon.Env → Address → RevealConstantInfo → Prop +DEFINITION +fun source commitment info => + ∃ opening, + opening.secret.hash.size = 32 ∧ + opening.payload.hash.size = 32 ∧ + opening.commit = commitment ∧ + ∃ bytes object, ObjectBytes source opening.payload bytes object ∧ RevealMatches info object.info +PREMISE Ix.Certified.SemanticClaimMeaning +Ixon.Env → Nat → Envelope → Prop +DEFINITION +fun source fuel envelope => + envelope.protocol = Protocol.current ∧ + match envelope.claim with + | Claim.check const assumptions => LogicalMeaning source fuel envelope + | Claim.checkEnv root assumptions => LogicalMeaning source fuel envelope + | Claim.catalog members content assumptions => LogicalMeaning source fuel envelope + | Claim.contains root target => envelope.logicalAxioms = none ∧ TreeMembership root target + | Claim.reveal commitment info => envelope.logicalAxioms = none ∧ RevealMeaning source commitment info + | Claim.eval input output assumptions => False +PREMISE Ix.Certified.ModelHint.mk +Address → List Address → List Address → List ModelRuleHints → ModelHint +PREMISE Ix.Certified.ModelRuleHints.mk +Address → List (Option ModelProofHint) → ModelRuleHints +PREMISE Ix.Certified.ModelProofHint.mk +Address → Address → Address → Address → ModelProofHint +PREMISE Ix.Theory.Certificate.Modeled.Candidate.mk +{β : Type u} → + β → + List (Theory.ConstRef β) → + List (Theory.ConstRef β) → + List (Theory.ConstRef β × List (Option (Theory.Certificate.Modeled.ProofHint β))) → + Theory.Certificate.Modeled.Candidate β +PREMISE Ix.Theory.Certificate.Modeled.ProofHint.mk +{β : Type u} → + Theory.ConstRef β → Theory.ConstRef β → Theory.ConstRef β → Theory.VExpr β → Theory.Certificate.Modeled.ProofHint β +PREMISE Ix.Theory.Certified.Modeled.Witness.mk +{β : Type u} → + β → + List (Theory.ConstRef β) → + List (Theory.Certified.Modeled.Companion β) → + List (List (Theory.Certified.Modeled.EquationWitness β)) → Theory.Certified.Modeled.Witness β +PREMISE Ix.Theory.Certified.Modeled.Checked.mk +∀ {β : Type u} [inst : DecidableEq β] {entries : Theory.Model.Environment β} {store : Theory.Store β} + {witness : Theory.Certified.Modeled.Witness β}, + Theory.Certified.Modeled.sourceRefs? store witness.source witness.recursors = + some (List.map (fun x => x.header.ref) witness.companions) → + (∀ (companion : Theory.Certified.Modeled.Companion β), + companion ∈ witness.companions → Theory.Certified.Modeled.SourceMatches store companion) → + Theory.Certified.Modeled.CheckedCompanions entries witness.companions → + Theory.Certified.Modeled.Checked entries store witness +PREMISE Ix.Theory.Certified.Modeled.SourceMatches +{β : Type u} → [DecidableEq β] → Theory.Store β → Theory.Certified.Modeled.Companion β → Prop +DEFINITION +fun {β} [DecidableEq β] store companion => + store.type companion.header.ref = some companion.header.type.erase ∧ + store.uvars companion.header.ref = some companion.header.universes ∧ + Theory.Certified.Modeled.sourceRules? store companion.header.ref = + some (List.map Theory.Certified.Modeled.eraseRule companion.rules) +PREMISE Ix.Theory.Certified.Modeled.sourceRefs? +{β : Type u} → [DecidableEq β] → Theory.Store β → β → List (Theory.ConstRef β) → Option (List (Theory.ConstRef β)) +DEFINITION +fun {β} [DecidableEq β] store source recursors => do + let families ← Theory.Certified.Modeled.sourceFamilies? store source + if recursors.length < families.length then none + else do + let majors ← + List.mapM + (fun ref => do + let info ← Theory.Certified.Modeled.recursorSource? store ref + let major ← Theory.Certified.Modeled.majorSource? store info + pure major.family) + recursors + if (List.take families.length majors != families) = true then none + else do + let members ← + List.mapM + (fun family => do + let ctors ← Theory.Certified.Modeled.familyConstructors? store family + pure (family :: ctors)) + families + pure (members.flatten ++ recursors) +PREMISE Ix.Theory.Certified.Modeled.recursorSource? +{β : Type u} → Theory.Store β → Theory.ConstRef β → Option (Theory.Certified.Modeled.RecursorSource β) +DEFINITION +fun {β} store ref => do + let __x ← store.lookup ref + match __x with + | Theory.Const.recursor universes parameters indices motives minors type rules false Theory.Safety.safe => + pure + { universes := universes, parameters := parameters, indices := indices, motives := motives, minors := minors, + type := type, rules := rules } + | x => none +PREMISE Ix.Theory.Certified.Modeled.majorSource? +{β : Type u} → + [DecidableEq β] → + Theory.Store β → Theory.Certified.Modeled.RecursorSource β → Option (Theory.Certified.Modeled.MajorSource β) +DEFINITION +fun {β} [DecidableEq β] store source => do + let __x ← Theory.Certified.Modeled.splitPi? source.prefix source.type + match __x with + | (prefixDomains, tail) => do + let __x ← Theory.Certified.Modeled.splitPi? source.indices tail + match __x with + | (fst, major.forallE body) => do + let major ← Theory.Certified.Modeled.betaHead? 512 major + match major.appHead with + | Theory.VExpr.const family levels => do + let info ← Theory.Certified.Modeled.familySource? store family + if (info.indices != source.indices || levels.length != info.universes) = true then none + else + have arguments := major.appArgs []; + if (arguments.length != info.parameters + source.indices) = true then none + else + if (List.drop info.parameters arguments != Theory.VExpr.bvarRevRange 0 source.indices) = true then + none + else + have actual := List.take info.parameters arguments; + have parameters := List.map (fun p => p.unliftN source.indices 0) actual; + if (List.map (fun x => Theory.VExpr.liftN source.indices x) parameters != actual) = true then none + else + pure + { family := family, levels := levels, parameters := parameters, + prefixDomains := prefixDomains } + | x => none + | x => none +PREMISE Ix.Theory.Certified.Modeled.ruleSource? +{β : Type u} → + [DecidableEq β] → + Theory.Store β → + Theory.ConstRef β → + Theory.Certified.Modeled.RecursorSource β → + Theory.Certified.Modeled.MajorSource β → Theory.ConstRef β → Option (Theory.Certified.Modeled.RawRule β) +DEFINITION +fun {β} [DecidableEq β] store recursor source major constructor => + match constructor with + | Theory.ConstRef.ctor block member c => + if (major.family != Theory.ConstRef.member block member) = true then none + else do + let family ← Theory.Certified.Modeled.familySource? store major.family + let ctor ← store.lookupCtor constructor + if + (ctor.safety != Theory.Safety.safe || ctor.uvars != family.universes || ctor.nparams != family.parameters) = + true then + none + else + match store.ctorRuleIndex? constructor with + | some slot => do + let stored ← source.rules[slot]? + if (stored.nfields != ctor.nfields) = true then none + else do + let specialized ← + Theory.Certified.Modeled.applyPi? + (Theory.VExpr.liftN source.prefix (Theory.VExpr.instL major.levels ctor.type)) major.parameters + let __x ← Theory.Certified.Modeled.splitPi? ctor.nfields specialized + match __x with + | (fields, result) => + if (result.appHead != Theory.VExpr.const major.family major.levels) = true then none + else + have args := result.appArgs []; + have parameters := List.map (fun x => Theory.VExpr.liftN ctor.nfields x) major.parameters; + if + (args.length != ctor.nparams + source.indices || List.take ctor.nparams args != parameters) = + true then + none + else + have constructorValue := + (Theory.VExpr.const constructor major.levels).appN + (parameters ++ Theory.VExpr.bvarRevRange 0 ctor.nfields); + have callArgs := + Theory.VExpr.bvarRevRange ctor.nfields source.prefix ++ List.drop ctor.nparams args ++ + [constructorValue]; + do + let resultType ← + Theory.Certified.Modeled.applyPi? + (Theory.VExpr.liftN (source.prefix + ctor.nfields) source.type) callArgs + have telescope : List (Theory.VExpr β) := major.prefixDomains ++ fields + let _ ← Theory.Certified.Modeled.splitLam? telescope.length stored.rhs + pure + { universes := source.universes, type := Theory.VExpr.forallN telescope resultType, + lhs := + Theory.VExpr.lamN telescope + ((Theory.VExpr.const recursor + (List.map Theory.VLevel.param (List.range source.universes))).appN + callArgs), + rhs := stored.rhs } + | x => none + | x => none +PREMISE Ix.Theory.Certified.Modeled.Companion.entry +{β : Type u} → Theory.Certified.Modeled.Companion β → Theory.Model.ConstantEntry β +DEFINITION +fun {β} companion => + { universes := companion.header.universes, type := companion.header.type, body := none, + equations := List.map (fun rule => { lhs := rule.lhs, rhs := rule.rhs }) companion.rules } +PREMISE Ix.Theory.Certified.Modeled.CompanionChecked.mk +∀ {β : Type u} [inst : DecidableEq β] {entries : Theory.Model.Environment β} + {companions : List (Theory.Certified.Modeled.Companion β)} {companion : Theory.Certified.Modeled.Companion β}, + (∃ entry, + entries companion.model = some entry ∧ + entry.universes = companion.header.universes ∧ + entry.type = Theory.Model.AExpr.mapRefs (Theory.Certified.Modeled.mapping companions) companion.header.type) → + Theory.Model.AExpr.Scope companion.header.universes 0 companion.header.type → + Theory.Model.AExpr.ReferencesIn (Theory.Certified.Modeled.environment entries companions) companion.header.type → + (∀ (rule : Theory.Certified.Signature.Rule β), + rule ∈ companion.rules → + rule.universes = companion.header.universes ∧ + Theory.Model.AExpr.Scope companion.header.universes 0 rule.lhs ∧ + Theory.Model.AExpr.Scope companion.header.universes 0 rule.rhs) → + (∀ (rule : Theory.Certified.Signature.Rule β), + rule ∈ companion.rules → + Theory.Model.AExpr.ReferencesIn (Theory.Certified.Modeled.environment entries companions) rule.lhs ∧ + Theory.Model.AExpr.ReferencesIn (Theory.Certified.Modeled.environment entries companions) rule.rhs) → + (∀ (rule : Theory.Certified.Signature.Rule β), + rule ∈ companion.rules → + Theory.Certified.Modeled.CheckedEquation entries (Theory.Certified.Modeled.mapRule companions rule)) → + Theory.Certified.Modeled.CompanionChecked entries companions companion +PREMISE Ix.Theory.Certified.Modeled.CheckedCompanions.mk +∀ {β : Type u} [inst : DecidableEq β] {entries : Theory.Model.Environment β} + {companions : List (Theory.Certified.Modeled.Companion β)}, + (List.map (fun x => x.header.ref) companions).Nodup → + (∀ (companion : Theory.Certified.Modeled.Companion β), + companion ∈ companions → entries companion.header.ref = none) → + (∀ (companion : Theory.Certified.Modeled.Companion β), + companion ∈ companions → Theory.Certified.Modeled.CompanionChecked entries companions companion) → + Theory.Certified.Modeled.CheckedCompanions entries companions +PREMISE Ix.Theory.Certified.Modeled.CheckedEquation.mk +∀ {β : Type u} {entries : Theory.Model.Environment β} {rule : Theory.Certified.Signature.Rule β}, + Theory.Certified.Signature.RuleFormed entries rule → + Theory.Model.ConversionClaim entries [] rule.lhs rule.rhs → Theory.Certified.Modeled.CheckedEquation entries rule +PREMISE Ix.Theory.Certified.Modeled.checkEquation? +{β : Type u} → + [DecidableEq β] → + Nat → + (entries : Theory.Model.Environment β) → + (rule : Theory.Certified.Signature.Rule β) → + Theory.Certified.Modeled.EquationWitness β → + Option (Theory.Certified.CheckedClaim (Theory.Certified.Modeled.CheckedEquation entries rule)) +DEFINITION +fun {β} [DecidableEq β] fuel entries rule witness => do + let formed ← Theory.Certified.Signature.checkRule fuel entries rule witness.formation + match witness.proof with + | Theory.Certified.Modeled.EquationProof.conversion witness => do + let equality ← Theory.Certified.verifyConversion fuel rule.universes entries [] rule.lhs rule.rhs witness + pure { down := ⋯ } + | Theory.Certified.Modeled.EquationProof.propositional family reflexivity recursor proof typing => + if hi : Theory.Certified.Basis.Equality.Interface entries family reflexivity recursor then do + let type ← + Theory.Certified.verifyType fuel rule.universes entries [] rule.type + (Theory.Model.AExpr.sort witness.formation.level) witness.formation.type + let checked ← + Theory.Certified.verifyType fuel rule.universes entries [] proof + (Theory.Certified.Basis.Equality.applied family witness.formation.level rule.type rule.lhs rule.rhs) + typing + pure { down := ⋯ } + else none +RUNTIME EXTERN Blake3.Rust.hasherFinalize +Blake3.Rust.Hasher → (length : USize) → { r // r.size = length.toNat } +RUNTIME EXTERN Blake3.Rust.hasherInit +Unit → Blake3.Rust.Hasher +RUNTIME EXTERN Blake3.Rust.hasherInitDeriveKey +ByteArray → Blake3.Rust.Hasher +RUNTIME EXTERN Blake3.Rust.hasherInitKeyed +Blake3.Blake3Key → Blake3.Rust.Hasher +RUNTIME EXTERN Blake3.Rust.hasherUpdate +Blake3.Rust.Hasher → ByteArray → Blake3.Rust.Hasher +RECURSION WORKER Ixon.getExprAllBinders._unsafe_rec +Ixon.GetM Ixon.Expr → Nat → Ixon.GetM (List (Ixon.Uses × Ixon.Owned × Ixon.Expr)) +IMPLEMENTATION +fun recur x => + match x with + | 0 => pure [] + | count.succ => do + let mode ← Ixon.getU8 + have __do_jp : Unit → Ixon.GetM (List (Ixon.Uses × Ixon.Owned × Ixon.Expr)) := fun __r => + match Ixon.Uses.ofBits? (mode &&& 3) with + | some uses => + match Ixon.Owned.ofBits? (mode >>> 2 &&& 1) with + | some owned => do + let ty ← recur + let tail ← Ixon.getExprAllBinders._unsafe_rec recur count + pure ((uses, owned, ty) :: tail) + | x => throw (toString "getExpr: invalid forall ownership mode " ++ toString mode) + | x => throw (toString "getExpr: invalid forall usage mode " ++ toString mode) + if mode > 7 then do + let __r ← throw (toString "getExpr: invalid forall mode " ++ toString mode) + __do_jp __r + else __do_jp () +RECURSION WORKER Ixon.getExprAppArgs._unsafe_rec +Ixon.GetM Ixon.Expr → Nat → Ixon.Expr → Ixon.GetM Ixon.Expr +IMPLEMENTATION +fun recur x x_1 => + match x, x_1 with + | 0, result => pure result + | count.succ, result => do + let arg ← recur + Ixon.getExprAppArgs._unsafe_rec recur count (result.app arg) +RECURSION WORKER Ixon.getExprFuel._unsafe_rec +Nat → Ixon.GetM Ixon.Expr +IMPLEMENTATION +fun x => + match x with + | 0 => throw "getExpr: recursion budget exhausted" + | fuel.succ => Ixon.getTag4 >>= Ixon.getExprFromTag (Ixon.getExprFuel._unsafe_rec fuel) +RECURSION WORKER Ixon.getExprLamBinders._unsafe_rec +Ixon.GetM Ixon.Expr → Nat → Ixon.GetM (List (Ixon.Uses × Ixon.Expr)) +IMPLEMENTATION +fun recur x => + match x with + | 0 => pure [] + | count.succ => do + let mode ← Ixon.getU8 + match Ixon.Uses.ofBits? mode with + | some uses => do + let ty ← recur + let tail ← Ixon.getExprLamBinders._unsafe_rec recur count + pure ((uses, ty) :: tail) + | x => throw (toString "getExpr: invalid lambda mode " ++ toString mode) +RECURSION WORKER Ixon.getTag0Sizes._unsafe_rec +Nat → Ixon.GetM (List UInt64) +IMPLEMENTATION +fun x => + match x with + | 0 => pure [] + | count.succ => do + let __do_lift ← Ixon.getTag0 + let head : UInt64 := __do_lift.size + let tail ← Ixon.getTag0Sizes._unsafe_rec count + pure (head :: tail) +RECURSION WORKER Ixon.getU64TrimmedLEAux._unsafe_rec +Nat → Ixon.GetM UInt64 +IMPLEMENTATION +fun x => + match x with + | 0 => pure 0 + | len.succ => do + let low ← Ixon.getU8 + let high ← Ixon.getU64TrimmedLEAux._unsafe_rec len + pure (low.toUInt64 ||| high <<< 8) +RECURSION WORKER Ixon.getUnivFuel._unsafe_rec +Nat → Ixon.GetM Ixon.Univ +IMPLEMENTATION +fun x => + match x with + | 0 => throw "getUniv: recursion budget exhausted" + | fuel.succ => Ixon.getTag2 >>= Ixon.getUnivFromTag (Ixon.getUnivFuel._unsafe_rec fuel) +RECURSION WORKER Ixon.putExpr._unsafe_rec +Ixon.Expr → Ixon.PutM Unit +IMPLEMENTATION +fun x => + match x with + | Ixon.Expr.sort idx => Ixon.putTag4 { flag := Ixon.Expr.FLAG_SORT, size := idx } + | Ixon.Expr.var idx => Ixon.putTag4 { flag := Ixon.Expr.FLAG_VAR, size := idx } + | Ixon.Expr.ref refIdx univIdxs => do + Ixon.putTag4 { flag := Ixon.Expr.FLAG_REF, size := univIdxs.size.toUInt64 } + Ixon.putTag0 { size := refIdx } + forIn univIdxs PUnit.unit fun idx __s => do + Ixon.putTag0 { size := idx } + pure (ForInStep.yield PUnit.unit) + pure () + | Ixon.Expr.recur recIdx univIdxs => do + Ixon.putTag4 { flag := Ixon.Expr.FLAG_REC, size := univIdxs.size.toUInt64 } + Ixon.putTag0 { size := recIdx } + forIn univIdxs PUnit.unit fun idx __s => do + Ixon.putTag0 { size := idx } + pure (ForInStep.yield PUnit.unit) + pure () + | Ixon.Expr.prj typeRefIdx fieldIdx val => do + Ixon.putTag4 { flag := Ixon.Expr.FLAG_PRJ, size := fieldIdx } + Ixon.putTag0 { size := typeRefIdx } + Ixon.putExpr._unsafe_rec val + | Ixon.Expr.str refIdx => Ixon.putTag4 { flag := Ixon.Expr.FLAG_STR, size := refIdx } + | Ixon.Expr.nat refIdx => Ixon.putTag4 { flag := Ixon.Expr.FLAG_NAT, size := refIdx } + | e@h:(a.app a_1) => do + Ixon.putTag4 { flag := Ixon.Expr.FLAG_APP, size := e.collectAppArgs.fst.length.toUInt64 } + Ixon.putExpr._unsafe_rec e.collectAppArgs.snd + forIn e.collectAppArgs.fst PUnit.unit fun arg __s => do + Ixon.putExpr._unsafe_rec arg + pure (ForInStep.yield PUnit.unit) + pure () + | e@h:(Ixon.Expr.lam a a_1 a_2) => do + Ixon.putTag4 { flag := Ixon.Expr.FLAG_LAM, size := e.collectLamBinders.fst.length.toUInt64 } + forIn e.collectLamBinders.fst PUnit.unit fun binder __s => do + Ixon.putU8 binder.fst.toBits + Ixon.putExpr._unsafe_rec binder.snd + pure (ForInStep.yield PUnit.unit) + Ixon.putExpr._unsafe_rec e.collectLamBinders.snd + | e@h:(Ixon.Expr.all a a_1 a_2 a_3) => do + Ixon.putTag4 { flag := Ixon.Expr.FLAG_ALL, size := e.collectAllBinders.fst.length.toUInt64 } + forIn e.collectAllBinders.fst PUnit.unit fun binder __s => do + Ixon.putU8 (binder.fst.toBits ||| binder.snd.fst.toBits <<< 2) + Ixon.putExpr._unsafe_rec binder.snd.snd + pure (ForInStep.yield PUnit.unit) + Ixon.putExpr._unsafe_rec e.collectAllBinders.snd + | Ixon.Expr.letE nonDep ty val body => do + Ixon.putTag4 { flag := Ixon.Expr.FLAG_LET, size := if nonDep = true then 1 else 0 } + Ixon.putExpr._unsafe_rec ty + Ixon.putExpr._unsafe_rec val + Ixon.putExpr._unsafe_rec body + | Ixon.Expr.share idx => Ixon.putTag4 { flag := Ixon.Expr.FLAG_SHARE, size := idx } +RECURSION WORKER Ixon.putU64TrimmedLEAux._unsafe_rec +UInt64 → Nat → Ixon.PutM Unit +IMPLEMENTATION +fun x x_1 => + match x_1 with + | 0 => pure () + | len.succ => do + Ixon.putU8 x.toUInt8 + Ixon.putU64TrimmedLEAux._unsafe_rec (x >>> 8) len +RECURSION WORKER Ixon.putUniv._unsafe_rec +Ixon.Univ → Ixon.PutM Unit +IMPLEMENTATION +fun x => + match x with + | Ixon.Univ.zero => Ixon.putTag2 { flag := Ixon.Univ.FLAG_ZERO_SUCC, size := 0 } + | u@h:a.succ => do + Ixon.putTag2 { flag := Ixon.Univ.FLAG_ZERO_SUCC, size := u.succCount } + Ixon.putUniv._unsafe_rec u.succBase + | a.max b => do + Ixon.putTag2 { flag := Ixon.Univ.FLAG_MAX, size := 0 } + Ixon.putUniv._unsafe_rec a + Ixon.putUniv._unsafe_rec b + | a.imax b => do + Ixon.putTag2 { flag := Ixon.Univ.FLAG_IMAX, size := 0 } + Ixon.putUniv._unsafe_rec a + Ixon.putUniv._unsafe_rec b + | Ixon.Univ.var idx => Ixon.putTag2 { flag := Ixon.Univ.FLAG_VAR, size := idx } +RECURSION WORKER _private.Ix.Address.0.bytesOfHex.go._unsafe_rec +List Char → Option (List UInt8) +IMPLEMENTATION +fun a => + match a with + | hi :: lo :: rest => do + let b ← byteOfHex hi lo + let bs ← bytesOfHex.go._unsafe_rec✝ rest + some (b :: bs) + | [] => pure [] + | x => none +RECURSION WORKER Ix.Certified.frontierOrder?._unsafe_rec +Nat → + Theory.Certified.PrimitiveSignature Address → + Theory.Store Address → + List (Theory.ConstRef Address) → + List (Theory.ConstRef Address) → + List (Theory.ConstRef Address) → List (Theory.ConstRef Address) → Option (List (Theory.ConstRef Address)) +IMPLEMENTATION +fun fuel signature store allowed pending visited active => + match fuel, pending with + | x, [] => some visited + | 0, head :: tail => none + | fuel.succ, ref :: rest => + if ref = signature.falseType ∨ ref = signature.falseElim ∨ ref ∈ visited then + frontierOrder?._unsafe_rec fuel signature store allowed rest visited active + else + if ref ∈ active ∨ ¬ref ∈ allowed then none + else do + let type ← store.type ref + let visited ← frontierOrder?._unsafe_rec fuel signature store allowed type.refs visited (ref :: active) + frontierOrder?._unsafe_rec fuel signature store allowed rest (visited ++ [ref]) active +RECURSION WORKER Ix.Certified.getTreeBody._unsafe_rec +Nat → Ixon.GetM AssumptionTree +IMPLEMENTATION +fun x => + match x with + | 0 => throw "certified tree depth limit" + | fuel.succ => do + let __do_lift ← Ixon.getU8 + match __do_lift with + | 0 => do + let __do_lift ← Ixon.Serialize.get + pure (AssumptionTree.leaf __do_lift) + | 1 => pure AssumptionTree.padding + | 2 => do + let __do_lift ← getTreeBody._unsafe_rec fuel + let __do_lift_1 ← getTreeBody._unsafe_rec fuel + pure (__do_lift.node __do_lift_1) + | x => throw "invalid certified tree node" +RECURSION WORKER Ix.Certified.lookup._unsafe_rec +{α : Type u_1} → List (Address × α) → Address → Option α +IMPLEMENTATION +fun {α} entries address => + match entries with + | [] => none + | (key, value) :: rest => if address = key then some value else lookup._unsafe_rec rest address +RECURSION WORKER Ix.Certified.putTreeBody._unsafe_rec +AssumptionTree → Ixon.PutM Unit +IMPLEMENTATION +fun x => + match x with + | AssumptionTree.leaf address => do + Ixon.putU8 0 + Ixon.Serialize.put address + | AssumptionTree.padding => Ixon.putU8 1 + | left.node right => do + Ixon.putU8 2 + putTreeBody._unsafe_rec left + putTreeBody._unsafe_rec right +RECURSION WORKER Ix.Certified.readBlocks?._unsafe_rec +Nat → Objects → Naturals → Objects → Option (List (Address × Theory.Block Address)) +IMPLEMENTATION +fun fuel objects naturals x => + match x with + | [] => some [] + | (address, source) :: rest => + if isProjection source.info = true then do + let _ ← resolveReference? objects address + readBlocks?._unsafe_rec fuel objects naturals rest + else do + let block ← readBlock? fuel objects naturals address source + let rest ← readBlocks?._unsafe_rec fuel objects naturals rest + pure ((address, block) :: rest) +RECURSION WORKER Ix.Certified.readExpr?._unsafe_rec +Nat → Objects → Naturals → Address → Ixon.Constant → Ixon.Expr → Option (Theory.VExpr Address) +IMPLEMENTATION +fun fuel objects naturals block source => + match (motive := Nat → Ixon.Expr → Option (Theory.VExpr Address)) fuel with + | 0 => fun x => none + | fuel.succ => fun expression => + match expression with + | Ixon.Expr.var index => some (Theory.VExpr.bvar index.toNat) + | Ixon.Expr.sort index => Option.map (Theory.VExpr.sort ∘ readLevel) source.univs[index.toNat]? + | Ixon.Expr.ref index levels => do + let address ← source.refs[index.toNat]? + let reference ← resolveReference? objects address + let levels ← readLevels? source levels + pure (Theory.VExpr.const reference levels) + | Ixon.Expr.recur index levels => do + let levels ← readLevels? source levels + pure (Theory.VExpr.const (Theory.ConstRef.member block index.toNat) levels) + | f.app a => do + let f ← readExpr?._unsafe_rec fuel objects naturals block source f + let a ← readExpr?._unsafe_rec fuel objects naturals block source a + pure (f.app a) + | Ixon.Expr.lam Ixon.Uses.many A b => do + let A ← readExpr?._unsafe_rec fuel objects naturals block source A + let b ← readExpr?._unsafe_rec fuel objects naturals block source b + pure (A.lam b) + | Ixon.Expr.all Ixon.Uses.many Ixon.Owned.shared A B => do + let A ← readExpr?._unsafe_rec fuel objects naturals block source A + let B ← readExpr?._unsafe_rec fuel objects naturals block source B + pure (A.forallE B) + | Ixon.Expr.prj owner field value => do + let address ← source.refs[owner.toNat]? + let ownerSource ← lookup objects address + match ownerSource.info with + | Ixon.ConstantInfo.iPrj a => do + let reference ← resolveReference? objects address + let value ← readExpr?._unsafe_rec fuel objects naturals block source value + pure (Theory.VExpr.proj reference field.toNat value) + | x => none + | Ixon.Expr.nat index => do + let address ← source.refs[index.toNat]? + let __do_lift ← lookup naturals address + pure (Theory.VExpr.natLit __do_lift) + | Ixon.Expr.share index => do + let shared ← source.sharing[index.toNat]? + readExpr?._unsafe_rec fuel objects naturals block source shared + | x => none +RECURSION WORKER Ix.Certified.readLevel._unsafe_rec +Ixon.Univ → Theory.VLevel +IMPLEMENTATION +fun x => + match x with + | Ixon.Univ.zero => Theory.VLevel.zero + | u.succ => (readLevel._unsafe_rec u).succ + | u.max v => (readLevel._unsafe_rec u).max (readLevel._unsafe_rec v) + | u.imax v => (readLevel._unsafe_rec u).imax (readLevel._unsafe_rec v) + | Ixon.Univ.var i => Theory.VLevel.param i.toNat +RECURSION WORKER Ix.Certified.sourceLevelValue._unsafe_rec +List Nat → Ixon.Univ → Nat +IMPLEMENTATION +fun levels x => + match x with + | Ixon.Univ.zero => 0 + | level.succ => sourceLevelValue._unsafe_rec levels level + 1 + | left.max right => max (sourceLevelValue._unsafe_rec levels left) (sourceLevelValue._unsafe_rec levels right) + | left.imax right => + if sourceLevelValue._unsafe_rec levels right = 0 then 0 + else max (sourceLevelValue._unsafe_rec levels left) (sourceLevelValue._unsafe_rec levels right) + | Ixon.Univ.var index => levels.getD index.toNat 0 +RECURSION WORKER Ix.Certified.treeLeaves._unsafe_rec +AssumptionTree → List Address +IMPLEMENTATION +fun x => + match x with + | AssumptionTree.leaf address => [address] + | AssumptionTree.padding => [] + | left.node right => treeLeaves._unsafe_rec left ++ treeLeaves._unsafe_rec right +RECURSION WORKER Ix.Certified.treeRoot._unsafe_rec +AssumptionTree → Address +IMPLEMENTATION +fun x => + match x with + | AssumptionTree.leaf address => Merkle.leafHash address + | AssumptionTree.padding => Merkle.zeroAddress + | left.node right => Merkle.nodeHash (treeRoot._unsafe_rec left) (treeRoot._unsafe_rec right) +RECURSION WORKER Ixon.Expr.collectAllBinders._unsafe_rec +Ixon.Expr → List (Ixon.Uses × Ixon.Owned × Ixon.Expr) × Ixon.Expr +IMPLEMENTATION +fun x => + match x with + | Ixon.Expr.all uses owned ty body => + match Ixon.Expr.collectAllBinders._unsafe_rec body with + | (binders, base) => ((uses, owned, ty) :: binders, base) + | e => ([], e) +RECURSION WORKER Ixon.Expr.collectAppArgs._unsafe_rec +Ixon.Expr → List Ixon.Expr × Ixon.Expr +IMPLEMENTATION +fun x => + match x with + | f.app a => + match Ixon.Expr.collectAppArgs._unsafe_rec f with + | (args, base) => (args ++ [a], base) + | e => ([], e) +RECURSION WORKER Ixon.Expr.collectLamBinders._unsafe_rec +Ixon.Expr → List (Ixon.Uses × Ixon.Expr) × Ixon.Expr +IMPLEMENTATION +fun x => + match x with + | Ixon.Expr.lam uses ty body => + match Ixon.Expr.collectLamBinders._unsafe_rec body with + | (binders, base) => ((uses, ty) :: binders, base) + | e => ([], e) +RECURSION WORKER Ixon.Expr.nodeCount._unsafe_rec +Ixon.Expr → Nat +IMPLEMENTATION +fun x => + match x with + | Ixon.Expr.sort a => 1 + | Ixon.Expr.var a => 1 + | Ixon.Expr.ref a a_1 => 1 + | Ixon.Expr.recur a a_1 => 1 + | Ixon.Expr.str a => 1 + | Ixon.Expr.nat a => 1 + | Ixon.Expr.share a => 1 + | Ixon.Expr.prj a a_1 val => Ixon.Expr.nodeCount._unsafe_rec val + 1 + | fn.app arg => Ixon.Expr.nodeCount._unsafe_rec fn + Ixon.Expr.nodeCount._unsafe_rec arg + 1 + | Ixon.Expr.lam a fn arg => Ixon.Expr.nodeCount._unsafe_rec fn + Ixon.Expr.nodeCount._unsafe_rec arg + 1 + | Ixon.Expr.all a a_1 fn arg => Ixon.Expr.nodeCount._unsafe_rec fn + Ixon.Expr.nodeCount._unsafe_rec arg + 1 + | Ixon.Expr.letE a ty val body => + Ixon.Expr.nodeCount._unsafe_rec ty + Ixon.Expr.nodeCount._unsafe_rec val + Ixon.Expr.nodeCount._unsafe_rec body + 1 +RECURSION WORKER Ixon.Univ.addSucc._unsafe_rec +Nat → Ixon.Univ → Ixon.Univ +IMPLEMENTATION +fun x x_1 => + match x, x_1 with + | 0, base => base + | count.succ, base => (Ixon.Univ.addSucc._unsafe_rec count base).succ +RECURSION WORKER Ixon.Univ.succBase._unsafe_rec +Ixon.Univ → Ixon.Univ +IMPLEMENTATION +fun x => + match x with + | inner.succ => Ixon.Univ.succBase._unsafe_rec inner + | u => u +RECURSION WORKER Ixon.Univ.succCountNat._unsafe_rec +Ixon.Univ → Nat +IMPLEMENTATION +fun x => + match x with + | inner.succ => 1 + Ixon.Univ.succCountNat._unsafe_rec inner + | x => 0 +RECURSION WORKER _private.Ix.Common.0.Nat.toBytesLE.go._unsafe_rec +Nat → Nat → List UInt8 +IMPLEMENTATION +fun a a_1 => + match a, a_1 with + | x, 0 => [] + | 0, x => [] + | f.succ, x => x.toUInt8 :: Nat.toBytesLE.go._unsafe_rec✝ f (x / 256) +RECURSION WORKER Ix.Certified.InputCache.natural?._unsafe_rec +(address : Address) → (bytes : ByteArray) → List CachedNatural → Option (DecodedNatural address bytes) +IMPLEMENTATION +fun address bytes x => + match x with + | [] => none + | entry :: rest => + if ha : entry.address = address then + if hb : entry.bytes = bytes then some (⋯.mpr entry.decoded) + else InputCache.natural?._unsafe_rec address bytes rest + else InputCache.natural?._unsafe_rec address bytes rest +RECURSION WORKER Ix.Certified.InputCache.object?._unsafe_rec +(address : Address) → (bytes : ByteArray) → List CachedObject → Option (DecodedObject address bytes) +IMPLEMENTATION +fun address bytes x => + match x with + | [] => none + | entry :: rest => + if ha : entry.address = address then + if hb : entry.bytes = bytes then some (⋯.mpr entry.decoded) else InputCache.object?._unsafe_rec address bytes rest + else InputCache.object?._unsafe_rec address bytes rest +RECURSION WORKER Ix.Theory.Certificate.annotations._unsafe_rec +{β : Type u} → Theory.Model.AExpr β → Theory.Model.AnnotationTree +IMPLEMENTATION +fun {β} x => + match x with + | Theory.Model.AExpr.bvar index => Theory.Model.AnnotationTree.leaf + | Theory.Model.AExpr.sort level => Theory.Model.AnnotationTree.leaf + | Theory.Model.AExpr.const ref levels => Theory.Model.AnnotationTree.leaf + | Theory.Model.AExpr.natLit value => Theory.Model.AnnotationTree.leaf + | f.app a => (Theory.Certificate.annotations._unsafe_rec f).app (Theory.Certificate.annotations._unsafe_rec a) + | Theory.Model.AExpr.lam p A b => + Theory.Model.AnnotationTree.lam p.toRaw (Theory.Certificate.annotations._unsafe_rec A) + (Theory.Certificate.annotations._unsafe_rec b) + | Theory.Model.AExpr.forallE p A B => + Theory.Model.AnnotationTree.forallE p.toRaw (Theory.Certificate.annotations._unsafe_rec A) + (Theory.Certificate.annotations._unsafe_rec B) + | Theory.Model.AExpr.proj ref field e => (Theory.Certificate.annotations._unsafe_rec e).proj +RECURSION WORKER Ix.Theory.Certificate.applicationArgs._unsafe_rec +{β : Type u} → Theory.Model.AExpr β → List (Theory.Model.AExpr β) → List (Theory.Model.AExpr β) +IMPLEMENTATION +fun {β} x x_1 => + match x, x_1 with + | f.app a, args => Theory.Certificate.applicationArgs._unsafe_rec f (a :: args) + | x, args => args +RECURSION WORKER Ix.Theory.Certificate.applyFact?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Nat → + Theory.Model.Environment β → + Theory.Model.Context β → + Theory.Certificate.Suggestion β → List (Theory.Model.AExpr β) → Option (Theory.Certificate.Suggestion β) +IMPLEMENTATION +fun {β} [inst : DecidableEq β] [Theory.Certificate.Hints β] fuel n entries Γ fact args => + match fuel with + | 0 => none + | fuel.succ => + match args with + | [] => some fact + | arg :: rest => + match fact.expression with + | Theory.Model.AExpr.lam p D body => + match fact.type with + | Theory.Model.AExpr.forallE q A B => + if (p != q || D != A) = true then none + else do + let a ← Theory.Certificate.inferAnnotated?._unsafe_rec fuel n entries Γ arg + let aw ← Theory.Certificate.castWith?._unsafe_rec fuel n entries Γ a D + Theory.Certificate.applyFact?._unsafe_rec fuel n entries Γ + { expression := body.inst arg, type := B.inst arg, + witness := Theory.Certified.TypingWitness.betaResult p D body arg B fact.witness aw } + rest + | x => none + | x => none +RECURSION WORKER Ix.Theory.Certificate.beta?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Nat → + Theory.Model.Environment β → + Theory.Model.Context β → + Theory.Model.AExpr β → Option (Theory.Model.AExpr β × Theory.Certified.ConversionWitness β) +IMPLEMENTATION +fun {β} [inst : DecidableEq β] [Theory.Certificate.Hints β] fuel n entries Γ e => + match fuel with + | 0 => none + | fuel.succ => + match e with + | (Theory.Model.AExpr.lam p D body).app arg => do + let fn ← Theory.Certificate.inferAnnotated?._unsafe_rec fuel n entries Γ (Theory.Model.AExpr.lam p D body) + let a ← Theory.Certificate.inferAnnotated?._unsafe_rec fuel n entries Γ arg + let aw ← Theory.Certificate.castWith?._unsafe_rec fuel n entries Γ a D + pure (body.inst arg, Theory.Certified.ConversionWitness.beta fn.type fn.witness aw) + | x => none +RECURSION WORKER Ix.Theory.Certificate.castWith?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Nat → + Theory.Model.Environment β → + Theory.Model.Context β → + Theory.Certificate.Suggestion β → Theory.Model.AExpr β → Option (Theory.Certified.TypingWitness β) +IMPLEMENTATION +fun {β} [DecidableEq β] [Theory.Certificate.Hints β] fuel n entries Γ suggestion type => + match fuel with + | 0 => none + | fuel.succ => + match Theory.Certificate.cast? n suggestion type with + | some witness => some witness + | none => do + let target ← Theory.Certificate.inferAnnotated?._unsafe_rec fuel n entries Γ type + let target ← Theory.Certificate.normalizeType?._unsafe_rec fuel n entries Γ target + match target.type with + | Theory.Model.AExpr.sort l => do + let witness ← Theory.Certificate.conversion?._unsafe_rec fuel n entries Γ suggestion.type type + pure (Theory.Certified.TypingWitness.conv suggestion.type l suggestion.witness target.witness witness) + | x => none +RECURSION WORKER Ix.Theory.Certificate.conversion?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Nat → + Theory.Model.Environment β → + Theory.Model.Context β → + Theory.Model.AExpr β → Theory.Model.AExpr β → Option (Theory.Certified.ConversionWitness β) +IMPLEMENTATION +fun {β} [DecidableEq β] [Theory.Certificate.Hints β] fuel n entries Γ a b => + match fuel with + | 0 => none + | fuel.succ => + if a = b then some Theory.Certified.ConversionWitness.refl + else + match Theory.Certificate.headStep?._unsafe_rec fuel n entries Γ a with + | some (a', w) => do + let __do_lift ← Theory.Certificate.conversion?._unsafe_rec fuel n entries Γ a' b + pure (Theory.Certified.ConversionWitness.trans a' w __do_lift) + | none => + match Theory.Certificate.headStep?._unsafe_rec fuel n entries Γ b with + | some (b', w) => do + let __do_lift ← Theory.Certificate.conversion?._unsafe_rec fuel n entries Γ a b' + pure (Theory.Certified.ConversionWitness.trans b' __do_lift w.symm) + | none => + let congruence := fun x => + match a, b with + | Theory.Model.AExpr.sort l, Theory.Model.AExpr.sort l' => + if Theory.Certified.LevelEq.check n l l' = true then some Theory.Certified.ConversionWitness.sort + else none + | f.app x, g.app y => do + let __do_lift ← Theory.Certificate.conversion?._unsafe_rec fuel n entries Γ f g + let __do_lift_1 ← Theory.Certificate.conversion?._unsafe_rec fuel n entries Γ x y + pure (__do_lift.app __do_lift_1) + | Theory.Model.AExpr.proj r i x, Theory.Model.AExpr.proj q j y => + if r = q ∧ i = j then + (fun x => x.proj) <$> Theory.Certificate.conversion?._unsafe_rec fuel n entries Γ x y + else none + | Theory.Model.AExpr.lam p D body, Theory.Model.AExpr.lam q D' body' => + if (p != q) = true then none + else do + let domain ← Theory.Certificate.inferAnnotated?._unsafe_rec fuel n entries Γ D + match domain.type with + | Theory.Model.AExpr.sort l => do + let __do_lift ← Theory.Certificate.conversion?._unsafe_rec fuel n entries Γ D D' + let __do_lift_1 ← + Theory.Certificate.conversion?._unsafe_rec fuel n entries (Theory.Model.Context.push D Γ) body + body' + pure (Theory.Certified.ConversionWitness.lam l domain.witness __do_lift __do_lift_1) + | x => none + | Theory.Model.AExpr.forallE p D B, Theory.Model.AExpr.forallE q D' B' => + if (p != q) = true then none + else do + let domain ← Theory.Certificate.inferAnnotated?._unsafe_rec fuel n entries Γ D + match domain.type with + | Theory.Model.AExpr.sort l => do + let __do_lift ← Theory.Certificate.conversion?._unsafe_rec fuel n entries Γ D D' + let __do_lift_1 ← + Theory.Certificate.conversion?._unsafe_rec fuel n entries (Theory.Model.Context.push D Γ) B B' + pure (Theory.Certified.ConversionWitness.forallE l domain.witness __do_lift __do_lift_1) + | x => none + | x, x_1 => none; + let proofIrrel := fun x => do + let left ← Theory.Certificate.inferAnnotated?._unsafe_rec fuel n entries Γ a + let type ← Theory.Certificate.inferAnnotated?._unsafe_rec fuel n entries Γ left.type + let typeWitness ← Theory.Certificate.cast? n type (Theory.Model.AExpr.sort Theory.VLevel.zero) + let right ← Theory.Certificate.inferAnnotated?._unsafe_rec fuel n entries Γ b + let rightWitness ← Theory.Certificate.castWith?._unsafe_rec fuel n entries Γ right left.type + pure (Theory.Certified.ConversionWitness.proofIrrel left.type typeWitness left.witness rightWitness); + Theory.Certificate.eta?._unsafe_rec fuel n entries Γ a b <|> + (fun x => x.symm) <$> Theory.Certificate.eta?._unsafe_rec fuel n entries Γ b a <|> + congruence () <|> + match Theory.Certificate.equationStep?._unsafe_rec fuel n entries Γ a with + | some (a', w) => do + let __do_lift ← Theory.Certificate.conversion?._unsafe_rec fuel n entries Γ a' b + pure (Theory.Certified.ConversionWitness.trans a' w __do_lift) + | none => + match Theory.Certificate.equationStep?._unsafe_rec fuel n entries Γ b with + | some (b', w) => do + let __do_lift ← Theory.Certificate.conversion?._unsafe_rec fuel n entries Γ a b' + pure (Theory.Certified.ConversionWitness.trans b' __do_lift w.symm) + | none => proofIrrel () +RECURSION WORKER Ix.Theory.Certificate.declarationWitnesses?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Theory.Store β → + Theory.Model.Environment β → + List (Theory.Certificate.DeclarationSource β) → + Option (Theory.Model.Environment β × List (Theory.Certified.DeclarationWitness β)) +IMPLEMENTATION +fun {β} [DecidableEq β] [Theory.Certificate.Hints β] fuel store entries x => + match x with + | [] => some (entries, []) + | Theory.Certificate.DeclarationSource.definition r :: rest => do + let source ← store.lookup r + let witness ← Theory.Certificate.definitionWitness? fuel entries r source + let reading ← Theory.Certified.readDefinition? source witness.typeAnnotations witness.bodyAnnotations + let __x ← Theory.Certificate.declarationWitnesses?._unsafe_rec fuel store (entries.insert r reading.val.entry) rest + match __x with + | (entries', witnesses) => pure (entries', Theory.Certified.DeclarationWitness.definition witness :: witnesses) + | Theory.Certificate.DeclarationSource.ordinary source recursor :: rest => do + let witness ← Theory.Certificate.Ordinary.sourceBlock? fuel entries store source recursor + if Theory.Certificate.Hints.natural = some (Theory.ConstRef.member source 0) then + if (witness.shape.shape != Theory.Certified.Natural.shape) = true then none + else do + let __x ← + Theory.Certificate.declarationWitnesses?._unsafe_rec fuel store + (Theory.Certified.Natural.environment entries source recursor witness.mode) rest + match __x with + | (entries', witnesses) => pure (entries', Theory.Certified.DeclarationWitness.natural witness :: witnesses) + else + match Theory.Certificate.Structure.witness? fuel entries witness with + | some structured => do + let __x ← + Theory.Certificate.declarationWitnesses?._unsafe_rec fuel store + (structured.facts.description.publishedEnvironment entries source recursor witness.mode) rest + match __x with + | (entries', witnesses) => + pure (entries', Theory.Certified.DeclarationWitness.structure structured :: witnesses) + | none => do + let __x ← + Theory.Certificate.declarationWitnesses?._unsafe_rec fuel store + (witness.shape.shape.publishedEnvironment entries source recursor witness.mode) rest + match __x with + | (entries', witnesses) => + pure (entries', Theory.Certified.DeclarationWitness.ordinary witness :: witnesses) + | Theory.Certificate.DeclarationSource.standard ref :: rest => do + let source ← store.lookup ref + let witness ← Theory.Certificate.Standard.witness? fuel store entries ref source + let __x ← + Theory.Certificate.declarationWitnesses?._unsafe_rec fuel store (entries.insert ref witness.spec.entry) rest + match __x with + | (entries', witnesses) => pure (entries', Theory.Certified.DeclarationWitness.standard witness :: witnesses) + | Theory.Certificate.DeclarationSource.quotient refs :: rest => do + let witness ← Theory.Certificate.Quotient.witness? fuel entries refs + let __x ← Theory.Certificate.declarationWitnesses?._unsafe_rec fuel store (refs.environment entries) rest + match __x with + | (entries', witnesses) => pure (entries', Theory.Certified.DeclarationWitness.quotient witness :: witnesses) + | Theory.Certificate.DeclarationSource.modeled candidate :: rest => do + let witness ← candidate.witness? fuel entries store + let __x ← + Theory.Certificate.declarationWitnesses?._unsafe_rec fuel store + (Theory.Certified.Modeled.environment entries witness.companions) rest + match __x with + | (entries', witnesses) => pure (entries', Theory.Certified.DeclarationWitness.modeled witness :: witnesses) +RECURSION WORKER Ix.Theory.Certificate.dependencyOrder?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + Nat → + Theory.Certified.PrimitiveSignature β → + Theory.Store β → + List (Theory.ConstRef β) → + List (Theory.ConstRef β) → + List (Theory.ConstRef β) → + optParam (List (Theory.Certificate.DeclarationSource β)) [] → + optParam (List (Theory.Certificate.Modeled.Candidate β)) [] → + Option (List (Theory.ConstRef β) × List (Theory.Certificate.DeclarationSource β)) +IMPLEMENTATION +fun {β} [DecidableEq β] fuel signature store pending visited active order models => + match fuel, pending with + | x, [] => some (visited, order) + | 0, head :: tail => none + | fuel.succ, r :: rest => + if r = signature.falseType ∨ r = signature.falseElim ∨ r ∈ visited then + Theory.Certificate.dependencyOrder?._unsafe_rec fuel signature store rest visited active order models + else + if r ∈ active then none + else do + let group ← Theory.Certificate.sourceGroup? store r signature.natType models + if (group.references.any fun x => active.contains x) = true then none + else do + let __x ← + Theory.Certificate.dependencyOrder?._unsafe_rec fuel signature store group.dependencies visited + (group.references ++ active) order models + match __x with + | (visited, order) => + Theory.Certificate.dependencyOrder?._unsafe_rec fuel signature store rest (visited ++ group.references) + active (order ++ [group.declaration]) models +RECURSION WORKER Ix.Theory.Certificate.equationStep?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Nat → + Theory.Model.Environment β → + Theory.Model.Context β → + Theory.Model.AExpr β → Option (Theory.Model.AExpr β × Theory.Certified.ConversionWitness β) +IMPLEMENTATION +fun {β} [DecidableEq β] [Theory.Certificate.Hints β] fuel n entries Γ e => + match fuel with + | 0 => none + | fuel.succ => + have __do_jp := fun __x => + match __x with + | (ref, levels) => do + let entry ← entries ref + if (levels.length != entry.universes) = true then none + else + List.findSome? + (fun x => + match x with + | (law, i) => + let lhs := Theory.Model.AExpr.instL levels law.lhs; + let rhs := Theory.Model.AExpr.instL levels law.rhs; + do + let args ← + Theory.Certificate.equationArguments? lhs e <|> + (match Theory.Certificate.lambdaBody lhs with + | (fst, fn.app constructor) => do + let __x ← Theory.Certificate.headConstant? constructor + match __x with + | (Theory.ConstRef.ctor b i c, snd) => + match e with + | fn.app major => do + let expanded ← Theory.Certificate.etaMajor?._unsafe_rec fuel n entries Γ major + Theory.Certificate.equationArguments? lhs (fn.app expanded) + | x => none + | x => none + | x => none) <|> + match e with + | fn.app major => do + let typed ← Theory.Certificate.inferAnnotated?._unsafe_rec fuel n entries Γ major + let type ← Theory.Certificate.inferAnnotated?._unsafe_rec fuel n entries Γ typed.type + let _ ← Theory.Certificate.cast? n type (Theory.Model.AExpr.sort Theory.VLevel.zero) + Theory.Certificate.equationProofArguments? lhs e + | x => none + let lhsApplied : Theory.Model.AExpr β := List.foldl Theory.Model.AExpr.app lhs args + let rhsApplied : Theory.Model.AExpr β := List.foldl Theory.Model.AExpr.app rhs args + let beta ← Theory.Certificate.conversion?._unsafe_rec fuel n entries Γ lhsApplied e + let equation : Theory.Certified.ConversionWitness β := + List.foldl (fun w x => w.app Theory.Certified.ConversionWitness.refl) + (Theory.Certified.ConversionWitness.equation ref i levels) args + pure (rhsApplied, Theory.Certified.ConversionWitness.trans lhsApplied beta.symm equation)) + entry.equations.zipIdx; + match e with + | Theory.Model.AExpr.proj ref field major => do + let typed ← Theory.Certificate.inferAnnotated?._unsafe_rec fuel n entries Γ major + let typed ← Theory.Certificate.normalizeType?._unsafe_rec fuel n entries Γ typed + let __x ← Theory.Certificate.headConstant? typed.type + match __x with + | (owner, levels) => + if owner = ref then do + let __x ← some (ref, levels) + __do_jp __x + else do + let __x ← none + __do_jp __x + | x => do + let __x ← Theory.Certificate.headConstant? e + match __x with + | (ref, levels) => + let owner := + match ref with + | Theory.ConstRef.ctor source member c => Theory.ConstRef.member source member + | x => ref; + do + let __x ← some (owner, levels) + __do_jp __x +RECURSION WORKER Ix.Theory.Certificate.eta?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Nat → + Theory.Model.Environment β → + Theory.Model.Context β → + Theory.Model.AExpr β → Theory.Model.AExpr β → Option (Theory.Certified.ConversionWitness β) +IMPLEMENTATION +fun {β} [DecidableEq β] [Theory.Certificate.Hints β] fuel n entries Γ a b => + match fuel with + | 0 => none + | fuel.succ => + match a with + | Theory.Model.AExpr.lam p D body => do + let fn ← Theory.Certificate.inferAnnotated?._unsafe_rec fuel n entries Γ b + let fn ← Theory.Certificate.normalizeType?._unsafe_rec fuel n entries Γ fn + match fn.type with + | Theory.Model.AExpr.forallE q D' B => + if p = q ∧ D = D' ∧ body = (Theory.Model.AExpr.liftN 1 b).app (Theory.Model.AExpr.bvar 0) then + pure (Theory.Certified.ConversionWitness.eta B fn.witness) + else none + | x => none + | x => none +RECURSION WORKER Ix.Theory.Certificate.etaMajor?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Nat → Theory.Model.Environment β → Theory.Model.Context β → Theory.Model.AExpr β → Option (Theory.Model.AExpr β) +IMPLEMENTATION +fun {β} [DecidableEq β] [Theory.Certificate.Hints β] fuel n entries Γ major => + match fuel with + | 0 => none + | fuel.succ => do + let typed ← Theory.Certificate.inferAnnotated?._unsafe_rec fuel n entries Γ major + let typed ← Theory.Certificate.normalizeType?._unsafe_rec fuel n entries Γ typed + let __x ← Theory.Certificate.headConstant? typed.type + match __x with + | (family, levels) => do + let entry ← entries family + if (levels.length != entry.universes) = true then none + else + List.findSome? + (fun law => + let arguments := Theory.Certificate.applicationArgs typed.type [] ++ [major]; + do + let rhs ← Theory.Certificate.instantiateLambdas? (Theory.Model.AExpr.instL levels law.rhs) arguments + if (rhs != major) = true then none + else do + let lhs ← Theory.Certificate.instantiateLambdas? (Theory.Model.AExpr.instL levels law.lhs) arguments + if lhs = major then none else some lhs) + entry.equations +RECURSION WORKER Ix.Theory.Certificate.frontierWitnesses?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Theory.Store β → + Theory.Model.Environment β → + List (Theory.ConstRef β) → Option (Theory.Model.Environment β × List (Theory.Certified.FrontierWitness β)) +IMPLEMENTATION +fun {β} [DecidableEq β] [Theory.Certificate.Hints β] fuel store entries x => + match x with + | [] => some (entries, []) + | ref :: refs => + match store.uvars ref, store.type ref with + | some universes, some type => do + let inferred ← Theory.Certificate.inferSource? fuel universes entries [] type + match inferred.type with + | Theory.Model.AExpr.sort level => + let witness := + { ref := ref, annotations := Theory.Certificate.annotations inferred.expression, level := level, + typing := inferred.witness }; + do + let reading ← Theory.Certified.readFrontierHeader? store witness + let __x ← + Theory.Certificate.frontierWitnesses?._unsafe_rec fuel store (entries.insert ref reading.header.entry) refs + match __x with + | (entries', rest) => pure (entries', witness :: rest) + | x => none + | x, x_1 => none +RECURSION WORKER Ix.Theory.Certificate.hasLiteral._unsafe_rec +{β : Type u} → Theory.VExpr β → Bool +IMPLEMENTATION +fun {β} x => + match x with + | Theory.VExpr.natLit value => true + | a.app b => Theory.Certificate.hasLiteral._unsafe_rec a || Theory.Certificate.hasLiteral._unsafe_rec b + | a.lam b => Theory.Certificate.hasLiteral._unsafe_rec a || Theory.Certificate.hasLiteral._unsafe_rec b + | a.forallE b => Theory.Certificate.hasLiteral._unsafe_rec a || Theory.Certificate.hasLiteral._unsafe_rec b + | Theory.VExpr.proj ref index e => Theory.Certificate.hasLiteral._unsafe_rec e + | x => false +RECURSION WORKER Ix.Theory.Certificate.headConstant?._unsafe_rec +{β : Type u} → Theory.Model.AExpr β → Option (Theory.ConstRef β × List Theory.VLevel) +IMPLEMENTATION +fun {β} x => + match x with + | Theory.Model.AExpr.const r levels => some (r, levels) + | f.app arg => Theory.Certificate.headConstant?._unsafe_rec f + | x => none +RECURSION WORKER Ix.Theory.Certificate.headStep?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Nat → + Theory.Model.Environment β → + Theory.Model.Context β → + Theory.Model.AExpr β → Option (Theory.Model.AExpr β × Theory.Certified.ConversionWitness β) +IMPLEMENTATION +fun {β} [DecidableEq β] [Theory.Certificate.Hints β] fuel n entries Γ e => + match fuel with + | 0 => none + | fuel.succ => + let delta := fun x => + match e with + | Theory.Model.AExpr.const r ls => do + let entry ← entries r + if (ls.length != entry.universes) = true then none + else do + let body ← entry.body + pure (Theory.Model.AExpr.instL ls body, Theory.Certified.ConversionWitness.delta) + | x => none; + let app := fun x => + match e with + | f.app x => do + let __x ← + Theory.Certificate.headStep?._unsafe_rec fuel n entries Γ f <|> + Theory.Certificate.equationStep?._unsafe_rec fuel n entries Γ f + match __x with + | (f', w) => pure (f'.app x, w.app Theory.Certified.ConversionWitness.refl) + | x => none; + let argument := fun x => + match e with + | f.app x => do + let __x ← + Theory.Certificate.headStep?._unsafe_rec fuel n entries Γ x <|> + Theory.Certificate.equationStep?._unsafe_rec fuel n entries Γ x + match __x with + | (x', w) => pure (f.app x', Theory.Certified.ConversionWitness.refl.app w) + | x => none; + let projection := fun x => + match e with + | Theory.Model.AExpr.proj r i x => do + let __x ← + Theory.Certificate.headStep?._unsafe_rec fuel n entries Γ x <|> + Theory.Certificate.equationStep?._unsafe_rec fuel n entries Γ x + match __x with + | (x', w) => pure (Theory.Model.AExpr.proj r i x', w.proj) + | x => none; + let literal := fun x => + match e with + | Theory.Model.AExpr.natLit value => do + let ref ← Theory.Certificate.Hints.natural + let entry ← entries ref + if (entry.universes != 0) = true then none + else + List.findSome? + (fun x => + match x with + | (fact, i) => + match fact with + | Theory.Model.ConstantFact.natural zero succ => + some + (match value with + | 0 => Theory.Model.AExpr.const zero [] + | n.succ => (Theory.Model.AExpr.const succ []).app (Theory.Model.AExpr.natLit n), + Theory.Certified.ConversionWitness.natLiteral ref i) + | x => none) + entry.facts.zipIdx + | x => none; + Theory.Certificate.beta?._unsafe_rec fuel n entries Γ e <|> + delta () <|> app () <|> argument () <|> projection () <|> literal () +RECURSION WORKER Ix.Theory.Certificate.inferAnnotated?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Nat → + Theory.Model.Environment β → + Theory.Model.Context β → Theory.Model.AExpr β → Option (Theory.Certificate.Suggestion β) +IMPLEMENTATION +fun {β} [DecidableEq β] [Theory.Certificate.Hints β] fuel n entries Γ e => + match fuel with + | 0 => none + | fuel.succ => do + let result ← Theory.Certificate.inferSource?._unsafe_rec fuel n entries Γ e.erase + if result.expression = e then some result else none +RECURSION WORKER Ix.Theory.Certificate.inferSource?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Nat → + Theory.Model.Environment β → + Theory.Model.Context β → Theory.VExpr β → Option (Theory.Certificate.Suggestion β) +IMPLEMENTATION +fun {β} [inst : DecidableEq β] [Theory.Certificate.Hints β] fuel n entries Γ => + match (motive := Nat → Theory.VExpr β → Option (Theory.Certificate.Suggestion β)) fuel with + | 0 => fun x => none + | fuel.succ => fun source => + match source with + | Theory.VExpr.bvar i => do + let A ← Γ[i]? + pure { expression := Theory.Model.AExpr.bvar i, type := A, witness := Theory.Certified.TypingWitness.bvar } + | Theory.VExpr.sort l => + if Theory.VLevel.WF n l then + some + { expression := Theory.Model.AExpr.sort l, type := Theory.Model.AExpr.sort l.succ, + witness := Theory.Certified.TypingWitness.sort } + else none + | Theory.VExpr.const r ls => do + let entry ← entries r + if ls.length = entry.universes ∧ ∀ (l : Theory.VLevel), l ∈ ls → Theory.VLevel.WF n l then + pure + { expression := Theory.Model.AExpr.const r ls, type := Theory.Model.AExpr.instL ls entry.type, + witness := Theory.Certified.TypingWitness.const } + else none + | A.forallE B => do + let a ← Theory.Certificate.inferSource?._unsafe_rec fuel n entries Γ A + let a ← Theory.Certificate.normalizeType?._unsafe_rec fuel n entries Γ a + match a.type with + | Theory.Model.AExpr.sort lA => do + let b ← + Theory.Certificate.inferSource?._unsafe_rec fuel n entries (Theory.Model.Context.push a.expression Γ) B + let b ← + Theory.Certificate.normalizeType?._unsafe_rec fuel n entries (Theory.Model.Context.push a.expression Γ) b + match b.type with + | Theory.Model.AExpr.sort lB => + pure + { + expression := + Theory.Model.AExpr.forallE (Theory.Certified.zeroCondition lB) a.expression b.expression, + type := Theory.Model.AExpr.sort (lA.imax lB), + witness := Theory.Certified.TypingWitness.forallE lA lB a.witness b.witness } + | x => none + | x => none + | A.lam body => do + let a ← Theory.Certificate.inferSource?._unsafe_rec fuel n entries Γ A + let a ← Theory.Certificate.normalizeType?._unsafe_rec fuel n entries Γ a + match a.type with + | Theory.Model.AExpr.sort lA => do + let b ← + Theory.Certificate.inferSource?._unsafe_rec fuel n entries (Theory.Model.Context.push a.expression Γ) body + let b ← + Theory.Certificate.normalizeType?._unsafe_rec fuel n entries (Theory.Model.Context.push a.expression Γ) b + let B ← + Theory.Certificate.inferAnnotated?._unsafe_rec fuel n entries (Theory.Model.Context.push a.expression Γ) + b.type + let B ← + Theory.Certificate.normalizeType?._unsafe_rec fuel n entries (Theory.Model.Context.push a.expression Γ) B + match B.type with + | Theory.Model.AExpr.sort lB => + pure + { expression := Theory.Model.AExpr.lam (Theory.Certified.zeroCondition lB) a.expression b.expression, + type := Theory.Model.AExpr.forallE (Theory.Certified.zeroCondition lB) a.expression b.type, + witness := Theory.Certified.TypingWitness.lam lA lB b.type a.witness B.witness b.witness } + | x => none + | x => none + | f.app arg => do + let fn ← Theory.Certificate.inferSource?._unsafe_rec fuel n entries Γ f + let fn ← Theory.Certificate.normalizeType?._unsafe_rec fuel n entries Γ fn + match fn.type with + | Theory.Model.AExpr.forallE p A B => do + let a ← Theory.Certificate.inferSource?._unsafe_rec fuel n entries Γ arg + let aw ← Theory.Certificate.castWith?._unsafe_rec fuel n entries Γ a A + pure + { expression := fn.expression.app a.expression, type := B.inst a.expression, + witness := Theory.Certified.TypingWitness.app p A B fn.witness aw } + | x => none + | Theory.VExpr.proj ref index major => do + let major ← Theory.Certificate.inferSource?._unsafe_rec fuel n entries Γ major + let typed ← Theory.Certificate.normalizeType?._unsafe_rec fuel n entries Γ major + let __x ← Theory.Certificate.headConstant? typed.type + match __x with + | (owner, ls) => + if (owner != ref) = true then none + else do + let entry ← entries ref + if (ls.length != entry.universes) = true then none + else + List.findSome? + (fun x => + match x with + | (fact, factIndex) => + match fact with + | Theory.Model.ConstantFact.typed expression type => + match Theory.Certificate.lambdaBody expression with + | (fst, Theory.Model.AExpr.proj family field major_1) => + if (family != ref || field != index) = true then none + else do + let result ← + Theory.Certificate.applyFact?._unsafe_rec fuel n entries Γ + { expression := Theory.Model.AExpr.instL ls expression, + type := Theory.Model.AExpr.instL ls type, + witness := Theory.Certified.TypingWitness.fact ref factIndex ls } + (Theory.Certificate.applicationArgs typed.type [] ++ [major.expression]) + if result.expression = Theory.Model.AExpr.proj ref index major.expression then some result + else none + | x => none + | x => none) + entry.facts.zipIdx + | Theory.VExpr.natLit value => do + let ref ← Theory.Certificate.Hints.natural + let entry ← entries ref + if (entry.universes != 0) = true then none + else + List.findSome? + (fun x => + match x with + | (fact, i) => + match fact with + | Theory.Model.ConstantFact.natural zero succ => + some + { expression := Theory.Model.AExpr.natLit value, type := Theory.Model.AExpr.const ref [], + witness := Theory.Certified.TypingWitness.natLit ref i } + | x => none) + entry.facts.zipIdx +RECURSION WORKER Ix.Theory.Certificate.instantiateLambdas?._unsafe_rec +{β : Type u} → Theory.Model.AExpr β → List (Theory.Model.AExpr β) → Option (Theory.Model.AExpr β) +IMPLEMENTATION +fun {β} x x_1 => + match x, x_1 with + | e, [] => some e + | Theory.Model.AExpr.lam condition domain body, arg :: rest => + Theory.Certificate.instantiateLambdas?._unsafe_rec (body.inst arg) rest + | x, x_2 => none +RECURSION WORKER Ix.Theory.Certificate.lambdaBody._unsafe_rec +{β : Type u} → Theory.Model.AExpr β → Nat × Theory.Model.AExpr β +IMPLEMENTATION +fun {β} x => + match x with + | Theory.Model.AExpr.lam condition domain body => + match Theory.Certificate.lambdaBody._unsafe_rec body with + | (n, result) => (n + 1, result) + | e => (0, e) +RECURSION WORKER Ix.Theory.Certificate.matchEquationArgs?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + Theory.Model.AExpr β → + Theory.Model.AExpr β → List (Option (Theory.Model.AExpr β)) → Option (List (Option (Theory.Model.AExpr β))) +IMPLEMENTATION +fun {β} [DecidableEq β] x x_1 x_2 => + match x, x_1, x_2 with + | Theory.Model.AExpr.bvar i, actual, slots => do + let slot ← slots[i]? + match slot with + | none => some (slots.set i (some actual)) + | some previous => if previous = actual then some slots else none + | f.app x, g.app y, slots => do + let slots ← Theory.Certificate.matchEquationArgs?._unsafe_rec f g slots + Theory.Certificate.matchEquationArgs?._unsafe_rec x y slots + | Theory.Model.AExpr.const r ls, Theory.Model.AExpr.const q ms, slots => if r = q ∧ ls = ms then some slots else none + | Theory.Model.AExpr.sort l, Theory.Model.AExpr.sort m, slots => if l = m then some slots else none + | Theory.Model.AExpr.proj r i major, Theory.Model.AExpr.proj q j actual, slots => + if r = q ∧ i = j then Theory.Certificate.matchEquationArgs?._unsafe_rec major actual slots else none + | Theory.Model.AExpr.lam condition domain body, Theory.Model.AExpr.lam condition_1 domain_1 body_1, slots => + some slots + | Theory.Model.AExpr.forallE condition domain body, Theory.Model.AExpr.forallE condition_1 domain_1 body_1, slots => + some slots + | x, x_3, x_4 => none +RECURSION WORKER Ix.Theory.Certificate.normalizeType?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Nat → + Theory.Model.Environment β → + Theory.Model.Context β → Theory.Certificate.Suggestion β → Option (Theory.Certificate.Suggestion β) +IMPLEMENTATION +fun {β} [DecidableEq β] [Theory.Certificate.Hints β] fuel n entries Γ suggestion => + match fuel with + | 0 => none + | fuel.succ => + match suggestion.type with + | Theory.Model.AExpr.sort level => some suggestion + | Theory.Model.AExpr.forallE condition domain body => some suggestion + | x => + match + Theory.Certificate.headStep?._unsafe_rec fuel n entries Γ suggestion.type <|> + Theory.Certificate.equationStep?._unsafe_rec fuel n entries Γ suggestion.type with + | none => some suggestion + | some (type, conversion) => do + let formed ← Theory.Certificate.inferAnnotated?._unsafe_rec fuel n entries Γ type + let formed ← Theory.Certificate.normalizeType?._unsafe_rec fuel n entries Γ formed + match formed.type with + | Theory.Model.AExpr.sort level => + Theory.Certificate.normalizeType?._unsafe_rec fuel n entries Γ + { expression := suggestion.expression, type := type, + witness := + Theory.Certified.TypingWitness.conv suggestion.type level suggestion.witness formed.witness + conversion } + | x => none +RECURSION WORKER Ix.Theory.Certificate.recursorFamily?._unsafe_rec +{β : Type u} → Theory.VExpr β → Option β +IMPLEMENTATION +fun {β} x => + match x with + | binderType.forallE (A.forallE B) => Theory.Certificate.recursorFamily?._unsafe_rec (A.forallE B) + | A.forallE body => + match A.appHead with + | Theory.VExpr.const (Theory.ConstRef.member source 0) levels => some source + | x => none + | x => none +RECURSION WORKER Ix.Theory.Certified.checkDeclarationExtensions?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + Nat → + {signature : Theory.Certified.PrimitiveSignature β} → + {store : Theory.Store β} → + (state : Theory.Certified.CheckedInterface signature) → + List (Theory.Certified.DeclarationWitness β) → + Option (Theory.Certified.CheckedExtension signature store state) +IMPLEMENTATION +fun {β} [DecidableEq β] fuel {signature} {store} state x => + match x with + | [] => some (Theory.Certified.CheckedExtension.refl state) + | witness :: rest => do + let step ← Theory.Certified.checkDeclarationExtension? fuel state witness + let rest ← Theory.Certified.checkDeclarationExtensions?._unsafe_rec fuel step.result rest + pure (step.trans rest) +RECURSION WORKER Ix.Theory.Certified.replayDeclarations?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + Nat → + {signature : Theory.Certified.PrimitiveSignature β} → + {store : Theory.Store β} → + (state : Theory.Certified.CheckedInterface signature) → + List (Theory.Certified.DeclarationWitness β) → + Option (Theory.Certified.CheckedExtension signature store state) +IMPLEMENTATION +fun {β} [DecidableEq β] fuel {signature} {store} state x => + match x with + | [] => some (Theory.Certified.CheckedExtension.refl state) + | witness :: rest => + have __do_jp := fun step => do + let rest ← Theory.Certified.replayDeclarations?._unsafe_rec fuel step.result rest + pure (step.trans rest); + if (witness.outputs.all fun ref => (state.entries ref).isSome) = true then do + let step ← some (Theory.Certified.CheckedExtension.refl state) + __do_jp step + else do + let step ← Theory.Certified.checkDeclarationExtension? fuel state witness + __do_jp step +RECURSION WORKER Ix.Theory.Certified.replayNodes?._unsafe_rec +{β : Type u} → + [inst : DecidableEq β] → + {fuel : Nat} → + {signature : Theory.Certified.PrimitiveSignature β} → + {store : Theory.Store β} → + (state : Theory.Certified.CheckedInterface signature) → + (known : List (Theory.ConstRef β)) → + (nodes : List (Theory.Certified.CheckedNode fuel signature store)) → + Option (Theory.Certified.ReplayResult state known nodes) +IMPLEMENTATION +fun {β} [DecidableEq β] {fuel} {signature} {store} state known x => + match x with + | [] => some { checked := Theory.Certified.CheckedExtension.refl state, order := ⋯, subjects := ⋯, frontiers := ⋯ } + | node :: nodes => + if available : (node.receipt.frontier.refs.all fun x => known.contains x) = true then + if aligned : + (node.receipt.frontier.headers.all fun h => decide (Theory.Certified.HeaderPresent state.entries h)) = + true then + do + let step ← Theory.Certified.replayDeclarations? fuel state node.input.declarations + if present : (node.input.subjects.all fun ref => (step.result.entries ref).isSome) = true then do + let tail ← Theory.Certified.replayNodes?._unsafe_rec step.result (known ++ node.input.subjects) nodes + pure { checked := step.trans tail.checked, order := ⋯, subjects := ⋯, frontiers := ⋯ } + else none + else none + else none +RECURSION WORKER Ix.Theory.Certified.verifyArguments._unsafe_rec +{β : Type u} → + [DecidableEq β] → + Nat → + Nat → + (entries : Theory.Model.Environment β) → + (Γ : Theory.Model.Context β) → + (domains args : List (Theory.Model.AExpr β)) → + List (Theory.Certified.TypingWitness β) → + Option (Theory.Certified.CheckedClaim (Theory.Certified.ArgumentsFit entries Γ domains args)) +IMPLEMENTATION +fun {β} [DecidableEq β] fuel n entries Γ x x_1 x_2 => + match x, x_1, x_2 with + | [], [], [] => some { down := ⋯ } + | A :: domains, a :: args, witness :: witnesses => do + let ha ← Theory.Certified.verifyType fuel n entries Γ a A witness + let hrest ← + Theory.Certified.verifyArguments._unsafe_rec fuel n entries Γ (Theory.Model.Telescope.inst a domains) args + witnesses + pure { down := ⋯ } + | x, x_3, x_4 => none +RECURSION WORKER Ix.Theory.Certified.verifyConversion._unsafe_rec +{β : Type u} → + [DecidableEq β] → + Nat → + Nat → + (entries : Theory.Model.Environment β) → + (Γ : Theory.Model.Context β) → + (a b : Theory.Model.AExpr β) → + Theory.Certified.ConversionWitness β → + Option (Theory.Certified.CheckedClaim (Theory.Model.ConversionClaim entries Γ a b)) +IMPLEMENTATION +fun {β} [inst : DecidableEq β] fuel n entries Γ a b witness => + match fuel with + | 0 => none + | fuel.succ => + if Theory.Model.AExpr.Scope n (List.length Γ) a ∧ Theory.Model.AExpr.Scope n (List.length Γ) b then + match a, b, witness with + | a, b, Theory.Certified.ConversionWitness.refl => if h : a = b then some { down := ⋯ } else none + | a, b, w.symm => do + let h ← Theory.Certified.verifyConversion._unsafe_rec fuel n entries Γ b a w + pure { down := ⋯ } + | a, b, Theory.Certified.ConversionWitness.trans c w₁ w₂ => do + let h₁ ← Theory.Certified.verifyConversion._unsafe_rec fuel n entries Γ a c w₁ + let h₂ ← Theory.Certified.verifyConversion._unsafe_rec fuel n entries Γ c b w₂ + pure { down := ⋯ } + | f.app a, f'.app a', wf.app wa => do + let hf ← Theory.Certified.verifyConversion._unsafe_rec fuel n entries Γ f f' wf + let ha ← Theory.Certified.verifyConversion._unsafe_rec fuel n entries Γ a a' wa + pure { down := ⋯ } + | Theory.Model.AExpr.proj r i a, Theory.Model.AExpr.proj r' i' b, w.proj => + if hr : r = r' then + if hi : i = i' then do + let h ← Theory.Certified.verifyConversion._unsafe_rec fuel n entries Γ a b w + pure { down := ⋯ } + else none + else none + | Theory.Model.AExpr.lam p D body, Theory.Model.AExpr.lam p' D' body', + Theory.Certified.ConversionWitness.lam lD wD wd wb => + if hp : p = p' then do + let hD ← Theory.Certified.verifyType._unsafe_rec fuel n entries Γ D (Theory.Model.AExpr.sort lD) wD + let hd ← Theory.Certified.verifyConversion._unsafe_rec fuel n entries Γ D D' wd + let hb ← + Theory.Certified.verifyConversion._unsafe_rec fuel n entries (Theory.Model.Context.push D Γ) body body' wb + pure { down := ⋯ } + else none + | Theory.Model.AExpr.forallE p D B, Theory.Model.AExpr.forallE p' D' B', + Theory.Certified.ConversionWitness.forallE lD wD wd wb => + if hp : p = p' then do + let hD ← Theory.Certified.verifyType._unsafe_rec fuel n entries Γ D (Theory.Model.AExpr.sort lD) wD + let hd ← Theory.Certified.verifyConversion._unsafe_rec fuel n entries Γ D D' wd + let hb ← Theory.Certified.verifyConversion._unsafe_rec fuel n entries (Theory.Model.Context.push D Γ) B B' wb + pure { down := ⋯ } + else none + | (Theory.Model.AExpr.lam p D body).app arg, result, Theory.Certified.ConversionWitness.beta T wl wa => + if hr : result = body.inst arg then do + let hl ← Theory.Certified.verifyType._unsafe_rec fuel n entries Γ (Theory.Model.AExpr.lam p D body) T wl + let ha ← Theory.Certified.verifyType._unsafe_rec fuel n entries Γ arg D wa + pure { down := ⋯ } + else none + | Theory.Model.AExpr.lam p D body, f, Theory.Certified.ConversionWitness.eta B wf => + if hb : body = (Theory.Model.AExpr.liftN 1 f).app (Theory.Model.AExpr.bvar 0) then do + let hf ← Theory.Certified.verifyType._unsafe_rec fuel n entries Γ f (Theory.Model.AExpr.forallE p D B) wf + pure { down := ⋯ } + else none + | a, b, Theory.Certified.ConversionWitness.proofIrrel A wA wa wb => do + let hA ← + Theory.Certified.verifyType._unsafe_rec fuel n entries Γ A (Theory.Model.AExpr.sort Theory.VLevel.zero) wA + let ha ← Theory.Certified.verifyType._unsafe_rec fuel n entries Γ a A wa + let hb ← Theory.Certified.verifyType._unsafe_rec fuel n entries Γ b A wb + pure { down := ⋯ } + | Theory.Model.AExpr.const r ls, b, Theory.Certified.ConversionWitness.delta => + match h : entries r with + | none => none + | some entry => + match h_1 : entry.body with + | none => none + | some body => + if hn : ls.length = entry.universes then + if hB : b = Theory.Model.AExpr.instL ls body then some { down := ⋯ } else none + else none + | Theory.Model.AExpr.sort l, Theory.Model.AExpr.sort l', Theory.Certified.ConversionWitness.sort => + if h : Theory.Certified.LevelEq.check n l l' = true then some { down := ⋯ } else none + | a, b, Theory.Certified.ConversionWitness.equation r index ls => + match h : entries r with + | none => none + | some entry => + match he : entry.equations[index]? with + | none => none + | some law => + if hn : ls.length = entry.universes then + if hls : ∀ (l : Theory.VLevel), l ∈ ls → Theory.VLevel.WF n l then + if ha : a = Theory.Model.AExpr.instL ls law.lhs then + if hb : b = Theory.Model.AExpr.instL ls law.rhs then some { down := ⋯ } else none + else none + else none + else none + | Theory.Model.AExpr.natLit value, result, Theory.Certified.ConversionWitness.natLiteral r index => + match h : entries r with + | none => none + | some entry => + match hf : entry.facts[index]? with + | some (Theory.Model.ConstantFact.natural zero succ) => + if hn : entry.universes = 0 then + match value with + | 0 => if he : result = Theory.Model.AExpr.const zero [] then some { down := ⋯ } else none + | value.succ => + if he : result = (Theory.Model.AExpr.const succ []).app (Theory.Model.AExpr.natLit value) then + some { down := ⋯ } + else none + else none + | x => none + | x, x_1, x_2 => none + else none +RECURSION WORKER Ix.Theory.Certified.verifyPropTelescope._unsafe_rec +{β : Type u} → + [DecidableEq β] → + Nat → + Nat → + (entries : Theory.Model.Environment β) → + (atLevel : Theory.VLevel) → + (Γ : Theory.Model.Context β) → + (domains : List (Theory.Model.AExpr β)) → + List (Theory.Certified.DomainWitness β) → + Option (Theory.Certified.CheckedClaim (Theory.Certified.TelescopeProp entries Γ domains atLevel)) +IMPLEMENTATION +fun {β} [DecidableEq β] fuel n entries atLevel x x_1 x_2 => + match x, x_1, x_2 with + | Γ, [], [] => some { down := ⋯ } + | Γ, A :: rest, witness :: witnesses => + if hl : Theory.Certified.checkZeroImplies atLevel witness.level = true then do + let hA ← Theory.Certified.verifyType fuel n entries Γ A (Theory.Model.AExpr.sort witness.level) witness.typing + let htail ← + Theory.Certified.verifyPropTelescope._unsafe_rec fuel n entries atLevel (Theory.Model.Context.push A Γ) rest + witnesses + pure { down := ⋯ } + else none + | x, x_3, x_4 => none +RECURSION WORKER Ix.Theory.Certified.verifyTelescope._unsafe_rec +{β : Type u} → + [DecidableEq β] → + Nat → + Nat → + (entries : Theory.Model.Environment β) → + (bound : Option Theory.VLevel) → + (Γ : Theory.Model.Context β) → + (domains : List (Theory.Model.AExpr β)) → + List (Theory.Certified.DomainWitness β) → + Option + (Theory.Certified.CheckedClaim + (Theory.Model.Telescope.Formed entries Γ domains ∧ + Theory.Certified.TelescopeBound entries Γ domains bound)) +IMPLEMENTATION +fun {β} [DecidableEq β] fuel n entries bound x x_1 x_2 => + match x, x_1, x_2 with + | Γ, [], [] => some { down := ⋯ } + | Γ, A :: rest, witness :: witnesses => + if hl : Theory.Certified.checkDomainBound n bound witness.level = true then do + let hA ← Theory.Certified.verifyType fuel n entries Γ A (Theory.Model.AExpr.sort witness.level) witness.typing + let hrest ← + Theory.Certified.verifyTelescope._unsafe_rec fuel n entries bound (Theory.Model.Context.push A Γ) rest witnesses + pure { down := ⋯ } + else none + | x, x_3, x_4 => none +RECURSION WORKER Ix.Theory.Certified.verifyType._unsafe_rec +{β : Type u} → + [DecidableEq β] → + Nat → + Nat → + (entries : Theory.Model.Environment β) → + (Γ : Theory.Model.Context β) → + (e A : Theory.Model.AExpr β) → + Theory.Certified.TypingWitness β → + Option (Theory.Certified.CheckedClaim (Theory.Model.TypingClaim entries Γ e A)) +IMPLEMENTATION +fun {β} [inst : DecidableEq β] fuel n entries Γ e A witness => + match fuel with + | 0 => none + | fuel.succ => + if Theory.Model.AExpr.Scope n (List.length Γ) e ∧ Theory.Model.AExpr.Scope n (List.length Γ) A then + match e, witness with + | Theory.Model.AExpr.sort l, Theory.Certified.TypingWitness.sort => + if h : A = Theory.Model.AExpr.sort l.succ then some { down := ⋯ } else none + | Theory.Model.AExpr.bvar i, Theory.Certified.TypingWitness.bvar => + match h : Γ[i]? with + | none => none + | some B => if hA : A = B then some { down := ⋯ } else none + | Theory.Model.AExpr.const r ls, Theory.Certified.TypingWitness.const => + match h : entries r with + | none => none + | some entry => + if hn : ls.length = entry.universes then + if hA : A = Theory.Model.AExpr.instL ls entry.type then some { down := ⋯ } else none + else none + | f.app a, Theory.Certified.TypingWitness.app p D B wf wa => + if hA : A = B.inst a then do + let hf ← Theory.Certified.verifyType._unsafe_rec fuel n entries Γ f (Theory.Model.AExpr.forallE p D B) wf + let ha ← Theory.Certified.verifyType._unsafe_rec fuel n entries Γ a D wa + pure { down := ⋯ } + else none + | Theory.Model.AExpr.lam p D body, Theory.Certified.TypingWitness.lam lD lB B wD wB wb => + if hp : p = Theory.Certified.zeroCondition lB then + if hA : A = Theory.Model.AExpr.forallE p D B then do + let hD ← Theory.Certified.verifyType._unsafe_rec fuel n entries Γ D (Theory.Model.AExpr.sort lD) wD + let hB ← + Theory.Certified.verifyType._unsafe_rec fuel n entries (Theory.Model.Context.push D Γ) B + (Theory.Model.AExpr.sort lB) wB + let hb ← Theory.Certified.verifyType._unsafe_rec fuel n entries (Theory.Model.Context.push D Γ) body B wb + pure { down := ⋯ } + else none + else none + | Theory.Model.AExpr.forallE p D B, Theory.Certified.TypingWitness.forallE lD lB wD wB => + if hp : p = Theory.Certified.zeroCondition lB then + if hA : A = Theory.Model.AExpr.sort (lD.imax lB) then do + let hD ← Theory.Certified.verifyType._unsafe_rec fuel n entries Γ D (Theory.Model.AExpr.sort lD) wD + let hB ← + Theory.Certified.verifyType._unsafe_rec fuel n entries (Theory.Model.Context.push D Γ) B + (Theory.Model.AExpr.sort lB) wB + pure { down := ⋯ } + else none + else none + | e, Theory.Certified.TypingWitness.conv B lA we wA wc => do + let he ← Theory.Certified.verifyType._unsafe_rec fuel n entries Γ e B we + let hA ← Theory.Certified.verifyType._unsafe_rec fuel n entries Γ A (Theory.Model.AExpr.sort lA) wA + let hc ← Theory.Certified.verifyConversion._unsafe_rec fuel n entries Γ B A wc + pure { down := ⋯ } + | e, Theory.Certified.TypingWitness.fact r index ls => + match h : entries r with + | none => none + | some entry => + match hf : entry.facts[index]? with + | some (Theory.Model.ConstantFact.typed value type) => + if hn : ls.length = entry.universes then + if ∀ (l : Theory.VLevel), l ∈ ls → Theory.VLevel.WF n l then + if he : e = Theory.Model.AExpr.instL ls value then + if hA : A = Theory.Model.AExpr.instL ls type then some { down := ⋯ } else none + else none + else none + else none + | x => none + | Theory.Model.AExpr.natLit value, Theory.Certified.TypingWitness.natLit r index => + match h : entries r with + | none => none + | some entry => + match hf : entry.facts[index]? with + | some (Theory.Model.ConstantFact.natural zero succ) => + if hn : entry.universes = 0 then if hA : A = Theory.Model.AExpr.const r [] then some { down := ⋯ } else none + else none + | x => none + | e, Theory.Certified.TypingWitness.betaResult p D body arg B wl wa => + if he : e = body.inst arg then + if hA : A = B.inst arg then do + let hl ← + Theory.Certified.verifyType._unsafe_rec fuel n entries Γ (Theory.Model.AExpr.lam p D body) + (Theory.Model.AExpr.forallE p D B) wl + let ha ← Theory.Certified.verifyType._unsafe_rec fuel n entries Γ arg D wa + pure { down := ⋯ } + else none + else none + | x, x_1 => none + else none +RECURSION WORKER Ix.Theory.Certified.zeroCondition._unsafe_rec +Theory.VLevel → Theory.Certified.PropWhen +IMPLEMENTATION +fun x => + match x with + | Theory.VLevel.zero => Theory.Certified.PropWhen.always + | a.succ => Theory.Certified.PropWhen.never + | a.max b => (Theory.Certified.zeroCondition._unsafe_rec a).inter (Theory.Certified.zeroCondition._unsafe_rec b) + | a.imax b => Theory.Certified.zeroCondition._unsafe_rec b + | Theory.VLevel.param i => Theory.Certified.PropWhen.param i +RECURSION WORKER Ix.Theory.Model.WellDenoted._unsafe_rec +{β : Type u} → + {V : Type v} → + [Theory.Model.SetTheory V] → Theory.Model.Assignment β V → List Nat → (Nat → V) → Theory.Model.AExpr β → Prop +IMPLEMENTATION +fun {β} {V} [Theory.Model.SetTheory V] constants levels env x => + match x with + | Theory.Model.AExpr.bvar index => True + | Theory.Model.AExpr.sort level => True + | Theory.Model.AExpr.const ref levels => True + | f.app a => + Theory.Model.WellDenoted._unsafe_rec constants levels env f ∧ + Theory.Model.WellDenoted._unsafe_rec constants levels env a ∧ + ∃ v A B, + Theory.Model.SetTheory.Mem (Theory.Model.interp constants levels env f) (Theory.Model.SetModel.piR v A B) ∧ + Theory.Model.SetTheory.Mem (Theory.Model.interp constants levels env a) A ∧ + ∀ (x : V), + Theory.Model.SetTheory.Mem x A → Theory.Model.SetTheory.Mem (B x) (Theory.Model.SetTheory.univ v) + | Theory.Model.AExpr.lam p A b => + Theory.Model.WellDenoted._unsafe_rec constants levels env A ∧ + (∀ (x : V), + Theory.Model.SetTheory.Mem x (Theory.Model.interp constants levels env A) → + Theory.Model.WellDenoted._unsafe_rec constants levels (Theory.Model.Valuation.cons x env) b) ∧ + ∃ v B, + (Theory.Model.regime p levels = 0 ↔ v = 0) ∧ + ∀ (x : V), + Theory.Model.SetTheory.Mem x (Theory.Model.interp constants levels env A) → + Theory.Model.SetTheory.Mem (Theory.Model.interp constants levels (Theory.Model.Valuation.cons x env) b) + (B x) ∧ + Theory.Model.SetTheory.Mem (B x) (Theory.Model.SetTheory.univ v) + | Theory.Model.AExpr.forallE p A B => + Theory.Model.WellDenoted._unsafe_rec constants levels env A ∧ + (∀ (x : V), + Theory.Model.SetTheory.Mem x (Theory.Model.interp constants levels env A) → + Theory.Model.WellDenoted._unsafe_rec constants levels (Theory.Model.Valuation.cons x env) B) ∧ + ∃ v, + (Theory.Model.regime p levels = 0 ↔ v = 0) ∧ + ∀ (x : V), + Theory.Model.SetTheory.Mem x (Theory.Model.interp constants levels env A) → + Theory.Model.SetTheory.Mem (Theory.Model.interp constants levels (Theory.Model.Valuation.cons x env) B) + (Theory.Model.SetTheory.univ v) + | Theory.Model.AExpr.proj ref field e => Theory.Model.WellDenoted._unsafe_rec constants levels env e + | Theory.Model.AExpr.natLit value => True +RECURSION WORKER Ix.Theory.Model.readAnnotations?._unsafe_rec +{β : Type u} → + (n k : Nat) → (source : Theory.VExpr β) → Theory.Model.AnnotationTree → Option (Theory.Model.Reading n k source) +IMPLEMENTATION +fun {β} n k source tree => + match source, tree with + | Theory.VExpr.bvar i, Theory.Model.AnnotationTree.leaf => + if h : i < k then some ⟨Theory.Model.AExpr.bvar i, ⋯⟩ else none + | Theory.VExpr.sort l, Theory.Model.AnnotationTree.leaf => + if h : Theory.VLevel.WF n l then some ⟨Theory.Model.AExpr.sort l, ⋯⟩ else none + | Theory.VExpr.const r ls, Theory.Model.AnnotationTree.leaf => + if h : ∀ (l : Theory.VLevel), l ∈ ls → Theory.VLevel.WF n l then some ⟨Theory.Model.AExpr.const r ls, ⋯⟩ else none + | f.app a, tf.app ta => do + let f' ← Theory.Model.readAnnotations?._unsafe_rec n k f tf + let a' ← Theory.Model.readAnnotations?._unsafe_rec n k a ta + pure ⟨f'.val.app a'.val, ⋯⟩ + | a.lam b, Theory.Model.AnnotationTree.lam raw ta tb => + match Ix.Theory.Model.readCondition?✝ n raw with + | none => none + | some p => do + let a' ← Theory.Model.readAnnotations?._unsafe_rec n k a ta + let b' ← Theory.Model.readAnnotations?._unsafe_rec n (k + 1) b tb + pure ⟨Theory.Model.AExpr.lam p.val a'.val b'.val, ⋯⟩ + | a.forallE b, Theory.Model.AnnotationTree.forallE raw ta tb => + match Ix.Theory.Model.readCondition?✝ n raw with + | none => none + | some p => do + let a' ← Theory.Model.readAnnotations?._unsafe_rec n k a ta + let b' ← Theory.Model.readAnnotations?._unsafe_rec n (k + 1) b tb + pure ⟨Theory.Model.AExpr.forallE p.val a'.val b'.val, ⋯⟩ + | Theory.VExpr.proj r i e, te.proj => do + let e' ← Theory.Model.readAnnotations?._unsafe_rec n k e te + pure ⟨Theory.Model.AExpr.proj r i e'.val, ⋯⟩ + | Theory.VExpr.natLit v, Theory.Model.AnnotationTree.leaf => some ⟨Theory.Model.AExpr.natLit v, ⋯⟩ + | x, x_1 => none +RECURSION WORKER Ix.Theory.VExpr.appArgs._unsafe_rec +{β : Type u_1} → Theory.VExpr β → List (Theory.VExpr β) → List (Theory.VExpr β) +IMPLEMENTATION +fun {β} x x_1 => + match x, x_1 with + | f.app a, acc => Theory.VExpr.appArgs._unsafe_rec f (a :: acc) + | x, acc => acc +RECURSION WORKER Ix.Theory.VExpr.appHead._unsafe_rec +{β : Type u_1} → Theory.VExpr β → Theory.VExpr β +IMPLEMENTATION +fun {β} x => + match x with + | f.app arg => Theory.VExpr.appHead._unsafe_rec f + | e => e +RECURSION WORKER Ix.Theory.VExpr.appN._unsafe_rec +{β : Type u_1} → Theory.VExpr β → List (Theory.VExpr β) → Theory.VExpr β +IMPLEMENTATION +fun {β} f x => + match x with + | [] => f + | a :: as => Theory.VExpr.appN._unsafe_rec (f.app a) as +RECURSION WORKER Ix.Theory.VExpr.bvarRevRange._unsafe_rec +{β : Type u_1} → Nat → Nat → List (Theory.VExpr β) +IMPLEMENTATION +fun {β} off x => + match x with + | 0 => [] + | m.succ => Theory.VExpr.bvar (off + m) :: Theory.VExpr.bvarRevRange._unsafe_rec off m +RECURSION WORKER Ix.Theory.VExpr.forallN._unsafe_rec +{β : Type u_1} → List (Theory.VExpr β) → Theory.VExpr β → Theory.VExpr β +IMPLEMENTATION +fun {β} x x_1 => + match x, x_1 with + | [], e => e + | A :: As, e => A.forallE (Theory.VExpr.forallN._unsafe_rec As e) +RECURSION WORKER Ix.Theory.VExpr.inst._unsafe_rec +{β : Type u_1} → Theory.VExpr β → Theory.VExpr β → optParam Nat 0 → Theory.VExpr β +IMPLEMENTATION +fun {β} x x_1 x_2 => + match x, x_1, x_2 with + | Theory.VExpr.bvar i, e, k => Theory.VExpr.instVar i e k + | Theory.VExpr.sort u, x, x_3 => Theory.VExpr.sort u + | Theory.VExpr.const r us, x, x_3 => Theory.VExpr.const r us + | fn.app arg, e, k => (Theory.VExpr.inst._unsafe_rec fn e k).app (Theory.VExpr.inst._unsafe_rec arg e k) + | ty.lam body, e, k => (Theory.VExpr.inst._unsafe_rec ty e k).lam (Theory.VExpr.inst._unsafe_rec body e (k + 1)) + | ty.forallE body, e, k => + (Theory.VExpr.inst._unsafe_rec ty e k).forallE (Theory.VExpr.inst._unsafe_rec body e (k + 1)) + | Theory.VExpr.proj r i p, e, k => Theory.VExpr.proj r i (Theory.VExpr.inst._unsafe_rec p e k) + | Theory.VExpr.natLit value, x, x_3 => Theory.VExpr.natLit value +RECURSION WORKER Ix.Theory.VExpr.instL._unsafe_rec +List Theory.VLevel → {β : Type u_1} → Theory.VExpr β → Theory.VExpr β +IMPLEMENTATION +fun levels {β} x => + match x with + | Theory.VExpr.bvar i => Theory.VExpr.bvar i + | Theory.VExpr.sort u => Theory.VExpr.sort (Theory.VLevel.inst levels u) + | Theory.VExpr.const r us => Theory.VExpr.const r (List.map (Theory.VLevel.inst levels) us) + | fn.app arg => (Theory.VExpr.instL._unsafe_rec levels fn).app (Theory.VExpr.instL._unsafe_rec levels arg) + | ty.lam body => (Theory.VExpr.instL._unsafe_rec levels ty).lam (Theory.VExpr.instL._unsafe_rec levels body) + | ty.forallE body => (Theory.VExpr.instL._unsafe_rec levels ty).forallE (Theory.VExpr.instL._unsafe_rec levels body) + | Theory.VExpr.proj r i e => Theory.VExpr.proj r i (Theory.VExpr.instL._unsafe_rec levels e) + | Theory.VExpr.natLit value => Theory.VExpr.natLit value +RECURSION WORKER Ix.Theory.VExpr.lamN._unsafe_rec +{β : Type u_1} → List (Theory.VExpr β) → Theory.VExpr β → Theory.VExpr β +IMPLEMENTATION +fun {β} x x_1 => + match x, x_1 with + | [], e => e + | A :: As, e => A.lam (Theory.VExpr.lamN._unsafe_rec As e) +RECURSION WORKER Ix.Theory.VExpr.liftN._unsafe_rec +Nat → {β : Type u_1} → Theory.VExpr β → optParam Nat 0 → Theory.VExpr β +IMPLEMENTATION +fun n {β} x x_1 => + match x, x_1 with + | Theory.VExpr.bvar i, k => Theory.VExpr.bvar (Theory.liftVar n i k) + | Theory.VExpr.sort u, x => Theory.VExpr.sort u + | Theory.VExpr.const r us, x => Theory.VExpr.const r us + | fn.app arg, k => (Theory.VExpr.liftN._unsafe_rec n fn k).app (Theory.VExpr.liftN._unsafe_rec n arg k) + | ty.lam body, k => (Theory.VExpr.liftN._unsafe_rec n ty k).lam (Theory.VExpr.liftN._unsafe_rec n body (k + 1)) + | ty.forallE body, k => + (Theory.VExpr.liftN._unsafe_rec n ty k).forallE (Theory.VExpr.liftN._unsafe_rec n body (k + 1)) + | Theory.VExpr.proj r i e, k => Theory.VExpr.proj r i (Theory.VExpr.liftN._unsafe_rec n e k) + | Theory.VExpr.natLit value, x => Theory.VExpr.natLit value +RECURSION WORKER Ix.Theory.VExpr.mapRefs._unsafe_rec +{β : Type u} → {γ : Type v} → (Theory.ConstRef β → Theory.ConstRef γ) → Theory.VExpr β → Theory.VExpr γ +IMPLEMENTATION +fun {β} {γ} mapping x => + match x with + | Theory.VExpr.bvar i => Theory.VExpr.bvar i + | Theory.VExpr.sort level => Theory.VExpr.sort level + | Theory.VExpr.const ref levels => Theory.VExpr.const (mapping ref) levels + | f.app a => (Theory.VExpr.mapRefs._unsafe_rec mapping f).app (Theory.VExpr.mapRefs._unsafe_rec mapping a) + | type.lam body => (Theory.VExpr.mapRefs._unsafe_rec mapping type).lam (Theory.VExpr.mapRefs._unsafe_rec mapping body) + | type.forallE body => + (Theory.VExpr.mapRefs._unsafe_rec mapping type).forallE (Theory.VExpr.mapRefs._unsafe_rec mapping body) + | Theory.VExpr.proj ref index major => + Theory.VExpr.proj (mapping ref) index (Theory.VExpr.mapRefs._unsafe_rec mapping major) + | Theory.VExpr.natLit n => Theory.VExpr.natLit n +RECURSION WORKER Ix.Theory.VExpr.refs._unsafe_rec +{β : Type u_1} → Theory.VExpr β → List (Theory.ConstRef β) +IMPLEMENTATION +fun {β} x => + match x with + | Theory.VExpr.bvar deBruijnIndex => [] + | Theory.VExpr.sort u => [] + | Theory.VExpr.natLit value => [] + | Theory.VExpr.const r levels => [r] + | e₁.app e₂ => Theory.VExpr.refs._unsafe_rec e₁ ++ Theory.VExpr.refs._unsafe_rec e₂ + | e₁.lam e₂ => Theory.VExpr.refs._unsafe_rec e₁ ++ Theory.VExpr.refs._unsafe_rec e₂ + | e₁.forallE e₂ => Theory.VExpr.refs._unsafe_rec e₁ ++ Theory.VExpr.refs._unsafe_rec e₂ + | Theory.VExpr.proj r index e => r :: Theory.VExpr.refs._unsafe_rec e +RECURSION WORKER Ix.Theory.VExpr.unliftN._unsafe_rec +{β : Type u_1} → Theory.VExpr β → Nat → Nat → Theory.VExpr β +IMPLEMENTATION +fun {β} e n k => + match n with + | 0 => e + | n.succ => Theory.VExpr.unliftN._unsafe_rec (e.inst default k) n k +RECURSION WORKER Ix.Theory.VLevel.WF._unsafe_rec +Nat → Theory.VLevel → Prop +IMPLEMENTATION +fun n x => + match x with + | Theory.VLevel.zero => True + | l.succ => Theory.VLevel.WF._unsafe_rec n l + | l₁.max l₂ => Theory.VLevel.WF._unsafe_rec n l₁ ∧ Theory.VLevel.WF._unsafe_rec n l₂ + | l₁.imax l₂ => Theory.VLevel.WF._unsafe_rec n l₁ ∧ Theory.VLevel.WF._unsafe_rec n l₂ + | Theory.VLevel.param i => i < n +RECURSION WORKER Ix.Theory.VLevel.decidable_WF._unsafe_rec +{n : Nat} → {l : Theory.VLevel} → Decidable (Theory.VLevel.WF n l) +IMPLEMENTATION +fun {n} x => + match x with + | Theory.VLevel.zero => instDecidableTrue + | l.succ => Theory.VLevel.decidable_WF._unsafe_rec + | a.max a_1 => instDecidableAnd + | a.imax a_1 => instDecidableAnd + | Theory.VLevel.param a => a.decLt n +RECURSION WORKER Ix.Theory.VLevel.eval._unsafe_rec +List Nat → Theory.VLevel → Nat +IMPLEMENTATION +fun ls x => + match x with + | Theory.VLevel.zero => 0 + | l.succ => Theory.VLevel.eval._unsafe_rec ls l + 1 + | l₁.max l₂ => (Theory.VLevel.eval._unsafe_rec ls l₁).max (Theory.VLevel.eval._unsafe_rec ls l₂) + | l₁.imax l₂ => Theory.VLevel.natIMax (Theory.VLevel.eval._unsafe_rec ls l₁) (Theory.VLevel.eval._unsafe_rec ls l₂) + | Theory.VLevel.param i => ls.getD i 0 +RECURSION WORKER Ix.Theory.VLevel.inst._unsafe_rec +List Theory.VLevel → Theory.VLevel → Theory.VLevel +IMPLEMENTATION +fun ls x => + match x with + | Theory.VLevel.zero => Theory.VLevel.zero + | l.succ => (Theory.VLevel.inst._unsafe_rec ls l).succ + | l₁.max l₂ => (Theory.VLevel.inst._unsafe_rec ls l₁).max (Theory.VLevel.inst._unsafe_rec ls l₂) + | l₁.imax l₂ => (Theory.VLevel.inst._unsafe_rec ls l₁).imax (Theory.VLevel.inst._unsafe_rec ls l₂) + | Theory.VLevel.param i => ls.getD i Theory.VLevel.zero +RECURSION WORKER Ix.Theory.instDecidableEqVExpr.decEq._unsafe_rec +{β : Type u_1} → [DecidableEq β] → (x x_1 : Theory.VExpr β) → Decidable (x = x_1) +IMPLEMENTATION +fun {β} [inst : DecidableEq β] x x_1 => + match x, x_1 with + | Theory.VExpr.bvar a, Theory.VExpr.bvar b => if h : a = b then h ▸ isTrue ⋯ else isFalse ⋯ + | Theory.VExpr.bvar deBruijnIndex, Theory.VExpr.sort u => isFalse ⋯ + | Theory.VExpr.bvar deBruijnIndex, Theory.VExpr.const ref levels => isFalse ⋯ + | Theory.VExpr.bvar deBruijnIndex, fn.app arg => isFalse ⋯ + | Theory.VExpr.bvar deBruijnIndex, binderType.lam body => isFalse ⋯ + | Theory.VExpr.bvar deBruijnIndex, binderType.forallE body => isFalse ⋯ + | Theory.VExpr.bvar deBruijnIndex, Theory.VExpr.proj ref index expr => isFalse ⋯ + | Theory.VExpr.bvar deBruijnIndex, Theory.VExpr.natLit value => isFalse ⋯ + | Theory.VExpr.sort u, Theory.VExpr.bvar deBruijnIndex => isFalse ⋯ + | Theory.VExpr.sort a, Theory.VExpr.sort b => if h : a = b then h ▸ isTrue ⋯ else isFalse ⋯ + | Theory.VExpr.sort u, Theory.VExpr.const ref levels => isFalse ⋯ + | Theory.VExpr.sort u, fn.app arg => isFalse ⋯ + | Theory.VExpr.sort u, binderType.lam body => isFalse ⋯ + | Theory.VExpr.sort u, binderType.forallE body => isFalse ⋯ + | Theory.VExpr.sort u, Theory.VExpr.proj ref index expr => isFalse ⋯ + | Theory.VExpr.sort u, Theory.VExpr.natLit value => isFalse ⋯ + | Theory.VExpr.const ref levels, Theory.VExpr.bvar deBruijnIndex => isFalse ⋯ + | Theory.VExpr.const ref levels, Theory.VExpr.sort u => isFalse ⋯ + | Theory.VExpr.const a a_1, Theory.VExpr.const b b_1 => + if h : a = b then h ▸ if h : a_1 = b_1 then h ▸ isTrue ⋯ else isFalse ⋯ else isFalse ⋯ + | Theory.VExpr.const ref levels, fn.app arg => isFalse ⋯ + | Theory.VExpr.const ref levels, binderType.lam body => isFalse ⋯ + | Theory.VExpr.const ref levels, binderType.forallE body => isFalse ⋯ + | Theory.VExpr.const ref levels, Theory.VExpr.proj ref_1 index expr => isFalse ⋯ + | Theory.VExpr.const ref levels, Theory.VExpr.natLit value => isFalse ⋯ + | fn.app arg, Theory.VExpr.bvar deBruijnIndex => isFalse ⋯ + | fn.app arg, Theory.VExpr.sort u => isFalse ⋯ + | fn.app arg, Theory.VExpr.const ref levels => isFalse ⋯ + | a.app a_1, b.app b_1 => + let inst_1 := Theory.instDecidableEqVExpr.decEq._unsafe_rec a b; + if h : a = b then + h ▸ + let inst_2 := Theory.instDecidableEqVExpr.decEq._unsafe_rec a a; + let inst_3 := Theory.instDecidableEqVExpr.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst := Theory.instDecidableEqVExpr.decEq._unsafe_rec a_1 a_1; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + | fn.app arg, binderType.lam body => isFalse ⋯ + | fn.app arg, binderType.forallE body => isFalse ⋯ + | fn.app arg, Theory.VExpr.proj ref index expr => isFalse ⋯ + | fn.app arg, Theory.VExpr.natLit value => isFalse ⋯ + | binderType.lam body, Theory.VExpr.bvar deBruijnIndex => isFalse ⋯ + | binderType.lam body, Theory.VExpr.sort u => isFalse ⋯ + | binderType.lam body, Theory.VExpr.const ref levels => isFalse ⋯ + | binderType.lam body, fn.app arg => isFalse ⋯ + | a.lam a_1, b.lam b_1 => + let inst_1 := Theory.instDecidableEqVExpr.decEq._unsafe_rec a b; + if h : a = b then + h ▸ + let inst_2 := Theory.instDecidableEqVExpr.decEq._unsafe_rec a a; + let inst_3 := Theory.instDecidableEqVExpr.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst := Theory.instDecidableEqVExpr.decEq._unsafe_rec a_1 a_1; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + | binderType.lam body, binderType_1.forallE body_1 => isFalse ⋯ + | binderType.lam body, Theory.VExpr.proj ref index expr => isFalse ⋯ + | binderType.lam body, Theory.VExpr.natLit value => isFalse ⋯ + | binderType.forallE body, Theory.VExpr.bvar deBruijnIndex => isFalse ⋯ + | binderType.forallE body, Theory.VExpr.sort u => isFalse ⋯ + | binderType.forallE body, Theory.VExpr.const ref levels => isFalse ⋯ + | binderType.forallE body, fn.app arg => isFalse ⋯ + | binderType.forallE body, binderType_1.lam body_1 => isFalse ⋯ + | a.forallE a_1, b.forallE b_1 => + let inst_1 := Theory.instDecidableEqVExpr.decEq._unsafe_rec a b; + if h : a = b then + h ▸ + let inst_2 := Theory.instDecidableEqVExpr.decEq._unsafe_rec a a; + let inst_3 := Theory.instDecidableEqVExpr.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst := Theory.instDecidableEqVExpr.decEq._unsafe_rec a_1 a_1; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + | binderType.forallE body, Theory.VExpr.proj ref index expr => isFalse ⋯ + | binderType.forallE body, Theory.VExpr.natLit value => isFalse ⋯ + | Theory.VExpr.proj ref index expr, Theory.VExpr.bvar deBruijnIndex => isFalse ⋯ + | Theory.VExpr.proj ref index expr, Theory.VExpr.sort u => isFalse ⋯ + | Theory.VExpr.proj ref index expr, Theory.VExpr.const ref_1 levels => isFalse ⋯ + | Theory.VExpr.proj ref index expr, fn.app arg => isFalse ⋯ + | Theory.VExpr.proj ref index expr, binderType.lam body => isFalse ⋯ + | Theory.VExpr.proj ref index expr, binderType.forallE body => isFalse ⋯ + | Theory.VExpr.proj a a_1 a_2, Theory.VExpr.proj b b_1 b_2 => + if h : a = b then + h ▸ + if h : a_1 = b_1 then + h ▸ + let inst_1 := Theory.instDecidableEqVExpr.decEq._unsafe_rec a_2 b_2; + if h : a_2 = b_2 then + h ▸ + let inst := Theory.instDecidableEqVExpr.decEq._unsafe_rec a_2 a_2; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + else isFalse ⋯ + | Theory.VExpr.proj ref index expr, Theory.VExpr.natLit value => isFalse ⋯ + | Theory.VExpr.natLit value, Theory.VExpr.bvar deBruijnIndex => isFalse ⋯ + | Theory.VExpr.natLit value, Theory.VExpr.sort u => isFalse ⋯ + | Theory.VExpr.natLit value, Theory.VExpr.const ref levels => isFalse ⋯ + | Theory.VExpr.natLit value, fn.app arg => isFalse ⋯ + | Theory.VExpr.natLit value, binderType.lam body => isFalse ⋯ + | Theory.VExpr.natLit value, binderType.forallE body => isFalse ⋯ + | Theory.VExpr.natLit value, Theory.VExpr.proj ref index expr => isFalse ⋯ + | Theory.VExpr.natLit a, Theory.VExpr.natLit b => if h : a = b then h ▸ isTrue ⋯ else isFalse ⋯ +RECURSION WORKER Ix.Theory.instDecidableEqVLevel.decEq._unsafe_rec +(x x_1 : Theory.VLevel) → Decidable (x = x_1) +IMPLEMENTATION +fun x x_1 => + match x, x_1 with + | Theory.VLevel.zero, Theory.VLevel.zero => isTrue ⋯ + | Theory.VLevel.zero, a.succ => isFalse ⋯ + | Theory.VLevel.zero, a.max a_1 => isFalse ⋯ + | Theory.VLevel.zero, a.imax a_1 => isFalse ⋯ + | Theory.VLevel.zero, Theory.VLevel.param a => isFalse ⋯ + | a.succ, Theory.VLevel.zero => isFalse ⋯ + | a.succ, b.succ => + let inst := Theory.instDecidableEqVLevel.decEq._unsafe_rec a b; + if h : a = b then + h ▸ + let inst := Theory.instDecidableEqVLevel.decEq._unsafe_rec a a; + isTrue ⋯ + else isFalse ⋯ + | a.succ, a_1.max a_2 => isFalse ⋯ + | a.succ, a_1.imax a_2 => isFalse ⋯ + | a.succ, Theory.VLevel.param a_1 => isFalse ⋯ + | a.max a_1, Theory.VLevel.zero => isFalse ⋯ + | a.max a_1, a_2.succ => isFalse ⋯ + | a.max a_1, b.max b_1 => + let inst := Theory.instDecidableEqVLevel.decEq._unsafe_rec a b; + if h : a = b then + h ▸ + let inst := Theory.instDecidableEqVLevel.decEq._unsafe_rec a a; + let inst := Theory.instDecidableEqVLevel.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst := Theory.instDecidableEqVLevel.decEq._unsafe_rec a_1 a_1; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + | a.max a_1, a_2.imax a_3 => isFalse ⋯ + | a.max a_1, Theory.VLevel.param a_2 => isFalse ⋯ + | a.imax a_1, Theory.VLevel.zero => isFalse ⋯ + | a.imax a_1, a_2.succ => isFalse ⋯ + | a.imax a_1, a_2.max a_3 => isFalse ⋯ + | a.imax a_1, b.imax b_1 => + let inst := Theory.instDecidableEqVLevel.decEq._unsafe_rec a b; + if h : a = b then + h ▸ + let inst := Theory.instDecidableEqVLevel.decEq._unsafe_rec a a; + let inst := Theory.instDecidableEqVLevel.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst := Theory.instDecidableEqVLevel.decEq._unsafe_rec a_1 a_1; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + | a.imax a_1, Theory.VLevel.param a_2 => isFalse ⋯ + | Theory.VLevel.param a, Theory.VLevel.zero => isFalse ⋯ + | Theory.VLevel.param a, a_1.succ => isFalse ⋯ + | Theory.VLevel.param a, a_1.max a_2 => isFalse ⋯ + | Theory.VLevel.param a, a_1.imax a_2 => isFalse ⋯ + | Theory.VLevel.param a, Theory.VLevel.param b => if h : a = b then h ▸ isTrue ⋯ else isFalse ⋯ +RECURSION WORKER Ix.Theory.Certificate.Ordinary.arguments?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Nat → + Theory.Model.Environment β → + Theory.Model.Context β → + List (Theory.Model.AExpr β) → + List (Theory.Model.AExpr β) → Option (List (Theory.Certified.TypingWitness β)) +IMPLEMENTATION +fun {β} [DecidableEq β] [Theory.Certificate.Hints β] fuel n entries Γ x x_1 => + match x, x_1 with + | [], [] => some [] + | A :: domains, a :: args => do + let typed ← Theory.Certificate.inferAnnotated? fuel n entries Γ a + let witness ← Theory.Certificate.castWith? fuel n entries Γ typed A + let tail ← + Theory.Certificate.Ordinary.arguments?._unsafe_rec fuel n entries Γ (Theory.Model.Telescope.inst a domains) args + pure (witness :: tail) + | x, x_2 => none +RECURSION WORKER Ix.Theory.Certificate.Ordinary.domains?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Nat → + Theory.Model.Environment β → + Theory.Model.Context β → List (Theory.Model.AExpr β) → Option (List (Theory.Certified.DomainWitness β)) +IMPLEMENTATION +fun {β} [DecidableEq β] [Theory.Certificate.Hints β] fuel n entries x x_1 => + match x, x_1 with + | x, [] => some [] + | Γ, A :: rest => do + let typed ← Theory.Certificate.inferAnnotated? fuel n entries Γ A + match typed.type with + | Theory.Model.AExpr.sort level => do + let tail ← Theory.Certificate.Ordinary.domains?._unsafe_rec fuel n entries (Theory.Model.Context.push A Γ) rest + pure ({ level := level, typing := typed.witness } :: tail) + | x => none +RECURSION WORKER Ix.Theory.Certificate.Ordinary.sourceDomains?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Nat → + Theory.Model.Environment β → + Theory.Model.Context β → List (Theory.VExpr β) → Option (List (Theory.Model.AExpr β)) +IMPLEMENTATION +fun {β} [DecidableEq β] [Theory.Certificate.Hints β] fuel n entries x x_1 => + match x, x_1 with + | x, [] => some [] + | Γ, A :: rest => do + let typed ← Theory.Certificate.inferSource? fuel n entries Γ A + match typed.type with + | Theory.Model.AExpr.sort level => do + let tail ← + Theory.Certificate.Ordinary.sourceDomains?._unsafe_rec fuel n entries + (Theory.Model.Context.push typed.expression Γ) rest + pure (typed.expression :: tail) + | x => none +RECURSION WORKER Ix.Theory.Certificate.Ordinary.telescope._unsafe_rec +{β : Type u} → Theory.VExpr β → List (Theory.VExpr β) × Theory.VExpr β +IMPLEMENTATION +fun {β} x => + match x with + | A.forallE B => + match Theory.Certificate.Ordinary.telescope._unsafe_rec B with + | (tail, result) => (A :: tail, result) + | e => ([], e) +RECURSION WORKER Ix.Theory.Certificate.Quotient.typeWitnesses?._unsafe_rec +{β : Type u} → + [DecidableEq β] → + [Theory.Certificate.Hints β] → + Nat → + Theory.Model.Environment β → + List (Theory.Certified.Signature.Header β) → Option (List (Theory.Certified.Signature.TypeWitness β)) +IMPLEMENTATION +fun {β} [DecidableEq β] [Theory.Certificate.Hints β] fuel entries x => + match x with + | [] => some [] + | header :: headers => do + let inferred ← Theory.Certificate.inferAnnotated? fuel header.universes entries [] header.type + match inferred.type with + | Theory.Model.AExpr.sort level => do + let rest ← + Theory.Certificate.Quotient.typeWitnesses?._unsafe_rec fuel (entries.insert header.ref header.entry) headers + pure ({ level := level, typing := inferred.witness } :: rest) + | x => none +RECURSION WORKER Ix.Theory.Certified.IndexSet.insert._unsafe_rec +Nat → List Nat → List Nat +IMPLEMENTATION +fun x x_1 => + match x_1 with + | [] => [x] + | y :: ys => + if x < y then x :: y :: ys else if x = y then y :: ys else y :: Theory.Certified.IndexSet.insert._unsafe_rec x ys +RECURSION WORKER Ix.Theory.Certified.IndexSet.normalize._unsafe_rec +List Nat → List Nat +IMPLEMENTATION +fun x => + match x with + | [] => [] + | x :: xs => Theory.Certified.IndexSet.insert x (Theory.Certified.IndexSet.normalize._unsafe_rec xs) +RECURSION WORKER Ix.Theory.Certified.LevelEq.leq._unsafe_rec +Theory.VLevel → Theory.VLevel → Bool +IMPLEMENTATION +fun a b => + if a = b then true + else + match a, b with + | Theory.VLevel.zero, x => true + | a.max b, c => Theory.Certified.LevelEq.leq._unsafe_rec a c && Theory.Certified.LevelEq.leq._unsafe_rec b c + | a, b.max c => Theory.Certified.LevelEq.leq._unsafe_rec a b || Theory.Certified.LevelEq.leq._unsafe_rec a c + | a.succ, b.succ => Theory.Certified.LevelEq.leq._unsafe_rec a b + | a, b.succ => Theory.Certified.LevelEq.leq._unsafe_rec a b + | a.imax b, c => Theory.Certified.LevelEq.leq._unsafe_rec a c && Theory.Certified.LevelEq.leq._unsafe_rec b c + | x, x_1 => false +RECURSION WORKER Ix.Theory.Certified.LevelEq.normalize._unsafe_rec +Theory.VLevel → Theory.VLevel +IMPLEMENTATION +fun x => + match x with + | Theory.VLevel.zero => Theory.VLevel.zero + | Theory.VLevel.param i => Theory.VLevel.param i + | a.succ => (Theory.Certified.LevelEq.normalize._unsafe_rec a).succ + | a.max b => + Theory.Certified.LevelEq.max (Theory.Certified.LevelEq.normalize._unsafe_rec a) + (Theory.Certified.LevelEq.normalize._unsafe_rec b) + | a.imax b => + Theory.Certified.LevelEq.imax (Theory.Certified.LevelEq.normalize._unsafe_rec a) + (Theory.Certified.LevelEq.normalize._unsafe_rec b) +RECURSION WORKER Ix.Theory.Certified.Modeled.applyPi?._unsafe_rec +{β : Type u} → Theory.VExpr β → List (Theory.VExpr β) → Option (Theory.VExpr β) +IMPLEMENTATION +fun {β} x x_1 => + match x, x_1 with + | type, [] => some type + | binderType.forallE body, argument :: rest => Theory.Certified.Modeled.applyPi?._unsafe_rec (body.inst argument) rest + | x, head :: tail => none +RECURSION WORKER Ix.Theory.Certified.Modeled.betaHead?._unsafe_rec +{β : Type u} → Nat → Theory.VExpr β → Option (Theory.VExpr β) +IMPLEMENTATION +fun {β} x x_1 => + match x, x_1 with + | 0, x => none + | fuel.succ, function.app argument => do + let function ← Theory.Certified.Modeled.betaHead?._unsafe_rec fuel function + match function with + | binderType.lam body => Theory.Certified.Modeled.betaHead?._unsafe_rec fuel (body.inst argument) + | x => some (function.app argument) + | n.succ, expression => some expression +RECURSION WORKER Ix.Theory.Certified.Modeled.checkEach?._unsafe_rec +{β : Type u} → + [inst : DecidableEq β] → + Nat → + (entries : Theory.Model.Environment β) → + (companions pending : List (Theory.Certified.Modeled.Companion β)) → + List (List (Theory.Certified.Modeled.EquationWitness β)) → + Option + (Theory.Certified.CheckedClaim + (∀ (companion : Theory.Certified.Modeled.Companion β), + companion ∈ pending → Theory.Certified.Modeled.CompanionChecked entries companions companion)) +IMPLEMENTATION +fun {β} [DecidableEq β] fuel entries companions x x_1 => + match x, x_1 with + | [], [] => some { down := ⋯ } + | companion :: rest, witnesses :: remaining => do + let first ← Theory.Certified.Modeled.checkCompanion? fuel entries companions companion witnesses + let others ← Theory.Certified.Modeled.checkEach?._unsafe_rec fuel entries companions rest remaining + pure { down := ⋯ } + | x, x_2 => none +RECURSION WORKER Ix.Theory.Certified.Modeled.checkRules?._unsafe_rec +{β : Type u} → + [inst : DecidableEq β] → + Nat → + (entries : Theory.Model.Environment β) → + (companions : List (Theory.Certified.Modeled.Companion β)) → + (rules : List (Theory.Certified.Signature.Rule β)) → + List (Theory.Certified.Modeled.EquationWitness β) → + Option + (Theory.Certified.CheckedClaim + (∀ (rule : Theory.Certified.Signature.Rule β), + rule ∈ rules → + Theory.Certified.Modeled.CheckedEquation entries + (Theory.Certified.Modeled.mapRule companions rule))) +IMPLEMENTATION +fun {β} [DecidableEq β] fuel entries companions x x_1 => + match x, x_1 with + | [], [] => some { down := ⋯ } + | rule :: rules, witness :: witnesses => do + let first ← + Theory.Certified.Modeled.checkEquation? fuel entries (Theory.Certified.Modeled.mapRule companions rule) witness + let rest ← Theory.Certified.Modeled.checkRules?._unsafe_rec fuel entries companions rules witnesses + pure { down := ⋯ } + | x, x_2 => none +RECURSION WORKER Ix.Theory.Certified.Modeled.lookupCompanion._unsafe_rec +{β : Type u} → + [DecidableEq β] → + List (Theory.Certified.Modeled.Companion β) → Theory.ConstRef β → Option (Theory.Certified.Modeled.Companion β) +IMPLEMENTATION +fun {β} [DecidableEq β] x x_1 => + match x, x_1 with + | [], x => none + | companion :: rest, ref => + if companion.header.ref = ref then some companion else Theory.Certified.Modeled.lookupCompanion._unsafe_rec rest ref +RECURSION WORKER Ix.Theory.Certified.Modeled.splitLam?._unsafe_rec +{β : Type u} → Nat → Theory.VExpr β → Option (List (Theory.VExpr β) × Theory.VExpr β) +IMPLEMENTATION +fun {β} x x_1 => + match x, x_1 with + | 0, expression => some ([], expression) + | n.succ, domain.lam body => do + let __x ← Theory.Certified.Modeled.splitLam?._unsafe_rec n body + match __x with + | (domains, result) => pure (domain :: domains, result) + | n.succ, x => none +RECURSION WORKER Ix.Theory.Certified.Modeled.splitPi?._unsafe_rec +{β : Type u} → Nat → Theory.VExpr β → Option (List (Theory.VExpr β) × Theory.VExpr β) +IMPLEMENTATION +fun {β} x x_1 => + match x, x_1 with + | 0, type => some ([], type) + | n.succ, domain.forallE body => do + let __x ← Theory.Certified.Modeled.splitPi?._unsafe_rec n body + match __x with + | (domains, result) => pure (domain :: domains, result) + | n.succ, x => none +RECURSION WORKER Ix.Theory.Certified.Ordinary.checkConstructors._unsafe_rec +{β : Type u} → + [DecidableEq β] → + Nat → + (entries : Theory.Model.Environment β) → + (shape : Theory.Certified.Ordinary.Shape β) → + (ctors : List (Theory.Certified.Ordinary.Constructor β)) → + List (Theory.Certified.Ordinary.ConstructorWitness β) → + Option + (Theory.Certified.CheckedClaim + (∀ (ctor : Theory.Certified.Ordinary.Constructor β), + ctor ∈ ctors → Theory.Certified.Ordinary.ConstructorEvidence entries shape ctor)) +IMPLEMENTATION +fun {β} [DecidableEq β] fuel entries shape x x_1 => + match x, x_1 with + | [], [] => some { down := ⋯ } + | ctor :: ctors, witness :: witnesses => + if hrefs : + ∀ (e : Theory.Model.AExpr β), e ∈ ctor.fields ++ ctor.indices → Theory.Model.AExpr.ReferencesIn entries e then + do + let fields ← + Theory.Certified.verifyTelescope fuel shape.universes entries (some shape.level) shape.parameterContext + ctor.fields witness.fields + let indices ← + Theory.Certified.verifyArguments fuel shape.universes entries (ctor.context shape) + (Theory.Model.Telescope.lift ctor.fields.length shape.indices) ctor.indices witness.indices + let recursive ← Theory.Certified.Ordinary.checkRecursive fuel entries shape ctor ctor.recursive witness.recursive + let rest ← Theory.Certified.Ordinary.checkConstructors._unsafe_rec fuel entries shape ctors witnesses + pure { down := ⋯ } + else none + | x, x_2 => none +RECURSION WORKER Ix.Theory.Certified.Ordinary.checkRecursive._unsafe_rec +{β : Type u} → + [DecidableEq β] → + Nat → + (entries : Theory.Model.Environment β) → + (shape : Theory.Certified.Ordinary.Shape β) → + (ctor : Theory.Certified.Ordinary.Constructor β) → + (fields : List (Theory.Certified.Ordinary.RecursiveField β)) → + List (Theory.Certified.Ordinary.RecursiveWitness β) → + Option + (Theory.Certified.CheckedClaim + (∀ (field : Theory.Certified.Ordinary.RecursiveField β), + field ∈ fields → Theory.Certified.Ordinary.RecursiveEvidence entries shape ctor field)) +IMPLEMENTATION +fun {β} [DecidableEq β] fuel entries shape ctor x x_1 => + match x, x_1 with + | [], [] => some { down := ⋯ } + | field :: fields, witness :: witnesses => + if hrefs : + ∀ (e : Theory.Model.AExpr β), + e ∈ field.domains ++ field.indices → Theory.Model.AExpr.ReferencesIn entries e then + do + let domains ← + Theory.Certified.verifyTelescope fuel shape.universes entries (some shape.level) (ctor.context shape) + field.domains witness.domains + let indices ← + Theory.Certified.verifyArguments fuel shape.universes entries + (Theory.Model.Telescope.context (ctor.context shape) field.domains) + (Theory.Model.Telescope.lift (ctor.fields.length + field.domains.length) shape.indices) field.indices + witness.indices + let rest ← Theory.Certified.Ordinary.checkRecursive._unsafe_rec fuel entries shape ctor fields witnesses + pure { down := ⋯ } + else none + | x, x_2 => none +RECURSION WORKER Ix.Theory.Certified.Ordinary.parameterVars._unsafe_rec +{β : Type u} → Nat → Nat → List (Theory.Model.AExpr β) +IMPLEMENTATION +fun {β} offset x => + match x with + | 0 => [] + | n.succ => Theory.Model.AExpr.bvar (offset + n) :: Theory.Certified.Ordinary.parameterVars._unsafe_rec offset n +RECURSION WORKER Ix.Theory.Certified.PropWhen.bindList._unsafe_rec +(Nat → Theory.Certified.PropWhen) → List Nat → Theory.Certified.PropWhen +IMPLEMENTATION +fun f x => + match x with + | [] => Theory.Certified.PropWhen.always + | i :: ps => (f i).inter (Theory.Certified.PropWhen.bindList._unsafe_rec f ps) +RECURSION WORKER Ix.Theory.Certified.Signature.checkTypes._unsafe_rec +{β : Type u} → + [inst : DecidableEq β] → + Nat → + (entries : Theory.Model.Environment β) → + (headers : List (Theory.Certified.Signature.Header β)) → + List (Theory.Certified.Signature.TypeWitness β) → + Option (Theory.Certified.CheckedClaim (Theory.Certified.Signature.Formed entries headers)) +IMPLEMENTATION +fun {β} [DecidableEq β] fuel entries x x_1 => + match x, x_1 with + | [], [] => some { down := ⋯ } + | header :: headers, witness :: witnesses => + if hf : entries header.ref = none then + if hs : Theory.Model.AExpr.Scope header.universes 0 header.type then + if hr : Theory.Model.AExpr.ReferencesIn entries header.type then do + let ht ← + Theory.Certified.verifyType fuel header.universes entries [] header.type + (Theory.Model.AExpr.sort witness.level) witness.typing + let rest ← + Theory.Certified.Signature.checkTypes._unsafe_rec fuel (entries.insert header.ref header.entry) headers + witnesses + pure { down := ⋯ } + else none + else none + else none + | x, x_2 => none +RECURSION WORKER Ix.Theory.Certified.Signature.environment._unsafe_rec +{β : Type u} → + [DecidableEq β] → Theory.Model.Environment β → List (Theory.Certified.Signature.Header β) → Theory.Model.Environment β +IMPLEMENTATION +fun {β} [DecidableEq β] entries x => + match x with + | [] => entries + | header :: rest => Theory.Certified.Signature.environment._unsafe_rec (entries.insert header.ref header.entry) rest +RECURSION WORKER Ix.Theory.Certified.Structure.checkFields._unsafe_rec +{β : Type u} → + [DecidableEq β] → + Nat → + Nat → + (entries : Theory.Model.Environment β) → + (w : Theory.VLevel) → + (Γ : Theory.Model.Context β) → + (fields : List (Theory.Certified.Structure.Field β)) → + List (Theory.Certified.TypingWitness β) → + Option (Theory.Certified.CheckedClaim (Theory.Certified.Structure.FieldsFormed entries w Γ fields)) +IMPLEMENTATION +fun {β} [DecidableEq β] fuel n entries w x x_1 x_2 => + match x, x_1, x_2 with + | x, [], [] => some { down := ⋯ } + | Γ, field :: fields, witness :: witnesses => + if hz : Theory.Certified.checkZeroImplies w field.level = true then do + let hA ← Theory.Certified.verifyType fuel n entries Γ field.domain (Theory.Model.AExpr.sort field.level) witness + let rest ← + Theory.Certified.Structure.checkFields._unsafe_rec fuel n entries w (Theory.Model.Context.push field.domain Γ) + fields witnesses + pure { down := ⋯ } + else none + | x, x_3, x_4 => none +RECURSION WORKER Ix.Theory.Certified.Structure.checkIota._unsafe_rec +{β : Type u} → + [inst : DecidableEq β] → + Nat → + (entries : Theory.Model.Environment β) → + (d : Theory.Certified.Structure.Description β) → + (source recursor : β) → + (mode : Theory.Inductive.ElimMode) → + (fields : List (Theory.Certified.Structure.Field β × Nat)) → + List (Theory.Certified.Signature.RuleWitness β) → + Option + (Theory.Certified.CheckedClaim + (∀ (field : Theory.Certified.Structure.Field β) (i : Nat), + (field, i) ∈ fields → + Theory.Certified.Signature.RuleFormed (d.iotaEnvironment entries source recursor mode i) + (d.iotaRule source i field))) +IMPLEMENTATION +fun {β} [DecidableEq β] fuel entries d source recursor mode x x_1 => + match x, x_1 with + | [], [] => some { down := ⋯ } + | (field, i) :: fields, witness :: witnesses => do + let ht ← + Theory.Certified.Signature.checkRule fuel (d.iotaEnvironment entries source recursor mode i) + (d.iotaRule source i field) witness + let rest ← Theory.Certified.Structure.checkIota._unsafe_rec fuel entries d source recursor mode fields witnesses + pure { down := ⋯ } + | x, x_2 => none +RECURSION WORKER Ix.Theory.Model.AExpr.Scope._unsafe_rec +{β : Type u_1} → Nat → Nat → Theory.Model.AExpr β → Prop +IMPLEMENTATION +fun {β} universes depth x => + match x with + | Theory.Model.AExpr.bvar i => i < depth + | Theory.Model.AExpr.sort l => Theory.VLevel.WF universes l + | Theory.Model.AExpr.const ref ls => ∀ (l : Theory.VLevel), l ∈ ls → Theory.VLevel.WF universes l + | f.app a => + Theory.Model.AExpr.Scope._unsafe_rec universes depth f ∧ Theory.Model.AExpr.Scope._unsafe_rec universes depth a + | Theory.Model.AExpr.lam p a b => + Theory.Certified.PropWhen.WF universes p ∧ + Theory.Model.AExpr.Scope._unsafe_rec universes depth a ∧ + Theory.Model.AExpr.Scope._unsafe_rec universes (depth + 1) b + | Theory.Model.AExpr.forallE p a b => + Theory.Certified.PropWhen.WF universes p ∧ + Theory.Model.AExpr.Scope._unsafe_rec universes depth a ∧ + Theory.Model.AExpr.Scope._unsafe_rec universes (depth + 1) b + | Theory.Model.AExpr.proj ref field e => Theory.Model.AExpr.Scope._unsafe_rec universes depth e + | Theory.Model.AExpr.natLit value => True +RECURSION WORKER Ix.Theory.Model.AExpr.appN._unsafe_rec +{β : Type u} → Theory.Model.AExpr β → List (Theory.Model.AExpr β) → Theory.Model.AExpr β +IMPLEMENTATION +fun {β} f x => + match x with + | [] => f + | x :: xs => Theory.Model.AExpr.appN._unsafe_rec (f.app x) xs +RECURSION WORKER Ix.Theory.Model.AExpr.decidableScope._unsafe_rec +{β : Type u_1} → {n k : Nat} → {e : Theory.Model.AExpr β} → Decidable (Theory.Model.AExpr.Scope n k e) +IMPLEMENTATION +fun {β} {n k} x => + match x with + | Theory.Model.AExpr.bvar i => Theory.Model.AExpr.decidableScope._aux_1 i + | Theory.Model.AExpr.sort l => Theory.VLevel.decidable_WF + | Theory.Model.AExpr.const ref ls => Theory.Model.AExpr.decidableScope._aux_3 ref ls + | f.app a => instDecidableAnd + | Theory.Model.AExpr.lam p A b => instDecidableAnd + | Theory.Model.AExpr.forallE p A b => instDecidableAnd + | Theory.Model.AExpr.proj ref field e => Theory.Model.AExpr.decidableScope._unsafe_rec + | Theory.Model.AExpr.natLit value => instDecidableTrue +RECURSION WORKER Ix.Theory.Model.AExpr.erase._unsafe_rec +{β : Type u_1} → Theory.Model.AExpr β → Theory.VExpr β +IMPLEMENTATION +fun {β} x => + match x with + | Theory.Model.AExpr.bvar i => Theory.VExpr.bvar i + | Theory.Model.AExpr.sort l => Theory.VExpr.sort l + | Theory.Model.AExpr.const r ls => Theory.VExpr.const r ls + | f.app a => (Theory.Model.AExpr.erase._unsafe_rec f).app (Theory.Model.AExpr.erase._unsafe_rec a) + | Theory.Model.AExpr.lam condition a b => + (Theory.Model.AExpr.erase._unsafe_rec a).lam (Theory.Model.AExpr.erase._unsafe_rec b) + | Theory.Model.AExpr.forallE condition a b => + (Theory.Model.AExpr.erase._unsafe_rec a).forallE (Theory.Model.AExpr.erase._unsafe_rec b) + | Theory.Model.AExpr.proj r i e => Theory.VExpr.proj r i (Theory.Model.AExpr.erase._unsafe_rec e) + | Theory.Model.AExpr.natLit v => Theory.VExpr.natLit v +RECURSION WORKER Ix.Theory.Model.AExpr.forallN._unsafe_rec +{β : Type u} → Theory.Certified.PropWhen → List (Theory.Model.AExpr β) → Theory.Model.AExpr β → Theory.Model.AExpr β +IMPLEMENTATION +fun {β} p x x_1 => + match x, x_1 with + | [], B => B + | A :: rest, B => Theory.Model.AExpr.forallE p A (Theory.Model.AExpr.forallN._unsafe_rec p rest B) +RECURSION WORKER Ix.Theory.Model.AExpr.inst._unsafe_rec +{β : Type u_1} → Theory.Model.AExpr β → Theory.Model.AExpr β → optParam Nat 0 → Theory.Model.AExpr β +IMPLEMENTATION +fun {β} x x_1 x_2 => + match x, x_1, x_2 with + | Theory.Model.AExpr.bvar i, a, k => Theory.Model.AExpr.instVar i a k + | Theory.Model.AExpr.sort l, x, x_3 => Theory.Model.AExpr.sort l + | Theory.Model.AExpr.const r ls, x, x_3 => Theory.Model.AExpr.const r ls + | f.app a, e, k => (Theory.Model.AExpr.inst._unsafe_rec f e k).app (Theory.Model.AExpr.inst._unsafe_rec a e k) + | Theory.Model.AExpr.lam p a b, e, k => + Theory.Model.AExpr.lam p (Theory.Model.AExpr.inst._unsafe_rec a e k) + (Theory.Model.AExpr.inst._unsafe_rec b e (k + 1)) + | Theory.Model.AExpr.forallE p a b, e, k => + Theory.Model.AExpr.forallE p (Theory.Model.AExpr.inst._unsafe_rec a e k) + (Theory.Model.AExpr.inst._unsafe_rec b e (k + 1)) + | Theory.Model.AExpr.proj r i e, a, k => Theory.Model.AExpr.proj r i (Theory.Model.AExpr.inst._unsafe_rec e a k) + | Theory.Model.AExpr.natLit v, x, x_3 => Theory.Model.AExpr.natLit v +RECURSION WORKER Ix.Theory.Model.AExpr.instL._unsafe_rec +{β : Type u_1} → List Theory.VLevel → Theory.Model.AExpr β → Theory.Model.AExpr β +IMPLEMENTATION +fun {β} levels x => + match x with + | Theory.Model.AExpr.bvar i => Theory.Model.AExpr.bvar i + | Theory.Model.AExpr.sort l => Theory.Model.AExpr.sort (Theory.VLevel.inst levels l) + | Theory.Model.AExpr.const r ls => Theory.Model.AExpr.const r (List.map (Theory.VLevel.inst levels) ls) + | f.app a => (Theory.Model.AExpr.instL._unsafe_rec levels f).app (Theory.Model.AExpr.instL._unsafe_rec levels a) + | Theory.Model.AExpr.lam p a b => + Theory.Model.AExpr.lam (Theory.Certified.instCondition levels p) (Theory.Model.AExpr.instL._unsafe_rec levels a) + (Theory.Model.AExpr.instL._unsafe_rec levels b) + | Theory.Model.AExpr.forallE p a b => + Theory.Model.AExpr.forallE (Theory.Certified.instCondition levels p) (Theory.Model.AExpr.instL._unsafe_rec levels a) + (Theory.Model.AExpr.instL._unsafe_rec levels b) + | Theory.Model.AExpr.proj r i e => Theory.Model.AExpr.proj r i (Theory.Model.AExpr.instL._unsafe_rec levels e) + | Theory.Model.AExpr.natLit v => Theory.Model.AExpr.natLit v +RECURSION WORKER Ix.Theory.Model.AExpr.instRev._unsafe_rec +{β : Type u} → Theory.Model.AExpr β → List (Theory.Model.AExpr β) → Theory.Model.AExpr β +IMPLEMENTATION +fun {β} x x_1 => + match x, x_1 with + | e, [] => e + | e, a :: rest => Theory.Model.AExpr.instRev._unsafe_rec (e.inst a rest.length) rest +RECURSION WORKER Ix.Theory.Model.AExpr.lamN._unsafe_rec +{β : Type u} → Theory.Certified.PropWhen → List (Theory.Model.AExpr β) → Theory.Model.AExpr β → Theory.Model.AExpr β +IMPLEMENTATION +fun {β} p x x_1 => + match x, x_1 with + | [], body => body + | A :: rest, body => Theory.Model.AExpr.lam p A (Theory.Model.AExpr.lamN._unsafe_rec p rest body) +RECURSION WORKER Ix.Theory.Model.AExpr.liftN._unsafe_rec +{β : Type u_1} → Nat → Theory.Model.AExpr β → optParam Nat 0 → Theory.Model.AExpr β +IMPLEMENTATION +fun {β} count x x_1 => + match x, x_1 with + | Theory.Model.AExpr.bvar i, k => Theory.Model.AExpr.bvar (Theory.liftVar count i k) + | Theory.Model.AExpr.sort l, x => Theory.Model.AExpr.sort l + | Theory.Model.AExpr.const r ls, x => Theory.Model.AExpr.const r ls + | f.app a, k => (Theory.Model.AExpr.liftN._unsafe_rec count f k).app (Theory.Model.AExpr.liftN._unsafe_rec count a k) + | Theory.Model.AExpr.lam p a b, k => + Theory.Model.AExpr.lam p (Theory.Model.AExpr.liftN._unsafe_rec count a k) + (Theory.Model.AExpr.liftN._unsafe_rec count b (k + 1)) + | Theory.Model.AExpr.forallE p a b, k => + Theory.Model.AExpr.forallE p (Theory.Model.AExpr.liftN._unsafe_rec count a k) + (Theory.Model.AExpr.liftN._unsafe_rec count b (k + 1)) + | Theory.Model.AExpr.proj r i e, k => Theory.Model.AExpr.proj r i (Theory.Model.AExpr.liftN._unsafe_rec count e k) + | Theory.Model.AExpr.natLit v, x => Theory.Model.AExpr.natLit v +RECURSION WORKER Ix.Theory.Model.AExpr.mapRefs._unsafe_rec +{β : Type u} → {γ : Type v} → (Theory.ConstRef β → Theory.ConstRef γ) → Theory.Model.AExpr β → Theory.Model.AExpr γ +IMPLEMENTATION +fun {β} {γ} mapping x => + match x with + | Theory.Model.AExpr.bvar i => Theory.Model.AExpr.bvar i + | Theory.Model.AExpr.sort level => Theory.Model.AExpr.sort level + | Theory.Model.AExpr.const ref levels => Theory.Model.AExpr.const (mapping ref) levels + | f.app a => (Theory.Model.AExpr.mapRefs._unsafe_rec mapping f).app (Theory.Model.AExpr.mapRefs._unsafe_rec mapping a) + | Theory.Model.AExpr.lam condition type body => + Theory.Model.AExpr.lam condition (Theory.Model.AExpr.mapRefs._unsafe_rec mapping type) + (Theory.Model.AExpr.mapRefs._unsafe_rec mapping body) + | Theory.Model.AExpr.forallE condition type body => + Theory.Model.AExpr.forallE condition (Theory.Model.AExpr.mapRefs._unsafe_rec mapping type) + (Theory.Model.AExpr.mapRefs._unsafe_rec mapping body) + | Theory.Model.AExpr.proj ref index major => + Theory.Model.AExpr.proj (mapping ref) index (Theory.Model.AExpr.mapRefs._unsafe_rec mapping major) + | Theory.Model.AExpr.natLit n => Theory.Model.AExpr.natLit n +RECURSION WORKER Ix.Theory.Model.AExpr.references._unsafe_rec +{β : Type u} → Theory.Model.AExpr β → List (Theory.ConstRef β) +IMPLEMENTATION +fun {β} x => + match x with + | Theory.Model.AExpr.bvar index => [] + | Theory.Model.AExpr.sort level => [] + | Theory.Model.AExpr.natLit value => [] + | Theory.Model.AExpr.const r levels => [r] + | f.app a => Theory.Model.AExpr.references._unsafe_rec f ++ Theory.Model.AExpr.references._unsafe_rec a + | Theory.Model.AExpr.lam condition f a => + Theory.Model.AExpr.references._unsafe_rec f ++ Theory.Model.AExpr.references._unsafe_rec a + | Theory.Model.AExpr.forallE condition f a => + Theory.Model.AExpr.references._unsafe_rec f ++ Theory.Model.AExpr.references._unsafe_rec a + | Theory.Model.AExpr.proj r field e => r :: Theory.Model.AExpr.references._unsafe_rec e +RECURSION WORKER Ix.Theory.Model.Telescope.context._unsafe_rec +{β : Type u} → Theory.Model.Context β → List (Theory.Model.AExpr β) → Theory.Model.Context β +IMPLEMENTATION +fun {β} Γ x => + match x with + | [] => Γ + | A :: rest => Theory.Model.Telescope.context._unsafe_rec (Theory.Model.Context.push A Γ) rest +RECURSION WORKER Ix.Theory.Model.Telescope.extend._unsafe_rec +{V : Type v} → (Nat → V) → List V → Nat → V +IMPLEMENTATION +fun {V} env x => + match (motive := List V → Nat → V) x with + | [] => env + | x :: xs => Theory.Model.Telescope.extend._unsafe_rec (Theory.Model.Valuation.cons x env) xs +RECURSION WORKER Ix.Theory.Model.Telescope.independent._unsafe_rec +{β : Type u} → List (Theory.Model.AExpr β) → optParam Nat 0 → List (Theory.Model.AExpr β) +IMPLEMENTATION +fun {β} x x_1 => + match x, x_1 with + | [], x => [] + | A :: rest, offset => + Theory.Model.AExpr.liftN offset A :: Theory.Model.Telescope.independent._unsafe_rec rest (offset + 1) +RECURSION WORKER Ix.Theory.Model.Telescope.inst._unsafe_rec +{β : Type u} → Theory.Model.AExpr β → List (Theory.Model.AExpr β) → optParam Nat 0 → List (Theory.Model.AExpr β) +IMPLEMENTATION +fun {β} arg x x_1 => + match x, x_1 with + | [], x => [] + | A :: rest, k => A.inst arg k :: Theory.Model.Telescope.inst._unsafe_rec arg rest (k + 1) +RECURSION WORKER Ix.Theory.Model.Telescope.lift._unsafe_rec +{β : Type u} → Nat → List (Theory.Model.AExpr β) → optParam Nat 0 → List (Theory.Model.AExpr β) +IMPLEMENTATION +fun {β} count x x_1 => + match x, x_1 with + | [], x => [] + | A :: rest, k => Theory.Model.AExpr.liftN count A k :: Theory.Model.Telescope.lift._unsafe_rec count rest (k + 1) +RECURSION WORKER Ix.Theory.Model.Telescope.simple._unsafe_rec +{V : Type v} → (domains : List V) → Theory.Model.SetTheory.Tower.TeleS V domains.length +IMPLEMENTATION +fun {V} x => + match x with + | [] => Theory.Model.SetTheory.Tower.TeleS.nil + | A :: rest => Theory.Model.SetTheory.Tower.TeleS.cons A fun x => Theory.Model.Telescope.simple._unsafe_rec rest +RECURSION WORKER Ix.Theory.Model.instDecidableEqAExpr.decEq._unsafe_rec +{β : Type u_1} → [DecidableEq β] → (x x_1 : Theory.Model.AExpr β) → Decidable (x = x_1) +IMPLEMENTATION +fun {β} [inst : DecidableEq β] x x_1 => + match x, x_1 with + | Theory.Model.AExpr.bvar a, Theory.Model.AExpr.bvar b => if h : a = b then h ▸ isTrue ⋯ else isFalse ⋯ + | Theory.Model.AExpr.bvar index, Theory.Model.AExpr.sort level => isFalse ⋯ + | Theory.Model.AExpr.bvar index, Theory.Model.AExpr.const ref levels => isFalse ⋯ + | Theory.Model.AExpr.bvar index, fn.app arg => isFalse ⋯ + | Theory.Model.AExpr.bvar index, Theory.Model.AExpr.lam condition domain body => isFalse ⋯ + | Theory.Model.AExpr.bvar index, Theory.Model.AExpr.forallE condition domain body => isFalse ⋯ + | Theory.Model.AExpr.bvar index, Theory.Model.AExpr.proj ref field major => isFalse ⋯ + | Theory.Model.AExpr.bvar index, Theory.Model.AExpr.natLit value => isFalse ⋯ + | Theory.Model.AExpr.sort level, Theory.Model.AExpr.bvar index => isFalse ⋯ + | Theory.Model.AExpr.sort a, Theory.Model.AExpr.sort b => if h : a = b then h ▸ isTrue ⋯ else isFalse ⋯ + | Theory.Model.AExpr.sort level, Theory.Model.AExpr.const ref levels => isFalse ⋯ + | Theory.Model.AExpr.sort level, fn.app arg => isFalse ⋯ + | Theory.Model.AExpr.sort level, Theory.Model.AExpr.lam condition domain body => isFalse ⋯ + | Theory.Model.AExpr.sort level, Theory.Model.AExpr.forallE condition domain body => isFalse ⋯ + | Theory.Model.AExpr.sort level, Theory.Model.AExpr.proj ref field major => isFalse ⋯ + | Theory.Model.AExpr.sort level, Theory.Model.AExpr.natLit value => isFalse ⋯ + | Theory.Model.AExpr.const ref levels, Theory.Model.AExpr.bvar index => isFalse ⋯ + | Theory.Model.AExpr.const ref levels, Theory.Model.AExpr.sort level => isFalse ⋯ + | Theory.Model.AExpr.const a a_1, Theory.Model.AExpr.const b b_1 => + if h : a = b then h ▸ if h : a_1 = b_1 then h ▸ isTrue ⋯ else isFalse ⋯ else isFalse ⋯ + | Theory.Model.AExpr.const ref levels, fn.app arg => isFalse ⋯ + | Theory.Model.AExpr.const ref levels, Theory.Model.AExpr.lam condition domain body => isFalse ⋯ + | Theory.Model.AExpr.const ref levels, Theory.Model.AExpr.forallE condition domain body => isFalse ⋯ + | Theory.Model.AExpr.const ref levels, Theory.Model.AExpr.proj ref_1 field major => isFalse ⋯ + | Theory.Model.AExpr.const ref levels, Theory.Model.AExpr.natLit value => isFalse ⋯ + | fn.app arg, Theory.Model.AExpr.bvar index => isFalse ⋯ + | fn.app arg, Theory.Model.AExpr.sort level => isFalse ⋯ + | fn.app arg, Theory.Model.AExpr.const ref levels => isFalse ⋯ + | a.app a_1, b.app b_1 => + let inst_1 := Theory.Model.instDecidableEqAExpr.decEq._unsafe_rec a b; + if h : a = b then + h ▸ + let inst_2 := Theory.Model.instDecidableEqAExpr.decEq._unsafe_rec a a; + let inst_3 := Theory.Model.instDecidableEqAExpr.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst := Theory.Model.instDecidableEqAExpr.decEq._unsafe_rec a_1 a_1; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + | fn.app arg, Theory.Model.AExpr.lam condition domain body => isFalse ⋯ + | fn.app arg, Theory.Model.AExpr.forallE condition domain body => isFalse ⋯ + | fn.app arg, Theory.Model.AExpr.proj ref field major => isFalse ⋯ + | fn.app arg, Theory.Model.AExpr.natLit value => isFalse ⋯ + | Theory.Model.AExpr.lam condition domain body, Theory.Model.AExpr.bvar index => isFalse ⋯ + | Theory.Model.AExpr.lam condition domain body, Theory.Model.AExpr.sort level => isFalse ⋯ + | Theory.Model.AExpr.lam condition domain body, Theory.Model.AExpr.const ref levels => isFalse ⋯ + | Theory.Model.AExpr.lam condition domain body, fn.app arg => isFalse ⋯ + | Theory.Model.AExpr.lam a a_1 a_2, Theory.Model.AExpr.lam b b_1 b_2 => + if h : a = b then + h ▸ + let inst_1 := Theory.Model.instDecidableEqAExpr.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst_2 := Theory.Model.instDecidableEqAExpr.decEq._unsafe_rec a_1 a_1; + let inst_3 := Theory.Model.instDecidableEqAExpr.decEq._unsafe_rec a_2 b_2; + if h : a_2 = b_2 then + h ▸ + let inst := Theory.Model.instDecidableEqAExpr.decEq._unsafe_rec a_2 a_2; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + else isFalse ⋯ + | Theory.Model.AExpr.lam condition domain body, Theory.Model.AExpr.forallE condition_1 domain_1 body_1 => isFalse ⋯ + | Theory.Model.AExpr.lam condition domain body, Theory.Model.AExpr.proj ref field major => isFalse ⋯ + | Theory.Model.AExpr.lam condition domain body, Theory.Model.AExpr.natLit value => isFalse ⋯ + | Theory.Model.AExpr.forallE condition domain body, Theory.Model.AExpr.bvar index => isFalse ⋯ + | Theory.Model.AExpr.forallE condition domain body, Theory.Model.AExpr.sort level => isFalse ⋯ + | Theory.Model.AExpr.forallE condition domain body, Theory.Model.AExpr.const ref levels => isFalse ⋯ + | Theory.Model.AExpr.forallE condition domain body, fn.app arg => isFalse ⋯ + | Theory.Model.AExpr.forallE condition domain body, Theory.Model.AExpr.lam condition_1 domain_1 body_1 => isFalse ⋯ + | Theory.Model.AExpr.forallE a a_1 a_2, Theory.Model.AExpr.forallE b b_1 b_2 => + if h : a = b then + h ▸ + let inst_1 := Theory.Model.instDecidableEqAExpr.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst_2 := Theory.Model.instDecidableEqAExpr.decEq._unsafe_rec a_1 a_1; + let inst_3 := Theory.Model.instDecidableEqAExpr.decEq._unsafe_rec a_2 b_2; + if h : a_2 = b_2 then + h ▸ + let inst := Theory.Model.instDecidableEqAExpr.decEq._unsafe_rec a_2 a_2; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + else isFalse ⋯ + | Theory.Model.AExpr.forallE condition domain body, Theory.Model.AExpr.proj ref field major => isFalse ⋯ + | Theory.Model.AExpr.forallE condition domain body, Theory.Model.AExpr.natLit value => isFalse ⋯ + | Theory.Model.AExpr.proj ref field major, Theory.Model.AExpr.bvar index => isFalse ⋯ + | Theory.Model.AExpr.proj ref field major, Theory.Model.AExpr.sort level => isFalse ⋯ + | Theory.Model.AExpr.proj ref field major, Theory.Model.AExpr.const ref_1 levels => isFalse ⋯ + | Theory.Model.AExpr.proj ref field major, fn.app arg => isFalse ⋯ + | Theory.Model.AExpr.proj ref field major, Theory.Model.AExpr.lam condition domain body => isFalse ⋯ + | Theory.Model.AExpr.proj ref field major, Theory.Model.AExpr.forallE condition domain body => isFalse ⋯ + | Theory.Model.AExpr.proj a a_1 a_2, Theory.Model.AExpr.proj b b_1 b_2 => + if h : a = b then + h ▸ + if h : a_1 = b_1 then + h ▸ + let inst_1 := Theory.Model.instDecidableEqAExpr.decEq._unsafe_rec a_2 b_2; + if h : a_2 = b_2 then + h ▸ + let inst := Theory.Model.instDecidableEqAExpr.decEq._unsafe_rec a_2 a_2; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + else isFalse ⋯ + | Theory.Model.AExpr.proj ref field major, Theory.Model.AExpr.natLit value => isFalse ⋯ + | Theory.Model.AExpr.natLit value, Theory.Model.AExpr.bvar index => isFalse ⋯ + | Theory.Model.AExpr.natLit value, Theory.Model.AExpr.sort level => isFalse ⋯ + | Theory.Model.AExpr.natLit value, Theory.Model.AExpr.const ref levels => isFalse ⋯ + | Theory.Model.AExpr.natLit value, fn.app arg => isFalse ⋯ + | Theory.Model.AExpr.natLit value, Theory.Model.AExpr.lam condition domain body => isFalse ⋯ + | Theory.Model.AExpr.natLit value, Theory.Model.AExpr.forallE condition domain body => isFalse ⋯ + | Theory.Model.AExpr.natLit value, Theory.Model.AExpr.proj ref field major => isFalse ⋯ + | Theory.Model.AExpr.natLit a, Theory.Model.AExpr.natLit b => if h : a = b then h ▸ isTrue ⋯ else isFalse ⋯ +RECURSION WORKER Ix.Theory.Certified.Ordinary.Shape.checkConstructorTypes._unsafe_rec +{β : Type u} → + [inst : DecidableEq β] → + Nat → + (entries : Theory.Model.Environment β) → + (shape : Theory.Certified.Ordinary.Shape β) → + (source : β) → + (ctors : List (Theory.Certified.Ordinary.Constructor β)) → + List (Theory.Certified.Ordinary.Shape.TypeWitness β) → + Option + (Theory.Certified.CheckedClaim + (∀ (ctor : Theory.Certified.Ordinary.Constructor β), + ctor ∈ ctors → + Theory.Model.EntryClosed (shape.familyEnvironment entries source) + (shape.constructorEntry source ctor) ∧ + ∃ l, + Theory.Model.TypingClaim (shape.familyEnvironment entries source) [] + (ctor.type shape source) (Theory.Model.AExpr.sort l))) +IMPLEMENTATION +fun {β} [DecidableEq β] fuel entries shape source x x_1 => + match x, x_1 with + | [], [] => some { down := ⋯ } + | ctor :: ctors, witness :: witnesses => + if hs : Theory.Model.AExpr.Scope shape.universes 0 (ctor.type shape source) then + if hr : Theory.Model.AExpr.ReferencesIn (shape.familyEnvironment entries source) (ctor.type shape source) then do + let ht ← + Theory.Certified.verifyType fuel shape.universes (shape.familyEnvironment entries source) [] + (ctor.type shape source) (Theory.Model.AExpr.sort witness.level) witness.witness + let rest ← + Theory.Certified.Ordinary.Shape.checkConstructorTypes._unsafe_rec fuel entries shape source ctors witnesses + pure { down := ⋯ } + else none + else none + | x, x_2 => none +RECURSION WORKER Ix.Theory.Certified.Ordinary.Shape.checkRules._unsafe_rec +{β : Type u} → + [DecidableEq β] → + Nat → + (entries : Theory.Model.Environment β) → + (shape : Theory.Certified.Ordinary.Shape β) → + (source recursor : β) → + (mode : Theory.Inductive.ElimMode) → + (ctors : List (Theory.Certified.Ordinary.Constructor β × Nat)) → + List (Theory.Certified.Ordinary.RuleWitness β) → + Option + (Theory.Certified.CheckedClaim + (∀ (ctor : Theory.Certified.Ordinary.Constructor β) (i : Nat), + (ctor, i) ∈ ctors → + Theory.Certified.Ordinary.Shape.RuleFormation entries shape source recursor mode i ctor)) +IMPLEMENTATION +fun {β} [DecidableEq β] fuel entries shape source recursor mode x x_1 => + match x, x_1 with + | [], [] => some { down := ⋯ } + | (ctor, i) :: ctors, witness :: witnesses => do + let rule ← Theory.Certified.Ordinary.Shape.checkRule fuel entries shape source recursor mode i ctor witness + let rest ← + Theory.Certified.Ordinary.Shape.checkRules._unsafe_rec fuel entries shape source recursor mode ctors witnesses + pure { down := ⋯ } + | x, x_2 => none +RECURSION WORKER Ix.Theory.Model.SetTheory.Tower.BoundS._unsafe_rec +{V : Type u} → [Theory.Model.SetTheory V] → Nat → {n : Nat} → Theory.Model.SetTheory.Tower.TeleS V n → Prop +IMPLEMENTATION +fun {V} [Theory.Model.SetTheory V] w x x_1 => + match x, x_1 with + | .(0), Theory.Model.SetTheory.Tower.TeleS.nil => True + | .(n + 1), Theory.Model.SetTheory.Tower.TeleS.cons A B => + Theory.Model.SetTheory.Mem A (Theory.Model.SetTheory.univ w) ∧ + ∀ (a : V), Theory.Model.SetTheory.Mem a A → Theory.Model.SetTheory.Tower.BoundS._unsafe_rec w (B a) +RECURSION WORKER Ix.Theory.Model.SetTheory.Tower.FitsS._unsafe_rec +{V : Type u} → [Theory.Model.SetTheory V] → {n : Nat} → Theory.Model.SetTheory.Tower.TeleS V n → List V → Prop +IMPLEMENTATION +fun {V} [Theory.Model.SetTheory V] x x_1 x_2 => + match x, x_1, x_2 with + | .(0), Theory.Model.SetTheory.Tower.TeleS.nil, [] => True + | .(0), Theory.Model.SetTheory.Tower.TeleS.nil, head :: tail => False + | .(n + 1), Theory.Model.SetTheory.Tower.TeleS.cons A B, [] => False + | .(n + 1), Theory.Model.SetTheory.Tower.TeleS.cons A B, a :: as => + Theory.Model.SetTheory.Mem a A ∧ Theory.Model.SetTheory.Tower.FitsS._unsafe_rec (B a) as +RECURSION WORKER Ix.Theory.Model.SetTheory.Tower.PropS._unsafe_rec +{V : Type u} → [Theory.Model.SetTheory V] → {n : Nat} → Theory.Model.SetTheory.Tower.TeleS V n → Prop +IMPLEMENTATION +fun {V} [Theory.Model.SetTheory V] x x_1 => + match x, x_1 with + | .(0), Theory.Model.SetTheory.Tower.TeleS.nil => True + | .(n + 1), Theory.Model.SetTheory.Tower.TeleS.cons A B => + Theory.Model.SetTheory.Mem A Theory.Model.SetTheory.univZero ∧ + ∀ (a : V), Theory.Model.SetTheory.Mem a A → Theory.Model.SetTheory.Tower.PropS._unsafe_rec (B a) +Certified host adapters ROOTS 86; LOGICAL DECLARATIONS 9682; WITH RUNTIME 10957 +RUNTIME EXTERNS [Blake3.Rust.hasherFinalize, + Blake3.Rust.hasherInit, + Blake3.Rust.hasherInitDeriveKey, + Blake3.Rust.hasherInitKeyed, + Blake3.Rust.hasherUpdate] +RECURSION WORKERS 130 +All inventoried recursion workers have safe logical sources; no partial opaque source or executable replacement is permitted. Runtime diagnostics cover Ix and Blake3 modules, including private constants. Lean/Std execution and the BLAKE3 foreign interface remain external runtime boundaries. +These host-adapter roots establish semantic claims under the enforced profile and explicit model premises. They do not establish full production-checker refinement or Aiur compiler/AIR soundness. diff --git a/Tests/Fixtures/Certified/README.md b/Tests/Fixtures/Certified/README.md new file mode 100644 index 000000000..0ff6ec05b --- /dev/null +++ b/Tests/Fixtures/Certified/README.md @@ -0,0 +1,27 @@ +# Frozen certified adapter evidence + +`c7-handoff.tar.gz` preserves the 53 original certified adapter source files, +their authenticated patches and source identities, and the historical +reports and regression corpora. Its SHA-256 and the source-to-local mappings +are recorded in [`ImportManifest.lean`](../../Certified/ImportManifest.lean). + +Run `lake run check-certified` from the repository root. The gate checks the +archive and maintained host source identities, the exact declaration audit, six +native test programs and three command-line corpora. Generated source and +envelope bytes and process output must match the frozen evidence. JSON +requests and aggregate CLI records are compared as parsed values because the +maintained Lean driver uses different whitespace and object-key ordering. + +The historical VM pilot and packet producer stay in the archive for a later +change. Host reports are compared after removing only the `pilotDeclined` +and `pilotDeclines` fields. All source/envelope fixtures and CLI outcomes +retain their original comparisons. + +The archive's historical Python and shell drivers are provenance, not test +dependencies. Current tests use Lean. Execution requires the usual Linux +build tools, including `tar`, `sha256sum` and `timeout`. + +The complete historical Ix base commit is not included. The preserved patch +sequence is sufficient to reconstruct the selected adapter files because +each first appears as a new file. The historical reproduction script still +requires its original base checkouts; current Ix validation is self-contained. diff --git a/Tests/Fixtures/Certified/c7-handoff.tar.gz b/Tests/Fixtures/Certified/c7-handoff.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..38e00ee7e1667d2ee5f408a7875a6259d618f605 GIT binary patch literal 3510635 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%0D@mn}KaLQoUFcH)u(POmy5KY_g*o%+E#HdibO`gZ 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�>luxt&GLvi{gEc|x86V9DqL)r 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≺H9`t*PXb73qS?c(uF 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%0DaSXHJhmnxm_ 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%0A*33(;@P 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�>->_v=MHm&>~~ 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 z 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

oDzxe6Ngwk-7;uVVMZSp6Q^@s(UI75)?_li~Y%N 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 z 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≤Amnp8b>2WyNwOW9f{}1S4#G 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

%Q=o112i3d|EzxE? z@X5CUlO-FB<8{_ZckA>Tj?_k~P^7L4xT~snCBbM4En+df`M2@s zqx32o8tK5j4H&+t1xJMFM4hEiK>>46!`1qQLPf}xmpkUKn|!Y3U}mCZs7-F&^~+ zO=b=(#JwT_^vR>6_sID}fj|Zf6TCnAO00-XkQNNTOXkY++&v*M(ko(=ZI$=M?72*; z_mlc7o6oM)U3{zsACNe_alZ44B@RK~HT3GtwABJ$ABhZpz7>XgeJ+bMS8Is8b8wSU z_&Q&ub%A1Wx1LwbUM5lnLl}IsOUGsI96Ebq(d@{v0hNV3Ef|G~k$5Zc*Tcoo8UGTe z)4*l??(ZV-FxU#hZfY{fOd(!05=L6z3H3u`z?$CC#mFoYd<%j)lJRt*&kT%I-4EnG zA*;;YaGE{s&39JrsmaF%MIE|KGj295T63~i*1PMDWYNTDQT$zFG6^rsCXchE^X3ss?qcAV^e*Ve8D;P$Q=38EZJ@&Qzo~23LdK+$8Zg)qtLEDCXf%sY9z{G ziYOMY39{MSU#zclJMv5^F!95<>gL2vFQ0j@UH#^v@6H}vDaVkbkac;ETDHTPd{GCYEbtvMK3)vZx;RO(z_a$xxPO<`(>>JINW-*{p+ zXi@=iP=Y`t4Yh_B{Hq|8$sk1wIE{cPSnuPvx4AutyPZC;!PGboSshcpd`J@1mZ)hIb9n-J4SvBi9EC$ z7iBA0B_9YdKXOtgNIwNRz42+cS$X6!Gpl%i@r8q`=c9DZ1F{+~SB9uoPMP7ESsQzT z4jg@UaE)U6V!7b@#Ky!E3=iMimK!D9Ozh-t>Nv-Xg)BL)m3-zP)p|N_SaFQNg#o00 zicv2qX=2wDfD)EGf26rRlx$FfiHf<`lQzM0#$19(;Q;!HMTTLX&>2+2B3CF;kprF} z=?t8!DNW>0Bi{9M4DheQ1TW5JG8d+{2`?Z7nI#pmMODx}VI()R9XlKWtzGV#y+^%s z`8efdVbAsuN?zwFSrUDu(t9)zr;<;PM>26r?wdj1&PsbPnl}XJ7XsPY z8R^RLw-dg8lQB|Tl*W$T7iYRzrcSXUdu!uDBL_pw+Nr`ZF3I=cyuiCcc%~H|u@#8_ zLtwZyMn(98@S9`Zga60Q0r`jlum{FAxHlk?xWNxc84<|(+D@ShFt|vl;Qk6wmTT|M z9?bMAk(5Gkes(^Lo!DPu--Yy#aDAJ4Z6Q$*%M&m!giIkoy|k4{inpxq%a7;xtyD-f4DJ;Azkl z==N@Ki(4Zz18Z%odCg@@c#Tz%3;K`Qs{==uH{9V&%`Wa7A5Nz%&>KZwLs%cDB2UfZ z=ERl9$Qo)LGp8Tkr}eP(;@U5dADnn>m$LU%PwNr~hR5RXg^oLpoezzw1RK^qUZ0*R zFtDc5ux!l+ZXoLpV_BZcQxzTLUl{aC(N*q#NyhfLh6dLA^IupujgE81+)_SNFmda<~B>> z-Hd_4z9UCQ(I|}E3YH21d9j}d5a4fUT*N*O@*NYc28(!UPe{SI!7y?eXb06lBUTO| zV%(&C7aNF4;K(!Nc49J81zb-h}bc{?<$69rb8Zqzj zopRq^&0l%#>iyRpqVef2HrxDy9;E5kfn{9L46re{6_=37_IImz?C@drhPl>5b4*g# zl+_<4+sNMwi*nrf_?hOB4@>v2W$clow6e)+OEWwfpPrxlR?y+2*kG}6rJY5^8OCCZ z73Yp$SuS)w$~0h^b?d_m50kw`apP>T4c-M3^k+8~-C=KPrQfzk#I_ z$ZdF+HQy1T>mXDoolTt}&Sc03DKRn!5keL&`w7_+?1|?(yq~;ZlHoD-bvRW$iP5I+ zc(;HxdOz0X&<@A?4ubvCBF|c`rkAOJfT-J-31lV=zLF0f8bl1W_PTG6(OrB`QLB>I%MJ7{fvA;rx+53Jw~fHk}rub*wuP4^{;Xa^naO#VB1UqCYN znJ&={VBbxHAz+j{iN`TVC>$f3g5mw;;nZR(Fvn_mPthxb1V-%WR}U{T)^Giwyve3z z=lPbBZg6>cJ?b3S``t=eP^)Ta~!=&*e%!>8-mu^)=6%8SlN4 znEdOUuu}S>c~WBW)ul7KmW7|xkvlXRweRecMP?^bL>y`__M`=eo~$Y~j?$sDV$he` z6{;Au{F@28?!dxn!2#EiGHLV3h4*c~`|SI)WA{1#MJM!ceuH%!HMV4{7*CAv{;cs^Em5DC}S1O=_MW_rKBIcZp>?{P#D zk&}LCu%)*sIr{2VE1CNb zO_E=mpB^6&Ms}HP3!Jv(sr%Qb+(60A>-Xp%H@&iC|MKM7!N;NhgL4veF4mLI zF`!|0Tqb9L%nQf12!bd8J?f)4NruI>-(E2Bj<8en^0x+`dW)pCiMMnWvdWdocjE0u z^iXDBj_C5NM8uTVxbA-VYPXQ5ywzz2j>+U)eCB4t(OcHbat_CD03q{5deTJeH=MM4d*RzPbbu7=R3@I9YQInQzQ~f+ zv-_7%P*c&;!nymFP5&=E?ozz`3~mKs4y_)5LFMT>5atqq?n7WAV?99j#L4@zaF3uK zIvVC9Jl_q|8(d;G=Jckot18pZlO8|T!k*`B6{}!3+8R-fBH9)>L7*dEKMJpbY9ZJk zjN!yJizeGI_}x2veD)y)T!xdgX;8+Y1;SK-<~nQ@ut9k;r`%srXqI}B^kua zulg1bSAN=mxV9Rp9j!Jol*v4qt&>h$pVX^)(1M?N;CP z$}6AwOrG@IsR8|DI>o(hk>NhvV534R^w>rH_TF!M_59d&*ZG$px%LMq0%8pBJB9j? zu#coE!rt8$mXTp%8zcj|ZguM{>-y$6EL748GZ5CA*_19^yE}M+%>1txsH3}azfGcM zbr%%GOV{h>oQ)G3+S@&%Ar-S=iSEV7?iDkJp3{#m-E?n$Dd%e|JnFU6H1k1O`%zJC z8RLg@E-ji@tzeZV_QKseKr|!IA=KZ0R`|&m3s#(;%gsCu;C;b6P7&<~Vt>QRY`~1? zHmwBGV%0yE0_hG87T|;T!LjmDC~tnj*xhe%zhdM-qCS?*Cit-?aCNu;bLBIqv2*()Q|5CLj@`2^&hb}M8;Q+{le@Gy{F6e;ZQDtl zcnk~a)KDA_aJ;wF&*%Mzhh=-O^oWQmtJ#V;9M>6GYoUC_?&XskB^YmA`~Bm(j>6En zxNsA)n7_Ns zF86fS)t>hT2X{JvYuSVtuTMw1(XDQ^uWB?r+dtWxWz%D7YjV6jkZujFZkd`$x~$ho zwQx(|>+zt~HN>&kC-Yg&2C-9hoAvH{F?^Awubj#!i8-$(PUc@M4v|3UAFD;CwVWS5 zQ(4re>2a*(+7ZNsrE-1*m}1#UsN*wIxxq!av5qr{z5=jEt$5SYLTYHYrq=nw$E9v| z(ocfKhf>INrLDa$t!@dIEDsU%-Pho_K=JH;nX89bWC?#GYX$lq!*iQ0Zs-mBW(&@^ zedBFIw(+73R+4%_GbOSD0?z5ArhmUSV~SLT8x-NPYvF+WIS8eR9()1FGuY%%AMj%& zAx|p6WqWx;mw+Fl2z*^X0B=eNH(J97%IWp!zAyFYQ?QbY%b&pwA|a3r+mNQtz4nnp zh80ShaN8bo_@NW;;v{p<1-*72el$tqIyOy&TX^KWx~DdN>J^xyolar~no={qs+^q? z0SbP@9=5w_NK}q1reHxrhvqQ`^oV(bBMHQU&S*X1jCSYl#wN;lKq+@BRHY}0YVNSY z?K_sfxWp$VJ{;t(OfGoh_BQY&sB;^UF)}={*wX)~TFLFnM!C#q=~7Cw6IUb!nabr@ zeYn^wbFN{@diwPL8s8WJ>w`y{$UUckI5$&gpq@C$Cc^;=PQ-Jl;G<6?u$&ehbUowq)HjZV?0aJ z3bU;B61O+oeuYBeHf<}3J55I_Y0>M7vfZoVy@T*OA&I~-4T#{zyxpa||H&@p1(JI# z+ggj%e@# z8=o$Hg>^ZTrHXeJhMCkYefZ$r{+kE8wRX+LjWd70#GidaJLRn>f#Q#g^WNFOCbIaq z4uPcZ4X*t=kdSZw0Fnvf9TuqwlyLbhI=%a9k;CA!IhlpM;TEWJlGmZi(LrPMO0{m% z)k$Qe+En)b8!ESCxs{*3eaz;QN+T3adNWP`gy)wD$91q=#*~Wo&WVCkdtCxJy~CBK zBh|)A8%s=0^%0=)MZSyz*yoJfr#^7O4)#>^EiX4(4m!;4N~X(udsk4HXs2JHcZ=II zQl-J!;0a_t;7=65CLeb-$ZA?Dc_7U4>zv0jLF+DS+1{&PCbdaI!+pi$SAzR1L$gA6 zd^8CeL}rRz&Qe>y#AK18`^Dw#@?%Qz?8kQwnj{1<8oKAmiLAY6NZqsR#!S8qQUW@t ze`n{3&7iX54=(nkA7(9YG7+24Mt9|tknkq3bjsj%eKMjC5@rpM*60q9m^jh>GYc}5 z%){d>%CKXgdO=e-apY37a|4yBPjvYb>Wq>-Ywo;C-z)qmR(i|S-V1|m_2x1%Hb3rh z5WNEz!;Fuc&c?mCu;Pl&?7ho5LX9c@3fmi~-{x36>5I9uKE+^>qSg`J!p{{dxwx#M z>azhiiziv;{zs~Bto@`V|NKsapVa}=gORhUO=P64_Bo8abU7$&^A-#+;3z^H;BSVO z5(oKV0P#(bMLdISD>OlzP+TJuIE<3Tl8rzb?iY`*<*gWuHmwmVtTp?CYk+db*LH`m z&hkQ(tnM%8cF!w`kdLAy*&CJTK{M+M&&=`x61tNN;)aAZ zXO3Y9t^7^8H9~@B+9W3EKi3J3_%fs7b;yBPsab39d+04FJ1yN73QieBtTE9y?Jkq~LrxzLv)f7u8NoPGPHovkDf5~%-df@Fh;;-4l+E%^Au1Yr)! zkMk@KIY>QfcwFnbPH;Us$tDZO0_vhhWIfzuny73BtzFq(c)vq$X@G6Xi&t$HuXbu= zFxLj^$JM%2C+c>?92+N1+lkSmgLd04)s}mK4C5;RNR+=6*+3#hEAjzWg#hQ&*^3LTORiRFm2Ck_IqpT$YM<`?VV>k5C$7kZijy|+5Ta? z`j92MaJ=Y$8!sYE1_PsG?()!cggNBqRGO7JDd|{SSx3eipE8E8Q0fm28!+-Ng{hq@ z7QK(txoCKJpxR-LtswJBlb3~TjN6xd2gkU)U4G^kZH?NEw%+l*?Kue?>|)thJu7kn z_b?hxE|vKXR`BV9?j#Gj{$1x@L<8mCB99x7b7b6WHV3U%m3AmCt+=@Nlb-&wSq-lq zW=b7iTC>jxm-ShtWmL5yMJjq*XR7ex57}pSh96Ye^lX3Ya@)KhtmBq1=6dO?ch8cT#Z-Mba7fc6HDPkrkCl z<-L>7K8J!CJ+a!G*aIR*hM$WZaFCo9KQrDxNL=Z_9!rMDZPk+9M=A4Pgs)!tvgbuV_`4qA0LCcD(b)K^HDYDvO-NKFSN5yU~Dm_bV{I2=ME8nqNbDNiy&@!2s(>Lz=P;zZ-_h;4X zXSVj{Y<$ufs*w0dXx+lk&qVfo-uL#w(T&Ib?}ZfRbE>x%5>jh4NeE^F$V-%s`}5i; zEOPcF+iTGD{Z-|b7rlLUV|Cz$Ngl2nyzU|RD0Bq#nD>Z=krHK_5Vc|q(P&kZ1I$Q! z?imI<0-4ODf&OgLWHAXj%`$2#V!}jM1U#0ThcL25@2Ge-(Tb>VkSiUlDv(UUi5|3? z{gH_T;PUJ4b18~0fyC-3RpILDe(rX(TB7@Y%zlvQ?`X#`(Z3RZFgU<)hg7oRy2me_ zosI0~Sd6QNcU$VQ zV1sopKD?~TsSPpw`cXA^OePbx+cJneGR#E-4;_{a+R=O+Z3fL#W;_w!->bW6fV^6Y$o6>p+D&0iRLAm#vC{+m?~R+c4Jrl)={LAKeCaeg?CrM2#rA5T*QRhL0k`qEB?>$hDbdG_)QK#rpFP9R&#EDxxO)Xj zotu&AHX)lgJQ&Edzkk!KlG1bX>ejXvuhtsoxs&Lk+xnI4l3n@!_ibbR)8@ZgvH14G zdU@~|R}awkEud^mCq4RRXWBs2H3NjTp={H~ zb26$ZNMEY=N6#@-W7v*dwB4;noH9`s=*15d+|X zW621q19bk@iX-;Id`yD^qr+fhq*5a?6A$#=ihi9zz%E#1526c zIIl2(UI;8+V@f5!ba&Q zFjxf+-Fy&G1Ey0Em^sTAw!%;<60cMI4JHPzs{zMn!Vmslpx{T(MjxF3K{cRJh>o6^ zRR_-EOuJ{}1{yF)B+Nl_s{y=JL8UtHJp}Sopm&<%ek=7uC&A9P_Pe`92F7ZoK(P%sc z7||4IPY!CRGKrkK!ywFA&7#opQ>1;3H3*rt^W7j~>8=Pi>cf<|BWUwCL@UZaD zM%2)0MB>&7P;mP+eKrvu*OV5WJOlbW9G85w+=iZLzR)728l=)zb@g$#7%U=}vL_U@ zuM@DxVJDv40_Nr>SgMk3*+9Z_qr%0P&5)^nlyuuQ{H%TeWK~ze3 zs0G;NfgLatk;Y9YT#`vtS^Wt~nl=f~8#~7$*XhJfX|Q%f^dV(YV7hbW*Tv5Jp>(zs zkC>Lvc9A?of?Y>)f?%R`L0rYQf1F}>Z70jXvYVT!Z{dk&|9-`SIN&Q-l=j&Rho7mT zBGDjBjDu|Y>s0S#XVwY8(7WzFJWxrY5n0iiVcByZ-;zPdZ|%+4ES>zgP*-Mq963WN zkwx43ww6G6vbX*39)F|H58O2u-8 z2ls^YUXSDE#)9`)4a_R~W3hE{?H@ky!kH@sc;m$;wSrI6xbnX=d)LqDAYYW%JJV`# zzy~M7G8YmstNA#^S>Wp+@{J3xnw15UyGwC6nF5+eJyThfuT{u3u+zhTfZ{PDOCD^d zA`U=8Tota4eJom`(AN;44CIU^czMYGcq;4mkwKxcfQKgVJz&rdo_0_FW1!||jP6($ zF*Ah;isW{AafpnHoH3j>f|PJT^WGt3B;UcMFg*L!J+ZJAu^aBa*q9RGGr3Ap%YGm0 z#hoQ}99yMR`=mEy?NTSf=j1ix*$@1(f$253rs0*%XY){0#U)dKnWjxcEQLUc;1LX( zHJY%_JP?t5%+36cSa#zOxA+Oic(#9a88~Pqu1;x<9&WuUl>cyn7oORTe5(fU;embl zaCr@8Vygz7ydS<0$?R7G$-$@KkT^{Ad%5B6)j`sp(3bw!r|e6XwQY>9UNYj@+S+cB zy;5WUsYCta1_Q|cxuE1-7Mr3@iMF!~uiISkjNO)5veE)8Y2o3U3u#?~9yI82}p;ZXB04ja?BtN)}--8tPwwYwG z;`e#*V>a9pe$URV!PGPc(QsBKBVi*~bk0L?hm|=2f!*{m zvhh77C)>!MVqtc~n2rk3Ns%T=IFDzkySjaSCYg}DT zP=jTx`{fgIhfdiPGXa`x#bliqHilv&9%*v@ZA=s6>H^yHj9EmkN7{zsK<{wIO-F2Z zUdBZ&D`7SD&&>{Q>guB-o(+^(gXZjny3%`x&fZ&L^*F<9$MNS&le&+@7QWbGZCjCV z8>!G}sTIk#dzJAy@ujo!enW6-_yl99hml%QVUpy0A^X!e+cnuQyb~(toInT9F$zw^ zeqEDo9ouxYaq+SSTGadXDwA1?8{DtDu091^oP^vO!DfuOE(q2&5Mslc&ptR|0z3l3 z7)&oLBKnO%@{oN{1E6$p@bPxI_WAFGvg`+^kN?1;X)w*A8bdyUqi*Cfue2}7@X-*^ zS;Qu)u1Y7G1HV!#OzNo&1mVGOp)Xv~A@JZu5F$Owsu5xn_iVe@4|V}zn1_%}RQ4bD z)f9VEnGiVLOU6pzJDZ#Z_L}38bAJ1m&)_-^izPh*(i#_<5GRZPjZqW+hu6~$8bHuH z@FGheb4j}U-sx42+sn?cJLi`zcFw^?BM(d|~ zE^qO~ppOLUb{W4*KEGXW%LG@{-|6jih< zCEz*iHM6fS{rjetu~XB#dAqm4+!4@mJrR%X1G6BM!#r`Vpm-V=`T77Bz2Fi$KO&VF z;baFcD)o~q$v*()t+&olVij@)hu6hCNCWEX#QURZz?4l4Eyf5Mk6`5~!Y^YP$RiWZ z&fY6J=|n?FijI}RT(f(0Fai}TDDpqtix$$mXJvvgYiYocUOis(7HfL(IExduIrCIz zpI*V(*YnkEj~!nt@$-Nz{KET(Y=pA zgzyBoIqwu+5Pc0Ul{MC%kR{7u4%p8?Jjz@Pi2gSB-l&sGU8t4qKzxqVsM)&yM$mR> zR~!Rr;Yx^GVDQ2HZHm5QkgUYv6fGS{1Wj6J@o975=6+#R_vhwr`DF9cf@XauGc%3P zr;h2+DYeskst}k&C7}a30WqOLBYNbu;I7;TL|fEfZr~zDs5m8Z3m2VvVUW^3t*a8% zfg5ll16jec62J)Nd!u0yxCL;7??V$A9zw93#jjHt&Qlla-Ki1JW)5*HthT$m0Jo-3A2AB5g>b2L8GvLnOwFo1uINXG$ zPb-apnYaRv%JAp_ulup!)qL3fV_zPEpxK|0ZunySGA-WH;&aB|Pg9 z3FJCob8Q567-3y8PW*?(_@N2eymFN$5Ko0i8jh%f@^{0_wJuGRcd^ao%da$r2OX_# zmkC~8bJjR&+Xp0JT4^*m51_Vw_C_L}>#}|W6<*h8#y7TTEU0}A*1l&Eq&|a%$}d?2 zHIM+~7||K407M<>RV7Qy`vrCj&r;j}dvZ$=npxg}%7w0da^48oeyO2gHrW_=&P9v$iO-eyoYPX zZz~7p%B-~Or&DYN#@LisECS3-IwOir*g5HP4=nuxf?jjb&%j~wK_lwLEq}lwtSf`1 z6}JLuTR71*af@9*8)K1wjGqvh&SOWX`^95O@p>ld~-n*?iT@a&XC z7GVgHglS0^@RGNp@jpwo{Qw9$6Rm45tyW>4S67L@mZ=Hvz#PvV=Axkw!F!<}46z#4 z0s2u4$n|puxWzz9^P7P=ObdJ?)%2w)5PRIJRi2JYW+Wt3Jk9o1ClGU)Uu}gAERDqn zxJC>lkv9>HfXATxE9J`X;irzCDkRJ|u5Vxy6UCr^MRipwqD}KFiGb7DPhSB92~Qw5 z=fM(l?!*;*A{*!cB%$+nR@K(We?V`0tbA7i%may}x!}ap;!4Qqo>w@nAG-{f*k*|7 zx-q~CF2`SB8g6+vg<+++LS{+l36KNJw80W!ESSwyYGxB+uh|w= z{UX-Le;0i0{6r5LX(SNmLn-G`TjBwj{rB>DD%y!`nE@#%wS%e9&PxFKY&z~I%*(98 z7^gUn4lac|1vgOXx55`c_&4w8P2pm8Ca0rGp@>RMHmC-{);IniZ(jlrW!tx%Qj$s$ zT1`8lq>U8QN|v&;ply<*-CD|awU9!o5JHoP7A2|d#=d4r3$hGZ27|E;X1mVse~r86 z-k$gQp6C7E@BPj1*YCbBHD<2sJdgAEAOGbL2GdkwFue;R82Gmo+-vg{6lEyW4%R8? zL!4r_hjr=M>Q|hq3!6X2zr#Q3zfDsL_Ri)|&aui~m9@wj&_tagkCvenktUe8KI0qB zY&^FSK`bhMM*yT7<=j+68B&!zz5;}U-dSLFVU`1}KF4k($wTxLbQPBFDA{`5V`To< zd-jq|p^6S^q5Q5^oThJ&Bq4~V%Lc8CQh88((j)QR=Z{)NkdAUrjOa+ty!IwIV?M}Ab z9x`@qkRN<|nl`dXmI5i4Hfii~F^~cBw7_r;ega)qzr<{HcjEAh*N{~ z-m64#aD+jz!sR5+e#JH_zw273z9r@9n-0hZ`HPouoy8YY@SP6t5PkBE3QV><3x$vP ziypYT1|1n+1SdGr@hr5q@Kz$O5FsM;>Or9T$CsyACpUikxGPksfmX;D7;PqgIHMiP zv^g-6H?^D$)@|3Gz;YF6Eop+FWQbhF!A7Zx-f!@RB~9=VL(xr+s$mn~s0Qwxt7y2h zfZj)Db76ojJPl}qFUCeDYZ&1Wf<8vyq=3|o{r0J25opc`DxYcG$e<-B;CnCNVvVRm z{-=PRcA?%~>yZTk(u>vioSA98f1fJ7d+Q0m##;l{GAm<7YEPx!(lPY}pEgCH9af@& zVP*|H&>8h7FlP)$& z-7#kZ#KyY?uJIVD3EStxk%XJ$Ic+yiT&O}MWe5W7Qed8nfOS^tInTbbl{Ug15y5p& z*Q4S;H^m!Wp79!S-(VzYVm?iH&k38DVMDqLc;nDs28l;gnHopyQ=}FOrJ&nZ(;ysf z;>y+4o@1^{qD|FAK_Q}S2Hd5y;pjmb58Dfir}=v*ho;afiH;8Bsq$-W-RcbFO>9%p zGu6?iLHd9*8Z4v?#XcTnX&=M~<6YR&RJ%tD#O*#-wo#q%;H|LTYO#-^kBC%i{hEQ8a^g!VjkaR{Zgm zbHh%cKFT*M6{xP+O86o`vHA&4>?X8q8JJSQ6LB5v{OeDyv#LR|fO96L; zDscE6EH5!=P1vdl0G6mbI2X3nC@Ba)bR1->6OZD&3XhGOIkx@eHC|)RZNH{Hh~WyG zCqrAH4Q)PwmHVv(-W+VRXDe3Db9SG(s2HoLRrOXy+a9N*y)jb;=@!Z8Ae=32M^iY4CV+FLwgdQmWnZbrLR?C^kzqtv%TRS+WaD|l%hC?+b0#nFvH%8jwht+ewv zG+MO%xkq1Nvtl)NWO@;gUkz_;kJd^qiV#kJe9y6U-ZI7};=zY7XD*vk?h~h3Hq98o zS_pvG-{Am$!Pp|`JdTEN;#v&>lM@bj{%^zN$#rljWO1l(BTETLWD%1?y4bXB_{P8B z};0QK|^&^=m+b-=Sv-0 zqGe4E>|_s=$?quWxT8op@cFn4^-hfMsiiw%TCVSS(0g>YjA1$rhI7LM!!{mL85tRA z@0DqiueddW?Y#!h6dhegsQ{QA;FrMz1uPg{g#P6vXfEK#;Wq+$=egsIFxSM0u$l=m z*H)n$BUl|5?GIm!<{u+KvIeF<3PI8zM|}$qqiMtBgbQ2};l*^g^JxP&ct+a>%?n9% zJDpg9zcT&FFE8J!&{?$Q5w;Bxk?rim$d%!m_`1v?)mm;pVzVajjh-4#`6Em|Iu2fY zu#DhjVX;};xyZ^3my}%qu@b-js`d&E7``z8Qt1hc4C3#*E8-w~cB?0}>kqx_I8G zoCk+j&KtRGL2N70p*Gq@T#k#(HO?aSH#A)}C8v#{&(r4@M=KU`c9h)S)5Y3{RUTTe zRPe0ieOcg&bH}SRK3WCrfdbQ=_Daa?k$LGlxtHr+%x_CE)roDXn#HEn$0V#jez@zt zpYG114{K{=541{0>Yf%D%{ye-gcJv0GA*DDK<%`DLd#0nlweVU%Caz%4g}V)lDPjW zV*Yk8DyY7Ili&_pUjB!|!|bPuV=R0v&g?J$hcAcF<+n6A8A@ zAnD7}o2cdLOWa+mG7SeCh+R@dfP|}_dpR{K7x?0#YvDgZ{{+Pgoq)19t@IE8+X`Uvt=Wg2MuP+e>iRa1;{Lflk7mBvL`4 zfMIZ4s% zs|@rAAhoRr(R2~`2*&Ucs!50d=Uf2iipi;q$}sP71Hbu7gb-VJQ7Lhyl~zVfr4Skk z5#TuXY6iXPtlSo0kvEYVsF^~qVed7>EMHf!@leaXwvbVOqER zKF>4BjC-0tq&;u9+~-|BFkC`z;tyh)+L$?NtFCj!^=YQxlLfaqzYMKoRB3FJ-2v*QMOnIyQImAlV5ey+)KdqlC$j zFJYkhl<QR2=V#6HKn$$Nw3i7UuTfily~ zR{fY*`+da)Y>pFdpmdw-2+>(HgLbeNGu1%(Ij`3aVi;;yCXH4mk?rVUc*z{`;D13K zB=j?l%xxl%f+t4C=q8-bqYX)@x-u73#38kLShwWYCG_DTY6klViH3A(L$53F7Ig7N z=8%D{TMi|h)@DQP@1bIBIUOpOEy=HO&FnmjzGc_ZxEWN}+(I(K9`otc{yyvn0`=rF zCrh_~YRW24jzTzkws#2P@b9>h-;{ut_rER)dj{1M3eHtIffj2vD1|M(U97{#oR8~% zUs}A&|NO%{&t^X>`C%WkrwtW6)6)4)6maQ63?D#59?gPo9odO(d!q=9dss8X=F zBwyn|AwoQAbW2?yVZF&?W(Ohg&s+g!Y@%R~!)xnngGmPaAg`BO8v&keDN|=aVDcL| zrgCqO!{K}eDPjE}-F3LMxft_B_lbY}YrFp1vstI03s?U2=p{>XXxJ_A&iZ#v|390xO;p)Ap5U+AD`EvmgIb@;?meZ;|Vg zVUT0QL8`s>3xT<4X*L(!LTPU!g+SX*DJtOj&*s=P>yOgRMbGtpzoCQxtOoiv(?#{Jn?`O z&M6Mk+lbKrFF`bXIBSuSPol2=qN*L|K{8#jNx-dh!}nlP3^_n7ex~?IjNC}aAC&U0yV7Ei0ullu^8@8hvca|;xK_IXg_kSOO zLHplK$Ov$JISOTjA+r5cU}(f}3KM*jZq}6n)qO4wxc7l65H$QrMJO3TZ)O^2vMG=r zcoLK?psCDYAt>$dWq$tjV~zeksT6xtU&U8XT{tHt7h4VK1ggF|rIt~l^JV9{2a91c z`p#<`z`^3lnBYRr2X=h(kL>ty#i=ZNn6iAN&9gDIyD0g!DhE2#rD&fje!ZhtL3=PCb){B1?XuxsiOvnOJ>v~b^Ujwb@-!ZLfnq&xy_9Fx^;lPS@99B93 z_9n}-iOAtQxl>)+zmUkCOBZTF;CswY;M;Q!G_q1k?`$VrS&h*9Q@PH{Tj)IODNILq z0$!w2uelaEv%)USk|eW()H9oH9(ynwNZcO8-Y$u`4g_HbM2Qnk|$T2mHu9*^FqDIcu&Ci<4OQh~$qAidpcw_6Nbr581!$piG) zgd zF3`+_FxjXVmF%+wkK%s;x#7PcH{o6dwhlC9dsu!S+Q}co(M$T-KD~N~Lax?mQZ(5_vj5ewZ9`*S$Zbi`k-(RYmVdXJDVSZr(`f0y^39?)v$OdCo z9RC%)sKwIm7s1`5Hm*|UVm{3CE#RoiPG-_O0ZEO90Nc0|!HstulHTCSVL1_kK?N3C z)AT#M3NH)c^~*nj*EVi^>2L7*{yMV!1+M@azu*Oj~i&CELGs4J6QtO=J0E1Ba0*+Hx8J#=snO%34^slVNv&f;)((~FZ_ppjasYN z6rjI^_;2L%n=K{;cCX;Qg~e6%C~v}6qF~5XCrhXhM-QStvW?^sL7HwB1pRAJS;%ZS zUl>@<%~c45+jJ(niC$ErUxvhw_|E=U2f>i|eLqL_Gm*I`MgmndO`%#joL=aLfG4-P z{J}Adkj#StqO!+HoQL>*A7^&yQx--YA-#F|(~y>7XGi@w3y+JJFND zA>L$R8CE!1aV$?E7lLv?cvQ9VZNu@#`i$}QEpz&D+ec8#2!&t)y(mTvYGi7lCM}g8 zp9r>Toa!26-R#hV_WY_4%9CF`&51)wM|Iy(yeODi{vBN>S7SE-UBU6{CzQOsar%5g zU~tP}-h!k$TaBo_)?aFK*oCD1oAYdY6y;q|ue%(Y3uED!pE z3j7dD(CSMax$ga#pFr{`yu;v+Qapqiu)38lgD?^OZ00aq6)PS5c$8m79{q01q0S?- z=TUj>6%A}#kG>;RxBNSdM$Sd&#{QgK_h?EwkLr$p8%b|w$d5MIavzc|e|N^|)%=oN zRhR4u@!Y&p#18t5Ads6 zS{8RePdl_|@s|zNI!5}I3N2jf`r>azt)?T8;8jE-!5JYq{C_B~RaO*id!TWw>-Jz``I!NGT2sU!Y4fH=jxuo4MwPB;zWT0r^Gz!vuF0|K>f1|sBS5b3wcR7}g4o`xj zQ3UF(Fw>y~nlW*N+GOwv-T3kV{U>$;^s2wf^Kr-pwBmG79H1l#_WbtWp?4&l5S&$8 zi@_=VTL`%4{LCusm2QC1j|_Z}6w62&5{#T=K!prx{KZC=65<$zamj zIjYx2F!_Jv%&6FvHAwF|_`nlNFc_yXw+5o4;vGajO4o`gY5LRTAKy;17Q=fbyAo`C|1B3~1<-d{|QgP-CLTZRXdW5V%1T&r-TbbWPXnz3b zh0oDvE35)Me?aH(hn8TR(H;4U(0nOG{S5@<@`TXPF+kT63Js%F7lG})F{R;@@Zv9} zVclPqh8dtV+#ORIAYY~ig5Ljw42)gp2tm)`qj@ijRo0-KrS*6w3&lu*(op#glm_+J z!JHdVgTOzJkQ)A79T-(Ka>gAp{pRd(GD~EK zlFWyxZ+yx`jT2dxRBe=M({J>3j@gT{9TVjy+Y1cxHn>yhbS>M)p^+PH zhm9MlyfnIN#YU>5hYjo43)*Vxuxz*8K1|>$f%YCA2Od%5A91UNM)i|lC!h+L$=Yyp zIr;#0^0kO%(2eYJ3I@pgM`%WRx(!AmyYaK}m@Ujs%@a^B{P|Gtl;6=pOJ&jz4PjCb zz%&*H*n8qeOF8kGLN{j9Cn#R};CIS8MXIjxtc&My1uP>AX+&NW1T@6ZRu_XceK|H! z|0%u-sv>xU6cJUrFftJrvuQ`#cE{=@lH3@S-dd-TqpVD?;Bd#rY;5z0wAqD>wm@k? z+8&HUu6|f#tQcR$$9`c9y06NsQuqYFo%_&QG>YH$Izt-rAxvt)M?L+t4Z;$-XB9b(AoX6 zq#zP%uhN1iMR9UfAG(N}d%D~aK*mr&*|YE^_FD9u3nDqpDie`|oEEHaOQ z`NfxGGZvyf8k&5HnJw#){8N6le%4LCKNCHl)sF4?b72EheqResQ@my!8q9~z*~mHN z*v9PMLm!KaVjlNjUQJ?=ENqh+Y)r$3hC_C;?A+CoG!H^x^g;C8CoBa;ovcAUp5NJ| zX@<55)s&DYBF9X=g0vB!4Q~UPjiBkzSD>xMNSWHqkCkr1Qm0ws$_tG5;-w7QJ{;-d zN9>$NN#W>>gz)-yypW9o`e=V!-hnMNC9$s>NO=SWB%|7CN1(O=$E;5ck{dc#G(9cn-Ab=1T%O@lspv zw7}!uipaXeQ#Ed*>5p6AiiQ8<%9t=f{OKfF)lcyncP1PK3H%ELM#2vWs;Rli*FaR& zvKSch9>D6z)7Wl_NG2GBQ8Bkh_(o{6lY=*4lxA=CB6Fie-%S%hs*aT;&eNs}F4}Pu1+zGzH$g zQ{s6HKUki@k*q}3;;)&sD$*MjRn=)eW?R&>mK+6L7!JfIugFlvF`NK@1MKfMIB}>a z1!fNiR6$|$yz>FCW7=F=P5Vlz_g{Z(E*v{90_S=h%O2lKpJ1VrK?;U8&Y z&7<)jP``C8vV+q4NX3XG1xzZv#>G3Su0lq~=7fvDGJ%X6=cj3?ca4wcC=9{;2H^*D ze*2XYV&p1uN#iBxsa{Cj6SVWgTlS(KOnTiYl}}oXR$WD%8`g2+Gw_Tu939uc%HMR% zibH)VKzpl9M)qzAApY*5Q4*MM$IctOmB0Q>75h9I-^(456;M4}&Cx}uNR330xO+7Q zuP}$0>G_ZyJgtN=bJoryVW^m*3Xk{H5p*J{Q`OLcXYgu}a?SKPO`@ZVT*iqvMUpS< z@mwKTUPvAoX5PheB_F%+KBynDtA`#ly+(EovzOa(r6Oz-ry|qohYTxyw5DbGBgXa6 zZ9p+`$`l?}zxpf+cE}F0woHgElsp~~z0Ib+Ku2M-_)b~R7vFEUZCkFnPieZv{H1H| zOrR#L8;H8ir^H1WdOqU$7@YlLE=RwqSIqYd-O)-5q@ED*b(6Wh6p2-}qWqw8toWy^ zGrQYNf9}hT zb)Dgd&Qt&>mZ9WX&-zJRloDwGESSn%0%AgSI3Q^WS@|_0(6aPDL`&Z!iBYl`)PWrs zzaazpZ$v?~QYgro*oQUTR74-7-rD1Zqg{M45dcOFB|f!6^ITud4dG7~p%yM#$%{jk z{$n9X6G|2PHX6TY{Dv~*X7U5Q{!alD7Th)_*MCw>QVmnpuyWj6#jd$Lq(@@V!Xeq9 zF9v|JCGHJvpr6Bn$-@_*=1X`DWbHO(H+)m`^FFc$`ZhO*Z>IS~2X+bm))+xz|pGlk1A78N)%?fmNc4 znSpG;#oZj6b8K8PADRxsw{&CQdDS9kp~zwX_~-U~$}qVDF@%$cU-HewiO(?jk1M|? zMtiqv;;&|Gxid(6M-(;}g98%5Zb`~l9}Q4k4!4rnF zhAY3(QYRkXix&=I+mW0usJKaR++37MF6hUKQ7aW`53t}I9wj{zqG%CWI(3Ls0h$S% zYlLsx=_Ab-4OdO`WTBvyPv$Z(kJEewADnaU-IPY9aqIWWL2);GCm$ zN}4?k4xW^+{3PV@%y@L>Hk%?x5L|&-%OHK)9}zSD?Y~4!4fq;y=(s?R36&210?1gx z9}PF`_|-qq=(}!Aqa54xZy_cKf&fh!H=f}+Za)sa%{39QSu6QQ3)gugdngDaIAaUu zHiX$h`9l(9q2H@+u&rMr*FzoE1@sCpLJx`vzB|Wn3(|x#<(1o?x4TSF>mx(|c{qSgBjx zI=3HU)q2#)InV#XD@idZd>!ssez(ll&2F%Lx8&>P{GRg$th*mGSp7DX5#Rg(BR4cG z)=TSsx=Zj)xdMHw>&;-5i`jlsct^r|Cd5w`MA$CikKRy+LWWJGkwoY4;kd*%A4@Zi zR6ncTT}=l{)}r%g%DTfN@TC0%FBQwoX{Omu(N1x9c1JCgIQ}|ls+Ii2tq=0=Q}ZI* zH{U(+=%&e&F95>#TFEA7++IABO(-5O&sjcMr2^a5JI|wIUx=v%?#fX?OK$tg-h5{9 zuh3`@CGIGad(_MimhR594)Z(LHOF6UuphtA zzEtPF{DYpYzs1n>YB|SOQBYxKW{_3D59X22+!WA>4f8o`NY+*0B|dkY?9L-rh}_u% zZ!?)m=>&YLsO=i0mlQH+un+#oSOeqxI4sX7@D<{lot-=ge|PQx`3<6aqKkhG0iMi9 z`!_x*;80Q!HBV4+p`(hUI)s#=cc!19#Lo(^;4)ma(b9(-KP0{OQR}4VLe@wiYY7)n zHHN=7fy+-i4lP1^?3w|AokhPHw-W>vo+>UTK=X;AYTJX5lE~M4V3GUl%I^xt7p+}| zm<%8`ZZ=GYI`|YqTJm0vtR?~-Ht-+hx8MruGrpqA_q3RahlPCkC^U9aYqy&bRCG6o z^abtKyT(rgr8gD&mR=poB7Qfg8XV0w3g2rv0&Y8Zpu+QL1KRFzhx1PZA?q@iQ;)-1H*SM(3!uTOx1sv(|9Ac)xT@);1>l=FuVxv%({~ zQ=&QXr$oLH1bZ_Q0p0)ss~I;psIBeU*8VAqF*X3`2Q3BF04oYd&^K+qzw}$Y?tI4p zxr`gH(nf(FiO)MLNUE;rKIZX)mP|UEwGIl4*<4n#0A0_9xRFT{lGb4a3xN0o6H=jJ za;6H8ba#-xM^T^AQ%n_A{n*&tGwkT~c~0n2%)Hy5ysv*%`JL2pr*UxsxJ_T$&g8Nd zsIcYQzcgN7aJNfHBgaQhY}(3;r^2MV~jo(Dsl-#PDJ$J8r1B0rwx1Ai%}ok zO{I>1#Q#kTxXq&p_v{~^<*fqjBIp7gXz9{IZoG6Q5*JW+4N%;LZyvn`Q54?C(JmNk z`#(|a`oTBqce#e_w=%g3X}dAY2X&N#5opSLe{LGo`{|p9r&W7aq_^f8X=j=*aM`1#cm9#$g~p*ubiOpU zT$6XDLL=F#Elt(G`-RQ2Bsb1lbqxEWK(lKX#}b{szw%a1Ii5i+y)zkzCl6cE4N-QS zfO2AI;Kb$Mt{3|yuIo$GnEq~Eko}X_yZuhikvP6Ue~IbJ32)+5m)u`d8dyy0-hmq( zk_ow`ySRTaex;ZGpz2Hhz8VedgPzw{$n(~0P|>JVRU z1gO*BFhc`B3rgQ7fhxyoLJL`rP;~FNQG5Avavex`vdX1IfHh7XLJiEoc%n59N0 ziSuzEp%H+}!}!OQ-%rhk8%}()MOT4gS`!6L0Yb_Il~M=B5qgA11vEQ2KpstUH}CAh zESa50qENFJ@dEDav%akT$)>yMUu9Fo#t1G?isLNu9ZQ3=Al3rL z2K;M$%|A3A?(P2s!^9`)>dRpC)+W9wdRP%--` z_dUgl#0GPRadFs%5vJEMa=dkiz9X_y_e>YnX!6ZzA8bSiaF0d)vOM*KRMxo z8`;X^>E)OkTNwTyR3|3tx6{|&^&-_O29E!&-yzvwK>HgAc(%~KGiU|%C}-m^CX`tH zRge>O*K9!qw?|b^k}8>D`Qkf3@%sSc8sOk+cY%<2w!t3SF%Af-u%@eavLRKdmx9{% z0L>HuU?r58m{K9QLipHEKAek^|Mo!s4N;)w6$`a`t=fVw_-`X-%m`9r&_eP?L4Nxq%AJIvLDlXnk-;{5krvRr~cFYQgX7-Cvg!#`%X)*COp6qxh zh{z@NaSBJIoOl$mb&%MqB7{#j+<-F|5FDfnsIL70WkjI362b-28>OiKu3wI6m?iy` zpb3PA33@|4>b|)u0&PRIHPCC!5=Q0WV|#k)o(FI+rQwCWbK5KgwMmbedOtomH-7HhVS2rVrFThh^vE; zDOn|?pM)?U;Ey)`RTXdn{i1k1{M|8WD}5!D>gFnm6zDoq*c0PM9MjQ|7Kr(ADSPO4{jO5c_vxI|!;LROS={&uMEe8&Y7UW~VA6<>`y~#QnBp>^sl81AcRazT z>FvQ>UtYo_!wE<5L-FWt9a0V1h93dQ72@V^GHL>%2{Rfz66K-5Ka1-x?NfEdbwDB8 z_qFroG{Ip@C!LsTk}R)9a(}w7W7H~P1H_VMA)dR683?>M?~{&sM{x-`l)B{<1dhL@ zoeEGZ0+1e0s7yk+w5}VCKyjajB_X8p=&{jJy|Guv5>kC+P9NyHzunDW%=C-a@4Pe` zzVn}XX#j&noGR~dlN$q9M1`rAfoch0=?AOHwdc7K0rhxQ7vJp31R-ov+xccMkoUkZ zDfNvuCt3j?nkc3^$9bE;@ia2xm)GslSat<>RMp^QNm7P8H{I2`@Ki=&^+h8cz} zMam&XXxZVCEQf*|l}eq-^VA}9KS{b%PO4OT>3*LAEM;-0i0KhvB@ z!+EcOgpwge2#&A;cqC{!NVXJqB5WA{g+ar+&9(|z0Rg7OUZHjl2qXSQ$citiizvdp z#qYb^N%DF>rH5dw9EyGIVAubwlp2)xm&DTw7_uRP$JdP^V>y~li9lDaa0U?}bK-I* z*=mlZq=*@M~{)cgV*SIm^>#uSApCB`@i>pkc zq6z>_0{QK0OmnM5!KWEkpiUyspGLTkW)X#7nUap0S^5hSk-emp`>i!@-+DpCB@ z^LXJVh|!5;idhG0^IXwIt1|j@>cPAxAb@>FPY`v9B9pR=&=P981SF-t|8J?M%}{q$ zJng@sq_$v0Z2t$MNv(eQA1kT<9n544>4QF&{4PCxo#IiE{2{+f0<%^UlI)7nQYs;oF6wO8{MEc(USBNv0>L2m53r#8|ba$*b=K! zLfTRPe0A0RzKgR;uPP|DXzJyzHhA4U(|4IsD8X9|Us_KkdI_MoYCe32KbOc0C2%f3 zxak?2w5k#qe8UhXnNglm{V4zF8J&!0nd)!jKNc$@icVS&ZEp4+Lvg)9D82H{g^ygq zO$_CbMNrH27fkXzR5(aB#vpjX9)TYn46h%=E)X|pxknTA#%r=GUd-Ca+iG8X&5_Ec zm#24=1sxCR*zg=W{{XkPF#RM`Elk1u{^|Gw&IWE-$8;o(rA2 zc#gx&_AfYJV)D!tb6%*FNQEx94=*t;SoCG_%*|7VK)$F|{G91$y?EP#{u=+?1t0co z&pi&QxfaWX79njE4Yh}%tE&pQky9?`4A@F#5&A_9oG5^*e+Eh@XgDPx4P!zC1sWcR zOA06vtIrD*Hjaana0PnbSC1^H^-N>g(pKL^H(OkWdqIIP^ z!f$TgRi#+2QE}oP_Kbg{(P#B-Q_JY}+m>xG{xNbs+NmICOX7*eDE3aJ-aSrLE7|;B zjfU^Gf<%oX+Kwm3=`kjcmM@+bu#d;7G+nM1Y(HOl=F1SC8lSWiSDttbs3{l5C;VMi z-iW`Fx+=_9NsGV-B9eX}B_18OVJx2aMvgh}+0@o?M1=$Rw81LtWRjPlLSsyB#&=o< zQl=qguMte)Q#s18MHA&PL7cBDzXtOdtWxk8$d*Lx{q)p!0R@$id8(rrH4UkvmNa#( zi>2)=9sSXV%Y7q{odntB^ZLBaGdF*nwgmZ~1amUO`DdJ!ihd!O4S0T}ZSXX~I_a_qtuRxOL5Y zowYq-Pg7`n_MN%DOFUrWwKbMF>*R*_N|U$Ev9MZPzHMdP9Y5eh`ODE93&q{btz}D% zeFClZ9dj>!I$jWGxHvni(#Lr6$t5x|FNVB!%gE(Z`mkA?ST@%8cNHnHg=~tvfiwKp z4HOAxZjuUy7UB+36VSNWwcG($YG2n`NGNY*C#m59I~Wn@udYVFB4HC`QC!Nx@V`q^!#R zteRjrMt=u{vK_C1LaKC81vP3C-d?u6h+Y|1|`MGKSAwGE|xn@HvK( z408BtYeD`eKFk5I&;H7$2BDU*!i{_m`RGAdkz`%CtL{x%ALTA^%4zk9oxK^2?h>jS z8R<9B@t{KELGsC}X4x~2|6FZj0G=ppycK7OfaSi1vPeW!EtG#-#>zT@=qy`WW{ zooFH}mo&8<<%M|qnj2hLDSh8>$@1$zk3F5a9D`Hx9(Ejph<_$$FJWU)T?v(6!Wcsz zDcr;43@xyYOOV1Kxe1$3Y_B#X+Qk~5$~9_11w#n(Py|CrYc8pa--J&iU+(IuZgk4F zA6bbhT&cDK=#OG(+4i=Qc~z&)(g%N9`PYexB=^Du4&k9>*)AZbG?l!olpNnImY`=E zx%Prpl=NfI#S%IDn0XG1q!%d3c&kj83N5%+VLq4h`NRnFx_|5S2@X}I{gaLTiQOUk z@;7f!ZZUtc<%dcswsw`M!L|Z^QH;v&=*c0ZSYdK>_@4oZXAAk@KY(OODft=7g-|ml zj`k=NEFo?8grwq$16VTpJO(6;5H>c1oO0p7`?s>oKXF=F*vE6lv^sj@$D!U+bv$`& z_#(}GGbaxwiZ|iRRX$%S83TMuMo%NJ*R8L>Z85LETD#AulFs>_?N7e^r2%_CXis)6 zJYAv5Jd;*mck#%h1}A|QQ?U8MJ|I-klLd7vz#f(lM6L4;P&pw!c>dm$m?h7w&Yzn* z(PGBg*C%EVDo%c&a+kk!`g(@_(l6`$#z*YAg!|hl?aHa#QQ!BJZ4qc*0L z{G8`IbTmKwkkfmb{Bh2R)tu+6^;3MBp>qg+Qxg%Q|FLDLn&83jamha}6>o@5VOy|2 z@u5B5F-N#9as~c-p!TarfOVkGo3$L8hYKr%vQhArtZm6t#@_d32-J^aL79j@FP__; zV4J+$t$i8EV}RC(l1}gavZKFJfQ5Z@>xxUHj`-c!bRd&AQ1sa4fJ138*>P}0-F-m9 z=5e38flY?X=mTXtME?Om3<52#s_==Ala-&UtbQSDcryIiHEO>7y=O1{CY(Qid|j#e z45sni`)B6ZS-a1<9cU3_h+8WRU{yPOcpgs&S%G;T1#9IGt{kup_EfV`@(mFmAerA> zykGCrKAg5IDCWDF{ew}Mn!WHh0{3^OsmnxA*zsG1=w2>49BOU@*^*H9YKOw4W8R=R z;ublLKO(rzLXcLGKFL6DWa2&Y3o|cOO_bAK#P3R^_tzexv#ZwL4A$-mCS7T_U6tA; zu8_(2Ou$9vB3?weao$Wyi7y6Ag<)ytYQ-ReZ{W z7azBtawBSD0hmTHg}>_(q}Rfm{afCv60N95ot`V0q_R(_8CO?;BWDhWe3M6t*kXqL z;GywDm@`2czbR0M@~D|jP0ONeZQ@@&;2<-zR%h4Q-4%1dhrX>&?%1#?As+|ILp5* zFGf?AZ$@%w?qkJQmz6v>w=wKjY!z4ckx>xIU0qBze#e(X95VUg0UbU{bY5P0 zX@8rD$DGOI?_7U3qhv^G&iZUE$#u`K-d-8EL2-Mec!_+oUJQFce$wLETT7E?pD3=n zerx8v*?CGcQpYWmIpkFmJzmLt(wA!!uOH}mx@?gaL+kpwk+#RY##$S4j)3-dTjP=9 zlw1rYj?~gQBczolug_lg;=rf9*WDFg4PLg}X1?@=^x=k*zUJ~+oW?Pl*5iKiZc^Jtn`m$o-#9BpM}0vpp{Ho!YCQW zQ5wi*K}FUCB!)}!Nuy-Y*L71lbXhjtoKMPri&LQruL+s9A=#ndu7r8H^cR+K$Ww6z zv5PJA5Y#dQ&0p$^EkM<&NMV#_xQ>CA*G{7?+j~^Qt=ho9zG&4!kM2dtt#T)2l%4DC zag0W-XVKUSv3o+l7W;@+suT~9KcE`|%Dc+at&H0 zvA%_FB64ibR}(H-Ckt6eUgC$m0t97xX*YiQ<%={3OkU=e80)r zwe>OSMQJaubAy;0E8Aeiqe~9k*z-<-gs9?L(yE6pHfAWSOmptJzc+S>T>OULUGnTz zO!w-R3fYy1-}Sq5d6MB;jntO!`l)=QG^8*d1$)H^D77vXD+=BHKDq3?J@`XM-%2gt z+0%G#h{KPyxi>7WZMH0$Iptu{Et&f@7h6&KmT-OFP`L+6XXi*CKBF2wO>x&O(eICy zeF78P4n${9JZF_M?R5V+&0{drWGk-FfC5&-eSib(*McE#=tYt^16whImP$cW8*zUP z9-_|SvHv|&K#LlmwdQ%$Feb`3ntrB`wgM?E#5cmNoQub^`4y4KNd6HxYHxPOeOynz z)@Wg3&gDOukMRLBkGBK(ezSVV5!=t+Xe5SBM@$2k;Z=`M7L>=oy~An{d|Z#Q`v%T_ zXQfr(v^gi3*yYCPD6VSi`3j4Q2<~;4E;Bmk#|PJLC*Hf#m^U87_1;w1(>zq~n5!D> z#)@$jGpm=ZzIRbC7y&Wj}SHlcU-RPYa6RQymto_Ku_n+~^wntJR_-P<~T3M=#C$a-ZK zCy7q^U@7M)>&k~RI?D%&(2*x3OGU60Snzoj2{ zdaB{B=Ed7$R7CTdP!|L15|FcDpwt+i{Mv8T?$Kdr84OMSwL*fB-Uv8F2E*-$1w5JZ z3r_;Z@MId&RipFy(3r92b2tiH?b;v_jCEXJtD16xwRe(PV@+ZO;{e${@0xd91I{p` zXgkxGcRdcJXbxpNeCWujRi|0SI^LyqWCi$Z32h--zJ-TZ%9!Cg_D5(RtuQ*DE z^p%Tik>(%NcOnFvT%lq2{dLv}gJBT7M%WCl9!il&c?F4Ea3@o@FLj65Y-+UC7KWCQePh9~UO zDfbC3j~Kq&d2HG!jr*U#@>CA>hD$#K7RvNr3nh1Kp{zveIXJZvZImeE&^Eof!he|L z-X82q&UESFpQjuhc#<|6krc=K(eGVW!)Dpi880Qw3>!a2F%Lg6fk$~9>1HFF$bNy) z2+nJ=H&f&bkmszr`Ewo?dz1 zSM==Cb&mNH>SxhR7Ty2Y=6C}c+yj$o(kK!hFn@r|RDY*I8c3GVeJ?zfUJQ_5pc`zu z%2!|7sdDIUNmBWG2eZN@x#W|JEcj=8Q0+UsLIY;gd$sfFtrZQ!B{DWz(9h=4yP$hm zId(JF7nM~0_^b0sf^X7-&Fvwz)`%vqwf&Vi8tC|GFf^pv>(;uGMtRGeZZ<#NQn1qI zbiY3}WTc9oIXbX+9!qJ}iFmE^x#CmGIBrOqa+KFi|8%8Vg28euieu(J$V^EZO&w z^&>}5FLu|({WZF;tKTwM6}s~1vTsE*q@MddT)f@8?8%J#VxD&WQC}+9U2Q_${Zx=S6bX@9h?GZ$sfIizBcX-ox1UJSrravmjbd6gCV?`>|7R- z3%Q$jxp=kz9}MA=e_AYFen9C$NN~9E%o)|6TCRHa8#=Y36a5E$%pya#=O_4P=gN&a z!mVnYo_qaZ-s|j#^G?sYdw6y6t|pT;cOuch1_dUhv&`g$2QR=XoMhOE$fNxI#QLGN zMPA^~BW{kGMDb5*DuPz7L?AAQ%CxWKRAcKd;&alTlRTREwptGTg~NwjvT-b#D~Nym z!6A6Tss@je=sj;9UOV}xhrZIe<$d^-XK$ZbOX=$?>6?Sn>~5`DU>FYF|I!;6l@H?;oiH|$1_iEYA8+tbjK zA{oEYw&~hvXF#tj_b%PtwLGe>r)@f_T9M;^*rrdfzyt(lQ2Sn8hJWo_#ccF;7xCEiar&7e}v<=vaByoFuYS z-cEIj^H7~x>6Oc2W+Z+YIec`u--EG5?*ZSo1=m*^pc;SVaH!(q0@AzH-DuMJE1bQl z?|0sike%B3AwQnHE`BuO@;D3mj6SGlBt9H@>rHF6xaIxmha1aHvg9`z zd1dExY)SXejo|CsdW*~4vj23?KF@sKYK2G2C9|Sb4(5l*9`=16)zWCeO!;|0=7y)$ zKIxQB4Q(COiSMq}%vd~FuvTGO%iN924X2vU-k&}@#?$Y|S*FRpucy41EPFiqwo~KB zp=S4f?R5n=O_$9XzOJYjEgn8|-ja(q#Y!jo%X>dRzvZ*6_lNhFw-szyps?$tN&oG) zU}^gNvWscCOWgjMR%d*uALI)3%QniG%pxnE@O%2eB{HZmo~Byze6qPr;=-G^)|aoH zbNX#eV2sbsr|E&Kj0+<6<=eb@yDVSKT0AG;bqkkWv2iCOzj4Q=+{&$&&;9Kd~>qx#1dt4!)nVj?pYu2Ri2Z zt4x(RZ(l1YNIyK|)9Gga(7eubBlil7`&C}?rq6h!?eujm z1ycxf6P#tL=jM2LnyqP2VIG2Z$PAkLDR-0BXNmdMuXi0SoV?`o#=8@ILu75H9(0>{ z>*B*xFD9__JH?h5g$y2$Sh6wTx$plW?oGgA?A!P82qBC;2_qqUWC_KjRQAY{Y$IC; zV||j!Eg^d=*)mdEB_t*4rl_m0WjGgUYzlQtGY{->kV##4C@hc6bE)VV1kdw5! zguKtIewS#bn=@#GN$3Q%@oqC0fcq%q=fh7ECn+r-(5ithIH3!z!q+=Uxwa5q02BAv zz~%up@URUi$;gK^tFPwL>m5qle#LZ9{`cp8&v!2zQt)_?@({)*ca~vjFr?VuApXo0 zsUVxa@EUBp)q>4{g$l!Jsl+_}*2Qel?jk2D0#+%;W(g*)lnQE?Gp%nh=DWp_il`|> z@`^I(EGMkm>vLcE=cUCZ9#kJSOa|Xd$9b8RK{%_T1ND^Y-x~va)fF?+y-_PyW3>? zaJvm4)0#jEn$M2_znc%^h>aEY0-qkk_g;M;C?Z$S>q;A_iA%ZTU9s0FQq1pw3`@0D zP#=7hwq96#R-tPaA6v#jbrr9khC;8E+d%Tn;oQUlUxLFp%uB)>$vm(+qom*XWHIpq z&%EmhhAEAtReS45(9SO*hBp)GrmG)siEDV=HB_aiSwGicR zKwG)IzKc2B3ynBfsOL9>$KN)*fl_%+Ojl zz*%o=!nw_Q~@v0BD@zb=$xwq)+*FAW=!ts?B4O%)}?GbMt@ zD);r_&UP`T)jlVW(7b2dSxf!qN}0!pfkik&1h4*(Dng-lN$(4=Tb=oUUJ7m-lZny~Fw?3M1PwkCL=)lB$p?=6QN0?mL}-j1j(OV>{;3QC9H@C}%I4XH(+ zRX#tyRDQK`Z)4XDqa!x^8T&jhJ3XbQXnN|P&6yAKyPW2@T#WQRG^Xp69rKbDn)Y>J z^2`|f$S3t5D~BYsJ5S)SoqicT9)B_zJnVL`Y3YN7lZy5j4&2^rQ(0<{DI0CA){hw( zSvaX*W6K~r;b#MfoS6L8_Srlaxm_Y_#fME_@YtI=hYOR69L@_P#8F>SJqxBq`=@cH zUs+VhJ7!qDTL;ecZDRMBS3{muSlkA6^)y1({S0kb$fo`p%`3+c&YxI0sLTOOQCyt1^KKNHVf1e@*4VXb)d(P!5+ zHXBAbf8^urxM+oC6`mKnx@sL^*;3h1P0XP+7BmxE+ex`(^C(q;Eg ziutO|#9T#_nU7RE3Kj*yiSkyc7?+ZobRY)<}h+=o)?sAfpoQE12NAB<ZH=U0h^7ZW5{(7cB1+1ozH zM^6fe1=Vl1kBFAVE>GVTYHr#q+5i5By&Ea|l1vuv~e#hRQhft!7HRL^GZ zvvbo(qTt>2p*K+#m$Tz%t89dvb~yEnR{QafMS+<3xEO~R37$c~X1v-Rzrb^fHIvdy z>{60T;M#zJQnq?sEkB3VqF&I+S(3IiI19|qEBLBOGM@_Ql~444+(K(f;mNV35Gf)? zYsoxJ?!vgCAY7YtLcx}DnuyB^!hIe$&f=J*;}@OhvyP89MzcW{jHri)$J$;|25I00 z4(nmG2nN)0)S3U~R`Vx-2d>pOiL}AFk9S9NQ@R(3K2g@PW&90Ja zytM_DL>0ed8BnJd!C2XIr)E9VJC-$q&dtQ8c2Yv%t=;w)_|+2VY}KyM7CG7PfQpfv zG?PZHKpoeZKBv%->NRo8$TZ?ov?{oHhamvle?@O{6p|vLW5dRW+_L#A7o~69 z>91=Ayzp7_!^6Iu+swK{`(?Fjs3sd@a5b^}e<~pK$ETQG{U%py7k7KlWian0BSpqb z31Nr5d|D$D@~a?QwMlT)^cM4w2d&XeSP!HGj(_+RcBA0c)4HOgGfNAn-P77@av!(| z)n7k?UK*@CK?{wB;)x_4-!Iu;9F1h_kElIoaEhA;=6EdQR?4ps?~h<`3x6G~P9GqvRFM*7z2S>Y$I-;re<6xB>8nKS;jz?{&HW6(rN(b+Qz>(taS9@Lw zV>R)VvrMQ6u%y^GuRud<6b^-B^ILjm?$Xa{IsPS|r0--o?eD6c4@rh9vnd=0CH9b$ zMm^sXf{}rRPT;q2j|wiCD_{{|j30bAr6Ewkg(amq`RytGYGYc!+b$+sI~}utF*~1u z$5a`o@tA_-b?Ni{^OWXF#~qz1D#tDrl~4(s|6tJ z95Wfs9R_x;59vAq7&H-Kw}}3pAkkVt#S$Q37O8^SkFyqBN$P2!vR73_`42zGu2G~p zSj7`2n?8XMXNdBI=wC5l1FR*1*9ziZc;1y-)y*FIwG+=K`K8)qk6J4F+m^!G7b_#5;w73BxcD!a|{kGzup~c76bM?W8{%(?z;v&GR#0ePJDG+PHY2e?qY zJnnf1(WYH?c}?F0IPAxkX5+*}JJ(QpVKm`x30VqmMrRRQxrI(QlT+jlhnvY_D)#bW zJofW%uD7#HO2kS83}q3SJOb`D%Fsv-W>Vs`9DW7g_4%;%Bbgjc<2tx!+Dw$SqXcik zAG0atAHEP}Po<{8>JN8m3kTQVlJgv4{<>ub>2PvcS4v>B-3E5)jZTHeD)*Ie{z5I= z>;Yyq{X=WWn)auKIPkOabq?Vw=Nf$}%%?mE@o`J%x90@n8wnmnwTq!(O#)R&P+tvP zzL{C`sCqk6V#;!%T`dIi%ONv#O1IYM7`p_wt2Mm?D>aOrW!AU%fuZ*Yc(C^%ByU^3 z_DSou=~Ipj=<%q#d)O4m{#~`q=I=Bey>;B91wm=U#_AhRxjHTIpl8~ObIb0P#6J&M z;9(W$R@Ooff8saN>&y67hz<)BkYjaCb?p~jVI>8m`N%$-nX^t@Jf!bAEn~&yAh!>z zz2b=KfF=5xuL39b-nQL0=IYY$)PO#hXzPx~h`nh<4OjOIV31}pe6@|)R%0yaeC(MV z!8`E$a~YCId}^+YJ5WX1jMKUDIrT&%_UicTm^2}M3lTXqt>2-^z@6Iwj#X>m5{u)} zSdBx17j_4}dt?Hf0AfN_c-!Okphc7Czk)~w*fgO*MLDAf?d3q$Z^2)8dgtf&0c@D=AO zq+ELh*j+DxQ%Cmxpo09ArGo1t7!MY=T=X3OhGifQPr|5pPC+2$Yya~uCWA1J*6ZH( zYQ0I|s>a;i*UjW`Ca^XhiX<|L;(!B`$3>U-BL0O8;w$2FxU5eO@tbA)TSO1=kaRNd=k&D=ez)*pa0QsFR}cfgz(&^(knmfnEbrE% zX{;@MIm)M|(Zp#5UwI$C;y@C2 zU;&&zbnph3#a{yh_n{tP3xI+Hs@3C-zMPc+=GQ|N|SHth?2wA9u+ zPcS;h#KLrz=gwO};iRio@@iJ;+r7TkMhkP-u@24*bvSm_J2~y{C8*jmap}HL@x1P$ z;TbD@we+i3E1F^Jy8 z(223^9()``O}aMQS)oudwj9Y31b*5I6B=~rtxk{bCgLH-_6jvM)Yu2=q{JU zd%*{uie5g6mp_P)4XF{CE~Ii=M&|C6v|7K?Xg^~aua(yEz5>h$WP~_nMm{R?K5T9$ zwIsFQlDZw5Zo*s<9ea$niR&q~9ChSI@j)YsiwY^wC+TDZG}3`nx5&6&?T|=3hk{`*rEOU7V@-dS zkw&Tw7;-57qvts1L)J{IQv7R6YX}<#Rhi^E4wCUimYj@nVfoipkhKIT0S)_r994>Q z$)0$>Qsa3Z{@!C1CynyZb1Nn95gk zan?4AiD^F7#C&{b<=@6J@}`wC_F#EvB}dB-l0Yzfg;@6{3}U!!F@EQOcbrCr-RMnK zd{qYHMmGy=>1a<`5PfOc8;&~jc9JCEjBxQklr|d-GvPx2Oqddf_xAxf)uo6d=Iq!# zByH5*!Y$k8SalTFXAS9Oo?X4G+;M$hhNZ8rMW*FLVzG!7+_q~uII%8=R6e;}ND z&FdkhH?60(LW~8`2h6nLScGEoI-ZVB>nYenvi)GKua04%zpp&@Yr9}9Mlkiy`G$bU>_LY!YI8JJ?0`7Z3&BP?UPyYb(R$xv!zLv0`IXd;J~Mn z1z@LOY*FhgmYi1t(~b_mN5$`$cgZRZZ1BWmcqok;TpcD6-g-mYDeScXiWKZBLe9i0 z)_0Z>Spa#Z+_lZbvSCTca{h#QZ{h06-^8qhn?Rf&TVqa>6W5$-fEnA@DhXq44Dbp- zjRKpY5rn&xeE1QP%}iPqcE$DiO?(dBY`8&Oq>)&!8s+$QGs`(6CyRb};2x1R7HeR7 za;b27oOBjn{#XNA$_~N1{SgV#x_ClAf~ICQJlZe{x-s(cy3Q?ogJ@ zGpgg4I-Xnvk3_^BoX*|Sv2;Ni?hdP%PV8nOWwU^%f)ufCQP_^%PZufx{VkV}( z&9td`9FF7pCFIyrT+Q~<76_PG4P(Og{X0PIs036bgGxZ9x%zn2Z2wO`XygutMk1h| z#8}+Fs)hl=YQf1_miNe2tC;{K;8is%A0+00?)V2dbWK}9JcI4s{b@ftMwDrQnA7<1 zFO1w_9r$fiDWZr#Se`KBr7XvUUqsx3<$bIuz1J#j;xHR$PHpFcU?t#*7C+0nKF?rV=S&W24_Z_@bd5$rM#t|}D z1o}fxfC{Bw$F^7dA%3bdc#EJsNTn4MlbP?jir9y%HbCHJcAdgbGhU%VLyImw(-N?ADbN#6Y`Z73xX)%*Poqy|F^LaJO4aO$r#^441T zEC<`kxCGEHvaas6I=(RR? z{*0oma}WGi-fWB-uRvw09nwf|S=12tlmylbY^k=0`xa{O`FdjPU~RDb>MVtf3i7w0 zkRlQ}K}0G;Ir9>vm`Jp^{zTLU_#6de@yRN_@hUh@>){@oM8}+js9~;j&bUVr zWP=TDnbZ#ld~yej|B5!C$8AM}8d@+0CRqj2gFN%d47o=~5odT24kLE3m*#{VJ<_Dc zKk_2VrEDwTrBsqoKNtrDOB?du3cx>uuLzT~6PqM0MCDe$n-bp)zuD6l)uxf31>RGAa6>F!d&q zS0PP)g@tG%RuWRfYJsRF`RiL~4`dPj_0Ngr9*Rrh)3d<-(0&R}H+X5vS3oXp!#5k% zOxD!7Cx;N)D0^Rl*UA4nH_!W?n_C`1=`x( zcms7Vg%W;1lJPS)4}?5zPs~1xhZSAl98?#BWeOgcfLqu)H|T?2 zdgILQw-C>6!1yV+D2=V5oXNxWcOiYiFR037ULkFdVQ#+T%+K7s4QZL&oM*e2p|hNy zG?7V;vzX*~8-#Qg!QrZ0Ijfo2$ta}EyI+X%r326G98e_iT~zuxmIT z2Jd8a1G0fY-JpcY$uUf;#*48tn>|g$WdxMt5$pFx)7l-vx?hZ1E1YgYE;zftqTYg? z_sqkobhdFAVFKLF-KhfzU9rJF``gO1Mo1=G!E0$R1;Lb$F`-rm?r3X5$vcGx!)4rA zbj&M?cSolT#-UdMr!9ya9A@--<+k1{!BoGWu8w`l7SHfS-8W4h zSdl!1Wubbt+vG{!%Z8NWBsnpAg;vDkAoHu9Pu@H%nsa5;g5`#9J{pgnJg8z>cu?m% zy-a6395Zd-w64i>gLJe#F{Ov_&P>+Y_SJt1b%>eRu+y)XCvK2TqMHnwjxhqB7wuYTlkfIBa6YXPh?+}> z{LA#7lvStBa+VWed9TG>B!XEc-;uL3A}8~lOyl8Kn2$rI$9;L?z2)kENbt9+Q)G>$ zN*QO6=X-K9L~WySvXh*Px<#{AZ_42tj4%_?21U@cSG$RSKc~wQgL%!CIJ$%GaE!*X z)E^?srzj|t5ht@mq9c{I*ZRuMW^9OPhm**HMPzpdCRafZ z5vfyn1p-aVNK6YE1}2;WPx17;9hm2?>kMomXAQ>FPf~$N@t*_CZi>f&rypUyI@ZQd z&Q1Vh2pQ=s3%LOoM5Yk zbR|(D%@LEgaGLK=!4hYVwf}!1SWa9ZIrRC^wxRDiUlrWJT`_4vJ*u*N*U`Nbuf_{h z9H#3?h|L|5XNJp`%gIi^^UO>1Qz;q$6@`Ys;Nl9Iuox!Pz@Bc+C>?WK$%F<%nYZtQ zr|mw<1TTQ4C5^fltFjtq?0u!aC!cd-Xv~YoL$!k|tE}fQ%l<-D1zwZv<9$)0 z7G|QkZ!5)HtXqEAKriLDRZ;}2Mo6B191Rd=)GQ@TE*QpSZm4o76ZCU(!Gh6$LdB(3 zg2_zIPw;3~GwH&l2H%r+$Vb5)*{O^i)r;Y)N>DV0h&CnEv$w4B)=KJ5K@-s%wQMOQ zjlx>Q$;;xL7VrJfHb?*=@$P0EX%;SXa0Kn^5)us+?kAtF%^)Ulm&l=dymAN;(8ujgVJ(GTd=B@Brk{}z80RPyU zc8`*!6VW0Mx?ISSNTR4*wo7FQ_dux`syyWj^`6=}DxwMq87oo#g6#~o5Jk3L;wTh6 zDpyEpa9dOS66r&1Cov%%A%H%XQVoxrEjdj_zv!ZNS3>sPv>3+j>NRi1#06#hrcCsj ze|OcgnFC*sdpj-f!I1K#i>JgKd*ARmHx^vdypx|!G=0b?MqTewXKXX|Y}P2hGfvU~ zwZND8P`)v!WQyjg9UBOkKGo~IudmIv-2)bWEF!MojQqq<{*qe4UBXsx zV)tP#JySRuM)gUAP_YNYIq!bwoMV6IoEQy<#ODhnNECB1%kvxfhdh2IFoLO)b1Jzg z6Zjn7l0u}8fzT04MX0L&l7eDUXco$*MI!QF3!_a~EgIdg0wY}I0xPx~Ys^}HR7CZc zdZT^!>W|*7uM3kY-dI6&!s|z$X3;AfPZ}$|7N>_-AY557MV>;K+Qh!oD9urA;vY3t z)_n>H6h@y*Jx;27!3mdN%Ry+CXi$cc@45UVE+9`$q=>j%;BZ>b9`_+x_4>+bEN^4; zr+YRz{M%v>24*a+h{mOg`~H2<9}`gfSxRt>_VfY#ttbC$~yaHTC5j}nvPP$W>AXzJlU&hlU=r}z() z^Pm{jPlj?PL&3go)Pd)vz;6-s9_O}%(LnJgRx6n)a}RT?7zOn$R-wNOrR?vl*YF;u z7j3UboG4bZVSP%ki&8(RYi8j_%@%WJwk*pbrcFQw$4w+&lat zOXl7)Xxj9|Jqe|Qs4=6{uRdkFtPstvzaUo$pyyLq3t%yOEV4Rikg(#_hwL$e)&eZ( zxC}?G{u5hEC{Wo13;$(Fs9=q6#Q-CEdr~4SjzrVFDHnK30L= zre?7H%E+SZgahSkZ#H>>T;z8YRyBl_l1*o=1r&!XWgTszJ!VT>8xC;A;lx%U$&2j6 z`Y>jNOt&8Te5KV`pRO2bIytMK7IKxJh}Xs%dk8uG%Qzz$rMRsguImf|=U>Ws|C%p4 zVGZX;DZDNLjsY>GDx45}X8n~eGWf{k*#WpggY)a=WjwR~~snvy_my4wVcW65&pB*tyiii?* z*BhY*&d_UqCc;V(jcO*UU73XMDs(0=>ZNBD*2Nujh}({^mdUfvD?*}j4ddIOP+0>1 z*O(W&L`E(+h0@6JpC$0xk-w=qwM=}yd)1dkEpDYZIhjK{OAlvwa1(JjP4r^zI`2$UTwpz%$Jgk1&iPgp?)gQ+;dQI<-vhpZ_eHf)<8 zQ`Die_51D=fu-!@_h#ID5597-meDkoukMW^rLuIi2o|>g{bOO$4HN*1aAh+Bewn zbRV^I>6+_m`d!xta&UngXAe`S=l0*zJ}wDdj0 z6H#6a!lUSx-19rAre~ik`%w|C&=)L8eKB%s#3dhNBA9g!UmMGPr8_ zbtB3TC)7h^6MuS-WkiqA8bKXp5eVc&6pxY$Bg;8tvk%`{kV{txjf6fz$L1KWB1Db- znr`797F5!5Ya%y0noi|9zwk(qiY=*}PhFJ)&Ay_oAdFK|)ij=o*JSjk`4NG*4s(wRC@|NNcD z(=0E@Q#(hRP47KFxQ1MD<-)K@-AV(}YrsTT3_6>a!-zu8(F!e87KuUjdk5E)xj z@}i^DQS3SedYkcaWwbU^Qb4Srwt_z92TDo*dn^*{q$JO%y{#=^72#7$&IR2evN9wc zIkIskx8M?fYBZ+=gKj?=c^}E1_tutM55>~>YuR<#eJX3}Q)`NDhUc)xi#eaKFN!EE z<@V!tE}NTxMf`%^sJ^QX7fVhUC*}Z?EFWjwh2PoUEI3m^@mxi0b&X-r#muA6jXiCU z*i2SY7AK?}qhEqU9o7-GJ$b+mPmyx}OXy2N_Z$u$++p=iqx^7Wr={qmmx9%a!&t^e zcQwQ9EW#&&g)OB_mi|K-d^;W66C*Z*E@GZ=^)ia^=zjptqzr6ytHZvs~ zY#pm3dwl5mzheKnI7LQ2I3`C&`2u(;;&1Ic2L>?<#xelmjeqjJt~lzvvMTb2noWxo zOXSL$&5J+OY|@Wl82QI_@CITvN*RtbuCoU%3a&SJJ~%s#5`1_L{B5886w23&ke@nm4<$~^M`B*3? zKBN(c5=NuD&UNgDe0s671eW#M|56Drz;Bp}(3iR)1Lps(F;2mX)0%wF=Y{Ew zM+#wWIcMLvrAp{4r`&h7Ku??z{6MKME{A+Qg*)4^b+Z1TgxVCnFs^eY#V2KreDXJL zV?@*q=SD@psJP#x-96SPI#(CHkPy&X7MVPjkZ@e2a=MO=9@6R+!EKF}%rZI;eq`e{ zs8plNEIINIQSa^U!Pgghio7|8DR~H==CLUXWHLW^%OZ>K=sEh$8nx${9($HdosZrb zEP~@oqKIK%pxE}a)Kb5jyL42RxvJNhH|9@zZyRv^{qSjQ^T1J4N9Mb&=n*(d>kghp zt7e{lFjV9`@%*C1Rdk=NV*V&gliiP&?a)74HgVDIv~-!}sN}gr61tu#T}DZ|no;bJ z6DN)7CEf1(+Gs@@F|(H6Lb;#)ry2B8bL8zzzVUk+zjnO?)SuuhX~7@&2R_3 zSII3!#B=QAJea3UmXTN>!~GpS*Eb6p;#ozwf}YOrF#7`#BJ+tHq@NN+6r()e##z!X zO>LbDNb%E=X7XvXKse{Sz!V1k6XEf@vnV;{1X_|+l-42#DEJ0}{VGVbmj6yBrDE)G z8UITqyf@`!G8Az?0ZvZ@ceP)F4kcrXKXqW5_YDS@VD{G1@O;~)EVtpFZ|`0IF!dbXmIpT@F3NY7*LI>aj}zm7N^o za<2*oFc1Vz(uSEte=7qSXm+QRHGe`+fzwe7Y}~(z$Yg~ev>M{zc_l~nf0#uxMCng# z(%$4p7M(_{J_etW;tH}LuRCbaELLR(j8FISuV0c*eQr3mX3#3rIFR;h>G40R*$>wx~X_tRwsN_^|nvlButlT)4azCRbbI`ANioU;MyjWc+ z#$g!;k-pgTlq78ezbv9|o@D}|*2Xhiq2}M3ix|)CuL`9-6^Oz@WDW1xW$@W+R+t7F z<~vYTesq_3?B`o5j)&NEqGY-Wg_cuv!Lu=Hw3o72jGxk1%2&LOI+`bfPq|U_1cmwv6Vn`DJ7m%69{zt79JlA~?K|ShG82!Ww7|W-!EZ<9 z92%%;AN}&Q*J-QCZ_cD&xVUAP>8uq`?hjj1!+W}l-n?tj2-6uWY*rJz$dYGt@V+Z! zuUCjS?7ua4T<+44GxH8@@^&3iy;`%M%`E+$qdGM4v;PwTRf-WvN!{nc39N;C9mc9j zhRibfRoo~aD(3i5rBv4I1ylyuQ;fF~3px}LhGz5o2@-Y#g5!Fz5*Yt%LT5-RI&z93 zgDLZT=bj8=5)oo${kyHe?f1t5H#@Vn<6Wzw@}~{6mUINe zr2kfI{2wIV2EA2ec9>WFJNmh>7yldFzC|F)NVN2+s5BJnumm~(6?zagk|0&?k?&c} z_|#Ds`1=yhzn2@)0saNP;=I1ft(RBSEucGJ zV`v@Y7!oV&N}IT?@Yx-Co`~N_^Sdy9Arzo)!Z@9d4C?$uO8$FNf1pfR%7+cD5E2v> z-Q!~=;{#ll3t`qBo#_Mrlf=76M;Rv?f$0zZ$Tz&?Bp&|9u+R5&)FdXsMbD73meZYD z_%EI360(-nkui?`smT2^^Im{>6IasFpq6do`Kh=yUVQODb41W6yZWQe%d+Hg2KB1w zhZjRX8R!1rLFKMuIAFX?=`^QFZ`_JX1H=y-W|G2=@rRPTXnTtzHI(6~W}&9iUL>gi zrG1|B(*wavpc(JcZ8+Dv_jk)s2N`#&GN(VmD7|a)h~j^zH>UPhde7Gv5py5DMN`pt zy-^Y>ltmCwz;FEIp17%jbfH-AEtPI@c(%7C7!)3aqLx;CwMZ;+jdgiVNg@y2SN;4e zNxT*^a(+y@nyOd&lry^q*-d-*A}@|r(`BC?NK^z(WS+6N11Q)3=PoiUh2Aj zEIhEiSspO3Jf1$#r?Ej3Y#LoeNba1XBT2u}(a+~W~C zdu-s5kh5Z!Cu1CBnn{$t9#kUNfZ;P(?Fm<~omA{-okpxpI)3rVJH>Gh)l8e1Qd zt9q6xjef(B=pBqylEDxN2ED0Rb$KkIRcUa%6`{16qsJ~8{7@@X@&(~-Wsc7D-VmCI znMxi0JIUb1&`t=7Sf4KVCVnZ?LkFMxY$=3L(X-F>tRl*hKw{$!8YVIEj{a{FZ;yGl zNap^Scc;)Es5eljlsX{pv9JckWS& zJNSoP5=F%SYnrHMcKVNPH|Rg_t@M(T=qgdR;-H(&a?IcO&zZ#g&#qI4wSZPiyy}7A z(;r(_O@B8_E}-->{%Dq5uQW>@`GZ+9UWV;8XaKm4J%ny@a$HY2UiyM|S|;c(`jfY3 zhmtq`t+!`z!4D_Ui0|H>tF&JSrMCzBUZpIfzpY|>;rDC*lO%ebqGu@ON)v3Y;i!k9 zv*tyNyaZ34sU)o-mE}}$*+~{L1Wu0*-r2lYc1WrJA0nq6rZ<)7sNh^dpOG@vGNdG zR>+kZK$R?&Pk;G#o%|Y;6E7m1D=mqNFz&OSE4 z%-LBI(D%qj{i|Wiw!7=EvYmF`eP;Hdds}V>2QGb;JW=!N@CfJ^c?q^y)ut`b?i83d ze8I?oa)OXcYei`GN+Dh`#xMrF^I@ z#`UB*MZ_cKmTM85k^}se} zl+u6Ie{+OHYpUu$XuvuC_f3aLMt^FKcTAcI+h1KgJO0+igW%Vpsf~I3sEPgb@MvQd z?-?K5}al3(Gi60jA6H-QuMetpZ9$X%3h%D0H3quvoG1 z#M^1-N~cWGUwJh@px=|U{hGSNmYkZFE?S0C;pPJ~=AB=Zw60sN-YNYvYNnG{1ZW1& z^tLhb9+{!$wro$I8GXYpKbgqzNbfIIeWjKTN+?9H*01d{A!0IF&KhBP6E0!C``Uyf z;yaO*FWg=q_0nBAX|!7yHR1Y7F$Cj*47w%>(|cpJjNyoQO{!K94_+yBQ52k$D&y(F znKpqo60E;u5)IOvq?RVW{GFb{_8_obMA1bpO{wqK)<0{yL$;K}D*P>7nzcy!B-waf znsc<;K|lz~r}oI%_!R3>MhM6mNoeGp`<`DP@XqAq{8dEA`>un#i;ctQxO|KHYjb8# z^Q{Yd=tQG}`q_4yiGR(a?wL!2ZLggVnj{_CYlqQ_kMnxea6?~&(kV8Z%=8CbdwI%j zy7$#k(C)>^0=hZGMBJCZ2!y#ZOMlo%PySAfr{P((oc{57M1qp{T2Po)r zY+4ktWeOe(!&H)0nq24%Gi=3$hxZc{l^z`GqIH7gWR(25{f6cbs)D8i(PZjsqpk5H!oIjYO$Ly_X)*K;_UM)Q-9|w6Lgrjf5&c zPSfK`mzF*PS`kZB_yFB>C*&Db$Z392F!*T0TR%=IU2XT?P|%R}tx&?bFY?Q{uIAxV z*T8pfy%0hjFp6S$rb<~ZlS=nS-`-jwtRg&ic~(15vDz`)bJ=B9u9vUQVC;qUeiuGx zTIR~zyKk3dnm_1bYIr_tUbkU74`Z6xKBIih0+;HF+2hi8%^cX{#GpHG@z|||)YUdc zjw<|iGeh5B*{QR1{iuQ?b6n~*R=r%P7Ll^(uJQei`pJaeM! z*#2XWzgbgvx=YE-$cXj3``y*pGPvuM)w#vDA3xrzqaz=ZNx!|A?j>>LcgYf->!nsY z!!A2V@T}5hcFvu{orjI)0wd7U}tDSf4qLjnXuN}E(fyX! z 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%0Dhic@mBhTwf(!9d+j;@J`*%Yj3 zRQoo}rrh^|=>^}1N8JZXf;69J+8-&2tqnh>qO#vsbGN2tZ;7W^gxuC+r4<4?d$nz( zo#N_ZSXY_~l$PzbRielyww
DaB3S65oT3%@??Y>eFzaWlb#de3i5-2N1`{%uFV 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%0D996)0i0yz&qF+b!< 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

wTGhU^qqQ^diT|=SqQR#xc6;q^ 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

&py6GrB0da8Y1W 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 z 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%0D}Q^!w|v^=~v!nNi2@J)KC66f4b* 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åg8aDD3tuk*NY)HUJ{^#1eYu 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

%6a?V;KTxfD4&q76zUVMeY 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�sP|2MuieXPNe;bYol&+p5+n43}s6A{4a(HI0JR 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 z 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

RSY6iR#d~~iln9b;>stWQd;gbrE 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%0Dd!UxFJfM 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μ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�N;e40L?U&FMLdv4*?3cP}Dg%ah9c!J?Oqn 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

LHyiDJh1qd#cZthupr%a1Co2ZEVP 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

V1Di=8cG7 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 Uho28We~v%@FL_T#BLLvz1mZasbpQYW literal 0 HcmV?d00001 diff --git a/Tests/Ix/Catalog.lean b/Tests/Ix/Catalog.lean index 5443fee08..7d5a1bd00 100644 --- a/Tests/Ix/Catalog.lean +++ b/Tests/Ix/Catalog.lean @@ -18,8 +18,8 @@ members_root drift, non-topo deps, path-traversal labels). - Strict-anon compile (`rs_compile_env_anon`): §5 empty, §3 hints SURVIVE (the `finalize_hints` ordering pin from the plan's risk - table), and the env root equals the named compile's — the - anon-invariance the old C5 suite asserted, now structural. + table), and the env root equals the named compile's: content + identity is independent of source names. -/ module diff --git a/Tests/Ix/CatalogDedup.lean b/Tests/Ix/CatalogDedup.lean index 8d613d446..dea1b36b7 100644 --- a/Tests/Ix/CatalogDedup.lean +++ b/Tests/Ix/CatalogDedup.lean @@ -1,6 +1,6 @@ /- Cross-process anonymous dedup gate (`--ignored catalog-dedup`) — the - re-homed C2/C5 invariant from the retired union-loader suites. + content-identity invariant from the retired union-loader suites. The RelocFixture collision pair (packages A and B, both declaring `Collision.Tree` with DIFFERENT definitions, sharing the toolchain @@ -13,8 +13,7 @@ in anon space (names play no part in catalog identity). - Shared content dedups: the union is strictly smaller than the sum of the pieces — the shared base's constants got IDENTICAL - addresses in both processes (the old C5 audit's invariant, now - structural: content addressing is name-independent). + addresses in both processes: content addressing is name-independent. - Genuine difference survives: the union is strictly larger than either piece — the two `Collision.Tree`s are distinct content at distinct addresses, coexisting without any namespace work. diff --git a/Tests/Ix/Compile/LevelSpellings.lean b/Tests/Ix/Compile/LevelSpellings.lean index edf68641c..6f318dbe3 100644 --- a/Tests/Ix/Compile/LevelSpellings.lean +++ b/Tests/Ix/Compile/LevelSpellings.lean @@ -19,7 +19,7 @@ Consumed by: `validateAuxClosure` (validate-aux / aux-gen-diff / decompile-diff), and — via the Tests binary env — the whole-env tc-roundtrip and kernel-ixon-roundtrip suites, where the stage-1 - spelling decorations (Lean `Ix.Tc` + Rust kernel) are load-bearing. + spelling decorations (Lean `Ix.Kernel` + Rust kernel) are load-bearing. Import note: `Lean.Elab.BuiltinEvalCommand` (the `run_cmd` elab), NOT all of `Lean` — the roundtrip suites' envs are this file's module diff --git a/Tests/Ix/ImportIxe.lean b/Tests/Ix/ImportIxe.lean index e20e70b63..517822166 100644 --- a/Tests/Ix/ImportIxe.lean +++ b/Tests/Ix/ImportIxe.lean @@ -1,5 +1,5 @@ /- - Materialization FFI + `import_ixe` tests (plan C6/C8). + Materialization FFI + `import_ixe` tests. A self-contained fixture environment is built by direct kernel `addDecl` on an empty env (no Init, so the compiled `.ixe` is closed @@ -11,7 +11,7 @@ max/imax level polymorphism, and opaques. Gates: - - C6 parity: `rs_decompile_env_consts` output matches the original + - Materialization parity: `rs_decompile_env_consts` output matches the original constants field-for-field (types, values, level params, hints, recursor rules — the round trip is exact on this fixture). - Root equality: recompiling the materialized constants reproduces @@ -263,7 +263,7 @@ private def roundtripTest : IO (Bool × Nat × Nat × Option String) := do if status.ungrounded.size > 0 then return (false, 0, 0, some s!"fixture compile ungrounded: {status.ungrounded}") - -- C6 parity: materialize everything and compare per constant. + -- Materialize everything and compare per constant. let materialized ← Ix.ImportIxe.materializeIxe path if materialized.size != original.size then return (false, 0, 0, some s!"constant count: original \ diff --git a/Tests/Ix/IxVM/Exploits.lean b/Tests/Ix/IxVM/Exploits.lean index 33419a0a7..81201043f 100644 --- a/Tests/Ix/IxVM/Exploits.lean +++ b/Tests/Ix/IxVM/Exploits.lean @@ -430,7 +430,7 @@ position, the type reconstruction — EXCEPT `minor_var` (`Kernel/Check.lean:2573`), where `n_minors` ALONE selects which binder each canonical body points at. IxVM never compares the prover's declared `motives`/`minors` against the reconstruction; both references do -(`inductive.rs:4945`, `Ix/Tc/Inductive.lean:2346`, error "arity metadata +(`inductive.rs:4945`, `Ix/Kernel/Inductive.lean:2346`, error "arity metadata mismatch"). -/ /-- Replace the innermost (non-`lam`) body of a `lam` telescope, keeping @@ -592,7 +592,7 @@ argument — the `[self : SizeOf Unit]` instance is never read. But `SizeOf` is a one-field class, so its instance is prover-chosen: `@SizeOf.sizeOf Unit ⟨fun _ => 5⟩ ()` is `5` in Lean (project the field, apply). lean4lean unfolds the instance and computes `5`; IxVM answers -`1`. The reference IxVM kernels (`whnf.rs`, `Ix/Tc/Whnf.lean`) share the +`1`. The reference IxVM kernels (`whnf.rs`, `Ix/Kernel/Whnf.lean`) share the blindness, but lean4lean — the ground truth — does not, so the three do NOT agree and IxVM is free to follow Lean. -/ diff --git a/Tests/Ix/Tc/AccelDiff.lean b/Tests/Ix/Kernel/AccelDiff.lean similarity index 93% rename from Tests/Ix/Tc/AccelDiff.lean rename to Tests/Ix/Kernel/AccelDiff.lean index e9f096ae1..b7ad2f9a5 100644 --- a/Tests/Ix/Tc/AccelDiff.lean +++ b/Tests/Ix/Kernel/AccelDiff.lean @@ -1,8 +1,8 @@ module public import LSpec -public import Ix.Tc -public import Tests.Ix.Tc.ParityEnv +public import Ix.Kernel +public import Tests.Ix.Kernel.ParityEnv /-! `tc-accel-diff` (ignored runner): differential fixtures for the @@ -12,7 +12,7 @@ BOTH kernels (BitVec natives, Decidable natives, `reduceBool`/`reduceNat` markers, `Fin.val`-through-`Decidable.rec`), so cross-kernel diffing is structurally blind to a common bug — these fixtures check accelerated results against genuine pure reduction over the real env (compiled on -demand — see `Tests.Tc.ParityEnv`; the pure path needs real definitions +demand — see `Tests.Kernel.ParityEnv`; the pure path needs real definitions to delta-unfold). Small literals only: the pure path is exponential in literal size by design (see `TcState.noAccel`). @@ -23,10 +23,10 @@ Comparison discipline: representation-producing accels (BitVec→Nat, the canonical accel proof term is a different-but-def-eq witness. -/ -namespace Tests.Tc.AccelDiff +namespace Tests.Kernel.AccelDiff open LSpec -open Ix.Tc +open Ix.Kernel abbrev AE := KExpr .anon @@ -73,7 +73,7 @@ def ctorCase (ixonEnv : Ixon.Env) (name : String) (e : AE) public def run : IO UInt32 := do IO.println "tc-accel-diff" - let path ← Tests.Tc.ParityEnv.ensure + let path ← Tests.Kernel.ParityEnv.ensure let bytes ← IO.FS.readBinFile path match Ixon.deEnvAnon bytes with | .error e => @@ -99,4 +99,4 @@ public def run : IO UInt32 := do ok false == ok true && ok false) : Bool) lspecIO (.ofList [("tc-accel-diff", [seq])]) [] -end Tests.Tc.AccelDiff +end Tests.Kernel.AccelDiff diff --git a/Tests/Ix/Tc/AnonDiff.lean b/Tests/Ix/Kernel/AnonDiff.lean similarity index 96% rename from Tests/Ix/Tc/AnonDiff.lean rename to Tests/Ix/Kernel/AnonDiff.lean index 7ef55e177..865018fe2 100644 --- a/Tests/Ix/Tc/AnonDiff.lean +++ b/Tests/Ix/Kernel/AnonDiff.lean @@ -1,7 +1,7 @@ module public import LSpec -public import Ix.Tc +public import Ix.Kernel public import Ix.CompileM public import Ix.KernelCheck public import Ix.Meta @@ -12,7 +12,7 @@ Anon verdict differential (`tc-anon-diff`, ignored suite). Compiles Lean closures through the Rust compiler, writes the serialized env to a temp `.ixe`, and compares per-target verdicts: Rust kernel -(`rsCheckAnonFFI`) vs pure-Lean `Ix.Tc.checkEnvAnon` over the same bytes. +(`rsCheckAnonFFI`) vs pure-Lean `Ix.Kernel.checkEnvAnon` over the same bytes. Lean verdicts carrying a "not yet ported" stub message are counted and skipped — a guard from the incremental port, now expected to count zero @@ -20,10 +20,10 @@ since inductive/recursor validation is in. Every other verdict must agree (pass/fail; messages are not compared). -/ -namespace Tests.Tc.AnonDiff +namespace Tests.Kernel.AnonDiff open LSpec -open Ix.Tc +open Ix.Kernel public section @@ -126,4 +126,4 @@ public def suite : List TestSeq := [diffSuite] end -end Tests.Tc.AnonDiff +end Tests.Kernel.AnonDiff diff --git a/Tests/Ix/Kernel/CheckPrimeGaps.lean b/Tests/Ix/Kernel/CheckPrimeGaps.lean index c972e606b..d876b270c 100644 --- a/Tests/Ix/Kernel/CheckPrimeGaps.lean +++ b/Tests/Ix/Kernel/CheckPrimeGaps.lean @@ -71,7 +71,7 @@ def testRustCheckPrimeGaps : TestSeq := ) .done def testLeanCheckPrimeGaps : TestSeq := - .individualIO s!"Ix.Tc check {focusConsts.size} inlined PrimeGaps consts" none (do + .individualIO s!"Ix.Kernel check {focusConsts.size} inlined PrimeGaps consts" none (do let names ← filterFocusConsts focusConsts let leanEnv ← get_env! let (_, closedConsts) := diff --git a/Tests/Ix/Kernel/CheckTauCetiReduction.lean b/Tests/Ix/Kernel/CheckTauCetiReduction.lean index 3af61243b..fc9cbb78f 100644 --- a/Tests/Ix/Kernel/CheckTauCetiReduction.lean +++ b/Tests/Ix/Kernel/CheckTauCetiReduction.lean @@ -58,7 +58,7 @@ def testRustCheckTauCetiReduction : TestSeq := ) .done def testLeanCheckTauCetiReduction : TestSeq := - .individualIO s!"Ix.Tc check {focusConsts.size} inlined TauCeti reductions" none (do + .individualIO s!"Ix.Kernel check {focusConsts.size} inlined TauCeti reductions" none (do let leanEnv ← get_env! let (_, closedConsts) := Tests.Ix.Kernel.TutorialMeta.collectDepsWithExtras leanEnv {} focusConsts.toList diff --git a/Tests/Ix/Tc/CheckTests.lean b/Tests/Ix/Kernel/CheckTests.lean similarity index 98% rename from Tests/Ix/Tc/CheckTests.lean rename to Tests/Ix/Kernel/CheckTests.lean index f5026dc06..6e22b7481 100644 --- a/Tests/Ix/Tc/CheckTests.lean +++ b/Tests/Ix/Kernel/CheckTests.lean @@ -1,24 +1,24 @@ module public import LSpec -public import Ix.Tc -public import Tests.Ix.Tc.IxonFixtures -public import Tests.Ix.Tc.IngressMetaTests +public import Ix.Kernel +public import Tests.Ix.Kernel.IxonFixtures +public import Tests.Ix.Kernel.IngressMetaTests /-! Constant-checking tests: `checkConst` dispatch (axio/defn/theorem/quot paths), well-scopedness validation, the safety lattice, defn-block coordination with failure replay, the lazy-fault driver end-to-end (`TcState.newLazyAnon`, -`checkEnvAnon`), and the parallel driver (`Ix.Tc.ParCheck`: +`checkEnvAnon`), and the parallel driver (`Ix.Kernel.ParCheck`: `ingressAnonEnvParallel`/`ingressMetaEnvParallel` + `buildCheckWork` + `checkEnvParallel`) against the sequential verdicts. -/ -namespace Tests.Tc.CheckTests +namespace Tests.Kernel.CheckTests open LSpec -open Ix.Tc -open Tests.Tc.Fixtures (storeConst storeMutsWithProjs axiomA envA) +open Ix.Kernel +open Tests.Kernel.Fixtures (storeConst storeMutsWithProjs axiomA envA) abbrev AE := KExpr .anon @@ -515,7 +515,7 @@ def cacheIsolationTests : TestSeq := Acceleration substitutes native semantics for the declarations at the hardcoded primitive addresses, so those must verify even when the caller -skips integrity checking (`Ix/Tc/Primitive.lean` at `primAddrSet`). -/ +skips integrity checking (`Ix/Kernel/Primitive.lean` at `primAddrSet`). -/ def primVerifyTests : TestSeq := test "prim-addressed constant is verified even under verify := false" @@ -912,7 +912,7 @@ def recursorTests : TestSeq := failsContaining ixon recAddr "populate_recursor_rules_from_block: canonical header mismatch" : Bool)) -/-! ### Parallel driver (`Ix.Tc.ParCheck`) -/ +/-! ### Parallel driver (`Ix.Kernel.ParCheck`) -/ /-- Env with two passing standalones (A, idA) and a passing inductive block (B, B.mk) — 4 targets, mirrors the `checkEnvAnon` lazy test. -/ @@ -1003,7 +1003,7 @@ def parallelTests : TestSeq := run2 passed={r2.passed} fails={r2.failures.size}") | e1, e2 => return (false, 0, 0, some s!"{e1.isOk} {e2.isOk}")) .done ++ .individualIO "parallel meta check over meta fixture env" none (do - let (env, _, _) := Tests.Tc.IngressMeta.envMetaDefn + let (env, _, _) := Tests.Kernel.IngressMeta.envMetaDefn match ingressMetaEnvParallel env (chunkSize := 1) with | .error e => return (false, 0, 0, some s!"ingress: {e}") | .ok kenv => @@ -1023,4 +1023,4 @@ public def suite : List TestSeq := lazyTests, cacheIsolationTests, primVerifyTests, inductiveTests, recursorTests, parallelTests] -end Tests.Tc.CheckTests +end Tests.Kernel.CheckTests diff --git a/Tests/Ix/Tc/Roundtrip.lean b/Tests/Ix/Kernel/CheckerRoundtrip.lean similarity index 92% rename from Tests/Ix/Tc/Roundtrip.lean rename to Tests/Ix/Kernel/CheckerRoundtrip.lean index 44a990b5b..783f0cd54 100644 --- a/Tests/Ix/Tc/Roundtrip.lean +++ b/Tests/Ix/Kernel/CheckerRoundtrip.lean @@ -1,13 +1,13 @@ module public import LSpec -public import Ix.Tc +public import Ix.Kernel public import Ix.CompileM public import Ix.CanonM public import Ix.Meta public import Ix.Common -public import Tests.Ix.Tc.AnonDiff -public import Tests.Ix.Tc.IxonFixtures +public import Tests.Ix.Kernel.AnonDiff +public import Tests.Ix.Kernel.IxonFixtures /-! Kernel ↔ Ixon roundtrip (`tc-roundtrip` ignored suite + `tc-unit` entries). @@ -18,12 +18,12 @@ Two halves, mirroring the Rust `kernel-ixon-roundtrip`'s scope: ingressed into the pure-Lean kernel, egressed back to an `Ixon.Constant`, and compared **structurally** against the original (canonical forms — sharing expanded, tables renumbered, universes reduced; see -`Ix.Tc.Egress`). Projections compare byte-exact. Certifies exactly the +`Ix.Kernel.Egress`). Projections compare byte-exact. Certifies exactly the kernel-held, hash-relevant structure. **Meta** (full fidelity): the whole env is meta-ingressed (phase-parallel: chunked local envs merged via `KEnv.union`), every named entry is egressed -back to `Ix.ConstantInfo` (`Ix.Tc.EgressLean`), and compared against +back to `Ix.ConstantInfo` (`Ix.Kernel.EgressLean`), and compared against `CanonM.canonConst` of the source Lean constant with Rust `compare_envs` semantics — LEON content hashes are name/info/mdata-sensitive, so this certifies metadata fidelity too. Skipped with counts: aux-rewritten @@ -41,18 +41,18 @@ Layers: this module's closure plus the level-spelling fixtures) — anon and meta. Parallel over the task pool. Arbitrary Lean files (and external `.ixe` images) go through `ix validate-lean` instead, which - shares the same drivers (`Ix.Tc.Validate`). + shares the same drivers (`Ix.Kernel.Validate`). -/ -namespace Tests.Tc.Roundtrip +namespace Tests.Kernel.Roundtrip open LSpec -open Ix.Tc +open Ix.Kernel public section /-- Roundtrip every work item of an env (shared driver: - `Ix.Tc.anonRoundtripEnv`). -/ + `Ix.Kernel.anonRoundtripEnv`). -/ def roundtripAll (ixonEnv : Ixon.Env) : Nat × Option String := anonRoundtripEnv ixonEnv @@ -66,7 +66,7 @@ def fixtureEnv : IO Lean.Environment := /-! ### Fixture roundtrips + tamper negatives (`tc-unit`) -/ -open Tests.Tc.Fixtures in +open Tests.Kernel.Fixtures in /-- All hand-built fixture envs roundtrip clean. Exercises: axioms, defns with refs, nat/str literal blobs, `share` normalization, mutual `recur` blocks with projections, inductive blocks with ctor projections. -/ @@ -92,7 +92,7 @@ def fixtureTests : TestSeq := Id.run do ts := ts ++ .individualIO s!"roundtrip fixture: {label}" none testIO .done return ts -open Tests.Tc.Fixtures in +open Tests.Kernel.Fixtures in /-- Ingress a standalone fixture constant and hand back `(original, kernel constant)` for tampering. -/ def ingressedStandalone (env : Ixon.Env) (addr : Address) : @@ -119,7 +119,7 @@ def tamperCaught (original : Ixon.Constant) (tampered : KConst .anon) | .ok (some _) => true | .error _ => true -open Tests.Tc.Fixtures in +open Tests.Kernel.Fixtures in /-- Standalone recursor fixture (`R.rec`-shaped): one rule, for the dropped-rule tamper. -/ def envRecrStandalone : Ixon.Env × Address := Id.run do @@ -130,7 +130,7 @@ def envRecrStandalone : Ixon.Env × Address := Id.run do let addr := Address.blake3 (Ixon.serConstant c) return (env.storeConst addr c, addr) -open Tests.Tc.Fixtures in +open Tests.Kernel.Fixtures in /-- Standalone defn whose value is a `letE` (for the nonDep-flip tamper). -/ def envLetDefn : Ixon.Env × Address := Id.run do let (env, aAddr) := envA @@ -143,7 +143,7 @@ def envLetDefn : Ixon.Env × Address := Id.run do /-- Tamper 1: swap a defn's value for its type. -/ def tamperDefnValue : Bool := - let (env, _, idAddr) := Tests.Tc.Fixtures.envIdA + let (env, _, idAddr) := Tests.Kernel.Fixtures.envIdA match ingressedStandalone env idAddr with | .error _ => false | .ok (orig, kc) => @@ -209,7 +209,7 @@ def unitTests : List TestSeq := [fixtureTests, negativeTests] constant of the resulting env. -/ def roundtripOnSeeds (leanEnv : Lean.Environment) (label : String) (seeds : List Lean.Name) : IO (Nat × Option String) := do - let consts := Tests.Tc.AnonDiff.closureOf leanEnv seeds + let consts := Tests.Kernel.AnonDiff.closureOf leanEnv seeds if consts.isEmpty then return (0, some s!"empty closure for {seeds}") let dir ← IO.FS.createTempDir @@ -222,7 +222,7 @@ def roundtripOnSeeds (leanEnv : Lean.Environment) (label : String) | .ok ixonEnv => return roundtripAll ixonEnv def seedSets : List (String × List Lean.Name) := - Tests.Tc.AnonDiff.seedSets ++ + Tests.Kernel.AnonDiff.seedSets ++ [ ("inductives-recursors", [`Nat.rec, `List.rec, `Acc.rec, `WellFounded.fix, `Prod.rec, `PSigma.rec, `Or.rec]), @@ -254,7 +254,7 @@ def closureSuite : TestSeq := Id.run do let mut ts : TestSeq := .done for (label, seeds) in seedSets do ts := ts ++ .individualIO s!"roundtrip closure: {label}" none (do - let env ← Tests.Tc.Roundtrip.fixtureEnv + let env ← Tests.Kernel.Roundtrip.fixtureEnv let (rows, err?) ← roundtripOnSeeds env label seeds return (err?.isNone, rows, 0, err?)) .done return ts @@ -266,7 +266,7 @@ def closureSuite : TestSeq := Id.run do `ix roundtrip-tc ` instead. -/ def wholeEnvSuite : TestSeq := .individualIO "roundtrip whole get_env environment" none (do - let leanEnv ← Tests.Tc.Roundtrip.fixtureEnv + let leanEnv ← Tests.Kernel.Roundtrip.fixtureEnv let consts := leanEnv.constants.toList let dir ← IO.FS.createTempDir let path := dir / "tc-roundtrip-whole-env.ixe" @@ -293,10 +293,10 @@ names are informational `notFound`; aux-rewritten entries fidelity is covered by the anon roundtrip above). -/ /-- Compile `consts` through the Rust compiler and run the shared meta - roundtrip driver (`Ix.Tc.metaRoundtripEnv`) against `leanEnv`. -/ + roundtrip driver (`Ix.Kernel.metaRoundtripEnv`) against `leanEnv`. -/ def metaRoundtripOn (leanEnv : Lean.Environment) (label : String) (consts : List (Lean.Name × Lean.ConstantInfo)) : - IO (Ix.Tc.MetaRoundtripReport × Option String) := do + IO (Ix.Kernel.MetaRoundtripReport × Option String) := do let dir ← IO.FS.createTempDir let path := dir / s!"tc-meta-roundtrip-{label}.ixe" let _ ← Ix.CompileM.rsCompileEnvBytesFFI consts path.toString true @@ -322,8 +322,8 @@ def metaClosureSuite : TestSeq := Id.run do let mut ts : TestSeq := .done for (label, seeds) in seedSets do ts := ts ++ .individualIO s!"meta roundtrip closure: {label}" none (do - let env ← Tests.Tc.Roundtrip.fixtureEnv - let consts := Tests.Tc.AnonDiff.closureOf env seeds + let env ← Tests.Kernel.Roundtrip.fixtureEnv + let consts := Tests.Kernel.AnonDiff.closureOf env seeds let (report, err?) ← metaRoundtripOn env label consts return (err?.isNone, report.checked, 0, err?)) .done return ts @@ -331,7 +331,7 @@ def metaClosureSuite : TestSeq := Id.run do /-- The centerpiece: meta roundtrip of the WHOLE current Lean env. -/ def metaWholeEnvSuite : TestSeq := .individualIO "meta roundtrip whole get_env environment" none (do - let leanEnv ← Tests.Tc.Roundtrip.fixtureEnv + let leanEnv ← Tests.Kernel.Roundtrip.fixtureEnv let (report, err?) ← metaRoundtripOn leanEnv "whole-env" leanEnv.constants.toList IO.println s!"[tc-meta-roundtrip] checked {report.checked}, \ @@ -344,4 +344,4 @@ public def suite : List TestSeq := end -end Tests.Tc.Roundtrip +end Tests.Kernel.Roundtrip diff --git a/Tests/Ix/Kernel/FocusedLeanCheck.lean b/Tests/Ix/Kernel/FocusedLeanCheck.lean index 50a507c00..7919cd400 100644 --- a/Tests/Ix/Kernel/FocusedLeanCheck.lean +++ b/Tests/Ix/Kernel/FocusedLeanCheck.lean @@ -32,7 +32,7 @@ def checkClosure (tag fileStem : String) {status.ungrounded.size} constant(s); first: {first}") let bytes ← IO.FS.readBinFile path - let cfg : Ix.Tc.ParCheckCfg := { + let cfg : Ix.Kernel.ParCheckCfg := { workers := 1 verbose := true progressMs := 0 @@ -43,7 +43,7 @@ def checkClosure (tag fileStem : String) let only := names.map fun name => toString name match ← Ix.Cli.CheckLeanCmd.runMetaCheck bytes cfg true none only none with | .error err => - return (false, 0, names.size, some s!"[{tag}] Ix.Tc driver failed: {err}") + return (false, 0, names.size, some s!"[{tag}] Ix.Kernel driver failed: {err}") | .ok (report, workItems) => IO.println s!"[{tag}] {report.passed}/{report.targetsCovered} passed in \ {report.elapsedMs}ms" diff --git a/Tests/Ix/Tc/InferDefEq.lean b/Tests/Ix/Kernel/InferDefEq.lean similarity index 97% rename from Tests/Ix/Tc/InferDefEq.lean rename to Tests/Ix/Kernel/InferDefEq.lean index 4d88638d0..e47097904 100644 --- a/Tests/Ix/Tc/InferDefEq.lean +++ b/Tests/Ix/Kernel/InferDefEq.lean @@ -1,22 +1,22 @@ module public import LSpec -public import Ix.Tc -public import Tests.Ix.Tc.IxonFixtures -public import Tests.Ix.Tc.WhnfTests +public import Ix.Kernel +public import Tests.Ix.Kernel.IxonFixtures +public import Tests.Ix.Kernel.WhnfTests /-! Type inference and definitional equality with the real knot -(`Ix.Tc.methodsN`). Exercises the tiers that were inert under stub methods: +(`Ix.Kernel.methodsN`). Exercises the tiers that were inert under stub methods: K-like ctor synthesis in iota, proof irrelevance, lambda eta, struct eta, and unit-like equality. -/ -namespace Tests.Tc.InferDefEq +namespace Tests.Kernel.InferDefEq open LSpec -open Ix.Tc -open Tests.Tc.Fixtures (storeConst storeMutsWithProjs axiomA) +open Ix.Kernel +open Tests.Kernel.Fixtures (storeConst storeMutsWithProjs axiomA) abbrev AE := KExpr .anon @@ -137,7 +137,7 @@ def ingressEnvOf (p : Ixon.Env × Address) : AnonEnv := /-- `A : Sort 1` plus `idA : A → A := λ a. a` and an axiom `c : A`. -/ def baseEnv : AnonEnv × Address × Address × Address := Id.run do - let (ixon, aAddr) := Tests.Tc.Fixtures.envA + let (ixon, aAddr) := Tests.Kernel.Fixtures.envA let idDefn : Ixon.Constant := ⟨.defn ⟨.defn, .safe, 0, .leanAll (.ref 0 #[]) (.ref 0 #[]), .leanLam (.ref 0 #[]) (.var 0)⟩, @@ -151,7 +151,7 @@ def baseEnv : AnonEnv × Address × Address × Address := Id.run do `idA`. The hint channel is separate from the content-addressed constant. -/ def sameHeadEnv (hints : Lean.ReducibilityHints) : AnonEnv × Address × Address × Address × Address := Id.run do - let (ixon, aAddr) := Tests.Tc.Fixtures.envA + let (ixon, aAddr) := Tests.Kernel.Fixtures.envA let idDefn : Ixon.Constant := ⟨.defn ⟨.defn, .safe, 0, .leanAll (.ref 0 #[]) (.ref 0 #[]), .leanLam (.ref 0 #[]) (.var 0)⟩, @@ -302,7 +302,7 @@ def defEqAdvanced : TestSeq := -- irrelevance can. defEq env (pAddr hAddr) (pAddr hoAddr) : Bool)) ++ test "lambda eta (λ x. f x ≡ f)" - ((let (ixon, aAddr) := Tests.Tc.Fixtures.envA + ((let (ixon, aAddr) := Tests.Kernel.Fixtures.envA let fAxio : Ixon.Constant := ⟨.axio ⟨false, 0, .leanAll (.ref 0 #[]) (.ref 0 #[])⟩, #[], #[aAddr], #[]⟩ let (ixon, fAddr) := storeConst ixon fAxio @@ -411,4 +411,4 @@ def natOffsetTests : TestSeq := public def suite : List TestSeq := [knotFuelTests, inferTests, defEqBasics, defEqAdvanced, natOffsetTests] -end Tests.Tc.InferDefEq +end Tests.Kernel.InferDefEq diff --git a/Tests/Ix/Tc/IngressMetaTests.lean b/Tests/Ix/Kernel/IngressMetaTests.lean similarity index 96% rename from Tests/Ix/Tc/IngressMetaTests.lean rename to Tests/Ix/Kernel/IngressMetaTests.lean index 17fe3b422..0801df2f3 100644 --- a/Tests/Ix/Tc/IngressMetaTests.lean +++ b/Tests/Ix/Kernel/IngressMetaTests.lean @@ -1,12 +1,12 @@ module public import LSpec -public import Ix.Tc +public import Ix.Kernel public import Ix.CompileM public import Ix.Meta public import Ix.Common -public import Tests.Ix.Tc.AnonDiff -public import Tests.Ix.Tc.IxonFixtures +public import Tests.Ix.Kernel.AnonDiff +public import Tests.Ix.Kernel.IxonFixtures /-! Meta-mode ingress tests (`tc-unit` entries + `tc-ingress-meta` ignored). @@ -26,10 +26,10 @@ item, and check per-named-entry address parity against the anon ingress of the same env. Whole-env meta scale is exercised by the R3 roundtrip. -/ -namespace Tests.Tc.IngressMeta +namespace Tests.Kernel.IngressMeta open LSpec -open Ix.Tc +open Ix.Kernel public section @@ -64,7 +64,7 @@ def nId : Ix.Name := Ix.Name.mkStr Ix.Name.mkAnon "idA" def nX : Ix.Name := Ix.Name.mkStr Ix.Name.mkAnon "x" def nKey : Ix.Name := Ix.Name.mkStr Ix.Name.mkAnon "key" -open Tests.Tc.Fixtures in +open Tests.Kernel.Fixtures in /-- Axiom `A : Sort 1` with `.axio` metadata: name `A`, one level param `u`, leaf arena. -/ def envMetaAxiom : Ixon.Env × Address := Id.run do @@ -74,7 +74,7 @@ def envMetaAxiom : Ixon.Env × Address := Id.run do let env := withNamed env nA aAddr cm (extraNames := [nU]) return (env, aAddr) -open Tests.Tc.Fixtures in +open Tests.Kernel.Fixtures in /-- Definition `idA : A → A := fun (x : A) => x` with full arena metadata: binder name `x` (implicit), and an mdata layer on the value root. -/ def envMetaDefn : Ixon.Env × Address × Address := Id.run do @@ -118,7 +118,7 @@ def metaAxiomChecks : Bool × Bool × Bool := Id.run do | some (.axio name levelParams _ _ _) => -- Address parity with the anon ingress of the same env. let anonOk := Id.run do - match Tests.Tc.Fixtures.runIngress + match Tests.Kernel.Fixtures.runIngress (ingressAnonAddrShallow env aAddr) with | .ok (_, aenv) => match aenv.get? ⟨aAddr, ()⟩, kenv.get? ⟨aAddr, nA⟩ with @@ -156,7 +156,7 @@ def metaDefnChecks : Bool × Bool × Bool × Bool := Id.run do def metaDefnAddrParity : Bool := Id.run do let (env, dAddr, _) := envMetaDefn match ingressAllMeta env, - Tests.Tc.Fixtures.runIngress (ingressAnonAddrShallow env dAddr) with + Tests.Kernel.Fixtures.runIngress (ingressAnonAddrShallow env dAddr) with | .ok kenv, .ok (_, aenv) => match kenv.get? ⟨dAddr, nId⟩, aenv.get? ⟨dAddr, ()⟩ with | some (.defn (ty := mty) (val := mval) ..), @@ -165,7 +165,7 @@ def metaDefnAddrParity : Bool := Id.run do | _, _ => return false | _, _ => return false -open Tests.Tc.Fixtures in +open Tests.Kernel.Fixtures in /-- `ref` without an arena `.ref` node is a hard error. -/ def metaRefMissingArenaCaught : Bool := Id.run do let (env, aAddr) := envA @@ -179,7 +179,7 @@ def metaRefMissingArenaCaught : Bool := Id.run do | .error e => return (e.splitOn "has no metadata name").length > 1 | .ok _ => return false -open Tests.Tc.Fixtures in +open Tests.Kernel.Fixtures in /-- `lam` without an arena binder soft-falls-back to a synthetic name. -/ def metaLamSynthFallback : Bool := Id.run do let (env, aAddr) := envA @@ -201,7 +201,7 @@ def metaLamSynthFallback : Bool := Id.run do return n == Ix.Name.mkStr Ix.Name.mkAnon "_s0" | _ => return false -open Tests.Tc.Fixtures in +open Tests.Kernel.Fixtures in /-- A `callSite` arena node distributes canonical-arg metadata and names the head from the call-site's `name` field. -/ def metaCallSiteFixture : Bool := Id.run do @@ -271,7 +271,7 @@ def weirdULevel : Ix.Level := (Ix.Level.mkParam nU)) (Ix.Level.mkParam nU) -open Tests.Tc.Fixtures in +open Tests.Kernel.Fixtures in /-- Axiom `W.{u} : Sort (imax (imax 1 u) u)` — one weird table entry. -/ def envWeirdSort : Ixon.Env × Address := Id.run do let c : Ixon.Constant := ⟨.axio ⟨false, 1, .sort 0⟩, #[], #[], #[weirdU]⟩ @@ -298,7 +298,7 @@ def decorSortChecks : Bool × Bool × Bool := Id.run do ty.addr == (KExpr.mkSort (m := .anon) (.mkParam 0 ())).addr -- Anon parity (tc-meta-addr survives the decoration). let parityOk := Id.run do - match Tests.Tc.Fixtures.runIngress + match Tests.Kernel.Fixtures.runIngress (ingressAnonAddrShallow env wAddr) with | .ok (_, aenv) => match aenv.get? ⟨wAddr, ()⟩ with @@ -326,7 +326,7 @@ def decorSortEgress : Bool × Bool := Id.run do | _ => return (false, false) | _ => return (false, false) -open Tests.Tc.Fixtures in +open Tests.Kernel.Fixtures in /-- Axiom `T.{u} : Sort (imax (imax 1 u) u) → Sort u` — BOTH spellings of one Géran class in one constant (the Design-A killer): the two sort occurrences share a semantic address but differ in decoration. -/ @@ -373,7 +373,7 @@ def decorTwinChecks : Bool × Bool × Bool := Id.run do | _ => return (false, false, false) | _ => return (false, false, false) -open Tests.Tc.Fixtures in +open Tests.Kernel.Fixtures in /-- `gpoly.{u} : @fpoly.{imax (imax 1 u) u} → @fpoly.{u}` — const-arg twins: the two `const` occurrences share a semantic address but only the weird one carries a decoration (full original list). -/ @@ -422,7 +422,7 @@ def decorConstChecks : Bool × Bool × Bool := Id.run do | _ => return (false, false, false) | _ => return (false, false, false) -open Tests.Tc.Fixtures in +open Tests.Kernel.Fixtures in /-- Control: an already-normal spelling (`max u v`) gets NO decoration. -/ def decorNormalControl : Bool := Id.run do let c : Ixon.Constant := @@ -479,7 +479,7 @@ def unitTests : List TestSeq := `(namedChecked, firstErr?)`. -/ def closureParity (leanEnv : Lean.Environment) (label : String) (seeds : List Lean.Name) : IO (Nat × Option String) := do - let consts := Tests.Tc.AnonDiff.closureOf leanEnv seeds + let consts := Tests.Kernel.AnonDiff.closureOf leanEnv seeds if consts.isEmpty then return (0, some s!"empty closure for {seeds}") let dir ← IO.FS.createTempDir @@ -495,7 +495,7 @@ def closureParity (leanEnv : Lean.Environment) (label : String) | .error e => return (0, some s!"meta ingress failed: {e}") | .ok kenv => pure kenv -- Anon ingress of the whole env. - let aenv ← match Tests.Tc.Fixtures.runIngress (do + let aenv ← match Tests.Kernel.Fixtures.runIngress (do match buildAnonWork ixonEnv with | .error e => throw e | .ok work => @@ -529,7 +529,7 @@ def closureParity (leanEnv : Lean.Environment) (label : String) return (checked, firstErr) def suite : List TestSeq := - (Tests.Tc.AnonDiff.seedSets ++ + (Tests.Kernel.AnonDiff.seedSets ++ [("inductives-recursors", [`Nat.rec, `List.rec, `Acc.rec, `Prod.rec, `PSigma.rec])]).map fun (label, seeds) => @@ -540,4 +540,4 @@ def suite : List TestSeq := end -end Tests.Tc.IngressMeta +end Tests.Kernel.IngressMeta diff --git a/Tests/Ix/Tc/InitScale.lean b/Tests/Ix/Kernel/InitScale.lean similarity index 94% rename from Tests/Ix/Tc/InitScale.lean rename to Tests/Ix/Kernel/InitScale.lean index 19553e059..7bff70048 100644 --- a/Tests/Ix/Tc/InitScale.lean +++ b/Tests/Ix/Kernel/InitScale.lean @@ -1,9 +1,9 @@ module public import LSpec -public import Ix.Tc +public import Ix.Kernel public import Ix.KernelCheck -public import Tests.Ix.Tc.AnonDiff +public import Tests.Ix.Kernel.AnonDiff /-! Init-scale anon verdict differential (`tc-init`, ignored suite). @@ -20,10 +20,10 @@ Two modes: Acceptance: zero verdict mismatches vs `rsCheckAnonFFI`. -/ -namespace Tests.Tc.InitScale +namespace Tests.Kernel.InitScale open LSpec -open Ix.Tc +open Ix.Kernel /-- Broad Init-representative seeds: arithmetic, containers, characters, strings, well-founded recursion, decidability, structures with @@ -89,11 +89,11 @@ def envGatedSuite : TestSeq := Id.run do | none => let env ← get_env! let (compared, skipped, diff?) ← - Tests.Tc.AnonDiff.diffOnSeeds env "init-scale" initSeeds + Tests.Kernel.AnonDiff.diffOnSeeds env "init-scale" initSeeds let msg := diff?.map (s!"compared {compared}, skipped {skipped}: " ++ ·) return (diff?.isNone && skipped == 0, compared, skipped, msg)) .done return ts public def suite : List TestSeq := [envGatedSuite] -end Tests.Tc.InitScale +end Tests.Kernel.InitScale diff --git a/Tests/Ix/Tc/IxonFixtures.lean b/Tests/Ix/Kernel/IxonFixtures.lean similarity index 98% rename from Tests/Ix/Tc/IxonFixtures.lean rename to Tests/Ix/Kernel/IxonFixtures.lean index d636767f7..e516d38d1 100644 --- a/Tests/Ix/Tc/IxonFixtures.lean +++ b/Tests/Ix/Kernel/IxonFixtures.lean @@ -1,26 +1,26 @@ module public import LSpec -public import Ix.Tc +public import Ix.Kernel /-! -Hand-built `Ixon.Env` fixtures for `Ix.Tc` ingress tests (no FFI, no +Hand-built `Ixon.Env` fixtures for `Ix.Kernel` ingress tests (no FFI, no compiler): constants stored at `blake3 (serConstant c)`, mutual blocks stored together with all their projection constants (mirroring what the compiler emits), plus ingress negative cases (index OOB, missing blob, missing projection, integrity violation). The accept tests assert exact kernel node addresses computed independently -via the `Ix.Tc` smart constructors — a pure-Lean end-to-end check of the +via the `Ix.Kernel` smart constructors — a pure-Lean end-to-end check of the index-resolution + Blake3 pipeline. -/ public section -namespace Tests.Tc.Fixtures +namespace Tests.Kernel.Fixtures open LSpec -open Ix.Tc +open Ix.Kernel /-! ### Builders -/ @@ -322,6 +322,6 @@ def rejectTests : TestSeq := public def suite : List TestSeq := [acceptTests, rejectTests] -end Tests.Tc.Fixtures +end Tests.Kernel.Fixtures end diff --git a/Tests/Ix/Tc/ParityEnv.lean b/Tests/Ix/Kernel/ParityEnv.lean similarity index 97% rename from Tests/Ix/Tc/ParityEnv.lean rename to Tests/Ix/Kernel/ParityEnv.lean index 9b2afb0bf..672280506 100644 --- a/Tests/Ix/Tc/ParityEnv.lean +++ b/Tests/Ix/Kernel/ParityEnv.lean @@ -16,7 +16,7 @@ plus the checked-in seed list when the file isn't already present, so the tests run unconditionally instead of skipping when no fixture is supplied. -/ -namespace Tests.Tc.ParityEnv +namespace Tests.Kernel.ParityEnv open Lean @@ -49,4 +49,4 @@ public def ensure : IO System.FilePath := do let _ ← Ix.CompileM.rsCompileEnvBytesFFI closed out.toString false return out -end Tests.Tc.ParityEnv +end Tests.Kernel.ParityEnv diff --git a/Tests/Ix/Tc/Pins.lean b/Tests/Ix/Kernel/Pins.lean similarity index 94% rename from Tests/Ix/Tc/Pins.lean rename to Tests/Ix/Kernel/Pins.lean index 0fb67426a..847d9c3e6 100644 --- a/Tests/Ix/Tc/Pins.lean +++ b/Tests/Ix/Kernel/Pins.lean @@ -1,26 +1,26 @@ module public import LSpec -public import Ix.Tc +public import Ix.Kernel public import Ix.Cli.CheckLeanCmd -public import Tests.Ix.Tc.ParityEnv +public import Tests.Ix.Kernel.ParityEnv /-! `tc-pins` (ignored runner): regression pins for the pure-Lean kernel -against a real serialized env (compiled on demand — see `Tests.Tc.ParityEnv`). +against a real serialized env (compiled on demand — see `Tests.Kernel.ParityEnv`). Each pin is a constant that previously OOMed, was falsely rejected at depth 2001, or blew the worker stack (see the short-circuit note in -`Ix/Tc/DefEq.lean` and `ParCheckCfg.stackBytes`); the suite ingresses +`Ix/Kernel/DefEq.lean` and `ParCheckCfg.stackBytes`); the suite ingresses the env once and checks each pin subject-only under a wall-clock budget. Budgets are generous — the pins guard ∞/OOM regression classes, not micro-perf. Known-slow pins (open perf issues) run only with `IX_PINS_SLOW=1`. -/ -namespace Tests.Tc.Pins +namespace Tests.Kernel.Pins open LSpec -open Ix.Tc +open Ix.Kernel structure Pin where /-- Fail-out-style label (meta-mode Lean name rendering). -/ @@ -76,7 +76,7 @@ def checkPin (kenv : MetaEnv) (prims : Primitives .meta) public def run : IO UInt32 := do IO.println "tc-pins" - let path ← Tests.Tc.ParityEnv.ensure + let path ← Tests.Kernel.ParityEnv.ensure let includeSlow := (← IO.getEnv "IX_PINS_SLOW").isSome let t0 ← IO.monoMsNow let bytes ← IO.FS.readBinFile path @@ -99,4 +99,4 @@ public def run : IO UInt32 := do seq := seq ++ (← checkPin kenv prims work pin) lspecIO (.ofList [("tc-pins", [seq])]) [] -end Tests.Tc.Pins +end Tests.Kernel.Pins diff --git a/Tests/Ix/Kernel/PrimAddrs.lean b/Tests/Ix/Kernel/PrimAddrs.lean index 68f638e96..3615c1721 100644 --- a/Tests/Ix/Kernel/PrimAddrs.lean +++ b/Tests/Ix/Kernel/PrimAddrs.lean @@ -19,7 +19,7 @@ This test closes that class. It reads the byte arrays out of the ELABORATED toplevel — not the source text — and compares each against - the address computed at test runtime from `Ix.Tc.PrimAddrs`, which is + the address computed at test runtime from `Ix.Kernel.PrimAddrs`, which is the same table the host-side closure walker and ingress verifier use. Any drift between the two sources of truth fails here. @@ -32,7 +32,7 @@ import Ix.Meta import Ix.Aiur.Compiler import Ix.IxVM import Ix.IxVM.Toplevel -import Ix.Tc.Primitive +import Ix.Kernel.Primitive import LSpec open LSpec @@ -68,12 +68,12 @@ private def collectAddrFns (t : Aiur.Source.Toplevel) : /-- The canonical address set, computed at test runtime from the Lean parity table + reserved markers — the same source the host closure - walker (`Ix.Tc.primAddrSet`) consults. -/ + walker (`Ix.Kernel.primAddrSet`) consults. -/ private def canonical : Std.HashMap Address String := Id.run do let mut m : Std.HashMap Address String := {} - for (name, a) in Ix.Tc.PrimAddrs.leanParityTable do + for (name, a) in Ix.Kernel.PrimAddrs.leanParityTable do m := m.insert a name - for (name, a) in Ix.Tc.PrimAddrs.reservedMarkerAddrs do + for (name, a) in Ix.Kernel.PrimAddrs.reservedMarkerAddrs do m := m.insert a name return m diff --git a/Tests/Ix/Tc/Substrate.lean b/Tests/Ix/Kernel/Substrate.lean similarity index 98% rename from Tests/Ix/Tc/Substrate.lean rename to Tests/Ix/Kernel/Substrate.lean index c5fadfb1d..ebf1139df 100644 --- a/Tests/Ix/Tc/Substrate.lean +++ b/Tests/Ix/Kernel/Substrate.lean @@ -1,10 +1,10 @@ module public import LSpec -public import Ix.Tc +public import Ix.Kernel /-! -Unit tests for the `Ix.Tc` substrate (Equiv/Primitive/Env/Subst/Lctx/Monad): +Unit tests for the `Ix.Kernel` substrate (Equiv/Primitive/Env/Subst/Lctx/Monad): subst/lift/instantiateRev/abstractFVars round-trips (ported from subst.rs tests), cheap-beta cases, ctx-id chain determinism, suffix-aware cache keys, fuel/tick semantics under EStateM, union-find behavior, local-context @@ -12,10 +12,10 @@ open/close, universe instantiation, and the primitives parity check against the live Rust `PrimAddrs::new()` table (FFI). -/ -namespace Tests.Tc.Substrate +namespace Tests.Kernel.Substrate open LSpec -open Ix.Tc +open Ix.Kernel /-! ### Helpers -/ @@ -473,4 +473,4 @@ public def suite : List TestSeq := cheapBetaTests, internTests, equivTests, ctxTests, fuelTests, openBinderTests, univInstTests, envTests, primsParity] -end Tests.Tc.Substrate +end Tests.Kernel.Substrate diff --git a/Tests/Ix/Tc/TutorialTc.lean b/Tests/Ix/Kernel/TutorialTc.lean similarity index 96% rename from Tests/Ix/Tc/TutorialTc.lean rename to Tests/Ix/Kernel/TutorialTc.lean index a65055b61..91c30e44c 100644 --- a/Tests/Ix/Tc/TutorialTc.lean +++ b/Tests/Ix/Kernel/TutorialTc.lean @@ -1,11 +1,11 @@ /- - Kernel tutorial test cases through the pure-Lean `Ix.Tc` kernel + Kernel tutorial test cases through the pure-Lean `Ix.Kernel` kernel (`tc-tutorial`, ignored suite). Mirrors `Tests/Ix/Kernel/Tutorial.lean` (the Rust-kernel runner): reads the same good/bad test cases registered by `TutorialDefs.lean`, compiles each batch through the Rust compiler to Ixon bytes, and checks verdicts - with `Ix.Tc.checkEnvAnon` instead of `rsCheckConstsFFI`. + with `Ix.Kernel.checkEnvAnon` instead of `rsCheckConstsFFI`. Differences from the Rust runner, by necessity: - Renaming test cases are skipped (their collision check is Lean-side @@ -26,14 +26,14 @@ import Ix.Common import Ix.Meta import Ix.CompileM -import Ix.Tc +import Ix.Kernel import Tests.Ix.Kernel.TutorialMeta import Tests.Ix.Kernel.TutorialDefs import LSpec open LSpec -namespace Tests.Tc.TutorialTc +namespace Tests.Kernel.TutorialTc /-- Same closure walk as `Tutorial.lean`'s `collectDepsWithExtras` (that one is `private`). -/ @@ -83,7 +83,7 @@ partial def collectDepsWithExtras let closed := env.constants.toList.filter fun (n, _) => needed.contains n return (needed, closed) -/-- Compile a batch and check it through Ix.Tc. Returns +/-- Compile a batch and check it through Ix.Kernel. Returns `name ↦ (none = passed | some err)`; `.error` when the whole batch failed to compile (callers decide how that maps to expectations). -/ def checkBatchTc (consts : List (Lean.Name × Lean.ConstantInfo)) @@ -106,12 +106,12 @@ def checkBatchTc (consts : List (Lean.Name × Lean.ConstantInfo)) let fullEnv ← match Ixon.deEnv bytes with | .ok env => pure env | .error e => return .error s!"deEnv failed: {e}" - let verdicts ← match Ix.Tc.checkIxeBytesAnon bytes with + let verdicts ← match Ix.Kernel.checkIxeBytesAnon bytes with | .ok results => pure (results.foldl (init := (Std.HashMap.emptyWithCapacity results.size : Std.HashMap Address (Option String))) fun acc r => acc.insert r.addr r.err?) - | .error e => return .error s!"Ix.Tc driver failed: {e}" + | .error e => return .error s!"Ix.Kernel driver failed: {e}" let mut out : Std.HashMap Lean.Name (Option String) := {} for n in names do let ixName := Ix.Name.fromLeanName n @@ -124,7 +124,7 @@ def checkBatchTc (consts : List (Lean.Name × Lean.ConstantInfo)) return .ok out def testTutorialTc : TestSeq := - .individualIO "Ix.Tc kernel tutorial checks" none (do + .individualIO "Ix.Kernel kernel tutorial checks" none (do let leanEnv ← get_env! let testCases := Tests.Ix.Kernel.TutorialMeta.getTestCases leanEnv let rawConsts := Tests.Ix.Kernel.TutorialMeta.getRawConsts leanEnv @@ -256,4 +256,4 @@ def testTutorialTc : TestSeq := def suite : List TestSeq := [testTutorialTc] -end Tests.Tc.TutorialTc +end Tests.Kernel.TutorialTc diff --git a/Tests/Ix/Tc/Unit.lean b/Tests/Ix/Kernel/Unit.lean similarity index 98% rename from Tests/Ix/Tc/Unit.lean rename to Tests/Ix/Kernel/Unit.lean index 40e8cbc1d..7a3dc1766 100644 --- a/Tests/Ix/Tc/Unit.lean +++ b/Tests/Ix/Kernel/Unit.lean @@ -1,12 +1,12 @@ module public import LSpec -public import Ix.Tc +public import Ix.Kernel public import Ix.IxonUniv public import Tests.Gen.Ixon /-! -Unit tests for the `Ix.Tc` foundations (Mode/Id/Level/Expr/Const): +Unit tests for the `Ix.Kernel` foundations (Mode/Id/Level/Expr/Const): - raw-node address parity with the existing `Ix.Level` wire constructors (shared tag bytes ⇒ shared preimages for closed levels) @@ -17,10 +17,10 @@ Unit tests for the `Ix.Tc` foundations (Mode/Id/Level/Expr/Const): - seeded property tests (xorshift PRNG ported from level.rs) -/ -namespace Tests.Tc.Unit +namespace Tests.Kernel.Unit open LSpec -open Ix.Tc +open Ix.Kernel /-! ### Helpers -/ @@ -412,7 +412,7 @@ linearizer bug found during development lived there), plus: - P4 against the kernel's own Géran machinery (`Level.normalizeLevel` on the mk*-rebuilt `KUniv`s), modulo subsumption's empty-entry artifacts — see the `Ix/IxonUniv.lean` module-doc O1 note; -- the `Ixon.reduceUniv` ≍ `Ix.Tc.reduceIxonUniv` twin pin (same +- the `Ixon.reduceUniv` ≍ `Ix.Kernel.reduceIxonUniv` twin pin (same closure, one via transliterated rules, one via the kernel's actual smart constructors — drift here would desync the stage-1 decoration test from what ingress really does). -/ @@ -479,7 +479,7 @@ def canonUnivTests : TestSeq := ++ test "canonUniv P4: kernel Géran oracle, modulo empty entries \ (exhaustive ≤6)" p4 ++ test "canonUniv P6: mk* rebuild absorbed (exhaustive ≤6)" p6 - ++ test "reduceUniv ≍ Tc.reduceIxonUniv twin agreement (exhaustive ≤6)" + ++ test "reduceUniv ≍ Kernel.reduceIxonUniv twin agreement (exhaustive ≤6)" agree public def suite : List TestSeq := @@ -487,4 +487,4 @@ public def suite : List TestSeq := canonicalTotalizationTests, occurrenceTests, exprInfo, smartCtors, levelAlgebra, props, modeTests, canonUnivTests] -end Tests.Tc.Unit +end Tests.Kernel.Unit diff --git a/Tests/Ix/Tc/WhnfTests.lean b/Tests/Ix/Kernel/WhnfTests.lean similarity index 98% rename from Tests/Ix/Tc/WhnfTests.lean rename to Tests/Ix/Kernel/WhnfTests.lean index 88c2e1cb1..08f764f4e 100644 --- a/Tests/Ix/Tc/WhnfTests.lean +++ b/Tests/Ix/Kernel/WhnfTests.lean @@ -1,8 +1,8 @@ module public import LSpec -public import Ix.Tc -public import Tests.Ix.Tc.IxonFixtures +public import Ix.Kernel +public import Tests.Ix.Kernel.IxonFixtures /-! Whnf tests, run with throw-stub `Methods`: the infer/isDefEq back-edges @@ -13,11 +13,11 @@ reduction in isolation. Assertions are by exact node address. public section -namespace Tests.Tc.WhnfTests +namespace Tests.Kernel.WhnfTests open LSpec -open Ix.Tc -open Tests.Tc.Fixtures (storeConst storeMutsWithProjs runIngress axiomA) +open Ix.Kernel +open Tests.Kernel.Fixtures (storeConst storeMutsWithProjs runIngress axiomA) abbrev AE := KExpr .anon @@ -417,6 +417,6 @@ public def suite : List TestSeq := [pureHelperTests, structuralTests, iotaTests, natTests, nativeTests, cacheTests] -end Tests.Tc.WhnfTests +end Tests.Kernel.WhnfTests end diff --git a/Tests/Ix/Lean4Lean.lean b/Tests/Ix/Lean4Lean.lean deleted file mode 100644 index 00ead89ce..000000000 --- a/Tests/Ix/Lean4Lean.lean +++ /dev/null @@ -1,37 +0,0 @@ -import LSpec -import Benchmarks.Lean4Lean - -/-! -Smoke tests for the lean4lean dependency (ignored runner `lean4lean`): -the reference Lean4-in-Lean4 kernel accepts a real closure replayed from -the test env and rejects an ill-typed declaration. Guards the pinned -require (toolchain drift or an API change in `Lean4Lean.addDecl` surfaces -here and in `bench-lean4lean`, which shares the replay machinery). --/ - -namespace Tests.Ix.Lean4Lean - -open LSpec - -def run (env : Lean.Environment) : IO UInt32 := do - IO.println "lean4lean" - let newConstants := env.constants.fold - (init := ({} : Std.HashMap Lean.Name Lean.ConstantInfo)) fun m n ci => m.insert n ci - -- Accept: replay a real constant's whole closure into a fresh kernel env - -- (the `bench-lean4lean --consts` path). - let closRes ← (BenchLean4Lean.replayClosure env newConstants `Nat.add_comm false).toBaseIO - -- Reject: an axiom whose type is a Nat literal (not a sort) must fail - -- `checkConstantVal`. - let bogus : Lean.AxiomVal := - { name := `l4lTestBogusAxiom, levelParams := [], type := Lean.mkRawNatLit 0 - isUnsafe := false } - let rejected := !(Lean4Lean.addAxiom env.toKernelEnv bogus).isOk - let seq : TestSeq := - (match closRes with - | .ok n => test s!"Nat.add_comm closure replays through lean4lean ({n} declarations)" - (n > 0) - | .error e => test s!"Nat.add_comm closure replay failed: {e}" false) - ++ test "ill-typed axiom (type = Nat literal) is rejected" rejected - lspecIO (.ofList [("lean4lean", [seq])]) [] - -end Tests.Ix.Lean4Lean diff --git a/Tests/Main.lean b/Tests/Main.lean index 486d41861..0e7753500 100644 --- a/Tests/Main.lean +++ b/Tests/Main.lean @@ -34,19 +34,19 @@ import Tests.Ix.RustSerialize import Tests.Ix.RustDecompile import Tests.Ix.Sharing import Tests.Ix.BenchMeasures -import Tests.Ix.Tc.Unit -import Tests.Ix.Tc.Substrate -import Tests.Ix.Tc.IxonFixtures -import Tests.Ix.Tc.WhnfTests -import Tests.Ix.Tc.InferDefEq -import Tests.Ix.Tc.CheckTests -import Tests.Ix.Tc.AnonDiff -import Tests.Ix.Tc.InitScale -import Tests.Ix.Tc.TutorialTc -import Tests.Ix.Tc.Roundtrip -import Tests.Ix.Tc.IngressMetaTests -import Tests.Ix.Tc.Pins -import Tests.Ix.Tc.AccelDiff +import Tests.Ix.Kernel.Unit +import Tests.Ix.Kernel.Substrate +import Tests.Ix.Kernel.IxonFixtures +import Tests.Ix.Kernel.WhnfTests +import Tests.Ix.Kernel.InferDefEq +import Tests.Ix.Kernel.CheckTests +import Tests.Ix.Kernel.AnonDiff +import Tests.Ix.Kernel.InitScale +import Tests.Ix.Kernel.TutorialTc +import Tests.Ix.Kernel.CheckerRoundtrip +import Tests.Ix.Kernel.IngressMetaTests +import Tests.Ix.Kernel.Pins +import Tests.Ix.Kernel.AccelDiff import Tests.Ix.CanonM import Tests.Ix.GraphM import Tests.Ix.CondenseM @@ -60,7 +60,6 @@ import Tests.Cli import Tests.Ix.Ixes import Tests.ShardMap import Tests.Ix.EnvBody -import Tests.Ix.Lean4Lean import Tests.Ix.MetaEnv import Tests.Ix.Catalog import Tests.Ix.CatalogDedup @@ -106,10 +105,10 @@ def primarySuites : Std.HashMap String (List LSpec.TestSeq) := .ofList [ ("primitive-address-parity", Tests.Ix.Kernel.BuildPrimitives.paritySuite ++ Tests.Ix.Kernel.BuildPrimOrigs.paritySuite), ("decompile-unit", Tests.Decompile.unitSuite), - ("tc-unit", Tests.Tc.Unit.suite ++ Tests.Tc.Substrate.suite - ++ Tests.Tc.Fixtures.suite ++ Tests.Tc.WhnfTests.suite - ++ Tests.Tc.InferDefEq.suite ++ Tests.Tc.CheckTests.suite - ++ Tests.Tc.Roundtrip.unitTests ++ Tests.Tc.IngressMeta.unitTests), + ("tc-unit", Tests.Kernel.Unit.suite ++ Tests.Kernel.Substrate.suite + ++ Tests.Kernel.Fixtures.suite ++ Tests.Kernel.WhnfTests.suite + ++ Tests.Kernel.InferDefEq.suite ++ Tests.Kernel.CheckTests.suite + ++ Tests.Kernel.Roundtrip.unitTests ++ Tests.Kernel.IngressMeta.unitTests), ] /-- Ignored test suites - expensive, run only when explicitly requested. These require significant RAM -/ @@ -148,11 +147,11 @@ def ignoredSuites : Std.HashMap String (List LSpec.TestSeq) := .ofList [ ("kernel-check-tauceti-reduction", Tests.Ix.Kernel.CheckTauCetiReduction.suite), ("rust-kernel-build-primitives", Tests.Ix.Kernel.BuildPrimitives.suite), ("rust-kernel-build-prim-origs", Tests.Ix.Kernel.BuildPrimOrigs.suite), - ("tc-anon-diff", Tests.Tc.AnonDiff.suite), - ("tc-init", Tests.Tc.InitScale.suite), - ("tc-tutorial", Tests.Tc.TutorialTc.suite), - ("tc-roundtrip", Tests.Tc.Roundtrip.suite), - ("tc-ingress-meta", Tests.Tc.IngressMeta.suite), + ("tc-anon-diff", Tests.Kernel.AnonDiff.suite), + ("tc-init", Tests.Kernel.InitScale.suite), + ("tc-tutorial", Tests.Kernel.TutorialTc.suite), + ("tc-roundtrip", Tests.Kernel.Roundtrip.suite), + ("tc-ingress-meta", Tests.Kernel.IngressMeta.suite), ] /-- Primary test runners — quick suites run by default alongside @@ -287,15 +286,12 @@ def ignoredRunners (env : Lean.Environment) : List (String × IO UInt32) := [ -- Tests.Ix.Compile.AuxGenDiff). ("aux-gen-diff", Tests.Compile.AuxGenDiff.run env), ("decompile-diff", Tests.Compile.DecompileDiff.run env), - -- lean4lean dependency smoke: accept a real closure, reject an - -- ill-typed decl (see Tests.Ix.Lean4Lean). - ("lean4lean", Tests.Ix.Lean4Lean.run env), -- Pure-Lean kernel regression pins against a real .ixe, compiled on - -- demand (see Tests.Tc.ParityEnv). - ("tc-pins", Tests.Tc.Pins.run), + -- demand (see Tests.Kernel.ParityEnv). + ("tc-pins", Tests.Kernel.Pins.run), -- Accelerated-vs-pure reduction differentials over that same real env - -- (see Tests.Tc.AccelDiff and TcState.noAccel). - ("tc-accel-diff", Tests.Tc.AccelDiff.run), + -- (see Tests.Kernel.AccelDiff and TcState.noAccel). + ("tc-accel-diff", Tests.Kernel.AccelDiff.run), ] def main (args : List String) : IO UInt32 := do diff --git a/Tests/Theory.lean b/Tests/Theory.lean new file mode 100644 index 000000000..71dc8f405 --- /dev/null +++ b/Tests/Theory.lean @@ -0,0 +1,22 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Theory.Audit.Certified +import Tests.Theory.Certified +import Tests.Theory.Checker +import Tests.Theory.Acceptance +import Tests.Theory.Operations +import Tests.Theory.Suggestions +import Tests.Theory.Ordinary +import Tests.Theory.OrdinaryAcceptance +import Tests.Theory.Standard +import Tests.Theory.Quotient +import Tests.Theory.Structure +import Tests.Theory.Natural +import Tests.Theory.Claims +import Tests.Theory.Modeled +import Tests.Theory.ModeledNested +import Tests.Theory.ModeledPermutation +import Tests.Theory.ModeledEquations diff --git a/Tests/Theory/Acceptance.lean b/Tests/Theory/Acceptance.lean new file mode 100644 index 000000000..2c40186bb --- /dev/null +++ b/Tests/Theory/Acceptance.lean @@ -0,0 +1,161 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Accept +import Tests.Theory.Checker +import Tests.Theory.Certified + +open Ix.Theory + +namespace Tests.Theory.Acceptance + +open Ix.Theory.Certified Ix.Theory.Model +open Tests.Theory.Checker (identity identityType identityWitness) +open Tests.Theory.Certified (primitives primitiveStore) + +def prelude : Store Nat := + primitiveStore PrimitiveSignature.falseDeclaration primitives.falseElimDeclaration + +def identityAnnotations (l : VLevel) : AnnotationTree := + .lam (zeroCondition l).toRaw .leaf (.lam (zeroCondition l).toRaw .leaf .leaf) + +def identityTypeAnnotations (l : VLevel) : AnnotationTree := + .forallE (zeroCondition l).toRaw .leaf (.forallE (zeroCondition l).toRaw .leaf .leaf) + +def identityTypeLevel (l : VLevel) : VLevel := .imax (.succ l) (.imax l l) + +def identityTypeWitness (l : VLevel) : TypingWitness Nat := + .forallE (.succ l) (.imax l l) .sort (.forallE l l .bvar .bvar) + +def identityPropositionWitness : TypingWitness Nat := + .conv (.sort (identityTypeLevel .zero)) (.succ .zero) (identityTypeWitness .zero) .sort .sort + +def identityInput : ProofInput Nat := + ⟨prelude, 0, (identity .zero).erase, (identityType .zero).erase⟩ + +def identityProofWitness : ProofWitness Nat := + ⟨[], identityAnnotations .zero, identityTypeAnnotations .zero, + identityWitness .zero, identityPropositionWitness⟩ + +#guard acceptsCertified.{0,0} 50 primitives identityInput identityProofWitness + +def withDefinition (declaration : Const Nat) : Store Nat where + dom := [10, 11, 12] + nodup := by decide + blocks b := + if b = 10 then some ⟨[PrimitiveSignature.falseDeclaration]⟩ + else if b = 11 then some ⟨[primitives.falseElimDeclaration]⟩ + else if b = 12 then some ⟨[declaration]⟩ else none + mem_dom b := by + by_cases h : b = 10 <;> by_cases h' : b = 11 <;> by_cases h'' : b = 12 <;> simp_all + +def idRef : ConstRef Nat := .member 12 0 + +def idDeclaration (kind : DefKind := .definition) : Const Nat := + .defn 1 kind (identityType (.param 0)).erase (identity (.param 0)).erase .safe + +def idDefinitionWitness : DefinitionWitness Nat := + ⟨idRef, identityTypeAnnotations (.param 0), identityAnnotations (.param 0), + identityTypeLevel (.param 0), identityTypeWitness (.param 0), identityWitness (.param 0)⟩ + +def idDefinitionInput (kind : DefKind := .definition) : ProofInput Nat := + ⟨withDefinition (idDeclaration kind), 0, .const idRef [.zero], (identityType .zero).erase⟩ + +def idDefinitionProofWitness : ProofWitness Nat := + { identityProofWitness with + declarations := [.definition idDefinitionWitness] + proofAnnotations := .leaf + proofWitness := .const } + +#guard acceptsCertified.{0,0} 50 primitives (idDefinitionInput .definition) idDefinitionProofWitness +#guard acceptsCertified.{0,0} 50 primitives (idDefinitionInput .theorem) idDefinitionProofWitness +#guard acceptsCertified.{0,0} 50 primitives (idDefinitionInput .opaque) idDefinitionProofWitness + +-- Positive-universe instantiation of the same admitted definition occurs as +-- an argument to a proposition-valued function. +def largeIdentityUse : AExpr Nat := + .app (.lam .always (identityType (.succ .zero)) (identity .zero)) + (.const idRef [.succ .zero]) + +def largeIdentityUseAnnotations : AnnotationTree := + .app (.lam (some []) (identityTypeAnnotations (.succ .zero)) (identityAnnotations .zero)) .leaf + +def largeIdentityUseWitness : TypingWitness Nat := + .app .always (identityType (.succ .zero)) (identityType .zero) + (.lam (identityTypeLevel (.succ .zero)) .zero (identityType .zero) + (identityTypeWitness (.succ .zero)) identityPropositionWitness (identityWitness .zero)) + .const + +#guard acceptsCertified.{0,0} 70 primitives + { idDefinitionInput with proof := largeIdentityUse.erase } + { idDefinitionProofWitness with + proofAnnotations := largeIdentityUseAnnotations + proofWitness := largeIdentityUseWitness } + +-- Every declaration kind still checks its supplied body. +def forgedBodyInput (kind : DefKind) : ProofInput Nat := + { idDefinitionInput kind with + store := withDefinition (.defn 1 kind (identityType (.param 0)).erase (.sort .zero) .safe) } +#guard !acceptsCertified.{0,0} 50 primitives (forgedBodyInput .definition) idDefinitionProofWitness +#guard !acceptsCertified.{0,0} 50 primitives (forgedBodyInput .theorem) idDefinitionProofWitness +#guard !acceptsCertified.{0,0} 50 primitives (forgedBodyInput .opaque) idDefinitionProofWitness + +-- Object axioms cannot enter through the declaration fold. +#guard !acceptsCertified.{0,0} 50 primitives + { idDefinitionInput with + store := withDefinition (.axiom 1 (identityType (.param 0)).erase .safe) } + idDefinitionProofWitness + +-- Missing, repeated, forward, and self dependencies fail at admission. +#guard !acceptsCertified.{0,0} 50 primitives (idDefinitionInput) + { idDefinitionProofWitness with declarations := [] } +#guard !acceptsCertified.{0,0} 50 primitives (idDefinitionInput) + { idDefinitionProofWitness with declarations := [.definition idDefinitionWitness, .definition idDefinitionWitness] } +#guard !acceptsCertified.{0,0} 50 primitives + { idDefinitionInput with + store := withDefinition + (.defn 1 .definition (identityType (.param 0)).erase (.const idRef [.param 0]) .safe) } + { idDefinitionProofWitness with + declarations := [.definition { idDefinitionWitness with bodyAnnotations := .leaf, bodyWitness := .const }] } + +-- A changed original statement is checked, even with an otherwise valid proof. +#guard !acceptsCertified.{0,0} 50 primitives + { identityInput with proposition := primitives.falseExpr } + { identityProofWitness with propositionAnnotations := .leaf, propositionWitness := .const } + +-- Complete primitive metadata is checked before even a constant-free proof. +#guard !acceptsCertified.{0,0} 50 primitives + { identityInput with + store := primitiveStore (.axiom 0 (.sort .zero) .safe) primitives.falseElimDeclaration } + identityProofWitness + +def falseA : AExpr Nat := .const primitives.falseType [] +def eliminatorProposition : AExpr Nat := primitives.falseElimReading.instL [.zero] +def eliminatorAnnotations : AnnotationTree := + .forallE (some []) (.forallE none .leaf .leaf) + (.forallE (some []) .leaf (.app .leaf .leaf)) + +def eliminatorTypeLevel : VLevel := + .imax (.imax .zero (.succ .zero)) (.imax .zero .zero) + +def eliminatorPropositionWitness : TypingWitness Nat := + .conv (.sort eliminatorTypeLevel) (.succ .zero) + (.forallE (.imax .zero (.succ .zero)) (.imax .zero .zero) + (.forallE .zero (.succ .zero) .const .sort) + (.forallE .zero .zero .const (.app .never falseA (.sort .zero) .bvar .bvar))) + .sort .sort + +def eliminatorInput : ProofInput Nat := + ⟨prelude, 0, .const primitives.falseElim [.zero], eliminatorProposition.erase⟩ +def eliminatorProofWitness : ProofWitness Nat := + ⟨[], .leaf, eliminatorAnnotations, .const, eliminatorPropositionWitness⟩ + +#guard acceptsCertified.{0,0} 50 primitives eliminatorInput eliminatorProofWitness +#guard !acceptsCertified.{0,0} 50 primitives + { eliminatorInput with proof := .const primitives.falseElim [] } eliminatorProofWitness +#guard !acceptsCertified.{0,0} 50 primitives + { eliminatorInput with proof := .const primitives.falseElim [.param 0] } eliminatorProofWitness + +end Tests.Theory.Acceptance diff --git a/Tests/Theory/Audit/Certified.lean b/Tests/Theory/Audit/Certified.lean new file mode 100644 index 000000000..2239e242a --- /dev/null +++ b/Tests/Theory/Audit/Certified.lean @@ -0,0 +1,484 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Lean +import Ix.Theory.Certified + +open Ix.Theory + +/-! +# Certified mathematical dependency audit + +This audit names the implemented checking, admission, and no-False roots. +The generated report includes theorem types so that explicit +mathematical hypotheses remain visible alongside the transitive axiom leaves. +-/ + +open Lean Lean.Elab Command + +namespace Tests.Theory.Audit.Certified + +def roots : Array Name := #[ + `Ix.Theory.Model.SetTheory.empty_exists, + `Ix.Theory.Model.SetTheory.not_mem_empty, + `Ix.Theory.VLevel.eval_inst, + `Ix.Theory.Certified.PropWhen.eq_iff_holds, + `Ix.Theory.Certified.validateZero?_sound, + `Ix.Theory.Certified.zeroCondition_correct, + `Ix.Theory.Certified.zeroCondition_inst, + `Ix.Theory.Certified.instCondition_comp, + `Ix.Theory.Model.readAnnotations?, + `Ix.Theory.Model.interp_inst, + `Ix.Theory.Model.interp_instL, + `Ix.Theory.Model.interp_rename, + `Ix.Theory.Model.piR_mem_univ, + `Ix.Theory.Model.falseValue_uninhabited, + `Ix.Theory.Model.falseElimValue_mem, + `Ix.Theory.Certified.PrimitiveSignature.validate_iff, + `Ix.Theory.Certified.readDefinition?, + `Ix.Theory.Certified.LevelEq.check_sound, + `Ix.Theory.Certified.LevelEq.leq_sound, + `Ix.Theory.Model.wellDenoted_inst, + `Ix.Theory.Model.wellDenoted_beta, + `Ix.Theory.Model.extend_definition, + `Ix.Theory.Model.ConversionClaim.equation, + `Ix.Theory.Model.IndexedContainer.closed_exists, + `Ix.Theory.Model.IndexedContainer.carrier_eq, + `Ix.Theory.Model.IndexedContainer.induction, + `Ix.Theory.Model.IndexedContainer.child_wellFounded, + `Ix.Theory.Model.IndexedContainer.fold_node, + `Ix.Theory.Model.IndexedContainer.fold_mem, + `Ix.Theory.Model.IndexedContainer.small_elim, + `Ix.Theory.Model.Telescope.applyN_curry, + `Ix.Theory.Model.Telescope.curry_applyN, + `Ix.Theory.Certified.verifyTelescope_sound, + `Ix.Theory.Certified.verifyArguments_sound, + `Ix.Theory.Certified.Ordinary.checkShape_sound, + `Ix.Theory.Certified.Ordinary.checkLarge_sound, + `Ix.Theory.Certified.Ordinary.Shape.container_wf, + `Ix.Theory.Certified.Ordinary.Shape.constructorValue_mem, + `Ix.Theory.Certified.Ordinary.Shape.decodeField_branches, + `Ix.Theory.Certified.Ordinary.Shape.branches_decodeFields, + `Ix.Theory.Certified.Ordinary.Shape.container_large, + `Ix.Theory.Certified.Ordinary.Shape.largeValue_iota, + `Ix.Theory.Certified.Ordinary.Shape.constructorType_interp, + `Ix.Theory.Certified.Ordinary.Shape.recursorType_interp, + `Ix.Theory.Certified.Ordinary.Shape.closedRecursorValue_mem_source, + `Ix.Theory.Certified.Ordinary.Shape.produced_rule_eq, + `Ix.Theory.Certified.Ordinary.checkBlock_sound, + `Ix.Theory.Certified.Ordinary.Shape.publishedAssignment_realizes, + `Ix.Theory.Certified.admitOrdinary?_extends, + `Ix.Theory.Model.ConversionClaim.proofIrrel, + `Ix.Theory.Certified.Basis.Equality.Interface.of_checked, + `Ix.Theory.Certified.Basis.Equality.value_eq_eqv, + `Ix.Theory.Certified.Basis.Iff.Interface.of_checked, + `Ix.Theory.Certified.Basis.Iff.eq_of_mem, + `Ix.Theory.Certified.Basis.Nonempty.Interface.of_checked, + `Ix.Theory.Certified.Basis.Nonempty.value_eq_truthVal, + `Ix.Theory.Certified.Standard.Spec.value_mem, + `Ix.Theory.Certified.Standard.check_sound, + `Ix.Theory.Certified.Standard.assignment_realizes, + `Ix.Theory.Certified.admitStandard?_extends, + `Ix.Theory.Model.SetTheory.quotSet_mem_univ, + `Ix.Theory.Model.SetTheory.quotSound, + `Ix.Theory.Certified.Signature.checkTypes_sound, + `Ix.Theory.Certified.Signature.Formed.realizes, + `Ix.Theory.Certified.Quotient.value_mem, + `Ix.Theory.Certified.Quotient.liftValue_apply, + `Ix.Theory.Certified.Quotient.liftRule_eq, + `Ix.Theory.Certified.Quotient.check_sound, + `Ix.Theory.Certified.Quotient.assignment_realizes, + `Ix.Theory.Certified.admitQuotient?_extends, + `Ix.Theory.Model.TypingClaim.fact, + `Ix.Theory.Model.TypingClaim.natLit, + `Ix.Theory.Model.TypingClaim.betaResult, + `Ix.Theory.Model.ConversionClaim.natZero, + `Ix.Theory.Model.ConversionClaim.natSucc, + `Ix.Theory.Model.ConversionClaim.proj, + `Ix.Theory.Certified.Structure.Description.projections_fit, + `Ix.Theory.Certified.Structure.Description.constructor_eta, + `Ix.Theory.Certified.Structure.Description.constructor_iota, + `Ix.Theory.Certified.Structure.Description.projection_meaning, + `Ix.Theory.Certified.Structure.Description.eta_eq, + `Ix.Theory.Certified.Structure.Description.iota_eq, + `Ix.Theory.Certified.Structure.check_sound, + `Ix.Theory.Certified.Structure.Description.factAssignment_realizes, + `Ix.Theory.Certified.Structure.Description.publishedAssignment_realizes, + `Ix.Theory.Certified.admitStructure?_extends, + `Ix.Theory.Certified.Natural.value_mem, + `Ix.Theory.Certified.Natural.meaning, + `Ix.Theory.Certified.Natural.check_sound, + `Ix.Theory.Certified.Natural.assignment_realizes, + `Ix.Theory.Certified.admitNatural?_extends, + `Ix.Theory.Model.interp_mapRefs, + `Ix.Theory.Model.wellDenoted_mapRefs, + `Ix.Theory.Model.AExpr.mapRefs_restore, + `Ix.Theory.Model.interp_mapRefs_restore, + `Ix.Theory.Model.interp_mapRefs_permutation, + `Ix.Theory.Model.wellDenoted_mapRefs_permutation, + `Ix.Theory.Model.interp_mapRefs_merge, + `Ix.Theory.Model.wellDenoted_mapRefs_merge, + `Ix.Theory.Certified.Modeled.propositional_equation, + `Ix.Theory.Certified.Modeled.checkEquation?_sound, + `Ix.Theory.Certified.Modeled.CheckedCompanions.fixed_old, + `Ix.Theory.Certified.Modeled.environment_wf, + `Ix.Theory.Certified.Modeled.assignment_agrees, + `Ix.Theory.Certified.Modeled.assignment_realizes, + `Ix.Theory.Certified.Modeled.check?_sound, + `Ix.Theory.Certified.Modeled.EntrySource.not_axiom, + `Ix.Theory.Certified.Modeled.EntrySource.header, + `Ix.Theory.Certified.checkModeledExtension?, + `Ix.Theory.Certified.admitModeled?_extends, + `Ix.Theory.Certified.PrimitiveSignature.compatible_assignment, + `Ix.Theory.Certified.checkTypeCertified_sound, + `Ix.Theory.Certified.defeqCertified_sound, + `Ix.Theory.Certified.inferCertified_sound, + `Ix.Theory.Certified.whnfCertified_sound, + `Ix.Theory.Certified.admitDefinition?_extends, + `Ix.Theory.Certified.checkStoreCertified, + `Ix.Theory.Certified.accepted_store_has_model, + `Ix.Theory.Certified.EntrySource.header, + `Ix.Theory.Certified.CheckedStore.subject_sound, + `Ix.Theory.Certified.accepted_store_source_sound, + `Ix.Theory.Certified.checkDeclarationExtensions?, + `Ix.Theory.Certified.CheckedExtension.admit, + `Ix.Theory.Certified.checkFrontier?_refs, + `Ix.Theory.Certified.CheckedFrontier.compatible, + `Ix.Theory.Certified.ConditionalStore.subject_sound, + `Ix.Theory.Certified.EntrySource.axiom_policy, + `Ix.Theory.Certified.ConditionalStore.logical_axioms_authorized, + `Ix.Theory.Certified.acceptsBatch, + `Ix.Theory.Certified.CheckedBatch.closed_has_model, + `Ix.Theory.Certified.CheckedBatch.compatible_leaves, + `Ix.Theory.Certified.CheckedBatch.frontier_coverage, + `Ix.Theory.Certified.DependencyOrder.before, + `Ix.Theory.Certified.DependencyOrder.edge_for, + `Ix.Theory.Certified.CheckedBatch.acyclic, + `Ix.Theory.Certified.CheckedBatch.no_circular_discharge, + `Ix.Theory.Certified.CheckedBatch.leaf_axiom_retained, + `Ix.Theory.Certified.CheckedBatch.logicalUses_authorized, + `Ix.Theory.Certified.checkBatch?_assoc, + `Ix.Theory.Certified.CheckedBatch.no_False, + `Ix.Theory.Certified.acceptsCertified, + `Ix.Theory.Certified.accepted_has_model, + `Ix.Theory.Certified.accepted_proof_sound, + `Ix.Theory.Certified.no_proof_of_False, + `Ix.Theory.Certified.no_proof_of_empty] + +private def baseline : Array Name := #[`propext, `Classical.choice, `Quot.sound] + +/-- Definitions whose logical content is hidden behind a name in a root's +pretty-printed type. Changes to their bodies must change the frozen report. -/ +private def premiseDefinitions : Array Name := #[ + `Ix.Theory.Model.AExpr.mapRefs, + `Ix.Theory.Certified.Modeled.EquationWitness.mk, + `Ix.Theory.Certified.Modeled.EquationProof.propositional, + `Ix.Theory.Certified.Modeled.CheckedEquation.mk, + `Ix.Theory.Certified.Modeled.checkEquation?, + `Ix.Theory.Certified.Modeled.Companion.mk, + `Ix.Theory.Certified.Modeled.Companion.entry, + `Ix.Theory.Certified.Modeled.mapping, + `Ix.Theory.Certified.Modeled.CompanionChecked.mk, + `Ix.Theory.Certified.Modeled.CheckedCompanions.mk, + `Ix.Theory.Certified.Modeled.checkCompanions?, + `Ix.Theory.Certified.Modeled.assignment, + `Ix.Theory.Certified.Modeled.SourceMatches, + `Ix.Theory.Certified.Modeled.sourceRefs?, + `Ix.Theory.Certified.Modeled.sourceRules?, + `Ix.Theory.Certified.Modeled.betaHead?, + `Ix.Theory.Certified.Modeled.majorSource?, + `Ix.Theory.Certified.Modeled.ruleSource?, + `Ix.Theory.Certified.Modeled.Witness.mk, + `Ix.Theory.Certified.Modeled.Checked.mk, + `Ix.Theory.Certified.Modeled.EntrySource, + `Ix.Theory.Model.Equinumerous, `Ix.Theory.Model.IsTGUniverse, + `Ix.Theory.Model.SetTheory.mk, + `Ix.Theory.Model.AExpr.Scope, `Ix.Theory.Model.Reading, + `Ix.Theory.Certified.DefinitionReading.mk, + `Ix.Theory.Certified.PrimitiveSignature.mk, + `Ix.Theory.Certified.CheckedStore.mk, + `Ix.Theory.Certified.SourceHeader.mk, + `Ix.Theory.Certified.CheckedInterface.mk, + `Ix.Theory.Certified.CheckedExtension.mk, + `Ix.Theory.Certified.FrontierWitness.mk, + `Ix.Theory.Certified.FrontierHeader.mk, + `Ix.Theory.Certified.deferredSource, + `Ix.Theory.Certified.CheckedFrontier.mk, + `Ix.Theory.Certified.CheckedFrontier.interface, + `Ix.Theory.Certified.HeaderPresent, + `Ix.Theory.Certified.ConditionalStore.mk, + `Ix.Theory.Certified.ClaimNode.mk, + `Ix.Theory.Certified.CheckedNode.mk, + `Ix.Theory.Certified.ReplayResult.mk, + `Ix.Theory.Certified.CheckedBatch.mk, + `Ix.Theory.Certified.DependencyOrder.nil, + `Ix.Theory.Certified.DependencyOrder.cons, + `Ix.Theory.Certified.DependencyEdge, + `Ix.Theory.Certified.outstanding, + `Ix.Theory.Certified.logicalAxioms, + `Ix.Theory.Certified.CheckedBatch.logicalUses, + `Ix.Theory.Model.WellDenoted, `Ix.Theory.Model.Context.Valid, + `Ix.Theory.Model.TypingClaim, `Ix.Theory.Model.ConversionClaim, + `Ix.Theory.Model.ConstantEntry.mk, `Ix.Theory.Model.Realizes.mk, + `Ix.Theory.Model.ConstantEquation.mk, + `Ix.Theory.Model.ConstantFact.typed, + `Ix.Theory.Model.ConstantFact.natural, + `Ix.Theory.Model.ConstantFact.Meaning, + `Ix.Theory.Model.ConstantFact.Scope, + `Ix.Theory.Model.ConstantFact.ReferencesIn, + `Ix.Theory.Model.NaturalMeaning.mk, + `Ix.Theory.Model.projectValue, + `Ix.Theory.Model.Numeral.zero, + `Ix.Theory.Model.Numeral.succ, + `Ix.Theory.Model.Numeral.value, + `Ix.Theory.Model.Environment.WF.mk, `Ix.Theory.Model.Assignment.AgreesOn, + `Ix.Theory.Model.IndexedContainer.mk, `Ix.Theory.Model.IndexedContainer.WF.mk, + `Ix.Theory.Model.IndexedContainer.LargeElim, `Ix.Theory.Model.IndexedContainer.AlgebraTyping, + `Ix.Theory.Certified.TelescopeBound, `Ix.Theory.Certified.TelescopeProp, + `Ix.Theory.Certified.ArgumentsFit, + `Ix.Theory.Certified.Ordinary.Shape.mk, + `Ix.Theory.Certified.Ordinary.Constructor.mk, + `Ix.Theory.Certified.Ordinary.RecursiveField.mk, + `Ix.Theory.Certified.Ordinary.CheckedShape.mk, + `Ix.Theory.Certified.Ordinary.ConstructorEvidence, + `Ix.Theory.Certified.Ordinary.RecursiveEvidence, + `Ix.Theory.Certified.Ordinary.LargeEvidence, + `Ix.Theory.Certified.Ordinary.Shape.container, + `Ix.Theory.Certified.Ordinary.FamilyReading.mk, + `Ix.Theory.Certified.Ordinary.ConstructorReading.mk, + `Ix.Theory.Certified.Ordinary.RecursorReading.mk, + `Ix.Theory.Certified.Ordinary.ModeEvidence, + `Ix.Theory.Certified.Ordinary.Shape.ConstructorFormation, + `Ix.Theory.Certified.Ordinary.Shape.RecursorFormation.mk, + `Ix.Theory.Certified.Ordinary.Shape.SupportsK, + `Ix.Theory.Certified.Ordinary.Shape.RecursorSourceMatches, + `Ix.Theory.Certified.Ordinary.Shape.RuleFormation.mk, + `Ix.Theory.Certified.Ordinary.CheckedBlock.mk, + `Ix.Theory.Certified.Ordinary.EntrySource, + `Ix.Theory.Certified.Ordinary.Shape.recursorAt, + `Ix.Theory.Certified.Ordinary.BlockWitness.mk, + `Ix.Theory.Certified.DeclarationWitness.definition, + `Ix.Theory.Certified.DeclarationWitness.ordinary, + `Ix.Theory.Certified.DeclarationWitness.standard, + `Ix.Theory.Certified.DeclarationWitness.quotient, + `Ix.Theory.Certified.DeclarationWitness.structure, + `Ix.Theory.Certified.DeclarationWitness.natural, + `Ix.Theory.Certified.Natural.shape, + `Ix.Theory.Certified.Natural.fact, + `Ix.Theory.Certified.Natural.entry, + `Ix.Theory.Certified.Natural.environment, + `Ix.Theory.Certified.Natural.Checked.mk, + `Ix.Theory.Certified.Natural.EntrySource, + `Ix.Theory.Certified.Structure.Field.mk, + `Ix.Theory.Certified.Structure.Description.mk, + `Ix.Theory.Certified.Structure.Description.ordinary, + `Ix.Theory.Certified.Structure.FieldsFormed.nil, + `Ix.Theory.Certified.Structure.FieldsFormed.cons, + `Ix.Theory.Certified.Structure.fieldResult, + `Ix.Theory.Certified.Structure.Description.facts, + `Ix.Theory.Certified.Structure.Description.equations, + `Ix.Theory.Certified.Structure.Description.iotaEnvironment, + `Ix.Theory.Certified.Structure.Description.etaRule, + `Ix.Theory.Certified.Structure.Description.iotaRule, + `Ix.Theory.Certified.Structure.FactsWitness.mk, + `Ix.Theory.Certified.Structure.Witness.mk, + `Ix.Theory.Certified.Structure.FactsChecked.mk, + `Ix.Theory.Certified.Structure.Checked.mk, + `Ix.Theory.Certified.Structure.EntrySource, + `Ix.Theory.Model.Environment.HasType, + `Ix.Theory.Certified.Basis.Equality.Interface.mk, + `Ix.Theory.Certified.Basis.Equality.shape, + `Ix.Theory.Certified.Basis.Equality.type, + `Ix.Theory.Certified.Basis.Equality.reflType, + `Ix.Theory.Certified.Basis.Equality.recType, + `Ix.Theory.Certified.Basis.Iff.Interface.mk, + `Ix.Theory.Certified.Basis.Iff.shape, + `Ix.Theory.Certified.Basis.Iff.type, + `Ix.Theory.Certified.Basis.Iff.introType, + `Ix.Theory.Certified.Basis.Iff.recType, + `Ix.Theory.Certified.Basis.Nonempty.Interface.mk, + `Ix.Theory.Certified.Basis.Nonempty.shape, + `Ix.Theory.Certified.Basis.Nonempty.type, + `Ix.Theory.Certified.Basis.Nonempty.introType, + `Ix.Theory.Certified.Basis.Nonempty.recType, + `Ix.Theory.Certified.Standard.Spec.Prerequisites, + `Ix.Theory.Certified.Standard.Spec.type, + `Ix.Theory.Certified.Standard.Spec.value, + `Ix.Theory.Certified.Standard.chooseSet, + `Ix.Theory.Certified.Standard.Witness.mk, + `Ix.Theory.Certified.Standard.Checked.mk, + `Ix.Theory.Certified.Standard.EntrySource, + `Ix.Theory.Certified.Signature.Header.mk, + `Ix.Theory.Certified.Signature.Formed.nil, + `Ix.Theory.Certified.Signature.Formed.cons, + `Ix.Theory.Certified.Signature.TypeWitness.mk, + `Ix.Theory.Certified.Signature.Rule.mk, + `Ix.Theory.Certified.Signature.RuleFormed.mk, + `Ix.Theory.Certified.Signature.RuleWitness.mk, + `Ix.Theory.Model.SetTheory.quotSet, + `Ix.Theory.Model.SetTheory.quotClass, + `Ix.Theory.Model.SetTheory.qrep, + `Ix.Theory.Model.SetTheory.QuotRel.base, + `Ix.Theory.Model.SetTheory.QuotRel.refl, + `Ix.Theory.Model.SetTheory.QuotRel.symm, + `Ix.Theory.Model.SetTheory.QuotRel.trans, + `Ix.Theory.Certified.Quotient.Refs.mk, + `Ix.Theory.Certified.Quotient.Refs.ExactSource, + `Ix.Theory.Certified.Quotient.Refs.environment, + `Ix.Theory.Certified.Quotient.Refs.assignment, + `Ix.Theory.Certified.Quotient.Refs.entryType, + `Ix.Theory.Certified.Quotient.Refs.source, + `Ix.Theory.Certified.Quotient.Refs.equations, + `Ix.Theory.Certified.Quotient.Reading.mk, + `Ix.Theory.Certified.Quotient.Witness.mk, + `Ix.Theory.Certified.Quotient.Checked.mk, + `Ix.Theory.Certified.Quotient.EntrySource, + `Ix.Theory.Certified.Quotient.formerValue, + `Ix.Theory.Certified.Quotient.constructorValue, + `Ix.Theory.Certified.Quotient.liftValue, + `Ix.Theory.Certified.PrimitiveSignature.Compatible.mk, + `Ix.Theory.Certified.EntrySource, `Ix.Theory.Certified.Extends.mk, + `Ix.Theory.Certified.AdmittedEnvironment.mk, + `Ix.Theory.Certified.CheckedClaim.mk, + `Ix.Theory.Certified.InferenceResult.mk, `Ix.Theory.Certified.WhnfResult.mk, + `Ix.Theory.Store.mk, `Ix.Theory.Certified.ProofInput.mk, + `Ix.Theory.Certified.ProofWitness.mk, `Ix.Theory.Certified.CheckedProof.mk] + +private def directConstants (info : ConstantInfo) : Array Name := + info.type.getUsedConstants ++ match info with + | .thmInfo value => value.value.getUsedConstants + | .defnInfo value => value.value.getUsedConstants + | .opaqueInfo value => value.value.getUsedConstants + | .inductInfo value => value.ctors.toArray + | _ => #[] + +private partial def closure (env : Environment) (pending : List Name) + (seen : NameSet := {}) : NameSet := + match pending with + | [] => seen + | name :: rest => + if seen.contains name then closure env rest seen + else match env.checked.get.find? name with + | some info => closure env ((directConstants info).toList ++ rest) (seen.insert name) + | none => closure env rest (seen.insert name) + +private def axiomsIn (env : Environment) (dependencies : NameSet) : Array Name := + dependencies.toList.toArray.filter fun name => + match env.checked.get.find? name with + | some (.axiomInfo _) => true + | _ => false + +private def forbiddenModule (name : Name) : Bool := + ((`Ix).isPrefixOf name && !(`Ix.Theory).isPrefixOf name) || + #[`Ix.Theory.Named, `Tests, `Benchmarks, `Ix.Theory.Fixtures, `Ix.Theory.Tests, `Ix.Theory.Harness, + `Ix.Theory.Audit, `Ix.Theory.Typing.Conjectures, + `Ix.Theory.Typing.UniqueTyping].any (·.isPrefixOf name) + +run_cmd do + let env ← getEnv + let modules := env.allImportedModuleNames + let some boundary := modules.toList.idxOf? `Ix.Theory.Certified | + throwError "certified audit: public target is missing" + -- The audit's own Lean import is outside the certified boundary. Walk the + -- mathematical import graph from that boundary. Meta-only tactic imports + -- are tooling, and are outside this graph; proof constants remain audited. + let mut pending := [boundary] + let mut visited : List Nat := [] + while let index :: rest := pending do + pending := rest + unless visited.contains index do + visited := index :: visited + let name := modules[index]! + if forbiddenModule name || (`Lean).isPrefixOf name then + throwError m!"certified target imports forbidden module {name}" + for dependency in env.header.moduleData[index]!.imports do + unless dependency.isMeta do + if let some depIndex := modules.toList.idxOf? dependency.module then + pending := depIndex :: pending + + -- Freeze the foundational premise's complete fields. It must never acquire + -- a field that postulates kernel acceptance or semantic soundness. + let some foundation := getStructureInfo? env `Ix.Theory.Model.SetTheory | + throwError "certified audit: SetTheory is missing" + let expectedFields : Array Name := #[ + `Mem, `ext, `upair, `mem_upair, `sUnion, `mem_sUnion, `power, `mem_power, + `regularity, `image, `mem_image, `univChain, `univChain_mem, `univChain_tg] + unless foundation.fieldNames == expectedFields do + throwError m!"certified audit: foundational fields changed: {foundation.fieldNames}" + + let mut declarations : Nat := 0 + let mut recursionWorkers : Array Name := #[] + let mut projectDeclarations : List Name := [] + for (name, _) in env.constants.toList do + if let some index := env.getModuleIdxFor? name then + if visited.contains index.toNat && (`Ix.Theory).isPrefixOf modules[index.toNat]! then + declarations := declarations + 1 + projectDeclarations := name :: projectDeclarations + -- Compiled replacements and unsafe implementations are a separate + -- boundary from proof axioms. User-authored escapes are forbidden; + -- the compiler's recursion workers are inventoried in full below. + if (Lean.Compiler.getImplementedBy? env name).isSome || Lean.isExtern env name || + Lean.Elab.ComputedFields.computedFieldAttr.hasTag env name then + throwError m!"certified declaration has an unreviewed runtime replacement: {name}" + if let some replacement := (Lean.Compiler.CSimp.ext.getState env).map.find? name then + throwError m!"certified declaration has an unreviewed csimp replacement: {name} -> {replacement.toDeclName}" + if let some info := env.find? name then + match info with + | .defnInfo value => + unless value.safety == .safe do + let some parent := Lean.Compiler.isUnsafeRecName? name | + throwError m!"certified declaration is unsafe or partial: {name}" + let some (.defnInfo original) := env.find? parent | + throwError m!"recursion worker has no source definition: {name}" + unless original.safety == .safe do + throwError m!"recursion worker's source is not safe: {name}" + recursionWorkers := recursionWorkers.push name + | .opaqueInfo value => + if value.isUnsafe then throwError m!"certified opaque declaration is unsafe: {name}" + | _ => pure () + unless declarations > 0 do throwError "certified audit measured no project declarations" + + -- Traverse the checked declarations, including every constructor field. + -- Imported axiom summaries can omit dependencies of recursive groups. + for axiomName in axiomsIn env (closure env projectDeclarations) do + unless baseline.contains axiomName do + throwError m!"certified foundation reaches unapproved axiom {axiomName}" + + for root in roots do + let some info := env.find? root | + throwError m!"certified audit: missing root {root}" + let dependencies := closure env [root] + unless dependencies.size > 1 do + throwError m!"certified audit: empty dependency measurement for {root}" + let type ← liftTermElabM <| Meta.ppExpr info.type + let axioms := (axiomsIn env dependencies).qsort Name.lt + let mut projectModules : NameSet := {} + for dependency in dependencies.toList do + if let some index := env.getModuleIdxFor? dependency then + let moduleName := modules[index.toNat]! + if (`Ix.Theory).isPrefixOf moduleName then + projectModules := projectModules.insert moduleName + let moduleRows := projectModules.toList.toArray.qsort Name.lt + logInfo m!"certified root {root}\n type: {type}\n axioms: {axioms}\n constants: {dependencies.size}\n project modules: {moduleRows}" + for name in premiseDefinitions do + let some info := env.find? name | + throwError m!"certified audit: missing premise definition {name}" + let type ← liftTermElabM <| Meta.ppExpr info.type + match info with + | .defnInfo value => + let body ← liftTermElabM <| Meta.ppExpr value.value + logInfo m!"premise definition {name}\n type: {type}\n definition: {body}" + | .ctorInfo _ => logInfo m!"premise constructor {name}\n type: {type}" + | _ => throwError m!"certified audit: unclassified premise definition {name}" + for name in recursionWorkers.qsort Name.lt do + let some (.defnInfo value) := env.find? name | + throwError m!"certified audit: missing recursion worker {name}" + let type ← liftTermElabM <| Meta.ppExpr value.type + let body ← liftTermElabM <| Meta.ppExpr value.value + logInfo m!"compiler recursion worker {name}\n type: {type}\n implementation: {body}" + logInfo m!"Certified project runtime audit OK: {recursionWorkers.size} compiler recursion workers inventoried; no other unsafe/partial declarations, extern, implemented_by, computed_field, or csimp replacements in the mathematical import graph" + logInfo m!"Certified foundation audit OK: {declarations} declarations; {roots.size} named roots; explicit SetTheory hypothesis; baseline axioms only" + +end Tests.Theory.Audit.Certified diff --git a/Tests/Theory/Certified.lean b/Tests/Theory/Certified.lean new file mode 100644 index 000000000..75734ccc5 --- /dev/null +++ b/Tests/Theory/Certified.lean @@ -0,0 +1,76 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified + +open Ix.Theory + +/-! Structural and universe-validation regressions. These do not yet exercise +proof acceptance; that gate will add its own corpus when implemented. -/ + +namespace Tests.Theory.Certified + +open Ix.Theory.Certified Ix.Theory.Model + +#guard (PropWhen.ofList [2, 0, 2]).toRaw == some [0, 2] +#guard PropWhen.fromRaw? 2 (some [0, 1]) |>.isSome +#guard PropWhen.fromRaw? 2 (some [1, 0]) |>.isNone +#guard PropWhen.fromRaw? 1 (some [0, 0]) |>.isNone +#guard PropWhen.fromRaw? 1 (some [1]) |>.isNone +#guard validateZero? 1 (.param 0) (some [0]) |>.isSome +#guard validateZero? 1 (.param 0) none |>.isNone +#guard validateZero? 1 (.param 0) (some []) |>.isNone +#guard validateZero? 0 (.succ (.param 0)) none |>.isNone +#guard validateZero? 0 (.imax (.param 0) .zero) (some []) |>.isNone +#guard validateZero? 2 (.imax (.param 0) (.param 1)) (some [1]) |>.isSome +#guard (zeroCondition (.imax (.param 0) (.param 1))).holds ([5, 0].getD · 0) +#guard !(zeroCondition (.imax (.param 0) (.param 1))).holds ([0, 5].getD · 0) +#guard (instCondition [.succ .zero] (.param 0)).toRaw == none +#guard (instCondition [.zero] (.param 0)).toRaw == some [] + +-- Equal zero conditions do not imply equal universes. +example : zeroCondition (.succ .zero) = zeroCondition (.succ (.succ .zero)) := rfl +example : VLevel.eval [] (.succ .zero) ≠ VLevel.eval [] (.succ (.succ .zero)) := by decide + +def identity : VExpr Nat := .lam (.sort (.param 0)) (.lam (.bvar 0) (.bvar 0)) +def identityAnnotations : AnnotationTree := + .lam (some [0]) .leaf (.lam (some [0]) .leaf .leaf) + +#guard readAnnotations? 1 0 identity identityAnnotations |>.isSome +#guard ((readAnnotations? 1 0 identity identityAnnotations).map (·.val.erase)) == some identity +#guard readAnnotations? 0 0 identity identityAnnotations |>.isNone +#guard readAnnotations? 1 0 identity .leaf |>.isNone +-- The same source node at two context depths is checked separately. +#guard readAnnotations? 0 1 (VExpr.bvar (β := Nat) 0) .leaf |>.isSome +#guard readAnnotations? 0 0 (VExpr.bvar (β := Nat) 0) .leaf |>.isNone + +def primitives : PrimitiveSignature Nat := ⟨.member 10 0, .member 11 0, by decide, none⟩ + +def primitiveStore (falseDecl recDecl : Const Nat) : Store Nat where + dom := [10, 11] + nodup := by decide + blocks b := if b = 10 then some ⟨[falseDecl]⟩ else if b = 11 then some ⟨[recDecl]⟩ else none + mem_dom b := by + by_cases h : b = 10 <;> by_cases h' : b = 11 <;> simp_all + +#guard primitives.validate (primitiveStore PrimitiveSignature.falseDeclaration + primitives.falseElimDeclaration) +#guard !primitives.validate (primitiveStore (.axiom 0 (.sort .zero) .safe) + primitives.falseElimDeclaration) +#guard !primitives.validate (primitiveStore PrimitiveSignature.falseDeclaration + (.recursor 0 0 0 1 0 primitives.falseElimType [] false .safe)) +#guard !primitives.validate (primitiveStore PrimitiveSignature.falseDeclaration + (.recursor 1 0 0 1 0 primitives.falseElimType [] true .safe)) +#guard !primitives.validate (primitiveStore + (.induct 0 0 0 (.sort .zero) [⟨0, 0, 0, primitives.falseExpr, .safe⟩] .safe) + primitives.falseElimDeclaration) + +#guard readDefinition? (Const.axiom 0 primitives.falseExpr .safe) .leaf .leaf |>.isNone +#guard readDefinition? (Const.axiom (β := Nat) 0 (.forallE (.sort .zero) (.bvar 0)) .safe) + .leaf .leaf |>.isNone +#guard readDefinition? (Const.defn 0 .opaque primitives.falseExpr (.sort .zero) .unsafe) + .leaf .leaf |>.isNone + +end Tests.Theory.Certified diff --git a/Tests/Theory/Checker.lean b/Tests/Theory/Checker.lean new file mode 100644 index 000000000..0d0a8e61f --- /dev/null +++ b/Tests/Theory/Checker.lean @@ -0,0 +1,86 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Checker + +open Ix.Theory + +namespace Tests.Theory.Checker + +open Ix.Theory.Certified Ix.Theory.Model + +def emptyEnvironment : Environment Nat := fun _ => none + +def identity (l : VLevel) : AExpr Nat := + .lam (zeroCondition l) (.sort l) (.lam (zeroCondition l) (.bvar 0) (.bvar 0)) + +def identityType (l : VLevel) : AExpr Nat := + .forallE (zeroCondition l) (.sort l) (.forallE (zeroCondition l) (.bvar 0) (.bvar 1)) + +def identityWitness (l : VLevel) : TypingWitness Nat := + .lam (.succ l) (.imax l l) (.forallE (zeroCondition l) (.bvar 0) (.bvar 1)) + .sort (.forallE l l .bvar .bvar) + (.lam l l (.bvar 1) .bvar .bvar .bvar) + +#guard checkTypeCertified.{0,0} 30 1 emptyEnvironment [] (identity (.param 0)) + (identityType (.param 0)) (identityWitness (.param 0)) +#guard checkTypeCertified.{0,0} 30 0 emptyEnvironment [] (identity .zero) + (identityType .zero) (identityWitness .zero) +#guard checkTypeCertified.{0,0} 30 0 emptyEnvironment [] (identity (.succ .zero)) + (identityType (.succ .zero)) (identityWitness (.succ .zero)) + +-- The same parameterized expression is rejected with a missing universe slot. +#guard !checkTypeCertified.{0,0} 30 0 emptyEnvironment [] (identity (.param 0)) + (identityType (.param 0)) (identityWitness (.param 0)) + +-- Structural erasure alone would permit this false annotation. +def wrongIdentity : AExpr Nat := .lam .never (.sort .zero) (.lam .never (.bvar 0) (.bvar 0)) +def wrongIdentityType : AExpr Nat := + .forallE .never (.sort .zero) (.forallE .never (.bvar 0) (.bvar 1)) +#guard !checkTypeCertified.{0,0} 30 0 emptyEnvironment [] wrongIdentity wrongIdentityType + (identityWitness .zero) + +def betaSource : AExpr Nat := .app (identity (.succ .zero)) (.sort .zero) +def betaResult : AExpr Nat := .lam .never (.sort .zero) (.bvar 0) +def betaWitness : ConversionWitness Nat := + .beta (identityType (.succ .zero)) (identityWitness (.succ .zero)) .sort +#guard defeqCertified.{0,0} 30 0 emptyEnvironment [] betaSource betaResult betaWitness + +-- A proposed beta step cannot substitute an argument outside the lambda domain. +#guard !defeqCertified.{0,0} 30 0 emptyEnvironment [] + (.app (identity (.succ .zero)) (.sort (.succ .zero))) + (.lam .never (.sort (.succ .zero)) (.bvar 0)) betaWitness +-- This check is required in the proof-point regime too. +#guard !defeqCertified.{0,0} 30 0 emptyEnvironment [] + (.app (identity .zero) (.sort .zero)) + (.lam .always (.sort .zero) (.bvar 0)) + (.beta (identityType .zero) (identityWitness .zero) .sort) + +def etaSource : AExpr Nat := + .lam .never (.sort (.succ .zero)) + (.app ((identity (.succ .zero)).liftN 1) (.bvar 0)) +#guard defeqCertified.{0,0} 30 0 emptyEnvironment [] etaSource (identity (.succ .zero)) + (.eta (.forallE .never (.bvar 0) (.bvar 1)) (identityWitness (.succ .zero))) + +def proofContext : Context Nat := + Context.push (.bvar 1) (Context.push (.bvar 0) (Context.push (.sort .zero) [])) +#guard defeqCertified.{0,0} 30 0 emptyEnvironment proofContext (.bvar 0) (.bvar 1) + (.proofIrrel (.bvar 2) .bvar .bvar .bvar) + +#guard LevelEq.check 1 (.imax (.param 0) (.param 0)) (.param 0) +#guard LevelEq.check 0 (.imax (.succ .zero) .zero) .zero +#guard !LevelEq.check 0 (.succ .zero) (.succ (.succ .zero)) +#guard !LevelEq.check 0 (.imax (.param 0) .zero) .zero +#guard defeqCertified.{0,0} 10 1 emptyEnvironment [] + (.sort (.imax (.param 0) (.param 0))) (.sort (.param 0)) .sort +#guard !defeqCertified.{0,0} 10 0 emptyEnvironment [] + (.sort (.succ .zero)) (.sort (.succ (.succ .zero))) .sort + +#guard !checkTypeCertified.{0,0} 0 1 emptyEnvironment [] (identity (.param 0)) + (identityType (.param 0)) (identityWitness (.param 0)) +#guard !checkTypeCertified.{0,0} 30 0 emptyEnvironment [] (.natLit 0) (.sort .zero) .const +#guard !checkTypeCertified.{0,0} 30 0 emptyEnvironment [] (.const (.member 42 0) []) (.sort .zero) .const + +end Tests.Theory.Checker diff --git a/Tests/Theory/Claims.lean b/Tests/Theory/Claims.lean new file mode 100644 index 000000000..f8376371c --- /dev/null +++ b/Tests/Theory/Claims.lean @@ -0,0 +1,130 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certificate.Claims +import Ix.Theory.Certified.ClaimComposition +import Tests.Theory.Acceptance +import Tests.Theory.Standard + +open Ix.Theory + +namespace Tests.Theory.Claims + +open Ix.Theory.Model Ix.Theory.Certified Ix.Theory.Certificate +open Tests.Theory.Certified (primitives) +open Tests.Theory.Checker (identity identityType) + +set_option maxRecDepth 8192 +set_option maxHeartbeats 8000000 + +def ref (n : Nat) : ConstRef Nat := .member n 0 +def aliasDeclaration (n : Nat) : Const Nat := + .defn 1 .theorem (identityType (.param 0)).erase (.const (ref n) [.param 0]) .safe + +def declaration : Nat → Const Nat + | 10 => PrimitiveSignature.falseDeclaration + | 11 => primitives.falseElimDeclaration + | 12 => Acceptance.idDeclaration + | 13 | 14 => aliasDeclaration 12 + | 15 => .defn 1 .theorem (identityType (.param 0)).erase + (.app (.lam (identityType (.param 0)).erase (.const (ref 13) [.param 0])) + (.const (ref 14) [.param 0])) .safe + | _ => .axiom 0 primitives.falseExpr .safe + +def store (alter : Nat → Const Nat → Const Nat := fun _ c => c) : Store Nat where + dom := [10, 11, 12, 13, 14, 15] + nodup := by decide + blocks b := if b ∈ [10, 11, 12, 13, 14, 15] then some ⟨[alter b (declaration b)]⟩ else none + mem_dom b := by split <;> simp_all + +def specs : List (List (ConstRef Nat) × List (ConstRef Nat)) := + [([], [ref 12]), ([ref 12], [ref 13]), ([ref 12], [ref 14]), ([ref 13, ref 14], [ref 15])] + +def nodes? (source : Store Nat := store) : Option (List (ClaimNode Nat)) := + specs.mapM fun (frontier, subjects) => claimNode? 1000 primitives source frontier subjects + +def accepted (source : Store Nat) (nodes : List (ClaimNode Nat)) : Bool := + (batchFrontier? 1000 primitives source nodes).any fun frontier => + acceptsBatch.{0,0} 1000 primitives source frontier nodes + +def positive : Bool := (nodes?).any (accepted store) +#guard positive + +-- Each leaf is independently a valid conditional check. +#guard (nodes?).any fun nodes => nodes.all fun node => + (checkNode?.{0,0} 1000 primitives store node).isSome + +-- The same diamond can retain A as a genuine external obligation. +#guard (nodes?).any fun nodes => accepted store (nodes.drop 1) +#guard (nodes?).any fun nodes => + (batchFrontier? 1000 primitives store (nodes.drop 1)).any fun frontier => + (checkBatch?.{0,0} 1000 primitives store frontier (nodes.drop 1)).any fun result => + result.receipt.frontier.refs == [ref 12] + +-- Structural discharge closes all four subjects and produces their model. +#guard (nodes?).any fun nodes => + (checkBatch?.{0,0} 1000 primitives store [] nodes).any fun result => + result.receipt.frontier.refs.isEmpty && nodeSubjects result.nodes == [ref 12, ref 13, ref 14, ref 15] + +-- A reverse dependency cannot be supplied by a later node. +#guard (nodes?).any fun nodes => !accepted store nodes.reverse +#guard (nodes?).any fun nodes => !accepted store (nodes[0]! :: nodes[3]! :: [nodes[1]!, nodes[2]!]) +-- Repeated subjects share an existing checked interpretation. +#guard (nodes?).any fun nodes => accepted store (nodes ++ nodes) +#guard (nodes?).any fun nodes => + !(acceptsBatch.{0,0} 1000 primitives store [] (nodes.drop 1)) +#guard (nodes?).any fun nodes => + !(acceptsBatch.{0,0} 0 primitives store [] nodes) + +-- Source changes reject an original valid leaf certificate. +#guard (nodes?).any fun nodes => + !accepted (store fun n c => if n = 15 then + .defn 1 .theorem primitives.falseExpr (.const (ref 13) [.param 0]) .safe else c) nodes +#guard (nodes?).any fun nodes => + !accepted (store fun n c => if n = 12 then .axiom 1 (identityType (.param 0)).erase .safe else c) nodes + +def cyclicStore : Store Nat := store fun n c => + if n = 12 then .defn 0 .theorem primitives.falseExpr (.const (ref 13) []) .safe + else if n = 13 then .defn 0 .theorem primitives.falseExpr (.const (ref 12) []) .safe else c + +def cyclicNodes? : Option (List (ClaimNode Nat)) := do + return [← claimNode? 1000 primitives cyclicStore [ref 13] [ref 12], + ← claimNode? 1000 primitives cyclicStore [ref 12] [ref 13]] + +-- Both conditional implications pass, but neither circular ordering closes. +#guard cyclicNodes?.any fun nodes => nodes.all fun node => + (checkNode?.{0,0} 1000 primitives cyclicStore node).isSome +#guard cyclicNodes?.any fun nodes => !accepted cyclicStore nodes +#guard cyclicNodes?.any fun nodes => !accepted cyclicStore nodes.reverse + +-- Claiming a deferred member as an owned subject is rejected. +#guard (claimNode? 1000 primitives store [ref 12] [ref 12]).any fun node => + (checkNode?.{0,0} 1000 primitives store node).isNone + +-- A source axiom cannot enter as a deferred assumption, including supported +-- schemas: their semantic producer must run, and their use remains recorded. +#guard (frontierWitnesses? 1000 Standard.store primitives.environment [Standard.propextRef]).isNone + +def standardNodes? : Option (List (ClaimNode Nat)) := do + return [← claimNode? 2000 primitives Standard.store [] [Standard.propextRef], + ← claimNode? 2000 primitives Standard.store [] [Standard.choiceRef]] + +#guard standardNodes?.any fun nodes => + (checkBatch?.{0,0} 2000 primitives Standard.store [] nodes).any fun result => + result.receipt.frontier.refs.isEmpty && + result.logicalUses == [Standard.propextRef, Standard.choiceRef] + +-- The recorded axiom remains even when that same reference is a checked subject. +#guard standardNodes?.any fun nodes => + (checkBatch?.{0,0} 2000 primitives Standard.store [] (nodes.take 1)).any fun result => + Standard.propextRef ∈ nodeSubjects result.nodes && + Standard.propextRef ∈ result.logicalUses + +-- Regrouping composition has identical acceptance and complete subjects. +#guard nodes?.any fun nodes => + accepted store (composeClaims (composeClaims (nodes.take 1) ((nodes.drop 1).take 1)) (nodes.drop 2)) && + accepted store (composeClaims (nodes.take 1) (composeClaims ((nodes.drop 1).take 1) (nodes.drop 2))) + +end Tests.Theory.Claims diff --git a/Tests/Theory/ImportManifest.lean b/Tests/Theory/ImportManifest.lean new file mode 100644 index 000000000..43e9e7b9c --- /dev/null +++ b/Tests/Theory/ImportManifest.lean @@ -0,0 +1,176 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +/-! Import provenance, recorded from the Lean4Ix working tree on 2026-09-11. +The working tree contained the new, uncommitted consistency model; the base +revision alone does not identify these inputs. Source hashes identify each file. +The selected file inventory is enforced by `Tests/Theory/Provenance.lean`. +The generated `Ix/Theory.lean` umbrella records its sole upstream import. -/ + +namespace Tests.Theory.ImportManifest + +def lean4IxBaseRevision : String := "ab42e79e2a4e2615a3ca6ef983d510f374057a38" +def conLecheRevision : String := "86cd20a65660d757cedc81561a44579099b565d0" + +structure SourceFile where + source : String + target : String + sourceSha256 : String + deriving Repr + +def selected : Array SourceFile := #[ + ⟨"Lean4Ix/Certificate/Build.lean", "Ix/Theory/Certificate/Build.lean", "833e462fae9934f3d7809fc78b7c791d7ede3ab4398732079f26ea73f53abc4c"⟩, + ⟨"Lean4Ix/Certificate/Claims.lean", "Ix/Theory/Certificate/Claims.lean", "c3532bd7b49d0657f52913f4da285580aa2de467ecd27b41f5263a165ae34d0f"⟩, + ⟨"Lean4Ix/Certificate/Modeled.lean", "Ix/Theory/Certificate/Modeled.lean", "28d4f7cd3bdb5945d31b8f6f10c686d6c22889e96511c765b7a28c59b17ac47a"⟩, + ⟨"Lean4Ix/Certificate/Ordinary.lean", "Ix/Theory/Certificate/Ordinary.lean", "7c85145faf3bef4f934087fe9694171dd457c2e84190000f8c5c355af9d357c3"⟩, + ⟨"Lean4Ix/Certificate/OrdinarySource.lean", "Ix/Theory/Certificate/OrdinarySource.lean", "291704dd2ff2d53d70ac73876ff1505eb99c96e4a029f46e9428dc64eadd24f4"⟩, + ⟨"Lean4Ix/Certificate/Quotient.lean", "Ix/Theory/Certificate/Quotient.lean", "8c5a44003cc09313a600d3e53ebf0de1bfbee5d64c6c24fa6753fc87659aad83"⟩, + ⟨"Lean4Ix/Certificate/Standard.lean", "Ix/Theory/Certificate/Standard.lean", "54605d2a22c2671429fe65e9cd210677deee90dc38f629991f1f0f1dc157bea8"⟩, + ⟨"Lean4Ix/Certificate/Structure.lean", "Ix/Theory/Certificate/Structure.lean", "a0e5c274ed5c5ced8854841144086396d11c105b080bdb2c46b8ccf44d34a1d2"⟩, + ⟨"Lean4Ix/Certificate/Suggest.lean", "Ix/Theory/Certificate/Suggest.lean", "d7863536599fa0554d4ce34427b496b306301982cdef8a9bda2395380fd1ee2a"⟩, + ⟨"Lean4Ix/Certified.lean", "Ix/Theory/Certified.lean", "db42519eb53c5c24965b27879cb1ad81eca1010513fc67cf486f324c942fce98"⟩, + ⟨"Lean4Ix/Certified/Accept.lean", "Ix/Theory/Certified/Accept.lean", "ce3975aa6a82f020ad6c92a5644945d3bb68ea323f97ef7eace8ba9d5d5aa80e"⟩, + ⟨"Lean4Ix/Certified/Admission.lean", "Ix/Theory/Certified/Admission.lean", "ea6a1b8d3126190332557d424957551af7d780abe98aa0af5433a928d97ba991"⟩, + ⟨"Lean4Ix/Certified/Basis/Equality.lean", "Ix/Theory/Certified/Basis/Equality.lean", "fa4886a2d0d764ba85c0711a258d0d94888a30cf39db2842290d220b1d7213c0"⟩, + ⟨"Lean4Ix/Certified/Basis/Iff.lean", "Ix/Theory/Certified/Basis/Iff.lean", "368fa1c9c359a29a5877af993f16698025d84aed967ce4720122280532644fc8"⟩, + ⟨"Lean4Ix/Certified/Basis/Interface.lean", "Ix/Theory/Certified/Basis/Interface.lean", "6bcd43996a8101c48f3f11a17f1950b3db74ca1a7cb970de324cd0fe78f8d4ce"⟩, + ⟨"Lean4Ix/Certified/Basis/Nonempty.lean", "Ix/Theory/Certified/Basis/Nonempty.lean", "5f62092f92138ee8433f9f56977aec6c03827819342bb7417080cdc2c19b35d4"⟩, + ⟨"Lean4Ix/Certified/Checker.lean", "Ix/Theory/Certified/Checker.lean", "677f0f68b1422d9ccc467234acaa12737fd97f9ecdc43755b46e12f36be9858a"⟩, + ⟨"Lean4Ix/Certified/ClaimComposition.lean", "Ix/Theory/Certified/ClaimComposition.lean", "d99702d5199447cb696e365ccbb246b3bf9663b432ead0563b6572101f19f731"⟩, + ⟨"Lean4Ix/Certified/Claims.lean", "Ix/Theory/Certified/Claims.lean", "fe28187c7eeabd148c8b737532f0559f0424397b8ba70cf2b990edd7d9623738"⟩, + ⟨"Lean4Ix/Certified/Frontier.lean", "Ix/Theory/Certified/Frontier.lean", "a0dfec4872d5e7ef728494a0a71bef107cfb243a42107fe92185e3fb0d371fb0"⟩, + ⟨"Lean4Ix/Certified/Level.lean", "Ix/Theory/Certified/Level.lean", "37c6fa05fddfc0a01a70288cf49d870a77625119be6c73bd0061fe76c0d27420"⟩, + ⟨"Lean4Ix/Certified/LevelEq.lean", "Ix/Theory/Certified/LevelEq.lean", "a6402afba171af4b4df58ea4de64b3e1517fae90bd8ce145bb96775bb4862923"⟩, + ⟨"Lean4Ix/Certified/LogicalPolicy.lean", "Ix/Theory/Certified/LogicalPolicy.lean", "763a20bd20133c2cf235c27cec875f2c67bb0ec0c5a7c25c5df82562531a496a"⟩, + ⟨"Lean4Ix/Certified/Modeled/Admission.lean", "Ix/Theory/Certified/Modeled/Admission.lean", "68c0ef0b5e19984d08e8d34c296548eceeb4f5c4f4d86d5df3174be9ae0d939c"⟩, + ⟨"Lean4Ix/Certified/Modeled/Equation.lean", "Ix/Theory/Certified/Modeled/Equation.lean", "bb49b60d019b57819c030e0ea41f853727f4d0aecb0174e31c236639747dde4d"⟩, + ⟨"Lean4Ix/Certified/Modeled/Source.lean", "Ix/Theory/Certified/Modeled/Source.lean", "ac4a72a22fc77f5a512511a19c0b0c01925cf2eca5b1c1de4f00c111b048c88d"⟩, + ⟨"Lean4Ix/Certified/Modeled/Transport.lean", "Ix/Theory/Certified/Modeled/Transport.lean", "d0d020ca9fd123fb036cd35d662b237778f9e1c24119250185cb2946bd3d99ab"⟩, + ⟨"Lean4Ix/Certified/Natural/Admission.lean", "Ix/Theory/Certified/Natural/Admission.lean", "8d7ce4e7f0da80d5ffe45b45aeba72320ec7da35bbad3eb3fdc321512ff74a1a"⟩, + ⟨"Lean4Ix/Certified/Natural/Checked.lean", "Ix/Theory/Certified/Natural/Checked.lean", "2dd7ad9e96c65a5d74a84f8c6c6cec3ba2693ab6f77aefcd7a29089529d6172b"⟩, + ⟨"Lean4Ix/Certified/Natural/Publish.lean", "Ix/Theory/Certified/Natural/Publish.lean", "fac41bc4b4a409d0266d64b4375a638ed1ec281c28ec20c993d7166072d15ca1"⟩, + ⟨"Lean4Ix/Certified/Natural/Value.lean", "Ix/Theory/Certified/Natural/Value.lean", "db39def05ce46d1da7b78209acfba10596e67642a2942ad438b38a049ab5d51b"⟩, + ⟨"Lean4Ix/Certified/Operations.lean", "Ix/Theory/Certified/Operations.lean", "9c7bb4b947421dfbaa12b7dc7ddbac0021847a0f2b2c092decfb3dd2e582fc34"⟩, + ⟨"Lean4Ix/Certified/Ordinary/Admission.lean", "Ix/Theory/Certified/Ordinary/Admission.lean", "b19b0077e451aae6cc608d9007def64bd66523e89e2a5f558d0cf7abf77926ad"⟩, + ⟨"Lean4Ix/Certified/Ordinary/Checked.lean", "Ix/Theory/Certified/Ordinary/Checked.lean", "04ffd22d2d6b5c0070f8e8bb6b8504b408046ae39ffd07625f2708f7994bed8e"⟩, + ⟨"Lean4Ix/Certified/Ordinary/Computation.lean", "Ix/Theory/Certified/Ordinary/Computation.lean", "27dc5c8f9d2a7990e64c13c2fab256da3dd99d5fa5ba8eeea9134f3d39f44792"⟩, + ⟨"Lean4Ix/Certified/Ordinary/ConstructorStage.lean", "Ix/Theory/Certified/Ordinary/ConstructorStage.lean", "ebd00026aff885831dc52fa28f33be3a562d19980bbbb50ec0111b9ebbbb60b2"⟩, + ⟨"Lean4Ix/Certified/Ordinary/Constructors.lean", "Ix/Theory/Certified/Ordinary/Constructors.lean", "e57eac74ac4190013c6fc603b14fbf747985266a1369bf1b7d8cdf3785da718c"⟩, + ⟨"Lean4Ix/Certified/Ordinary/Container.lean", "Ix/Theory/Certified/Ordinary/Container.lean", "a5f560feda487c89356faa1e775a4133f2dfc9bfaa8002472a217a227ddac582"⟩, + ⟨"Lean4Ix/Certified/Ordinary/Eliminator.lean", "Ix/Theory/Certified/Ordinary/Eliminator.lean", "8e2162333ed48c3fbeeef2ce15ccc2406628dfa71f927550204eb1c295a2dde2"⟩, + ⟨"Lean4Ix/Certified/Ordinary/Family.lean", "Ix/Theory/Certified/Ordinary/Family.lean", "b668e27738cbe8336059f8cd0313feb8709ab112ee64126d53e28998aac77568"⟩, + ⟨"Lean4Ix/Certified/Ordinary/LargeElim.lean", "Ix/Theory/Certified/Ordinary/LargeElim.lean", "af7d28444877b6a7524aaaf277944c6fc9b75bdadfa4baf481bdc3edf4ee337f"⟩, + ⟨"Lean4Ix/Certified/Ordinary/Reading.lean", "Ix/Theory/Certified/Ordinary/Reading.lean", "8da97d7af13f89fc5a1983c45f1988bb5c8dea391481825b5040a102599b7cbb"⟩, + ⟨"Lean4Ix/Certified/Ordinary/RecursorReading.lean", "Ix/Theory/Certified/Ordinary/RecursorReading.lean", "0013a5e2233423cc0bfc5e6db6259a86d45649098912594cf5e9bb844d6f1043"⟩, + ⟨"Lean4Ix/Certified/Ordinary/RecursorStage.lean", "Ix/Theory/Certified/Ordinary/RecursorStage.lean", "9e6cbbfee2e9748be9042845d5d7f702b60aaa0516483892d16d974d8315ea26"⟩, + ⟨"Lean4Ix/Certified/Ordinary/RecursorSyntax.lean", "Ix/Theory/Certified/Ordinary/RecursorSyntax.lean", "df1bef5d63e0e84506bc7f3c9be345d080ace1e226b3573073b005a493b9c4fb"⟩, + ⟨"Lean4Ix/Certified/Ordinary/RecursorValue.lean", "Ix/Theory/Certified/Ordinary/RecursorValue.lean", "ab4328139fc3b76b25b71a42e01ddb089ae17e04d811dbc63aead88450370f30"⟩, + ⟨"Lean4Ix/Certified/Ordinary/RuleChecks.lean", "Ix/Theory/Certified/Ordinary/RuleChecks.lean", "ef1fe53fa44205b23ef384cb6b72e0129d3341e07834339184d694a8951225bb"⟩, + ⟨"Lean4Ix/Certified/Ordinary/RuleEquations.lean", "Ix/Theory/Certified/Ordinary/RuleEquations.lean", "40b04a47431f9c12aaf8450c408cddc849aa6da53c77b11c33612fbcfb9b7236"⟩, + ⟨"Lean4Ix/Certified/Ordinary/RuleReading.lean", "Ix/Theory/Certified/Ordinary/RuleReading.lean", "263f59728e844b9d905c7e28ba20eefc7f3c708b2237157814248490e40d19bd"⟩, + ⟨"Lean4Ix/Certified/Ordinary/Shape.lean", "Ix/Theory/Certified/Ordinary/Shape.lean", "e071e91be7609ed73c995623153a28d96cae1c1e057e3b0464373d85f9084c7e"⟩, + ⟨"Lean4Ix/Certified/Policy.lean", "Ix/Theory/Certified/Policy.lean", "67f69859dfed1dd21bdb6b253a578b1473d34fca6d408d13bbcd0176644373d9"⟩, + ⟨"Lean4Ix/Certified/Prelude.lean", "Ix/Theory/Certified/Prelude.lean", "1cdc62000e9c5532b989c905d7bf1b37c3c1578cd15c19a6cccfae7a1581892c"⟩, + ⟨"Lean4Ix/Certified/PropWhen.lean", "Ix/Theory/Certified/PropWhen.lean", "c4d80fc1af048f6bf97841d4b63f8528cf7891a71f6e05744fd33edabc757343"⟩, + ⟨"Lean4Ix/Certified/Quotient/Admission.lean", "Ix/Theory/Certified/Quotient/Admission.lean", "4efb5df9a2703a1aaed2f286e47fc0445ea05c0a4891499e9b58e3ddaac7a347"⟩, + ⟨"Lean4Ix/Certified/Quotient/Checked.lean", "Ix/Theory/Certified/Quotient/Checked.lean", "d093c1dd39f35be954c8ddbc0267f00cbf6119262a512efb5c9ad96250e208ba"⟩, + ⟨"Lean4Ix/Certified/Quotient/Publish.lean", "Ix/Theory/Certified/Quotient/Publish.lean", "e637fa623b20d14592b09fbf8101bd08a741d525dab462404a057fbcafc004bd"⟩, + ⟨"Lean4Ix/Certified/Quotient/Reading.lean", "Ix/Theory/Certified/Quotient/Reading.lean", "b20e0748f126fa0281d4c552e242f22d9ba3c2ce2d578de0604794504b199f60"⟩, + ⟨"Lean4Ix/Certified/Quotient/Syntax.lean", "Ix/Theory/Certified/Quotient/Syntax.lean", "25ec7aebc6499c1a2778355bef69c0dc262b66a00fe12124c78c38dd7a867adb"⟩, + ⟨"Lean4Ix/Certified/Quotient/Value.lean", "Ix/Theory/Certified/Quotient/Value.lean", "08575dd364648ad62fadba8b2d67e514b03e775354c17b029dea6a6b4c7c0985"⟩, + ⟨"Lean4Ix/Certified/Signature.lean", "Ix/Theory/Certified/Signature.lean", "7be85d7fa465ce50f0dfc8ab65e364c5562e81763914fa779857a636d97a64b7"⟩, + ⟨"Lean4Ix/Certified/Source.lean", "Ix/Theory/Certified/Source.lean", "f1bbfea2e6e31b9969c3acc70da1182b015c2a11700f92ce8d6304c74128b3ec"⟩, + ⟨"Lean4Ix/Certified/Standard/Admission.lean", "Ix/Theory/Certified/Standard/Admission.lean", "cfd3c5d8662b25116d3c58cf3fa7429368b36071f0ebc032df154e154fa05ff4"⟩, + ⟨"Lean4Ix/Certified/Standard/Checked.lean", "Ix/Theory/Certified/Standard/Checked.lean", "cb58c99a87d53fa919f1a742fef8727df0690d77b2713b42d1c815e880666cfd"⟩, + ⟨"Lean4Ix/Certified/Standard/Realization.lean", "Ix/Theory/Certified/Standard/Realization.lean", "2a90165564c1f56591d6b68501dbfa846673f4fe59cf47aeaa16d0fb79e6c172"⟩, + ⟨"Lean4Ix/Certified/Store.lean", "Ix/Theory/Certified/Store.lean", "84ab117484c092edd994dff513cea86042f23bb5d076c61bcb640fda3d146842"⟩, + ⟨"Lean4Ix/Certified/Structure/Admission.lean", "Ix/Theory/Certified/Structure/Admission.lean", "b38964cc9ff83d383c8a4d110cb3e11d9d0ea66c0b3498e9e7481a31cd560483"⟩, + ⟨"Lean4Ix/Certified/Structure/Checked.lean", "Ix/Theory/Certified/Structure/Checked.lean", "1d92b154e7a69cf25bbfbf138aca9f0a77172e55be1e2f5392cc0a2d84f27f33"⟩, + ⟨"Lean4Ix/Certified/Structure/Computation.lean", "Ix/Theory/Certified/Structure/Computation.lean", "c537302f6d91deb46c5b9eb29641124fc30fb0acc84d231d51d6ae7c3be4c65f"⟩, + ⟨"Lean4Ix/Certified/Structure/Publish.lean", "Ix/Theory/Certified/Structure/Publish.lean", "48015840ff11abaea77db702acca3ec29e479395f4509a5fc409cba7cb2f923d"⟩, + ⟨"Lean4Ix/Certified/Structure/Reading.lean", "Ix/Theory/Certified/Structure/Reading.lean", "36074e7c2f8a23d4ea5cece00a703773cdc9bc8907304072038b78845b359ddd"⟩, + ⟨"Lean4Ix/Certified/Structure/Syntax.lean", "Ix/Theory/Certified/Structure/Syntax.lean", "ffdbc5cced526a36b7f5139af30cdb2fb3be03222194b56b0f68edb5e72775d4"⟩, + ⟨"Lean4Ix/Certified/Structure/Value.lean", "Ix/Theory/Certified/Structure/Value.lean", "6e96be97f9894a209efdc79d2ff96c21f4202ad11afb7afa639f9d5bc5c553ee"⟩, + ⟨"Lean4Ix/Certified/Telescope.lean", "Ix/Theory/Certified/Telescope.lean", "40fecfc7cf9d1abafb855079b8c8d55cff471e6ffb9b7ddd919ea90951b78987"⟩, + ⟨"Lean4Ix/Theory/Const.lean", "Ix/Theory/Const.lean", "bfb95d9f4af666804e79e34933e2fd094c0a783bce5b5d3da7b8b6061f4d0f0a"⟩, + ⟨"Lean4Ix/Theory/Expr.lean", "Ix/Theory/Expr.lean", "c848e36c9654d211718ef4fedba3c338ac0428e4e54024c770022263aecf437b"⟩, + ⟨"Lean4Ix/Theory/Inductive/Levels.lean", "Ix/Theory/Inductive/Levels.lean", "59c8752ec02adbe0860adf60eadedd480152d5bf352058f647512158f0a9a6cb"⟩, + ⟨"Lean4Ix/Model/Annotated.lean", "Ix/Theory/Model/Annotated.lean", "55f71adb9e40f4a85aae2fbb4349498f4f2007d568a6bc84fffbdec437741a04"⟩, + ⟨"Lean4Ix/Model/Context.lean", "Ix/Theory/Model/Context.lean", "b0ce5e4808fc1a4a4d4ca8bf5faf850420675eb9065d3912ce9156de4ba93c37"⟩, + ⟨"Lean4Ix/Model/Environment.lean", "Ix/Theory/Model/Environment.lean", "bd646827350485bde61df1825f47a4ff20861ebf6f45853ddb2f3cf611858e38"⟩, + ⟨"Lean4Ix/Model/Extension.lean", "Ix/Theory/Model/Extension.lean", "c1f649360ad30417e9ddb84d6dea939e77c0aebc80279fa5b56d806feb22f109"⟩, + ⟨"Lean4Ix/Model/Inductive/Codes.lean", "Ix/Theory/Model/Inductive/Codes.lean", "9855264e0038c77b57491c3818c192bbd0bd4fba210e33bf5a2d9dfded591939"⟩, + ⟨"Lean4Ix/Model/Inductive/Container.lean", "Ix/Theory/Model/Inductive/Container.lean", "e14328e0152de4f8017a28b0500e96615bc432f98b1475babb229b34967394ba"⟩, + ⟨"Lean4Ix/Model/Inductive/Recursor.lean", "Ix/Theory/Model/Inductive/Recursor.lean", "3028689c3bb11d90e38170e680560c8a9d123bce42d46b00f1bfd4e78d4c9026"⟩, + ⟨"Lean4Ix/Model/Inductive/Telescope.lean", "Ix/Theory/Model/Inductive/Telescope.lean", "8867df5a379798d10615f7c7b068f4982c3e846b1511cdb18bfc600ec66c7c18"⟩, + ⟨"Lean4Ix/Model/Instantiation.lean", "Ix/Theory/Model/Instantiation.lean", "994e4027343d62e996fb814fc32d12c86571d4557fe7a157349a6eaf5b2e81f6"⟩, + ⟨"Lean4Ix/Model/Interpret.lean", "Ix/Theory/Model/Interpret.lean", "a505d97b1c6658d72391f0187462c443396cdce88fe6256f070842d71ea3fdf9"⟩, + ⟨"Lean4Ix/Model/Judgment.lean", "Ix/Theory/Model/Judgment.lean", "a98aff0288e2db11369e658f7e083309ff4e6794bc66e0b3cab7465aac0a7591"⟩, + ⟨"Lean4Ix/Model/PrimitiveValues.lean", "Ix/Theory/Model/PrimitiveValues.lean", "84d16a4015cc253f731494e4c5213170d8b4174b6e33871becc598f9c1a46d55"⟩, + ⟨"Lean4Ix/Model/ReferenceMap.lean", "Ix/Theory/Model/ReferenceMap.lean", "a9d50c7c0f6ae0f4258ecfaa828a127361c277a242ae8111b9d149c137acb29e"⟩, + ⟨"Lean4Ix/Model/SetModel/Container.lean", "Ix/Theory/Model/SetModel/Container.lean", "bfc5c926aee16b6456151f48832bd8c41bbb9582f541ccc864eec753277c3a25"⟩, + ⟨"Lean4Ix/Model/SetModel/Iter.lean", "Ix/Theory/Model/SetModel/Iter.lean", "6962b64d8ccd7b34df2dce7c7e0dff6bf4872b6d11de2ab608807b4787e230a0"⟩, + ⟨"Lean4Ix/Model/SetModel/Ops.lean", "Ix/Theory/Model/SetModel/Ops.lean", "17a7e331d36a0fb0a5153bad2e115f3795cdb2db24f20e1a96a3102e845b1955"⟩, + ⟨"Lean4Ix/Model/SetModel/RecGraph.lean", "Ix/Theory/Model/SetModel/RecGraph.lean", "e6ca5ce2ab2681efbc6d3f549d596e6c7522860d1656db7bbe3528a874b8593c"⟩, + ⟨"Lean4Ix/Model/SetModel/TaggedSum.lean", "Ix/Theory/Model/SetModel/TaggedSum.lean", "3f8f97c2b4d752a97bada45b5e7bc6dd3cb20a054f703a72c5e06af1646286dc"⟩, + ⟨"Lean4Ix/Model/SetModel/TupleTower.lean", "Ix/Theory/Model/SetModel/TupleTower.lean", "0665b318c36faad149ccc4ef213fe81be47d36d7283c372ff70f26d80c0fd8a9"⟩, + ⟨"Lean4Ix/Model/SetTheory/Core.lean", "Ix/Theory/Model/SetTheory/Core.lean", "1ad5614af10cf9d8eb3e390ee279d1161a0704d3af1ba36d50012e8683aa06d5"⟩, + ⟨"Lean4Ix/Model/SetTheory/Derive/Choice.lean", "Ix/Theory/Model/SetTheory/Derive/Choice.lean", "a451c14971f8be37fcc35534a45b6b8f569a87abb87920aa3efb02c352d4ffa5"⟩, + ⟨"Lean4Ix/Model/SetTheory/Derive/Empty.lean", "Ix/Theory/Model/SetTheory/Derive/Empty.lean", "72408fc268cf2d0bd6a39d27873a69576a67f8b75a1539dc26fd6147c11bcbb2"⟩, + ⟨"Lean4Ix/Model/SetTheory/Derive/Graphs.lean", "Ix/Theory/Model/SetTheory/Derive/Graphs.lean", "fcea63a8b11704d8f25b8dcf92fa05c494097a7195673859fe45288503deae00"⟩, + ⟨"Lean4Ix/Model/SetTheory/Derive/Lfp.lean", "Ix/Theory/Model/SetTheory/Derive/Lfp.lean", "9a4f8d03a9b8dca4b112e89862540aa9cf4d53efe7580f7dc6d61690783101b2"⟩, + ⟨"Lean4Ix/Model/SetTheory/Derive/LfpFam.lean", "Ix/Theory/Model/SetTheory/Derive/LfpFam.lean", "862fce7477e11f1a18c5e07336ba5331b5325e7f9efc6926a87eac836f6715e7"⟩, + ⟨"Lean4Ix/Model/SetTheory/Derive/Omega.lean", "Ix/Theory/Model/SetTheory/Derive/Omega.lean", "aa76b574e7a6fe136cca8f9ba3d87cbd6a9577cec39cbdb91a193c265185d39d"⟩, + ⟨"Lean4Ix/Model/SetTheory/Derive/Pair.lean", "Ix/Theory/Model/SetTheory/Derive/Pair.lean", "99ecac13fda8ad3f3e87e0a2d4bf2f5dd72e8e859dfb98d0da17ca0b14c77134"⟩, + ⟨"Lean4Ix/Model/SetTheory/Derive/Pt.lean", "Ix/Theory/Model/SetTheory/Derive/Pt.lean", "3a2213814398c232d3a1f46365f22fc71d028f0e40f9c55fd1e135d07b304260"⟩, + ⟨"Lean4Ix/Model/SetTheory/Derive/Quot.lean", "Ix/Theory/Model/SetTheory/Derive/Quot.lean", "de5148118f48b3042609d813fdfe930924330af6ca0a5cd1b831560a035a51bc"⟩, + ⟨"Lean4Ix/Model/SetTheory/Derive/Sep.lean", "Ix/Theory/Model/SetTheory/Derive/Sep.lean", "24624815b917df8747786c56e2e47be740fc1e1be9f685f5fd053ac1001dbdc7"⟩, + ⟨"Lean4Ix/Model/SetTheory/Derive/Sigma.lean", "Ix/Theory/Model/SetTheory/Derive/Sigma.lean", "ca545076118e71a0235b6f3f6ada2e6c4c96830ca606e31dade4f3a2e060e681"⟩, + ⟨"Lean4Ix/Model/SetTheory/Derive/Univ.lean", "Ix/Theory/Model/SetTheory/Derive/Univ.lean", "3d59dcca1339d8e1e28bee7efe8792f44c0917fb1d1c749c61ed5a5976fcdeae"⟩, + ⟨"Lean4Ix/Model/SetTheory/Derive/Universe.lean", "Ix/Theory/Model/SetTheory/Derive/Universe.lean", "f2c24862145d6624dcd9dacfcd68b845b6c68522d0fc81c1eac06839b455b61e"⟩, + ⟨"Lean4Ix/Model/Signature.lean", "Ix/Theory/Model/Signature.lean", "5c6d3f9467466e3d677865a1536309bc05b96aafadf4312f7dc1679710929837"⟩, + ⟨"Lean4Ix/Model/Support.lean", "Ix/Theory/Model/Support.lean", "020dc36c6376e3f7f725db8d4fd6a559f4e1ad303cdfb41c5e738f00c6ac05de"⟩, + ⟨"Lean4Ix/Model/TelescopeSemantics.lean", "Ix/Theory/Model/TelescopeSemantics.lean", "a717a653a8d8d88d69a52acba0a8fe81b0c5635194c408bfdb503382ec9d0d66"⟩, + ⟨"Lean4Ix/Model/Value.lean", "Ix/Theory/Model/Value.lean", "b2cf0796c8bcf6e138136c3f9f017d3df551b2a243d2e860fa943bb8d1411524"⟩, + ⟨"Lean4Ix/Model/WellDenoted.lean", "Ix/Theory/Model/WellDenoted.lean", "8fc3eeb787fcc0182da41c880cf416c214d7930a433d0f35f77f393a2e9526e8"⟩, + ⟨"Lean4Ix/Theory/Quot.lean", "Ix/Theory/Quot.lean", "321d4c7739af0b052da2a5c59ba875fe1ec35ec334a382d6107e02aeac238b49"⟩, + ⟨"Lean4Ix/Theory/Ref.lean", "Ix/Theory/Ref.lean", "885181febe5ce0e1f9c6baac03e4604efb4acea27e0e8b61fde9900c14112f00"⟩, + ⟨"Lean4Ix/Theory/Rename.lean", "Ix/Theory/Rename.lean", "3ce70afd260f3d9f68b830e11c5d1937f0d9ccf3b3f8a4e70ffc139a84fb8322"⟩, + ⟨"Lean4Ix/Std/Basic.lean", "Ix/Theory/Std/Basic.lean", "450413877e701f1d906d13cf3074149127bb1637a9e5a4bd723d20769662fe89"⟩, + ⟨"Lean4Ix/Theory/Store.lean", "Ix/Theory/Store.lean", "316c34c9f083a5d751f0fe9a478c50d6f3fc511a7aa8529042776a5b774fd763"⟩, + ⟨"Lean4Ix/Theory/VLevel.lean", "Ix/Theory/VLevel.lean", "fc91db763262069edc1181dcb0ff12ac78d1efdd370a8acb2b297d3fcec963ee"⟩, + ⟨"Lean4Ix/Certified.lean", "Ix/Theory.lean", "db42519eb53c5c24965b27879cb1ad81eca1010513fc67cf486f324c942fce98"⟩] + +structure ConLecheFile where + source : String + target : String + sourceSha256 : String + lean4IxSha256 : String + targetSha256 : String + deriving Repr + +def conLeche : Array ConLecheFile := #[ + ⟨"ConLeche/SetTheory/Core.lean", "Ix/Theory/Model/SetTheory/Core.lean", "52f54c7f8664a0d00ba04083d428ff81c2b89e7e68fbd6e24ffb349ef441f19a", "1ad5614af10cf9d8eb3e390ee279d1161a0704d3af1ba36d50012e8683aa06d5", "9e8d3537a66ded9e4d9142765c9ebda3eb4c3a8f780927cea6657e076933657b"⟩, + ⟨"ConLeche/SetTheory/Derive/Empty.lean", "Ix/Theory/Model/SetTheory/Derive/Empty.lean", "259b3b365926256fbe8281a7066be601e419c3e492452b62086e0fd44e1a3f16", "72408fc268cf2d0bd6a39d27873a69576a67f8b75a1539dc26fd6147c11bcbb2", "b9a961b71e8a0576ceb00fbdc0288231d360576cd7afa72375606c793da15541"⟩, + ⟨"ConLeche/SetTheory/Derive/Sep.lean", "Ix/Theory/Model/SetTheory/Derive/Sep.lean", "a6a452a34028ff2e194987de8fc5bc8f847ff8950f962b5d33c6c6ee5045a52e", "24624815b917df8747786c56e2e47be740fc1e1be9f685f5fd053ac1001dbdc7", "38536c268957fe0ad6e0127a1f07677bbfe4a17de82d0a2738b9042db55be2c7"⟩, + ⟨"ConLeche/SetTheory/Derive/Pair.lean", "Ix/Theory/Model/SetTheory/Derive/Pair.lean", "851f954187fcc05ba2f43a6b81112df27c91e34f7de54352eaf461380fee1bd7", "99ecac13fda8ad3f3e87e0a2d4bf2f5dd72e8e859dfb98d0da17ca0b14c77134", "a72c5b13f5657a8299b060bd344068f56c3e2fadfc3619e64b9bc28e98085c91"⟩, + ⟨"ConLeche/SetTheory/Derive/Universe.lean", "Ix/Theory/Model/SetTheory/Derive/Universe.lean", "57a24b4144088ac14dabdb9b6f88c49cf251a0659f8d4c073aad6682baeb099d", "f2c24862145d6624dcd9dacfcd68b845b6c68522d0fc81c1eac06839b455b61e", "c3eb55ee316ce142c16fa5c9a3e06ac45721767bad845ce8cd4c485ac7cce494"⟩, + ⟨"ConLeche/SetTheory/Derive/Pt.lean", "Ix/Theory/Model/SetTheory/Derive/Pt.lean", "93f9ed86375f7af7a004f3ef2612ce8c9d8c6626ce73dbf6c3d6702f95f67eae", "3a2213814398c232d3a1f46365f22fc71d028f0e40f9c55fd1e135d07b304260", "520286bf987d29f56976d32ed2527ddf494ac80592fba05c1ffc4ad7627d9934"⟩, + ⟨"ConLeche/SetTheory/Derive/Graphs.lean", "Ix/Theory/Model/SetTheory/Derive/Graphs.lean", "f61433765add5a733b38e95e38e217273f526a5e710b7109f24eb0f83f70faaf", "fcea63a8b11704d8f25b8dcf92fa05c494097a7195673859fe45288503deae00", "5bff626e2b77ea3d83302dd8959ef0e0816e9d9da59d52d573134f139e69316c"⟩, + ⟨"ConLeche/SetTheory/Derive/Omega.lean", "Ix/Theory/Model/SetTheory/Derive/Omega.lean", "c0b20ecd0cc88a74ff6091e5a88506269b587bbc6f1919f72cc63083d2f7e55a", "aa76b574e7a6fe136cca8f9ba3d87cbd6a9577cec39cbdb91a193c265185d39d", "0cf178fada4077bfc8ba7dc257d8ceb77e7db8a452a9fba09eb736ad84f475be"⟩, + ⟨"ConLeche/SetTheory/Derive/Univ.lean", "Ix/Theory/Model/SetTheory/Derive/Univ.lean", "e1084f7c82d9c0d144281491ceb05b015001d1215947d0d687e4dc07497ff911", "3d59dcca1339d8e1e28bee7efe8792f44c0917fb1d1c749c61ed5a5976fcdeae", "33e83e18da0a782904f42acc563c1c4259b60a1833bf2b22af41a28f1bd797e0"⟩, + ⟨"ConLeche/SetModel/Ops.lean", "Ix/Theory/Model/SetModel/Ops.lean", "b19d965f92d1e281afd40a7ee4b2edd1a324b9bdf6d8f26425944dbafe987a11", "17a7e331d36a0fb0a5153bad2e115f3795cdb2db24f20e1a96a3102e845b1955", "6ce498a847103c32824e40f22e51db42163da98c09374f6a69e2a85a69f1e732"⟩, + ⟨"ConLeche/SetTheory/Derive/Sigma.lean", "Ix/Theory/Model/SetTheory/Derive/Sigma.lean", "1a844ecb6709ae0ccefd43a4eb8d6da92b3753033e882ce74cfbd27c6890545f", "ca545076118e71a0235b6f3f6ada2e6c4c96830ca606e31dade4f3a2e060e681", "59cfd35485e1b90d199089aea2886fe7ed12bd9f02637d2d7cd5e21536ec6ef5"⟩, + ⟨"ConLeche/SetTheory/Derive/Lfp.lean", "Ix/Theory/Model/SetTheory/Derive/Lfp.lean", "d6c1a824ee0b358f4007c2d297c4e29011c95914c2241d402f5bf27da333614f", "9a4f8d03a9b8dca4b112e89862540aa9cf4d53efe7580f7dc6d61690783101b2", "65e44e5bfe2fa0720b334cd31aff2c2eff511364feadfd5fd60c1ebf150b9a89"⟩, + ⟨"ConLeche/SetTheory/Derive/LfpFam.lean", "Ix/Theory/Model/SetTheory/Derive/LfpFam.lean", "18fa2c12b37d9786b9a1961ed526e7d411869d93cfa241ebbdf2910d4418ac97", "862fce7477e11f1a18c5e07336ba5331b5325e7f9efc6926a87eac836f6715e7", "0657c96c6dffb7a49fec92c97769356595ec8a6f06b5f73594d4732d2fef8b8f"⟩, + ⟨"ConLeche/SetTheory/Derive/Choice.lean", "Ix/Theory/Model/SetTheory/Derive/Choice.lean", "a8404566f4d831874e044dcfba6865b750fda3c9427e13c3d2b68d7ac45d5b48", "a451c14971f8be37fcc35534a45b6b8f569a87abb87920aa3efb02c352d4ffa5", "47e64bd1d52287230b993398ba5ee584d7b15e256b86f4fa0eaf1735c4510b93"⟩, + ⟨"ConLeche/SetModel/TupleTower.lean", "Ix/Theory/Model/SetModel/TupleTower.lean", "143b965d29faae965c16288021ba1cf401c3f10e76550386ddb81d9ca1f5cbee", "0665b318c36faad149ccc4ef213fe81be47d36d7283c372ff70f26d80c0fd8a9", "1089b4ea6d9e7ff3ed9902a3c3d8f6697332b2aac23f94e503584b9cdf972750"⟩, + ⟨"ConLeche/SetModel/TaggedSum.lean", "Ix/Theory/Model/SetModel/TaggedSum.lean", "eb64def578bf85bc2b197b83648749996ebcd243be573752f617a5f91f3332d8", "3f8f97c2b4d752a97bada45b5e7bc6dd3cb20a054f703a72c5e06af1646286dc", "53b565da9a2ce8848ca2442e52bfa0ff13b3f044017858fe69be42a9b2614aff"⟩, + ⟨"ConLeche/SetModel/Iter.lean", "Ix/Theory/Model/SetModel/Iter.lean", "0688fa56fad4dcac618589cd9bafc2860c43b95978dc6dcf6c3e292cafc4b149", "6962b64d8ccd7b34df2dce7c7e0dff6bf4872b6d11de2ab608807b4787e230a0", "019ef9fa62f5d141ccc8c7e41259ff52dfba56412b3a57917ba60c28a2bfb378"⟩, + ⟨"ConLeche/SetModel/RecGraph.lean", "Ix/Theory/Model/SetModel/RecGraph.lean", "66de7b97c640cec3987f89c31ae19c4daa27b6da53ba6943767e0078802f1faf", "e6ca5ce2ab2681efbc6d3f549d596e6c7522860d1656db7bbe3528a874b8593c", "da698d1a7346eaaa2f5b4734de06c955a05791ff52728d554082814328bd5caa"⟩, + ⟨"ConLeche/SetModel/Container.lean", "Ix/Theory/Model/SetModel/Container.lean", "47b7bbab4764309ad1a11d7e6a3d2061c2968928367e2285e2a7e549bf7e6c0c", "bfc5c926aee16b6456151f48832bd8c41bbb9582f541ccc864eec753277c3a25", "08928de8de78f1fabc535b65e8511b068ad3c8b517055aa363674c2c90cebd4e"⟩, + ⟨"ConLeche/SetTheory/Derive/Quot.lean", "Ix/Theory/Model/SetTheory/Derive/Quot.lean", "cadc387495c563d796ba4ca45cc0125cbe8ff5168d81bc47b364bd6776c3898b", "de5148118f48b3042609d813fdfe930924330af6ca0a5cd1b831560a035a51bc", "356b3b639fad5c6a9990bf106a38d49b32d0b0ae80537aba2719aaab8f9d9c84"⟩] + +end Tests.Theory.ImportManifest diff --git a/Tests/Theory/Modeled.lean b/Tests/Theory/Modeled.lean new file mode 100644 index 000000000..6f62ff1d2 --- /dev/null +++ b/Tests/Theory/Modeled.lean @@ -0,0 +1,165 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certificate.Modeled +import Ix.Theory.Certificate.Build +import Tests.Theory.Ordinary +import Tests.Theory.Certified +import Tests.Theory.ModeledFixtures + +open Ix.Theory + +/-! Model declarations and certificates are generated from actual anonymous +source data, then checked through the public store entry point. This corpus +does not substitute a caller-supplied realization for declaration checking. -/ + +namespace Tests.Theory.Modeled + +open Ix.Theory.Model Ix.Theory.Certified Ix.Theory.Certificate Ix.Theory.Inductive +open Tests.Theory.Certified (primitives) + +set_option maxRecDepth 8192 +set_option maxHeartbeats 16000000 + +def ref (block member : Nat := 0) : ConstRef Nat := .member block member +def const (block member : Nat := 0) (levels : List VLevel := []) : VExpr Nat := .const (ref block member) levels +def type0 : VExpr Nat := .sort (.succ .zero) +def natType : VExpr Nat := const 20 + +def mutualSource : Block Nat := ModeledFixtures.mutualSource +def mutualRecursors : Block Nat := ModeledFixtures.mutualRecursors + +/-- Both families are modeled by Nat. B's constructor is identity; A has zero +and successor. The dependent mutual fold is built from the checked Nat fold. -/ +def modelTargets : List (ConstRef Nat) := + [ref 20, .ctor 20 0 0, .ctor 20 0 1, ref 20, ref 40, ref 41, ref 42] +def modelPairs : List (ConstRef Nat × ConstRef Nat) := + [ref 30, .ctor 30 0 0, .ctor 30 0 1, ref 30 1, .ctor 30 1 0, ref 31, ref 31 1].zip modelTargets +def mapped (expression : VExpr Nat) : VExpr Nat := expression.mapRefs (Certificate.Modeled.target modelPairs) +def recursorType (index : Nat) : VExpr Nat := + mapped ((mutualRecursors.members[index]?).map Const.type |>.getD type0) +def recPrefix : List (VExpr Nat) := (recursorType 0).telN 5 + +def modelA : VExpr Nat := + .lamN recPrefix (.appN (const 21 0 [.param 0]) [ + .bvar 4, .bvar 2, + .lam natType (.lam (.app (.bvar 5) (.bvar 0)) + (.appN (.bvar 3) [.bvar 1, .appN (.bvar 2) [.bvar 1, .bvar 0]]))]) + +def modelB : VExpr Nat := + .lamN (recPrefix ++ [natType]) (.appN (.bvar 1) + [.bvar 0, .appN (const 41 0 [.param 0]) (VExpr.bvarRevRange 1 5 ++ [.bvar 0])]) + +def block : Nat → Block Nat + | 10 => ⟨[PrimitiveSignature.falseDeclaration]⟩ + | 11 => ⟨[primitives.falseElimDeclaration]⟩ + | 20 => ⟨[Tests.Theory.Ordinary.natShape.source 20]⟩ + | 21 => ⟨[Tests.Theory.Ordinary.natShape.recursorSource 20 21 .large]⟩ + | 30 => mutualSource + | 31 => mutualRecursors + | 40 => ⟨[.defn 0 .definition (.forallE natType natType) (.lam natType (.bvar 0)) .safe]⟩ + | 41 => ⟨[.defn 1 .definition (recursorType 0) modelA .safe]⟩ + | 42 => ⟨[.defn 1 .definition (recursorType 1) modelB .safe]⟩ + | _ => ⟨[]⟩ + +def store (alter : Nat → Block Nat → Block Nat := fun _ b => b) : Store Nat where + dom := List.range 43 + nodup := List.nodup_range + blocks b := if b < 43 then some (alter b (block b)) else none + mem_dom b := by split <;> simp_all + +def prefixWitnesses? : Option (Environment Nat × List (DeclarationWitness Nat)) := + declarationWitnesses? 3000 store primitives.environment + [.ordinary 20 21, .definition (ref 40), .definition (ref 41), .definition (ref 42)] + +def modeledWitness? : Option (Ix.Theory.Certified.Modeled.Witness Nat) := do + let (entries, _) ← prefixWitnesses? + Certificate.Modeled.witness? 3000 entries store 30 [ref 31, ref 31 1] modelTargets + +def witnesses? : Option (List (DeclarationWitness Nat)) := do + let (_, earlier) ← prefixWitnesses? + return earlier ++ [.modeled (← modeledWitness?)] + +def accepted : Bool := witnesses?.any fun witnesses => + acceptsStoreCertified.{0,0} 3000 primitives store [ref 30, ref 30 1, ref 31, ref 31 1] witnesses + +#guard (Ix.Theory.Certified.Modeled.sourceRefs? store 30 [ref 31, ref 31 1]).isSome +#guard (Ix.Theory.Certified.Modeled.recursorRules? store (ref 31)).any (fun rules => rules.length == 2) +#guard (Ix.Theory.Certified.Modeled.recursorRules? store (ref 31 1)).any (fun rules => rules.length == 1) +#guard store.ctorRuleIndex? (.ctor 30 1 0) == some 2 +#guard prefixWitnesses?.isSome +#guard modeledWitness?.isSome +#guard accepted + +def candidate : Certificate.Modeled.Candidate Nat := ⟨30, [ref 31, ref 31 1], modelTargets, []⟩ +def generated? : Option (List (DeclarationWitness Nat)) := + Certificate.storeWitness? 3000 primitives store [ref 31, ref 31 1] [candidate] +#guard generated?.any (acceptsStoreCertified.{0,0} 3000 primitives store [ref 31, ref 31 1]) + +def computed : VExpr Nat := .appN (const 31 0 [.succ .zero]) [ + .lam (const 30) (.sort .zero), .lam (const 30 1) (.sort .zero), .bvar 1, + .lam (const 30 1) (.lam (.sort .zero) (.bvar 0)), + .lam (const 30) (.lam (.sort .zero) (.bvar 0)), + .app (.const (.ctor 30 0 1) []) + (.app (.const (.ctor 30 1 0) []) (.const (.ctor 30 0 0) []))] +def proof : VExpr Nat := .lam (.sort .zero) (.lam (.bvar 0) (.bvar 0)) +def proposition : VExpr Nat := .forallE (.sort .zero) (.forallE (.bvar 0) computed) +def input : ProofInput Nat := ⟨store, 0, proof, proposition⟩ +def proofAccepted : Bool := (Certificate.proofWitness? 3000 primitives input [candidate]).any + (acceptsCertified.{0,0} 3000 primitives input) +#guard proofAccepted + +def withRecursor (alter : Const Nat → Const Nat) : Store Nat := + store fun n b => if n = 31 then ⟨b.members.map alter⟩ else b +def withRules (alter : List (RecRule Nat) → List (RecRule Nat)) : Store Nat := + withRecursor fun c => match c with + | .recursor u p i m n t rules k s => .recursor u p i m n t (alter rules) k s + | c => c +def sourceRejected (source : Store Nat) : Bool := witnesses?.any fun witnesses => + !(acceptsStoreCertified.{0,0} 3000 primitives source [ref 31, ref 31 1] witnesses) + +-- Both families contribute to the same table. Slot 2 must not become slot 1, +-- and an equation about the other recursor cannot justify the selected rule. +#guard sourceRejected (withRules fun rules => rules.reverse) +#guard sourceRejected (withRules fun rules => rules.take 2) +#guard sourceRejected (withRules fun rules => rules.map fun r => { r with nfields := r.nfields + 1 }) +#guard sourceRejected (withRules fun rules => rules.map fun r => { r with rhs := .sort .zero }) +#guard sourceRejected (withRecursor fun c => match c with + | .recursor u p i m n t rs _ s => .recursor u p i m n t rs true s | c => c) +#guard sourceRejected (withRecursor fun c => match c with + | .recursor u p i m n t rs k _ => .recursor u p i m n t rs k .unsafe | c => c) +#guard sourceRejected (store fun n b => if n = 40 then + ⟨[.axiom 0 (.forallE natType natType) .safe]⟩ else b) +#guard sourceRejected (store fun n b => if n = 40 then + ⟨[.defn 0 .definition (.forallE natType natType) (.const (.ctor 30 1 0) []) .safe]⟩ else b) + +def witnessRejected (alter : Ix.Theory.Certified.Modeled.Witness Nat → Ix.Theory.Certified.Modeled.Witness Nat) : Bool := + (prefixWitnesses?).any fun (_, declarations) => (modeledWitness?).any fun witness => + !(acceptsStoreCertified.{0,0} 3000 primitives store [ref 31, ref 31 1] + (declarations ++ [.modeled (alter witness)])) + +#guard witnessRejected fun w => { w with recursors := w.recursors.reverse } +#guard witnessRejected fun w => { w with companions := w.companions.reverse } +#guard witnessRejected fun w => { w with companions := w.companions.drop 1 } +#guard witnessRejected fun w => { w with equations := w.equations.drop 1 } +#guard witnessRejected fun w => { w with companions := w.companions.map fun c => { c with model := c.header.ref } } +#guard witnessRejected fun w => { w with companions := w.companions.map fun c => + { c with rules := c.rules.map fun r => { r with lhs := r.rhs } } } +#guard witnessRejected fun w => { w with equations := w.equations.map (fun es => + es.map (fun e => { e with proof := .conversion .refl })) } + +-- The generic dependency walker rejects a model that depends on the new +-- source, and does not turn a missing model witness into ordinary admission. +#guard (Certificate.storeWitness? 3000 primitives store [ref 31] [{ candidate with models := + [ref 30, .ctor 30 0 0, .ctor 30 0 1, ref 30 1, .ctor 30 1 0, ref 31, ref 31 1] }]).isNone +#guard (Certificate.storeWitness? 3000 primitives store [ref 31]).isNone + +-- Model admission supplies no projection or eta facts for the new families. +#guard (modeledWitness?).all fun w => w.companions.all (fun c => c.entry.facts.isEmpty) +#guard (prefixWitnesses?).any fun (entries, _) => (modeledWitness?).any fun w => + (inferSource? 3000 0 (Ix.Theory.Certified.Modeled.environment entries w.companions) [] + (.proj (ref 30 1) 0 (.app (.const (.ctor 30 1 0) []) (.const (.ctor 30 0 0) [])))).isNone + +end Tests.Theory.Modeled diff --git a/Tests/Theory/ModeledEquations.lean b/Tests/Theory/ModeledEquations.lean new file mode 100644 index 000000000..449e18a8e --- /dev/null +++ b/Tests/Theory/ModeledEquations.lean @@ -0,0 +1,99 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Theory.Modeled + +open Ix.Theory + +namespace Tests.Theory.ModeledEquations + +open Ix.Theory.Model Ix.Theory.Certified Ix.Theory.Certificate +open Tests.Theory.Certified (primitives) +open Tests.Theory.Modeled (ref) + +set_option maxRecDepth 16384 +set_option maxHeartbeats 32000000 + +def eqRef : ConstRef Nat := ref 60 +def reflRef : ConstRef Nat := .ctor 60 0 0 +def eqRec : ConstRef Nat := ref 61 +def block : Nat → Block Nat + | 60 => ⟨[Basis.Equality.shape.source 60]⟩ + | 61 => ⟨[Basis.Equality.shape.recursorSource 60 61 .large true]⟩ + | n => Tests.Theory.Modeled.block n +def store : Store Nat where + dom := List.range 62 + nodup := List.nodup_range + blocks b := if b < 62 then some (block b) else none + mem_dom b := by split <;> simp_all + +def prefixWitnesses? := declarationWitnesses? 4000 store primitives.environment + [.ordinary 60 61, .ordinary 20 21, .definition (ref 40), .definition (ref 41), .definition (ref 42)] + +/-- Use a checked proof of equality of the complete functions, with their +entire telescopes as endpoints. No equation is reflected into Theory.IsDefEq. -/ +def propositional? (entries : Environment Nat) (rule : Signature.Rule Nat) + (witness : Ix.Theory.Certified.Modeled.EquationWitness Nat) : + Option (Ix.Theory.Certified.Modeled.EquationWitness Nat) := do + let proof := Basis.Equality.reflexivity reflRef witness.formation.level rule.type rule.lhs + let inferred ← inferAnnotated? 4000 rule.universes entries [] proof + let typed ← castWith? 4000 rule.universes entries [] inferred + (Basis.Equality.applied eqRef witness.formation.level rule.type rule.lhs rule.rhs) + return { witness with proof := .propositional eqRef reflRef eqRec proof typed } + +def witness? : Option (Ix.Theory.Certified.Modeled.Witness Nat) := do + let (entries, _) ← prefixWitnesses? + let witness ← Tests.Theory.Modeled.candidate.witness? 4000 entries store + let equations ← (witness.companions.zip witness.equations).mapM fun (companion, proofs) => + (companion.rules.zip proofs).mapM fun (rule, proof) => + propositional? entries (Ix.Theory.Certified.Modeled.mapRule witness.companions rule) proof + return { witness with equations } + +def accepted (alter : Ix.Theory.Certified.Modeled.Witness Nat → Ix.Theory.Certified.Modeled.Witness Nat := id) : Bool := + prefixWitnesses?.any fun (_, earlier) => witness?.any fun witness => + acceptsStoreCertified.{0,0} 4000 primitives store [ref 31, ref 31 1] (earlier ++ [.modeled (alter witness)]) + +#guard accepted +#guard !accepted fun w => { w with equations := w.equations.map (fun proofs => proofs.map fun proof => + { proof with proof := .propositional eqRef reflRef eqRec (.sort .zero) .sort }) } +#guard !accepted fun w => { w with equations := w.equations.map (fun proofs => proofs.map fun proof => + match proof.proof with + | .propositional _ r rec term typed => { proof with proof := .propositional (ref 20) r rec term typed } + | _ => proof) } +#guard !accepted fun w => { w with companions := w.companions.map (fun c => + { c with rules := c.rules.map fun rule => { rule with lhs := rule.rhs } }) } + +/-- Actual model equation declarations for serialization and command tests. +Each proof is part of the source store and checked as a normal theorem. -/ +def equationDeclarations? : Option (List (Const Nat)) := do + let (entries, _) ← prefixWitnesses? + let witness ← Tests.Theory.Modeled.candidate.witness? 4000 entries store + return (witness.companions.zip witness.equations).flatMap fun (companion, proofs) => + (companion.rules.zip proofs).map fun (rule, proof) => + let rule := Ix.Theory.Certified.Modeled.mapRule witness.companions rule + let type := Basis.Equality.applied eqRef proof.formation.level rule.type rule.lhs rule.rhs + let body := Basis.Equality.reflexivity reflRef proof.formation.level rule.type rule.lhs + .defn rule.universes .theorem type.erase body.erase .safe + +def proofBlock (n : Nat) : Block Nat := + if n < 70 then block n else ⟨((equationDeclarations?.getD [])[n - 70]?).toList⟩ +def proofStore : Store Nat where + dom := List.range 73 + nodup := List.nodup_range + blocks b := if b < 73 then some (proofBlock b) else none + mem_dom b := by split <;> simp_all +def hint (n : Nat) : Certificate.Modeled.ProofHint Nat := + ⟨eqRef, reflRef, eqRec, .const (ref n) [.param 0]⟩ +def candidate : Certificate.Modeled.Candidate Nat := + { Tests.Theory.Modeled.candidate with proofs := + [(ref 31, [some (hint 70), some (hint 71)]), (ref 31 1, [some (hint 72)])] } +def input : ProofInput Nat := ⟨proofStore, 0, Tests.Theory.Modeled.proof, Tests.Theory.Modeled.proposition⟩ +def generatedAccepted : Bool := (Certificate.proofWitness? 4000 primitives input [candidate]).any + (acceptsCertified.{0,0} 4000 primitives input) +#guard generatedAccepted +#guard (Certificate.proofWitness? 4000 primitives input + [{ candidate with proofs := [(ref 31, [some (hint 71), some (hint 70)])] }]).isNone + +end Tests.Theory.ModeledEquations diff --git a/Tests/Theory/ModeledFixtures.lean b/Tests/Theory/ModeledFixtures.lean new file mode 100644 index 000000000..2369379d7 --- /dev/null +++ b/Tests/Theory/ModeledFixtures.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Const + +open Ix.Theory + +namespace Tests.Theory.ModeledFixtures + +/- Frozen source and recursor blocks from Lean4Ix's generator. The model +tests validate these inputs without importing the old metatheory. -/ + +def mutualSource : Block Nat := ⟨[ + .induct 0 0 0 (.sort (.succ .zero)) [ + ⟨0, 0, 0, (.const (.member 30 0) []), .safe⟩, + ⟨0, 0, 1, (.forallE (.const (.member 30 1) []) (.const (.member 30 0) [])), .safe⟩ + ] .safe, + .induct 0 0 0 (.sort (.succ .zero)) [ + ⟨0, 0, 1, (.forallE (.const (.member 30 0) []) (.const (.member 30 1) [])), .safe⟩ + ] .safe + ]⟩ + +def mutualRecursors : Block Nat := ⟨[ + .recursor 1 0 0 2 3 (.forallE (.forallE (.const (.member 30 0) []) (.sort (.param 0))) (.forallE (.forallE (.const (.member 30 1) []) (.sort (.param 0))) (.forallE (.app (.bvar 1) (.const (.ctor 30 0 0) [])) (.forallE (.forallE (.const (.member 30 1) []) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 0 1) []) (.bvar 1))))) (.forallE (.forallE (.const (.member 30 0) []) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 1 0) []) (.bvar 1))))) (.forallE (.const (.member 30 0) []) (.app (.bvar 5) (.bvar 0)))))))) [ + ⟨0, (.lam (.forallE (.const (.member 30 0) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.sort (.param 0))) (.lam (.app (.bvar 1) (.const (.ctor 30 0 0) [])) (.lam (.forallE (.const (.member 30 1) []) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 0 1) []) (.bvar 1))))) (.lam (.forallE (.const (.member 30 0) []) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 1 0) []) (.bvar 1))))) (.bvar 2))))))⟩, + ⟨1, (.lam (.forallE (.const (.member 30 0) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.sort (.param 0))) (.lam (.app (.bvar 1) (.const (.ctor 30 0 0) [])) (.lam (.forallE (.const (.member 30 1) []) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 0 1) []) (.bvar 1))))) (.lam (.forallE (.const (.member 30 0) []) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 1 0) []) (.bvar 1))))) (.lam (.const (.member 30 1) []) (.app (.app (.bvar 2) (.bvar 0)) (.app (.app (.app (.app (.app (.app (.const (.member 31 1) [(.param 0)]) (.bvar 5)) (.bvar 4)) (.bvar 3)) (.bvar 2)) (.bvar 1)) (.bvar 0)))))))))⟩, + ⟨1, (.lam (.forallE (.const (.member 30 0) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.sort (.param 0))) (.lam (.app (.bvar 1) (.const (.ctor 30 0 0) [])) (.lam (.forallE (.const (.member 30 1) []) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 0 1) []) (.bvar 1))))) (.lam (.forallE (.const (.member 30 0) []) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 1 0) []) (.bvar 1))))) (.lam (.const (.member 30 0) []) (.app (.app (.bvar 1) (.bvar 0)) (.app (.app (.app (.app (.app (.app (.const (.member 31 0) [(.param 0)]) (.bvar 5)) (.bvar 4)) (.bvar 3)) (.bvar 2)) (.bvar 1)) (.bvar 0)))))))))⟩ + ] false .safe, + .recursor 1 0 0 2 3 (.forallE (.forallE (.const (.member 30 0) []) (.sort (.param 0))) (.forallE (.forallE (.const (.member 30 1) []) (.sort (.param 0))) (.forallE (.app (.bvar 1) (.const (.ctor 30 0 0) [])) (.forallE (.forallE (.const (.member 30 1) []) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 0 1) []) (.bvar 1))))) (.forallE (.forallE (.const (.member 30 0) []) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 1 0) []) (.bvar 1))))) (.forallE (.const (.member 30 1) []) (.app (.bvar 4) (.bvar 0)))))))) [ + ⟨0, (.lam (.forallE (.const (.member 30 0) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.sort (.param 0))) (.lam (.app (.bvar 1) (.const (.ctor 30 0 0) [])) (.lam (.forallE (.const (.member 30 1) []) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 0 1) []) (.bvar 1))))) (.lam (.forallE (.const (.member 30 0) []) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 1 0) []) (.bvar 1))))) (.bvar 2))))))⟩, + ⟨1, (.lam (.forallE (.const (.member 30 0) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.sort (.param 0))) (.lam (.app (.bvar 1) (.const (.ctor 30 0 0) [])) (.lam (.forallE (.const (.member 30 1) []) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 0 1) []) (.bvar 1))))) (.lam (.forallE (.const (.member 30 0) []) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 1 0) []) (.bvar 1))))) (.lam (.const (.member 30 1) []) (.app (.app (.bvar 2) (.bvar 0)) (.app (.app (.app (.app (.app (.app (.const (.member 31 1) [(.param 0)]) (.bvar 5)) (.bvar 4)) (.bvar 3)) (.bvar 2)) (.bvar 1)) (.bvar 0)))))))))⟩, + ⟨1, (.lam (.forallE (.const (.member 30 0) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.sort (.param 0))) (.lam (.app (.bvar 1) (.const (.ctor 30 0 0) [])) (.lam (.forallE (.const (.member 30 1) []) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 0 1) []) (.bvar 1))))) (.lam (.forallE (.const (.member 30 0) []) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 1 0) []) (.bvar 1))))) (.lam (.const (.member 30 0) []) (.app (.app (.bvar 1) (.bvar 0)) (.app (.app (.app (.app (.app (.app (.const (.member 31 0) [(.param 0)]) (.bvar 5)) (.bvar 4)) (.bvar 3)) (.bvar 2)) (.bvar 1)) (.bvar 0)))))))))⟩ + ] false .safe + ]⟩ + +def permutedSource : Block Nat := ⟨[ + .induct 0 0 0 (.sort (.succ .zero)) [ + ⟨0, 0, 1, (.forallE (.const (.member 30 1) []) (.const (.member 30 0) [])), .safe⟩ + ] .safe, + .induct 0 0 0 (.sort (.succ .zero)) [ + ⟨0, 0, 0, (.const (.member 30 1) []), .safe⟩, + ⟨0, 0, 1, (.forallE (.const (.member 30 0) []) (.const (.member 30 1) [])), .safe⟩ + ] .safe + ]⟩ + +def permutedRecursors : Block Nat := ⟨[ + .recursor 1 0 0 2 3 (.forallE (.forallE (.const (.member 30 0) []) (.sort (.param 0))) (.forallE (.forallE (.const (.member 30 1) []) (.sort (.param 0))) (.forallE (.forallE (.const (.member 30 1) []) (.forallE (.app (.bvar 1) (.bvar 0)) (.app (.bvar 3) (.app (.const (.ctor 30 0 0) []) (.bvar 1))))) (.forallE (.app (.bvar 1) (.const (.ctor 30 1 0) [])) (.forallE (.forallE (.const (.member 30 0) []) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 1 1) []) (.bvar 1))))) (.forallE (.const (.member 30 0) []) (.app (.bvar 5) (.bvar 0)))))))) [ + ⟨1, (.lam (.forallE (.const (.member 30 0) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.forallE (.app (.bvar 1) (.bvar 0)) (.app (.bvar 3) (.app (.const (.ctor 30 0 0) []) (.bvar 1))))) (.lam (.app (.bvar 1) (.const (.ctor 30 1 0) [])) (.lam (.forallE (.const (.member 30 0) []) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 1 1) []) (.bvar 1))))) (.lam (.const (.member 30 1) []) (.app (.app (.bvar 3) (.bvar 0)) (.app (.app (.app (.app (.app (.app (.const (.member 31 1) [(.param 0)]) (.bvar 5)) (.bvar 4)) (.bvar 3)) (.bvar 2)) (.bvar 1)) (.bvar 0)))))))))⟩, + ⟨0, (.lam (.forallE (.const (.member 30 0) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.forallE (.app (.bvar 1) (.bvar 0)) (.app (.bvar 3) (.app (.const (.ctor 30 0 0) []) (.bvar 1))))) (.lam (.app (.bvar 1) (.const (.ctor 30 1 0) [])) (.lam (.forallE (.const (.member 30 0) []) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 1 1) []) (.bvar 1))))) (.bvar 1))))))⟩, + ⟨1, (.lam (.forallE (.const (.member 30 0) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.forallE (.app (.bvar 1) (.bvar 0)) (.app (.bvar 3) (.app (.const (.ctor 30 0 0) []) (.bvar 1))))) (.lam (.app (.bvar 1) (.const (.ctor 30 1 0) [])) (.lam (.forallE (.const (.member 30 0) []) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 1 1) []) (.bvar 1))))) (.lam (.const (.member 30 0) []) (.app (.app (.bvar 1) (.bvar 0)) (.app (.app (.app (.app (.app (.app (.const (.member 31 0) [(.param 0)]) (.bvar 5)) (.bvar 4)) (.bvar 3)) (.bvar 2)) (.bvar 1)) (.bvar 0)))))))))⟩ + ] false .safe, + .recursor 1 0 0 2 3 (.forallE (.forallE (.const (.member 30 0) []) (.sort (.param 0))) (.forallE (.forallE (.const (.member 30 1) []) (.sort (.param 0))) (.forallE (.forallE (.const (.member 30 1) []) (.forallE (.app (.bvar 1) (.bvar 0)) (.app (.bvar 3) (.app (.const (.ctor 30 0 0) []) (.bvar 1))))) (.forallE (.app (.bvar 1) (.const (.ctor 30 1 0) [])) (.forallE (.forallE (.const (.member 30 0) []) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 1 1) []) (.bvar 1))))) (.forallE (.const (.member 30 1) []) (.app (.bvar 4) (.bvar 0)))))))) [ + ⟨1, (.lam (.forallE (.const (.member 30 0) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.forallE (.app (.bvar 1) (.bvar 0)) (.app (.bvar 3) (.app (.const (.ctor 30 0 0) []) (.bvar 1))))) (.lam (.app (.bvar 1) (.const (.ctor 30 1 0) [])) (.lam (.forallE (.const (.member 30 0) []) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 1 1) []) (.bvar 1))))) (.lam (.const (.member 30 1) []) (.app (.app (.bvar 3) (.bvar 0)) (.app (.app (.app (.app (.app (.app (.const (.member 31 1) [(.param 0)]) (.bvar 5)) (.bvar 4)) (.bvar 3)) (.bvar 2)) (.bvar 1)) (.bvar 0)))))))))⟩, + ⟨0, (.lam (.forallE (.const (.member 30 0) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.forallE (.app (.bvar 1) (.bvar 0)) (.app (.bvar 3) (.app (.const (.ctor 30 0 0) []) (.bvar 1))))) (.lam (.app (.bvar 1) (.const (.ctor 30 1 0) [])) (.lam (.forallE (.const (.member 30 0) []) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 1 1) []) (.bvar 1))))) (.bvar 1))))))⟩, + ⟨1, (.lam (.forallE (.const (.member 30 0) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.sort (.param 0))) (.lam (.forallE (.const (.member 30 1) []) (.forallE (.app (.bvar 1) (.bvar 0)) (.app (.bvar 3) (.app (.const (.ctor 30 0 0) []) (.bvar 1))))) (.lam (.app (.bvar 1) (.const (.ctor 30 1 0) [])) (.lam (.forallE (.const (.member 30 0) []) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.const (.ctor 30 1 1) []) (.bvar 1))))) (.lam (.const (.member 30 0) []) (.app (.app (.bvar 1) (.bvar 0)) (.app (.app (.app (.app (.app (.app (.const (.member 31 0) [(.param 0)]) (.bvar 5)) (.bvar 4)) (.bvar 3)) (.bvar 2)) (.bvar 1)) (.bvar 0)))))))))⟩ + ] false .safe + ]⟩ + +def nestedRecursors : Block Nat := ⟨[ + .recursor 2 1 0 2 3 (.forallE (.sort (.succ (.param 1))) (.forallE (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.forallE (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 1)) (.sort (.param 0))) (.forallE (.forallE (.bvar 2) (.app (.bvar 2) (.app (.app (.const (.ctor 40 0 0) [(.param 1)]) (.bvar 3)) (.bvar 0)))) (.forallE (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 3)) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.app (.const (.ctor 40 0 1) [(.param 1)]) (.bvar 5)) (.bvar 1))))) (.forallE (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 4)) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.ctor 20 0 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 6)) (.bvar 1))))) (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 5)) (.app (.bvar 5) (.bvar 0))))))))) [ + ⟨1, (.lam (.sort (.succ (.param 1))) (.lam (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.lam (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 1)) (.sort (.param 0))) (.lam (.forallE (.bvar 2) (.app (.bvar 2) (.app (.app (.const (.ctor 40 0 0) [(.param 1)]) (.bvar 3)) (.bvar 0)))) (.lam (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 3)) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.app (.const (.ctor 40 0 1) [(.param 1)]) (.bvar 5)) (.bvar 1))))) (.lam (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 4)) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.ctor 20 0 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 6)) (.bvar 1))))) (.lam (.bvar 5) (.app (.bvar 3) (.bvar 0)))))))))⟩, + ⟨1, (.lam (.sort (.succ (.param 1))) (.lam (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.lam (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 1)) (.sort (.param 0))) (.lam (.forallE (.bvar 2) (.app (.bvar 2) (.app (.app (.const (.ctor 40 0 0) [(.param 1)]) (.bvar 3)) (.bvar 0)))) (.lam (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 3)) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.app (.const (.ctor 40 0 1) [(.param 1)]) (.bvar 5)) (.bvar 1))))) (.lam (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 4)) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.ctor 20 0 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 6)) (.bvar 1))))) (.lam (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 5)) (.app (.app (.bvar 2) (.bvar 0)) (.app (.app (.app (.app (.app (.app (.app (.const (.member 41 1) [(.param 0), (.param 1)]) (.bvar 6)) (.bvar 5)) (.bvar 4)) (.bvar 3)) (.bvar 2)) (.bvar 1)) (.bvar 0))))))))))⟩, + ⟨1, (.lam (.sort (.succ (.param 1))) (.lam (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.lam (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 1)) (.sort (.param 0))) (.lam (.forallE (.bvar 2) (.app (.bvar 2) (.app (.app (.const (.ctor 40 0 0) [(.param 1)]) (.bvar 3)) (.bvar 0)))) (.lam (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 3)) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.app (.const (.ctor 40 0 1) [(.param 1)]) (.bvar 5)) (.bvar 1))))) (.lam (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 4)) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.ctor 20 0 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 6)) (.bvar 1))))) (.lam (.app (.const (.member 40 0) [(.param 1)]) (.bvar 5)) (.app (.app (.bvar 1) (.bvar 0)) (.app (.app (.app (.app (.app (.app (.app (.const (.member 41 0) [(.param 0), (.param 1)]) (.bvar 6)) (.bvar 5)) (.bvar 4)) (.bvar 3)) (.bvar 2)) (.bvar 1)) (.bvar 0))))))))))⟩ + ] false .safe, + .recursor 2 1 0 2 3 (.forallE (.sort (.succ (.param 1))) (.forallE (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.forallE (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 1)) (.sort (.param 0))) (.forallE (.forallE (.bvar 2) (.app (.bvar 2) (.app (.app (.const (.ctor 40 0 0) [(.param 1)]) (.bvar 3)) (.bvar 0)))) (.forallE (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 3)) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.app (.const (.ctor 40 0 1) [(.param 1)]) (.bvar 5)) (.bvar 1))))) (.forallE (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 4)) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.ctor 20 0 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 6)) (.bvar 1))))) (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 5)) (.app (.bvar 4) (.bvar 0))))))))) [ + ⟨1, (.lam (.sort (.succ (.param 1))) (.lam (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.lam (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 1)) (.sort (.param 0))) (.lam (.forallE (.bvar 2) (.app (.bvar 2) (.app (.app (.const (.ctor 40 0 0) [(.param 1)]) (.bvar 3)) (.bvar 0)))) (.lam (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 3)) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.app (.const (.ctor 40 0 1) [(.param 1)]) (.bvar 5)) (.bvar 1))))) (.lam (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 4)) (.forallE (.app (.bvar 4) (.bvar 0)) (.app (.bvar 4) (.app (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.ctor 20 0 0) [(.param 1)]) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)))) (.bvar 6)) (.bvar 1))))) (.lam (.app (.const (.member 40 0) [(.param 1)]) (.bvar 5)) (.app (.app (.bvar 1) (.bvar 0)) (.app (.app (.app (.app (.app (.app (.app (.const (.member 41 0) [(.param 0), (.param 1)]) (.bvar 6)) (.bvar 5)) (.bvar 4)) (.bvar 3)) (.bvar 2)) (.bvar 1)) (.bvar 0))))))))))⟩ + ] false .safe + ]⟩ + +def nestedPiRecursors : Block Nat := ⟨[ + .recursor 2 1 0 2 3 (.forallE (.sort (.succ (.param 1))) (.forallE (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.forallE (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 1)) (.sort (.param 0))) (.forallE (.forallE (.bvar 2) (.app (.bvar 2) (.app (.app (.const (.ctor 40 0 0) [(.param 1)]) (.bvar 3)) (.bvar 0)))) (.forallE (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 3)) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.app (.const (.ctor 40 0 1) [(.param 1)]) (.bvar 5)) (.bvar 1))))) (.forallE (.forallE (.forallE (.bvar 4) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 5))) (.forallE (.forallE (.bvar 5) (.app (.bvar 5) (.app (.bvar 1) (.bvar 0)))) (.app (.bvar 4) (.app (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.ctor 20 0 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 6)) (.bvar 1))))) (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 5)) (.app (.bvar 5) (.bvar 0))))))))) [ + ⟨1, (.lam (.sort (.succ (.param 1))) (.lam (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.lam (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 1)) (.sort (.param 0))) (.lam (.forallE (.bvar 2) (.app (.bvar 2) (.app (.app (.const (.ctor 40 0 0) [(.param 1)]) (.bvar 3)) (.bvar 0)))) (.lam (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 3)) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.app (.const (.ctor 40 0 1) [(.param 1)]) (.bvar 5)) (.bvar 1))))) (.lam (.forallE (.forallE (.bvar 4) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 5))) (.forallE (.forallE (.bvar 5) (.app (.bvar 5) (.app (.bvar 1) (.bvar 0)))) (.app (.bvar 4) (.app (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.ctor 20 0 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 6)) (.bvar 1))))) (.lam (.bvar 5) (.app (.bvar 3) (.bvar 0)))))))))⟩, + ⟨1, (.lam (.sort (.succ (.param 1))) (.lam (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.lam (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 1)) (.sort (.param 0))) (.lam (.forallE (.bvar 2) (.app (.bvar 2) (.app (.app (.const (.ctor 40 0 0) [(.param 1)]) (.bvar 3)) (.bvar 0)))) (.lam (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 3)) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.app (.const (.ctor 40 0 1) [(.param 1)]) (.bvar 5)) (.bvar 1))))) (.lam (.forallE (.forallE (.bvar 4) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 5))) (.forallE (.forallE (.bvar 5) (.app (.bvar 5) (.app (.bvar 1) (.bvar 0)))) (.app (.bvar 4) (.app (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.ctor 20 0 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 6)) (.bvar 1))))) (.lam (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 5)) (.app (.app (.bvar 2) (.bvar 0)) (.app (.app (.app (.app (.app (.app (.app (.const (.member 41 1) [(.param 0), (.param 1)]) (.bvar 6)) (.bvar 5)) (.bvar 4)) (.bvar 3)) (.bvar 2)) (.bvar 1)) (.bvar 0))))))))))⟩, + ⟨1, (.lam (.sort (.succ (.param 1))) (.lam (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.lam (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 1)) (.sort (.param 0))) (.lam (.forallE (.bvar 2) (.app (.bvar 2) (.app (.app (.const (.ctor 40 0 0) [(.param 1)]) (.bvar 3)) (.bvar 0)))) (.lam (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 3)) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.app (.const (.ctor 40 0 1) [(.param 1)]) (.bvar 5)) (.bvar 1))))) (.lam (.forallE (.forallE (.bvar 4) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 5))) (.forallE (.forallE (.bvar 5) (.app (.bvar 5) (.app (.bvar 1) (.bvar 0)))) (.app (.bvar 4) (.app (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.ctor 20 0 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 6)) (.bvar 1))))) (.lam (.forallE (.bvar 5) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 6))) (.app (.app (.bvar 1) (.bvar 0)) (.lam (.bvar 6) (.app (.app (.app (.app (.app (.app (.app (.const (.member 41 0) [(.param 0), (.param 1)]) (.bvar 7)) (.bvar 6)) (.bvar 5)) (.bvar 4)) (.bvar 3)) (.bvar 2)) (.app (.bvar 1) (.bvar 0))))))))))))⟩ + ] false .safe, + .recursor 2 1 0 2 3 (.forallE (.sort (.succ (.param 1))) (.forallE (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.forallE (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 1)) (.sort (.param 0))) (.forallE (.forallE (.bvar 2) (.app (.bvar 2) (.app (.app (.const (.ctor 40 0 0) [(.param 1)]) (.bvar 3)) (.bvar 0)))) (.forallE (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 3)) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.app (.const (.ctor 40 0 1) [(.param 1)]) (.bvar 5)) (.bvar 1))))) (.forallE (.forallE (.forallE (.bvar 4) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 5))) (.forallE (.forallE (.bvar 5) (.app (.bvar 5) (.app (.bvar 1) (.bvar 0)))) (.app (.bvar 4) (.app (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.ctor 20 0 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 6)) (.bvar 1))))) (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 5)) (.app (.bvar 4) (.bvar 0))))))))) [ + ⟨1, (.lam (.sort (.succ (.param 1))) (.lam (.forallE (.app (.const (.member 40 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.lam (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 1)) (.sort (.param 0))) (.lam (.forallE (.bvar 2) (.app (.bvar 2) (.app (.app (.const (.ctor 40 0 0) [(.param 1)]) (.bvar 3)) (.bvar 0)))) (.lam (.forallE (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.member 20 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 3)) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 4) (.app (.app (.const (.ctor 40 0 1) [(.param 1)]) (.bvar 5)) (.bvar 1))))) (.lam (.forallE (.forallE (.bvar 4) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 5))) (.forallE (.forallE (.bvar 5) (.app (.bvar 5) (.app (.bvar 1) (.bvar 0)))) (.app (.bvar 4) (.app (.app (.lam (.sort (.succ (.param 1))) (.app (.const (.ctor 20 0 0) [(.param 1)]) (.forallE (.bvar 0) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 1))))) (.bvar 6)) (.bvar 1))))) (.lam (.forallE (.bvar 5) (.app (.const (.member 40 0) [(.param 1)]) (.bvar 6))) (.app (.app (.bvar 1) (.bvar 0)) (.lam (.bvar 6) (.app (.app (.app (.app (.app (.app (.app (.const (.member 41 0) [(.param 0), (.param 1)]) (.bvar 7)) (.bvar 6)) (.bvar 5)) (.bvar 4)) (.bvar 3)) (.bvar 2)) (.app (.bvar 1) (.bvar 0))))))))))))⟩ + ] false .safe + ]⟩ + +end Tests.Theory.ModeledFixtures diff --git a/Tests/Theory/ModeledNested.lean b/Tests/Theory/ModeledNested.lean new file mode 100644 index 000000000..54bd19390 --- /dev/null +++ b/Tests/Theory/ModeledNested.lean @@ -0,0 +1,177 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Theory.Modeled + +open Ix.Theory + +namespace Tests.Theory.ModeledNested + +open Ix.Theory.Model Ix.Theory.Certified Ix.Theory.Certificate Ix.Theory.Inductive +open Tests.Theory.Certified (primitives) +open Tests.Theory.Modeled (ref const) + +set_option maxRecDepth 16384 +set_option maxHeartbeats 32000000 + +def boxShape : Ix.Theory.Certified.Ordinary.Shape Nat := + ⟨1, [.sort (.succ (.param 0))], [], .succ (.param 0), [⟨[.bvar 0], [], []⟩]⟩ + +def unaryShape (recursivePi : Bool) : Ix.Theory.Certified.Ordinary.Shape Nat := + ⟨1, [.sort (.succ (.param 0))], [], .succ (.param 0), + [⟨[.bvar 0], [], []⟩, ⟨[], [⟨if recursivePi then [.bvar 0] else [], []⟩], []⟩]⟩ + +def tree (level : VLevel) (parameter : VExpr Nat) : VExpr Nat := .app (const 40 0 [level]) parameter +def box (level : VLevel) (parameter : VExpr Nat) : VExpr Nat := .app (const 20 0 [level]) parameter +def unary (level : VLevel) (parameter : VExpr Nat) : VExpr Nat := .app (const 30 0 [level]) parameter +def branch (recursivePi : Bool) (parameter family : VExpr Nat) : VExpr Nat := + if recursivePi then .forallE parameter (family.liftN 1) else family + +def boxMk (level : VLevel) (parameter value : VExpr Nat) : VExpr Nat := + .appN (.const (.ctor 20 0 0) [level]) [parameter, value] + +/-- Tree α = leaf α | node (Box (Tree α)), or node (Box (α → Tree α)). +Both are parameterized nested occurrences with a restored Box recursor. -/ +def treeSource (recursivePi : Bool) : Const Nat := .induct 1 1 0 + (.forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))) [ + ⟨1, 1, 1, .forallE (.sort (.succ (.param 0))) + (.forallE (.bvar 0) (tree (.param 0) (.bvar 1))), .safe⟩, + ⟨1, 1, 1, .forallE (.sort (.succ (.param 0))) + (.forallE (box (.param 0) (branch recursivePi (.bvar 0) (tree (.param 0) (.bvar 0)))) (tree (.param 0) (.bvar 1))), .safe⟩] .safe + +def sourceBlock (recursivePi : Bool) : Nat → Block Nat + | 10 => ⟨[PrimitiveSignature.falseDeclaration]⟩ + | 11 => ⟨[primitives.falseElimDeclaration]⟩ + | 20 => ⟨[boxShape.source 20]⟩ + | 21 => ⟨[boxShape.recursorSource 20 21 .large]⟩ + | 30 => ⟨[(unaryShape recursivePi).source 30]⟩ + | 31 => ⟨[(unaryShape recursivePi).recursorSource 30 31 .large]⟩ + | 40 => ⟨[treeSource recursivePi]⟩ + | _ => ⟨[]⟩ + +def storeFor (blocks : Nat → Block Nat) : Store Nat where + dom := List.range 53 + nodup := List.nodup_range + blocks b := if b < 53 then some (blocks b) else none + mem_dom b := by split <;> simp_all + +def sourceStore (recursivePi : Bool) : Store Nat := storeFor (sourceBlock recursivePi) +def treeRecursors (recursivePi : Bool) : Block Nat := + if recursivePi then ModeledFixtures.nestedPiRecursors else ModeledFixtures.nestedRecursors + +def modelTargets : List (ConstRef Nat) := [ref 30, .ctor 30 0 0, ref 50, ref 51, ref 52] +def modelPairs : List (ConstRef Nat × ConstRef Nat) := + [ref 40, .ctor 40 0 0, .ctor 40 0 1, ref 41, ref 41 1].zip modelTargets +def mapped (expression : VExpr Nat) : VExpr Nat := expression.mapRefs (Certificate.Modeled.target modelPairs) +def recursorType (recursivePi : Bool) (index : Nat) : VExpr Nat := + mapped (((treeRecursors recursivePi).members[index]?).map Const.type |>.getD (.sort .zero)) +def recPrefix (recursivePi : Bool) : List (VExpr Nat) := (recursorType recursivePi 0).telN 6 + +def nodeType (recursivePi : Bool) : VExpr Nat := + match treeSource recursivePi with + | .induct _ _ _ _ ctors _ => mapped ((ctors[1]?).map Ctor.type |>.getD (.sort .zero)) + | _ => .sort .zero +def nodeBody (recursivePi : Bool) : VExpr Nat := .lam (.sort (.succ (.param 0))) + (.lam (box (.param 0) (branch recursivePi (.bvar 0) (unary (.param 0) (.bvar 0)))) + (.appN (.const (.ctor 30 0 1) [.param 0]) [.bvar 1, .proj (ref 20) 0 (.bvar 0)])) + +/-- Ordinary unary recursion supplies both nested calls. The constructor +and auxiliary recursor use Box's independently checked projection and eta. -/ +def treeModel (recursivePi : Bool) : VExpr Nat := .lamN (recPrefix recursivePi) + (.appN (const 31 0 [.param 0, .param 1]) [ + .bvar 5, .bvar 4, .bvar 2, + .lam (branch recursivePi (.bvar 5) (unary (.param 1) (.bvar 5))) + (.lam (if recursivePi then .forallE (.bvar 6) (.app (.bvar 6) (.app (.bvar 1) (.bvar 0))) + else .app (.bvar 5) (.bvar 0)) + (.appN (.bvar 3) [boxMk (.param 1) (branch recursivePi (.bvar 7) (unary (.param 1) (.bvar 7))) (.bvar 1), + .appN (.bvar 2) [.bvar 1, .bvar 0]]))]) + +def boxModel (recursivePi : Bool) : VExpr Nat := .lamN (recPrefix recursivePi) + (.appN (const 21 0 [.param 0, .param 1]) [ + branch recursivePi (.bvar 5) (unary (.param 1) (.bvar 5)), .bvar 3, + .lam (branch recursivePi (.bvar 5) (unary (.param 1) (.bvar 5))) + (.appN (.bvar 1) [.bvar 0, + if recursivePi then .lam (.bvar 6) + (.appN (const 51 0 [.param 0, .param 1]) (VExpr.bvarRevRange 2 6 ++ [.app (.bvar 1) (.bvar 0)])) + else .appN (const 51 0 [.param 0, .param 1]) (VExpr.bvarRevRange 1 6 ++ [.bvar 0])])]) + +def block (recursivePi : Bool) : Nat → Block Nat + | 41 => treeRecursors recursivePi + | 50 => ⟨[.defn 1 .definition (nodeType recursivePi) (nodeBody recursivePi) .safe]⟩ + | 51 => ⟨[.defn 2 .definition (recursorType recursivePi 0) (treeModel recursivePi) .safe]⟩ + | 52 => ⟨[.defn 2 .definition (recursorType recursivePi 1) (boxModel recursivePi) .safe]⟩ + | n => sourceBlock recursivePi n +def store (recursivePi : Bool) : Store Nat := storeFor (block recursivePi) +def candidate : Certificate.Modeled.Candidate Nat := ⟨40, [ref 41, ref 41 1], modelTargets, []⟩ + +def prefixWitnesses? (recursivePi : Bool) : Option (Environment Nat × List (DeclarationWitness Nat)) := + declarationWitnesses? 4000 (store recursivePi) primitives.environment + [.ordinary 20 21, .ordinary 30 31, .definition (ref 50), .definition (ref 51), .definition (ref 52)] +def modeledWitness? (recursivePi : Bool) : Option (Ix.Theory.Certified.Modeled.Witness Nat) := do + let (entries, _) ← prefixWitnesses? recursivePi + candidate.witness? 4000 entries (store recursivePi) +def generated? (recursivePi : Bool) : Option (List (DeclarationWitness Nat)) := + Certificate.storeWitness? 4000 primitives (store recursivePi) [ref 41, ref 41 1] [candidate] +def accepted (recursivePi : Bool) : Bool := (generated? recursivePi).any + (acceptsStoreCertified.{0,0} 4000 primitives (store recursivePi) [ref 40, ref 41, ref 41 1]) + +#guard [false, true].all fun recursivePi => (treeRecursors recursivePi).members.length == 2 +#guard [false, true].all fun recursivePi => + (Ix.Theory.Certified.Modeled.sourceRefs? (store recursivePi) 40 [ref 41, ref 41 1]).isSome +#guard [false, true].all fun recursivePi => + (Ix.Theory.Certified.Modeled.recursorRules? (store recursivePi) (ref 41)).any (fun rules => rules.length == 2) +#guard [false, true].all fun recursivePi => + (Ix.Theory.Certified.Modeled.recursorRules? (store recursivePi) (ref 41 1)).any (fun rules => rules.length == 1) +#guard [false, true].all fun recursivePi => (prefixWitnesses? recursivePi).isSome +#guard [false, true].all fun recursivePi => (modeledWitness? recursivePi).isSome +#guard [false, true].all accepted + +def computed (recursivePi : Bool) : VExpr Nat := + let treeType := tree .zero (.sort .zero) + let fieldType := branch recursivePi (.sort .zero) treeType + let boxType := box .zero fieldType + let leaf := fun value => VExpr.appN (.const (.ctor 40 0 0) [.zero]) [.sort .zero, value] + let field := if recursivePi then .lam (.sort .zero) (leaf (.bvar 0)) else leaf (.bvar 1) + .appN (const 41 0 [.succ .zero, .zero]) [ + .sort .zero, + .lam treeType (.sort .zero), .lam boxType (.sort .zero), + .lam (.sort .zero) (.bvar 0), + .lam boxType (.lam (.sort .zero) (.bvar 0)), + .lam fieldType (.lam + (if recursivePi then .forallE (.sort .zero) (.sort .zero) else .sort .zero) + (if recursivePi then .app (.bvar 0) (.bvar 3) else .bvar 0)), + .appN (.const (.ctor 40 0 1) [.zero]) [.sort .zero, boxMk .zero fieldType field]] + +def input (recursivePi : Bool) : ProofInput Nat := + ⟨store recursivePi, 0, Tests.Theory.Modeled.proof, + .forallE (.sort .zero) (.forallE (.bvar 0) (computed recursivePi))⟩ +def proofAccepted (recursivePi : Bool) : Bool := + (Certificate.proofWitness? 4000 primitives (input recursivePi) [candidate]).any + (acceptsCertified.{0,0} 4000 primitives (input recursivePi)) + +#guard [false, true].all proofAccepted + +def tampered (recursivePi : Bool) (alter : Const Nat → Const Nat) : Store Nat := + storeFor fun n => if n = 41 then ⟨(treeRecursors recursivePi).members.map alter⟩ else block recursivePi n + +def rejected (recursivePi : Bool) (source : Store Nat) : Bool := (generated? recursivePi).any fun witnesses => + !(acceptsStoreCertified.{0,0} 4000 primitives source [ref 41, ref 41 1] witnesses) + +#guard [false, true].all fun recursivePi => rejected recursivePi (tampered recursivePi fun c => match c with + | .recursor u p i m n t rs k s => .recursor u p i m n (t.instL [.param 1, .param 0]) rs k s + | c => c) +#guard [false, true].all fun recursivePi => rejected recursivePi (tampered recursivePi fun c => match c with + | .recursor u p i m n t rs k s => .recursor u p i m n t (rs.map fun r => { r with nfields := 0 }) k s + | c => c) + +-- Swapping the restored auxiliary with its root, or substituting its model +-- for the root's model, cannot preserve the actual selected rule statements. +#guard [false, true].all fun recursivePi => (prefixWitnesses? recursivePi).any fun (entries, _) => + (Certificate.Modeled.witness? 4000 entries (store recursivePi) 40 [ref 41 1, ref 41] modelTargets).isNone +#guard [false, true].all fun recursivePi => (prefixWitnesses? recursivePi).any fun (entries, _) => + (Certificate.Modeled.witness? 4000 entries (store recursivePi) 40 [ref 41, ref 41 1] + [ref 30, .ctor 30 0 0, ref 50, ref 52, ref 51]).isNone + +end Tests.Theory.ModeledNested diff --git a/Tests/Theory/ModeledPermutation.lean b/Tests/Theory/ModeledPermutation.lean new file mode 100644 index 000000000..8b1eeeaff --- /dev/null +++ b/Tests/Theory/ModeledPermutation.lean @@ -0,0 +1,105 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Theory.Modeled + +open Ix.Theory + +namespace Tests.Theory.ModeledPermutation + +open Ix.Theory.Model Ix.Theory.Certified Ix.Theory.Certificate Ix.Theory.Inductive +open Tests.Theory.Certified (primitives) +open Tests.Theory.Modeled (ref const) + +set_option maxRecDepth 16384 +set_option maxHeartbeats 32000000 + +def swap : ConstRef Nat → ConstRef Nat + | .member 30 0 => .member 30 1 + | .member 30 1 => .member 30 0 + | .ctor 30 0 c => .ctor 30 1 c + | .ctor 30 1 c => .ctor 30 0 c + | .member 31 0 => .member 31 1 + | .member 31 1 => .member 31 0 + | ref => ref + +def source : Block Nat := ModeledFixtures.permutedSource +def recursors : Block Nat := ModeledFixtures.permutedRecursors +def targets : List (ConstRef Nat) := + [ref 20, ref 40, ref 20, .ctor 20 0 0, .ctor 20 0 1, ref 43, ref 44] +def pairs : List (ConstRef Nat × ConstRef Nat) := + [ref 30, .ctor 30 0 0, ref 30 1, .ctor 30 1 0, .ctor 30 1 1, ref 31, ref 31 1].zip targets +def type (index : Nat) : VExpr Nat := + (((recursors.members[index]?).map Const.type).getD (.sort .zero)).mapRefs (Certificate.Modeled.target pairs) + +/-- Reorder the complete motive/minor telescope when wrapping the earlier +models. Merely permuting addresses or constructor counts would not typecheck. -/ +def body (index : Nat) : VExpr Nat := .lamN ((type index).telN 5) + (.appN (const (if index = 0 then 42 else 41) 0 [.param 0]) [.bvar 3, .bvar 4, .bvar 1, .bvar 0, .bvar 2]) +def block : Nat → Block Nat + | 30 => source + | 31 => recursors + | 43 => ⟨[.defn 1 .definition (type 0) (body 0) .safe]⟩ + | 44 => ⟨[.defn 1 .definition (type 1) (body 1) .safe]⟩ + | n => Tests.Theory.Modeled.block n +def store : Store Nat where + dom := List.range 45 + nodup := List.nodup_range + blocks b := if b < 45 then some (block b) else none + mem_dom b := by split <;> simp_all +def candidate : Certificate.Modeled.Candidate Nat := ⟨30, [ref 31, ref 31 1], targets, []⟩ +def witnesses? := Certificate.storeWitness? 4000 primitives store [ref 31, ref 31 1] [candidate] +def accepted : Bool := witnesses?.any + (acceptsStoreCertified.{0,0} 4000 primitives store [ref 30, ref 30 1, ref 31, ref 31 1]) + +#guard store.ctorRuleIndex? (.ctor 30 1 0) == some 1 +#guard store.ctorRuleIndex? (.ctor 30 1 1) == some 2 +#guard accepted +#guard (Certificate.storeWitness? 4000 primitives store [ref 31] + [{ candidate with models := Tests.Theory.Modeled.modelTargets }]).isNone + +def computed : VExpr Nat := .appN (const 31 1 [.succ .zero]) [ + .lam (const 30) (.sort .zero), .lam (const 30 1) (.sort .zero), + .lam (const 30 1) (.lam (.sort .zero) (.bvar 0)), .bvar 1, + .lam (const 30) (.lam (.sort .zero) (.bvar 0)), + .app (.const (.ctor 30 1 1) []) + (.app (.const (.ctor 30 0 0) []) (.const (.ctor 30 1 0) []))] +def input : ProofInput Nat := ⟨store, 0, Tests.Theory.Modeled.proof, + .forallE (.sort .zero) (.forallE (.bvar 0) computed)⟩ +def proofAccepted : Bool := (Certificate.proofWitness? 4000 primitives input [candidate]).any + (acceptsCertified.{0,0} 4000 primitives input) +#guard proofAccepted + +/-- Two fresh source auxiliary recursors may share one already checked model. +Their complete types and own rule statements still pass independent checks. -/ +def mergedBlock : Nat → Block Nat + | 45 => ⟨(Tests.Theory.Modeled.mutualRecursors.members[1]?).toList⟩ + | n => Tests.Theory.Modeled.block n +def mergedStore : Store Nat where + dom := List.range 46 + nodup := List.nodup_range + blocks b := if b < 46 then some (mergedBlock b) else none + mem_dom b := by split <;> simp_all +def mergedCandidate : Certificate.Modeled.Candidate Nat := + ⟨30, [ref 31, ref 31 1, ref 45], Tests.Theory.Modeled.modelTargets ++ [ref 42], []⟩ +def mergedWitnesses? := Certificate.storeWitness? 4000 primitives mergedStore [ref 31 1, ref 45] [mergedCandidate] +def mergedAccepted : Bool := mergedWitnesses?.any + (acceptsStoreCertified.{0,0} 4000 primitives mergedStore [ref 31 1, ref 45]) +#guard mergedAccepted +#guard (Certificate.storeWitness? 4000 primitives mergedStore [ref 45] + [{ mergedCandidate with models := Tests.Theory.Modeled.modelTargets ++ [ref 41] }]).isNone + +def mergedComputed : VExpr Nat := .appN (const 45 0 [.succ .zero]) [ + .lam (const 30) (.sort .zero), .lam (const 30 1) (.sort .zero), .bvar 1, + .lam (const 30 1) (.lam (.sort .zero) (.bvar 0)), + .lam (const 30) (.lam (.sort .zero) (.bvar 0)), + .app (.const (.ctor 30 1 0) []) (.const (.ctor 30 0 0) [])] +def mergedInput : ProofInput Nat := ⟨mergedStore, 0, Tests.Theory.Modeled.proof, + .forallE (.sort .zero) (.forallE (.bvar 0) mergedComputed)⟩ +def mergedProofAccepted : Bool := (Certificate.proofWitness? 4000 primitives mergedInput [mergedCandidate]).any + (acceptsCertified.{0,0} 4000 primitives mergedInput) +#guard mergedProofAccepted + +end Tests.Theory.ModeledPermutation diff --git a/Tests/Theory/NamedManifest.lean b/Tests/Theory/NamedManifest.lean new file mode 100644 index 000000000..6bdf540cd --- /dev/null +++ b/Tests/Theory/NamedManifest.lean @@ -0,0 +1,123 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +/-! Retained named-specification source inventory. Original modules, +revision and attribution are recorded in `Ix/Theory/Named/NOTICE`. +Hashes identify the original inputs, before Ix integration. -/ + +namespace Tests.Theory.NamedManifest + +structure SourceFile where + target : String + sourceSha256 : String + +def selected : Array SourceFile := #[ + ⟨"Ix/Theory/Named/Reference/Declaration.lean", "cefbf66e2134bec472aa9fd643122a3052bbbfd12ab04e6dbd85fe21e32fb0fa"⟩, + ⟨"Ix/Theory/Named/Reference/Environment.lean", "8de5b7407e225c96ed4116bc46e2e3bad5009b6bfb0cb4f68d4ce2799eff4f11"⟩, + ⟨"Ix/Theory/Named/Reference/Environment/Basic.lean", "cc39f3a7ae819fed332407c01239dc604a49c8a5dffb379564aade970ff1bcf9"⟩, + ⟨"Ix/Theory/Named/Reference/Expr.lean", "6cb6f0e5df484973fc282bb86968bb51a1c73ec32d726010b1d7b2133229ac81"⟩, + ⟨"Ix/Theory/Named/Reference/ForEachExprV.lean", "260167ef5e882a5bd2482734dd72749b809857dbe99bc2aa32e28c6b4b2fb37e"⟩, + ⟨"Ix/Theory/Named/Reference/FuelConfig.lean", "3ab65df013711059ef867c2683a7516b77cea87a526cf47c1772e9c8a529d35a"⟩, + ⟨"Ix/Theory/Named/Reference/Inductive/Add.lean", "17dda89d60cac89cba4fc9a4db8233eee8199ce7aee4de337bcd677d076eceb6"⟩, + ⟨"Ix/Theory/Named/Reference/Inductive/EliminationTrace.lean", "d14f7bc2aeb2037701ac12d0217f405b94812fdc441942458656bb543627d095"⟩, + ⟨"Ix/Theory/Named/Reference/Inductive/Reduce.lean", "c6daf4dcec4ffc6c27a64e1054500ec025646d6d15c7c2d61c1323e4928dcc17"⟩, + ⟨"Ix/Theory/Named/Reference/Inductive/ValidationTrace.lean", "7b4e36920a1286b319237a4db632acfac09e203cb3699835fa7440e31f52bbbd"⟩, + ⟨"Ix/Theory/Named/Reference/Instantiate.lean", "ae08b31657faf3b54de22688ae287d0bff362c573d6a2dd09f12f417b1975ec2"⟩, + ⟨"Ix/Theory/Named/Reference/Level.lean", "75932c6c1d406cbbf75e60c56b75e4a9b33f743651f5aa50814c7c369edbed76"⟩, + ⟨"Ix/Theory/Named/Reference/List.lean", "4ddde2232aff649a50a8d1bec44e9495e2387dd3fcc29833d4f9c67c514431a4"⟩, + ⟨"Ix/Theory/Named/Reference/LocalContext.lean", "5baf7b6b379cbbd3902578c5da9d454558031b52f2af508481bc4647c0d91a1b"⟩, + ⟨"Ix/Theory/Named/Reference/Primitive.lean", "a8407d6fdb55c15225c5535b89a1c647af7b20729adb810b9887372b1a9ea745"⟩, + ⟨"Ix/Theory/Named/Reference/PtrEq.lean", "395e6b6907e548582d9e40e37f5344d6919e597500608c597048e621f27f0aa2"⟩, + ⟨"Ix/Theory/Named/Reference/Quot.lean", "79c51dae8b2ac0e09537aaabfb32075fe4a9d5dd34a1a78d47a88229a74926c5"⟩, + ⟨"Ix/Theory/Named/Std/Basic.lean", "885fd60674f7ceec447d20b56f34a2945dccc9d1193041d552162b7220ba6d67"⟩, + ⟨"Ix/Theory/Named/Std/Control.lean", "71dd61bc6bb315f1590ae79e276ada1c0c5c8eb31e67b0f994ed29f92b05fcc2"⟩, + ⟨"Ix/Theory/Named/Std/HashMap.lean", "67afa618c36314613b553eb95dafbbf7c5cca01b9e7d8749b4c024de966c37db"⟩, + ⟨"Ix/Theory/Named/Std/NodupKeys.lean", "b0a5f079a7f034f0c436de4ca120fd07197775cba26c625d2a2e1c8054ef460e"⟩, + ⟨"Ix/Theory/Named/Std/Ord.lean", "26380558d7e4fd742274d4f4d9396eb2d10feaa0ad945cfd57c0a37b12a04fa4"⟩, + ⟨"Ix/Theory/Named/Std/PersistentHashMap.lean", "c619b8962018a8b4fe3afb9fb0ee7c77bb92e75c323fc3f849ed4d8c199a4c59"⟩, + ⟨"Ix/Theory/Named/Std/SMap.lean", "574b0996076ccb5814ba381f13f288259fbb821abd33c20f9b891bac6c1e1fb0"⟩, + ⟨"Ix/Theory/Named/Std/ToExpr.lean", "5068adbd3d6c0b2d33ec84420efd66e8025dad2b6ed7171b7d10d1453db08e82"⟩, + ⟨"Ix/Theory/Named/Std/VariableBang.lean", "783456566db97120957903fc5beccf549507ed8e1f0fcd76cde4d0175f02a012"⟩, + ⟨"Ix/Theory/Named/Fixtures/ProjectionExpressibility.lean", "ee30b121ece1f5ea7ca2bb315b8e02557b75b5ef33a9986edb12b7b7dab76de0"⟩, + ⟨"Ix/Theory/Named/ConstructorValidityFixtures.lean", "4743314f7fd0e0323463cc7fd403a68792c5e0dabbdbcaaba9e1289136527cde"⟩, + ⟨"Ix/Theory/Named/Inductive.lean", "5cf6f5c19b301396b35d141a90b6b0281ae984e0e64c502d657d6dc15fb5dc57"⟩, + ⟨"Ix/Theory/Named/InductiveFixtures.lean", "4e663484b880adc47045c4d3760456e3c9192951ff9b0f188eea34eff5adcc55"⟩, + ⟨"Ix/Theory/Named/Literals.lean", "cfb64f593c6e3643d83d479d29129d0e73522327529143ef41fd62e162d6998a"⟩, + ⟨"Ix/Theory/Named/LocalContext.lean", "e31fe96e8ec2e2718c65bd2eb8a4b9c01286d54956d1a4a23ef47b83706c7d1f"⟩, + ⟨"Ix/Theory/Named/Meta.lean", "37f47b54a8b855ee7f0c4f2a322fb3c7ad319d1749861c386d9b966142f2c8b7"⟩, + ⟨"Ix/Theory/Named/MutualInductiveFixtures.lean", "907e4cc7a7855eec5b32bf22457baf0eb482b15529e56d8eb2f2369eaaf79414"⟩, + ⟨"Ix/Theory/Named/NestedInductive.lean", "c88720c864cd7a4c62d44b116779c7ddb93aacd2b9b51c2fea87e4c1afeddfaf"⟩, + ⟨"Ix/Theory/Named/NestedInductiveFixtures.lean", "2b31e2f7691a631007a5a3e4c83227515476c13d812c391e7ab111ceba4b8d99"⟩, + ⟨"Ix/Theory/Named/Projection.lean", "9f5108d2192b951a2b8ee05f102fe03bea5c40563f4f10fa27b74c34853884e6"⟩, + ⟨"Ix/Theory/Named/Quot.lean", "3742bc54150fb1b410d5807cd64355cc169fd6ea809dbb71662dc9d28a64c617"⟩, + ⟨"Ix/Theory/Named/SingletonParity.lean", "018c3ea29f04d2274bd47362ed09d959ab3873abf59f0ad23c79afb03f37eb52"⟩, + ⟨"Ix/Theory/Named/Typing/Basic.lean", "24827047806ee6ffb9af8ed457143cbb6b0c57326557e0f433339840272114a5"⟩, + ⟨"Ix/Theory/Named/Typing/Env.lean", "72949fadf8565117631d4ce822a2d44ebd671843e52a70a8617f0c9c85411cfa"⟩, + ⟨"Ix/Theory/Named/Typing/EnvLemmas.lean", "bd9f777d16b60d6bfa74e5e4e2dbf2423df1c1478c38970144a849078623e80b"⟩, + ⟨"Ix/Theory/Named/Typing/InductiveCertificate.lean", "ef4b4970be4680efda15c6ed921584f30d174a462c2643c6bb60c43d2248d353"⟩, + ⟨"Ix/Theory/Named/Typing/InductiveLemmas.lean", "90bb4f3964705de120018360d7bcf14ab81f05c621fd7c0c0c49539af301fa77"⟩, + ⟨"Ix/Theory/Named/Typing/InductivePattern.lean", "45480de8808c6311c0635ab0afc7fcdbe0c3534558cb1902f7d97d80886c423a"⟩, + ⟨"Ix/Theory/Named/Typing/InductivePatternWF.lean", "884955ce4111aed9540b7d02ebc2469aa26168a8efa476b2ee1cdd01ee402926"⟩, + ⟨"Ix/Theory/Named/Typing/Injectivity.lean", "e7233bde95305b46aa1d22d3e13e21aea93f7e3f903eeb46865c226ffe0bba88"⟩, + ⟨"Ix/Theory/Named/Typing/Lemmas.lean", "f183e28ec58a52dadba983cb53a55537cb9a497d9e8e4f78d2c2ca4cc9427a38"⟩, + ⟨"Ix/Theory/Named/Typing/Meta.lean", "16b0fb88d6719cdd1293108ca2520c1ef2c601819ef5562d0aa7c2d007021d9b"⟩, + ⟨"Ix/Theory/Named/Typing/NestedInductiveLemmas.lean", "f1dcef8e876386abfbebc5a36ca751dafaa75257b7c6c9f506c7447c4be9cd49"⟩, + ⟨"Ix/Theory/Named/Typing/Pattern.lean", "db05a0847000f4d3e2a69f4b053112bfca31dbc2b11082a804052bb44562f4e3"⟩, + ⟨"Ix/Theory/Named/Typing/QuotLemmas.lean", "63abdab93b43679259fc25df480afa1b2086edc8cf52cca5b08b2f36be847309"⟩, + ⟨"Ix/Theory/Named/Typing/Strong.lean", "266fbcb98287ad2f53c0c7ed11960aaa620471b71d4289def448ec259019d46a"⟩, + ⟨"Ix/Theory/Named/Typing/UniqueTyping.lean", "c297cd16f1ec43d6ea4fba930ddd490555cf212d59389f39765beb986dde5a3f"⟩, + ⟨"Ix/Theory/Named/VDecl.lean", "94f3032a2aa1e9e71056b1373fb456b242ede7405c88abe593e7f8432f760a66"⟩, + ⟨"Ix/Theory/Named/VEnv.lean", "16c9bb411f7482d5c08585a278b8ad8209eb8deb023bcb77f7bad675825a83a9"⟩, + ⟨"Ix/Theory/Named/VExpr.lean", "469c797a6eaee8883060470e0830dbe56b4b64f7ca91d9ae63304e288849ac8c"⟩, + ⟨"Ix/Theory/Named/VLevel.lean", "a570dff7d2065ca64acd1bb920628ec332b4e39f18a2dc423cbf3a7bd469ba58"⟩, + ⟨"Ix/Theory/Named/Reference/TypeChecker.lean", "40f99258932cfa5f2c99a2c2b5f7b59b467c10348cd238b70453f95e61dd7383"⟩, + ⟨"Ix/Theory/Named/Verify/Axioms.lean", "4c890728353a1df1b754b09bec3635b75bbfade362aff83a35005b2f316f5863"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/Basic.lean", "161d0abd5c8f4a8a8a09db2ab3bdcd988171aba2c7dcb4a997bd6a8cb32a89ae"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/ConstructorValidation.lean", "2ce3772020b93feed5e88a0a27d3dcdcb32801b0aca24c7c1e5fb694d9cf382e"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/ConstructorValidityMatrix.lean", "4245035c98958b93c3daefa2efd78f1f0c9779d5bd2a5633db0c2f60958b88b7"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/Elimination.lean", "89365c4a39346bcd7b1f94b7f568a272e4e02b64ff1b897df38f598079890011"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/EliminationFixturesCommon.lean", "1a2dbd72b2538f673c5080abd023c639bee0c200f9d2fd832158f312f726e8b9"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/EliminationFixturesEdges.lean", "86aaa7338723cdc0005d3646b68c77099ad8e1c113ac283b9d6048783bda4910"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/EliminationFixturesEq.lean", "371f2bd76c849048b8c27b741af65214e6345374e09c1658e00eceef0bd5e9d4"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/EliminationFixturesEqNat.lean", "08d2a0352b9a7652ee0c3296b8e6de36e3f3d16b44c77fa962855b355412479a"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/EliminationFixturesNat.lean", "5fc23bf4dacc65a476fa6416fbf548c75b563574aad7d1c670d28df5a27ce541"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/EliminationFixturesOrAnd.lean", "993466ee37f03b2f7494116d58dca184614f32308b851d0bceb275ddd4c3a79f"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/EliminationFixturesSmall.lean", "65d5114cba03dbc1e25b5ec644f35d72ac27dc370a376a37536ffb8c9d4e1409"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/IndexedVecCandidate.lean", "ba5fc7e4d21f85c1fbd091bdcdecc51c7c252a00329c6e4ce41cf703cfbfb280"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/IndexedVecConsReplay.lean", "5fc8c9d63d58a208f0318894f9ba77d87cff021f2dc413575da4e8e5ae42d738"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/IndexedVecConstructors.lean", "effb4340d8f020607ef34f0bb0195ee5e4217f15385e0c690b354c1c2aa23b04"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/IndexedVecOuterReplay.lean", "b15ebd574638b1f728439045f06b85421402dabfcc1df1e92fda97198d7de7e8"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/IndexedVecSemanticReplay.lean", "d2793f18d3052acac81951e18e01d5a5a20c3569cae1d831e3064c251bcf05c9"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/InductiveFixtures.lean", "5da7e485923a6e5d9db14361844d48c9628f22b7088072d861f9d24e01acd934"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/Lemmas.lean", "fe1c3fcec718f780e29af9051b62a906b3f6aa53502ac8740d01f8477ab49576"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/MutualInductiveFixtures.lean", "cab86779e590746367fb013f95ecb48f0a3f86b06a56ccf1ac43ae7fec78514d"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/NestedReplay.lean", "c76e7291e064a68f64b3d545de4c887fd8321950d514b485832dce76108bfa28"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/NestedRepresentation.lean", "7e5cd82cf41d35a6844d2cbc1f5af67ab5ccad33f81e199984e4017348487b2d"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/NestedTransformation.lean", "fd1aefd95d4374e0ae87cff11b10c468b7743c822f0f4cbb1a4f3923baee0b0f"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/Normalization.lean", "a9df5c28f597dc88f0cc496a1ad8a2c189ef82fddd194007e2bda7de15c27cdc"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/NormalizationMatrix.lean", "0495afd91d4770ec0dc998431828b370c65b4cc1b894994c06b4a50525b44342"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/SingletonParityMatrix.lean", "abc03daf11487f774a89f18506995d3e1f45e817f99dcc361219da73dd586322"⟩, + ⟨"Ix/Theory/Named/Verify/Environment/SingletonParityReplay.lean", "19cf222cf463ad9882c7c2ce278150678310b00141fa712684cb68061d1761c2"⟩, + ⟨"Ix/Theory/Named/Verify/Expr.lean", "520da96e974f708a15b738479cea37bdc3458adaca6523975f69b15f7bf52cd4"⟩, + ⟨"Ix/Theory/Named/Verify/Level.lean", "66f0ffcb59ae63498e8c6fb9cee87fa4003c67f1628fb87ec7661714975a4e02"⟩, + ⟨"Ix/Theory/Named/Verify/LevelStd.lean", "8aada81455e9c6a88eba0dcce86e30daf34ec9215e26ae4dfc9f396a49b8f57c"⟩, + ⟨"Ix/Theory/Named/Verify/LocalContext.lean", "91718812c4d727d27f8723e039b5e7d490e03a7d8bb4163ffed26ee1df030fe8"⟩, + ⟨"Ix/Theory/Named/Verify/Name.lean", "8a3ae08cc776deba9354b28010327470f241e4c924c335dff142b3f8ec326c78"⟩, + ⟨"Ix/Theory/Named/Verify/NameGenerator.lean", "3d7305876fcfc487c625aaee8c737a4bdb1a80b62f70a4fc3cfeaf3cf39219af"⟩, + ⟨"Ix/Theory/Named/Verify/NormLt.lean", "970149d18191ecbd889136507e6dd68ea43b833b6170ab825b69a90b4561e2f5"⟩, + ⟨"Ix/Theory/Named/Verify/QSort.lean", "79cbf8f96fb223526690c12333a095aa87ecf7d889b4ba2abacd328609550b9a"⟩, + ⟨"Ix/Theory/Named/Verify/TypeChecker.lean", "0a8441fa3354c7002b2b4b7491f04eaac94bd137d7eaaf8a1859bae0af6f58be"⟩, + ⟨"Ix/Theory/Named/Verify/TypeChecker/Basic.lean", "a9cc58cb6b86a6f84b30f41f520f3b9f26a30bdd56885107efc6f3e03b3df8dc"⟩, + ⟨"Ix/Theory/Named/Verify/TypeChecker/InferType.lean", "ddc9fa7ec80b958169d6515a92a723dbe6042ca4b3cdd7d6cfcc6fb859b1f90f"⟩, + ⟨"Ix/Theory/Named/Verify/TypeChecker/IsDefEq.lean", "3b9d09d09029eec24514e9905dd00c7435ba103717e506a46939818a80a39c9b"⟩, + ⟨"Ix/Theory/Named/Verify/TypeChecker/Reduce.lean", "ddc6abecc09ad21769b83b857d11bbecd3859be58debab62360fa9d9a5ec31cf"⟩, + ⟨"Ix/Theory/Named/Verify/TypeChecker/WHNF.lean", "745637e76413130151c0fb21401a3ee10cbb420aa8c9de4203afff147c2bd9cd"⟩, + ⟨"Ix/Theory/Named/Verify/Typing/ConditionallyTyped.lean", "2df5d00a10e4497b631a4d2a90b93cf811ab13cbfb735234a5815963928c3255"⟩, + ⟨"Ix/Theory/Named/Verify/Typing/Expr.lean", "2f9cb98009b6dcd0d45ec4b967e22756f4af1a32a4d45949ad5b4152b8e416c5"⟩, + ⟨"Ix/Theory/Named/Verify/Typing/Lemmas.lean", "ac1a2960dc945d5d9b5b998b438da6dd7ad6160b08a646f3e2c0b56cede54669"⟩, + ⟨"Ix/Theory/Named/Verify/VLCtx.lean", "f99f9fb85d79b2f9acd201323cc493f737285c34c064fc2bc68680c595878aa3"⟩ +] + +end Tests.Theory.NamedManifest diff --git a/Tests/Theory/Natural.lean b/Tests/Theory/Natural.lean new file mode 100644 index 000000000..a0bfa450a --- /dev/null +++ b/Tests/Theory/Natural.lean @@ -0,0 +1,108 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Theory.Standard + +open Ix.Theory + +namespace Tests.Theory.Natural + +open Ix.Theory.Model Ix.Theory.Certified Ix.Theory.Certified.Basis Ix.Theory.Certificate +open Tests.Theory.Certified (primitives) + +set_option maxRecDepth 16384 +set_option maxHeartbeats 16000000 + +def nat : AExpr Nat := .const (.member 200 0) [] +def zero : AExpr Nat := .const (.ctor 200 0 0) [] +def succ (n : AExpr Nat) : AExpr Nat := .app (.const (.ctor 200 0 1) []) n +def numeral : Nat → AExpr Nat + | 0 => zero + | n + 1 => succ (numeral n) + +def profile : PrimitiveSignature Nat := { primitives with natType := some (.member 200 0) } +def binaryType : AExpr Nat := .forallE .never nat (.forallE .never nat nat) +def motive : AExpr Nat := .lam .never nat nat +def addBody : AExpr Nat := + .lam .never nat (.lam .never nat + (.appN (.const (.member 201 0) [.succ .zero]) + [motive, .bvar 1, .lam .never nat (.lam .never nat (succ (.bvar 0))), .bvar 0])) +def add (n m : AExpr Nat) : AExpr Nat := .appN (.const (.member 300 0) []) [n, m] +def mulBody : AExpr Nat := + .lam .never nat (.lam .never nat + (.appN (.const (.member 201 0) [.succ .zero]) + [motive, .natLit 0, .lam .never nat (.lam .never nat (add (.bvar 3) (.bvar 0))), .bvar 0])) +def mul (n m : AExpr Nat) : AExpr Nat := .appN (.const (.member 301 0) []) [n, m] + +def declaration : Nat → Const Nat + | 200 => Certified.Natural.shape.source 200 + | 201 => Certified.Natural.shape.recursorSource 200 201 .large + | 300 => .defn 0 .definition binaryType.erase addBody.erase .safe + | 301 => .defn 0 .definition binaryType.erase mulBody.erase .safe + | 302 => .defn 0 .definition nat.erase (.natLit 3) .safe + | b => Standard.declaration b + +def store (alter : Nat → Const Nat → Const Nat := fun _ c => c) : Store Nat where + dom := [10, 11, 100, 101, 200, 201, 300, 301, 302] + nodup := by decide + blocks b := if b ∈ [10, 11, 100, 101, 200, 201, 300, 301, 302] then some ⟨[alter b (declaration b)]⟩ else none + mem_dom b := by split <;> simp_all + +def proposition (lhs rhs : AExpr Nat) : AExpr Nat := Equality.applied Standard.eq (.succ .zero) nat lhs rhs +def proof (n : Nat) : AExpr Nat := Equality.reflexivity Standard.refl (.succ .zero) nat (.natLit n) +def input (lhs : AExpr Nat) (value : Nat) : ProofInput Nat := + ⟨store, 0, (proof value).erase, (proposition lhs (.natLit value)).erase⟩ +def accepted (lhs : AExpr Nat) (value : Nat) : Bool := + let input := input lhs value + (Certificate.proofWitness? 3200 profile input).any (acceptsCertified.{0,0} 6400 profile input) + +#guard accepted zero 0 +#guard accepted (numeral 4) 4 +#guard accepted (succ (.natLit 8)) 9 +#guard accepted (.const (.member 302 0) []) 3 +#guard accepted (add (.natLit 0) (.natLit 0)) 0 +#guard accepted (add (.natLit 2) (.natLit 3)) 5 +#guard accepted (mul (.natLit 2) (.natLit 3)) 6 +#guard accepted (mul (.natLit 5) (.natLit 0)) 0 + +def sampleInput := input (add (.natLit 2) (.natLit 3)) 5 +def sampleWitness := Certificate.proofWitness? 3200 profile sampleInput + +#guard sampleWitness.isSome +#guard sampleWitness.any fun witness => witness.declarations.any fun declaration => match declaration with + | .natural _ => true + | _ => false + +-- The original successful witness must fail when the primitive selection, +-- authenticated source, claimed answer or admitted literal fact is changed. +#guard sampleWitness.any fun witness => !acceptsCertified.{0,0} 6400 primitives sampleInput witness +#guard sampleWitness.any fun witness => + !acceptsCertified.{0,0} 6400 { profile with natType := some (.member 100 0) } sampleInput witness +#guard sampleWitness.any fun witness => !acceptsCertified.{0,0} 6400 profile + { sampleInput with proposition := (proposition (add (.natLit 2) (.natLit 3)) (.natLit 6)).erase } witness +#guard sampleWitness.any fun witness => + let declarations := witness.declarations.map fun declaration => match declaration with + | .natural block => .ordinary block + | _ => declaration + !acceptsCertified.{0,0} 6400 profile sampleInput { witness with declarations } + +def unsafeNat (b : Nat) (c : Const Nat) : Const Nat := + if b = 200 then match c with + | .induct n p i type ctors _ => .induct n p i type ctors .unsafe + | _ => c + else c +def forgedRule (b : Nat) (c : Const Nat) : Const Nat := + if b = 201 then match c with + | .recursor n p i m r type rules k safety => + .recursor n p i m r type (rules.map fun rule => { rule with rhs := zero.erase }) k safety + | _ => c + else c +def changedAdd (b : Nat) (c : Const Nat) : Const Nat := + if b = 300 then .defn 0 .definition binaryType.erase (.lam nat.erase (.lam nat.erase (.natLit 0))) .safe else c + +#guard [unsafeNat, forgedRule, changedAdd].all fun alter => sampleWitness.any fun witness => + !acceptsCertified.{0,0} 6400 profile { sampleInput with store := store alter } witness + +end Tests.Theory.Natural diff --git a/Tests/Theory/Operations.lean b/Tests/Theory/Operations.lean new file mode 100644 index 000000000..b281555e1 --- /dev/null +++ b/Tests/Theory/Operations.lean @@ -0,0 +1,49 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certified.Operations +import Tests.Theory.Acceptance + +open Ix.Theory + +namespace Tests.Theory.Operations + +open Ix.Theory.Certified Ix.Theory.Model +open Tests.Theory.Checker +open Tests.Theory.Acceptance (identityAnnotations) + +def betaType : AExpr Nat := .forallE .never (.sort .zero) (.sort .zero) + +def betaInference : InferenceWitness Nat := { + annotations := .app (identityAnnotations (.succ .zero)) .leaf + type := betaType + typing := .app .never (.sort (.succ .zero)) (.forallE .never (.bvar 0) (.bvar 1)) + (identityWitness (.succ .zero)) .sort +} + +def betaWhnf : WhnfWitness Nat := { + source := betaInference + result := betaResult + resultTyping := .lam (.succ .zero) (.succ .zero) (.sort .zero) .sort .sort .bvar + conversion := betaWitness +} + +#guard inferCertified.{0,0} 50 0 emptyEnvironment [] betaSource.erase betaInference |>.isSome +#guard whnfCertified.{0,0} 50 0 emptyEnvironment [] betaSource.erase betaWhnf |>.isSome + +-- Valid typing plus reflexive conversion does not certify a remaining redex. +#guard whnfCertified.{0,0} 50 0 emptyEnvironment [] betaSource.erase + { betaWhnf with + result := betaSource + resultTyping := betaInference.typing + conversion := .refl } |>.isNone + +-- Neither the input reading nor the proposed normal form can change unchecked. +#guard whnfCertified.{0,0} 50 0 emptyEnvironment [] + (.app (identity .zero).erase (.sort .zero)) betaWhnf |>.isNone +#guard whnfCertified.{0,0} 50 0 emptyEnvironment [] betaSource.erase + { betaWhnf with result := .sort .zero, resultTyping := .sort } |>.isNone + +end Tests.Theory.Operations diff --git a/Tests/Theory/Ordinary.lean b/Tests/Theory/Ordinary.lean new file mode 100644 index 000000000..0ba2acd5d --- /dev/null +++ b/Tests/Theory/Ordinary.lean @@ -0,0 +1,203 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certificate.Ordinary +import Ix.Theory.Certified.Ordinary.LargeElim +import Ix.Theory.Certified.Ordinary.RecursorSyntax +import Tests.Theory.RecursorGoldens + +open Ix.Theory + +/-! These exercise shape and evidence production. They do not claim closed +proof acceptance or VM coverage of inductives. -/ + +namespace Tests.Theory.Ordinary + +open Model Certified Certified.Ordinary + +private structure SourceFamily where + type : VExpr Nat + ctors : List (Ctor Nat) + +def natSourceBlock : Nat := 10 +def functionalSourceBlock : Nat := 50 +def indexedSourceBlock : Nat := 60 + +private def natType : VExpr Nat := .const (.member natSourceBlock 0) [] +private def natFamily : SourceFamily := ⟨.sort (.succ .zero), [ + ⟨0, 0, 0, natType, .safe⟩, + ⟨0, 0, 1, .forallE natType natType, .safe⟩]⟩ + +private def functionalType : VExpr Nat := .const (.member functionalSourceBlock 0) [] +private def functionalFamily : SourceFamily := ⟨.sort (.succ .zero), [ + ⟨0, 0, 1, .forallE (.forallE (.sort .zero) functionalType) functionalType, .safe⟩]⟩ + +private def indexedFamily : SourceFamily := + ⟨.forallE (.sort (.succ .zero)) (.sort (.succ .zero)), [ + ⟨0, 0, 0, .app (.const (.member indexedSourceBlock 0) []) (.sort .zero), .safe⟩]⟩ + +def noEntries : Environment Nat := fun _ => none + +def singletonStore (source : Nat) (decl : Const Nat) : Store Nat where + dom := [source] + nodup := by simp + blocks b := if b = source then some ⟨[decl]⟩ else none + mem_dom b := by by_cases h : b = source <;> simp [h] + +def checked (source : Nat) (decl : Const Nat) (shape : Shape Nat) : Bool := + match Certificate.Ordinary.shape? 100 noEntries shape with + | none => false + | some witness => (checkShape.{0,0} 100 noEntries (singletonStore source decl) source witness).isSome + +def largeChecked (shape : Shape Nat) : Bool := + match Certificate.Ordinary.shape? 100 noEntries shape with + | none => false + | some witness => (checkLarge.{0,0} 100 noEntries witness).isSome + +def natShape : Shape Nat := + ⟨0, [], [], .succ .zero, [⟨[], [], []⟩, ⟨[], [⟨[], []⟩], []⟩]⟩ + +#guard natShape.source natSourceBlock = + .induct 0 0 0 natFamily.type natFamily.ctors .safe +#guard checked natSourceBlock (.induct 0 0 0 natFamily.type natFamily.ctors .safe) natShape +#guard largeChecked natShape + +def functionalShape : Shape Nat := + ⟨0, [], [], .succ .zero, [⟨[], [⟨[.sort .zero], []⟩], []⟩]⟩ + +#guard functionalShape.source functionalSourceBlock = + .induct 0 0 0 functionalFamily.type functionalFamily.ctors .safe +#guard checked functionalSourceBlock + (.induct 0 0 0 functionalFamily.type functionalFamily.ctors .safe) functionalShape + +def indexedShape : Shape Nat := + ⟨0, [], [.sort (.succ .zero)], .succ .zero, [⟨[], [], [.sort .zero]⟩]⟩ + +#guard indexedShape.source indexedSourceBlock = + .induct 0 0 1 indexedFamily.type indexedFamily.ctors .safe +#guard checked indexedSourceBlock + (.induct 0 0 1 indexedFamily.type indexedFamily.ctors .safe) indexedShape + +def listShape : Shape Nat := + ⟨1, [.sort (.succ (.param 0))], [], .succ (.param 0), + [⟨[], [], []⟩, ⟨[.bvar 0], [⟨[], []⟩], []⟩]⟩ + +def listType : VExpr Nat := .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0))) +def listDecl : Const Nat := .induct 1 1 0 listType [ + ⟨1, 1, 0, .forallE (.sort (.succ (.param 0))) (.app (.const (.member 70 0) [.param 0]) (.bvar 0)), .safe⟩, + ⟨1, 1, 2, .forallE (.sort (.succ (.param 0))) <| .forallE (.bvar 0) <| + .forallE (.app (.const (.member 70 0) [.param 0]) (.bvar 1)) + (.app (.const (.member 70 0) [.param 0]) (.bvar 2)), .safe⟩] .safe + +#guard listShape.source 70 = listDecl +#guard checked 70 listDecl listShape +#guard largeChecked listShape + +-- The branch domain depends on the constructor's ordinary field and a +-- parameter function: W (A : Type) (B : A → Type), node a (B a → W A B). +def wShape : Shape Nat := + ⟨0, [.sort (.succ .zero), .forallE .never (.bvar 0) (.sort (.succ .zero))], [], .succ .zero, + [⟨[.bvar 1], [⟨[.app (.bvar 1) (.bvar 0)], []⟩], []⟩]⟩ +#guard checked 71 (wShape.source 71) wShape +#guard largeChecked wShape + +-- The second index depends on the first. Checking only a list of arities +-- would miss an ill-typed second argument. +def dependentIndices : Shape Nat := + ⟨0, [], [.sort .zero, .bvar 0], .succ .zero, + [⟨[.sort .zero, .bvar 0], [], [.bvar 1, .bvar 0]⟩]⟩ +#guard checked 72 (dependentIndices.source 72) dependentIndices + +def wrongIndices : Shape Nat := + { dependentIndices with constructors := [⟨[.sort .zero, .bvar 0], [], [.bvar 1, .sort .zero]⟩] } +#guard !checked 72 (wrongIndices.source 72) wrongIndices + +def tooLarge : Shape Nat := ⟨0, [], [], .succ .zero, [⟨[.sort (.succ .zero)], [], []⟩]⟩ +#guard !checked 73 (tooLarge.source 73) tooLarge + +-- A negative occurrence and a same-block recursive domain are rejected even +-- when the proposed descriptor exactly reproduces the source declaration. +def negative : Shape Nat := + ⟨0, [], [], .succ .zero, [⟨[.forallE .never (.const (.member 74 0) []) (.sort .zero)], [], []⟩]⟩ +#guard !checked 74 (negative.source 74) negative +def recursiveDomain : Shape Nat := + ⟨0, [], [], .succ .zero, [⟨[], [⟨[.const (.member 75 0) []], []⟩], []⟩]⟩ +#guard !checked 75 (recursiveDomain.source 75) recursiveDomain + +def emptyProp : Shape Nat := ⟨0, [], [], .zero, []⟩ +def singletonProp : Shape Nat := ⟨0, [.sort .zero], [], .zero, [⟨[.bvar 0], [], []⟩]⟩ +def manyProp : Shape Nat := ⟨0, [], [], .zero, [⟨[], [], []⟩, ⟨[], [], []⟩]⟩ +def dataProp : Shape Nat := ⟨0, [], [], .zero, [⟨[.sort .zero], [], []⟩]⟩ + +#guard checked 76 (emptyProp.source 76) emptyProp +#guard checked 77 (singletonProp.source 77) singletonProp +#guard checked 78 (manyProp.source 78) manyProp +#guard checked 79 (dataProp.source 79) dataProp +#guard largeChecked emptyProp +#guard largeChecked singletonProp +#guard !largeChecked manyProp +#guard !largeChecked dataProp + +#guard !checked natSourceBlock (.induct 0 0 0 natFamily.type natFamily.ctors .unsafe) natShape +#guard !checked (natSourceBlock + 1) (.induct 0 0 0 natFamily.type natFamily.ctors .safe) natShape +#guard !checked natSourceBlock (.induct 1 0 0 natFamily.type natFamily.ctors .safe) natShape + +def recursorSyntaxMatches (name : String) (shape : Shape Nat) (source recursor : Nat) + (mode : Inductive.ElimMode) : Bool := + match recursorGoldens.find? (·.1 == name) with + | none => false + | some (_, expectedType, expectedRules) => + (shape.recursorType source mode).erase == expectedType && + (shape.constructors.zipIdx.map fun (ctor, i) => + ((shape.ruleRhs source recursor mode i ctor).erase, + (shape.ruleType source mode i ctor).erase)) == expectedRules + +#guard recursorSyntaxMatches "nat" natShape natSourceBlock 90 .large +#guard recursorSyntaxMatches "list" listShape 70 91 .large +#guard recursorSyntaxMatches "indexed" indexedShape indexedSourceBlock 92 .large +#guard recursorSyntaxMatches "functional" functionalShape functionalSourceBlock 93 .large +#guard recursorSyntaxMatches "w" wShape 71 94 .large +#guard recursorSyntaxMatches "dependentIndices" dependentIndices 72 95 .large +#guard recursorSyntaxMatches "manyProp" manyProp 78 96 .small +#guard recursorSyntaxMatches "singletonProp" singletonProp 77 97 .large + +def pairedStore (source recursor : Nat) (h : source ≠ recursor) (decl rec : Const Nat) : Store Nat where + dom := [source, recursor] + nodup := by simp [h] + blocks b := if b = source then some ⟨[decl]⟩ else if b = recursor then some ⟨[rec]⟩ else none + mem_dom b := by by_cases hs : b = source <;> by_cases hr : b = recursor <;> simp [hs, hr, Ne.symm h] + +def signaturesChecked (shape : Shape Nat) (source recursor : Nat) (mode : Inductive.ElimMode) : Bool := Id.run do + if hne : source ≠ recursor then + let store := pairedStore source recursor hne (shape.source source) (shape.recursorSource source recursor mode) + let some sw := Certificate.Ordinary.shape? 500 noEntries shape | return false + let some _ := checkShape.{0,0} 500 noEntries store source sw | return false + let some _ := checkMode.{0,0} 500 noEntries sw mode | return false + let some cws := Certificate.Ordinary.constructorTypes? 500 noEntries shape source | return false + let some _ := shape.checkConstructorTypes.{0,0} 500 noEntries source shape.constructors cws | return false + let some rw := Certificate.Ordinary.recursorType? 500 noEntries shape source mode | return false + let some _ := shape.checkRecursorType.{0,0} 500 noEntries store source recursor mode rw | return false + let some rules := Certificate.Ordinary.rules? 500 noEntries shape source recursor mode | return false + return (shape.checkRules.{0,0} 500 (shape.recursorEnvironment noEntries source recursor mode) + source recursor mode shape.constructors.zipIdx rules).isSome + else return false + +#guard signaturesChecked natShape natSourceBlock 90 .large +#guard signaturesChecked listShape 70 91 .large +#guard signaturesChecked indexedShape indexedSourceBlock 92 .large +#guard signaturesChecked functionalShape functionalSourceBlock 93 .large +#guard signaturesChecked wShape 71 94 .large +#guard signaturesChecked dependentIndices 72 95 .large +#guard signaturesChecked manyProp 78 96 .small +#guard signaturesChecked singletonProp 77 97 .large +#guard !signaturesChecked manyProp 78 96 .large + +-- Formation of a signature alone publishes no equation. +def formedSignature : Environment Nat := fun r => + if r = .member 80 0 then some ⟨0, .sort (.succ .zero), none, [], []⟩ else none +#guard !(verifyConversion.{0,0} 10 0 formedSignature [] (.sort .zero) (.sort (.succ .zero)) + (.equation (.member 80 0) 0 [])).isSome + +end Tests.Theory.Ordinary diff --git a/Tests/Theory/OrdinaryAcceptance.lean b/Tests/Theory/OrdinaryAcceptance.lean new file mode 100644 index 000000000..3d98da6eb --- /dev/null +++ b/Tests/Theory/OrdinaryAcceptance.lean @@ -0,0 +1,162 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Theory.Ordinary +import Tests.Theory.Acceptance +import Ix.Theory.Certificate.Build + +open Ix.Theory + +/-! Closed acceptance through atomic ordinary admission and generated equation +certificates. These are abstract-source tests; serialized VM coverage has its +own adapter and corpus. -/ + +namespace Tests.Theory.OrdinaryAcceptance + +open Ix.Theory.Model Ix.Theory.Certified Ix.Theory.Certified.Ordinary Ix.Theory.Certificate +open Tests.Theory.Certified (primitives) +open Tests.Theory.Ordinary (natShape listShape indexedShape emptyProp singletonProp manyProp dataProp) + +set_option maxRecDepth 4096 +set_option maxHeartbeats 8000000 + +def storeFor (shape : Shape Nat) (mode : Inductive.ElimMode) + (recursorOverride : Option (Const Nat) := none) : Store Nat where + dom := [10, 11, 100, 101] + nodup := by decide + blocks b := + if b = 10 then some ⟨[PrimitiveSignature.falseDeclaration]⟩ + else if b = 11 then some ⟨[primitives.falseElimDeclaration]⟩ + else if b = 100 then some ⟨[shape.source 100]⟩ + else if b = 101 then some ⟨[recursorOverride.getD (shape.recursorSource 100 101 mode)]⟩ else none + mem_dom b := by + by_cases h0 : b = 10 <;> by_cases h1 : b = 11 <;> + by_cases hs : b = 100 <;> by_cases hr : b = 101 <;> simp_all + +def admitted (shape : Shape Nat) (mode : Inductive.ElimMode) : Bool := + match initialize?.{0,0} primitives (storeFor shape mode), + Certificate.Ordinary.block? 1000 primitives.environment shape 100 101 mode with + | some state, some witness => (admitOrdinary?.{0,0} 1000 state witness).isSome + | _, _ => false + +#guard admitted natShape .large +#guard admitted listShape .large +#guard admitted indexedShape .large +#guard admitted emptyProp .large +#guard admitted singletonProp .large +#guard admitted manyProp .small +#guard !admitted manyProp .large +#guard !admitted dataProp .large + +def identity : AExpr Nat := .lam .always (.sort .zero) (.lam .always (.bvar 0) (.bvar 0)) +def identityType : AExpr Nat := .forallE .always (.sort .zero) (.forallE .always (.bvar 0) (.bvar 1)) +def proposition (computed : AExpr Nat) : AExpr Nat := + .forallE .always (.sort .zero) (.forallE .always (.bvar 0) computed) + +def proofWitness? (shape : Shape Nat) (mode : Inductive.ElimMode) (proof proposition : AExpr Nat) : + Option (ProofWitness Nat) := do + let block ← Certificate.Ordinary.block? 1000 primitives.environment shape 100 101 mode + let entries := shape.publishedEnvironment primitives.environment 100 101 mode + let P ← inferAnnotated? 1000 0 entries [] proposition + let Pw ← castWith? 1000 0 entries [] P (.sort .zero) + let e ← inferAnnotated? 1000 0 entries [] proof + let ew ← castWith? 1000 0 entries [] e proposition + return ⟨[.ordinary block], annotations proof, annotations proposition, ew, Pw⟩ + +def accepted (shape : Shape Nat) (mode : Inductive.ElimMode) (proof proposition : AExpr Nat) + (recursorOverride : Option (Const Nat) := none) : Bool := + match proofWitness? shape mode proof proposition with + | none => false + | some witness => acceptsCertified.{0,0} 2000 primitives + ⟨storeFor shape mode recursorOverride, 0, proof.erase, proposition.erase⟩ witness + +def family : AExpr Nat := .const (.member 100 0) [] +def zero : AExpr Nat := .const (.ctor 100 0 0) [] +def succ (n : AExpr Nat) : AExpr Nat := .app (.const (.ctor 100 0 1) []) n + +def natComputed : AExpr Nat := .appN (.const (.member 101 0) [.succ .zero]) + [.lam .never family (.sort .zero), .bvar 1, + .lam .never family (.lam .never (.sort .zero) (.bvar 0)), succ zero] + +#guard (proofWitness? natShape .large identity (proposition natComputed)).isSome +#guard accepted natShape .large identity (proposition natComputed) + +def listType : AExpr Nat := .app (.const (.member 100 0) [.zero]) (.sort .zero) +def nil : AExpr Nat := .app (.const (.ctor 100 0 0) [.zero]) (.sort .zero) +def cons : AExpr Nat := .appN (.const (.ctor 100 0 1) [.zero]) [.sort .zero, .bvar 1, nil] +def listComputed : AExpr Nat := .appN (.const (.member 101 0) [.succ .zero, .zero]) + [.sort .zero, .lam .never listType (.sort .zero), .bvar 1, + .lam .never (.sort .zero) (.lam .never listType (.lam .never (.sort .zero) (.bvar 0))), cons] + +#guard (proofWitness? listShape .large identity (proposition listComputed)).isSome +#guard accepted listShape .large identity (proposition listComputed) + +def indexedComputed : AExpr Nat := .appN (.const (.member 101 0) [.succ .zero]) + [.lam .never (.sort (.succ .zero)) (.lam .never (.app family (.bvar 0)) (.sort .zero)), + .bvar 1, .sort .zero, zero] + +#guard accepted indexedShape .large identity (proposition indexedComputed) + +def functionalShape : Shape Nat := + ⟨0, [], [], .succ .zero, [⟨[], [], []⟩, ⟨[], [⟨[.sort .zero], []⟩], []⟩]⟩ +def functionalComputed : AExpr Nat := .appN (.const (.member 101 0) [.succ .zero]) + [.lam .never family (.sort .zero), .bvar 1, + .lam .never (.forallE .never (.sort .zero) family) + (.lam .never (.forallE .never (.sort .zero) (.sort .zero)) (.app (.bvar 0) (.bvar 3))), + succ (.lam .never (.sort .zero) zero)] + +#guard admitted functionalShape .large +#guard (proofWitness? functionalShape .large identity (proposition functionalComputed)).isSome +#guard accepted functionalShape .large identity (proposition functionalComputed) + +def smallProof : AExpr Nat := .lam .always (.sort .zero) (.lam .always (.bvar 0) + (.appN (.const (.member 101 0) []) [.lam .never family (.bvar 2), .bvar 0, .bvar 0, zero])) +#guard accepted manyProp .small smallProof identityType + +-- Source rule tampering cannot be repaired by a valid shape/type certificate. +def forgedNatRecursor : Const Nat := + match natShape.recursorSource 100 101 .large with + | .recursor u p i m n type (_ :: rules) k safety => + .recursor u p i m n type (⟨0, .sort .zero⟩ :: rules) k safety + | other => other +#guard !accepted natShape .large identity (proposition natComputed) (some forgedNatRecursor) + +-- A staged signature has no equation; the public environment gets the law +-- only after the entire block checker succeeds. +def natEquationLicensed (entries : Environment Nat) : Bool := Id.run do + let some law := (natShape.recursorLaws 100 101 .large)[0]? | return false + return (verifyConversion.{0,0} 1000 0 entries [] + (law.lhs.instL [.succ .zero]) (law.rhs.instL [.succ .zero]) + (.equation (.member 101 0) 0 [.succ .zero])).isSome + +#guard natEquationLicensed (natShape.publishedEnvironment primitives.environment 100 101 .large) +#guard !natEquationLicensed (natShape.recursorEnvironment primitives.environment 100 101 .large) + +/-- The generic driver recovers its shape and declaration order from the raw +store. No description is provided to witness construction. -/ +def sourceAccepted (shape : Shape Nat) (mode : Inductive.ElimMode) + (proof proposition : AExpr Nat) : Bool := + let input : ProofInput Nat := ⟨storeFor shape mode, 0, proof.erase, proposition.erase⟩ + (Certificate.proofWitness? 2000 primitives input).any + (acceptsCertified.{0,0} 2000 primitives input) + +#guard sourceAccepted natShape .large identity (proposition natComputed) +#guard sourceAccepted listShape .large identity (proposition listComputed) +#guard sourceAccepted indexedShape .large identity (proposition indexedComputed) +#guard sourceAccepted functionalShape .large identity (proposition functionalComputed) +#guard sourceAccepted manyProp .small smallProof identityType + +-- The source parser also reconstructs dependent telescopes and more than one +-- recursive field. It never silently removes a recursive dependency. +#guard [Tests.Theory.Ordinary.wShape, Tests.Theory.Ordinary.dependentIndices].all fun shape => + (Certificate.Ordinary.description? 1000 primitives.environment 100 (shape.source 100)).isSome + +def multipleFields : Shape Nat := + ⟨0, [], [], .succ .zero, [⟨[], [], []⟩, ⟨[], [⟨[], []⟩, ⟨[], []⟩], []⟩]⟩ +#guard (Certificate.Ordinary.description? 1000 primitives.environment 100 + (multipleFields.source 100)).isSome +#guard (Certificate.Ordinary.removeVariables? 1 (.bvar 0 : VExpr Nat)).isNone + +end Tests.Theory.OrdinaryAcceptance diff --git a/Tests/Theory/Provenance.lean b/Tests/Theory/Provenance.lean new file mode 100644 index 000000000..b430036a3 --- /dev/null +++ b/Tests/Theory/Provenance.lean @@ -0,0 +1,57 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Lean +import Tests.Theory.ImportManifest +import Tests.Theory.NamedManifest + +open System Tests.Theory.ImportManifest + +private def need (ok : Bool) (message : String) : IO Unit := + unless ok do throw (IO.userError message) + +private def sha256 (path : FilePath) : IO String := do + let result ← IO.Process.output { cmd := "sha256sum", args := #["--", path.toString] } + need (result.exitCode == 0) result.stderr + return (result.stdout.splitOn " ").headD "" + +private def sameFiles (actual expected : Array String) : IO Unit := + need (actual.qsort (· < ·) == expected.qsort (· < ·)) "theory source inventory changed; review import provenance" + +def main (args : List String) : IO Unit := do + need (args.length ≤ 1) "usage: lake env lean --run Tests/Theory/Provenance.lean [CON_LECHE_REPOSITORY]" + let actual := ((← (FilePath.mk "Ix/Theory").walkDir).filter (·.extension == some "lean")).map (·.toString) + let named := actual.filter (·.startsWith "Ix/Theory/Named/") + sameFiles ((actual.filter (!·.startsWith "Ix/Theory/Named/")).push "Ix/Theory.lean") + (selected.map (·.target)) + sameFiles named ((Tests.Theory.NamedManifest.selected.map (·.target)).push + "Ix/Theory/Named/Std/AxiomAudit.lean") + need (← (FilePath.mk "Ix/Theory/Named/LICENSE").pathExists) "missing named-specification Apache license" + need (← (FilePath.mk "Ix/Theory/Named/NOTICE").pathExists) "missing named-specification attribution" + for row in selected do + let contents ← IO.FS.readFile row.target + need (!(contents.contains "import Ix.Theory.Named")) + s!"set-model foundation depends on named checker proof support: {row.target}" + let mut ports := #[] + for directory in ["Ix/Theory/Model/SetTheory", "Ix/Theory/Model/SetModel"] do + ports := ports ++ ((← (FilePath.mk directory).walkDir).filter (·.extension == some "lean")).map (·.toString) + sameFiles ports (conLeche.map (·.target)) + need (← (FilePath.mk "Ix/Theory/LICENSE-APACHE").pathExists) "missing con-leche Apache license" + for row in conLeche do + need ((← sha256 row.target) == row.targetSha256) s!"con-leche port changed: {row.target}" + let contents ← IO.FS.readFile row.target + need (contents.startsWith s!"/-\nPorted from con-leche ({conLecheRevision}).\nSource: {row.source}\n" && + contents.contains "SPDX-License-Identifier: Apache-2.0 AND (MIT OR Apache-2.0)") + s!"con-leche notice changed: {row.target}" + if let some repository := args.head? then + let blob ← IO.Process.output { + cmd := "git", args := #["-C", repository, "show", s!"{conLecheRevision}:{row.source}"] } + need (blob.exitCode == 0) blob.stderr + IO.FS.withTempFile fun handle path => do + handle.putStr blob.stdout + handle.flush + need ((← sha256 path) == row.sourceSha256) s!"con-leche upstream hash mismatch: {row.source}" + IO.println s!"Theory provenance OK: {selected.size} selected source files; {conLeche.size} con-leche ports, exact content and notices." + IO.println s!"Named proof support: {named.size} local source files; separate from the set-model foundation." diff --git a/Tests/Theory/Quotient.lean b/Tests/Theory/Quotient.lean new file mode 100644 index 000000000..792d56c09 --- /dev/null +++ b/Tests/Theory/Quotient.lean @@ -0,0 +1,144 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certificate.Build +import Tests.Theory.Certified + +open Ix.Theory + +namespace Tests.Theory.Quotient + +open Ix.Theory.Model Ix.Theory.Certified Ix.Theory.Certified.Basis +open Tests.Theory.Certified (primitives) + +set_option maxRecDepth 8192 +set_option maxHeartbeats 12000000 + +def refs : Certified.Quotient.Refs Nat where + eq := .member 100 0 + eqRefl := .ctor 100 0 0 + eqRec := .member 101 0 + type := .member 200 0 + ctor := .member 201 0 + lift := .member 202 0 + ind := .member 203 0 + sound := .member 204 0 + +def declaration : Nat → Const Nat + | 10 => PrimitiveSignature.falseDeclaration + | 11 => primitives.falseElimDeclaration + | 100 => Equality.shape.source 100 + | 101 => Equality.shape.recursorSource 100 101 .large true + | 200 => refs.source .type + | 201 => refs.source .ctor + | 202 => refs.source .lift + | 203 => refs.source .ind + | 204 => refs.source .sound + | _ => .axiom 0 (.sort .zero) .safe + +def addresses : List Nat := [10, 11, 100, 101, 200, 201, 202, 203, 204] + +def store (alter : Nat → Const Nat → Const Nat := fun _ c => c) : Store Nat where + dom := addresses + nodup := by decide + blocks b := if b ∈ addresses then some ⟨[alter b (declaration b)]⟩ else none + mem_dom b := by split <;> simp_all + +def input (n : Nat) (proof proposition : AExpr Nat) : ProofInput Nat := + ⟨store, n, proof.erase, proposition.erase⟩ + +def accepted (n : Nat) (proof proposition : AExpr Nat) : Bool := + let input := input n proof proposition + (Certificate.proofWitness? 3000 primitives input).any (acceptsCertified.{0,0} 3000 primitives input) + +#guard (Certificate.Quotient.refs? store refs.type).isSome +#guard (Certificate.Quotient.refs? store refs.sound).isSome + +-- Exact soundness and dependent Prop induction are themselves closed logical +-- propositions, admitted through source discovery and executable validation. +#guard accepted 1 (.const refs.sound [.param 0]) (Certified.Quotient.soundType refs) +#guard accepted 1 (.const refs.ind [.param 0]) (Certified.Quotient.indType refs) + +def relation (u : VLevel) (A : AExpr Nat) : AExpr Nat := + .lam .never A (.lam .never (A.liftN 1) + (Equality.applied refs.eq u (A.liftN 2) (.bvar 1) (.bvar 0))) + +def constantInvariant (u v : VLevel) (A B b : AExpr Nat) : AExpr Nat := + .lamN .always [A, A.liftN 1, .appN ((relation u A).liftN 2) [.bvar 1, .bvar 0]] + (Equality.reflexivity refs.eqRefl v (B.liftN 3) (b.liftN 3)) + +def computed (u v : VLevel) (A B b a : AExpr Nat) : AExpr Nat := + .appN (.const refs.lift [u, v]) [A, relation u A, B, + .lam (zeroCondition v) A (b.liftN 1), constantInvariant u v A B b, + Certified.Quotient.constructed refs u A (relation u A) a] + +def binders (u : VLevel) : List (AExpr Nat) := [.sort u, .bvar 0, .sort .zero, .bvar 0] +def betaProof (u : VLevel) : AExpr Nat := .lamN .always (binders u) (.bvar 0) +def betaProposition (u : VLevel) : AExpr Nat := + .forallN .always (binders u) (computed u (.succ .zero) (.bvar 3) (.sort .zero) (.bvar 1) (.bvar 2)) + +-- The original statement contains a quotient lift returning a proposition. +-- Conversion to that statement requires the newly admitted computation law. +#guard accepted 1 (betaProof (.param 0)) (betaProposition (.param 0)) +#guard accepted 0 (betaProof .zero) (betaProposition .zero) +#guard accepted 0 (betaProof (.succ .zero)) (betaProposition (.succ .zero)) + +def propLiftProof (u : VLevel) : AExpr Nat := + .lamN .always (binders u) (computed u .zero (.bvar 3) (.bvar 1) (.bvar 0) (.bvar 2)) +def propLiftProposition (u : VLevel) : AExpr Nat := .forallN .always (binders u) (.bvar 1) + +#guard accepted 1 (propLiftProof (.param 0)) (propLiftProposition (.param 0)) +#guard accepted 0 (propLiftProof .zero) (propLiftProposition .zero) + +def betaInput := input 1 (betaProof (.param 0)) (betaProposition (.param 0)) +def betaWitness := Certificate.proofWitness? 3000 primitives betaInput + +#guard betaWitness.isSome + +def wrongKind (b : Nat) (c : Const Nat) : Const Nat := + if b = 200 then .quot .ctor 1 Certified.Quotient.typeType.erase else c +def wrongArity (b : Nat) (c : Const Nat) : Const Nat := + if b = 201 then .quot .ctor 2 (Certified.Quotient.ctorType refs).erase else c +def falseSound (b : Nat) (c : Const Nat) : Const Nat := + if b = 204 then .axiom 1 primitives.falseExpr .safe else c +def unsafeSound (b : Nat) (c : Const Nat) : Const Nat := + if b = 204 then .axiom 1 (Certified.Quotient.soundType refs).erase .unsafe else c +def wrongLift (b : Nat) (c : Const Nat) : Const Nat := + if b = 202 then .quot .lift 2 (Certified.Quotient.indType refs).erase else c + +-- A valid certificate for the original declarations must fail against each +-- altered source store; this tests the validator, not just producer decline. +#guard [wrongKind, wrongArity, falseSound, unsafeSound, wrongLift].all fun alter => + betaWitness.any fun witness => + !acceptsCertified.{0,0} 3000 primitives { betaInput with store := store alter } witness + +#guard betaWitness.any fun witness => + let declarations := witness.declarations.map fun declaration => match declaration with + | .quotient q => DeclarationWitness.quotient { q with types := q.types.drop 1 } + | other => other + !acceptsCertified.{0,0} 3000 primitives betaInput { witness with declarations } + +#guard betaWitness.any fun witness => + let declarations := witness.declarations.map fun declaration => match declaration with + | .quotient q => DeclarationWitness.quotient { q with liftRule := q.indRule } + | other => other + !acceptsCertified.{0,0} 3000 primitives betaInput { witness with declarations } + +#guard betaWitness.any fun witness => + let declarations := witness.declarations.map fun declaration => match declaration with + | .quotient q => DeclarationWitness.quotient { q with refs := { q.refs with ctor := q.refs.type } } + | other => other + !acceptsCertified.{0,0} 3000 primitives betaInput { witness with declarations } + +#guard betaWitness.any fun witness => + let quotient := witness.declarations.filter fun declaration => match declaration with + | .quotient _ => true + | _ => false + let rest := witness.declarations.filter fun declaration => match declaration with + | .quotient _ => false + | _ => true + !acceptsCertified.{0,0} 3000 primitives betaInput { witness with declarations := quotient ++ rest } + +end Tests.Theory.Quotient diff --git a/Tests/Theory/RecursorGoldens.lean b/Tests/Theory/RecursorGoldens.lean new file mode 100644 index 000000000..c02b6b8f0 --- /dev/null +++ b/Tests/Theory/RecursorGoldens.lean @@ -0,0 +1,45 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Expr + +open Ix.Theory + +namespace Tests.Theory + +/-- Frozen outputs of the former inductive generator, captured during the +model import. These retain the independent recursor comparisons without +depending on the old typing/metatheory implementation. -/ +def recursorGoldens : List (String × VExpr Nat × List (VExpr Nat × VExpr Nat)) := [ + ("nat", (.forallE (.forallE (.const (.member 10 0) []) (.sort (.param 0))) (.forallE (.app (.bvar 0) (.const (.ctor 10 0 0) [])) (.forallE (.forallE (.const (.member 10 0) []) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 3) (.app (.const (.ctor 10 0 1) []) (.bvar 1))))) (.forallE (.const (.member 10 0) []) (.app (.bvar 3) (.bvar 0)))))), [ + ((.lam (.forallE (.const (.member 10 0) []) (.sort (.param 0))) (.lam (.app (.bvar 0) (.const (.ctor 10 0 0) [])) (.lam (.forallE (.const (.member 10 0) []) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 3) (.app (.const (.ctor 10 0 1) []) (.bvar 1))))) (.bvar 1)))), (.forallE (.forallE (.const (.member 10 0) []) (.sort (.param 0))) (.forallE (.app (.bvar 0) (.const (.ctor 10 0 0) [])) (.forallE (.forallE (.const (.member 10 0) []) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 3) (.app (.const (.ctor 10 0 1) []) (.bvar 1))))) (.app (.bvar 2) (.const (.ctor 10 0 0) [])))))), + ((.lam (.forallE (.const (.member 10 0) []) (.sort (.param 0))) (.lam (.app (.bvar 0) (.const (.ctor 10 0 0) [])) (.lam (.forallE (.const (.member 10 0) []) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 3) (.app (.const (.ctor 10 0 1) []) (.bvar 1))))) (.lam (.const (.member 10 0) []) (.app (.app (.bvar 1) (.bvar 0)) (.app (.app (.app (.app (.const (.member 90 0) [(.param 0)]) (.bvar 3)) (.bvar 2)) (.bvar 1)) (.bvar 0))))))), (.forallE (.forallE (.const (.member 10 0) []) (.sort (.param 0))) (.forallE (.app (.bvar 0) (.const (.ctor 10 0 0) [])) (.forallE (.forallE (.const (.member 10 0) []) (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.bvar 3) (.app (.const (.ctor 10 0 1) []) (.bvar 1))))) (.forallE (.const (.member 10 0) []) (.app (.bvar 3) (.app (.const (.ctor 10 0 1) []) (.bvar 0)))))))) + ]), + ("list", (.forallE (.sort (.succ (.param 1))) (.forallE (.forallE (.app (.const (.member 70 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.forallE (.app (.bvar 0) (.app (.const (.ctor 70 0 0) [(.param 1)]) (.bvar 1))) (.forallE (.forallE (.bvar 2) (.forallE (.app (.const (.member 70 0) [(.param 1)]) (.bvar 3)) (.forallE (.app (.bvar 3) (.bvar 0)) (.app (.bvar 4) (.app (.app (.app (.const (.ctor 70 0 1) [(.param 1)]) (.bvar 5)) (.bvar 2)) (.bvar 1)))))) (.forallE (.app (.const (.member 70 0) [(.param 1)]) (.bvar 3)) (.app (.bvar 3) (.bvar 0))))))), [ + ((.lam (.sort (.succ (.param 1))) (.lam (.forallE (.app (.const (.member 70 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.lam (.app (.bvar 0) (.app (.const (.ctor 70 0 0) [(.param 1)]) (.bvar 1))) (.lam (.forallE (.bvar 2) (.forallE (.app (.const (.member 70 0) [(.param 1)]) (.bvar 3)) (.forallE (.app (.bvar 3) (.bvar 0)) (.app (.bvar 4) (.app (.app (.app (.const (.ctor 70 0 1) [(.param 1)]) (.bvar 5)) (.bvar 2)) (.bvar 1)))))) (.bvar 1))))), (.forallE (.sort (.succ (.param 1))) (.forallE (.forallE (.app (.const (.member 70 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.forallE (.app (.bvar 0) (.app (.const (.ctor 70 0 0) [(.param 1)]) (.bvar 1))) (.forallE (.forallE (.bvar 2) (.forallE (.app (.const (.member 70 0) [(.param 1)]) (.bvar 3)) (.forallE (.app (.bvar 3) (.bvar 0)) (.app (.bvar 4) (.app (.app (.app (.const (.ctor 70 0 1) [(.param 1)]) (.bvar 5)) (.bvar 2)) (.bvar 1)))))) (.app (.bvar 2) (.app (.const (.ctor 70 0 0) [(.param 1)]) (.bvar 3)))))))), + ((.lam (.sort (.succ (.param 1))) (.lam (.forallE (.app (.const (.member 70 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.lam (.app (.bvar 0) (.app (.const (.ctor 70 0 0) [(.param 1)]) (.bvar 1))) (.lam (.forallE (.bvar 2) (.forallE (.app (.const (.member 70 0) [(.param 1)]) (.bvar 3)) (.forallE (.app (.bvar 3) (.bvar 0)) (.app (.bvar 4) (.app (.app (.app (.const (.ctor 70 0 1) [(.param 1)]) (.bvar 5)) (.bvar 2)) (.bvar 1)))))) (.lam (.bvar 3) (.lam (.app (.const (.member 70 0) [(.param 1)]) (.bvar 4)) (.app (.app (.app (.bvar 2) (.bvar 1)) (.bvar 0)) (.app (.app (.app (.app (.app (.const (.member 91 0) [(.param 0), (.param 1)]) (.bvar 5)) (.bvar 4)) (.bvar 3)) (.bvar 2)) (.bvar 0))))))))), (.forallE (.sort (.succ (.param 1))) (.forallE (.forallE (.app (.const (.member 70 0) [(.param 1)]) (.bvar 0)) (.sort (.param 0))) (.forallE (.app (.bvar 0) (.app (.const (.ctor 70 0 0) [(.param 1)]) (.bvar 1))) (.forallE (.forallE (.bvar 2) (.forallE (.app (.const (.member 70 0) [(.param 1)]) (.bvar 3)) (.forallE (.app (.bvar 3) (.bvar 0)) (.app (.bvar 4) (.app (.app (.app (.const (.ctor 70 0 1) [(.param 1)]) (.bvar 5)) (.bvar 2)) (.bvar 1)))))) (.forallE (.bvar 3) (.forallE (.app (.const (.member 70 0) [(.param 1)]) (.bvar 4)) (.app (.bvar 4) (.app (.app (.app (.const (.ctor 70 0 1) [(.param 1)]) (.bvar 5)) (.bvar 1)) (.bvar 0)))))))))) + ]), + ("indexed", (.forallE (.forallE (.sort (.succ .zero)) (.forallE (.app (.const (.member 60 0) []) (.bvar 0)) (.sort (.param 0)))) (.forallE (.app (.app (.bvar 0) (.sort .zero)) (.const (.ctor 60 0 0) [])) (.forallE (.sort (.succ .zero)) (.forallE (.app (.const (.member 60 0) []) (.bvar 0)) (.app (.app (.bvar 3) (.bvar 1)) (.bvar 0)))))), [ + ((.lam (.forallE (.sort (.succ .zero)) (.forallE (.app (.const (.member 60 0) []) (.bvar 0)) (.sort (.param 0)))) (.lam (.app (.app (.bvar 0) (.sort .zero)) (.const (.ctor 60 0 0) [])) (.bvar 0))), (.forallE (.forallE (.sort (.succ .zero)) (.forallE (.app (.const (.member 60 0) []) (.bvar 0)) (.sort (.param 0)))) (.forallE (.app (.app (.bvar 0) (.sort .zero)) (.const (.ctor 60 0 0) [])) (.app (.app (.bvar 1) (.sort .zero)) (.const (.ctor 60 0 0) []))))) + ]), + ("functional", (.forallE (.forallE (.const (.member 50 0) []) (.sort (.param 0))) (.forallE (.forallE (.forallE (.sort .zero) (.const (.member 50 0) [])) (.forallE (.forallE (.sort .zero) (.app (.bvar 2) (.app (.bvar 1) (.bvar 0)))) (.app (.bvar 2) (.app (.const (.ctor 50 0 0) []) (.bvar 1))))) (.forallE (.const (.member 50 0) []) (.app (.bvar 2) (.bvar 0))))), [ + ((.lam (.forallE (.const (.member 50 0) []) (.sort (.param 0))) (.lam (.forallE (.forallE (.sort .zero) (.const (.member 50 0) [])) (.forallE (.forallE (.sort .zero) (.app (.bvar 2) (.app (.bvar 1) (.bvar 0)))) (.app (.bvar 2) (.app (.const (.ctor 50 0 0) []) (.bvar 1))))) (.lam (.forallE (.sort .zero) (.const (.member 50 0) [])) (.app (.app (.bvar 1) (.bvar 0)) (.lam (.sort .zero) (.app (.app (.app (.const (.member 93 0) [(.param 0)]) (.bvar 3)) (.bvar 2)) (.app (.bvar 1) (.bvar 0)))))))), (.forallE (.forallE (.const (.member 50 0) []) (.sort (.param 0))) (.forallE (.forallE (.forallE (.sort .zero) (.const (.member 50 0) [])) (.forallE (.forallE (.sort .zero) (.app (.bvar 2) (.app (.bvar 1) (.bvar 0)))) (.app (.bvar 2) (.app (.const (.ctor 50 0 0) []) (.bvar 1))))) (.forallE (.forallE (.sort .zero) (.const (.member 50 0) [])) (.app (.bvar 2) (.app (.const (.ctor 50 0 0) []) (.bvar 0))))))) + ]), + ("w", (.forallE (.sort (.succ .zero)) (.forallE (.forallE (.bvar 0) (.sort (.succ .zero))) (.forallE (.forallE (.app (.app (.const (.member 71 0) []) (.bvar 1)) (.bvar 0)) (.sort (.param 0))) (.forallE (.forallE (.bvar 2) (.forallE (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.app (.const (.member 71 0) []) (.bvar 4)) (.bvar 3))) (.forallE (.forallE (.app (.bvar 3) (.bvar 1)) (.app (.bvar 3) (.app (.bvar 1) (.bvar 0)))) (.app (.bvar 3) (.app (.app (.app (.app (.const (.ctor 71 0 0) []) (.bvar 5)) (.bvar 4)) (.bvar 2)) (.bvar 1)))))) (.forallE (.app (.app (.const (.member 71 0) []) (.bvar 3)) (.bvar 2)) (.app (.bvar 2) (.bvar 0))))))), [ + ((.lam (.sort (.succ .zero)) (.lam (.forallE (.bvar 0) (.sort (.succ .zero))) (.lam (.forallE (.app (.app (.const (.member 71 0) []) (.bvar 1)) (.bvar 0)) (.sort (.param 0))) (.lam (.forallE (.bvar 2) (.forallE (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.app (.const (.member 71 0) []) (.bvar 4)) (.bvar 3))) (.forallE (.forallE (.app (.bvar 3) (.bvar 1)) (.app (.bvar 3) (.app (.bvar 1) (.bvar 0)))) (.app (.bvar 3) (.app (.app (.app (.app (.const (.ctor 71 0 0) []) (.bvar 5)) (.bvar 4)) (.bvar 2)) (.bvar 1)))))) (.lam (.bvar 3) (.lam (.forallE (.app (.bvar 3) (.bvar 0)) (.app (.app (.const (.member 71 0) []) (.bvar 5)) (.bvar 4))) (.app (.app (.app (.bvar 2) (.bvar 1)) (.bvar 0)) (.lam (.app (.bvar 4) (.bvar 1)) (.app (.app (.app (.app (.app (.const (.member 94 0) [(.param 0)]) (.bvar 6)) (.bvar 5)) (.bvar 4)) (.bvar 3)) (.app (.bvar 1) (.bvar 0))))))))))), (.forallE (.sort (.succ .zero)) (.forallE (.forallE (.bvar 0) (.sort (.succ .zero))) (.forallE (.forallE (.app (.app (.const (.member 71 0) []) (.bvar 1)) (.bvar 0)) (.sort (.param 0))) (.forallE (.forallE (.bvar 2) (.forallE (.forallE (.app (.bvar 2) (.bvar 0)) (.app (.app (.const (.member 71 0) []) (.bvar 4)) (.bvar 3))) (.forallE (.forallE (.app (.bvar 3) (.bvar 1)) (.app (.bvar 3) (.app (.bvar 1) (.bvar 0)))) (.app (.bvar 3) (.app (.app (.app (.app (.const (.ctor 71 0 0) []) (.bvar 5)) (.bvar 4)) (.bvar 2)) (.bvar 1)))))) (.forallE (.bvar 3) (.forallE (.forallE (.app (.bvar 3) (.bvar 0)) (.app (.app (.const (.member 71 0) []) (.bvar 5)) (.bvar 4))) (.app (.bvar 3) (.app (.app (.app (.app (.const (.ctor 71 0 0) []) (.bvar 5)) (.bvar 4)) (.bvar 1)) (.bvar 0)))))))))) + ]), + ("dependentIndices", (.forallE (.forallE (.sort .zero) (.forallE (.bvar 0) (.forallE (.app (.app (.const (.member 72 0) []) (.bvar 1)) (.bvar 0)) (.sort (.param 0))))) (.forallE (.forallE (.sort .zero) (.forallE (.bvar 0) (.app (.app (.app (.bvar 2) (.bvar 1)) (.bvar 0)) (.app (.app (.const (.ctor 72 0 0) []) (.bvar 1)) (.bvar 0))))) (.forallE (.sort .zero) (.forallE (.bvar 0) (.forallE (.app (.app (.const (.member 72 0) []) (.bvar 1)) (.bvar 0)) (.app (.app (.app (.bvar 4) (.bvar 2)) (.bvar 1)) (.bvar 0))))))), [ + ((.lam (.forallE (.sort .zero) (.forallE (.bvar 0) (.forallE (.app (.app (.const (.member 72 0) []) (.bvar 1)) (.bvar 0)) (.sort (.param 0))))) (.lam (.forallE (.sort .zero) (.forallE (.bvar 0) (.app (.app (.app (.bvar 2) (.bvar 1)) (.bvar 0)) (.app (.app (.const (.ctor 72 0 0) []) (.bvar 1)) (.bvar 0))))) (.lam (.sort .zero) (.lam (.bvar 0) (.app (.app (.bvar 2) (.bvar 1)) (.bvar 0)))))), (.forallE (.forallE (.sort .zero) (.forallE (.bvar 0) (.forallE (.app (.app (.const (.member 72 0) []) (.bvar 1)) (.bvar 0)) (.sort (.param 0))))) (.forallE (.forallE (.sort .zero) (.forallE (.bvar 0) (.app (.app (.app (.bvar 2) (.bvar 1)) (.bvar 0)) (.app (.app (.const (.ctor 72 0 0) []) (.bvar 1)) (.bvar 0))))) (.forallE (.sort .zero) (.forallE (.bvar 0) (.app (.app (.app (.bvar 3) (.bvar 1)) (.bvar 0)) (.app (.app (.const (.ctor 72 0 0) []) (.bvar 1)) (.bvar 0)))))))) + ]), + ("manyProp", (.forallE (.forallE (.const (.member 78 0) []) (.sort .zero)) (.forallE (.app (.bvar 0) (.const (.ctor 78 0 0) [])) (.forallE (.app (.bvar 1) (.const (.ctor 78 0 1) [])) (.forallE (.const (.member 78 0) []) (.app (.bvar 3) (.bvar 0)))))), [ + ((.lam (.forallE (.const (.member 78 0) []) (.sort .zero)) (.lam (.app (.bvar 0) (.const (.ctor 78 0 0) [])) (.lam (.app (.bvar 1) (.const (.ctor 78 0 1) [])) (.bvar 1)))), (.forallE (.forallE (.const (.member 78 0) []) (.sort .zero)) (.forallE (.app (.bvar 0) (.const (.ctor 78 0 0) [])) (.forallE (.app (.bvar 1) (.const (.ctor 78 0 1) [])) (.app (.bvar 2) (.const (.ctor 78 0 0) [])))))), + ((.lam (.forallE (.const (.member 78 0) []) (.sort .zero)) (.lam (.app (.bvar 0) (.const (.ctor 78 0 0) [])) (.lam (.app (.bvar 1) (.const (.ctor 78 0 1) [])) (.bvar 0)))), (.forallE (.forallE (.const (.member 78 0) []) (.sort .zero)) (.forallE (.app (.bvar 0) (.const (.ctor 78 0 0) [])) (.forallE (.app (.bvar 1) (.const (.ctor 78 0 1) [])) (.app (.bvar 2) (.const (.ctor 78 0 1) [])))))) + ]), + ("singletonProp", (.forallE (.sort .zero) (.forallE (.forallE (.app (.const (.member 77 0) []) (.bvar 0)) (.sort (.param 0))) (.forallE (.forallE (.bvar 1) (.app (.bvar 1) (.app (.app (.const (.ctor 77 0 0) []) (.bvar 2)) (.bvar 0)))) (.forallE (.app (.const (.member 77 0) []) (.bvar 2)) (.app (.bvar 2) (.bvar 0)))))), [ + ((.lam (.sort .zero) (.lam (.forallE (.app (.const (.member 77 0) []) (.bvar 0)) (.sort (.param 0))) (.lam (.forallE (.bvar 1) (.app (.bvar 1) (.app (.app (.const (.ctor 77 0 0) []) (.bvar 2)) (.bvar 0)))) (.lam (.bvar 2) (.app (.bvar 1) (.bvar 0)))))), (.forallE (.sort .zero) (.forallE (.forallE (.app (.const (.member 77 0) []) (.bvar 0)) (.sort (.param 0))) (.forallE (.forallE (.bvar 1) (.app (.bvar 1) (.app (.app (.const (.ctor 77 0 0) []) (.bvar 2)) (.bvar 0)))) (.forallE (.bvar 2) (.app (.bvar 2) (.app (.app (.const (.ctor 77 0 0) []) (.bvar 3)) (.bvar 0)))))))) + ]) +] + +end Tests.Theory diff --git a/Tests/Theory/Standard.lean b/Tests/Theory/Standard.lean new file mode 100644 index 000000000..c04d45074 --- /dev/null +++ b/Tests/Theory/Standard.lean @@ -0,0 +1,132 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certificate.Build +import Tests.Theory.Certified + +open Ix.Theory + +namespace Tests.Theory.Standard + +open Ix.Theory.Model Ix.Theory.Certified Ix.Theory.Certified.Basis Ix.Theory.Certificate +open Tests.Theory.Certified (primitives) + +set_option maxRecDepth 8192 +set_option maxHeartbeats 8000000 + +def eq : ConstRef Nat := .member 100 0 +def refl : ConstRef Nat := .ctor 100 0 0 +def iff : ConstRef Nat := .member 102 0 +def ne : ConstRef Nat := .member 104 0 +def neIntro : ConstRef Nat := .ctor 104 0 0 +def propextRef : ConstRef Nat := .member 106 0 +def choiceRef : ConstRef Nat := .member 107 0 + +def propextSpec : Certified.Standard.Spec Nat := + .propext eq refl (.member 101 0) iff (.ctor 102 0 0) (.member 103 0) +def choiceSpec : Certified.Standard.Spec Nat := .choice ne neIntro (.member 105 0) + +def declaration : Nat → Const Nat + | 10 => PrimitiveSignature.falseDeclaration + | 11 => primitives.falseElimDeclaration + | 100 => Equality.shape.source 100 + | 101 => Equality.shape.recursorSource 100 101 .large true + | 102 => Iff.shape.source 102 + | 103 => Iff.shape.recursorSource 102 103 .large + | 104 => Nonempty.shape.source 104 + | 105 => Nonempty.shape.recursorSource 104 105 .small + | 106 => propextSpec.source + | 107 => choiceSpec.source + | _ => .axiom 0 (.sort .zero) .safe + +def store (alter : Nat → Const Nat → Const Nat := fun _ c => c) : Store Nat where + dom := [10, 11, 100, 101, 102, 103, 104, 105, 106, 107] + nodup := by decide + blocks b := if b ∈ [10, 11, 100, 101, 102, 103, 104, 105, 106, 107] then + some ⟨[alter b (declaration b)]⟩ else none + mem_dom b := by split <;> simp_all + +def input (n : Nat) (proof proposition : AExpr Nat) : ProofInput Nat := + ⟨store, n, proof.erase, proposition.erase⟩ + +def accepted (n : Nat) (proof proposition : AExpr Nat) : Bool := + let input := input n proof proposition + (Certificate.proofWitness? 2000 primitives input).any (acceptsCertified.{0,0} 2000 primitives input) + +#guard accepted 0 (.const propextRef []) propextSpec.type +#guard accepted 0 (.const choiceRef [.zero]) (choiceSpec.type.instL [.zero]) + +def chosen (u : VLevel) (A a : AExpr Nat) : AExpr Nat := + .appN (.const choiceRef [u]) [A, Nonempty.introduction neIntro u A a] + +-- The choice result is used at its actual positive source universe as an +-- argument to reflexivity, under arbitrary A and a : A. +def choiceRefl (u : VLevel) : AExpr Nat := + .lam .always (.sort u) (.lam .always (.bvar 0) + (Equality.reflexivity refl u (.bvar 1) (chosen u (.bvar 1) (.bvar 0)))) +def choiceReflType (u : VLevel) : AExpr Nat := + .forallE .always (.sort u) (.forallE .always (.bvar 0) + (Equality.applied eq u (.bvar 1) (chosen u (.bvar 1) (.bvar 0)) (chosen u (.bvar 1) (.bvar 0)))) + +#guard accepted 1 (choiceRefl (.param 0)) (choiceReflType (.param 0)) +#guard accepted 0 (choiceRefl (.succ .zero)) (choiceReflType (.succ .zero)) +#guard accepted 0 (choiceRefl (.succ (.succ .zero))) (choiceReflType (.succ (.succ .zero))) + +def kDomain (P : AExpr Nat) : AExpr Nat := + Equality.applied eq (.succ .zero) (.sort .zero) P P +def kMotive (P : AExpr Nat) : AExpr Nat := + .lam .never (.sort .zero) + (.lam .never (Equality.applied eq (.succ .zero) (.sort .zero) (P.liftN 1) (.bvar 0)) (.sort .zero)) +def kComputed (P proof : AExpr Nat) : AExpr Nat := + .appN (.const (.member 101 0) [.succ .zero, .succ .zero]) + [.sort .zero, P, kMotive P, P, P, proof] +def kProof : AExpr Nat := + .lam .always (.sort .zero) (.lam .always (.bvar 0) + (.lam .always (kDomain (.bvar 1)) (.bvar 1))) +def kProposition : AExpr Nat := + .forallE .always (.sort .zero) (.forallE .always (.bvar 0) + (.forallE .always (kDomain (.bvar 1)) (kComputed (.bvar 2) (.bvar 0)))) + +-- The major premise is an arbitrary proof of reflexive equality, not a +-- constructor application. Admission permits the exact Eq K metadata, and +-- typed proof irrelevance plus the admitted iota law justify this conversion. +#guard accepted 0 kProof kProposition + +#guard Equality.shape.SupportsK (β := Nat) +#guard ¬ Nonempty.shape.SupportsK (β := Nat) + +def propextInput := input 0 (.const propextRef []) propextSpec.type +def propextWitness := Certificate.proofWitness? 2000 primitives propextInput + +#guard propextWitness.isSome + +def falseAxiom (b : Nat) (c : Const Nat) : Const Nat := + if b = 106 then .axiom 0 primitives.falseExpr .safe else c +def unsafeAxiom (b : Nat) (c : Const Nat) : Const Nat := + if b = 106 then .axiom 0 propextSpec.type.erase .unsafe else c +def wrongArity (b : Nat) (c : Const Nat) : Const Nat := + if b = 106 then .axiom 1 propextSpec.type.erase .safe else c + +#guard [falseAxiom, unsafeAxiom, wrongArity].all fun alter => propextWitness.any fun witness => + !acceptsCertified.{0,0} 2000 primitives { propextInput with store := store alter } witness + +-- A standard schema cannot be admitted before its realized prerequisites. +#guard propextWitness.any fun witness => + let axioms := witness.declarations.filter fun declaration => match declaration with + | .standard _ => true + | _ => false + let rest := witness.declarations.filter fun declaration => match declaration with + | .standard _ => false + | _ => true + !acceptsCertified.{0,0} 2000 primitives propextInput { witness with declarations := axioms ++ rest } + +-- Removing the actual dependent eliminator's certificate is also a failure. +#guard propextWitness.any fun witness => + let declarations := witness.declarations.filter fun declaration => match declaration with + | .ordinary w => w.source != 100 + | _ => true + !acceptsCertified.{0,0} 2000 primitives propextInput { witness with declarations } + +end Tests.Theory.Standard diff --git a/Tests/Theory/Structure.lean b/Tests/Theory/Structure.lean new file mode 100644 index 000000000..b4e95e2ec --- /dev/null +++ b/Tests/Theory/Structure.lean @@ -0,0 +1,159 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Tests.Theory.Standard + +open Ix.Theory + +namespace Tests.Theory.Structure + +open Ix.Theory.Model Ix.Theory.Certified Ix.Theory.Certified.Basis Ix.Theory.Certificate +open Tests.Theory.Certified (primitives) + +set_option maxRecDepth 8192 +set_option maxHeartbeats 8000000 + +def box : Certified.Structure.Description Nat := + ⟨1, [], [⟨.sort (.param 0), .succ (.param 0)⟩, ⟨.bvar 0, .param 0⟩], .succ (.param 0)⟩ + +def pair : Certified.Structure.Description Nat := + ⟨1, [.sort (.param 0)], [⟨.bvar 0, .param 0⟩, ⟨.bvar 1, .param 0⟩], .param 0⟩ + +def store (d : Certified.Structure.Description Nat) (alter : Nat → Const Nat → Const Nat := fun _ c => c) : Store Nat where + dom := [10, 11, 100, 101, 200, 201] + nodup := by decide + blocks b := if b ∈ [10, 11, 100, 101, 200, 201] then some ⟨[alter b (match b with + | 200 => d.ordinary.source 200 + | 201 => d.ordinary.recursorSource 200 201 .large + | _ => Standard.declaration b)]⟩ else none + mem_dom b := by split <;> simp_all + +def input (d : Certified.Structure.Description Nat) (n : Nat) (proof proposition : AExpr Nat) : ProofInput Nat := + ⟨store d, n, proof.erase, proposition.erase⟩ + +def accepted (d : Certified.Structure.Description Nat) (n : Nat) (proof proposition : AExpr Nat) : Bool := + let input := input d n proof proposition + (Certificate.proofWitness? 2400 primitives input).any (acceptsCertified.{0,0} 4000 primitives input) + +def boxType (u : VLevel) : AExpr Nat := .const (.member 200 0) [u] +def boxMk (u : VLevel) (A a : AExpr Nat) : AExpr Nat := .appN (.const (.ctor 200 0 0) [u]) [A, a] +def proj (i : Nat) (s : AExpr Nat) : AExpr Nat := .proj (.member 200 0) i s + +def boxProof : AExpr Nat := + .lam .always (.sort .zero) (.lam .always (.bvar 0) (proj 1 (boxMk .zero (.bvar 1) (.bvar 0)))) +def boxProposition : AExpr Nat := + .forallE .always (.sort .zero) (.forallE .always (.bvar 0) (.bvar 1)) + +#guard accepted box 0 boxProof boxProposition + +def etaProof (u : VLevel) : AExpr Nat := + .lam .always (boxType u) (Equality.reflexivity Standard.refl (.succ u) (boxType u) (.bvar 0)) +def etaProposition (u : VLevel) : AExpr Nat := + .forallE .always (boxType u) (Equality.applied Standard.eq (.succ u) (boxType u) + (boxMk u (proj 0 (.bvar 0)) (proj 1 (.bvar 0))) (.bvar 0)) + +#guard accepted box 0 (etaProof .zero) (etaProposition .zero) +#guard accepted box 1 (etaProof (.param 0)) (etaProposition (.param 0)) + +def pairProof : AExpr Nat := + .lam .always (.sort .zero) (.lam .always (.bvar 0) + (proj 1 (.appN (.const (.ctor 200 0 0) [.zero]) [.bvar 1, .bvar 0, .bvar 0]))) + +#guard accepted pair 0 pairProof boxProposition + +def fieldProof (u : VLevel) : AExpr Nat := + .lam .always (.sort u) (.lam .always (.bvar 0) + (Equality.reflexivity Standard.refl u (.bvar 1) (.bvar 0))) +def fieldProposition (u : VLevel) : AExpr Nat := + .forallE .always (.sort u) (.forallE .always (.bvar 0) + (Equality.applied Standard.eq u (.bvar 1) (proj 1 (boxMk u (.bvar 1) (.bvar 0))) (.bvar 0))) + +#guard accepted box 0 (fieldProof .zero) (fieldProposition .zero) +#guard accepted box 0 (fieldProof (.succ .zero)) (fieldProposition (.succ .zero)) +#guard accepted box 1 (fieldProof (.param 0)) (fieldProposition (.param 0)) + +def pairType (u : VLevel) (A : AExpr Nat) : AExpr Nat := .app (boxType u) A +def pairEtaProof (u : VLevel) : AExpr Nat := + .lam .always (.sort u) (.lam .always (pairType u (.bvar 0)) + (Equality.reflexivity Standard.refl u (pairType u (.bvar 1)) (.bvar 0))) +def pairEtaProposition (u : VLevel) : AExpr Nat := + .forallE .always (.sort u) (.forallE .always (pairType u (.bvar 0)) + (Equality.applied Standard.eq u (pairType u (.bvar 1)) + (.appN (.const (.ctor 200 0 0) [u]) [.bvar 1, proj 0 (.bvar 0), proj 1 (.bvar 0)]) (.bvar 0))) + +#guard accepted pair 0 (pairEtaProof .zero) (pairEtaProposition .zero) +#guard accepted pair 1 (pairEtaProof (.param 0)) (pairEtaProposition (.param 0)) + +def boxInput := input box 0 boxProof boxProposition +def boxWitness := Certificate.proofWitness? 2400 primitives boxInput + +#guard boxWitness.isSome +#guard boxWitness.any fun w => w.declarations.any fun d => match d with + | .structure _ => true + | _ => false + +-- These mutations are sent to the validator with the original successful +-- certificate, so a producer decline is not the rejection being tested. +def wrongFieldCount (b : Nat) (c : Const Nat) : Const Nat := + if b = 200 then match c with + | .induct n p i type ctors safety => .induct n p i type (ctors.map fun c => { c with nfields := 1 }) safety + | _ => c + else c +def wrongRecursor (b : Nat) (c : Const Nat) : Const Nat := + if b = 201 then match c with + | .recursor n p i m r type rules _ safety => .recursor n p i m r type rules true safety + | _ => c + else c + +#guard [wrongFieldCount, wrongRecursor].all fun alter => boxWitness.any fun w => + !acceptsCertified.{0,0} 4000 primitives { boxInput with store := store box alter } w + +def badProjectionProof (r : ConstRef Nat) (i : Nat) : AExpr Nat := + .lam .always (.sort .zero) (.lam .always (.bvar 0) + (.proj r i (boxMk .zero (.bvar 1) (.bvar 0)))) + +#guard [(.member 200 0, 2), (.member 100 0, 1)].all fun (r, i) => boxWitness.any fun w => + !acceptsCertified.{0,0} 4000 primitives { boxInput with proof := (badProjectionProof r i).erase } w + +#guard boxWitness.any fun w => + let declarations := w.declarations.map fun declaration => match declaration with + | .structure s => .ordinary s.facts.block + | _ => declaration + !acceptsCertified.{0,0} 4000 primitives boxInput { w with declarations } + +#guard boxWitness.any fun w => + let declarations := w.declarations.map fun declaration => match declaration with + | .structure s => .structure { s with facts := { s.facts with description := + { s.facts.description with fields := s.facts.description.fields.map fun f => { f with level := .zero } } } } + | _ => declaration + !acceptsCertified.{0,0} 4000 primitives boxInput { w with declarations } + +#guard boxWitness.any fun w => + let declarations := w.declarations.map fun declaration => match declaration with + | .structure s => .structure { s with iota := s.iota.reverse } + | _ => declaration + !acceptsCertified.{0,0} 4000 primitives boxInput { w with declarations } + +-- A Prop constructor with a data field remains an ordinary inductive. Even +-- a hand-built structure description cannot grant a projection from it. +def nonempty : Certified.Structure.Description Nat := + ⟨1, [.sort (.param 0)], [⟨.bvar 0, .param 0⟩], .zero⟩ +def nonemptyStore : Store Nat := store nonempty (fun b c => + if b = 201 then nonempty.ordinary.recursorSource 200 201 .small else c) +def nonemptyBlock := Certificate.Ordinary.sourceBlock? 2400 primitives.environment nonemptyStore 200 201 + +#guard nonemptyBlock.isSome +#guard nonemptyBlock.any fun block => + (Certified.Ordinary.checkBlock.{0,0} 4000 primitives.environment nonemptyStore block).isSome && + (Certificate.Structure.description? block).isNone +#guard nonemptyBlock.any fun block => + let domains := Certificate.Ordinary.domains? 2400 nonempty.universes + (nonempty.ordinary.publishedEnvironment primitives.environment 200 201 .small) [] + (nonempty.projectionDomains 200) + domains.any fun domains => + let witness : Certified.Structure.FactsWitness Nat := ⟨nonempty, block, [.bvar], domains⟩ + (Certified.Structure.checkFacts.{0,0} 4000 primitives.environment nonemptyStore witness).isNone + +end Tests.Theory.Structure diff --git a/Tests/Theory/Suggestions.lean b/Tests/Theory/Suggestions.lean new file mode 100644 index 000000000..86fbb37db --- /dev/null +++ b/Tests/Theory/Suggestions.lean @@ -0,0 +1,55 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Certificate.Build +import Tests.Theory.Acceptance + +open Ix.Theory + +namespace Tests.Theory.Suggestions + +open Ix.Theory.Certified Ix.Theory.Model Ix.Theory.Certificate +open Tests.Theory.Acceptance +open Tests.Theory.Certified (primitives) + +/-- Search success is deliberately insufficient: every test runs the public +acceptance validator on the produced ordinary witness data. -/ +def autoAccept (input : ProofInput Nat) : Bool := + (proofWitness? 300 primitives input).any (acceptsCertified.{0,0} 300 primitives input) + +#guard autoAccept identityInput +#guard autoAccept (idDefinitionInput .definition) +#guard autoAccept (idDefinitionInput .theorem) +#guard autoAccept (idDefinitionInput .opaque) +#guard autoAccept { idDefinitionInput with proof := largeIdentityUse.erase } +#guard autoAccept eliminatorInput +#guard !autoAccept { identityInput with proposition := primitives.falseExpr } + +-- Dependent application with a nonempty-domain possibility: +-- λ A B f x => f x, where B : A → Prop and f : (x : A) → B x. +def dependentApplication (u : VLevel) : VExpr Nat := + .lam (.sort u) + (.lam (.forallE (.bvar 0) (.sort .zero)) + (.lam (.forallE (.bvar 1) (.app (.bvar 1) (.bvar 0))) + (.lam (.bvar 2) (.app (.bvar 1) (.bvar 0))))) + +def dependentApplicationType (u : VLevel) : VExpr Nat := + .forallE (.sort u) + (.forallE (.forallE (.bvar 0) (.sort .zero)) + (.forallE (.forallE (.bvar 1) (.app (.bvar 1) (.bvar 0))) + (.forallE (.bvar 2) (.app (.bvar 2) (.bvar 0))))) + +def dependentInput (n : Nat) (u : VLevel) : ProofInput Nat := + ⟨prelude, n, dependentApplication u, dependentApplicationType u⟩ + +#guard autoAccept (dependentInput 1 (.param 0)) +#guard autoAccept (dependentInput 0 .zero) +#guard autoAccept (dependentInput 0 (.succ .zero)) +#guard autoAccept (dependentInput 0 (.succ (.succ .zero))) + +-- An out-of-scope universe is still rejected by the producer and validator. +#guard !autoAccept (dependentInput 0 (.param 0)) + +end Tests.Theory.Suggestions diff --git a/Tests/Theory/certified-foundation.txt b/Tests/Theory/certified-foundation.txt new file mode 100644 index 000000000..df3ab3819 --- /dev/null +++ b/Tests/Theory/certified-foundation.txt @@ -0,0 +1,10669 @@ +certified root Ix.Theory.Model.SetTheory.empty_exists + type: ∀ {V : Type u} [inst : Model.SetTheory V], ∃ e, ∀ (z : V), ¬Model.SetTheory.Mem z e + axioms: [] + constants: 55 + project modules: [Ix.Theory.Model.SetTheory.Core, Ix.Theory.Model.SetTheory.Derive.Empty] +certified root Ix.Theory.Model.SetTheory.not_mem_empty + type: ∀ {V : Type u} [inst : Model.SetTheory V] (z : V), ¬Model.SetTheory.Mem z Model.SetTheory.empty + axioms: [Classical.choice] + constants: 69 + project modules: [Ix.Theory.Model.SetTheory.Core, Ix.Theory.Model.SetTheory.Derive.Empty] +certified root Ix.Theory.VLevel.eval_inst + type: ∀ {ls : List VLevel} {ns : List Nat} {l : VLevel}, + VLevel.eval ns (VLevel.inst ls l) = VLevel.eval (List.map (VLevel.eval ns) ls) l + axioms: [propext] + constants: 669 + project modules: [Ix.Theory.VLevel] +certified root Ix.Theory.Certified.PropWhen.eq_iff_holds + type: ∀ (p q : Certified.PropWhen), + p = q ↔ ∀ (v : Nat → Nat), Certified.PropWhen.holds v p = Certified.PropWhen.holds v q + axioms: [propext, Classical.choice, Quot.sound] + constants: 1705 + project modules: [Ix.Theory.Certified.PropWhen] +certified root Ix.Theory.Certified.validateZero?_sound + type: ∀ {n : Nat} {l : VLevel} {raw : Option (List Nat)} {p : Certified.PropWhen}, + Certified.validateZero? n l raw = some p → + VLevel.WF n l ∧ p.toRaw = raw ∧ Certified.PropWhen.WF n p ∧ p = Certified.zeroCondition l + axioms: [propext, Quot.sound] + constants: 1602 + project modules: [Ix.Theory.VLevel, Ix.Theory.Certified.Level, Ix.Theory.Certified.PropWhen] +certified root Ix.Theory.Certified.zeroCondition_correct + type: ∀ (l : VLevel) (values : List Nat), + Certified.PropWhen.holds (fun x => values.getD x 0) (Certified.zeroCondition l) = (VLevel.eval values l == 0) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1753 + project modules: [Ix.Theory.VLevel, Ix.Theory.Certified.Level, Ix.Theory.Certified.PropWhen] +certified root Ix.Theory.Certified.zeroCondition_inst + type: ∀ (l : VLevel) (levels : List VLevel), + Certified.zeroCondition (VLevel.inst levels l) = Certified.instCondition levels (Certified.zeroCondition l) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1797 + project modules: [Ix.Theory.VLevel, Ix.Theory.Certified.Level, Ix.Theory.Certified.PropWhen] +certified root Ix.Theory.Certified.instCondition_comp + type: ∀ (p : Certified.PropWhen) (levels levels' : List VLevel), + Certified.instCondition levels' (Certified.instCondition levels p) = + Certified.instCondition (List.map (VLevel.inst levels') levels) p + axioms: [propext, Classical.choice, Quot.sound] + constants: 1803 + project modules: [Ix.Theory.VLevel, Ix.Theory.Certified.Level, Ix.Theory.Certified.PropWhen] +certified root Ix.Theory.Model.readAnnotations? + type: {β : Type u} → (n k : Nat) → (source : VExpr β) → Model.AnnotationTree → Option (Model.Reading n k source) + axioms: [propext] + constants: 542 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated] +certified root Ix.Theory.Model.interp_inst + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] (e a : Model.AExpr β) (constants : Model.Assignment β V) + (levels : List Nat) (env : Nat → V) (k : Nat), + Model.interp constants levels env (e.inst a k) = + Model.interp constants levels + (Model.Valuation.insert k (Model.interp constants levels (Model.Valuation.skip k 0 env) a) env) e + axioms: [propext, Classical.choice, Quot.sound] + constants: 1621 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.interp_instL + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] (e : Model.AExpr β) (constants : Model.Assignment β V) + (levels : List Nat) (env : Nat → V) (ls : List VLevel), + Model.interp constants levels env (Model.AExpr.instL ls e) = + Model.interp constants (List.map (VLevel.eval levels) ls) env e + axioms: [propext, Classical.choice, Quot.sound] + constants: 1863 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.interp_rename + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {γ : Type w} (e : Model.AExpr β) (mapping : β → γ) + (constants : Model.Assignment β V) (constants' : Model.Assignment γ V), + (∀ (r : ConstRef β) (ls : List Nat), constants' (ConstRef.rename mapping r) ls = constants r ls) → + ∀ (levels : List Nat) (env : Nat → V), + Model.interp constants' levels env (Model.AExpr.rename mapping e) = Model.interp constants levels env e + axioms: [propext, Classical.choice, Quot.sound] + constants: 725 + project modules: [Ix.Theory.Ref, + Ix.Theory.Rename, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.piR_mem_univ + type: ∀ {V : Type u} [inst : Model.SetTheory V] {a b : Nat} {A : V} {B : V → V}, + Model.SetTheory.Mem A (Model.SetTheory.univ a) → + (∀ (x : V), Model.SetTheory.Mem x A → Model.SetTheory.Mem (B x) (Model.SetTheory.univ b)) → + Model.SetTheory.Mem (Model.SetModel.piR b A B) (Model.SetTheory.univ (VLevel.natIMax a b)) + axioms: [propext, Classical.choice, Quot.sound] + constants: 379 + project modules: [Ix.Theory.VLevel, + Ix.Theory.Model.Value, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Model.falseValue_uninhabited + type: ∀ {V : Type u} [inst : Model.SetTheory V] (x : V), ¬Model.SetTheory.Mem x Model.falseValue + axioms: [Classical.choice] + constants: 71 + project modules: [Ix.Theory.Model.Value, Ix.Theory.Model.SetTheory.Core, Ix.Theory.Model.SetTheory.Derive.Empty] +certified root Ix.Theory.Model.falseElimValue_mem + type: ∀ {V : Type u} [inst : Model.SetTheory V] (level : Nat), + Model.SetTheory.Mem (Model.falseElimValue level) (Model.falseElimType level) + axioms: [propext, Classical.choice, Quot.sound] + constants: 290 + project modules: [Ix.Theory.Model.Value, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.PrimitiveSignature.validate_iff + type: ∀ {β : Type u} [inst : DecidableEq β] (signature : Certified.PrimitiveSignature β) (store : Store β), + signature.validate store = true ↔ + store.lookup signature.falseType = some Certified.PrimitiveSignature.falseDeclaration ∧ + store.lookup signature.falseElim = some signature.falseElimDeclaration + axioms: [propext] + constants: 895 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Policy] +certified root Ix.Theory.Certified.readDefinition? + type: {β : Type u} → + (source : Const β) → Model.AnnotationTree → Model.AnnotationTree → Option { entry // entry.erase = source } + axioms: [propext] + constants: 584 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated] +certified root Ix.Theory.Certified.LevelEq.check_sound + type: ∀ {n : Nat} {a b : VLevel}, + Certified.LevelEq.check n a b = true → + VLevel.WF n a ∧ VLevel.WF n b ∧ ∀ (values : List Nat), VLevel.eval values a = VLevel.eval values b + axioms: [propext, Quot.sound] + constants: 1747 + project modules: [Ix.Theory.VLevel, Ix.Theory.Certified.LevelEq] +certified root Ix.Theory.Certified.LevelEq.leq_sound + type: ∀ {a b : VLevel}, + Certified.LevelEq.leq a b = true → ∀ (values : List Nat), VLevel.eval values a ≤ VLevel.eval values b + axioms: [propext, Quot.sound] + constants: 1655 + project modules: [Ix.Theory.VLevel, Ix.Theory.Certified.LevelEq] +certified root Ix.Theory.Model.wellDenoted_inst + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] (e a : Model.AExpr β) (constants : Model.Assignment β V) + (levels : List Nat) (env : Nat → V) (k : Nat), + Model.WellDenoted constants levels (Model.Valuation.skip k 0 env) a → + Model.WellDenoted constants levels + (Model.Valuation.insert k (Model.interp constants levels (Model.Valuation.skip k 0 env) a) env) e → + Model.WellDenoted constants levels env (e.inst a k) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1635 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.wellDenoted_beta + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {constants : Model.Assignment β V} {levels : List Nat} + {env : Nat → V} {p : Certified.PropWhen} {A b a : Model.AExpr β}, + Model.WellDenoted constants levels env (Model.AExpr.lam p A b) → + Model.WellDenoted constants levels env a → + Model.SetTheory.Mem (Model.interp constants levels env a) (Model.interp constants levels env A) → + Model.WellDenoted constants levels env (b.inst a) ∧ + Model.interp constants levels env ((Model.AExpr.lam p A b).app a) = + Model.interp constants levels env (b.inst a) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1681 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.extend_definition + type: ∀ {β : Type u} [inst : DecidableEq β] {V : Type v} [inst_1 : Model.SetTheory V] {entries : Model.Environment β} + {r : ConstRef β} {entry : Model.ConstantEntry β} {body : Model.AExpr β} {level : VLevel}, + entries.WF → + entries r = none → + entry.body = some body → + entry.equations = [] → + entry.facts = [] → + Model.AExpr.Scope entry.universes 0 body → + Model.AExpr.ReferencesIn entries entry.type → + Model.AExpr.ReferencesIn entries body → + Model.TypingClaim entries [] entry.type (Model.AExpr.sort level) → + Model.TypingClaim entries [] body entry.type → + ∀ (constants : Model.Assignment β V), + Model.Realizes constants entries → + ∃ constants', + Model.Realizes constants' (entries.insert r entry) ∧ + Model.Assignment.AgreesOn entries constants constants' + axioms: [propext, Classical.choice, Quot.sound] + constants: 1642 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.ConversionClaim.equation + type: ∀ {β : Type u} {entries : Model.Environment β} {Γ : Model.Context β} {r : ConstRef β} + {entry : Model.ConstantEntry β} {law : Model.ConstantEquation β} {ls : List VLevel}, + entries r = some entry → + law ∈ entry.equations → + ls.length = entry.universes → + Model.ConversionClaim entries Γ (Model.AExpr.instL ls law.lhs) (Model.AExpr.instL ls law.rhs) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1894 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.IndexedContainer.closed_exists + type: ∀ {V : Type u} [inst : Model.SetTheory V] {D : Model.IndexedContainer V} {w : Nat}, + D.WF w → ∃ L, Model.SetTheory.IsClosedFam w D.indices (D.step w) L + axioms: [propext, Classical.choice, Quot.sound] + constants: 1795 + project modules: [Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Model.IndexedContainer.carrier_eq + type: ∀ {V : Type u} [inst : Model.SetTheory V] {D : Model.IndexedContainer V} {w : Nat}, + D.WF w → ∀ {i : V}, Model.SetTheory.Mem i D.indices → Model.SetTheory.app (D.carrier w) i = D.fibre w (D.carrier w) i + axioms: [propext, Classical.choice, Quot.sound] + constants: 1803 + project modules: [Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Model.IndexedContainer.induction + type: ∀ {V : Type u} [inst : Model.SetTheory V] {D : Model.IndexedContainer V} {w : Nat}, + D.WF w → + ∀ (P : V → V → Prop), + (∀ (i : V), + Model.SetTheory.Mem i D.indices → + ∀ (a : V), + Model.SetTheory.Mem a (D.shapes i) → + ∀ (g : V), + Model.SetTheory.Mem g + (Model.SetTheory.piSet (D.positions a) fun p => + Model.SetTheory.app (D.carrier w) (D.target a p)) → + (∀ (p : V), Model.SetTheory.Mem p (D.positions a) → P (D.target a p) (Model.SetTheory.app g p)) → + P i (Model.IndexedContainer.node w a g)) → + ∀ (i : V), + Model.SetTheory.Mem i D.indices → + ∀ (x : V), Model.SetTheory.Mem x (Model.SetTheory.app (D.carrier w) i) → P i x + axioms: [propext, Classical.choice, Quot.sound] + constants: 1801 + project modules: [Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Model.IndexedContainer.child_wellFounded + type: ∀ {V : Type u} [inst : Model.SetTheory V] {D : Model.IndexedContainer V} {w : Nat}, + D.WF w → D.LargeElim w → WellFounded (D.Child w) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1812 + project modules: [Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Model.IndexedContainer.fold_node + type: ∀ {V : Type u} [inst : Model.SetTheory V] {D : Model.IndexedContainer V} {w : Nat} (hD : D.WF w) + (hlarge : D.LargeElim w) (algebra : V → V → V → V → V) {i a g : V} (hi : Model.SetTheory.Mem i D.indices) + (ha : Model.SetTheory.Mem a (D.shapes i)) + (hg : + Model.SetTheory.Mem g + (Model.SetTheory.piSet (D.positions a) fun p => Model.SetTheory.app (D.carrier w) (D.target a p))), + Model.IndexedContainer.fold hD hlarge algebra ⟨(i, Model.IndexedContainer.node w a g), ⋯⟩ = + algebra i a g (Model.IndexedContainer.childResults hD hi ha hg (Model.IndexedContainer.fold hD hlarge algebra)) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1845 + project modules: [Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Model.IndexedContainer.fold_mem + type: ∀ {V : Type u} [inst : Model.SetTheory V] {D : Model.IndexedContainer V} {w : Nat} (hD : D.WF w) + (hlarge : D.LargeElim w) {M : V → V → V} {algebra : V → V → V → V → V}, + D.AlgebraTyping w M algebra → + ∀ (z : D.Element w), Model.SetTheory.Mem (Model.IndexedContainer.fold hD hlarge algebra z) (M z.val.fst z.val.snd) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1846 + project modules: [Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Model.IndexedContainer.small_elim + type: ∀ {V : Type u} [inst : Model.SetTheory V] {D : Model.IndexedContainer V} {w : Nat}, + D.WF w → + ∀ {M : V → V → V} {algebra : V → V → V → V → V}, + (∀ (i : V), + Model.SetTheory.Mem i D.indices → + ∀ (x : V), + Model.SetTheory.Mem x (Model.SetTheory.app (D.carrier w) i) → + Model.SetTheory.Mem (M i x) Model.SetTheory.univZero) → + D.AlgebraTyping w M algebra → + ∀ (i : V), + Model.SetTheory.Mem i D.indices → + ∀ (x : V), + Model.SetTheory.Mem x (Model.SetTheory.app (D.carrier w) i) → + Model.SetTheory.Mem Model.SetTheory.pt (M i x) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1809 + project modules: [Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Model.Telescope.applyN_curry + type: ∀ {V : Type v} [inst : Model.SetTheory V] {w n : Nat} (T : Model.SetTheory.Tower.TeleS V n) {f R : List V → V} + {xs : List V}, + Model.SetTheory.Tower.FitsS T xs → + (∀ (ys : List V), Model.SetTheory.Tower.FitsS T ys → Model.SetTheory.Mem (f ys) (R ys)) → + (w = 0 → + ∀ (ys : List V), Model.SetTheory.Tower.FitsS T ys → Model.SetTheory.Mem (R ys) Model.SetTheory.univZero) → + Model.Telescope.applyN (Model.Telescope.curry w T f) xs = f xs + axioms: [propext, Classical.choice, Quot.sound] + constants: 346 + project modules: [Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep] +certified root Ix.Theory.Model.Telescope.curry_applyN + type: ∀ {V : Type v} [inst : Model.SetTheory V] {w n : Nat} (T : Model.SetTheory.Tower.TeleS V n) {f : V} + {R : List V → V}, + Model.SetTheory.Mem f (Model.Telescope.piN w T R) → + (w = 0 → ∀ (ys : List V), Model.SetTheory.Tower.FitsS T ys → Model.SetTheory.Mem (R ys) Model.SetTheory.univZero) → + Model.Telescope.curry w T (Model.Telescope.applyN f) = f + axioms: [propext, Classical.choice, Quot.sound] + constants: 352 + project modules: [Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep] +certified root Ix.Theory.Certified.verifyTelescope_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel n : Nat} {entries : Model.Environment β} {bound : Option VLevel} + {Γ : Model.Context β} {domains : List (Model.AExpr β)} {witnesses : List (Certified.DomainWitness β)} + {result : + Certified.CheckedClaim + (Model.Telescope.Formed entries Γ domains ∧ Certified.TelescopeBound entries Γ domains bound)}, + Certified.verifyTelescope fuel n entries bound Γ domains witnesses = some result → + Model.Telescope.Formed entries Γ domains ∧ Certified.TelescopeBound entries Γ domains bound + axioms: [propext, Classical.choice, Quot.sound] + constants: 2583 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.verifyArguments_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel n : Nat} {entries : Model.Environment β} {Γ : Model.Context β} + {domains args : List (Model.AExpr β)} {witnesses : List (Certified.TypingWitness β)} + {result : Certified.CheckedClaim (Certified.ArgumentsFit entries Γ domains args)}, + Certified.verifyArguments fuel n entries Γ domains args witnesses = some result → + Certified.ArgumentsFit entries Γ domains args + axioms: [propext, Classical.choice, Quot.sound] + constants: 2579 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Ordinary.checkShape_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {entries : Model.Environment β} {store : Store β} + {source : β} {witness : Certified.Ordinary.ShapeWitness β} + {result : Certified.CheckedClaim (Certified.Ordinary.CheckedShape entries store source witness.shape)}, + Certified.Ordinary.checkShape fuel entries store source witness = some result → + Certified.Ordinary.CheckedShape entries store source witness.shape + axioms: [propext, Classical.choice, Quot.sound] + constants: 2891 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Ordinary.checkLarge_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {entries : Model.Environment β} + {witness : Certified.Ordinary.ShapeWitness β} + {result : Certified.CheckedClaim (Certified.Ordinary.LargeEvidence entries witness.shape)}, + Certified.Ordinary.checkLarge fuel entries witness = some result → + Certified.Ordinary.LargeEvidence entries witness.shape + axioms: [propext, Classical.choice, Quot.sound] + constants: 2612 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Ordinary.Shape.container_wf + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {shape : Certified.Ordinary.Shape β} {constants : Model.Assignment β V} {levels : List Nat} {env : Nat → V} + {store : Store β} {source : β}, + Certified.Ordinary.CheckedShape entries store source shape → + Model.Realizes constants entries → + Model.Context.Valid constants levels shape.parameterContext env → + (shape.container constants levels env).WF (VLevel.eval levels shape.level) + axioms: [propext, Classical.choice, Quot.sound] + constants: 2032 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Ordinary.Shape.constructorValue_mem + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {shape : Certified.Ordinary.Shape β} {constants : Model.Assignment β V} {levels : List Nat} {env : Nat → V} + {ctor : Certified.Ordinary.Constructor β} {store : Store β} {source : β}, + Certified.Ordinary.CheckedShape entries store source shape → + Model.Realizes constants entries → + Model.Context.Valid constants levels shape.parameterContext env → + ∀ {i : Nat}, + shape.constructors[i]? = some ctor → + ∀ {xs fs : List V}, + Model.SetTheory.Tower.FitsS (Model.Telescope.interpret constants levels env ctor.fields) xs → + shape.RecursiveValuesFit constants levels env ctor xs fs → + Model.SetTheory.Mem (shape.constructorValue constants levels env ctor i xs fs) + (Model.SetTheory.app (shape.carrier constants levels env) + (Model.SetTheory.Tower.mkTower + (List.map (Model.interp constants levels (Model.Telescope.extend env xs)) ctor.indices))) + axioms: [propext, Classical.choice, Quot.sound] + constants: 2277 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Ordinary.Shape.decodeField_branches + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {shape : Certified.Ordinary.Shape β} {constants : Model.Assignment β V} {levels : List Nat} {env : Nat → V} + {ctor : Certified.Ordinary.Constructor β} {i j : Nat} {field : Certified.Ordinary.RecursiveField β}, + shape.constructors[i]? = some ctor → + ctor.recursive[j]? = some field → + Certified.Ordinary.ConstructorEvidence entries shape ctor → + Model.Realizes constants entries → + Model.Context.Valid constants levels shape.parameterContext env → + ∀ {xs fs : List V}, + Model.SetTheory.Tower.FitsS (Model.Telescope.interpret constants levels env ctor.fields) xs → + shape.RecursiveValuesFit constants levels env ctor xs fs → + shape.decodeField constants levels env xs j field (shape.branches constants levels env ctor i xs fs) = + fs.getD j Model.SetTheory.empty + axioms: [propext, Classical.choice, Quot.sound] + constants: 1230 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Ordinary.Shape.branches_decodeFields + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {shape : Certified.Ordinary.Shape β} {constants : Model.Assignment β V} {levels : List Nat} {env : Nat → V} + {ctor : Certified.Ordinary.Constructor β} {i : Nat}, + shape.constructors[i]? = some ctor → + Certified.Ordinary.ConstructorEvidence entries shape ctor → + Model.Realizes constants entries → + Model.Context.Valid constants levels shape.parameterContext env → + ∀ {xs : List V}, + Model.SetTheory.Tower.FitsS (Model.Telescope.interpret constants levels env ctor.fields) xs → + ∀ {g : V}, + Model.SetTheory.Mem g + (Model.SetTheory.piSet + (shape.positions constants levels env + (Model.SetTheory.Tower.inj i (Model.SetTheory.Tower.mkTower xs))) + fun p => + Model.SetTheory.app (shape.carrier constants levels env) + (shape.targetIndex constants levels env + (Model.SetTheory.Tower.inj i (Model.SetTheory.Tower.mkTower xs)) p)) → + shape.branches constants levels env ctor i xs (shape.decodeFields constants levels env ctor xs g) = g + axioms: [propext, Classical.choice, Quot.sound] + constants: 1269 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Ordinary.Shape.container_large + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {shape : Certified.Ordinary.Shape β}, + Certified.Ordinary.LargeEvidence entries shape → + ∀ {constants : Model.Assignment β V}, + Model.Realizes constants entries → + ∀ {levels : List Nat} {env : Nat → V}, + Model.Context.Valid constants levels shape.parameterContext env → + (shape.container constants levels env).LargeElim (VLevel.eval levels shape.level) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1015 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Ordinary.Shape.largeValue_iota + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} {store : Store β} + {source : β} {shape : Certified.Ordinary.Shape β} {constants : Model.Assignment β V} {levels : List Nat} + {env : Nat → V}, + Certified.Ordinary.CheckedShape entries store source shape → + Model.Realizes constants entries → + Model.Context.Valid constants levels shape.parameterContext env → + ∀ {v : Nat} {m : V} {minors : List V}, + Model.SetTheory.Mem m (shape.motiveSet constants levels env v) → + shape.MinorValuesFit constants levels env v m minors → + ∀ (hD : (shape.container constants levels env).WF (VLevel.eval levels shape.level)) + (hlarge : (shape.container constants levels env).LargeElim (VLevel.eval levels shape.level)) {i : Nat} + {ctor : Certified.Ordinary.Constructor β}, + shape.constructors[i]? = some ctor → + ∀ {xs fs : List V}, + Model.SetTheory.Tower.FitsS (Model.Telescope.interpret constants levels env ctor.fields) xs → + shape.RecursiveValuesFit constants levels env ctor xs fs → + Model.Telescope.applyN (shape.largeValue constants levels env v m minors hD hlarge) + (List.map (Model.interp constants levels (Model.Telescope.extend env xs)) ctor.indices ++ + [shape.constructorValue constants levels env ctor i xs fs]) = + Model.Telescope.applyN (minors.getD i Model.SetTheory.empty) + (xs ++ fs ++ shape.recursiveCalls constants levels env v m minors hD hlarge ctor xs fs) + axioms: [propext, Classical.choice, Quot.sound] + constants: 2417 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Ordinary.Shape.constructorType_interp + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {shape : Certified.Ordinary.Shape β} {source : β} {constants reading : Model.Assignment β V} {levels : List Nat} + {store : Store β}, + Certified.Ordinary.CheckedShape entries store source shape → + Certified.Ordinary.FamilyReading entries shape source constants reading → + Model.Realizes constants entries → + levels.length = shape.universes → + ∀ {ctor : Certified.Ordinary.Constructor β}, + ctor ∈ shape.constructors → + Model.interp reading levels (fun x => Model.SetTheory.empty) (ctor.type shape source) = + shape.constructorSet constants levels ctor + axioms: [propext, Classical.choice, Quot.sound] + constants: 2309 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Ordinary.Shape.recursorType_interp + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {shape : Certified.Ordinary.Shape β} {source : β} {constants reading : Model.Assignment β V} {levels : List Nat} + {mode : Inductive.ElimMode} {store : Store β}, + Certified.Ordinary.CheckedShape entries store source shape → + Certified.Ordinary.ConstructorReading entries shape source constants reading → + Model.Realizes constants entries → + levels.length = mode.recUvars shape.universes → + Model.interp reading levels (fun x => Model.SetTheory.empty) (shape.recursorType source mode) = + shape.closedRecursorSet constants levels mode + axioms: [propext, Classical.choice, Quot.sound] + constants: 2719 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Ordinary.Shape.closedRecursorValue_mem_source + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {shape : Certified.Ordinary.Shape β} {source : β} {store : Store β} {constants : Model.Assignment β V} + {levels : List Nat} {mode : Inductive.ElimMode}, + Certified.Ordinary.CheckedShape entries store source shape → + ∀ {reading : Model.Assignment β V}, + Certified.Ordinary.ConstructorReading entries shape source constants reading → + Certified.Ordinary.ModeEvidence entries shape mode → + Model.Realizes constants entries → + levels.length = mode.recUvars shape.universes → + Model.SetTheory.Mem (shape.closedRecursorValue constants levels mode) + (Model.interp reading levels (fun x => Model.SetTheory.empty) (shape.recursorType source mode)) + axioms: [propext, Classical.choice, Quot.sound] + constants: 2828 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Ordinary.Shape.produced_rule_eq + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {shape : Certified.Ordinary.Shape β} {source recursor : β} {constants : Model.Assignment β V} {levels : List Nat} + {mode : Inductive.ElimMode} [inst_1 : DecidableEq β] {store : Store β}, + Certified.Ordinary.CheckedShape entries store source shape → + entries.WF → + Certified.Ordinary.Shape.ConstructorFormation entries shape source → + Certified.Ordinary.Shape.RecursorFormation entries store shape source recursor mode → + Certified.Ordinary.ModeEvidence entries shape mode → + Model.Realizes constants entries → + levels.length = mode.recUvars shape.universes → + ∀ {i : Nat} {ctor : Certified.Ordinary.Constructor β}, + shape.constructors[i]? = some ctor → + Certified.Ordinary.Shape.RuleFormation (shape.recursorEnvironment entries source recursor mode) + shape source recursor mode i ctor → + ∀ (env : Nat → V), + Model.interp (shape.recursorAssignment constants source recursor mode) levels env + (shape.ruleLhs source recursor mode i ctor) = + Model.interp (shape.recursorAssignment constants source recursor mode) levels env + (shape.ruleRhs source recursor mode i ctor) + axioms: [propext, Classical.choice, Quot.sound] + constants: 3127 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Ordinary.checkBlock_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {entries : Model.Environment β} {store : Store β} + {witness : Certified.Ordinary.BlockWitness β} + {result : + Certified.CheckedClaim + (Certified.Ordinary.CheckedBlock entries store witness.source witness.recursor witness.shape.shape witness.mode)}, + Certified.Ordinary.checkBlock fuel entries store witness = some result → + Certified.Ordinary.CheckedBlock entries store witness.source witness.recursor witness.shape.shape witness.mode + axioms: [propext, Classical.choice, Quot.sound] + constants: 3074 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Ordinary.Shape.publishedAssignment_realizes + type: ∀ {β : Type u} [inst : DecidableEq β] {V : Type v} [inst_1 : Model.SetTheory V] {entries : Model.Environment β} + {store : Store β} {source recursor : β} {shape : Certified.Ordinary.Shape β} {mode : Inductive.ElimMode}, + Certified.Ordinary.CheckedBlock entries store source recursor shape mode → + entries.WF → + ∀ (constants : Model.Assignment β V), + Model.Realizes constants entries → + Model.Realizes (shape.recursorAssignment constants source recursor mode) + (shape.publishedEnvironment entries source recursor mode) + axioms: [propext, Classical.choice, Quot.sound] + constants: 3172 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.admitOrdinary?_extends + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {state : Certified.AdmittedEnvironment signature store} + {witness : Certified.Ordinary.BlockWitness β} + {result : { result // Certified.Extends signature state.entries result.entries }}, + Certified.admitOrdinary? fuel state witness = some result → + Certified.Extends signature state.entries result.val.entries + axioms: [propext, Classical.choice, Quot.sound] + constants: 4207 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Model.ConversionClaim.proofIrrel + type: ∀ {β : Type u} {entries : Model.Environment β} {Γ : Model.Context β} {A a b : Model.AExpr β}, + Model.TypingClaim entries Γ A (Model.AExpr.sort VLevel.zero) → + Model.TypingClaim entries Γ a A → Model.TypingClaim entries Γ b A → Model.ConversionClaim entries Γ a b + axioms: [propext, Classical.choice, Quot.sound] + constants: 752 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Basis.Equality.Interface.of_checked + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} {store : Store β} {source recursor : β}, + Certified.Ordinary.CheckedBlock entries store source recursor Certified.Basis.Equality.shape + Inductive.ElimMode.large → + Certified.Basis.Equality.Interface + (Certified.Basis.Equality.shape.publishedEnvironment entries source recursor Inductive.ElimMode.large) + (ConstRef.member source 0) (ConstRef.ctor source 0 0) (ConstRef.member recursor 0) + axioms: [propext, Classical.choice, Quot.sound] + constants: 2010 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Basis.Equality.value_eq_eqv + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {family refl recursor : ConstRef β} {constants : Model.Assignment β V}, + Certified.Basis.Equality.Interface entries family refl recursor → + Model.Realizes constants entries → + ∀ {u : Nat} {A a b : V}, + Model.SetTheory.Mem A (Model.SetTheory.univ u) → + Model.SetTheory.Mem a A → + Model.SetTheory.Mem b A → Certified.Basis.Equality.value constants family u A a b = Model.SetTheory.eqv a b + axioms: [propext, Classical.choice, Quot.sound] + constants: 1106 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Basis.Iff.Interface.of_checked + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} {store : Store β} {source recursor : β}, + Certified.Ordinary.CheckedBlock entries store source recursor Certified.Basis.Iff.shape Inductive.ElimMode.large → + Certified.Basis.Iff.Interface + (Certified.Basis.Iff.shape.publishedEnvironment entries source recursor Inductive.ElimMode.large) + (ConstRef.member source 0) (ConstRef.ctor source 0 0) (ConstRef.member recursor 0) + axioms: [propext, Classical.choice, Quot.sound] + constants: 2012 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Basis.Iff.eq_of_mem + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {family ctor recursor : ConstRef β} {constants : Model.Assignment β V}, + Certified.Basis.Iff.Interface entries family ctor recursor → + Model.Realizes constants entries → + ∀ {P Q proof : V}, + Model.SetTheory.Mem P Model.SetTheory.univZero → + Model.SetTheory.Mem Q Model.SetTheory.univZero → + Model.SetTheory.Mem proof (Certified.Basis.Iff.value constants family P Q) → P = Q + axioms: [propext, Classical.choice, Quot.sound] + constants: 1109 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Basis.Nonempty.Interface.of_checked + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} {store : Store β} {source recursor : β}, + Certified.Ordinary.CheckedBlock entries store source recursor Certified.Basis.Nonempty.shape + Inductive.ElimMode.small → + Certified.Basis.Nonempty.Interface + (Certified.Basis.Nonempty.shape.publishedEnvironment entries source recursor Inductive.ElimMode.small) + (ConstRef.member source 0) (ConstRef.ctor source 0 0) (ConstRef.member recursor 0) + axioms: [propext, Classical.choice, Quot.sound] + constants: 2011 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Basis.Nonempty.value_eq_truthVal + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {family ctor recursor : ConstRef β} {constants : Model.Assignment β V}, + Certified.Basis.Nonempty.Interface entries family ctor recursor → + Model.Realizes constants entries → + ∀ {u : Nat} {A : V}, + Model.SetTheory.Mem A (Model.SetTheory.univ u) → + Certified.Basis.Nonempty.value constants family u A = Model.SetTheory.truthVal (∃ a, Model.SetTheory.Mem a A) + axioms: [propext, Classical.choice, Quot.sound] + constants: 921 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Standard.Spec.value_mem + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {constants : Model.Assignment β V} (spec : Certified.Standard.Spec β), + Certified.Standard.Spec.Prerequisites entries spec → + Model.Realizes constants entries → + ∀ (levels : List Nat), + levels.length = spec.universes → + ∀ (env : Nat → V), + Model.SetTheory.Mem (spec.value constants levels) (Model.interp constants levels env spec.type) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1197 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Standard.check_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {entries : Model.Environment β} {store : Store β} + {witness : Certified.Standard.Witness β} + {result : Certified.CheckedClaim (Certified.Standard.Checked entries store witness)}, + Certified.Standard.check fuel entries store witness = some result → Certified.Standard.Checked entries store witness + axioms: [propext, Classical.choice, Quot.sound] + constants: 2833 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Standard.assignment_realizes + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} {store : Store β} + {witness : Certified.Standard.Witness β} {V : Type v} [inst_1 : Model.SetTheory V], + Certified.Standard.Checked entries store witness → + entries.WF → + ∀ (constants : Model.Assignment β V), + Model.Realizes constants entries → + Model.Realizes (Certified.Standard.assignment constants witness) + (entries.insert witness.ref witness.spec.entry) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1407 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.admitStandard?_extends + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {state : Certified.AdmittedEnvironment signature store} {witness : Certified.Standard.Witness β} + {result : { result // Certified.Extends signature state.entries result.entries }}, + Certified.admitStandard? fuel state witness = some result → + Certified.Extends signature state.entries result.val.entries + axioms: [propext, Classical.choice, Quot.sound] + constants: 3332 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Model.SetTheory.quotSet_mem_univ + type: ∀ {V : Type u} [inst : Model.SetTheory V] {u : Nat} {A R : V}, + Model.SetTheory.Mem A (Model.SetTheory.univ u) → + Model.SetTheory.Mem (Model.SetTheory.quotSet u A R) (Model.SetTheory.univ u) + axioms: [propext, Classical.choice, Quot.sound] + constants: 225 + project modules: [Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Model.SetTheory.quotSound + type: ∀ {V : Type u} [inst : Model.SetTheory V] {u : Nat} {A R a b w : V}, + Model.SetTheory.Mem a A → + Model.SetTheory.Mem b A → + Model.SetTheory.Mem w (Model.SetTheory.app (Model.SetTheory.app R a) b) → + Model.SetTheory.quotClass u A R a = Model.SetTheory.quotClass u A R b + axioms: [propext, Classical.choice, Quot.sound] + constants: 205 + project modules: [Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep] +certified root Ix.Theory.Certified.Signature.checkTypes_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {entries : Model.Environment β} + {headers : List (Certified.Signature.Header β)} {witnesses : List (Certified.Signature.TypeWitness β)} + {result : Certified.CheckedClaim (Certified.Signature.Formed entries headers)}, + Certified.Signature.checkTypes fuel entries headers witnesses = some result → + Certified.Signature.Formed entries headers + axioms: [propext, Classical.choice, Quot.sound] + constants: 2581 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Signature.Formed.realizes + type: ∀ {β : Type u} [inst : DecidableEq β] {V : Type v} [inst_1 : Model.SetTheory V] {entries : Model.Environment β} + {headers : List (Certified.Signature.Header β)}, + Certified.Signature.Formed entries headers → + ∀ {constants : Model.Assignment β V}, + Model.Realizes constants entries → + (∀ (header : Certified.Signature.Header β), + header ∈ headers → + ∀ (levels : List Nat), + levels.length = header.universes → + ∀ (env : Nat → V), + Model.SetTheory.Mem (constants header.ref levels) (Model.interp constants levels env header.type)) → + Model.Realizes constants (Certified.Signature.environment entries headers) + axioms: [propext, Classical.choice, Quot.sound] + constants: 901 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Quotient.value_mem + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {constants : Model.Assignment β V} + {refs : Certified.Quotient.Refs β} {entries : Model.Environment β}, + Certified.Quotient.Reading constants refs → + Certified.Basis.Equality.Interface entries refs.eq refs.eqRefl refs.eqRec → + Model.Realizes constants entries → + ∀ (kind : Certified.Quotient.Kind) (levels : List Nat), + levels.length = kind.universes → + ∀ (env : Nat → V), + Model.SetTheory.Mem (constants (refs.ref kind) levels) + (Model.interp constants levels env (refs.entryType kind)) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1236 + project modules: [Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Quotient.liftValue_apply + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {constants : Model.Assignment β V} {eq refl recursor : ConstRef β}, + Certified.Basis.Equality.Interface entries eq refl recursor → + Model.Realizes constants entries → + ∀ {u v : Nat} {A R B f h a : V}, + Model.SetTheory.Mem A (Model.SetTheory.univ u) → + Model.SetTheory.Mem R (Certified.Quotient.relationSet A) → + Model.SetTheory.Mem B (Model.SetTheory.univ v) → + Model.SetTheory.Mem f (Model.SetModel.piR (Certified.Quotient.bit v) A fun x => B) → + Model.SetTheory.Mem h (Certified.Quotient.invariantSet constants eq v A R B f) → + Model.SetTheory.Mem a A → + Model.SetTheory.app + (Model.SetTheory.app + (Model.SetTheory.app + (Model.SetTheory.app + (Model.SetTheory.app + (Model.SetTheory.app (Certified.Quotient.liftValue constants eq u v) A) R) + B) + f) + h) + (Model.SetTheory.quotClass u A R a) = + Model.SetTheory.app f a + axioms: [propext, Classical.choice, Quot.sound] + constants: 1132 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Quotient.liftRule_eq + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {constants : Model.Assignment β V} + {refs : Certified.Quotient.Refs β} {entries : Model.Environment β}, + Certified.Quotient.Reading constants refs → + Certified.Basis.Equality.Interface entries refs.eq refs.eqRefl refs.eqRec → + Model.Realizes constants entries → + ∀ (u v : Nat) (env : Nat → V), + Model.interp constants [u, v] env (Certified.Quotient.liftRuleLhs refs) = + Model.interp constants [u, v] env (Certified.Quotient.liftRuleRhs refs) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1182 + project modules: [Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Quotient.check_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {entries : Model.Environment β} {store : Store β} + {witness : Certified.Quotient.Witness β} + {result : Certified.CheckedClaim (Certified.Quotient.Checked entries store witness.refs)}, + Certified.Quotient.check fuel entries store witness = some result → + Certified.Quotient.Checked entries store witness.refs + axioms: [propext, Classical.choice, Quot.sound] + constants: 2895 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Quotient.assignment_realizes + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} {store : Store β} + {refs : Certified.Quotient.Refs β} {V : Type v} [inst_1 : Model.SetTheory V], + Certified.Quotient.Checked entries store refs → + entries.WF → + ∀ (constants : Model.Assignment β V), + Model.Realizes constants entries → Model.Realizes (refs.assignment constants) (refs.environment entries) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1556 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.admitQuotient?_extends + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {state : Certified.AdmittedEnvironment signature store} {witness : Certified.Quotient.Witness β} + {result : { result // Certified.Extends signature state.entries result.entries }}, + Certified.admitQuotient? fuel state witness = some result → + Certified.Extends signature state.entries result.val.entries + axioms: [propext, Classical.choice, Quot.sound] + constants: 3463 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Model.TypingClaim.fact + type: ∀ {β : Type u} {entries : Model.Environment β} {Γ : Model.Context β} {r : ConstRef β} + {entry : Model.ConstantEntry β} {e A : Model.AExpr β} {ls : List VLevel}, + entries r = some entry → + Model.ConstantFact.typed e A ∈ entry.facts → + ls.length = entry.universes → Model.TypingClaim entries Γ (Model.AExpr.instL ls e) (Model.AExpr.instL ls A) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1904 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.TypingClaim.natLit + type: ∀ {β : Type u} {entries : Model.Environment β} {Γ : Model.Context β} {r zero succ : ConstRef β} + {entry : Model.ConstantEntry β}, + entries r = some entry → + Model.ConstantFact.natural zero succ ∈ entry.facts → + entry.universes = 0 → + ∀ (value : Nat), Model.TypingClaim entries Γ (Model.AExpr.natLit value) (Model.AExpr.const r []) + axioms: [propext, Classical.choice, Quot.sound] + constants: 741 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.TypingClaim.betaResult + type: ∀ {β : Type u} {entries : Model.Environment β} {Γ : Model.Context β} {p : Certified.PropWhen} + {D body arg B : Model.AExpr β}, + Model.TypingClaim entries Γ (Model.AExpr.lam p D body) (Model.AExpr.forallE p D B) → + Model.TypingClaim entries Γ arg D → Model.TypingClaim entries Γ (body.inst arg) (B.inst arg) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1731 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.ConversionClaim.natZero + type: ∀ {β : Type u} {entries : Model.Environment β} {Γ : Model.Context β} {r zero succ : ConstRef β} + {entry : Model.ConstantEntry β}, + entries r = some entry → + Model.ConstantFact.natural zero succ ∈ entry.facts → + entry.universes = 0 → Model.ConversionClaim entries Γ (Model.AExpr.natLit 0) (Model.AExpr.const zero []) + axioms: [propext, Classical.choice, Quot.sound] + constants: 741 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.ConversionClaim.natSucc + type: ∀ {β : Type u} {entries : Model.Environment β} {Γ : Model.Context β} {r zero succ : ConstRef β} + {entry : Model.ConstantEntry β}, + entries r = some entry → + Model.ConstantFact.natural zero succ ∈ entry.facts → + entry.universes = 0 → + ∀ (value : Nat), + Model.ConversionClaim entries Γ (Model.AExpr.natLit (value + 1)) + ((Model.AExpr.const succ []).app (Model.AExpr.natLit value)) + axioms: [propext, Classical.choice, Quot.sound] + constants: 741 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.ConversionClaim.proj + type: ∀ {β : Type u} {entries : Model.Environment β} {Γ : Model.Context β} {r : ConstRef β} {i : Nat} + {a b : Model.AExpr β}, + Model.ConversionClaim entries Γ a b → + Model.ConversionClaim entries Γ (Model.AExpr.proj r i a) (Model.AExpr.proj r i b) + axioms: [propext, Classical.choice, Quot.sound] + constants: 739 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Structure.Description.projections_fit + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {d : Certified.Structure.Description β} + {constants : Model.Assignment β V} {levels : List Nat} {env : Nat → V} {entries : Model.Environment β} + {store : Store β} {source : β}, + Certified.Ordinary.CheckedShape entries store source d.ordinary → + Certified.Structure.FieldsFormed entries d.level d.ordinary.parameterContext d.fields → + Model.Realizes constants entries → + Model.Context.Valid constants levels d.ordinary.parameterContext env → + ∀ {x : V}, + Model.SetTheory.Mem x (Model.SetTheory.app (d.ordinary.carrier constants levels env) Model.SetTheory.pt) → + Model.SetTheory.Tower.FitsS (Model.Telescope.interpret constants levels env d.constructor.fields) + (Certified.Structure.projectValues d.fields.length x) + axioms: [propext, Classical.choice, Quot.sound] + constants: 2282 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Structure.Description.constructor_eta + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {d : Certified.Structure.Description β} + {constants : Model.Assignment β V} {levels : List Nat} {env : Nat → V} {entries : Model.Environment β} + {store : Store β} {source : β}, + Certified.Ordinary.CheckedShape entries store source d.ordinary → + Model.Realizes constants entries → + Model.Context.Valid constants levels d.ordinary.parameterContext env → + ∀ {x : V}, + Model.SetTheory.Mem x (Model.SetTheory.app (d.ordinary.carrier constants levels env) Model.SetTheory.pt) → + d.ordinary.constructorValue constants levels env d.constructor 0 + (Certified.Structure.projectValues d.fields.length x) [] = + x + axioms: [propext, Classical.choice, Quot.sound] + constants: 2270 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Structure.Description.constructor_iota + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {d : Certified.Structure.Description β} + {constants : Model.Assignment β V} {levels : List Nat} {env : Nat → V} {entries : Model.Environment β}, + Certified.Structure.FieldsFormed entries d.level d.ordinary.parameterContext d.fields → + Model.Realizes constants entries → + Model.Context.Valid constants levels d.ordinary.parameterContext env → + ∀ {xs : List V}, + Model.SetTheory.Tower.FitsS (Model.Telescope.interpret constants levels env d.constructor.fields) xs → + Certified.Structure.projectValues d.fields.length + (d.ordinary.constructorValue constants levels env d.constructor 0 xs []) = + xs + axioms: [propext, Classical.choice, Quot.sound] + constants: 1113 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Structure.Description.projection_meaning + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {d : Certified.Structure.Description β} + {entries : Model.Environment β} {store : Store β} {source : β} {constants reading : Model.Assignment β V} + {levels : List Nat}, + Certified.Ordinary.CheckedShape entries store source d.ordinary → + Certified.Structure.FieldsFormed entries d.level d.ordinary.parameterContext d.fields → + Certified.Ordinary.FamilyReading entries d.ordinary source constants reading → + Model.Realizes constants entries → + levels.length = d.universes → + ∀ {stage : Model.Environment β}, + Model.Realizes reading stage → + Model.Telescope.Formed stage [] (d.projectionDomains source) → + ∀ {i : Nat} {field : Certified.Structure.Field β}, + d.fields[i]? = some field → + Model.AExpr.Scope d.universes 0 (d.projection source i field) ∧ + Model.AExpr.Scope d.universes 0 (d.projectionType source i field) → + ∀ (env : Nat → V), + Model.ConstantFact.Meaning reading (ConstRef.member source 0) levels env + (Model.ConstantFact.typed (d.projection source i field) (d.projectionType source i field)) + axioms: [propext, Classical.choice, Quot.sound] + constants: 2669 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Structure.Description.eta_eq + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {d : Certified.Structure.Description β} + {entries : Model.Environment β} {store : Store β} {source : β} {constants reading : Model.Assignment β V} + {levels : List Nat}, + Certified.Ordinary.CheckedShape entries store source d.ordinary → + Certified.Structure.FieldsFormed entries d.level d.ordinary.parameterContext d.fields → + Certified.Ordinary.ConstructorReading entries d.ordinary source constants reading → + Model.Realizes constants entries → + levels.length = d.universes → + Model.interp reading levels (fun x => Model.SetTheory.empty) (d.etaLhs source) = + Model.interp reading levels (fun x => Model.SetTheory.empty) (d.etaRhs source) + axioms: [propext, Classical.choice, Quot.sound] + constants: 2437 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Structure.Description.iota_eq + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {d : Certified.Structure.Description β} + {entries : Model.Environment β} {store : Store β} {source : β} {constants reading : Model.Assignment β V} + {levels : List Nat}, + Certified.Ordinary.CheckedShape entries store source d.ordinary → + Certified.Structure.FieldsFormed entries d.level d.ordinary.parameterContext d.fields → + Certified.Ordinary.ConstructorReading entries d.ordinary source constants reading → + Model.Realizes constants entries → + levels.length = d.universes → + ∀ {i : Nat} {field : Certified.Structure.Field β}, + d.fields[i]? = some field → + Model.interp reading levels (fun x => Model.SetTheory.empty) (d.iotaLhs source i field) = + Model.interp reading levels (fun x => Model.SetTheory.empty) (d.iotaRhs i field) + axioms: [propext, Classical.choice, Quot.sound] + constants: 2431 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Structure.check_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {entries : Model.Environment β} {store : Store β} + {witness : Certified.Structure.Witness β} + {result : + Certified.CheckedClaim + (Certified.Structure.Checked entries store witness.facts.description witness.facts.block.source + witness.facts.block.recursor witness.facts.block.mode)}, + Certified.Structure.check fuel entries store witness = some result → + Certified.Structure.Checked entries store witness.facts.description witness.facts.block.source + witness.facts.block.recursor witness.facts.block.mode + axioms: [propext, Classical.choice, Quot.sound] + constants: 3205 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Structure.Description.factAssignment_realizes + type: ∀ {β : Type u} [inst : DecidableEq β] {d : Certified.Structure.Description β} {entries : Model.Environment β} + {store : Store β} {source recursor : β} {mode : Inductive.ElimMode} {V : Type v} [inst_1 : Model.SetTheory V], + Certified.Structure.FactsChecked entries store d source recursor mode → + entries.WF → + ∀ (constants : Model.Assignment β V), + Model.Realizes constants entries → + Model.Realizes (d.ordinary.recursorAssignment constants source recursor mode) + (d.factEnvironment entries source recursor mode) + axioms: [propext, Classical.choice, Quot.sound] + constants: 3279 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Structure.Description.publishedAssignment_realizes + type: ∀ {β : Type u} [inst : DecidableEq β] {d : Certified.Structure.Description β} {entries : Model.Environment β} + {store : Store β} {source recursor : β} {mode : Inductive.ElimMode} {V : Type v} [inst_1 : Model.SetTheory V], + Certified.Structure.Checked entries store d source recursor mode → + entries.WF → + ∀ (constants : Model.Assignment β V), + Model.Realizes constants entries → + Model.Realizes (d.ordinary.recursorAssignment constants source recursor mode) + (d.publishedEnvironment entries source recursor mode) + axioms: [propext, Classical.choice, Quot.sound] + constants: 3312 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.admitStructure?_extends + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {state : Certified.AdmittedEnvironment signature store} {witness : Certified.Structure.Witness β} + {result : { result // Certified.Extends signature state.entries result.entries }}, + Certified.admitStructure? fuel state witness = some result → + Certified.Extends signature state.entries result.val.entries + axioms: [propext, Classical.choice, Quot.sound] + constants: 4377 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Natural.value_mem + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {constants : Model.Assignment β V} + {entries : Model.Environment β} {store : Store β} {source : β}, + Certified.Ordinary.CheckedShape entries store source Certified.Natural.shape → + Model.Realizes constants entries → + ∀ (n : Nat), Model.SetTheory.Mem (Model.Numeral.value n) (Certified.Natural.shape.familyValue constants []) + axioms: [propext, Classical.choice, Quot.sound] + constants: 2299 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Natural.meaning + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {constants : Model.Assignment β V} + {entries : Model.Environment β} {store : Store β} {source : β}, + Certified.Ordinary.CheckedShape entries store source Certified.Natural.shape → + ∀ {reading : Model.Assignment β V}, + Certified.Ordinary.ConstructorReading entries Certified.Natural.shape source constants reading → + Model.Realizes constants entries → + Model.NaturalMeaning reading (ConstRef.member source 0) (ConstRef.ctor source 0 0) (ConstRef.ctor source 0 1) + axioms: [propext, Classical.choice, Quot.sound] + constants: 2324 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Natural.check_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {entries : Model.Environment β} {store : Store β} + {pin : ConstRef β} {witness : Certified.Ordinary.BlockWitness β} + {result : + Certified.CheckedClaim (Certified.Natural.Checked entries store pin witness.source witness.recursor witness.mode)}, + Certified.Natural.check fuel entries store pin witness = some result → + Certified.Natural.Checked entries store pin witness.source witness.recursor witness.mode + axioms: [propext, Classical.choice, Quot.sound] + constants: 3124 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Natural.assignment_realizes + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} {store : Store β} {pin : ConstRef β} + {source recursor : β} {mode : Inductive.ElimMode} {V : Type v} [inst_1 : Model.SetTheory V], + Certified.Natural.Checked entries store pin source recursor mode → + entries.WF → + ∀ (constants : Model.Assignment β V), + Model.Realizes constants entries → + Model.Realizes (Certified.Natural.shape.recursorAssignment constants source recursor mode) + (Certified.Natural.environment entries source recursor mode) + axioms: [propext, Classical.choice, Quot.sound] + constants: 3196 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.admitNatural?_extends + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {state : Certified.AdmittedEnvironment signature store} + {witness : Certified.Ordinary.BlockWitness β} + {result : { result // Certified.Extends signature state.entries result.entries }}, + Certified.admitNatural? fuel state witness = some result → + Certified.Extends signature state.entries result.val.entries + axioms: [propext, Classical.choice, Quot.sound] + constants: 4275 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Model.interp_mapRefs + type: ∀ {β : Type u} {γ : Type v} {V : Type x} [inst : Model.SetTheory V] (e : Model.AExpr β) + (mapping : ConstRef β → ConstRef γ) (constants : Model.Assignment γ V) (levels : List Nat) (env : Nat → V), + Model.interp constants levels env (Model.AExpr.mapRefs mapping e) = + Model.interp (fun ref values => constants (mapping ref) values) levels env e + axioms: [propext, Classical.choice, Quot.sound] + constants: 721 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Value, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.wellDenoted_mapRefs + type: ∀ {β : Type u} {γ : Type v} {V : Type x} [inst : Model.SetTheory V] (e : Model.AExpr β) + (mapping : ConstRef β → ConstRef γ) (constants : Model.Assignment γ V) (levels : List Nat) (env : Nat → V), + Model.WellDenoted constants levels env (Model.AExpr.mapRefs mapping e) ↔ + Model.WellDenoted (fun ref values => constants (mapping ref) values) levels env e + axioms: [propext, Classical.choice, Quot.sound] + constants: 736 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.AExpr.mapRefs_restore + type: ∀ {β : Type u} {γ : Type v} (e : Model.AExpr β) (forward : ConstRef β → ConstRef γ) + (backward : ConstRef γ → ConstRef β), + (∀ (ref : ConstRef β), ref ∈ e.references → backward (forward ref) = ref) → + Model.AExpr.mapRefs backward (Model.AExpr.mapRefs forward e) = e + axioms: [propext] + constants: 169 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Support] +certified root Ix.Theory.Model.interp_mapRefs_restore + type: ∀ {β : Type u} {γ : Type v} {V : Type x} [inst : Model.SetTheory V] (e : Model.AExpr β) + (forward : ConstRef β → ConstRef γ) (backward : ConstRef γ → ConstRef β) (constants : Model.Assignment β V), + (∀ (ref : ConstRef β), ref ∈ e.references → backward (forward ref) = ref) → + ∀ (levels : List Nat) (env : Nat → V), + Model.interp constants levels env (Model.AExpr.mapRefs backward (Model.AExpr.mapRefs forward e)) = + Model.interp constants levels env e + axioms: [propext, Classical.choice, Quot.sound] + constants: 759 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.interp_mapRefs_permutation + type: ∀ {β : Type u} {γ : Type v} {V : Type x} [inst : Model.SetTheory V] (e : Model.AExpr β) + (forward : ConstRef β → ConstRef γ) (backward : ConstRef γ → ConstRef β) (constants : Model.Assignment β V), + (∀ (ref : ConstRef β), ref ∈ e.references → backward (forward ref) = ref) → + ∀ (levels : List Nat) (env : Nat → V), + Model.interp (fun ref values => constants (backward ref) values) levels env (Model.AExpr.mapRefs forward e) = + Model.interp constants levels env e + axioms: [propext, Classical.choice, Quot.sound] + constants: 755 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.wellDenoted_mapRefs_permutation + type: ∀ {β : Type u} {γ : Type v} {V : Type x} [inst : Model.SetTheory V] (e : Model.AExpr β) + (forward : ConstRef β → ConstRef γ) (backward : ConstRef γ → ConstRef β) (constants : Model.Assignment β V), + (∀ (ref : ConstRef β), ref ∈ e.references → backward (forward ref) = ref) → + ∀ (levels : List Nat) (env : Nat → V), + Model.WellDenoted (fun ref values => constants (backward ref) values) levels env (Model.AExpr.mapRefs forward e) ↔ + Model.WellDenoted constants levels env e + axioms: [propext, Classical.choice, Quot.sound] + constants: 771 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.interp_mapRefs_merge + type: ∀ {β : Type u} {γ : Type v} {V : Type x} [inst : Model.SetTheory V] (e : Model.AExpr β) + (before : Model.Assignment β V) (after : Model.Assignment γ V) (mapping : ConstRef β → ConstRef γ), + (∀ (ref : ConstRef β), ref ∈ e.references → ∀ (levels : List Nat), before ref levels = after (mapping ref) levels) → + ∀ (levels : List Nat) (env : Nat → V), + Model.interp before levels env e = Model.interp after levels env (Model.AExpr.mapRefs mapping e) + axioms: [propext, Classical.choice, Quot.sound] + constants: 755 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Model.wellDenoted_mapRefs_merge + type: ∀ {β : Type u} {γ : Type v} {V : Type x} [inst : Model.SetTheory V] (e : Model.AExpr β) + (before : Model.Assignment β V) (after : Model.Assignment γ V) (mapping : ConstRef β → ConstRef γ), + (∀ (ref : ConstRef β), ref ∈ e.references → ∀ (levels : List Nat), before ref levels = after (mapping ref) levels) → + ∀ (levels : List Nat) (env : Nat → V), + Model.WellDenoted before levels env e ↔ Model.WellDenoted after levels env (Model.AExpr.mapRefs mapping e) + axioms: [propext, Classical.choice, Quot.sound] + constants: 771 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Modeled.propositional_equation + type: ∀ {β : Type u} {entries : Model.Environment β} {family reflexivity recursor : ConstRef β}, + Certified.Basis.Equality.Interface entries family reflexivity recursor → + ∀ {rule : Certified.Signature.Rule β} {level : VLevel} {proof : Model.AExpr β}, + Model.TypingClaim entries [] rule.type (Model.AExpr.sort level) → + Model.TypingClaim entries [] rule.lhs rule.type → + Model.TypingClaim entries [] rule.rhs rule.type → + Model.TypingClaim entries [] proof + (Certified.Basis.Equality.applied family level rule.type rule.lhs rule.rhs) → + Model.ConversionClaim entries [] rule.lhs rule.rhs + axioms: [propext, Classical.choice, Quot.sound] + constants: 1115 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Modeled.checkEquation?_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {entries : Model.Environment β} + {rule : Certified.Signature.Rule β} {witness : Certified.Modeled.EquationWitness β} + {result : Certified.CheckedClaim (Certified.Modeled.CheckedEquation entries rule)}, + Certified.Modeled.checkEquation? fuel entries rule witness = some result → + Certified.Modeled.CheckedEquation entries rule + axioms: [propext, Classical.choice, Quot.sound] + constants: 2631 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Modeled.CheckedCompanions.fixed_old + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} + {companions : List (Certified.Modeled.Companion β)}, + Certified.Modeled.CheckedCompanions entries companions → + ∀ {ref : ConstRef β} {entry : Model.ConstantEntry β}, + entries ref = some entry → Certified.Modeled.mapping companions ref = ref + axioms: [propext, Classical.choice, Quot.sound] + constants: 834 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Modeled.environment_wf + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} + {companions : List (Certified.Modeled.Companion β)}, + Certified.Modeled.CheckedCompanions entries companions → + entries.WF → (Certified.Modeled.environment entries companions).WF + axioms: [propext, Classical.choice, Quot.sound] + constants: 913 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Modeled.assignment_agrees + type: ∀ {β : Type u} [inst : DecidableEq β] {V : Type v} {entries : Model.Environment β} + {companions : List (Certified.Modeled.Companion β)}, + Certified.Modeled.CheckedCompanions entries companions → + ∀ (constants : Model.Assignment β V), + Model.Assignment.AgreesOn entries constants (Certified.Modeled.assignment constants companions) + axioms: [propext, Classical.choice, Quot.sound] + constants: 837 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Modeled.assignment_realizes + type: ∀ {β : Type u} [inst : DecidableEq β] {V : Type v} [inst_1 : Model.SetTheory V] {entries : Model.Environment β} + {companions : List (Certified.Modeled.Companion β)}, + Certified.Modeled.CheckedCompanions entries companions → + entries.WF → + ∀ (constants : Model.Assignment β V), + Model.Realizes constants entries → + Model.Realizes (Certified.Modeled.assignment constants companions) + (Certified.Modeled.environment entries companions) + axioms: [propext, Classical.choice, Quot.sound] + constants: 1030 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.Modeled.check?_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {entries : Model.Environment β} {store : Store β} + {witness : Certified.Modeled.Witness β} + {result : Certified.CheckedClaim (Certified.Modeled.Checked entries store witness)}, + Certified.Modeled.check? fuel entries store witness = some result → Certified.Modeled.Checked entries store witness + axioms: [propext, Classical.choice, Quot.sound] + constants: 2947 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.Modeled.EntrySource.not_axiom + type: ∀ {β : Type u} {store : Store β} {ref : ConstRef β} {entry : Model.ConstantEntry β}, + Certified.Modeled.EntrySource store ref entry → + ∀ {n : Nat} {type : VExpr β} {safety : Safety}, store.lookup ref = some (Const.axiom n type safety) → False + axioms: [propext] + constants: 964 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Environment, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport] +certified root Ix.Theory.Certified.Modeled.EntrySource.header + type: ∀ {β : Type u} {store : Store β} {ref : ConstRef β} {entry : Model.ConstantEntry β}, + Certified.Modeled.EntrySource store ref entry → Certified.SourceHeader store ref entry + axioms: [propext] + constants: 968 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Environment, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport] +certified root Ix.Theory.Certified.checkModeledExtension? + type: {β : Type u} → + [DecidableEq β] → + Nat → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → + (state : Certified.CheckedInterface signature) → + Certified.Modeled.Witness β → Option (Certified.CheckedExtension signature store state) + axioms: [propext, Classical.choice, Quot.sound] + constants: 3257 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.admitModeled?_extends + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {state : Certified.AdmittedEnvironment signature store} {witness : Certified.Modeled.Witness β} + {result : { result // Certified.Extends signature state.entries result.entries }}, + Certified.admitModeled? fuel state witness = some result → + Certified.Extends signature state.entries result.val.entries + axioms: [propext, Classical.choice, Quot.sound] + constants: 3277 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.PrimitiveSignature.compatible_assignment + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] (signature : Certified.PrimitiveSignature β) + [inst_1 : DecidableEq β], signature.Compatible signature.environment signature.assignment + axioms: [propext, Classical.choice, Quot.sound] + constants: 2006 + project modules: [Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.checkTypeCertified_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel n : Nat} {entries : Model.Environment β} {Γ : Model.Context β} + {e A : Model.AExpr β} {witness : Certified.TypingWitness β}, + Certified.checkTypeCertified fuel n entries Γ e A witness = true → Model.TypingClaim entries Γ e A + axioms: [propext, Classical.choice, Quot.sound] + constants: 2553 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.defeqCertified_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel n : Nat} {entries : Model.Environment β} {Γ : Model.Context β} + {a b : Model.AExpr β} {witness : Certified.ConversionWitness β}, + Certified.defeqCertified fuel n entries Γ a b witness = true → Model.ConversionClaim entries Γ a b + axioms: [propext, Classical.choice, Quot.sound] + constants: 2553 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.inferCertified_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel n : Nat} {entries : Model.Environment β} {Γ : Model.Context β} + {source : VExpr β} {witness : Certified.InferenceWitness β} {result : Certified.InferenceResult entries n Γ source}, + Certified.inferCertified fuel n entries Γ source witness = some result → + result.reading.val.erase = source ∧ + Model.AExpr.Scope n (List.length Γ) result.type ∧ Model.TypingClaim entries Γ result.reading.val result.type + axioms: [propext, Classical.choice, Quot.sound] + constants: 2610 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Operations, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.whnfCertified_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel n : Nat} {entries : Model.Environment β} {Γ : Model.Context β} + {source : VExpr β} {witness : Certified.WhnfWitness β} {result : Certified.WhnfResult entries n Γ source}, + Certified.whnfCertified fuel n entries Γ source witness = some result → + ∀ (V : Type v) [inst : Model.SetTheory V] (constants : Model.Assignment β V), + Model.Realizes constants entries → + ∀ (levels : List Nat) (env : Nat → V), + Model.Context.Valid constants levels Γ env → + result.input.reading.val.erase = source ∧ + Certified.whnfShape entries result.result = true ∧ + Model.WellDenoted constants levels env result.input.reading.val ∧ + Model.WellDenoted constants levels env result.result ∧ + Model.interp constants levels env result.input.reading.val = + Model.interp constants levels env result.result + axioms: [propext, Classical.choice, Quot.sound] + constants: 2632 + project modules: [Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Operations, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.admitDefinition?_extends + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {state : Certified.AdmittedEnvironment signature store} {witness : Certified.DefinitionWitness β} + {result : { result // Certified.Extends signature state.entries result.entries }}, + Certified.admitDefinition? fuel state witness = some result → + Certified.Extends signature state.entries result.val.entries + axioms: [propext, Classical.choice, Quot.sound] + constants: 3145 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.checkStoreCertified + type: {β : Type u} → + [DecidableEq β] → + Nat → + (signature : Certified.PrimitiveSignature β) → + (store : Store β) → + (targets : List (ConstRef β)) → + List (Certified.DeclarationWitness β) → Option (Certified.CheckedStore signature store targets) + axioms: [propext, Classical.choice, Quot.sound] + constants: 4954 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Store, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.accepted_store_has_model + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {targets : List (ConstRef β)} {witness : List (Certified.DeclarationWitness β)}, + Certified.acceptsStoreCertified fuel signature store targets witness = true → + ∀ (V : Type v) [inst_1 : Model.SetTheory V], + ∃ result, + Certified.checkStoreCertified fuel signature store targets witness = some result ∧ + ∃ constants, + signature.Compatible result.environment.entries constants ∧ + ∀ (r : ConstRef β), + r ∈ targets → + ∃ entry, + result.environment.entries r = some entry ∧ + Certified.EntrySource signature store r entry ∧ + ∀ (levels : List Nat), + levels.length = entry.universes → + ∀ (env : Nat → V), + Model.WellDenoted constants levels env entry.type ∧ + Model.SetTheory.Mem (constants r levels) (Model.interp constants levels env entry.type) + axioms: [propext, Classical.choice, Quot.sound] + constants: 4958 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Store, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.EntrySource.header + type: ∀ {β : Type u} {signature : Certified.PrimitiveSignature β} {store : Store β} {ref : ConstRef β} + {entry : Model.ConstantEntry β}, + Certified.EntrySource signature store ref entry → Certified.SourceHeader store ref entry + axioms: [propext, Quot.sound] + constants: 2123 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Instantiation, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Model.Inductive.Telescope] +certified root Ix.Theory.Certified.CheckedStore.subject_sound + type: ∀ {β : Type u} {signature : Certified.PrimitiveSignature β} {store : Store β} {subjects : List (ConstRef β)} + (result : Certified.CheckedStore signature store subjects) {ref : ConstRef β}, + ref ∈ subjects → + ∃ entry, + result.environment.entries ref = some entry ∧ + Certified.SourceHeader store ref entry ∧ + ∀ (V : Type v) [inst : Model.SetTheory V] (constants : Model.Assignment β V), + signature.Compatible result.environment.entries constants → + ∀ (levels : List Nat), + levels.length = entry.universes → + ∀ (env : Nat → V), + Model.WellDenoted constants levels env entry.type ∧ + Model.SetTheory.Mem (constants ref levels) (Model.interp constants levels env entry.type) + axioms: [propext, Classical.choice, Quot.sound] + constants: 2252 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Certified.Store, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.accepted_store_source_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {subjects : List (ConstRef β)} {witness : List (Certified.DeclarationWitness β)}, + Certified.acceptsStoreCertified fuel signature store subjects witness = true → + ∀ (V : Type v) [inst_1 : Model.SetTheory V], + ∃ result, + Certified.checkStoreCertified fuel signature store subjects witness = some result ∧ + ∃ constants, + signature.Compatible result.environment.entries constants ∧ + ∀ (ref : ConstRef β), + ref ∈ subjects → + ∃ entry, + result.environment.entries ref = some entry ∧ + Certified.SourceHeader store ref entry ∧ + ∀ (levels : List Nat), + levels.length = entry.universes → + ∀ (env : Nat → V), + Model.WellDenoted constants levels env entry.type ∧ + Model.SetTheory.Mem (constants ref levels) + (Model.interp constants levels env entry.type) + axioms: [propext, Classical.choice, Quot.sound] + constants: 4976 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Certified.Store, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.checkDeclarationExtensions? + type: {β : Type u} → + [DecidableEq β] → + Nat → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → + (state : Certified.CheckedInterface signature) → + List (Certified.DeclarationWitness β) → Option (Certified.CheckedExtension signature store state) + axioms: [propext, Classical.choice, Quot.sound] + constants: 4897 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.CheckedExtension.admit + type: {β : Type u} → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → + (state : Certified.AdmittedEnvironment signature store) → + Certified.CheckedExtension signature store state.interface → + { result // Certified.Extends signature state.entries result.entries } + axioms: [propext, Classical.choice, Quot.sound] + constants: 2246 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.checkFrontier?_refs + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {witnesses : List (Certified.FrontierWitness β)} + {result : Certified.CheckedFrontier signature store}, + Certified.checkFrontier? fuel signature store witnesses = some result → + result.refs = List.map (fun x => x.ref) witnesses + axioms: [propext, Classical.choice, Quot.sound] + constants: 2912 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Frontier, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.CheckedFrontier.compatible + type: ∀ {β : Type u} [inst : DecidableEq β] {signature : Certified.PrimitiveSignature β} {store : Store β} + (frontier : Certified.CheckedFrontier signature store) (provider : Certified.CheckedInterface signature), + (∀ (header : Certified.Signature.Header β), + header ∈ frontier.headers → Certified.HeaderPresent provider.entries header) → + ∀ (V : Type v) [inst_1 : Model.SetTheory V] (constants : Model.Assignment β V), + signature.Compatible provider.entries constants → signature.Compatible frontier.interface.entries constants + axioms: [propext, Classical.choice, Quot.sound] + constants: 1213 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Frontier, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.ConditionalStore.subject_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {signature : Certified.PrimitiveSignature β} {store : Store β} + {subjects : List (ConstRef β)} (receipt : Certified.ConditionalStore signature store subjects) (V : Type v) + [inst_1 : Model.SetTheory V] (constants : Model.Assignment β V), + signature.Compatible receipt.frontier.interface.entries constants → + ∃ constants', + signature.Compatible receipt.checked.result.entries constants' ∧ + Model.Assignment.AgreesOn receipt.frontier.interface.entries constants constants' ∧ + ∀ (ref : ConstRef β), + ref ∈ subjects → + ∃ entry, + receipt.checked.result.entries ref = some entry ∧ + Certified.EntrySource signature store ref entry ∧ + Certified.SourceHeader store ref entry ∧ + ∀ (levels : List Nat), + levels.length = entry.universes → + ∀ (env : Nat → V), + Model.WellDenoted constants' levels env entry.type ∧ + Model.SetTheory.Mem (constants' ref levels) + (Model.interp constants' levels env entry.type) + axioms: [propext, Classical.choice, Quot.sound] + constants: 2445 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Frontier, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.EntrySource.axiom_policy + type: ∀ {β : Type u} {signature : Certified.PrimitiveSignature β} {store : Store β} {ref : ConstRef β} + {entry : Model.ConstantEntry β} {n : Nat} {type : VExpr β} {safety : Safety}, + Certified.EntrySource signature store ref entry → + store.lookup ref = some (Const.axiom n type safety) → + Certified.Standard.EntrySource store ref entry ∨ Certified.Quotient.EntrySource store ref entry + axioms: [propext, Quot.sound] + constants: 2111 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LogicalPolicy, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Instantiation, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Model.Inductive.Telescope] +certified root Ix.Theory.Certified.ConditionalStore.logical_axioms_authorized + type: ∀ {β : Type u} [inst : DecidableEq β] {signature : Certified.PrimitiveSignature β} {store : Store β} + {subjects : List (ConstRef β)} (receipt : Certified.ConditionalStore signature store subjects) {ref : ConstRef β}, + ref ∈ Certified.logicalAxioms store receipt.checked.result.entries → + ∃ entry, + receipt.checked.result.entries ref = some entry ∧ + (Certified.Standard.EntrySource store ref entry ∨ Certified.Quotient.EntrySource store ref entry) + axioms: [propext, Classical.choice, Quot.sound] + constants: 2596 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Frontier, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LogicalPolicy, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.Support, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ] +certified root Ix.Theory.Certified.acceptsBatch + type: {β : Type u} → + [DecidableEq β] → + Nat → + Certified.PrimitiveSignature β → + Store β → List (Certified.FrontierWitness β) → List (Certified.ClaimNode β) → Bool + axioms: [propext, Classical.choice, Quot.sound] + constants: 5081 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Claims, + Ix.Theory.Certified.Frontier, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.CheckedBatch.closed_has_model + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {inputs : List (Certified.ClaimNode β)} + (batch : Certified.CheckedBatch fuel signature store inputs), + batch.receipt.frontier.refs = [] → + ∀ (V : Type v) [inst_1 : Model.SetTheory V], + ∃ constants, + signature.Compatible batch.receipt.checked.result.entries constants ∧ + ∀ (ref : ConstRef β), + ref ∈ Certified.nodeSubjects batch.nodes → + ∃ entry, + batch.receipt.checked.result.entries ref = some entry ∧ + Certified.SourceHeader store ref entry ∧ + ∀ (levels : List Nat), + levels.length = entry.universes → + ∀ (env : Nat → V), + Model.SetTheory.Mem (constants ref levels) (Model.interp constants levels env entry.type) + axioms: [propext, Classical.choice, Quot.sound] + constants: 5023 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Claims, + Ix.Theory.Certified.Frontier, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.CheckedBatch.compatible_leaves + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {inputs : List (Certified.ClaimNode β)} (batch : Certified.CheckedBatch fuel signature store inputs) + (V : Type v) [inst_1 : Model.SetTheory V] (constants : Model.Assignment β V), + signature.Compatible batch.receipt.checked.result.entries constants → + ∀ {node : Certified.CheckedNode fuel signature store}, + node ∈ batch.nodes → signature.Compatible node.receipt.frontier.interface.entries constants + axioms: [propext, Classical.choice, Quot.sound] + constants: 4993 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Claims, + Ix.Theory.Certified.Frontier, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.CheckedBatch.frontier_coverage + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {inputs : List (Certified.ClaimNode β)} (batch : Certified.CheckedBatch fuel signature store inputs) + {ref : ConstRef β}, + ref ∈ batch.receipt.frontier.refs ↔ + (∃ node, node ∈ inputs ∧ ref ∈ List.map (fun x => x.ref) node.frontier) ∧ + ¬∃ node, node ∈ inputs ∧ ref ∈ node.subjects + axioms: [propext, Classical.choice, Quot.sound] + constants: 5129 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.ClaimComposition, + Ix.Theory.Certified.Claims, + Ix.Theory.Certified.Frontier, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.DependencyOrder.before + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {known : List (ConstRef β)} {nodes : List (Certified.CheckedNode fuel signature store)}, + Certified.DependencyOrder known nodes → + ∀ {index : Nat} {node : Certified.CheckedNode fuel signature store}, + nodes[index]? = some node → + ∀ {ref : ConstRef β}, + ref ∈ node.receipt.frontier.refs → + ref ∈ known ∨ + ∃ previous, + previous < index ∧ ∃ provider, nodes[previous]? = some provider ∧ ref ∈ provider.input.subjects + axioms: [propext, Classical.choice, Quot.sound] + constants: 4970 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.ClaimComposition, + Ix.Theory.Certified.Claims, + Ix.Theory.Certified.Frontier, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.DependencyOrder.edge_for + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {known : List (ConstRef β)} {nodes : List (Certified.CheckedNode fuel signature store)}, + Certified.DependencyOrder known nodes → + ∀ {consumer : Nat} {node : Certified.CheckedNode fuel signature store}, + nodes[consumer]? = some node → + ∀ {ref : ConstRef β}, + ref ∈ node.receipt.frontier.refs → ref ∈ known ∨ ∃ provider, Certified.DependencyEdge nodes provider consumer + axioms: [propext, Classical.choice, Quot.sound] + constants: 4993 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.ClaimComposition, + Ix.Theory.Certified.Claims, + Ix.Theory.Certified.Frontier, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.CheckedBatch.acyclic + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {inputs : List (Certified.ClaimNode β)} + (batch : Certified.CheckedBatch fuel signature store inputs), WellFounded (Certified.DependencyEdge batch.nodes) + axioms: [propext, Classical.choice, Quot.sound] + constants: 5080 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.ClaimComposition, + Ix.Theory.Certified.Claims, + Ix.Theory.Certified.Frontier, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.CheckedBatch.no_circular_discharge + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {inputs : List (Certified.ClaimNode β)} (batch : Certified.CheckedBatch fuel signature store inputs) + {a b : Nat}, Certified.DependencyEdge batch.nodes a b → Certified.DependencyEdge batch.nodes b a → False + axioms: [propext, Classical.choice, Quot.sound] + constants: 5078 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.ClaimComposition, + Ix.Theory.Certified.Claims, + Ix.Theory.Certified.Frontier, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.CheckedBatch.leaf_axiom_retained + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {inputs : List (Certified.ClaimNode β)} (batch : Certified.CheckedBatch fuel signature store inputs) + {node : Certified.CheckedNode fuel signature store}, + node ∈ batch.nodes → + ∀ {ref : ConstRef β}, + ref ∈ Certified.logicalAxioms store node.receipt.checked.result.entries → ref ∈ batch.logicalUses + axioms: [propext, Classical.choice, Quot.sound] + constants: 5040 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.ClaimComposition, + Ix.Theory.Certified.Claims, + Ix.Theory.Certified.Frontier, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.LogicalPolicy, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.CheckedBatch.logicalUses_authorized + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {inputs : List (Certified.ClaimNode β)} (batch : Certified.CheckedBatch fuel signature store inputs) + {ref : ConstRef β}, + ref ∈ batch.logicalUses → + ∃ entry, Certified.Standard.EntrySource store ref entry ∨ Certified.Quotient.EntrySource store ref entry + axioms: [propext, Classical.choice, Quot.sound] + constants: 5079 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.ClaimComposition, + Ix.Theory.Certified.Claims, + Ix.Theory.Certified.Frontier, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.LogicalPolicy, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.checkBatch?_assoc + type: ∀ {β : Type u} [inst : DecidableEq β] (fuel : Nat) (signature : Certified.PrimitiveSignature β) + (store : Store β) (frontier : List (Certified.FrontierWitness β)) (a b c : List (Certified.ClaimNode β)), + Certified.acceptsBatch fuel signature store frontier (Certified.composeClaims (Certified.composeClaims a b) c) = + Certified.acceptsBatch fuel signature store frontier (Certified.composeClaims a (Certified.composeClaims b c)) + axioms: [propext, Classical.choice, Quot.sound] + constants: 5084 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.ClaimComposition, + Ix.Theory.Certified.Claims, + Ix.Theory.Certified.Frontier, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.CheckedBatch.no_False + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {inputs : List (Certified.ClaimNode β)} + (batch : Certified.CheckedBatch fuel signature store inputs), + batch.receipt.frontier.refs = [] → + ∀ {ref : ConstRef β}, + ref ∈ Certified.nodeSubjects batch.nodes → + store.type ref = some signature.falseExpr → ∀ (V : Type v) [Model.SetTheory V], False + axioms: [propext, Classical.choice, Quot.sound] + constants: 5028 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.ClaimComposition, + Ix.Theory.Certified.Claims, + Ix.Theory.Certified.Frontier, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Source, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.acceptsCertified + type: {β : Type u} → + [DecidableEq β] → Nat → Certified.PrimitiveSignature β → Certified.ProofInput β → Certified.ProofWitness β → Bool + axioms: [propext, Classical.choice, Quot.sound] + constants: 4969 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Accept, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.accepted_has_model + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {input : Certified.ProofInput β} {witness : Certified.ProofWitness β}, + Certified.acceptsCertified fuel signature input witness = true → + ∀ (V : Type v) [inst_1 : Model.SetTheory V] (levels : List Nat) (env : Nat → V), + ∃ result, + Certified.checkProofCertified fuel signature input witness = some result ∧ + ∃ constants, + signature.Compatible result.environment.entries constants ∧ + Model.WellDenoted constants levels env result.proof.val ∧ + Model.WellDenoted constants levels env result.proposition.val ∧ + Model.SetTheory.Mem (Model.interp constants levels env result.proof.val) + (Model.interp constants levels env result.proposition.val) + axioms: [propext, Classical.choice, Quot.sound] + constants: 4974 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Accept, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.accepted_proof_sound + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {input : Certified.ProofInput β} {witness : Certified.ProofWitness β}, + Certified.acceptsCertified fuel signature input witness = true → + ∃ result, + Certified.checkProofCertified fuel signature input witness = some result ∧ + ∀ (V : Type v) [inst : Model.SetTheory V] (constants : Model.Assignment β V), + signature.Compatible result.environment.entries constants → + ∀ (levels : List Nat) (env : Nat → V), + Model.SetTheory.Mem (Model.interp constants levels env result.proof.val) + (Model.interp constants levels env result.proposition.val) + axioms: [propext, Classical.choice, Quot.sound] + constants: 4974 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Accept, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.no_proof_of_False + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {input : Certified.ProofInput β} {witness : Certified.ProofWitness β} (V : Type v) [Model.SetTheory V], + input.proposition = signature.falseExpr → Certified.acceptsCertified fuel signature input witness = true → False + axioms: [propext, Classical.choice, Quot.sound] + constants: 4978 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Accept, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +certified root Ix.Theory.Certified.no_proof_of_empty + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {input : Certified.ProofInput β} {witness : Certified.ProofWitness β} (V : Type v) [inst_1 : Model.SetTheory V], + (∀ (result : Certified.CheckedProof signature input), + Certified.checkProofCertified fuel signature input witness = some result → + ∀ (constants : Model.Assignment β V), + signature.Compatible result.environment.entries constants → + Model.interp constants [] (fun x => Model.SetTheory.empty) result.proposition.val = Model.SetTheory.empty) → + Certified.acceptsCertified fuel signature input witness = true → False + axioms: [propext, Classical.choice, Quot.sound] + constants: 4975 + project modules: [Ix.Theory.Const, + Ix.Theory.Expr, + Ix.Theory.Quot, + Ix.Theory.Ref, + Ix.Theory.Store, + Ix.Theory.VLevel, + Ix.Theory.Certified.Accept, + Ix.Theory.Certified.Admission, + Ix.Theory.Certified.Checker, + Ix.Theory.Certified.Level, + Ix.Theory.Certified.LevelEq, + Ix.Theory.Certified.Policy, + Ix.Theory.Certified.Prelude, + Ix.Theory.Certified.PropWhen, + Ix.Theory.Certified.Signature, + Ix.Theory.Certified.Telescope, + Ix.Theory.Inductive.Levels, + Ix.Theory.Model.Annotated, + Ix.Theory.Model.Context, + Ix.Theory.Model.Environment, + Ix.Theory.Model.Extension, + Ix.Theory.Model.Instantiation, + Ix.Theory.Model.Interpret, + Ix.Theory.Model.Judgment, + Ix.Theory.Model.PrimitiveValues, + Ix.Theory.Model.ReferenceMap, + Ix.Theory.Model.Signature, + Ix.Theory.Model.Support, + Ix.Theory.Model.TelescopeSemantics, + Ix.Theory.Model.Value, + Ix.Theory.Model.WellDenoted, + Ix.Theory.Certified.Basis.Equality, + Ix.Theory.Certified.Basis.Iff, + Ix.Theory.Certified.Basis.Interface, + Ix.Theory.Certified.Basis.Nonempty, + Ix.Theory.Certified.Modeled.Admission, + Ix.Theory.Certified.Modeled.Equation, + Ix.Theory.Certified.Modeled.Source, + Ix.Theory.Certified.Modeled.Transport, + Ix.Theory.Certified.Natural.Admission, + Ix.Theory.Certified.Natural.Checked, + Ix.Theory.Certified.Natural.Publish, + Ix.Theory.Certified.Natural.Value, + Ix.Theory.Certified.Ordinary.Admission, + Ix.Theory.Certified.Ordinary.Checked, + Ix.Theory.Certified.Ordinary.Computation, + Ix.Theory.Certified.Ordinary.ConstructorStage, + Ix.Theory.Certified.Ordinary.Constructors, + Ix.Theory.Certified.Ordinary.Container, + Ix.Theory.Certified.Ordinary.Eliminator, + Ix.Theory.Certified.Ordinary.Family, + Ix.Theory.Certified.Ordinary.LargeElim, + Ix.Theory.Certified.Ordinary.Reading, + Ix.Theory.Certified.Ordinary.RecursorReading, + Ix.Theory.Certified.Ordinary.RecursorStage, + Ix.Theory.Certified.Ordinary.RecursorSyntax, + Ix.Theory.Certified.Ordinary.RecursorValue, + Ix.Theory.Certified.Ordinary.RuleChecks, + Ix.Theory.Certified.Ordinary.RuleEquations, + Ix.Theory.Certified.Ordinary.RuleReading, + Ix.Theory.Certified.Ordinary.Shape, + Ix.Theory.Certified.Quotient.Admission, + Ix.Theory.Certified.Quotient.Checked, + Ix.Theory.Certified.Quotient.Publish, + Ix.Theory.Certified.Quotient.Reading, + Ix.Theory.Certified.Quotient.Syntax, + Ix.Theory.Certified.Quotient.Value, + Ix.Theory.Certified.Standard.Admission, + Ix.Theory.Certified.Standard.Checked, + Ix.Theory.Certified.Standard.Realization, + Ix.Theory.Certified.Structure.Admission, + Ix.Theory.Certified.Structure.Checked, + Ix.Theory.Certified.Structure.Computation, + Ix.Theory.Certified.Structure.Publish, + Ix.Theory.Certified.Structure.Reading, + Ix.Theory.Certified.Structure.Syntax, + Ix.Theory.Certified.Structure.Value, + Ix.Theory.Model.Inductive.Codes, + Ix.Theory.Model.Inductive.Container, + Ix.Theory.Model.Inductive.Recursor, + Ix.Theory.Model.Inductive.Telescope, + Ix.Theory.Model.SetModel.Container, + Ix.Theory.Model.SetModel.Iter, + Ix.Theory.Model.SetModel.Ops, + Ix.Theory.Model.SetModel.RecGraph, + Ix.Theory.Model.SetModel.TaggedSum, + Ix.Theory.Model.SetModel.TupleTower, + Ix.Theory.Model.SetTheory.Core, + Ix.Theory.Model.SetTheory.Derive.Choice, + Ix.Theory.Model.SetTheory.Derive.Empty, + Ix.Theory.Model.SetTheory.Derive.Graphs, + Ix.Theory.Model.SetTheory.Derive.Lfp, + Ix.Theory.Model.SetTheory.Derive.LfpFam, + Ix.Theory.Model.SetTheory.Derive.Omega, + Ix.Theory.Model.SetTheory.Derive.Pair, + Ix.Theory.Model.SetTheory.Derive.Pt, + Ix.Theory.Model.SetTheory.Derive.Quot, + Ix.Theory.Model.SetTheory.Derive.Sep, + Ix.Theory.Model.SetTheory.Derive.Sigma, + Ix.Theory.Model.SetTheory.Derive.Univ, + Ix.Theory.Model.SetTheory.Derive.Universe] +premise definition Ix.Theory.Model.AExpr.mapRefs + type: {β : Type u} → {γ : Type v} → (ConstRef β → ConstRef γ) → Model.AExpr β → Model.AExpr γ + definition: fun {β} {γ} mapping x => Model.AExpr.brecOn x (Model.AExpr.mapRefs._f mapping) +premise constructor Ix.Theory.Certified.Modeled.EquationWitness.mk + type: {β : Type u} → + Certified.Signature.RuleWitness β → Certified.Modeled.EquationProof β → Certified.Modeled.EquationWitness β +premise constructor Ix.Theory.Certified.Modeled.EquationProof.propositional + type: {β : Type u} → + ConstRef β → ConstRef β → ConstRef β → Model.AExpr β → Certified.TypingWitness β → Certified.Modeled.EquationProof β +premise constructor Ix.Theory.Certified.Modeled.CheckedEquation.mk + type: ∀ {β : Type u} {entries : Model.Environment β} {rule : Certified.Signature.Rule β}, + Certified.Signature.RuleFormed entries rule → + Model.ConversionClaim entries [] rule.lhs rule.rhs → Certified.Modeled.CheckedEquation entries rule +premise definition Ix.Theory.Certified.Modeled.checkEquation? + type: {β : Type u} → + [DecidableEq β] → + Nat → + (entries : Model.Environment β) → + (rule : Certified.Signature.Rule β) → + Certified.Modeled.EquationWitness β → + Option (Certified.CheckedClaim (Certified.Modeled.CheckedEquation entries rule)) + definition: fun {β} [DecidableEq β] fuel entries rule witness => do + let formed ← Certified.Signature.checkRule fuel entries rule witness.formation + match witness.proof with + | Certified.Modeled.EquationProof.conversion witness => do + let equality ← Certified.verifyConversion fuel rule.universes entries [] rule.lhs rule.rhs witness + pure { down := ⋯ } + | Certified.Modeled.EquationProof.propositional family reflexivity recursor proof typing => + if hi : Certified.Basis.Equality.Interface entries family reflexivity recursor then do + let type ← + Certified.verifyType fuel rule.universes entries [] rule.type (Model.AExpr.sort witness.formation.level) + witness.formation.type + let checked ← + Certified.verifyType fuel rule.universes entries [] proof + (Certified.Basis.Equality.applied family witness.formation.level rule.type rule.lhs rule.rhs) typing + pure { down := ⋯ } + else none +premise constructor Ix.Theory.Certified.Modeled.Companion.mk + type: {β : Type u} → + Certified.Signature.Header β → ConstRef β → List (Certified.Signature.Rule β) → Certified.Modeled.Companion β +premise definition Ix.Theory.Certified.Modeled.Companion.entry + type: {β : Type u} → Certified.Modeled.Companion β → Model.ConstantEntry β + definition: fun {β} companion => + { universes := companion.header.universes, type := companion.header.type, body := none, + equations := List.map (fun rule => { lhs := rule.lhs, rhs := rule.rhs }) companion.rules } +premise definition Ix.Theory.Certified.Modeled.mapping + type: {β : Type u} → [DecidableEq β] → List (Certified.Modeled.Companion β) → ConstRef β → ConstRef β + definition: fun {β} [DecidableEq β] companions ref => + match Certified.Modeled.lookupCompanion companions ref with + | none => ref + | some companion => companion.model +premise constructor Ix.Theory.Certified.Modeled.CompanionChecked.mk + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} + {companions : List (Certified.Modeled.Companion β)} {companion : Certified.Modeled.Companion β}, + (∃ entry, + entries companion.model = some entry ∧ + entry.universes = companion.header.universes ∧ + entry.type = Model.AExpr.mapRefs (Certified.Modeled.mapping companions) companion.header.type) → + Model.AExpr.Scope companion.header.universes 0 companion.header.type → + Model.AExpr.ReferencesIn (Certified.Modeled.environment entries companions) companion.header.type → + (∀ (rule : Certified.Signature.Rule β), + rule ∈ companion.rules → + rule.universes = companion.header.universes ∧ + Model.AExpr.Scope companion.header.universes 0 rule.lhs ∧ + Model.AExpr.Scope companion.header.universes 0 rule.rhs) → + (∀ (rule : Certified.Signature.Rule β), + rule ∈ companion.rules → + Model.AExpr.ReferencesIn (Certified.Modeled.environment entries companions) rule.lhs ∧ + Model.AExpr.ReferencesIn (Certified.Modeled.environment entries companions) rule.rhs) → + (∀ (rule : Certified.Signature.Rule β), + rule ∈ companion.rules → + Certified.Modeled.CheckedEquation entries (Certified.Modeled.mapRule companions rule)) → + Certified.Modeled.CompanionChecked entries companions companion +premise constructor Ix.Theory.Certified.Modeled.CheckedCompanions.mk + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} + {companions : List (Certified.Modeled.Companion β)}, + (List.map (fun x => x.header.ref) companions).Nodup → + (∀ (companion : Certified.Modeled.Companion β), companion ∈ companions → entries companion.header.ref = none) → + (∀ (companion : Certified.Modeled.Companion β), + companion ∈ companions → Certified.Modeled.CompanionChecked entries companions companion) → + Certified.Modeled.CheckedCompanions entries companions +premise definition Ix.Theory.Certified.Modeled.checkCompanions? + type: {β : Type u} → + [inst : DecidableEq β] → + Nat → + (entries : Model.Environment β) → + (companions : List (Certified.Modeled.Companion β)) → + List (List (Certified.Modeled.EquationWitness β)) → + Option (Certified.CheckedClaim (Certified.Modeled.CheckedCompanions entries companions)) + definition: fun {β} [DecidableEq β] fuel entries companions witnesses => + if hu : (List.map (fun x => x.header.ref) companions).Nodup then + if hf : + ∀ (companion : Certified.Modeled.Companion β), companion ∈ companions → entries companion.header.ref = none then + do + let checked ← Certified.Modeled.checkEach? fuel entries companions companions witnesses + pure { down := ⋯ } + else none + else none +premise definition Ix.Theory.Certified.Modeled.assignment + type: {β : Type u} → + [DecidableEq β] → {V : Type v} → Model.Assignment β V → List (Certified.Modeled.Companion β) → Model.Assignment β V + definition: fun {β} [DecidableEq β] {V} constants companions ref levels => + constants (Certified.Modeled.mapping companions ref) levels +premise definition Ix.Theory.Certified.Modeled.SourceMatches + type: {β : Type u} → [DecidableEq β] → Store β → Certified.Modeled.Companion β → Prop + definition: fun {β} [DecidableEq β] store companion => + store.type companion.header.ref = some companion.header.type.erase ∧ + store.uvars companion.header.ref = some companion.header.universes ∧ + Certified.Modeled.sourceRules? store companion.header.ref = + some (List.map Certified.Modeled.eraseRule companion.rules) +premise definition Ix.Theory.Certified.Modeled.sourceRefs? + type: {β : Type u} → [DecidableEq β] → Store β → β → List (ConstRef β) → Option (List (ConstRef β)) + definition: fun {β} [DecidableEq β] store source recursors => do + let families ← Certified.Modeled.sourceFamilies? store source + if recursors.length < families.length then none + else do + let majors ← + List.mapM + (fun ref => do + let info ← Certified.Modeled.recursorSource? store ref + let major ← Certified.Modeled.majorSource? store info + pure major.family) + recursors + if (List.take families.length majors != families) = true then none + else do + let members ← + List.mapM + (fun family => do + let ctors ← Certified.Modeled.familyConstructors? store family + pure (family :: ctors)) + families + pure (members.flatten ++ recursors) +premise definition Ix.Theory.Certified.Modeled.sourceRules? + type: {β : Type u} → [DecidableEq β] → Store β → ConstRef β → Option (List (Certified.Modeled.RawRule β)) + definition: fun {β} [DecidableEq β] store ref => + match ref with + | ConstRef.ctor block member c => do + let _ ← Certified.Modeled.familySource? store (ConstRef.member block member) + let ctor ← store.lookupCtor ref + if ctor.safety = Safety.safe then some [] else none + | ConstRef.member b i => + match store.lookup ref with + | some (Const.induct uvars nparams nindices type ctors Safety.safe) => some [] + | some (Const.recursor uvars nparams nindices nmotives nminors type rules k safety) => + Certified.Modeled.recursorRules? store ref + | x => none +premise definition Ix.Theory.Certified.Modeled.betaHead? + type: {β : Type u} → Nat → VExpr β → Option (VExpr β) + definition: fun {β} x x_1 => + Nat.brecOn (motive := fun x => VExpr β → Option (VExpr β)) x Certified.Modeled.betaHead?._f x_1 +premise definition Ix.Theory.Certified.Modeled.majorSource? + type: {β : Type u} → + [DecidableEq β] → Store β → Certified.Modeled.RecursorSource β → Option (Certified.Modeled.MajorSource β) + definition: fun {β} [DecidableEq β] store source => do + let __x ← Certified.Modeled.splitPi? source.prefix source.type + match __x with + | (prefixDomains, tail) => do + let __x ← Certified.Modeled.splitPi? source.indices tail + match __x with + | (fst, major.forallE body) => do + let major ← Certified.Modeled.betaHead? 512 major + match major.appHead with + | VExpr.const family levels => do + let info ← Certified.Modeled.familySource? store family + if (info.indices != source.indices || levels.length != info.universes) = true then none + else + have arguments := major.appArgs []; + if (arguments.length != info.parameters + source.indices) = true then none + else + if (List.drop info.parameters arguments != VExpr.bvarRevRange 0 source.indices) = true then none + else + have actual := List.take info.parameters arguments; + have parameters := List.map (fun p => p.unliftN source.indices 0) actual; + if (List.map (fun x => VExpr.liftN source.indices x) parameters != actual) = true then none + else + pure + { family := family, levels := levels, parameters := parameters, + prefixDomains := prefixDomains } + | x => none + | x => none +premise definition Ix.Theory.Certified.Modeled.ruleSource? + type: {β : Type u} → + [DecidableEq β] → + Store β → + ConstRef β → + Certified.Modeled.RecursorSource β → + Certified.Modeled.MajorSource β → ConstRef β → Option (Certified.Modeled.RawRule β) + definition: fun {β} [DecidableEq β] store recursor source major constructor => + match constructor with + | ConstRef.ctor block member c => + if (major.family != ConstRef.member block member) = true then none + else do + let family ← Certified.Modeled.familySource? store major.family + let ctor ← store.lookupCtor constructor + if (ctor.safety != Safety.safe || ctor.uvars != family.universes || ctor.nparams != family.parameters) = true then + none + else + match store.ctorRuleIndex? constructor with + | some slot => do + let stored ← source.rules[slot]? + if (stored.nfields != ctor.nfields) = true then none + else do + let specialized ← + Certified.Modeled.applyPi? (VExpr.liftN source.prefix (VExpr.instL major.levels ctor.type)) + major.parameters + let __x ← Certified.Modeled.splitPi? ctor.nfields specialized + match __x with + | (fields, result) => + if (result.appHead != VExpr.const major.family major.levels) = true then none + else + have args := result.appArgs []; + have parameters := List.map (fun x => VExpr.liftN ctor.nfields x) major.parameters; + if + (args.length != ctor.nparams + source.indices || List.take ctor.nparams args != parameters) = + true then + none + else + have constructorValue := + (VExpr.const constructor major.levels).appN (parameters ++ VExpr.bvarRevRange 0 ctor.nfields); + have callArgs := + VExpr.bvarRevRange ctor.nfields source.prefix ++ List.drop ctor.nparams args ++ + [constructorValue]; + do + let resultType ← + Certified.Modeled.applyPi? (VExpr.liftN (source.prefix + ctor.nfields) source.type) callArgs + have telescope : List (VExpr β) := major.prefixDomains ++ fields + let _ ← Certified.Modeled.splitLam? telescope.length stored.rhs + pure + { universes := source.universes, type := VExpr.forallN telescope resultType, + lhs := + VExpr.lamN telescope + ((VExpr.const recursor (List.map VLevel.param (List.range source.universes))).appN + callArgs), + rhs := stored.rhs } + | x => none + | x => none +premise constructor Ix.Theory.Certified.Modeled.Witness.mk + type: {β : Type u} → + β → + List (ConstRef β) → + List (Certified.Modeled.Companion β) → + List (List (Certified.Modeled.EquationWitness β)) → Certified.Modeled.Witness β +premise constructor Ix.Theory.Certified.Modeled.Checked.mk + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} {store : Store β} + {witness : Certified.Modeled.Witness β}, + Certified.Modeled.sourceRefs? store witness.source witness.recursors = + some (List.map (fun x => x.header.ref) witness.companions) → + (∀ (companion : Certified.Modeled.Companion β), + companion ∈ witness.companions → Certified.Modeled.SourceMatches store companion) → + Certified.Modeled.CheckedCompanions entries witness.companions → Certified.Modeled.Checked entries store witness +premise definition Ix.Theory.Certified.Modeled.EntrySource + type: {β : Type u} → Store β → ConstRef β → Model.ConstantEntry β → Prop + definition: fun {β} store ref entry => + ∃ decision companion, + Certified.Modeled.SourceMatches store companion ∧ ref = companion.header.ref ∧ entry = companion.entry +premise definition Ix.Theory.Model.Equinumerous + type: {V : Type u} → (V → V → Prop) → V → V → Prop + definition: fun {V} mem y u => + ∃ f, + (∀ (z : V), mem z y → mem (f z) u) ∧ + (∀ (z z' : V), mem z y → mem z' y → f z = f z' → z = z') ∧ ∀ (w : V), mem w u → ∃ z, mem z y ∧ f z = w +premise definition Ix.Theory.Model.IsTGUniverse + type: {V : Type u} → (V → V → Prop) → V → Prop + definition: fun {V} mem u => + (∀ (y z : V), mem y u → mem z y → mem z u) ∧ + (∀ (y z : V), mem y u → (∀ (w : V), mem w z → mem w y) → mem z u) ∧ + (∀ (y : V), mem y u → ∃ p, mem p u ∧ ∀ (z : V), (∀ (w : V), mem w z → mem w y) → mem z p) ∧ + ∀ (y : V), (∀ (w : V), mem w y → mem w u) → Model.Equinumerous mem y u ∨ mem y u +premise constructor Ix.Theory.Model.SetTheory.mk + type: {V : Type u} → + (Mem : V → V → Prop) → + (∀ {x y : V}, (∀ (z : V), Mem z x ↔ Mem z y) → x = y) → + (upair : V → V → V) → + (∀ {z a b : V}, Mem z (upair a b) ↔ z = a ∨ z = b) → + (sUnion : V → V) → + (∀ {z x : V}, Mem z (sUnion x) ↔ ∃ y, Mem y x ∧ Mem z y) → + (power : V → V) → + (∀ {z x : V}, Mem z (power x) ↔ ∀ (w : V), Mem w z → Mem w x) → + (∀ (x : V), (∃ y, Mem y x) → ∃ y, Mem y x ∧ ¬∃ z, Mem z y ∧ Mem z x) → + (image : (V → V) → V → V) → + (∀ {f : V → V} {a z : V}, Mem z (image f a) ↔ ∃ w, Mem w a ∧ z = f w) → + (univChain : Nat → V) → + (∀ (n : Nat), Mem (univChain n) (univChain (n + 1))) → + (∀ (n : Nat), Model.IsTGUniverse Mem (univChain n)) → Model.SetTheory V +premise definition Ix.Theory.Model.AExpr.Scope + type: {β : Type u_1} → Nat → Nat → Model.AExpr β → Prop + definition: fun {β} universes depth x => + Model.AExpr.brecOn (motive := fun x => Nat → Prop) x (Model.AExpr.Scope._f universes) depth +premise definition Ix.Theory.Model.Reading + type: {β : Type u} → Nat → Nat → VExpr β → Type (max 0 u) + definition: fun {β} n k source => { e // e.erase = source ∧ Model.AExpr.Scope n k e } +premise constructor Ix.Theory.Certified.DefinitionReading.mk + type: {β : Type u} → + (universes : Nat) → + Ix.Theory.DefKind → + (type body : Model.AExpr β) → + Model.AExpr.Scope universes 0 type → Model.AExpr.Scope universes 0 body → Certified.DefinitionReading β +premise constructor Ix.Theory.Certified.PrimitiveSignature.mk + type: {β : Type u} → + (falseType falseElim : ConstRef β) → falseType ≠ falseElim → Option (ConstRef β) → Certified.PrimitiveSignature β +premise constructor Ix.Theory.Certified.CheckedStore.mk + type: {β : Type u} → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → + {targets : List (ConstRef β)} → + (environment : Certified.AdmittedEnvironment signature store) → + (∀ (r : ConstRef β), r ∈ targets → (environment.entries r).isSome = true) → + Certified.CheckedStore signature store targets +premise constructor Ix.Theory.Certified.SourceHeader.mk + type: ∀ {β : Type u} {store : Store β} {ref : ConstRef β} {entry : Model.ConstantEntry β}, + store.type ref = some entry.type.erase → + store.uvars ref = some entry.universes → Certified.SourceHeader store ref entry +premise constructor Ix.Theory.Certified.CheckedInterface.mk + type: {β : Type u} → + {signature : Certified.PrimitiveSignature β} → + (entries : Model.Environment β) → entries.WF → signature.Present entries → Certified.CheckedInterface signature +premise constructor Ix.Theory.Certified.CheckedExtension.mk + type: {β : Type u} → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → + {input : Certified.CheckedInterface signature} → + (result : Certified.CheckedInterface signature) → + Certified.Extends signature input.entries result.entries → + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + result.entries r = some entry → + input.entries r = some entry ∨ Certified.EntrySource signature store r entry) → + Certified.CheckedExtension signature store input +premise constructor Ix.Theory.Certified.FrontierWitness.mk + type: {β : Type u} → + ConstRef β → Model.AnnotationTree → VLevel → Certified.TypingWitness β → Certified.FrontierWitness β +premise constructor Ix.Theory.Certified.FrontierHeader.mk + type: {β : Type u} → + {store : Store β} → + (header : Certified.Signature.Header β) → + Certified.SourceHeader store header.ref header.entry → + Certified.deferredSource store header.ref = true → Certified.FrontierHeader store +premise definition Ix.Theory.Certified.deferredSource + type: {β : Type u} → Store β → ConstRef β → Bool + definition: fun {β} store ref => + match store.lookup ref with + | some (Const.defn uvars kind type value Safety.safe) => true + | some (Const.induct uvars nparams nindices type ctors Safety.safe) => true + | some (Const.recursor uvars nparams nindices nmotives nminors type rules k Safety.safe) => true + | some val => false + | none => + match store.lookupCtor ref with + | some ctor => ctor.safety == Safety.safe + | none => false +premise constructor Ix.Theory.Certified.CheckedFrontier.mk + type: {β : Type u} → + [inst : DecidableEq β] → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → + (readings : List (Certified.FrontierHeader store)) → + signature.validate store = true → + Certified.Signature.Formed signature.environment (List.map (fun x => x.header) readings) → + Certified.CheckedFrontier signature store +premise definition Ix.Theory.Certified.CheckedFrontier.interface + type: {β : Type u} → + [inst : DecidableEq β] → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → Certified.CheckedFrontier signature store → Certified.CheckedInterface signature + definition: fun {β} [DecidableEq β] {signature} {store} frontier => + { entries := Certified.Signature.environment signature.environment frontier.headers, wf := ⋯, present := ⋯ } +premise definition Ix.Theory.Certified.HeaderPresent + type: {β : Type u} → Model.Environment β → Certified.Signature.Header β → Prop + definition: fun {β} entries header => + match entries header.ref with + | none => False + | some entry => entry.universes = header.universes ∧ entry.type = header.type +premise constructor Ix.Theory.Certified.ConditionalStore.mk + type: {β : Type u} → + [inst : DecidableEq β] → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → + {subjects : List (ConstRef β)} → + (frontier : Certified.CheckedFrontier signature store) → + (checked : Certified.CheckedExtension signature store frontier.interface) → + (∀ (r : ConstRef β), r ∈ subjects → frontier.interface.entries r = none) → + (∀ (r : ConstRef β), r ∈ subjects → (checked.result.entries r).isSome = true) → + Certified.ConditionalStore signature store subjects +premise constructor Ix.Theory.Certified.ClaimNode.mk + type: {β : Type u} → + List (ConstRef β) → List (Certified.FrontierWitness β) → List (Certified.DeclarationWitness β) → Certified.ClaimNode β +premise constructor Ix.Theory.Certified.CheckedNode.mk + type: {β : Type u} → + [inst : DecidableEq β] → + {fuel : Nat} → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → + (input : Certified.ClaimNode β) → + (receipt : Certified.ConditionalStore signature store input.subjects) → + Certified.checkConditionalStore? fuel signature store input.frontier input.subjects input.declarations = + some receipt → + Certified.CheckedNode fuel signature store +premise constructor Ix.Theory.Certified.ReplayResult.mk + type: {β : Type u} → + [inst : DecidableEq β] → + {fuel : Nat} → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → + {state : Certified.CheckedInterface signature} → + {known : List (ConstRef β)} → + {nodes : List (Certified.CheckedNode fuel signature store)} → + (checked : Certified.CheckedExtension signature store state) → + Certified.DependencyOrder known nodes → + (∀ (ref : ConstRef β), + ref ∈ Certified.nodeSubjects nodes → (checked.result.entries ref).isSome = true) → + (∀ (node : Certified.CheckedNode fuel signature store), + node ∈ nodes → + ∀ (header : Certified.Signature.Header β), + header ∈ node.receipt.frontier.headers → + Certified.HeaderPresent checked.result.entries header) → + Certified.ReplayResult state known nodes +premise constructor Ix.Theory.Certified.CheckedBatch.mk + type: {β : Type u} → + [inst : DecidableEq β] → + {fuel : Nat} → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → + {inputs : List (Certified.ClaimNode β)} → + (nodes : List (Certified.CheckedNode fuel signature store)) → + List.map (fun x => x.input) nodes = inputs → + (receipt : Certified.ConditionalStore signature store (Certified.nodeSubjects nodes)) → + (∀ (ref : ConstRef β), ref ∈ receipt.frontier.refs ↔ ref ∈ Certified.outstanding nodes) → + Certified.DependencyOrder (receipt.frontier.refs ++ [signature.falseType, signature.falseElim]) + nodes → + (∀ (node : Certified.CheckedNode fuel signature store), + node ∈ nodes → + ∀ (header : Certified.Signature.Header β), + header ∈ node.receipt.frontier.headers → + Certified.HeaderPresent receipt.checked.result.entries header) → + Certified.CheckedBatch fuel signature store inputs +premise constructor Ix.Theory.Certified.DependencyOrder.nil + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} (known : List (ConstRef β)), Certified.DependencyOrder known [] +premise constructor Ix.Theory.Certified.DependencyOrder.cons + type: ∀ {β : Type u} [inst : DecidableEq β] {fuel : Nat} {signature : Certified.PrimitiveSignature β} + {store : Store β} {known : List (ConstRef β)} {node : Certified.CheckedNode fuel signature store} + {rest : List (Certified.CheckedNode fuel signature store)}, + (∀ (ref : ConstRef β), ref ∈ node.receipt.frontier.refs → ref ∈ known) → + Certified.DependencyOrder (known ++ node.input.subjects) rest → Certified.DependencyOrder known (node :: rest) +premise definition Ix.Theory.Certified.DependencyEdge + type: {β : Type u} → + [inst : DecidableEq β] → + {fuel : Nat} → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → List (Certified.CheckedNode fuel signature store) → Nat → Nat → Prop + definition: fun {β} [DecidableEq β] {fuel} {signature} {store} nodes provider consumer => + ∃ a b ref, + nodes[provider]? = some a ∧ + nodes[consumer]? = some b ∧ + ref ∈ a.input.subjects ∧ + ref ∈ b.receipt.frontier.refs ∧ + ∀ (previous : Nat), + previous < provider → + ∀ (other : Certified.CheckedNode fuel signature store), + nodes[previous]? = some other → ¬ref ∈ other.input.subjects +premise definition Ix.Theory.Certified.outstanding + type: {β : Type u} → + [inst : DecidableEq β] → + {fuel : Nat} → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → List (Certified.CheckedNode fuel signature store) → List (ConstRef β) + definition: fun {β} [DecidableEq β] {fuel} {signature} {store} nodes => + (List.filter (fun ref => !(Certified.nodeSubjects nodes).contains ref) (Certified.nodeFrontiers nodes)).eraseDups +premise definition Ix.Theory.Certified.logicalAxioms + type: {β : Type u} → Store β → Model.Environment β → List (ConstRef β) + definition: fun {β} store entries => + List.filter (fun ref => Certified.isLogicalAxiom store ref && (entries ref).isSome) (Certified.sourceMembers store) +premise definition Ix.Theory.Certified.CheckedBatch.logicalUses + type: {β : Type u} → + [inst : DecidableEq β] → + {fuel : Nat} → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → + {inputs : List (Certified.ClaimNode β)} → + Certified.CheckedBatch fuel signature store inputs → List (ConstRef β) + definition: fun {β} [DecidableEq β] {fuel} {signature} {store} {inputs} batch => + (Certified.logicalAxioms store batch.receipt.checked.result.entries ++ + List.flatMap (fun node => Certified.logicalAxioms store node.receipt.checked.result.entries) + batch.nodes).eraseDups +premise definition Ix.Theory.Model.WellDenoted + type: {β : Type u} → + {V : Type v} → [Model.SetTheory V] → Model.Assignment β V → List Nat → (Nat → V) → Model.AExpr β → Prop + definition: fun {β} {V} [Model.SetTheory V] constants levels env x => + Model.AExpr.brecOn (motive := fun x => (Nat → V) → Prop) x (Model.WellDenoted._f constants levels) env +premise definition Ix.Theory.Model.Context.Valid + type: {β : Type u} → + {V : Type v} → [Model.SetTheory V] → Model.Assignment β V → List Nat → Model.Context β → (Nat → V) → Prop + definition: fun {β} {V} [Model.SetTheory V] constants levels Γ env => + ∀ (i : Nat) (A : Model.AExpr β), + Γ[i]? = some A → + Model.WellDenoted constants levels env A ∧ Model.SetTheory.Mem (env i) (Model.interp constants levels env A) +premise definition Ix.Theory.Model.TypingClaim + type: {β : Type u} → Model.Environment β → Model.Context β → Model.AExpr β → Model.AExpr β → Prop + definition: fun {β} entries Γ e A => + ∀ (V : Type v) [inst : Model.SetTheory V] (constants : Model.Assignment β V), + Model.Realizes constants entries → + ∀ (levels : List Nat) (env : Nat → V), + Model.Context.Valid constants levels Γ env → + Model.WellDenoted constants levels env e ∧ + Model.WellDenoted constants levels env A ∧ + Model.SetTheory.Mem (Model.interp constants levels env e) (Model.interp constants levels env A) +premise definition Ix.Theory.Model.ConversionClaim + type: {β : Type u} → Model.Environment β → Model.Context β → Model.AExpr β → Model.AExpr β → Prop + definition: fun {β} entries Γ a b => + ∀ (V : Type v) [inst : Model.SetTheory V] (constants : Model.Assignment β V), + Model.Realizes constants entries → + ∀ (levels : List Nat) (env : Nat → V), + Model.Context.Valid constants levels Γ env → + Model.interp constants levels env a = Model.interp constants levels env b +premise constructor Ix.Theory.Model.ConstantEntry.mk + type: {β : Type u} → + Nat → + Model.AExpr β → + Option (Model.AExpr β) → List (Model.ConstantEquation β) → List (Model.ConstantFact β) → Model.ConstantEntry β +premise constructor Ix.Theory.Model.Realizes.mk + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {constants : Model.Assignment β V} + {entries : Model.Environment β}, + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + entries r = some entry → + ∀ (levels : List Nat), + levels.length = entry.universes → ∀ (env : Nat → V), Model.WellDenoted constants levels env entry.type) → + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + entries r = some entry → + ∀ (levels : List Nat), + levels.length = entry.universes → + ∀ (env : Nat → V), + Model.SetTheory.Mem (constants r levels) (Model.interp constants levels env entry.type)) → + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + entries r = some entry → + ∀ (body : Model.AExpr β), + entry.body = some body → + ∀ (levels : List Nat), + levels.length = entry.universes → ∀ (env : Nat → V), Model.WellDenoted constants levels env body) → + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + entries r = some entry → + ∀ (body : Model.AExpr β), + entry.body = some body → + ∀ (levels : List Nat), + levels.length = entry.universes → + ∀ (env : Nat → V), constants r levels = Model.interp constants levels env body) → + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + entries r = some entry → + ∀ (equation : Model.ConstantEquation β), + equation ∈ entry.equations → + ∀ (levels : List Nat), + levels.length = entry.universes → + ∀ (env : Nat → V), + Model.interp constants levels env equation.lhs = + Model.interp constants levels env equation.rhs) → + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + entries r = some entry → + ∀ (fact : Model.ConstantFact β), + fact ∈ entry.facts → + ∀ (levels : List Nat), + levels.length = entry.universes → + ∀ (env : Nat → V), Model.ConstantFact.Meaning constants r levels env fact) → + Model.Realizes constants entries +premise constructor Ix.Theory.Model.ConstantEquation.mk + type: {β : Type u} → Model.AExpr β → Model.AExpr β → Model.ConstantEquation β +premise constructor Ix.Theory.Model.ConstantFact.typed + type: {β : Type u} → Model.AExpr β → Model.AExpr β → Model.ConstantFact β +premise constructor Ix.Theory.Model.ConstantFact.natural + type: {β : Type u} → ConstRef β → ConstRef β → Model.ConstantFact β +premise definition Ix.Theory.Model.ConstantFact.Meaning + type: {β : Type u} → + {V : Type v} → + [Model.SetTheory V] → Model.Assignment β V → ConstRef β → List Nat → (Nat → V) → Model.ConstantFact β → Prop + definition: fun {β} {V} [Model.SetTheory V] constants owner levels env x => + match x with + | Model.ConstantFact.typed e type => + Model.WellDenoted constants levels env e ∧ + Model.WellDenoted constants levels env type ∧ + Model.SetTheory.Mem (Model.interp constants levels env e) (Model.interp constants levels env type) + | Model.ConstantFact.natural zero succ => levels = [] ∧ Model.NaturalMeaning constants owner zero succ +premise definition Ix.Theory.Model.ConstantFact.Scope + type: {β : Type u} → Nat → Model.ConstantFact β → Prop + definition: fun {β} n x => + match x with + | Model.ConstantFact.typed e type => Model.AExpr.Scope n 0 e ∧ Model.AExpr.Scope n 0 type + | Model.ConstantFact.natural zero succ => n = 0 +premise definition Ix.Theory.Model.ConstantFact.ReferencesIn + type: {β : Type u} → Model.Environment β → Model.ConstantFact β → Prop + definition: fun {β} entries fact => ∀ (r : ConstRef β), r ∈ fact.references → (entries r).isSome = true +premise constructor Ix.Theory.Model.NaturalMeaning.mk + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {constants : Model.Assignment β V} + {family zero succ : ConstRef β}, + (∀ (n : Nat), Model.SetTheory.Mem (Model.Numeral.value n) (constants family [])) → + constants zero [] = Model.Numeral.value 0 → + (∀ (n : Nat), Model.SetTheory.app (constants succ []) (Model.Numeral.value n) = Model.Numeral.value (n + 1)) → + Model.NaturalMeaning constants family zero succ +premise definition Ix.Theory.Model.projectValue + type: {V : Type u} → [Model.SetTheory V] → Nat → V → V + definition: fun {V} [Model.SetTheory V] field value => + Model.SetTheory.Tower.projS field (Model.SetTheory.ssnd (Model.SetTheory.sfst value)) +premise definition Ix.Theory.Model.Numeral.zero + type: {V : Type u} → [Model.SetTheory V] → V + definition: fun {V} [Model.SetTheory V] => + Model.SetTheory.spair (Model.SetTheory.Tower.inj 0 Model.SetTheory.pt) Model.SetTheory.empty +premise definition Ix.Theory.Model.Numeral.succ + type: {V : Type u} → [Model.SetTheory V] → V → V + definition: fun {V} [Model.SetTheory V] n => + Model.SetTheory.spair (Model.SetTheory.Tower.inj 1 Model.SetTheory.pt) + (Model.SetTheory.graph (fun x => n) (Model.SetTheory.sing (Model.SetTheory.Tower.inj 0 Model.SetTheory.pt))) +premise definition Ix.Theory.Model.Numeral.value + type: {V : Type u} → [Model.SetTheory V] → Nat → V + definition: fun {V} [Model.SetTheory V] x => Nat.brecOn x Model.Numeral.value._f +premise constructor Ix.Theory.Model.Environment.WF.mk + type: ∀ {β : Type u} {entries : Model.Environment β}, + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + entries r = some entry → Model.AExpr.Scope entry.universes 0 entry.type) → + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + entries r = some entry → + ∀ (body : Model.AExpr β), entry.body = some body → Model.AExpr.Scope entry.universes 0 body) → + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + entries r = some entry → Model.AExpr.ReferencesIn entries entry.type) → + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + entries r = some entry → + ∀ (body : Model.AExpr β), entry.body = some body → Model.AExpr.ReferencesIn entries body) → + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + entries r = some entry → + ∀ (equation : Model.ConstantEquation β), + equation ∈ entry.equations → + Model.AExpr.Scope entry.universes 0 equation.lhs ∧ + Model.AExpr.Scope entry.universes 0 equation.rhs) → + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + entries r = some entry → + ∀ (equation : Model.ConstantEquation β), + equation ∈ entry.equations → + Model.AExpr.ReferencesIn entries equation.lhs ∧ Model.AExpr.ReferencesIn entries equation.rhs) → + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + entries r = some entry → + ∀ (fact : Model.ConstantFact β), + fact ∈ entry.facts → Model.ConstantFact.Scope entry.universes fact) → + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + entries r = some entry → + ∀ (fact : Model.ConstantFact β), + fact ∈ entry.facts → Model.ConstantFact.ReferencesIn entries fact) → + entries.WF +premise definition Ix.Theory.Model.Assignment.AgreesOn + type: {β : Type u} → {V : Type u_1} → Model.Environment β → Model.Assignment β V → Model.Assignment β V → Prop + definition: fun {β} {V} entries constants constants' => + ∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + entries r = some entry → ∀ (levels : List Nat), constants' r levels = constants r levels +premise constructor Ix.Theory.Model.IndexedContainer.mk + type: {V : Type u} → V → (V → V) → (V → V) → (V → V → V) → Model.IndexedContainer V +premise constructor Ix.Theory.Model.IndexedContainer.WF.mk + type: ∀ {V : Type u} [inst : Model.SetTheory V] {D : Model.IndexedContainer V} {w : Nat}, + (w ≠ 0 → ∀ (i : V), Model.SetTheory.Mem i D.indices → Model.SetTheory.Mem (D.shapes i) (Model.SetTheory.univ w)) → + (w ≠ 0 → + ∀ (i a : V), + Model.SetTheory.Mem i D.indices → + Model.SetTheory.Mem a (D.shapes i) → Model.SetTheory.Mem (D.positions a) (Model.SetTheory.univ w)) → + (∀ (i a p : V), + Model.SetTheory.Mem i D.indices → + Model.SetTheory.Mem a (D.shapes i) → + Model.SetTheory.Mem p (D.positions a) → Model.SetTheory.Mem (D.target a p) D.indices) → + D.WF w +premise definition Ix.Theory.Model.IndexedContainer.LargeElim + type: {V : Type u} → [Model.SetTheory V] → Model.IndexedContainer V → Nat → Prop + definition: fun {V} [Model.SetTheory V] D w => + w = 0 → + ∀ (i : V), + Model.SetTheory.Mem i D.indices → + ∀ (a b : V), Model.SetTheory.Mem a (D.shapes i) → Model.SetTheory.Mem b (D.shapes i) → a = b +premise definition Ix.Theory.Model.IndexedContainer.AlgebraTyping + type: {V : Type u} → [Model.SetTheory V] → Model.IndexedContainer V → Nat → (V → V → V) → (V → V → V → V → V) → Prop + definition: fun {V} [Model.SetTheory V] D w M algebra => + ∀ (i : V), + Model.SetTheory.Mem i D.indices → + ∀ (a : V), + Model.SetTheory.Mem a (D.shapes i) → + ∀ (g : V), + Model.SetTheory.Mem g + (Model.SetTheory.piSet (D.positions a) fun p => Model.SetTheory.app (D.carrier w) (D.target a p)) → + ∀ (ih : V), + Model.SetTheory.Mem ih + (Model.SetTheory.piSet (D.positions a) fun p => M (D.target a p) (Model.SetTheory.app g p)) → + Model.SetTheory.Mem (algebra i a g ih) (M i (Model.IndexedContainer.node w a g)) +premise definition Ix.Theory.Certified.TelescopeBound + type: {β : Type u} → Model.Environment β → Model.Context β → List (Model.AExpr β) → Option VLevel → Prop + definition: fun {β} entries Γ domains bound => + ∀ (w : VLevel), + bound = some w → + ∀ (V : Type v) [inst : Model.SetTheory V] (constants : Model.Assignment β V), + Model.Realizes constants entries → + ∀ (levels : List Nat) (env : Nat → V), + Model.Context.Valid constants levels Γ env → + VLevel.eval levels w ≠ 0 → + Model.SetTheory.Tower.BoundS (VLevel.eval levels w) + (Model.Telescope.interpret constants levels env domains) +premise definition Ix.Theory.Certified.TelescopeProp + type: {β : Type u} → Model.Environment β → Model.Context β → List (Model.AExpr β) → VLevel → Prop + definition: fun {β} entries Γ domains atLevel => + ∀ (V : Type v) [inst : Model.SetTheory V] (constants : Model.Assignment β V), + Model.Realizes constants entries → + ∀ (levels : List Nat) (env : Nat → V), + Model.Context.Valid constants levels Γ env → + VLevel.eval levels atLevel = 0 → + Model.SetTheory.Tower.PropS (Model.Telescope.interpret constants levels env domains) +premise definition Ix.Theory.Certified.ArgumentsFit + type: {β : Type u} → Model.Environment β → Model.Context β → List (Model.AExpr β) → List (Model.AExpr β) → Prop + definition: fun {β} entries Γ domains args => + ∀ (V : Type v) [inst : Model.SetTheory V] (constants : Model.Assignment β V), + Model.Realizes constants entries → + ∀ (levels : List Nat) (env : Nat → V), + Model.Context.Valid constants levels Γ env → + Model.SetTheory.Tower.FitsS (Model.Telescope.interpret constants levels env domains) + (List.map (Model.interp constants levels env) args) +premise constructor Ix.Theory.Certified.Ordinary.Shape.mk + type: {β : Type u} → + Nat → + List (Model.AExpr β) → + List (Model.AExpr β) → VLevel → List (Certified.Ordinary.Constructor β) → Certified.Ordinary.Shape β +premise constructor Ix.Theory.Certified.Ordinary.Constructor.mk + type: {β : Type u} → + List (Model.AExpr β) → + List (Certified.Ordinary.RecursiveField β) → List (Model.AExpr β) → Certified.Ordinary.Constructor β +premise constructor Ix.Theory.Certified.Ordinary.RecursiveField.mk + type: {β : Type u} → List (Model.AExpr β) → List (Model.AExpr β) → Certified.Ordinary.RecursiveField β +premise constructor Ix.Theory.Certified.Ordinary.CheckedShape.mk + type: ∀ {β : Type u} {entries : Model.Environment β} {store : Store β} {source : β} + {shape : Certified.Ordinary.Shape β}, + store.blocks source = some { members := [shape.source source] } → + (∀ (r : ConstRef β), r ∈ shape.references source → entries r = none) → + (∀ (e : Model.AExpr β), e ∈ shape.parameters ++ shape.indices → Model.AExpr.ReferencesIn entries e) → + Model.AExpr.Scope shape.universes 0 shape.type → + (∀ (ctor : Certified.Ordinary.Constructor β), + ctor ∈ shape.constructors → Model.AExpr.Scope shape.universes 0 (ctor.type shape source)) → + Model.Telescope.Formed entries [] shape.parameters → + Model.Telescope.Formed entries shape.parameterContext shape.indices → + (∀ (ctor : Certified.Ordinary.Constructor β), + ctor ∈ shape.constructors → Certified.Ordinary.ConstructorEvidence entries shape ctor) → + Certified.Ordinary.CheckedShape entries store source shape +premise definition Ix.Theory.Certified.Ordinary.ConstructorEvidence + type: {β : Type u} → Model.Environment β → Certified.Ordinary.Shape β → Certified.Ordinary.Constructor β → Prop + definition: fun {β} entries shape ctor => + (∀ (e : Model.AExpr β), e ∈ ctor.fields ++ ctor.indices → Model.AExpr.ReferencesIn entries e) ∧ + Model.Telescope.Formed entries shape.parameterContext ctor.fields ∧ + Certified.TelescopeBound entries shape.parameterContext ctor.fields (some shape.level) ∧ + Certified.ArgumentsFit entries (ctor.context shape) (Model.Telescope.lift ctor.fields.length shape.indices) + ctor.indices ∧ + ∀ (field : Certified.Ordinary.RecursiveField β), + field ∈ ctor.recursive → Certified.Ordinary.RecursiveEvidence entries shape ctor field +premise definition Ix.Theory.Certified.Ordinary.RecursiveEvidence + type: {β : Type u} → + Model.Environment β → + Certified.Ordinary.Shape β → Certified.Ordinary.Constructor β → Certified.Ordinary.RecursiveField β → Prop + definition: fun {β} entries shape ctor field => + (∀ (e : Model.AExpr β), e ∈ field.domains ++ field.indices → Model.AExpr.ReferencesIn entries e) ∧ + Model.Telescope.Formed entries (ctor.context shape) field.domains ∧ + Certified.TelescopeBound entries (ctor.context shape) field.domains (some shape.level) ∧ + Certified.ArgumentsFit entries (Model.Telescope.context (ctor.context shape) field.domains) + (Model.Telescope.lift (ctor.fields.length + field.domains.length) shape.indices) field.indices +premise definition Ix.Theory.Certified.Ordinary.LargeEvidence + type: {β : Type u} → Model.Environment β → Certified.Ordinary.Shape β → Prop + definition: fun {β} entries shape => + (∀ (levels : List Nat), VLevel.eval levels shape.level ≠ 0) ∨ + shape.constructors = [] ∨ + ∃ ctor, + shape.constructors = [ctor] ∧ Certified.TelescopeProp entries shape.parameterContext ctor.fields shape.level +premise definition Ix.Theory.Certified.Ordinary.Shape.container + type: {β : Type u} → + {V : Type v} → + [Model.SetTheory V] → + Certified.Ordinary.Shape β → Model.Assignment β V → List Nat → (Nat → V) → Model.IndexedContainer V + definition: fun {β} {V} [Model.SetTheory V] shape constants levels env => + { indices := shape.indexSet constants levels env, + shapes := fun i => + Model.SetTheory.sep (shape.allShapes constants levels env) fun a => shape.resultIndex constants levels env a = i, + positions := shape.positions constants levels env, target := shape.targetIndex constants levels env } +premise constructor Ix.Theory.Certified.Ordinary.FamilyReading.mk + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {shape : Certified.Ordinary.Shape β} {source : β} {constants reading : Model.Assignment β V}, + Model.Assignment.AgreesOn entries constants reading → + (∀ (levels : List Nat), + levels.length = shape.universes → + reading (ConstRef.member source 0) levels = shape.familyValue constants levels) → + Certified.Ordinary.FamilyReading entries shape source constants reading +premise constructor Ix.Theory.Certified.Ordinary.ConstructorReading.mk + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {shape : Certified.Ordinary.Shape β} {source : β} {constants reading : Model.Assignment β V}, + Certified.Ordinary.FamilyReading entries shape source constants reading → + (∀ (levels : List Nat), + levels.length = shape.universes → + ∀ (i : Nat) (ctor : Certified.Ordinary.Constructor β), + shape.constructors[i]? = some ctor → + reading (ConstRef.ctor source 0 i) levels = shape.constructorClosedValue constants levels i ctor) → + Certified.Ordinary.ConstructorReading entries shape source constants reading +premise constructor Ix.Theory.Certified.Ordinary.RecursorReading.mk + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {entries : Model.Environment β} + {shape : Certified.Ordinary.Shape β} {source recursor : β} {mode : Inductive.ElimMode} + {constants reading : Model.Assignment β V}, + Certified.Ordinary.ConstructorReading entries shape source constants reading → + (∀ (levels : List Nat), + levels.length = mode.recUvars shape.universes → + reading (ConstRef.member recursor 0) levels = shape.closedRecursorValue constants levels mode) → + Certified.Ordinary.RecursorReading entries shape source recursor mode constants reading +premise definition Ix.Theory.Certified.Ordinary.ModeEvidence + type: {β : Type u} → Model.Environment β → Certified.Ordinary.Shape β → Inductive.ElimMode → Prop + definition: fun {β} entries shape x => + match x with + | Inductive.ElimMode.small => True + | Inductive.ElimMode.large => Certified.Ordinary.LargeEvidence entries shape +premise definition Ix.Theory.Certified.Ordinary.Shape.ConstructorFormation + type: {β : Type u} → [DecidableEq β] → Model.Environment β → Certified.Ordinary.Shape β → β → Prop + definition: fun {β} [DecidableEq β] entries shape source => + ∀ (ctor : Certified.Ordinary.Constructor β), + ctor ∈ shape.constructors → + Model.EntryClosed (shape.familyEnvironment entries source) (shape.constructorEntry source ctor) ∧ + ∃ l, Model.TypingClaim (shape.familyEnvironment entries source) [] (ctor.type shape source) (Model.AExpr.sort l) +premise constructor Ix.Theory.Certified.Ordinary.Shape.RecursorFormation.mk + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} {store : Store β} + {shape : Certified.Ordinary.Shape β} {source recursor : β} {mode : Inductive.ElimMode}, + shape.RecursorSourceMatches store source recursor mode → + shape.constructorEnvironment entries source (ConstRef.member recursor 0) = none → + Model.EntryClosed (shape.constructorEnvironment entries source) (shape.recursorEntry source mode) → + (∃ l, + Model.TypingClaim (shape.constructorEnvironment entries source) [] (shape.recursorType source mode) + (Model.AExpr.sort l)) → + Certified.Ordinary.Shape.RecursorFormation entries store shape source recursor mode +premise definition Ix.Theory.Certified.Ordinary.Shape.SupportsK + type: {β : Type u} → Certified.Ordinary.Shape β → Prop + definition: fun {β} shape => + shape.level = VLevel.zero ∧ ∃ ctor, shape.constructors = [ctor] ∧ ctor.fields = [] ∧ ctor.recursive = [] +premise definition Ix.Theory.Certified.Ordinary.Shape.RecursorSourceMatches + type: {β : Type u} → Certified.Ordinary.Shape β → Store β → β → β → Inductive.ElimMode → Prop + definition: fun {β} shape store source recursor mode => + store.blocks recursor = some { members := [shape.recursorSource source recursor mode] } ∨ + shape.SupportsK ∧ store.blocks recursor = some { members := [shape.recursorSource source recursor mode true] } +premise constructor Ix.Theory.Certified.Ordinary.Shape.RuleFormation.mk + type: ∀ {β : Type u} {entries : Model.Environment β} {shape : Certified.Ordinary.Shape β} {source recursor : β} + {mode : Inductive.ElimMode} {i : Nat} {ctor : Certified.Ordinary.Constructor β}, + Model.AExpr.Scope (mode.recUvars shape.universes) 0 (shape.ruleLhs source recursor mode i ctor) ∧ + Model.AExpr.Scope (mode.recUvars shape.universes) 0 (shape.ruleRhs source recursor mode i ctor) → + Model.AExpr.ReferencesIn entries (shape.ruleLhs source recursor mode i ctor) ∧ + Model.AExpr.ReferencesIn entries (shape.ruleRhs source recursor mode i ctor) → + (∃ l, + Model.TypingClaim entries [] (shape.ruleType source mode i ctor) (Model.AExpr.sort l) ∧ + (mode = Inductive.ElimMode.small → ∀ (levels : List Nat), VLevel.eval levels l = 0)) → + Model.TypingClaim entries [] (shape.ruleLhs source recursor mode i ctor) (shape.ruleType source mode i ctor) → + Model.TypingClaim entries [] (shape.ruleRhs source recursor mode i ctor) (shape.ruleType source mode i ctor) → + Certified.Ordinary.Shape.RuleFormation entries shape source recursor mode i ctor +premise constructor Ix.Theory.Certified.Ordinary.CheckedBlock.mk + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} {store : Store β} {source recursor : β} + {shape : Certified.Ordinary.Shape β} {mode : Inductive.ElimMode}, + Certified.Ordinary.CheckedShape entries store source shape → + Certified.Ordinary.Shape.ConstructorFormation entries shape source → + Certified.Ordinary.Shape.RecursorFormation entries store shape source recursor mode → + Certified.Ordinary.ModeEvidence entries shape mode → + (∀ (i : Nat) (ctor : Certified.Ordinary.Constructor β), + shape.constructors[i]? = some ctor → + Certified.Ordinary.Shape.RuleFormation (shape.recursorEnvironment entries source recursor mode) shape + source recursor mode i ctor) → + Certified.Ordinary.CheckedBlock entries store source recursor shape mode +premise definition Ix.Theory.Certified.Ordinary.EntrySource + type: {β : Type u} → Store β → ConstRef β → Model.ConstantEntry β → Prop + definition: fun {β} store r entry => + ∃ shape source recursor mode, + store.blocks source = some { members := [shape.source source] } ∧ + shape.RecursorSourceMatches store source recursor mode ∧ + (r = ConstRef.member source 0 ∧ entry = shape.familyEntry ∨ + (∃ i ctor, + shape.constructors[i]? = some ctor ∧ + r = ConstRef.ctor source 0 i ∧ entry = shape.constructorEntry source ctor) ∨ + r = ConstRef.member recursor 0 ∧ entry = shape.publishedRecursorEntry source recursor mode) +premise definition Ix.Theory.Certified.Ordinary.Shape.recursorAt + type: {β : Type u} → + {V : Type v} → + [Model.SetTheory V] → + Certified.Ordinary.Shape β → Model.Assignment β V → List Nat → (Nat → V) → Inductive.ElimMode → V → List V → V + definition: fun {β} {V} [Model.SetTheory V] shape constants levels env mode m minors => + match mode with + | Inductive.ElimMode.small => Model.SetTheory.pt + | Inductive.ElimMode.large => + if hD : + (shape.container constants (mode.sourceArgs levels) env).WF + (VLevel.eval (mode.sourceArgs levels) shape.level) then + if hlarge : + (shape.container constants (mode.sourceArgs levels) env).LargeElim + (VLevel.eval (mode.sourceArgs levels) shape.level) then + shape.largeValue constants (mode.sourceArgs levels) env (VLevel.eval levels mode.motiveLevel) m minors hD hlarge + else Model.SetTheory.pt + else Model.SetTheory.pt +premise constructor Ix.Theory.Certified.Ordinary.BlockWitness.mk + type: {β : Type u} → + β → + β → + Certified.Ordinary.ShapeWitness β → + Inductive.ElimMode → + List (Certified.Ordinary.Shape.TypeWitness β) → + Certified.Ordinary.Shape.TypeWitness β → + List (Certified.Ordinary.RuleWitness β) → Certified.Ordinary.BlockWitness β +premise constructor Ix.Theory.Certified.DeclarationWitness.definition + type: {β : Type u} → Certified.DefinitionWitness β → Certified.DeclarationWitness β +premise constructor Ix.Theory.Certified.DeclarationWitness.ordinary + type: {β : Type u} → Certified.Ordinary.BlockWitness β → Certified.DeclarationWitness β +premise constructor Ix.Theory.Certified.DeclarationWitness.standard + type: {β : Type u} → Certified.Standard.Witness β → Certified.DeclarationWitness β +premise constructor Ix.Theory.Certified.DeclarationWitness.quotient + type: {β : Type u} → Certified.Quotient.Witness β → Certified.DeclarationWitness β +premise constructor Ix.Theory.Certified.DeclarationWitness.structure + type: {β : Type u} → Certified.Structure.Witness β → Certified.DeclarationWitness β +premise constructor Ix.Theory.Certified.DeclarationWitness.natural + type: {β : Type u} → Certified.Ordinary.BlockWitness β → Certified.DeclarationWitness β +premise definition Ix.Theory.Certified.Natural.shape + type: {β : Type u} → Certified.Ordinary.Shape β + definition: fun {β} => + { universes := 0, parameters := [], indices := [], level := VLevel.zero.succ, + constructors := [Certified.Natural.zeroConstructor, Certified.Natural.succConstructor] } +premise definition Ix.Theory.Certified.Natural.fact + type: {β : Type u} → β → Model.ConstantFact β + definition: fun {β} source => Model.ConstantFact.natural (ConstRef.ctor source 0 0) (ConstRef.ctor source 0 1) +premise definition Ix.Theory.Certified.Natural.entry + type: {β : Type u} → β → Model.ConstantEntry β + definition: fun {β} source => + have __src := Certified.Natural.shape.familyEntry; + { universes := __src.universes, type := __src.type, body := __src.body, equations := __src.equations, + facts := [Certified.Natural.fact source] } +premise definition Ix.Theory.Certified.Natural.environment + type: {β : Type u} → [DecidableEq β] → Model.Environment β → β → β → Inductive.ElimMode → Model.Environment β + definition: fun {β} [DecidableEq β] entries source recursor mode => + (Certified.Natural.shape.publishedEnvironment entries source recursor mode).insert (ConstRef.member source 0) + (Certified.Natural.entry source) +premise constructor Ix.Theory.Certified.Natural.Checked.mk + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} {store : Store β} {pin : ConstRef β} + {source recursor : β} {mode : Inductive.ElimMode}, + Certified.Ordinary.CheckedBlock entries store source recursor Certified.Natural.shape mode → + pin = ConstRef.member source 0 → + Model.ConstantFact.ReferencesIn (Certified.Natural.shape.publishedEnvironment entries source recursor mode) + (Certified.Natural.fact source) → + Certified.Natural.Checked entries store pin source recursor mode +premise definition Ix.Theory.Certified.Natural.EntrySource + type: {β : Type u} → Option (ConstRef β) → Store β → ConstRef β → Model.ConstantEntry β → Prop + definition: fun {β} pin store r e => + pin = some r ∧ + ∃ source recursor mode, + store.blocks source = some { members := [Certified.Natural.shape.source source] } ∧ + Certified.Natural.shape.RecursorSourceMatches store source recursor mode ∧ + r = ConstRef.member source 0 ∧ e = Certified.Natural.entry source +premise constructor Ix.Theory.Certified.Structure.Field.mk + type: {β : Type u} → Model.AExpr β → VLevel → Certified.Structure.Field β +premise constructor Ix.Theory.Certified.Structure.Description.mk + type: {β : Type u} → + Nat → List (Model.AExpr β) → List (Certified.Structure.Field β) → VLevel → Certified.Structure.Description β +premise definition Ix.Theory.Certified.Structure.Description.ordinary + type: {β : Type u} → Certified.Structure.Description β → Certified.Ordinary.Shape β + definition: fun {β} d => + { universes := d.universes, parameters := d.parameters, indices := [], level := d.level, + constructors := [d.constructor] } +premise constructor Ix.Theory.Certified.Structure.FieldsFormed.nil + type: ∀ {β : Type u} {entries : Model.Environment β} {w : VLevel} {Γ : Model.Context β}, + Certified.Structure.FieldsFormed entries w Γ [] +premise constructor Ix.Theory.Certified.Structure.FieldsFormed.cons + type: ∀ {β : Type u} {entries : Model.Environment β} {w : VLevel} {Γ : Model.Context β} + {field : Certified.Structure.Field β} {rest : List (Certified.Structure.Field β)}, + Model.TypingClaim entries Γ field.domain (Model.AExpr.sort field.level) → + (∀ (levels : List Nat), VLevel.eval levels w = 0 → VLevel.eval levels field.level = 0) → + Certified.Structure.FieldsFormed entries w (Model.Context.push field.domain Γ) rest → + Certified.Structure.FieldsFormed entries w Γ (field :: rest) +premise definition Ix.Theory.Certified.Structure.fieldResult + type: {β : Type u} → β → Nat → Certified.Structure.Field β → Model.AExpr β + definition: fun {β} source index field => + (Model.AExpr.liftN 1 field.domain index).instRev (Certified.Structure.projections source index) +premise definition Ix.Theory.Certified.Structure.Description.facts + type: {β : Type u} → Certified.Structure.Description β → β → List (Model.ConstantFact β) + definition: fun {β} d source => + List.map + (fun x => + match x with + | (field, i) => Model.ConstantFact.typed (d.projection source i field) (d.projectionType source i field)) + d.fields.zipIdx +premise definition Ix.Theory.Certified.Structure.Description.equations + type: {β : Type u} → Certified.Structure.Description β → β → List (Model.ConstantEquation β) + definition: fun {β} d source => + { lhs := d.etaLhs source, rhs := d.etaRhs source } :: + List.map + (fun x => + match x with + | (field, i) => { lhs := d.iotaLhs source i field, rhs := d.iotaRhs i field }) + d.fields.zipIdx +premise definition Ix.Theory.Certified.Structure.Description.iotaEnvironment + type: {β : Type u} → + [DecidableEq β] → + Certified.Structure.Description β → Model.Environment β → β → β → Inductive.ElimMode → Nat → Model.Environment β + definition: fun {β} [DecidableEq β] d entries source recursor mode count => + (d.ordinary.publishedEnvironment entries source recursor mode).insert (ConstRef.member source 0) + (have __src := d.factEntry source; + { universes := __src.universes, type := __src.type, body := __src.body, + equations := List.take count (List.drop 1 (d.equations source)), facts := __src.facts }) +premise definition Ix.Theory.Certified.Structure.Description.etaRule + type: {β : Type u} → Certified.Structure.Description β → β → Certified.Signature.Rule β + definition: fun {β} d source => + { universes := d.universes, + type := + Model.AExpr.forallN (Certified.zeroCondition d.level) (d.projectionDomains source) + (d.ordinary.familyApp source 1 []), + lhs := d.etaLhs source, rhs := d.etaRhs source } +premise definition Ix.Theory.Certified.Structure.Description.iotaRule + type: {β : Type u} → + Certified.Structure.Description β → β → Nat → Certified.Structure.Field β → Certified.Signature.Rule β + definition: fun {β} d source i field => + { universes := d.universes, + type := + Model.AExpr.forallN (Certified.zeroCondition field.level) (d.parameters ++ d.constructor.fields) + (Model.AExpr.liftN (d.fields.length - i) field.domain), + lhs := d.iotaLhs source i field, rhs := d.iotaRhs i field } +premise constructor Ix.Theory.Certified.Structure.FactsWitness.mk + type: {β : Type u} → + Certified.Structure.Description β → + Certified.Ordinary.BlockWitness β → + List (Certified.TypingWitness β) → List (Certified.DomainWitness β) → Certified.Structure.FactsWitness β +premise constructor Ix.Theory.Certified.Structure.Witness.mk + type: {β : Type u} → + Certified.Structure.FactsWitness β → + Certified.Signature.RuleWitness β → List (Certified.Signature.RuleWitness β) → Certified.Structure.Witness β +premise constructor Ix.Theory.Certified.Structure.FactsChecked.mk + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} {store : Store β} + {d : Certified.Structure.Description β} {source recursor : β} {mode : Inductive.ElimMode}, + Certified.Ordinary.CheckedBlock entries store source recursor d.ordinary mode → + Certified.Structure.FieldsFormed entries d.level d.ordinary.parameterContext d.fields → + Model.Telescope.Formed (d.ordinary.publishedEnvironment entries source recursor mode) [] + (d.projectionDomains source) → + (∀ (fact : Model.ConstantFact β), fact ∈ d.facts source → Model.ConstantFact.Scope d.universes fact) → + (∀ (fact : Model.ConstantFact β), + fact ∈ d.facts source → + Model.ConstantFact.ReferencesIn (d.ordinary.publishedEnvironment entries source recursor mode) fact) → + Certified.Structure.FactsChecked entries store d source recursor mode +premise constructor Ix.Theory.Certified.Structure.Checked.mk + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} {store : Store β} + {d : Certified.Structure.Description β} {source recursor : β} {mode : Inductive.ElimMode}, + Certified.Structure.FactsChecked entries store d source recursor mode → + Certified.Signature.RuleFormed (d.factEnvironment entries source recursor mode) (d.etaRule source) → + (∀ (field : Certified.Structure.Field β) (i : Nat), + (field, i) ∈ d.fields.zipIdx → + Certified.Signature.RuleFormed (d.iotaEnvironment entries source recursor mode i) + (d.iotaRule source i field)) → + Certified.Structure.Checked entries store d source recursor mode +premise definition Ix.Theory.Certified.Structure.EntrySource + type: {β : Type u} → Store β → ConstRef β → Model.ConstantEntry β → Prop + definition: fun {β} store r entry => + ∃ d source recursor mode, + store.blocks source = some { members := [d.ordinary.source source] } ∧ + d.ordinary.RecursorSourceMatches store source recursor mode ∧ + r = ConstRef.member source 0 ∧ entry = d.publishedEntry source +premise definition Ix.Theory.Model.Environment.HasType + type: {β : Type u} → Model.Environment β → ConstRef β → Nat → Model.AExpr β → Prop + definition: fun {β} entries ref n type => ∃ entry, entries ref = some entry ∧ entry.universes = n ∧ entry.type = type +premise constructor Ix.Theory.Certified.Basis.Equality.Interface.mk + type: ∀ {β : Type u} {entries : Model.Environment β} {family refl recursor : ConstRef β}, + entries.HasType family 1 Certified.Basis.Equality.type → + entries.HasType refl 1 (Certified.Basis.Equality.reflType family) → + entries.HasType recursor 2 (Certified.Basis.Equality.recType family refl) → + Certified.Basis.Equality.Interface entries family refl recursor +premise definition Ix.Theory.Certified.Basis.Equality.shape + type: {β : Type u} → Certified.Ordinary.Shape β + definition: fun {β} => + { universes := 1, parameters := [Model.AExpr.sort (VLevel.param 0), Model.AExpr.bvar 0], + indices := [Model.AExpr.bvar 1], level := VLevel.zero, + constructors := [{ fields := [], recursive := [], indices := [Model.AExpr.bvar 0] }] } +premise definition Ix.Theory.Certified.Basis.Equality.type + type: {β : Type u} → Model.AExpr β + definition: fun {β} => + Model.AExpr.forallE Certified.PropWhen.never (Model.AExpr.sort (VLevel.param 0)) + (Model.AExpr.forallE Certified.PropWhen.never (Model.AExpr.bvar 0) + (Model.AExpr.forallE Certified.PropWhen.never (Model.AExpr.bvar 1) (Model.AExpr.sort VLevel.zero))) +premise definition Ix.Theory.Certified.Basis.Equality.reflType + type: {β : Type u} → ConstRef β → Model.AExpr β + definition: fun {β} family => + Model.AExpr.forallE Certified.PropWhen.always (Model.AExpr.sort (VLevel.param 0)) + (Model.AExpr.forallE Certified.PropWhen.always (Model.AExpr.bvar 0) + (Certified.Basis.Equality.applied family (VLevel.param 0) (Model.AExpr.bvar 1) (Model.AExpr.bvar 0) + (Model.AExpr.bvar 0))) +premise definition Ix.Theory.Certified.Basis.Equality.recType + type: {β : Type u} → ConstRef β → ConstRef β → Model.AExpr β + definition: fun {β} family refl => + Model.AExpr.forallE (Certified.PropWhen.param 0) (Model.AExpr.sort (VLevel.param 1)) + (Model.AExpr.forallE (Certified.PropWhen.param 0) (Model.AExpr.bvar 0) + (Model.AExpr.forallE (Certified.PropWhen.param 0) + (Model.AExpr.forallE Certified.PropWhen.never (Model.AExpr.bvar 1) + (Model.AExpr.forallE Certified.PropWhen.never + (Certified.Basis.Equality.applied family (VLevel.param 1) (Model.AExpr.bvar 2) (Model.AExpr.bvar 1) + (Model.AExpr.bvar 0)) + (Model.AExpr.sort (VLevel.param 0)))) + (Model.AExpr.forallE (Certified.PropWhen.param 0) + ((Model.AExpr.bvar 0).appN + [Model.AExpr.bvar 1, + Certified.Basis.Equality.reflexivity refl (VLevel.param 1) (Model.AExpr.bvar 2) (Model.AExpr.bvar 1)]) + (Model.AExpr.forallE (Certified.PropWhen.param 0) (Model.AExpr.bvar 3) + (Model.AExpr.forallE (Certified.PropWhen.param 0) + (Certified.Basis.Equality.applied family (VLevel.param 1) (Model.AExpr.bvar 4) (Model.AExpr.bvar 3) + (Model.AExpr.bvar 0)) + ((Model.AExpr.bvar 3).appN [Model.AExpr.bvar 1, Model.AExpr.bvar 0])))))) +premise constructor Ix.Theory.Certified.Basis.Iff.Interface.mk + type: ∀ {β : Type u} {entries : Model.Environment β} {family ctor recursor : ConstRef β}, + entries.HasType family 0 Certified.Basis.Iff.type → + entries.HasType ctor 0 (Certified.Basis.Iff.introType family) → + entries.HasType recursor 1 (Certified.Basis.Iff.recType family ctor) → + Certified.Basis.Iff.Interface entries family ctor recursor +premise definition Ix.Theory.Certified.Basis.Iff.shape + type: {β : Type u} → Certified.Ordinary.Shape β + definition: fun {β} => + { universes := 0, parameters := [Model.AExpr.sort VLevel.zero, Model.AExpr.sort VLevel.zero], indices := [], + level := VLevel.zero, constructors := [Certified.Basis.Iff.constructor] } +premise definition Ix.Theory.Certified.Basis.Iff.type + type: {β : Type u} → Model.AExpr β + definition: fun {β} => + Model.AExpr.forallE Certified.PropWhen.never (Model.AExpr.sort VLevel.zero) + (Model.AExpr.forallE Certified.PropWhen.never (Model.AExpr.sort VLevel.zero) (Model.AExpr.sort VLevel.zero)) +premise definition Ix.Theory.Certified.Basis.Iff.introType + type: {β : Type u} → ConstRef β → Model.AExpr β + definition: fun {β} family => + Model.AExpr.forallE Certified.PropWhen.always (Model.AExpr.sort VLevel.zero) + (Model.AExpr.forallE Certified.PropWhen.always (Model.AExpr.sort VLevel.zero) + (Model.AExpr.forallE Certified.PropWhen.always + (Certified.Basis.Iff.arrow (Model.AExpr.bvar 1) (Model.AExpr.bvar 0)) + (Model.AExpr.forallE Certified.PropWhen.always + (Certified.Basis.Iff.arrow (Model.AExpr.bvar 1) (Model.AExpr.bvar 2)) + (Certified.Basis.Iff.applied family (Model.AExpr.bvar 3) (Model.AExpr.bvar 2))))) +premise definition Ix.Theory.Certified.Basis.Iff.recType + type: {β : Type u} → ConstRef β → ConstRef β → Model.AExpr β + definition: fun {β} family ctor => + Model.AExpr.forallE (Certified.PropWhen.param 0) (Model.AExpr.sort VLevel.zero) + (Model.AExpr.forallE (Certified.PropWhen.param 0) (Model.AExpr.sort VLevel.zero) + (Model.AExpr.forallE (Certified.PropWhen.param 0) + (Model.AExpr.forallE Certified.PropWhen.never + (Certified.Basis.Iff.applied family (Model.AExpr.bvar 1) (Model.AExpr.bvar 0)) + (Model.AExpr.sort (VLevel.param 0))) + (Model.AExpr.forallE (Certified.PropWhen.param 0) + (Model.AExpr.forallE (Certified.PropWhen.param 0) + (Certified.Basis.Iff.arrow (Model.AExpr.bvar 2) (Model.AExpr.bvar 1)) + (Model.AExpr.forallE (Certified.PropWhen.param 0) + (Certified.Basis.Iff.arrow (Model.AExpr.bvar 2) (Model.AExpr.bvar 3)) + ((Model.AExpr.bvar 2).app + (Certified.Basis.Iff.introduction ctor (Model.AExpr.bvar 4) (Model.AExpr.bvar 3) (Model.AExpr.bvar 1) + (Model.AExpr.bvar 0))))) + (Model.AExpr.forallE (Certified.PropWhen.param 0) + (Certified.Basis.Iff.applied family (Model.AExpr.bvar 3) (Model.AExpr.bvar 2)) + ((Model.AExpr.bvar 2).app (Model.AExpr.bvar 0)))))) +premise constructor Ix.Theory.Certified.Basis.Nonempty.Interface.mk + type: ∀ {β : Type u} {entries : Model.Environment β} {family ctor recursor : ConstRef β}, + entries.HasType family 1 Certified.Basis.Nonempty.type → + entries.HasType ctor 1 (Certified.Basis.Nonempty.introType family) → + entries.HasType recursor 1 (Certified.Basis.Nonempty.recType family ctor) → + Certified.Basis.Nonempty.Interface entries family ctor recursor +premise definition Ix.Theory.Certified.Basis.Nonempty.shape + type: {β : Type u} → Certified.Ordinary.Shape β + definition: fun {β} => + { universes := 1, parameters := [Model.AExpr.sort (VLevel.param 0)], indices := [], level := VLevel.zero, + constructors := [Certified.Basis.Nonempty.constructor] } +premise definition Ix.Theory.Certified.Basis.Nonempty.type + type: {β : Type u} → Model.AExpr β + definition: fun {β} => + Model.AExpr.forallE Certified.PropWhen.never (Model.AExpr.sort (VLevel.param 0)) (Model.AExpr.sort VLevel.zero) +premise definition Ix.Theory.Certified.Basis.Nonempty.introType + type: {β : Type u} → ConstRef β → Model.AExpr β + definition: fun {β} family => + Model.AExpr.forallE Certified.PropWhen.always (Model.AExpr.sort (VLevel.param 0)) + (Model.AExpr.forallE Certified.PropWhen.always (Model.AExpr.bvar 0) + (Certified.Basis.Nonempty.applied family (VLevel.param 0) (Model.AExpr.bvar 1))) +premise definition Ix.Theory.Certified.Basis.Nonempty.recType + type: {β : Type u} → ConstRef β → ConstRef β → Model.AExpr β + definition: fun {β} family ctor => + Model.AExpr.forallE Certified.PropWhen.always (Model.AExpr.sort (VLevel.param 0)) + (Model.AExpr.forallE Certified.PropWhen.always + (Model.AExpr.forallE Certified.PropWhen.never + (Certified.Basis.Nonempty.applied family (VLevel.param 0) (Model.AExpr.bvar 0)) (Model.AExpr.sort VLevel.zero)) + (Model.AExpr.forallE Certified.PropWhen.always + (Model.AExpr.forallE Certified.PropWhen.always (Model.AExpr.bvar 1) + ((Model.AExpr.bvar 1).app + (Certified.Basis.Nonempty.introduction ctor (VLevel.param 0) (Model.AExpr.bvar 2) (Model.AExpr.bvar 0)))) + (Model.AExpr.forallE Certified.PropWhen.always + (Certified.Basis.Nonempty.applied family (VLevel.param 0) (Model.AExpr.bvar 2)) + ((Model.AExpr.bvar 2).app (Model.AExpr.bvar 0))))) +premise definition Ix.Theory.Certified.Standard.Spec.Prerequisites + type: {β : Type u} → Model.Environment β → Certified.Standard.Spec β → Prop + definition: fun {β} entries x => + match x with + | Certified.Standard.Spec.propext eq eqRefl eqRec iff iffIntro iffRec => + Certified.Basis.Equality.Interface entries eq eqRefl eqRec ∧ + Certified.Basis.Iff.Interface entries iff iffIntro iffRec + | Certified.Standard.Spec.choice nonempty intro recursor => + Certified.Basis.Nonempty.Interface entries nonempty intro recursor +premise definition Ix.Theory.Certified.Standard.Spec.type + type: {β : Type u} → Certified.Standard.Spec β → Model.AExpr β + definition: fun {β} x => + match x with + | Certified.Standard.Spec.propext eq eqRefl eqRec iff iffIntro iffRec => Certified.Standard.propextType eq iff + | Certified.Standard.Spec.choice nonempty intro recursor => Certified.Standard.choiceType nonempty +premise definition Ix.Theory.Certified.Standard.Spec.value + type: {β : Type u} → + {V : Type v} → [Model.SetTheory V] → Certified.Standard.Spec β → Model.Assignment β V → List Nat → V + definition: fun {β} {V} [Model.SetTheory V] spec constants levels => + match spec with + | Certified.Standard.Spec.propext eq eqRefl eqRec iff iffIntro iffRec => Model.SetTheory.pt + | Certified.Standard.Spec.choice nonempty intro recursor => + Certified.Standard.choiceValue constants nonempty (levels.getD 0 0) +premise definition Ix.Theory.Certified.Standard.chooseSet + type: {V : Type v} → [Model.SetTheory V] → V → V + definition: fun {V} [Model.SetTheory V] A => + if h : ∃ a, Model.SetTheory.Mem a A then Classical.choose h else Model.SetTheory.empty +premise constructor Ix.Theory.Certified.Standard.Witness.mk + type: {β : Type u} → + ConstRef β → Certified.Standard.Spec β → VLevel → Certified.TypingWitness β → Certified.Standard.Witness β +premise constructor Ix.Theory.Certified.Standard.Checked.mk + type: ∀ {β : Type u} {entries : Model.Environment β} {store : Store β} {witness : Certified.Standard.Witness β}, + store.lookup witness.ref = some witness.spec.source → + entries witness.ref = none → + Certified.Standard.Spec.Prerequisites entries witness.spec → + Model.AExpr.Scope witness.spec.universes 0 witness.spec.type → + Model.AExpr.ReferencesIn entries witness.spec.type → + Model.TypingClaim entries [] witness.spec.type (Model.AExpr.sort witness.level) → + Certified.Standard.Checked entries store witness +premise definition Ix.Theory.Certified.Standard.EntrySource + type: {β : Type u} → Store β → ConstRef β → Model.ConstantEntry β → Prop + definition: fun {β} store ref entry => ∃ spec, store.lookup ref = some spec.source ∧ entry = spec.entry +premise constructor Ix.Theory.Certified.Signature.Header.mk + type: {β : Type u} → ConstRef β → Nat → Model.AExpr β → Certified.Signature.Header β +premise constructor Ix.Theory.Certified.Signature.Formed.nil + type: ∀ {β : Type u} [inst : DecidableEq β] (entries : Model.Environment β), Certified.Signature.Formed entries [] +premise constructor Ix.Theory.Certified.Signature.Formed.cons + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} {header : Certified.Signature.Header β} + {rest : List (Certified.Signature.Header β)}, + entries header.ref = none → + Model.AExpr.Scope header.universes 0 header.type → + Model.AExpr.ReferencesIn entries header.type → + ∀ {level : VLevel}, + Model.TypingClaim entries [] header.type (Model.AExpr.sort level) → + Certified.Signature.Formed (entries.insert header.ref header.entry) rest → + Certified.Signature.Formed entries (header :: rest) +premise constructor Ix.Theory.Certified.Signature.TypeWitness.mk + type: {β : Type u} → VLevel → Certified.TypingWitness β → Certified.Signature.TypeWitness β +premise constructor Ix.Theory.Certified.Signature.Rule.mk + type: {β : Type u} → Nat → Model.AExpr β → Model.AExpr β → Model.AExpr β → Certified.Signature.Rule β +premise constructor Ix.Theory.Certified.Signature.RuleFormed.mk + type: ∀ {β : Type u} {entries : Model.Environment β} {rule : Certified.Signature.Rule β}, + Model.AExpr.Scope rule.universes 0 rule.type ∧ + Model.AExpr.Scope rule.universes 0 rule.lhs ∧ Model.AExpr.Scope rule.universes 0 rule.rhs → + Model.AExpr.ReferencesIn entries rule.type ∧ + Model.AExpr.ReferencesIn entries rule.lhs ∧ Model.AExpr.ReferencesIn entries rule.rhs → + (∃ level, Model.TypingClaim entries [] rule.type (Model.AExpr.sort level)) → + Model.TypingClaim entries [] rule.lhs rule.type → + Model.TypingClaim entries [] rule.rhs rule.type → Certified.Signature.RuleFormed entries rule +premise constructor Ix.Theory.Certified.Signature.RuleWitness.mk + type: {β : Type u} → + VLevel → + Certified.TypingWitness β → + Certified.TypingWitness β → Certified.TypingWitness β → Certified.Signature.RuleWitness β +premise definition Ix.Theory.Model.SetTheory.quotSet + type: {V : Type u} → [Model.SetTheory V] → Nat → V → V → V + definition: fun {V} [Model.SetTheory V] u A R => + if u = 0 then Model.SetTheory.image (fun x => Model.SetTheory.pt) A + else Model.SetTheory.image (fun a => Model.SetTheory.qclass A R a) A +premise definition Ix.Theory.Model.SetTheory.quotClass + type: {V : Type u} → [Model.SetTheory V] → Nat → V → V → V → V + definition: fun {V} [Model.SetTheory V] u A R a => if u = 0 then Model.SetTheory.pt else Model.SetTheory.qclass A R a +premise definition Ix.Theory.Model.SetTheory.qrep + type: {V : Type u} → [Model.SetTheory V] → Nat → V → V → V → V + definition: fun {V} [Model.SetTheory V] u A R q => + if h : ∃ a, Model.SetTheory.Mem a A ∧ q = Model.SetTheory.quotClass u A R a then Classical.choose h + else Model.SetTheory.empty +premise constructor Ix.Theory.Model.SetTheory.QuotRel.base + type: ∀ {V : Type u} [inst : Model.SetTheory V] {A R a b : V}, + Model.SetTheory.Mem a A → + Model.SetTheory.Mem b A → + (∃ w, Model.SetTheory.Mem w (Model.SetTheory.app (Model.SetTheory.app R a) b)) → Model.SetTheory.QuotRel A R a b +premise constructor Ix.Theory.Model.SetTheory.QuotRel.refl + type: ∀ {V : Type u} [inst : Model.SetTheory V] {A R a : V}, Model.SetTheory.Mem a A → Model.SetTheory.QuotRel A R a a +premise constructor Ix.Theory.Model.SetTheory.QuotRel.symm + type: ∀ {V : Type u} [inst : Model.SetTheory V] {A R a b : V}, + Model.SetTheory.QuotRel A R a b → Model.SetTheory.QuotRel A R b a +premise constructor Ix.Theory.Model.SetTheory.QuotRel.trans + type: ∀ {V : Type u} [inst : Model.SetTheory V] {A R a b c : V}, + Model.SetTheory.QuotRel A R a b → Model.SetTheory.QuotRel A R b c → Model.SetTheory.QuotRel A R a c +premise constructor Ix.Theory.Certified.Quotient.Refs.mk + type: {β : Type u} → Store.QuotRefs β → ConstRef β → ConstRef β → ConstRef β → Certified.Quotient.Refs β +premise definition Ix.Theory.Certified.Quotient.Refs.ExactSource + type: {β : Type u} → Certified.Quotient.Refs β → Store β → Prop + definition: fun {β} refs store => + ∀ (kind : Certified.Quotient.Kind), + kind ∈ Certified.Quotient.kinds → store.lookup (refs.ref kind) = some (refs.source kind) +premise definition Ix.Theory.Certified.Quotient.Refs.environment + type: {β : Type u} → [DecidableEq β] → Certified.Quotient.Refs β → Model.Environment β → Model.Environment β + definition: fun {β} [DecidableEq β] refs entries r => + match refs.kind? r with + | some kind => some (refs.entry kind) + | none => entries r +premise definition Ix.Theory.Certified.Quotient.Refs.assignment + type: {β : Type u} → + [DecidableEq β] → + {V : Type v} → [Model.SetTheory V] → Certified.Quotient.Refs β → Model.Assignment β V → Model.Assignment β V + definition: fun {β} [DecidableEq β] {V} [Model.SetTheory V] refs constants r levels => + match refs.kind? r with + | some kind => refs.value constants kind levels + | none => constants r levels +premise definition Ix.Theory.Certified.Quotient.Refs.entryType + type: {β : Type u} → Certified.Quotient.Refs β → Certified.Quotient.Kind → Model.AExpr β + definition: fun {β} refs x => + match x with + | Certified.Quotient.Kind.type => Certified.Quotient.typeType + | Certified.Quotient.Kind.ctor => Certified.Quotient.ctorType refs + | Certified.Quotient.Kind.lift => Certified.Quotient.liftType refs + | Certified.Quotient.Kind.ind => Certified.Quotient.indType refs + | Certified.Quotient.Kind.sound => Certified.Quotient.soundType refs +premise definition Ix.Theory.Certified.Quotient.Refs.source + type: {β : Type u} → Certified.Quotient.Refs β → Certified.Quotient.Kind → Const β + definition: fun {β} refs x => + match x with + | Certified.Quotient.Kind.type => Const.quot Ix.Theory.QuotKind.type 1 Certified.Quotient.typeType.erase + | Certified.Quotient.Kind.ctor => Const.quot Ix.Theory.QuotKind.ctor 1 (Certified.Quotient.ctorType refs).erase + | Certified.Quotient.Kind.lift => Const.quot Ix.Theory.QuotKind.lift 2 (Certified.Quotient.liftType refs).erase + | Certified.Quotient.Kind.ind => Const.quot Ix.Theory.QuotKind.ind 1 (Certified.Quotient.indType refs).erase + | Certified.Quotient.Kind.sound => Const.axiom 1 (Certified.Quotient.soundType refs).erase Safety.safe +premise definition Ix.Theory.Certified.Quotient.Refs.equations + type: {β : Type u} → Certified.Quotient.Refs β → Certified.Quotient.Kind → List (Model.ConstantEquation β) + definition: fun {β} refs x => + match x with + | Certified.Quotient.Kind.lift => + [{ lhs := Certified.Quotient.liftRuleLhs refs, rhs := Certified.Quotient.liftRuleRhs refs }] + | Certified.Quotient.Kind.ind => + [{ lhs := Certified.Quotient.indRuleLhs refs, rhs := Certified.Quotient.indRuleRhs refs }] + | x => [] +premise constructor Ix.Theory.Certified.Quotient.Reading.mk + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {constants : Model.Assignment β V} + {refs : Certified.Quotient.Refs β}, + (∀ (u : Nat), constants refs.type [u] = Certified.Quotient.formerValue u) → + (∀ (u : Nat), constants refs.ctor [u] = Certified.Quotient.constructorValue u) → + (∀ (u v : Nat), constants refs.lift [u, v] = Certified.Quotient.liftValue constants refs.eq u v) → + (∀ (u : Nat), constants refs.ind [u] = Model.SetTheory.pt) → + (∀ (u : Nat), constants refs.sound [u] = Model.SetTheory.pt) → Certified.Quotient.Reading constants refs +premise constructor Ix.Theory.Certified.Quotient.Witness.mk + type: {β : Type u} → + Certified.Quotient.Refs β → + List (Certified.Signature.TypeWitness β) → + Certified.Signature.RuleWitness β → Certified.Signature.RuleWitness β → Certified.Quotient.Witness β +premise constructor Ix.Theory.Certified.Quotient.Checked.mk + type: ∀ {β : Type u} [inst : DecidableEq β] {entries : Model.Environment β} {store : Store β} + {refs : Certified.Quotient.Refs β}, + refs.ExactSource store → + refs.Fresh entries → + Certified.Basis.Equality.Interface entries refs.eq refs.eqRefl refs.eqRec → + Certified.Signature.Formed entries refs.headers → + Certified.Signature.RuleFormed (refs.typeEnvironment entries) refs.liftRule → + Certified.Signature.RuleFormed (refs.typeEnvironment entries) refs.indRule → + Certified.Quotient.Checked entries store refs +premise definition Ix.Theory.Certified.Quotient.EntrySource + type: {β : Type u} → Store β → ConstRef β → Model.ConstantEntry β → Prop + definition: fun {β} store r entry => ∃ refs kind, refs.ExactSource store ∧ r = refs.ref kind ∧ entry = refs.entry kind +premise definition Ix.Theory.Certified.Quotient.formerValue + type: {V : Type v} → [Model.SetTheory V] → Nat → V + definition: fun {V} [Model.SetTheory V] u => + Model.SetModel.lamR 1 (Model.SetTheory.univ u) fun A => + Model.SetModel.lamR 1 (Certified.Quotient.relationSet A) fun R => Model.SetTheory.quotSet u A R +premise definition Ix.Theory.Certified.Quotient.constructorValue + type: {V : Type v} → [Model.SetTheory V] → Nat → V + definition: fun {V} [Model.SetTheory V] u => + Model.SetModel.lamR (Certified.Quotient.bit u) (Model.SetTheory.univ u) fun A => + Model.SetModel.lamR (Certified.Quotient.bit u) (Certified.Quotient.relationSet A) fun R => + Model.SetModel.lamR (Certified.Quotient.bit u) A fun a => Model.SetTheory.quotClass u A R a +premise definition Ix.Theory.Certified.Quotient.liftValue + type: {β : Type u} → {V : Type v} → [Model.SetTheory V] → Model.Assignment β V → ConstRef β → Nat → Nat → V + definition: fun {β} {V} [Model.SetTheory V] constants eq u v => + Model.SetModel.lamR (Certified.Quotient.bit v) (Model.SetTheory.univ u) fun A => + Model.SetModel.lamR (Certified.Quotient.bit v) (Certified.Quotient.relationSet A) fun R => + Model.SetModel.lamR (Certified.Quotient.bit v) (Model.SetTheory.univ v) fun B => + Model.SetModel.lamR (Certified.Quotient.bit v) (Model.SetModel.piR (Certified.Quotient.bit v) A fun x => B) + fun f => + Model.SetModel.lamR (Certified.Quotient.bit v) (Certified.Quotient.invariantSet constants eq v A R B f) + fun x => + Model.SetModel.lamR (Certified.Quotient.bit v) (Model.SetTheory.quotSet u A R) fun q => + Model.SetTheory.app f (Model.SetTheory.qrep u A R q) +premise constructor Ix.Theory.Certified.PrimitiveSignature.Compatible.mk + type: ∀ {β : Type u} {V : Type v} [inst : Model.SetTheory V] {signature : Certified.PrimitiveSignature β} + {entries : Model.Environment β} {constants : Model.Assignment β V}, + Model.Realizes constants entries → + (∀ (levels : List Nat), constants signature.falseType levels = Model.SetTheory.empty) → + (∀ (levels : List Nat), constants signature.falseElim levels = Model.falseElimValue (levels.getD 0 0)) → + signature.Compatible entries constants +premise definition Ix.Theory.Certified.EntrySource + type: {β : Type u} → Certified.PrimitiveSignature β → Store β → ConstRef β → Model.ConstantEntry β → Prop + definition: fun {β} signature store r entry => + r = signature.falseType ∧ + entry = Certified.PrimitiveSignature.falseEntry ∧ + store.lookup r = some Certified.PrimitiveSignature.falseDeclaration ∨ + r = signature.falseElim ∧ entry = signature.falseElimEntry ∧ store.lookup r = some signature.falseElimDeclaration ∨ + (∃ kind body, + entry.body = some body ∧ + entry.equations = [] ∧ + entry.facts = [] ∧ + store.lookup r = some (Const.defn entry.universes kind entry.type.erase body.erase Safety.safe)) ∨ + Certified.Ordinary.EntrySource store r entry ∨ + Certified.Standard.EntrySource store r entry ∨ + Certified.Quotient.EntrySource store r entry ∨ + Certified.Structure.EntrySource store r entry ∨ + Certified.Natural.EntrySource signature.natType store r entry ∨ + Certified.Modeled.EntrySource store r entry +premise constructor Ix.Theory.Certified.Extends.mk + type: ∀ {β : Type u} {signature : Certified.PrimitiveSignature β} {entries entries' : Model.Environment β}, + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), entries r = some entry → entries' r = some entry) → + (∀ (V : Type v) [inst : Model.SetTheory V] (constants : Model.Assignment β V), + signature.Compatible entries constants → + ∃ constants', + signature.Compatible entries' constants' ∧ Model.Assignment.AgreesOn entries constants constants') → + Certified.Extends signature entries entries' +premise constructor Ix.Theory.Certified.AdmittedEnvironment.mk + type: {β : Type u} → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → + (entries : Model.Environment β) → + entries.WF → + signature.Present entries → + (∀ (r : ConstRef β) (entry : Model.ConstantEntry β), + entries r = some entry → Certified.EntrySource signature store r entry) → + (∀ (V : Type v) [inst : Model.SetTheory V], ∃ constants, signature.Compatible entries constants) → + Certified.AdmittedEnvironment signature store +premise constructor Ix.Theory.Certified.CheckedClaim.mk + type: {claim : Prop} → claim → Certified.CheckedClaim claim +premise constructor Ix.Theory.Certified.InferenceResult.mk + type: {β : Type u} → + {entries : Model.Environment β} → + {n : Nat} → + {Γ : Model.Context β} → + {source : VExpr β} → + (reading : Model.Reading n (List.length Γ) source) → + (type : Model.AExpr β) → + Model.AExpr.Scope n (List.length Γ) type → + Model.TypingClaim entries Γ reading.val type → Certified.InferenceResult entries n Γ source +premise constructor Ix.Theory.Certified.WhnfResult.mk + type: {β : Type u} → + {entries : Model.Environment β} → + {n : Nat} → + {Γ : Model.Context β} → + {source : VExpr β} → + (input : Certified.InferenceResult entries n Γ source) → + (result : Model.AExpr β) → + Model.AExpr.Scope n (List.length Γ) result → + Certified.whnfShape entries result = true → + Model.TypingClaim entries Γ result input.type → + Model.ConversionClaim entries Γ input.reading.val result → Certified.WhnfResult entries n Γ source +premise constructor Ix.Theory.Store.mk + type: {β : Type u} → + (dom : List β) → + dom.Nodup → (blocks : β → Option (Block β)) → (∀ (b : β), (blocks b).isSome = true ↔ b ∈ dom) → Store β +premise constructor Ix.Theory.Certified.ProofInput.mk + type: {β : Type u} → Store β → Nat → VExpr β → VExpr β → Certified.ProofInput β +premise constructor Ix.Theory.Certified.ProofWitness.mk + type: {β : Type u} → + List (Certified.DeclarationWitness β) → + Model.AnnotationTree → + Model.AnnotationTree → Certified.TypingWitness β → Certified.TypingWitness β → Certified.ProofWitness β +premise constructor Ix.Theory.Certified.CheckedProof.mk + type: {β : Type u} → + {signature : Certified.PrimitiveSignature β} → + {input : Certified.ProofInput β} → + (environment : Certified.AdmittedEnvironment signature input.store) → + (proof : Model.Reading input.universes 0 input.proof) → + (proposition : Model.Reading input.universes 0 input.proposition) → + Model.AExpr.ReferencesIn environment.entries proof.val → + Model.AExpr.ReferencesIn environment.entries proposition.val → + Model.TypingClaim environment.entries [] proof.val proposition.val → + Model.TypingClaim environment.entries [] proposition.val (Model.AExpr.sort VLevel.zero) → + Certified.CheckedProof signature input +compiler recursion worker Ix.Theory.Certified.admitDefinitions?._unsafe_rec + type: {β : Type u} → + [DecidableEq β] → + Nat → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → + (state : Certified.AdmittedEnvironment signature store) → + List (Certified.DefinitionWitness β) → + Option { result // Certified.Extends signature state.entries result.entries } + implementation: fun {β} [DecidableEq β] fuel {signature} {store} state x => + match x with + | [] => some ⟨state, ⋯⟩ + | witness :: rest => do + let step ← Certified.admitDefinition? fuel state witness + let rest ← Certified.admitDefinitions?._unsafe_rec fuel step.val rest + pure ⟨rest.val, ⋯⟩ +compiler recursion worker Ix.Theory.Certified.checkDeclarationExtensions?._unsafe_rec + type: {β : Type u} → + [DecidableEq β] → + Nat → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → + (state : Certified.CheckedInterface signature) → + List (Certified.DeclarationWitness β) → Option (Certified.CheckedExtension signature store state) + implementation: fun {β} [DecidableEq β] fuel {signature} {store} state x => + match x with + | [] => some (Certified.CheckedExtension.refl state) + | witness :: rest => do + let step ← Certified.checkDeclarationExtension? fuel state witness + let rest ← Certified.checkDeclarationExtensions?._unsafe_rec fuel step.result rest + pure (step.trans rest) +compiler recursion worker Ix.Theory.Certified.replayDeclarations?._unsafe_rec + type: {β : Type u} → + [DecidableEq β] → + Nat → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → + (state : Certified.CheckedInterface signature) → + List (Certified.DeclarationWitness β) → Option (Certified.CheckedExtension signature store state) + implementation: fun {β} [DecidableEq β] fuel {signature} {store} state x => + match x with + | [] => some (Certified.CheckedExtension.refl state) + | witness :: rest => + have __do_jp := fun step => do + let rest ← Certified.replayDeclarations?._unsafe_rec fuel step.result rest + pure (step.trans rest); + if (witness.outputs.all fun ref => (state.entries ref).isSome) = true then do + let step ← some (Certified.CheckedExtension.refl state) + __do_jp step + else do + let step ← Certified.checkDeclarationExtension? fuel state witness + __do_jp step +compiler recursion worker Ix.Theory.Certified.replayNodes?._unsafe_rec + type: {β : Type u} → + [inst : DecidableEq β] → + {fuel : Nat} → + {signature : Certified.PrimitiveSignature β} → + {store : Store β} → + (state : Certified.CheckedInterface signature) → + (known : List (ConstRef β)) → + (nodes : List (Certified.CheckedNode fuel signature store)) → + Option (Certified.ReplayResult state known nodes) + implementation: fun {β} [DecidableEq β] {fuel} {signature} {store} state known x => + match x with + | [] => some { checked := Certified.CheckedExtension.refl state, order := ⋯, subjects := ⋯, frontiers := ⋯ } + | node :: nodes => + if available : (node.receipt.frontier.refs.all fun x => known.contains x) = true then + if aligned : + (node.receipt.frontier.headers.all fun h => decide (Certified.HeaderPresent state.entries h)) = true then do + let step ← Certified.replayDeclarations? fuel state node.input.declarations + if present : (node.input.subjects.all fun ref => (step.result.entries ref).isSome) = true then do + let tail ← Certified.replayNodes?._unsafe_rec step.result (known ++ node.input.subjects) nodes + pure { checked := step.trans tail.checked, order := ⋯, subjects := ⋯, frontiers := ⋯ } + else none + else none + else none +compiler recursion worker Ix.Theory.Certified.verifyArguments._unsafe_rec + type: {β : Type u} → + [DecidableEq β] → + Nat → + Nat → + (entries : Model.Environment β) → + (Γ : Model.Context β) → + (domains args : List (Model.AExpr β)) → + List (Certified.TypingWitness β) → + Option (Certified.CheckedClaim (Certified.ArgumentsFit entries Γ domains args)) + implementation: fun {β} [DecidableEq β] fuel n entries Γ x x_1 x_2 => + match x, x_1, x_2 with + | [], [], [] => some { down := ⋯ } + | A :: domains, a :: args, witness :: witnesses => do + let ha ← Certified.verifyType fuel n entries Γ a A witness + let hrest ← Certified.verifyArguments._unsafe_rec fuel n entries Γ (Model.Telescope.inst a domains) args witnesses + pure { down := ⋯ } + | x, x_3, x_4 => none +compiler recursion worker Ix.Theory.Certified.verifyConversion._unsafe_rec + type: {β : Type u} → + [DecidableEq β] → + Nat → + Nat → + (entries : Model.Environment β) → + (Γ : Model.Context β) → + (a b : Model.AExpr β) → + Certified.ConversionWitness β → Option (Certified.CheckedClaim (Model.ConversionClaim entries Γ a b)) + implementation: fun {β} [inst : DecidableEq β] fuel n entries Γ a b witness => + match fuel with + | 0 => none + | fuel.succ => + if Model.AExpr.Scope n (List.length Γ) a ∧ Model.AExpr.Scope n (List.length Γ) b then + match a, b, witness with + | a, b, Certified.ConversionWitness.refl => if h : a = b then some { down := ⋯ } else none + | a, b, w.symm => do + let h ← Certified.verifyConversion._unsafe_rec fuel n entries Γ b a w + pure { down := ⋯ } + | a, b, Certified.ConversionWitness.trans c w₁ w₂ => do + let h₁ ← Certified.verifyConversion._unsafe_rec fuel n entries Γ a c w₁ + let h₂ ← Certified.verifyConversion._unsafe_rec fuel n entries Γ c b w₂ + pure { down := ⋯ } + | f.app a, f'.app a', wf.app wa => do + let hf ← Certified.verifyConversion._unsafe_rec fuel n entries Γ f f' wf + let ha ← Certified.verifyConversion._unsafe_rec fuel n entries Γ a a' wa + pure { down := ⋯ } + | Model.AExpr.proj r i a, Model.AExpr.proj r' i' b, w.proj => + if hr : r = r' then + if hi : i = i' then do + let h ← Certified.verifyConversion._unsafe_rec fuel n entries Γ a b w + pure { down := ⋯ } + else none + else none + | Model.AExpr.lam p D body, Model.AExpr.lam p' D' body', Certified.ConversionWitness.lam lD wD wd wb => + if hp : p = p' then do + let hD ← Certified.verifyType._unsafe_rec fuel n entries Γ D (Model.AExpr.sort lD) wD + let hd ← Certified.verifyConversion._unsafe_rec fuel n entries Γ D D' wd + let hb ← Certified.verifyConversion._unsafe_rec fuel n entries (Model.Context.push D Γ) body body' wb + pure { down := ⋯ } + else none + | Model.AExpr.forallE p D B, Model.AExpr.forallE p' D' B', Certified.ConversionWitness.forallE lD wD wd wb => + if hp : p = p' then do + let hD ← Certified.verifyType._unsafe_rec fuel n entries Γ D (Model.AExpr.sort lD) wD + let hd ← Certified.verifyConversion._unsafe_rec fuel n entries Γ D D' wd + let hb ← Certified.verifyConversion._unsafe_rec fuel n entries (Model.Context.push D Γ) B B' wb + pure { down := ⋯ } + else none + | (Model.AExpr.lam p D body).app arg, result, Certified.ConversionWitness.beta T wl wa => + if hr : result = body.inst arg then do + let hl ← Certified.verifyType._unsafe_rec fuel n entries Γ (Model.AExpr.lam p D body) T wl + let ha ← Certified.verifyType._unsafe_rec fuel n entries Γ arg D wa + pure { down := ⋯ } + else none + | Model.AExpr.lam p D body, f, Certified.ConversionWitness.eta B wf => + if hb : body = (Model.AExpr.liftN 1 f).app (Model.AExpr.bvar 0) then do + let hf ← Certified.verifyType._unsafe_rec fuel n entries Γ f (Model.AExpr.forallE p D B) wf + pure { down := ⋯ } + else none + | a, b, Certified.ConversionWitness.proofIrrel A wA wa wb => do + let hA ← Certified.verifyType._unsafe_rec fuel n entries Γ A (Model.AExpr.sort VLevel.zero) wA + let ha ← Certified.verifyType._unsafe_rec fuel n entries Γ a A wa + let hb ← Certified.verifyType._unsafe_rec fuel n entries Γ b A wb + pure { down := ⋯ } + | Model.AExpr.const r ls, b, Certified.ConversionWitness.delta => + match h : entries r with + | none => none + | some entry => + match h_1 : entry.body with + | none => none + | some body => + if hn : ls.length = entry.universes then + if hB : b = Model.AExpr.instL ls body then some { down := ⋯ } else none + else none + | Model.AExpr.sort l, Model.AExpr.sort l', Certified.ConversionWitness.sort => + if h : Certified.LevelEq.check n l l' = true then some { down := ⋯ } else none + | a, b, Certified.ConversionWitness.equation r index ls => + match h : entries r with + | none => none + | some entry => + match he : entry.equations[index]? with + | none => none + | some law => + if hn : ls.length = entry.universes then + if hls : ∀ (l : VLevel), l ∈ ls → VLevel.WF n l then + if ha : a = Model.AExpr.instL ls law.lhs then + if hb : b = Model.AExpr.instL ls law.rhs then some { down := ⋯ } else none + else none + else none + else none + | Model.AExpr.natLit value, result, Certified.ConversionWitness.natLiteral r index => + match h : entries r with + | none => none + | some entry => + match hf : entry.facts[index]? with + | some (Model.ConstantFact.natural zero succ) => + if hn : entry.universes = 0 then + match value with + | 0 => if he : result = Model.AExpr.const zero [] then some { down := ⋯ } else none + | value.succ => + if he : result = (Model.AExpr.const succ []).app (Model.AExpr.natLit value) then some { down := ⋯ } + else none + else none + | x => none + | x, x_1, x_2 => none + else none +compiler recursion worker Ix.Theory.Certified.verifyPropTelescope._unsafe_rec + type: {β : Type u} → + [DecidableEq β] → + Nat → + Nat → + (entries : Model.Environment β) → + (atLevel : VLevel) → + (Γ : Model.Context β) → + (domains : List (Model.AExpr β)) → + List (Certified.DomainWitness β) → + Option (Certified.CheckedClaim (Certified.TelescopeProp entries Γ domains atLevel)) + implementation: fun {β} [DecidableEq β] fuel n entries atLevel x x_1 x_2 => + match x, x_1, x_2 with + | Γ, [], [] => some { down := ⋯ } + | Γ, A :: rest, witness :: witnesses => + if hl : Certified.checkZeroImplies atLevel witness.level = true then do + let hA ← Certified.verifyType fuel n entries Γ A (Model.AExpr.sort witness.level) witness.typing + let htail ← + Certified.verifyPropTelescope._unsafe_rec fuel n entries atLevel (Model.Context.push A Γ) rest witnesses + pure { down := ⋯ } + else none + | x, x_3, x_4 => none +compiler recursion worker Ix.Theory.Certified.verifyTelescope._unsafe_rec + type: {β : Type u} → + [DecidableEq β] → + Nat → + Nat → + (entries : Model.Environment β) → + (bound : Option VLevel) → + (Γ : Model.Context β) → + (domains : List (Model.AExpr β)) → + List (Certified.DomainWitness β) → + Option + (Certified.CheckedClaim + (Model.Telescope.Formed entries Γ domains ∧ Certified.TelescopeBound entries Γ domains bound)) + implementation: fun {β} [DecidableEq β] fuel n entries bound x x_1 x_2 => + match x, x_1, x_2 with + | Γ, [], [] => some { down := ⋯ } + | Γ, A :: rest, witness :: witnesses => + if hl : Certified.checkDomainBound n bound witness.level = true then do + let hA ← Certified.verifyType fuel n entries Γ A (Model.AExpr.sort witness.level) witness.typing + let hrest ← Certified.verifyTelescope._unsafe_rec fuel n entries bound (Model.Context.push A Γ) rest witnesses + pure { down := ⋯ } + else none + | x, x_3, x_4 => none +compiler recursion worker Ix.Theory.Certified.verifyType._unsafe_rec + type: {β : Type u} → + [DecidableEq β] → + Nat → + Nat → + (entries : Model.Environment β) → + (Γ : Model.Context β) → + (e A : Model.AExpr β) → + Certified.TypingWitness β → Option (Certified.CheckedClaim (Model.TypingClaim entries Γ e A)) + implementation: fun {β} [inst : DecidableEq β] fuel n entries Γ e A witness => + match fuel with + | 0 => none + | fuel.succ => + if Model.AExpr.Scope n (List.length Γ) e ∧ Model.AExpr.Scope n (List.length Γ) A then + match e, witness with + | Model.AExpr.sort l, Certified.TypingWitness.sort => + if h : A = Model.AExpr.sort l.succ then some { down := ⋯ } else none + | Model.AExpr.bvar i, Certified.TypingWitness.bvar => + match h : Γ[i]? with + | none => none + | some B => if hA : A = B then some { down := ⋯ } else none + | Model.AExpr.const r ls, Certified.TypingWitness.const => + match h : entries r with + | none => none + | some entry => + if hn : ls.length = entry.universes then + if hA : A = Model.AExpr.instL ls entry.type then some { down := ⋯ } else none + else none + | f.app a, Certified.TypingWitness.app p D B wf wa => + if hA : A = B.inst a then do + let hf ← Certified.verifyType._unsafe_rec fuel n entries Γ f (Model.AExpr.forallE p D B) wf + let ha ← Certified.verifyType._unsafe_rec fuel n entries Γ a D wa + pure { down := ⋯ } + else none + | Model.AExpr.lam p D body, Certified.TypingWitness.lam lD lB B wD wB wb => + if hp : p = Certified.zeroCondition lB then + if hA : A = Model.AExpr.forallE p D B then do + let hD ← Certified.verifyType._unsafe_rec fuel n entries Γ D (Model.AExpr.sort lD) wD + let hB ← Certified.verifyType._unsafe_rec fuel n entries (Model.Context.push D Γ) B (Model.AExpr.sort lB) wB + let hb ← Certified.verifyType._unsafe_rec fuel n entries (Model.Context.push D Γ) body B wb + pure { down := ⋯ } + else none + else none + | Model.AExpr.forallE p D B, Certified.TypingWitness.forallE lD lB wD wB => + if hp : p = Certified.zeroCondition lB then + if hA : A = Model.AExpr.sort (lD.imax lB) then do + let hD ← Certified.verifyType._unsafe_rec fuel n entries Γ D (Model.AExpr.sort lD) wD + let hB ← Certified.verifyType._unsafe_rec fuel n entries (Model.Context.push D Γ) B (Model.AExpr.sort lB) wB + pure { down := ⋯ } + else none + else none + | e, Certified.TypingWitness.conv B lA we wA wc => do + let he ← Certified.verifyType._unsafe_rec fuel n entries Γ e B we + let hA ← Certified.verifyType._unsafe_rec fuel n entries Γ A (Model.AExpr.sort lA) wA + let hc ← Certified.verifyConversion._unsafe_rec fuel n entries Γ B A wc + pure { down := ⋯ } + | e, Certified.TypingWitness.fact r index ls => + match h : entries r with + | none => none + | some entry => + match hf : entry.facts[index]? with + | some (Model.ConstantFact.typed value type) => + if hn : ls.length = entry.universes then + if ∀ (l : VLevel), l ∈ ls → VLevel.WF n l then + if he : e = Model.AExpr.instL ls value then + if hA : A = Model.AExpr.instL ls type then some { down := ⋯ } else none + else none + else none + else none + | x => none + | Model.AExpr.natLit value, Certified.TypingWitness.natLit r index => + match h : entries r with + | none => none + | some entry => + match hf : entry.facts[index]? with + | some (Model.ConstantFact.natural zero succ) => + if hn : entry.universes = 0 then if hA : A = Model.AExpr.const r [] then some { down := ⋯ } else none + else none + | x => none + | e, Certified.TypingWitness.betaResult p D body arg B wl wa => + if he : e = body.inst arg then + if hA : A = B.inst arg then do + let hl ← + Certified.verifyType._unsafe_rec fuel n entries Γ (Model.AExpr.lam p D body) (Model.AExpr.forallE p D B) + wl + let ha ← Certified.verifyType._unsafe_rec fuel n entries Γ arg D wa + pure { down := ⋯ } + else none + else none + | x, x_1 => none + else none +compiler recursion worker Ix.Theory.Certified.whnfShape._unsafe_rec + type: {β : Type u} → Model.Environment β → Model.AExpr β → Bool + implementation: fun {β} entries x => + match x with + | Model.AExpr.bvar index => true + | Model.AExpr.sort level => true + | Model.AExpr.lam condition domain body => true + | Model.AExpr.forallE condition domain body => true + | Model.AExpr.const r levels => Option.any (fun entry => entry.body.isNone) (entries r) + | (Model.AExpr.lam condition domain body).app arg => false + | f.app arg => Certified.whnfShape._unsafe_rec entries f + | Model.AExpr.natLit value => false + | Model.AExpr.proj ref field major => false +compiler recursion worker Ix.Theory.Certified.zeroCondition._unsafe_rec + type: VLevel → Certified.PropWhen + implementation: fun x => + match x with + | VLevel.zero => Certified.PropWhen.always + | a.succ => Certified.PropWhen.never + | a.max b => (Certified.zeroCondition._unsafe_rec a).inter (Certified.zeroCondition._unsafe_rec b) + | a.imax b => Certified.zeroCondition._unsafe_rec b + | VLevel.param i => Certified.PropWhen.param i +compiler recursion worker Ix.Theory.Lift.comp._unsafe_rec + type: Lift → Lift → Lift + implementation: fun l₁ l₂ => + match l₂, l₁ with + | Lift.refl, l₁ => l₁ + | l₂.skip, l₁ => (Lift.comp._unsafe_rec l₁ l₂).skip + | l₂.cons, Lift.refl => l₂.cons + | l₂.cons, l₁.skip => (Lift.comp._unsafe_rec l₁ l₂).skip + | l₂.cons, l₁.cons => (Lift.comp._unsafe_rec l₁ l₂).cons +compiler recursion worker Ix.Theory.Lift.consN._unsafe_rec + type: Lift → Nat → Lift + implementation: fun l x => + match x with + | 0 => l + | n.succ => (Lift.consN._unsafe_rec l n).cons +compiler recursion worker Ix.Theory.Lift.depth._unsafe_rec + type: Lift → Nat + implementation: fun x => + match x with + | Lift.refl => 0 + | l.skip => Lift.depth._unsafe_rec l + 1 + | l.cons => Lift.depth._unsafe_rec l +compiler recursion worker Ix.Theory.Lift.diff._unsafe_rec + type: Lift → Lift → Lift + implementation: fun x x_1 => + match x, x_1 with + | Lift.refl, x => Lift.refl + | l, Lift.refl => l + | l₁.skip, l₂.skip => Lift.diff._unsafe_rec l₁ l₂ + | l₁.cons, l₂.skip => Lift.diff._unsafe_rec l₁ l₂ + | l₁.skip, l₂.cons => (Lift.diff._unsafe_rec l₁ l₂).skip + | l₁.cons, l₂.cons => (Lift.diff._unsafe_rec l₁ l₂).cons +compiler recursion worker Ix.Theory.Lift.dom._unsafe_rec + type: Lift → Nat + implementation: fun x => + match x with + | Lift.refl => 0 + | l.skip => Lift.dom._unsafe_rec l + | l.cons => Lift.dom._unsafe_rec l + 1 +compiler recursion worker Ix.Theory.Lift.liftVar._unsafe_rec + type: Lift → Nat → Nat + implementation: fun x x_1 => + match x, x_1 with + | Lift.refl, n => n + | l.skip, n => Lift.liftVar._unsafe_rec l n + 1 + | tail.cons, 0 => 0 + | l.cons, n.succ => Lift.liftVar._unsafe_rec l n + 1 +compiler recursion worker Ix.Theory.Lift.size._unsafe_rec + type: Lift → Nat + implementation: fun x => + match x with + | Lift.refl => 0 + | l.skip => Lift.size._unsafe_rec l + 1 + | l.cons => Lift.size._unsafe_rec l + 1 +compiler recursion worker Ix.Theory.Lift.skipN._unsafe_rec + type: Lift → Nat → Lift + implementation: fun l x => + match x with + | 0 => l + | n.succ => (Lift.skipN._unsafe_rec l n).skip +compiler recursion worker Ix.Theory.Model.WellDenoted._unsafe_rec + type: {β : Type u} → + {V : Type v} → [Model.SetTheory V] → Model.Assignment β V → List Nat → (Nat → V) → Model.AExpr β → Prop + implementation: fun {β} {V} [Model.SetTheory V] constants levels env x => + match x with + | Model.AExpr.bvar index => True + | Model.AExpr.sort level => True + | Model.AExpr.const ref levels => True + | f.app a => + Model.WellDenoted._unsafe_rec constants levels env f ∧ + Model.WellDenoted._unsafe_rec constants levels env a ∧ + ∃ v A B, + Model.SetTheory.Mem (Model.interp constants levels env f) (Model.SetModel.piR v A B) ∧ + Model.SetTheory.Mem (Model.interp constants levels env a) A ∧ + ∀ (x : V), Model.SetTheory.Mem x A → Model.SetTheory.Mem (B x) (Model.SetTheory.univ v) + | Model.AExpr.lam p A b => + Model.WellDenoted._unsafe_rec constants levels env A ∧ + (∀ (x : V), + Model.SetTheory.Mem x (Model.interp constants levels env A) → + Model.WellDenoted._unsafe_rec constants levels (Model.Valuation.cons x env) b) ∧ + ∃ v B, + (Model.regime p levels = 0 ↔ v = 0) ∧ + ∀ (x : V), + Model.SetTheory.Mem x (Model.interp constants levels env A) → + Model.SetTheory.Mem (Model.interp constants levels (Model.Valuation.cons x env) b) (B x) ∧ + Model.SetTheory.Mem (B x) (Model.SetTheory.univ v) + | Model.AExpr.forallE p A B => + Model.WellDenoted._unsafe_rec constants levels env A ∧ + (∀ (x : V), + Model.SetTheory.Mem x (Model.interp constants levels env A) → + Model.WellDenoted._unsafe_rec constants levels (Model.Valuation.cons x env) B) ∧ + ∃ v, + (Model.regime p levels = 0 ↔ v = 0) ∧ + ∀ (x : V), + Model.SetTheory.Mem x (Model.interp constants levels env A) → + Model.SetTheory.Mem (Model.interp constants levels (Model.Valuation.cons x env) B) + (Model.SetTheory.univ v) + | Model.AExpr.proj ref field e => Model.WellDenoted._unsafe_rec constants levels env e + | Model.AExpr.natLit value => True +compiler recursion worker Ix.Theory.Model.readAnnotations?._unsafe_rec + type: {β : Type u} → (n k : Nat) → (source : VExpr β) → Model.AnnotationTree → Option (Model.Reading n k source) + implementation: fun {β} n k source tree => + match source, tree with + | VExpr.bvar i, Model.AnnotationTree.leaf => if h : i < k then some ⟨Model.AExpr.bvar i, ⋯⟩ else none + | VExpr.sort l, Model.AnnotationTree.leaf => if h : VLevel.WF n l then some ⟨Model.AExpr.sort l, ⋯⟩ else none + | VExpr.const r ls, Model.AnnotationTree.leaf => + if h : ∀ (l : VLevel), l ∈ ls → VLevel.WF n l then some ⟨Model.AExpr.const r ls, ⋯⟩ else none + | f.app a, tf.app ta => do + let f' ← Model.readAnnotations?._unsafe_rec n k f tf + let a' ← Model.readAnnotations?._unsafe_rec n k a ta + pure ⟨f'.val.app a'.val, ⋯⟩ + | a.lam b, Model.AnnotationTree.lam raw ta tb => + match Ix.Theory.Model.readCondition?✝ n raw with + | none => none + | some p => do + let a' ← Model.readAnnotations?._unsafe_rec n k a ta + let b' ← Model.readAnnotations?._unsafe_rec n (k + 1) b tb + pure ⟨Model.AExpr.lam p.val a'.val b'.val, ⋯⟩ + | a.forallE b, Model.AnnotationTree.forallE raw ta tb => + match Ix.Theory.Model.readCondition?✝ n raw with + | none => none + | some p => do + let a' ← Model.readAnnotations?._unsafe_rec n k a ta + let b' ← Model.readAnnotations?._unsafe_rec n (k + 1) b tb + pure ⟨Model.AExpr.forallE p.val a'.val b'.val, ⋯⟩ + | VExpr.proj r i e, te.proj => do + let e' ← Model.readAnnotations?._unsafe_rec n k e te + pure ⟨Model.AExpr.proj r i e'.val, ⋯⟩ + | VExpr.natLit v, Model.AnnotationTree.leaf => some ⟨Model.AExpr.natLit v, ⋯⟩ + | x, x_1 => none +compiler recursion worker Ix.Theory.VExpr.ClosedN._unsafe_rec + type: {β : Type u_1} → VExpr β → optParam Nat 0 → Prop + implementation: fun {β} x x_1 => + match x, x_1 with + | VExpr.bvar i, k => i < k + | VExpr.sort u, x => True + | VExpr.const ref levels, x => True + | VExpr.natLit value, x => True + | fn.app arg, k => VExpr.ClosedN._unsafe_rec fn k ∧ VExpr.ClosedN._unsafe_rec arg k + | ty.lam body, k => VExpr.ClosedN._unsafe_rec ty k ∧ VExpr.ClosedN._unsafe_rec body (k + 1) + | ty.forallE body, k => VExpr.ClosedN._unsafe_rec ty k ∧ VExpr.ClosedN._unsafe_rec body (k + 1) + | VExpr.proj ref index e, k => VExpr.ClosedN._unsafe_rec e k +compiler recursion worker Ix.Theory.VExpr.LevelWF._unsafe_rec + type: {β : Type u_1} → Nat → VExpr β → Prop + implementation: fun {β} U x => + match x with + | VExpr.bvar deBruijnIndex => True + | VExpr.natLit value => True + | VExpr.sort l => VLevel.WF U l + | VExpr.const ref levels => ∀ (l : VLevel), l ∈ levels → VLevel.WF U l + | e₁.app e₂ => VExpr.LevelWF._unsafe_rec U e₁ ∧ VExpr.LevelWF._unsafe_rec U e₂ + | e₁.lam e₂ => VExpr.LevelWF._unsafe_rec U e₁ ∧ VExpr.LevelWF._unsafe_rec U e₂ + | e₁.forallE e₂ => VExpr.LevelWF._unsafe_rec U e₁ ∧ VExpr.LevelWF._unsafe_rec U e₂ + | VExpr.proj ref index e => VExpr.LevelWF._unsafe_rec U e +compiler recursion worker Ix.Theory.VExpr.Skips'._unsafe_rec + type: {β : Type u_1} → Nat → VExpr β → optParam Nat 0 → Prop + implementation: fun {β} n x x_1 => + match x, x_1 with + | VExpr.bvar i, k => i < k + n → i < k + | VExpr.sort u, x => True + | VExpr.const ref levels, x => True + | VExpr.natLit value, x => True + | fn.app arg, k => VExpr.Skips'._unsafe_rec n fn k ∧ VExpr.Skips'._unsafe_rec n arg k + | ty.lam body, k => VExpr.Skips'._unsafe_rec n ty k ∧ VExpr.Skips'._unsafe_rec n body (k + 1) + | ty.forallE body, k => VExpr.Skips'._unsafe_rec n ty k ∧ VExpr.Skips'._unsafe_rec n body (k + 1) + | VExpr.proj ref index e, k => VExpr.Skips'._unsafe_rec n e k +compiler recursion worker Ix.Theory.VExpr.appArgs._unsafe_rec + type: {β : Type u_1} → VExpr β → List (VExpr β) → List (VExpr β) + implementation: fun {β} x x_1 => + match x, x_1 with + | f.app a, acc => VExpr.appArgs._unsafe_rec f (a :: acc) + | x, acc => acc +compiler recursion worker Ix.Theory.VExpr.appHead._unsafe_rec + type: {β : Type u_1} → VExpr β → VExpr β + implementation: fun {β} x => + match x with + | f.app arg => VExpr.appHead._unsafe_rec f + | e => e +compiler recursion worker Ix.Theory.VExpr.appN._unsafe_rec + type: {β : Type u_1} → VExpr β → List (VExpr β) → VExpr β + implementation: fun {β} f x => + match x with + | [] => f + | a :: as => VExpr.appN._unsafe_rec (f.app a) as +compiler recursion worker Ix.Theory.VExpr.bvarRevRange._unsafe_rec + type: {β : Type u_1} → Nat → Nat → List (VExpr β) + implementation: fun {β} off x => + match x with + | 0 => [] + | m.succ => VExpr.bvar (off + m) :: VExpr.bvarRevRange._unsafe_rec off m +compiler recursion worker Ix.Theory.VExpr.dropN._unsafe_rec + type: {β : Type u_1} → Nat → VExpr β → VExpr β + implementation: fun {β} x x_1 => + match x, x_1 with + | 0, e => e + | n.succ, binderType.forallE rest => VExpr.dropN._unsafe_rec n rest + | n.succ, e => e +compiler recursion worker Ix.Theory.VExpr.forallN._unsafe_rec + type: {β : Type u_1} → List (VExpr β) → VExpr β → VExpr β + implementation: fun {β} x x_1 => + match x, x_1 with + | [], e => e + | A :: As, e => A.forallE (VExpr.forallN._unsafe_rec As e) +compiler recursion worker Ix.Theory.VExpr.inst._unsafe_rec + type: {β : Type u_1} → VExpr β → VExpr β → optParam Nat 0 → VExpr β + implementation: fun {β} x x_1 x_2 => + match x, x_1, x_2 with + | VExpr.bvar i, e, k => VExpr.instVar i e k + | VExpr.sort u, x, x_3 => VExpr.sort u + | VExpr.const r us, x, x_3 => VExpr.const r us + | fn.app arg, e, k => (VExpr.inst._unsafe_rec fn e k).app (VExpr.inst._unsafe_rec arg e k) + | ty.lam body, e, k => (VExpr.inst._unsafe_rec ty e k).lam (VExpr.inst._unsafe_rec body e (k + 1)) + | ty.forallE body, e, k => (VExpr.inst._unsafe_rec ty e k).forallE (VExpr.inst._unsafe_rec body e (k + 1)) + | VExpr.proj r i p, e, k => VExpr.proj r i (VExpr.inst._unsafe_rec p e k) + | VExpr.natLit value, x, x_3 => VExpr.natLit value +compiler recursion worker Ix.Theory.VExpr.instL._unsafe_rec + type: List VLevel → {β : Type u_1} → VExpr β → VExpr β + implementation: fun levels {β} x => + match x with + | VExpr.bvar i => VExpr.bvar i + | VExpr.sort u => VExpr.sort (VLevel.inst levels u) + | VExpr.const r us => VExpr.const r (List.map (VLevel.inst levels) us) + | fn.app arg => (VExpr.instL._unsafe_rec levels fn).app (VExpr.instL._unsafe_rec levels arg) + | ty.lam body => (VExpr.instL._unsafe_rec levels ty).lam (VExpr.instL._unsafe_rec levels body) + | ty.forallE body => (VExpr.instL._unsafe_rec levels ty).forallE (VExpr.instL._unsafe_rec levels body) + | VExpr.proj r i e => VExpr.proj r i (VExpr.instL._unsafe_rec levels e) + | VExpr.natLit value => VExpr.natLit value +compiler recursion worker Ix.Theory.VExpr.instRev._unsafe_rec + type: {β : Type u_1} → VExpr β → List (VExpr β) → VExpr β + implementation: fun {β} x x_1 => + match x, x_1 with + | C, [] => C + | C, e :: es => VExpr.instRev._unsafe_rec (C.inst e es.length) es +compiler recursion worker Ix.Theory.VExpr.instRevAt._unsafe_rec + type: {β : Type u_1} → VExpr β → List (VExpr β) → Nat → VExpr β + implementation: fun {β} x x_1 x_2 => + match x, x_1, x_2 with + | e, [], x => e + | e, a :: as, k => VExpr.instRevAt._unsafe_rec (e.inst a (k + as.length)) as k +compiler recursion worker Ix.Theory.VExpr.instTelN._unsafe_rec + type: {β : Type u_1} → VExpr β → List (VExpr β) → Nat → List (VExpr β) + implementation: fun {β} a x x_1 => + match x, x_1 with + | [], x => [] + | A :: As, k => A.inst a k :: VExpr.instTelN._unsafe_rec a As (k + 1) +compiler recursion worker Ix.Theory.VExpr.lamN._unsafe_rec + type: {β : Type u_1} → List (VExpr β) → VExpr β → VExpr β + implementation: fun {β} x x_1 => + match x, x_1 with + | [], e => e + | A :: As, e => A.lam (VExpr.lamN._unsafe_rec As e) +compiler recursion worker Ix.Theory.VExpr.lift'._unsafe_rec + type: {β : Type u_1} → VExpr β → Lift → VExpr β + implementation: fun {β} x x_1 => + match x, x_1 with + | VExpr.bvar i, ρ => VExpr.bvar (ρ.liftVar i) + | VExpr.sort u, x => VExpr.sort u + | VExpr.const r us, x => VExpr.const r us + | fn.app arg, ρ => (VExpr.lift'._unsafe_rec fn ρ).app (VExpr.lift'._unsafe_rec arg ρ) + | ty.lam body, ρ => (VExpr.lift'._unsafe_rec ty ρ).lam (VExpr.lift'._unsafe_rec body ρ.cons) + | ty.forallE body, ρ => (VExpr.lift'._unsafe_rec ty ρ).forallE (VExpr.lift'._unsafe_rec body ρ.cons) + | VExpr.proj r i e, ρ => VExpr.proj r i (VExpr.lift'._unsafe_rec e ρ) + | VExpr.natLit value, x => VExpr.natLit value +compiler recursion worker Ix.Theory.VExpr.liftN._unsafe_rec + type: Nat → {β : Type u_1} → VExpr β → optParam Nat 0 → VExpr β + implementation: fun n {β} x x_1 => + match x, x_1 with + | VExpr.bvar i, k => VExpr.bvar (liftVar n i k) + | VExpr.sort u, x => VExpr.sort u + | VExpr.const r us, x => VExpr.const r us + | fn.app arg, k => (VExpr.liftN._unsafe_rec n fn k).app (VExpr.liftN._unsafe_rec n arg k) + | ty.lam body, k => (VExpr.liftN._unsafe_rec n ty k).lam (VExpr.liftN._unsafe_rec n body (k + 1)) + | ty.forallE body, k => (VExpr.liftN._unsafe_rec n ty k).forallE (VExpr.liftN._unsafe_rec n body (k + 1)) + | VExpr.proj r i e, k => VExpr.proj r i (VExpr.liftN._unsafe_rec n e k) + | VExpr.natLit value, x => VExpr.natLit value +compiler recursion worker Ix.Theory.VExpr.liftTelN._unsafe_rec + type: {β : Type u_1} → Nat → List (VExpr β) → Nat → List (VExpr β) + implementation: fun {β} n x x_1 => + match x, x_1 with + | [], x => [] + | A :: As, k => VExpr.liftN n A k :: VExpr.liftTelN._unsafe_rec n As (k + 1) +compiler recursion worker Ix.Theory.VExpr.mapRefs._unsafe_rec + type: {β : Type u} → {γ : Type v} → (ConstRef β → ConstRef γ) → VExpr β → VExpr γ + implementation: fun {β} {γ} mapping x => + match x with + | VExpr.bvar i => VExpr.bvar i + | VExpr.sort level => VExpr.sort level + | VExpr.const ref levels => VExpr.const (mapping ref) levels + | f.app a => (VExpr.mapRefs._unsafe_rec mapping f).app (VExpr.mapRefs._unsafe_rec mapping a) + | type.lam body => (VExpr.mapRefs._unsafe_rec mapping type).lam (VExpr.mapRefs._unsafe_rec mapping body) + | type.forallE body => (VExpr.mapRefs._unsafe_rec mapping type).forallE (VExpr.mapRefs._unsafe_rec mapping body) + | VExpr.proj ref index major => VExpr.proj (mapping ref) index (VExpr.mapRefs._unsafe_rec mapping major) + | VExpr.natLit n => VExpr.natLit n +compiler recursion worker Ix.Theory.VExpr.mentions._unsafe_rec + type: {β : Type u_1} → [DecidableEq β] → VExpr β → ConstRef β → Bool + implementation: fun {β} [DecidableEq β] x x_1 => + match x, x_1 with + | VExpr.bvar deBruijnIndex, x => false + | VExpr.sort u, x => false + | VExpr.natLit value, x => false + | VExpr.const r levels, target => decide (r = target) + | e₁.app e₂, target => VExpr.mentions._unsafe_rec e₁ target || VExpr.mentions._unsafe_rec e₂ target + | e₁.lam e₂, target => VExpr.mentions._unsafe_rec e₁ target || VExpr.mentions._unsafe_rec e₂ target + | e₁.forallE e₂, target => VExpr.mentions._unsafe_rec e₁ target || VExpr.mentions._unsafe_rec e₂ target + | VExpr.proj r index e, target => decide (r = target) || VExpr.mentions._unsafe_rec e target +compiler recursion worker Ix.Theory.VExpr.refs._unsafe_rec + type: {β : Type u_1} → VExpr β → List (ConstRef β) + implementation: fun {β} x => + match x with + | VExpr.bvar deBruijnIndex => [] + | VExpr.sort u => [] + | VExpr.natLit value => [] + | VExpr.const r levels => [r] + | e₁.app e₂ => VExpr.refs._unsafe_rec e₁ ++ VExpr.refs._unsafe_rec e₂ + | e₁.lam e₂ => VExpr.refs._unsafe_rec e₁ ++ VExpr.refs._unsafe_rec e₂ + | e₁.forallE e₂ => VExpr.refs._unsafe_rec e₁ ++ VExpr.refs._unsafe_rec e₂ + | VExpr.proj r index e => r :: VExpr.refs._unsafe_rec e +compiler recursion worker Ix.Theory.VExpr.rename._unsafe_rec + type: {β : Type u_1} → {γ : Type u_2} → (β → γ) → VExpr β → VExpr γ + implementation: fun {β} {γ} mapping x => + match x with + | VExpr.bvar index => VExpr.bvar index + | VExpr.sort level => VExpr.sort level + | VExpr.const ref levels => VExpr.const (ConstRef.rename mapping ref) levels + | function.app argument => (VExpr.rename._unsafe_rec mapping function).app (VExpr.rename._unsafe_rec mapping argument) + | domain.lam body => (VExpr.rename._unsafe_rec mapping domain).lam (VExpr.rename._unsafe_rec mapping body) + | domain.forallE body => (VExpr.rename._unsafe_rec mapping domain).forallE (VExpr.rename._unsafe_rec mapping body) + | VExpr.proj ref field major => + VExpr.proj (ConstRef.rename mapping ref) field (VExpr.rename._unsafe_rec mapping major) + | VExpr.natLit value => VExpr.natLit value +compiler recursion worker Ix.Theory.VExpr.resultOf._unsafe_rec + type: {β : Type u_1} → VExpr β → VExpr β + implementation: fun {β} x => + match x with + | binderType.forallE rest => VExpr.resultOf._unsafe_rec rest + | e => e +compiler recursion worker Ix.Theory.VExpr.substConst._unsafe_rec + type: {β : Type u_1} → (ConstRef β → Option (VExpr β)) → VExpr β → VExpr β + implementation: fun {β} interp x => + match x with + | VExpr.bvar index => VExpr.bvar index + | VExpr.sort level => VExpr.sort level + | VExpr.const ref levels => + match interp ref with + | some value => VExpr.instL levels value + | none => VExpr.const ref levels + | function.app argument => + (VExpr.substConst._unsafe_rec interp function).app (VExpr.substConst._unsafe_rec interp argument) + | domain.lam body => (VExpr.substConst._unsafe_rec interp domain).lam (VExpr.substConst._unsafe_rec interp body) + | domain.forallE body => + (VExpr.substConst._unsafe_rec interp domain).forallE (VExpr.substConst._unsafe_rec interp body) + | VExpr.proj ref field expression => VExpr.proj ref field (VExpr.substConst._unsafe_rec interp expression) + | VExpr.natLit value => VExpr.natLit value +compiler recursion worker Ix.Theory.VExpr.telN._unsafe_rec + type: {β : Type u_1} → Nat → VExpr β → List (VExpr β) + implementation: fun {β} x x_1 => + match x, x_1 with + | 0, x => [] + | n.succ, A.forallE rest => A :: VExpr.telN._unsafe_rec n rest + | n.succ, x => [] +compiler recursion worker Ix.Theory.VExpr.unliftN._unsafe_rec + type: {β : Type u_1} → VExpr β → Nat → Nat → VExpr β + implementation: fun {β} e n k => + match n with + | 0 => e + | n.succ => VExpr.unliftN._unsafe_rec (e.inst default k) n k +compiler recursion worker Ix.Theory.VLevel.WF._unsafe_rec + type: Nat → VLevel → Prop + implementation: fun n x => + match x with + | VLevel.zero => True + | l.succ => VLevel.WF._unsafe_rec n l + | l₁.max l₂ => VLevel.WF._unsafe_rec n l₁ ∧ VLevel.WF._unsafe_rec n l₂ + | l₁.imax l₂ => VLevel.WF._unsafe_rec n l₁ ∧ VLevel.WF._unsafe_rec n l₂ + | VLevel.param i => i < n +compiler recursion worker Ix.Theory.VLevel.decidable_WF._unsafe_rec + type: {n : Nat} → {l : VLevel} → Decidable (VLevel.WF n l) + implementation: fun {n} x => + match x with + | VLevel.zero => instDecidableTrue + | l.succ => VLevel.decidable_WF._unsafe_rec + | a.max a_1 => instDecidableAnd + | a.imax a_1 => instDecidableAnd + | VLevel.param a => a.decLt n +compiler recursion worker Ix.Theory.VLevel.eval._unsafe_rec + type: List Nat → VLevel → Nat + implementation: fun ls x => + match x with + | VLevel.zero => 0 + | l.succ => VLevel.eval._unsafe_rec ls l + 1 + | l₁.max l₂ => (VLevel.eval._unsafe_rec ls l₁).max (VLevel.eval._unsafe_rec ls l₂) + | l₁.imax l₂ => VLevel.natIMax (VLevel.eval._unsafe_rec ls l₁) (VLevel.eval._unsafe_rec ls l₂) + | VLevel.param i => ls.getD i 0 +compiler recursion worker Ix.Theory.VLevel.inst._unsafe_rec + type: List VLevel → VLevel → VLevel + implementation: fun ls x => + match x with + | VLevel.zero => VLevel.zero + | l.succ => (VLevel.inst._unsafe_rec ls l).succ + | l₁.max l₂ => (VLevel.inst._unsafe_rec ls l₁).max (VLevel.inst._unsafe_rec ls l₂) + | l₁.imax l₂ => (VLevel.inst._unsafe_rec ls l₁).imax (VLevel.inst._unsafe_rec ls l₂) + | VLevel.param i => ls.getD i VLevel.zero +compiler recursion worker Ix.Theory.instDecidableEqVExpr.decEq._unsafe_rec + type: {β : Type u_1} → [DecidableEq β] → (x x_1 : VExpr β) → Decidable (x = x_1) + implementation: fun {β} [inst : DecidableEq β] x x_1 => + match x, x_1 with + | VExpr.bvar a, VExpr.bvar b => if h : a = b then h ▸ isTrue ⋯ else isFalse ⋯ + | VExpr.bvar deBruijnIndex, VExpr.sort u => isFalse ⋯ + | VExpr.bvar deBruijnIndex, VExpr.const ref levels => isFalse ⋯ + | VExpr.bvar deBruijnIndex, fn.app arg => isFalse ⋯ + | VExpr.bvar deBruijnIndex, binderType.lam body => isFalse ⋯ + | VExpr.bvar deBruijnIndex, binderType.forallE body => isFalse ⋯ + | VExpr.bvar deBruijnIndex, VExpr.proj ref index expr => isFalse ⋯ + | VExpr.bvar deBruijnIndex, VExpr.natLit value => isFalse ⋯ + | VExpr.sort u, VExpr.bvar deBruijnIndex => isFalse ⋯ + | VExpr.sort a, VExpr.sort b => if h : a = b then h ▸ isTrue ⋯ else isFalse ⋯ + | VExpr.sort u, VExpr.const ref levels => isFalse ⋯ + | VExpr.sort u, fn.app arg => isFalse ⋯ + | VExpr.sort u, binderType.lam body => isFalse ⋯ + | VExpr.sort u, binderType.forallE body => isFalse ⋯ + | VExpr.sort u, VExpr.proj ref index expr => isFalse ⋯ + | VExpr.sort u, VExpr.natLit value => isFalse ⋯ + | VExpr.const ref levels, VExpr.bvar deBruijnIndex => isFalse ⋯ + | VExpr.const ref levels, VExpr.sort u => isFalse ⋯ + | VExpr.const a a_1, VExpr.const b b_1 => + if h : a = b then h ▸ if h : a_1 = b_1 then h ▸ isTrue ⋯ else isFalse ⋯ else isFalse ⋯ + | VExpr.const ref levels, fn.app arg => isFalse ⋯ + | VExpr.const ref levels, binderType.lam body => isFalse ⋯ + | VExpr.const ref levels, binderType.forallE body => isFalse ⋯ + | VExpr.const ref levels, VExpr.proj ref_1 index expr => isFalse ⋯ + | VExpr.const ref levels, VExpr.natLit value => isFalse ⋯ + | fn.app arg, VExpr.bvar deBruijnIndex => isFalse ⋯ + | fn.app arg, VExpr.sort u => isFalse ⋯ + | fn.app arg, VExpr.const ref levels => isFalse ⋯ + | a.app a_1, b.app b_1 => + let inst_1 := instDecidableEqVExpr.decEq._unsafe_rec a b; + if h : a = b then + h ▸ + let inst_2 := instDecidableEqVExpr.decEq._unsafe_rec a a; + let inst_3 := instDecidableEqVExpr.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst := instDecidableEqVExpr.decEq._unsafe_rec a_1 a_1; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + | fn.app arg, binderType.lam body => isFalse ⋯ + | fn.app arg, binderType.forallE body => isFalse ⋯ + | fn.app arg, VExpr.proj ref index expr => isFalse ⋯ + | fn.app arg, VExpr.natLit value => isFalse ⋯ + | binderType.lam body, VExpr.bvar deBruijnIndex => isFalse ⋯ + | binderType.lam body, VExpr.sort u => isFalse ⋯ + | binderType.lam body, VExpr.const ref levels => isFalse ⋯ + | binderType.lam body, fn.app arg => isFalse ⋯ + | a.lam a_1, b.lam b_1 => + let inst_1 := instDecidableEqVExpr.decEq._unsafe_rec a b; + if h : a = b then + h ▸ + let inst_2 := instDecidableEqVExpr.decEq._unsafe_rec a a; + let inst_3 := instDecidableEqVExpr.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst := instDecidableEqVExpr.decEq._unsafe_rec a_1 a_1; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + | binderType.lam body, binderType_1.forallE body_1 => isFalse ⋯ + | binderType.lam body, VExpr.proj ref index expr => isFalse ⋯ + | binderType.lam body, VExpr.natLit value => isFalse ⋯ + | binderType.forallE body, VExpr.bvar deBruijnIndex => isFalse ⋯ + | binderType.forallE body, VExpr.sort u => isFalse ⋯ + | binderType.forallE body, VExpr.const ref levels => isFalse ⋯ + | binderType.forallE body, fn.app arg => isFalse ⋯ + | binderType.forallE body, binderType_1.lam body_1 => isFalse ⋯ + | a.forallE a_1, b.forallE b_1 => + let inst_1 := instDecidableEqVExpr.decEq._unsafe_rec a b; + if h : a = b then + h ▸ + let inst_2 := instDecidableEqVExpr.decEq._unsafe_rec a a; + let inst_3 := instDecidableEqVExpr.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst := instDecidableEqVExpr.decEq._unsafe_rec a_1 a_1; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + | binderType.forallE body, VExpr.proj ref index expr => isFalse ⋯ + | binderType.forallE body, VExpr.natLit value => isFalse ⋯ + | VExpr.proj ref index expr, VExpr.bvar deBruijnIndex => isFalse ⋯ + | VExpr.proj ref index expr, VExpr.sort u => isFalse ⋯ + | VExpr.proj ref index expr, VExpr.const ref_1 levels => isFalse ⋯ + | VExpr.proj ref index expr, fn.app arg => isFalse ⋯ + | VExpr.proj ref index expr, binderType.lam body => isFalse ⋯ + | VExpr.proj ref index expr, binderType.forallE body => isFalse ⋯ + | VExpr.proj a a_1 a_2, VExpr.proj b b_1 b_2 => + if h : a = b then + h ▸ + if h : a_1 = b_1 then + h ▸ + let inst_1 := instDecidableEqVExpr.decEq._unsafe_rec a_2 b_2; + if h : a_2 = b_2 then + h ▸ + let inst := instDecidableEqVExpr.decEq._unsafe_rec a_2 a_2; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + else isFalse ⋯ + | VExpr.proj ref index expr, VExpr.natLit value => isFalse ⋯ + | VExpr.natLit value, VExpr.bvar deBruijnIndex => isFalse ⋯ + | VExpr.natLit value, VExpr.sort u => isFalse ⋯ + | VExpr.natLit value, VExpr.const ref levels => isFalse ⋯ + | VExpr.natLit value, fn.app arg => isFalse ⋯ + | VExpr.natLit value, binderType.lam body => isFalse ⋯ + | VExpr.natLit value, binderType.forallE body => isFalse ⋯ + | VExpr.natLit value, VExpr.proj ref index expr => isFalse ⋯ + | VExpr.natLit a, VExpr.natLit b => if h : a = b then h ▸ isTrue ⋯ else isFalse ⋯ +compiler recursion worker Ix.Theory.instDecidableEqVLevel.decEq._unsafe_rec + type: (x x_1 : VLevel) → Decidable (x = x_1) + implementation: fun x x_1 => + match x, x_1 with + | VLevel.zero, VLevel.zero => isTrue ⋯ + | VLevel.zero, a.succ => isFalse ⋯ + | VLevel.zero, a.max a_1 => isFalse ⋯ + | VLevel.zero, a.imax a_1 => isFalse ⋯ + | VLevel.zero, VLevel.param a => isFalse ⋯ + | a.succ, VLevel.zero => isFalse ⋯ + | a.succ, b.succ => + let inst := instDecidableEqVLevel.decEq._unsafe_rec a b; + if h : a = b then + h ▸ + let inst := instDecidableEqVLevel.decEq._unsafe_rec a a; + isTrue ⋯ + else isFalse ⋯ + | a.succ, a_1.max a_2 => isFalse ⋯ + | a.succ, a_1.imax a_2 => isFalse ⋯ + | a.succ, VLevel.param a_1 => isFalse ⋯ + | a.max a_1, VLevel.zero => isFalse ⋯ + | a.max a_1, a_2.succ => isFalse ⋯ + | a.max a_1, b.max b_1 => + let inst := instDecidableEqVLevel.decEq._unsafe_rec a b; + if h : a = b then + h ▸ + let inst := instDecidableEqVLevel.decEq._unsafe_rec a a; + let inst := instDecidableEqVLevel.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst := instDecidableEqVLevel.decEq._unsafe_rec a_1 a_1; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + | a.max a_1, a_2.imax a_3 => isFalse ⋯ + | a.max a_1, VLevel.param a_2 => isFalse ⋯ + | a.imax a_1, VLevel.zero => isFalse ⋯ + | a.imax a_1, a_2.succ => isFalse ⋯ + | a.imax a_1, a_2.max a_3 => isFalse ⋯ + | a.imax a_1, b.imax b_1 => + let inst := instDecidableEqVLevel.decEq._unsafe_rec a b; + if h : a = b then + h ▸ + let inst := instDecidableEqVLevel.decEq._unsafe_rec a a; + let inst := instDecidableEqVLevel.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst := instDecidableEqVLevel.decEq._unsafe_rec a_1 a_1; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + | a.imax a_1, VLevel.param a_2 => isFalse ⋯ + | VLevel.param a, VLevel.zero => isFalse ⋯ + | VLevel.param a, a_1.succ => isFalse ⋯ + | VLevel.param a, a_1.max a_2 => isFalse ⋯ + | VLevel.param a, a_1.imax a_2 => isFalse ⋯ + | VLevel.param a, VLevel.param b => if h : a = b then h ▸ isTrue ⋯ else isFalse ⋯ +compiler recursion worker Ix.Theory.instHashableVExpr.hash._unsafe_rec + type: {β : Type u_1} → [Hashable β] → VExpr β → UInt64 + implementation: fun {β} [Hashable β] x => + match x with + | VExpr.bvar a => mixHash 0 (hash a) + | VExpr.sort a => mixHash 1 (hash a) + | VExpr.const a a_1 => mixHash (mixHash 2 (hash a)) (hash a_1) + | a.app a_1 => mixHash (mixHash 3 (instHashableVExpr.hash._unsafe_rec a)) (instHashableVExpr.hash._unsafe_rec a_1) + | a.lam a_1 => mixHash (mixHash 4 (instHashableVExpr.hash._unsafe_rec a)) (instHashableVExpr.hash._unsafe_rec a_1) + | a.forallE a_1 => mixHash (mixHash 5 (instHashableVExpr.hash._unsafe_rec a)) (instHashableVExpr.hash._unsafe_rec a_1) + | VExpr.proj a a_1 a_2 => mixHash (mixHash (mixHash 6 (hash a)) (hash a_1)) (instHashableVExpr.hash._unsafe_rec a_2) + | VExpr.natLit a => mixHash 7 (hash a) +compiler recursion worker Ix.Theory.instHashableVLevel.hash._unsafe_rec + type: VLevel → UInt64 + implementation: fun x => + match x with + | VLevel.zero => 0 + | a.succ => mixHash 1 (instHashableVLevel.hash._unsafe_rec a) + | a.max a_1 => mixHash (mixHash 2 (instHashableVLevel.hash._unsafe_rec a)) (instHashableVLevel.hash._unsafe_rec a_1) + | a.imax a_1 => mixHash (mixHash 3 (instHashableVLevel.hash._unsafe_rec a)) (instHashableVLevel.hash._unsafe_rec a_1) + | VLevel.param a => mixHash 4 (hash a) +compiler recursion worker Ix.Theory.Certified.IndexSet.insert._unsafe_rec + type: Nat → List Nat → List Nat + implementation: fun x x_1 => + match x_1 with + | [] => [x] + | y :: ys => + if x < y then x :: y :: ys else if x = y then y :: ys else y :: Certified.IndexSet.insert._unsafe_rec x ys +compiler recursion worker Ix.Theory.Certified.IndexSet.normalize._unsafe_rec + type: List Nat → List Nat + implementation: fun x => + match x with + | [] => [] + | x :: xs => Certified.IndexSet.insert x (Certified.IndexSet.normalize._unsafe_rec xs) +compiler recursion worker Ix.Theory.Certified.LevelEq.leq._unsafe_rec + type: VLevel → VLevel → Bool + implementation: fun a b => + if a = b then true + else + match a, b with + | VLevel.zero, x => true + | a.max b, c => Certified.LevelEq.leq._unsafe_rec a c && Certified.LevelEq.leq._unsafe_rec b c + | a, b.max c => Certified.LevelEq.leq._unsafe_rec a b || Certified.LevelEq.leq._unsafe_rec a c + | a.succ, b.succ => Certified.LevelEq.leq._unsafe_rec a b + | a, b.succ => Certified.LevelEq.leq._unsafe_rec a b + | a.imax b, c => Certified.LevelEq.leq._unsafe_rec a c && Certified.LevelEq.leq._unsafe_rec b c + | x, x_1 => false +compiler recursion worker Ix.Theory.Certified.LevelEq.normalize._unsafe_rec + type: VLevel → VLevel + implementation: fun x => + match x with + | VLevel.zero => VLevel.zero + | VLevel.param i => VLevel.param i + | a.succ => (Certified.LevelEq.normalize._unsafe_rec a).succ + | a.max b => + Certified.LevelEq.max (Certified.LevelEq.normalize._unsafe_rec a) (Certified.LevelEq.normalize._unsafe_rec b) + | a.imax b => + Certified.LevelEq.imax (Certified.LevelEq.normalize._unsafe_rec a) (Certified.LevelEq.normalize._unsafe_rec b) +compiler recursion worker Ix.Theory.Certified.Modeled.applyPi?._unsafe_rec + type: {β : Type u} → VExpr β → List (VExpr β) → Option (VExpr β) + implementation: fun {β} x x_1 => + match x, x_1 with + | type, [] => some type + | binderType.forallE body, argument :: rest => Certified.Modeled.applyPi?._unsafe_rec (body.inst argument) rest + | x, head :: tail => none +compiler recursion worker Ix.Theory.Certified.Modeled.betaHead?._unsafe_rec + type: {β : Type u} → Nat → VExpr β → Option (VExpr β) + implementation: fun {β} x x_1 => + match x, x_1 with + | 0, x => none + | fuel.succ, function.app argument => do + let function ← Certified.Modeled.betaHead?._unsafe_rec fuel function + match function with + | binderType.lam body => Certified.Modeled.betaHead?._unsafe_rec fuel (body.inst argument) + | x => some (function.app argument) + | n.succ, expression => some expression +compiler recursion worker Ix.Theory.Certified.Modeled.checkEach?._unsafe_rec + type: {β : Type u} → + [inst : DecidableEq β] → + Nat → + (entries : Model.Environment β) → + (companions pending : List (Certified.Modeled.Companion β)) → + List (List (Certified.Modeled.EquationWitness β)) → + Option + (Certified.CheckedClaim + (∀ (companion : Certified.Modeled.Companion β), + companion ∈ pending → Certified.Modeled.CompanionChecked entries companions companion)) + implementation: fun {β} [DecidableEq β] fuel entries companions x x_1 => + match x, x_1 with + | [], [] => some { down := ⋯ } + | companion :: rest, witnesses :: remaining => do + let first ← Certified.Modeled.checkCompanion? fuel entries companions companion witnesses + let others ← Certified.Modeled.checkEach?._unsafe_rec fuel entries companions rest remaining + pure { down := ⋯ } + | x, x_2 => none +compiler recursion worker Ix.Theory.Certified.Modeled.checkRules?._unsafe_rec + type: {β : Type u} → + [inst : DecidableEq β] → + Nat → + (entries : Model.Environment β) → + (companions : List (Certified.Modeled.Companion β)) → + (rules : List (Certified.Signature.Rule β)) → + List (Certified.Modeled.EquationWitness β) → + Option + (Certified.CheckedClaim + (∀ (rule : Certified.Signature.Rule β), + rule ∈ rules → + Certified.Modeled.CheckedEquation entries (Certified.Modeled.mapRule companions rule))) + implementation: fun {β} [DecidableEq β] fuel entries companions x x_1 => + match x, x_1 with + | [], [] => some { down := ⋯ } + | rule :: rules, witness :: witnesses => do + let first ← Certified.Modeled.checkEquation? fuel entries (Certified.Modeled.mapRule companions rule) witness + let rest ← Certified.Modeled.checkRules?._unsafe_rec fuel entries companions rules witnesses + pure { down := ⋯ } + | x, x_2 => none +compiler recursion worker Ix.Theory.Certified.Modeled.lookupCompanion._unsafe_rec + type: {β : Type u} → + [DecidableEq β] → List (Certified.Modeled.Companion β) → ConstRef β → Option (Certified.Modeled.Companion β) + implementation: fun {β} [DecidableEq β] x x_1 => + match x, x_1 with + | [], x => none + | companion :: rest, ref => + if companion.header.ref = ref then some companion else Certified.Modeled.lookupCompanion._unsafe_rec rest ref +compiler recursion worker Ix.Theory.Certified.Modeled.splitLam?._unsafe_rec + type: {β : Type u} → Nat → VExpr β → Option (List (VExpr β) × VExpr β) + implementation: fun {β} x x_1 => + match x, x_1 with + | 0, expression => some ([], expression) + | n.succ, domain.lam body => do + let __x ← Certified.Modeled.splitLam?._unsafe_rec n body + match __x with + | (domains, result) => pure (domain :: domains, result) + | n.succ, x => none +compiler recursion worker Ix.Theory.Certified.Modeled.splitPi?._unsafe_rec + type: {β : Type u} → Nat → VExpr β → Option (List (VExpr β) × VExpr β) + implementation: fun {β} x x_1 => + match x, x_1 with + | 0, type => some ([], type) + | n.succ, domain.forallE body => do + let __x ← Certified.Modeled.splitPi?._unsafe_rec n body + match __x with + | (domains, result) => pure (domain :: domains, result) + | n.succ, x => none +compiler recursion worker Ix.Theory.Certified.Ordinary.checkConstructors._unsafe_rec + type: {β : Type u} → + [DecidableEq β] → + Nat → + (entries : Model.Environment β) → + (shape : Certified.Ordinary.Shape β) → + (ctors : List (Certified.Ordinary.Constructor β)) → + List (Certified.Ordinary.ConstructorWitness β) → + Option + (Certified.CheckedClaim + (∀ (ctor : Certified.Ordinary.Constructor β), + ctor ∈ ctors → Certified.Ordinary.ConstructorEvidence entries shape ctor)) + implementation: fun {β} [DecidableEq β] fuel entries shape x x_1 => + match x, x_1 with + | [], [] => some { down := ⋯ } + | ctor :: ctors, witness :: witnesses => + if hrefs : ∀ (e : Model.AExpr β), e ∈ ctor.fields ++ ctor.indices → Model.AExpr.ReferencesIn entries e then do + let fields ← + Certified.verifyTelescope fuel shape.universes entries (some shape.level) shape.parameterContext ctor.fields + witness.fields + let indices ← + Certified.verifyArguments fuel shape.universes entries (ctor.context shape) + (Model.Telescope.lift ctor.fields.length shape.indices) ctor.indices witness.indices + let recursive ← Certified.Ordinary.checkRecursive fuel entries shape ctor ctor.recursive witness.recursive + let rest ← Certified.Ordinary.checkConstructors._unsafe_rec fuel entries shape ctors witnesses + pure { down := ⋯ } + else none + | x, x_2 => none +compiler recursion worker Ix.Theory.Certified.Ordinary.checkRecursive._unsafe_rec + type: {β : Type u} → + [DecidableEq β] → + Nat → + (entries : Model.Environment β) → + (shape : Certified.Ordinary.Shape β) → + (ctor : Certified.Ordinary.Constructor β) → + (fields : List (Certified.Ordinary.RecursiveField β)) → + List (Certified.Ordinary.RecursiveWitness β) → + Option + (Certified.CheckedClaim + (∀ (field : Certified.Ordinary.RecursiveField β), + field ∈ fields → Certified.Ordinary.RecursiveEvidence entries shape ctor field)) + implementation: fun {β} [DecidableEq β] fuel entries shape ctor x x_1 => + match x, x_1 with + | [], [] => some { down := ⋯ } + | field :: fields, witness :: witnesses => + if hrefs : ∀ (e : Model.AExpr β), e ∈ field.domains ++ field.indices → Model.AExpr.ReferencesIn entries e then do + let domains ← + Certified.verifyTelescope fuel shape.universes entries (some shape.level) (ctor.context shape) field.domains + witness.domains + let indices ← + Certified.verifyArguments fuel shape.universes entries + (Model.Telescope.context (ctor.context shape) field.domains) + (Model.Telescope.lift (ctor.fields.length + field.domains.length) shape.indices) field.indices + witness.indices + let rest ← Certified.Ordinary.checkRecursive._unsafe_rec fuel entries shape ctor fields witnesses + pure { down := ⋯ } + else none + | x, x_2 => none +compiler recursion worker Ix.Theory.Certified.Ordinary.parameterVars._unsafe_rec + type: {β : Type u} → Nat → Nat → List (Model.AExpr β) + implementation: fun {β} offset x => + match x with + | 0 => [] + | n.succ => Model.AExpr.bvar (offset + n) :: Certified.Ordinary.parameterVars._unsafe_rec offset n +compiler recursion worker Ix.Theory.Certified.PropWhen.bindList._unsafe_rec + type: (Nat → Certified.PropWhen) → List Nat → Certified.PropWhen + implementation: fun f x => + match x with + | [] => Certified.PropWhen.always + | i :: ps => (f i).inter (Certified.PropWhen.bindList._unsafe_rec f ps) +compiler recursion worker Ix.Theory.Certified.Signature.checkTypes._unsafe_rec + type: {β : Type u} → + [inst : DecidableEq β] → + Nat → + (entries : Model.Environment β) → + (headers : List (Certified.Signature.Header β)) → + List (Certified.Signature.TypeWitness β) → + Option (Certified.CheckedClaim (Certified.Signature.Formed entries headers)) + implementation: fun {β} [DecidableEq β] fuel entries x x_1 => + match x, x_1 with + | [], [] => some { down := ⋯ } + | header :: headers, witness :: witnesses => + if hf : entries header.ref = none then + if hs : Model.AExpr.Scope header.universes 0 header.type then + if hr : Model.AExpr.ReferencesIn entries header.type then do + let ht ← + Certified.verifyType fuel header.universes entries [] header.type (Model.AExpr.sort witness.level) + witness.typing + let rest ← + Certified.Signature.checkTypes._unsafe_rec fuel (entries.insert header.ref header.entry) headers witnesses + pure { down := ⋯ } + else none + else none + else none + | x, x_2 => none +compiler recursion worker Ix.Theory.Certified.Signature.environment._unsafe_rec + type: {β : Type u} → [DecidableEq β] → Model.Environment β → List (Certified.Signature.Header β) → Model.Environment β + implementation: fun {β} [DecidableEq β] entries x => + match x with + | [] => entries + | header :: rest => Certified.Signature.environment._unsafe_rec (entries.insert header.ref header.entry) rest +compiler recursion worker Ix.Theory.Certified.Structure.checkFields._unsafe_rec + type: {β : Type u} → + [DecidableEq β] → + Nat → + Nat → + (entries : Model.Environment β) → + (w : VLevel) → + (Γ : Model.Context β) → + (fields : List (Certified.Structure.Field β)) → + List (Certified.TypingWitness β) → + Option (Certified.CheckedClaim (Certified.Structure.FieldsFormed entries w Γ fields)) + implementation: fun {β} [DecidableEq β] fuel n entries w x x_1 x_2 => + match x, x_1, x_2 with + | x, [], [] => some { down := ⋯ } + | Γ, field :: fields, witness :: witnesses => + if hz : Certified.checkZeroImplies w field.level = true then do + let hA ← Certified.verifyType fuel n entries Γ field.domain (Model.AExpr.sort field.level) witness + let rest ← + Certified.Structure.checkFields._unsafe_rec fuel n entries w (Model.Context.push field.domain Γ) fields + witnesses + pure { down := ⋯ } + else none + | x, x_3, x_4 => none +compiler recursion worker Ix.Theory.Certified.Structure.checkIota._unsafe_rec + type: {β : Type u} → + [inst : DecidableEq β] → + Nat → + (entries : Model.Environment β) → + (d : Certified.Structure.Description β) → + (source recursor : β) → + (mode : Inductive.ElimMode) → + (fields : List (Certified.Structure.Field β × Nat)) → + List (Certified.Signature.RuleWitness β) → + Option + (Certified.CheckedClaim + (∀ (field : Certified.Structure.Field β) (i : Nat), + (field, i) ∈ fields → + Certified.Signature.RuleFormed (d.iotaEnvironment entries source recursor mode i) + (d.iotaRule source i field))) + implementation: fun {β} [DecidableEq β] fuel entries d source recursor mode x x_1 => + match x, x_1 with + | [], [] => some { down := ⋯ } + | (field, i) :: fields, witness :: witnesses => do + let ht ← + Certified.Signature.checkRule fuel (d.iotaEnvironment entries source recursor mode i) (d.iotaRule source i field) + witness + let rest ← Certified.Structure.checkIota._unsafe_rec fuel entries d source recursor mode fields witnesses + pure { down := ⋯ } + | x, x_2 => none +compiler recursion worker Ix.Theory.Model.AExpr.Scope._unsafe_rec + type: {β : Type u_1} → Nat → Nat → Model.AExpr β → Prop + implementation: fun {β} universes depth x => + match x with + | Model.AExpr.bvar i => i < depth + | Model.AExpr.sort l => VLevel.WF universes l + | Model.AExpr.const ref ls => ∀ (l : VLevel), l ∈ ls → VLevel.WF universes l + | f.app a => Model.AExpr.Scope._unsafe_rec universes depth f ∧ Model.AExpr.Scope._unsafe_rec universes depth a + | Model.AExpr.lam p a b => + Certified.PropWhen.WF universes p ∧ + Model.AExpr.Scope._unsafe_rec universes depth a ∧ Model.AExpr.Scope._unsafe_rec universes (depth + 1) b + | Model.AExpr.forallE p a b => + Certified.PropWhen.WF universes p ∧ + Model.AExpr.Scope._unsafe_rec universes depth a ∧ Model.AExpr.Scope._unsafe_rec universes (depth + 1) b + | Model.AExpr.proj ref field e => Model.AExpr.Scope._unsafe_rec universes depth e + | Model.AExpr.natLit value => True +compiler recursion worker Ix.Theory.Model.AExpr.appN._unsafe_rec + type: {β : Type u} → Model.AExpr β → List (Model.AExpr β) → Model.AExpr β + implementation: fun {β} f x => + match x with + | [] => f + | x :: xs => Model.AExpr.appN._unsafe_rec (f.app x) xs +compiler recursion worker Ix.Theory.Model.AExpr.decidableScope._unsafe_rec + type: {β : Type u_1} → {n k : Nat} → {e : Model.AExpr β} → Decidable (Model.AExpr.Scope n k e) + implementation: fun {β} {n k} x => + match x with + | Model.AExpr.bvar i => Model.AExpr.decidableScope._aux_1 i + | Model.AExpr.sort l => VLevel.decidable_WF + | Model.AExpr.const ref ls => Model.AExpr.decidableScope._aux_3 ref ls + | f.app a => instDecidableAnd + | Model.AExpr.lam p A b => instDecidableAnd + | Model.AExpr.forallE p A b => instDecidableAnd + | Model.AExpr.proj ref field e => Model.AExpr.decidableScope._unsafe_rec + | Model.AExpr.natLit value => instDecidableTrue +compiler recursion worker Ix.Theory.Model.AExpr.erase._unsafe_rec + type: {β : Type u_1} → Model.AExpr β → VExpr β + implementation: fun {β} x => + match x with + | Model.AExpr.bvar i => VExpr.bvar i + | Model.AExpr.sort l => VExpr.sort l + | Model.AExpr.const r ls => VExpr.const r ls + | f.app a => (Model.AExpr.erase._unsafe_rec f).app (Model.AExpr.erase._unsafe_rec a) + | Model.AExpr.lam condition a b => (Model.AExpr.erase._unsafe_rec a).lam (Model.AExpr.erase._unsafe_rec b) + | Model.AExpr.forallE condition a b => (Model.AExpr.erase._unsafe_rec a).forallE (Model.AExpr.erase._unsafe_rec b) + | Model.AExpr.proj r i e => VExpr.proj r i (Model.AExpr.erase._unsafe_rec e) + | Model.AExpr.natLit v => VExpr.natLit v +compiler recursion worker Ix.Theory.Model.AExpr.forallN._unsafe_rec + type: {β : Type u} → Certified.PropWhen → List (Model.AExpr β) → Model.AExpr β → Model.AExpr β + implementation: fun {β} p x x_1 => + match x, x_1 with + | [], B => B + | A :: rest, B => Model.AExpr.forallE p A (Model.AExpr.forallN._unsafe_rec p rest B) +compiler recursion worker Ix.Theory.Model.AExpr.inst._unsafe_rec + type: {β : Type u_1} → Model.AExpr β → Model.AExpr β → optParam Nat 0 → Model.AExpr β + implementation: fun {β} x x_1 x_2 => + match x, x_1, x_2 with + | Model.AExpr.bvar i, a, k => Model.AExpr.instVar i a k + | Model.AExpr.sort l, x, x_3 => Model.AExpr.sort l + | Model.AExpr.const r ls, x, x_3 => Model.AExpr.const r ls + | f.app a, e, k => (Model.AExpr.inst._unsafe_rec f e k).app (Model.AExpr.inst._unsafe_rec a e k) + | Model.AExpr.lam p a b, e, k => + Model.AExpr.lam p (Model.AExpr.inst._unsafe_rec a e k) (Model.AExpr.inst._unsafe_rec b e (k + 1)) + | Model.AExpr.forallE p a b, e, k => + Model.AExpr.forallE p (Model.AExpr.inst._unsafe_rec a e k) (Model.AExpr.inst._unsafe_rec b e (k + 1)) + | Model.AExpr.proj r i e, a, k => Model.AExpr.proj r i (Model.AExpr.inst._unsafe_rec e a k) + | Model.AExpr.natLit v, x, x_3 => Model.AExpr.natLit v +compiler recursion worker Ix.Theory.Model.AExpr.instL._unsafe_rec + type: {β : Type u_1} → List VLevel → Model.AExpr β → Model.AExpr β + implementation: fun {β} levels x => + match x with + | Model.AExpr.bvar i => Model.AExpr.bvar i + | Model.AExpr.sort l => Model.AExpr.sort (VLevel.inst levels l) + | Model.AExpr.const r ls => Model.AExpr.const r (List.map (VLevel.inst levels) ls) + | f.app a => (Model.AExpr.instL._unsafe_rec levels f).app (Model.AExpr.instL._unsafe_rec levels a) + | Model.AExpr.lam p a b => + Model.AExpr.lam (Certified.instCondition levels p) (Model.AExpr.instL._unsafe_rec levels a) + (Model.AExpr.instL._unsafe_rec levels b) + | Model.AExpr.forallE p a b => + Model.AExpr.forallE (Certified.instCondition levels p) (Model.AExpr.instL._unsafe_rec levels a) + (Model.AExpr.instL._unsafe_rec levels b) + | Model.AExpr.proj r i e => Model.AExpr.proj r i (Model.AExpr.instL._unsafe_rec levels e) + | Model.AExpr.natLit v => Model.AExpr.natLit v +compiler recursion worker Ix.Theory.Model.AExpr.instRev._unsafe_rec + type: {β : Type u} → Model.AExpr β → List (Model.AExpr β) → Model.AExpr β + implementation: fun {β} x x_1 => + match x, x_1 with + | e, [] => e + | e, a :: rest => Model.AExpr.instRev._unsafe_rec (e.inst a rest.length) rest +compiler recursion worker Ix.Theory.Model.AExpr.lamN._unsafe_rec + type: {β : Type u} → Certified.PropWhen → List (Model.AExpr β) → Model.AExpr β → Model.AExpr β + implementation: fun {β} p x x_1 => + match x, x_1 with + | [], body => body + | A :: rest, body => Model.AExpr.lam p A (Model.AExpr.lamN._unsafe_rec p rest body) +compiler recursion worker Ix.Theory.Model.AExpr.liftN._unsafe_rec + type: {β : Type u_1} → Nat → Model.AExpr β → optParam Nat 0 → Model.AExpr β + implementation: fun {β} count x x_1 => + match x, x_1 with + | Model.AExpr.bvar i, k => Model.AExpr.bvar (liftVar count i k) + | Model.AExpr.sort l, x => Model.AExpr.sort l + | Model.AExpr.const r ls, x => Model.AExpr.const r ls + | f.app a, k => (Model.AExpr.liftN._unsafe_rec count f k).app (Model.AExpr.liftN._unsafe_rec count a k) + | Model.AExpr.lam p a b, k => + Model.AExpr.lam p (Model.AExpr.liftN._unsafe_rec count a k) (Model.AExpr.liftN._unsafe_rec count b (k + 1)) + | Model.AExpr.forallE p a b, k => + Model.AExpr.forallE p (Model.AExpr.liftN._unsafe_rec count a k) (Model.AExpr.liftN._unsafe_rec count b (k + 1)) + | Model.AExpr.proj r i e, k => Model.AExpr.proj r i (Model.AExpr.liftN._unsafe_rec count e k) + | Model.AExpr.natLit v, x => Model.AExpr.natLit v +compiler recursion worker Ix.Theory.Model.AExpr.mapRefs._unsafe_rec + type: {β : Type u} → {γ : Type v} → (ConstRef β → ConstRef γ) → Model.AExpr β → Model.AExpr γ + implementation: fun {β} {γ} mapping x => + match x with + | Model.AExpr.bvar i => Model.AExpr.bvar i + | Model.AExpr.sort level => Model.AExpr.sort level + | Model.AExpr.const ref levels => Model.AExpr.const (mapping ref) levels + | f.app a => (Model.AExpr.mapRefs._unsafe_rec mapping f).app (Model.AExpr.mapRefs._unsafe_rec mapping a) + | Model.AExpr.lam condition type body => + Model.AExpr.lam condition (Model.AExpr.mapRefs._unsafe_rec mapping type) + (Model.AExpr.mapRefs._unsafe_rec mapping body) + | Model.AExpr.forallE condition type body => + Model.AExpr.forallE condition (Model.AExpr.mapRefs._unsafe_rec mapping type) + (Model.AExpr.mapRefs._unsafe_rec mapping body) + | Model.AExpr.proj ref index major => + Model.AExpr.proj (mapping ref) index (Model.AExpr.mapRefs._unsafe_rec mapping major) + | Model.AExpr.natLit n => Model.AExpr.natLit n +compiler recursion worker Ix.Theory.Model.AExpr.references._unsafe_rec + type: {β : Type u} → Model.AExpr β → List (ConstRef β) + implementation: fun {β} x => + match x with + | Model.AExpr.bvar index => [] + | Model.AExpr.sort level => [] + | Model.AExpr.natLit value => [] + | Model.AExpr.const r levels => [r] + | f.app a => Model.AExpr.references._unsafe_rec f ++ Model.AExpr.references._unsafe_rec a + | Model.AExpr.lam condition f a => Model.AExpr.references._unsafe_rec f ++ Model.AExpr.references._unsafe_rec a + | Model.AExpr.forallE condition f a => Model.AExpr.references._unsafe_rec f ++ Model.AExpr.references._unsafe_rec a + | Model.AExpr.proj r field e => r :: Model.AExpr.references._unsafe_rec e +compiler recursion worker Ix.Theory.Model.AExpr.rename._unsafe_rec + type: {β : Type u_1} → {γ : Type u_2} → (β → γ) → Model.AExpr β → Model.AExpr γ + implementation: fun {β} {γ} mapping x => + match x with + | Model.AExpr.bvar i => Model.AExpr.bvar i + | Model.AExpr.sort l => Model.AExpr.sort l + | Model.AExpr.const r ls => Model.AExpr.const (ConstRef.rename mapping r) ls + | f.app a => (Model.AExpr.rename._unsafe_rec mapping f).app (Model.AExpr.rename._unsafe_rec mapping a) + | Model.AExpr.lam p a b => + Model.AExpr.lam p (Model.AExpr.rename._unsafe_rec mapping a) (Model.AExpr.rename._unsafe_rec mapping b) + | Model.AExpr.forallE p a b => + Model.AExpr.forallE p (Model.AExpr.rename._unsafe_rec mapping a) (Model.AExpr.rename._unsafe_rec mapping b) + | Model.AExpr.proj r i e => Model.AExpr.proj (ConstRef.rename mapping r) i (Model.AExpr.rename._unsafe_rec mapping e) + | Model.AExpr.natLit v => Model.AExpr.natLit v +compiler recursion worker Ix.Theory.Model.Telescope.context._unsafe_rec + type: {β : Type u} → Model.Context β → List (Model.AExpr β) → Model.Context β + implementation: fun {β} Γ x => + match x with + | [] => Γ + | A :: rest => Model.Telescope.context._unsafe_rec (Model.Context.push A Γ) rest +compiler recursion worker Ix.Theory.Model.Telescope.extend._unsafe_rec + type: {V : Type v} → (Nat → V) → List V → Nat → V + implementation: fun {V} env x => + match (motive := List V → Nat → V) x with + | [] => env + | x :: xs => Model.Telescope.extend._unsafe_rec (Model.Valuation.cons x env) xs +compiler recursion worker Ix.Theory.Model.Telescope.independent._unsafe_rec + type: {β : Type u} → List (Model.AExpr β) → optParam Nat 0 → List (Model.AExpr β) + implementation: fun {β} x x_1 => + match x, x_1 with + | [], x => [] + | A :: rest, offset => Model.AExpr.liftN offset A :: Model.Telescope.independent._unsafe_rec rest (offset + 1) +compiler recursion worker Ix.Theory.Model.Telescope.inst._unsafe_rec + type: {β : Type u} → Model.AExpr β → List (Model.AExpr β) → optParam Nat 0 → List (Model.AExpr β) + implementation: fun {β} arg x x_1 => + match x, x_1 with + | [], x => [] + | A :: rest, k => A.inst arg k :: Model.Telescope.inst._unsafe_rec arg rest (k + 1) +compiler recursion worker Ix.Theory.Model.Telescope.lift._unsafe_rec + type: {β : Type u} → Nat → List (Model.AExpr β) → optParam Nat 0 → List (Model.AExpr β) + implementation: fun {β} count x x_1 => + match x, x_1 with + | [], x => [] + | A :: rest, k => Model.AExpr.liftN count A k :: Model.Telescope.lift._unsafe_rec count rest (k + 1) +compiler recursion worker Ix.Theory.Model.Telescope.simple._unsafe_rec + type: {V : Type v} → (domains : List V) → Model.SetTheory.Tower.TeleS V domains.length + implementation: fun {V} x => + match x with + | [] => Model.SetTheory.Tower.TeleS.nil + | A :: rest => Model.SetTheory.Tower.TeleS.cons A fun x => Model.Telescope.simple._unsafe_rec rest +compiler recursion worker Ix.Theory.Model.instDecidableEqAExpr.decEq._unsafe_rec + type: {β : Type u_1} → [DecidableEq β] → (x x_1 : Model.AExpr β) → Decidable (x = x_1) + implementation: fun {β} [inst : DecidableEq β] x x_1 => + match x, x_1 with + | Model.AExpr.bvar a, Model.AExpr.bvar b => if h : a = b then h ▸ isTrue ⋯ else isFalse ⋯ + | Model.AExpr.bvar index, Model.AExpr.sort level => isFalse ⋯ + | Model.AExpr.bvar index, Model.AExpr.const ref levels => isFalse ⋯ + | Model.AExpr.bvar index, fn.app arg => isFalse ⋯ + | Model.AExpr.bvar index, Model.AExpr.lam condition domain body => isFalse ⋯ + | Model.AExpr.bvar index, Model.AExpr.forallE condition domain body => isFalse ⋯ + | Model.AExpr.bvar index, Model.AExpr.proj ref field major => isFalse ⋯ + | Model.AExpr.bvar index, Model.AExpr.natLit value => isFalse ⋯ + | Model.AExpr.sort level, Model.AExpr.bvar index => isFalse ⋯ + | Model.AExpr.sort a, Model.AExpr.sort b => if h : a = b then h ▸ isTrue ⋯ else isFalse ⋯ + | Model.AExpr.sort level, Model.AExpr.const ref levels => isFalse ⋯ + | Model.AExpr.sort level, fn.app arg => isFalse ⋯ + | Model.AExpr.sort level, Model.AExpr.lam condition domain body => isFalse ⋯ + | Model.AExpr.sort level, Model.AExpr.forallE condition domain body => isFalse ⋯ + | Model.AExpr.sort level, Model.AExpr.proj ref field major => isFalse ⋯ + | Model.AExpr.sort level, Model.AExpr.natLit value => isFalse ⋯ + | Model.AExpr.const ref levels, Model.AExpr.bvar index => isFalse ⋯ + | Model.AExpr.const ref levels, Model.AExpr.sort level => isFalse ⋯ + | Model.AExpr.const a a_1, Model.AExpr.const b b_1 => + if h : a = b then h ▸ if h : a_1 = b_1 then h ▸ isTrue ⋯ else isFalse ⋯ else isFalse ⋯ + | Model.AExpr.const ref levels, fn.app arg => isFalse ⋯ + | Model.AExpr.const ref levels, Model.AExpr.lam condition domain body => isFalse ⋯ + | Model.AExpr.const ref levels, Model.AExpr.forallE condition domain body => isFalse ⋯ + | Model.AExpr.const ref levels, Model.AExpr.proj ref_1 field major => isFalse ⋯ + | Model.AExpr.const ref levels, Model.AExpr.natLit value => isFalse ⋯ + | fn.app arg, Model.AExpr.bvar index => isFalse ⋯ + | fn.app arg, Model.AExpr.sort level => isFalse ⋯ + | fn.app arg, Model.AExpr.const ref levels => isFalse ⋯ + | a.app a_1, b.app b_1 => + let inst_1 := Model.instDecidableEqAExpr.decEq._unsafe_rec a b; + if h : a = b then + h ▸ + let inst_2 := Model.instDecidableEqAExpr.decEq._unsafe_rec a a; + let inst_3 := Model.instDecidableEqAExpr.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst := Model.instDecidableEqAExpr.decEq._unsafe_rec a_1 a_1; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + | fn.app arg, Model.AExpr.lam condition domain body => isFalse ⋯ + | fn.app arg, Model.AExpr.forallE condition domain body => isFalse ⋯ + | fn.app arg, Model.AExpr.proj ref field major => isFalse ⋯ + | fn.app arg, Model.AExpr.natLit value => isFalse ⋯ + | Model.AExpr.lam condition domain body, Model.AExpr.bvar index => isFalse ⋯ + | Model.AExpr.lam condition domain body, Model.AExpr.sort level => isFalse ⋯ + | Model.AExpr.lam condition domain body, Model.AExpr.const ref levels => isFalse ⋯ + | Model.AExpr.lam condition domain body, fn.app arg => isFalse ⋯ + | Model.AExpr.lam a a_1 a_2, Model.AExpr.lam b b_1 b_2 => + if h : a = b then + h ▸ + let inst_1 := Model.instDecidableEqAExpr.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst_2 := Model.instDecidableEqAExpr.decEq._unsafe_rec a_1 a_1; + let inst_3 := Model.instDecidableEqAExpr.decEq._unsafe_rec a_2 b_2; + if h : a_2 = b_2 then + h ▸ + let inst := Model.instDecidableEqAExpr.decEq._unsafe_rec a_2 a_2; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + else isFalse ⋯ + | Model.AExpr.lam condition domain body, Model.AExpr.forallE condition_1 domain_1 body_1 => isFalse ⋯ + | Model.AExpr.lam condition domain body, Model.AExpr.proj ref field major => isFalse ⋯ + | Model.AExpr.lam condition domain body, Model.AExpr.natLit value => isFalse ⋯ + | Model.AExpr.forallE condition domain body, Model.AExpr.bvar index => isFalse ⋯ + | Model.AExpr.forallE condition domain body, Model.AExpr.sort level => isFalse ⋯ + | Model.AExpr.forallE condition domain body, Model.AExpr.const ref levels => isFalse ⋯ + | Model.AExpr.forallE condition domain body, fn.app arg => isFalse ⋯ + | Model.AExpr.forallE condition domain body, Model.AExpr.lam condition_1 domain_1 body_1 => isFalse ⋯ + | Model.AExpr.forallE a a_1 a_2, Model.AExpr.forallE b b_1 b_2 => + if h : a = b then + h ▸ + let inst_1 := Model.instDecidableEqAExpr.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst_2 := Model.instDecidableEqAExpr.decEq._unsafe_rec a_1 a_1; + let inst_3 := Model.instDecidableEqAExpr.decEq._unsafe_rec a_2 b_2; + if h : a_2 = b_2 then + h ▸ + let inst := Model.instDecidableEqAExpr.decEq._unsafe_rec a_2 a_2; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + else isFalse ⋯ + | Model.AExpr.forallE condition domain body, Model.AExpr.proj ref field major => isFalse ⋯ + | Model.AExpr.forallE condition domain body, Model.AExpr.natLit value => isFalse ⋯ + | Model.AExpr.proj ref field major, Model.AExpr.bvar index => isFalse ⋯ + | Model.AExpr.proj ref field major, Model.AExpr.sort level => isFalse ⋯ + | Model.AExpr.proj ref field major, Model.AExpr.const ref_1 levels => isFalse ⋯ + | Model.AExpr.proj ref field major, fn.app arg => isFalse ⋯ + | Model.AExpr.proj ref field major, Model.AExpr.lam condition domain body => isFalse ⋯ + | Model.AExpr.proj ref field major, Model.AExpr.forallE condition domain body => isFalse ⋯ + | Model.AExpr.proj a a_1 a_2, Model.AExpr.proj b b_1 b_2 => + if h : a = b then + h ▸ + if h : a_1 = b_1 then + h ▸ + let inst_1 := Model.instDecidableEqAExpr.decEq._unsafe_rec a_2 b_2; + if h : a_2 = b_2 then + h ▸ + let inst := Model.instDecidableEqAExpr.decEq._unsafe_rec a_2 a_2; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + else isFalse ⋯ + | Model.AExpr.proj ref field major, Model.AExpr.natLit value => isFalse ⋯ + | Model.AExpr.natLit value, Model.AExpr.bvar index => isFalse ⋯ + | Model.AExpr.natLit value, Model.AExpr.sort level => isFalse ⋯ + | Model.AExpr.natLit value, Model.AExpr.const ref levels => isFalse ⋯ + | Model.AExpr.natLit value, fn.app arg => isFalse ⋯ + | Model.AExpr.natLit value, Model.AExpr.lam condition domain body => isFalse ⋯ + | Model.AExpr.natLit value, Model.AExpr.forallE condition domain body => isFalse ⋯ + | Model.AExpr.natLit value, Model.AExpr.proj ref field major => isFalse ⋯ + | Model.AExpr.natLit a, Model.AExpr.natLit b => if h : a = b then h ▸ isTrue ⋯ else isFalse ⋯ +compiler recursion worker Ix.Theory.Model.instDecidableEqAnnotationTree.decEq._unsafe_rec + type: (x x_1 : Model.AnnotationTree) → Decidable (x = x_1) + implementation: fun x x_1 => + match x, x_1 with + | Model.AnnotationTree.leaf, Model.AnnotationTree.leaf => isTrue ⋯ + | Model.AnnotationTree.leaf, fn.app arg => isFalse ⋯ + | Model.AnnotationTree.leaf, Model.AnnotationTree.lam condition domain body => isFalse ⋯ + | Model.AnnotationTree.leaf, Model.AnnotationTree.forallE condition domain body => isFalse ⋯ + | Model.AnnotationTree.leaf, major.proj => isFalse ⋯ + | fn.app arg, Model.AnnotationTree.leaf => isFalse ⋯ + | a.app a_1, b.app b_1 => + let inst := Model.instDecidableEqAnnotationTree.decEq._unsafe_rec a b; + if h : a = b then + h ▸ + let inst := Model.instDecidableEqAnnotationTree.decEq._unsafe_rec a a; + let inst := Model.instDecidableEqAnnotationTree.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst := Model.instDecidableEqAnnotationTree.decEq._unsafe_rec a_1 a_1; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + | fn.app arg, Model.AnnotationTree.lam condition domain body => isFalse ⋯ + | fn.app arg, Model.AnnotationTree.forallE condition domain body => isFalse ⋯ + | fn.app arg, major.proj => isFalse ⋯ + | Model.AnnotationTree.lam condition domain body, Model.AnnotationTree.leaf => isFalse ⋯ + | Model.AnnotationTree.lam condition domain body, fn.app arg => isFalse ⋯ + | Model.AnnotationTree.lam a a_1 a_2, Model.AnnotationTree.lam b b_1 b_2 => + if h : a = b then + h ▸ + let inst := Model.instDecidableEqAnnotationTree.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst := Model.instDecidableEqAnnotationTree.decEq._unsafe_rec a_1 a_1; + let inst := Model.instDecidableEqAnnotationTree.decEq._unsafe_rec a_2 b_2; + if h : a_2 = b_2 then + h ▸ + let inst := Model.instDecidableEqAnnotationTree.decEq._unsafe_rec a_2 a_2; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + else isFalse ⋯ + | Model.AnnotationTree.lam condition domain body, Model.AnnotationTree.forallE condition_1 domain_1 body_1 => + isFalse ⋯ + | Model.AnnotationTree.lam condition domain body, major.proj => isFalse ⋯ + | Model.AnnotationTree.forallE condition domain body, Model.AnnotationTree.leaf => isFalse ⋯ + | Model.AnnotationTree.forallE condition domain body, fn.app arg => isFalse ⋯ + | Model.AnnotationTree.forallE condition domain body, Model.AnnotationTree.lam condition_1 domain_1 body_1 => + isFalse ⋯ + | Model.AnnotationTree.forallE a a_1 a_2, Model.AnnotationTree.forallE b b_1 b_2 => + if h : a = b then + h ▸ + let inst := Model.instDecidableEqAnnotationTree.decEq._unsafe_rec a_1 b_1; + if h : a_1 = b_1 then + h ▸ + let inst := Model.instDecidableEqAnnotationTree.decEq._unsafe_rec a_1 a_1; + let inst := Model.instDecidableEqAnnotationTree.decEq._unsafe_rec a_2 b_2; + if h : a_2 = b_2 then + h ▸ + let inst := Model.instDecidableEqAnnotationTree.decEq._unsafe_rec a_2 a_2; + isTrue ⋯ + else isFalse ⋯ + else isFalse ⋯ + else isFalse ⋯ + | Model.AnnotationTree.forallE condition domain body, major.proj => isFalse ⋯ + | major.proj, Model.AnnotationTree.leaf => isFalse ⋯ + | major.proj, fn.app arg => isFalse ⋯ + | major.proj, Model.AnnotationTree.lam condition domain body => isFalse ⋯ + | major.proj, Model.AnnotationTree.forallE condition domain body => isFalse ⋯ + | a.proj, b.proj => + let inst := Model.instDecidableEqAnnotationTree.decEq._unsafe_rec a b; + if h : a = b then + h ▸ + let inst := Model.instDecidableEqAnnotationTree.decEq._unsafe_rec a a; + isTrue ⋯ + else isFalse ⋯ +compiler recursion worker Ix.Theory.Model.instReprAnnotationTree.repr._unsafe_rec + type: Model.AnnotationTree → Nat → Format + implementation: fun x prec => + match x with + | Model.AnnotationTree.leaf => + Repr.addAppParen + (Format.nest (if prec ≥ 1024 then 1 else 2) (Format.text "Ix.Theory.Model.AnnotationTree.leaf")).group prec + | a.app a_1 => + Repr.addAppParen + (Format.nest (if prec ≥ 1024 then 1 else 2) + (Format.text "Ix.Theory.Model.AnnotationTree.app" ++ Format.line ++ + Model.instReprAnnotationTree.repr._unsafe_rec a 1024 ++ + Format.line ++ + Model.instReprAnnotationTree.repr._unsafe_rec a_1 1024)).group + prec + | Model.AnnotationTree.lam a a_1 a_2 => + Repr.addAppParen + (Format.nest (if prec ≥ 1024 then 1 else 2) + (Format.text "Ix.Theory.Model.AnnotationTree.lam" ++ Format.line ++ reprArg a ++ Format.line ++ + Model.instReprAnnotationTree.repr._unsafe_rec a_1 1024 ++ + Format.line ++ + Model.instReprAnnotationTree.repr._unsafe_rec a_2 1024)).group + prec + | Model.AnnotationTree.forallE a a_1 a_2 => + Repr.addAppParen + (Format.nest (if prec ≥ 1024 then 1 else 2) + (Format.text "Ix.Theory.Model.AnnotationTree.forallE" ++ Format.line ++ reprArg a ++ Format.line ++ + Model.instReprAnnotationTree.repr._unsafe_rec a_1 1024 ++ + Format.line ++ + Model.instReprAnnotationTree.repr._unsafe_rec a_2 1024)).group + prec + | a.proj => + Repr.addAppParen + (Format.nest (if prec ≥ 1024 then 1 else 2) + (Format.text "Ix.Theory.Model.AnnotationTree.proj" ++ Format.line ++ + Model.instReprAnnotationTree.repr._unsafe_rec a 1024)).group + prec +compiler recursion worker Ix.Theory.Certified.Ordinary.Shape.checkConstructorTypes._unsafe_rec + type: {β : Type u} → + [inst : DecidableEq β] → + Nat → + (entries : Model.Environment β) → + (shape : Certified.Ordinary.Shape β) → + (source : β) → + (ctors : List (Certified.Ordinary.Constructor β)) → + List (Certified.Ordinary.Shape.TypeWitness β) → + Option + (Certified.CheckedClaim + (∀ (ctor : Certified.Ordinary.Constructor β), + ctor ∈ ctors → + Model.EntryClosed (shape.familyEnvironment entries source) + (shape.constructorEntry source ctor) ∧ + ∃ l, + Model.TypingClaim (shape.familyEnvironment entries source) [] (ctor.type shape source) + (Model.AExpr.sort l))) + implementation: fun {β} [DecidableEq β] fuel entries shape source x x_1 => + match x, x_1 with + | [], [] => some { down := ⋯ } + | ctor :: ctors, witness :: witnesses => + if hs : Model.AExpr.Scope shape.universes 0 (ctor.type shape source) then + if hr : Model.AExpr.ReferencesIn (shape.familyEnvironment entries source) (ctor.type shape source) then do + let ht ← + Certified.verifyType fuel shape.universes (shape.familyEnvironment entries source) [] (ctor.type shape source) + (Model.AExpr.sort witness.level) witness.witness + let rest ← Certified.Ordinary.Shape.checkConstructorTypes._unsafe_rec fuel entries shape source ctors witnesses + pure { down := ⋯ } + else none + else none + | x, x_2 => none +compiler recursion worker Ix.Theory.Certified.Ordinary.Shape.checkRules._unsafe_rec + type: {β : Type u} → + [DecidableEq β] → + Nat → + (entries : Model.Environment β) → + (shape : Certified.Ordinary.Shape β) → + (source recursor : β) → + (mode : Inductive.ElimMode) → + (ctors : List (Certified.Ordinary.Constructor β × Nat)) → + List (Certified.Ordinary.RuleWitness β) → + Option + (Certified.CheckedClaim + (∀ (ctor : Certified.Ordinary.Constructor β) (i : Nat), + (ctor, i) ∈ ctors → + Certified.Ordinary.Shape.RuleFormation entries shape source recursor mode i ctor)) + implementation: fun {β} [DecidableEq β] fuel entries shape source recursor mode x x_1 => + match x, x_1 with + | [], [] => some { down := ⋯ } + | (ctor, i) :: ctors, witness :: witnesses => do + let rule ← Certified.Ordinary.Shape.checkRule fuel entries shape source recursor mode i ctor witness + let rest ← Certified.Ordinary.Shape.checkRules._unsafe_rec fuel entries shape source recursor mode ctors witnesses + pure { down := ⋯ } + | x, x_2 => none +compiler recursion worker Ix.Theory.Model.SetTheory.Tower.BoundS._unsafe_rec + type: {V : Type u} → [Model.SetTheory V] → Nat → {n : Nat} → Model.SetTheory.Tower.TeleS V n → Prop + implementation: fun {V} [Model.SetTheory V] w x x_1 => + match x, x_1 with + | .(0), Model.SetTheory.Tower.TeleS.nil => True + | .(n + 1), Model.SetTheory.Tower.TeleS.cons A B => + Model.SetTheory.Mem A (Model.SetTheory.univ w) ∧ + ∀ (a : V), Model.SetTheory.Mem a A → Model.SetTheory.Tower.BoundS._unsafe_rec w (B a) +compiler recursion worker Ix.Theory.Model.SetTheory.Tower.FitsS._unsafe_rec + type: {V : Type u} → [Model.SetTheory V] → {n : Nat} → Model.SetTheory.Tower.TeleS V n → List V → Prop + implementation: fun {V} [Model.SetTheory V] x x_1 x_2 => + match x, x_1, x_2 with + | .(0), Model.SetTheory.Tower.TeleS.nil, [] => True + | .(0), Model.SetTheory.Tower.TeleS.nil, head :: tail => False + | .(n + 1), Model.SetTheory.Tower.TeleS.cons A B, [] => False + | .(n + 1), Model.SetTheory.Tower.TeleS.cons A B, a :: as => + Model.SetTheory.Mem a A ∧ Model.SetTheory.Tower.FitsS._unsafe_rec (B a) as +compiler recursion worker Ix.Theory.Model.SetTheory.Tower.PropS._unsafe_rec + type: {V : Type u} → [Model.SetTheory V] → {n : Nat} → Model.SetTheory.Tower.TeleS V n → Prop + implementation: fun {V} [Model.SetTheory V] x x_1 => + match x, x_1 with + | .(0), Model.SetTheory.Tower.TeleS.nil => True + | .(n + 1), Model.SetTheory.Tower.TeleS.cons A B => + Model.SetTheory.Mem A Model.SetTheory.univZero ∧ + ∀ (a : V), Model.SetTheory.Mem a A → Model.SetTheory.Tower.PropS._unsafe_rec (B a) +Certified project runtime audit OK: 101 compiler recursion workers inventoried; no other unsafe/partial declarations, extern, implemented_by, computed_field, or csimp replacements in the mathematical import graph +Certified foundation audit OK: 6568 declarations; 145 named roots; explicit SetTheory hypothesis; baseline axioms only diff --git a/crates/kernel/src/def_eq.rs b/crates/kernel/src/def_eq.rs index 7e04e0170..8e0010007 100644 --- a/crates/kernel/src/def_eq.rs +++ b/crates/kernel/src/def_eq.rs @@ -563,7 +563,7 @@ impl TypeChecker<'_, M> { break; } - // C6: Before unfolding a definition, try reducing projection apps + // Before unfolding a definition, try reducing projection apps // on the non-definition side (lean4lean tryUnfoldProjApp). if a_delta && !b_delta { if let Some(wb2) = self.try_unfold_proj_app(wb)? { @@ -1566,7 +1566,7 @@ impl TypeChecker<'_, M> { } // ----------------------------------------------------------------------- - // Post-delta congruence and projection unfolding (C5, C6) + // Post-delta congruence and projection unfolding // ----------------------------------------------------------------------- /// Structural congruence after lazy delta exhaustion (lean4lean isDefEqConst/Proj). diff --git a/crates/kernel/src/inductive.rs b/crates/kernel/src/inductive.rs index b5f0bc436..222ec5254 100644 --- a/crates/kernel/src/inductive.rs +++ b/crates/kernel/src/inductive.rs @@ -4949,7 +4949,7 @@ flat[{gi}].id={}, rec_ids[{gi}]={}; complete recursor types differ", Ok(()) }, None => { - // C2: No generated recursor found — MUST NOT silently pass. + // Reject a recursor with no generated candidate for its major type. // If we can't generate a canonical recursor, we can't verify the provided one. Err(TcError::Other(format!( "check_recursor: no generated recursor for major {}", diff --git a/docs/benchmarking.md b/docs/benchmarking.md index e5a566412..e545248f6 100644 --- a/docs/benchmarking.md +++ b/docs/benchmarking.md @@ -85,11 +85,6 @@ ix bench fetch-main --sha $(git merge-base origin/main HEAD) \ ix bench compare --backend aiur --env InitStd --mode prove \ --base main.json --pr .lake/benches/aiur-InitStd-prove.json -# The lean4lean reference kernel over InitStd — whole-library replay plus -# per-constant closure rows, from oleans (no .ixe needed). Read next to the -# ooc cell's rows for the Rust-vs-reference-kernel gap on the same library: -ix bench run --backend lean4lean --env InitStd -ix bench compare --backend lean4lean --env InitStd ``` `--repo ` points the run at another checkout: the *measured* tools @@ -104,7 +99,6 @@ a PR tree and compare them — exactly what the PR workflow does. | `zisk` | ZisK VM execute: cycles, execute-time, throughput, peak-rss, constants (pre-shard closure count, same universe as aiur's), shards (the runtime-planned partition size; 1 when the closure fits) | `zisk-host` | | `sp1` | SP1 VM execute (currently disabled in the registry) | `sp1-host` | | `ooc` | out-of-circuit Rust kernel: whole-env row + one full-closure row per constant (`check-time` wraps only the check — the env loads once, outside every row's timed window) | `ix check-rs --json` | -| `lean4lean` | the reference Lean4-in-Lean4 kernel ([digama0/lean4lean](https://github.com/digama0/lean4lean), required by the lakefile at a pinned rev) — the external yardstick for the Ix kernels on the same libraries. Olean-driven (no `.ixe`): the whole-library row replays every module in the env's import closure through lean4lean, module-parallel (check-time, constants, throughput, peak-rss; tune parallelism with `LEAN_NUM_THREADS`), plus one full-closure row per constant (the name's transitive closure into a fresh kernel env), mirroring ooc's row shape. Registry-disabled for CI (no bencher testbed yet); `ix bench run --backend lean4lean` works locally regardless | `bench-lean4lean` | | `compile` | `ix compile .lean → .ixe`: compile-time, file-size, constants, throughput | `ix compile --json` | | `decompile` | inverse of compile — `ix decompile .ixe → Lean consts`: decompile-time, throughput, peak-rss, constants, file-size (input `.ixe`). Consumes the compile cell's `.ixe` rather than producing one; a malformed decompile reddens the cell. Deep roundtrip fidelity is gated by the canonical checks (`ix validate` / roundtrip tests), which need the original Lean env the `.ixe` can't supply | `ix decompile --json` | diff --git a/docs/certified-checking.md b/docs/certified-checking.md new file mode 100644 index 000000000..c0f8b6057 --- /dev/null +++ b/docs/certified-checking.md @@ -0,0 +1,160 @@ +# Certified source and claim checking + +Ix contains certified source and claim adapters under +`Ix.Certified`, with checker wrappers in `Ix.Kernel.Certified` and +`Ix.Kernel.CertifiedClaims`. Successful validation constructs receipts that +connect the authenticated Ixon source to the set model in `Ix.Theory`. +The maintained implementation and its regression evidence are local to this +repository. + +The certificate profile checks a specific collection of declaration forms +and logical schemas. Witness search supplies proposals; the certified +validator checks every proposal before reporting success. Unsupported forms, +missing models and unsuccessful searches decline. These commands provide an +explicit entry point for that profile. + +## Commands + +Build the source and claim commands: + +```sh +lake build --wfail certified-check certified-claim-check +``` + +Source checking takes a lazy `.ixe` file and a JSON request that selects the +target or subjects, primitive addresses, source objects and natural values: + +```sh +.lake/build/bin/certified-check proof SOURCE.ixe REQUEST.json +.lake/build/bin/certified-check store SOURCE.ixe REQUEST.json +``` + +The request fields are `target`, `subjects`, `objects`, `naturals`, +`falseType`, `falseElim`, nullable `natType`, and optional `models`. Addresses +are 32-byte hexadecimal strings. Model hints select earlier source +declarations; the validator checks their types and complete source equations. +The request parser is defined in +[`Command.lean`](../Ix/Certified/Command.lean). + +For a runnable source example: + +```sh +lake build certified-source-tests +.lake/build/bin/certified-source-tests /tmp/ix-certified-examples +.lake/build/bin/certified-check proof \ + /tmp/ix-certified-examples/identity/source.ixe \ + /tmp/ix-certified-examples/identity/request.json +``` + +Claim checking also takes the exact serialized envelope. The JSON request +selects its expected address and supplies logical, membership or revelation +witness hints: + +```sh +.lake/build/bin/certified-claim-check SOURCE.ixe ENVELOPE.bin REQUEST.json +``` + +The envelope binds the claim, primitive profile, logical-axiom manifest and +protocol versions. The current checker version is **2**; format, codec, +policy and aggregation versions are **1**. Exact decoding, canonical +re-encoding, the expected content address and all versions are checked. +Trailing bytes and older checker envelopes reject. See +[`Envelope.lean`](../Ix/Certified/Envelope.lean) and +[`ClaimCommand.lean`](../Ix/Certified/ClaimCommand.lean). + +Both commands currently use fuel 6,400. Success exits 0 and prints a JSON +acceptance record; a rejected request exits 1. Fuel or witness-search failure +can reject a valid source, so success and completeness are separate +properties. + +## Semantic contract + +`ClaimCommand.run_meaning` proves that successful pure command execution +produces a receipt for the exact envelope bytes and establishes +`SemanticClaimMeaning`. For logical claims, the receipt preserves the +original source statements and constructs a compatible interpretation while +preserving the declared frontier. Model companions for mutual and nested +inductives must already have been checked; their complete recursor equations +are validated before the new source package is admitted. + +For a logical receipt with no structural frontier, +`LogicalReceipt.closed_subject_meaning` constructs its model. +`LogicalReceipt.no_False` then rules out a checked subject whose type is the +profile's false proposition, under the explicit `[SetTheory V]` hypothesis. +The admitted logical schemas have realizations in that model. A closed +structural frontier does not imply an empty logical-axiom manifest; the +manifest records admitted source axioms under the enforced policy. + +Membership and revelation establish their respective structural meanings. +Evaluation claims are excluded from this semantic profile. The relevant +definitions and statements are in +[`ClaimAccept.lean`](../Ix/Certified/ClaimAccept.lean), +[`ClaimMeaning.lean`](../Ix/Certified/ClaimMeaning.lean) and +[`LogicalPolicy.lean`](../Ix/Theory/Certified/LogicalPolicy.lean). + +The mathematical contract concerns the Lean functions. Native Lean +execution, filesystem loading and the BLAKE3 foreign interface remain +execution boundaries. Full production-checker refinement and execution +of this certification inside an authenticated Aiur proof remain separate +obligations described in +[`kernel-verification.md`](kernel-verification.md). + +## Audits and regression evidence + +Run the complete local gate with: + +```sh +lake run check-certified +``` + +The gate builds the adapters, audits and test programs with warnings treated +as errors. It compares the exact foundation report, checks the frozen source +archive and maintained import identities, runs six native host test programs, +and exercises the actual source and claim executables on all three CLI +corpora. CI runs the same gate. + +The combined audit covers **86 distinct roots**, **74 premise definitions or +constructor types**, **9,682 logical declarations** and **10,957 declarations +including runtime dependencies**. The five historical component audits +remain independently callable. All 90 historical root occurrences and 75 +premise occurrences retain their statements after namespace qualification is +normalized. The exact report is +[`Tests/Certified/foundation.txt`](../Tests/Certified/foundation.txt). + +The audit traverses full checked types, bodies and inductive constructors. +It checks exact axiom sets and follows compiler recursion workers and +executable replacements transitively, including private constants. These +roots use only `propext`, `Classical.choice` and `Quot.sound`. The runtime +inventory records five BLAKE3 foreign operations and 130 recursion workers +with safe source definitions; it permits no partial opaque worker source or +executable replacement. + +This stronger traversal exposed a native-evaluation axiom that the historical +imported axiom summaries missed. It came from the BLAKE3 helper's 32-byte +output bound. `Address.blake3` now supplies a kernel-checked proof of that +bound. The wrapper is definitionally equal to the former hash operation, +and generated source/envelope corpora are unchanged. +The affected kernel and source-compiler audits also remove this dependency. + +| Actual command corpus | Fixtures | Accepted | Rejected | +| --- | ---: | ---: | ---: | +| Source proof/store | 42 source fixtures | 84 | 562 | +| Versioned claims | 1,578 claim fixtures | 584 | 5,454 | +| Modeled source and claims | 44 source and 74 claim fixtures | 30 | 540 | + +The native modeled tests additionally cover 29 source mutations with accepted +controls, 42 forged witnesses and nine malformed mutual inputs. The other +native suites cover source fidelity, lazy loading, cold/warm caches, +rollback, claim cycles, shared dependencies, membership and revelation. + +All generated source and envelope bytes and CLI process outputs are +compared against the frozen adapter evidence. Host reports preserve every +non-VM field; only `pilotDeclined` and `pilotDeclines` are projected away +from the three historical reports that mixed host and VM checks. The VM +pilot, packet encoder and VM execution suite are archived for a later change. +CLI JSON requests and aggregate records are compared as parsed values because +the maintained Lean driver formats JSON differently. The archive, source mappings, licenses and +historical reproduction limits are documented in +[`Tests/Fixtures/Certified`](../Tests/Fixtures/Certified/README.md). The archive +recovers the selected adapter sources and fixtures; it does not include the +complete historical Ix base checkout. diff --git a/docs/ix_canonicity.md b/docs/ix_canonicity.md index 0616e2197..bdaa5bc19 100644 --- a/docs/ix_canonicity.md +++ b/docs/ix_canonicity.md @@ -1490,7 +1490,7 @@ this rule closes that gap. **The quotient.** Universe-level spellings are presentation, not content. Two levels are identified in canonical form exactly when the kernels' semantic level equality holds — `univEq` -(`Ix/Tc/Level.lean`), the relation all three kernels decide during +(`Ix/Kernel/Level.lean`), the relation all three kernels decide during defeq. `univEq`'s normal-form comparison ignores EMPTY subsumption entries (constant 0, no vars — bookkeeping the subsumption pass leaves behind rather than removing; `normLevelLe` always ignored @@ -1518,7 +1518,7 @@ unnormalized substitution results). **Canonical representative.** `canonUniv : Univ → Univ` is `linearize ∘ subsumption ∘ normalizeAux` — the kernels' Géran -comparison form (`normalizeLevel`, `Ix/Tc/Level.lean:227-472`; +comparison form (`normalizeLevel`, `Ix/Kernel/Level.lean:227-472`; `crates/kernel/src/level.rs:335-696`; `Ix/IxVM/Kernel/Levels.lean:336-386`), transliterated to positional `Ixon.Univ` and then linearized back into a level term. The Géran @@ -1556,7 +1556,7 @@ tests in both languages: - **P5 (mirror parity):** Rust and Lean `canonUniv` agree byte-for-byte on serialized output. - **P6 (mk\* absorption):** `canonUniv ∘ reduceIxonUniv = canonUniv` - (`reduceIxonUniv`, `Ix/Tc/Ingress.lean`, is the retained oracle + (`reduceIxonUniv`, `Ix/Kernel/Ingress.lean`, is the retained oracle for this). `canonUniv` lives next to the wire type (`crates/ixon/src/univ.rs` @@ -1565,13 +1565,13 @@ and probes share one implementation per language; the three kernel `NormLevel` implementations stay untouched as the P4 oracle. Worst case, canonical forms are exponential in nested `imax`-of-`max` depth (the Géran distribution rules duplicate the left subterm, -`Ix/Tc/Level.lean:333-351`); real levels are a handful of nodes, and +`Ix/Kernel/Level.lean:333-351`); real levels are a handful of nodes, and the blowup is paid once at compile time by whoever writes a pathological spelling — never by readers of stored canonical forms. **The kernel-rebuild rule set.** The `mk*` simplification rules — Lean `kernel/level.cpp:81-103`/`:112-120`, mirrored at -`Ix/Tc/Level.lean:144-197`, `crates/kernel/src/level.rs:162-247`, +`Ix/Kernel/Level.lean:144-197`, `crates/kernel/src/level.rs:162-247`, `Ix/IxVM/Kernel/Levels.lean:637-680` — are normative here in three roles: the P3 fixpoint target (kernel ingress rebuilds through them), the stage-1 decoration-presence test (below), and the P6 oracle. @@ -1601,7 +1601,7 @@ They are **not** the address quotient (they miss commutative twins). | I6 | otherwise raw `imax a b` | — | with predicates `isZero` / `isExplicit` (`succ^n zero`) / -`isNeverZero` / `offset` exactly as in `Ix/Tc/Level.lean:64-95` / +`isNeverZero` / `offset` exactly as in `Ix/Kernel/Level.lean:64-95` / `crates/kernel/src/level.rs:111-150`. Both rule sets — `mk*` and the Géran linearization — are **frozen** at the pinned toolchain's behavior; upstream drift mints new spellings, which simply @@ -1663,11 +1663,11 @@ class as binder names, and is caught by the strict roundtrip gates). one kernel-side consumer: the original spelling rides as a **decoration** on meta-mode `sort`/`const` occurrence nodes — folded into the metadata-aware `metaAddr` (so interning and egress -memoization never collapse spelling twins, `Ix/Tc/Expr.lean:61-72`) +memoization never collapse spelling twins, `Ix/Kernel/Expr.lean:61-72`) and **never** into the semantic `addr` (anon/meta address parity, `tc-meta-addr`, is preserved; checking never sees spellings). Meta egress replays the decoration at its `sort`/`const` arms -(`Ix/Tc/EgressLean.lean:119-128`) instead of egressing the +(`Ix/Kernel/EgressLean.lean:119-128`) instead of egressing the normalized kernel level. Decorations must not live on `KUniv` nodes — KUniv interning is semantic-address-keyed and would collapse spelling twins first-wins, the same lossiness as table-keyed @@ -1677,7 +1677,7 @@ comparison and is an active spelling-fidelity gate through the kernel's data path, complementing the decompiler gate (phase 5). With stored content canonical, the anon roundtrip comparison is strict too (the `reduceIxonUniv` modulo in `canonExpr` was deleted; -`Ix/Tc/Egress.lean` module doc). +`Ix/Kernel/Egress.lean` module doc). **Decoration source.** Patches are the primary source: meta ingress looks the occurrence's (post-mdata) arena index up in `univPatches` @@ -2309,13 +2309,13 @@ Phase 2 paths already were). Remaining audit items: `restoreLeanSourceNameHints` / `restoreSourceNamesSameContent` in `Ix/AuxGen/Kernel.lean`) match arm-for-arm; what diverged — and produced the Mathlib redStep instance — was the **key - equivalence**: the Lean side keys hints by `Ix.Tc.KExpr` content + equivalence**: the Lean side keys hints by `Ix.Kernel.KExpr` content addresses, while the Rust side keyed by `KExpr::hash_key()`, which is an intern-assigned uid, fresh for every un-interned `to_kexpr_static` construction — so no restore-time key ever matched a collect-time key and the Rust hint pass restored nothing. Originally fixed by `kexpr_content_key` (a pure name-erased structural - digest mirroring the `ExprKey`/`Ix.Tc` equivalence) and by making the + digest mirroring the `ExprKey`/`Ix.Kernel` equivalence) and by making the WHNF no-op test structural (`==`) rather than uid equality. The hint pass now uses exact, pass-local structural identities instead of that digest, with full shallow-key equality on hash collisions. Any future @@ -2378,7 +2378,7 @@ pipelines; decompile patch replay; the decoration source switch to patches (stage-1 rule retained as the patchless fallback); phase 3 strict; `univEq` empty-entry-insensitive (the exact semantic quotient, §10.6); primitive pins regenerated in all mirrors -(`prim_addrs.rs`, `Ix/Tc/Primitive.lean`, the IxVM address +(`prim_addrs.rs`, `Ix/Kernel/Primitive.lean`, the IxVM address literals); artifacts regenerated with the format-break hint. **Acceptance evidence:** `ix validate` and `ix validate-lean` at diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md new file mode 100644 index 000000000..7dfc385f7 --- /dev/null +++ b/docs/kernel-verification.md @@ -0,0 +1,144 @@ +# Kernel verification + +`Ix.Kernel` is the production Lean checker. `Ix.Kernel.Verify` contains its +implementation proofs; `Ix.Compile.Verify` contains the Lean-to-Ixon compiler +proofs. Their named specification and reference implementation lemmas are local +under `Ix.Theory.Named`. Building or checking them requires no external +formalization repository. + +The named development retains the 104 source modules needed by the existing +proof dependency graph, including the inductive fixtures consumed by Ix. +Standalone applications, benchmarks, and unrelated tests are excluded. Its +original source hashes and attribution are recorded in `Ix/Theory/Named/NOTICE` +and `Tests/Theory/NamedManifest.lean`; the Apache license is preserved alongside +the sources. The added axiom-audit helper is authored in Ix. + +## Connection to the consistency model + +The name-indexed specification and the set model use the same +`Ix.Theory.VLevel`. The set model and its foundational audit remain independent +of the named development. Mathlib is confined to the separate +[set-theory model package](../Models/SetTheory/README.md). + +That package's `IxSetTheoryModel.setTheoryOfCarneiro` constructs the exact +`Ix.Theory.Model.SetTheory` interface on Mathlib's `ZFSet`. It takes an explicit +`OmegaInaccessibles` hypothesis: a strictly increasing countable sequence of +strongly inaccessible cardinals. The universe chain is `V_ (κ n).ord`. +`carneiro_implies_ix` proves model existence under this hypothesis, with a full +dependency audit restricted to Lean's three standard axioms. + +`Ix.Kernel.Verify.Consistency` proves the following direct connections: + +- Production universe equality and ordering agree with model evaluation, + under the existing finite address-faithfulness and arithmetic bounds. +- A structural reader maps kernel expressions to model syntax, resolves + addresses to explicit store references, preserves projections and natural + literals, and substitutes let values. Free variables, unresolved addresses, + and string literals are outside the current reader's domain. +- Hash equality and intern-table reuse preserve that reading under their + stated address/key collision assumptions. Metadata cannot change it. +- `inferUncached_sort_sound` interprets an actual successful execution of the + production sort-inference branch. It proves the model typing postcondition + for the returned type, including intern-table reuse. +- `ModelTyping.no_false` rules out a closed model-typed kernel expression at + primitive False when its environment has been admitted by the certified + interface and the set-theory assumption has an instance. + +The last theorem assumes semantic typing; it does not assume or prove that +arbitrary checker success supplies it. A complete checker consistency theorem +still requires the remaining inference/conversion cases, cache invariants, +address-to-store resolution, and declaration admission to establish that +postcondition for `checkEnvAnon`. The existing named-calculus proofs are retained +to support this refinement, rather than being treated as a set-model proof. + +## Trust checks + +The audits traverse checked declaration types and bodies, including inductive +constructors. They compare exact axiom sets and record direct origins of +`sorryAx`; they do not rely on cached imported axiom summaries. During the +migration, full traversal of the original sources exposed two wrapper reports +that listed 3 axioms but depended on 30. Their boundaries now include the +existing implementation assumptions and unfinished metatheory. All 441 retained +named-specification assertions use the original full dependency graphs as their +migration baseline. + +The same traversal covers 2,034 kernel manifest roots. Thirteen entries omitted +logical or native dependencies through constructor fields; their corrected +boundaries were checked against freshly compiled pre-migration sources. Direct +dependency lookups are cached within a fixed environment, while each root's +reachable declarations, axioms, and proof-hole origins are computed separately. + +`Ix.Kernel.Frontier.Pending` quarantines the remaining explicit metatheory +axioms. Completed roots cannot depend on that namespace. Named-specification +proof holes and implementation bridge axioms are tracked separately from +Lean's logical axioms and generated native proofs. No direct consistency root +permits a proof hole or a metatheory/implementation bridge axiom. Both the +context hash and content-address hash now use kernel-checked proofs of their +32-byte output bounds on both platform sizes. Their former generated native +assumptions have been removed from the affected exact audits. + +Run the complete local kernel-certification gate: + +```sh +lake run check-kernel --with-model +``` + +It combines the following checks. Omit `--with-model` to skip the separate +Mathlib package. Ordinary PR CI covers the root checks in its build, theory, +and test jobs. The model has a separate workflow triggered by changes to the +package, interface, or configuration; the merge queue adds the expensive +parity corpus. + +```sh +lake build IxKernelVerify IxCompileVerify +lake build --wfail IxKernelConsistency +lake run check-theory +lake run check-certified +lake test --wfail -- tc-unit +lake -d Models/SetTheory build --wfail +``` + +The consistency target checks 18 exact theorem boundaries. Its remaining +native assumptions are explicitly named proofs reached through production +smart-constructor code; the model's no-False theorem itself uses only +`propext`, `Classical.choice`, and `Quot.sound`, with set theory as a hypothesis. + +## Certified host adapters + +The certified source and claim adapters are maintained under +`Ix.Certified`, with explicit certified entry points in `Ix.Kernel`. +`ClaimCommand.run_meaning` connects successful source validation to the exact +versioned envelope and its semantic meaning. A closed logical receipt +constructs its model and excludes a checked subject of the profile's false +type under the set-theory hypothesis. + +Run `lake run check-certified` for the exact 86-root foundation audit and +native/CLI regressions against the locally preserved adapter evidence. The audit +permits only the three standard Lean axioms in these roots and inventories +the separate BLAKE3 and native execution boundaries. See +[the command interface, theorem contracts and evidence](certified-checking.md). + +These host results do not cover the full production inference dispatcher or +prove that an Aiur public verifier executes the host validator. The VM pilot +remains in the frozen archive for a later change; its build target and execution +tests are outside the host gate. + +## Review entry points + +| Area | Entry point | +| --- | --- | +| Certified checker contracts | [`Ix/Kernel/Certified.lean`](../Ix/Kernel/Certified.lean), [`CertifiedClaims.lean`](../Ix/Kernel/CertifiedClaims.lean) | +| Direct production refinement and its audit | [`Ix/Kernel/Verify/Consistency.lean`](../Ix/Kernel/Verify/Consistency.lean) | +| Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | +| Host commands, receipts, and frozen regression evidence | [Certified checking guide](certified-checking.md) | +| Concrete set-theory instance | [Separate model package](../Models/SetTheory/README.md) | + +## Change scope + +The kernel, theory and certified host scaffolding is extracted from +`jcb/monorepo` at `7b06b754` in a single change. Existing callers move from +`Ix.Tc` to `Ix.Kernel`. Existing `Ix.Compile.Verify` proofs receive the +necessary named-specification imports and exact hash-axiom audit updates. +The new compiler development, circuit changes and certificate VM pilot +are deferred. The external `lean4lean` dependency and its benchmark/test +targets are removed; all verification dependencies are local to Ix. diff --git a/docs/tc-context-digest-collision-boundary.md b/docs/tc-context-digest-collision-boundary.md index f7b4fb1a4..63e539edc 100644 --- a/docs/tc-context-digest-collision-boundary.md +++ b/docs/tc-context-digest-collision-boundary.md @@ -1,4 +1,4 @@ -# Ix.Tc context-digest collision boundary +# Ix.Kernel context-digest collision boundary Snapshot: 2026-07-31. This note records a proof boundary for the K1/K2 cache soundness argument. diff --git a/docs/tc-k0-backedge-audit.md b/docs/tc-k0-backedge-audit.md index 243e79e71..b9cc4aef4 100644 --- a/docs/tc-k0-backedge-audit.md +++ b/docs/tc-k0-backedge-audit.md @@ -1,4 +1,4 @@ -# Ix.Tc K0 recursion and back-edge audit +# Ix.Kernel K0 recursion and back-edge audit Snapshot: 2026-07-27. This is the named K0 tick/measure artifact required by the formal-verification plan. Its production scope is the kernel call graph @@ -67,7 +67,7 @@ The direct-read audit is: ```text rg -n '\(← read\)\.(whnf|whnfCore|whnfMode|whnfCoreFlags|infer|isDefEq)' \ - Ix/Tc --glob '*.lean' + Ix/Kernel --glob '*.lean' ``` The live edges have the following roles: @@ -116,11 +116,11 @@ adversarial inputs that could previously diverge in an unbounded loop now return `.maxRecDepth` at the documented cap. Valid-corpus verdict and headroom parity is the A5 closure gate. Rust and Aiur are deliberately unchanged in K0; any corresponding hardening is a later transport obligation -after the Ix.Tc theorem interface is stable. +after the Ix.Kernel theorem interface is stable. ## Proof and regression surface -`Ix.Tc.Verify.Totalization` exposes, and the completed trust manifest audits: +`Ix.Kernel.Verify.Totalization` exposes, and the completed trust manifest audits: - zero/successor equations for all six `methodsN` fields; - unchanged-error-state equations for all six `methodsOut` fields; @@ -142,11 +142,11 @@ The reproducible source audits are: ```text rg -n '^\s*partial def' \ - Ix/Tc/{Whnf,Infer,DefEq,Inductive,Check}.lean + Ix/Kernel/{Whnf,Infer,DefEq,Inductive,Check}.lean rg -n '^\s*(while|repeat)\b' \ - Ix/Tc/{Whnf,Infer,DefEq,Inductive,Check}.lean + Ix/Kernel/{Whnf,Infer,DefEq,Inductive,Check}.lean rg -n 'TcM\.tick' \ - Ix/Tc/{Whnf,Infer,DefEq,Inductive,Check}.lean + Ix/Kernel/{Whnf,Infer,DefEq,Inductive,Check}.lean ``` The first two must return no matches; the last must return exactly the WHNF @@ -168,7 +168,7 @@ The 2026-07-27 K0 closure run passed all of the following: - exact four-statement sorry-frontier check; - completed (295 roots) and statement (4 roots) trust audits; -- `lake build IxTcVerify` and the default `lake build`; +- `lake build IxKernelVerify` and the default `lake build`; - strict `tc-unit` with warnings treated as failures; - pinned Init/Std stress constants and accelerated-versus-pure differential; - Init-scale anon verdict parity; @@ -176,5 +176,5 @@ The 2026-07-27 K0 closure run passed all of the following: - Lean4Lean replay and tutorial suites. No production source in the Rust kernel or Aiur IxVM was changed. Their -acceptance simulation/refinement work remains downstream of the Ix.Tc +acceptance simulation/refinement work remains downstream of the Ix.Kernel soundness theorem. diff --git a/docs/theory.md b/docs/theory.md new file mode 100644 index 000000000..be2cd142e --- /dev/null +++ b/docs/theory.md @@ -0,0 +1,68 @@ +# Consistency model + +`Ix.Theory` contains Ix's set-theoretic consistency model, its certified acceptance +interface, and certificate construction. Build it with `lake build IxTheory`; +run the selected regression tests, provenance check, and exact foundation +audit with `lake run check-theory`. + +`Ix.Theory.Certified.accepted_has_model` constructs a model for accepted +declarations. `accepted_proof_sound` and `no_proof_of_False` give the corresponding semantic +and consistency results. These theorems explicitly assume a type `V` equipped +with `Ix.Theory.Model.SetTheory V`: set operations and laws together with a +countable tower of Grothendieck universes. The separate +[`Models/SetTheory`](../Models/SetTheory/README.md) package constructs this +structure on Mathlib's `ZFSet` from a strictly increasing countable sequence +of strongly inaccessible cardinals. Its `carneiro_implies_ix` theorem retains +that large-cardinal hypothesis explicitly and passes an axiom guard for +`propext`, `Classical.choice`, and `Quot.sound`. User-supplied logical axioms and +open frontiers retain their stated model hypotheses. + +These are relative consistency results for the certified interface. The +[kernel verification](kernel-verification.md) now shares its universe syntax +and proves direct model results for universe comparison, structural expression +reading, interning, and the production sort-inference branch. The certified +host adapters connect authenticated serialized Ixon to model admission. +A complete bridge from arbitrary `Ix.Kernel.checkEnvAnon` success or +compiler/backend execution remains unfinished. + +`Ix.Theory.Named` retains the local name-indexed specification and proof +support needed by the existing kernel and compiler verification. It shares +`Ix.Theory.VLevel`; its expression calculus remains separate from the store +references and annotated semantics of the set model. The `IxTheory` build and +foundation import graph exclude this auxiliary development. + +The foundation audit freezes 145 named roots, their types and dependencies, +the fields of the set-theory assumption, and the compiler-generated recursion +workers. It rejects proof holes and unapproved axioms, native implementations, +and mathematical imports from the checker, compiler, test harnesses, or older +metatheory. Its exact report is `Tests/Theory/certified-foundation.txt`. +The axiom checks read checked declaration types, bodies, and constructor fields +directly, including the full graph of every declaration in the mathematical +import boundary. + +## Provenance and scope + +The port contains 120 selected source modules and a certified umbrella, from +the Lean4Ix working tree on 2026-09-11, +based on commit `ab42e79e2a4e2615a3ca6ef983d510f374057a38`. The new model included +uncommitted files, so that revision alone is insufficient to identify the +input. `Tests/Theory/ImportManifest.lean` records each selected source path and +SHA-256. Namespace changes move `Lean4Ix` to `Ix.Theory` and flatten the former +`Lean4Ix.Theory` syntax helpers. + +The import includes the exact dependencies of the certified interface and +certificate builders. The old full theory umbrella, generators, conjecture +frontier, and harness are excluded. Matching model tests live in `Tests/Theory`. +Tests that formerly invoked the old generators use frozen constructor and +recursor data extracted from their upstream compiled test fixtures. + +The model's 20 con-leche foundation files derive from revision +`86cd20a65660d757cedc81561a44579099b565d0`. Their upstream and intermediate hashes, +ported content hashes, and notices are retained and checked. Imported license +and notice texts are under `Ix/Theory`; `PORTING.md` explains their scope. + +On recompilation in the monorepo, two roots—`Modeled.CheckedCompanions.fixed_old` +and `Modeled.assignment_agrees`—acquire `Classical.choice` and `Quot.sound` in +addition to `propext`, all already permitted baseline axioms. The reviewed +report also accounts for namespace qualification and module ordering. All root +types, constant counts, and module sets otherwise match the source report. diff --git a/flake.nix b/flake.nix index 84079f7f7..07981c6a9 100644 --- a/flake.nix +++ b/flake.nix @@ -194,22 +194,6 @@ }; lakeDeps = lake2nix.buildDeps { src = leanSrc; - depOverride = { - # lean4-nix guesses a dep's library target by capitalizing the - # package name ("lean4lean" -> "Lean4lean"), but this package's - # library is `Lean4Lean`. Build the stock default targets - # (Lean4Lean, the lean4lean exe, Theory, Verify, Tests) plus the - # shared/static facets so consumers linking exes find the - # module `.o` files in the read-only store path. - lean4lean = { - buildPhase = '' - runHook preBuild - lake build - lake build Lean4Lean:shared Lean4Lean:static - runHook postBuild - ''; - }; - }; depOverrideDeriv = { Blake3 = blake3-lean.packages.${system}.rust; }; diff --git a/lake-manifest.json b/lake-manifest.json index 1f571e9bb..d10016758 100644 --- a/lake-manifest.json +++ b/lake-manifest.json @@ -1,17 +1,7 @@ {"version": "1.2.0", "packagesDir": ".lake/packages", "packages": - [{"url": "https://github.com/argumentcomputer/lean4ix", - "type": "git", - "subDir": null, - "scope": "", - "rev": "a4188d7c2979378d85c6bb41fdd96c3a48a71371", - "name": "lean4lean", - "manifestFile": "lake-manifest.json", - "inputRev": "a4188d7c2979378d85c6bb41fdd96c3a48a71371", - "inherited": false, - "configFile": "lakefile.toml"}, - {"url": "https://github.com/leanprover-community/batteries", + [{"url": "https://github.com/leanprover-community/batteries", "type": "git", "subDir": null, "scope": "", diff --git a/lakefile.lean b/lakefile.lean index 98fe391ce..5ac660b42 100644 --- a/lakefile.lean +++ b/lakefile.lean @@ -10,7 +10,7 @@ require LSpec from git /- Blake3 precompiles its libraries, so Lake loads their shared objects -- which bundle the C and Rust FFI objects -- into any process elaborating a module that imports them. That is what supplies the BLAKE3 backend to Lean's native evaluator -for the `native_decide` proofs in `IxTcVerify`, so this pin must stay at or after +for the `native_decide` proofs in `IxKernelVerify`, so this pin must stay at or after the revision that turned precompilation on. Before it, Blake3 exposed a `blake3_rs_shared` cdylib that `ix_native_decide_dynlib` had to fetch and link; that target no longer exists. -/ @@ -23,20 +23,6 @@ require Cli from git require batteries from git "https://github.com/leanprover-community/batteries" @ "v4.33.0" -/- Reference Lean4-in-Lean4 theory and checker. `IxTcVerify` imports its -Theory/Verify specification surface, while `bench-lean4lean` and the ignored -`lean4lean` test runner exercise the implementation. The default `ix` target -still does not build this dependency. Pin `argumentcomputer/lean4ix` exactly -- -the Argument development line, a standalone repository rather than a GitHub -fork of digama0/lean4lean: this revision carries the upstream v4.32/v4.33 -kernel hardening — including the `checkNoMVarNoFVar` check on an opaque's -value (leanprover/lean4#14498), which the replay path in -`Benchmarks/Lean4Lean.lean` reaches — on top of that line's certified -inductive-environment and projection development, and tracks Lean v4.33.1 as -this package does. -/ -require lean4lean from git - "https://github.com/argumentcomputer/lean4ix" @ "a4188d7c2979378d85c6bb41fdd96c3a48a71371" - /-! ## FFI The Rust static libraries use `target` + `moreLinkObjs` instead of `extern_lib` because different Lean executables need different Cargo features: @@ -194,18 +180,6 @@ lean_exe «bench-aggregate-policy» where -- symbols are then resolved from ix_ffi and not pulled twice. moreLinkObjs := #[ix_rs] -/- The lean4lean replay machinery as an importable lib: the -`bench-lean4lean` exe root and the ignored `lean4lean` test runner both -import `Benchmarks.Lean4Lean`, and modules under `Benchmarks/` belong to -no other lib target, so without this Lake cannot schedule the module from -the Tests import graph. -/ -lean_lib Lean4LeanBench where - globs := #[.one `Benchmarks.Lean4Lean] - -lean_exe «bench-lean4lean» where - root := `Benchmarks.Lean4LeanMain - supportInterpreter := true - lean_exe «bench-compile-init» where root := `Benchmarks.CompileInit @@ -228,9 +202,13 @@ lean_exe truthmines where end Benchmarks -section IxTcVerify +lean_lib IxTheoryNamed where + roots := #[] + globs := #[.submodules `Ix.Theory.Named] -/-- Loadable FFI for Lean's native evaluator while `IxTcVerify` is elaborated. +section IxKernelVerify + +/-- Loadable FFI for Lean's native evaluator while `IxKernelVerify` is elaborated. `native_decide` runs compiled Lean before any executable is linked, so for each opaque `@[extern]` it reaches, both symbol layers must be loadable up front: @@ -255,31 +233,37 @@ target ix_native_decide_dynlib pkg : Dynlib := do (pkg.buildDir / nameToSharedLib "ix_native_decide") (boxedObjs.push ixCdylib) #[] -/- Formal verification of `Ix.Tc` against the lean4lean `Theory` spec. +/- Formal verification of `Ix.Kernel` against the internal named specification. Non-default: `lake build ix` never touches it, and `build-all` (the lint driver) skips it by name because its -pinned Lean4Lean dependencies still emit named `sorry` warnings — `lake lint +internal named-specification proofs still emit named `sorry` warnings — `lake lint -- --wfail` would otherwise fail even though the Ix verification source has no local `sorry` tokens. Required CI builds it separately without `--wfail`, audits the exact local sorry frontier, and checks exact per-root transitive axiom plus direct-`sorryAx`-origin manifests. Dev loop: -`lake build IxTcVerify`; focused trust audit: -`lake build Ix.Tc.Verify.Audit.Completed Ix.Tc.Verify.Audit.Conditional -Ix.Tc.Verify.Audit.Statements`. -/ -lean_lib IxTcVerify where - globs := #[.submodules `Ix.Tc.Verify] +`lake build IxKernelVerify`; focused trust audit: +`lake build Ix.Kernel.Verify.Audit.Completed Ix.Kernel.Verify.Audit.Conditional +Ix.Kernel.Verify.Audit.Statements`. -/ +lean_lib IxKernelVerify where + globs := #[.submodules `Ix.Kernel.Verify] -- `supportInterpreter` is a `lean_exe` option and takes effect only when -- that executable is linked, after its modules have been elaborated. -- These native-decide proofs need the boxed FFI symbols while the library -- modules are being elaborated, so they must be supplied as a dynlib. dynlibs := #[ix_native_decide_dynlib] -end IxTcVerify +end IxKernelVerify + +/- Direct refinement of production operations into the set model, with its +own exact axiom audit. This is distinct from whole-checker soundness. -/ +lean_lib IxKernelConsistency where + roots := #[`Ix.Kernel.Verify.Consistency] + globs := #[.andSubmodules `Ix.Kernel.Verify.Consistency] section IxCompileVerify /- Formal verification of the Lean-to-Ixon compiler against the same -Lean4Lean Theory endpoint as `IxTcVerify`. Kept as a separate non-default +internal named-specification endpoint as `IxKernelVerify`. Kept as a separate non-default library so compiler proofs cannot accidentally inherit checker acceptance theorems as their specification. -/ lean_lib IxCompileVerify where @@ -344,10 +328,11 @@ script "build-all" (args) := do let pkg ← getRootPackage let libNames := pkg.configTargets LeanLib.configKind |>.map (·.name.toString) let exeNames := pkg.configTargets LeanExe.configKind |>.map (·.name.toString) - -- IxTcVerify is the WIP proofs lib: sorry-bearing by design while the - -- verification frontier is open, so it must not run under `--wfail`. - -- Required CI builds it separately and audits the exact frontier. - let allNames := (libNames ++ exeNames |>.toList).filter (· != "IxTcVerify") + -- The named specification and its implementation proofs retain an audited + -- frontier. CI builds them separately without `--wfail`. The set model, + -- direct consistency roots, and certified adapters are checked strictly. + let allNames := (libNames ++ exeNames |>.toList).filter fun name => + name != "IxKernelVerify" && name != "IxCompileVerify" && name != "IxTheoryNamed" for name in allNames do IO.println s!"Building: {name}" let child ← IO.Process.spawn { @@ -358,3 +343,144 @@ script "build-all" (args) := do return 0 end Scripts + +section Theory + +lean_lib IxTheory where + roots := #[`Ix.Theory] + -- Keep the set-model foundation independent of named checker proof support. + globs := #[.one `Ix.Theory, .one `Ix.Theory.Certified, + .one `Ix.Theory.Const, .one `Ix.Theory.Expr, .one `Ix.Theory.Quot, + .one `Ix.Theory.Ref, .one `Ix.Theory.Rename, .one `Ix.Theory.Store, + .one `Ix.Theory.VLevel, .submodules `Ix.Theory.Certificate, + .submodules `Ix.Theory.Certified, .submodules `Ix.Theory.Inductive, + .submodules `Ix.Theory.Model, .submodules `Ix.Theory.Std] + +lean_lib IxTheoryCertified where + roots := #[`Ix.Theory.Certified] + +lean_lib IxTheoryTests where + roots := #[`Tests.Theory] + +lean_exe «theory-provenance» where + root := `Tests.Theory.Provenance + +end Theory + +section Certified + +lean_lib IxCertified where + roots := #[`Ix.Certified] + moreLinkObjs := #[ix_rs] + +lean_lib IxCertifiedAudit where + roots := #[`Ix.Certified.AuditAll] + +lean_exe «certified-cli-tests» where + root := `Tests.Certified.CLI + supportInterpreter := true + +lean_exe «certified-adapter-tests» where + root := `Tests.Certified.Check + supportInterpreter := true + +lean_exe «certified-check» where + root := `Ix.Certified.Main + supportInterpreter := true + moreLinkObjs := #[ix_rs] + +lean_exe «certified-claim-check» where + root := `Ix.Certified.ClaimMain + supportInterpreter := true + moreLinkObjs := #[ix_rs] + +lean_exe «certified-feature-tests» where + root := `Tests.Certified.Features + supportInterpreter := true + moreLinkObjs := #[ix_rs] + +lean_exe «certified-ordinary-tests» where + root := `Tests.Certified.Ordinary + supportInterpreter := true + moreLinkObjs := #[ix_rs] + +lean_exe «certified-source-tests» where + root := `Tests.Certified.SourceMain + supportInterpreter := true + moreLinkObjs := #[ix_rs] + +lean_exe «certified-fidelity-tests» where + root := `Tests.Certified.FidelityMain + supportInterpreter := true + moreLinkObjs := #[ix_rs] + +lean_exe «certified-claim-tests» where + root := `Tests.Certified.ClaimsMain + supportInterpreter := true + moreLinkObjs := #[ix_rs] + +lean_exe «certified-modeled-tests» where + root := `Tests.Certified.ModeledMain + supportInterpreter := true + moreLinkObjs := #[ix_rs] + +end Certified + +namespace KernelChecks + +private def run (command : String) (args : Array String := #[]) : IO Unit := do + let child ← IO.Process.spawn { cmd := command, args, stdout := .inherit, stderr := .inherit } + let code ← child.wait + unless code == 0 do throw (IO.userError s!"{command} failed ({code})") + +private def checkReport (module expectedPath : String) : IO Unit := do + let report ← IO.Process.output { + cmd := "lake", args := #["env", "lean", "-DwarningAsError=true", module] } + unless report.exitCode == 0 do throw (IO.userError s!"{report.stdout}{report.stderr}") + unless report.stdout == (← IO.FS.readFile expectedPath) do + IO.FS.withTempFile fun handle path => do + handle.putStr report.stdout + handle.flush + let child ← IO.Process.spawn { + cmd := "diff", args := #["-u", expectedPath, path.toString] + stdout := .inherit, stderr := .inherit } + let _ ← child.wait + throw (IO.userError s!"foundation report differs from {expectedPath}") + +end KernelChecks + +open KernelChecks + +/-- Check the set model, its provenance, and its exact foundation report. -/ +script "check-theory" := do + run "lake" #["build", "--wfail", "IxTheory", "IxTheoryTests", "theory-provenance"] + run ".lake/build/bin/theory-provenance" + checkReport "Tests/Theory/Audit/Certified.lean" "Tests/Theory/certified-foundation.txt" + IO.println "Theory checks passed: exact root types, axioms, dependencies, and runtime inventory." + return 0 + +/-- Check host certification against exact audits and frozen source/claim evidence. -/ +script "check-certified" := do + run "lake" #["build", "--wfail", "IxCertified", "IxCertifiedAudit", + "certified-check", "certified-claim-check", "certified-feature-tests", + "certified-ordinary-tests", "certified-source-tests", "certified-fidelity-tests", + "certified-claim-tests", "certified-modeled-tests", "certified-cli-tests", + "certified-adapter-tests"] + checkReport "Ix/Certified/AuditAll.lean" "Tests/Certified/foundation.txt" + run ".lake/build/bin/certified-adapter-tests" + return 0 + +/-- Run the kernel implementation, consistency, foundation, and host checks. -/ +script "check-kernel" (args) := do + unless args.isEmpty || args == ["--with-model"] do + IO.eprintln "usage: lake run check-kernel [--with-model]" + return 2 + run "lake" #["build", "IxKernelVerify", "IxCompileVerify"] + run "lake" #["build", "--wfail", "IxKernelConsistency"] + run "lake" #["run", "check-theory"] + run "lake" #["run", "check-certified"] + run "lake" #["test", "--wfail", "--", "tc-unit"] + if args == ["--with-model"] then + run "lake" #["-d", "Models/SetTheory", "build", "--wfail"] + IO.println "Kernel certification checks passed." + return 0 From 76dc310214d26800e290c267b0f129e4ca59ef08 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 06:53:42 -0400 Subject: [PATCH 02/63] Prove relative consistency for an atomic checkEnvAnon fragment Connect all-success production checkEnvAnon results to model extension for monomorphic standalone aliases and closed universe terms. The proof follows the actual lazy lookup, standalone routing, reset, validation, inference, and conversion calls. Its operational witnesses require uncached value inference and the initial hash-equality conversion path with explicit representation faithfulness; they do not assume a checker-soundness callback or supplied typing proof. Start the model interface with exactly the source axioms, then add fresh definitions in dependency order while retaining their actual serial work positions. Every model of the axiom interface extends without changing any axiom interpretation. Source representation includes the checked type, universe arity, and definition body. The no-False corollary preserves an initial empty interpretation, and a separate theorem excludes fresh self aliases. All result rows must succeed; the public outer ok is insufficient. The full checker and independent serialized-Ixon reader refinement remain outside this fragment. Runtime checker behavior is unchanged. Validation: - lake build --wfail IxKernelConsistency: 36 exact full-dependency audits, no proof holes or checker/certificate soundness shortcuts; four existing generated 32-byte length proofs are enumerated explicitly. - lake test --wfail -- tc-unit: 403 checks, including eight new production regressions for aliases, sorts, cache clearing, and failed result rows. - git diff --check --- Ix/Kernel/Verify/Consistency.lean | 8 +- Ix/Kernel/Verify/Consistency/Atomic.lean | 217 +++++++++ Ix/Kernel/Verify/Consistency/Audit.lean | 42 +- Ix/Kernel/Verify/Consistency/Environment.lean | 455 ++++++++++++++++++ Ix/Kernel/Verify/Consistency/Production.lean | 246 ++++++++++ README.md | 8 +- Tests/Ix/Kernel/Consistency.lean | 91 ++++ Tests/Main.lean | 2 + docs/kernel-verification.md | 89 +++- docs/theory.md | 6 + lakefile.lean | 4 +- 11 files changed, 1154 insertions(+), 14 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/Atomic.lean create mode 100644 Ix/Kernel/Verify/Consistency/Environment.lean create mode 100644 Ix/Kernel/Verify/Consistency/Production.lean create mode 100644 Tests/Ix/Kernel/Consistency.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index c00dfe950..13dc8657e 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -7,6 +7,9 @@ import Ix.Kernel.Verify.Consistency.Level import Ix.Kernel.Verify.Consistency.Expr import Ix.Kernel.Verify.Consistency.Judgment import Ix.Kernel.Verify.Consistency.Infer +import Ix.Kernel.Verify.Consistency.Atomic +import Ix.Kernel.Verify.Consistency.Production +import Ix.Kernel.Verify.Consistency.Environment import Ix.Kernel.Verify.Consistency.Audit /-! @@ -14,6 +17,7 @@ import Ix.Kernel.Verify.Consistency.Audit This library connects production kernel operations to `Ix.Theory`. Each transport keeps its representation, arithmetic, and dependency assumptions -explicit. The component theorems are distinct from a full soundness theorem -for `checkEnvAnon`. +explicit. A production `checkEnvAnon` fragment now preserves models of its +axiom set for monomorphic aliases and closed sorts under the stated execution +resources. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Atomic.lean b/Ix/Kernel/Verify/Consistency/Atomic.lean new file mode 100644 index 000000000..12565de84 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Atomic.lean @@ -0,0 +1,217 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Infer +import Ix.Theory.Model.Extension + +/-! +# Atomic production inference + +The first declaration fragment admits closed sorts and monomorphic references. +References use the type of an already admitted interface entry. The premises +below describe concrete cache misses, lookup results, and finite interning +support; none assumes a typing judgment or the soundness of a checker method. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +private theorem runTcBind {α β : Type} (x : TcM .anon α) (k : α → TcM .anon β) + (state : TcState .anon) : + EStateM.bind x k state = match x state with + | .ok value after => k value after + | .error err after => .error err after := by + show EStateM.bind x k state = _ + unfold EStateM.bind + cases x state <;> rfl + +/-- The two inference-cache partitions miss at the actual key computed by +production. Both misses are required, so this applies in either policy mode. -/ +structure UncachedInference (before : TcState .anon) (term : KExpr .anon) where + key : Address × Address + keyed : TcState .anon + keyRun : TcM.inferKey term before = .ok key keyed + fullMiss : keyed.env.inferCache[key]? = none + onlyMiss : keyed.env.inferOnlyCache[key]? = none + +/-- Strip only key lookup and the final cache write from an actual successful +inference. The recursive method table and returned type are unchanged. -/ +theorem infer_uncached_success {term type : KExpr .anon} + {methods : Methods .anon} {before after : TcState .anon} + (miss : UncachedInference before term) + (accepted : RecM.infer term methods before = .ok type after) : + ∃ inferredState, RecM.inferUncached RecM.inferCall before.inferOnly term + methods miss.keyed = .ok type inferredState := by + change (RecM.infer term).run methods before = .ok type after at accepted + unfold RecM.infer RecM.inferWith at accepted + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted + change EStateM.bind (TcM.inferKey term) _ before = _ at accepted + rw [runTcBind, miss.keyRun] at accepted + change EStateM.bind (get : TcM .anon (TcState .anon)) _ miss.keyed = _ at accepted + rw [runTcBind, show (get : TcM .anon (TcState .anon)) miss.keyed = + .ok miss.keyed miss.keyed from rfl] at accepted + simp only at accepted + rw [miss.fullMiss] at accepted + cases policy : before.inferOnly with + | false => + simp only [policy, Bool.false_eq_true, if_false] at accepted + change EStateM.bind (RecM.inferUncached RecM.inferCall false term methods) + _ miss.keyed = _ at accepted + cases run : RecM.inferUncached RecM.inferCall false term methods miss.keyed with + | error err failed => rw [runTcBind, run] at accepted; contradiction + | ok ty state => + rw [runTcBind, run] at accepted + change EStateM.Result.ok ty { state with env := { state.env with + inferCache := state.env.inferCache.insert miss.key ty } } = .ok type after at accepted + cases accepted + exact ⟨state, rfl⟩ + | true => + simp only [policy, if_true] at accepted + simp only [ReaderT.run_bind] at accepted + change EStateM.bind (get : TcM .anon (TcState .anon)) _ miss.keyed = _ at accepted + rw [runTcBind, show (get : TcM .anon (TcState .anon)) miss.keyed = + .ok miss.keyed miss.keyed from rfl] at accepted + simp only at accepted + rw [miss.onlyMiss] at accepted + change EStateM.bind (RecM.inferUncached RecM.inferCall true term methods) + _ miss.keyed = _ at accepted + cases run : RecM.inferUncached RecM.inferCall true term methods miss.keyed with + | error err failed => rw [runTcBind, run] at accepted; contradiction + | ok ty state => + rw [runTcBind, run] at accepted + change EStateM.Result.ok ty { state with env := { state.env with + inferOnlyCache := state.env.inferOnlyCache.insert miss.key ty } } = .ok type after at accepted + cases accepted + exact ⟨state, rfl⟩ + +/-- Supported syntax plus the concrete resources needed by its uncached +production branch. A constant's semantic type comes from an existing entry, +while `lookup` checks agreement with the actual lazy-loaded declaration. +The monomorphic type is explicitly stable under the empty level substitution. -/ +inductive AtomicInferenceSupport {β : Type u} + (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) : + KExpr .anon → TcState .anon → AExpr β → AExpr β → Prop + | sort {level : KUniv .anon} {info : ExprInfo .anon} {before : TcState .anon} + (closed : (readLevel level).WF 0) + (coherent : before.env.intern.WF) + (faithful : KExpr.KeyCollisionFree fun e => + before.env.intern.ExprSupport e ∨ e = KExpr.mkSort (KUniv.mkSucc level)) : + AtomicInferenceSupport resolve entries (.sort level info) before + (.sort (readLevel level)) (.sort (.succ (readLevel level))) + | const {id : KId .anon} {info : ExprInfo .anon} {before : TcState .anon} + {ref : ConstRef β} {entry : ConstantEntry β} + (resolved : resolve id.addr = some ref) + (found : entries ref = some entry) + (monomorphic : entry.universes = 0) + (stable : entry.type.instL [] = entry.type) + (lookup : ∀ concrete loaded, + TcM.getConst id before = .ok concrete loaded → + readExpr? resolve concrete.ty = some entry.type.erase) : + AtomicInferenceSupport resolve entries (.const id #[] info) before + (.const ref []) entry.type + +namespace AtomicInferenceSupport + +/-- This fragment's typing rule is proved from the sort rule or an existing +constant interface, independently of any production success assumption. -/ +theorem typing {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {term : KExpr .anon} {before : TcState .anon} + {body type : AExpr β} (support : AtomicInferenceSupport resolve entries term before body type) : + TypingClaim.{u,v} entries [] body type := by + cases support with + | sort => exact TypingClaim.sort _ + | const resolved found monomorphic stable lookup => + simpa only [stable] using + (TypingClaim.const (ls := []) found (by simpa using monomorphic.symm)) + +theorem reads {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {term : KExpr .anon} {before : TcState .anon} + {body type : AExpr β} (support : AtomicInferenceSupport resolve entries term before body type) : + readExpr? resolve term = some body.erase := by + cases support with + | sort => rfl + | const resolved found monomorphic stable lookup => + simp [readExpr?, resolved, AExpr.erase] + +/-- Exact output refinement for the production syntax dispatcher. -/ +theorem output {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {term inferred : KExpr .anon} + {before after : TcState .anon} {body type : AExpr β} + (support : AtomicInferenceSupport resolve entries term before body type) + {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} + {inferOnly : Bool} {methods : Methods .anon} + (accepted : RecM.inferUncached inferRec inferOnly term methods before = + .ok inferred after) : + readExpr? resolve inferred = some type.erase := by + cases support with + | sort closed coherent faithful => + change EStateM.Result.ok + (before.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc _))).1 + _ = .ok inferred after at accepted + cases accepted + rw [internExpr_readExpr? coherent faithful] + simp only [readExpr?_mkSort, readLevel_mkSucc, AExpr.erase] + | @const id info before ref entry resolved found monomorphic stable lookup => + change (RecM.inferUncached inferRec inferOnly (.const id #[] info)).run + methods before = .ok inferred after at accepted + unfold RecM.inferUncached at accepted + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted + change EStateM.bind (TcM.getConst id) _ before = _ at accepted + cases got : TcM.getConst id before with + | error err failed => rw [runTcBind, got] at accepted; contradiction + | ok concrete loaded => + rw [runTcBind, got] at accepted + simp only at accepted + split at accepted + · contradiction + · change EStateM.Result.ok concrete.ty loaded = .ok inferred after at accepted + cases accepted + exact lookup concrete _ got + +theorem scopeAndReferences {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {term : KExpr .anon} {before : TcState .anon} + {body type : AExpr β} (support : AtomicInferenceSupport resolve entries term before body type) + (wellFormed : entries.WF) : + body.Scope 0 0 ∧ type.Scope 0 0 ∧ + body.ReferencesIn entries ∧ type.ReferencesIn entries := by + cases support with + | sort closed coherent faithful => + exact ⟨closed, closed, by simp [AExpr.ReferencesIn, AExpr.references], + by simp [AExpr.ReferencesIn, AExpr.references]⟩ + | const resolved found monomorphic stable lookup => + refine ⟨by simp [AExpr.Scope], ?_, ?_, wellFormed.typeReferences _ _ found⟩ + · simpa only [monomorphic] using wellFormed.typeScope _ _ found + · intro ref href + simp only [AExpr.references, List.mem_singleton] at href + subst ref + simp only [found, Option.isSome_some] + +end AtomicInferenceSupport + +/-- Resources at the actual inference entry and its computed cache key. -/ +structure AtomicInference {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (before : TcState .anon) (term : KExpr .anon) + (body type : AExpr β) where + misses : UncachedInference before term + support : AtomicInferenceSupport resolve entries term misses.keyed body type + +/-- A successful public inference in this fragment returns the semantic type +of the exact source tree. The inference implementation supplies the output +agreement; callers do not supply a typing or method-soundness premise. -/ +theorem AtomicInference.sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {before after : TcState .anon} + {term inferred : KExpr .anon} {body type : AExpr β} {methods : Methods .anon} + (fragment : AtomicInference resolve entries before term body type) + (accepted : RecM.infer term methods before = .ok inferred after) : + readExpr? resolve term = some body.erase ∧ + readExpr? resolve inferred = some type.erase ∧ + TypingClaim.{u,v} entries [] body type := by + obtain ⟨state, run⟩ := infer_uncached_success fragment.misses accepted + exact ⟨fragment.support.reads, fragment.support.output run, fragment.support.typing⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 9fc86756d..7d49bf4a3 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -4,6 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.Infer +import Ix.Kernel.Verify.Consistency.Environment import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -21,6 +22,39 @@ private def levelNative : Lean.Name := private def expressionNative : Lean.Name := nativeAxiom `Ix.Kernel.Expr `Ix.Kernel.KExpr.mkVar._native.native_decide.ax_1 +/-- The public driver reaches these additional generated output-length +proofs through the full production method table, including inactive branches. +No new native proof is introduced by the fragment verification. -/ +private def productionNative : Array Lean.Name := #[ + expressionNative, levelNative, + nativeAxiom `Ix.Environment `Ix.Name.mkStr._native.native_decide.ax_1, + nativeAxiom `Ix.Kernel.Inductive `Ix.Kernel.RecM.canonicalAuxOrder._native.native_decide.ax_9 +] + +private def atomicRoots : Array Lean.Name := #[ + ``infer_uncached_success, ``AtomicInferenceSupport.typing, + ``AtomicInferenceSupport.reads, ``AtomicInferenceSupport.output, + ``AtomicInferenceSupport.scopeAndReferences, ``AtomicInference.sound +] + +private def productionRoots : Array Lean.Name := #[ + ``StandalonePrefix.member_success, ``definition_body_trace, + ``AtomicDefinitionRun.sound, ``AtomicDefinitionRun.no_self_alias, + ``WorkPosition.check_success, ``AtomicDefinitionPlan.extends, + ``AtomicDefinitionPlan.sound, ``AtomicDefinitionPlan.represents, + ``checkEnvAnon_atomic_preserves_model, ``checkEnvAnon_atomic_represents_source, + ``checkEnvAnon_atomic_no_false +] + +/-- The new production roots must not acquire a checker-soundness assumption +or invoke the independent certificate validator to establish acceptance. -/ +private def forbiddenProduction : Array Lean.Name := #[ + `Ix.Kernel.CheckSuccessSound, + `Ix.Kernel.SupportedCheckFragment, + `Ix.Theory.Certified.checkProofCertified, + `Ix.Certified.acceptsSerializedStore +] + def roots : Array RootAllowance := #[ { root := ``readLevel_eq, standardAxioms := #[``propext] }, { root := ``readLevel_eval, standardAxioms := #[``propext] }, @@ -44,7 +78,13 @@ def roots : Array RootAllowance := #[ { root := ``sort_conversion, standardAxioms := standard }, { root := ``inferUncached_sort_sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } -] +] ++ atomicRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], + forbiddenDependencies := forbiddenProduction +}) ++ productionRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := productionNative, + forbiddenDependencies := forbiddenProduction +}) ++ #[{ root := ``extend_atomic_definition, standardAxioms := standard }] run_cmd Kernel.Verify.Audit.check roots diff --git a/Ix/Kernel/Verify/Consistency/Environment.lean b/Ix/Kernel/Verify/Consistency/Environment.lean new file mode 100644 index 000000000..f30a54e1f --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Environment.lean @@ -0,0 +1,455 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Production + +/-! +# Relative consistency of a production environment fragment + +The initial interface contains exactly the supplied axiom declarations. A +dependency order adds fresh monomorphic definitions, using successful calls +from the real serial `checkEnvAnon` run. Each addition constructs its value in +every model of the previous interface. Thus definitions preserve any given +model of the axiom set; no axiom is silently assumed to be inhabited. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model Theory.Model.SetTheory + +universe u v + +/-- A location in the exact work array, retaining the preceding work so the +checker state is the one actually reached by the production serial loop. -/ +structure WorkPosition (work : Array AnonWorkItem) (item : AnonWorkItem) where + leading : List AnonWorkItem + trailing : List AnonWorkItem + split : work.toList = leading ++ item :: trailing + +def WorkPosition.state {work : Array AnonWorkItem} {item : AnonWorkItem} + (position : WorkPosition work item) (env : Ixon.Env) (cfg : CheckCfg) : + AnonCheckLoopState := + runAnonCheckList cfg position.leading (initialAnonCheckLoopState env cfg) + +private theorem runList_append (cfg : CheckCfg) (left right : List AnonWorkItem) + (before : AnonCheckLoopState) : + runAnonCheckList cfg (left ++ right) before = + runAnonCheckList cfg right (runAnonCheckList cfg left before) := by + induction left generalizing before with + | nil => rfl + | cons item rest ih => exact ih (runAnonCheckItem cfg before item) + +private theorem finish_results (cfg : CheckCfg) (before : AnonCheckLoopState) + (item : AnonWorkItem) (checker : TcState .anon) (err : Option String) : + (finishAnonCheckItem cfg before item checker err).results = + before.results ++ item.targets.map (fun addr => ⟨addr, err⟩) := by + unfold finishAnonCheckItem + dsimp only + split <;> rfl + +private theorem item_keeps_result (cfg : CheckCfg) (before : AnonCheckLoopState) + (item : AnonWorkItem) {result : CheckResult} (found : result ∈ before.results) : + result ∈ (runAnonCheckItem cfg before item).results := by + cases run : TcM.checkConst (⟨item.primary, ()⟩ : KId .anon) before.checker with + | ok value after => + cases value + simp only [runAnonCheckItem, EStateM.run, run, finish_results] + exact Array.mem_append.mpr (.inl found) + | error err after => + simp only [runAnonCheckItem, EStateM.run, run, finish_results] + exact Array.mem_append.mpr (.inl found) + +private theorem list_keeps_result (cfg : CheckCfg) (work : List AnonWorkItem) + (before : AnonCheckLoopState) {result : CheckResult} (found : result ∈ before.results) : + result ∈ (runAnonCheckList cfg work before).results := by + induction work generalizing before with + | nil => exact found + | cons item rest ih => exact ih _ (item_keeps_result cfg before item found) + +/-- All-success result rows expose a successful call at this exact work +position. This rules out the public `.ok` result that contains failed rows. -/ +theorem WorkPosition.check_success {env : Ixon.Env} {cfg : CheckCfg} + {work : Array AnonWorkItem} {addr : Address} + (position : WorkPosition work (.standalone addr)) + (succeeded : ∀ result ∈ (runAnonCheckList cfg work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none) : + ∃ after, TcM.checkConst (⟨addr, ()⟩ : KId .anon) + (position.state env cfg).checker = .ok () after := by + rw [position.split, runList_append] at succeeded + change ∀ result ∈ (runAnonCheckList cfg position.trailing + (runAnonCheckItem cfg (position.state env cfg) (.standalone addr))).results, + result.err? = none at succeeded + cases run : TcM.checkConst (⟨addr, ()⟩ : KId .anon) (position.state env cfg).checker with + | ok value after => + cases value + exact ⟨after, rfl⟩ + | error err after => + let failed : CheckResult := ⟨addr, some (toString err)⟩ + have appears : failed ∈ + (runAnonCheckItem cfg (position.state env cfg) (.standalone addr)).results := by + simp only [runAnonCheckItem, EStateM.run, AnonWorkItem.primary, run, finish_results, + AnonWorkItem.targets] + apply Array.mem_append.mpr + exact .inr (by simp [failed]) + have contradiction := succeeded failed + (list_keeps_result cfg position.trailing _ appears) + simp [failed] at contradiction + +/-- The semantic entry published for a monomorphic definition. No reduction +equations or extra typed facts are manufactured by this fragment. -/ +def definitionEntry {β : Type u} (body type : AExpr β) : ConstantEntry β := + { universes := 0, type, body := some body } + +/-- Body typing already includes hereditary validity of the declared type. +This constructs a model extension directly, without separately assuming a +model for the pending declaration. -/ +theorem extend_atomic_definition {β : Type u} [DecidableEq β] + {entries : Model.Environment β} {ref : ConstRef β} {body type : AExpr β} + (wellFormed : entries.WF) (fresh : entries ref = none) + (bodyScope : body.Scope 0 0) + (bodyRefs : body.ReferencesIn entries) (typeRefs : type.ReferencesIn entries) + (typed : TypingClaim.{u,v} entries [] body type) + {V : Type v} [SetTheory V] (constants : Assignment β V) (realizes : Realizes constants entries) : + ∃ next : Assignment β V, + Realizes next (entries.insert ref (definitionEntry body type)) ∧ + Assignment.AgreesOn entries constants next := by + let next := constants.insert ref (fun levels => interp constants levels (fun _ => empty) body) + have agrees : Assignment.AgreesOn entries constants next := + Assignment.insert_agrees fresh constants _ + have value (levels : List Nat) (env : Nat → V) : + next ref levels = interp constants levels env body := by + simp only [next, Assignment.insert, if_true] + exact interp_closed body constants levels bodyScope _ env + refine ⟨next, (realizes.of_agrees wellFormed agrees).insert ?_, agrees⟩ + constructor + · intro levels _ env + exact (agrees.wellDenoted typeRefs levels env).mpr + (typed V constants realizes levels env (Context.valid_nil constants levels env)).2.1 + · intro levels _ env + change next ref levels ∈ˢ interp next levels env type + rw [value, agrees.interp typeRefs] + exact (typed V constants realizes levels env (Context.valid_nil constants levels env)).2.2 + · intro candidate present levels _ env + change some body = some candidate at present + cases Option.some.inj present + exact (agrees.wellDenoted bodyRefs levels env).mpr + (typed V constants realizes levels env (Context.valid_nil constants levels env)).1 + · intro candidate present levels _ env + change some body = some candidate at present + cases Option.some.inj present + rw [value, agrees.interp bodyRefs] + · intro law present + exact (List.not_mem_nil present).elim + · intro fact present + exact (List.not_mem_nil present).elim + +/-- A declaration's source data and its annotated semantic interface. The +production run must establish both erasure agreements and body typing. -/ +structure DefinitionSpec (β : Type u) where + input : DefinitionInput + ref : ConstRef β + body : AExpr β + type : AExpr β + +def DefinitionSpec.entry {β : Type u} (spec : DefinitionSpec β) : ConstantEntry β := + definitionEntry spec.body spec.type + +private theorem insert_definition_wf {β : Type u} [DecidableEq β] + {entries : Model.Environment β} {ref : ConstRef β} {body type : AExpr β} + (wellFormed : entries.WF) (bodyScope : body.Scope 0 0) (typeScope : type.Scope 0 0) + (bodyRefs : body.ReferencesIn entries) (typeRefs : type.ReferencesIn entries) : + (entries.insert ref (definitionEntry body type)).WF := by + apply wellFormed.insert typeScope + · intro candidate present + change some body = some candidate at present + cases Option.some.inj present + exact bodyScope + · exact typeRefs + · intro candidate present + change some body = some candidate at present + cases Option.some.inj present + exact bodyRefs + · simp [definitionEntry] + · simp [definitionEntry] + · simp [definitionEntry] + · simp [definitionEntry] + +/-- A dependency order over the checked definitions. Each reference is fresh +and each atomic body reads only the preceding interface. The work position +retains the original serial order, which may differ from this dependency +order. No semantic acceptance premise appears in this plan. -/ +inductive AtomicDefinitionPlan {β : Type u} [DecidableEq β] + (env : Ixon.Env) (cfg : CheckCfg) (work : Array AnonWorkItem) + (resolve : Address → Option (ConstRef β)) : + Model.Environment β → List (DefinitionSpec β) → Model.Environment β → Prop + | nil (entries) : AtomicDefinitionPlan env cfg work resolve entries [] entries + | cons {before after : Model.Environment β} {rest : List (DefinitionSpec β)} + (spec : DefinitionSpec β) + (resolved : resolve spec.input.id.addr = some spec.ref) + (fresh : before spec.ref = none) + (position : WorkPosition work (.standalone spec.input.id.addr)) + (run : AtomicDefinitionRun resolve before spec.input + (position.state env cfg).checker spec.body spec.type) + (tail : AtomicDefinitionPlan env cfg work resolve + (before.insert spec.ref spec.entry) rest after) : + AtomicDefinitionPlan env cfg work resolve before (spec :: rest) after + +/-- Every old interface entry is retained by a dependency plan. -/ +theorem AtomicDefinitionPlan.extends {β : Type u} [DecidableEq β] + {env : Ixon.Env} {cfg : CheckCfg} {work : Array AnonWorkItem} + {resolve : Address → Option (ConstRef β)} + {before after : Model.Environment β} {definitions : List (DefinitionSpec β)} + (plan : AtomicDefinitionPlan env cfg work resolve before definitions after) : + ∀ ref entry, before ref = some entry → after ref = some entry := by + induction plan with + | nil entries => exact fun _ _ h => h + | cons spec resolved fresh position run tail ih => + intro ref entry present + exact ih ref entry (Model.Environment.insert_old fresh present) + +/-- Model preservation keeps every old reference's interpretation fixed. -/ +def PreservesModels {β : Type u} (before after : Model.Environment β) : Prop := + ∀ (V : Type v) [SetTheory V] (constants : Assignment β V), + Realizes constants before → ∃ next : Assignment β V, + Realizes next after ∧ Assignment.AgreesOn before constants next + +private theorem anon_id (id : KId .anon) : (⟨id.addr, ()⟩ : KId .anon) = id := by + cases id with + | mk addr name => cases name; rfl + +/-- Every accepted definition in the dependency order extends the preceding +model. The initial interface need only have a model; its axioms need not be +part of a fixed whitelist. -/ +theorem AtomicDefinitionPlan.sound {β : Type u} [DecidableEq β] + {env : Ixon.Env} {cfg : CheckCfg} {work : Array AnonWorkItem} + {resolve : Address → Option (ConstRef β)} + {before after : Model.Environment β} {definitions : List (DefinitionSpec β)} + (plan : AtomicDefinitionPlan env cfg work resolve before definitions after) + (wellFormed : before.WF) + (succeeded : ∀ result ∈ (runAnonCheckList cfg work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none) : + after.WF ∧ PreservesModels.{u,v} before after := by + induction plan with + | nil entries => + exact ⟨wellFormed, fun _ _ constants realizes => + ⟨constants, realizes, fun _ _ _ _ => rfl⟩⟩ + | @cons before after rest spec resolved fresh position run tail ih => + obtain ⟨checkedState, accepted⟩ := position.check_success succeeded + rw [anon_id] at accepted + have bodySound := run.sound wellFormed accepted + have formed : (before.insert spec.ref spec.entry).WF := + insert_definition_wf wellFormed bodySound.2.2.1 bodySound.2.2.2.1 + bodySound.2.2.2.2.1 bodySound.2.2.2.2.2.1 + obtain ⟨finalFormed, preserve⟩ := ih formed + refine ⟨finalFormed, ?_⟩ + intro V _ constants realizes + obtain ⟨next, nextModel, agrees⟩ := extend_atomic_definition wellFormed fresh + bodySound.2.2.1 bodySound.2.2.2.2.1 bodySound.2.2.2.2.2.1 + bodySound.2.2.2.2.2.2 constants realizes + obtain ⟨final, finalModel, finalAgrees⟩ := preserve V next nextModel + refine ⟨final, finalModel, ?_⟩ + intro ref entry present levels + exact (finalAgrees ref entry (Model.Environment.insert_old fresh present) levels).trans + (agrees ref entry present levels) + +/-- Every published definition interface reads the actual checked type and +body. Freshness ensures later definitions cannot overwrite that interface. -/ +theorem AtomicDefinitionPlan.represents {β : Type u} [DecidableEq β] + {env : Ixon.Env} {cfg : CheckCfg} {work : Array AnonWorkItem} + {resolve : Address → Option (ConstRef β)} + {before after : Model.Environment β} {definitions : List (DefinitionSpec β)} + (plan : AtomicDefinitionPlan env cfg work resolve before definitions after) + (wellFormed : before.WF) + (succeeded : ∀ result ∈ (runAnonCheckList cfg work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none) : + ∀ spec ∈ definitions, + resolve spec.input.id.addr = some spec.ref ∧ after spec.ref = some spec.entry ∧ + readExpr? resolve spec.input.value = some spec.body.erase ∧ + readExpr? resolve spec.input.type = some spec.type.erase := by + induction plan with + | nil entries => simp + | @cons before after rest spec resolved fresh position run tail ih => + obtain ⟨checkedState, accepted⟩ := position.check_success succeeded + rw [anon_id] at accepted + have bodySound := AtomicDefinitionRun.sound.{u,0} run wellFormed accepted + have formed : (before.insert spec.ref spec.entry).WF := + insert_definition_wf wellFormed bodySound.2.2.1 bodySound.2.2.2.1 + bodySound.2.2.2.2.1 bodySound.2.2.2.2.2.1 + intro candidate present + rcases List.mem_cons.mp present with same | later + · subst candidate + exact ⟨resolved, tail.extends _ _ (Model.Environment.insert_same ..), + bodySound.1, bodySound.2.1⟩ + · exact ih formed candidate later + +private theorem AtomicDefinitionPlan.locations {β : Type u} [DecidableEq β] + {env : Ixon.Env} {cfg : CheckCfg} {work : Array AnonWorkItem} + {resolve : Address → Option (ConstRef β)} + {before after : Model.Environment β} {definitions : List (DefinitionSpec β)} + (plan : AtomicDefinitionPlan env cfg work resolve before definitions after) : + ∀ spec ∈ definitions, ∃ position : WorkPosition work (.standalone spec.input.id.addr), + Nonempty (StandalonePrefix spec.input.id (position.state env cfg).checker spec.input.constant) := by + induction plan with + | nil => simp + | cons spec resolved fresh position run tail ih => + intro candidate present + rcases List.mem_cons.mp present with same | later + · subst candidate + exact ⟨position, ⟨run.path⟩⟩ + · exact ih candidate later + +/-- An axiom has no value or computational equations in the initial model. -/ +structure AxiomSpec (β : Type u) where + id : KId .anon + ref : ConstRef β + isUnsafe : Bool + sourceType : KExpr .anon + type : AExpr β + +def AxiomSpec.constant {β : Type u} (spec : AxiomSpec β) : KConst .anon := + .axio () () spec.isUnsafe 0 spec.sourceType + +def AxiomSpec.entry {β : Type u} (spec : AxiomSpec β) : ConstantEntry β := + { universes := 0, type := spec.type, body := none } + +/-- Exactly the declared axiom interface, starting with no other entries. -/ +def axiomEnvironment {β : Type u} [DecidableEq β] : List (AxiomSpec β) → Model.Environment β + | [] => fun _ => none + | spec :: rest => (axiomEnvironment rest).insert spec.ref spec.entry + +/-- Syntactic provenance of one axiom at a real work position. The model +hypothesis supplies its inhabitant; this record only identifies its source +declaration and exact type. -/ +structure AxiomObservation {β : Type u} (env : Ixon.Env) (cfg : CheckCfg) + (work : Array AnonWorkItem) (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (spec : AxiomSpec β) where + position : WorkPosition work (.standalone spec.id.addr) + path : StandalonePrefix spec.id (position.state env cfg).checker spec.constant + resolved : resolve spec.id.addr = some spec.ref + installed : entries spec.ref = some spec.entry + reads : readExpr? resolve spec.sourceType = some spec.type.erase + +/-- Complete support for the selected production environment fragment. +Every source key is checked, every work item is represented, and the axiom +interface contains exactly the listed source axioms. All definition resources +refer to concrete states of this same production run. -/ +structure AtomicEnvironmentFragment {β : Type u} [DecidableEq β] + (env : Ixon.Env) (cfg : CheckCfg) (resolve : Address → Option (ConstRef β)) where + work : Array AnonWorkItem + enumerated : buildAnonWork env = .ok work + axioms : List (AxiomSpec β) + definitions : List (DefinitionSpec β) + entries : Model.Environment β + axiomRuns : ∀ spec ∈ axioms, + Nonempty (AxiomObservation env cfg work resolve (axiomEnvironment axioms) spec) + plan : AtomicDefinitionPlan env cfg work resolve (axiomEnvironment axioms) definitions entries + sourceCovered : ∀ addr ∈ env.consts.keys, .standalone addr ∈ work + workCovered : ∀ item ∈ work, + (∃ spec ∈ axioms, item = .standalone spec.id.addr) ∨ + (∃ spec ∈ definitions, item = .standalone spec.input.id.addr) + +private theorem AtomicEnvironmentFragment.serial_success {β : Type u} [DecidableEq β] + {env : Ixon.Env} {cfg : CheckCfg} {resolve : Address → Option (ConstRef β)} + (fragment : AtomicEnvironmentFragment env cfg resolve) {results : Array CheckResult} + (accepted : checkEnvAnon env cfg = .ok results) + (succeeded : ∀ result ∈ results, result.err? = none) : + ∀ result ∈ (runAnonCheckList cfg fragment.work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none := by + unfold checkEnvAnon at accepted + rw [fragment.enumerated] at accepted + cases accepted + exact succeeded + +/-- A successful `checkEnvAnon` run in the supported fragment extends every +model of its axiom set while preserving all axiom interpretations. This is a +relative model theorem, not a claim that arbitrary axioms are consistent. -/ +theorem checkEnvAnon_atomic_preserves_model {β : Type u} [DecidableEq β] + {env : Ixon.Env} {cfg : CheckCfg} {resolve : Address → Option (ConstRef β)} + (fragment : AtomicEnvironmentFragment env cfg resolve) + (wellFormed : (axiomEnvironment fragment.axioms).WF) + {results : Array CheckResult} (accepted : checkEnvAnon env cfg = .ok results) + (succeeded : ∀ result ∈ results, result.err? = none) : + fragment.entries.WF ∧ PreservesModels.{u,v} + (axiomEnvironment fragment.axioms) fragment.entries := + fragment.plan.sound wellFormed (fragment.serial_success accepted succeeded) + +/-- Exact type, universe arity, and value agreement with the declaration +returned by production lookup. Axiom entries contain no body. -/ +def DeclarationReading {β : Type u} (resolve : Address → Option (ConstRef β)) + (concrete : KConst .anon) (entry : ConstantEntry β) : Prop := + readExpr? resolve concrete.ty = some entry.type.erase ∧ + entry.universes = concrete.lvls.toNat ∧ + match concrete with + | .axio .. => entry.body = none + | .defn (val := value) .. => + ∃ body, entry.body = some body ∧ readExpr? resolve value = some body.erase + | _ => False + +/-- Every source address receives an interface entry whose type reads the +declaration actually reached by production lookup. Definitions additionally +have the exact checked body in their model entry. -/ +theorem checkEnvAnon_atomic_represents_source {β : Type u} [DecidableEq β] + {env : Ixon.Env} {cfg : CheckCfg} {resolve : Address → Option (ConstRef β)} + (fragment : AtomicEnvironmentFragment env cfg resolve) + (wellFormed : (axiomEnvironment fragment.axioms).WF) + {results : Array CheckResult} (accepted : checkEnvAnon env cfg = .ok results) + (succeeded : ∀ result ∈ results, result.err? = none) : + ∀ addr ∈ env.consts.keys, ∃ ref entry, + resolve addr = some ref ∧ fragment.entries ref = some entry ∧ + ∃ concrete : KConst .anon, + (∃ before after, TcM.checkConst (⟨addr, ()⟩ : KId .anon) before = .ok () after ∧ + Nonempty (StandalonePrefix (⟨addr, ()⟩ : KId .anon) before concrete)) ∧ + DeclarationReading resolve concrete entry := by + have serial := fragment.serial_success accepted succeeded + have represented := fragment.plan.represents wellFormed serial + intro addr present + rcases fragment.workCovered (.standalone addr) (fragment.sourceCovered addr present) with + ⟨spec, listed, same⟩ | ⟨spec, listed, same⟩ + · cases AnonWorkItem.standalone.inj same + obtain ⟨observation⟩ := fragment.axiomRuns spec listed + obtain ⟨after, run⟩ := observation.position.check_success serial + refine ⟨spec.ref, spec.entry, observation.resolved, + fragment.plan.extends _ _ observation.installed, spec.constant, + ⟨(observation.position.state env cfg).checker, after, run, ?_⟩, + observation.reads, rfl, rfl⟩ + simpa only [anon_id] using Nonempty.intro observation.path + · cases AnonWorkItem.standalone.inj same + have represents := represented spec listed + obtain ⟨position, path⟩ := fragment.plan.locations spec listed + obtain ⟨after, run⟩ := position.check_success serial + refine ⟨spec.ref, spec.entry, represents.1, represents.2.1, spec.input.constant, + ⟨(position.state env cfg).checker, after, run, ?_⟩, represents.2.2.2, + rfl, spec.body, rfl, represents.2.2.1⟩ + simpa only [anon_id] using path + +/-- No declaration in the resulting environment can inhabit a type whose +axiom-model interpretation is empty. In particular this applies to `False`. +The empty interpretation is preserved, rather than assumed for the completed +environment. -/ +theorem checkEnvAnon_atomic_no_false {β : Type u} [DecidableEq β] + {env : Ixon.Env} {cfg : CheckCfg} {resolve : Address → Option (ConstRef β)} + (fragment : AtomicEnvironmentFragment env cfg resolve) + (wellFormed : (axiomEnvironment fragment.axioms).WF) + {results : Array CheckResult} (accepted : checkEnvAnon env cfg = .ok results) + (succeeded : ∀ result ∈ results, result.err? = none) + {V : Type v} [SetTheory V] (axiomValues : Assignment β V) + (axiomModel : Realizes axiomValues (axiomEnvironment fragment.axioms)) + {falseRef : ConstRef β} {falseEntry : ConstantEntry β} + (hasFalse : axiomEnvironment fragment.axioms falseRef = some falseEntry) + (falseEmpty : axiomValues falseRef [] = empty) + {ref : ConstRef β} {entry : ConstantEntry β} + (present : fragment.entries ref = some entry) + (isFalse : entry.type.erase = .const falseRef []) : False := by + obtain ⟨_, preserve⟩ := checkEnvAnon_atomic_preserves_model fragment wellFormed accepted succeeded + obtain ⟨values, model, agrees⟩ := preserve V axiomValues axiomModel + have emptyValue : values falseRef [] = empty := (agrees _ _ hasFalse []).trans falseEmpty + have typeEq := AExpr.eq_const_of_erase_eq isFalse + have member := model.member ref entry present (List.replicate entry.universes 0) + (by simp) (fun _ => empty) + rw [typeEq] at member + simp only [interp, List.map_nil, emptyValue] at member + exact not_mem_empty _ member + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Production.lean b/Ix/Kernel/Verify/Consistency/Production.lean new file mode 100644 index 000000000..320b46722 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Production.lean @@ -0,0 +1,246 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Driver +import Ix.Kernel.Verify.Consistency.Atomic + +/-! +# Standalone production declaration checks + +These theorems invert the real public checker, including its error isolation, +initial lazy lookup, block routing, per-constant reset, validation, type +inference, theorem guard, value inference, and conversion. The supported +conversion path is the initial address-equality branch. Finite address +faithfulness connects that comparison to the exact model syntax. + +The resource records contain operational equations and structural interface +agreement. They do not contain a typing proof, a method-soundness callback, +or an assumption that an accepted declaration already has a model. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +private theorem runTcBind {α β : Type} (x : TcM .anon α) (k : α → TcM .anon β) + (state : TcState .anon) : + EStateM.bind x k state = match x state with + | .ok value after => k value after + | .error err after => .error err after := by + unfold EStateM.bind + cases x state <;> rfl + +/-- The concrete path of a standalone public check. This ties the member +declaration to both production lookup and the actual reset, without assuming +that block coordination or lazy ingress preserves an arbitrary predicate. -/ +structure StandalonePrefix (id : KId .anon) (before : TcState .anon) + (concrete : KConst .anon) where + first : KConst .anon + loaded : TcState .anon + routed : TcState .anon + reset : TcState .anon + ready : TcState .anon + firstGet : TcM.getConst id before = .ok first loaded + route : (RecM.coordinatedBlockFor first).run (methodsN before.recFuel.toNat) + loaded = .ok none routed + resetRun : TcM.reset routed = .ok () reset + memberGet : TcM.getConst id reset = .ok concrete ready + +/-- Success at the public boundary entails success of the exact member +identified by the standalone path. In particular, an error cannot be +turned into acceptance by the public cache rollback wrapper. -/ +theorem StandalonePrefix.member_success {id : KId .anon} + {before after : TcState .anon} {concrete : KConst .anon} + (path : StandalonePrefix id before concrete) + (accepted : TcM.checkConst id before = .ok () after) : + (RecM.checkConstMember id concrete).run (methodsN before.recFuel.toNat) + path.ready = .ok () after := by + unfold TcM.checkConst TcM.isolateCheckErrors at accepted + cases run : TcM.runRec (RecM.checkConst id) before with + | error err failed => rw [run] at accepted; contradiction + | ok result finished => + rw [run] at accepted + cases accepted + change (RecM.checkConst id).run (methodsN before.recFuel.toNat) before = + .ok () after at run + unfold RecM.checkConst at run + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at run + change EStateM.bind (TcM.getConst id) _ before = _ at run + rw [runTcBind, path.firstGet] at run + change EStateM.bind + ((RecM.coordinatedBlockFor path.first).run (methodsN before.recFuel.toNat)) + _ path.loaded = _ at run + rw [runTcBind, path.route] at run + change (RecM.checkConstMemberFresh id).run (methodsN before.recFuel.toNat) + path.routed = .ok () after at run + unfold RecM.checkConstMemberFresh at run + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at run + change EStateM.bind TcM.reset _ path.routed = _ at run + rw [runTcBind, path.resetRun] at run + change EStateM.bind (TcM.getConst id) _ path.reset = _ at run + rw [runTcBind, path.memberGet] at run + exact run + +/-- Monomorphic definition data. Its complete concrete declaration is the +one returned by the production lookup, including kind, safety, and block. -/ +structure DefinitionInput where + id : KId .anon + kind : Ix.DefKind + safety : Ix.DefinitionSafety + hints : Lean.ReducibilityHints + type : KExpr .anon + value : KExpr .anon + block : KId .anon + +def DefinitionInput.constant (input : DefinitionInput) : KConst .anon := + .defn () () input.kind input.safety input.hints 0 input.type input.value () input.block + +/-- Exact successful observations through a definition's value-conversion +call. Later safety checks may still reject; the extraction theorem requires +the entire member check to have succeeded. -/ +structure DefinitionBodyTrace (input : DefinitionInput) (methods : Methods .anon) + (before : TcState .anon) where + validated : TcState .anon + inferredType : KExpr .anon + typeState : TcState .anon + level : KUniv .anon + valueStart : TcState .anon + inferredValue : KExpr .anon + conversionStart : TcState .anon + conversionEnd : TcState .anon + validationRun : (RecM.validateConstWellScoped input.constant).run methods before = + .ok () validated + typeRun : (RecM.infer input.type).run methods validated = .ok inferredType typeState + sortRun : (RecM.ensureSortDirect inferredType).run methods typeState = .ok level valueStart + theoremGuard : (input.kind == .thm && !univEq level .mkZero) = false + valueRun : (RecM.infer input.value).run methods valueStart = .ok inferredValue conversionStart + conversionRun : (RecM.isDefEq inferredValue input.type).run methods conversionStart = + .ok true conversionEnd + +/-- Invert the production declaration branch rather than assuming that its +acceptance establishes either type or value typing. -/ +theorem definition_body_trace {input : DefinitionInput} {methods : Methods .anon} + {before after : TcState .anon} + (accepted : (RecM.checkConstMember input.id input.constant).run methods before = + .ok () after) : Nonempty (DefinitionBodyTrace input methods before) := by + unfold RecM.checkConstMember at accepted + simp only [DefinitionInput.constant, Mode.F.hasDups, Bool.false_eq_true, if_false, + ReaderT.run_bind] at accepted + cases validation : (RecM.validateConstWellScoped input.constant).run methods before with + | error err failed => + change EStateM.bind ((RecM.validateConstWellScoped input.constant).run methods) + _ before = _ at accepted + rw [runTcBind, validation] at accepted + contradiction + | ok result validated => + cases result + change EStateM.bind ((RecM.validateConstWellScoped input.constant).run methods) + _ before = _ at accepted + rw [runTcBind, validation] at accepted + change EStateM.bind ((RecM.infer input.type).run methods) _ validated = _ at accepted + cases typeRun : (RecM.infer input.type).run methods validated with + | error err failed => rw [runTcBind, typeRun] at accepted; contradiction + | ok inferredType typeState => + rw [runTcBind, typeRun] at accepted + change EStateM.bind ((RecM.ensureSortDirect inferredType).run methods) + _ typeState = _ at accepted + cases sortRun : (RecM.ensureSortDirect inferredType).run methods typeState with + | error err failed => rw [runTcBind, sortRun] at accepted; contradiction + | ok level valueStart => + rw [runTcBind, sortRun] at accepted + simp only at accepted + by_cases guard : input.kind == .thm && !univEq level .mkZero + · simp only [guard, if_true] at accepted + contradiction + · simp only [guard, Bool.false_eq_true, if_false, ReaderT.run_bind] at accepted + cases valueRun : (RecM.infer input.value).run methods valueStart with + | error err failed => + change EStateM.bind ((RecM.infer input.value).run methods) + _ valueStart = _ at accepted + rw [runTcBind, valueRun] at accepted + contradiction + | ok inferredValue conversionStart => + change EStateM.bind ((RecM.infer input.value).run methods) + _ valueStart = _ at accepted + rw [runTcBind, valueRun] at accepted + change EStateM.bind ((RecM.isDefEq inferredValue input.type).run methods) + _ conversionStart = _ at accepted + cases conversionRun : (RecM.isDefEq inferredValue input.type).run methods + conversionStart with + | error err failed => rw [runTcBind, conversionRun] at accepted; contradiction + | ok answer conversionEnd => + rw [runTcBind, conversionRun] at accepted + cases answer with + | false => + simp only [Bool.not_false, if_true] at accepted + contradiction + | true => + exact ⟨{ + validated, inferredType, typeState, level, valueStart, + inferredValue, conversionStart, conversionEnd, + validationRun := validation, typeRun, sortRun, + theoremGuard := Bool.eq_false_iff.mpr guard, + valueRun, conversionRun }⟩ + +/-- Operational support for the selected production definition fragment. +Resources are required only at the states exposed by successful body traces. +The conversion guard records the actual initial hash-equality path. -/ +structure AtomicDefinitionRun {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (input : DefinitionInput) (before : TcState .anon) + (body type : AExpr β) where + path : StandalonePrefix input.id before input.constant + inference : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, + AtomicInference resolve entries trace.valueStart input.value body type + hashPath : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, + (trace.inferredValue == input.type) = true + faithful : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, + trace.inferredValue.AddrFaithful input.type + +/-- Successful production checking yields a model typing judgment for the +actual value and declared type, with syntactic closure and dependency support +needed to extend a model. -/ +theorem AtomicDefinitionRun.sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {input : DefinitionInput} {before after : TcState .anon} {body type : AExpr β} + (fragment : AtomicDefinitionRun resolve entries input before body type) + (wellFormed : entries.WF) + (accepted : TcM.checkConst input.id before = .ok () after) : + readExpr? resolve input.value = some body.erase ∧ + readExpr? resolve input.type = some type.erase ∧ + body.Scope 0 0 ∧ type.Scope 0 0 ∧ + body.ReferencesIn entries ∧ type.ReferencesIn entries ∧ + TypingClaim.{u,v} entries [] body type := by + obtain ⟨trace⟩ := definition_body_trace (fragment.path.member_success accepted) + have result := (fragment.inference trace).sound trace.valueRun + have same := beq_readExpr? (resolve := resolve) + (fragment.faithful trace) (fragment.hashPath trace) + have scope := (fragment.inference trace).support.scopeAndReferences wellFormed + exact ⟨result.1, same.symm.trans result.2.1, scope.1, scope.2.1, + scope.2.2.1, scope.2.2.2, result.2.2⟩ + +/-- A fresh definition cannot use itself as the atomic value that justifies +its own type. This exclusion follows from the preceding interface, without +any assumption about the consistency of the axioms. -/ +theorem AtomicDefinitionRun.no_self_alias {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {input : DefinitionInput} {before after : TcState .anon} {body type : AExpr β} + (fragment : AtomicDefinitionRun resolve entries input before body type) + (wellFormed : entries.WF) {ref : ConstRef β} + (resolved : resolve input.id.addr = some ref) (fresh : entries ref = none) + {info : ExprInfo .anon} (self : input.value = .const input.id #[] info) + (accepted : TcM.checkConst input.id before = .ok () after) : False := by + have result := AtomicDefinitionRun.sound.{u,0} fragment wellFormed accepted + have reads := result.1 + rw [self] at reads + simp [readExpr?, resolved] at reads + have bodyEq := AExpr.eq_const_of_erase_eq reads.symm + have references := result.2.2.2.2.1 + rw [bodyEq] at references + have present := references ref (by simp [AExpr.references]) + simp only [fresh, Option.isSome_none, Bool.false_eq_true] at present + +end Ix.Kernel.Consistency diff --git a/README.md b/README.md index 37832c63a..c9bc8f732 100644 --- a/README.md +++ b/README.md @@ -176,9 +176,11 @@ Ix consists of the following core components: `Ix.Kernel` includes audited implementation proofs and a certified receipt interface with relative consistency theorems under an explicit set-theory -assumption. Full consistency of the production `checkEnvAnon` checker remains -open. See the [kernel verification guide](docs/kernel-verification.md) for the -theorem boundaries and remaining obligations, and +assumption. A production `checkEnvAnon` fragment for monomorphic aliases and +closed universe terms preserves any given model of its source axioms. Full +checker consistency remains open. See the +[kernel verification guide](docs/kernel-verification.md) for the fragment's +execution premises and remaining obligations, and [certified checking](docs/certified-checking.md) for runnable examples. Run `lake run check-kernel` to validate the implementation proofs, consistency diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean new file mode 100644 index 000000000..7b9376856 --- /dev/null +++ b/Tests/Ix/Kernel/Consistency.lean @@ -0,0 +1,91 @@ +module + +public import LSpec +public import Ix.Kernel +public import Tests.Ix.Kernel.IxonFixtures + +/-! +Production regressions for the atomic consistency fragment. These execute +the real lazy serial driver on content-addressed Ixon declarations. The +formal results and their resource premises are checked separately by +`IxKernelConsistency`; these tests do not substitute for those proofs. +-/ + +namespace Tests.Kernel.Consistency + +open LSpec Ix.Kernel Tests.Kernel.Fixtures + +private def allSucceeded (env : Ixon.Env) (expected : Nat) (cfg : CheckCfg := {}) : Bool := + match checkEnvAnon env cfg with + | .ok results => results.size == expected && results.all (·.err?.isNone) + | .error _ => false + +private def rowFailed (env : Ixon.Env) (target : Address) : Bool := + match checkEnvAnon env with + | .ok results => results.any fun row => row.addr == target && row.err?.isSome + | .error _ => false + +/-- `P : Prop`, `p : P`, then definition/theorem/opaque aliases of `p`. +The axiom set supplies `p`; the definitions introduce no additional axiom. -/ +private def aliasEnvironment : Ixon.Env := Id.run do + let (env, proposition) := storeConst {} + ⟨.axio ⟨false, 0, .sort 0⟩, #[], #[], #[.zero]⟩ + let (env, witness) := storeConst env + ⟨.axio ⟨false, 0, .ref 0 #[]⟩, #[], #[proposition], #[]⟩ + let (env, first) := storeConst env + ⟨.defn ⟨.defn, .safe, 0, .ref 0 #[], .ref 1 #[]⟩, + #[], #[proposition, witness], #[]⟩ + let (env, second) := storeConst env + ⟨.defn ⟨.thm, .safe, 0, .ref 0 #[], .ref 1 #[]⟩, + #[], #[proposition, first], #[]⟩ + let (env, _) := storeConst env + ⟨.defn ⟨.opaq, .safe, 0, .ref 0 #[], .ref 1 #[]⟩, + #[], #[proposition, second], #[]⟩ + return env + +private def sortEnvironment : Ixon.Env := Id.run do + let definition : Ix.DefKind → Ixon.Constant := fun kind => + ⟨.defn ⟨kind, .safe, 0, .sort 1, .sort 0⟩, #[], #[], #[.zero, .succ .zero]⟩ + let (env, _) := storeConst {} (definition .defn) + let (env, _) := storeConst env (definition .opaq) + return env + +private def failedAlias : Ixon.Env × Address := Id.run do + let (env, carrier) := envA + let (env, witness) := storeConst env + ⟨.axio ⟨false, 0, .ref 0 #[]⟩, #[], #[carrier], #[]⟩ + -- The witness has type A, whereas this definition declares Prop. + return storeConst env + ⟨.defn ⟨.defn, .safe, 0, .sort 0, .ref 0 #[]⟩, + #[], #[witness], #[.zero]⟩ + +private def mismatchedSort : Ixon.Env × Address := + storeConst {} + ⟨.defn ⟨.defn, .safe, 0, .sort 0, .sort 0⟩, #[], #[], #[.zero]⟩ + +private def missingReference : Ixon.Env × Address := + storeConst {} + ⟨.defn ⟨.defn, .safe, 0, .sort 0, .ref 0 #[]⟩, + #[], #[Address.blake3 "atomic-fragment-missing-reference".toUTF8], #[.zero]⟩ + +private def cases : TestSeq := + test "atomic fragment: source axioms and transitive aliases check" + (allSucceeded aliasEnvironment 5) + ++ test "atomic fragment: alias checks survive cache clearing at every item" + (allSucceeded aliasEnvironment 5 { clearEvery := 1 }) + ++ test "atomic fragment: closed sort definitions check with persistent caches" + (allSucceeded sortEnvironment 2 { clearEvery := 0 }) + ++ test "atomic fragment: closed sort definitions check with fresh caches" + (allSucceeded sortEnvironment 2 { clearEvery := 1 }) + ++ test "atomic fragment: value/type mismatch is an error row inside public ok" + (let (env, target) := failedAlias; rowFailed env target) + ++ test "atomic fragment: Sort 0 does not inhabit Sort 0" + (let (env, target) := mismatchedSort; rowFailed env target) + ++ test "atomic fragment: an unresolved reference is not an admitted axiom" + (let (env, target) := missingReference; rowFailed env target) + ++ test "atomic fragment: empty input returns an empty successful result" + (allSucceeded {} 0) + +public def suite : List TestSeq := [cases] + +end Tests.Kernel.Consistency diff --git a/Tests/Main.lean b/Tests/Main.lean index 0e7753500..3650fcb55 100644 --- a/Tests/Main.lean +++ b/Tests/Main.lean @@ -40,6 +40,7 @@ import Tests.Ix.Kernel.IxonFixtures import Tests.Ix.Kernel.WhnfTests import Tests.Ix.Kernel.InferDefEq import Tests.Ix.Kernel.CheckTests +import Tests.Ix.Kernel.Consistency import Tests.Ix.Kernel.AnonDiff import Tests.Ix.Kernel.InitScale import Tests.Ix.Kernel.TutorialTc @@ -108,6 +109,7 @@ def primarySuites : Std.HashMap String (List LSpec.TestSeq) := .ofList [ ("tc-unit", Tests.Kernel.Unit.suite ++ Tests.Kernel.Substrate.suite ++ Tests.Kernel.Fixtures.suite ++ Tests.Kernel.WhnfTests.suite ++ Tests.Kernel.InferDefEq.suite ++ Tests.Kernel.CheckTests.suite + ++ Tests.Kernel.Consistency.suite ++ Tests.Kernel.Roundtrip.unitTests ++ Tests.Kernel.IngressMeta.unitTests), ] diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 7dfc385f7..3f8eabaf7 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -43,14 +43,80 @@ dependency audit restricted to Lean's three standard axioms. - `ModelTyping.no_false` rules out a closed model-typed kernel expression at primitive False when its environment has been admitted by the certified interface and the set-theory assumption has an instance. +- `checkEnvAnon_atomic_preserves_model` connects a supported production + environment run to model extension. `checkEnvAnon_atomic_no_false` excludes + a declaration at an axiom type interpreted as empty, including False. -The last theorem assumes semantic typing; it does not assume or prove that -arbitrary checker success supplies it. A complete checker consistency theorem +`ModelTyping.no_false` assumes semantic typing. The production fragment below +constructs that typing from its supported inference and conversion paths. +A complete checker consistency theorem still requires the remaining inference/conversion cases, cache invariants, address-to-store resolution, and declaration admission to establish that postcondition for `checkEnvAnon`. The existing named-calculus proofs are retained to support this refinement, rather than being treated as a set-model proof. +## Production environment fragment + +The axiom policy is relative: **every model of the source axioms extends to a +model of the checked environment, preserving the axiom interpretations**. +The axiom interface starts empty and contains exactly the listed source +axioms. Its types must be closed and refer only to that interface. A concrete +`Realizes` witness supplies their interpretation; checking an axiom's type +does not manufacture an inhabitant. This model-existence hypothesis is not +identified with syntactic consistency of an arbitrary axiom theory. + +The initial fragment covers monomorphic standalone definitions, theorems, +and opaque definitions whose values are either closed universe terms or +references to preceding interface entries. Examples include: + +```lean +axiom P : Prop +axiom p : P +def q : P := p +theorem r : P := q +def typeAlias : Type := Prop +``` + +`AtomicEnvironmentFragment` records the precise execution boundary: + +- Every source key occurs in the exact `buildAnonWork` result, and every work + item is represented by an axiom or a definition. Standalone lookup, routing, + and reset witnesses identify the actual checked `KConst`. +- Value inference misses both cache partitions. Constant lookup agrees with + an already admitted monomorphic type; empty level substitution preserves + that type. Sort inference retains the finite interning coherence and + address-faithfulness premises. +- Conversion takes the initial hash-equality path, with faithfulness of the + compared expressions. General reduction and conversion caches are outside + this fragment. +- Definitions are added in a dependency order with fresh references. Their + runtime observations still use the states reached in the original serial + work order, including cache clearing. `AtomicDefinitionRun.no_self_alias` + proves that a fresh definition cannot justify its own type by referring to + itself. +- `checkEnvAnon` returns `.ok results` **and every result row has no error**. + The outer `.ok` alone does not mean that the declarations passed. + +These are operational and representation witnesses, not a checker-soundness +callback or a supplied typing proof. A caller must establish them for a run; +this change does not automatically derive them from arbitrary Ixon syntax. +The proof follows the public checker through validation, type inference, +the theorem guard, value inference, and conversion. Successful value +inference constructs the body typing used to extend the preceding model. +Lambdas, applications, polymorphic instantiation, inductives, coordinated +blocks, and other conversion paths remain outside this slice. + +`checkEnvAnon_atomic_represents_source` assigns every source address an +interface whose type reads the exact declaration reached by production +lookup. Definition interfaces also retain their checked bodies. This is a +statement about the production ingress/lookup result; the independent +serialized-Ixon reader refinement remains a separate boundary. + +The no-False corollary takes a model of the initial axiom interface in which +the designated false type is empty. Model extension preserves that value, +so no resulting declaration can inhabit it. It does not blacklist axiom +names or merely exclude declarations literally written with type `False`. + ## Trust checks The audits traverse checked declaration types and bodies, including inductive @@ -98,10 +164,15 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 18 exact theorem boundaries. Its remaining -native assumptions are explicitly named proofs reached through production -smart-constructor code; the model's no-False theorem itself uses only -`propext`, `Classical.choice`, and `Quot.sound`, with set theory as a hypothesis. +The consistency target checks 36 exact theorem boundaries. The production +environment roots retain four existing generated output-length proofs, +reached through expression/universe construction, names, and the full +production method table. They introduce no new native proofs. The model +extension lemma and `ModelTyping.no_false` use only `propext`, +`Classical.choice`, and `Quot.sound`, with set theory as a hypothesis. +The new production roots additionally forbid the earlier abstract +`CheckSuccessSound`/`SupportedCheckFragment` interfaces and the independent +certificate validator in their dependency closures. ## Certified host adapters @@ -129,6 +200,7 @@ tests are outside the host gate. | --- | --- | | Certified checker contracts | [`Ix/Kernel/Certified.lean`](../Ix/Kernel/Certified.lean), [`CertifiedClaims.lean`](../Ix/Kernel/CertifiedClaims.lean) | | Direct production refinement and its audit | [`Ix/Kernel/Verify/Consistency.lean`](../Ix/Kernel/Verify/Consistency.lean) | +| Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | | Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | | Host commands, receipts, and frozen regression evidence | [Certified checking guide](certified-checking.md) | | Concrete set-theory instance | [Separate model package](../Models/SetTheory/README.md) | @@ -142,3 +214,8 @@ necessary named-specification imports and exact hash-axiom audit updates. The new compiler development, circuit changes and certificate VM pilot are deferred. The external `lean4lean` dependency and its benchmark/test targets are removed; all verification dependencies are local to Ix. + +The subsequent production-fragment change adds direct inference, declaration, +and serial-environment proofs on top of that extraction. It changes no +production checker behavior and adds regression cases to the existing kernel +unit gate. diff --git a/docs/theory.md b/docs/theory.md index be2cd142e..6aa76b3a2 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -22,6 +22,12 @@ These are relative consistency results for the certified interface. The and proves direct model results for universe comparison, structural expression reading, interning, and the production sort-inference branch. The certified host adapters connect authenticated serialized Ixon to model admission. +A production fragment for monomorphic aliases and closed universe terms now +proves that `checkEnvAnon` preserves a supplied model of its source axiom set, +under explicit operational and representation witnesses. Its no-False +corollary preserves the axiom model's empty interpretation of the false type; +it does not assume that arbitrary source axioms are consistent. See the +[fragment contract](kernel-verification.md#production-environment-fragment). A complete bridge from arbitrary `Ix.Kernel.checkEnvAnon` success or compiler/backend execution remains unfinished. diff --git a/lakefile.lean b/lakefile.lean index 5ac660b42..b174c29c0 100644 --- a/lakefile.lean +++ b/lakefile.lean @@ -254,8 +254,8 @@ lean_lib IxKernelVerify where end IxKernelVerify -/- Direct refinement of production operations into the set model, with its -own exact axiom audit. This is distinct from whole-checker soundness. -/ +/- Direct production refinement, including the atomic environment fragment, +with its own exact axiom audit. Whole-checker soundness remains separate. -/ lean_lib IxKernelConsistency where roots := #[`Ix.Kernel.Verify.Consistency] globs := #[.andSubmodules `Ix.Kernel.Verify.Consistency] From 8172135c663d562caf15f91d48f20de0a5aa84c1 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 07:51:29 -0400 Subject: [PATCH 03/63] Simplify kernel certification proofs and documentation Replace repeated state-monad case splits with a local bind-success lemma and direct EStateM.bind unfolding. Name the components of soundness results and simplify result-preservation and entry-equality proofs. Keep model extension's explicit import in the environment layer. Public theorem statements and their assumptions are unchanged. Tighten comments and documentation around the supported production fragment, the source-axiom model hypothesis, and remaining refinement obligations. Collect provenance details together and link the Mathlib model to the fragment contract. Validation: - lake build --wfail IxKernelConsistency: 36 exact theorem audits passed. - lake test --wfail -- tc-unit: 403 checks passed. - git diff --check: passed. --- Ix/Kernel/Verify/Consistency.lean | 2 +- Ix/Kernel/Verify/Consistency/Atomic.lean | 43 ++--- Ix/Kernel/Verify/Consistency/Audit.lean | 2 +- Ix/Kernel/Verify/Consistency/Environment.lean | 84 ++++------ Ix/Kernel/Verify/Consistency/Production.lean | 150 +++++++----------- Models/SetTheory/README.md | 10 +- Tests/Ix/Kernel/Consistency.lean | 5 +- docs/certified-checking.md | 14 +- docs/kernel-verification.md | 139 +++++++--------- docs/theory.md | 23 +-- 10 files changed, 189 insertions(+), 283 deletions(-) diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 13dc8657e..e6c5a00b0 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -17,7 +17,7 @@ import Ix.Kernel.Verify.Consistency.Audit This library connects production kernel operations to `Ix.Theory`. Each transport keeps its representation, arithmetic, and dependency assumptions -explicit. A production `checkEnvAnon` fragment now preserves models of its +explicit. A production `checkEnvAnon` fragment preserves models of its axiom set for monomorphic aliases and closed sorts under the stated execution resources. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Atomic.lean b/Ix/Kernel/Verify/Consistency/Atomic.lean index 12565de84..d6317d312 100644 --- a/Ix/Kernel/Verify/Consistency/Atomic.lean +++ b/Ix/Kernel/Verify/Consistency/Atomic.lean @@ -4,15 +4,14 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.Infer -import Ix.Theory.Model.Extension /-! # Atomic production inference -The first declaration fragment admits closed sorts and monomorphic references. -References use the type of an already admitted interface entry. The premises -below describe concrete cache misses, lookup results, and finite interning -support; none assumes a typing judgment or the soundness of a checker method. +The declaration fragment admits closed sorts and monomorphic references. +References use the type of an existing interface entry. Cache misses, lookup +agreement, and finite interning support connect each production result to its +model type. -/ namespace Ix.Kernel.Consistency @@ -21,15 +20,6 @@ open Theory Theory.Model universe u v -private theorem runTcBind {α β : Type} (x : TcM .anon α) (k : α → TcM .anon β) - (state : TcState .anon) : - EStateM.bind x k state = match x state with - | .ok value after => k value after - | .error err after => .error err after := by - show EStateM.bind x k state = _ - unfold EStateM.bind - cases x state <;> rfl - /-- The two inference-cache partitions miss at the actual key computed by production. Both misses are required, so this applies in either policy mode. -/ structure UncachedInference (before : TcState .anon) (term : KExpr .anon) where @@ -51,9 +41,9 @@ theorem infer_uncached_success {term type : KExpr .anon} unfold RecM.infer RecM.inferWith at accepted simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted change EStateM.bind (TcM.inferKey term) _ before = _ at accepted - rw [runTcBind, miss.keyRun] at accepted + rw [EStateM.bind, miss.keyRun] at accepted change EStateM.bind (get : TcM .anon (TcState .anon)) _ miss.keyed = _ at accepted - rw [runTcBind, show (get : TcM .anon (TcState .anon)) miss.keyed = + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) miss.keyed = .ok miss.keyed miss.keyed from rfl] at accepted simp only at accepted rw [miss.fullMiss] at accepted @@ -63,9 +53,9 @@ theorem infer_uncached_success {term type : KExpr .anon} change EStateM.bind (RecM.inferUncached RecM.inferCall false term methods) _ miss.keyed = _ at accepted cases run : RecM.inferUncached RecM.inferCall false term methods miss.keyed with - | error err failed => rw [runTcBind, run] at accepted; contradiction + | error err failed => rw [EStateM.bind, run] at accepted; contradiction | ok ty state => - rw [runTcBind, run] at accepted + rw [EStateM.bind, run] at accepted change EStateM.Result.ok ty { state with env := { state.env with inferCache := state.env.inferCache.insert miss.key ty } } = .ok type after at accepted cases accepted @@ -74,16 +64,16 @@ theorem infer_uncached_success {term type : KExpr .anon} simp only [policy, if_true] at accepted simp only [ReaderT.run_bind] at accepted change EStateM.bind (get : TcM .anon (TcState .anon)) _ miss.keyed = _ at accepted - rw [runTcBind, show (get : TcM .anon (TcState .anon)) miss.keyed = + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) miss.keyed = .ok miss.keyed miss.keyed from rfl] at accepted simp only at accepted rw [miss.onlyMiss] at accepted change EStateM.bind (RecM.inferUncached RecM.inferCall true term methods) _ miss.keyed = _ at accepted cases run : RecM.inferUncached RecM.inferCall true term methods miss.keyed with - | error err failed => rw [runTcBind, run] at accepted; contradiction + | error err failed => rw [EStateM.bind, run] at accepted; contradiction | ok ty state => - rw [runTcBind, run] at accepted + rw [EStateM.bind, run] at accepted change EStateM.Result.ok ty { state with env := { state.env with inferOnlyCache := state.env.inferOnlyCache.insert miss.key ty } } = .ok type after at accepted cases accepted @@ -117,8 +107,7 @@ inductive AtomicInferenceSupport {β : Type u} namespace AtomicInferenceSupport -/-- This fragment's typing rule is proved from the sort rule or an existing -constant interface, independently of any production success assumption. -/ +/-- The model's sort and constant rules type each supported syntax form. -/ theorem typing {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {term : KExpr .anon} {before : TcState .anon} {body type : AExpr β} (support : AtomicInferenceSupport resolve entries term before body type) : @@ -163,9 +152,9 @@ theorem output {β : Type u} {resolve : Address → Option (ConstRef β)} simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted change EStateM.bind (TcM.getConst id) _ before = _ at accepted cases got : TcM.getConst id before with - | error err failed => rw [runTcBind, got] at accepted; contradiction + | error err failed => rw [EStateM.bind, got] at accepted; contradiction | ok concrete loaded => - rw [runTcBind, got] at accepted + rw [EStateM.bind, got] at accepted simp only at accepted split at accepted · contradiction @@ -200,9 +189,7 @@ structure AtomicInference {β : Type u} (resolve : Address → Option (ConstRef misses : UncachedInference before term support : AtomicInferenceSupport resolve entries term misses.keyed body type -/-- A successful public inference in this fragment returns the semantic type -of the exact source tree. The inference implementation supplies the output -agreement; callers do not supply a typing or method-soundness premise. -/ +/-- Successful production inference returns the model type of the source tree. -/ theorem AtomicInference.sound {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {before after : TcState .anon} {term inferred : KExpr .anon} {body type : AExpr β} {methods : Methods .anon} diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 7d49bf4a3..3a6684b17 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -46,7 +46,7 @@ private def productionRoots : Array Lean.Name := #[ ``checkEnvAnon_atomic_no_false ] -/-- The new production roots must not acquire a checker-soundness assumption +/-- Production roots must not acquire a checker-soundness assumption or invoke the independent certificate validator to establish acceptance. -/ private def forbiddenProduction : Array Lean.Name := #[ `Ix.Kernel.CheckSuccessSound, diff --git a/Ix/Kernel/Verify/Consistency/Environment.lean b/Ix/Kernel/Verify/Consistency/Environment.lean index f30a54e1f..9569ef804 100644 --- a/Ix/Kernel/Verify/Consistency/Environment.lean +++ b/Ix/Kernel/Verify/Consistency/Environment.lean @@ -4,15 +4,15 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.Production +import Ix.Theory.Model.Extension /-! # Relative consistency of a production environment fragment -The initial interface contains exactly the supplied axiom declarations. A -dependency order adds fresh monomorphic definitions, using successful calls -from the real serial `checkEnvAnon` run. Each addition constructs its value in -every model of the previous interface. Thus definitions preserve any given -model of the axiom set; no axiom is silently assumed to be inhabited. +The initial interface contains exactly the supplied axiom declarations. +Successful calls from the serial `checkEnvAnon` run add fresh monomorphic +definitions in dependency order. Each addition extends every model of the +previous interface and preserves its existing interpretations. -/ namespace Ix.Kernel.Consistency @@ -52,14 +52,9 @@ private theorem finish_results (cfg : CheckCfg) (before : AnonCheckLoopState) private theorem item_keeps_result (cfg : CheckCfg) (before : AnonCheckLoopState) (item : AnonWorkItem) {result : CheckResult} (found : result ∈ before.results) : result ∈ (runAnonCheckItem cfg before item).results := by - cases run : TcM.checkConst (⟨item.primary, ()⟩ : KId .anon) before.checker with - | ok value after => - cases value - simp only [runAnonCheckItem, EStateM.run, run, finish_results] - exact Array.mem_append.mpr (.inl found) - | error err after => - simp only [runAnonCheckItem, EStateM.run, run, finish_results] - exact Array.mem_append.mpr (.inl found) + cases run : TcM.checkConst (⟨item.primary, ()⟩ : KId .anon) before.checker <;> + simp only [runAnonCheckItem, EStateM.run, run, finish_results] <;> + exact Array.mem_append.mpr (.inl found) private theorem list_keeps_result (cfg : CheckCfg) (work : List AnonWorkItem) (before : AnonCheckLoopState) {result : CheckResult} (found : result ∈ before.results) : @@ -97,14 +92,12 @@ theorem WorkPosition.check_success {env : Ixon.Env} {cfg : CheckCfg} (list_keeps_result cfg position.trailing _ appears) simp [failed] at contradiction -/-- The semantic entry published for a monomorphic definition. No reduction -equations or extra typed facts are manufactured by this fragment. -/ +/-- A monomorphic definition entry, with its body and no additional laws or facts. -/ def definitionEntry {β : Type u} (body type : AExpr β) : ConstantEntry β := { universes := 0, type, body := some body } -/-- Body typing already includes hereditary validity of the declared type. -This constructs a model extension directly, without separately assuming a -model for the pending declaration. -/ +/-- Interpret a fresh definition's body to extend the preceding model. +Body typing supplies hereditary validity of the declared type. -/ theorem extend_atomic_definition {β : Type u} [DecidableEq β] {entries : Model.Environment β} {ref : ConstRef β} {body type : AExpr β} (wellFormed : entries.WF) (fresh : entries ref = none) @@ -133,12 +126,12 @@ theorem extend_atomic_definition {β : Type u} [DecidableEq β] exact (typed V constants realizes levels env (Context.valid_nil constants levels env)).2.2 · intro candidate present levels _ env change some body = some candidate at present - cases Option.some.inj present + cases present exact (agrees.wellDenoted bodyRefs levels env).mpr (typed V constants realizes levels env (Context.valid_nil constants levels env)).1 · intro candidate present levels _ env change some body = some candidate at present - cases Option.some.inj present + cases present rw [value, agrees.interp bodyRefs] · intro law present exact (List.not_mem_nil present).elim @@ -164,22 +157,21 @@ private theorem insert_definition_wf {β : Type u} [DecidableEq β] apply wellFormed.insert typeScope · intro candidate present change some body = some candidate at present - cases Option.some.inj present + cases present exact bodyScope · exact typeRefs · intro candidate present change some body = some candidate at present - cases Option.some.inj present + cases present exact bodyRefs · simp [definitionEntry] · simp [definitionEntry] · simp [definitionEntry] · simp [definitionEntry] -/-- A dependency order over the checked definitions. Each reference is fresh -and each atomic body reads only the preceding interface. The work position -retains the original serial order, which may differ from this dependency -order. No semantic acceptance premise appears in this plan. -/ +/-- Definitions in dependency order, with fresh references and bodies that +read the preceding interface. Work positions retain the runtime serial order, +which may differ from this dependency order. -/ inductive AtomicDefinitionPlan {β : Type u} [DecidableEq β] (env : Ixon.Env) (cfg : CheckCfg) (work : Array AnonWorkItem) (resolve : Address → Option (ConstRef β)) : @@ -219,9 +211,7 @@ private theorem anon_id (id : KId .anon) : (⟨id.addr, ()⟩ : KId .anon) = id cases id with | mk addr name => cases name; rfl -/-- Every accepted definition in the dependency order extends the preceding -model. The initial interface need only have a model; its axioms need not be -part of a fixed whitelist. -/ +/-- Accepted definitions extend every model of the preceding interface. -/ theorem AtomicDefinitionPlan.sound {β : Type u} [DecidableEq β] {env : Ixon.Env} {cfg : CheckCfg} {work : Array AnonWorkItem} {resolve : Address → Option (ConstRef β)} @@ -238,16 +228,15 @@ theorem AtomicDefinitionPlan.sound {β : Type u} [DecidableEq β] | @cons before after rest spec resolved fresh position run tail ih => obtain ⟨checkedState, accepted⟩ := position.check_success succeeded rw [anon_id] at accepted - have bodySound := run.sound wellFormed accepted + obtain ⟨_, _, bodyScope, typeScope, bodyRefs, typeRefs, typed⟩ := + run.sound wellFormed accepted have formed : (before.insert spec.ref spec.entry).WF := - insert_definition_wf wellFormed bodySound.2.2.1 bodySound.2.2.2.1 - bodySound.2.2.2.2.1 bodySound.2.2.2.2.2.1 + insert_definition_wf wellFormed bodyScope typeScope bodyRefs typeRefs obtain ⟨finalFormed, preserve⟩ := ih formed refine ⟨finalFormed, ?_⟩ intro V _ constants realizes obtain ⟨next, nextModel, agrees⟩ := extend_atomic_definition wellFormed fresh - bodySound.2.2.1 bodySound.2.2.2.2.1 bodySound.2.2.2.2.2.1 - bodySound.2.2.2.2.2.2 constants realizes + bodyScope bodyRefs typeRefs typed constants realizes obtain ⟨final, finalModel, finalAgrees⟩ := preserve V next nextModel refine ⟨final, finalModel, ?_⟩ intro ref entry present levels @@ -273,15 +262,15 @@ theorem AtomicDefinitionPlan.represents {β : Type u} [DecidableEq β] | @cons before after rest spec resolved fresh position run tail ih => obtain ⟨checkedState, accepted⟩ := position.check_success succeeded rw [anon_id] at accepted - have bodySound := AtomicDefinitionRun.sound.{u,0} run wellFormed accepted + obtain ⟨valueReads, typeReads, bodyScope, typeScope, bodyRefs, typeRefs, _⟩ := + AtomicDefinitionRun.sound.{u,0} run wellFormed accepted have formed : (before.insert spec.ref spec.entry).WF := - insert_definition_wf wellFormed bodySound.2.2.1 bodySound.2.2.2.1 - bodySound.2.2.2.2.1 bodySound.2.2.2.2.2.1 + insert_definition_wf wellFormed bodyScope typeScope bodyRefs typeRefs intro candidate present rcases List.mem_cons.mp present with same | later · subst candidate exact ⟨resolved, tail.extends _ _ (Model.Environment.insert_same ..), - bodySound.1, bodySound.2.1⟩ + valueReads, typeReads⟩ · exact ih formed candidate later private theorem AtomicDefinitionPlan.locations {β : Type u} [DecidableEq β] @@ -362,9 +351,8 @@ private theorem AtomicEnvironmentFragment.serial_success {β : Type u} [Decidabl cases accepted exact succeeded -/-- A successful `checkEnvAnon` run in the supported fragment extends every -model of its axiom set while preserving all axiom interpretations. This is a -relative model theorem, not a claim that arbitrary axioms are consistent. -/ +/-- A successful `checkEnvAnon` run in the fragment extends every model of +its axiom set while preserving all axiom interpretations. -/ theorem checkEnvAnon_atomic_preserves_model {β : Type u} [DecidableEq β] {env : Ixon.Env} {cfg : CheckCfg} {resolve : Address → Option (ConstRef β)} (fragment : AtomicEnvironmentFragment env cfg resolve) @@ -416,18 +404,16 @@ theorem checkEnvAnon_atomic_represents_source {β : Type u} [DecidableEq β] observation.reads, rfl, rfl⟩ simpa only [anon_id] using Nonempty.intro observation.path · cases AnonWorkItem.standalone.inj same - have represents := represented spec listed + obtain ⟨resolved, installed, valueReads, typeReads⟩ := represented spec listed obtain ⟨position, path⟩ := fragment.plan.locations spec listed obtain ⟨after, run⟩ := position.check_success serial - refine ⟨spec.ref, spec.entry, represents.1, represents.2.1, spec.input.constant, - ⟨(position.state env cfg).checker, after, run, ?_⟩, represents.2.2.2, - rfl, spec.body, rfl, represents.2.2.1⟩ + refine ⟨spec.ref, spec.entry, resolved, installed, spec.input.constant, + ⟨(position.state env cfg).checker, after, run, ?_⟩, typeReads, + rfl, spec.body, rfl, valueReads⟩ simpa only [anon_id] using path -/-- No declaration in the resulting environment can inhabit a type whose -axiom-model interpretation is empty. In particular this applies to `False`. -The empty interpretation is preserved, rather than assumed for the completed -environment. -/ +/-- No declaration can inhabit an axiom type interpreted as empty, such as +`False`: model extension preserves the initial empty interpretation. -/ theorem checkEnvAnon_atomic_no_false {β : Type u} [DecidableEq β] {env : Ixon.Env} {cfg : CheckCfg} {resolve : Address → Option (ConstRef β)} (fragment : AtomicEnvironmentFragment env cfg resolve) diff --git a/Ix/Kernel/Verify/Consistency/Production.lean b/Ix/Kernel/Verify/Consistency/Production.lean index 320b46722..b48ea8f97 100644 --- a/Ix/Kernel/Verify/Consistency/Production.lean +++ b/Ix/Kernel/Verify/Consistency/Production.lean @@ -9,15 +9,13 @@ import Ix.Kernel.Verify.Consistency.Atomic /-! # Standalone production declaration checks -These theorems invert the real public checker, including its error isolation, +These theorems invert the public checker, including its error isolation, initial lazy lookup, block routing, per-constant reset, validation, type inference, theorem guard, value inference, and conversion. The supported conversion path is the initial address-equality branch. Finite address faithfulness connects that comparison to the exact model syntax. -The resource records contain operational equations and structural interface -agreement. They do not contain a typing proof, a method-soundness callback, -or an assumption that an accepted declaration already has a model. +Operational equations and interface agreement suffice to derive body typing. -/ namespace Ix.Kernel.Consistency @@ -26,17 +24,19 @@ open Theory Theory.Model universe u v -private theorem runTcBind {α β : Type} (x : TcM .anon α) (k : α → TcM .anon β) - (state : TcState .anon) : - EStateM.bind x k state = match x state with - | .ok value after => k value after - | .error err after => .error err after := by - unfold EStateM.bind - cases x state <;> rfl - -/-- The concrete path of a standalone public check. This ties the member -declaration to both production lookup and the actual reset, without assuming -that block coordination or lazy ingress preserves an arbitrary predicate. -/ +private theorem bind_success {α β : Type} {x : TcM .anon α} {k : α → TcM .anon β} + {before after : TcState .anon} {value : β} + (accepted : EStateM.bind x k before = .ok value after) : + ∃ intermediate state, x before = .ok intermediate state ∧ + k intermediate state = .ok value after := by + rw [EStateM.bind] at accepted + cases run : x before with + | error err state => rw [run] at accepted; contradiction + | ok intermediate state => + rw [run] at accepted + exact ⟨intermediate, state, rfl, accepted⟩ + +/-- The lookup, routing, and reset states reached by a standalone public check. -/ structure StandalonePrefix (id : KId .anon) (before : TcState .anon) (concrete : KConst .anon) where first : KConst .anon @@ -50,9 +50,7 @@ structure StandalonePrefix (id : KId .anon) (before : TcState .anon) resetRun : TcM.reset routed = .ok () reset memberGet : TcM.getConst id reset = .ok concrete ready -/-- Success at the public boundary entails success of the exact member -identified by the standalone path. In particular, an error cannot be -turned into acceptance by the public cache rollback wrapper. -/ +/-- Public success entails member success through the error-isolation wrapper. -/ theorem StandalonePrefix.member_success {id : KId .anon} {before after : TcState .anon} {concrete : KConst .anon} (path : StandalonePrefix id before concrete) @@ -70,19 +68,19 @@ theorem StandalonePrefix.member_success {id : KId .anon} unfold RecM.checkConst at run simp only [ReaderT.run_bind, ReaderT.run_monadLift] at run change EStateM.bind (TcM.getConst id) _ before = _ at run - rw [runTcBind, path.firstGet] at run + rw [EStateM.bind, path.firstGet] at run change EStateM.bind ((RecM.coordinatedBlockFor path.first).run (methodsN before.recFuel.toNat)) _ path.loaded = _ at run - rw [runTcBind, path.route] at run + rw [EStateM.bind, path.route] at run change (RecM.checkConstMemberFresh id).run (methodsN before.recFuel.toNat) path.routed = .ok () after at run unfold RecM.checkConstMemberFresh at run simp only [ReaderT.run_bind, ReaderT.run_monadLift] at run change EStateM.bind TcM.reset _ path.routed = _ at run - rw [runTcBind, path.resetRun] at run + rw [EStateM.bind, path.resetRun] at run change EStateM.bind (TcM.getConst id) _ path.reset = _ at run - rw [runTcBind, path.memberGet] at run + rw [EStateM.bind, path.memberGet] at run exact run /-- Monomorphic definition data. Its complete concrete declaration is the @@ -99,9 +97,8 @@ structure DefinitionInput where def DefinitionInput.constant (input : DefinitionInput) : KConst .anon := .defn () () input.kind input.safety input.hints 0 input.type input.value () input.block -/-- Exact successful observations through a definition's value-conversion -call. Later safety checks may still reject; the extraction theorem requires -the entire member check to have succeeded. -/ +/-- The execution prefix through value conversion. A successful member check +also passes the subsequent safety checks. -/ structure DefinitionBodyTrace (input : DefinitionInput) (methods : Methods .anon) (before : TcState .anon) where validated : TcState .anon @@ -121,8 +118,7 @@ structure DefinitionBodyTrace (input : DefinitionInput) (methods : Methods .anon conversionRun : (RecM.isDefEq inferredValue input.type).run methods conversionStart = .ok true conversionEnd -/-- Invert the production declaration branch rather than assuming that its -acceptance establishes either type or value typing. -/ +/-- Extract the execution trace from a successful production member check. -/ theorem definition_body_trace {input : DefinitionInput} {methods : Methods .anon} {before after : TcState .anon} (accepted : (RecM.checkConstMember input.id input.constant).run methods before = @@ -130,61 +126,33 @@ theorem definition_body_trace {input : DefinitionInput} {methods : Methods .anon unfold RecM.checkConstMember at accepted simp only [DefinitionInput.constant, Mode.F.hasDups, Bool.false_eq_true, if_false, ReaderT.run_bind] at accepted - cases validation : (RecM.validateConstWellScoped input.constant).run methods before with - | error err failed => - change EStateM.bind ((RecM.validateConstWellScoped input.constant).run methods) - _ before = _ at accepted - rw [runTcBind, validation] at accepted - contradiction - | ok result validated => - cases result - change EStateM.bind ((RecM.validateConstWellScoped input.constant).run methods) - _ before = _ at accepted - rw [runTcBind, validation] at accepted - change EStateM.bind ((RecM.infer input.type).run methods) _ validated = _ at accepted - cases typeRun : (RecM.infer input.type).run methods validated with - | error err failed => rw [runTcBind, typeRun] at accepted; contradiction - | ok inferredType typeState => - rw [runTcBind, typeRun] at accepted - change EStateM.bind ((RecM.ensureSortDirect inferredType).run methods) - _ typeState = _ at accepted - cases sortRun : (RecM.ensureSortDirect inferredType).run methods typeState with - | error err failed => rw [runTcBind, sortRun] at accepted; contradiction - | ok level valueStart => - rw [runTcBind, sortRun] at accepted - simp only at accepted - by_cases guard : input.kind == .thm && !univEq level .mkZero - · simp only [guard, if_true] at accepted - contradiction - · simp only [guard, Bool.false_eq_true, if_false, ReaderT.run_bind] at accepted - cases valueRun : (RecM.infer input.value).run methods valueStart with - | error err failed => - change EStateM.bind ((RecM.infer input.value).run methods) - _ valueStart = _ at accepted - rw [runTcBind, valueRun] at accepted - contradiction - | ok inferredValue conversionStart => - change EStateM.bind ((RecM.infer input.value).run methods) - _ valueStart = _ at accepted - rw [runTcBind, valueRun] at accepted - change EStateM.bind ((RecM.isDefEq inferredValue input.type).run methods) - _ conversionStart = _ at accepted - cases conversionRun : (RecM.isDefEq inferredValue input.type).run methods - conversionStart with - | error err failed => rw [runTcBind, conversionRun] at accepted; contradiction - | ok answer conversionEnd => - rw [runTcBind, conversionRun] at accepted - cases answer with - | false => - simp only [Bool.not_false, if_true] at accepted - contradiction - | true => - exact ⟨{ - validated, inferredType, typeState, level, valueStart, - inferredValue, conversionStart, conversionEnd, - validationRun := validation, typeRun, sortRun, - theoremGuard := Bool.eq_false_iff.mpr guard, - valueRun, conversionRun }⟩ + change EStateM.bind ((RecM.validateConstWellScoped input.constant).run methods) + _ before = _ at accepted + obtain ⟨⟨⟩, validated, validationRun, accepted⟩ := bind_success accepted + change EStateM.bind ((RecM.infer input.type).run methods) _ validated = _ at accepted + obtain ⟨inferredType, typeState, typeRun, accepted⟩ := bind_success accepted + change EStateM.bind ((RecM.ensureSortDirect inferredType).run methods) + _ typeState = _ at accepted + obtain ⟨level, valueStart, sortRun, accepted⟩ := bind_success accepted + by_cases guard : input.kind == .thm && !univEq level .mkZero + · simp only [guard, if_true] at accepted + contradiction + · simp only [guard, Bool.false_eq_true, if_false, ReaderT.run_bind] at accepted + change EStateM.bind ((RecM.infer input.value).run methods) _ valueStart = _ at accepted + obtain ⟨inferredValue, conversionStart, valueRun, accepted⟩ := bind_success accepted + change EStateM.bind ((RecM.isDefEq inferredValue input.type).run methods) + _ conversionStart = _ at accepted + obtain ⟨answer, conversionEnd, conversionRun, accepted⟩ := bind_success accepted + cases answer with + | false => + simp only [Bool.not_false, if_true] at accepted + contradiction + | true => + exact ⟨{ + validated, inferredType, typeState, level, valueStart, + inferredValue, conversionStart, conversionEnd, + validationRun, typeRun, sortRun, + theoremGuard := Bool.eq_false_iff.mpr guard, valueRun, conversionRun }⟩ /-- Operational support for the selected production definition fragment. Resources are required only at the states exposed by successful body traces. @@ -215,16 +183,15 @@ theorem AtomicDefinitionRun.sound {β : Type u} body.ReferencesIn entries ∧ type.ReferencesIn entries ∧ TypingClaim.{u,v} entries [] body type := by obtain ⟨trace⟩ := definition_body_trace (fragment.path.member_success accepted) - have result := (fragment.inference trace).sound trace.valueRun + obtain ⟨valueReads, typeReads, typed⟩ := (fragment.inference trace).sound trace.valueRun have same := beq_readExpr? (resolve := resolve) (fragment.faithful trace) (fragment.hashPath trace) - have scope := (fragment.inference trace).support.scopeAndReferences wellFormed - exact ⟨result.1, same.symm.trans result.2.1, scope.1, scope.2.1, - scope.2.2.1, scope.2.2.2, result.2.2⟩ + obtain ⟨bodyScope, typeScope, bodyRefs, typeRefs⟩ := + (fragment.inference trace).support.scopeAndReferences wellFormed + exact ⟨valueReads, same.symm.trans typeReads, bodyScope, typeScope, bodyRefs, typeRefs, typed⟩ -/-- A fresh definition cannot use itself as the atomic value that justifies -its own type. This exclusion follows from the preceding interface, without -any assumption about the consistency of the axioms. -/ +/-- A fresh definition cannot justify its type through a self-reference: +its atomic value must reference the preceding interface. -/ theorem AtomicDefinitionRun.no_self_alias {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {input : DefinitionInput} {before after : TcState .anon} {body type : AExpr β} @@ -233,12 +200,11 @@ theorem AtomicDefinitionRun.no_self_alias {β : Type u} (resolved : resolve input.id.addr = some ref) (fresh : entries ref = none) {info : ExprInfo .anon} (self : input.value = .const input.id #[] info) (accepted : TcM.checkConst input.id before = .ok () after) : False := by - have result := AtomicDefinitionRun.sound.{u,0} fragment wellFormed accepted - have reads := result.1 + obtain ⟨reads, _, _, _, references, _, _⟩ := + AtomicDefinitionRun.sound.{u,0} fragment wellFormed accepted rw [self] at reads simp [readExpr?, resolved] at reads have bodyEq := AExpr.eq_const_of_erase_eq reads.symm - have references := result.2.2.2.2.1 rw [bodyEq] at references have present := references ref (by simp [AExpr.references]) simp only [fresh, Option.isSome_none, Bool.false_eq_true] at present diff --git a/Models/SetTheory/README.md b/Models/SetTheory/README.md index e74498555..74f64fe7e 100644 --- a/Models/SetTheory/README.md +++ b/Models/SetTheory/README.md @@ -17,16 +17,16 @@ The construction includes the interface's Lean-level replacement scheme: Mathlib's `Classical.allZFSetDefinable` supplies images of arbitrary functions `ZFSet → ZFSet`. The audit traverses the theorem's checked types, bodies, and constructor fields and permits exactly `propext`, `Classical.choice`, and -`Quot.sound`. Inaccessible cardinals remain an explicit -hypothesis, rather than an added Lean axiom. +`Quot.sound`. Inaccessible cardinals remain an explicit theorem hypothesis. The package imports the actual Ix interface by a path dependency on the repository root. Mathlib is confined to this package; ordinary Ix and `Ix.Theory` builds do not depend on it. This construction supplies the set-theoretic assumption used by the [consistency model](../../docs/theory.md). -The connection from the production checker to the certified interface is a -separate proof obligation. A converse from the interface to inaccessible -cardinals is not proved here. +The [production fragment](../../docs/kernel-verification.md#production-environment-fragment) +uses this assumption to preserve models of source axioms. Full checker +refinement and a converse from the interface to inaccessible cardinals are +separate proof obligations. ## Build diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 7b9376856..03862215f 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -6,9 +6,8 @@ public import Tests.Ix.Kernel.IxonFixtures /-! Production regressions for the atomic consistency fragment. These execute -the real lazy serial driver on content-addressed Ixon declarations. The -formal results and their resource premises are checked separately by -`IxKernelConsistency`; these tests do not substitute for those proofs. +the lazy serial driver on content-addressed Ixon declarations. The fragment's +theorems and resource premises are checked by `IxKernelConsistency`. -/ namespace Tests.Kernel.Consistency diff --git a/docs/certified-checking.md b/docs/certified-checking.md index c0f8b6057..b565d7b30 100644 --- a/docs/certified-checking.md +++ b/docs/certified-checking.md @@ -129,12 +129,10 @@ inventory records five BLAKE3 foreign operations and 130 recursion workers with safe source definitions; it permits no partial opaque worker source or executable replacement. -This stronger traversal exposed a native-evaluation axiom that the historical -imported axiom summaries missed. It came from the BLAKE3 helper's 32-byte -output bound. `Address.blake3` now supplies a kernel-checked proof of that -bound. The wrapper is definitionally equal to the former hash operation, -and generated source/envelope corpora are unchanged. -The affected kernel and source-compiler audits also remove this dependency. +`Address.blake3` supplies a kernel-checked proof of its 32-byte output bound, +replacing a native-evaluation axiom missed by the historical imported summaries. +The wrapper is definitionally equal to the former hash operation. The frozen +corpora check that source and envelope bytes are unchanged. | Actual command corpus | Fixtures | Accepted | Rejected | | --- | ---: | ---: | ---: | @@ -153,8 +151,8 @@ non-VM field; only `pilotDeclined` and `pilotDeclines` are projected away from the three historical reports that mixed host and VM checks. The VM pilot, packet encoder and VM execution suite are archived for a later change. CLI JSON requests and aggregate records are compared as parsed values because -the maintained Lean driver formats JSON differently. The archive, source mappings, licenses and -historical reproduction limits are documented in +the maintained Lean driver formats JSON differently. The archive, source +mappings, licenses and historical reproduction limits are documented in [`Tests/Fixtures/Certified`](../Tests/Fixtures/Certified/README.md). The archive recovers the selected adapter sources and fixtures; it does not include the complete historical Ix base checkout. diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 3f8eabaf7..1cd6c31b5 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -6,26 +6,16 @@ proofs. Their named specification and reference implementation lemmas are local under `Ix.Theory.Named`. Building or checking them requires no external formalization repository. -The named development retains the 104 source modules needed by the existing -proof dependency graph, including the inductive fixtures consumed by Ix. -Standalone applications, benchmarks, and unrelated tests are excluded. Its -original source hashes and attribution are recorded in `Ix/Theory/Named/NOTICE` -and `Tests/Theory/NamedManifest.lean`; the Apache license is preserved alongside -the sources. The added axiom-audit helper is authored in Ix. +The certified receipt interface and a fragment of production `checkEnvAnon` +have relative consistency theorems. Full checker consistency remains open. ## Connection to the consistency model -The name-indexed specification and the set model use the same -`Ix.Theory.VLevel`. The set model and its foundational audit remain independent -of the named development. Mathlib is confined to the separate -[set-theory model package](../Models/SetTheory/README.md). - -That package's `IxSetTheoryModel.setTheoryOfCarneiro` constructs the exact -`Ix.Theory.Model.SetTheory` interface on Mathlib's `ZFSet`. It takes an explicit -`OmegaInaccessibles` hypothesis: a strictly increasing countable sequence of -strongly inaccessible cardinals. The universe chain is `V_ (κ n).ord`. -`carneiro_implies_ix` proves model existence under this hypothesis, with a full -dependency audit restricted to Lean's three standard axioms. +The named specification and the set model share `Ix.Theory.VLevel`. The +[model foundation](theory.md) and its audit are independent of the named +development. The separate [Mathlib package](../Models/SetTheory/README.md) +constructs `Ix.Theory.Model.SetTheory` on `ZFSet` from a countable strictly +increasing sequence of strongly inaccessible cardinals. `Ix.Kernel.Verify.Consistency` proves the following direct connections: @@ -47,25 +37,22 @@ dependency audit restricted to Lean's three standard axioms. environment run to model extension. `checkEnvAnon_atomic_no_false` excludes a declaration at an axiom type interpreted as empty, including False. -`ModelTyping.no_false` assumes semantic typing. The production fragment below -constructs that typing from its supported inference and conversion paths. -A complete checker consistency theorem -still requires the remaining inference/conversion cases, cache invariants, -address-to-store resolution, and declaration admission to establish that -postcondition for `checkEnvAnon`. The existing named-calculus proofs are retained -to support this refinement, rather than being treated as a set-model proof. +`ModelTyping.no_false` assumes semantic typing; the production fragment below +derives it for its supported paths. Full checker refinement still requires the +remaining inference and conversion cases, cache invariants, address-to-store +resolution, and declaration admission. The named-calculus proofs provide +support for these obligations. ## Production environment fragment The axiom policy is relative: **every model of the source axioms extends to a model of the checked environment, preserving the axiom interpretations**. -The axiom interface starts empty and contains exactly the listed source -axioms. Its types must be closed and refer only to that interface. A concrete -`Realizes` witness supplies their interpretation; checking an axiom's type -does not manufacture an inhabitant. This model-existence hypothesis is not -identified with syntactic consistency of an arbitrary axiom theory. +The initial interface contains exactly the source axioms, with closed types +that refer only to that interface. A `Realizes` witness supplies a model of +these axioms. The hypothesis is model existence; a theorem connecting +arbitrary syntactic consistency to model existence is outside this result. -The initial fragment covers monomorphic standalone definitions, theorems, +The fragment covers monomorphic standalone definitions, theorems, and opaque definitions whose values are either closed universe terms or references to preceding interface entries. Examples include: @@ -79,9 +66,9 @@ def typeAlias : Type := Prop `AtomicEnvironmentFragment` records the precise execution boundary: -- Every source key occurs in the exact `buildAnonWork` result, and every work - item is represented by an axiom or a definition. Standalone lookup, routing, - and reset witnesses identify the actual checked `KConst`. +- Every source key occurs in the `buildAnonWork` result, and every work item + represents an axiom or a definition. Lookup, routing, and reset witnesses + identify the checked `KConst`. - Value inference misses both cache partitions. Constant lookup agrees with an already admitted monomorphic type; empty level substitution preserves that type. Sort inference retains the finite interning coherence and @@ -89,44 +76,38 @@ def typeAlias : Type := Prop - Conversion takes the initial hash-equality path, with faithfulness of the compared expressions. General reduction and conversion caches are outside this fragment. -- Definitions are added in a dependency order with fresh references. Their - runtime observations still use the states reached in the original serial - work order, including cache clearing. `AtomicDefinitionRun.no_self_alias` +- Definitions are added in dependency order with fresh references. Their + observations use the states reached in the original serial work order, + including cache clearing. `AtomicDefinitionRun.no_self_alias` proves that a fresh definition cannot justify its own type by referring to itself. - `checkEnvAnon` returns `.ok results` **and every result row has no error**. The outer `.ok` alone does not mean that the declarations passed. -These are operational and representation witnesses, not a checker-soundness -callback or a supplied typing proof. A caller must establish them for a run; -this change does not automatically derive them from arbitrary Ixon syntax. -The proof follows the public checker through validation, type inference, -the theorem guard, value inference, and conversion. Successful value -inference constructs the body typing used to extend the preceding model. -Lambdas, applications, polymorphic instantiation, inductives, coordinated -blocks, and other conversion paths remain outside this slice. - -`checkEnvAnon_atomic_represents_source` assigns every source address an -interface whose type reads the exact declaration reached by production -lookup. Definition interfaces also retain their checked bodies. This is a -statement about the production ingress/lookup result; the independent +Callers must establish these operational and representation witnesses for +the run. They supply no typing or checker-soundness premise. The proof +extracts the validation, type-inference, theorem-guard, value-inference, and +conversion steps from public success, then derives body typing to extend +the preceding model. Automatic witness construction, lambdas, applications, +polymorphic instantiation, inductives, coordinated blocks, and other conversion +paths remain outside the fragment. + +`checkEnvAnon_atomic_represents_source` ties every source address to an +interface with the type and body reached by production lookup. The independent serialized-Ixon reader refinement remains a separate boundary. -The no-False corollary takes a model of the initial axiom interface in which -the designated false type is empty. Model extension preserves that value, -so no resulting declaration can inhabit it. It does not blacklist axiom -names or merely exclude declarations literally written with type `False`. +The no-False corollary assumes the designated false type is empty in the +initial axiom model. Extension preserves that value, so no resulting +declaration can inhabit it. No axiom-name restriction is needed. ## Trust checks The audits traverse checked declaration types and bodies, including inductive constructors. They compare exact axiom sets and record direct origins of -`sorryAx`; they do not rely on cached imported axiom summaries. During the -migration, full traversal of the original sources exposed two wrapper reports -that listed 3 axioms but depended on 30. Their boundaries now include the -existing implementation assumptions and unfinished metatheory. All 441 retained -named-specification assertions use the original full dependency graphs as their -migration baseline. +`sorryAx`. Full traversal includes dependencies missed by cached imported +axiom summaries, including implementation assumptions and unfinished +metatheory. All 441 retained named-specification assertions use their original +full dependency graphs as the migration baseline. The same traversal covers 2,034 kernel manifest roots. Thirteen entries omitted logical or native dependencies through constructor fields; their corrected @@ -138,12 +119,11 @@ reachable declarations, axioms, and proof-hole origins are computed separately. axioms. Completed roots cannot depend on that namespace. Named-specification proof holes and implementation bridge axioms are tracked separately from Lean's logical axioms and generated native proofs. No direct consistency root -permits a proof hole or a metatheory/implementation bridge axiom. Both the -context hash and content-address hash now use kernel-checked proofs of their -32-byte output bounds on both platform sizes. Their former generated native -assumptions have been removed from the affected exact audits. +permits a proof hole or a metatheory/implementation bridge axiom. The context +hash and content-address hash use kernel-checked proofs of their 32-byte output +bounds on both platform sizes. -Run the complete local kernel-certification gate: +Run the complete local certification gate: ```sh lake run check-kernel --with-model @@ -170,7 +150,7 @@ reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model extension lemma and `ModelTyping.no_false` use only `propext`, `Classical.choice`, and `Quot.sound`, with set theory as a hypothesis. -The new production roots additionally forbid the earlier abstract +The production roots additionally forbid the abstract `CheckSuccessSound`/`SupportedCheckFragment` interfaces and the independent certificate validator in their dependency closures. @@ -189,10 +169,8 @@ permits only the three standard Lean axioms in these roots and inventories the separate BLAKE3 and native execution boundaries. See [the command interface, theorem contracts and evidence](certified-checking.md). -These host results do not cover the full production inference dispatcher or -prove that an Aiur public verifier executes the host validator. The VM pilot -remains in the frozen archive for a later change; its build target and execution -tests are outside the host gate. +Aiur execution of the host validator remains a separate proof obligation. +The VM pilot is preserved in the frozen archive and excluded from the host gate. ## Review entry points @@ -205,17 +183,16 @@ tests are outside the host gate. | Host commands, receipts, and frozen regression evidence | [Certified checking guide](certified-checking.md) | | Concrete set-theory instance | [Separate model package](../Models/SetTheory/README.md) | -## Change scope +## Provenance and scope -The kernel, theory and certified host scaffolding is extracted from -`jcb/monorepo` at `7b06b754` in a single change. Existing callers move from -`Ix.Tc` to `Ix.Kernel`. Existing `Ix.Compile.Verify` proofs receive the -necessary named-specification imports and exact hash-axiom audit updates. -The new compiler development, circuit changes and certificate VM pilot -are deferred. The external `lean4lean` dependency and its benchmark/test -targets are removed; all verification dependencies are local to Ix. +The kernel, theory, and certified host scaffolding comes from `jcb/monorepo` +at `7b06b754`. The checker namespace is `Ix.Kernel` (formerly `Ix.Tc`), and +verification dependencies are local to Ix. The external `lean4lean` dependency +and its benchmark/test targets have been removed. The new compiler development, +circuit changes, and certificate VM pilot are deferred. -The subsequent production-fragment change adds direct inference, declaration, -and serial-environment proofs on top of that extraction. It changes no -production checker behavior and adds regression cases to the existing kernel -unit gate. +The named development retains 104 source modules, including the inductive +fixtures used by the existing proofs. Original hashes and attribution are +recorded in `Ix/Theory/Named/NOTICE` and `Tests/Theory/NamedManifest.lean`; the +Apache license is preserved alongside the sources. The axiom-audit helper and +direct production-fragment proofs are authored in Ix. diff --git a/docs/theory.md b/docs/theory.md index 6aa76b3a2..8c348f3aa 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -6,8 +6,8 @@ run the selected regression tests, provenance check, and exact foundation audit with `lake run check-theory`. `Ix.Theory.Certified.accepted_has_model` constructs a model for accepted -declarations. `accepted_proof_sound` and `no_proof_of_False` give the corresponding semantic -and consistency results. These theorems explicitly assume a type `V` equipped +declarations. `accepted_proof_sound` and `no_proof_of_False` give the corresponding +semantic and consistency results. These theorems assume a type `V` equipped with `Ix.Theory.Model.SetTheory V`: set operations and laws together with a countable tower of Grothendieck universes. The separate [`Models/SetTheory`](../Models/SetTheory/README.md) package constructs this @@ -17,19 +17,12 @@ that large-cardinal hypothesis explicitly and passes an axiom guard for `propext`, `Classical.choice`, and `Quot.sound`. User-supplied logical axioms and open frontiers retain their stated model hypotheses. -These are relative consistency results for the certified interface. The -[kernel verification](kernel-verification.md) now shares its universe syntax -and proves direct model results for universe comparison, structural expression -reading, interning, and the production sort-inference branch. The certified -host adapters connect authenticated serialized Ixon to model admission. -A production fragment for monomorphic aliases and closed universe terms now -proves that `checkEnvAnon` preserves a supplied model of its source axiom set, -under explicit operational and representation witnesses. Its no-False -corollary preserves the axiom model's empty interpretation of the false type; -it does not assume that arbitrary source axioms are consistent. See the -[fragment contract](kernel-verification.md#production-environment-fragment). -A complete bridge from arbitrary `Ix.Kernel.checkEnvAnon` success or -compiler/backend execution remains unfinished. +The [certified host adapters](certified-checking.md) connect authenticated +serialized Ixon to model admission. Separately, a production `checkEnvAnon` +[fragment](kernel-verification.md#production-environment-fragment) for +monomorphic aliases and closed universe terms extends every model of its +source axioms under explicit execution witnesses. Full checker consistency +and compiler/backend refinement remain open. `Ix.Theory.Named` retains the local name-indexed specification and proof support needed by the existing kernel and compiler verification. It shares From 4abe7af422dc0a8fd57c248824c7ef665cdf19eb Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 08:04:26 -0400 Subject: [PATCH 04/63] Make verification documentation self-contained --- Ix/Compile/Verify/Catalog.lean | 4 +- Ix/Compile/Verify/Codec.lean | 8 +- Ix/Kernel/Driver.lean | 4 +- Ix/Kernel/Inductive.lean | 18 +-- Ix/Kernel/Infer.lean | 2 +- Ix/Kernel/Level.lean | 2 +- Ix/Kernel/Verify/Audit/Completed.lean | 116 ++++++++-------- Ix/Kernel/Verify/Audit/Statements.lean | 9 +- Ix/Kernel/Verify/Cache.lean | 22 +-- Ix/Kernel/Verify/Check/Acceptance.lean | 8 +- Ix/Kernel/Verify/Check/BinderRoundTrip.lean | 2 +- Ix/Kernel/Verify/Check/BlockAcceptance.lean | 5 +- Ix/Kernel/Verify/Check/BlockCache.lean | 2 +- Ix/Kernel/Verify/Check/BlockDefinition.lean | 18 +-- Ix/Kernel/Verify/Check/BlockIdentity.lean | 6 +- Ix/Kernel/Verify/Check/BlockNatFixture.lean | 4 +- Ix/Kernel/Verify/Check/BlockOracle.lean | 19 ++- Ix/Kernel/Verify/Check/BlockTransaction.lean | 14 +- Ix/Kernel/Verify/Check/BoundedPipelines.lean | 10 +- .../Verify/Check/CheckConstTransaction.lean | 11 +- Ix/Kernel/Verify/Check/CheckerEvidence.lean | 2 +- .../Verify/Check/DeclarationValidation.lean | 2 +- Ix/Kernel/Verify/Check/FullInference.lean | 6 +- .../Check/FullInferenceApplications.lean | 14 +- .../Verify/Check/FullInferenceBinders.lean | 4 +- .../Verify/Check/FullInferenceCache.lean | 8 +- .../Verify/Check/FullInferenceDispatcher.lean | 8 +- Ix/Kernel/Verify/Check/FullInferenceKnot.lean | 10 +- .../Verify/Check/FullInferenceLeaves.lean | 4 +- .../Check/FullInferenceProjections.lean | 10 +- Ix/Kernel/Verify/Check/InferencePolicy.lean | 8 +- Ix/Kernel/Verify/Check/MemberEvidence.lean | 2 +- Ix/Kernel/Verify/Check/PositiveFuelSort.lean | 14 +- .../Verify/Check/PreTranslationScopes.lean | 4 +- Ix/Kernel/Verify/Check/PublicBlocks.lean | 6 +- Ix/Kernel/Verify/Check/PublicStandalone.lean | 10 +- Ix/Kernel/Verify/Check/QuotientBoundary.lean | 4 +- Ix/Kernel/Verify/Check/SafetyFrame.lean | 2 +- Ix/Kernel/Verify/Check/Scoped.lean | 2 +- Ix/Kernel/Verify/Check/ScopedActiveBlock.lean | 4 +- .../Verify/Check/ScopedBoundedPipelines.lean | 6 +- .../Check/ScopedPositiveFuelCertificate.lean | 2 +- .../Verify/Check/SingletonInductive.lean | 14 +- Ix/Kernel/Verify/Check/StandaloneDriver.lean | 4 +- Ix/Kernel/Verify/Check/ValidatorFrame.lean | 2 +- Ix/Kernel/Verify/Ctx.lean | 4 +- Ix/Kernel/Verify/Decl.lean | 15 +-- Ix/Kernel/Verify/DefEq.lean | 16 +-- Ix/Kernel/Verify/DefEq/BoolTrue.lean | 2 +- Ix/Kernel/Verify/DefEq/CheapReduction.lean | 2 +- Ix/Kernel/Verify/DefEq/EqualRankCache.lean | 2 +- Ix/Kernel/Verify/DefEq/EqualRankPrefix.lean | 2 +- Ix/Kernel/Verify/DefEq/FinalWhnf/Closure.lean | 4 +- .../DefEq/FinalWhnf/StringExpansion.lean | 2 +- Ix/Kernel/Verify/DefEq/LazyDeltaClosure.lean | 2 +- .../Verify/DefEq/LazyDeltaIteration.lean | 4 +- .../Verify/DefEq/PropositionClassifier.lean | 8 +- Ix/Kernel/Verify/DefEq/StringLiteral.lean | 6 +- Ix/Kernel/Verify/DefEq/Structural.lean | 2 +- .../Verify/Driver/BooleanAcceptance.lean | 16 +-- Ix/Kernel/Verify/Driver/Dependencies.lean | 2 +- Ix/Kernel/Verify/Driver/Enumeration.lean | 2 +- Ix/Kernel/Verify/Driver/Fixtures.lean | 2 +- Ix/Kernel/Verify/Driver/Model.lean | 4 +- Ix/Kernel/Verify/Driver/Serial.lean | 2 +- .../Verify/Driver/SupportedAcceptance.lean | 62 ++++----- .../Driver/SupportedAcceptanceFixtures.lean | 20 +-- Ix/Kernel/Verify/Env.lean | 10 +- Ix/Kernel/Verify/Execution.lean | 2 +- Ix/Kernel/Verify/Inductive.lean | 14 +- .../Inductive/AliasFormerAdmission.lean | 2 +- .../Inductive/AliasFormerCertificate.lean | 6 +- .../Verify/Inductive/AliasRecAdmission.lean | 2 +- .../Inductive/AnnotatedPiAdmission.lean | 2 +- .../Inductive/AnnotatedPiCertificate.lean | 9 +- .../Verify/Inductive/BlockCertificate.lean | 4 +- .../ConstructorPositivityTraversal.lean | 2 +- .../ConstructorValidationTraversal.lean | 2 +- .../Inductive/EliminationBreadthFixture.lean | 2 +- .../Verify/Inductive/EnumerationFixture.lean | 6 +- .../Verify/Inductive/ExactLeanSyntax.lean | 4 +- .../GeneratedRecursorAcceptanceClosure.lean | 4 +- .../Inductive/GeneratedRecursorAdmission.lean | 4 +- .../GeneratedRecursorCheckerFixture.lean | 8 +- .../GeneratedRecursorInitialInvariant.lean | 4 +- .../GeneratedRecursorMemberCheck.lean | 2 +- .../GeneratedRecursorMemberFixture.lean | 4 +- .../Inductive/GeneratedRecursorSelection.lean | 2 +- .../Inductive/GeneratedRecursorSemantics.lean | 2 +- .../Inductive/IndexedCandidateSyntax.lean | 2 +- .../IndexedCandidateTransaction.lean | 4 +- .../Inductive/IndexedPositivityTransport.lean | 2 +- .../Inductive/IndexedProducerClosure.lean | 2 +- .../IndexedRecursiveCertificate.lean | 6 +- .../Inductive/IndexedRecursiveSoundness.lean | 2 +- Ix/Kernel/Verify/Inductive/IotaPattern.lean | 2 +- .../Inductive/MutualBlockCertificate.lean | 8 +- .../Inductive/MutualRecursorAdmission.lean | 2 +- .../Inductive/NestedRecursiveFixture.lean | 2 +- .../Verify/Inductive/OccurrenceClosure.lean | 6 +- .../Inductive/OccurrenceValidation.lean | 6 +- .../Verify/Inductive/PositivityTraversal.lean | 2 +- .../ProducedGenerationTransaction.lean | 8 +- .../Inductive/RecursivePiAdmission.lean | 2 +- .../Inductive/RecursivePiCertificate.lean | 5 +- .../Inductive/RecursivePiSoundness.lean | 2 +- .../Inductive/SingletonEnumeration.lean | 16 +-- .../Verify/Inductive/SingletonFamily.lean | 8 +- .../Verify/Inductive/SingletonOracle.lean | 2 +- .../Verify/Inductive/SingletonRecursor.lean | 8 +- .../Inductive/StructuralCacheSemantics.lean | 6 +- Ix/Kernel/Verify/Infer.lean | 8 +- Ix/Kernel/Verify/Infer/CacheSoundness.lean | 4 +- Ix/Kernel/Verify/Infer/Callbacks.lean | 2 +- Ix/Kernel/Verify/Infer/CheapBeta.lean | 2 +- Ix/Kernel/Verify/Infer/Constants.lean | 4 +- Ix/Kernel/Verify/Ingress/LiteralBlobs.lean | 4 +- Ix/Kernel/Verify/Ingress/Representation.lean | 7 +- .../Verify/Ingress/SerializedBoolean.lean | 12 +- Ix/Kernel/Verify/Knot.lean | 28 ++-- Ix/Kernel/Verify/NatFixture.lean | 51 ++++--- .../Verify/RecursiveMethods/Closure.lean | 2 +- .../RecursiveMethods/ScopedCallDomains.lean | 6 +- .../RecursiveMethods/ScopedSortInference.lean | 2 +- Ix/Kernel/Verify/Run.lean | 6 +- Ix/Kernel/Verify/State.lean | 14 +- Ix/Kernel/Verify/Statements.lean | 22 +-- Ix/Kernel/Verify/Suffix.lean | 6 +- Ix/Kernel/Verify/Support.lean | 2 +- Ix/Kernel/Verify/Totalization.lean | 2 +- Ix/Kernel/Verify/Whnf.lean | 125 +++++++++--------- Ix/Kernel/Verify/Whnf/Closure.lean | 32 +++-- .../Verify/Whnf/Delta/CacheExecution.lean | 2 +- Ix/Kernel/Verify/Whnf/Delta/TrustedBody.lean | 4 +- .../Verify/Whnf/Iota/ApplicationRequests.lean | 2 +- .../Verify/Whnf/Iota/ConstructorDispatch.lean | 2 +- Ix/Kernel/Verify/Whnf/Iota/NatOffset.lean | 4 +- .../Verify/Whnf/Iota/OptionalReduction.lean | 2 +- Ix/Kernel/Verify/Whnf/Iota/SelectedRule.lean | 2 +- .../Verify/Whnf/Iota/StructEtaControl.lean | 2 +- .../Verify/Whnf/Iota/SynthesisRequests.lean | 2 +- .../Whnf/NoDelta/QuotientReflection.lean | 2 +- .../Whnf/Projection/StringExpansion.lean | 4 +- Ix/Kernel/Verify/Whnf/README.md | 5 +- .../Verify/Whnf/Runtime/LazyIngress.lean | 2 +- .../Verify/Whnf/StructEta/RebuildTail.lean | 2 +- .../Verify/Whnf/Structural/BasicStep.lean | 2 +- .../Whnf/Structural/ProjectionStep.lean | 2 +- Ix/Kernel/Verify/World.lean | 18 ++- Ix/Kernel/Whnf.lean | 2 +- .../Named/ConstructorValidityFixtures.lean | 2 +- .../Fixtures/ProjectionExpressibility.lean | 2 +- Ix/Theory/Named/Inductive.lean | 9 +- Ix/Theory/Named/InductiveFixtures.lean | 2 +- Ix/Theory/Named/MutualInductiveFixtures.lean | 2 +- Ix/Theory/Named/NestedInductive.lean | 26 ++-- Ix/Theory/Named/NestedInductiveFixtures.lean | 6 +- Ix/Theory/Named/Projection.lean | 9 +- Ix/Theory/Named/SingletonParity.lean | 4 +- .../Named/Typing/InductiveCertificate.lean | 2 +- Ix/Theory/Named/Typing/InductivePattern.lean | 8 +- .../Named/Typing/InductivePatternWF.lean | 6 +- .../Named/Typing/NestedInductiveLemmas.lean | 2 +- .../Environment/ConstructorValidation.lean | 8 +- .../ConstructorValidityMatrix.lean | 2 +- .../Environment/EliminationFixturesEdges.lean | 2 +- .../Environment/EliminationFixturesEq.lean | 2 +- .../Environment/EliminationFixturesEqNat.lean | 2 +- .../Environment/EliminationFixturesNat.lean | 2 +- .../Environment/EliminationFixturesOrAnd.lean | 2 +- .../Environment/EliminationFixturesSmall.lean | 2 +- .../Environment/IndexedVecSemanticReplay.lean | 4 +- .../Verify/Environment/InductiveFixtures.lean | 8 +- .../Verify/Environment/NestedReplay.lean | 2 +- .../Environment/NestedRepresentation.lean | 35 +++-- .../Environment/NestedTransformation.lean | 8 +- .../Environment/NormalizationMatrix.lean | 2 +- .../Environment/SingletonParityMatrix.lean | 4 +- .../Environment/SingletonParityReplay.lean | 6 +- Tests/Ix/Kernel/CheckEnv.lean | 2 +- Tests/Ix/Kernel/CheckTauCeti.lean | 1 - Tests/Ix/Kernel/CheckTests.lean | 2 +- ...-backedge-audit.md => kernel-recursion.md} | 59 ++++----- docs/kernel-verification.md | 36 +++-- docs/tc-context-digest-collision-boundary.md | 18 +-- docs/theory.md | 8 +- 186 files changed, 739 insertions(+), 777 deletions(-) rename docs/{tc-k0-backedge-audit.md => kernel-recursion.md} (77%) diff --git a/Ix/Compile/Verify/Catalog.lean b/Ix/Compile/Verify/Catalog.lean index 4eafad6c1..0f931e270 100644 --- a/Ix/Compile/Verify/Catalog.lean +++ b/Ix/Compile/Verify/Catalog.lean @@ -5,7 +5,7 @@ import Std.Data.HashMap.Lemmas /-! # Immutable compiler catalog and representation well-formedness -This module states the X1 representation boundary without running Ix.Kernel. It +This module states the representation boundary without running Ix.Kernel. It separates content-address integrity, finite immutable lookup support, logical environment views, wire representability, and expression-table resolution. @@ -392,7 +392,7 @@ structure Catalog.Finite (catalog : Catalog) : Prop where anonHints : FinitelySupported catalog.anonHints memberAddrs : FinitelySupported catalog.memberAddrs -/-- X1 in-memory catalog integrity. This is representation +/-- In-memory catalog integrity. This is representation well-formedness, not Ix.Theory.Named `VEnv.WF`. -/ structure Catalog.WF (catalog : Catalog) : Prop where finite : catalog.Finite diff --git a/Ix/Compile/Verify/Codec.lean b/Ix/Compile/Verify/Codec.lean index 15c0753f9..3fc3f2da9 100644 --- a/Ix/Compile/Verify/Codec.lean +++ b/Ix/Compile/Verify/Codec.lean @@ -4,7 +4,7 @@ import Std.Tactic.BVDecide /-! # Proof-visible v2 codecs -These X1 slices make universe serialization kernel-visible end to end. +These proofs make universe serialization kernel-visible end to end. `Reads` records exact cursor movement in arbitrary surrounding bytes, while `Writes` records append-only writer behavior. The public theorem covers both the one-byte and trimmed 1–8-byte `Tag2` forms, subject only to the format's @@ -1422,7 +1422,7 @@ theorem getUniv_reads (u : Ixon.Univ) (h : WireWF u) : have hread := getUnivFuel_reads u h _ hfuel before after exact hread -/-- X1-U64: exact full-buffer universe round trip for every representable +/-- Exact full-buffer universe round trip for every representable compressed successor count. -/ theorem deUniv_serUniv (u : Ixon.Univ) (h : WireWF u) : Ixon.deUniv (Ixon.serUniv u) = .ok u := by @@ -1467,13 +1467,13 @@ abbrev UnivWireWF : Ixon.Univ → Prop := abbrev SmallUnivWireWF : Ixon.Univ → Prop := Codec.Ixon.Univ.SmallWireWF -/-- X1-U64: exact full-buffer universe round trip across both the one-byte +/-- Exact full-buffer universe round trip across both the one-byte and trimmed large-size `Tag2` forms. -/ theorem deUniv_serUniv (u : Ixon.Univ) (h : UnivWireWF u) : Ixon.deUniv (Ixon.serUniv u) = .ok u := Codec.Ixon.Univ.deUniv_serUniv u h -/-- X1-U8: exact full-buffer universe round trip for the one-byte tag domain. +/-- Exact full-buffer universe round trip for the one-byte tag domain. This domain contains `.succ .zero`, the encoding of `Sort 1`. -/ theorem deUniv_serUniv_small (u : Ixon.Univ) (h : SmallUnivWireWF u) : Ixon.deUniv (Ixon.serUniv u) = .ok u := diff --git a/Ix/Kernel/Driver.lean b/Ix/Kernel/Driver.lean index be0310eeb..400b00eb5 100644 --- a/Ix/Kernel/Driver.lean +++ b/Ix/Kernel/Driver.lean @@ -107,7 +107,7 @@ def constantInfoTag : Ixon.ConstantInfo → Ixon.ConstTag | .rPrj _ => .rPrj | .dPrj _ => .dPrj -/-- Deterministic source-key order shared by the implementation and E1's +/-- Deterministic source-key order shared by the implementation and serial composition's coverage certificate. -/ def orderedAnonConstAddrs (env : Ixon.Env) : Array Address := env.consts.keys.toArray.qsort fun a b => a.cmpBytes b == .lt @@ -228,7 +228,7 @@ structure CheckResult where deriving Repr, Inhabited /-- Explicit accumulator for the serial anonymous driver. Naming this -state makes the production loop available to E1's trace theorem without +state makes the production loop available to serial composition's trace theorem without changing its persistent-checker semantics. -/ structure AnonCheckLoopState where results : Array CheckResult diff --git a/Ix/Kernel/Inductive.lean b/Ix/Kernel/Inductive.lean index 18b78eb1e..e57d3a91e 100644 --- a/Ix/Kernel/Inductive.lean +++ b/Ix/Kernel/Inductive.lean @@ -579,7 +579,7 @@ def checkPositiveParametersFrom (id : KId m) checkPositiveParametersFrom id args params (index + 1) remaining /-- The exact stateful parameter-comparison loop used by positivity. Naming - it separately exposes the successful recursive `isDefEq` trace to E2c + it separately exposes the successful recursive `isDefEq` trace to inductive verification without changing the production comparison order or diagnostics. -/ def checkPositiveParameters (id : KId m) (args params : Array (KExpr m)) (nParams : Nat) : RecM m Unit := @@ -593,7 +593,7 @@ def positiveIndicesIndependent (args : Array (KExpr m)) (nParams : Nat) !exprMentionsAnyAddr index rootAddrs /-- The stateless prefix of recursive-application validation. Keeping the - original error values here makes the production control flow and the E2c + original error values here makes the production control flow and the inductive verification success characterization share one definition. -/ def checkPositiveRecursiveApplicationPreconditions (us : Array (KUniv m)) (args : Array (KExpr m)) @@ -618,7 +618,7 @@ def checkPositiveRecursiveApplicationPreconditions s!"positivity: recursive occurrence has wrong argument count: expected {nParams + nIndices}, got {args.size}") /-- Validate the already-resolved inductive header of an active recursive - application. Separating the lookup/match from these guards gives E2c an + application. Separating the lookup/match from these guards gives inductive verification an exact successful-branch seam while preserving their production order. -/ def checkPositiveRecursiveApplicationHeader (id : KId m) (us : Array (KUniv m)) (args : Array (KExpr m)) @@ -769,7 +769,7 @@ def checkNestedPositivityApplicationResolvedFuel (fuel : Nat) (id : KId m) parameter specialization mentions the root block. The caller has already reduced the field domain to this constant-headed spine and established that the head is not a root-family address. Keeping the lookup and - resolved continuation as named production actions gives E2c an exact + resolved continuation as named production actions gives inductive verification an exact successful-header boundary without changing execution order. -/ def checkNestedPositivityApplicationFuel (fuel : Nat) (id : KId m) (us : Array (KUniv m)) (args : Array (KExpr m)) @@ -1020,7 +1020,7 @@ def checkCtorMetadataAgainstParent (ctorId inductId : KId m) /-- Complete A1–A4 validation of one constructor after its parent inductive header and block context have been resolved. Naming this shared sequence keeps member-wide and standalone-constructor checking on the same production -path and gives E2c one exact seam at which successful positivity can be +path and gives inductive verification one exact seam at which successful positivity can be retained before the later universe and return-type checks. -/ def checkInductiveConstructor (ctorId inductId : KId m) (expectedCidx indParams indIndices : Nat) (indLvls : UInt64) @@ -1228,7 +1228,7 @@ def mkIndUnivs (indLvls offset : UInt64) : /-- Reuse or append the exact auxiliary specialization discovered by the flat-block scan. Keeping this decision as a named production action - exposes the shared specialization key to the E2c transport without + exposes the shared specialization key to the inductive verification transport without changing the source-ordered queue traversal around it. -/ def appendNestedAuxiliary (headId : KId m) (occurrenceUs : Array (KUniv m)) (specParams : Array (KExpr m)) @@ -1388,7 +1388,7 @@ def scanFlatConstructors (allBlockAddrs : Array Address) /-- One source-ordered production queue step. Naming the callback preserves the executable traversal while exposing the dynamically growing flat array -and its deduplication set to E2c proofs. -/ +and its deduplication set to inductive verification proofs. -/ def buildFlatBlockQueueStep (allBlockAddrs : Array Address) (nRecParams univOffset : UInt64) (state : FlatBlockQueueState m) : RecM m (BoundedStep (FlatBlockQueueState m) @@ -2856,7 +2856,7 @@ def checkGeneratedRecursorCandidate (ty : KExpr m) throw (.other "check_recursor: type mismatch") let generatedRules := generated.rules if generatedRules.isEmpty && !storedRules.isEmpty then - -- C1: cannot verify stored rules against a missing canonical form. + -- Cannot verify stored rules against a missing canonical form. throw (.other s!"check_recursor: rule generation failed, cannot verify {storedRules.size} stored rules") else if !generatedRules.isEmpty && storedRules.isEmpty then throw (.other s!"check_recursor: stored recursor has no rules (expected {generatedRules.size})") @@ -2913,7 +2913,7 @@ def collectGeneratedRecursorTypeMatches (ty : KExpr m) /-- Select the generated recursor corresponding to one frozen stored declaration. Complete recursor types are closed, unlike major domains peeled from under forall binders, so the stateful DefEq calls remain inside the -top-level K2 translation context. Returning the index separately gives +top-level inference/conversion translation context. Returning the index separately gives verification an exact boundary between selection and exhaustive comparison. -/ def selectGeneratedRecursorIndex (recBlock id : KId m) (ty : KExpr m) (params motives minors : UInt64) (indId : KId m) diff --git a/Ix/Kernel/Infer.lean b/Ix/Kernel/Infer.lean index abfedbfcc..2fc49e7ed 100644 --- a/Ix/Kernel/Infer.lean +++ b/Ix/Kernel/Infer.lean @@ -146,7 +146,7 @@ def inferWith (inferRec : KExpr m → RecM m (KExpr m)) TcM.withInferOnly (methods.infer e) /-- Tie Infer's structural recursive calls through the indexed method table. - `inferWith` is the transparent one-layer body consumed by K0/K1 proofs. -/ + `inferWith` is the transparent one-layer body consumed by totalization/WHNF proofs. -/ def infer (e : KExpr m) : RecM m (KExpr m) := inferWith inferCall e diff --git a/Ix/Kernel/Level.lean b/Ix/Kernel/Level.lean index 62b99ecdb..2f8fd5362 100644 --- a/Ix/Kernel/Level.lean +++ b/Ix/Kernel/Level.lean @@ -372,7 +372,7 @@ decreasing_by all_goals simp [KUniv.size]; omega end -/-! ### Subsumption (Phase 2) -/ +/-! ### Subsumption -/ /-- Keep only the `xs` entries not dominated by a `ys` entry (merge-walk over sorted var lists). Mirrors level.rs `subsume_vars`. Written as index diff --git a/Ix/Kernel/Verify/Audit/Completed.lean b/Ix/Kernel/Verify/Audit/Completed.lean index a9659a1d4..b4751da2d 100644 --- a/Ix/Kernel/Verify/Audit/Completed.lean +++ b/Ix/Kernel/Verify/Audit/Completed.lean @@ -2495,11 +2495,11 @@ private def booleanEnumerationNative : Array Lean.Name := `Ix.Kernel.BooleanEnumerationFixture.recursorOwnerNative._native.native_decide.ax_1_1 ] ++ booleanSemanticFixtureNative -/- The E3-S family-body bridge consumes only the family-side slice of the +/- The supported fragment family-body bridge consumes only the family-side slice of the full end-to-end Boolean witness. Keep this narrower than `booleanEnumerationNative`: in particular it must not inherit the executable recursor run, kernel-run, generated-rule, or recursor-ingress facts merely -because the larger E2b witness uses them. -/ +because the larger singleton certification witness uses them. -/ private def booleanFamilyBodyNative : Array Lean.Name := inductiveNative ++ #[ enumerationAcceptanceNativeAxiom `Ix.Kernel.BooleanEnumerationFixture.familyBlockLoadedNative._native.native_decide.ax_1_1, @@ -2581,7 +2581,7 @@ private def booleanFamilyBodyNative : Array Lean.Name := inductiveNative ++ #[ `Ix.Kernel.BooleanEnumerationFixture.trueTypeNative._native.native_decide.ax_1_2 ] -/-- Exact evaluator boundary of the final E3-S Boolean whole-driver witness. +/-- Exact evaluator boundary of the Boolean whole-driver witness. This is intentionally narrower than `booleanEnumerationNative`: the release root consumes the generated Theory certificate and exact physical links, but does not inherit the earlier standalone body/kernel executions as semantic @@ -2769,7 +2769,7 @@ def booleanDriverNative : Array Lean.Name := inductiveNative ++ #[ `Ix.Kernel.BooleanEnumerationFixture.trueTypeNative._native.native_decide.ax_1_2 ] -/-- Exact evaluator boundary of the serialized T0 Boolean certificate. Each +/-- Exact evaluator boundary of the serialized Boolean certificate. Each closed computation is named so changes in the byte, eager, lazy, dependency, or driver slices are visible independently in the trust manifest. -/ def serializedBooleanNative : Array Lean.Name := booleanDriverNative ++ #[ @@ -2907,7 +2907,7 @@ def serializedBooleanNative : Array Lean.Name := booleanDriverNative ++ #[ `Ix.Kernel.BooleanSerialized.trueProjectionLookupNative._native.native_decide.ax_1_1 ] -/- Exact evaluator boundary of the non-vacuous literal/blob T0 fixture. -/ +/- Exact evaluator boundary of the non-vacuous literal/blob serialization fixture. -/ private def literalRoundTripNative : Array Lean.Name := nameNative ++ #[ literalBlobsNativeAxiom `Ix.Kernel.SerializedLiteralBlobs.blobKeysClassifiedNative._native.native_decide.ax_1_1, @@ -2982,7 +2982,7 @@ than allowing the remainder of the executable inductive-fixture debt. -/ private def projectionDebt : Array Lean.Name := typingDebt.push ``Ix.Theory.Named.VEnv.WF.registeredStructureHeadInversion -/- The empty legacy whole-`KEnv` inductive path is forbidden from every G2b +/- The empty legacy whole-`KEnv` inductive path is forbidden from every catalog lookup consumer root. Keeping this list in the executable audit prevents an innocent-looking helper from reintroducing the old `nomatch` dependency. -/ private def legacyWholeEnv : Array Lean.Name := #[ @@ -2992,7 +2992,7 @@ private def legacyWholeEnv : Array Lean.Name := #[ ``Ix.Kernel.TrKEnv ] -/- E2a is intentionally a Theory-only certificate consumer. These +/- The generation adapter is a Theory-only certificate consumer. These checker/catalog/pattern declarations must not enter its dependency graph. -/ private def certificateAdapterForbidden : Array Lean.Name := #[ ``Ix.Kernel.Catalog, @@ -3016,7 +3016,7 @@ private def annotatedPiCertificateForbidden : Array Lean.Name := #[ ] /- The pre-TrustedBody delta route admitted successful unfolding through a broad -reflection oracle and arbitrary cache-write authority. The final K1 closure +reflection oracle and arbitrary cache-write authority. The final WHNF closure must use exact trusted declaration certificates instead. -/ private def legacyDeltaAuthority : Array Lean.Name := #[ ``Ix.Kernel.UnfoldCacheWriteOracle, @@ -3045,8 +3045,8 @@ private def legacyAllDepthKnot : Array Lean.Name := #[ private def boundedKnotForbiddenDependencies : Array Lean.Name := k1ForbiddenDependencies ++ legacyAllDepthKnot -/- E2c occurrence-validation roots must be derived from the production run, -not from the ambient semantic inductive oracle retained by E2b. -/ +/- Occurrence-validation roots must be derived from the production run, +not from the ambient semantic inductive oracle retained by singleton certification. -/ private def occurrenceValidationForbiddenDependencies : Array Lean.Name := boundedKnotForbiddenDependencies.push ``Ix.Kernel.InductiveOracle @@ -3074,7 +3074,7 @@ private def oracleWorldMaterialization : Array Lean.Name := #[ private def existingSemanticBlockForbiddenDependencies : Array Lean.Name := boundedKnotForbiddenDependencies ++ oracleWorldMaterialization -/- K2S keeps the global suffix model as a compatibility surface only. The +/- Scoped method proofs keep the global suffix model as a compatibility surface only. The finite positive-fuel construction must neither manufacture that model nor reach the older public adapters that consume it. -/ private def legacyGlobalSuffix : Array Lean.Name := #[ @@ -3124,7 +3124,7 @@ private def roots : Array RootAllowance := #[ { root := ``Ix.Kernel.TcM.instantiateUnivParams_wf, standardAxioms := standard, nativeAxioms := levelNative }, - -- G3a finite run support and generated-term resource bounds. Universe + -- Finite run support and generated-term resource bounds. Universe -- instantiation can rebuild sorts/constants and therefore reaches the now -- total expression serializer's standard `UInt8` quotient implementation. { root := ``Ix.Kernel.KExpr.LiftReach.finite, @@ -3175,7 +3175,7 @@ private def roots : Array RootAllowance := #[ standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - -- G3b closes the remaining formalized walker/direct-intern families and + -- Execution-indexed support covers the formalized walker/direct-intern families and -- ties the exact finite request list to an actual TcM computation. The -- simultaneous/reverse instantiation specs can likewise rebuild serialized -- expressions and inherit the same standard quotient footprint. @@ -3321,7 +3321,7 @@ private def roots : Array RootAllowance := #[ { root := ``Ix.Kernel.TrKExpr.defeq, standardAxioms := standard, sorryOrigins := typingDebt }, - -- Legacy whole-environment compatibility interfaces. G2b consumer roots + -- Legacy whole-environment compatibility interfaces. Catalog lookup consumer roots -- below are forbidden from depending on these declarations. { root := ``Ix.Kernel.TrKEnv.wf, standardAxioms := standard }, @@ -3343,7 +3343,7 @@ private def roots : Array RootAllowance := #[ { root := ``Ix.Kernel.CtxRecon.fvar_resolves, standardAxioms := standard }, - -- G1a's non-circular world and one-way lazy-load boundary. + -- Non-circular world model and one-way lazy-load boundary. { root := ``Ix.Kernel.VerifyWorld.ofCatalog_catalogued_not_trusted, standardAxioms := standardWithoutChoice }, { root := ``Ix.Kernel.VerifyWorld.LE.trans, @@ -3361,7 +3361,7 @@ private def roots : Array RootAllowance := #[ { root := ``Ix.Kernel.VerifyWorld.ofCatalog_loaded_not_trusted, standardAxioms := standard }, - -- G1b's raw/pending boundary. Raw correspondence has no declaration-WF + -- Pending-declaration isolation. Raw correspondence has no declaration-WF -- premise; the fixture roots pin the concrete non-WF pending case. { root := ``Ix.Kernel.RawExprRel.mono, standardAxioms := standard }, @@ -3380,7 +3380,7 @@ private def roots : Array RootAllowance := #[ { root := ``Ix.Kernel.IllTypedPending.loaded_pending_but_not_wf, standardAxioms := standard }, - -- G1c's trusted-only catalog log and explicit-WF promotion boundary. + -- Trusted-only catalog log and explicit-WF promotion boundary. { root := ``Ix.Kernel.RawDeclRel.wf_le, standardAxioms := standard }, { root := ``Ix.Kernel.TrustedCatalogLog.wf, @@ -3414,7 +3414,7 @@ private def roots : Array RootAllowance := #[ { root := ``Ix.Kernel.WellTypedPromotion.promotes, standardAxioms := standard }, - -- G1d's world-based concrete-state invariant. Loading stays + -- World-based concrete-state invariant. Loading stays -- representation-only, promotion requires a fresh WF witness, and the -- fixed-world Hoare roots pin no-promotion behavior on both outcomes. { root := ``Ix.Kernel.TcStateWF.of_consts_eq, @@ -3434,7 +3434,7 @@ private def roots : Array RootAllowance := #[ { root := ``Ix.Kernel.TcM.instantiateUnivParams.tcStateWF, standardAxioms := standard, nativeAxioms := levelNative }, - -- Pin A / E2a: the certified-generation adapter may use only Ix.Theory.Named + -- The certified-generation adapter may use only Ix.Theory.Named -- Theory transaction facts, never Ix checker/catalog/pattern authority. { root := ``Ix.Kernel.CertifiedGenerationTransaction.trace, standardAxioms := standardWithoutChoice, @@ -3446,7 +3446,7 @@ private def roots : Array RootAllowance := #[ standardAxioms := standardWithoutChoice, forbiddenDependencies := certificateAdapterForbidden }, - -- Spec-08's Theory-only block adapter preserves the same quarantine while + -- The Theory-only block adapter preserves the same quarantine while -- exposing one atomic all-families/all-constructors/all-recursors/all-rules -- transaction rather than a sequence of singleton admissions. { root := ``Ix.Kernel.CertifiedBlockGenerationTransaction.trace, @@ -3459,7 +3459,7 @@ private def roots : Array RootAllowance := #[ standardAxioms := standardWithoutChoice, forbiddenDependencies := certificateAdapterForbidden }, - -- E2c retains the exact Ix.Theory.Named candidate-producer equation alongside + -- Inductive verification retains the exact Ix.Theory.Named candidate-producer equation alongside -- the certified Theory transaction. Unlike the Theory-only adapter above, -- this Verify-backed bridge deliberately inherits the pinned analyzer debt. { root := ``Ix.Kernel.ProducedGenerationTransaction.facts, @@ -3494,7 +3494,7 @@ private def roots : Array RootAllowance := #[ standardAxioms := standard, nativeAxioms := mutualFamilyNative }, - -- E2c's first concrete breadth witness is the exact staged `IndexedVec` + -- The concrete breadth witness is the exact staged `IndexedVec` -- certificate: one parameter, one changing index, a recursive field, large -- elimination, and both generated rules. It remains Theory-only here; -- production Ix catalog correspondence is audited in the later linkage. @@ -3611,7 +3611,7 @@ private def roots : Array RootAllowance := #[ sorryOrigins := aliasRecUpstreamDebt, forbiddenDependencies := annotatedPiCertificateForbidden }, - -- E2c occurrence-validation seam. These roots expose the selected loaded + -- Occurrence-validation boundary. These roots expose the selected loaded -- family and strengthen every production guard into the elementwise -- valid-inductive-application invariant, without oracle authority. { root := @@ -3651,7 +3651,7 @@ private def roots : Array RootAllowance := #[ ``Ix.Kernel.RecM.PositiveParameterComparisonTrace.theoryDefEqScoped, standardAxioms := standard, nativeAxioms := inferNative, - -- This is the K2S instantiation bridge, not the oracle-free occurrence + -- This is the scoped method instantiation bridge, not the oracle-free occurrence -- theorem above. `ScopedWhnfStateInv` contains `TrustedCatalogLog`, whose -- ambient constructor names `InductiveOracle`; semantic use remains -- confined to the projected `ScopedWFAtOn.isDefEq` field. @@ -3695,7 +3695,7 @@ private def roots : Array RootAllowance := #[ nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - -- E2c production-traversal seam. Root-free domains are state-preserving; + -- Production-traversal boundary. Root-free domains are state-preserving; -- direct recursive-family applications inherit the oracle-free occurrence -- invariant; and forall success exposes the decremented recursive run plus -- exact local-context restoration. @@ -3810,7 +3810,7 @@ private def roots : Array RootAllowance := #[ nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - -- E2c nested auxiliary expansion. The complete positivity trace emits an + -- Nested auxiliary expansion. The complete positivity trace emits an -- exact existing-or-fresh request; the flat scanner classifies every -- successful detector call as an unchanged pair or one fresh exact append. -- The source-ordered constructor and bounded-queue histories prove that the @@ -3936,7 +3936,7 @@ private def roots : Array RootAllowance := #[ nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - -- Concrete E2c nested reachability. The compiler-shaped Box/Tree fixture + -- Concrete nested reachability. The compiler-shaped Box/Tree fixture -- runs production ingress, positivity, and flat-block construction on the -- same `Box Tree` occurrence. Its headline root proves that the exact -- fresh positivity request is retained under the audited queue invariant. @@ -4187,7 +4187,7 @@ private def roots : Array RootAllowance := #[ sorryOrigins := nestedRestoredPatternUpstreamDebt, forbiddenDependencies := existingSemanticBlockForbiddenDependencies }, - -- E2c generated-recursor metadata. The seven cached header fields are + -- Generated-recursor metadata. The seven cached header fields are -- derived positionally from the certified flat block and are invariant -- under both best-effort and complete rule population. The final root -- covers the actual anonymous-mode cache insertion phase; none of these @@ -4267,7 +4267,7 @@ private def roots : Array RootAllowance := #[ nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - -- E2c generated-recursor type closure. Production closes the accumulated + -- Generated-recursor type closure. Production closes the accumulated -- domains through explicit right-to-left intern requests. These roots prove -- exact finite-support execution, operation-shaped structural translation, -- and equality with Ix.Theory.Named's public canonical mixed recursor type. @@ -4339,9 +4339,9 @@ private def roots : Array RootAllowance := #[ nativeAxioms := generatedRecursorRuleFixtureNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - -- E2c generated-recursor commit, selection, and exhaustive comparison. + -- Generated-recursor commit, selection, and exhaustive comparison. -- Production selection compares complete closed types through an explicit - -- finite fold; one K2S successor layer preserves the scoped state across + -- finite fold; one scoped method successor layer preserves the scoped state across -- selection and gives semantic meaning to the repeated type and positional -- rule comparisons. { root := ``Ix.Kernel.RecM.checkGeneratedRecursorFromCache_success, @@ -4373,7 +4373,7 @@ private def roots : Array RootAllowance := #[ nativeAxioms := generatedRecursorCanonicalFixtureNative, forbiddenDependencies := canonicalRecursorForbiddenDependencies }, - -- E2c outer member closure and exact semantic admission. The explicit + -- Outer member closure and exact semantic admission. The explicit -- transition bridge fixes both Theory environments and requires complete -- trusted provenance for every exact physical member; the existing-block -- specialization keeps that environment unchanged for the recursor block. @@ -4442,7 +4442,7 @@ private def roots : Array RootAllowance := #[ sorryOrigins := aliasRecUpstreamDebt, forbiddenDependencies := canonicalRecursorForbiddenDependencies }, - -- E2c flat semantic transport. The refined flat production trace erases + -- Flat semantic transport. The refined flat production trace erases -- to the exhaustive classifier, and the operation-shaped cross-kernel -- contract recursively constructs Ix.Theory.Named's retained positivity trace. -- Nested auxiliary expansion remains a separate explicit bridge. @@ -4456,18 +4456,18 @@ private def roots : Array RootAllowance := #[ nativeAxioms := inferNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - -- E2c's concrete cross-kernel trace bridge. These roots start at the + -- Concrete cross-kernel trace bridge. These roots start at the -- exact positivity calls selected by the production IndexedVec family -- checker, transport those operations to Ix.Theory.Named, and replay the complete -- retained constructor validator. The direct recursive fixture has no - -- nested auxiliary expansion; that remains the next generic E2c bridge. + -- nested auxiliary expansion; that remains a separate generic proof obligation. { root := ``Ix.Kernel.IndexedRecursiveFixture.indexedVecConsConstructorValidationRun, standardAxioms := standard, nativeAxioms := indexedConstructorValidationNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - -- E2c's first production-linked indexed/recursive vertical slice. The + -- Production-linked indexed/recursive fixture. The -- generated cons equation includes its predecessor recursive call; the -- oracle is then instantiated by exact anonymous ingress, production -- family/recursor checking, exact ownership, and atomic admission. The @@ -4509,7 +4509,7 @@ private def roots : Array RootAllowance := #[ nativeAxioms := kTargetAcceptanceNative, forbiddenDependencies := occurrenceValidationForbiddenDependencies }, - -- E2b's singleton link and legacy oracle constructors remain audited as + -- The singleton link and legacy oracle constructors remain audited as -- compatibility surfaces. The concrete Boolean closure below no longer -- consumes those oracle constructors: its family block advances the exact -- generated Theory environment, and its recursor block consumes entries @@ -4552,7 +4552,7 @@ private def roots : Array RootAllowance := #[ sorryOrigins := typingDebt, forbiddenDependencies := canonicalRecursorForbiddenDependencies }, - -- G2a's explicit ambient-inductive assumption boundary. Audit every + -- Explicit ambient-inductive assumption boundary. Audit every -- oracle projection so adding a field changes this manifest, then pin the -- constructive Nat model and its adversarial loaded-state witness. { root := ``Ix.Kernel.RawInductiveConstRel.mono, @@ -4633,7 +4633,7 @@ private def roots : Array RootAllowance := #[ standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - -- G4's lookup isolation, exhaustive semantic-cache provenance, monotone + -- Lookup isolation, exhaustive semantic-cache provenance, monotone -- warm-world transport, and transactional public-check error boundary. { root := ``Ix.Kernel.PendingDecl.lookup_isolation, standardAxioms := standard }, @@ -4682,7 +4682,7 @@ private def roots : Array RootAllowance := #[ standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - -- K1's concrete Theory reduction meaning, exact five-way cache overlay, + -- WHNF's concrete Theory reduction meaning, exact five-way cache overlay, -- and real ambient-Nat warm-hit witness. The only sorries are the already -- named upstream inductive-environment boundary. { root := ``Ix.Kernel.WhnfMeaning.refl, @@ -6762,7 +6762,7 @@ private def roots : Array RootAllowance := #[ forbiddenDependencies := legacyWholeEnv }, -- StringExpansion: the remaining String-expansion premise is reduced to a pure, -- finite plan. The actual primitive read, seven prefix interns, recursive - -- character fold, and final intern preserve the complete K1 invariant and + -- character fold, and final intern preserve the complete WHNF invariant and -- return the exact structurally translated generated expression. { root := ``Ix.Kernel.RecM.strLitListToConstructor_plan_wf, standardAxioms := standard, nativeAxioms := expressionNative, @@ -6821,7 +6821,7 @@ private def roots : Array RootAllowance := #[ -- NatOffset: the actual post-major iota preprocessing path. Bounded Nat-offset -- parsing, Nat constructor expansion, cleanup, lazy constructor lookup, -- finite String expansion, the policy-selected recursive callback, and the - -- constructor/struct-eta dispatch all preserve the complete K1 invariant. + -- constructor/struct-eta dispatch all preserve the complete WHNF invariant. -- Only the ordinary-constructor and struct-eta tails remain named inputs. { root := ``Ix.Kernel.RecM.prims_state_wf, standardAxioms := standardWithoutChoice, @@ -8124,7 +8124,7 @@ private def roots : Array RootAllowance := #[ nativeAxioms := contextNative.push nameDecideNative, forbiddenDependencies := legacyWholeEnv }, - -- K2a: suffix semantics reduce open-context cache validity to one explicit + -- Suffix semantics reduce open-context cache validity to one explicit -- operational model. The recursive method table closes by induction from -- an exact one-layer contract split between WHNF and Infer/DefEq ownership. { root := ``Ix.Kernel.WhnfSuffixModel.keyRepresents, @@ -8149,7 +8149,7 @@ private def roots : Array RootAllowance := #[ standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := legacyWholeEnv }, - -- K2a also assigns exact meanings to the remaining cache families. A + -- Exact meanings for the remaining cache families. A -- positive DefEq result carries Theory equality; negative results are -- intentionally vacuous for the one-way soundness claim. { root := ``Ix.Kernel.InferMeaning.mono, @@ -8183,7 +8183,7 @@ private def roots : Array RootAllowance := #[ standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - -- K2b: production key executions now generate the canonical operational + -- Production key executions generate the canonical operational -- context witnesses. Physical inference/DefEq writes preserve every -- cache partition, including the rejection-only same-head failure set. { root := ``Ix.Kernel.CacheInvariant.insertInfer, @@ -8359,7 +8359,7 @@ private def roots : Array RootAllowance := #[ standardAxioms := standard, forbiddenDependencies := legacyWholeEnv }, - -- First production K2 branches: both inference hit partitions, collision- + -- Production inference/conversion branches: both inference hit partitions, collision- -- safe DefEq address reflexivity, and a positive full DefEq hit including -- canonical ordering and its final union-find mutation. { root := ``Ix.Kernel.RecM.isDefEq_fullHit_true, @@ -9072,7 +9072,7 @@ private def roots : Array RootAllowance := #[ standardAxioms := standard, nativeAxioms := levelNative, forbiddenDependencies := k1ForbiddenDependencies }, - -- C1A's usable production boundary: a finite schedule closes only the + -- Bounded production methods: a finite schedule closes only the -- method-table depths selected by this run's recursion fuel. The public -- adapters consume the terminal successor-layer domain and have no -- `sorryAx` dependency. @@ -9114,7 +9114,7 @@ private def roots : Array RootAllowance := #[ sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - -- K3 reconstructs the typed source translation from untyped/scoped + -- Declaration checking reconstructs the typed source translation from untyped/scoped -- checker ingress. These roots are usable before the final checkConst -- assembly and do not depend on its statement placeholder. { root := ``Ix.Kernel.KUniv.scoped_iff_toVLevel_wf, @@ -9440,7 +9440,7 @@ private def roots : Array RootAllowance := #[ standardAxioms := standard, nativeAxioms := inferNative, forbiddenDependencies := k1ForbiddenDependencies }, - -- K3 closes the concrete operational policy and retains the old strong + -- Declaration checking closes the concrete operational policy and retains the old strong -- all-support inference roots below as compatibility artifacts. The -- public checker now consumes the bounded successor-layer resources above. { root := ``Ix.Kernel.Methods.next_preservesInferOnly, @@ -9497,10 +9497,10 @@ private def roots : Array RootAllowance := #[ sorryOrigins := typingDebt, forbiddenDependencies := scopedK2SForbiddenDependencies }, - -- E0 closes the atomic coordinated-block transaction around the real + -- Block admission closes the atomic coordinated-block transaction around the real -- production router, classifier, body, and block-result cache. The - -- singleton-definition adapter consumes K3; inductive/recursor bodies keep - -- their E2 oracle premise explicit. Quotients are audited as excluded from + -- singleton-definition adapter consumes the declaration-checking theorem; inductive/recursor bodies keep + -- their inductive certification oracle premise explicit. Quotients are audited as excluded from -- this authority rather than being silently admitted by the block theorem. { root := ``Ix.Kernel.ExactCheckBlock.rebaseWorld, standardAxioms := standardWithoutChoice, @@ -9628,7 +9628,7 @@ private def roots : Array RootAllowance := #[ standardAxioms := standard, forbiddenDependencies := boundedKnotForbiddenDependencies }, - -- E1 models semantic declaration dependencies in the production Address + -- Serial composition models semantic declaration dependencies in the production Address -- domain, proves buildAnonWork is an exact duplicate-free partition, and -- composes successful items in a constructive collapsed-block order. The -- serial roots recover real successful checkConst calls from the public @@ -9754,10 +9754,10 @@ private def roots : Array RootAllowance := #[ standardAxioms := propextOnly, forbiddenDependencies := boundedKnotForbiddenDependencies }, - -- E3-S assembles the scoped K3 standalone theorem and E0's exact atomic - -- disposition into E1's concrete-call adapter. The operational body sum - -- remains transparent: singleton definitions use the scoped K3 certificate - -- and fresh inductive/recursor bodies retain an explicit E2 oracle resource. + -- The supported fragment combines the scoped standalone theorem and atomic + -- block disposition in the serial driver's concrete-call adapter. Singleton + -- definitions use the scoped checking certificate; fresh inductive/recursor + -- bodies retain an explicit inductive-certification oracle resource. -- Separately, the certificate-backed replay adapter consumes already- -- installed member provenance, admits exact arrays idempotently, and gives -- all-block consumers a path which cannot reach oracle materialization. @@ -9844,7 +9844,7 @@ private def roots : Array RootAllowance := #[ sorryOrigins := typingDebt, forbiddenDependencies := boundedKnotForbiddenDependencies }, - -- K2S closed-context vertical slice. These roots certify the exact + -- Scoped methods in a closed context. These roots certify the exact -- fuel-one public trace, package its finite requests and bounded recursive -- schedule, instantiate `ScopedKernelSuffixModel.finiteOperational`, and -- retain `StateInScope` through successful semantic promotion. None may diff --git a/Ix/Kernel/Verify/Audit/Statements.lean b/Ix/Kernel/Verify/Audit/Statements.lean index 976bd7d50..c07319c0a 100644 --- a/Ix/Kernel/Verify/Audit/Statements.lean +++ b/Ix/Kernel/Verify/Audit/Statements.lean @@ -9,8 +9,9 @@ All seven roots are concrete results over the bounded production recursion schedule and checker. The three recursive-method adapters have no `sorryAx` dependency; the standalone and atomic-block checker roots retain only the two named Ix.Theory.Named typing lemmas through their singleton-definition branch. The -E3-S root executes the exact Boolean serial workset and composes its actual -runtime success gates with E1 and fixed certificate-backed E2 entries. This +supported-fragment root executes the exact Boolean serial workset and composes +its runtime success gates with serial composition and fixed inductive +certificate entries. This module permits no local statement placeholder and additionally forbids the Boolean/serialized roots from reaching oracle construction, restaging, or world materialization. @@ -48,7 +49,7 @@ private def legacyAllDepthKnot : Array Lean.Name := #[ private def forbidden : Array Lean.Name := legacyWholeEnv ++ legacyAllDepthKnot -/- K2S public recursive roots may retain the legacy declarations in the +/- Scoped public recursive roots may retain the legacy declarations in the library, but must not manufacture a global suffix model or pass through the old proposition-classifier/run-context path. -/ private def legacyGlobalSuffix : Array Lean.Name := #[ @@ -65,7 +66,7 @@ private def legacyGlobalSuffix : Array Lean.Name := #[ private def scopedForbidden : Array Lean.Name := forbidden ++ legacyGlobalSuffix -/- The all-block E3-S statement must consume fixed semantic entries and may +/- The all-block statement for the supported fragment must consume fixed semantic entries and may not regain the retired residual-oracle/world-materialization path through an adapter refactor. -/ private def oracleWorldMaterialization : Array Lean.Name := #[ diff --git a/Ix/Kernel/Verify/Cache.lean b/Ix/Kernel/Verify/Cache.lean index d35ea1e8a..4050f57c3 100644 --- a/Ix/Kernel/Verify/Cache.lean +++ b/Ix/Kernel/Verify/Cache.lean @@ -8,7 +8,7 @@ import Std.Data.HashSet.Lemmas /-! # Cache provenance and pending-declaration isolation -This is the G4 boundary between an optimization hit and a semantic fact. +Cache provenance connects an optimization hit to a semantic fact. `KEnv` stores only compact address keys, values, and booleans; it does not store the world or expression witnesses under which an entry was produced. The verification therefore carries that missing data as ghost provenance: @@ -25,10 +25,10 @@ The verification therefore carries that missing data as ghost provenance: * `CacheEntry.ReferencesAuthorized` records that every direct constant root behind an entry is trusted (or, for a structural block artifact only, is an active block member); and -* `CacheSemantics.Valid` is the exact family of C1/K1/K2 semantic meanings. - G4 keeps it parametric and requires its world monotonicity. A run chooses - its final finite support up front; K1 and K2 instantiate and preserve the - contract at each concrete insertion site. +* `CacheSemantics.Valid` gives each cache family's semantic meaning and + requires monotonicity under world extension. A run chooses its final finite + support up front; reduction, inference, and conversion proofs instantiate + and preserve the contract at each insertion site. The split is deliberate. This file proves generic cache-hit, world-extension, support-witness weakening, reset, clearing, error-restoration, and @@ -49,8 +49,8 @@ the active atomic block. Every reduction/delta/cache fact is role (3), so a pending target cannot justify its own type or value. These roles are proof-side labels: the production `TcM.getConst` API is not -yet intrinsically capability-tagged. G4 proves the standalone raw-translation -barrier and the stable-cache barrier. K1/K2 must still classify and discharge +yet intrinsically capability-tagged. This module proves the standalone +raw-translation and stable-cache barriers. Each operation's proof must discharge `LookupScope.Allows` at each whnf/infer/defeq/inductive call site; this audit does not treat an untagged concrete lookup as trusted merely because it was listed here. @@ -357,8 +357,8 @@ def CacheEntry.ReferencesAuthorized (authority : CacheAuthority) authority.world.trusted id ∨ (entry.SubjectScoped ∧ authority.active id) -/-- The semantic meaning of each tagged cache family. K1/K2 provide the -concrete `Valid`; G4 requires world monotonicity so a warm entry remains +/-- The semantic meaning of each tagged cache family. Reduction, inference, +and conversion supply `Valid`; world monotonicity keeps a warm entry usable after declarations are admitted. A composite run chooses its final finite support up front, so changing support is deliberately not hidden in this interface. -/ @@ -739,8 +739,8 @@ theorem insertBlockError {semantics : CacheSemantics} (CacheProvenance.blockError semantics authority support block err) /-- Insert one certified full-whnf result while retaining provenance for all -old entries. The four policy-specific siblings below cover every other K1 -WHNF expression map; their exact semantic payload is supplied by +old entries. The four policy-specific siblings below cover the other WHNF +expression maps; their exact semantic payload is supplied by `Verify/Whnf.lean`. -/ theorem insertWhnf {semantics : CacheSemantics} {authority : CacheAuthority} {support : RunSupport} diff --git a/Ix/Kernel/Verify/Check/Acceptance.lean b/Ix/Kernel/Verify/Check/Acceptance.lean index 39eaf2b06..67a9650f1 100644 --- a/Ix/Kernel/Verify/Check/Acceptance.lean +++ b/Ix/Kernel/Verify/Check/Acceptance.lean @@ -11,7 +11,7 @@ The operational checker should produce only the typing fact which differs by declaration kind. Fresh installation and trusted-world promotion are then pure consequences of the existing pending-declaration model. -This keeps K3's critical implication explicit: +This keeps the declaration checker's critical implication explicit: * an axiom is accepted only when its declared type is a Theory type; * a definition, opaque definition, or theorem is accepted only when its @@ -123,7 +123,7 @@ theorem accepted end StandaloneCheckEvidence -/-- The complete declaration-local result expected from K3: a validated raw +/-- The complete declaration-local result expected from declaration-checking: a validated raw declaration has an exact untyped Theory translation, and the checker has supplied the semantic evidence appropriate to its declaration kind. -/ structure StandaloneCheckResult (trProj : RawProjRel) @@ -206,7 +206,7 @@ theorem promoteOfAccepted ⟨concrete, hcatalog, hraw, huntrusted, hclosed, hfresh⟩ hwf /-- Validator scope, exact raw ingress, and successful checker evidence -assemble into the K3 result and one trusted-world promotion. In particular, +assemble into the declaration-checking result and one trusted-world promotion. In particular, scope alone cannot promote a declaration, and semantic evidence alone cannot choose a translation for the concrete Ix syntax. -/ theorem checkResultAndPromote @@ -230,7 +230,7 @@ theorem checkResultAndPromote exact ⟨⟨hingress, hevidence⟩, promoteOfAccepted hstate hpending hevidence.accepted⟩ -/-- End-to-end K3 assembly at the standalone validation boundary. The exact +/-- End-to-end declaration-checking assembly at the standalone validation boundary. The exact production validator supplies raw scoping, while checker evidence supplies semantic acceptance; together they produce the pre-translation result and a fresh trusted-world promotion. -/ diff --git a/Ix/Kernel/Verify/Check/BinderRoundTrip.lean b/Ix/Kernel/Verify/Check/BinderRoundTrip.lean index cc6b44acb..88529396e 100644 --- a/Ix/Kernel/Verify/Check/BinderRoundTrip.lean +++ b/Ix/Kernel/Verify/Check/BinderRoundTrip.lean @@ -6,7 +6,7 @@ import Ix.Kernel.Verify.Infer.BinderClosing The Ix.Theory.Named checker closes a freshly opened binder with `FVarsIn.abstract_instantiate1`. Ix uses address-carrying `KExpr` smart -constructors and separate cached walkers, so K3 needs the corresponding pure +constructors and separate cached walkers, so declaration-checking needs the corresponding pure syntax theorem for `instantiateRevSpec` followed by singleton `abstractFVarsSpec`. -/ diff --git a/Ix/Kernel/Verify/Check/BlockAcceptance.lean b/Ix/Kernel/Verify/Check/BlockAcceptance.lean index d0d14584e..579cc85ce 100644 --- a/Ix/Kernel/Verify/Check/BlockAcceptance.lean +++ b/Ix/Kernel/Verify/Check/BlockAcceptance.lean @@ -356,9 +356,8 @@ theorem fresh {trProj : RawProjRel} {world : VerifyWorld} certificate.oracle.fresh ((certificate.memberIff id).2 hid) /-- Commit an oracle-backed block as one exact ghost transaction and one -trusted-log event. This theorem is intentionally semantic: E2 supplies the -future operational proof that a production inductive/recursor block success -constructs this certificate. -/ +trusted-log event. A separate operational proof must construct the certificate +from a successful production inductive/recursor block check. -/ theorem admit {trProj : RawProjRel} {world : VerifyWorld} {block : KId .anon} {members : Array (KId .anon)} {kind : CheckBlockKind} diff --git a/Ix/Kernel/Verify/Check/BlockCache.lean b/Ix/Kernel/Verify/Check/BlockCache.lean index 777a0c4ed..505be6e40 100644 --- a/Ix/Kernel/Verify/Check/BlockCache.lean +++ b/Ix/Kernel/Verify/Check/BlockCache.lean @@ -89,7 +89,7 @@ theorem closeExactBlockSuccess closeBlockSuccess hcaches hle hexact.blockLookup haccepted /-- A stable physical success hit covers every catalog declaration owned by -the exact block. This is the member-level replay theorem used by E0. -/ +the exact block. This is the member-level replay theorem used by block admission. -/ theorem replayCoordinatedMember {semantics : CacheSemantics} {support : RunSupport} {world : VerifyWorld} {env : KEnv .anon} diff --git a/Ix/Kernel/Verify/Check/BlockDefinition.lean b/Ix/Kernel/Verify/Check/BlockDefinition.lean index 573636b84..dfb91e859 100644 --- a/Ix/Kernel/Verify/Check/BlockDefinition.lean +++ b/Ix/Kernel/Verify/Check/BlockDefinition.lean @@ -8,10 +8,10 @@ import Ix.Kernel.Verify.Check.StandaloneDriver The production definition-block branch iterates `checkConstMemberFresh` over the complete array and then publishes the peak DefEq depth. Ix.Theory.Named does -not yet have an atomic mutual-definition declaration, so the constructive E0 +not yet have an atomic mutual-definition declaration, so the constructive block admission bridge is intentionally the singleton specialization. It extracts the -actual member run from `checkClassifiedBlock`, invokes K3 without performing -K3's standalone promotion, and packages that evidence for the enclosing +actual member run from `checkClassifiedBlock`, invokes declaration-checking without performing +the declaration checker's standalone promotion, and packages that evidence for the enclosing atomic block transaction. -/ @@ -47,13 +47,13 @@ theorem checkClassifiedBlock_singleton_definition_success simp [UInt32.max_def] /-- Construct the certified singleton-definition body from the actual -production trace and K3's fixed-world member theorem. Classifier correctness +production trace and the declaration checker's fixed-world member theorem. Classifier correctness is applied to the exact observed classification equation, so no invariant for an unexecuted branch can satisfy it. -`hblocksAfter` is the remaining representation frame for the legacy K1/K2 -invariant, which tracks loaded constants and intern/cache state but predates -E0's explicit block-array agreement layer. -/ +`hblocksAfter` supplies block-array agreement in addition to the reduction, +inference, and conversion invariant, which tracks loaded constants and +intern/cache state. -/ theorem certifySingletonDefinition {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {model : KernelSuffixModel trProj world} @@ -109,8 +109,8 @@ theorem certifySingletonDefinition activePost := ActiveBlockStateWF.ofKernel hevidence.2.1 hblocksAfter evidence := .singletonDefinition hpending hevidence.1 } -/-- Run-scoped singleton-definition certification for the E3-S adapter. -This is the atomic-block analogue of K3's scoped standalone theorem: the +/-- Run-scoped singleton-definition certification for the supported fragment adapter. +This is the atomic-block analogue of the declaration checker's scoped standalone theorem: the member run produces evidence in the original world and retains the finite suffix-model witness, while the enclosing block transaction remains the sole semantic commit point. -/ diff --git a/Ix/Kernel/Verify/Check/BlockIdentity.lean b/Ix/Kernel/Verify/Check/BlockIdentity.lean index b3e6bc8ef..b12fa2c85 100644 --- a/Ix/Kernel/Verify/Check/BlockIdentity.lean +++ b/Ix/Kernel/Verify/Check/BlockIdentity.lean @@ -22,7 +22,7 @@ namespace Ix.Kernel /-! ## Concrete/world block agreement -/ -/-- E0's representation invariant. The existing `TcStateWF` continues to +/-- Block admission's representation invariant. The existing `TcStateWF` continues to separate loaded constants from semantic trust; this layer adds the one-way agreement for lazily loaded block arrays. -/ structure BlockStateWF (trProj : RawProjRel) (s : TcState .anon) @@ -177,7 +177,7 @@ theorem coordinated_trusted {world : VerifyWorld} {block id : KId .anon} end ExactCheckBlock -/-- Global coherence required of E0's immutable inputs: every catalogued +/-- Global coherence required of block admission's immutable inputs: every catalogued declaration which records coordinated ownership has one exact block entry of the same kind. The premise does not trust or type the declaration. -/ def ExactCoordinatedCatalog (world : VerifyWorld) : Prop := @@ -257,7 +257,7 @@ theorem block_targets {blocks : BlockCatalog} {blockAddr primary : Address} exact hprimary /-- `provenTargets` is the exact target array plus the original Muts block -address. This is the extra coverage later consumed by E1; it is not smuggled +address. This is the extra coverage later consumed by serial composition; it is not smuggled into the member array or treated as a trusted declaration. -/ theorem block_provenTargets {blocks : BlockCatalog} {blockAddr primary : Address} {targets : Array Address} diff --git a/Ix/Kernel/Verify/Check/BlockNatFixture.lean b/Ix/Kernel/Verify/Check/BlockNatFixture.lean index f53bbba2d..6126bbfb7 100644 --- a/Ix/Kernel/Verify/Check/BlockNatFixture.lean +++ b/Ix/Kernel/Verify/Check/BlockNatFixture.lean @@ -2,11 +2,11 @@ import Ix.Kernel.Verify.Check.PublicBlocks import Ix.Kernel.Verify.NatFixture /-! -# Concrete Nat block fixture for E0 +# Concrete Nat block fixture for block admission The existing ambient-Nat oracle is instantiated here against an exact physical block table. This fixture exercises the semantic transaction and -the adversarial cache rule without pretending that E2 has already connected +the adversarial cache rule without pretending that inductive certification has already connected the production inductive checker to the oracle. -/ diff --git a/Ix/Kernel/Verify/Check/BlockOracle.lean b/Ix/Kernel/Verify/Check/BlockOracle.lean index 744ae1c60..0c9a32317 100644 --- a/Ix/Kernel/Verify/Check/BlockOracle.lean +++ b/Ix/Kernel/Verify/Check/BlockOracle.lean @@ -4,13 +4,12 @@ import Ix.Kernel.Verify.Inductive.Certificate /-! # Oracle-backed inductive and recursor blocks -E0 proves the transaction and cache ordering around production block checks. -The semantic meaning of a successful inductive/recursor body remains the -explicit E2b boundary: E2b must connect the actual Ix validators and generated -recursor patterns to an `InductiveOracle`. The Ix.Theory.Named -`CertifiedGenerationTransaction` supplies the Theory-owned portion of that -future construction, but cannot determine Ix addresses, member arrays, or -checker execution on its own. +Block admission proves the transaction and cache ordering around production +block checks. Certifying an inductive/recursor body additionally requires a +connection from the Ix validators and generated recursor patterns to an +`InductiveOracle`. The Ix.Theory.Named `CertifiedGenerationTransaction` +supplies the Theory-owned part of that construction; Ix addresses, member +arrays, and checker execution remain separate obligations. This module packages exactly that remaining boundary and ties it to the real classified-body trace. It introduces no unindexed “block succeeded” axiom. @@ -18,8 +17,8 @@ classified-body trace. It introduces no unindexed “block succeeded” axiom. namespace Ix.Kernel -/-- The E2b resources which remain after E0 has fixed the exact physical -array and production classifier kind. The post-state uses temporary block +/-- Semantic resources required after block admission fixes the physical +array and production classifier kind. The post-state uses temporary block authority; it cannot be exposed as a stable success until the oracle's exact member set is atomically admitted. -/ structure OracleBackedBlockResources @@ -35,7 +34,7 @@ structure OracleBackedBlockResources namespace RecM -/-- Package an actual successful inductive/recursor body for E0. The trace, +/-- Package an actual successful inductive/recursor body for block admission. The trace, exact block, active post-state, and oracle all share the same `members` and `kind` indices. -/ theorem certifyOracleBackedBlock diff --git a/Ix/Kernel/Verify/Check/BlockTransaction.lean b/Ix/Kernel/Verify/Check/BlockTransaction.lean index 86a4e96b0..c7641fd77 100644 --- a/Ix/Kernel/Verify/Check/BlockTransaction.lean +++ b/Ix/Kernel/Verify/Check/BlockTransaction.lean @@ -11,10 +11,10 @@ successful verdict. There are exactly two currently supported semantic sources: -* a singleton definition, whose successful K3 result supplies ordinary +* a singleton definition, whose successful declaration-checking result supplies ordinary declaration acceptance; and * an inductive or recursor block, relative to the explicit inductive oracle - which E2 must construct from the corresponding production checker. + which inductive certification must construct from the corresponding production checker. Ix.Theory.Named does not yet expose an atomic mutual-definition declaration, so no constructor below decomposes a multi-definition production block into a @@ -43,8 +43,8 @@ namespace ActiveBlockStateWF /-- Enter temporary block authority from an ordinary stable kernel state. The additional authority does not validate any new cache entry; it only weakens the authority relation under which already-valid entries are viewed. -Exact loaded-block agreement is supplied separately because the legacy K1/K2 -kernel invariant intentionally tracks constants but not block arrays. -/ +Exact loaded-block agreement is supplied separately because the reduction, +inference, and conversion invariant tracks constants but not block arrays. -/ theorem ofKernel {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} @@ -93,13 +93,13 @@ end ActiveBlockStateWF /-! ## Semantic evidence tied to one classified array -/ -/-- Semantic evidence admitted by E0. Its indices are the production array +/-- Semantic evidence admitted by block admission. Its indices are the production array and classified kind; this rules out pairing an operational trace with a certificate for a different block shape. -The singleton-definition constructor retains the actual K3 checker result, +The singleton-definition constructor retains the actual declaration checker result, not merely an assumed `VDecl.WF`. The oracle-backed constructor is limited -definitionally to inductive/recursor kinds and remains the named E2 boundary. +definitionally to inductive/recursor kinds and retains the certification premise. -/ inductive BlockAdmissionEvidence (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (block : KId .anon) : diff --git a/Ix/Kernel/Verify/Check/BoundedPipelines.lean b/Ix/Kernel/Verify/Check/BoundedPipelines.lean index 8fd014a12..a280d0812 100644 --- a/Ix/Kernel/Verify/Check/BoundedPipelines.lean +++ b/Ix/Kernel/Verify/Check/BoundedPipelines.lean @@ -6,7 +6,7 @@ import Ix.Kernel.Verify.RecursiveMethods.CallDomains /-! # Bounded standalone-checker pipelines -The legacy K3 checker proof quantified strong full inference over every +The legacy declaration checker proof quantified strong full inference over every expression in one finite `RunSupport`. That is too strong: a successful sort inference places its successor sort in the result footprint, and reusing the same footprint as the next input domain demands an infinite successor tower. @@ -34,8 +34,8 @@ def AdmitsEnsureSortDirect (calls : CallDomain) : KExpr .anon → Prop end CallDomain -/-- Strong K3 inference, restricted to the inference calls admitted at one -finite method-table depth. Unlike ordinary C1A inference, the premise is an +/-- Strong full inference, restricted to the inference calls admitted at one +finite method-table depth. Unlike ordinary bounded inference, the premise is an untyped `PreTrKExprS` and the successful postcondition constructs the typed translation. -/ def FullInferenceWFAtOn @@ -58,7 +58,7 @@ def FullInferenceWFAtOn namespace FullInferenceWFAtOn -/-- Ordinary bounded inference already implies the stronger K3 contract on +/-- Ordinary bounded inference already implies the stronger declaration-checking contract on an input domain whose pretranslations can be upgraded without running the checker. This covers syntax-directed typed leaves such as sorts while retaining the independent inference-policy frame on both outcomes. -/ @@ -307,7 +307,7 @@ private theorem ensureSortDirect_wfAtOn all_goals exact TcM.WF.throw fun _ => trivial -/-- A bounded successful type pipeline constructs the same K3 evidence as +/-- A bounded successful type pipeline constructs the same declaration-checking evidence as the legacy proof, but every method call is justified by the declaration's successor-layer call domain. -/ theorem checkTypePipeline_bounded_sound diff --git a/Ix/Kernel/Verify/Check/CheckConstTransaction.lean b/Ix/Kernel/Verify/Check/CheckConstTransaction.lean index 57dd9c9b0..100f7614f 100644 --- a/Ix/Kernel/Verify/Check/CheckConstTransaction.lean +++ b/Ix/Kernel/Verify/Check/CheckConstTransaction.lean @@ -6,11 +6,12 @@ import Ix.Kernel.Verify.Check.CheckConstExecution This module joins the real top-level dispatcher to exact coordinated-block admission. The success theorem is exhaustive: a routed call yields semantic block acceptance; an unrouted call is returned as the standalone branch -already covered by K3. +already covered by declaration-checking. The body certifier is relative to the remaining checker-specific semantic -source: K3 supplies singleton definitions, while E2 supplies inductive and -recursor oracles. Before invoking it, this module proves that the body's +source: declaration checking supplies singleton-definition evidence; +inductive certification supplies inductive and recursor oracles. Before +invoking it, this module proves that the body's second block lookup and classifier selected the same ordered members and kind as the route. Thus the certifier cannot be applied to a TOCTOU-substituted block. @@ -18,7 +19,7 @@ block. namespace Ix.Kernel -/-- Stable kernel state plus E0's physical/ghost block-table agreement. -/ +/-- Stable kernel state plus block admission's physical/ghost block-table agreement. -/ structure CoordinatedKernelStateWF (semantics : CacheSemantics) (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (state : TcState .anon) : Prop where @@ -38,7 +39,7 @@ end CoordinatedKernelStateWF /-- Exhaustive semantic disposition of a successful production call. The standalone constructor is intentionally operational: its semantic result is -the existing K3 theorem, with declaration-specific premises. -/ +the existing declaration-checking theorem, with declaration-specific premises. -/ inductive CheckConstSuccessDisposition (semantics : CacheSemantics) (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (methods : Methods .anon) diff --git a/Ix/Kernel/Verify/Check/CheckerEvidence.lean b/Ix/Kernel/Verify/Check/CheckerEvidence.lean index 7dc5c6ad8..02664b721 100644 --- a/Ix/Kernel/Verify/Check/CheckerEvidence.lean +++ b/Ix/Kernel/Verify/Check/CheckerEvidence.lean @@ -6,7 +6,7 @@ import Ix.Kernel.Verify.Check.FullInferenceCache This module connects the two sequential computation fragments used by `checkConstMember` to the declaration-local evidence consumed by acceptance. -The inference premise is K3's stronger full-mode contract: it starts from a +The inference premise is the declaration checker's stronger full-mode contract: it starts from a raw pretranslation and establishes the typed structural translation itself. The value pipeline is parameterized by the semantic contract for the actual diff --git a/Ix/Kernel/Verify/Check/DeclarationValidation.lean b/Ix/Kernel/Verify/Check/DeclarationValidation.lean index 68824d8c1..af934dbd6 100644 --- a/Ix/Kernel/Verify/Check/DeclarationValidation.lean +++ b/Ix/Kernel/Verify/Check/DeclarationValidation.lean @@ -6,7 +6,7 @@ import Ix.Kernel.Verify.Check.DeclarationIngress This module lifts expression-validator soundness to the production `validateConstWellScoped` boundary for the standalone declaration kinds -handled by K3. Finite-run coverage and no-wrap size budgets are explicit +handled by declaration-checking. Finite-run coverage and no-wrap size budgets are explicit resources; neither is inferred from a successful validator return. -/ diff --git a/Ix/Kernel/Verify/Check/FullInference.lean b/Ix/Kernel/Verify/Check/FullInference.lean index 197c96b91..e685a8a0f 100644 --- a/Ix/Kernel/Verify/Check/FullInference.lean +++ b/Ix/Kernel/Verify/Check/FullInference.lean @@ -4,8 +4,8 @@ import Ix.Kernel.Verify.Infer.CacheSoundness /-! # Full inference from untyped checker ingress -The ordinary K2 contract starts from `TrKExprS`, which already contains the -typing facts checked by full inference. K3 instead starts from +The ordinary inference contract starts from `TrKExprS`, which already contains +the typing facts checked by full inference. Declaration checking starts from `PreTrKExprS` and must return the missing typed translation together with the usual inference result. @@ -31,7 +31,7 @@ def FullInferPost (trProj : RawProjRel) (world : VerifyWorld) namespace FullInferPost -/-- Strengthen the ordinary K2 inference post once the current source has +/-- Strengthen the ordinary inference post once the current source has independently been upgraded to a typed structural translation. -/ theorem of_typed {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} diff --git a/Ix/Kernel/Verify/Check/FullInferenceApplications.lean b/Ix/Kernel/Verify/Check/FullInferenceApplications.lean index 263823f25..263114b41 100644 --- a/Ix/Kernel/Verify/Check/FullInferenceApplications.lean +++ b/Ix/Kernel/Verify/Check/FullInferenceApplications.lean @@ -4,12 +4,12 @@ import Ix.Kernel.Verify.Check.InferencePolicy /-! # Full inference for applications -K2 proves application inference from an already typed `TrKExprS` source. +The ordinary application-inference proof starts from a typed `TrKExprS` source. That premise is circular at checker ingress: the application constructor of `TrKExprS` already says that the function and argument have compatible types. -This file proves the corresponding K3 branch from `PreTrKExprS`. Its +This file proves the corresponding declaration-checking branch from `PreTrKExprS`. Its callback context deliberately records the additional operational fact needed by full checking: recursive inference, Pi exposure, and DefEq all restore `inferOnly = false`, including on partial errors. The later concrete-knot @@ -23,7 +23,7 @@ open Ix.Theory.Named (VExpr) /-- Strong recursive services used while reconstructing a typed translation from successful full inference. These are properties of one concrete -smaller method table, rather than consequences of the ordinary K2 method +smaller method table, rather than consequences of the ordinary inference/conversion method contract. -/ structure FullInferenceStepContext (semantics : CacheSemantics) (trProj : RawProjRel) @@ -84,9 +84,9 @@ structure FullInferenceStepContext namespace FullInferenceStepContext -/-- Assemble the strong K3 callback record from independent semantic proofs +/-- Assemble the strong declaration-checking callback record from independent semantic proofs and the outcome-sensitive operational policy frame. The separation matters: -ordinary K2 soundness does not mention `inferOnly`, while the policy audit +ordinary inference/conversion soundness does not mention `inferOnly`, while the policy audit does not claim typing. -/ theorem of_semantic_and_policy {semantics : CacheSemantics} {trProj : RawProjRel} @@ -243,7 +243,7 @@ private theorem throwApplicationMismatch_full_wf exact hrec methods hmethods /-- Semantic reconstruction for a fully checked application. In contrast -to K2's application lemma, argument compatibility is obtained from the +to the typed application's inference lemma, argument compatibility is obtained from the actual recursive inference and true DefEq result, not from the source translation premise. -/ private theorem fullApplicationResult @@ -342,7 +342,7 @@ private theorem finishFullApplication_wf /-- The full-mode application branch, starting from an untyped structural translation. This theorem is deliberately indexed by one concrete smaller -method table and its stronger K3 callback context. -/ +method table and its stronger declaration-checking callback context. -/ theorem inferUncached_app_full_wf {alpha : Type} {initial : TcState .anon} {program : TcM .anon alpha} {requests : List WalkerRequest} {support : RunSupport} diff --git a/Ix/Kernel/Verify/Check/FullInferenceBinders.lean b/Ix/Kernel/Verify/Check/FullInferenceBinders.lean index 6000b42af..1bbfee7ad 100644 --- a/Ix/Kernel/Verify/Check/FullInferenceBinders.lean +++ b/Ix/Kernel/Verify/Check/FullInferenceBinders.lean @@ -9,8 +9,8 @@ open Ix.Theory (VLevel) /-! # Full inference for binding forms -K2's lambda and forall proofs assume the complete source translation is -already typed. These K3 branches instead start from `PreTrKExprS`, validate +The ordinary lambda and forall inference proofs assume a typed source +translation. These full-inference branches start from `PreTrKExprS`, validate the domain, infer the freshly opened body, and close its newly established typed translation back to the original de Bruijn syntax. -/ diff --git a/Ix/Kernel/Verify/Check/FullInferenceCache.lean b/Ix/Kernel/Verify/Check/FullInferenceCache.lean index 17d4a6c3e..9355a9be2 100644 --- a/Ix/Kernel/Verify/Check/FullInferenceCache.lean +++ b/Ix/Kernel/Verify/Check/FullInferenceCache.lean @@ -4,10 +4,10 @@ import Ix.Kernel.Verify.Infer.CacheSoundness /-! # Full-inference cache shell -The K3 uncached dispatcher establishes a typed source translation from +The full-inference uncached dispatcher establishes a typed source translation from `PreTrKExprS`. This module closes the production `inferWith` cache shell around that result. Full-cache hits are reconciled with the current raw -translation; misses construct ordinary collision-robust K2 provenance before +translation; misses construct ordinary collision-robust inference/conversion provenance before writing the validated cache partition. -/ @@ -37,7 +37,7 @@ private theorem cacheReferences · exact context.base.references hty hresult /-- Execute a full-mode cache miss and install its result only after the -typed source translation has supplied ordinary K2 inference provenance. +typed source translation has supplied ordinary inference provenance. Both the uncached body and the cache write preserve full mode on errors. -/ private theorem missTail_full_wf {alpha : Type} {initial : TcState .anon} {program : TcM .anon alpha} @@ -95,7 +95,7 @@ namespace RecM /-- Complete production `inferWith` in full mode from untyped structural ingress. A hit upgrades the current raw translation from cache provenance; -a miss runs the exhaustive K3 dispatcher and writes validated provenance. -/ +a miss runs the exhaustive full-inference dispatcher and writes validated provenance. -/ theorem inferWith_full_wf {alpha : Type} {initial : TcState .anon} {program : TcM .anon alpha} {requests : List WalkerRequest} {trProj : RawProjRel} diff --git a/Ix/Kernel/Verify/Check/FullInferenceDispatcher.lean b/Ix/Kernel/Verify/Check/FullInferenceDispatcher.lean index 0e0549307..1a0097058 100644 --- a/Ix/Kernel/Verify/Check/FullInferenceDispatcher.lean +++ b/Ix/Kernel/Verify/Check/FullInferenceDispatcher.lean @@ -4,8 +4,8 @@ import Ix.Kernel.Verify.Infer.Dispatcher /-! # Exhaustive full-mode inference dispatcher -This module assembles the constructor-local K3 proofs for -`inferUncached inferCall false`. Unlike the K2 dispatcher, its input is only +This module assembles the constructor-local declaration-checking proofs for +`inferUncached inferCall false`. Unlike the inference/conversion dispatcher, its input is only `PreTrKExprS`; successful execution establishes the missing typed source translation as part of `FullInferPost`. @@ -19,7 +19,7 @@ namespace Ix.Kernel namespace FullUncachedInference /-- Resources for one full-mode layer over a fixed smaller method table. -`uncachedPolicy` covers the leaf actions reused from K2, while +`uncachedPolicy` covers the leaf actions reused from inference/conversion, while `projectionPolicy` exposes the corresponding frame for the projection helper itself. Both are purely operational obligations to be discharged by the concrete policy closure proof. -/ @@ -69,7 +69,7 @@ private theorem strengthenFullLeaf · intro _ _ post exact post.1 -/-- Exhaustive K3 correctness of `inferUncached` in full mode. Every syntax +/-- Exhaustive declaration-checking correctness of `inferUncached` in full mode. Every syntax constructor is covered from untyped structural ingress, and both outcomes retain `inferOnly = false`. -/ theorem inferUncached_full_wf diff --git a/Ix/Kernel/Verify/Check/FullInferenceKnot.lean b/Ix/Kernel/Verify/Check/FullInferenceKnot.lean index b6eedb221..77f888682 100644 --- a/Ix/Kernel/Verify/Check/FullInferenceKnot.lean +++ b/Ix/Kernel/Verify/Check/FullInferenceKnot.lean @@ -5,8 +5,8 @@ import Ix.Kernel.Verify.RecursiveMethods.Closure /-! # Full-inference closure of the production recursion knot -K2 closes the ordinary six-field semantic contract from an already typed -source. K3 needs a stronger contract for the inference field: when the +The ordinary six-field semantic contract assumes a typed source. +Declaration checking needs a stronger inference contract: when the caller is in full mode, successful inference must construct the typed source translation from `PreTrKExprS`, and both success and partial errors must restore full mode. @@ -21,8 +21,8 @@ namespace Ix.Kernel namespace Methods -/-- Strong K3 contract for the inference field of one fixed method table. -Unlike ordinary K2 inference, this starts from untyped structural ingress and +/-- Strong declaration-checking contract for the inference field of one fixed method table. +Unlike ordinary inference, this starts from untyped structural ingress and records the full-mode frame on both outcomes. -/ def FullInferenceWFAt (semantics : CacheSemantics) (trProj : RawProjRel) @@ -101,7 +101,7 @@ def fullInferenceContext · exact ProjectionInference.preservesInferOnlyAt methods hpolicy hnextPolicy.whnf -/-- One unfolded production inference layer satisfies K3 whenever its +/-- One unfolded production inference layer satisfies the full-mode contract whenever its strictly smaller callback table satisfies the three independent premises. -/ theorem next_fullInferenceWFAt {alpha : Type} {initial : TcState .anon} {program : TcM .anon alpha} diff --git a/Ix/Kernel/Verify/Check/FullInferenceLeaves.lean b/Ix/Kernel/Verify/Check/FullInferenceLeaves.lean index 46d13aafe..dc7af501d 100644 --- a/Ix/Kernel/Verify/Check/FullInferenceLeaves.lean +++ b/Ix/Kernel/Verify/Check/FullInferenceLeaves.lean @@ -6,9 +6,9 @@ open Ix.Theory (VLevel) # Full inference for untyped leaf ingress The leaf constructors of `PreTrKExprS` already contain every premise of the -corresponding `TrKExprS` constructor. They therefore reuse the completed K2 +corresponding `TrKExprS` constructor. They therefore reuse the ordinary inference operational proofs directly and strengthen only the postcondition. The -application and binder constructors remain genuinely new K3 work because +application and binder constructors require separate full-inference proofs because their typed constructors contain the checks full inference must establish. -/ diff --git a/Ix/Kernel/Verify/Check/FullInferenceProjections.lean b/Ix/Kernel/Verify/Check/FullInferenceProjections.lean index cfe360bb4..ade2a4fee 100644 --- a/Ix/Kernel/Verify/Check/FullInferenceProjections.lean +++ b/Ix/Kernel/Verify/Check/FullInferenceProjections.lean @@ -4,8 +4,8 @@ import Ix.Kernel.Verify.Infer.ProjectionTypes /-! # Full inference for projections -The K2 projection branch starts from a typed `TrKExprS` source. At checker -ingress K3 instead has only `PreTrKExprS`: it first establishes a typed +The ordinary projection-inference branch starts from a typed `TrKExprS` source. +At checker ingress only `PreTrKExprS` is available: full inference establishes a typed translation for the projected value, then delegates to the already verified `inferProj` helper. @@ -27,7 +27,7 @@ def PreservesInferOnlyAt (methods : Methods .anon) : Prop := ∀ structId field val valTy, ((RecM.inferProj structId field val valTy).run methods).PreservesInferOnly -/-- Strong projection-helper contract needed by K3 full inference. It is +/-- Strong projection-helper contract needed by full inference. It is fixed to the smaller production method table and retains full mode on both success and error. -/ def FullWFAt (semantics : CacheSemantics) (trProj : RawProjRel) @@ -50,9 +50,9 @@ def FullWFAt (semantics : CacheSemantics) (trProj : RawProjRel) InferPost trProj world uvars Delta projectedV result) (fun _ after => after.inferOnly = false) -/-- Combine K2 projection soundness with the independent full-mode frame. +/-- Combine projection-inference soundness with the independent full-mode frame. This is the only adapter from the ordinary, method-parametric projection -contract to K3's fixed-table contract. -/ +contract to the declaration checker's fixed-table contract. -/ theorem FullWFAt.of_semantic_and_policy {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} diff --git a/Ix/Kernel/Verify/Check/InferencePolicy.lean b/Ix/Kernel/Verify/Check/InferencePolicy.lean index cba828a38..1441a0aaa 100644 --- a/Ix/Kernel/Verify/Check/InferencePolicy.lean +++ b/Ix/Kernel/Verify/Check/InferencePolicy.lean @@ -7,7 +7,7 @@ import Ix.Kernel.Verify.Whnf.StructEta.RecursionClassifier # Inference-policy frames Full inference is selected by the mutable `TcState.inferOnly` bit. The -ordinary K1/K2 semantic contracts intentionally ignore operational flags, so +ordinary reduction, inference, and conversion semantic contracts intentionally ignore operational flags, so they cannot by themselves justify that a recursive callback which starts in full mode returns in full mode. @@ -421,7 +421,7 @@ theorem withInferOnly (x : TcM .anon alpha) : cases x {before with inferOnly := true} <;> rfl /-- Combine an existing semantic Hoare proof with an independent policy -frame. This is the adapter used by K3 callback contexts. -/ +frame. This is the adapter used by declaration-checking callback contexts. -/ theorem strengthenWF {I : TcState .anon → Prop} {before : TcState .anon} {x : TcM .anon alpha} {Q : alpha → TcState .anon → Prop} @@ -642,8 +642,8 @@ theorem methodsN_preservesInferOnly simpa [Methods.methodsN_succ, Nat.succ_eq_add_one] using hclosed (methodsN n) ih -/-- The ordinary K2 DefEq contract plus its independent operational frame is -exactly the strong DefEq callback required by K3 full inference. -/ +/-- The ordinary DefEq contract plus its independent operational frame is +exactly the strong DefEq callback required by full inference. -/ theorem PreservesInferOnly.isDefEq_full_wf {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} diff --git a/Ix/Kernel/Verify/Check/MemberEvidence.lean b/Ix/Kernel/Verify/Check/MemberEvidence.lean index 33589ff34..84a3fa286 100644 --- a/Ix/Kernel/Verify/Check/MemberEvidence.lean +++ b/Ix/Kernel/Verify/Check/MemberEvidence.lean @@ -439,7 +439,7 @@ theorem checkConstMember_validation_success cases validationValue exact ⟨afterValidation, rfl⟩ -/-- End-to-end member-level K3 result from an untyped pending declaration. +/-- End-to-end member-level declaration-checking result from an untyped pending declaration. Successful production validation constructs the pretranslation; successful checking constructs semantic evidence; the pure acceptance layer then promotes exactly the pending target in the ghost world. -/ diff --git a/Ix/Kernel/Verify/Check/PositiveFuelSort.lean b/Ix/Kernel/Verify/Check/PositiveFuelSort.lean index 39cc0d1be..493a4020e 100644 --- a/Ix/Kernel/Verify/Check/PositiveFuelSort.lean +++ b/Ix/Kernel/Verify/Check/PositiveFuelSort.lean @@ -6,8 +6,8 @@ import Ix.Kernel.Verify.ScopedSuffix.ClosedContext /-! # Positive-fuel bounded checker witness -This fixture instantiates the corrected C1A/K3 interfaces at recursion fuel -one. Its method-call domain contains exactly one closed sort inference; its +This fixture instantiates bounded method and declaration-checking contracts +at recursion fuel one. Its call domain contains one closed sort inference; its finite result footprint contains that source and its successor-sort result. The joint suffix model remains an explicit semantic parameter, but the call schedule, syntax, reduction of collision freedom to two exact digest @@ -124,7 +124,7 @@ def theory (uvars : Nat) : /-! ## Concrete run-scoped suffix instance -/ -/-- K2S's production suffix model for this closed fixture. Unlike the +/-- Scoped production suffix model for this closed fixture. Unlike the legacy theorems below, this value contains only the singleton normalized context input reached by the run. -/ def scopedModel : ScopedKernelSuffixModel RawProjRel.none VerifyWorld.empty := @@ -268,7 +268,7 @@ theorem scheduleAtFuelOne Methods.SortSchedule.two (support_collisionFree separation) source_supported result_supported (theory model.keys.uvars) trustedReferences -/-- Concrete C1A contract for the outer production body at fuel one. Its +/-- Concrete bounded method contract for the outer production body at fuel one. Its only admitted method call is inference of `source`. -/ theorem methodContractAtFuelOne (separation : AddressSeparation) @@ -281,7 +281,7 @@ theorem methodContractAtFuelOne simpa [Methods.SortSchedule.calls] using (scheduleAtFuelOne separation model).nextSelected -/-- Concrete strong K3 inference contract obtained from the bounded C1A +/-- Concrete strong full inference contract obtained from the bounded method contract because sort pretranslation is already typed. -/ theorem fullInferenceAtFuelOne (separation : AddressSeparation) @@ -296,7 +296,7 @@ theorem fullInferenceAtFuelOne (Methods.next_preservesInferOnly _ (Methods.methodsN_concrete_preservesInferOnly 1)) -/-- Declaration-local K3 pipeline resources at fuel one. The type pipeline +/-- Declaration-local pipeline resources at fuel one. The type pipeline admits one sort inference and no WHNF/DefEq callback. -/ def pipelinesAtFuelOne (separation : AddressSeparation) @@ -310,7 +310,7 @@ def pipelinesAtFuelOne def concreteAxiom : KConst .anon := .axio () () false 0 source -/-- The concrete sort axiom is covered by the positive-fuel K3 resources. -/ +/-- The concrete sort axiom is covered by the positive-fuel declaration-checking resources. -/ theorem pipelines_cover_concreteAxiom (separation : AddressSeparation) (model : KernelSuffixModel RawProjRel.none VerifyWorld.empty) : diff --git a/Ix/Kernel/Verify/Check/PreTranslationScopes.lean b/Ix/Kernel/Verify/Check/PreTranslationScopes.lean index d1bf29d5c..822ef8b4a 100644 --- a/Ix/Kernel/Verify/Check/PreTranslationScopes.lean +++ b/Ix/Kernel/Verify/Check/PreTranslationScopes.lean @@ -7,7 +7,7 @@ import Ix.Kernel.Verify.Infer.LetScopes # Binder scopes for pre-typed checker ingress The ordinary inference scope theorem assumes the binder body already has a -typed `TrKExprS` witness. K3 cannot make that assumption: full inference is +typed `TrKExprS` witness. Declaration checking cannot make that assumption: full inference is the operation which must construct the witness. This wrapper combines the factored operational binder-opening core with `PreTrKExprS.openFVarZero` and the independent inference-policy frame. @@ -138,7 +138,7 @@ end TcM namespace RecM /-- Scope a pre-translated binder around one fixed recursive method table. -Unlike the ordinary K2 scope rule, the body need not be typed before the +Unlike the ordinary inference/conversion scope rule, the body need not be typed before the continuation runs: the continuation receives its exact opened pre-translation and may establish typing by recursive full inference. -/ theorem withLctxScope_openBinder_pre_wf diff --git a/Ix/Kernel/Verify/Check/PublicBlocks.lean b/Ix/Kernel/Verify/Check/PublicBlocks.lean index 3817f3e11..48393fc4a 100644 --- a/Ix/Kernel/Verify/Check/PublicBlocks.lean +++ b/Ix/Kernel/Verify/Check/PublicBlocks.lean @@ -6,7 +6,7 @@ import Ix.Kernel.Verify.Check.QuotientBoundary /-! # Public coordinated-block checker theorem -This is E0's stable public import frontier. It instantiates the recursive +This is block admission's stable public import frontier. It instantiates the recursive driver with the exact finite method table chosen by `TcM.runRec` and crosses the public error-isolation wrapper. Successful isolation is transparent, so the semantic disposition is indexed by the public checker's exact final @@ -19,8 +19,8 @@ namespace TcM.checkConst /-- A successful public checker call either atomically accepts the exact routed block or executes the separately verified standalone branch. The -coordinated body certifier is explicitly relative to K3 singleton-definition -evidence or E2's inductive/recursor oracle resources. -/ +coordinated body certifier is explicitly relative to declaration-checking singleton-definition +evidence or inductive/recursor oracle resources. -/ theorem blockDisposition {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} diff --git a/Ix/Kernel/Verify/Check/PublicStandalone.lean b/Ix/Kernel/Verify/Check/PublicStandalone.lean index dae7ce08d..e0b98c862 100644 --- a/Ix/Kernel/Verify/Check/PublicStandalone.lean +++ b/Ix/Kernel/Verify/Check/PublicStandalone.lean @@ -4,13 +4,13 @@ import Ix.Kernel.Verify.RecursiveMethods.Public /-! # Public standalone constant checking -The member and driver proofs establish K3 for a fixed method table. This +The member and driver proofs establish declaration-checking for a fixed method table. This module instantiates that table with the exact finite approximation selected by production `TcM.runRec`, and then crosses `isolateCheckErrors`. The latter is transparent on success, so the certified final state is exactly the state returned by the public checker. -Whole-block coordination remains the separately named E0 boundary. The +Whole-block coordination remains the separately named block admission boundary. The theorem below therefore requires `StandaloneRoute`; axioms discharge it definitionally, while standalone definitions may supply a finite routing proof for their concrete block environment. @@ -60,7 +60,7 @@ theorem rollback_preserves_kernel hbefore.restoreCheckCachesOnError hfailedCore hfailedIntern⟩ /-- Successful public checking of a pending standalone declaration produces -the concrete K3 acceptance result and promotes exactly that declaration into +the concrete declaration-checking acceptance result and promotes exactly that declaration into a trusted ghost world. The recursive callbacks and the stronger checker inference pipeline are both restricted to the successor-layer call domain selected by the finite production schedule. -/ @@ -209,7 +209,7 @@ theorem wf /-- An intrinsically ill-typed pending standalone declaration cannot be accepted by the public checker. The contradiction uses the raw pending -translation and freshness to turn K3's successful semantic evidence into the +translation and freshness to turn the declaration checker's successful semantic evidence into the forbidden Theory declaration transition; no typing fact is assumed at ingress. -/ theorem rejected_of_no_decl_wf @@ -260,7 +260,7 @@ theorem rejected_of_no_decl_wf hfresh⟩ := hpending exact False.elim <| hnotWF (hraw.wfOfAccepted hfresh hresult.1.accepted) -/-- Axioms take the standalone route by definition, so their public K3 +/-- Axioms take the standalone route by definition, so their public declaration-checking theorem has no residual block-coordination premise. -/ theorem axiom_pending_sound {before : TcState .anon} {id : KId .anon} diff --git a/Ix/Kernel/Verify/Check/QuotientBoundary.lean b/Ix/Kernel/Verify/Check/QuotientBoundary.lean index 1ea1fb437..e868282b9 100644 --- a/Ix/Kernel/Verify/Check/QuotientBoundary.lean +++ b/Ix/Kernel/Verify/Check/QuotientBoundary.lean @@ -12,7 +12,7 @@ member where it is observed. The semantic catalog relation likewise has no coordinated quotient constructor. Quotient acceptance therefore uses the separate four-check atomic bridge; it -never acquires inductive-oracle or block-cache authority through E0. +never acquires inductive-oracle or block-cache authority through block admission. -/ namespace Ix.Kernel @@ -91,7 +91,7 @@ end Catalog namespace StandaloneRoute /-- Quotient declarations take the operational standalone branch, while -their semantic acceptance remains deliberately outside K3/E0. -/ +their semantic acceptance is outside these standalone and block-admission theorems. -/ theorem quotientRoute (I : TcState .anon → Prop) (methods : Methods .anon) (name : Mode.anon.F Name) (levelParams : Mode.anon.F (Array Name)) diff --git a/Ix/Kernel/Verify/Check/SafetyFrame.lean b/Ix/Kernel/Verify/Check/SafetyFrame.lean index 816416774..85544605c 100644 --- a/Ix/Kernel/Verify/Check/SafetyFrame.lean +++ b/Ix/Kernel/Verify/Check/SafetyFrame.lean @@ -3,7 +3,7 @@ import Ix.Kernel.Verify.Check.ValidatorFrame /-! # State framing for the unsafe-reference traversal -`checkNoUnsafeRefs` is semantically a safety guard. For K3 its important +`checkNoUnsafeRefs` is semantically a safety guard. For declaration-checking its important operational property is that the iterative expression walk changes checker state only through optional constant lookup. Consequently every outcome preserves any invariant framed by the installed lazy-ingress hook. diff --git a/Ix/Kernel/Verify/Check/Scoped.lean b/Ix/Kernel/Verify/Check/Scoped.lean index 9f4ced707..7ae9d0ad6 100644 --- a/Ix/Kernel/Verify/Check/Scoped.lean +++ b/Ix/Kernel/Verify/Check/Scoped.lean @@ -6,7 +6,7 @@ open Ix.Theory (VLevel) /-! # Successful well-scopedness validation -`PendingDecl` deliberately permits raw, ill-scoped syntax. K3 therefore +`PendingDecl` deliberately permits raw, ill-scoped syntax. Declaration checking therefore cannot assume universe-parameter or de Bruijn bounds when it starts checking a declaration: those facts have to be recovered from the successful production `validateExprWellScoped` pass. diff --git a/Ix/Kernel/Verify/Check/ScopedActiveBlock.lean b/Ix/Kernel/Verify/Check/ScopedActiveBlock.lean index 4768295e6..73c10e0a0 100644 --- a/Ix/Kernel/Verify/Check/ScopedActiveBlock.lean +++ b/Ix/Kernel/Verify/Check/ScopedActiveBlock.lean @@ -20,7 +20,7 @@ can consume the active-member disjunct. namespace Ix.Kernel -/-- The complete K1/K2 state invariant while one exact coordinated block is +/-- The complete reduction, inference, and conversion state invariant while one exact coordinated block is active, refined by membership in a finite suffix-model state domain. -/ structure ScopedActiveWhnfStateInv {trProj : RawProjRel} {world : VerifyWorld} @@ -121,7 +121,7 @@ theorem of_internSemanticFrame /-- Changing only operational bookkeeping fields preserves the complete active scoped invariant. The suffix-domain frame is separate from the semantic-state equations because context-digest scope also observes memo and -fault fields which ordinary K1 cache provenance intentionally ignores. -/ +fault fields which ordinary WHNF cache provenance intentionally ignores. -/ theorem of_semantic_fields_eq {trProj : RawProjRel} {world : VerifyWorld} {model : ScopedKernelSuffixModel trProj world} diff --git a/Ix/Kernel/Verify/Check/ScopedBoundedPipelines.lean b/Ix/Kernel/Verify/Check/ScopedBoundedPipelines.lean index b81ed4d73..7b2f4c650 100644 --- a/Ix/Kernel/Verify/Check/ScopedBoundedPipelines.lean +++ b/Ix/Kernel/Verify/Check/ScopedBoundedPipelines.lean @@ -4,7 +4,7 @@ import Ix.Kernel.Verify.RecursiveMethods.ScopedCallDomains /-! # Run-scoped standalone-checker pipelines -This is K3's bounded type/value pipeline with `StateInScope` retained across +This is the declaration checker's bounded type/value pipeline with `StateInScope` retained across every method callback. It deliberately consumes `Methods.ScopedWFAtOn` directly and contains no conversion to the legacy global suffix model. -/ @@ -38,7 +38,7 @@ def ScopedFullInferenceWFAtOn namespace ScopedFullInferenceWFAtOn -/-- Ordinary scoped inference upgrades to the K3 contract wherever raw +/-- Ordinary scoped inference upgrades to the declaration-checking contract wherever raw ingress is intrinsically typed. -/ theorem ofTypedIngress {trProj : RawProjRel} {world : VerifyWorld} @@ -88,7 +88,7 @@ end ScopedFullInferenceWFAtOn end Methods -/-- Declaration-local K3 resources whose method contracts preserve the +/-- Declaration-local resources whose method contracts preserve the finite suffix-state witness. -/ structure ScopedStandalonePipelineResources {trProj : RawProjRel} {world : VerifyWorld} diff --git a/Ix/Kernel/Verify/Check/ScopedPositiveFuelCertificate.lean b/Ix/Kernel/Verify/Check/ScopedPositiveFuelCertificate.lean index bb9f4a65c..58067dc8d 100644 --- a/Ix/Kernel/Verify/Check/ScopedPositiveFuelCertificate.lean +++ b/Ix/Kernel/Verify/Check/ScopedPositiveFuelCertificate.lean @@ -299,7 +299,7 @@ theorem runAssumptions (separation : AddressSeparation) : ⟨by simpa [requests] using public_requests separation, support_collisionFree separation, request_coverage, request_bounds⟩ -/-- The complete K2S public-run package for a real fuel-one checker +/-- The complete scoped method public-run package for a real fuel-one checker execution. Its method-call schedule and its suffix-state domain are both finite, but deliberately separate: the call domain contains the source sort, while the run support also contains the constructed successor sort. -/ diff --git a/Ix/Kernel/Verify/Check/SingletonInductive.lean b/Ix/Kernel/Verify/Check/SingletonInductive.lean index 4e0679e3c..059dd873f 100644 --- a/Ix/Kernel/Verify/Check/SingletonInductive.lean +++ b/Ix/Kernel/Verify/Check/SingletonInductive.lean @@ -6,9 +6,9 @@ import Ix.Kernel.Verify.Inductive.SingletonOracle /-! # Certificate-backed singleton family blocks -E0 fixes the exact physical block, classifier, execution trace, and active +Block admission fixes the exact physical block, classifier, execution trace, and active post-state. `SingletonFamilyCatalogLink` fixes the same member array and -constructs its semantic oracle from the E2a transaction. This module joins +constructs its semantic oracle from the certified generation transaction. This module joins those independently audited indices, so a successful production family block does not need an additional ambient inductive oracle. @@ -22,7 +22,7 @@ namespace Ix.Kernel namespace SingletonFamilyCatalogLink /-- The exact family/constructor link supplies all oracle-backed resources -for an E0 inductive-block trace. -/ +for an inductive-block admission trace. -/ def blockResources {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} @@ -72,7 +72,7 @@ end SingletonRecursorCatalogLink namespace RecM /-- Certify one actual successful singleton family/constructor block by -combining E0's exact trace with E2a/E2b's exact catalog link. -/ +combining block admission's exact trace with the certified singleton catalog link. -/ theorem certifySingletonFamilyBlock {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {methods : Methods .anon} @@ -91,7 +91,7 @@ theorem certifySingletonFamilyBlock certifyOracleBackedBlock trace hexact (link.blockResources activePost) /-- Certify one actual successful singleton enumeration recursor block by -combining E0's exact trace with E2a/E2b's generated-rule correspondence. -/ +combining block admission's exact trace with the certified singleton generated-rule correspondence. -/ theorem certifySingletonRecursorBlock {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {methods : Methods .anon} @@ -221,7 +221,7 @@ theorem certifySingletonRecursorIngressBlock /-- Join one actual anonymous family-block ingress execution to one actual successful production checker-body execution. The semantic catalog link is constructed internally from the conversion interpretation, publication -trace, loaded-catalog invariant, trusted log, and E2a transaction. -/ +trace, loaded-catalog invariant, trusted log, and certified generation transaction. -/ theorem certifySingletonFamilyIngressExecution {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {methods : Methods .anon} @@ -255,7 +255,7 @@ theorem certifySingletonFamilyIngressExecution /-- Join one actual anonymous recursor-block ingress execution to one actual successful production recursor checker-body execution. Positional generated -equation and iota-pattern facts remain derived from the E2a certificate and +equation and iota-pattern facts remain derived from the certified generation certificate and the supported enumeration shape. -/ theorem certifySingletonRecursorIngressExecution {semantics : CacheSemantics} {trProj : RawProjRel} diff --git a/Ix/Kernel/Verify/Check/StandaloneDriver.lean b/Ix/Kernel/Verify/Check/StandaloneDriver.lean index 5e4f707c1..6abd32a65 100644 --- a/Ix/Kernel/Verify/Check/StandaloneDriver.lean +++ b/Ix/Kernel/Verify/Check/StandaloneDriver.lean @@ -12,7 +12,7 @@ changing which pending declaration is certified. namespace Ix.Kernel -/-- Operational boundary separating K3 standalone checking from E0 block +/-- Operational boundary separating standalone declaration checking from block coordination. The exact production router must preserve the checker invariant and select no coordinated block. This condition is definitionally inhabited for axioms; definition-family instances are discharged when their @@ -203,7 +203,7 @@ theorem checkConstMemberFresh_pending_evidence /-- Lift the fresh-member theorem through the exact standalone branch of `RecM.checkConst`. The first required lookup and router are both executed -before the production reset; `StandaloneRoute` makes the E0 boundary +before the production reset; `StandaloneRoute` makes the block admission boundary explicit and rules out silently treating block acceptance as member acceptance. -/ theorem checkConst_standalone_pending_sound diff --git a/Ix/Kernel/Verify/Check/ValidatorFrame.lean b/Ix/Kernel/Verify/Check/ValidatorFrame.lean index adee2bc3b..27cf4caf9 100644 --- a/Ix/Kernel/Verify/Check/ValidatorFrame.lean +++ b/Ix/Kernel/Verify/Check/ValidatorFrame.lean @@ -286,7 +286,7 @@ theorem validateExprWellScoped_frame /-- Standalone declaration validation preserves the checker invariant on both outcomes. The resource witness restricts this theorem to the axiom and -definition shapes owned by K3. -/ +definition shapes owned by declaration-checking. -/ theorem validateConstWellScoped_frame {support : RunSupport} {c : KConst .anon} (hresources : StandaloneValidationResources support c) diff --git a/Ix/Kernel/Verify/Ctx.lean b/Ix/Kernel/Verify/Ctx.lean index 6cc948568..6fbce5403 100644 --- a/Ix/Kernel/Verify/Ctx.lean +++ b/Ix/Kernel/Verify/Ctx.lean @@ -39,8 +39,8 @@ equations) so they are robust to record-update spelling; the pop lemmas require the Δ-head to be the popped `(none, d)` entry — Δ is per-call ghost data, so the soundness layers always know the head form at a pop site (popping under live newer fvars would be a scoping bug and is -deliberately unrepresentable). K0 has replaced the `truncate` and -`restoreDepth` `while` loops with explicit Nat recursion; their exact +deliberately unrepresentable). The `truncate` and `restoreDepth` loops use +explicit Nat recursion; their exact production equations live in `Verify/Totalization`. Preservation lemmas for `CtxRecon` over more than one pop remain part of the checker-soundness layer. -/ diff --git a/Ix/Kernel/Verify/Decl.lean b/Ix/Kernel/Verify/Decl.lean index 0d46a008d..b3f583445 100644 --- a/Ix/Kernel/Verify/Decl.lean +++ b/Ix/Kernel/Verify/Decl.lean @@ -8,8 +8,8 @@ open Ix.Theory (VLevel) /-! # Raw, pending, and trusted declarations -This is the additive G1b boundary between a catalogued declaration and a -Theory declaration. The distinction is deliberately sharp: +This module separates catalogued declarations from well-formed Theory +declarations: * `RawExprRel` is syntax-directed. In particular, it has no typing premises, no level-well-formedness premises, and no literal-well-formedness premises. @@ -18,8 +18,7 @@ Theory declaration. The distinction is deliberately sharp: * `RawDeclRel` preserves the standalone declaration kind and translates its type and (for definitions) value. This slice covers axioms and the three definition kinds. Quotient and inductive-family declarations require an - atomic multi-target relation and are intentionally left to the corresponding - block milestone. + atomic multi-target relation and are outside this standalone relation. * `PendingDecl` contains raw correspondence, catalog closure, and absence of the target from both the trusted index and the Theory constant table. It contains no `VConstant.WF`, `VDecl.WF`, or equivalent field. @@ -397,7 +396,7 @@ end RawDeclRel /-- The target's Theory name is not already installed. Together with an untrusted target id, this blocks self-justification through constant lookup. -Warm-cache provenance and isolation are state-specific G4 obligations. -/ +Warm-cache provenance and isolation additionally depend on the checker state. -/ def TargetFresh (world : VerifyWorld) (id : KId .anon) : Prop := ∀ ⦃name⦄, world.nameOf id.addr = some name → world.venv.constants name = none @@ -488,8 +487,8 @@ theorem no_target_lookup {trProj : RawProjRel} {world : VerifyWorld} exact ⟨name, hname, hfresh hname⟩ /-- The pending target cannot occur as a constant/projection head in its own -translated type or value. This is the G1b self-unfolding barrier; cache -provenance and collision-mediated aliasing are addressed in G4/G3. -/ +translated type or value. Cache provenance and collision-mediated aliasing +are addressed separately in `Verify.Cache` and `Verify.Support`. -/ theorem no_self_expr_reference {trProj : RawProjRel} {world : VerifyWorld} {id : KId .anon} {d : VDecl} (h : PendingDecl trProj world id d) : ∃ concrete, world.catalog id = some concrete ∧ @@ -582,7 +581,7 @@ theorem theoryDecl_not_wf : cases hwf with | «axiom» hconstant _ => exact theoryConstant_not_wf hconstant -/-- Machine-checked G1b acceptance witness: raw correspondence and pending +/-- Adversarial isolation witness: raw correspondence and pending status are constructible for a declaration whose Theory WF judgment is false. -/ theorem pending_but_not_wf : diff --git a/Ix/Kernel/Verify/DefEq.lean b/Ix/Kernel/Verify/DefEq.lean index 9bd1dfb45..37823e989 100644 --- a/Ix/Kernel/Verify/DefEq.lean +++ b/Ix/Kernel/Verify/DefEq.lean @@ -3,7 +3,7 @@ import Ix.Kernel.Verify.Whnf.Closure import Batteries.Data.UInt /-! -# K2 definitional-equality cache semantics +# Definitional-equality cache semantics For checker soundness, a cached `true` must denote Theory definitional equality. A cached `false` (including the narrow failure set) can only reject @@ -563,8 +563,8 @@ they may later be proved from a finite context-digest collision hypothesis and the declarative suffix-closure theorem, but must not be inferred from a bare address equality. -/ -/-- One context-key interpretation sufficient for every K1/K2 semantic cache -family. Operational representation is shared, while WHNF, inference, DefEq, +/-- One context-key interpretation sufficient for every reduction, inference, +and conversion cache family. Operational representation is shared, while WHNF, inference, DefEq, and the auxiliary proposition classifier each state their own context-transport consequence. -/ structure KernelSuffixModel (trProj : RawProjRel) (world : VerifyWorld) where @@ -671,7 +671,7 @@ theorem defEqCtxKey_model_matches_wf end TcM /-- Declarative sufficiency of one normalized context-digest input. This is -the semantic half of K2's suffix theorem: equality of the exact input—not +the semantic half of the suffix theorem: equality of the exact input—not equality of its Blake3 output—must preserve each judgment family at the radius that production requested. -/ structure ContextSuffixSemantics {trProj : RawProjRel} {world : VerifyWorld} @@ -824,7 +824,7 @@ def transports {trProj : RawProjRel} {world : VerifyWorld} end ScopedKernelSuffixModel /-- The ordinary checker invariant refined by membership in one explicit -suffix-model state domain. K2S uses this predicate at every model-dependent +suffix-model state domain. Scoped method proofs use this predicate at every model-dependent key boundary; the unscoped invariant remains available for model-independent helpers and legacy compatibility theorems. -/ def ScopedWhnfStateInv {trProj : RawProjRel} {world : VerifyWorld} @@ -1033,7 +1033,7 @@ end TcM namespace KernelSuffixModel -/-- Forget the K2 transports and recover exactly the K1 suffix model. -/ +/-- Forget the inference/conversion transports and recover exactly the WHNF suffix model. -/ def toWhnfSuffixModel {trProj : RawProjRel} {world : VerifyWorld} (model : KernelSuffixModel trProj world) : WhnfSuffixModel trProj world where @@ -1223,7 +1223,7 @@ theorem result {keys : WhnfContextKeys} {trProj : RawProjRel} end DefEqCacheValid -/-- Overlay K2 def-eq meanings on K1+inference cache semantics. -/ +/-- Overlay definitional-equality meanings on WHNF+inference cache semantics. -/ def defEqCacheSemantics (keys : WhnfContextKeys) (trProj : RawProjRel) (fallback : CacheSemantics) : CacheSemantics where Valid := DefEqCacheValid keys trProj fallback @@ -1241,7 +1241,7 @@ def defEqCacheSemantics (keys : WhnfContextKeys) (trProj : RawProjRel) intro authority support block h exact fallback.blockSuccessSound authority support block h -/-- Canonical K1+K2 semantic stack. K1's WHNF and fixed-universe unfold +/-- Canonical reduction and typing semantic stack. WHNF and fixed-universe unfold layers stay outermost; inference and def-eq occupy precisely the fallback families they own. -/ def kernelCacheSemantics (keys : WhnfContextKeys) (trProj : RawProjRel) : diff --git a/Ix/Kernel/Verify/DefEq/BoolTrue.lean b/Ix/Kernel/Verify/DefEq/BoolTrue.lean index 515983494..fe11f7d7e 100644 --- a/Ix/Kernel/Verify/DefEq/BoolTrue.lean +++ b/Ix/Kernel/Verify/DefEq/BoolTrue.lean @@ -113,7 +113,7 @@ theorem boolTrueReductionAllowed_wf rcases hread with ⟨rfl, rfl⟩ exact RecM.WF.pure (E := fun _ _ => True) fun _ => rfl -/-- Direct WHNF contract used by the eager Boolean tier. K1 supplies this +/-- Direct WHNF contract used by the eager Boolean tier. WHNF supplies this for the current unfolded layer; keeping it generic avoids confusing the current reducer with the predecessor-table callback. -/ def DefEqDirectWhnf.WFAt (layer : WhnfLayer) diff --git a/Ix/Kernel/Verify/DefEq/CheapReduction.lean b/Ix/Kernel/Verify/DefEq/CheapReduction.lean index 52500b8f2..d85fbaaec 100644 --- a/Ix/Kernel/Verify/DefEq/CheapReduction.lean +++ b/Ix/Kernel/Verify/DefEq/CheapReduction.lean @@ -144,7 +144,7 @@ structure DefEqCheapReductionContext namespace DefEqCheapReductionContext -/-- Construct the public cheap reducers from their unwrapped K1/K2 body +/-- Construct the public cheap reducers from their unwrapped reduction, inference, and conversion body contracts. -/ theorem ofBodies {layer : WhnfLayer} {semantics : CacheSemantics} diff --git a/Ix/Kernel/Verify/DefEq/EqualRankCache.lean b/Ix/Kernel/Verify/DefEq/EqualRankCache.lean index b4ce02801..59d78da98 100644 --- a/Ix/Kernel/Verify/DefEq/EqualRankCache.lean +++ b/Ix/Kernel/Verify/DefEq/EqualRankCache.lean @@ -44,7 +44,7 @@ end CacheEntry namespace DefEqFailureCacheResources -/-- The joint K2 suffix model supplies failure-marker provenance without any +/-- The joint inference/conversion suffix model supplies failure-marker provenance without any semantic equality premise; validity of this partition is deliberately vacuous on acceptance. -/ theorem ofKernelSuffixModel diff --git a/Ix/Kernel/Verify/DefEq/EqualRankPrefix.lean b/Ix/Kernel/Verify/DefEq/EqualRankPrefix.lean index b8135d0bb..f1d73194a 100644 --- a/Ix/Kernel/Verify/DefEq/EqualRankPrefix.lean +++ b/Ix/Kernel/Verify/DefEq/EqualRankPrefix.lean @@ -84,7 +84,7 @@ theorem ofPrefix exact (defEqLazyDeltaStepWithEqualRank_wf hfault hcached hafter hI.2.1.wf hpair) methods hmethods hI -/-- Concrete no-acceleration/K2 construction of the equal-rank branch. -/ +/-- Concrete no-acceleration/inference/conversion construction of the equal-rank branch. -/ theorem ofKernelResources {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} (model : KernelSuffixModel trProj world) diff --git a/Ix/Kernel/Verify/DefEq/FinalWhnf/Closure.lean b/Ix/Kernel/Verify/DefEq/FinalWhnf/Closure.lean index 36c1c5e6b..983c19e8f 100644 --- a/Ix/Kernel/Verify/DefEq/FinalWhnf/Closure.lean +++ b/Ix/Kernel/Verify/DefEq/FinalWhnf/Closure.lean @@ -12,14 +12,14 @@ import Ix.Kernel.Verify.DefEq.PropositionClassifier The final comparator consists of an exhaustive structural prefix followed by the ordered Nat, lambda-eta, String, structure-eta, unit-like, and proof- irrelevance fallbacks. This module assembles those independently verified -phases under one canonical K2 suffix model. +phases under one canonical inference/conversion suffix model. -/ namespace Ix.Kernel namespace RecM /-- Concrete resources for every production phase of `isDefEqWhnf`. The -proposition-classifier context fixes the canonical K2 suffix model used by +proposition-classifier context fixes the canonical inference/conversion suffix model used by all cache-aware fields. -/ structure FinalWhnfClosureResources {trProj : RawProjRel} {world : VerifyWorld} (support : RunSupport) diff --git a/Ix/Kernel/Verify/DefEq/FinalWhnf/StringExpansion.lean b/Ix/Kernel/Verify/DefEq/FinalWhnf/StringExpansion.lean index 82744e37e..fc81b3960 100644 --- a/Ix/Kernel/Verify/DefEq/FinalWhnf/StringExpansion.lean +++ b/Ix/Kernel/Verify/DefEq/FinalWhnf/StringExpansion.lean @@ -5,7 +5,7 @@ import Ix.Kernel.Verify.DefEq.StringLiteral # Final-WHNF String-literal expansion This module verifies the ordered, bidirectional String-expansion phase in the -final-WHNF comparator. Each compact literal is expanded by the exact K1 plan, +final-WHNF comparator. Each compact literal is expanded by the exact WHNF plan, whose result translates to the same Theory literal as the source syntax. -/ diff --git a/Ix/Kernel/Verify/DefEq/LazyDeltaClosure.lean b/Ix/Kernel/Verify/DefEq/LazyDeltaClosure.lean index 1716ac4dc..4105ca82a 100644 --- a/Ix/Kernel/Verify/DefEq/LazyDeltaClosure.lean +++ b/Ix/Kernel/Verify/DefEq/LazyDeltaClosure.lean @@ -6,7 +6,7 @@ import Ix.Kernel.Verify.DefEq.StoppedContinuationClosure The bounded driver needs one verified iteration and one verified continuation for a stopped pair. This module joins those independently proved executable -surfaces under the canonical K2 suffix/cache model. +surfaces under the canonical inference/conversion suffix/cache model. -/ namespace Ix.Kernel diff --git a/Ix/Kernel/Verify/DefEq/LazyDeltaIteration.lean b/Ix/Kernel/Verify/DefEq/LazyDeltaIteration.lean index bf6b3180a..dcb9cdca8 100644 --- a/Ix/Kernel/Verify/DefEq/LazyDeltaIteration.lean +++ b/Ix/Kernel/Verify/DefEq/LazyDeltaIteration.lean @@ -9,7 +9,7 @@ module records their exact shared inputs and composes them, in execution order, into the contract for one complete bounded lazy-delta iteration. The remaining inputs are deliberately concrete contracts rather than -acceptance oracles: Nat-offset decomposition, the K1 reducers reused by +acceptance oracles: Nat-offset decomposition, the WHNF reducers reused by DefEq, and finite run-scoped resources for same-head comparison and cache writes. -/ @@ -19,7 +19,7 @@ namespace Ix.Kernel namespace RecM /-- Run-scoped resources needed to assemble every branch of one production -lazy-delta iteration under the canonical K2 cache semantics. -/ +lazy-delta iteration under the canonical inference/conversion cache semantics. -/ structure LazyDeltaIterationResources {trProj : RawProjRel} {world : VerifyWorld} (support : RunSupport) (model : KernelSuffixModel trProj world) where diff --git a/Ix/Kernel/Verify/DefEq/PropositionClassifier.lean b/Ix/Kernel/Verify/DefEq/PropositionClassifier.lean index 28d2118fa..a19aaf3fd 100644 --- a/Ix/Kernel/Verify/DefEq/PropositionClassifier.lean +++ b/Ix/Kernel/Verify/DefEq/PropositionClassifier.lean @@ -6,9 +6,9 @@ open Ix.Theory (VLevel) # Memoized proposition classification This module verifies the production `isPropType` implementation used by -proof irrelevance. Cache hits are interpreted through the joint K2 suffix +proof irrelevance. Cache hits are interpreted through the joint inference/conversion suffix model. Cache misses infer the queried expression, normalize its inferred -type with the direct K1 reducer, and install only a provenance-certified +type with the direct WHNF reducer, and install only a provenance-certified classification. -/ @@ -17,8 +17,8 @@ namespace Ix.Kernel open Ix.Theory.Named (VExpr) /-- Resources needed by the concrete proposition classifier. Direct WHNF -is the already-closed K1 reducer; inference remains a predecessor-table edge -until K2 ties the recursive method-table knot. -/ +is the already-closed WHNF reducer; inference remains a predecessor-table edge +until inference/conversion ties the recursive method-table knot. -/ structure PropositionClassifierContext (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) where model : KernelSuffixModel trProj world diff --git a/Ix/Kernel/Verify/DefEq/StringLiteral.lean b/Ix/Kernel/Verify/DefEq/StringLiteral.lean index 0a89eb4b4..cbc536cfc 100644 --- a/Ix/Kernel/Verify/DefEq/StringLiteral.lean +++ b/Ix/Kernel/Verify/DefEq/StringLiteral.lean @@ -5,7 +5,7 @@ import Ix.Kernel.Verify.Whnf.Projection.StringExpansion # String-literal definitional equality The third recursive tier expands compact String syntax before either side is -normalized. K1's expansion plan proves that the concrete intern transaction +normalized. WHNF's expansion plan proves that the concrete intern transaction terminates with a supported, structurally translatable term. DefEq needs the stronger fact recorded here: that exact generated term translates to the same Theory literal as the compact source syntax. @@ -15,7 +15,7 @@ namespace Ix.Kernel open Ix.Theory.Named (VExpr) -/-- A K1 String-expansion plan together with the exact Theory meaning needed +/-- A WHNF String-expansion plan together with the exact Theory meaning needed by DefEq. Merely knowing that the generated expression has *some* translation would not justify comparing it in place of the source literal. -/ structure DefEqStringExpansionPlan @@ -40,7 +40,7 @@ namespace RecM attribute [local irreducible] strLitToConstructor strLitToConstructorWithPrimitives -/-- A concrete semantic plan strengthens K1's exact-result expansion theorem +/-- A concrete semantic plan strengthens WHNF's exact-result expansion theorem with the particular Theory literal required by DefEq. -/ theorem strLitToConstructor_defeq_plan_wf {layer : WhnfLayer} {semantics : CacheSemantics} diff --git a/Ix/Kernel/Verify/DefEq/Structural.lean b/Ix/Kernel/Verify/DefEq/Structural.lean index 2fc370351..e48f143f4 100644 --- a/Ix/Kernel/Verify/DefEq/Structural.lean +++ b/Ix/Kernel/Verify/DefEq/Structural.lean @@ -262,7 +262,7 @@ namespace DefEqAfterQuick /-- Semantic contract for the production-owned tail after Tier 1 misses. Later tier modules refine and discharge this boundary; keeping it generic in -the cache semantics lets the structural proof be reused at the final K2 +the cache semantics lets the structural proof be reused at the final inference/conversion stack. -/ def WF (layer : WhnfLayer) (semantics : CacheSemantics) (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) diff --git a/Ix/Kernel/Verify/Driver/BooleanAcceptance.lean b/Ix/Kernel/Verify/Driver/BooleanAcceptance.lean index 2690273ea..23b075397 100644 --- a/Ix/Kernel/Verify/Driver/BooleanAcceptance.lean +++ b/Ix/Kernel/Verify/Driver/BooleanAcceptance.lean @@ -4,8 +4,8 @@ import Ix.Kernel.Verify.Inductive.EnumerationAcceptance /-! # Certificate-backed Boolean driver acceptance -This module connects the concrete E2 Boolean generation certificate to the -E3-S production-driver adapter. The runtime checker call remains a required +This module connects the concrete Boolean generation certificate to the +supported fragment production-driver adapter. The runtime checker call remains a required gate, but semantic authority for these two coordinated blocks comes from the fixed family transition and existing-recursor certificates. In particular, the proof does not reinterpret the runtime cache order as a topological @@ -56,7 +56,7 @@ deriving instance DecidableEq for Ixon.LazyConstant deriving instance DecidableEq for AnonWorkItem deriving instance DecidableEq for CheckResult -/-- The E2-certified Theory result, with no concrete Ix declaration trusted +/-- The certified Theory result, with no concrete Ix declaration trusted yet. Catalog, block table, names, and the empty trust predicate are inherited unchanged from the concrete ingress fixture. -/ def stagedWorld : VerifyWorld := @@ -509,7 +509,7 @@ def stagedRecursorResources : theoryAfter recursorId exact familyRecursorSemanticEntry -/-- Transport the fixed family certificate to an arbitrary monotone E1 world. +/-- Transport the fixed family certificate to a world extended by serial checking. No freshness premise is needed: replay unions all exact members idempotently. -/ def familyCertificateResources @@ -519,7 +519,7 @@ def familyCertificateResources stagedFamilyResources.rebaseWorld hle /-- Transport the fixed generated-recursor certificate to an arbitrary -monotone E1 world. -/ +world extended by serial checking. -/ def recursorCertificateResources (current : VerifyWorld) (hle : stagedWorld ≤ current) : CertificateBackedBlockResources current recursorBlockAddress recursorId.addr @@ -532,7 +532,7 @@ def recursorCertificateResources certificate-backed evidence at its *current* semantic world. The actual successful `checkConst` equation remains in the provider interface and hence in `CheckSuccessSound`; it is deliberately not used as a substitute for the -E2 semantic certificate. -/ +inductive semantic certificate. -/ def supportedFragment : CertificateBackedCheckFragment stagedWorld dependencyGraph booleanWork where resources := by @@ -581,10 +581,10 @@ theorem allResultsSucceeded : simp [successfulResults] at hresult rcases hresult with rfl | rfl | rfl | rfl <;> rfl -/-- Whole-driver E3-S witness for a real environment containing an inductive +/-- Whole-driver supported fragment witness for a real environment containing an inductive family and its generated recursor. The theorem consumes the actual `checkEnvAnon` success, the exact finite source domain, the collapsed -dependency schedule, and fixed E2 semantic entries for both coordinated +dependency schedule, and fixed inductive semantic entries for both coordinated blocks. Its dependency path contains no oracle-selected world materialization. -/ theorem subjectWF : SubjectWF stagedWorld dependencyGraph diff --git a/Ix/Kernel/Verify/Driver/Dependencies.lean b/Ix/Kernel/Verify/Driver/Dependencies.lean index ace78133c..2d5101d70 100644 --- a/Ix/Kernel/Verify/Driver/Dependencies.lean +++ b/Ix/Kernel/Verify/Driver/Dependencies.lean @@ -5,7 +5,7 @@ import Ix.Kernel.Verify.Driver.Model `Ixon.Constant.refs` is an intern table, not itself a dependency list. Nat and String nodes index blobs through the same table, and malformed/unused -table entries must not become declaration assumptions. E1 therefore follows +table entries must not become declaration assumptions. Serial composition follows the expression constructors which ingress turns into kernel constants: * `.ref i` contributes `refs[i]`; diff --git a/Ix/Kernel/Verify/Driver/Enumeration.lean b/Ix/Kernel/Verify/Driver/Enumeration.lean index 8bb44fac5..dd39d8d68 100644 --- a/Ix/Kernel/Verify/Driver/Enumeration.lean +++ b/Ix/Kernel/Verify/Driver/Enumeration.lean @@ -557,7 +557,7 @@ theorem expectedAnonWork_matchesCatalog {env : Ixon.Env} intro item hitem addr hcovered exact h.matches_blockOfAddr hitem hcovered -/-- Public production-facing E1 enumeration result. -/ +/-- Production enumeration result used by serial composition. -/ theorem buildAnonWork_exact {env : Ixon.Env} (h : AnonWorkEnvWF env) (hblock : IxonEnv.BlockOfIdempotent env) : ∃ work, diff --git a/Ix/Kernel/Verify/Driver/Fixtures.lean b/Ix/Kernel/Verify/Driver/Fixtures.lean index 1d3567768..f6d4343f8 100644 --- a/Ix/Kernel/Verify/Driver/Fixtures.lean +++ b/Ix/Kernel/Verify/Driver/Fixtures.lean @@ -1,7 +1,7 @@ import Ix.Kernel.Verify.Driver.Serial /-! -# Adversarial E1 fixtures +# Adversarial serial composition fixtures These small, Blake3-independent fixtures exercise the checked-set contracts themselves. They deliberately use fixed distinct addresses so failures in diff --git a/Ix/Kernel/Verify/Driver/Model.lean b/Ix/Kernel/Verify/Driver/Model.lean index 6428b01fa..4865a19ce 100644 --- a/Ix/Kernel/Verify/Driver/Model.lean +++ b/Ix/Kernel/Verify/Driver/Model.lean @@ -3,7 +3,7 @@ import Ix.Kernel.Verify.Check.BlockIdentity /-! # Workset and dependency model -This file is the semantic half of E1. It deliberately keeps three address +This file is the semantic half of serial composition. It deliberately keeps three address roles distinct while representing all of them with the production `Address` type: @@ -127,7 +127,7 @@ namespace VerifyWorld /-- Semantic acceptance for a raw address. Declaration projection and standalone addresses are accepted by `trusted`; a Muts envelope address is -accepted by the atomic `AcceptedBlock` fact established in E0. -/ +accepted by the atomic `AcceptedBlock` fact established in block admission. -/ def AcceptsAddress (world : VerifyWorld) (addr : Address) : Prop := world.trusted (⟨addr, ()⟩ : KId .anon) ∨ world.AcceptedBlock (⟨addr, ()⟩ : KId .anon) diff --git a/Ix/Kernel/Verify/Driver/Serial.lean b/Ix/Kernel/Verify/Driver/Serial.lean index 5a7de4f2c..cc91c904c 100644 --- a/Ix/Kernel/Verify/Driver/Serial.lean +++ b/Ix/Kernel/Verify/Driver/Serial.lean @@ -189,7 +189,7 @@ theorem checkEnvAnon_eq_serial {env : Ixon.Env} rw [h.buildAnonWork_eq_expected] rfl -/-- E1's production serial-driver corollary. All emitted rows succeeding is +/-- Production serial-driver corollary. All emitted rows succeeding is converted to concrete per-item success traces, those traces are interpreted through the per-item success rule, and the resulting admissions are reordered by the proved collapsed-block schedule. -/ diff --git a/Ix/Kernel/Verify/Driver/SupportedAcceptance.lean b/Ix/Kernel/Verify/Driver/SupportedAcceptance.lean index e2c019ad2..3ff087e21 100644 --- a/Ix/Kernel/Verify/Driver/SupportedAcceptance.lean +++ b/Ix/Kernel/Verify/Driver/SupportedAcceptance.lean @@ -5,25 +5,25 @@ import Ix.Kernel.Verify.Driver.Serial /-! # Supported production-checker acceptance -This module is the concrete adapter between the per-call K3/E0 theorems and -E1's serial checked-set composition. It intentionally does not contain an +This module connects per-call declaration and block-admission theorems to +the serial checked-set composition. It does not contain an opaque `checkConst succeeded, therefore the declaration is sound` callback. Instead, every reusable successful call must expose: -* one finite, run-scoped recursive-method context; +* one finite, run-scoped method context; * the exact physical/world cache and block-table invariants for that call; * agreement between the source work item and the block selected by the production router; -* declaration-local K3 resources for an observed standalone route; and +* declaration-local resources for an observed standalone route; and * either constructive scoped singleton-definition evidence or an explicit - E2 oracle-backed resource for every fresh coordinated body; + inductive oracle resource for every fresh coordinated body; * certificate-backed replay resources for coordinated blocks whose semantic entries are already installed in the current Theory environment. The composition theorems below turn those resources into `CheckSuccessSound`, which `Driver.Serial` then composes into `SubjectWF`. The source-to-kernel route agreement remains an explicit representation -premise until the later ingress/refinement phase discharges it generically. +premise requiring a general ingress/refinement proof. -/ namespace Ix.Kernel @@ -32,7 +32,7 @@ namespace AnonWorkItem /-- Exact relation between a production work item and the result observed from `coordinatedBlockFor`. A standalone source entry may be checked either -through K3 (axioms) or through its singleton coordinated block +through declaration-checking (axioms) or through its singleton coordinated block (definitions/recursors). A Muts work item must route to its advertised envelope address. -/ def SelectedBlockMatches (item : AnonWorkItem) : @@ -44,10 +44,10 @@ def SelectedBlockMatches (item : AnonWorkItem) : end AnonWorkItem -/-! ## Standalone K3 resources -/ +/-! ## Standalone declaration-checking resources -/ /-- The declaration-local premises needed when the actual production router -selects K3's standalone path. None of these fields assumes a declaration-WF +selects the declaration checker's standalone path. None of these fields assumes a declaration-WF transition or target trust; `PendingDecl` explicitly asserts the opposite. -/ structure SupportedStandaloneResources {initial : TcState .anon} {id : KId .anon} @@ -76,8 +76,8 @@ structure SupportedStandaloneResources namespace SupportedStandaloneResources -/-- Apply K3 to the exact successful public call and retain only the world -extension and target-trust facts required by E1. -/ +/-- Apply declaration-checking to the exact successful public call and retain only the world +extension and target-trust facts required by serial composition. -/ theorem promotes {initial after : TcState .anon} {id : KId .anon} {requests : List WalkerRequest} {trProj : RawProjRel} @@ -105,11 +105,11 @@ end SupportedStandaloneResources /-! ## Coordinated-body resources -/ -/-- Exhaustive body evidence supported by the E3-S adapter. +/-- Exhaustive body evidence accepted by the fragment adapter. -The first constructor is constructive K3 evidence for the only definition +The first constructor is constructive declaration-checking evidence for the only definition block shape currently modeled atomically by Ix.Theory.Named: one definition. The -second constructor keeps the E2 inductive/recursor oracle visible. In +second constructor keeps the inductive/recursor oracle visible. In particular there is no constructor containing a prebuilt `CertifiedBlockBodySuccess`. -/ inductive SupportedBlockBodyResources @@ -154,7 +154,7 @@ inductive SupportedBlockBodyResources namespace SupportedBlockBodyResources -/-- Turn one transparent supported-body constructor into the exact E0 body +/-- Turn one transparent supported-body constructor into the exact block admission body certificate for the observed trace. -/ theorem certify {initial : TcState .anon} {id : KId .anon} @@ -201,8 +201,8 @@ end SupportedBlockBodyResources namespace CheckBlockKind -/-- Kinds which E3-S may replay from already-installed semantic entries. -Definitions remain on the constructive K3 route; this adapter is only for +/-- Kinds the supported-fragment adapter may replay from installed semantic entries. +Definitions remain on the constructive declaration-checking route; this adapter is only for inductive-family and generated-recursor blocks. -/ def CertificateBacked : CheckBlockKind → Prop | .inductive' | .recursor => True @@ -213,7 +213,7 @@ end CheckBlockKind /-- A coordinated block whose complete semantic entries are already installed in `world.venv`. -This is the reusable E3-S form of E2's fixed semantic certificates. Unlike an +This reuses fixed inductive certificates in the supported fragment. Unlike an `ExistingSemanticBlockCertificate`, it deliberately has no freshness premise: checked-set composition may replay a block in a monotone world which already trusts a proper subset of its exact members. Admission is therefore the @@ -294,7 +294,7 @@ theorem le_admittedWorld ⟨rfl, rfl, rfl, fun {_} hold => Or.inr hold, Ix.Theory.Named.VEnv.LE.rfl⟩ /-- Reindex installed semantic entries across an arbitrary monotone current -world. This is the reusable bridge used by E1 after prior work items have +world. This is the reusable bridge used by serial composition after prior work items have possibly trusted a proper subset of this block. -/ def rebaseWorld {before current : VerifyWorld} {blockAddr primary : Address} @@ -414,7 +414,7 @@ structure SupportedCheckRun (world : VerifyWorld) (item : AnonWorkItem) namespace SupportedCheckRun -/-- K3/E0 assembly for one actual successful production call. -/ +/-- Assemble declaration and block evidence for a successful production call. -/ theorem accepts {world : VerifyWorld} {item : AnonWorkItem} {initial after : TcState .anon} @@ -483,11 +483,11 @@ theorem accepts end SupportedCheckRun -/-! ## Reusable fragment and E1 composition -/ +/-! ## Reusable fragment and serial composition -/ -/-- Exhaustive semantic resources accepted by the supported-fragment -adapter. `operational` is the full K3/E0 state-and-cache route. -`certificateBackedBlock` is the narrower E2 replay route for +/-- Exhaustive semantic resources accepted by the supported fragment +adapter. `operational` is the full declaration and block checking state-and-cache route. +`certificateBackedBlock` is the narrower inductive certification replay route for inductive/recursor blocks whose complete semantic entries are already installed; it does not manufacture a standalone recursive-method context or an oracle-selected future world. -/ @@ -521,7 +521,7 @@ end SupportedCheckEvidence /-- Exact all-block evidence used when every row in a fragment is backed by already-installed semantic entries. Keeping this narrow evidence separate from `SupportedCheckEvidence` gives all-block consumers a dependency path -which cannot reach the operational oracle-backed E0 branch. -/ +which cannot reach the operational oracle-backed block admission branch. -/ inductive CertificateBackedCheckEvidence (world : VerifyWorld) : AnonWorkItem → Type | block {blockAddr primary targets} : @@ -564,7 +564,7 @@ structure CertificateBackedCheckFragment (baseline : VerifyWorld) namespace CertificateBackedCheckFragment -/-- The oracle-free all-block adapter demanded by E1. -/ +/-- The oracle-free all-block adapter demanded by serial composition. -/ theorem checkSuccessSound {baseline : VerifyWorld} {catalog : DependencyCatalog} {work : Array AnonWorkItem} @@ -581,7 +581,7 @@ end CertificateBackedCheckFragment /-- A precisely scoped fragment provider. Resources are requested only when the item is not already accepted in `current`; this keeps the pending/fresh -K3 premise honest while allowing E1 to reuse the rule at arbitrary monotone +declaration-checking premise honest while allowing serial composition to reuse the rule at arbitrary monotone world extensions. The provider may use the accepted external dependencies to establish the run's cache and declaration premises, but cannot assume its own `WorkItemAccepted` conclusion. -/ @@ -601,7 +601,7 @@ structure SupportedCheckFragment (baseline : VerifyWorld) namespace SupportedCheckFragment -/-- The concrete K3/E0 adapter demanded by E1. -/ +/-- The concrete declaration and block checking adapter demanded by serial composition. -/ theorem checkSuccessSound {baseline : VerifyWorld} {catalog : DependencyCatalog} {work : Array AnonWorkItem} @@ -618,8 +618,8 @@ end SupportedCheckFragment namespace AnonWorkEnvWF -/-- E3-S composition specialized to an all-block certificate-backed fragment. -This route keeps the public serial success gate and E1 schedule unchanged while +/-- Serial composition specialized to an all-block certificate-backed fragment. +This route retains the public success gate and serial schedule while excluding the operational oracle-backed body branch from its dependency closure. -/ theorem checkEnvAnon_certificateBacked_subjectWF @@ -642,7 +642,7 @@ theorem checkEnvAnon_certificateBacked_subjectWF exact h.checkEnvAnon_subjectWF hblock hdeps hwf hassumptions hdisjoint fragment.checkSuccessSound cfg hrun hresults -/-- E3-S supported-fragment composition theorem. Successful `checkEnvAnon` +/-- Supported-fragment composition theorem. Successful `checkEnvAnon` rows imply `SubjectWF` for the exact enumerated work/subject sets and the explicit assumption set, provided every still-pending successful call belongs to the transparent supported fragment above. -/ diff --git a/Ix/Kernel/Verify/Driver/SupportedAcceptanceFixtures.lean b/Ix/Kernel/Verify/Driver/SupportedAcceptanceFixtures.lean index 4feb1f616..ef2daa361 100644 --- a/Ix/Kernel/Verify/Driver/SupportedAcceptanceFixtures.lean +++ b/Ix/Kernel/Verify/Driver/SupportedAcceptanceFixtures.lean @@ -5,10 +5,10 @@ import Ix.Kernel.Verify.Inductive.EnumerationAcceptance /-! # Supported-acceptance adversarial and inductive fixtures -These fixtures guard the two representation-sensitive edges of the E3-S +These fixtures guard the two representation-sensitive edges of the supported fragment adapter. The first pair proves that a Muts work item cannot be discharged by an unrouted call or a call routed to a different envelope. The second joins -E2b's concrete Boolean family execution to the adapter's oracle-backed body +the concrete Boolean family execution to the adapter's oracle-backed body constructor; the only remaining inputs are the explicitly advertised scoped recursive context and active cache invariant. -/ @@ -32,23 +32,23 @@ theorem block_rejects_wrong_route : simp [blockItem, AnonWorkItem.SelectedBlockMatches] /-- Standalone source entries deliberately admit either operational branch: -axioms use K3 directly, while singleton definitions and recursors are -committed through E0. -/ +axioms use declaration-checking directly, while singleton definitions and recursors are +committed through block admission. -/ theorem standalone_allows_coordinated_route (selected : Option (KId .anon)) : (AnonWorkItem.standalone E1Fixture.first).SelectedBlockMatches selected := trivial -/-- The fixed-certificate replay surface cannot bypass K3 for definition +/-- The fixed-certificate replay surface cannot bypass declaration-checking for definition blocks. -/ theorem certificate_backed_definition_excluded : ¬ CheckBlockKind.CertificateBacked .defn := by intro h exact h -/-! ## Concrete E2b body bridge -/ +/-! ## Concrete singleton certification body bridge -/ -/-- E2b's actual Boolean family/constructor block inhabits the exact -oracle-backed constructor consumed by E3-S. This theorem is indexed by the +/-- The Boolean family/constructor block inhabits the exact oracle-backed +constructor consumed by the supported-fragment adapter. This theorem is indexed by the real production body states and exact physical member array from `BooleanEnumerationFixture`; it does not replace them with an abstract inductive environment. -/ @@ -72,8 +72,8 @@ def booleanFamilyBodyResources .oracleBacked (BooleanEnumerationFixture.familyLink.blockResources activePost) -/-- The adapter turns that E2b resource and the actual successful production -trace into E0's exact atomic-body certificate. -/ +/-- The adapter turns that singleton certification resource and the actual successful production +trace into block admission's exact atomic-body certificate. -/ theorem booleanFamilyBody_certified {requests : List WalkerRequest} {support : RunSupport} (context : ScopedRecursiveMethodRunContext diff --git a/Ix/Kernel/Verify/Env.lean b/Ix/Kernel/Verify/Env.lean index f80f9f6d9..4957b692e 100644 --- a/Ix/Kernel/Verify/Env.lean +++ b/Ix/Kernel/Verify/Env.lean @@ -41,7 +41,7 @@ content-addressed environment. The structural divergences: `AddInduct` is an empty `-- TODO`). Thus this legacy relation remains uninhabited for envs containing inductives. The trusted-world log below retains `TrustedCatalogLog.ambient` and its explicit `InductiveOracle` as a - compatibility constructor. Certificate-backed E2c paths instead use + compatibility constructor. Certificate-backed inductive verification paths instead use `semanticBlock` and `existingBlock`; migrating the remaining ambient users depends on completing the broader inductive fragment. -/ @@ -184,7 +184,7 @@ theorem TrKDefVal.mono {safety : Ix.DefinitionSafety} /-- Block-level inductive translation in the legacy whole-`KEnv` relation — upstream-parity STUB (their `AddInduct` is an empty inductive pending the - `addInduct` spec). `TrustedCatalogLog.ambient` below is the live G2 path; + `addInduct` spec). `TrustedCatalogLog.ambient` below is the live inductive interface path; this constructor remains only as a quarantined compatibility interface. -/ inductive AddKInduct : HashMap (KId .anon) (KConst .anon) → VEnv → VInductDecl → @@ -377,7 +377,7 @@ theorem TrKEnv.find? {safety : Ix.DefinitionSafety} let ⟨_, H⟩ := H H.find? h hs -/-! ## G1c: trusted-catalog log +/-! ## Trusted-catalog log The legacy `TrKEnv'` above indexes its semantic log by the entire concrete hash map, forcing pending declarations to be WF before `checkConst` runs. @@ -994,7 +994,7 @@ theorem TrustedCatalogRel.promote TrustedCatalogRel.promoteExact hrel hpending hwf exact ⟨world', hexact.promotes, hworld, hdecl⟩ -/-- The G1b ill-typed pending world already satisfies the G1c trusted-log +/-- The ill-typed pending world satisfies the trusted-log invariant: its catalog entry remains completely outside the empty log. -/ theorem IllTypedPending.trustedCatalogRel : TrustedCatalogRel RawProjRel.none IllTypedPending.world := @@ -1076,7 +1076,7 @@ theorem trustedCatalogRel : TrustedCatalogRel RawProjRel.none world := TrustedCatalogLog.empty -/-- Positive G1c fixture: supplying the new WF derivation promotes exactly +/-- Positive trusted-catalog log fixture: supplying the new WF derivation promotes exactly the pending id and immediately yields trusted lookup evidence. -/ theorem promotes : ∃ world', diff --git a/Ix/Kernel/Verify/Execution.lean b/Ix/Kernel/Verify/Execution.lean index 4cac142ea..049890d12 100644 --- a/Ix/Kernel/Verify/Execution.lean +++ b/Ix/Kernel/Verify/Execution.lean @@ -1,7 +1,7 @@ import Ix.Kernel.Verify.Support /-! -# G3b: execution-indexed finite run assumptions +# Execution-indexed finite run assumptions An explicit request list is not evidence that it describes a checker run: choosing `[]` would make coverage and bounds vacuous. `ExecutionRequests` is diff --git a/Ix/Kernel/Verify/Inductive.lean b/Ix/Kernel/Verify/Inductive.lean index 82567cb06..416ae2e1f 100644 --- a/Ix/Kernel/Verify/Inductive.lean +++ b/Ix/Kernel/Verify/Inductive.lean @@ -8,9 +8,7 @@ open Ix.Theory (VLevel) /-! # Ambient inductive oracle -G2 introduced this interface before Ix.Theory.Named had a usable inductive -specification, so it records the semantic consequences needed by the checker -directly: +This interface records the semantic consequences needed by the checker: * every admitted concrete inductive-family constant has an exact raw Theory translation and lookup; @@ -19,15 +17,15 @@ directly: * every concrete recursor rule has an explicit, well-formed Theory defeq witness headed by that recursor. -Pin A now provides Ix.Theory.Named's proved normalized `GenerationCertificate` and +Ix.Theory.Named provides the normalized `GenerationCertificate` and proved `addInductCertified` transaction. `Inductive/Certificate.lean` derives the Theory-owned environment, lookup, freshness, and rule-registration facts from that certificate. It intentionally cannot supply the Ix-owned catalog/name translation, checker-execution, and recursor-pattern fields below. `InductiveOracle` therefore remains an explicit assumption boundary, not a -claim that Ix's inductive checker has already been verified. E2b must combine -the certificate facts with actual Ix block checking and pattern-generation +claim that Ix's inductive checker has already been verified. The singleton +adapters combine certificate facts with Ix block checking and pattern-generation proofs. Keeping the interface in terms of semantic consequences permits a closed Nat model while the recursor clause prevents WHNF proofs from treating computation rules as an unrecorded ambient fact. @@ -372,7 +370,7 @@ structure RecursorRulePattern where (constructorParams.toNat + constructorFields.toNat)).Check /-- Finite production metadata required by one recursor pattern, separated -from its semantic rewrite law so E2 adapters can show exactly which part is +from its semantic rewrite law so inductive certification adapters can show exactly which part is discharged by catalog/layout correspondence. -/ structure RawRecursorRulePatternMetadataRel (catalog : Catalog) (nameOf : Address → Option Lean.Name) (id : KId .anon) @@ -519,7 +517,7 @@ oracle from re-certifying an existing trusted id. The oracle records `before ≤ after` rather than requiring every consumer to carry a transaction equation. `CertifiedGenerationFacts` now derives this -Theory-owned portion; the remaining fields are the E2b Ix correspondence +Theory-owned portion; the remaining fields are the singleton certification Ix correspondence boundary. -/ structure InductiveOracle (trProj : RawProjRel) (catalog : Catalog) (nameOf : Address → Option Lean.Name) (trusted : KId .anon → Prop) diff --git a/Ix/Kernel/Verify/Inductive/AliasFormerAdmission.lean b/Ix/Kernel/Verify/Inductive/AliasFormerAdmission.lean index e0ad3d12c..ddb7f9896 100644 --- a/Ix/Kernel/Verify/Inductive/AliasFormerAdmission.lean +++ b/Ix/Kernel/Verify/Inductive/AliasFormerAdmission.lean @@ -4,7 +4,7 @@ import Ix.Kernel.Verify.Inductive.OneFamilyAdmission /-! # Oracle-free family-result-normalizing singleton admission -This module closes the concrete `AliasFormer` E2c transaction. The +This module closes the concrete `AliasFormer` inductive verification transaction. The transparent `TypeFamilyAlias` dependency is promoted first, the family/constructor block advances that explicit Theory world, and the independently stored generated recursor is then admitted from semantic entries already present in diff --git a/Ix/Kernel/Verify/Inductive/AliasFormerCertificate.lean b/Ix/Kernel/Verify/Inductive/AliasFormerCertificate.lean index 54c3894b8..7c4018e68 100644 --- a/Ix/Kernel/Verify/Inductive/AliasFormerCertificate.lean +++ b/Ix/Kernel/Verify/Inductive/AliasFormerCertificate.lean @@ -53,14 +53,14 @@ theorem beforeWF : typeFamilyAliasEnv.WF := /-- Exact post-environment selected by that certificate. -/ def finalEnv : VEnv := aliasFormerFinalEnv -/-- The exact Spec-01E package. Its producer-shape index is deliberately +/-- The exact producer-selected package. Its producer-shape index is deliberately inferred here because Ix.Theory.Named keeps the fixture's concrete shape witness private while exposing this public dependent existence theorem. -/ def exactPackage := Classical.choice aliasFormerExactProducedGenerationCandidatePackage_exists /-- The exact successful outer metadata producer, dependent semantic package, -and certified Theory insertion retained as one E2c transaction. Construction +and certified Theory insertion retained as one inductive verification transaction. Construction keeps the producer-selected source and generation indices intact. -/ def exactProducedTransaction := ExactProducedGenerationTransaction.mk @@ -75,7 +75,7 @@ def exactProducedTransaction := exact aliasFormer_addInductCertified_checked) beforeWF -/-- Intentional operational erasure of the exact Spec-01E indices. -/ +/-- Operational erasure of the exact source and generation indices. -/ def producedTransaction : ProducedGenerationTransaction typeFamilyAliasEnv finalEnv [] := exactProducedTransaction.toProduced diff --git a/Ix/Kernel/Verify/Inductive/AliasRecAdmission.lean b/Ix/Kernel/Verify/Inductive/AliasRecAdmission.lean index a1027c0ee..dbd0a63e4 100644 --- a/Ix/Kernel/Verify/Inductive/AliasRecAdmission.lean +++ b/Ix/Kernel/Verify/Inductive/AliasRecAdmission.lean @@ -4,7 +4,7 @@ import Ix.Kernel.Verify.Inductive.OneFamilyAdmission /-! # Oracle-free recursive-field-normalizing direct-recursive admission -This module closes the concrete `AliasRec` E2c transaction. The +This module closes the concrete `AliasRec` inductive verification transaction. The transparent `RecAlias` dependency is promoted first, the family/constructor block advances that explicit Theory world, and the independently stored generated recursor is then admitted from semantic entries already present in diff --git a/Ix/Kernel/Verify/Inductive/AnnotatedPiAdmission.lean b/Ix/Kernel/Verify/Inductive/AnnotatedPiAdmission.lean index b5e2899b8..7673958b9 100644 --- a/Ix/Kernel/Verify/Inductive/AnnotatedPiAdmission.lean +++ b/Ix/Kernel/Verify/Inductive/AnnotatedPiAdmission.lean @@ -4,7 +4,7 @@ import Ix.Kernel.Verify.Inductive.OneFamilyAdmission /-! # Oracle-free annotation-normalizing recursive-Pi admission -This module closes the concrete `AnnotatedPi` E2c transaction. The +This module closes the concrete `AnnotatedPi` inductive verification transaction. The transparent `outParam` dependency is promoted first, the family/constructor block advances that explicit Theory world, and the independently stored generated recursor is then admitted from semantic entries already present in diff --git a/Ix/Kernel/Verify/Inductive/AnnotatedPiCertificate.lean b/Ix/Kernel/Verify/Inductive/AnnotatedPiCertificate.lean index cef1c052a..891f50b68 100644 --- a/Ix/Kernel/Verify/Inductive/AnnotatedPiCertificate.lean +++ b/Ix/Kernel/Verify/Inductive/AnnotatedPiCertificate.lean @@ -6,8 +6,7 @@ import Ix.Theory.Named.Verify.Environment.InductiveFixtures `AnnotatedPi.mk` retains the raw binder domain `outParam Prop`, while the Ix.Theory.Named analyzer classifies recursion through the normalized domain `Prop`. -This is the first Ix transaction whose certified generation is deliberately -non-identity: the stored family and generated artifacts preserve raw syntax, +The certified generation is non-identity: the stored family and generated artifacts preserve raw syntax, but recursive classification is owned by the checked view produced by the ordinary Ix.Theory.Named candidate pipeline. @@ -59,14 +58,14 @@ theorem beforeWF : outParamEnv.WF := /-- Exact post-environment selected by that certificate. -/ def finalEnv : VEnv := annotatedPiFinalEnv -/-- The exact Spec-01E package. Its producer-shape index is deliberately +/-- The exact producer-selected package. Its producer-shape index is deliberately inferred here because Ix.Theory.Named keeps the fixture's concrete shape witness private while exposing this public dependent existence theorem. -/ def exactPackage := Classical.choice annotatedPiExactProducedGenerationCandidatePackage_exists /-- The exact successful outer metadata producer, dependent semantic package, -and certified Theory insertion retained as one E2c transaction. Construction +and certified Theory insertion retained as one inductive verification transaction. Construction keeps the producer-selected source and generation indices intact. -/ def exactProducedTransaction := ExactProducedGenerationTransaction.mk @@ -81,7 +80,7 @@ def exactProducedTransaction := exact annotatedPi_addInductCertified) beforeWF -/-- Intentional operational erasure of the exact Spec-01E indices. -/ +/-- Operational erasure of the exact source and generation indices. -/ def producedTransaction : ProducedGenerationTransaction outParamEnv finalEnv [] := exactProducedTransaction.toProduced diff --git a/Ix/Kernel/Verify/Inductive/BlockCertificate.lean b/Ix/Kernel/Verify/Inductive/BlockCertificate.lean index 7f9c1c284..1ca0fde33 100644 --- a/Ix/Kernel/Verify/Inductive/BlockCertificate.lean +++ b/Ix/Kernel/Verify/Inductive/BlockCertificate.lean @@ -3,7 +3,7 @@ import Ix.Theory.Named.Typing.InductiveCertificate /-! # Certified mutual-inductive block transactions -This is the Theory-only consumer boundary for Ix.Theory.Named's Spec-08 block +This is the Theory-only consumer boundary for Ix.Theory.Named's block generation certificate. A mutual block is one atomic semantic transaction: all family constants are inserted before any constructors, all constructors before any recursors, and all recursors before the globally flattened iota @@ -65,7 +65,7 @@ def toBlockCertificate {source : VInductDecl} {before after : VEnv} success := tx.success beforeWF := tx.beforeWF -/-- Recover Spec-08's exact four-phase trace for the atomic block. -/ +/-- Recover the exact four-phase generation trace for the atomic block. -/ theorem trace {source : VInductDecl} {before after : VEnv} (tx : CertifiedBlockGenerationTransaction source before after) : Nonempty (VEnv.AddInductBlockGenerationTrace before after diff --git a/Ix/Kernel/Verify/Inductive/ConstructorPositivityTraversal.lean b/Ix/Kernel/Verify/Inductive/ConstructorPositivityTraversal.lean index 7df19c39f..efdcdfd41 100644 --- a/Ix/Kernel/Verify/Inductive/ConstructorPositivityTraversal.lean +++ b/Ix/Kernel/Verify/Inductive/ConstructorPositivityTraversal.lean @@ -4,7 +4,7 @@ import Ix.Kernel.Verify.Inductive.RecursivePositivityTraversal # Complete constructor-positivity traversal `PositivityDomainTrace` starts after production has already opened the shared -parameter prefix and selected a constructor field. E2c also needs evidence +parameter prefix and selected a constructor field. Inductive verification also needs evidence that those field calls are the ones reached by the enclosing `checkPositivity` execution. This module retains that missing outer spine: parameter opening, source-ordered field traversal, and final local-context diff --git a/Ix/Kernel/Verify/Inductive/ConstructorValidationTraversal.lean b/Ix/Kernel/Verify/Inductive/ConstructorValidationTraversal.lean index 106a2826f..e1f3e162e 100644 --- a/Ix/Kernel/Verify/Inductive/ConstructorValidationTraversal.lean +++ b/Ix/Kernel/Verify/Inductive/ConstructorValidationTraversal.lean @@ -4,7 +4,7 @@ import Ix.Kernel.Verify.Inductive.ConstructorPositivityTraversal # Production constructor-validation traversal `ConstructorPositivityTrace` classifies a successful strict-positivity call, -but E2c must also establish that the call is the one selected by production's +but inductive verification must also establish that the call is the one selected by production's complete constructor-validation branch. This module retains the exact A1–A4 execution around that call: derived metadata, shared-parameter agreement, safety gating, field-universe validation, and constructor-return validation. diff --git a/Ix/Kernel/Verify/Inductive/EliminationBreadthFixture.lean b/Ix/Kernel/Verify/Inductive/EliminationBreadthFixture.lean index 7d8f4403d..0eebd5367 100644 --- a/Ix/Kernel/Verify/Inductive/EliminationBreadthFixture.lean +++ b/Ix/Kernel/Verify/Inductive/EliminationBreadthFixture.lean @@ -8,7 +8,7 @@ import Ix.Theory.Named.Verify.Environment.EliminationFixturesSmall /-! # Concrete small-elimination and K-target breadth -Ix.Theory.Named's Spec-06 fixtures retain the exact kernel elimination traversal and +Ix.Theory.Named's elimination fixtures retain the exact kernel traversal and align it with the generated Theory metadata. This module takes the remaining Ix step: it compiles those same kernel declarations to production Ixon blocks, ingresses the family and recursor projections, and runs both production block diff --git a/Ix/Kernel/Verify/Inductive/EnumerationFixture.lean b/Ix/Kernel/Verify/Inductive/EnumerationFixture.lean index c02c71f8e..f4ac6d198 100644 --- a/Ix/Kernel/Verify/Inductive/EnumerationFixture.lean +++ b/Ix/Kernel/Verify/Inductive/EnumerationFixture.lean @@ -7,7 +7,7 @@ open Ix.Theory (VLevel) /-! # Concrete singleton-enumeration fixture -This module closes E2b's executable witness with a two-constructor Boolean +This module closes singleton certification's executable witness with a two-constructor Boolean enumeration. The Theory side uses Ix.Theory.Named's checked identity-generation certificate; the concrete side below is built from the actual Ixon block encoding and production anonymous ingress/checker functions. @@ -663,7 +663,7 @@ theorem rawConstZero {expected : KId .anon} {expression : KExpr .anon} /-- Translate the closed core syntax used by the Boolean family and recursor. The partiality is intentional: free variables, lets, projections, and -literals are outside this E2b fixture. Constant translation consults the +literals are outside this singleton certification fixture. Constant translation consults the same immutable Theory environment and address-to-name interpretation used by `RawExprRel`. -/ def translateCore? : KExpr .anon → Option VExpr @@ -771,7 +771,7 @@ private theorem recursorRulesSizeNative : recursorRules.size = 2 := by theorem recursorRulesSize : recursorRules.size = 2 := recursorRulesSizeNative -/-- Total finite selector; the accompanying size theorem proves that E2b +/-- Total finite selector; the accompanying size theorem proves that singleton certification uses it only at actual rule positions. -/ def concreteRuleAt (index : Nat) : RecRule .anon := recursorRules[index]! diff --git a/Ix/Kernel/Verify/Inductive/ExactLeanSyntax.lean b/Ix/Kernel/Verify/Inductive/ExactLeanSyntax.lean index c7bfa00c3..59a3de7c0 100644 --- a/Ix/Kernel/Verify/Inductive/ExactLeanSyntax.lean +++ b/Ix/Kernel/Verify/Inductive/ExactLeanSyntax.lean @@ -5,7 +5,7 @@ import Ix.Kernel.Verify.Inductive.CandidateSyntax Lean's kernel `Level` and `Expr` values expose fast Boolean equality but do not provide the proof-producing decidable equality needed to certify a -native-evaluated checker result. The E2c constructor bridge needs exactly +native-evaluated checker result. The inductive verification constructor bridge needs exactly that implication: a finite computation may observe an expression, but the public proof must recover structural equality without importing reflected implementation equations. @@ -57,7 +57,7 @@ def literalCheck : Lean.Literal → Lean.Literal → Bool All fields which contribute to kernel expression equality are compared, including binder names and annotations. Metavariable nodes are supported so -the checker remains useful for exact failure diagnostics, although E2c's +the checker remains useful for exact failure diagnostics, although inductive verification's successful constructor candidates contain none. -/ def exprCheck : Lean.Expr → Lean.Expr → Bool | .bvar left, .bvar right => decide (left = right) diff --git a/Ix/Kernel/Verify/Inductive/GeneratedRecursorAcceptanceClosure.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorAcceptanceClosure.lean index 78c4e2a5b..6af991ade 100644 --- a/Ix/Kernel/Verify/Inductive/GeneratedRecursorAcceptanceClosure.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorAcceptanceClosure.lean @@ -7,7 +7,7 @@ import Ix.Kernel.Verify.RecursiveMethods.ScopedCallDomains The exhaustive recursor comparison retains exactly one type DefEq call and one RHS DefEq call per positional rule. This module interprets those calls -through K2S's finite successor-layer method contract. It does not require a +through the finite successor-layer contract for scoped methods. It does not require a global DefEq oracle or place every expression in an unbounded call domain. -/ @@ -74,7 +74,7 @@ end Methods.ActiveScopedWFAtOn namespace RecM /-- A successful selected-candidate comparison is semantically canonical -under the same finite K2S successor-layer contract used by the production +under the same finite scoped method successor-layer contract used by the production recursive-method knot. -/ theorem checkGeneratedRecursorCandidate_canonicalScoped {trProj : RawProjRel} {world : VerifyWorld} diff --git a/Ix/Kernel/Verify/Inductive/GeneratedRecursorAdmission.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorAdmission.lean index 6709d5c35..cb69f6640 100644 --- a/Ix/Kernel/Verify/Inductive/GeneratedRecursorAdmission.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorAdmission.lean @@ -4,7 +4,7 @@ import Ix.Kernel.Verify.Inductive.OneFamilyAdmission /-! # Canonical generated-recursor admission -This module closes the first E2c production recursor transaction. The +This module proves admission for a production-generated recursor. The certified family transaction has already installed `IndexedVec.rec` and its two equations in Ix.Theory.Named's Theory environment. Ix nevertheless keeps the separately ingressed recursor block untrusted while @@ -173,7 +173,7 @@ theorem familyMemberCheckStable : /-- One premise-free statement joins the real outer member run, exhaustive canonical artifact comparison, exact oracle-free admission, and stable cache -publication. This is the first E2c recursor transaction closed at the same +publication. This recursor transaction is proved at the same boundary used by coordinated block checking. -/ structure CanonicalRecursorAtomicClosure : Prop where memberRun : diff --git a/Ix/Kernel/Verify/Inductive/GeneratedRecursorCheckerFixture.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorCheckerFixture.lean index f6fcce8c8..090f0ea08 100644 --- a/Ix/Kernel/Verify/Inductive/GeneratedRecursorCheckerFixture.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorCheckerFixture.lean @@ -244,7 +244,7 @@ theorem familyAcceptedWorld_nameOf_eq : familyAcceptedWorld.nameOf = nameOf := rfl /-- Transport the concrete closed-type translations to the exact suffix-model -universe count selected by K2S. -/ +universe count selected by the scoped method contract. -/ theorem familySelectionTranslationsScoped {model : ScopedKernelSuffixModel RawProjRel.none familyAcceptedWorld} (uvars : transaction.certificate.generation.recursor.uvars = @@ -256,7 +256,7 @@ theorem familySelectionTranslationsScoped familyAcceptedWorld_nameOf_eq, ← uvars] using familySelectionTranslations -/-- The concrete selection phase preserves the scoped K2 invariant under the +/-- The concrete selection phase preserves the scoped inference/conversion invariant under the same finite successor layer later used for exhaustive artifact comparison. -/ theorem familySelectionInvariantScoped {model : ScopedKernelSuffixModel RawProjRel.none familyAcceptedWorld} @@ -284,7 +284,7 @@ theorem familySelectionInvariantScoped successor initialInvariant selection /-- All concrete production and representation premises are discharged. The -remaining semantic inputs are selection-state preservation and the K2 meaning +remaining semantic inputs are selection-state preservation and the inference/conversion meaning of the exact three comparison calls. -/ theorem familyCacheCheckCanonical {invariant : TcState .anon → Prop} @@ -309,7 +309,7 @@ theorem familyCacheCheckCanonical familyInstalledRecursorCanonicalAt familyStoredArtifactTranslations familyArtifactCallPlanAt selectionInvariant defEq -/-- Concrete E2c checker closure: one finite K2S successor-layer contract +/-- Concrete checker closure: one finite scoped method successor-layer contract accounts for the selection call, the repeated full-type comparison, and both positional rule comparisons. -/ theorem familyCacheCheckCanonicalScoped diff --git a/Ix/Kernel/Verify/Inductive/GeneratedRecursorInitialInvariant.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorInitialInvariant.lean index 125320ce2..ff507a98a 100644 --- a/Ix/Kernel/Verify/Inductive/GeneratedRecursorInitialInvariant.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorInitialInvariant.lean @@ -1062,7 +1062,7 @@ private theorem familyMemberNatReferenceWhnf : familyMemberWhnfTheory.projections.wf trivial) /-- Lift one finite WHNF census fact to complete provenance in the full -inductive-aware K1/K2 semantic stack. -/ +inductive-aware reduction, inference, and conversion semantic stack. -/ private theorem familyMemberWhnfEntryProvenance {kind : ExprCacheKind} (isWhnf : kind.IsWhnf) {key : Address × Address} {value : KExpr .anon} @@ -1619,7 +1619,7 @@ private theorem familyMemberBlockResultProvenance /-- Every physical semantic cache entry in the reached production state has finite support, authorized direct roots, and the meaning assigned by the -complete K1/K2/inductive cache stack. -/ +complete reduction, inference, conversion, and inductive cache stack. -/ theorem familyMemberInitial_cacheInvariant : CacheInvariant (kernelCacheSemanticsWithInductives familyMemberModel.keys diff --git a/Ix/Kernel/Verify/Inductive/GeneratedRecursorMemberCheck.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorMemberCheck.lean index bb6320dc7..e67b630e2 100644 --- a/Ix/Kernel/Verify/Inductive/GeneratedRecursorMemberCheck.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorMemberCheck.lean @@ -13,7 +13,7 @@ cache entry. Its tail selects one generated entry and exhaustively compares the frozen stored type and rules. This module proves the exact operational handoff. It intentionally does not -postulate that the prelude preserves the K2S invariant; subsequent modules +postulate that the prelude preserves the scoped method invariant; subsequent modules must prove that from the individual production operations. -/ diff --git a/Ix/Kernel/Verify/Inductive/GeneratedRecursorMemberFixture.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorMemberFixture.lean index 17324a966..fd3e7e8c9 100644 --- a/Ix/Kernel/Verify/Inductive/GeneratedRecursorMemberFixture.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorMemberFixture.lean @@ -213,7 +213,7 @@ theorem familyMemberInitial_preservesScoped recursive fixture the `TrustedReferences` premise cannot be instantiated: the installed `cons` rule names `IndexedVec.rec`, which has not yet crossed the recursor-block admission boundary. The active theorem below is the -constructive E2c path. -/ +constructive inductive verification path. -/ theorem familyInstalledRecursorsProvenance {model : ScopedKernelSuffixModel RawProjRel.none familyAcceptedWorld} {layer : WhnfLayer} {support : RunSupport} @@ -2234,7 +2234,7 @@ theorem familyMemberCheckExecution : /-! ## Scoped semantic closure of the reached tail -/ -/-- Selection from the actual post-prelude state preserves the scoped K2S +/-- Selection from the actual post-prelude state preserves the scoped method invariant under the same finite call contract as the cache-only fixture. -/ theorem familyPreparedSelectionInvariantScoped {model : ScopedKernelSuffixModel RawProjRel.none familyAcceptedWorld} diff --git a/Ix/Kernel/Verify/Inductive/GeneratedRecursorSelection.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorSelection.lean index 9183b0c16..973034adc 100644 --- a/Ix/Kernel/Verify/Inductive/GeneratedRecursorSelection.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorSelection.lean @@ -219,7 +219,7 @@ theorem selectGeneratedRecursorIndex_preserves rw [run] at post exact post.1 -/-- K2S's finite successor-layer contract supplies state preservation for +/-- The finite successor-layer contract for scoped methods preserves state during exactly the complete closed type calls named by a selection plan. -/ theorem selectGeneratedRecursorIndex_preservesScoped {trProj : RawProjRel} {world : VerifyWorld} diff --git a/Ix/Kernel/Verify/Inductive/GeneratedRecursorSemantics.lean b/Ix/Kernel/Verify/Inductive/GeneratedRecursorSemantics.lean index 90a3e53e1..821b8e1a0 100644 --- a/Ix/Kernel/Verify/Inductive/GeneratedRecursorSemantics.lean +++ b/Ix/Kernel/Verify/Inductive/GeneratedRecursorSemantics.lean @@ -4,7 +4,7 @@ import Ix.Kernel.Verify.Trans /-! # Canonical semantics of generated recursor artifacts -This is the representation boundary for E2c's generated artifacts. It names +This is the representation boundary for inductive verification's generated artifacts. It names the exact structural correspondence that the production builders must prove: the generated type is Ix.Theory.Named's `GenerationChecked.recType`, and rule `i` is Ix.Theory.Named's `GenerationChecked.rule i` for constructor `i`. Rule lookup is diff --git a/Ix/Kernel/Verify/Inductive/IndexedCandidateSyntax.lean b/Ix/Kernel/Verify/Inductive/IndexedCandidateSyntax.lean index 3629e913a..202490ea7 100644 --- a/Ix/Kernel/Verify/Inductive/IndexedCandidateSyntax.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedCandidateSyntax.lean @@ -24,7 +24,7 @@ The upstream replay exposes the right executable values, but its public trace. Merely mentioning that projection therefore imports the replay's reflection axioms into a downstream theorem statement. Reconstruct the same post-family context and constructor-local binders directly from transparent -data. These are the values E2c relates to production Ix execution. +data. These are the values inductive verification relates to production Ix execution. -/ /-- Post-family context in which the two IndexedVec constructors are checked. diff --git a/Ix/Kernel/Verify/Inductive/IndexedCandidateTransaction.lean b/Ix/Kernel/Verify/Inductive/IndexedCandidateTransaction.lean index 75bfda340..5f19f90ed 100644 --- a/Ix/Kernel/Verify/Inductive/IndexedCandidateTransaction.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedCandidateTransaction.lean @@ -19,7 +19,7 @@ open Ix.Theory.Named open Ix.Theory.Named.InductiveFixtures open Ix.Theory.Named.InductiveReplayFixtures -/-- The Spec-01E package retains the exact source declaration and checked +/-- The producer-selected package retains the exact source declaration and checked generation selected by the ordinary metadata producer. -/ noncomputable def exactPackage : VInductDecl.ExactProducedGenerationCandidatePackage natFinalEnv [`u] @@ -45,7 +45,7 @@ noncomputable def exactProducedTransaction : exact indexedVecSemantic_addInductCertified beforeWF := natWF -/-- Intentional operational erasure of the exact Spec-01E indices. -/ +/-- Operational erasure of the exact source and generation indices. -/ noncomputable def producedTransaction : ProducedGenerationTransaction natFinalEnv indexedVecFinalEnv [`u] := exactProducedTransaction.toProduced diff --git a/Ix/Kernel/Verify/Inductive/IndexedPositivityTransport.lean b/Ix/Kernel/Verify/Inductive/IndexedPositivityTransport.lean index c9fda586b..c0c3cc0e7 100644 --- a/Ix/Kernel/Verify/Inductive/IndexedPositivityTransport.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedPositivityTransport.lean @@ -20,7 +20,7 @@ open Ix.Theory.Named.InductiveReplayFixtures The upstream replay modules are useful for naming the exact candidate states, but their proof lemmas intentionally depend on reflected implementation -equations. E2c does not inherit that trust. Recheck the finite observations +equations. Inductive verification does not inherit that trust. Recheck the finite observations consumed below with private native facts, so the exported transport depends on the concrete executions without admitting those reflected equations. -/ diff --git a/Ix/Kernel/Verify/Inductive/IndexedProducerClosure.lean b/Ix/Kernel/Verify/Inductive/IndexedProducerClosure.lean index 82113542d..e8495a95e 100644 --- a/Ix/Kernel/Verify/Inductive/IndexedProducerClosure.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedProducerClosure.lean @@ -6,7 +6,7 @@ import Ix.Kernel.Verify.Inductive.IndexedCandidateTransaction The trust-minimal `IndexedVec` Theory transaction and the executable Ix.Theory.Named candidate replay are intentionally audited separately. This module -joins them only at a stronger E2c root: the exact producer-selected package +joins them only at a stronger inductive verification root: the exact producer-selected package erases to the same certificate transaction consumed by the Ix family and generated-recursor admission, while all three anonymous ingress calls and both production block checks remain explicit. diff --git a/Ix/Kernel/Verify/Inductive/IndexedRecursiveCertificate.lean b/Ix/Kernel/Verify/Inductive/IndexedRecursiveCertificate.lean index cb47c4ea1..a3dac73ee 100644 --- a/Ix/Kernel/Verify/Inductive/IndexedRecursiveCertificate.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedRecursiveCertificate.lean @@ -7,7 +7,7 @@ import Ix.Theory.Named.Verify.Environment.InductiveFixtures Ix.Theory.Named's `IndexedVec` fixture is the first certificate in the dependency whose source is simultaneously parameterized, indexed, and recursive. This module reconstructs it through the Theory-only proof-carrying transaction -boundary and records the exact breadth facts that distinguish it from E2b's +boundary and records the exact breadth facts that distinguish it from singleton certification's Boolean enumeration. The ambient `Nat` environment is reconstructed through its own certified @@ -75,7 +75,7 @@ normalization candidate; it is not an independently regenerated artifact. -/ transaction.certificate.generation = indexedVecChecked.identityGeneration := rfl -/-- Auditable shape of the first non-enumeration E2c certificate. +/-- Auditable shape of the indexed recursive certificate. The recursive argument is the third constructor field and targets the sole family at the predecessor index. The constructor result advances that index @@ -111,7 +111,7 @@ structure BreadthFacts : Prop where theorem breadth : BreadthFacts := by constructor <;> rfl -/-- Stable semantic consequences obtained only from the Ix E2a adapter. -/ +/-- Stable semantic consequences obtained only from the Ix certified generation adapter. -/ theorem certifiedFacts : CertifiedGenerationFacts natFinalEnv indexedVecFinalEnv transaction.certificate := diff --git a/Ix/Kernel/Verify/Inductive/IndexedRecursiveSoundness.lean b/Ix/Kernel/Verify/Inductive/IndexedRecursiveSoundness.lean index 5c2bedecf..639f00d79 100644 --- a/Ix/Kernel/Verify/Inductive/IndexedRecursiveSoundness.lean +++ b/Ix/Kernel/Verify/Inductive/IndexedRecursiveSoundness.lean @@ -6,7 +6,7 @@ open Ix.Theory (VLevel) # Indexed recursive iota soundness This module opens the two generated `IndexedVec.rec` equations selected by -the E2a certificate and relates them to the dependent patterns consumed by +the certified generation certificate and relates them to the dependent patterns consumed by production WHNF. All equation shapes below reduce from the retained `GenerationChecked.rule`; no independently supplied rewrite law is used. -/ diff --git a/Ix/Kernel/Verify/Inductive/IotaPattern.lean b/Ix/Kernel/Verify/Inductive/IotaPattern.lean index 7022c09d1..b7d826c79 100644 --- a/Ix/Kernel/Verify/Inductive/IotaPattern.lean +++ b/Ix/Kernel/Verify/Inductive/IotaPattern.lean @@ -8,7 +8,7 @@ open Ix.Theory (VLevel) `Pattern.varN` captures application arguments from left to right, but its dependent `Path` type represents the newest argument as `none` and every older argument under another `some`. The definitions and proofs below make -that ordering explicit. E2b uses them to select the certified minor premise +that ordering explicit. Singleton certification uses them to select the certified minor premise at a constructor's exact rule index; an off-by-one or reversed-spine adapter cannot satisfy the positional theorem. -/ diff --git a/Ix/Kernel/Verify/Inductive/MutualBlockCertificate.lean b/Ix/Kernel/Verify/Inductive/MutualBlockCertificate.lean index 15731b877..8794dafb1 100644 --- a/Ix/Kernel/Verify/Inductive/MutualBlockCertificate.lean +++ b/Ix/Kernel/Verify/Inductive/MutualBlockCertificate.lean @@ -4,13 +4,13 @@ import Ix.Theory.Named.Verify.Environment.MutualInductiveFixtures /-! # Genuine mutual-block certificate fixture -`Tree`/`TreeList` is the first E2c witness that cannot be represented honestly -by a singleton transaction. It has two mutually visible families, five +`Tree`/`TreeList` requires one atomic mutual-block transaction. It has two +mutually visible families, five globally flattened constructors, one generated recursor per family, and five globally flattened iota rules. Recursive fields target both the sibling family and their own family, including one recursive occurrence below a Pi. -This module consumes Ix.Theory.Named's retained Spec-08 certificate through Ix's +This module consumes Ix.Theory.Named's block-generation certificate through Ix's Theory-only block adapter. Physical Ix ingress, checker execution, catalog linkage, and recursor admission remain separate obligations. -/ @@ -80,7 +80,7 @@ theorem breadth : BreadthFacts where treeListTargetsItself := rfl /-- Complete block-wide family/constructor/recursor/rule consequences from -the exact retained Spec-08 transaction. -/ +the exact block-generation transaction. -/ theorem certifiedFacts : CertifiedBlockGenerationFacts VEnv.empty treeFinalEnv transaction.certificate := diff --git a/Ix/Kernel/Verify/Inductive/MutualRecursorAdmission.lean b/Ix/Kernel/Verify/Inductive/MutualRecursorAdmission.lean index f2368aad5..d8bcc18b8 100644 --- a/Ix/Kernel/Verify/Inductive/MutualRecursorAdmission.lean +++ b/Ix/Kernel/Verify/Inductive/MutualRecursorAdmission.lean @@ -1017,7 +1017,7 @@ theorem familyRecursorBlockAccepted : familyRecursorAcceptedWorld.AcceptedBlock recursorBlockId := familyRecursorAtomicAdmission.accepted -/-- E3-FP's first conditional mutual checkpoint. Every physical and +/-- Conditional mutual-inductive admission. Every physical and semantic fact is closed for the two-family/five-constructor/two-recursor transaction. Its transitive trust boundary retains exactly the two quarantined upstream witnesses: reversed-family generation WF and generated diff --git a/Ix/Kernel/Verify/Inductive/NestedRecursiveFixture.lean b/Ix/Kernel/Verify/Inductive/NestedRecursiveFixture.lean index 65551c631..ed52f7f89 100644 --- a/Ix/Kernel/Verify/Inductive/NestedRecursiveFixture.lean +++ b/Ix/Kernel/Verify/Inductive/NestedRecursiveFixture.lean @@ -5,7 +5,7 @@ import Ix.Kernel.Verify.Ingress.AnonStructural /-! # Concrete nested-recursive reachability fixture -This fixture isolates the cross-stage E2c obligation with two compiler-shaped +This fixture isolates the cross-stage inductive verification obligation with two compiler-shaped anonymous blocks: * `Box (α : Sort 1) : Sort 1`, with `wrap : α → Box α`; diff --git a/Ix/Kernel/Verify/Inductive/OccurrenceClosure.lean b/Ix/Kernel/Verify/Inductive/OccurrenceClosure.lean index d66f5f8fb..9c24aa2d1 100644 --- a/Ix/Kernel/Verify/Inductive/OccurrenceClosure.lean +++ b/Ix/Kernel/Verify/Inductive/OccurrenceClosure.lean @@ -4,10 +4,10 @@ import Ix.Kernel.Verify.RecursiveMethods.ScopedCallDomains /-! # Run-scoped recursive-occurrence closure -The first E2c occurrence slice exposes the exact `isDefEq` calls made while +Occurrence verification exposes the exact `isDefEq` calls made while checking uniform recursive parameters, but deliberately leaves their semantic meaning behind `PositiveParameterDefEqContract`. This module closes that -boundary against K2S's actual finite successor-layer method contract. +boundary against the finite successor-layer contract for scoped methods. The call evidence is positional. We require only the parameter pairs that the successful production loop executed, rather than admitting every pair of @@ -29,7 +29,7 @@ def PositiveParameterCallPlan (calls : Methods.CallDomain) namespace RecM.PositiveParameterComparisonTrace /-- Interpret every successful parameter comparison through the real -successor method-table layer selected by K2S. +successor method-table layer selected by the scoped method contract. `Methods.next methods` is definitionally the six production algorithms run with `methods` as their recursive callback table. Its `isDefEq` field is diff --git a/Ix/Kernel/Verify/Inductive/OccurrenceValidation.lean b/Ix/Kernel/Verify/Inductive/OccurrenceValidation.lean index 2751b612c..d1edee567 100644 --- a/Ix/Kernel/Verify/Inductive/OccurrenceValidation.lean +++ b/Ix/Kernel/Verify/Inductive/OccurrenceValidation.lean @@ -7,7 +7,7 @@ import Ix.Kernel.Verify.World /-! # Recursive-occurrence validation -E2c consumes the successful branch of production positivity checking. This +Inductive verification consumes the successful branch of production positivity checking. This module makes that branch proof-visible: a success identifies the active family, the exact loaded inductive header, every pure arity/universe/index guard, and the complete state-threaded parameter-definitional-equality loop. @@ -101,8 +101,8 @@ def TranslatedParameterDefEq This contract intentionally stops at the production `isDefEq` call. It does not grant positivity access to the complete DefEq closure, proposition -classification, or inductive authority. K2 may instantiate it from an -oracle-free recursive-method closure; E2c only consumes the successful-call +classification, or inductive authority. Inference/conversion proofs may instantiate it from an +oracle-free recursive-method closure; inductive verification only consumes the successful-call meaning and state preservation recorded here. -/ def PositiveParameterDefEqContract (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) diff --git a/Ix/Kernel/Verify/Inductive/PositivityTraversal.lean b/Ix/Kernel/Verify/Inductive/PositivityTraversal.lean index 338a5867d..12196da49 100644 --- a/Ix/Kernel/Verify/Inductive/PositivityTraversal.lean +++ b/Ix/Kernel/Verify/Inductive/PositivityTraversal.lean @@ -3,7 +3,7 @@ import Ix.Kernel.Verify.Inductive.OccurrenceValidation /-! # Production positivity traversal -E2c must account for the recursive control flow that reaches occurrence +Inductive verification must account for the recursive control flow that reaches occurrence validation, rather than assuming that a particular constructor field was already reduced to an active-family application. This module starts that assembly at the production `checkPositivityDomainFuel` entry point. diff --git a/Ix/Kernel/Verify/Inductive/ProducedGenerationTransaction.lean b/Ix/Kernel/Verify/Inductive/ProducedGenerationTransaction.lean index 0f6b94cc2..2674524d6 100644 --- a/Ix/Kernel/Verify/Inductive/ProducedGenerationTransaction.lean +++ b/Ix/Kernel/Verify/Inductive/ProducedGenerationTransaction.lean @@ -9,7 +9,7 @@ generation certificate and the exact `VEnv.addInductCertified` result, but it does not remember which ordinary Ix.Theory.Named metadata execution selected that certificate. -For E2c we need both facts at once. A successful outer producer call must not +For inductive verification we need both facts at once. A successful outer producer call must not be allowed to justify Theory semantics by itself, and an independently chosen Theory certificate must not be passed off as the result of that producer. The record below therefore owns Ix.Theory.Named's dependent @@ -50,7 +50,7 @@ def certificate {before after : VEnv} {Us : List Name} /-- Erase only the Verify-side producer provenance, preserving the exact package-owned source, certificate, successful post-environment, and input WF -evidence in E2a's Theory-only transaction. -/ +evidence in the Theory-only transaction. -/ def toCertified {before after : VEnv} {Us : List Name} (tx : ProducedGenerationTransaction before after Us) : CertifiedGenerationTransaction tx.source before after where @@ -67,7 +67,7 @@ def toCertified {before after : VEnv} {Us : List Name} tx.toCertified.certificate.generation = tx.package.package.generation := rfl -/-- Exact producer and semantic-transition facts retained at the E2c +/-- Exact producer and semantic-transition facts retained at the inductive verification boundary. In particular, the outer producer equation and the Theory certificate are projections of one dependent package rather than unrelated premises. -/ @@ -98,7 +98,7 @@ end ProducedGenerationTransaction /-! ## Exact dependent producer transactions -/ -/-- The Spec-01E producer closure before source and generation indices are +/-- The producer closure before source and generation indices are erased. Its type retains the exact raw family, kernel source, producer arguments, normalized source declaration, and checked generation selected by one successful outer candidate execution. diff --git a/Ix/Kernel/Verify/Inductive/RecursivePiAdmission.lean b/Ix/Kernel/Verify/Inductive/RecursivePiAdmission.lean index 0d02dcc1b..da7441e39 100644 --- a/Ix/Kernel/Verify/Inductive/RecursivePiAdmission.lean +++ b/Ix/Kernel/Verify/Inductive/RecursivePiAdmission.lean @@ -4,7 +4,7 @@ import Ix.Kernel.Verify.Inductive.OneFamilyAdmission /-! # Oracle-free recursive-Pi admission -This module closes the concrete `Acc` E2c transaction. The family and its +This module closes the concrete `Acc` inductive verification transaction. The family and its constructor are admitted by the exact certified Theory-environment transition. The separately ingressed and production-checked `Acc.rec` block is then admitted from semantic entries already installed by that transition. diff --git a/Ix/Kernel/Verify/Inductive/RecursivePiCertificate.lean b/Ix/Kernel/Verify/Inductive/RecursivePiCertificate.lean index f6f4a4824..19e36c0d8 100644 --- a/Ix/Kernel/Verify/Inductive/RecursivePiCertificate.lean +++ b/Ix/Kernel/Verify/Inductive/RecursivePiCertificate.lean @@ -5,8 +5,7 @@ import Ix.Theory.Named.InductiveFixtures # Certified recursive-Pi generation fixture `Acc.intro` contains a recursive occurrence beneath a two-binder function -telescope. This is the next one-family E2c breadth case after `IndexedVec`: -the recursive argument is not a direct family application, and its induction +telescope. The recursive argument is not a direct family application, and its induction hypothesis is itself a function. This module stays on the Theory-only side of the boundary. It constructs the @@ -81,7 +80,7 @@ structure BreadthFacts : Prop where theorem breadth : BreadthFacts := by constructor <;> rfl -/-- Stable semantic consequences obtained through the same generic E2a +/-- Stable semantic consequences obtained through the same generic certified generation adapter used by the IndexedVec transaction. -/ theorem certifiedFacts : CertifiedGenerationFacts VEnv.empty finalEnv transaction.certificate := diff --git a/Ix/Kernel/Verify/Inductive/RecursivePiSoundness.lean b/Ix/Kernel/Verify/Inductive/RecursivePiSoundness.lean index e880344ef..c0f5dfbe6 100644 --- a/Ix/Kernel/Verify/Inductive/RecursivePiSoundness.lean +++ b/Ix/Kernel/Verify/Inductive/RecursivePiSoundness.lean @@ -6,7 +6,7 @@ open Ix.Theory (VLevel) # Recursive-Pi iota soundness This module proves that the production `Acc.intro` iota pattern denotes the -exact registered equation generated by the E2a certificate. Both uniform +exact registered equation generated by the certified generation certificate. Both uniform parameters and the result index are aligned semantically before the closed equation is applied. The pattern RHS remains the exact registered RHS application, so the functional induction hypothesis beneath two lambdas is diff --git a/Ix/Kernel/Verify/Inductive/SingletonEnumeration.lean b/Ix/Kernel/Verify/Inductive/SingletonEnumeration.lean index ff64eaac0..8d6a4a3b5 100644 --- a/Ix/Kernel/Verify/Inductive/SingletonEnumeration.lean +++ b/Ix/Kernel/Verify/Inductive/SingletonEnumeration.lean @@ -5,14 +5,14 @@ open Ix.Theory (VLevel) /-! # Certified singleton enumerations -This module is E2b's first executable inductive fragment. A singleton +This module certifies a fragment of singleton inductives. A singleton enumeration has no declaration universes, parameters, indices, constructor fields, or recursive arguments. It may have several nullary constructors, so its generated iota rules are non-vacuous: rule `i` returns the exact `i`-th minor premise. The restriction is intentionally stated over the normalized generation -retained by E2a. It is therefore a decidable fragment boundary around the +retained by certified generation. It is therefore a decidable fragment boundary around the actual generated artifacts, not a second inductive-declaration model. -/ @@ -22,16 +22,16 @@ open Ix.Theory.Named (VConstVal VEnv VExpr VInductDecl) namespace CertifiedSingletonGeneration -/-- The first executable E2b fragment: one nonempty, universe-free, +/-- The executable singleton fragment: one nonempty, universe-free, parameter-free, index-free family whose constructors are nullary and nonrecursive. -/ structure IsEnumeration {source : VInductDecl} (generation : source.GenerationChecked) : Prop where noUniverses : source.uvars = 0 noParameters : source.nparams = 0 - /-- E2b's enumeration pattern currently covers the ordinary large - eliminator. Small and K elimination use the Spec-06 universe layout and - remain explicit E2c breadth cases. -/ + /-- The enumeration pattern covers the ordinary large + eliminator. Small and K elimination use the certified universe layout and + require separate inductive-verification proofs. -/ largeElimination : generation.elimination = .large noIndices : generation.block.rawIndices = [] nonempty : 0 < generation.block.ctorPairs.length @@ -416,7 +416,7 @@ theorem enumerationConstructorAt | _ => simp [KConst.IsCertifiedSingletonConstructor] at hconcrete /-- All finite pattern metadata for an enum rule is forced by the two exact -catalog links and the E2a generation position. No semantic rewrite premise +catalog links and the certified generation position. No semantic rewrite premise is used here. -/ theorem enumerationPatternMetadata {trProj : RawProjRel} {catalog : Catalog} @@ -465,7 +465,7 @@ theorem enumerationPatternMetadata · simpa [enumerationPattern] using hruleFields /-- The compiled enum pattern is semantically justified by the exact -registered generated equation. This is the central E2b bridge: a successful +registered generated equation. This is the central singleton certification bridge: a successful pattern match is reduced through Ix.Theory.Named's registered equality, rather than through an independently postulated iota law. -/ theorem enumerationPatternSound diff --git a/Ix/Kernel/Verify/Inductive/SingletonFamily.lean b/Ix/Kernel/Verify/Inductive/SingletonFamily.lean index 57e960a1a..c69f0a4de 100644 --- a/Ix/Kernel/Verify/Inductive/SingletonFamily.lean +++ b/Ix/Kernel/Verify/Inductive/SingletonFamily.lean @@ -3,7 +3,7 @@ import Ix.Kernel.Verify.Inductive /-! # Certified singleton-family admission -This module is the first production-facing half of E2b. A Ix.Theory.Named +This module links singleton certificates to production declarations. An Ix.Theory.Named `CertifiedGenerationTransaction` already owns the semantic generation of one family, all of its constructors, its recursor, and its iota equations. What that transaction cannot know is which anonymous Ix addresses contain the @@ -22,7 +22,7 @@ link for the physical family block: The structure deliberately contains no `InductiveOracle`, environment-WF, environment-extension, constant-WF, or recursor-rule premise. Those facts -are derived below from the E2a transaction. The later checker adapter must +are derived below from the certified generation transaction. The later checker adapter must construct this link from ingress plus a successful production block run. -/ @@ -134,7 +134,7 @@ end CertifiedSingletonGeneration /-! ## Exact supported production shapes -/ -/-- The concrete family shape supported by the singleton E2b adapter. +/-- The concrete family shape supported by the singleton adapter. The singleton family is member zero of its physical inductive block. Its stored parameter and index counters must agree with the raw/view generation @@ -428,7 +428,7 @@ theorem noRecursorRuleAt exact hshape.noRecursorRuleAt ruleIndex rule /-- Construct the complete family-block oracle from the exact Ix/source link -and E2a's certified transaction. The recursor clauses are vacuous for this +and the certified generation transaction. The recursor clauses are vacuous for this physical block because it contains only the family and its constructors. -/ def oracle {trProj : RawProjRel} {catalog : Catalog} diff --git a/Ix/Kernel/Verify/Inductive/SingletonOracle.lean b/Ix/Kernel/Verify/Inductive/SingletonOracle.lean index b35038ec6..b6aeac4ca 100644 --- a/Ix/Kernel/Verify/Inductive/SingletonOracle.lean +++ b/Ix/Kernel/Verify/Inductive/SingletonOracle.lean @@ -18,7 +18,7 @@ namespace SingletonRecursorCatalogLink /-- Construct a complete `InductiveOracle` for the actual singleton recursor block. Every rule and pattern is selected by its concrete array position and -is justified by the equation installed by the E2a transaction. -/ +is justified by the equation installed by the certified generation transaction. -/ def oracle {trProj : RawProjRel} {catalog : Catalog} {nameOf : Address → Option Lean.Name} {trusted : KId .anon → Prop} diff --git a/Ix/Kernel/Verify/Inductive/SingletonRecursor.lean b/Ix/Kernel/Verify/Inductive/SingletonRecursor.lean index f4e04c4d1..31dff523a 100644 --- a/Ix/Kernel/Verify/Inductive/SingletonRecursor.lean +++ b/Ix/Kernel/Verify/Inductive/SingletonRecursor.lean @@ -3,7 +3,7 @@ import Ix.Kernel.Verify.Inductive.SingletonFamily /-! # Certified singleton-recursor correspondence -The Ix.Theory.Named transaction used by E2a installs a singleton family's recursor +The Ix.Theory.Named transaction used by certified generation installs a singleton family's recursor and all of its iota equations atomically with the family. Anonymous Ix ingress does not: the family/constructor block and the recursor block are distinct physical blocks. This module links the latter block to the exact @@ -70,7 +70,7 @@ end CertifiedSingletonGeneration /-! ## Exact supported recursor shape -/ -/-- Concrete recursor metadata supported by E2b's singleton adapter. +/-- Concrete recursor metadata supported by the singleton adapter. `motives = 1` is the explicit no-mutual/no-nested boundary of this adapter. The exact rule count is retained, and `RecursorMajorIdxCoherent` rules out the @@ -165,7 +165,7 @@ end KConst.IsCertifiedSingletonRecursor /-! ## Exact recursor/rule correspondence -/ /-- Positional correspondence between the one physical Ix recursor block and -the recursor/equations already installed by an E2a transaction. +the recursor/equations already installed by a certified generation transaction. The structure contains representation facts only. Registration, equation WF, recursor WF, and recursor-headedness are derived from the transaction and @@ -271,7 +271,7 @@ theorem ruleAt CertifiedSingletonGeneration.generatedRuleAt _ hnormalized, hfields, hraw, htyped⟩ -/-- E2a registration plus the exact positional Ix link yields the complete +/-- Certified registration plus the exact positional Ix link yields the complete registered-rule semantic relation. -/ theorem registeredRuleAt {trProj : RawProjRel} {catalog : Catalog} diff --git a/Ix/Kernel/Verify/Inductive/StructuralCacheSemantics.lean b/Ix/Kernel/Verify/Inductive/StructuralCacheSemantics.lean index 1a240ec47..d19cf0487 100644 --- a/Ix/Kernel/Verify/Inductive/StructuralCacheSemantics.lean +++ b/Ix/Kernel/Verify/Inductive/StructuralCacheSemantics.lean @@ -3,7 +3,7 @@ import Ix.Kernel.Verify.DefEq /-! # Certified semantics for inductive structural caches -The K1/K2 semantic stack deliberately gives no meaning to the three caches +The reduction, inference, and conversion semantic stack deliberately gives no meaning to the three caches owned only by inductive checking: generated recursors, the major-set to block index, and the peer-agreement marker. Rejecting those entries makes every stable post-inductive state uninhabitable, while accepting them without a @@ -52,7 +52,7 @@ theorem authorizesBlock_of_accepted {world : VerifyWorld} end CacheAuthority /-- Semantic ownership of the three inductive-only cache families. The -fallback retains the complete K1/K2 and block-result meanings. -/ +fallback retains the complete reduction, inference, and conversion and block-result meanings. -/ def StructuralInductiveCacheValid (fallback : CacheSemantics) (authority : CacheAuthority) (support : RunSupport) : CacheEntry → Prop | .recursor block generated => @@ -114,7 +114,7 @@ def structuralInductiveCacheSemantics intro authority support block h exact fallback.blockSuccessSound authority support block h -/-- The production K1/K2 stack with a non-vacuous meaning for every +/-- The production reduction, inference, and conversion stack with a non-vacuous meaning for every inductive structural cache family. -/ def kernelCacheSemanticsWithInductives (keys : WhnfContextKeys) (trProj : RawProjRel) : CacheSemantics := diff --git a/Ix/Kernel/Verify/Infer.lean b/Ix/Kernel/Verify/Infer.lean index b97f376b9..981a60b5f 100644 --- a/Ix/Kernel/Verify/Infer.lean +++ b/Ix/Kernel/Verify/Infer.lean @@ -2,9 +2,9 @@ import Ix.Kernel.Verify.Suffix import Ix.Kernel.Verify.Knot /-! -# K2 inference semantics +# Inference semantics -This module replaces the inference-cache portion of K1's fallback semantics +This module replaces the inference-cache portion of WHNF's fallback semantics with its exact Theory meaning. Algorithmic branch proofs will consume the hit and insertion interfaces defined here. -/ @@ -235,7 +235,7 @@ inductive IsInfer : ExprCacheKind → Prop end ExprCacheKind /-- Exact validity of the two inference cache families. All other entries -retain the semantics already established by the caller (normally K1 WHNF). +retain the semantics already established by the caller (normally WHNF). Persistent entries created under a later-popped local scope are required to carry semantic meaning only when their source is structurally in scope in the represented context. -/ @@ -283,7 +283,7 @@ theorem expr {keys : WhnfContextKeys} {trProj : RawProjRel} end InferCacheValid -/-- Overlay K2's inference meanings on the already-selected cache semantics. -/ +/-- Overlay inference meanings on the already-selected cache semantics. -/ def inferCacheSemantics (keys : WhnfContextKeys) (trProj : RawProjRel) (fallback : CacheSemantics) : CacheSemantics where Valid := InferCacheValid keys trProj fallback diff --git a/Ix/Kernel/Verify/Infer/CacheSoundness.lean b/Ix/Kernel/Verify/Infer/CacheSoundness.lean index 54061b764..0915a3683 100644 --- a/Ix/Kernel/Verify/Infer/CacheSoundness.lean +++ b/Ix/Kernel/Verify/Infer/CacheSoundness.lean @@ -4,7 +4,7 @@ import Ix.Kernel.Verify.Infer.Dispatcher # Inference cache soundness This module closes the production `inferWith` shell around the exhaustive -uncached dispatcher. Cache hits are accepted only through canonical K2 +uncached dispatcher. Cache hits are accepted only through canonical inference/conversion provenance. Cache misses build new provenance from the exact key execution, finite expression collision freedom, suffix transport, and the concrete uncached typing result before mutating either cache partition. @@ -14,7 +14,7 @@ namespace Ix.Kernel namespace TcM -/-- A joint K2 suffix model turns the actual inference-key execution into the +/-- A joint inference/conversion suffix model turns the actual inference-key execution into the same operational match used to validate both hits and writes. -/ theorem inferKey_model_matches_wf {layer : WhnfLayer} {trProj : RawProjRel} {world : VerifyWorld} diff --git a/Ix/Kernel/Verify/Infer/Callbacks.lean b/Ix/Kernel/Verify/Infer/Callbacks.lean index 849a4183c..409466eda 100644 --- a/Ix/Kernel/Verify/Infer/Callbacks.lean +++ b/Ix/Kernel/Verify/Infer/Callbacks.lean @@ -14,7 +14,7 @@ namespace Ix.Kernel namespace DirectWhnf /-- Semantic contract for the direct `RecM.whnf` body at one universe count. -K1's fixed-universe closure constructs this contract when K2 assembles the +WHNF's fixed-universe closure constructs this contract when inference/conversion assembles the joint layer. -/ def WFAt (semantics : CacheSemantics) (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (uvars : Nat) : Prop := diff --git a/Ix/Kernel/Verify/Infer/CheapBeta.lean b/Ix/Kernel/Verify/Infer/CheapBeta.lean index f7feb33a0..0f6dc5d9d 100644 --- a/Ix/Kernel/Verify/Infer/CheapBeta.lean +++ b/Ix/Kernel/Verify/Infer/CheapBeta.lean @@ -209,7 +209,7 @@ theorem cheapBetaPlan?_simul cases hplan /-- Cheap beta reduction preserves the Theory meaning of a structurally -translated source. A successful plan is discharged by K1's constructive +translated source. A successful plan is discharged by WHNF's constructive multi-beta theorem; an absent plan is reflexive. -/ theorem KExpr.cheapBetaReduceResult_meaning {trProj : RawProjRel} {world : VerifyWorld} {uvars : Nat} diff --git a/Ix/Kernel/Verify/Infer/Constants.lean b/Ix/Kernel/Verify/Infer/Constants.lean index d40014ef9..cbebc9303 100644 --- a/Ix/Kernel/Verify/Infer/Constants.lean +++ b/Ix/Kernel/Verify/Infer/Constants.lean @@ -9,7 +9,7 @@ open Ix.Theory (VLevel) This module verifies required constant lookup, universe-arity checking, and type instantiation. The trusted world currently retains only `RawExprRel` for declaration types; inference needs the stronger typed `TrKExprS` -relation. `TrustedConstTypes` names that boundary explicitly so the final K2 +relation. `TrustedConstTypes` names that boundary explicitly so the final inference/conversion closure must derive it from declaration admission rather than silently upgrading raw syntax correspondence. -/ @@ -76,7 +76,7 @@ theorem getConst_loaded_wf {I : TcState .anon → Prop} end TcM /-- Typed declaration-type evidence missing from the current raw trusted -catalog interface. K2 closure must construct this for every trusted +catalog interface. Inference/conversion closure must construct this for every trusted constant that a supported run can infer. -/ def TrustedConstTypes (trProj : RawProjRel) (world : VerifyWorld) : Prop := ∀ {id : KId .anon} {c : KConst .anon}, diff --git a/Ix/Kernel/Verify/Ingress/LiteralBlobs.lean b/Ix/Kernel/Verify/Ingress/LiteralBlobs.lean index 4b266d104..3da42d29c 100644 --- a/Ix/Kernel/Verify/Ingress/LiteralBlobs.lean +++ b/Ix/Kernel/Verify/Ingress/LiteralBlobs.lean @@ -4,7 +4,7 @@ import Ix.Kernel.Verify.Ingress.Representation /-! # Serialized literal/blob ingress -This T0 fixture makes the blob side of the serialized representation +This fixture makes the blob side of the serialized representation contract non-vacuous. A Nat literal and a String literal are stored in one Ixon environment, serialized, decoded with the pure reference decoder, and ingressed through the production anonymous lazy-fault path. Separate @@ -418,7 +418,7 @@ def literalCertificate : LiteralBlobRoundTrip where stringRun := stringRun stringLoaded := stringLoadedNative -/-- Non-vacuous T0 literal/blob round-trip through serialized bytes and the +/-- Non-vacuous literal/blob round-trip through serialized bytes and the actual anonymous ingress implementation. -/ theorem literalRoundTrip : Nonempty LiteralBlobRoundTrip := ⟨literalCertificate⟩ diff --git a/Ix/Kernel/Verify/Ingress/Representation.lean b/Ix/Kernel/Verify/Ingress/Representation.lean index 4711d3c26..b122a56b5 100644 --- a/Ix/Kernel/Verify/Ingress/Representation.lean +++ b/Ix/Kernel/Verify/Ingress/Representation.lean @@ -12,10 +12,9 @@ hashes, blob hashes, the canonical constant Merkle root, sorted table keys, the optional main pointer, and trailing-byte exhaustion. The predicates below retain the resulting facts in the exact form consumed by ingress. -The mmap-backed Rust decoder `Ixon.deEnvAnon` is intentionally not equated to -the pure decoder here. That implementation/refinement result belongs to the -later Rust transport phase; T0 reasons from the pure decoder and the actual -Lean eager/lazy ingress functions. +The mmap-backed Rust decoder `Ixon.deEnvAnon` requires a separate refinement +proof relating it to the pure decoder. This module reasons from the pure +decoder and the actual Lean eager/lazy ingress functions. -/ namespace Ix.Kernel diff --git a/Ix/Kernel/Verify/Ingress/SerializedBoolean.lean b/Ix/Kernel/Verify/Ingress/SerializedBoolean.lean index fbd208d02..7d05c3948 100644 --- a/Ix/Kernel/Verify/Ingress/SerializedBoolean.lean +++ b/Ix/Kernel/Verify/Ingress/SerializedBoolean.lean @@ -5,10 +5,10 @@ import Ix.Kernel.Verify.Ingress.Representation /-! # Serialized Boolean acceptance -This is the first complete T0 vertical slice. It serializes the certified +This fixture serializes the certified Boolean Ixon environment, decodes the resulting bytes with the pure reference decoder, erases anonymous-irrelevant metadata, and reconnects the decoded -source to the existing E3-S semantic world. +source to the semantic world of the supported Boolean fragment. -/ namespace Ix.Kernel @@ -879,7 +879,7 @@ private theorem originalFamilyProjectionLookupNative : some familyProjectionConstant := by native_decide -/-- Successful lookups in the in-memory source used by E3-S have the exact +/-- Successful lookups in the in-memory source used by the Boolean proof have the exact same materialized value after serialization and pure decoding. The finite key classification avoids any appeal to injectivity of content hashes. -/ private theorem decodedGetConst_of_original {addr : Address} @@ -927,7 +927,7 @@ private theorem decodedGetConst_of_original {addr : Address} subst constant exact familyProjectionEntry.getConst -/-- Every dependency used by the E3-S Boolean proof is a reference stored in +/-- Every dependency used by the Boolean proof is a reference stored in the corresponding constant recovered from the decoded byte array. -/ theorem dependencyBound : SerializedDependencyBound env dependencyGraph := by @@ -968,7 +968,7 @@ theorem checkEnvAnon_eq : checkEnvAnon env checkCfg = .ok successfulResults := checkEnvAnonNative -/-! ## Public T0 certificate -/ +/-! ## Public serialized-ingress certificate -/ def certificate : SerializedSubjectCertificate bytes stagedWorld dependencyGraph @@ -987,7 +987,7 @@ def certificate : resultsSucceeded := allResultsSucceeded semantic := semanticSubjectWF -/-- T0-S: the serialized Boolean environment passes pure decoding, integrity +/-- The serialized Boolean environment passes pure decoding, integrity checks, exact eager and cold-lazy ingress, production checking, dependency binding, and the existing semantic acceptance theorem. -/ theorem subjectWF : diff --git a/Ix/Kernel/Verify/Knot.lean b/Ix/Kernel/Verify/Knot.lean index a4fda3c5c..4b9f5c253 100644 --- a/Ix/Kernel/Verify/Knot.lean +++ b/Ix/Kernel/Verify/Knot.lean @@ -13,9 +13,9 @@ finite method table. This file isolates the non-circular proof shape: * `methodsOut_wf` and `methodsN_wf` close every finite approximation; and * `TcM.runRec_wf` transports a reader-level proof to the public knot runner. -The remaining K2 work is therefore deliberately visible in `Methods.Closed`: -K1 supplies the four WHNF fields and K2 supplies inference and definitional -equality. No theorem below assumes the recursive table is already closed. +`Methods.Closed` retains the four WHNF fields and the inference and +definitional-equality fields as explicit obligations. No theorem below +assumes the recursive table is already closed. -/ namespace Ix.Kernel @@ -39,7 +39,7 @@ def LayerWFAt (layer : WhnfLayer) (semantics : CacheSemantics) (uvars : Nat) (methods : Methods .anon) : Prop := Methods.WFAt layer semantics trProj world support uvars (next methods) -/-- K1's four fields for one unfolded method-table layer at a fixed universe +/-- WHNF's four fields for one unfolded method-table layer at a fixed universe count. This is the closure shape used by universe-indexed WHNF and unfold cache semantics. -/ structure WhnfLayerWFAt (layer : WhnfLayer) (semantics : CacheSemantics) @@ -78,7 +78,7 @@ structure WhnfLayerWFAt (layer : WhnfLayer) (semantics : CacheSemantics) (fun result _ => support result ∧ WhnfPost trProj world uvars Delta sourceV result) -/-- K2's two fields for one unfolded method-table layer at a fixed universe +/-- The two inference/conversion fields for one unfolded method-table layer at a fixed universe count. -/ structure InferDefEqLayerWFAt (layer : WhnfLayer) (semantics : CacheSemantics) (trProj : RawProjRel) @@ -103,7 +103,7 @@ structure InferDefEqLayerWFAt (layer : WhnfLayer) (fun answer _ => answer = true → world.venv.IsDefEqU uvars Delta.toCtx va vb) -/-- The fixed-universe K1 and K2 records assemble the exact next layer. -/ +/-- The fixed-universe WHNF and inference/conversion records assemble the exact next layer. -/ theorem LayerWFAt.of_parts {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} @@ -129,8 +129,8 @@ def ClosedAt (layer : WhnfLayer) (semantics : CacheSemantics) Methods.WFAt layer semantics trProj world support uvars methods → LayerWFAt layer semantics trProj world support uvars methods -/-- K1's fixed-universe closure obligation, independent of construction of -the two K2 fields. -/ +/-- WHNF's fixed-universe closure obligation, independent of construction of +the two inference/conversion fields. -/ def WhnfClosedAt (layer : WhnfLayer) (semantics : CacheSemantics) (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (uvars : Nat) : Prop := @@ -138,7 +138,7 @@ def WhnfClosedAt (layer : WhnfLayer) (semantics : CacheSemantics) Methods.WFAt layer semantics trProj world support uvars methods → WhnfLayerWFAt layer semantics trProj world support uvars methods -/-- K2's fixed-universe closure obligation. -/ +/-- Fixed-universe inference/conversion closure obligation. -/ def InferDefEqClosedAt (layer : WhnfLayer) (semantics : CacheSemantics) (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (uvars : Nat) : Prop := @@ -165,7 +165,7 @@ def LayerWF (layer : WhnfLayer) (semantics : CacheSemantics) (methods : Methods .anon) : Prop := Methods.WF layer semantics trProj world support (next methods) -/-- K1's four fields for one unfolded method-table layer. -/ +/-- WHNF's four fields for one unfolded method-table layer. -/ structure WhnfLayerWF (layer : WhnfLayer) (semantics : CacheSemantics) (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (methods : Methods .anon) : Prop where @@ -198,7 +198,7 @@ structure WhnfLayerWF (layer : WhnfLayer) (semantics : CacheSemantics) (fun result _ => support result ∧ WhnfPost trProj world uvars Delta sourceV result) -/-- K2's two fields for one unfolded method-table layer. -/ +/-- The two inference/conversion fields for one unfolded method-table layer. -/ structure InferDefEqLayerWF (layer : WhnfLayer) (semantics : CacheSemantics) (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) @@ -220,7 +220,7 @@ structure InferDefEqLayerWF (layer : WhnfLayer) (fun answer _ => answer = true → world.venv.IsDefEqU uvars Delta.toCtx va vb) -/-- The independently proved K1 and K2 fields assemble the exact next-layer +/-- The independently proved WHNF and inference/conversion fields assemble the exact next-layer record; no field may use the table it is currently proving. -/ theorem LayerWF.of_parts {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} @@ -242,13 +242,13 @@ def Closed (layer : WhnfLayer) (semantics : CacheSemantics) ∀ methods, Methods.WF layer semantics trProj world support methods → LayerWF layer semantics trProj world support methods -/-- K1 closure obligation, separate from inference and def-eq. -/ +/-- WHNF closure obligation, separate from inference and def-eq. -/ def WhnfClosed (layer : WhnfLayer) (semantics : CacheSemantics) (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) : Prop := ∀ methods, Methods.WF layer semantics trProj world support methods → WhnfLayerWF layer semantics trProj world support methods -/-- K2 closure obligation, assuming only the smaller table's six contracts. -/ +/-- Inference/conversion closure obligation, assuming only the smaller table's six contracts. -/ def InferDefEqClosed (layer : WhnfLayer) (semantics : CacheSemantics) (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) : Prop := ∀ methods, Methods.WF layer semantics trProj world support methods → diff --git a/Ix/Kernel/Verify/NatFixture.lean b/Ix/Kernel/Verify/NatFixture.lean index ce4d55760..f904fef0a 100644 --- a/Ix/Kernel/Verify/NatFixture.lean +++ b/Ix/Kernel/Verify/NatFixture.lean @@ -5,7 +5,7 @@ import Ix.Kernel.Verify.Whnf.Structural.BetaBoundary open Ix.Theory (VLevel) /-! -# G2a ambient-Nat fixture +# Ambient Nat fixture This file instantiates `InductiveOracle` with a small, closed Theory model of `Nat`, `Nat.zero`, and `Nat.succ`. The concrete catalog entries retain their @@ -18,15 +18,12 @@ it does not claim an eliminator or pretend that Ix.Theory.Named's still-opaque The fixture then promotes one ordinary axiom whose type is `Nat` and leaves a second, raw-translatable but ill-typed axiom pending. Thus adding an ambient -inductive family does not collapse the pending/trusted boundary established -in G1. G3a first instantiated finite run support for lift, substitution, and -universe instantiation. As in the G1 -adversarial fixture, fixed distinct addresses keep this logical model -independent of the Blake3 FFI; it establishes semantic -inhabitation, not ingress hash-integrity or Rust parity. G3b extends that -witness to direct expression/universe interning, every currently formalized -walker family, and a non-empty `ExecutionRequests` certificate for the exact -same request list. +inductive family preserves the pending/trusted boundary. Fixed distinct +addresses keep this logical model independent of the Blake3 FFI; it establishes +semantic inhabitation, not ingress hash-integrity or Rust parity. Finite +execution support covers lifting, substitution, universe instantiation, direct +expression/universe interning, every formalized walker family, and a non-empty +`ExecutionRequests` certificate for the same request list. -/ namespace Ix.Kernel @@ -366,7 +363,7 @@ theorem succRaw : RawInductiveConstRel natEnv nameOf RawProjRel.none · exact RawExprRel.const nameOf_nat natEnv_nat rfl · exact RawExprRel.const nameOf_nat natEnv_nat rfl -/-- A real model of the G2a assumption boundary. This particular block has +/-- A real model of the ambient-inductive interface assumption boundary. This particular block has no recursor declaration, so `recursorFacts` and `recursorPatterns` are vacuous; any later block that contains a `.recr` entry must supply both its Theory equation and exact iota-pattern witnesses explicitly. -/ @@ -652,7 +649,7 @@ theorem stateWF (prims : Primitives .anon) : TcStateWF RawProjRel.none (state prims) worldGood := ⟨trustedCatalogRelGood, loadedAgrees, InternTable.WF.empty⟩ -/-- The G2b consumer lookup is inhabited by an ambient inductive member in a +/-- Catalog lookup is inhabited by an ambient inductive member in a real concrete state; no legacy whole-environment translation is involved. -/ theorem natResolved (prims : Primitives .anon) : ∃ name ci, @@ -688,7 +685,7 @@ theorem natResolvedInv (prims : Primitives .anon) : TrustedConstRel RawProjRel.none world natId natConcrete name ci := (stateWF prims).tcInv.resolve loadedEnv_nat nat_trusted_good -/-! ## G3 finite run-support and execution witness -/ +/-! ## Finite run support and execution witness -/ /-- A constructed constant reference to the ambient Nat family. Recording the smart constructor's own info makes it both a concrete Nat reference and a @@ -816,7 +813,7 @@ theorem checkSupport (prims : Primitives .anon) : · exact fun _ hu => False.elim hu /-- Source traversal and generated-result arithmetic are simultaneously -bounded for the concrete G3b request list. -/ +bounded for the concrete execution request list. -/ theorem resourceBounds : ResourceBounds supportRequests := by constructor intro request hmem @@ -852,7 +849,7 @@ theorem resourceBounds : ResourceBounds supportRequests := by /-- A small real `TcM` computation containing exactly the eight recorded interning operations. It is a proof fixture, not a claim about `checkConst`; -later K1--K3 proofs build the same certificate compositionally for the +reducer, inference, and declaration proofs build the certificate compositionally for the production entry points. -/ def supportProgram : TcM .anon Unit := do let _ ← TcM.intern supportExpr @@ -884,7 +881,7 @@ theorem runAssumptions (prims : Primitives .anon) : RunSupport.pair_collisionFree supportExpr zeroLevel, checkSupport prims, resourceBounds⟩ -/-- G3b is non-vacuous in the same state that contains a trusted ambient Nat +/-- Execution-indexed support is non-vacuous in a state containing a trusted ambient Nat family and a loaded ill-typed pending declaration. Its execution list cannot be replaced by `[]`: it is indexed by the concrete eight-operation program. -/ theorem supportAcceptance (prims : Primitives .anon) : @@ -897,14 +894,14 @@ theorem supportAcceptance (prims : Primitives .anon) : ⟨stateWF prims, nat_trusted_good, badPending, runAssumptions prims⟩ -/-! ## K1 exact nonempty warm-cache witness -/ +/-! ## WHNF exact nonempty warm-cache witness -/ /-- This fixture contains only closed source expressions, so its context-key model relates the distinguished empty key to the empty semantic context. -/ def whnfContextKeys : WhnfContextKeys := WhnfContextKeys.closed 0 -/-- Exact K1 semantics for all five WHNF cache families. Non-WHNF semantic +/-- Exact WHNF semantics for all five WHNF cache families. Non-WHNF semantic caches are absent from the fixture; cached block errors remain replayable. -/ def whnfSemantics : CacheSemantics := whnfCacheSemantics whnfContextKeys RawProjRel.none @@ -912,7 +909,7 @@ def whnfSemantics : CacheSemantics := /-- The closed Nat reference is definitionally equal to itself in the real ambient-Nat Theory world. This is the semantic fact stored by the warm -cache, replacing G4's former address-only identity contract. -/ +cache, including its Theory meaning as well as its address. -/ theorem supportExpr_whnfMeaning : WhnfMeaning RawProjRel.none worldNat 0 [] supportExpr supportExpr := by obtain ⟨name, ci, hresolved⟩ := @@ -948,7 +945,7 @@ def warmState (prims : Primitives .anon) : TcState .anon := { env := warmEnv, prims, ctxId := natAddress } /-- The loaded ambient-Nat environment has constants but no semantic cache -entries. This is the fresh side of the G4 fresh/warm comparison. -/ +entries. This is the fresh state in the provenance comparison. -/ theorem loadedEnv_noCacheEntries (entry : CacheEntry) : ¬loadedEnv.HasCacheEntry entry := by intro hentry @@ -1041,7 +1038,7 @@ theorem whnfLeafTheoryWF : VExpr.WF worldGood.venv 0 [] (.sort .zero) := ⟨_, VEnv.HasType.sort trivial⟩ -/-- The concrete ambient-Nat state inhabits the syntax-directed K1 fixture +/-- The concrete ambient-Nat state inhabits the syntax-directed WHNF fixture layer with acceleration disabled. Its primitive table remains intentionally parametric; production closure uses `productionNoAccelStateInv` below. -/ theorem noAccelStateInv (prims : Primitives .anon) : @@ -1096,7 +1093,7 @@ theorem noAccelInvariant_rejects_mismatched_primitives /-- A real Nat-containing state instantiates the first conditional `RecM.whnf` theorem. This branch returns before any cache, fuel, native, or -recursive-method operation, but still preserves the complete K1 invariant on +recursive-method operation, but still preserves the complete WHNF invariant on both EStateM outcomes. -/ theorem whnfLeaf_noAccel_wf (prims : Primitives .anon) : RecM.WF .structuralNoAccel whnfSemantics RawProjRel.none worldGood support 0 [] @@ -1189,7 +1186,7 @@ theorem warmCache_cannotResolvePending (prims : Primitives .anon) : ¬warmEntry.References support IllTypedPending.targetId := (warmKernelStateWF prims).pendingCacheIsolation badPending warmEnv_hit -/-- G4's formal acceptance witness contains both the fresh and nonempty warm +/-- The cache-provenance acceptance witness contains both the fresh and nonempty warm states, transported provenance, and pending-declaration isolation. The executable failed-then-valid regression lives in `Tests.Ix.Kernel.CheckTests`. -/ theorem cacheAcceptance (prims : Primitives .anon) : @@ -1209,7 +1206,7 @@ theorem zero_trusted_good : worldGood.trusted zeroId := theorem succ_trusted_good : worldGood.trusted succId := TrustInsert.old succ_trusted -/-! ### K1 structural beta witness -/ +/-! ### WHNF structural beta witness -/ /-- A closed, typed beta redex over the ambient Nat family. Smart constructors supply its actual content metadata; the proof below does not @@ -1287,7 +1284,7 @@ theorem whnfCoreConst_noAccel_acceptance (prims : Primitives .anon) (.const natName []) result) := ⟨noAccelStateInv prims, whnfCoreConst_noAccel_wf prims flags⟩ -/-- Nontrivial K1 semantic witness: the concrete Nat identity application +/-- Nontrivial WHNF semantic witness: the concrete Nat identity application is definitionally equal to the exact output of the verified substitution specification. -/ theorem betaIdentityMeaning : @@ -1402,7 +1399,7 @@ theorem betaResultMeaning : /-- Small operational harness for the single recursive-head callback used by this fixture. It is not claimed to satisfy `Methods.WF` or to be the tied production knot; the generic theorem above isolates the exact callback -equation that K2 must later prove for `methodsN`. -/ +equation required to use `methodsN` in its place. -/ def betaHarnessMethods : Methods .anon where whnf := fun e => pure e whnfCore := fun e => pure e @@ -6452,7 +6449,7 @@ theorem multiBetaMiddleRebase : hlaterTr.rebase worldGood.venvWF (by trivial) hthroughTr hthroughEq exact ⟨priorArgs, laterArgs, resultV, hlater, hresultTr, hresultEq⟩ -/-- G2a acceptance witness: one concrete state simultaneously contains a +/-- Ambient-inductive acceptance witness: one concrete state simultaneously contains a trusted, well-formed ambient Nat family; a successfully promoted standalone declaration that uses Nat; and an independently loaded pending declaration for which declaration WF is impossible. -/ diff --git a/Ix/Kernel/Verify/RecursiveMethods/Closure.lean b/Ix/Kernel/Verify/RecursiveMethods/Closure.lean index 495ea8a56..124ac3614 100644 --- a/Ix/Kernel/Verify/RecursiveMethods/Closure.lean +++ b/Ix/Kernel/Verify/RecursiveMethods/Closure.lean @@ -12,7 +12,7 @@ This module performs that final fixed-universe assembly. namespace Ix.Kernel -/-- The semantic cache layers beneath K1's outer WHNF and delta layers. -/ +/-- The semantic cache layers beneath the outer WHNF and delta layers. -/ def kernelCacheFallback (keys : WhnfContextKeys) (trProj : RawProjRel) : CacheSemantics := inferCacheSemantics keys trProj <| diff --git a/Ix/Kernel/Verify/RecursiveMethods/ScopedCallDomains.lean b/Ix/Kernel/Verify/RecursiveMethods/ScopedCallDomains.lean index 1de663397..6306dd3e5 100644 --- a/Ix/Kernel/Verify/RecursiveMethods/ScopedCallDomains.lean +++ b/Ix/Kernel/Verify/RecursiveMethods/ScopedCallDomains.lean @@ -2,10 +2,10 @@ import Ix.Kernel.Verify.DefEq import Ix.Kernel.Verify.RecursiveMethods.CallDomains /-! -# Run-scoped recursive-method call domains +# Run-scoped method call domains -The original bounded call-domain contract carries the kernel invariant but -not the finite context-digest state domain. K2S must retain both: a method +The bounded call-domain contract carries the kernel invariant but +not the finite context-digest state domain. Scoped method contracts retain both: a method may construct or reuse a suffix key only while its concrete pre-state belongs to `ScopedKernelSuffixModel.StateInScope`, and both success and partial-error states must remain in that domain. diff --git a/Ix/Kernel/Verify/RecursiveMethods/ScopedSortInference.lean b/Ix/Kernel/Verify/RecursiveMethods/ScopedSortInference.lean index f099c235f..1b4ab55a3 100644 --- a/Ix/Kernel/Verify/RecursiveMethods/ScopedSortInference.lean +++ b/Ix/Kernel/Verify/RecursiveMethods/ScopedSortInference.lean @@ -203,7 +203,7 @@ theorem sort_scoped_wf_fuel_one exact sort_scoped_wf_bounded hcollision hsourceSupport hresultSupport theory references hsource -/-- The K2S construction can be consumed at the public positive-fuel entry +/-- The scoped method construction can be consumed at the public positive-fuel entry without first manufacturing a universally quantified suffix model. -/ theorem sort_finiteOperational_wf_fuel_one {initial : TcState .anon} diff --git a/Ix/Kernel/Verify/Run.lean b/Ix/Kernel/Verify/Run.lean index 0c53a2472..019f4cbce 100644 --- a/Ix/Kernel/Verify/Run.lean +++ b/Ix/Kernel/Verify/Run.lean @@ -3,7 +3,7 @@ import Ix.Kernel.Verify.Execution import Ix.Kernel.Verify.Frame /-! -# G3b: execution-indexed run assumptions +# Execution-indexed run assumptions An explicit request list is not, by itself, evidence that it describes a checker run: choosing `[]` would make coverage and bounds vacuous. This file @@ -21,8 +21,8 @@ existing walker masters and retain both finite intern ranges in post-states. namespace Ix.Kernel -/-- State invariant used by run-level adapters and top-level statements. G4 -adds stable-world provenance for every warm cache entry. -/ +/-- State invariant used by run-level adapters and top-level statements. +It includes stable-world provenance for every warm cache entry. -/ def SupportedState (semantics : CacheSemantics) (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (s : TcState .anon) : Prop := diff --git a/Ix/Kernel/Verify/State.lean b/Ix/Kernel/Verify/State.lean index d40d7b0eb..0d0bd0d89 100644 --- a/Ix/Kernel/Verify/State.lean +++ b/Ix/Kernel/Verify/State.lean @@ -26,10 +26,10 @@ monotone extension proof from the caller's baseline. Fixed-world Hoare triples are proved first below; their error branches retain exactly the same world, so ordinary state mutation cannot silently promote a declaration. -Ambient-inductive admission is carried by the trusted log beginning in G2a. -G4 layers finite-support cache provenance onto this deliberately small core -through `KernelStateWF` below. Dual-context agreement and the concrete -reduction/inference/native semantic contracts remain K1/K2 obligations. +The trusted log carries ambient-inductive admission. `KernelStateWF` adds +finite-support cache provenance to this core. Dual-context agreement and +the concrete reduction, inference, and native semantic contracts remain +obligations of the operation-specific proofs. -/ namespace Ix.Kernel @@ -103,7 +103,7 @@ def TcInv (trProj : RawProjRel) (world₀ : VerifyWorld) (s : TcState .anon) : Prop := ∃ world, world₀ ≤ world ∧ TcStateWF trProj s world -/-! ## G4 semantic-cache layer -/ +/-! ## Semantic-cache layer -/ /-- The complete stable checker invariant at a finite run boundary. @@ -122,7 +122,7 @@ structure KernelStateWF (semantics : CacheSemantics) (trProj : RawProjRel) equivalences : EquivManager.WF (semantics.Equiv (CacheAuthority.stable world) support) s.equivManager -/-- Existential current-world form of the complete G4 state invariant. -/ +/-- Existential current-world form of the complete cache provenance state invariant. -/ def KernelTcInv (semantics : CacheSemantics) (trProj : RawProjRel) (world₀ : VerifyWorld) (support : RunSupport) (s : TcState .anon) : Prop := @@ -301,7 +301,7 @@ theorem stateWF (prims : Primitives .anon) : exact loaded_pending_but_not_wf.1 · exact InternTable.WF.empty -/-- G1's central adversarial witness: the complete state invariant and +/-- An adversarial world: the complete state invariant and pending precondition are inhabited even though declaration WF is impossible. This rules out a hidden whole-`KEnv` typing premise in `TcInv`. -/ theorem tcInv_pending_but_not_wf (prims : Primitives .anon) : diff --git a/Ix/Kernel/Verify/Statements.lean b/Ix/Kernel/Verify/Statements.lean index b37e30cce..8b86280b3 100644 --- a/Ix/Kernel/Verify/Statements.lean +++ b/Ix/Kernel/Verify/Statements.lean @@ -7,33 +7,33 @@ import Ix.Kernel.Verify.RecursiveMethods.Public /-! # Public checker theorem frontier -All seven public theorem roots now use the concrete verification relations and +All seven public theorem roots use concrete verification relations and a finite, fuel-indexed production call schedule: -* `TcM.whnf.wf`, `TcM.infer.wf`, and `TcM.isDefEq.wf` are the C1A adapters - from `RecursiveMethods/Public.lean`; -* `TcM.checkConst.wf` is K3's standalone axiom/definition-family theorem, +* `TcM.whnf.wf`, `TcM.infer.wf`, and `TcM.isDefEq.wf` are bounded + recursive-method adapters from `RecursiveMethods/Public.lean`; +* `TcM.checkConst.wf` is the standalone axiom/definition-family theorem, starting from `PendingDecl` and untyped validator ingress and producing a real `StandaloneCheckResult`, a `VDecl.WF`-backed trusted-world promotion, and the promoted post-state invariant; and -* `TcM.checkConst.blockDisposition` is E0's exhaustive successful-dispatch +* `TcM.checkConst.blockDisposition` is the exhaustive successful-dispatch theorem: the production call either performs one exact atomic coordinated admission or takes the separately verified standalone branch; and -* `BooleanEnumerationFixture.subjectWF` is the E3-S acceptance root: the +* `BooleanEnumerationFixture.subjectWF` is the supported fragment acceptance root: the production serial driver successfully checks the exact six-entry Boolean source environment, and its two coordinated work rows satisfy `SubjectWF` - through transparent run-scoped K3/E0 resources, an explicit empty - assumption set, and certificate-backed E2 inductive evidence. -* `BooleanSerialized.subjectWF` is the T0-S representation root: the same + through run-scoped declaration and block-checking resources, an explicit empty + assumption set, and certificate-backed inductive evidence. +* `BooleanSerialized.subjectWF` is the serialized-ingress representation root: the same semantic result is connected to a successful pure Ixon byte decode, exact hash-verified eager and cold-lazy ingress, serialized dependency refs, and a successful run of the production anonymous driver. -The K3 statement deliberately exposes `StandaloneRoute`. E0 now closes the +The standalone theorem exposes `StandaloneRoute`. Block admission covers the coordinated transaction, physical/ghost identity, and cache-publication layers. Singleton definition blocks are constructive. Inductive and recursor bodies remain relative in the generic adapter to an explicitly -supplied `InductiveOracle` resource; the public E3-S root instantiates both +supplied `InductiveOracle` resource; the public supported fragment root instantiates both resources from the Ix.Theory.Named Boolean generation certificate. Quotient semantics, mutual/nested inductives, indexed or parameterized families, and multi-definition blocks remain outside this certificate-backed release diff --git a/Ix/Kernel/Verify/Suffix.lean b/Ix/Kernel/Verify/Suffix.lean index 3dc7f5df8..195d47e8c 100644 --- a/Ix/Kernel/Verify/Suffix.lean +++ b/Ix/Kernel/Verify/Suffix.lean @@ -1,7 +1,7 @@ import Ix.Kernel.Verify.Whnf /-! -# K2 suffix-context transport boundary +# Suffix-context transport boundary WHNF cache keys hash only the de-Bruijn suffix reachable from an expression. The hash itself is not semantic evidence. This module states the two facts a @@ -505,8 +505,8 @@ namespace WhnfSuffixModel /-- Construct the operational model once the actual semantic sufficiency theorem for equal emitted suffix addresses is available. This removes the -former representation oracle entirely: only semantic transport remains K2 -proof debt. -/ +former representation oracle entirely: semantic transport remains an explicit +proof obligation. -/ def operational {trProj : RawProjRel} {world : VerifyWorld} (uvars : Nat) (htransport : ∀ {ctxAddr : Address} {Delta Delta' : KVLCtx} {source result : KExpr .anon}, diff --git a/Ix/Kernel/Verify/Support.lean b/Ix/Kernel/Verify/Support.lean index 4fef22776..9a85439e4 100644 --- a/Ix/Kernel/Verify/Support.lean +++ b/Ix/Kernel/Verify/Support.lean @@ -2,7 +2,7 @@ import Ix.Kernel.Verify.InstUniv import Std.Data.HashMap.Lemmas /-! -# G3: finite run-scoped collision and arithmetic support +# Finite run-scoped collision and arithmetic support The expression walkers were already proved against an abstract predicate `S`, with separate hypotheses that their reach relation and the initial diff --git a/Ix/Kernel/Verify/Totalization.lean b/Ix/Kernel/Verify/Totalization.lean index e9eeb84e3..1efd15ff4 100644 --- a/Ix/Kernel/Verify/Totalization.lean +++ b/Ix/Kernel/Verify/Totalization.lean @@ -3,7 +3,7 @@ import Ix.Kernel.CanonicalCheck import Ix.Kernel.Check /-! -# K0: equations for the total recursive-methods knot +# Totalization: equations for the total recursive-methods knot These equations expose the production definitions needed by the later `Methods.WF` induction. They also pin the runtime boundary precisely: diff --git a/Ix/Kernel/Verify/Whnf.lean b/Ix/Kernel/Verify/Whnf.lean index a3113ecbe..bc3334848 100644 --- a/Ix/Kernel/Verify/Whnf.lean +++ b/Ix/Kernel/Verify/Whnf.lean @@ -8,11 +8,12 @@ open Ix.Theory (VLevel) /-! # WHNF soundness boundary -This file starts K1 at the semantic boundary shared by reduction, caches, and +This file defines the semantic boundary shared by reduction, caches, and the recursive method knot. It intentionally does not identify a context hash with a typing context by fiat. `WhnfContextKeys.Represents` is the one named ghost relation whose production implementation must be connected to -`TcM.ctxAddrForLbr`; K2 supplies the suffix-sufficiency transport theorem. +`TcM.ctxAddrForLbr`. The suffix model in `Verify.DefEq` supplies the semantic +transport contract for equal suffix keys. The semantic payload is already concrete: `WhnfMeaning` says that source and result have structural `TrKExprS` translations in the same `KVLCtx`, and that @@ -21,9 +22,9 @@ Consequently a cache hit is useful only after supplying both its finite source witness and the represented context. Address equality alone carries no semantic meaning. -K1 has five expression-cache policies. The inference caches are excluded +WHNF has five expression-cache policies. The inference caches are excluded from this layer and continue through a caller-supplied fallback semantics; -K2 replaces that fallback with their exact typing contracts. +`Verify.Infer` supplies their exact typing contracts. -/ namespace Ix.Kernel @@ -82,7 +83,7 @@ end WhnfMeaning /-! ## Cache-policy partition -/ /-- The five semantic policies implemented by the WHNF expression caches. -The policy records operational strength; every policy has the same C1 +The policy records operational strength; every policy has the same cache soundness consequence (`WhnfMeaning`). -/ inductive WhnfCachePolicy where | full @@ -94,7 +95,8 @@ inductive WhnfCachePolicy where namespace ExprCacheKind -/-- Classify exactly the K1 cache families. Inference caches are K2. -/ +/-- Classify exactly the WHNF cache families. Inference caches belong to the +separate inference layer. -/ def whnfPolicy? : ExprCacheKind → Option WhnfCachePolicy | .whnf => some .full | .whnfNoDelta => some .noDelta @@ -124,7 +126,7 @@ def whnfPolicy? : ExprCacheKind → Option WhnfCachePolicy @[simp] theorem whnfPolicy?_inferOnly : ExprCacheKind.inferOnly.whnfPolicy? = none := rfl -/-- Proof-relevant membership in the K1 cache partition. -/ +/-- Proof-relevant membership in the WHNF cache partition. -/ inductive IsWhnf : ExprCacheKind → Prop | whnf : IsWhnf .whnf | whnfNoDelta : IsWhnf .whnfNoDelta @@ -169,9 +171,10 @@ end ExprCacheKind /-! ## Context-key interpretation and exact cache semantics -/ /-- Ghost interpretation of suffix-aware context addresses. `Represents` -may relate one key to several definitionally equal contexts; K1 cache -validity is deliberately quantified over every represented context. K2 -constructs this model from `ctxAddrForLbr` plus suffix sufficiency. -/ +may relate one key to several definitionally equal contexts; WHNF cache +validity is deliberately quantified over every represented context. The +suffix-model adapter constructs this interpretation from `ctxAddrForLbr` +plus suffix sufficiency. -/ structure WhnfContextKeys where uvars : Nat /-- `Represents lbr key Δ` interprets `key` as the suffix requested at @@ -194,7 +197,7 @@ def closed (uvars : Nat) : WhnfContextKeys where /-- A represented semantic context tied to the actual production cache-key computation in a concrete state. Constructing this witness—not merely -postulating `Represents`—is the K1/K2 context-key proof obligation. -/ +postulating `Represents`—is the reduction, inference, and conversion context-key proof obligation. -/ def Matches (keys : WhnfContextKeys) (trProj : RawProjRel) (world : VerifyWorld) (s : TcState .anon) (Δ : KVLCtx) (source : KExpr .anon) (key : Address × Address) : Prop := @@ -216,7 +219,7 @@ def InternUpdateFrame (before after : TcState .anon) : Prop := after = { before with env := { before.env with intern := after.env.intern } } /-- The empty projection relation satisfies every structural closure law -vacuously. This is the canonical K1 fixture interpretation for fragments +vacuously. This is the canonical WHNF fixture interpretation for fragments that contain no projection nodes. -/ theorem RawProjRel.none_ok (env : Ix.Theory.Named.VEnv) (uvars : Nat) : TrProjOK env uvars RawProjRel.none := by @@ -339,8 +342,8 @@ def blockResults : CacheSemantics where intro authority support block h exact h -/-- Compatibility spelling retained for existing K1/K2 clients. Unlike its -pre-E0 definition, successful block verdicts now have the exact sound meaning +/-- Compatibility spelling for block-result cache semantics. +Successful block verdicts have the exact sound meaning specified by `blockResults`. -/ abbrev blockErrorsOnly : CacheSemantics := blockResults @@ -405,7 +408,7 @@ def isRecCacheSemantics (fallback : CacheSemantics) : CacheSemantics where intro authority support block h exact fallback.blockSuccessSound authority support block h -/-- Exact K1 validity for one tagged entry. The fallback owns every non-K1 +/-- Exact WHNF validity for one tagged entry. The fallback owns every non-WHNF cache family. A WHNF entry must be sound for every finite-support source whose address is its first key component and every context represented by its second component whenever that source is structurally in scope there. @@ -465,7 +468,7 @@ theorem mono {keys : WhnfContextKeys} {trProj : RawProjRel} recursor | recMajors | blockPeer | blockResult => exact fallback.mono hle h -/-- Project the concrete reduction meaning from any of the five K1 cache +/-- Project the concrete reduction meaning from any of the five WHNF cache families. -/ theorem expr {keys : WhnfContextKeys} {trProj : RawProjRel} {fallback : CacheSemantics} {authority : CacheAuthority} @@ -492,7 +495,7 @@ theorem natSuccStuck {keys : WhnfContextKeys} {trProj : RawProjRel} end WhnfCacheValid -/-- Overlay the exact K1 meanings on an existing semantic family. -/ +/-- Overlay the exact WHNF meanings on an existing semantic family. -/ def whnfCacheSemantics (keys : WhnfContextKeys) (trProj : RawProjRel) (fallback : CacheSemantics) : CacheSemantics where Valid := WhnfCacheValid keys trProj fallback @@ -512,7 +515,7 @@ def whnfCacheSemantics (keys : WhnfContextKeys) (trProj : RawProjRel) namespace CacheProvenance -/-- Construct K1 provenance for a negative successor marker. Unlike a +/-- Construct WHNF provenance for a negative successor marker. Unlike a cached expression result, the marker needs no Theory reduction witness; it still records a supported source address and proves that every supported source sharing that address refers only to trusted declarations. -/ @@ -532,7 +535,7 @@ theorem whnfNatSuccStuck intro id href exact .inl (hreferences href) -/-- A provenance-certified K1 hit exposes concrete Theory reduction +/-- A provenance-certified WHNF hit exposes concrete Theory reduction meaning; support and dependency facts remain available in `h`. -/ theorem whnfMeaning {keys : WhnfContextKeys} {trProj : RawProjRel} {fallback : CacheSemantics} {authority : CacheAuthority} @@ -566,7 +569,7 @@ end CacheProvenance namespace CacheInvariant -/-- Physical hit plus the exact K1 cache invariant yields its Theory +/-- Physical hit plus the exact WHNF cache invariant yields its Theory meaning. -/ theorem whnfHit {keys : WhnfContextKeys} {trProj : RawProjRel} {fallback : CacheSemantics} {authority : CacheAuthority} @@ -601,7 +604,7 @@ end CacheInvariant /-! ## Conditional recursive-method interface -/ -/-- The theorem layers used by K1. `structuralNoAccel` is deliberately +/-- The theorem layers used by WHNF. `structuralNoAccel` is deliberately restricted to syntax-directed fixtures: it pins the acceleration gate but does not claim that the state's primitive table is the production anon table. The two production layers both bind every observable table address to @@ -856,7 +859,7 @@ end WhnfStateInv namespace ContextKeyFrame -/-- Populating the suffix-key memo preserves the complete K1 invariant. +/-- Populating the suffix-key memo preserves the complete WHNF invariant. The proof projects the exact frame rather than treating the memo operation as pure; this catches future writes to context, environment, fuel, or flags. -/ theorem whnfStateInv {layer : WhnfLayer} {semantics : CacheSemantics} @@ -913,7 +916,7 @@ theorem trans {s₀ s₁ s₂ : TcState .anon} rw [h₂, h₁] /-- Intern-table growth preserves the context and acceleration components of -the K1 invariant once the post-state kernel invariant has been re-established. +the WHNF invariant once the post-state kernel invariant has been re-established. Keeping the kernel premise explicit lets the finite-support walker proofs supply its new intern-table coherence and coverage facts. -/ theorem whnfStateInv {layer : WhnfLayer} {semantics : CacheSemantics} @@ -953,7 +956,7 @@ end InternUpdateFrame namespace TcM -/-- Lift an exact `InternM` specification to the complete K1 state invariant. +/-- Lift an exact `InternM` specification to the complete WHNF state invariant. This is the common state bridge for beta/zeta walkers: the intern table may grow, but contexts, flags, loaded declarations, and semantic caches frame. -/ theorem runIntern_whnf_wf {layer : WhnfLayer} @@ -1143,7 +1146,7 @@ theorem ctxAddrForLbr_wf {I : TcState .anon → Prop} · intro _ after hafter exact TcM.WF.pure fun _ => hafter -/-- `whnfKey` preserves K1 state and fixes the expression-address component +/-- `whnfKey` preserves WHNF state and fixes the expression-address component of the returned key. -/ theorem whnfKey_wf {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} @@ -1160,8 +1163,8 @@ theorem whnfKey_wf {layer : WhnfLayer} {semantics : CacheSemantics} exact TcM.WF.pure fun _ => ⟨rfl, hframe⟩ /-- Operational context-match constructor. `hrep` is deliberately the only -remaining ghost obligation: K1 cannot infer suffix sufficiency from a hash, -and K2 will discharge it from the concrete context-closure algorithm. -/ +remaining ghost obligation: suffix sufficiency must follow from the concrete +context-closure algorithm and its semantic transport contract. -/ theorem whnfKey_matches_wf {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {keys : WhnfContextKeys} @@ -1289,7 +1292,7 @@ end TcM namespace RunAssumptions /-- One certified expression-intern request returns the requested raw -expression exactly, preserves the complete K1 invariant, and changes only the +expression exactly, preserves the complete WHNF invariant, and changes only the intern table. Collision freedom and finite support are supplied by the execution-indexed request rather than assumed for an arbitrary expression. -/ theorem internExpr_whnf_eval {α : Type} {initial : TcState .anon} @@ -1308,7 +1311,7 @@ theorem internExpr_whnf_eval {α : Type} {initial : TcState .anon} exact TcM.runIntern_whnf_eval (fun _ hwf hsup => h.internExpr_spec hmem hwf hsup) hI -/-- The verified single-substitution walker preserves the complete K1 +/-- The verified single-substitution walker preserves the complete WHNF invariant. This is the explicit-let sibling of `simulSubst_whnf_wf`: production substitutes the let value into its body while only the intern table may grow. -/ @@ -1347,7 +1350,7 @@ theorem subst_whnf_eval {α : Type} {initial : TcState .anon} TcM.runIntern_whnf_eval (fun _ hwf hsup => h.subst_spec hmem hwf hsup) hI -/-- The verified lifting walker preserves the complete K1 invariant. This +/-- The verified lifting walker preserves the complete WHNF invariant. This is the legacy-zeta sibling of `simulSubst_whnf_wf`: the stored let value is rebased to the current de Bruijn depth while only the intern table may grow. -/ theorem lift_whnf_wf {α : Type} {initial : TcState .anon} @@ -1385,7 +1388,7 @@ theorem lift_whnf_eval {α : Type} {initial : TcState .anon} TcM.runIntern_whnf_eval (fun _ hwf hsup => h.lift_spec hmem hwf hsup) hI -/-- The verified simultaneous-substitution walker preserves the complete K1 +/-- The verified simultaneous-substitution walker preserves the complete WHNF invariant, not merely intern-table coherence. Its request membership keeps finite collision/support and UInt64 resource assumptions tied to an actual execution certificate. -/ @@ -1689,7 +1692,7 @@ theorem meaning {trProj : RawProjRel} {world : VerifyWorld} end WhnfPost /-- Successful inference callback postcondition used inside WHNF's K/struct -fallbacks. K2 proves this field for the concrete method table. -/ +fallbacks. Closing the concrete method table requires a proof of this field. -/ def InferPost (trProj : RawProjRel) (world : VerifyWorld) (uvars : Nat) (Δ : KVLCtx) (sourceV : VExpr) (ty : KExpr .anon) : Prop := @@ -1755,9 +1758,9 @@ structure WFAt (layer : WhnfLayer) (semantics : CacheSemantics) (fun answer _ => answer = true → world.venv.IsDefEqU uvars Δ.toCtx va vb) -/-- Conditional semantic closure of all six K0 method-table back-edges. -K1 consumes this record while proving WHNF; K2 proves the inference/defeq -fields and closes `methodsN` by induction. -/ +/-- Conditional semantic closure of all six recursive method-table calls. +The reducer proofs consume this record; closing `methodsN` by induction +also requires the inference and definitional-equality fields. -/ structure WF (layer : WhnfLayer) (semantics : CacheSemantics) (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (methods : Methods .anon) : Prop where @@ -1880,8 +1883,8 @@ structure InductiveReductionOracle (layer : WhnfLayer) namespace RecM /-- Reader-level Hoare triple conditional on a semantically closed method -table. Quantification over every `Methods.WF` table is what lets K1 land -before K2 ties the total recursive knot. -/ +table. Quantification over every `Methods.WF` table lets branch proofs use +the callback contracts without depending on the concrete recursive knot. -/ def WF (layer : WhnfLayer) (semantics : CacheSemantics) (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (uvars : Nat) (Δ : KVLCtx) (s : TcState .anon) (x : RecM .anon α) @@ -2008,7 +2011,7 @@ theorem liftTcM {layer : WhnfLayer} {semantics : CacheSemantics} intro methods hmethods exact hx -/-- Reader-level state observation preserves the K1 invariant exactly. -/ +/-- Reader-level state observation preserves the WHNF invariant exactly. -/ theorem get {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Delta : KVLCtx} {s : TcState .anon} @@ -2328,7 +2331,7 @@ theorem whnfNatReducerArg_wf · intro _ _ _ trivial -/-- Generic invariant rule for K0's total bounded-loop driver. Exhaustion +/-- Generic invariant rule for the total bounded-loop driver. Exhaustion is explicit in `hexhaust`; every successful `.next` re-establishes `P`, and every `.done` establishes the final postcondition. -/ theorem runBounded_wf {layer : WhnfLayer} {semantics : CacheSemantics} @@ -2462,7 +2465,7 @@ theorem no_zero {layer : WhnfLayer} {semantics : CacheSemantics} /-- A local semantic contract is sufficient to reconstruct the exact execution-indexed trace for every successful bounded run. On failure, - the same induction preserves the K1 invariant and says whether the loop + the same induction preserves the WHNF invariant and says whether the loop exhausted its own bound or the production step raised the error. -/ theorem complete {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} @@ -2591,7 +2594,7 @@ theorem eval {layer : WhnfLayer} {semantics : CacheSemantics} rw [hstep] exact ih -/-- The first state in a trace satisfies the same fixed K1 invariant carried +/-- The first state in a trace satisfies the same fixed WHNF invariant carried by every later state. -/ theorem initialInv {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} @@ -2603,7 +2606,7 @@ theorem initialInv {layer : WhnfLayer} {semantics : CacheSemantics} WhnfStateInv layer semantics trProj world support uvars Δ s := by cases h <;> assumption -/-- The last state in a trace still satisfies the fixed K1 invariant. -/ +/-- The last state in a trace still satisfies the fixed WHNF invariant. -/ theorem finalInv {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {uvars : Nat} {Δ : KVLCtx} {methods : Methods .anon} @@ -2645,7 +2648,7 @@ theorem uncached_eval {layer : WhnfLayer} {semantics : CacheSemantics} unfold whnfCoreWithFlagsUncached exact h.eval -/-- K1 structural-loop acceptance package: exact production execution, +/-- WHNF structural-loop acceptance package: exact production execution, initial/final fixed-world invariants, and the transitively composed Theory meaning. -/ theorem uncached_acceptance {layer : WhnfLayer} @@ -3180,7 +3183,7 @@ theorem whnfCoreWithFlags_transient_acceptance /-- Execution-indexed semantic certificate for the production no-delta WHNF loop. Each constructor records the exact named step equation, the -fixed K1 invariant on both sides, and the local Theory meaning. -/ +fixed WHNF invariant on both sides, and the local Theory meaning. -/ inductive WhnfNoDeltaTrace (layer : WhnfLayer) (semantics : CacheSemantics) (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) (uvars : Nat) @@ -3754,7 +3757,7 @@ def Represents (keys : WhnfContextKeys) (trProj : RawProjRel) TcM.whnfKey source before = .ok key after -> keys.Represents source.lbr key.2 Delta -/-- K2's suffix transport is unnecessary for a syntactically closed source: +/-- Suffix transport is unnecessary for a syntactically closed source: production returns the distinguished empty-context key exactly. -/ theorem closed_represents {uvars : Nat} {source : KExpr .anon} {trProj : RawProjRel} {world : VerifyWorld} @@ -3786,7 +3789,7 @@ end TransientNatWork sites. A meaning proof for the executed source alone is insufficient: cache validity quantifies over every supported source sharing the key's address and every represented context. Keeping this interface explicit - prevents a hash-collision assumption from entering K1 unnoticed. -/ + prevents a hash-collision assumption from entering WHNF unnoticed. -/ structure WhnfCacheWriteOracle (keys : WhnfContextKeys) (trProj : RawProjRel) (fallback : CacheSemantics) (world : VerifyWorld) (support : RunSupport) : Prop where @@ -3819,8 +3822,8 @@ namespace WhnfCacheWriteOracle /-- Construct all three outer write rules for closed expressions. Expression collision freedom identifies every supported source at the address key; the remaining premise is exactly direct-reference authorization for the - concrete cache entry. Open-context transport is deliberately absent and - remains K2 work. -/ + concrete cache entry. Open-context transport remains a separate semantic + proof obligation. -/ theorem closed {uvars : Nat} {trProj : RawProjRel} {fallback : CacheSemantics} {world : VerifyWorld} {support : RunSupport} @@ -4770,7 +4773,7 @@ theorem whnfWithNatSuccMode_nonLeaf_wf hcharge hstep hwrites hsupport hsource /-- The full-WHNF trace/statistics prefix preserves every semantic component - of the K1 state invariant, independently of instrumentation settings. -/ + of the WHNF state invariant, independently of instrumentation settings. -/ theorem whnfWithNatSuccModePrefix_wf {semantics : CacheSemantics} {layer : WhnfLayer} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} @@ -4789,7 +4792,7 @@ theorem whnfWithNatSuccModePrefix_wf (fun _ => rfl) (fun _ => rfl) (fun _ => rfl) (fun _ => rfl) (fun _ => rfl) (fun _ => rfl) (fun _ => rfl) (fun _ => rfl) s1 -/-- The full-WHNF miss charge preserves the K1 invariant on both outcomes. +/-- The full-WHNF miss charge preserves the WHNF invariant on both outcomes. Its only possible error is the underlying `.maxRecFuel`; the bounded-loop `.maxRecDepth` classification remains separate in `WhnfLoopError`. -/ theorem whnfWithNatSuccModeMissCharge_wf @@ -5033,8 +5036,8 @@ theorem whnfNoDelta_wf WhnfPost trProj world keys.uvars Delta sourceV result) := whnfNoDeltaImpl_wf theory hkeyRep htransient hstep hwrites hsupport hsource -/-- Public `RecM.whnf` specialization. K2 can use this theorem directly - when proving the corresponding `Methods.WF.whnf` field for `methodsN`. -/ +/-- Public `RecM.whnf` specialization, used to prove the corresponding + `Methods.WF.whnf` field for `methodsN`. -/ theorem whnf_wf {keys : WhnfContextKeys} {fallback : CacheSemantics} {layer : WhnfLayer} {trProj : RawProjRel} {world : VerifyWorld} @@ -7250,7 +7253,7 @@ theorem whnfCoreWithFlagsUncached_betaOne_wf exact ⟨s', whnfCoreWithFlagsUncached_betaOne hhead hwalk hleaf, hI', hframe⟩ -/-- First algorithmic K1 slice: all immediate-return WHNF forms preserve the +/-- All immediate-return WHNF forms preserve the complete fixed-world/context/cache invariant and their exact Theory meaning. The theorem is layer-polymorphic because these branches never inspect `noAccel` and never consume a `NativeOracle`. -/ @@ -7283,7 +7286,7 @@ theorem whnf_leaf_wf_of_theory {layer : WhnfLayer} exact TcM.WF.pure fun hI => WhnfPost.refl htr (theory.exprWF hI.2.1 htr) -/-- Immediate structural-WHNF forms preserve the complete K1 invariant and +/-- Immediate structural-WHNF forms preserve the complete WHNF invariant and have reflexive Theory meaning. This covers the actual flag-parametric core entry point, including constants and the cheap projection policy. -/ theorem whnfCoreWithFlags_leaf_wf {layer : WhnfLayer} @@ -7586,7 +7589,7 @@ structure NoDeltaInputSupport (support : RunSupport) : Prop where source.collectSpine = (head, args) → support head ∧ ∀ (i : Nat) (hi : i < args.size), support args[i] -/-- The concrete K1 input for active no-delta primitive proofs. It binds the +/-- The concrete WHNF input for active no-delta primitive proofs. It binds the canonical anon table to trusted Theory names, carries Ix.Theory.Named's primitive reflection laws, records the quotient lift equation, and scopes generated syntax to actual supported executions. This is necessary but intentionally @@ -12595,7 +12598,7 @@ theorem recordNatSuccStuck_eval (recordNatSuccStuck visited).run methods s = .ok () after := by rfl -/-- The shared memo commit preserves every K1 state component when each +/-- The shared memo commit preserves every WHNF state component when each visited marker has explicit cache provenance. -/ theorem recordNatSuccStuck_wf {layer : WhnfLayer} {semantics : CacheSemantics} @@ -13057,7 +13060,7 @@ structure NatSuccStuckWriteOracle (semantics : CacheSemantics) namespace NatSuccStuckWriteOracle -/-- Construct the marker oracle for K1's WHNF semantic overlay once every +/-- Construct the marker oracle for the WHNF semantic overlay once every finite-support expression is known to reference trusted declarations. -/ theorem forWhnfCache {keys : WhnfContextKeys} {trProj : RawProjRel} @@ -13129,7 +13132,7 @@ structure NatSuccLinearReflection (layer : WhnfLayer) world.venv.IsDefEqU uvars Delta.toCtx (natSuccIterV offset curV) reducedV -/-- The syntactic step recognizer preserves K1 state through its sole +/-- The syntactic step recognizer preserves WHNF state through its sole recursive WHNF callback. All later lambda/spine/address tests and primitive reads are state-transparent. -/ theorem isNatSuccIhStep_wf @@ -13585,7 +13588,7 @@ theorem tryReduceNatSuccIterStep_wf /-- The public successor-collapse helper satisfies its semantic result contract for arbitrary successor chains. The entry memo hit is a safe miss; the miss path seeds certified provenance and invokes the generic bounded-loop -driver, whose exhaustion and callback errors still preserve K1 state. -/ +driver, whose exhaustion and callback errors still preserve WHNF state. -/ theorem tryReduceNatSuccIter_wf {semantics : CacheSemantics} {trProj : RawProjRel} {world : VerifyWorld} {support : RunSupport} {flags : WhnfFlags} @@ -14058,7 +14061,7 @@ theorem tryReduceNatWithSuccMode_collapse_optional_wf rw [hrun] exact ⟨hI, trivial⟩ -/-- K1's narrow collapse-mode Nat closure surface. The implementation proof +/-- WHNF's narrow collapse-mode Nat closure surface. The implementation proof constructs both former whole-computation assumptions: descriptor ingress plus callback closure yield the linear recognizer's effect contract, while successful callback meaning plus canonical result-shape separation yields an @@ -14084,7 +14087,7 @@ theorem tryReduceNatWithSuccMode_collapse_optional_wf_of_boundaries (NatSuccLinearOracle.of_reflection context partsPreserve reflection) (NatCollapseRequestCensus.of_result_shape context theory shape) -/-- K1's stuck-mode Nat closure surface. Unary `Nat.succ` is deliberately +/-- WHNF's stuck-mode Nat closure surface. Unary `Nat.succ` is deliberately reserved for the surrounding successor loop, so this mode needs neither the linear Nat.rec reflection boundary nor stuck-cache writes. Canonical Nat/Bool result-shape separation is the only semantic boundary beyond the diff --git a/Ix/Kernel/Verify/Whnf/Closure.lean b/Ix/Kernel/Verify/Whnf/Closure.lean index 6a4acd990..1ccdad0e5 100644 --- a/Ix/Kernel/Verify/Whnf/Closure.lean +++ b/Ix/Kernel/Verify/Whnf/Closure.lean @@ -4,9 +4,9 @@ import Ix.Kernel.Verify.Knot /-! # Four-field fixed-universe WHNF closure -The structural, no-delta, full-WHNF, and trusted-delta reducers now expose +The structural, no-delta, full-WHNF, and trusted-delta reducers expose fixed-universe contracts. This module assembles those contracts into the -four K1 fields of one unfolded production method-table layer. +four WHNF fields of one unfolded production method-table layer. The context retains the exact construction boundary: @@ -19,17 +19,16 @@ The context retains the exact construction boundary: In particular, the full reducer is constructed with `FullWhnfStepContext.ofTrustedDelta`; callers cannot replace delta unfolding -with a free successful-reduction oracle. The `tryNatOffsetStuck` stage added -after the original K1 driver proof remains an explicit closure obligation -until its callbacks and intern operations are decomposed into finite -run-scoped inputs. +with a free successful-reduction oracle. The `tryNatOffsetStuck` stage remains +an explicit closure obligation requiring its callbacks and intern operations +to be decomposed into finite run-scoped inputs. -## K1 acceptance boundary +## WHNF acceptance boundary -`K1ClosureContext.closedAt` below is the K1 closure result: it supplies exactly +`K1ClosureContext.closedAt` below is the WHNF closure result: it supplies exactly the four fixed-universe WHNF fields of `Methods.next`. It deliberately does -not tie the complete six-method production knot. That later step also needs -K2's `infer` and `isDefEq` fields before `Methods.ClosedAt.of_parts`, +not tie the complete six-method production knot. That also requires the +`infer` and `isDefEq` fields before `Methods.ClosedAt.of_parts`, `Methods.methodsN_wfAt`, and the public runner can be used. The universally quantified caller context is not assumed well formed merely @@ -37,12 +36,12 @@ to construct `K1ClosureContext`. Each reducer instead recovers that fact from the `CtxRecon` component of the runtime invariant at its point of use. The concrete successful, absent, stuck, and partial-error executions in `NatFixture` separately keep the branch contracts inhabited; they are not a -substitute for K2's two missing recursive fields. +substitute for the two remaining inference and conversion fields. -/ namespace Ix.Kernel -/-- The final K1 cache composition at the universe count encoded by `keys`: +/-- The final WHNF cache composition at the universe count encoded by `keys`: WHNF expression entries outside, universe-sensitive delta bodies underneath, and the caller's remaining cache families as the base. -/ def k1CacheSemantics (keys : WhnfContextKeys) (trProj : RawProjRel) @@ -52,7 +51,7 @@ def k1CacheSemantics (keys : WhnfContextKeys) (trProj : RawProjRel) namespace RecM -/-- Complete input family needed to prove the four K1 method-table fields at +/-- Complete input family needed to prove the four WHNF method-table fields at one universe count. -/ structure K1ClosureContext {alpha : Type} (initial : TcState .anon) (program : TcM .anon alpha) @@ -63,8 +62,7 @@ structure K1ClosureContext NoDeltaDriverContext initial program requests keys (unfoldCacheSemantics keys.uvars trProj fallback) trProj world support Delta .FULL - /-- Exact closure obligation for the compact symbolic-Nat stage introduced - after the original K1 driver proof. -/ + /-- Closure obligation for the compact symbolic-Nat reduction stage. -/ natOffsetStuck : OptionalReduction.WFAt .noAccel (k1CacheSemantics keys trProj fallback) trProj world support keys.uvars tryNatOffsetStuck @@ -78,7 +76,7 @@ structure K1ClosureContext namespace K1ClosureContext -/-- Assemble K1's four fixed-universe fields for one smaller, already +/-- Assemble WHNF's four fixed-universe fields for one smaller, already well-formed method table. -/ theorem layer {alpha : Type} {initial : TcState .anon} {program : TcM .anon alpha} @@ -122,7 +120,7 @@ theorem layer (context.structuralFlags Delta flags) hsourceSupport hsource) methods hmethods -/-- K1's headline fixed-universe closure result: any semantically valid +/-- WHNF's headline fixed-universe closure result: any semantically valid smaller method table proves all four WHNF fields of the next production layer. -/ theorem closedAt diff --git a/Ix/Kernel/Verify/Whnf/Delta/CacheExecution.lean b/Ix/Kernel/Verify/Whnf/Delta/CacheExecution.lean index 71e63f63f..4ac644dec 100644 --- a/Ix/Kernel/Verify/Whnf/Delta/CacheExecution.lean +++ b/Ix/Kernel/Verify/Whnf/Delta/CacheExecution.lean @@ -3,7 +3,7 @@ import Ix.Kernel.Verify.Whnf.Delta.StableCache /-! # Certified production unfold-cache execution -The public K1 cache contract is a WHNF overlay whose fallback owns delta +The public WHNF cache contract is a WHNF overlay whose fallback owns delta entries. StableCache constructs the exact fallback provenance for a trusted body; this module transports that provenance through the overlay and verifies both physical paths of production's `unfoldConstValue`: diff --git a/Ix/Kernel/Verify/Whnf/Delta/TrustedBody.lean b/Ix/Kernel/Verify/Whnf/Delta/TrustedBody.lean index b42339ef3..f358bbfaa 100644 --- a/Ix/Kernel/Verify/Whnf/Delta/TrustedBody.lean +++ b/Ix/Kernel/Verify/Whnf/Delta/TrustedBody.lean @@ -7,7 +7,7 @@ open Ix.Theory (VLevel) `UnfoldingState` closes the operational state and support behavior of delta unfolding, but its two remaining semantic inputs are intentionally too broad for final -K1 closure: one can certify an unfold-cache write for an arbitrary supported +WHNF closure: one can certify an unfold-cache write for an arbitrary supported head/result pair, and the other can reflect any observed successful delta run. @@ -200,7 +200,7 @@ private theorem sourceStructural (.const id us info) (.const ci.name (instantiatedLevels us)) := .const h.nameEq h.lookup hus harity -/-- Exact K1 semantics of unfolding one trusted definition or theorem body. +/-- Exact WHNF semantics of unfolding one trusted definition or theorem body. Ordinary definitions use their registered equation. Theorem constants are not registered as reducible Theory equations, so the proof uses the diff --git a/Ix/Kernel/Verify/Whnf/Iota/ApplicationRequests.lean b/Ix/Kernel/Verify/Whnf/Iota/ApplicationRequests.lean index 95ba62c0f..578e4b3b3 100644 --- a/Ix/Kernel/Verify/Whnf/Iota/ApplicationRequests.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/ApplicationRequests.lean @@ -36,7 +36,7 @@ inductive IotaArgsInternRequests (requests : List WalkerRequest) : namespace IotaArgsInternRequests -/-- A certified non-transient list fold preserves the complete K1 invariant +/-- A certified non-transient list fold preserves the complete WHNF invariant and returns its indexed final application. -/ theorem wfList {α : Type} {initial : TcState .anon} {program : TcM .anon α} diff --git a/Ix/Kernel/Verify/Whnf/Iota/ConstructorDispatch.lean b/Ix/Kernel/Verify/Whnf/Iota/ConstructorDispatch.lean index b281ccd9a..f740dc992 100644 --- a/Ix/Kernel/Verify/Whnf/Iota/ConstructorDispatch.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/ConstructorDispatch.lean @@ -503,7 +503,7 @@ theorem tryIotaWithFlags_regularCtor /-- Headline ConstructorDispatch contract: the actual parameter-free regular-constructor branch executes the checked rule selected at the runtime constructor index. The mutable preprocessing prefix must supply its intern-only frame and the -invariant at dispatch ingress; later K1 slices discharge those facts for the +invariant at dispatch ingress; later WHNF slices discharge those facts for the cleanup, callback, and lazy-lookup helpers themselves. -/ theorem tryIotaWithFlags_regularCtor_checkedAcceptance_empty {layer : WhnfLayer} {semantics : CacheSemantics} diff --git a/Ix/Kernel/Verify/Whnf/Iota/NatOffset.lean b/Ix/Kernel/Verify/Whnf/Iota/NatOffset.lean index fb5b60aca..bb7264b90 100644 --- a/Ix/Kernel/Verify/Whnf/Iota/NatOffset.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/NatOffset.lean @@ -229,7 +229,7 @@ successful Nat-offset cleanup. The oracle is indexed by the actual production execution and assumes neither state preservation nor callback behavior. A later primitive/parser trace -construction supplies this field; K1 uses it only to recover the support and +construction supplies this field; WHNF uses it only to recover the support and structural translation required by `Methods.WF` for the selected major. -/ structure NatOffsetCleanupInputOracle (trProj : RawProjRel) (world : VerifyWorld) (support : RunSupport) : Prop where @@ -492,7 +492,7 @@ theorem tryIotaAfterCleanup_state_wf | prj id field value info => exact hdispatch (.prj id field value info) s | nat value blob info => exact hdispatch (.nat value blob info) s -/-- The complete post-major preprocessing stage preserves the fixed K1 +/-- The complete post-major preprocessing stage preserves the fixed WHNF invariant. Nat conversion and both cleanup passes are now concrete. String conversion uses the finite StringExpansion plan and the predecessor method-table contract for its one policy-selected callback. -/ diff --git a/Ix/Kernel/Verify/Whnf/Iota/OptionalReduction.lean b/Ix/Kernel/Verify/Whnf/Iota/OptionalReduction.lean index 262a58432..89aee4202 100644 --- a/Ix/Kernel/Verify/Whnf/Iota/OptionalReduction.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/OptionalReduction.lean @@ -4,7 +4,7 @@ import Ix.Kernel.Verify.Whnf.Iota.Ingress # Exhaustive iota optional-reduction contract `Ingress` proves that every result or partial error of the production -`tryIotaWithFlags` dispatcher preserves the complete K1 state invariant. This +`tryIotaWithFlags` dispatcher preserves the complete WHNF state invariant. This slice separates the two remaining concerns: * `IotaCallbackFrameOracle` retains the trusted-reference and diff --git a/Ix/Kernel/Verify/Whnf/Iota/SelectedRule.lean b/Ix/Kernel/Verify/Whnf/Iota/SelectedRule.lean index 680fd94ca..a2e5c43d4 100644 --- a/Ix/Kernel/Verify/Whnf/Iota/SelectedRule.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/SelectedRule.lean @@ -156,7 +156,7 @@ end RecM namespace TcM -/-- A successful universe-instantiation run preserves the complete K1 +/-- A successful universe-instantiation run preserves the complete WHNF invariant, changes only the intern table, and returns an expression in the walk's finite support. RuleInstantiation used the walker equation semantically; this is the state/resource half needed before the three ArgumentExecution traces can start. -/ diff --git a/Ix/Kernel/Verify/Whnf/Iota/StructEtaControl.lean b/Ix/Kernel/Verify/Whnf/Iota/StructEtaControl.lean index 8ebcb7d67..82da3ada9 100644 --- a/Ix/Kernel/Verify/Whnf/Iota/StructEtaControl.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/StructEtaControl.lean @@ -886,7 +886,7 @@ theorem eval h.selection.eval (h.probes.success h.admissible h.instantiation h.rebuild) -/-- K1 acceptance at the honest semantic boundary. The operational trace is +/-- WHNF acceptance at the honest semantic boundary. The operational trace is constructed here; state preservation, finite support, and Theory meaning are explicit premises because structure-likeness alone does not supply the registered struct-eta equation or projection interpretation. -/ diff --git a/Ix/Kernel/Verify/Whnf/Iota/SynthesisRequests.lean b/Ix/Kernel/Verify/Whnf/Iota/SynthesisRequests.lean index 69221e64e..3d4f82c82 100644 --- a/Ix/Kernel/Verify/Whnf/Iota/SynthesisRequests.lean +++ b/Ix/Kernel/Verify/Whnf/Iota/SynthesisRequests.lean @@ -233,7 +233,7 @@ theorem state_wf end FinishAppRequests -/-- Candidate construction preserves the complete K1 invariant from the +/-- Candidate construction preserves the complete WHNF invariant from the finite intern plan plus the two exact callback authorities. -/ theorem verifyKSynthCandidate_state_wf_of_requests {α : Type} {initial : TcState .anon} {program : TcM .anon α} diff --git a/Ix/Kernel/Verify/Whnf/NoDelta/QuotientReflection.lean b/Ix/Kernel/Verify/Whnf/NoDelta/QuotientReflection.lean index 549a76cbe..fbc1907da 100644 --- a/Ix/Kernel/Verify/Whnf/NoDelta/QuotientReflection.lean +++ b/Ix/Kernel/Verify/Whnf/NoDelta/QuotientReflection.lean @@ -17,7 +17,7 @@ namespace Ix.Kernel open Ix.Theory.Named (VExpr) -/-- Theory-only quotient contraction laws expected from Ix.Theory.Named Spec-19B. +/-- Theory-only quotient contraction laws required from Ix.Theory.Named. Both laws are deliberately phrased after the quotient major has been related to an exact `Quot.mk` application. The Ix adapter below owns the proof of that diff --git a/Ix/Kernel/Verify/Whnf/Projection/StringExpansion.lean b/Ix/Kernel/Verify/Whnf/Projection/StringExpansion.lean index ad7a264f4..78f4badb7 100644 --- a/Ix/Kernel/Verify/Whnf/Projection/StringExpansion.lean +++ b/Ix/Kernel/Verify/Whnf/Projection/StringExpansion.lean @@ -111,7 +111,7 @@ inductive StringListPlan (support : RunSupport) StringListPlan support charOfNat cons (c :: chars) list result /-- The actual recursive String-list builder executes any finite pure plan, -returning its exact result and preserving the complete K1 invariant. -/ +returning its exact result and preserving the complete WHNF invariant. -/ theorem strLitListToConstructor_plan_wf {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} @@ -264,7 +264,7 @@ theorem strLitToConstructorWithPrimitives_plan_exact_wf rw [hrun] exact ⟨hI9, rfl, plan.final, plan.translation uvars Delta⟩ -/-- Compatibility form used by K1 callers that need only support and some +/-- Compatibility form used by WHNF callers that need only support and some structural translation of the generated constructor term. -/ theorem strLitToConstructorWithPrimitives_plan_wf {layer : WhnfLayer} {semantics : CacheSemantics} {trProj : RawProjRel} diff --git a/Ix/Kernel/Verify/Whnf/README.md b/Ix/Kernel/Verify/Whnf/README.md index f204baea5..c3a257534 100644 --- a/Ix/Kernel/Verify/Whnf/README.md +++ b/Ix/Kernel/Verify/Whnf/README.md @@ -1,8 +1,7 @@ # WHNF verification modules -The WHNF formalization is organized by proof responsibility rather than by -project milestone. The directories are conceptual layers; imports retain the -precise proof-dependency order needed by Lean. +The WHNF formalization is organized by proof responsibility. The directories +group related contracts; imports retain the proof-dependency order needed by Lean. - `RuntimeContracts.lean` defines the common state, callback, and result contracts used by the reducer proofs. diff --git a/Ix/Kernel/Verify/Whnf/Runtime/LazyIngress.lean b/Ix/Kernel/Verify/Whnf/Runtime/LazyIngress.lean index 99045d5ce..db2656c95 100644 --- a/Ix/Kernel/Verify/Whnf/Runtime/LazyIngress.lean +++ b/Ix/Kernel/Verify/Whnf/Runtime/LazyIngress.lean @@ -102,7 +102,7 @@ theorem ctxRecon simpa [TcM.lazyIngressPost, frame.nextFVarId] using hold · simpa [TcM.lazyIngressPost] using h.lets -/-- One callback outcome preserves the entire K1 invariant, including the +/-- One callback outcome preserves the entire WHNF invariant, including the address mark retained by production on both success and failure. -/ theorem whnfStateInv {layer : WhnfLayer} {semantics : CacheSemantics} diff --git a/Ix/Kernel/Verify/Whnf/StructEta/RebuildTail.lean b/Ix/Kernel/Verify/Whnf/StructEta/RebuildTail.lean index ff2231721..a7e0da5fb 100644 --- a/Ix/Kernel/Verify/Whnf/StructEta/RebuildTail.lean +++ b/Ix/Kernel/Verify/Whnf/StructEta/RebuildTail.lean @@ -14,7 +14,7 @@ namespace Ix.Kernel namespace TcM -/-- Universe instantiation preserves the complete K1 invariant on both +/-- Universe instantiation preserves the complete WHNF invariant on both outcomes. On success it additionally returns the pure-spec equation and a result in finite run support. The error proof is important here: production uses non-backtracking `EStateM`, so a failed walk may retain intern-table diff --git a/Ix/Kernel/Verify/Whnf/Structural/BasicStep.lean b/Ix/Kernel/Verify/Whnf/Structural/BasicStep.lean index f9d33dd5c..a4a390df2 100644 --- a/Ix/Kernel/Verify/Whnf/Structural/BasicStep.lean +++ b/Ix/Kernel/Verify/Whnf/Structural/BasicStep.lean @@ -18,7 +18,7 @@ namespace Ix.Kernel namespace RecM /-- Runtime safety needed by production's unchanged let-fvar return. This -property is indexed by every state satisfying the fixed K1 invariant so it +property is indexed by every state satisfying the fixed WHNF invariant so it can be consumed by the uniform `WhnfStep.WF` contract rather than by one hand-picked execution fixture. -/ def FVarZetaSafety (layer : WhnfLayer) (semantics : CacheSemantics) diff --git a/Ix/Kernel/Verify/Whnf/Structural/ProjectionStep.lean b/Ix/Kernel/Verify/Whnf/Structural/ProjectionStep.lean index 5cb237693..a0e641c7b 100644 --- a/Ix/Kernel/Verify/Whnf/Structural/ProjectionStep.lean +++ b/Ix/Kernel/Verify/Whnf/Structural/ProjectionStep.lean @@ -11,7 +11,7 @@ lazy ingress. `ProjectionHelper.WF` records exactly that remaining implementation boundary: for a supported callback result, the actual `tryProjReduce` computation -preserves the fixed K1 state invariant on hits, misses, and errors, and any +preserves the fixed WHNF state invariant on hits, misses, and errors, and any successful result remains in finite run support. The step theorem below then proves every concrete projection outcome. Semantic authority for a hit stays with `InductiveReductionOracle`; a syntax-directed helper execution alone is diff --git a/Ix/Kernel/Verify/World.lean b/Ix/Kernel/Verify/World.lean index 3876b7358..576960bfe 100644 --- a/Ix/Kernel/Verify/World.lean +++ b/Ix/Kernel/Verify/World.lean @@ -4,8 +4,8 @@ import Ix.Theory.Named.Typing.Env /-! # Non-circular verification worlds -This is the additive G1a model. It separates four things which the old -whole-`KEnv` relation conflates: +The verification world separates the catalog, trusted declarations, and +concrete lazy-load state: * `Catalog`: immutable ghost input, including pending and unrelated declarations; @@ -21,14 +21,12 @@ whole-`KEnv` relation conflates: Crucially, being catalogued is not a typing fact. `VerifyWorld.ofCatalog` accepts an arbitrary catalog while trusting nothing, and `LoadedAgrees` does not require every catalog entry to be loaded. The trusted-catalog semantic -log is deliberately not faked as a bare structure field: G1c's -`TrustedCatalogRel` in `Verify/Env.lean` is an explicit proof object connecting +log is an explicit proof object: `TrustedCatalogRel` in `Verify/Env.lean` connects `trusted` to `venv`. Consumers must carry that relation before treating trusted membership as a WF witness. -This module was introduced beside the old whole-`KEnv` relation in G1a. -`Verify/State.lean` now uses it for `TcInv`, and G2b consumers resolve exact -constants through `TrustedConstRel`. The legacy `TrKEnv` remains only as a +`Verify/State.lean` uses this model for `TcInv`; lookup consumers resolve exact +constants through `TrustedConstRel`. The legacy `TrKEnv` remains a quarantined compatibility proof interface. -/ @@ -86,7 +84,7 @@ structure VerifyWorld where venvWF : venv.WF trustedCatalogued : ∀ {id}, trusted id → Catalog.Contains catalog id /-- Exact block identity is immutable ghost input. The default preserves - the pre-E0 standalone fixtures, which do not exercise coordinated blocks. -/ + standalone fixtures, which do not exercise coordinated blocks. -/ blocks : BlockCatalog := BlockCatalog.empty namespace VerifyWorld @@ -257,8 +255,8 @@ section Insert variable [LawfulBEq (KId .anon)] [LawfulHashable (KId .anon)] /-- A lazy-load insertion preserves agreement when the inserted declaration -is the catalog entry. The lawfulness instances are hypotheses here; G1a -does not move the existing global instances out of `Verify/Env.lean`. -/ +is the catalog entry. Lawfulness instances are explicit hypotheses; the +global instances are defined in `Verify/Env.lean`. -/ theorem insert {catalog : Catalog} {env : KEnv .anon} (h : LoadedAgrees catalog env) {id : KId .anon} {c : KConst .anon} (hc : catalog id = some c) : LoadedAgrees catalog (env.insert id c) := by diff --git a/Ix/Kernel/Whnf.lean b/Ix/Kernel/Whnf.lean index d74511c4f..f19786d71 100644 --- a/Ix/Kernel/Whnf.lean +++ b/Ix/Kernel/Whnf.lean @@ -382,7 +382,7 @@ def natOffsetStuckHead (p : Primitives m) : KExpr m → Bool These helpers used to live in the large recursive WHNF mutual block even though none of them takes a recursive edge. Keeping them outside that block -makes their equations transparent to the K0 proofs without adding runtime +makes their equations transparent to the totalization proofs without adding runtime fuel or changing their operational behavior. -/ diff --git a/Ix/Theory/Named/ConstructorValidityFixtures.lean b/Ix/Theory/Named/ConstructorValidityFixtures.lean index 7d5348b7b..a04903c68 100644 --- a/Ix/Theory/Named/ConstructorValidityFixtures.lean +++ b/Ix/Theory/Named/ConstructorValidityFixtures.lean @@ -11,7 +11,7 @@ open Ix.Theory (VLevel) /-! # Constructor-validity parity fixtures -Focused singleton declarations for Spec-05. The positive declarations retain +Focused singleton constructor-validity declarations. The positive declarations retain real kernel metadata while remaining inside the already-supported large- elimination and non-K fragment. The failed declarations pin the nearest Lean elaborator/kernel rejection for each neighboring positivity or universe diff --git a/Ix/Theory/Named/Fixtures/ProjectionExpressibility.lean b/Ix/Theory/Named/Fixtures/ProjectionExpressibility.lean index e5c89c2ca..b06bed1fb 100644 --- a/Ix/Theory/Named/Fixtures/ProjectionExpressibility.lean +++ b/Ix/Theory/Named/Fixtures/ProjectionExpressibility.lean @@ -1139,7 +1139,7 @@ theorem dependentRecord_rules_registered : /-! ## Frozen legacy surface -The seven fields below preserve the exact pre-Spec-13 theorem shapes. They +The seven fields below preserve the earlier projection theorem shapes. They are intentionally only statement data: constructing this bundle would reintroduce the old proof obligations. In particular, `wf` permits unrelated contexts, `uniq` permits unrelated structure names, and every diff --git a/Ix/Theory/Named/Inductive.lean b/Ix/Theory/Named/Inductive.lean index 2e67b9fd2..de7598221 100644 --- a/Ix/Theory/Named/Inductive.lean +++ b/Ix/Theory/Named/Inductive.lean @@ -898,9 +898,8 @@ def checkedFamilies? (source : VInductDecl) (params : List VExpr) : /-- Source-indexed checked representation of a complete inductive block. Shared parameters are stored once; every per-family component lives in the -dependent `families` spine indexed by `source.types` itself. This is the -Spec-08A analysis boundary and intentionally has no generation or insertion -projection. -/ +dependent `families` spine indexed by `source.types` itself. This analysis +artifact contains no generation or insertion result. -/ structure CheckedBlock (source : VInductDecl) where params : List VExpr params_eq : params = blockParams source.nparams source.types @@ -1312,7 +1311,7 @@ structure GenerationChecked (source : VInductDecl) where /-- Identity normalization always satisfies the mixed generator's positional layout gate. The only non-reflexive facts are the parameter and constructor -parameter lengths already certified by Stage 3. -/ +parameter lengths certified by `Checked.direct_layout`. -/ theorem Checked.identityBlock_generationShape {decl : VInductDecl} (checked : decl.Checked) : checked.identityBlock.generationShape = true := by @@ -2991,7 +2990,7 @@ def VEnv.addInduct (env : VEnv) (decl : VInductDecl) : Option VEnv := do let generation ← decl.identityBlockGeneration? env.addInductBlockGeneration generation -/-- Compatibility wrapper for the pre-Spec-08C one-family raw transaction. +/-- Compatibility wrapper for the one-family raw transaction. Unlike `addInduct`, this deliberately projects the legacy `Checked` artifact and therefore rejects every genuinely mutual declaration. It remains only diff --git a/Ix/Theory/Named/InductiveFixtures.lean b/Ix/Theory/Named/InductiveFixtures.lean index f1b2ff3be..480b6e8aa 100644 --- a/Ix/Theory/Named/InductiveFixtures.lean +++ b/Ix/Theory/Named/InductiveFixtures.lean @@ -12,7 +12,7 @@ import Ix.Theory.Named.Typing.Meta open Ix.Theory (VLevel) -/-! Adequacy fixtures for `VEnv.addInduct` (stage 3): run the generator on +/-! Adequacy fixtures for `VEnv.addInduct`: run the generator on hand-written declarations and check the output against the real kernel's constants, translated by the `vconst`/`vdefeq` macros. A mismatch in telescope order, universe conventions, or de Bruijn arithmetic fails these diff --git a/Ix/Theory/Named/MutualInductiveFixtures.lean b/Ix/Theory/Named/MutualInductiveFixtures.lean index 23cfbdb48..c6548c444 100644 --- a/Ix/Theory/Named/MutualInductiveFixtures.lean +++ b/Ix/Theory/Named/MutualInductiveFixtures.lean @@ -14,7 +14,7 @@ import Ix.Theory.Named.Meta These real kernel declarations pin shared parameters, per-family indices/results, constructor order, and cross-family recursive target ordinals at the `VInductDecl.CheckedBlock` boundary, then exercise the -block-wide public generation transaction added by Spec-08C. The Verify mutual +block-wide public generation transaction. The Verify mutual fixture supplies semantic preservation, complete kernel metadata comparison, and environment replay. -/ diff --git a/Ix/Theory/Named/NestedInductive.lean b/Ix/Theory/Named/NestedInductive.lean index 2b469231a..45fc9759d 100644 --- a/Ix/Theory/Named/NestedInductive.lean +++ b/Ix/Theory/Named/NestedInductive.lean @@ -9,11 +9,11 @@ import Ix.Theory.Named.Inductive open Ix.Theory (VLevel) /-! -# Nested-inductive flattening (Spec-09B) +# Nested-inductive flattening The Theory mirror of the kernel's `ElimNestedInductive` transformation, -following the committed Spec-09A design -(`Ix.Theory.Named/Verify/Environment/NestedRepresentation.lean`): the stored +with representation probes in `Verify/Environment/NestedRepresentation.lean`: +the stored payload of a nested declaration is the source `VInductDecl`, and nested support flows through an additive artifact coupling @@ -45,16 +45,15 @@ The transformation mirrors the kernel phase for phase: block is stable. - Auxiliary names are canonical: `(`_nested` ++ familyName).appendIndexAfter i` with a global counter, matching the kernel's choice whenever the ambient - environment contains no colliding `_nested.*` constant. The Spec-09A + environment contains no colliding `_nested.*` constant. The representation collision probe shows the choice is erased from all final artifacts, and in-block collisions are rejected downstream by `blockNamesOK` exactly where the kernel's `checkName` rejects its own collisions. Acceptance (`nestedStage3`) is flattening success plus generation -readiness of the flattened block through the unchanged Spec-08 machinery. -No generated recursor, rule, or environment replay is claimed at this -checkpoint; the restoration substitution over generation artifacts is -Spec-09C's obligation. +readiness of the flattened block through the block analyzer and generator. +Generation artifacts are subsequently restored by the substitution defined +in this module; environment replay is verified separately. -/ namespace Ix.Theory.Named @@ -287,9 +286,8 @@ def NestedElimination.numNested {source : VInductDecl} elim.specs.length /-- A flattened declaration accepted by the unchanged arbitrary-block -machinery: the complete Spec-09B validation gate. Positivity, name, level, -anatomy, and generation-shape checking of the flattened block reuse the -Spec-08 analyzers verbatim. -/ +machinery. Positivity, name, level, anatomy, and generation-shape checking +reuse the block analyzers. -/ structure NestedBlockChecked (source : VInductDecl) where elim : NestedElimination source generation : BlockGenerationChecked elim.flat @@ -306,7 +304,7 @@ def nestedStage3 (targets : List NestedTargetBlock) (source : VInductDecl) (fuel : Nat := 1000) : Bool := (nestedBlockChecked? targets source fuel).isSome -/-! ## Restoration (Spec-09C) +/-! ## Restoration The restoration substitution σ maps the flattened block's generation artifacts back to the stored metadata surface: auxiliary family constants @@ -477,8 +475,8 @@ well formed at their exact insertion environments. The phase environments are determined by the deterministic constant folds, so each later field takes the earlier folds as hypotheses; a fixture discharges them by computation. Inhabiting this package from the flattened block's staged -semantic certificate is the σ-transport route recorded by the Spec-09A -design note; fixtures may equally inhabit it from direct checker +semantic certificate requires typing transport along σ; fixtures may also +inhabit it from direct checker executions on the restored artifacts. -/ structure NestedBlockChecked.WF {source : VInductDecl} (nested : NestedBlockChecked source) (env : VEnv) : Prop where diff --git a/Ix/Theory/Named/NestedInductiveFixtures.lean b/Ix/Theory/Named/NestedInductiveFixtures.lean index 8ca84b912..015ce17ad 100644 --- a/Ix/Theory/Named/NestedInductiveFixtures.lean +++ b/Ix/Theory/Named/NestedInductiveFixtures.lean @@ -7,7 +7,7 @@ Source attribution and revision: Ix/Theory/Named/NOTICE. import Ix.Theory.Named.NestedInductive /-! -# Nested flattening fixtures (Spec-09B) +# Nested flattening fixtures Executable pins for `nestedElimination?` on the two ladder fixtures — a universe-polymorphic rose tree through `List` and a nested indexed family @@ -85,7 +85,7 @@ def roseSource : VInductDecl where /-- The expected flattened rose block: the rewritten source family plus one auxiliary family, exactly the shapes pinned against the kernel by the -Spec-09A probes. -/ +representation probes. -/ def roseFlat : VInductDecl where uvars := 1 nparams := 1 @@ -266,7 +266,7 @@ def collisionSource : VInductDecl where #guard (nestedElimination? [listTarget] collisionSource).isSome #guard !nestedStage3 [listTarget] collisionSource -/-! ## Restoration pins (Spec-09C) +/-! ## Restoration checks Structural pins for the restored generation artifacts; the exact comparison against Lean's stored recursor types and rule RHSs lives in the diff --git a/Ix/Theory/Named/Projection.lean b/Ix/Theory/Named/Projection.lean index b4ed1ed5c..c3df2c34c 100644 --- a/Ix/Theory/Named/Projection.lean +++ b/Ix/Theory/Named/Projection.lean @@ -3447,7 +3447,7 @@ remains the proved `projector_constructor_exact` theorem. Its eventual proof uses `IsDefEqU.weakN_iff` together with injectivity of registered inductive heads. Keeping the boundary in Theory makes the -temporary Spec-16/17 dependency explicit instead of leaving Verify's +remaining inversion and injectivity dependencies explicit instead of leaving Verify's structural laws as local holes. -/ structure RegisteredStructureHeadInversion (env : VEnv) : Prop where weak'_inv : @@ -3499,10 +3499,9 @@ structure RegisteredStructureHeadInversion (env : VEnv) : Prop where code constructorName runtimeMajor runtimeField) set_option warn.sorry false in -/-- Public Tier-R registered-head inversion statement. Spec-16/17 discharge -the underlying constant-head theorem; projection structural laws consume only -this stable interface and therefore shed `sorryAx` automatically when it is -proved. -/ +/-- Registered-head inversion statement. The constant-head inversion proof +remains unfinished. Projection structural laws consume this interface and +inherit its `sorryAx` dependency until that proof is supplied. -/ theorem WF.registeredStructureHeadInversion (self : VEnv.WF env) : RegisteredStructureHeadInversion env := by sorry diff --git a/Ix/Theory/Named/SingletonParity.lean b/Ix/Theory/Named/SingletonParity.lean index 32a76e7d4..aa1b08e7a 100644 --- a/Ix/Theory/Named/SingletonParity.lean +++ b/Ix/Theory/Named/SingletonParity.lean @@ -12,7 +12,7 @@ open Ix.Theory (VLevel) /-! # Complete singleton-family parity inputs -This module is the Theory-side inventory for Spec-07. Each row owns the exact +This module inventories singleton Theory fixtures. Each row owns the exact raw declaration together with the one checked generation artifact consumed by the kernel differential and environment-replay matrix. `Unit` is represented honestly by the kernel's polymorphic `PUnit` family; the reducible `Unit` @@ -135,7 +135,7 @@ def constructorNames (artifact : SingletonParityArtifact) : List Name := end SingletonParityArtifact -/-- The fixed Spec-07 positive matrix, in roadmap order. The `Unit` row points +/-- The positive matrix in fixed order. The `Unit` row points to `PUnit`, matching the actual v4.31 kernel representation rather than inventing alias-level inductive metadata. -/ def singletonPositiveArtifacts : List SingletonParityArtifact := diff --git a/Ix/Theory/Named/Typing/InductiveCertificate.lean b/Ix/Theory/Named/Typing/InductiveCertificate.lean index 3f0afdf3e..991c81b97 100644 --- a/Ix/Theory/Named/Typing/InductiveCertificate.lean +++ b/Ix/Theory/Named/Typing/InductiveCertificate.lean @@ -320,7 +320,7 @@ theorem recursorPattern .mk hentry /-- Rule-level consumer bundle: exact global position, generated-list -membership, registration, well-formedness, and the corresponding Spec-10 +membership, registration, well-formedness, and the corresponding iota-pattern pattern all come from the same completed block. -/ structure RecursorRuleFacts (certificate : BlockCertificate source before after) diff --git a/Ix/Theory/Named/Typing/InductivePattern.lean b/Ix/Theory/Named/Typing/InductivePattern.lean index b853952d7..75a95627f 100644 --- a/Ix/Theory/Named/Typing/InductivePattern.lean +++ b/Ix/Theory/Named/Typing/InductivePattern.lean @@ -34,9 +34,9 @@ Church–Rosser `Params` interface demands of one certified block: same-recursor rules comes from the analyzer's terminal `blockTarget?` arity equation. -No open-environment `Params` instance is installed here; the block supplies -the facts, and soundness (`pat_wf`) plus the block-local environment -assembler belong to the pattern-soundness milestone. -/ +The block supplies the `Params` facts. Pattern soundness (`pat_wf`) and the +block-local environment assembler are defined in `Typing/InductivePatternWF.lean`. +This module installs no open-environment `Params` instance. -/ namespace Ix.Theory.Named @@ -535,7 +535,7 @@ theorem rulePattern_inj {i i' : Nat} {c c' : NormalizedBlockCtor} /-- Closedness inputs for one certified block's rule payloads: the towers a rule's RHS template and checks embed as fixed template constants. Concrete -fixtures discharge this bundle by `decide`; the pattern-soundness milestone +fixtures discharge this bundle by `decide`; the pattern-soundness proof derives it from the staged environment's rule well-formedness. -/ structure RuleClosure : Prop where rhs_closed : ∀ ⦃i : Nat⦄ ⦃constructor : NormalizedBlockCtor⦄, diff --git a/Ix/Theory/Named/Typing/InductivePatternWF.lean b/Ix/Theory/Named/Typing/InductivePatternWF.lean index d01b7305a..ab8ef68d9 100644 --- a/Ix/Theory/Named/Typing/InductivePatternWF.lean +++ b/Ix/Theory/Named/Typing/InductivePatternWF.lean @@ -12,7 +12,7 @@ open Ix.Theory (VLevel) /-! # Pattern soundness for generated iota rules -The typed β-collapse layer for Spec-10B: applying a lambda tower to a +Typed β-collapse: applying a lambda tower to a well-typed argument spine is definitionally equal to the iterated instantiation of its body (`IsDefEq.appN_lamN`), applications are congruent along spines (`IsDefEq.appN_congr`, `IsDefEq.appN_defEq` over @@ -921,8 +921,8 @@ end Ix.Theory.Named The typed β-collapse layer is sorry-free. `pat_wf` composes typed defeqs through `IsDefEqU.of_l`/`IsDefEqU.trans` and therefore carries exactly the transitional unique-typing closure the Church–Rosser development itself -carries; it sheds `sorryAx` automatically when the Spec-16/17 inversion -milestones land, with no restatement. -/ +carries. The `sorryAx` dependency remains until the underlying inversion +and unique-typing proofs are complete. -/ #guard_named_axioms Ix.Theory.Named.VEnv.IsDefEq.appN_lamN [propext, Quot.sound] diff --git a/Ix/Theory/Named/Typing/NestedInductiveLemmas.lean b/Ix/Theory/Named/Typing/NestedInductiveLemmas.lean index 1a8ad71d8..9c856542e 100644 --- a/Ix/Theory/Named/Typing/NestedInductiveLemmas.lean +++ b/Ix/Theory/Named/Typing/NestedInductiveLemmas.lean @@ -8,7 +8,7 @@ import Ix.Theory.Named.NestedInductive import Ix.Theory.Named.Typing.InductiveLemmas /-! -# Nested transaction facts and preservation (Spec-09C) +# Nested transaction facts and preservation The `addInductNested` analog of the block-wide transaction lemma suite: exact phase recovery, atomicity, monotonicity, freshness, lookup and rule diff --git a/Ix/Theory/Named/Verify/Environment/ConstructorValidation.lean b/Ix/Theory/Named/Verify/Environment/ConstructorValidation.lean index 78ff92f85..375fdd742 100644 --- a/Ix/Theory/Named/Verify/Environment/ConstructorValidation.lean +++ b/Ix/Theory/Named/Verify/Environment/ConstructorValidation.lean @@ -2024,8 +2024,8 @@ evidence, not caller-supplied Theory judgments. /-- Executable fragment on which strict kernel-to-Theory translation has a syntactically unique endpoint. Projections are excluded because the current `TrProj` contract determines their result only up to definitional equality; -projection-bearing inductives remain outside the singleton subset until the -projection milestones establish an exact structural API. -/ +projection-bearing inductives require an exact structural projection API +before they can be included in the singleton subset. -/ def theoryTranslationUnique : Expr → Bool | .bvar _ | .fvar _ @@ -4231,8 +4231,8 @@ namespace VInductDecl The ordinary outer producer deliberately remains unchanged: its successful equation records kernel validation, while this additive wrapper retains the -verified universe-semantic audit introduced by Spec-01D1 and extended by -Spec-02C. The normalized branch intersects the ordinary core decision with +verified audit of constructor-universe comparisons. The normalized branch +intersects the ordinary core decision with the proved project comparison, without making bare `buildNormalizationCandidate` success carry Theory meaning. -/ diff --git a/Ix/Theory/Named/Verify/Environment/ConstructorValidityMatrix.lean b/Ix/Theory/Named/Verify/Environment/ConstructorValidityMatrix.lean index 1692e8052..cc2391518 100644 --- a/Ix/Theory/Named/Verify/Environment/ConstructorValidityMatrix.lean +++ b/Ix/Theory/Named/Verify/Environment/ConstructorValidityMatrix.lean @@ -8,7 +8,7 @@ import Ix.Theory.Named.ConstructorValidityFixtures import Ix.Theory.Named.Verify.Environment.InductiveFixtures /-! -# Spec-05 constructor-validity differential matrix +# Constructor-validity differential matrix The positive half quotes real Lean metadata, runs the ordinary normalization candidate producer, then runs both strengthened constructor gates at their diff --git a/Ix/Theory/Named/Verify/Environment/EliminationFixturesEdges.lean b/Ix/Theory/Named/Verify/Environment/EliminationFixturesEdges.lean index 4f9c11fa7..8eeccab9e 100644 --- a/Ix/Theory/Named/Verify/Environment/EliminationFixturesEdges.lean +++ b/Ix/Theory/Named/Verify/Environment/EliminationFixturesEdges.lean @@ -8,7 +8,7 @@ import Ix.Theory.Named.Verify.Environment.EliminationFixturesCommon open Ix.Theory (VLevel) -/-! Exact Spec-06C `Unit`/`PUnit` and `Empty` edge-shape fixtures. +/-! Exact `Unit`/`PUnit` and `Empty` elimination edge-shape fixtures. `Unit` is a reducible alias for `PUnit` on this Lean revision. The alias has definition metadata but no independent inductive/constructor/recursor records, diff --git a/Ix/Theory/Named/Verify/Environment/EliminationFixturesEq.lean b/Ix/Theory/Named/Verify/Environment/EliminationFixturesEq.lean index 11b50dbb2..69e1bb788 100644 --- a/Ix/Theory/Named/Verify/Environment/EliminationFixturesEq.lean +++ b/Ix/Theory/Named/Verify/Environment/EliminationFixturesEq.lean @@ -6,7 +6,7 @@ Source attribution and revision: Ix/Theory/Named/NOTICE. import Ix.Theory.Named.Verify.Environment.EliminationFixturesCommon -/-! Exact Spec-06B Eq differential fixture. -/ +/-! Exact Eq elimination differential fixture. -/ namespace Ix.Theory.Named.InductiveReplayFixtures open Lean Meta Elab Term diff --git a/Ix/Theory/Named/Verify/Environment/EliminationFixturesEqNat.lean b/Ix/Theory/Named/Verify/Environment/EliminationFixturesEqNat.lean index ba3fbfbc4..c86c38b17 100644 --- a/Ix/Theory/Named/Verify/Environment/EliminationFixturesEqNat.lean +++ b/Ix/Theory/Named/Verify/Environment/EliminationFixturesEqNat.lean @@ -7,4 +7,4 @@ Source attribution and revision: Ix/Theory/Named/NOTICE. import Ix.Theory.Named.Verify.Environment.EliminationFixturesEq import Ix.Theory.Named.Verify.Environment.EliminationFixturesNat -/-! Umbrella for the exact Spec-06A Eq and Nat differential fixtures. -/ +/-! Umbrella for the exact Eq and Nat elimination differential fixtures. -/ diff --git a/Ix/Theory/Named/Verify/Environment/EliminationFixturesNat.lean b/Ix/Theory/Named/Verify/Environment/EliminationFixturesNat.lean index 5da576adb..17fd8314c 100644 --- a/Ix/Theory/Named/Verify/Environment/EliminationFixturesNat.lean +++ b/Ix/Theory/Named/Verify/Environment/EliminationFixturesNat.lean @@ -6,7 +6,7 @@ Source attribution and revision: Ix/Theory/Named/NOTICE. import Ix.Theory.Named.Verify.Environment.EliminationFixturesCommon -/-! Exact Spec-06B Nat never-zero elimination and non-K fixture. -/ +/-! Exact Nat never-zero elimination and non-K fixture. -/ namespace Ix.Theory.Named.InductiveReplayFixtures open Lean Meta Elab Term diff --git a/Ix/Theory/Named/Verify/Environment/EliminationFixturesOrAnd.lean b/Ix/Theory/Named/Verify/Environment/EliminationFixturesOrAnd.lean index 3688cf991..bf50f1151 100644 --- a/Ix/Theory/Named/Verify/Environment/EliminationFixturesOrAnd.lean +++ b/Ix/Theory/Named/Verify/Environment/EliminationFixturesOrAnd.lean @@ -6,7 +6,7 @@ Source attribution and revision: Ix/Theory/Named/NOTICE. import Ix.Theory.Named.Verify.Environment.EliminationFixturesCommon -/-! Exact Spec-06B Or/And differential fixtures. -/ +/-! Exact Or/And elimination differential fixtures. -/ namespace Ix.Theory.Named.InductiveReplayFixtures open Lean Meta Elab Term diff --git a/Ix/Theory/Named/Verify/Environment/EliminationFixturesSmall.lean b/Ix/Theory/Named/Verify/Environment/EliminationFixturesSmall.lean index 309021ddf..88999bce7 100644 --- a/Ix/Theory/Named/Verify/Environment/EliminationFixturesSmall.lean +++ b/Ix/Theory/Named/Verify/Environment/EliminationFixturesSmall.lean @@ -6,7 +6,7 @@ Source attribution and revision: Ix/Theory/Named/NOTICE. import Ix.Theory.Named.Verify.Environment.EliminationFixturesCommon -/-! Exact Spec-06B source-universe small-elimination differential fixture. -/ +/-! Exact source-universe small-elimination differential fixture. -/ namespace Ix.Theory.Named.InductiveReplayFixtures open Lean Meta Elab Term diff --git a/Ix/Theory/Named/Verify/Environment/IndexedVecSemanticReplay.lean b/Ix/Theory/Named/Verify/Environment/IndexedVecSemanticReplay.lean index 1a12306a7..d153fb0f5 100644 --- a/Ix/Theory/Named/Verify/Environment/IndexedVecSemanticReplay.lean +++ b/Ix/Theory/Named/Verify/Environment/IndexedVecSemanticReplay.lean @@ -12,7 +12,7 @@ import Ix.Theory.Named.Verify.Environment.IndexedVecOuterReplay This module connects the exact executable family/`nil`/`cons` candidate produced by `buildNormalizationCandidate` to its Theory generation -certificate and the E1 kernel-environment replay. Every retained candidate +certificate and the kernel-environment replay. Every retained candidate node is interpreted in its exact pre-family or post-family verifier context; the final transaction therefore consumes the certificate projected from the same producer-selected package rather than an independently supplied @@ -3231,7 +3231,7 @@ theorem indexedVecSemantic_aligned_checked : indexedVecSemantic_trEnv'_checked.aligned /- -The semantic assembly, executable producer, and final E1 replay intentionally +The semantic assembly, executable producer, and final environment replay inherit the existing transitional verifier closure. These guards make additions to that closure visible at the public roots of this module. -/ diff --git a/Ix/Theory/Named/Verify/Environment/InductiveFixtures.lean b/Ix/Theory/Named/Verify/Environment/InductiveFixtures.lean index 0d9cb3079..b6efeea82 100644 --- a/Ix/Theory/Named/Verify/Environment/InductiveFixtures.lean +++ b/Ix/Theory/Named/Verify/Environment/InductiveFixtures.lean @@ -7594,11 +7594,9 @@ theorem aliasFormerAlignmentRun : aliasFormerNormalizationCandidate.families.singleton.constructors { aliasFormerNormalizationCandidate.families.singleton.familyType.type.trace.terminalContext with env := aliasFormerCtorCandidateContext.env } = .ok () := by - -- Tier V (Spec-19B, v4.33 reconciliation repair debt): the premerge proof - -- stepped `ConstructorCandidateAlignmentTrace.build` with `rw [build.eq_def]`, - -- which the v4.33 elaborator no longer matches (and eq_def-in-simp loops). - -- The statement is an exact closed checker run and remains true; the - -- stepping proof needs a rework against the new equation-lemma shapes. + -- This exact checker-run proof needs updated equation lemmas for Lean 4.33. + -- `rw [build.eq_def]` no longer matches `ConstructorCandidateAlignmentTrace.build`, + -- and using that equation in `simp` loops. sorry private def aliasFormerStagedPostFamilyInput : diff --git a/Ix/Theory/Named/Verify/Environment/NestedReplay.lean b/Ix/Theory/Named/Verify/Environment/NestedReplay.lean index e06fb7d68..e23eb4c19 100644 --- a/Ix/Theory/Named/Verify/Environment/NestedReplay.lean +++ b/Ix/Theory/Named/Verify/Environment/NestedReplay.lean @@ -9,7 +9,7 @@ import Ix.Theory.Named.Verify.Environment.SingletonParityReplay import Ix.Theory.Named.Verify.Environment.NestedTransformation /-! -# Nested environment replay (Spec-09C) +# Nested environment replay Both ladder fixtures replayed from real stored metadata: the rose tree over the completed `List` environment and the nested-indexed family over diff --git a/Ix/Theory/Named/Verify/Environment/NestedRepresentation.lean b/Ix/Theory/Named/Verify/Environment/NestedRepresentation.lean index 765a88594..84291cdc6 100644 --- a/Ix/Theory/Named/Verify/Environment/NestedRepresentation.lean +++ b/Ix/Theory/Named/Verify/Environment/NestedRepresentation.lean @@ -11,14 +11,11 @@ import Ix.Theory.Named.Meta open Ix.Theory (VLevel) /-! -# Spec-09A: nested-inductive representation audit and decision +# Nested-inductive representation and restoration -This file is the committed design note and the executable metadata probes -for the nested-inductive representation decision. Every claim below is -pinned by a build-failing probe in this file unless it is explicitly marked -as a forward-looking obligation. This checkpoint changes no acceptance -behavior: the probes only observe the implementation and the existing -Theory analyzers. +The executable probes in this module check nested-inductive metadata and +its Theory representation. The remaining semantic proof obligations are +listed separately from those concrete checks. ## Audit: how the implementation represents nested inductives @@ -42,8 +39,7 @@ Theory analyzers. restoration (`Result.restoreNested`), each auxiliary family's recursor is re-added under the name `(mkRecName mainName).appendIndexAfter i` with restored type and rules, and finally every `aux2nested` value - `I Ds` is type-checked (the lean4#14577 escape-hatch check, regression - tested in `Tests/NestedInductive.lean`). The auxiliary families, + `I Ds` is type-checked. The auxiliary families, constructors, and recursor names never enter the final environment. The stored metadata therefore has this shape (probes P1, P2): @@ -74,13 +70,12 @@ new field: `numNested` is implementation metadata recoverable as the number of auxiliary specifications, and parity fixtures pin it per row exactly as they already pin `numNested == 0` for non-nested rows. -Nested support is an additive checked-block artifact (built in Spec-09B/C), -coupling: +Nested support is a checked-block artifact coupling: 1. the flattened block as an ordinary `VInductDecl` — probe P4 shows both target fixtures' flattened blocks are already accepted by the existing `identityBlockGeneration?` machinery, so flattening reuses the complete - Spec-08 block analyzer and generator unchanged; + block analyzer and generator; 2. one auxiliary specification per auxiliary family, in flattened family order: the auxiliary name, the nested value `I Ds` open over the block parameters (the Theory analog of `aux2nested`), and the restored @@ -124,15 +119,15 @@ Rejected alternatives: relation alone would force Verify to re-synthesize them. The artifact's executable coherence checks subsume the relation. -## Obligations recorded for Spec-09B/09C (not claimed here) +## Semantic proof obligations -- 09B: Theory-side flattening and auxiliary-specification validation — +- Theory-side flattening and auxiliary-specification validation — positivity through the existing block analyzer on the flattened block; executable instantiation checks of auxiliary family/constructor types against `I`'s metadata at `Ds`; nearest rejection differentials (ill-typed `Ds` — the lean4#14577 class — wrong specification order, non-matching instantiation). -- 09C: σ as a total Theory function. The spine rule needs a simultaneous +- Restoration by a total Theory function σ. The spine rule needs a simultaneous `instantiateRev`-style multi-substitution for `nparams > 1`: iterating single `VExpr.inst` is wrong once parameter arguments mention bvars. Generation, preservation (typing transport along σ: auxiliary constants @@ -430,7 +425,7 @@ run_meta do /-! ## P3: exact flattening pins The flattened blocks, translated to binder-erased `VExpr` form. These are -the descriptors the Spec-09B transformation must produce. -/ +the descriptors the flattening transformation must produce. -/ def roseFlatFamilies : List (Name × VExpr) := [(``RoseTree, .forallE (.sort (.succ (.param 0))) (.sort (.succ (.param 0)))), @@ -563,10 +558,10 @@ run_meta do /-! ## P5: the restoration substitution σ `restoreV09A` mirrors `ElimNestedInductive.Result.restoreNested` on -`VExpr`. It is probe-local: the Spec-09C artifact path must define the -total Theory version (with a simultaneous parameter substitution once -`nparams > 1` is in scope; the probe fixtures have `nparams ≤ 1`, where -iterated `VExpr.inst` coincides with it). -/ +`VExpr`. It is local to these probes; `NestedInductive.lean` defines the +total restoration function with simultaneous parameter substitution. +The probe fixtures have `nparams ≤ 1`, where iterated `VExpr.inst` agrees +with simultaneous substitution. -/ structure AuxSpec09A where aux : Name diff --git a/Ix/Theory/Named/Verify/Environment/NestedTransformation.lean b/Ix/Theory/Named/Verify/Environment/NestedTransformation.lean index d32c26af8..3968fce5a 100644 --- a/Ix/Theory/Named/Verify/Environment/NestedTransformation.lean +++ b/Ix/Theory/Named/Verify/Environment/NestedTransformation.lean @@ -8,7 +8,7 @@ import Ix.Theory.Named.Verify.Environment.NestedRepresentation import Ix.Theory.Named.NestedInductiveFixtures /-! -# Nested flattening differential (Spec-09B) +# Nested flattening differential Ties the Theory transformation `nestedElimination?` to the implementation: @@ -264,13 +264,13 @@ run_meta do unless !nestedStage3 [] roseV do throwError "noTarget: Theory gate accepted without target metadata" -/-! ## Restoration parity (Spec-09C) +/-! ## Restoration parity The Theory restoration over the flattened block's generation artifacts reproduces Lean's stored metadata exactly: every restored recursor name, universe count, and type, and every rule RHS in the globally flattened order, on all three real fixtures. This runs the product σ -(`NestedBlockChecked.recursors`/`generatedRules`), not the Spec-09A design +(`NestedBlockChecked.recursors`/`generatedRules`), not the representation probe. -/ open Elab in @@ -319,7 +319,7 @@ run_meta do checkRestoreParity "nv" ``NVTree [] 0 [pvecStoredTarget] checkRestoreParity "cu" ``CURose [] 0 [listTarget] -/-! ## Real-output round-trip (Spec-09C) +/-! ## Real-output round-trip Run the port's complete `Environment.addInductive` on a dependency-only kernel environment and compare its entire output — not the ambient diff --git a/Ix/Theory/Named/Verify/Environment/NormalizationMatrix.lean b/Ix/Theory/Named/Verify/Environment/NormalizationMatrix.lean index 4c68f01b9..abedd29a3 100644 --- a/Ix/Theory/Named/Verify/Environment/NormalizationMatrix.lean +++ b/Ix/Theory/Named/Verify/Environment/NormalizationMatrix.lean @@ -12,7 +12,7 @@ open Ix.Theory (VLevel) /-! # Normalization differential matrix -Executable and semantic replay for the Spec-04 matrix. The fixture deliberately +Executable and semantic replay for normalized inductives. The fixture deliberately places reducible aliases at every inductive-analysis boundary, including a Pi-producing alias around a recursive target. This file observes the actual kernel declarations, pins the exact analyzer output and its fuel boundary, and diff --git a/Ix/Theory/Named/Verify/Environment/SingletonParityMatrix.lean b/Ix/Theory/Named/Verify/Environment/SingletonParityMatrix.lean index f60012ae8..73df928be 100644 --- a/Ix/Theory/Named/Verify/Environment/SingletonParityMatrix.lean +++ b/Ix/Theory/Named/Verify/Environment/SingletonParityMatrix.lean @@ -13,7 +13,7 @@ import Ix.Theory.Named.Verify.Environment.EliminationFixturesEdges import Ix.Theory.Named.Verify.Environment.NormalizationMatrix /-! -# Spec-07 complete singleton kernel matrix +# Complete singleton kernel matrix The rows below join the single Theory artifact inventory to Lean's actual `inductInfo`/`ctorInfo`/`recInfo` records. The executable predicate compares @@ -380,7 +380,7 @@ def recursorEliminationRejected07 (info : ConstantInfo) (sourceUvars : Nat) : def typeCollisionEnv07 : VEnv := (VEnv.empty.addConst ``Nat ⟨0, .sort .zero⟩).get (by decide) -/-- The complete Spec-07 negative matrix. Earlier phase-specific fixtures +/-- The complete negative matrix. Individual stage-specific fixtures retain their exact kernel error messages; this table makes their coverage and combined acceptance result executable from one public artifact path. -/ def singletonNegativeRows : List SingletonNegativeRow := diff --git a/Ix/Theory/Named/Verify/Environment/SingletonParityReplay.lean b/Ix/Theory/Named/Verify/Environment/SingletonParityReplay.lean index 944cc6aab..14ef0134e 100644 --- a/Ix/Theory/Named/Verify/Environment/SingletonParityReplay.lean +++ b/Ix/Theory/Named/Verify/Environment/SingletonParityReplay.lean @@ -10,7 +10,7 @@ import Ix.Theory.Named.Verify.Environment.SingletonParityMatrix open Ix.Theory (VLevel) /-! -# Spec-07 environment replay inventory +# Singleton environment replay inventory This module is the sole public environment-facing inventory for singleton parity. A row packages the actual implementation map, the Theory input and @@ -2701,7 +2701,7 @@ def emptyReplay07 : SingletonReplayArtifact where transaction := emptyAddInduct07 aligned := emptyAligned07 -/-- Every fixed Spec-07 positive row, in exactly the same order as the +/-- Every fixed positive row, in exactly the same order as the Theory/kernel parity matrix. Each entry carries an actual `ConstantInfo` transaction and final environment alignment, including the real dependency environments required by `Fin` and `Vector`. -/ @@ -2717,7 +2717,7 @@ def singletonNormalizationReplays : [aliasFormerReplay07, aliasRecReplay07, normalizationMatrixReplay07, annotatedPiReplay07, annotatedParamReplay07] -/-- The sole public Spec-07 environment replay inventory. -/ +/-- The public singleton environment replay inventory. -/ def singletonReplayMatrix : List SingletonReplayArtifact := singletonFixedReplays ++ singletonNormalizationReplays diff --git a/Tests/Ix/Kernel/CheckEnv.lean b/Tests/Ix/Kernel/CheckEnv.lean index ebaf6fc4d..edb6a5e7a 100644 --- a/Tests/Ix/Kernel/CheckEnv.lean +++ b/Tests/Ix/Kernel/CheckEnv.lean @@ -174,7 +174,7 @@ def focusConsts : Array Lean.Name := #[ `Tests.Ix.Compile.Mutual.PropRecMotives.pa_eq, `Tests.Ix.Compile.Mutual.PropRecMotives.pb2_length, `Tests.Ix.Compile.Mutual.PropRecMotives.pa2_eq, - -- Call-site shape coverage (plans/callsite-adapter-generalization.md): + -- Call-site shape coverage: -- direct full application at the Const head, inner-full redex, and -- dead-binder split redex (Collapsed-entry fallback), each in both -- source orders. diff --git a/Tests/Ix/Kernel/CheckTauCeti.lean b/Tests/Ix/Kernel/CheckTauCeti.lean index c2a3a5299..c6f03c040 100644 --- a/Tests/Ix/Kernel/CheckTauCeti.lean +++ b/Tests/Ix/Kernel/CheckTauCeti.lean @@ -52,7 +52,6 @@ def focusConsts : Array Lean.Name := #[ -- (`chebyshevMeasureT_eq_withDensity`, proved by measure ext), so -- every whnf of the stuck cast retries K-like reduction, whose -- `measureT ≟ withDensity …` def-eq must exhaust unfoldings and fail. - -- Optimization work items: plans/kernel-rec-fuel.md. `TauCeti.chebyshevWeightL2Isometry_apply, `TauCeti.chebyshevWeightL2Isometry_symm_apply, diff --git a/Tests/Ix/Kernel/CheckTests.lean b/Tests/Ix/Kernel/CheckTests.lean index 6e22b7481..3d54dcfb5 100644 --- a/Tests/Ix/Kernel/CheckTests.lean +++ b/Tests/Ix/Kernel/CheckTests.lean @@ -134,7 +134,7 @@ def wellScopedTests : TestSeq := | .error (.unknownConst _) => true | _ => false) : Bool)) -/-! ### K0 totalization boundaries -/ +/-! ### Totalization boundaries -/ def totalizationTests : TestSeq := test "universe validation preserves LIFO error order" diff --git a/docs/tc-k0-backedge-audit.md b/docs/kernel-recursion.md similarity index 77% rename from docs/tc-k0-backedge-audit.md rename to docs/kernel-recursion.md index b9cc4aef4..717c80a87 100644 --- a/docs/tc-k0-backedge-audit.md +++ b/docs/kernel-recursion.md @@ -1,14 +1,13 @@ -# Ix.Kernel K0 recursion and back-edge audit +# Kernel recursion and back-edge audit -Snapshot: 2026-07-27. This is the named K0 tick/measure artifact required by -the formal-verification plan. Its production scope is the kernel call graph -rooted at `TcM.checkConst`: `Whnf`, `Infer`, `DefEq`, `Inductive`, and +This audit covers the kernel call graph rooted at `TcM.checkConst`: +`Whnf`, `Infer`, `DefEq`, `Inductive`, and `Check`, together with the shared monad, expression, local-context, union-find, and canonical-checking helpers they call. Ingress, egress, parallel scheduling, and the meta-level trust-audit visitor are not on that call graph. -The audit result is now: +The call graph has: - zero `partial def` declarations in the production kernel call graph; - zero `while` or `repeat` terms in the five recursive kernel modules; @@ -17,8 +16,8 @@ The audit result is now: component that Lean could not accept structurally; - exact equations and fuel-boundary regressions for the totalization seams. -A finite bound is operational evidence, not a soundness proof. K1 and K2 -must still prove the semantic WF properties of the transparent algorithms. +Finite bounds establish termination. Soundness additionally requires proofs +that each operation preserves its semantic invariant. ## Shared runtime fuel @@ -111,12 +110,10 @@ context-suffix closure, and union-find path halving use finite container-size bounds. These replacements preserve the old traversal order where errors are observable; unit regressions pin the LIFO universe-validation order. -The operational behavior change is intentionally narrow: malformed or -adversarial inputs that could previously diverge in an unbounded loop now -return `.maxRecDepth` at the documented cap. Valid-corpus verdict and -headroom parity is the A5 closure gate. Rust and Aiur are deliberately -unchanged in K0; any corresponding hardening is a later transport obligation -after the Ix.Kernel theorem interface is stable. +Inputs that exhaust a bounded loop return `.maxRecDepth` at its documented +cap. Verdict and fuel-headroom regressions check the behavior on valid +corpora. Transporting these contracts to Rust and Aiur is a separate +verification obligation. ## Proof and regression surface @@ -152,29 +149,23 @@ rg -n 'TcM\.tick' \ The first two must return no matches; the last must return exactly the WHNF and DefEq charge sites described above. -## Boundary to K1/K2 +## Semantic proof obligations -K0 establishes total, equation-visible production functions and preserves -their tested operational behavior. It does **not** establish checker -soundness. `Methods.WF`, the conditional WHNF/Infer/DefEq WF theorems, and -the knot-closing induction `(methodsN n).WF` belong to K1/K2. In particular, -the method index closes Lean termination for tick-free cycles, but K1/K2 -must still prove that each field preserves `VerifyWorld`, run support, cache -coherence, and the declared native/inductive oracle boundaries. +Total production functions expose equations for the semantic proofs. +`Methods.WF`, the conditional WHNF/Infer/DefEq WF theorems, and the +knot-closing induction `(methodsN n).WF` require each field to preserve +`VerifyWorld`, run support, cache coherence, and the declared native/inductive +oracle boundaries. The method index establishes termination even for cycles +that consume no runtime fuel; semantic preservation is a further obligation. -## Closure validation +## Validation -The 2026-07-27 K0 closure run passed all of the following: +Build the implementation proofs and run the kernel regressions: -- exact four-statement sorry-frontier check; -- completed (295 roots) and statement (4 roots) trust audits; -- `lake build IxKernelVerify` and the default `lake build`; -- strict `tc-unit` with warnings treated as failures; -- pinned Init/Std stress constants and accelerated-versus-pure differential; -- Init-scale anon verdict parity; -- focused anon differential, full anon/meta roundtrip, and `tc-init` suites; -- Lean4Lean replay and tutorial suites. +```sh +lake build IxKernelVerify +lake test --wfail -- tc-unit +``` -No production source in the Rust kernel or Aiur IxVM was changed. Their -acceptance simulation/refinement work remains downstream of the Ix.Kernel -soundness theorem. +The [kernel verification guide](kernel-verification.md#trust-checks) describes +the exact trust audits and the complete local validation command. diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 1cd6c31b5..b6836b2fd 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -9,6 +9,10 @@ formalization repository. The certified receipt interface and a fragment of production `checkEnvAnon` have relative consistency theorems. Full checker consistency remains open. +The [recursion audit](kernel-recursion.md) explains the production call graph +and fuel bounds. The [context-digest guide](tc-context-digest-collision-boundary.md) +details the collision and suffix-transport assumptions for cache soundness. + ## Connection to the consistency model The named specification and the set model share `Ix.Theory.VLevel`. The @@ -104,16 +108,13 @@ declaration can inhabit it. No axiom-name restriction is needed. The audits traverse checked declaration types and bodies, including inductive constructors. They compare exact axiom sets and record direct origins of -`sorryAx`. Full traversal includes dependencies missed by cached imported -axiom summaries, including implementation assumptions and unfinished -metatheory. All 441 retained named-specification assertions use their original -full dependency graphs as the migration baseline. +`sorryAx`. Full traversal includes implementation assumptions and unfinished +metatheory. The named-specification audit checks 441 assertions against exact +dependency manifests. -The same traversal covers 2,034 kernel manifest roots. Thirteen entries omitted -logical or native dependencies through constructor fields; their corrected -boundaries were checked against freshly compiled pre-migration sources. Direct -dependency lookups are cached within a fixed environment, while each root's -reachable declarations, axioms, and proof-hole origins are computed separately. +The same traversal covers 2,034 kernel manifest roots. Direct dependency +lookups are cached within a fixed environment, while each root's reachable +declarations, axioms, and proof-hole origins are computed separately. `Ix.Kernel.Frontier.Pending` quarantines the remaining explicit metatheory axioms. Completed roots cannot depend on that namespace. Named-specification @@ -183,16 +184,11 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Host commands, receipts, and frozen regression evidence | [Certified checking guide](certified-checking.md) | | Concrete set-theory instance | [Separate model package](../Models/SetTheory/README.md) | -## Provenance and scope - -The kernel, theory, and certified host scaffolding comes from `jcb/monorepo` -at `7b06b754`. The checker namespace is `Ix.Kernel` (formerly `Ix.Tc`), and -verification dependencies are local to Ix. The external `lean4lean` dependency -and its benchmark/test targets have been removed. The new compiler development, -circuit changes, and certificate VM pilot are deferred. +## Source inventory The named development retains 104 source modules, including the inductive -fixtures used by the existing proofs. Original hashes and attribution are -recorded in `Ix/Theory/Named/NOTICE` and `Tests/Theory/NamedManifest.lean`; the -Apache license is preserved alongside the sources. The axiom-audit helper and -direct production-fragment proofs are authored in Ix. +fixtures used by the existing proofs. Source hashes and attribution are in +[`Ix/Theory/Named/NOTICE`](../Ix/Theory/Named/NOTICE) and +[`Tests/Theory/NamedManifest.lean`](../Tests/Theory/NamedManifest.lean); the +Apache license is preserved alongside the sources. The axiom-audit helper +and direct production-fragment proofs are authored in Ix. diff --git a/docs/tc-context-digest-collision-boundary.md b/docs/tc-context-digest-collision-boundary.md index 63e539edc..c847a5a9b 100644 --- a/docs/tc-context-digest-collision-boundary.md +++ b/docs/tc-context-digest-collision-boundary.md @@ -1,7 +1,7 @@ # Ix.Kernel context-digest collision boundary -Snapshot: 2026-07-31. This note records a proof boundary for the K1/K2 cache -soundness argument. +This note records the context-digest assumptions needed for sound reduction, +inference, and definitional-equality caches. ## Two distinct collision obligations @@ -103,20 +103,20 @@ establish a genuinely global specification. The `ContextDigestSpec.execution` premise includes memo hits and requires `ContextDigestSpec.StateValid` for the pre-state. This is load-bearing: a successful lookup in `ctxAddrCache` is not evidence that the -cached address is the digest of the current normalized suffix. Concrete K2 -must define that validity predicate from execution history or a strengthened -state invariant. The interface now requires both `memoValid` and `preserves`, +cached address is the digest of the current normalized suffix. Production +proofs must establish that validity predicate from execution history or a +strengthened state invariant. The interface requires both `memoValid` and `preserves`, so an implementation cannot label an initial state valid while leaving later memoized calls outside the proof domain. -Production now exposes the pure calculation as +Production exposes the pure calculation as `TcM.ctxAddrForLbrUncached`. The exact fast-path, cache-hit, and cache-miss equations prove immediate replay stability and preservation of -`TcM.ContextAddrMemoValid`. This closes the operational memo-mutation part of -K2; it does not yet prove that the pure hash input is the chosen semantic +`TcM.ContextAddrMemoValid`. These establish the operational memo-mutation +contract; they do not prove that the pure hash input is the chosen semantic normalization of the reconciled `KVLCtx`. -## Required K2 discharge +## Production proof obligations Instantiating the finite construction for production must: diff --git a/docs/theory.md b/docs/theory.md index 8c348f3aa..0537cf56f 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -60,8 +60,6 @@ The model's 20 con-leche foundation files derive from revision ported content hashes, and notices are retained and checked. Imported license and notice texts are under `Ix/Theory`; `PORTING.md` explains their scope. -On recompilation in the monorepo, two roots—`Modeled.CheckedCompanions.fixed_old` -and `Modeled.assignment_agrees`—acquire `Classical.choice` and `Quot.sound` in -addition to `propext`, all already permitted baseline axioms. The reviewed -report also accounts for namespace qualification and module ordering. All root -types, constant counts, and module sets otherwise match the source report. +The exact report records each root's checked type, axioms, and dependencies. +For example, `Modeled.CheckedCompanions.fixed_old` and +`Modeled.assignment_agrees` use `propext`, `Classical.choice`, and `Quot.sound`. From 9615301e3a9fa758bda28f875195d5ac5bc635de Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 10:11:42 -0400 Subject: [PATCH 05/63] Add typed Ixon inputs to certified checking commands --- Ix/Certified.lean | 3 +- Ix/Certified/CLI.lean | 167 +++++++++++ Ix/Certified/ClaimCommand.lean | 26 +- Ix/Certified/ClaimMain.lean | 2 +- Ix/Certified/Command.lean | 21 +- Ix/Certified/InputText.lean | 430 ++++++++++++++++++++++++++++ Ix/Certified/Main.lean | 2 +- Ix/Certified/ModelHints.lean | 2 +- Ix/Certified/RequestCodec.lean | 202 +++++++++++++ Ix/Certified/TextCodec.lean | 190 ++++++++++++ Tests/Certified/CLI.lean | 6 +- Tests/Certified/Check.lean | 2 + Tests/Certified/ImportManifest.lean | 10 +- Tests/Certified/Inputs.lean | 262 +++++++++++++++++ docs/certified-checking.md | 140 +++++++-- docs/kernel-verification.md | 7 +- lakefile.lean | 7 +- 17 files changed, 1395 insertions(+), 84 deletions(-) create mode 100644 Ix/Certified/CLI.lean create mode 100644 Ix/Certified/InputText.lean create mode 100644 Ix/Certified/RequestCodec.lean create mode 100644 Ix/Certified/TextCodec.lean create mode 100644 Tests/Certified/Inputs.lean diff --git a/Ix/Certified.lean b/Ix/Certified.lean index 05cb4f0ef..1108d6f0e 100644 --- a/Ix/Certified.lean +++ b/Ix/Certified.lean @@ -3,8 +3,7 @@ Copyright (c) 2026 Argument Computer Corporation. SPDX-License-Identifier: MIT OR Apache-2.0 -/ -import Ix.Certified.Command -import Ix.Certified.ClaimCommand +import Ix.Certified.CLI /-! Source and claim adapters for the explicit certified profile. Their validation receipts connect authenticated Ixon inputs to the set model. diff --git a/Ix/Certified/CLI.lean b/Ix/Certified/CLI.lean new file mode 100644 index 000000000..8bf8d3252 --- /dev/null +++ b/Ix/Certified/CLI.lean @@ -0,0 +1,167 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.InputText + +/-! File input for the certified commands. Binary Ixon is the default; +readable `.ixon` data uses the same Lean request and envelope types. JSON +requests remain an explicit compatibility format for the frozen CLI corpus. +All input formats reach the same certified acceptance functions. +-/ + +namespace Ix.Certified.CLI + +inductive Format where + | binary + | text + | json + deriving BEq, Repr + +structure Options where + requestFormat : Format := .binary + envelopeFormat : Format := .binary + positional : List String := [] + help : Bool := false + deriving Repr + +private def readFormat (envelope : Bool) : String → Except String Format + | "binary" => .ok .binary + | "text" => .ok .text + | "json" => if envelope then .error "envelope format must be binary or text" else .ok .json + | _ => .error (if envelope then "envelope format must be binary or text" + else "request format must be binary, text or json") + +def parseArgs (claim : Bool) (args : List String) : Except String Options := + go args {} false false +where + go : List String → Options → Bool → Bool → Except String Options + | [], options, _, _ => .ok options + | "--" :: rest, options, _, _ => .ok { options with positional := options.positional ++ rest } + | "--help" :: _, options, _, _ + | "-h" :: _, options, _, _ => .ok { options with help := true } + | "--request-format" :: value :: rest, options, requestSeen, envelopeSeen => do + if requestSeen then throw "duplicate --request-format" + let format ← readFormat false value + go rest { options with requestFormat := format } true envelopeSeen + | ["--request-format"], _, _, _ => .error "--request-format requires a value" + | "--envelope-format" :: value :: rest, options, requestSeen, envelopeSeen => do + unless claim do throw "--envelope-format is only available for certified-claim-check" + if envelopeSeen then throw "duplicate --envelope-format" + let format ← readFormat true value + go rest { options with envelopeFormat := format } requestSeen true + | ["--envelope-format"], _, _, _ => .error "--envelope-format requires a value" + | arg :: rest, options, requestSeen, envelopeSeen => do + if arg.startsWith "--request-format=" then + if requestSeen then throw "duplicate --request-format" + let format ← readFormat false (arg.dropPrefix "--request-format=" |>.toString) + go rest { options with requestFormat := format } true envelopeSeen + else if arg.startsWith "--envelope-format=" then + unless claim do throw "--envelope-format is only available for certified-claim-check" + if envelopeSeen then throw "duplicate --envelope-format" + let format ← readFormat true (arg.dropPrefix "--envelope-format=" |>.toString) + go rest { options with envelopeFormat := format } requestSeen true + else if arg.startsWith "-" then throw s!"unknown option: {arg}" + else go rest { options with positional := options.positional ++ [arg] } requestSeen envelopeSeen + +def sourceUsage : String := + "usage: certified-check [--request-format binary|text|json] proof|store SOURCE.ixe REQUEST\n" ++ + " binary (default): Ixon request bytes; text: a typed .ixon data definition\n" ++ + " json: legacy request compatibility" + +def claimUsage : String := + "usage: certified-claim-check [--request-format binary|text|json] " ++ + "[--envelope-format binary|text] SOURCE.ixe ENVELOPE REQUEST\n" ++ + " binary (default): Ixon bytes; text: a typed .ixon data definition\n" ++ + " request and envelope formats are independent; json is legacy request compatibility" + +def readSourceRequest (format : Format) (path : System.FilePath) : IO (Except String Command.Request) := do + match format with + | .binary => return Command.Request.ofIxon (← IO.FS.readBinFile path) + | .text => return Command.Request.ofText (← IO.FS.readFile path) + | .json => + let text ← IO.FS.readFile path + return Lean.Json.parse text >>= Command.readRequest + +def readClaimRequest (format : Format) (path : System.FilePath) : IO (Except String ClaimCommand.Request) := do + match format with + | .binary => return ClaimCommand.Request.ofIxon (← IO.FS.readBinFile path) + | .text => return ClaimCommand.Request.ofText (← IO.FS.readFile path) + | .json => + let text ← IO.FS.readFile path + return Lean.Json.parse text >>= ClaimCommand.readRequest + +/-- Binary input retains the original bytes for authentication. Text input +denotes an envelope whose canonical Ixon encoding is the authenticated object. -/ +def readEnvelope (format : Format) (path : System.FilePath) : IO (Except String ByteArray) := do + match format with + | .binary => return .ok (← IO.FS.readBinFile path) + | .text => return Envelope.toIxon <$> Envelope.ofText (← IO.FS.readFile path) + | .json => return .error "envelope format must be binary or text" + +end Ix.Certified.CLI + +namespace Ix.Certified.Command + +def main (args : List String) : IO UInt32 := do + let parsed := CLI.parseArgs false args + let .ok options := parsed | do + match parsed with + | .error error => IO.eprintln error + | .ok _ => pure () + IO.eprintln CLI.sourceUsage + return 2 + if options.help then IO.println CLI.sourceUsage; return 0 + let [mode, sourcePath, requestPath] := options.positional | do + IO.eprintln CLI.sourceUsage + return 2 + try + let bytes ← IO.FS.readBinFile sourcePath + let parsed ← CLI.readSourceRequest options.requestFormat requestPath + let result := do + let request ← parsed + let parts ← Ixon.deEnvVerifiedLazy bytes + run.{0} mode 6400 parts.env request + match result with + | .error error => IO.eprintln error; return 1 + | .ok () => + IO.println <| (Lean.Json.mkObj [("accepted", Lean.toJson true), ("mode", Lean.toJson mode)]).compress + return 0 + catch error => IO.eprintln error; return 1 + +end Ix.Certified.Command + +namespace Ix.Certified.ClaimCommand + +def main (args : List String) : IO UInt32 := do + let parsed := CLI.parseArgs true args + let .ok options := parsed | do + match parsed with + | .error error => IO.eprintln error + | .ok _ => pure () + IO.eprintln CLI.claimUsage + return 2 + if options.help then IO.println CLI.claimUsage; return 0 + let [sourcePath, envelopePath, requestPath] := options.positional | do + IO.eprintln CLI.claimUsage + return 2 + try + let sourceBytes ← IO.FS.readBinFile sourcePath + let envelope ← CLI.readEnvelope options.envelopeFormat envelopePath + let parsed ← CLI.readClaimRequest options.requestFormat requestPath + let result := do + let request ← parsed + let envelope ← envelope + let parts ← Ixon.deEnvVerifiedLazy sourceBytes + run.{0} 6400 parts.env envelope request + return request.address + match result with + | .error error => IO.eprintln error; return 1 + | .ok address => + IO.println <| (Lean.Json.mkObj [("accepted", Lean.toJson true), + ("address", Lean.toJson (hexOfBytes address.hash))]).compress + return 0 + catch error => IO.eprintln error; return 1 + +end Ix.Certified.ClaimCommand diff --git a/Ix/Certified/ClaimCommand.lean b/Ix/Certified/ClaimCommand.lean index 659c87de0..d62572974 100644 --- a/Ix/Certified/ClaimCommand.lean +++ b/Ix/Certified/ClaimCommand.lean @@ -6,9 +6,10 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Certified.ClaimSuggest import Ix.Kernel.CertifiedClaims -/-! Generic command for the versioned public claim protocol. Public bytes -and their expected address are inputs; JSON supplies only untrusted search -hints. Each successful command ends in the certified TcM claim checker. -/ +/-! Typed checking for the versioned public claim protocol. Public bytes and +their expected address are inputs; requests supply untrusted search hints. +The JSON reader supports legacy requests. Command-line input formats are in +`Ix.Certified.CLI`. Each successful run ends in the certified TcM claim checker. -/ namespace Ix.Certified.ClaimCommand @@ -95,23 +96,4 @@ theorem run_meaning {fuel : Nat} {source : Ixon.Env} {bytes : ByteArray} {reques obtain ⟨receipt, _, ha, hb, hm⟩ := Kernel.accepted_tc_claim_meaning hw exact ⟨receipt, ha, hb, hm⟩ -def main (args : List String) : IO UInt32 := do - let [sourcePath, envelopePath, requestPath] := args | do - IO.eprintln "usage: certified-claim-check SOURCE.ixe ENVELOPE.bin REQUEST.json" - return 2 - let sourceBytes ← IO.FS.readBinFile sourcePath - let envelope ← IO.FS.readBinFile envelopePath - let requestText ← IO.FS.readFile requestPath - let result := do - let request ← readRequest (← Lean.Json.parse requestText) - let parts ← Ixon.deEnvVerifiedLazy sourceBytes - run.{0} 6400 parts.env envelope request - return request.address - match result with - | .error error => IO.eprintln error; return 1 - | .ok address => - IO.println <| (Lean.Json.mkObj [("accepted", Lean.toJson true), - ("address", Lean.toJson (hexOfBytes address.hash))]).compress - return 0 - end Ix.Certified.ClaimCommand diff --git a/Ix/Certified/ClaimMain.lean b/Ix/Certified/ClaimMain.lean index 2028b1f52..feca20f5c 100644 --- a/Ix/Certified/ClaimMain.lean +++ b/Ix/Certified/ClaimMain.lean @@ -3,6 +3,6 @@ Copyright (c) 2026 Argument Computer Corporation. SPDX-License-Identifier: MIT OR Apache-2.0 -/ -import Ix.Certified.ClaimCommand +import Ix.Certified.CLI def main := Ix.Certified.ClaimCommand.main diff --git a/Ix/Certified/Command.lean b/Ix/Certified/Command.lean index 0ad2ba24c..177762ea0 100644 --- a/Ix/Certified/Command.lean +++ b/Ix/Certified/Command.lean @@ -6,10 +6,11 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Certified.Suggest import Ix.Kernel.Certified -/-! Generic source-file driver for the certified profile. The JSON request +/-! Typed source checking for the certified profile. The request selects the expected target/subjects, primitive signature and finite source closure. Witness search is untrusted and always followed by certified TcM -validation. There is no inference-only or unchecked fallback command. -/ +validation. The JSON reader supports legacy request files; the command-line +input formats are defined in `Ix.Certified.CLI`. -/ namespace Ix.Certified.Command @@ -77,20 +78,4 @@ theorem run_success {mode : String} {fuel : Nat} {source : Ixon.Env} {request : · simp [run, hs, hw, ha] at h · simp [run, hp, hs] at h -def main (args : List String) : IO UInt32 := do - let [mode, sourcePath, requestPath] := args | do - IO.eprintln "usage: certified-check proof|store SOURCE.ixe REQUEST.json" - return 2 - let bytes ← IO.FS.readBinFile sourcePath - let requestText ← IO.FS.readFile requestPath - let result := do - let request ← readRequest (← Lean.Json.parse requestText) - let parts ← Ixon.deEnvVerifiedLazy bytes - run.{0} mode 6400 parts.env request - match result with - | .error error => IO.eprintln error; return 1 - | .ok () => - IO.println <| (Lean.Json.mkObj [("accepted", Lean.toJson true), ("mode", Lean.toJson mode)]).compress - return 0 - end Ix.Certified.Command diff --git a/Ix/Certified/InputText.lean b/Ix/Certified/InputText.lean new file mode 100644 index 000000000..fe9bc165c --- /dev/null +++ b/Ix/Certified/InputText.lean @@ -0,0 +1,430 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.TextCodec + +/-! Readable Ixon data values for certified requests and envelopes. The codecs +cover all claim variants, model hints, and selective-revelation fields. Each +constructor is decoded to its existing Lean type before witness search. +-/ + +namespace Ix.Certified.Text + +instance : Codec Ix.DefKind where + type := ref "Ix.DefKind" + encode + | .defn => + ref "Ix.DefKind.defn" + | .opaq => + ref "Ix.DefKind.opaq" + | .thm => + ref "Ix.DefKind.thm" + decode term := do + let (name, args) ← constructor term + match name with + | "Ix.DefKind.defn" => do + arity name 0 args + return .defn + | "Ix.DefKind.opaq" => do + arity name 0 args + return .opaq + | "Ix.DefKind.thm" => do + arity name 0 args + return .thm + | _ => throw "expected a Ix.DefKind constructor" + +instance : Codec Ix.DefinitionSafety where + type := ref "Ix.DefinitionSafety" + encode + | .safe => + ref "Ix.DefinitionSafety.safe" + | .unsaf => + ref "Ix.DefinitionSafety.unsaf" + | .part => + ref "Ix.DefinitionSafety.part" + decode term := do + let (name, args) ← constructor term + match name with + | "Ix.DefinitionSafety.safe" => do + arity name 0 args + return .safe + | "Ix.DefinitionSafety.unsaf" => do + arity name 0 args + return .unsaf + | "Ix.DefinitionSafety.part" => do + arity name 0 args + return .part + | _ => throw "expected a Ix.DefinitionSafety constructor" + +instance : Codec Ix.QuotKind where + type := ref "Ix.QuotKind" + encode + | .type => + ref "Ix.QuotKind.type" + | .ctor => + ref "Ix.QuotKind.ctor" + | .lift => + ref "Ix.QuotKind.lift" + | .ind => + ref "Ix.QuotKind.ind" + decode term := do + let (name, args) ← constructor term + match name with + | "Ix.QuotKind.type" => do + arity name 0 args + return .type + | "Ix.QuotKind.ctor" => do + arity name 0 args + return .ctor + | "Ix.QuotKind.lift" => do + arity name 0 args + return .lift + | "Ix.QuotKind.ind" => do + arity name 0 args + return .ind + | _ => throw "expected a Ix.QuotKind constructor" + +instance : Codec Ix.RevealConstructorInfo where + type := ref "Ix.RevealConstructorInfo" + encode value := ctor "Ix.RevealConstructorInfo.mk" #[ + encode value.isUnsafe, encode value.lvls, encode value.cidx, encode value.params, + encode value.fields, encode value.typ] + decode term := do + let args ← arguments "Ix.RevealConstructorInfo.mk" 6 term + return ⟨ + ← decode args[0]!, ← decode args[1]!, ← decode args[2]!, ← decode args[3]!, + ← decode args[4]!, ← decode args[5]!⟩ + +instance : Codec Ix.RevealRecursorRule where + type := ref "Ix.RevealRecursorRule" + encode value := ctor "Ix.RevealRecursorRule.mk" #[ + encode value.ruleIdx, encode value.fields, encode value.rhs] + decode term := do + let args ← arguments "Ix.RevealRecursorRule.mk" 3 term + return ⟨ + ← decode args[0]!, ← decode args[1]!, ← decode args[2]!⟩ + +instance : Codec Ix.RevealMutConstInfo where + type := ref "Ix.RevealMutConstInfo" + encode + | .defn kind safety lvls typ value => + ctor "Ix.RevealMutConstInfo.defn" #[ + encode kind, encode safety, encode lvls, encode typ, encode value] + | .indc isUnsafe lvls params indices typ ctors => + ctor "Ix.RevealMutConstInfo.indc" #[ + encode isUnsafe, encode lvls, encode params, encode indices, encode typ, encode ctors] + | .recr k isUnsafe lvls params indices motives minors typ rules => + ctor "Ix.RevealMutConstInfo.recr" #[ + encode k, encode isUnsafe, encode lvls, encode params, encode indices, encode motives, + encode minors, encode typ, encode rules] + decode term := do + let (name, args) ← constructor term + match name with + | "Ix.RevealMutConstInfo.defn" => do + arity name 5 args + return .defn + (← decode args[0]!) (← decode args[1]!) (← decode args[2]!) (← decode args[3]!) (← decode args[4]!) + | "Ix.RevealMutConstInfo.indc" => do + arity name 6 args + return .indc + (← decode args[0]!) (← decode args[1]!) (← decode args[2]!) (← decode args[3]!) (← decode args[4]!) (← decode args[5]!) + | "Ix.RevealMutConstInfo.recr" => do + arity name 9 args + return .recr + (← decode args[0]!) (← decode args[1]!) (← decode args[2]!) (← decode args[3]!) (← decode args[4]!) (← decode args[5]!) (← decode args[6]!) (← decode args[7]!) (← decode args[8]!) + | _ => throw "expected a Ix.RevealMutConstInfo constructor" + +instance : Codec Ix.RevealConstantInfo where + type := ref "Ix.RevealConstantInfo" + encode + | .defn kind safety lvls typ value => + ctor "Ix.RevealConstantInfo.defn" #[ + encode kind, encode safety, encode lvls, encode typ, encode value] + | .recr k isUnsafe lvls params indices motives minors typ rules => + ctor "Ix.RevealConstantInfo.recr" #[ + encode k, encode isUnsafe, encode lvls, encode params, encode indices, encode motives, + encode minors, encode typ, encode rules] + | .axio isUnsafe lvls typ => + ctor "Ix.RevealConstantInfo.axio" #[ + encode isUnsafe, encode lvls, encode typ] + | .quot kind lvls typ => + ctor "Ix.RevealConstantInfo.quot" #[ + encode kind, encode lvls, encode typ] + | .cPrj idx cidx block => + ctor "Ix.RevealConstantInfo.cPrj" #[ + encode idx, encode cidx, encode block] + | .rPrj idx block => + ctor "Ix.RevealConstantInfo.rPrj" #[ + encode idx, encode block] + | .iPrj idx block => + ctor "Ix.RevealConstantInfo.iPrj" #[ + encode idx, encode block] + | .dPrj idx block => + ctor "Ix.RevealConstantInfo.dPrj" #[ + encode idx, encode block] + | .muts components => + ctor "Ix.RevealConstantInfo.muts" #[ + encode components] + decode term := do + let (name, args) ← constructor term + match name with + | "Ix.RevealConstantInfo.defn" => do + arity name 5 args + return .defn + (← decode args[0]!) (← decode args[1]!) (← decode args[2]!) (← decode args[3]!) (← decode args[4]!) + | "Ix.RevealConstantInfo.recr" => do + arity name 9 args + return .recr + (← decode args[0]!) (← decode args[1]!) (← decode args[2]!) (← decode args[3]!) (← decode args[4]!) (← decode args[5]!) (← decode args[6]!) (← decode args[7]!) (← decode args[8]!) + | "Ix.RevealConstantInfo.axio" => do + arity name 3 args + return .axio + (← decode args[0]!) (← decode args[1]!) (← decode args[2]!) + | "Ix.RevealConstantInfo.quot" => do + arity name 3 args + return .quot + (← decode args[0]!) (← decode args[1]!) (← decode args[2]!) + | "Ix.RevealConstantInfo.cPrj" => do + arity name 3 args + return .cPrj + (← decode args[0]!) (← decode args[1]!) (← decode args[2]!) + | "Ix.RevealConstantInfo.rPrj" => do + arity name 2 args + return .rPrj + (← decode args[0]!) (← decode args[1]!) + | "Ix.RevealConstantInfo.iPrj" => do + arity name 2 args + return .iPrj + (← decode args[0]!) (← decode args[1]!) + | "Ix.RevealConstantInfo.dPrj" => do + arity name 2 args + return .dPrj + (← decode args[0]!) (← decode args[1]!) + | "Ix.RevealConstantInfo.muts" => do + arity name 1 args + return .muts + (← decode args[0]!) + | _ => throw "expected a Ix.RevealConstantInfo constructor" + +instance : Codec Ix.Claim where + type := ref "Ix.Claim" + encode + | .eval input output assumptions => + ctor "Ix.Claim.eval" #[ + encode input, encode output, encode assumptions] + | .check const assumptions => + ctor "Ix.Claim.check" #[ + encode const, encode assumptions] + | .checkEnv root assumptions => + ctor "Ix.Claim.checkEnv" #[ + encode root, encode assumptions] + | .reveal comm info => + ctor "Ix.Claim.reveal" #[ + encode comm, encode info] + | .contains tree const => + ctor "Ix.Claim.contains" #[ + encode tree, encode const] + | .catalog members content assumptions => + ctor "Ix.Claim.catalog" #[ + encode members, encode content, encode assumptions] + decode term := do + let (name, args) ← constructor term + match name with + | "Ix.Claim.eval" => do + arity name 3 args + return .eval + (← decode args[0]!) (← decode args[1]!) (← decode args[2]!) + | "Ix.Claim.check" => do + arity name 2 args + return .check + (← decode args[0]!) (← decode args[1]!) + | "Ix.Claim.checkEnv" => do + arity name 2 args + return .checkEnv + (← decode args[0]!) (← decode args[1]!) + | "Ix.Claim.reveal" => do + arity name 2 args + return .reveal + (← decode args[0]!) (← decode args[1]!) + | "Ix.Claim.contains" => do + arity name 2 args + return .contains + (← decode args[0]!) (← decode args[1]!) + | "Ix.Claim.catalog" => do + arity name 3 args + return .catalog + (← decode args[0]!) (← decode args[1]!) (← decode args[2]!) + | _ => throw "expected a Ix.Claim constructor" + +instance : Codec Ix.Certified.Profile where + type := ref "Ix.Certified.Profile" + encode value := ctor "Ix.Certified.Profile.mk" #[ + encode value.falseType, encode value.falseElim, encode value.natType] + decode term := do + let args ← arguments "Ix.Certified.Profile.mk" 3 term + return ⟨ + ← decode args[0]!, ← decode args[1]!, ← decode args[2]!⟩ + +instance : Codec Ix.Certified.Protocol where + type := ref "Ix.Certified.Protocol" + encode value := ctor "Ix.Certified.Protocol.mk" #[ + encode value.format, encode value.codec, encode value.checker, encode value.policy, + encode value.aggregation] + decode term := do + let args ← arguments "Ix.Certified.Protocol.mk" 5 term + return ⟨ + ← decode args[0]!, ← decode args[1]!, ← decode args[2]!, ← decode args[3]!, + ← decode args[4]!⟩ + +instance : Codec Ix.Certified.Envelope where + type := ref "Ix.Certified.Envelope" + encode value := ctor "Ix.Certified.Envelope.mk" #[ + encode value.protocol, encode value.profile, encode value.claim, encode value.logicalAxioms] + decode term := do + let args ← arguments "Ix.Certified.Envelope.mk" 4 term + return ⟨ + ← decode args[0]!, ← decode args[1]!, ← decode args[2]!, ← decode args[3]!⟩ + +instance : Codec Ix.Certified.InputSelection where + type := ref "Ix.Certified.InputSelection" + encode value := ctor "Ix.Certified.InputSelection.mk" #[ + encode value.objects, encode value.naturals] + decode term := do + let args ← arguments "Ix.Certified.InputSelection.mk" 2 term + return ⟨ + ← decode args[0]!, ← decode args[1]!⟩ + +instance : Codec Ix.Certified.ModelProofHint where + type := ref "Ix.Certified.ModelProofHint" + encode value := ctor "Ix.Certified.ModelProofHint.mk" #[ + encode value.equality, encode value.reflexivity, encode value.eliminator, encode value.proof] + decode term := do + let args ← arguments "Ix.Certified.ModelProofHint.mk" 4 term + return ⟨ + ← decode args[0]!, ← decode args[1]!, ← decode args[2]!, ← decode args[3]!⟩ + +instance : Codec Ix.Certified.ModelRuleHints where + type := ref "Ix.Certified.ModelRuleHints" + encode value := ctor "Ix.Certified.ModelRuleHints.mk" #[ + encode value.owner, encode value.proofs] + decode term := do + let args ← arguments "Ix.Certified.ModelRuleHints.mk" 2 term + return ⟨ + ← decode args[0]!, ← decode args[1]!⟩ + +instance : Codec Ix.Certified.ModelHint where + type := ref "Ix.Certified.ModelHint" + encode value := ctor "Ix.Certified.ModelHint.mk" #[ + encode value.source, encode value.recursors, encode value.targets, encode value.proofs] + decode term := do + let args ← arguments "Ix.Certified.ModelHint.mk" 4 term + return ⟨ + ← decode args[0]!, ← decode args[1]!, ← decode args[2]!, ← decode args[3]!⟩ + +instance : Codec Ix.Certified.Command.Request where + type := ref "Ix.Certified.Command.Request" + encode value := ctor "Ix.Certified.Command.Request.mk" #[ + encode value.profile, encode value.target, encode value.subjects, encode value.selection, + encode value.models] + decode term := do + let args ← arguments "Ix.Certified.Command.Request.mk" 5 term + return ⟨ + ← decode args[0]!, ← decode args[1]!, ← decode args[2]!, ← decode args[3]!, + ← decode args[4]!⟩ + +instance : Codec Ix.Certified.LeafHint where + type := ref "Ix.Certified.LeafHint" + encode value := ctor "Ix.Certified.LeafHint.mk" #[ + encode value.claim, encode value.subjects, encode value.frontierTree] + decode term := do + let args ← arguments "Ix.Certified.LeafHint.mk" 3 term + return ⟨ + ← decode args[0]!, ← decode args[1]!, ← decode args[2]!⟩ + +instance : Codec Ix.Certified.LogicalHint where + type := ref "Ix.Certified.LogicalHint" + encode value := ctor "Ix.Certified.LogicalHint.mk" #[ + encode value.selection, encode value.leaves, encode value.subjects, encode value.members, + encode value.frontierTree, encode value.axiomTree, encode value.models] + decode term := do + let args ← arguments "Ix.Certified.LogicalHint.mk" 7 term + return ⟨ + ← decode args[0]!, ← decode args[1]!, ← decode args[2]!, ← decode args[3]!, + ← decode args[4]!, ← decode args[5]!, ← decode args[6]!⟩ + +instance : Codec Ixon.Comm where + type := ref "Ixon.Comm" + encode value := ctor "Ixon.Comm.mk" #[ + encode value.secret, encode value.payload] + decode term := do + let args ← arguments "Ixon.Comm.mk" 2 term + return ⟨ + ← decode args[0]!, ← decode args[1]!⟩ + +instance : Codec Ix.Certified.RevealWitness where + type := ref "Ix.Certified.RevealWitness" + encode value := ctor "Ix.Certified.RevealWitness.mk" #[ + encode value.opening] + decode term := do + let args ← arguments "Ix.Certified.RevealWitness.mk" 1 term + return ⟨ + ← decode args[0]!⟩ + +instance : Codec Ix.Certified.ClaimCommand.Hint where + type := ref "Ix.Certified.ClaimCommand.Hint" + encode + | .logical hint => + ctor "Ix.Certified.ClaimCommand.Hint.logical" #[ + encode hint] + | .contains tree => + ctor "Ix.Certified.ClaimCommand.Hint.contains" #[ + encode tree] + | .reveal witness => + ctor "Ix.Certified.ClaimCommand.Hint.reveal" #[ + encode witness] + decode term := do + let (name, args) ← constructor term + match name with + | "Ix.Certified.ClaimCommand.Hint.logical" => do + arity name 1 args + return .logical + (← decode args[0]!) + | "Ix.Certified.ClaimCommand.Hint.contains" => do + arity name 1 args + return .contains + (← decode args[0]!) + | "Ix.Certified.ClaimCommand.Hint.reveal" => do + arity name 1 args + return .reveal + (← decode args[0]!) + | _ => throw "expected a Ix.Certified.ClaimCommand.Hint constructor" + +instance : Codec Ix.Certified.ClaimCommand.Request where + type := ref "Ix.Certified.ClaimCommand.Request" + encode value := ctor "Ix.Certified.ClaimCommand.Request.mk" #[ + encode value.address, encode value.hint] + decode term := do + let args ← arguments "Ix.Certified.ClaimCommand.Request.mk" 2 term + return ⟨ + ← decode args[0]!, ← decode args[1]!⟩ + +end Ix.Certified.Text + +namespace Ix.Certified + +def Command.Request.toText (request : Command.Request) : String := Text.write "request" request + +def Command.Request.ofText (text : String) : Except String Command.Request := Text.read text + +def ClaimCommand.Request.toText (request : ClaimCommand.Request) : String := Text.write "request" request + +def ClaimCommand.Request.ofText (text : String) : Except String ClaimCommand.Request := Text.read text + +def Envelope.toText (envelope : Envelope) : String := Text.write "envelope" envelope + +def Envelope.ofText (text : String) : Except String Envelope := Text.read text + +end Ix.Certified + diff --git a/Ix/Certified/Main.lean b/Ix/Certified/Main.lean index 0570b7cec..3c14b8ca4 100644 --- a/Ix/Certified/Main.lean +++ b/Ix/Certified/Main.lean @@ -3,6 +3,6 @@ Copyright (c) 2026 Argument Computer Corporation. SPDX-License-Identifier: MIT OR Apache-2.0 -/ -import Ix.Certified.Command +import Ix.Certified.CLI def main := Ix.Certified.Command.main diff --git a/Ix/Certified/ModelHints.lean b/Ix/Certified/ModelHints.lean index 4d65dec82..14f5d8c66 100644 --- a/Ix/Certified/ModelHints.lean +++ b/Ix/Certified/ModelHints.lean @@ -7,7 +7,7 @@ import Ix.Certified.Bytes import Ix.Theory.Certificate.Modeled /-! Optional untrusted model-package hints refer to actual stored objects. -No mathematical declaration is imported from JSON: model definitions and +No mathematical declaration is supplied by a hint: model definitions and equation proofs come from the selected authenticated source store and are admitted before the original block by the ordinary certified checker. -/ diff --git a/Ix/Certified/RequestCodec.lean b/Ix/Certified/RequestCodec.lean new file mode 100644 index 000000000..98f6888aa --- /dev/null +++ b/Ix/Certified/RequestCodec.lean @@ -0,0 +1,202 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.Command +import Ix.Certified.ClaimCommand + +/-! Ixon serialization for the typed certified requests. Request frames carry +their own version and distinguish source requests from claim requests. Public +envelopes keep their existing Ixon representation and content addresses. +The public decoders consume the complete input and require canonical bytes. +-/ + +namespace Ix.Certified + +namespace RequestCodec + +open Ixon + +def magic : ByteArray := "IX-CERTIFIED-REQUEST".toUTF8 ++ ⟨#[0]⟩ + +/-- Bound transport decoding before allocating collections or parsing claims. -/ +def maxBytes : Nat := 1 <<< 20 + +def putHeader (kind : UInt8) : PutM Unit := do + putBytes magic + putTag0 ⟨1⟩ + putU8 kind + +def getHeader (kind : UInt8) : GetM Unit := do + if (← getBytes magic.size) != magic then throw "invalid certified request magic" + if (← getTag0).size != 1 then throw "unsupported certified request version" + if (← getU8) != kind then throw "incorrect certified request kind" + +def putList (putValue : α → PutM Unit) (values : List α) : PutM Unit := do + putTag0 ⟨values.length.toUInt64⟩ + values.forM putValue + +/-- Every item used by this codec consumes at least one byte. Check the count +against the remaining input before allocating the collection. -/ +def getList (getValue : GetM α) : GetM (List α) := do + let count := (← getTag0).size.toNat + let state ← get + if count > state.bytes.size - state.idx then throw "certified request list exceeds remaining input" + let mut values := [] + for _ in [:count] do values := (← getValue) :: values + return values.reverse + +def putOption (putValue : α → PutM Unit) : Option α → PutM Unit + | none => putU8 0 + | some value => do putU8 1; putValue value + +def getOption (getValue : GetM α) : GetM (Option α) := do + match ← getU8 with + | 0 => return none + | 1 => return some (← getValue) + | _ => throw "invalid certified request option tag" + +def putBlob (bytes : ByteArray) : PutM Unit := do + putTag0 ⟨bytes.size.toUInt64⟩ + putBytes bytes + +def getBlob : GetM ByteArray := do + getBytes (← getTag0).size.toNat + +def putProfile (profile : Profile) : PutM Unit := do + Serialize.put profile.falseType + Serialize.put profile.falseElim + putOption Serialize.put profile.natType + +def getProfile : GetM Profile := + return ⟨← Serialize.get, ← Serialize.get, ← getOption Serialize.get⟩ + +def putSelection (selection : InputSelection) : PutM Unit := do + putList Serialize.put selection.objects + putList Serialize.put selection.naturals + +def getSelection : GetM InputSelection := + return ⟨← getList Serialize.get, ← getList Serialize.get⟩ + +def putModelProof (hint : ModelProofHint) : PutM Unit := do + Serialize.put hint.equality + Serialize.put hint.reflexivity + Serialize.put hint.eliminator + Serialize.put hint.proof + +def getModelProof : GetM ModelProofHint := + return ⟨← Serialize.get, ← Serialize.get, ← Serialize.get, ← Serialize.get⟩ + +def putModelRules (hint : ModelRuleHints) : PutM Unit := do + Serialize.put hint.owner + putList (putOption putModelProof) hint.proofs + +def getModelRules : GetM ModelRuleHints := + return ⟨← Serialize.get, ← getList (getOption getModelProof)⟩ + +def putModel (hint : ModelHint) : PutM Unit := do + Serialize.put hint.source + putList Serialize.put hint.recursors + putList Serialize.put hint.targets + putList putModelRules hint.proofs + +def getModel : GetM ModelHint := + return ⟨← Serialize.get, ← getList Serialize.get, ← getList Serialize.get, + ← getList getModelRules⟩ + +def putSourceRequest (request : Command.Request) : PutM Unit := do + putHeader 0 + putProfile request.profile + Serialize.put request.target + putList Serialize.put request.subjects + putSelection request.selection + putList putModel request.models + +def getSourceRequest : GetM Command.Request := do + getHeader 0 + return ⟨← getProfile, ← Serialize.get, ← getList Serialize.get, + ← getSelection, ← getList getModel⟩ + +def putLeaf (hint : LeafHint) : PutM Unit := do + Ix.Claim.put hint.claim + putOption putBlob hint.subjects + putOption putBlob hint.frontierTree + +def getLeaf : GetM LeafHint := + return ⟨← Ix.Claim.get, ← getOption getBlob, ← getOption getBlob⟩ + +def putLogical (hint : LogicalHint) : PutM Unit := do + putSelection hint.selection + putList putLeaf hint.leaves + putOption putBlob hint.subjects + putOption putBlob hint.members + putOption putBlob hint.frontierTree + putOption putBlob hint.axiomTree + putList putModel hint.models + +def getLogical : GetM LogicalHint := + return ⟨← getSelection, ← getList getLeaf, ← getOption getBlob, + ← getOption getBlob, ← getOption getBlob, ← getOption getBlob, + ← getList getModel⟩ + +def putHint : ClaimCommand.Hint → PutM Unit + | .logical hint => do putU8 0; putLogical hint + | .contains tree => do putU8 1; putBlob tree + | .reveal witness => do + putU8 2 + Serialize.put witness.opening.secret + Serialize.put witness.opening.payload + +def getHint : GetM ClaimCommand.Hint := do + match ← getU8 with + | 0 => return .logical (← getLogical) + | 1 => return .contains (← getBlob) + | 2 => return .reveal ⟨⟨← Serialize.get, ← Serialize.get⟩⟩ + | _ => throw "invalid certified claim hint tag" + +def putClaimRequest (request : ClaimCommand.Request) : PutM Unit := do + putHeader 1 + Serialize.put request.address + putHint request.hint + +def getClaimRequest : GetM ClaimCommand.Request := do + getHeader 1 + return ⟨← Serialize.get, ← getHint⟩ + +def decode (getValue : GetM α) (putValue : α → PutM Unit) (bytes : ByteArray) : Except String α := do + if bytes.size > maxBytes then throw "certified input exceeds the 1 MiB transport limit" + let value ← runGetExact getValue bytes + if runPut (putValue value) != bytes then throw "noncanonical certified input bytes" + return value + +end RequestCodec + +instance : Ixon.Serialize Command.Request where + put := RequestCodec.putSourceRequest + get := RequestCodec.getSourceRequest + +instance : Ixon.Serialize ClaimCommand.Request where + put := RequestCodec.putClaimRequest + get := RequestCodec.getClaimRequest + +instance : Ixon.Serialize Envelope where + put := putEnvelope + get := getEnvelope + +def Command.Request.toIxon (request : Command.Request) : ByteArray := Ixon.ser request + +def Command.Request.ofIxon (bytes : ByteArray) : Except String Command.Request := + RequestCodec.decode RequestCodec.getSourceRequest RequestCodec.putSourceRequest bytes + +def ClaimCommand.Request.toIxon (request : ClaimCommand.Request) : ByteArray := Ixon.ser request + +def ClaimCommand.Request.ofIxon (bytes : ByteArray) : Except String ClaimCommand.Request := + RequestCodec.decode RequestCodec.getClaimRequest RequestCodec.putClaimRequest bytes + +def Envelope.toIxon (envelope : Envelope) : ByteArray := envelopeBytes envelope + +def Envelope.ofIxon (bytes : ByteArray) : Except String Envelope := + RequestCodec.decode getEnvelope putEnvelope bytes + +end Ix.Certified diff --git a/Ix/Certified/TextCodec.lean b/Ix/Certified/TextCodec.lean new file mode 100644 index 000000000..5e024391b --- /dev/null +++ b/Ix/Certified/TextCodec.lean @@ -0,0 +1,190 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.RequestCodec +import Ix.IxonSyntax + +/-! Data codecs using the existing Ixon constructor-expression syntax. +Readers recognize concrete constructors and literal helpers. They do not +evaluate user code, resolve imports, or run Lean elaboration. +-/ + +namespace Ix.Certified.Text + +open Ixon.Syntax + +/-- A convenient literal helper for Lean authors. Text input checks the same +hexadecimal form and rejects invalid addresses before constructing a value. -/ +def address! (hex : String) : Address := (Address.fromString hex).get! + +/-- A byte-string literal for Lean authors and Ixon constructor expressions. -/ +def bytes! (hex : String) : ByteArray := (bytesOfHex hex).get! + +def ref (name : String) : Term := + .ref { name := some { parts := (name.splitOn ".").toArray.map NameComponent.str } } + +def ctor (name : String) (args : Array Term := #[]) : Term := + if args.isEmpty then ref name else .app (ref name) args {} + +class Codec (α : Type) where + type : Term + encode : α → Term + decode : Term → Except String α + +def typeOf (α : Type) [Codec α] : Term := Codec.type (α := α) +def encode [Codec α] (value : α) : Term := Codec.encode value +def decode [Codec α] (term : Term) : Except String α := Codec.decode term + +def expectType [Codec α] (term : Term) : Except String Unit := do + if printTerm term != printTerm (typeOf α) then + throw s!"expected Ixon type {printTerm (typeOf α)}" + +/-- Parenthesized application spines have the same constructor arguments. -/ +def spine : Term → Term × Array Term + | .app head args _ => + let (head, previous) := spine head + (head, previous ++ args) + | term => (term, #[]) + +def constructor (term : Term) : Except String (String × Array Term) := do + let (head, args) := spine term + let .ref reference := head | throw "expected an Ixon data constructor" + let some name := reference.name | throw "expected a named Ixon data constructor" + if reference.hash.isSome || reference.levels.isSome then + throw "data constructors must use unpinned names with default universe levels" + let parts ← name.parts.toList.mapM fun + | .str value => pure value + | .num _ => throw "invalid data constructor name" + return (String.intercalate "." parts, args) + +def arguments (name : String) (count : Nat) (term : Term) : Except String (Array Term) := do + let (actual, args) ← constructor term + if actual != name || args.size != count then + throw s!"expected {name} with {count} arguments" + return args + +def arity (name : String) (count : Nat) (args : Array Term) : Except String Unit := do + if args.size != count then throw s!"expected {count} arguments to {name}" + +instance : Codec Address where + type := ref "Address" + encode value := ctor "Ix.Certified.Text.address!" #[.strLit (hexOfBytes value.hash) {}] + decode term := do + let args ← arguments "Ix.Certified.Text.address!" 1 term + let .strLit hex _ := args[0]! | throw "expected a hexadecimal address string" + match Address.fromString hex with + | some value => return value + | none => throw "expected a 32-byte hexadecimal address" + +instance : Codec ByteArray where + type := ref "ByteArray" + encode value := ctor "Ix.Certified.Text.bytes!" #[.strLit (hexOfBytes value) {}] + decode term := do + let args ← arguments "Ix.Certified.Text.bytes!" 1 term + let .strLit hex _ := args[0]! | throw "expected a hexadecimal byte string" + match bytesOfHex hex with + | some value => return value + | none => throw "expected an even-length hexadecimal byte string" + +instance : Codec Bool where + type := ref "Bool" + encode value := ref (if value then "Bool.true" else "Bool.false") + decode term := do + let (name, args) ← constructor term + arity name 0 args + match name with + | "Bool.true" => return true + | "Bool.false" => return false + | _ => throw "expected Bool.true or Bool.false" + +instance : Codec UInt64 where + type := ref "UInt64" + encode value := ctor "UInt64.ofNat" #[.natLit value.toNat {}] + decode term := do + let args ← arguments "UInt64.ofNat" 1 term + let .natLit value _ := args[0]! | throw "expected an unsigned integer literal" + if value >= 2 ^ 64 then throw "unsigned integer exceeds UInt64" + return value.toUInt64 + +instance [Codec α] : Codec (Option α) where + type := ctor "Option" #[typeOf α] + encode + | none => ctor "Option.none" #[typeOf α] + | some value => ctor "Option.some" #[typeOf α, encode value] + decode term := do + let (name, args) ← constructor term + if name == "Option.none" then + arity name 1 args + expectType (α := α) args[0]! + return none + else if name == "Option.some" then + arity name 2 args + expectType (α := α) args[0]! + return some (← decode args[1]!) + else throw "expected Option.none or Option.some" + +def encodeList [Codec α] : List α → Term + | [] => ctor "List.nil" #[typeOf α] + | value :: rest => ctor "List.cons" #[typeOf α, encode value, encodeList rest] + +def decodeList [Codec α] : Nat → Term → Except String (List α) + | 0, _ => .error "Ixon data list exceeds the nesting limit" + | fuel + 1, term => do + let (name, args) ← constructor term + if name == "List.nil" then + arity name 1 args + expectType (α := α) args[0]! + return [] + else if name == "List.cons" then + arity name 3 args + expectType (α := α) args[0]! + return (← decode args[1]!) :: (← decodeList fuel args[2]!) + else throw "expected List.nil or List.cons" + +instance [Codec α] : Codec (List α) where + type := ctor "List" #[typeOf α] + encode := encodeList + decode := decodeList 1024 + +instance [Codec α] : Codec (Array α) where + type := ctor "Array" #[typeOf α] + encode values := ctor "List.toArray" #[typeOf α, encode values.toList] + decode term := do + let args ← arguments "List.toArray" 2 term + expectType (α := α) args[0]! + return (← decode (α := List α) args[1]!).toArray + +instance [Codec α] [Codec β] : Codec (α × β) where + type := ctor "Prod" #[typeOf α, typeOf β] + encode value := ctor "Prod.mk" #[typeOf α, typeOf β, encode value.1, encode value.2] + decode term := do + let args ← arguments "Prod.mk" 4 term + expectType (α := α) args[0]! + expectType (α := β) args[1]! + return (← decode args[2]!, ← decode args[3]!) + +/-- Print a single typed definition in the standard `.ixon` file grammar. -/ +def write [Codec α] (name : String) (value : α) : String := + printFile { version := VERSION, decls := #[.defn { + kw := .defn, name := some { parts := #[.str name] }, + ty := typeOf α, value := encode value }] } + +/-- A data file contains one safe monomorphic definition or one annotated main +expression. Its type annotation and every constructor are checked explicitly. -/ +def read [Codec α] (text : String) : Except String α := do + let file ← (parseFile text).mapError toString + unless file.imports.isEmpty do throw "Ixon data files cannot import code" + let (type, value) ← match file.decls.toList, file.main with + | [], some main => pure (main.ty, main.value) + | [.defn declaration], none => do + unless declaration.kw == .defn && !declaration.mods.isUnsafe && + !declaration.mods.isPartial && declaration.uparams.isEmpty do + throw "expected a safe monomorphic data definition" + pure (declaration.ty, declaration.value) + | _, _ => throw "expected exactly one typed Ixon data value" + expectType (α := α) type + decode value + +end Ix.Certified.Text diff --git a/Tests/Certified/CLI.lean b/Tests/Certified/CLI.lean index b96ed8668..34fbd3b74 100644 --- a/Tests/Certified/CLI.lean +++ b/Tests/Certified/CLI.lean @@ -6,7 +6,8 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Lean /-! Native command regressions ported from the frozen source and claim CLI drivers. -JSON is retained for the existing command protocol and differential evidence. -/ +JSON compatibility is selected explicitly to preserve the frozen protocol +and differential evidence. Binary and text inputs have separate regressions. -/ open Lean System @@ -51,7 +52,8 @@ private def check (executable : String) (directory : FilePath) (source : ByteArr IO.FS.writeBinFile envelopePath envelope pure (#[sourcePath.toString, envelopePath.toString, requestPath.toString], Json.mkObj [("accepted", .bool true), ("address", field request "address")]) - let result ← IO.Process.output { cmd := "timeout", args := #["60", executable] ++ args } + let result ← IO.Process.output { + cmd := "timeout", args := #["60", executable, "--request-format", "json"] ++ args } IO.FS.writeFile (directory / "stdout.txt") result.stdout IO.FS.writeFile (directory / "stderr.txt") result.stderr unless result.exitCode == 0 || result.exitCode == 1 do diff --git a/Tests/Certified/Check.lean b/Tests/Certified/Check.lean index 25ac2055e..b601b0b6e 100644 --- a/Tests/Certified/Check.lean +++ b/Tests/Certified/Check.lean @@ -129,6 +129,8 @@ def main (args : List String) : IO UInt32 := do IO.FS.createDirAll output runNative frozen output runCLI frozen output + let formats ← run ".lake/build/bin/certified-input-tests" #[frozen.toString, (output / "formats").toString] + IO.print formats.stdout IO.println "Certified adapter checks passed." return 0 | ["--compare", frozen, actual] => diff --git a/Tests/Certified/ImportManifest.lean b/Tests/Certified/ImportManifest.lean index 64ebc47de..8e989babb 100644 --- a/Tests/Certified/ImportManifest.lean +++ b/Tests/Certified/ImportManifest.lean @@ -29,19 +29,19 @@ def selected : Array SourceFile := #[ ⟨"Ix/Certified/ClaimAccept.lean", some "Ix/Certified/ClaimAccept.lean", "0c179bd4ea2c1a9d8a3b239ffe2feb60b88038f8f9f09c5d9fd8e1e83011f981", "198d8b9b436b937c311bf7ae7e47d4523bdc80022447e42dcc03a131fb01ef38"⟩, ⟨"Ix/Certified/ClaimAudit.lean", some "Ix/Certified/ClaimAudit.lean", "8916803f12bb53e22fa1c94318a6b2a736ec9634ffae811f710b579d3d278bf4", "363f6524397b56c2225d2bdc5cadee29522959d043cfa8a5ace854078a5c30bb"⟩, ⟨"Ix/Certified/ClaimCheck.lean", some "Ix/Certified/ClaimCheck.lean", "e4d598badcb189897a339e187b9529658e2ef75445645e1463704cc6b349a196", "8f8f6c94581ed08c8b8b00d3157bd351e7aed29cd8aa53e491cb53dc48e8845f"⟩, - ⟨"Ix/Certified/ClaimCommand.lean", some "Ix/Certified/ClaimCommand.lean", "8f360b0d0a586ab15e3b8bb3a375f5dc56ad6157344295a67673d876bf8650eb", "e053cb7bab6e1ca19e36de92bcb44c60be9764fc30ab160b92224f4703a4fb4d"⟩, + ⟨"Ix/Certified/ClaimCommand.lean", some "Ix/Certified/ClaimCommand.lean", "8f360b0d0a586ab15e3b8bb3a375f5dc56ad6157344295a67673d876bf8650eb", "0c409330af91cbb31169fd6636f4e2f8146efd035fa470cb87f80b6d43d5e844"⟩, ⟨"Ix/Certified/ClaimInput.lean", some "Ix/Certified/ClaimInput.lean", "d2d0df3ef446690600e843c84683a8f2056e1d8a843a523fc57b550763963f32", "861b0cfd3a1dac9c683dbd1b71088065a00d9a47be9ddbdce304722271027451"⟩, - ⟨"Ix/Certified/ClaimMain.lean", some "Ix/Certified/ClaimMain.lean", "ed1f1b0edaec4e0928d90a814f35673a8989e55037c9cc023a6e0e287c7574e7", "ed1f1b0edaec4e0928d90a814f35673a8989e55037c9cc023a6e0e287c7574e7"⟩, + ⟨"Ix/Certified/ClaimMain.lean", some "Ix/Certified/ClaimMain.lean", "ed1f1b0edaec4e0928d90a814f35673a8989e55037c9cc023a6e0e287c7574e7", "b4c32e74a11fc97d74c484e561ee9093492b0a7e754fa5c13f50a490d16e73ee"⟩, ⟨"Ix/Certified/ClaimMeaning.lean", some "Ix/Certified/ClaimMeaning.lean", "78edb69d209768d8010b28b16d334fd2fa8bc6225490f9b2bbe5fd66cc906f36", "5f2550f0235fe84927d654a360a4298f426e7ca2e7911245c84f91dbd11eef6f"⟩, ⟨"Ix/Certified/ClaimSuggest.lean", some "Ix/Certified/ClaimSuggest.lean", "5f83e6b65542661c4efacd6389c5c6ca0a0eeaffb2d0d24cb58168be514a10f6", "285ed4b14540aa47afa67e28f20bc44e7fdce93fd94d0742971711f15df8336b"⟩, - ⟨"Ix/Certified/Command.lean", some "Ix/Certified/Command.lean", "f9f8a32be2761dc97bf7215585cde40b0e310153482707a570f7c1a106252abe", "c6803f699ed2edd6508e2db73b55f4abb163f6425bcf6b755da11b885991ad17"⟩, + ⟨"Ix/Certified/Command.lean", some "Ix/Certified/Command.lean", "f9f8a32be2761dc97bf7215585cde40b0e310153482707a570f7c1a106252abe", "b44c0fd7a770e1e6d41cadc880db15f9f0b6dd20a180e7ba11bdbc52bc3f03f0"⟩, ⟨"Ix/Certified/Corpus.lean", some "Ix/Certified/Corpus.lean", "fc1d66c81430461bed9e0045e6a26a1e01c85de90854eccea2343f24fe5dde97", "fc1d66c81430461bed9e0045e6a26a1e01c85de90854eccea2343f24fe5dde97"⟩, ⟨"Ix/Certified/Envelope.lean", some "Ix/Certified/Envelope.lean", "da0269803ff6f48c6d0347627d57a7f7699c0ea06e54f959406e3d8570a326f1", "da0269803ff6f48c6d0347627d57a7f7699c0ea06e54f959406e3d8570a326f1"⟩, ⟨"Ix/Certified/Fixtures.lean", some "Ix/Certified/Fixtures.lean", "fe7a503a0e436a2ecf924b48d9b66ed3f6ef901c7bcbe6098853c62c7748675a", "f85f883f498cdee55ab674876dd1fb19bae5dbe8aa729118cffd29ab626c8b25"⟩, ⟨"Ix/Certified/Ingress.lean", some "Ix/Certified/Ingress.lean", "1fe00c0a512ae94fddd2847196f1bafa415ff2c452412f0e69e9e060883dd0cf", "afd9e82827fed62d453c3979fbfdb1238f83573f19af3b6728574f97ff1a86a4"⟩, ⟨"Ix/Certified/Ixon.lean", some "Ix/Certified/Ixon.lean", "9b7f0d64f08c03ed3a215ff5b23e136bd85c71f953e9198fb940b597d7b54410", "5e37eaa6b5cb872edad7954cde413706f30996950c71d21417174f8166377bcb"⟩, - ⟨"Ix/Certified/Main.lean", some "Ix/Certified/Main.lean", "c571357f5e1120efbe23c7db4906fc3eeaebffccf2956e1899212494800d38a5", "c571357f5e1120efbe23c7db4906fc3eeaebffccf2956e1899212494800d38a5"⟩, - ⟨"Ix/Certified/ModelHints.lean", some "Ix/Certified/ModelHints.lean", "74fd6763d56ac5c8aa9af4507172879739c2dc681969a3f342aadbc30ab27837", "de098f48fc03893643c8de08c3bf2bed315f91767c71e9e229816784abb8f124"⟩, + ⟨"Ix/Certified/Main.lean", some "Ix/Certified/Main.lean", "c571357f5e1120efbe23c7db4906fc3eeaebffccf2956e1899212494800d38a5", "34a3e5137991cce17b3c36fc40451fc02450b9aa9da2a4368e38c2770c3e5a18"⟩, + ⟨"Ix/Certified/ModelHints.lean", some "Ix/Certified/ModelHints.lean", "74fd6763d56ac5c8aa9af4507172879739c2dc681969a3f342aadbc30ab27837", "de8128c9496e82468403d4954999a5c2fc607114028dc307ec705d0b0e8519b7"⟩, ⟨"Ix/Certified/ModeledAudit.lean", some "Ix/Certified/ModeledAudit.lean", "55101ebfee84fd89e4c99f08d88aac7edb7000302ef6d022a8c639e331fb3ec2", "c37969f6a69e11d62547bc91b685695cd08f002c115db70a05069f34fe111de1"⟩, ⟨"Ix/Certified/Native.lean", none, "7b617483cbeef932b9caaf6ca3e118be93e5621aaf1ca6bb8132821b336b1c82", ""⟩, ⟨"Ix/Certified/Packet.lean", none, "a694ff77c95d6fe3bd31b99e28597769604b901926d4e0a3dfea7802fb57711d", ""⟩, diff --git a/Tests/Certified/Inputs.lean b/Tests/Certified/Inputs.lean new file mode 100644 index 000000000..bad648641 --- /dev/null +++ b/Tests/Certified/Inputs.lean @@ -0,0 +1,262 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Certified.CLI +import Tests.Certified.ImportManifest + +/-! Binary/text roundtrips over the frozen source, claim, and modeled corpora, +plus process regressions for format selection and malformed transport inputs. +Equality compares all typed fields, independently of their encoded bytes. +-/ + +open Ix.Certified System Lean + +deriving instance BEq for Profile, Protocol, InputSelection, ModelProofHint, + ModelRuleHints, ModelHint, Command.Request, LeafHint, LogicalHint, + RevealWitness, ClaimCommand.Hint, ClaimCommand.Request, Envelope + +namespace Tests.Certified.Inputs + +private def need (condition : Bool) (message : String) : IO Unit := + unless condition do throw (IO.userError message) + +private def readJson (path : FilePath) : IO Json := do + IO.ofExcept (Json.parse (← IO.FS.readFile path)) + +private def sourceRequest (directory : FilePath) : IO Command.Request := do + IO.ofExcept (Command.readRequest (← readJson (directory / "request.json"))) + +private def claimRequest (directory : FilePath) : IO ClaimCommand.Request := do + IO.ofExcept (ClaimCommand.readRequest (← readJson (directory / "request.json"))) + +private def directories (path : FilePath) : IO (Array FilePath) := do + let mut result := #[] + for entry in ← path.readDir do + if ← entry.path.isDir then result := result.push entry.path + return result.qsort (fun a b => a.toString < b.toString) + +private def roundtrip [BEq α] (label : String) (expected : α) (actual : Except String α) : IO Unit := do + let value ← IO.ofExcept (actual.mapError (fun error => s!"{label}: {error}")) + need (value == expected) s!"{label}: decoded fields differ" + +private def rejected (label : String) (actual : Except String α) : IO Unit := + need (!actual.isOk) s!"{label}: malformed input accepted" + +private def sourceRoundtrip (directory : FilePath) : IO Unit := do + let request ← sourceRequest directory + roundtrip s!"{directory}: binary" request (Command.Request.ofIxon request.toIxon) + roundtrip s!"{directory}: text" request (Command.Request.ofText request.toText) + +private def claimRoundtrip (directory : FilePath) : IO Unit := do + let request ← claimRequest directory + roundtrip s!"{directory}: binary" request (ClaimCommand.Request.ofIxon request.toIxon) + roundtrip s!"{directory}: text" request (ClaimCommand.Request.ofText request.toText) + let bytes ← IO.FS.readBinFile (directory / "envelope.bin") + let envelope ← IO.ofExcept (Envelope.ofIxon bytes) + need (envelope.toIxon == bytes) s!"{directory}: public envelope bytes changed" + roundtrip s!"{directory}: envelope" envelope (Envelope.ofText envelope.toText) + let decoded ← IO.ofExcept (Envelope.ofText envelope.toText) + need (decoded.toIxon == bytes) s!"{directory}: text changed the authenticated object" + +private def malformedSource (request : Command.Request) : IO Unit := do + let bytes := request.toIxon + let header := RequestCodec.magic.size + let optionOffset := header + 2 + 64 + let countOffset := optionOffset + 1 + (if request.profile.natType.isSome then 32 else 0) + 32 + let hugeCount := ByteArray.mk #[0x87, 255, 255, 255, 255, 255, 255, 255, 255] + let variants := #[ + ("empty", ByteArray.empty), ("trailing", bytes.push 0), + ("truncated", bytes.extract 0 (bytes.size - 1)), + ("bad magic", bytes.set! 0 0), ("unsupported version", bytes.set! header 2), + ("wrong kind", bytes.set! (header + 1) 1), + ("noncanonical version", bytes.extract 0 header ++ ⟨#[0x80, 1]⟩ ++ bytes.extract (header + 1) bytes.size), + ("invalid option", bytes.set! optionOffset 2), + ("unbounded count", bytes.extract 0 countOffset ++ hugeCount ++ bytes.extract (countOffset + 1) bytes.size), + ("oversize", ByteArray.mk (Array.replicate (RequestCodec.maxBytes + 1) 0))] + for (label, changed) in variants do rejected label (Command.Request.ofIxon changed) + let text := request.toText + for (label, changed) in #[ + ("trailing definition", text ++ "\ndef extra : Nat := 0\n"), + ("unsafe definition", text.replace "def request" "unsafe def request"), + ("partial definition", text.replace "def request" "partial def request"), + ("wrong type", text.replace "def request : Ix.Certified.Command.Request" + "def request : Ix.Certified.ClaimCommand.Request"), + ("unknown constructor", text.replace "Ix.Certified.Command.Request.mk" "Unknown.mk"), + ("short address", text.replace (hexOfBytes request.profile.falseType.hash) "01"), + ("import", "import #" ++ hexOfBytes request.target.hash ++ "\n" ++ text), + ("unsupported grammar", "ixon 999\n" ++ text)] do + rejected label (Command.Request.ofText changed) + roundtrip "text comments" request + (Command.Request.ofText ("/- input comment -/\n" ++ text ++ "\n-- trailing comment\n")) + +private def malformedClaim (request : ClaimCommand.Request) (envelope : Envelope) : IO Unit := do + let bytes := request.toIxon + rejected "claim trailing bytes" (ClaimCommand.Request.ofIxon (bytes.push 0)) + rejected "claim truncation" (ClaimCommand.Request.ofIxon (bytes.extract 0 (bytes.size - 1))) + rejected "claim kind confusion" (Command.Request.ofIxon bytes) + rejected "claim invalid hint" (ClaimCommand.Request.ofIxon + (bytes.set! (RequestCodec.magic.size + 2 + 32) 255)) + rejected "claim text type confusion" (Command.Request.ofText request.toText) + rejected "envelope trailing bytes" (Envelope.ofIxon (envelope.toIxon.push 0)) + rejected "envelope truncated bytes" + (Envelope.ofIxon (envelope.toIxon.extract 0 (envelope.toIxon.size - 1))) + rejected "envelope integer overflow" (Envelope.ofText + (envelope.toText.replace "UInt64.ofNat 1" "UInt64.ofNat 18446744073709551616")) + +private def process (executable : String) (args : Array String) (expected : UInt32) + (success : Option Json := none) : IO Unit := do + let result ← IO.Process.output { cmd := "timeout", args := #["30", executable] ++ args } + need (result.exitCode == expected) + s!"{executable} {args}: expected exit {expected}, got {result.exitCode}\n{result.stderr}" + if let some expected := success then + need ((← IO.ofExcept (Json.parse result.stdout)) == expected) "incorrect acceptance record" + else if expected != 0 then + need result.stdout.trimAscii.isEmpty "rejected input printed an acceptance record" + +private def writeSource (directory : FilePath) (source : ByteArray) (request : Command.Request) : IO Unit := do + IO.FS.createDirAll directory + IO.FS.writeBinFile (directory / "source.ixe") source + IO.FS.writeBinFile (directory / "request.ix") request.toIxon + IO.FS.writeFile (directory / "request.ixon") request.toText + +private def writeClaim (directory : FilePath) (source : ByteArray) + (request : ClaimCommand.Request) (envelope : Envelope) : IO Unit := do + IO.FS.createDirAll directory + IO.FS.writeBinFile (directory / "source.ixe") source + IO.FS.writeBinFile (directory / "request.ix") request.toIxon + IO.FS.writeFile (directory / "request.ixon") request.toText + IO.FS.writeBinFile (directory / "envelope.ix") envelope.toIxon + IO.FS.writeFile (directory / "envelope.ixon") envelope.toText + +private def formats : Array (String × String) := #[("binary", "ix"), ("text", "ixon")] + +private def sourceCLI (fixture output : FilePath) : IO Unit := do + let source ← IO.FS.readBinFile (fixture / "source.ixe") + let request ← sourceRequest fixture + let good := output / "source" + let bad := output / "bad-source" + writeSource good source request + writeSource bad source { request with profile := { request.profile with falseType := request.target } } + malformedSource request + for (format, extension) in formats do + for mode in #["proof", "store"] do + for (directory, accepted) in #[(good, true), (bad, false)] do + let args := #[mode, (directory / "source.ixe").toString, + (directory / s!"request.{extension}").toString, s!"--request-format={format}"] + let success := if accepted then some (Json.mkObj [("accepted", .bool true), ("mode", .str mode)]) else none + process ".lake/build/bin/certified-check" args (if accepted then 0 else 1) success + let success := Json.mkObj [("accepted", .bool true), ("mode", .str "proof")] + process ".lake/build/bin/certified-check" + #["proof", (good / "source.ixe").toString, (good / "request.ix").toString] 0 (some success) + process ".lake/build/bin/certified-check" + #["proof", (good / "source.ixe").toString, (good / "request.ixon").toString] 1 + process ".lake/build/bin/certified-check" + #["--request-format", "text", "proof", (good / "source.ixe").toString, (good / "request.ix").toString] 1 + +private def claimCLI (fixture output : FilePath) : IO Unit := do + let source ← IO.FS.readBinFile (fixture / "source.ixe") + let request ← claimRequest fixture + let envelope ← IO.ofExcept (Envelope.ofIxon (← IO.FS.readBinFile (fixture / "envelope.bin"))) + let good := output / "good" + let bad := output / "bad" + let old := output / "unsupported-version" + writeClaim good source request envelope + writeClaim bad source { request with address := Address.blake3 "wrong digest".toUTF8 } envelope + let oldEnvelope := { envelope with protocol := { envelope.protocol with checker := 1 } } + writeClaim old source { request with address := Address.blake3 oldEnvelope.toIxon } oldEnvelope + malformedClaim request envelope + for (requestFormat, requestExtension) in formats do + for (envelopeFormat, envelopeExtension) in formats do + for (directory, accepted) in #[(good, true), (bad, false), (old, false)] do + let args := #["--request-format", requestFormat, s!"--envelope-format={envelopeFormat}", + (directory / "source.ixe").toString, (directory / s!"envelope.{envelopeExtension}").toString, + (directory / s!"request.{requestExtension}").toString] + let success := if accepted then some (Json.mkObj [("accepted", .bool true), + ("address", .str (hexOfBytes request.address.hash))]) else none + process ".lake/build/bin/certified-claim-check" args (if accepted then 0 else 1) success + IO.FS.writeBinFile (good / "trailing.ix") (envelope.toIxon.push 0) + process ".lake/build/bin/certified-claim-check" #[(good / "source.ixe").toString, + (good / "trailing.ix").toString, (good / "request.ix").toString] 1 + let success := Json.mkObj [("accepted", .bool true), ("address", .str (hexOfBytes request.address.hash))] + process ".lake/build/bin/certified-claim-check" #[(good / "source.ixe").toString, + (good / "envelope.ix").toString, (good / "request.ix").toString] 0 (some success) + +private def flagErrors : IO Unit := do + for executable in #["certified-check", "certified-claim-check"] do + let executable := s!".lake/build/bin/{executable}" + process executable #["--help"] 0 + for args in #[#[], #["--unknown"], #["--request-format"], + #["--request-format", "garbage"], #["--request-format=json", "--request-format", "text"]] do + process executable args 2 + process ".lake/build/bin/certified-check" #["--envelope-format", "text"] 2 + process ".lake/build/bin/certified-claim-check" #["--envelope-format", "json"] 2 + +private def run (frozen output : FilePath) : IO Unit := do + let sources ← directories (frozen / "evidence/c5/inputs") + let modeledSources ← directories (frozen / "evidence/c7/modeled-inputs/source") + let claims ← directories (frozen / "evidence/c7/claim-inputs") + let modeledClaims ← directories (frozen / "evidence/c7/modeled-inputs/claims") + need (sources.size == 42 && modeledSources.size == 44 && claims.size == 1578 && modeledClaims.size == 74) + "incomplete transport fixture coverage" + for directory in sources ++ modeledSources do sourceRoundtrip directory + for directory in claims ++ modeledClaims do claimRoundtrip directory + IO.println "Typed binary/text roundtrips passed: 86 source requests, 1,652 claim requests and envelopes." + sourceCLI (frozen / "evidence/c5/inputs/identity") output + let mut selected : Array String := #[] + for directory in claims do + let request ← claimRequest directory + let kind := match request.hint with + | .logical _ => "logical" | .contains _ => "contains" | .reveal _ => "reveal" + if selected.contains kind then continue + let expected ← readJson (directory / "expected.json") + unless (← IO.ofExcept (expected.getObjValAs? Bool "accepted")) do continue + claimCLI directory (output / kind) + selected := selected.push kind + need (selected.size == 3) "missing logical, membership, or revelation process coverage" + flagErrors + IO.println "Certified input format, malformed input, digest, version, and CLI regressions passed." + +private def examples (frozen output : FilePath) : IO Unit := do + let source := frozen / "evidence/c5/inputs/identity" + writeSource (output / "source") (← IO.FS.readBinFile (source / "source.ixe")) (← sourceRequest source) + for directory in ← directories (frozen / "evidence/c7/claim-inputs") do + let request ← claimRequest directory + let .logical _ := request.hint | continue + let expected ← readJson (directory / "expected.json") + unless (← IO.ofExcept (expected.getObjValAs? Bool "accepted")) do continue + let envelope ← IO.ofExcept (Envelope.ofIxon (← IO.FS.readBinFile (directory / "envelope.bin"))) + writeClaim (output / "claim") (← IO.FS.readBinFile (directory / "source.ixe")) request envelope + break + IO.println s!"Wrote Lean-typed Ixon inputs in binary and text form to {output}." + +private def withArchive (action : FilePath → IO Unit) : IO Unit := do + let archive := Tests.Certified.ImportManifest.archive + let digest ← IO.Process.output { cmd := "sha256sum", args := #["--", archive] } + need (digest.exitCode == 0 && (digest.stdout.splitOn " ").headD "" == + Tests.Certified.ImportManifest.archiveSha256) "frozen adapter archive identity changed" + IO.FS.withTempDir fun directory => do + let unpack ← IO.Process.output { + cmd := "tar", args := #["-xzf", archive, "--no-same-owner", "--no-same-permissions", "-C", directory.toString] } + need (unpack.exitCode == 0) unpack.stderr + action directory + +def main (args : List String) : IO UInt32 := do + match args with + | [] => + withArchive fun frozen => IO.FS.withTempDir (run frozen) + return 0 + | ["--examples", output] => + withArchive fun frozen => examples frozen output + return 0 + | [frozen, output] => + run frozen output + return 0 + | _ => + IO.eprintln "usage: certified-input-tests [FROZEN OUTPUT | --examples OUTPUT]" + return 2 + +end Tests.Certified.Inputs + +def main := Tests.Certified.Inputs.main diff --git a/docs/certified-checking.md b/docs/certified-checking.md index b565d7b30..ecc5e963f 100644 --- a/docs/certified-checking.md +++ b/docs/certified-checking.md @@ -21,39 +21,116 @@ Build the source and claim commands: lake build --wfail certified-check certified-claim-check ``` -Source checking takes a lazy `.ixe` file and a JSON request that selects the -target or subjects, primitive addresses, source objects and natural values: +Source checking takes a lazy `.ixe` file and a typed request that selects the +target or subjects, primitive addresses, source objects and natural values. +Binary Ixon is the default input format: ```sh -.lake/build/bin/certified-check proof SOURCE.ixe REQUEST.json -.lake/build/bin/certified-check store SOURCE.ixe REQUEST.json +.lake/build/bin/certified-check proof SOURCE.ixe REQUEST.ix +.lake/build/bin/certified-check store SOURCE.ixe REQUEST.ix +.lake/build/bin/certified-claim-check SOURCE.ixe ENVELOPE.ix REQUEST.ix ``` -The request fields are `target`, `subjects`, `objects`, `naturals`, -`falseType`, `falseElim`, nullable `natType`, and optional `models`. Addresses -are 32-byte hexadecimal strings. Model hints select earlier source -declarations; the validator checks their types and complete source equations. -The request parser is defined in -[`Command.lean`](../Ix/Certified/Command.lean). - -For a runnable source example: +Use `--request-format text` for a readable `.ixon` request. Claim checking +selects the request and envelope encodings independently: ```sh -lake build certified-source-tests -.lake/build/bin/certified-source-tests /tmp/ix-certified-examples -.lake/build/bin/certified-check proof \ - /tmp/ix-certified-examples/identity/source.ixe \ - /tmp/ix-certified-examples/identity/request.json +.lake/build/bin/certified-check --request-format text proof SOURCE.ixe REQUEST.ixon +.lake/build/bin/certified-check --request-format text store SOURCE.ixe REQUEST.ixon +.lake/build/bin/certified-claim-check \ + --request-format text --envelope-format text \ + SOURCE.ixe ENVELOPE.ixon REQUEST.ixon +.lake/build/bin/certified-claim-check \ + --request-format text --envelope-format binary \ + SOURCE.ixe ENVELOPE.ix REQUEST.ixon ``` -Claim checking also takes the exact serialized envelope. The JSON request -selects its expected address and supplies logical, membership or revelation -witness hints: +Both flags accept `binary` or `text`, including `--request-format=text` +syntax. The selected flag determines the encoding independently of the file +suffix. `SOURCE.ixe` always contains a serialized Ixon environment. + +For runnable examples in both encodings: ```sh -.lake/build/bin/certified-claim-check SOURCE.ixe ENVELOPE.bin REQUEST.json +lake build certified-input-tests +.lake/build/bin/certified-input-tests --examples /tmp/ix-certified-examples +.lake/build/bin/certified-check --request-format text proof \ + /tmp/ix-certified-examples/source/source.ixe \ + /tmp/ix-certified-examples/source/request.ixon +.lake/build/bin/certified-claim-check --request-format text --envelope-format binary \ + /tmp/ix-certified-examples/claim/source.ixe \ + /tmp/ix-certified-examples/claim/envelope.ix \ + /tmp/ix-certified-examples/claim/request.ixon ``` +Both commands use fuel 6,400. Success exits 0 and prints a JSON acceptance +record; rejection or a file-reading error exits 1, and invalid command-line +arguments exit 2. Fuel or witness-search failure can reject a valid source, +so success and completeness are separate properties. + +### Lean API + +Import `Ix.Certified` to construct the actual Lean values: + +| Value | Type | +| --- | --- | +| Source proof/store request | `Ix.Certified.Command.Request` | +| Claim request and witness hints | `Ix.Certified.ClaimCommand.Request` | +| Public claim envelope | `Ix.Certified.Envelope` | + +Source requests contain `profile`, `target`, `subjects`, `selection`, and +`models`. The profile pins `falseType`, `falseElim`, and optional `natType`; +the selection lists object and natural-value addresses. Model hints select +earlier source declarations whose types and complete equations the validator +checks. Claim requests contain the expected envelope `address` and a typed +`.logical`, `.contains`, or `.reveal` hint. + +Each of the three types provides `toIxon`/`ofIxon` and `toText`/`ofText`. +They also have `Ixon.Serialize` instances. Use the named `ofIxon` decoders +when reading a complete input: they reject trailing and noncanonical bytes. +For example, a Lean program can export a source request without JSON: + +```lean +import Ix.Certified + +def saveRequest (directory : System.FilePath) + (request : Ix.Certified.Command.Request) : IO Unit := do + IO.FS.createDirAll directory + IO.FS.writeBinFile (directory / "request.ix") request.toIxon + IO.FS.writeFile (directory / "request.ixon") request.toText +``` + +`Command.run` and `ClaimCommand.run` also accept these typed requests directly +when the source environment is already loaded. Lean authoring supports normal +record syntax and computation; the exported `.ixon` file contains the resulting +data value. + +### Input representations + +Text inputs use the existing Ixon parser and printer. Each file contains one +safe monomorphic `def` or one annotated main expression. The reader checks +the type annotation, constructor names, argument counts, and literal values. +Lists, options, claims, model hints, and revelation fields use their actual +Lean constructors, with explicit type arguments as required by Ixon syntax. +`Ix.Certified.Text.address!` and `bytes!` provide hexadecimal literals. +The data reader accepts constructor expressions and checks their contents; +imports, extra declarations, and arbitrary computation are rejected. + +Binary requests have an `IX-CERTIFIED-REQUEST` header, request-format version +1, and a distinct source/claim tag. Addresses occupy 32 bytes; list and byte +lengths use Ixon `Tag0` encoding, and options use tags 0 and 1. Complete binary +decoding requires canonical re-encoding. Request decoding is capped at 1 MiB; +text additionally uses the standard Ixon parser's node and depth limits. +See [`RequestCodec.lean`](../Ix/Certified/RequestCodec.lean), +[`TextCodec.lean`](../Ix/Certified/TextCodec.lean), and +[`InputText.lean`](../Ix/Certified/InputText.lean). + +Claim envelopes retain their existing canonical Ixon representation. With +binary input, authentication checks the exact bytes read from the file. +With text input, the authenticated object is the parsed envelope's canonical +binary encoding. The request's expected address must match the BLAKE3 hash +of those bytes. Text formatting therefore does not change the claim address. + The envelope binds the claim, primitive profile, logical-axiom manifest and protocol versions. The current checker version is **2**; format, codec, policy and aggregation versions are **1**. Exact decoding, canonical @@ -62,10 +139,10 @@ Trailing bytes and older checker envelopes reject. See [`Envelope.lean`](../Ix/Certified/Envelope.lean) and [`ClaimCommand.lean`](../Ix/Certified/ClaimCommand.lean). -Both commands currently use fuel 6,400. Success exits 0 and prints a JSON -acceptance record; a rejected request exits 1. Fuel or witness-search failure -can reject a valid source, so success and completeness are separate -properties. +Existing JSON requests remain available with `--request-format json` for +compatibility. The frozen CLI corpus selects this mode explicitly. All +formats pass through the same witness search and certified validation. +The file interface is implemented in [`CLI.lean`](../Ix/Certified/CLI.lean). ## Semantic contract @@ -93,7 +170,7 @@ definitions and statements are in [`LogicalPolicy.lean`](../Ix/Theory/Certified/LogicalPolicy.lean). The mathematical contract concerns the Lean functions. Native Lean -execution, filesystem loading and the BLAKE3 foreign interface remain +execution, transport parsing, filesystem loading and the BLAKE3 foreign interface remain execution boundaries. Full production-checker refinement and execution of this certification inside an authenticated Aiur proof remain separate obligations described in @@ -110,8 +187,9 @@ lake run check-certified The gate builds the adapters, audits and test programs with warnings treated as errors. It compares the exact foundation report, checks the frozen source archive and maintained import identities, runs six native host test programs, -and exercises the actual source and claim executables on all three CLI -corpora. CI runs the same gate. +and exercises the actual source and claim executables on all three legacy CLI +corpora. It also checks the binary/text input codecs and format-selection +regressions. CI runs the same gate. The combined audit covers **86 distinct roots**, **74 premise definitions or constructor types**, **9,682 logical declarations** and **10,957 declarations @@ -151,7 +229,11 @@ non-VM field; only `pilotDeclined` and `pilotDeclines` are projected away from the three historical reports that mixed host and VM checks. The VM pilot, packet encoder and VM execution suite are archived for a later change. CLI JSON requests and aggregate records are compared as parsed values because -the maintained Lean driver formats JSON differently. The archive, source +the maintained Lean driver formats JSON differently. Typed binary/text +roundtrips cover all 86 source requests and 1,652 claim requests and envelopes +from the source, claim, and modeled corpora. Additional process tests exercise +mixed input formats, malformed encodings, wrong digests, unsupported versions, +and command-line errors. The archive, source mappings, licenses and historical reproduction limits are documented in [`Tests/Fixtures/Certified`](../Tests/Fixtures/Certified/README.md). The archive recovers the selected adapter sources and fixtures; it does not include the diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index b6836b2fd..820c8ed0d 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -6,8 +6,11 @@ proofs. Their named specification and reference implementation lemmas are local under `Ix.Theory.Named`. Building or checking them requires no external formalization repository. -The certified receipt interface and a fragment of production `checkEnvAnon` -have relative consistency theorems. Full checker consistency remains open. +Certified acceptance has model-construction and relative consistency theorems, +with a concrete set-model construction in the separate Mathlib package. A +direct theorem also connects a fragment of production `checkEnvAnon` to that +semantics. Extending this connection to all paths of the ordinary Lean checker +requires the remaining production-refinement proofs described below. The [recursion audit](kernel-recursion.md) explains the production call graph and fuel bounds. The [context-digest guide](tc-context-digest-collision-boundary.md) diff --git a/lakefile.lean b/lakefile.lean index b174c29c0..e2c94dad0 100644 --- a/lakefile.lean +++ b/lakefile.lean @@ -380,6 +380,11 @@ lean_exe «certified-cli-tests» where root := `Tests.Certified.CLI supportInterpreter := true +lean_exe «certified-input-tests» where + root := `Tests.Certified.Inputs + supportInterpreter := true + moreLinkObjs := #[ix_rs] + lean_exe «certified-adapter-tests» where root := `Tests.Certified.Check supportInterpreter := true @@ -464,7 +469,7 @@ script "check-certified" := do run "lake" #["build", "--wfail", "IxCertified", "IxCertifiedAudit", "certified-check", "certified-claim-check", "certified-feature-tests", "certified-ordinary-tests", "certified-source-tests", "certified-fidelity-tests", - "certified-claim-tests", "certified-modeled-tests", "certified-cli-tests", + "certified-claim-tests", "certified-modeled-tests", "certified-cli-tests", "certified-input-tests", "certified-adapter-tests"] checkReport "Ix/Certified/AuditAll.lean" "Tests/Certified/foundation.txt" run ".lake/build/bin/certified-adapter-tests" From 1bd7a44d74aeb054382ad43b061862a44fdd7a26 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 10:43:15 -0400 Subject: [PATCH 06/63] Refine polymorphic constant inference into the set model --- Ix/Kernel/Verify/Consistency.lean | 5 + Ix/Kernel/Verify/Consistency/Audit.lean | 20 +- Ix/Kernel/Verify/Consistency/Constant.lean | 98 +++++++ Ix/Kernel/Verify/Consistency/InstUniv.lean | 305 +++++++++++++++++++++ Ix/Kernel/Verify/InstL.lean | 104 +------ Ix/Kernel/Verify/UniverseSupport.lean | 116 ++++++++ Ix/Theory/Model/LevelCongruence.lean | 224 +++++++++++++++ Tests/Ix/Kernel/Consistency.lean | 99 ++++++- Tests/Theory/ImportManifest.lean | 6 +- Tests/Theory/Provenance.lean | 10 +- docs/kernel-verification.md | 30 +- 11 files changed, 899 insertions(+), 118 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/Constant.lean create mode 100644 Ix/Kernel/Verify/Consistency/InstUniv.lean create mode 100644 Ix/Kernel/Verify/UniverseSupport.lean create mode 100644 Ix/Theory/Model/LevelCongruence.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index e6c5a00b0..121f7e437 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -7,6 +7,8 @@ import Ix.Kernel.Verify.Consistency.Level import Ix.Kernel.Verify.Consistency.Expr import Ix.Kernel.Verify.Consistency.Judgment import Ix.Kernel.Verify.Consistency.Infer +import Ix.Kernel.Verify.Consistency.InstUniv +import Ix.Kernel.Verify.Consistency.Constant import Ix.Kernel.Verify.Consistency.Atomic import Ix.Kernel.Verify.Consistency.Production import Ix.Kernel.Verify.Consistency.Environment @@ -20,4 +22,7 @@ transport keeps its representation, arithmetic, and dependency assumptions explicit. A production `checkEnvAnon` fragment preserves models of its axiom set for monomorphic aliases and closed sorts under the stated execution resources. General checker soundness remains outside this fragment. +Separately, polymorphic constant inference refines the model for arbitrary +readable entry types, using the actual universe-instantiation walker and +explicit lookup, scope, and finite-support resources. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 3a6684b17..f9e0c91f6 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -4,6 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.Infer +import Ix.Kernel.Verify.Consistency.Constant import Ix.Kernel.Verify.Consistency.Environment import Ix.Kernel.Verify.Audit.Basic @@ -37,6 +38,11 @@ private def atomicRoots : Array Lean.Name := #[ ``AtomicInferenceSupport.scopeAndReferences, ``AtomicInference.sound ] +private def instantiationRoots : Array Lean.Name := #[ + ``instUnivSpec_readExpr?, ``instantiateUnivParams_readExpr?, + ``instantiateUnivParams_readAnnotated, ``inferUncached_const_sound, ``infer_const_sound +] + private def productionRoots : Array Lean.Name := #[ ``StandalonePrefix.member_success, ``definition_body_trace, ``AtomicDefinitionRun.sound, ``AtomicDefinitionRun.no_self_alias, @@ -77,8 +83,18 @@ def roots : Array RootAllowance := #[ { root := ``ModelTyping.no_false, standardAxioms := standard }, { root := ``sort_conversion, standardAxioms := standard }, { root := ``inferUncached_sort_sound, standardAxioms := standard, - nativeAxioms := #[expressionNative, levelNative] } -] ++ atomicRoots.map (fun root => { + nativeAxioms := #[expressionNative, levelNative] }, + { root := ``Theory.VExpr.LevelEquivalent.refl, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.VExpr.LevelEquivalent.liftN, standardAxioms := #[``propext] }, + { root := ``Theory.VExpr.LevelEquivalent.inst, standardAxioms := #[``propext] }, + { root := ``Theory.VExpr.instL_liftN, standardAxioms := #[``propext] }, + { root := ``Theory.VExpr.instL_inst, standardAxioms := #[``propext] }, + { root := ``Theory.VExpr.LevelWF.instL_nil, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.reannotate_levels, standardAxioms := #[``propext] }, + { root := ``Theory.Model.AExpr.LevelEquivalent.interp, standardAxioms := standard }, + { root := ``Theory.Model.AExpr.LevelEquivalent.wellDenoted, standardAxioms := standard }, + { root := ``Theory.Model.AExpr.LevelEquivalent.typing, standardAxioms := standard } +] ++ (atomicRoots ++ instantiationRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) ++ productionRoots.map (fun root => { diff --git a/Ix/Kernel/Verify/Consistency/Constant.lean b/Ix/Kernel/Verify/Consistency/Constant.lean new file mode 100644 index 000000000..babefe0b4 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Constant.lean @@ -0,0 +1,98 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Atomic +import Ix.Kernel.Verify.Consistency.InstUniv + +/-! +# Production inference of polymorphic constants + +Successful constant inference supplies its own arity check and runs the actual +memoized universe-instantiation routine. Lookup agreement identifies the +loaded declaration's universe count and type with an existing model entry. +No restriction to monomorphic entries or sort-shaped types is needed. + +The remaining operational assumptions are explicit: lookup agreement, the +loaded state's finite interning and level-substitution resources, and (for +the cached entry point) misses at the actual inference key. This does not yet +establish these invariants for arbitrary loader states or cache hits. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- Agreement with the concrete declaration returned by lazy lookup, together +with the resources needed by the walker at that exact post-lookup state. -/ +structure ConstantInferenceSupport {β : Type u} + (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) + (before : TcState .anon) (id : KId .anon) (arguments : Array (KUniv .anon)) + (ref : ConstRef β) (entry : ConstantEntry β) : Prop where + resolved : resolve id.addr = some ref + found : entries ref = some entry + lookup : ∀ concrete loaded, TcM.getConst id before = .ok concrete loaded → + concrete.lvls.toNat = entry.universes ∧ + readExpr? resolve concrete.ty = some entry.type.erase ∧ + UniverseInstantiationSupport loaded concrete.ty arguments + +/-- A successful uncached production branch types its exact returned tree in +every model of the preceding interface. The runtime guard establishes arity; +the model environment's well-formedness supplies the declaration type's scope. -/ +theorem inferUncached_const_sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {context : Model.Context β} {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} + {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} + {methods : Methods .anon} {before after : TcState .anon} {type : KExpr .anon} + (support : ConstantInferenceSupport resolve entries before id arguments ref entry) + (wellFormed : entries.WF) + (accepted : RecM.inferUncached inferRec inferOnly (.const id arguments info) + methods before = .ok type after) : + ModelTyping.{u,v} resolve entries context (.const id arguments info) type := by + change (RecM.inferUncached inferRec inferOnly (.const id arguments info)).run + methods before = .ok type after at accepted + unfold RecM.inferUncached at accepted + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted + change EStateM.bind (TcM.getConst id) _ before = _ at accepted + cases got : TcM.getConst id before with + | error err failed => rw [EStateM.bind, got] at accepted; contradiction + | ok concrete loaded => + rw [EStateM.bind, got] at accepted + obtain ⟨count, reading, resources⟩ := support.lookup concrete loaded got + by_cases arity : concrete.lvls.toNat = arguments.size + · simp only [arity, bne_self_eq_false, Bool.false_eq_true, if_false] at accepted + change TcM.instantiateUnivParams concrete.ty arguments loaded = .ok type after at accepted + have length : (arguments.toList.map readLevel).length = entry.universes := by + simpa only [List.length_map, Array.length_toList] using arity.symm.trans count + have scope : entry.type.erase.LevelWF arguments.size := by + rw [← arity, count] + exact (wellFormed.typeScope ref entry support.found).erase.1 + obtain ⟨output, outputReads, same⟩ := + instantiateUnivParams_readAnnotated resources scope reading accepted + refine ⟨.const ref (arguments.toList.map readLevel), output, ?_, outputReads, + same.typing (TypingClaim.const support.found length)⟩ + simp [readExpr?, support.resolved, AExpr.erase] + · simp only [bne_iff_ne] at accepted + rw [if_pos arity] at accepted + contradiction + +/-- The ordinary production inference entry point, including its actual key +lookup and final cache write, refines polymorphic constant typing on misses. -/ +theorem infer_const_sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {context : Model.Context β} {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} + {methods : Methods .anon} {before after : TcState .anon} {type : KExpr .anon} + (miss : UncachedInference before (.const id arguments info)) + (support : ConstantInferenceSupport resolve entries miss.keyed id arguments ref entry) + (wellFormed : entries.WF) + (accepted : RecM.infer (.const id arguments info) methods before = .ok type after) : + ModelTyping.{u,v} resolve entries context (.const id arguments info) type := by + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + exact inferUncached_const_sound support wellFormed run + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/InstUniv.lean b/Ix/Kernel/Verify/Consistency/InstUniv.lean new file mode 100644 index 000000000..f945aa0d2 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/InstUniv.lean @@ -0,0 +1,305 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Expr +import Ix.Kernel.Verify.UniverseSupport +import Ix.Theory.Model.LevelCongruence + +/-! +# Production universe instantiation in the consistency model + +The existing walker proof establishes equality with its memo-free specification +under finite interning support. This module connects that specification to +the structural reader, including simplifying universe constructors and lets. +Its final theorem supplies an annotated reading of the actual returned tree, +with the same interpretation and hereditary validity as model substitution. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u +variable {β : Type u} + +/-- The precise level-side assumptions used by the simplifying constructors. -/ +structure UniverseSubstitutionSupport (arguments : Array (KUniv .anon)) + (term : KExpr .anon) : Prop where + faithful : ∀ left right, KExpr.LevelReach arguments term left → + KExpr.LevelReach arguments term right → left.AddrFaithful right + bounded : ∀ level, KExpr.LevelReach arguments term level → level.size < UInt64.size + +private theorem substUniv_readLevel {arguments : Array (KUniv .anon)} + {level result : KUniv .anon} + (run : TcM.substUniv level arguments = .ok result) + (faithful : ∀ left right, KUniv.SubstUnivReach arguments level left → + KUniv.SubstUnivReach arguments level right → left.AddrFaithful right) + (bounded : ∀ value, KUniv.SubstUnivReach arguments level value → + value.size < UInt64.size) : + (readLevel level).inst (arguments.toList.map readLevel) ≈ readLevel result := by + rw [show (readLevel : KUniv .anon → VLevel) = KUniv.toVLevel from funext readLevel_eq] + exact VLevel.equiv_def.mpr fun valuation => + (VLevel.equiv_def.mp (TcM.substUniv_toVLevel run faithful bounded) valuation).symm + +private theorem list_substUniv_readLevels {arguments : Array (KUniv .anon)} : + ∀ {levels results : List (KUniv .anon)}, + levels.mapM (TcM.substUniv · arguments) = .ok results → + (∀ level ∈ levels, ∀ left right, KUniv.SubstUnivReach arguments level left → + KUniv.SubstUnivReach arguments level right → left.AddrFaithful right) → + (∀ level ∈ levels, ∀ value, KUniv.SubstUnivReach arguments level value → + value.size < UInt64.size) → + ∀ valuation, + ((levels.map readLevel).map (VLevel.inst (arguments.toList.map readLevel))).map + (VLevel.eval valuation) = (results.map readLevel).map (VLevel.eval valuation) := by + intro levels + induction levels with + | nil => + intro results run _ _ valuation + simp only [List.mapM_nil] at run + cases run + rfl + | cons level rest ih => + intro results run faithful bounded valuation + rw [List.mapM_cons] at run + cases first : TcM.substUniv level arguments with + | error err => rw [first] at run; contradiction + | ok result => + cases tail : rest.mapM (TcM.substUniv · arguments) with + | error err => rw [first, tail] at run; contradiction + | ok remaining => + rw [first, tail] at run + cases run + simp only [List.map_cons, List.cons.injEq] + exact ⟨VLevel.equiv_def.mp (substUniv_readLevel first + (faithful level List.mem_cons_self) (bounded level List.mem_cons_self)) valuation, + ih tail + (fun level member => faithful level (List.mem_cons_of_mem _ member)) + (fun level member => bounded level (List.mem_cons_of_mem _ member)) valuation⟩ + +private theorem array_substUniv_readLevels {arguments levels results : Array (KUniv .anon)} + (run : levels.mapM (TcM.substUniv · arguments) = .ok results) + (faithful : ∀ level ∈ levels, ∀ left right, KUniv.SubstUnivReach arguments level left → + KUniv.SubstUnivReach arguments level right → left.AddrFaithful right) + (bounded : ∀ level ∈ levels, ∀ value, KUniv.SubstUnivReach arguments level value → + value.size < UInt64.size) : + ∀ valuation, + ((levels.toList.map readLevel).map (VLevel.inst (arguments.toList.map readLevel))).map + (VLevel.eval valuation) = (results.toList.map readLevel).map (VLevel.eval valuation) := by + have listRun : levels.toList.mapM (TcM.substUniv · arguments) = .ok results.toList := by + rw [← Array.toList_mapM, run] + rfl + exact list_substUniv_readLevels listRun + (fun level member => faithful level (by simpa using member)) + (fun level member => bounded level (by simpa using member)) + +private theorem except_bind_success {ε α γ : Type _} {action : Except ε α} + {next : α → Except ε γ} {result : γ} (run : action.bind next = .ok result) : + ∃ intermediate, action = .ok intermediate ∧ next intermediate = .ok result := by + cases action with + | error err => contradiction + | ok value => exact ⟨value, rfl, run⟩ + +private theorem option_bind_success {α γ : Type _} {action : Option α} + {next : α → Option γ} {result : γ} (run : action.bind next = some result) : + ∃ intermediate, action = some intermediate ∧ next intermediate = some result := by + cases action with + | none => contradiction + | some value => exact ⟨value, rfl, run⟩ + +/-- Successful pure instantiation reads as model substitution, up to equivalent +universe levels. All expression positions are covered; the reader's existing +exclusions of free variables, strings, and unresolved references are retained. -/ +theorem instUnivSpec_readExpr? {resolve : Address → Option (ConstRef β)} + {arguments : Array (KUniv .anon)} {term result : KExpr .anon} {source : VExpr β} + (support : UniverseSubstitutionSupport arguments term) + (reading : readExpr? resolve term = some source) + (run : KExpr.instUnivSpec term arguments = .ok result) : + ∃ output, readExpr? resolve result = some output ∧ + VExpr.LevelEquivalent (source.instL (arguments.toList.map readLevel)) output := by + induction term generalizing source result with + | var index name info => + cases run + cases reading + exact ⟨_, rfl, .bvar _⟩ + | fvar _ _ _ | str _ _ _ => contradiction + | nat value name info => + cases run + cases reading + exact ⟨_, rfl, .natLit _⟩ + | sort level info => + cases reading + rw [KExpr.instUnivSpec] at run + obtain ⟨value, substituted, run⟩ := except_bind_success run + cases run + exact ⟨_, rfl, .sort (substUniv_readLevel substituted + (fun left right hl hr => support.faithful left right + ⟨level, .sort, hl⟩ ⟨level, .sort, hr⟩) + (fun value hv => support.bounded value ⟨level, .sort, hv⟩))⟩ + | const id levels info => + cases resolved : resolve id.addr with + | none => simp [readExpr?, resolved] at reading + | some ref => + simp [readExpr?, resolved] at reading + subst source + rw [KExpr.instUnivSpec] at run + obtain ⟨values, substituted, run⟩ := except_bind_success run + cases run + refine ⟨.const ref (values.toList.map readLevel), ?_, .const ref ?_⟩ + · change readExpr? resolve (.const id values _) = _ + simp [readExpr?, resolved] + · exact array_substUniv_readLevels substituted + (fun level member left right hl hr => support.faithful left right + ⟨level, .const member, hl⟩ ⟨level, .const member, hr⟩) + (fun level member value hv => support.bounded value ⟨level, .const member, hv⟩) + | app fn arg info hf ha => + rw [readExpr?] at reading + obtain ⟨f, fReads, reading⟩ := option_bind_success reading + obtain ⟨a, aReads, reading⟩ := option_bind_success reading + cases reading + rw [KExpr.instUnivSpec] at run + obtain ⟨f', fRun, run⟩ := except_bind_success run + obtain ⟨a', aRun, run⟩ := except_bind_success run + cases run + obtain ⟨vf, vfReads, vfSame⟩ := hf + ⟨fun x y hx hy => support.faithful x y hx.app_f hy.app_f, + fun x hx => support.bounded x hx.app_f⟩ fReads fRun + obtain ⟨va, vaReads, vaSame⟩ := ha + ⟨fun x y hx hy => support.faithful x y hx.app_a hy.app_a, + fun x hx => support.bounded x hx.app_a⟩ aReads aRun + refine ⟨.app vf va, ?_, .app vfSame vaSame⟩ + change readExpr? resolve (.app f' a' _) = _ + simp [readExpr?, vfReads, vaReads] + | lam name bi domain body info hA hb => + rw [readExpr?] at reading + obtain ⟨A, aReads, reading⟩ := option_bind_success reading + obtain ⟨b, bReads, reading⟩ := option_bind_success reading + cases reading + rw [KExpr.instUnivSpec] at run + obtain ⟨A', aRun, run⟩ := except_bind_success run + obtain ⟨b', bRun, run⟩ := except_bind_success run + cases run + obtain ⟨vA, vAReads, vASame⟩ := hA + ⟨fun x y hx hy => support.faithful x y hx.lam_ty hy.lam_ty, + fun x hx => support.bounded x hx.lam_ty⟩ aReads aRun + obtain ⟨vb, vbReads, vbSame⟩ := hb + ⟨fun x y hx hy => support.faithful x y hx.lam_body hy.lam_body, + fun x hx => support.bounded x hx.lam_body⟩ bReads bRun + refine ⟨.lam vA vb, ?_, .lam vASame vbSame⟩ + change readExpr? resolve (.lam name bi A' b' _) = _ + simp [readExpr?, vAReads, vbReads] + | all name bi domain body info hA hb => + rw [readExpr?] at reading + obtain ⟨A, aReads, reading⟩ := option_bind_success reading + obtain ⟨b, bReads, reading⟩ := option_bind_success reading + cases reading + rw [KExpr.instUnivSpec] at run + obtain ⟨A', aRun, run⟩ := except_bind_success run + obtain ⟨b', bRun, run⟩ := except_bind_success run + cases run + obtain ⟨vA, vAReads, vASame⟩ := hA + ⟨fun x y hx hy => support.faithful x y hx.all_ty hy.all_ty, + fun x hx => support.bounded x hx.all_ty⟩ aReads aRun + obtain ⟨vb, vbReads, vbSame⟩ := hb + ⟨fun x y hx hy => support.faithful x y hx.all_body hy.all_body, + fun x hx => support.bounded x hx.all_body⟩ bReads bRun + refine ⟨.forallE vA vb, ?_, .forallE vASame vbSame⟩ + change readExpr? resolve (.all name bi A' b' _) = _ + simp [readExpr?, vAReads, vbReads] + | letE name domain value body nonDep info hA hv hb => + rw [readExpr?] at reading + obtain ⟨A, aReads, reading⟩ := option_bind_success reading + obtain ⟨value, valueReads, reading⟩ := option_bind_success reading + obtain ⟨body, bodyReads, reading⟩ := option_bind_success reading + cases reading + rw [KExpr.instUnivSpec] at run + obtain ⟨A', aRun, run⟩ := except_bind_success run + obtain ⟨v', vRun, run⟩ := except_bind_success run + obtain ⟨b', bRun, run⟩ := except_bind_success run + cases run + obtain ⟨vA, vAReads, _⟩ := hA + ⟨fun x y hx hy => support.faithful x y hx.letE_ty hy.letE_ty, + fun x hx => support.bounded x hx.letE_ty⟩ aReads aRun + obtain ⟨vv, vvReads, vvSame⟩ := hv + ⟨fun x y hx hy => support.faithful x y hx.letE_val hy.letE_val, + fun x hx => support.bounded x hx.letE_val⟩ valueReads vRun + obtain ⟨vb, vbReads, vbSame⟩ := hb + ⟨fun x y hx hy => support.faithful x y hx.letE_body hy.letE_body, + fun x hx => support.bounded x hx.letE_body⟩ bodyReads bRun + refine ⟨vb.inst vv, ?_, ?_⟩ + · change readExpr? resolve (.letE name A' v' b' nonDep _) = _ + simp [readExpr?, vAReads, vvReads, vbReads] + · rw [VExpr.instL_inst] + exact vbSame.inst vvSame + | prj id index value info ih => + cases resolved : resolve id.addr with + | none => simp [readExpr?, resolved] at reading + | some ref => + rw [readExpr?, resolved] at reading + change (readExpr? resolve value).bind + (fun value => some (.proj ref index.toNat value)) = some source at reading + obtain ⟨source, sourceReads, reading⟩ := option_bind_success reading + cases reading + rw [KExpr.instUnivSpec] at run + obtain ⟨value', valueRun, run⟩ := except_bind_success run + cases run + obtain ⟨output, outputReads, outputSame⟩ := ih + ⟨fun x y hx hy => support.faithful x y hx.prj hy.prj, + fun x hx => support.bounded x hx.prj⟩ sourceReads valueRun + refine ⟨.proj ref index.toNat output, ?_, .proj ref index.toNat outputSame⟩ + change readExpr? resolve (.prj id index value' _) = _ + simp [readExpr?, resolved, outputReads] + +/-- Runtime resources for the memoized walker, all on its actual finite support. -/ +structure UniverseInstantiationSupport (before : TcState .anon) + (term : KExpr .anon) (arguments : Array (KUniv .anon)) : Prop where + coherent : before.env.intern.WF + faithful : KExpr.CollisionFree fun candidate => before.env.intern.ExprSupport candidate ∨ + KExpr.InstUnivReach arguments term candidate + levels : UniverseSubstitutionSupport arguments term + +/-- Connect the actual memoized and interned execution to model substitution. +The empty-argument shortcut requires scope because production skips its usual +parameter-range checks on that path. -/ +theorem instantiateUnivParams_readExpr? {resolve : Address → Option (ConstRef β)} + {arguments : Array (KUniv .anon)} {term result : KExpr .anon} {source : VExpr β} + {before after : TcState .anon} + (support : UniverseInstantiationSupport before term arguments) + (scope : source.LevelWF arguments.size) + (reading : readExpr? resolve term = some source) + (run : TcM.instantiateUnivParams term arguments before = .ok result after) : + ∃ output, readExpr? resolve result = some output ∧ + VExpr.LevelEquivalent (source.instL (arguments.toList.map readLevel)) output := by + have post := TcM.instantiateUnivParams_wf support.faithful + (fun _ h => Or.inr h) ⟨support.coherent, fun _ h => Or.inl h⟩ + rw [run] at post + have spec := post.2.1 + by_cases empty : arguments.isEmpty = true + · have args : arguments = #[] := Array.isEmpty_iff.mp empty + subst arguments + change Except.ok term = .ok result at spec + cases spec + refine ⟨source, reading, ?_⟩ + simpa only [Array.toList_empty, List.map_nil, scope.instL_nil] using + VExpr.LevelEquivalent.refl source + · rw [KExpr.instantiateUnivParamsSpec, if_neg empty] at spec + exact instUnivSpec_readExpr? support.levels reading spec + +/-- An annotated reading of the returned tree, preserving all binder conditions +after their model-side universe substitution. -/ +theorem instantiateUnivParams_readAnnotated {resolve : Address → Option (ConstRef β)} + {arguments : Array (KUniv .anon)} {term result : KExpr .anon} {source : AExpr β} + {before after : TcState .anon} + (support : UniverseInstantiationSupport before term arguments) + (scope : source.erase.LevelWF arguments.size) + (reading : readExpr? resolve term = some source.erase) + (run : TcM.instantiateUnivParams term arguments before = .ok result after) : + ∃ output : AExpr β, readExpr? resolve result = some output.erase ∧ + AExpr.LevelEquivalent (source.instL (arguments.toList.map readLevel)) output := by + obtain ⟨raw, rawReads, same⟩ := instantiateUnivParams_readExpr? support scope reading run + rw [← AExpr.erase_instL] at same + obtain ⟨output, erased, equivalent⟩ := AExpr.reannotate_levels _ same + exact ⟨output, erased ▸ rawReads, equivalent⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/InstL.lean b/Ix/Kernel/Verify/InstL.lean index e80a4eb37..71d9a77a2 100644 --- a/Ix/Kernel/Verify/InstL.lean +++ b/Ix/Kernel/Verify/InstL.lean @@ -1,5 +1,5 @@ import Ix.Kernel.Verify.Trans -import Ix.Kernel.Verify.InstUniv +import Ix.Kernel.Verify.UniverseSupport open Ix.Theory (VLevel) @@ -26,108 +26,6 @@ namespace Ix.Kernel open Ix.Theory.Named (VExpr VEnv) -/-! ### Level occurrences and the reach set -/ - -/-- The levels of an expression: `sort` payloads and `const` level - arguments, through all subterms. -/ -inductive KExpr.HasLevel {m : Mode} : KExpr m → KUniv m → Prop - | sort {u : KUniv m} {md : ExprInfo m} : HasLevel (.sort u md) u - | const {id : KId m} {us : Array (KUniv m)} {md : ExprInfo m} - {u : KUniv m} : - u ∈ us → HasLevel (.const id us md) u - | app_f {f a : KExpr m} {md : ExprInfo m} {u : KUniv m} : - HasLevel f u → HasLevel (.app f a md) u - | app_a {f a : KExpr m} {md : ExprInfo m} {u : KUniv m} : - HasLevel a u → HasLevel (.app f a md) u - | lam_ty {n : m.F Name} {bi : m.F Lean.BinderInfo} {ty body : KExpr m} - {md : ExprInfo m} {u : KUniv m} : - HasLevel ty u → HasLevel (.lam n bi ty body md) u - | lam_body {n : m.F Name} {bi : m.F Lean.BinderInfo} - {ty body : KExpr m} {md : ExprInfo m} {u : KUniv m} : - HasLevel body u → HasLevel (.lam n bi ty body md) u - | all_ty {n : m.F Name} {bi : m.F Lean.BinderInfo} {ty body : KExpr m} - {md : ExprInfo m} {u : KUniv m} : - HasLevel ty u → HasLevel (.all n bi ty body md) u - | all_body {n : m.F Name} {bi : m.F Lean.BinderInfo} - {ty body : KExpr m} {md : ExprInfo m} {u : KUniv m} : - HasLevel body u → HasLevel (.all n bi ty body md) u - | letE_ty {n : m.F Name} {ty val body : KExpr m} {nd : Bool} - {md : ExprInfo m} {u : KUniv m} : - HasLevel ty u → HasLevel (.letE n ty val body nd md) u - | letE_val {n : m.F Name} {ty val body : KExpr m} {nd : Bool} - {md : ExprInfo m} {u : KUniv m} : - HasLevel val u → HasLevel (.letE n ty val body nd md) u - | letE_body {n : m.F Name} {ty val body : KExpr m} {nd : Bool} - {md : ExprInfo m} {u : KUniv m} : - HasLevel body u → HasLevel (.letE n ty val body nd md) u - | prj {id : KId m} {field : UInt64} {val : KExpr m} {md : ExprInfo m} - {u : KUniv m} : - HasLevel val u → HasLevel (.prj id field val md) u - -/-- Level-side reach set of the instantiation walk on `e`: everything - `substUniv us` can address-compare while rewriting `e`'s levels. - Finite and spec-determined (union of `SubstUnivReach` over the - levels of `e`) — never closed under constructors. -/ -def KExpr.LevelReach {m : Mode} (us : Array (KUniv m)) (e : KExpr m) - (x : KUniv m) : Prop := - ∃ u, KExpr.HasLevel e u ∧ KUniv.SubstUnivReach us u x - -namespace KExpr.LevelReach - -variable {m : Mode} {us : Array (KUniv m)} {x : KUniv m} - -theorem app_f {f a : KExpr m} {md : ExprInfo m} - (h : LevelReach us f x) : LevelReach us (.app f a md) x := - let ⟨u, hu, hr⟩ := h; ⟨u, .app_f hu, hr⟩ - -theorem app_a {f a : KExpr m} {md : ExprInfo m} - (h : LevelReach us a x) : LevelReach us (.app f a md) x := - let ⟨u, hu, hr⟩ := h; ⟨u, .app_a hu, hr⟩ - -theorem lam_ty {n : m.F Name} {bi : m.F Lean.BinderInfo} - {ty body : KExpr m} {md : ExprInfo m} - (h : LevelReach us ty x) : LevelReach us (.lam n bi ty body md) x := - let ⟨u, hu, hr⟩ := h; ⟨u, .lam_ty hu, hr⟩ - -theorem lam_body {n : m.F Name} {bi : m.F Lean.BinderInfo} - {ty body : KExpr m} {md : ExprInfo m} - (h : LevelReach us body x) : - LevelReach us (.lam n bi ty body md) x := - let ⟨u, hu, hr⟩ := h; ⟨u, .lam_body hu, hr⟩ - -theorem all_ty {n : m.F Name} {bi : m.F Lean.BinderInfo} - {ty body : KExpr m} {md : ExprInfo m} - (h : LevelReach us ty x) : LevelReach us (.all n bi ty body md) x := - let ⟨u, hu, hr⟩ := h; ⟨u, .all_ty hu, hr⟩ - -theorem all_body {n : m.F Name} {bi : m.F Lean.BinderInfo} - {ty body : KExpr m} {md : ExprInfo m} - (h : LevelReach us body x) : - LevelReach us (.all n bi ty body md) x := - let ⟨u, hu, hr⟩ := h; ⟨u, .all_body hu, hr⟩ - -theorem letE_ty {n : m.F Name} {ty val body : KExpr m} {nd : Bool} - {md : ExprInfo m} (h : LevelReach us ty x) : - LevelReach us (.letE n ty val body nd md) x := - let ⟨u, hu, hr⟩ := h; ⟨u, .letE_ty hu, hr⟩ - -theorem letE_val {n : m.F Name} {ty val body : KExpr m} {nd : Bool} - {md : ExprInfo m} (h : LevelReach us val x) : - LevelReach us (.letE n ty val body nd md) x := - let ⟨u, hu, hr⟩ := h; ⟨u, .letE_val hu, hr⟩ - -theorem letE_body {n : m.F Name} {ty val body : KExpr m} {nd : Bool} - {md : ExprInfo m} (h : LevelReach us body x) : - LevelReach us (.letE n ty val body nd md) x := - let ⟨u, hu, hr⟩ := h; ⟨u, .letE_body hu, hr⟩ - -theorem prj {id : KId m} {field : UInt64} {val : KExpr m} - {md : ExprInfo m} (h : LevelReach us val x) : - LevelReach us (.prj id field val md) x := - let ⟨u, hu, hr⟩ := h; ⟨u, .prj hu, hr⟩ - -end KExpr.LevelReach - /-! ### `Except`-valued `mapM` decomposition -/ private theorem list_mapM_ok {ε α β : Type _} {f : α → Except ε β} : diff --git a/Ix/Kernel/Verify/UniverseSupport.lean b/Ix/Kernel/Verify/UniverseSupport.lean new file mode 100644 index 000000000..4e63ad9b3 --- /dev/null +++ b/Ix/Kernel/Verify/UniverseSupport.lean @@ -0,0 +1,116 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.InstUniv + +/-! Universe occurrences and finite substitution support shared by the named +translation and the direct set-theoretic refinement. -/ + +namespace Ix.Kernel + +/-! ### Level occurrences and the reach set -/ + +/-- The levels of an expression: `sort` payloads and `const` level + arguments, through all subterms. -/ +inductive KExpr.HasLevel {m : Mode} : KExpr m → KUniv m → Prop + | sort {u : KUniv m} {md : ExprInfo m} : HasLevel (.sort u md) u + | const {id : KId m} {us : Array (KUniv m)} {md : ExprInfo m} + {u : KUniv m} : + u ∈ us → HasLevel (.const id us md) u + | app_f {f a : KExpr m} {md : ExprInfo m} {u : KUniv m} : + HasLevel f u → HasLevel (.app f a md) u + | app_a {f a : KExpr m} {md : ExprInfo m} {u : KUniv m} : + HasLevel a u → HasLevel (.app f a md) u + | lam_ty {n : m.F Name} {bi : m.F Lean.BinderInfo} {ty body : KExpr m} + {md : ExprInfo m} {u : KUniv m} : + HasLevel ty u → HasLevel (.lam n bi ty body md) u + | lam_body {n : m.F Name} {bi : m.F Lean.BinderInfo} + {ty body : KExpr m} {md : ExprInfo m} {u : KUniv m} : + HasLevel body u → HasLevel (.lam n bi ty body md) u + | all_ty {n : m.F Name} {bi : m.F Lean.BinderInfo} {ty body : KExpr m} + {md : ExprInfo m} {u : KUniv m} : + HasLevel ty u → HasLevel (.all n bi ty body md) u + | all_body {n : m.F Name} {bi : m.F Lean.BinderInfo} + {ty body : KExpr m} {md : ExprInfo m} {u : KUniv m} : + HasLevel body u → HasLevel (.all n bi ty body md) u + | letE_ty {n : m.F Name} {ty val body : KExpr m} {nd : Bool} + {md : ExprInfo m} {u : KUniv m} : + HasLevel ty u → HasLevel (.letE n ty val body nd md) u + | letE_val {n : m.F Name} {ty val body : KExpr m} {nd : Bool} + {md : ExprInfo m} {u : KUniv m} : + HasLevel val u → HasLevel (.letE n ty val body nd md) u + | letE_body {n : m.F Name} {ty val body : KExpr m} {nd : Bool} + {md : ExprInfo m} {u : KUniv m} : + HasLevel body u → HasLevel (.letE n ty val body nd md) u + | prj {id : KId m} {field : UInt64} {val : KExpr m} {md : ExprInfo m} + {u : KUniv m} : + HasLevel val u → HasLevel (.prj id field val md) u + +/-- Level-side reach set of the instantiation walk on `e`: everything + `substUniv us` can address-compare while rewriting `e`'s levels. + Finite and spec-determined (union of `SubstUnivReach` over the + levels of `e`) — never closed under constructors. -/ +def KExpr.LevelReach {m : Mode} (us : Array (KUniv m)) (e : KExpr m) + (x : KUniv m) : Prop := + ∃ u, KExpr.HasLevel e u ∧ KUniv.SubstUnivReach us u x + +namespace KExpr.LevelReach + +variable {m : Mode} {us : Array (KUniv m)} {x : KUniv m} + +theorem app_f {f a : KExpr m} {md : ExprInfo m} + (h : LevelReach us f x) : LevelReach us (.app f a md) x := + let ⟨u, hu, hr⟩ := h; ⟨u, .app_f hu, hr⟩ + +theorem app_a {f a : KExpr m} {md : ExprInfo m} + (h : LevelReach us a x) : LevelReach us (.app f a md) x := + let ⟨u, hu, hr⟩ := h; ⟨u, .app_a hu, hr⟩ + +theorem lam_ty {n : m.F Name} {bi : m.F Lean.BinderInfo} + {ty body : KExpr m} {md : ExprInfo m} + (h : LevelReach us ty x) : LevelReach us (.lam n bi ty body md) x := + let ⟨u, hu, hr⟩ := h; ⟨u, .lam_ty hu, hr⟩ + +theorem lam_body {n : m.F Name} {bi : m.F Lean.BinderInfo} + {ty body : KExpr m} {md : ExprInfo m} + (h : LevelReach us body x) : + LevelReach us (.lam n bi ty body md) x := + let ⟨u, hu, hr⟩ := h; ⟨u, .lam_body hu, hr⟩ + +theorem all_ty {n : m.F Name} {bi : m.F Lean.BinderInfo} + {ty body : KExpr m} {md : ExprInfo m} + (h : LevelReach us ty x) : LevelReach us (.all n bi ty body md) x := + let ⟨u, hu, hr⟩ := h; ⟨u, .all_ty hu, hr⟩ + +theorem all_body {n : m.F Name} {bi : m.F Lean.BinderInfo} + {ty body : KExpr m} {md : ExprInfo m} + (h : LevelReach us body x) : + LevelReach us (.all n bi ty body md) x := + let ⟨u, hu, hr⟩ := h; ⟨u, .all_body hu, hr⟩ + +theorem letE_ty {n : m.F Name} {ty val body : KExpr m} {nd : Bool} + {md : ExprInfo m} (h : LevelReach us ty x) : + LevelReach us (.letE n ty val body nd md) x := + let ⟨u, hu, hr⟩ := h; ⟨u, .letE_ty hu, hr⟩ + +theorem letE_val {n : m.F Name} {ty val body : KExpr m} {nd : Bool} + {md : ExprInfo m} (h : LevelReach us val x) : + LevelReach us (.letE n ty val body nd md) x := + let ⟨u, hu, hr⟩ := h; ⟨u, .letE_val hu, hr⟩ + +theorem letE_body {n : m.F Name} {ty val body : KExpr m} {nd : Bool} + {md : ExprInfo m} (h : LevelReach us body x) : + LevelReach us (.letE n ty val body nd md) x := + let ⟨u, hu, hr⟩ := h; ⟨u, .letE_body hu, hr⟩ + +theorem prj {id : KId m} {field : UInt64} {val : KExpr m} + {md : ExprInfo m} (h : LevelReach us val x) : + LevelReach us (.prj id field val md) x := + let ⟨u, hu, hr⟩ := h; ⟨u, .prj hu, hr⟩ + +end KExpr.LevelReach + +end Ix.Kernel + diff --git a/Ix/Theory/Model/LevelCongruence.lean b/Ix/Theory/Model/LevelCongruence.lean new file mode 100644 index 000000000..3662d96b5 --- /dev/null +++ b/Ix/Theory/Model/LevelCongruence.lean @@ -0,0 +1,224 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Judgment + +/-! +# Replacing universe levels by equivalent levels + +Kernel universe substitution uses simplifying constructors. Its result can +therefore differ from structural substitution while denoting the same type. +The relations below retain every term constructor, reference, index, and +binder annotation. Only universe expressions may change, and their values +must agree under every universe valuation. + +This congruence preserves hereditary validity as well as interpretation. +Consequently it transports typing without assuming that an arbitrary +semantically equal expression is a valid type. +-/ + +namespace Ix.Theory + +universe u v +variable {β : Type u} + +namespace VExpr + +/-- Structural equality except for semantically equivalent universe levels. -/ +inductive LevelEquivalent : VExpr β → VExpr β → Prop + | bvar (index) : LevelEquivalent (.bvar index) (.bvar index) + | sort {left right} (levels : left ≈ right) : + LevelEquivalent (.sort left) (.sort right) + | const (ref) {left right} + (levels : ∀ valuation, left.map (VLevel.eval valuation) = + right.map (VLevel.eval valuation)) : + LevelEquivalent (.const ref left) (.const ref right) + | app {f f' a a'} (fn : LevelEquivalent f f') (arg : LevelEquivalent a a') : + LevelEquivalent (.app f a) (.app f' a') + | lam {A A' b b'} (domain : LevelEquivalent A A') (body : LevelEquivalent b b') : + LevelEquivalent (.lam A b) (.lam A' b') + | forallE {A A' B B'} (domain : LevelEquivalent A A') (body : LevelEquivalent B B') : + LevelEquivalent (.forallE A B) (.forallE A' B') + | proj (ref index) {e e'} (major : LevelEquivalent e e') : + LevelEquivalent (.proj ref index e) (.proj ref index e') + | natLit (value) : LevelEquivalent (.natLit value) (.natLit value) + +namespace LevelEquivalent + +theorem refl (e : VExpr β) : LevelEquivalent e e := by + induction e with + | bvar i => exact .bvar i + | sort l => exact .sort (VLevel.equiv_def.mpr fun _ => rfl) + | const r ls => exact .const r fun _ => rfl + | app _ _ hf ha => exact .app hf ha + | lam _ _ hA hb => exact .lam hA hb + | forallE _ _ hA hB => exact .forallE hA hB + | proj r i _ he => exact .proj r i he + | natLit n => exact .natLit n + +theorem liftN {e e' : VExpr β} (h : LevelEquivalent e e') (n k : Nat) : + LevelEquivalent (e.liftN n k) (e'.liftN n k) := by + induction h generalizing k with + | bvar i => exact .bvar _ + | sort h => exact .sort h + | const r h => exact .const r h + | app _ _ hf ha => exact .app (hf k) (ha k) + | lam _ _ hA hb => exact .lam (hA k) (hb (k + 1)) + | forallE _ _ hA hB => exact .forallE (hA k) (hB (k + 1)) + | proj r i _ he => exact .proj r i (he k) + | natLit n => exact .natLit n + +theorem inst {e e' a a' : VExpr β} (h : LevelEquivalent e e') + (ha : LevelEquivalent a a') (k : Nat := 0) : + LevelEquivalent (e.inst a k) (e'.inst a' k) := by + induction h generalizing k with + | bvar i => + by_cases hi : i < k + · simpa [VExpr.inst, instVar, hi] using LevelEquivalent.bvar (β := β) i + · by_cases hik : i = k + · simpa [VExpr.inst, instVar, hi, hik] using ha.liftN k 0 + · simpa [VExpr.inst, instVar, hi, hik] using LevelEquivalent.bvar (β := β) (i - 1) + | sort h => exact .sort h + | const r h => exact .const r h + | app _ _ hf hb => exact .app (hf k) (hb k) + | lam _ _ hA hb => exact .lam (hA k) (hb (k + 1)) + | forallE _ _ hA hB => exact .forallE (hA k) (hB (k + 1)) + | proj r i _ he => exact .proj r i (he k) + | natLit n => exact .natLit n + +end LevelEquivalent + +theorem instL_liftN (e : VExpr β) (levels : List VLevel) (n k : Nat) : + (e.liftN n k).instL levels = (e.instL levels).liftN n k := by + induction e generalizing k <;> simp_all [VExpr.liftN, VExpr.instL] + +theorem instL_inst (e a : VExpr β) (levels : List VLevel) (k : Nat := 0) : + (e.inst a k).instL levels = (e.instL levels).inst (a.instL levels) k := by + induction e generalizing k with + | bvar i => + by_cases hi : i < k + · simp [VExpr.inst, VExpr.instL, instVar, hi] + · by_cases hik : i = k <;> + simp [VExpr.inst, VExpr.instL, instVar, hi, hik, instL_liftN] + | _ => simp_all [VExpr.inst, VExpr.instL] + +/-- The production empty-argument fast path is justified by universe scope. -/ +theorem LevelWF.instL_nil {e : VExpr β} (scope : e.LevelWF 0) : + e.instL [] = e := by + induction e with + | bvar _ | natLit _ => rfl + | sort l => + exact congrArg VExpr.sort (VLevel.inst_id scope) + | const r ls => + simp only [VExpr.instL, VExpr.const.injEq, true_and] + simpa using List.map_congr_left (f := VLevel.inst []) (g := id) + (fun l hl => VLevel.inst_id (scope l hl)) + | app _ _ hf ha => simp only [VExpr.instL, hf scope.1, ha scope.2] + | lam _ _ hA hb => simp only [VExpr.instL, hA scope.1, hb scope.2] + | forallE _ _ hA hB => simp only [VExpr.instL, hA scope.1, hB scope.2] + | proj r i _ he => exact congrArg (VExpr.proj r i) (he scope) + +end VExpr + +namespace Model +namespace AExpr + +/-- Universe congruence with the same binder annotations at every occurrence. -/ +inductive LevelEquivalent : AExpr β → AExpr β → Prop + | bvar (index) : LevelEquivalent (.bvar index) (.bvar index) + | sort {left right} (levels : left ≈ right) : + LevelEquivalent (.sort left) (.sort right) + | const (ref) {left right} + (levels : ∀ valuation, left.map (VLevel.eval valuation) = + right.map (VLevel.eval valuation)) : + LevelEquivalent (.const ref left) (.const ref right) + | app {f f' a a'} (fn : LevelEquivalent f f') (arg : LevelEquivalent a a') : + LevelEquivalent (.app f a) (.app f' a') + | lam (condition) {A A' b b'} + (domain : LevelEquivalent A A') (body : LevelEquivalent b b') : + LevelEquivalent (.lam condition A b) (.lam condition A' b') + | forallE (condition) {A A' B B'} + (domain : LevelEquivalent A A') (body : LevelEquivalent B B') : + LevelEquivalent (.forallE condition A B) (.forallE condition A' B') + | proj (ref index) {e e'} (major : LevelEquivalent e e') : + LevelEquivalent (.proj ref index e) (.proj ref index e') + | natLit (value) : LevelEquivalent (.natLit value) (.natLit value) + +/-- Copy occurrence annotations to a structurally congruent expression. -/ +theorem reannotate_levels (e : AExpr β) {source : VExpr β} + (same : VExpr.LevelEquivalent e.erase source) : + ∃ result : AExpr β, result.erase = source ∧ LevelEquivalent e result := by + induction e generalizing source with + | bvar i => cases same; exact ⟨.bvar i, rfl, .bvar i⟩ + | sort l => cases same with | sort h => exact ⟨.sort _, rfl, .sort h⟩ + | const r ls => cases same with | const _ h => exact ⟨.const r _, rfl, .const r h⟩ + | app f a hf ha => + cases same with + | app sf sa => + obtain ⟨f', rfl, ef⟩ := hf sf + obtain ⟨a', rfl, ea⟩ := ha sa + exact ⟨.app f' a', rfl, .app ef ea⟩ + | lam p A b hA hb => + cases same with + | lam sA sb => + obtain ⟨A', rfl, eA⟩ := hA sA + obtain ⟨b', rfl, eb⟩ := hb sb + exact ⟨.lam p A' b', rfl, .lam p eA eb⟩ + | forallE p A B hA hB => + cases same with + | forallE sA sB => + obtain ⟨A', rfl, eA⟩ := hA sA + obtain ⟨B', rfl, eB⟩ := hB sB + exact ⟨.forallE p A' B', rfl, .forallE p eA eB⟩ + | proj r i e ih => + cases same with + | proj _ _ se => + obtain ⟨e', rfl, ee⟩ := ih se + exact ⟨.proj r i e', rfl, .proj r i ee⟩ + | natLit n => cases same; exact ⟨.natLit n, rfl, .natLit n⟩ + +namespace LevelEquivalent + +theorem interp {e e' : AExpr β} (same : LevelEquivalent e e') + {V : Type v} [SetTheory V] (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) : + Model.interp constants levels env e = Model.interp constants levels env e' := by + induction same generalizing env with + | bvar _ | natLit _ => rfl + | sort h => exact congrArg SetTheory.univ (VLevel.equiv_def.mp h levels) + | const r h => exact congrArg (constants r) (h levels) + | app _ _ hf ha => simp only [Model.interp, hf, ha] + | lam p _ _ hA hb | forallE p _ _ hA hb => + simp only [Model.interp, hA] + congr 1 + funext x + exact hb _ + | proj r i _ he => exact congrArg (projectValue i) (he env) + +theorem wellDenoted {e e' : AExpr β} (same : LevelEquivalent e e') + {V : Type v} [SetTheory V] (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) : + WellDenoted constants levels env e ↔ WellDenoted constants levels env e' := by + induction same generalizing env with + | bvar _ | sort _ | const _ _ | natLit _ => rfl + | app sf sa hf ha => + simp only [WellDenoted, hf, ha, sf.interp constants levels, sa.interp constants levels] + | lam p sA sb hA hb | forallE p sA sb hA hb => + simp only [WellDenoted, hA, hb, sA.interp constants levels, sb.interp constants levels] + | proj r i se he => exact he env + +/-- Type transport uses hereditary validity, not just equality of denotations. -/ +theorem typing {entries : Environment β} {context : Context β} {e A B : AExpr β} + (same : LevelEquivalent A B) (typed : TypingClaim.{u,v} entries context e A) : + TypingClaim.{u,v} entries context e B := by + intro V _ constants realizes levels env valid + obtain ⟨termValid, typeValid, member⟩ := typed V constants realizes levels env valid + exact ⟨termValid, (same.wellDenoted constants levels env).mp typeValid, + same.interp constants levels env ▸ member⟩ + +end LevelEquivalent +end AExpr +end Model +end Ix.Theory diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 03862215f..526856d6d 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -5,9 +5,10 @@ public import Ix.Kernel public import Tests.Ix.Kernel.IxonFixtures /-! -Production regressions for the atomic consistency fragment. These execute -the lazy serial driver on content-addressed Ixon declarations. The fragment's -theorems and resource premises are checked by `IxKernelConsistency`. +Production regressions for the atomic consistency fragment and polymorphic +constant inference. These execute the lazy loader, inference, and serial driver +on content-addressed Ixon declarations. The theorems and their resource +premises are checked separately by `IxKernelConsistency`. -/ namespace Tests.Kernel.Consistency @@ -85,6 +86,96 @@ private def cases : TestSeq := ++ test "atomic fragment: empty input returns an empty successful result" (allSucceeded {} 0) -public def suite : List TestSeq := [cases] +/-- A polymorphic identity axiom with a dependent function type. Inferring its +reference substitutes under binders without invoking binder inference. -/ +private def polymorphicIdentity : Ixon.Env × Address := + storeConst {} + ⟨.axio ⟨false, 1, .leanAll (.sort 0) (.leanAll (.var 0) (.var 1))⟩, + #[], #[], #[.var 0]⟩ + +private def levelOne : KUniv .anon := .mkSucc .mkZero +private def levelTwo : KUniv .anon := .mkSucc levelOne + +private def identityType : KExpr .anon := + .mkAll () () (.mkSort levelOne) (.mkAll () () (.mkVar 0 ()) (.mkVar 1 ())) + +/-- Two occurrences of the same instantiated reference exercise both the +level-argument array and the walker's per-call memo reuse. -/ +private def polymorphicReferences : Ixon.Env × Address × Address := Id.run do + let (env, carrier) := storeConst {} + ⟨.axio ⟨false, 1, .sort 0⟩, #[], #[], #[.var 0]⟩ + let (env, function) := storeConst env + ⟨.axio ⟨false, 1, .leanAll (.ref 0 #[0]) (.ref 0 #[0])⟩, + #[], #[carrier], #[.var 0]⟩ + return (env, function, carrier) + +private def polymorphicLet : Ixon.Env × Address := + storeConst {} + ⟨.axio ⟨false, 1, + .letE false (.sort 1) (.sort 0) (.leanAll (.var 0) (.var 1))⟩, + #[], #[], #[.var 0, .succ (.var 0)]⟩ + +private def simplifyingUniverses : Ixon.Env × Address := + storeConst {} + ⟨.axio ⟨false, 2, .sort 0⟩, #[], #[], + #[.imax (.max (.var 0) (.var 1)) (.var 1)]⟩ + +private def inferStored (fixture : Ixon.Env × Address) (arguments : Array (KUniv .anon)) + (expected : KExpr .anon) (inferOnly := false) (warmIntern := false) : Bool := + let action : TcM .anon (KExpr .anon) := do + if warmIntern then + let _ ← TcM.intern expected + TcM.infer (.mkConst ⟨fixture.2, ()⟩ arguments) + match action { TcState.newLazyAnon fixture.1 with inferOnly } with + | .ok type after => + type.addr == expected.addr && after.env.consts.size > 0 && + (if inferOnly then after.env.inferOnlyCache.size == 1 + else after.env.inferCache.size == 1) + | .error _ _ => false + +private def arityRejected (arguments : Array (KUniv .anon)) : Bool := + match TcM.infer (.mkConst ⟨polymorphicIdentity.2, ()⟩ arguments) + (TcState.newLazyAnon polymorphicIdentity.1) with + | .error (.univParamMismatch expected actual) after => + expected == 1 && actual == arguments.size && after.env.inferCache.isEmpty + | _ => false + +private def polymorphicCases : TestSeq := + test "polymorphic constant: lazy inference substitutes a dependent function type" + (inferStored polymorphicIdentity #[levelOne] identityType) + ++ test "polymorphic constant: inference-only policy records its own cache partition" + (inferStored polymorphicIdentity #[levelOne] identityType true) + ++ test "polymorphic constant: an existing interned result is reused on a cache miss" + (inferStored polymorphicIdentity #[levelOne] identityType false true) + ++ test "polymorphic constant: shared nested references retain their universe arguments" + (let (env, function, carrier) := polymorphicReferences + let type := KExpr.mkConst ⟨carrier, ()⟩ #[levelOne] + inferStored (env, function) #[levelOne] (.mkAll () () type type)) + ++ test "polymorphic constant: substitution preserves let and de Bruijn structure" + (inferStored polymorphicLet #[levelOne] + (.mkLet () (.mkSort levelTwo) (.mkSort levelOne) + (.mkAll () () (.mkVar 0 ()) (.mkVar 1 ())) false)) + ++ test "polymorphic constant: imax with zero simplifies the returned sort" + (inferStored simplifyingUniverses #[levelTwo, .mkZero] (.mkSort .mkZero)) + ++ test "polymorphic constant: swapped universe arguments change imax's result" + (inferStored simplifyingUniverses #[.mkZero, levelTwo] (.mkSort levelTwo)) + ++ test "polymorphic constant: missing universe arguments reject before cache insertion" + (arityRejected #[]) + ++ test "polymorphic constant: excess universe arguments reject before cache insertion" + (arityRejected #[levelOne, levelTwo]) + ++ test "universe instantiation: nonempty arguments reject an out-of-range parameter" + (match TcM.instantiateUnivParams (.mkSort (.mkParam 1 ())) #[.mkZero] + (TcState.ofEnvAnon {}) with + | .error (.univParamOutOfRange index count) _ => index == 1 && count == 1 + | _ => false : Bool) + -- This deliberately invalid input records why the refinement theorem needs + -- declaration scope: the production empty shortcut performs no range check. + ++ test "universe instantiation: empty shortcut requires an external scope invariant" + (match TcM.instantiateUnivParams (.mkSort (.mkParam 0 ())) #[] + (TcState.ofEnvAnon {}) with + | .ok (.sort (.param index _ _) _) _ => index == 0 + | _ => false : Bool) + +public def suite : List TestSeq := [cases, polymorphicCases] end Tests.Kernel.Consistency diff --git a/Tests/Theory/ImportManifest.lean b/Tests/Theory/ImportManifest.lean index 43e9e7b9c..f074c0b9f 100644 --- a/Tests/Theory/ImportManifest.lean +++ b/Tests/Theory/ImportManifest.lean @@ -6,7 +6,8 @@ SPDX-License-Identifier: MIT OR Apache-2.0 /-! Import provenance, recorded from the Lean4Ix working tree on 2026-09-11. The working tree contained the new, uncommitted consistency model; the base revision alone does not identify these inputs. Source hashes identify each file. -The selected file inventory is enforced by `Tests/Theory/Provenance.lean`. +The selected and Ix-authored file inventories are enforced separately by +`Tests/Theory/Provenance.lean`. The generated `Ix/Theory.lean` umbrella records its sole upstream import. -/ namespace Tests.Theory.ImportManifest @@ -14,6 +15,9 @@ namespace Tests.Theory.ImportManifest def lean4IxBaseRevision : String := "ab42e79e2a4e2615a3ca6ef983d510f374057a38" def conLecheRevision : String := "86cd20a65660d757cedc81561a44579099b565d0" +/-- New mathematical modules authored in Ix, with no imported source hash. -/ +def authored : Array String := #["Ix/Theory/Model/LevelCongruence.lean"] + structure SourceFile where source : String target : String diff --git a/Tests/Theory/Provenance.lean b/Tests/Theory/Provenance.lean index b430036a3..0c0c4fc79 100644 --- a/Tests/Theory/Provenance.lean +++ b/Tests/Theory/Provenance.lean @@ -25,15 +25,15 @@ def main (args : List String) : IO Unit := do let actual := ((← (FilePath.mk "Ix/Theory").walkDir).filter (·.extension == some "lean")).map (·.toString) let named := actual.filter (·.startsWith "Ix/Theory/Named/") sameFiles ((actual.filter (!·.startsWith "Ix/Theory/Named/")).push "Ix/Theory.lean") - (selected.map (·.target)) + (selected.map (·.target) ++ authored) sameFiles named ((Tests.Theory.NamedManifest.selected.map (·.target)).push "Ix/Theory/Named/Std/AxiomAudit.lean") need (← (FilePath.mk "Ix/Theory/Named/LICENSE").pathExists) "missing named-specification Apache license" need (← (FilePath.mk "Ix/Theory/Named/NOTICE").pathExists) "missing named-specification attribution" - for row in selected do - let contents ← IO.FS.readFile row.target + for path in selected.map (·.target) ++ authored do + let contents ← IO.FS.readFile path need (!(contents.contains "import Ix.Theory.Named")) - s!"set-model foundation depends on named checker proof support: {row.target}" + s!"set-model foundation depends on named checker proof support: {path}" let mut ports := #[] for directory in ["Ix/Theory/Model/SetTheory", "Ix/Theory/Model/SetModel"] do ports := ports ++ ((← (FilePath.mk directory).walkDir).filter (·.extension == some "lean")).map (·.toString) @@ -53,5 +53,5 @@ def main (args : List String) : IO Unit := do handle.putStr blob.stdout handle.flush need ((← sha256 path) == row.sourceSha256) s!"con-leche upstream hash mismatch: {row.source}" - IO.println s!"Theory provenance OK: {selected.size} selected source files; {conLeche.size} con-leche ports, exact content and notices." + IO.println s!"Theory provenance OK: {selected.size} selected source files; {authored.size} Ix-authored modules; {conLeche.size} con-leche ports, exact content and notices." IO.println s!"Named proof support: {named.size} local source files; separate from the set-model foundation." diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 820c8ed0d..c1a069d4c 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -37,6 +37,18 @@ increasing sequence of strongly inaccessible cardinals. - `inferUncached_sort_sound` interprets an actual successful execution of the production sort-inference branch. It proves the model typing postcondition for the returned type, including intern-table reuse. +- `instantiateUnivParams_readAnnotated` connects the actual memoized universe + substitution walker to model substitution for every readable expression. + Simplifying `max` and `imax` may change the returned syntax; the proof + preserves both interpretation and hereditary validity, including beneath + binders and through let substitution. The empty-argument shortcut is + justified by the source type's universe scope. +- `inferUncached_const_sound` and `infer_const_sound` derive model typing for + polymorphic constant references with arbitrary readable declaration types. + Production success supplies the universe-arity check. The premises retain + a well-formed model interface, agreement with the actual lazy-loaded + declaration, finite interning and level-substitution resources, and (for + `infer`) misses in both cache partitions at the computed key. - `ModelTyping.no_false` rules out a closed model-typed kernel expression at primitive False when its environment has been admitted by the certified interface and the set-theory assumption has an instance. @@ -96,8 +108,10 @@ the run. They supply no typing or checker-soundness premise. The proof extracts the validation, type-inference, theorem-guard, value-inference, and conversion steps from public success, then derives body typing to extend the preceding model. Automatic witness construction, lambdas, applications, -polymorphic instantiation, inductives, coordinated blocks, and other conversion -paths remain outside the fragment. +inductives, coordinated blocks, and other conversion paths remain outside the +fragment. The polymorphic constant-inference theorem above is a separate proof +increment; composing it into general declaration admission and environment +model extension remains to be done. `checkEnvAnon_atomic_represents_source` ties every source address to an interface with the type and body reached by production lookup. The independent @@ -148,7 +162,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 36 exact theorem boundaries. The production +The consistency target checks 51 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -158,6 +172,13 @@ The production roots additionally forbid the abstract `CheckSuccessSound`/`SupportedCheckFragment` interfaces and the independent certificate validator in their dependency closures. +The polymorphic inference and substitution roots retain only the two existing +expression/universe output-length proofs, alongside the standard Lean axioms. +Their model-side level congruence introduces no native proof dependency. +Kernel unit regressions cover lazy loading, both inference policies, interning +reuse, dependent function types, shared references, lets, `imax` simplification, +argument order, and rejection of wrong arities and out-of-range parameters. + ## Certified host adapters The certified source and claim adapters are maintained under @@ -182,6 +203,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | --- | --- | | Certified checker contracts | [`Ix/Kernel/Certified.lean`](../Ix/Kernel/Certified.lean), [`CertifiedClaims.lean`](../Ix/Kernel/CertifiedClaims.lean) | | Direct production refinement and its audit | [`Ix/Kernel/Verify/Consistency.lean`](../Ix/Kernel/Verify/Consistency.lean) | +| Polymorphic inference and universe substitution | [`Consistency/Constant.lean`](../Ix/Kernel/Verify/Consistency/Constant.lean), [`InstUniv.lean`](../Ix/Kernel/Verify/Consistency/InstUniv.lean), [`Model/LevelCongruence.lean`](../Ix/Theory/Model/LevelCongruence.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | | Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | | Host commands, receipts, and frozen regression evidence | [Certified checking guide](certified-checking.md) | @@ -195,3 +217,5 @@ fixtures used by the existing proofs. Source hashes and attribution are in [`Tests/Theory/NamedManifest.lean`](../Tests/Theory/NamedManifest.lean); the Apache license is preserved alongside the sources. The axiom-audit helper and direct production-fragment proofs are authored in Ix. +`Model/LevelCongruence.lean` is an Ix-authored mathematical addition, listed +separately from the imported files in the theory provenance manifest. From ec40b6fc3dcfc3bf3846507140d97bf252e11474 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 11:08:34 -0400 Subject: [PATCH 07/63] Extend production environment consistency to polymorphic specializations --- Ix/Kernel/Verify/Consistency.lean | 11 +- Ix/Kernel/Verify/Consistency/Audit.lean | 21 ++- Ix/Kernel/Verify/Consistency/Constant.lean | 70 +++++++-- Ix/Kernel/Verify/Consistency/Environment.lean | 8 +- Ix/Kernel/Verify/Consistency/InstUniv.lean | 138 +++++++++++++++--- Ix/Kernel/Verify/Consistency/Production.lean | 77 ++++++++-- Ix/Theory/Model/LevelCongruence.lean | 123 ++++++++++++++++ Tests/Ix/Kernel/Consistency.lean | 64 +++++++- docs/kernel-verification.md | 39 +++-- docs/theory.md | 5 +- 10 files changed, 490 insertions(+), 66 deletions(-) diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 121f7e437..447f78de8 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -20,9 +20,10 @@ import Ix.Kernel.Verify.Consistency.Audit This library connects production kernel operations to `Ix.Theory`. Each transport keeps its representation, arithmetic, and dependency assumptions explicit. A production `checkEnvAnon` fragment preserves models of its -axiom set for monomorphic aliases and closed sorts under the stated execution -resources. General checker soundness remains outside this fragment. -Separately, polymorphic constant inference refines the model for arbitrary -readable entry types, using the actual universe-instantiation walker and -explicit lookup, scope, and finite-support resources. +axiom set for monomorphic aliases, closed sorts, and monomorphic +specializations of polymorphic constants under the stated execution resources. +Constant inference supports arbitrary readable entry types, using the actual +universe-instantiation walker and explicit lookup and finite-support resources. +The returned type's scope and references justify declaration admission. +General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index f9e0c91f6..944c66c80 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -35,12 +35,16 @@ private def productionNative : Array Lean.Name := #[ private def atomicRoots : Array Lean.Name := #[ ``infer_uncached_success, ``AtomicInferenceSupport.typing, ``AtomicInferenceSupport.reads, ``AtomicInferenceSupport.output, - ``AtomicInferenceSupport.scopeAndReferences, ``AtomicInference.sound + ``AtomicInferenceSupport.scopeAndReferences, ``AtomicInference.sound, + ``DefinitionBodySupport.sound ] private def instantiationRoots : Array Lean.Name := #[ - ``instUnivSpec_readExpr?, ``instantiateUnivParams_readExpr?, - ``instantiateUnivParams_readAnnotated, ``inferUncached_const_sound, ``infer_const_sound + ``instUnivSpec_readExpr?, ``instUnivSpec_readExpr?_withScope, + ``instantiateUnivParams_readExpr?, ``instantiateUnivParams_readAnnotated, + ``instantiateUnivParams_readAnnotated_scoped, + ``inferUncached_const_refinement, ``infer_const_refinement, + ``inferUncached_const_sound, ``infer_const_sound ] private def productionRoots : Array Lean.Name := #[ @@ -84,13 +88,24 @@ def roots : Array RootAllowance := #[ { root := ``sort_conversion, standardAxioms := standard }, { root := ``inferUncached_sort_sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``Theory.VExpr.LevelWF.liftN, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.VExpr.LevelWF.inst, standardAxioms := #[``propext, ``Quot.sound] }, { root := ``Theory.VExpr.LevelEquivalent.refl, standardAxioms := #[``propext, ``Quot.sound] }, { root := ``Theory.VExpr.LevelEquivalent.liftN, standardAxioms := #[``propext] }, { root := ``Theory.VExpr.LevelEquivalent.inst, standardAxioms := #[``propext] }, { root := ``Theory.VExpr.instL_liftN, standardAxioms := #[``propext] }, { root := ``Theory.VExpr.instL_inst, standardAxioms := #[``propext] }, { root := ``Theory.VExpr.LevelWF.instL_nil, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.eq_of_erase_annotations, standardAxioms := #[``propext] }, + { root := ``Theory.Model.AExpr.Scope.instL, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.references_instL, standardAxioms := #[``propext, ``Quot.sound] }, { root := ``Theory.Model.AExpr.reannotate_levels, standardAxioms := #[``propext] }, + { root := ``Theory.Model.AExpr.LevelEquivalent.annotations, standardAxioms := #[``propext] }, + { root := ``Theory.Model.AExpr.LevelEquivalent.erase, standardAxioms := #[``propext] }, + { root := ``Theory.Model.AExpr.LevelEquivalent.scope, standardAxioms := #[``propext] }, + { root := ``Theory.Model.AExpr.LevelEquivalent.references, standardAxioms := #[``propext] }, + { root := ``Theory.Model.AExpr.LevelEquivalent.of_erase_annotations, + standardAxioms := #[``propext] }, { root := ``Theory.Model.AExpr.LevelEquivalent.interp, standardAxioms := standard }, { root := ``Theory.Model.AExpr.LevelEquivalent.wellDenoted, standardAxioms := standard }, { root := ``Theory.Model.AExpr.LevelEquivalent.typing, standardAxioms := standard } diff --git a/Ix/Kernel/Verify/Consistency/Constant.lean b/Ix/Kernel/Verify/Consistency/Constant.lean index babefe0b4..6b2a3b297 100644 --- a/Ix/Kernel/Verify/Consistency/Constant.lean +++ b/Ix/Kernel/Verify/Consistency/Constant.lean @@ -39,12 +39,11 @@ structure ConstantInferenceSupport {β : Type u} readExpr? resolve concrete.ty = some entry.type.erase ∧ UniverseInstantiationSupport loaded concrete.ty arguments -/-- A successful uncached production branch types its exact returned tree in -every model of the preceding interface. The runtime guard establishes arity; -the model environment's well-formedness supplies the declaration type's scope. -/ -theorem inferUncached_const_sound {β : Type u} +/-- Refine the returned type, including its arity, scope, and references. +The only scope premise concerns the preceding interface and actual arguments. -/ +theorem inferUncached_const_refinement {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} - {context : Model.Context β} {id : KId .anon} {arguments : Array (KUniv .anon)} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} {methods : Methods .anon} {before after : TcState .anon} {type : KExpr .anon} @@ -52,7 +51,11 @@ theorem inferUncached_const_sound {β : Type u} (wellFormed : entries.WF) (accepted : RecM.inferUncached inferRec inferOnly (.const id arguments info) methods before = .ok type after) : - ModelTyping.{u,v} resolve entries context (.const id arguments info) type := by + ∃ output : AExpr β, readExpr? resolve type = some output.erase ∧ + AExpr.LevelEquivalent (entry.type.instL (arguments.toList.map readLevel)) output ∧ + (arguments.toList.map readLevel).length = entry.universes ∧ + (∀ n, (∀ level ∈ arguments, (readLevel level).WF n) → output.Scope n 0) ∧ + output.references = entry.type.references := by change (RecM.inferUncached inferRec inferOnly (.const id arguments info)).run methods before = .ok type after at accepted unfold RecM.inferUncached at accepted @@ -68,18 +71,61 @@ theorem inferUncached_const_sound {β : Type u} change TcM.instantiateUnivParams concrete.ty arguments loaded = .ok type after at accepted have length : (arguments.toList.map readLevel).length = entry.universes := by simpa only [List.length_map, Array.length_toList] using arity.symm.trans count - have scope : entry.type.erase.LevelWF arguments.size := by + have scope : entry.type.Scope arguments.size 0 := by rw [← arity, count] - exact (wellFormed.typeScope ref entry support.found).erase.1 + exact wellFormed.typeScope ref entry support.found obtain ⟨output, outputReads, same⟩ := - instantiateUnivParams_readAnnotated resources scope reading accepted - refine ⟨.const ref (arguments.toList.map readLevel), output, ?_, outputReads, - same.typing (TypingClaim.const support.found length)⟩ - simp [readExpr?, support.resolved, AExpr.erase] + instantiateUnivParams_readAnnotated resources scope.erase.1 reading accepted + refine ⟨output, outputReads, same, length, ?_, + same.references.symm.trans (AExpr.references_instL entry.type _)⟩ + intro n argumentsWF + obtain ⟨closedOutput, scopedReads, scopedSame, scopedWF, _⟩ := + instantiateUnivParams_readAnnotated_scoped resources scope argumentsWF reading accepted + have equal := AExpr.eq_of_erase_annotations + (Option.some.inj (scopedReads.symm.trans outputReads)) + (scopedSame.annotations.symm.trans same.annotations) + exact equal ▸ scopedWF · simp only [bne_iff_ne] at accepted rw [if_pos arity] at accepted contradiction +/-- A successful uncached production branch types its exact returned tree in +every model of the preceding interface. The runtime guard establishes arity. -/ +theorem inferUncached_const_sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {context : Model.Context β} {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} + {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} + {methods : Methods .anon} {before after : TcState .anon} {type : KExpr .anon} + (support : ConstantInferenceSupport resolve entries before id arguments ref entry) + (wellFormed : entries.WF) + (accepted : RecM.inferUncached inferRec inferOnly (.const id arguments info) + methods before = .ok type after) : + ModelTyping.{u,v} resolve entries context (.const id arguments info) type := by + obtain ⟨output, reads, same, length, _, _⟩ := + inferUncached_const_refinement support wellFormed accepted + refine ⟨.const ref (arguments.toList.map readLevel), output, ?_, reads, + same.typing (TypingClaim.const support.found length)⟩ + simp [readExpr?, support.resolved, AExpr.erase] + +/-- The same structural refinement through the ordinary inference entry point. -/ +theorem infer_const_refinement {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} + {methods : Methods .anon} {before after : TcState .anon} {type : KExpr .anon} + (miss : UncachedInference before (.const id arguments info)) + (support : ConstantInferenceSupport resolve entries miss.keyed id arguments ref entry) + (wellFormed : entries.WF) + (accepted : RecM.infer (.const id arguments info) methods before = .ok type after) : + ∃ output : AExpr β, readExpr? resolve type = some output.erase ∧ + AExpr.LevelEquivalent (entry.type.instL (arguments.toList.map readLevel)) output ∧ + (arguments.toList.map readLevel).length = entry.universes ∧ + (∀ n, (∀ level ∈ arguments, (readLevel level).WF n) → output.Scope n 0) ∧ + output.references = entry.type.references := by + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + exact inferUncached_const_refinement support wellFormed run + /-- The ordinary production inference entry point, including its actual key lookup and final cache write, refines polymorphic constant typing on misses. -/ theorem infer_const_sound {β : Type u} diff --git a/Ix/Kernel/Verify/Consistency/Environment.lean b/Ix/Kernel/Verify/Consistency/Environment.lean index 9569ef804..1456669ce 100644 --- a/Ix/Kernel/Verify/Consistency/Environment.lean +++ b/Ix/Kernel/Verify/Consistency/Environment.lean @@ -289,19 +289,21 @@ private theorem AtomicDefinitionPlan.locations {β : Type u} [DecidableEq β] exact ⟨position, ⟨run.path⟩⟩ · exact ih candidate later -/-- An axiom has no value or computational equations in the initial model. -/ +/-- An axiom retains its declared universe arity and has no value or +computational equations in the initial model. -/ structure AxiomSpec (β : Type u) where id : KId .anon ref : ConstRef β isUnsafe : Bool + universes : UInt64 := 0 sourceType : KExpr .anon type : AExpr β def AxiomSpec.constant {β : Type u} (spec : AxiomSpec β) : KConst .anon := - .axio () () spec.isUnsafe 0 spec.sourceType + .axio () () spec.isUnsafe spec.universes spec.sourceType def AxiomSpec.entry {β : Type u} (spec : AxiomSpec β) : ConstantEntry β := - { universes := 0, type := spec.type, body := none } + { universes := spec.universes.toNat, type := spec.type, body := none } /-- Exactly the declared axiom interface, starting with no other entries. -/ def axiomEnvironment {β : Type u} [DecidableEq β] : List (AxiomSpec β) → Model.Environment β diff --git a/Ix/Kernel/Verify/Consistency/InstUniv.lean b/Ix/Kernel/Verify/Consistency/InstUniv.lean index f945aa0d2..2381f2840 100644 --- a/Ix/Kernel/Verify/Consistency/InstUniv.lean +++ b/Ix/Kernel/Verify/Consistency/InstUniv.lean @@ -94,6 +94,37 @@ private theorem array_substUniv_readLevels {arguments levels results : Array (KU (fun level member => faithful level (by simpa using member)) (fun level member => bounded level (by simpa using member)) +private theorem substUniv_readLevel_wf {arguments : Array (KUniv .anon)} {n : Nat} + (argumentsWF : ∀ level ∈ arguments, (readLevel level).WF n) + {level result : KUniv .anon} (run : TcM.substUniv level arguments = .ok result) : + (readLevel result).WF n := by + simpa only [readLevel_eq] using TcM.substUniv_wf + (fun level member => readLevel_eq level ▸ argumentsWF level member) run + +private theorem list_substUniv_readLevels_wf {arguments : Array (KUniv .anon)} {n : Nat} + (argumentsWF : ∀ level ∈ arguments, (readLevel level).WF n) : + ∀ {levels results : List (KUniv .anon)}, + levels.mapM (TcM.substUniv · arguments) = .ok results → + ∀ result ∈ results, (readLevel result).WF n := by + intro levels + induction levels with + | nil => intro results run; cases run; simp + | cons level rest ih => + intro results run + rw [List.mapM_cons] at run + cases first : TcM.substUniv level arguments with + | error err => rw [first] at run; contradiction + | ok result => + cases tail : rest.mapM (TcM.substUniv · arguments) with + | error err => rw [first, tail] at run; contradiction + | ok remaining => + rw [first, tail] at run + cases run + intro value member + rcases List.mem_cons.mp member with rfl | member + · exact substUniv_readLevel_wf argumentsWF first + · exact ih tail value member + private theorem except_bind_success {ε α γ : Type _} {action : Except ε α} {next : α → Except ε γ} {result : γ} (run : action.bind next = .ok result) : ∃ intermediate, action = .ok intermediate ∧ next intermediate = .ok result := by @@ -111,23 +142,24 @@ private theorem option_bind_success {α γ : Type _} {action : Option α} /-- Successful pure instantiation reads as model substitution, up to equivalent universe levels. All expression positions are covered; the reader's existing exclusions of free variables, strings, and unresolved references are retained. -/ -theorem instUnivSpec_readExpr? {resolve : Address → Option (ConstRef β)} +theorem instUnivSpec_readExpr?_withScope {resolve : Address → Option (ConstRef β)} {arguments : Array (KUniv .anon)} {term result : KExpr .anon} {source : VExpr β} (support : UniverseSubstitutionSupport arguments term) (reading : readExpr? resolve term = some source) (run : KExpr.instUnivSpec term arguments = .ok result) : ∃ output, readExpr? resolve result = some output ∧ - VExpr.LevelEquivalent (source.instL (arguments.toList.map readLevel)) output := by + VExpr.LevelEquivalent (source.instL (arguments.toList.map readLevel)) output ∧ + ∀ n, (∀ level ∈ arguments, (readLevel level).WF n) → output.LevelWF n := by induction term generalizing source result with | var index name info => cases run cases reading - exact ⟨_, rfl, .bvar _⟩ + exact ⟨_, rfl, .bvar _, fun _ _ => trivial⟩ | fvar _ _ _ | str _ _ _ => contradiction | nat value name info => cases run cases reading - exact ⟨_, rfl, .natLit _⟩ + exact ⟨_, rfl, .natLit _, fun _ _ => trivial⟩ | sort level info => cases reading rw [KExpr.instUnivSpec] at run @@ -136,7 +168,8 @@ theorem instUnivSpec_readExpr? {resolve : Address → Option (ConstRef β)} exact ⟨_, rfl, .sort (substUniv_readLevel substituted (fun left right hl hr => support.faithful left right ⟨level, .sort, hl⟩ ⟨level, .sort, hr⟩) - (fun value hv => support.bounded value ⟨level, .sort, hv⟩))⟩ + (fun value hv => support.bounded value ⟨level, .sort, hv⟩)), + fun _ argumentsWF => substUniv_readLevel_wf argumentsWF substituted⟩ | const id levels info => cases resolved : resolve id.addr with | none => simp [readExpr?, resolved] at reading @@ -146,13 +179,18 @@ theorem instUnivSpec_readExpr? {resolve : Address → Option (ConstRef β)} rw [KExpr.instUnivSpec] at run obtain ⟨values, substituted, run⟩ := except_bind_success run cases run - refine ⟨.const ref (values.toList.map readLevel), ?_, .const ref ?_⟩ + refine ⟨.const ref (values.toList.map readLevel), ?_, .const ref ?_, ?_⟩ · change readExpr? resolve (.const id values _) = _ simp [readExpr?, resolved] · exact array_substUniv_readLevels substituted (fun level member left right hl hr => support.faithful left right ⟨level, .const member, hl⟩ ⟨level, .const member, hr⟩) (fun level member value hv => support.bounded value ⟨level, .const member, hv⟩) + · intro n argumentsWF level member + obtain ⟨value, valueMember, rfl⟩ := List.mem_map.mp member + have listRun : levels.toList.mapM (TcM.substUniv · arguments) = + .ok values.toList := by rw [← Array.toList_mapM, substituted]; rfl + exact list_substUniv_readLevels_wf argumentsWF listRun value valueMember | app fn arg info hf ha => rw [readExpr?] at reading obtain ⟨f, fReads, reading⟩ := option_bind_success reading @@ -162,13 +200,14 @@ theorem instUnivSpec_readExpr? {resolve : Address → Option (ConstRef β)} obtain ⟨f', fRun, run⟩ := except_bind_success run obtain ⟨a', aRun, run⟩ := except_bind_success run cases run - obtain ⟨vf, vfReads, vfSame⟩ := hf + obtain ⟨vf, vfReads, vfSame, vfWF⟩ := hf ⟨fun x y hx hy => support.faithful x y hx.app_f hy.app_f, fun x hx => support.bounded x hx.app_f⟩ fReads fRun - obtain ⟨va, vaReads, vaSame⟩ := ha + obtain ⟨va, vaReads, vaSame, vaWF⟩ := ha ⟨fun x y hx hy => support.faithful x y hx.app_a hy.app_a, fun x hx => support.bounded x hx.app_a⟩ aReads aRun - refine ⟨.app vf va, ?_, .app vfSame vaSame⟩ + refine ⟨.app vf va, ?_, .app vfSame vaSame, + fun n h => ⟨vfWF n h, vaWF n h⟩⟩ change readExpr? resolve (.app f' a' _) = _ simp [readExpr?, vfReads, vaReads] | lam name bi domain body info hA hb => @@ -180,13 +219,14 @@ theorem instUnivSpec_readExpr? {resolve : Address → Option (ConstRef β)} obtain ⟨A', aRun, run⟩ := except_bind_success run obtain ⟨b', bRun, run⟩ := except_bind_success run cases run - obtain ⟨vA, vAReads, vASame⟩ := hA + obtain ⟨vA, vAReads, vASame, vAWF⟩ := hA ⟨fun x y hx hy => support.faithful x y hx.lam_ty hy.lam_ty, fun x hx => support.bounded x hx.lam_ty⟩ aReads aRun - obtain ⟨vb, vbReads, vbSame⟩ := hb + obtain ⟨vb, vbReads, vbSame, vbWF⟩ := hb ⟨fun x y hx hy => support.faithful x y hx.lam_body hy.lam_body, fun x hx => support.bounded x hx.lam_body⟩ bReads bRun - refine ⟨.lam vA vb, ?_, .lam vASame vbSame⟩ + refine ⟨.lam vA vb, ?_, .lam vASame vbSame, + fun n h => ⟨vAWF n h, vbWF n h⟩⟩ change readExpr? resolve (.lam name bi A' b' _) = _ simp [readExpr?, vAReads, vbReads] | all name bi domain body info hA hb => @@ -198,13 +238,14 @@ theorem instUnivSpec_readExpr? {resolve : Address → Option (ConstRef β)} obtain ⟨A', aRun, run⟩ := except_bind_success run obtain ⟨b', bRun, run⟩ := except_bind_success run cases run - obtain ⟨vA, vAReads, vASame⟩ := hA + obtain ⟨vA, vAReads, vASame, vAWF⟩ := hA ⟨fun x y hx hy => support.faithful x y hx.all_ty hy.all_ty, fun x hx => support.bounded x hx.all_ty⟩ aReads aRun - obtain ⟨vb, vbReads, vbSame⟩ := hb + obtain ⟨vb, vbReads, vbSame, vbWF⟩ := hb ⟨fun x y hx hy => support.faithful x y hx.all_body hy.all_body, fun x hx => support.bounded x hx.all_body⟩ bReads bRun - refine ⟨.forallE vA vb, ?_, .forallE vASame vbSame⟩ + refine ⟨.forallE vA vb, ?_, .forallE vASame vbSame, + fun n h => ⟨vAWF n h, vbWF n h⟩⟩ change readExpr? resolve (.all name bi A' b' _) = _ simp [readExpr?, vAReads, vbReads] | letE name domain value body nonDep info hA hv hb => @@ -218,16 +259,16 @@ theorem instUnivSpec_readExpr? {resolve : Address → Option (ConstRef β)} obtain ⟨v', vRun, run⟩ := except_bind_success run obtain ⟨b', bRun, run⟩ := except_bind_success run cases run - obtain ⟨vA, vAReads, _⟩ := hA + obtain ⟨vA, vAReads, _, _⟩ := hA ⟨fun x y hx hy => support.faithful x y hx.letE_ty hy.letE_ty, fun x hx => support.bounded x hx.letE_ty⟩ aReads aRun - obtain ⟨vv, vvReads, vvSame⟩ := hv + obtain ⟨vv, vvReads, vvSame, vvWF⟩ := hv ⟨fun x y hx hy => support.faithful x y hx.letE_val hy.letE_val, fun x hx => support.bounded x hx.letE_val⟩ valueReads vRun - obtain ⟨vb, vbReads, vbSame⟩ := hb + obtain ⟨vb, vbReads, vbSame, vbWF⟩ := hb ⟨fun x y hx hy => support.faithful x y hx.letE_body hy.letE_body, fun x hx => support.bounded x hx.letE_body⟩ bodyReads bRun - refine ⟨vb.inst vv, ?_, ?_⟩ + refine ⟨vb.inst vv, ?_, ?_, fun n h => (vbWF n h).inst (vvWF n h)⟩ · change readExpr? resolve (.letE name A' v' b' nonDep _) = _ simp [readExpr?, vAReads, vvReads, vbReads] · rw [VExpr.instL_inst] @@ -244,13 +285,24 @@ theorem instUnivSpec_readExpr? {resolve : Address → Option (ConstRef β)} rw [KExpr.instUnivSpec] at run obtain ⟨value', valueRun, run⟩ := except_bind_success run cases run - obtain ⟨output, outputReads, outputSame⟩ := ih + obtain ⟨output, outputReads, outputSame, outputWF⟩ := ih ⟨fun x y hx hy => support.faithful x y hx.prj hy.prj, fun x hx => support.bounded x hx.prj⟩ sourceReads valueRun - refine ⟨.proj ref index.toNat output, ?_, .proj ref index.toNat outputSame⟩ + refine ⟨.proj ref index.toNat output, ?_, .proj ref index.toNat outputSame, outputWF⟩ change readExpr? resolve (.prj id index value' _) = _ simp [readExpr?, resolved, outputReads] +/-- The unscoped refinement remains useful in arbitrary inference contexts. -/ +theorem instUnivSpec_readExpr? {resolve : Address → Option (ConstRef β)} + {arguments : Array (KUniv .anon)} {term result : KExpr .anon} {source : VExpr β} + (support : UniverseSubstitutionSupport arguments term) + (reading : readExpr? resolve term = some source) + (run : KExpr.instUnivSpec term arguments = .ok result) : + ∃ output, readExpr? resolve result = some output ∧ + VExpr.LevelEquivalent (source.instL (arguments.toList.map readLevel)) output := by + obtain ⟨output, reads, same, _⟩ := instUnivSpec_readExpr?_withScope support reading run + exact ⟨output, reads, same⟩ + /-- Runtime resources for the memoized walker, all on its actual finite support. -/ structure UniverseInstantiationSupport (before : TcState .anon) (term : KExpr .anon) (arguments : Array (KUniv .anon)) : Prop where @@ -302,4 +354,48 @@ theorem instantiateUnivParams_readAnnotated {resolve : Address → Option (Const obtain ⟨output, erased, equivalent⟩ := AExpr.reannotate_levels _ same exact ⟨output, erased ▸ rawReads, equivalent⟩ +/-- Scope and dependencies of the actual returned annotated type. Nonempty +substitution checks every source parameter; the empty shortcut uses source +scope. Neither syntactic property follows from semantic equivalence alone. -/ +theorem instantiateUnivParams_readAnnotated_scoped + {resolve : Address → Option (ConstRef β)} + {arguments : Array (KUniv .anon)} {term result : KExpr .anon} {source : AExpr β} + {before after : TcState .anon} {n depth : Nat} + (support : UniverseInstantiationSupport before term arguments) + (scope : source.Scope arguments.size depth) + (argumentsWF : ∀ level ∈ arguments, (readLevel level).WF n) + (reading : readExpr? resolve term = some source.erase) + (run : TcM.instantiateUnivParams term arguments before = .ok result after) : + ∃ output : AExpr β, readExpr? resolve result = some output.erase ∧ + AExpr.LevelEquivalent (source.instL (arguments.toList.map readLevel)) output ∧ + output.Scope n depth ∧ output.references = source.references := by + obtain ⟨output, reads, same⟩ := + instantiateUnivParams_readAnnotated support scope.erase.1 reading run + have args : ∀ level ∈ arguments.toList.map readLevel, level.WF n := by + intro level member + obtain ⟨value, valueMember, rfl⟩ := List.mem_map.mp member + exact argumentsWF value (by simpa using valueMember) + have outputWF : output.erase.LevelWF n := by + have post := TcM.instantiateUnivParams_wf support.faithful + (fun _ h => Or.inr h) ⟨support.coherent, fun _ h => Or.inl h⟩ + rw [run] at post + have spec := post.2.1 + by_cases empty : arguments.isEmpty = true + · have emptyArgs : arguments = #[] := Array.isEmpty_iff.mp empty + subst arguments + change Except.ok term = .ok result at spec + cases spec + have equal := Option.some.inj (reads.symm.trans reading) + rw [equal] + have substituted := (scope.instL args).erase.1 + simpa only [AExpr.erase_instL, Array.toList_empty, List.map_nil, + scope.erase.1.instL_nil] using substituted + · rw [KExpr.instantiateUnivParamsSpec, if_neg empty] at spec + obtain ⟨raw, rawReads, _, rawWF⟩ := + instUnivSpec_readExpr?_withScope support.levels reading spec + have equal := Option.some.inj (reads.symm.trans rawReads) + exact equal ▸ rawWF n argumentsWF + exact ⟨output, reads, same, same.scope (scope.instL args) outputWF, + same.references.symm.trans (AExpr.references_instL source _)⟩ + end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Production.lean b/Ix/Kernel/Verify/Consistency/Production.lean index b48ea8f97..0ae6cc111 100644 --- a/Ix/Kernel/Verify/Consistency/Production.lean +++ b/Ix/Kernel/Verify/Consistency/Production.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Driver -import Ix.Kernel.Verify.Consistency.Atomic +import Ix.Kernel.Verify.Consistency.Constant /-! # Standalone production declaration checks @@ -97,6 +97,68 @@ structure DefinitionInput where def DefinitionInput.constant (input : DefinitionInput) : KConst .anon := .defn () () input.kind input.safety input.hints 0 input.type input.value () input.block +/-- Closed sort/alias inference, or a specialization of an existing constant. +The declared type supplies only raw syntax and occurrence annotations. Its +typing, scope, and references are derived from successful production inference. -/ +inductive DefinitionBodySupport {β : Type u} + (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) + (before : TcState .anon) (declared : KExpr .anon) : + KExpr .anon → AExpr β → AExpr β → Type u + | atomic {term : KExpr .anon} {body type : AExpr β} + (inference : AtomicInference resolve entries before term body type) : + DefinitionBodySupport resolve entries before declared term body type + | specialization {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} + (misses : UncachedInference before (.const id arguments info)) + (support : ConstantInferenceSupport resolve entries misses.keyed id arguments ref entry) + (closed : ∀ level ∈ arguments, (readLevel level).WF 0) + (reading : readExpr? resolve declared = some type.erase) + (conditions : (entry.type.instL (arguments.toList.map readLevel)).annotations = + type.annotations) : + DefinitionBodySupport resolve entries before declared (.const id arguments info) + (.const ref (arguments.toList.map readLevel)) type + +theorem DefinitionBodySupport.sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {before after : TcState .anon} {term declared inferred : KExpr .anon} + {body type : AExpr β} {methods : Methods .anon} + (fragment : DefinitionBodySupport resolve entries before declared term body type) + (wellFormed : entries.WF) + (accepted : RecM.infer term methods before = .ok inferred after) + (faithful : inferred.AddrFaithful declared) (hashPath : (inferred == declared) = true) : + readExpr? resolve term = some body.erase ∧ + readExpr? resolve declared = some type.erase ∧ + body.Scope 0 0 ∧ type.Scope 0 0 ∧ + body.ReferencesIn entries ∧ type.ReferencesIn entries ∧ + TypingClaim.{u,v} entries [] body type := by + have hashReads := beq_readExpr? (resolve := resolve) faithful hashPath + cases fragment with + | atomic inference => + obtain ⟨valueReads, typeReads, typed⟩ := inference.sound accepted + obtain ⟨bodyScope, typeScope, bodyRefs, typeRefs⟩ := + inference.support.scopeAndReferences wellFormed + exact ⟨valueReads, hashReads.symm.trans typeReads, + bodyScope, typeScope, bodyRefs, typeRefs, typed⟩ + | specialization misses support closed reading conditions => + obtain ⟨output, reads, same, arity, scope, references⟩ := + infer_const_refinement misses support wellFormed accepted + have equal := AExpr.eq_of_erase_annotations + (Option.some.inj (reads.symm.trans (hashReads.trans reading))) + (same.annotations.symm.trans conditions) + refine ⟨?_, reading, ?_, equal ▸ scope 0 closed, ?_, ?_, + equal ▸ same.typing (TypingClaim.const support.found arity)⟩ + · simp [readExpr?, support.resolved, AExpr.erase] + · intro level member + obtain ⟨value, valueMember, rfl⟩ := List.mem_map.mp member + exact closed value (by simpa using valueMember) + · intro ref member + simp only [AExpr.references, List.mem_singleton] at member + subst ref + simp only [support.found, Option.isSome_some] + · intro ref member + rw [← equal, references] at member + exact wellFormed.typeReferences _ _ support.found ref member + /-- The execution prefix through value conversion. A successful member check also passes the subsequent safety checks. -/ structure DefinitionBodyTrace (input : DefinitionInput) (methods : Methods .anon) @@ -162,7 +224,7 @@ structure AtomicDefinitionRun {β : Type u} (resolve : Address → Option (Const (body type : AExpr β) where path : StandalonePrefix input.id before input.constant inference : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, - AtomicInference resolve entries trace.valueStart input.value body type + DefinitionBodySupport resolve entries trace.valueStart input.type input.value body type hashPath : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, (trace.inferredValue == input.type) = true faithful : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, @@ -183,22 +245,19 @@ theorem AtomicDefinitionRun.sound {β : Type u} body.ReferencesIn entries ∧ type.ReferencesIn entries ∧ TypingClaim.{u,v} entries [] body type := by obtain ⟨trace⟩ := definition_body_trace (fragment.path.member_success accepted) - obtain ⟨valueReads, typeReads, typed⟩ := (fragment.inference trace).sound trace.valueRun - have same := beq_readExpr? (resolve := resolve) + exact (fragment.inference trace).sound wellFormed trace.valueRun (fragment.faithful trace) (fragment.hashPath trace) - obtain ⟨bodyScope, typeScope, bodyRefs, typeRefs⟩ := - (fragment.inference trace).support.scopeAndReferences wellFormed - exact ⟨valueReads, same.symm.trans typeReads, bodyScope, typeScope, bodyRefs, typeRefs, typed⟩ /-- A fresh definition cannot justify its type through a self-reference: -its atomic value must reference the preceding interface. -/ +its value, at any universe arguments, must reference the preceding interface. -/ theorem AtomicDefinitionRun.no_self_alias {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {input : DefinitionInput} {before after : TcState .anon} {body type : AExpr β} (fragment : AtomicDefinitionRun resolve entries input before body type) (wellFormed : entries.WF) {ref : ConstRef β} (resolved : resolve input.id.addr = some ref) (fresh : entries ref = none) - {info : ExprInfo .anon} (self : input.value = .const input.id #[] info) + {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + (self : input.value = .const input.id arguments info) (accepted : TcM.checkConst input.id before = .ok () after) : False := by obtain ⟨reads, _, _, _, references, _, _⟩ := AtomicDefinitionRun.sound.{u,0} fragment wellFormed accepted diff --git a/Ix/Theory/Model/LevelCongruence.lean b/Ix/Theory/Model/LevelCongruence.lean index 3662d96b5..907b361ff 100644 --- a/Ix/Theory/Model/LevelCongruence.lean +++ b/Ix/Theory/Model/LevelCongruence.lean @@ -4,6 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Theory.Model.Judgment +import Ix.Theory.Model.Support /-! # Replacing universe levels by equivalent levels @@ -26,6 +27,22 @@ variable {β : Type u} namespace VExpr +theorem LevelWF.liftN {e : VExpr β} {n : Nat} (scope : e.LevelWF n) + (count cutoff : Nat) : (e.liftN count cutoff).LevelWF n := by + induction e generalizing cutoff <;> simp_all [VExpr.liftN, VExpr.LevelWF] + +theorem LevelWF.inst {e a : VExpr β} {n : Nat} + (scope : e.LevelWF n) (argument : a.LevelWF n) (cutoff : Nat := 0) : + (e.inst a cutoff).LevelWF n := by + induction e generalizing cutoff with + | bvar i => + by_cases hi : i < cutoff + · simp [VExpr.inst, instVar, hi, VExpr.LevelWF] + · by_cases he : i = cutoff + · simpa [VExpr.inst, instVar, hi, he] using argument.liftN cutoff 0 + · simp [VExpr.inst, instVar, hi, he, VExpr.LevelWF] + | _ => simp_all [VExpr.inst, VExpr.LevelWF] + /-- Structural equality except for semantically equivalent universe levels. -/ inductive LevelEquivalent : VExpr β → VExpr β → Prop | bvar (index) : LevelEquivalent (.bvar index) (.bvar index) @@ -125,6 +142,71 @@ end VExpr namespace Model namespace AExpr +/-- The binder conditions at their exact expression occurrences. -/ +def annotations : AExpr β → AnnotationTree + | .bvar _ | .sort _ | .const _ _ | .natLit _ => .leaf + | .app f a => .app f.annotations a.annotations + | .lam p A b => .lam p.toRaw A.annotations b.annotations + | .forallE p A B => .forallE p.toRaw A.annotations B.annotations + | .proj _ _ e => .proj e.annotations + +/-- Raw syntax and occurrence annotations uniquely determine a reading. -/ +theorem eq_of_erase_annotations {left right : AExpr β} + (shape : left.erase = right.erase) (conditions : left.annotations = right.annotations) : + left = right := by + induction left generalizing right with + | app f a hf ha => + cases right <;> simp [erase] at shape + next f' a' => + simp only [annotations, AnnotationTree.app.injEq] at conditions + rw [hf shape.1 conditions.1, ha shape.2 conditions.2] + | lam p A b hA hb => + cases right <;> simp [erase] at shape + next q A' b' => + simp only [annotations, AnnotationTree.lam.injEq] at conditions + have equal := Certified.PropWhen.toRaw_injective conditions.1 + subst q + rw [hA shape.1 conditions.2.1, hb shape.2 conditions.2.2] + | forallE p A b hA hb => + cases right <;> simp [erase] at shape + next q A' b' => + simp only [annotations, AnnotationTree.forallE.injEq] at conditions + have equal := Certified.PropWhen.toRaw_injective conditions.1 + subst q + rw [hA shape.1 conditions.2.1, hb shape.2 conditions.2.2] + | proj r i e he => + cases right <;> simp [erase] at shape + next r' i' e' => + obtain ⟨rfl, rfl, equal⟩ := shape + exact congrArg (AExpr.proj r i) (he equal (AnnotationTree.proj.inj conditions)) + | _ => cases right <;> simp_all [erase] + +theorem Scope.instL {e : AExpr β} {n depth target : Nat} {levels : List VLevel} + (scope : e.Scope n depth) (arguments : ∀ level ∈ levels, level.WF target) : + (e.instL levels).Scope target depth := by + have condition : ∀ p : Certified.PropWhen, p.WF n → + (Certified.instCondition levels p).WF target := by + intro p hp + apply hp.bind + intro i _ + apply Certified.zeroCondition_wf + exact VLevel.WF.inst (l := .param i) arguments + induction e generalizing depth with + | bvar _ | natLit _ => exact scope + | sort _ => exact VLevel.WF.inst arguments + | const r ls => + intro level member + obtain ⟨source, _, rfl⟩ := List.mem_map.mp member + exact VLevel.WF.inst arguments + | app _ _ hf ha => exact ⟨hf scope.1, ha scope.2⟩ + | lam p _ _ hA hb | forallE p _ _ hA hb => + exact ⟨condition p scope.1, hA scope.2.1, hb scope.2.2⟩ + | proj _ _ _ he => exact he scope + +theorem references_instL (e : AExpr β) (levels : List VLevel) : + (e.instL levels).references = e.references := by + induction e <;> simp_all [instL, references] + /-- Universe congruence with the same binder annotations at every occurrence. -/ inductive LevelEquivalent : AExpr β → AExpr β → Prop | bvar (index) : LevelEquivalent (.bvar index) (.bvar index) @@ -181,6 +263,47 @@ theorem reannotate_levels (e : AExpr β) {source : VExpr β} namespace LevelEquivalent +theorem annotations {e e' : AExpr β} (same : LevelEquivalent e e') : + e.annotations = e'.annotations := by + induction same <;> simp_all [AExpr.annotations] + +theorem erase {e e' : AExpr β} (same : LevelEquivalent e e') : + VExpr.LevelEquivalent e.erase e'.erase := by + induction same with + | bvar i => exact .bvar i + | sort h => exact .sort h + | const r h => exact .const r h + | app _ _ hf ha => exact .app hf ha + | lam _ _ _ hA hb => exact .lam hA hb + | forallE _ _ _ hA hb => exact .forallE hA hb + | proj r i _ he => exact .proj r i he + | natLit n => exact .natLit n + +/-- Equivalent levels need not have the same scope: the actual target levels +must separately be well scoped. Indices and binder annotations are preserved. -/ +theorem scope {e e' : AExpr β} {n depth : Nat} (same : LevelEquivalent e e') + (source : e.Scope n depth) (levels : e'.erase.LevelWF n) : e'.Scope n depth := by + induction same generalizing depth with + | bvar _ | natLit _ => exact source + | sort _ | const _ _ => exact levels + | app _ _ hf ha => exact ⟨hf source.1 levels.1, ha source.2 levels.2⟩ + | lam _ _ _ hA hb | forallE _ _ _ hA hb => + exact ⟨source.1, hA source.2.1 levels.1, hb source.2.2 levels.2⟩ + | proj _ _ _ he => exact he source levels + +theorem references {e e' : AExpr β} (same : LevelEquivalent e e') : + e.references = e'.references := by + induction same <;> simp_all [AExpr.references] + +/-- A fixed declared reading agrees when its raw tree is congruent and its +occurrence annotations match. These are syntactic premises, not typing. -/ +theorem of_erase_annotations {e e' : AExpr β} + (shape : VExpr.LevelEquivalent e.erase e'.erase) + (conditions : e.annotations = e'.annotations) : LevelEquivalent e e' := by + obtain ⟨output, erased, same⟩ := reannotate_levels e shape + have equal := eq_of_erase_annotations erased (same.annotations.symm.trans conditions) + exact equal ▸ same + theorem interp {e e' : AExpr β} (same : LevelEquivalent e e') {V : Type v} [SetTheory V] (constants : Assignment β V) (levels : List Nat) (env : Nat → V) : diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 526856d6d..9a067b61d 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -93,6 +93,30 @@ private def polymorphicIdentity : Ixon.Env × Address := ⟨.axio ⟨false, 1, .leanAll (.sort 0) (.leanAll (.var 0) (.var 1))⟩, #[], #[], #[.var 0]⟩ +/-- Specializations at Prop and Type, followed by ordinary aliases. The source +axiom stays polymorphic while each admitted definition is monomorphic. -/ +private def specializationEnvironment : Ixon.Env := Id.run do + let (env, identity) := polymorphicIdentity + let type := Ixon.Expr.leanAll (.sort 0) (.leanAll (.var 0) (.var 1)) + let (env, propIdentity) := storeConst env + ⟨.defn ⟨.defn, .safe, 0, type, .ref 0 #[0]⟩, #[], #[identity], #[.zero]⟩ + let (env, typeIdentity) := storeConst env + ⟨.defn ⟨.opaq, .safe, 0, type, .ref 0 #[0]⟩, #[], #[identity], #[.succ .zero]⟩ + let (env, _) := storeConst env + ⟨.defn ⟨.thm, .safe, 0, type, .ref 0 #[]⟩, #[], #[propIdentity], #[.zero]⟩ + let (env, _) := storeConst env + ⟨.defn ⟨.defn, .safe, 0, type, .ref 0 #[]⟩, + #[], #[typeIdentity], #[.succ .zero]⟩ + return env + +private def failedSpecialization (arguments : Array UInt64) (level : Ixon.Univ) : + Ixon.Env × Address := + let (env, identity) := polymorphicIdentity + storeConst env + ⟨.defn ⟨.defn, .safe, 0, + .leanAll (.sort 0) (.leanAll (.var 0) (.var 1)), .ref 0 arguments⟩, + #[], #[identity], #[level]⟩ + private def levelOne : KUniv .anon := .mkSucc .mkZero private def levelTwo : KUniv .anon := .mkSucc levelOne @@ -120,6 +144,26 @@ private def simplifyingUniverses : Ixon.Env × Address := ⟨.axio ⟨false, 2, .sort 0⟩, #[], #[], #[.imax (.max (.var 0) (.var 1)) (.var 1)]⟩ +private def referenceSpecialization : Ixon.Env := + let (env, function, carrier) := polymorphicReferences + (storeConst env + ⟨.defn ⟨.defn, .safe, 0, + .leanAll (.ref 0 #[0]) (.ref 0 #[0]), .ref 1 #[0]⟩, + #[], #[carrier, function], #[.succ .zero]⟩).1 + +private def simplifiedSpecialization : Ixon.Env := + let (env, source) := simplifyingUniverses + (storeConst env + ⟨.defn ⟨.defn, .safe, 0, .sort 0, .ref 0 #[1, 0]⟩, + #[], #[source], #[.zero, .succ (.succ .zero)]⟩).1 + +private def wrongSpecializationType : Ixon.Env × Address := + let (env, identity) := polymorphicIdentity + storeConst env + ⟨.defn ⟨.defn, .safe, 0, + .leanAll (.sort 0) (.leanAll (.var 0) (.var 1)), .ref 0 #[1]⟩, + #[], #[identity], #[.zero, .succ .zero]⟩ + private def inferStored (fixture : Ixon.Env × Address) (arguments : Array (KUniv .anon)) (expected : KExpr .anon) (inferOnly := false) (warmIntern := false) : Bool := let action : TcM .anon (KExpr .anon) := do @@ -176,6 +220,24 @@ private def polymorphicCases : TestSeq := | .ok (.sort (.param index _ _) _) _ => index == 0 | _ => false : Bool) -public def suite : List TestSeq := [cases, polymorphicCases] +private def specializationCases : TestSeq := + test "polymorphic environment: Prop and Type specializations and transitive aliases check" + (allSucceeded specializationEnvironment 5 { clearEvery := 0 }) + ++ test "polymorphic environment: specializations check with fresh per-item caches" + (allSucceeded specializationEnvironment 5 { clearEvery := 1 }) + ++ test "polymorphic environment: specialized types preserve nested interface references" + (allSucceeded referenceSpecialization 3) + ++ test "polymorphic environment: simplified imax type passes declaration conversion" + (allSucceeded simplifiedSpecialization 2) + ++ test "polymorphic environment: a different declared specialization fails conversion" + (let (env, target) := wrongSpecializationType; rowFailed env target) + ++ test "polymorphic environment: missing universe arguments fail declaration admission" + (let (env, target) := failedSpecialization #[] .zero; rowFailed env target) + ++ test "polymorphic environment: excess universe arguments fail declaration admission" + (let (env, target) := failedSpecialization #[0, 0] .zero; rowFailed env target) + ++ test "polymorphic environment: a monomorphic body cannot retain a universe parameter" + (let (env, target) := failedSpecialization #[0] (.var 0); rowFailed env target) + +public def suite : List TestSeq := [cases, polymorphicCases, specializationCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index c1a069d4c..10357a0c9 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -43,6 +43,10 @@ increasing sequence of strongly inaccessible cardinals. preserves both interpretation and hereditary validity, including beneath binders and through let substitution. The empty-argument shortcut is justified by the source type's universe scope. +- `instantiateUnivParams_readAnnotated_scoped` also proves scope and reference + preservation for the actual returned type. Scope comes from the successful + substitution and scoped arguments; semantic level equivalence alone would + not exclude an out-of-range parameter in a simplified expression. - `inferUncached_const_sound` and `infer_const_sound` derive model typing for polymorphic constant references with arbitrary readable declaration types. Production success supplies the universe-arity check. The premises retain @@ -66,14 +70,18 @@ support for these obligations. The axiom policy is relative: **every model of the source axioms extends to a model of the checked environment, preserving the axiom interpretations**. -The initial interface contains exactly the source axioms, with closed types -that refer only to that interface. A `Realizes` witness supplies a model of +The initial interface contains exactly the source axioms, retaining their +declared universe arities. Their types have no free term variables, use only +their declared universe parameters, and refer only to that interface. +A `Realizes` witness supplies a model of these axioms. The hypothesis is model existence; a theorem connecting arbitrary syntactic consistency to model existence is outside this result. The fragment covers monomorphic standalone definitions, theorems, and opaque definitions whose values are either closed universe terms or -references to preceding interface entries. Examples include: +references to preceding interface entries, including monomorphic +specializations of polymorphic constants. Referenced types may contain +dependent functions and other readable expression forms. Examples include: ```lean axiom P : Prop @@ -81,6 +89,8 @@ axiom p : P def q : P := p theorem r : P := q def typeAlias : Type := Prop +axiom ident.{u} : (α : Sort u) → α → α +def propIdent : (α : Prop) → α → α := ident.{0} ``` `AtomicEnvironmentFragment` records the precise execution boundary: @@ -89,9 +99,13 @@ def typeAlias : Type := Prop represents an axiom or a definition. Lookup, routing, and reset witnesses identify the checked `KConst`. - Value inference misses both cache partitions. Constant lookup agrees with - an already admitted monomorphic type; empty level substitution preserves - that type. Sort inference retains the finite interning coherence and - address-faithfulness premises. + an already admitted type and universe count. A specialization supplies + closed universe arguments and finite interning/substitution resources at + the actual post-lookup state. The occurrence annotations on the declared + type agree with the substituted entry's annotations. These are structural + data checks; successful inference derives typing, scope, and references. + Ordinary aliases retain the simpler empty-substitution path. Sort inference + retains finite interning coherence and address-faithfulness premises. - Conversion takes the initial hash-equality path, with faithfulness of the compared expressions. General reduction and conversion caches are outside this fragment. @@ -109,9 +123,10 @@ extracts the validation, type-inference, theorem-guard, value-inference, and conversion steps from public success, then derives body typing to extend the preceding model. Automatic witness construction, lambdas, applications, inductives, coordinated blocks, and other conversion paths remain outside the -fragment. The polymorphic constant-inference theorem above is a separate proof -increment; composing it into general declaration admission and environment -model extension remains to be done. +fragment. Polymorphic constant inference is composed into declaration +admission and model extension for the monomorphic specializations described +above. Definitions with their own universe parameters remain outside this +environment fragment. `checkEnvAnon_atomic_represents_source` ties every source address to an interface with the type and body reached by production lookup. The independent @@ -162,7 +177,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 51 exact theorem boundaries. The production +The consistency target checks 66 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -178,6 +193,10 @@ Their model-side level congruence introduces no native proof dependency. Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, argument order, and rejection of wrong arities and out-of-range parameters. +Environment regressions additionally check Prop/Type specializations, +transitive aliases, nested references, simplified declaration types, cache +clearing, and admission failures for wrong arities, open parameters, and a +mismatched declared specialization. ## Certified host adapters diff --git a/docs/theory.md b/docs/theory.md index 0537cf56f..880ed6d3f 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -20,8 +20,9 @@ open frontiers retain their stated model hypotheses. The [certified host adapters](certified-checking.md) connect authenticated serialized Ixon to model admission. Separately, a production `checkEnvAnon` [fragment](kernel-verification.md#production-environment-fragment) for -monomorphic aliases and closed universe terms extends every model of its -source axioms under explicit execution witnesses. Full checker consistency +monomorphic aliases, closed universe terms, and monomorphic specializations +of polymorphic constants extends every model of its source axioms under +explicit execution witnesses. Full checker consistency and compiler/backend refinement remain open. `Ix.Theory.Named` retains the local name-indexed specification and proof From 2e45671189c7b5c74787b6babb8d577a020bffa1 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 12:06:17 -0400 Subject: [PATCH 08/63] Extend production consistency to dependent function bodies --- Ix/Kernel/Verify/Consistency.lean | 13 +- Ix/Kernel/Verify/Consistency/Audit.lean | 41 ++- .../Verify/Consistency/BinderInference.lean | 286 ++++++++++++++++ .../Verify/Consistency/BinderOpening.lean | 323 ++++++++++++++++++ Ix/Kernel/Verify/Consistency/Context.lean | 297 ++++++++++++++++ Ix/Kernel/Verify/Consistency/Level.lean | 9 + Ix/Kernel/Verify/Consistency/Production.lean | 43 ++- Ix/Kernel/Verify/Consistency/ScopedExpr.lean | 258 ++++++++++++++ Ix/Theory/Model/Checking.lean | 69 ++++ Tests/Ix/Kernel/Consistency.lean | 71 +++- Tests/Theory/ImportManifest.lean | 4 +- docs/kernel-verification.md | 51 ++- docs/theory.md | 8 +- 13 files changed, 1443 insertions(+), 30 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/BinderInference.lean create mode 100644 Ix/Kernel/Verify/Consistency/BinderOpening.lean create mode 100644 Ix/Kernel/Verify/Consistency/Context.lean create mode 100644 Ix/Kernel/Verify/Consistency/ScopedExpr.lean create mode 100644 Ix/Theory/Model/Checking.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 447f78de8..3ab048595 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -10,6 +10,10 @@ import Ix.Kernel.Verify.Consistency.Infer import Ix.Kernel.Verify.Consistency.InstUniv import Ix.Kernel.Verify.Consistency.Constant import Ix.Kernel.Verify.Consistency.Atomic +import Ix.Kernel.Verify.Consistency.ScopedExpr +import Ix.Kernel.Verify.Consistency.Context +import Ix.Kernel.Verify.Consistency.BinderOpening +import Ix.Kernel.Verify.Consistency.BinderInference import Ix.Kernel.Verify.Consistency.Production import Ix.Kernel.Verify.Consistency.Environment import Ix.Kernel.Verify.Consistency.Audit @@ -20,10 +24,15 @@ import Ix.Kernel.Verify.Consistency.Audit This library connects production kernel operations to `Ix.Theory`. Each transport keeps its representation, arithmetic, and dependency assumptions explicit. A production `checkEnvAnon` fragment preserves models of its -axiom set for monomorphic aliases, closed sorts, and monomorphic -specializations of polymorphic constants under the stated execution resources. +axiom set for monomorphic aliases, closed sorts, monomorphic specializations of +polymorphic constants, and closed function bodies built from sorts, locals, +dependent functions, and full-mode lambdas under the stated execution resources. Constant inference supports arbitrary readable entry types, using the actual universe-instantiation walker and explicit lookup and finite-support resources. The returned type's scope and references justify declaration admission. +The binder case also requires explicit syntactic scope and empty constant +reference lists. Its separate declared-type inference turns semantic checking +into typing. Local cache hits agree with the actual declaration type; other +inference nodes retain cache-miss boundaries. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 944c66c80..fc7c05645 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -36,6 +36,9 @@ private def atomicRoots : Array Lean.Name := #[ ``infer_uncached_success, ``AtomicInferenceSupport.typing, ``AtomicInferenceSupport.reads, ``AtomicInferenceSupport.output, ``AtomicInferenceSupport.scopeAndReferences, ``AtomicInference.sound, + ``inferUncached_fvar_sound, ``infer_fvar_sound, + ``FVarInferenceSupport.output, ``FVarInferenceSupport.sound, + ``ForallInferenceTrace.output, ``LambdaInferenceTrace.output, ``BinderInference.sound, ``DefinitionBodySupport.sound ] @@ -56,6 +59,25 @@ private def productionRoots : Array Lean.Name := #[ ``checkEnvAnon_atomic_no_false ] +private def scopedRoots : Array Lean.Name := #[ + ``localIndex?_mem, ``localIndex?_getElem, ``localIndex?_fresh, + ``readScopedExpr?_closed, ``readScopedExpr?_eraseMeta, + ``beq_readScopedExpr?, ``internExpr_readScopedExpr?, ``readScopedExpr?_push, + ``LocalContextReading.empty +] + +private def contextRoots : Array Lean.Name := #[ + ``localContext_find?_push_same, ``localContext_find?_push_ne, + ``LocalContextReading.push, ``ScopedModelTyping.closed +] + +private def binderWalkerRoots : Array Lean.Name := #[ + ``inferKey_lctx, ``UncachedInference.localContext, + ``readScopedExpr?_instantiateRevSpec, ``readScopedExpr?_abstractFVarsSpec, + ``openBinder_eq, ``openBinder_sound, + ``abstractFVars_singleton_spec, ``abstractFVars_readScopedExpr? +] + /-- Production roots must not acquire a checker-soundness assumption or invoke the independent certificate validator to establish acceptance. -/ private def forbiddenProduction : Array Lean.Name := #[ @@ -72,11 +94,15 @@ def roots : Array RootAllowance := #[ { root := ``readLevel_eraseMeta, standardAxioms := #[``propext] }, { root := ``readLevel_mkSucc, standardAxioms := #[``propext, ``Classical.choice], nativeAxioms := #[levelNative] }, + { root := ``readLevel_mkIMax, standardAxioms := standard, + nativeAxioms := #[levelNative] }, { root := ``univEq_sound, standardAxioms := standard }, { root := ``univGeq_sound, standardAxioms := standard }, { root := ``readExpr?_mkSort, standardAxioms := standard, nativeAxioms := #[expressionNative] }, { root := ``readExpr?_eraseMeta, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``readScopedExpr?_lam_parts }, + { root := ``readScopedExpr?_all_parts }, { root := ``beq_readExpr?, standardAxioms := #[``propext, ``Quot.sound] }, { root := ``internExpr_readExpr?, standardAxioms := #[``propext, ``Quot.sound] }, { root := ``ModelTyping.sort, standardAxioms := standard, @@ -108,8 +134,19 @@ def roots : Array RootAllowance := #[ standardAxioms := #[``propext] }, { root := ``Theory.Model.AExpr.LevelEquivalent.interp, standardAxioms := standard }, { root := ``Theory.Model.AExpr.LevelEquivalent.wellDenoted, standardAxioms := standard }, - { root := ``Theory.Model.AExpr.LevelEquivalent.typing, standardAxioms := standard } -] ++ (atomicRoots ++ instantiationRoots).map (fun root => { + { root := ``Theory.Model.AExpr.LevelEquivalent.typing, standardAxioms := standard }, + { root := ``Theory.Model.TypingClaim.checking, standardAxioms := standard }, + { root := ``Theory.Model.CheckingClaim.typing, standardAxioms := standard }, + { root := ``Theory.Model.CheckingClaim.typingSort, standardAxioms := standard }, + { root := ``Theory.Model.CheckingClaim.lam, standardAxioms := standard } +] ++ scopedRoots.map (fun root => { + root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction +}) ++ contextRoots.map (fun root => { + root, standardAxioms := standard, forbiddenDependencies := forbiddenProduction +}) ++ binderWalkerRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative], + forbiddenDependencies := forbiddenProduction +}) ++ (atomicRoots ++ instantiationRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) ++ productionRoots.map (fun root => { diff --git a/Ix/Kernel/Verify/Consistency/BinderInference.lean b/Ix/Kernel/Verify/Consistency/BinderInference.lean new file mode 100644 index 000000000..d7da09fca --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BinderInference.lean @@ -0,0 +1,286 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BinderOpening +import Ix.Kernel.Knot +import Ix.Theory.Model.Checking + +/-! +# Production dependent function inference + +Finite inference trees follow the production method table's decreasing fuel. +Their premises record cache misses, actual execution prefixes, local-context +frames, finite interning support, and index bounds. Semantic checking is +derived from these trees. A declaration's separate type inference supplies +the expected type's hereditary validity before checking becomes typing. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +private theorem withLctxScope_eq (action : RecM .anon α) + (methods : Methods .anon) (before : TcState .anon) : + (RecM.withLctxScope action).run methods before = + match action.run methods before with + | .ok value after => + .ok value {after with lctx := after.lctx.truncate before.lctx.size} + | .error err after => + .error err {after with lctx := after.lctx.truncate before.lctx.size} := by + unfold RecM.withLctxScope + rw [ReaderT.run_bind] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + unfold EStateM.bind + rw [show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + simp only + unfold tryFinally + change EStateM.map (fun pair : α × PUnit => pair.1) + (tryFinally' (action.run methods) (fun _ => + (modify (fun after : TcState .anon => + {after with lctx := after.lctx.truncate before.lctx.size}) : + TcM .anon PUnit))) before = _ + unfold EStateM.map MonadFinally.tryFinally' EStateM.instMonadFinally + cases run : action.run methods before <;> simp only [run] <;> rfl + +private theorem withLctxScope_success {action : RecM .anon α} + {methods : Methods .anon} {before after : TcState .anon} {result : α} + (accepted : (RecM.withLctxScope action).run methods before = .ok result after) : + ∃ state, action.run methods before = .ok result state := by + rw [withLctxScope_eq] at accepted + cases run : action.run methods before with + | error err state => rw [run] at accepted; contradiction + | ok value state => + rw [run] at accepted + cases accepted + exact ⟨state, rfl⟩ + +/-- The exact recursive calls and opening prefix of a forall branch. +Both sort exposures take the production syntactic fast path. -/ +structure ForallInferenceTrace (fuel : Nat) (before : TcState .anon) + (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) + (domain body : KExpr .anon) where + domainLevel : KUniv .anon + domainInfo : ExprInfo .anon + domainState : TcState .anon + opened : KExpr .anon + fresh : FVarId + openedState : TcState .anon + bodyLevel : KUniv .anon + bodyInfo : ExprInfo .anon + bodyState : TcState .anon + domainRun : RecM.infer domain (methodsN fuel) before = + .ok (.sort domainLevel domainInfo) domainState + openRun : TcM.openBinder name bi domain body domainState = .ok (opened, fresh) openedState + bodyRun : RecM.infer opened (methodsN fuel) openedState = + .ok (.sort bodyLevel bodyInfo) bodyState + contextPreserved : domainState.lctx = before.lctx + +/-- Inversion reaches the actual final interning operation after both recursive +calls, sort exposures, and binder opening. -/ +theorem ForallInferenceTrace.output {fuel : Nat} {before after : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body result : KExpr .anon} {info : ExprInfo .anon} {inferOnly : Bool} + (trace : ForallInferenceTrace fuel before name bi domain body) + (accepted : RecM.inferUncached RecM.inferCall inferOnly (.all name bi domain body info) + (methodsN (fuel + 1)) before = .ok result after) : + result = (trace.bodyState.env.intern.internExpr + (KExpr.mkSort (KUniv.mkIMax trace.domainLevel trace.bodyLevel))).1 := by + change (RecM.inferUncached RecM.inferCall inferOnly (.all name bi domain body info)).run + (methodsN (fuel + 1)) before = .ok result after at accepted + unfold RecM.inferUncached at accepted + simp only [ReaderT.run_bind] at accepted + change EStateM.bind (RecM.infer domain (methodsN fuel)) _ before = _ at accepted + rw [EStateM.bind, trace.domainRun] at accepted + change (RecM.withLctxScope _).run (methodsN (fuel + 1)) trace.domainState = _ at accepted + obtain ⟨scopedState, accepted⟩ := withLctxScope_success accepted + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted + change EStateM.bind (TcM.openBinder name bi domain body) _ trace.domainState = + .ok result scopedState at accepted + rw [EStateM.bind, trace.openRun] at accepted + have bodyRun : (RecM.inferCall trace.opened).run (methodsN (fuel + 1)) + trace.openedState = .ok (.sort trace.bodyLevel trace.bodyInfo) trace.bodyState := trace.bodyRun + change EStateM.bind ((RecM.inferCall trace.opened).run (methodsN (fuel + 1))) + _ trace.openedState = .ok result scopedState at accepted + rw [EStateM.bind, bodyRun] at accepted + cases accepted + rfl + +/-- Full-mode lambda inference validates its domain, opens the binder, and +infers the body. The current fragment takes the unchanged cheap-beta path. -/ +structure LambdaInferenceTrace (fuel : Nat) (before : TcState .anon) + (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) + (domain body : KExpr .anon) where + domainLevel : KUniv .anon + domainInfo : ExprInfo .anon + domainState : TcState .anon + opened : KExpr .anon + fresh : FVarId + openedState : TcState .anon + bodyType : KExpr .anon + bodyState : TcState .anon + domainRun : RecM.infer domain (methodsN fuel) before = + .ok (.sort domainLevel domainInfo) domainState + openRun : TcM.openBinder name bi domain body domainState = .ok (opened, fresh) openedState + bodyRun : RecM.infer opened (methodsN fuel) openedState = .ok bodyType bodyState + contextPreserved : domainState.lctx = before.lctx + betaUnchanged : cheapBetaPlan? bodyType = none + +def LambdaInferenceTrace.abstracted {fuel : Nat} {before : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body : KExpr .anon} (trace : LambdaInferenceTrace fuel before name bi domain body) := + abstractFVars trace.bodyType #[trace.fresh] trace.bodyState.env.intern + +/-- The returned lambda type is the actual abstracted body type, wrapped in +the production Pi constructor and passed through the final intern table. -/ +theorem LambdaInferenceTrace.output {fuel : Nat} {before after : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body result : KExpr .anon} {info : ExprInfo .anon} + (trace : LambdaInferenceTrace fuel before name bi domain body) + (accepted : RecM.inferUncached RecM.inferCall false (.lam name bi domain body info) + (methodsN (fuel + 1)) before = .ok result after) : + result = (trace.abstracted.2.internExpr (KExpr.mkAll () () domain trace.abstracted.1)).1 := by + change (RecM.inferUncached RecM.inferCall false (.lam name bi domain body info)).run + (methodsN (fuel + 1)) before = .ok result after at accepted + unfold RecM.inferUncached at accepted + simp only [Bool.not_false, if_true, ReaderT.run_bind] at accepted + change EStateM.bind (RecM.infer domain (methodsN fuel)) _ before = _ at accepted + rw [EStateM.bind, trace.domainRun] at accepted + change (RecM.withLctxScope _).run (methodsN (fuel + 1)) trace.domainState = _ at accepted + obtain ⟨scopedState, accepted⟩ := withLctxScope_success accepted + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted + change EStateM.bind (TcM.openBinder name bi domain body) _ trace.domainState = + .ok result scopedState at accepted + rw [EStateM.bind, trace.openRun] at accepted + have bodyRun : (RecM.inferCall trace.opened).run (methodsN (fuel + 1)) + trace.openedState = .ok trace.bodyType trace.bodyState := trace.bodyRun + change EStateM.bind ((RecM.inferCall trace.opened).run (methodsN (fuel + 1))) + _ trace.openedState = .ok result scopedState at accepted + rw [EStateM.bind, bodyRun] at accepted + simp only [cheapBetaReduce, trace.betaUnchanged] at accepted + cases accepted + rfl + +/-- Structural and operational support for a finite production inference +tree. The source reading and local-context agreement are inputs to soundness, +so each recursive body's reading must be derived by opening its binder. -/ +inductive BinderInference {β : Type u} + (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) : + List FVarId → Model.Context β → Nat → TcState .anon → KExpr .anon → + AExpr β → AExpr β → Type u + | sort {locals context fuel before level info} + (miss : UncachedInference before (.sort level info)) + (coherent : miss.keyed.env.intern.WF) + (faithful : KExpr.KeyCollisionFree fun term => + miss.keyed.env.intern.ExprSupport term ∨ term = KExpr.mkSort (KUniv.mkSucc level)) : + BinderInference resolve entries locals context fuel before (.sort level info) + (.sort (readLevel level)) (.sort (.succ (readLevel level))) + | fvar {locals context fuel before id name info index A} + (cache : FVarInferenceSupport before id name info) + (registered : localIndex? locals id = some index) + (atIndex : context[index]? = some A) : + BinderInference resolve entries locals context fuel before (.fvar id name info) (.bvar index) A + | forallE {locals context fuel before name bi domain body info A B} + (miss : UncachedInference before (.all name bi domain body info)) + (trace : ForallInferenceTrace fuel miss.keyed name bi domain body) + (opening : BinderOpeningSupport trace.domainState body) + (absent : (⟨trace.domainState.env.nextFVarId⟩ : FVarId) ∉ locals) + (domainTree : BinderInference resolve entries locals context fuel miss.keyed domain + A (.sort (readLevel trace.domainLevel))) + (bodyTree : BinderInference resolve entries (trace.fresh :: locals) (context.push A) + fuel trace.openedState trace.opened B (.sort (readLevel trace.bodyLevel))) + (levelFaithful : ∀ a b, + (KUniv.Sub a trace.domainLevel ∨ KUniv.Sub a trace.bodyLevel) → + (KUniv.Sub b trace.domainLevel ∨ KUniv.Sub b trace.bodyLevel) → a.AddrFaithful b) + (domainBound : trace.domainLevel.size < UInt64.size) + (bodyBound : trace.bodyLevel.size < UInt64.size) + (coherent : trace.bodyState.env.intern.WF) + (faithful : KExpr.KeyCollisionFree fun term => trace.bodyState.env.intern.ExprSupport term ∨ + term = KExpr.mkSort (KUniv.mkIMax trace.domainLevel trace.bodyLevel)) : + BinderInference resolve entries locals context (fuel + 1) before (.all name bi domain body info) + (.forallE (Certified.zeroCondition (readLevel trace.bodyLevel)) A B) + (.sort (readLevel (KUniv.mkIMax trace.domainLevel trace.bodyLevel))) + | lam {locals context fuel before name bi domain body info A b B condition} + (full : before.inferOnly = false) + (miss : UncachedInference before (.lam name bi domain body info)) + (trace : LambdaInferenceTrace fuel miss.keyed name bi domain body) + (opening : BinderOpeningSupport trace.domainState body) + (absent : (⟨trace.domainState.env.nextFVarId⟩ : FVarId) ∉ locals) + (bodyTree : BinderInference resolve entries (trace.fresh :: locals) (context.push A) + fuel trace.openedState trace.opened b B) + (constructed : trace.bodyType.Constructed) + (bound : trace.bodyType.size + 1 < UInt64.size) + (coherent : trace.bodyState.env.intern.WF) + (closingFaithful : KExpr.CollisionFree fun term => trace.bodyState.env.intern.ExprSupport term ∨ + KExpr.AbstractReach ((∅ : Std.HashMap FVarId UInt64).insert trace.fresh 0) + 1 trace.bodyType 0 term) + (faithful : KExpr.KeyCollisionFree fun term => trace.abstracted.2.ExprSupport term ∨ + term = KExpr.mkAll () () domain trace.abstracted.1) : + BinderInference resolve entries locals context (fuel + 1) before (.lam name bi domain body info) + (.lam condition A b) (.forallE condition A B) + +/-- Successful production inference reads the expected model type and checks +the actual source term against it. No recursive semantic premise is supplied +by the caller: induction follows the finite operational support tree. -/ +theorem BinderInference.sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {before after : TcState .anon} {term result : KExpr .anon} {e A : AExpr β} + (support : BinderInference resolve entries locals context fuel before term e A) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals term = some e.erase) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + readScopedExpr? resolve locals result = some A.erase ∧ + CheckingClaim.{u,v} entries context e A := by + induction support generalizing result after with + | sort miss coherent faithful => + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + change EStateM.Result.ok + (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc _))).1 _ = + .ok result state at run + cases run + refine ⟨?_, (TypingClaim.sort _).checking⟩ + rw [internExpr_readScopedExpr? coherent faithful] + simp [AExpr.erase] + | fvar cache registered atIndex => + obtain ⟨typeReads, typed⟩ := cache.sound agreement registered atIndex accepted + exact ⟨typeReads, typed.checking⟩ + | forallE miss trace opening absent domainTree bodyTree levelFaithful domainBound bodyBound + coherent faithful ihDomain ihBody => + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨_, domainChecked⟩ := ihDomain keyedAgreement domainReads trace.domainRun + have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + obtain ⟨_, openedReads, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun + obtain ⟨_, bodyChecked⟩ := ihBody openedAgreement openedReads trace.bodyRun + refine ⟨?_, ?_⟩ + · rw [trace.output run, internExpr_readScopedExpr? coherent faithful] + rfl + · have formed := TypingClaim.forallE domainChecked.typingSort bodyChecked.typingSort rfl + exact (AExpr.LevelEquivalent.sort + (Theory.VLevel.equiv_def.mpr fun levels => + (Theory.VLevel.equiv_def.mp (readLevel_mkIMax levelFaithful domainBound bodyBound) + levels).symm) |>.typing formed).checking + | lam full miss trace opening absent bodyTree constructed bound coherent closingFaithful + faithful ihBody => + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + rw [full] at run + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + obtain ⟨_, openedReads, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun + obtain ⟨bodyTypeReads, bodyChecked⟩ := ihBody openedAgreement openedReads trace.bodyRun + obtain ⟨closedReads, closedCoherent⟩ := abstractFVars_readScopedExpr? constructed bound coherent + closingFaithful bodyTypeReads + refine ⟨?_, bodyChecked.lam⟩ + rw [trace.output run, + internExpr_readScopedExpr? (table := trace.abstracted.2) closedCoherent faithful] + simp [LambdaInferenceTrace.abstracted, domainReads, closedReads, AExpr.erase] + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BinderOpening.lean b/Ix/Kernel/Verify/Consistency/BinderOpening.lean new file mode 100644 index 000000000..16c35701e --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BinderOpening.lean @@ -0,0 +1,323 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Context +import Ix.Kernel.Verify.Subst + +/-! +# Reading the production binder walkers + +Opening a syntactic binder and registering its fresh free variable preserve +the same model de Bruijn expression. Closing performs the inverse change of +representation. The bounds below prevent production's `UInt64` depth from +wrapping; they impose no semantic typing assumptions. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u +variable {β : Type u} + +private theorem bind_success {α γ : Type _} {action : Option α} + {next : α → Option γ} {result : γ} (run : action.bind next = some result) : + ∃ intermediate, action = some intermediate ∧ next intermediate = some result := by + cases action with + | none => contradiction + | some value => exact ⟨value, rfl, run⟩ + +private theorem depth_succ {depth : UInt64} (bound : depth.toNat + 1 < UInt64.size) : + (depth + 1).toNat = depth.toNat + 1 := by + rw [UInt64.toNat_add, show (1 : UInt64).toNat = 1 from rfl, Nat.mod_eq_of_lt bound] + +/-- A fresh local replaces exactly the removed syntactic binder, even below +nested binders and in occurrences of older dependent locals. -/ +theorem readScopedExpr?_instantiateRevSpec + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {body : KExpr .anon} {source : VExpr β} {depth : UInt64} {fresh : FVarId} + (absent : fresh ∉ locals) + (bound : depth.toNat + body.size + 1 < UInt64.size) + (reading : readScopedExpr? resolve locals body (depth.toNat + 1) = some source) : + readScopedExpr? resolve (fresh :: locals) + (KExpr.instantiateRevSpec body #[KExpr.mkFVar fresh ()] depth) depth.toNat = + some source := by + induction body generalizing source depth with + | var index name info => + simp only [readScopedExpr?] at reading + split at reading + next inScope => + cases reading + have next := depth_succ (depth := depth) (by simp only [KExpr.size] at bound; omega) + by_cases equal : index = depth + · subst index + simp [KExpr.instantiateRevSpec, UInt64.lt_iff_toNat_lt, next, + localIndex?] + · have smaller : index.toNat < depth.toNat := by + have : index.toNat ≠ depth.toNat := fun h => equal (UInt64.toNat_inj.mp h) + omega + have before : ¬ index ≥ depth := by + simp only [UInt64.le_iff_toNat_le]; omega + have beforeNext : ¬ index ≥ depth + 1 := by + simp only [UInt64.le_iff_toNat_le, next]; omega + simp [KExpr.instantiateRevSpec, before, beforeNext, readScopedExpr?, smaller] + · contradiction + | fvar id name info => + rw [readScopedExpr?] at reading + obtain ⟨index, found, reading⟩ := Option.map_eq_some_iff.mp reading + cases reading + simp [KExpr.instantiateRevSpec, readScopedExpr?, localIndex?_fresh absent found, + Nat.add_assoc, Nat.add_comm 1] + | sort _ _ | const _ _ _ | nat _ _ _ => exact reading + | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | app fn arg info hf ha => + rw [readScopedExpr?] at reading + obtain ⟨f, fReads, reading⟩ := bind_success reading + obtain ⟨a, aReads, reading⟩ := bind_success reading + cases reading + simp only [KExpr.size] at bound + simp [KExpr.instantiateRevSpec, + hf (by omega) fReads, ha (by omega) aReads] + | lam name bi domain body info hd hb | all name bi domain body info hd hb => + rw [readScopedExpr?] at reading + obtain ⟨A, domainReads, reading⟩ := bind_success reading + obtain ⟨B, bodyReads, reading⟩ := bind_success reading + cases reading + simp only [KExpr.size] at bound + have next := depth_succ (depth := depth) (by omega) + have bodyBound : (depth + 1).toNat + body.size + 1 < UInt64.size := by + rw [next]; omega + have bodyReads' : readScopedExpr? resolve locals body ((depth + 1).toNat + 1) = + some B := by simpa only [next] using bodyReads + have bodyOut : readScopedExpr? resolve (fresh :: locals) + (KExpr.instantiateRevSpec body #[KExpr.mkFVar fresh ()] (depth + 1)) + (depth.toNat + 1) = some B := by + simpa only [next] using hb bodyBound bodyReads' + simp [KExpr.instantiateRevSpec, hd (by omega) domainReads, bodyOut] + | prj id index value info ih => + rw [readScopedExpr?] at reading + obtain ⟨ref, resolved, reading⟩ := bind_success reading + obtain ⟨value, valueReads, reading⟩ := bind_success reading + cases reading + simp only [KExpr.size] at bound + simp [KExpr.instantiateRevSpec, resolved, + ih (by omega) valueReads] + +private local instance : LawfulBEq FVarId where + eq_of_beq := by + intro left right equal + cases left + cases right + congr 1 + exact eq_of_beq equal + rfl {a} := by + cases a with + | mk x => show (x == x) = true; exact beq_self_eq_true x + +/-- Closing the newest local restores the syntactic binder without changing +the model expression. Unknown locals and loose variables cannot pass the +source reading. -/ +theorem readScopedExpr?_abstractFVarsSpec + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {body : KExpr .anon} {source : VExpr β} {depth : UInt64} {fresh : FVarId} + (bound : depth.toNat + body.size + 1 < UInt64.size) + (reading : readScopedExpr? resolve (fresh :: locals) body depth.toNat = some source) : + readScopedExpr? resolve locals + (KExpr.abstractFVarsSpec body ((∅ : Std.HashMap FVarId UInt64).insert fresh 0) + 1 depth) (depth.toNat + 1) = some source := by + induction body generalizing source depth with + | var index name info => + simp only [readScopedExpr?] at reading + split at reading + next inScope => + cases reading + have before : ¬ index ≥ depth := by + simp only [UInt64.le_iff_toNat_le]; omega + simp [KExpr.abstractFVarsSpec, before, readScopedExpr?, show index.toNat < + depth.toNat + 1 by omega] + · contradiction + | fvar id name info => + by_cases equal : id = fresh + · subst id + simp [readScopedExpr?, localIndex?] at reading + cases reading + simp [KExpr.abstractFVarsSpec] + · simp only [readScopedExpr?, localIndex?, equal, if_false, Option.map_map] at reading + obtain ⟨index, found, reading⟩ := Option.map_eq_some_iff.mp reading + cases reading + simp [KExpr.abstractFVarsSpec, Ne.symm equal, + readScopedExpr?, found, + Nat.add_assoc, Nat.add_comm 1] + | sort _ _ | const _ _ _ | nat _ _ _ => exact reading + | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | app fn arg info hf ha => + rw [readScopedExpr?] at reading + obtain ⟨f, fReads, reading⟩ := bind_success reading + obtain ⟨a, aReads, reading⟩ := bind_success reading + cases reading + simp only [KExpr.size] at bound + simp [KExpr.abstractFVarsSpec, + hf (by omega) fReads, ha (by omega) aReads] + | lam name bi domain body info hd hb | all name bi domain body info hd hb => + rw [readScopedExpr?] at reading + obtain ⟨A, domainReads, reading⟩ := bind_success reading + obtain ⟨B, bodyReads, reading⟩ := bind_success reading + cases reading + simp only [KExpr.size] at bound + have next := depth_succ (depth := depth) (by omega) + have bodyBound : (depth + 1).toNat + body.size + 1 < UInt64.size := by + rw [next]; omega + have bodyReads' : readScopedExpr? resolve (fresh :: locals) body (depth + 1).toNat = + some B := by simpa only [next] using bodyReads + have bodyOut : readScopedExpr? resolve locals + (KExpr.abstractFVarsSpec body ((∅ : Std.HashMap FVarId UInt64).insert fresh 0) + 1 (depth + 1)) (depth.toNat + 1 + 1) = some B := by + simpa only [next] using hb bodyBound bodyReads' + simp [KExpr.abstractFVarsSpec, hd (by omega) domainReads, bodyOut] + | prj id index value info ih => + rw [readScopedExpr?] at reading + obtain ⟨ref, resolved, reading⟩ := bind_success reading + obtain ⟨value, valueReads, reading⟩ := bind_success reading + cases reading + simp only [KExpr.size] at bound + simp [KExpr.abstractFVarsSpec, resolved, + ih (by omega) valueReads] + +/-- The exact successful-state shape of production binder opening. -/ +theorem openBinder_eq (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) + (type body : KExpr .anon) (before : TcState .anon) : + TcM.openBinder name bi type body before = + if before.env.nextFVarId.toNat + 1 < UInt64.size then + let fresh : FVarId := ⟨before.env.nextFVarId⟩ + let internedLocal := before.env.intern.internExpr (KExpr.mkFVar fresh name) + let opened := instantiateRev body #[internedLocal.1] internedLocal.2 + .ok (opened.1, fresh) {before with + env := {before.env with + nextFVarId := before.env.nextFVarId + 1 + intern := opened.2} + lctx := before.lctx.push fresh (.cdecl name bi type)} + else .error (.other "free-variable id space exhausted") before := by + unfold TcM.openBinder + change EStateM.bind TcM.freshFVarId _ before = _ + rw [EStateM.bind, TcM.freshFVarId] + by_cases room : before.env.nextFVarId.toNat + 1 < UInt64.size + · simp only [room, if_true] + rfl + · simp only [room, if_false] + +/-- Finite production resources for one binder opening. Collision freedom is +needed only on the existing table, the new local, and this walker's reach. -/ +structure BinderOpeningSupport (before : TcState .anon) (body : KExpr .anon) : Prop where + constructed : body.Constructed + bound : body.size + 1 < UInt64.size + coherent : before.env.intern.WF + faithful : KExpr.CollisionFree fun term => + before.env.intern.ExprSupport term ∨ + term = KExpr.mkFVar ⟨before.env.nextFVarId⟩ () ∨ + KExpr.InstRevReach #[KExpr.mkFVar ⟨before.env.nextFVarId⟩ ()] body 0 term + +/-- Successful binder opening preserves the model body and implements the +model's dependent context extension, using the actual allocated id. -/ +theorem openBinder_sound + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {context : Model.Context β} {type body opened : KExpr .anon} + {A : AExpr β} {b : VExpr β} {fresh : FVarId} {before after : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + (support : BinderOpeningSupport before body) + (agreement : LocalContextReading resolve locals before.lctx context) + (absent : (⟨before.env.nextFVarId⟩ : FVarId) ∉ locals) + (typeReads : readScopedExpr? resolve locals type = some A.erase) + (bodyReads : readScopedExpr? resolve locals body 1 = some b) + (accepted : TcM.openBinder name bi type body before = .ok (opened, fresh) after) : + fresh = ⟨before.env.nextFVarId⟩ ∧ + readScopedExpr? resolve (fresh :: locals) opened = some b ∧ + LocalContextReading resolve (fresh :: locals) after.lctx (context.push A) ∧ + after.env.intern.WF := by + have nameUnit : name = () := Subsingleton.elim _ _ + have biUnit : bi = () := Subsingleton.elim _ _ + subst name bi + have interned : (before.env.intern.internExpr + (KExpr.mkFVar ⟨before.env.nextFVarId⟩ ())).1 = + KExpr.mkFVar ⟨before.env.nextFVarId⟩ () := by + have keyFaithful : KExpr.KeyCollisionFree (fun term => + before.env.intern.ExprSupport term ∨ term = KExpr.mkFVar ⟨before.env.nextFVarId⟩ ()) := + KExpr.keyCollisionFree_anon.mpr + (support.faithful.mono fun term h => h.elim Or.inl (fun equal => .inr (.inl equal))) + simpa only [KExpr.eraseMeta_anon] using + before.env.intern.internExpr_eraseMeta support.coherent keyFaithful + have walk := instantiateRev_spec (fvars := #[KExpr.mkFVar ⟨before.env.nextFVarId⟩ ()]) + support.faithful support.constructed (by simpa using support.bound) + (fun _ reached => .inr (.inr reached)) + (support.coherent.internExpr (KExpr.mkFVar ⟨before.env.nextFVarId⟩ ())) + (fun _ member => (InternTable.ExprSupport.of_internExpr member).elim + Or.inl (fun equal => .inr (.inl equal))) + rw [openBinder_eq] at accepted + split at accepted + · simp only [interned] at accepted + cases accepted + refine ⟨rfl, ?_, agreement.push (decl := .cdecl () () type) absent typeReads, walk.2.1⟩ + rw [walk.1] + exact readScopedExpr?_instantiateRevSpec absent (by simpa using support.bound) bodyReads + · contradiction + +private theorem abstractFVars_singleton_eq (body : KExpr .anon) (fresh : FVarId) : + abstractFVars body #[fresh] = + if (!body.hasFVars && body.lbr == 0) then pure body + else runWalk (abstractFVarsCached body + ((∅ : Std.HashMap FVarId UInt64).insert fresh 0) 1 0) := by + unfold abstractFVars + simp only [show #[fresh].isEmpty = false from rfl, Bool.false_or] + by_cases fast : (!body.hasFVars && body.lbr == 0) = true + · simp only [fast, if_true] + · simp only [fast, ← Array.forIn_toList] + simp + +/-- Exact refinement of the production singleton abstraction, including its +no-free-variable fast path and its memoized recursive walk. -/ +theorem abstractFVars_singleton_spec {body : KExpr .anon} {fresh : FVarId} + {table : InternTable .anon} {support : KExpr .anon → Prop} + (constructed : body.Constructed) (bound : body.size < UInt64.size) + (faithful : KExpr.CollisionFree support) (coherent : table.WF) + (initial : ∀ term, table.ExprSupport term → support term) + (reachable : ∀ term, KExpr.AbstractReach + ((∅ : Std.HashMap FVarId UInt64).insert fresh 0) 1 body 0 term → support term) : + (abstractFVars body #[fresh] table).1 = KExpr.abstractFVarsSpec body + ((∅ : Std.HashMap FVarId UInt64).insert fresh 0) 1 0 ∧ + (abstractFVars body #[fresh] table).2.WF := by + rw [abstractFVars_singleton_eq] + split + next fast => + have noFVars : body.hasFVars = false := by + have := (Bool.and_eq_true_iff.mp fast).1 + simpa using this + have noLoose : body.lbr ≤ (0 : UInt64) := by + rw [eq_of_beq (Bool.and_eq_true_iff.mp fast).2] + exact UInt64.le_refl _ + exact ⟨(KExpr.abstractFVarsSpec_id constructed (by simpa using bound) noFVars noLoose).symm, + coherent⟩ + · have post := abstractFVarsCached_spec faithful constructed (depth := 0) + (by simpa using bound) reachable coherent initial + (WalkScratchInv.empty support _) + exact ⟨post.result, post.wf⟩ + +/-- Production abstraction recovers the model body under the closed binder. -/ +theorem abstractFVars_readScopedExpr? + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {body : KExpr .anon} {fresh : FVarId} {source : VExpr β} + {table : InternTable .anon} + (constructed : body.Constructed) (bound : body.size + 1 < UInt64.size) + (coherent : table.WF) + (faithful : KExpr.CollisionFree fun term => table.ExprSupport term ∨ + KExpr.AbstractReach ((∅ : Std.HashMap FVarId UInt64).insert fresh 0) 1 body 0 term) + (reading : readScopedExpr? resolve (fresh :: locals) body = some source) : + readScopedExpr? resolve locals (abstractFVars body #[fresh] table).1 1 = some source ∧ + (abstractFVars body #[fresh] table).2.WF := by + obtain ⟨result, coherent⟩ := abstractFVars_singleton_spec constructed (by omega) + faithful coherent (fun _ => Or.inl) (fun _ => Or.inr) + refine ⟨?_, coherent⟩ + rw [result] + exact readScopedExpr?_abstractFVarsSpec (by simpa using bound) reading + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Context.lean b/Ix/Kernel/Verify/Consistency/Context.lean new file mode 100644 index 000000000..12f49aaaf --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Context.lean @@ -0,0 +1,297 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.ScopedExpr +import Ix.Kernel.Verify.Consistency.Atomic +import Std.Data.HashMap.Lemmas + +/-! +# Production locals and model contexts + +`LocalContextReading` identifies the actual declaration returned by each +active free-variable lookup and reads its type in the full model context. +It contains structural agreement only. The model variable rule supplies +typing for every valuation satisfying that context. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +private local instance : LawfulBEq FVarId where + eq_of_beq := by + intro left right equal + cases left + cases right + congr 1 + exact eq_of_beq equal + rfl {a} := by + cases a with + | mk x => show (x == x) = true; exact beq_self_eq_true x + +theorem localContext_find?_push_same {m : Mode} (context : LocalContext m) + (id : FVarId) (decl : LocalDecl m) : + (context.push id decl).find? id = some decl := by + simp [LocalContext.find?, LocalContext.push, Array.getElem?_push] + +theorem localContext_find?_push_ne {m : Mode} {context : LocalContext m} + {id fresh : FVarId} {decl added : LocalDecl m} + (different : id ≠ fresh) (found : context.find? id = some decl) : + (context.push fresh added).find? id = some decl := by + cases index : context.index[id]? with + | none => simp [LocalContext.find?, index] at found + | some position => + cases value : context.decls[position]? with + | none => simp [LocalContext.find?, index, value] at found + | some pair => + have bound := (Array.getElem?_eq_some_iff.mp value).1 + simp [LocalContext.find?, index, value] at found + simp [LocalContext.find?, LocalContext.push, Std.HashMap.getElem?_insert, + Ne.symm different, index, Array.getElem?_push, Nat.ne_of_lt bound, value, found] + +/-- Every registered id corresponds to the exact type returned by production +lookup. All types are expressed in the full current model context. -/ +structure LocalContextReading {β : Type u} (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (concrete : LocalContext .anon) (context : Model.Context β) : Prop where + distinct : locals.Nodup + length : context.length = locals.length + lookup : ∀ id index, localIndex? locals id = some index → + ∃ decl type, concrete.find? id = some decl ∧ context[index]? = some type ∧ + readScopedExpr? resolve locals decl.ty = some type.erase + +theorem LocalContextReading.empty {β : Type u} + (resolve : Address → Option (ConstRef β)) (concrete : LocalContext .anon) : + LocalContextReading resolve [] concrete [] := by + refine ⟨List.nodup_nil, rfl, ?_⟩ + intro id index found + contradiction + +/-- Pushing a fresh production declaration implements the model's lifted +context extension, including the types of older dependent locals. -/ +theorem LocalContextReading.push {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {concrete : LocalContext .anon} {context : Model.Context β} + {fresh : FVarId} {decl : LocalDecl .anon} {type : AExpr β} + (agreement : LocalContextReading resolve locals concrete context) + (absent : fresh ∉ locals) + (reading : readScopedExpr? resolve locals decl.ty = some type.erase) : + LocalContextReading resolve (fresh :: locals) (concrete.push fresh decl) + (context.push type) := by + refine ⟨List.nodup_cons.mpr ⟨absent, agreement.distinct⟩, ?_, ?_⟩ + · simp [Model.Context.push, agreement.length] + · intro id index found + by_cases equal : id = fresh + · subst id + simp only [localIndex?, if_true, Option.some.injEq] at found + subst index + refine ⟨decl, type.liftN 1, localContext_find?_push_same _ _ _, rfl, ?_⟩ + simpa only [AExpr.erase_liftN] using readScopedExpr?_push absent reading + · simp only [localIndex?, equal, if_false] at found + obtain ⟨previous, hit, rfl⟩ := Option.map_eq_some_iff.mp found + obtain ⟨oldDecl, oldType, oldFound, oldIndex, oldReads⟩ := agreement.lookup id previous hit + refine ⟨oldDecl, oldType.liftN 1, localContext_find?_push_ne equal oldFound, ?_, ?_⟩ + · simp [Model.Context.push, oldIndex] + · simpa only [AExpr.erase_liftN] using readScopedExpr?_push absent oldReads + +/-- Semantic typing tied to the exact opened expression and returned type. -/ +def ScopedModelTyping {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (locals : List FVarId) (context : Model.Context β) + (term type : KExpr .anon) : Prop := + ∃ e A : AExpr β, + readScopedExpr? resolve locals term = some e.erase ∧ + readScopedExpr? resolve locals type = some A.erase ∧ + TypingClaim.{u,v} entries context e A + +theorem ScopedModelTyping.closed {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {term type : KExpr .anon} + (typed : ScopedModelTyping.{u,v} resolve entries [] [] term type) : + ModelTyping.{u,v} resolve entries [] term type := by + obtain ⟨e, A, he, hA, typed⟩ := typed + exact ⟨e, A, readScopedExpr?_closed he, readScopedExpr?_closed hA, typed⟩ + +/-- Computing an inference key changes only its memo table, preserving the +actual free-variable context in every fast, hit, and miss branch. -/ +theorem inferKey_lctx {term : KExpr .anon} {before after : TcState .anon} + {key : Address × Address} + (run : TcM.inferKey term before = .ok key after) : after.lctx = before.lctx := by + unfold TcM.inferKey at run + change EStateM.bind (TcM.ctxAddrForLbr term.lbr) _ before = _ at run + unfold TcM.ctxAddrForLbr at run + change EStateM.bind (fun state => EStateM.bind (get : TcM .anon (TcState .anon)) + _ state) _ before = _ at run + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = + .ok before before from rfl] at run + by_cases fast : (term.lbr == 0 || before.ctx.isEmpty) = true + · rw [if_pos fast] at run + cases run; rfl + · rw [if_neg fast] at run + cases cached : before.ctxAddrCache[(before.ctxId, term.lbr)]? with + | none => rw [cached] at run; cases run; rfl + | some address => rw [cached] at run; cases run; rfl + +theorem UncachedInference.localContext {term : KExpr .anon} {before : TcState .anon} + (miss : UncachedInference before term) : miss.keyed.lctx = before.lctx := + inferKey_lctx miss.keyRun + +/-- The actual free-variable inference branch reads its type from the local +context; model typing follows from that same context entry. -/ +theorem inferUncached_fvar_sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {id : FVarId} {index : Nat} + {info : ExprInfo .anon} {name : Mode.anon.F Name} + {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} + {methods : Methods .anon} {before after : TcState .anon} {type : KExpr .anon} + (agreement : LocalContextReading resolve locals before.lctx context) + (registered : localIndex? locals id = some index) + (accepted : RecM.inferUncached inferRec inferOnly (.fvar id name info) + methods before = .ok type after) : + ∃ A : AExpr β, context[index]? = some A ∧ + readScopedExpr? resolve locals type = some A.erase ∧ + TypingClaim.{u,v} entries context (.bvar index) A := by + obtain ⟨decl, A, found, atIndex, reading⟩ := agreement.lookup id index registered + change (RecM.inferUncached inferRec inferOnly (.fvar id name info)).run + methods before = .ok type after at accepted + unfold RecM.inferUncached at accepted + simp only [ReaderT.run_bind] at accepted + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ at accepted + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = + .ok before before from rfl] at accepted + simp only [found] at accepted + cases accepted + exact ⟨A, atIndex, reading, TypingClaim.bvar atIndex⟩ + +/-- Free-variable inference through both cache lookups and the final cache +write, under the same explicit miss boundary as constant inference. -/ +theorem infer_fvar_sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {id : FVarId} {index : Nat} + {info : ExprInfo .anon} {name : Mode.anon.F Name} + {methods : Methods .anon} {before after : TcState .anon} {type : KExpr .anon} + (miss : UncachedInference before (.fvar id name info)) + (agreement : LocalContextReading resolve locals miss.keyed.lctx context) + (registered : localIndex? locals id = some index) + (accepted : RecM.infer (.fvar id name info) methods before = .ok type after) : + ScopedModelTyping.{u,v} resolve entries locals context (.fvar id name info) type := by + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + obtain ⟨A, _, reading, typed⟩ := inferUncached_fvar_sound agreement registered run + exact ⟨.bvar index, A, by simp [readScopedExpr?, registered, AExpr.erase], reading, typed⟩ + +/-- A free-variable cache entry must be the exact type returned by its +current production declaration. This is a check on concrete data at one key; +it does not assert semantic typing of a cached answer. -/ +structure FVarInferenceSupport (before : TcState .anon) (id : FVarId) + (name : Mode.anon.F Name) (info : ExprInfo .anon) where + key : Address × Address + keyed : TcState .anon + keyRun : TcM.inferKey (.fvar id name info) before = .ok key keyed + fullMatches : ∀ cached decl, keyed.env.inferCache[key]? = some cached → + keyed.lctx.find? id = some decl → cached = decl.ty + onlyMatches : ∀ cached decl, keyed.env.inferOnlyCache[key]? = some cached → + keyed.lctx.find? id = some decl → cached = decl.ty + +def FVarInferenceSupport.ofMiss {before : TcState .anon} {id : FVarId} + {name : Mode.anon.F Name} {info : ExprInfo .anon} + (miss : UncachedInference before (.fvar id name info)) : + FVarInferenceSupport before id name info := { + key := miss.key, keyed := miss.keyed, keyRun := miss.keyRun + fullMatches := by intros; simp_all [miss.fullMiss] + onlyMatches := by intros; simp_all [miss.onlyMiss] +} + +/-- Local inference returns the actual declaration type through either cache +partition or the uncached lookup, including the final cache insertion. -/ +theorem FVarInferenceSupport.output {before after : TcState .anon} {id : FVarId} + {name : Mode.anon.F Name} {info : ExprInfo .anon} {decl : LocalDecl .anon} + {methods : Methods .anon} {type : KExpr .anon} + (support : FVarInferenceSupport before id name info) + (found : support.keyed.lctx.find? id = some decl) + (accepted : RecM.infer (.fvar id name info) methods before = .ok type after) : + type = decl.ty := by + change (RecM.infer (.fvar id name info)).run methods before = .ok type after at accepted + unfold RecM.infer RecM.inferWith at accepted + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted + change EStateM.bind (TcM.inferKey (.fvar id name info)) _ before = _ at accepted + rw [EStateM.bind, support.keyRun] at accepted + change EStateM.bind (get : TcM .anon (TcState .anon)) _ support.keyed = _ at accepted + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) support.keyed = + .ok support.keyed support.keyed from rfl] at accepted + cases full : support.keyed.env.inferCache[support.key]? with + | some cached => + simp only [full] at accepted + cases accepted + exact support.fullMatches _ _ full found + | none => + simp only [full] at accepted + cases policy : before.inferOnly with + | false => + simp only [policy, Bool.false_eq_true, if_false] at accepted + have lookup : RecM.inferUncached RecM.inferCall false (.fvar id name info) + methods support.keyed = .ok decl.ty support.keyed := by + change (RecM.inferUncached RecM.inferCall false (.fvar id name info)).run + methods support.keyed = _ + unfold RecM.inferUncached + simp only [ReaderT.run_bind] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ support.keyed = _ + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) support.keyed = + .ok support.keyed support.keyed from rfl] + simp only [found] + rfl + change EStateM.bind (RecM.inferUncached RecM.inferCall false (.fvar id name info) methods) + _ support.keyed = _ at accepted + rw [EStateM.bind, lookup] at accepted + cases accepted + rfl + | true => + simp only [policy, if_true, ReaderT.run_bind] at accepted + change EStateM.bind (get : TcM .anon (TcState .anon)) _ support.keyed = _ at accepted + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) support.keyed = + .ok support.keyed support.keyed from rfl] at accepted + cases only : support.keyed.env.inferOnlyCache[support.key]? with + | some cached => + simp only [only] at accepted + cases accepted + exact support.onlyMatches _ _ only found + | none => + simp only [only] at accepted + have lookup : RecM.inferUncached RecM.inferCall true (.fvar id name info) + methods support.keyed = .ok decl.ty support.keyed := by + change (RecM.inferUncached RecM.inferCall true (.fvar id name info)).run + methods support.keyed = _ + unfold RecM.inferUncached + simp only [ReaderT.run_bind] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ support.keyed = _ + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) support.keyed = + .ok support.keyed support.keyed from rfl] + simp only [found] + rfl + change EStateM.bind (RecM.inferUncached RecM.inferCall true + (.fvar id name info) methods) _ support.keyed = _ at accepted + rw [EStateM.bind, lookup] at accepted + cases accepted + rfl + +theorem FVarInferenceSupport.sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {before after : TcState .anon} + {id : FVarId} {name : Mode.anon.F Name} {info : ExprInfo .anon} + {index : Nat} {A : AExpr β} {methods : Methods .anon} {type : KExpr .anon} + (support : FVarInferenceSupport before id name info) + (agreement : LocalContextReading resolve locals before.lctx context) + (registered : localIndex? locals id = some index) + (atIndex : context[index]? = some A) + (accepted : RecM.infer (.fvar id name info) methods before = .ok type after) : + readScopedExpr? resolve locals type = some A.erase ∧ + TypingClaim.{u,v} entries context (.bvar index) A := by + have keyedAgreement := (inferKey_lctx support.keyRun).symm ▸ agreement + obtain ⟨decl, B, found, position, reading⟩ := keyedAgreement.lookup id index registered + have equal := Option.some.inj (position.symm.trans atIndex) + subst B + rw [support.output found accepted] + exact ⟨reading, TypingClaim.bvar atIndex⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Level.lean b/Ix/Kernel/Verify/Consistency/Level.lean index 59441f43d..f6155f54c 100644 --- a/Ix/Kernel/Verify/Consistency/Level.lean +++ b/Ix/Kernel/Verify/Consistency/Level.lean @@ -43,6 +43,15 @@ theorem readLevel_eq (u : KUniv m) : readLevel u = u.toVLevel := by readLevel (KUniv.mkSucc u) = .succ (readLevel u) := by rw [readLevel_eq, KUniv.toVLevel_mkSucc, ← readLevel_eq] +/-- The simplifying Pi-universe constructor preserves evaluation on its +finite input subterms, including its Prop and nonzero branches. -/ +theorem readLevel_mkIMax {left right : KUniv m} + (faithful : ∀ a b, (KUniv.Sub a left ∨ KUniv.Sub a right) → + (KUniv.Sub b left ∨ KUniv.Sub b right) → a.AddrFaithful b) + (leftBound : left.size < UInt64.size) (rightBound : right.size < UInt64.size) : + readLevel (KUniv.mkIMax left right) ≈ .imax (readLevel left) (readLevel right) := by + simpa only [readLevel_eq] using KUniv.toVLevel_mkIMax faithful leftBound rightBound + /-- The structural reader preserves universe evaluation. -/ theorem readLevel_eval (levels : List Nat) (u : KUniv m) : (readLevel u).eval levels = u.toVLevel.eval levels := by diff --git a/Ix/Kernel/Verify/Consistency/Production.lean b/Ix/Kernel/Verify/Consistency/Production.lean index 0ae6cc111..f064fabaa 100644 --- a/Ix/Kernel/Verify/Consistency/Production.lean +++ b/Ix/Kernel/Verify/Consistency/Production.lean @@ -5,6 +5,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Driver import Ix.Kernel.Verify.Consistency.Constant +import Ix.Kernel.Verify.Consistency.BinderInference /-! # Standalone production declaration checks @@ -97,16 +98,20 @@ structure DefinitionInput where def DefinitionInput.constant (input : DefinitionInput) : KConst .anon := .defn () () input.kind input.safety input.hints 0 input.type input.value () input.block -/-- Closed sort/alias inference, or a specialization of an existing constant. -The declared type supplies only raw syntax and occurrence annotations. Its -typing, scope, and references are derived from successful production inference. -/ +/-- Closed sort/alias inference, a specialization of an existing constant, +or a finite binder inference tree with a separately checked declared type. +Specializations supply raw syntax and occurrence annotations; successful +inference derives their typing, scope, and references. Binder definitions +also supply syntactic scope and empty constant-reference inventories. Every +case derives body typing without a semantic typing premise. -/ inductive DefinitionBodySupport {β : Type u} (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) + (methods : Methods .anon) (before : TcState .anon) (declared : KExpr .anon) : KExpr .anon → AExpr β → AExpr β → Type u | atomic {term : KExpr .anon} {body type : AExpr β} (inference : AtomicInference resolve entries before term body type) : - DefinitionBodySupport resolve entries before declared term body type + DefinitionBodySupport resolve entries methods before declared term body type | specialization {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} (misses : UncachedInference before (.const id arguments info)) @@ -115,14 +120,25 @@ inductive DefinitionBodySupport {β : Type u} (reading : readExpr? resolve declared = some type.erase) (conditions : (entry.type.instL (arguments.toList.map readLevel)).annotations = type.annotations) : - DefinitionBodySupport resolve entries before declared (.const id arguments info) + DefinitionBodySupport resolve entries methods before declared (.const id arguments info) (.const ref (arguments.toList.map readLevel)) type + | binder {fuel : Nat} {term inferredType : KExpr .anon} + {body type : AExpr β} {level : VLevel} {typeBefore typeAfter : TcState .anon} + (tied : methods = methodsN fuel) + (valueInference : BinderInference resolve entries [] [] fuel before term body type) + (typeInference : BinderInference resolve entries [] [] fuel typeBefore declared type (.sort level)) + (typeRun : RecM.infer declared methods typeBefore = .ok inferredType typeAfter) + (valueReading : readScopedExpr? resolve [] term = some body.erase) + (typeReading : readScopedExpr? resolve [] declared = some type.erase) + (scope : body.Scope 0 0 ∧ type.Scope 0 0) + (noConstants : body.references = [] ∧ type.references = []) : + DefinitionBodySupport resolve entries methods before declared term body type theorem DefinitionBodySupport.sound {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {before after : TcState .anon} {term declared inferred : KExpr .anon} {body type : AExpr β} {methods : Methods .anon} - (fragment : DefinitionBodySupport resolve entries before declared term body type) + (fragment : DefinitionBodySupport resolve entries methods before declared term body type) (wellFormed : entries.WF) (accepted : RecM.infer term methods before = .ok inferred after) (faithful : inferred.AddrFaithful declared) (hashPath : (inferred == declared) = true) : @@ -158,6 +174,18 @@ theorem DefinitionBodySupport.sound {β : Type u} · intro ref member rw [← equal, references] at member exact wellFormed.typeReferences _ _ support.found ref member + | binder tied valueInference typeInference typeRun valueReading typeReading scope noConstants => + subst methods + obtain ⟨_, typeChecked⟩ := typeInference.sound (LocalContextReading.empty _ _) + typeReading typeRun + obtain ⟨_, valueChecked⟩ := valueInference.sound (LocalContextReading.empty _ _) + valueReading accepted + refine ⟨readScopedExpr?_closed valueReading, readScopedExpr?_closed typeReading, + scope.1, scope.2, ?_, ?_, valueChecked.typing typeChecked.typingSort⟩ + · intro ref member + simp only [noConstants.1, List.not_mem_nil] at member + · intro ref member + simp only [noConstants.2, List.not_mem_nil] at member /-- The execution prefix through value conversion. A successful member check also passes the subsequent safety checks. -/ @@ -224,7 +252,8 @@ structure AtomicDefinitionRun {β : Type u} (resolve : Address → Option (Const (body type : AExpr β) where path : StandalonePrefix input.id before input.constant inference : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, - DefinitionBodySupport resolve entries trace.valueStart input.type input.value body type + DefinitionBodySupport resolve entries (methodsN before.recFuel.toNat) + trace.valueStart input.type input.value body type hashPath : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, (trace.inferredValue == input.type) = true faithful : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, diff --git a/Ix/Kernel/Verify/Consistency/ScopedExpr.lean b/Ix/Kernel/Verify/Consistency/ScopedExpr.lean new file mode 100644 index 000000000..94caa9729 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/ScopedExpr.lean @@ -0,0 +1,258 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Expr +import Ix.Theory.Model.LevelCongruence + +/-! +# Reading opened binders + +The production binder paths use free variables for their active locals. +`readScopedExpr?` maps those identifiers to model de Bruijn indices, keeping +syntactically bound variables distinct from active locals. Unknown free +variables and loose legacy variables fail. This binder fragment also excludes +lets and strings; the original closed reader remains available independently. + +The local identifier list is newest first, matching `Model.Context.push`. +No local declaration's type or semantic validity is assumed by this reader. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u +variable {β : Type u} {m : Mode} + +def localIndex? (locals : List FVarId) (id : FVarId) : Option Nat := + match locals with + | [] => none + | head :: rest => if id = head then some 0 else (localIndex? rest id).map Nat.succ + +theorem localIndex?_mem {locals : List FVarId} {id : FVarId} {index : Nat} + (found : localIndex? locals id = some index) : id ∈ locals := by + induction locals generalizing index with + | nil => contradiction + | cons head rest ih => + by_cases equal : id = head + · simp [equal] + · simp only [localIndex?, equal, if_false] at found + obtain ⟨previous, found, _⟩ := Option.map_eq_some_iff.mp found + exact List.mem_cons_of_mem _ (ih found) + +theorem localIndex?_getElem {locals : List FVarId} {id : FVarId} {index : Nat} + (found : localIndex? locals id = some index) : locals[index]? = some id := by + induction locals generalizing index with + | nil => contradiction + | cons head rest ih => + by_cases equal : id = head + · simp only [localIndex?, equal, if_true, Option.some.injEq] at found + subst index + simp [equal] + · simp only [localIndex?, equal, if_false] at found + obtain ⟨previous, hit, rfl⟩ := Option.map_eq_some_iff.mp found + exact ih hit + +theorem localIndex?_fresh {locals : List FVarId} {id fresh : FVarId} {index : Nat} + (absent : fresh ∉ locals) (found : localIndex? locals id = some index) : + localIndex? (fresh :: locals) id = some (index + 1) := by + have different : id ≠ fresh := fun equal => absent (equal ▸ localIndex?_mem found) + simp [localIndex?, different, found] + +/-- Structural reading in the currently opened local context. `depth` counts +only syntactic binders still present inside the expression. -/ +def readScopedExpr? (resolve : Address → Option (ConstRef β)) (locals : List FVarId) : + KExpr m → (depth : Nat := 0) → Option (VExpr β) + | .var index _ _, depth => + if index.toNat < depth then some (.bvar index.toNat) else none + | .fvar id _ _, depth => + (localIndex? locals id).map (fun index => .bvar (depth + index)) + | .sort level _, _ => some (.sort (readLevel level)) + | .const id levels _, _ => do + return .const (← resolve id.addr) (levels.toList.map readLevel) + | .app fn arg _, depth => do + return .app (← readScopedExpr? resolve locals fn depth) + (← readScopedExpr? resolve locals arg depth) + | .lam _ _ domain body _, depth => do + return .lam (← readScopedExpr? resolve locals domain depth) + (← readScopedExpr? resolve locals body (depth + 1)) + | .all _ _ domain body _, depth => do + return .forallE (← readScopedExpr? resolve locals domain depth) + (← readScopedExpr? resolve locals body (depth + 1)) + | .prj id index value _, depth => do + return .proj (← resolve id.addr) index.toNat + (← readScopedExpr? resolve locals value depth) + | .nat value _ _, _ => some (.natLit value) + | .letE .., _ | .str .., _ => none + +@[simp] theorem readScopedExpr?_mkVar (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (index : UInt64) (name : m.F Name) (depth : Nat) : + readScopedExpr? resolve locals (KExpr.mkVar index name) depth = + if index.toNat < depth then some (.bvar index.toNat) else none := rfl + +@[simp] theorem readScopedExpr?_mkFVar (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (id : FVarId) (name : m.F Name) (depth : Nat) : + readScopedExpr? resolve locals (KExpr.mkFVar id name) depth = + (localIndex? locals id).map (fun index => .bvar (depth + index)) := rfl + +@[simp] theorem readScopedExpr?_mkSort (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (level : KUniv m) (depth : Nat) : + readScopedExpr? resolve locals (KExpr.mkSort level) depth = some (.sort (readLevel level)) := rfl + +@[simp] theorem readScopedExpr?_mkApp (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (fn arg : KExpr m) (depth : Nat) : + readScopedExpr? resolve locals (KExpr.mkApp fn arg) depth = do + return .app (← readScopedExpr? resolve locals fn depth) + (← readScopedExpr? resolve locals arg depth) := rfl + +@[simp] theorem readScopedExpr?_mkLam (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (name : m.F Name) (bi : m.F Lean.BinderInfo) + (domain body : KExpr m) (depth : Nat) : + readScopedExpr? resolve locals (KExpr.mkLam name bi domain body) depth = do + return .lam (← readScopedExpr? resolve locals domain depth) + (← readScopedExpr? resolve locals body (depth + 1)) := rfl + +@[simp] theorem readScopedExpr?_mkAll (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (name : m.F Name) (bi : m.F Lean.BinderInfo) + (domain body : KExpr m) (depth : Nat) : + readScopedExpr? resolve locals (KExpr.mkAll name bi domain body) depth = do + return .forallE (← readScopedExpr? resolve locals domain depth) + (← readScopedExpr? resolve locals body (depth + 1)) := rfl + +@[simp] theorem readScopedExpr?_mkPrj (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (id : KId m) (index : UInt64) (value : KExpr m) (depth : Nat) : + readScopedExpr? resolve locals (KExpr.mkPrj id index value) depth = do + return .proj (← resolve id.addr) index.toNat + (← readScopedExpr? resolve locals value depth) := rfl + +private theorem option_bind_success {α γ : Type _} {action : Option α} + {next : α → Option γ} {result : γ} (run : action.bind next = some result) : + ∃ intermediate, action = some intermediate ∧ next intermediate = some result := by + cases action with + | none => contradiction + | some value => exact ⟨value, rfl, run⟩ + +theorem readScopedExpr?_lam_parts {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {name : m.F Name} {bi : m.F Lean.BinderInfo} + {domain body : KExpr m} {info : ExprInfo m} {A b : VExpr β} {depth : Nat} + (reading : readScopedExpr? resolve locals (.lam name bi domain body info) depth = + some (.lam A b)) : + readScopedExpr? resolve locals domain depth = some A ∧ + readScopedExpr? resolve locals body (depth + 1) = some b := by + rw [readScopedExpr?] at reading + obtain ⟨A', domainReads, reading⟩ := option_bind_success reading + obtain ⟨b', bodyReads, reading⟩ := option_bind_success reading + cases reading + exact ⟨domainReads, bodyReads⟩ + +theorem readScopedExpr?_all_parts {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {name : m.F Name} {bi : m.F Lean.BinderInfo} + {domain body : KExpr m} {info : ExprInfo m} {A B : VExpr β} {depth : Nat} + (reading : readScopedExpr? resolve locals (.all name bi domain body info) depth = + some (.forallE A B)) : + readScopedExpr? resolve locals domain depth = some A ∧ + readScopedExpr? resolve locals body (depth + 1) = some B := by + rw [readScopedExpr?] at reading + obtain ⟨A', domainReads, reading⟩ := option_bind_success reading + obtain ⟨B', bodyReads, reading⟩ := option_bind_success reading + cases reading + exact ⟨domainReads, bodyReads⟩ + +/-- Closing the local context recovers the original reader exactly. -/ +theorem readScopedExpr?_closed {resolve : Address → Option (ConstRef β)} + {term : KExpr m} {source : VExpr β} {depth : Nat} + (reading : readScopedExpr? resolve [] term depth = some source) : + readExpr? resolve term = some source := by + induction term generalizing source depth with + | var index name info => + simp only [readScopedExpr?] at reading + split at reading + · exact reading + · contradiction + | fvar _ _ _ | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | sort _ _ | const _ _ _ | nat _ _ _ => exact reading + | app fn arg info hf ha | lam _ _ fn arg info hf ha | all _ _ fn arg info hf ha => + rw [readScopedExpr?] at reading + obtain ⟨f, fReads, reading⟩ := option_bind_success reading + obtain ⟨a, aReads, reading⟩ := option_bind_success reading + cases reading + simp [readExpr?, hf fReads, ha aReads] + | prj id index value info ih => + rw [readScopedExpr?] at reading + obtain ⟨ref, resolved, reading⟩ := option_bind_success reading + obtain ⟨value, valueReads, reading⟩ := option_bind_success reading + cases reading + simp [readExpr?, resolved, ih valueReads] + +@[simp] theorem readScopedExpr?_eraseMeta + (resolve : Address → Option (ConstRef β)) (locals : List FVarId) + (term : KExpr m) (depth : Nat) : + readScopedExpr? resolve locals term.eraseMeta depth = + readScopedExpr? resolve locals term depth := by + induction term generalizing depth <;> + simp_all [readScopedExpr?, KExpr.eraseMeta, KId.eraseMeta, + Array.toList_map, List.map_map, Function.comp_def] + +theorem beq_readScopedExpr? {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {left right : KExpr m} {depth : Nat} + (faithful : left.AddrFaithful right) (equal : (left == right) = true) : + readScopedExpr? resolve locals left depth = readScopedExpr? resolve locals right depth := by + have erased := faithful (KExpr.beq_def left right ▸ equal) + simpa only [readScopedExpr?_eraseMeta] using + congrArg (fun term => readScopedExpr? resolve locals term depth) erased + +theorem internExpr_readScopedExpr? {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {table : InternTable m} {term : KExpr m} {depth : Nat} + (coherent : table.WF) + (faithful : KExpr.KeyCollisionFree fun value => table.ExprSupport value ∨ value = term) : + readScopedExpr? resolve locals (table.internExpr term).1 depth = + readScopedExpr? resolve locals term depth := by + simpa only [readScopedExpr?_eraseMeta] using + congrArg (fun term => readScopedExpr? resolve locals term depth) + (table.internExpr_eraseMeta coherent faithful) + +/-- A fresh local shifts every existing local occurrence by one, below the +syntactic binders. Reading success excludes a hidden occurrence of the new id. -/ +theorem readScopedExpr?_push {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {term : KExpr m} {source : VExpr β} {depth : Nat} + {fresh : FVarId} (absent : fresh ∉ locals) + (reading : readScopedExpr? resolve locals term depth = some source) : + readScopedExpr? resolve (fresh :: locals) term depth = some (source.liftN 1 depth) := by + induction term generalizing source depth with + | var index name info => + simp only [readScopedExpr?] at reading ⊢ + split at reading + next bound => + cases reading + simp [bound, VExpr.liftN, liftVar] + · contradiction + | fvar id name info => + rw [readScopedExpr?] at reading + obtain ⟨index, found, reading⟩ := Option.map_eq_some_iff.mp reading + cases reading + simp [readScopedExpr?, localIndex?_fresh absent found, VExpr.liftN, liftVar, + Nat.not_lt.mpr (Nat.le_add_right depth index), Nat.add_assoc, Nat.add_comm 1] + | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | sort level info => cases reading; rfl + | nat value name info => cases reading; rfl + | const id levels info => + rw [readScopedExpr?] at reading + obtain ⟨ref, resolved, reading⟩ := option_bind_success reading + cases reading + simp [readScopedExpr?, resolved, VExpr.liftN] + | app fn arg info hf ha | lam _ _ fn arg info hf ha | all _ _ fn arg info hf ha => + rw [readScopedExpr?] at reading + obtain ⟨f, fReads, reading⟩ := option_bind_success reading + obtain ⟨a, aReads, reading⟩ := option_bind_success reading + cases reading + simp [readScopedExpr?, hf fReads, ha aReads, VExpr.liftN] + | prj id index value info ih => + rw [readScopedExpr?] at reading + obtain ⟨ref, resolved, reading⟩ := option_bind_success reading + obtain ⟨value, valueReads, reading⟩ := option_bind_success reading + cases reading + simp [readScopedExpr?, resolved, ih valueReads, VExpr.liftN] + +end Ix.Kernel.Consistency diff --git a/Ix/Theory/Model/Checking.lean b/Ix/Theory/Model/Checking.lean new file mode 100644 index 000000000..3e9836ae3 --- /dev/null +++ b/Ix/Theory/Model/Checking.lean @@ -0,0 +1,69 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Judgment + +/-! +# Checking against a formed type + +Inference of a lambda constructs a dependent function type from the body's +inferred type. It does not independently infer the sort of that codomain. +`CheckingClaim` defers hereditary validity of the expected type until the +declaration's separate type check establishes it. Its rule producers still +quantify over every dependency model, level valuation, and valid context. +-/ + +namespace Ix.Theory.Model + +open SetTheory SetModel Certified + +universe u v +variable {β : Type u} {entries : Environment β} {Γ : Context β} + +def CheckingClaim (entries : Environment β) (Γ : Context β) (e A : AExpr β) : Prop := + ∀ (V : Type v) [SetTheory V] (constants : Assignment β V), + Realizes constants entries → ∀ levels env, Γ.Valid constants levels env → + WellDenoted constants levels env A → + WellDenoted constants levels env e ∧ + interp constants levels env e ∈ˢ interp constants levels env A + +theorem TypingClaim.checking {e A : AExpr β} (typed : TypingClaim.{u,v} entries Γ e A) : + CheckingClaim.{u,v} entries Γ e A := by + intro V _ constants realizes levels env valid _ + have checked := typed V constants realizes levels env valid + exact ⟨checked.1, checked.2.2⟩ + +namespace CheckingClaim + +theorem typing {e A : AExpr β} {level : VLevel} + (checked : CheckingClaim.{u,v} entries Γ e A) + (formed : TypingClaim.{u,v} entries Γ A (.sort level)) : + TypingClaim.{u,v} entries Γ e A := by + intro V _ constants realizes levels env valid + have typeValid := (formed V constants realizes levels env valid).1 + have result := checked V constants realizes levels env valid typeValid + exact ⟨result.1, typeValid, result.2⟩ + +theorem typingSort {e : AExpr β} {level : VLevel} + (checked : CheckingClaim.{u,v} entries Γ e (.sort level)) : + TypingClaim.{u,v} entries Γ e (.sort level) := + checked.typing (TypingClaim.sort level) + +/-- The expected Pi supplies a uniform codomain universe and hereditary +validity, including the Prop regime when the domain is empty. -/ +theorem lam {A B body : AExpr β} {condition : PropWhen} + (checked : CheckingClaim.{u,v} entries (Γ.push A) body B) : + CheckingClaim.{u,v} entries Γ (.lam condition A body) (.forallE condition A B) := by + intro V _ constants realizes levels env valid formed + obtain ⟨domainValid, bodyValid, codomainLevel, regimeAgrees, inUniverse⟩ := formed + have bodyChecked x hx := checked V constants realizes levels (Valuation.cons x env) + (valid.push domainValid hx) (bodyValid x hx) + refine ⟨⟨domainValid, fun x hx => (bodyChecked x hx).1, codomainLevel, + (fun x => interp constants levels (Valuation.cons x env) B), regimeAgrees, + fun x hx => ⟨(bodyChecked x hx).2, inUniverse x hx⟩⟩, ?_⟩ + exact lamR_mem (fun x hx => (bodyChecked x hx).2) + +end CheckingClaim +end Ix.Theory.Model diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 9a067b61d..780b03081 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -5,8 +5,8 @@ public import Ix.Kernel public import Tests.Ix.Kernel.IxonFixtures /-! -Production regressions for the atomic consistency fragment and polymorphic -constant inference. These execute the lazy loader, inference, and serial driver +Production regressions for the consistency fragment, polymorphic constant +inference, and dependent binders. These execute the lazy loader, inference, and serial driver on content-addressed Ixon declarations. The theorems and their resource premises are checked separately by `IxKernelConsistency`. -/ @@ -238,6 +238,71 @@ private def specializationCases : TestSeq := ++ test "polymorphic environment: a monomorphic body cannot retain a universe parameter" (let (env, target) := failedSpecialization #[0] (.var 0); rowFailed env target) -public def suite : List TestSeq := [cases, polymorphicCases, specializationCases] +/-- Real function bodies, with no source axiom supplying their values: +`idProp (P : Prop) (p : P) : P := p`, its Type analogue, and a theorem alias. -/ +private def binderEnvironment : Ixon.Env := Id.run do + let type := Ixon.Expr.leanAll (.sort 0) (.leanAll (.var 0) (.var 1)) + let value := Ixon.Expr.leanLam (.sort 0) (.leanLam (.var 0) (.var 0)) + let (env, propIdentity) := storeConst {} + ⟨.defn ⟨.defn, .safe, 0, type, value⟩, #[], #[], #[.zero]⟩ + let (env, _) := storeConst env + ⟨.defn ⟨.opaq, .safe, 0, type, value⟩, #[], #[], #[.succ .zero]⟩ + let (env, _) := storeConst env + ⟨.defn ⟨.thm, .safe, 0, type, .ref 0 #[]⟩, #[], #[propIdentity], #[.zero]⟩ + return env + +private def failedBinder (body : Ixon.Expr) : Ixon.Env × Address := + storeConst {} + ⟨.defn ⟨.defn, .safe, 0, + .leanAll (.sort 0) (.leanAll (.var 0) (.var 1)), + .leanLam (.sort 0) (.leanLam (.var 0) body)⟩, #[], #[], #[.zero]⟩ + +/-- Inferring the same older local after a dependent push must reuse its +concrete type. Both cache partitions and scope cleanup are observable here. -/ +private def dependentLocalCache (inferOnly : Bool) : Bool := + let propType := KExpr.mkSort (m := .anon) .mkZero + let action : RecM .anon Bool := RecM.withLctxScope do + let (first, firstId) ← TcM.openBinder () () propType (.mkVar 0 ()) + let firstType ← RecM.inferCall first + let (_, secondId) ← TcM.openBinder () () first (.mkVar 1 ()) + let cachedType ← RecM.inferCall first + let state ← get + let key ← TcM.inferKey first + return firstId != secondId && firstType.addr == propType.addr && + cachedType.addr == propType.addr && state.lctx.size == 2 && + (if inferOnly then state.env.inferOnlyCache[key]?.isSome + else state.env.inferCache[key]?.isSome) + match TcM.runRec action { TcState.ofEnvAnon {} with inferOnly } with + | .ok passed after => passed && after.lctx.size == 0 && after.env.nextFVarId == 2 + | .error _ _ => false + +private def nestedBinderInference (inferOnly : Bool) : Bool := + let propType := KExpr.mkSort (m := .anon) .mkZero + let type := KExpr.mkAll () () propType (.mkAll () () (.mkVar 0 ()) (.mkVar 1 ())) + let body := KExpr.mkLam () () propType (.mkLam () () (.mkVar 0 ()) (.mkVar 0 ())) + match TcM.infer body { TcState.ofEnvAnon {} with inferOnly } with + | .ok inferred after => inferred.addr == type.addr && after.lctx.size == 0 && + after.env.nextFVarId == 2 + | .error _ _ => false + +private def binderCases : TestSeq := + test "binder environment: real Prop and Type identity bodies and theorem alias check" + (allSucceeded binderEnvironment 3 { clearEvery := 0 }) + ++ test "binder environment: identity bodies check with fresh per-item caches" + (allSucceeded binderEnvironment 3 { clearEvery := 1 }) + ++ test "binder inference: nested dependent lambdas close their local context" + (nestedBinderInference false) + ++ test "binder inference: inference-only lambdas return the same closed type" + (nestedBinderInference true) + ++ test "binder cache: full-mode lookup survives a newer dependent local" + (dependentLocalCache false) + ++ test "binder cache: inference-only lookup survives a newer dependent local" + (dependentLocalCache true) + ++ test "binder environment: returning the type variable instead of its witness fails" + (let (env, target) := failedBinder (.var 1); rowFailed env target) + ++ test "binder environment: an escaping bound variable fails validation" + (let (env, target) := failedBinder (.var 2); rowFailed env target) + +public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases] end Tests.Kernel.Consistency diff --git a/Tests/Theory/ImportManifest.lean b/Tests/Theory/ImportManifest.lean index f074c0b9f..ee4f7d141 100644 --- a/Tests/Theory/ImportManifest.lean +++ b/Tests/Theory/ImportManifest.lean @@ -16,7 +16,9 @@ def lean4IxBaseRevision : String := "ab42e79e2a4e2615a3ca6ef983d510f374057a38" def conLecheRevision : String := "86cd20a65660d757cedc81561a44579099b565d0" /-- New mathematical modules authored in Ix, with no imported source hash. -/ -def authored : Array String := #["Ix/Theory/Model/LevelCongruence.lean"] +def authored : Array String := #[ + "Ix/Theory/Model/Checking.lean", "Ix/Theory/Model/LevelCongruence.lean" +] structure SourceFile where source : String diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 10357a0c9..92485e5b5 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -30,8 +30,10 @@ increasing sequence of strongly inaccessible cardinals. under the existing finite address-faithfulness and arithmetic bounds. - A structural reader maps kernel expressions to model syntax, resolves addresses to explicit store references, preserves projections and natural - literals, and substitutes let values. Free variables, unresolved addresses, - and string literals are outside the current reader's domain. + literals, and substitutes let values. This closed reader excludes free + variables, unresolved addresses, and string literals. The binder reader + `readScopedExpr?` maps registered free variables to model context indices; + unknown locals, loose legacy variables, lets, and strings fail that reader. - Hash equality and intern-table reuse preserve that reading under their stated address/key collision assumptions. Metadata cannot change it. - `inferUncached_sort_sound` interprets an actual successful execution of the @@ -56,6 +58,15 @@ increasing sequence of strongly inaccessible cardinals. - `ModelTyping.no_false` rules out a closed model-typed kernel expression at primitive False when its environment has been admitted by the certified interface and the set-theory assumption has an instance. +- `BinderInference.sound` follows finite production inference trees for sorts, + locals, dependent functions, and full-mode lambdas. `LocalContextReading.push` + and `openBinder_sound` connect actual declaration lookup and fresh-variable + opening to the model's dependent context. Singleton abstraction closes the + resulting function type, and the simplifying `imax` constructor preserves + its universe interpretation. +- `FVarInferenceSupport.sound` handles both local-variable cache partitions + when a cached answer equals the current declaration's concrete type. These + are structural cache checks, with no assumed semantic typing of cached data. - `checkEnvAnon_atomic_preserves_model` connects a supported production environment run to model extension. `checkEnvAnon_atomic_no_false` excludes a declaration at an axiom type interpreted as empty, including False. @@ -78,9 +89,9 @@ these axioms. The hypothesis is model existence; a theorem connecting arbitrary syntactic consistency to model existence is outside this result. The fragment covers monomorphic standalone definitions, theorems, -and opaque definitions whose values are either closed universe terms or -references to preceding interface entries, including monomorphic -specializations of polymorphic constants. Referenced types may contain +and opaque definitions whose values are closed universe terms, references to +preceding interface entries, monomorphic specializations of polymorphic +constants, or closed function bodies in the binder fragment. Referenced types may contain dependent functions and other readable expression forms. Examples include: ```lean @@ -91,6 +102,7 @@ theorem r : P := q def typeAlias : Type := Prop axiom ident.{u} : (α : Sort u) → α → α def propIdent : (α : Prop) → α → α := ident.{0} +def idProp (P : Prop) (p : P) : P := p ``` `AtomicEnvironmentFragment` records the precise execution boundary: @@ -98,7 +110,9 @@ def propIdent : (α : Prop) → α → α := ident.{0} - Every source key occurs in the `buildAnonWork` result, and every work item represents an axiom or a definition. Lookup, routing, and reset witnesses identify the checked `KConst`. -- Value inference misses both cache partitions. Constant lookup agrees with +- Sort, constant, forall, and lambda nodes in the inference witnesses miss both cache partitions. + Local-variable hits must match the current production declaration type. + Constant lookup agrees with an already admitted type and universe count. A specialization supplies closed universe arguments and finite interning/substitution resources at the actual post-lookup state. The occurrence annotations on the declared @@ -106,6 +120,16 @@ def propIdent : (α : Prop) → α → α := ident.{0} data checks; successful inference derives typing, scope, and references. Ordinary aliases retain the simpler empty-substitution path. Sort inference retains finite interning coherence and address-faithfulness premises. +- Binder definitions supply finite inference trees for both the value and its + separately checked declared type, exact source readings, closed annotated + syntax, and empty constant-reference lists. Recursive calls use the actual + smaller method table. Binder-opening and abstraction resources cover fresh + ids, context preservation during domain inference, intern-table coherence, + finite collision freedom, and bounds preventing index overflow. Sort + exposures are syntactic; lambdas use full mode and the unchanged cheap-beta + path. `CheckingClaim` derives body validity and membership once declared-type + inference establishes hereditary validity. No codomain-typing premise is + added to the production proof. - Conversion takes the initial hash-equality path, with faithfulness of the compared expressions. General reduction and conversion caches are outside this fragment. @@ -121,9 +145,9 @@ Callers must establish these operational and representation witnesses for the run. They supply no typing or checker-soundness premise. The proof extracts the validation, type-inference, theorem-guard, value-inference, and conversion steps from public success, then derives body typing to extend -the preceding model. Automatic witness construction, lambdas, applications, -inductives, coordinated blocks, and other conversion paths remain outside the -fragment. Polymorphic constant inference is composed into declaration +the preceding model. Automatic witness construction, application inference, +lets, more general lambda paths, inductives, coordinated blocks, and other +conversion paths remain outside the fragment. Polymorphic constant inference is composed into declaration admission and model extension for the monomorphic specializations described above. Definitions with their own universe parameters remain outside this environment fragment. @@ -177,7 +201,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 66 exact theorem boundaries. The production +The consistency target checks 101 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -187,9 +211,10 @@ The production roots additionally forbid the abstract `CheckSuccessSound`/`SupportedCheckFragment` interfaces and the independent certificate validator in their dependency closures. -The polymorphic inference and substitution roots retain only the two existing +The polymorphic inference, substitution, and binder inference roots retain only the two existing expression/universe output-length proofs, alongside the standard Lean axioms. Their model-side level congruence introduces no native proof dependency. +Semantic checking against a formed type also uses only standard Lean axioms. Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, argument order, and rejection of wrong arities and out-of-range parameters. @@ -223,6 +248,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Certified checker contracts | [`Ix/Kernel/Certified.lean`](../Ix/Kernel/Certified.lean), [`CertifiedClaims.lean`](../Ix/Kernel/CertifiedClaims.lean) | | Direct production refinement and its audit | [`Ix/Kernel/Verify/Consistency.lean`](../Ix/Kernel/Verify/Consistency.lean) | | Polymorphic inference and universe substitution | [`Consistency/Constant.lean`](../Ix/Kernel/Verify/Consistency/Constant.lean), [`InstUniv.lean`](../Ix/Kernel/Verify/Consistency/InstUniv.lean), [`Model/LevelCongruence.lean`](../Ix/Theory/Model/LevelCongruence.lean) | +| Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | | Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | | Host commands, receipts, and frozen regression evidence | [Certified checking guide](certified-checking.md) | @@ -236,5 +262,6 @@ fixtures used by the existing proofs. Source hashes and attribution are in [`Tests/Theory/NamedManifest.lean`](../Tests/Theory/NamedManifest.lean); the Apache license is preserved alongside the sources. The axiom-audit helper and direct production-fragment proofs are authored in Ix. -`Model/LevelCongruence.lean` is an Ix-authored mathematical addition, listed +`Model/LevelCongruence.lean` and `Model/Checking.lean` are Ix-authored mathematical +additions, listed separately from the imported files in the theory provenance manifest. diff --git a/docs/theory.md b/docs/theory.md index 880ed6d3f..a470dd06a 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -20,9 +20,11 @@ open frontiers retain their stated model hypotheses. The [certified host adapters](certified-checking.md) connect authenticated serialized Ixon to model admission. Separately, a production `checkEnvAnon` [fragment](kernel-verification.md#production-environment-fragment) for -monomorphic aliases, closed universe terms, and monomorphic specializations -of polymorphic constants extends every model of its source axioms under -explicit execution witnesses. Full checker consistency +monomorphic aliases, closed universe terms, monomorphic specializations of +polymorphic constants, and a fragment of closed dependent function bodies +extends every model of its source axioms under explicit execution witnesses. +Binder inference uses the declared type's separate formation check to turn +`Model.CheckingClaim` into semantic typing. Full checker consistency and compiler/backend refinement remain open. `Ix.Theory.Named` retains the local name-indexed specification and proof From 7ee64b4618c9a033577aaf9ab297ba6d9fc364ac Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 12:32:57 -0400 Subject: [PATCH 09/63] Refine applications and constant references in function bodies --- Ix/Kernel/Verify/Consistency.lean | 11 +- Ix/Kernel/Verify/Consistency/Application.lean | 239 ++++++++++++++++++ Ix/Kernel/Verify/Consistency/Audit.lean | 7 +- .../Verify/Consistency/BinderInference.lean | 144 ++++++++++- Ix/Kernel/Verify/Consistency/Production.lean | 14 +- Ix/Kernel/Verify/Consistency/ScopedExpr.lean | 12 + Ix/Theory/Model/Checking.lean | 12 + Tests/Ix/Kernel/Consistency.lean | 106 +++++++- docs/kernel-verification.md | 36 ++- docs/theory.md | 5 +- 10 files changed, 552 insertions(+), 34 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/Application.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 3ab048595..414d1a0df 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -13,6 +13,7 @@ import Ix.Kernel.Verify.Consistency.Atomic import Ix.Kernel.Verify.Consistency.ScopedExpr import Ix.Kernel.Verify.Consistency.Context import Ix.Kernel.Verify.Consistency.BinderOpening +import Ix.Kernel.Verify.Consistency.Application import Ix.Kernel.Verify.Consistency.BinderInference import Ix.Kernel.Verify.Consistency.Production import Ix.Kernel.Verify.Consistency.Environment @@ -26,13 +27,17 @@ transport keeps its representation, arithmetic, and dependency assumptions explicit. A production `checkEnvAnon` fragment preserves models of its axiom set for monomorphic aliases, closed sorts, monomorphic specializations of polymorphic constants, and closed function bodies built from sorts, locals, -dependent functions, and full-mode lambdas under the stated execution resources. +monomorphic references, applications, dependent functions, and full-mode lambdas +under the stated execution resources. Constant inference supports arbitrary readable entry types, using the actual universe-instantiation walker and explicit lookup and finite-support resources. The returned type's scope and references justify declaration admission. -The binder case also requires explicit syntactic scope and empty constant -reference lists. Its separate declared-type inference turns semantic checking +The binder case also requires explicit syntactic scope and references to the +preceding interface. Its separate declared-type inference turns semantic checking into typing. Local cache hits agree with the actual declaration type; other inference nodes retain cache-miss boundaries. +Applications use syntactic Pi exposure, full argument checking, hash conversion, +and the ordinary eager-marker path. Constant- and local-headed spines derive +type validity from the admitted model or local context; their arguments may be lambdas. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Application.lean b/Ix/Kernel/Verify/Consistency/Application.lean new file mode 100644 index 000000000..acdee2e99 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Application.lean @@ -0,0 +1,239 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BinderOpening +import Ix.Kernel.Knot + +/-! +# Production application and dependent substitution + +The application result is the actual memoized substitution of its argument +into the inferred Pi codomain. Registered locals remain free in the kernel +tree while their model indices move below each syntactic binder. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u +variable {β : Type u} + +private theorem bind_success {α γ : Type _} {action : Option α} + {next : α → Option γ} {result : γ} (run : action.bind next = some result) : + ∃ intermediate, action = some intermediate ∧ next intermediate = some result := by + cases action with + | none => contradiction + | some value => exact ⟨value, rfl, run⟩ + +private theorem depth_succ {depth : UInt64} (bound : depth.toNat + 1 < UInt64.size) : + (depth + 1).toNat = depth.toNat + 1 := by + rw [UInt64.toNat_add, show (1 : UInt64).toNat = 1 from rfl, Nat.mod_eq_of_lt bound] + +/-- Lifting a term with no loose legacy variables shifts the reading of its +registered locals beneath the new syntactic binders. -/ +theorem readScopedExpr?_liftSpec + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {term : KExpr .anon} {source : VExpr β} {depth shift : UInt64} + (bound : depth.toNat + term.size < UInt64.size) + (reading : readScopedExpr? resolve locals term depth.toNat = some source) : + readScopedExpr? resolve locals (KExpr.liftSpec term shift depth) + (depth.toNat + shift.toNat) = some (source.liftN shift.toNat depth.toNat) := by + induction term generalizing source depth with + | var index name info => + simp only [readScopedExpr?] at reading + split at reading + next inScope => + cases reading + have below : ¬ index ≥ depth := by + simp only [UInt64.le_iff_toNat_le]; omega + simp [KExpr.liftSpec, below, readScopedExpr?, show index.toNat < + depth.toNat + shift.toNat by omega, VExpr.liftN, liftVar, inScope] + · contradiction + | fvar id name info => + rw [readScopedExpr?] at reading + obtain ⟨index, found, reading⟩ := Option.map_eq_some_iff.mp reading + cases reading + simp only [KExpr.liftSpec, readScopedExpr?, found, Option.map_some, VExpr.liftN, liftVar, + Nat.not_lt.mpr (Nat.le_add_right depth.toNat index), if_false] + congr 2 + omega + | sort _ _ | nat _ _ _ => cases reading; rfl + | const id levels info => + rw [readScopedExpr?] at reading + obtain ⟨ref, resolved, reading⟩ := bind_success reading + cases reading + simp [KExpr.liftSpec, readScopedExpr?, resolved, VExpr.liftN] + | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | app fn arg info hf ha => + rw [readScopedExpr?] at reading + obtain ⟨f, fReads, reading⟩ := bind_success reading + obtain ⟨a, aReads, reading⟩ := bind_success reading + cases reading + simp only [KExpr.size] at bound + simp [KExpr.liftSpec, hf (by omega) fReads, ha (by omega) aReads, VExpr.liftN] + | lam name bi domain body info hd hb | all name bi domain body info hd hb => + rw [readScopedExpr?] at reading + obtain ⟨A, domainReads, reading⟩ := bind_success reading + obtain ⟨B, bodyReads, reading⟩ := bind_success reading + cases reading + simp only [KExpr.size] at bound + have next := depth_succ (depth := depth) (by omega) + have bodyOut := hb (depth := depth + 1) (by rw [next]; omega) + (by simpa only [next] using bodyReads) + simp only [next] at bodyOut + simp [KExpr.liftSpec, hd (by omega) domainReads, VExpr.liftN, + show depth.toNat + shift.toNat + 1 = depth.toNat + 1 + shift.toNat by omega, + bodyOut] + | prj id index value info ih => + rw [readScopedExpr?] at reading + obtain ⟨ref, resolved, reading⟩ := bind_success reading + obtain ⟨value, valueReads, reading⟩ := bind_success reading + cases reading + simp only [KExpr.size] at bound + simp [KExpr.liftSpec, resolved, ih (by omega) valueReads, VExpr.liftN] + +/-- Removing one syntactic binder agrees with model substitution, including +arguments that contain registered locals and their own nested binders. -/ +theorem readScopedExpr?_substSpec + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {body arg : KExpr .anon} {source argument : VExpr β} {depth : UInt64} + (bound : depth.toNat + body.size + 1 < UInt64.size) + (argBound : arg.size < UInt64.size) + (bodyReads : readScopedExpr? resolve locals body (depth.toNat + 1) = some source) + (argReads : readScopedExpr? resolve locals arg = some argument) : + readScopedExpr? resolve locals (KExpr.substSpec body arg depth) depth.toNat = + some (source.inst argument depth.toNat) := by + induction body generalizing source depth with + | var index name info => + simp only [readScopedExpr?] at bodyReads + split at bodyReads + next inScope => + cases bodyReads + by_cases equal : index = depth + · subst index + simp only [KExpr.substSpec, beq_self_eq_true, if_true, VExpr.inst, VExpr.instVar, + Nat.lt_irrefl, if_false] + simpa only [UInt64.toNat_zero, Nat.zero_add] using + (readScopedExpr?_liftSpec (depth := 0) (shift := depth) + (by simpa using argBound) argReads) + · have smaller : index.toNat < depth.toNat := by + have : index.toNat ≠ depth.toNat := fun h => equal (UInt64.toNat_inj.mp h) + omega + have below : ¬ index > depth := by + simp only [UInt64.lt_iff_toNat_lt]; omega + simp [KExpr.substSpec, equal, below, readScopedExpr?, smaller, + VExpr.inst, VExpr.instVar] + · contradiction + | fvar id name info => + rw [readScopedExpr?] at bodyReads + obtain ⟨index, found, bodyReads⟩ := Option.map_eq_some_iff.mp bodyReads + cases bodyReads + have greater : depth.toNat < depth.toNat + 1 + index := by omega + simp [KExpr.substSpec, readScopedExpr?, found, VExpr.inst, VExpr.instVar, + Nat.not_lt.mpr (by omega : depth.toNat ≤ depth.toNat + 1 + index), + Nat.ne_of_gt greater, show depth.toNat + 1 + index - 1 = depth.toNat + index by omega] + | sort _ _ | nat _ _ _ => cases bodyReads; rfl + | const id levels info => + rw [readScopedExpr?] at bodyReads + obtain ⟨ref, resolved, bodyReads⟩ := bind_success bodyReads + cases bodyReads + simp [KExpr.substSpec, readScopedExpr?, resolved, VExpr.inst] + | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | app fn value info hf ha => + rw [readScopedExpr?] at bodyReads + obtain ⟨f, fReads, bodyReads⟩ := bind_success bodyReads + obtain ⟨a, aReads, bodyReads⟩ := bind_success bodyReads + cases bodyReads + simp only [KExpr.size] at bound + simp [KExpr.substSpec, hf (by omega) fReads, ha (by omega) aReads, VExpr.inst] + | lam name bi domain body info hd hb | all name bi domain body info hd hb => + rw [readScopedExpr?] at bodyReads + obtain ⟨A, domainReads, bodyReads⟩ := bind_success bodyReads + obtain ⟨B, innerReads, reading⟩ := bind_success bodyReads + cases reading + simp only [KExpr.size] at bound + have next := depth_succ (depth := depth) (by omega) + have bodyOut := hb (depth := depth + 1) (by rw [next]; omega) + (by simpa only [next] using innerReads) + simp only [next] at bodyOut + simp [KExpr.substSpec, hd (by omega) domainReads, bodyOut, VExpr.inst] + | prj id index value info ih => + rw [readScopedExpr?] at bodyReads + obtain ⟨ref, resolved, bodyReads⟩ := bind_success bodyReads + obtain ⟨value, valueReads, bodyReads⟩ := bind_success bodyReads + cases bodyReads + simp only [KExpr.size] at bound + simp [KExpr.substSpec, resolved, ih (by omega) valueReads, VExpr.inst] + +/-- The actual memoized, interned walker inherits the structural substitution +reading under finite collision freedom and bounds excluding index overflow. -/ +theorem subst_readScopedExpr? + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {body arg : KExpr .anon} {source argument : VExpr β} {table : InternTable .anon} + (bodyConstructed : body.Constructed) (argConstructed : arg.Constructed) + (bodyBound : body.size + 1 < UInt64.size) (argBound : arg.size < UInt64.size) + (coherent : table.WF) + (faithful : KExpr.CollisionFree fun term => table.ExprSupport term ∨ + KExpr.SubstReach arg body 0 term) + (bodyReads : readScopedExpr? resolve locals body 1 = some source) + (argReads : readScopedExpr? resolve locals arg = some argument) : + readScopedExpr? resolve locals (subst body arg 0 table).1 = some (source.inst argument) ∧ + (subst body arg 0 table).2.WF := by + obtain ⟨result, coherent, _⟩ := subst_spec faithful bodyConstructed argConstructed + (by simpa using (show body.size < UInt64.size by omega)) argBound + (fun _ h => Or.inr h) coherent (fun _ h => Or.inl h) + refine ⟨?_, coherent⟩ + rw [result] + exact readScopedExpr?_substSpec (depth := 0) (by simpa using bodyBound) + argBound bodyReads argReads + +/-- Full-mode application takes a syntactic Pi, infers its argument, and +compares the inferred argument type with the domain. Eager markers are outside +this trace; the comparison and substitution states are the actual ones. -/ +structure ApplicationInferenceTrace (fuel : Nat) (before : TcState .anon) + (fn arg : KExpr .anon) where + name : Mode.anon.F Name + bi : Mode.anon.F Lean.BinderInfo + domain : KExpr .anon + codomain : KExpr .anon + info : ExprInfo .anon + functionState : TcState .anon + argumentType : KExpr .anon + argumentState : TcState .anon + comparedState : TcState .anon + functionRun : RecM.infer fn (methodsN fuel) before = + .ok (.all name bi domain codomain info) functionState + argumentRun : RecM.infer arg (methodsN fuel) functionState = .ok argumentType argumentState + ordinary : TcM.isEagerReduce arg argumentState = .ok false argumentState + compareRun : RecM.isDefEq argumentType domain (methodsN fuel) argumentState = + .ok true comparedState + contextPreserved : functionState.lctx = before.lctx + +/-- Inverting the successful production branch reaches its exact interned +codomain substitution after the real argument check. -/ +theorem ApplicationInferenceTrace.output {fuel : Nat} {before after : TcState .anon} + {fn arg result : KExpr .anon} {info : ExprInfo .anon} + (trace : ApplicationInferenceTrace fuel before fn arg) + (accepted : RecM.inferUncached RecM.inferCall false (.app fn arg info) + (methodsN (fuel + 1)) before = .ok result after) : + result = (subst trace.codomain arg 0 trace.comparedState.env.intern).1 := by + change (RecM.inferUncached RecM.inferCall false (.app fn arg info)).run + (methodsN (fuel + 1)) before = .ok result after at accepted + unfold RecM.inferUncached at accepted + simp only [ReaderT.run_bind] at accepted + change EStateM.bind (RecM.infer fn (methodsN fuel)) _ before = _ at accepted + rw [EStateM.bind, trace.functionRun] at accepted + change EStateM.bind (RecM.infer arg (methodsN fuel)) _ trace.functionState = _ at accepted + rw [EStateM.bind, trace.argumentRun] at accepted + change EStateM.bind (TcM.isEagerReduce arg) _ trace.argumentState = _ at accepted + rw [EStateM.bind, trace.ordinary] at accepted + change EStateM.bind (RecM.isDefEq trace.argumentType trace.domain (methodsN fuel)) + _ trace.argumentState = _ at accepted + rw [EStateM.bind, trace.compareRun] at accepted + cases accepted + rfl + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index fc7c05645..dc93d5cc0 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -39,6 +39,8 @@ private def atomicRoots : Array Lean.Name := #[ ``inferUncached_fvar_sound, ``infer_fvar_sound, ``FVarInferenceSupport.output, ``FVarInferenceSupport.sound, ``ForallInferenceTrace.output, ``LambdaInferenceTrace.output, ``BinderInference.sound, + ``inferUncached_monomorphic_const_scoped, ``ApplicationInferenceTrace.output, + ``BinderInference.soundWithSynthesis, ``BinderInference.synthesis, ``DefinitionBodySupport.sound ] @@ -75,7 +77,8 @@ private def binderWalkerRoots : Array Lean.Name := #[ ``inferKey_lctx, ``UncachedInference.localContext, ``readScopedExpr?_instantiateRevSpec, ``readScopedExpr?_abstractFVarsSpec, ``openBinder_eq, ``openBinder_sound, - ``abstractFVars_singleton_spec, ``abstractFVars_readScopedExpr? + ``abstractFVars_singleton_spec, ``abstractFVars_readScopedExpr?, + ``readScopedExpr?_liftSpec, ``readScopedExpr?_substSpec, ``subst_readScopedExpr? ] /-- Production roots must not acquire a checker-soundness assumption @@ -102,6 +105,7 @@ def roots : Array RootAllowance := #[ nativeAxioms := #[expressionNative] }, { root := ``readExpr?_eraseMeta, standardAxioms := #[``propext, ``Quot.sound] }, { root := ``readScopedExpr?_lam_parts }, + { root := ``readScopedExpr?_app_parts }, { root := ``readScopedExpr?_all_parts }, { root := ``beq_readExpr?, standardAxioms := #[``propext, ``Quot.sound] }, { root := ``internExpr_readExpr?, standardAxioms := #[``propext, ``Quot.sound] }, @@ -136,6 +140,7 @@ def roots : Array RootAllowance := #[ { root := ``Theory.Model.AExpr.LevelEquivalent.wellDenoted, standardAxioms := standard }, { root := ``Theory.Model.AExpr.LevelEquivalent.typing, standardAxioms := standard }, { root := ``Theory.Model.TypingClaim.checking, standardAxioms := standard }, + { root := ``Theory.Model.TypingClaim.appChecking, standardAxioms := standard }, { root := ``Theory.Model.CheckingClaim.typing, standardAxioms := standard }, { root := ``Theory.Model.CheckingClaim.typingSort, standardAxioms := standard }, { root := ``Theory.Model.CheckingClaim.lam, standardAxioms := standard } diff --git a/Ix/Kernel/Verify/Consistency/BinderInference.lean b/Ix/Kernel/Verify/Consistency/BinderInference.lean index d7da09fca..64688b4fd 100644 --- a/Ix/Kernel/Verify/Consistency/BinderInference.lean +++ b/Ix/Kernel/Verify/Consistency/BinderInference.lean @@ -3,8 +3,7 @@ Copyright (c) 2026 Argument Computer Corporation. SPDX-License-Identifier: MIT OR Apache-2.0 -/ -import Ix.Kernel.Verify.Consistency.BinderOpening -import Ix.Kernel.Knot +import Ix.Kernel.Verify.Consistency.Application import Ix.Theory.Model.Checking /-! @@ -23,6 +22,42 @@ open Theory Theory.Model universe u v +/-- Application spines whose type validity comes from the current local +declaration or an admitted constant. Lambda arguments may still be checked +against the domain supplied by such a function. -/ +inductive SynthesisHead {β : Type u} : AExpr β → Prop + | bvar (index : Nat) : SynthesisHead (.bvar index) + | const (ref : ConstRef β) (levels : List VLevel) : SynthesisHead (.const ref levels) + | app {fn arg : AExpr β} (head : SynthesisHead fn) : SynthesisHead (.app fn arg) + +/-- Empty universe instantiation returns the exact declaration type reached +by lazy lookup. The scoped reading can include dependent Pi types. -/ +theorem inferUncached_monomorphic_const_scoped {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {id : KId .anon} {info : ExprInfo .anon} {type : AExpr β} + {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} + {methods : Methods .anon} {before after : TcState .anon} {result : KExpr .anon} + (lookup : ∀ concrete loaded, TcM.getConst id before = .ok concrete loaded → + readScopedExpr? resolve locals concrete.ty = some type.erase) + (accepted : RecM.inferUncached inferRec inferOnly (.const id #[] info) + methods before = .ok result after) : + readScopedExpr? resolve locals result = some type.erase := by + change (RecM.inferUncached inferRec inferOnly (.const id #[] info)).run + methods before = .ok result after at accepted + unfold RecM.inferUncached at accepted + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted + change EStateM.bind (TcM.getConst id) _ before = _ at accepted + cases got : TcM.getConst id before with + | error err failed => rw [EStateM.bind, got] at accepted; contradiction + | ok concrete loaded => + rw [EStateM.bind, got] at accepted + simp only at accepted + split at accepted + · contradiction + · change EStateM.Result.ok concrete.ty loaded = .ok result after at accepted + cases accepted + exact lookup concrete _ got + private theorem withLctxScope_eq (action : RecM .anon α) (methods : Methods .anon) (before : TcState .anon) : (RecM.withLctxScope action).run methods before = @@ -183,6 +218,36 @@ inductive BinderInference {β : Type u} (registered : localIndex? locals id = some index) (atIndex : context[index]? = some A) : BinderInference resolve entries locals context fuel before (.fvar id name info) (.bvar index) A + | const {locals context fuel before id info ref entry} + (miss : UncachedInference before (.const id #[] info)) + (resolved : resolve id.addr = some ref) + (found : entries ref = some entry) + (monomorphic : entry.universes = 0) + (stable : entry.type.instL [] = entry.type) + (lookup : ∀ concrete loaded, TcM.getConst id miss.keyed = .ok concrete loaded → + readScopedExpr? resolve locals concrete.ty = some entry.type.erase) : + BinderInference resolve entries locals context fuel before (.const id #[] info) + (.const ref []) entry.type + | app {locals context fuel before fn arg info f a A A' B condition} + (full : before.inferOnly = false) + (miss : UncachedInference before (.app fn arg info)) + (trace : ApplicationInferenceTrace fuel miss.keyed fn arg) + (functionTree : BinderInference resolve entries locals context fuel miss.keyed fn + f (.forallE condition A B)) + (head : SynthesisHead f) + (argumentTree : BinderInference resolve entries locals context fuel trace.functionState arg a A') + (conditions : A'.annotations = A.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) + (bodyConstructed : trace.codomain.Constructed) + (argConstructed : arg.Constructed) + (bodyBound : trace.codomain.size + 1 < UInt64.size) + (argBound : arg.size < UInt64.size) + (coherent : trace.comparedState.env.intern.WF) + (faithful : KExpr.CollisionFree fun term => trace.comparedState.env.intern.ExprSupport term ∨ + KExpr.SubstReach arg trace.codomain 0 term) : + BinderInference resolve entries locals context (fuel + 1) before (.app fn arg info) + (.app f a) (B.inst a) | forallE {locals context fuel before name bi domain body info A B} (miss : UncachedInference before (.all name bi domain body info)) (trace : ForallInferenceTrace fuel miss.keyed name bi domain body) @@ -225,7 +290,7 @@ inductive BinderInference {β : Type u} /-- Successful production inference reads the expected model type and checks the actual source term against it. No recursive semantic premise is supplied by the caller: induction follows the finite operational support tree. -/ -theorem BinderInference.sound {β : Type u} +theorem BinderInference.soundWithSynthesis {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {fuel : Nat} {before after : TcState .anon} {term result : KExpr .anon} {e A : AExpr β} @@ -234,7 +299,8 @@ theorem BinderInference.sound {β : Type u} (reading : readScopedExpr? resolve locals term = some e.erase) (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : readScopedExpr? resolve locals result = some A.erase ∧ - CheckingClaim.{u,v} entries context e A := by + CheckingClaim.{u,v} entries context e A ∧ + (SynthesisHead e → TypingClaim.{u,v} entries context e A) := by induction support generalizing result after with | sort miss coherent faithful => obtain ⟨state, run⟩ := infer_uncached_success miss accepted @@ -242,23 +308,50 @@ theorem BinderInference.sound {β : Type u} (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc _))).1 _ = .ok result state at run cases run - refine ⟨?_, (TypingClaim.sort _).checking⟩ + refine ⟨?_, (TypingClaim.sort _).checking, fun head => by cases head⟩ rw [internExpr_readScopedExpr? coherent faithful] simp [AExpr.erase] | fvar cache registered atIndex => obtain ⟨typeReads, typed⟩ := cache.sound agreement registered atIndex accepted - exact ⟨typeReads, typed.checking⟩ + exact ⟨typeReads, typed.checking, fun _ => typed⟩ + | @const locals context fuel before id info ref entry miss resolved found monomorphic stable lookup => + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + have typed : TypingClaim.{u,v} entries context (.const ref []) entry.type := by + simpa only [stable] using + (TypingClaim.const (Γ := context) (ls := []) found (by simpa using monomorphic.symm)) + exact ⟨inferUncached_monomorphic_const_scoped lookup run, typed.checking, fun _ => typed⟩ + | @app locals context fuel before fn arg info f a A A' B condition + full miss trace functionTree head argumentTree conditions hashPath comparisonFaithful + bodyConstructed argConstructed bodyBound argBound coherent faithful ihFunction ihArgument => + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + rw [full] at run + obtain ⟨fnReads, argReads⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨functionTypeReads, _, functionTyped⟩ := ihFunction keyedAgreement fnReads trace.functionRun + obtain ⟨domainReads, codomainReads⟩ := readScopedExpr?_all_parts functionTypeReads + have argumentAgreement := trace.contextPreserved.symm ▸ keyedAgreement + obtain ⟨argumentTypeReads, argumentChecked, _⟩ := ihArgument argumentAgreement argReads trace.argumentRun + have sameReading := beq_readScopedExpr? (resolve := resolve) (locals := locals) + (depth := 0) comparisonFaithful hashPath + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (argumentTypeReads.symm.trans (sameReading.trans domainReads))) conditions + have checked := sameType ▸ argumentChecked + have typed := (functionTyped head).appChecking checked + refine ⟨?_, typed.checking, fun _ => typed⟩ + rw [trace.output run, AExpr.erase_inst] + exact (subst_readScopedExpr? bodyConstructed argConstructed bodyBound argBound + coherent faithful codomainReads argReads).1 | forallE miss trace opening absent domainTree bodyTree levelFaithful domainBound bodyBound coherent faithful ihDomain ihBody => obtain ⟨state, run⟩ := infer_uncached_success miss accepted obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement - obtain ⟨_, domainChecked⟩ := ihDomain keyedAgreement domainReads trace.domainRun + obtain ⟨_, domainChecked, _⟩ := ihDomain keyedAgreement domainReads trace.domainRun have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement obtain ⟨_, openedReads, openedAgreement, _⟩ := openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun - obtain ⟨_, bodyChecked⟩ := ihBody openedAgreement openedReads trace.bodyRun - refine ⟨?_, ?_⟩ + obtain ⟨_, bodyChecked, _⟩ := ihBody openedAgreement openedReads trace.bodyRun + refine ⟨?_, ?_, fun head => by cases head⟩ · rw [trace.output run, internExpr_readScopedExpr? coherent faithful] rfl · have formed := TypingClaim.forallE domainChecked.typingSort bodyChecked.typingSort rfl @@ -275,12 +368,41 @@ theorem BinderInference.sound {β : Type u} have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement obtain ⟨_, openedReads, openedAgreement, _⟩ := openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun - obtain ⟨bodyTypeReads, bodyChecked⟩ := ihBody openedAgreement openedReads trace.bodyRun + obtain ⟨bodyTypeReads, bodyChecked, _⟩ := ihBody openedAgreement openedReads trace.bodyRun obtain ⟨closedReads, closedCoherent⟩ := abstractFVars_readScopedExpr? constructed bound coherent closingFaithful bodyTypeReads - refine ⟨?_, bodyChecked.lam⟩ + refine ⟨?_, bodyChecked.lam, fun head => by cases head⟩ rw [trace.output run, internExpr_readScopedExpr? (table := trace.abstracted.2) closedCoherent faithful] simp [LambdaInferenceTrace.abstracted, domainReads, closedReads, AExpr.erase] +/-- The checking conclusion applies to all supported finite trees. -/ +theorem BinderInference.sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {before after : TcState .anon} {term result : KExpr .anon} {e A : AExpr β} + (support : BinderInference resolve entries locals context fuel before term e A) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals term = some e.erase) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + readScopedExpr? resolve locals result = some A.erase ∧ + CheckingClaim.{u,v} entries context e A := by + obtain ⟨reads, checked, _⟩ := support.soundWithSynthesis agreement reading accepted + exact ⟨reads, checked⟩ + +/-- Constant- and local-headed application spines synthesize full typing, +including hereditary validity of the exact returned dependent type. -/ +theorem BinderInference.synthesis {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {before after : TcState .anon} {term result : KExpr .anon} {e A : AExpr β} + (support : BinderInference resolve entries locals context fuel before term e A) + (head : SynthesisHead e) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals term = some e.erase) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + readScopedExpr? resolve locals result = some A.erase ∧ TypingClaim.{u,v} entries context e A := by + obtain ⟨reads, _, typed⟩ := support.soundWithSynthesis agreement reading accepted + exact ⟨reads, typed head⟩ + end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Production.lean b/Ix/Kernel/Verify/Consistency/Production.lean index f064fabaa..894a75e71 100644 --- a/Ix/Kernel/Verify/Consistency/Production.lean +++ b/Ix/Kernel/Verify/Consistency/Production.lean @@ -102,7 +102,7 @@ def DefinitionInput.constant (input : DefinitionInput) : KConst .anon := or a finite binder inference tree with a separately checked declared type. Specializations supply raw syntax and occurrence annotations; successful inference derives their typing, scope, and references. Binder definitions -also supply syntactic scope and empty constant-reference inventories. Every +also supply syntactic scope and references to the preceding interface. Every case derives body typing without a semantic typing premise. -/ inductive DefinitionBodySupport {β : Type u} (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) @@ -131,7 +131,7 @@ inductive DefinitionBodySupport {β : Type u} (valueReading : readScopedExpr? resolve [] term = some body.erase) (typeReading : readScopedExpr? resolve [] declared = some type.erase) (scope : body.Scope 0 0 ∧ type.Scope 0 0) - (noConstants : body.references = [] ∧ type.references = []) : + (references : body.ReferencesIn entries ∧ type.ReferencesIn entries) : DefinitionBodySupport resolve entries methods before declared term body type theorem DefinitionBodySupport.sound {β : Type u} @@ -174,18 +174,14 @@ theorem DefinitionBodySupport.sound {β : Type u} · intro ref member rw [← equal, references] at member exact wellFormed.typeReferences _ _ support.found ref member - | binder tied valueInference typeInference typeRun valueReading typeReading scope noConstants => + | binder tied valueInference typeInference typeRun valueReading typeReading scope references => subst methods obtain ⟨_, typeChecked⟩ := typeInference.sound (LocalContextReading.empty _ _) typeReading typeRun obtain ⟨_, valueChecked⟩ := valueInference.sound (LocalContextReading.empty _ _) valueReading accepted - refine ⟨readScopedExpr?_closed valueReading, readScopedExpr?_closed typeReading, - scope.1, scope.2, ?_, ?_, valueChecked.typing typeChecked.typingSort⟩ - · intro ref member - simp only [noConstants.1, List.not_mem_nil] at member - · intro ref member - simp only [noConstants.2, List.not_mem_nil] at member + exact ⟨readScopedExpr?_closed valueReading, readScopedExpr?_closed typeReading, + scope.1, scope.2, references.1, references.2, valueChecked.typing typeChecked.typingSort⟩ /-- The execution prefix through value conversion. A successful member check also passes the subsequent safety checks. -/ diff --git a/Ix/Kernel/Verify/Consistency/ScopedExpr.lean b/Ix/Kernel/Verify/Consistency/ScopedExpr.lean index 94caa9729..3b6eb6957 100644 --- a/Ix/Kernel/Verify/Consistency/ScopedExpr.lean +++ b/Ix/Kernel/Verify/Consistency/ScopedExpr.lean @@ -147,6 +147,18 @@ theorem readScopedExpr?_lam_parts {resolve : Address → Option (ConstRef β)} cases reading exact ⟨domainReads, bodyReads⟩ +theorem readScopedExpr?_app_parts {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fn arg : KExpr m} {info : ExprInfo m} + {f a : VExpr β} {depth : Nat} + (reading : readScopedExpr? resolve locals (.app fn arg info) depth = some (.app f a)) : + readScopedExpr? resolve locals fn depth = some f ∧ + readScopedExpr? resolve locals arg depth = some a := by + rw [readScopedExpr?] at reading + obtain ⟨f', fnReads, reading⟩ := option_bind_success reading + obtain ⟨a', argReads, reading⟩ := option_bind_success reading + cases reading + exact ⟨fnReads, argReads⟩ + theorem readScopedExpr?_all_parts {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {name : m.F Name} {bi : m.F Lean.BinderInfo} {domain body : KExpr m} {info : ExprInfo m} {A B : VExpr β} {depth : Nat} diff --git a/Ix/Theory/Model/Checking.lean b/Ix/Theory/Model/Checking.lean index 3e9836ae3..4e69f9f70 100644 --- a/Ix/Theory/Model/Checking.lean +++ b/Ix/Theory/Model/Checking.lean @@ -35,6 +35,18 @@ theorem TypingClaim.checking {e A : AExpr β} (typed : TypingClaim.{u,v} entries have checked := typed V constants realizes levels env valid exact ⟨checked.1, checked.2.2⟩ +/-- A synthesized function supplies the domain's hereditary validity, so its +argument may be checked without a separate universe-formation derivation. -/ +theorem TypingClaim.appChecking {f a A B : AExpr β} {condition : PropWhen} + (function : TypingClaim.{u,v} entries Γ f (.forallE condition A B)) + (argument : CheckingClaim.{u,v} entries Γ a A) : + TypingClaim.{u,v} entries Γ (.app f a) (B.inst a) := by + apply function.app + intro V _ constants realizes levels env valid + have domainValid := (function V constants realizes levels env valid).2.1.1 + obtain ⟨argumentValid, member⟩ := argument V constants realizes levels env valid domainValid + exact ⟨argumentValid, domainValid, member⟩ + namespace CheckingClaim theorem typing {e A : AExpr β} {level : VLevel} diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 780b03081..0074c0ebe 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -6,7 +6,7 @@ public import Tests.Ix.Kernel.IxonFixtures /-! Production regressions for the consistency fragment, polymorphic constant -inference, and dependent binders. These execute the lazy loader, inference, and serial driver +inference, dependent binders, and applications. These execute the lazy loader, inference, and serial driver on content-addressed Ixon declarations. The theorems and their resource premises are checked separately by `IxKernelConsistency`. -/ @@ -303,6 +303,108 @@ private def binderCases : TestSeq := ++ test "binder environment: an escaping bound variable fails validation" (let (env, target) := failedBinder (.var 2); rowFailed env target) -public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases] +private def monomorphicIdentity (level : Ixon.Univ) (env : Ixon.Env := {}) : Ixon.Env × Address := + storeConst env + ⟨.defn ⟨.defn, .safe, 0, + .leanAll (.sort 0) (.leanAll (.var 0) (.var 1)), + .leanLam (.sort 0) (.leanLam (.var 0) (.var 0))⟩, #[], #[], #[level]⟩ + +/-- Real definitions call an earlier identity at Prop and Type. A theorem +then calls the first wrapper, exercising a second semantic dependency. -/ +private def applicationEnvironment : Ixon.Env := Id.run do + let (env, propIdentity) := monomorphicIdentity .zero + let (env, typeIdentity) := monomorphicIdentity (.succ .zero) env + let type := Ixon.Expr.leanAll (.sort 0) (.leanAll (.var 0) (.var 1)) + let value := Ixon.Expr.leanLam (.sort 0) (.leanLam (.var 0) + (.app (.app (.ref 0 #[]) (.var 1)) (.var 0))) + let (env, wrapper) := storeConst env + ⟨.defn ⟨.defn, .safe, 0, type, value⟩, #[], #[propIdentity], #[.zero]⟩ + let (env, _) := storeConst env + ⟨.defn ⟨.thm, .safe, 0, type, value⟩, #[], #[wrapper], #[.zero]⟩ + let (env, _) := storeConst env + ⟨.defn ⟨.opaq, .safe, 0, type, value⟩, #[], #[typeIdentity], #[.succ .zero]⟩ + return env + +/-- `(P Q : Prop) → (P → Q) → P → Q`, with body `f p`. -/ +private def localApplication : Ixon.Env := + let functionType := Ixon.Expr.leanAll (.var 1) (.var 1) + (storeConst {} + ⟨.defn ⟨.defn, .safe, 0, + .leanAll (.sort 0) (.leanAll (.sort 0) + (.leanAll functionType (.leanAll (.var 2) (.var 2)))), + .leanLam (.sort 0) (.leanLam (.sort 0) + (.leanLam functionType (.leanLam (.var 2) (.app (.var 1) (.var 0)))))⟩, + #[], #[], #[.zero]⟩).1 + +/-- `(A : Type) → (B : A → Type) → ((x : A) → B x) → (x : A) → B x`. +Substitution must retain both active locals in the resulting `B x`. -/ +private def dependentApplication : Ixon.Env := + let familyType := Ixon.Expr.leanAll (.var 0) (.sort 0) + let functionType := Ixon.Expr.leanAll (.var 1) (.app (.var 1) (.var 0)) + (storeConst {} + ⟨.defn ⟨.defn, .safe, 0, + .leanAll (.sort 0) (.leanAll familyType + (.leanAll functionType (.leanAll (.var 2) (.app (.var 2) (.var 0))))), + .leanLam (.sort 0) (.leanLam familyType + (.leanLam functionType (.leanLam (.var 2) (.app (.var 1) (.var 0)))))⟩, + #[], #[], #[.succ .zero]⟩).1 + +/-- `(P : Prop) → ((P → P) → P) → P`, with body `f (fun p => p)`. +The function supplies the expected Pi's validity for the lambda argument. -/ +private def lambdaArgument : Ixon.Env := + let functionType := Ixon.Expr.leanAll (.leanAll (.var 0) (.var 1)) (.var 1) + (storeConst {} + ⟨.defn ⟨.defn, .safe, 0, + .leanAll (.sort 0) (.leanAll functionType (.var 1)), + .leanLam (.sort 0) (.leanLam functionType + (.app (.var 0) (.leanLam (.var 1) (.var 0))))⟩, + #[], #[], #[.zero]⟩).1 + +private def failedApplication : Ixon.Env × Address := + let (env, identity) := monomorphicIdentity .zero + storeConst env + ⟨.defn ⟨.defn, .safe, 0, + .leanAll (.sort 0) (.leanAll (.var 0) (.var 1)), + .leanLam (.sort 0) (.leanLam (.var 0) + (.app (.app (.ref 0 #[]) (.var 1)) (.var 1)))⟩, + #[], #[identity], #[.zero]⟩ + +/-- Inspect the exact dependent result before leaving the scope. A repeated +application must preserve it when the production inference cache is warm. -/ +private def applicationLocalResult : Bool := + let (env, identity) := monomorphicIdentity .zero + let propType := KExpr.mkSort (m := .anon) .mkZero + let action : RecM .anon Bool := RecM.withLctxScope do + let (proposition, _) ← TcM.openBinder () () propType (.mkVar 0 ()) + let (witness, _) ← TcM.openBinder () () proposition (.mkVar 0 ()) + let term := KExpr.mkApp (.mkApp (.mkConst ⟨identity, ()⟩ #[]) proposition) witness + let first ← RecM.inferCall term + let second ← RecM.inferCall term + let state ← get + return first.addr == proposition.addr && second.addr == proposition.addr && state.lctx.size == 2 + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed after => passed && after.lctx.size == 0 && after.env.nextFVarId == 2 + | .error _ _ => false + +private def applicationCases : TestSeq := + test "application environment: Prop/Type identity calls and transitive theorem calls check" + (allSucceeded applicationEnvironment 5 { clearEvery := 0 }) + ++ test "application environment: calls check with fresh per-item caches" + (allSucceeded applicationEnvironment 5 { clearEvery := 1 }) + ++ test "application environment: a local function checks its argument against a distinct domain" + (allSucceeded localApplication 1) + ++ test "application environment: a dependent local function returns the substituted family" + (allSucceeded dependentApplication 1) + ++ test "application environment: a local function accepts a checked lambda argument" + (allSucceeded lambdaArgument 1) + ++ test "application inference: exact local result survives cache reuse and scope cleanup" + applicationLocalResult + ++ test "application environment: passing the proposition instead of its witness fails" + (let (env, target) := failedApplication; rowFailed env target) + ++ test "application environment: applying a proof with no function type fails" + (let (env, target) := failedBinder (.app (.var 0) (.var 0)); rowFailed env target) + +public def suite : List TestSeq := + [cases, polymorphicCases, specializationCases, binderCases, applicationCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 92485e5b5..66174de9c 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -59,11 +59,19 @@ increasing sequence of strongly inaccessible cardinals. primitive False when its environment has been admitted by the certified interface and the set-theory assumption has an instance. - `BinderInference.sound` follows finite production inference trees for sorts, - locals, dependent functions, and full-mode lambdas. `LocalContextReading.push` + locals, monomorphic constants, applications, dependent functions, and + full-mode lambdas. `LocalContextReading.push` and `openBinder_sound` connect actual declaration lookup and fresh-variable opening to the model's dependent context. Singleton abstraction closes the resulting function type, and the simplifying `imax` constructor preserves its universe interpretation. +- `BinderInference.synthesis` derives full typing for application spines + headed by a local or an admitted monomorphic constant. The function's type + supplies hereditary validity of its domain, so arguments may be checked + lambdas. Faithful hash conversion identifies the argument's inferred type + with that domain, with matching occurrence annotations. + `subst_readScopedExpr?` connects the actual memoized and interned codomain + substitution to model substitution, including active locals and nested binders. - `FVarInferenceSupport.sound` handles both local-variable cache partitions when a cached answer equals the current declaration's concrete type. These are structural cache checks, with no assumed semantic typing of cached data. @@ -103,6 +111,8 @@ def typeAlias : Type := Prop axiom ident.{u} : (α : Sort u) → α → α def propIdent : (α : Prop) → α → α := ident.{0} def idProp (P : Prop) (p : P) : P := p +def useId (P : Prop) (p : P) : P := idProp P p +def applyProp (P Q : Prop) (f : P → Q) (p : P) : Q := f p ``` `AtomicEnvironmentFragment` records the precise execution boundary: @@ -110,7 +120,7 @@ def idProp (P : Prop) (p : P) : P := p - Every source key occurs in the `buildAnonWork` result, and every work item represents an axiom or a definition. Lookup, routing, and reset witnesses identify the checked `KConst`. -- Sort, constant, forall, and lambda nodes in the inference witnesses miss both cache partitions. +- Sort, constant, application, forall, and lambda nodes in the inference witnesses miss both cache partitions. Local-variable hits must match the current production declaration type. Constant lookup agrees with an already admitted type and universe count. A specialization supplies @@ -122,7 +132,7 @@ def idProp (P : Prop) (p : P) : P := p retains finite interning coherence and address-faithfulness premises. - Binder definitions supply finite inference trees for both the value and its separately checked declared type, exact source readings, closed annotated - syntax, and empty constant-reference lists. Recursive calls use the actual + syntax, and references to the preceding interface. Recursive calls use the actual smaller method table. Binder-opening and abstraction resources cover fresh ids, context preservation during domain inference, intern-table coherence, finite collision freedom, and bounds preventing index overflow. Sort @@ -130,6 +140,14 @@ def idProp (P : Prop) (p : P) : P := p path. `CheckingClaim` derives body validity and membership once declared-type inference establishes hereditary validity. No codomain-typing premise is added to the production proof. +- Applications use full mode, syntactic Pi exposure, an ordinary argument + without an eager-reduction marker, and the hash-equality conversion path. + Their witnesses retain the actual recursive calls, context preservation + during function inference, and finite substitution resources. Function + spines start with locals or monomorphic constants; the latter use exact + lazy-lookup agreement and empty universe substitution. Applying a lambda + directly, polymorphic references within binder trees, and reduction to + expose a Pi require further refinement. - Conversion takes the initial hash-equality path, with faithfulness of the compared expressions. General reduction and conversion caches are outside this fragment. @@ -145,8 +163,8 @@ Callers must establish these operational and representation witnesses for the run. They supply no typing or checker-soundness premise. The proof extracts the validation, type-inference, theorem-guard, value-inference, and conversion steps from public success, then derives body typing to extend -the preceding model. Automatic witness construction, application inference, -lets, more general lambda paths, inductives, coordinated blocks, and other +the preceding model. Automatic witness construction, broader application and +lambda paths, lets, inductives, coordinated blocks, and other conversion paths remain outside the fragment. Polymorphic constant inference is composed into declaration admission and model extension for the monomorphic specializations described above. Definitions with their own universe parameters remain outside this @@ -201,7 +219,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 101 exact theorem boundaries. The production +The consistency target checks 110 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -222,6 +240,10 @@ Environment regressions additionally check Prop/Type specializations, transitive aliases, nested references, simplified declaration types, cache clearing, and admission failures for wrong arities, open parameters, and a mismatched declared specialization. +Function-body regressions cover calls to earlier Prop/Type identities, +transitive theorem calls, local functions with distinct domains and codomains, +dependent result families, lambda arguments, exact local results through cache +reuse and scope cleanup, and rejected argument/function types. ## Certified host adapters @@ -248,7 +270,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Certified checker contracts | [`Ix/Kernel/Certified.lean`](../Ix/Kernel/Certified.lean), [`CertifiedClaims.lean`](../Ix/Kernel/CertifiedClaims.lean) | | Direct production refinement and its audit | [`Ix/Kernel/Verify/Consistency.lean`](../Ix/Kernel/Verify/Consistency.lean) | | Polymorphic inference and universe substitution | [`Consistency/Constant.lean`](../Ix/Kernel/Verify/Consistency/Constant.lean), [`InstUniv.lean`](../Ix/Kernel/Verify/Consistency/InstUniv.lean), [`Model/LevelCongruence.lean`](../Ix/Theory/Model/LevelCongruence.lean) | -| Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | +| Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`Application.lean`](../Ix/Kernel/Verify/Consistency/Application.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | | Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | | Host commands, receipts, and frozen regression evidence | [Certified checking guide](certified-checking.md) | diff --git a/docs/theory.md b/docs/theory.md index a470dd06a..3a9f63013 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -24,7 +24,10 @@ monomorphic aliases, closed universe terms, monomorphic specializations of polymorphic constants, and a fragment of closed dependent function bodies extends every model of its source axioms under explicit execution witnesses. Binder inference uses the declared type's separate formation check to turn -`Model.CheckingClaim` into semantic typing. Full checker consistency +`Model.CheckingClaim` into semantic typing. Application spines headed by locals +or admitted monomorphic constants derive their type's validity from the context +or dependency model, then check arguments and substitute the dependent result. +Full checker consistency and compiler/backend refinement remain open. `Ix.Theory.Named` retains the local name-indexed specification and proof From 4835e6e600f27ecfdadf59243cf1913cd34b83af Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 13:03:16 -0400 Subject: [PATCH 10/63] Refine polymorphic constant calls inside function bodies --- Ix/Kernel/Verify/Consistency.lean | 10 +- Ix/Kernel/Verify/Consistency/Audit.lean | 9 +- .../Verify/Consistency/BinderInference.lean | 12 ++ .../Verify/Consistency/ScopedConstant.lean | 194 ++++++++++++++++++ Ix/Kernel/Verify/Consistency/ScopedExpr.lean | 22 ++ .../Verify/Consistency/ScopedInstUniv.lean | 128 ++++++++++++ Tests/Ix/Kernel/Consistency.lean | 93 ++++++++- docs/kernel-verification.md | 30 ++- docs/theory.md | 2 +- 9 files changed, 486 insertions(+), 14 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/ScopedConstant.lean create mode 100644 Ix/Kernel/Verify/Consistency/ScopedInstUniv.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 414d1a0df..a4e64c077 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -11,6 +11,8 @@ import Ix.Kernel.Verify.Consistency.InstUniv import Ix.Kernel.Verify.Consistency.Constant import Ix.Kernel.Verify.Consistency.Atomic import Ix.Kernel.Verify.Consistency.ScopedExpr +import Ix.Kernel.Verify.Consistency.ScopedInstUniv +import Ix.Kernel.Verify.Consistency.ScopedConstant import Ix.Kernel.Verify.Consistency.Context import Ix.Kernel.Verify.Consistency.BinderOpening import Ix.Kernel.Verify.Consistency.Application @@ -27,7 +29,7 @@ transport keeps its representation, arithmetic, and dependency assumptions explicit. A production `checkEnvAnon` fragment preserves models of its axiom set for monomorphic aliases, closed sorts, monomorphic specializations of polymorphic constants, and closed function bodies built from sorts, locals, -monomorphic references, applications, dependent functions, and full-mode lambdas +polymorphic references, applications, dependent functions, and full-mode lambdas under the stated execution resources. Constant inference supports arbitrary readable entry types, using the actual universe-instantiation walker and explicit lookup and finite-support resources. @@ -37,7 +39,9 @@ preceding interface. Its separate declared-type inference turns semantic checkin into typing. Local cache hits agree with the actual declaration type; other inference nodes retain cache-miss boundaries. Applications use syntactic Pi exposure, full argument checking, hash conversion, -and the ordinary eager-marker path. Constant- and local-headed spines derive -type validity from the admitted model or local context; their arguments may be lambdas. +and arguments without eager-reduction markers. Constant- and local-headed spines +derive type validity from the admitted model or local context; their arguments may be lambdas. +Polymorphic nodes use closed source readings, finite substitution resources, +and a pure prediction of the returned syntax with matching occurrence annotations. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index dc93d5cc0..916e7fd0b 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -48,8 +48,13 @@ private def instantiationRoots : Array Lean.Name := #[ ``instUnivSpec_readExpr?, ``instUnivSpec_readExpr?_withScope, ``instantiateUnivParams_readExpr?, ``instantiateUnivParams_readAnnotated, ``instantiateUnivParams_readAnnotated_scoped, + ``instUnivSpec_scoped_eq, ``instantiateUnivParamsSpec_scoped_eq, + ``instantiateUnivParams_scoped_eq, ``instantiateUnivParams_readScopedAnnotated, ``inferUncached_const_refinement, ``infer_const_refinement, - ``inferUncached_const_sound, ``infer_const_sound + ``inferUncached_const_sound, ``infer_const_sound, + ``inferUncached_const_instantiation, ``ScopedConstantInferenceSupport.closed, + ``inferUncached_const_scoped_refinement, ``inferUncached_const_scoped_sound, + ``inferUncached_const_predicted_type, ``infer_const_scoped_annotated ] private def productionRoots : Array Lean.Name := #[ @@ -63,7 +68,7 @@ private def productionRoots : Array Lean.Name := #[ private def scopedRoots : Array Lean.Name := #[ ``localIndex?_mem, ``localIndex?_getElem, ``localIndex?_fresh, - ``readScopedExpr?_closed, ``readScopedExpr?_eraseMeta, + ``readScopedExpr?_closed, ``readScopedExpr?_weaken_closed, ``readScopedExpr?_eraseMeta, ``beq_readScopedExpr?, ``internExpr_readScopedExpr?, ``readScopedExpr?_push, ``LocalContextReading.empty ] diff --git a/Ix/Kernel/Verify/Consistency/BinderInference.lean b/Ix/Kernel/Verify/Consistency/BinderInference.lean index 64688b4fd..4b47a4a33 100644 --- a/Ix/Kernel/Verify/Consistency/BinderInference.lean +++ b/Ix/Kernel/Verify/Consistency/BinderInference.lean @@ -4,6 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.Application +import Ix.Kernel.Verify.Consistency.ScopedConstant import Ix.Theory.Model.Checking /-! @@ -228,6 +229,14 @@ inductive BinderInference {β : Type u} readScopedExpr? resolve locals concrete.ty = some entry.type.erase) : BinderInference resolve entries locals context fuel before (.const id #[] info) (.const ref []) entry.type + | polymorphic {locals context fuel before id arguments info ref entry type} + (miss : UncachedInference before (.const id arguments info)) + (support : ScopedConstantInferenceSupport resolve entries miss.keyed id arguments ref entry) + (prediction : ∀ concrete loaded, TcM.getConst id miss.keyed = .ok concrete loaded → + readInstantiatedType? resolve concrete.ty arguments = some type.erase) + (conditions : (entry.type.instL (arguments.toList.map readLevel)).annotations = type.annotations) : + BinderInference resolve entries locals context fuel before (.const id arguments info) + (.const ref (arguments.toList.map readLevel)) type | app {locals context fuel before fn arg info f a A A' B condition} (full : before.inferOnly = false) (miss : UncachedInference before (.app fn arg info)) @@ -320,6 +329,9 @@ theorem BinderInference.soundWithSynthesis {β : Type u} simpa only [stable] using (TypingClaim.const (Γ := context) (ls := []) found (by simpa using monomorphic.symm)) exact ⟨inferUncached_monomorphic_const_scoped lookup run, typed.checking, fun _ => typed⟩ + | polymorphic miss support prediction conditions => + obtain ⟨typeReads, typed⟩ := infer_const_scoped_annotated miss support prediction conditions accepted + exact ⟨typeReads, typed.checking, fun _ => typed⟩ | @app locals context fuel before fn arg info f a A A' B condition full miss trace functionTree head argumentTree conditions hashPath comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful ihFunction ihArgument => diff --git a/Ix/Kernel/Verify/Consistency/ScopedConstant.lean b/Ix/Kernel/Verify/Consistency/ScopedConstant.lean new file mode 100644 index 000000000..e67317935 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/ScopedConstant.lean @@ -0,0 +1,194 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Constant +import Ix.Kernel.Verify.Consistency.Context +import Ix.Kernel.Verify.Consistency.ScopedInstUniv + +/-! +# Polymorphic constant inference inside function bodies + +Lookup supplies a closed declaration type and finite resources for its actual +universe-instantiation walker. The resulting type remains closed while the +surrounding function's locals are active. A pure prediction of the substituted +syntax fixes an inference tree's annotated result type without assuming its +semantic validity or typing. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- The runtime arity guard and the precise state passed to universe +instantiation follow from successful production constant inference. -/ +theorem inferUncached_const_instantiation + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} + {methods : Methods .anon} {before after : TcState .anon} {result : KExpr .anon} + (accepted : RecM.inferUncached inferRec inferOnly (.const id arguments info) + methods before = .ok result after) : + ∃ concrete loaded, TcM.getConst id before = .ok concrete loaded ∧ + concrete.lvls.toNat = arguments.size ∧ + TcM.instantiateUnivParams concrete.ty arguments loaded = .ok result after := by + change (RecM.inferUncached inferRec inferOnly (.const id arguments info)).run + methods before = .ok result after at accepted + unfold RecM.inferUncached at accepted + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted + change EStateM.bind (TcM.getConst id) _ before = _ at accepted + cases got : TcM.getConst id before with + | error err failed => rw [EStateM.bind, got] at accepted; contradiction + | ok concrete loaded => + rw [EStateM.bind, got] at accepted + by_cases arity : concrete.lvls.toNat = arguments.size + · simp only [arity, bne_self_eq_false, Bool.false_eq_true, if_false] at accepted + exact ⟨concrete, loaded, rfl, arity, accepted⟩ + · simp only [bne_iff_ne] at accepted + rw [if_pos arity] at accepted + contradiction + +/-- The selected admitted type is scoped at its declared universe arity. +Lookup uses the closed binder reader, retaining its exclusion of lets and +loose variables. All mutable resources concern the actual post-lookup state. -/ +structure ScopedConstantInferenceSupport {β : Type u} + (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) + (before : TcState .anon) (id : KId .anon) (arguments : Array (KUniv .anon)) + (ref : ConstRef β) (entry : ConstantEntry β) : Prop where + resolved : resolve id.addr = some ref + found : entries ref = some entry + scope : entry.type.Scope entry.universes 0 + lookup : ∀ concrete loaded, TcM.getConst id before = .ok concrete loaded → + concrete.lvls.toNat = entry.universes ∧ + readScopedExpr? resolve [] concrete.ty = some entry.type.erase ∧ + UniverseInstantiationSupport loaded concrete.ty arguments + +/-- The stronger closed binder reading also supplies the earlier constant +refinement's lookup interface. -/ +theorem ScopedConstantInferenceSupport.closed {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {before : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} + {ref : ConstRef β} {entry : ConstantEntry β} + (support : ScopedConstantInferenceSupport resolve entries before id arguments ref entry) : + ConstantInferenceSupport resolve entries before id arguments ref entry := by + refine ⟨support.resolved, support.found, ?_⟩ + intro concrete loaded got + obtain ⟨count, reading, resources⟩ := support.lookup concrete loaded got + exact ⟨count, readScopedExpr?_closed reading, resources⟩ + +/-- Refinement derives the exact returned type's scoped reading, arity, +universe congruence, reference inventory, and scope for scoped arguments. -/ +theorem inferUncached_const_scoped_refinement {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} + {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} + {methods : Methods .anon} {before after : TcState .anon} {result : KExpr .anon} + (support : ScopedConstantInferenceSupport resolve entries before id arguments ref entry) + (accepted : RecM.inferUncached inferRec inferOnly (.const id arguments info) + methods before = .ok result after) : + ∃ output : AExpr β, readScopedExpr? resolve locals result = some output.erase ∧ + AExpr.LevelEquivalent (entry.type.instL (arguments.toList.map readLevel)) output ∧ + (arguments.toList.map readLevel).length = entry.universes ∧ + output.references = entry.type.references ∧ + (∀ n, (∀ level ∈ arguments, (readLevel level).WF n) → output.Scope n 0) := by + obtain ⟨concrete, loaded, got, arity, instantiated⟩ := inferUncached_const_instantiation accepted + obtain ⟨count, reading, resources⟩ := support.lookup concrete loaded got + have scope : entry.type.Scope arguments.size 0 := by + rw [← arity, count] + exact support.scope + obtain ⟨output, outputReads, same⟩ := instantiateUnivParams_readScopedAnnotated + (locals := locals) resources scope.erase.1 reading instantiated + refine ⟨output, outputReads, same, ?_, + same.references.symm.trans (AExpr.references_instL entry.type _), ?_⟩ + · simpa only [List.length_map, Array.length_toList] using arity.symm.trans count + · intro n argumentsWF + obtain ⟨scopedOutput, scopedReads, scopedSame, scopedWF, _⟩ := + instantiateUnivParams_readAnnotated_scoped resources scope argumentsWF + (readScopedExpr?_closed reading) instantiated + have closedOutputReads := + (instantiateUnivParams_scoped_eq locals resources reading instantiated).symm.trans outputReads + have sameOutput := AExpr.eq_of_erase_annotations + (Option.some.inj (scopedReads.symm.trans closedOutputReads)) + (scopedSame.annotations.symm.trans same.annotations) + exact sameOutput ▸ scopedWF + +/-- Polymorphic references synthesize full typing in every active model +context. The dependency model supplies the instantiated type's validity. -/ +theorem inferUncached_const_scoped_sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {ref : ConstRef β} {entry : ConstantEntry β} + {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} + {methods : Methods .anon} {before after : TcState .anon} {result : KExpr .anon} + (support : ScopedConstantInferenceSupport resolve entries before id arguments ref entry) + (accepted : RecM.inferUncached inferRec inferOnly (.const id arguments info) + methods before = .ok result after) : + ScopedModelTyping.{u,v} resolve entries locals context (.const id arguments info) result := by + obtain ⟨output, outputReads, same, arity, _, _⟩ := + inferUncached_const_scoped_refinement support accepted + refine ⟨.const ref (arguments.toList.map readLevel), output, ?_, outputReads, + same.typing (TypingClaim.const support.found arity)⟩ + simp [readScopedExpr?, support.resolved, AExpr.erase] + +/-- Read a pure prediction of the instantiated declaration type. This is a +computable syntax check, independent of interning and semantic typing. -/ +def readInstantiatedType? {β : Type u} (resolve : Address → Option (ConstRef β)) + (type : KExpr .anon) (arguments : Array (KUniv .anon)) : Option (VExpr β) := + match KExpr.instantiateUnivParamsSpec type arguments with + | .ok result => readScopedExpr? resolve [] result + | .error _ => none + +/-- The actual interned result agrees with the pure prediction under the +walker's finite resource assumptions. Its reading is closed under locals. -/ +theorem inferUncached_const_predicted_type {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} + {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {inferOnly : Bool} + {methods : Methods .anon} {before after : TcState .anon} {result : KExpr .anon} + (support : ScopedConstantInferenceSupport resolve entries before id arguments ref entry) + (prediction : ∀ concrete loaded, TcM.getConst id before = .ok concrete loaded → + readInstantiatedType? resolve concrete.ty arguments = some type.erase) + (accepted : RecM.inferUncached inferRec inferOnly (.const id arguments info) + methods before = .ok result after) : + readScopedExpr? resolve locals result = some type.erase := by + obtain ⟨concrete, loaded, got, _, instantiated⟩ := inferUncached_const_instantiation accepted + obtain ⟨_, _, resources⟩ := support.lookup concrete loaded got + have post := TcM.instantiateUnivParams_wf resources.faithful + (fun _ h => Or.inr h) ⟨resources.coherent, fun _ h => Or.inl h⟩ + rw [instantiated] at post + have predicted := prediction concrete loaded got + rw [readInstantiatedType?, post.2.1] at predicted + exact readScopedExpr?_weaken_closed predicted locals + +/-- Fix an inference tree's result annotation from the pure substituted tree. +The semantic conclusion follows from the admitted entry and actual execution, +including any universe simplification; it is not a witness premise. -/ +theorem infer_const_scoped_annotated {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} + {methods : Methods .anon} {before after : TcState .anon} {result : KExpr .anon} + (miss : UncachedInference before (.const id arguments info)) + (support : ScopedConstantInferenceSupport resolve entries miss.keyed id arguments ref entry) + (prediction : ∀ concrete loaded, TcM.getConst id miss.keyed = .ok concrete loaded → + readInstantiatedType? resolve concrete.ty arguments = some type.erase) + (conditions : (entry.type.instL (arguments.toList.map readLevel)).annotations = type.annotations) + (accepted : RecM.infer (.const id arguments info) methods before = .ok result after) : + readScopedExpr? resolve locals result = some type.erase ∧ + TypingClaim.{u,v} entries context (.const ref (arguments.toList.map readLevel)) type := by + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + have predicted := inferUncached_const_predicted_type (locals := locals) support prediction run + obtain ⟨output, outputReads, same, arity, _, _⟩ := + inferUncached_const_scoped_refinement (locals := locals) support run + have sameOutput := AExpr.eq_of_erase_annotations + (Option.some.inj (outputReads.symm.trans predicted)) + (same.annotations.symm.trans conditions) + exact ⟨predicted, sameOutput ▸ same.typing (TypingClaim.const support.found arity)⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/ScopedExpr.lean b/Ix/Kernel/Verify/Consistency/ScopedExpr.lean index 3b6eb6957..82dd5ebff 100644 --- a/Ix/Kernel/Verify/Consistency/ScopedExpr.lean +++ b/Ix/Kernel/Verify/Consistency/ScopedExpr.lean @@ -172,6 +172,28 @@ theorem readScopedExpr?_all_parts {resolve : Address → Option (ConstRef β)} cases reading exact ⟨domainReads, bodyReads⟩ +/-- A term readable without registered locals keeps that reading in any +local context. Its syntactic binders retain the same indices. -/ +theorem readScopedExpr?_weaken_closed {resolve : Address → Option (ConstRef β)} + {term : KExpr m} {source : VExpr β} {depth : Nat} + (reading : readScopedExpr? resolve [] term depth = some source) (locals : List FVarId) : + readScopedExpr? resolve locals term depth = some source := by + induction term generalizing source depth with + | var _ _ _ | sort _ _ | const _ _ _ | nat _ _ _ => exact reading + | fvar _ _ _ | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | app fn arg info hf ha | lam _ _ fn arg info hf ha | all _ _ fn arg info hf ha => + rw [readScopedExpr?] at reading + obtain ⟨f, fReads, reading⟩ := option_bind_success reading + obtain ⟨a, aReads, reading⟩ := option_bind_success reading + cases reading + simp [readScopedExpr?, hf fReads, ha aReads] + | prj id index value info ih => + rw [readScopedExpr?] at reading + obtain ⟨ref, resolved, reading⟩ := option_bind_success reading + obtain ⟨value, valueReads, reading⟩ := option_bind_success reading + cases reading + simp [readScopedExpr?, resolved, ih valueReads] + /-- Closing the local context recovers the original reader exactly. -/ theorem readScopedExpr?_closed {resolve : Address → Option (ConstRef β)} {term : KExpr m} {source : VExpr β} {depth : Nat} diff --git a/Ix/Kernel/Verify/Consistency/ScopedInstUniv.lean b/Ix/Kernel/Verify/Consistency/ScopedInstUniv.lean new file mode 100644 index 000000000..427e7b588 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/ScopedInstUniv.lean @@ -0,0 +1,128 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.ScopedExpr +import Ix.Kernel.Verify.Consistency.InstUniv + +/-! +# Universe instantiation of closed types in an active local context + +The universe walker preserves the term binders of a closed declaration type. +Its returned type therefore has the same scoped reading in every active local +context. Simplifying levels retain the existing exact annotation and semantic +congruence guarantees. No runtime result reading is assumed. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u +variable {β : Type u} + +private theorem option_bind_success {α γ : Type _} {action : Option α} + {next : α → Option γ} {result : γ} (run : action.bind next = some result) : + ∃ intermediate, action = some intermediate ∧ next intermediate = some result := by + cases action with + | none => contradiction + | some value => exact ⟨value, rfl, run⟩ + +private theorem except_bind_success {ε α γ : Type _} {action : Except ε α} + {next : α → Except ε γ} {result : γ} (run : action.bind next = .ok result) : + ∃ intermediate, action = .ok intermediate ∧ next intermediate = .ok result := by + cases action with + | error err => contradiction + | ok value => exact ⟨value, rfl, run⟩ + +/-- Pure universe substitution preserves exactly the term-binding structure +needed to read a closed declaration type in an arbitrary local context. -/ +theorem instUnivSpec_scoped_eq {resolve : Address → Option (ConstRef β)} + {arguments : Array (KUniv .anon)} {term result : KExpr .anon} + {source : VExpr β} {depth : Nat} (locals : List FVarId) + (reading : readScopedExpr? resolve [] term depth = some source) + (run : KExpr.instUnivSpec term arguments = .ok result) : + readScopedExpr? resolve locals result depth = readExpr? resolve result := by + induction term generalizing source result depth with + | var index name info => + cases run + simp only [readScopedExpr?] at reading ⊢ + split at reading + next bound => simp [bound, readExpr?] + · contradiction + | fvar _ _ _ | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | nat _ _ _ => cases run; rfl + | sort _ _ | const _ _ _ => + rw [KExpr.instUnivSpec] at run + obtain ⟨_, _, run⟩ := except_bind_success run + cases run + rfl + | app fn arg info hf ha | lam _ _ fn arg info hf ha | all _ _ fn arg info hf ha => + rw [readScopedExpr?] at reading + obtain ⟨f, fReads, reading⟩ := option_bind_success reading + obtain ⟨a, aReads, reading⟩ := option_bind_success reading + rw [KExpr.instUnivSpec] at run + obtain ⟨fn', fRun, run⟩ := except_bind_success run + obtain ⟨arg', aRun, run⟩ := except_bind_success run + cases run + simp only [readScopedExpr?_mkApp, readScopedExpr?_mkLam, readScopedExpr?_mkAll] + rw [hf fReads fRun, ha aReads aRun] + rfl + | prj id index value info ih => + rw [readScopedExpr?] at reading + obtain ⟨ref, _, reading⟩ := option_bind_success reading + obtain ⟨value, valueReads, _⟩ := option_bind_success reading + rw [KExpr.instUnivSpec] at run + obtain ⟨value', valueRun, run⟩ := except_bind_success run + cases run + simp only [readScopedExpr?_mkPrj] + rw [ih valueReads valueRun] + rfl + +/-- Include the production empty-argument shortcut in the structural result. -/ +theorem instantiateUnivParamsSpec_scoped_eq {resolve : Address → Option (ConstRef β)} + {arguments : Array (KUniv .anon)} {term result : KExpr .anon} + {source : VExpr β} {depth : Nat} (locals : List FVarId) + (reading : readScopedExpr? resolve [] term depth = some source) + (run : KExpr.instantiateUnivParamsSpec term arguments = .ok result) : + readScopedExpr? resolve locals result depth = readExpr? resolve result := by + unfold KExpr.instantiateUnivParamsSpec at run + split at run + · cases run + exact (readScopedExpr?_weaken_closed reading locals).trans + (readScopedExpr?_closed reading).symm + · exact instUnivSpec_scoped_eq locals reading run + +/-- The real memoized and interned execution preserves closed term scope, +including when its universe constructors simplify the returned syntax. -/ +theorem instantiateUnivParams_scoped_eq {resolve : Address → Option (ConstRef β)} + {arguments : Array (KUniv .anon)} {term result : KExpr .anon} + {source : VExpr β} {depth : Nat} {before after : TcState .anon} + (locals : List FVarId) (support : UniverseInstantiationSupport before term arguments) + (reading : readScopedExpr? resolve [] term depth = some source) + (run : TcM.instantiateUnivParams term arguments before = .ok result after) : + readScopedExpr? resolve locals result depth = readExpr? resolve result := by + have post := TcM.instantiateUnivParams_wf support.faithful + (fun _ h => Or.inr h) ⟨support.coherent, fun _ h => Or.inl h⟩ + rw [run] at post + exact instantiateUnivParamsSpec_scoped_eq locals reading post.2.1 + +/-- Read the actual returned annotated type in an active local context. +Universe scope justifies the empty shortcut; all annotation occurrences and +hereditary validity are preserved by the level congruence. -/ +theorem instantiateUnivParams_readScopedAnnotated + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {arguments : Array (KUniv .anon)} {term result : KExpr .anon} {source : AExpr β} + {before after : TcState .anon} + (support : UniverseInstantiationSupport before term arguments) + (scope : source.erase.LevelWF arguments.size) + (reading : readScopedExpr? resolve [] term = some source.erase) + (run : TcM.instantiateUnivParams term arguments before = .ok result after) : + ∃ output : AExpr β, readScopedExpr? resolve locals result = some output.erase ∧ + AExpr.LevelEquivalent (source.instL (arguments.toList.map readLevel)) output := by + obtain ⟨output, outputReads, same⟩ := instantiateUnivParams_readAnnotated support scope + (readScopedExpr?_closed reading) run + exact ⟨output, (instantiateUnivParams_scoped_eq locals support reading run).trans outputReads, same⟩ + +end Ix.Kernel.Consistency diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 0074c0ebe..0e9f7e93a 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -404,7 +404,98 @@ private def applicationCases : TestSeq := ++ test "application environment: applying a proof with no function type fails" (let (env, target) := failedBinder (.app (.var 0) (.var 0)); rowFailed env target) +/-- Call a polymorphic identity from a monomorphic function body. Universe +indices select entries in the declaration's explicit level table. -/ +private def storePolymorphicCall (env : Ixon.Env) (identity : Address) + (levels : Array Ixon.Univ) (domain : UInt64) (arguments : Array UInt64) + (kind : Ix.DefKind := .defn) : Ixon.Env × Address := + storeConst env + ⟨.defn ⟨kind, .safe, 0, + .leanAll (.sort domain) (.leanAll (.var 0) (.var 1)), + .leanLam (.sort domain) (.leanLam (.var 0) + (.app (.app (.ref 0 arguments) (.var 1)) (.var 0)))⟩, + #[], #[identity], levels⟩ + +private def polymorphicApplicationEnvironment : Ixon.Env := Id.run do + let (env, identity) := polymorphicIdentity + let (env, propCall) := storePolymorphicCall env identity #[.zero] 0 #[0] + let (env, _) := storePolymorphicCall env identity #[.succ .zero] 0 #[0] .opaq + let (env, _) := storePolymorphicCall env propCall #[.zero] 0 #[] .thm + return env + +/-- The source type retains a compound level until its two parameters are +substituted. This exercises simplification inside the returned Pi domain. -/ +private def computedIdentity (level : Ixon.Univ) : Ixon.Env × Address := + storeConst {} + ⟨.axio ⟨false, 2, .leanAll (.sort 0) (.leanAll (.var 0) (.var 1))⟩, + #[], #[], #[level]⟩ + +private def simplifiedPolymorphicCall (imax : Bool) : Ixon.Env := + let (env, identity) := computedIdentity + (if imax then .imax (.var 0) (.var 1) else .max (.var 0) (.var 1)) + let levels := if imax then #[.succ (.succ .zero), .zero] else #[.zero, .succ .zero] + (storePolymorphicCall env identity levels 1 #[0, 1]).1 + +/-- A declaration type containing instantiated references must stay closed +when inferred beneath an unrelated active local. -/ +private def scopedPolymorphicReferences : Bool := + let (env, function, carrier) := polymorphicReferences + let propType := KExpr.mkSort (m := .anon) .mkZero + let carrierType := KExpr.mkConst (m := .anon) ⟨carrier, ()⟩ #[levelOne] + let expected := KExpr.mkAll () () carrierType carrierType + let action : RecM .anon Bool := RecM.withLctxScope do + let _ ← TcM.openBinder () () propType (.mkVar 0 ()) + let inferred ← RecM.inferCall (.mkConst ⟨function, ()⟩ #[levelOne]) + return inferred.addr == expected.addr && inferred.lbr == 0 && (← get).lctx.size == 1 + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed after => passed && after.lctx.size == 0 && after.env.nextFVarId == 1 + | .error _ _ => false + +/-- Different level instances use different inference keys, while neither +returned type can capture the active local. -/ +private def scopedPolymorphicInstances : Bool := + let propType := KExpr.mkSort (m := .anon) .mkZero + let propIdentity := KExpr.mkAll () () propType (.mkAll () () (.mkVar 0 ()) (.mkVar 1 ())) + let action : RecM .anon Bool := RecM.withLctxScope do + let _ ← TcM.openBinder () () propType (.mkVar 0 ()) + let first ← RecM.inferCall (.mkConst ⟨polymorphicIdentity.2, ()⟩ #[.mkZero]) + let second ← RecM.inferCall (.mkConst ⟨polymorphicIdentity.2, ()⟩ #[levelOne]) + let state ← get + return first.addr == propIdentity.addr && second.addr == identityType.addr && + first.addr != second.addr && first.lbr == 0 && second.lbr == 0 && + state.env.inferCache.size == 2 && state.lctx.size == 1 + match TcM.runRec action (TcState.newLazyAnon polymorphicIdentity.1) with + | .ok passed after => passed && after.lctx.size == 0 && after.env.nextFVarId == 1 + | .error _ _ => false + +private def polymorphicApplicationCases : TestSeq := + test "polymorphic calls: Prop/Type instances and a transitive theorem check" + (allSucceeded polymorphicApplicationEnvironment 4 { clearEvery := 0 }) + ++ test "polymorphic calls: function bodies check with fresh per-item caches" + (allSucceeded polymorphicApplicationEnvironment 4 { clearEvery := 1 }) + ++ test "polymorphic calls: max simplification exposes the Type domain" + (allSucceeded (simplifiedPolymorphicCall false) 2) + ++ test "polymorphic calls: imax simplification exposes the Prop domain" + (allSucceeded (simplifiedPolymorphicCall true) 2) + ++ test "polymorphic inference: nested reference arguments stay closed under an active local" + scopedPolymorphicReferences + ++ test "polymorphic inference: distinct instances retain closed types and separate cache keys" + scopedPolymorphicInstances + ++ test "polymorphic calls: a Type argument cannot use the Prop instance" + (let (env, identity) := polymorphicIdentity + let (env, target) := storePolymorphicCall env identity #[.zero, .succ .zero] 1 #[0] + rowFailed env target) + ++ test "polymorphic calls: missing universe arguments reject inside a function body" + (let (env, identity) := polymorphicIdentity + let (env, target) := storePolymorphicCall env identity #[.zero] 0 #[] + rowFailed env target) + ++ test "polymorphic calls: excess universe arguments reject inside a function body" + (let (env, identity) := polymorphicIdentity + let (env, target) := storePolymorphicCall env identity #[.zero] 0 #[0, 0] + rowFailed env target) + public def suite : List TestSeq := - [cases, polymorphicCases, specializationCases, binderCases, applicationCases] + [cases, polymorphicCases, specializationCases, binderCases, applicationCases, + polymorphicApplicationCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 66174de9c..029f6ed39 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -59,19 +59,26 @@ increasing sequence of strongly inaccessible cardinals. primitive False when its environment has been admitted by the certified interface and the set-theory assumption has an instance. - `BinderInference.sound` follows finite production inference trees for sorts, - locals, monomorphic constants, applications, dependent functions, and + locals, polymorphic constants, applications, dependent functions, and full-mode lambdas. `LocalContextReading.push` and `openBinder_sound` connect actual declaration lookup and fresh-variable opening to the model's dependent context. Singleton abstraction closes the resulting function type, and the simplifying `imax` constructor preserves its universe interpretation. - `BinderInference.synthesis` derives full typing for application spines - headed by a local or an admitted monomorphic constant. The function's type + headed by a local or an admitted constant. The function's type supplies hereditary validity of its domain, so arguments may be checked lambdas. Faithful hash conversion identifies the argument's inferred type with that domain, with matching occurrence annotations. `subst_readScopedExpr?` connects the actual memoized and interned codomain substitution to model substitution, including active locals and nested binders. +- `instantiateUnivParams_readScopedAnnotated` brings the actual substituted + declaration type into any active local context, preserving its closed term + scope and occurrence annotations. `infer_const_scoped_annotated` uses this + result for polymorphic references in binder trees. A pure substitution and + syntax-reading check fixes the tree's result annotation; the admitted model + and actual execution establish typing. Runtime success supplies arity, while + scoped arguments also give scope of the returned type. - `FVarInferenceSupport.sound` handles both local-variable cache partitions when a cached answer equals the current declaration's concrete type. These are structural cache checks, with no assumed semantic typing of cached data. @@ -113,6 +120,7 @@ def propIdent : (α : Prop) → α → α := ident.{0} def idProp (P : Prop) (p : P) : P := p def useId (P : Prop) (p : P) : P := idProp P p def applyProp (P Q : Prop) (f : P → Q) (p : P) : Q := f p +def usePoly (P : Prop) (p : P) : P := ident.{0} P p ``` `AtomicEnvironmentFragment` records the precise execution boundary: @@ -144,10 +152,13 @@ def applyProp (P Q : Prop) (f : P → Q) (p : P) : Q := f p without an eager-reduction marker, and the hash-equality conversion path. Their witnesses retain the actual recursive calls, context preservation during function inference, and finite substitution resources. Function - spines start with locals or monomorphic constants; the latter use exact - lazy-lookup agreement and empty universe substitution. Applying a lambda - directly, polymorphic references within binder trees, and reduction to - expose a Pi require further refinement. + spines start with locals or admitted constants. Polymorphic constants supply + exact lazy-lookup agreement, a closed scoped reading of the selected entry's + type, and finite universe-substitution resources. Their result type is fixed + by a pure `readInstantiatedType?` check with the substituted occurrence + annotations, including when levels simplify. Monomorphic references retain + their simpler empty-substitution rule. Applying a lambda directly and + reduction to expose a Pi require further refinement. - Conversion takes the initial hash-equality path, with faithfulness of the compared expressions. General reduction and conversion caches are outside this fragment. @@ -219,7 +230,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 110 exact theorem boundaries. The production +The consistency target checks 121 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -244,6 +255,10 @@ Function-body regressions cover calls to earlier Prop/Type identities, transitive theorem calls, local functions with distinct domains and codomains, dependent result families, lambda arguments, exact local results through cache reuse and scope cleanup, and rejected argument/function types. +Polymorphic-call regressions include Prop/Type instances in real function +bodies, `max`/`imax` simplification inside Pi domains, closed nested references +under active locals, separate cache keys for different universe instances, +and rejected universe arities and argument types. ## Certified host adapters @@ -270,6 +285,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Certified checker contracts | [`Ix/Kernel/Certified.lean`](../Ix/Kernel/Certified.lean), [`CertifiedClaims.lean`](../Ix/Kernel/CertifiedClaims.lean) | | Direct production refinement and its audit | [`Ix/Kernel/Verify/Consistency.lean`](../Ix/Kernel/Verify/Consistency.lean) | | Polymorphic inference and universe substitution | [`Consistency/Constant.lean`](../Ix/Kernel/Verify/Consistency/Constant.lean), [`InstUniv.lean`](../Ix/Kernel/Verify/Consistency/InstUniv.lean), [`Model/LevelCongruence.lean`](../Ix/Theory/Model/LevelCongruence.lean) | +| Polymorphic calls inside binders | [`Consistency/ScopedConstant.lean`](../Ix/Kernel/Verify/Consistency/ScopedConstant.lean), [`ScopedInstUniv.lean`](../Ix/Kernel/Verify/Consistency/ScopedInstUniv.lean) | | Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`Application.lean`](../Ix/Kernel/Verify/Consistency/Application.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | | Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | diff --git a/docs/theory.md b/docs/theory.md index 3a9f63013..da733f35e 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -25,7 +25,7 @@ polymorphic constants, and a fragment of closed dependent function bodies extends every model of its source axioms under explicit execution witnesses. Binder inference uses the declared type's separate formation check to turn `Model.CheckingClaim` into semantic typing. Application spines headed by locals -or admitted monomorphic constants derive their type's validity from the context +or admitted polymorphic constants derive their type's validity from the context or dependency model, then check arguments and substitute the dependent result. Full checker consistency and compiler/backend refinement remain open. From caa982650646d008b946587008081006a838a855 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 13:39:28 -0400 Subject: [PATCH 11/63] Refine constant cache hits in function inference --- Ix/Kernel/Verify/Consistency.lean | 9 +- Ix/Kernel/Verify/Consistency/Atomic.lean | 29 +++- Ix/Kernel/Verify/Consistency/Audit.lean | 8 +- .../Verify/Consistency/BinderInference.lean | 11 +- .../Verify/Consistency/ConstantCache.lean | 150 ++++++++++++++++++ .../Verify/Consistency/ScopedInstUniv.lean | 30 ++++ Tests/Ix/Kernel/Consistency.lean | 111 ++++++++++++- docs/kernel-verification.md | 34 +++- docs/theory.md | 3 + 9 files changed, 366 insertions(+), 19 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/ConstantCache.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index a4e64c077..74022d49c 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -13,6 +13,7 @@ import Ix.Kernel.Verify.Consistency.Atomic import Ix.Kernel.Verify.Consistency.ScopedExpr import Ix.Kernel.Verify.Consistency.ScopedInstUniv import Ix.Kernel.Verify.Consistency.ScopedConstant +import Ix.Kernel.Verify.Consistency.ConstantCache import Ix.Kernel.Verify.Consistency.Context import Ix.Kernel.Verify.Consistency.BinderOpening import Ix.Kernel.Verify.Consistency.Application @@ -36,12 +37,16 @@ universe-instantiation walker and explicit lookup and finite-support resources. The returned type's scope and references justify declaration admission. The binder case also requires explicit syntactic scope and references to the preceding interface. Its separate declared-type inference turns semantic checking -into typing. Local cache hits agree with the actual declaration type; other -inference nodes retain cache-miss boundaries. +into typing. Local cache hits agree with the actual declaration type. Constant +hits agree with pure universe substitution of a loaded, admitted declaration; +the other inference nodes retain cache-miss boundaries. Applications use syntactic Pi exposure, full argument checking, hash conversion, and arguments without eager-reduction markers. Constant- and local-headed spines derive type validity from the admitted model or local context; their arguments may be lambdas. Polymorphic nodes use closed source readings, finite substitution resources, and a pure prediction of the returned syntax with matching occurrence annotations. +Cache hits additionally check arity because they skip the runtime guard. Exact +cache selection and successful constant writes are proved; general preservation +of cache agreement across checker operations remains an explicit obligation. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Atomic.lean b/Ix/Kernel/Verify/Consistency/Atomic.lean index d6317d312..e17b55c20 100644 --- a/Ix/Kernel/Verify/Consistency/Atomic.lean +++ b/Ix/Kernel/Verify/Consistency/Atomic.lean @@ -29,14 +29,20 @@ structure UncachedInference (before : TcState .anon) (term : KExpr .anon) where fullMiss : keyed.env.inferCache[key]? = none onlyMiss : keyed.env.inferOnlyCache[key]? = none -/-- Strip only key lookup and the final cache write from an actual successful -inference. The recursive method table and returned type are unchanged. -/ -theorem infer_uncached_success {term type : KExpr .anon} +/-- Recover the uncached execution and the exact final cache write from a +successful miss. The policy is read before production computes the key. -/ +theorem infer_uncached_success_state {term type : KExpr .anon} {methods : Methods .anon} {before after : TcState .anon} (miss : UncachedInference before term) (accepted : RecM.infer term methods before = .ok type after) : ∃ inferredState, RecM.inferUncached RecM.inferCall before.inferOnly term - methods miss.keyed = .ok type inferredState := by + methods miss.keyed = .ok type inferredState ∧ + after = if before.inferOnly then + { inferredState with env := { inferredState.env with + inferOnlyCache := inferredState.env.inferOnlyCache.insert miss.key type } } + else + { inferredState with env := { inferredState.env with + inferCache := inferredState.env.inferCache.insert miss.key type } } := by change (RecM.infer term).run methods before = .ok type after at accepted unfold RecM.infer RecM.inferWith at accepted simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted @@ -59,7 +65,7 @@ theorem infer_uncached_success {term type : KExpr .anon} change EStateM.Result.ok ty { state with env := { state.env with inferCache := state.env.inferCache.insert miss.key ty } } = .ok type after at accepted cases accepted - exact ⟨state, rfl⟩ + exact ⟨state, rfl, rfl⟩ | true => simp only [policy, if_true] at accepted simp only [ReaderT.run_bind] at accepted @@ -77,7 +83,18 @@ theorem infer_uncached_success {term type : KExpr .anon} change EStateM.Result.ok ty { state with env := { state.env with inferOnlyCache := state.env.inferOnlyCache.insert miss.key ty } } = .ok type after at accepted cases accepted - exact ⟨state, rfl⟩ + exact ⟨state, rfl, rfl⟩ + +/-- Strip only key lookup and the final cache write from an actual successful +inference. The recursive method table and returned type are unchanged. -/ +theorem infer_uncached_success {term type : KExpr .anon} + {methods : Methods .anon} {before after : TcState .anon} + (miss : UncachedInference before term) + (accepted : RecM.infer term methods before = .ok type after) : + ∃ inferredState, RecM.inferUncached RecM.inferCall before.inferOnly term + methods miss.keyed = .ok type inferredState := by + obtain ⟨state, run, _⟩ := infer_uncached_success_state miss accepted + exact ⟨state, run⟩ /-- Supported syntax plus the concrete resources needed by its uncached production branch. A constant's semantic type comes from an existing entry, diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 916e7fd0b..e0f399783 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -33,11 +33,12 @@ private def productionNative : Array Lean.Name := #[ ] private def atomicRoots : Array Lean.Name := #[ - ``infer_uncached_success, ``AtomicInferenceSupport.typing, + ``infer_uncached_success_state, ``infer_uncached_success, ``AtomicInferenceSupport.typing, ``AtomicInferenceSupport.reads, ``AtomicInferenceSupport.output, ``AtomicInferenceSupport.scopeAndReferences, ``AtomicInference.sound, ``inferUncached_fvar_sound, ``infer_fvar_sound, ``FVarInferenceSupport.output, ``FVarInferenceSupport.sound, + ``InferenceCacheHit.run, ``ForallInferenceTrace.output, ``LambdaInferenceTrace.output, ``BinderInference.sound, ``inferUncached_monomorphic_const_scoped, ``ApplicationInferenceTrace.output, ``BinderInference.soundWithSynthesis, ``BinderInference.synthesis, @@ -50,11 +51,14 @@ private def instantiationRoots : Array Lean.Name := #[ ``instantiateUnivParams_readAnnotated_scoped, ``instUnivSpec_scoped_eq, ``instantiateUnivParamsSpec_scoped_eq, ``instantiateUnivParams_scoped_eq, ``instantiateUnivParams_readScopedAnnotated, + ``instantiateUnivParamsSpec_readScopedAnnotated, ``inferUncached_const_refinement, ``infer_const_refinement, ``inferUncached_const_sound, ``infer_const_sound, ``inferUncached_const_instantiation, ``ScopedConstantInferenceSupport.closed, ``inferUncached_const_scoped_refinement, ``inferUncached_const_scoped_sound, - ``inferUncached_const_predicted_type, ``infer_const_scoped_annotated + ``inferUncached_const_predicted_type, ``infer_const_scoped_annotated, + ``infer_const_cache_write, ``CachedConstantInferenceSupport.refinement, + ``CachedConstantInferenceSupport.sound ] private def productionRoots : Array Lean.Name := #[ diff --git a/Ix/Kernel/Verify/Consistency/BinderInference.lean b/Ix/Kernel/Verify/Consistency/BinderInference.lean index 4b47a4a33..c0950986e 100644 --- a/Ix/Kernel/Verify/Consistency/BinderInference.lean +++ b/Ix/Kernel/Verify/Consistency/BinderInference.lean @@ -4,14 +4,14 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.Application -import Ix.Kernel.Verify.Consistency.ScopedConstant +import Ix.Kernel.Verify.Consistency.ConstantCache import Ix.Theory.Model.Checking /-! # Production dependent function inference Finite inference trees follow the production method table's decreasing fuel. -Their premises record cache misses, actual execution prefixes, local-context +Their premises record cache observations, actual execution prefixes, local-context frames, finite interning support, and index bounds. Semantic checking is derived from these trees. A declaration's separate type inference supplies the expected type's hereditary validity before checking becomes typing. @@ -237,6 +237,10 @@ inductive BinderInference {β : Type u} (conditions : (entry.type.instL (arguments.toList.map readLevel)).annotations = type.annotations) : BinderInference resolve entries locals context fuel before (.const id arguments info) (.const ref (arguments.toList.map readLevel)) type + | cachedConst {locals context fuel before id arguments info ref entry type} + (cache : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) : + BinderInference resolve entries locals context fuel before (.const id arguments info) + (.const ref (arguments.toList.map readLevel)) type | app {locals context fuel before fn arg info f a A A' B condition} (full : before.inferOnly = false) (miss : UncachedInference before (.app fn arg info)) @@ -332,6 +336,9 @@ theorem BinderInference.soundWithSynthesis {β : Type u} | polymorphic miss support prediction conditions => obtain ⟨typeReads, typed⟩ := infer_const_scoped_annotated miss support prediction conditions accepted exact ⟨typeReads, typed.checking, fun _ => typed⟩ + | cachedConst cache => + obtain ⟨typeReads, typed⟩ := cache.sound accepted + exact ⟨typeReads, typed.checking, fun _ => typed⟩ | @app locals context fuel before fn arg info f a A A' B condition full miss trace functionTree head argumentTree conditions hashPath comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful ihFunction ihArgument => diff --git a/Ix/Kernel/Verify/Consistency/ConstantCache.lean b/Ix/Kernel/Verify/Consistency/ConstantCache.lean new file mode 100644 index 000000000..728b72934 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/ConstantCache.lean @@ -0,0 +1,150 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.ScopedConstant + +/-! +# Constant inference cache hits + +Production cache selection gives full checking priority and consults the +inference-only partition only under that policy. A selected constant result +must equal pure universe substitution of its current loaded declaration. +The admitted entry then supplies typing; no semantic cache invariant is an +input. These witnesses check individual entries, not global cache maintenance. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- The exact cache entry eligible at production's computed inference key. +An inference-only result cannot witness a full-mode hit or override a full +result. No declaration lookup or recursive inference is performed on a hit. -/ +structure InferenceCacheHit (before : TcState .anon) (term : KExpr .anon) where + key : Address × Address + keyed : TcState .anon + cached : KExpr .anon + keyRun : TcM.inferKey term before = .ok key keyed + selected : keyed.env.inferCache[key]? = some cached ∨ + (keyed.env.inferCache[key]? = none ∧ before.inferOnly = true ∧ + keyed.env.inferOnlyCache[key]? = some cached) + +/-- Cache selection determines both the returned type and state, without +executing the uncached branch or rewriting either cache partition. -/ +theorem InferenceCacheHit.run {before : TcState .anon} {term : KExpr .anon} + (hit : InferenceCacheHit before term) (methods : Methods .anon) : + RecM.infer term methods before = .ok hit.cached hit.keyed := by + change (RecM.infer term).run methods before = _ + unfold RecM.infer RecM.inferWith + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + change EStateM.bind (TcM.inferKey term) _ before = _ + rw [EStateM.bind, hit.keyRun] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ hit.keyed = _ + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) hit.keyed = + .ok hit.keyed hit.keyed from rfl] + rcases hit.selected with full | ⟨full, policy, only⟩ + · simp only [full]; rfl + · simp only [full, policy, if_true, ReaderT.run_bind] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ hit.keyed = _ + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) hit.keyed = + .ok hit.keyed hit.keyed from rfl] + simp only [only] + rfl + +/-- A successful constant miss inserts the exact pure substituted type in +the selected partition. This establishes the concrete substitution agreement +used by later hits; preserving it across other operations remains explicit. -/ +theorem infer_const_cache_write {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {before after : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} + {methods : Methods .anon} {result : KExpr .anon} + (miss : UncachedInference before (.const id arguments info)) + (support : ScopedConstantInferenceSupport resolve entries miss.keyed id arguments ref entry) + (accepted : RecM.infer (.const id arguments info) methods before = .ok result after) : + ∃ concrete loaded, TcM.getConst id miss.keyed = .ok concrete loaded ∧ + concrete.lvls.toNat = arguments.size ∧ + KExpr.instantiateUnivParamsSpec concrete.ty arguments = .ok result ∧ + (if before.inferOnly then after.env.inferOnlyCache[miss.key]? + else after.env.inferCache[miss.key]?) = some result := by + obtain ⟨state, run, written⟩ := infer_uncached_success_state miss accepted + obtain ⟨concrete, loaded, got, arity, instantiated⟩ := inferUncached_const_instantiation run + obtain ⟨_, _, resources⟩ := support.lookup concrete loaded got + have post := TcM.instantiateUnivParams_wf resources.faithful + (fun _ h => Or.inr h) ⟨resources.coherent, fun _ h => Or.inl h⟩ + rw [instantiated] at post + refine ⟨concrete, loaded, got, arity, post.2.1, ?_⟩ + cases policy : before.inferOnly <;> simp [policy, written] + +/-- Concrete coherence for one selected constant cache entry. Both arity +equalities are explicit because a hit skips the runtime arity guard. The +cached syntax equals pure substitution of a loaded declaration whose type +reads as the admitted entry. Prediction and annotations select the result +of a finite inference tree; they do not assert typing or validity. -/ +structure CachedConstantInferenceSupport {β : Type u} + (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) + (before : TcState .anon) (id : KId .anon) (arguments : Array (KUniv .anon)) + (info : ExprInfo .anon) (ref : ConstRef β) (entry : ConstantEntry β) (type : AExpr β) where + hit : InferenceCacheHit before (.const id arguments info) + concrete : KConst .anon + loaded : hit.keyed.env.get? id = some concrete + resolved : resolve id.addr = some ref + found : entries ref = some entry + count : concrete.lvls.toNat = entry.universes + arity : concrete.lvls.toNat = arguments.size + scope : entry.type.Scope entry.universes 0 + reading : readScopedExpr? resolve [] concrete.ty = some entry.type.erase + levels : UniverseSubstitutionSupport arguments concrete.ty + substitution : KExpr.instantiateUnivParamsSpec concrete.ty arguments = .ok hit.cached + prediction : readInstantiatedType? resolve concrete.ty arguments = some type.erase + conditions : (entry.type.instL (arguments.toList.map readLevel)).annotations = type.annotations + +/-- The pure prediction and concrete cache agreement derive the cached +type's reading in every active context and its congruence with the admitted +entry's instantiated type. Mutable interning resources are unnecessary. -/ +theorem CachedConstantInferenceSupport.refinement {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {before : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} + (support : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) + (locals : List FVarId) : + readScopedExpr? resolve locals support.hit.cached = some type.erase ∧ + AExpr.LevelEquivalent (entry.type.instL (arguments.toList.map readLevel)) type ∧ + (arguments.toList.map readLevel).length = entry.universes := by + have scope : entry.type.Scope arguments.size 0 := by + rw [← support.arity, support.count] + exact support.scope + obtain ⟨output, outputReads, same⟩ := instantiateUnivParamsSpec_readScopedAnnotated + (locals := locals) support.levels scope.erase.1 support.reading support.substitution + have predicted := support.prediction + rw [readInstantiatedType?, support.substitution] at predicted + have typeReads := readScopedExpr?_weaken_closed predicted locals + have equal := AExpr.eq_of_erase_annotations + (Option.some.inj (outputReads.symm.trans typeReads)) + (same.annotations.symm.trans support.conditions) + refine ⟨typeReads, equal ▸ same, ?_⟩ + simpa only [List.length_map, Array.length_toList] using support.arity.symm.trans support.count + +/-- An actual cache hit has the admitted constant's full model typing. The +semantic conclusion is derived from concrete cache coherence, pure universe +substitution, and the dependency model, including simplifying levels. -/ +theorem CachedConstantInferenceSupport.sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} + {before after : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} + {methods : Methods .anon} {result : KExpr .anon} + (support : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) + (accepted : RecM.infer (.const id arguments info) methods before = .ok result after) : + readScopedExpr? resolve locals result = some type.erase ∧ + TypingClaim.{u,v} entries context (.const ref (arguments.toList.map readLevel)) type := by + rw [support.hit.run methods] at accepted + cases accepted + obtain ⟨typeReads, same, arity⟩ := support.refinement locals + exact ⟨typeReads, same.typing (TypingClaim.const support.found arity)⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/ScopedInstUniv.lean b/Ix/Kernel/Verify/Consistency/ScopedInstUniv.lean index 427e7b588..1cb237e01 100644 --- a/Ix/Kernel/Verify/Consistency/ScopedInstUniv.lean +++ b/Ix/Kernel/Verify/Consistency/ScopedInstUniv.lean @@ -108,6 +108,36 @@ theorem instantiateUnivParams_scoped_eq {resolve : Address → Option (ConstRef rw [run] at post exact instantiateUnivParamsSpec_scoped_eq locals reading post.2.1 +/-- The pure substituted tree has the admitted type's meaning, including the +empty shortcut and simplifying universe constructors. Cache verification uses +only level resources here; it does not run an interning operation. -/ +theorem instantiateUnivParamsSpec_readScopedAnnotated + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {arguments : Array (KUniv .anon)} {term result : KExpr .anon} {source : AExpr β} + (support : UniverseSubstitutionSupport arguments term) + (scope : source.erase.LevelWF arguments.size) + (reading : readScopedExpr? resolve [] term = some source.erase) + (run : KExpr.instantiateUnivParamsSpec term arguments = .ok result) : + ∃ output : AExpr β, readScopedExpr? resolve locals result = some output.erase ∧ + AExpr.LevelEquivalent (source.instL (arguments.toList.map readLevel)) output := by + have raw : ∃ output, readExpr? resolve result = some output ∧ + VExpr.LevelEquivalent (source.erase.instL (arguments.toList.map readLevel)) output := by + by_cases empty : arguments.isEmpty = true + · have args : arguments = #[] := Array.isEmpty_iff.mp empty + subst arguments + change Except.ok term = .ok result at run + cases run + refine ⟨source.erase, readScopedExpr?_closed reading, ?_⟩ + simpa only [Array.toList_empty, List.map_nil, scope.instL_nil] using + VExpr.LevelEquivalent.refl source.erase + · rw [KExpr.instantiateUnivParamsSpec, if_neg empty] at run + exact instUnivSpec_readExpr? support (readScopedExpr?_closed reading) run + obtain ⟨raw, rawReads, same⟩ := raw + rw [← AExpr.erase_instL] at same + obtain ⟨output, erased, equivalent⟩ := AExpr.reannotate_levels _ same + exact ⟨output, (instantiateUnivParamsSpec_scoped_eq locals reading run).trans + (erased ▸ rawReads), equivalent⟩ + /-- Read the actual returned annotated type in an active local context. Universe scope justifies the empty shortcut; all annotation occurrences and hereditary validity are preserved by the level congruence. -/ diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 0e9f7e93a..c293e6a66 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -494,8 +494,117 @@ private def polymorphicApplicationCases : TestSeq := let (env, target) := storePolymorphicCall env identity #[.zero] 0 #[0, 0] rowFailed env target) +/-- Every wrapper repeats the same closed carrier in its declared domain, +codomain, and lambda domain. The second and later references hit the cache +even when the driver clears caches before every declaration. -/ +private def repeatedReferenceEnvironment : Ixon.Env := Id.run do + let (env, function, carrier) := polymorphicReferences + let wrapper : Address → Ixon.Univ → Array UInt64 → Ix.DefKind → Ixon.Constant := + fun callee level arguments kind => + ⟨.defn ⟨kind, .safe, 0, + .leanAll (.ref 0 #[0]) (.ref 0 #[0]), + .leanLam (.ref 0 #[0]) (.app (.ref 1 arguments) (.var 0))⟩, + #[], #[carrier, callee], #[level]⟩ + let (env, first) := storeConst env (wrapper function .zero #[0] .defn) + let (env, _) := storeConst env (wrapper function (.succ .zero) #[0] .opaq) + let (env, _) := storeConst env (wrapper first .zero #[] .thm) + return env + +/-- The selected entry is populated by real inference, then reused in two +different local scopes. Cache keys and closed returned types stay stable. -/ +private def cachedConstantAcrossScopes (source : Ixon.Env × Address) + (arguments : Array (KUniv .anon)) (expected : KExpr .anon) (inferOnly : Bool) : Bool := + let term := KExpr.mkConst (m := .anon) ⟨source.2, ()⟩ arguments + let propType := KExpr.mkSort (m := .anon) .mkZero + let action : RecM .anon Bool := do + let first ← RecM.inferCall term + let key ← TcM.inferKey term + let initial ← get + let reuse : RecM .anon Bool := RecM.withLctxScope do + let _ ← TcM.openBinder () () propType (.mkVar 0 ()) + let activeKey ← TcM.inferKey term + let result ← RecM.inferCall term + return activeKey == key && result.addr == expected.addr && result.lbr == 0 && + (← get).lctx.size == 1 + let second ← reuse + let third ← reuse + let final ← get + return first.addr == expected.addr && second && third && + (if inferOnly then initial.env.inferOnlyCache[key]?.isSome && + final.env.inferOnlyCache.size == initial.env.inferOnlyCache.size && final.env.inferCache.isEmpty + else initial.env.inferCache[key]?.isSome && + final.env.inferCache.size == initial.env.inferCache.size && final.env.inferOnlyCache.isEmpty) + match TcM.runRec action { TcState.newLazyAnon source.1 with inferOnly } with + | .ok passed after => passed && after.lctx.size == 0 && after.env.nextFVarId == 2 + | .error _ _ => false + +/-- A deliberately different result in the ineligible partition makes the +selection policy observable. Full mode must compute a checked answer when +only that entry exists; inference-only mode must prefer a full result. -/ +private def constantCachePriority (fullHit : Bool) : Bool := + let term := KExpr.mkConst (m := .anon) ⟨polymorphicIdentity.2, ()⟩ #[levelOne] + let sentinel := KExpr.mkSort (m := .anon) .mkZero + let action : RecM .anon Bool := do + let expected ← RecM.inferOnlyCall term + let key ← TcM.inferKey term + modify fun state => { state with inferOnly := fullHit, env := { state.env with + inferCache := if fullHit then state.env.inferCache.insert key expected else state.env.inferCache + inferOnlyCache := state.env.inferOnlyCache.insert key sentinel } } + let result ← RecM.inferCall term + let state ← get + return result.addr == identityType.addr && result.addr != sentinel.addr && + state.env.inferCache[key]?.any (fun cached => cached.addr == expected.addr) && + state.env.inferOnlyCache[key]?.any (fun cached => cached.addr == sentinel.addr) + match TcM.runRec action (TcState.newLazyAnon polymorphicIdentity.1) with + | .ok passed _ => passed + | .error _ _ => false + +/-- Alternating two instances after their first use must retrieve the type +for that instance, while retaining only two closed constant cache entries. -/ +private def repeatedConstantInstances : Bool := + let propType := KExpr.mkSort (m := .anon) .mkZero + let propIdentity := KExpr.mkAll () () propType (.mkAll () () (.mkVar 0 ()) (.mkVar 1 ())) + let propTerm := KExpr.mkConst (m := .anon) ⟨polymorphicIdentity.2, ()⟩ #[.mkZero] + let typeTerm := KExpr.mkConst (m := .anon) ⟨polymorphicIdentity.2, ()⟩ #[levelOne] + let action : RecM .anon Bool := do + let _ ← RecM.inferCall propTerm + let _ ← RecM.inferCall typeTerm + let propResult ← RecM.inferCall propTerm + let typeResult ← RecM.inferCall typeTerm + return propTerm.addr != typeTerm.addr && propResult.addr == propIdentity.addr && + typeResult.addr == identityType.addr && (← get).env.inferCache.size == 2 + match TcM.runRec action (TcState.newLazyAnon polymorphicIdentity.1) with + | .ok passed _ => passed + | .error _ _ => false + +private def constantCacheCases : TestSeq := + test "constant cache: repeated carrier references and transitive calls check" + (allSucceeded repeatedReferenceEnvironment 5 { clearEvery := 0 }) + ++ test "constant cache: repeated references within one declaration survive per-item clearing" + (allSucceeded repeatedReferenceEnvironment 5 { clearEvery := 1 }) + ++ test "constant cache: polymorphic full results survive two fresh local scopes" + (cachedConstantAcrossScopes polymorphicIdentity #[levelOne] identityType false) + ++ test "constant cache: polymorphic inference-only results survive two fresh local scopes" + (cachedConstantAcrossScopes polymorphicIdentity #[levelOne] identityType true) + ++ test "constant cache: empty universe substitution reuses a monomorphic definition type" + (cachedConstantAcrossScopes (monomorphicIdentity (.succ .zero)) #[] identityType false) + ++ test "constant cache: repeated max substitution retains its simplified type" + (cachedConstantAcrossScopes (computedIdentity (.max (.var 0) (.var 1))) + #[.mkZero, levelOne] identityType false) + ++ test "constant cache: repeated imax substitution retains its simplified type" + (let propType := KExpr.mkSort (m := .anon) .mkZero + let expected := KExpr.mkAll () () propType (.mkAll () () (.mkVar 0 ()) (.mkVar 1 ())) + cachedConstantAcrossScopes (computedIdentity (.imax (.var 0) (.var 1))) + #[levelOne, .mkZero] expected true) + ++ test "constant cache: inference-only mode gives the full result priority" + (constantCachePriority true) + ++ test "constant cache: full mode ignores an inference-only answer and checks the constant" + (constantCachePriority false) + ++ test "constant cache: alternating universe instances retrieve their own cached types" + repeatedConstantInstances + public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, - polymorphicApplicationCases] + polymorphicApplicationCases, constantCacheCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 029f6ed39..93725fbc4 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -82,6 +82,15 @@ increasing sequence of strongly inaccessible cardinals. - `FVarInferenceSupport.sound` handles both local-variable cache partitions when a cached answer equals the current declaration's concrete type. These are structural cache checks, with no assumed semantic typing of cached data. +- `CachedConstantInferenceSupport.sound` brings monomorphic and polymorphic + constant hits into binder trees. The selected cached value equals pure + universe substitution of the current loaded declaration, whose type agrees + with the admitted entry. Pure substitution establishes semantic congruence, + including simplifying levels, without mutable interning resources. + `InferenceCacheHit.run` proves the exact cache selection and returned state; + full results take priority, and inference-only results require that policy. + `infer_const_cache_write` establishes substitution agreement for a successful + miss's insertion. General preservation of that agreement remains open. - `checkEnvAnon_atomic_preserves_model` connects a supported production environment run to model extension. `checkEnvAnon_atomic_no_false` excludes a declaration at an axiom type interpreted as empty, including False. @@ -121,6 +130,9 @@ def idProp (P : Prop) (p : P) : P := p def useId (P : Prop) (p : P) : P := idProp P p def applyProp (P Q : Prop) (f : P → Q) (p : P) : Q := f p def usePoly (P : Prop) (p : P) : P := ident.{0} P p +axiom T.{u} : Sort u +axiom f.{u} : T.{u} → T.{u} +def useF (x : T.{1}) : T.{1} := f.{1} x ``` `AtomicEnvironmentFragment` records the precise execution boundary: @@ -128,10 +140,13 @@ def usePoly (P : Prop) (p : P) : P := ident.{0} P p - Every source key occurs in the `buildAnonWork` result, and every work item represents an axiom or a definition. Lookup, routing, and reset witnesses identify the checked `KConst`. -- Sort, constant, application, forall, and lambda nodes in the inference witnesses miss both cache partitions. +- Sort, application, forall, and lambda nodes in the inference witnesses miss both cache partitions. Local-variable hits must match the current production declaration type. - Constant lookup agrees with - an already admitted type and universe count. A specialization supplies + Constant nodes use either the existing miss rule or a selected cache hit. + A hit checks the loaded declaration, universe arity, finite level resources, + and equality of the cached value with pure substitution of its type. + Its arity agreement is explicit because no runtime arity guard executes. + A miss's lookup agrees with an already admitted type and universe count. A specialization supplies closed universe arguments and finite interning/substitution resources at the actual post-lookup state. The occurrence annotations on the declared type agree with the substituted entry's annotations. These are structural @@ -153,8 +168,9 @@ def usePoly (P : Prop) (p : P) : P := ident.{0} P p Their witnesses retain the actual recursive calls, context preservation during function inference, and finite substitution resources. Function spines start with locals or admitted constants. Polymorphic constants supply - exact lazy-lookup agreement, a closed scoped reading of the selected entry's - type, and finite universe-substitution resources. Their result type is fixed + lazy-lookup agreement on misses or loaded-declaration agreement on hits, a + closed scoped reading of the selected entry's type, and finite + universe-substitution resources. Their result type is fixed by a pure `readInstantiatedType?` check with the substituted occurrence annotations, including when levels simplify. Monomorphic references retain their simpler empty-substitution rule. Applying a lambda directly and @@ -230,7 +246,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 121 exact theorem boundaries. The production +The consistency target checks 127 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -259,6 +275,11 @@ Polymorphic-call regressions include Prop/Type instances in real function bodies, `max`/`imax` simplification inside Pi domains, closed nested references under active locals, separate cache keys for different universe instances, and rejected universe arities and argument types. +Constant-cache regressions cover repeated carrier references within declared +types and bodies, including per-item cache clearing; full and inference-only +reuse across fresh local scopes; monomorphic and simplifying substitutions; +partition priority; and alternating universe instances. Deliberately different +values in an ineligible partition make the selection tests observable. ## Certified host adapters @@ -286,6 +307,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Direct production refinement and its audit | [`Ix/Kernel/Verify/Consistency.lean`](../Ix/Kernel/Verify/Consistency.lean) | | Polymorphic inference and universe substitution | [`Consistency/Constant.lean`](../Ix/Kernel/Verify/Consistency/Constant.lean), [`InstUniv.lean`](../Ix/Kernel/Verify/Consistency/InstUniv.lean), [`Model/LevelCongruence.lean`](../Ix/Theory/Model/LevelCongruence.lean) | | Polymorphic calls inside binders | [`Consistency/ScopedConstant.lean`](../Ix/Kernel/Verify/Consistency/ScopedConstant.lean), [`ScopedInstUniv.lean`](../Ix/Kernel/Verify/Consistency/ScopedInstUniv.lean) | +| Constant cache selection, writes, and typing | [`Consistency/ConstantCache.lean`](../Ix/Kernel/Verify/Consistency/ConstantCache.lean) | | Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`Application.lean`](../Ix/Kernel/Verify/Consistency/Application.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | | Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | diff --git a/docs/theory.md b/docs/theory.md index da733f35e..73a5a39c9 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -27,6 +27,9 @@ Binder inference uses the declared type's separate formation check to turn `Model.CheckingClaim` into semantic typing. Application spines headed by locals or admitted polymorphic constants derive their type's validity from the context or dependency model, then check arguments and substitute the dependent result. +Constant cache hits derive the same typing from concrete agreement with pure +universe substitution of a loaded declaration. Cache selection and successful +miss writes are proved; general cache maintenance remains a separate obligation. Full checker consistency and compiler/backend refinement remain open. From 6ff0ccaa5fb90d6e43561e31abcb86b99c4c6a8a Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 15:09:41 -0400 Subject: [PATCH 12/63] Prove inference cache preservation for sorts and loaded constants --- Ix/Kernel/Verify/Consistency.lean | 16 +- Ix/Kernel/Verify/Consistency/Atomic.lean | 8 +- Ix/Kernel/Verify/Consistency/Audit.lean | 34 +- .../Verify/Consistency/BinderInference.lean | 54 +-- .../Verify/Consistency/ConstantCache.lean | 201 +++++++++-- Ix/Kernel/Verify/Consistency/Context.lean | 5 +- .../Verify/Consistency/InferenceCache.lean | 323 ++++++++++++++++++ Ix/Kernel/Verify/Consistency/SortCache.lean | 109 ++++++ Tests/Ix/Kernel/Consistency.lean | 138 +++++++- docs/kernel-verification.md | 46 ++- docs/theory.md | 9 +- 11 files changed, 854 insertions(+), 89 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/InferenceCache.lean create mode 100644 Ix/Kernel/Verify/Consistency/SortCache.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 74022d49c..f7e00969d 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -13,6 +13,8 @@ import Ix.Kernel.Verify.Consistency.Atomic import Ix.Kernel.Verify.Consistency.ScopedExpr import Ix.Kernel.Verify.Consistency.ScopedInstUniv import Ix.Kernel.Verify.Consistency.ScopedConstant +import Ix.Kernel.Verify.Consistency.InferenceCache +import Ix.Kernel.Verify.Consistency.SortCache import Ix.Kernel.Verify.Consistency.ConstantCache import Ix.Kernel.Verify.Consistency.Context import Ix.Kernel.Verify.Consistency.BinderOpening @@ -39,14 +41,20 @@ The binder case also requires explicit syntactic scope and references to the preceding interface. Its separate declared-type inference turns semantic checking into typing. Local cache hits agree with the actual declaration type. Constant hits agree with pure universe substitution of a loaded, admitted declaration; -the other inference nodes retain cache-miss boundaries. +sort hits return the canonical successor sort. Application, forall, and lambda +nodes retain misses in every eligible cache partition. Full mode leaves the +inference-only partition unconstrained at a miss. Applications use syntactic Pi exposure, full argument checking, hash conversion, and arguments without eager-reduction markers. Constant- and local-headed spines derive type validity from the admitted model or local context; their arguments may be lambdas. Polymorphic nodes use closed source readings, finite substitution resources, and a pure prediction of the returned syntax with matching occurrence annotations. -Cache hits additionally check arity because they skip the runtime guard. Exact -cache selection and successful constant writes are proved; general preservation -of cache agreement across checker operations remains an explicit obligation. +Constant hits additionally check arity because they skip the runtime guard. +Concrete cache agreement is preserved by sort and already-loaded constant +inference. Cache frames compose through interning, key computation, binder +opening, unrelated writes, and scope/policy cleanup, including errors. +These frames transport closed constant witnesses; maintained agreement also +constructs sort leaves. Initial agreement, finite execution resources, lazy +loading, and preservation through composite inference remain explicit obligations. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Atomic.lean b/Ix/Kernel/Verify/Consistency/Atomic.lean index e17b55c20..ebc75a988 100644 --- a/Ix/Kernel/Verify/Consistency/Atomic.lean +++ b/Ix/Kernel/Verify/Consistency/Atomic.lean @@ -20,14 +20,14 @@ open Theory Theory.Model universe u v -/-- The two inference-cache partitions miss at the actual key computed by -production. Both misses are required, so this applies in either policy mode. -/ +/-- Every eligible inference-cache partition misses at the actual production +key. Full mode does not inspect or constrain the inference-only partition. -/ structure UncachedInference (before : TcState .anon) (term : KExpr .anon) where key : Address × Address keyed : TcState .anon keyRun : TcM.inferKey term before = .ok key keyed fullMiss : keyed.env.inferCache[key]? = none - onlyMiss : keyed.env.inferOnlyCache[key]? = none + onlyMiss : before.inferOnly = true → keyed.env.inferOnlyCache[key]? = none /-- Recover the uncached execution and the exact final cache write from a successful miss. The policy is read before production computes the key. -/ @@ -73,7 +73,7 @@ theorem infer_uncached_success_state {term type : KExpr .anon} rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) miss.keyed = .ok miss.keyed miss.keyed from rfl] at accepted simp only at accepted - rw [miss.onlyMiss] at accepted + rw [miss.onlyMiss policy] at accepted change EStateM.bind (RecM.inferUncached RecM.inferCall true term methods) _ miss.keyed = _ at accepted cases run : RecM.inferUncached RecM.inferCall true term methods miss.keyed with diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index e0f399783..18977e082 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -38,7 +38,8 @@ private def atomicRoots : Array Lean.Name := #[ ``AtomicInferenceSupport.scopeAndReferences, ``AtomicInference.sound, ``inferUncached_fvar_sound, ``infer_fvar_sound, ``FVarInferenceSupport.output, ``FVarInferenceSupport.sound, - ``InferenceCacheHit.run, + ``InferenceCacheHit.run, ``infer_sort_cached_sound, + ``infer_sort_cache_agreement, ``infer_sort_cache_frame, ``BinderInference.sortOfAgreement, ``ForallInferenceTrace.output, ``LambdaInferenceTrace.output, ``BinderInference.sound, ``inferUncached_monomorphic_const_scoped, ``ApplicationInferenceTrace.output, ``BinderInference.soundWithSynthesis, ``BinderInference.synthesis, @@ -58,7 +59,30 @@ private def instantiationRoots : Array Lean.Name := #[ ``inferUncached_const_scoped_refinement, ``inferUncached_const_scoped_sound, ``inferUncached_const_predicted_type, ``infer_const_scoped_annotated, ``infer_const_cache_write, ``CachedConstantInferenceSupport.refinement, - ``CachedConstantInferenceSupport.sound + ``CachedConstantInferenceSupport.sound, + ``instantiateUnivParams_cache_frame, ``infer_const_cache_frame, + ``infer_const_cache_agreement, ``CachedConstantInferenceSupport.transport, + ``CachedConstantInferenceSupport.openBinder, ``CachedConstantInferenceSupport.sound_after_frame +] + +private def cacheFrameRoots : Array Lean.Name := #[ + ``InferenceCacheFrame.refl, ``InferenceCacheFrame.trans, ``InferenceCacheAgreement.frame, + ``cacheInferResult_eq, ``PreservesInferenceCache.pure, ``PreservesInferenceCache.bind, + ``PreservesInferenceCache.runIntern, ``InferenceCacheFrame.localContext, + ``InferenceCacheAgreement.policy, ``withInferOnly_eq, + ``PreservesInferenceCache.withInferOnly, ``getConst_loaded +] + +private def cacheMapRoots : Array Lean.Name := #[ + ``InferenceCacheAgreement.write, ``PreservesInferenceCache.write_other, + ``InferenceCacheAgreement.clearReductionCaches +] + +private def cacheKeyRoots : Array Lean.Name := #[ + ``inferKey_closed, ``InferenceCacheHit.key_closed, ``observeInferenceCache, + ``InferenceCacheAgreement.selected, ``InferenceCacheHit.transport, + ``PreservesInferenceCache.inferKey, ``PreservesInferenceCache.openBinder, + ``withLctxScope_eq, ``PreservesInferenceCache.withLctxScope ] private def productionRoots : Array Lean.Name := #[ @@ -153,11 +177,11 @@ def roots : Array RootAllowance := #[ { root := ``Theory.Model.CheckingClaim.typing, standardAxioms := standard }, { root := ``Theory.Model.CheckingClaim.typingSort, standardAxioms := standard }, { root := ``Theory.Model.CheckingClaim.lam, standardAxioms := standard } -] ++ scopedRoots.map (fun root => { +] ++ (scopedRoots ++ cacheFrameRoots).map (fun root => { root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction -}) ++ contextRoots.map (fun root => { +}) ++ (contextRoots ++ cacheMapRoots).map (fun root => { root, standardAxioms := standard, forbiddenDependencies := forbiddenProduction -}) ++ binderWalkerRoots.map (fun root => { +}) ++ (binderWalkerRoots ++ cacheKeyRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction }) ++ (atomicRoots ++ instantiationRoots).map (fun root => { diff --git a/Ix/Kernel/Verify/Consistency/BinderInference.lean b/Ix/Kernel/Verify/Consistency/BinderInference.lean index c0950986e..7d503b5f1 100644 --- a/Ix/Kernel/Verify/Consistency/BinderInference.lean +++ b/Ix/Kernel/Verify/Consistency/BinderInference.lean @@ -5,6 +5,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Verify.Consistency.Application import Ix.Kernel.Verify.Consistency.ConstantCache +import Ix.Kernel.Verify.Consistency.SortCache import Ix.Theory.Model.Checking /-! @@ -59,29 +60,6 @@ theorem inferUncached_monomorphic_const_scoped {β : Type u} cases accepted exact lookup concrete _ got -private theorem withLctxScope_eq (action : RecM .anon α) - (methods : Methods .anon) (before : TcState .anon) : - (RecM.withLctxScope action).run methods before = - match action.run methods before with - | .ok value after => - .ok value {after with lctx := after.lctx.truncate before.lctx.size} - | .error err after => - .error err {after with lctx := after.lctx.truncate before.lctx.size} := by - unfold RecM.withLctxScope - rw [ReaderT.run_bind] - change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ - unfold EStateM.bind - rw [show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] - simp only - unfold tryFinally - change EStateM.map (fun pair : α × PUnit => pair.1) - (tryFinally' (action.run methods) (fun _ => - (modify (fun after : TcState .anon => - {after with lctx := after.lctx.truncate before.lctx.size}) : - TcM .anon PUnit))) before = _ - unfold EStateM.map MonadFinally.tryFinally' EStateM.instMonadFinally - cases run : action.run methods before <;> simp only [run] <;> rfl - private theorem withLctxScope_success {action : RecM .anon α} {methods : Methods .anon} {before after : TcState .anon} {result : α} (accepted : (RecM.withLctxScope action).run methods before = .ok result after) : @@ -214,6 +192,11 @@ inductive BinderInference {β : Type u} miss.keyed.env.intern.ExprSupport term ∨ term = KExpr.mkSort (KUniv.mkSucc level)) : BinderInference resolve entries locals context fuel before (.sort level info) (.sort (readLevel level)) (.sort (.succ (readLevel level))) + | cachedSort {locals context fuel before level info} + (hit : InferenceCacheHit before (.sort level info)) + (canonical : hit.cached = KExpr.mkSort (KUniv.mkSucc level)) : + BinderInference resolve entries locals context fuel before (.sort level info) + (.sort (readLevel level)) (.sort (.succ (readLevel level))) | fvar {locals context fuel before id name info index A} (cache : FVarInferenceSupport before id name info) (registered : localIndex? locals id = some index) @@ -300,6 +283,28 @@ inductive BinderInference {β : Type u} BinderInference resolve entries locals context (fuel + 1) before (.lam name bi domain body info) (.lam condition A b) (.forallE condition A B) +/-- Construct the sort leaf from a maintained cache invariant. The production +key and eligible hit/miss observation are derived, including when only the +ignored partition is populated in full mode. -/ +def BinderInference.sortOfAgreement {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {before : TcState .anon} {level : KUniv .anon} {info : ExprInfo .anon} + (closed : (KExpr.sort level info).lbr = 0) + (agreement : InferenceCacheAgreement before ((KExpr.sort level info).addr, emptyCtxAddr) + (KExpr.mkSort (KUniv.mkSucc level))) + (coherent : before.env.intern.WF) + (faithful : KExpr.KeyCollisionFree fun term => before.env.intern.ExprSupport term ∨ + term = KExpr.mkSort (KUniv.mkSucc level)) : + BinderInference resolve entries locals context fuel before (.sort level info) + (.sort (readLevel level)) (.sort (.succ (readLevel level))) := by + rcases observeInferenceCache (inferKey_closed closed before) with + ⟨hit, keyEq, stateEq⟩ | ⟨miss, _, stateEq⟩ + · refine .cachedSort hit (InferenceCacheAgreement.selected hit ?_) + simpa only [keyEq, stateEq] using agreement + · exact .sort miss (by simpa only [stateEq] using coherent) + (by simpa only [stateEq] using faithful) + /-- Successful production inference reads the expected model type and checks the actual source term against it. No recursive semantic premise is supplied by the caller: induction follows the finite operational support tree. -/ @@ -324,6 +329,9 @@ theorem BinderInference.soundWithSynthesis {β : Type u} refine ⟨?_, (TypingClaim.sort _).checking, fun head => by cases head⟩ rw [internExpr_readScopedExpr? coherent faithful] simp [AExpr.erase] + | cachedSort hit canonical => + obtain ⟨typeReads, typed⟩ := infer_sort_cached_sound hit canonical accepted + exact ⟨typeReads, typed.checking, fun head => by cases head⟩ | fvar cache registered atIndex => obtain ⟨typeReads, typed⟩ := cache.sound agreement registered atIndex accepted exact ⟨typeReads, typed.checking, fun _ => typed⟩ diff --git a/Ix/Kernel/Verify/Consistency/ConstantCache.lean b/Ix/Kernel/Verify/Consistency/ConstantCache.lean index 728b72934..0b4a7e80d 100644 --- a/Ix/Kernel/Verify/Consistency/ConstantCache.lean +++ b/Ix/Kernel/Verify/Consistency/ConstantCache.lean @@ -4,6 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.ScopedConstant +import Ix.Kernel.Verify.Consistency.InferenceCache /-! # Constant inference cache hits @@ -12,7 +13,10 @@ Production cache selection gives full checking priority and consults the inference-only partition only under that policy. A selected constant result must equal pure universe substitution of its current loaded declaration. The admitted entry then supplies typing; no semantic cache invariant is an -input. These witnesses check individual entries, not global cache maintenance. +input. Structural frames transport these witnesses through operations that +retain the watched entries and loaded declarations. Inference of an already +loaded constant preserves concrete agreement at its key and frames other keys; +lazy loading and general recursive inference need further preservation proofs. -/ namespace Ix.Kernel.Consistency @@ -21,39 +25,104 @@ open Theory Theory.Model universe u v -/-- The exact cache entry eligible at production's computed inference key. -An inference-only result cannot witness a full-mode hit or override a full -result. No declaration lookup or recursive inference is performed on a hit. -/ -structure InferenceCacheHit (before : TcState .anon) (term : KExpr .anon) where - key : Address × Address - keyed : TcState .anon - cached : KExpr .anon - keyRun : TcM.inferKey term before = .ok key keyed - selected : keyed.env.inferCache[key]? = some cached ∨ - (keyed.env.inferCache[key]? = none ∧ before.inferOnly = true ∧ - keyed.env.inferOnlyCache[key]? = some cached) - -/-- Cache selection determines both the returned type and state, without -executing the uncached branch or rewriting either cache partition. -/ -theorem InferenceCacheHit.run {before : TcState .anon} {term : KExpr .anon} - (hit : InferenceCacheHit before term) (methods : Methods .anon) : - RecM.infer term methods before = .ok hit.cached hit.keyed := by - change (RecM.infer term).run methods before = _ - unfold RecM.infer RecM.inferWith - simp only [ReaderT.run_bind, ReaderT.run_monadLift] - change EStateM.bind (TcM.inferKey term) _ before = _ - rw [EStateM.bind, hit.keyRun] - change EStateM.bind (get : TcM .anon (TcState .anon)) _ hit.keyed = _ - rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) hit.keyed = - .ok hit.keyed hit.keyed from rfl] - rcases hit.selected with full | ⟨full, policy, only⟩ - · simp only [full]; rfl - · simp only [full, policy, if_true, ReaderT.run_bind] - change EStateM.bind (get : TcM .anon (TcState .anon)) _ hit.keyed = _ - rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) hit.keyed = - .ok hit.keyed hit.keyed from rfl] - simp only [only] +/-- Looking up an already loaded declaration does not invoke lazy loading +or change any checker state. -/ +theorem getConst_loaded {before : TcState .anon} {id : KId .anon} {concrete : KConst .anon} + (loaded : before.env.get? id = some concrete) : + TcM.getConst id before = .ok concrete before := by + have found : TcM.tryGetConst id before = .ok (some concrete) before := by + unfold TcM.tryGetConst + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = + .ok before before from rfl] + simp only [loaded] rfl + unfold TcM.getConst + change EStateM.bind (TcM.tryGetConst id) _ before = _ + rw [EStateM.bind, found] + rfl + +/-- The verified universe walker changes only its intern table, preserving +both inference partitions and the loaded declarations at every key. -/ +theorem instantiateUnivParams_cache_frame {before after : TcState .anon} + {term result : KExpr .anon} {arguments : Array (KUniv .anon)} + (support : UniverseInstantiationSupport before term arguments) + (run : TcM.instantiateUnivParams term arguments before = .ok result after) + (key : Address × Address) : InferenceCacheFrame key before after := by + have post := TcM.instantiateUnivParams_wf support.faithful + (fun _ h => Or.inr h) ⟨support.coherent, fun _ h => Or.inl h⟩ + rw [run] at post + rw [post.2.2.1] + exact ⟨rfl, rfl, rfl⟩ + +/-- Inference of an already loaded constant frames all other cache keys, +including on a miss that runs universe substitution and writes a result. +Lazy loading needs its own preservation proof when the declaration is absent. -/ +theorem infer_const_cache_frame {before keyed after : TcState .anon} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {key other : Address × Address} {concrete : KConst .anon} + {methods : Methods .anon} {result : KExpr .anon} + (keyRun : TcM.inferKey (.const id arguments info) before = .ok key keyed) + (different : key ≠ other) + (loaded : keyed.env.get? id = some concrete) + (resources : UniverseInstantiationSupport keyed concrete.ty arguments) + (accepted : RecM.infer (.const id arguments info) methods before = .ok result after) : + InferenceCacheFrame other before after := by + have keyFrame := PreservesInferenceCache.inferKey other (.const id arguments info) before + rw [keyRun] at keyFrame + rcases observeInferenceCache keyRun with ⟨hit, _, stateEq⟩ | ⟨miss, keyEq, stateEq⟩ + · rw [hit.run methods] at accepted + cases accepted + simpa only [stateEq] using keyFrame + · obtain ⟨state, run, written⟩ := infer_uncached_success_state miss accepted + rw [stateEq] at run + obtain ⟨actual, foundState, got, _, instantiated⟩ := inferUncached_const_instantiation run + rw [getConst_loaded loaded] at got + cases got + have substitution := instantiateUnivParams_cache_frame resources instantiated other + have wrote : RecM.cacheInferResult before.inferOnly key result methods state = .ok () after := by + rw [cacheInferResult_eq] + rw [keyEq] at written + rw [written] + have tail := PreservesInferenceCache.write_other different before.inferOnly result methods state + rw [wrote] at tail + exact keyFrame.trans (substitution.trans tail) + +/-- A maintained agreement at a loaded constant's key survives its actual +inference. The pure substituted tree determines both cache writes and hits; +the two partition witnesses no longer need to be re-established afterward. -/ +theorem infer_const_cache_agreement {before keyed after : TcState .anon} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {key : Address × Address} {concrete : KConst .anon} {expected result : KExpr .anon} + {methods : Methods .anon} + (keyRun : TcM.inferKey (.const id arguments info) before = .ok key keyed) + (agreement : InferenceCacheAgreement keyed key expected) + (loaded : keyed.env.get? id = some concrete) + (resources : UniverseInstantiationSupport keyed concrete.ty arguments) + (prediction : KExpr.instantiateUnivParamsSpec concrete.ty arguments = .ok expected) + (accepted : RecM.infer (.const id arguments info) methods before = .ok result after) : + result = expected ∧ InferenceCacheAgreement after key expected := by + rcases observeInferenceCache keyRun with ⟨hit, keyEq, stateEq⟩ | ⟨miss, keyEq, stateEq⟩ + · rw [hit.run methods] at accepted + cases accepted + refine ⟨InferenceCacheAgreement.selected hit ?_, ?_⟩ + · simpa only [keyEq, stateEq] using agreement + · simpa only [stateEq] using agreement + · obtain ⟨state, run, written⟩ := infer_uncached_success_state miss accepted + rw [stateEq] at run + obtain ⟨actual, foundState, got, _, instantiated⟩ := inferUncached_const_instantiation run + rw [getConst_loaded loaded] at got + cases got + have post := TcM.instantiateUnivParams_wf resources.faithful + (fun _ h => Or.inr h) ⟨resources.coherent, fun _ h => Or.inl h⟩ + rw [instantiated] at post + have equal := Except.ok.inj (post.2.1.symm.trans prediction) + refine ⟨equal, ?_⟩ + have unchanged := agreement.frame (instantiateUnivParams_cache_frame resources instantiated key) + apply unchanged.write (policy := before.inferOnly) (methods := methods) + rw [cacheInferResult_eq] + rw [keyEq, equal] at written + rw [written] /-- A successful constant miss inserts the exact pure substituted type in the selected partition. This establishes the concrete substitution agreement @@ -103,6 +172,54 @@ structure CachedConstantInferenceSupport {β : Type u} prediction : readInstantiatedType? resolve concrete.ty arguments = some type.erase conditions : (entry.type.instL (arguments.toList.map readLevel)).annotations = type.annotations +/-- A preserved closed cache entry and unchanged loaded declarations carry +the complete constant witness into a later checker state. Only checking +policy matters for the eligibility of an inference-only hit. -/ +def CachedConstantInferenceSupport.transport {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {before after : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} + (support : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) + (closed : (KExpr.const id arguments info).lbr = 0) + (frame : InferenceCacheFrame ((KExpr.const id arguments info).addr, emptyCtxAddr) before after) + (policy : after.inferOnly = before.inferOnly) : + CachedConstantInferenceSupport resolve entries after id arguments info ref entry type := { + hit := support.hit.transport closed frame policy + concrete := support.concrete + loaded := by + change after.env.get? id = some support.concrete + have loaded := support.loaded + rw [(support.hit.key_closed closed).2] at loaded + simpa only [KEnv.get?, frame.constants] using loaded + resolved := support.resolved, found := support.found + count := support.count, arity := support.arity, scope := support.scope + reading := support.reading, levels := support.levels + substitution := support.substitution, prediction := support.prediction + conditions := support.conditions +} + +/-- Actual binder opening supplies the cache frame and policy equality. No +new lookup, substitution prediction, or cache-hit observation is supplied. -/ +def CachedConstantInferenceSupport.openBinder {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {before after : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body opened : KExpr .anon} {fresh : FVarId} + (support : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) + (closed : (KExpr.const id arguments info).lbr = 0) + (run : TcM.openBinder name bi domain body before = .ok (opened, fresh) after) : + CachedConstantInferenceSupport resolve entries after id arguments info ref entry type := + support.transport closed (by + have frame := PreservesInferenceCache.openBinder + ((KExpr.const id arguments info).addr, emptyCtxAddr) name bi domain body before + rw [run] at frame + exact frame) (by + rw [openBinder_eq] at run + split at run + · cases run; rfl + · contradiction) + /-- The pure prediction and concrete cache agreement derive the cached type's reading in every active context and its congruence with the admitted entry's instantiated type. Mutable interning resources are unnecessary. -/ @@ -147,4 +264,22 @@ theorem CachedConstantInferenceSupport.sound {β : Type u} obtain ⟨typeReads, same, arity⟩ := support.refinement locals exact ⟨typeReads, same.typing (TypingClaim.const support.found arity)⟩ +/-- Reuse the proved constant type after a sequence of operations whose +cache frames compose. The later hit observation and declaration agreement +are transported from the earlier witness. -/ +theorem CachedConstantInferenceSupport.sound_after_frame {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} + {before later after : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} + {methods : Methods .anon} {result : KExpr .anon} + (support : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) + (closed : (KExpr.const id arguments info).lbr = 0) + (frame : InferenceCacheFrame ((KExpr.const id arguments info).addr, emptyCtxAddr) before later) + (policy : later.inferOnly = before.inferOnly) + (accepted : RecM.infer (.const id arguments info) methods later = .ok result after) : + readScopedExpr? resolve locals result = some type.erase ∧ + TypingClaim.{u,v} entries context (.const ref (arguments.toList.map readLevel)) type := + (support.transport closed frame policy).sound accepted + end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Context.lean b/Ix/Kernel/Verify/Consistency/Context.lean index 12f49aaaf..d79f61833 100644 --- a/Ix/Kernel/Verify/Consistency/Context.lean +++ b/Ix/Kernel/Verify/Consistency/Context.lean @@ -191,7 +191,8 @@ structure FVarInferenceSupport (before : TcState .anon) (id : FVarId) keyRun : TcM.inferKey (.fvar id name info) before = .ok key keyed fullMatches : ∀ cached decl, keyed.env.inferCache[key]? = some cached → keyed.lctx.find? id = some decl → cached = decl.ty - onlyMatches : ∀ cached decl, keyed.env.inferOnlyCache[key]? = some cached → + onlyMatches : before.inferOnly = true → + ∀ cached decl, keyed.env.inferOnlyCache[key]? = some cached → keyed.lctx.find? id = some decl → cached = decl.ty def FVarInferenceSupport.ofMiss {before : TcState .anon} {id : FVarId} @@ -255,7 +256,7 @@ theorem FVarInferenceSupport.output {before after : TcState .anon} {id : FVarId} | some cached => simp only [only] at accepted cases accepted - exact support.onlyMatches _ _ only found + exact support.onlyMatches policy _ _ only found | none => simp only [only] at accepted have lookup : RecM.inferUncached RecM.inferCall true (.fvar id name info) diff --git a/Ix/Kernel/Verify/Consistency/InferenceCache.lean b/Ix/Kernel/Verify/Consistency/InferenceCache.lean new file mode 100644 index 000000000..5f62028d0 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/InferenceCache.lean @@ -0,0 +1,323 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BinderOpening + +/-! +# Structural inference-cache invariants + +Agreement records concrete types in both cache partitions. Frame lemmas +preserve the entries at a fixed key and the loaded constants through actual +checker operations. Scope changes and unrelated cache writes do not require +a new semantic typing premise for each cached answer. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +/-- Closed source expressions use one key in every local context. Computing +that key leaves the entire checker state unchanged. -/ +theorem inferKey_closed {term : KExpr .anon} (closed : term.lbr = 0) + (before : TcState .anon) : + TcM.inferKey term before = .ok (term.addr, emptyCtxAddr) before := by + unfold TcM.inferKey + change EStateM.bind (TcM.ctxAddrForLbr term.lbr) _ before = _ + rw [closed] + rfl + +/-- The exact cache entry eligible at production's computed inference key. +An inference-only result cannot override a full result or serve full mode. -/ +structure InferenceCacheHit (before : TcState .anon) (term : KExpr .anon) where + key : Address × Address + keyed : TcState .anon + cached : KExpr .anon + keyRun : TcM.inferKey term before = .ok key keyed + selected : keyed.env.inferCache[key]? = some cached ∨ + (keyed.env.inferCache[key]? = none ∧ before.inferOnly = true ∧ + keyed.env.inferOnlyCache[key]? = some cached) + +/-- Cache selection determines both the returned type and state. -/ +theorem InferenceCacheHit.run {before : TcState .anon} {term : KExpr .anon} + (hit : InferenceCacheHit before term) (methods : Methods .anon) : + RecM.infer term methods before = .ok hit.cached hit.keyed := by + change (RecM.infer term).run methods before = _ + unfold RecM.infer RecM.inferWith + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + change EStateM.bind (TcM.inferKey term) _ before = _ + rw [EStateM.bind, hit.keyRun] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ hit.keyed = _ + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) hit.keyed = + .ok hit.keyed hit.keyed from rfl] + rcases hit.selected with full | ⟨full, policy, only⟩ + · simp only [full]; rfl + · simp only [full, policy, if_true, ReaderT.run_bind] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ hit.keyed = _ + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) hit.keyed = + .ok hit.keyed hit.keyed from rfl] + simp only [only] + rfl + +theorem InferenceCacheHit.key_closed {before : TcState .anon} {term : KExpr .anon} + (hit : InferenceCacheHit before term) (closed : term.lbr = 0) : + hit.key = (term.addr, emptyCtxAddr) ∧ hit.keyed = before := by + have run := hit.keyRun + rw [inferKey_closed closed] at run + exact ⟨(EStateM.Result.ok.inj run).1.symm, (EStateM.Result.ok.inj run).2.symm⟩ + +/-- Inspect the two concrete maps under production's policy and construct +the corresponding hit or miss witness at the supplied actual key. -/ +def observeInferenceCache {before keyed : TcState .anon} {term : KExpr .anon} + {key : Address × Address} (keyRun : TcM.inferKey term before = .ok key keyed) : + {hit : InferenceCacheHit before term // hit.key = key ∧ hit.keyed = keyed} ⊕ + {miss : UncachedInference before term // miss.key = key ∧ miss.keyed = keyed} := by + cases full : keyed.env.inferCache[key]? with + | some cached => + exact .inl ⟨⟨key, keyed, cached, keyRun, .inl full⟩, rfl, rfl⟩ + | none => + by_cases policy : before.inferOnly = true + · cases only : keyed.env.inferOnlyCache[key]? with + | some cached => + exact .inl ⟨⟨key, keyed, cached, keyRun, .inr ⟨full, policy, only⟩⟩, rfl, rfl⟩ + | none => exact .inr ⟨⟨key, keyed, keyRun, full, fun _ => only⟩, rfl, rfl⟩ + · exact .inr ⟨⟨key, keyed, keyRun, full, fun active => False.elim (policy active)⟩, rfl, rfl⟩ + +/-- Any populated entry at this key is the specified concrete type. Both +partitions are covered so agreement survives a change in checking policy. -/ +structure InferenceCacheAgreement (before : TcState .anon) + (key : Address × Address) (type : KExpr .anon) : Prop where + full : ∀ cached, before.env.inferCache[key]? = some cached → cached = type + only : ∀ cached, before.env.inferOnlyCache[key]? = some cached → cached = type + +/-- Relevant data retained by an operation: the two entries at one key and +the loaded declarations. Other keys and checker fields may change. -/ +structure InferenceCacheFrame (key : Address × Address) + (before after : TcState .anon) : Prop where + full : after.env.inferCache[key]? = before.env.inferCache[key]? + only : after.env.inferOnlyCache[key]? = before.env.inferOnlyCache[key]? + constants : after.env.consts = before.env.consts + +theorem InferenceCacheFrame.refl (key : Address × Address) (state : TcState .anon) : + InferenceCacheFrame key state state := ⟨rfl, rfl, rfl⟩ + +theorem InferenceCacheFrame.trans {key : Address × Address} + {before middle after : TcState .anon} + (first : InferenceCacheFrame key before middle) + (second : InferenceCacheFrame key middle after) : + InferenceCacheFrame key before after := + ⟨second.full.trans first.full, second.only.trans first.only, + second.constants.trans first.constants⟩ + +theorem InferenceCacheAgreement.frame {key : Address × Address} {type : KExpr .anon} + {before after : TcState .anon} (agreement : InferenceCacheAgreement before key type) + (frame : InferenceCacheFrame key before after) : InferenceCacheAgreement after key type := by + refine ⟨?_, ?_⟩ + · intro cached found; exact agreement.full cached (frame.full.symm.trans found) + · intro cached found; exact agreement.only cached (frame.only.symm.trans found) + +theorem InferenceCacheAgreement.selected {before : TcState .anon} {term : KExpr .anon} + {type : KExpr .anon} (hit : InferenceCacheHit before term) + (agreement : InferenceCacheAgreement hit.keyed hit.key type) : hit.cached = type := by + rcases hit.selected with full | ⟨_, _, only⟩ + · exact agreement.full _ full + · exact agreement.only _ only + +/-- Reuse a closed expression's selected hit after an operation preserving +its entries and checking policy. Its key and result are derived, not supplied +as another observation of inference. -/ +def InferenceCacheHit.transport {before after : TcState .anon} {term : KExpr .anon} + (hit : InferenceCacheHit before term) (closed : term.lbr = 0) + (frame : InferenceCacheFrame (term.addr, emptyCtxAddr) before after) + (policy : after.inferOnly = before.inferOnly) : InferenceCacheHit after term := { + key := (term.addr, emptyCtxAddr), keyed := after, cached := hit.cached + keyRun := inferKey_closed closed after + selected := by + obtain ⟨key, state⟩ := hit.key_closed closed + have selected := hit.selected + rw [key, state] at selected + rcases selected with full | ⟨full, onlyPolicy, only⟩ + · exact .inl (frame.full.trans full) + · exact .inr ⟨frame.full.trans full, policy.trans onlyPolicy, frame.only.trans only⟩ +} + +/-- The final write is an exact state update, with the policy captured at +entry rather than inferred from any intervening recursive call. -/ +theorem cacheInferResult_eq (policy : Bool) (key : Address × Address) + (type : KExpr .anon) (methods : Methods .anon) (before : TcState .anon) : + RecM.cacheInferResult policy key type methods before = .ok () + (if policy then {before with env := {before.env with + inferOnlyCache := before.env.inferOnlyCache.insert key type}} + else {before with env := {before.env with + inferCache := before.env.inferCache.insert key type}}) := by + cases policy <;> rfl + +/-- Writing a matching result preserves agreement at the written key. -/ +theorem InferenceCacheAgreement.write {before after : TcState .anon} + {key : Address × Address} {type : KExpr .anon} {policy : Bool} {methods : Methods .anon} + (agreement : InferenceCacheAgreement before key type) + (run : RecM.cacheInferResult policy key type methods before = .ok () after) : + InferenceCacheAgreement after key type := by + rw [cacheInferResult_eq] at run + cases policy <;> cases run + · refine ⟨?_, agreement.only⟩ + intro cached found + simpa using found.symm + · refine ⟨agreement.full, ?_⟩ + intro cached found + simpa using found.symm + +/-- Every outcome preserves the observed entries, including failure. -/ +def PreservesInferenceCache (key : Address × Address) (action : TcM .anon α) : Prop := + ∀ before, match action before with + | .ok _ after | .error _ after => InferenceCacheFrame key before after + +theorem PreservesInferenceCache.pure (key : Address × Address) (value : α) : + PreservesInferenceCache key (pure value) := fun before => .refl key before + +theorem PreservesInferenceCache.bind {key : Address × Address} + {action : TcM .anon α} {next : α → TcM .anon γ} + (first : PreservesInferenceCache key action) + (rest : ∀ value, PreservesInferenceCache key (next value)) : + PreservesInferenceCache key (action >>= next) := by + intro before + change (match EStateM.bind action next before with + | .ok _ after | .error _ after => InferenceCacheFrame key before after) + have initial := first before + cases run : action before with + | error err after => + rw [EStateM.bind, run] + simpa only [run] using initial + | ok value middle => + rw [run] at initial + rw [EStateM.bind, run] + simp only + have final := rest value middle + cases result : next value middle <;> rw [result] at final <;> exact initial.trans final + +theorem PreservesInferenceCache.runIntern (key : Address × Address) (action : InternM .anon α) : + PreservesInferenceCache key (TcM.runIntern action) := fun _ => ⟨rfl, rfl, rfl⟩ + +theorem PreservesInferenceCache.inferKey (key : Address × Address) (term : KExpr .anon) : + PreservesInferenceCache key (TcM.inferKey term) := by + intro before + cases run : TcM.inferKey term before + all_goals + unfold TcM.inferKey at run + change EStateM.bind (TcM.ctxAddrForLbr term.lbr) _ before = _ at run + unfold TcM.ctxAddrForLbr at run + change EStateM.bind (fun state => EStateM.bind (get : TcM .anon (TcState .anon)) + _ state) _ before = _ at run + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = + .ok before before from rfl] at run + by_cases fast : (term.lbr == 0 || before.ctx.isEmpty) = true + · rw [if_pos fast] at run + cases run <;> exact ⟨rfl, rfl, rfl⟩ + · rw [if_neg fast] at run + cases cached : before.ctxAddrCache[(before.ctxId, term.lbr)]? <;> + rw [cached] at run <;> cases run <;> exact ⟨rfl, rfl, rfl⟩ + +theorem PreservesInferenceCache.openBinder (key : Address × Address) + (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) (type body : KExpr .anon) : + PreservesInferenceCache key (TcM.openBinder name bi type body) := by + intro before + rw [openBinder_eq] + by_cases room : before.env.nextFVarId.toNat + 1 < UInt64.size + · simp only [room, if_true]; exact ⟨rfl, rfl, rfl⟩ + · simp only [room, if_false]; exact ⟨rfl, rfl, rfl⟩ + +/-- An actual insertion at another key leaves this key's entries and loaded +declarations unchanged, for either validation policy. -/ +theorem PreservesInferenceCache.write_other {key other : Address × Address} + (different : other ≠ key) (policy : Bool) (type : KExpr .anon) (methods : Methods .anon) : + PreservesInferenceCache key (RecM.cacheInferResult policy other type methods) := by + intro before + rw [cacheInferResult_eq] + cases policy <;> refine ⟨?_, ?_, rfl⟩ <;> simp [Std.HashMap.getElem?_insert, different] + +/-- Exact scope cleanup on either outcome, retaining the body's other state +updates. Shared by inference inversion and cache-preservation proofs. -/ +theorem withLctxScope_eq (action : RecM .anon α) + (methods : Methods .anon) (before : TcState .anon) : + (RecM.withLctxScope action).run methods before = + match action.run methods before with + | .ok value after => + .ok value {after with lctx := after.lctx.truncate before.lctx.size} + | .error err after => + .error err {after with lctx := after.lctx.truncate before.lctx.size} := by + unfold RecM.withLctxScope + rw [ReaderT.run_bind] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + unfold EStateM.bind + rw [show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + simp only + unfold tryFinally + change EStateM.map (fun pair : α × PUnit => pair.1) + (tryFinally' (action.run methods) (fun _ => + (modify (fun after : TcState .anon => + {after with lctx := after.lctx.truncate before.lctx.size}) : + TcM .anon PUnit))) before = _ + unfold EStateM.map MonadFinally.tryFinally' EStateM.instMonadFinally + cases run : action.run methods before <;> simp only [run] <;> rfl + +theorem PreservesInferenceCache.withLctxScope {key : Address × Address} + {action : RecM .anon α} {methods : Methods .anon} + (preserved : PreservesInferenceCache key (action.run methods)) : + PreservesInferenceCache key ((RecM.withLctxScope action).run methods) := by + intro before + rw [withLctxScope_eq] + have frame := preserved before + cases run : action.run methods before <;> rw [run] at frame <;> + exact ⟨frame.full, frame.only, frame.constants⟩ + +/-- Changing only local declarations never changes closed cache data. -/ +theorem InferenceCacheFrame.localContext (key : Address × Address) + (before : TcState .anon) (context : LocalContext .anon) : + InferenceCacheFrame key before {before with lctx := context} := ⟨rfl, rfl, rfl⟩ + +/-- Agreement itself is independent of the currently selected policy. -/ +theorem InferenceCacheAgreement.policy {before : TcState .anon} + {key : Address × Address} {type : KExpr .anon} + (agreement : InferenceCacheAgreement before key type) (policy : Bool) : + InferenceCacheAgreement {before with inferOnly := policy} key type := + ⟨agreement.full, agreement.only⟩ + +/-- Cache clearing establishes agreement vacuously, while retaining loaded +declarations for subsequent misses. -/ +theorem InferenceCacheAgreement.clearReductionCaches (before : TcState .anon) + (key : Address × Address) (type : KExpr .anon) : + InferenceCacheAgreement {before with env := before.env.clearReductionCaches} key type := by + constructor <;> intro cached found <;> simp [KEnv.clearReductionCaches] at found + +/-- Exact policy restoration, including after an error from the body. -/ +theorem withInferOnly_eq (action : TcM .anon α) (before : TcState .anon) : + TcM.withInferOnly action before = + match action {before with inferOnly := true} with + | .ok value after => .ok value {after with inferOnly := before.inferOnly} + | .error err after => .error err {after with inferOnly := before.inferOnly} := by + unfold TcM.withInferOnly + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = + .ok before before from rfl] + unfold tryFinally + change EStateM.map (fun pair : α × PUnit => pair.1) + (tryFinally' action (fun _ => + (modify (fun after : TcState .anon => + {after with inferOnly := before.inferOnly}) : TcM .anon PUnit))) + {before with inferOnly := true} = _ + unfold EStateM.map MonadFinally.tryFinally' EStateM.instMonadFinally + cases run : action {before with inferOnly := true} <;> simp only [run] <;> rfl + +theorem PreservesInferenceCache.withInferOnly {key : Address × Address} {action : TcM .anon α} + (preserved : PreservesInferenceCache key action) : + PreservesInferenceCache key (TcM.withInferOnly action) := by + intro before + rw [withInferOnly_eq] + have frame := preserved {before with inferOnly := true} + cases run : action {before with inferOnly := true} <;> rw [run] at frame <;> + exact ⟨frame.full, frame.only, frame.constants⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SortCache.lean b/Ix/Kernel/Verify/Consistency/SortCache.lean new file mode 100644 index 000000000..9119c9da3 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SortCache.lean @@ -0,0 +1,109 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.InferenceCache + +/-! +# Sort inference and cache preservation + +The canonical type of `Sort u` is the production tree for `Sort (succ u)`. +Concrete cache agreement derives both typing and its own preservation through +the actual inference call. Other cache keys and loaded declarations are framed. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- Cached sorts synthesize their canonical type under any active locals. +No intern-table resources are needed when the selected result is cached. -/ +theorem infer_sort_cached_sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} + {before after : TcState .anon} {level : KUniv .anon} {info : ExprInfo .anon} + {methods : Methods .anon} {result : KExpr .anon} + (hit : InferenceCacheHit before (.sort level info)) + (canonical : hit.cached = KExpr.mkSort (KUniv.mkSucc level)) + (accepted : RecM.infer (.sort level info) methods before = .ok result after) : + readScopedExpr? resolve locals result = some (.sort (.succ (readLevel level))) ∧ + TypingClaim.{u,v} entries context (.sort (readLevel level)) (.sort (.succ (readLevel level))) := by + rw [hit.run methods] at accepted + cases accepted + rw [canonical] + exact ⟨by simp, TypingClaim.sort _⟩ + +/-- Both policies return the exact canonical sort tree and retain agreement +at its key. The miss branch establishes this through interning and the real +cache write; the hit branch reads it from the maintained invariant. -/ +theorem infer_sort_cache_agreement {before keyed after : TcState .anon} + {level : KUniv .anon} {info : ExprInfo .anon} {key : Address × Address} + {methods : Methods .anon} {result : KExpr .anon} + (keyRun : TcM.inferKey (.sort level info) before = .ok key keyed) + (agreement : InferenceCacheAgreement keyed key (KExpr.mkSort (KUniv.mkSucc level))) + (coherent : keyed.env.intern.WF) + (faithful : KExpr.KeyCollisionFree fun term => keyed.env.intern.ExprSupport term ∨ + term = KExpr.mkSort (KUniv.mkSucc level)) + (accepted : RecM.infer (.sort level info) methods before = .ok result after) : + result = KExpr.mkSort (KUniv.mkSucc level) ∧ + InferenceCacheAgreement after key (KExpr.mkSort (KUniv.mkSucc level)) := by + rcases observeInferenceCache keyRun with ⟨hit, keyEq, stateEq⟩ | ⟨miss, keyEq, stateEq⟩ + · rw [hit.run methods] at accepted + cases accepted + refine ⟨InferenceCacheAgreement.selected hit ?_, ?_⟩ + · simpa only [keyEq, stateEq] using agreement + · simpa only [stateEq] using agreement + · obtain ⟨state, run, written⟩ := infer_uncached_success_state miss accepted + rw [stateEq] at run + change EStateM.Result.ok + (keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).1 + {keyed with env := {keyed.env with intern := + (keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).2}} = + .ok result state at run + cases run + have canonical := keyed.env.intern.internExpr_eraseMeta coherent faithful + simp only [KExpr.eraseMeta_anon] at canonical + refine ⟨canonical, ?_⟩ + have unchanged : InferenceCacheAgreement + {keyed with env := {keyed.env with intern := + (keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).2}} + key (KExpr.mkSort (KUniv.mkSucc level)) := ⟨agreement.full, agreement.only⟩ + apply unchanged.write (policy := before.inferOnly) (methods := methods) + rw [cacheInferResult_eq] + rw [keyEq, canonical] at written + rw [written] + +/-- A successful sort call preserves every other cache key and all loaded +declarations. This frame is operational and needs no typing or cache +agreement premise, even when the call writes a new result at its own key. -/ +theorem infer_sort_cache_frame {before keyed after : TcState .anon} + {level : KUniv .anon} {info : ExprInfo .anon} {key other : Address × Address} + {methods : Methods .anon} {result : KExpr .anon} + (keyRun : TcM.inferKey (.sort level info) before = .ok key keyed) + (different : key ≠ other) + (accepted : RecM.infer (.sort level info) methods before = .ok result after) : + InferenceCacheFrame other before after := by + have keyFrame := PreservesInferenceCache.inferKey other (.sort level info) before + rw [keyRun] at keyFrame + rcases observeInferenceCache keyRun with ⟨hit, keyEq, stateEq⟩ | ⟨miss, keyEq, stateEq⟩ + · rw [hit.run methods] at accepted + cases accepted + simpa only [stateEq] using keyFrame + · obtain ⟨state, run, written⟩ := infer_uncached_success_state miss accepted + rw [stateEq] at run + change EStateM.Result.ok + (keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).1 + {keyed with env := {keyed.env with intern := + (keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).2}} = + .ok result state at run + cases run + apply keyFrame.trans + rw [keyEq] at written + rw [written] + cases policy : before.inferOnly <;> + refine ⟨?_, ?_, rfl⟩ <;> simp [Std.HashMap.getElem?_insert, different] + +end Ix.Kernel.Consistency diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index c293e6a66..4d49329b4 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -512,9 +512,9 @@ private def repeatedReferenceEnvironment : Ixon.Env := Id.run do /-- The selected entry is populated by real inference, then reused in two different local scopes. Cache keys and closed returned types stay stable. -/ -private def cachedConstantAcrossScopes (source : Ixon.Env × Address) - (arguments : Array (KUniv .anon)) (expected : KExpr .anon) (inferOnly : Bool) : Bool := - let term := KExpr.mkConst (m := .anon) ⟨source.2, ()⟩ arguments +private def cachedInferenceAcrossScopes (term expected : KExpr .anon) + (state : TcState .anon) : Bool := + let inferOnly := state.inferOnly let propType := KExpr.mkSort (m := .anon) .mkZero let action : RecM .anon Bool := do let first ← RecM.inferCall term @@ -534,15 +534,20 @@ private def cachedConstantAcrossScopes (source : Ixon.Env × Address) final.env.inferOnlyCache.size == initial.env.inferOnlyCache.size && final.env.inferCache.isEmpty else initial.env.inferCache[key]?.isSome && final.env.inferCache.size == initial.env.inferCache.size && final.env.inferOnlyCache.isEmpty) - match TcM.runRec action { TcState.newLazyAnon source.1 with inferOnly } with + match TcM.runRec action state with | .ok passed after => passed && after.lctx.size == 0 && after.env.nextFVarId == 2 | .error _ _ => false +private def cachedConstantAcrossScopes (source : Ixon.Env × Address) + (arguments : Array (KUniv .anon)) (expected : KExpr .anon) (inferOnly : Bool) : Bool := + cachedInferenceAcrossScopes (.mkConst ⟨source.2, ()⟩ arguments) expected + { TcState.newLazyAnon source.1 with inferOnly } + /-- A deliberately different result in the ineligible partition makes the selection policy observable. Full mode must compute a checked answer when only that entry exists; inference-only mode must prefer a full result. -/ -private def constantCachePriority (fullHit : Bool) : Bool := - let term := KExpr.mkConst (m := .anon) ⟨polymorphicIdentity.2, ()⟩ #[levelOne] +private def inferenceCachePriority (term knownType : KExpr .anon) + (initial : TcState .anon) (fullHit : Bool) : Bool := let sentinel := KExpr.mkSort (m := .anon) .mkZero let action : RecM .anon Bool := do let expected ← RecM.inferOnlyCall term @@ -552,13 +557,17 @@ private def constantCachePriority (fullHit : Bool) : Bool := inferOnlyCache := state.env.inferOnlyCache.insert key sentinel } } let result ← RecM.inferCall term let state ← get - return result.addr == identityType.addr && result.addr != sentinel.addr && + return result.addr == knownType.addr && result.addr != sentinel.addr && state.env.inferCache[key]?.any (fun cached => cached.addr == expected.addr) && state.env.inferOnlyCache[key]?.any (fun cached => cached.addr == sentinel.addr) - match TcM.runRec action (TcState.newLazyAnon polymorphicIdentity.1) with + match TcM.runRec action initial with | .ok passed _ => passed | .error _ _ => false +private def constantCachePriority (fullHit : Bool) : Bool := + inferenceCachePriority (.mkConst ⟨polymorphicIdentity.2, ()⟩ #[levelOne]) identityType + (TcState.newLazyAnon polymorphicIdentity.1) fullHit + /-- Alternating two instances after their first use must retrieve the type for that instance, while retaining only two closed constant cache entries. -/ private def repeatedConstantInstances : Bool := @@ -603,8 +612,119 @@ private def constantCacheCases : TestSeq := ++ test "constant cache: alternating universe instances retrieve their own cached types" repeatedConstantInstances +/-- Repeated `Sort u` domains in both the declaration and its value exercise +sort hits under fresh local scopes: `fun P Q p q => p`, at Prop and Type. -/ +private def repeatedSortEnvironment : Ixon.Env := Id.run do + let definition : Ixon.Univ → Ix.DefKind → Ixon.Constant := fun level kind => + ⟨.defn ⟨kind, .safe, 0, + .leanAll (.sort 0) (.leanAll (.sort 0) + (.leanAll (.var 1) (.leanAll (.var 1) (.var 3)))), + .leanLam (.sort 0) (.leanLam (.sort 0) + (.leanLam (.var 1) (.leanLam (.var 1) (.var 1))))⟩, #[], #[], #[level]⟩ + let (env, _) := storeConst {} (definition .zero .defn) + let (env, _) := storeConst env (definition (.succ .zero) .opaq) + return env + +/-- Warm a constant, then infer a sort and another instance of the loaded +constant inside a scope. Both successful cleanup and an error from an +inference-only call retain the original entry and the intervening writes. -/ +private def constantCacheThroughInference (inferOnly fail : Bool) : Bool := + let id : KId .anon := ⟨polymorphicIdentity.2, ()⟩ + let term := KExpr.mkConst (m := .anon) id #[levelOne] + let other := KExpr.mkConst (m := .anon) id #[.mkZero] + let propType := KExpr.mkSort (m := .anon) .mkZero + let propIdentity := KExpr.mkAll () () propType (.mkAll () () (.mkVar 0 ()) (.mkVar 1 ())) + let action : RecM .anon Bool := do + let first ← RecM.inferCall term + let key ← TcM.inferKey term + let otherKey ← TcM.inferKey other + let sortKey ← TcM.inferKey propType + let initial ← get + let expectedExit ← try + RecM.withLctxScope do + let _ ← TcM.openBinder () () propType (.mkVar 0 ()) + let _ ← RecM.inferCall propType + let _ ← RecM.inferCall other + if fail then + let _ ← RecM.inferOnlyCall (.mkFVar ⟨99⟩ ()) + return false + return true + catch _ => pure fail + let later ← get + let cache := if inferOnly then later.env.inferOnlyCache else later.env.inferCache + let opposite := if inferOnly then later.env.inferCache else later.env.inferOnlyCache + let reused ← RecM.inferCall term + return expectedExit && first.addr == identityType.addr && reused.addr == first.addr && + key != otherKey && key != sortKey && otherKey != sortKey && + cache[key]?.any (fun cached => cached.addr == first.addr) && + cache[otherKey]?.any (fun cached => cached.addr == propIdentity.addr) && + cache[sortKey]?.any (fun cached => cached.addr == (.mkSort levelOne : KExpr .anon).addr) && + opposite.isEmpty && later.env.consts.size == initial.env.consts.size && + (later.env.get? id).map (·.ty.addr) == (initial.env.get? id).map (·.ty.addr) && + later.lctx.size == 0 && later.env.nextFVarId == 1 && later.inferOnly == inferOnly + match TcM.runRec action { TcState.newLazyAnon polymorphicIdentity.1 with inferOnly } with + | .ok passed _ => passed + | .error _ _ => false + +/-- Populate both partitions, clear them through the production operation, +then re-infer with a loaded source under the selected policy. -/ +private def inferenceAfterClearing (inferOnly : Bool) : Bool := + let id : KId .anon := ⟨polymorphicIdentity.2, ()⟩ + let term := KExpr.mkConst (m := .anon) id #[levelOne] + let propType := KExpr.mkSort (m := .anon) .mkZero + let action : RecM .anon Bool := do + let _ ← RecM.inferOnlyCall term + let _ ← RecM.inferCall term + let _ ← RecM.inferOnlyCall propType + let _ ← RecM.inferCall propType + let key ← TcM.inferKey term + let sortKey ← TcM.inferKey propType + let populated ← get + modify fun state => { state with inferOnly, env := state.env.clearReductionCaches } + let cleared ← get + let constantResult ← RecM.inferCall term + let sortResult ← RecM.inferCall propType + let final ← get + let cache := if inferOnly then final.env.inferOnlyCache else final.env.inferCache + let opposite := if inferOnly then final.env.inferCache else final.env.inferOnlyCache + return populated.env.inferCache.size == 2 && populated.env.inferOnlyCache.size == 2 && + cleared.env.inferCache.isEmpty && cleared.env.inferOnlyCache.isEmpty && + (cleared.env.get? id).isSome && cleared.env.consts.size == populated.env.consts.size && + constantResult.addr == identityType.addr && sortResult.addr == (.mkSort levelOne : KExpr .anon).addr && + cache[key]?.isSome && cache[sortKey]?.isSome && cache.size == 2 && opposite.isEmpty + match TcM.runRec action (TcState.newLazyAnon polymorphicIdentity.1) with + | .ok passed _ => passed + | .error _ _ => false + +private def cacheInvariantCases : TestSeq := + test "cache invariants: repeated sort domains check with persistent caches" + (allSucceeded repeatedSortEnvironment 2 { clearEvery := 0 }) + ++ test "cache invariants: repeated sort domains check with per-item clearing" + (allSucceeded repeatedSortEnvironment 2 { clearEvery := 1 }) + ++ test "cache invariants: full sort results survive two fresh scopes" + (cachedInferenceAcrossScopes (.mkSort .mkZero) (.mkSort levelOne) (TcState.ofEnvAnon {})) + ++ test "cache invariants: inference-only sort results survive two fresh scopes" + (cachedInferenceAcrossScopes (.mkSort .mkZero) (.mkSort levelOne) + { TcState.ofEnvAnon {} with inferOnly := true }) + ++ test "cache invariants: sort inference gives the full result priority" + (inferenceCachePriority (.mkSort .mkZero) (.mkSort levelOne) (TcState.ofEnvAnon {}) true) + ++ test "cache invariants: full sort inference ignores the inference-only entry" + (inferenceCachePriority (.mkSort .mkZero) (.mkSort levelOne) (TcState.ofEnvAnon {}) false) + ++ test "cache invariants: full entries survive other inference and scope cleanup" + (constantCacheThroughInference false false) + ++ test "cache invariants: inference-only entries survive other inference and scope cleanup" + (constantCacheThroughInference true false) + ++ test "cache invariants: full entries and policy survive failed inference-only scope" + (constantCacheThroughInference false true) + ++ test "cache invariants: inference-only entries and policy survive failed scope" + (constantCacheThroughInference true true) + ++ test "cache invariants: clearing both partitions permits fresh full inference" + (inferenceAfterClearing false) + ++ test "cache invariants: clearing both partitions permits fresh inference-only synthesis" + (inferenceAfterClearing true) + public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, - polymorphicApplicationCases, constantCacheCases] + polymorphicApplicationCases, constantCacheCases, cacheInvariantCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 93725fbc4..dac7f0cb6 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -39,6 +39,8 @@ increasing sequence of strongly inaccessible cardinals. - `inferUncached_sort_sound` interprets an actual successful execution of the production sort-inference branch. It proves the model typing postcondition for the returned type, including intern-table reuse. + `infer_sort_cached_sound` derives the same typing when the eligible cached + value is the canonical successor sort. - `instantiateUnivParams_readAnnotated` connects the actual memoized universe substitution walker to model substitution for every readable expression. Simplifying `max` and `imax` may change the returned syntax; the proof @@ -54,7 +56,8 @@ increasing sequence of strongly inaccessible cardinals. Production success supplies the universe-arity check. The premises retain a well-formed model interface, agreement with the actual lazy-loaded declaration, finite interning and level-substitution resources, and (for - `infer`) misses in both cache partitions at the computed key. + `infer`) misses in every eligible cache partition at the computed key. + Full mode imposes no condition on the ignored inference-only partition. - `ModelTyping.no_false` rules out a closed model-typed kernel expression at primitive False when its environment has been admitted by the certified interface and the set-theory assumption has an instance. @@ -90,7 +93,20 @@ increasing sequence of strongly inaccessible cardinals. `InferenceCacheHit.run` proves the exact cache selection and returned state; full results take priority, and inference-only results require that policy. `infer_const_cache_write` establishes substitution agreement for a successful - miss's insertion. General preservation of that agreement remains open. + miss's insertion. +- `InferenceCacheAgreement` records the expected concrete value in both + partitions at one key. Sort inference and inference of an already-loaded + constant preserve this agreement at their own key and leave other keys and + loaded declarations unchanged. The miss proofs use the actual interning or + universe-substitution operation and its final cache write. + `PreservesInferenceCache` composes structural preservation through key + computation, interning, binder opening, unrelated writes, and scope/policy + cleanup, including errors. Cache clearing establishes empty-cache agreement. + `CachedConstantInferenceSupport.transport` reuses a closed constant witness + after such a frame; `BinderInference.sortOfAgreement` constructs a sort leaf + from maintained agreement. Initial agreement and finite execution resources + remain premises. General preservation through lazy loading, environment + extension, and composite inference remains open. - `checkEnvAnon_atomic_preserves_model` connects a supported production environment run to model extension. `checkEnvAnon_atomic_no_false` excludes a declaration at an axiom type interpreted as empty, including False. @@ -130,6 +146,7 @@ def idProp (P : Prop) (p : P) : P := p def useId (P : Prop) (p : P) : P := idProp P p def applyProp (P Q : Prop) (f : P → Q) (p : P) : Q := f p def usePoly (P : Prop) (p : P) : P := ident.{0} P p +def chooseLeft (P Q : Prop) (p : P) (q : Q) : P := p axiom T.{u} : Sort u axiom f.{u} : T.{u} → T.{u} def useF (x : T.{1}) : T.{1} := f.{1} x @@ -140,7 +157,10 @@ def useF (x : T.{1}) : T.{1} := f.{1} x - Every source key occurs in the `buildAnonWork` result, and every work item represents an axiom or a definition. Lookup, routing, and reset witnesses identify the checked `KConst`. -- Sort, application, forall, and lambda nodes in the inference witnesses miss both cache partitions. +- Application, forall, and lambda nodes in the inference witnesses miss every + eligible cache partition. Full mode may have a populated inference-only + partition. Sort nodes use a miss or a hit equal to the canonical successor + sort; a maintained agreement can construct that leaf's cache observation. Local-variable hits must match the current production declaration type. Constant nodes use either the existing miss rule or a selected cache hit. A hit checks the loaded declaration, universe arity, finite level resources, @@ -151,8 +171,15 @@ def useF (x : T.{1}) : T.{1} := f.{1} x the actual post-lookup state. The occurrence annotations on the declared type agree with the substituted entry's annotations. These are structural data checks; successful inference derives typing, scope, and references. - Ordinary aliases retain the simpler empty-substitution path. Sort inference - retains finite interning coherence and address-faithfulness premises. + Ordinary aliases retain the simpler empty-substitution path. Sort misses + retain finite interning coherence and address-faithfulness premises. +- Cache agreement covers both partitions so it survives policy changes. + Closed constant witnesses can be transported through composed frames that + preserve their key's entries and the loaded-constant map. Actual sort and + already-loaded constant inference provide frames for other keys. These + results reduce repeated witnesses; they do not yet derive initial agreement + or preservation through lazy loading, environment extension, or the entire + recursive inference tree. - Binder definitions supply finite inference trees for both the value and its separately checked declared type, exact source readings, closed annotated syntax, and references to the preceding interface. Recursive calls use the actual @@ -190,7 +217,7 @@ Callers must establish these operational and representation witnesses for the run. They supply no typing or checker-soundness premise. The proof extracts the validation, type-inference, theorem-guard, value-inference, and conversion steps from public success, then derives body typing to extend -the preceding model. Automatic witness construction, broader application and +the preceding model. General automatic witness construction, broader application and lambda paths, lets, inductives, coordinated blocks, and other conversion paths remain outside the fragment. Polymorphic constant inference is composed into declaration admission and model extension for the monomorphic specializations described @@ -246,7 +273,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 127 exact theorem boundaries. The production +The consistency target checks 161 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -280,6 +307,10 @@ types and bodies, including per-item cache clearing; full and inference-only reuse across fresh local scopes; monomorphic and simplifying substitutions; partition priority; and alternating universe instances. Deliberately different values in an ineligible partition make the selection tests observable. +Cache-preservation regressions add repeated sort domains, sort reuse and +partition priority, preservation through intervening sort and loaded-constant +inference, successful and failed scope cleanup, policy restoration, and +fresh inference after clearing both partitions. ## Certified host adapters @@ -308,6 +339,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Polymorphic inference and universe substitution | [`Consistency/Constant.lean`](../Ix/Kernel/Verify/Consistency/Constant.lean), [`InstUniv.lean`](../Ix/Kernel/Verify/Consistency/InstUniv.lean), [`Model/LevelCongruence.lean`](../Ix/Theory/Model/LevelCongruence.lean) | | Polymorphic calls inside binders | [`Consistency/ScopedConstant.lean`](../Ix/Kernel/Verify/Consistency/ScopedConstant.lean), [`ScopedInstUniv.lean`](../Ix/Kernel/Verify/Consistency/ScopedInstUniv.lean) | | Constant cache selection, writes, and typing | [`Consistency/ConstantCache.lean`](../Ix/Kernel/Verify/Consistency/ConstantCache.lean) | +| Cache invariants and sort cache typing | [`Consistency/InferenceCache.lean`](../Ix/Kernel/Verify/Consistency/InferenceCache.lean), [`SortCache.lean`](../Ix/Kernel/Verify/Consistency/SortCache.lean) | | Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`Application.lean`](../Ix/Kernel/Verify/Consistency/Application.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | | Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | diff --git a/docs/theory.md b/docs/theory.md index 73a5a39c9..5f0808c89 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -28,8 +28,13 @@ Binder inference uses the declared type's separate formation check to turn or admitted polymorphic constants derive their type's validity from the context or dependency model, then check arguments and substitute the dependent result. Constant cache hits derive the same typing from concrete agreement with pure -universe substitution of a loaded declaration. Cache selection and successful -miss writes are proved; general cache maintenance remains a separate obligation. +universe substitution of a loaded declaration; sort hits use the canonical +successor sort. Sort and already-loaded constant inference preserve agreement +at their cache key and retain other entries. Structural preservation composes +through binder opening, interning, unrelated writes, and scope/policy cleanup, +including errors. It transports closed constant witnesses, and maintained +agreement constructs sort leaves. Initial agreement, lazy loading, environment +extension, and preservation through composite inference remain obligations. Full checker consistency and compiler/backend refinement remain open. From cc67fffa597878c948e1fb129883803cfe8a48d6 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 16:11:28 -0400 Subject: [PATCH 13/63] Carry cache preservation through recursive inference --- Ix/Kernel/Verify/Consistency.lean | 10 +- Ix/Kernel/Verify/Consistency/Application.lean | 16 +- Ix/Kernel/Verify/Consistency/Audit.lean | 15 +- .../Verify/Consistency/BinderInference.lean | 48 ++- .../Verify/Consistency/RecursiveCache.lean | 368 ++++++++++++++++++ Tests/Ix/Kernel/Consistency.lean | 87 ++++- docs/kernel-verification.md | 31 +- docs/theory.md | 8 +- 8 files changed, 559 insertions(+), 24 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/RecursiveCache.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index f7e00969d..94f9f3ef5 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -20,6 +20,7 @@ import Ix.Kernel.Verify.Consistency.Context import Ix.Kernel.Verify.Consistency.BinderOpening import Ix.Kernel.Verify.Consistency.Application import Ix.Kernel.Verify.Consistency.BinderInference +import Ix.Kernel.Verify.Consistency.RecursiveCache import Ix.Kernel.Verify.Consistency.Production import Ix.Kernel.Verify.Consistency.Environment import Ix.Kernel.Verify.Consistency.Audit @@ -54,7 +55,12 @@ Concrete cache agreement is preserved by sort and already-loaded constant inference. Cache frames compose through interning, key computation, binder opening, unrelated writes, and scope/policy cleanup, including errors. These frames transport closed constant witnesses; maintained agreement also -constructs sort leaves. Initial agreement, finite execution resources, lazy -loading, and preservation through composite inference remain explicit obligations. +constructs sort leaves. A finite InferenceCacheTrace computes the keys written +by recursive applications, dependent functions, and full-mode lambdas, including +domain validation and outer cache writes. Successful inference preserves entries +outside that footprint, loaded declarations, and checking policy. This derives +later constant witnesses and sort leaves without new cache-hit observations. +Initial agreement, finite execution resources, trace construction, lazy loading, +and preservation for keys inside the footprint remain explicit obligations. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Application.lean b/Ix/Kernel/Verify/Consistency/Application.lean index acdee2e99..9af94b268 100644 --- a/Ix/Kernel/Verify/Consistency/Application.lean +++ b/Ix/Kernel/Verify/Consistency/Application.lean @@ -214,12 +214,14 @@ structure ApplicationInferenceTrace (fuel : Nat) (before : TcState .anon) /-- Inverting the successful production branch reaches its exact interned codomain substitution after the real argument check. -/ -theorem ApplicationInferenceTrace.output {fuel : Nat} {before after : TcState .anon} +theorem ApplicationInferenceTrace.output_state {fuel : Nat} {before after : TcState .anon} {fn arg result : KExpr .anon} {info : ExprInfo .anon} (trace : ApplicationInferenceTrace fuel before fn arg) (accepted : RecM.inferUncached RecM.inferCall false (.app fn arg info) (methodsN (fuel + 1)) before = .ok result after) : - result = (subst trace.codomain arg 0 trace.comparedState.env.intern).1 := by + result = (subst trace.codomain arg 0 trace.comparedState.env.intern).1 ∧ + after = {trace.comparedState with env := {trace.comparedState.env with + intern := (subst trace.codomain arg 0 trace.comparedState.env.intern).2}} := by change (RecM.inferUncached RecM.inferCall false (.app fn arg info)).run (methodsN (fuel + 1)) before = .ok result after at accepted unfold RecM.inferUncached at accepted @@ -234,6 +236,14 @@ theorem ApplicationInferenceTrace.output {fuel : Nat} {before after : TcState .a _ trace.argumentState = _ at accepted rw [EStateM.bind, trace.compareRun] at accepted cases accepted - rfl + exact ⟨rfl, rfl⟩ + +theorem ApplicationInferenceTrace.output {fuel : Nat} {before after : TcState .anon} + {fn arg result : KExpr .anon} {info : ExprInfo .anon} + (trace : ApplicationInferenceTrace fuel before fn arg) + (accepted : RecM.inferUncached RecM.inferCall false (.app fn arg info) + (methodsN (fuel + 1)) before = .ok result after) : + result = (subst trace.codomain arg 0 trace.comparedState.env.intern).1 := + (trace.output_state accepted).1 end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 18977e082..3af2d24df 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -6,6 +6,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Verify.Consistency.Infer import Ix.Kernel.Verify.Consistency.Constant import Ix.Kernel.Verify.Consistency.Environment +import Ix.Kernel.Verify.Consistency.RecursiveCache import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -79,12 +80,22 @@ private def cacheMapRoots : Array Lean.Name := #[ ] private def cacheKeyRoots : Array Lean.Name := #[ - ``inferKey_closed, ``InferenceCacheHit.key_closed, ``observeInferenceCache, + ``inferKey_closed, ``inferKey_policy, ``InferenceCacheHit.key_closed, ``observeInferenceCache, ``InferenceCacheAgreement.selected, ``InferenceCacheHit.transport, ``PreservesInferenceCache.inferKey, ``PreservesInferenceCache.openBinder, ``withLctxScope_eq, ``PreservesInferenceCache.withLctxScope ] +private def recursiveCacheRoots : Array Lean.Name := #[ + ``ApplicationInferenceTrace.output_state, ``ForallInferenceTrace.output_state, + ``LambdaInferenceTrace.output_state, ``isDefEq_hash_state, ``isDefEq_hash_frame, + ``InferenceCacheTrace.writes, ``InferenceCacheTrace.sortOfKey, + ``InferenceCacheTrace.fvarOfKey, ``InferenceCacheTrace.constOfKey, + ``InferenceCacheTrace.frame, ``InferenceCacheTrace.agreement, + ``InferenceCacheHit.afterInference, ``CachedConstantInferenceSupport.afterInference, + ``CachedConstantInferenceSupport.sound_after_inference, ``BinderInference.sortAfterInference +] + private def productionRoots : Array Lean.Name := #[ ``StandalonePrefix.member_success, ``definition_body_trace, ``AtomicDefinitionRun.sound, ``AtomicDefinitionRun.no_self_alias, @@ -184,7 +195,7 @@ def roots : Array RootAllowance := #[ }) ++ (binderWalkerRoots ++ cacheKeyRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction -}) ++ (atomicRoots ++ instantiationRoots).map (fun root => { +}) ++ (atomicRoots ++ instantiationRoots ++ recursiveCacheRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) ++ productionRoots.map (fun root => { diff --git a/Ix/Kernel/Verify/Consistency/BinderInference.lean b/Ix/Kernel/Verify/Consistency/BinderInference.lean index 7d503b5f1..6ca510094 100644 --- a/Ix/Kernel/Verify/Consistency/BinderInference.lean +++ b/Ix/Kernel/Verify/Consistency/BinderInference.lean @@ -63,14 +63,15 @@ theorem inferUncached_monomorphic_const_scoped {β : Type u} private theorem withLctxScope_success {action : RecM .anon α} {methods : Methods .anon} {before after : TcState .anon} {result : α} (accepted : (RecM.withLctxScope action).run methods before = .ok result after) : - ∃ state, action.run methods before = .ok result state := by + ∃ state, action.run methods before = .ok result state ∧ + after = {state with lctx := state.lctx.truncate before.lctx.size} := by rw [withLctxScope_eq] at accepted cases run : action.run methods before with | error err state => rw [run] at accepted; contradiction | ok value state => rw [run] at accepted cases accepted - exact ⟨state, rfl⟩ + exact ⟨state, rfl, rfl⟩ /-- The exact recursive calls and opening prefix of a forall branch. Both sort exposures take the production syntactic fast path. -/ @@ -95,14 +96,18 @@ structure ForallInferenceTrace (fuel : Nat) (before : TcState .anon) /-- Inversion reaches the actual final interning operation after both recursive calls, sort exposures, and binder opening. -/ -theorem ForallInferenceTrace.output {fuel : Nat} {before after : TcState .anon} +theorem ForallInferenceTrace.output_state {fuel : Nat} {before after : TcState .anon} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body result : KExpr .anon} {info : ExprInfo .anon} {inferOnly : Bool} (trace : ForallInferenceTrace fuel before name bi domain body) (accepted : RecM.inferUncached RecM.inferCall inferOnly (.all name bi domain body info) (methodsN (fuel + 1)) before = .ok result after) : result = (trace.bodyState.env.intern.internExpr - (KExpr.mkSort (KUniv.mkIMax trace.domainLevel trace.bodyLevel))).1 := by + (KExpr.mkSort (KUniv.mkIMax trace.domainLevel trace.bodyLevel))).1 ∧ + after = {trace.bodyState with + env := {trace.bodyState.env with intern := (trace.bodyState.env.intern.internExpr + (KExpr.mkSort (KUniv.mkIMax trace.domainLevel trace.bodyLevel))).2} + lctx := trace.bodyState.lctx.truncate trace.domainState.lctx.size} := by change (RecM.inferUncached RecM.inferCall inferOnly (.all name bi domain body info)).run (methodsN (fuel + 1)) before = .ok result after at accepted unfold RecM.inferUncached at accepted @@ -110,7 +115,7 @@ theorem ForallInferenceTrace.output {fuel : Nat} {before after : TcState .anon} change EStateM.bind (RecM.infer domain (methodsN fuel)) _ before = _ at accepted rw [EStateM.bind, trace.domainRun] at accepted change (RecM.withLctxScope _).run (methodsN (fuel + 1)) trace.domainState = _ at accepted - obtain ⟨scopedState, accepted⟩ := withLctxScope_success accepted + obtain ⟨scopedState, accepted, cleanup⟩ := withLctxScope_success accepted simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted change EStateM.bind (TcM.openBinder name bi domain body) _ trace.domainState = .ok result scopedState at accepted @@ -121,7 +126,17 @@ theorem ForallInferenceTrace.output {fuel : Nat} {before after : TcState .anon} _ trace.openedState = .ok result scopedState at accepted rw [EStateM.bind, bodyRun] at accepted cases accepted - rfl + exact ⟨rfl, cleanup⟩ + +theorem ForallInferenceTrace.output {fuel : Nat} {before after : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body result : KExpr .anon} {info : ExprInfo .anon} {inferOnly : Bool} + (trace : ForallInferenceTrace fuel before name bi domain body) + (accepted : RecM.inferUncached RecM.inferCall inferOnly (.all name bi domain body info) + (methodsN (fuel + 1)) before = .ok result after) : + result = (trace.bodyState.env.intern.internExpr + (KExpr.mkSort (KUniv.mkIMax trace.domainLevel trace.bodyLevel))).1 := + (trace.output_state accepted).1 /-- Full-mode lambda inference validates its domain, opens the binder, and infers the body. The current fragment takes the unchanged cheap-beta path. -/ @@ -150,13 +165,17 @@ def LambdaInferenceTrace.abstracted {fuel : Nat} {before : TcState .anon} /-- The returned lambda type is the actual abstracted body type, wrapped in the production Pi constructor and passed through the final intern table. -/ -theorem LambdaInferenceTrace.output {fuel : Nat} {before after : TcState .anon} +theorem LambdaInferenceTrace.output_state {fuel : Nat} {before after : TcState .anon} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body result : KExpr .anon} {info : ExprInfo .anon} (trace : LambdaInferenceTrace fuel before name bi domain body) (accepted : RecM.inferUncached RecM.inferCall false (.lam name bi domain body info) (methodsN (fuel + 1)) before = .ok result after) : - result = (trace.abstracted.2.internExpr (KExpr.mkAll () () domain trace.abstracted.1)).1 := by + result = (trace.abstracted.2.internExpr (KExpr.mkAll () () domain trace.abstracted.1)).1 ∧ + after = {trace.bodyState with + env := {trace.bodyState.env with + intern := (trace.abstracted.2.internExpr (KExpr.mkAll () () domain trace.abstracted.1)).2} + lctx := trace.bodyState.lctx.truncate trace.domainState.lctx.size} := by change (RecM.inferUncached RecM.inferCall false (.lam name bi domain body info)).run (methodsN (fuel + 1)) before = .ok result after at accepted unfold RecM.inferUncached at accepted @@ -164,7 +183,7 @@ theorem LambdaInferenceTrace.output {fuel : Nat} {before after : TcState .anon} change EStateM.bind (RecM.infer domain (methodsN fuel)) _ before = _ at accepted rw [EStateM.bind, trace.domainRun] at accepted change (RecM.withLctxScope _).run (methodsN (fuel + 1)) trace.domainState = _ at accepted - obtain ⟨scopedState, accepted⟩ := withLctxScope_success accepted + obtain ⟨scopedState, accepted, cleanup⟩ := withLctxScope_success accepted simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted change EStateM.bind (TcM.openBinder name bi domain body) _ trace.domainState = .ok result scopedState at accepted @@ -176,7 +195,16 @@ theorem LambdaInferenceTrace.output {fuel : Nat} {before after : TcState .anon} rw [EStateM.bind, bodyRun] at accepted simp only [cheapBetaReduce, trace.betaUnchanged] at accepted cases accepted - rfl + exact ⟨rfl, cleanup⟩ + +theorem LambdaInferenceTrace.output {fuel : Nat} {before after : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body result : KExpr .anon} {info : ExprInfo .anon} + (trace : LambdaInferenceTrace fuel before name bi domain body) + (accepted : RecM.inferUncached RecM.inferCall false (.lam name bi domain body info) + (methodsN (fuel + 1)) before = .ok result after) : + result = (trace.abstracted.2.internExpr (KExpr.mkAll () () domain trace.abstracted.1)).1 := + (trace.output_state accepted).1 /-- Structural and operational support for a finite production inference tree. The source reading and local-context agreement are inputs to soundness, diff --git a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean new file mode 100644 index 000000000..d42bc9fe1 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean @@ -0,0 +1,368 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BinderInference + +/-! +# Cache preservation through recursive inference + +Finite operational trees follow the actual smaller method table, recording +the keys written by successful misses. An entry outside those writes and its +loaded declaration survive the entire inference, so a closed cached witness +can be reused afterward. No semantic typing or per-call cache frame is an input. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- Key computation may memoize a context digest but cannot change policy. -/ +theorem inferKey_policy {term : KExpr .anon} {before after : TcState .anon} + {key : Address × Address} (run : TcM.inferKey term before = .ok key after) : + after.inferOnly = before.inferOnly := by + unfold TcM.inferKey at run + change EStateM.bind (TcM.ctxAddrForLbr term.lbr) _ before = _ at run + unfold TcM.ctxAddrForLbr at run + change EStateM.bind (fun state => EStateM.bind (get : TcM .anon (TcState .anon)) + _ state) _ before = _ at run + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = + .ok before before from rfl] at run + by_cases fast : (term.lbr == 0 || before.ctx.isEmpty) = true + · rw [if_pos fast] at run + cases run; rfl + · rw [if_neg fast] at run + cases cached : before.ctxAddrCache[(before.ctxId, term.lbr)]? <;> + rw [cached] at run <;> cases run <;> rfl + +/-- Hash conversion executes only tracing and its optional statistics update. +The exact state includes that counter update; it is not assumed unchanged. -/ +theorem isDefEq_hash_state {left right : KExpr .anon} + (equal : (left.addr == right.addr) = true) + (methods : Methods .anon) (before : TcState .anon) : + RecM.isDefEq left right methods before = .ok true + (if before.stats then {before with deqCalls := before.deqCalls + 1} else before) := by + have traced : TcM.stepTrace "deq" + (fun _ => s!"{TcM.addr8 left.addr} ~ {TcM.addr8 right.addr}") before = .ok () before := by + unfold TcM.stepTrace + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + dsimp only + split <;> rfl + have bumped : TcM.bumpStats + (fun state : TcState .anon => {state with deqCalls := state.deqCalls + 1}) before = + .ok () (if before.stats then {before with deqCalls := before.deqCalls + 1} else before) := by + unfold TcM.bumpStats + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + dsimp only + by_cases enabled : before.stats = true + · rw [if_pos enabled, if_pos enabled]; rfl + · rw [if_neg enabled, if_neg enabled]; rfl + change (RecM.isDefEq left right).run methods before = _ + unfold RecM.isDefEq + rw [ReaderT.run_bind] + change EStateM.bind (TcM.stepTrace "deq" + (fun _ => s!"{TcM.addr8 left.addr} ~ {TcM.addr8 right.addr}")) _ before = _ + rw [EStateM.bind, traced] + simp only [ReaderT.run_bind] + change EStateM.bind (TcM.bumpStats + (fun state : TcState .anon => {state with deqCalls := state.deqCalls + 1})) _ before = _ + rw [EStateM.bind, bumped] + simp only [equal, if_true] + rfl + +/-- The supported conversion path retains inference caches, loaded sources, +and the caller's checking policy even when statistics are enabled. -/ +theorem isDefEq_hash_frame {left right : KExpr .anon} + {methods : Methods .anon} {before after : TcState .anon} + (equal : (left.addr == right.addr) = true) + (run : RecM.isDefEq left right methods before = .ok true after) + (key : Address × Address) : + InferenceCacheFrame key before after ∧ after.inferOnly = before.inferOnly := by + rw [isDefEq_hash_state equal] at run + split at run <;> cases run <;> exact ⟨⟨rfl, rfl, rfl⟩, rfl⟩ + +private theorem openBinder_policy {name : Mode.anon.F Name} + {bi : Mode.anon.F Lean.BinderInfo} {domain body opened : KExpr .anon} + {fresh : FVarId} {before after : TcState .anon} + (run : TcM.openBinder name bi domain body before = .ok (opened, fresh) after) : + after.inferOnly = before.inferOnly := by + rw [openBinder_eq] at run + split at run + · cases run; rfl + · contradiction + +private theorem openBinder_frame {name : Mode.anon.F Name} + {bi : Mode.anon.F Lean.BinderInfo} {domain body opened : KExpr .anon} + {fresh : FVarId} {before after : TcState .anon} (key : Address × Address) + (run : TcM.openBinder name bi domain body before = .ok (opened, fresh) after) : + InferenceCacheFrame key before after := by + have frame := PreservesInferenceCache.openBinder key name bi domain body before + rw [run] at frame + exact frame + +private theorem infer_miss_frame {term result : KExpr .anon} + {methods : Methods .anon} {before after : TcState .anon} {key : Address × Address} + (miss : UncachedInference before term) (different : miss.key ≠ key) + (accepted : RecM.infer term methods before = .ok result after) + (uncached : ∀ middle, + RecM.inferUncached RecM.inferCall before.inferOnly term methods miss.keyed = .ok result middle → + InferenceCacheFrame key miss.keyed middle ∧ middle.inferOnly = miss.keyed.inferOnly) : + InferenceCacheFrame key before after ∧ after.inferOnly = before.inferOnly := by + obtain ⟨middle, run, written⟩ := infer_uncached_success_state miss accepted + obtain ⟨bodyFrame, bodyPolicy⟩ := uncached middle run + have keyFrame := PreservesInferenceCache.inferKey key term before + rw [miss.keyRun] at keyFrame + have writeRun : RecM.cacheInferResult before.inferOnly miss.key result methods middle = + .ok () after := by + rw [cacheInferResult_eq, written] + have tail := PreservesInferenceCache.write_other different before.inferOnly result methods middle + rw [writeRun] at tail + have policy : after.inferOnly = middle.inferOnly := by + rw [written] + cases before.inferOnly <;> rfl + exact ⟨keyFrame.trans (bodyFrame.trans tail), + policy.trans (bodyPolicy.trans (inferKey_policy miss.keyRun))⟩ + +/-- Operational support for the successful recursive fragment. Hits write +nothing, including repeated uses of the watched entry. Misses record their +actual key; constant misses require a loaded source and finite walker resources. +The lambda domain call is included even though semantic checking can omit its +typing subtree once the declared type's formation has been established. -/ +inductive InferenceCacheTrace : Nat → TcState .anon → KExpr .anon → Type + | hit {fuel before term} (hit : InferenceCacheHit before term) : + InferenceCacheTrace fuel before term + | sort {fuel before level info} (miss : UncachedInference before (.sort level info)) : + InferenceCacheTrace fuel before (.sort level info) + | fvar {fuel before id name info} (miss : UncachedInference before (.fvar id name info)) : + InferenceCacheTrace fuel before (.fvar id name info) + | const {fuel before id arguments info} + (miss : UncachedInference before (.const id arguments info)) + (concrete : KConst .anon) (loaded : miss.keyed.env.get? id = some concrete) + (resources : UniverseInstantiationSupport miss.keyed concrete.ty arguments) : + InferenceCacheTrace fuel before (.const id arguments info) + | app {fuel before fn arg info} (full : before.inferOnly = false) + (miss : UncachedInference before (.app fn arg info)) + (trace : ApplicationInferenceTrace fuel miss.keyed fn arg) + (hashPath : (trace.argumentType.addr == trace.domain.addr) = true) + (functionTree : InferenceCacheTrace fuel miss.keyed fn) + (argumentTree : InferenceCacheTrace fuel trace.functionState arg) : + InferenceCacheTrace (fuel + 1) before (.app fn arg info) + | forallE {fuel before name bi domain body info} + (miss : UncachedInference before (.all name bi domain body info)) + (trace : ForallInferenceTrace fuel miss.keyed name bi domain body) + (domainTree : InferenceCacheTrace fuel miss.keyed domain) + (bodyTree : InferenceCacheTrace fuel trace.openedState trace.opened) : + InferenceCacheTrace (fuel + 1) before (.all name bi domain body info) + | lam {fuel before name bi domain body info} (full : before.inferOnly = false) + (miss : UncachedInference before (.lam name bi domain body info)) + (trace : LambdaInferenceTrace fuel miss.keyed name bi domain body) + (domainTree : InferenceCacheTrace fuel miss.keyed domain) + (bodyTree : InferenceCacheTrace fuel trace.openedState trace.opened) : + InferenceCacheTrace (fuel + 1) before (.lam name bi domain body info) + +/-- The finite write footprint is computed from the operational tree. Cache +hits contribute no key; recursive calls and each outer insertion are included. -/ +def InferenceCacheTrace.writes {fuel : Nat} {before : TcState .anon} {term : KExpr .anon} : + InferenceCacheTrace fuel before term → List (Address × Address) + | .hit _ => [] + | .sort miss | .fvar miss | .const miss .. => [miss.key] + | .app _ miss _ _ first second | .forallE miss _ first second | .lam _ miss _ first second => + miss.key :: (first.writes ++ second.writes) + +/-- Leaf construction inspects the real cache policy; callers need not +provide a separate hit/miss observation for a sort. -/ +def InferenceCacheTrace.sortOfKey {fuel : Nat} {before keyed : TcState .anon} + {level : KUniv .anon} {info : ExprInfo .anon} {key : Address × Address} + (keyRun : TcM.inferKey (.sort level info) before = .ok key keyed) : + InferenceCacheTrace fuel before (.sort level info) := by + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ + · exact .hit hit + · exact .sort miss + +def InferenceCacheTrace.fvarOfKey {fuel : Nat} {before keyed : TcState .anon} + {id : FVarId} {name : Mode.anon.F Name} {info : ExprInfo .anon} {key : Address × Address} + (keyRun : TcM.inferKey (.fvar id name info) before = .ok key keyed) : + InferenceCacheTrace fuel before (.fvar id name info) := by + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ + · exact .hit hit + · exact .fvar miss + +def InferenceCacheTrace.constOfKey {fuel : Nat} {before keyed : TcState .anon} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {key : Address × Address} {concrete : KConst .anon} + (keyRun : TcM.inferKey (.const id arguments info) before = .ok key keyed) + (loaded : keyed.env.get? id = some concrete) + (resources : UniverseInstantiationSupport keyed concrete.ty arguments) : + InferenceCacheTrace fuel before (.const id arguments info) := by + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, _, stateEq⟩ + · exact .hit hit + · exact .const miss concrete (by simpa only [stateEq] using loaded) + (by simpa only [stateEq] using resources) + +/-- Every successful call in the finite tree preserves entries outside its +computed write footprint and retains the loaded declarations and policy. +The proof follows the recursive calls, then their real outer cache insertion. -/ +theorem InferenceCacheTrace.frame {fuel : Nat} {before after : TcState .anon} + {term result : KExpr .anon} (tree : InferenceCacheTrace fuel before term) + {key : Address × Address} (outside : key ∉ tree.writes) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + InferenceCacheFrame key before after ∧ after.inferOnly = before.inferOnly := by + induction tree generalizing result after with + | @hit fuel before term hit => + rw [hit.run] at accepted + cases accepted + have frame := PreservesInferenceCache.inferKey key term before + rw [hit.keyRun] at frame + exact ⟨frame, inferKey_policy hit.keyRun⟩ + | @sort fuel before level info miss => + simp only [writes, List.mem_singleton] at outside + apply infer_miss_frame miss (Ne.symm outside) accepted + intro middle run + change EStateM.Result.ok + (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).1 + {miss.keyed with env := {miss.keyed.env with intern := + (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).2}} = + .ok result middle at run + cases run + exact ⟨⟨rfl, rfl, rfl⟩, rfl⟩ + | @fvar fuel before id name info miss => + simp only [writes, List.mem_singleton] at outside + apply infer_miss_frame miss (Ne.symm outside) accepted + intro middle run + change (RecM.inferUncached RecM.inferCall before.inferOnly (.fvar id name info)).run + (methodsN fuel) miss.keyed = _ at run + unfold RecM.inferUncached at run + simp only [ReaderT.run_bind] at run + change EStateM.bind (get : TcM .anon (TcState .anon)) _ miss.keyed = _ at run + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) miss.keyed = + .ok miss.keyed miss.keyed from rfl] at run + dsimp only at run + split at run + · cases run; exact ⟨.refl key _, rfl⟩ + · contradiction + | const miss concrete loaded resources => + simp only [writes, List.mem_singleton] at outside + apply infer_miss_frame miss (Ne.symm outside) accepted + intro middle run + obtain ⟨actual, foundState, got, _, instantiated⟩ := inferUncached_const_instantiation run + rw [getConst_loaded loaded] at got + cases got + have post := TcM.instantiateUnivParams_wf resources.faithful + (fun _ h => Or.inr h) ⟨resources.coherent, fun _ h => Or.inl h⟩ + rw [instantiated] at post + rw [post.2.2.1] + exact ⟨⟨rfl, rfl, rfl⟩, rfl⟩ + | app full miss trace hashPath functionTree argumentTree functionIH argumentIH => + simp only [writes, List.mem_cons, List.mem_append, not_or] at outside + apply infer_miss_frame miss (Ne.symm outside.1) accepted + intro middle run + rw [full] at run + obtain ⟨functionFrame, functionPolicy⟩ := functionIH outside.2.1 trace.functionRun + obtain ⟨argumentFrame, argumentPolicy⟩ := argumentIH outside.2.2 trace.argumentRun + obtain ⟨comparisonFrame, comparisonPolicy⟩ := isDefEq_hash_frame hashPath trace.compareRun key + have state := (trace.output_state run).2 + rw [state] + exact ⟨(functionFrame.trans (argumentFrame.trans comparisonFrame)).trans ⟨rfl, rfl, rfl⟩, + comparisonPolicy.trans (argumentPolicy.trans functionPolicy)⟩ + | forallE miss trace domainTree bodyTree domainIH bodyIH => + simp only [writes, List.mem_cons, List.mem_append, not_or] at outside + apply infer_miss_frame miss (Ne.symm outside.1) accepted + intro middle run + obtain ⟨domainFrame, domainPolicy⟩ := domainIH outside.2.1 trace.domainRun + obtain ⟨bodyFrame, bodyPolicy⟩ := bodyIH outside.2.2 trace.bodyRun + have opening := openBinder_frame key trace.openRun + have state := (trace.output_state run).2 + rw [state] + exact ⟨(domainFrame.trans (opening.trans bodyFrame)).trans ⟨rfl, rfl, rfl⟩, + bodyPolicy.trans ((openBinder_policy trace.openRun).trans domainPolicy)⟩ + | lam full miss trace domainTree bodyTree domainIH bodyIH => + simp only [writes, List.mem_cons, List.mem_append, not_or] at outside + apply infer_miss_frame miss (Ne.symm outside.1) accepted + intro middle run + rw [full] at run + obtain ⟨domainFrame, domainPolicy⟩ := domainIH outside.2.1 trace.domainRun + obtain ⟨bodyFrame, bodyPolicy⟩ := bodyIH outside.2.2 trace.bodyRun + have opening := openBinder_frame key trace.openRun + have state := (trace.output_state run).2 + rw [state] + exact ⟨(domainFrame.trans (opening.trans bodyFrame)).trans ⟨rfl, rfl, rfl⟩, + bodyPolicy.trans ((openBinder_policy trace.openRun).trans domainPolicy)⟩ + +/-- Concrete agreement at an unwritten key is retained by the entire tree. -/ +theorem InferenceCacheTrace.agreement {fuel : Nat} {before after : TcState .anon} + {term result expected : KExpr .anon} (tree : InferenceCacheTrace fuel before term) + {key : Address × Address} (outside : key ∉ tree.writes) + (agreement : InferenceCacheAgreement before key expected) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + InferenceCacheAgreement after key expected := + agreement.frame (tree.frame outside accepted).1 + +/-- Reconstruct the later selected hit from the earlier closed hit and the +whole recursive call, with no additional cache or policy observation. -/ +def InferenceCacheHit.afterInference {fuel : Nat} {before after : TcState .anon} + {cachedTerm term result : KExpr .anon} (hit : InferenceCacheHit before cachedTerm) + (closed : cachedTerm.lbr = 0) (tree : InferenceCacheTrace fuel before term) + (outside : (cachedTerm.addr, emptyCtxAddr) ∉ tree.writes) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + InferenceCacheHit after cachedTerm := + hit.transport closed (tree.frame outside accepted).1 (tree.frame outside accepted).2 + +/-- Carry the admitted declaration, substitution prediction, and selected +constant hit through inference of an entire supported function body. -/ +def CachedConstantInferenceSupport.afterInference {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {fuel : Nat} {before after : TcState .anon} {term result : KExpr .anon} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} + (support : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) + (closed : (KExpr.const id arguments info).lbr = 0) + (tree : InferenceCacheTrace fuel before term) + (outside : ((KExpr.const id arguments info).addr, emptyCtxAddr) ∉ tree.writes) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + CachedConstantInferenceSupport resolve entries after id arguments info ref entry type := + support.transport closed (tree.frame outside accepted).1 (tree.frame outside accepted).2 + +/-- A later constant's actual returned type inherits typing from its earlier +witness after recursive inference; no semantic premise about caches is added. -/ +theorem CachedConstantInferenceSupport.sound_after_inference {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} + {fuel : Nat} {before middle after : TcState .anon} {term result cachedResult : KExpr .anon} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} {methods : Methods .anon} + (support : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) + (closed : (KExpr.const id arguments info).lbr = 0) + (tree : InferenceCacheTrace fuel before term) + (outside : ((KExpr.const id arguments info).addr, emptyCtxAddr) ∉ tree.writes) + (inferred : RecM.infer term (methodsN fuel) before = .ok result middle) + (accepted : RecM.infer (.const id arguments info) methods middle = .ok cachedResult after) : + readScopedExpr? resolve locals cachedResult = some type.erase ∧ + TypingClaim.{u,v} entries context (.const ref (arguments.toList.map readLevel)) type := + (support.afterInference closed tree outside inferred).sound accepted + +/-- Construct a later sort leaf using agreement preserved by a whole +recursive call, deriving its current key and cache selection automatically. -/ +def BinderInference.sortAfterInference {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel nextFuel : Nat} + {before after : TcState .anon} {term result : KExpr .anon} + {level : KUniv .anon} {info : ExprInfo .anon} + (closed : (KExpr.sort level info).lbr = 0) + (agreement : InferenceCacheAgreement before ((KExpr.sort level info).addr, emptyCtxAddr) + (KExpr.mkSort (KUniv.mkSucc level))) + (tree : InferenceCacheTrace fuel before term) + (outside : ((KExpr.sort level info).addr, emptyCtxAddr) ∉ tree.writes) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) + (coherent : after.env.intern.WF) + (faithful : KExpr.KeyCollisionFree fun candidate => after.env.intern.ExprSupport candidate ∨ + candidate = KExpr.mkSort (KUniv.mkSucc level)) : + BinderInference resolve entries locals context nextFuel after (.sort level info) + (.sort (readLevel level)) (.sort (.succ (readLevel level))) := + .sortOfAgreement closed (tree.agreement outside agreement accepted) coherent faithful + +end Ix.Kernel.Consistency diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 4d49329b4..92bfa5368 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -723,8 +723,93 @@ private def cacheInvariantCases : TestSeq := ++ test "cache invariants: clearing both partitions permits fresh inference-only synthesis" (inferenceAfterClearing true) +/-- Preserve warm constant and sort entries through entire recursive calls. +The application checks a lambda argument; the lambda itself uses the watched +constant twice. Replaying the composite result also exercises a root cache hit. +These are operational preservation cases; composite cache-hit typing remains +a separate refinement boundary. -/ +private def cacheAfterComposite (shape : Nat) (inferOnly stats surroundingScope : Bool) : Bool := + let id : KId .anon := ⟨polymorphicIdentity.2, ()⟩ + let watched := KExpr.mkConst (m := .anon) id #[levelOne] + let sortType := KExpr.mkSort (m := .anon) levelOne + let useIdentity : KExpr .anon → KExpr .anon := fun arg => + .mkApp (.mkApp watched (.mkVar 1 ())) arg + let body := KExpr.mkLam () () sortType + (.mkLam () () (.mkVar 0 ()) (useIdentity (useIdentity (.mkVar 0 ())))) + let application := KExpr.mkApp (.mkApp (.mkConst id #[levelTwo]) identityType) body + let term := if shape == 0 then identityType else if shape == 1 then body else application + let expected := if shape == 0 then KExpr.mkSort levelTwo else identityType + let action : RecM .anon Bool := do + let constantType ← RecM.inferCall watched + let sortResult ← RecM.inferCall sortType + let key ← TcM.inferKey watched + let sortKey ← TcM.inferKey sortType + let initial ← get + RecM.withLctxScope do + if surroundingScope then + let _ ← TcM.openBinder () () sortType (.mkVar 0 ()) + pure () + let activeSize := (← get).lctx.size + let rootKey ← TcM.inferKey term + let result ← RecM.inferCall term + let after ← get + let cache := if inferOnly then after.env.inferOnlyCache else after.env.inferCache + let oldCache := if inferOnly then initial.env.inferOnlyCache else initial.env.inferCache + let opposite := if inferOnly then after.env.inferCache else after.env.inferOnlyCache + let replay ← RecM.inferCall term + let constantReuse ← RecM.inferCall watched + let sortReuse ← RecM.inferCall sortType + let final ← get + return result.addr == expected.addr && result.lbr == 0 && replay.addr == result.addr && + constantReuse.addr == constantType.addr && constantType.addr == identityType.addr && + sortReuse.addr == sortResult.addr && sortResult.addr == (.mkSort levelTwo : KExpr .anon).addr && + rootKey != key && rootKey != sortKey && key != sortKey && + cache[key]?.map (·.addr) == oldCache[key]?.map (·.addr) && + cache[sortKey]?.map (·.addr) == oldCache[sortKey]?.map (·.addr) && + cache[rootKey]?.any (fun cached => cached.addr == result.addr) && + cache.size > oldCache.size && opposite.isEmpty && + after.env.consts.size == initial.env.consts.size && + (after.env.get? id).map (·.ty.addr) == (initial.env.get? id).map (·.ty.addr) && + after.lctx.size == activeSize && after.inferOnly == inferOnly && + (if stats && shape != 0 then after.deqCalls > initial.deqCalls + else after.deqCalls == initial.deqCalls) && final.deqCalls == after.deqCalls + match TcM.runRec action { TcState.newLazyAnon polymorphicIdentity.1 with inferOnly, stats } with + | .ok passed after => passed && after.lctx.size == 0 && after.inferOnly == inferOnly + | .error _ _ => false + +private def recursiveCacheEnvironment : Ixon.Env := Id.run do + let (env, identity) := polymorphicIdentity + let nestedCall : Ixon.Expr → Ixon.Expr := fun arg => + .app (.app (.ref 0 #[0]) (.var 1)) arg + let definition : Ixon.Univ → Ix.DefKind → Ixon.Constant := fun level kind => + ⟨.defn ⟨kind, .safe, 0, + .leanAll (.sort 0) (.leanAll (.var 0) (.var 1)), + .leanLam (.sort 0) (.leanLam (.var 0) (nestedCall (nestedCall (.var 0))))⟩, + #[], #[identity], #[level]⟩ + let (env, _) := storeConst env (definition (.succ .zero) .defn) + let (env, _) := storeConst env (definition (.succ (.succ .zero)) .opaq) + return env + +private def recursiveCacheCases : TestSeq := + test "recursive cache: full dependent type inference retains warm sort and constant entries" + (cacheAfterComposite 0 false false false) + ++ test "recursive cache: inference-only dependent type inference retains warm entries" + (cacheAfterComposite 0 true false false) + ++ test "recursive cache: nested lambda applications retain repeatedly used constant entries" + (cacheAfterComposite 1 false false false) + ++ test "recursive cache: application with a lambda argument retains warm entries" + (cacheAfterComposite 2 false false false) + ++ test "recursive cache: hash-conversion statistics preserve entries and root replay is read-only" + (cacheAfterComposite 2 false true false) + ++ test "recursive cache: nested inference preserves an existing outer local scope" + (cacheAfterComposite 2 false true true) + ++ test "recursive cache: nested polymorphic calls check with persistent caches" + (allSucceeded recursiveCacheEnvironment 3 { clearEvery := 0 }) + ++ test "recursive cache: nested polymorphic calls check with per-item clearing" + (allSucceeded recursiveCacheEnvironment 3 { clearEvery := 1 }) + public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, - polymorphicApplicationCases, constantCacheCases, cacheInvariantCases] + polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index dac7f0cb6..22496e4d0 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -106,7 +106,18 @@ increasing sequence of strongly inaccessible cardinals. after such a frame; `BinderInference.sortOfAgreement` constructs a sort leaf from maintained agreement. Initial agreement and finite execution resources remain premises. General preservation through lazy loading, environment - extension, and composite inference remains open. + extension, and all recursive paths remains open. +- `InferenceCacheTrace.frame` carries preservation through finite recursive + application, forall, and full-mode lambda trees. The trace computes the keys + written by misses, including recursive calls and the final outer insertion; + hits contribute no writes. Entries outside this footprint, loaded declarations, + and checking policy survive the entire successful call. The proof accounts + for lambda domain validation, binder cleanup, and hash conversion's optional + statistics update. `CachedConstantInferenceSupport.afterInference` transports + a closed constant witness through that call, and `BinderInference.sortAfterInference` + constructs a later sort leaf from preserved agreement. Neither needs another + cache-hit observation after the call. The operational tree and exclusion of + the protected key from its writes remain explicit inputs. - `checkEnvAnon_atomic_preserves_model` connects a supported production environment run to model extension. `checkEnvAnon_atomic_no_false` excludes a declaration at an axiom type interpreted as empty, including False. @@ -178,8 +189,14 @@ def useF (x : T.{1}) : T.{1} := f.{1} x preserve their key's entries and the loaded-constant map. Actual sort and already-loaded constant inference provide frames for other keys. These results reduce repeated witnesses; they do not yet derive initial agreement - or preservation through lazy loading, environment extension, or the entire - recursive inference tree. + or preservation through lazy loading or environment extension. + A separate `InferenceCacheTrace` derives a frame for an entire supported + recursive call at any key outside its computed writes. It shares the existing + application and binder execution traces and additionally follows lambda-domain + inference. Constant misses require already-loaded declarations and finite + universe-walker resources; applications use full mode and hash conversion. + The operational trace can frame any selected cache hit, while semantic typing + of composite hits remains outside `BinderInference`. - Binder definitions supply finite inference trees for both the value and its separately checked declared type, exact source readings, closed annotated syntax, and references to the preceding interface. Recursive calls use the actual @@ -273,7 +290,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 161 exact theorem boundaries. The production +The consistency target checks 177 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -311,6 +328,11 @@ Cache-preservation regressions add repeated sort domains, sort reuse and partition priority, preservation through intervening sort and loaded-constant inference, successful and failed scope cleanup, policy restoration, and fresh inference after clearing both partitions. +Recursive preservation regressions retain warm sort and constant entries +through dependent types in both policies, nested lambda applications, and +applications with lambda arguments. They also check reuse of the whole cached +result, statistics updates during hash conversion, an existing outer local +scope, and nested polymorphic declarations with persistent or cleared caches. ## Certified host adapters @@ -340,6 +362,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Polymorphic calls inside binders | [`Consistency/ScopedConstant.lean`](../Ix/Kernel/Verify/Consistency/ScopedConstant.lean), [`ScopedInstUniv.lean`](../Ix/Kernel/Verify/Consistency/ScopedInstUniv.lean) | | Constant cache selection, writes, and typing | [`Consistency/ConstantCache.lean`](../Ix/Kernel/Verify/Consistency/ConstantCache.lean) | | Cache invariants and sort cache typing | [`Consistency/InferenceCache.lean`](../Ix/Kernel/Verify/Consistency/InferenceCache.lean), [`SortCache.lean`](../Ix/Kernel/Verify/Consistency/SortCache.lean) | +| Recursive cache preservation and witness reuse | [`Consistency/RecursiveCache.lean`](../Ix/Kernel/Verify/Consistency/RecursiveCache.lean) | | Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`Application.lean`](../Ix/Kernel/Verify/Consistency/Application.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | | Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | diff --git a/docs/theory.md b/docs/theory.md index 5f0808c89..78da02b32 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -33,8 +33,12 @@ successor sort. Sort and already-loaded constant inference preserve agreement at their cache key and retain other entries. Structural preservation composes through binder opening, interning, unrelated writes, and scope/policy cleanup, including errors. It transports closed constant witnesses, and maintained -agreement constructs sort leaves. Initial agreement, lazy loading, environment -extension, and preservation through composite inference remain obligations. +agreement constructs sort leaves. A finite operational trace now carries +preservation through recursive applications, dependent types, and full-mode +lambdas for keys absent from the recorded writes. It derives later constant +witnesses and sort leaves without repeated cache-hit observations. Initial +agreement, trace construction, lazy loading, environment extension, and +preservation for keys that are written remain obligations. Full checker consistency and compiler/backend refinement remain open. From 179d25c82ebb53e063ccbf180c4d4e37a40a9a01 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 16:59:08 -0400 Subject: [PATCH 14/63] Preserve cached witnesses through standalone lazy loading --- Ix/Kernel/Ingress.lean | 173 +++++++---- Ix/Kernel/Verify/Consistency.lean | 11 +- Ix/Kernel/Verify/Consistency/Audit.lean | 21 +- .../Verify/Consistency/ConstantCache.lean | 11 +- .../Verify/Consistency/InferenceCache.lean | 36 ++- Ix/Kernel/Verify/Consistency/LazyCache.lean | 287 ++++++++++++++++++ .../Verify/Consistency/RecursiveCache.lean | 92 +++++- Ix/Kernel/Verify/Consistency/SortCache.lean | 4 +- Tests/Ix/Kernel/Consistency.lean | 145 ++++++++- docs/kernel-verification.md | 41 ++- docs/theory.md | 10 +- 11 files changed, 731 insertions(+), 100 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/LazyCache.lean diff --git a/Ix/Kernel/Ingress.lean b/Ix/Kernel/Ingress.lean index f459ddbd4..e02f443b6 100644 --- a/Ix/Kernel/Ingress.lean +++ b/Ix/Kernel/Ingress.lean @@ -50,8 +50,31 @@ abbrev AnonEnv := KEnv .anon /-- Ingress monad: state-threads the anon `KEnv` being populated. -/ abbrev IngressM := EStateM IngressErr AnonEnv +/-- Expression conversion can update intern tables, but cannot publish +declarations or change checker caches. Partial progress is retained on error. -/ +abbrev InternIngressM := EStateM IngressErr (InternTable .anon) + +namespace InternIngressM + +@[inline] def internE (e : KExpr .anon) : InternIngressM (KExpr .anon) := fun it => + let (e, it) := it.internExpr e + .ok e it + +@[inline] def internU (u : KUniv .anon) : InternIngressM (KUniv .anon) := fun it => + let (u, it) := it.internUniv u + .ok u it + +end InternIngressM + namespace IngressM +/-- Run conversion with access only to the environment's intern tables. +Both outcomes retain the caller's declarations and checker caches. -/ +@[inline] def runIntern (action : InternIngressM α) : IngressM α := fun env => + match action env.intern with + | .ok value it => .ok value {env with intern := it} + | .error err it => .error err {env with intern := it} + @[inline] def internE (e : KExpr .anon) : IngressM (KExpr .anon) := fun env => let (e, it) := env.intern.internExpr e .ok e { env with intern := it } @@ -135,6 +158,9 @@ structure ConvState where /-- Conversion monad: per-constant caches over the env-threading ingress. -/ abbrev ConvM := StateT ConvState IngressM +/-- Internal conversion state excludes the declaration and checker caches. -/ +abbrev InternConvM := StateT ConvState InternIngressM + inductive UFrame where | process (u : Ixon.Univ) | succ @@ -145,7 +171,7 @@ inductive UFrame where /-- Convert one universe tree (iterative). Uses the *simplifying* `mkMax`/`mkIMax` smart constructors — reduced-node addresses must match the Rust kernel node-for-node — and interns every node. -/ -def ingressUnivTree (root : Ixon.Univ) : IngressM (KUniv .anon) := do +def convertUnivTree (root : Ixon.Univ) : InternIngressM (KUniv .anon) := do let mut stack : Array UFrame := #[.process root] let mut values : Array (KUniv .anon) := #[] while !stack.isEmpty do @@ -155,7 +181,7 @@ def ingressUnivTree (root : Ixon.Univ) : IngressM (KUniv .anon) := do | .process u => match u with | .zero => - values := values.push (← IngressM.internU .mkZero) + values := values.push (← InternIngressM.internU .mkZero) | .succ inner => stack := stack.push .succ |>.push (.process inner) | .max a b => @@ -163,23 +189,27 @@ def ingressUnivTree (root : Ixon.Univ) : IngressM (KUniv .anon) := do | .imax a b => stack := stack.push .imax |>.push (.process b) |>.push (.process a) | .var idx => - values := values.push (← IngressM.internU (.mkParam idx ())) + values := values.push (← InternIngressM.internU (.mkParam idx ())) | .succ => let inner := values.back! values := values.pop - values := values.push (← IngressM.internU (.mkSucc inner)) + values := values.push (← InternIngressM.internU (.mkSucc inner)) | .max => let b := values.back!; values := values.pop let a := values.back!; values := values.pop - values := values.push (← IngressM.internU (.mkMax a b)) + values := values.push (← InternIngressM.internU (.mkMax a b)) | .imax => let b := values.back!; values := values.pop let a := values.back!; values := values.pop - values := values.push (← IngressM.internU (.mkIMax a b)) + values := values.push (← InternIngressM.internU (.mkIMax a b)) match values.back? with | some v => return v | none => throw "ingressUnivTree: empty result stack" +/-- Environment-threading interface for universe-tree conversion. -/ +def ingressUnivTree (root : Ixon.Univ) : IngressM (KUniv .anon) := + IngressM.runIntern (convertUnivTree root) + /-- Ixon universe → kernel universe via the *simplifying* smart constructors (the same reduction ingress applies). Pure: no interning. -/ def ixonUnivToK : Ixon.Univ → KUniv .anon @@ -209,22 +239,22 @@ def reduceIxonUniv (u : Ixon.Univ) : Ixon.Univ := kUnivToIxon (ixonUnivToK u) /-- Convert the universe at table index `idx`, cached per constant. -/ -def ingressUnivIdx (ctx : IngressCtx) (idx : UInt64) : - ConvM (KUniv .anon) := do +def convertUnivIdx (ctx : IngressCtx) (idx : UInt64) : + InternConvM (KUniv .anon) := do if let some cached := (← get).univCache[idx]? then return cached let some u := ctx.univs[idx.toNat]? | throw s!"invalid universe index {idx} (len {ctx.univs.size})" - let ku ← liftM (ingressUnivTree u) + let ku ← liftM (convertUnivTree u) modify fun s => { s with univCache := s.univCache.insert idx ku } return ku /-- Convert an array of universe-table indices. -/ -def ingressUnivArgs (ctx : IngressCtx) (idxs : Array UInt64) : - ConvM (Array (KUniv .anon)) := do +def convertUnivArgs (ctx : IngressCtx) (idxs : Array UInt64) : + InternConvM (Array (KUniv .anon)) := do let mut out : Array (KUniv .anon) := Array.mkEmpty idxs.size for i in idxs do - out := out.push (← ingressUnivIdx ctx i) + out := out.push (← convertUnivIdx ctx i) return out inductive EFrame where @@ -244,8 +274,8 @@ inductive EFrame where refs[i]` names a blob, decoded from the env (the blob address is the hash payload; the value itself is never hashed); `share i` expands transparently against the sharing table (cached by index). -/ -def ingressExpr (ixonEnv : Ixon.Env) (ctx : IngressCtx) (root : Ixon.Expr) : - ConvM (KExpr .anon) := do +def convertExpr (ixonEnv : Ixon.Env) (ctx : IngressCtx) (root : Ixon.Expr) : + InternConvM (KExpr .anon) := do let mut stack : Array EFrame := #[.process root] let mut values : Array (KExpr .anon) := #[] while !stack.isEmpty do @@ -262,22 +292,22 @@ def ingressExpr (ixonEnv : Ixon.Env) (ctx : IngressCtx) (root : Ixon.Expr) : | throw s!"invalid Share index {idx}" stack := stack.push (.cacheShare idx) |>.push (.process expansion) | .var idx => - values := values.push (← liftM (IngressM.internE (.mkVar idx ()))) + values := values.push (← liftM (InternIngressM.internE (.mkVar idx ()))) | .sort uidx => - let u ← ingressUnivIdx ctx uidx - values := values.push (← liftM (IngressM.internE (.mkSort u))) + let u ← convertUnivIdx ctx uidx + values := values.push (← liftM (InternIngressM.internE (.mkSort u))) | .ref refIdx univIdxs => let some addr := ctx.refs[refIdx.toNat]? | throw s!"invalid Ref index {refIdx}" - let univs ← ingressUnivArgs ctx univIdxs + let univs ← convertUnivArgs ctx univIdxs values := values.push - (← liftM (IngressM.internE (.mkConst ⟨addr, ()⟩ univs))) + (← liftM (InternIngressM.internE (.mkConst ⟨addr, ()⟩ univs))) | .recur recIdx univIdxs => let some mid := ctx.mutCtx[recIdx.toNat]? | throw s!"invalid Rec index {recIdx}" - let univs ← ingressUnivArgs ctx univIdxs + let univs ← convertUnivArgs ctx univIdxs values := values.push - (← liftM (IngressM.internE (.mkConst mid univs))) + (← liftM (InternIngressM.internE (.mkConst mid univs))) | .nat blobIdx => let some blobAddr := ctx.refs[blobIdx.toNat]? | throw s!"invalid Nat blob ref index {blobIdx}" @@ -285,7 +315,7 @@ def ingressExpr (ixonEnv : Ixon.Env) (ctx : IngressCtx) (root : Ixon.Expr) : | throw s!"missing Nat blob {blobAddr}" let val := Nat.fromBytesLE bytes.data values := values.push - (← liftM (IngressM.internE (.mkNat val blobAddr))) + (← liftM (InternIngressM.internE (.mkNat val blobAddr))) | .str blobIdx => let some blobAddr := ctx.refs[blobIdx.toNat]? | throw s!"invalid Str blob ref index {blobIdx}" @@ -294,7 +324,7 @@ def ingressExpr (ixonEnv : Ixon.Env) (ctx : IngressCtx) (root : Ixon.Expr) : let some val := String.fromUTF8? bytes | throw s!"Str blob {blobAddr} is not valid UTF-8" values := values.push - (← liftM (IngressM.internE (.mkStr val blobAddr))) + (← liftM (InternIngressM.internE (.mkStr val blobAddr))) | .app f a => stack := stack.push .appDone |>.push (.process a) |>.push (.process f) | .lam _ ty body => @@ -314,27 +344,27 @@ def ingressExpr (ixonEnv : Ixon.Env) (ctx : IngressCtx) (root : Ixon.Expr) : | .appDone => let a := values.back!; values := values.pop let f := values.back!; values := values.pop - values := values.push (← liftM (IngressM.internE (.mkApp f a))) + values := values.push (← liftM (InternIngressM.internE (.mkApp f a))) | .lamDone => let body := values.back!; values := values.pop let ty := values.back!; values := values.pop values := values.push - (← liftM (IngressM.internE (.mkLam () () ty body))) + (← liftM (InternIngressM.internE (.mkLam () () ty body))) | .allDone => let body := values.back!; values := values.pop let ty := values.back!; values := values.pop values := values.push - (← liftM (IngressM.internE (.mkAll () () ty body))) + (← liftM (InternIngressM.internE (.mkAll () () ty body))) | .letDone nd => let body := values.back!; values := values.pop let val := values.back!; values := values.pop let ty := values.back!; values := values.pop values := values.push - (← liftM (IngressM.internE (.mkLet () ty val body nd))) + (← liftM (InternIngressM.internE (.mkLet () ty val body nd))) | .prjDone id field => let val := values.back!; values := values.pop values := values.push - (← liftM (IngressM.internE (.mkPrj id field val))) + (← liftM (InternIngressM.internE (.mkPrj id field val))) | .cacheShare idx => let v := values.back! modify fun s => { s with exprCache := s.exprCache.insert idx v } @@ -345,6 +375,20 @@ def ingressExpr (ixonEnv : Ixon.Env) (ctx : IngressCtx) (root : Ixon.Expr) : return v | none => throw "ingressExpr: empty result stack" +/-- Environment-threading interface for cached universe conversion. -/ +def ingressUnivIdx (ctx : IngressCtx) (idx : UInt64) : ConvM (KUniv .anon) := + fun state => IngressM.runIntern (convertUnivIdx ctx idx state) + +/-- Environment-threading interface for universe-argument conversion. -/ +def ingressUnivArgs (ctx : IngressCtx) (idxs : Array UInt64) : + ConvM (Array (KUniv .anon)) := + fun state => IngressM.runIntern (convertUnivArgs ctx idxs state) + +/-- Environment-threading interface for expression conversion. -/ +def ingressExpr (ixonEnv : Ixon.Env) (ctx : IngressCtx) (root : Ixon.Expr) : + ConvM (KExpr .anon) := + fun state => IngressM.runIntern (convertExpr ixonEnv ctx root state) + /-! ### Constant conversion (anon) -/ /-- One converted `(KId, KConst)` entry. -/ @@ -393,37 +437,52 @@ def insertMutsEntries (entries : Array Entry) : IngressM Unit := do callers pass `#[selfId]` (self-recursive standalones encode their self-reference as `recur 0`). Hints come from the env's `anonHints` channel with the Rust `Regular 0` fall-through. -/ -def ingressDefnAnon (ixonEnv : Ixon.Env) (defn : Ixon.Definition) - (selfId : KId .anon) (constant : Ixon.Constant) (block : KId .anon) +def convertDefnAnon (ixonEnv : Ixon.Env) (defn : Ixon.Definition) + (constant : Ixon.Constant) (block : KId .anon) (mutCtx : Array (KId .anon)) (hintsOverride : Option Lean.ReducibilityHints) : - IngressM (Array Entry) := do + InternIngressM (KConst .anon) := do let ctx : IngressCtx := { sharing := constant.sharing, refs := constant.refs univs := constant.univs, mutCtx } - let (ty, st) ← (ingressExpr ixonEnv ctx defn.typ).run {} - let (val, _) ← (ingressExpr ixonEnv ctx defn.value).run st + let (ty, st) ← (convertExpr ixonEnv ctx defn.typ).run {} + let (val, _) ← (convertExpr ixonEnv ctx defn.value).run st let hints := hintsOverride.getD (.regular 0) - return #[(selfId, - .defn () () defn.kind defn.safety hints defn.lvls ty val () block)] + return .defn () () defn.kind defn.safety hints defn.lvls ty val () block + +/-- Convert one definition entry without publishing it. -/ +def ingressDefnAnon (ixonEnv : Ixon.Env) (defn : Ixon.Definition) + (selfId : KId .anon) (constant : Ixon.Constant) (block : KId .anon) + (mutCtx : Array (KId .anon)) (hintsOverride : Option Lean.ReducibilityHints) : + IngressM (Array Entry) := do + let converted ← IngressM.runIntern + (convertDefnAnon ixonEnv defn constant block mutCtx hintsOverride) + return #[(selfId, converted)] /-- Convert a `Recursor`. `memberIdx` stays 0 on the anon path (Rust parity: "filled in by caller for muts blocks" — the anon caller never does). -/ -def ingressRecursorAnon (ixonEnv : Ixon.Env) (rec : Ixon.Recursor) - (selfId : KId .anon) (constant : Ixon.Constant) (block : KId .anon) - (mutCtx : Array (KId .anon)) : IngressM (Array Entry) := do +def convertRecursorAnon (ixonEnv : Ixon.Env) (rec : Ixon.Recursor) + (constant : Ixon.Constant) (block : KId .anon) + (mutCtx : Array (KId .anon)) : InternIngressM (KConst .anon) := do let ctx : IngressCtx := { sharing := constant.sharing, refs := constant.refs univs := constant.univs, mutCtx } - let (ty, st) ← (ingressExpr ixonEnv ctx rec.typ).run {} + let (ty, st) ← (convertExpr ixonEnv ctx rec.typ).run {} let mut st := st let mut rules : Array (RecRule .anon) := Array.mkEmpty rec.rules.size for rule in rec.rules do - let (rhs, st') ← (ingressExpr ixonEnv ctx rule.rhs).run st + let (rhs, st') ← (convertExpr ixonEnv ctx rule.rhs).run st st := st' rules := rules.push { ctor := (), fields := rule.fields, rhs } - return #[(selfId, - .recr () () rec.k rec.isUnsafe rec.lvls rec.params rec.indices - rec.motives rec.minors block 0 ty rules ())] + return .recr () () rec.k rec.isUnsafe rec.lvls rec.params rec.indices + rec.motives rec.minors block 0 ty rules () + +/-- Convert one recursor entry without publishing it. -/ +def ingressRecursorAnon (ixonEnv : Ixon.Env) (rec : Ixon.Recursor) + (selfId : KId .anon) (constant : Ixon.Constant) (block : KId .anon) + (mutCtx : Array (KId .anon)) : IngressM (Array Entry) := do + let converted ← IngressM.runIntern + (convertRecursorAnon ixonEnv rec constant block mutCtx) + return #[(selfId, converted)] /-- Convert an `Inductive` block member plus all of its constructors. `ctorAddrs` are the caller-computed CPrj addresses. -/ @@ -451,34 +510,40 @@ def ingressAnonInductive (ixonEnv : Ixon.Env) (ind : Ixon.Inductive) ctor.fields ctorTy) return results -/-- Anon ingress for a single standalone (non-mutual) constant. +/-- Convert a standalone without publishing it or accessing checker caches. `Defn`/`Recr` get `mutCtx = #[selfId]` for `recur 0` self-references; `Axio`/`Quot` cannot contain `recur`. Projections/`Muts` are not valid standalone entries. -/ -def ingressAnonStandalone (ixonEnv : Ixon.Env) (addr : Address) - (constant : Ixon.Constant) : IngressM (KId .anon) := do +def convertAnonStandalone (ixonEnv : Ixon.Env) (addr : Address) + (constant : Ixon.Constant) : InternIngressM (KConst .anon) := do let selfId : KId .anon := ⟨addr, ()⟩ let hintsOverride := ixonEnv.anonHints[addr]? - let entries ← match constant.info with + match constant.info with | .defn d => - ingressDefnAnon ixonEnv d selfId constant selfId #[selfId] hintsOverride + convertDefnAnon ixonEnv d constant selfId #[selfId] hintsOverride | .recr r => - ingressRecursorAnon ixonEnv r selfId constant selfId #[selfId] + convertRecursorAnon ixonEnv r constant selfId #[selfId] | .axio a => do let ctx : IngressCtx := { sharing := constant.sharing, refs := constant.refs univs := constant.univs, mutCtx := #[] } - let (ty, _) ← (ingressExpr ixonEnv ctx a.typ).run {} - pure #[(selfId, .axio () () a.isUnsafe a.lvls ty)] + let (ty, _) ← (convertExpr ixonEnv ctx a.typ).run {} + pure (.axio () () a.isUnsafe a.lvls ty) | .quot q => do let ctx : IngressCtx := { sharing := constant.sharing, refs := constant.refs univs := constant.univs, mutCtx := #[] } - let (ty, _) ← (ingressExpr ixonEnv ctx q.typ).run {} - pure #[(selfId, .quot () () q.kind q.lvls ty)] + let (ty, _) ← (convertExpr ixonEnv ctx q.typ).run {} + pure (.quot () () q.kind q.lvls ty) | _ => throw s!"ingressAnonStandalone: {addr} is a projection or Muts block, not a standalone" - insertStandaloneEntries entries + +/-- Convert a standalone, then publish its single declaration and block. -/ +def ingressAnonStandalone (ixonEnv : Ixon.Env) (addr : Address) + (constant : Ixon.Constant) : IngressM (KId .anon) := do + let selfId : KId .anon := ⟨addr, ()⟩ + let converted ← IngressM.runIntern (convertAnonStandalone ixonEnv addr constant) + insertStandaloneEntries #[(selfId, converted)] return selfId /-- Proof-visible result of converting and inserting an entire anonymous diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 94f9f3ef5..81a619b23 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -16,6 +16,7 @@ import Ix.Kernel.Verify.Consistency.ScopedConstant import Ix.Kernel.Verify.Consistency.InferenceCache import Ix.Kernel.Verify.Consistency.SortCache import Ix.Kernel.Verify.Consistency.ConstantCache +import Ix.Kernel.Verify.Consistency.LazyCache import Ix.Kernel.Verify.Consistency.Context import Ix.Kernel.Verify.Consistency.BinderOpening import Ix.Kernel.Verify.Consistency.Application @@ -60,7 +61,13 @@ by recursive applications, dependent functions, and full-mode lambdas, including domain validation and outer cache writes. Successful inference preserves entries outside that footprint, loaded declarations, and checking policy. This derives later constant witnesses and sort leaves without new cache-hit observations. -Initial agreement, finite execution resources, trace construction, lazy loading, -and preservation for keys inside the footprint remain explicit obligations. +Frames allow declaration growth while retaining every old declaration. Verified +standalone lazy loading derives such a frame on success and failure, including +partial conversion state and fault deduplication. Conversion has access only to +intern tables; single-entry registration is fresh on an actual lookup miss. +Recursive constant leaves can use that loader and post-lookup walker resources, +so later closed witnesses survive inference that loads another dependency. +Initial agreement, finite execution resources, trace construction, mutual-block +loading, and preservation for keys inside the footprint remain obligations. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 3af2d24df..e199a4413 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -67,16 +67,20 @@ private def instantiationRoots : Array Lean.Name := #[ ] private def cacheFrameRoots : Array Lean.Name := #[ - ``InferenceCacheFrame.refl, ``InferenceCacheFrame.trans, ``InferenceCacheAgreement.frame, + ``InferenceCacheFrame.of_eq, ``InferenceCacheFrame.refl, ``InferenceCacheFrame.trans, + ``InferenceCacheAgreement.frame, ``cacheInferResult_eq, ``PreservesInferenceCache.pure, ``PreservesInferenceCache.bind, ``PreservesInferenceCache.runIntern, ``InferenceCacheFrame.localContext, ``InferenceCacheAgreement.policy, ``withInferOnly_eq, - ``PreservesInferenceCache.withInferOnly, ``getConst_loaded + ``PreservesInferenceCache.withInferOnly, ``getConst_loaded, + ``IngressCacheExtension.refl, ``IngressCacheExtension.intern, ``IngressCacheExtension.trans, + ``ingress_runIntern_cache, ``LazyLookupFrame.refl, ``LazyLookupFrame.cache, ``LazyLookupFrame.policy ] private def cacheMapRoots : Array Lean.Name := #[ ``InferenceCacheAgreement.write, ``PreservesInferenceCache.write_other, - ``InferenceCacheAgreement.clearReductionCaches + ``InferenceCacheAgreement.clearReductionCaches, + ``IngressCacheExtension.insert, ``insertStandaloneEntries_singleton ] private def cacheKeyRoots : Array Lean.Name := #[ @@ -90,12 +94,19 @@ private def recursiveCacheRoots : Array Lean.Name := #[ ``ApplicationInferenceTrace.output_state, ``ForallInferenceTrace.output_state, ``LambdaInferenceTrace.output_state, ``isDefEq_hash_state, ``isDefEq_hash_frame, ``InferenceCacheTrace.writes, ``InferenceCacheTrace.sortOfKey, - ``InferenceCacheTrace.fvarOfKey, ``InferenceCacheTrace.constOfKey, + ``InferenceCacheTrace.fvarOfKey, ``InferenceCacheTrace.constOfKey, ``InferenceCacheTrace.lazyConstOfKey, ``InferenceCacheTrace.frame, ``InferenceCacheTrace.agreement, + ``infer_lazyConst_cache_frame, ``CachedConstantInferenceSupport.afterLazyInference, ``InferenceCacheHit.afterInference, ``CachedConstantInferenceSupport.afterInference, ``CachedConstantInferenceSupport.sound_after_inference, ``BinderInference.sortAfterInference ] +private def lazyCacheRoots : Array Lean.Name := #[ + ``ingressAnonStandalone_cache, ``ingressAnonAddrShallow_cache, ``lazyIngressAddr_cache, + ``tryGetConst_standalone_cache, ``getConst_standalone_cache, + ``CachedConstantInferenceSupport.afterGetConst, ``CachedConstantInferenceSupport.afterFailedGetConst +] + private def productionRoots : Array Lean.Name := #[ ``StandalonePrefix.member_success, ``definition_body_trace, ``AtomicDefinitionRun.sound, ``AtomicDefinitionRun.no_self_alias, @@ -195,7 +206,7 @@ def roots : Array RootAllowance := #[ }) ++ (binderWalkerRoots ++ cacheKeyRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction -}) ++ (atomicRoots ++ instantiationRoots ++ recursiveCacheRoots).map (fun root => { +}) ++ (atomicRoots ++ instantiationRoots ++ recursiveCacheRoots ++ lazyCacheRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) ++ productionRoots.map (fun root => { diff --git a/Ix/Kernel/Verify/Consistency/ConstantCache.lean b/Ix/Kernel/Verify/Consistency/ConstantCache.lean index 0b4a7e80d..1d9142602 100644 --- a/Ix/Kernel/Verify/Consistency/ConstantCache.lean +++ b/Ix/Kernel/Verify/Consistency/ConstantCache.lean @@ -15,8 +15,9 @@ must equal pure universe substitution of its current loaded declaration. The admitted entry then supplies typing; no semantic cache invariant is an input. Structural frames transport these witnesses through operations that retain the watched entries and loaded declarations. Inference of an already -loaded constant preserves concrete agreement at its key and frames other keys; -lazy loading and general recursive inference need further preservation proofs. +loaded constant preserves concrete agreement at its key and frames other keys. +`LazyCache` and `RecursiveCache` extend preservation to standalone lazy loading +and finite recursive inference trees. -/ namespace Ix.Kernel.Consistency @@ -53,7 +54,7 @@ theorem instantiateUnivParams_cache_frame {before after : TcState .anon} (fun _ h => Or.inr h) ⟨support.coherent, fun _ h => Or.inl h⟩ rw [run] at post rw [post.2.2.1] - exact ⟨rfl, rfl, rfl⟩ + exact .of_eq rfl rfl rfl /-- Inference of an already loaded constant frames all other cache keys, including on a miss that runs universe substitution and writes a result. @@ -172,7 +173,7 @@ structure CachedConstantInferenceSupport {β : Type u} prediction : readInstantiatedType? resolve concrete.ty arguments = some type.erase conditions : (entry.type.instL (arguments.toList.map readLevel)).annotations = type.annotations -/-- A preserved closed cache entry and unchanged loaded declarations carry +/-- A preserved closed cache entry and retained loaded declarations carry the complete constant witness into a later checker state. Only checking policy matters for the eligibility of an inference-only hit. -/ def CachedConstantInferenceSupport.transport {β : Type u} @@ -190,7 +191,7 @@ def CachedConstantInferenceSupport.transport {β : Type u} change after.env.get? id = some support.concrete have loaded := support.loaded rw [(support.hit.key_closed closed).2] at loaded - simpa only [KEnv.get?, frame.constants] using loaded + exact frame.constants id support.concrete loaded resolved := support.resolved, found := support.found count := support.count, arity := support.arity, scope := support.scope reading := support.reading, levels := support.levels diff --git a/Ix/Kernel/Verify/Consistency/InferenceCache.lean b/Ix/Kernel/Verify/Consistency/InferenceCache.lean index 5f62028d0..41a5019aa 100644 --- a/Ix/Kernel/Verify/Consistency/InferenceCache.lean +++ b/Ix/Kernel/Verify/Consistency/InferenceCache.lean @@ -94,15 +94,28 @@ structure InferenceCacheAgreement (before : TcState .anon) only : ∀ cached, before.env.inferOnlyCache[key]? = some cached → cached = type /-- Relevant data retained by an operation: the two entries at one key and -the loaded declarations. Other keys and checker fields may change. -/ +every previously loaded declaration. New declarations may be loaded; other +keys and checker fields may change. -/ structure InferenceCacheFrame (key : Address × Address) (before after : TcState .anon) : Prop where full : after.env.inferCache[key]? = before.env.inferCache[key]? only : after.env.inferOnlyCache[key]? = before.env.inferOnlyCache[key]? - constants : after.env.consts = before.env.consts + constants : ∀ id concrete, before.env.get? id = some concrete → + after.env.get? id = some concrete + +/-- The existing exact-map transitions are also declaration extensions. -/ +theorem InferenceCacheFrame.of_eq {key : Address × Address} + {before after : TcState .anon} + (full : after.env.inferCache[key]? = before.env.inferCache[key]?) + (only : after.env.inferOnlyCache[key]? = before.env.inferOnlyCache[key]?) + (constants : after.env.consts = before.env.consts) : + InferenceCacheFrame key before after := by + refine ⟨full, only, ?_⟩ + intro id concrete loaded + simpa only [KEnv.get?, constants] using loaded theorem InferenceCacheFrame.refl (key : Address × Address) (state : TcState .anon) : - InferenceCacheFrame key state state := ⟨rfl, rfl, rfl⟩ + InferenceCacheFrame key state state := .of_eq rfl rfl rfl theorem InferenceCacheFrame.trans {key : Address × Address} {before middle after : TcState .anon} @@ -110,7 +123,7 @@ theorem InferenceCacheFrame.trans {key : Address × Address} (second : InferenceCacheFrame key middle after) : InferenceCacheFrame key before after := ⟨second.full.trans first.full, second.only.trans first.only, - second.constants.trans first.constants⟩ + fun id concrete loaded => second.constants id concrete (first.constants id concrete loaded)⟩ theorem InferenceCacheAgreement.frame {key : Address × Address} {type : KExpr .anon} {before after : TcState .anon} (agreement : InferenceCacheAgreement before key type) @@ -199,7 +212,7 @@ theorem PreservesInferenceCache.bind {key : Address × Address} cases result : next value middle <;> rw [result] at final <;> exact initial.trans final theorem PreservesInferenceCache.runIntern (key : Address × Address) (action : InternM .anon α) : - PreservesInferenceCache key (TcM.runIntern action) := fun _ => ⟨rfl, rfl, rfl⟩ + PreservesInferenceCache key (TcM.runIntern action) := fun _ => .of_eq rfl rfl rfl theorem PreservesInferenceCache.inferKey (key : Address × Address) (term : KExpr .anon) : PreservesInferenceCache key (TcM.inferKey term) := by @@ -215,10 +228,10 @@ theorem PreservesInferenceCache.inferKey (key : Address × Address) (term : KExp .ok before before from rfl] at run by_cases fast : (term.lbr == 0 || before.ctx.isEmpty) = true · rw [if_pos fast] at run - cases run <;> exact ⟨rfl, rfl, rfl⟩ + cases run <;> exact .of_eq rfl rfl rfl · rw [if_neg fast] at run cases cached : before.ctxAddrCache[(before.ctxId, term.lbr)]? <;> - rw [cached] at run <;> cases run <;> exact ⟨rfl, rfl, rfl⟩ + rw [cached] at run <;> cases run <;> exact .of_eq rfl rfl rfl theorem PreservesInferenceCache.openBinder (key : Address × Address) (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) (type body : KExpr .anon) : @@ -226,8 +239,8 @@ theorem PreservesInferenceCache.openBinder (key : Address × Address) intro before rw [openBinder_eq] by_cases room : before.env.nextFVarId.toNat + 1 < UInt64.size - · simp only [room, if_true]; exact ⟨rfl, rfl, rfl⟩ - · simp only [room, if_false]; exact ⟨rfl, rfl, rfl⟩ + · simp only [room, if_true]; exact .of_eq rfl rfl rfl + · simp only [room, if_false]; exact .of_eq rfl rfl rfl /-- An actual insertion at another key leaves this key's entries and loaded declarations unchanged, for either validation policy. -/ @@ -236,7 +249,8 @@ theorem PreservesInferenceCache.write_other {key other : Address × Address} PreservesInferenceCache key (RecM.cacheInferResult policy other type methods) := by intro before rw [cacheInferResult_eq] - cases policy <;> refine ⟨?_, ?_, rfl⟩ <;> simp [Std.HashMap.getElem?_insert, different] + cases policy <;> apply InferenceCacheFrame.of_eq <;> + simp [Std.HashMap.getElem?_insert, different] /-- Exact scope cleanup on either outcome, retaining the body's other state updates. Shared by inference inversion and cache-preservation proofs. -/ @@ -276,7 +290,7 @@ theorem PreservesInferenceCache.withLctxScope {key : Address × Address} /-- Changing only local declarations never changes closed cache data. -/ theorem InferenceCacheFrame.localContext (key : Address × Address) (before : TcState .anon) (context : LocalContext .anon) : - InferenceCacheFrame key before {before with lctx := context} := ⟨rfl, rfl, rfl⟩ + InferenceCacheFrame key before {before with lctx := context} := .of_eq rfl rfl rfl /-- Agreement itself is independent of the currently selected policy. -/ theorem InferenceCacheAgreement.policy {before : TcState .anon} diff --git a/Ix/Kernel/Verify/Consistency/LazyCache.lean b/Ix/Kernel/Verify/Consistency/LazyCache.lean new file mode 100644 index 000000000..c7a8937bd --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/LazyCache.lean @@ -0,0 +1,287 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.ConstantCache +import Ix.Kernel.Ingress + +/-! +# Inference caches across verified standalone lazy loading + +Conversion accesses only intern tables. Registration inserts one fresh +declaration, so existing declarations and inference caches survive both +successful loading and errors with partial conversion progress. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +private instance : LawfulBEq (KId .anon) where + eq_of_beq {a b} equal := by + cases a with | mk left leftName => + cases b with | mk right rightName => + have same : left = right := eq_of_beq (Bool.and_eq_true_iff.mp equal).1 + cases leftName + cases rightName + cases same + rfl + rfl {a} := by + change (a.addr == a.addr && Mode.F.beq a.name a.name) = true + exact Bool.and_eq_true_iff.mpr ⟨beq_self_eq_true _, rfl⟩ + +private instance : LawfulHashable (KId .anon) where + hash_eq _ _ equal := by rw [eq_of_beq equal] + +/-- Data protected while a lazy load grows the declaration map. -/ +structure IngressCacheExtension (before after : AnonEnv) : Prop where + full : after.inferCache = before.inferCache + only : after.inferOnlyCache = before.inferOnlyCache + constants : ∀ id concrete, before.get? id = some concrete → after.get? id = some concrete + freshIds : after.nextFVarId = before.nextFVarId + +theorem IngressCacheExtension.refl (env : AnonEnv) : IngressCacheExtension env env := + ⟨rfl, rfl, fun _ _ found => found, rfl⟩ + +theorem IngressCacheExtension.intern (env : AnonEnv) (it : InternTable .anon) : + IngressCacheExtension env {env with intern := it} := + ⟨rfl, rfl, fun _ _ found => found, rfl⟩ + +theorem IngressCacheExtension.trans {before middle after : AnonEnv} + (first : IngressCacheExtension before middle) (second : IngressCacheExtension middle after) : + IngressCacheExtension before after := + ⟨second.full.trans first.full, second.only.trans first.only, + fun id concrete found => second.constants id concrete (first.constants id concrete found), + second.freshIds.trans first.freshIds⟩ + +/-- A fresh single-entry registration retains every old declaration. -/ +theorem IngressCacheExtension.insert {env : AnonEnv} {id : KId .anon} + (fresh : env.get? id = none) (concrete : KConst .anon) : + IngressCacheExtension env ((env.insert id concrete).insertBlock id #[id]) := by + refine ⟨rfl, rfl, ?_, rfl⟩ + intro old value loaded + have different : id ≠ old := by + intro equal + subst old + rw [fresh] at loaded + contradiction + simpa only [KEnv.get?, KEnv.insert, KEnv.insertBlock, Std.HashMap.getElem?_insert, + beq_iff_eq, different, ↓reduceIte] using loaded + +/-- The public converter preserves protected state on success and error. -/ +theorem ingress_runIntern_cache (action : InternIngressM α) (before : AnonEnv) : + match IngressM.runIntern action before with + | .ok _ after | .error _ after => IngressCacheExtension before after := by + unfold IngressM.runIntern + cases action before.intern <;> exact .intern _ _ + +theorem insertStandaloneEntries_singleton (id : KId .anon) (concrete : KConst .anon) + (before : AnonEnv) : + insertStandaloneEntries #[(id, concrete)] before = + match reservedMarkerName id.addr with + | some marker => .error + s!"attempted to insert constant at reserved kernel marker address {marker} ({id.addr})" before + | none => .ok () ((before.insert id concrete).insertBlock id #[id]) := by + simp [insertStandaloneEntries, guardReserved] + cases reservedMarkerName id.addr <;> rfl + +/-- Fresh standalone ingress preserves old declarations and both caches, +including conversion errors and rejection by the reserved-address guard. -/ +theorem ingressAnonStandalone_cache (source : Ixon.Env) (addr : Address) + (constant : Ixon.Constant) (before : AnonEnv) + (fresh : before.get? ⟨addr, ()⟩ = none) : + match ingressAnonStandalone source addr constant before with + | .ok _ after | .error _ after => IngressCacheExtension before after := by + unfold ingressAnonStandalone + change (match (EStateM.bind (IngressM.runIntern (convertAnonStandalone source addr constant)) + _ : IngressM (KId .anon)) before with + | .ok _ after | .error _ after => IngressCacheExtension before after) + unfold EStateM.bind IngressM.runIntern + cases converted : convertAnonStandalone source addr constant before.intern with + | error err it => exact .intern _ _ + | ok concrete it => + change (match (EStateM.bind (insertStandaloneEntries #[(⟨addr, ()⟩, concrete)]) + _ : IngressM (KId .anon)) {before with intern := it} with + | .ok _ after | .error _ after => IngressCacheExtension before after) + rw [EStateM.bind, insertStandaloneEntries_singleton] + cases reservedMarkerName addr with + | some marker => exact .intern _ _ + | none => + exact (IngressCacheExtension.intern before it).trans + (.insert (env := {before with intern := it}) fresh concrete) + +/-- The verified lookup either fails, misses, or materializes a standalone. +Mutual blocks and their projections require a separate publication proof. -/ +def StandaloneMaterialization (source : Ixon.Env) (addr : Address) : Prop := + match getConstVerified source addr true with + | .error _ | .ok none => True + | .ok (some constant) => match constant.info with + | .defn _ | .recr _ | .axio _ | .quot _ => True + | _ => False + +/-- The actual verified shallow loader derives the extension from fresh +standalone registration, with no assumed callback-preservation premise. -/ +theorem ingressAnonAddrShallow_cache (source : Ixon.Env) (addr : Address) + (standalone : StandaloneMaterialization source addr) (before : AnonEnv) + (fresh : before.get? ⟨addr, ()⟩ = none) : + match ingressAnonAddrShallow source addr true before with + | .ok _ after | .error _ after => IngressCacheExtension before after := by + unfold ingressAnonAddrShallow + change (match (EStateM.bind (IngressM.liftExcept (getConstVerified source addr true)) + _ : IngressM Bool) before with + | .ok _ after | .error _ after => IngressCacheExtension before after) + unfold StandaloneMaterialization at standalone + cases verified : getConstVerified source addr true with + | error err => exact .refl _ + | ok optional => + cases optional with + | none => exact .refl _ + | some constant => + rw [verified] at standalone + cases info : constant.info <;> simp only [info] at standalone + all_goals try contradiction + all_goals + dsimp only [IngressM.liftExcept, Bind.bind, Pure.pure, EStateM.pure, EStateM.bind] + rw [info] + dsimp only [Bind.bind, EStateM.bind] + have frame := ingressAnonStandalone_cache source addr constant before fresh + cases run : ingressAnonStandalone source addr constant before <;> + rw [run] at frame <;> exact frame + +/-- A lookup may grow the environment and record a fault attempt. Every +other checker field, including policy and both local contexts, is retained. -/ +structure LazyLookupFrame (before after : TcState .anon) : Prop where + environment : IngressCacheExtension before.env after.env + checker : after = {before with env := after.env, faultedAddrs := after.faultedAddrs} + +theorem LazyLookupFrame.refl (state : TcState .anon) : LazyLookupFrame state state := + ⟨.refl _, rfl⟩ + +theorem LazyLookupFrame.cache {before after : TcState .anon} + (frame : LazyLookupFrame before after) (key : Address × Address) : + InferenceCacheFrame key before after := by + refine ⟨?_, ?_, frame.environment.constants⟩ + · rw [frame.environment.full] + · rw [frame.environment.only] + +theorem LazyLookupFrame.policy {before after : TcState .anon} + (frame : LazyLookupFrame before after) : after.inferOnly = before.inferOnly := by + rw [frame.checker] + +/-- The installed callback is the production loader with verification on. +The source condition concerns materialization shape, not cache preservation. -/ +structure StandaloneLazySupport (before : TcState .anon) (addr : Address) where + source : Ixon.Env + installed : before.lazyFault = some (fun address => ingressAnonAddrShallow source address true) + standalone : StandaloneMaterialization source addr + +/-- Direct fresh faults retain existing cache witnesses on every outcome, +including an address already recorded in the fault-deduplication set. -/ +theorem lazyIngressAddr_cache {before : TcState .anon} {addr : Address} + (support : StandaloneLazySupport before addr) + (fresh : before.env.get? ⟨addr, ()⟩ = none) : + match TcM.lazyIngressAddr addr before with + | .ok _ after | .error _ after => LazyLookupFrame before after := by + unfold TcM.lazyIngressAddr + rw [support.installed] + dsimp only + by_cases faulted : before.faultedAddrs.contains addr = true + · rw [if_pos faulted] + exact .refl _ + · rw [if_neg faulted] + have frame := ingressAnonAddrShallow_cache support.source addr support.standalone before.env fresh + cases run : ingressAnonAddrShallow support.source addr true before.env <;> + rw [run] at frame <;> refine ⟨frame, ?_⟩ <;> simp only [support.installed] + +/-- Try-lookup derives freshness from its own initial miss. Loaded hits, +verified source misses, conversion failures, and deduplicated faults all +retain the original cache entries and loaded declarations. -/ +theorem tryGetConst_standalone_cache {before : TcState .anon} {id : KId .anon} + (support : StandaloneLazySupport before id.addr) : + match TcM.tryGetConst id before with + | .ok _ after | .error _ after => LazyLookupFrame before after := by + unfold TcM.tryGetConst + change (match (EStateM.bind (get : TcM .anon (TcState .anon)) _ : + TcM .anon (Option (KConst .anon))) before with + | .ok _ after | .error _ after => LazyLookupFrame before after) + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + dsimp only + cases loaded : before.env.get? id with + | some concrete => exact .refl _ + | none => + have fresh : before.env.get? ⟨id.addr, ()⟩ = none := by + cases id with | mk addr name => cases name; exact loaded + change (match (EStateM.bind (TcM.lazyIngressAddr id.addr) _ : + TcM .anon (Option (KConst .anon))) before with + | .ok _ after | .error _ after => LazyLookupFrame before after) + have frame := lazyIngressAddr_cache support fresh + cases fault : TcM.lazyIngressAddr id.addr before with + | error err after => + rw [EStateM.bind, fault] + simpa only [fault] using frame + | ok value after => + rw [fault] at frame + rw [EStateM.bind, fault] + change (match (EStateM.bind (get : TcM .anon (TcState .anon)) _ : + TcM .anon (Option (KConst .anon))) after with + | .ok _ state | .error _ state => LazyLookupFrame before state) + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) after = .ok after after from rfl] + dsimp only + cases after.env.get? id with + | some concrete => exact frame + | none => cases before.lazyFault.isSome <;> exact frame + +/-- Hard lookup has the same frame, including the error for an absent source. -/ +theorem getConst_standalone_cache {before : TcState .anon} {id : KId .anon} + (support : StandaloneLazySupport before id.addr) : + match TcM.getConst id before with + | .ok _ after | .error _ after => LazyLookupFrame before after := by + unfold TcM.getConst + change (match (EStateM.bind (TcM.tryGetConst id) _ : TcM .anon (KConst .anon)) before with + | .ok _ after | .error _ after => LazyLookupFrame before after) + have frame := tryGetConst_standalone_cache support + cases tried : TcM.tryGetConst id before with + | error err after => + rw [EStateM.bind, tried] + simpa only [tried] using frame + | ok optional after => + rw [tried] at frame + rw [EStateM.bind, tried] + cases optional <;> exact frame + +/-- An earlier cached constant witness survives a successful lazy lookup of +another declaration. No new cache observation or typing premise is supplied. -/ +def CachedConstantInferenceSupport.afterGetConst {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {before after : TcState .anon} {id requested : KId .anon} + {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} {concrete : KConst .anon} + (support : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) + (closed : (KExpr.const id arguments info).lbr = 0) + (loader : StandaloneLazySupport before requested.addr) + (run : TcM.getConst requested before = .ok concrete after) : + CachedConstantInferenceSupport resolve entries after id arguments info ref entry type := by + have frame := getConst_standalone_cache loader + rw [run] at frame + exact support.transport closed (frame.cache _) frame.policy + +/-- Failed loading retains the earlier witness even when conversion has +already changed the intern tables and the fault attempt remains recorded. -/ +def CachedConstantInferenceSupport.afterFailedGetConst {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {before after : TcState .anon} {id requested : KId .anon} + {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} {err : TcError .anon} + (support : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) + (closed : (KExpr.const id arguments info).lbr = 0) + (loader : StandaloneLazySupport before requested.addr) + (run : TcM.getConst requested before = .error err after) : + CachedConstantInferenceSupport resolve entries after id arguments info ref entry type := by + have frame := getConst_standalone_cache loader + rw [run] at frame + exact support.transport closed (frame.cache _) frame.policy + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean index d42bc9fe1..0ea5cfff4 100644 --- a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean +++ b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean @@ -4,6 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.BinderInference +import Ix.Kernel.Verify.Consistency.LazyCache /-! # Cache preservation through recursive inference @@ -84,7 +85,7 @@ theorem isDefEq_hash_frame {left right : KExpr .anon} (key : Address × Address) : InferenceCacheFrame key before after ∧ after.inferOnly = before.inferOnly := by rw [isDefEq_hash_state equal] at run - split at run <;> cases run <;> exact ⟨⟨rfl, rfl, rfl⟩, rfl⟩ + split at run <;> cases run <;> exact ⟨(.of_eq rfl rfl rfl), rfl⟩ private theorem openBinder_policy {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body opened : KExpr .anon} @@ -130,7 +131,8 @@ private theorem infer_miss_frame {term result : KExpr .anon} /-- Operational support for the successful recursive fragment. Hits write nothing, including repeated uses of the watched entry. Misses record their -actual key; constant misses require a loaded source and finite walker resources. +actual key; constant misses use a loaded source or verified standalone lazy +loading, with finite walker resources at the actual post-lookup state. The lambda domain call is included even though semantic checking can omit its typing subtree once the declared type's formation has been established. -/ inductive InferenceCacheTrace : Nat → TcState .anon → KExpr .anon → Type @@ -145,6 +147,12 @@ inductive InferenceCacheTrace : Nat → TcState .anon → KExpr .anon → Type (concrete : KConst .anon) (loaded : miss.keyed.env.get? id = some concrete) (resources : UniverseInstantiationSupport miss.keyed concrete.ty arguments) : InferenceCacheTrace fuel before (.const id arguments info) + | lazyConst {fuel before id arguments info} + (miss : UncachedInference before (.const id arguments info)) + (loader : StandaloneLazySupport miss.keyed id.addr) + (resources : ∀ concrete loaded, TcM.getConst id miss.keyed = .ok concrete loaded → + UniverseInstantiationSupport loaded concrete.ty arguments) : + InferenceCacheTrace fuel before (.const id arguments info) | app {fuel before fn arg info} (full : before.inferOnly = false) (miss : UncachedInference before (.app fn arg info)) (trace : ApplicationInferenceTrace fuel miss.keyed fn arg) @@ -170,7 +178,7 @@ hits contribute no key; recursive calls and each outer insertion are included. - def InferenceCacheTrace.writes {fuel : Nat} {before : TcState .anon} {term : KExpr .anon} : InferenceCacheTrace fuel before term → List (Address × Address) | .hit _ => [] - | .sort miss | .fvar miss | .const miss .. => [miss.key] + | .sort miss | .fvar miss | .const miss .. | .lazyConst miss .. => [miss.key] | .app _ miss _ _ first second | .forallE miss _ first second | .lam _ miss _ first second => miss.key :: (first.writes ++ second.writes) @@ -204,6 +212,21 @@ def InferenceCacheTrace.constOfKey {fuel : Nat} {before keyed : TcState .anon} · exact .const miss concrete (by simpa only [stateEq] using loaded) (by simpa only [stateEq] using resources) +/-- Construct a constant leaf that may load its declaration. Cache selection +comes from the actual maps; walker resources concern the returned lookup state. -/ +def InferenceCacheTrace.lazyConstOfKey {fuel : Nat} {before keyed : TcState .anon} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {key : Address × Address} + (keyRun : TcM.inferKey (.const id arguments info) before = .ok key keyed) + (loader : StandaloneLazySupport keyed id.addr) + (resources : ∀ concrete loaded, TcM.getConst id keyed = .ok concrete loaded → + UniverseInstantiationSupport loaded concrete.ty arguments) : + InferenceCacheTrace fuel before (.const id arguments info) := by + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, _, stateEq⟩ + · exact .hit hit + · exact .lazyConst miss (by simpa only [stateEq] using loader) + (by simpa only [stateEq] using resources) + /-- Every successful call in the finite tree preserves entries outside its computed write footprint and retains the loaded declarations and policy. The proof follows the recursive calls, then their real outer cache insertion. -/ @@ -229,7 +252,20 @@ theorem InferenceCacheTrace.frame {fuel : Nat} {before after : TcState .anon} (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).2}} = .ok result middle at run cases run - exact ⟨⟨rfl, rfl, rfl⟩, rfl⟩ + exact ⟨(.of_eq rfl rfl rfl), rfl⟩ + | lazyConst miss loader resources => + simp only [writes, List.mem_singleton] at outside + apply infer_miss_frame miss (Ne.symm outside) accepted + intro middle run + obtain ⟨concrete, foundState, got, _, instantiated⟩ := inferUncached_const_instantiation run + have lookup := getConst_standalone_cache loader + rw [got] at lookup + have resource := resources concrete foundState got + have post := TcM.instantiateUnivParams_wf resource.faithful + (fun _ h => Or.inr h) ⟨resource.coherent, fun _ h => Or.inl h⟩ + rw [instantiated] at post + rw [post.2.2.1] + exact ⟨(lookup.cache key).trans (.of_eq rfl rfl rfl), lookup.policy⟩ | @fvar fuel before id name info miss => simp only [writes, List.mem_singleton] at outside apply infer_miss_frame miss (Ne.symm outside) accepted @@ -256,7 +292,7 @@ theorem InferenceCacheTrace.frame {fuel : Nat} {before after : TcState .anon} (fun _ h => Or.inr h) ⟨resources.coherent, fun _ h => Or.inl h⟩ rw [instantiated] at post rw [post.2.2.1] - exact ⟨⟨rfl, rfl, rfl⟩, rfl⟩ + exact ⟨(.of_eq rfl rfl rfl), rfl⟩ | app full miss trace hashPath functionTree argumentTree functionIH argumentIH => simp only [writes, List.mem_cons, List.mem_append, not_or] at outside apply infer_miss_frame miss (Ne.symm outside.1) accepted @@ -267,7 +303,7 @@ theorem InferenceCacheTrace.frame {fuel : Nat} {before after : TcState .anon} obtain ⟨comparisonFrame, comparisonPolicy⟩ := isDefEq_hash_frame hashPath trace.compareRun key have state := (trace.output_state run).2 rw [state] - exact ⟨(functionFrame.trans (argumentFrame.trans comparisonFrame)).trans ⟨rfl, rfl, rfl⟩, + exact ⟨(functionFrame.trans (argumentFrame.trans comparisonFrame)).trans (.of_eq rfl rfl rfl), comparisonPolicy.trans (argumentPolicy.trans functionPolicy)⟩ | forallE miss trace domainTree bodyTree domainIH bodyIH => simp only [writes, List.mem_cons, List.mem_append, not_or] at outside @@ -278,7 +314,7 @@ theorem InferenceCacheTrace.frame {fuel : Nat} {before after : TcState .anon} have opening := openBinder_frame key trace.openRun have state := (trace.output_state run).2 rw [state] - exact ⟨(domainFrame.trans (opening.trans bodyFrame)).trans ⟨rfl, rfl, rfl⟩, + exact ⟨(domainFrame.trans (opening.trans bodyFrame)).trans (.of_eq rfl rfl rfl), bodyPolicy.trans ((openBinder_policy trace.openRun).trans domainPolicy)⟩ | lam full miss trace domainTree bodyTree domainIH bodyIH => simp only [writes, List.mem_cons, List.mem_append, not_or] at outside @@ -290,9 +326,28 @@ theorem InferenceCacheTrace.frame {fuel : Nat} {before after : TcState .anon} have opening := openBinder_frame key trace.openRun have state := (trace.output_state run).2 rw [state] - exact ⟨(domainFrame.trans (opening.trans bodyFrame)).trans ⟨rfl, rfl, rfl⟩, + exact ⟨(domainFrame.trans (opening.trans bodyFrame)).trans (.of_eq rfl rfl rfl), bodyPolicy.trans ((openBinder_policy trace.openRun).trans domainPolicy)⟩ +/-- A standalone constant call needs no separately constructed operational +tree: the real key and cache selection build its hit or lazy-miss leaf. -/ +theorem infer_lazyConst_cache_frame {fuel : Nat} {before keyed after : TcState .anon} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {key watched : Address × Address} {result : KExpr .anon} + (keyRun : TcM.inferKey (.const id arguments info) before = .ok key keyed) + (different : key ≠ watched) (loader : StandaloneLazySupport keyed id.addr) + (resources : ∀ concrete loaded, TcM.getConst id keyed = .ok concrete loaded → + UniverseInstantiationSupport loaded concrete.ty arguments) + (accepted : RecM.infer (.const id arguments info) (methodsN fuel) before = .ok result after) : + InferenceCacheFrame watched before after ∧ after.inferOnly = before.inferOnly := by + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, keyEq, stateEq⟩ + · exact (InferenceCacheTrace.hit (fuel := fuel) hit).frame (by simp [InferenceCacheTrace.writes]) accepted + · let tree : InferenceCacheTrace fuel before (.const id arguments info) := + .lazyConst miss (by simpa only [stateEq] using loader) + (by simpa only [stateEq] using resources) + apply tree.frame _ accepted + simpa only [tree, InferenceCacheTrace.writes, List.mem_singleton, keyEq] using Ne.symm different + /-- Concrete agreement at an unwritten key is retained by the entire tree. -/ theorem InferenceCacheTrace.agreement {fuel : Nat} {before after : TcState .anon} {term result expected : KExpr .anon} (tree : InferenceCacheTrace fuel before term) @@ -327,6 +382,27 @@ def CachedConstantInferenceSupport.afterInference {β : Type u} CachedConstantInferenceSupport resolve entries after id arguments info ref entry type := support.transport closed (tree.frame outside accepted).1 (tree.frame outside accepted).2 +/-- Reuse the complete earlier witness after inference may load another +standalone. The new cache selection and declaration extension are derived. -/ +def CachedConstantInferenceSupport.afterLazyInference {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {fuel : Nat} {before keyed after : TcState .anon} + {id requested : KId .anon} {arguments requestedArguments : Array (KUniv .anon)} + {info requestedInfo : ExprInfo .anon} {key : Address × Address} {result : KExpr .anon} + {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} + (support : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) + (closed : (KExpr.const id arguments info).lbr = 0) + (keyRun : TcM.inferKey (.const requested requestedArguments requestedInfo) before = .ok key keyed) + (different : key ≠ ((KExpr.const id arguments info).addr, emptyCtxAddr)) + (loader : StandaloneLazySupport keyed requested.addr) + (resources : ∀ concrete loaded, TcM.getConst requested keyed = .ok concrete loaded → + UniverseInstantiationSupport loaded concrete.ty requestedArguments) + (accepted : RecM.infer (.const requested requestedArguments requestedInfo) + (methodsN fuel) before = .ok result after) : + CachedConstantInferenceSupport resolve entries after id arguments info ref entry type := + let frame := infer_lazyConst_cache_frame keyRun different loader resources accepted + support.transport closed frame.1 frame.2 + /-- A later constant's actual returned type inherits typing from its earlier witness after recursive inference; no semantic premise about caches is added. -/ theorem CachedConstantInferenceSupport.sound_after_inference {β : Type u} diff --git a/Ix/Kernel/Verify/Consistency/SortCache.lean b/Ix/Kernel/Verify/Consistency/SortCache.lean index 9119c9da3..5a16c3ef7 100644 --- a/Ix/Kernel/Verify/Consistency/SortCache.lean +++ b/Ix/Kernel/Verify/Consistency/SortCache.lean @@ -103,7 +103,7 @@ theorem infer_sort_cache_frame {before keyed after : TcState .anon} apply keyFrame.trans rw [keyEq] at written rw [written] - cases policy : before.inferOnly <;> - refine ⟨?_, ?_, rfl⟩ <;> simp [Std.HashMap.getElem?_insert, different] + cases policy : before.inferOnly <;> apply InferenceCacheFrame.of_eq <;> + simp [Std.HashMap.getElem?_insert, different] end Ix.Kernel.Consistency diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 92bfa5368..8c1976d3e 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -808,8 +808,151 @@ private def recursiveCacheCases : TestSeq := ++ test "recursive cache: nested polymorphic calls check with per-item clearing" (allSucceeded recursiveCacheEnvironment 3 { clearEvery := 1 }) +/-- Sharing and recursor-rule conversion exercise the restricted converter +state. These fixtures test lookup and inference, not recursor or quotient admission. -/ +private def lazyCacheDependency (kind : Nat) : Ixon.Constant := + let type := Ixon.Expr.leanAll (.sort 0) (.leanAll (.var 0) (.var 1)) + let value := Ixon.Expr.leanLam (.sort 0) (.leanLam (.var 0) (.var 0)) + let info := match kind with + | 0 => Ixon.ConstantInfo.axio ⟨false, 0, .share 0⟩ + | 1 => .defn ⟨.defn, .safe, 0, .share 0, .share 1⟩ + | 2 => .recr ⟨false, false, 0, 0, 0, 0, 0, .share 0, + #[⟨0, .share 1⟩, ⟨1, .share 1⟩]⟩ + | _ => .quot ⟨.type, 0, .share 0⟩ + ⟨info, #[type, value], #[], #[.succ .zero]⟩ + +/-- Warm A, infer a cold B directly or inside a recursive body, and reuse A. +The new declaration and block are retained while both old cache slots survive. -/ +private def cacheAcrossLazyDependency (kind shape : Nat) (inferOnly : Bool) : Bool := + let (source, warmAddr) := polymorphicIdentity + let (source, coldAddr) := storeConst source (lazyCacheDependency kind) + let warmId : KId .anon := ⟨warmAddr, ()⟩ + let coldId : KId .anon := ⟨coldAddr, ()⟩ + let warm := KExpr.mkConst (m := .anon) warmId #[levelOne] + let cold := KExpr.mkConst (m := .anon) coldId #[] + let sortType := KExpr.mkSort (m := .anon) levelOne + let body := KExpr.mkLam () () sortType (.mkLam () () (.mkVar 0 ()) + (.mkApp (.mkApp cold (.mkVar 1 ())) (.mkVar 0 ()))) + let term := if shape == 0 then cold else if shape == 1 then body else + KExpr.mkApp (.mkApp (.mkConst warmId #[levelTwo]) identityType) body + let action : RecM .anon Bool := do + let expected ← RecM.inferCall warm + let key ← TcM.inferKey warm + RecM.withLctxScope do + let _ ← TcM.openBinder () () sortType (.mkVar 0 ()) + let before ← get + let result ← RecM.inferCall term + let loaded ← get + let reused ← RecM.inferCall warm + let replay ← RecM.inferCall term + let _ ← liftM (TcM.lazyIngressAddr (m := .anon) coldAddr) + let after ← get + return expected.addr == identityType.addr && result.addr == expected.addr && + reused.addr == expected.addr && replay.addr == result.addr && warmAddr != coldAddr && + (before.env.get? coldId).isNone && (loaded.env.get? coldId).isSome && + loaded.env.consts.size == before.env.consts.size + 1 && + (loaded.env.get? warmId).map (·.ty.addr) == (before.env.get? warmId).map (·.ty.addr) && + loaded.env.blocks[coldId]?.any (fun members => members.size == 1 && + members[0]?.any (fun member => member.addr == coldAddr)) && + loaded.env.inferCache[key]?.map (·.addr) == before.env.inferCache[key]?.map (·.addr) && + loaded.env.inferOnlyCache[key]?.map (·.addr) == before.env.inferOnlyCache[key]?.map (·.addr) && + loaded.faultedAddrs.contains coldAddr && loaded.faultedAddrs.contains warmAddr && + loaded.lctx.size == before.lctx.size && loaded.inferOnly == inferOnly && + loaded.env.nextFVarId >= before.env.nextFVarId && + after.env.inferCache.size == loaded.env.inferCache.size && + after.env.inferOnlyCache.size == loaded.env.inferOnlyCache.size && + after.env.intern.exprs.size == loaded.env.intern.exprs.size && + after.env.intern.univs.size == loaded.env.intern.univs.size && + after.env.consts.size == loaded.env.consts.size && after.deqCalls == loaded.deqCalls + match TcM.runRec action { TcState.newLazyAnon source with inferOnly, stats := true } with + | .ok passed after => passed && after.lctx.size == 0 && after.inferOnly == inferOnly + | .error _ _ => false + +/-- Errors retain partial intern progress and the fault marker. Retrying a +failed address is deduplicated and returns unknownConst without reconversion. -/ +private def cacheAcrossLazyFailure (kind : Nat) (inferOnly : Bool) : Bool := + let (source, warmAddr) := polymorphicIdentity + let missing := Address.blake3 "consistency-lazy-cache-missing".toUTF8 + let wrong := Address.blake3 "consistency-lazy-cache-wrong-hash".toUTF8 + let level : Ixon.Univ := .succ (.succ (.succ (.succ (.succ .zero)))) + let broken : Ixon.Constant := if kind == 2 then + ⟨.defn ⟨.defn, .safe, 0, .sort 0, .share 9⟩, #[], #[], #[level]⟩ + else ⟨.recr ⟨false, false, 0, 0, 0, 0, 0, .sort 0, + #[⟨0, .sort 0⟩, ⟨1, .share 9⟩]⟩, #[], #[], #[level]⟩ + let (source, coldAddr) := if kind == 0 then (source, missing) + else if kind == 1 then + ({source with consts := source.consts.insert wrong (.ofConstant (lazyCacheDependency 0))}, wrong) + else storeConst source broken + let warmId : KId .anon := ⟨warmAddr, ()⟩ + let coldId : KId .anon := ⟨coldAddr, ()⟩ + let warm := KExpr.mkConst (m := .anon) warmId #[levelOne] + let action : RecM .anon Bool := do + let expected ← RecM.inferCall warm + let key ← TcM.inferKey warm + RecM.withLctxScope do + let _ ← TcM.openBinder () () (.mkSort levelOne) (.mkVar 0 ()) + let before ← get + let rejected ← try + let _ ← RecM.inferCall (.mkConst coldId #[]) + pure false + catch err => + let fragment := if kind == 0 then "unknown constant" else if kind == 1 then + "fails integrity check" else "invalid Share index 9" + pure (((toString err).splitOn fragment).length > 1) + let failed ← get + let deduplicated ← try + let _ ← liftM (TcM.getConst coldId) + pure false + catch err => + match err with + | .unknownConst addr => pure (addr == coldAddr) + | _ => pure false + let reused ← RecM.inferCall warm + let after ← get + return rejected && deduplicated && reused.addr == expected.addr && + (failed.env.get? coldId).isNone && failed.env.consts.size == before.env.consts.size && + failed.env.blocks.size == before.env.blocks.size && failed.faultedAddrs.contains coldAddr && + (failed.env.get? warmId).map (·.ty.addr) == (before.env.get? warmId).map (·.ty.addr) && + failed.env.inferCache[key]?.map (·.addr) == before.env.inferCache[key]?.map (·.addr) && + failed.env.inferOnlyCache[key]?.map (·.addr) == before.env.inferOnlyCache[key]?.map (·.addr) && + failed.env.inferCache.size == before.env.inferCache.size && + failed.env.inferOnlyCache.size == before.env.inferOnlyCache.size && + (if kind >= 2 then failed.env.intern.exprs.size > before.env.intern.exprs.size && + failed.env.intern.univs.size > before.env.intern.univs.size + else failed.env.intern.exprs.size == before.env.intern.exprs.size && + failed.env.intern.univs.size == before.env.intern.univs.size) && + failed.lctx.size == before.lctx.size && failed.env.nextFVarId == before.env.nextFVarId && + failed.inferOnly == inferOnly && after.env.intern.exprs.size == failed.env.intern.exprs.size && + after.env.intern.univs.size == failed.env.intern.univs.size + match TcM.runRec action { TcState.newLazyAnon source with inferOnly } with + | .ok passed after => passed && after.lctx.size == 0 && after.inferOnly == inferOnly + | .error _ _ => false + +private def lazyCacheCases : TestSeq := + test "lazy cache: fresh axiom loading retains a warm full witness" + (cacheAcrossLazyDependency 0 0 false) + ++ test "lazy cache: fresh definition loading retains an inference-only witness" + (cacheAcrossLazyDependency 1 0 true) + ++ test "lazy cache: recursor rule conversion retains a warm witness" + (cacheAcrossLazyDependency 2 0 false) + ++ test "lazy cache: quotient conversion retains a warm witness" + (cacheAcrossLazyDependency 3 0 false) + ++ test "lazy cache: a lambda loads a cold dependency and retains a warm witness" + (cacheAcrossLazyDependency 1 1 false) + ++ test "lazy cache: an application loads a dependency inside its lambda argument" + (cacheAcrossLazyDependency 1 2 false) + ++ test "lazy cache: a missing source preserves warm entries and records the fault" + (cacheAcrossLazyFailure 0 false) + ++ test "lazy cache: an integrity failure preserves warm entries and records the fault" + (cacheAcrossLazyFailure 1 true) + ++ test "lazy cache: a failed definition retains partial conversion and the full witness" + (cacheAcrossLazyFailure 2 false) + ++ test "lazy cache: a failed recursor retains partial conversion and the inference-only witness" + (cacheAcrossLazyFailure 3 true) + public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, - polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases] + polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, + lazyCacheCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 22496e4d0..1d5099bf8 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -105,8 +105,8 @@ increasing sequence of strongly inaccessible cardinals. `CachedConstantInferenceSupport.transport` reuses a closed constant witness after such a frame; `BinderInference.sortOfAgreement` constructs a sort leaf from maintained agreement. Initial agreement and finite execution resources - remain premises. General preservation through lazy loading, environment - extension, and all recursive paths remains open. + remain premises. Frames allow new declarations while retaining every + previously loaded declaration. - `InferenceCacheTrace.frame` carries preservation through finite recursive application, forall, and full-mode lambda trees. The trace computes the keys written by misses, including recursive calls and the final outer insertion; @@ -118,6 +118,18 @@ increasing sequence of strongly inaccessible cardinals. constructs a later sort leaf from preserved agreement. Neither needs another cache-hit observation after the call. The operational tree and exclusion of the protected key from its writes remain explicit inputs. +- `getConst_standalone_cache` proves preservation for the installed production + loader with verification enabled. Conversion accesses only intern tables; + the initial lookup miss makes its single-entry registration fresh. Both + inference maps, old declarations, the fresh-id counter, and all checker + fields except the environment and fault history are retained. The proof + includes absent sources, integrity/parse errors, partial conversion failures, + the reserved-address guard, and fault deduplication. + `InferenceCacheTrace.lazyConst` composes this result with universe substitution + at the actual post-lookup state. `CachedConstantInferenceSupport.afterLazyInference` + reuses an earlier witness after a constant call loads another dependency; + the recursive transport also covers loads inside application and binder trees. + Mutual-block publication and the other recursive paths remain open. - `checkEnvAnon_atomic_preserves_model` connects a supported production environment run to model extension. `checkEnvAnon_atomic_no_false` excludes a declaration at an axiom type interpreted as empty, including False. @@ -186,15 +198,20 @@ def useF (x : T.{1}) : T.{1} := f.{1} x retain finite interning coherence and address-faithfulness premises. - Cache agreement covers both partitions so it survives policy changes. Closed constant witnesses can be transported through composed frames that - preserve their key's entries and the loaded-constant map. Actual sort and - already-loaded constant inference provide frames for other keys. These - results reduce repeated witnesses; they do not yet derive initial agreement - or preservation through lazy loading or environment extension. + preserve their key's entries and each previously loaded declaration. Actual + sort and already-loaded constant inference provide frames for other keys. These + results reduce repeated witnesses; they do not yet derive initial agreement. + Verified standalone lookup supplies an extension frame on every outcome, + retaining partial intern progress on error. The installed callback must be + the actual verified loader, and successful materialization must be standalone. A separate `InferenceCacheTrace` derives a frame for an entire supported recursive call at any key outside its computed writes. It shares the existing application and binder execution traces and additionally follows lambda-domain - inference. Constant misses require already-loaded declarations and finite - universe-walker resources; applications use full mode and hash conversion. + inference. Constant misses use already-loaded declarations or verified + standalone loading, with finite universe-walker resources after lookup; + applications use full mode and hash conversion. The new dependency's semantic + source agreement and intern coherence remain explicit resources. Mutual-block + loading, preservation at written keys, and automatic trace construction remain open. The operational trace can frame any selected cache hit, while semantic typing of composite hits remains outside `BinderInference`. - Binder definitions supply finite inference trees for both the value and its @@ -290,7 +307,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 177 exact theorem boundaries. The production +The consistency target checks 197 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -333,6 +350,11 @@ through dependent types in both policies, nested lambda applications, and applications with lambda arguments. They also check reuse of the whole cached result, statistics updates during hash conversion, an existing outer local scope, and nested polymorphic declarations with persistent or cleared caches. +Standalone lazy-loading regressions warm one witness, load another declaration +directly or inside a lambda/application, and reuse the original witness. +They cover all four standalone conversion forms, both inference policies, +missing and corrupt sources, definition and recursor failures with retained +intern progress, and deduplicated retries. ## Certified host adapters @@ -363,6 +385,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Constant cache selection, writes, and typing | [`Consistency/ConstantCache.lean`](../Ix/Kernel/Verify/Consistency/ConstantCache.lean) | | Cache invariants and sort cache typing | [`Consistency/InferenceCache.lean`](../Ix/Kernel/Verify/Consistency/InferenceCache.lean), [`SortCache.lean`](../Ix/Kernel/Verify/Consistency/SortCache.lean) | | Recursive cache preservation and witness reuse | [`Consistency/RecursiveCache.lean`](../Ix/Kernel/Verify/Consistency/RecursiveCache.lean) | +| Verified standalone lazy loading and cache frames | [`Consistency/LazyCache.lean`](../Ix/Kernel/Verify/Consistency/LazyCache.lean) | | Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`Application.lean`](../Ix/Kernel/Verify/Consistency/Application.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | | Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | diff --git a/docs/theory.md b/docs/theory.md index 78da02b32..56e653502 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -36,9 +36,13 @@ including errors. It transports closed constant witnesses, and maintained agreement constructs sort leaves. A finite operational trace now carries preservation through recursive applications, dependent types, and full-mode lambdas for keys absent from the recorded writes. It derives later constant -witnesses and sort leaves without repeated cache-hit observations. Initial -agreement, trace construction, lazy loading, environment extension, and -preservation for keys that are written remain obligations. +witnesses and sort leaves without repeated cache-hit observations. Frames allow +new declarations while retaining old ones. The actual verified standalone loader +preserves inference caches on success and failure, including partial intern +progress and deduplicated faults. Recursive constant leaves use this result, +so an earlier witness survives inference that loads another dependency. +Initial agreement, post-lookup interning resources, trace construction, +mutual-block loading, and preservation for keys that are written remain obligations. Full checker consistency and compiler/backend refinement remain open. From acfc296d3991c4f558a188f5d413b5bbb2bc4b5b Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 18:23:44 -0400 Subject: [PATCH 15/63] Preserve cached witnesses through mutual-block loading --- Ix/Kernel/Ingress.lean | 62 ++- Ix/Kernel/Verify/Consistency.lean | 14 +- Ix/Kernel/Verify/Consistency/Audit.lean | 14 +- Ix/Kernel/Verify/Consistency/BlockCache.lean | 366 ++++++++++++++++++ Ix/Kernel/Verify/Consistency/LazyCache.lean | 4 +- .../Verify/Consistency/RecursiveCache.lean | 64 ++- Tests/Ix/Kernel/Consistency.lean | 203 +++++++++- docs/kernel-verification.md | 40 +- docs/theory.md | 15 +- 9 files changed, 724 insertions(+), 58 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/BlockCache.lean diff --git a/Ix/Kernel/Ingress.lean b/Ix/Kernel/Ingress.lean index e02f443b6..438044598 100644 --- a/Ix/Kernel/Ingress.lean +++ b/Ix/Kernel/Ingress.lean @@ -397,11 +397,15 @@ abbrev Entry := KId .anon × KConst .anon /-- Insert converted entries into the kernel env, enforcing the reserved-marker guard (Rust panics in `KEnv::insert`; ingress is the only untrusted-input path, so the check lives here). -/ -def guardReserved (entries : Array Entry) : IngressM Unit := do +def checkReserved (entries : Array Entry) : Except IngressErr Unit := do for (id, _) in entries do if let some marker := reservedMarkerName id.addr then throw s!"attempted to insert constant at reserved kernel marker address {marker} ({id.addr})" +/-- Reserved-address validation has no access to the kernel environment. -/ +def guardReserved (entries : Array Entry) : IngressM Unit := + IngressM.liftExcept (checkReserved entries) + /-- Standalone registration: each entry is its own single-member block. -/ def insertStandaloneEntries (entries : Array Entry) : IngressM Unit := do guardReserved entries @@ -486,16 +490,16 @@ def ingressRecursorAnon (ixonEnv : Ixon.Env) (rec : Ixon.Recursor) /-- Convert an `Inductive` block member plus all of its constructors. `ctorAddrs` are the caller-computed CPrj addresses. -/ -def ingressAnonInductive (ixonEnv : Ixon.Env) (ind : Ixon.Inductive) +def convertAnonInductive (ixonEnv : Ixon.Env) (ind : Ixon.Inductive) (selfId : KId .anon) (blockConstant : Ixon.Constant) (blockId : KId .anon) (memberIdx : UInt64) (ctorAddrs : Array Address) - (mutCtx : Array (KId .anon)) : IngressM (Array Entry) := do + (mutCtx : Array (KId .anon)) : InternIngressM (Array Entry) := do if ctorAddrs.size != ind.ctors.size then throw s!"ingressAnonInductive: ctorAddrs.size={ctorAddrs.size} but ind.ctors.size={ind.ctors.size}" let ctx : IngressCtx := { sharing := blockConstant.sharing, refs := blockConstant.refs univs := blockConstant.univs, mutCtx } - let (ty, st) ← (ingressExpr ixonEnv ctx ind.typ).run {} + let (ty, st) ← (convertExpr ixonEnv ctx ind.typ).run {} let ctorIds : Array (KId .anon) := ctorAddrs.map (⟨·, ()⟩) let mut results : Array Entry := #[(selfId, .indc () () ind.lvls ind.params ind.indices ind.isUnsafe blockId @@ -503,13 +507,21 @@ def ingressAnonInductive (ixonEnv : Ixon.Env) (ind : Ixon.Inductive) let mut st := st for h : cidx in [0:ind.ctors.size] do let ctor := ind.ctors[cidx] - let (ctorTy, st') ← (ingressExpr ixonEnv ctx ctor.typ).run st + let (ctorTy, st') ← (convertExpr ixonEnv ctx ctor.typ).run st st := st' results := results.push (ctorIds[cidx]!, .ctor () () ctor.isUnsafe ctor.lvls selfId ctor.cidx ctor.params ctor.fields ctorTy) return results +/-- Environment-threading interface for an inductive and its constructors. -/ +def ingressAnonInductive (ixonEnv : Ixon.Env) (ind : Ixon.Inductive) + (selfId : KId .anon) (blockConstant : Ixon.Constant) (blockId : KId .anon) + (memberIdx : UInt64) (ctorAddrs : Array Address) + (mutCtx : Array (KId .anon)) : IngressM (Array Entry) := + IngressM.runIntern + (convertAnonInductive ixonEnv ind selfId blockConstant blockId memberIdx ctorAddrs mutCtx) + /-- Convert a standalone without publishing it or accessing checker caches. `Defn`/`Recr` get `mutCtx = #[selfId]` for `recur 0` self-references; `Axio`/`Quot` cannot contain `recur`. Projections/`Muts` are not valid @@ -558,9 +570,9 @@ structure AnonBlockIngressTrace where /-- Convert an entire Muts block without publishing it. Splitting conversion from insertion makes the atomic publication tail explicit to verification; the public wrapper below preserves the original all-or-error behavior. -/ -def prepareAnonBlock (ixonEnv : Ixon.Env) +def convertAnonBlock (ixonEnv : Ixon.Env) (blockConstant : Ixon.Constant) (blockAddr : Address) : - IngressM AnonBlockIngressTrace := do + InternIngressM AnonBlockIngressTrace := do let .muts members := blockConstant.info | throw s!"ingressAnonBlock: {blockAddr} is not a Muts block" let blockId : KId .anon := ⟨blockAddr, ()⟩ @@ -576,7 +588,7 @@ def prepareAnonBlock (ixonEnv : Ixon.Env) out := out.push ⟨addr, ()⟩ return out let verifyProj (kind : String) (projAddr : Address) (idx : UInt64) - (cidx : Option UInt64) : IngressM Unit := do + (cidx : Option UInt64) : InternIngressM Unit := do if !ixonEnv.consts.contains projAddr then let tail := match cidx with | none => s!"(block {blockAddr} idx {idx})" @@ -593,17 +605,17 @@ def prepareAnonBlock (ixonEnv : Ixon.Env) let selfId : KId .anon := ⟨projAddr, ()⟩ memberKids := memberKids.push selfId let hintsOverride := ixonEnv.anonHints[projAddr]? - let entries ← ingressDefnAnon ixonEnv d selfId blockConstant blockId + let converted ← convertDefnAnon ixonEnv d blockConstant blockId mutCtx hintsOverride - allEntries := allEntries ++ entries + allEntries := allEntries ++ #[(selfId, converted)] | .recr r => let projAddr := recrProjAddr blockAddr idx verifyProj "RPrj" projAddr idx none let selfId : KId .anon := ⟨projAddr, ()⟩ memberKids := memberKids.push selfId - let entries ← ingressRecursorAnon ixonEnv r selfId blockConstant + let converted ← convertRecursorAnon ixonEnv r blockConstant blockId mutCtx - allEntries := allEntries ++ entries + allEntries := allEntries ++ #[(selfId, converted)] | .indc ind => let projAddr := indcProjAddr blockAddr idx verifyProj "IPrj" projAddr idx none @@ -612,11 +624,17 @@ def prepareAnonBlock (ixonEnv : Ixon.Env) let ctorAddrs := anonCtorAddrs blockAddr idx ind for h : cidx in [0:ctorAddrs.size] do verifyProj "CPrj" ctorAddrs[cidx] idx (some cidx.toUInt64) - let entries ← ingressAnonInductive ixonEnv ind selfId blockConstant + let entries ← convertAnonInductive ixonEnv ind selfId blockConstant blockId idx ctorAddrs mutCtx allEntries := allEntries ++ entries return { memberKids, allEntries } +/-- Block preparation changes only intern tables, on either outcome. -/ +def prepareAnonBlock (ixonEnv : Ixon.Env) + (blockConstant : Ixon.Constant) (blockAddr : Address) : + IngressM AnonBlockIngressTrace := + IngressM.runIntern (convertAnonBlock ixonEnv blockConstant blockAddr) + /-- Traced anon ingress for an entire Muts block: convert every member, then atomically publish the exact flat entry array returned in the trace. -/ def ingressAnonBlockWithTrace (ixonEnv : Ixon.Env) @@ -631,6 +649,15 @@ def ingressAnonBlock (ixonEnv : Ixon.Env) (blockConstant : Ixon.Constant) (blockAddr : Address) : IngressM (Array (KId .anon)) := do return (← ingressAnonBlockWithTrace ixonEnv blockConstant blockAddr).memberKids +/-- The owning block requested by shallow ingress, if the source is mutual. -/ +def ingressBlockAddr? (addr : Address) : Ixon.ConstantInfo → Option Address + | .dPrj p => some p.block + | .iPrj p => some p.block + | .rPrj p => some p.block + | .cPrj p => some p.block + | .muts _ => some addr + | _ => none + /-- Anon shallow ingress for a single address — the lazy fault path. For a projection, fetches the parent block and ingresses the whole block (with block-level dedup via `kenv.blocks`); for a standalone, ingresses @@ -639,14 +666,7 @@ def ingressAnonAddrShallow (ixonEnv : Ixon.Env) (addr : Address) (verify : Bool := true) : IngressM Bool := do let some constant ← IngressM.liftExcept (getConstVerified ixonEnv addr verify) | return false - let blockAddr? : Option Address := match constant.info with - | .dPrj p => some p.block - | .iPrj p => some p.block - | .rPrj p => some p.block - | .cPrj p => some p.block - | .muts _ => some addr - | _ => none - match blockAddr? with + match ingressBlockAddr? addr constant.info with | some blockAddr => -- Block dedup: a prior fault on any projection of this block already -- populated every member. diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 81a619b23..a70b4e8ea 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -17,6 +17,7 @@ import Ix.Kernel.Verify.Consistency.InferenceCache import Ix.Kernel.Verify.Consistency.SortCache import Ix.Kernel.Verify.Consistency.ConstantCache import Ix.Kernel.Verify.Consistency.LazyCache +import Ix.Kernel.Verify.Consistency.BlockCache import Ix.Kernel.Verify.Consistency.Context import Ix.Kernel.Verify.Consistency.BinderOpening import Ix.Kernel.Verify.Consistency.Application @@ -62,12 +63,15 @@ domain validation and outer cache writes. Successful inference preserves entries outside that footprint, loaded declarations, and checking policy. This derives later constant witnesses and sort leaves without new cache-hit observations. Frames allow declaration growth while retaining every old declaration. Verified -standalone lazy loading derives such a frame on success and failure, including -partial conversion state and fault deduplication. Conversion has access only to -intern tables; single-entry registration is fresh on an actual lookup miss. +lazy loading derives such a frame on success and failure, including partial +conversion state and fault deduplication. Standalone and block preparation have +access only to intern tables; single-entry registration is fresh on an actual +lookup miss. Block publication requires each converted entry to agree with any +old declaration at its key. Fresh or partially loaded blocks meet this condition +through pointwise lookup checks; uniqueness among fresh keys is unnecessary. Recursive constant leaves can use that loader and post-lookup walker resources, so later closed witnesses survive inference that loads another dependency. -Initial agreement, finite execution resources, trace construction, mutual-block -loading, and preservation for keys inside the footprint remain obligations. +Initial agreement, block overlap checks, finite execution resources, trace +construction, and preservation for keys inside the footprint remain obligations. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index e199a4413..5fc15ed20 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -74,13 +74,15 @@ private def cacheFrameRoots : Array Lean.Name := #[ ``InferenceCacheAgreement.policy, ``withInferOnly_eq, ``PreservesInferenceCache.withInferOnly, ``getConst_loaded, ``IngressCacheExtension.refl, ``IngressCacheExtension.intern, ``IngressCacheExtension.trans, + ``EntriesCompatible.ofFresh, ``EntriesCompatible.ofLookups, ``ingress_runIntern_cache, ``LazyLookupFrame.refl, ``LazyLookupFrame.cache, ``LazyLookupFrame.policy ] private def cacheMapRoots : Array Lean.Name := #[ ``InferenceCacheAgreement.write, ``PreservesInferenceCache.write_other, ``InferenceCacheAgreement.clearReductionCaches, - ``IngressCacheExtension.insert, ``insertStandaloneEntries_singleton + ``IngressCacheExtension.insert, ``insertStandaloneEntries_singleton, + ``insertMutsEntriesState_cache, ``guardReserved_state, ``insertMutsEntries_cache ] private def cacheKeyRoots : Array Lean.Name := #[ @@ -97,6 +99,8 @@ private def recursiveCacheRoots : Array Lean.Name := #[ ``InferenceCacheTrace.fvarOfKey, ``InferenceCacheTrace.constOfKey, ``InferenceCacheTrace.lazyConstOfKey, ``InferenceCacheTrace.frame, ``InferenceCacheTrace.agreement, ``infer_lazyConst_cache_frame, ``CachedConstantInferenceSupport.afterLazyInference, + ``InferenceCacheTrace.verifiedConstOfKey, + ``infer_verifiedConst_cache_frame, ``CachedConstantInferenceSupport.afterVerifiedInference, ``InferenceCacheHit.afterInference, ``CachedConstantInferenceSupport.afterInference, ``CachedConstantInferenceSupport.sound_after_inference, ``BinderInference.sortAfterInference ] @@ -104,7 +108,13 @@ private def recursiveCacheRoots : Array Lean.Name := #[ private def lazyCacheRoots : Array Lean.Name := #[ ``ingressAnonStandalone_cache, ``ingressAnonAddrShallow_cache, ``lazyIngressAddr_cache, ``tryGetConst_standalone_cache, ``getConst_standalone_cache, - ``CachedConstantInferenceSupport.afterGetConst, ``CachedConstantInferenceSupport.afterFailedGetConst + ``CachedConstantInferenceSupport.afterGetConst, ``CachedConstantInferenceSupport.afterFailedGetConst, + ``BlockEntriesCompatible.ofFresh, ``BlockEntriesCompatible.ofLookups, + ``prepareAnonBlock_cache, ``ingressAnonBlockWithTrace_cache, ``ingressAnonBlock_cache, + ``ingressAnonAddrShallow_verified_cache, ``StandaloneLazySupport.toVerified, + ``lazyIngressAddr_verified_cache, ``tryGetConst_verified_cache, ``getConst_verified_cache, + ``CachedConstantInferenceSupport.afterVerifiedGetConst, + ``CachedConstantInferenceSupport.afterFailedVerifiedGetConst ] private def productionRoots : Array Lean.Name := #[ diff --git a/Ix/Kernel/Verify/Consistency/BlockCache.lean b/Ix/Kernel/Verify/Consistency/BlockCache.lean new file mode 100644 index 000000000..3945c6867 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BlockCache.lean @@ -0,0 +1,366 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.LazyCache + +/-! +# Cache preservation through verified mutual-block loading + +Preparation can change only intern tables. Publication uses the production +left-to-right insertion fold and retains old declarations when every entry +agrees with any declaration already loaded at its key. Duplicate fresh keys +do not require an injectivity assumption to preserve the original environment. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- Each proposed declaration agrees with any previously loaded declaration +at its key. Fresh entries satisfy this condition without a comparison. -/ +def EntriesCompatible (before : AnonEnv) (entries : Array Entry) : Prop := + ∀ entry ∈ entries, ∀ old, before.get? entry.1 = some old → entry.2 = old + +theorem EntriesCompatible.ofFresh {before : AnonEnv} {entries : Array Entry} + (fresh : ∀ entry ∈ entries, before.get? entry.1 = none) : + EntriesCompatible before entries := by + intro entry member old loaded + rw [fresh entry member] at loaded + contradiction + +/-- A finite check may mix fresh entries with exact repeats of old entries. -/ +theorem EntriesCompatible.ofLookups {before : AnonEnv} {entries : Array Entry} + (lookups : ∀ entry ∈ entries, before.get? entry.1 = none ∨ + before.get? entry.1 = some entry.2) : EntriesCompatible before entries := by + intro entry member old loaded + rcases lookups entry member with fresh | same + · rw [fresh] at loaded + contradiction + · exact Option.some.inj (same.symm.trans loaded) + +private theorem insert_retains_original {before current : AnonEnv} + (frame : IngressCacheExtension before current) (entry : Entry) + (compatible : ∀ old, before.get? entry.1 = some old → entry.2 = old) : + IngressCacheExtension before (current.insert entry.1 entry.2) := by + refine ⟨frame.full, frame.only, ?_, frame.freshIds⟩ + intro id concrete loaded + by_cases same : entry.1 = id + · rw [← same] at loaded ⊢ + simp [KEnv.get?, KEnv.insert, compatible concrete loaded] + · simpa [KEnv.get?, KEnv.insert, Std.HashMap.getElem?_insert, same] using + frame.constants id concrete loaded + +private theorem foldl_insert_retains_original (before : AnonEnv) (entries : List Entry) + (compatible : ∀ entry ∈ entries, ∀ old, before.get? entry.1 = some old → entry.2 = old) + (current : AnonEnv) (frame : IngressCacheExtension before current) : + IngressCacheExtension before + (entries.foldl (fun env entry => env.insert entry.1 entry.2) current) := by + induction entries generalizing current with + | nil => exact frame + | cons entry rest ih => + apply ih (fun member found => compatible member (List.mem_cons_of_mem _ found)) + exact insert_retains_original frame entry (compatible entry (List.mem_cons_self)) + +/-- Last-write-wins publication preserves the original declarations without +requiring uniqueness among fresh entries. The block map may also grow. -/ +theorem insertMutsEntriesState_cache {before : AnonEnv} {entries : Array Entry} + (compatible : EntriesCompatible before entries) : + IngressCacheExtension before (insertMutsEntriesState before entries) := by + unfold insertMutsEntriesState insertEntriesState + apply foldl_insert_retains_original before entries.toList + · intro entry member old loaded + exact compatible entry (by simpa using member) old loaded + · split <;> exact ⟨rfl, rfl, fun _ _ found => found, rfl⟩ + +/-- Reserved-address validation retains the exact environment on both outcomes. -/ +theorem guardReserved_state (entries : Array Entry) (before : AnonEnv) : + guardReserved entries before = match checkReserved entries with + | .ok _ => .ok () before + | .error err => .error err before := by + unfold guardReserved IngressM.liftExcept + cases checkReserved entries with + | error err => rfl + | ok value => cases value; rfl + +theorem insertMutsEntries_cache {before : AnonEnv} {entries : Array Entry} + (compatible : EntriesCompatible before entries) : + match insertMutsEntries entries before with + | .ok _ after | .error _ after => IngressCacheExtension before after := by + unfold insertMutsEntries + change (match (EStateM.bind (guardReserved entries) _ : IngressM Unit) before with + | .ok _ after | .error _ after => IngressCacheExtension before after) + rw [EStateM.bind, guardReserved_state] + cases checkReserved entries with + | error err => exact .refl _ + | ok value => exact insertMutsEntriesState_cache compatible + +/-- This resource describes the actual converted entries before publication; +it supplies no assertion about cache state or the final environment. -/ +def BlockEntriesCompatible (source : Ixon.Env) (constant : Ixon.Constant) + (addr : Address) (before : AnonEnv) : Prop := + ∀ trace converted, prepareAnonBlock source constant addr before = .ok trace converted → + EntriesCompatible before trace.allEntries + +theorem BlockEntriesCompatible.ofFresh {source : Ixon.Env} {constant : Ixon.Constant} + {addr : Address} {before : AnonEnv} + (fresh : ∀ trace converted, prepareAnonBlock source constant addr before = .ok trace converted → + ∀ entry ∈ trace.allEntries, before.get? entry.1 = none) : + BlockEntriesCompatible source constant addr before := + fun trace converted run => EntriesCompatible.ofFresh (fresh trace converted run) + +theorem BlockEntriesCompatible.ofLookups {source : Ixon.Env} {constant : Ixon.Constant} + {addr : Address} {before : AnonEnv} + (lookups : ∀ trace converted, prepareAnonBlock source constant addr before = .ok trace converted → + ∀ entry ∈ trace.allEntries, before.get? entry.1 = none ∨ + before.get? entry.1 = some entry.2) : BlockEntriesCompatible source constant addr before := + fun trace converted run => EntriesCompatible.ofLookups (lookups trace converted run) + +/-- All preparation effects, including failures after earlier members were +converted, are confined to intern tables. No entry is published here. -/ +theorem prepareAnonBlock_cache (source : Ixon.Env) (constant : Ixon.Constant) + (addr : Address) (before : AnonEnv) : + match prepareAnonBlock source constant addr before with + | .ok _ after | .error _ after => IngressCacheExtension before after := by + unfold prepareAnonBlock IngressM.runIntern + cases convertAnonBlock source constant addr before.intern <;> exact .intern _ _ + +theorem ingressAnonBlockWithTrace_cache (source : Ixon.Env) (constant : Ixon.Constant) + (addr : Address) (before : AnonEnv) + (compatible : BlockEntriesCompatible source constant addr before) : + match ingressAnonBlockWithTrace source constant addr before with + | .ok _ after | .error _ after => IngressCacheExtension before after := by + unfold ingressAnonBlockWithTrace + change (match (EStateM.bind (prepareAnonBlock source constant addr) _ : + IngressM AnonBlockIngressTrace) before with + | .ok _ after | .error _ after => IngressCacheExtension before after) + unfold prepareAnonBlock IngressM.runIntern EStateM.bind + dsimp only + cases converted : convertAnonBlock source constant addr before.intern with + | error err it => exact .intern _ _ + | ok trace it => + have prepared : prepareAnonBlock source constant addr before = + .ok trace {before with intern := it} := by + unfold prepareAnonBlock IngressM.runIntern + rw [converted] + have entries := compatible trace _ prepared + have published := insertMutsEntries_cache + (before := {before with intern := it}) entries + change (match EStateM.bind (insertMutsEntries trace.allEntries) + (fun _ => EStateM.pure trace) {before with intern := it} with + | .ok _ after | .error _ after => IngressCacheExtension before after) + rw [EStateM.bind] + cases run : insertMutsEntries trace.allEntries {before with intern := it} <;> + rw [run] at published <;> exact (IngressCacheExtension.intern before it).trans published + +theorem ingressAnonBlock_cache (source : Ixon.Env) (constant : Ixon.Constant) + (addr : Address) (before : AnonEnv) + (compatible : BlockEntriesCompatible source constant addr before) : + match ingressAnonBlock source constant addr before with + | .ok _ after | .error _ after => IngressCacheExtension before after := by + unfold ingressAnonBlock + change (match (EStateM.bind (ingressAnonBlockWithTrace source constant addr) _ : + IngressM (Array (KId .anon))) before with + | .ok _ after | .error _ after => IngressCacheExtension before after) + have frame := ingressAnonBlockWithTrace_cache source constant addr before compatible + rw [EStateM.bind] + cases run : ingressAnonBlockWithTrace source constant addr before <;> rw [run] at frame <;> exact frame + +/-- Only an actual block publication needs overlap agreement. Failed source +lookups and already recorded blocks require no declaration resource. -/ +def LazyMaterializationSupport (source : Ixon.Env) (addr : Address) (before : AnonEnv) : Prop := + match getConstVerified source addr true with + | .error _ | .ok none => True + | .ok (some constant) => match ingressBlockAddr? addr constant.info with + | none => True + | some blockAddr => if before.blocks.contains ⟨blockAddr, ()⟩ then True else + match getConstVerified source blockAddr true with + | .error _ | .ok none => True + | .ok (some blockConstant) => BlockEntriesCompatible source blockConstant blockAddr before + +/-- The real verified loader preserves old declarations and both caches on +every outcome, including failed preparation and successful whole-block publication. -/ +theorem ingressAnonAddrShallow_verified_cache (source : Ixon.Env) (addr : Address) + (before : AnonEnv) (materialization : LazyMaterializationSupport source addr before) + (fresh : before.get? ⟨addr, ()⟩ = none) : + match ingressAnonAddrShallow source addr true before with + | .ok _ after | .error _ after => IngressCacheExtension before after := by + unfold ingressAnonAddrShallow + change (match (EStateM.bind (IngressM.liftExcept (getConstVerified source addr true)) + _ : IngressM Bool) before with + | .ok _ after | .error _ after => IngressCacheExtension before after) + unfold LazyMaterializationSupport at materialization + cases verified : getConstVerified source addr true with + | error err => exact .refl _ + | ok optional => + cases optional with + | none => exact .refl _ + | some constant => + rw [verified] at materialization + dsimp only at materialization + dsimp only [IngressM.liftExcept, Bind.bind, Pure.pure, EStateM.pure, EStateM.bind] + cases block : ingressBlockAddr? addr constant.info with + | none => + dsimp only [Bind.bind, EStateM.bind] + have frame := ingressAnonStandalone_cache source addr constant before fresh + cases run : ingressAnonStandalone source addr constant before <;> + rw [run] at frame <;> exact frame + | some blockAddr => + rw [block] at materialization + dsimp only [Bind.bind, EStateM.bind] + rw [show (get : IngressM AnonEnv) before = .ok before before from rfl] + dsimp only + by_cases recorded : before.blocks.contains ⟨blockAddr, ()⟩ = true + · simp only [recorded, ↓reduceIte] + exact .refl _ + · simp only [recorded, Bool.false_eq_true, ↓reduceIte] at materialization ⊢ + cases parent : getConstVerified source blockAddr true with + | error err => exact .refl _ + | ok optional => + cases optional with + | none => exact .refl _ + | some blockConstant => + rw [parent] at materialization + have frame := ingressAnonBlock_cache source blockConstant blockAddr before + materialization + dsimp only [IngressM.liftExcept, Bind.bind, Pure.pure, EStateM.pure, EStateM.bind] + cases run : ingressAnonBlock source blockConstant blockAddr before <;> + rw [run] at frame <;> exact frame + +/-- The callback is the production loader with integrity verification on. +An initial loaded hit needs no publication resource; an actual miss requires +agreement only for entries of the block that would be prepared. -/ +structure VerifiedLazySupport (before : TcState .anon) (addr : Address) where + source : Ixon.Env + installed : before.lazyFault = some (fun address => ingressAnonAddrShallow source address true) + materialization : before.env.get? ⟨addr, ()⟩ = none → + LazyMaterializationSupport source addr before.env + +def StandaloneLazySupport.toVerified {before : TcState .anon} {addr : Address} + (support : StandaloneLazySupport before addr) : VerifiedLazySupport before addr := by + refine ⟨support.source, support.installed, fun _ => ?_⟩ + have standalone := support.standalone + unfold StandaloneMaterialization at standalone + unfold LazyMaterializationSupport + cases verified : getConstVerified support.source addr true with + | error err => trivial + | ok optional => + cases optional with + | none => trivial + | some constant => + rw [verified] at standalone + cases info : constant.info <;> simp only [info] at standalone + all_goals try contradiction + all_goals simp only [info, ingressBlockAddr?] + +/-- Direct fresh faults retain existing cache witnesses on every outcome, +including an address already recorded in the fault-deduplication set. -/ +theorem lazyIngressAddr_verified_cache {before : TcState .anon} {addr : Address} + (support : VerifiedLazySupport before addr) + (fresh : before.env.get? ⟨addr, ()⟩ = none) : + match TcM.lazyIngressAddr addr before with + | .ok _ after | .error _ after => LazyLookupFrame before after := by + unfold TcM.lazyIngressAddr + rw [support.installed] + dsimp only + by_cases faulted : before.faultedAddrs.contains addr = true + · rw [if_pos faulted] + exact .refl _ + · rw [if_neg faulted] + have frame := ingressAnonAddrShallow_verified_cache support.source addr before.env + (support.materialization fresh) fresh + cases run : ingressAnonAddrShallow support.source addr true before.env <;> + rw [run] at frame <;> refine ⟨frame, ?_⟩ <;> simp only [support.installed] + +/-- Try-lookup derives freshness from its own initial miss. Loaded hits, +verified source misses, conversion failures, and deduplicated faults all +retain the original cache entries and loaded declarations. -/ +theorem tryGetConst_verified_cache {before : TcState .anon} {id : KId .anon} + (support : VerifiedLazySupport before id.addr) : + match TcM.tryGetConst id before with + | .ok _ after | .error _ after => LazyLookupFrame before after := by + unfold TcM.tryGetConst + change (match (EStateM.bind (get : TcM .anon (TcState .anon)) _ : + TcM .anon (Option (KConst .anon))) before with + | .ok _ after | .error _ after => LazyLookupFrame before after) + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + dsimp only + cases loaded : before.env.get? id with + | some concrete => exact .refl _ + | none => + have fresh : before.env.get? ⟨id.addr, ()⟩ = none := by + cases id with | mk addr name => cases name; exact loaded + change (match (EStateM.bind (TcM.lazyIngressAddr id.addr) _ : + TcM .anon (Option (KConst .anon))) before with + | .ok _ after | .error _ after => LazyLookupFrame before after) + have frame := lazyIngressAddr_verified_cache support fresh + cases fault : TcM.lazyIngressAddr id.addr before with + | error err after => + rw [EStateM.bind, fault] + simpa only [fault] using frame + | ok value after => + rw [fault] at frame + rw [EStateM.bind, fault] + change (match (EStateM.bind (get : TcM .anon (TcState .anon)) _ : + TcM .anon (Option (KConst .anon))) after with + | .ok _ state | .error _ state => LazyLookupFrame before state) + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) after = .ok after after from rfl] + dsimp only + cases after.env.get? id with + | some concrete => exact frame + | none => cases before.lazyFault.isSome <;> exact frame + +/-- Hard lookup has the same frame, including the error for an absent source. -/ +theorem getConst_verified_cache {before : TcState .anon} {id : KId .anon} + (support : VerifiedLazySupport before id.addr) : + match TcM.getConst id before with + | .ok _ after | .error _ after => LazyLookupFrame before after := by + unfold TcM.getConst + change (match (EStateM.bind (TcM.tryGetConst id) _ : TcM .anon (KConst .anon)) before with + | .ok _ after | .error _ after => LazyLookupFrame before after) + have frame := tryGetConst_verified_cache support + cases tried : TcM.tryGetConst id before with + | error err after => + rw [EStateM.bind, tried] + simpa only [tried] using frame + | ok optional after => + rw [tried] at frame + rw [EStateM.bind, tried] + cases optional <;> exact frame + +/-- An earlier cached constant witness survives a successful lazy lookup of +another declaration. No new cache observation or typing premise is supplied. -/ +def CachedConstantInferenceSupport.afterVerifiedGetConst {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {before after : TcState .anon} {id requested : KId .anon} + {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} {concrete : KConst .anon} + (support : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) + (closed : (KExpr.const id arguments info).lbr = 0) + (loader : VerifiedLazySupport before requested.addr) + (run : TcM.getConst requested before = .ok concrete after) : + CachedConstantInferenceSupport resolve entries after id arguments info ref entry type := by + have frame := getConst_verified_cache loader + rw [run] at frame + exact support.transport closed (frame.cache _) frame.policy + +/-- Failed loading retains the earlier witness even when conversion has +already changed the intern tables and the fault attempt remains recorded. -/ +def CachedConstantInferenceSupport.afterFailedVerifiedGetConst {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {before after : TcState .anon} {id requested : KId .anon} + {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} {err : TcError .anon} + (support : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) + (closed : (KExpr.const id arguments info).lbr = 0) + (loader : VerifiedLazySupport before requested.addr) + (run : TcM.getConst requested before = .error err after) : + CachedConstantInferenceSupport resolve entries after id arguments info ref entry type := by + have frame := getConst_verified_cache loader + rw [run] at frame + exact support.transport closed (frame.cache _) frame.policy + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/LazyCache.lean b/Ix/Kernel/Verify/Consistency/LazyCache.lean index c7a8937bd..abc349577 100644 --- a/Ix/Kernel/Verify/Consistency/LazyCache.lean +++ b/Ix/Kernel/Verify/Consistency/LazyCache.lean @@ -85,7 +85,7 @@ theorem insertStandaloneEntries_singleton (id : KId .anon) (concrete : KConst .a | some marker => .error s!"attempted to insert constant at reserved kernel marker address {marker} ({id.addr})" before | none => .ok () ((before.insert id concrete).insertBlock id #[id]) := by - simp [insertStandaloneEntries, guardReserved] + simp [insertStandaloneEntries, guardReserved, checkReserved, IngressM.liftExcept] cases reservedMarkerName id.addr <;> rfl /-- Fresh standalone ingress preserves old declarations and both caches, @@ -129,7 +129,7 @@ theorem ingressAnonAddrShallow_cache (source : Ixon.Env) (addr : Address) (fresh : before.get? ⟨addr, ()⟩ = none) : match ingressAnonAddrShallow source addr true before with | .ok _ after | .error _ after => IngressCacheExtension before after := by - unfold ingressAnonAddrShallow + unfold ingressAnonAddrShallow ingressBlockAddr? change (match (EStateM.bind (IngressM.liftExcept (getConstVerified source addr true)) _ : IngressM Bool) before with | .ok _ after | .error _ after => IngressCacheExtension before after) diff --git a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean index 0ea5cfff4..15f1a9f99 100644 --- a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean +++ b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.BinderInference -import Ix.Kernel.Verify.Consistency.LazyCache +import Ix.Kernel.Verify.Consistency.BlockCache /-! # Cache preservation through recursive inference @@ -149,7 +149,7 @@ inductive InferenceCacheTrace : Nat → TcState .anon → KExpr .anon → Type InferenceCacheTrace fuel before (.const id arguments info) | lazyConst {fuel before id arguments info} (miss : UncachedInference before (.const id arguments info)) - (loader : StandaloneLazySupport miss.keyed id.addr) + (loader : VerifiedLazySupport miss.keyed id.addr) (resources : ∀ concrete loaded, TcM.getConst id miss.keyed = .ok concrete loaded → UniverseInstantiationSupport loaded concrete.ty arguments) : InferenceCacheTrace fuel before (.const id arguments info) @@ -214,11 +214,11 @@ def InferenceCacheTrace.constOfKey {fuel : Nat} {before keyed : TcState .anon} /-- Construct a constant leaf that may load its declaration. Cache selection comes from the actual maps; walker resources concern the returned lookup state. -/ -def InferenceCacheTrace.lazyConstOfKey {fuel : Nat} {before keyed : TcState .anon} +def InferenceCacheTrace.verifiedConstOfKey {fuel : Nat} {before keyed : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} {key : Address × Address} (keyRun : TcM.inferKey (.const id arguments info) before = .ok key keyed) - (loader : StandaloneLazySupport keyed id.addr) + (loader : VerifiedLazySupport keyed id.addr) (resources : ∀ concrete loaded, TcM.getConst id keyed = .ok concrete loaded → UniverseInstantiationSupport loaded concrete.ty arguments) : InferenceCacheTrace fuel before (.const id arguments info) := by @@ -227,6 +227,17 @@ def InferenceCacheTrace.lazyConstOfKey {fuel : Nat} {before keyed : TcState .ano · exact .lazyConst miss (by simpa only [stateEq] using loader) (by simpa only [stateEq] using resources) +/-- Standalone loaders remain a special case of the verified leaf. -/ +def InferenceCacheTrace.lazyConstOfKey {fuel : Nat} {before keyed : TcState .anon} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {key : Address × Address} + (keyRun : TcM.inferKey (.const id arguments info) before = .ok key keyed) + (loader : StandaloneLazySupport keyed id.addr) + (resources : ∀ concrete loaded, TcM.getConst id keyed = .ok concrete loaded → + UniverseInstantiationSupport loaded concrete.ty arguments) : + InferenceCacheTrace fuel before (.const id arguments info) := + .verifiedConstOfKey keyRun loader.toVerified resources + /-- Every successful call in the finite tree preserves entries outside its computed write footprint and retains the loaded declarations and policy. The proof follows the recursive calls, then their real outer cache insertion. -/ @@ -258,7 +269,7 @@ theorem InferenceCacheTrace.frame {fuel : Nat} {before after : TcState .anon} apply infer_miss_frame miss (Ne.symm outside) accepted intro middle run obtain ⟨concrete, foundState, got, _, instantiated⟩ := inferUncached_const_instantiation run - have lookup := getConst_standalone_cache loader + have lookup := getConst_verified_cache loader rw [got] at lookup have resource := resources concrete foundState got have post := TcM.instantiateUnivParams_wf resource.faithful @@ -329,13 +340,13 @@ theorem InferenceCacheTrace.frame {fuel : Nat} {before after : TcState .anon} exact ⟨(domainFrame.trans (opening.trans bodyFrame)).trans (.of_eq rfl rfl rfl), bodyPolicy.trans ((openBinder_policy trace.openRun).trans domainPolicy)⟩ -/-- A standalone constant call needs no separately constructed operational +/-- A verified constant call needs no separately constructed operational tree: the real key and cache selection build its hit or lazy-miss leaf. -/ -theorem infer_lazyConst_cache_frame {fuel : Nat} {before keyed after : TcState .anon} +theorem infer_verifiedConst_cache_frame {fuel : Nat} {before keyed after : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} {key watched : Address × Address} {result : KExpr .anon} (keyRun : TcM.inferKey (.const id arguments info) before = .ok key keyed) - (different : key ≠ watched) (loader : StandaloneLazySupport keyed id.addr) + (different : key ≠ watched) (loader : VerifiedLazySupport keyed id.addr) (resources : ∀ concrete loaded, TcM.getConst id keyed = .ok concrete loaded → UniverseInstantiationSupport loaded concrete.ty arguments) (accepted : RecM.infer (.const id arguments info) (methodsN fuel) before = .ok result after) : @@ -348,6 +359,18 @@ theorem infer_lazyConst_cache_frame {fuel : Nat} {before keyed after : TcState . apply tree.frame _ accepted simpa only [tree, InferenceCacheTrace.writes, List.mem_singleton, keyEq] using Ne.symm different +/-- Compatibility wrapper for a standalone constant call. -/ +theorem infer_lazyConst_cache_frame {fuel : Nat} {before keyed after : TcState .anon} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {key watched : Address × Address} {result : KExpr .anon} + (keyRun : TcM.inferKey (.const id arguments info) before = .ok key keyed) + (different : key ≠ watched) (loader : StandaloneLazySupport keyed id.addr) + (resources : ∀ concrete loaded, TcM.getConst id keyed = .ok concrete loaded → + UniverseInstantiationSupport loaded concrete.ty arguments) + (accepted : RecM.infer (.const id arguments info) (methodsN fuel) before = .ok result after) : + InferenceCacheFrame watched before after ∧ after.inferOnly = before.inferOnly := + infer_verifiedConst_cache_frame keyRun different loader.toVerified resources accepted + /-- Concrete agreement at an unwritten key is retained by the entire tree. -/ theorem InferenceCacheTrace.agreement {fuel : Nat} {before after : TcState .anon} {term result expected : KExpr .anon} (tree : InferenceCacheTrace fuel before term) @@ -383,7 +406,27 @@ def CachedConstantInferenceSupport.afterInference {β : Type u} support.transport closed (tree.frame outside accepted).1 (tree.frame outside accepted).2 /-- Reuse the complete earlier witness after inference may load another -standalone. The new cache selection and declaration extension are derived. -/ +dependency, including a mutual block. Cache selection and declaration extension are derived. -/ +def CachedConstantInferenceSupport.afterVerifiedInference {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {fuel : Nat} {before keyed after : TcState .anon} + {id requested : KId .anon} {arguments requestedArguments : Array (KUniv .anon)} + {info requestedInfo : ExprInfo .anon} {key : Address × Address} {result : KExpr .anon} + {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} + (support : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) + (closed : (KExpr.const id arguments info).lbr = 0) + (keyRun : TcM.inferKey (.const requested requestedArguments requestedInfo) before = .ok key keyed) + (different : key ≠ ((KExpr.const id arguments info).addr, emptyCtxAddr)) + (loader : VerifiedLazySupport keyed requested.addr) + (resources : ∀ concrete loaded, TcM.getConst requested keyed = .ok concrete loaded → + UniverseInstantiationSupport loaded concrete.ty requestedArguments) + (accepted : RecM.infer (.const requested requestedArguments requestedInfo) + (methodsN fuel) before = .ok result after) : + CachedConstantInferenceSupport resolve entries after id arguments info ref entry type := + let frame := infer_verifiedConst_cache_frame keyRun different loader resources accepted + support.transport closed frame.1 frame.2 + +/-- Standalone witness reuse is a special case of verified loading. -/ def CachedConstantInferenceSupport.afterLazyInference {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {fuel : Nat} {before keyed after : TcState .anon} @@ -400,8 +443,7 @@ def CachedConstantInferenceSupport.afterLazyInference {β : Type u} (accepted : RecM.infer (.const requested requestedArguments requestedInfo) (methodsN fuel) before = .ok result after) : CachedConstantInferenceSupport resolve entries after id arguments info ref entry type := - let frame := infer_lazyConst_cache_frame keyRun different loader resources accepted - support.transport closed frame.1 frame.2 + support.afterVerifiedInference closed keyRun different loader.toVerified resources accepted /-- A later constant's actual returned type inherits typing from its earlier witness after recursive inference; no semantic premise about caches is added. -/ diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 8c1976d3e..6b5499e41 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -950,9 +950,210 @@ private def lazyCacheCases : TestSeq := ++ test "lazy cache: a failed recursor retains partial conversion and the inference-only witness" (cacheAcrossLazyFailure 3 true) +/-- Populate both partitions so every block regression checks two live slots. -/ +private def warmBothCaches (term : KExpr .anon) : RecM .anon (KExpr .anon) := do + let policy := (← get).inferOnly + modify fun state => {state with inferOnly := true} + let _ ← RecM.inferCall term + modify fun state => {state with inferOnly := false} + let result ← RecM.inferCall term + modify fun state => {state with inferOnly := policy} + return result + +private def warmSlotsRetained (key : Address × Address) (before after : TcState .anon) : Bool := + before.env.inferCache[key]?.isSome && before.env.inferOnlyCache[key]?.isSome && + after.env.inferCache[key]?.map (·.addr) == before.env.inferCache[key]?.map (·.addr) && + after.env.inferOnlyCache[key]?.map (·.addr) == before.env.inferOnlyCache[key]?.map (·.addr) + +private def cacheBlock (recursor : Bool) : Ixon.Constant := + let type := Ixon.Expr.leanAll (.sort 0) (.leanAll (.var 0) (.var 1)) + let value := Ixon.Expr.leanLam (.sort 0) (.leanLam (.var 0) (.var 0)) + let first : Ixon.MutConst := .defn ⟨.defn, .safe, 0, .share 0, .recur 1 #[]⟩ + let second : Ixon.MutConst := if recursor then + .recr ⟨false, false, 0, 0, 0, 0, 0, .share 0, #[⟨0, .share 1⟩, ⟨1, .share 1⟩]⟩ + else .defn ⟨.defn, .safe, 0, .share 0, .share 1⟩ + ⟨.muts #[first, second], #[type, value], #[], #[.succ .zero]⟩ + +/-- A projection loads the whole block. A partial preload warms the first +member without recording the block; publication must preserve that member too. -/ +private def cacheAcrossBlock (recursor preloaded : Bool) (shape : Nat) (inferOnly : Bool) : Bool := + let (source, warmAddr) := polymorphicIdentity + let block := cacheBlock recursor + let (source, blockAddr) := storeMutsWithProjs source block + let first : KId .anon := ⟨defnProjAddr blockAddr 0, ()⟩ + let second : KId .anon := ⟨if recursor then recrProjAddr blockAddr 1 else defnProjAddr blockAddr 1, ()⟩ + let requested := if preloaded || recursor then second else first + let sibling := if preloaded || recursor then first else second + let warmId : KId .anon := ⟨warmAddr, ()⟩ + let warm := KExpr.mkConst (m := .anon) warmId #[levelOne] + let cold := KExpr.mkConst (m := .anon) requested #[] + let sortType := KExpr.mkSort (m := .anon) levelOne + let body := KExpr.mkLam () () sortType (.mkLam () () (.mkVar 0 ()) + (.mkApp (.mkApp cold (.mkVar 1 ())) (.mkVar 0 ()))) + let term := if shape == 0 then cold else if shape == 1 then body else + KExpr.mkApp (.mkApp (.mkConst warmId #[levelTwo]) identityType) body + let action : RecM .anon Bool := do + let expected ← warmBothCaches warm + let key ← TcM.inferKey warm + if preloaded then + let state ← get + let .ok trace converted := prepareAnonBlock source block blockAddr state.env | return false + let some entry := trace.allEntries[0]? | return false + modify fun state => {state with env := converted.insert entry.1 entry.2} + let _ ← warmBothCaches (.mkConst first #[]) + let firstKey ← TcM.inferKey (.mkConst first #[]) + let before ← get + let result ← RecM.inferCall term + let loaded ← get + let reused ← RecM.inferCall warm + let replay ← RecM.inferCall term + let beforeDedup ← get + let _ ← liftM (TcM.lazyIngressAddr (m := .anon) sibling.addr) + let after ← get + let overlap := if preloaded then + warmSlotsRetained firstKey before loaded && + match before.env.get? first, loaded.env.get? first with + | some (.defn (kind := k) (safety := s) (hints := h) (lvls := l) (ty := t) (val := v) (block := b) ..), + some (.defn (kind := k') (safety := s') (hints := h') (lvls := l') (ty := t') (val := v') (block := b') ..) => + k == k' && s == s' && h == h' && l == l' && t.addr == t'.addr && v.addr == v'.addr && b == b' + | _, _ => false + else true + return result.addr == expected.addr && expected.addr == identityType.addr && + reused.addr == expected.addr && replay.addr == result.addr && overlap && + warmSlotsRetained key before loaded && (before.env.get? requested).isNone && + !before.env.blocks.contains ⟨blockAddr, ()⟩ && + (loaded.env.get? first).isSome && (loaded.env.get? second).isSome && + loaded.env.consts.size == before.env.consts.size + (if preloaded then 1 else 2) && + loaded.env.blocks[(⟨blockAddr, ()⟩ : KId .anon)]?.any (fun members => members == #[first, second]) && + !beforeDedup.faultedAddrs.contains sibling.addr && after.faultedAddrs.contains sibling.addr && + after.env.consts.size == beforeDedup.env.consts.size && + after.env.intern.exprs.size == beforeDedup.env.intern.exprs.size && + after.env.intern.univs.size == beforeDedup.env.intern.univs.size && + after.env.inferCache.size == beforeDedup.env.inferCache.size && + after.env.inferOnlyCache.size == beforeDedup.env.inferOnlyCache.size && + after.env.nextFVarId == beforeDedup.env.nextFVarId && after.inferOnly == inferOnly + match TcM.runRec action { TcState.newLazyAnon source with inferOnly } with + | .ok passed _ => passed + | .error _ _ => false + +/-- Inductive and constructor projections exercise flattened publication. +This tests loader framing, without claiming inductive admission soundness. -/ +private def cacheAcrossInductiveBlock (constructor : Bool) : Bool := + let (source, blockAddr) := envInductive + let (source, warmAddr) := storeConst source + ⟨.axio ⟨false, 1, .leanAll (.sort 0) (.leanAll (.var 0) (.var 1))⟩, #[], #[], #[.var 0]⟩ + let induct : KId .anon := ⟨indcProjAddr blockAddr 0, ()⟩ + let ctor : KId .anon := ⟨ctorProjAddr blockAddr 0 0, ()⟩ + let warm := KExpr.mkConst (m := .anon) ⟨warmAddr, ()⟩ #[levelOne] + let action : RecM .anon Bool := do + let expected ← warmBothCaches warm + let key ← TcM.inferKey warm + let before ← get + let result ← RecM.inferCall (.mkConst (if constructor then ctor else induct) #[]) + let loaded ← get + let reused ← RecM.inferCall warm + return warmSlotsRetained key before loaded && reused.addr == expected.addr && + result.addr == (if constructor then (KExpr.mkConst induct #[]).addr else sort1K.addr) && + loaded.env.consts.size == before.env.consts.size + 2 && + loaded.env.blocks[(⟨blockAddr, ()⟩ : KId .anon)]?.any (fun members => members == #[induct, ctor]) && + match loaded.env.get? induct, loaded.env.get? ctor with + | some (.indc (ctors := ctors) ..), some (.ctor (induct := parent) ..) => + ctors == #[ctor] && parent == induct + | _, _ => false + match TcM.runRec action (TcState.newLazyAnon source) with + | .ok passed _ => passed + | .error _ _ => false + +/-- A failure can retain partial conversion, or even a complete publication +when the requested block root has no declaration entry. Retries deduplicate. -/ +private def cacheAcrossBlockFailure (kind : Nat) : Bool := + let (source, warmAddr) := polymorphicIdentity + let level : Ixon.Univ := .succ (.succ (.succ (.succ (.succ .zero)))) + let first : Ixon.MutConst := .defn ⟨.defn, .safe, 0, .sort 0, .sort 0⟩ + let second : Ixon.MutConst := .defn ⟨.defn, .safe, 0, .sort 0, + if kind == 3 then .share 9 else .sort 0⟩ + let (source, blockAddr) := storeMutsWithProjs source ⟨.muts #[first, second], #[], #[], #[level]⟩ + let firstAddr := defnProjAddr blockAddr 0 + let secondAddr := defnProjAddr blockAddr 1 + let source := if kind == 0 then {source with consts := source.consts.erase blockAddr} + else if kind == 1 then + {source with consts := source.consts.insert blockAddr (.ofConstant (lazyCacheDependency 0))} + else if kind == 2 then {source with consts := source.consts.erase secondAddr} else source + let requested : KId .anon := ⟨if kind == 4 then blockAddr else firstAddr, ()⟩ + let warm := KExpr.mkConst (m := .anon) ⟨warmAddr, ()⟩ #[levelOne] + let action : RecM .anon Bool := do + let expected ← warmBothCaches warm + let key ← TcM.inferKey warm + let before ← get + let rejected ← try + let _ ← liftM (TcM.getConst requested) + pure false + catch err => + let fragment := if kind == 0 then "absent" else if kind == 1 then "fails integrity check" + else if kind == 2 then "not present in env" else if kind == 3 then "invalid Share index 9" + else "unknown constant" + pure (((toString err).splitOn fragment).length > 1) + let failed ← get + let retry ← try + let _ ← liftM (TcM.getConst requested) + pure false + catch err => + match err with + | .unknownConst addr => pure (addr == requested.addr) + | _ => pure false + let reused ← RecM.inferCall warm + let after ← get + return rejected && retry && reused.addr == expected.addr && warmSlotsRetained key before failed && + (failed.env.get? requested).isNone && failed.faultedAddrs.contains requested.addr && + failed.env.consts.size == before.env.consts.size + (if kind == 4 then 2 else 0) && + failed.env.blocks.size == before.env.blocks.size + (if kind == 4 then 1 else 0) && + (if kind == 4 then (failed.env.get? ⟨firstAddr, ()⟩).isSome && + (failed.env.get? ⟨secondAddr, ()⟩).isSome else true) && + failed.env.inferCache.size == before.env.inferCache.size && + failed.env.inferOnlyCache.size == before.env.inferOnlyCache.size && + (if kind >= 2 then failed.env.intern.exprs.size > before.env.intern.exprs.size && + failed.env.intern.univs.size > before.env.intern.univs.size + else failed.env.intern.exprs.size == before.env.intern.exprs.size && + failed.env.intern.univs.size == before.env.intern.univs.size) && + failed.env.nextFVarId == before.env.nextFVarId && failed.inferOnly == before.inferOnly && + after.env.consts.size == failed.env.consts.size && + after.env.intern.exprs.size == failed.env.intern.exprs.size && + after.env.intern.univs.size == failed.env.intern.univs.size + match TcM.runRec action (TcState.newLazyAnon source) with + | .ok passed _ => passed + | .error _ _ => false + +private def blockCacheCases : TestSeq := + test "block cache: a definition projection loads both members and preserves both warm slots" + (cacheAcrossBlock false false 0 false) + ++ test "block cache: partial publication retains a cached sibling in full mode" + (cacheAcrossBlock false true 0 false) + ++ test "block cache: partial publication retains a cached sibling in inference-only mode" + (cacheAcrossBlock false true 0 true) + ++ test "block cache: a lambda loads a mutual dependency and preserves both warm slots" + (cacheAcrossBlock false false 1 false) + ++ test "block cache: an application loads a mutual dependency inside its lambda argument" + (cacheAcrossBlock false false 2 false) + ++ test "block cache: a recursor projection loads the whole block and converts its rules" + (cacheAcrossBlock true false 0 false) + ++ test "block cache: an inductive projection publishes its constructors and retains warm slots" + (cacheAcrossInductiveBlock false) + ++ test "block cache: a constructor projection publishes its inductive and retains warm slots" + (cacheAcrossInductiveBlock true) + ++ test "block cache: a missing parent retains both warm slots" + (cacheAcrossBlockFailure 0) + ++ test "block cache: a corrupt parent retains both warm slots" + (cacheAcrossBlockFailure 1) + ++ test "block cache: a missing later projection retains partial conversion without publication" + (cacheAcrossBlockFailure 2) + ++ test "block cache: a failed later member retains partial conversion without publication" + (cacheAcrossBlockFailure 3) + ++ test "block cache: an unknown root retains a completed block publication and both warm slots" + (cacheAcrossBlockFailure 4) + public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, - lazyCacheCases] + lazyCacheCases, blockCacheCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 1d5099bf8..5ec4c8b84 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -125,11 +125,20 @@ increasing sequence of strongly inaccessible cardinals. fields except the environment and fault history are retained. The proof includes absent sources, integrity/parse errors, partial conversion failures, the reserved-address guard, and fault deduplication. +- `getConst_verified_cache` extends that result to mutual-block loading. + Block preparation accesses only intern tables, including conversion of + inductives, constructors, and recursor rules. Publication follows the actual + insertion fold and preserves old declarations when each converted entry + agrees with any old declaration at its key. Fresh entries and exact repeats + satisfy this condition; duplicate fresh keys need no uniqueness premise. + The proof includes parent lookup failures, block deduplication, partial + preparation failures, and lookup errors after successful publication. `InferenceCacheTrace.lazyConst` composes this result with universe substitution - at the actual post-lookup state. `CachedConstantInferenceSupport.afterLazyInference` + at the actual post-lookup state. `CachedConstantInferenceSupport.afterVerifiedInference` reuses an earlier witness after a constant call loads another dependency; - the recursive transport also covers loads inside application and binder trees. - Mutual-block publication and the other recursive paths remain open. + recursive transport also covers loads inside application and binder trees. + Block overlap agreement remains a data premise. The source admission and + other recursive paths still need proofs. - `checkEnvAnon_atomic_preserves_model` connects a supported production environment run to model extension. `checkEnvAnon_atomic_no_false` excludes a declaration at an axiom type interpreted as empty, including False. @@ -201,17 +210,20 @@ def useF (x : T.{1}) : T.{1} := f.{1} x preserve their key's entries and each previously loaded declaration. Actual sort and already-loaded constant inference provide frames for other keys. These results reduce repeated witnesses; they do not yet derive initial agreement. - Verified standalone lookup supplies an extension frame on every outcome, - retaining partial intern progress on error. The installed callback must be - the actual verified loader, and successful materialization must be standalone. + Verified lookup supplies an extension frame on every outcome, retaining + partial intern progress on error. The installed callback must be the actual + verified loader. Standalone registration uses freshness from the lookup miss; + block publication requires pointwise agreement between its prepared entries + and any already loaded declarations at their keys. This admits partially + loaded blocks without assuming a cache frame or a final-state invariant. A separate `InferenceCacheTrace` derives a frame for an entire supported recursive call at any key outside its computed writes. It shares the existing application and binder execution traces and additionally follows lambda-domain inference. Constant misses use already-loaded declarations or verified - standalone loading, with finite universe-walker resources after lookup; + standalone/block loading, with finite universe-walker resources after lookup; applications use full mode and hash conversion. The new dependency's semantic - source agreement and intern coherence remain explicit resources. Mutual-block - loading, preservation at written keys, and automatic trace construction remain open. + source agreement, block overlap checks, and intern coherence remain explicit + resources. Preservation at written keys and automatic trace construction remain open. The operational trace can frame any selected cache hit, while semantic typing of composite hits remains outside `BinderInference`. - Binder definitions supply finite inference trees for both the value and its @@ -307,7 +319,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 197 exact theorem boundaries. The production +The consistency target checks 217 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -355,6 +367,13 @@ directly or inside a lambda/application, and reuse the original witness. They cover all four standalone conversion forms, both inference policies, missing and corrupt sources, definition and recursor failures with retained intern progress, and deduplicated retries. +Block-loading regressions populate both cache partitions and retain them through +fresh and partially loaded mutual blocks, recursive function bodies, and all +four projection forms. They check cached sibling preservation, block-level +deduplication, missing or corrupt parents, preparation failures after earlier +members converted, and an unknown block-root lookup that retains completed +publication. These exercise loading and cache preservation; they do not establish +semantic admission of inductives or recursors. ## Certified host adapters @@ -386,6 +405,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Cache invariants and sort cache typing | [`Consistency/InferenceCache.lean`](../Ix/Kernel/Verify/Consistency/InferenceCache.lean), [`SortCache.lean`](../Ix/Kernel/Verify/Consistency/SortCache.lean) | | Recursive cache preservation and witness reuse | [`Consistency/RecursiveCache.lean`](../Ix/Kernel/Verify/Consistency/RecursiveCache.lean) | | Verified standalone lazy loading and cache frames | [`Consistency/LazyCache.lean`](../Ix/Kernel/Verify/Consistency/LazyCache.lean) | +| Mutual-block publication and verified lookup frames | [`Consistency/BlockCache.lean`](../Ix/Kernel/Verify/Consistency/BlockCache.lean) | | Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`Application.lean`](../Ix/Kernel/Verify/Consistency/Application.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | | Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | diff --git a/docs/theory.md b/docs/theory.md index 56e653502..85f6561ba 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -37,12 +37,15 @@ agreement constructs sort leaves. A finite operational trace now carries preservation through recursive applications, dependent types, and full-mode lambdas for keys absent from the recorded writes. It derives later constant witnesses and sort leaves without repeated cache-hit observations. Frames allow -new declarations while retaining old ones. The actual verified standalone loader -preserves inference caches on success and failure, including partial intern -progress and deduplicated faults. Recursive constant leaves use this result, -so an earlier witness survives inference that loads another dependency. -Initial agreement, post-lookup interning resources, trace construction, -mutual-block loading, and preservation for keys that are written remain obligations. +new declarations while retaining old ones. The actual verified loader preserves +inference caches on success and failure, including partial intern progress and +deduplicated faults. This covers standalone and mutual-block loading. Block +publication requires every prepared entry to agree with any old declaration at +its key, admitting fresh entries and exact repeats in a partially loaded block. +Recursive constant leaves use this result, so an earlier witness survives +inference that loads another dependency. Initial agreement, block overlap checks, +post-lookup interning resources, trace construction, and preservation for keys +that are written remain obligations. Full checker consistency and compiler/backend refinement remain open. From b53bd32be7d8cc844c39de4bfce2eb289ecf09cf Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 19:36:54 -0400 Subject: [PATCH 16/63] Preserve intern coherence through lazy loading --- Ix/Kernel/Ingress.lean | 344 ++++++---- Ix/Kernel/Verify/Consistency.lean | 7 + Ix/Kernel/Verify/Consistency/Audit.lean | 21 +- .../Verify/Consistency/IngressCoherence.lean | 645 ++++++++++++++++++ .../Verify/Consistency/RecursiveCache.lean | 109 ++- Tests/Ix/Kernel/Consistency.lean | 149 +++- docs/kernel-verification.md | 30 +- docs/theory.md | 10 +- 8 files changed, 1164 insertions(+), 151 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/IngressCoherence.lean diff --git a/Ix/Kernel/Ingress.lean b/Ix/Kernel/Ingress.lean index 438044598..50f2ca1ec 100644 --- a/Ix/Kernel/Ingress.lean +++ b/Ix/Kernel/Ingress.lean @@ -168,43 +168,72 @@ inductive UFrame where | imax deriving Inhabited -/-- Convert one universe tree (iterative). Uses the *simplifying* - `mkMax`/`mkIMax` smart constructors — reduced-node addresses must match - the Rust kernel node-for-node — and interns every node. -/ -def convertUnivTree (root : Ixon.Univ) : InternIngressM (KUniv .anon) := do - let mut stack : Array UFrame := #[.process root] - let mut values : Array (KUniv .anon) := #[] - while !stack.isEmpty do - let frame := stack.back! - stack := stack.pop - match frame with - | .process u => - match u with - | .zero => - values := values.push (← InternIngressM.internU .mkZero) - | .succ inner => - stack := stack.push .succ |>.push (.process inner) - | .max a b => - stack := stack.push .max |>.push (.process b) |>.push (.process a) - | .imax a b => - stack := stack.push .imax |>.push (.process b) |>.push (.process a) - | .var idx => - values := values.push (← InternIngressM.internU (.mkParam idx ())) - | .succ => - let inner := values.back! - values := values.pop - values := values.push (← InternIngressM.internU (.mkSucc inner)) - | .max => - let b := values.back!; values := values.pop - let a := values.back!; values := values.pop - values := values.push (← InternIngressM.internU (.mkMax a b)) - | .imax => - let b := values.back!; values := values.pop - let a := values.back!; values := values.pop - values := values.push (← InternIngressM.internU (.mkIMax a b)) - match values.back? with - | some v => return v - | none => throw "ingressUnivTree: empty result stack" +/-- Count universe nodes with a tail-recursive worklist, so deriving the +conversion bound does not put deep source trees on the runtime call stack. -/ +def univIngressSize (root : Ixon.Univ) : Nat := go [root] 0 where + go (pending : List Ixon.Univ) (count : Nat) : Nat := + match pending with + | [] => count + | .zero :: rest | .var _ :: rest => go rest (count + 1) + | .succ inner :: rest => go (inner :: rest) (count + 1) + | .max left right :: rest | .imax left right :: rest => + go (left :: right :: rest) (count + 1) + termination_by (pending.map sizeOf).sum + decreasing_by all_goals simp_wf <;> omega + +/-- One frame of the universe stack machine, in the same traversal order +as expression conversion. All mutable effects are interning operations. -/ +def convertUnivStep (stack : Array UFrame) (values : Array (KUniv .anon)) : + InternIngressM (Array UFrame × Array (KUniv .anon)) := do + let frame := stack.back! + let mut stack := stack.pop + let mut values := values + match frame with + | .process u => + match u with + | .zero => + values := values.push (← InternIngressM.internU .mkZero) + | .succ inner => + stack := stack.push .succ |>.push (.process inner) + | .max a b => + stack := stack.push .max |>.push (.process b) |>.push (.process a) + | .imax a b => + stack := stack.push .imax |>.push (.process b) |>.push (.process a) + | .var idx => + values := values.push (← InternIngressM.internU (.mkParam idx ())) + | .succ => + let inner := values.back! + values := values.pop + values := values.push (← InternIngressM.internU (.mkSucc inner)) + | .max => + let b := values.back!; values := values.pop + let a := values.back!; values := values.pop + values := values.push (← InternIngressM.internU (.mkMax a b)) + | .imax => + let b := values.back!; values := values.pop + let a := values.back!; values := values.pop + values := values.push (← InternIngressM.internU (.mkIMax a b)) + return (stack, values) + +/-- Bounded tail loop for universe conversion. Each source node needs at +most a processing frame and a constructor frame. -/ +def convertUnivLoop (fuel : Nat) (stack : Array UFrame) (values : Array (KUniv .anon)) : + InternIngressM (KUniv .anon) := do + if stack.isEmpty then + match values.back? with + | some value => return value + | none => throw "ingressUnivTree: empty result stack" + else + match fuel with + | 0 => throw "ingressUnivTree: conversion step bound exhausted" + | fuel + 1 => + let (stack, values) ← convertUnivStep stack values + convertUnivLoop fuel stack values + +/-- Convert one universe tree (iterative). Uses the simplifying smart +constructors and interns every node. The bound comes from the source tree. -/ +def convertUnivTree (root : Ixon.Univ) : InternIngressM (KUniv .anon) := + convertUnivLoop (2 * univIngressSize root) #[.process root] #[] /-- Environment-threading interface for universe-tree conversion. -/ def ingressUnivTree (root : Ixon.Univ) : IngressM (KUniv .anon) := @@ -267,113 +296,148 @@ inductive EFrame where | cacheShare (idx : UInt64) deriving Inhabited -/-- Convert one Ixon expression (explicit stack machine). - - Resolution: `sort i → univs[i]`; `ref i us → refs[i]` KId + univ args; - `recur i us → mutCtx[i]` (the sibling *projection* KId); `nat/str i → - refs[i]` names a blob, decoded from the env (the blob address is the hash - payload; the value itself is never hashed); `share i` expands - transparently against the sharing table (cached by index). -/ -def convertExpr (ixonEnv : Ixon.Env) (ctx : IngressCtx) (root : Ixon.Expr) : - InternConvM (KExpr .anon) := do - let mut stack : Array EFrame := #[.process root] - let mut values : Array (KExpr .anon) := #[] - while !stack.isEmpty do - let frame := stack.back! - stack := stack.pop - match frame with - | .process e => - match e with - | .share idx => - if let some cached := (← get).exprCache[idx]? then - values := values.push cached - else - let some expansion := ctx.sharing[idx.toNat]? - | throw s!"invalid Share index {idx}" - stack := stack.push (.cacheShare idx) |>.push (.process expansion) - | .var idx => - values := values.push (← liftM (InternIngressM.internE (.mkVar idx ()))) - | .sort uidx => - let u ← convertUnivIdx ctx uidx - values := values.push (← liftM (InternIngressM.internE (.mkSort u))) - | .ref refIdx univIdxs => - let some addr := ctx.refs[refIdx.toNat]? - | throw s!"invalid Ref index {refIdx}" - let univs ← convertUnivArgs ctx univIdxs - values := values.push - (← liftM (InternIngressM.internE (.mkConst ⟨addr, ()⟩ univs))) - | .recur recIdx univIdxs => - let some mid := ctx.mutCtx[recIdx.toNat]? - | throw s!"invalid Rec index {recIdx}" - let univs ← convertUnivArgs ctx univIdxs - values := values.push - (← liftM (InternIngressM.internE (.mkConst mid univs))) - | .nat blobIdx => - let some blobAddr := ctx.refs[blobIdx.toNat]? - | throw s!"invalid Nat blob ref index {blobIdx}" - let some bytes := ixonEnv.getBlob? blobAddr - | throw s!"missing Nat blob {blobAddr}" - let val := Nat.fromBytesLE bytes.data - values := values.push - (← liftM (InternIngressM.internE (.mkNat val blobAddr))) - | .str blobIdx => - let some blobAddr := ctx.refs[blobIdx.toNat]? - | throw s!"invalid Str blob ref index {blobIdx}" - let some bytes := ixonEnv.getBlob? blobAddr - | throw s!"missing Str blob {blobAddr}" - let some val := String.fromUTF8? bytes - | throw s!"Str blob {blobAddr} is not valid UTF-8" - values := values.push - (← liftM (InternIngressM.internE (.mkStr val blobAddr))) - | .app f a => - stack := stack.push .appDone |>.push (.process a) |>.push (.process f) - | .lam _ ty body => - stack := stack.push .lamDone |>.push (.process body) - |>.push (.process ty) - | .all _ _ ty body => - stack := stack.push .allDone |>.push (.process body) - |>.push (.process ty) - | .letE nd ty val body => - stack := stack.push (.letDone nd) |>.push (.process body) - |>.push (.process val) |>.push (.process ty) - | .prj typeRefIdx field val => - let some typeAddr := ctx.refs[typeRefIdx.toNat]? - | throw s!"invalid Prj type ref index {typeRefIdx}" - stack := stack.push (.prjDone ⟨typeAddr, ()⟩ field) - |>.push (.process val) - | .appDone => - let a := values.back!; values := values.pop - let f := values.back!; values := values.pop - values := values.push (← liftM (InternIngressM.internE (.mkApp f a))) - | .lamDone => - let body := values.back!; values := values.pop - let ty := values.back!; values := values.pop +/-- Count the source expression nodes, treating sharing indices as leaves. +The worklist keeps this pass tail-recursive even for deeply nested terms. -/ +def exprIngressSize (pending : List Ixon.Expr) : Nat := go pending 0 where + go (pending : List Ixon.Expr) (count : Nat) : Nat := + match pending with + | [] => count + | .share _ :: rest | .var _ :: rest | .sort _ :: rest + | .ref _ _ :: rest | .recur _ _ :: rest | .nat _ :: rest | .str _ :: rest => + go rest (count + 1) + | .app left right :: rest | .lam _ left right :: rest + | .all _ _ left right :: rest => go (left :: right :: rest) (count + 1) + | .letE _ type value body :: rest => go (type :: value :: body :: rest) (count + 1) + | .prj _ _ value :: rest => go (value :: rest) (count + 1) + termination_by (pending.map sizeOf).sum + decreasing_by all_goals (simp_wf; omega) + +/-- One expression frame. Sharing and universe caches remain local to the +constant; failed resolution retains any earlier intern-table progress. -/ +def convertExprStep (ixonEnv : Ixon.Env) (ctx : IngressCtx) + (stack : Array EFrame) (values : Array (KExpr .anon)) : + InternConvM (Array EFrame × Array (KExpr .anon)) := do + let frame := stack.back! + let mut stack := stack.pop + let mut values := values + match frame with + | .process e => + match e with + | .share idx => + if let some cached := (← get).exprCache[idx]? then + values := values.push cached + else + let some expansion := ctx.sharing[idx.toNat]? + | throw s!"invalid Share index {idx}" + stack := stack.push (.cacheShare idx) |>.push (.process expansion) + | .var idx => + values := values.push (← liftM (InternIngressM.internE (.mkVar idx ()))) + | .sort uidx => + let u ← convertUnivIdx ctx uidx + values := values.push (← liftM (InternIngressM.internE (.mkSort u))) + | .ref refIdx univIdxs => + let some addr := ctx.refs[refIdx.toNat]? + | throw s!"invalid Ref index {refIdx}" + let univs ← convertUnivArgs ctx univIdxs values := values.push - (← liftM (InternIngressM.internE (.mkLam () () ty body))) - | .allDone => - let body := values.back!; values := values.pop - let ty := values.back!; values := values.pop + (← liftM (InternIngressM.internE (.mkConst ⟨addr, ()⟩ univs))) + | .recur recIdx univIdxs => + let some mid := ctx.mutCtx[recIdx.toNat]? + | throw s!"invalid Rec index {recIdx}" + let univs ← convertUnivArgs ctx univIdxs values := values.push - (← liftM (InternIngressM.internE (.mkAll () () ty body))) - | .letDone nd => - let body := values.back!; values := values.pop - let val := values.back!; values := values.pop - let ty := values.back!; values := values.pop + (← liftM (InternIngressM.internE (.mkConst mid univs))) + | .nat blobIdx => + let some blobAddr := ctx.refs[blobIdx.toNat]? + | throw s!"invalid Nat blob ref index {blobIdx}" + let some bytes := ixonEnv.getBlob? blobAddr + | throw s!"missing Nat blob {blobAddr}" + let val := Nat.fromBytesLE bytes.data values := values.push - (← liftM (InternIngressM.internE (.mkLet () ty val body nd))) - | .prjDone id field => - let val := values.back!; values := values.pop + (← liftM (InternIngressM.internE (.mkNat val blobAddr))) + | .str blobIdx => + let some blobAddr := ctx.refs[blobIdx.toNat]? + | throw s!"invalid Str blob ref index {blobIdx}" + let some bytes := ixonEnv.getBlob? blobAddr + | throw s!"missing Str blob {blobAddr}" + let some val := String.fromUTF8? bytes + | throw s!"Str blob {blobAddr} is not valid UTF-8" values := values.push - (← liftM (InternIngressM.internE (.mkPrj id field val))) - | .cacheShare idx => - let v := values.back! - modify fun s => { s with exprCache := s.exprCache.insert idx v } - match values.back? with - | some v => - if values.size != 1 then - throw s!"ingressExpr: unbalanced value stack ({values.size} values)" - return v - | none => throw "ingressExpr: empty result stack" + (← liftM (InternIngressM.internE (.mkStr val blobAddr))) + | .app f a => + stack := stack.push .appDone |>.push (.process a) |>.push (.process f) + | .lam _ ty body => + stack := stack.push .lamDone |>.push (.process body) + |>.push (.process ty) + | .all _ _ ty body => + stack := stack.push .allDone |>.push (.process body) + |>.push (.process ty) + | .letE nd ty val body => + stack := stack.push (.letDone nd) |>.push (.process body) + |>.push (.process val) |>.push (.process ty) + | .prj typeRefIdx field val => + let some typeAddr := ctx.refs[typeRefIdx.toNat]? + | throw s!"invalid Prj type ref index {typeRefIdx}" + stack := stack.push (.prjDone ⟨typeAddr, ()⟩ field) + |>.push (.process val) + | .appDone => + let a := values.back!; values := values.pop + let f := values.back!; values := values.pop + values := values.push (← liftM (InternIngressM.internE (.mkApp f a))) + | .lamDone => + let body := values.back!; values := values.pop + let ty := values.back!; values := values.pop + values := values.push + (← liftM (InternIngressM.internE (.mkLam () () ty body))) + | .allDone => + let body := values.back!; values := values.pop + let ty := values.back!; values := values.pop + values := values.push + (← liftM (InternIngressM.internE (.mkAll () () ty body))) + | .letDone nd => + let body := values.back!; values := values.pop + let val := values.back!; values := values.pop + let ty := values.back!; values := values.pop + values := values.push + (← liftM (InternIngressM.internE (.mkLet () ty val body nd))) + | .prjDone id field => + let val := values.back!; values := values.pop + values := values.push + (← liftM (InternIngressM.internE (.mkPrj id field val))) + | .cacheShare idx => + let v := values.back! + modify fun s => { s with exprCache := s.exprCache.insert idx v } + return (stack, values) + +/-- The expression stack machine with an explicit step bound. Cyclic sharing +cannot recurse forever; exhaustion retains the partial intern state. -/ +def convertExprLoop (ixonEnv : Ixon.Env) (ctx : IngressCtx) (fuel : Nat) + (stack : Array EFrame) (values : Array (KExpr .anon)) : InternConvM (KExpr .anon) := do + if stack.isEmpty then + match values.back? with + | some value => + if values.size != 1 then + throw s!"ingressExpr: unbalanced value stack ({values.size} values)" + return value + | none => throw "ingressExpr: empty result stack" + else + match fuel with + | 0 => throw "ingressExpr: conversion step bound exhausted" + | fuel + 1 => + let (stack, values) ← convertExprStep ixonEnv ctx stack values + convertExprLoop ixonEnv ctx fuel stack values + +/-- Convert one Ixon expression with the production stack machine. + +Resolution: `sort i → univs[i]`; `ref i us → refs[i]` plus universe arguments; +`recur i us → mutCtx[i]`; literal indices name blobs; sharing expands through +its per-constant cache. For acyclic sharing, every expansion is cached before +it is revisited, so twice the nodes in the root and sharing table bounds all +processing, construction, and cache frames. The bound uses unbounded `Nat`. -/ +def convertExpr (ixonEnv : Ixon.Env) (ctx : IngressCtx) (root : Ixon.Expr) : + InternConvM (KExpr .anon) := + convertExprLoop ixonEnv ctx (2 * exprIngressSize (root :: ctx.sharing.toList)) + #[.process root] #[] /-- Environment-threading interface for cached universe conversion. -/ def ingressUnivIdx (ctx : IngressCtx) (idx : UInt64) : ConvM (KUniv .anon) := diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index a70b4e8ea..294f89d20 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -18,6 +18,7 @@ import Ix.Kernel.Verify.Consistency.SortCache import Ix.Kernel.Verify.Consistency.ConstantCache import Ix.Kernel.Verify.Consistency.LazyCache import Ix.Kernel.Verify.Consistency.BlockCache +import Ix.Kernel.Verify.Consistency.IngressCoherence import Ix.Kernel.Verify.Consistency.Context import Ix.Kernel.Verify.Consistency.BinderOpening import Ix.Kernel.Verify.Consistency.Application @@ -71,6 +72,12 @@ old declaration at its key. Fresh or partially loaded blocks meet this condition through pointwise lookup checks; uniqueness among fresh keys is unnecessary. Recursive constant leaves can use that loader and post-lookup walker resources, so later closed witnesses survive inference that loads another dependency. +The production conversion loops have bounds derived from source syntax and +reject exhausted cyclic sharing while retaining partial intern state. Coherence +is proved through every conversion form and the actual loader on both outcomes. +Constant inference derives post-lookup coherence from its initial state and +returns coherence after substitution and cache publication, so the next call +can reuse it. Finite collision and level resources remain explicit. Initial agreement, block overlap checks, finite execution resources, trace construction, and preservation for keys inside the footprint remain obligations. General checker soundness remains outside this fragment. diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 5fc15ed20..710798dd6 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -23,13 +23,15 @@ private def levelNative : Lean.Name := nativeAxiom `Ix.Kernel.Level `Ix.Kernel.KUniv.mkSucc._native.native_decide.ax_1 private def expressionNative : Lean.Name := nativeAxiom `Ix.Kernel.Expr `Ix.Kernel.KExpr.mkVar._native.native_decide.ax_1 +private def nameNative : Lean.Name := + nativeAxiom `Ix.Environment `Ix.Name.mkStr._native.native_decide.ax_1 /-- The public driver reaches these additional generated output-length proofs through the full production method table, including inactive branches. No new native proof is introduced by the fragment verification. -/ private def productionNative : Array Lean.Name := #[ expressionNative, levelNative, - nativeAxiom `Ix.Environment `Ix.Name.mkStr._native.native_decide.ax_1, + nameNative, nativeAxiom `Ix.Kernel.Inductive `Ix.Kernel.RecM.canonicalAuxOrder._native.native_decide.ax_9 ] @@ -75,6 +77,7 @@ private def cacheFrameRoots : Array Lean.Name := #[ ``PreservesInferenceCache.withInferOnly, ``getConst_loaded, ``IngressCacheExtension.refl, ``IngressCacheExtension.intern, ``IngressCacheExtension.trans, ``EntriesCompatible.ofFresh, ``EntriesCompatible.ofLookups, + ``insertMutsEntriesState_intern, ``ingress_runIntern_cache, ``LazyLookupFrame.refl, ``LazyLookupFrame.cache, ``LazyLookupFrame.policy ] @@ -86,7 +89,8 @@ private def cacheMapRoots : Array Lean.Name := #[ ] private def cacheKeyRoots : Array Lean.Name := #[ - ``inferKey_closed, ``inferKey_policy, ``InferenceCacheHit.key_closed, ``observeInferenceCache, + ``inferKey_closed, ``inferKey_policy, ``inferKey_environment, + ``InferenceCacheHit.key_closed, ``observeInferenceCache, ``InferenceCacheAgreement.selected, ``InferenceCacheHit.transport, ``PreservesInferenceCache.inferKey, ``PreservesInferenceCache.openBinder, ``withLctxScope_eq, ``PreservesInferenceCache.withLctxScope @@ -101,6 +105,8 @@ private def recursiveCacheRoots : Array Lean.Name := #[ ``infer_lazyConst_cache_frame, ``CachedConstantInferenceSupport.afterLazyInference, ``InferenceCacheTrace.verifiedConstOfKey, ``infer_verifiedConst_cache_frame, ``CachedConstantInferenceSupport.afterVerifiedInference, + ``InferenceCacheTrace.coherentConstOfKey, ``infer_verifiedConst_coherent, + ``infer_coherentConst_cache_frame, ``CachedConstantInferenceSupport.afterCoherentInference, ``InferenceCacheHit.afterInference, ``CachedConstantInferenceSupport.afterInference, ``CachedConstantInferenceSupport.sound_after_inference, ``BinderInference.sortAfterInference ] @@ -114,7 +120,11 @@ private def lazyCacheRoots : Array Lean.Name := #[ ``ingressAnonAddrShallow_verified_cache, ``StandaloneLazySupport.toVerified, ``lazyIngressAddr_verified_cache, ``tryGetConst_verified_cache, ``getConst_verified_cache, ``CachedConstantInferenceSupport.afterVerifiedGetConst, - ``CachedConstantInferenceSupport.afterFailedVerifiedGetConst + ``CachedConstantInferenceSupport.afterFailedVerifiedGetConst, + ``convertExpr_coherent, ``convertAnonStandalone_coherent, ``convertAnonBlock_coherent, + ``prepareAnonBlock_coherent, ``ingressAnonBlock_coherent, ``ingressAnonAddrShallow_coherent, + ``lazyIngressAddr_coherent, ``tryGetConst_coherent, ``getConst_coherent, + ``UniverseInstantiationSupport.afterVerifiedGetConst ] private def productionRoots : Array Lean.Name := #[ @@ -156,6 +166,11 @@ private def forbiddenProduction : Array Lean.Name := #[ ] def roots : Array RootAllowance := #[ + { root := ``convertUnivTree_coherent, standardAxioms := standard, + nativeAxioms := #[levelNative], forbiddenDependencies := forbiddenProduction }, + { root := ``newLazyAnon_intern_coherent, standardAxioms := standard, + nativeAxioms := #[expressionNative, levelNative, nameNative], + forbiddenDependencies := forbiddenProduction }, { root := ``readLevel_eq, standardAxioms := #[``propext] }, { root := ``readLevel_eval, standardAxioms := #[``propext] }, { root := ``readLevel_wf, standardAxioms := #[``propext] }, diff --git a/Ix/Kernel/Verify/Consistency/IngressCoherence.lean b/Ix/Kernel/Verify/Consistency/IngressCoherence.lean new file mode 100644 index 000000000..8b8d9e212 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/IngressCoherence.lean @@ -0,0 +1,645 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BlockCache +import Ix.Kernel.Driver +import Init.Data.Range.Lemmas + +/-! +# Intern-table coherence through production ingress + +Both outcomes of the bounded conversion machines preserve key coherence. +No collision-freedom or semantic source-agreement premise is needed for this +structural invariant. Failed conversion retains its coherent partial state. +-/ + +namespace Ix.Kernel.Consistency + +private def InternCoherent (action : InternIngressM α) : Prop := + ∀ before, before.WF → match action before with + | .ok _ after | .error _ after => after.WF + +private theorem intern_pure (value : α) : InternCoherent (pure value) := + fun _ coherent => coherent + +private theorem intern_throw (err : IngressErr) : InternCoherent (throw err : InternIngressM α) := + fun _ coherent => coherent + +private theorem intern_throw_bind (err : IngressErr) (next : α → InternIngressM β) : + InternCoherent ((throw err : InternIngressM α) >>= next) := fun _ coherent => coherent + +private theorem intern_bind {action : InternIngressM α} {next : α → InternIngressM β} + (first : InternCoherent action) (rest : ∀ value, InternCoherent (next value)) : + InternCoherent (action >>= next) := by + intro before coherent + have preserved := first before coherent + change (match EStateM.bind action next before with + | .ok _ after | .error _ after => after.WF) + rw [EStateM.bind] + cases run : action before <;> rw [run] at preserved + · exact rest _ _ preserved + · exact preserved + +private theorem intern_expr (term : KExpr .anon) : InternCoherent (InternIngressM.internE term) := + fun _ coherent => coherent.internExpr term + +private theorem intern_univ (level : KUniv .anon) : InternCoherent (InternIngressM.internU level) := + fun _ coherent => coherent.internUniv level + +private def ConvCoherent (action : InternConvM α) : Prop := + ∀ cache, InternCoherent (action cache) + +private theorem conv_pure (value : α) : ConvCoherent (pure value) := + fun _ => intern_pure _ + +private theorem conv_throw (err : IngressErr) : ConvCoherent (throw err : InternConvM α) := + fun _ => intern_throw _ + +private theorem conv_throw_bind (err : IngressErr) (next : α → InternConvM β) : + ConvCoherent ((throw err : InternConvM α) >>= next) := fun _ _ coherent => coherent + +private theorem conv_bind {action : InternConvM α} {next : α → InternConvM β} + (first : ConvCoherent action) (rest : ∀ value, ConvCoherent (next value)) : + ConvCoherent (action >>= next) := by + intro cache + change InternCoherent (EStateM.bind (action cache) (fun (value, cache) => next value cache)) + exact intern_bind (first cache) (fun (value, cache) => rest value cache) + +private theorem conv_lift {action : InternIngressM α} (coherent : InternCoherent action) : + ConvCoherent (liftM action) := by + intro cache + exact intern_bind coherent (fun _ => intern_pure _) + +private theorem conv_get : ConvCoherent (get : InternConvM ConvState) := + fun _ => intern_pure _ + +private theorem conv_modify (update : ConvState → ConvState) : + ConvCoherent (modify update : InternConvM Unit) := fun _ => intern_pure _ + +private theorem forIn_keeps {m : Type → Type} [Monad m] + (P : {α : Type} → m α → Prop) + (hpure : ∀ {α} (value : α), P (pure value)) + (hbind : ∀ {α β} {action : m α} {next : α → m β}, + P action → (∀ value, P (next value)) → P (action >>= next)) + (items : List α) (initial : β) (body : α → β → m (ForInStep β)) + (step : ∀ item state, P (body item state)) : P (forIn items initial body) := by + induction items generalizing initial with + | nil => simpa only [List.forIn_nil] using hpure initial + | cons item rest ih => + rw [List.forIn_cons] + apply hbind (step item initial) + intro result + cases result with + | done state => exact hpure state + | yield state => exact ih state + +private theorem array_forIn_keeps {m : Type → Type} [Monad m] + (P : {α : Type} → m α → Prop) + (hpure : ∀ {α} (value : α), P (pure value)) + (hbind : ∀ {α β} {action : m α} {next : α → m β}, + P action → (∀ value, P (next value)) → P (action >>= next)) + (items : Array α) (initial : β) (body : α → β → m (ForInStep β)) + (step : ∀ item state, P (body item state)) : P (forIn items initial body) := by + rw [← Array.forIn_toList] + exact forIn_keeps P hpure hbind items.toList initial body step + +private theorem forIn'_keeps {m : Type → Type} [Monad m] + (P : {α : Type} → m α → Prop) + (hpure : ∀ {α} (value : α), P (pure value)) + (hbind : ∀ {α β} {action : m α} {next : α → m β}, + P action → (∀ value, P (next value)) → P (action >>= next)) + (items : List α) (initial : β) (body : (item : α) → item ∈ items → β → m (ForInStep β)) + (step : ∀ item member state, P (body item member state)) : P (forIn' items initial body) := by + induction items generalizing initial with + | nil => simpa only [List.forIn'_nil] using hpure initial + | cons item rest ih => + rw [List.forIn'_cons] + apply hbind (step item (List.mem_cons_self) initial) + intro result + cases result with + | done state => exact hpure state + | yield state => + exact ih state _ (fun item member state => step item (List.mem_cons_of_mem _ member) state) + +private theorem range_forIn'_keeps {m : Type → Type} [Monad m] + (P : {α : Type} → m α → Prop) + (hpure : ∀ {α} (value : α), P (pure value)) + (hbind : ∀ {α β} {action : m α} {next : α → m β}, + P action → (∀ value, P (next value)) → P (action >>= next)) + (range : Std.Legacy.Range) (initial : β) + (body : (item : Nat) → item ∈ range → β → m (ForInStep β)) + (step : ∀ item member state, P (body item member state)) : P (forIn' range initial body) := by + rw [Std.Legacy.Range.forIn'_eq_forIn'_range'] + exact forIn'_keeps P hpure hbind _ initial _ (fun item _ state => step item _ state) + +private theorem convertUnivStep_coherent (stack : Array UFrame) (values : Array (KUniv .anon)) : + InternCoherent (convertUnivStep stack values) := by + dsimp only [convertUnivStep] + cases stack.back! with + | process level => + cases level <;> first + | exact intern_pure _ + | exact intern_bind (intern_univ _) (fun _ => intern_pure _) + | succ | max | imax => exact intern_bind (intern_univ _) (fun _ => intern_pure _) + +private theorem convertUnivLoop_coherent (fuel : Nat) (stack : Array UFrame) + (values : Array (KUniv .anon)) : InternCoherent (convertUnivLoop fuel stack values) := by + induction fuel generalizing stack values with + | zero => + unfold convertUnivLoop + split + · cases values.back? <;> first | exact intern_pure _ | exact intern_throw _ + · exact intern_throw _ + | succ fuel ih => + unfold convertUnivLoop + split + · cases values.back? <;> first | exact intern_pure _ | exact intern_throw _ + · exact intern_bind (convertUnivStep_coherent stack values) (fun (stack, values) => ih stack values) + +/-- Actual universe conversion preserves coherent keys, on both outcomes. -/ +theorem convertUnivTree_coherent (root : Ixon.Univ) (before : InternTable .anon) + (coherent : before.WF) : + match convertUnivTree root before with + | .ok _ after | .error _ after => after.WF := by + unfold convertUnivTree + have preserved := convertUnivLoop_coherent (2 * univIngressSize root) #[.process root] #[] before coherent + cases run : convertUnivLoop (2 * univIngressSize root) #[.process root] #[] before <;> + rw [run] at preserved <;> exact preserved + +private theorem convertUnivIdx_coherent (ctx : IngressCtx) (idx : UInt64) : + ConvCoherent (convertUnivIdx ctx idx) := by + unfold convertUnivIdx + apply conv_bind conv_get + intro cache + cases cache.univCache[idx]? with + | some value => exact conv_pure _ + | none => + cases ctx.univs[idx.toNat]? with + | none => exact conv_throw _ + | some level => + have tree : InternCoherent (convertUnivTree level) := by + intro before coherent + have preserved := convertUnivTree_coherent level before coherent + cases run : convertUnivTree level before <;> rw [run] at preserved <;> exact preserved + apply conv_bind (conv_lift tree) + intro value + exact conv_bind (conv_modify _) (fun _ => conv_pure _) + +private theorem convertUnivArgs_coherent (ctx : IngressCtx) (idxs : Array UInt64) : + ConvCoherent (convertUnivArgs ctx idxs) := by + unfold convertUnivArgs + apply conv_bind _ (fun _ => conv_pure _) + apply array_forIn_keeps (@ConvCoherent) (@conv_pure) (@conv_bind) + intro idx out + exact conv_bind (convertUnivIdx_coherent ctx idx) (fun _ => conv_pure _) + +private theorem convertExprStep_coherent (source : Ixon.Env) (ctx : IngressCtx) + (stack : Array EFrame) (values : Array (KExpr .anon)) : + ConvCoherent (convertExprStep source ctx stack values) := by + dsimp only [convertExprStep] + cases stack.back! with + | process term => + cases term with + | share idx => + apply conv_bind conv_get + intro cache + cases cache.exprCache[idx]? with + | some value => exact conv_pure _ + | none => cases ctx.sharing[idx.toNat]? <;> first + | exact conv_pure _ + | exact conv_throw_bind _ _ + | var idx => exact conv_bind (conv_lift (intern_expr _)) (fun _ => conv_pure _) + | sort idx => + exact conv_bind (convertUnivIdx_coherent ctx idx) + (fun _ => conv_bind (conv_lift (intern_expr _)) (fun _ => conv_pure _)) + | ref idx arguments => + dsimp only + cases ctx.refs[idx.toNat]? with + | none => exact conv_throw_bind _ _ + | some addr => + dsimp only + exact conv_bind (convertUnivArgs_coherent ctx arguments) + (fun _ => conv_bind (conv_lift (intern_expr _)) (fun _ => conv_pure _)) + | recur idx arguments => + dsimp only + cases ctx.mutCtx[idx.toNat]? with + | none => exact conv_throw_bind _ _ + | some id => + exact conv_bind (convertUnivArgs_coherent ctx arguments) + (fun _ => conv_bind (conv_lift (intern_expr _)) (fun _ => conv_pure _)) + | nat idx => + dsimp only + cases ctx.refs[idx.toNat]? with + | none => exact conv_throw_bind _ _ + | some addr => + dsimp only + cases source.getBlob? addr with + | none => exact conv_throw_bind _ _ + | some bytes => exact conv_bind (conv_lift (intern_expr _)) (fun _ => conv_pure _) + | str idx => + dsimp only + cases ctx.refs[idx.toNat]? with + | none => exact conv_throw_bind _ _ + | some addr => + dsimp only + cases source.getBlob? addr with + | none => exact conv_throw_bind _ _ + | some bytes => + dsimp only + cases String.fromUTF8? bytes with + | none => exact conv_throw_bind _ _ + | some value => exact conv_bind (conv_lift (intern_expr _)) (fun _ => conv_pure _) + | app | lam | all | letE => exact conv_pure _ + | prj idx field value => + dsimp only + cases ctx.refs[idx.toNat]? <;> first + | exact conv_pure _ + | exact conv_throw_bind _ _ + | appDone | lamDone | allDone | letDone | prjDone => + exact conv_bind (conv_lift (intern_expr _)) (fun _ => conv_pure _) + | cacheShare idx => exact conv_bind (conv_modify _) (fun _ => conv_pure _) + +private theorem convertExprLoop_coherent (source : Ixon.Env) (ctx : IngressCtx) + (fuel : Nat) (stack : Array EFrame) (values : Array (KExpr .anon)) : + ConvCoherent (convertExprLoop source ctx fuel stack values) := by + induction fuel generalizing stack values with + | zero => + unfold convertExprLoop + split + · cases values.back? with + | none => exact conv_throw _ + | some value => + dsimp only + split + · exact conv_throw_bind _ _ + · exact conv_pure _ + · exact conv_throw _ + | succ fuel ih => + unfold convertExprLoop + split + · cases values.back? with + | none => exact conv_throw _ + | some value => + dsimp only + split + · exact conv_throw_bind _ _ + · exact conv_pure _ + · exact conv_bind (convertExprStep_coherent source ctx stack values) + (fun (stack, values) => ih stack values) + +private theorem convertExpr_preserves (source : Ixon.Env) (ctx : IngressCtx) (root : Ixon.Expr) : + ConvCoherent (convertExpr source ctx root) := by + unfold convertExpr + exact convertExprLoop_coherent source ctx _ _ _ + +/-- Expression conversion preserves coherent intern keys independently of +the source's semantic validity, including failure with partial progress. -/ +theorem convertExpr_coherent (source : Ixon.Env) (ctx : IngressCtx) (root : Ixon.Expr) + (cache : ConvState) (before : InternTable .anon) (coherent : before.WF) : + match convertExpr source ctx root cache before with + | .ok _ after | .error _ after => after.WF := by + have preserved := convertExpr_preserves source ctx root cache before coherent + cases run : convertExpr source ctx root cache before <;> rw [run] at preserved <;> exact preserved + +private theorem convertDefnAnon_preserves (source : Ixon.Env) (defn : Ixon.Definition) + (constant : Ixon.Constant) (block : KId .anon) (mutCtx : Array (KId .anon)) + (hints : Option Lean.ReducibilityHints) : + InternCoherent (convertDefnAnon source defn constant block mutCtx hints) := by + unfold convertDefnAnon + apply intern_bind (convertExpr_preserves source _ defn.typ {}) + intro result + exact intern_bind (convertExpr_preserves source _ defn.value result.2) (fun _ => intern_pure _) + +private theorem convertRecursorAnon_preserves (source : Ixon.Env) (rec : Ixon.Recursor) + (constant : Ixon.Constant) (block : KId .anon) (mutCtx : Array (KId .anon)) : + InternCoherent (convertRecursorAnon source rec constant block mutCtx) := by + unfold convertRecursorAnon + apply intern_bind (convertExpr_preserves source _ rec.typ {}) + intro result + apply intern_bind _ (fun _ => intern_pure _) + apply array_forIn_keeps (@InternCoherent) (@intern_pure) (@intern_bind) + intro rule state + exact intern_bind (convertExpr_preserves source _ rule.rhs _) (fun _ => intern_pure _) + +private theorem convertAnonInductive_preserves (source : Ixon.Env) (ind : Ixon.Inductive) + (self : KId .anon) (constant : Ixon.Constant) (block : KId .anon) (idx : UInt64) + (ctorAddrs : Array Address) (mutCtx : Array (KId .anon)) : + InternCoherent (convertAnonInductive source ind self constant block idx ctorAddrs mutCtx) := by + unfold convertAnonInductive + split + · exact intern_throw_bind _ _ + · apply intern_bind (convertExpr_preserves source _ ind.typ {}) + intro result + apply intern_bind _ (fun _ => intern_pure _) + apply range_forIn'_keeps (@InternCoherent) (@intern_pure) (@intern_bind) + intro cidx member state + exact intern_bind (convertExpr_preserves source _ _ _) (fun _ => intern_pure _) + +private theorem convertAnonStandalone_preserves (source : Ixon.Env) (addr : Address) + (constant : Ixon.Constant) : InternCoherent (convertAnonStandalone source addr constant) := by + unfold convertAnonStandalone + cases constant.info with + | defn defn => exact convertDefnAnon_preserves source defn constant _ _ _ + | recr rec => exact convertRecursorAnon_preserves source rec constant _ _ + | axio axio => exact intern_bind (convertExpr_preserves source _ axio.typ {}) (fun _ => intern_pure _) + | quot quot => exact intern_bind (convertExpr_preserves source _ quot.typ {}) (fun _ => intern_pure _) + | muts | dPrj | iPrj | rPrj | cPrj => exact intern_throw _ + +private theorem convertAnonBlock_preserves (source : Ixon.Env) (constant : Ixon.Constant) + (addr : Address) : InternCoherent (convertAnonBlock source constant addr) := by + unfold convertAnonBlock + cases constant.info with + | defn | recr | axio | quot | dPrj | iPrj | rPrj | cPrj => exact intern_throw _ + | muts members => + apply intern_bind _ (fun _ => intern_pure _) + apply range_forIn'_keeps (@InternCoherent) (@intern_pure) (@intern_bind) + intro idx member state + dsimp only + cases members[idx] with + | defn defn => + apply intern_bind + · split + · exact intern_throw _ + · exact intern_pure _ + · intro _ + exact intern_bind (convertDefnAnon_preserves source defn constant _ _ _) + (fun _ => intern_pure _) + | recr rec => + apply intern_bind + · split + · exact intern_throw _ + · exact intern_pure _ + · intro _ + exact intern_bind (convertRecursorAnon_preserves source rec constant _ _) + (fun _ => intern_pure _) + | indc ind => + apply intern_bind + · split + · exact intern_throw _ + · exact intern_pure _ + · intro _ + apply intern_bind + · apply range_forIn'_keeps (@InternCoherent) (@intern_pure) (@intern_bind) + intro cidx member state + apply intern_bind + · split + · exact intern_throw _ + · exact intern_pure _ + · intro _; exact intern_pure _ + · intro _ + exact intern_bind (convertAnonInductive_preserves source ind _ constant _ _ _ _) + (fun _ => intern_pure _) + +/-- Standalone conversion derives coherence for definitions, recursors, +axioms, and quotients, including failed conversion and rejected source forms. -/ +theorem convertAnonStandalone_coherent (source : Ixon.Env) (addr : Address) + (constant : Ixon.Constant) (before : InternTable .anon) (coherent : before.WF) : + match convertAnonStandalone source addr constant before with + | .ok _ after | .error _ after => after.WF := by + have preserved := convertAnonStandalone_preserves source addr constant before coherent + cases run : convertAnonStandalone source addr constant before <;> + rw [run] at preserved <;> exact preserved + +/-- Every block member and constructor uses the same coherent intern tables; +an error in a later member retains the coherent effects of earlier members. -/ +theorem convertAnonBlock_coherent (source : Ixon.Env) (constant : Ixon.Constant) + (addr : Address) (before : InternTable .anon) (coherent : before.WF) : + match convertAnonBlock source constant addr before with + | .ok _ after | .error _ after => after.WF := by + have preserved := convertAnonBlock_preserves source constant addr before coherent + cases run : convertAnonBlock source constant addr before <;> rw [run] at preserved <;> exact preserved + +private def IngressCoherent (action : IngressM α) : Prop := + ∀ before, before.intern.WF → match action before with + | .ok _ after | .error _ after => after.intern.WF + +private theorem ingress_pure (value : α) : IngressCoherent (pure value) := fun _ coherent => coherent + +private theorem ingress_throw (err : IngressErr) : IngressCoherent (throw err : IngressM α) := + fun _ coherent => coherent + +private theorem ingress_bind {action : IngressM α} {next : α → IngressM β} + (first : IngressCoherent action) (rest : ∀ value, IngressCoherent (next value)) : + IngressCoherent (action >>= next) := by + intro before coherent + have preserved := first before coherent + change (match EStateM.bind action next before with + | .ok _ after | .error _ after => after.intern.WF) + rw [EStateM.bind] + cases run : action before <;> rw [run] at preserved + · exact rest _ _ preserved + · exact preserved + +private theorem ingress_liftExcept (result : Except IngressErr α) : + IngressCoherent (IngressM.liftExcept result) := by + cases result with + | ok value => exact ingress_pure value + | error err => exact ingress_throw err + +private theorem ingress_runIntern {action : InternIngressM α} (preserves : InternCoherent action) : + IngressCoherent (IngressM.runIntern action) := by + intro before coherent + have preserved := preserves before.intern coherent + unfold IngressM.runIntern + cases run : action before.intern <;> rw [run] at preserved <;> exact preserved + +private theorem insert_list_intern (entries : List Entry) (before : AnonEnv) : + (entries.foldl (fun env entry => env.insert entry.1 entry.2) before).intern = before.intern := by + induction entries generalizing before with + | nil => rfl + | cons entry rest ih => exact ih (before.insert entry.1 entry.2) + +/-- Publication does not access the intern tables, regardless of declaration +overlap. Coherence and cache preservation have different requirements. -/ +theorem insertMutsEntriesState_intern (entries : Array Entry) (before : AnonEnv) : + (insertMutsEntriesState before entries).intern = before.intern := by + unfold insertMutsEntriesState insertEntriesState + rw [insert_list_intern] + split <;> rfl + +private theorem insertMutsEntries_preserves (entries : Array Entry) : + IngressCoherent (insertMutsEntries entries) := by + unfold insertMutsEntries + apply ingress_bind + · intro before coherent + rw [guardReserved_state] + cases checkReserved entries <;> exact coherent + · intro _ before coherent + change (insertMutsEntriesState before entries).intern.WF + rw [insertMutsEntriesState_intern] + exact coherent + +private theorem ingressAnonStandalone_preserves (source : Ixon.Env) (addr : Address) + (constant : Ixon.Constant) : IngressCoherent (ingressAnonStandalone source addr constant) := by + unfold ingressAnonStandalone + apply ingress_bind (ingress_runIntern (convertAnonStandalone_preserves source addr constant)) + intro concrete + apply ingress_bind _ (fun _ => ingress_pure _) + intro before coherent + rw [insertStandaloneEntries_singleton] + cases reservedMarkerName addr <;> exact coherent + +/-- Preparation derives post-state coherence from the pre-state invariant. -/ +theorem prepareAnonBlock_coherent (source : Ixon.Env) (constant : Ixon.Constant) + (addr : Address) (before : AnonEnv) (coherent : before.intern.WF) : + match prepareAnonBlock source constant addr before with + | .ok _ after | .error _ after => after.intern.WF := by + have preserved := ingress_runIntern (convertAnonBlock_preserves source constant addr) before coherent + unfold prepareAnonBlock + cases run : IngressM.runIntern (convertAnonBlock source constant addr) before <;> + rw [run] at preserved <;> exact preserved + +private theorem ingressAnonBlockWithTrace_preserves (source : Ixon.Env) (constant : Ixon.Constant) + (addr : Address) : IngressCoherent (ingressAnonBlockWithTrace source constant addr) := by + unfold ingressAnonBlockWithTrace + apply ingress_bind (ingress_runIntern (convertAnonBlock_preserves source constant addr)) + intro trace + exact ingress_bind (insertMutsEntries_preserves trace.allEntries) (fun _ => ingress_pure _) + +private theorem ingressAnonBlock_preserves (source : Ixon.Env) (constant : Ixon.Constant) + (addr : Address) : IngressCoherent (ingressAnonBlock source constant addr) := by + unfold ingressAnonBlock + exact ingress_bind (ingressAnonBlockWithTrace_preserves source constant addr) (fun _ => ingress_pure _) + +/-- Block publication preserves coherent intern tables on success and error, +without a freshness or compatibility premise on the declaration map. -/ +theorem ingressAnonBlock_coherent (source : Ixon.Env) (constant : Ixon.Constant) + (addr : Address) (before : AnonEnv) (coherent : before.intern.WF) : + match ingressAnonBlock source constant addr before with + | .ok _ after | .error _ after => after.intern.WF := by + have preserved := ingressAnonBlock_preserves source constant addr before coherent + cases run : ingressAnonBlock source constant addr before <;> rw [run] at preserved <;> exact preserved + +private theorem ingressAnonAddrShallow_preserves (source : Ixon.Env) (addr : Address) (verify : Bool) : + IngressCoherent (ingressAnonAddrShallow source addr verify) := by + unfold ingressAnonAddrShallow + apply ingress_bind (ingress_liftExcept _) + intro optional + cases optional with + | none => exact ingress_pure _ + | some constant => + dsimp only + cases ingressBlockAddr? addr constant.info with + | none => exact ingress_bind (ingressAnonStandalone_preserves source addr constant) (fun _ => ingress_pure _) + | some blockAddr => + apply ingress_bind (fun _ coherent => coherent) + intro state + split + · exact ingress_pure _ + · apply ingress_bind (ingress_liftExcept _) + intro optional + cases optional with + | none => exact ingress_throw _ + | some blockConstant => + exact ingress_bind (ingressAnonBlock_preserves source blockConstant blockAddr) (fun _ => ingress_pure _) + +/-- The actual shallow loader preserves coherence on both outcomes, even +when materialization fails or the requested projection is already recorded. -/ +theorem ingressAnonAddrShallow_coherent (source : Ixon.Env) (addr : Address) (verify : Bool) + (before : AnonEnv) (coherent : before.intern.WF) : + match ingressAnonAddrShallow source addr verify before with + | .ok _ after | .error _ after => after.intern.WF := by + have preserved := ingressAnonAddrShallow_preserves source addr verify before coherent + cases run : ingressAnonAddrShallow source addr verify before <;> rw [run] at preserved <;> exact preserved + +/-- Fault dispatch preserves coherence with the actual ingress callback. +Its fault-history update retains the post-conversion intern tables. -/ +theorem lazyIngressAddr_coherent {before : TcState .anon} {addr : Address} + (source : Ixon.Env) (verify : Bool) + (installed : before.lazyFault = some (fun address => ingressAnonAddrShallow source address verify)) + (coherent : before.env.intern.WF) : + match TcM.lazyIngressAddr addr before with + | .ok _ after | .error _ after => after.env.intern.WF := by + unfold TcM.lazyIngressAddr + rw [installed] + dsimp only + by_cases faulted : before.faultedAddrs.contains addr = true + · rw [if_pos faulted] + exact coherent + · rw [if_neg faulted] + have preserved := ingressAnonAddrShallow_coherent source addr verify before.env coherent + cases run : ingressAnonAddrShallow source addr verify before.env <;> + rw [run] at preserved <;> exact preserved + +/-- Loaded hits, actual lazy faults, misses, and errors all retain coherent +intern tables. No assertion about the callback's final state is an input. -/ +theorem tryGetConst_coherent {before : TcState .anon} {id : KId .anon} + (source : Ixon.Env) (verify : Bool) + (installed : before.lazyFault = some (fun address => ingressAnonAddrShallow source address verify)) + (coherent : before.env.intern.WF) : + match TcM.tryGetConst id before with + | .ok _ after | .error _ after => after.env.intern.WF := by + unfold TcM.tryGetConst + change (match (EStateM.bind (get : TcM .anon (TcState .anon)) _ : + TcM .anon (Option (KConst .anon))) before with + | .ok _ after | .error _ after => after.env.intern.WF) + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + dsimp only + cases before.env.get? id with + | some concrete => exact coherent + | none => + change (match (EStateM.bind (TcM.lazyIngressAddr id.addr) _ : + TcM .anon (Option (KConst .anon))) before with + | .ok _ after | .error _ after => after.env.intern.WF) + have preserved := lazyIngressAddr_coherent (addr := id.addr) source verify installed coherent + cases fault : TcM.lazyIngressAddr id.addr before with + | error err after => + rw [EStateM.bind, fault] + simpa only [fault] using preserved + | ok value after => + rw [fault] at preserved + rw [EStateM.bind, fault] + change (match (EStateM.bind (get : TcM .anon (TcState .anon)) _ : + TcM .anon (Option (KConst .anon))) after with + | .ok _ state | .error _ state => state.env.intern.WF) + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) after = .ok after after from rfl] + dsimp only + cases after.env.get? id with + | some concrete => exact preserved + | none => cases before.lazyFault.isSome <;> exact preserved + +/-- Hard lookup derives post-state coherence from pre-state coherence and +the installed production loader, for success and every error path. -/ +theorem getConst_coherent {before : TcState .anon} {id : KId .anon} + (source : Ixon.Env) (verify : Bool) + (installed : before.lazyFault = some (fun address => ingressAnonAddrShallow source address verify)) + (coherent : before.env.intern.WF) : + match TcM.getConst id before with + | .ok _ after | .error _ after => after.env.intern.WF := by + unfold TcM.getConst + change (match (EStateM.bind (TcM.tryGetConst id) _ : TcM .anon (KConst .anon)) before with + | .ok _ after | .error _ after => after.env.intern.WF) + have preserved := tryGetConst_coherent (id := id) source verify installed coherent + cases tried : TcM.tryGetConst id before with + | error err after => + rw [EStateM.bind, tried] + simpa only [tried] using preserved + | ok optional after => + rw [tried] at preserved + rw [EStateM.bind, tried] + cases optional <;> exact preserved + +/-- The production lazy checker's initial intern tables are coherent. -/ +theorem newLazyAnon_intern_coherent (source : Ixon.Env) : + (TcState.newLazyAnon source).env.intern.WF := + InternTable.WF.empty + +/-- Build the post-lookup walker resource from coherence before loading. +Finite collision freedom and level resources remain data requirements; the +coherence field is established by the actual lookup, including block loading. -/ +theorem UniverseInstantiationSupport.afterVerifiedGetConst + {before after : TcState .anon} {id : KId .anon} {concrete : KConst .anon} + {arguments : Array (KUniv .anon)} + (loader : VerifiedLazySupport before id.addr) (coherent : before.env.intern.WF) + (run : TcM.getConst id before = .ok concrete after) + (faithful : KExpr.CollisionFree fun candidate => after.env.intern.ExprSupport candidate ∨ + KExpr.InstUnivReach arguments concrete.ty candidate) + (levels : UniverseSubstitutionSupport arguments concrete.ty) : + UniverseInstantiationSupport after concrete.ty arguments := by + have preserved := getConst_coherent (id := id) loader.source true loader.installed coherent + rw [run] at preserved + exact ⟨preserved, faithful, levels⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean index 15f1a9f99..bbc6e9742 100644 --- a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean +++ b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.BinderInference -import Ix.Kernel.Verify.Consistency.BlockCache +import Ix.Kernel.Verify.Consistency.IngressCoherence /-! # Cache preservation through recursive inference @@ -39,6 +39,24 @@ theorem inferKey_policy {term : KExpr .anon} {before after : TcState .anon} cases cached : before.ctxAddrCache[(before.ctxId, term.lbr)]? <;> rw [cached] at run <;> cases run <;> rfl +/-- Context-digest memoization leaves the complete kernel environment intact. -/ +theorem inferKey_environment {term : KExpr .anon} {before after : TcState .anon} + {key : Address × Address} (run : TcM.inferKey term before = .ok key after) : + after.env = before.env := by + unfold TcM.inferKey at run + change EStateM.bind (TcM.ctxAddrForLbr term.lbr) _ before = _ at run + unfold TcM.ctxAddrForLbr at run + change EStateM.bind (fun state => EStateM.bind (get : TcM .anon (TcState .anon)) + _ state) _ before = _ at run + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = + .ok before before from rfl] at run + by_cases fast : (term.lbr == 0 || before.ctx.isEmpty) = true + · rw [if_pos fast] at run + cases run; rfl + · rw [if_neg fast] at run + cases cached : before.ctxAddrCache[(before.ctxId, term.lbr)]? <;> + rw [cached] at run <;> cases run <;> rfl + /-- Hash conversion executes only tracing and its optional statistics update. The exact state includes that counter update; it is not assumed unchanged. -/ theorem isDefEq_hash_state {left right : KExpr .anon} @@ -238,6 +256,22 @@ def InferenceCacheTrace.lazyConstOfKey {fuel : Nat} {before keyed : TcState .ano InferenceCacheTrace fuel before (.const id arguments info) := .verifiedConstOfKey keyRun loader.toVerified resources +/-- A lazy constant leaf derives post-lookup coherence from the state before +key computation. Only finite collision and level data are supplied afterward. -/ +def InferenceCacheTrace.coherentConstOfKey {fuel : Nat} {before keyed : TcState .anon} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {key : Address × Address} + (keyRun : TcM.inferKey (.const id arguments info) before = .ok key keyed) + (loader : VerifiedLazySupport keyed id.addr) (coherent : before.env.intern.WF) + (resources : ∀ concrete loaded, TcM.getConst id keyed = .ok concrete loaded → + (KExpr.CollisionFree fun candidate => loaded.env.intern.ExprSupport candidate ∨ + KExpr.InstUnivReach arguments concrete.ty candidate) ∧ + UniverseSubstitutionSupport arguments concrete.ty) : + InferenceCacheTrace fuel before (.const id arguments info) := + .verifiedConstOfKey keyRun loader fun concrete loaded run => + .afterVerifiedGetConst loader (by rwa [inferKey_environment keyRun]) run + (resources concrete loaded run).1 (resources concrete loaded run).2 + /-- Every successful call in the finite tree preserves entries outside its computed write footprint and retains the loaded declarations and policy. The proof follows the recursive calls, then their real outer cache insertion. -/ @@ -371,6 +405,56 @@ theorem infer_lazyConst_cache_frame {fuel : Nat} {before keyed after : TcState . InferenceCacheFrame watched before after ∧ after.inferOnly = before.inferOnly := infer_verifiedConst_cache_frame keyRun different loader.toVerified resources accepted +/-- Coherence survives the complete successful constant call, including +key computation, actual loading, universe substitution, and the cache write. -/ +theorem infer_verifiedConst_coherent {fuel : Nat} {before keyed after : TcState .anon} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {key : Address × Address} {result : KExpr .anon} + (keyRun : TcM.inferKey (.const id arguments info) before = .ok key keyed) + (loader : VerifiedLazySupport keyed id.addr) (coherent : before.env.intern.WF) + (faithful : ∀ concrete loaded, TcM.getConst id keyed = .ok concrete loaded → + KExpr.CollisionFree fun candidate => loaded.env.intern.ExprSupport candidate ∨ + KExpr.InstUnivReach arguments concrete.ty candidate) + (accepted : RecM.infer (.const id arguments info) (methodsN fuel) before = .ok result after) : + after.env.intern.WF := by + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, _, stateEq⟩ + · rw [hit.run] at accepted + cases accepted + rwa [inferKey_environment hit.keyRun] + · obtain ⟨middle, run, written⟩ := infer_uncached_success_state miss accepted + rw [stateEq] at run + obtain ⟨concrete, loaded, got, _, instantiated⟩ := inferUncached_const_instantiation run + have lookup := getConst_coherent (id := id) loader.source true loader.installed + (by rwa [inferKey_environment keyRun]) + rw [got] at lookup + have post := TcM.instantiateUnivParams_wf (faithful concrete loaded got) + (fun _ h => Or.inr h) ⟨lookup, fun _ h => Or.inl h⟩ + rw [instantiated] at post + rw [written] + cases before.inferOnly <;> exact post.1.1 + +/-- The complete constant call needs coherence only before it starts and +returns it alongside cache preservation, ready for the next operation. -/ +theorem infer_coherentConst_cache_frame {fuel : Nat} {before keyed after : TcState .anon} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {key watched : Address × Address} {result : KExpr .anon} + (keyRun : TcM.inferKey (.const id arguments info) before = .ok key keyed) + (different : key ≠ watched) (loader : VerifiedLazySupport keyed id.addr) + (coherent : before.env.intern.WF) + (resources : ∀ concrete loaded, TcM.getConst id keyed = .ok concrete loaded → + (KExpr.CollisionFree fun candidate => loaded.env.intern.ExprSupport candidate ∨ + KExpr.InstUnivReach arguments concrete.ty candidate) ∧ + UniverseSubstitutionSupport arguments concrete.ty) + (accepted : RecM.infer (.const id arguments info) (methodsN fuel) before = .ok result after) : + InferenceCacheFrame watched before after ∧ after.inferOnly = before.inferOnly ∧ + after.env.intern.WF := + let frame := infer_verifiedConst_cache_frame keyRun different loader + (fun concrete loaded run => .afterVerifiedGetConst loader + (by rwa [inferKey_environment keyRun]) run + (resources concrete loaded run).1 (resources concrete loaded run).2) accepted + ⟨frame.1, frame.2, infer_verifiedConst_coherent keyRun loader coherent + (fun concrete loaded run => (resources concrete loaded run).1) accepted⟩ + /-- Concrete agreement at an unwritten key is retained by the entire tree. -/ theorem InferenceCacheTrace.agreement {fuel : Nat} {before after : TcState .anon} {term result expected : KExpr .anon} (tree : InferenceCacheTrace fuel before term) @@ -445,6 +529,29 @@ def CachedConstantInferenceSupport.afterLazyInference {β : Type u} CachedConstantInferenceSupport resolve entries after id arguments info ref entry type := support.afterVerifiedInference closed keyRun different loader.toVerified resources accepted +/-- Reuse the old witness after loading and inference without assuming that +the loader returned coherent intern tables. That fact follows from execution. -/ +def CachedConstantInferenceSupport.afterCoherentInference {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {fuel : Nat} {before keyed after : TcState .anon} + {id requested : KId .anon} {arguments requestedArguments : Array (KUniv .anon)} + {info requestedInfo : ExprInfo .anon} {key : Address × Address} {result : KExpr .anon} + {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} + (support : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) + (closed : (KExpr.const id arguments info).lbr = 0) + (keyRun : TcM.inferKey (.const requested requestedArguments requestedInfo) before = .ok key keyed) + (different : key ≠ ((KExpr.const id arguments info).addr, emptyCtxAddr)) + (loader : VerifiedLazySupport keyed requested.addr) (coherent : before.env.intern.WF) + (resources : ∀ concrete loaded, TcM.getConst requested keyed = .ok concrete loaded → + (KExpr.CollisionFree fun candidate => loaded.env.intern.ExprSupport candidate ∨ + KExpr.InstUnivReach requestedArguments concrete.ty candidate) ∧ + UniverseSubstitutionSupport requestedArguments concrete.ty) + (accepted : RecM.infer (.const requested requestedArguments requestedInfo) + (methodsN fuel) before = .ok result after) : + CachedConstantInferenceSupport resolve entries after id arguments info ref entry type := + let frame := infer_coherentConst_cache_frame keyRun different loader coherent resources accepted + support.transport closed frame.1 frame.2.1 + /-- A later constant's actual returned type inherits typing from its earlier witness after recursive inference; no semantic premise about caches is added. -/ theorem CachedConstantInferenceSupport.sound_after_inference {β : Type u} diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 6b5499e41..4478c9fd6 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -1151,9 +1151,156 @@ private def blockCacheCases : TestSeq := ++ test "block cache: an unknown root retains a completed block publication and both warm slots" (cacheAcrossBlockFailure 4) +private def internKeysCoherent (table : InternTable .anon) : Bool := + table.univs.toList.all (fun (key, level) => level.addr == key) && + table.exprs.toList.all (fun (key, term) => term.internKey == key) + +/-- Deriving the loop bound must also work on source trees deeper than the +runtime call stack. Build the expected value independently in forward order. -/ +private def coherentDeepUniverse : Bool := Id.run do + let depth := 4096 + let mut source : Ixon.Univ := .zero + let mut expected : KUniv .anon := .mkZero + for _ in [0:depth] do + source := .succ source + expected := .mkSucc expected + return match convertUnivTree source .empty with + | .ok result table => result.addr == expected.addr && table.univs.size == depth + 1 && + table.exprs.isEmpty && internKeysCoherent table + | .error _ _ => false + +private def coherentUniverseBranches : Bool := + let source := Ixon.Univ.imax (.max (.var 0) (.succ .zero)) (.max (.var 1) (.var 2)) + let expected := KUniv.mkIMax (m := .anon) (.mkMax (.mkParam 0 ()) levelOne) + (.mkMax (.mkParam 1 ()) (.mkParam 2 ())) + match convertUnivTree source .empty with + | .ok result table => result.addr == expected.addr && internKeysCoherent table + | .error _ _ => false + +private def coherentDeepExpression (binder : Bool) : Bool := Id.run do + let depth := 4096 + let mut source : Ixon.Expr := .var 0 + let mut expected : KExpr .anon := .mkVar 0 () + let baseVar := KExpr.mkVar (m := .anon) 0 () + let sort := KExpr.mkSort (m := .anon) levelOne + for _ in [0:depth] do + if binder then + source := .leanLam (.sort 0) source + expected := .mkLam () () sort expected + else + source := .app source (.var 0) + expected := .mkApp expected baseVar + let ctx : IngressCtx := {sharing := #[], refs := #[], univs := #[.succ .zero], mutCtx := #[]} + return match convertExpr {} ctx source {} .empty with + | .ok (result, cache) table => result.addr == expected.addr && internKeysCoherent table && + table.exprs.size == depth + (if binder then 2 else 1) && + cache.univCache.size == (if binder then 1 else 0) + | .error _ _ => false + +/-- A long acyclic sharing chain nearly consumes the derived step bound. +Both forward and backward references must finish and memoize every expansion. -/ +private def coherentSharingChain (forward : Bool) : Bool := + let depth := 2048 + let sharing : Array Ixon.Expr := (Array.range depth).map fun idx => + if forward then + if idx + 1 < depth then .share (idx + 1).toUInt64 else .var 7 + else if idx == 0 then .var 7 else .share (idx - 1).toUInt64 + let root := Ixon.Expr.share (if forward then 0 else (depth - 1).toUInt64) + let ctx : IngressCtx := {sharing, refs := #[], univs := #[], mutCtx := #[]} + let expected := KExpr.mkVar (m := .anon) 7 () + match convertExpr {} ctx root {} .empty with + | .ok (result, cache) table => result.addr == expected.addr && internKeysCoherent table && + table.exprs.size == 1 && table.univs.isEmpty && cache.exprCache.size == depth && + cache.exprCache.toList.all (fun (_, value) => value.addr == expected.addr) + | .error _ _ => false + +private def coherentSharedDiamond : Bool := + let sharing := #[Ixon.Expr.leanLam (.sort 0) (.var 0)] + let ctx : IngressCtx := {sharing, refs := #[], univs := #[.succ .zero], mutCtx := #[]} + let function := KExpr.mkLam (m := .anon) () () (.mkSort levelOne) (.mkVar 0 ()) + match convertExpr {} ctx (.app (.share 0) (.share 0)) {} .empty with + | .ok (result, cache) table => result.addr == (KExpr.mkApp function function).addr && + internKeysCoherent table && cache.exprCache.size == 1 && cache.univCache.size == 1 && + table.exprs.size == 4 + | .error _ _ => false + +private def coherentUnusedCycle : Bool := + let ctx : IngressCtx := {sharing := #[.share 0], refs := #[], univs := #[], mutCtx := #[]} + match convertExpr {} ctx (.var 0) {} .empty with + | .ok (result, cache) table => result.addr == (KExpr.mkVar (m := .anon) 0 ()).addr && + internKeysCoherent table && cache.exprCache.isEmpty && table.exprs.size == 1 + | .error _ _ => false + +/-- Cyclic source sharing now returns a bounded diagnostic through the real +fault hook. Warm entries and coherent partial conversion survive the error. -/ +private def coherenceAfterCyclicLoad (block : Bool) : Bool := + let (source, warmAddr) := polymorphicIdentity + let level : Ixon.Univ := .succ (.succ (.succ (.succ (.succ .zero)))) + let broken : Ixon.Definition := ⟨.defn, .safe, 0, .sort 0, .share 0⟩ + let info := if block then Ixon.ConstantInfo.muts + #[.defn ⟨.defn, .safe, 0, .sort 0, .sort 0⟩, .defn broken] else .defn broken + let sharing := if block then #[Ixon.Expr.share 1, .share 0] else #[Ixon.Expr.share 0] + let constant : Ixon.Constant := ⟨info, sharing, #[], #[level]⟩ + let (source, storedAddr) := if block then storeMutsWithProjs source constant else storeConst source constant + let requested : KId .anon := ⟨if block then defnProjAddr storedAddr 1 else storedAddr, ()⟩ + let warm := KExpr.mkConst (m := .anon) ⟨warmAddr, ()⟩ #[levelOne] + let action : RecM .anon Bool := do + let expected ← warmBothCaches warm + let key ← TcM.inferKey warm + let before ← get + let rejected ← try + let _ ← liftM (TcM.getConst requested) + pure false + catch err => pure (((toString err).splitOn "conversion step bound exhausted").length > 1) + let failed ← get + let retry ← try + let _ ← liftM (TcM.getConst requested) + pure false + catch err => + match err with + | .unknownConst addr => pure (addr == requested.addr) + | _ => pure false + let reused ← RecM.inferCall warm + let after ← get + return rejected && retry && reused.addr == expected.addr && warmSlotsRetained key before failed && + internKeysCoherent before.env.intern && internKeysCoherent failed.env.intern && + failed.env.intern.exprs.size > before.env.intern.exprs.size && + failed.env.intern.univs.size > before.env.intern.univs.size && + failed.env.consts.size == before.env.consts.size && failed.env.blocks.size == before.env.blocks.size && + failed.env.inferCache.size == before.env.inferCache.size && + failed.env.inferOnlyCache.size == before.env.inferOnlyCache.size && + failed.env.nextFVarId == before.env.nextFVarId && failed.faultedAddrs.contains requested.addr && + after.env.intern.exprs.size == failed.env.intern.exprs.size && + after.env.intern.univs.size == failed.env.intern.univs.size + match TcM.runRec action (TcState.newLazyAnon source) with + | .ok passed _ => passed + | .error _ _ => false + +private def ingressCoherenceCases : TestSeq := + test "ingress coherence: deep universe conversion uses a bounded worklist" + coherentDeepUniverse + ++ test "ingress coherence: bounded universe conversion retains max/imax normalization" + coherentUniverseBranches + ++ test "ingress coherence: deep application conversion and its counting pass finish" + (coherentDeepExpression false) + ++ test "ingress coherence: deep binder conversion retains universe memoization" + (coherentDeepExpression true) + ++ test "ingress coherence: a long forward sharing chain fits the source-derived bound" + (coherentSharingChain true) + ++ test "ingress coherence: a long backward sharing chain fits the source-derived bound" + (coherentSharingChain false) + ++ test "ingress coherence: repeated sharing is converted once" + coherentSharedDiamond + ++ test "ingress coherence: an unused cyclic sharing entry is not expanded" + coherentUnusedCycle + ++ test "ingress coherence: cyclic standalone sharing fails with coherent partial state" + (coherenceAfterCyclicLoad false) + ++ test "ingress coherence: cyclic block sharing retains warm witnesses without publication" + (coherenceAfterCyclicLoad true) + public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, - lazyCacheCases, blockCacheCases] + lazyCacheCases, blockCacheCases, ingressCoherenceCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 5ec4c8b84..b11ed37f8 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -139,6 +139,20 @@ increasing sequence of strongly inaccessible cardinals. recursive transport also covers loads inside application and binder trees. Block overlap agreement remains a data premise. The source admission and other recursive paths still need proofs. +- `getConst_coherent` derives intern-table key coherence through the actual + loader on success and error. Universe and expression conversion now use + finite step bounds computed from source syntax; their counting passes and + conversion loops use worklists to handle deep inputs. Exhausted cyclic + sharing returns an error with coherent partial state. The proof follows every + conversion form, publication, and fault dispatch without collision or + declaration-overlap premises. + `UniverseInstantiationSupport.afterVerifiedGetConst` builds the walker's + coherence field from the pre-load invariant. `infer_verifiedConst_coherent` + carries it through key computation, loading, substitution, and the final + cache write. `CachedConstantInferenceSupport.afterCoherentInference` reuses + the earlier witness with coherence required only before the call. Finite + collision and level resources, source agreement, and block overlap checks + remain explicit. - `checkEnvAnon_atomic_preserves_model` connects a supported production environment run to model extension. `checkEnvAnon_atomic_no_false` excludes a declaration at an axiom type interpreted as empty, including False. @@ -222,8 +236,12 @@ def useF (x : T.{1}) : T.{1} := f.{1} x inference. Constant misses use already-loaded declarations or verified standalone/block loading, with finite universe-walker resources after lookup; applications use full mode and hash conversion. The new dependency's semantic - source agreement, block overlap checks, and intern coherence remain explicit - resources. Preservation at written keys and automatic trace construction remain open. + source agreement, block overlap checks, and finite collision/level resources + remain explicit. Intern coherence follows through actual loading from the + pre-load invariant; successful constant inference also returns coherence for + the next operation. Initial coherence holds for `TcState.newLazyAnon`. Extending + its preservation to every checker operation, preservation at written keys, + and automatic trace construction remain open. The operational trace can frame any selected cache hit, while semantic typing of composite hits remains outside `BinderInference`. - Binder definitions supply finite inference trees for both the value and its @@ -319,7 +337,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 217 exact theorem boundaries. The production +The consistency target checks 235 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -374,6 +392,11 @@ deduplication, missing or corrupt parents, preparation failures after earlier members converted, and an unknown block-root lookup that retains completed publication. These exercise loading and cache preservation; they do not establish semantic admission of inductives or recursors. +Intern-coherence regressions include 4,096-deep universe, application, and +binder trees; forward and backward sharing chains; repeated sharing; and +unexpanded cyclic entries. Cyclic standalone and block loads must return a +bounded diagnostic, preserve both warm cache partitions and coherent partial +intern state, and deduplicate retries without publishing declarations. ## Certified host adapters @@ -406,6 +429,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Recursive cache preservation and witness reuse | [`Consistency/RecursiveCache.lean`](../Ix/Kernel/Verify/Consistency/RecursiveCache.lean) | | Verified standalone lazy loading and cache frames | [`Consistency/LazyCache.lean`](../Ix/Kernel/Verify/Consistency/LazyCache.lean) | | Mutual-block publication and verified lookup frames | [`Consistency/BlockCache.lean`](../Ix/Kernel/Verify/Consistency/BlockCache.lean) | +| Intern coherence through conversion and lazy loading | [`Consistency/IngressCoherence.lean`](../Ix/Kernel/Verify/Consistency/IngressCoherence.lean) | | Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`Application.lean`](../Ix/Kernel/Verify/Consistency/Application.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | | Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | diff --git a/docs/theory.md b/docs/theory.md index 85f6561ba..f1e9bd8f5 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -43,9 +43,13 @@ deduplicated faults. This covers standalone and mutual-block loading. Block publication requires every prepared entry to agree with any old declaration at its key, admitting fresh entries and exact repeats in a partially loaded block. Recursive constant leaves use this result, so an earlier witness survives -inference that loads another dependency. Initial agreement, block overlap checks, -post-lookup interning resources, trace construction, and preservation for keys -that are written remain obligations. +inference that loads another dependency. Key coherence of the intern tables +is now derived through every production conversion form and the actual loader, +including errors. Conversion uses bounds computed from source syntax and +rejects exhausted cyclic sharing with coherent partial state. Constant inference +carries pre-call coherence through lookup, substitution, and cache publication. +Initial cache agreement, block overlap checks, finite collision/level resources, +trace construction, and preservation for keys that are written remain obligations. Full checker consistency and compiler/backend refinement remain open. From 7ec2e6760f490a768774aa66e84ee6524d68f050 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 20:14:07 -0400 Subject: [PATCH 17/63] Derive lazy block compatibility from source ownership --- Ix/Kernel/SourceOwnership.lean | 60 ++ Ix/Kernel/Verify/Consistency.lean | 13 +- Ix/Kernel/Verify/Consistency/Audit.lean | 26 +- .../Verify/Consistency/BlockOwnership.lean | 645 ++++++++++++++++++ .../Verify/Consistency/RecursiveCache.lean | 96 ++- .../Consistency/SourceOwnershipCheck.lean | 63 ++ Tests/Ix/Kernel/Consistency.lean | 142 +++- docs/kernel-verification.md | 41 +- docs/theory.md | 12 +- 9 files changed, 1082 insertions(+), 16 deletions(-) create mode 100644 Ix/Kernel/SourceOwnership.lean create mode 100644 Ix/Kernel/Verify/Consistency/BlockOwnership.lean create mode 100644 Ix/Kernel/Verify/Consistency/SourceOwnershipCheck.lean diff --git a/Ix/Kernel/SourceOwnership.lean b/Ix/Kernel/SourceOwnership.lean new file mode 100644 index 000000000..5eb9d6901 --- /dev/null +++ b/Ix/Kernel/SourceOwnership.lean @@ -0,0 +1,60 @@ +module + +public import Ix.Kernel.Ingress + +/-! +Finite source-header ownership checks for the verified lazy loader. +The checker does not convert expressions or inspect a kernel environment. +-/ + +public section +@[expose] section + +namespace Ix.Kernel + +/-- The finite projection keys contributed by one source member. -/ +def memberProjectionIds (block : Address) (idx : UInt64) : Ixon.MutConst → Array (KId .anon) + | .defn _ => #[⟨defnProjAddr block idx, ()⟩] + | .recr _ => #[⟨recrProjAddr block idx, ()⟩] + | .indc ind => #[⟨indcProjAddr block idx, ()⟩] ++ + (anonCtorAddrs block idx ind).map (⟨·, ()⟩) + +/-- Enumerate actual source projection keys, including constructors. -/ +def blockProjectionIds (block : Address) (constant : Ixon.Constant) : Array (KId .anon) := + match constant.info with + | .muts members => (Array.range members.size).flatMap fun idx => + memberProjectionIds block idx.toUInt64 members[idx]! + | _ => #[] + +/-- Address data for a verified source header. Repeated keys within one block +are permitted; only different owners or a standalone/projection overlap reject. -/ +structure OwnershipRow where + addr : Address + projections : Array (KId .anon) + standalone : Bool + +def ownershipRow (addr : Address) (constant : Ixon.Constant) : OwnershipRow := + ⟨addr, blockProjectionIds addr constant, (ingressBlockAddr? addr constant.info).isNone⟩ + +/-- Only verified, parsed constants can be used by the production loader. -/ +def sourceOwnershipRows (source : Ixon.Env) : List OwnershipRow := + source.consts.toList.filterMap fun (addr, _) => + match getConstVerified source addr true with + | .ok (some constant) => some (ownershipRow addr constant) + | _ => none + +/-- A finite comparison of source projection ownership and standalone keys. -/ +def ownershipRowsCheck (rows : List OwnershipRow) : Bool := + rows.all fun left => rows.all fun right => + (left.projections.all fun id => !right.projections.contains id || left.addr == right.addr) && + (!left.standalone || !right.projections.contains ⟨left.addr, ()⟩) + +/-- Optional source-only preflight for the ownership preservation theorems. +Failure reports conflicting ownership; it does not change loader behavior. -/ +def sourceOwnershipCheck (source : Ixon.Env) : Bool := + ownershipRowsCheck (sourceOwnershipRows source) + +end Ix.Kernel + +end +end diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 294f89d20..4cf284c96 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -19,6 +19,8 @@ import Ix.Kernel.Verify.Consistency.ConstantCache import Ix.Kernel.Verify.Consistency.LazyCache import Ix.Kernel.Verify.Consistency.BlockCache import Ix.Kernel.Verify.Consistency.IngressCoherence +import Ix.Kernel.Verify.Consistency.BlockOwnership +import Ix.Kernel.Verify.Consistency.SourceOwnershipCheck import Ix.Kernel.Verify.Consistency.Context import Ix.Kernel.Verify.Consistency.BinderOpening import Ix.Kernel.Verify.Consistency.Application @@ -78,7 +80,14 @@ is proved through every conversion form and the actual loader on both outcomes. Constant inference derives post-lookup coherence from its initial state and returns coherence after substitution and cache publication, so the next call can reuse it. Finite collision and level resources remain explicit. -Initial agreement, block overlap checks, finite execution resources, trace -construction, and preservation for keys inside the footprint remain obligations. +For a fixed source, a finite header check establishes disjoint block ownership. +Actual conversion emits only the enumerated projection keys, and atomic +publication records their owner. The invariant that loaded projections have a +recorded block starts empty and survives lookup on both outcomes and successful +constant inference. It derives fresh entries for unrecorded blocks, so these +calls need no per-block overlap premise. Externally partially populated states +can still use the general compatibility resource. +Initial agreement, general state/source agreement, finite execution resources, +trace construction, and preservation inside the footprint remain obligations. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 710798dd6..2bf82c83e 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -127,6 +127,22 @@ private def lazyCacheRoots : Array Lean.Name := #[ ``UniverseInstantiationSupport.afterVerifiedGetConst ] +private def sourceOwnershipRoots : Array Lean.Name := #[ + ``SourceOwnership.ofOwner, ``LoadedBlockInvariant.empty, + ``LoadedBlockInvariant.ofMaps, ``LoadedBlockInvariant.intern, + ``sourceOwnershipRows_mem, ``SourceOwnership.ofCheck +] + +private def ownedLoaderRoots : Array Lean.Name := #[ + ``convertAnonBlock_projection_keys, ``LoadedBlockInvariant.compatible, + ``LoadedBlockInvariant.materialization, ``ingressAnonBlock_blocks, + ``ingressAnonAddrShallow_blocks, ``OwnedLazySupport.toVerified, + ``lazyIngressAddr_blocks, ``tryGetConst_blocks, ``getConst_owned, + ``OwnedLazySupport.afterGetConst, ``OwnedLazySupport.afterFailedGetConst, + ``OwnedLazySupport.afterInferKey, ``InferenceCacheTrace.ownedConstOfKey, + ``OwnedLazySupport.afterConstInference, ``CachedConstantInferenceSupport.afterOwnedInference +] + private def productionRoots : Array Lean.Name := #[ ``StandalonePrefix.member_success, ``definition_body_trace, ``AtomicDefinitionRun.sound, ``AtomicDefinitionRun.no_self_alias, @@ -171,6 +187,12 @@ def roots : Array RootAllowance := #[ { root := ``newLazyAnon_intern_coherent, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative, nameNative], forbiddenDependencies := forbiddenProduction }, + { root := ``OwnedLazySupport.newLazyAnon, standardAxioms := standard, + nativeAxioms := #[expressionNative, levelNative, nameNative], + forbiddenDependencies := forbiddenProduction }, + { root := ``OwnedLazySupport.ofCheckedSource, standardAxioms := standard, + nativeAxioms := #[expressionNative, levelNative, nameNative], + forbiddenDependencies := forbiddenProduction }, { root := ``readLevel_eq, standardAxioms := #[``propext] }, { root := ``readLevel_eval, standardAxioms := #[``propext] }, { root := ``readLevel_wf, standardAxioms := #[``propext] }, @@ -226,12 +248,12 @@ def roots : Array RootAllowance := #[ { root := ``Theory.Model.CheckingClaim.lam, standardAxioms := standard } ] ++ (scopedRoots ++ cacheFrameRoots).map (fun root => { root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction -}) ++ (contextRoots ++ cacheMapRoots).map (fun root => { +}) ++ (contextRoots ++ cacheMapRoots ++ sourceOwnershipRoots).map (fun root => { root, standardAxioms := standard, forbiddenDependencies := forbiddenProduction }) ++ (binderWalkerRoots ++ cacheKeyRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction -}) ++ (atomicRoots ++ instantiationRoots ++ recursiveCacheRoots ++ lazyCacheRoots).map (fun root => { +}) ++ (atomicRoots ++ instantiationRoots ++ recursiveCacheRoots ++ lazyCacheRoots ++ ownedLoaderRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) ++ productionRoots.map (fun root => { diff --git a/Ix/Kernel/Verify/Consistency/BlockOwnership.lean b/Ix/Kernel/Verify/Consistency/BlockOwnership.lean new file mode 100644 index 000000000..c38f407eb --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BlockOwnership.lean @@ -0,0 +1,645 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.IngressCoherence +import Ix.Kernel.SourceOwnership + +/-! +# Source ownership and complete lazy blocks + +Projection ownership depends only on source headers and deterministic addresses. +Once any projection is loaded, its block is recorded. This invariant makes the +entries of an unrecorded block fresh, independently of expression conversion. +-/ + +namespace Ix.Kernel.Consistency + +private def publicationBlock? : KConst .anon → Option (KId .anon) + | .defn (block := block) .. | .recr (block := block) .. | .indc (block := block) .. => some block + | _ => none + +private def HeadOwned (block : Address) (entries : Array Entry) : Prop := + ∀ entry, entries[0]? = some entry → publicationBlock? entry.2 = some ⟨block, ()⟩ + +private def BlockShape (block : Address) (keys : Array (KId .anon)) (entries : Array Entry) : Prop := + (∀ entry ∈ entries, entry.1 ∈ keys) ∧ HeadOwned block entries + +private def InternEnsures (action : InternIngressM α) (P : α → Prop) : Prop := + ∀ before value after, action before = .ok value after → P value + +private theorem ensures_pure (value : α) (P : α → Prop) (holds : P value) : + InternEnsures (pure value) P := by + intro before result after run + cases run + exact holds + +private theorem ensures_throw (err : IngressErr) (P : α → Prop) : + InternEnsures (throw err) P := by + intro before result after run + cases run + +private theorem ensures_throw_bind (err : IngressErr) (next : α → InternIngressM β) (P : β → Prop) : + InternEnsures ((throw err : InternIngressM α) >>= next) P := by + intro before result after run + cases run + +private theorem ensures_bind {action : InternIngressM α} {next : α → InternIngressM β} + {P : α → Prop} {Q : β → Prop} + (first : InternEnsures action P) (rest : ∀ value, P value → InternEnsures (next value) Q) : + InternEnsures (action >>= next) Q := by + intro before result after run + change EStateM.bind action next before = _ at run + rw [EStateM.bind] at run + cases step : action before with + | error err failed => rw [step] at run; contradiction + | ok value middle => + rw [step] at run + exact rest value (first before value middle step) middle result after run + +private theorem ensures_then {action : InternIngressM α} {next : α → InternIngressM β} + {Q : β → Prop} (rest : ∀ value, InternEnsures (next value) Q) : + InternEnsures (action >>= next) Q := + ensures_bind (P := fun _ => True) (fun _ _ _ _ => True.intro) (fun value _ => rest value) + +private theorem ensures_forIn' (items : List α) (initial : β) (P : β → Prop) + (body : (item : α) → item ∈ items → β → InternIngressM (ForInStep β)) + (holds : P initial) + (step : ∀ item member state, P state → InternEnsures (body item member state) + (fun result => match result with | .done next | .yield next => P next)) : + InternEnsures (forIn' items initial body) P := by + induction items generalizing initial with + | nil => simpa only [List.forIn'_nil] using ensures_pure initial P holds + | cons item rest ih => + rw [List.forIn'_cons] + refine ensures_bind (Q := P) (step item (List.mem_cons_self) initial holds) ?_ + intro result preserved + cases result with + | done state => exact ensures_pure state P preserved + | yield state => + exact ih state _ preserved + (fun item member state valid => step item (List.mem_cons_of_mem _ member) state valid) + +private theorem ensures_range (range : Std.Legacy.Range) (initial : β) (P : β → Prop) + (body : (item : Nat) → item ∈ range → β → InternIngressM (ForInStep β)) + (holds : P initial) + (step : ∀ item member state, P state → InternEnsures (body item member state) + (fun result => match result with | .done next | .yield next => P next)) : + InternEnsures (forIn' range initial body) P := by + rw [Std.Legacy.Range.forIn'_eq_forIn'_range'] + exact ensures_forIn' _ initial P _ holds (fun item _ state valid => step item _ state valid) + +attribute [local irreducible] InternEnsures + +private theorem headOwned_empty (block : Address) : HeadOwned block #[] := by + intro entry found + simp at found + +private theorem headOwned_append {block : Address} {left right : Array Entry} + (hl : HeadOwned block left) (hr : HeadOwned block right) : HeadOwned block (left ++ right) := by + intro entry found + by_cases empty : left.size = 0 + · have equal : left = #[] := Array.eq_empty_of_size_eq_zero empty + exact hr entry (by simpa only [equal, Array.empty_append] using found) + · rw [Array.getElem?_append_left (by omega)] at found + exact hl entry found + +private theorem shape_empty (block : Address) (keys : Array (KId .anon)) : BlockShape block keys #[] := + ⟨by simp, headOwned_empty block⟩ + +private theorem shape_append {block : Address} {keys : Array (KId .anon)} {left right : Array Entry} + (hl : BlockShape block keys left) (hr : BlockShape block keys right) : + BlockShape block keys (left ++ right) := by + refine ⟨?_, headOwned_append hl.2 hr.2⟩ + intro entry member + rcases Array.mem_append.mp member with member | member + · exact hl.1 entry member + · exact hr.1 entry member + +private theorem shape_singleton {block : Address} {keys : Array (KId .anon)} {entry : Entry} + (key : entry.1 ∈ keys) (owned : publicationBlock? entry.2 = some ⟨block, ()⟩) : + BlockShape block keys #[entry] := by + refine ⟨?_, ?_⟩ + · intro value member + simpa using (Array.mem_singleton.mp member ▸ key) + · intro value found + simp only [Array.getElem?_singleton, ↓reduceIte] at found + cases found + exact owned + +private theorem shape_push {block : Address} {keys : Array (KId .anon)} + {entries : Array Entry} {entry : Entry} (shape : BlockShape block keys entries) + (nonempty : 0 < entries.size) (key : entry.1 ∈ keys) : + BlockShape block keys (entries.push entry) := by + refine ⟨?_, ?_⟩ + · intro value member + rcases Array.mem_push.mp member with member | equal + · exact shape.1 value member + · exact equal ▸ key + · intro value found + rw [Array.getElem?_push_lt nonempty] at found + exact shape.2 value (by simpa only [Array.getElem?_eq_getElem nonempty] using found) + +private theorem convertDefnAnon_block (source : Ixon.Env) (defn : Ixon.Definition) + (constant : Ixon.Constant) (block : KId .anon) (mutCtx : Array (KId .anon)) + (hints : Option Lean.ReducibilityHints) : + InternEnsures (convertDefnAnon source defn constant block mutCtx hints) + (fun concrete => publicationBlock? concrete = some block) := by + unfold convertDefnAnon + apply ensures_then + intro result + apply ensures_then + intro value + exact ensures_pure _ _ rfl + +private theorem convertRecursorAnon_block (source : Ixon.Env) (rec : Ixon.Recursor) + (constant : Ixon.Constant) (block : KId .anon) (mutCtx : Array (KId .anon)) : + InternEnsures (convertRecursorAnon source rec constant block mutCtx) + (fun concrete => publicationBlock? concrete = some block) := by + unfold convertRecursorAnon + apply ensures_then + intro result + apply ensures_then + intro value + exact ensures_pure _ _ rfl + +private theorem convertAnonInductive_shape (source : Ixon.Env) (ind : Ixon.Inductive) + (self : KId .anon) (constant : Ixon.Constant) (block : Address) (idx : UInt64) + (ctorAddrs : Array Address) (mutCtx : Array (KId .anon)) (keys : Array (KId .anon)) + (selfKey : self ∈ keys) + (ctorKeys : ∀ id ∈ ctorAddrs.map (⟨·, ()⟩ : Address → KId .anon), id ∈ keys) : + InternEnsures (convertAnonInductive source ind self constant ⟨block, ()⟩ idx ctorAddrs mutCtx) + (BlockShape block keys) := by + unfold convertAnonInductive + split + · exact ensures_throw_bind _ _ _ + · rename_i lengths + have sameSize : ctorAddrs.size = ind.ctors.size := by simpa using lengths + apply ensures_then + intro result + refine ensures_bind (P := fun state => BlockShape block keys state.1 ∧ 0 < state.1.size) + (Q := BlockShape block keys) ?_ ?_ + · apply ensures_range + · exact ⟨shape_singleton selfKey rfl, by simp⟩ + · intro cidx member state valid + apply ensures_then + intro converted + apply ensures_pure + refine ⟨shape_push valid.1 valid.2 ?_, by simp⟩ + apply ctorKeys + have bound : cidx < (ctorAddrs.map (⟨·, ()⟩ : Address → KId .anon)).size := by + simp only [Array.size_map, sameSize] + exact member.2.1 + dsimp only + rw [getElem!_pos (ctorAddrs.map (⟨·, ()⟩ : Address → KId .anon)) cidx bound] + exact Array.getElem_mem _ + · intro state valid + exact ensures_pure _ _ valid.1 + +private theorem memberProjectionIds_mem {block : Address} {constant : Ixon.Constant} + {members : Array Ixon.MutConst} (info : constant.info = .muts members) + {idx : Nat} (bound : idx < members.size) {id : KId .anon} + (member : id ∈ memberProjectionIds block idx.toUInt64 members[idx]) : + id ∈ blockProjectionIds block constant := by + simp only [blockProjectionIds, info] + apply Array.mem_flatMap.mpr + refine ⟨idx, Array.mem_range.mpr bound, ?_⟩ + simpa only [getElem!_pos members idx bound] using member + +private theorem convertAnonBlock_shape (source : Ixon.Env) (constant : Ixon.Constant) + (block : Address) : InternEnsures (convertAnonBlock source constant block) + (fun trace => BlockShape block (blockProjectionIds block constant) trace.allEntries) := by + unfold convertAnonBlock + cases info : constant.info with + | defn | recr | axio | quot | dPrj | iPrj | rPrj | cPrj => exact ensures_throw _ _ + | muts members => + refine ensures_bind (P := fun state : Array Entry × Array (KId .anon) => + BlockShape block (blockProjectionIds block constant) state.1) + (Q := fun trace : AnonBlockIngressTrace => + BlockShape block (blockProjectionIds block constant) trace.allEntries) ?_ ?_ + · apply ensures_range + · exact shape_empty _ _ + · intro idx member state valid + dsimp only + cases entry : members[idx] with + | defn defn => + apply ensures_then + intro _ + refine ensures_bind + (convertDefnAnon_block source defn constant ⟨block, ()⟩ _ _) ?_ + intro converted header + apply ensures_pure + apply shape_append valid + apply shape_singleton _ header + apply memberProjectionIds_mem info member.2.1 + simp only [entry, memberProjectionIds, Array.mem_singleton] + | recr rec => + apply ensures_then + intro _ + refine ensures_bind + (convertRecursorAnon_block source rec constant ⟨block, ()⟩ _) ?_ + intro converted header + apply ensures_pure + apply shape_append valid + apply shape_singleton _ header + apply memberProjectionIds_mem info member.2.1 + simp only [entry, memberProjectionIds, Array.mem_singleton] + | indc ind => + apply ensures_then + intro _ + apply ensures_then + intro _ + refine ensures_bind + (convertAnonInductive_shape source ind ⟨indcProjAddr block idx.toUInt64, ()⟩ + constant block idx.toUInt64 (anonCtorAddrs block idx.toUInt64 ind) _ + (blockProjectionIds block constant) + (by + apply memberProjectionIds_mem info member.2.1 + simp [entry, memberProjectionIds]) + (by + intro id ctor + apply memberProjectionIds_mem info member.2.1 + simp only [entry, memberProjectionIds] + exact Array.mem_append.mpr (Or.inr ctor))) ?_ + intro converted shape + exact ensures_pure _ _ (shape_append valid shape) + · intro state valid + exact ensures_pure _ _ valid + +attribute [local semireducible] InternEnsures + +/-- Successful production conversion emits only the finite projection keys +computed from this source block's headers. Interning needs no extra premise. -/ +theorem convertAnonBlock_projection_keys {source : Ixon.Env} {constant : Ixon.Constant} + {block : Address} {before after : InternTable .anon} {trace : AnonBlockIngressTrace} + (run : convertAnonBlock source constant block before = .ok trace after) : + ∀ entry ∈ trace.allEntries, entry.1 ∈ blockProjectionIds block constant := + (convertAnonBlock_shape source constant block before trace after run).1 + +/-- A projection belongs to a block verified in this fixed source. The key +inventory uses only declaration headers, member indices, and constructor counts. -/ +def SourceProjection (source : Ixon.Env) (block : Address) (id : KId .anon) : Prop := + ∃ constant, getConstVerified source block true = .ok (some constant) ∧ + id ∈ blockProjectionIds block constant + +/-- Finite source address separation. Different verified blocks have disjoint +projection inventories, and verified standalones have no projection key. +This says nothing about converted expressions, caches, or final checker states. -/ +structure SourceOwnership (source : Ixon.Env) : Prop where + blocks : ∀ block other id, SourceProjection source block id → + SourceProjection source other id → block = other + standalone : ∀ addr constant, getConstVerified source addr true = .ok (some constant) → + ingressBlockAddr? addr constant.info = none → + ∀ block, ¬ SourceProjection source block ⟨addr, ()⟩ + +/-- A source-only ownership table can discharge separation by finite lookups. -/ +theorem SourceOwnership.ofOwner {source : Ixon.Env} (owner : KId .anon → Option Address) + (projections : ∀ block id, SourceProjection source block id → owner id = some block) + (standalones : ∀ addr constant, getConstVerified source addr true = .ok (some constant) → + ingressBlockAddr? addr constant.info = none → owner ⟨addr, ()⟩ = none) : + SourceOwnership source := by + refine ⟨?_, ?_⟩ + · intro block other id left right + exact Option.some.inj ((projections block id left).symm.trans (projections other id right)) + · intro addr constant verified standalone block projection + have owned := projections block ⟨addr, ()⟩ projection + rw [standalones addr constant verified standalone] at owned + contradiction + +/-- Every loaded source projection has an already-recorded owning block. +An unrecorded block therefore cannot overlap the current declaration map. -/ +def LoadedBlockInvariant (source : Ixon.Env) (env : AnonEnv) : Prop := + ∀ block id, SourceProjection source block id → ∀ concrete, + env.get? id = some concrete → env.blocks.contains ⟨block, ()⟩ = true + +theorem LoadedBlockInvariant.empty (source : Ixon.Env) : LoadedBlockInvariant source {} := by + intro block id projection concrete loaded + simp [KEnv.get?] at loaded + +theorem LoadedBlockInvariant.ofMaps {source : Ixon.Env} {before after : AnonEnv} + (valid : LoadedBlockInvariant source before) + (constants : after.consts = before.consts) (blocks : after.blocks = before.blocks) : + LoadedBlockInvariant source after := by + intro block id projection concrete loaded + rw [blocks] + exact valid block id projection concrete (by simpa only [KEnv.get?, constants] using loaded) + +theorem LoadedBlockInvariant.intern {source : Ixon.Env} {env : AnonEnv} + (valid : LoadedBlockInvariant source env) (table : InternTable .anon) : + LoadedBlockInvariant source {env with intern := table} := valid + +private theorem invariant_insertBlock {source : Ixon.Env} {env : AnonEnv} + (valid : LoadedBlockInvariant source env) (id : KId .anon) (members : Array (KId .anon)) : + LoadedBlockInvariant source (env.insertBlock id members) := by + intro block key projection concrete loaded + have recorded := valid block key projection concrete loaded + simp only [KEnv.insertBlock, Std.HashMap.contains_insert, recorded, Bool.or_true] + +private theorem invariant_insert {source : Ixon.Env} {env : AnonEnv} + (valid : LoadedBlockInvariant source env) (entry : Entry) + (recorded : ∀ block, SourceProjection source block entry.1 → + env.blocks.contains ⟨block, ()⟩ = true) : + LoadedBlockInvariant source (env.insert entry.1 entry.2) := by + intro block id projection concrete loaded + by_cases equal : entry.1 = id + · subst id + exact recorded block projection + · apply valid block id projection concrete + simpa only [KEnv.get?, KEnv.insert, Std.HashMap.getElem?_insert, beq_iff_eq, + equal, ↓reduceIte] using loaded + +private theorem invariant_insert_list {source : Ixon.Env} {env : AnonEnv} + (valid : LoadedBlockInvariant source env) (entries : List Entry) + (recorded : ∀ entry ∈ entries, ∀ block, SourceProjection source block entry.1 → + env.blocks.contains ⟨block, ()⟩ = true) : + LoadedBlockInvariant source + (entries.foldl (fun current entry => current.insert entry.1 entry.2) env) := by + induction entries generalizing env with + | nil => exact valid + | cons entry rest ih => + exact ih (invariant_insert valid entry (recorded entry (List.mem_cons_self))) + (fun item member => recorded item (List.mem_cons_of_mem _ member)) + +private theorem publish_shape_invariant {source : Ixon.Env} {constant : Ixon.Constant} + {block : Address} {env : AnonEnv} {entries : Array Entry} + (sourceOwned : SourceOwnership source) + (verified : getConstVerified source block true = .ok (some constant)) + (valid : LoadedBlockInvariant source env) + (shape : BlockShape block (blockProjectionIds block constant) entries) : + LoadedBlockInvariant source (insertMutsEntriesState env entries) := by + unfold insertMutsEntriesState insertEntriesState + change LoadedBlockInvariant source (entries.toList.foldl + (fun current entry => current.insert entry.1 entry.2) + (match entries[0]?.bind (fun entry => publicationBlock? entry.2) with + | some id => env.insertBlock id (entries.map (·.1)) + | none => env)) + cases first : entries[0]? with + | none => + have empty : entries = #[] := by + apply Array.eq_empty_of_size_eq_zero + have := Array.getElem?_eq_none_iff.mp first + omega + simpa only [empty, Array.toList_empty, List.foldl_nil, Option.bind_none] using valid + | some entry => + rw [Option.bind_some, shape.2 entry first] + apply invariant_insert_list (invariant_insertBlock valid _ _) + intro item member other projection + have own : SourceProjection source block item.1 := + ⟨constant, verified, shape.1 item (by simpa using member)⟩ + have equal := sourceOwned.blocks block other item.1 own projection + subst other + simp [KEnv.insertBlock] + +/-- Any actual preparation of an unrecorded block has entirely fresh keys. +No per-entry comparison with old converted declarations is required. -/ +theorem LoadedBlockInvariant.compatible {source : Ixon.Env} {constant : Ixon.Constant} + {block : Address} {before : AnonEnv} + (valid : LoadedBlockInvariant source before) + (verified : getConstVerified source block true = .ok (some constant)) + (unrecorded : before.blocks.contains ⟨block, ()⟩ ≠ true) : + BlockEntriesCompatible source constant block before := by + apply BlockEntriesCompatible.ofFresh + intro trace converted run entry member + unfold prepareAnonBlock IngressM.runIntern at run + cases conversion : convertAnonBlock source constant block before.intern with + | error err failed => rw [conversion] at run; contradiction + | ok result table => + rw [conversion] at run + cases run + have projection : SourceProjection source block entry.1 := + ⟨constant, verified, convertAnonBlock_projection_keys conversion entry member⟩ + cases loaded : before.get? entry.1 with + | none => rfl + | some concrete => exact False.elim (unrecorded (valid block entry.1 projection concrete loaded)) + +theorem LoadedBlockInvariant.materialization {source : Ixon.Env} {before : AnonEnv} + (valid : LoadedBlockInvariant source before) (addr : Address) : + LazyMaterializationSupport source addr before := by + unfold LazyMaterializationSupport + cases getConstVerified source addr true with + | error => trivial + | ok optional => + cases optional with + | none => trivial + | some constant => + dsimp only + cases ingressBlockAddr? addr constant.info with + | none => trivial + | some block => + dsimp only + split + · trivial + · rename_i unrecorded + cases verified : getConstVerified source block true with + | error => trivial + | ok optional => + cases optional with + | none => trivial + | some constant => exact valid.compatible verified unrecorded + +private theorem ingressAnonStandalone_blocks {source : Ixon.Env} {addr : Address} + {constant : Ixon.Constant} (sourceOwned : SourceOwnership source) + (verified : getConstVerified source addr true = .ok (some constant)) + (standalone : ingressBlockAddr? addr constant.info = none) + (before : AnonEnv) (valid : LoadedBlockInvariant source before) : + match ingressAnonStandalone source addr constant before with + | .ok _ after | .error _ after => LoadedBlockInvariant source after := by + unfold ingressAnonStandalone + change (match (EStateM.bind (IngressM.runIntern (convertAnonStandalone source addr constant)) + _ : IngressM (KId .anon)) before with + | .ok _ after | .error _ after => LoadedBlockInvariant source after) + unfold EStateM.bind IngressM.runIntern + cases converted : convertAnonStandalone source addr constant before.intern with + | error err table => exact valid.intern table + | ok concrete table => + change (match (EStateM.bind (insertStandaloneEntries #[(⟨addr, ()⟩, concrete)]) + _ : IngressM (KId .anon)) {before with intern := table} with + | .ok _ after | .error _ after => LoadedBlockInvariant source after) + rw [EStateM.bind, insertStandaloneEntries_singleton] + cases reservedMarkerName addr with + | some marker => exact valid.intern table + | none => + apply invariant_insertBlock + apply invariant_insert (valid.intern table) (⟨addr, ()⟩, concrete) + intro block projection + exact False.elim (sourceOwned.standalone addr constant verified standalone block projection) + +/-- Whole-block loading preserves the ownership invariant on both outcomes, +including failure after partial conversion and successful flat publication. -/ +theorem ingressAnonBlock_blocks {source : Ixon.Env} {constant : Ixon.Constant} + {block : Address} (sourceOwned : SourceOwnership source) + (verified : getConstVerified source block true = .ok (some constant)) + (before : AnonEnv) (valid : LoadedBlockInvariant source before) : + match ingressAnonBlock source constant block before with + | .ok _ after | .error _ after => LoadedBlockInvariant source after := by + unfold ingressAnonBlock ingressAnonBlockWithTrace + change (match (EStateM.bind (EStateM.bind (prepareAnonBlock source constant block) + _) _ : IngressM (Array (KId .anon))) before with + | .ok _ after | .error _ after => LoadedBlockInvariant source after) + unfold prepareAnonBlock IngressM.runIntern EStateM.bind + dsimp only + cases converted : convertAnonBlock source constant block before.intern with + | error err table => exact valid.intern table + | ok trace table => + have shape := convertAnonBlock_shape source constant block before.intern trace table converted + change (match (EStateM.bind (EStateM.bind (insertMutsEntries trace.allEntries) + _) _ : IngressM (Array (KId .anon))) {before with intern := table} with + | .ok _ after | .error _ after => LoadedBlockInvariant source after) + simp only [insertMutsEntries, Bind.bind, EStateM.bind] + rw [guardReserved_state] + cases checkReserved trace.allEntries with + | error err => exact valid.intern table + | ok value => exact publish_shape_invariant sourceOwned verified (valid.intern table) shape + +/-- The source ownership condition and the current invariant suffice for +every shallow load. Source misses and all failure paths preserve the invariant. -/ +theorem ingressAnonAddrShallow_blocks {source : Ixon.Env} (sourceOwned : SourceOwnership source) + (addr : Address) (before : AnonEnv) (valid : LoadedBlockInvariant source before) : + match ingressAnonAddrShallow source addr true before with + | .ok _ after | .error _ after => LoadedBlockInvariant source after := by + unfold ingressAnonAddrShallow + change (match (EStateM.bind (IngressM.liftExcept (getConstVerified source addr true)) + _ : IngressM Bool) before with + | .ok _ after | .error _ after => LoadedBlockInvariant source after) + cases verified : getConstVerified source addr true with + | error err => exact valid + | ok optional => + cases optional with + | none => exact valid + | some constant => + dsimp only [IngressM.liftExcept, Bind.bind, Pure.pure, EStateM.pure, EStateM.bind] + cases block : ingressBlockAddr? addr constant.info with + | none => + have preserved := ingressAnonStandalone_blocks sourceOwned verified block before valid + dsimp only [Bind.bind, EStateM.bind] + cases run : ingressAnonStandalone source addr constant before <;> + rw [run] at preserved <;> exact preserved + | some blockAddr => + dsimp only [Bind.bind, EStateM.bind] + rw [show (get : IngressM AnonEnv) before = .ok before before from rfl] + dsimp only + by_cases recorded : before.blocks.contains ⟨blockAddr, ()⟩ = true + · simp only [recorded, ↓reduceIte] + exact valid + · simp only [recorded, Bool.false_eq_true, ↓reduceIte] + cases parent : getConstVerified source blockAddr true with + | error err => exact valid + | ok optional => + cases optional with + | none => exact valid + | some blockConstant => + have preserved := ingressAnonBlock_blocks sourceOwned parent before valid + dsimp only [IngressM.liftExcept, Bind.bind, Pure.pure, EStateM.pure, EStateM.bind] + cases run : ingressAnonBlock source blockConstant blockAddr before <;> + rw [run] at preserved <;> exact preserved + +/-- One reusable loader resource for every address in a fixed source. Its +state field is initialized empty and preserved by actual production lookup. -/ +structure OwnedLazySupport (before : TcState .anon) where + source : Ixon.Env + ownership : SourceOwnership source + installed : before.lazyFault = some (fun addr => ingressAnonAddrShallow source addr true) + blocks : LoadedBlockInvariant source before.env + +def OwnedLazySupport.toVerified {before : TcState .anon} (support : OwnedLazySupport before) + (addr : Address) : VerifiedLazySupport before addr := + ⟨support.source, support.installed, fun _ => support.blocks.materialization addr⟩ + +/-- No block-overlap assumptions on a checker state are needed at startup. -/ +def OwnedLazySupport.newLazyAnon (source : Ixon.Env) (ownership : SourceOwnership source) : + OwnedLazySupport (TcState.newLazyAnon source) := + ⟨source, ownership, rfl, LoadedBlockInvariant.empty source⟩ + +theorem lazyIngressAddr_blocks {before : TcState .anon} {addr : Address} + (support : OwnedLazySupport before) : + match TcM.lazyIngressAddr addr before with + | .ok _ after | .error _ after => LoadedBlockInvariant support.source after.env := by + unfold TcM.lazyIngressAddr + rw [support.installed] + dsimp only + by_cases faulted : before.faultedAddrs.contains addr = true + · rw [if_pos faulted] + exact support.blocks + · rw [if_neg faulted] + have preserved := ingressAnonAddrShallow_blocks support.ownership addr before.env support.blocks + cases run : ingressAnonAddrShallow support.source addr true before.env <;> + rw [run] at preserved <;> exact preserved + +theorem tryGetConst_blocks {before : TcState .anon} {id : KId .anon} + (support : OwnedLazySupport before) : + match TcM.tryGetConst id before with + | .ok _ after | .error _ after => LoadedBlockInvariant support.source after.env := by + unfold TcM.tryGetConst + change (match (EStateM.bind (get : TcM .anon (TcState .anon)) _ : + TcM .anon (Option (KConst .anon))) before with + | .ok _ after | .error _ after => LoadedBlockInvariant support.source after.env) + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + dsimp only + cases before.env.get? id with + | some concrete => exact support.blocks + | none => + change (match (EStateM.bind (TcM.lazyIngressAddr id.addr) _ : + TcM .anon (Option (KConst .anon))) before with + | .ok _ after | .error _ after => LoadedBlockInvariant support.source after.env) + have preserved := lazyIngressAddr_blocks (addr := id.addr) support + cases fault : TcM.lazyIngressAddr id.addr before with + | error err after => + rw [EStateM.bind, fault] + simpa only [fault] using preserved + | ok value after => + rw [fault] at preserved + rw [EStateM.bind, fault] + change (match (EStateM.bind (get : TcM .anon (TcState .anon)) _ : + TcM .anon (Option (KConst .anon))) after with + | .ok _ state | .error _ state => LoadedBlockInvariant support.source state.env) + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) after = .ok after after from rfl] + dsimp only + cases after.env.get? id with + | some concrete => exact preserved + | none => cases before.lazyFault.isSome <;> exact preserved + +/-- Actual lookup derives cache preservation and retains the block invariant +for the next call, without per-block freshness or declaration comparisons. -/ +theorem getConst_owned {before : TcState .anon} {id : KId .anon} + (support : OwnedLazySupport before) : + match TcM.getConst id before with + | .ok _ after | .error _ after => + LazyLookupFrame before after ∧ LoadedBlockInvariant support.source after.env := by + have frame := getConst_verified_cache (id := id) (support.toVerified id.addr) + have blocks : match TcM.getConst id before with + | .ok _ after | .error _ after => LoadedBlockInvariant support.source after.env := by + unfold TcM.getConst + change (match (EStateM.bind (TcM.tryGetConst id) _ : TcM .anon (KConst .anon)) before with + | .ok _ after | .error _ after => LoadedBlockInvariant support.source after.env) + have preserved := tryGetConst_blocks (id := id) support + cases tried : TcM.tryGetConst id before with + | error err after => + rw [EStateM.bind, tried] + simpa only [tried] using preserved + | ok optional after => + rw [tried] at preserved + rw [EStateM.bind, tried] + cases optional <;> exact preserved + cases run : TcM.getConst id before <;> + rw [run] at frame blocks <;> exact ⟨frame, blocks⟩ + +def OwnedLazySupport.afterGetConst {before after : TcState .anon} {id : KId .anon} + {concrete : KConst .anon} (support : OwnedLazySupport before) + (run : TcM.getConst id before = .ok concrete after) : OwnedLazySupport after := by + have preserved := getConst_owned (id := id) support + rw [run] at preserved + refine ⟨support.source, support.ownership, ?_, preserved.2⟩ + rw [preserved.1.checker] + exact support.installed + +def OwnedLazySupport.afterFailedGetConst {before after : TcState .anon} {id : KId .anon} + {err : TcError .anon} (support : OwnedLazySupport before) + (run : TcM.getConst id before = .error err after) : OwnedLazySupport after := by + have preserved := getConst_owned (id := id) support + rw [run] at preserved + refine ⟨support.source, support.ownership, ?_, preserved.2⟩ + rw [preserved.1.checker] + exact support.installed + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean index bbc6e9742..fb2b247ec 100644 --- a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean +++ b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.BinderInference -import Ix.Kernel.Verify.Consistency.IngressCoherence +import Ix.Kernel.Verify.Consistency.SourceOwnershipCheck /-! # Cache preservation through recursive inference @@ -57,6 +57,27 @@ theorem inferKey_environment {term : KExpr .anon} {before after : TcState .anon} cases cached : before.ctxAddrCache[(before.ctxId, term.lbr)]? <;> rw [cached] at run <;> cases run <;> rfl +/-- Key memoization preserves the reusable source ownership resource. -/ +def OwnedLazySupport.afterInferKey {term : KExpr .anon} {before after : TcState .anon} + {key : Address × Address} (support : OwnedLazySupport before) + (run : TcM.inferKey term before = .ok key after) : OwnedLazySupport after := by + refine ⟨support.source, support.ownership, ?_, ?_⟩ + · unfold TcM.inferKey at run + change EStateM.bind (TcM.ctxAddrForLbr term.lbr) _ before = _ at run + unfold TcM.ctxAddrForLbr at run + change EStateM.bind (fun state => EStateM.bind (get : TcM .anon (TcState .anon)) + _ state) _ before = _ at run + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = + .ok before before from rfl] at run + by_cases fast : (term.lbr == 0 || before.ctx.isEmpty) = true + · rw [if_pos fast] at run + cases run; exact support.installed + · rw [if_neg fast] at run + cases cached : before.ctxAddrCache[(before.ctxId, term.lbr)]? <;> + rw [cached] at run <;> cases run <;> exact support.installed + · rw [inferKey_environment run] + exact support.blocks + /-- Hash conversion executes only tracing and its optional statistics update. The exact state includes that counter update; it is not assumed unchanged. -/ theorem isDefEq_hash_state {left right : KExpr .anon} @@ -272,6 +293,20 @@ def InferenceCacheTrace.coherentConstOfKey {fuel : Nat} {before keyed : TcState .afterVerifiedGetConst loader (by rwa [inferKey_environment keyRun]) run (resources concrete loaded run).1 (resources concrete loaded run).2 +/-- Constant leaf construction uses one source resource for every address, +deriving both overlap compatibility and post-load intern coherence. -/ +def InferenceCacheTrace.ownedConstOfKey {fuel : Nat} {before keyed : TcState .anon} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {key : Address × Address} + (keyRun : TcM.inferKey (.const id arguments info) before = .ok key keyed) + (loader : OwnedLazySupport before) (coherent : before.env.intern.WF) + (resources : ∀ concrete loaded, TcM.getConst id keyed = .ok concrete loaded → + (KExpr.CollisionFree fun candidate => loaded.env.intern.ExprSupport candidate ∨ + KExpr.InstUnivReach arguments concrete.ty candidate) ∧ + UniverseSubstitutionSupport arguments concrete.ty) : + InferenceCacheTrace fuel before (.const id arguments info) := + .coherentConstOfKey keyRun ((loader.afterInferKey keyRun).toVerified id.addr) coherent resources + /-- Every successful call in the finite tree preserves entries outside its computed write footprint and retains the loaded declarations and policy. The proof follows the recursive calls, then their real outer cache insertion. -/ @@ -455,6 +490,42 @@ theorem infer_coherentConst_cache_frame {fuel : Nat} {before keyed after : TcSta ⟨frame.1, frame.2, infer_verifiedConst_coherent keyRun loader coherent (fun concrete loaded run => (resources concrete loaded run).1) accepted⟩ +/-- Source ownership survives the whole successful constant call, including +key computation, substitution, and cache publication. It can be reused by the +next call without checking the block-overlap condition again. -/ +def OwnedLazySupport.afterConstInference {fuel : Nat} {before keyed after : TcState .anon} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {key : Address × Address} {result : KExpr .anon} + (support : OwnedLazySupport before) + (keyRun : TcM.inferKey (.const id arguments info) before = .ok key keyed) + (coherent : before.env.intern.WF) + (faithful : ∀ concrete loaded, TcM.getConst id keyed = .ok concrete loaded → + KExpr.CollisionFree fun candidate => loaded.env.intern.ExprSupport candidate ∨ + KExpr.InstUnivReach arguments concrete.ty candidate) + (accepted : RecM.infer (.const id arguments info) (methodsN fuel) before = .ok result after) : + OwnedLazySupport after := by + have kept : after.lazyFault = some (fun addr => ingressAnonAddrShallow support.source addr true) ∧ + LoadedBlockInvariant support.source after.env := by + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, _, stateEq⟩ + · rw [hit.run] at accepted + cases accepted + let keyedSupport := support.afterInferKey hit.keyRun + exact ⟨keyedSupport.installed, keyedSupport.blocks⟩ + · obtain ⟨middle, run, written⟩ := infer_uncached_success_state miss accepted + rw [stateEq] at run + obtain ⟨concrete, loaded, got, _, instantiated⟩ := inferUncached_const_instantiation run + let keyedSupport := support.afterInferKey keyRun + let loadedSupport := keyedSupport.afterGetConst got + have lookup := getConst_coherent (id := id) keyedSupport.source true keyedSupport.installed + (by rwa [inferKey_environment keyRun]) + rw [got] at lookup + have post := TcM.instantiateUnivParams_wf (faithful concrete loaded got) + (fun _ h => Or.inr h) ⟨lookup, fun _ h => Or.inl h⟩ + rw [instantiated] at post + rw [written, post.2.2.1] + cases before.inferOnly <;> exact ⟨loadedSupport.installed, loadedSupport.blocks⟩ + exact ⟨support.source, support.ownership, kept.1, kept.2⟩ + /-- Concrete agreement at an unwritten key is retained by the entire tree. -/ theorem InferenceCacheTrace.agreement {fuel : Nat} {before after : TcState .anon} {term result expected : KExpr .anon} (tree : InferenceCacheTrace fuel before term) @@ -552,6 +623,29 @@ def CachedConstantInferenceSupport.afterCoherentInference {β : Type u} let frame := infer_coherentConst_cache_frame keyRun different loader coherent resources accepted support.transport closed frame.1 frame.2.1 +/-- An earlier closed cache witness survives constant inference using only +the reusable source ownership invariant and finite substitution resources. -/ +def CachedConstantInferenceSupport.afterOwnedInference {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {fuel : Nat} {before keyed after : TcState .anon} + {id requested : KId .anon} {arguments requestedArguments : Array (KUniv .anon)} + {info requestedInfo : ExprInfo .anon} {key : Address × Address} {result : KExpr .anon} + {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} + (support : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) + (closed : (KExpr.const id arguments info).lbr = 0) + (keyRun : TcM.inferKey (.const requested requestedArguments requestedInfo) before = .ok key keyed) + (different : key ≠ ((KExpr.const id arguments info).addr, emptyCtxAddr)) + (loader : OwnedLazySupport before) (coherent : before.env.intern.WF) + (resources : ∀ concrete loaded, TcM.getConst requested keyed = .ok concrete loaded → + (KExpr.CollisionFree fun candidate => loaded.env.intern.ExprSupport candidate ∨ + KExpr.InstUnivReach requestedArguments concrete.ty candidate) ∧ + UniverseSubstitutionSupport requestedArguments concrete.ty) + (accepted : RecM.infer (.const requested requestedArguments requestedInfo) + (methodsN fuel) before = .ok result after) : + CachedConstantInferenceSupport resolve entries after id arguments info ref entry type := + support.afterCoherentInference closed keyRun different + ((loader.afterInferKey keyRun).toVerified requested.addr) coherent resources accepted + /-- A later constant's actual returned type inherits typing from its earlier witness after recursive inference; no semantic premise about caches is added. -/ theorem CachedConstantInferenceSupport.sound_after_inference {β : Type u} diff --git a/Ix/Kernel/Verify/Consistency/SourceOwnershipCheck.lean b/Ix/Kernel/Verify/Consistency/SourceOwnershipCheck.lean new file mode 100644 index 000000000..0463aecf4 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SourceOwnershipCheck.lean @@ -0,0 +1,63 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BlockOwnership + +/-! +# Finite source ownership checks + +The check reads verified source headers once, then compares only their finite +key inventories. Acceptance supplies `SourceOwnership`; neither conversion +results nor the current kernel environment are inputs. +-/ + +namespace Ix.Kernel.Consistency + +theorem sourceOwnershipRows_mem {source : Ixon.Env} {addr : Address} {constant : Ixon.Constant} + (verified : getConstVerified source addr true = .ok (some constant)) : + ownershipRow addr constant ∈ sourceOwnershipRows source := by + have present : ∃ lazy, source.consts[addr]? = some lazy := by + cases stored : source.consts[addr]? with + | some lazy => exact ⟨lazy, rfl⟩ + | none => + unfold getConstVerified at verified + rw [stored] at verified + cases verified + obtain ⟨lazy, stored⟩ := present + apply List.mem_filterMap.mpr + refine ⟨(addr, lazy), Std.HashMap.mem_toList_iff_getElem?_eq_some.mpr stored, ?_⟩ + simp only [verified] + +/-- A successful finite inventory check discharges source ownership. Malformed +or missing source constants need no rows because verified loading cannot use them. -/ +theorem SourceOwnership.ofCheck {source : Ixon.Env} (checked : sourceOwnershipCheck source = true) : + SourceOwnership source := by + have pair := List.all_eq_true.mp checked + refine ⟨?_, ?_⟩ + · intro block other id left right + obtain ⟨constant, verified, member⟩ := left + obtain ⟨otherConstant, otherVerified, otherMember⟩ := right + have row := List.all_eq_true.mp (pair _ (sourceOwnershipRows_mem verified)) + _ (sourceOwnershipRows_mem otherVerified) + have key := (Array.all_eq_true_iff_forall_mem.mp (Bool.and_eq_true_iff.mp row).1) id member + have present : (blockProjectionIds other otherConstant).contains id = true := + Array.contains_iff_mem.mpr otherMember + simp only [present, Bool.not_true, Bool.false_or, ownershipRow] at key + exact eq_of_beq key + · intro addr constant verified standalone block projection + obtain ⟨blockConstant, blockVerified, member⟩ := projection + have row := List.all_eq_true.mp (pair _ (sourceOwnershipRows_mem verified)) + _ (sourceOwnershipRows_mem blockVerified) + have absent := (Bool.and_eq_true_iff.mp row).2 + have present : (blockProjectionIds block blockConstant).contains ⟨addr, ()⟩ = true := + Array.contains_iff_mem.mpr member + simp only [ownershipRow, standalone, Option.isNone_none, Bool.not_true, Bool.false_or, + present, Bool.false_eq_true] at absent + +def OwnedLazySupport.ofCheckedSource (source : Ixon.Env) (checked : sourceOwnershipCheck source = true) : + OwnedLazySupport (TcState.newLazyAnon source) := + .newLazyAnon source (.ofCheck checked) + +end Ix.Kernel.Consistency diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 4478c9fd6..d7578be24 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -2,6 +2,7 @@ module public import LSpec public import Ix.Kernel +public import Ix.Kernel.SourceOwnership public import Tests.Ix.Kernel.IxonFixtures /-! @@ -1298,9 +1299,148 @@ private def ingressCoherenceCases : TestSeq := ++ test "ingress coherence: cyclic block sharing retains warm witnesses without publication" (coherenceAfterCyclicLoad true) +private def ownedRows (kind : Nat) : Bool := + let first := Address.blake3 "ownership-first".toUTF8 + let second := Address.blake3 "ownership-second".toUTF8 + let key : KId .anon := ⟨Address.blake3 "ownership-key".toUTF8, ()⟩ + let rows : List OwnershipRow := match kind with + | 0 => [] + | 1 => [⟨first, #[key], false⟩, ⟨second, #[key], false⟩] + | 2 => [⟨key.addr, #[], true⟩, ⟨first, #[key], false⟩] + | 3 => [⟨first, #[key, key], false⟩, ⟨first, #[key], false⟩] + | _ => [⟨first, #[], true⟩, ⟨second, #[], true⟩] + ownershipRowsCheck rows == (kind == 0 || kind >= 3) + +private def ownershipInventory : Bool := + let ind : Ixon.Inductive := + ⟨false, 0, 0, 0, .sort 0, + #[⟨false, 0, 0, 0, 0, .sort 0⟩, ⟨false, 0, 1, 0, 0, .sort 0⟩]⟩ + let constant : Ixon.Constant := + ⟨.muts #[.defn ⟨.defn, .safe, 0, .sort 0, .sort 0⟩, + .recr ⟨false, false, 0, 0, 0, 0, 0, .sort 0, #[]⟩, .indc ind], #[], #[], #[.succ .zero]⟩ + let (source, block) := storeMutsWithProjs {} constant + let expected : Array (KId .anon) := #[⟨defnProjAddr block 0, ()⟩, ⟨recrProjAddr block 1, ()⟩, + ⟨indcProjAddr block 2, ()⟩, ⟨ctorProjAddr block 2 0, ()⟩, ⟨ctorProjAddr block 2 1, ()⟩] + sourceOwnershipCheck source && blockProjectionIds block constant == expected && + match convertAnonBlock source constant block .empty with + | .ok trace _ => trace.allEntries.map (·.1) == expected + | .error _ _ => false + +private def loadedBlocksMatchOwnership (rows : List OwnershipRow) (env : AnonEnv) : Bool := + rows.all fun row => row.projections.all fun id => + (env.get? id).isNone || env.blocks.contains ⟨row.addr, ()⟩ + +/-- Partial external insertion lies outside the invariant even if its value +would agree with a later publication; the general overlap theorem still covers it. -/ +private def ownershipDetectsPartialPreload : Bool := + let constant := cacheBlock false + let (source, block) := storeMutsWithProjs {} constant + let rows := sourceOwnershipRows source + match prepareAnonBlock source constant block {} with + | .error _ _ => false + | .ok trace converted => + match trace.allEntries[0]? with + | none => false + | some entry => + sourceOwnershipCheck source && loadedBlocksMatchOwnership rows converted && + !loadedBlocksMatchOwnership rows (converted.insert entry.1 entry.2) && + loadedBlocksMatchOwnership rows (insertMutsEntriesState converted trace.allEntries) + +private def ownershipCorruptSource : Bool := + let constant := cacheBlock false + let (source, block) := storeMutsWithProjs {} constant + let source := {source with consts := source.consts.insert block (.ofConstant (cacheBlock true))} + sourceOwnershipCheck source && + !(sourceOwnershipRows source).any (fun row => row.addr == block) && + match TcM.getConst (m := .anon) ⟨defnProjAddr block 0, ()⟩ (TcState.newLazyAnon source) with + | .ok _ _ => false + | .error err after => + ((toString err).splitOn "fails integrity check").length > 1 && + after.env.consts.isEmpty && after.env.blocks.isEmpty + +/-- One fixed source mixes standalone, definition, recursor, and inductive +loads, plus failures before and after publication. Check the invariant and both +warm cache slots after every call, in both inference modes. -/ +private def ownershipAcrossLoads (inferOnly rootFirst : Bool) : Bool := + let (source, indBlock) := envInductive + let (source, warmAddr) := storeConst source + ⟨.axio ⟨false, 1, .leanAll (.sort 0) (.leanAll (.var 0) (.var 1))⟩, #[], #[], #[.var 0]⟩ + let (source, defBlock) := storeMutsWithProjs source (cacheBlock false) + let (source, recBlock) := storeMutsWithProjs source (cacheBlock true) + let broken : Ixon.Constant := ⟨.muts + #[.defn ⟨.defn, .safe, 0, .sort 0, .sort 0⟩, + .defn ⟨.defn, .safe, 0, .sort 0, .share 77⟩], #[], #[], #[.succ (.succ (.succ .zero))]⟩ + let (source, brokenBlock) := storeMutsWithProjs source broken + let rows := sourceOwnershipRows source + let warm := KExpr.mkConst (m := .anon) ⟨warmAddr, ()⟩ #[levelOne] + let first : KId .anon := ⟨defnProjAddr defBlock 0, ()⟩ + let second : KId .anon := ⟨recrProjAddr recBlock 1, ()⟩ + let ctor : KId .anon := ⟨ctorProjAddr indBlock 0 0, ()⟩ + let brokenId : KId .anon := ⟨defnProjAddr brokenBlock 0, ()⟩ + let action : RecM .anon Bool := do + let expected ← warmBothCaches warm + let key ← TcM.inferKey warm + let initial ← get + let keeps (state : TcState .anon) := loadedBlocksMatchOwnership rows state.env && + warmSlotsRetained key initial state + let rootRejected ← if rootFirst then + try + let _ ← liftM (TcM.getConst (m := .anon) ⟨defBlock, ()⟩) + pure false + catch err => + match err with + | .unknownConst addr => pure (addr == defBlock) + | _ => pure false + else pure true + let rootState ← get + let firstType ← RecM.inferCall (.mkConst first #[]) + let firstState ← get + let secondType ← RecM.inferCall (.mkConst second #[]) + let secondState ← get + let failed ← try + let _ ← liftM (TcM.getConst brokenId) + pure false + catch err => pure (((toString err).splitOn "invalid Share index 77").length > 1) + let failedState ← get + let _ ← RecM.inferCall (.mkConst ctor #[]) + let final ← get + let reused ← RecM.inferCall warm + return sourceOwnershipCheck source && keeps initial && rootRejected && keeps rootState && + firstType.addr == expected.addr && secondType.addr == expected.addr && + keeps firstState && keeps secondState && failed && keeps failedState && keeps final && + reused.addr == expected.addr && + !failedState.env.blocks.contains ⟨brokenBlock, ()⟩ && + (failedState.env.get? brokenId).isNone && + final.env.blocks.contains ⟨defBlock, ()⟩ && final.env.blocks.contains ⟨recBlock, ()⟩ && + final.env.blocks.contains ⟨indBlock, ()⟩ && final.inferOnly == inferOnly + match TcM.runRec action {TcState.newLazyAnon source with inferOnly} with + | .ok passed _ => passed + | .error _ _ => false + +private def sourceOwnershipCases : TestSeq := + test "source ownership: empty inventory is accepted" (ownedRows 0) + ++ test "source ownership: different blocks cannot own the same projection" (ownedRows 1) + ++ test "source ownership: standalone/projection overlap is rejected" (ownedRows 2) + ++ test "source ownership: repeated keys within one block are accepted" (ownedRows 3) + ++ test "source ownership: independent standalones are accepted" (ownedRows 4) + ++ test "source ownership: mixed block inventory matches all converted member and constructor keys" + ownershipInventory + ++ test "source ownership: partial external insertion is detected until its block is recorded" + ownershipDetectsPartialPreload + ++ test "source ownership: corrupt headers are excluded and actual loading rejects them" + ownershipCorruptSource + ++ test "source ownership: full inference retains the invariant across mixed loads and failures" + (ownershipAcrossLoads false false) + ++ test "source ownership: inference-only calls retain the invariant across mixed loads and failures" + (ownershipAcrossLoads true false) + ++ test "source ownership: publication before a full-mode root error retains the invariant" + (ownershipAcrossLoads false true) + ++ test "source ownership: publication before an inference-only root error retains the invariant" + (ownershipAcrossLoads true true) + public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, - lazyCacheCases, blockCacheCases, ingressCoherenceCases] + lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index b11ed37f8..fb9219a48 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -137,8 +137,9 @@ increasing sequence of strongly inaccessible cardinals. at the actual post-lookup state. `CachedConstantInferenceSupport.afterVerifiedInference` reuses an earlier witness after a constant call loads another dependency; recursive transport also covers loads inside application and binder trees. - Block overlap agreement remains a data premise. The source admission and - other recursive paths still need proofs. + This general interface retains overlap agreement as a data premise, including + for externally partially loaded states. Source admission and other recursive + paths still need proofs. - `getConst_coherent` derives intern-table key coherence through the actual loader on success and error. Universe and expression conversion now use finite step bounds computed from source syntax; their counting passes and @@ -151,8 +152,23 @@ increasing sequence of strongly inaccessible cardinals. carries it through key computation, loading, substitution, and the final cache write. `CachedConstantInferenceSupport.afterCoherentInference` reuses the earlier witness with coherence required only before the call. Finite - collision and level resources, source agreement, and block overlap checks - remain explicit. + collision and level resources and source agreement remain explicit. +- `getConst_owned` derives overlap compatibility from a reusable block + invariant. `sourceOwnershipCheck` compares finite key inventories from + verified source headers; `SourceOwnership.ofCheck` proves that acceptance + separates different block owners and standalone/projection keys. Repeated + keys within one block are allowed. Actual conversion emits only those + projection keys, including constructors, and publication records their owner. + The invariant that loaded projections have recorded blocks starts empty; + an unrecorded block's entries are therefore fresh. It survives lookup on + success and error, including errors after publication. + `OwnedLazySupport.afterConstInference` retains it through key computation, + substitution, and cache writes. `CachedConstantInferenceSupport.afterOwnedInference` + reuses an earlier witness without per-block overlap comparisons. The source + check is an optional proof preflight; it does not change loader admission. + Corrupt headers are excluded because verified loading rejects them. Semantic + source agreement, finite substitution resources, and preservation through + other checker paths remain obligations. - `checkEnvAnon_atomic_preserves_model` connects a supported production environment run to model extension. `checkEnvAnon_atomic_no_false` excludes a declaration at an axiom type interpreted as empty, including False. @@ -236,9 +252,12 @@ def useF (x : T.{1}) : T.{1} := f.{1} x inference. Constant misses use already-loaded declarations or verified standalone/block loading, with finite universe-walker resources after lookup; applications use full mode and hash conversion. The new dependency's semantic - source agreement, block overlap checks, and finite collision/level resources - remain explicit. Intern coherence follows through actual loading from the - pre-load invariant; successful constant inference also returns coherence for + source agreement and finite collision/level resources remain explicit. + `OwnedLazySupport` derives block compatibility from a source ownership check + and an invariant established at initialization and retained by lookup and + successful constant calls. Arbitrary partially loaded states can use the + general pointwise overlap condition. Intern coherence follows through actual + loading from the pre-load invariant; successful constant inference also returns coherence for the next operation. Initial coherence holds for `TcState.newLazyAnon`. Extending its preservation to every checker operation, preservation at written keys, and automatic trace construction remain open. @@ -337,7 +356,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 235 exact theorem boundaries. The production +The consistency target checks 258 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -397,6 +416,11 @@ binder trees; forward and backward sharing chains; repeated sharing; and unexpanded cyclic entries. Cyclic standalone and block loads must return a bounded diagnostic, preserve both warm cache partitions and coherent partial intern state, and deduplicate retries without publishing declarations. +Source ownership regressions cover conflicting block owners, standalone/projection +overlap, duplicate keys within one block, exact mixed-member and constructor +inventories, corrupt source exclusion, and detection of partial external loads. +Mixed loading sequences preserve the block invariant and both warm cache slots +in full and inference-only modes, including errors before and after publication. ## Certified host adapters @@ -430,6 +454,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Verified standalone lazy loading and cache frames | [`Consistency/LazyCache.lean`](../Ix/Kernel/Verify/Consistency/LazyCache.lean) | | Mutual-block publication and verified lookup frames | [`Consistency/BlockCache.lean`](../Ix/Kernel/Verify/Consistency/BlockCache.lean) | | Intern coherence through conversion and lazy loading | [`Consistency/IngressCoherence.lean`](../Ix/Kernel/Verify/Consistency/IngressCoherence.lean) | +| Source ownership, block registration, and finite preflight | [`Consistency/BlockOwnership.lean`](../Ix/Kernel/Verify/Consistency/BlockOwnership.lean), [`Consistency/SourceOwnershipCheck.lean`](../Ix/Kernel/Verify/Consistency/SourceOwnershipCheck.lean), [`SourceOwnership.lean`](../Ix/Kernel/SourceOwnership.lean) | | Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`Application.lean`](../Ix/Kernel/Verify/Consistency/Application.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | | Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | diff --git a/docs/theory.md b/docs/theory.md index f1e9bd8f5..05c425ea3 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -48,8 +48,16 @@ is now derived through every production conversion form and the actual loader, including errors. Conversion uses bounds computed from source syntax and rejects exhausted cyclic sharing with coherent partial state. Constant inference carries pre-call coherence through lookup, substitution, and cache publication. -Initial cache agreement, block overlap checks, finite collision/level resources, -trace construction, and preservation for keys that are written remain obligations. +For a fixed source, a finite check establishes disjoint ownership of projection +keys and separates them from standalones. Conversion follows this source key +inventory, and publication records each owning block. The invariant that loaded +projections have recorded blocks starts empty and survives actual lookup on +both outcomes and successful constant inference. Unrecorded blocks then have +fresh entries, discharging per-block overlap checks for this path. General +partially loaded states retain the pointwise compatibility interface. The source +check supplies address ownership, without establishing semantic admission. +Initial cache agreement, general source/state invariants, finite collision/level +resources, trace construction, and preservation at written keys remain obligations. Full checker consistency and compiler/backend refinement remain open. From c9c48c1327469eacc2f0a3d119da7eaba7507abe Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 20:35:06 -0400 Subject: [PATCH 18/63] Preserve owned checker state through recursive inference --- Ix/Kernel/Verify/Consistency.lean | 7 + Ix/Kernel/Verify/Consistency/Audit.lean | 23 +- .../Verify/Consistency/RecursiveState.lean | 415 ++++++++++++++++++ Tests/Ix/Kernel/Consistency.lean | 84 +++- docs/kernel-verification.md | 35 +- docs/theory.md | 7 + 6 files changed, 561 insertions(+), 10 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/RecursiveState.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 4cf284c96..7965f3c4d 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -26,6 +26,7 @@ import Ix.Kernel.Verify.Consistency.BinderOpening import Ix.Kernel.Verify.Consistency.Application import Ix.Kernel.Verify.Consistency.BinderInference import Ix.Kernel.Verify.Consistency.RecursiveCache +import Ix.Kernel.Verify.Consistency.RecursiveState import Ix.Kernel.Verify.Consistency.Production import Ix.Kernel.Verify.Consistency.Environment import Ix.Kernel.Verify.Consistency.Audit @@ -87,6 +88,12 @@ recorded block starts empty and survives lookup on both outcomes and successful constant inference. It derives fresh entries for unrecorded blocks, so these calls need no per-block overlap premise. Externally partially populated states can still use the general compatibility resource. +An `OwnedInferenceTrace` now carries these ownership and coherence invariants +through recursive applications, dependent functions, and full-mode lambdas. +Its nodes contain finite walker data, while one initial state invariant supplies +every recursive boundary and post-lookup table. The resulting cache frame +transports earlier constant witnesses, supplies later sort coherence, and +returns the state resource for a subsequent constant that loads another block. Initial agreement, general state/source agreement, finite execution resources, trace construction, and preservation inside the footprint remain obligations. General checker soundness remains outside this fragment. diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 2bf82c83e..e7d0d8265 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -7,6 +7,7 @@ import Ix.Kernel.Verify.Consistency.Infer import Ix.Kernel.Verify.Consistency.Constant import Ix.Kernel.Verify.Consistency.Environment import Ix.Kernel.Verify.Consistency.RecursiveCache +import Ix.Kernel.Verify.Consistency.RecursiveState import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -143,6 +144,19 @@ private def ownedLoaderRoots : Array Lean.Name := #[ ``OwnedLazySupport.afterConstInference, ``CachedConstantInferenceSupport.afterOwnedInference ] +private def recursiveStateRoots : Array Lean.Name := #[ + ``InferenceStateInvariant.owned, ``InferenceStateInvariant.ofOwned, + ``InferenceStateInvariant.ofMaps, ``InferenceStateInvariant.afterInferKey, + ``InferenceStateInvariant.getConst, ``InferenceStateInvariant.openBinder, + ``OwnedInferenceTrace.writes, ``OwnedInferenceTrace.preserves, + ``OwnedInferenceTrace.toCacheTrace, ``OwnedInferenceTrace.toCacheTrace_writes, + ``OwnedInferenceTrace.frame, ``OwnedInferenceTrace.sortOfKey, + ``OwnedInferenceTrace.fvarOfKey, ``OwnedInferenceTrace.constOfKey, + ``InferenceStateInvariant.afterConstInference, + ``CachedConstantInferenceSupport.afterOwnedRecursiveInference, + ``BinderInference.sortAfterOwnedInference +] + private def productionRoots : Array Lean.Name := #[ ``StandalonePrefix.member_success, ``definition_body_trace, ``AtomicDefinitionRun.sound, ``AtomicDefinitionRun.no_self_alias, @@ -169,7 +183,8 @@ private def binderWalkerRoots : Array Lean.Name := #[ ``readScopedExpr?_instantiateRevSpec, ``readScopedExpr?_abstractFVarsSpec, ``openBinder_eq, ``openBinder_sound, ``abstractFVars_singleton_spec, ``abstractFVars_readScopedExpr?, - ``readScopedExpr?_liftSpec, ``readScopedExpr?_substSpec, ``subst_readScopedExpr? + ``readScopedExpr?_liftSpec, ``readScopedExpr?_substSpec, ``subst_readScopedExpr?, + ``ApplicationSubstitutionData.coherent, ``LambdaClosingData.coherent ] /-- Production roots must not acquire a checker-soundness assumption @@ -193,6 +208,9 @@ def roots : Array RootAllowance := #[ { root := ``OwnedLazySupport.ofCheckedSource, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative, nameNative], forbiddenDependencies := forbiddenProduction }, + { root := ``InferenceStateInvariant.ofCheckedSource, standardAxioms := standard, + nativeAxioms := #[expressionNative, levelNative, nameNative], + forbiddenDependencies := forbiddenProduction }, { root := ``readLevel_eq, standardAxioms := #[``propext] }, { root := ``readLevel_eval, standardAxioms := #[``propext] }, { root := ``readLevel_wf, standardAxioms := #[``propext] }, @@ -253,7 +271,8 @@ def roots : Array RootAllowance := #[ }) ++ (binderWalkerRoots ++ cacheKeyRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction -}) ++ (atomicRoots ++ instantiationRoots ++ recursiveCacheRoots ++ lazyCacheRoots ++ ownedLoaderRoots).map (fun root => { +}) ++ (atomicRoots ++ instantiationRoots ++ recursiveCacheRoots ++ lazyCacheRoots ++ + ownedLoaderRoots ++ recursiveStateRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) ++ productionRoots.map (fun root => { diff --git a/Ix/Kernel/Verify/Consistency/RecursiveState.lean b/Ix/Kernel/Verify/Consistency/RecursiveState.lean new file mode 100644 index 000000000..05d344521 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/RecursiveState.lean @@ -0,0 +1,415 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.RecursiveCache + +/-! +# Reusable state invariants through recursive inference + +One checked source and initial intern table supply ownership and coherence +throughout a finite operational tree. Nodes carry finite walker data, without +assuming the invariants at recursive boundaries or after lazy loading. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- The fixed verified source, block registration, and intern coherence needed +by the next inference call. No cache typing or source interpretation is asserted. -/ +structure InferenceStateInvariant (source : Ixon.Env) (state : TcState .anon) : Prop where + ownership : SourceOwnership source + installed : state.lazyFault = some (fun addr => ingressAnonAddrShallow source addr true) + blocks : LoadedBlockInvariant source state.env + coherent : state.env.intern.WF + +def InferenceStateInvariant.owned {source : Ixon.Env} {state : TcState .anon} + (valid : InferenceStateInvariant source state) : OwnedLazySupport state := + ⟨source, valid.ownership, valid.installed, valid.blocks⟩ + +theorem InferenceStateInvariant.ofOwned {state : TcState .anon} + (owned : OwnedLazySupport state) (coherent : state.env.intern.WF) : + InferenceStateInvariant owned.source state := + ⟨owned.ownership, owned.installed, owned.blocks, coherent⟩ + +/-- One finite source check initializes the reusable invariant. -/ +theorem InferenceStateInvariant.ofCheckedSource (source : Ixon.Env) + (checked : sourceOwnershipCheck source = true) : + InferenceStateInvariant source (TcState.newLazyAnon source) := + ⟨.ofCheck checked, rfl, .empty source, newLazyAnon_intern_coherent source⟩ + +/-- Cache writes, local scope cleanup, and statistics updates use this frame; +the new intern table's coherence is supplied by the operation that changed it. -/ +theorem InferenceStateInvariant.ofMaps {source : Ixon.Env} {before after : TcState .anon} + (valid : InferenceStateInvariant source before) + (installed : after.lazyFault = before.lazyFault) + (constants : after.env.consts = before.env.consts) + (blocks : after.env.blocks = before.env.blocks) + (coherent : after.env.intern.WF) : InferenceStateInvariant source after := + ⟨valid.ownership, installed.trans valid.installed, + valid.blocks.ofMaps constants blocks, coherent⟩ + +theorem InferenceStateInvariant.afterInferKey {source : Ixon.Env} + {term : KExpr .anon} {before after : TcState .anon} {key : Address × Address} + (valid : InferenceStateInvariant source before) + (run : TcM.inferKey term before = .ok key after) : + InferenceStateInvariant source after := + .ofOwned (valid.owned.afterInferKey run) (by rw [inferKey_environment run]; exact valid.coherent) + +/-- Lookup retains both resources on success and failure, including partially +completed conversion and publication before an unknown-root error. -/ +theorem InferenceStateInvariant.getConst {source : Ixon.Env} {before : TcState .anon} + (valid : InferenceStateInvariant source before) (id : KId .anon) : + match TcM.getConst id before with + | .ok _ after | .error _ after => InferenceStateInvariant source after := by + have ownership := getConst_owned (id := id) valid.owned + have coherence := getConst_coherent (id := id) source true valid.installed valid.coherent + cases run : TcM.getConst id before <;> rw [run] at ownership coherence <;> + exact ⟨valid.ownership, by rw [ownership.1.checker]; exact valid.installed, + ownership.2, coherence⟩ + +/-- Binder data omits coherence, which comes from the preceding recursive call. -/ +structure BinderOpeningData (before : TcState .anon) (body : KExpr .anon) : Prop where + constructed : body.Constructed + bound : body.size + 1 < UInt64.size + faithful : KExpr.CollisionFree fun term => + before.env.intern.ExprSupport term ∨ + term = KExpr.mkFVar ⟨before.env.nextFVarId⟩ () ∨ + KExpr.InstRevReach #[KExpr.mkFVar ⟨before.env.nextFVarId⟩ ()] body 0 term + +theorem InferenceStateInvariant.openBinder {source : Ixon.Env} + {before after : TcState .anon} {name : Mode.anon.F Name} + {bi : Mode.anon.F Lean.BinderInfo} {domain body opened : KExpr .anon} {fresh : FVarId} + (valid : InferenceStateInvariant source before) (data : BinderOpeningData before body) + (run : TcM.openBinder name bi domain body before = .ok (opened, fresh) after) : + InferenceStateInvariant source after := by + have nameUnit : name = () := Subsingleton.elim _ _ + have biUnit : bi = () := Subsingleton.elim _ _ + subst name bi + have interned : (before.env.intern.internExpr + (KExpr.mkFVar ⟨before.env.nextFVarId⟩ ())).1 = + KExpr.mkFVar ⟨before.env.nextFVarId⟩ () := by + have faithful := KExpr.keyCollisionFree_anon.mpr + (data.faithful.mono (fun _ h => h.elim Or.inl (fun equal => .inr (.inl equal))) : + KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ + term = KExpr.mkFVar ⟨before.env.nextFVarId⟩ ()) + simpa only [KExpr.eraseMeta_anon] using + before.env.intern.internExpr_eraseMeta valid.coherent faithful + have walk := instantiateRev_spec (fvars := #[KExpr.mkFVar ⟨before.env.nextFVarId⟩ ()]) + data.faithful data.constructed (by simpa using data.bound) + (fun _ reached => .inr (.inr reached)) + (valid.coherent.internExpr (KExpr.mkFVar ⟨before.env.nextFVarId⟩ ())) + (fun _ member => (InternTable.ExprSupport.of_internExpr member).elim + Or.inl (fun equal => .inr (.inl equal))) + rw [openBinder_eq] at run + split at run + · simp only [interned] at run + cases run + exact valid.ofMaps rfl rfl rfl walk.2.1 + · contradiction + +/-- Finite codomain-substitution resources at the actual comparison state. -/ +structure ApplicationSubstitutionData (table : InternTable .anon) + (body arg : KExpr .anon) : Prop where + bodyConstructed : body.Constructed + argConstructed : arg.Constructed + bodyBound : body.size < UInt64.size + argBound : arg.size < UInt64.size + faithful : KExpr.CollisionFree fun term => table.ExprSupport term ∨ KExpr.SubstReach arg body 0 term + +theorem ApplicationSubstitutionData.coherent {table : InternTable .anon} + {body arg : KExpr .anon} (data : ApplicationSubstitutionData table body arg) + (coherent : table.WF) : (subst body arg 0 table).2.WF := + (subst_spec data.faithful data.bodyConstructed data.argConstructed + (by simpa using data.bodyBound) data.argBound (fun _ => Or.inr) coherent (fun _ => Or.inl)).2.1 + +/-- Finite abstraction resources at the actual returned body type. -/ +structure LambdaClosingData (table : InternTable .anon) (body : KExpr .anon) + (fresh : FVarId) : Prop where + constructed : body.Constructed + bound : body.size < UInt64.size + faithful : KExpr.CollisionFree fun term => table.ExprSupport term ∨ + KExpr.AbstractReach ((∅ : Std.HashMap FVarId UInt64).insert fresh 0) 1 body 0 term + +theorem LambdaClosingData.coherent {table : InternTable .anon} {body : KExpr .anon} + {fresh : FVarId} (data : LambdaClosingData table body fresh) (coherent : table.WF) : + (abstractFVars body #[fresh] table).2.WF := + (abstractFVars_singleton_spec data.constructed data.bound data.faithful coherent + (fun _ => Or.inl) (fun _ => Or.inr)).2 + +private theorem invariant_hash {source : Ixon.Env} {before after : TcState .anon} + {left right : KExpr .anon} {methods : Methods .anon} + (valid : InferenceStateInvariant source before) (equal : (left.addr == right.addr) = true) + (run : RecM.isDefEq left right methods before = .ok true after) : + InferenceStateInvariant source after := by + rw [isDefEq_hash_state equal] at run + split at run <;> cases run <;> exact valid.ofMaps rfl rfl rfl valid.coherent + +private theorem invariant_miss {source : Ixon.Env} {term result : KExpr .anon} + {methods : Methods .anon} {before after : TcState .anon} + (valid : InferenceStateInvariant source before) (miss : UncachedInference before term) + (accepted : RecM.infer term methods before = .ok result after) + (uncached : ∀ middle, + RecM.inferUncached RecM.inferCall before.inferOnly term methods miss.keyed = .ok result middle → + InferenceStateInvariant source miss.keyed → InferenceStateInvariant source middle) : + InferenceStateInvariant source after := by + obtain ⟨middle, run, written⟩ := infer_uncached_success_state miss accepted + have post := uncached middle run (valid.afterInferKey miss.keyRun) + rw [written] + cases before.inferOnly <;> exact post.ofMaps rfl rfl rfl post.coherent + +/-- Constant data concerns only the finite walk after an actual successful +lookup. Its coherence and block compatibility are derived from the tree root. -/ +structure ConstantInstantiationData (before : TcState .anon) (id : KId .anon) + (arguments : Array (KUniv .anon)) : Prop where + faithful : ∀ concrete loaded, TcM.getConst id before = .ok concrete loaded → + KExpr.CollisionFree fun term => loaded.env.intern.ExprSupport term ∨ + KExpr.InstUnivReach arguments concrete.ty term + levels : ∀ concrete loaded, TcM.getConst id before = .ok concrete loaded → + UniverseSubstitutionSupport arguments concrete.ty + +/-- Operational trees with finite walker data. Ownership and coherence occur +only in the initial state resource, not in any constructor or child resource. +The supported production paths match `InferenceCacheTrace`. -/ +inductive OwnedInferenceTrace : Nat → TcState .anon → KExpr .anon → Type + | hit {fuel before term} (hit : InferenceCacheHit before term) : + OwnedInferenceTrace fuel before term + | sort {fuel before level info} (miss : UncachedInference before (.sort level info)) : + OwnedInferenceTrace fuel before (.sort level info) + | fvar {fuel before id name info} (miss : UncachedInference before (.fvar id name info)) : + OwnedInferenceTrace fuel before (.fvar id name info) + | const {fuel before id arguments info} + (miss : UncachedInference before (.const id arguments info)) + (data : ConstantInstantiationData miss.keyed id arguments) : + OwnedInferenceTrace fuel before (.const id arguments info) + | app {fuel before fn arg info} (full : before.inferOnly = false) + (miss : UncachedInference before (.app fn arg info)) + (trace : ApplicationInferenceTrace fuel miss.keyed fn arg) + (hashPath : (trace.argumentType.addr == trace.domain.addr) = true) + (data : ApplicationSubstitutionData trace.comparedState.env.intern trace.codomain arg) + (functionTree : OwnedInferenceTrace fuel miss.keyed fn) + (argumentTree : OwnedInferenceTrace fuel trace.functionState arg) : + OwnedInferenceTrace (fuel + 1) before (.app fn arg info) + | forallE {fuel before name bi domain body info} + (miss : UncachedInference before (.all name bi domain body info)) + (trace : ForallInferenceTrace fuel miss.keyed name bi domain body) + (opening : BinderOpeningData trace.domainState body) + (domainTree : OwnedInferenceTrace fuel miss.keyed domain) + (bodyTree : OwnedInferenceTrace fuel trace.openedState trace.opened) : + OwnedInferenceTrace (fuel + 1) before (.all name bi domain body info) + | lam {fuel before name bi domain body info} (full : before.inferOnly = false) + (miss : UncachedInference before (.lam name bi domain body info)) + (trace : LambdaInferenceTrace fuel miss.keyed name bi domain body) + (opening : BinderOpeningData trace.domainState body) + (closing : LambdaClosingData trace.bodyState.env.intern trace.bodyType trace.fresh) + (domainTree : OwnedInferenceTrace fuel miss.keyed domain) + (bodyTree : OwnedInferenceTrace fuel trace.openedState trace.opened) : + OwnedInferenceTrace (fuel + 1) before (.lam name bi domain body info) + +def OwnedInferenceTrace.writes {fuel : Nat} {before : TcState .anon} {term : KExpr .anon} : + OwnedInferenceTrace fuel before term → List (Address × Address) + | .hit _ => [] + | .sort miss | .fvar miss | .const miss _ => [miss.key] + | .app _ miss _ _ _ first second | .forallE miss _ _ first second | + .lam _ miss _ _ _ first second => miss.key :: (first.writes ++ second.writes) + +/-- A successful whole call returns the invariant needed by its successor. +The proof follows each actual recursive call, binder walk, and final cache write. -/ +theorem OwnedInferenceTrace.preserves {source : Ixon.Env} {fuel : Nat} + {before after : TcState .anon} {term result : KExpr .anon} + (tree : OwnedInferenceTrace fuel before term) (valid : InferenceStateInvariant source before) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + InferenceStateInvariant source after := by + induction tree generalizing result after with + | hit hit => + rw [hit.run] at accepted + cases accepted + exact valid.afterInferKey hit.keyRun + | @sort fuel before level info miss => + apply invariant_miss valid miss accepted + intro middle run keyed + change EStateM.Result.ok + (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).1 + {miss.keyed with env := {miss.keyed.env with intern := + (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).2}} = + .ok result middle at run + cases run + exact keyed.ofMaps rfl rfl rfl (keyed.coherent.internExpr _) + | @fvar fuel before id name info miss => + apply invariant_miss valid miss accepted + intro middle run keyed + change (RecM.inferUncached RecM.inferCall before.inferOnly (.fvar id name info)).run + (methodsN fuel) miss.keyed = _ at run + unfold RecM.inferUncached at run + simp only [ReaderT.run_bind] at run + change EStateM.bind (get : TcM .anon (TcState .anon)) _ miss.keyed = _ at run + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) miss.keyed = + .ok miss.keyed miss.keyed from rfl] at run + dsimp only at run + split at run + · cases run; exact keyed + · contradiction + | @const fuel before id arguments info miss data => + apply invariant_miss valid miss accepted + intro middle run keyed + obtain ⟨concrete, loaded, got, _, instantiated⟩ := inferUncached_const_instantiation run + have lookup := keyed.getConst id + rw [got] at lookup + have post := TcM.instantiateUnivParams_wf (data.faithful concrete loaded got) + (fun _ h => Or.inr h) ⟨lookup.coherent, fun _ h => Or.inl h⟩ + rw [instantiated] at post + rw [post.2.2.1] + exact lookup.ofMaps rfl rfl rfl post.1.1 + | app full miss trace hashPath data functionTree argumentTree functionIH argumentIH => + apply invariant_miss valid miss accepted + intro middle run keyed + rw [full] at run + have function := functionIH keyed trace.functionRun + have argument := argumentIH function trace.argumentRun + have compared := invariant_hash argument hashPath trace.compareRun + rw [(trace.output_state run).2] + exact compared.ofMaps rfl rfl rfl (data.coherent compared.coherent) + | forallE miss trace opening domainTree bodyTree domainIH bodyIH => + apply invariant_miss valid miss accepted + intro middle run keyed + have domain := domainIH keyed trace.domainRun + have opened := domain.openBinder opening trace.openRun + have body := bodyIH opened trace.bodyRun + rw [(trace.output_state run).2] + exact body.ofMaps rfl rfl rfl (body.coherent.internExpr _) + | lam full miss trace opening closing domainTree bodyTree domainIH bodyIH => + apply invariant_miss valid miss accepted + intro middle run keyed + rw [full] at run + have domain := domainIH keyed trace.domainRun + have opened := domain.openBinder opening trace.openRun + have body := bodyIH opened trace.bodyRun + rw [(trace.output_state run).2] + exact body.ofMaps rfl rfl rfl ((closing.coherent body.coherent).internExpr _) + +/-- Recover the existing cache-frame tree by deriving every constant leaf's +loader and post-lookup coherence from the single initial state invariant. -/ +def OwnedInferenceTrace.toCacheTrace {source : Ixon.Env} {fuel : Nat} + {before : TcState .anon} {term : KExpr .anon} (tree : OwnedInferenceTrace fuel before term) + (valid : InferenceStateInvariant source before) : InferenceCacheTrace fuel before term := + match tree with + | .hit cached => .hit cached + | .sort miss => .sort miss + | .fvar miss => .fvar miss + | @OwnedInferenceTrace.const _ _ id _ _ miss data => + let keyed := valid.afterInferKey miss.keyRun + let loader := keyed.owned.toVerified id.addr + .lazyConst miss loader fun concrete loaded got => + .afterVerifiedGetConst loader keyed.coherent got + (data.faithful concrete loaded got) (data.levels concrete loaded got) + | .app full miss trace hashPath _ functionTree argumentTree => + let keyed := valid.afterInferKey miss.keyRun + .app full miss trace hashPath (functionTree.toCacheTrace keyed) + (argumentTree.toCacheTrace (functionTree.preserves keyed trace.functionRun)) + | .forallE miss trace opening domainTree bodyTree => + let keyed := valid.afterInferKey miss.keyRun + let domain := domainTree.preserves keyed trace.domainRun + .forallE miss trace (domainTree.toCacheTrace keyed) + (bodyTree.toCacheTrace (domain.openBinder opening trace.openRun)) + | .lam full miss trace opening _ domainTree bodyTree => + let keyed := valid.afterInferKey miss.keyRun + let domain := domainTree.preserves keyed trace.domainRun + .lam full miss trace (domainTree.toCacheTrace keyed) + (bodyTree.toCacheTrace (domain.openBinder opening trace.openRun)) + +theorem OwnedInferenceTrace.toCacheTrace_writes {source : Ixon.Env} {fuel : Nat} + {before : TcState .anon} {term : KExpr .anon} (tree : OwnedInferenceTrace fuel before term) + (valid : InferenceStateInvariant source before) : + (tree.toCacheTrace valid).writes = tree.writes := by + induction tree <;> simp only [toCacheTrace, writes, InferenceCacheTrace.writes, *] + +/-- The whole call retains the earlier cache frame and returns both state +invariants. Only entries outside the computed write footprint are protected. -/ +theorem OwnedInferenceTrace.frame {source : Ixon.Env} {fuel : Nat} + {before after : TcState .anon} {term result : KExpr .anon} + (tree : OwnedInferenceTrace fuel before term) (valid : InferenceStateInvariant source before) + {key : Address × Address} (outside : key ∉ tree.writes) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + InferenceCacheFrame key before after ∧ after.inferOnly = before.inferOnly ∧ + InferenceStateInvariant source after := + let frame := (tree.toCacheTrace valid).frame (by rwa [tree.toCacheTrace_writes valid]) accepted + ⟨frame.1, frame.2, tree.preserves valid accepted⟩ + +/-- Atomic constructors observe the actual cache selection. -/ +def OwnedInferenceTrace.sortOfKey {fuel : Nat} {before keyed : TcState .anon} + {level : KUniv .anon} {info : ExprInfo .anon} {key : Address × Address} + (keyRun : TcM.inferKey (.sort level info) before = .ok key keyed) : + OwnedInferenceTrace fuel before (.sort level info) := by + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ + · exact .hit hit + · exact .sort miss + +def OwnedInferenceTrace.fvarOfKey {fuel : Nat} {before keyed : TcState .anon} + {id : FVarId} {name : Mode.anon.F Name} {info : ExprInfo .anon} {key : Address × Address} + (keyRun : TcM.inferKey (.fvar id name info) before = .ok key keyed) : + OwnedInferenceTrace fuel before (.fvar id name info) := by + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ + · exact .hit hit + · exact .fvar miss + +def OwnedInferenceTrace.constOfKey {fuel : Nat} {before keyed : TcState .anon} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + {key : Address × Address} + (keyRun : TcM.inferKey (.const id arguments info) before = .ok key keyed) + (data : ConstantInstantiationData keyed id arguments) : + OwnedInferenceTrace fuel before (.const id arguments info) := by + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, _, stateEq⟩ + · exact .hit hit + · exact .const miss (by simpa only [stateEq] using data) + +/-- A later constant inference reuses the invariant returned by any supported +recursive call, including when it must load another block. -/ +theorem InferenceStateInvariant.afterConstInference {source : Ixon.Env} {fuel : Nat} + {before keyed after : TcState .anon} {id : KId .anon} + {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} {key : Address × Address} + {result : KExpr .anon} (valid : InferenceStateInvariant source before) + (keyRun : TcM.inferKey (.const id arguments info) before = .ok key keyed) + (data : ConstantInstantiationData keyed id arguments) + (accepted : RecM.infer (.const id arguments info) (methodsN fuel) before = .ok result after) : + InferenceStateInvariant source after := + (OwnedInferenceTrace.constOfKey keyRun data).preserves valid accepted + +def CachedConstantInferenceSupport.afterOwnedRecursiveInference {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {source : Ixon.Env} {fuel : Nat} {before after : TcState .anon} + {term result : KExpr .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} + (support : CachedConstantInferenceSupport resolve entries before id arguments info ref entry type) + (closed : (KExpr.const id arguments info).lbr = 0) + (tree : OwnedInferenceTrace fuel before term) (valid : InferenceStateInvariant source before) + (outside : ((KExpr.const id arguments info).addr, emptyCtxAddr) ∉ tree.writes) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + CachedConstantInferenceSupport resolve entries after id arguments info ref entry type := + support.afterInference closed (tree.toCacheTrace valid) + (by rwa [tree.toCacheTrace_writes valid]) accepted + +/-- The later sort leaf no longer needs a separate post-inference coherence +premise. Its collision data and earlier cache agreement remain explicit. -/ +def BinderInference.sortAfterOwnedInference {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {source : Ixon.Env} {fuel nextFuel : Nat} + {before after : TcState .anon} {term result : KExpr .anon} + {level : KUniv .anon} {info : ExprInfo .anon} + (closed : (KExpr.sort level info).lbr = 0) + (agreement : InferenceCacheAgreement before ((KExpr.sort level info).addr, emptyCtxAddr) + (KExpr.mkSort (KUniv.mkSucc level))) + (tree : OwnedInferenceTrace fuel before term) (valid : InferenceStateInvariant source before) + (outside : ((KExpr.sort level info).addr, emptyCtxAddr) ∉ tree.writes) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) + (faithful : KExpr.KeyCollisionFree fun candidate => after.env.intern.ExprSupport candidate ∨ + candidate = KExpr.mkSort (KUniv.mkSucc level)) : + BinderInference resolve entries locals context nextFuel after (.sort level info) + (.sort (readLevel level)) (.sort (.succ (readLevel level))) := + .sortAfterInference closed agreement (tree.toCacheTrace valid) + (by rwa [tree.toCacheTrace_writes valid]) accepted (tree.preserves valid accepted).coherent faithful + +end Ix.Kernel.Consistency diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index d7578be24..87e81744c 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -1438,9 +1438,91 @@ private def sourceOwnershipCases : TestSeq := ++ test "source ownership: publication before an inference-only root error retains the invariant" (ownershipAcrossLoads true true) +/-- A recursive call loads one block, then a constant call loads a different +block. Check ownership, intern keys, scope cleanup, and both warm cache slots +at each boundary, including a final hit on the whole recursive expression. -/ +private def stateAcrossRecursiveLoads (shape : Nat) (inferOnly stats surroundingScope : Bool) : Bool := + let (source, indBlock) := envInductive + let (source, warmAddr) := storeConst source + ⟨.axio ⟨false, 1, .leanAll (.sort 0) (.leanAll (.var 0) (.var 1))⟩, #[], #[], #[.var 0]⟩ + let (source, defBlock) := storeMutsWithProjs source (cacheBlock false) + let (source, nextBlock) := storeMutsWithProjs source (cacheBlock true) + let rows := sourceOwnershipRows source + let warm := KExpr.mkConst (m := .anon) ⟨warmAddr, ()⟩ #[levelOne] + let first : KId .anon := ⟨if shape == 0 then indcProjAddr indBlock 0 else defnProjAddr defBlock 0, ()⟩ + let next : KId .anon := ⟨recrProjAddr nextBlock 1, ()⟩ + let cold := KExpr.mkConst first #[] + let sortType := KExpr.mkSort (m := .anon) levelOne + let body := KExpr.mkLam () () sortType (.mkLam () () (.mkVar 0 ()) + (.mkApp (.mkApp cold (.mkVar 1 ())) (.mkVar 0 ()))) + let term := if shape == 0 then KExpr.mkAll () () cold cold + else if shape == 1 then body + else KExpr.mkApp (.mkApp (.mkConst ⟨warmAddr, ()⟩ #[levelTwo]) identityType) body + let expected := if shape == 0 then sortType else identityType + let action : RecM .anon Bool := do + let warmType ← warmBothCaches warm + let key ← TcM.inferKey warm + let initial ← get + let keeps (state : TcState .anon) := loadedBlocksMatchOwnership rows state.env && + internKeysCoherent state.env.intern && warmSlotsRetained key initial state + RecM.withLctxScope do + if surroundingScope then + let _ ← TcM.openBinder () () sortType (.mkVar 0 ()) + pure () + let active ← get + let rootKey ← TcM.inferKey term + let result ← RecM.inferCall term + let recursive ← get + let nextType ← RecM.inferCall (.mkConst next #[]) + let successor ← get + let reused ← RecM.inferCall warm + let replay ← RecM.inferCall term + let final ← get + let cache := if inferOnly then successor.env.inferOnlyCache else successor.env.inferCache + return sourceOwnershipCheck source && keeps initial && keeps active && keeps recursive && + keeps successor && keeps final && result.addr == expected.addr && result.lbr == 0 && + nextType.addr == identityType.addr && reused.addr == warmType.addr && + warmType.addr == identityType.addr && replay.addr == result.addr && rootKey != key && + cache[rootKey]?.any (fun cached => cached.addr == result.addr) && + (active.env.get? first).isNone && (recursive.env.get? first).isSome && + (recursive.env.get? next).isNone && (successor.env.get? next).isSome && + recursive.env.blocks.contains ⟨if shape == 0 then indBlock else defBlock, ()⟩ && + !recursive.env.blocks.contains ⟨nextBlock, ()⟩ && + successor.env.blocks.contains ⟨nextBlock, ()⟩ && + recursive.env.consts.size == active.env.consts.size + 2 && + successor.env.consts.size == recursive.env.consts.size + 2 && + recursive.env.intern.exprs.size > active.env.intern.exprs.size && + recursive.lctx.size == active.lctx.size && successor.lctx.size == active.lctx.size && + recursive.inferOnly == inferOnly && successor.inferOnly == inferOnly && + recursive.env.nextFVarId > active.env.nextFVarId && + (if stats && shape != 0 then recursive.deqCalls > active.deqCalls + else recursive.deqCalls == active.deqCalls) && + final.deqCalls == successor.deqCalls && final.env.nextFVarId == successor.env.nextFVarId && + final.env.consts.size == successor.env.consts.size && + final.env.intern.exprs.size == successor.env.intern.exprs.size && + final.env.intern.univs.size == successor.env.intern.univs.size + match TcM.runRec action {TcState.newLazyAnon source with inferOnly, stats} with + | .ok passed after => passed && after.lctx.size == 0 && after.inferOnly == inferOnly && + loadedBlocksMatchOwnership rows after.env && internKeysCoherent after.env.intern + | .error _ _ => false + +private def recursiveStateCases : TestSeq := + test "recursive state: forall inference and a later constant retain ownership, coherence, and warm slots" + (stateAcrossRecursiveLoads 0 false false false) + ++ test "recursive state: inference-only forall and later loading retain an outer scope" + (stateAcrossRecursiveLoads 0 true true true) + ++ test "recursive state: lambda opening and closing retain resources for the next block" + (stateAcrossRecursiveLoads 1 false false false) + ++ test "recursive state: lambda conversion statistics and scope cleanup retain both invariants" + (stateAcrossRecursiveLoads 1 false true true) + ++ test "recursive state: application with a lazy lambda argument permits subsequent block loading" + (stateAcrossRecursiveLoads 2 false false false) + ++ test "recursive state: nested application and later cache replay retain both invariants" + (stateAcrossRecursiveLoads 2 false true true) + public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, - lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases] + lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index fb9219a48..0bdffc188 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -169,6 +169,18 @@ increasing sequence of strongly inaccessible cardinals. Corrupt headers are excluded because verified loading rejects them. Semantic source agreement, finite substitution resources, and preservation through other checker paths remain obligations. +- `OwnedInferenceTrace.preserves` carries ownership and intern coherence through + a whole supported recursive call. `InferenceStateInvariant.ofCheckedSource` + initializes both from the finite source check and the empty production state. + Application substitution, binder opening, and lambda abstraction take finite + collision, construction, and size data; their initial coherence is derived + from the preceding recursive calls. Constant leaves derive their loader and + post-lookup coherence from the same invariant. The trace recovers the existing + cache frame outside its computed writes, transports earlier constant witnesses, + supplies coherence for later sort leaves, and returns the state resource for + another constant call that loads a new block. Recursive traces and finite + walker data remain explicit. The result covers successful inference in the + supported fragment; the general checker-state proof remains open. - `checkEnvAnon_atomic_preserves_model` connects a supported production environment run to model extension. `checkEnvAnon_atomic_no_false` excludes a declaration at an axiom type interpreted as empty, including False. @@ -255,12 +267,15 @@ def useF (x : T.{1}) : T.{1} := f.{1} x source agreement and finite collision/level resources remain explicit. `OwnedLazySupport` derives block compatibility from a source ownership check and an invariant established at initialization and retained by lookup and - successful constant calls. Arbitrary partially loaded states can use the - general pointwise overlap condition. Intern coherence follows through actual - loading from the pre-load invariant; successful constant inference also returns coherence for - the next operation. Initial coherence holds for `TcState.newLazyAnon`. Extending - its preservation to every checker operation, preservation at written keys, - and automatic trace construction remain open. + supported successful recursive calls. Arbitrary partially loaded states can + use the general pointwise overlap condition. Intern coherence follows through actual + loading from the pre-load invariant. `OwnedInferenceTrace` returns ownership + and coherence after applications, foralls, and full-mode lambdas, using one + initial state resource and finite data for their actual walkers. It also + reconstructs the cache-frame trace without per-leaf invariant premises. + Initial coherence holds for `TcState.newLazyAnon`. Preservation by every + checker operation, preservation at written keys, and automatic trace + construction remain open. The operational trace can frame any selected cache hit, while semantic typing of composite hits remains outside `BinderInference`. - Binder definitions supply finite inference trees for both the value and its @@ -356,7 +371,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 258 exact theorem boundaries. The production +The consistency target checks 278 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -421,6 +436,11 @@ overlap, duplicate keys within one block, exact mixed-member and constructor inventories, corrupt source exclusion, and detection of partial external loads. Mixed loading sequences preserve the block invariant and both warm cache slots in full and inference-only modes, including errors before and after publication. +Recursive-state regressions check ownership and intern keys after application, +forall, and lambda inference that loads one block, followed by a constant call +that loads another. They retain both warm partitions, outer local scopes, and +the actual statistics updates; replaying the recursive cache hit changes +neither intern tables nor fresh-local allocation. ## Certified host adapters @@ -451,6 +471,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Constant cache selection, writes, and typing | [`Consistency/ConstantCache.lean`](../Ix/Kernel/Verify/Consistency/ConstantCache.lean) | | Cache invariants and sort cache typing | [`Consistency/InferenceCache.lean`](../Ix/Kernel/Verify/Consistency/InferenceCache.lean), [`SortCache.lean`](../Ix/Kernel/Verify/Consistency/SortCache.lean) | | Recursive cache preservation and witness reuse | [`Consistency/RecursiveCache.lean`](../Ix/Kernel/Verify/Consistency/RecursiveCache.lean) | +| Ownership and intern coherence through recursive inference | [`Consistency/RecursiveState.lean`](../Ix/Kernel/Verify/Consistency/RecursiveState.lean) | | Verified standalone lazy loading and cache frames | [`Consistency/LazyCache.lean`](../Ix/Kernel/Verify/Consistency/LazyCache.lean) | | Mutual-block publication and verified lookup frames | [`Consistency/BlockCache.lean`](../Ix/Kernel/Verify/Consistency/BlockCache.lean) | | Intern coherence through conversion and lazy loading | [`Consistency/IngressCoherence.lean`](../Ix/Kernel/Verify/Consistency/IngressCoherence.lean) | diff --git a/docs/theory.md b/docs/theory.md index 05c425ea3..b59bf6017 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -56,6 +56,13 @@ both outcomes and successful constant inference. Unrecorded blocks then have fresh entries, discharging per-block overlap checks for this path. General partially loaded states retain the pointwise compatibility interface. The source check supplies address ownership, without establishing semantic admission. +`OwnedInferenceTrace` carries this ownership invariant and intern coherence +through successful applications, dependent functions, and full-mode lambdas. +One initial state resource supplies every recursive boundary and post-lookup +table; finite collision, construction, and size data remain explicit for the +actual substitution, opening, and closing walkers. The result preserves earlier +cache witnesses outside its computed writes and supplies the state resource +for a later constant call, including another block load. Initial cache agreement, general source/state invariants, finite collision/level resources, trace construction, and preservation at written keys remain obligations. Full checker consistency From 26cdfcca1f7b032154738ebb9d74463fc064603b Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 21:22:12 -0400 Subject: [PATCH 19/63] Derive standalone source agreement through conversion and inference --- Ix/Kernel/SourceConversion.lean | 336 ++++++++++ Ix/Kernel/Verify/Consistency.lean | 15 +- Ix/Kernel/Verify/Consistency/Audit.lean | 38 +- .../Verify/Consistency/ConversionRecipe.lean | 398 +++++++++++ .../Verify/Consistency/SourceAgreement.lean | 618 ++++++++++++++++++ Tests/Ix/Kernel/Consistency.lean | 247 ++++++- docs/kernel-verification.md | 49 +- docs/theory.md | 15 +- 8 files changed, 1704 insertions(+), 12 deletions(-) create mode 100644 Ix/Kernel/SourceConversion.lean create mode 100644 Ix/Kernel/Verify/Consistency/ConversionRecipe.lean create mode 100644 Ix/Kernel/Verify/Consistency/SourceAgreement.lean diff --git a/Ix/Kernel/SourceConversion.lean b/Ix/Kernel/SourceConversion.lean new file mode 100644 index 000000000..3ea13f3c3 --- /dev/null +++ b/Ix/Kernel/SourceConversion.lean @@ -0,0 +1,336 @@ +module + +public import Ix.Kernel.Ingress + +/-! +# Source-only conversion predictions + +A conversion recipe records the finite interning operations performed by the +bounded production converters. Its prediction returns each proposed node +directly, keeping source resolution, sharing caches, universe normalization, +and errors independent of the checker's mutable intern tables. + +The correspondence with production execution and the finite collision +conditions needed to use a prediction are proved in the consistency library. +This optional interface does not change production loading or admission. +-/ + +public section +@[expose] section + +namespace Ix.Kernel + +/-- An explicit description of a terminating conversion's intern effects. -/ +inductive ConversionRecipe (α : Type) where + | done (value : α) + | fail (error : IngressErr) + | internE (candidate : KExpr .anon) (next : KExpr .anon → ConversionRecipe α) + | internU (candidate : KUniv .anon) (next : KUniv .anon → ConversionRecipe α) + +namespace ConversionRecipe + +def bind (recipe : ConversionRecipe α) (next : α → ConversionRecipe β) : ConversionRecipe β := + match recipe with + | .done value => next value + | .fail error => .fail error + | .internE candidate rest => .internE candidate (fun value => bind (rest value) next) + | .internU candidate rest => .internU candidate (fun value => bind (rest value) next) + +instance : Monad ConversionRecipe where + pure := .done + bind := bind + +def catchError (recipe : ConversionRecipe α) (handler : IngressErr → ConversionRecipe α) : + ConversionRecipe α := + match recipe with + | .done value => .done value + | .fail error => handler error + | .internE candidate rest => .internE candidate (fun value => catchError (rest value) handler) + | .internU candidate rest => .internU candidate (fun value => catchError (rest value) handler) + +instance : MonadExceptOf IngressErr ConversionRecipe where + throw := .fail + tryCatch := catchError + +def emitExpr (term : KExpr .anon) : ConversionRecipe (KExpr .anon) := .internE term .done +def emitUniv (level : KUniv .anon) : ConversionRecipe (KUniv .anon) := .internU level .done + +/-- Interpret the description using the actual production intern operations. -/ +def run (recipe : ConversionRecipe α) : InternIngressM α := + match recipe with + | .done value => pure value + | .fail error => throw error + | .internE candidate next => InternIngressM.internE candidate >>= fun value => run (next value) + | .internU candidate next => InternIngressM.internU candidate >>= fun value => run (next value) + +/-- Prediction consults no intern table. Every proposed node is kept exactly. -/ +def predict (recipe : ConversionRecipe α) : Except IngressErr α := + match recipe with + | .done value => .ok value + | .fail error => .error error + | .internE candidate next => predict (next candidate) + | .internU candidate next => predict (next candidate) + +/-- The finite expression candidates encountered along the predicted path. -/ +def exprs (recipe : ConversionRecipe α) : List (KExpr .anon) := + match recipe with + | .done _ | .fail _ => [] + | .internE candidate next => candidate :: exprs (next candidate) + | .internU candidate next => exprs (next candidate) + +/-- The finite universe candidates encountered along the predicted path. -/ +def univs (recipe : ConversionRecipe α) : List (KUniv .anon) := + match recipe with + | .done _ | .fail _ => [] + | .internE candidate next => univs (next candidate) + | .internU candidate next => candidate :: univs (next candidate) + +def univStep (stack : Array UFrame) (values : Array (KUniv .anon)) : + ConversionRecipe (Array UFrame × Array (KUniv .anon)) := do + let frame := stack.back! + let mut stack := stack.pop + let mut values := values + match frame with + | .process u => + match u with + | .zero => + values := values.push (← emitUniv .mkZero) + | .succ inner => + stack := stack.push .succ |>.push (.process inner) + | .max a b => + stack := stack.push .max |>.push (.process b) |>.push (.process a) + | .imax a b => + stack := stack.push .imax |>.push (.process b) |>.push (.process a) + | .var idx => + values := values.push (← emitUniv (.mkParam idx ())) + | .succ => + let inner := values.back! + values := values.pop + values := values.push (← emitUniv (.mkSucc inner)) + | .max => + let b := values.back!; values := values.pop + let a := values.back!; values := values.pop + values := values.push (← emitUniv (.mkMax a b)) + | .imax => + let b := values.back!; values := values.pop + let a := values.back!; values := values.pop + values := values.push (← emitUniv (.mkIMax a b)) + return (stack, values) + +def univLoop (fuel : Nat) (stack : Array UFrame) (values : Array (KUniv .anon)) : + ConversionRecipe (KUniv .anon) := do + if stack.isEmpty then + match values.back? with + | some value => return value + | none => throw "ingressUnivTree: empty result stack" + else + match fuel with + | 0 => throw "ingressUnivTree: conversion step bound exhausted" + | fuel + 1 => + let (stack, values) ← univStep stack values + univLoop fuel stack values + +def univTree (root : Ixon.Univ) : ConversionRecipe (KUniv .anon) := + univLoop (2 * univIngressSize root) #[.process root] #[] + +def univIdx (ctx : IngressCtx) (idx : UInt64) : + StateT ConvState ConversionRecipe (KUniv .anon) := do + if let some cached := (← get).univCache[idx]? then + return cached + let some u := ctx.univs[idx.toNat]? + | throw s!"invalid universe index {idx} (len {ctx.univs.size})" + let ku ← liftM (univTree u) + modify fun s => { s with univCache := s.univCache.insert idx ku } + return ku + +def univArgs (ctx : IngressCtx) (idxs : Array UInt64) : + StateT ConvState ConversionRecipe (Array (KUniv .anon)) := do + let mut out : Array (KUniv .anon) := Array.mkEmpty idxs.size + for i in idxs do + out := out.push (← univIdx ctx i) + return out + +def exprStep (ixonEnv : Ixon.Env) (ctx : IngressCtx) + (stack : Array EFrame) (values : Array (KExpr .anon)) : + StateT ConvState ConversionRecipe (Array EFrame × Array (KExpr .anon)) := do + let frame := stack.back! + let mut stack := stack.pop + let mut values := values + match frame with + | .process e => + match e with + | .share idx => + if let some cached := (← get).exprCache[idx]? then + values := values.push cached + else + let some expansion := ctx.sharing[idx.toNat]? + | throw s!"invalid Share index {idx}" + stack := stack.push (.cacheShare idx) |>.push (.process expansion) + | .var idx => + values := values.push (← liftM (emitExpr (.mkVar idx ()))) + | .sort uidx => + let u ← univIdx ctx uidx + values := values.push (← liftM (emitExpr (.mkSort u))) + | .ref refIdx univIdxs => + let some addr := ctx.refs[refIdx.toNat]? + | throw s!"invalid Ref index {refIdx}" + let univs ← univArgs ctx univIdxs + values := values.push + (← liftM (emitExpr (.mkConst ⟨addr, ()⟩ univs))) + | .recur recIdx univIdxs => + let some mid := ctx.mutCtx[recIdx.toNat]? + | throw s!"invalid Rec index {recIdx}" + let univs ← univArgs ctx univIdxs + values := values.push + (← liftM (emitExpr (.mkConst mid univs))) + | .nat blobIdx => + let some blobAddr := ctx.refs[blobIdx.toNat]? + | throw s!"invalid Nat blob ref index {blobIdx}" + let some bytes := ixonEnv.getBlob? blobAddr + | throw s!"missing Nat blob {blobAddr}" + let val := Nat.fromBytesLE bytes.data + values := values.push + (← liftM (emitExpr (.mkNat val blobAddr))) + | .str blobIdx => + let some blobAddr := ctx.refs[blobIdx.toNat]? + | throw s!"invalid Str blob ref index {blobIdx}" + let some bytes := ixonEnv.getBlob? blobAddr + | throw s!"missing Str blob {blobAddr}" + let some val := String.fromUTF8? bytes + | throw s!"Str blob {blobAddr} is not valid UTF-8" + values := values.push + (← liftM (emitExpr (.mkStr val blobAddr))) + | .app f a => + stack := stack.push .appDone |>.push (.process a) |>.push (.process f) + | .lam _ ty body => + stack := stack.push .lamDone |>.push (.process body) + |>.push (.process ty) + | .all _ _ ty body => + stack := stack.push .allDone |>.push (.process body) + |>.push (.process ty) + | .letE nd ty val body => + stack := stack.push (.letDone nd) |>.push (.process body) + |>.push (.process val) |>.push (.process ty) + | .prj typeRefIdx field val => + let some typeAddr := ctx.refs[typeRefIdx.toNat]? + | throw s!"invalid Prj type ref index {typeRefIdx}" + stack := stack.push (.prjDone ⟨typeAddr, ()⟩ field) + |>.push (.process val) + | .appDone => + let a := values.back!; values := values.pop + let f := values.back!; values := values.pop + values := values.push (← liftM (emitExpr (.mkApp f a))) + | .lamDone => + let body := values.back!; values := values.pop + let ty := values.back!; values := values.pop + values := values.push + (← liftM (emitExpr (.mkLam () () ty body))) + | .allDone => + let body := values.back!; values := values.pop + let ty := values.back!; values := values.pop + values := values.push + (← liftM (emitExpr (.mkAll () () ty body))) + | .letDone nd => + let body := values.back!; values := values.pop + let val := values.back!; values := values.pop + let ty := values.back!; values := values.pop + values := values.push + (← liftM (emitExpr (.mkLet () ty val body nd))) + | .prjDone id field => + let val := values.back!; values := values.pop + values := values.push + (← liftM (emitExpr (.mkPrj id field val))) + | .cacheShare idx => + let v := values.back! + modify fun s => { s with exprCache := s.exprCache.insert idx v } + return (stack, values) + +def exprLoop (ixonEnv : Ixon.Env) (ctx : IngressCtx) (fuel : Nat) + (stack : Array EFrame) (values : Array (KExpr .anon)) : StateT ConvState ConversionRecipe (KExpr .anon) := do + if stack.isEmpty then + match values.back? with + | some value => + if values.size != 1 then + throw s!"ingressExpr: unbalanced value stack ({values.size} values)" + return value + | none => throw "ingressExpr: empty result stack" + else + match fuel with + | 0 => throw "ingressExpr: conversion step bound exhausted" + | fuel + 1 => + let (stack, values) ← exprStep ixonEnv ctx stack values + exprLoop ixonEnv ctx fuel stack values + +def expr (ixonEnv : Ixon.Env) (ctx : IngressCtx) (root : Ixon.Expr) : + StateT ConvState ConversionRecipe (KExpr .anon) := + exprLoop ixonEnv ctx (2 * exprIngressSize (root :: ctx.sharing.toList)) + #[.process root] #[] + +def defn (ixonEnv : Ixon.Env) (defn : Ixon.Definition) + (constant : Ixon.Constant) (block : KId .anon) + (mutCtx : Array (KId .anon)) (hintsOverride : Option Lean.ReducibilityHints) : + ConversionRecipe (KConst .anon) := do + let ctx : IngressCtx := + { sharing := constant.sharing, refs := constant.refs + univs := constant.univs, mutCtx } + let (ty, st) ← (expr ixonEnv ctx defn.typ).run {} + let (val, _) ← (expr ixonEnv ctx defn.value).run st + let hints := hintsOverride.getD (.regular 0) + return .defn () () defn.kind defn.safety hints defn.lvls ty val () block + +def recursor (ixonEnv : Ixon.Env) (rec : Ixon.Recursor) + (constant : Ixon.Constant) (block : KId .anon) + (mutCtx : Array (KId .anon)) : ConversionRecipe (KConst .anon) := do + let ctx : IngressCtx := + { sharing := constant.sharing, refs := constant.refs + univs := constant.univs, mutCtx } + let (ty, st) ← (expr ixonEnv ctx rec.typ).run {} + let mut st := st + let mut rules : Array (RecRule .anon) := Array.mkEmpty rec.rules.size + for rule in rec.rules do + let (rhs, st') ← (expr ixonEnv ctx rule.rhs).run st + st := st' + rules := rules.push { ctor := (), fields := rule.fields, rhs } + return .recr () () rec.k rec.isUnsafe rec.lvls rec.params rec.indices + rec.motives rec.minors block 0 ty rules () + +def standalone (ixonEnv : Ixon.Env) (addr : Address) + (constant : Ixon.Constant) : ConversionRecipe (KConst .anon) := do + let selfId : KId .anon := ⟨addr, ()⟩ + let hintsOverride := ixonEnv.anonHints[addr]? + match constant.info with + | .defn d => + defn ixonEnv d constant selfId #[selfId] hintsOverride + | .recr r => + recursor ixonEnv r constant selfId #[selfId] + | .axio a => do + let ctx : IngressCtx := + { sharing := constant.sharing, refs := constant.refs + univs := constant.univs, mutCtx := #[] } + let (ty, _) ← (expr ixonEnv ctx a.typ).run {} + pure (.axio () () a.isUnsafe a.lvls ty) + | .quot q => do + let ctx : IngressCtx := + { sharing := constant.sharing, refs := constant.refs + univs := constant.univs, mutCtx := #[] } + let (ty, _) ← (expr ixonEnv ctx q.typ).run {} + pure (.quot () () q.kind q.lvls ty) + | _ => + throw s!"ingressAnonStandalone: {addr} is a projection or Muts block, not a standalone" + +end ConversionRecipe + +/-- A verified standalone declaration predicted using only its source. Blocks +and projections have no entry in this standalone catalog. Parse, integrity, +and conversion errors remain explicit. -/ +def predictStandalone? (source : Ixon.Env) (addr : Address) : + Except IngressErr (Option (KConst .anon)) := do + let some constant ← getConstVerified source addr true | return none + match ingressBlockAddr? addr constant.info with + | some _ => return none + | none => return some (← (ConversionRecipe.standalone source addr constant).predict) + +end Ix.Kernel + +end +end diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 7965f3c4d..b46a88bbf 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -27,6 +27,8 @@ import Ix.Kernel.Verify.Consistency.Application import Ix.Kernel.Verify.Consistency.BinderInference import Ix.Kernel.Verify.Consistency.RecursiveCache import Ix.Kernel.Verify.Consistency.RecursiveState +import Ix.Kernel.Verify.Consistency.ConversionRecipe +import Ix.Kernel.Verify.Consistency.SourceAgreement import Ix.Kernel.Verify.Consistency.Production import Ix.Kernel.Verify.Consistency.Environment import Ix.Kernel.Verify.Consistency.Audit @@ -94,7 +96,16 @@ Its nodes contain finite walker data, while one initial state invariant supplies every recursive boundary and post-lookup table. The resulting cache frame transports earlier constant witnesses, supplies later sort coherence, and returns the state resource for a subsequent constant that loads another block. -Initial agreement, general state/source agreement, finite execution resources, -trace construction, and preservation inside the footprint remain obligations. +Source-only conversion recipes predict complete standalone declarations and +the finite intern candidates used to construct them. Under collision freedom +on the initial table and those candidates, actual conversion follows the +prediction on success and failure. Standalone source agreement starts empty, +survives verified lookup on both outcomes, and is preserved by supported +recursive inference. Source ownership protects its keys during block loads. +A static model binding reads the predicted type; actual constant lookup then +derives its type reading, arity, and coherence without post-load reading premises. +Model admission, mutual-member interpretations, initial semantic cache agreement, +finite execution resources, trace construction, and preservation inside the +footprint remain obligations. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index e7d0d8265..8cb997c60 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -8,6 +8,7 @@ import Ix.Kernel.Verify.Consistency.Constant import Ix.Kernel.Verify.Consistency.Environment import Ix.Kernel.Verify.Consistency.RecursiveCache import Ix.Kernel.Verify.Consistency.RecursiveState +import Ix.Kernel.Verify.Consistency.SourceAgreement import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -157,6 +158,27 @@ private def recursiveStateRoots : Array Lean.Name := #[ ``BinderInference.sortAfterOwnedInference ] +private def conversionUniverseRoots : Array Lean.Name := #[ + ``conversionRecipe_univStep, ``conversionRecipe_univLoop, ``conversionRecipe_univTree, + ``conversionRecipe_univIdx, ``conversionRecipe_univArgs +] + +private def sourceAgreementRoots : Array Lean.Name := #[ + ``conversionRecipe_exprStep, ``conversionRecipe_exprLoop, ``conversionRecipe_expr, + ``conversionRecipe_defn, ``conversionRecipe_recursor, ``conversionRecipe_standalone, + ``convertAnonStandalone_prediction, ``predictStandalone?_verified, ``predictStandalone?_some, + ``StandaloneSourceAgreement.empty, ``StandaloneSourceAgreement.ofMap, + ``SourceOwnership.projection_unpredicted, ``ingressAnonBlock_sourceAgreement, + ``ingressAnonStandalone_sourceAgreement, ``ingressAnonAddrShallow_sourceAgreement, + ``SourceStateInvariant.ofMaps, ``SourceStateInvariant.afterInferKey, + ``lazyIngressAddr_sourceAgreement, ``tryGetConst_sourceAgreement, ``getConst_sourceAgreement, + ``SourceStateInvariant.getConst, ``StandaloneModelBinding.getConst, + ``ScopedConstantInferenceSupport.ofSource, ``ConstantInferenceSupport.ofSource, + ``infer_const_source_sound, ``SourceStateInvariant.openBinder, + ``OwnedInferenceTrace.SourceData, ``OwnedInferenceTrace.preservesSource, + ``OwnedInferenceTrace.frameSource +] + private def productionRoots : Array Lean.Name := #[ ``StandalonePrefix.member_success, ``definition_body_trace, ``AtomicDefinitionRun.sound, ``AtomicDefinitionRun.no_self_alias, @@ -197,6 +219,15 @@ private def forbiddenProduction : Array Lean.Name := #[ ] def roots : Array RootAllowance := #[ + { root := ``ConversionRecipe.run_predict, standardAxioms := standard, + forbiddenDependencies := forbiddenProduction }, + { root := ``Ix.Kernel.ConversionRecipe.run_bind, standardAxioms := #[``propext, ``Quot.sound], + forbiddenDependencies := forbiddenProduction }, + { root := ``getConst_result_loaded, standardAxioms := #[``propext, ``Quot.sound], + forbiddenDependencies := forbiddenProduction }, + { root := ``SourceStateInvariant.ofCheckedSource, standardAxioms := standard, + nativeAxioms := #[expressionNative, levelNative, nameNative], + forbiddenDependencies := forbiddenProduction }, { root := ``convertUnivTree_coherent, standardAxioms := standard, nativeAxioms := #[levelNative], forbiddenDependencies := forbiddenProduction }, { root := ``newLazyAnon_intern_coherent, standardAxioms := standard, @@ -264,7 +295,10 @@ def roots : Array RootAllowance := #[ { root := ``Theory.Model.CheckingClaim.typing, standardAxioms := standard }, { root := ``Theory.Model.CheckingClaim.typingSort, standardAxioms := standard }, { root := ``Theory.Model.CheckingClaim.lam, standardAxioms := standard } -] ++ (scopedRoots ++ cacheFrameRoots).map (fun root => { +] ++ conversionUniverseRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[levelNative], + forbiddenDependencies := forbiddenProduction +}) ++ (scopedRoots ++ cacheFrameRoots).map (fun root => { root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction }) ++ (contextRoots ++ cacheMapRoots ++ sourceOwnershipRoots).map (fun root => { root, standardAxioms := standard, forbiddenDependencies := forbiddenProduction @@ -272,7 +306,7 @@ def roots : Array RootAllowance := #[ root, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction }) ++ (atomicRoots ++ instantiationRoots ++ recursiveCacheRoots ++ lazyCacheRoots ++ - ownedLoaderRoots ++ recursiveStateRoots).map (fun root => { + ownedLoaderRoots ++ recursiveStateRoots ++ sourceAgreementRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) ++ productionRoots.map (fun root => { diff --git a/Ix/Kernel/Verify/Consistency/ConversionRecipe.lean b/Ix/Kernel/Verify/Consistency/ConversionRecipe.lean new file mode 100644 index 000000000..a2adc4a56 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/ConversionRecipe.lean @@ -0,0 +1,398 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.SourceConversion +import Ix.Kernel.Verify.Consistency.IngressCoherence + +/-! +# Exact source conversion under finite interning assumptions + +The prediction and its finite candidate inventories depend only on source +conversion. Faithful interning makes actual execution follow that same path, +including failures, while deriving coherence of every intermediate table. +-/ + +namespace Ix.Kernel.Consistency + +/-- Collision data for the initial table and the finite predicted candidates. +No conclusion about a loaded declaration or model reading is an input. -/ +structure ConversionData (recipe : ConversionRecipe α) (before : InternTable .anon) : Prop where + expressions : KExpr.CollisionFree fun term => before.ExprSupport term ∨ term ∈ recipe.exprs + universes : KUniv.CollisionFree fun level => before.UnivSupport level ∨ level ∈ recipe.univs + +private structure InternClosed (expressions : KExpr .anon → Prop) + (universes : KUniv .anon → Prop) (table : InternTable .anon) : Prop where + coherent : table.WF + exprs : ∀ term, table.ExprSupport term → expressions term + univs : ∀ level, table.UnivSupport level → universes level + +private theorem recipe_spec {expressions : KExpr .anon → Prop} {universes : KUniv .anon → Prop} + (exprFaithful : KExpr.CollisionFree expressions) (univFaithful : KUniv.CollisionFree universes) + (recipe : ConversionRecipe α) (coveredExprs : ∀ term ∈ recipe.exprs, expressions term) + (coveredUnivs : ∀ level ∈ recipe.univs, universes level) + (before : InternTable .anon) (valid : InternClosed expressions universes before) : + match recipe.run before with + | .ok value after => recipe.predict = .ok value ∧ InternClosed expressions universes after + | .error error after => recipe.predict = .error error ∧ InternClosed expressions universes after := by + induction recipe generalizing before with + | done value => exact ⟨rfl, valid⟩ + | fail error => exact ⟨rfl, valid⟩ + | internE candidate next ih => + have member : expressions candidate := coveredExprs candidate (by simp [ConversionRecipe.exprs]) + have exactValue : (before.internExpr candidate).1 = candidate := by + simpa only [KExpr.eraseMeta_anon] using before.internExpr_eraseMeta valid.coherent + (KExpr.keyCollisionFree_anon.mpr (exprFaithful.mono fun term h => + h.elim (valid.exprs term) (fun equal => equal ▸ member))) + have nextValid : InternClosed expressions universes (before.internExpr candidate).2 := by + refine ⟨valid.coherent.internExpr candidate, ?_, ?_⟩ + · intro term found + exact (InternTable.ExprSupport.of_internExpr found).elim + (valid.exprs term) (fun equal => equal ▸ member) + · intro level found + exact valid.univs level (by simpa only [InternTable.UnivSupport, + InternTable.internExpr_univs] using found) + have post := ih candidate + (fun term h => coveredExprs term (List.mem_cons_of_mem _ h)) coveredUnivs _ nextValid + simpa only [ConversionRecipe.run, ConversionRecipe.predict, InternIngressM.internE, + Bind.bind, EStateM.bind, exactValue] using post + | internU candidate next ih => + have member : universes candidate := coveredUnivs candidate (by simp [ConversionRecipe.univs]) + have exactValue : (before.internUniv candidate).1 = candidate := by + simpa only [KUniv.eraseMeta_anon] using before.internUniv_eraseMeta valid.coherent + (univFaithful.mono fun level h => + h.elim (valid.univs level) (fun equal => equal ▸ member)) + have nextValid : InternClosed expressions universes (before.internUniv candidate).2 := by + refine ⟨valid.coherent.internUniv candidate, ?_, ?_⟩ + · intro term found + exact valid.exprs term (by simpa only [InternTable.ExprSupport, + InternTable.internUniv_exprs] using found) + · intro level found + exact (InternTable.UnivSupport.of_internUniv found).elim + (valid.univs level) (fun equal => equal ▸ member) + have post := ih candidate coveredExprs + (fun level h => coveredUnivs level (List.mem_cons_of_mem _ h)) _ nextValid + simpa only [ConversionRecipe.run, ConversionRecipe.predict, InternIngressM.internU, + Bind.bind, EStateM.bind, exactValue] using post + +/-- Both actual outcomes agree with the source-only prediction. The finite +collision domain also covers every newly retained intern entry. -/ +theorem ConversionRecipe.run_predict (recipe : ConversionRecipe α) (before : InternTable .anon) + (coherent : before.WF) (data : ConversionData recipe before) : + match recipe.run before with + | .ok value after => recipe.predict = .ok value ∧ after.WF ∧ + (∀ term, after.ExprSupport term → before.ExprSupport term ∨ term ∈ recipe.exprs) ∧ + (∀ level, after.UnivSupport level → before.UnivSupport level ∨ level ∈ recipe.univs) + | .error error after => recipe.predict = .error error ∧ after.WF ∧ + (∀ term, after.ExprSupport term → before.ExprSupport term ∨ term ∈ recipe.exprs) ∧ + (∀ level, after.UnivSupport level → before.UnivSupport level ∨ level ∈ recipe.univs) := by + have post := recipe_spec data.expressions data.universes recipe (fun _ => Or.inr) + (fun _ => Or.inr) before ⟨coherent, fun _ => Or.inl, fun _ => Or.inl⟩ + cases run : recipe.run before <;> rw [run] at post <;> + exact ⟨post.1, post.2.coherent, post.2.exprs, post.2.univs⟩ + +end Ix.Kernel.Consistency + +namespace Ix.Kernel.ConversionRecipe + +theorem run_bind (recipe : ConversionRecipe α) (next : α → ConversionRecipe β) : + (recipe >>= next).run = recipe.run >>= fun value => (next value).run := by + change (bind recipe next).run = _ + induction recipe with + | done value => rfl + | fail error => rfl + | internE candidate rest ih | internU candidate rest ih => + simp only [bind, run, ih, bind_assoc] + +end Ix.Kernel.ConversionRecipe + +namespace Ix.Kernel.Consistency + +private def RecipeCorresponds (recipe : ConversionRecipe α) (action : InternIngressM α) : Prop := + recipe.run = action + +private theorem recipe_pure (value : α) : RecipeCorresponds (pure value) (pure value) := rfl +private theorem recipe_throw (error : IngressErr) : + RecipeCorresponds (throw error : ConversionRecipe α) (throw error) := rfl + +private theorem recipe_bind {first : ConversionRecipe α} {second : α → ConversionRecipe β} + {action : InternIngressM α} {next : α → InternIngressM β} + (head : RecipeCorresponds first action) (tail : ∀ value, RecipeCorresponds (second value) (next value)) : + RecipeCorresponds (first >>= second) (action >>= next) := by + change (first >>= second).run = _ + rw [ConversionRecipe.run_bind, head] + exact congrArg (fun rest => action >>= rest) (funext tail) + +private theorem recipe_expr (term : KExpr .anon) : + RecipeCorresponds (ConversionRecipe.emitExpr term) (InternIngressM.internE term) := rfl + +private theorem recipe_univ (level : KUniv .anon) : + RecipeCorresponds (ConversionRecipe.emitUniv level) (InternIngressM.internU level) := rfl + +private def ConvCorresponds (recipe : StateT ConvState ConversionRecipe α) + (action : InternConvM α) : Prop := ∀ cache, RecipeCorresponds (recipe cache) (action cache) + +private theorem conv_pure (value : α) : ConvCorresponds (pure value) (pure value) := fun _ => rfl +private theorem conv_throw (error : IngressErr) : + ConvCorresponds (throw error : StateT ConvState ConversionRecipe α) (throw error) := fun _ => rfl +private theorem conv_throw_bind (error : IngressErr) (next : α → StateT ConvState ConversionRecipe β) + (action : α → InternConvM β) : + ConvCorresponds ((throw error : StateT ConvState ConversionRecipe α) >>= next) + ((throw error : InternConvM α) >>= action) := fun _ => rfl + +private theorem conv_bind {first : StateT ConvState ConversionRecipe α} + {second : α → StateT ConvState ConversionRecipe β} {action : InternConvM α} + {next : α → InternConvM β} (head : ConvCorresponds first action) + (tail : ∀ value, ConvCorresponds (second value) (next value)) : + ConvCorresponds (first >>= second) (action >>= next) := by + intro cache + exact recipe_bind (head cache) (fun (value, cache) => tail value cache) + +private theorem conv_lift {recipe : ConversionRecipe α} {action : InternIngressM α} + (correct : RecipeCorresponds recipe action) : ConvCorresponds (liftM recipe) (liftM action) := + fun _ => recipe_bind correct (fun _ => recipe_pure _) + +private theorem conv_get : ConvCorresponds (get : StateT ConvState ConversionRecipe ConvState) get := + fun _ => rfl + +private theorem conv_modify (update : ConvState → ConvState) : + ConvCorresponds (modify update : StateT ConvState ConversionRecipe Unit) (modify update) := fun _ => rfl + +theorem conversionRecipe_univStep (stack : Array UFrame) (values : Array (KUniv .anon)) : + (ConversionRecipe.univStep stack values).run = convertUnivStep stack values := by + change RecipeCorresponds _ _ + dsimp only [ConversionRecipe.univStep, convertUnivStep] + cases stack.back! with + | process level => + cases level <;> first + | exact recipe_pure _ + | exact recipe_bind (recipe_univ _) (fun _ => recipe_pure _) + | succ | max | imax => exact recipe_bind (recipe_univ _) (fun _ => recipe_pure _) + +theorem conversionRecipe_univLoop (fuel : Nat) (stack : Array UFrame) (values : Array (KUniv .anon)) : + (ConversionRecipe.univLoop fuel stack values).run = convertUnivLoop fuel stack values := by + change RecipeCorresponds _ _ + induction fuel generalizing stack values with + | zero => + dsimp only [ConversionRecipe.univLoop, convertUnivLoop] + split + · cases values.back? <;> first | exact recipe_pure _ | exact recipe_throw _ + · exact recipe_throw _ + | succ fuel ih => + dsimp only [ConversionRecipe.univLoop, convertUnivLoop] + split + · cases values.back? <;> first | exact recipe_pure _ | exact recipe_throw _ + · exact recipe_bind (conversionRecipe_univStep stack values) (fun (stack, values) => ih stack values) + +theorem conversionRecipe_univTree (root : Ixon.Univ) : + (ConversionRecipe.univTree root).run = convertUnivTree root := + conversionRecipe_univLoop _ _ _ + +theorem conversionRecipe_univIdx (ctx : IngressCtx) (idx : UInt64) (cache : ConvState) : + (ConversionRecipe.univIdx ctx idx cache).run = convertUnivIdx ctx idx cache := by + suffices correct : ConvCorresponds (ConversionRecipe.univIdx ctx idx) (convertUnivIdx ctx idx) from + correct cache + dsimp only [ConversionRecipe.univIdx, convertUnivIdx] + apply conv_bind conv_get + intro state + cases state.univCache[idx]? with + | some level => exact conv_pure _ + | none => + cases ctx.univs[idx.toNat]? with + | none => exact conv_throw _ + | some level => + exact conv_bind (conv_lift (conversionRecipe_univTree level)) + (fun _ => conv_bind (conv_modify _) (fun _ => conv_pure _)) + +private theorem list_forIn_corresponds {m n : Type → Type} [Monad m] [Monad n] + (P : {α : Type} → m α → n α → Prop) + (hpure : ∀ {α} (value : α), P (pure value) (pure value)) + (hbind : ∀ {α β} {first : m α} {second : α → m β} {action : n α} {next : α → n β}, + P first action → (∀ value, P (second value) (next value)) → P (first >>= second) (action >>= next)) + (items : List α) (initial : β) (left : α → β → m (ForInStep β)) + (right : α → β → n (ForInStep β)) (step : ∀ item state, P (left item state) (right item state)) : + P (forIn items initial left) (forIn items initial right) := by + induction items generalizing initial with + | nil => simpa only [List.forIn_nil] using hpure initial + | cons item rest ih => + simp only [List.forIn_cons] + apply hbind (step item initial) + intro result + cases result with + | done state => exact hpure state + | yield state => exact ih state + +private theorem array_forIn_corresponds {m n : Type → Type} [Monad m] [Monad n] + (P : {α : Type} → m α → n α → Prop) + (hpure : ∀ {α} (value : α), P (pure value) (pure value)) + (hbind : ∀ {α β} {first : m α} {second : α → m β} {action : n α} {next : α → n β}, + P first action → (∀ value, P (second value) (next value)) → P (first >>= second) (action >>= next)) + (items : Array α) (initial : β) (left : α → β → m (ForInStep β)) + (right : α → β → n (ForInStep β)) (step : ∀ item state, P (left item state) (right item state)) : + P (forIn items initial left) (forIn items initial right) := by + rw [← Array.forIn_toList, ← Array.forIn_toList] + exact list_forIn_corresponds P hpure hbind _ initial _ _ step + +theorem conversionRecipe_univArgs (ctx : IngressCtx) (idxs : Array UInt64) (cache : ConvState) : + (ConversionRecipe.univArgs ctx idxs cache).run = convertUnivArgs ctx idxs cache := by + suffices correct : ConvCorresponds (ConversionRecipe.univArgs ctx idxs) (convertUnivArgs ctx idxs) from + correct cache + dsimp only [ConversionRecipe.univArgs, convertUnivArgs] + apply conv_bind _ (fun _ => conv_pure _) + apply array_forIn_corresponds (@ConvCorresponds) (@conv_pure) (@conv_bind) + intro idx out + exact conv_bind (conversionRecipe_univIdx ctx idx) (fun _ => conv_pure _) + +theorem conversionRecipe_exprStep (source : Ixon.Env) (ctx : IngressCtx) + (stack : Array EFrame) (values : Array (KExpr .anon)) (cache : ConvState) : + (ConversionRecipe.exprStep source ctx stack values cache).run = + convertExprStep source ctx stack values cache := by + suffices correct : ConvCorresponds (ConversionRecipe.exprStep source ctx stack values) + (convertExprStep source ctx stack values) from correct cache + dsimp only [ConversionRecipe.exprStep, convertExprStep] + cases stack.back! with + | process term => + cases term with + | share idx => + apply conv_bind conv_get + intro state + cases state.exprCache[idx]? with + | some value => exact conv_pure _ + | none => cases ctx.sharing[idx.toNat]? <;> first + | exact conv_pure _ + | exact conv_throw_bind _ _ _ + | var idx => exact conv_bind (conv_lift (recipe_expr _)) (fun _ => conv_pure _) + | sort idx => + exact conv_bind (conversionRecipe_univIdx ctx idx) + (fun _ => conv_bind (conv_lift (recipe_expr _)) (fun _ => conv_pure _)) + | ref idx arguments => + dsimp only + cases ctx.refs[idx.toNat]? with + | none => exact conv_throw_bind _ _ _ + | some addr => + dsimp only + exact conv_bind (conversionRecipe_univArgs ctx arguments) + (fun _ => conv_bind (conv_lift (recipe_expr _)) (fun _ => conv_pure _)) + | recur idx arguments => + dsimp only + cases ctx.mutCtx[idx.toNat]? with + | none => exact conv_throw_bind _ _ _ + | some id => + exact conv_bind (conversionRecipe_univArgs ctx arguments) + (fun _ => conv_bind (conv_lift (recipe_expr _)) (fun _ => conv_pure _)) + | nat idx => + dsimp only + cases ctx.refs[idx.toNat]? with + | none => exact conv_throw_bind _ _ _ + | some addr => + dsimp only + cases source.getBlob? addr with + | none => exact conv_throw_bind _ _ _ + | some bytes => exact conv_bind (conv_lift (recipe_expr _)) (fun _ => conv_pure _) + | str idx => + dsimp only + cases ctx.refs[idx.toNat]? with + | none => exact conv_throw_bind _ _ _ + | some addr => + dsimp only + cases source.getBlob? addr with + | none => exact conv_throw_bind _ _ _ + | some bytes => + dsimp only + cases String.fromUTF8? bytes with + | none => exact conv_throw_bind _ _ _ + | some value => exact conv_bind (conv_lift (recipe_expr _)) (fun _ => conv_pure _) + | app | lam | all | letE => exact conv_pure _ + | prj idx field value => + dsimp only + cases ctx.refs[idx.toNat]? <;> first + | exact conv_pure _ + | exact conv_throw_bind _ _ _ + | appDone | lamDone | allDone | letDone | prjDone => + exact conv_bind (conv_lift (recipe_expr _)) (fun _ => conv_pure _) + | cacheShare idx => exact conv_bind (conv_modify _) (fun _ => conv_pure _) + +theorem conversionRecipe_exprLoop (source : Ixon.Env) (ctx : IngressCtx) (fuel : Nat) + (stack : Array EFrame) (values : Array (KExpr .anon)) (cache : ConvState) : + (ConversionRecipe.exprLoop source ctx fuel stack values cache).run = + convertExprLoop source ctx fuel stack values cache := by + suffices correct : ConvCorresponds (ConversionRecipe.exprLoop source ctx fuel stack values) + (convertExprLoop source ctx fuel stack values) from correct cache + induction fuel generalizing stack values with + | zero => + dsimp only [ConversionRecipe.exprLoop, convertExprLoop] + split + · cases values.back? with + | none => exact conv_throw _ + | some value => + dsimp only + split + · exact conv_throw_bind _ _ _ + · exact conv_pure _ + · exact conv_throw _ + | succ fuel ih => + dsimp only [ConversionRecipe.exprLoop, convertExprLoop] + split + · cases values.back? with + | none => exact conv_throw _ + | some value => + dsimp only + split + · exact conv_throw_bind _ _ _ + · exact conv_pure _ + · exact conv_bind (conversionRecipe_exprStep source ctx stack values) + (fun (stack, values) => ih stack values) + +theorem conversionRecipe_expr (source : Ixon.Env) (ctx : IngressCtx) (root : Ixon.Expr) + (cache : ConvState) : + (ConversionRecipe.expr source ctx root cache).run = convertExpr source ctx root cache := + conversionRecipe_exprLoop source ctx _ _ _ cache + +theorem conversionRecipe_defn (source : Ixon.Env) (defn : Ixon.Definition) + (constant : Ixon.Constant) (block : KId .anon) (mutCtx : Array (KId .anon)) + (hints : Option Lean.ReducibilityHints) : + (ConversionRecipe.defn source defn constant block mutCtx hints).run = + convertDefnAnon source defn constant block mutCtx hints := by + change RecipeCorresponds _ _ + dsimp only [ConversionRecipe.defn, convertDefnAnon] + apply recipe_bind (conversionRecipe_expr source _ defn.typ {}) + intro result + exact recipe_bind (conversionRecipe_expr source _ defn.value result.2) (fun _ => recipe_pure _) + +theorem conversionRecipe_recursor (source : Ixon.Env) (rec : Ixon.Recursor) + (constant : Ixon.Constant) (block : KId .anon) (mutCtx : Array (KId .anon)) : + (ConversionRecipe.recursor source rec constant block mutCtx).run = + convertRecursorAnon source rec constant block mutCtx := by + change RecipeCorresponds _ _ + dsimp only [ConversionRecipe.recursor, convertRecursorAnon] + apply recipe_bind (conversionRecipe_expr source _ rec.typ {}) + intro result + apply recipe_bind _ (fun _ => recipe_pure _) + apply array_forIn_corresponds (@RecipeCorresponds) (@recipe_pure) (@recipe_bind) + intro rule state + exact recipe_bind (conversionRecipe_expr source _ rule.rhs _) (fun _ => recipe_pure _) + +theorem conversionRecipe_standalone (source : Ixon.Env) (addr : Address) (constant : Ixon.Constant) : + (ConversionRecipe.standalone source addr constant).run = convertAnonStandalone source addr constant := by + change RecipeCorresponds _ _ + dsimp only [ConversionRecipe.standalone, convertAnonStandalone] + cases constant.info with + | defn defn => exact conversionRecipe_defn source defn constant _ _ _ + | recr rec => exact conversionRecipe_recursor source rec constant _ _ + | axio axio => exact recipe_bind (conversionRecipe_expr source _ axio.typ {}) (fun _ => recipe_pure _) + | quot quot => exact recipe_bind (conversionRecipe_expr source _ quot.typ {}) (fun _ => recipe_pure _) + | muts | dPrj | iPrj | rPrj | cPrj => exact recipe_throw _ + +/-- The actual standalone converter returns the declaration predicted from +the source, including its exact universe arity, type, body, and metadata. -/ +theorem convertAnonStandalone_prediction {source : Ixon.Env} {addr : Address} + {constant : Ixon.Constant} {before after : InternTable .anon} {concrete : KConst .anon} + (coherent : before.WF) (data : ConversionData (ConversionRecipe.standalone source addr constant) before) + (run : convertAnonStandalone source addr constant before = .ok concrete after) : + (ConversionRecipe.standalone source addr constant).predict = .ok concrete ∧ after.WF := by + have post := ConversionRecipe.run_predict (ConversionRecipe.standalone source addr constant) before coherent data + rw [conversionRecipe_standalone, run] at post + exact ⟨post.1, post.2.1⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SourceAgreement.lean b/Ix/Kernel/Verify/Consistency/SourceAgreement.lean new file mode 100644 index 000000000..e78140b9e --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SourceAgreement.lean @@ -0,0 +1,618 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.ConversionRecipe +import Ix.Kernel.Verify.Consistency.RecursiveState + +/-! +# Standalone source agreement through actual lazy loading + +Source-only conversion predicts a complete standalone declaration. A loaded +declaration agrees with this prediction, initially by emptiness and afterward +by actual conversion and publication. Source ownership prevents block loads +from overwriting these standalone entries. Model bindings inspect only the +source prediction, rather than assuming a reading after each mutable lookup. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +theorem predictStandalone?_verified {source : Ixon.Env} {addr : Address} {constant : Ixon.Constant} + (verified : getConstVerified source addr true = .ok (some constant)) + (standalone : ingressBlockAddr? addr constant.info = none) : + predictStandalone? source addr = + (ConversionRecipe.standalone source addr constant).predict.map some := by + unfold predictStandalone? + change Except.bind (getConstVerified source addr true) _ = _ + rw [verified, Except.bind] + dsimp only + rw [standalone] + cases (ConversionRecipe.standalone source addr constant).predict <;> rfl + +theorem predictStandalone?_some {source : Ixon.Env} {addr : Address} {expected : KConst .anon} + (predicted : predictStandalone? source addr = .ok (some expected)) : + ∃ constant, getConstVerified source addr true = .ok (some constant) ∧ + ingressBlockAddr? addr constant.info = none ∧ + (ConversionRecipe.standalone source addr constant).predict = .ok expected := by + unfold predictStandalone? at predicted + change Except.bind (getConstVerified source addr true) _ = _ at predicted + cases verified : getConstVerified source addr true with + | error err => rw [verified, Except.bind] at predicted; contradiction + | ok optional => + cases optional with + | none => + rw [verified, Except.bind] at predicted + change Except.ok none = Except.ok (some expected) at predicted + cases predicted + | some constant => + simp only [verified, Except.bind] at predicted + cases standalone : ingressBlockAddr? addr constant.info with + | some block => + rw [standalone] at predicted + change Except.ok none = Except.ok (some expected) at predicted + cases predicted + | none => + rw [standalone] at predicted + cases result : (ConversionRecipe.standalone source addr constant).predict with + | error err => simp [result, Bind.bind, Except.bind] at predicted + | ok concrete => + simp only [result, Bind.bind, Except.bind, Pure.pure, Except.pure, + Except.ok.injEq, Option.some.injEq] at predicted + cases predicted + exact ⟨constant, rfl, standalone, result⟩ + +/-- Agreement is required at the standalone catalog's selected keys. The +catalog does not claim to interpret mutual members or admit declarations. -/ +def StandaloneSourceAgreement (source : Ixon.Env) (env : AnonEnv) : Prop := + ∀ id concrete, env.get? id = some concrete → ∀ expected, + predictStandalone? source id.addr = .ok (some expected) → concrete = expected + +theorem StandaloneSourceAgreement.empty (source : Ixon.Env) : StandaloneSourceAgreement source {} := by + intro id concrete loaded + simp [KEnv.get?] at loaded + +theorem StandaloneSourceAgreement.ofMap {source : Ixon.Env} {before after : AnonEnv} + (valid : StandaloneSourceAgreement source before) (constants : after.consts = before.consts) : + StandaloneSourceAgreement source after := by + intro id concrete loaded + exact valid id concrete (by simpa only [KEnv.get?, constants] using loaded) + +private theorem agreement_insert {source : Ixon.Env} {env : AnonEnv} {id : KId .anon} + {concrete : KConst .anon} (valid : StandaloneSourceAgreement source env) + (corresponds : ∀ expected, predictStandalone? source id.addr = .ok (some expected) → concrete = expected) : + StandaloneSourceAgreement source (env.insert id concrete) := by + intro other actual loaded expected predicted + by_cases same : id = other + · subst other + simp only [KEnv.get?, KEnv.insert, Std.HashMap.getElem?_insert_self, Option.some.injEq] at loaded + subst actual + exact corresponds expected predicted + · exact valid other actual + (by simpa [KEnv.get?, KEnv.insert, Std.HashMap.getElem?_insert, same] using loaded) expected predicted + +/-- A projected member cannot be a selected standalone catalog key. -/ +theorem SourceOwnership.projection_unpredicted {source : Ixon.Env} (ownership : SourceOwnership source) + {block : Address} {id : KId .anon} (projection : SourceProjection source block id) + {expected : KConst .anon} (predicted : predictStandalone? source id.addr = .ok (some expected)) : False := by + obtain ⟨constant, verified, standalone, _⟩ := predictStandalone?_some predicted + have eta : (⟨id.addr, ()⟩ : KId .anon) = id := by + cases id with | mk addr name => cases name; rfl + exact ownership.standalone id.addr constant verified standalone block (by rwa [eta]) + +private theorem agreement_fold {source : Ixon.Env} (entries : List Entry) + (corresponds : ∀ entry ∈ entries, ∀ expected, + predictStandalone? source entry.1.addr = .ok (some expected) → entry.2 = expected) + (before : AnonEnv) (valid : StandaloneSourceAgreement source before) : + StandaloneSourceAgreement source + (entries.foldl (fun env entry => env.insert entry.1 entry.2) before) := by + induction entries generalizing before with + | nil => exact valid + | cons entry rest ih => + apply ih (fun member found => corresponds member (List.mem_cons_of_mem _ found)) + exact agreement_insert valid (corresponds entry List.mem_cons_self) + +private theorem agreement_publish {source : Ixon.Env} {before : AnonEnv} {entries : Array Entry} + (valid : StandaloneSourceAgreement source before) + (corresponds : ∀ entry ∈ entries, ∀ expected, + predictStandalone? source entry.1.addr = .ok (some expected) → entry.2 = expected) : + StandaloneSourceAgreement source (insertMutsEntriesState before entries) := by + unfold insertMutsEntriesState insertEntriesState + apply agreement_fold entries.toList (by simpa using corresponds) + split <;> exact valid.ofMap rfl + +private theorem agreement_insertMuts {source : Ixon.Env} {before : AnonEnv} {entries : Array Entry} + (valid : StandaloneSourceAgreement source before) + (corresponds : ∀ entry ∈ entries, ∀ expected, + predictStandalone? source entry.1.addr = .ok (some expected) → entry.2 = expected) : + match insertMutsEntries entries before with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after := by + unfold insertMutsEntries + change (match (EStateM.bind (guardReserved entries) _ : IngressM Unit) before with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after) + rw [EStateM.bind, guardReserved_state] + cases checkReserved entries with + | error err => exact valid + | ok value => exact agreement_publish valid corresponds + +/-- Block conversion may add arbitrary member types while preserving all +standalone source bindings: its emitted keys belong only to the owning block. -/ +theorem ingressAnonBlock_sourceAgreement {source : Ixon.Env} (ownership : SourceOwnership source) + {addr : Address} {constant : Ixon.Constant} + (verified : getConstVerified source addr true = .ok (some constant)) + (before : AnonEnv) (valid : StandaloneSourceAgreement source before) : + match ingressAnonBlock source constant addr before with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after := by + have inner : match ingressAnonBlockWithTrace source constant addr before with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after := by + unfold ingressAnonBlockWithTrace + change (match (EStateM.bind (prepareAnonBlock source constant addr) _ : + IngressM AnonBlockIngressTrace) before with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after) + unfold prepareAnonBlock IngressM.runIntern EStateM.bind + dsimp only + cases converted : convertAnonBlock source constant addr before.intern with + | error err it => exact valid.ofMap rfl + | ok trace it => + have keys := convertAnonBlock_projection_keys converted + have published := agreement_insertMuts (valid.ofMap (after := {before with intern := it}) rfl) + (entries := trace.allEntries) (fun entry member _ predicted => False.elim + (ownership.projection_unpredicted ⟨constant, verified, keys entry member⟩ predicted)) + change (match EStateM.bind (insertMutsEntries trace.allEntries) + (fun _ => EStateM.pure trace) {before with intern := it} with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after) + rw [EStateM.bind] + cases run : insertMutsEntries trace.allEntries {before with intern := it} <;> + rw [run] at published <;> exact published + unfold ingressAnonBlock + change (match (EStateM.bind (ingressAnonBlockWithTrace source constant addr) _ : + IngressM (Array (KId .anon))) before with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after) + rw [EStateM.bind] + cases run : ingressAnonBlockWithTrace source constant addr before <;> rw [run] at inner <;> exact inner + +/-- Actual standalone publication establishes the catalog agreement using +finite collision data before conversion, with no post-load reading premise. -/ +theorem ingressAnonStandalone_sourceAgreement {source : Ixon.Env} {addr : Address} + {constant : Ixon.Constant} + (verified : getConstVerified source addr true = .ok (some constant)) + (standalone : ingressBlockAddr? addr constant.info = none) + (before : AnonEnv) (valid : StandaloneSourceAgreement source before) (coherent : before.intern.WF) + (data : ConversionData (ConversionRecipe.standalone source addr constant) before.intern) : + match ingressAnonStandalone source addr constant before with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after := by + unfold ingressAnonStandalone + change (match (EStateM.bind (IngressM.runIntern (convertAnonStandalone source addr constant)) + _ : IngressM (KId .anon)) before with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after) + unfold EStateM.bind IngressM.runIntern + cases converted : convertAnonStandalone source addr constant before.intern with + | error err it => exact valid.ofMap rfl + | ok concrete it => + have predicted := (convertAnonStandalone_prediction coherent data converted).1 + have catalog : predictStandalone? source addr = .ok (some concrete) := by + rw [predictStandalone?_verified verified standalone, predicted] + rfl + change (match (EStateM.bind (insertStandaloneEntries #[(⟨addr, ()⟩, concrete)]) + _ : IngressM (KId .anon)) {before with intern := it} with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after) + rw [EStateM.bind, insertStandaloneEntries_singleton] + cases reservedMarkerName addr with + | some marker => exact valid.ofMap rfl + | none => + apply (agreement_insert (valid.ofMap (after := {before with intern := it}) rfl) ?_).ofMap rfl + intro expected expectedPrediction + exact Option.some.inj (Except.ok.inj (catalog.symm.trans expectedPrediction)) + +/-- Only a verified standalone's source conversion needs collision data. +Blocks need key ownership to preserve this catalog, not a reading of their types. -/ +def StandaloneConversionData (source : Ixon.Env) (addr : Address) (before : AnonEnv) : Prop := + ∀ constant, getConstVerified source addr true = .ok (some constant) → + ingressBlockAddr? addr constant.info = none → + ConversionData (ConversionRecipe.standalone source addr constant) before.intern + +theorem ingressAnonAddrShallow_sourceAgreement {source : Ixon.Env} (ownership : SourceOwnership source) + (addr : Address) (before : AnonEnv) (valid : StandaloneSourceAgreement source before) + (coherent : before.intern.WF) (data : StandaloneConversionData source addr before) : + match ingressAnonAddrShallow source addr true before with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after := by + unfold ingressAnonAddrShallow + change (match (EStateM.bind (IngressM.liftExcept (getConstVerified source addr true)) + _ : IngressM Bool) before with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after) + cases verified : getConstVerified source addr true with + | error err => exact valid + | ok optional => + cases optional with + | none => exact valid + | some constant => + dsimp only [IngressM.liftExcept, Bind.bind, Pure.pure, EStateM.pure, EStateM.bind] + cases block : ingressBlockAddr? addr constant.info with + | none => + dsimp only [Bind.bind, EStateM.bind] + have preserved := ingressAnonStandalone_sourceAgreement verified block before valid coherent + (data constant verified block) + cases run : ingressAnonStandalone source addr constant before <;> + rw [run] at preserved <;> exact preserved + | some blockAddr => + dsimp only [Bind.bind, EStateM.bind] + rw [show (get : IngressM AnonEnv) before = .ok before before from rfl] + dsimp only + by_cases recorded : before.blocks.contains ⟨blockAddr, ()⟩ = true + · simp only [recorded, ↓reduceIte] + exact valid + · simp only [recorded, Bool.false_eq_true, ↓reduceIte] + cases parent : getConstVerified source blockAddr true with + | error err => exact valid + | ok optional => + cases optional with + | none => exact valid + | some blockConstant => + have preserved := ingressAnonBlock_sourceAgreement ownership parent before valid + dsimp only [IngressM.liftExcept, Bind.bind, Pure.pure, EStateM.pure, EStateM.bind] + cases run : ingressAnonBlock source blockConstant blockAddr before <;> + rw [run] at preserved <;> exact preserved + +/-- Reusable source agreement accompanies the previously established ownership +and coherence resource. It makes no claim about the typing of newly loaded code. -/ +structure SourceStateInvariant (source : Ixon.Env) (before : TcState .anon) : Prop where + state : InferenceStateInvariant source before + agreement : StandaloneSourceAgreement source before.env + +theorem SourceStateInvariant.ofCheckedSource (source : Ixon.Env) + (checked : sourceOwnershipCheck source = true) : + SourceStateInvariant source (TcState.newLazyAnon source) := + ⟨.ofCheckedSource source checked, .empty source⟩ + +theorem SourceStateInvariant.ofMaps {source : Ixon.Env} {before after : TcState .anon} + (valid : SourceStateInvariant source before) (installed : after.lazyFault = before.lazyFault) + (constants : after.env.consts = before.env.consts) (blocks : after.env.blocks = before.env.blocks) + (coherent : after.env.intern.WF) : SourceStateInvariant source after := + ⟨valid.state.ofMaps installed constants blocks coherent, valid.agreement.ofMap constants⟩ + +theorem SourceStateInvariant.afterInferKey {source : Ixon.Env} {before after : TcState .anon} + {term : KExpr .anon} {key : Address × Address} (valid : SourceStateInvariant source before) + (run : TcM.inferKey term before = .ok key after) : SourceStateInvariant source after := + ⟨valid.state.afterInferKey run, valid.agreement.ofMap (congrArg KEnv.consts (inferKey_environment run))⟩ + +theorem lazyIngressAddr_sourceAgreement {source : Ixon.Env} {before : TcState .anon} {addr : Address} + (valid : SourceStateInvariant source before) (data : StandaloneConversionData source addr before.env) : + match TcM.lazyIngressAddr addr before with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after.env := by + unfold TcM.lazyIngressAddr + rw [valid.state.installed] + dsimp only + by_cases faulted : before.faultedAddrs.contains addr = true + · rw [if_pos faulted] + exact valid.agreement + · rw [if_neg faulted] + have preserved := ingressAnonAddrShallow_sourceAgreement valid.state.ownership addr before.env + valid.agreement valid.state.coherent data + cases run : ingressAnonAddrShallow source addr true before.env <;> + rw [run] at preserved <;> exact preserved + +theorem tryGetConst_sourceAgreement {source : Ixon.Env} {before : TcState .anon} {id : KId .anon} + (valid : SourceStateInvariant source before) (data : StandaloneConversionData source id.addr before.env) : + match TcM.tryGetConst id before with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after.env := by + unfold TcM.tryGetConst + change (match (EStateM.bind (get : TcM .anon (TcState .anon)) _ : + TcM .anon (Option (KConst .anon))) before with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after.env) + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + dsimp only + cases before.env.get? id with + | some concrete => exact valid.agreement + | none => + change (match (EStateM.bind (TcM.lazyIngressAddr id.addr) _ : + TcM .anon (Option (KConst .anon))) before with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after.env) + have preserved := lazyIngressAddr_sourceAgreement valid data + cases fault : TcM.lazyIngressAddr id.addr before with + | error err after => + rw [EStateM.bind, fault] + simpa only [fault] using preserved + | ok value after => + rw [fault] at preserved + rw [EStateM.bind, fault] + change (match (EStateM.bind (get : TcM .anon (TcState .anon)) _ : + TcM .anon (Option (KConst .anon))) after with + | .ok _ state | .error _ state => StandaloneSourceAgreement source state.env) + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) after = .ok after after from rfl] + dsimp only + cases after.env.get? id with + | some concrete => exact preserved + | none => cases before.lazyFault.isSome <;> exact preserved + +theorem getConst_sourceAgreement {source : Ixon.Env} {before : TcState .anon} {id : KId .anon} + (valid : SourceStateInvariant source before) (data : StandaloneConversionData source id.addr before.env) : + match TcM.getConst id before with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after.env := by + unfold TcM.getConst + change (match (EStateM.bind (TcM.tryGetConst id) _ : TcM .anon (KConst .anon)) before with + | .ok _ after | .error _ after => StandaloneSourceAgreement source after.env) + have preserved := tryGetConst_sourceAgreement valid data + cases tried : TcM.tryGetConst id before with + | error err after => + rw [EStateM.bind, tried] + simpa only [tried] using preserved + | ok optional after => + rw [tried] at preserved + rw [EStateM.bind, tried] + cases optional <;> exact preserved + +/-- Both lookup outcomes return ownership, coherence, and exact agreement +with the standalone source prediction for use by subsequent calls. -/ +theorem SourceStateInvariant.getConst {source : Ixon.Env} {before : TcState .anon} {id : KId .anon} + (valid : SourceStateInvariant source before) (data : StandaloneConversionData source id.addr before.env) : + match TcM.getConst id before with + | .ok _ after | .error _ after => SourceStateInvariant source after := by + have state := valid.state.getConst id + have agreement := getConst_sourceAgreement valid data + cases run : TcM.getConst id before <;> rw [run] at state agreement <;> exact ⟨state, agreement⟩ + +private theorem tryGetConst_result_loaded {id : KId .anon} {concrete : KConst .anon} + {before after : TcState .anon} (run : TcM.tryGetConst id before = .ok (some concrete) after) : + after.env.get? id = some concrete := by + unfold TcM.tryGetConst at run + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ at run + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] at run + dsimp only at run + cases loaded : before.env.get? id with + | some value => rw [loaded] at run; cases run; exact loaded + | none => + rw [loaded] at run + change EStateM.bind (TcM.lazyIngressAddr id.addr) _ before = _ at run + rw [EStateM.bind] at run + cases fault : TcM.lazyIngressAddr id.addr before with + | error err failed => rw [fault] at run; contradiction + | ok value foundState => + rw [fault] at run + change EStateM.bind (get : TcM .anon (TcState .anon)) _ foundState = _ at run + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) foundState = + .ok foundState foundState from rfl] at run + dsimp only at run + cases found : foundState.env.get? id with + | some value => rw [found] at run; cases run; exact found + | none => + rw [found] at run + cases enabled : before.lazyFault.isSome with + | false => + simp only [enabled, Bool.false_eq_true, ↓reduceIte] at run + change EStateM.Result.ok none foundState = .ok (some concrete) after at run + cases run + | true => + simp only [enabled, ↓reduceIte] at run + change EStateM.Result.error (TcError.unknownConst id.addr) foundState = _ at run + cases run + +/-- A successful lookup returns a declaration present in its actual final +environment, regardless of which lazy callback was installed. -/ +theorem getConst_result_loaded {id : KId .anon} {concrete : KConst .anon} + {before after : TcState .anon} (run : TcM.getConst id before = .ok concrete after) : + after.env.get? id = some concrete := by + unfold TcM.getConst at run + change EStateM.bind (TcM.tryGetConst id) _ before = _ at run + rw [EStateM.bind] at run + cases tried : TcM.tryGetConst id before with + | error err failed => rw [tried] at run; contradiction + | ok optional foundState => + rw [tried] at run + cases optional with + | none => cases run + | some value => cases run; exact tryGetConst_result_loaded tried + +/-- A model binding inspects the immutable source prediction once. Its +universe count and closed type reading do not mention a mutable lookup run. -/ +structure StandaloneModelBinding {β : Type u} (source : Ixon.Env) + (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) + (id : KId .anon) (ref : ConstRef β) (entry : ConstantEntry β) where + constant : KConst .anon + predicted : predictStandalone? source id.addr = .ok (some constant) + resolved : resolve id.addr = some ref + found : entries ref = some entry + universes : constant.lvls.toNat = entry.universes + reading : readScopedExpr? resolve [] constant.ty = some entry.type.erase + +/-- Actual lookup derives the admitted entry's arity and type reading from +the static source binding, and returns the invariant for the next call. -/ +theorem StandaloneModelBinding.getConst {β : Type u} {source : Ixon.Env} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {id : KId .anon} {ref : ConstRef β} {entry : ConstantEntry β} + {before after : TcState .anon} {concrete : KConst .anon} + (binding : StandaloneModelBinding source resolve entries id ref entry) + (valid : SourceStateInvariant source before) + (data : StandaloneConversionData source id.addr before.env) + (run : TcM.getConst id before = .ok concrete after) : + concrete.lvls.toNat = entry.universes ∧ + readScopedExpr? resolve [] concrete.ty = some entry.type.erase ∧ SourceStateInvariant source after := by + have post := valid.getConst data + rw [run] at post + have equal := post.agreement id concrete (getConst_result_loaded run) binding.constant binding.predicted + exact ⟨equal ▸ binding.universes, equal ▸ binding.reading, post⟩ + +/-- Build the existing scoped inference interface without its post-lookup +arity, type-reading, or coherence premises. Only finite conversion and +universe-instantiation collision/level data remain at the executed operations. -/ +theorem ScopedConstantInferenceSupport.ofSource {β : Type u} {source : Ixon.Env} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {before : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} + {ref : ConstRef β} {entry : ConstantEntry β} + (binding : StandaloneModelBinding source resolve entries id ref entry) + (valid : SourceStateInvariant source before) (wellFormed : entries.WF) + (conversion : StandaloneConversionData source id.addr before.env) + (instantiation : ConstantInstantiationData before id arguments) : + ScopedConstantInferenceSupport resolve entries before id arguments ref entry := by + refine ⟨binding.resolved, binding.found, wellFormed.typeScope ref entry binding.found, ?_⟩ + intro concrete loaded got + obtain ⟨arity, reading, post⟩ := binding.getConst valid conversion got + exact ⟨arity, reading, post.state.coherent, + instantiation.faithful concrete loaded got, instantiation.levels concrete loaded got⟩ + +theorem ConstantInferenceSupport.ofSource {β : Type u} {source : Ixon.Env} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {before : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} + {ref : ConstRef β} {entry : ConstantEntry β} + (binding : StandaloneModelBinding source resolve entries id ref entry) + (valid : SourceStateInvariant source before) (wellFormed : entries.WF) + (conversion : StandaloneConversionData source id.addr before.env) + (instantiation : ConstantInstantiationData before id arguments) : + ConstantInferenceSupport resolve entries before id arguments ref entry := + (ScopedConstantInferenceSupport.ofSource binding valid wellFormed conversion instantiation).closed + +/-- The actual constant call refines model typing from a source-only binding, +with key computation included and no assumed type reading after lookup. -/ +theorem infer_const_source_sound {β : Type u} {source : Ixon.Env} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {context : Model.Context β} {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} + {methods : Methods .anon} {before after : TcState .anon} {result : KExpr .anon} + (binding : StandaloneModelBinding source resolve entries id ref entry) + (valid : SourceStateInvariant source before) (wellFormed : entries.WF) + (miss : UncachedInference before (.const id arguments info)) + (conversion : StandaloneConversionData source id.addr before.env) + (instantiation : ConstantInstantiationData miss.keyed id arguments) + (accepted : RecM.infer (.const id arguments info) methods before = .ok result after) : + ModelTyping.{u,v} resolve entries context (.const id arguments info) result := + infer_const_sound miss (.ofSource binding (valid.afterInferKey miss.keyRun) wellFormed + (by rwa [inferKey_environment miss.keyRun]) instantiation) wellFormed accepted + +/-- Binder opening changes only local state and intern tables, retaining the +standalone source catalog while deriving the new table's coherence. -/ +theorem SourceStateInvariant.openBinder {source : Ixon.Env} + {before after : TcState .anon} {name : Mode.anon.F Name} + {bi : Mode.anon.F Lean.BinderInfo} {domain body opened : KExpr .anon} {fresh : FVarId} + (valid : SourceStateInvariant source before) (data : BinderOpeningData before body) + (run : TcM.openBinder name bi domain body before = .ok (opened, fresh) after) : + SourceStateInvariant source after := by + refine ⟨valid.state.openBinder data run, ?_⟩ + rw [openBinder_eq] at run + split at run + · cases run; exact valid.agreement.ofMap rfl + · contradiction + +/-- The existing operational tree needs only finite standalone-conversion +collision data at constant misses. It carries no post-load reading or source +agreement assumption at any recursive boundary. -/ +def OwnedInferenceTrace.SourceData {fuel : Nat} {before : TcState .anon} {term : KExpr .anon} + (tree : OwnedInferenceTrace fuel before term) (source : Ixon.Env) : Prop := + match tree with + | .hit _ | .sort _ | .fvar _ => True + | @OwnedInferenceTrace.const _ _ id _ _ miss _ => + StandaloneConversionData source id.addr miss.keyed.env + | .app _ _ _ _ _ first second | .forallE _ _ _ first second | + .lam _ _ _ _ _ first second => first.SourceData source ∧ second.SourceData source + +private theorem source_invariant_hash {source : Ixon.Env} {before after : TcState .anon} + {left right : KExpr .anon} {methods : Methods .anon} + (valid : SourceStateInvariant source before) (equal : (left.addr == right.addr) = true) + (run : RecM.isDefEq left right methods before = .ok true after) : + SourceStateInvariant source after := by + rw [isDefEq_hash_state equal] at run + split at run <;> cases run <;> exact valid.ofMaps rfl rfl rfl valid.state.coherent + +private theorem source_invariant_miss {source : Ixon.Env} {term result : KExpr .anon} + {methods : Methods .anon} {before after : TcState .anon} + (valid : SourceStateInvariant source before) (miss : UncachedInference before term) + (accepted : RecM.infer term methods before = .ok result after) + (uncached : ∀ middle, + RecM.inferUncached RecM.inferCall before.inferOnly term methods miss.keyed = .ok result middle → + SourceStateInvariant source miss.keyed → SourceStateInvariant source middle) : + SourceStateInvariant source after := by + obtain ⟨middle, run, written⟩ := infer_uncached_success_state miss accepted + have post := uncached middle run (valid.afterInferKey miss.keyRun) + rw [written] + cases before.inferOnly <;> exact post.ofMaps rfl rfl rfl post.state.coherent + +/-- Actual recursive inference preserves the source catalog from its initial state. -/ +theorem OwnedInferenceTrace.preservesSource {source : Ixon.Env} {fuel : Nat} + {before after : TcState .anon} {term result : KExpr .anon} + (tree : OwnedInferenceTrace fuel before term) (valid : SourceStateInvariant source before) + (sourceData : tree.SourceData source) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + SourceStateInvariant source after := by + induction tree generalizing result after with + | hit hit => + rw [hit.run] at accepted + cases accepted + exact valid.afterInferKey hit.keyRun + | @sort fuel before level info miss => + apply source_invariant_miss valid miss accepted + intro middle run keyed + change EStateM.Result.ok + (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).1 + {miss.keyed with env := {miss.keyed.env with intern := + (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).2}} = + .ok result middle at run + cases run + exact keyed.ofMaps rfl rfl rfl (keyed.state.coherent.internExpr _) + | @fvar fuel before id name info miss => + apply source_invariant_miss valid miss accepted + intro middle run keyed + change (RecM.inferUncached RecM.inferCall before.inferOnly (.fvar id name info)).run + (methodsN fuel) miss.keyed = _ at run + unfold RecM.inferUncached at run + simp only [ReaderT.run_bind] at run + change EStateM.bind (get : TcM .anon (TcState .anon)) _ miss.keyed = _ at run + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) miss.keyed = + .ok miss.keyed miss.keyed from rfl] at run + dsimp only at run + split at run + · cases run; exact keyed + · contradiction + | @const fuel before id arguments info miss data => + apply source_invariant_miss valid miss accepted + intro middle run keyed + obtain ⟨concrete, loaded, got, _, instantiated⟩ := inferUncached_const_instantiation run + have lookup := keyed.getConst sourceData + rw [got] at lookup + have post := TcM.instantiateUnivParams_wf (data.faithful concrete loaded got) + (fun _ h => Or.inr h) ⟨lookup.state.coherent, fun _ h => Or.inl h⟩ + rw [instantiated] at post + rw [post.2.2.1] + exact lookup.ofMaps rfl rfl rfl post.1.1 + | app full miss trace hashPath data functionTree argumentTree functionIH argumentIH => + apply source_invariant_miss valid miss accepted + intro middle run keyed + rw [full] at run + have function := functionIH keyed sourceData.1 trace.functionRun + have argument := argumentIH function sourceData.2 trace.argumentRun + have compared := source_invariant_hash argument hashPath trace.compareRun + rw [(trace.output_state run).2] + exact compared.ofMaps rfl rfl rfl (data.coherent compared.state.coherent) + | forallE miss trace opening domainTree bodyTree domainIH bodyIH => + apply source_invariant_miss valid miss accepted + intro middle run keyed + have domain := domainIH keyed sourceData.1 trace.domainRun + have opened := domain.openBinder opening trace.openRun + have body := bodyIH opened sourceData.2 trace.bodyRun + rw [(trace.output_state run).2] + exact body.ofMaps rfl rfl rfl (body.state.coherent.internExpr _) + | lam full miss trace opening closing domainTree bodyTree domainIH bodyIH => + apply source_invariant_miss valid miss accepted + intro middle run keyed + rw [full] at run + have domain := domainIH keyed sourceData.1 trace.domainRun + have opened := domain.openBinder opening trace.openRun + have body := bodyIH opened sourceData.2 trace.bodyRun + rw [(trace.output_state run).2] + exact body.ofMaps rfl rfl rfl ((closing.coherent body.state.coherent).internExpr _) + +/-- A recursive call retains the existing cache frame and returns source +agreement, ownership, and coherence for subsequent dependent lookups. -/ +theorem OwnedInferenceTrace.frameSource {source : Ixon.Env} {fuel : Nat} + {before after : TcState .anon} {term result : KExpr .anon} + (tree : OwnedInferenceTrace fuel before term) (valid : SourceStateInvariant source before) + (sourceData : tree.SourceData source) + {key : Address × Address} (outside : key ∉ tree.writes) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + InferenceCacheFrame key before after ∧ after.inferOnly = before.inferOnly ∧ + SourceStateInvariant source after := + let frame := tree.frame valid.state outside accepted + ⟨frame.1, frame.2.1, tree.preservesSource valid sourceData accepted⟩ + +end Ix.Kernel.Consistency diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 87e81744c..7aa1dd183 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -3,6 +3,7 @@ module public import LSpec public import Ix.Kernel public import Ix.Kernel.SourceOwnership +public import Ix.Kernel.SourceConversion public import Tests.Ix.Kernel.IxonFixtures /-! @@ -1520,9 +1521,253 @@ private def recursiveStateCases : TestSeq := ++ test "recursive state: nested application and later cache replay retain both invariants" (stateAcrossRecursiveLoads 2 false true true) +/-- Compare every anonymous field, including recursive syntax and annotations. +The production expression/level BEq instances compare addresses only. -/ +private def sameSourceLevel : KUniv .anon → KUniv .anon → Bool + | .zero a, .zero b => a == b + | .succ a ah, .succ b bh => ah == bh && sameSourceLevel a b + | .max a b ah, .max c d bh | .imax a b ah, .imax c d bh => + ah == bh && sameSourceLevel a c && sameSourceLevel b d + | .param a _ ah, .param b _ bh => a == b && ah == bh + | _, _ => false + +private def sameSourceExpr (left right : KExpr .anon) : Bool := + left.addr == right.addr && left.lbr == right.lbr && left.count0 == right.count0 && + left.hasFVars == right.hasFVars && match left, right with + | .var a _ _, .var b _ _ => a == b + | .fvar a _ _, .fvar b _ _ => a == b + | .sort a _, .sort b _ => sameSourceLevel a b + | .const a us _, .const b vs _ => a.addr == b.addr && us.size == vs.size && + (us.zip vs).all (fun (u, v) => sameSourceLevel u v) + | .app a b _, .app c d _ | .lam _ _ a b _, .lam _ _ c d _ | + .all _ _ a b _, .all _ _ c d _ => sameSourceExpr a c && sameSourceExpr b d + | .letE _ a b c nd _, .letE _ d e f md _ => nd == md && + sameSourceExpr a d && sameSourceExpr b e && sameSourceExpr c f + | .prj a i v _, .prj b j w _ => a.addr == b.addr && i == j && sameSourceExpr v w + | .nat a ah _, .nat b bh _ => a == b && ah == bh + | .str a ah _, .str b bh _ => a == b && ah == bh + | _, _ => false + +private def sameStandalone : KConst .anon → KConst .anon → Bool + | .axio _ _ isUnsafe n ty, .axio _ _ isUnsafe' n' ty' => + isUnsafe == isUnsafe' && n == n' && sameSourceExpr ty ty' + | .quot _ _ kind n ty, .quot _ _ kind' n' ty' => + kind == kind' && n == n' && sameSourceExpr ty ty' + | .defn _ _ kind safety hints n ty val _ block, + .defn _ _ kind' safety' hints' n' ty' val' _ block' => + kind == kind' && safety == safety' && hints == hints' && n == n' && + sameSourceExpr ty ty' && sameSourceExpr val val' && block.addr == block'.addr + | .recr _ _ k isUnsafe n p i m s block idx ty rules _, + .recr _ _ k' isUnsafe' n' p' i' m' s' block' idx' ty' rules' _ => + k == k' && isUnsafe == isUnsafe' && n == n' && p == p' && i == i' && m == m' && + s == s' && block.addr == block'.addr && idx == idx' && sameSourceExpr ty ty' && + rules.size == rules'.size && (rules.zip rules').all (fun (a, b) => + a.fields == b.fields && sameSourceExpr a.rhs b.rhs) + | _, _ => false + +private def sourceCatalogMatches (source : Ixon.Env) (env : AnonEnv) : Bool := + env.consts.toList.all fun (id, concrete) => match predictStandalone? source id.addr with + | .ok (some expected) => sameStandalone concrete expected + | _ => true + +/-- Check independent expected fields, actual cold and warm conversion, and +publication by the verified lazy loader. -/ +private def predictionMatches (source : Ixon.Env) (addr : Address) (expected : KConst .anon) : Bool := + match getConstVerified source addr true, predictStandalone? source addr with + | .ok (some constant), .ok (some predicted) => + match convertAnonStandalone source addr constant .empty with + | .ok cold table => + match convertAnonStandalone source addr constant table, + TcM.getConst (m := .anon) ⟨addr, ()⟩ (TcState.newLazyAnon source) with + | .ok warm after, .ok loaded state => + sameStandalone predicted expected && sameStandalone cold expected && + sameStandalone warm expected && sameStandalone loaded expected && + sourceCatalogMatches source state.env && internKeysCoherent after && + after.exprs.size == table.exprs.size && after.univs.size == table.univs.size + | _, _ => false + | .error _ _ => false + | _, _ => false + +private def predictionShared (kind : Nat) : Bool := + let (source, addr) := storeConst {} (lazyCacheDependency kind) + let value := KExpr.mkLam (m := .anon) () () (.mkSort levelOne) + (.mkLam () () (.mkVar 0 ()) (.mkVar 0 ())) + let expected := match kind with + | 0 => KConst.axio () () false 0 identityType + | 1 => .defn () () .defn .safe (.regular 0) 0 identityType value () ⟨addr, ()⟩ + | 2 => .recr () () false false 0 0 0 0 0 ⟨addr, ()⟩ 0 identityType + #[⟨(), 0, value⟩, ⟨(), 1, value⟩] () + | _ => .quot () () .type 0 identityType + predictionMatches source addr expected + +/-- All expression forms, both let flags, literal blobs, self/reference level +arguments, normalization, and anonymous reducibility hints in one declaration. -/ +private def predictionRichDefinition : Bool := + let (source, carrier) := envA + let (source, natBlob) := source.storeBlob ⟨(42 : Nat).toBytesLE⟩ + let (source, strBlob) := source.storeBlob "héllo".toUTF8 + let shared := Ixon.Expr.leanAll (.sort 0) (.var 0) + let typ := Ixon.Expr.leanAll (.sort 1) + (.letE true (.share 0) (.recur 0 #[1, 0]) + (.prj 0 2 (.app (.ref 0 #[0, 0]) (.var 0)))) + let value := Ixon.Expr.letE false (.sort 1) (.nat 1) + (.leanLam (.sort 0) (.app (.str 2) (.share 0))) + let level : Ixon.Univ := .imax (.max (.var 0) (.var 1)) (.var 1) + let constant : Ixon.Constant := ⟨.defn ⟨.opaq, .unsaf, 2, typ, value⟩, + #[shared], #[carrier, natBlob, strBlob], #[level, .imax (.succ .zero) .zero]⟩ + let (source, addr) := storeConst source constant + let source := {source with anonHints := source.anonHints.insert addr (.regular 37)} + let u := KUniv.mkIMax (.mkMax (.mkParam 0 ()) (.mkParam 1 ())) (.mkParam 1 ()) + let sort := KExpr.mkSort (m := .anon) u + let prop := KExpr.mkSort (m := .anon) .mkZero + let shared := KExpr.mkAll () () sort (.mkVar 0 ()) + let typ := KExpr.mkAll () () prop + (.mkLet () shared (.mkConst ⟨addr, ()⟩ #[.mkZero, u]) + (.mkPrj ⟨carrier, ()⟩ 2 (.mkApp (.mkConst ⟨carrier, ()⟩ #[u, u]) (.mkVar 0 ()))) true) + let value := KExpr.mkLet () prop (.mkNat 42 natBlob) + (.mkLam () () sort (.mkApp (.mkStr "héllo" strBlob) shared)) false + predictionMatches source addr (.defn () () .opaq .unsaf (.regular 37) 2 typ value () ⟨addr, ()⟩) + +private def predictionRecursorFields : Bool := + let (source, addr) := storeConst {} + ⟨.recr ⟨true, true, 2, 3, 4, 5, 6, .sort 0, + #[⟨7, .recur 0 #[0]⟩, ⟨8, .sort 0⟩]⟩, #[], #[], #[.var 1]⟩ + let u := KUniv.mkParam (m := .anon) 1 () + let ty := KExpr.mkSort u + predictionMatches source addr (.recr () () true true 2 3 4 5 6 ⟨addr, ()⟩ 0 ty + #[⟨(), 7, .mkConst ⟨addr, ()⟩ #[u]⟩, ⟨(), 8, ty⟩] ()) + +/-- The prediction remains independent of a poisoned intern entry. This +intentionally violates finite collision freedom while keeping the same hash. -/ +private def predictionCollisionBoundary (levelCollision : Bool) : Bool := + let (source, addr) := envA + match getConstVerified source addr true, predictStandalone? source addr with + | .ok (some constant), .ok (some predicted) => + let forged := KExpr.sort levelOne {predicted.ty.info with lbr := 9, count0 := 8} + let levels := (∅ : Std.HashMap Address (KUniv .anon)).insert + levelOne.addr (.param 9 () levelOne.addr) + let expressions := (∅ : Std.HashMap Address (KExpr .anon)).insert predicted.ty.addr forged + let table : InternTable .anon := if levelCollision then + {InternTable.empty with univs := levels} + else {InternTable.empty with exprs := expressions} + match convertAnonStandalone source addr constant table with + | .ok actual _ => actual.ty.addr == predicted.ty.addr && !sameStandalone actual predicted && + sameSourceExpr predicted.ty (.mkSort levelOne) + | .error _ _ => false + | _, _ => false + +private def predictionFailure (kind : Nat) : Bool := + let broken : Ixon.Expr := match kind with + | 0 => .share 9 + | 1 => .sort 9 + | 2 => .ref 9 #[] + | 3 => .recur 9 #[] + | _ => .share 0 + let constant : Ixon.Constant := + ⟨.defn ⟨.defn, .safe, 0, .sort 0, broken⟩, #[.share 0], #[], #[.succ .zero]⟩ + let (source, addr) := storeConst {} constant + match predictStandalone? source addr, convertAnonStandalone source addr constant .empty with + | .error predicted, .error actual table => predicted == actual && !table.exprs.isEmpty && + !table.univs.isEmpty && internKeysCoherent table && + !(ConversionRecipe.standalone source addr constant).exprs.isEmpty + | _, _ => false + +private def predictionCatalogSelection : Bool := + let (source, block) := storeMutsWithProjs {} (cacheBlock false) + let missing := Address.blake3 "source-prediction-missing".toUTF8 + let bad := Address.blake3 "source-prediction-corrupt".toUTF8 + let source := {source with consts := source.consts.insert bad (.ofConstant axiomA)} + match predictStandalone? source block, predictStandalone? source (defnProjAddr block 0), + predictStandalone? source missing, predictStandalone? source bad with + | .ok none, .ok none, .ok none, .error _ => true + | _, _, _, _ => false + +/-- A recursive call loads a standalone; a mixed block load and another +standalone follow. The source catalog is checked at each actual boundary. -/ +private def sourceAcrossRecursiveLoads (shape : Nat) (inferOnly surroundingScope : Bool) : Bool := + let (source, warmAddr) := polymorphicIdentity + let constant := if shape == 0 then axiomA else lazyCacheDependency 1 + let (source, coldAddr) := storeConst source constant + let (source, block) := storeMutsWithProjs source (cacheBlock true) + let (source, nextAddr) := storeConst source (lazyCacheDependency 3) + let warm := KExpr.mkConst (m := .anon) ⟨warmAddr, ()⟩ #[levelOne] + let cold := KExpr.mkConst (m := .anon) ⟨coldAddr, ()⟩ #[] + let sort := KExpr.mkSort (m := .anon) levelOne + let body := KExpr.mkLam () () sort (.mkLam () () (.mkVar 0 ()) + (.mkApp (.mkApp cold (.mkVar 1 ())) (.mkVar 0 ()))) + let term := if shape == 0 then KExpr.mkAll () () cold cold else if shape == 1 then body + else KExpr.mkApp (.mkApp (.mkConst ⟨warmAddr, ()⟩ #[levelTwo]) identityType) body + let action : RecM .anon Bool := do + let expected ← warmBothCaches warm + let key ← TcM.inferKey warm + let initial ← get + let keeps (state : TcState .anon) := sourceCatalogMatches source state.env && + internKeysCoherent state.env.intern && warmSlotsRetained key initial state + RecM.withLctxScope do + if surroundingScope then + let _ ← TcM.openBinder () () sort (.mkVar 0 ()) + pure () + let active ← get + let result ← RecM.inferCall term + let recursive ← get + let _ ← liftM (TcM.getConst (m := .anon) ⟨recrProjAddr block 1, ()⟩) + let mixed ← get + let next ← RecM.inferCall (.mkConst ⟨nextAddr, ()⟩ #[]) + let successor ← get + let replay ← RecM.inferCall term + let reused ← RecM.inferCall warm + let final ← get + return sourceOwnershipCheck source && keeps initial && keeps active && keeps recursive && + keeps mixed && keeps successor && keeps final && + sameSourceExpr result (if shape == 0 then sort else identityType) && + sameSourceExpr next identityType && sameSourceExpr replay result && sameSourceExpr reused expected && + (active.env.get? ⟨coldAddr, ()⟩).isNone && (recursive.env.get? ⟨coldAddr, ()⟩).isSome && + (mixed.env.get? ⟨nextAddr, ()⟩).isNone && (successor.env.get? ⟨nextAddr, ()⟩).isSome && + mixed.env.blocks.contains ⟨block, ()⟩ && recursive.lctx.size == active.lctx.size && + successor.lctx.size == active.lctx.size && final.env.nextFVarId == successor.env.nextFVarId && + final.env.intern.exprs.size == successor.env.intern.exprs.size && + final.env.intern.univs.size == successor.env.intern.univs.size + match TcM.runRec action {TcState.newLazyAnon source with inferOnly, stats := true} with + | .ok passed after => passed && sourceCatalogMatches source after.env && after.lctx.size == 0 + | .error _ _ => false + +private def sourceAgreementCases : TestSeq := + test "source prediction: shared axiom matches cold/warm conversion and lazy publication" (predictionShared 0) + ++ test "source prediction: shared definition body matches complete expected fields" (predictionShared 1) + ++ test "source prediction: recursor rules preserve sharing and distinct field counts" (predictionShared 2) + ++ test "source prediction: quotient fields match actual lazy loading" (predictionShared 3) + ++ test "source prediction: every expression form, normalized universes, blobs, and hints agree" + predictionRichDefinition + ++ test "source prediction: recursor flags, counts, block identity, and self references agree" + predictionRecursorFields + ++ test "source prediction: equal hashes do not hide different expression annotations" + (predictionCollisionBoundary false) + ++ test "source prediction: equal hashes do not hide different universe trees" + (predictionCollisionBoundary true) + ++ test "source prediction: missing share retains the same error and partial intern state" (predictionFailure 0) + ++ test "source prediction: missing universe retains the same error and partial intern state" (predictionFailure 1) + ++ test "source prediction: missing reference retains the same error and partial intern state" (predictionFailure 2) + ++ test "source prediction: missing recursive member retains the same error and partial state" (predictionFailure 3) + ++ test "source prediction: cyclic sharing returns the same bounded error" (predictionFailure 4) + ++ test "source prediction: blocks, projections, missing addresses, and corrupt source remain distinct" + predictionCatalogSelection + ++ test "source agreement: forall inference retains readings through mixed and later standalone loads" + (sourceAcrossRecursiveLoads 0 false false) + ++ test "source agreement: inference-only forall retains readings under an outer scope" + (sourceAcrossRecursiveLoads 0 true true) + ++ test "source agreement: lambda opening and closing retain readings for subsequent loading" + (sourceAcrossRecursiveLoads 1 false false) + ++ test "source agreement: lambda inference retains readings under an outer scope" + (sourceAcrossRecursiveLoads 1 false true) + ++ test "source agreement: application retains readings for subsequent mixed and standalone loading" + (sourceAcrossRecursiveLoads 2 false false) + ++ test "source agreement: application replay and surrounding scope retain the catalog" + (sourceAcrossRecursiveLoads 2 false true) + public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, - lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases] + lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, + sourceAgreementCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 0bdffc188..ad76366f7 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -181,6 +181,27 @@ increasing sequence of strongly inaccessible cardinals. another constant call that loads a new block. Recursive traces and finite walker data remain explicit. The result covers successful inference in the supported fragment; the general checker-state proof remains open. +- `ConversionRecipe.run_predict` connects source-only prediction to actual + intern operations on success and failure. The recipe preserves source + resolution, sharing caches, universe normalization, every expression form, + and all four standalone declaration converters. Its finite candidate lists + supply collision domains together with the initial intern table; no global + hash-injectivity assumption is required. Correspondence proofs equate each + recipe with the bounded production converter. `predictStandalone?` verifies + the source and selects standalone declarations, retaining conversion errors. + It is an optional prediction interface and changes no production loading. +- `SourceStateInvariant` adds standalone source agreement to ownership and + intern coherence. It starts empty after the finite source check and survives + actual lookup on both outcomes. Block loads preserve the catalog because + ownership separates their emitted keys. `OwnedInferenceTrace.preservesSource` + carries the combined invariant through supported recursive calls, with finite + conversion data at constant misses. `StandaloneModelBinding` reads a predicted + declaration's type in an already admitted model entry. Its actual lookup + derives the type reading, arity, and post-load coherence; + `ScopedConstantInferenceSupport.ofSource` and `infer_const_source_sound` use + these derived facts. Static source/model bindings, finite collision and level + data, and operational traces remain explicit. This catalog does not interpret + mutual members or prove declaration admission. - `checkEnvAnon_atomic_preserves_model` connects a supported production environment run to model extension. `checkEnvAnon_atomic_no_false` excludes a declaration at an axiom type interpreted as empty, including False. @@ -240,7 +261,10 @@ def useF (x : T.{1}) : T.{1} := f.{1} x A hit checks the loaded declaration, universe arity, finite level resources, and equality of the cached value with pure substitution of its type. Its arity agreement is explicit because no runtime arity guard executes. - A miss's lookup agrees with an already admitted type and universe count. A specialization supplies + A miss's lookup agrees with an already admitted type and universe count. + For a standalone, a static `StandaloneModelBinding` and the maintained source + invariant derive this lookup agreement from the source prediction. + A specialization supplies closed universe arguments and finite interning/substitution resources at the actual post-lookup state. The occurrence annotations on the declared type agree with the substituted entry's annotations. These are structural @@ -263,8 +287,9 @@ def useF (x : T.{1}) : T.{1} := f.{1} x application and binder execution traces and additionally follows lambda-domain inference. Constant misses use already-loaded declarations or verified standalone/block loading, with finite universe-walker resources after lookup; - applications use full mode and hash conversion. The new dependency's semantic - source agreement and finite collision/level resources remain explicit. + applications use full mode and hash conversion. The general interface keeps + dependency agreement explicit; the standalone source interface derives its + mutable lookup facts from a static binding and finite conversion data. `OwnedLazySupport` derives block compatibility from a source ownership check and an invariant established at initialization and retained by lookup and supported successful recursive calls. Arbitrary partially loaded states can @@ -273,6 +298,8 @@ def useF (x : T.{1}) : T.{1} := f.{1} x and coherence after applications, foralls, and full-mode lambdas, using one initial state resource and finite data for their actual walkers. It also reconstructs the cache-frame trace without per-leaf invariant premises. + `SourceStateInvariant` additionally retains agreement with source-only + standalone predictions through these calls and subsequent block loads. Initial coherence holds for `TcState.newLazyAnon`. Preservation by every checker operation, preservation at written keys, and automatic trace construction remain open. @@ -293,7 +320,8 @@ def useF (x : T.{1}) : T.{1} := f.{1} x Their witnesses retain the actual recursive calls, context preservation during function inference, and finite substitution resources. Function spines start with locals or admitted constants. Polymorphic constants supply - lazy-lookup agreement on misses or loaded-declaration agreement on hits, a + lazy-lookup agreement on misses (derived from source for bound standalones) + or loaded-declaration agreement on hits, a closed scoped reading of the selected entry's type, and finite universe-substitution resources. Their result type is fixed by a pure `readInstantiatedType?` check with the substituted occurrence @@ -371,7 +399,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 278 exact theorem boundaries. The production +The consistency target checks 316 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -441,6 +469,15 @@ forall, and lambda inference that loads one block, followed by a constant call that loads another. They retain both warm partitions, outer local scopes, and the actual statistics updates; replaying the recursive cache hit changes neither intern tables nor fresh-local allocation. +Source-prediction regressions compare complete anonymous declarations through +cold conversion, warm conversion, and actual lazy publication. They cover +expression annotations, universe trees and normalization, every expression +form, literal blobs, reducibility hints, recursor fields and rules, invalid +indices, cyclic sharing, and catalog selection. Deliberately conflicting intern +entries with equal hashes demonstrate why finite collision freedom is needed. +Recursive source-agreement cases retain the catalog through applications and +binders, a mixed block load, another standalone load, and cache replay under +an outer scope. The unit suite contains 548 checks. ## Certified host adapters @@ -472,6 +509,8 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Cache invariants and sort cache typing | [`Consistency/InferenceCache.lean`](../Ix/Kernel/Verify/Consistency/InferenceCache.lean), [`SortCache.lean`](../Ix/Kernel/Verify/Consistency/SortCache.lean) | | Recursive cache preservation and witness reuse | [`Consistency/RecursiveCache.lean`](../Ix/Kernel/Verify/Consistency/RecursiveCache.lean) | | Ownership and intern coherence through recursive inference | [`Consistency/RecursiveState.lean`](../Ix/Kernel/Verify/Consistency/RecursiveState.lean) | +| Exact source conversion and finite candidate inventories | [`SourceConversion.lean`](../Ix/Kernel/SourceConversion.lean), [`Consistency/ConversionRecipe.lean`](../Ix/Kernel/Verify/Consistency/ConversionRecipe.lean) | +| Standalone source/model agreement through lookup and inference | [`Consistency/SourceAgreement.lean`](../Ix/Kernel/Verify/Consistency/SourceAgreement.lean) | | Verified standalone lazy loading and cache frames | [`Consistency/LazyCache.lean`](../Ix/Kernel/Verify/Consistency/LazyCache.lean) | | Mutual-block publication and verified lookup frames | [`Consistency/BlockCache.lean`](../Ix/Kernel/Verify/Consistency/BlockCache.lean) | | Intern coherence through conversion and lazy loading | [`Consistency/IngressCoherence.lean`](../Ix/Kernel/Verify/Consistency/IngressCoherence.lean) | diff --git a/docs/theory.md b/docs/theory.md index b59bf6017..e572540a2 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -63,8 +63,19 @@ table; finite collision, construction, and size data remain explicit for the actual substitution, opening, and closing walkers. The result preserves earlier cache witnesses outside its computed writes and supplies the state resource for a later constant call, including another block load. -Initial cache agreement, general source/state invariants, finite collision/level -resources, trace construction, and preservation at written keys remain obligations. +Source-only conversion now predicts complete standalone declarations, with +finite inventories of the proposed intern nodes. Correspondence with actual +conversion is proved under collision freedom on the initial table and these +candidates, including errors. Agreement of loaded standalones with this source +catalog starts empty and survives lookup on both outcomes and successful +supported recursive inference. Source ownership protects these entries from +block publication. A static binding reads a predicted declaration's type in +an admitted model entry; actual constant lookup derives its type reading, +arity, and coherence. Thus this path needs no new post-load reading witness. +The static bindings still select already admitted entries. General declaration +admission, mutual-member interpretations, initial semantic cache agreement, +finite collision/level resources, trace construction, and preservation at +written keys remain obligations. Full checker consistency and compiler/backend refinement remain open. From 0914b698e49dd060f1247a2b31d553329e925058 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 21:55:20 -0400 Subject: [PATCH 20/63] Derive source cache agreement from execution histories --- Ix/Kernel/Verify/Consistency.lean | 15 +- Ix/Kernel/Verify/Consistency/Audit.lean | 29 +- Ix/Kernel/Verify/Consistency/SourceCache.lean | 621 ++++++++++++++++++ Tests/Ix/Kernel/Consistency.lean | 145 +++- docs/kernel-verification.md | 35 +- docs/theory.md | 13 +- 6 files changed, 843 insertions(+), 15 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/SourceCache.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index b46a88bbf..5cea1fc4e 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -29,6 +29,7 @@ import Ix.Kernel.Verify.Consistency.RecursiveCache import Ix.Kernel.Verify.Consistency.RecursiveState import Ix.Kernel.Verify.Consistency.ConversionRecipe import Ix.Kernel.Verify.Consistency.SourceAgreement +import Ix.Kernel.Verify.Consistency.SourceCache import Ix.Kernel.Verify.Consistency.Production import Ix.Kernel.Verify.Consistency.Environment import Ix.Kernel.Verify.Consistency.Audit @@ -104,8 +105,16 @@ survives verified lookup on both outcomes, and is preserved by supported recursive inference. Source ownership protects its keys during block loads. A static model binding reads the predicted type; actual constant lookup then derives its type reading, arity, and coherence without post-load reading premises. -Model admission, mutual-member interpretations, initial semantic cache agreement, -finite execution resources, trace construction, and preservation inside the -footprint remain obligations. +A finite catalog of closed sorts and source constant instances now establishes +both cache partitions' agreement and loaded-declaration coverage from the empty +state. Actual recursive inference preserves every catalog entry, including +writes at those keys. Finite input collision domains prevent other syntax +forms from writing them. Execution histories also include lookup errors, +policy changes, binder scopes, and cache clearing. Constant and sort leaves +derive their hit/miss interfaces from this invariant, and constant typing +follows from the source binding and history without fresh cache witnesses. +Model admission, mutual-member interpretations, general semantic cache +invariants, finite execution resources, and automatic trace construction remain +obligations. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 8cb997c60..798b46f7d 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -9,6 +9,7 @@ import Ix.Kernel.Verify.Consistency.Environment import Ix.Kernel.Verify.Consistency.RecursiveCache import Ix.Kernel.Verify.Consistency.RecursiveState import Ix.Kernel.Verify.Consistency.SourceAgreement +import Ix.Kernel.Verify.Consistency.SourceCache import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -91,7 +92,7 @@ private def cacheMapRoots : Array Lean.Name := #[ ] private def cacheKeyRoots : Array Lean.Name := #[ - ``inferKey_closed, ``inferKey_policy, ``inferKey_environment, + ``inferKey_closed, ``inferKey_policy, ``inferKey_environment, ``inferKey_address, ``InferenceCacheHit.key_closed, ``observeInferenceCache, ``InferenceCacheAgreement.selected, ``InferenceCacheHit.transport, ``PreservesInferenceCache.inferKey, ``PreservesInferenceCache.openBinder, @@ -179,6 +180,25 @@ private def sourceAgreementRoots : Array Lean.Name := #[ ``OwnedInferenceTrace.frameSource ] +private def sourceCacheRoots : Array Lean.Name := #[ + ``SourceCacheRequest.term, ``SourceCacheRequest.result, ``SourceCacheRequest.key, + ``SourceCacheRequest.closed, ``SourceCacheRequest.Loaded, ``SourceCacheRequest.Loaded.frame, + ``SourceCacheRequest.Loaded.ofMap, ``SourceCacheEntry.frame, ``SourceCacheAgreement, + ``SourceCacheAgreement.ofMaps, ``SourceCacheAgreement.afterInferKey, + ``SourceCacheAgreement.openBinder, ``SourceCacheAgreement.getConst, ``SourceCacheAgreement.write, + ``SourceCacheKeyData, ``SourceCacheKeyData.same, ``OwnedInferenceTrace.CacheData, + ``OwnedInferenceTrace.preservesCache, ``OwnedInferenceTrace.preservesSourceCache, + ``SourceCacheInvariant.getConst, ``SourceCacheInvariant.policy, ``SourceCacheInvariant.truncate, + ``SourceCacheInvariant.openBinder, ``SourceCacheInvariant.clearReductionCaches, + ``StandaloneModelBinding.cacheRequest, ``CachedConstantInferenceSupport.ofSourceCache, + ``BinderInference.constFromSourceCache, ``BinderInference.sortFromSourceCache +] + +private def sourceCacheInitialRoots : Array Lean.Name := #[ + ``SourceCacheAgreement.empty, ``SourceCacheInvariant.ofCheckedSource, + ``SourceCacheHistory.invariant, ``infer_const_history_sound +] + private def productionRoots : Array Lean.Name := #[ ``StandalonePrefix.member_success, ``definition_body_trace, ``AtomicDefinitionRun.sound, ``AtomicDefinitionRun.no_self_alias, @@ -295,7 +315,10 @@ def roots : Array RootAllowance := #[ { root := ``Theory.Model.CheckingClaim.typing, standardAxioms := standard }, { root := ``Theory.Model.CheckingClaim.typingSort, standardAxioms := standard }, { root := ``Theory.Model.CheckingClaim.lam, standardAxioms := standard } -] ++ conversionUniverseRoots.map (fun root => { +] ++ sourceCacheInitialRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative, nameNative], + forbiddenDependencies := forbiddenProduction +}) ++ conversionUniverseRoots.map (fun root => { root, standardAxioms := standard, nativeAxioms := #[levelNative], forbiddenDependencies := forbiddenProduction }) ++ (scopedRoots ++ cacheFrameRoots).map (fun root => { @@ -306,7 +329,7 @@ def roots : Array RootAllowance := #[ root, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction }) ++ (atomicRoots ++ instantiationRoots ++ recursiveCacheRoots ++ lazyCacheRoots ++ - ownedLoaderRoots ++ recursiveStateRoots ++ sourceAgreementRoots).map (fun root => { + ownedLoaderRoots ++ recursiveStateRoots ++ sourceAgreementRoots ++ sourceCacheRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) ++ productionRoots.map (fun root => { diff --git a/Ix/Kernel/Verify/Consistency/SourceCache.lean b/Ix/Kernel/Verify/Consistency/SourceCache.lean new file mode 100644 index 000000000..05c715340 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SourceCache.lean @@ -0,0 +1,621 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.SourceAgreement + +/-! +# Inference cache agreement established from source + +A finite catalog selects closed sort and standalone-constant instances. Its +expected types come from source prediction and pure universe substitution. +Both inference partitions start empty and retain this agreement through actual +recursive calls, including writes at catalog keys. Later hits need no separate +observation asserting their type or the presence of a loaded declaration. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- A source request records syntax, arity, and a pure conversion result. +It contains no inference run, cache observation, or semantic typing premise. -/ +inductive SourceCacheRequest (source : Ixon.Env) where + | sort (level : KUniv .anon) + | const (id : KId .anon) (arguments : Array (KUniv .anon)) + (declaration : KConst .anon) (result : KExpr .anon) + (predicted : predictStandalone? source id.addr = .ok (some declaration)) + (arity : declaration.lvls.toNat = arguments.size) + (substitution : KExpr.instantiateUnivParamsSpec declaration.ty arguments = .ok result) + +def SourceCacheRequest.term {source : Ixon.Env} : SourceCacheRequest source → KExpr .anon + | .sort level => .mkSort level + | .const id arguments .. => .mkConst id arguments + +def SourceCacheRequest.result {source : Ixon.Env} : SourceCacheRequest source → KExpr .anon + | .sort level => .mkSort (.mkSucc level) + | .const _ _ _ result .. => result + +def SourceCacheRequest.key {source : Ixon.Env} (request : SourceCacheRequest source) : + Address × Address := (request.term.addr, emptyCtxAddr) + +theorem SourceCacheRequest.closed {source : Ixon.Env} (request : SourceCacheRequest source) : + request.term.lbr = 0 := by cases request <;> rfl + +/-- Constant cache entries retain the source declaration that produced them. -/ +def SourceCacheRequest.Loaded {source : Ixon.Env} (request : SourceCacheRequest source) + (before : TcState .anon) : Prop := + match request with + | .sort _ => True + | .const id _ declaration .. => before.env.get? id = some declaration + +theorem SourceCacheRequest.Loaded.frame {source : Ixon.Env} {request : SourceCacheRequest source} + {before after : TcState .anon} (loaded : request.Loaded before) + (constants : ∀ id declaration, before.env.get? id = some declaration → + after.env.get? id = some declaration) : request.Loaded after := by + cases request with + | sort => trivial + | const => exact constants _ _ loaded + +theorem SourceCacheRequest.Loaded.ofMap {source : Ixon.Env} {request : SourceCacheRequest source} + {before after : TcState .anon} (loaded : request.Loaded before) + (constants : after.env.consts = before.env.consts) : request.Loaded after := + loaded.frame (fun _ _ found => by simpa only [KEnv.get?, constants] using found) + +structure SourceCacheEntry {source : Ixon.Env} (request : SourceCacheRequest source) + (before : TcState .anon) : Prop where + correct : InferenceCacheAgreement before request.key request.result + loaded : ∀ cached, before.env.inferCache[request.key]? = some cached ∨ + before.env.inferOnlyCache[request.key]? = some cached → request.Loaded before + +theorem SourceCacheEntry.frame {source : Ixon.Env} {request : SourceCacheRequest source} + {before after : TcState .anon} (entry : SourceCacheEntry request before) + (frame : InferenceCacheFrame request.key before after) : SourceCacheEntry request after := by + refine ⟨entry.correct.frame frame, ?_⟩ + intro cached present + have old : before.env.inferCache[request.key]? = some cached ∨ + before.env.inferOnlyCache[request.key]? = some cached := by + simpa only [frame.full, frame.only] using present + exact (entry.loaded cached old).frame frame.constants + +/-- Agreement includes both partitions, even when only one is currently eligible. -/ +def SourceCacheAgreement {source : Ixon.Env} (catalog : List (SourceCacheRequest source)) + (before : TcState .anon) : Prop := + ∀ request ∈ catalog, SourceCacheEntry request before + +theorem SourceCacheAgreement.empty (source : Ixon.Env) (catalog : List (SourceCacheRequest source)) : + SourceCacheAgreement catalog (TcState.newLazyAnon source) := by + intro request member + constructor + · constructor <;> intro cached found <;> simp [TcState.newLazyAnon] at found + · intro cached found + simp [TcState.newLazyAnon] at found + +theorem SourceCacheAgreement.ofMaps {source : Ixon.Env} {catalog : List (SourceCacheRequest source)} + {before after : TcState .anon} (agreement : SourceCacheAgreement catalog before) + (full : after.env.inferCache = before.env.inferCache) + (only : after.env.inferOnlyCache = before.env.inferOnlyCache) + (constants : after.env.consts = before.env.consts) : + SourceCacheAgreement catalog after := by + intro request member + exact (agreement request member).frame + (.of_eq (by rw [full]) (by rw [only]) constants) + +theorem SourceCacheAgreement.afterInferKey {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {before after : TcState .anon} + {term : KExpr .anon} {key : Address × Address} + (agreement : SourceCacheAgreement catalog before) + (run : TcM.inferKey term before = .ok key after) : SourceCacheAgreement catalog after := + agreement.ofMaps (congrArg KEnv.inferCache (inferKey_environment run)) + (congrArg KEnv.inferOnlyCache (inferKey_environment run)) + (congrArg KEnv.consts (inferKey_environment run)) + +theorem SourceCacheAgreement.openBinder {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {before after : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body opened : KExpr .anon} {fresh : FVarId} + (agreement : SourceCacheAgreement catalog before) + (run : TcM.openBinder name bi domain body before = .ok (opened, fresh) after) : + SourceCacheAgreement catalog after := by + intro request member + apply (agreement request member).frame + have frame := PreservesInferenceCache.openBinder request.key name bi domain body before + simpa only [run] using frame + +theorem SourceCacheAgreement.getConst {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {before : TcState .anon} {id : KId .anon} + (agreement : SourceCacheAgreement catalog before) (valid : SourceStateInvariant source before) : + match TcM.getConst id before with + | .ok _ after | .error _ after => SourceCacheAgreement catalog after := by + have frame := getConst_owned (id := id) valid.state.owned + cases run : TcM.getConst id before <;> rw [run] at frame <;> + exact fun request member => (agreement request member).frame (frame.1.cache request.key) + +/-- A write at a catalog key must contain its predicted type. Other writes +retain agreement without constraining their values. -/ +theorem SourceCacheAgreement.write {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {before after : TcState .anon} + {key : Address × Address} {result : KExpr .anon} {policy : Bool} {methods : Methods .anon} + (agreement : SourceCacheAgreement catalog before) + (correct : ∀ request ∈ catalog, key = request.key → + result = request.result ∧ request.Loaded before) + (run : RecM.cacheInferResult policy key result methods before = .ok () after) : + SourceCacheAgreement catalog after := by + intro request member + by_cases same : key = request.key + · obtain ⟨resultEq, loaded⟩ := correct request member same + subst key result + refine ⟨(agreement request member).correct.write run, ?_⟩ + intro cached present + apply loaded.ofMap + rw [cacheInferResult_eq] at run + cases policy <;> cases run <;> rfl + · apply (agreement request member).frame + have frame := PreservesInferenceCache.write_other same policy result methods before + simpa only [run] using frame + +/-- The key always stores the actual input address, even when its context +component required memoization. -/ +theorem inferKey_address {term : KExpr .anon} {before after : TcState .anon} + {key : Address × Address} (run : TcM.inferKey term before = .ok key after) : + key.1 = term.addr := by + unfold TcM.inferKey at run + change EStateM.bind (TcM.ctxAddrForLbr term.lbr) _ before = _ at run + rw [EStateM.bind] at run + cases computed : TcM.ctxAddrForLbr term.lbr before <;> rw [computed] at run + · cases run; rfl + · contradiction + +/-- A finite collision domain consists only of one executed input and the +catalog's inputs. It prevents another syntax form from writing a catalog key. -/ +def SourceCacheKeyData {source : Ixon.Env} (catalog : List (SourceCacheRequest source)) + (term : KExpr .anon) : Prop := + KExpr.CollisionFree fun candidate => candidate = term ∨ + ∃ request ∈ catalog, candidate = request.term + +theorem SourceCacheKeyData.same {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {term : KExpr .anon} + {before after : TcState .anon} {key : Address × Address} + (data : SourceCacheKeyData catalog term) {request : SourceCacheRequest source} + (member : request ∈ catalog) (run : TcM.inferKey term before = .ok key after) + (same : key = request.key) : term = request.term := by + have addr : term.addr = request.term.addr := + (inferKey_address run).symm.trans (congrArg Prod.fst same) + simpa only [KExpr.eraseMeta_anon] using data (Or.inl rfl) (Or.inr ⟨request, member, rfl⟩) addr + +private theorem request_sort {source : Ixon.Env} {request : SourceCacheRequest source} + {level : KUniv .anon} {info : ExprInfo .anon} (before : TcState .anon) + (equal : KExpr.sort level info = request.term) : + request.result = KExpr.mkSort (KUniv.mkSucc level) ∧ request.Loaded before := by + cases request with + | sort other => + change KExpr.sort level info = KExpr.sort other _ at equal + have levels := (KExpr.sort.inj equal).1 + subst other + exact ⟨rfl, trivial⟩ + | const id arguments declaration result predicted arity substitution => + change KExpr.sort level info = KExpr.const id arguments _ at equal + cases equal + +private theorem request_const {source : Ixon.Env} {request : SourceCacheRequest source} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} + (equal : KExpr.const id arguments info = request.term) : + ∃ declaration, predictStandalone? source id.addr = .ok (some declaration) ∧ + declaration.lvls.toNat = arguments.size ∧ + KExpr.instantiateUnivParamsSpec declaration.ty arguments = .ok request.result ∧ + (∀ before : TcState .anon, before.env.get? id = some declaration → request.Loaded before) := by + cases request with + | sort level => + change KExpr.const id arguments info = KExpr.sort level _ at equal + cases equal + | const other levels declaration result predicted arity substitution => + change KExpr.const id arguments info = KExpr.const other levels _ at equal + obtain ⟨rfl, rfl, _⟩ := KExpr.const.inj equal + exact ⟨declaration, predicted, arity, substitution, fun _ loaded => loaded⟩ + +/-- Recursive nodes contain only finite key-collision data and the proposed +sort result's intern domain. Constant walkers already supply their finite +substitution data in `OwnedInferenceTrace`, and source conversion data is +carried by `SourceData`. -/ +def OwnedInferenceTrace.CacheData {source : Ixon.Env} (catalog : List (SourceCacheRequest source)) + {fuel : Nat} {before : TcState .anon} {term : KExpr .anon} + (tree : OwnedInferenceTrace fuel before term) : Prop := + match tree with + | .hit _ => True + | @OwnedInferenceTrace.sort _ _ level _ miss => + SourceCacheKeyData catalog term ∧ KExpr.KeyCollisionFree fun candidate => + miss.keyed.env.intern.ExprSupport candidate ∨ candidate = KExpr.mkSort (KUniv.mkSucc level) + | .fvar _ | .const _ _ => SourceCacheKeyData catalog term + | .app _ _ _ _ _ first second | .forallE _ _ _ first second | + .lam _ _ _ _ _ first second => + SourceCacheKeyData catalog term ∧ first.CacheData catalog ∧ second.CacheData catalog + +private theorem source_cache_hash {source : Ixon.Env} {catalog : List (SourceCacheRequest source)} + {before after : TcState .anon} {left right : KExpr .anon} {methods : Methods .anon} + (agreement : SourceCacheAgreement catalog before) (equal : (left.addr == right.addr) = true) + (run : RecM.isDefEq left right methods before = .ok true after) : + SourceCacheAgreement catalog after := by + intro request member + exact (agreement request member).frame (isDefEq_hash_frame equal run request.key).1 + +private theorem source_cache_miss {source : Ixon.Env} {catalog : List (SourceCacheRequest source)} + {term result : KExpr .anon} {methods : Methods .anon} {before after : TcState .anon} + (valid : SourceStateInvariant source before) (agreement : SourceCacheAgreement catalog before) + (miss : UncachedInference before term) + (accepted : RecM.infer term methods before = .ok result after) + (uncached : ∀ middle, + RecM.inferUncached RecM.inferCall before.inferOnly term methods miss.keyed = .ok result middle → + SourceStateInvariant source miss.keyed → SourceCacheAgreement catalog miss.keyed → + SourceCacheAgreement catalog middle ∧ + (∀ request ∈ catalog, miss.key = request.key → + result = request.result ∧ request.Loaded middle)) : SourceCacheAgreement catalog after := by + obtain ⟨middle, run, written⟩ := infer_uncached_success_state miss accepted + obtain ⟨post, correct⟩ := uncached middle run (valid.afterInferKey miss.keyRun) + (agreement.afterInferKey miss.keyRun) + apply post.write (policy := before.inferOnly) (methods := methods) correct + rw [cacheInferResult_eq, written] + +/-- The complete recursive call preserves both partitions at every catalog +key, including keys written by its constant and sort leaves. Key collision +data rules out writes from a different syntax form at those same addresses. -/ +theorem OwnedInferenceTrace.preservesCache {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {fuel : Nat} + {before after : TcState .anon} {term result : KExpr .anon} + (tree : OwnedInferenceTrace fuel before term) (valid : SourceStateInvariant source before) + (agreement : SourceCacheAgreement catalog before) (sourceData : tree.SourceData source) + (cacheData : tree.CacheData catalog) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + SourceCacheAgreement catalog after := by + induction tree generalizing result after with + | hit hit => + rw [hit.run] at accepted + cases accepted + exact agreement.afterInferKey hit.keyRun + | @sort fuel before level info miss => + apply source_cache_miss valid agreement miss accepted + intro middle run keyed cache + change EStateM.Result.ok + (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).1 + {miss.keyed with env := {miss.keyed.env with intern := + (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).2}} = + .ok result middle at run + cases run + refine ⟨cache.ofMaps rfl rfl rfl, ?_⟩ + intro request member same + have equal := cacheData.1.same member miss.keyRun same + obtain ⟨typeEq, loaded⟩ := request_sort _ equal + refine ⟨?_, loaded⟩ + rw [typeEq] + simpa only [KExpr.eraseMeta_anon] using + miss.keyed.env.intern.internExpr_eraseMeta keyed.state.coherent cacheData.2 + | @fvar fuel before id name info miss => + apply source_cache_miss valid agreement miss accepted + intro middle run keyed cache + change (RecM.inferUncached RecM.inferCall before.inferOnly (.fvar id name info)).run + (methodsN fuel) miss.keyed = _ at run + unfold RecM.inferUncached at run + simp only [ReaderT.run_bind] at run + change EStateM.bind (get : TcM .anon (TcState .anon)) _ miss.keyed = _ at run + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) miss.keyed = + .ok miss.keyed miss.keyed from rfl] at run + dsimp only at run + split at run + · cases run + refine ⟨cache, ?_⟩ + intro request member same + have equal := cacheData.same member miss.keyRun same + cases request <;> cases equal + · contradiction + | @const fuel before id arguments info miss data => + apply source_cache_miss valid agreement miss accepted + intro middle run keyed cache + obtain ⟨concrete, loaded, got, _, instantiated⟩ := inferUncached_const_instantiation run + have post := keyed.getConst sourceData + have retained := cache.getConst (id := id) keyed + rw [got] at post retained + have walked := TcM.instantiateUnivParams_wf (data.faithful concrete loaded got) + (fun _ h => Or.inr h) ⟨post.state.coherent, fun _ h => Or.inl h⟩ + rw [instantiated] at walked + refine ⟨?_, ?_⟩ + · rw [walked.2.2.1] + exact retained.ofMaps rfl rfl rfl + · intro request member same + obtain ⟨declaration, predicted, _, substitution, load⟩ := + request_const (cacheData.same member miss.keyRun same) + have equal := post.agreement id concrete (getConst_result_loaded got) declaration predicted + have prediction := walked.2.1 + rw [equal] at prediction + refine ⟨Except.ok.inj (prediction.symm.trans substitution), ?_⟩ + rw [walked.2.2.1] + apply (load loaded (by rw [← equal]; exact getConst_result_loaded got)).ofMap rfl + | @app fuel before fn arg info full miss trace hashPath data functionTree argumentTree functionIH argumentIH => + apply source_cache_miss valid agreement miss accepted + intro middle run keyed cache + rw [full] at run + have functionState := functionTree.preservesSource keyed sourceData.1 trace.functionRun + have function := functionIH keyed cache sourceData.1 cacheData.2.1 trace.functionRun + have argument := argumentIH functionState function sourceData.2 cacheData.2.2 trace.argumentRun + have compared := source_cache_hash argument hashPath trace.compareRun + refine ⟨?_, ?_⟩ + · rw [(trace.output_state run).2] + exact compared.ofMaps rfl rfl rfl + · intro request member same + have equal := cacheData.1.same member miss.keyRun same + cases request <;> cases equal + | @forallE fuel before name bi domain body info miss trace opening domainTree bodyTree domainIH bodyIH => + apply source_cache_miss valid agreement miss accepted + intro middle run keyed cache + have domainState := domainTree.preservesSource keyed sourceData.1 trace.domainRun + have domainCache := domainIH keyed cache sourceData.1 cacheData.2.1 trace.domainRun + have bodyCache := bodyIH (domainState.openBinder opening trace.openRun) + (domainCache.openBinder trace.openRun) sourceData.2 cacheData.2.2 trace.bodyRun + refine ⟨?_, ?_⟩ + · rw [(trace.output_state run).2] + exact bodyCache.ofMaps rfl rfl rfl + · intro request member same + have equal := cacheData.1.same member miss.keyRun same + cases request <;> cases equal + | @lam fuel before name bi domain body info full miss trace opening closing domainTree bodyTree domainIH bodyIH => + apply source_cache_miss valid agreement miss accepted + intro middle run keyed cache + rw [full] at run + have domainState := domainTree.preservesSource keyed sourceData.1 trace.domainRun + have domainCache := domainIH keyed cache sourceData.1 cacheData.2.1 trace.domainRun + have bodyCache := bodyIH (domainState.openBinder opening trace.openRun) + (domainCache.openBinder trace.openRun) sourceData.2 cacheData.2.2 trace.bodyRun + refine ⟨?_, ?_⟩ + · rw [(trace.output_state run).2] + exact bodyCache.ofMaps rfl rfl rfl + · intro request member same + have equal := cacheData.1.same member miss.keyRun same + cases request <;> cases equal + +/-- Source, intern, and cache agreement form the resource returned to the +next call. The catalog is fixed independently of mutable cache contents. -/ +structure SourceCacheInvariant {source : Ixon.Env} (catalog : List (SourceCacheRequest source)) + (before : TcState .anon) : Prop where + state : SourceStateInvariant source before + cache : SourceCacheAgreement catalog before + +theorem SourceCacheInvariant.ofCheckedSource (source : Ixon.Env) + (catalog : List (SourceCacheRequest source)) (checked : sourceOwnershipCheck source = true) : + SourceCacheInvariant catalog (TcState.newLazyAnon source) := + ⟨.ofCheckedSource source checked, .empty source catalog⟩ + +theorem OwnedInferenceTrace.preservesSourceCache {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {fuel : Nat} + {before after : TcState .anon} {term result : KExpr .anon} + (tree : OwnedInferenceTrace fuel before term) (valid : SourceCacheInvariant catalog before) + (sourceData : tree.SourceData source) (cacheData : tree.CacheData catalog) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + SourceCacheInvariant catalog after := + ⟨tree.preservesSource valid.state sourceData accepted, + tree.preservesCache valid.state valid.cache sourceData cacheData accepted⟩ + +theorem SourceCacheInvariant.getConst {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {before : TcState .anon} {id : KId .anon} + (valid : SourceCacheInvariant catalog before) + (data : StandaloneConversionData source id.addr before.env) : + match TcM.getConst id before with + | .ok _ after | .error _ after => SourceCacheInvariant catalog after := by + have state := valid.state.getConst data + have cache := valid.cache.getConst (id := id) valid.state + cases run : TcM.getConst id before <;> rw [run] at state cache <;> exact ⟨state, cache⟩ + +theorem SourceCacheInvariant.policy {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {before : TcState .anon} + (valid : SourceCacheInvariant catalog before) (policy : Bool) : + SourceCacheInvariant catalog {before with inferOnly := policy} := + ⟨valid.state.ofMaps rfl rfl rfl valid.state.state.coherent, valid.cache.ofMaps rfl rfl rfl⟩ + +theorem SourceCacheInvariant.truncate {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {before : TcState .anon} + (valid : SourceCacheInvariant catalog before) (size : Nat) : + SourceCacheInvariant catalog {before with lctx := before.lctx.truncate size} := + ⟨valid.state.ofMaps rfl rfl rfl valid.state.state.coherent, valid.cache.ofMaps rfl rfl rfl⟩ + +theorem SourceCacheInvariant.openBinder {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {before after : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body opened : KExpr .anon} {fresh : FVarId} + (valid : SourceCacheInvariant catalog before) (data : BinderOpeningData before body) + (run : TcM.openBinder name bi domain body before = .ok (opened, fresh) after) : + SourceCacheInvariant catalog after := + ⟨valid.state.openBinder data run, valid.cache.openBinder run⟩ + +theorem SourceCacheInvariant.clearReductionCaches {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {before : TcState .anon} + (valid : SourceCacheInvariant catalog before) : + SourceCacheInvariant catalog {before with env := before.env.clearReductionCaches} := by + refine ⟨valid.state.ofMaps rfl rfl rfl valid.state.state.coherent, ?_⟩ + intro request member + refine ⟨.clearReductionCaches before request.key request.result, ?_⟩ + intro cached present + simp [KEnv.clearReductionCaches] at present + +/-- An execution history starts at the actual empty lazy state and records +operations and finite data, never a cache-correctness or typing assumption. -/ +inductive SourceCacheHistory {source : Ixon.Env} (catalog : List (SourceCacheRequest source)) : + TcState .anon → Prop + | initial (checked : sourceOwnershipCheck source = true) : + SourceCacheHistory catalog (TcState.newLazyAnon source) + | infer {fuel before after term result} + (previous : SourceCacheHistory catalog before) (tree : OwnedInferenceTrace fuel before term) + (sourceData : tree.SourceData source) (cacheData : tree.CacheData catalog) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + SourceCacheHistory catalog after + | getConst {before after id declaration} (previous : SourceCacheHistory catalog before) + (data : StandaloneConversionData source id.addr before.env) + (run : TcM.getConst id before = .ok declaration after) : SourceCacheHistory catalog after + | failedGetConst {before after id error} (previous : SourceCacheHistory catalog before) + (data : StandaloneConversionData source id.addr before.env) + (run : TcM.getConst id before = .error error after) : SourceCacheHistory catalog after + | policy {before} (previous : SourceCacheHistory catalog before) (policy : Bool) : + SourceCacheHistory catalog {before with inferOnly := policy} + | openBinder {before after name bi domain body opened fresh} + (previous : SourceCacheHistory catalog before) (data : BinderOpeningData before body) + (run : TcM.openBinder name bi domain body before = .ok (opened, fresh) after) : + SourceCacheHistory catalog after + | truncate {before} (previous : SourceCacheHistory catalog before) (size : Nat) : + SourceCacheHistory catalog {before with lctx := before.lctx.truncate size} + | clear {before} (previous : SourceCacheHistory catalog before) : + SourceCacheHistory catalog {before with env := before.env.clearReductionCaches} + +theorem SourceCacheHistory.invariant {source : Ixon.Env} {catalog : List (SourceCacheRequest source)} + {before : TcState .anon} (history : SourceCacheHistory catalog before) : + SourceCacheInvariant catalog before := by + induction history with + | initial checked => exact .ofCheckedSource source catalog checked + | infer previous tree sourceData cacheData accepted ih => + exact tree.preservesSourceCache ih sourceData cacheData accepted + | getConst previous data run ih | failedGetConst previous data run ih => + have post := ih.getConst data + simpa only [run] using post + | policy previous policy ih => exact ih.policy policy + | openBinder previous data run ih => exact ih.openBinder data run + | truncate previous size ih => exact ih.truncate size + | clear previous ih => exact ih.clearReductionCaches + +/-- Static source/model bindings supply catalog requests independently of +the checker's mutable state or its cache selection. -/ +def StandaloneModelBinding.cacheRequest {β : Type u} {source : Ixon.Env} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {id : KId .anon} {ref : ConstRef β} {entry : ConstantEntry β} + (binding : StandaloneModelBinding source resolve entries id ref entry) + (arguments : Array (KUniv .anon)) (result : KExpr .anon) + (arity : entry.universes = arguments.size) + (substitution : KExpr.instantiateUnivParamsSpec binding.constant.ty arguments = .ok result) : + SourceCacheRequest source := + .const id arguments binding.constant result binding.predicted (binding.universes.trans arity) substitution + +/-- A real selected hit obtains its complete earlier inference interface from +maintained catalog agreement. The loaded declaration and cached substitution +are derived here, including for the inference-only partition. -/ +def CachedConstantInferenceSupport.ofSourceCache {β : Type u} {source : Ixon.Env} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {id : KId .anon} {ref : ConstRef β} {entry : ConstantEntry β} + {arguments : Array (KUniv .anon)} {result : KExpr .anon} {type : AExpr β} + {catalog : List (SourceCacheRequest source)} {before : TcState .anon} + (binding : StandaloneModelBinding source resolve entries id ref entry) + (arity : entry.universes = arguments.size) + (substitution : KExpr.instantiateUnivParamsSpec binding.constant.ty arguments = .ok result) + (member : binding.cacheRequest arguments result arity substitution ∈ catalog) + (valid : SourceCacheInvariant catalog before) (wellFormed : entries.WF) + (instantiation : ConstantInstantiationData before id arguments) + (prediction : readInstantiatedType? resolve binding.constant.ty arguments = some type.erase) + (conditions : (entry.type.instL (arguments.toList.map readLevel)).annotations = type.annotations) + (hit : InferenceCacheHit before (.mkConst id arguments)) : + CachedConstantInferenceSupport resolve entries before id arguments + (KExpr.mkConst id arguments).info ref entry type := by + let request := binding.cacheRequest arguments result arity substitution + have requestKey : request.key = ((KExpr.mkConst id arguments).addr, emptyCtxAddr) := rfl + have requestResult : request.result = result := rfl + have cached := valid.cache request member + obtain ⟨keyEq, stateEq⟩ := hit.key_closed (by rfl) + have selected : before.env.inferCache[request.key]? = some hit.cached ∨ + before.env.inferOnlyCache[request.key]? = some hit.cached := by + rcases hit.selected with full | ⟨_, _, only⟩ + · exact .inl (by simpa only [stateEq, keyEq, requestKey] using full) + · exact .inr (by simpa only [stateEq, keyEq, requestKey] using only) + have loaded : before.env.get? id = some binding.constant := cached.loaded _ selected + have equal : hit.cached = result := InferenceCacheAgreement.selected hit + (by simpa only [stateEq, keyEq, requestKey, requestResult] using cached.correct) + exact { + hit + concrete := binding.constant + loaded := by rwa [stateEq] + resolved := binding.resolved + found := binding.found + count := binding.universes + arity := binding.universes.trans arity + scope := wellFormed.typeScope ref entry binding.found + reading := binding.reading + levels := instantiation.levels binding.constant before (getConst_loaded loaded) + substitution := by rw [equal]; exact substitution + prediction, conditions + } + +/-- Construct a constant inference leaf by observing production's actual +selection. Source bindings and the history-derived invariant supply both +miss and hit interfaces; no lookup reading or cache agreement is an input. -/ +def BinderInference.constFromSourceCache {β : Type u} {source : Ixon.Env} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {id : KId .anon} {ref : ConstRef β} {entry : ConstantEntry β} + {arguments : Array (KUniv .anon)} {result : KExpr .anon} {type : AExpr β} + {catalog : List (SourceCacheRequest source)} {before : TcState .anon} + (binding : StandaloneModelBinding source resolve entries id ref entry) + (arity : entry.universes = arguments.size) + (substitution : KExpr.instantiateUnivParamsSpec binding.constant.ty arguments = .ok result) + (member : binding.cacheRequest arguments result arity substitution ∈ catalog) + (valid : SourceCacheInvariant catalog before) (wellFormed : entries.WF) + (conversion : StandaloneConversionData source id.addr before.env) + (instantiation : ConstantInstantiationData before id arguments) + (prediction : readInstantiatedType? resolve binding.constant.ty arguments = some type.erase) + (conditions : (entry.type.instL (arguments.toList.map readLevel)).annotations = type.annotations) : + BinderInference resolve entries locals context fuel before (.mkConst id arguments) + (.const ref (arguments.toList.map readLevel)) type := by + rcases observeInferenceCache (inferKey_closed (term := KExpr.mkConst id arguments) rfl before) with + ⟨hit, _, _⟩ | ⟨miss, _, stateEq⟩ + · exact .cachedConst (.ofSourceCache binding arity substitution member valid wellFormed + instantiation prediction conditions hit) + · have keyed := valid.state.afterInferKey miss.keyRun + have convert : StandaloneConversionData source id.addr miss.keyed.env := by rwa [stateEq] + refine .polymorphic miss (.ofSource binding keyed wellFormed convert + (by rwa [stateEq])) ?_ conditions + intro concrete loaded got + have post := keyed.getConst convert + rw [got] at post + have equal := post.agreement id concrete (getConst_result_loaded got) binding.constant binding.predicted + rw [equal] + exact prediction + +def BinderInference.sortFromSourceCache {β : Type u} {source : Ixon.Env} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {catalog : List (SourceCacheRequest source)} {before : TcState .anon} {level : KUniv .anon} + (valid : SourceCacheInvariant catalog before) (member : SourceCacheRequest.sort level ∈ catalog) + (faithful : KExpr.KeyCollisionFree fun term => before.env.intern.ExprSupport term ∨ + term = KExpr.mkSort (KUniv.mkSucc level)) : + BinderInference resolve entries locals context fuel before (.mkSort level) + (.sort (readLevel level)) (.sort (.succ (readLevel level))) := + .sortOfAgreement rfl (valid.cache (.sort level) member).correct valid.state.state.coherent faithful + +/-- Actual constant inference is model-typed after a history beginning with +empty caches. Both cache partitions and the miss path are handled by actual +selection; no initial cache agreement or post-lookup reading is assumed. -/ +theorem infer_const_history_sound {β : Type u} {source : Ixon.Env} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {context : Model.Context β} {id : KId .anon} {ref : ConstRef β} {entry : ConstantEntry β} + {arguments : Array (KUniv .anon)} {expected result : KExpr .anon} {type : AExpr β} + {catalog : List (SourceCacheRequest source)} {before after : TcState .anon} + {methods : Methods .anon} + (binding : StandaloneModelBinding source resolve entries id ref entry) + (arity : entry.universes = arguments.size) + (substitution : KExpr.instantiateUnivParamsSpec binding.constant.ty arguments = .ok expected) + (member : binding.cacheRequest arguments expected arity substitution ∈ catalog) + (history : SourceCacheHistory catalog before) (wellFormed : entries.WF) + (conversion : StandaloneConversionData source id.addr before.env) + (instantiation : ConstantInstantiationData before id arguments) + (prediction : readInstantiatedType? resolve binding.constant.ty arguments = some type.erase) + (conditions : (entry.type.instL (arguments.toList.map readLevel)).annotations = type.annotations) + (accepted : RecM.infer (.mkConst id arguments) methods before = .ok result after) : + ModelTyping.{u,v} resolve entries context (.mkConst id arguments) result := by + have valid := history.invariant + rcases observeInferenceCache (inferKey_closed (term := KExpr.mkConst id arguments) rfl before) with + ⟨hit, _, _⟩ | ⟨miss, _, stateEq⟩ + · have support := CachedConstantInferenceSupport.ofSourceCache binding arity substitution member + valid wellFormed instantiation prediction conditions hit + obtain ⟨reading, typed⟩ := support.sound (locals := []) accepted + exact ⟨.const ref (arguments.toList.map readLevel), type, + by + change readExpr? resolve (.const id arguments (KExpr.mkConst id arguments).info) = _ + simp [readExpr?, binding.resolved, AExpr.erase], + readScopedExpr?_closed reading, typed⟩ + · exact infer_const_source_sound binding valid.state wellFormed miss conversion + (by rwa [stateEq]) accepted + +end Ix.Kernel.Consistency diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 7aa1dd183..8365675be 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -1764,10 +1764,153 @@ private def sourceAgreementCases : TestSeq := ++ test "source agreement: application replay and surrounding scope retain the catalog" (sourceAcrossRecursiveLoads 2 false true) +/-- Check the two concrete maps against a finite source catalog. A cached +constant must also retain the complete declaration that produced its type. -/ +private def sourceCacheMatches (source : Ixon.Env) + (catalog : Array (KExpr .anon × KExpr .anon)) (state : TcState .anon) : Bool := + catalog.all fun (term, expected) => + let loaded := match term with + | .sort .. => true + | .const id _ _ => match predictStandalone? source id.addr, state.env.get? id with + | .ok (some predicted), some actual => sameStandalone actual predicted + | _, _ => false + | _ => false + let valid := fun cached => match cached with + | none => true + | some actual => sameSourceExpr actual expected && loaded + let key := (term.addr, emptyCtxAddr) + valid state.env.inferCache[key]? && valid state.env.inferOnlyCache[key]? + +/-- Start with empty caches, write both policies at the same constant key, +infer recursively, load a block, retain partial failure state, load another +standalone, replay hits, then clear and repopulate the caches. -/ +private def sourceCacheHistory (shape : Nat) (outerScope finalOnly : Bool) : Bool := + let (source, warmAddr) := polymorphicIdentity + let (source, coldAddr) := storeConst source (if shape == 0 then axiomA else lazyCacheDependency 1) + let (source, block) := storeMutsWithProjs source (cacheBlock true) + let (source, nextAddr) := storeConst source (lazyCacheDependency 3) + let (source, badAddr) := storeConst source + ⟨.defn ⟨.defn, .safe, 0, .sort 0, .share 9⟩, #[], #[], + #[.succ (.succ (.succ (.succ (.succ .zero))))]⟩ + let warm := KExpr.mkConst (m := .anon) ⟨warmAddr, ()⟩ #[levelOne] + let warmTwo := KExpr.mkConst (m := .anon) ⟨warmAddr, ()⟩ #[levelTwo] + let twoType := KExpr.mkAll (m := .anon) () () (.mkSort levelTwo) + (.mkAll () () (.mkVar 0 ()) (.mkVar 1 ())) + let cold := KExpr.mkConst (m := .anon) ⟨coldAddr, ()⟩ #[] + let next := KExpr.mkConst (m := .anon) ⟨nextAddr, ()⟩ #[] + let prop := KExpr.mkSort (m := .anon) .mkZero + let sort := KExpr.mkSort (m := .anon) levelOne + let sortTwo := KExpr.mkSort (m := .anon) levelTwo + let coldType := if shape == 0 then sort else identityType + let catalog := #[(warm, identityType), (warmTwo, twoType), (cold, coldType), + (next, identityType), (prop, sort), (sort, sortTwo), (sortTwo, .mkSort (.mkSucc levelTwo))] + let body := KExpr.mkLam () () sort (.mkLam () () (.mkVar 0 ()) + (.mkApp (.mkApp cold (.mkVar 1 ())) (.mkVar 0 ()))) + let term := if shape == 0 then KExpr.mkAll () () cold cold else if shape == 1 then body + else KExpr.mkApp (.mkApp warmTwo identityType) body + let action : RecM .anon Bool := do + let initial ← get + modify fun state => {state with inferOnly := true} + let onlyType ← RecM.inferCall warm + let only ← get + modify fun state => {state with inferOnly := false} + let fullType ← RecM.inferCall warm + let full ← get + RecM.withLctxScope do + if outerScope then + let _ ← TcM.openBinder () () sort (.mkVar 0 ()) + pure () + modify fun state => {state with inferOnly := shape == 0 && finalOnly} + let active ← get + let result ← RecM.inferCall term + let recursive ← get + let _ ← liftM (TcM.getConst (m := .anon) ⟨recrProjAddr block 1, ()⟩) + let mixed ← get + let rejected ← try + let _ ← liftM (TcM.getConst (m := .anon) ⟨badAddr, ()⟩) + pure false + catch error => pure (((toString error).splitOn "invalid Share index 9").length > 1) + let failed ← get + let nextType ← RecM.inferCall next + let distinctType ← RecM.inferCall warmTwo + let successor ← get + let replay ← RecM.inferCall term + modify fun state => {state with inferOnly := true} + let reused ← RecM.inferCall warm + let final ← get + modify fun state => {state with env := state.env.clearReductionCaches, inferOnly := finalOnly} + let cleared ← get + let rebuilt ← RecM.inferCall warm + let after ← get + let key := (warm.addr, emptyCtxAddr) + return sourceOwnershipCheck source && initial.env.inferCache.isEmpty && + initial.env.inferOnlyCache.isEmpty && + #[initial, only, full, active, recursive, mixed, failed, successor, final, cleared, after].all + (fun state => sourceCacheMatches source catalog state && sourceCatalogMatches source state.env && + internKeysCoherent state.env.intern) && + only.env.inferCache[key]?.isNone && only.env.inferOnlyCache[key]?.isSome && + full.env.inferCache[key]?.isSome && full.env.inferOnlyCache[key]?.isSome && + sameSourceExpr onlyType identityType && sameSourceExpr fullType identityType && + sameSourceExpr result (if shape == 0 then sort else identityType) && + sameSourceExpr nextType identityType && sameSourceExpr distinctType twoType && + sameSourceExpr replay result && sameSourceExpr reused identityType && sameSourceExpr rebuilt identityType && + (active.env.get? ⟨coldAddr, ()⟩).isNone && (recursive.env.get? ⟨coldAddr, ()⟩).isSome && + rejected && failed.faultedAddrs.contains badAddr && + failed.env.intern.exprs.size > mixed.env.intern.exprs.size && + failed.env.consts.size == mixed.env.consts.size && mixed.env.blocks.contains ⟨block, ()⟩ && + successor.lctx.size == active.lctx.size && final.env.nextFVarId == successor.env.nextFVarId && + cleared.env.inferCache.isEmpty && cleared.env.inferOnlyCache.isEmpty && + cleared.env.consts.size == final.env.consts.size && + (if finalOnly then after.env.inferCache[key]?.isNone && after.env.inferOnlyCache[key]?.isSome + else after.env.inferCache[key]?.isSome && after.env.inferOnlyCache[key]?.isNone) + match TcM.runRec action {TcState.newLazyAnon source with stats := true} with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +private def sourceCacheMissingDeclaration : Bool := + let (source, addr) := polymorphicIdentity + let term := KExpr.mkConst (m := .anon) ⟨addr, ()⟩ #[levelOne] + let before := TcState.newLazyAnon source + let forged := {before with env := {before.env with + inferCache := before.env.inferCache.insert (term.addr, emptyCtxAddr) identityType}} + sourceCacheMatches source #[(term, identityType)] before && + !sourceCacheMatches source #[(term, identityType)] forged + +/-- A different input forged at the same key can overwrite a valid result. +The finite key domain rules out exactly this case in the preservation proof. -/ +private def sourceCacheForeignWrite : Bool := + let (source, addr) := polymorphicIdentity + let term := KExpr.mkConst (m := .anon) ⟨addr, ()⟩ #[levelOne] + let prop := KExpr.mkSort (m := .anon) .mkZero + let forged := KExpr.app term prop {(KExpr.mkApp term prop).info with addr := term.addr} + match TcM.infer forged (TcState.newLazyAnon source) with + | .ok result after => sameSourceExpr result (.mkAll () () prop prop) && + after.env.inferCache[(term.addr, emptyCtxAddr)]?.isSome && + !sourceCacheMatches source #[(term, identityType)] after + | .error _ _ => false + +private def sourceCacheCases : TestSeq := + test "source cache: empty/full/only caches survive forall, loading, failures, and clearing" + (sourceCacheHistory 0 false false) + ++ test "source cache: inference-only forall and repopulation retain an outer scope" + (sourceCacheHistory 0 true true) + ++ test "source cache: lambda leaves establish agreement at written catalog keys" + (sourceCacheHistory 1 false false) + ++ test "source cache: lambda history retains both policies and loaded dependencies" + (sourceCacheHistory 1 true true) + ++ test "source cache: application history preserves distinct universe instances" + (sourceCacheHistory 2 false false) + ++ test "source cache: application history survives scopes, replay, and clearing" + (sourceCacheHistory 2 true true) + ++ test "source cache: a correct cached type with a missing declaration violates coverage" + sourceCacheMissingDeclaration + ++ test "source cache: a forged same-key application violates source result agreement" + sourceCacheForeignWrite + public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, - sourceAgreementCases] + sourceAgreementCases, sourceCacheCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index ad76366f7..753545e26 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -202,6 +202,21 @@ increasing sequence of strongly inaccessible cardinals. these derived facts. Static source/model bindings, finite collision and level data, and operational traces remain explicit. This catalog does not interpret mutual members or prove declaration admission. +- `SourceCacheHistory.invariant` establishes cache agreement from the actual + empty lazy state for a finite catalog of closed sorts and standalone constant + instances. Each populated slot has its predicted type and retains the source + declaration that produced it. `OwnedInferenceTrace.preservesCache` follows + recursive writes at every catalog key, using finite input collision domains + to exclude writes by other syntax forms. Both cache partitions remain valid + across policy changes, binder scopes, block loading, lookup errors, and cache + clearing. `BinderInference.constFromSourceCache` and `sortFromSourceCache` + observe the real selection and derive the existing hit/miss interfaces. + `infer_const_history_sound` derives model typing after such a history without + initial cache agreement, a separate cache-hit type witness, or a new lookup + reading. The catalog, static source/model bindings, finite collision/level + data, and recursive execution traces remain explicit. General composite and + local cache typing, other checker operations, and declaration admission are + still outside this result. - `checkEnvAnon_atomic_preserves_model` connects a supported production environment run to model extension. `checkEnvAnon_atomic_no_false` excludes a declaration at an axiom type interpreted as empty, including False. @@ -275,7 +290,9 @@ def useF (x : T.{1}) : T.{1} := f.{1} x Closed constant witnesses can be transported through composed frames that preserve their key's entries and each previously loaded declaration. Actual sort and already-loaded constant inference provide frames for other keys. These - results reduce repeated witnesses; they do not yet derive initial agreement. + frame results reduce repeated witnesses. The source-cache history additionally + establishes agreement and loaded-declaration coverage for a finite catalog + from empty caches, then preserves it at the catalog's written keys. Verified lookup supplies an extension frame on every outcome, retaining partial intern progress on error. The installed callback must be the actual verified loader. Standalone registration uses freshness from the lookup miss; @@ -301,8 +318,9 @@ def useF (x : T.{1}) : T.{1} := f.{1} x `SourceStateInvariant` additionally retains agreement with source-only standalone predictions through these calls and subsequent block loads. Initial coherence holds for `TcState.newLazyAnon`. Preservation by every - checker operation, preservation at written keys, and automatic trace - construction remain open. + checker operation and automatic trace construction remain open. + Catalog constant and sort entries are preserved even when written by these + recursive calls. General semantic cache invariants remain open. The operational trace can frame any selected cache hit, while semantic typing of composite hits remains outside `BinderInference`. - Binder definitions supply finite inference trees for both the value and its @@ -399,7 +417,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 316 exact theorem boundaries. The production +The consistency target checks 349 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -477,7 +495,13 @@ indices, cyclic sharing, and catalog selection. Deliberately conflicting intern entries with equal hashes demonstrate why finite collision freedom is needed. Recursive source-agreement cases retain the catalog through applications and binders, a mixed block load, another standalone load, and cache replay under -an outer scope. The unit suite contains 548 checks. +an outer scope. +Source-cache histories start empty, populate both policies at the same key, +and check complete results and declaration coverage through recursive calls, +mixed block loads, partial lookup failures, distinct universe instances, scopes, +replay, clearing, and repopulation. Negative cases detect a correct cached type +with no loaded declaration and a forged application writing a constant's key. +The unit suite contains 556 checks. ## Certified host adapters @@ -511,6 +535,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Ownership and intern coherence through recursive inference | [`Consistency/RecursiveState.lean`](../Ix/Kernel/Verify/Consistency/RecursiveState.lean) | | Exact source conversion and finite candidate inventories | [`SourceConversion.lean`](../Ix/Kernel/SourceConversion.lean), [`Consistency/ConversionRecipe.lean`](../Ix/Kernel/Verify/Consistency/ConversionRecipe.lean) | | Standalone source/model agreement through lookup and inference | [`Consistency/SourceAgreement.lean`](../Ix/Kernel/Verify/Consistency/SourceAgreement.lean) | +| Source cache agreement from empty-state execution histories | [`Consistency/SourceCache.lean`](../Ix/Kernel/Verify/Consistency/SourceCache.lean) | | Verified standalone lazy loading and cache frames | [`Consistency/LazyCache.lean`](../Ix/Kernel/Verify/Consistency/LazyCache.lean) | | Mutual-block publication and verified lookup frames | [`Consistency/BlockCache.lean`](../Ix/Kernel/Verify/Consistency/BlockCache.lean) | | Intern coherence through conversion and lazy loading | [`Consistency/IngressCoherence.lean`](../Ix/Kernel/Verify/Consistency/IngressCoherence.lean) | diff --git a/docs/theory.md b/docs/theory.md index e572540a2..a31bd695d 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -72,10 +72,17 @@ supported recursive inference. Source ownership protects these entries from block publication. A static binding reads a predicted declaration's type in an admitted model entry; actual constant lookup derives its type reading, arity, and coherence. Thus this path needs no new post-load reading witness. +For a finite catalog of closed sorts and standalone constant instances, +execution histories now establish both inference partitions' agreement and +loaded-declaration coverage from empty caches. Recursive calls preserve these +entries even at written keys; finite collision domains separate other input +forms. Lookup errors, policy changes, binder scopes, and cache clearing retain +the invariant. Actual selection then constructs the constant and sort inference +interfaces, and constant typing follows from the source binding and history. The static bindings still select already admitted entries. General declaration -admission, mutual-member interpretations, initial semantic cache agreement, -finite collision/level resources, trace construction, and preservation at -written keys remain obligations. +admission, mutual-member interpretations, semantic cache invariants beyond this +catalog, finite collision/level resources, and automatic trace construction +remain obligations. Full checker consistency and compiler/backend refinement remain open. From c53453c586601174ce9186af0bac8ff311d512c7 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 22:19:18 -0400 Subject: [PATCH 21/63] Admit polymorphic definitions from production checking --- .../Verify/Check/ValidatorSoundness.lean | 32 ++--- Ix/Kernel/Verify/Consistency.lean | 15 ++- Ix/Kernel/Verify/Consistency/Audit.lean | 10 +- Ix/Kernel/Verify/Consistency/Environment.lean | 22 ++-- Ix/Kernel/Verify/Consistency/Production.lean | 122 +++++++++++++++--- Ix/Kernel/Verify/Consistency/Validation.lean | 112 ++++++++++++++++ Tests/Ix/Kernel/Consistency.lean | 91 ++++++++++++- docs/kernel-verification.md | 38 ++++-- docs/theory.md | 10 +- 9 files changed, 385 insertions(+), 67 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/Validation.lean diff --git a/Ix/Kernel/Verify/Check/ValidatorSoundness.lean b/Ix/Kernel/Verify/Check/ValidatorSoundness.lean index 259f94b5b..21b041e0d 100644 --- a/Ix/Kernel/Verify/Check/ValidatorSoundness.lean +++ b/Ix/Kernel/Verify/Check/ValidatorSoundness.lean @@ -340,7 +340,7 @@ theorem validateUnivParamsSeen_go_sound : hfrontier.insertAndExpand hdomain hcollision hlevelDomain cases level with | zero addr => - simp only [pure_bind] at hrun + simp only at hrun have hlocal' := hlocal.insert hdomain hcollision hlevelDomain (by trivial) exact UnivValidationPost.ofExpanded rfl <| @@ -350,7 +350,7 @@ theorem validateUnivParamsSeen_go_sound : (by simpa [KUniv.validationChildren, KUniv.addr] using hexpandedFrontier) methods state finalSeen after hrun | succ child addr => - simp only [pure_bind] at hrun + simp only at hrun have hlocal' := hlocal.insert hdomain hcollision hlevelDomain (by trivial) exact UnivValidationPost.ofExpanded rfl <| @@ -361,7 +361,7 @@ theorem validateUnivParamsSeen_go_sound : (by simpa [KUniv.validationChildren, KUniv.addr] using hexpandedFrontier) methods state finalSeen after hrun | max left right addr => - simp only [pure_bind] at hrun + simp only at hrun have hlocal' := hlocal.insert hdomain hcollision hlevelDomain (by trivial) exact UnivValidationPost.ofExpanded rfl <| @@ -372,7 +372,7 @@ theorem validateUnivParamsSeen_go_sound : (by simpa [KUniv.validationChildren, KUniv.addr] using hexpandedFrontier) methods state finalSeen after hrun | imax left right addr => - simp only [pure_bind] at hrun + simp only at hrun have hlocal' := hlocal.insert hdomain hcollision hlevelDomain (by trivial) exact UnivValidationPost.ofExpanded rfl <| @@ -383,7 +383,7 @@ theorem validateUnivParamsSeen_go_sound : (by simpa [KUniv.validationChildren, KUniv.addr] using hexpandedFrontier) methods state finalSeen after hrun | param idx name addr => - simp only [pure_bind] at hrun + simp only at hrun split at hrun · contradiction · rename_i hinRange @@ -995,7 +995,7 @@ theorem validateExprWellScoped_go_sound : ExprValidationPost.ofExpanded rfl hunivMono hpost⟩ cases expr with | var idx name info => - simp only [pure_bind] at hrun + simp only at hrun split at hrun · contradiction · rename_i hinRange @@ -1009,7 +1009,7 @@ theorem validateExprWellScoped_go_sound : KExpr.validationChildrenAt, KExpr.treeSize] · simpa [KExpr.validationChildrenAt] using hrun | fvar id name info => - simp only [pure_bind] at hrun + simp only at hrun apply finishFresh (afterUnivs := seenUnivs) (nextState := state) (by trivial) (AddressSetLE.refl seenUnivs) @@ -1018,7 +1018,7 @@ theorem validateExprWellScoped_go_sound : KExpr.validationChildrenAt, KExpr.treeSize] · simpa [KExpr.validationChildrenAt] using hrun | sort level info => - simp only [pure_bind] at hrun + simp only at hrun rw [ReaderT.run_bind, runTcBind] at hrun cases hvalidate : (validateUnivParamsSeen level bound seenUnivs).run @@ -1045,7 +1045,7 @@ theorem validateExprWellScoped_go_sound : KExpr.validationChildrenAt, KExpr.treeSize] · simpa [KExpr.validationChildrenAt] using hrun | const id levels info => - simp only [pure_bind] at hrun + simp only at hrun rw [ReaderT.run_bind, ReaderT.run_monadLift, runTcBind] at hrun cases hget : (monadLift (TcM.getConst id) : TcM .anon (KConst .anon)) @@ -1089,7 +1089,7 @@ theorem validateExprWellScoped_go_sound : KExpr.validationChildrenAt, KExpr.treeSize] · simpa [KExpr.validationChildrenAt] using hrun | app fn arg info => - simp only [pure_bind] at hrun + simp only at hrun apply finishFresh (afterUnivs := seenUnivs) (nextState := state) (by trivial) (AddressSetLE.refl seenUnivs) @@ -1099,7 +1099,7 @@ theorem validateExprWellScoped_go_sound : omega · simpa [KExpr.validationChildrenAt] using hrun | lam name bi type body info => - simp only [pure_bind] at hrun + simp only at hrun apply finishFresh (afterUnivs := seenUnivs) (nextState := state) (by trivial) (AddressSetLE.refl seenUnivs) @@ -1109,7 +1109,7 @@ theorem validateExprWellScoped_go_sound : omega · simpa [KExpr.validationChildrenAt] using hrun | all name bi type body info => - simp only [pure_bind] at hrun + simp only at hrun apply finishFresh (afterUnivs := seenUnivs) (nextState := state) (by trivial) (AddressSetLE.refl seenUnivs) @@ -1119,7 +1119,7 @@ theorem validateExprWellScoped_go_sound : omega · simpa [KExpr.validationChildrenAt] using hrun | letE name type value body nonDep info => - simp only [pure_bind] at hrun + simp only at hrun apply finishFresh (afterUnivs := seenUnivs) (nextState := state) (by trivial) (AddressSetLE.refl seenUnivs) @@ -1129,7 +1129,7 @@ theorem validateExprWellScoped_go_sound : omega · simpa [KExpr.validationChildrenAt] using hrun | prj id field value info => - simp only [pure_bind] at hrun + simp only at hrun rw [ReaderT.run_bind, ReaderT.run_monadLift, runTcBind] at hrun cases hhas : (monadLift (TcM.hasConst id) : TcM .anon Bool) state with @@ -1148,7 +1148,7 @@ theorem validateExprWellScoped_go_sound : KExpr.validationChildrenAt, KExpr.treeSize] · simpa [KExpr.validationChildrenAt] using hrun | nat value blob info => - simp only [pure_bind] at hrun + simp only at hrun apply finishFresh (afterUnivs := seenUnivs) (nextState := state) (by trivial) (AddressSetLE.refl seenUnivs) @@ -1157,7 +1157,7 @@ theorem validateExprWellScoped_go_sound : KExpr.validationChildrenAt, KExpr.treeSize] · simpa [KExpr.validationChildrenAt] using hrun | str value blob info => - simp only [pure_bind] at hrun + simp only at hrun apply finishFresh (afterUnivs := seenUnivs) (nextState := state) (by trivial) (AddressSetLE.refl seenUnivs) diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 5cea1fc4e..6bcfcbc5a 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -25,6 +25,7 @@ import Ix.Kernel.Verify.Consistency.Context import Ix.Kernel.Verify.Consistency.BinderOpening import Ix.Kernel.Verify.Consistency.Application import Ix.Kernel.Verify.Consistency.BinderInference +import Ix.Kernel.Verify.Consistency.Validation import Ix.Kernel.Verify.Consistency.RecursiveCache import Ix.Kernel.Verify.Consistency.RecursiveState import Ix.Kernel.Verify.Consistency.ConversionRecipe @@ -40,15 +41,19 @@ import Ix.Kernel.Verify.Consistency.Audit This library connects production kernel operations to `Ix.Theory`. Each transport keeps its representation, arithmetic, and dependency assumptions explicit. A production `checkEnvAnon` fragment preserves models of its -axiom set for monomorphic aliases, closed sorts, monomorphic specializations of -polymorphic constants, and closed function bodies built from sorts, locals, +axiom set for aliases, universe terms, instances of earlier constants, and +closed function bodies built from sorts, locals, polymorphic references, applications, dependent functions, and full-mode lambdas -under the stated execution resources. +under the stated execution resources. Definitions may declare their own +universe parameters; model entries retain the exact arity and interpretations +at every instance. Constant inference supports arbitrary readable entry types, using the actual universe-instantiation walker and explicit lookup and finite-support resources. The returned type's scope and references justify declaration admission. -The binder case also requires explicit syntactic scope and references to the -preceding interface. Its separate declared-type inference turns semantic checking +The binder case retains references to the preceding interface. Actual production +validation and the closed scoped reading derive source scope from finite +validation coverage and collision freedom. Auxiliary condition bounds remain +a separate syntax check. The declared-type inference from that same execution turns semantic checking into typing. Local cache hits agree with the actual declaration type. Constant hits agree with pure universe substitution of a loaded, admitted declaration; sort hits return the canonical successor sort. Application, forall, and lambda diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 798b46f7d..75e700a1b 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -49,7 +49,7 @@ private def atomicRoots : Array Lean.Name := #[ ``ForallInferenceTrace.output, ``LambdaInferenceTrace.output, ``BinderInference.sound, ``inferUncached_monomorphic_const_scoped, ``ApplicationInferenceTrace.output, ``BinderInference.soundWithSynthesis, ``BinderInference.synthesis, - ``DefinitionBodySupport.sound + ``DefinitionBodySupport.sound, ``DefinitionBodyTrace.scopes, ``DefinitionBodyTrace.binderSupport ] private def instantiationRoots : Array Lean.Name := #[ @@ -212,7 +212,7 @@ private def scopedRoots : Array Lean.Name := #[ ``localIndex?_mem, ``localIndex?_getElem, ``localIndex?_fresh, ``readScopedExpr?_closed, ``readScopedExpr?_weaken_closed, ``readScopedExpr?_eraseMeta, ``beq_readScopedExpr?, ``internExpr_readScopedExpr?, ``readScopedExpr?_push, - ``LocalContextReading.empty + ``LocalContextReading.empty, ``readScopedExpr?_scope, ``readScopedExpr?_annotated_scope ] private def contextRoots : Array Lean.Name := #[ @@ -314,7 +314,11 @@ def roots : Array RootAllowance := #[ { root := ``Theory.Model.TypingClaim.appChecking, standardAxioms := standard }, { root := ``Theory.Model.CheckingClaim.typing, standardAxioms := standard }, { root := ``Theory.Model.CheckingClaim.typingSort, standardAxioms := standard }, - { root := ``Theory.Model.CheckingClaim.lam, standardAxioms := standard } + { root := ``Theory.Model.CheckingClaim.lam, standardAxioms := standard }, + { root := ``ConditionsScoped, standardAxioms := #[``propext], + forbiddenDependencies := forbiddenProduction }, + { root := ``ConditionsScoped.scope, standardAxioms := #[``propext], + forbiddenDependencies := forbiddenProduction } ] ++ sourceCacheInitialRoots.map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative, nameNative], forbiddenDependencies := forbiddenProduction diff --git a/Ix/Kernel/Verify/Consistency/Environment.lean b/Ix/Kernel/Verify/Consistency/Environment.lean index 1456669ce..457cae183 100644 --- a/Ix/Kernel/Verify/Consistency/Environment.lean +++ b/Ix/Kernel/Verify/Consistency/Environment.lean @@ -10,7 +10,7 @@ import Ix.Theory.Model.Extension # Relative consistency of a production environment fragment The initial interface contains exactly the supplied axiom declarations. -Successful calls from the serial `checkEnvAnon` run add fresh monomorphic +Successful calls from the serial `checkEnvAnon` run add fresh universe-polymorphic definitions in dependency order. Each addition extends every model of the previous interface and preserves its existing interpretations. -/ @@ -92,21 +92,21 @@ theorem WorkPosition.check_success {env : Ixon.Env} {cfg : CheckCfg} (list_keeps_result cfg position.trailing _ appears) simp [failed] at contradiction -/-- A monomorphic definition entry, with its body and no additional laws or facts. -/ -def definitionEntry {β : Type u} (body type : AExpr β) : ConstantEntry β := - { universes := 0, type, body := some body } +/-- A definition entry with its exact universe arity and body, and no additional laws or facts. -/ +def definitionEntry {β : Type u} (universes : Nat) (body type : AExpr β) : ConstantEntry β := + { universes, type, body := some body } /-- Interpret a fresh definition's body to extend the preceding model. Body typing supplies hereditary validity of the declared type. -/ theorem extend_atomic_definition {β : Type u} [DecidableEq β] - {entries : Model.Environment β} {ref : ConstRef β} {body type : AExpr β} + {entries : Model.Environment β} {ref : ConstRef β} {body type : AExpr β} {universes : Nat} (wellFormed : entries.WF) (fresh : entries ref = none) - (bodyScope : body.Scope 0 0) + (bodyScope : body.Scope universes 0) (bodyRefs : body.ReferencesIn entries) (typeRefs : type.ReferencesIn entries) (typed : TypingClaim.{u,v} entries [] body type) {V : Type v} [SetTheory V] (constants : Assignment β V) (realizes : Realizes constants entries) : ∃ next : Assignment β V, - Realizes next (entries.insert ref (definitionEntry body type)) ∧ + Realizes next (entries.insert ref (definitionEntry universes body type)) ∧ Assignment.AgreesOn entries constants next := by let next := constants.insert ref (fun levels => interp constants levels (fun _ => empty) body) have agrees : Assignment.AgreesOn entries constants next := @@ -147,13 +147,13 @@ structure DefinitionSpec (β : Type u) where type : AExpr β def DefinitionSpec.entry {β : Type u} (spec : DefinitionSpec β) : ConstantEntry β := - definitionEntry spec.body spec.type + definitionEntry spec.input.universes.toNat spec.body spec.type private theorem insert_definition_wf {β : Type u} [DecidableEq β] - {entries : Model.Environment β} {ref : ConstRef β} {body type : AExpr β} - (wellFormed : entries.WF) (bodyScope : body.Scope 0 0) (typeScope : type.Scope 0 0) + {entries : Model.Environment β} {ref : ConstRef β} {body type : AExpr β} {universes : Nat} + (wellFormed : entries.WF) (bodyScope : body.Scope universes 0) (typeScope : type.Scope universes 0) (bodyRefs : body.ReferencesIn entries) (typeRefs : type.ReferencesIn entries) : - (entries.insert ref (definitionEntry body type)).WF := by + (entries.insert ref (definitionEntry universes body type)).WF := by apply wellFormed.insert typeScope · intro candidate present change some body = some candidate at present diff --git a/Ix/Kernel/Verify/Consistency/Production.lean b/Ix/Kernel/Verify/Consistency/Production.lean index 894a75e71..6bfa1014e 100644 --- a/Ix/Kernel/Verify/Consistency/Production.lean +++ b/Ix/Kernel/Verify/Consistency/Production.lean @@ -6,6 +6,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Driver import Ix.Kernel.Verify.Consistency.Constant import Ix.Kernel.Verify.Consistency.BinderInference +import Ix.Kernel.Verify.Consistency.Validation /-! # Standalone production declaration checks @@ -84,43 +85,73 @@ theorem StandalonePrefix.member_success {id : KId .anon} rw [EStateM.bind, path.memberGet] at run exact run -/-- Monomorphic definition data. Its complete concrete declaration is the +/-- Definition data, including its declared universe arity. Its complete concrete declaration is the one returned by the production lookup, including kind, safety, and block. -/ structure DefinitionInput where id : KId .anon kind : Ix.DefKind safety : Ix.DefinitionSafety hints : Lean.ReducibilityHints + universes : UInt64 := 0 type : KExpr .anon value : KExpr .anon block : KId .anon def DefinitionInput.constant (input : DefinitionInput) : KConst .anon := - .defn () () input.kind input.safety input.hints 0 input.type input.value () input.block + .defn () () input.kind input.safety input.hints input.universes + input.type input.value () input.block + +private theorem levelScope_mono {level : VLevel} {before after : Nat} + (scopeOK : level.WF before) (bound : before ≤ after) : level.WF after := by + induction level with + | zero => trivial + | succ level ih => exact ih scopeOK + | max left right ihLeft ihRight | imax left right ihLeft ihRight => + exact ⟨ihLeft scopeOK.1, ihRight scopeOK.2⟩ + | param index => exact Nat.lt_of_lt_of_le scopeOK bound + +private theorem expressionScope_mono {β : Type u} {term : AExpr β} + {before after depth : Nat} (scopeOK : term.Scope before depth) + (bound : before ≤ after) : term.Scope after depth := by + have condition {p : Certified.PropWhen} (valid : p.WF before) : p.WF after := by + cases p with + | never => trivial + | allZero indices sorted => + exact fun index member => Nat.lt_of_lt_of_le (valid index member) bound + induction term generalizing depth with + | bvar => exact scopeOK + | sort => exact levelScope_mono scopeOK bound + | const => exact fun level member => levelScope_mono (scopeOK level member) bound + | app fn arg ihFn ihArg => exact ⟨ihFn scopeOK.1, ihArg scopeOK.2⟩ + | lam p domain body ihDomain ihBody | forallE p domain body ihDomain ihBody => + exact ⟨condition scopeOK.1, ihDomain scopeOK.2.1, ihBody scopeOK.2.2⟩ + | proj ref field major ih => exact ih scopeOK + | natLit => trivial /-- Closed sort/alias inference, a specialization of an existing constant, or a finite binder inference tree with a separately checked declared type. Specializations supply raw syntax and occurrence annotations; successful -inference derives their typing, scope, and references. Binder definitions +inference derives their typing, scope, and references. Universe arguments and +binder conditions may use the declaration's own parameters. Binder definitions also supply syntactic scope and references to the preceding interface. Every case derives body typing without a semantic typing premise. -/ inductive DefinitionBodySupport {β : Type u} (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) (methods : Methods .anon) - (before : TcState .anon) (declared : KExpr .anon) : + (before : TcState .anon) (declared : KExpr .anon) (universes : Nat) : KExpr .anon → AExpr β → AExpr β → Type u | atomic {term : KExpr .anon} {body type : AExpr β} (inference : AtomicInference resolve entries before term body type) : - DefinitionBodySupport resolve entries methods before declared term body type + DefinitionBodySupport resolve entries methods before declared universes term body type | specialization {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} {ref : ConstRef β} {entry : ConstantEntry β} {type : AExpr β} (misses : UncachedInference before (.const id arguments info)) (support : ConstantInferenceSupport resolve entries misses.keyed id arguments ref entry) - (closed : ∀ level ∈ arguments, (readLevel level).WF 0) + (scopeOK : ∀ level ∈ arguments, (readLevel level).WF universes) (reading : readExpr? resolve declared = some type.erase) (conditions : (entry.type.instL (arguments.toList.map readLevel)).annotations = type.annotations) : - DefinitionBodySupport resolve entries methods before declared (.const id arguments info) + DefinitionBodySupport resolve entries methods before declared universes (.const id arguments info) (.const ref (arguments.toList.map readLevel)) type | binder {fuel : Nat} {term inferredType : KExpr .anon} {body type : AExpr β} {level : VLevel} {typeBefore typeAfter : TcState .anon} @@ -130,21 +161,21 @@ inductive DefinitionBodySupport {β : Type u} (typeRun : RecM.infer declared methods typeBefore = .ok inferredType typeAfter) (valueReading : readScopedExpr? resolve [] term = some body.erase) (typeReading : readScopedExpr? resolve [] declared = some type.erase) - (scope : body.Scope 0 0 ∧ type.Scope 0 0) + (scope : body.Scope universes 0 ∧ type.Scope universes 0) (references : body.ReferencesIn entries ∧ type.ReferencesIn entries) : - DefinitionBodySupport resolve entries methods before declared term body type + DefinitionBodySupport resolve entries methods before declared universes term body type theorem DefinitionBodySupport.sound {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {before after : TcState .anon} {term declared inferred : KExpr .anon} - {body type : AExpr β} {methods : Methods .anon} - (fragment : DefinitionBodySupport resolve entries methods before declared term body type) + {body type : AExpr β} {methods : Methods .anon} {universes : Nat} + (fragment : DefinitionBodySupport resolve entries methods before declared universes term body type) (wellFormed : entries.WF) (accepted : RecM.infer term methods before = .ok inferred after) (faithful : inferred.AddrFaithful declared) (hashPath : (inferred == declared) = true) : readExpr? resolve term = some body.erase ∧ readExpr? resolve declared = some type.erase ∧ - body.Scope 0 0 ∧ type.Scope 0 0 ∧ + body.Scope universes 0 ∧ type.Scope universes 0 ∧ body.ReferencesIn entries ∧ type.ReferencesIn entries ∧ TypingClaim.{u,v} entries [] body type := by have hashReads := beq_readExpr? (resolve := resolve) faithful hashPath @@ -154,19 +185,20 @@ theorem DefinitionBodySupport.sound {β : Type u} obtain ⟨bodyScope, typeScope, bodyRefs, typeRefs⟩ := inference.support.scopeAndReferences wellFormed exact ⟨valueReads, hashReads.symm.trans typeReads, - bodyScope, typeScope, bodyRefs, typeRefs, typed⟩ - | specialization misses support closed reading conditions => + expressionScope_mono bodyScope (Nat.zero_le universes), + expressionScope_mono typeScope (Nat.zero_le universes), bodyRefs, typeRefs, typed⟩ + | specialization misses support scopeOK reading conditions => obtain ⟨output, reads, same, arity, scope, references⟩ := infer_const_refinement misses support wellFormed accepted have equal := AExpr.eq_of_erase_annotations (Option.some.inj (reads.symm.trans (hashReads.trans reading))) (same.annotations.symm.trans conditions) - refine ⟨?_, reading, ?_, equal ▸ scope 0 closed, ?_, ?_, + refine ⟨?_, reading, ?_, equal ▸ scope universes scopeOK, ?_, ?_, equal ▸ same.typing (TypingClaim.const support.found arity)⟩ · simp [readExpr?, support.resolved, AExpr.erase] · intro level member obtain ⟨value, valueMember, rfl⟩ := List.mem_map.mp member - exact closed value (by simpa using valueMember) + exact scopeOK value (by simpa using valueMember) · intro ref member simp only [AExpr.references, List.mem_singleton] at member subst ref @@ -240,6 +272,60 @@ theorem definition_body_trace {input : DefinitionInput} {methods : Methods .anon validationRun, typeRun, sortRun, theoremGuard := Bool.eq_false_iff.mpr guard, valueRun, conversionRun }⟩ +/-- Recover source universe and term scope from the validation that this +production member check actually executed. Only bounds on the extra model +binder conditions remain a separate syntactic check. -/ +theorem DefinitionBodyTrace.scopes {β : Type u} {input : DefinitionInput} + {methods : Methods .anon} {before : TcState .anon} + (trace : DefinitionBodyTrace input methods before) + {resolve : Address → Option (ConstRef β)} {body type : AExpr β} + {support : RunSupport} (typeCoverage : input.type.ValidationCoverage support) + (valueCoverage : input.value.ValidationCoverage support) + (collision : support.CollisionFree) + (valueReading : readScopedExpr? resolve [] input.value = some body.erase) + (typeReading : readScopedExpr? resolve [] input.type = some type.erase) + (valueConditions : ConditionsScoped input.universes.toNat body) + (typeConditions : ConditionsScoped input.universes.toNat type) : + body.Scope input.universes.toNat 0 ∧ type.Scope input.universes.toNat 0 := by + have validated := trace.validationRun + unfold RecM.validateConstWellScoped at validated + change EStateM.bind + ((RecM.validateExprWellScoped input.type 0 input.universes.toNat).run methods) + _ before = _ at validated + obtain ⟨⟨⟩, intermediate, typeRun, valueRun⟩ := bind_success validated + change (RecM.validateExprWellScoped input.value 0 input.universes.toNat).run methods + intermediate = .ok () trace.validated at valueRun + obtain ⟨_, _, _, typeScope⟩ := RecM.validateExprWellScoped_sound typeCoverage collision typeRun + obtain ⟨_, _, _, valueScope⟩ := RecM.validateExprWellScoped_sound valueCoverage collision valueRun + exact ⟨readScopedExpr?_annotated_scope valueReading valueScope valueConditions, + readScopedExpr?_annotated_scope typeReading typeScope typeConditions⟩ + +/-- Build binder admission from the exact type and value inference calls of +a member trace. Production validation derives their source scope, including +the definition's own universe parameters. No whole-expression model scope +or semantic typing witness is supplied by this constructor. -/ +def DefinitionBodyTrace.binderSupport {β : Type u} {input : DefinitionInput} + {fuel : Nat} {before : TcState .anon} + (trace : DefinitionBodyTrace input (methodsN fuel) before) + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {body type : AExpr β} {level : VLevel} + {support : RunSupport} (typeCoverage : input.type.ValidationCoverage support) + (valueCoverage : input.value.ValidationCoverage support) + (collision : support.CollisionFree) + (valueInference : BinderInference resolve entries [] [] fuel trace.valueStart input.value body type) + (typeInference : BinderInference resolve entries [] [] fuel trace.validated input.type type (.sort level)) + (valueReading : readScopedExpr? resolve [] input.value = some body.erase) + (typeReading : readScopedExpr? resolve [] input.type = some type.erase) + (valueConditions : ConditionsScoped input.universes.toNat body) + (typeConditions : ConditionsScoped input.universes.toNat type) + (references : body.ReferencesIn entries ∧ type.ReferencesIn entries) : + DefinitionBodySupport resolve entries (methodsN fuel) trace.valueStart input.type + input.universes.toNat input.value body type := + .binder rfl valueInference typeInference trace.typeRun valueReading typeReading + (trace.scopes typeCoverage valueCoverage collision valueReading typeReading + valueConditions typeConditions) + references + /-- Operational support for the selected production definition fragment. Resources are required only at the states exposed by successful body traces. The conversion guard records the actual initial hash-equality path. -/ @@ -249,7 +335,7 @@ structure AtomicDefinitionRun {β : Type u} (resolve : Address → Option (Const path : StandalonePrefix input.id before input.constant inference : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, DefinitionBodySupport resolve entries (methodsN before.recFuel.toNat) - trace.valueStart input.type input.value body type + trace.valueStart input.type input.universes.toNat input.value body type hashPath : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, (trace.inferredValue == input.type) = true faithful : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, @@ -266,7 +352,7 @@ theorem AtomicDefinitionRun.sound {β : Type u} (accepted : TcM.checkConst input.id before = .ok () after) : readExpr? resolve input.value = some body.erase ∧ readExpr? resolve input.type = some type.erase ∧ - body.Scope 0 0 ∧ type.Scope 0 0 ∧ + body.Scope input.universes.toNat 0 ∧ type.Scope input.universes.toNat 0 ∧ body.ReferencesIn entries ∧ type.ReferencesIn entries ∧ TypingClaim.{u,v} entries [] body type := by obtain ⟨trace⟩ := definition_body_trace (fragment.path.member_success accepted) diff --git a/Ix/Kernel/Verify/Consistency/Validation.lean b/Ix/Kernel/Verify/Consistency/Validation.lean new file mode 100644 index 000000000..6b68ffcd5 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Validation.lean @@ -0,0 +1,112 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.ScopedExpr +import Ix.Kernel.Verify.Check.ValidatorSoundness + +/-! +# Production validation and model scope + +Successful declaration validation supplies the universe bounds of its actual +syntax. The scoped reader supplies de Bruijn closure. Only the occurrence +annotations, which are absent from kernel syntax, need a separate bounds +check; no typing or model-realization premise is used here. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u +variable {β : Type u} + +/-- Bounds for the auxiliary binder conditions, without any claim about +source syntax, typing, or the truth of those conditions. -/ +def ConditionsScoped (universes : Nat) : AExpr β → Prop + | .app fn arg => ConditionsScoped universes fn ∧ ConditionsScoped universes arg + | .lam condition domain body | .forallE condition domain body => + condition.WF universes ∧ ConditionsScoped universes domain ∧ + ConditionsScoped universes body + | .proj _ _ major => ConditionsScoped universes major + | _ => True + +private theorem option_bind_success {α γ : Type _} {action : Option α} + {next : α → Option γ} {result : γ} (run : action.bind next = some result) : + ∃ intermediate, action = some intermediate ∧ next intermediate = some result := by + cases action with + | none => contradiction + | some value => exact ⟨value, rfl, run⟩ + +/-- The actual validator's universe bounds and the closed scoped reading +establish both parts of raw model scope. The reader counts binders with +unbounded naturals, so this theorem does not convert a wrapped kernel depth. -/ +theorem readScopedExpr?_scope {resolve : Address → Option (ConstRef β)} + {term : KExpr .anon} {source : VExpr β} {depth universes : Nat} {kernelDepth : UInt64} + (reading : readScopedExpr? resolve [] term depth = some source) + (validSyntax : term.Scoped kernelDepth universes) : + source.LevelWF universes ∧ source.ClosedN depth := by + induction term generalizing source depth kernelDepth with + | var index name info => + simp only [readScopedExpr?] at reading + split at reading + next bound => cases reading; exact ⟨trivial, bound⟩ + next => contradiction + | fvar _ _ _ | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | sort level info => + cases reading + exact ⟨readLevel_eq level ▸ validSyntax.toVLevel_wf, trivial⟩ + | const id levels info => + rw [readScopedExpr?] at reading + obtain ⟨ref, _, reading⟩ := option_bind_success reading + cases reading + refine ⟨?_, trivial⟩ + intro level member + obtain ⟨value, present, rfl⟩ := List.mem_map.mp member + simpa only [readLevel_eq] using (validSyntax value (by simpa using present)).toVLevel_wf + | app fn arg info ihFn ihArg | lam _ _ fn arg info ihFn ihArg | all _ _ fn arg info ihFn ihArg => + rw [readScopedExpr?] at reading + obtain ⟨f, fReads, reading⟩ := option_bind_success reading + obtain ⟨a, aReads, reading⟩ := option_bind_success reading + cases reading + have fScope := ihFn fReads validSyntax.1 + have aScope := ihArg aReads validSyntax.2 + exact ⟨⟨fScope.1, aScope.1⟩, ⟨fScope.2, aScope.2⟩⟩ + | prj id index value info ih => + rw [readScopedExpr?] at reading + obtain ⟨ref, _, reading⟩ := option_bind_success reading + obtain ⟨value, valueReads, reading⟩ := option_bind_success reading + cases reading + have scope := ih valueReads validSyntax + exact ⟨scope.1, scope.2⟩ + | nat value name info => cases reading; exact ⟨trivial, trivial⟩ + +/-- Add only the auxiliary condition bounds to the scope of the erased +syntax; every source universe and term index comes from that syntax. -/ +theorem ConditionsScoped.scope {term : AExpr β} {universes depth : Nat} + (conditions : ConditionsScoped universes term) + (levels : term.erase.LevelWF universes) (closed : term.erase.ClosedN depth) : + term.Scope universes depth := by + induction term generalizing depth with + | bvar => exact closed + | sort | const => exact levels + | app fn arg ihFn ihArg => + exact ⟨ihFn conditions.1 levels.1 closed.1, ihArg conditions.2 levels.2 closed.2⟩ + | lam p domain body ihDomain ihBody | forallE p domain body ihDomain ihBody => + exact ⟨conditions.1, ihDomain conditions.2.1 levels.1 closed.1, + ihBody conditions.2.2 levels.2 closed.2⟩ + | proj ref field major ih => exact ih conditions levels closed + | natLit => trivial + +/-- Scope of an annotated reading follows from the production syntax +certificate and bounds on the auxiliary conditions. -/ +theorem readScopedExpr?_annotated_scope {resolve : Address → Option (ConstRef β)} + {term : KExpr .anon} {source : AExpr β} {depth universes : Nat} {kernelDepth : UInt64} + (reading : readScopedExpr? resolve [] term depth = some source.erase) + (validSyntax : term.Scoped kernelDepth universes) + (conditions : ConditionsScoped universes source) : source.Scope universes depth := by + have scope := readScopedExpr?_scope reading validSyntax + exact conditions.scope scope.1 scope.2 + +end Ix.Kernel.Consistency diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 8365675be..efeac2418 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -1907,10 +1907,99 @@ private def sourceCacheCases : TestSeq := ++ test "source cache: a forged same-key application violates source result agreement" sourceCacheForeignWrite +/-- Definitions declare their own parameters. A two-parameter alias uses +`max u v` to instantiate a one-parameter definition; a wrapper applies that +alias beneath binders. Subsequent declarations specialize them at Prop and +Type, and an unused parameter still contributes to the declaration's arity. -/ +private def polymorphicDefinitionEnvironment : Ixon.Env × Array (Address × UInt64) := Id.run do + let type (index : UInt64) := Ixon.Expr.leanAll (.sort index) + (.leanAll (.var 0) (.var 1)) + let value (index : UInt64) := Ixon.Expr.leanLam (.sort index) + (.leanLam (.var 0) (.var 0)) + let (env, identity) := storeConst {} + ⟨.defn ⟨.defn, .safe, 1, type 0, value 0⟩, #[], #[], #[.var 0]⟩ + let (env, opaqueIdentity) := storeConst env + ⟨.defn ⟨.opaq, .safe, 1, type 0, value 0⟩, #[], #[], #[.var 0]⟩ + let universes := #[Ixon.Univ.var 0, .var 1, .max (.var 0) (.var 1)] + let (env, alias) := storeConst env + ⟨.defn ⟨.defn, .safe, 2, type 2, .ref 0 #[2]⟩, #[], #[identity], universes⟩ + let (env, wrapper) := storeConst env + ⟨.defn ⟨.defn, .safe, 2, type 2, + .leanLam (.sort 2) (.leanLam (.var 0) + (.app (.app (.ref 0 #[0, 1]) (.var 1)) (.var 0)))⟩, + #[], #[alias], universes⟩ + let (env, propInstance) := storeConst env + ⟨.defn ⟨.thm, .safe, 0, type 0, .ref 0 #[0]⟩, #[], #[identity], #[.zero]⟩ + let (env, typeInstance) := storeConst env + ⟨.defn ⟨.opaq, .safe, 0, type 1, .ref 0 #[0, 1]⟩, + #[], #[alias], #[.zero, .succ .zero]⟩ + let (env, unused) := storeConst env + ⟨.defn ⟨.thm, .safe, 2, type 0, .ref 0 #[]⟩, #[], #[propInstance], #[.zero]⟩ + let (env, sortFamily) := storeConst env + ⟨.defn ⟨.defn, .safe, 2, .sort 1, .sort 0⟩, #[], #[], + #[.imax (.var 0) (.var 1), .succ (.imax (.var 0) (.var 1))]⟩ + return (env, #[(identity, 1), (opaqueIdentity, 1), (alias, 2), (wrapper, 2), + (propInstance, 0), (typeInstance, 0), (unused, 2), (sortFamily, 2)]) + +private def admittedUniverseInstances : Bool := + let (source, catalog) := polymorphicDefinitionEnvironment + let action : TcM .anon Bool := do + for (addr, arity) in catalog do + let declaration ← TcM.getConst ⟨addr, ()⟩ + match declaration with + | .defn (lvls := count) .. => if count != arity then return false + | _ => return false + let requests := #[(catalog[0]!.1, #[.mkZero], KUniv.mkZero (m := .anon)), + (catalog[0]!.1, #[levelOne], levelOne), + (catalog[2]!.1, #[.mkZero, levelTwo], levelTwo), + (catalog[2]!.1, #[levelTwo, .mkZero], levelTwo), + (catalog[3]!.1, #[.mkZero, .mkZero], .mkZero), + (catalog[3]!.1, #[levelOne, levelTwo], levelTwo), + (catalog[6]!.1, #[levelOne, levelTwo], .mkZero)] + for (addr, arguments, level) in requests do + let expected := KExpr.mkAll () () (.mkSort level) + (.mkAll () () (.mkVar 0 ()) (.mkVar 1 ())) + let result ← TcM.infer (.mkConst ⟨addr, ()⟩ arguments) + if !sameSourceExpr result expected then return false + return true + allSucceeded source catalog.size && + match action (TcState.newLazyAnon source) with + | .ok result _ => result + | .error _ _ => false + +private def badPolymorphicDefinition (arity : UInt64) (bodyLevel : Ixon.Univ) + (kind : Ix.DefKind := .defn) : Ixon.Env × Address := + storeConst {} + ⟨.defn ⟨kind, .safe, arity, + .leanAll (.sort 0) (.leanAll (.var 0) (.var 1)), + .leanLam (.sort 1) (.leanLam (.var 0) (.var 0))⟩, + #[], #[], #[.var 0, bodyLevel]⟩ + +private def polymorphicDefinitionCases : TestSeq := + test "polymorphic admission: definitions, opaque values, reparameterized aliases, and wrappers check" + (allSucceeded polymorphicDefinitionEnvironment.1 8 { clearEvery := 0 }) + ++ test "polymorphic admission: declaration parameters survive clearing at every item" + (allSucceeded polymorphicDefinitionEnvironment.1 8 { clearEvery := 1 }) + ++ test "polymorphic admission: declared arities and distinct universe instances are retained" + admittedUniverseInstances + ++ test "polymorphic admission: an undeclared type parameter is rejected" + (let (source, target) := badPolymorphicDefinition 0 (.var 0); rowFailed source target) + ++ test "polymorphic admission: an out-of-range parameter in the value is rejected" + (let (source, target) := badPolymorphicDefinition 1 (.var 1); rowFailed source target) + ++ test "polymorphic admission: a different in-range parameter cannot justify the declared type" + (let (source, target) := badPolymorphicDefinition 2 (.var 1); rowFailed source target) + ++ test "polymorphic admission: a theorem cannot have an arbitrary sort-valued codomain" + (let (source, target) := badPolymorphicDefinition 1 (.var 0) .thm; rowFailed source target) + ++ test "polymorphic admission: an unused declared parameter still requires an argument" + (let (source, catalog) := polymorphicDefinitionEnvironment + match TcM.infer (.mkConst ⟨catalog[6]!.1, ()⟩ #[.mkZero]) (TcState.newLazyAnon source) with + | .error (.univParamMismatch expected actual) _ => expected == 2 && actual == 1 + | _ => false : Bool) + public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, - sourceAgreementCases, sourceCacheCases] + sourceAgreementCases, sourceCacheCases, polymorphicDefinitionCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 753545e26..e7c278dfa 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -68,6 +68,14 @@ increasing sequence of strongly inaccessible cardinals. opening to the model's dependent context. Singleton abstraction closes the resulting function type, and the simplifying `imax` constructor preserves its universe interpretation. +- `DefinitionBodyTrace.scopes` derives source scope from the type and value + validation executed by the same production member check. It combines finite + validation coverage and collision freedom with the closed scoped reading; + `ConditionsScoped` checks only the auxiliary binder annotations. + `DefinitionBodyTrace.binderSupport` uses the actual type and value inference + calls to construct admission support, including a definition's own universe + parameters. No semantic typing or whole-expression model scope is assumed + by this constructor. - `BinderInference.synthesis` derives full typing for application spines headed by a local or an admitted constant. The function's type supplies hereditary validity of its domain, so arguments may be checked @@ -238,10 +246,10 @@ A `Realizes` witness supplies a model of these axioms. The hypothesis is model existence; a theorem connecting arbitrary syntactic consistency to model existence is outside this result. -The fragment covers monomorphic standalone definitions, theorems, -and opaque definitions whose values are closed universe terms, references to -preceding interface entries, monomorphic specializations of polymorphic -constants, or closed function bodies in the binder fragment. Referenced types may contain +The fragment covers standalone definitions, theorems, +and opaque definitions, including their own universe parameters, whose values +are universe terms, references to preceding interface entries, instances of +polymorphic constants, or closed function bodies in the binder fragment. Referenced types may contain dependent functions and other readable expression forms. Examples include: ```lean @@ -260,6 +268,9 @@ def chooseLeft (P Q : Prop) (p : P) (q : Q) : P := p axiom T.{u} : Sort u axiom f.{u} : T.{u} → T.{u} def useF (x : T.{1}) : T.{1} := f.{1} x +def idSort.{u} (A : Sort u) (a : A) : A := a +def aliasSort.{u,v} : (A : Sort (max u v)) → A → A := idSort.{max u v} +def useSort.{u} (A : Sort u) (a : A) : A := idSort.{u} A a ``` `AtomicEnvironmentFragment` records the precise execution boundary: @@ -363,10 +374,11 @@ extracts the validation, type-inference, theorem-guard, value-inference, and conversion steps from public success, then derives body typing to extend the preceding model. General automatic witness construction, broader application and lambda paths, lets, inductives, coordinated blocks, and other -conversion paths remain outside the fragment. Polymorphic constant inference is composed into declaration -admission and model extension for the monomorphic specializations described -above. Definitions with their own universe parameters remain outside this -environment fragment. +conversion paths remain outside the fragment. The declaration's exact universe +arity is carried from production lookup into its model entry. Model extension +interprets the checked body at every universe instance, retaining old +interpretations at every instance as well. Specializations may use the new +definition's own parameters, including expressions such as `max u v`. `checkEnvAnon_atomic_represents_source` ties every source address to an interface with the type and body reached by production lookup. The independent @@ -417,7 +429,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 349 exact theorem boundaries. The production +The consistency target checks 355 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -501,7 +513,13 @@ and check complete results and declaration coverage through recursive calls, mixed block loads, partial lookup failures, distinct universe instances, scopes, replay, clearing, and repopulation. Negative cases detect a correct cached type with no loaded declaration and a forged application writing a constant's key. -The unit suite contains 556 checks. +Polymorphic admission regressions check parameterized definitions and opaque +values, aliases with changed universe arguments, applications under binders, +later Prop/Type instances, exact loaded arities, and unused parameters. They +reject undeclared parameters, out-of-range value parameters, different +in-range parameters in place of the declared type, non-Prop theorem types, +and missing arguments for unused parameters. +The unit suite contains 564 checks. ## Certified host adapters diff --git a/docs/theory.md b/docs/theory.md index a31bd695d..e072ea2ee 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -20,11 +20,15 @@ open frontiers retain their stated model hypotheses. The [certified host adapters](certified-checking.md) connect authenticated serialized Ixon to model admission. Separately, a production `checkEnvAnon` [fragment](kernel-verification.md#production-environment-fragment) for -monomorphic aliases, closed universe terms, monomorphic specializations of -polymorphic constants, and a fragment of closed dependent function bodies +aliases, universe terms, instances of earlier constants, and a fragment of +closed dependent function bodies, including definitions with their own universe parameters, extends every model of its source axioms under explicit execution witnesses. Binder inference uses the declared type's separate formation check to turn -`Model.CheckingClaim` into semantic typing. Application spines headed by locals +`Model.CheckingClaim` into semantic typing. The actual production validation +supplies source universe bounds; the scoped reading supplies term closure, +with bounds on auxiliary binder conditions checked separately. Model entries +retain the exact declared universe count and denote the checked body at every +universe instance. Application spines headed by locals or admitted polymorphic constants derive their type's validity from the context or dependency model, then check arguments and substitute the dependent result. Constant cache hits derive the same typing from concrete agreement with pure From 2f0b47c2fc0906cce769ac16b6a180f15cdead7a Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Sun, 13 Sep 2026 23:28:29 -0400 Subject: [PATCH 22/63] Reject circular safe definitions during kernel admission --- Ix/Kernel/DefinitionDependencies.lean | 111 ++++++ Ix/Kernel/Inductive.lean | 2 + Ix/Kernel/Verify/Audit/Completed.lean | 11 +- .../Verify/Check/DeclarationValidation.lean | 1 + Ix/Kernel/Verify/Check/ValidatorFrame.lean | 59 ++- Ix/Kernel/Verify/Consistency.lean | 8 + Ix/Kernel/Verify/Consistency/Audit.lean | 25 ++ .../Verify/Consistency/Dependencies.lean | 169 +++++++++ Ix/Kernel/Verify/Consistency/Production.lean | 8 +- Ix/Kernel/Verify/DefinitionDependencies.lean | 340 ++++++++++++++++++ Ix/Kernel/Verify/DefinitionReferences.lean | 213 +++++++++++ Tests/Ix/Kernel/AnonDiff.lean | 42 ++- Tests/Ix/Kernel/DefinitionDependencies.lean | 142 ++++++++ Tests/Main.lean | 2 + crates/kernel/src/check.rs | 283 +++++++++++++++ docs/kernel-verification.md | 31 +- docs/theory.md | 7 + 17 files changed, 1444 insertions(+), 10 deletions(-) create mode 100644 Ix/Kernel/DefinitionDependencies.lean create mode 100644 Ix/Kernel/Verify/Consistency/Dependencies.lean create mode 100644 Ix/Kernel/Verify/DefinitionDependencies.lean create mode 100644 Ix/Kernel/Verify/DefinitionReferences.lean create mode 100644 Tests/Ix/Kernel/DefinitionDependencies.lean diff --git a/Ix/Kernel/DefinitionDependencies.lean b/Ix/Kernel/DefinitionDependencies.lean new file mode 100644 index 000000000..c506e016f --- /dev/null +++ b/Ix/Kernel/DefinitionDependencies.lean @@ -0,0 +1,111 @@ +module + +public import Ix.Kernel.Monad + +/-! +Safe definitions must have an acyclic graph of definition dependencies. +Type checking a reference only checks its declared type; it cannot justify a +cycle such as `theorem loop : P := loop`. This traversal follows constants in +both types and values, including references hidden under binders and lets. +Inductives, constructors, recursors, and axioms are terminal declarations; +their own admission rules justify their interpretations separately. +-/ + +public section +@[expose] section + +namespace Ix.Kernel + +/-- Shared Lean/Rust bound for the dependency walk. Exhaustion rejects the +declaration; it never turns an incomplete traversal into success. -/ +def maxDefinitionDependencySteps : Nat := 1_000_000 + +/-- Constant references in a list of expression roots. The worklist avoids +host-stack recursion, and address memoization visits shared syntax once. +Projection heads are references as well as their major arguments. -/ +def definitionRefs (roots : List (KExpr m)) : Array (KId m) := + go roots {} #[] +where + go (stack : List (KExpr m)) (seen : Std.HashSet Address) + (refs : Array (KId m)) : Array (KId m) := + match stack with + | [] => refs + | expr :: stack => + if seen.contains expr.addr then go stack seen refs + else + let seen := seen.insert expr.addr + match expr with + | .const id .. => go stack seen (refs.push id) + | .app fn arg _ => go (fn :: arg :: stack) seen refs + | .lam _ _ domain body _ | .all _ _ domain body _ => + go (domain :: body :: stack) seen refs + | .letE _ domain value body _ _ => go (domain :: value :: body :: stack) seen refs + | .prj id _ major _ => go (major :: stack) seen (refs.push id) + | _ => go stack seen refs + termination_by exprWorkSize stack + decreasing_by + all_goals simp [exprWorkSize, KExpr.treeSize, KExpr.treeSize_pos] <;> omega + +/-- Only definitions add dependency edges. Recursive inductive and recursor +blocks are checked by their dedicated validators. -/ +def KConst.definitionDependencies : KConst m → Array (KId m) + | .defn (ty := type) (val := value) .. => definitionRefs [type, value] + | _ => #[] + +inductive DefinitionDependencyTask (m : Mode) where + | enter (id : KId m) + | finish (id : KId m) (declaration : KConst m) + +/-- The completed declarations are in dependency order. This certificate is +local to one check and is discarded on error; it is not a semantic cache. -/ +structure DefinitionDependencyState (m : Mode) where + pending : List (DefinitionDependencyTask m) + active : Std.HashSet Address := {} + finished : Std.HashSet Address := {} + ordered : Array (KId m × KConst m) := #[] + +namespace RecM + +/-- One depth-first dependency step, including the actual lazy lookup. A +completed node is published only after every direct dependency is complete. -/ +def definitionDependencyStep (walk : DefinitionDependencyState m) : + RecM m (BoundedStep (DefinitionDependencyState m) (Array (KId m × KConst m))) := do + match walk.pending with + | [] => return .done walk.ordered + | .enter id :: pending => + if walk.finished.contains id.addr then return .next { walk with pending } + if walk.active.contains id.addr then + throw (.other s!"cyclic definition dependency at {id}") + let declaration ← TcM.getConst id + let dependencies := declaration.definitionDependencies.toList.map DefinitionDependencyTask.enter + return .next { walk with + pending := dependencies ++ .finish id declaration :: pending + active := walk.active.insert id.addr } + | .finish id declaration :: pending => + if walk.finished.contains id.addr || + !(declaration.definitionDependencies.all fun dependency => + walk.finished.contains dependency.addr) then + throw (.other s!"incomplete definition dependencies at {id}") + return .next { walk with + pending, active := walk.active.erase id.addr, finished := walk.finished.insert id.addr + ordered := walk.ordered.push (id, declaration) } + +/-- Return the concrete dependency order produced by the bounded traversal. -/ +def definitionDependencyOrder (roots : Array (KId m)) : RecM m (Array (KId m × KConst m)) := + runBounded definitionDependencyStep maxDefinitionDependencySteps + { pending := roots.toList.map DefinitionDependencyTask.enter } + +/-- Safe declarations cannot use circular definitions to justify a type or +value. Partial and unsafe declarations retain their separate safety policy. -/ +def checkDefinitionDependencies (declaration : KConst m) : RecM m Unit := do + match declaration with + | .defn (safety := .safe) .. => + let _ ← definitionDependencyOrder declaration.definitionDependencies + return () + | _ => return () + +end RecM +end Ix.Kernel + +end +end diff --git a/Ix/Kernel/Inductive.lean b/Ix/Kernel/Inductive.lean index e57d3a91e..959aeff3e 100644 --- a/Ix/Kernel/Inductive.lean +++ b/Ix/Kernel/Inductive.lean @@ -2,6 +2,7 @@ module public import Ix.Kernel.Knot public import Ix.Kernel.CanonicalCheck +public import Ix.Kernel.DefinitionDependencies /-! Mirror: crates/kernel/src/inductive.rs @@ -347,6 +348,7 @@ def validateConstWellScoped (c : KConst m) : RecM m Unit := do match c with | .defn (val := val) .. => validateExprWellScoped val 0 lvlBound + checkDefinitionDependencies c | .recr (rules := rules) .. => for rule in rules do validateExprWellScoped rule.rhs 0 lvlBound diff --git a/Ix/Kernel/Verify/Audit/Completed.lean b/Ix/Kernel/Verify/Audit/Completed.lean index b4751da2d..60309bbfe 100644 --- a/Ix/Kernel/Verify/Audit/Completed.lean +++ b/Ix/Kernel/Verify/Audit/Completed.lean @@ -9228,8 +9228,17 @@ private def roots : Array RootAllowance := #[ { root := ``Ix.Kernel.RecM.validateExprWellScoped_frame, standardAxioms := standardWithoutChoice, forbiddenDependencies := k1ForbiddenDependencies }, + { root := ``Ix.Kernel.RecM.definitionDependencyStep_frame, + standardAxioms := standard, + forbiddenDependencies := k1ForbiddenDependencies }, + { root := ``Ix.Kernel.RecM.definitionDependencyOrder_loop_frame, + standardAxioms := standard, + forbiddenDependencies := k1ForbiddenDependencies }, + { root := ``Ix.Kernel.RecM.checkDefinitionDependencies_frame, + standardAxioms := standard, + forbiddenDependencies := k1ForbiddenDependencies }, { root := ``Ix.Kernel.RecM.validateConstWellScoped_frame, - standardAxioms := standardWithoutChoice, + standardAxioms := standard, forbiddenDependencies := k1ForbiddenDependencies }, { root := ``Ix.Kernel.TcM.LazyFaultPreserves.withInferOnly, standardAxioms := standardWithoutChoice, diff --git a/Ix/Kernel/Verify/Check/DeclarationValidation.lean b/Ix/Kernel/Verify/Check/DeclarationValidation.lean index af934dbd6..55eebe052 100644 --- a/Ix/Kernel/Verify/Check/DeclarationValidation.lean +++ b/Ix/Kernel/Verify/Check/DeclarationValidation.lean @@ -87,6 +87,7 @@ theorem validateConstWellScoped_sound | ok _ nextState => rw [htype] at hrun simp only at hrun + rw [ReaderT.run_bind, runTcBind] at hrun cases hvalue : (validateExprWellScoped value 0 levels.toNat).run methods nextState with diff --git a/Ix/Kernel/Verify/Check/ValidatorFrame.lean b/Ix/Kernel/Verify/Check/ValidatorFrame.lean index 27cf4caf9..60fb5b168 100644 --- a/Ix/Kernel/Verify/Check/ValidatorFrame.lean +++ b/Ix/Kernel/Verify/Check/ValidatorFrame.lean @@ -284,6 +284,59 @@ theorem validateExprWellScoped_frame exact validateExprWellScoped_go_frame bound [(root, rootDepth)] {} {} methods I hfault state +/-- Dependency traversal preserves every invariant supported by lazy lookup, +including on cycle detection, missing references, and other errors. -/ +theorem definitionDependencyStep_frame + (walk : DefinitionDependencyState .anon) (methods : Methods .anon) + {I : TcState .anon → Prop} (hfault : TcM.LazyFaultPreserves I) + (state : TcState .anon) : + TcM.WF I state ((definitionDependencyStep walk).run methods) + (fun _ _ => True) := by + unfold definitionDependencyStep + split + · exact TcM.WF.pure fun _ => trivial + · split + · exact TcM.WF.pure fun _ => trivial + · split + · exact TcM.WF.throw fun _ => trivial + · simp only [ReaderT.run_bind] + apply TcM.WF.bind (TcM.getConst_frame hfault _ state) + intro _ _ _ + exact TcM.WF.pure fun _ => trivial + · split + · exact TcM.WF.throw fun _ => trivial + · exact TcM.WF.pure fun _ => trivial + +theorem definitionDependencyOrder_loop_frame + (methods : Methods .anon) {I : TcState .anon → Prop} + (hfault : TcM.LazyFaultPreserves I) : + ∀ fuel (walk : DefinitionDependencyState .anon) (state : TcState .anon), + TcM.WF I state ((runBounded definitionDependencyStep fuel walk).run methods) + (fun _ _ => True) + | 0, _, _ => TcM.WF.throw fun _ => trivial + | fuel + 1, walk, state => by + rw [runBounded, ReaderT.run_bind] + apply TcM.WF.bind (definitionDependencyStep_frame walk methods hfault state) + intro result after _ + cases result with + | next walk => exact definitionDependencyOrder_loop_frame methods hfault fuel walk after + | done _ => exact TcM.WF.pure fun _ => trivial + +theorem checkDefinitionDependencies_frame + (declaration : KConst .anon) (methods : Methods .anon) + {I : TcState .anon → Prop} (hfault : TcM.LazyFaultPreserves I) + (state : TcState .anon) : + TcM.WF I state ((checkDefinitionDependencies declaration).run methods) + (fun _ _ => True) := by + unfold checkDefinitionDependencies + split + · rw [ReaderT.run_bind] + apply TcM.WF.bind + (definitionDependencyOrder_loop_frame methods hfault _ _ state) + intro _ _ _ + exact TcM.WF.pure fun _ => trivial + · exact TcM.WF.pure fun _ => trivial + /-- Standalone declaration validation preserves the checker invariant on both outcomes. The resource witness restricts this theorem to the axiom and definition shapes owned by declaration-checking. -/ @@ -309,8 +362,10 @@ theorem validateConstWellScoped_frame apply TcM.WF.bind (validateExprWellScoped_frame type 0 levels.toNat methods hfault state) intro _ afterType _ - exact validateExprWellScoped_frame value 0 levels.toNat methods hfault - afterType + apply TcM.WF.bind + (validateExprWellScoped_frame value 0 levels.toNat methods hfault afterType) + intro _ afterValue _ + exact checkDefinitionDependencies_frame _ methods hfault afterValue end RecM diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 6bcfcbc5a..41fa49dab 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -32,6 +32,7 @@ import Ix.Kernel.Verify.Consistency.ConversionRecipe import Ix.Kernel.Verify.Consistency.SourceAgreement import Ix.Kernel.Verify.Consistency.SourceCache import Ix.Kernel.Verify.Consistency.Production +import Ix.Kernel.Verify.Consistency.Dependencies import Ix.Kernel.Verify.Consistency.Environment import Ix.Kernel.Verify.Consistency.Audit @@ -47,6 +48,13 @@ polymorphic references, applications, dependent functions, and full-mode lambdas under the stated execution resources. Definitions may declare their own universe parameters; model entries retain the exact arity and interpretations at every instance. +Safe definition admission now checks its reachable definition dependencies. +The actual traversal has proved root coverage and an order with a decreasing +natural-number rank. Finite collision freedom justifies complete reference +collection through syntax sharing, binders, and lets. Successful validation +exposes this order to the model-reference proof. Both production checkers +reject circular safe definitions; general body typing and model construction +for the ordered declarations remain separate obligations. Constant inference supports arbitrary readable entry types, using the actual universe-instantiation walker and explicit lookup and finite-support resources. The returned type's scope and references justify declaration admission. diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 75e700a1b..749e88721 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -10,6 +10,7 @@ import Ix.Kernel.Verify.Consistency.RecursiveCache import Ix.Kernel.Verify.Consistency.RecursiveState import Ix.Kernel.Verify.Consistency.SourceAgreement import Ix.Kernel.Verify.Consistency.SourceCache +import Ix.Kernel.Verify.Consistency.Dependencies import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -239,6 +240,30 @@ private def forbiddenProduction : Array Lean.Name := #[ ] def roots : Array RootAllowance := #[ + { root := ``DefinitionDependencies.Ordered.closed, standardAxioms := #[``propext, ``Quot.sound], + forbiddenDependencies := forbiddenProduction }, + { root := ``DefinitionDependencies.Ordered.ranked, standardAxioms := standard, + forbiddenDependencies := forbiddenProduction }, + { root := ``DefinitionDependencies.Ordered.wellFounded, standardAxioms := standard, + forbiddenDependencies := forbiddenProduction }, + { root := ``DefinitionDependencies.WalkInvariant.initial, standardAxioms := standard, + forbiddenDependencies := forbiddenProduction }, + { root := ``DefinitionDependencies.step_certificate, standardAxioms := standard, + forbiddenDependencies := forbiddenProduction }, + { root := ``DefinitionDependencies.loop_certificate, standardAxioms := standard, + forbiddenDependencies := forbiddenProduction }, + { root := ``DefinitionDependencies.order_certificate, standardAxioms := standard, + forbiddenDependencies := forbiddenProduction }, + { root := ``DefinitionDependencies.order_sound, standardAxioms := standard, + forbiddenDependencies := forbiddenProduction }, + { root := ``DefinitionReferences.definitionRefs_complete, standardAxioms := standard, + forbiddenDependencies := forbiddenProduction }, + { root := ``readScopedExpr?_referencesIn, standardAxioms := standard, + forbiddenDependencies := forbiddenProduction }, + { root := ``DefinitionBodyTrace.dependencyOrder, standardAxioms := standard, + nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }, + { root := ``DefinitionBodyTrace.referencesIn, standardAxioms := standard, + nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }, { root := ``ConversionRecipe.run_predict, standardAxioms := standard, forbiddenDependencies := forbiddenProduction }, { root := ``Ix.Kernel.ConversionRecipe.run_bind, standardAxioms := #[``propext, ``Quot.sound], diff --git a/Ix/Kernel/Verify/Consistency/Dependencies.lean b/Ix/Kernel/Verify/Consistency/Dependencies.lean new file mode 100644 index 000000000..00127187e --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Dependencies.lean @@ -0,0 +1,169 @@ +import Ix.Kernel.Verify.DefinitionDependencies +import Ix.Kernel.Verify.DefinitionReferences +import Ix.Kernel.Verify.Consistency.Production + +/-! +Production definition admission supplies a concrete dependency order. +The same reference collector also bounds every reference in the model reading +of the checked type and value. These facts connect the new admission guard to +the preceding model interface; no semantic typing is assumed by this bridge. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u +variable {β : Type u} + +private def AllReferences (available : ConstRef β → Prop) : VExpr β → Prop + | .bvar _ | .sort _ | .natLit _ => True + | .const ref _ => available ref + | .app fn arg | .lam fn arg | .forallE fn arg => + AllReferences available fn ∧ AllReferences available arg + | .proj ref _ major => available ref ∧ AllReferences available major + +private theorem allReferences_erase (available : ConstRef β → Prop) (source : AExpr β) : + AllReferences available source.erase ↔ ∀ ref ∈ source.references, available ref := by + induction source <;> simp_all [AllReferences, AExpr.erase, AExpr.references, or_imp, forall_and] + +private theorem option_bind_success {α γ : Type _} {action : Option α} + {next : α → Option γ} {result : γ} (run : action.bind next = some result) : + ∃ intermediate, action = some intermediate ∧ next intermediate = some result := by + cases action with + | none => contradiction + | some value => exact ⟨value, rfl, run⟩ + +private theorem readScopedExpr?_allReferences {available : ConstRef β → Prop} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {term : KExpr .anon} {source : VExpr β} {depth : Nat} + (reading : readScopedExpr? resolve locals term depth = some source) + (references : ∀ id, DefinitionReferences.Reference id term → + ∀ ref, resolve id.addr = some ref → available ref) : AllReferences available source := by + induction term generalizing source depth with + | var index name info => + simp only [readScopedExpr?] at reading + split at reading + · cases reading; trivial + · contradiction + | fvar id name info => + obtain ⟨index, _, equality⟩ := Option.map_eq_some_iff.mp reading + cases equality + trivial + | sort _ _ | nat _ _ _ => cases reading; trivial + | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | const id levels info => + rw [readScopedExpr?] at reading + obtain ⟨ref, resolved, reading⟩ := option_bind_success reading + cases reading + exact references id (.head (by simp [DefinitionReferences.Heads])) ref resolved + | app fn arg info ihFn ihArg | lam _ _ fn arg info ihFn ihArg | all _ _ fn arg info ihFn ihArg => + rw [readScopedExpr?] at reading + obtain ⟨f, fReads, reading⟩ := option_bind_success reading + obtain ⟨a, aReads, reading⟩ := option_bind_success reading + cases reading + exact ⟨ihFn fReads (fun id reference => references id + (.child (by simp [DefinitionReferences.Children]) reference)), + ihArg aReads (fun id reference => references id + (.child (by simp [DefinitionReferences.Children]) reference))⟩ + | prj id field major info ih => + rw [readScopedExpr?] at reading + obtain ⟨ref, resolved, reading⟩ := option_bind_success reading + obtain ⟨value, valueReads, reading⟩ := option_bind_success reading + cases reading + exact ⟨references id (.head (by simp [DefinitionReferences.Heads])) ref resolved, + ih valueReads (fun id reference => references id + (.child (by simp [DefinitionReferences.Children]) reference))⟩ + +/-- Every reference in a model reading is backed by an actual collected +kernel dependency. This also covers opened locals; they add no constants. -/ +theorem readScopedExpr?_referencesIn {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {roots : List (KExpr .anon)} {support : RunSupport} + (coverage : ∀ root ∈ roots, root.ValidationCoverage support) + (collision : support.CollisionFree) {term : KExpr .anon} (member : term ∈ roots) + {source : AExpr β} {locals : List FVarId} {depth : Nat} + (reading : readScopedExpr? resolve locals term depth = some source.erase) + (available : ∀ id ∈ definitionRefs roots, ∀ ref, + resolve id.addr = some ref → (entries ref).isSome = true) : source.ReferencesIn entries := by + apply (allReferences_erase (fun ref => (entries ref).isSome = true) source).mp + apply readScopedExpr?_allReferences reading + intro id reference ref resolved + exact available id + (DefinitionReferences.definitionRefs_complete coverage collision member reference) ref resolved + +private theorem bind_success {α γ : Type} {action : TcM .anon α} + {next : α → TcM .anon γ} {before after : TcState .anon} {result : γ} + (run : EStateM.bind action next before = .ok result after) : + ∃ value middle, action before = .ok value middle ∧ next value middle = .ok result after := by + unfold EStateM.bind at run + cases step : action before with + | error error state => rw [step] at run; contradiction + | ok value middle => rw [step] at run; exact ⟨value, middle, rfl, run⟩ + +/-- Extract the actual dependency traversal from this successful safe +definition's validation, including its final checker state. -/ +theorem DefinitionBodyTrace.dependencyOrder {input : DefinitionInput} + {methods : Methods .anon} {before : TcState .anon} + (trace : DefinitionBodyTrace input methods before) (safe : input.safety = .safe) : + ∃ started order, + (RecM.definitionDependencyOrder input.constant.definitionDependencies).run methods started = + .ok order trace.validated ∧ + DefinitionDependencies.Certificate (fun _ _ => True) + input.constant.definitionDependencies order := by + have validated := trace.validationRun + unfold RecM.validateConstWellScoped at validated + change EStateM.bind + ((RecM.validateExprWellScoped input.type 0 input.universes.toNat).run methods) + _ before = _ at validated + obtain ⟨⟨⟩, intermediate, _, rest⟩ := bind_success validated + change EStateM.bind + ((RecM.validateExprWellScoped input.value 0 input.universes.toNat).run methods) + _ intermediate = _ at rest + obtain ⟨⟨⟩, started, _, checked⟩ := bind_success rest + change (RecM.checkDefinitionDependencies input.constant).run methods started = _ at checked + unfold RecM.checkDefinitionDependencies DefinitionInput.constant at checked + rw [safe] at checked + change EStateM.bind + ((RecM.definitionDependencyOrder input.constant.definitionDependencies).run methods) + _ started = _ at checked + obtain ⟨order, _, ordered, done⟩ := bind_success checked + cases done + exact ⟨started, order, ordered, DefinitionDependencies.order_sound ordered⟩ + +/-- The collector order supplies all references of the checked body and type +once its entries are resolved in the preceding model interface. Scope and +typing remain the conclusions of their separate production-run theorems. -/ +theorem DefinitionBodyTrace.referencesIn {input : DefinitionInput} + {methods : Methods .anon} {before : TcState .anon} + (trace : DefinitionBodyTrace input methods before) (safe : input.safety = .safe) + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {body type : AExpr β} {support : RunSupport} + (typeCoverage : input.type.ValidationCoverage support) + (valueCoverage : input.value.ValidationCoverage support) (collision : support.CollisionFree) + (valueReading : readScopedExpr? resolve [] input.value = some body.erase) + (typeReading : readScopedExpr? resolve [] input.type = some type.erase) + (available : ∀ started order, + (RecM.definitionDependencyOrder input.constant.definitionDependencies).run methods started = + .ok order trace.validated → + ∀ entry ∈ order, ∀ ref, resolve entry.1.addr = some ref → (entries ref).isSome = true) : + body.ReferencesIn entries ∧ type.ReferencesIn entries := by + obtain ⟨started, order, run, certificate⟩ := trace.dependencyOrder safe + have coverage : ∀ root ∈ [input.type, input.value], root.ValidationCoverage support := by + intro root member + rcases List.mem_cons.mp member with rfl | member + · exact typeCoverage + · have equality := List.mem_singleton.mp member + cases equality + exact valueCoverage + have present : ∀ id ∈ definitionRefs [input.type, input.value], ∀ ref, + resolve id.addr = some ref → (entries ref).isSome = true := by + intro id member ref resolved + have listed := certificate.covers id member + obtain ⟨entry, member, equality⟩ := List.mem_map.mp listed + apply available started order run entry (by simpa using member) ref + rw [equality] + exact resolved + exact ⟨readScopedExpr?_referencesIn coverage collision (by simp) valueReading present, + readScopedExpr?_referencesIn coverage collision (by simp) typeReading present⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Production.lean b/Ix/Kernel/Verify/Consistency/Production.lean index 6bfa1014e..6893b6eaf 100644 --- a/Ix/Kernel/Verify/Consistency/Production.lean +++ b/Ix/Kernel/Verify/Consistency/Production.lean @@ -292,9 +292,11 @@ theorem DefinitionBodyTrace.scopes {β : Type u} {input : DefinitionInput} change EStateM.bind ((RecM.validateExprWellScoped input.type 0 input.universes.toNat).run methods) _ before = _ at validated - obtain ⟨⟨⟩, intermediate, typeRun, valueRun⟩ := bind_success validated - change (RecM.validateExprWellScoped input.value 0 input.universes.toNat).run methods - intermediate = .ok () trace.validated at valueRun + obtain ⟨⟨⟩, intermediate, typeRun, rest⟩ := bind_success validated + change EStateM.bind + ((RecM.validateExprWellScoped input.value 0 input.universes.toNat).run methods) + _ intermediate = _ at rest + obtain ⟨⟨⟩, _, valueRun, _⟩ := bind_success rest obtain ⟨_, _, _, typeScope⟩ := RecM.validateExprWellScoped_sound typeCoverage collision typeRun obtain ⟨_, _, _, valueScope⟩ := RecM.validateExprWellScoped_sound valueCoverage collision valueRun exact ⟨readScopedExpr?_annotated_scope valueReading valueScope valueConditions, diff --git a/Ix/Kernel/Verify/DefinitionDependencies.lean b/Ix/Kernel/Verify/DefinitionDependencies.lean new file mode 100644 index 000000000..ae83fd9d8 --- /dev/null +++ b/Ix/Kernel/Verify/DefinitionDependencies.lean @@ -0,0 +1,340 @@ +import Ix.Kernel.DefinitionDependencies +import Ix.Kernel.Verify.Expr + +/-! +The dependency certificate returned by the production admission check. +Every completed declaration is fresh and all its direct dependencies occur +earlier. Root coverage is carried through the actual depth-first worklist. +Lookup agreement may be instantiated with a concrete immutable source model; +the unconditional ordering theorem does not assume declaration validity. +-/ + +namespace Ix.Kernel + +namespace DefinitionDependencies + +def Listed (address : Address) (entries : Array (KId m × KConst m)) : Prop := + address ∈ entries.toList.map (fun entry => entry.1.addr) + +@[simp] theorem listed_empty : ¬ Listed address (#[] : Array (KId m × KConst m)) := by + simp [Listed] + +@[simp] theorem listed_push {entries : Array (KId m × KConst m)} + {id : KId m} {declaration : KConst m} : + Listed address (entries.push (id, declaration)) ↔ + Listed address entries ∨ address = id.addr := by + simp [Listed] + +theorem listed_of_mem {entries : Array (KId m × KConst m)} + {entry : KId m × KConst m} (member : entry ∈ entries) : Listed entry.1.addr entries := + List.mem_map.mpr ⟨entry, by simpa using member, rfl⟩ + +/-- An inductive topological order: each appended declaration is fresh and +all the references collected from its type and value are already present. -/ +inductive Ordered : Array (KId m × KConst m) → Prop where + | empty : Ordered #[] + | push {entries : Array (KId m × KConst m)} {id : KId m} {declaration : KConst m} + (prior : Ordered entries) (fresh : ¬ Listed id.addr entries) + (dependencies : ∀ dependency ∈ declaration.definitionDependencies, + Listed dependency.addr entries) : Ordered (entries.push (id, declaration)) + +theorem Ordered.closed {entries : Array (KId m × KConst m)} (ordered : Ordered entries) : + ∀ entry ∈ entries, ∀ dependency ∈ entry.2.definitionDependencies, + Listed dependency.addr entries := by + induction ordered with + | empty => simp + | @push entries id declaration prior fresh dependencies ih => + intro entry member dependency dependencyMember + simp only [Array.mem_push] at member + apply listed_push.mpr + left + rcases member with member | member + · exact ih entry member dependency dependencyMember + · cases member; exact dependencies dependency dependencyMember + +/-- A natural-number rank decreases strictly along every recorded edge. +This converts the incremental certificate into a well-founded dependency +relation, without assuming that the declarations have already been checked. -/ +theorem Ordered.ranked {entries : Array (KId m × KConst m)} (ordered : Ordered entries) : + ∃ rank : Address → Nat, + (∀ address, Listed address entries → rank address < entries.size) ∧ + (∀ entry ∈ entries, ∀ dependency ∈ entry.2.definitionDependencies, + rank dependency.addr < rank entry.1.addr) := by + classical + induction ordered with + | empty => exact ⟨fun _ => 0, by simp, by simp⟩ + | @push entries id declaration prior fresh dependencies ih => + obtain ⟨rank, bound, decreasing⟩ := ih + have different (address : Address) (listed : Listed address entries) : address ≠ id.addr := by + intro equality + subst address + exact fresh listed + refine ⟨fun address => if address = id.addr then entries.size else rank address, ?_, ?_⟩ + · intro address listed + rcases listed_push.mp listed with listed | equality + · simp only [if_neg (different address listed), Array.size_push] + exact Nat.lt_succ_of_lt (bound address listed) + · subst address + simp + · intro entry member dependency dependencyMember + simp only [Array.mem_push] at member + rcases member with member | equality + · simp only [if_neg (different entry.1.addr (listed_of_mem member)), + if_neg (different dependency.addr (prior.closed entry member dependency dependencyMember))] + exact decreasing entry member dependency dependencyMember + · cases equality + simp only [if_neg (different dependency.addr + (dependencies dependency dependencyMember))] + exact bound dependency.addr (dependencies dependency dependencyMember) + +def Edge (entries : Array (KId m × KConst m)) (dependency user : Address) : Prop := + ∃ entry ∈ entries, entry.1.addr = user ∧ + ∃ target ∈ entry.2.definitionDependencies, target.addr = dependency + +theorem Ordered.wellFounded {entries : Array (KId m × KConst m)} + (ordered : Ordered entries) : WellFounded (Edge entries) := by + obtain ⟨rank, _, decreasing⟩ := ordered.ranked + apply Subrelation.wf (r := InvImage Nat.lt rank) ?_ (InvImage.wf rank Nat.lt_wfRel.wf) + rintro dependency user ⟨entry, member, rfl, target, targetMember, rfl⟩ + exact decreasing entry member target targetMember + +def TaskAddress : DefinitionDependencyTask m → Address + | .enter id | .finish id _ => id.addr + +def Queued (address : Address) (pending : List (DefinitionDependencyTask m)) : Prop := + address ∈ pending.map TaskAddress + +@[simp] theorem queued_nil : ¬ Queued address ([] : List (DefinitionDependencyTask m)) := by + simp [Queued] + +@[simp] theorem queued_cons : Queued address (task :: pending) ↔ + address = TaskAddress task ∨ Queued address pending := by + simp [Queued] + +@[simp] theorem queued_append : Queued address (left ++ right) ↔ + Queued address left ∨ Queued address right := by + simp [Queued] + +structure WalkInvariant (read : KId m → KConst m → Prop) + (roots : Array (KId m)) (walk : DefinitionDependencyState m) : Prop where + ordered : Ordered walk.ordered + finished : ∀ address, walk.finished.contains address = true ↔ Listed address walk.ordered + reads : ∀ entry ∈ walk.ordered, read entry.1 entry.2 + pendingReads : ∀ id declaration, .finish id declaration ∈ walk.pending → read id declaration + covers : ∀ root ∈ roots, + walk.finished.contains root.addr = true ∨ Queued root.addr walk.pending + +/-- The certificate refers to the declarations actually obtained by lookup. -/ +structure Certificate (read : KId m → KConst m → Prop) + (roots : Array (KId m)) (entries : Array (KId m × KConst m)) : Prop where + ordered : Ordered entries + covers : ∀ root ∈ roots, Listed root.addr entries + reads : ∀ entry ∈ entries, read entry.1 entry.2 + +private theorem bind_success {α β : Type} {action : TcM m α} + {next : α → TcM m β} {before after : TcState m} {result : β} + (run : EStateM.bind action next before = .ok result after) : + ∃ value middle, action before = .ok value middle ∧ next value middle = .ok result after := by + unfold EStateM.bind at run + cases step : action before with + | error error state => rw [step] at run; contradiction + | ok value middle => + rw [step] at run + exact ⟨value, middle, rfl, run⟩ + +private theorem finish_not_in_enters (id : KId m) (declaration : KConst m) + (dependencies : Array (KId m)) : + .finish id declaration ∉ dependencies.toList.map DefinitionDependencyTask.enter := by + simp + +theorem WalkInvariant.initial (read : KId m → KConst m → Prop) (roots : Array (KId m)) : + WalkInvariant read roots { pending := roots.toList.map DefinitionDependencyTask.enter } := by + constructor + · exact .empty + · intro address; simp [Listed] + · simp + · intro id declaration h; exact False.elim (finish_not_in_enters id declaration roots h) + · intro root hroot + right + exact List.mem_map.mpr ⟨.enter root, + List.mem_map.mpr ⟨root, by simpa using hroot, rfl⟩, rfl⟩ + +theorem WalkInvariant.skip {read : KId m → KConst m → Prop} + {roots : Array (KId m)} {walk : DefinitionDependencyState m} + (valid : WalkInvariant read roots walk) {id : KId m} + {pending : List (DefinitionDependencyTask m)} + (tasks : walk.pending = .enter id :: pending) + (finished : walk.finished.contains id.addr = true) : + WalkInvariant read roots { walk with pending } := by + refine ⟨valid.ordered, valid.finished, valid.reads, ?_, ?_⟩ + · intro target declaration h + exact valid.pendingReads target declaration (by rw [tasks]; exact .tail _ h) + · intro root hroot + rcases valid.covers root hroot with h | h + · exact .inl h + · rw [tasks, queued_cons] at h + rcases h with h | h + · exact .inl (by simpa [TaskAddress] using h ▸ finished) + · exact .inr h + +theorem WalkInvariant.enter {read : KId m → KConst m → Prop} + {roots : Array (KId m)} {walk : DefinitionDependencyState m} + (valid : WalkInvariant read roots walk) {id : KId m} {declaration : KConst m} + {pending : List (DefinitionDependencyTask m)} + (tasks : walk.pending = .enter id :: pending) (loaded : read id declaration) : + WalkInvariant read roots { walk with + pending := declaration.definitionDependencies.toList.map DefinitionDependencyTask.enter ++ + .finish id declaration :: pending + active := walk.active.insert id.addr } := by + refine ⟨valid.ordered, valid.finished, valid.reads, ?_, ?_⟩ + · intro target concrete h + simp only [List.mem_append, List.mem_cons] at h + rcases h with h | h | h + · exact False.elim (finish_not_in_enters target concrete _ h) + · cases h; exact loaded + · exact valid.pendingReads target concrete (by rw [tasks]; exact .tail _ h) + · intro root hroot + rcases valid.covers root hroot with h | h + · exact .inl h + · right + rw [tasks, queued_cons] at h + exact queued_append.mpr (.inr (queued_cons.mpr h)) + +theorem WalkInvariant.finish {read : KId m → KConst m → Prop} + {roots : Array (KId m)} {walk : DefinitionDependencyState m} + (valid : WalkInvariant read roots walk) {id : KId m} {declaration : KConst m} + {pending : List (DefinitionDependencyTask m)} + (tasks : walk.pending = .finish id declaration :: pending) + (fresh : walk.finished.contains id.addr = false) + (dependencies : ∀ dependency ∈ declaration.definitionDependencies, + walk.finished.contains dependency.addr = true) : + WalkInvariant read roots { walk with + pending := pending + active := walk.active.erase id.addr + finished := walk.finished.insert id.addr + ordered := walk.ordered.push (id, declaration) } := by + have loaded := valid.pendingReads id declaration (by rw [tasks]; exact .head _) + refine ⟨.push valid.ordered ?_ ?_, ?_, ?_, ?_, ?_⟩ + · intro listed + have := (valid.finished id.addr).mpr listed + simp [fresh] at this + · intro dependency h + exact (valid.finished _).mp (dependencies dependency h) + · intro address + simp only [Std.HashSet.contains_insert, Bool.or_eq_true, beq_iff_eq, listed_push] + rw [valid.finished] + rw [eq_comm (a := id.addr), or_comm] + · intro entry h + simp only [Array.mem_push] at h + rcases h with h | h + · exact valid.reads entry h + · cases h; exact loaded + · intro target concrete h + exact valid.pendingReads target concrete (by rw [tasks]; exact .tail _ h) + · intro root hroot + rcases valid.covers root hroot with h | h + · left + simp only [Std.HashSet.contains_insert, Bool.or_eq_true] + exact .inr h + · rw [tasks, queued_cons] at h + rcases h with h | h + · left + simp only [Std.HashSet.contains_insert, Bool.or_eq_true, beq_iff_eq] + exact .inl h.symm + · exact .inr h + +/-- Successful production steps preserve the order and the exact lookup +relation. The lookup premise is operational and is used only at an actual +`getConst` call, including its returned state. -/ +theorem step_certificate {read : KId m → KConst m → Prop} + {stateInvariant : TcState m → Prop} + (lookup : ∀ id before declaration after, stateInvariant before → + TcM.getConst id before = .ok declaration after → + stateInvariant after ∧ read id declaration) + {roots : Array (KId m)} {walk : DefinitionDependencyState m} + (valid : WalkInvariant read roots walk) {methods : Methods m} + {before after : TcState m} (state : stateInvariant before) + {result : RecM.BoundedStep (DefinitionDependencyState m) (Array (KId m × KConst m))} + (run : (RecM.definitionDependencyStep walk).run methods before = .ok result after) : + stateInvariant after ∧ match result with + | .next next => WalkInvariant read roots next + | .done entries => Certificate read roots entries := by + unfold RecM.definitionDependencyStep at run + split at run + · rename_i tasks + cases run + refine ⟨state, valid.ordered, ?_, valid.reads⟩ + intro root hroot + rcases valid.covers root hroot with h | h + · exact (valid.finished _).mp h + · simp [tasks] at h + · rename_i id pending tasks + split at run + · rename_i finished + cases run + exact ⟨state, valid.skip tasks finished⟩ + · split at run + · contradiction + · change EStateM.bind (TcM.getConst id) _ before = _ at run + obtain ⟨declaration, middle, loaded, run⟩ := bind_success run + obtain ⟨nextState, read⟩ := lookup id before declaration middle state loaded + cases run + exact ⟨nextState, valid.enter tasks read⟩ + · rename_i id declaration pending tasks + split at run + · contradiction + · rename_i guards + have guards : walk.finished.contains id.addr = false ∧ + declaration.definitionDependencies.all + (fun dependency => walk.finished.contains dependency.addr) = true := by + simpa only [Bool.or_eq_true, Bool.not_eq_true', not_or, Bool.not_eq_true, + Bool.not_eq_false] using guards + cases run + exact ⟨state, valid.finish tasks guards.1 + (Array.all_eq_true_iff_forall_mem.mp guards.2)⟩ + +theorem loop_certificate {read : KId m → KConst m → Prop} + {stateInvariant : TcState m → Prop} + (lookup : ∀ id before declaration after, stateInvariant before → + TcM.getConst id before = .ok declaration after → + stateInvariant after ∧ read id declaration) + {roots : Array (KId m)} {methods : Methods m} : + ∀ fuel {walk : DefinitionDependencyState m} (_valid : WalkInvariant read roots walk) + {before after : TcState m} (_state : stateInvariant before) + {entries : Array (KId m × KConst m)}, + (RecM.runBounded RecM.definitionDependencyStep fuel walk).run methods before = + .ok entries after → stateInvariant after ∧ Certificate read roots entries + | 0, _, _, _, _, _, _, run => by contradiction + | fuel + 1, walk, valid, before, after, state, entries, run => by + rw [RecM.runBounded] at run + change EStateM.bind _ _ before = _ at run + obtain ⟨step, middle, stepRun, rest⟩ := bind_success run + have stepValid := step_certificate lookup valid state stepRun + cases step with + | next next => exact loop_certificate lookup fuel stepValid.2 stepValid.1 rest + | done entries => + cases rest + exact stepValid + +theorem order_certificate {read : KId m → KConst m → Prop} + {stateInvariant : TcState m → Prop} + (lookup : ∀ id before declaration after, stateInvariant before → + TcM.getConst id before = .ok declaration after → + stateInvariant after ∧ read id declaration) + {roots : Array (KId m)} {methods : Methods m} {before after : TcState m} + (state : stateInvariant before) {entries : Array (KId m × KConst m)} + (run : (RecM.definitionDependencyOrder roots).run methods before = .ok entries after) : + stateInvariant after ∧ Certificate read roots entries := + loop_certificate lookup maxDefinitionDependencySteps (WalkInvariant.initial read roots) state run + +/-- No validity premise is required for the actual successful traversal's +ordering or root coverage. -/ +theorem order_sound {roots : Array (KId m)} {methods : Methods m} + {before after : TcState m} {entries : Array (KId m × KConst m)} + (run : (RecM.definitionDependencyOrder roots).run methods before = .ok entries after) : + Certificate (fun _ _ => True) roots entries := + (order_certificate (stateInvariant := fun _ => True) + (fun _ _ _ _ _ _ => ⟨trivial, trivial⟩) trivial run).2 + +end DefinitionDependencies +end Ix.Kernel diff --git a/Ix/Kernel/Verify/DefinitionReferences.lean b/Ix/Kernel/Verify/DefinitionReferences.lean new file mode 100644 index 000000000..921c55052 --- /dev/null +++ b/Ix/Kernel/Verify/DefinitionReferences.lean @@ -0,0 +1,213 @@ +import Ix.Kernel.DefinitionDependencies +import Ix.Kernel.Verify.Check.ValidatorSoundness + +/-! +Completeness of the production address-memoized reference collector. +An address is marked before its children are visited, so the invariant keeps +the local references and the pending child frontier separate. Finite syntax +collision freedom justifies each memo hit. No trusted parser or alternate +reference validator is used. +-/ + +namespace Ix.Kernel.DefinitionReferences + +def Children : KExpr .anon → List (KExpr .anon) + | .app fn arg _ => [fn, arg] + | .lam _ _ domain body _ | .all _ _ domain body _ => [domain, body] + | .letE _ domain value body _ _ => [domain, value, body] + | .prj _ _ major _ => [major] + | _ => [] + +def Heads : KExpr .anon → Array (KId .anon) + | .const id .. | .prj id .. => #[id] + | _ => #[] + +/-- Literal constant and projection-head references anywhere in the syntax. -/ +inductive Reference (id : KId .anon) : KExpr .anon → Prop where + | head {expr : KExpr .anon} (member : id ∈ Heads expr) : Reference id expr + | child {expr child : KExpr .anon} + (member : child ∈ Children expr) (nested : Reference id child) : Reference id expr + +def DomainClosed (domain : KExpr .anon → Prop) : Prop := + ∀ expr, domain expr → ∀ child ∈ Children expr, domain child + +def StackInDomain (domain : KExpr .anon → Prop) (stack : List (KExpr .anon)) : Prop := + ∀ expr ∈ stack, domain expr + +def SeenLocal (domain : KExpr .anon → Prop) (seen : Std.HashSet Address) + (refs : Array (KId .anon)) : Prop := + ∀ expr, domain expr → seen.contains expr.addr = true → ∀ id ∈ Heads expr, id ∈ refs + +def SeenFrontier (domain : KExpr .anon → Prop) (seen : Std.HashSet Address) + (stack : List (KExpr .anon)) : Prop := + ∀ expr, domain expr → seen.contains expr.addr = true → ∀ child ∈ Children expr, + seen.contains child.addr = true ∨ child ∈ stack + +private theorem go_step (expr : KExpr .anon) (stack : List (KExpr .anon)) + (seen : Std.HashSet Address) (refs : Array (KId .anon)) : + definitionRefs.go (expr :: stack) seen refs = + if seen.contains expr.addr then definitionRefs.go stack seen refs + else definitionRefs.go (Children expr ++ stack) (seen.insert expr.addr) (refs ++ Heads expr) := by + rw [definitionRefs.go] + split + · rfl + · cases expr <;> simp [Children, Heads] + +theorem Children.reachable {expr child : KExpr .anon} (member : child ∈ Children expr) : + expr.ValidationReach child := by + cases expr <;> simp only [Children, List.mem_cons, List.not_mem_nil, + or_false] at member + all_goals first + | contradiction + | rcases member with rfl | rfl | rfl + · exact .letType (.refl _) + · exact .letValue (.refl _) + · exact .letBody (.refl _) + | rcases member with rfl | rfl + all_goals first + | exact .appFn (.refl _) + | exact .appArg (.refl _) + | exact .lamType (.refl _) + | exact .lamBody (.refl _) + | exact .allType (.refl _) + | exact .allBody (.refl _) + | cases member; exact .projectionValue (.refl _) + +theorem SeenFrontier.drop {domain : KExpr .anon → Prop} {expr : KExpr .anon} + {seen : Std.HashSet Address} {stack : List (KExpr .anon)} + (frontier : SeenFrontier domain seen (expr :: stack)) + (present : seen.contains expr.addr = true) : SeenFrontier domain seen stack := by + intro parent parentDomain parentSeen child childMember + rcases frontier parent parentDomain parentSeen child childMember with h | h + · exact .inl h + · rcases List.mem_cons.mp h with rfl | h + · exact .inl present + · exact .inr h + +theorem SeenLocal.expand {domain : KExpr .anon → Prop} {expr : KExpr .anon} + {seen : Std.HashSet Address} {refs : Array (KId .anon)} + (faithful : ∀ left right, domain left → domain right → left.addr = right.addr → left = right) + (locals : SeenLocal domain seen refs) (exprDomain : domain expr) : + SeenLocal domain (seen.insert expr.addr) (refs ++ Heads expr) := by + intro candidate candidateDomain candidateSeen id idMember + rw [Std.HashSet.contains_insert, Bool.or_eq_true] at candidateSeen + apply Array.mem_append.mpr + rcases candidateSeen with h | h + · have equality := faithful expr candidate exprDomain candidateDomain (eq_of_beq h) + subst candidate + exact .inr idMember + · exact .inl (locals candidate candidateDomain h id idMember) + +theorem SeenFrontier.expand {domain : KExpr .anon → Prop} {expr : KExpr .anon} + {seen : Std.HashSet Address} {stack : List (KExpr .anon)} + (faithful : ∀ left right, domain left → domain right → left.addr = right.addr → left = right) + (frontier : SeenFrontier domain seen (expr :: stack)) (exprDomain : domain expr) : + SeenFrontier domain (seen.insert expr.addr) (Children expr ++ stack) := by + intro candidate candidateDomain candidateSeen child childMember + rw [Std.HashSet.contains_insert, Bool.or_eq_true] at candidateSeen + rcases candidateSeen with h | h + · have equality := faithful expr candidate exprDomain candidateDomain (eq_of_beq h) + subst candidate + exact .inr (List.mem_append_left _ childMember) + · rcases frontier candidate candidateDomain h child childMember with childSeen | childPending + · exact .inl (AddressSetLE.insert seen expr.addr _ childSeen) + · rcases List.mem_cons.mp childPending with equality | childPending + · subst child + exact .inl (AddressSetLE.insert_self seen expr.addr) + · exact .inr (List.mem_append_right _ childPending) + +structure Post (domain : KExpr .anon → Prop) (stack : List (KExpr .anon)) + (seen : Std.HashSet Address) (refs result : Array (KId .anon)) where + finalSeen : Std.HashSet Address + locals : SeenLocal domain finalSeen result + frontier : SeenFrontier domain finalSeen [] + seenMonotone : AddressSetLE seen finalSeen + refsMonotone : ∀ id ∈ refs, id ∈ result + covered : ∀ expr ∈ stack, finalSeen.contains expr.addr = true + +theorem go_complete {domain : KExpr .anon → Prop} + (closed : DomainClosed domain) + (faithful : ∀ left right, domain left → domain right → left.addr = right.addr → left = right) : + ∀ (stack : List (KExpr .anon)) (seen : Std.HashSet Address) (refs : Array (KId .anon)), + StackInDomain domain stack → SeenLocal domain seen refs → SeenFrontier domain seen stack → + Nonempty (Post domain stack seen refs (definitionRefs.go stack seen refs)) + | [], seen, refs, _, locals, frontier => by + rw [definitionRefs.go] + exact ⟨⟨seen, locals, frontier, .refl seen, fun _ h => h, by simp⟩⟩ + | expr :: stack, seen, refs, inDomain, locals, frontier => by + have exprDomain := inDomain expr (.head _) + rw [go_step] + split + · rename_i present + obtain ⟨post⟩ := go_complete closed faithful stack seen refs + (fun child member => inDomain child (.tail _ member)) locals (frontier.drop present) + refine ⟨⟨post.finalSeen, post.locals, post.frontier, post.seenMonotone, + post.refsMonotone, ?_⟩⟩ + intro child member + rcases List.mem_cons.mp member with rfl | member + · exact post.seenMonotone _ present + · exact post.covered child member + · obtain ⟨post⟩ := go_complete closed faithful (Children expr ++ stack) + (seen.insert expr.addr) (refs ++ Heads expr) + (by + intro child member + rcases List.mem_append.mp member with member | member + · exact closed expr exprDomain child member + · exact inDomain child (.tail _ member)) + (locals.expand faithful exprDomain) (frontier.expand faithful exprDomain) + refine ⟨⟨post.finalSeen, post.locals, post.frontier, + (AddressSetLE.insert seen expr.addr).trans post.seenMonotone, ?_, ?_⟩⟩ + · intro id member + exact post.refsMonotone id (Array.mem_append.mpr (.inl member)) + · intro child member + rcases List.mem_cons.mp member with equality | member + · subst child + exact post.seenMonotone _ (AddressSetLE.insert_self seen expr.addr) + · exact post.covered child (List.mem_append_right _ member) +termination_by stack _ _ _ _ _ => exprWorkSize stack +decreasing_by + all_goals simp_wf + all_goals first + | simp [exprWorkSize, KExpr.treeSize_pos]; omega + | cases expr <;> simp [Children, exprWorkSize, KExpr.treeSize] <;> omega + +theorem Reference.collected {domain : KExpr .anon → Prop} + {seen : Std.HashSet Address} {refs : Array (KId .anon)} + (closed : DomainClosed domain) (locals : SeenLocal domain seen refs) + (frontier : SeenFrontier domain seen []) {expr : KExpr .anon} {id : KId .anon} + (reference : Reference id expr) (exprDomain : domain expr) + (present : seen.contains expr.addr = true) : id ∈ refs := by + induction reference with + | head member => exact locals _ exprDomain present id member + | @child parent child member nested ih => + have childDomain := closed parent exprDomain child member + have childSeen : seen.contains child.addr = true := by + rcases frontier parent exprDomain present child member with h | h + · exact h + · contradiction + exact ih childDomain childSeen + +/-- Every literal reference beneath an input root occurs in the production +collector's result. Collision freedom is restricted to the supplied finite +run support; it is not a global hash-injectivity premise. -/ +theorem definitionRefs_complete {roots : List (KExpr .anon)} {support : RunSupport} + (coverage : ∀ root ∈ roots, root.ValidationCoverage support) + (collision : support.CollisionFree) {root : KExpr .anon} (member : root ∈ roots) + {id : KId .anon} (reference : Reference id root) : id ∈ definitionRefs roots := by + let domain := fun candidate => ∃ root ∈ roots, root.ValidationReach candidate + have closed : DomainClosed domain := by + rintro parent ⟨root, member, reachable⟩ child childMember + exact ⟨root, member, reachable.trans (Children.reachable childMember)⟩ + have faithful : ∀ left right, domain left → domain right → left.addr = right.addr → left = right := by + rintro left right ⟨leftRoot, leftMember, leftReach⟩ ⟨rightRoot, rightMember, rightReach⟩ equality + have erased := collision.expr ((coverage leftRoot leftMember).expr leftReach) + ((coverage rightRoot rightMember).expr rightReach) equality + simpa only [KExpr.eraseMeta_anon] using erased + obtain ⟨post⟩ := go_complete closed faithful roots {} #[] + (fun root member => ⟨root, member, .refl root⟩) + (by intro expr _ present; simp at present) + (by intro expr _ present; simp at present) + exact reference.collected closed post.locals post.frontier + ⟨root, member, .refl root⟩ (post.covered root member) + +end Ix.Kernel.DefinitionReferences diff --git a/Tests/Ix/Kernel/AnonDiff.lean b/Tests/Ix/Kernel/AnonDiff.lean index 865018fe2..fda9816c7 100644 --- a/Tests/Ix/Kernel/AnonDiff.lean +++ b/Tests/Ix/Kernel/AnonDiff.lean @@ -6,6 +6,7 @@ public import Ix.CompileM public import Ix.KernelCheck public import Ix.Meta public import Ix.Common +public import Tests.Ix.Kernel.DefinitionDependencies /-! Anon verdict differential (`tc-anon-diff`, ignored suite). @@ -122,7 +123,46 @@ def diffSuite : TestSeq := Id.run do return (diff?.isNone, compared, skipped, msg)) .done return ts -public def suite : List TestSeq := [diffSuite] +/-- Both checkers process the exact serialized counterexample, with integrity +verification enabled. Matching error counts alone cannot pass: target sets, +per-target verdicts, and the cycle diagnostics must also agree. -/ +def dependencyFixtureParity (source : Ixon.Env) (expected failures : Nat) : + IO (Bool × Option String) := do + let bytes ← match Ixon.serEnv source with + | .ok bytes => pure bytes + | .error error => return (false, some error) + let directory ← IO.FS.createTempDir + try + let path := directory / "definition-dependencies.ixe" + IO.FS.writeBinFile path bytes + let rust ← Ix.KernelCheck.rsCheckAnonFFI path.toString true "" + let lean ← match checkIxeBytesAnon bytes with + | .ok results => pure results + | .error error => return (false, some error) + let cycle := fun message => ("cyclic definition dependency").isPrefixOf message + let correct := rust.size == expected && lean.size == expected && + (lean.filter (·.err?.isSome)).size == failures && + lean.all fun row => + (row.err?.all cycle) && rust.any fun (address, error) => + address == toString row.addr && error.isSome == row.err?.isSome && + error.all (fun + | .kernelException message => cycle message + | .compileError _ => false) + let rustMessages := rust.map fun (address, error) => + (address, error.map Ix.KernelCheck.CheckError.message) + return (correct, if correct then none else some s!"expected {expected} targets/{failures} cycle failures; Lean {repr lean}; Rust {repr rustMessages}") + finally + IO.FS.removeDirAll directory + +def dependencyDiffSuite : TestSeq := Id.run do + let mut tests : TestSeq := .done + for (label, source, targets, failures) in DefinitionDependencies.parityFixtures do + tests := tests ++ .individualIO s!"anon verdict parity: {label}" none (do + let (passed, message) ← dependencyFixtureParity source targets failures + return (passed, targets, failures, message)) .done + return tests + +public def suite : List TestSeq := [diffSuite, dependencyDiffSuite] end diff --git a/Tests/Ix/Kernel/DefinitionDependencies.lean b/Tests/Ix/Kernel/DefinitionDependencies.lean new file mode 100644 index 000000000..7f73534fb --- /dev/null +++ b/Tests/Ix/Kernel/DefinitionDependencies.lean @@ -0,0 +1,142 @@ +module + +public import Tests.Ix.Kernel.IxonFixtures + +/-! +Definition-cycle regressions through the real content-addressed source loader +and public checker. The only axiom in each negative logical example is `P : Prop`. +-/ + +namespace Tests.Kernel.DefinitionDependencies + +open LSpec Ix.Kernel Tests.Kernel.Fixtures + +private def propSource : Ixon.Env × Address := + storeConst {} ⟨.axio ⟨false, 0, .sort 0⟩, #[], #[], #[.zero]⟩ + +private def cycleSource (kind : Ix.DefKind) (asBlock : Bool) + (value : Ixon.Expr := .recur 0 #[]) (safety : Ix.DefinitionSafety := .safe) + (sharing : Array Ixon.Expr := #[]) : Ixon.Env × Address := + let (source, proposition) := propSource + let declaration : Ixon.Definition := ⟨kind, safety, 0, .ref 0 #[], value⟩ + if asBlock then + let (source, block) := storeMutsWithProjs source + ⟨.muts #[.defn declaration], sharing, #[proposition], #[]⟩ + (source, defnProjAddr block 0) + else + storeConst source ⟨.defn declaration, sharing, #[proposition], #[]⟩ + +private def cyclicError (source : Ixon.Env) (target : Address) + (clearEvery : Nat := 0) : Bool := + match checkEnvAnon source { clearEvery } with + | .ok rows => rows.any fun row => + row.addr == target && match row.err? with + | some message => ("cyclic definition dependency").isPrefixOf message + | _ => false + | .error _ => false + +private def rejectsCycle (fixture : Ixon.Env × Address) : Bool := + cyclicError fixture.1 fixture.2 0 && cyclicError fixture.1 fixture.2 1 + +private def mutualCycle : Ixon.Env × Address := + let (source, proposition) := propSource + storeMutsWithProjs source + ⟨.muts #[ + .defn ⟨.thm, .safe, 0, .ref 0 #[], .recur 1 #[]⟩, + .defn ⟨.opaq, .safe, 0, .ref 0 #[], .recur 0 #[]⟩], + #[], #[proposition], #[]⟩ + +private def typeCycle : Ixon.Env × Address := + let (source, _) := propSource + storeConst source + ⟨.defn ⟨.defn, .safe, 0, .recur 0 #[], .sort 0⟩, #[], #[], #[.zero]⟩ + +private def acyclicSource : Ixon.Env × Address := Id.run do + let (source, proposition) := propSource + let (source, witness) := storeConst source + ⟨.axio ⟨false, 0, .ref 0 #[]⟩, #[], #[proposition], #[]⟩ + -- Forward reference, shared dependency, and mixed safe definition kinds. + -- The let initializer repeats the same shared leaf as its body. + storeMutsWithProjs source + ⟨.muts #[ + .defn ⟨.thm, .safe, 0, .ref 0 #[], .recur 2 #[]⟩, + .defn ⟨.opaq, .safe, 0, .ref 0 #[], .recur 2 #[]⟩, + .defn ⟨.defn, .safe, 0, .ref 0 #[], + .letE true (.ref 0 #[]) (.share 0) (.share 0)⟩], + #[.ref 1 #[]], #[proposition, witness], #[]⟩ + +private def acyclicPasses (clearEvery : Nat) : Bool := + match checkEnvAnon acyclicSource.1 { clearEvery } with + | .ok rows => rows.size == 5 && rows.all (·.err?.isNone) + | .error _ => false + +private def everyMemberSharesFailure : Bool := + let (source, block) := mutualCycle + let action : TcM .anon Bool := do + let mut messages : Array String := #[] + for member in #[0, 1, 0] do + try + TcM.checkConst ⟨defnProjAddr block member, ()⟩ + return false + catch + | .other message => messages := messages.push message + | _ => return false + return messages.size == 3 && messages.all (· == messages[0]!) && + ("cyclic definition dependency").isPrefixOf messages[0]! + match action (TcState.newLazyAnon source) with + | .ok result _ => result + | .error _ _ => false + +private def nonlogicalPasses (safety : Ix.DefinitionSafety) : Bool := + let (source, _) := cycleSource .defn false (.recur 0 #[]) safety + match checkEnvAnon source with + | .ok rows => rows.size == 2 && rows.all (·.err?.isNone) + | .error _ => false + +/-- Serialized-source regressions shared with the Rust differential suite. +The last two fields are the exact target count and expected failure count. -/ +public def parityFixtures : Array (String × Ixon.Env × Nat × Nat) := #[ + ("standalone-cycle", (cycleSource .thm false).1, 2, 1), + ("one-member-cycle", (cycleSource .thm true).1, 2, 1), + ("mutual-cycle", mutualCycle.1, 3, 2), + ("type-cycle", typeCycle.1, 2, 1), + ("shared-cycle", (cycleSource .opaq true (.share 0) .safe #[.recur 0 #[]]).1, 2, 1), + ("acyclic-block", acyclicSource.1, 5, 0), + ("partial-cycle", (cycleSource .defn false (.recur 0 #[]) .part).1, 2, 0), + ("unsafe-cycle", (cycleSource .defn false (.recur 0 #[]) .unsaf).1, 2, 0) +] + +public def suite : List TestSeq := [ + test "definition dependencies: a standalone theorem cannot justify itself" + (rejectsCycle (cycleSource .thm false)) + ++ test "definition dependencies: a one-member mutual theorem cannot justify itself" + (rejectsCycle (cycleSource .thm true)) + ++ test "definition dependencies: safe definitions cannot justify themselves" + (rejectsCycle (cycleSource .defn false)) + ++ test "definition dependencies: opaque values cannot justify themselves" + (rejectsCycle (cycleSource .opaq false)) + ++ test "definition dependencies: a two-member cycle is rejected" + (let (source, block) := mutualCycle; rejectsCycle (source, defnProjAddr block 0)) + ++ test "definition dependencies: circular declaration types are rejected" + (rejectsCycle typeCycle) + ++ test "definition dependencies: let initializers cannot hide a cycle" + (rejectsCycle (cycleSource .thm false + (.letE true (.ref 0 #[]) (.recur 0 #[]) (.recur 0 #[])))) + ++ test "definition dependencies: shared syntax cannot hide a cycle" + (rejectsCycle (cycleSource .thm true (.share 0) .safe #[.recur 0 #[]])) + ++ test "definition dependencies: binder bodies cannot hide a cycle" + (rejectsCycle (cycleSource .defn false + (.leanLam (.ref 0 #[]) (.recur 0 #[])))) + ++ test "definition dependencies: every member replays the same cycle failure" + everyMemberSharesFailure + ++ test "definition dependencies: acyclic forward references and shared leaves check" + (acyclicPasses 0) + ++ test "definition dependencies: acyclic blocks check with clearing at every item" + (acyclicPasses 1) + ++ test "definition dependencies: partial definitions retain their safety policy" + (nonlogicalPasses .part) + ++ test "definition dependencies: unsafe definitions retain their safety policy" + (nonlogicalPasses .unsaf) +] + +end Tests.Kernel.DefinitionDependencies diff --git a/Tests/Main.lean b/Tests/Main.lean index 3650fcb55..e6aca38b7 100644 --- a/Tests/Main.lean +++ b/Tests/Main.lean @@ -41,6 +41,7 @@ import Tests.Ix.Kernel.WhnfTests import Tests.Ix.Kernel.InferDefEq import Tests.Ix.Kernel.CheckTests import Tests.Ix.Kernel.Consistency +import Tests.Ix.Kernel.DefinitionDependencies import Tests.Ix.Kernel.AnonDiff import Tests.Ix.Kernel.InitScale import Tests.Ix.Kernel.TutorialTc @@ -110,6 +111,7 @@ def primarySuites : Std.HashMap String (List LSpec.TestSeq) := .ofList [ ++ Tests.Kernel.Fixtures.suite ++ Tests.Kernel.WhnfTests.suite ++ Tests.Kernel.InferDefEq.suite ++ Tests.Kernel.CheckTests.suite ++ Tests.Kernel.Consistency.suite + ++ Tests.Kernel.DefinitionDependencies.suite ++ Tests.Kernel.Roundtrip.unitTests ++ Tests.Kernel.IngressMeta.unitTests), ] diff --git a/crates/kernel/src/check.rs b/crates/kernel/src/check.rs index e6a71854c..6dd5f563a 100644 --- a/crates/kernel/src/check.rs +++ b/crates/kernel/src/check.rs @@ -47,6 +47,44 @@ struct ValidationTiming { univ: Duration, } +// Keep this bound and traversal order in sync with DefinitionDependencies.lean. +const MAX_DEFINITION_DEPENDENCY_STEPS: usize = 1_000_000; + +enum DefinitionDependencyTask { + Enter(KId), + Finish(KId, KConst), +} + +/// References in both the type and value, including projection heads. The +/// expression worklist visits shared syntax once without host-stack recursion. +fn definition_dependencies(c: &KConst) -> Vec> { + let KConst::Defn { ty, val, .. } = c else { return vec![] }; + let mut pending = vec![val, ty]; + let mut seen: FxHashSet = FxHashSet::default(); + let mut refs = vec![]; + while let Some(expr) = pending.pop() { + if !seen.insert(expr.hash_key()) { + continue; + } + match expr.data() { + ExprData::Const(id, ..) => refs.push(id.clone()), + ExprData::App(fun, arg, _) => pending.extend([arg, fun]), + ExprData::Lam(_, _, dom, body, _) | ExprData::All(_, _, dom, body, _) => { + pending.extend([body, dom]) + }, + ExprData::Let(_, ty, val, body, _, _) => { + pending.extend([body, val, ty]); + }, + ExprData::Prj(id, _, major, _) => { + refs.push(id.clone()); + pending.push(major); + }, + _ => {}, + } + } + refs +} + #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum CheckBlockKind { Defn, @@ -660,6 +698,7 @@ impl TypeChecker<'_, M> { if let (Some(t), Some(start)) = (timing.as_deref_mut(), val_start) { t.val += start.elapsed(); } + self.check_definition_dependencies(c)?; }, KConst::Recr { rules, .. } => { let rules_start = timing.as_ref().map(|_| Instant::now()); @@ -683,6 +722,61 @@ impl TypeChecker<'_, M> { Ok(()) } + /// A reference's declared type does not justify a circular definition. + /// Follow every reachable definition, while treating declarations with their + /// own admission rules (axioms, inductives, constructors, recursors) as leaves. + fn check_definition_dependencies( + &mut self, + c: &KConst, + ) -> Result<(), TcError> { + if !matches!(c, KConst::Defn { safety: DefinitionSafety::Safe, .. }) { + return Ok(()); + } + let mut pending: Vec<_> = definition_dependencies(c) + .into_iter() + .rev() + .map(DefinitionDependencyTask::Enter) + .collect(); + let mut active: FxHashSet
= FxHashSet::default(); + let mut finished: FxHashSet
= FxHashSet::default(); + for _ in 0..MAX_DEFINITION_DEPENDENCY_STEPS { + let Some(task) = pending.pop() else { return Ok(()) }; + match task { + DefinitionDependencyTask::Enter(id) => { + if finished.contains(&id.addr) { + continue; + } + if active.contains(&id.addr) { + return Err(TcError::Other(format!( + "cyclic definition dependency at {id}" + ))); + } + let declaration = self.get_const(&id)?; + let dependencies = definition_dependencies(&declaration); + active.insert(id.addr.clone()); + pending.push(DefinitionDependencyTask::Finish(id, declaration)); + pending.extend( + dependencies.into_iter().rev().map(DefinitionDependencyTask::Enter), + ); + }, + DefinitionDependencyTask::Finish(id, declaration) => { + if finished.contains(&id.addr) + || !definition_dependencies(&declaration) + .iter() + .all(|dependency| finished.contains(&dependency.addr)) + { + return Err(TcError::Other(format!( + "incomplete definition dependencies at {id}" + ))); + } + active.remove(&id.addr); + finished.insert(id.addr); + }, + } + } + Err(TcError::MaxRecDepth) + } + fn phase_timing_label_matches(&self, id: &KId) -> bool { match crate::env_var("IX_KERNEL_DEBUG_CONST") { Ok(filter) if filter.is_empty() => true, @@ -1803,6 +1897,195 @@ mod tests { ); } + fn dependency_def( + id: &KId, + ty: AE, + val: AE, + safety: DefinitionSafety, + kind: DefKind, + ) -> KConst { + KConst::Defn { + name: (), + level_params: (), + kind, + safety, + hints: ReducibilityHints::Regular(0), + lvls: 0, + ty, + val, + lean_all: (), + block: id.clone(), + } + } + + fn insert_proposition(env: &mut KEnv) -> AE { + let id = mk_id("P"); + env.insert( + id.clone(), + KConst::Axio { + name: (), + level_params: (), + is_unsafe: false, + lvls: 0, + ty: sort0(), + }, + ); + AE::cnst(id, Box::new([])) + } + + #[test] + fn definition_dependency_rejects_self_justifying_theorem() { + let mut env = KEnv::::new(); + let prop = insert_proposition(&mut env); + let id = mk_id("loop"); + env.insert( + id.clone(), + dependency_def( + &id, + prop, + AE::cnst(id.clone(), Box::new([])), + DefinitionSafety::Safe, + DefKind::Theorem, + ), + ); + let error = TypeChecker::new(&mut env).check_const(&id).unwrap_err(); + assert!(error.to_string().contains("cyclic definition dependency")); + } + + #[test] + fn definition_dependency_rejects_mutual_cycles_in_every_safe_kind() { + for kind in [DefKind::Definition, DefKind::Theorem, DefKind::Opaque] { + let mut env = KEnv::::new(); + let prop = insert_proposition(&mut env); + let left = mk_id("left"); + let right = mk_id("right"); + for (id, target) in [(&left, &right), (&right, &left)] { + env.insert( + id.clone(), + dependency_def( + id, + prop.clone(), + AE::cnst(target.clone(), Box::new([])), + DefinitionSafety::Safe, + kind, + ), + ); + } + let mut tc = TypeChecker::new(&mut env); + for id in [&left, &right] { + let error = tc.check_const(id).unwrap_err(); + assert!(error.to_string().contains("cyclic definition dependency")); + } + } + } + + #[test] + fn definition_dependency_checks_types_and_hidden_values() { + let id = mk_id("hidden_cycle"); + let self_ref = AE::cnst(id.clone(), Box::new([])); + let cases = [ + (self_ref.clone(), sort0()), + (sort0(), AE::lam((), (), sort0(), self_ref.clone())), + (sort0(), AE::let_((), sort0(), self_ref.clone(), sort0(), true)), + (sort0(), AE::all((), (), self_ref.clone(), sort0())), + (sort0(), AE::app(self_ref.clone(), self_ref)), + ]; + for (ty, val) in cases { + let mut env = KEnv::::new(); + env.insert( + id.clone(), + dependency_def( + &id, + ty, + val, + DefinitionSafety::Safe, + DefKind::Definition, + ), + ); + let error = TypeChecker::new(&mut env).check_const(&id).unwrap_err(); + assert!(error.to_string().contains("cyclic definition dependency")); + } + } + + #[test] + fn definition_dependency_preserves_partial_and_unsafe_policy() { + for safety in [DefinitionSafety::Partial, DefinitionSafety::Unsafe] { + let mut env = KEnv::::new(); + let prop = insert_proposition(&mut env); + let id = mk_id("nonlogical_loop"); + env.insert( + id.clone(), + dependency_def( + &id, + prop, + AE::cnst(id.clone(), Box::new([])), + safety, + DefKind::Definition, + ), + ); + TypeChecker::new(&mut env).check_const(&id).unwrap(); + } + } + + #[test] + fn definition_dependency_accepts_acyclic_forward_shared_dependencies() { + let mut env = KEnv::::new(); + let prop = insert_proposition(&mut env); + let witness = mk_id("p"); + env.insert( + witness.clone(), + KConst::Axio { + name: (), + level_params: (), + is_unsafe: false, + lvls: 0, + ty: prop.clone(), + }, + ); + let left = mk_id("left"); + let right = mk_id("right"); + let shared = mk_id("shared"); + let block = mk_id("acyclic_block"); + for (id, value) in [ + (&left, AE::cnst(right.clone(), Box::new([]))), + (&right, AE::cnst(shared.clone(), Box::new([]))), + (&shared, AE::cnst(witness, Box::new([]))), + ] { + let mut declaration = dependency_def( + id, + prop.clone(), + value, + DefinitionSafety::Safe, + DefKind::Theorem, + ); + if let KConst::Defn { block: target, .. } = &mut declaration { + *target = block.clone(); + } + env.insert(id.clone(), declaration); + } + env.insert_block(block, vec![left.clone(), right.clone(), shared.clone()]); + let mut tc = TypeChecker::new(&mut env); + for id in [&left, &shared, &right, &left] { + tc.check_const(id).unwrap(); + } + } + + #[test] + fn definition_dependency_collects_projection_heads() { + let id = mk_id("root"); + let head = mk_id("projection_head"); + let major = mk_id("major"); + let declaration = dependency_def( + &id, + sort0(), + AE::prj(head.clone(), 0, AE::cnst(major.clone(), Box::new([]))), + DefinitionSafety::Safe, + DefKind::Definition, + ); + let refs = super::definition_dependencies(&declaration); + assert_eq!(refs, vec![head, major]); + } + #[test] fn checking_one_definition_checks_sibling_block() { let mut env = KEnv::::new(); diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index e7c278dfa..c501ead3b 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -365,6 +365,25 @@ def useSort.{u} (A : Sort u) (a : A) : A := idSort.{u} A a including cache clearing. `AtomicDefinitionRun.no_self_alias` proves that a fresh definition cannot justify its own type by referring to itself. +- Safe definition admission now checks the reachable definition graph in both + Lean and Rust. Without this guard, the content-addressed source loader and + checker accepted `theorem loop : P := loop` with only `P : Prop` as an axiom, + including through a one-member mutual block. The new traversal follows type + and value references under applications, binders, and lets, and includes + projection heads. It rejects cycles and exhaustion of its shared one-million + step bound. Axioms, inductives, constructors, and recursors are leaves with + separate admission rules; partial and unsafe definitions retain their safety + policy. Acyclic mutual definitions remain supported. +- `DefinitionDependencies.order_sound` derives a concrete dependency order from + the production walk. Every entry is fresh and its collected dependencies + precede it; `Ordered.wellFounded` derives a decreasing natural-number rank. + The certificate covers the requested roots and can retain a lookup agreement + relation. `DefinitionReferences.definitionRefs_complete` proves that memoization + cannot omit a syntax reference, under finite-run collision freedom. + `DefinitionBodyTrace.dependencyOrder` extracts the actual walk from successful + safe validation, and `.referencesIn` connects that order to the references of + the model's reading. Admission of every entry in that order still needs the + broader body-typing and model-extension proofs. - `checkEnvAnon` returns `.ok results` **and every result row has no error**. The outer `.ok` alone does not mean that the declarations passed. @@ -396,7 +415,7 @@ constructors. They compare exact axiom sets and record direct origins of metatheory. The named-specification audit checks 441 assertions against exact dependency manifests. -The same traversal covers 2,034 kernel manifest roots. Direct dependency +The same traversal covers 2,037 kernel manifest roots. Direct dependency lookups are cached within a fixed environment, while each root's reachable declarations, axioms, and proof-hole origins are computed separately. @@ -429,7 +448,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 355 exact theorem boundaries. The production +The consistency target checks 367 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -519,7 +538,13 @@ later Prop/Type instances, exact loaded arities, and unused parameters. They reject undeclared parameters, out-of-range value parameters, different in-range parameters in place of the declared type, non-Prop theorem types, and missing arguments for unused parameters. -The unit suite contains 564 checks. +Definition-cycle regressions use content-addressed standalone and mutual +declarations, including a self-justifying theorem, a two-member cycle, type +cycles, lets, shared syntax, and binders. They check repeated member failures, +acyclic forward references, cache clearing, and the partial/unsafe policy. +The unit suite contains 578 checks. The anonymous differential additionally +serializes eight cycle-policy fixtures and checks exact target sets, verdicts, +failure counts, and cycle diagnostics in both implementations. ## Certified host adapters diff --git a/docs/theory.md b/docs/theory.md index e072ea2ee..1d18f0e2a 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -31,6 +31,13 @@ retain the exact declared universe count and denote the checked body at every universe instance. Application spines headed by locals or admitted polymorphic constants derive their type's validity from the context or dependency model, then check arguments and substitute the dependent result. +Safe definition admission also rejects circular justification in both Lean and +Rust, including `theorem loop : P := loop` with only `P : Prop` assumed. The +production dependency walk returns an order with a proved decreasing rank; +finite collision freedom ensures its memoized collector includes every syntax +reference. Successful validation exposes that order to the model-reference +proof. Constructing interpretations for all coordinated declarations remains +part of the full checker refinement. Constant cache hits derive the same typing from concrete agreement with pure universe substitution of a loaded declaration; sort hits use the canonical successor sort. Sort and already-loaded constant inference preserve agreement From 064301c7e8e97b96d69a7bb98dc8b1a80a6056b4 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 00:26:21 -0400 Subject: [PATCH 23/63] Derive inferred type formation for lambda applications --- Ix/Kernel/Verify/Consistency.lean | 16 +- Ix/Kernel/Verify/Consistency/Audit.lean | 22 +- Ix/Kernel/Verify/Consistency/Environment.lean | 80 ++++++ Ix/Kernel/Verify/Consistency/Formation.lean | 179 ++++++++++++ Ix/Kernel/Verify/Consistency/Production.lean | 84 +++++- .../Consistency/SynthesisInference.lean | 266 ++++++++++++++++++ Ix/Theory/Model/LevelCongruence.lean | 10 + Ix/Theory/Model/UniverseBounds.lean | 137 +++++++++ Tests/Ix/Kernel/Consistency.lean | 100 +++++++ Tests/Theory/ImportManifest.lean | 3 +- docs/kernel-verification.md | 40 ++- docs/theory.md | 11 + 12 files changed, 934 insertions(+), 14 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/Formation.lean create mode 100644 Ix/Kernel/Verify/Consistency/SynthesisInference.lean create mode 100644 Ix/Theory/Model/UniverseBounds.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 41fa49dab..98624d8f3 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -25,6 +25,8 @@ import Ix.Kernel.Verify.Consistency.Context import Ix.Kernel.Verify.Consistency.BinderOpening import Ix.Kernel.Verify.Consistency.Application import Ix.Kernel.Verify.Consistency.BinderInference +import Ix.Kernel.Verify.Consistency.Formation +import Ix.Kernel.Verify.Consistency.SynthesisInference import Ix.Kernel.Verify.Consistency.Validation import Ix.Kernel.Verify.Consistency.RecursiveCache import Ix.Kernel.Verify.Consistency.RecursiveState @@ -61,15 +63,23 @@ The returned type's scope and references justify declaration admission. The binder case retains references to the preceding interface. Actual production validation and the closed scoped reading derive source scope from finite validation coverage and collision freedom. Auxiliary condition bounds remain -a separate syntax check. The declared-type inference from that same execution turns semantic checking -into typing. Local cache hits agree with the actual declaration type. Constant +a separate syntax check. Declared-type inference from that same execution can +turn semantic checking into typing. Synthesis inference also derives formation +of its generated result type from actual binder-domain and earlier declaration +type checks. An inhabited product supplies a uniform codomain bound at an +application, preserving the exact Prop condition. Direct lambda applications +therefore synthesize full typing without an extra codomain inference call. +Earlier type checks remain reusable after interface growth and universe +instantiation. Local cache hits agree with the actual declaration type. Constant hits agree with pure universe substitution of a loaded, admitted declaration; sort hits return the canonical successor sort. Application, forall, and lambda nodes retain misses in every eligible cache partition. Full mode leaves the inference-only partition unconstrained at a miss. Applications use syntactic Pi exposure, full argument checking, hash conversion, and arguments without eager-reduction markers. Constant- and local-headed spines -derive type validity from the admitted model or local context; their arguments may be lambdas. +can derive type validity from the admitted model or local context; their +arguments may be lambdas. The synthesis rules also allow lambdas and their +application results in function position, with derived formation bounds. Polymorphic nodes use closed source readings, finite substitution resources, and a pure prediction of the returned syntax with matching occurrence annotations. Constant hits additionally check arity because they skip the runtime guard. diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 749e88721..f68345b59 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -50,7 +50,22 @@ private def atomicRoots : Array Lean.Name := #[ ``ForallInferenceTrace.output, ``LambdaInferenceTrace.output, ``BinderInference.sound, ``inferUncached_monomorphic_const_scoped, ``ApplicationInferenceTrace.output, ``BinderInference.soundWithSynthesis, ``BinderInference.synthesis, - ``DefinitionBodySupport.sound, ``DefinitionBodyTrace.scopes, ``DefinitionBodyTrace.binderSupport + ``CheckedType.sound, ``TypeFormation.sound, + ``SynthesisInference.sound, ``SynthesisInference.closed_sound, ``SynthesisInference.ofSort, + ``SynthesisTypeCheck.sound, ``SynthesisInference.ofTypeCheck, + ``DefinitionBodySupport.sound, ``DefinitionBodyTrace.scopes, ``DefinitionBodyTrace.binderSupport, + ``DefinitionBodyTrace.checkedType, ``DefinitionBodyTrace.synthesisTypeCheck, + ``DefinitionBodyTrace.synthesisSupport, ``AxiomTypeTrace.synthesisTypeCheck +] + +private def formationRoots : Array Lean.Name := #[ + ``Theory.Model.piSet_fibre_subset, ``Theory.Model.IsTGUniverse.piSet_fibre_mem, + ``Theory.Model.zeroCondition_conditionLevel, ``Theory.Model.zeroCondition_applicationLevel, + ``Theory.Model.applicationLevel_zero, ``Theory.Model.applicationLevel_ge, + ``Theory.Model.TypingClaim.applicationType, ``Theory.Model.AExpr.LevelEquivalent.termTyping, + ``InterfaceExtends.realizes, ``InterfaceExtends.typing, + ``context_valid_tail, ``typing_weaken, ``typing_instL_closed, + ``ContextFormation.empty, ``ContextFormation.push ] private def instantiationRoots : Array Lean.Name := #[ @@ -201,6 +216,7 @@ private def sourceCacheInitialRoots : Array Lean.Name := #[ ] private def productionRoots : Array Lean.Name := #[ + ``axiom_type_trace, ``AxiomObservation.synthesisTypeCheck, ``StandalonePrefix.definitionTypeCheck, ``StandalonePrefix.member_success, ``definition_body_trace, ``AtomicDefinitionRun.sound, ``AtomicDefinitionRun.no_self_alias, ``WorkPosition.check_success, ``AtomicDefinitionPlan.extends, @@ -240,6 +256,8 @@ private def forbiddenProduction : Array Lean.Name := #[ ] def roots : Array RootAllowance := #[ + { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, + { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, { root := ``DefinitionDependencies.Ordered.closed, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction }, { root := ``DefinitionDependencies.Ordered.ranked, standardAxioms := standard, @@ -352,7 +370,7 @@ def roots : Array RootAllowance := #[ forbiddenDependencies := forbiddenProduction }) ++ (scopedRoots ++ cacheFrameRoots).map (fun root => { root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction -}) ++ (contextRoots ++ cacheMapRoots ++ sourceOwnershipRoots).map (fun root => { +}) ++ (contextRoots ++ cacheMapRoots ++ sourceOwnershipRoots ++ formationRoots).map (fun root => { root, standardAxioms := standard, forbiddenDependencies := forbiddenProduction }) ++ (binderWalkerRoots ++ cacheKeyRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative], diff --git a/Ix/Kernel/Verify/Consistency/Environment.lean b/Ix/Kernel/Verify/Consistency/Environment.lean index 457cae183..7b5d2790a 100644 --- a/Ix/Kernel/Verify/Consistency/Environment.lean +++ b/Ix/Kernel/Verify/Consistency/Environment.lean @@ -305,6 +305,57 @@ def AxiomSpec.constant {β : Type u} (spec : AxiomSpec β) : KConst .anon := def AxiomSpec.entry {β : Type u} (spec : AxiomSpec β) : ConstantEntry β := { universes := spec.universes.toNat, type := spec.type, body := none } +/-- The axiom's type check is an actual prefix of member validation, just +as a definition's type check is. Its eventual interpretation remains the +user-supplied axiom-model premise. -/ +structure AxiomTypeTrace {β : Type u} (spec : AxiomSpec β) (methods : Methods .anon) + (before : TcState .anon) where + validated : TcState .anon + inferred : KExpr .anon + typeState : TcState .anon + level : KUniv .anon + afterSort : TcState .anon + validationRun : (RecM.validateConstWellScoped spec.constant).run methods before = .ok () validated + typeRun : (RecM.infer spec.sourceType).run methods validated = .ok inferred typeState + sortRun : (RecM.ensureSortDirect inferred).run methods typeState = .ok level afterSort + +private theorem axiom_bind_success {α γ : Type} {action : TcM .anon α} {next : α → TcM .anon γ} + {before after : TcState .anon} {result : γ} + (accepted : EStateM.bind action next before = .ok result after) : + ∃ value state, action before = .ok value state ∧ next value state = .ok result after := by + cases run : action before with + | error err failed => rw [EStateM.bind, run] at accepted; contradiction + | ok value state => + rw [EStateM.bind, run] at accepted + exact ⟨value, state, rfl, accepted⟩ + +theorem axiom_type_trace {β : Type u} {spec : AxiomSpec β} {methods : Methods .anon} + {before after : TcState .anon} + (accepted : (RecM.checkConstMember spec.id spec.constant).run methods before = .ok () after) : + Nonempty (AxiomTypeTrace spec methods before) := by + unfold RecM.checkConstMember at accepted + simp only [AxiomSpec.constant, Mode.F.hasDups, Bool.false_eq_true, if_false, + ReaderT.run_bind] at accepted + change EStateM.bind ((RecM.validateConstWellScoped spec.constant).run methods) _ before = _ at accepted + obtain ⟨⟨⟩, validated, validationRun, accepted⟩ := axiom_bind_success accepted + change EStateM.bind ((RecM.infer spec.sourceType).run methods) _ validated = _ at accepted + obtain ⟨inferred, typeState, typeRun, accepted⟩ := axiom_bind_success accepted + change EStateM.bind ((RecM.ensureSortDirect inferred).run methods) _ typeState = _ at accepted + obtain ⟨level, afterSort, sortRun, _⟩ := axiom_bind_success accepted + exact ⟨⟨validated, inferred, typeState, level, afterSort, validationRun, typeRun, sortRun⟩⟩ + +/-- Store formation from the exact inference call made while admitting this +axiom. The source reading and finite inference tree establish its meaning. -/ +def AxiomTypeTrace.synthesisTypeCheck {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {spec : AxiomSpec β} {fuel : Nat} {before : TcState .anon} + (trace : AxiomTypeTrace spec (methodsN fuel) before) {level bound : VLevel} + (inference : SynthesisInference resolve entries [] [] [] fuel trace.validated spec.sourceType + spec.type (.sort level) bound) + (reading : readScopedExpr? resolve [] spec.sourceType = some spec.type.erase) : + SynthesisTypeCheck resolve entries spec.type level := + { fuel, before := trace.validated, after := trace.typeState, source := spec.sourceType, + result := trace.inferred, bound, inference, reading, run := trace.typeRun } + /-- Exactly the declared axiom interface, starting with no other entries. -/ def axiomEnvironment {β : Type u} [DecidableEq β] : List (AxiomSpec β) → Model.Environment β | [] => fun _ => none @@ -322,6 +373,35 @@ structure AxiomObservation {β : Type u} (env : Ixon.Env) (cfg : CheckCfg) installed : entries spec.ref = some spec.entry reads : readExpr? resolve spec.sourceType = some spec.type.erase +/-- Successful rows from this environment run supply the axiom's executed +type check. The caller supplies only its finite inference support and scoped +source reading, not another successful inference call or a semantic judgment. -/ +theorem AxiomObservation.synthesisTypeCheck {β : Type u} + {env : Ixon.Env} {cfg : CheckCfg} {work : Array AnonWorkItem} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {spec : AxiomSpec β} + (observation : AxiomObservation env cfg work resolve entries spec) {level bound : VLevel} + (inference : ∀ trace : AxiomTypeTrace spec + (methodsN (observation.position.state env cfg).checker.recFuel.toNat) observation.path.ready, + Nonempty (SynthesisInference resolve entries [] [] [] + (observation.position.state env cfg).checker.recFuel.toNat trace.validated spec.sourceType + spec.type (.sort level) bound)) + (reading : readScopedExpr? resolve [] spec.sourceType = some spec.type.erase) + (enumerated : buildAnonWork env = .ok work) + {results : Array CheckResult} (accepted : checkEnvAnon env cfg = .ok results) + (succeeded : ∀ result ∈ results, result.err? = none) : + Nonempty (SynthesisTypeCheck resolve entries spec.type level) := by + have serial : ∀ result ∈ (runAnonCheckList cfg work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none := by + unfold checkEnvAnon at accepted + rw [enumerated] at accepted + cases accepted + exact succeeded + obtain ⟨after, run⟩ := observation.position.check_success serial + have member := observation.path.member_success (by simpa only [anon_id] using run) + obtain ⟨trace⟩ := axiom_type_trace member + obtain ⟨tree⟩ := inference trace + exact ⟨trace.synthesisTypeCheck tree reading⟩ + /-- Complete support for the selected production environment fragment. Every source key is checked, every work item is represented, and the axiom interface contains exactly the listed source axioms. All definition resources diff --git a/Ix/Kernel/Verify/Consistency/Formation.lean b/Ix/Kernel/Verify/Consistency/Formation.lean new file mode 100644 index 000000000..ca765dcf7 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Formation.lean @@ -0,0 +1,179 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BinderInference + +/-! +Type formation comes from actual inference of a type, including the type +check performed while admitting a declaration. These results preserve that +information when the dependency interface grows, a local binder is opened, +or a closed declaration is instantiated at new universe levels. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v +variable {β : Type u} + +/-- Every earlier declaration, with all its fields, remains in the later +interface. This is a syntactic map relation, not a realization assumption. -/ +def InterfaceExtends (earlier later : Model.Environment β) : Prop := + ∀ ref entry, earlier ref = some entry → later ref = some entry + +theorem InterfaceExtends.refl (entries : Model.Environment β) : InterfaceExtends entries entries := + fun _ _ found => found + +theorem InterfaceExtends.trans {first second third : Model.Environment β} + (left : InterfaceExtends first second) (right : InterfaceExtends second third) : + InterfaceExtends first third := fun ref entry found => right ref entry (left ref entry found) + +theorem InterfaceExtends.realizes {earlier later : Model.Environment β} + (extension : InterfaceExtends earlier later) {V : Type v} [SetTheory V] + {constants : Assignment β V} (realizes : Realizes constants later) : Realizes constants earlier := by + constructor + · intro ref entry found + exact realizes.typeValid ref entry (extension ref entry found) + · intro ref entry found + exact realizes.member ref entry (extension ref entry found) + · intro ref entry found + exact realizes.bodyValid ref entry (extension ref entry found) + · intro ref entry found + exact realizes.bodyValue ref entry (extension ref entry found) + · intro ref entry found + exact realizes.equationValue ref entry (extension ref entry found) + · intro ref entry found + exact realizes.factMeaning ref entry (extension ref entry found) + +theorem InterfaceExtends.typing {earlier later : Model.Environment β} + (extension : InterfaceExtends earlier later) {context : Model.Context β} {term type : AExpr β} + (typed : TypingClaim.{u,v} earlier context term type) : + TypingClaim.{u,v} later context term type := by + intro V _ constants realizes levels env valid + exact typed V constants (extension.realizes realizes) levels env valid + +theorem context_valid_tail {V : Type v} [SetTheory V] + {constants : Assignment β V} {levels : List Nat} {context : Model.Context β} + {domain : AExpr β} {env : Nat → V} (valid : (context.push domain).Valid constants levels env) : + context.Valid constants levels (Valuation.skip 1 0 env) := by + intro index type found + have after := valid (index + 1) (type.liftN 1) (by + simp only [Context.push, List.getElem?_cons_succ, List.getElem?_map, found, Option.map_some]) + simpa only [wellDenoted_liftN, interp_liftN, Valuation.skip, Nat.not_lt_zero, ↓reduceIte, + Nat.add_comm 1 index] using after + +theorem typing_weaken {entries : Model.Environment β} {context : Model.Context β} + {term type domain : AExpr β} (typed : TypingClaim.{u,v} entries context term type) : + TypingClaim.{u,v} entries (context.push domain) (term.liftN 1) (type.liftN 1) := by + intro V _ constants realizes levels env valid + have result := typed V constants realizes levels _ (context_valid_tail valid) + simpa only [wellDenoted_liftN, interp_liftN] using result + +/-- Closed type checks remain valid in any local context and at every +universe instance. The original assignment already realizes all instances +of every declaration in the dependency interface. -/ +theorem typing_instL_closed {entries : Model.Environment β} {context : Model.Context β} + {term type : AExpr β} (typed : TypingClaim.{u,v} entries [] term type) (arguments : List VLevel) : + TypingClaim.{u,v} entries context (term.instL arguments) (type.instL arguments) := by + intro V _ constants realizes levels env _ + have result := typed V constants realizes (arguments.map (VLevel.eval levels)) env + (Context.valid_nil constants _ env) + simpa only [wellDenoted_instL, interp_instL] using result + +/-- Evidence of an executed type-inference call. The witness stores raw +syntax, states, and a finite inference tree; it contains no semantic typing +field. Its conclusion is derived below from production soundness. -/ +structure CheckedType (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (context : Model.Context β) (type : AExpr β) (level : VLevel) where + locals : List FVarId + fuel : Nat + before : TcState .anon + after : TcState .anon + source : KExpr .anon + result : KExpr .anon + inference : BinderInference resolve entries locals context fuel before source type (.sort level) + agreement : LocalContextReading resolve locals before.lctx context + reading : readScopedExpr? resolve locals source = some type.erase + run : RecM.infer source (methodsN fuel) before = .ok result after + +theorem CheckedType.sound {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {type : AExpr β} {level : VLevel} + (checked : CheckedType resolve entries context type level) : + TypingClaim.{u,v} entries context type (.sort level) := + (checked.inference.sound checked.agreement checked.reading checked.run).2.typingSort + +/-- Formation facts retain their executed origin while being transported +through the operations needed by later inference. -/ +inductive TypeFormation (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → AExpr β → VLevel → Type u where + | checked {entries context type level} + (origin : CheckedType resolve entries context type level) : + TypeFormation resolve entries context type level + | sort {entries context} (level : VLevel) : + TypeFormation resolve entries context (.sort level) (.succ level) + | extend {earlier later context type level} + (extension : InterfaceExtends earlier later) + (prior : TypeFormation resolve earlier context type level) : + TypeFormation resolve later context type level + | weaken {entries context type level} (domain : AExpr β) + (prior : TypeFormation resolve entries context type level) : + TypeFormation resolve entries (context.push domain) (type.liftN 1) level + | instantiate {entries context type level} (arguments : List VLevel) + (prior : TypeFormation resolve entries [] type level) : + TypeFormation resolve entries context (type.instL arguments) (level.inst arguments) + | equivalent {entries context type type' level} + (same : AExpr.LevelEquivalent type type') + (prior : TypeFormation resolve entries context type level) : + TypeFormation resolve entries context type' level + | forallE {entries context domain body domainLevel bodyLevel} + (domainFormation : TypeFormation resolve entries context domain domainLevel) + (bodyFormation : TypeFormation resolve entries (context.push domain) body bodyLevel) : + TypeFormation resolve entries context + (.forallE (Certified.zeroCondition bodyLevel) domain body) (.imax domainLevel bodyLevel) + +theorem TypeFormation.sound {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {type : AExpr β} {level : VLevel} + (formation : TypeFormation resolve entries context type level) : + TypingClaim.{u,v} entries context type (.sort level) := by + induction formation with + | checked origin => exact origin.sound + | sort level => exact TypingClaim.sort level + | extend extension prior ih => exact extension.typing ih + | weaken domain prior ih => exact typing_weaken ih + | instantiate arguments prior ih => exact typing_instL_closed ih arguments + | equivalent same prior ih => exact same.termTyping ih + | forallE domain body ihDomain ihBody => exact TypingClaim.forallE ihDomain ihBody rfl + +/-- The levels carried for local declarations refer to their types, all +expressed in the current context. Binder inference derives each new entry +from the executed domain check. -/ +def ContextFormation (entries : Model.Environment β) (context : Model.Context β) + (bounds : List VLevel) : Prop := + ∀ (index : Nat) type level, context[index]? = some type → bounds[index]? = some level → + TypingClaim.{u,v} entries context type (.sort level) + +theorem ContextFormation.empty (entries : Model.Environment β) : + ContextFormation.{u,v} entries [] [] := by + intro index type level found + simp at found + +theorem ContextFormation.push {entries : Model.Environment β} {context : Model.Context β} + {bounds : List VLevel} {domain : AExpr β} {level : VLevel} + (formed : ContextFormation.{u,v} entries context bounds) + (domainFormed : TypingClaim.{u,v} entries context domain (.sort level)) : + ContextFormation.{u,v} entries (context.push domain) (level :: bounds) := by + intro index type bound found indexed + cases index with + | zero => + simp only [Context.push, List.getElem?_cons_zero, Option.some.injEq] at found indexed + subst type bound + exact typing_weaken domainFormed + | succ index => + simp only [Context.push, List.getElem?_cons_succ, List.getElem?_map] at found + obtain ⟨prior, priorFound, rfl⟩ := Option.map_eq_some_iff.mp found + exact typing_weaken (formed index prior bound priorFound indexed) + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Production.lean b/Ix/Kernel/Verify/Consistency/Production.lean index 6893b6eaf..7cb7f2000 100644 --- a/Ix/Kernel/Verify/Consistency/Production.lean +++ b/Ix/Kernel/Verify/Consistency/Production.lean @@ -5,7 +5,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Driver import Ix.Kernel.Verify.Consistency.Constant -import Ix.Kernel.Verify.Consistency.BinderInference +import Ix.Kernel.Verify.Consistency.SynthesisInference import Ix.Kernel.Verify.Consistency.Validation /-! @@ -129,7 +129,8 @@ private theorem expressionScope_mono {β : Type u} {term : AExpr β} | natLit => trivial /-- Closed sort/alias inference, a specialization of an existing constant, -or a finite binder inference tree with a separately checked declared type. +a finite binder inference tree with a separately checked declared type, or +inference that derives formation of its own returned type. Specializations supply raw syntax and occurrence annotations; successful inference derives their typing, scope, and references. Universe arguments and binder conditions may use the declaration's own parameters. Binder definitions @@ -164,6 +165,14 @@ inductive DefinitionBodySupport {β : Type u} (scope : body.Scope universes 0 ∧ type.Scope universes 0) (references : body.ReferencesIn entries ∧ type.ReferencesIn entries) : DefinitionBodySupport resolve entries methods before declared universes term body type + | synthesis {fuel : Nat} {term : KExpr .anon} {body type : AExpr β} {level : VLevel} + (tied : methods = methodsN fuel) + (valueInference : SynthesisInference resolve entries [] [] [] fuel before term body type level) + (valueReading : readScopedExpr? resolve [] term = some body.erase) + (typeReading : readScopedExpr? resolve [] declared = some type.erase) + (scope : body.Scope universes 0 ∧ type.Scope universes 0) + (references : body.ReferencesIn entries ∧ type.ReferencesIn entries) : + DefinitionBodySupport resolve entries methods before declared universes term body type theorem DefinitionBodySupport.sound {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} @@ -214,6 +223,11 @@ theorem DefinitionBodySupport.sound {β : Type u} valueReading accepted exact ⟨readScopedExpr?_closed valueReading, readScopedExpr?_closed typeReading, scope.1, scope.2, references.1, references.2, valueChecked.typing typeChecked.typingSort⟩ + | synthesis tied valueInference valueReading typeReading scope references => + subst methods + obtain ⟨_, valueTyped, _⟩ := valueInference.closed_sound valueReading accepted + exact ⟨readScopedExpr?_closed valueReading, readScopedExpr?_closed typeReading, + scope.1, scope.2, references.1, references.2, valueTyped⟩ /-- The execution prefix through value conversion. A successful member check also passes the subsequent safety checks. -/ @@ -328,6 +342,72 @@ def DefinitionBodyTrace.binderSupport {β : Type u} {input : DefinitionInput} valueConditions typeConditions) references +/-- Reuse the exact type inference performed by this declaration admission. +No extra inference on a generated codomain is postulated. -/ +def DefinitionBodyTrace.checkedType {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {input : DefinitionInput} {fuel : Nat} + {before : TcState .anon} (trace : DefinitionBodyTrace input (methodsN fuel) before) + {type : AExpr β} {level : VLevel} + (inference : BinderInference resolve entries [] [] fuel trace.validated input.type type (.sort level)) + (reading : readScopedExpr? resolve [] input.type = some type.erase) : + CheckedType resolve entries [] type level := + { locals := [], fuel, before := trace.validated, after := trace.typeState, + source := input.type, result := trace.inferredType, inference, + agreement := .empty _ _, reading, run := trace.typeRun } + +/-- Retain a type check that uses the synthesis rules, including direct +lambda applications, for subsequent universe-instantiated constant calls. -/ +def DefinitionBodyTrace.synthesisTypeCheck {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {input : DefinitionInput} {fuel : Nat} + {before : TcState .anon} (trace : DefinitionBodyTrace input (methodsN fuel) before) + {type : AExpr β} {level bound : VLevel} + (inference : SynthesisInference resolve entries [] [] [] fuel trace.validated input.type + type (.sort level) bound) + (reading : readScopedExpr? resolve [] input.type = some type.erase) : + SynthesisTypeCheck resolve entries type level := + { fuel, before := trace.validated, after := trace.typeState, source := input.type, + result := trace.inferredType, bound, inference, reading, run := trace.typeRun } + +/-- Public declaration-check success supplies the stored type-inference +execution. Only its finite inference tree and source reading remain inputs. -/ +theorem StandalonePrefix.definitionTypeCheck {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {input : DefinitionInput} {before after : TcState .anon} + (path : StandalonePrefix input.id before input.constant) {type : AExpr β} {level bound : VLevel} + (inference : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, + Nonempty (SynthesisInference resolve entries [] [] [] before.recFuel.toNat trace.validated + input.type type (.sort level) bound)) + (reading : readScopedExpr? resolve [] input.type = some type.erase) + (accepted : TcM.checkConst input.id before = .ok () after) : + Nonempty (SynthesisTypeCheck resolve entries type level) := by + obtain ⟨trace⟩ := definition_body_trace (path.member_success accepted) + obtain ⟨tree⟩ := inference trace + exact ⟨trace.synthesisTypeCheck tree reading⟩ + +/-- Synthesis supplies the body's full typing and the generated type's +formation. Source validation still supplies the scope of the actual declared +type and value; reference coverage is composed at the admission boundary. -/ +def DefinitionBodyTrace.synthesisSupport {β : Type u} {input : DefinitionInput} + {fuel : Nat} {before : TcState .anon} + (trace : DefinitionBodyTrace input (methodsN fuel) before) + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {body type : AExpr β} {level : VLevel} + {support : RunSupport} (typeCoverage : input.type.ValidationCoverage support) + (valueCoverage : input.value.ValidationCoverage support) + (collision : support.CollisionFree) + (valueInference : SynthesisInference resolve entries [] [] [] fuel trace.valueStart input.value body type level) + (valueReading : readScopedExpr? resolve [] input.value = some body.erase) + (typeReading : readScopedExpr? resolve [] input.type = some type.erase) + (valueConditions : ConditionsScoped input.universes.toNat body) + (typeConditions : ConditionsScoped input.universes.toNat type) + (references : body.ReferencesIn entries ∧ type.ReferencesIn entries) : + DefinitionBodySupport resolve entries (methodsN fuel) trace.valueStart input.type + input.universes.toNat input.value body type := + .synthesis rfl valueInference valueReading typeReading + (trace.scopes typeCoverage valueCoverage collision valueReading typeReading + valueConditions typeConditions) + references + /-- Operational support for the selected production definition fragment. Resources are required only at the states exposed by successful body traces. The conversion guard records the actual initial hash-equality path. -/ diff --git a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean new file mode 100644 index 000000000..a2f996649 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean @@ -0,0 +1,266 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Formation +import Ix.Theory.Model.UniverseBounds + +/-! +Inference with formation of the returned type. Local bounds come from the +executed checks of binder domains; external type bounds retain their actual +checking origin. Applications recover a codomain bound from an inhabited +product. Lambdas therefore synthesize full typing, including when they occur +directly in function position. + +The recorded level is an upper bound for the inferred type. It preserves the +exact zero condition, but its positive value need not be the sort the kernel +would infer on a separate call. No such extra call is assumed. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- Finite support for the actual production calls, enriched with the +origins and syntactic levels needed to derive formation. There are no +semantic typing or conversion fields. -/ +inductive SynthesisInference {β : Type u} + (resolve : Address → Option (ConstRef β)) : + Model.Environment β → List FVarId → Model.Context β → List VLevel → Nat → TcState .anon → KExpr .anon → + AExpr β → AExpr β → VLevel → Type u + | known {entries locals context bounds fuel before source term type level} + (inference : BinderInference resolve entries locals context fuel before source term type) + (formation : TypeFormation resolve entries context type level) : + SynthesisInference resolve entries locals context bounds fuel before source term type level + | reuseType {earlier entries locals context bounds fuel before source term type + typeFuel typeBefore typeAfter typeSource typeResult declaredType level typeBound arguments} + (inference : BinderInference resolve entries locals context fuel before source term type) + (typeTree : SynthesisInference resolve earlier [] [] [] typeFuel typeBefore typeSource + declaredType (.sort level) typeBound) + (extension : InterfaceExtends earlier entries) + (typeReading : readScopedExpr? resolve [] typeSource = some declaredType.erase) + (typeRun : RecM.infer typeSource (methodsN typeFuel) typeBefore = .ok typeResult typeAfter) + (same : AExpr.LevelEquivalent (declaredType.instL arguments) type) : + SynthesisInference resolve entries locals context bounds fuel before source term type (level.inst arguments) + | fvar {entries locals context bounds fuel before source index type level} + (inference : BinderInference resolve entries locals context fuel before source (.bvar index) type) + (atIndex : context[index]? = some type) + (boundAtIndex : bounds[index]? = some level) : + SynthesisInference resolve entries locals context bounds fuel before source (.bvar index) type level + | app {entries locals context bounds fuel before fn arg info f a A A' B condition functionLevel argumentLevel} + (full : before.inferOnly = false) + (miss : UncachedInference before (.app fn arg info)) + (trace : ApplicationInferenceTrace fuel miss.keyed fn arg) + (functionTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed fn + f (.forallE condition A B) functionLevel) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState + arg a A' argumentLevel) + (conditions : A'.annotations = A.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) + (bodyConstructed : trace.codomain.Constructed) + (argConstructed : arg.Constructed) + (bodyBound : trace.codomain.size + 1 < UInt64.size) + (argBound : arg.size < UInt64.size) + (coherent : trace.comparedState.env.intern.WF) + (faithful : KExpr.CollisionFree fun term => trace.comparedState.env.intern.ExprSupport term ∨ + KExpr.SubstReach arg trace.codomain 0 term) : + SynthesisInference resolve entries locals context bounds (fuel + 1) before (.app fn arg info) + (.app f a) (B.inst a) (applicationLevel functionLevel condition) + | forallE {entries locals context bounds fuel before name bi domain body info A B domainBoundLevel bodyBoundLevel} + (miss : UncachedInference before (.all name bi domain body info)) + (trace : ForallInferenceTrace fuel miss.keyed name bi domain body) + (opening : BinderOpeningSupport trace.domainState body) + (absent : (⟨trace.domainState.env.nextFVarId⟩ : FVarId) ∉ locals) + (domainTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed domain + A (.sort (readLevel trace.domainLevel)) domainBoundLevel) + (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) + (readLevel trace.domainLevel :: bounds) fuel trace.openedState trace.opened + B (.sort (readLevel trace.bodyLevel)) bodyBoundLevel) + (levelFaithful : ∀ a b, + (KUniv.Sub a trace.domainLevel ∨ KUniv.Sub a trace.bodyLevel) → + (KUniv.Sub b trace.domainLevel ∨ KUniv.Sub b trace.bodyLevel) → a.AddrFaithful b) + (domainBound : trace.domainLevel.size < UInt64.size) + (bodyBound : trace.bodyLevel.size < UInt64.size) + (coherent : trace.bodyState.env.intern.WF) + (faithful : KExpr.KeyCollisionFree fun term => trace.bodyState.env.intern.ExprSupport term ∨ + term = KExpr.mkSort (KUniv.mkIMax trace.domainLevel trace.bodyLevel)) : + SynthesisInference resolve entries locals context bounds (fuel + 1) before (.all name bi domain body info) + (.forallE (Certified.zeroCondition (readLevel trace.bodyLevel)) A B) + (.sort (readLevel (KUniv.mkIMax trace.domainLevel trace.bodyLevel))) + (.succ (readLevel (KUniv.mkIMax trace.domainLevel trace.bodyLevel))) + | lam {entries locals context bounds fuel before name bi domain body info A b B condition domainBoundLevel bodyLevel} + (full : before.inferOnly = false) + (miss : UncachedInference before (.lam name bi domain body info)) + (trace : LambdaInferenceTrace fuel miss.keyed name bi domain body) + (opening : BinderOpeningSupport trace.domainState body) + (absent : (⟨trace.domainState.env.nextFVarId⟩ : FVarId) ∉ locals) + (domainTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed domain + A (.sort (readLevel trace.domainLevel)) domainBoundLevel) + (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) + (readLevel trace.domainLevel :: bounds) fuel trace.openedState trace.opened b B bodyLevel) + (conditionAgrees : condition = Certified.zeroCondition bodyLevel) + (constructed : trace.bodyType.Constructed) + (bound : trace.bodyType.size + 1 < UInt64.size) + (coherent : trace.bodyState.env.intern.WF) + (closingFaithful : KExpr.CollisionFree fun term => trace.bodyState.env.intern.ExprSupport term ∨ + KExpr.AbstractReach ((∅ : Std.HashMap FVarId UInt64).insert trace.fresh 0) + 1 trace.bodyType 0 term) + (faithful : KExpr.KeyCollisionFree fun term => trace.abstracted.2.ExprSupport term ∨ + term = KExpr.mkAll () () domain trace.abstracted.1) : + SynthesisInference resolve entries locals context bounds (fuel + 1) before (.lam name bi domain body info) + (.lam condition A b) (.forallE condition A B) (.imax (readLevel trace.domainLevel) bodyLevel) + +/-- Simultaneous soundness of the term and formation of its returned type. +The context hypothesis is discharged by actual domain inference at each +binder, and by the empty context at the production declaration boundary. -/ +theorem SynthesisInference.sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + readScopedExpr? resolve locals result = some type.erase ∧ + TypingClaim.{u,v} entries context term type ∧ + TypingClaim.{u,v} entries context type (.sort level) := by + induction support generalizing result after with + | known inference formation => + obtain ⟨reads, checked⟩ := inference.sound agreement reading accepted + exact ⟨reads, checked.typing formation.sound, formation.sound⟩ + | @reuseType earlier entries locals context bounds fuel before source term type + typeFuel typeBefore typeAfter typeSource typeResult declaredType level typeBound arguments + inference typeTree extension typeReading typeRun same ihType => + obtain ⟨reads, checked⟩ := inference.sound agreement reading accepted + obtain ⟨_, typeTyped, _⟩ := ihType (.empty _) (.empty _ _) typeReading typeRun + have typeFormed : TypingClaim.{u,v} entries context type (.sort (level.inst arguments)) := + same.termTyping (typing_instL_closed (extension.typing typeTyped) _) + exact ⟨reads, checked.typing typeFormed, typeFormed⟩ + | fvar inference atIndex boundAtIndex => + obtain ⟨reads, typed⟩ := inference.synthesis (.bvar _) agreement reading accepted + exact ⟨reads, typed, formed _ _ _ atIndex boundAtIndex⟩ + | @app entries locals context bounds fuel before fn arg info f a A A' B condition functionLevel argumentLevel + full miss trace functionTree argumentTree conditions hashPath comparisonFaithful + bodyConstructed argConstructed bodyBound argBound coherent faithful ihFunction ihArgument => + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + rw [full] at run + obtain ⟨fnReads, argReads⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨functionTypeReads, functionTyped, functionFormed⟩ := + ihFunction formed keyedAgreement fnReads trace.functionRun + obtain ⟨domainReads, codomainReads⟩ := readScopedExpr?_all_parts functionTypeReads + have argumentAgreement := trace.contextPreserved.symm ▸ keyedAgreement + obtain ⟨argumentTypeReads, argumentTyped, _⟩ := + ihArgument formed argumentAgreement argReads trace.argumentRun + have sameReading := beq_readScopedExpr? (resolve := resolve) (locals := locals) + (depth := 0) comparisonFaithful hashPath + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (argumentTypeReads.symm.trans (sameReading.trans domainReads))) conditions + have checked := sameType ▸ argumentTyped.checking + refine ⟨?_, functionTyped.appChecking checked, + functionTyped.applicationType functionFormed checked⟩ + rw [trace.output run, AExpr.erase_inst] + exact (subst_readScopedExpr? bodyConstructed argConstructed bodyBound argBound + coherent faithful codomainReads argReads).1 + | forallE miss trace opening absent domainTree bodyTree levelFaithful domainBound bodyBound + coherent faithful ihDomain ihBody => + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨_, domainTyped, _⟩ := ihDomain formed keyedAgreement domainReads trace.domainRun + have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + obtain ⟨_, openedReads, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun + obtain ⟨_, bodyTyped, _⟩ := + ihBody (formed.push domainTyped) openedAgreement openedReads trace.bodyRun + refine ⟨?_, ?_, TypingClaim.sort _⟩ + · rw [trace.output run, internExpr_readScopedExpr? coherent faithful] + rfl + · exact AExpr.LevelEquivalent.sort + (Theory.VLevel.equiv_def.mpr fun levels => + (Theory.VLevel.equiv_def.mp (readLevel_mkIMax levelFaithful domainBound bodyBound) + levels).symm) |>.typing (TypingClaim.forallE domainTyped bodyTyped rfl) + | lam full miss trace opening absent domainTree bodyTree conditionAgrees constructed bound coherent + closingFaithful faithful ihDomain ihBody => + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + rw [full] at run + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨_, domainTyped, _⟩ := ihDomain formed keyedAgreement domainReads trace.domainRun + have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + obtain ⟨_, openedReads, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun + obtain ⟨bodyTypeReads, bodyTyped, bodyFormed⟩ := + ihBody (formed.push domainTyped) openedAgreement openedReads trace.bodyRun + obtain ⟨closedReads, closedCoherent⟩ := abstractFVars_readScopedExpr? constructed bound coherent + closingFaithful bodyTypeReads + refine ⟨?_, TypingClaim.lam domainTyped bodyFormed bodyTyped conditionAgrees, + TypingClaim.forallE domainTyped bodyFormed conditionAgrees⟩ + rw [trace.output run, + internExpr_readScopedExpr? (table := trace.abstracted.2) closedCoherent faithful] + simp [LambdaInferenceTrace.abstracted, domainReads, closedReads, AExpr.erase] + +/-- Closed production inference supplies its own formation facts. No local +or whole-term semantic typing premise remains at this boundary. -/ +theorem SynthesisInference.closed_sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries [] [] [] fuel before source term type level) + (reading : readScopedExpr? resolve [] source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + readScopedExpr? resolve [] result = some type.erase ∧ + TypingClaim.{u,v} entries [] term type ∧ TypingClaim.{u,v} entries [] type (.sort level) := + support.sound (.empty entries) (.empty _ _) reading accepted + +/-- An already supported type-inference call is a synthesis leaf because +its returned sort has an unconditional formation rule. -/ +def SynthesisInference.ofSort {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term : AExpr β} {level : VLevel} + (inference : BinderInference resolve entries locals context fuel before source term (.sort level)) : + SynthesisInference resolve entries locals context bounds fuel before source term (.sort level) (.succ level) := + .known inference (.sort level) + +/-- Package an executed closed type check so later declarations can retain +its origin. The check itself may contain direct lambda applications. -/ +structure SynthesisTypeCheck {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (type : AExpr β) (level : VLevel) where + fuel : Nat + before : TcState .anon + after : TcState .anon + source : KExpr .anon + result : KExpr .anon + bound : VLevel + inference : SynthesisInference resolve entries [] [] [] fuel before source type (.sort level) bound + reading : readScopedExpr? resolve [] source = some type.erase + run : RecM.infer source (methodsN fuel) before = .ok result after + +theorem SynthesisTypeCheck.sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {type : AExpr β} {level : VLevel} + (check : SynthesisTypeCheck resolve entries type level) : + TypingClaim.{u,v} entries [] type (.sort level) := + (check.inference.closed_sound check.reading check.run).2.1 + +/-- An instantiated constant can recover formation from an earlier actual +declaration type check, with exact interface preservation and structural +level congruence. -/ +def SynthesisInference.ofTypeCheck {β : Type u} {resolve : Address → Option (ConstRef β)} + {earlier entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} + {term type declaredType : AExpr β} {level : VLevel} + (inference : BinderInference resolve entries locals context fuel before source term type) + (check : SynthesisTypeCheck resolve earlier declaredType level) + (extension : InterfaceExtends earlier entries) (arguments : List VLevel) + (same : AExpr.LevelEquivalent (declaredType.instL arguments) type) : + SynthesisInference resolve entries locals context bounds fuel before source term type (level.inst arguments) := + .reuseType inference check.inference extension check.reading check.run same + +end Ix.Kernel.Consistency diff --git a/Ix/Theory/Model/LevelCongruence.lean b/Ix/Theory/Model/LevelCongruence.lean index 907b361ff..e448f5fef 100644 --- a/Ix/Theory/Model/LevelCongruence.lean +++ b/Ix/Theory/Model/LevelCongruence.lean @@ -341,6 +341,16 @@ theorem typing {entries : Environment β} {context : Context β} {e A B : AExpr exact ⟨termValid, (same.wellDenoted constants levels env).mp typeValid, same.interp constants levels env ▸ member⟩ +/-- Equivalent universe expressions also preserve typing of the term being +interpreted, including when that term is itself an inferred type. -/ +theorem termTyping {entries : Environment β} {context : Context β} {e e' A : AExpr β} + (same : LevelEquivalent e e') (typed : TypingClaim.{u,v} entries context e A) : + TypingClaim.{u,v} entries context e' A := by + intro V _ constants realizes levels env valid + obtain ⟨termValid, typeValid, member⟩ := typed V constants realizes levels env valid + exact ⟨(same.wellDenoted constants levels env).mp termValid, typeValid, + same.interp constants levels env ▸ member⟩ + end LevelEquivalent end AExpr end Model diff --git a/Ix/Theory/Model/UniverseBounds.lean b/Ix/Theory/Model/UniverseBounds.lean new file mode 100644 index 000000000..56052240b --- /dev/null +++ b/Ix/Theory/Model/UniverseBounds.lean @@ -0,0 +1,137 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Checking + +/-! +Universe bounds for inferred result types. An inhabited dependent product +in a Grothendieck universe has all its fibres in that universe: any fibre +element can replace the corresponding value in an existing function graph. +This avoids inverting universe membership of an empty product. +-/ + +namespace Ix.Theory.Model + +open SetTheory SetModel Certified + +universe u v +variable {V : Type v} [SetTheory V] + +/-- Varying one value of an existing function puts every fibre element in +the third union of the product. No choice of inhabitants for the remaining +fibres is needed. -/ +theorem piSet_fibre_subset {A f a : V} {B : V → V} + (member : f ∈ˢ piSet A B) (argument : a ∈ˢ A) : + B a ⊆ˢ sUnion (sUnion (sUnion (piSet A B))) := by + classical + intro y hy + let updated := fun x => if x = a then y else app f x + have updatedMember : graph updated A ∈ˢ piSet A B := by + apply graph_mem_piSet + intro x hx + dsimp [updated] + split + · next same => subst x; exact hy + · exact app_mem_of_mem_piSet member hx + have pairMember : kpair a y ∈ˢ graph updated A := by + apply mem_graph.mpr + exact ⟨a, argument, by simp [updated]⟩ + exact mem_sUnion.mpr ⟨upair a y, + mem_sUnion.mpr ⟨kpair a y, + mem_sUnion.mpr ⟨graph updated A, updatedMember, pairMember⟩, + mem_upair_right _ _⟩, mem_upair_right _ _⟩ + +/-- Inhabitation is essential: an empty product alone cannot bound all of +its fibres. The bound here follows from an actual function member. -/ +theorem IsTGUniverse.piSet_fibre_mem {U A f a : V} {B : V → V} + (grothendieck : IsTGUniverse (Mem (V := V)) U) + (formed : piSet A B ∈ˢ U) (member : f ∈ˢ piSet A B) (argument : a ∈ˢ A) : + B a ∈ˢ U := + grothendieck.mem_of_subset_mem + (grothendieck.sUnion_mem (grothendieck.sUnion_mem (grothendieck.sUnion_mem formed))) + (piSet_fibre_subset member argument) + +/-- A level with exactly the supplied binder's zero condition. Its positive +values serve only to preserve the Prop/data distinction in an upper bound. -/ +def conditionLevel : PropWhen → VLevel + | .never => .succ .zero + | .allZero parameters _ => parameters.foldr (fun i level => .max (.param i) level) .zero + +theorem zeroCondition_conditionLevel (condition : PropWhen) : + zeroCondition (conditionLevel condition) = condition := by + apply PropWhen.eq_of_holds + intro valuation + cases condition with + | never => rfl + | allZero parameters sorted => + change (zeroCondition (parameters.foldr (fun i level => .max (.param i) level) .zero)).holds + valuation = parameters.all (fun i => valuation i == 0) + clear sorted + induction parameters with + | nil => rfl + | cons index parameters ih => + simpa only [List.foldr_cons, zeroCondition, PropWhen.holds_inter, + PropWhen.holds_param, List.all_cons] using congrArg (fun b => (valuation index == 0) && b) ih + +/-- A positive upper bound in the graph regime and zero in the proof +regime. This is a semantic size bound, not a claimed exact inference result. -/ +def applicationLevel (functionLevel : VLevel) (condition : PropWhen) : VLevel := + .imax (.succ functionLevel) (conditionLevel condition) + +theorem zeroCondition_applicationLevel (functionLevel : VLevel) (condition : PropWhen) : + zeroCondition (applicationLevel functionLevel condition) = condition := + zeroCondition_conditionLevel condition + +theorem applicationLevel_zero (functionLevel : VLevel) (condition : PropWhen) (levels : List Nat) : + (applicationLevel functionLevel condition).eval levels = 0 ↔ regime condition levels = 0 := by + simpa only [zeroCondition_applicationLevel] using + (regime_zeroCondition (applicationLevel functionLevel condition) levels).symm + +theorem applicationLevel_ge {functionLevel : VLevel} {condition : PropWhen} {levels : List Nat} + (positive : regime condition levels ≠ 0) : + functionLevel.eval levels + 1 ≤ (applicationLevel functionLevel condition).eval levels := by + have nonzero : (conditionLevel condition).eval levels ≠ 0 := by + intro zero + apply positive + rw [← zeroCondition_conditionLevel condition] + exact (regime_zeroCondition _ _).mpr zero + simp only [applicationLevel, VLevel.eval, VLevel.natIMax, nonzero, ↓reduceIte] + exact Nat.le_max_left _ _ + +/-- Application retains a uniform bound on its inferred type. In the data +regime the function inhabits the product, so the fibre inversion above +applies; in the proof regime hereditary validity gives the exact bound zero. -/ +theorem TypingClaim.applicationType {β : Type u} {entries : Environment β} {context : Context β} + {function argument domain body : AExpr β} {condition : PropWhen} {level : VLevel} + (functionTyped : TypingClaim.{u,v} entries context function (.forallE condition domain body)) + (functionTypeFormed : TypingClaim.{u,v} entries context + (.forallE condition domain body) (.sort level)) + (argumentChecked : CheckingClaim.{u,v} entries context argument domain) : + TypingClaim.{u,v} entries context (body.inst argument) (.sort (applicationLevel level condition)) := by + intro V _ constants realizes levels env valid + have typed := functionTyped.appChecking argumentChecked V constants realizes levels env valid + obtain ⟨_, productValid, functionMember⟩ := functionTyped V constants realizes levels env valid + have argumentMember := (argumentChecked V constants realizes levels env valid productValid.1).2 + have productBound := (functionTypeFormed V constants realizes levels env valid).2.2 + refine ⟨typed.2.1, trivial, ?_⟩ + simp only [interp_inst, Valuation.skip_zero, Valuation.insert_zero, interp] + by_cases proofRegime : regime condition levels = 0 + · have zero := (applicationLevel_zero level condition levels).mpr proofRegime + obtain ⟨_, _, bodyLevel, agrees, bodyBound⟩ := productValid + have bodyZero := agrees.mp proofRegime + simpa only [zero, bodyZero] using bodyBound _ argumentMember + · have upper := applicationLevel_ge (functionLevel := level) proofRegime + have positive : (applicationLevel level condition).eval levels ≠ 0 := by omega + apply (univ_isTGUniverse positive).piSet_fibre_mem + (A := interp constants levels env domain) + (f := interp constants levels env function) + (a := interp constants levels env argument) + (B := fun x => interp constants levels (Valuation.cons x env) body) + · apply univ_mono (Nat.le_trans (Nat.le_succ _) upper) + simpa only [interp, piR_pos proofRegime] using productBound + · simpa only [interp, piR_pos proofRegime] using functionMember + · exact argumentMember + +end Ix.Theory.Model diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index efeac2418..fee74be34 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -388,6 +388,84 @@ private def applicationLocalResult : Bool := | .ok passed after => passed && after.lctx.size == 0 && after.env.nextFVarId == 2 | .error _ _ => false +/-- Put a direct lambda application beneath `A` and `a : A`, in the proof, +data, and universe-polymorphic regimes. -/ +private def storeLambdaCall (env : Ixon.Env) (body : Ixon.Expr) + (level : Ixon.Univ) (universes : UInt64 := 0) : Ixon.Env × Address := + storeConst env + ⟨.defn ⟨.defn, .safe, universes, + .leanAll (.sort 0) (.leanAll (.var 0) (.var 1)), + .leanLam (.sort 0) (.leanLam (.var 0) body)⟩, + #[], #[], #[level]⟩ + +private def directLambdaEnvironment : Ixon.Env := Id.run do + let body := Ixon.Expr.app (.leanLam (.var 1) (.var 0)) (.var 0) + let (env, _) := storeLambdaCall {} body .zero + let (env, _) := storeLambdaCall env body (.succ .zero) + return (storeLambdaCall env body (.var 0) 1).1 + +/-- The first lambda application returns another function, which is applied +again. Both application nodes need synthesis independently of their head. -/ +private def returnedLambdaEnvironment : Ixon.Env := Id.run do + let fn := Ixon.Expr.leanLam (.var 1) (.leanLam (.var 2) (.var 1)) + let body := Ixon.Expr.app (.app fn (.var 0)) (.var 0) + let (env, _) := storeLambdaCall {} body .zero + return (storeLambdaCall env body (.succ .zero)).1 + +/-- `((fun f : A → A => f a) (fun x : A => x))`. The lambda body's +application derives a bound that the enclosing lambda can reuse. -/ +private def higherOrderLambdaEnvironment : Ixon.Env := Id.run do + let functionType := Ixon.Expr.leanAll (.var 1) (.var 2) + let fn := Ixon.Expr.leanLam functionType (.app (.var 0) (.var 1)) + let body := Ixon.Expr.app fn (.leanLam (.var 1) (.var 0)) + let (env, _) := storeLambdaCall {} body .zero + return (storeLambdaCall env body (.succ .zero)).1 + +/-- `(fun y : A => f y) x`, with the dependent family in Prop or Type. +The generated codomain stays dependent through opening and abstraction. -/ +private def dependentLambdaEnvironment : Ixon.Env := Id.run do + let familyType := Ixon.Expr.leanAll (.var 0) (.sort 1) + let functionType := Ixon.Expr.leanAll (.var 1) (.app (.var 1) (.var 0)) + let type := Ixon.Expr.leanAll (.sort 0) (.leanAll familyType + (.leanAll functionType (.leanAll (.var 2) (.app (.var 2) (.var 0))))) + let body := Ixon.Expr.app (.leanLam (.var 3) (.app (.var 2) (.var 0))) (.var 0) + let value := Ixon.Expr.leanLam (.sort 0) (.leanLam familyType + (.leanLam functionType (.leanLam (.var 2) body))) + let (env, _) := storeConst {} + ⟨.defn ⟨.defn, .safe, 0, type, value⟩, #[], #[], #[.succ .zero, .zero]⟩ + return (storeConst env + ⟨.defn ⟨.defn, .safe, 0, type, value⟩, #[], #[], #[.succ .zero, .succ .zero]⟩).1 + +private def sortLambdaCall (declared : UInt64) : Ixon.Env × Address := + storeConst {} + ⟨.defn ⟨.defn, .safe, 0, .sort declared, + .app (.leanLam (.sort 1) (.var 0)) (.sort 0)⟩, + #[], #[], #[.zero, .succ .zero]⟩ + +/-- The earlier axiom's own type check contains a direct lambda application. +The alias reuses that exact declared type, taking hash conversion. -/ +private def appliedDeclarationType : Ixon.Env := Id.run do + let type := Ixon.Expr.app (.leanLam (.sort 1) (.var 0)) (.sort 0) + let (env, proposition) := storeConst {} + ⟨.axio ⟨false, 0, type⟩, #[], #[], #[.zero, .succ .zero]⟩ + return (storeConst env + ⟨.defn ⟨.defn, .safe, 0, type, .ref 0 #[]⟩, + #[], #[proposition], #[.zero, .succ .zero]⟩).1 + +private def lambdaApplicationLocalResult : Bool := + let propType := KExpr.mkSort (m := .anon) .mkZero + let action : RecM .anon Bool := RecM.withLctxScope do + let (proposition, _) ← TcM.openBinder () () propType (.mkVar 0 ()) + let (witness, _) ← TcM.openBinder () () proposition (.mkVar 0 ()) + let term := KExpr.mkApp (.mkLam () () proposition (.mkVar 0 ())) witness + let first ← RecM.inferCall term + let second ← RecM.inferCall term + let state ← get + return first.addr == proposition.addr && second.addr == proposition.addr && state.lctx.size == 2 + match TcM.runRec action (TcState.ofEnvAnon {}) with + | .ok passed after => passed && after.lctx.size == 0 && after.env.nextFVarId == 3 + | .error _ _ => false + private def applicationCases : TestSeq := test "application environment: Prop/Type identity calls and transitive theorem calls check" (allSucceeded applicationEnvironment 5 { clearEvery := 0 }) @@ -405,6 +483,28 @@ private def applicationCases : TestSeq := (let (env, target) := failedApplication; rowFailed env target) ++ test "application environment: applying a proof with no function type fails" (let (env, target) := failedBinder (.app (.var 0) (.var 0)); rowFailed env target) + ++ test "synthesis environment: direct lambda calls check in Prop, Type, and at a universe parameter" + (allSucceeded directLambdaEnvironment 3 { clearEvery := 0 }) + ++ test "synthesis environment: direct lambda calls check with fresh per-item caches" + (allSucceeded directLambdaEnvironment 3 { clearEvery := 1 }) + ++ test "synthesis environment: a direct lambda call returns a function that can be applied again" + (allSucceeded returnedLambdaEnvironment 2) + ++ test "synthesis environment: function and argument lambdas retain application bounds" + (allSucceeded higherOrderLambdaEnvironment 2) + ++ test "synthesis environment: dependent lambda bodies synthesize in Prop and Type" + (allSucceeded dependentLambdaEnvironment 2) + ++ test "synthesis environment: a direct lambda call returns the expected universe term" + (allSucceeded (sortLambdaCall 1).1 1) + ++ test "synthesis environment: an earlier declared type can itself contain a direct lambda call" + (allSucceeded appliedDeclarationType 2) + ++ test "synthesis inference: a direct lambda result survives cache reuse and scope cleanup" + lambdaApplicationLocalResult + ++ test "synthesis environment: a proposition cannot be used as its own witness argument" + (let (env, target) := failedBinder (.app (.leanLam (.var 1) (.var 0)) (.var 1)); rowFailed env target) + ++ test "synthesis environment: a direct lambda cannot return Sort 0 at type Sort 0" + (let (env, target) := sortLambdaCall 0; rowFailed env target) + ++ test "synthesis environment: a lambda domain must pass its executed sort check" + (let (env, target) := failedBinder (.app (.leanLam (.var 0) (.var 0)) (.var 0)); rowFailed env target) /-- Call a polymorphic identity from a monomorphic function body. Universe indices select entries in the declaration's explicit level table. -/ diff --git a/Tests/Theory/ImportManifest.lean b/Tests/Theory/ImportManifest.lean index ee4f7d141..ee6cf3cf9 100644 --- a/Tests/Theory/ImportManifest.lean +++ b/Tests/Theory/ImportManifest.lean @@ -17,7 +17,8 @@ def conLecheRevision : String := "86cd20a65660d757cedc81561a44579099b565d0" /-- New mathematical modules authored in Ix, with no imported source hash. -/ def authored : Array String := #[ - "Ix/Theory/Model/Checking.lean", "Ix/Theory/Model/LevelCongruence.lean" + "Ix/Theory/Model/Checking.lean", "Ix/Theory/Model/LevelCongruence.lean", + "Ix/Theory/Model/UniverseBounds.lean" ] structure SourceFile where diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index c501ead3b..b9845e1f7 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -83,6 +83,18 @@ increasing sequence of strongly inaccessible cardinals. with that domain, with matching occurrence annotations. `subst_readScopedExpr?` connects the actual memoized and interned codomain substitution to model substitution, including active locals and nested binders. +- `SynthesisInference.closed_sound` derives full typing and a universe bound + for the generated type, including direct lambda applications and applications + returning functions. Binder bounds come from actual domain inference; + `SynthesisTypeCheck` retains earlier executed declaration type checks across + interface growth and universe instantiation. An inhabited dependent product + bounds every fibre, so application preserves formation without another + codomain inference call. The bound preserves the exact Prop condition while + permitting a larger positive universe. `DefinitionBodyTrace.synthesisSupport` + connects this result to ordinary declaration admission. + `AxiomObservation.synthesisTypeCheck` extracts an axiom's type check from + successful `checkEnvAnon` rows; `StandalonePrefix.definitionTypeCheck` extracts + a definition's check from public declaration success. - `instantiateUnivParams_readScopedAnnotated` brings the actual substituted declaration type into any active local context, preserving its closed term scope and occurrence annotations. `infer_const_scoped_annotated` uses this @@ -271,6 +283,8 @@ def useF (x : T.{1}) : T.{1} := f.{1} x def idSort.{u} (A : Sort u) (a : A) : A := a def aliasSort.{u,v} : (A : Sort (max u v)) → A → A := idSort.{max u v} def useSort.{u} (A : Sort u) (a : A) : A := idSort.{u} A a +def callLambda.{u} (A : Sort u) (a : A) : A := (fun x : A => x) a +def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) a) a ``` `AtomicEnvironmentFragment` records the precise execution boundary: @@ -344,19 +358,27 @@ def useSort.{u} (A : Sort u) (a : A) : A := idSort.{u} A a path. `CheckingClaim` derives body validity and membership once declared-type inference establishes hereditary validity. No codomain-typing premise is added to the production proof. + Alternatively, `SynthesisInference` derives formation of the generated type + together with body typing. It follows the actual domain check at each lambda + and keeps a universe bound for each local declaration. Reused closed type + checks retain their source, states, successful run, and inference tree; + interface extension preserves their entries exactly. These are execution + resources, not semantic formation assumptions. The type checks can themselves + contain direct lambda applications. - Applications use full mode, syntactic Pi exposure, an ordinary argument without an eager-reduction marker, and the hash-equality conversion path. Their witnesses retain the actual recursive calls, context preservation during function inference, and finite substitution resources. Function - spines start with locals or admitted constants. Polymorphic constants supply + spines can start with locals or admitted constants, or use the synthesis + rules for direct lambdas and returned functions. Polymorphic constants supply lazy-lookup agreement on misses (derived from source for bound standalones) or loaded-declaration agreement on hits, a closed scoped reading of the selected entry's type, and finite universe-substitution resources. Their result type is fixed by a pure `readInstantiatedType?` check with the substituted occurrence annotations, including when levels simplify. Monomorphic references retain - their simpler empty-substitution rule. Applying a lambda directly and - reduction to expose a Pi require further refinement. + their simpler empty-substitution rule. Reduction to expose a Pi, eager + arguments, and changed cheap-beta paths require further refinement. - Conversion takes the initial hash-equality path, with faithfulness of the compared expressions. General reduction and conversion caches are outside this fragment. @@ -448,7 +470,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 367 exact theorem boundaries. The production +The consistency target checks 398 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -461,7 +483,8 @@ certificate validator in their dependency closures. The polymorphic inference, substitution, and binder inference roots retain only the two existing expression/universe output-length proofs, alongside the standard Lean axioms. Their model-side level congruence introduces no native proof dependency. -Semantic checking against a formed type also uses only standard Lean axioms. +Semantic checking against a formed type and the new product-fibre universe +bound also use only standard Lean axioms. Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, argument order, and rejection of wrong arities and out-of-range parameters. @@ -473,6 +496,10 @@ Function-body regressions cover calls to earlier Prop/Type identities, transitive theorem calls, local functions with distinct domains and codomains, dependent result families, lambda arguments, exact local results through cache reuse and scope cleanup, and rejected argument/function types. +Direct lambda regressions cover Prop, Type, universe parameters, returned +functions, higher-order arguments, dependent proof/data families, and earlier +declaration types containing lambda calls. Negative cases reject invalid +domains, wrong arguments, and a universe term claimed at its own sort. Polymorphic-call regressions include Prop/Type instances in real function bodies, `max`/`imax` simplification inside Pi domains, closed nested references under active locals, separate cache keys for different universe instances, @@ -542,7 +569,7 @@ Definition-cycle regressions use content-addressed standalone and mutual declarations, including a self-justifying theorem, a two-member cycle, type cycles, lets, shared syntax, and binders. They check repeated member failures, acyclic forward references, cache clearing, and the partial/unsafe policy. -The unit suite contains 578 checks. The anonymous differential additionally +The unit suite contains 589 checks. The anonymous differential additionally serializes eight cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. @@ -584,6 +611,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Intern coherence through conversion and lazy loading | [`Consistency/IngressCoherence.lean`](../Ix/Kernel/Verify/Consistency/IngressCoherence.lean) | | Source ownership, block registration, and finite preflight | [`Consistency/BlockOwnership.lean`](../Ix/Kernel/Verify/Consistency/BlockOwnership.lean), [`Consistency/SourceOwnershipCheck.lean`](../Ix/Kernel/Verify/Consistency/SourceOwnershipCheck.lean), [`SourceOwnership.lean`](../Ix/Kernel/SourceOwnership.lean) | | Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`Application.lean`](../Ix/Kernel/Verify/Consistency/Application.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | +| Inferred type formation and direct lambda applications | [`Consistency/SynthesisInference.lean`](../Ix/Kernel/Verify/Consistency/SynthesisInference.lean), [`Formation.lean`](../Ix/Kernel/Verify/Consistency/Formation.lean), [`Model/UniverseBounds.lean`](../Ix/Theory/Model/UniverseBounds.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | | Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | | Host commands, receipts, and frozen regression evidence | [Certified checking guide](certified-checking.md) | diff --git a/docs/theory.md b/docs/theory.md index 1d18f0e2a..184598a77 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -31,6 +31,17 @@ retain the exact declared universe count and denote the checked body at every universe instance. Application spines headed by locals or admitted polymorphic constants derive their type's validity from the context or dependency model, then check arguments and substitute the dependent result. +The synthesis rules additionally derive a uniform universe bound for every +generated type, allowing direct lambda applications and returned functions. +Bounds come from actual binder-domain and earlier declaration type checks. +For application, an inhabited dependent product bounds every fibre in a +Grothendieck universe; the proof regime retains the exact bound zero. The +resulting positive bounds can be larger than an independently inferred sort, +while preserving every binder's Prop condition. No additional codomain check +or semantic typing premise is required. Earlier executed type checks remain +usable after interface growth and universe instantiation. +Successful environment rows supply the axiom type-inference traces; public +declaration success supplies the corresponding definition traces. Safe definition admission also rejects circular justification in both Lean and Rust, including `theorem loop : P := loop` with only `P : Prop` assumed. The production dependency walk returns an order with a proved decreasing rank; From a0a4dd3188cf9b384ed9a1c49671bdae30c39098 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 00:57:54 -0400 Subject: [PATCH 24/63] Derive beta conversion from production type checks --- Ix/Kernel/Verify/Consistency.lean | 10 +- Ix/Kernel/Verify/Consistency/Audit.lean | 13 +- Ix/Kernel/Verify/Consistency/Beta.lean | 146 ++++++++++++++++++ .../Verify/Consistency/BetaSubstitution.lean | 102 ++++++++++++ Ix/Kernel/Verify/Consistency/Production.lean | 117 ++++++++++++-- Ix/Kernel/Verify/Whnf.lean | 35 +---- Ix/Kernel/Verify/Whnf/Beta/DirectStep.lean | 42 +++++ Ix/Theory/Model/Substitution.lean | 119 ++++++++++++++ Tests/Ix/Kernel/Consistency.lean | 74 +++++++++ Tests/Theory/ImportManifest.lean | 2 +- docs/kernel-verification.md | 36 ++++- docs/theory.md | 11 ++ 12 files changed, 647 insertions(+), 60 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/Beta.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaSubstitution.lean create mode 100644 Ix/Kernel/Verify/Whnf/Beta/DirectStep.lean create mode 100644 Ix/Theory/Model/Substitution.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 98624d8f3..42c7279fc 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -27,6 +27,8 @@ import Ix.Kernel.Verify.Consistency.Application import Ix.Kernel.Verify.Consistency.BinderInference import Ix.Kernel.Verify.Consistency.Formation import Ix.Kernel.Verify.Consistency.SynthesisInference +import Ix.Kernel.Verify.Consistency.BetaSubstitution +import Ix.Kernel.Verify.Consistency.Beta import Ix.Kernel.Verify.Consistency.Validation import Ix.Kernel.Verify.Consistency.RecursiveCache import Ix.Kernel.Verify.Consistency.RecursiveState @@ -70,7 +72,13 @@ type checks. An inhabited product supplies a uniform codomain bound at an application, preserving the exact Prop condition. Direct lambda applications therefore synthesize full typing without an extra codomain inference call. Earlier type checks remain reusable after interface growth and universe -instantiation. Local cache hits agree with the actual declaration type. Constant +instantiation. Source beta redexes retain their checked lambda domains; +substitution and the actual one-argument WHNF step preserve typing and +denotation under finite walker resources. Definition admission can reuse its +executed declared-type inference to justify a beta conversion to the value's +inferred type. This path is included in environment model preservation. +General reduction of generated types remains a separate obligation. +Local cache hits agree with the actual declaration type. Constant hits agree with pure universe substitution of a loaded, admitted declaration; sort hits return the canonical successor sort. Application, forall, and lambda nodes retain misses in every eligible cache partition. Full mode leaves the diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index f68345b59..4a9b87e7c 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -53,9 +53,14 @@ private def atomicRoots : Array Lean.Name := #[ ``CheckedType.sound, ``TypeFormation.sound, ``SynthesisInference.sound, ``SynthesisInference.closed_sound, ``SynthesisInference.ofSort, ``SynthesisTypeCheck.sound, ``SynthesisInference.ofTypeCheck, + ``BinderInference.lambda_type, ``SynthesisInference.lambda_type, + ``SynthesisInference.beta_sound, ``SynthesisInference.beta_step, + ``RecM.whnfCoreWithFlagsStep_betaOne, ``DefinitionBodySupport.sound, ``DefinitionBodyTrace.scopes, ``DefinitionBodyTrace.binderSupport, ``DefinitionBodyTrace.checkedType, ``DefinitionBodyTrace.synthesisTypeCheck, - ``DefinitionBodyTrace.synthesisSupport, ``AxiomTypeTrace.synthesisTypeCheck + ``DefinitionBodyTrace.synthesisSupport, ``AxiomTypeTrace.synthesisTypeCheck, + ``DefinitionCheckSupport.sound, ``DefinitionBodyTrace.betaDeclaredSupport, + ``AtomicDefinitionRun.ofHash ] private def formationRoots : Array Lean.Name := #[ @@ -65,7 +70,10 @@ private def formationRoots : Array Lean.Name := #[ ``Theory.Model.TypingClaim.applicationType, ``Theory.Model.AExpr.LevelEquivalent.termTyping, ``InterfaceExtends.realizes, ``InterfaceExtends.typing, ``context_valid_tail, ``typing_weaken, ``typing_instL_closed, - ``ContextFormation.empty, ``ContextFormation.push + ``ContextFormation.empty, ``ContextFormation.push, + ``Theory.Model.wellDenoted_of_inst, ``Theory.Model.wellDenoted_inst_iff, + ``Theory.Model.TypingClaim.inst, ``Theory.Model.CheckingClaim.inst, + ``Theory.Model.ConversionClaim.inst ] private def instantiationRoots : Array Lean.Name := #[ @@ -243,6 +251,7 @@ private def binderWalkerRoots : Array Lean.Name := #[ ``openBinder_eq, ``openBinder_sound, ``abstractFVars_singleton_spec, ``abstractFVars_readScopedExpr?, ``readScopedExpr?_liftSpec, ``readScopedExpr?_substSpec, ``subst_readScopedExpr?, + ``KExpr.simulSubstSpec_singleton, ``simulSubst_singleton_readScopedExpr?, ``ApplicationSubstitutionData.coherent, ``LambdaClosingData.coherent ] diff --git a/Ix/Kernel/Verify/Consistency/Beta.lean b/Ix/Kernel/Verify/Consistency/Beta.lean new file mode 100644 index 000000000..2144fbc25 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Beta.lean @@ -0,0 +1,146 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.SynthesisInference +import Ix.Kernel.Verify.Consistency.BetaSubstitution +import Ix.Kernel.Verify.Whnf.Beta.DirectStep +import Ix.Theory.Model.Substitution + +/-! +Beta reduction from the actual inference of a source redex. Inverting the +finite inference tree retains the checked lambda domain, which semantic +typing alone cannot recover when proof values have been identified. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- Lambda inference returns a product with the source lambda's domain +and condition. This fact comes from the executed inference tree. -/ +theorem BinderInference.lambda_type {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} + {condition : Certified.PropWhen} {domain body type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source + (.lam condition domain body) type) : + ∃ codomain, type = .forallE condition domain codomain := by + cases support with + | lam => exact ⟨_, rfl⟩ + +theorem SynthesisInference.lambda_type {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {level : VLevel} + {condition : Certified.PropWhen} {domain body type : AExpr β} + (support : SynthesisInference resolve entries locals context bounds fuel before source + (.lam condition domain body) type level) : + ∃ codomain, type = .forallE condition domain codomain := by + cases support with + | known inference => exact inference.lambda_type + | reuseType inference => exact inference.lambda_type + | lam => exact ⟨_, rfl⟩ + +private theorem BinderInference.no_direct_beta {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} + {condition : Certified.PropWhen} {domain body argument type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source + (.app (.lam condition domain body) argument) type) : False := by + cases support with + | app _ _ _ _ head => cases head + +/-- A supported successful inference of a source beta redex derives both +equality with the substitution result and typing of that result. No run +of inference on the generated substitution is required. -/ +theorem SynthesisInference.beta_sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} {level : VLevel} + {condition : Certified.PropWhen} {domain body argument type : AExpr β} + (support : SynthesisInference resolve entries locals context bounds fuel before source + (.app (.lam condition domain body) argument) type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = + some (AExpr.app (.lam condition domain body) argument).erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + ConversionClaim.{u,v} entries context + (.app (.lam condition domain body) argument) (body.inst argument) ∧ + TypingClaim.{u,v} entries context (body.inst argument) type := by + cases support with + | known inference => exact False.elim inference.no_direct_beta + | reuseType inference => exact False.elim inference.no_direct_beta + | app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful + bodyConstructed argConstructed bodyBound argBound coherent faithful => + obtain ⟨codomain, sameProduct⟩ := functionTree.lambda_type + cases sameProduct + obtain ⟨fnReads, argReads⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨functionTypeReads, functionTyped, _⟩ := + functionTree.sound formed keyedAgreement fnReads trace.functionRun + obtain ⟨domainReads, _⟩ := readScopedExpr?_all_parts functionTypeReads + have argumentAgreement := trace.contextPreserved.symm ▸ keyedAgreement + obtain ⟨argumentTypeReads, argumentTyped, _⟩ := + argumentTree.sound formed argumentAgreement argReads trace.argumentRun + have sameReading := beq_readScopedExpr? (resolve := resolve) (locals := locals) + (depth := 0) comparisonFaithful hashPath + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (argumentTypeReads.symm.trans (sameReading.trans domainReads))) conditions + have typedArgument := sameType ▸ argumentTyped + exact ⟨ConversionClaim.beta functionTyped typedArgument, + TypingClaim.betaResult functionTyped typedArgument⟩ + +/-- The real one-argument structural-WHNF step returns the well-typed beta +result justified by source inference. The production method table supplies +the lambda-head callback; finite walker resources supply the exact result +reading and the updated intern-table invariant. -/ +theorem SynthesisInference.beta_step {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {inferenceBefore inferenceAfter : TcState .anon} {inferred : KExpr .anon} {level : VLevel} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {rawDomain rawBody rawArgument : KExpr .anon} {lambdaInfo appInfo : ExprInfo .anon} + {condition : Certified.PropWhen} {domain body argument type : AExpr β} + (support : SynthesisInference resolve entries locals context bounds fuel inferenceBefore + (.app (.lam name bi rawDomain rawBody lambdaInfo) rawArgument appInfo) + (.app (.lam condition domain body) argument) type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals inferenceBefore.lctx context) + (reading : readScopedExpr? resolve locals + (.app (.lam name bi rawDomain rawBody lambdaInfo) rawArgument appInfo) = + some (AExpr.app (.lam condition domain body) argument).erase) + (accepted : RecM.infer (.app (.lam name bi rawDomain rawBody lambdaInfo) rawArgument appInfo) + (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) + (before : TcState .anon) (reductionFuel : Nat) (flags : WhnfFlags) + (bodyConstructed : rawBody.Constructed) (argumentConstructed : rawArgument.Constructed) + (bodyBound : rawBody.size + 1 < UInt64.size) (argumentBound : rawArgument.size < UInt64.size) + (coherent : before.env.intern.WF) + (faithful : KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ + KExpr.SimulSubstReach #[rawArgument] rawBody 0 term) : + ∃ result after, + (RecM.whnfCoreWithFlagsStep + (.app (.lam name bi rawDomain rawBody lambdaInfo) rawArgument appInfo) flags).run + (methodsN (reductionFuel + 1)) before = .ok (.next result) after ∧ + readScopedExpr? resolve locals result = some (body.inst argument).erase ∧ + ConversionClaim.{u,v} entries context + (.app (.lam condition domain body) argument) (body.inst argument) ∧ + TypingClaim.{u,v} entries context (body.inst argument) type ∧ after.env.intern.WF := by + obtain ⟨fnReads, argumentReads⟩ := readScopedExpr?_app_parts reading + obtain ⟨_, bodyReads⟩ := readScopedExpr?_lam_parts fnReads + obtain ⟨resultReads, resultCoherent⟩ := simulSubst_singleton_readScopedExpr? + bodyConstructed argumentConstructed bodyBound argumentBound coherent faithful bodyReads argumentReads + obtain ⟨conversion, typed⟩ := support.beta_sound formed agreement reading accepted + let walk := simulSubst rawBody #[rawArgument] 0 before.env.intern + refine ⟨walk.1, { before with env := { before.env with intern := walk.2 } }, ?_, + ?_, conversion, typed, resultCoherent⟩ + · exact RecM.whnfCoreWithFlagsStep_betaOne rfl rfl + · simpa only [AExpr.erase_inst] using resultReads + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaSubstitution.lean b/Ix/Kernel/Verify/Consistency/BetaSubstitution.lean new file mode 100644 index 000000000..ab3e21860 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaSubstitution.lean @@ -0,0 +1,102 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Application + +/-! The simultaneous walker used by production beta reduction agrees with +single substitution when exactly one lambda argument is consumed. -/ + +namespace Ix.Kernel + +/-- The two pure walker specifications coincide for one replacement, +including index shifting above the removed binder. -/ +theorem KExpr.simulSubstSpec_singleton {body argument : KExpr .anon} {depth : UInt64} + (bound : depth.toNat + body.size + 1 < UInt64.size) : + KExpr.simulSubstSpec body #[argument] depth = KExpr.substSpec body argument depth := by + induction body generalizing depth with + | var index name info => + simp only [KExpr.size] at bound + have next : (depth + 1).toNat = depth.toNat + 1 := by + rw [UInt64.toNat_add, show (1 : UInt64).toNat = 1 from rfl, + Nat.mod_eq_of_lt (show depth.toNat + 1 < UInt64.size by omega)] + have window : (index ≥ depth && index < depth + 1) = true ↔ index = depth := by + simp only [Bool.and_eq_true, decide_eq_true_eq, UInt64.le_iff_toNat_le, + UInt64.lt_iff_toNat_lt, next] + constructor + · intro h; exact UInt64.toNat_inj.mp (by omega) + · intro h; subst index; omega + have above : index ≥ depth + 1 ↔ index > depth := by + simp only [UInt64.le_iff_toNat_le, UInt64.lt_iff_toNat_lt, next] + omega + by_cases equal : index = depth + · subst index + have smaller : depth < depth + 1 := UInt64.lt_iff_toNat_lt.mpr (by rw [next]; omega) + simp [KExpr.simulSubstSpec, KExpr.substSpec, smaller] + · have outside := mt window.mp equal + have nameEq : name = anonName (m := .anon) := Subsingleton.elim _ _ + simp [KExpr.simulSubstSpec, KExpr.substSpec, equal, outside, above, nameEq] + | app fn arg info ihFn ihArg => + simp only [KExpr.size] at bound + simp [KExpr.simulSubstSpec, KExpr.substSpec, + ihFn (depth := depth) (by omega), ihArg (depth := depth) (by omega)] + | lam name bi domain body info ihDomain ihBody | all name bi domain body info ihDomain ihBody => + simp only [KExpr.size] at bound + have next : (depth + 1).toNat = depth.toNat + 1 := by + rw [UInt64.toNat_add, show (1 : UInt64).toNat = 1 from rfl, + Nat.mod_eq_of_lt (show depth.toNat + 1 < UInt64.size by omega)] + simp [KExpr.simulSubstSpec, KExpr.substSpec, ihDomain (depth := depth) (by omega), + ihBody (depth := depth + 1) (by rw [next]; omega)] + | letE name type value body nondep info ihType ihValue ihBody => + simp only [KExpr.size] at bound + have next : (depth + 1).toNat = depth.toNat + 1 := by + rw [UInt64.toNat_add, show (1 : UInt64).toNat = 1 from rfl, + Nat.mod_eq_of_lt (show depth.toNat + 1 < UInt64.size by omega)] + simp [KExpr.simulSubstSpec, KExpr.substSpec, + ihType (depth := depth) (by omega), ihValue (depth := depth) (by omega), + ihBody (depth := depth + 1) (by rw [next]; omega)] + | prj ref field major info ih => + simp only [KExpr.size] at bound + simp [KExpr.simulSubstSpec, KExpr.substSpec, ih (depth := depth) (by omega)] + | _ => rfl + +namespace Consistency + +open Theory Theory.Model + +universe u + +/-- The actual memoized simultaneous substitution used by a one-argument +beta step reads the model substitution and preserves intern coherence. -/ +theorem simulSubst_singleton_readScopedExpr? {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {body argument : KExpr .anon} {source value : VExpr β} {table : InternTable .anon} + (bodyConstructed : body.Constructed) (argumentConstructed : argument.Constructed) + (bodyBound : body.size + 1 < UInt64.size) (argumentBound : argument.size < UInt64.size) + (coherent : table.WF) + (faithful : KExpr.CollisionFree fun term => table.ExprSupport term ∨ + KExpr.SimulSubstReach #[argument] body 0 term) + (bodyReads : readScopedExpr? resolve locals body 1 = some source) + (argumentReads : readScopedExpr? resolve locals argument = some value) : + readScopedExpr? resolve locals (simulSubst body #[argument] 0 table).1 = + some (source.inst value) ∧ (simulSubst body #[argument] 0 table).2.WF := by + obtain ⟨result, preserved, _⟩ := simulSubst_spec faithful bodyConstructed + (by + intro index small + have : index = 0 := by simpa using small + subst index + exact argumentConstructed) + (by + intro index small + have : index = 0 := by simpa using small + subst index + exact argumentBound) + (by simpa using bodyBound) (fun _ h => Or.inr h) coherent (fun _ h => Or.inl h) + refine ⟨?_, preserved⟩ + rw [result, KExpr.simulSubstSpec_singleton (by simpa using bodyBound)] + exact readScopedExpr?_substSpec (depth := 0) (by simpa using bodyBound) + argumentBound bodyReads argumentReads + +end Consistency +end Ix.Kernel diff --git a/Ix/Kernel/Verify/Consistency/Production.lean b/Ix/Kernel/Verify/Consistency/Production.lean index 7cb7f2000..85e2c05cd 100644 --- a/Ix/Kernel/Verify/Consistency/Production.lean +++ b/Ix/Kernel/Verify/Consistency/Production.lean @@ -6,6 +6,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Driver import Ix.Kernel.Verify.Consistency.Constant import Ix.Kernel.Verify.Consistency.SynthesisInference +import Ix.Kernel.Verify.Consistency.Beta import Ix.Kernel.Verify.Consistency.Validation /-! @@ -13,9 +14,10 @@ import Ix.Kernel.Verify.Consistency.Validation These theorems invert the public checker, including its error isolation, initial lazy lookup, block routing, per-constant reset, validation, type -inference, theorem guard, value inference, and conversion. The supported -conversion path is the initial address-equality branch. Finite address -faithfulness connects that comparison to the exact model syntax. +inference, theorem guard, value inference, and conversion. Supported +comparisons use address equality or beta reduction of the declared type, +justified by that declaration's actual type check. Finite address +faithfulness connects hash comparisons to model syntax. Operational equations and interface agreement suffice to derive body typing. -/ @@ -408,20 +410,108 @@ def DefinitionBodyTrace.synthesisSupport {β : Type u} {input : DefinitionInput} valueConditions typeConditions) references +/-- The actual declaration trace determines the type and value checks used +to justify conversion. A beta case reuses the declared type's executed +inference; it does not request another check of a generated type. -/ +inductive DefinitionCheckSupport {β : Type u} + (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) + {input : DefinitionInput} {fuel : Nat} {before : TcState .anon} + (trace : DefinitionBodyTrace input (methodsN fuel) before) : + AExpr β → AExpr β → Type u + | hash {body type : AExpr β} + (inference : DefinitionBodySupport resolve entries (methodsN fuel) + trace.valueStart input.type input.universes.toNat input.value body type) + (faithful : trace.inferredValue.AddrFaithful input.type) + (hashPath : (trace.inferredValue == input.type) = true) : + DefinitionCheckSupport resolve entries trace body type + | betaDeclared {body domain inner argument : AExpr β} {condition : Certified.PropWhen} + {level typeBound valueBound : VLevel} + (typeInference : SynthesisInference resolve entries [] [] [] fuel trace.validated input.type + (.app (.lam condition domain inner) argument) (.sort level) typeBound) + (valueInference : SynthesisInference resolve entries [] [] [] fuel trace.valueStart input.value + body (inner.inst argument) valueBound) + (valueReading : readScopedExpr? resolve [] input.value = some body.erase) + (typeReading : readScopedExpr? resolve [] input.type = + some (AExpr.app (.lam condition domain inner) argument).erase) + (scope : body.Scope input.universes.toNat 0 ∧ + (AExpr.app (.lam condition domain inner) argument).Scope input.universes.toNat 0) + (references : body.ReferencesIn entries ∧ + (AExpr.app (.lam condition domain inner) argument).ReferencesIn entries) : + DefinitionCheckSupport resolve entries trace body (.app (.lam condition domain inner) argument) + +theorem DefinitionCheckSupport.sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {input : DefinitionInput} {fuel : Nat} {before : TcState .anon} + {trace : DefinitionBodyTrace input (methodsN fuel) before} {body type : AExpr β} + (support : DefinitionCheckSupport resolve entries trace body type) + (wellFormed : entries.WF) : + readExpr? resolve input.value = some body.erase ∧ + readExpr? resolve input.type = some type.erase ∧ + body.Scope input.universes.toNat 0 ∧ type.Scope input.universes.toNat 0 ∧ + body.ReferencesIn entries ∧ type.ReferencesIn entries ∧ + TypingClaim.{u,v} entries [] body type := by + cases support with + | hash inference faithful hashPath => + exact inference.sound wellFormed trace.valueRun faithful hashPath + | betaDeclared typeInference valueInference valueReading typeReading scope references => + obtain ⟨_, typeTyped, _⟩ := typeInference.closed_sound typeReading trace.typeRun + obtain ⟨conversion, _⟩ := typeInference.beta_sound (.empty entries) (.empty _ _) + typeReading trace.typeRun + obtain ⟨_, valueTyped, _⟩ := valueInference.closed_sound valueReading trace.valueRun + exact ⟨readScopedExpr?_closed valueReading, readScopedExpr?_closed typeReading, + scope.1, scope.2, references.1, references.2, + valueTyped.conv typeTyped conversion.symm⟩ + +/-- Production validation supplies scope for the beta-converted declaration +and its value. Both inference trees refer to the checks in this exact trace. -/ +def DefinitionBodyTrace.betaDeclaredSupport {β : Type u} {input : DefinitionInput} + {fuel : Nat} {before : TcState .anon} + (trace : DefinitionBodyTrace input (methodsN fuel) before) + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {body domain inner argument : AExpr β} {condition : Certified.PropWhen} + {level typeBound valueBound : VLevel} + {support : RunSupport} (typeCoverage : input.type.ValidationCoverage support) + (valueCoverage : input.value.ValidationCoverage support) + (collision : support.CollisionFree) + (typeInference : SynthesisInference resolve entries [] [] [] fuel trace.validated input.type + (.app (.lam condition domain inner) argument) (.sort level) typeBound) + (valueInference : SynthesisInference resolve entries [] [] [] fuel trace.valueStart input.value + body (inner.inst argument) valueBound) + (valueReading : readScopedExpr? resolve [] input.value = some body.erase) + (typeReading : readScopedExpr? resolve [] input.type = + some (AExpr.app (.lam condition domain inner) argument).erase) + (valueConditions : ConditionsScoped input.universes.toNat body) + (typeConditions : ConditionsScoped input.universes.toNat (.app (.lam condition domain inner) argument)) + (references : body.ReferencesIn entries ∧ + (AExpr.app (.lam condition domain inner) argument).ReferencesIn entries) : + DefinitionCheckSupport resolve entries trace body (.app (.lam condition domain inner) argument) := + .betaDeclared typeInference valueInference valueReading typeReading + (trace.scopes typeCoverage valueCoverage collision valueReading typeReading + valueConditions typeConditions) references + /-- Operational support for the selected production definition fragment. -Resources are required only at the states exposed by successful body traces. -The conversion guard records the actual initial hash-equality path. -/ +Resources are required only at the states exposed by successful body traces. -/ structure AtomicDefinitionRun {β : Type u} (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) (input : DefinitionInput) (before : TcState .anon) (body type : AExpr β) where path : StandalonePrefix input.id before input.constant - inference : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, - DefinitionBodySupport resolve entries (methodsN before.recFuel.toNat) - trace.valueStart input.type input.universes.toNat input.value body type - hashPath : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, - (trace.inferredValue == input.type) = true - faithful : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, - trace.inferredValue.AddrFaithful input.type + support : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, + DefinitionCheckSupport resolve entries trace body type + +/-- The original hash-comparison interface embeds in the extended +declaration boundary with the same execution resources. -/ +def AtomicDefinitionRun.ofHash {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {input : DefinitionInput} {before : TcState .anon} + {body type : AExpr β} (path : StandalonePrefix input.id before input.constant) + (inference : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, + DefinitionBodySupport resolve entries (methodsN before.recFuel.toNat) + trace.valueStart input.type input.universes.toNat input.value body type) + (hashPath : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, + (trace.inferredValue == input.type) = true) + (faithful : ∀ trace : DefinitionBodyTrace input (methodsN before.recFuel.toNat) path.ready, + trace.inferredValue.AddrFaithful input.type) : + AtomicDefinitionRun resolve entries input before body type := + ⟨path, fun trace => .hash (inference trace) (faithful trace) (hashPath trace)⟩ /-- Successful production checking yields a model typing judgment for the actual value and declared type, with syntactic closure and dependency support @@ -438,8 +528,7 @@ theorem AtomicDefinitionRun.sound {β : Type u} body.ReferencesIn entries ∧ type.ReferencesIn entries ∧ TypingClaim.{u,v} entries [] body type := by obtain ⟨trace⟩ := definition_body_trace (fragment.path.member_success accepted) - exact (fragment.inference trace).sound wellFormed trace.valueRun - (fragment.faithful trace) (fragment.hashPath trace) + exact (fragment.support trace).sound wellFormed /-- A fresh definition cannot justify its type through a self-reference: its value, at any universe arguments, must reference the preceding interface. -/ diff --git a/Ix/Kernel/Verify/Whnf.lean b/Ix/Kernel/Verify/Whnf.lean index bc3334848..4efa79637 100644 --- a/Ix/Kernel/Verify/Whnf.lean +++ b/Ix/Kernel/Verify/Whnf.lean @@ -2,6 +2,7 @@ import Ix.Kernel.Verify.Ctx import Ix.Kernel.Verify.Inductive import Ix.Kernel.Verify.Run import Ix.Kernel.Verify.State +import Ix.Kernel.Verify.Whnf.Beta.DirectStep open Ix.Theory (VLevel) @@ -6084,40 +6085,6 @@ theorem whnfCoreWithFlagsStep_letE rw [hwalk] rfl -/-- Exact production step for a direct one-argument beta redex. The head -callback equation is intentionally stronger than `Methods.WF`: semantic -closure alone cannot force a callback to return this syntactic lambda. -/ -theorem whnfCoreWithFlagsStep_betaOne - {methods : Methods .anon} {s s' : TcState .anon} - {nm : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} - {ty body arg result : KExpr .anon} - {lamMd appMd : ExprInfo .anon} {flags : WhnfFlags} - (hhead : methods.whnfCoreFlags (.lam nm bi ty body lamMd) flags s = - .ok (.lam nm bi ty body lamMd) s) - (hwalk : TcM.runIntern (simulSubst body #[arg] 0) s = .ok result s') : - (whnfCoreWithFlagsStep - (.app (.lam nm bi ty body lamMd) arg appMd) flags).run methods s = - .ok (.next result) s' := by - unfold whnfCoreWithFlagsStep - rw [ReaderT.run_bind] - simp only [KExpr.collectSpine, KExpr.collectSpine.go] - change EStateM.bind - (methods.whnfCoreFlags (.lam nm bi ty body lamMd) flags) _ s = _ - unfold EStateM.bind - rw [hhead] - simp [consumeBetaLams, consumeBetaLamsFuel] - change ReaderT.run - (BoundedStep.next <$> liftM - (TcM.runIntern (simulSubst body #[arg] 0)) : - RecM .anon (BoundedStep (KExpr .anon) (KExpr .anon))) methods s = _ - rw [ReaderT.run_map, ReaderT.run_monadLift] - rw [← bind_pure_comp] - change EStateM.bind (TcM.runIntern (simulSubst body #[arg] 0)) - (fun r => pure (BoundedStep.next r)) s = _ - unfold EStateM.bind - rw [hwalk] - rfl - /-- Exact production step for general multi-argument beta. Unlike the single-argument convenience theorem, this exposes the lambda-peeling result, the simultaneous-substitution execution, and rebuilding of only the diff --git a/Ix/Kernel/Verify/Whnf/Beta/DirectStep.lean b/Ix/Kernel/Verify/Whnf/Beta/DirectStep.lean new file mode 100644 index 000000000..05722b7ec --- /dev/null +++ b/Ix/Kernel/Verify/Whnf/Beta/DirectStep.lean @@ -0,0 +1,42 @@ +import Ix.Kernel.Whnf + +/-! The direct beta step's operational equation is shared by the named +verification and the independent set-model refinement. -/ + +namespace Ix.Kernel.RecM + +/-- Exact production step for a direct one-argument beta redex. The head +callback equation records the actual returned lambda, independently of any +semantic specification of the recursive method table. -/ +theorem whnfCoreWithFlagsStep_betaOne + {methods : Methods .anon} {s s' : TcState .anon} + {nm : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {ty body arg result : KExpr .anon} + {lamMd appMd : ExprInfo .anon} {flags : WhnfFlags} + (hhead : methods.whnfCoreFlags (.lam nm bi ty body lamMd) flags s = + .ok (.lam nm bi ty body lamMd) s) + (hwalk : TcM.runIntern (simulSubst body #[arg] 0) s = .ok result s') : + (whnfCoreWithFlagsStep + (.app (.lam nm bi ty body lamMd) arg appMd) flags).run methods s = + .ok (.next result) s' := by + unfold whnfCoreWithFlagsStep + rw [ReaderT.run_bind] + simp only [KExpr.collectSpine, KExpr.collectSpine.go] + change EStateM.bind + (methods.whnfCoreFlags (.lam nm bi ty body lamMd) flags) _ s = _ + unfold EStateM.bind + rw [hhead] + simp [consumeBetaLams, consumeBetaLamsFuel] + change ReaderT.run + (BoundedStep.next <$> liftM + (TcM.runIntern (simulSubst body #[arg] 0)) : + RecM .anon (BoundedStep (KExpr .anon) (KExpr .anon))) methods s = _ + rw [ReaderT.run_map, ReaderT.run_monadLift] + rw [← bind_pure_comp] + change EStateM.bind (TcM.runIntern (simulSubst body #[arg] 0)) + (fun r => pure (BoundedStep.next r)) s = _ + unfold EStateM.bind + rw [hwalk] + rfl + +end Ix.Kernel.RecM diff --git a/Ix/Theory/Model/Substitution.lean b/Ix/Theory/Model/Substitution.lean new file mode 100644 index 000000000..f8141ec4e --- /dev/null +++ b/Ix/Theory/Model/Substitution.lean @@ -0,0 +1,119 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Checking + +/-! +Substitution in the semantic judgments. The argument's typing supplies the +domain membership needed to extend a valid context. Hereditary validity of +an instantiated expected type can also be transported back to the original +body at that same argument value, which permits substitution in checking. +-/ + +namespace Ix.Theory.Model + +open SetTheory SetModel + +universe u v +variable {β : Type u} {V : Type v} [SetTheory V] + +/-- Substitution cannot hide an invalid original subexpression. This +direction needs no validity premise for the argument, since an original +bound variable is hereditarily valid at every valuation. -/ +theorem wellDenoted_of_inst (term argument : AExpr β) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) (cutoff : Nat) + (valid : WellDenoted constants levels env (term.inst argument cutoff)) : + WellDenoted constants levels + (Valuation.insert cutoff (interp constants levels (Valuation.skip cutoff 0 env) argument) env) term := by + induction term generalizing cutoff env with + | bvar index => trivial + | app fn arg ihFn ihArg => + obtain ⟨fnValid, argValid, level, domain, body, fnMember, argMember, bodyBound⟩ := valid + exact ⟨ihFn env cutoff fnValid, ihArg env cutoff argValid, level, domain, body, + by simpa only [interp_inst] using fnMember, + by simpa only [interp_inst] using argMember, bodyBound⟩ + | lam condition domain body ihDomain ihBody => + obtain ⟨domainValid, bodyValid, level, types, agrees, members⟩ := valid + refine ⟨ihDomain env cutoff domainValid, ?_, level, types, agrees, ?_⟩ + · intro x member + have inner := ihBody (Valuation.cons x env) (cutoff + 1) + (bodyValid x (by simpa only [interp_inst] using member)) + simpa only [Valuation.skip_succ_cons, Valuation.insert_cons] using inner + · intro x member + simpa only [interp_inst, Valuation.skip_succ_cons, Valuation.insert_cons] using + members x (by simpa only [interp_inst] using member) + | forallE condition domain body ihDomain ihBody => + obtain ⟨domainValid, bodyValid, level, agrees, members⟩ := valid + refine ⟨ihDomain env cutoff domainValid, ?_, level, agrees, ?_⟩ + · intro x member + have inner := ihBody (Valuation.cons x env) (cutoff + 1) + (bodyValid x (by simpa only [interp_inst] using member)) + simpa only [Valuation.skip_succ_cons, Valuation.insert_cons] using inner + · intro x member + simpa only [interp_inst, Valuation.skip_succ_cons, Valuation.insert_cons] using + members x (by simpa only [interp_inst] using member) + | proj ref field major ih => exact ih env cutoff valid + | _ => trivial + +theorem wellDenoted_inst_iff (term argument : AExpr β) (constants : Assignment β V) + (levels : List Nat) (env : Nat → V) (cutoff : Nat) + (argumentValid : WellDenoted constants levels (Valuation.skip cutoff 0 env) argument) : + WellDenoted constants levels env (term.inst argument cutoff) ↔ + WellDenoted constants levels + (Valuation.insert cutoff (interp constants levels (Valuation.skip cutoff 0 env) argument) env) term := + ⟨wellDenoted_of_inst term argument constants levels env cutoff, + wellDenoted_inst _ _ _ _ _ _ argumentValid⟩ + +/-- Instantiate a typed body using the actual domain of the removed +binder. Both the substituted term and type retain hereditary validity. -/ +theorem TypingClaim.inst {entries : Environment β} {context : Context β} + {term type argument domain : AExpr β} + (body : TypingClaim.{u,v} entries (context.push domain) term type) + (value : TypingClaim.{u,v} entries context argument domain) : + TypingClaim.{u,v} entries context (term.inst argument) (type.inst argument) := by + intro V _ constants realizes levels env valid + obtain ⟨argumentValid, domainValid, argumentMember⟩ := value V constants realizes levels env valid + obtain ⟨termValid, typeValid, member⟩ := body V constants realizes levels + (Valuation.cons (interp constants levels env argument) env) (valid.push domainValid argumentMember) + refine ⟨?_, ?_, ?_⟩ + · apply wellDenoted_inst + · simpa using argumentValid + · simpa using termValid + · apply wellDenoted_inst + · simpa using argumentValid + · simpa using typeValid + · simpa only [interp_inst, Valuation.skip_zero, Valuation.insert_zero] using member + +/-- Checking may defer validity of its expected type, even through +substitution: validity of the instantiated type supplies the body premise. -/ +theorem CheckingClaim.inst {entries : Environment β} {context : Context β} + {term type argument domain : AExpr β} + (body : CheckingClaim.{u,v} entries (context.push domain) term type) + (value : TypingClaim.{u,v} entries context argument domain) : + CheckingClaim.{u,v} entries context (term.inst argument) (type.inst argument) := by + intro V _ constants realizes levels env valid expected + obtain ⟨argumentValid, domainValid, argumentMember⟩ := value V constants realizes levels env valid + have typeValid := wellDenoted_of_inst type argument constants levels env 0 expected + obtain ⟨termValid, member⟩ := body V constants realizes levels + (Valuation.cons (interp constants levels env argument) env) (valid.push domainValid argumentMember) + (by simpa only [Valuation.skip_zero, Valuation.insert_zero] using typeValid) + refine ⟨?_, ?_⟩ + · apply wellDenoted_inst + · simpa using argumentValid + · simpa using termValid + · simpa only [interp_inst, Valuation.skip_zero, Valuation.insert_zero] using member + +theorem ConversionClaim.inst {entries : Environment β} {context : Context β} + {left right argument domain : AExpr β} + (body : ConversionClaim.{u,v} entries (context.push domain) left right) + (value : TypingClaim.{u,v} entries context argument domain) : + ConversionClaim.{u,v} entries context (left.inst argument) (right.inst argument) := by + intro V _ constants realizes levels env valid + have argumentTyped := value V constants realizes levels env valid + simpa only [interp_inst, Valuation.skip_zero, Valuation.insert_zero] using + body V constants realizes levels (Valuation.cons (interp constants levels env argument) env) + (valid.push argumentTyped.2.1 argumentTyped.2.2) + +end Ix.Theory.Model diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index fee74be34..e34c76fd0 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -466,6 +466,64 @@ private def lambdaApplicationLocalResult : Bool := | .ok passed after => passed && after.lctx.size == 0 && after.env.nextFVarId == 3 | .error _ _ => false +/-- The value's inferred type is `A`, but its declared type is +`(fun X : Sort u => X) A`. The final declaration comparison must reduce. -/ +private def betaDeclaredType (level : Ixon.Univ) (universes : UInt64 := 0) + (wrongValue : Bool := false) : Ixon.Env × Address := Id.run do + let (env, carrier) := storeConst {} + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], #[level]⟩ + let (env, witness) := storeConst env + ⟨.axio ⟨false, universes, .ref 0 (if universes == 0 then #[] else #[0])⟩, + #[], #[carrier], #[level]⟩ + let arguments := if universes == 0 then #[] else #[0] + return storeConst env + ⟨.defn ⟨.defn, .safe, universes, + .app (.leanLam (.sort 0) (.var 0)) (.ref 0 arguments), + .ref (if wrongValue then 0 else 1) arguments⟩, + #[], #[carrier, witness], #[level]⟩ + +/-- Substitution through the returned Pi changes both occurrences of the +family parameter. The value is an identity function at the concrete type. -/ +private def betaDeclaredFunction (level : Ixon.Univ) : Ixon.Env := Id.run do + let (env, carrier) := storeConst {} + ⟨.axio ⟨false, 0, .sort 0⟩, #[], #[], #[level]⟩ + return (storeConst env + ⟨.defn ⟨.defn, .safe, 0, + .app (.leanLam (.sort 0) (.leanAll (.var 0) (.var 1))) (.ref 0 #[]), + .leanLam (.ref 0 #[]) (.var 0)⟩, + #[], #[carrier], #[level]⟩).1 + +/-- Observe the unequal initial type hashes and successful repeated +conversion, in addition to the public environment-check regressions. -/ +private def betaDeclaredComparison : Bool := + let (env, target) := betaDeclaredType .zero + let action : RecM .anon Bool := do + let concrete ← TcM.getConst (m := .anon) ⟨target, ()⟩ + let .defn _ _ _ _ _ _ type value _ _ := concrete | return false + let inferred ← RecM.inferCall value + let reduced ← RecM.whnfCoreFlagsRec type .DEF_EQ_CORE + let first ← RecM.isDefEqCall inferred type + let second ← RecM.isDefEqCall inferred type + return inferred != type && reduced == inferred && first && second + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed _ => passed + | .error _ _ => false + +/-- A beta result can retain a lambda and outer registered locals. This +checks capture avoidance in the simultaneous walker used by WHNF. -/ +private def betaUnderBinder : Bool := + let action : RecM .anon Bool := RecM.withLctxScope do + let (carrier, _) ← TcM.openBinder (m := .anon) () () (.mkSort .mkZero) (.mkVar 0 ()) + let (value, _) ← TcM.openBinder (m := .anon) () () carrier (.mkVar 0 ()) + let body := KExpr.mkLam () () carrier (.mkVar 1 ()) + let term := KExpr.mkApp (.mkLam () () carrier body) value + let _ ← RecM.inferCall term + let result ← RecM.whnfCoreFlagsRec term .FULL + return result == KExpr.mkLam () () carrier value + match TcM.runRec action (TcState.ofEnvAnon {}) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + private def applicationCases : TestSeq := test "application environment: Prop/Type identity calls and transitive theorem calls check" (allSucceeded applicationEnvironment 5 { clearEvery := 0 }) @@ -505,6 +563,22 @@ private def applicationCases : TestSeq := (let (env, target) := sortLambdaCall 0; rowFailed env target) ++ test "synthesis environment: a lambda domain must pass its executed sort check" (let (env, target) := failedBinder (.app (.leanLam (.var 0) (.var 0)) (.var 0)); rowFailed env target) + ++ test "beta conversion: declared types reduce in Prop and Type" + (allSucceeded (betaDeclaredType .zero).1 3 && + allSucceeded (betaDeclaredType (.succ .zero)).1 3) + ++ test "beta conversion: declared types reduce at every universe instance" + (allSucceeded (betaDeclaredType (.var 0) 1).1 3) + ++ test "beta conversion: declarations check with fresh per-item caches" + (allSucceeded (betaDeclaredType .zero).1 3 { clearEvery := 1 }) + ++ test "beta conversion: substitution enters a returned dependent function type" + (allSucceeded (betaDeclaredFunction .zero) 2 && + allSucceeded (betaDeclaredFunction (.succ .zero)) 2) + ++ test "beta conversion: unequal initial hashes convert and reuse the result" + betaDeclaredComparison + ++ test "beta reduction: returned lambdas preserve outer locals without capture" + betaUnderBinder + ++ test "beta conversion: reducing a declared proposition cannot make it its own proof" + (let (env, target) := betaDeclaredType .zero 0 true; rowFailed env target) /-- Call a polymorphic identity from a monomorphic function body. Universe indices select entries in the declaration's explicit level table. -/ diff --git a/Tests/Theory/ImportManifest.lean b/Tests/Theory/ImportManifest.lean index ee6cf3cf9..77262ca7a 100644 --- a/Tests/Theory/ImportManifest.lean +++ b/Tests/Theory/ImportManifest.lean @@ -18,7 +18,7 @@ def conLecheRevision : String := "86cd20a65660d757cedc81561a44579099b565d0" /-- New mathematical modules authored in Ix, with no imported source hash. -/ def authored : Array String := #[ "Ix/Theory/Model/Checking.lean", "Ix/Theory/Model/LevelCongruence.lean", - "Ix/Theory/Model/UniverseBounds.lean" + "Ix/Theory/Model/UniverseBounds.lean", "Ix/Theory/Model/Substitution.lean" ] structure SourceFile where diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index b9845e1f7..d34d5e4a7 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -379,9 +379,15 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) annotations, including when levels simplify. Monomorphic references retain their simpler empty-substitution rule. Reduction to expose a Pi, eager arguments, and changed cheap-beta paths require further refinement. -- Conversion takes the initial hash-equality path, with faithfulness of the - compared expressions. General reduction and conversion caches are outside - this fragment. +- `DefinitionCheckSupport` permits the existing initial hash-equality path, + with faithfulness of the compared expressions, or a beta-reducible declared + type. For the beta case, the declaration's own type-inference tree and run + derive equality with the model substitution. The value's inference returns + that substituted type. No additional inference on a generated type is + assumed. This case covers `(fun X : Sort u => X) A` and a returned Pi such + as `(fun X : Sort u => X → X) A`. The successful production comparison is + retained in the declaration trace; its endpoints are justified by the + executed checks. General reduction and conversion caches remain open. - Definitions are added in dependency order with fresh references. Their observations use the states reached in the original serial work order, including cache clearing. `AtomicDefinitionRun.no_self_alias` @@ -470,7 +476,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 398 exact theorem boundaries. The production +The consistency target checks 413 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -484,7 +490,16 @@ The polymorphic inference, substitution, and binder inference roots retain only expression/universe output-length proofs, alongside the standard Lean axioms. Their model-side level congruence introduces no native proof dependency. Semantic checking against a formed type and the new product-fibre universe -bound also use only standard Lean axioms. +bound also use only standard Lean axioms. The substitution rules for model +typing, checking, and equality introduce no additional assumptions. +`SynthesisInference.beta_sound` derives source beta equality and result typing +from the actual lambda and argument inference. Its domain-shape proof retains +information lost when proof values are identified. `beta_step` connects this +to the one-argument production WHNF step, with finite construction, size, and +collision resources for its actual simultaneous-substitution walker. It +derives the lambda-head callback from the real recursive method table and +returns intern coherence. Arbitrary generated redexes and multi-step reduction +still require further proofs. Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, argument order, and rejection of wrong arities and out-of-range parameters. @@ -500,6 +515,10 @@ Direct lambda regressions cover Prop, Type, universe parameters, returned functions, higher-order arguments, dependent proof/data families, and earlier declaration types containing lambda calls. Negative cases reject invalid domains, wrong arguments, and a universe term claimed at its own sort. +Beta regressions cover declared-type conversion in Prop, Type, and at a +universe parameter; substitution through returned function types; unequal +initial hashes and repeated conversion; fresh caches; capture avoidance +beneath returned lambdas; and rejection of a proposition used as its own proof. Polymorphic-call regressions include Prop/Type instances in real function bodies, `max`/`imax` simplification inside Pi domains, closed nested references under active locals, separate cache keys for different universe instances, @@ -569,7 +588,7 @@ Definition-cycle regressions use content-addressed standalone and mutual declarations, including a self-justifying theorem, a two-member cycle, type cycles, lets, shared syntax, and binders. They check repeated member failures, acyclic forward references, cache clearing, and the partial/unsafe policy. -The unit suite contains 589 checks. The anonymous differential additionally +The unit suite contains 596 checks. The anonymous differential additionally serializes eight cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. @@ -612,6 +631,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Source ownership, block registration, and finite preflight | [`Consistency/BlockOwnership.lean`](../Ix/Kernel/Verify/Consistency/BlockOwnership.lean), [`Consistency/SourceOwnershipCheck.lean`](../Ix/Kernel/Verify/Consistency/SourceOwnershipCheck.lean), [`SourceOwnership.lean`](../Ix/Kernel/SourceOwnership.lean) | | Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`Application.lean`](../Ix/Kernel/Verify/Consistency/Application.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | | Inferred type formation and direct lambda applications | [`Consistency/SynthesisInference.lean`](../Ix/Kernel/Verify/Consistency/SynthesisInference.lean), [`Formation.lean`](../Ix/Kernel/Verify/Consistency/Formation.lean), [`Model/UniverseBounds.lean`](../Ix/Theory/Model/UniverseBounds.lean) | +| Source beta reduction and declaration conversion | [`Consistency/Beta.lean`](../Ix/Kernel/Verify/Consistency/Beta.lean), [`BetaSubstitution.lean`](../Ix/Kernel/Verify/Consistency/BetaSubstitution.lean), [`Model/Substitution.lean`](../Ix/Theory/Model/Substitution.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | | Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | | Host commands, receipts, and frozen regression evidence | [Certified checking guide](certified-checking.md) | @@ -625,6 +645,6 @@ fixtures used by the existing proofs. Source hashes and attribution are in [`Tests/Theory/NamedManifest.lean`](../Tests/Theory/NamedManifest.lean); the Apache license is preserved alongside the sources. The axiom-audit helper and direct production-fragment proofs are authored in Ix. -`Model/LevelCongruence.lean` and `Model/Checking.lean` are Ix-authored mathematical -additions, listed +`Model/LevelCongruence.lean`, `Model/Checking.lean`, `Model/UniverseBounds.lean`, +and `Model/Substitution.lean` are Ix-authored mathematical additions, listed separately from the imported files in the theory provenance manifest. diff --git a/docs/theory.md b/docs/theory.md index 184598a77..97892355b 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -42,6 +42,17 @@ or semantic typing premise is required. Earlier executed type checks remain usable after interface growth and universe instantiation. Successful environment rows supply the axiom type-inference traces; public declaration success supplies the corresponding definition traces. +For a source beta redex, the inference tree retains the lambda's checked +domain and supplies the argument's membership in it. This derives equality +and typing for the substituted result; denotational typing alone cannot +recover that domain after proof values have been identified. A direct +one-argument structural-WHNF step reads this result through the actual +simultaneous-substitution walker and preserves intern coherence. Substitution +also preserves the model's typing, checking, and equality judgments. +Declaration admission now includes a beta-reducible declared type: the +declaration's own executed type check justifies conversion from its reduced +form to the original type. The environment model and no-False theorems include +this case. General reduction and conversion remain open. Safe definition admission also rejects circular justification in both Lean and Rust, including `theorem loop : P := loop` with only `P : Prop` assumed. The production dependency walk returns an order with a proved decreasing rank; From 073a42588e99132cb2687a0131fe73b41c712cb8 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 01:53:21 -0400 Subject: [PATCH 25/63] Prove typed beta prefixes for production reduction --- Ix/Kernel/Verify/Consistency.lean | 20 +- Ix/Kernel/Verify/Consistency/Audit.lean | 67 ++++- Ix/Kernel/Verify/Consistency/BetaSpine.lean | 263 ++++++++++++++++++ .../Verify/Consistency/BetaSubstitution.lean | 4 +- Ix/Kernel/Verify/Consistency/CheapBeta.lean | 113 ++++++++ Ix/Kernel/Verify/Consistency/Production.lean | 57 +++- .../Verify/Consistency/Simultaneous.lean | 158 +++++++++++ .../Verify/Consistency/SpineReading.lean | 190 +++++++++++++ Ix/Kernel/Verify/Infer/CheapBeta.lean | 195 +------------ Ix/Kernel/Verify/Infer/CheapBetaPlan.lean | 203 ++++++++++++++ Ix/Kernel/Verify/Whnf.lean | 93 ------- .../Verify/Whnf/Beta/ConsumptionBoundary.lean | 1 + Ix/Kernel/Verify/Whnf/Beta/DirectStep.lean | 109 ++++++++ .../Verify/Whnf/Beta/LambdaInstantiation.lean | 1 + Ix/Kernel/Verify/Whnf/Beta/LambdaPeeling.lean | 10 +- .../Whnf/Beta/SimultaneousSubstitution.lean | 4 +- .../Whnf/Beta/SingletonSubstitution.lean | 2 +- Ix/Theory/Model/BetaSpine.lean | 179 ++++++++++++ Ix/Theory/Model/BetaSubstitution.lean | 157 +++++++++++ Tests/Ix/Kernel/Consistency.lean | 109 +++++++- Tests/Theory/ImportManifest.lean | 3 +- docs/kernel-verification.md | 43 ++- docs/theory.md | 22 +- 23 files changed, 1673 insertions(+), 330 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/BetaSpine.lean create mode 100644 Ix/Kernel/Verify/Consistency/CheapBeta.lean create mode 100644 Ix/Kernel/Verify/Consistency/Simultaneous.lean create mode 100644 Ix/Kernel/Verify/Consistency/SpineReading.lean create mode 100644 Ix/Kernel/Verify/Infer/CheapBetaPlan.lean create mode 100644 Ix/Theory/Model/BetaSpine.lean create mode 100644 Ix/Theory/Model/BetaSubstitution.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 42c7279fc..05df19e57 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -29,6 +29,10 @@ import Ix.Kernel.Verify.Consistency.Formation import Ix.Kernel.Verify.Consistency.SynthesisInference import Ix.Kernel.Verify.Consistency.BetaSubstitution import Ix.Kernel.Verify.Consistency.Beta +import Ix.Kernel.Verify.Consistency.Simultaneous +import Ix.Kernel.Verify.Consistency.SpineReading +import Ix.Kernel.Verify.Consistency.BetaSpine +import Ix.Kernel.Verify.Consistency.CheapBeta import Ix.Kernel.Verify.Consistency.Validation import Ix.Kernel.Verify.Consistency.RecursiveCache import Ix.Kernel.Verify.Consistency.RecursiveState @@ -72,12 +76,16 @@ type checks. An inhabited product supplies a uniform codomain bound at an application, preserving the exact Prop condition. Direct lambda applications therefore synthesize full typing without an extra codomain inference call. Earlier type checks remain reusable after interface growth and universe -instantiation. Source beta redexes retain their checked lambda domains; -substitution and the actual one-argument WHNF step preserve typing and -denotation under finite walker resources. Definition admission can reuse its -executed declared-type inference to justify a beta conversion to the value's -inferred type. This path is included in environment model preservation. -General reduction of generated types remains a separate obligation. +instantiation. Source beta spines retain every original checked lambda +domain. Simultaneous substitution and the actual multi-argument WHNF step +preserve typing and denotation under finite walker resources, including the +remaining application suffix. The substitution bounds concern the original +body and arguments. Selected cheap-beta plans have the same typed meaning +when an actual source check is available. Definition admission can reuse its +executed declared-type inference to justify a beta-prefix conversion to the +value's inferred type. These paths are included in environment model +preservation. Tracking the checking origins of generated types through +changed cheap beta in lambda inference and general reduction remains open. Local cache hits agree with the actual declaration type. Constant hits agree with pure universe substitution of a loaded, admitted declaration; sort hits return the canonical successor sort. Application, forall, and lambda diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 4a9b87e7c..a33633ce3 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -11,6 +11,7 @@ import Ix.Kernel.Verify.Consistency.RecursiveState import Ix.Kernel.Verify.Consistency.SourceAgreement import Ix.Kernel.Verify.Consistency.SourceCache import Ix.Kernel.Verify.Consistency.Dependencies +import Ix.Kernel.Verify.Consistency.CheapBeta import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -251,7 +252,7 @@ private def binderWalkerRoots : Array Lean.Name := #[ ``openBinder_eq, ``openBinder_sound, ``abstractFVars_singleton_spec, ``abstractFVars_readScopedExpr?, ``readScopedExpr?_liftSpec, ``readScopedExpr?_substSpec, ``subst_readScopedExpr?, - ``KExpr.simulSubstSpec_singleton, ``simulSubst_singleton_readScopedExpr?, + ``KExpr.simulSubstSpec_singleton_eq, ``simulSubst_singleton_readScopedExpr?, ``ApplicationSubstitutionData.coherent, ``LambdaClosingData.coherent ] @@ -264,6 +265,67 @@ private def forbiddenProduction : Array Lean.Name := #[ `Ix.Certified.acceptsSerializedStore ] +/-- Typed lambda-prefix reduction and its concrete walker/plan boundaries. -/ +private def betaRoots : Array RootAllowance := #[ + { root := ``Theory.Model.AExpr.appN_nil }, + { root := ``Theory.Model.AExpr.appN_cons }, + { root := ``Theory.Model.LambdaPrefix.inst }, + { root := ``Theory.Model.LambdaPrefix.truncate, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.LambdaPeel.length_bound, standardAxioms := #[``propext] }, + { root := ``Theory.Model.LambdaPeel.inst }, + { root := ``Theory.Model.LambdaPeel.snoc, standardAxioms := #[``propext] }, + { root := ``Theory.Model.LambdaPeel.betaPrefix, standardAxioms := #[``propext] }, + { root := ``Theory.Model.ArgumentSpine.append, standardAxioms := standard }, + { root := ``Theory.Model.ArgumentSpine.typing, standardAxioms := standard }, + { root := ``Theory.Model.ConversionClaim.appN, standardAxioms := standard }, + { root := ``Theory.Model.LambdaPrefix.beta_sound, standardAxioms := standard }, + { root := ``Theory.Model.AExpr.inst_liftN_top, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.instRevAt_zero }, + { root := ``Theory.Model.AExpr.erase_instRevAt, standardAxioms := #[``propext] }, + { root := ``Theory.Model.AExpr.instRevAt_sort }, + { root := ``Theory.Model.AExpr.instRevAt_const }, + { root := ``Theory.Model.AExpr.instRevAt_natLit }, + { root := ``Theory.Model.AExpr.instRevAt_app, standardAxioms := #[``propext] }, + { root := ``Theory.Model.AExpr.instRevAt_lam, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.instRevAt_forallE, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.instRevAt_proj, standardAxioms := #[``propext] }, + { root := ``Theory.Model.AExpr.instRevAt_liftN, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.instRevAt_bvar_below, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.instRevAt_bvar_above, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.instRevAt_bvar_selected, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``BinderInference.lambdaPrefix, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.lambdaPrefix, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisHead.appN_head }, + { root := ``SynthesisInference.lambda_spine, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.beta_spine_sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.beta_peel_sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.beta_many_step, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``readScopedExpr?_simulSubstSpec, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``simulSubst_readScopedExpr?, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``argumentsReading_get, standardAxioms := #[``propext] }, + { root := ``argumentsReading_reverse_get, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``readScopedExpr?_appN, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``readScopedExpr?_collectSpine, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``betaPeel_readScopedExpr?, standardAxioms := #[``propext] }, + { root := ``internAppChain_readScopedExpr?, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``finishAppResult_readScopedExpr?, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``DefinitionBodyTrace.betaDeclaredSpineSupport, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``RecM.appSpineView_go, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``RecM.appSpineView_collectSpine, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``RecM.finishAppResult_eq_foldlM, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``RecM.finishAppResult_eq_internAppChain, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``RecM.whnfCoreWithFlagsStep_betaMany, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``RecM.BetaPeel.fuel, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``RecM.BetaPeel.of_consume, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``RecM.BetaPeel.remaining_eq_drop, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``RecM.BetaPeel.consumed_append_remaining, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``RecM.BetaPeel.prepend }, + { root := ``RecM.BetaPeel.of_peelLamsN, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``WalkerRequest.Bounds.cheapBeta_simul, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``cheapBetaPlan?_simul, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``SynthesisInference.cheapBeta_plan_sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -391,7 +453,8 @@ def roots : Array RootAllowance := #[ }) ++ productionRoots.map (fun root => { root, standardAxioms := standard, nativeAxioms := productionNative, forbiddenDependencies := forbiddenProduction -}) ++ #[{ root := ``extend_atomic_definition, standardAxioms := standard }] +}) ++ betaRoots.map (fun allowance => { allowance with forbiddenDependencies := forbiddenProduction }) + ++ #[{ root := ``extend_atomic_definition, standardAxioms := standard }] run_cmd Kernel.Verify.Audit.check roots diff --git a/Ix/Kernel/Verify/Consistency/BetaSpine.lean b/Ix/Kernel/Verify/Consistency/BetaSpine.lean new file mode 100644 index 000000000..972e37a54 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaSpine.lean @@ -0,0 +1,263 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Beta +import Ix.Kernel.Verify.Consistency.SpineReading +import Ix.Theory.Model.BetaSpine + +/-! Recover the lambda and argument spines from the actual recursive +inference calls, then derive typed reduction of the original lambda prefix. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +private theorem list_reverse_induction {α : Type u} {motive : List α → Prop} + (nil : motive []) + (append_singleton : ∀ tail last, motive tail → motive (tail ++ [last])) + (values : List α) : motive values := by + have reversed : ∀ items : List α, motive items.reverse := by + intro items + induction items with + | nil => exact nil + | cons item items ih => + simpa only [List.reverse_cons] using append_singleton items.reverse item ih + simpa using reversed values.reverse + +theorem BinderInference.lambdaPrefix {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source term type) : + LambdaPrefix term type term.lambdaDepth := by + induction support with + | lam _ _ _ _ _ _ _ _ _ _ _ ih => exact .lam ih + | _ => exact .zero _ _ + +theorem SynthesisInference.lambdaPrefix {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) : + LambdaPrefix term type term.lambdaDepth := by + induction support with + | known inference => exact inference.lambdaPrefix + | reuseType inference => exact inference.lambdaPrefix + | lam _ _ _ _ _ _ _ _ _ _ _ _ _ _ ihBody => exact .lam ihBody + | _ => exact .zero _ _ + +theorem SynthesisHead.appN_head {β : Type u} {head : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisHead (head.appN arguments)) : SynthesisHead head := by + induction arguments generalizing head with + | nil => exact support + | cons argument arguments ih => + have applied := ih support + cases applied with + | app head => exact head + +private theorem BinderInference.no_lambda_spine {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} + {condition : Certified.PropWhen} {domain body type : AExpr β} {arguments : List (AExpr β)} + (support : BinderInference resolve entries locals context fuel before source + ((AExpr.lam condition domain body).appN arguments) type) + (nonempty : arguments ≠ []) : False := by + induction arguments using list_reverse_induction with + | nil => exact nonempty rfl + | append_singleton arguments argument ih => + simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] at support + cases support with + | app _ _ _ _ head => cases head.appN_head + +/-- Actual application inference supplies each dependent argument type, +while the head's inference supplies all original leading lambda domains. -/ +theorem SynthesisInference.lambda_spine {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} {level : VLevel} + {condition : Certified.PropWhen} {domain body type : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisInference resolve entries locals context bounds fuel before source + ((AExpr.lam condition domain body).appN arguments) type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = + some ((AExpr.lam condition domain body).appN arguments).erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + readScopedExpr? resolve locals result = some type.erase ∧ + ∃ headType, TypingClaim.{u,v} entries context (.lam condition domain body) headType ∧ + LambdaPrefix (.lam condition domain body) headType (body.lambdaDepth + 1) ∧ + ArgumentSpine.{u,v} entries context headType arguments type := by + induction arguments using list_reverse_induction generalizing fuel before after source result type level with + | nil => + obtain ⟨reads, typed, _⟩ := support.sound formed agreement reading accepted + exact ⟨reads, _, typed, support.lambdaPrefix, .nil _⟩ + | append_singleton arguments argument ih => + simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] at support reading + cases support with + | known inference => + apply False.elim + apply BinderInference.no_lambda_spine (arguments := arguments ++ [argument]) + (condition := condition) (domain := domain) (body := body) + · simpa only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] using inference + · simp + | reuseType inference => + apply False.elim + apply BinderInference.no_lambda_spine (arguments := arguments ++ [argument]) + (condition := condition) (domain := domain) (body := body) + · simpa only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] using inference + · simp + | app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful + bodyConstructed argConstructed bodyBound argBound coherent faithful => + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + rw [full] at run + obtain ⟨fnReads, argReads⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨functionTypeReads, headType, headTyped, leading, spine⟩ := + ih functionTree keyedAgreement fnReads trace.functionRun + obtain ⟨domainReads, codomainReads⟩ := readScopedExpr?_all_parts functionTypeReads + have argumentAgreement := trace.contextPreserved.symm ▸ keyedAgreement + obtain ⟨argumentTypeReads, argumentTyped, _⟩ := + argumentTree.sound formed argumentAgreement argReads trace.argumentRun + have sameReading := beq_readScopedExpr? (resolve := resolve) (locals := locals) + (depth := 0) comparisonFaithful hashPath + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (argumentTypeReads.symm.trans (sameReading.trans domainReads))) conditions + refine ⟨?_, headType, headTyped, leading, + spine.append (.cons (sameType ▸ argumentTyped) (.nil _))⟩ + rw [trace.output run, AExpr.erase_inst] + exact (subst_readScopedExpr? bodyConstructed argConstructed bodyBound argBound + coherent faithful codomainReads argReads).1 + +/-- The complete original lambda prefix may be consumed without another +inference call on any intermediate beta result. -/ +theorem SynthesisInference.beta_spine_sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} {level : VLevel} + {condition : Certified.PropWhen} {domain body type : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisInference resolve entries locals context bounds fuel before source + ((AExpr.lam condition domain body).appN arguments) type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = + some ((AExpr.lam condition domain body).appN arguments).erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + ConversionClaim.{u,v} entries context ((AExpr.lam condition domain body).appN arguments) + (AExpr.betaPrefix (body.lambdaDepth + 1) (.lam condition domain body) arguments) ∧ + TypingClaim.{u,v} entries context + (AExpr.betaPrefix (body.lambdaDepth + 1) (.lam condition domain body) arguments) type := by + obtain ⟨_, _, typed, leading, spine⟩ := support.lambda_spine formed agreement reading accepted + exact leading.beta_sound typed spine + +/-- Any prefix selected by production's lambda peel has the simultaneous +substitution meaning, with the remaining arguments in their original order. -/ +theorem SynthesisInference.beta_peel_sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} {level : VLevel} + {condition : Certified.PropWhen} {domain inner body type : AExpr β} + {consumed trailing : List (AExpr β)} + (support : SynthesisInference resolve entries locals context bounds fuel before source + ((AExpr.lam condition domain inner).appN (consumed ++ trailing)) type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = + some ((AExpr.lam condition domain inner).appN (consumed ++ trailing)).erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) + (peeling : LambdaPeel (.lam condition domain inner) consumed.length body) : + ConversionClaim.{u,v} entries context + ((AExpr.lam condition domain inner).appN (consumed ++ trailing)) + ((body.instRev consumed).appN trailing) ∧ + TypingClaim.{u,v} entries context ((body.instRev consumed).appN trailing) type := by + obtain ⟨_, _, typed, leading, spine⟩ := support.lambda_spine formed agreement reading accepted + have result := (leading.truncate peeling.length_bound).beta_sound typed spine + rwa [peeling.betaPrefix trailing] at result + +/-- The production multi-argument beta step preserves typing and model +meaning. Its peeled body and consumed argument order come from the actual +loop, and its suffix is rebuilt by the actual interned application chain. -/ +theorem SynthesisInference.beta_many_step {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {inferenceBefore inferenceAfter : TcState .anon} {inferred : KExpr .anon} {level : VLevel} + {rawFunction rawArgument : KExpr .anon} {appInfo : ExprInfo .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {rawDomain rawInner rawBody : KExpr .anon} {lambdaInfo : ExprInfo .anon} + {rawArguments consumed : Array (KExpr .anon)} + {condition : Certified.PropWhen} {domain inner type : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisInference resolve entries locals context bounds fuel inferenceBefore + (.app rawFunction rawArgument appInfo) + ((AExpr.lam condition domain inner).appN arguments) type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals inferenceBefore.lctx context) + (spine : (KExpr.app rawFunction rawArgument appInfo).collectSpine = + (.lam name bi rawDomain rawInner lambdaInfo, rawArguments)) + (headReads : readScopedExpr? resolve locals (.lam name bi rawDomain rawInner lambdaInfo) = + some (AExpr.lam condition domain inner).erase) + (argumentReads : rawArguments.toList.map (readScopedExpr? resolve locals ·) = + arguments.map (some ·.erase)) + (accepted : RecM.infer (.app rawFunction rawArgument appInfo) + (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) + (peeling : RecM.consumeBetaLams (.lam name bi rawDomain rawInner lambdaInfo) rawArguments = + (rawBody, consumed)) + (nonempty : (!consumed.isEmpty) = true) + (before : TcState .anon) (reductionFuel : Nat) (flags : WhnfFlags) + (walkerBounds : SimulSubstBounds rawBody consumed.reverse 0) + (coherent : before.env.intern.WF) + (walkerFaithful : KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ + KExpr.SimulSubstReach consumed.reverse rawBody 0 term) + (suffixFaithful : KExpr.CollisionFree fun term => + (simulSubst rawBody consumed.reverse 0 before.env.intern).2.ExprSupport term ∨ + term ∈ cheapBetaChainList (simulSubst rawBody consumed.reverse 0 before.env.intern).1 + (rawArguments.extract consumed.size rawArguments.size).toList) : + ∃ (body : AExpr β) (result : KExpr .anon) (after : TcState .anon), + (RecM.whnfCoreWithFlagsStep (.app rawFunction rawArgument appInfo) flags).run + (methodsN (reductionFuel + 1)) before = .ok (.next result) after ∧ + readScopedExpr? resolve locals result = + some ((body.instRev (arguments.take consumed.size)).appN (arguments.drop consumed.size)).erase ∧ + ConversionClaim.{u,v} entries context ((AExpr.lam condition domain inner).appN arguments) + ((body.instRev (arguments.take consumed.size)).appN (arguments.drop consumed.size)) ∧ + TypingClaim.{u,v} entries context + ((body.instRev (arguments.take consumed.size)).appN (arguments.drop consumed.size)) type ∧ + after.env.intern.WF := by + have reading := readScopedExpr?_collectSpine spine headReads argumentReads + obtain ⟨rawPeel, consumedPrefix, consumedBound⟩ := RecM.BetaPeel.of_consume peeling + obtain ⟨body, modelPeel, bodyReads⟩ := betaPeel_readScopedExpr? rawPeel headReads + have sizeAgrees : rawArguments.size = arguments.length := by + have lengths := congrArg List.length argumentReads + simpa using lengths + have consumedSize : (arguments.take consumed.size).length = consumed.size := by + simp only [List.length_take] + omega + have consumedReads : consumed.toList.map (readScopedExpr? resolve locals ·) = + (arguments.take consumed.size).map (some ·.erase) := by + rw [consumedPrefix, List.map_take, argumentReads, List.map_take] + have suffixReads : (rawArguments.extract consumed.size rawArguments.size).toList.map + (readScopedExpr? resolve locals ·) = (arguments.drop consumed.size).map (some ·.erase) := by + rw [RecM.BetaPeel.remaining_eq_drop peeling, List.map_drop, argumentReads, List.map_drop] + obtain ⟨walkReads, walkCoherent⟩ := simulSubst_readScopedExpr? + (by simpa only [Array.size_reverse] using consumedSize.symm) + walkerBounds.1 walkerBounds.2.1 (by simpa using walkerBounds.2.2.2.1) + walkerBounds.2.2.1 coherent walkerFaithful + (by simpa only [Nat.zero_add, Array.length_toList, consumedSize] using bodyReads) + (argumentsReading_reverse_get consumedReads consumedSize.symm) + have conversion := SynthesisInference.beta_peel_sound (consumed := arguments.take consumed.size) + (trailing := arguments.drop consumed.size) + (by simpa only [List.take_append_drop] using support) formed agreement + (by simpa only [List.take_append_drop] using reading) accepted + (by simpa only [consumedSize, Array.length_toList] using modelPeel) + let walk := simulSubst rawBody consumed.reverse 0 before.env.intern + let middle := { before with env := { before.env with intern := walk.2 } } + obtain ⟨result, after, finish, resultReads, preserved⟩ := + finishAppResult_readScopedExpr? (before := middle) walkCoherent suffixFaithful walkReads suffixReads + (methodsN (reductionFuel + 1)) + refine ⟨body, result, after, ?_, resultReads, ?_, conversion.2, preserved⟩ + · exact RecM.whnfCoreWithFlagsStep_betaMany spine rfl peeling nonempty rfl finish + · simpa only [List.take_append_drop] using conversion.1 + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaSubstitution.lean b/Ix/Kernel/Verify/Consistency/BetaSubstitution.lean index ab3e21860..04e460e91 100644 --- a/Ix/Kernel/Verify/Consistency/BetaSubstitution.lean +++ b/Ix/Kernel/Verify/Consistency/BetaSubstitution.lean @@ -12,7 +12,7 @@ namespace Ix.Kernel /-- The two pure walker specifications coincide for one replacement, including index shifting above the removed binder. -/ -theorem KExpr.simulSubstSpec_singleton {body argument : KExpr .anon} {depth : UInt64} +theorem KExpr.simulSubstSpec_singleton_eq {body argument : KExpr .anon} {depth : UInt64} (bound : depth.toNat + body.size + 1 < UInt64.size) : KExpr.simulSubstSpec body #[argument] depth = KExpr.substSpec body argument depth := by induction body generalizing depth with @@ -94,7 +94,7 @@ theorem simulSubst_singleton_readScopedExpr? {β : Type u} exact argumentBound) (by simpa using bodyBound) (fun _ h => Or.inr h) coherent (fun _ h => Or.inl h) refine ⟨?_, preserved⟩ - rw [result, KExpr.simulSubstSpec_singleton (by simpa using bodyBound)] + rw [result, KExpr.simulSubstSpec_singleton_eq (by simpa using bodyBound)] exact readScopedExpr?_substSpec (depth := 0) (by simpa using bodyBound) argumentBound bodyReads argumentReads diff --git a/Ix/Kernel/Verify/Consistency/CheapBeta.lean b/Ix/Kernel/Verify/Consistency/CheapBeta.lean new file mode 100644 index 000000000..32e0a6884 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/CheapBeta.lean @@ -0,0 +1,113 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaSpine +import Ix.Kernel.Verify.Infer.CheapBetaPlan + +/-! The selected cheap-beta plan has the same typed prefix meaning as +general beta, including its variable-selection and closed-body cases. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- A changed cheap-beta result is justified by an actual check of its +source. This theorem can reuse an earlier type check; it never assumes a +new inference call on a generated substitution result. -/ +theorem SynthesisInference.cheapBeta_plan_sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {inferenceBefore inferenceAfter : TcState .anon} {source inferred : KExpr .anon} {level : VLevel} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {rawDomain rawInner : KExpr .anon} {lambdaInfo : ExprInfo .anon} + {rawArguments : Array (KExpr .anon)} {plan : CheapBetaPlan .anon} + {condition : Certified.PropWhen} {domain inner type : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisInference resolve entries locals context bounds fuel inferenceBefore source + ((AExpr.lam condition domain inner).appN arguments) type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals inferenceBefore.lctx context) + (spine : source.collectSpine = (.lam name bi rawDomain rawInner lambdaInfo, rawArguments)) + (headReads : readScopedExpr? resolve locals (.lam name bi rawDomain rawInner lambdaInfo) = + some (AExpr.lam condition domain inner).erase) + (argumentReads : rawArguments.toList.map (readScopedExpr? resolve locals ·) = + arguments.map (some ·.erase)) + (accepted : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) + (selected : cheapBetaPlan? source = some plan) + (walkerBounds : KExpr.CheapBetaBounds source) + (table : InternTable .anon) (coherent : table.WF) + (faithful : KExpr.CollisionFree fun term => table.ExprSupport term ∨ KExpr.CheapBetaReach source term) : + ∃ (body : AExpr β) (count : Nat), + readScopedExpr? resolve locals (cheapBetaReduce source table).1 = + some ((body.instRev (arguments.take count)).appN (arguments.drop count)).erase ∧ + ConversionClaim.{u,v} entries context ((AExpr.lam condition domain inner).appN arguments) + ((body.instRev (arguments.take count)).appN (arguments.drop count)) ∧ + TypingClaim.{u,v} entries context + ((body.instRev (arguments.take count)).appN (arguments.drop count)) type ∧ + (cheapBetaReduce source table).2.WF := by + obtain ⟨head, rawBody, args, count, plannedSpine, peeling, countBound, base, trailing⟩ := + cheapBetaPlan?_simul selected walkerBounds + obtain ⟨sameHead, sameArgs⟩ := Prod.mk.inj (spine.symm.trans plannedSpine) + subst head args + have reading := readScopedExpr?_collectSpine spine headReads argumentReads + have rawPeel := RecM.BetaPeel.of_peelLamsN + (.lam name bi rawDomain rawInner lambdaInfo) rawArguments.toList + rw [Array.length_toList, peeling] at rawPeel + obtain ⟨body, modelPeel, bodyReads⟩ := betaPeel_readScopedExpr? rawPeel.1 headReads + have sizeAgrees : rawArguments.size = arguments.length := by + have lengths := congrArg List.length argumentReads + simpa using lengths + have countLength : (arguments.take count).length = count := by + simp only [List.length_take] + omega + have rawCountLength : (rawArguments.toList.take count).length = count := by + simp only [List.length_take, Array.length_toList] + omega + have prefixSize : (rawArguments.extract 0 count).size = count := by + simp only [Array.size_extract] + omega + have prefixReads : (rawArguments.extract 0 count).toList.map (readScopedExpr? resolve locals ·) = + (arguments.take count).map (some ·.erase) := by + simp only [Array.toList_extract, List.extract_eq_take_drop, List.drop_zero, Nat.sub_zero, + List.map_take, argumentReads] + have suffixReads : plan.trailing.map (readScopedExpr? resolve locals ·) = + (arguments.drop count).map (some ·.erase) := by + rw [trailing] + simp only [Array.toList_extract, List.extract_eq_take_drop, List.map_take, List.map_drop, + argumentReads, sizeAgrees] + rw [← List.map_drop, ← List.map_take] + congr 1 + have length : (arguments.drop count).length = arguments.length - count := List.length_drop + rw [← length, List.take_length] + have simulBounds := walkerBounds.2 spine peeling + have baseReads : readScopedExpr? resolve locals plan.base = + some (body.instRev (arguments.take count)).erase := by + rw [base] + have readSimul := readScopedExpr?_simulSubstSpec (depth := 0) + (by simp only [Array.size_reverse, prefixSize, countLength]) + simulBounds.2.2.2.1 simulBounds.2.2.1 + (by simpa only [UInt64.toNat_zero, Nat.zero_add, countLength, rawCountLength] using bodyReads) + (argumentsReading_reverse_get prefixReads (prefixSize.trans countLength.symm)) + simpa only [UInt64.toNat_zero, Nat.zero_add, AExpr.instRevAt_zero] using readSimul + have chainFaithful : KExpr.CollisionFree fun term => table.ExprSupport term ∨ + term ∈ cheapBetaChainList plan.base plan.trailing := by + apply faithful.mono + intro term member + rcases member with resident | candidate + · exact Or.inl resident + · exact Or.inr (by simpa [KExpr.CheapBetaReach, selected] using Or.inr candidate) + obtain ⟨resultReads, preserved⟩ := internAppChain_readScopedExpr? coherent chainFaithful baseReads suffixReads + have conversion := SynthesisInference.beta_peel_sound (consumed := arguments.take count) + (trailing := arguments.drop count) + (by simpa only [List.take_append_drop] using support) formed agreement + (by simpa only [List.take_append_drop] using reading) accepted + (by simpa only [countLength, rawCountLength] using modelPeel) + refine ⟨body, count, ?_, ?_, conversion.2, ?_⟩ + · simpa only [cheapBetaReduce, selected] using resultReads + · simpa only [List.take_append_drop] using conversion.1 + · simpa only [cheapBetaReduce, selected] using preserved + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Production.lean b/Ix/Kernel/Verify/Consistency/Production.lean index 85e2c05cd..24e4ef3b6 100644 --- a/Ix/Kernel/Verify/Consistency/Production.lean +++ b/Ix/Kernel/Verify/Consistency/Production.lean @@ -6,7 +6,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Driver import Ix.Kernel.Verify.Consistency.Constant import Ix.Kernel.Verify.Consistency.SynthesisInference -import Ix.Kernel.Verify.Consistency.Beta +import Ix.Kernel.Verify.Consistency.BetaSpine import Ix.Kernel.Verify.Consistency.Validation /-! @@ -438,6 +438,22 @@ inductive DefinitionCheckSupport {β : Type u} (references : body.ReferencesIn entries ∧ (AExpr.app (.lam condition domain inner) argument).ReferencesIn entries) : DefinitionCheckSupport resolve entries trace body (.app (.lam condition domain inner) argument) + | betaDeclaredSpine {value domain inner body : AExpr β} {condition : Certified.PropWhen} + {consumed trailing : List (AExpr β)} {level typeBound valueBound : VLevel} + (typeInference : SynthesisInference resolve entries [] [] [] fuel trace.validated input.type + ((AExpr.lam condition domain inner).appN (consumed ++ trailing)) (.sort level) typeBound) + (valueInference : SynthesisInference resolve entries [] [] [] fuel trace.valueStart input.value + value ((body.instRev consumed).appN trailing) valueBound) + (peeling : LambdaPeel (.lam condition domain inner) consumed.length body) + (valueReading : readScopedExpr? resolve [] input.value = some value.erase) + (typeReading : readScopedExpr? resolve [] input.type = + some ((AExpr.lam condition domain inner).appN (consumed ++ trailing)).erase) + (scope : value.Scope input.universes.toNat 0 ∧ + ((AExpr.lam condition domain inner).appN (consumed ++ trailing)).Scope input.universes.toNat 0) + (references : value.ReferencesIn entries ∧ + ((AExpr.lam condition domain inner).appN (consumed ++ trailing)).ReferencesIn entries) : + DefinitionCheckSupport resolve entries trace value + ((AExpr.lam condition domain inner).appN (consumed ++ trailing)) theorem DefinitionCheckSupport.sound {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} @@ -461,6 +477,14 @@ theorem DefinitionCheckSupport.sound {β : Type u} exact ⟨readScopedExpr?_closed valueReading, readScopedExpr?_closed typeReading, scope.1, scope.2, references.1, references.2, valueTyped.conv typeTyped conversion.symm⟩ + | betaDeclaredSpine typeInference valueInference peeling valueReading typeReading scope references => + obtain ⟨_, typeTyped, _⟩ := typeInference.closed_sound typeReading trace.typeRun + obtain ⟨conversion, _⟩ := typeInference.beta_peel_sound (.empty entries) (.empty _ _) + typeReading trace.typeRun peeling + obtain ⟨_, valueTyped, _⟩ := valueInference.closed_sound valueReading trace.valueRun + exact ⟨readScopedExpr?_closed valueReading, readScopedExpr?_closed typeReading, + scope.1, scope.2, references.1, references.2, + valueTyped.conv typeTyped conversion.symm⟩ /-- Production validation supplies scope for the beta-converted declaration and its value. Both inference trees refer to the checks in this exact trace. -/ @@ -489,6 +513,37 @@ def DefinitionBodyTrace.betaDeclaredSupport {β : Type u} {input : DefinitionInp (trace.scopes typeCoverage valueCoverage collision valueReading typeReading valueConditions typeConditions) references +/-- Admit a declaration whose checked type reduces through a lambda +prefix and an untouched argument suffix. Validation supplies both source +scopes; no inference of an intermediate beta result is required. -/ +def DefinitionBodyTrace.betaDeclaredSpineSupport {β : Type u} {input : DefinitionInput} + {fuel : Nat} {before : TcState .anon} + (trace : DefinitionBodyTrace input (methodsN fuel) before) + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {value domain inner body : AExpr β} {condition : Certified.PropWhen} + {consumed trailing : List (AExpr β)} {level typeBound valueBound : VLevel} + {support : RunSupport} (typeCoverage : input.type.ValidationCoverage support) + (valueCoverage : input.value.ValidationCoverage support) + (collision : support.CollisionFree) + (typeInference : SynthesisInference resolve entries [] [] [] fuel trace.validated input.type + ((AExpr.lam condition domain inner).appN (consumed ++ trailing)) (.sort level) typeBound) + (valueInference : SynthesisInference resolve entries [] [] [] fuel trace.valueStart input.value + value ((body.instRev consumed).appN trailing) valueBound) + (peeling : LambdaPeel (.lam condition domain inner) consumed.length body) + (valueReading : readScopedExpr? resolve [] input.value = some value.erase) + (typeReading : readScopedExpr? resolve [] input.type = + some ((AExpr.lam condition domain inner).appN (consumed ++ trailing)).erase) + (valueConditions : ConditionsScoped input.universes.toNat value) + (typeConditions : ConditionsScoped input.universes.toNat + ((AExpr.lam condition domain inner).appN (consumed ++ trailing))) + (references : value.ReferencesIn entries ∧ + ((AExpr.lam condition domain inner).appN (consumed ++ trailing)).ReferencesIn entries) : + DefinitionCheckSupport resolve entries trace value + ((AExpr.lam condition domain inner).appN (consumed ++ trailing)) := + .betaDeclaredSpine typeInference valueInference peeling valueReading typeReading + (trace.scopes typeCoverage valueCoverage collision valueReading typeReading + valueConditions typeConditions) references + /-- Operational support for the selected production definition fragment. Resources are required only at the states exposed by successful body traces. -/ structure AtomicDefinitionRun {β : Type u} (resolve : Address → Option (ConstRef β)) diff --git a/Ix/Kernel/Verify/Consistency/Simultaneous.lean b/Ix/Kernel/Verify/Consistency/Simultaneous.lean new file mode 100644 index 000000000..c001dedfa --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Simultaneous.lean @@ -0,0 +1,158 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Application +import Ix.Theory.Model.BetaSubstitution + +/-! Read simultaneous substitution in one structural pass, using the +original body and argument bounds rather than bounds on generated trees. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +private theorem bind_success {α γ : Type _} {action : Option α} + {next : α → Option γ} {result : γ} (run : action.bind next = some result) : + ∃ intermediate, action = some intermediate ∧ next intermediate = some result := by + cases action with + | none => contradiction + | some value => exact ⟨value, rfl, run⟩ + +theorem readScopedExpr?_simulSubstSpec {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {body : KExpr .anon} {source : AExpr β} {substs : Array (KExpr .anon)} + {arguments : List (AExpr β)} {depth : UInt64} + (sizeAgrees : substs.size = arguments.length) + (bound : depth.toNat + body.size + substs.size < UInt64.size) + (argumentBounds : ∀ index, index < substs.size → substs[index]!.size < UInt64.size) + (bodyReads : readScopedExpr? resolve locals body (depth.toNat + arguments.length) = some source.erase) + (argumentReads : ∀ index (small : index < substs.size), + readScopedExpr? resolve locals substs[index]! = + some (arguments[arguments.length - index - 1]'(by omega)).erase) : + readScopedExpr? resolve locals (KExpr.simulSubstSpec body substs depth) depth.toNat = + some (source.instRevAt arguments depth.toNat).erase := by + induction body generalizing source depth with + | var index name info => + simp only [readScopedExpr?] at bodyReads + split at bodyReads + next inScope => + cases source <;> cases bodyReads + have sizeNat : substs.size.toUInt64.toNat = substs.size := by + change substs.size % UInt64.size = substs.size + apply Nat.mod_eq_of_lt + simp only [KExpr.size] at bound + omega + have upper : (depth + substs.size.toUInt64).toNat = depth.toNat + substs.size := by + rw [UInt64.toNat_add, sizeNat] + exact Nat.mod_eq_of_lt (show depth.toNat + substs.size < UInt64.size by + simp only [KExpr.size] at bound + omega) + by_cases below : index.toNat < depth.toNat + · have outside : ¬ index ≥ depth := by simpa only [UInt64.le_iff_toNat_le] using Nat.not_le.mpr below + have outsideUpper : ¬ index ≥ depth + substs.size.toUInt64 := by + intro contrary + have inequality := UInt64.le_iff_toNat_le.mp contrary + rw [upper] at inequality + omega + simp [KExpr.simulSubstSpec, outside, outsideUpper, readScopedExpr?, below, + AExpr.instRevAt_bvar_below arguments index.toNat depth.toNat below, AExpr.erase] + · have inRange : (index ≥ depth && index < depth + substs.size.toUInt64) = true := by + simp only [Bool.and_eq_true, decide_eq_true_eq, UInt64.le_iff_toNat_le, + UInt64.lt_iff_toNat_lt, upper] + constructor <;> omega + have offset : (index - depth).toNat = index.toNat - depth.toNat := + UInt64.toNat_sub_of_le index depth (UInt64.le_iff_toNat_le.mpr (by omega)) + have small : index.toNat - depth.toNat < substs.size := by omega + rw [KExpr.simulSubstSpec, if_pos inRange, offset] + have lifted := readScopedExpr?_liftSpec (depth := 0) (shift := depth) + (by simpa using argumentBounds _ small) (argumentReads _ small) + have selected := AExpr.instRevAt_bvar_selected arguments + (index.toNat - depth.toNat) depth.toNat (by omega) + rw [show depth.toNat + (index.toNat - depth.toNat) = index.toNat by omega] at selected + rw [selected, AExpr.erase_liftN] + simpa only [UInt64.toNat_zero, Nat.zero_add] using lifted + · contradiction + | fvar id name info => + rw [readScopedExpr?] at bodyReads + obtain ⟨index, found, bodyReads⟩ := Option.map_eq_some_iff.mp bodyReads + cases source <;> cases bodyReads + rw [AExpr.instRevAt_bvar_above arguments _ _ (by omega)] + simp [KExpr.simulSubstSpec, readScopedExpr?, found, AExpr.erase, + show depth.toNat + arguments.length + index - arguments.length = depth.toNat + index by omega] + | sort level info => + cases source <;> cases bodyReads + simp [KExpr.simulSubstSpec, readScopedExpr?, AExpr.erase] + | nat value blob info => + cases source <;> cases bodyReads + simp [KExpr.simulSubstSpec, readScopedExpr?, AExpr.erase] + | const id levels info => + rw [readScopedExpr?] at bodyReads + obtain ⟨ref, resolved, bodyReads⟩ := bind_success bodyReads + cases source <;> cases bodyReads + simp [KExpr.simulSubstSpec, readScopedExpr?, resolved, AExpr.erase] + | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | app fn arg info ihFn ihArg => + rw [readScopedExpr?] at bodyReads + obtain ⟨f, fnReads, bodyReads⟩ := bind_success bodyReads + obtain ⟨a, argReads, bodyReads⟩ := bind_success bodyReads + cases source <;> cases bodyReads + simp only [KExpr.size] at bound + simp [KExpr.simulSubstSpec, AExpr.erase, + ihFn (depth := depth) (by omega) fnReads argumentReads, + ihArg (depth := depth) (by omega) argReads argumentReads] + | lam name bi domain body info ihDomain ihBody | all name bi domain body info ihDomain ihBody => + rw [readScopedExpr?] at bodyReads + obtain ⟨A, domainReads, bodyReads⟩ := bind_success bodyReads + obtain ⟨B, innerReads, bodyReads⟩ := bind_success bodyReads + cases source <;> cases bodyReads + simp only [KExpr.size] at bound + have next : (depth + 1).toNat = depth.toNat + 1 := by + rw [UInt64.toNat_add, show (1 : UInt64).toNat = 1 from rfl, + Nat.mod_eq_of_lt (show depth.toNat + 1 < UInt64.size by omega)] + have innerOut := ihBody (depth := depth + 1) (by rw [next]; omega) + (by simpa only [next, Nat.add_assoc, Nat.add_comm, Nat.add_left_comm] using innerReads) argumentReads + simp only [next] at innerOut + simp [KExpr.simulSubstSpec, AExpr.erase, + ihDomain (depth := depth) (by omega) domainReads argumentReads, innerOut] + | prj id field major info ih => + rw [readScopedExpr?] at bodyReads + obtain ⟨ref, resolved, bodyReads⟩ := bind_success bodyReads + obtain ⟨value, valueReads, bodyReads⟩ := bind_success bodyReads + cases source <;> cases bodyReads + simp only [KExpr.size] at bound + simp [KExpr.simulSubstSpec, resolved, AExpr.erase, + ih (depth := depth) (by omega) valueReads argumentReads] + +/-- The memoized production walker returns the reading established by the +single structural pass and preserves intern coherence. -/ +theorem simulSubst_readScopedExpr? {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {body : KExpr .anon} {source : AExpr β} {substs : Array (KExpr .anon)} + {arguments : List (AExpr β)} {table : InternTable .anon} + (sizeAgrees : substs.size = arguments.length) + (bodyConstructed : body.Constructed) + (argumentConstructed : ∀ index, index < substs.size → substs[index]!.Constructed) + (bodyBound : body.size + substs.size < UInt64.size) + (argumentBounds : ∀ index, index < substs.size → substs[index]!.size < UInt64.size) + (coherent : table.WF) + (faithful : KExpr.CollisionFree fun term => table.ExprSupport term ∨ + KExpr.SimulSubstReach substs body 0 term) + (bodyReads : readScopedExpr? resolve locals body arguments.length = some source.erase) + (argumentReads : ∀ index (small : index < substs.size), + readScopedExpr? resolve locals substs[index]! = + some (arguments[arguments.length - index - 1]'(by omega)).erase) : + readScopedExpr? resolve locals (simulSubst body substs 0 table).1 = some (source.instRev arguments).erase ∧ + (simulSubst body substs 0 table).2.WF := by + obtain ⟨result, preserved, _⟩ := simulSubst_spec faithful bodyConstructed argumentConstructed + argumentBounds (by simpa using bodyBound) (fun _ h => Or.inr h) coherent (fun _ h => Or.inl h) + refine ⟨?_, preserved⟩ + rw [result] + simpa only [UInt64.toNat_zero, Nat.zero_add, AExpr.instRevAt_zero] using + (readScopedExpr?_simulSubstSpec (depth := 0) sizeAgrees (by simpa using bodyBound) + argumentBounds (by simpa using bodyReads) argumentReads) + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SpineReading.lean b/Ix/Kernel/Verify/Consistency/SpineReading.lean new file mode 100644 index 000000000..ba9fd524f --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SpineReading.lean @@ -0,0 +1,190 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Simultaneous +import Ix.Kernel.Verify.Support +import Ix.Kernel.Verify.Whnf.Beta.DirectStep +import Ix.Kernel.Verify.Whnf.Beta.LambdaPeeling +import Ix.Theory.Model.BetaSpine + +/-! Read the actual application spine, peeled lambda body, and interned +suffix used by beta reduction. All resources concern concrete syntax. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +private theorem bind_success {α γ : Type _} {action : Option α} + {next : α → Option γ} {result : γ} (run : action.bind next = some result) : + ∃ intermediate, action = some intermediate ∧ next intermediate = some result := by + cases action with + | none => contradiction + | some value => exact ⟨value, rfl, run⟩ + +theorem argumentsReading_get {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {raw : List (KExpr .anon)} {arguments : List (AExpr β)} + (reading : raw.map (readScopedExpr? resolve locals ·) = arguments.map (some ·.erase)) + (index : Nat) (rawSmall : index < raw.length) (modelSmall : index < arguments.length) : + readScopedExpr? resolve locals raw[index] = some arguments[index].erase := by + have selected := congrArg (fun values => values[index]?) reading + simpa only [List.getElem?_map, List.getElem?_eq_getElem rawSmall, + List.getElem?_eq_getElem modelSmall, Option.map_some, Option.some.injEq] using selected + +theorem argumentsReading_reverse_get {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {raw : Array (KExpr .anon)} {arguments : List (AExpr β)} + (reading : raw.toList.map (readScopedExpr? resolve locals ·) = arguments.map (some ·.erase)) + (sizeAgrees : raw.size = arguments.length) + (index : Nat) (small : index < raw.reverse.size) : + readScopedExpr? resolve locals raw.reverse[index]! = + some (arguments[arguments.length - index - 1]'(by simp only [Array.size_reverse] at small; omega)).erase := by + rw [getElem!_pos raw.reverse index small, Array.getElem_reverse] + have selected := argumentsReading_get reading (raw.size - 1 - index) + (by simp only [Array.length_toList, Array.size_reverse] at *; omega) + (by simp only [Array.size_reverse] at small; omega) + simpa only [Array.getElem_toList, sizeAgrees, + show arguments.length - 1 - index = arguments.length - index - 1 by omega] using selected + +theorem readScopedExpr?_appN {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {rawHead : KExpr .anon} {head : AExpr β} + {rawArguments : List (KExpr .anon)} {arguments : List (AExpr β)} + (headReads : readScopedExpr? resolve locals rawHead = some head.erase) + (argumentReads : rawArguments.map (readScopedExpr? resolve locals ·) = + arguments.map (some ·.erase)) : + readScopedExpr? resolve locals (rawArguments.foldl KExpr.mkApp rawHead) = + some (head.appN arguments).erase := by + induction rawArguments generalizing rawHead head arguments with + | nil => + cases arguments <;> simp_all + | cons argument rawArguments ih => + cases arguments with + | nil => simp at argumentReads + | cons model arguments => + simp only [List.map_cons, List.cons.injEq] at argumentReads + apply ih _ argumentReads.2 + simp [headReads, argumentReads.1, AExpr.erase] + +private theorem readScopedExpr?_appSpineView {β : Type u} + (resolve : Address → Option (ConstRef β)) (locals : List FVarId) (source : KExpr .anon) : + readScopedExpr? resolve locals source = + readScopedExpr? resolve locals + ((RecM.appSpineView source).2.foldl KExpr.mkApp (RecM.appSpineView source).1) := by + induction source with + | app fn arg info ihFn ihArg => + simp only [RecM.appSpineView, List.foldl_append, List.foldl_cons, List.foldl_nil, + readScopedExpr?, readScopedExpr?_mkApp] + rw [ihFn] + | _ => rfl + +theorem readScopedExpr?_collectSpine {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {source rawHead : KExpr .anon} {rawArguments : Array (KExpr .anon)} + {head : AExpr β} {arguments : List (AExpr β)} + (spine : source.collectSpine = (rawHead, rawArguments)) + (headReads : readScopedExpr? resolve locals rawHead = some head.erase) + (argumentReads : rawArguments.toList.map (readScopedExpr? resolve locals ·) = + arguments.map (some ·.erase)) : + readScopedExpr? resolve locals source = some (head.appN arguments).erase := by + have view := RecM.appSpineView_collectSpine source + rw [spine] at view + rw [readScopedExpr?_appSpineView, ← view.1, ← view.2] + exact readScopedExpr?_appN headReads argumentReads + +theorem betaPeel_readScopedExpr? {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {start result : KExpr .anon} {consumed : List (KExpr .anon)} + {head : AExpr β} {depth : Nat} + (peeling : RecM.BetaPeel start consumed result) + (reading : readScopedExpr? resolve locals start depth = some head.erase) : + ∃ body, LambdaPeel head consumed.length body ∧ + readScopedExpr? resolve locals result (depth + consumed.length) = some body.erase := by + induction peeling with + | nil => exact ⟨head, .zero _, reading⟩ + | snoc preceding ih => + obtain ⟨current, modelPeel, currentReads⟩ := ih + rw [readScopedExpr?] at currentReads + obtain ⟨domain, domainReads, currentReads⟩ := bind_success currentReads + obtain ⟨body, bodyReads, currentReads⟩ := bind_success currentReads + cases current <;> cases currentReads + exact ⟨_, by simpa using modelPeel.snoc, by simpa [Nat.add_assoc] using bodyReads⟩ + +/-- Rebuilding a suffix needs collision freedom only for its finite chain +of application candidates and the current intern table. -/ +theorem internAppChain_readScopedExpr? {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {rawHead : KExpr .anon} {head : AExpr β} {table : InternTable .anon} + {rawArguments : List (KExpr .anon)} {arguments : List (AExpr β)} + (coherent : table.WF) + (faithful : KExpr.CollisionFree fun term => table.ExprSupport term ∨ + term ∈ cheapBetaChainList rawHead rawArguments) + (headReads : readScopedExpr? resolve locals rawHead = some head.erase) + (argumentReads : rawArguments.map (readScopedExpr? resolve locals ·) = + arguments.map (some ·.erase)) : + readScopedExpr? resolve locals (internAppChain rawHead rawArguments table).1 = + some (head.appN arguments).erase ∧ + (internAppChain rawHead rawArguments table).2.WF := by + induction rawArguments generalizing rawHead head table arguments with + | nil => + cases arguments with + | nil => exact ⟨headReads, coherent⟩ + | cons => simp at argumentReads + | cons argument rawArguments ih => + cases arguments with + | nil => simp at argumentReads + | cons model arguments => + simp only [List.map_cons, List.cons.injEq] at argumentReads + let candidate := KExpr.mkApp rawHead argument + have candidateIn : candidate ∈ cheapBetaChainList rawHead (argument :: rawArguments) := by + cases rawArguments <;> simp [cheapBetaChainList, candidate] + have canonical : (table.internExpr candidate).1 = candidate := by + have exactIntern := table.internExpr_eraseMeta coherent + (KExpr.keyCollisionFree_anon.mpr (faithful.mono fun term member => + member.elim Or.inl fun same => Or.inr (same ▸ candidateIn))) + simpa only [KExpr.eraseMeta_anon] using exactIntern + have tailFaithful : KExpr.CollisionFree fun term => + (table.internExpr candidate).2.ExprSupport term ∨ + term ∈ cheapBetaChainList candidate rawArguments := by + apply faithful.mono + intro term member + rcases member with resident | inTail + · rcases InternTable.ExprSupport.of_internExpr resident with old | same + · exact Or.inl old + · exact Or.inr (same ▸ candidateIn) + · exact Or.inr (List.mem_cons_of_mem _ inTail) + have output := ih (head := head.app model) (coherent.internExpr candidate) tailFaithful + (by simp [candidate, headReads, argumentReads.1, AExpr.erase]) argumentReads.2 + change readScopedExpr? resolve locals + (internAppChain (table.internExpr candidate).1 rawArguments + (table.internExpr candidate).2).1 = _ ∧ + (internAppChain (table.internExpr candidate).1 rawArguments + (table.internExpr candidate).2).2.WF + rw [canonical] + exact output + +theorem finishAppResult_readScopedExpr? {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {rawHead : KExpr .anon} {head : AExpr β} {before : TcState .anon} + {rawArguments : Array (KExpr .anon)} {arguments : List (AExpr β)} {consumed : Nat} + (coherent : before.env.intern.WF) + (faithful : KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ + term ∈ cheapBetaChainList rawHead (rawArguments.extract consumed rawArguments.size).toList) + (headReads : readScopedExpr? resolve locals rawHead = some head.erase) + (argumentReads : (rawArguments.extract consumed rawArguments.size).toList.map + (readScopedExpr? resolve locals ·) = arguments.map (some ·.erase)) + (methods : Methods .anon) : + ∃ result after, + (RecM.finishAppResult rawHead rawArguments consumed).run methods before = .ok result after ∧ + readScopedExpr? resolve locals result = some (head.appN arguments).erase ∧ after.env.intern.WF := by + obtain ⟨reads, preserved⟩ := internAppChain_readScopedExpr? coherent faithful headReads argumentReads + let output := internAppChain rawHead (rawArguments.extract consumed rawArguments.size).toList before.env.intern + refine ⟨output.1, { before with env := { before.env with intern := output.2 } }, ?_, reads, preserved⟩ + rw [RecM.finishAppResult_eq_internAppChain] + rfl + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Infer/CheapBeta.lean b/Ix/Kernel/Verify/Infer/CheapBeta.lean index 0f6dc5d9d..3a27b9473 100644 --- a/Ix/Kernel/Verify/Infer/CheapBeta.lean +++ b/Ix/Kernel/Verify/Infer/CheapBeta.lean @@ -1,4 +1,5 @@ import Ix.Kernel.Verify.Infer.BinderScopes +import Ix.Kernel.Verify.Infer.CheapBetaPlan import Ix.Kernel.Verify.Whnf.Beta.Meaning import Ix.Kernel.Verify.Whnf.Structural.ApplicationCongruence @@ -14,200 +15,6 @@ the operational theorem here cannot silently assume it. namespace Ix.Kernel -namespace RecM.BetaPeel - -/-- Prefix one already-proved peel by the outermost lambda and its first -argument. -/ -theorem prepend - {inner body : KExpr .anon} {consumed : List (KExpr .anon)} - {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} - {ty arg : KExpr .anon} {info : ExprInfo .anon} - (h : BetaPeel inner consumed body) : - BetaPeel (.lam name bi ty inner info) (arg :: consumed) body := by - induction h with - | nil => - simpa using - (BetaPeel.snoc (arg := arg) - (BetaPeel.nil (.lam name bi ty inner info))) - | snoc hprefix ih => - simpa [List.cons_append] using BetaPeel.snoc ih - -/-- `peelLamsN` consumes exactly the corresponding list prefix. -/ -theorem of_peelLamsN (head : KExpr .anon) (args : List (KExpr .anon)) : - let (body, consumed) := peelLamsN args.length head - BetaPeel head (args.take consumed) body ∧ consumed ≤ args.length := by - induction args generalizing head with - | nil => - simp only [List.length_nil, peelLamsN, List.take_zero] - exact ⟨BetaPeel.nil head, Nat.le_refl 0⟩ - | cons arg args ih => - cases head with - | lam name bi ty inner info => - simp only [List.length_cons] - generalize hpeel : peelLamsN args.length inner = peeled - rcases peeled with ⟨body, consumed⟩ - have hrun : - peelLamsN (args.length + 1) (.lam name bi ty inner info) = - (body, consumed + 1) := by - rw [peelLamsN, hpeel] - rw [hrun] - have htail := ih inner - rw [hpeel] at htail - dsimp only at htail - refine ⟨?_, by omega⟩ - simpa only [List.take_succ_cons] using - (htail.1.prepend (name := name) (bi := bi) (ty := ty) - (arg := arg) (info := info)) - | var | fvar | sort | const | app | all | letE | prj | nat | str => - simp only [List.length_cons, peelLamsN, List.take_zero] - exact ⟨BetaPeel.nil _, Nat.zero_le _⟩ - -end RecM.BetaPeel - -namespace WalkerRequest.Bounds - -/-- Recover the simultaneous-substitution budget for the exact prefix -selected by a cheap-beta plan. -/ -theorem cheapBeta_simul - {source head body : KExpr .anon} {args : Array (KExpr .anon)} - {consumed : Nat} - (h : WalkerRequest.Bounds (.cheapBeta source)) - (hspine : source.collectSpine = (head, args)) - (hpeel : peelLamsN args.size head = (body, consumed)) : - WalkerRequest.Bounds - (.simulSubst body (args.extract 0 consumed).reverse 0) := - h.2 hspine hpeel - -end WalkerRequest.Bounds - -private theorem toNat_toUInt64_cheapBeta (n : Nat) : - n.toUInt64.toNat = n % UInt64.size := by - unfold Nat.toUInt64 - rfl - -/-- A successful cheap-beta plan is exactly the simultaneous substitution -of the consumed lambda prefix followed by the untouched application suffix. -This is the arithmetic seam behind the selected-variable fast path: the -production index `consumed - k - 1` is index `k` in the reversed prefix. -/ -theorem cheapBetaPlan?_simul - {source : KExpr .anon} {plan : CheapBetaPlan .anon} - (hplan : cheapBetaPlan? source = some plan) - (hbounds : WalkerRequest.Bounds (.cheapBeta source)) : - ∃ (head body : KExpr .anon) (args : Array (KExpr .anon)) - (consumed : Nat), - source.collectSpine = (head, args) ∧ - peelLamsN args.size head = (body, consumed) ∧ - consumed ≤ args.size ∧ - plan.base = KExpr.simulSubstSpec body - (args.extract 0 consumed).reverse 0 ∧ - plan.trailing = (args.extract consumed args.size).toList := by - cases source with - | app f arg info => - simp only [cheapBetaPlan?] at hplan - generalize hspine : (KExpr.app f arg info).collectSpine = spine at hplan - rcases spine with ⟨head, args⟩ - cases head with - | lam name bi ty inner lamInfo => - generalize hpeel : - peelLamsN args.size (.lam name bi ty inner lamInfo) = peeled - at hplan - rcases peeled with ⟨body, consumed⟩ - have hcount := - RecM.BetaPeel.of_peelLamsN - (.lam name bi ty inner lamInfo) args.toList - rw [show args.toList.length = args.size by simp, hpeel] at hcount - dsimp only at hcount - have hsim := hbounds.2 hspine hpeel - have hprefixSize : (args.extract 0 consumed).size = consumed := by - simp only [Array.size_extract] - omega - by_cases hclosed : body.lbr == 0 - · simp only [hclosed, if_true, Option.some.injEq] at hplan - subst plan - have hlbr : body.lbr ≤ 0 := by - rw [beq_iff_eq.mp hclosed] - exact UInt64.le_iff_toNat_le.mpr (Nat.le_refl 0) - have hsimEq := KExpr.simulSubstSpec_id hsim.1 - (by simpa only [UInt64.toNat_zero, Nat.zero_add, hprefixSize] - using hsim.2.2.2.1) - hlbr - exact ⟨_, _, _, _, rfl, hpeel, hcount.2, - hsimEq.symm, rfl⟩ - · cases body with - | var k varName varInfo => - by_cases hk : k < consumed.toUInt64 - · simp only [hclosed, Bool.false_eq_true, if_false, hk, - if_true, Option.some.injEq] at hplan - subst plan - have hconsumedLt : consumed < UInt64.size := by - have hbodySize := KExpr.size_pos - (.var k varName varInfo : KExpr .anon) - have hbig := hsim.2.2.2.1 - simp only [Array.size_reverse, hprefixSize] at hbig - omega - have hconsumedNat : consumed.toUInt64.toNat = consumed := by - rw [toNat_toUInt64_cheapBeta] - exact Nat.mod_eq_of_lt hconsumedLt - have hkNat : k.toNat < consumed := by - have := UInt64.lt_iff_toNat_lt.mp hk - rwa [hconsumedNat] at this - have hkPrefix : - k.toNat < (args.extract 0 consumed).reverse.size := by - simpa only [Array.size_reverse, hprefixSize] using hkNat - have hselected : - (args.extract 0 consumed).reverse[k.toNat]! = - args[consumed - k.toNat - 1]! := by - rw [getElem!_pos - (args.extract 0 consumed).reverse k.toNat hkPrefix, - Array.getElem_reverse] - have hsourceIndex : consumed - k.toNat - 1 < args.size := - by omega - rw [getElem!_pos args (consumed - k.toNat - 1) - hsourceIndex, - Array.getElem_extract] - congr 1 - omega - have hprefixSize64 : - (args.extract 0 consumed).reverse.size.toUInt64.toNat = - consumed := by - rw [toNat_toUInt64_cheapBeta] - simp only [Array.size_reverse, hprefixSize] - exact Nat.mod_eq_of_lt hconsumedLt - have hkWindow : - (k ≥ (0 : UInt64) && - k < 0 + - (args.extract 0 consumed).reverse.size.toUInt64) = - true := by - apply Bool.and_eq_true_iff.mpr - constructor - · exact decide_eq_true (UInt64.le_iff_toNat_le.mpr - (Nat.zero_le _)) - · exact decide_eq_true (UInt64.lt_iff_toNat_lt.mpr - (by - rw [UInt64.toNat_add, UInt64.toNat_zero, - hprefixSize64, Nat.zero_add, - Nat.mod_eq_of_lt hconsumedLt] - exact hkNat)) - have hselectedConstructed := hsim.2.1 k.toNat (by - simpa only [Array.size_reverse, hprefixSize] using hkNat) - have hsimEq : - KExpr.simulSubstSpec (.var k varName varInfo) - (args.extract 0 consumed).reverse 0 = - args[consumed - k.toNat - 1]! := by - rw [KExpr.simulSubstSpec, if_pos hkWindow, - UInt64.sub_zero, - KExpr.liftSpec_zero hselectedConstructed, hselected] - exact ⟨_, _, _, _, rfl, hpeel, hcount.2, - hsimEq.symm, rfl⟩ - · simp [hclosed, hk] at hplan - | fvar | sort | const | app | lam | all | letE | prj | nat | - str => - simp [hclosed] at hplan - | var | fvar | sort | const | app | all | letE | prj | nat | str => - cases hplan - | var | fvar | sort | const | lam | all | letE | prj | nat | str => - cases hplan - /-- Cheap beta reduction preserves the Theory meaning of a structurally translated source. A successful plan is discharged by WHNF's constructive multi-beta theorem; an absent plan is reflexive. -/ diff --git a/Ix/Kernel/Verify/Infer/CheapBetaPlan.lean b/Ix/Kernel/Verify/Infer/CheapBetaPlan.lean new file mode 100644 index 000000000..6ab0940e9 --- /dev/null +++ b/Ix/Kernel/Verify/Infer/CheapBetaPlan.lean @@ -0,0 +1,203 @@ +import Ix.Kernel.Verify.Support +import Ix.Kernel.Verify.Whnf.Beta.LambdaPeeling + +/-! Pure planning and arithmetic for cheap beta, shared by both +semantic developments without importing either model. -/ + +namespace Ix.Kernel + +namespace RecM.BetaPeel + +/-- Prefix one already-proved peel by the outermost lambda and its first +argument. -/ +theorem prepend + {inner body : KExpr .anon} {consumed : List (KExpr .anon)} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {ty arg : KExpr .anon} {info : ExprInfo .anon} + (h : BetaPeel inner consumed body) : + BetaPeel (.lam name bi ty inner info) (arg :: consumed) body := by + induction h with + | nil => + simpa using + (BetaPeel.snoc (arg := arg) + (BetaPeel.nil (.lam name bi ty inner info))) + | snoc hprefix ih => + simpa [List.cons_append] using BetaPeel.snoc ih + +/-- `peelLamsN` consumes exactly the corresponding list prefix. -/ +theorem of_peelLamsN (head : KExpr .anon) (args : List (KExpr .anon)) : + let (body, consumed) := peelLamsN args.length head + BetaPeel head (args.take consumed) body ∧ consumed ≤ args.length := by + induction args generalizing head with + | nil => + simp only [List.length_nil, peelLamsN, List.take_zero] + exact ⟨BetaPeel.nil head, Nat.le_refl 0⟩ + | cons arg args ih => + cases head with + | lam name bi ty inner info => + simp only [List.length_cons] + generalize hpeel : peelLamsN args.length inner = peeled + rcases peeled with ⟨body, consumed⟩ + have hrun : + peelLamsN (args.length + 1) (.lam name bi ty inner info) = + (body, consumed + 1) := by + rw [peelLamsN, hpeel] + rw [hrun] + have htail := ih inner + rw [hpeel] at htail + dsimp only at htail + refine ⟨?_, by omega⟩ + simpa only [List.take_succ_cons] using + (htail.1.prepend (name := name) (bi := bi) (ty := ty) + (arg := arg) (info := info)) + | var | fvar | sort | const | app | all | letE | prj | nat | str => + simp only [List.length_cons, peelLamsN, List.take_zero] + exact ⟨BetaPeel.nil _, Nat.zero_le _⟩ + +end RecM.BetaPeel + +namespace WalkerRequest.Bounds + +/-- Recover the simultaneous-substitution budget for the exact prefix +selected by a cheap-beta plan. -/ +theorem cheapBeta_simul + {source head body : KExpr .anon} {args : Array (KExpr .anon)} + {consumed : Nat} + (h : WalkerRequest.Bounds (.cheapBeta source)) + (hspine : source.collectSpine = (head, args)) + (hpeel : peelLamsN args.size head = (body, consumed)) : + WalkerRequest.Bounds + (.simulSubst body (args.extract 0 consumed).reverse 0) := + h.2 hspine hpeel + +end WalkerRequest.Bounds + +private theorem toNat_toUInt64_cheapBeta (n : Nat) : + n.toUInt64.toNat = n % UInt64.size := by + unfold Nat.toUInt64 + rfl + +/-- A successful cheap-beta plan is exactly the simultaneous substitution +of the consumed lambda prefix followed by the untouched application suffix. +This is the arithmetic seam behind the selected-variable fast path: the +production index `consumed - k - 1` is index `k` in the reversed prefix. -/ +theorem cheapBetaPlan?_simul + {source : KExpr .anon} {plan : CheapBetaPlan .anon} + (hplan : cheapBetaPlan? source = some plan) + (hbounds : WalkerRequest.Bounds (.cheapBeta source)) : + ∃ (head body : KExpr .anon) (args : Array (KExpr .anon)) + (consumed : Nat), + source.collectSpine = (head, args) ∧ + peelLamsN args.size head = (body, consumed) ∧ + consumed ≤ args.size ∧ + plan.base = KExpr.simulSubstSpec body + (args.extract 0 consumed).reverse 0 ∧ + plan.trailing = (args.extract consumed args.size).toList := by + cases source with + | app f arg info => + simp only [cheapBetaPlan?] at hplan + generalize hspine : (KExpr.app f arg info).collectSpine = spine at hplan + rcases spine with ⟨head, args⟩ + cases head with + | lam name bi ty inner lamInfo => + generalize hpeel : + peelLamsN args.size (.lam name bi ty inner lamInfo) = peeled + at hplan + rcases peeled with ⟨body, consumed⟩ + have hcount := + RecM.BetaPeel.of_peelLamsN + (.lam name bi ty inner lamInfo) args.toList + rw [show args.toList.length = args.size by simp, hpeel] at hcount + dsimp only at hcount + have hsim := hbounds.2 hspine hpeel + have hprefixSize : (args.extract 0 consumed).size = consumed := by + simp only [Array.size_extract] + omega + by_cases hclosed : body.lbr == 0 + · simp only [hclosed, if_true, Option.some.injEq] at hplan + subst plan + have hlbr : body.lbr ≤ 0 := by + rw [beq_iff_eq.mp hclosed] + exact UInt64.le_iff_toNat_le.mpr (Nat.le_refl 0) + have hsimEq := KExpr.simulSubstSpec_id hsim.1 + (by simpa only [UInt64.toNat_zero, Nat.zero_add, hprefixSize] + using hsim.2.2.2.1) + hlbr + exact ⟨_, _, _, _, rfl, hpeel, hcount.2, + hsimEq.symm, rfl⟩ + · cases body with + | var k varName varInfo => + by_cases hk : k < consumed.toUInt64 + · simp only [hclosed, Bool.false_eq_true, if_false, hk, + if_true, Option.some.injEq] at hplan + subst plan + have hconsumedLt : consumed < UInt64.size := by + have hbodySize := KExpr.size_pos + (.var k varName varInfo : KExpr .anon) + have hbig := hsim.2.2.2.1 + simp only [Array.size_reverse, hprefixSize] at hbig + omega + have hconsumedNat : consumed.toUInt64.toNat = consumed := by + rw [toNat_toUInt64_cheapBeta] + exact Nat.mod_eq_of_lt hconsumedLt + have hkNat : k.toNat < consumed := by + have := UInt64.lt_iff_toNat_lt.mp hk + rwa [hconsumedNat] at this + have hkPrefix : + k.toNat < (args.extract 0 consumed).reverse.size := by + simpa only [Array.size_reverse, hprefixSize] using hkNat + have hselected : + (args.extract 0 consumed).reverse[k.toNat]! = + args[consumed - k.toNat - 1]! := by + rw [getElem!_pos + (args.extract 0 consumed).reverse k.toNat hkPrefix, + Array.getElem_reverse] + have hsourceIndex : consumed - k.toNat - 1 < args.size := + by omega + rw [getElem!_pos args (consumed - k.toNat - 1) + hsourceIndex, + Array.getElem_extract] + congr 1 + omega + have hprefixSize64 : + (args.extract 0 consumed).reverse.size.toUInt64.toNat = + consumed := by + rw [toNat_toUInt64_cheapBeta] + simp only [Array.size_reverse, hprefixSize] + exact Nat.mod_eq_of_lt hconsumedLt + have hkWindow : + (k ≥ (0 : UInt64) && + k < 0 + + (args.extract 0 consumed).reverse.size.toUInt64) = + true := by + apply Bool.and_eq_true_iff.mpr + constructor + · exact decide_eq_true (UInt64.le_iff_toNat_le.mpr + (Nat.zero_le _)) + · exact decide_eq_true (UInt64.lt_iff_toNat_lt.mpr + (by + rw [UInt64.toNat_add, UInt64.toNat_zero, + hprefixSize64, Nat.zero_add, + Nat.mod_eq_of_lt hconsumedLt] + exact hkNat)) + have hselectedConstructed := hsim.2.1 k.toNat (by + simpa only [Array.size_reverse, hprefixSize] using hkNat) + have hsimEq : + KExpr.simulSubstSpec (.var k varName varInfo) + (args.extract 0 consumed).reverse 0 = + args[consumed - k.toNat - 1]! := by + rw [KExpr.simulSubstSpec, if_pos hkWindow, + UInt64.sub_zero, + KExpr.liftSpec_zero hselectedConstructed, hselected] + exact ⟨_, _, _, _, rfl, hpeel, hcount.2, + hsimEq.symm, rfl⟩ + · simp [hclosed, hk] at hplan + | fvar | sort | const | app | lam | all | letE | prj | nat | + str => + simp [hclosed] at hplan + | var | fvar | sort | const | app | all | letE | prj | nat | str => + cases hplan + | var | fvar | sort | const | lam | all | letE | prj | nat | str => + cases hplan + +end Ix.Kernel diff --git a/Ix/Kernel/Verify/Whnf.lean b/Ix/Kernel/Verify/Whnf.lean index 4efa79637..3f4e0951f 100644 --- a/Ix/Kernel/Verify/Whnf.lean +++ b/Ix/Kernel/Verify/Whnf.lean @@ -5683,34 +5683,6 @@ inductive WhnfCoreNonLambda : KExpr .anon → Prop /-! ### Application-spine rebuilding -/ -/-- A structurally recursive list view of an application spine. The proof -below connects it to production's accumulator/reverse implementation, while -this view makes translation induction direct. -/ -def appSpineView (e : KExpr m) : KExpr m × List (KExpr m) := - match e with - | .app f a _ => - let (head, args) := appSpineView f - (head, args ++ [a]) - | e => (e, []) -termination_by structural e - -/-- Production's accumulator contains the reversed pending suffix; the -structural view contributes the already ordered prefix. -/ -theorem appSpineView_go (e : KExpr m) (acc : Array (KExpr m)) : - let (head, args) := appSpineView e - (KExpr.collectSpine.go e acc).1 = head ∧ - (KExpr.collectSpine.go e acc).2.toList = - args ++ acc.toList.reverse := by - induction e generalizing acc <;> - simp_all [appSpineView, KExpr.collectSpine.go, - List.reverse_append, List.append_assoc] - -/-- The structural view is extensionally the actual production spine. -/ -theorem appSpineView_collectSpine (e : KExpr m) : - let (head, args) := appSpineView e - e.collectSpine.1 = head ∧ e.collectSpine.2.toList = args := by - simpa [KExpr.collectSpine] using appSpineView_go e #[] - /-- Translation-indexed spine view. Each extension retains the exact function and argument typing derivations needed for semantic application congruence. -/ @@ -5829,18 +5801,6 @@ def finishAppResultSpec (result : KExpr .anon) (args : Array (KExpr .anon)) (consumed : Nat) : KExpr .anon := KExpr.mkAppN result (args.extract consumed args.size) -/-- The imperative `for` loop in `finishAppResult` is exactly a monadic -left fold over the requested suffix. This equation fixes both argument order -and the consumed-prefix boundary without changing the production helper. -/ -theorem finishAppResult_eq_foldlM (result : KExpr m) - (args : Array (KExpr m)) (consumed : Nat) : - finishAppResult result args consumed = - (args.extract consumed args.size).foldlM (m := RecM m) - (fun result arg => liftM (TcM.intern (KExpr.mkApp result arg))) - result := by - unfold finishAppResult - simp [Array.forIn_yield_eq_foldlM] - /-- Production's application-suffix rebuild is operationally total. This fact is deliberately weaker than semantic correctness: without a finite request certificate, an intern collision may change the returned syntax and @@ -6085,59 +6045,6 @@ theorem whnfCoreWithFlagsStep_letE rw [hwalk] rfl -/-- Exact production step for general multi-argument beta. Unlike the -single-argument convenience theorem, this exposes the lambda-peeling result, -the simultaneous-substitution execution, and rebuilding of only the -unconsumed argument suffix. -/ -theorem whnfCoreWithFlagsStep_betaMany - {methods : Methods .anon} {s s₁ s₂ s₃ : TcState .anon} - {f arg head : KExpr .anon} {appInfo : ExprInfo .anon} - {args : Array (KExpr .anon)} - {nm : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} - {ty body body₀ : KExpr .anon} {lamInfo : ExprInfo .anon} - {consumed : Array (KExpr .anon)} {substituted result : KExpr .anon} - {flags : WhnfFlags} - (hspine : (.app f arg appInfo : KExpr .anon).collectSpine = (head, args)) - (hhead : methods.whnfCoreFlags head flags s = - .ok (.lam nm bi ty body lamInfo) s₁) - (hconsume : consumeBetaLams (.lam nm bi ty body lamInfo) args = - (body₀, consumed)) - (hnonempty : (!consumed.isEmpty) = true) - (hsubst : TcM.runIntern (simulSubst body₀ consumed.reverse 0) s₁ = - .ok substituted s₂) - (hfinish : (finishAppResult substituted args consumed.size).run methods s₂ = - .ok result s₃) : - (whnfCoreWithFlagsStep (.app f arg appInfo) flags).run methods s = - .ok (.next result) s₃ := by - unfold whnfCoreWithFlagsStep - rw [ReaderT.run_bind] - rw [hspine] - change EStateM.bind (methods.whnfCoreFlags head flags) _ s = _ - unfold EStateM.bind - rw [hhead] - simp only - rw [hconsume] - simp only - rw [hnonempty] - simp only [↓reduceIte] - change ReaderT.run - ((liftM (TcM.runIntern (simulSubst body₀ consumed.reverse 0)) >>= fun r => do - pure PUnit.unit - let r ← finishAppResult r args consumed.size - pure (BoundedStep.next r)) : - RecM .anon (BoundedStep (KExpr .anon) (KExpr .anon))) methods s₁ = _ - rw [ReaderT.run_bind, ReaderT.run_monadLift] - change EStateM.bind - (TcM.runIntern (simulSubst body₀ consumed.reverse 0)) _ s₁ = _ - unfold EStateM.bind - rw [hsubst] - change EStateM.bind - (ReaderT.run (finishAppResult substituted args consumed.size) methods) _ - s₂ = _ - unfold EStateM.bind - rw [hfinish] - rfl - /-- Exact production step for a successful projection reduction. Both the cheap and full value-WHNF policies are represented by the same explicit callback equation, followed by the actual `tryProjReduce` execution. -/ diff --git a/Ix/Kernel/Verify/Whnf/Beta/ConsumptionBoundary.lean b/Ix/Kernel/Verify/Whnf/Beta/ConsumptionBoundary.lean index 0e3cfd9cf..f6040d756 100644 --- a/Ix/Kernel/Verify/Whnf/Beta/ConsumptionBoundary.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/ConsumptionBoundary.lean @@ -1,4 +1,5 @@ import Ix.Kernel.Verify.Whnf.Beta.LambdaPeeling +import Ix.Kernel.Verify.Whnf.Structural.StepAssembly /-! # Typed splitting at the multi-beta consumption boundary diff --git a/Ix/Kernel/Verify/Whnf/Beta/DirectStep.lean b/Ix/Kernel/Verify/Whnf/Beta/DirectStep.lean index 05722b7ec..00c7ae4b4 100644 --- a/Ix/Kernel/Verify/Whnf/Beta/DirectStep.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/DirectStep.lean @@ -39,4 +39,113 @@ theorem whnfCoreWithFlagsStep_betaOne rw [hwalk] rfl +/-- A structurally recursive list view of an application spine. The proof +below connects it to production's accumulator/reverse implementation, while +this view makes translation induction direct. -/ +def appSpineView (e : KExpr m) : KExpr m × List (KExpr m) := + match e with + | .app f a _ => + let (head, args) := appSpineView f + (head, args ++ [a]) + | e => (e, []) +termination_by structural e + +/-- Production's accumulator contains the reversed pending suffix; the +structural view contributes the already ordered prefix. -/ +theorem appSpineView_go (e : KExpr m) (acc : Array (KExpr m)) : + let (head, args) := appSpineView e + (KExpr.collectSpine.go e acc).1 = head ∧ + (KExpr.collectSpine.go e acc).2.toList = + args ++ acc.toList.reverse := by + induction e generalizing acc <;> + simp_all [appSpineView, KExpr.collectSpine.go, + List.reverse_append, List.append_assoc] + +/-- The structural view is extensionally the actual production spine. -/ +theorem appSpineView_collectSpine (e : KExpr m) : + let (head, args) := appSpineView e + e.collectSpine.1 = head ∧ e.collectSpine.2.toList = args := by + simpa [KExpr.collectSpine] using appSpineView_go e #[] + +/-- The imperative `for` loop in `finishAppResult` is exactly a monadic +left fold over the requested suffix. This equation fixes both argument order +and the consumed-prefix boundary without changing the production helper. -/ +theorem finishAppResult_eq_foldlM (result : KExpr m) + (args : Array (KExpr m)) (consumed : Nat) : + finishAppResult result args consumed = + (args.extract consumed args.size).foldlM (m := RecM m) + (fun result arg => liftM (TcM.intern (KExpr.mkApp result arg))) + result := by + unfold finishAppResult + simp [Array.forIn_yield_eq_foldlM] + +/-- The WHNF suffix loop and cheap beta use the same interned application +chain, including the exact final intern-table state. -/ +theorem finishAppResult_eq_internAppChain (result : KExpr m) + (args : Array (KExpr m)) (consumed : Nat) (methods : Methods m) (before : TcState m) : + (finishAppResult result args consumed).run methods before = + TcM.runIntern (internAppChain result (args.extract consumed args.size).toList) before := by + rw [finishAppResult_eq_foldlM, ← Array.foldlM_toList] + generalize (args.extract consumed args.size).toList = remaining + induction remaining generalizing result before with + | nil => rfl + | cons argument remaining ih => + rw [List.foldlM_cons, ReaderT.run_bind, ReaderT.run_monadLift] + change EStateM.bind (TcM.intern (KExpr.mkApp result argument)) _ before = _ + unfold EStateM.bind TcM.intern TcM.runIntern + exact ih _ _ + +/-- Exact production step for general multi-argument beta. Unlike the +single-argument convenience theorem, this exposes the lambda-peeling result, +the simultaneous-substitution execution, and rebuilding of only the +unconsumed argument suffix. -/ +theorem whnfCoreWithFlagsStep_betaMany + {methods : Methods .anon} {s s₁ s₂ s₃ : TcState .anon} + {f arg head : KExpr .anon} {appInfo : ExprInfo .anon} + {args : Array (KExpr .anon)} + {nm : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {ty body body₀ : KExpr .anon} {lamInfo : ExprInfo .anon} + {consumed : Array (KExpr .anon)} {substituted result : KExpr .anon} + {flags : WhnfFlags} + (hspine : (.app f arg appInfo : KExpr .anon).collectSpine = (head, args)) + (hhead : methods.whnfCoreFlags head flags s = + .ok (.lam nm bi ty body lamInfo) s₁) + (hconsume : consumeBetaLams (.lam nm bi ty body lamInfo) args = + (body₀, consumed)) + (hnonempty : (!consumed.isEmpty) = true) + (hsubst : TcM.runIntern (simulSubst body₀ consumed.reverse 0) s₁ = + .ok substituted s₂) + (hfinish : (finishAppResult substituted args consumed.size).run methods s₂ = + .ok result s₃) : + (whnfCoreWithFlagsStep (.app f arg appInfo) flags).run methods s = + .ok (.next result) s₃ := by + unfold whnfCoreWithFlagsStep + rw [ReaderT.run_bind] + rw [hspine] + change EStateM.bind (methods.whnfCoreFlags head flags) _ s = _ + unfold EStateM.bind + rw [hhead] + simp only + rw [hconsume] + simp only + rw [hnonempty] + simp only [↓reduceIte] + change ReaderT.run + ((liftM (TcM.runIntern (simulSubst body₀ consumed.reverse 0)) >>= fun r => do + pure PUnit.unit + let r ← finishAppResult r args consumed.size + pure (BoundedStep.next r)) : + RecM .anon (BoundedStep (KExpr .anon) (KExpr .anon))) methods s₁ = _ + rw [ReaderT.run_bind, ReaderT.run_monadLift] + change EStateM.bind + (TcM.runIntern (simulSubst body₀ consumed.reverse 0)) _ s₁ = _ + unfold EStateM.bind + rw [hsubst] + change EStateM.bind + (ReaderT.run (finishAppResult substituted args consumed.size) methods) _ + s₂ = _ + unfold EStateM.bind + rw [hfinish] + rfl + end Ix.Kernel.RecM diff --git a/Ix/Kernel/Verify/Whnf/Beta/LambdaInstantiation.lean b/Ix/Kernel/Verify/Whnf/Beta/LambdaInstantiation.lean index 754e5b526..2720a8cac 100644 --- a/Ix/Kernel/Verify/Whnf/Beta/LambdaInstantiation.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/LambdaInstantiation.lean @@ -1,4 +1,5 @@ import Ix.Kernel.Verify.Whnf.Beta.SimultaneousSubstitution +import Ix.Kernel.Verify.Whnf.Beta.PeelTrace /-! # Walker-tight lambda instantiation diff --git a/Ix/Kernel/Verify/Whnf/Beta/LambdaPeeling.lean b/Ix/Kernel/Verify/Whnf/Beta/LambdaPeeling.lean index 0d70cf0f8..8a5939f89 100644 --- a/Ix/Kernel/Verify/Whnf/Beta/LambdaPeeling.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/LambdaPeeling.lean @@ -1,4 +1,4 @@ -import Ix.Kernel.Verify.Whnf.Structural.StepAssembly +import Ix.Kernel.Whnf /-! # Certified lambda peeling for general beta @@ -37,10 +37,10 @@ theorem fuel intro fuel induction fuel generalizing current consumed with | zero => - simpa only [consumeBetaLamsFuel_zero] using + simpa only [consumeBetaLamsFuel] using And.intro hpeel (And.intro hprefix hsize) | succ fuel ih => - rw [consumeBetaLamsFuel_succ] + unfold consumeBetaLamsFuel by_cases hdone : consumed.size >= args.size · simp only [hdone, if_true] exact ⟨hpeel, hprefix, hsize⟩ @@ -81,7 +81,7 @@ theorem of_consume (consumed := Array.mkEmpty args.size) (.nil start) (by simp) (by simp) args.size dsimp only at h - rw [consumeBetaLams_equation] at hconsume + rw [consumeBetaLams] at hconsume rw [hconsume] at h exact h @@ -96,7 +96,7 @@ theorem remaining_eq_drop rw [Array.toList_extract] simp only [List.extract_eq_take_drop] have hargsLength : args.toList.length = args.size := by - simpa using congrArg Array.size (Array.toArray_toList (xs := args)) + simp have hdropLength : (args.toList.drop consumed.size).length = args.size - consumed.size := by diff --git a/Ix/Kernel/Verify/Whnf/Beta/SimultaneousSubstitution.lean b/Ix/Kernel/Verify/Whnf/Beta/SimultaneousSubstitution.lean index ee51c3db5..23975ed37 100644 --- a/Ix/Kernel/Verify/Whnf/Beta/SimultaneousSubstitution.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/SimultaneousSubstitution.lean @@ -29,9 +29,7 @@ private theorem getElemBang_singleton_append_succ_bw (#[a] ++ xs)[j + 1]! = xs[j]! := by rw [getElem!_pos (#[a] ++ xs) (j + 1) (by simp; omega), getElem!_pos xs j hj] - simpa using - (Array.getElem_append_right (xs := #[a]) (ys := xs) (i := j + 1) - (by simp)) + simp private theorem simulSubstSpec_mkApp_bw (f a : KExpr .anon) (md) (xs : Array (KExpr .anon)) (d : UInt64) : diff --git a/Ix/Kernel/Verify/Whnf/Beta/SingletonSubstitution.lean b/Ix/Kernel/Verify/Whnf/Beta/SingletonSubstitution.lean index 3129e2d37..38386d268 100644 --- a/Ix/Kernel/Verify/Whnf/Beta/SingletonSubstitution.lean +++ b/Ix/Kernel/Verify/Whnf/Beta/SingletonSubstitution.lean @@ -1,4 +1,4 @@ -import Ix.Kernel.Verify.Whnf.Beta.PeelTrace +import Ix.Kernel.Verify.Support /-! # Singleton simultaneous substitution diff --git a/Ix/Theory/Model/BetaSpine.lean b/Ix/Theory/Model/BetaSpine.lean new file mode 100644 index 000000000..792f113d9 --- /dev/null +++ b/Ix/Theory/Model/BetaSpine.lean @@ -0,0 +1,179 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Substitution +import Ix.Theory.Model.BetaSubstitution + +/-! Typed beta reduction along a finite application spine. The lambda +prefix records syntactic domains, while the argument spine records the +typing established at each dependent application. These are proof helpers +for source inference, not additional semantic premises at admission. -/ + +namespace Ix.Theory.Model + +open Certified + +universe u v + +namespace AExpr + +@[simp] theorem appN_nil (head : AExpr β) : head.appN [] = head := rfl + +@[simp] theorem appN_cons (head argument : AExpr β) (arguments : List (AExpr β)) : + head.appN (argument :: arguments) = (head.app argument).appN arguments := rfl + +def lambdaDepth : AExpr β → Nat + | .lam _ _ body => body.lambdaDepth + 1 + | _ => 0 + +/-- Reduce at most the specified number of original leading lambdas, +then retain the untouched argument suffix. -/ +def betaPrefix : Nat → AExpr β → List (AExpr β) → AExpr β + | 0, head, arguments => head.appN arguments + | count + 1, .lam _ _ body, argument :: arguments => + betaPrefix count (body.inst argument) arguments + | _ + 1, head, arguments => head.appN arguments + +end AExpr + +/-- Each leading lambda agrees with the corresponding inferred Pi. +The count limits this claim to the original syntactic prefix. -/ +inductive LambdaPrefix {β : Type u} : AExpr β → AExpr β → Nat → Prop + | zero (term type : AExpr β) : LambdaPrefix term type 0 + | lam {condition : PropWhen} {domain body codomain : AExpr β} {count : Nat} + (inner : LambdaPrefix body codomain count) : + LambdaPrefix (.lam condition domain body) (.forallE condition domain codomain) (count + 1) + +/-- Substitution preserves every domain in the original lambda prefix. +No claim is made about new lambdas exposed beyond that prefix. -/ +theorem LambdaPrefix.inst {β : Type u} {term type : AExpr β} {count : Nat} + (leading : LambdaPrefix term type count) (argument : AExpr β) (cutoff : Nat) : + LambdaPrefix (term.inst argument cutoff) (type.inst argument cutoff) count := by + induction leading generalizing cutoff with + | zero => exact .zero _ _ + | lam inner ih => exact .lam (ih (cutoff + 1)) + +theorem LambdaPrefix.truncate {β : Type u} {term type : AExpr β} {total count : Nat} + (leading : LambdaPrefix term type total) (enough : count ≤ total) : + LambdaPrefix term type count := by + induction leading generalizing count with + | zero => + have : count = 0 := by omega + subst count + exact .zero _ _ + | lam inner ih => + cases count with + | zero => exact .zero _ _ + | succ count => exact .lam (ih (by omega)) + +/-- The original body exposed by removing a syntactic lambda prefix. -/ +inductive LambdaPeel {β : Type u} : AExpr β → Nat → AExpr β → Prop + | zero (term : AExpr β) : LambdaPeel term 0 term + | lam {condition : PropWhen} {domain head body : AExpr β} {count : Nat} + (inner : LambdaPeel head count body) : + LambdaPeel (.lam condition domain head) (count + 1) body + +theorem LambdaPeel.length_bound {β : Type u} {head body : AExpr β} {count : Nat} + (peeling : LambdaPeel head count body) : count ≤ head.lambdaDepth := by + induction peeling with + | zero => exact Nat.zero_le _ + | lam inner ih => exact Nat.add_le_add_right ih 1 + +theorem LambdaPeel.inst {β : Type u} {head body : AExpr β} {count : Nat} + (peeling : LambdaPeel head count body) (argument : AExpr β) (cutoff : Nat) : + LambdaPeel (head.inst argument cutoff) count (body.inst argument (cutoff + count)) := by + induction peeling generalizing cutoff with + | zero => exact .zero _ + | lam inner ih => + simpa only [AExpr.inst, Nat.add_assoc, Nat.add_comm, Nat.add_left_comm] using + LambdaPeel.lam (condition := _) (domain := _) (ih (cutoff + 1)) + +theorem LambdaPeel.snoc {β : Type u} {head domain body : AExpr β} + {condition : PropWhen} {count : Nat} + (peeling : LambdaPeel head count (.lam condition domain body)) : + LambdaPeel head (count + 1) body := by + induction count generalizing head with + | zero => cases peeling; exact .lam (.zero _) + | succ count ih => + cases peeling with + | lam inner => exact .lam (ih inner) + +/-- Sequential beta steps produce exactly simultaneous substitution of +the consumed prefix, followed by its untouched application suffix. -/ +theorem LambdaPeel.betaPrefix {β : Type u} {head body : AExpr β} + {arguments : List (AExpr β)} (peeling : LambdaPeel head arguments.length body) + (trailing : List (AExpr β)) : + AExpr.betaPrefix arguments.length head (arguments ++ trailing) = + (body.instRev arguments).appN trailing := by + induction arguments generalizing head body with + | nil => cases peeling; rfl + | cons argument arguments ih => + cases peeling with + | lam inner => + have result := ih (inner.inst argument 0) + simpa only [List.length_cons, List.cons_append, AExpr.betaPrefix, AExpr.instRev, + Nat.zero_add] using result + +/-- Types of arguments along the original application's dependent Pi +spine. Production inference will derive this helper from its actual calls. -/ +inductive ArgumentSpine {β : Type u} (entries : Environment β) (context : Context β) : + AExpr β → List (AExpr β) → AExpr β → Prop + | nil (type : AExpr β) : ArgumentSpine entries context type [] type + | cons {condition : PropWhen} {domain codomain argument result : AExpr β} + {arguments : List (AExpr β)} + (typed : TypingClaim.{u,v} entries context argument domain) + (tail : ArgumentSpine entries context (codomain.inst argument) arguments result) : + ArgumentSpine entries context (.forallE condition domain codomain) (argument :: arguments) result + +theorem ArgumentSpine.append {β : Type u} {entries : Environment β} {context : Context β} + {start middle finish : AExpr β} {left right : List (AExpr β)} + (first : ArgumentSpine.{u,v} entries context start left middle) + (second : ArgumentSpine.{u,v} entries context middle right finish) : + ArgumentSpine.{u,v} entries context start (left ++ right) finish := by + induction first with + | nil => exact second + | cons typed tail ih => exact .cons typed (ih second) + +theorem ArgumentSpine.typing {β : Type u} {entries : Environment β} {context : Context β} + {head type result : AExpr β} {arguments : List (AExpr β)} + (spine : ArgumentSpine.{u,v} entries context type arguments result) + (typed : TypingClaim.{u,v} entries context head type) : + TypingClaim.{u,v} entries context (head.appN arguments) result := by + induction spine generalizing head with + | nil => exact typed + | cons argumentTyped tail ih => exact ih (typed.app argumentTyped) + +theorem ConversionClaim.appN {β : Type u} {entries : Environment β} {context : Context β} + {left right : AExpr β} (same : ConversionClaim.{u,v} entries context left right) + (arguments : List (AExpr β)) : + ConversionClaim.{u,v} entries context (left.appN arguments) (right.appN arguments) := by + induction arguments generalizing left right with + | nil => exact same + | cons argument arguments ih => exact ih (same.app (.refl argument)) + +/-- Every consumed lambda retains its actual domain after substitution. +The untouched suffix is handled by ordinary application congruence. -/ +theorem LambdaPrefix.beta_sound {β : Type u} {entries : Environment β} {context : Context β} + {head type result : AExpr β} {arguments : List (AExpr β)} {count : Nat} + (leading : LambdaPrefix head type count) + (typed : TypingClaim.{u,v} entries context head type) + (spine : ArgumentSpine.{u,v} entries context type arguments result) : + ConversionClaim.{u,v} entries context (head.appN arguments) + (AExpr.betaPrefix count head arguments) ∧ + TypingClaim.{u,v} entries context (AExpr.betaPrefix count head arguments) result := by + induction count generalizing head type arguments with + | zero => exact ⟨.refl _, spine.typing typed⟩ + | succ count ih => + cases leading with + | lam inner => + cases spine with + | nil => exact ⟨.refl _, typed⟩ + | cons argumentTyped tail => + obtain ⟨conversion, resultTyped⟩ := ih (inner.inst _ 0) + (TypingClaim.betaResult typed argumentTyped) tail + exact ⟨((ConversionClaim.beta typed argumentTyped).appN _).trans conversion, + resultTyped⟩ + +end Ix.Theory.Model diff --git a/Ix/Theory/Model/BetaSubstitution.lean b/Ix/Theory/Model/BetaSubstitution.lean new file mode 100644 index 000000000..43e6eb4bd --- /dev/null +++ b/Ix/Theory/Model/BetaSubstitution.lean @@ -0,0 +1,157 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Theory.Model.Instantiation + +/-! Structural laws for substituting an outermost-first argument list. +The natural-number syntax laws are independent of kernel index bounds. -/ + +namespace Ix.Theory.Model.AExpr + +universe u + +theorem inst_liftN_top (term argument : AExpr β) (count cutoff : Nat) : + (term.liftN (count + 1) cutoff).inst argument (count + cutoff) = term.liftN count cutoff := by + induction term generalizing cutoff with + | bvar index => + by_cases below : index < cutoff + · simp [liftN, liftVar, inst, instVar, below, show index < count + cutoff by omega] + · simp [liftN, liftVar, inst, instVar, below, + show ¬ count + 1 + index < count + cutoff by omega, + show count + 1 + index ≠ count + cutoff by omega] + | lam condition domain body ihDomain ihBody | forallE condition domain body ihDomain ihBody => + simp only [liftN, inst, ihDomain, show count + cutoff + 1 = count + (cutoff + 1) by omega, + ihBody] + | _ => simp_all [liftN, inst] + +/-- Simultaneous substitution above a fixed number of retained binders. -/ +def instRevAt : AExpr β → List (AExpr β) → Nat → AExpr β + | term, [], _ => term + | term, argument :: arguments, cutoff => + instRevAt (term.inst argument (cutoff + arguments.length)) arguments cutoff + +theorem instRevAt_zero (term : AExpr β) (arguments : List (AExpr β)) : + term.instRevAt arguments 0 = term.instRev arguments := by + induction arguments generalizing term with + | nil => rfl + | cons argument arguments ih => simpa only [instRevAt, instRev, Nat.zero_add] using ih _ + +@[simp] theorem erase_instRevAt (term : AExpr β) (arguments : List (AExpr β)) (cutoff : Nat) : + (term.instRevAt arguments cutoff).erase = term.erase.instRevAt (arguments.map erase) cutoff := by + induction arguments generalizing term with + | nil => rfl + | cons argument arguments ih => simp only [instRevAt, VExpr.instRevAt, List.map_cons, + List.length_map, erase_inst, ih] + +@[simp] theorem instRevAt_sort (level : VLevel) (arguments : List (AExpr β)) (cutoff : Nat) : + (sort level).instRevAt arguments cutoff = sort level := by + induction arguments with + | nil => rfl + | cons argument arguments ih => simpa only [instRevAt, inst] using ih + +@[simp] theorem instRevAt_const (ref : ConstRef β) (levels : List VLevel) + (arguments : List (AExpr β)) (cutoff : Nat) : + (const ref levels).instRevAt arguments cutoff = const ref levels := by + induction arguments with + | nil => rfl + | cons argument arguments ih => simpa only [instRevAt, inst] using ih + +@[simp] theorem instRevAt_natLit (value : Nat) (arguments : List (AExpr β)) (cutoff : Nat) : + (natLit value).instRevAt arguments cutoff = natLit value := by + induction arguments with + | nil => rfl + | cons argument arguments ih => simpa only [instRevAt, inst] using ih + +theorem instRevAt_liftN (term : AExpr β) (arguments : List (AExpr β)) (cutoff : Nat) : + (term.liftN (arguments.length + cutoff)).instRevAt arguments cutoff = term.liftN cutoff := by + induction arguments with + | nil => simp [instRevAt] + | cons argument arguments ih => + simp only [List.length_cons, instRevAt] + rw [show arguments.length + 1 + cutoff = (arguments.length + cutoff) + 1 by omega, + show cutoff + arguments.length = (arguments.length + cutoff) + 0 by omega, + inst_liftN_top] + exact ih + +theorem instRevAt_bvar_below (arguments : List (AExpr β)) (index cutoff : Nat) + (below : index < cutoff) : + (AExpr.bvar index).instRevAt arguments cutoff = .bvar index := by + induction arguments with + | nil => rfl + | cons argument arguments ih => + simpa only [instRevAt, inst, instVar, if_pos (show index < cutoff + arguments.length by omega)] using ih + +theorem instRevAt_bvar_above (arguments : List (AExpr β)) (index cutoff : Nat) + (above : cutoff + arguments.length ≤ index) : + (AExpr.bvar index).instRevAt arguments cutoff = .bvar (index - arguments.length) := by + induction arguments generalizing index with + | nil => rfl + | cons argument arguments ih => + simp only [List.length_cons] at above + simp only [instRevAt, inst, instVar, + if_neg (show ¬ index < cutoff + arguments.length by omega), + if_neg (show index ≠ cutoff + arguments.length by omega)] + rw [ih (index - 1) (by omega)] + congr 1 + simp only [List.length_cons] + omega + +theorem instRevAt_bvar_selected (arguments : List (AExpr β)) (index cutoff : Nat) + (inside : index < arguments.length) : + (AExpr.bvar (cutoff + index)).instRevAt arguments cutoff = + (arguments[arguments.length - index - 1]'(by omega)).liftN cutoff := by + induction arguments generalizing index with + | nil => simp at inside + | cons argument arguments ih => + simp only [List.length_cons] at inside + by_cases outermost : index = arguments.length + · subst index + simp only [instRevAt, inst, instVar, Nat.lt_irrefl, if_false, if_true] + rw [show cutoff + arguments.length = arguments.length + cutoff by omega, instRevAt_liftN] + simp + · have inner : index < arguments.length := by omega + simp only [instRevAt, inst, instVar, + if_pos (show cutoff + index < cutoff + arguments.length by omega)] + rw [ih index inner] + simp only [List.length_cons, + show arguments.length + 1 - index - 1 = (arguments.length - index - 1) + 1 by omega, + List.getElem_cons_succ] + +@[simp] theorem instRevAt_app (fn arg : AExpr β) (arguments : List (AExpr β)) (cutoff : Nat) : + (fn.app arg).instRevAt arguments cutoff = + (fn.instRevAt arguments cutoff).app (arg.instRevAt arguments cutoff) := by + induction arguments generalizing fn arg with + | nil => rfl + | cons argument arguments ih => simp only [instRevAt, inst, ih] + +@[simp] theorem instRevAt_lam (condition : Certified.PropWhen) (domain body : AExpr β) + (arguments : List (AExpr β)) (cutoff : Nat) : + (lam condition domain body).instRevAt arguments cutoff = + lam condition (domain.instRevAt arguments cutoff) (body.instRevAt arguments (cutoff + 1)) := by + induction arguments generalizing domain body with + | nil => rfl + | cons argument arguments ih => + simp only [instRevAt, inst, ih, + show cutoff + arguments.length + 1 = cutoff + 1 + arguments.length by omega] + +@[simp] theorem instRevAt_forallE (condition : Certified.PropWhen) (domain body : AExpr β) + (arguments : List (AExpr β)) (cutoff : Nat) : + (forallE condition domain body).instRevAt arguments cutoff = + forallE condition (domain.instRevAt arguments cutoff) (body.instRevAt arguments (cutoff + 1)) := by + induction arguments generalizing domain body with + | nil => rfl + | cons argument arguments ih => + simp only [instRevAt, inst, ih, + show cutoff + arguments.length + 1 = cutoff + 1 + arguments.length by omega] + +@[simp] theorem instRevAt_proj (ref : ConstRef β) (field : Nat) (major : AExpr β) + (arguments : List (AExpr β)) (cutoff : Nat) : + (proj ref field major).instRevAt arguments cutoff = + proj ref field (major.instRevAt arguments cutoff) := by + induction arguments generalizing major with + | nil => rfl + | cons argument arguments ih => simp only [instRevAt, inst, ih] + +end Ix.Theory.Model.AExpr diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index e34c76fd0..84aed0520 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -524,6 +524,90 @@ private def betaUnderBinder : Bool := | .ok passed after => passed && after.lctx.size == 0 | .error _ _ => false +/-- Observe one production step, including the exact consumed prefix. The +partial cases retain binders; the function case exposes a new lambda only +after substitution and therefore leaves its argument for a later step. -/ +private def multiBetaLocalResult (shape : Nat) : Bool := + let action : RecM .anon Bool := RecM.withLctxScope do + let (carrier, _) ← TcM.openBinder (m := .anon) () () (.mkSort .mkZero) (.mkVar 0 ()) + let (first, _) ← TcM.openBinder (m := .anon) () () carrier (.mkVar 0 ()) + let (second, _) ← TcM.openBinder (m := .anon) () () carrier (.mkVar 0 ()) + let three := KExpr.mkLam () () (.mkSort .mkZero) + (.mkLam () () (.mkVar 0 ()) (.mkLam () () (.mkVar 1 ()) (.mkVar 1 ()))) + let identity := KExpr.mkLam () () carrier (.mkVar 0 ()) + let head := if shape == 3 then + KExpr.mkLam () () (.mkAll () () carrier carrier) (.mkVar 0 ()) + else if shape == 4 then + KExpr.mkLam () () (.mkSort .mkZero) (.mkLam () () (.mkVar 0 ()) second) + else three + let arguments := if shape == 1 || shape == 4 then #[carrier, first] + else if shape == 2 then #[carrier] + else if shape == 3 then #[identity, first] + else if shape == 5 then #[carrier, carrier, second] + else #[carrier, first, second] + let term := KExpr.mkAppN head arguments + if shape == 5 then + try + let _ ← RecM.inferCall term + return false + catch _ => return true + let _ ← RecM.inferCall term + let expected := if shape == 1 then KExpr.mkLam () () carrier first + else if shape == 2 then KExpr.mkLam () () carrier (.mkLam () () carrier (.mkVar 1 ())) + else if shape == 3 then KExpr.mkApp identity first + else if shape == 4 then second + else first + let (rawHead, rawArguments) := term.collectSpine + let (_, consumed) := RecM.consumeBetaLams rawHead rawArguments + let beforeStep ← get + let .next result ← RecM.whnfCoreWithFlagsStep term .FULL | return false + let cheap ← TcM.runIntern (cheapBetaReduce term) + let afterStep ← get + return consumed == (if shape == 3 then #[identity] else arguments) && + result == expected && result != term && first != second && + afterStep.env.nextFVarId == beforeStep.env.nextFVarId && afterStep.lctx.size == beforeStep.lctx.size && + cheap == (if shape == 1 || shape == 2 then term else expected) + match TcM.runRec action (TcState.ofEnvAnon {}) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +/-- Distinct carrier addresses make selecting the wrong argument observable. +The second carrier retains an unused universe-table entry to distinguish its +content-addressed declaration from the first carrier's identical type. -/ +private def multiBetaDeclaredType (level : Ixon.Univ) (universes : UInt64 := 0) + (dependent wrongValue : Bool := false) : Ixon.Env × Address := Id.run do + let (env, carrier) := storeConst {} + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], #[level]⟩ + let (env, otherCarrier) := storeConst env + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], #[level, .succ level]⟩ + let arguments := if universes == 0 then #[] else #[0] + let (env, witness) := storeConst env + ⟨.axio ⟨false, universes, .ref 0 arguments⟩, #[], #[carrier], #[level]⟩ + let (env, otherWitness) := storeConst env + ⟨.axio ⟨false, universes, .ref 0 arguments⟩, #[], #[otherCarrier], #[level]⟩ + let head := Ixon.Expr.leanLam (.sort 0) + (.leanLam (if dependent then .var 0 else .sort 0) (.var 1)) + return storeConst env + ⟨.defn ⟨.defn, .safe, universes, + .app (.app head (.ref 0 arguments)) (.ref (if dependent then 2 else 1) arguments), + .ref (if wrongValue then 3 else 2) arguments⟩, + #[], #[carrier, otherCarrier, witness, otherWitness], #[level]⟩ + +/-- The declared type reduces to `F A` after consuming just its first +argument. The untouched suffix is essential to matching the witness's type. -/ +private def multiBetaDeclaredSuffix (level : Ixon.Univ) : Ixon.Env := Id.run do + let (env, carrier) := storeConst {} + ⟨.axio ⟨false, 0, .sort 0⟩, #[], #[], #[level]⟩ + let functionType := Ixon.Expr.leanAll (.sort 0) (.sort 0) + let (env, family) := storeConst env + ⟨.axio ⟨false, 0, functionType⟩, #[], #[], #[level]⟩ + let (env, witness) := storeConst env + ⟨.axio ⟨false, 0, .app (.ref 0 #[]) (.ref 1 #[])⟩, #[], #[family, carrier], #[]⟩ + return (storeConst env + ⟨.defn ⟨.defn, .safe, 0, + .app (.app (.leanLam functionType (.var 0)) (.ref 0 #[])) (.ref 1 #[]), + .ref 2 #[]⟩, #[], #[family, carrier, witness], #[level]⟩).1 + private def applicationCases : TestSeq := test "application environment: Prop/Type identity calls and transitive theorem calls check" (allSucceeded applicationEnvironment 5 { clearEvery := 0 }) @@ -580,6 +664,29 @@ private def applicationCases : TestSeq := ++ test "beta conversion: reducing a declared proposition cannot make it its own proof" (let (env, target) := betaDeclaredType .zero 0 true; rowFailed env target) +private def multiBetaCases : TestSeq := + test "multi beta: three dependent arguments retain outer-to-inner order" (multiBetaLocalResult 0) + ++ test "multi beta: two consumed binders leave a capture-free lambda" (multiBetaLocalResult 1) + ++ test "multi beta: one consumed binder substitutes through two remaining domains" (multiBetaLocalResult 2) + ++ test "multi beta: a newly exposed lambda retains the unconsumed argument suffix" (multiBetaLocalResult 3) + ++ test "cheap beta: a closed body selects the original outer local" (multiBetaLocalResult 4) + ++ test "multi beta: the dependent argument must inhabit the selected carrier" (multiBetaLocalResult 5) + ++ test "multi beta admission: distinct carrier arguments reduce in Prop and Type" + (allSucceeded (multiBetaDeclaredType .zero).1 5 && + allSucceeded (multiBetaDeclaredType (.succ .zero)).1 5) + ++ test "multi beta admission: declared universe parameters survive simultaneous substitution" + (allSucceeded (multiBetaDeclaredType (.var 0) 1).1 5) + ++ test "multi beta admission: the second lambda domain depends on the first argument" + (allSucceeded (multiBetaDeclaredType .zero 0 true).1 5 && + allSucceeded (multiBetaDeclaredType (.succ .zero) 0 true).1 5) + ++ test "multi beta admission: conversion succeeds with fresh per-item caches" + (allSucceeded (multiBetaDeclaredType .zero).1 5 { clearEvery := 1 }) + ++ test "multi beta admission: rebuilding the suffix retains the family application" + (allSucceeded (multiBetaDeclaredSuffix .zero) 4 && + allSucceeded (multiBetaDeclaredSuffix (.succ .zero)) 4) + ++ test "multi beta admission: selecting a witness of the other carrier is rejected" + (let (env, target) := multiBetaDeclaredType .zero 0 false true; rowFailed env target) + /-- Call a polymorphic identity from a monomorphic function body. Universe indices select entries in the declaration's explicit level table. -/ private def storePolymorphicCall (env : Ixon.Env) (identity : Address) @@ -2171,7 +2278,7 @@ private def polymorphicDefinitionCases : TestSeq := | _ => false : Bool) public def suite : List TestSeq := - [cases, polymorphicCases, specializationCases, binderCases, applicationCases, + [cases, polymorphicCases, specializationCases, binderCases, applicationCases, multiBetaCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, sourceAgreementCases, sourceCacheCases, polymorphicDefinitionCases] diff --git a/Tests/Theory/ImportManifest.lean b/Tests/Theory/ImportManifest.lean index 77262ca7a..59b729c78 100644 --- a/Tests/Theory/ImportManifest.lean +++ b/Tests/Theory/ImportManifest.lean @@ -18,7 +18,8 @@ def conLecheRevision : String := "86cd20a65660d757cedc81561a44579099b565d0" /-- New mathematical modules authored in Ix, with no imported source hash. -/ def authored : Array String := #[ "Ix/Theory/Model/Checking.lean", "Ix/Theory/Model/LevelCongruence.lean", - "Ix/Theory/Model/UniverseBounds.lean", "Ix/Theory/Model/Substitution.lean" + "Ix/Theory/Model/UniverseBounds.lean", "Ix/Theory/Model/Substitution.lean", + "Ix/Theory/Model/BetaSubstitution.lean", "Ix/Theory/Model/BetaSpine.lean" ] structure SourceFile where diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index d34d5e4a7..977746f04 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -476,7 +476,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 413 exact theorem boundaries. The production +The consistency target checks 470 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -492,14 +492,23 @@ Their model-side level congruence introduces no native proof dependency. Semantic checking against a formed type and the new product-fibre universe bound also use only standard Lean axioms. The substitution rules for model typing, checking, and equality introduce no additional assumptions. -`SynthesisInference.beta_sound` derives source beta equality and result typing -from the actual lambda and argument inference. Its domain-shape proof retains -information lost when proof values are identified. `beta_step` connects this -to the one-argument production WHNF step, with finite construction, size, and -collision resources for its actual simultaneous-substitution walker. It -derives the lambda-head callback from the real recursive method table and -returns intern coherence. Arbitrary generated redexes and multi-step reduction -still require further proofs. +`SynthesisInference.beta_peel_sound` derives beta-prefix equality and result +typing from actual lambda and dependent argument inference. Its domain-shape +proof retains information lost when proof values are identified. The original +lambda prefix is instantiated simultaneously, and the remaining arguments +retain their order. `beta_many_step` connects this to the production WHNF +step, with finite construction, size, and collision resources for the actual +walker and interned suffix. It derives the lambda-head callback from the real +recursive method table and returns intern coherence. Bounds concern the +original body and argument trees rather than generated substitution trees. +`cheapBeta_plan_sound` also connects selected closed-body and variable plans +to this meaning when an actual source check is available. +`DefinitionCheckSupport.betaDeclaredSpine` uses the declaration's own type +check to justify conversion through such a prefix and suffix, and therefore +reaches the environment model-extension and no-False roots. The checking +origins of generated types still need to be tracked through changed cheap +beta in lambda inference. Arbitrary generated redexes, repeated reduction, +and the other conversion paths remain open. Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, argument order, and rejection of wrong arities and out-of-range parameters. @@ -519,6 +528,13 @@ Beta regressions cover declared-type conversion in Prop, Type, and at a universe parameter; substitution through returned function types; unequal initial hashes and repeated conversion; fresh caches; capture avoidance beneath returned lambdas; and rejection of a proposition used as its own proof. +Multi-argument cases check dependent domains, distinct carrier arguments, +partial lambda prefixes, capture avoidance through remaining binders, and +suffix rebuilding after substitution exposes a new lambda. They exercise +both cheap-beta fast paths and check that reduction preserves the local +context and fresh-variable counter. Declaration cases cover universe +parameters, cache clearing, a remaining family application, and rejection of +a witness belonging to the other carrier. Polymorphic-call regressions include Prop/Type instances in real function bodies, `max`/`imax` simplification inside Pi domains, closed nested references under active locals, separate cache keys for different universe instances, @@ -588,7 +604,7 @@ Definition-cycle regressions use content-addressed standalone and mutual declarations, including a self-justifying theorem, a two-member cycle, type cycles, lets, shared syntax, and binders. They check repeated member failures, acyclic forward references, cache clearing, and the partial/unsafe policy. -The unit suite contains 596 checks. The anonymous differential additionally +The unit suite contains 608 checks. The anonymous differential additionally serializes eight cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. @@ -631,7 +647,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Source ownership, block registration, and finite preflight | [`Consistency/BlockOwnership.lean`](../Ix/Kernel/Verify/Consistency/BlockOwnership.lean), [`Consistency/SourceOwnershipCheck.lean`](../Ix/Kernel/Verify/Consistency/SourceOwnershipCheck.lean), [`SourceOwnership.lean`](../Ix/Kernel/SourceOwnership.lean) | | Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`Application.lean`](../Ix/Kernel/Verify/Consistency/Application.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | | Inferred type formation and direct lambda applications | [`Consistency/SynthesisInference.lean`](../Ix/Kernel/Verify/Consistency/SynthesisInference.lean), [`Formation.lean`](../Ix/Kernel/Verify/Consistency/Formation.lean), [`Model/UniverseBounds.lean`](../Ix/Theory/Model/UniverseBounds.lean) | -| Source beta reduction and declaration conversion | [`Consistency/Beta.lean`](../Ix/Kernel/Verify/Consistency/Beta.lean), [`BetaSubstitution.lean`](../Ix/Kernel/Verify/Consistency/BetaSubstitution.lean), [`Model/Substitution.lean`](../Ix/Theory/Model/Substitution.lean) | +| Source beta reduction and declaration conversion | [`Consistency/BetaSpine.lean`](../Ix/Kernel/Verify/Consistency/BetaSpine.lean), [`Simultaneous.lean`](../Ix/Kernel/Verify/Consistency/Simultaneous.lean), [`SpineReading.lean`](../Ix/Kernel/Verify/Consistency/SpineReading.lean), [`CheapBeta.lean`](../Ix/Kernel/Verify/Consistency/CheapBeta.lean), [`Model/BetaSpine.lean`](../Ix/Theory/Model/BetaSpine.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | | Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | | Host commands, receipts, and frozen regression evidence | [Certified checking guide](certified-checking.md) | @@ -646,5 +662,6 @@ fixtures used by the existing proofs. Source hashes and attribution are in Apache license is preserved alongside the sources. The axiom-audit helper and direct production-fragment proofs are authored in Ix. `Model/LevelCongruence.lean`, `Model/Checking.lean`, `Model/UniverseBounds.lean`, -and `Model/Substitution.lean` are Ix-authored mathematical additions, listed -separately from the imported files in the theory provenance manifest. +`Model/Substitution.lean`, `Model/BetaSubstitution.lean`, and +`Model/BetaSpine.lean` are Ix-authored mathematical additions, listed separately +from the imported files in the theory provenance manifest. diff --git a/docs/theory.md b/docs/theory.md index 97892355b..39fd98046 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -45,14 +45,20 @@ declaration success supplies the corresponding definition traces. For a source beta redex, the inference tree retains the lambda's checked domain and supplies the argument's membership in it. This derives equality and typing for the substituted result; denotational typing alone cannot -recover that domain after proof values have been identified. A direct -one-argument structural-WHNF step reads this result through the actual -simultaneous-substitution walker and preserves intern coherence. Substitution -also preserves the model's typing, checking, and equality judgments. -Declaration admission now includes a beta-reducible declared type: the -declaration's own executed type check justifies conversion from its reduced -form to the original type. The environment model and no-False theorems include -this case. General reduction and conversion remain open. +recover that domain after proof values have been identified. This extends to +the full original lambda prefix, with dependent arguments and any remaining +application suffix. The actual multi-argument structural-WHNF step reads the +result through simultaneous substitution and interned suffix rebuilding, +preserving intern coherence. The walk's bounds concern the original body and +argument trees. Substitution also preserves the model's typing, checking, +and equality judgments. Selected cheap-beta plans preserve the same meaning +when an actual source check is available. +Declaration admission includes declared types reduced through a lambda +prefix and a remaining suffix: the declaration's own executed type check +justifies conversion from that result to the original type. The environment +model and no-False theorems include this case. Checking origins still need to +be tracked through changed cheap beta in lambda inference; general reduction +and conversion remain open. Safe definition admission also rejects circular justification in both Lean and Rust, including `theorem loop : P := loop` with only `P : Prop` assumed. The production dependency walk returns an order with a proved decreasing rank; From 14bd090c3ec856abe2e784ac0b3b8e8f299596f8 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 02:49:57 -0400 Subject: [PATCH 26/63] Retain checked type origins through lambda beta reduction --- Ix/Kernel/Verify/Consistency.lean | 12 +- Ix/Kernel/Verify/Consistency/Audit.lean | 33 +- Ix/Kernel/Verify/Consistency/Beta.lean | 2 +- Ix/Kernel/Verify/Consistency/BetaSpine.lean | 100 +----- .../Verify/Consistency/BinderInference.lean | 62 +++- Ix/Kernel/Verify/Consistency/CheapBeta.lean | 59 +--- .../Verify/Consistency/CheapBetaReading.lean | 103 ++++++ Ix/Kernel/Verify/Consistency/Formation.lean | 90 ++++++ .../Verify/Consistency/SpineReading.lean | 92 ++++++ .../Consistency/SynthesisInference.lean | 305 ++++++++++++++++-- Ix/Theory/Model/BetaSpine.lean | 143 ++++++++ Ix/Theory/Model/BetaSubstitution.lean | 66 ++++ Tests/Ix/Kernel/Consistency.lean | 104 +++++- docs/kernel-verification.md | 38 ++- docs/theory.md | 12 +- 15 files changed, 1024 insertions(+), 197 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/CheapBetaReading.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 05df19e57..1289a04ae 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -31,6 +31,7 @@ import Ix.Kernel.Verify.Consistency.BetaSubstitution import Ix.Kernel.Verify.Consistency.Beta import Ix.Kernel.Verify.Consistency.Simultaneous import Ix.Kernel.Verify.Consistency.SpineReading +import Ix.Kernel.Verify.Consistency.CheapBetaReading import Ix.Kernel.Verify.Consistency.BetaSpine import Ix.Kernel.Verify.Consistency.CheapBeta import Ix.Kernel.Verify.Consistency.Validation @@ -84,8 +85,15 @@ body and arguments. Selected cheap-beta plans have the same typed meaning when an actual source check is available. Definition admission can reuse its executed declared-type inference to justify a beta-prefix conversion to the value's inferred type. These paths are included in environment model -preservation. Tracking the checking origins of generated types through -changed cheap beta in lambda inference and general reduction remains open. +preservation. Recursive lambda inference also handles changed cheap beta +when the returned body type retains its actual checking origin. The proof +preserves checked lambda domains during the same inference recursion, reads +the selected prefix from the actual generated type, and transports the +original check through interface growth, local weakening, and universe +instantiation. Earlier local contexts are reconstructed from their executed +domain checks. Abstraction uses the reduced type and the reduction's final +intern table. Automatic origin construction for arbitrary generated types +and general reduction remains open. Local cache hits agree with the actual declaration type. Constant hits agree with pure universe substitution of a loaded, admitted declaration; sort hits return the canonical successor sort. Application, forall, and lambda diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index a33633ce3..b7746d32a 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -326,6 +326,36 @@ private def betaRoots : Array RootAllowance := #[ { root := ``SynthesisInference.cheapBeta_plan_sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } ] +private def typeOriginRoots : Array RootAllowance := #[ + { root := ``LambdaBodyTrace.output_state, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``LambdaBodyTrace.output, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``readScopedExpr?_lambda_spine, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``CheapBetaSupport.reading, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``TypeReductionTransport.weakenPrefix, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``TypeReductionTransport.instantiatePrefix, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``TypeReductionTransport.sound, standardAxioms := standard }, + { root := ``BinderInference.lambdaSpineTyping, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.soundWithSpine, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisContext.sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``Theory.Model.AExpr.instL_liftN, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.instL_inst, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.liftN_liftN_comm, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.liftN_inst, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.liftN_inst_zero, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.lambdaDepth_liftN, standardAxioms := #[``propext] }, + { root := ``Theory.Model.AExpr.lambdaDepth_instL, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.liftN_appN }, + { root := ``Theory.Model.AExpr.instL_appN, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.liftN_betaPrefix, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.instL_betaPrefix, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.appN_ne_forallE }, + { root := ``Theory.Model.LambdaPrefix.lambdaDepth_zero, standardAxioms := #[``propext] }, + { root := ``Theory.Model.LambdaSpineTyping.non_application, standardAxioms := standard }, + { root := ``Theory.Model.LambdaSpineTyping.lam, standardAxioms := standard }, + { root := ``Theory.Model.LambdaSpineTyping.app, standardAxioms := standard }, + { root := ``Theory.Model.LambdaSpineTyping.betaPrefix, standardAxioms := standard } +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -453,7 +483,8 @@ def roots : Array RootAllowance := #[ }) ++ productionRoots.map (fun root => { root, standardAxioms := standard, nativeAxioms := productionNative, forbiddenDependencies := forbiddenProduction -}) ++ betaRoots.map (fun allowance => { allowance with forbiddenDependencies := forbiddenProduction }) +}) ++ (betaRoots ++ typeOriginRoots).map (fun allowance => { + allowance with forbiddenDependencies := forbiddenProduction }) ++ #[{ root := ``extend_atomic_definition, standardAxioms := standard }] run_cmd Kernel.Verify.Audit.check roots diff --git a/Ix/Kernel/Verify/Consistency/Beta.lean b/Ix/Kernel/Verify/Consistency/Beta.lean index 2144fbc25..524d455cd 100644 --- a/Ix/Kernel/Verify/Consistency/Beta.lean +++ b/Ix/Kernel/Verify/Consistency/Beta.lean @@ -44,7 +44,7 @@ theorem SynthesisInference.lambda_type {β : Type u} cases support with | known inference => exact inference.lambda_type | reuseType inference => exact inference.lambda_type - | lam => exact ⟨_, rfl⟩ + | lam | lamBeta => exact ⟨_, rfl⟩ private theorem BinderInference.no_direct_beta {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} diff --git a/Ix/Kernel/Verify/Consistency/BetaSpine.lean b/Ix/Kernel/Verify/Consistency/BetaSpine.lean index 972e37a54..73c7eb156 100644 --- a/Ix/Kernel/Verify/Consistency/BetaSpine.lean +++ b/Ix/Kernel/Verify/Consistency/BetaSpine.lean @@ -16,64 +16,6 @@ open Theory Theory.Model universe u v -private theorem list_reverse_induction {α : Type u} {motive : List α → Prop} - (nil : motive []) - (append_singleton : ∀ tail last, motive tail → motive (tail ++ [last])) - (values : List α) : motive values := by - have reversed : ∀ items : List α, motive items.reverse := by - intro items - induction items with - | nil => exact nil - | cons item items ih => - simpa only [List.reverse_cons] using append_singleton items.reverse item ih - simpa using reversed values.reverse - -theorem BinderInference.lambdaPrefix {β : Type u} - {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} - {locals : List FVarId} {context : Model.Context β} {fuel : Nat} - {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} - (support : BinderInference resolve entries locals context fuel before source term type) : - LambdaPrefix term type term.lambdaDepth := by - induction support with - | lam _ _ _ _ _ _ _ _ _ _ _ ih => exact .lam ih - | _ => exact .zero _ _ - -theorem SynthesisInference.lambdaPrefix {β : Type u} - {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} - {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} - {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} {level : VLevel} - (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) : - LambdaPrefix term type term.lambdaDepth := by - induction support with - | known inference => exact inference.lambdaPrefix - | reuseType inference => exact inference.lambdaPrefix - | lam _ _ _ _ _ _ _ _ _ _ _ _ _ _ ihBody => exact .lam ihBody - | _ => exact .zero _ _ - -theorem SynthesisHead.appN_head {β : Type u} {head : AExpr β} {arguments : List (AExpr β)} - (support : SynthesisHead (head.appN arguments)) : SynthesisHead head := by - induction arguments generalizing head with - | nil => exact support - | cons argument arguments ih => - have applied := ih support - cases applied with - | app head => exact head - -private theorem BinderInference.no_lambda_spine {β : Type u} - {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} - {locals : List FVarId} {context : Model.Context β} {fuel : Nat} - {before : TcState .anon} {source : KExpr .anon} - {condition : Certified.PropWhen} {domain body type : AExpr β} {arguments : List (AExpr β)} - (support : BinderInference resolve entries locals context fuel before source - ((AExpr.lam condition domain body).appN arguments) type) - (nonempty : arguments ≠ []) : False := by - induction arguments using list_reverse_induction with - | nil => exact nonempty rfl - | append_singleton arguments argument ih => - simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] at support - cases support with - | app _ _ _ _ head => cases head.appN_head - /-- Actual application inference supplies each dependent argument type, while the head's inference supplies all original leading lambda domains. -/ theorem SynthesisInference.lambda_spine {β : Type u} @@ -92,46 +34,8 @@ theorem SynthesisInference.lambda_spine {β : Type u} ∃ headType, TypingClaim.{u,v} entries context (.lam condition domain body) headType ∧ LambdaPrefix (.lam condition domain body) headType (body.lambdaDepth + 1) ∧ ArgumentSpine.{u,v} entries context headType arguments type := by - induction arguments using list_reverse_induction generalizing fuel before after source result type level with - | nil => - obtain ⟨reads, typed, _⟩ := support.sound formed agreement reading accepted - exact ⟨reads, _, typed, support.lambdaPrefix, .nil _⟩ - | append_singleton arguments argument ih => - simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] at support reading - cases support with - | known inference => - apply False.elim - apply BinderInference.no_lambda_spine (arguments := arguments ++ [argument]) - (condition := condition) (domain := domain) (body := body) - · simpa only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] using inference - · simp - | reuseType inference => - apply False.elim - apply BinderInference.no_lambda_spine (arguments := arguments ++ [argument]) - (condition := condition) (domain := domain) (body := body) - · simpa only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] using inference - · simp - | app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful - bodyConstructed argConstructed bodyBound argBound coherent faithful => - obtain ⟨state, run⟩ := infer_uncached_success miss accepted - rw [full] at run - obtain ⟨fnReads, argReads⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - obtain ⟨functionTypeReads, headType, headTyped, leading, spine⟩ := - ih functionTree keyedAgreement fnReads trace.functionRun - obtain ⟨domainReads, codomainReads⟩ := readScopedExpr?_all_parts functionTypeReads - have argumentAgreement := trace.contextPreserved.symm ▸ keyedAgreement - obtain ⟨argumentTypeReads, argumentTyped, _⟩ := - argumentTree.sound formed argumentAgreement argReads trace.argumentRun - have sameReading := beq_readScopedExpr? (resolve := resolve) (locals := locals) - (depth := 0) comparisonFaithful hashPath - have sameType := AExpr.eq_of_erase_annotations - (Option.some.inj (argumentTypeReads.symm.trans (sameReading.trans domainReads))) conditions - refine ⟨?_, headType, headTyped, leading, - spine.append (.cons (sameType ▸ argumentTyped) (.nil _))⟩ - rw [trace.output run, AExpr.erase_inst] - exact (subst_readScopedExpr? bodyConstructed argConstructed bodyBound argBound - coherent faithful codomainReads argReads).1 + obtain ⟨reads, _, _, spines⟩ := support.soundWithSpine formed agreement reading accepted + exact ⟨reads, spines _ _ _ _ rfl⟩ /-- The complete original lambda prefix may be consumed without another inference call on any intermediate beta result. -/ diff --git a/Ix/Kernel/Verify/Consistency/BinderInference.lean b/Ix/Kernel/Verify/Consistency/BinderInference.lean index 6ca510094..50b332c4b 100644 --- a/Ix/Kernel/Verify/Consistency/BinderInference.lean +++ b/Ix/Kernel/Verify/Consistency/BinderInference.lean @@ -138,9 +138,8 @@ theorem ForallInferenceTrace.output {fuel : Nat} {before after : TcState .anon} (KExpr.mkSort (KUniv.mkIMax trace.domainLevel trace.bodyLevel))).1 := (trace.output_state accepted).1 -/-- Full-mode lambda inference validates its domain, opens the binder, and -infers the body. The current fragment takes the unchanged cheap-beta path. -/ -structure LambdaInferenceTrace (fuel : Nat) (before : TcState .anon) +/-- The executed checks before lambda inference reduces its body type. -/ +structure LambdaBodyTrace (fuel : Nat) (before : TcState .anon) (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) (domain body : KExpr .anon) where domainLevel : KUniv .anon @@ -156,8 +155,65 @@ structure LambdaInferenceTrace (fuel : Nat) (before : TcState .anon) openRun : TcM.openBinder name bi domain body domainState = .ok (opened, fresh) openedState bodyRun : RecM.infer opened (methodsN fuel) openedState = .ok bodyType bodyState contextPreserved : domainState.lctx = before.lctx + +/-- The original no-op specialization of the general lambda-body trace. -/ +structure LambdaInferenceTrace (fuel : Nat) (before : TcState .anon) + (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) + (domain body : KExpr .anon) extends LambdaBodyTrace fuel before name bi domain body where betaUnchanged : cheapBetaPlan? bodyType = none +def LambdaBodyTrace.reduced {fuel : Nat} {before : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body : KExpr .anon} (trace : LambdaBodyTrace fuel before name bi domain body) := + cheapBetaReduce trace.bodyType trace.bodyState.env.intern + +def LambdaBodyTrace.abstracted {fuel : Nat} {before : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body : KExpr .anon} (trace : LambdaBodyTrace fuel before name bi domain body) := + abstractFVars trace.reduced.1 #[trace.fresh] trace.reduced.2 + +/-- The returned lambda type is the actual abstracted body type, wrapped in +the production Pi constructor and passed through the final intern table. -/ +theorem LambdaBodyTrace.output_state {fuel : Nat} {before after : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body result : KExpr .anon} {info : ExprInfo .anon} + (trace : LambdaBodyTrace fuel before name bi domain body) + (accepted : RecM.inferUncached RecM.inferCall false (.lam name bi domain body info) + (methodsN (fuel + 1)) before = .ok result after) : + result = (trace.abstracted.2.internExpr (KExpr.mkAll () () domain trace.abstracted.1)).1 ∧ + after = {trace.bodyState with + env := {trace.bodyState.env with + intern := (trace.abstracted.2.internExpr (KExpr.mkAll () () domain trace.abstracted.1)).2} + lctx := trace.bodyState.lctx.truncate trace.domainState.lctx.size} := by + change (RecM.inferUncached RecM.inferCall false (.lam name bi domain body info)).run + (methodsN (fuel + 1)) before = .ok result after at accepted + unfold RecM.inferUncached at accepted + simp only [Bool.not_false, if_true, ReaderT.run_bind] at accepted + change EStateM.bind (RecM.infer domain (methodsN fuel)) _ before = _ at accepted + rw [EStateM.bind, trace.domainRun] at accepted + change (RecM.withLctxScope _).run (methodsN (fuel + 1)) trace.domainState = _ at accepted + obtain ⟨scopedState, accepted, cleanup⟩ := withLctxScope_success accepted + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted + change EStateM.bind (TcM.openBinder name bi domain body) _ trace.domainState = + .ok result scopedState at accepted + rw [EStateM.bind, trace.openRun] at accepted + have bodyRun : (RecM.inferCall trace.opened).run (methodsN (fuel + 1)) + trace.openedState = .ok trace.bodyType trace.bodyState := trace.bodyRun + change EStateM.bind ((RecM.inferCall trace.opened).run (methodsN (fuel + 1))) + _ trace.openedState = .ok result scopedState at accepted + rw [EStateM.bind, bodyRun] at accepted + cases accepted + exact ⟨rfl, cleanup⟩ + +theorem LambdaBodyTrace.output {fuel : Nat} {before after : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body result : KExpr .anon} {info : ExprInfo .anon} + (trace : LambdaBodyTrace fuel before name bi domain body) + (accepted : RecM.inferUncached RecM.inferCall false (.lam name bi domain body info) + (methodsN (fuel + 1)) before = .ok result after) : + result = (trace.abstracted.2.internExpr (KExpr.mkAll () () domain trace.abstracted.1)).1 := + (trace.output_state accepted).1 + def LambdaInferenceTrace.abstracted {fuel : Nat} {before : TcState .anon} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body : KExpr .anon} (trace : LambdaInferenceTrace fuel before name bi domain body) := diff --git a/Ix/Kernel/Verify/Consistency/CheapBeta.lean b/Ix/Kernel/Verify/Consistency/CheapBeta.lean index 32e0a6884..ee3e0a541 100644 --- a/Ix/Kernel/Verify/Consistency/CheapBeta.lean +++ b/Ix/Kernel/Verify/Consistency/CheapBeta.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.BetaSpine -import Ix.Kernel.Verify.Infer.CheapBetaPlan +import Ix.Kernel.Verify.Consistency.CheapBetaReading /-! The selected cheap-beta plan has the same typed prefix meaning as general beta, including its variable-selection and closed-body cases. -/ @@ -48,11 +48,15 @@ theorem SynthesisInference.cheapBeta_plan_sound {β : Type u} TypingClaim.{u,v} entries context ((body.instRev (arguments.take count)).appN (arguments.drop count)) type ∧ (cheapBetaReduce source table).2.WF := by - obtain ⟨head, rawBody, args, count, plannedSpine, peeling, countBound, base, trailing⟩ := + obtain ⟨head, rawBody, args, count, plannedSpine, peeling, countBound, _, _⟩ := cheapBetaPlan?_simul selected walkerBounds obtain ⟨sameHead, sameArgs⟩ := Prod.mk.inj (spine.symm.trans plannedSpine) subst head args have reading := readScopedExpr?_collectSpine spine headReads argumentReads + have resources : CheapBetaSupport source table := ⟨plan, selected, walkerBounds, coherent, faithful⟩ + obtain ⟨enough, resultReads, preserved⟩ := resources.reading reading + obtain ⟨_, _, _, sourceSpine⟩ := support.soundWithSpine formed agreement reading accepted + obtain ⟨conversion, reducedTyped⟩ := sourceSpine.betaPrefix enough have rawPeel := RecM.BetaPeel.of_peelLamsN (.lam name bi rawDomain rawInner lambdaInfo) rawArguments.toList rw [Array.length_toList, peeling] at rawPeel @@ -66,48 +70,13 @@ theorem SynthesisInference.cheapBeta_plan_sound {β : Type u} have rawCountLength : (rawArguments.toList.take count).length = count := by simp only [List.length_take, Array.length_toList] omega - have prefixSize : (rawArguments.extract 0 count).size = count := by - simp only [Array.size_extract] - omega - have prefixReads : (rawArguments.extract 0 count).toList.map (readScopedExpr? resolve locals ·) = - (arguments.take count).map (some ·.erase) := by - simp only [Array.toList_extract, List.extract_eq_take_drop, List.drop_zero, Nat.sub_zero, - List.map_take, argumentReads] - have suffixReads : plan.trailing.map (readScopedExpr? resolve locals ·) = - (arguments.drop count).map (some ·.erase) := by - rw [trailing] - simp only [Array.toList_extract, List.extract_eq_take_drop, List.map_take, List.map_drop, - argumentReads, sizeAgrees] - rw [← List.map_drop, ← List.map_take] - congr 1 - have length : (arguments.drop count).length = arguments.length - count := List.length_drop - rw [← length, List.take_length] - have simulBounds := walkerBounds.2 spine peeling - have baseReads : readScopedExpr? resolve locals plan.base = - some (body.instRev (arguments.take count)).erase := by - rw [base] - have readSimul := readScopedExpr?_simulSubstSpec (depth := 0) - (by simp only [Array.size_reverse, prefixSize, countLength]) - simulBounds.2.2.2.1 simulBounds.2.2.1 - (by simpa only [UInt64.toNat_zero, Nat.zero_add, countLength, rawCountLength] using bodyReads) - (argumentsReading_reverse_get prefixReads (prefixSize.trans countLength.symm)) - simpa only [UInt64.toNat_zero, Nat.zero_add, AExpr.instRevAt_zero] using readSimul - have chainFaithful : KExpr.CollisionFree fun term => table.ExprSupport term ∨ - term ∈ cheapBetaChainList plan.base plan.trailing := by - apply faithful.mono - intro term member - rcases member with resident | candidate - · exact Or.inl resident - · exact Or.inr (by simpa [KExpr.CheapBetaReach, selected] using Or.inr candidate) - obtain ⟨resultReads, preserved⟩ := internAppChain_readScopedExpr? coherent chainFaithful baseReads suffixReads - have conversion := SynthesisInference.beta_peel_sound (consumed := arguments.take count) - (trailing := arguments.drop count) - (by simpa only [List.take_append_drop] using support) formed agreement - (by simpa only [List.take_append_drop] using reading) accepted - (by simpa only [countLength, rawCountLength] using modelPeel) - refine ⟨body, count, ?_, ?_, conversion.2, ?_⟩ - · simpa only [cheapBetaReduce, selected] using resultReads - · simpa only [List.take_append_drop] using conversion.1 - · simpa only [cheapBetaReduce, selected] using preserved + have counted : cheapBetaCount source = count := by + simp only [cheapBetaCount, spine, peeling] + have prefixEq := (show LambdaPeel (.lam condition domain inner) (arguments.take count).length body by + simpa only [countLength, rawCountLength] using modelPeel).betaPrefix (arguments.drop count) + simp only [List.take_append_drop, countLength] at prefixEq + exact ⟨body, count, by simpa only [counted, prefixEq] using resultReads, + by simpa only [counted, prefixEq] using conversion, + by simpa only [counted, prefixEq] using reducedTyped, preserved⟩ end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/CheapBetaReading.lean b/Ix/Kernel/Verify/Consistency/CheapBetaReading.lean new file mode 100644 index 000000000..240878a80 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/CheapBetaReading.lean @@ -0,0 +1,103 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.SpineReading +import Ix.Kernel.Verify.Infer.CheapBetaPlan + +/-! Read a selected cheap-beta result independently of inference soundness. +The only inputs are its source reading and finite representation resources. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +/-- The number of original lambdas selected by the production planner. -/ +def cheapBetaCount (source : KExpr .anon) : Nat := + (peelLamsN source.collectSpine.2.size source.collectSpine.1).2 + +/-- Concrete planning, arithmetic, and hash support for the actual cheap +reduction. The structure carries no typing or conversion facts. -/ +structure CheapBetaSupport (source : KExpr .anon) (table : InternTable .anon) where + plan : CheapBetaPlan .anon + selected : cheapBetaPlan? source = some plan + bounds : KExpr.CheapBetaBounds source + coherent : table.WF + faithful : KExpr.CollisionFree fun term => table.ExprSupport term ∨ KExpr.CheapBetaReach source term + +theorem CheapBetaSupport.reading {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {source : KExpr .anon} {table : InternTable .anon} + {condition : Certified.PropWhen} {domain inner : AExpr β} {arguments : List (AExpr β)} + (support : CheapBetaSupport source table) + (reading : readScopedExpr? resolve locals source = + some ((AExpr.lam condition domain inner).appN arguments).erase) : + cheapBetaCount source ≤ inner.lambdaDepth + 1 ∧ + readScopedExpr? resolve locals (cheapBetaReduce source table).1 = + some (AExpr.betaPrefix (cheapBetaCount source) (.lam condition domain inner) arguments).erase ∧ + (cheapBetaReduce source table).2.WF := by + obtain ⟨headReads, argumentReads⟩ := readScopedExpr?_lambda_spine reading + obtain ⟨head, rawBody, args, count, spine, peeling, countBound, base, trailing⟩ := + cheapBetaPlan?_simul support.selected support.bounds + have counted : cheapBetaCount source = count := by + simp only [cheapBetaCount, spine, peeling] + rw [spine] at headReads argumentReads + have rawPeel := RecM.BetaPeel.of_peelLamsN head args.toList + rw [Array.length_toList, peeling] at rawPeel + obtain ⟨body, modelPeel, bodyReads⟩ := betaPeel_readScopedExpr? rawPeel.1 headReads + have sizeAgrees : args.size = arguments.length := by + have lengths := congrArg List.length argumentReads + simpa using lengths + have countLength : (arguments.take count).length = count := by + simp only [List.length_take] + omega + have rawCountLength : (args.toList.take count).length = count := by + simp only [List.length_take, Array.length_toList] + omega + have prefixSize : (args.extract 0 count).size = count := by + simp only [Array.size_extract] + omega + have prefixReads : (args.extract 0 count).toList.map (readScopedExpr? resolve locals ·) = + (arguments.take count).map (some ·.erase) := by + simp only [Array.toList_extract, List.extract_eq_take_drop, List.drop_zero, Nat.sub_zero, + List.map_take, argumentReads] + have suffixReads : support.plan.trailing.map (readScopedExpr? resolve locals ·) = + (arguments.drop count).map (some ·.erase) := by + rw [trailing] + simp only [Array.toList_extract, List.extract_eq_take_drop, List.map_take, List.map_drop, + argumentReads, sizeAgrees] + rw [← List.map_drop, ← List.map_take] + congr 1 + have length : (arguments.drop count).length = arguments.length - count := List.length_drop + rw [← length, List.take_length] + have simulBounds := support.bounds.2 spine peeling + have baseReads : readScopedExpr? resolve locals support.plan.base = + some (body.instRev (arguments.take count)).erase := by + rw [base] + have readSimul := readScopedExpr?_simulSubstSpec (depth := 0) + (by simp only [Array.size_reverse, prefixSize, countLength]) + simulBounds.2.2.2.1 simulBounds.2.2.1 + (by simpa only [UInt64.toNat_zero, Nat.zero_add, countLength, rawCountLength] using bodyReads) + (argumentsReading_reverse_get prefixReads (prefixSize.trans countLength.symm)) + simpa only [UInt64.toNat_zero, Nat.zero_add, AExpr.instRevAt_zero] using readSimul + have chainFaithful : KExpr.CollisionFree fun term => table.ExprSupport term ∨ + term ∈ cheapBetaChainList support.plan.base support.plan.trailing := by + apply support.faithful.mono + intro term member + rcases member with resident | candidate + · exact Or.inl resident + · exact Or.inr (by simpa [KExpr.CheapBetaReach, support.selected] using Or.inr candidate) + obtain ⟨resultReads, preserved⟩ := + internAppChain_readScopedExpr? support.coherent chainFaithful baseReads suffixReads + have prefixEq := (show LambdaPeel (.lam condition domain inner) (arguments.take count).length body by + simpa only [countLength, rawCountLength] using modelPeel).betaPrefix (arguments.drop count) + simp only [List.take_append_drop, countLength] at prefixEq + refine ⟨?_, ?_, ?_⟩ + · simpa only [counted, rawCountLength, AExpr.lambdaDepth] using modelPeel.length_bound + · simpa only [cheapBetaReduce, support.selected, counted, prefixEq] using resultReads + · simpa only [cheapBetaReduce, support.selected] using preserved + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Formation.lean b/Ix/Kernel/Verify/Consistency/Formation.lean index ca765dcf7..09e5c6f38 100644 --- a/Ix/Kernel/Verify/Consistency/Formation.lean +++ b/Ix/Kernel/Verify/Consistency/Formation.lean @@ -4,6 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.BinderInference +import Ix.Theory.Model.BetaSpine /-! Type formation comes from actual inference of a type, including the type @@ -83,6 +84,95 @@ theorem typing_instL_closed {entries : Model.Environment β} {context : Model.Co (Context.valid_nil constants _ env) simpa only [wellDenoted_instL, interp_instL] using result +/-- Retain the syntax of both a checked type and its reduction while moving +that original check to its use site. Every step is an explicit interface or +syntax operation; no semantic equality is stored in this transport. -/ +inductive TypeReductionTransport : + Model.Environment β → Model.Context β → AExpr β → AExpr β → VLevel → + Model.Environment β → Model.Context β → AExpr β → AExpr β → VLevel → Type u where + | refl {entries context source reduced level} : + TypeReductionTransport entries context source reduced level entries context source reduced level + | extend {origin originContext source reduced level earlier later context current result bound} + (prior : TypeReductionTransport origin originContext source reduced level + earlier context current result bound) + (extension : InterfaceExtends earlier later) : + TypeReductionTransport origin originContext source reduced level later context current result bound + | weaken {origin originContext source reduced level entries context current result bound} + (prior : TypeReductionTransport origin originContext source reduced level + entries context current result bound) (domain : AExpr β) : + TypeReductionTransport origin originContext source reduced level entries (context.push domain) + (current.liftN 1) (result.liftN 1) bound + | instantiate {origin originContext source reduced level entries context current result bound} + (prior : TypeReductionTransport origin originContext source reduced level + entries [] current result bound) (arguments : List VLevel) : + TypeReductionTransport origin originContext source reduced level entries context + (current.instL arguments) (result.instL arguments) (bound.inst arguments) + | equivalent {origin originContext source reduced level entries context current result bound current' result'} + (prior : TypeReductionTransport origin originContext source reduced level + entries context current result bound) + (sameSource : AExpr.LevelEquivalent current current') + (sameResult : AExpr.LevelEquivalent result result') : + TypeReductionTransport origin originContext source reduced level entries context current' result' bound + +/-- An origin and its beta result cross a local binder together; the result +is derived by substitution algebra rather than supplied independently. -/ +def TypeReductionTransport.weakenPrefix + {origin entries : Model.Environment β} {originContext context : Model.Context β} + {source reduced head : AExpr β} {arguments : List (AExpr β)} {level bound : VLevel} {count : Nat} + (prior : TypeReductionTransport origin originContext source reduced level + entries context (head.appN arguments) (AExpr.betaPrefix count head arguments) bound) + (domain : AExpr β) : + TypeReductionTransport origin originContext source reduced level entries (context.push domain) + ((head.liftN 1).appN (arguments.map (AExpr.liftN 1 ·))) + (AExpr.betaPrefix count (head.liftN 1) (arguments.map (AExpr.liftN 1 ·))) bound := by + simpa only [AExpr.liftN_appN, AExpr.liftN_betaPrefix] using prior.weaken domain + +/-- Universe instantiation preserves the complete selected beta prefix, +including all annotations, dependent substitutions, and trailing arguments. -/ +def TypeReductionTransport.instantiatePrefix + {origin entries : Model.Environment β} {originContext context : Model.Context β} + {source reduced head : AExpr β} {arguments : List (AExpr β)} {level bound : VLevel} {count : Nat} + (prior : TypeReductionTransport origin originContext source reduced level + entries [] (head.appN arguments) (AExpr.betaPrefix count head arguments) bound) + (levels : List VLevel) : + TypeReductionTransport origin originContext source reduced level entries context + ((head.instL levels).appN (arguments.map (AExpr.instL levels))) + (AExpr.betaPrefix count (head.instL levels) (arguments.map (AExpr.instL levels))) + (bound.inst levels) := by + simpa only [AExpr.instL_appN, AExpr.instL_betaPrefix] using prior.instantiate levels + +theorem TypeReductionTransport.sound + {origin entries : Model.Environment β} {originContext context : Model.Context β} + {source reduced current result : AExpr β} {level bound : VLevel} + (transport : TypeReductionTransport origin originContext source reduced level + entries context current result bound) + (converted : ConversionClaim.{u,v} origin originContext source reduced) + (typed : TypingClaim.{u,v} origin originContext reduced (.sort level)) : + ConversionClaim.{u,v} entries context current result ∧ + TypingClaim.{u,v} entries context result (.sort bound) := by + induction transport with + | refl => exact ⟨converted, typed⟩ + | extend prior extension ih => + refine ⟨?_, extension.typing ih.2⟩ + intro V _ constants realizes levels env valid + exact ih.1 V constants (extension.realizes realizes) levels env valid + | weaken prior domain ih => + refine ⟨?_, typing_weaken ih.2⟩ + intro V _ constants realizes levels env valid + simpa only [interp_liftN] using + ih.1 V constants realizes levels _ (context_valid_tail valid) + | instantiate prior arguments ih => + refine ⟨?_, typing_instL_closed ih.2 arguments⟩ + intro V _ constants realizes levels env valid + simpa only [interp_instL] using + ih.1 V constants realizes (arguments.map (VLevel.eval levels)) env + (Context.valid_nil constants _ env) + | equivalent prior sameSource sameResult ih => + refine ⟨?_, sameResult.termTyping ih.2⟩ + intro V _ constants realizes levels env valid + exact (sameSource.interp constants levels env).symm.trans + ((ih.1 V constants realizes levels env valid).trans (sameResult.interp constants levels env)) + /-- Evidence of an executed type-inference call. The witness stores raw syntax, states, and a finite inference tree; it contains no semantic typing field. Its conclusion is derived below from production soundness. -/ diff --git a/Ix/Kernel/Verify/Consistency/SpineReading.lean b/Ix/Kernel/Verify/Consistency/SpineReading.lean index ba9fd524f..cbe2553ff 100644 --- a/Ix/Kernel/Verify/Consistency/SpineReading.lean +++ b/Ix/Kernel/Verify/Consistency/SpineReading.lean @@ -96,6 +96,98 @@ theorem readScopedExpr?_collectSpine {β : Type u} rw [readScopedExpr?_appSpineView, ← view.1, ← view.2] exact readScopedExpr?_appN headReads argumentReads +private theorem readScopedExpr?_app_inv {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {source : KExpr .anon} {fn arg : VExpr β} + (reading : readScopedExpr? resolve locals source = some (.app fn arg)) : + ∃ rawFn rawArg info, source = .app rawFn rawArg info ∧ + readScopedExpr? resolve locals rawFn = some fn ∧ + readScopedExpr? resolve locals rawArg = some arg := by + cases source with + | app rawFn rawArg info => exact ⟨rawFn, rawArg, info, rfl, readScopedExpr?_app_parts reading⟩ + | var index name info => simp [readScopedExpr?] at reading + | fvar id name info => + obtain ⟨_, _, same⟩ := Option.map_eq_some_iff.mp reading + cases same + | const id levels info => + obtain ⟨_, _, same⟩ := bind_success reading + cases same + | lam name bi domain body info | all name bi domain body info => + obtain ⟨_, _, reading⟩ := bind_success reading + obtain ⟨_, _, same⟩ := bind_success reading + cases same + | prj id index value info => + obtain ⟨_, _, reading⟩ := bind_success reading + obtain ⟨_, _, same⟩ := bind_success reading + cases same + | _ => simp [readScopedExpr?] at reading + +private theorem readScopedExpr?_lam_inv {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {source : KExpr .anon} {domain body : VExpr β} + (reading : readScopedExpr? resolve locals source = some (.lam domain body)) : + ∃ name bi rawDomain rawBody info, source = .lam name bi rawDomain rawBody info := by + cases source with + | lam name bi rawDomain rawBody info => exact ⟨name, bi, rawDomain, rawBody, info, rfl⟩ + | var index name info => simp [readScopedExpr?] at reading + | fvar id name info => + obtain ⟨_, _, same⟩ := Option.map_eq_some_iff.mp reading + cases same + | const id levels info => + obtain ⟨_, _, same⟩ := bind_success reading + cases same + | app fn arg info | all _ _ fn arg info => + obtain ⟨_, _, reading⟩ := bind_success reading + obtain ⟨_, _, same⟩ := bind_success reading + cases same + | prj id index value info => + obtain ⟨_, _, reading⟩ := bind_success reading + obtain ⟨_, _, same⟩ := bind_success reading + cases same + | _ => simp [readScopedExpr?] at reading + +private theorem list_reverse_induction {α : Type u} {motive : List α → Prop} + (nil : motive []) + (append_singleton : ∀ tail last, motive tail → motive (tail ++ [last])) + (values : List α) : motive values := by + have reversed : ∀ items : List α, motive items.reverse := by + intro items + induction items with + | nil => exact nil + | cons item items ih => + simpa only [List.reverse_cons] using append_singleton items.reverse item ih + simpa using reversed values.reverse + +/-- The reading of the whole generated type determines the actual raw +head and argument readings. No additional inference of its components is +needed when the kernel collects that type's lambda-headed spine. -/ +theorem readScopedExpr?_lambda_spine {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {source : KExpr .anon} {condition : Certified.PropWhen} {domain body : AExpr β} + {arguments : List (AExpr β)} + (reading : readScopedExpr? resolve locals source = + some ((AExpr.lam condition domain body).appN arguments).erase) : + readScopedExpr? resolve locals source.collectSpine.1 = + some (AExpr.lam condition domain body).erase ∧ + source.collectSpine.2.toList.map (readScopedExpr? resolve locals ·) = + arguments.map (some ·.erase) := by + have view : readScopedExpr? resolve locals (RecM.appSpineView source).1 = + some (AExpr.lam condition domain body).erase ∧ + (RecM.appSpineView source).2.map (readScopedExpr? resolve locals ·) = + arguments.map (some ·.erase) := by + induction arguments using list_reverse_induction generalizing source with + | nil => + obtain ⟨_, _, _, _, _, rfl⟩ := readScopedExpr?_lam_inv reading + exact ⟨reading, rfl⟩ + | append_singleton arguments argument ih => + simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil, AExpr.erase] at reading + obtain ⟨fn, arg, info, rfl, fnReads, argReads⟩ := readScopedExpr?_app_inv reading + obtain ⟨headReads, argumentReads⟩ := ih fnReads + exact ⟨headReads, by simp only [RecM.appSpineView, List.map_append, + List.map_cons, List.map_nil, argumentReads, argReads]⟩ + simpa only [(RecM.appSpineView_collectSpine source).1, + (RecM.appSpineView_collectSpine source).2] using view + theorem betaPeel_readScopedExpr? {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {start result : KExpr .anon} {consumed : List (KExpr .anon)} diff --git a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean index a2f996649..ec9168662 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean @@ -4,7 +4,9 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.Formation +import Ix.Kernel.Verify.Consistency.CheapBetaReading import Ix.Theory.Model.UniverseBounds +import Ix.Theory.Model.BetaSpine /-! Inference with formation of the returned type. Local bounds come from the @@ -24,6 +26,8 @@ open Theory Theory.Model universe u v +mutual + /-- Finite support for the actual production calls, enriched with the origins and syntactic levels needed to derive formation. There are no semantic typing or conversion fields. -/ @@ -113,11 +117,160 @@ inductive SynthesisInference {β : Type u} term = KExpr.mkAll () () domain trace.abstracted.1) : SynthesisInference resolve entries locals context bounds (fuel + 1) before (.lam name bi domain body info) (.lam condition A b) (.forallE condition A B) (.imax (readLevel trace.domainLevel) bodyLevel) + | lamBeta {entries locals context bounds fuel before name bi domain body info A b condition + domainBoundLevel bodyLevel headCondition headDomain headBody arguments + earlier typeLocals typeContext typeBounds typeFuel typeBefore typeAfter typeSource typeResult + originCondition originDomain originBody originArguments originLevel originBound reducedLevel} + (full : before.inferOnly = false) + (miss : UncachedInference before (.lam name bi domain body info)) + (trace : LambdaBodyTrace fuel miss.keyed name bi domain body) + (opening : BinderOpeningSupport trace.domainState body) + (absent : (⟨trace.domainState.env.nextFVarId⟩ : FVarId) ∉ locals) + (domainTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed domain + A (.sort (readLevel trace.domainLevel)) domainBoundLevel) + (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) + (readLevel trace.domainLevel :: bounds) fuel trace.openedState trace.opened b + ((AExpr.lam headCondition headDomain headBody).appN arguments) bodyLevel) + (typeContextSupport : SynthesisContext resolve entries context bounds earlier typeContext typeBounds) + (typeTree : SynthesisInference resolve earlier typeLocals typeContext typeBounds typeFuel typeBefore + typeSource ((AExpr.lam originCondition originDomain originBody).appN originArguments) + (.sort originLevel) originBound) + (typeAgreement : LocalContextReading resolve typeLocals typeBefore.lctx typeContext) + (typeReading : readScopedExpr? resolve typeLocals typeSource = + some ((AExpr.lam originCondition originDomain originBody).appN originArguments).erase) + (typeRun : RecM.infer typeSource (methodsN typeFuel) typeBefore = .ok typeResult typeAfter) + (originPrefix : cheapBetaCount trace.bodyType ≤ originBody.lambdaDepth + 1) + (transport : TypeReductionTransport earlier typeContext + ((AExpr.lam originCondition originDomain originBody).appN originArguments) + (AExpr.betaPrefix (cheapBetaCount trace.bodyType) (.lam originCondition originDomain originBody) + originArguments) originLevel entries (context.push A) + ((AExpr.lam headCondition headDomain headBody).appN arguments) + (AExpr.betaPrefix (cheapBetaCount trace.bodyType) (.lam headCondition headDomain headBody) arguments) + reducedLevel) + (reduction : CheapBetaSupport trace.bodyType trace.bodyState.env.intern) + (conditionAgrees : condition = Certified.zeroCondition reducedLevel) + (constructed : trace.reduced.1.Constructed) + (bound : trace.reduced.1.size + 1 < UInt64.size) + (closingFaithful : KExpr.CollisionFree fun term => trace.reduced.2.ExprSupport term ∨ + KExpr.AbstractReach ((∅ : Std.HashMap FVarId UInt64).insert trace.fresh 0) + 1 trace.reduced.1 0 term) + (faithful : KExpr.KeyCollisionFree fun term => trace.abstracted.2.ExprSupport term ∨ + term = KExpr.mkAll () () domain trace.abstracted.1) : + SynthesisInference resolve entries locals context bounds (fuel + 1) before (.lam name bi domain body info) + (.lam condition A b) + (.forallE condition A + (AExpr.betaPrefix (cheapBetaCount trace.bodyType) (.lam headCondition headDomain headBody) arguments)) + (.imax (readLevel trace.domainLevel) reducedLevel) + +/-- Contexts of retained checking origins come from the incoming context, +the empty context, or actual checks of their binder domains. This records +earlier checks even when a generated type is used several binders later. -/ +inductive SynthesisContext {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → + Model.Environment β → Model.Context β → List VLevel → Type u + | current {entries context bounds} : + SynthesisContext resolve entries context bounds entries context bounds + | empty {entries context bounds} (earlier : Model.Environment β) : + SynthesisContext resolve entries context bounds earlier [] [] + | push {entries context bounds earlier priorContext priorBounds locals fuel before after source result + domain level bound} + (prior : SynthesisContext resolve entries context bounds earlier priorContext priorBounds) + (domainTree : SynthesisInference resolve earlier locals priorContext priorBounds fuel before source + domain (.sort level) bound) + (agreement : LocalContextReading resolve locals before.lctx priorContext) + (reading : readScopedExpr? resolve locals source = some domain.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisContext resolve entries context bounds earlier (priorContext.push domain) (level :: priorBounds) + | extend {entries context bounds earlier later priorContext priorBounds} + (prior : SynthesisContext resolve entries context bounds earlier priorContext priorBounds) + (extension : InterfaceExtends earlier later) : + SynthesisContext resolve entries context bounds later priorContext priorBounds + +end + +private theorem list_reverse_induction {α : Type u} {motive : List α → Prop} + (nil : motive []) + (append_singleton : ∀ tail last, motive tail → motive (tail ++ [last])) + (values : List α) : motive values := by + have reversed : ∀ items : List α, motive items.reverse := by + intro items + induction items with + | nil => exact nil + | cons item items ih => + simpa only [List.reverse_cons] using append_singleton items.reverse item ih + simpa using reversed values.reverse + +theorem BinderInference.lambdaPrefix {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source term type) : + LambdaPrefix term type term.lambdaDepth := by + induction support with + | lam _ _ _ _ _ _ _ _ _ _ _ ih => exact .lam ih + | _ => exact .zero _ _ + +theorem SynthesisInference.lambdaPrefix {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) : + LambdaPrefix term type term.lambdaDepth := + match support with + | .known inference _ => inference.lambdaPrefix + | .reuseType inference _ _ _ _ _ => inference.lambdaPrefix + | .lam _ _ _ _ _ _ bodyTree _ _ _ _ _ _ => .lam bodyTree.lambdaPrefix + | .lamBeta _ _ _ _ _ _ bodyTree _ _ _ _ _ _ _ _ _ _ _ _ _ => by + have depth := bodyTree.lambdaPrefix.lambdaDepth_zero + (AExpr.appN_ne_forallE (by intro condition domain body same; cases same) _) + simpa only [AExpr.lambdaDepth, depth] using LambdaPrefix.lam (LambdaPrefix.zero _ _) + | .fvar .. | .app .. | .forallE .. => .zero _ _ +termination_by structural support + +theorem SynthesisHead.appN_head {β : Type u} {head : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisHead (head.appN arguments)) : SynthesisHead head := by + induction arguments generalizing head with + | nil => exact support + | cons argument arguments ih => + have applied := ih support + cases applied with + | app head => exact head + +private theorem BinderInference.no_lambda_spine {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} + {condition : Certified.PropWhen} {domain body type : AExpr β} {arguments : List (AExpr β)} + (support : BinderInference resolve entries locals context fuel before source + ((AExpr.lam condition domain body).appN arguments) type) + (nonempty : arguments ≠ []) : False := by + induction arguments using list_reverse_induction with + | nil => exact nonempty rfl + | append_singleton arguments argument ih => + simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] at support + cases support with + | app _ _ _ _ head => cases head.appN_head + +theorem BinderInference.lambdaSpineTyping {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source term type) + (typed : TypingClaim.{u,v} entries context term type) : + LambdaSpineTyping.{u,v} entries context term type := by + intro condition domain body arguments same + subst term + by_cases empty : arguments = [] + · subst arguments + exact ⟨type, typed, support.lambdaPrefix, .nil _⟩ + · exact False.elim (support.no_lambda_spine empty) + +mutual /-- Simultaneous soundness of the term and formation of its returned type. The context hypothesis is discharged by actual domain inference at each binder, and by the empty context at the production declaration boundary. -/ -theorem SynthesisInference.sound {β : Type u} +theorem SynthesisInference.soundWithSpine {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} @@ -129,82 +282,164 @@ theorem SynthesisInference.sound {β : Type u} (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : readScopedExpr? resolve locals result = some type.erase ∧ TypingClaim.{u,v} entries context term type ∧ - TypingClaim.{u,v} entries context type (.sort level) := by - induction support generalizing result after with - | known inference formation => + TypingClaim.{u,v} entries context type (.sort level) ∧ + LambdaSpineTyping.{u,v} entries context term type := + match support with + | .known inference formation => by obtain ⟨reads, checked⟩ := inference.sound agreement reading accepted - exact ⟨reads, checked.typing formation.sound, formation.sound⟩ - | @reuseType earlier entries locals context bounds fuel before source term type - typeFuel typeBefore typeAfter typeSource typeResult declaredType level typeBound arguments - inference typeTree extension typeReading typeRun same ihType => + have typed := checked.typing formation.sound + exact ⟨reads, typed, formation.sound, inference.lambdaSpineTyping typed⟩ + | .reuseType inference typeTree extension typeReading typeRun same => by obtain ⟨reads, checked⟩ := inference.sound agreement reading accepted - obtain ⟨_, typeTyped, _⟩ := ihType (.empty _) (.empty _ _) typeReading typeRun - have typeFormed : TypingClaim.{u,v} entries context type (.sort (level.inst arguments)) := - same.termTyping (typing_instL_closed (extension.typing typeTyped) _) - exact ⟨reads, checked.typing typeFormed, typeFormed⟩ - | fvar inference atIndex boundAtIndex => + obtain ⟨_, typeTyped, _, _⟩ := + typeTree.soundWithSpine (.empty _) (.empty _ _) typeReading typeRun + have typeFormed := same.termTyping + (typing_instL_closed (context := context) (extension.typing typeTyped) _) + have typed := checked.typing typeFormed + exact ⟨reads, typed, typeFormed, inference.lambdaSpineTyping typed⟩ + | .fvar inference atIndex boundAtIndex => by obtain ⟨reads, typed⟩ := inference.synthesis (.bvar _) agreement reading accepted - exact ⟨reads, typed, formed _ _ _ atIndex boundAtIndex⟩ - | @app entries locals context bounds fuel before fn arg info f a A A' B condition functionLevel argumentLevel - full miss trace functionTree argumentTree conditions hashPath comparisonFaithful - bodyConstructed argConstructed bodyBound argBound coherent faithful ihFunction ihArgument => + exact ⟨reads, typed, formed _ _ _ atIndex boundAtIndex, + LambdaSpineTyping.non_application (by intro fn arg same; cases same) + (by intro condition domain body same; cases same)⟩ + | .app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful + bodyConstructed argConstructed bodyBound argBound coherent faithful => by obtain ⟨state, run⟩ := infer_uncached_success miss accepted rw [full] at run obtain ⟨fnReads, argReads⟩ := readScopedExpr?_app_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement - obtain ⟨functionTypeReads, functionTyped, functionFormed⟩ := - ihFunction formed keyedAgreement fnReads trace.functionRun + obtain ⟨functionTypeReads, functionTyped, functionFormed, functionSpine⟩ := + functionTree.soundWithSpine formed keyedAgreement fnReads trace.functionRun obtain ⟨domainReads, codomainReads⟩ := readScopedExpr?_all_parts functionTypeReads have argumentAgreement := trace.contextPreserved.symm ▸ keyedAgreement - obtain ⟨argumentTypeReads, argumentTyped, _⟩ := - ihArgument formed argumentAgreement argReads trace.argumentRun + obtain ⟨argumentTypeReads, argumentTyped, _, _⟩ := + argumentTree.soundWithSpine formed argumentAgreement argReads trace.argumentRun have sameReading := beq_readScopedExpr? (resolve := resolve) (locals := locals) (depth := 0) comparisonFaithful hashPath have sameType := AExpr.eq_of_erase_annotations (Option.some.inj (argumentTypeReads.symm.trans (sameReading.trans domainReads))) conditions have checked := sameType ▸ argumentTyped.checking refine ⟨?_, functionTyped.appChecking checked, - functionTyped.applicationType functionFormed checked⟩ + functionTyped.applicationType functionFormed checked, + functionSpine.app (sameType ▸ argumentTyped)⟩ rw [trace.output run, AExpr.erase_inst] exact (subst_readScopedExpr? bodyConstructed argConstructed bodyBound argBound coherent faithful codomainReads argReads).1 - | forallE miss trace opening absent domainTree bodyTree levelFaithful domainBound bodyBound - coherent faithful ihDomain ihBody => + | .forallE miss trace opening absent domainTree bodyTree levelFaithful domainBound bodyBound + coherent faithful => by obtain ⟨state, run⟩ := infer_uncached_success miss accepted obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement - obtain ⟨_, domainTyped, _⟩ := ihDomain formed keyedAgreement domainReads trace.domainRun + obtain ⟨_, domainTyped, _, _⟩ := + domainTree.soundWithSpine formed keyedAgreement domainReads trace.domainRun have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement obtain ⟨_, openedReads, openedAgreement, _⟩ := openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun - obtain ⟨_, bodyTyped, _⟩ := - ihBody (formed.push domainTyped) openedAgreement openedReads trace.bodyRun - refine ⟨?_, ?_, TypingClaim.sort _⟩ + obtain ⟨_, bodyTyped, _, _⟩ := + bodyTree.soundWithSpine (formed.push domainTyped) openedAgreement openedReads trace.bodyRun + refine ⟨?_, ?_, TypingClaim.sort _, + LambdaSpineTyping.non_application (by intro fn arg same; cases same) + (by intro condition domain body same; cases same)⟩ · rw [trace.output run, internExpr_readScopedExpr? coherent faithful] rfl · exact AExpr.LevelEquivalent.sort (Theory.VLevel.equiv_def.mpr fun levels => (Theory.VLevel.equiv_def.mp (readLevel_mkIMax levelFaithful domainBound bodyBound) levels).symm) |>.typing (TypingClaim.forallE domainTyped bodyTyped rfl) - | lam full miss trace opening absent domainTree bodyTree conditionAgrees constructed bound coherent - closingFaithful faithful ihDomain ihBody => + | .lam full miss trace opening absent domainTree bodyTree conditionAgrees constructed bound coherent + closingFaithful faithful => by obtain ⟨state, run⟩ := infer_uncached_success miss accepted rw [full] at run obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement - obtain ⟨_, domainTyped, _⟩ := ihDomain formed keyedAgreement domainReads trace.domainRun + obtain ⟨_, domainTyped, _, _⟩ := + domainTree.soundWithSpine formed keyedAgreement domainReads trace.domainRun have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement obtain ⟨_, openedReads, openedAgreement, _⟩ := openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun - obtain ⟨bodyTypeReads, bodyTyped, bodyFormed⟩ := - ihBody (formed.push domainTyped) openedAgreement openedReads trace.bodyRun + obtain ⟨bodyTypeReads, bodyTyped, bodyFormed, _⟩ := + bodyTree.soundWithSpine (formed.push domainTyped) openedAgreement openedReads trace.bodyRun obtain ⟨closedReads, closedCoherent⟩ := abstractFVars_readScopedExpr? constructed bound coherent closingFaithful bodyTypeReads - refine ⟨?_, TypingClaim.lam domainTyped bodyFormed bodyTyped conditionAgrees, - TypingClaim.forallE domainTyped bodyFormed conditionAgrees⟩ + have typed := TypingClaim.lam domainTyped bodyFormed bodyTyped conditionAgrees + refine ⟨?_, typed, TypingClaim.forallE domainTyped bodyFormed conditionAgrees, + LambdaSpineTyping.lam typed (.lam bodyTree.lambdaPrefix)⟩ rw [trace.output run, internExpr_readScopedExpr? (table := trace.abstracted.2) closedCoherent faithful] simp [LambdaInferenceTrace.abstracted, domainReads, closedReads, AExpr.erase] + | .lamBeta full miss trace opening absent domainTree bodyTree typeContextSupport typeTree + typeAgreement typeReading typeRun originPrefix transport reduction conditionAgrees + constructed bound closingFaithful faithful => by + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + rw [full] at run + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨_, domainTyped, _, _⟩ := + domainTree.soundWithSpine formed keyedAgreement domainReads trace.domainRun + have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + obtain ⟨_, openedReads, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun + obtain ⟨bodyTypeReads, bodyTyped, _, _⟩ := + bodyTree.soundWithSpine (formed.push domainTyped) openedAgreement openedReads trace.bodyRun + obtain ⟨_, _, _, originSpine⟩ := typeTree.soundWithSpine + (typeContextSupport.sound formed) typeAgreement typeReading typeRun + obtain ⟨originConversion, originReducedTyped⟩ := originSpine.betaPrefix originPrefix + obtain ⟨conversion, reducedTyped⟩ := transport.sound originConversion originReducedTyped + obtain ⟨_, reducedReads, reducedCoherent⟩ := reduction.reading bodyTypeReads + obtain ⟨closedReads, closedCoherent⟩ := abstractFVars_readScopedExpr? constructed bound + reducedCoherent closingFaithful reducedReads + have typed := TypingClaim.lam domainTyped reducedTyped + (bodyTyped.conv reducedTyped conversion) conditionAgrees + have bodyDepth := bodyTree.lambdaPrefix.lambdaDepth_zero + (AExpr.appN_ne_forallE (by intro condition domain body same; cases same) _) + refine ⟨?_, typed, TypingClaim.forallE domainTyped reducedTyped conditionAgrees, + LambdaSpineTyping.lam typed ?_⟩ + · rw [trace.output run, + internExpr_readScopedExpr? (table := trace.abstracted.2) closedCoherent faithful] + simp [LambdaBodyTrace.abstracted, LambdaBodyTrace.reduced, domainReads, + AExpr.erase] at ⊢ closedReads + exact closedReads + · simpa only [bodyDepth] using LambdaPrefix.lam (LambdaPrefix.zero _ _) +termination_by structural support + +theorem SynthesisContext.sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries earlier : Model.Environment β} {context priorContext : Model.Context β} + {bounds priorBounds : List VLevel} + (support : SynthesisContext resolve entries context bounds earlier priorContext priorBounds) + (formed : ContextFormation.{u,v} entries context bounds) : + ContextFormation.{u,v} earlier priorContext priorBounds := + match support with + | .current => formed + | .empty earlier => .empty earlier + | .push prior domainTree agreement reading accepted => by + have priorFormation := prior.sound formed + obtain ⟨_, domainTyped, _, _⟩ := + domainTree.soundWithSpine priorFormation agreement reading accepted + exact priorFormation.push domainTyped + | .extend prior extension => by + intro index type bound found indexed + exact extension.typing (prior.sound formed index type bound found indexed) +termination_by structural support + +end + +/-- The public inference result projects ordinary typing and formation from +the stronger induction that also retains checked lambda domains. -/ +theorem SynthesisInference.sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + readScopedExpr? resolve locals result = some type.erase ∧ + TypingClaim.{u,v} entries context term type ∧ + TypingClaim.{u,v} entries context type (.sort level) := by + obtain ⟨reads, typed, formation, _⟩ := support.soundWithSpine formed agreement reading accepted + exact ⟨reads, typed, formation⟩ /-- Closed production inference supplies its own formation facts. No local or whole-term semantic typing premise remains at this boundary. -/ diff --git a/Ix/Theory/Model/BetaSpine.lean b/Ix/Theory/Model/BetaSpine.lean index 792f113d9..d0a75524d 100644 --- a/Ix/Theory/Model/BetaSpine.lean +++ b/Ix/Theory/Model/BetaSpine.lean @@ -36,6 +36,50 @@ def betaPrefix : Nat → AExpr β → List (AExpr β) → AExpr β betaPrefix count (body.inst argument) arguments | _ + 1, head, arguments => head.appN arguments +@[simp] theorem lambdaDepth_liftN (term : AExpr β) (count cutoff : Nat) : + (term.liftN count cutoff).lambdaDepth = term.lambdaDepth := by + induction term generalizing cutoff <;> simp_all [liftN, lambdaDepth] + +@[simp] theorem lambdaDepth_instL (term : AExpr β) (levels : List VLevel) : + (term.instL levels).lambdaDepth = term.lambdaDepth := by + induction term <;> simp_all [instL, lambdaDepth] + +theorem liftN_appN (head : AExpr β) (arguments : List (AExpr β)) (count cutoff : Nat) : + (head.appN arguments).liftN count cutoff = + (head.liftN count cutoff).appN (arguments.map (liftN count · cutoff)) := by + induction arguments generalizing head with + | nil => rfl + | cons argument arguments ih => simpa only [appN_cons, liftN, List.map_cons] using ih (head.app argument) + +theorem instL_appN (head : AExpr β) (arguments : List (AExpr β)) (levels : List VLevel) : + (head.appN arguments).instL levels = + (head.instL levels).appN (arguments.map (instL levels)) := by + induction arguments generalizing head with + | nil => rfl + | cons argument arguments ih => simpa only [appN_cons, instL, List.map_cons] using ih (head.app argument) + +theorem liftN_betaPrefix (count : Nat) (head : AExpr β) (arguments : List (AExpr β)) + (inserted cutoff : Nat) : + (betaPrefix count head arguments).liftN inserted cutoff = + betaPrefix count (head.liftN inserted cutoff) (arguments.map (liftN inserted · cutoff)) := by + induction count generalizing head arguments with + | zero => exact liftN_appN _ _ _ _ + | succ count ih => + cases head <;> cases arguments <;> + simp only [betaPrefix, liftN, List.map_nil, List.map_cons, appN_nil, liftN_appN, + ih, liftN_inst_zero] + +theorem instL_betaPrefix (count : Nat) (head : AExpr β) (arguments : List (AExpr β)) + (levels : List VLevel) : + (betaPrefix count head arguments).instL levels = + betaPrefix count (head.instL levels) (arguments.map (instL levels)) := by + induction count generalizing head arguments with + | zero => exact instL_appN _ _ _ + | succ count ih => + cases head <;> cases arguments <;> + simp only [betaPrefix, instL, List.map_nil, List.map_cons, appN_nil, instL_appN, + ih, instL_inst] + end AExpr /-- Each leading lambda agrees with the corresponding inferred Pi. @@ -68,6 +112,25 @@ theorem LambdaPrefix.truncate {β : Type u} {term type : AExpr β} {total count | zero => exact .zero _ _ | succ count => exact .lam (ih (by omega)) +theorem AExpr.appN_ne_forallE {β : Type u} {head : AExpr β} + (notPi : ∀ condition domain body, head ≠ .forallE condition domain body) + (arguments : List (AExpr β)) : + ∀ condition domain body, head.appN arguments ≠ .forallE condition domain body := by + induction arguments generalizing head with + | nil => exact notPi + | cons argument arguments ih => + exact ih (by intro condition domain body same; cases same) + +theorem LambdaPrefix.lambdaDepth_zero {β : Type u} {term type : AExpr β} + (leading : LambdaPrefix term type term.lambdaDepth) + (notPi : ∀ condition domain body, type ≠ .forallE condition domain body) : + term.lambdaDepth = 0 := by + cases term with + | lam condition domain body => + cases leading with + | lam => exact False.elim (notPi _ _ _ rfl) + | _ => rfl + /-- The original body exposed by removing a syntactic lambda prefix. -/ inductive LambdaPeel {β : Type u} : AExpr β → Nat → AExpr β → Prop | zero (term : AExpr β) : LambdaPeel term 0 term @@ -176,4 +239,84 @@ theorem LambdaPrefix.beta_sound {β : Type u} {entries : Environment β} {contex exact ⟨((ConversionClaim.beta typed argumentTyped).appN _).trans conversion, resultTyped⟩ +/-- Source inference retains the typing of every original lambda-headed +spine. This internal result carries the domains needed by later reduction; +ordinary semantic typing alone cannot recover them in the proof regime. -/ +def LambdaSpineTyping {β : Type u} (entries : Environment β) (context : Context β) + (term type : AExpr β) : Prop := + ∀ (condition : PropWhen) (domain body : AExpr β) (arguments : List (AExpr β)), + term = (AExpr.lam condition domain body).appN arguments → + ∃ headType, TypingClaim.{u,v} entries context (.lam condition domain body) headType ∧ + LambdaPrefix (.lam condition domain body) headType (body.lambdaDepth + 1) ∧ + ArgumentSpine.{u,v} entries context headType arguments type + +private theorem list_reverse_induction {α : Type u} {motive : List α → Prop} + (nil : motive []) + (append_singleton : ∀ tail last, motive tail → motive (tail ++ [last])) + (values : List α) : motive values := by + have reversed : ∀ items : List α, motive items.reverse := by + intro items + induction items with + | nil => exact nil + | cons item items ih => + simpa only [List.reverse_cons] using append_singleton items.reverse item ih + simpa using reversed values.reverse + +namespace LambdaSpineTyping + +theorem non_application {β : Type u} {entries : Environment β} {context : Context β} + {term type : AExpr β} + (notApp : ∀ fn arg, term ≠ .app fn arg) + (notLam : ∀ condition domain body, term ≠ .lam condition domain body) : + LambdaSpineTyping.{u,v} entries context term type := by + intro condition domain body arguments same + induction arguments using list_reverse_induction with + | nil => exact False.elim (notLam _ _ _ same) + | append_singleton arguments argument ih => + exact False.elim (notApp _ _ + (by simpa only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] using same)) + +theorem lam {β : Type u} {entries : Environment β} {context : Context β} + {condition : PropWhen} {domain body type : AExpr β} + (typed : TypingClaim.{u,v} entries context (.lam condition domain body) type) + (leading : LambdaPrefix (.lam condition domain body) type (body.lambdaDepth + 1)) : + LambdaSpineTyping.{u,v} entries context (.lam condition domain body) type := by + intro otherCondition otherDomain otherBody arguments same + induction arguments using list_reverse_induction with + | nil => + cases same + exact ⟨type, typed, leading, .nil _⟩ + | append_singleton arguments argument ih => + simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] at same + cases same + +theorem app {β : Type u} {entries : Environment β} {context : Context β} + {fn arg domain body : AExpr β} {condition : PropWhen} + (function : LambdaSpineTyping.{u,v} entries context fn (.forallE condition domain body)) + (argument : TypingClaim.{u,v} entries context arg domain) : + LambdaSpineTyping.{u,v} entries context (.app fn arg) (body.inst arg) := by + intro headCondition headDomain headBody arguments same + induction arguments using list_reverse_induction with + | nil => cases same + | append_singleton arguments last ih => + simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil, AExpr.app.injEq] at same + obtain ⟨headType, headTyped, leading, spine⟩ := function _ _ _ arguments same.1 + refine ⟨headType, headTyped, leading, ?_⟩ + rw [← same.2] + exact spine.append (.cons argument (.nil _)) + +theorem betaPrefix {β : Type u} {entries : Environment β} {context : Context β} + {condition : PropWhen} {domain body type : AExpr β} {arguments : List (AExpr β)} {count : Nat} + (source : LambdaSpineTyping.{u,v} entries context + ((AExpr.lam condition domain body).appN arguments) type) + (enough : count ≤ body.lambdaDepth + 1) : + ConversionClaim.{u,v} entries context ((AExpr.lam condition domain body).appN arguments) + (AExpr.betaPrefix count (.lam condition domain body) arguments) ∧ + TypingClaim.{u,v} entries context + (AExpr.betaPrefix count (.lam condition domain body) arguments) type := by + obtain ⟨_, typed, leading, spine⟩ := source _ _ _ _ rfl + exact (leading.truncate enough).beta_sound typed spine + +end LambdaSpineTyping + end Ix.Theory.Model diff --git a/Ix/Theory/Model/BetaSubstitution.lean b/Ix/Theory/Model/BetaSubstitution.lean index 43e6eb4bd..9a6624381 100644 --- a/Ix/Theory/Model/BetaSubstitution.lean +++ b/Ix/Theory/Model/BetaSubstitution.lean @@ -12,6 +12,72 @@ namespace Ix.Theory.Model.AExpr universe u +theorem instL_liftN (term : AExpr β) (levels : List VLevel) (count cutoff : Nat) : + (term.liftN count cutoff).instL levels = (term.instL levels).liftN count cutoff := by + induction term generalizing cutoff <;> simp_all [liftN, instL] + +theorem instL_inst (term argument : AExpr β) (levels : List VLevel) (cutoff : Nat := 0) : + (term.inst argument cutoff).instL levels = + (term.instL levels).inst (argument.instL levels) cutoff := by + induction term generalizing cutoff with + | bvar index => + by_cases below : index < cutoff + · simp [inst, instL, instVar, below] + · by_cases equal : index = cutoff <;> + simp [inst, instL, instVar, below, equal, instL_liftN] + | _ => simp_all [inst, instL] + +/-- Insert disjoint groups of variables in either order, adjusting the +later cutoff by the variables inserted before it. -/ +theorem liftN_liftN_comm (term : AExpr β) (count first second cutoff : Nat) + (ordered : cutoff ≤ first) : + (term.liftN count first).liftN second cutoff = + (term.liftN second cutoff).liftN count (second + first) := by + induction term generalizing first cutoff with + | bvar index => + by_cases below : index < cutoff + · simp [liftN, liftVar, below, show index < first by omega, + show index < second + first by omega] + · by_cases before : index < first + · simp [liftN, liftVar, below, before] + · simp [liftN, liftVar, below, before, show ¬ count + index < cutoff by omega, + Nat.add_left_comm] + | lam condition domain body ihDomain ihBody | forallE condition domain body ihDomain ihBody => + simp only [liftN, ihDomain first cutoff ordered, + ihBody (first + 1) (cutoff + 1) (by omega), Nat.add_assoc] + | _ => simp_all [liftN] + +/-- Weakening commutes with substitution at an earlier variable. -/ +theorem liftN_inst (term argument : AExpr β) (count cutoff depth : Nat) : + (term.inst argument depth).liftN count (cutoff + depth) = + (term.liftN count (cutoff + depth + 1)).inst (argument.liftN count cutoff) depth := by + induction term generalizing depth with + | bvar index => + by_cases below : index < depth + · simp [inst, instVar, liftN, liftVar, below, + show index < cutoff + depth by omega, show index < cutoff + depth + 1 by omega] + · by_cases equal : index = depth + · subst index + simp only [inst, instVar, Nat.lt_irrefl, if_false, if_true, liftN, + liftVar, if_pos (show depth < cutoff + depth + 1 by omega)] + simpa only [Nat.add_zero, Nat.add_comm] using + (liftN_liftN_comm argument count cutoff depth 0 (Nat.zero_le _)).symm + · by_cases before : index < cutoff + depth + 1 + · simp [inst, instVar, liftN, liftVar, below, equal, before, + show index - 1 < cutoff + depth by omega] + · simp [inst, instVar, liftN, liftVar, below, equal, before, + show ¬ index - 1 < cutoff + depth by omega, + show ¬ count + index < depth by omega, show count + index ≠ depth by omega] + omega + | lam condition domain body ihDomain ihBody | forallE condition domain body ihDomain ihBody => + simp only [inst, liftN, ihDomain, ihBody, Nat.add_assoc] + | _ => simp_all [inst, liftN] + +theorem liftN_inst_zero (term argument : AExpr β) (count cutoff : Nat) : + (term.inst argument).liftN count cutoff = + (term.liftN count (cutoff + 1)).inst (argument.liftN count cutoff) := by + simpa only [Nat.add_zero] using liftN_inst term argument count cutoff 0 + theorem inst_liftN_top (term argument : AExpr β) (count cutoff : Nat) : (term.liftN (count + 1) cutoff).inst argument (count + cutoff) = term.liftN count cutoff := by induction term generalizing cutoff with diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 84aed0520..d3fa3dbda 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -608,6 +608,80 @@ private def multiBetaDeclaredSuffix (level : Ixon.Univ) : Ixon.Env := Id.run do .app (.app (.leanLam functionType (.var 0)) (.ref 0 #[])) (.ref 1 #[]), .ref 2 #[]⟩, #[], #[family, carrier, witness], #[level]⟩).1 +/-- The lambda body returns a local whose declared type is a beta redex. +Observe the changed synthesized codomain, including reuse beneath another +binder and inside both recursive application positions. -/ +private def cheapLambdaLocalResult (shape : Nat) (level : KUniv .anon := .mkZero) : Bool := + let sort := KExpr.mkSort (m := .anon) level + let action : RecM .anon Bool := RecM.withLctxScope do + let (carrier, _) ← TcM.openBinder () () sort (.mkVar 0 ()) + let (witness, _) ← TcM.openBinder () () carrier (.mkVar 0 ()) + let head := if shape == 1 then + KExpr.mkLam () () sort (.mkLam () () (.mkVar 0 ()) (.mkVar 1 ())) + else KExpr.mkLam () () sort (if shape == 2 then carrier else .mkVar 0 ()) + let sourceType := KExpr.mkAppN head (if shape == 1 then #[carrier, witness] else #[carrier]) + let sourceSort ← RecM.inferCall sourceType + let lambda := KExpr.mkLam () () sourceType (.mkVar 0 ()) + let expectedLambda := KExpr.mkAll () () sourceType carrier + let term ← if shape == 3 then + pure (KExpr.mkLam () () carrier lambda) + else if shape == 4 then do + let (argument, _) ← TcM.openBinder () () sourceType (.mkVar 0 ()) + pure (KExpr.mkApp lambda argument) + else if shape == 5 then do + let consumerType := KExpr.mkAll () () expectedLambda carrier + let _ ← RecM.inferCall consumerType + let (consumer, _) ← TcM.openBinder () () consumerType (.mkVar 0 ()) + pure (KExpr.mkApp consumer lambda) + else pure lambda + let expected := if shape == 3 then KExpr.mkAll () () carrier expectedLambda + else if shape == 4 || shape == 5 then carrier else expectedLambda + let before ← get + let inferred ← RecM.inferCall term + let repeated ← RecM.inferCall term + let after ← get + let reduced ← TcM.runIntern (cheapBetaReduce sourceType) + return sourceSort == sort && (cheapBetaPlan? sourceType).isSome && sourceType != reduced && + reduced == carrier && inferred == expected && repeated == expected && after.lctx.size == before.lctx.size + match TcM.runRec action (TcState.ofEnvAnon {}) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +/-- An earlier declaration retains a beta-redex type. Returning its value +from a new lambda reduces that type during inference, before abstraction. -/ +private def cheapLambdaConstant (level : Ixon.Univ) (universes : UInt64 := 0) + (multiple wrongValue : Bool := false) : Ixon.Env × Address := Id.run do + let (env, carrier) := storeConst {} + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], #[level]⟩ + let arguments := if universes == 0 then #[] else #[0] + let (env, witness) := storeConst env + ⟨.axio ⟨false, universes, .ref 0 arguments⟩, #[], #[carrier], #[level]⟩ + let type := if multiple then + Ixon.Expr.app (.app (.leanLam (.sort 0) (.leanLam (.var 0) (.var 1))) (.ref 0 arguments)) + (.ref 1 arguments) + else .app (.leanLam (.sort 0) (.var 0)) (.ref 0 arguments) + let (env, value) := storeConst env + ⟨.axio ⟨false, universes, type⟩, #[], #[carrier, witness], #[level]⟩ + return storeConst env + ⟨.defn ⟨.defn, .safe, universes, + .leanAll (.ref 0 arguments) (.ref 0 arguments), + .leanLam (.ref 0 arguments) (.ref (if wrongValue then 0 else 1) arguments)⟩, + #[], #[carrier, value], #[level]⟩ + +private def cheapLambdaConstantResult : Bool := + let (env, target) := cheapLambdaConstant .zero 0 true + let action : RecM .anon Bool := do + let concrete ← TcM.getConst (m := .anon) ⟨target, ()⟩ + let .defn _ _ _ _ _ _ type value _ _ := concrete | return false + let .lam _ _ domain body _ := value | return false + let original ← RecM.inferCall body + let inferred ← RecM.inferCall value + let reduced ← TcM.runIntern (cheapBetaReduce original) + return (cheapBetaPlan? original).isSome && original != reduced && reduced == domain && inferred == type + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + private def applicationCases : TestSeq := test "application environment: Prop/Type identity calls and transitive theorem calls check" (allSucceeded applicationEnvironment 5 { clearEvery := 0 }) @@ -687,6 +761,34 @@ private def multiBetaCases : TestSeq := ++ test "multi beta admission: selecting a witness of the other carrier is rejected" (let (env, target) := multiBetaDeclaredType .zero 0 false true; rowFailed env target) +private def cheapLambdaCases : TestSeq := + test "lambda cheap beta: a checked local type reduces in Prop and Type" + (cheapLambdaLocalResult 0 && cheapLambdaLocalResult 0 (.mkSucc .mkZero)) + ++ test "lambda cheap beta: a dependent two-lambda type reduces before abstraction" + (cheapLambdaLocalResult 1) + ++ test "lambda cheap beta: a closed body retains the earlier local" + (cheapLambdaLocalResult 2) + ++ test "lambda cheap beta: a retained local type crosses another binder" + (cheapLambdaLocalResult 3) + ++ test "lambda cheap beta: the changed lambda type is used in function position" + (cheapLambdaLocalResult 4) + ++ test "lambda cheap beta: the changed lambda type is used in argument position" + (cheapLambdaLocalResult 5) + ++ test "lambda cheap beta admission: earlier declaration types reduce in Prop and Type" + (allSucceeded (cheapLambdaConstant .zero).1 4 && + allSucceeded (cheapLambdaConstant (.succ .zero)).1 4) + ++ test "lambda cheap beta admission: retained declaration checks support universe parameters" + (allSucceeded (cheapLambdaConstant (.var 0) 1).1 4) + ++ test "lambda cheap beta admission: earlier checks retain dependent argument domains" + (allSucceeded (cheapLambdaConstant .zero 0 true).1 4 && + allSucceeded (cheapLambdaConstant (.var 0) 1 true).1 4) + ++ test "lambda cheap beta admission: reduction survives per-item cache clearing" + (allSucceeded (cheapLambdaConstant .zero 0 true).1 4 { clearEvery := 1 }) + ++ test "lambda cheap beta inference: the original and reduced body types have different hashes" + cheapLambdaConstantResult + ++ test "lambda cheap beta admission: returning a carrier in place of its witness is rejected" + (let (env, target) := cheapLambdaConstant .zero 0 true true; rowFailed env target) + /-- Call a polymorphic identity from a monomorphic function body. Universe indices select entries in the declaration's explicit level table. -/ private def storePolymorphicCall (env : Ixon.Env) (identity : Address) @@ -2278,7 +2380,7 @@ private def polymorphicDefinitionCases : TestSeq := | _ => false : Bool) public def suite : List TestSeq := - [cases, polymorphicCases, specializationCases, binderCases, applicationCases, multiBetaCases, + [cases, polymorphicCases, specializationCases, binderCases, applicationCases, multiBetaCases, cheapLambdaCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, sourceAgreementCases, sourceCacheCases, polymorphicDefinitionCases] diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 977746f04..8a492ac37 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -364,7 +364,14 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) checks retain their source, states, successful run, and inference tree; interface extension preserves their entries exactly. These are execution resources, not semantic formation assumptions. The type checks can themselves - contain direct lambda applications. + contain direct lambda applications. The `lamBeta` rule retains an actual + type-check tree when cheap beta changes the body's inferred type. Its source + and reduced syntax move together through interface extension, weakening, + universe instantiation, and structural level congruence. Earlier local + contexts are reconstructed from actual binder-domain checks. The reader + follows the selected plan through substitution and interning, and lambda + abstraction uses that reduction's output table. No new check of the + generated result is assumed. - Applications use full mode, syntactic Pi exposure, an ordinary argument without an eager-reduction marker, and the hash-equality conversion path. Their witnesses retain the actual recursive calls, context preservation @@ -378,7 +385,8 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) by a pure `readInstantiatedType?` check with the substituted occurrence annotations, including when levels simplify. Monomorphic references retain their simpler empty-substitution rule. Reduction to expose a Pi, eager - arguments, and changed cheap-beta paths require further refinement. + arguments, and automatic checking origins for arbitrary generated types + require further refinement. - `DefinitionCheckSupport` permits the existing initial hash-equality path, with faithfulness of the compared expressions, or a beta-reducible declared type. For the beta case, the declaration's own type-inference tree and run @@ -476,7 +484,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 470 exact theorem boundaries. The production +The consistency target checks 497 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -505,10 +513,18 @@ original body and argument trees rather than generated substitution trees. to this meaning when an actual source check is available. `DefinitionCheckSupport.betaDeclaredSpine` uses the declaration's own type check to justify conversion through such a prefix and suffix, and therefore -reaches the environment model-extension and no-False roots. The checking -origins of generated types still need to be tracked through changed cheap -beta in lambda inference. Arbitrary generated redexes, repeated reduction, -and the other conversion paths remain open. +reaches the environment model-extension and no-False roots. +`SynthesisInference.soundWithSpine` retains checked lambda domains in the +same recursion that proves typing and type formation. Its `lamBeta` case +uses an original executed type check, transported to the generated type's +current context, to justify the changed cheap-beta result. The actual source +reading determines the raw head and arguments; `CheapBetaSupport.reading` +connects the selected prefix to the reduced syntax and final intern table. +`SynthesisContext.sound` derives earlier contexts from their domain checks, +so the origin can precede additional binders. The public inference and +environment results include this case. Automatic origin construction for +arbitrary generated types, repeated reduction, and other conversion paths +remain open. Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, argument order, and rejection of wrong arities and out-of-range parameters. @@ -535,6 +551,11 @@ both cheap-beta fast paths and check that reduction preserves the local context and fresh-variable counter. Declaration cases cover universe parameters, cache clearing, a remaining family application, and rejection of a witness belonging to the other carrier. +Lambda cheap-beta regressions observe different original and reduced body +types, local checking origins beneath further binders, dependent prefixes, +both application positions, and reused declaration types in Prop, Type, and +at universe parameters. They also check cache clearing, scope cleanup, and +rejection of a carrier returned in place of its witness. Polymorphic-call regressions include Prop/Type instances in real function bodies, `max`/`imax` simplification inside Pi domains, closed nested references under active locals, separate cache keys for different universe instances, @@ -604,7 +625,7 @@ Definition-cycle regressions use content-addressed standalone and mutual declarations, including a self-justifying theorem, a two-member cycle, type cycles, lets, shared syntax, and binders. They check repeated member failures, acyclic forward references, cache clearing, and the partial/unsafe policy. -The unit suite contains 608 checks. The anonymous differential additionally +The unit suite contains 620 checks. The anonymous differential additionally serializes eight cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. @@ -647,6 +668,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Source ownership, block registration, and finite preflight | [`Consistency/BlockOwnership.lean`](../Ix/Kernel/Verify/Consistency/BlockOwnership.lean), [`Consistency/SourceOwnershipCheck.lean`](../Ix/Kernel/Verify/Consistency/SourceOwnershipCheck.lean), [`SourceOwnership.lean`](../Ix/Kernel/SourceOwnership.lean) | | Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`Application.lean`](../Ix/Kernel/Verify/Consistency/Application.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | | Inferred type formation and direct lambda applications | [`Consistency/SynthesisInference.lean`](../Ix/Kernel/Verify/Consistency/SynthesisInference.lean), [`Formation.lean`](../Ix/Kernel/Verify/Consistency/Formation.lean), [`Model/UniverseBounds.lean`](../Ix/Theory/Model/UniverseBounds.lean) | +| Retained type checks and changed cheap beta in lambda inference | [`Consistency/SynthesisInference.lean`](../Ix/Kernel/Verify/Consistency/SynthesisInference.lean), [`CheapBetaReading.lean`](../Ix/Kernel/Verify/Consistency/CheapBetaReading.lean), [`Formation.lean`](../Ix/Kernel/Verify/Consistency/Formation.lean) | | Source beta reduction and declaration conversion | [`Consistency/BetaSpine.lean`](../Ix/Kernel/Verify/Consistency/BetaSpine.lean), [`Simultaneous.lean`](../Ix/Kernel/Verify/Consistency/Simultaneous.lean), [`SpineReading.lean`](../Ix/Kernel/Verify/Consistency/SpineReading.lean), [`CheapBeta.lean`](../Ix/Kernel/Verify/Consistency/CheapBeta.lean), [`Model/BetaSpine.lean`](../Ix/Theory/Model/BetaSpine.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | | Foundation assumptions, theorem contracts, and provenance | [Consistency model guide](theory.md) | diff --git a/docs/theory.md b/docs/theory.md index 39fd98046..c5eb200e4 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -56,9 +56,15 @@ when an actual source check is available. Declaration admission includes declared types reduced through a lambda prefix and a remaining suffix: the declaration's own executed type check justifies conversion from that result to the original type. The environment -model and no-False theorems include this case. Checking origins still need to -be tracked through changed cheap beta in lambda inference; general reduction -and conversion remain open. +model and no-False theorems include this case. Recursive lambda inference +also supports changed cheap beta when the generated body type retains its +actual checking origin. The inference proof preserves checked lambda domains; +the raw reader derives the selected prefix and reduced expression. A syntactic +transport moves the original check through interface extension, local +weakening, and universe instantiation, and earlier local contexts come from +their executed domain checks. The lambda closes the reduced type using the +actual final intern table. Automatic origin construction for arbitrary +generated types, general reduction, and conversion remain open. Safe definition admission also rejects circular justification in both Lean and Rust, including `theorem loop : P := loop` with only `P : Prop` assumed. The production dependency walk returns an order with a proved decreasing rank; From e734650268d2dc28825485293f80a4abd2e60680 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 03:38:19 -0400 Subject: [PATCH 27/63] Preserve checked type origins through dependent substitution --- Ix/Kernel/Verify/Consistency.lean | 7 +- Ix/Kernel/Verify/Consistency/Audit.lean | 33 +- Ix/Kernel/Verify/Consistency/Formation.lean | 64 +++- .../Consistency/SynthesisInference.lean | 353 +++++++++++++++++- Ix/Theory/Model/BetaSpine.lean | 34 ++ Ix/Theory/Model/BetaSubstitution.lean | 96 +++++ Ix/Theory/Model/Substitution.lean | 148 ++++++-- Tests/Ix/Kernel/Consistency.lean | 87 +++++ docs/kernel-verification.md | 37 +- docs/theory.md | 15 +- 10 files changed, 823 insertions(+), 51 deletions(-) diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 1289a04ae..1b4e32769 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -91,7 +91,12 @@ preserves checked lambda domains during the same inference recursion, reads the selected prefix from the actual generated type, and transports the original check through interface growth, local weakening, and universe instantiation. Earlier local contexts are reconstructed from their executed -domain checks. Abstraction uses the reduced type and the reduction's final +domain checks. Codomain origins are extracted from earlier function-type +trees, and actual function/argument calls justify term substitution through +them. The context relation updates later dependent parameters at any cutoff; +the original beta prefix is preserved by the same substitution. These +transports are proved sound in the inference recursion and consumed by the +lambda case. Abstraction uses the reduced type and the reduction's final intern table. Automatic origin construction for arbitrary generated types and general reduction remains open. Local cache hits agree with the actual declaration type. Constant diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index b7746d32a..3079f4798 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -356,6 +356,37 @@ private def typeOriginRoots : Array RootAllowance := #[ { root := ``Theory.Model.LambdaSpineTyping.betaPrefix, standardAxioms := standard } ] +private def substitutedOriginRoots : Array RootAllowance := #[ + { root := ``Theory.Model.AExpr.liftN_liftN_merge, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.inst_liftN, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.inst_liftN_within, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.inst_inst, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.inst_inst_zero, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.AExpr.lambdaDepth_le_inst, standardAxioms := #[``propext] }, + { root := ``Theory.Model.AExpr.inst_appN }, + { root := ``Theory.Model.AExpr.inst_betaPrefix, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.Context.Valid.tail, standardAxioms := standard }, + { root := ``Theory.Model.Context.Valid.head, standardAxioms := standard }, + { root := ``Theory.Model.ContextSubstitution.base_valid, standardAxioms := standard }, + { root := ``Theory.Model.ContextSubstitution.source_valid, standardAxioms := standard }, + { root := ``Theory.Model.TypingClaim.instAt, standardAxioms := standard }, + { root := ``Theory.Model.CheckingClaim.instAt, standardAxioms := standard }, + { root := ``Theory.Model.ConversionClaim.instAt, standardAxioms := standard }, + { root := ``context_valid_instL, standardAxioms := standard }, + { root := ``typing_instL_context, standardAxioms := standard }, + { root := ``context_valid_prefix, standardAxioms := standard }, + { root := ``typing_append_context, standardAxioms := standard }, + { root := ``context_push_instL, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``SynthesisTypeTransport.sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisTypingOrigin.sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisTypeTransport.substitutePrefixAt, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``ApplicationInferenceTrace.substituteTypeOriginAt, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``ApplicationInferenceTrace.substituteTypeOrigin, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BinderInference.forallBodyCheck, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.forallBodyCheck, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisTypeCheck.forallBody, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -483,7 +514,7 @@ def roots : Array RootAllowance := #[ }) ++ productionRoots.map (fun root => { root, standardAxioms := standard, nativeAxioms := productionNative, forbiddenDependencies := forbiddenProduction -}) ++ (betaRoots ++ typeOriginRoots).map (fun allowance => { +}) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots).map (fun allowance => { allowance with forbiddenDependencies := forbiddenProduction }) ++ #[{ root := ``extend_atomic_definition, standardAxioms := standard }] diff --git a/Ix/Kernel/Verify/Consistency/Formation.lean b/Ix/Kernel/Verify/Consistency/Formation.lean index 09e5c6f38..9ab4c9b34 100644 --- a/Ix/Kernel/Verify/Consistency/Formation.lean +++ b/Ix/Kernel/Verify/Consistency/Formation.lean @@ -59,12 +59,7 @@ theorem InterfaceExtends.typing {earlier later : Model.Environment β} theorem context_valid_tail {V : Type v} [SetTheory V] {constants : Assignment β V} {levels : List Nat} {context : Model.Context β} {domain : AExpr β} {env : Nat → V} (valid : (context.push domain).Valid constants levels env) : - context.Valid constants levels (Valuation.skip 1 0 env) := by - intro index type found - have after := valid (index + 1) (type.liftN 1) (by - simp only [Context.push, List.getElem?_cons_succ, List.getElem?_map, found, Option.map_some]) - simpa only [wellDenoted_liftN, interp_liftN, Valuation.skip, Nat.not_lt_zero, ↓reduceIte, - Nat.add_comm 1 index] using after + context.Valid constants levels (Valuation.skip 1 0 env) := valid.tail theorem typing_weaken {entries : Model.Environment β} {context : Model.Context β} {term type domain : AExpr β} (typed : TypingClaim.{u,v} entries context term type) : @@ -84,6 +79,43 @@ theorem typing_instL_closed {entries : Model.Environment β} {context : Model.Co (Context.valid_nil constants _ env) simpa only [wellDenoted_instL, interp_instL] using result +theorem context_valid_instL {V : Type v} [SetTheory V] + {constants : Assignment β V} {levels : List Nat} {context : Model.Context β} + {arguments : List VLevel} {env : Nat → V} + (valid : Context.Valid constants levels (context.map (AExpr.instL arguments)) env) : + context.Valid constants (arguments.map (VLevel.eval levels)) env := by + intro index type found + have atIndex := valid index (type.instL arguments) (by + simp only [List.getElem?_map, found, Option.map_some]) + simpa only [wellDenoted_instL, interp_instL] using atIndex + +theorem typing_instL_context {entries : Model.Environment β} {context : Model.Context β} + {term type : AExpr β} (typed : TypingClaim.{u,v} entries context term type) + (arguments : List VLevel) : + TypingClaim.{u,v} entries (context.map (AExpr.instL arguments)) + (term.instL arguments) (type.instL arguments) := by + intro V _ constants realizes levels env valid + simpa only [wellDenoted_instL, interp_instL] using + typed V constants realizes (arguments.map (VLevel.eval levels)) env (context_valid_instL valid) + +theorem context_valid_prefix {V : Type v} [SetTheory V] + {constants : Assignment β V} {levels : List Nat} {context outer : Model.Context β} + {env : Nat → V} (valid : (context ++ outer).Valid constants levels env) : + context.Valid constants levels env := by + intro index type found + exact valid index type (by rw [List.getElem?_append_left (List.getElem?_eq_some_iff.mp found).1]; exact found) + +theorem typing_append_context {entries : Model.Environment β} {context : Model.Context β} + {term type : AExpr β} (typed : TypingClaim.{u,v} entries context term type) + (outer : Model.Context β) : TypingClaim.{u,v} entries (context ++ outer) term type := by + intro V _ constants realizes levels env valid + exact typed V constants realizes levels env (context_valid_prefix valid) + +theorem context_push_instL (context : Model.Context β) (domain : AExpr β) (arguments : List VLevel) : + (context.push domain).map (AExpr.instL arguments) = + Context.push (domain.instL arguments) (context.map (AExpr.instL arguments)) := by + simp only [Context.push, List.map_cons, AExpr.instL_liftN, List.map_map, Function.comp_def] + /-- Retain the syntax of both a checked type and its reduction while moving that original check to its use site. Every step is an explicit interface or syntax operation; no semantic equality is stored in this transport. -/ @@ -107,6 +139,17 @@ inductive TypeReductionTransport : entries [] current result bound) (arguments : List VLevel) : TypeReductionTransport origin originContext source reduced level entries context (current.instL arguments) (result.instL arguments) (bound.inst arguments) + | instantiateContext {origin originContext source reduced level entries context current result bound} + (prior : TypeReductionTransport origin originContext source reduced level + entries context current result bound) (arguments : List VLevel) : + TypeReductionTransport origin originContext source reduced level + entries (context.map (AExpr.instL arguments)) + (current.instL arguments) (result.instL arguments) (bound.inst arguments) + | appendContext {origin originContext source reduced level entries context current result bound} + (prior : TypeReductionTransport origin originContext source reduced level + entries context current result bound) (outer : Model.Context β) : + TypeReductionTransport origin originContext source reduced level entries (context ++ outer) + current result bound | equivalent {origin originContext source reduced level entries context current result bound current' result'} (prior : TypeReductionTransport origin originContext source reduced level entries context current result bound) @@ -167,6 +210,15 @@ theorem TypeReductionTransport.sound simpa only [interp_instL] using ih.1 V constants realizes (arguments.map (VLevel.eval levels)) env (Context.valid_nil constants _ env) + | instantiateContext prior arguments ih => + refine ⟨?_, typing_instL_context ih.2 arguments⟩ + intro V _ constants realizes levels env valid + simpa only [interp_instL] using + ih.1 V constants realizes (arguments.map (VLevel.eval levels)) env (context_valid_instL valid) + | appendContext prior outer ih => + refine ⟨?_, typing_append_context ih.2 outer⟩ + intro V _ constants realizes levels env valid + exact ih.1 V constants realizes levels env (context_valid_prefix valid) | equivalent prior sameSource sameResult ih => refine ⟨?_, sameResult.termTyping ih.2⟩ intro V _ constants realizes levels env valid diff --git a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean index ec9168662..942d5a473 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean @@ -140,7 +140,7 @@ inductive SynthesisInference {β : Type u} some ((AExpr.lam originCondition originDomain originBody).appN originArguments).erase) (typeRun : RecM.infer typeSource (methodsN typeFuel) typeBefore = .ok typeResult typeAfter) (originPrefix : cheapBetaCount trace.bodyType ≤ originBody.lambdaDepth + 1) - (transport : TypeReductionTransport earlier typeContext + (transport : SynthesisTypeTransport resolve entries context bounds earlier typeContext ((AExpr.lam originCondition originDomain originBody).appN originArguments) (AExpr.betaPrefix (cheapBetaCount trace.bodyType) (.lam originCondition originDomain originBody) originArguments) originLevel entries (context.push A) @@ -185,9 +185,299 @@ inductive SynthesisContext {β : Type u} (resolve : Address → Option (ConstRef (prior : SynthesisContext resolve entries context bounds earlier priorContext priorBounds) (extension : InterfaceExtends earlier later) : SynthesisContext resolve entries context bounds later priorContext priorBounds + | compose {entries context bounds middle middleContext middleBounds earlier priorContext priorBounds} + (prior : SynthesisContext resolve entries context bounds middle middleContext middleBounds) + (next : SynthesisContext resolve middle middleContext middleBounds earlier priorContext priorBounds) : + SynthesisContext resolve entries context bounds earlier priorContext priorBounds + +/-- A checking origin may also cross a dependent term substitution. The +substituted argument retains its actual inference call, whose typing is +derived in the same recursion as the enclosing lambda. -/ +inductive SynthesisTypeTransport {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → + Model.Environment β → Model.Context β → AExpr β → AExpr β → VLevel → + Model.Environment β → Model.Context β → AExpr β → AExpr β → VLevel → Type u + | pure {incoming incomingContext incomingBounds origin originContext source reduced level + entries context current result bound} + (transport : TypeReductionTransport origin originContext source reduced level + entries context current result bound) : + SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries context current result bound + | map {incoming incomingContext incomingBounds origin originContext source reduced level + middle middleContext current result bound entries context current' result' bound'} + (prior : SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level middle middleContext current result bound) + (transport : TypeReductionTransport middle middleContext current result bound + entries context current' result' bound') : + SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries context current' result' bound' + | substituteAt {incoming incomingContext incomingBounds origin originContext source reduced level + entries base sourceContext targetContext current result bound domain argument cutoff} + (prior : SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries sourceContext current result bound) + (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds + entries base argument domain) + (substitution : ContextSubstitution base domain argument sourceContext targetContext cutoff) : + SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries targetContext + (current.inst argument cutoff) (result.inst argument cutoff) bound + +/-- Internal typing origins for generated expressions. Leaves are actual +source inference calls. Subsequent context and substitution steps retain +those calls, including when an argument must cross still-open parameters +of an earlier declaration's type. No semantic typing field is accepted. -/ +inductive SynthesisTypingOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → + Model.Environment β → Model.Context β → AExpr β → AExpr β → Type u + | checked {incoming incomingContext incomingBounds entries context bounds locals fuel before after source result + term type level} + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type + | applicationArgument {incoming incomingContext incomingBounds entries context bounds locals fuel before fn arg + f a domain argumentType body condition functionLevel argumentLevel} + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (trace : ApplicationInferenceTrace fuel before fn arg) + (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn + f (.forallE condition domain body) functionLevel) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState arg + a argumentType argumentLevel) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals arg = some a.erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context a domain + | weaken {incoming incomingContext incomingBounds entries context term type} + (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) + (domain : AExpr β) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries (context.push domain) + (term.liftN 1) (type.liftN 1) + | instantiate {incoming incomingContext incomingBounds entries context term type} + (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) + (arguments : List VLevel) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds + entries (context.map (AExpr.instL arguments)) (term.instL arguments) (type.instL arguments) + | appendContext {incoming incomingContext incomingBounds entries context term type} + (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) + (outer : Model.Context β) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries (context ++ outer) term type + | extend {incoming incomingContext incomingBounds earlier entries context term type} + (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds earlier context term type) + (extension : InterfaceExtends earlier entries) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type + | termEquivalent {incoming incomingContext incomingBounds entries context term term' type} + (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) + (same : AExpr.LevelEquivalent term term') : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term' type + | typeEquivalent {incoming incomingContext incomingBounds entries context term type type'} + (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) + (same : AExpr.LevelEquivalent type type') : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type' + | substituteAt {incoming incomingContext incomingBounds entries base sourceContext targetContext + domain term type argument cutoff} + (body : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds + entries sourceContext term type) + (value : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries base argument domain) + (substitution : ContextSubstitution base domain argument sourceContext targetContext cutoff) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries targetContext + (term.inst argument cutoff) (type.inst argument cutoff) end +/-- The checked argument transports an already justified original beta +prefix to its substituted application result. The two resulting expressions +are computed by the proved substitution laws. -/ +def SynthesisTypeTransport.substitutePrefixAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming origin entries : Model.Environment β} + {incomingContext originContext base sourceContext targetContext : Model.Context β} + {incomingBounds : List VLevel} {source reduced head domain argument : AExpr β} + {arguments : List (AExpr β)} {level bound : VLevel} {count cutoff : Nat} + (prior : SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries sourceContext + (head.appN arguments) (AExpr.betaPrefix count head arguments) bound) + (enough : count ≤ head.lambdaDepth) + (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds + entries base argument domain) + (substitution : ContextSubstitution base domain argument sourceContext targetContext cutoff) : + SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries targetContext + ((head.inst argument cutoff).appN (arguments.map (AExpr.inst · argument cutoff))) + (AExpr.betaPrefix count (head.inst argument cutoff) + (arguments.map (AExpr.inst · argument cutoff))) bound := by + simpa only [AExpr.inst_appN, AExpr.inst_betaPrefix count head arguments argument cutoff enough] using + prior.substituteAt argumentOrigin substitution + +/-- Follow an actual argument call through the remaining dependent +parameters of a previously checked function type. The context relation +computes their updated domains at the same substitution cutoff. -/ +def ApplicationInferenceTrace.substituteTypeOriginAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming origin entries : Model.Environment β} + {incomingContext originContext context sourceContext targetContext : Model.Context β} + {incomingBounds bounds : List VLevel} {source reduced head domain argumentType argument f body : AExpr β} + {condition : Certified.PropWhen} {arguments : List (AExpr β)} + {level bound functionBound argumentBound : VLevel} {count cutoff fuel : Nat} + {locals : List FVarId} {before : TcState .anon} {fn rawArgument : KExpr .anon} + (trace : ApplicationInferenceTrace fuel before fn rawArgument) + (prior : SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries sourceContext + (head.appN arguments) (AExpr.betaPrefix count head arguments) bound) + (enough : count ≤ head.lambdaDepth) + (argumentContext : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn + f (.forallE condition domain body) functionBound) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState rawArgument + argument argumentType argumentBound) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals rawArgument = some argument.erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) + (substitution : ContextSubstitution context domain argument sourceContext targetContext cutoff) : + SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries targetContext + ((head.inst argument cutoff).appN (arguments.map (AExpr.inst · argument cutoff))) + (AExpr.betaPrefix count (head.inst argument cutoff) + (arguments.map (AExpr.inst · argument cutoff))) bound := + prior.substitutePrefixAt enough + (.applicationArgument argumentContext trace functionTree argumentTree agreement functionReading argumentReading + conditions hashPath comparisonFaithful) substitution + +/-- Reuse a codomain check from an earlier closed function type at the +actual application site. The old function parameter keeps index zero while +the caller's locals are added outside it, then the actual argument call +supplies the substitution. -/ +def ApplicationInferenceTrace.substituteTypeOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming origin entries : Model.Environment β} {incomingContext originContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {source reduced head domain argumentType argument f body : AExpr β} + {condition : Certified.PropWhen} {arguments : List (AExpr β)} + {level bound functionBound argumentBound : VLevel} {count fuel : Nat} + {locals : List FVarId} {before : TcState .anon} {fn rawArgument : KExpr .anon} + (trace : ApplicationInferenceTrace fuel before fn rawArgument) + (prior : TypeReductionTransport origin originContext source reduced level + entries (Context.push domain []) + (head.appN arguments) (AExpr.betaPrefix count head arguments) bound) + (enough : count ≤ head.lambdaDepth) + (argumentContext : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn + f (.forallE condition domain body) functionBound) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState rawArgument + argument argumentType argumentBound) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals rawArgument = some argument.erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) : + SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries context + ((head.inst argument).appN (arguments.map (AExpr.inst · argument))) + (AExpr.betaPrefix count (head.inst argument) (arguments.map (AExpr.inst · argument))) bound := by + have imported : TypeReductionTransport origin originContext source reduced level + entries (context.push domain) (head.appN arguments) (AExpr.betaPrefix count head arguments) bound := by + simpa only [Context.push, List.map_nil, List.cons_append, List.nil_append] using + prior.appendContext (context.map (AExpr.liftN 1 ·)) + exact trace.substituteTypeOriginAt (.pure imported) enough argumentContext functionTree argumentTree + agreement functionReading argumentReading conditions hashPath comparisonFaithful .root + +/-- The actual codomain call retained inside a function-type check. Its +local context is reconstructed from that same call's domain check. -/ +structure SynthesisForallBodyCheck {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (context : Model.Context β) (bounds : List VLevel) + (condition : Certified.PropWhen) (domain body : AExpr β) where + domainLevel : VLevel + bodyLevel : VLevel + bound : VLevel + locals : List FVarId + fuel : Nat + before : TcState .anon + after : TcState .anon + source : KExpr .anon + result : KExpr .anon + contextOrigin : SynthesisContext resolve entries context bounds entries + (context.push domain) (domainLevel :: bounds) + tree : SynthesisInference resolve entries locals (context.push domain) (domainLevel :: bounds) + fuel before source body (.sort bodyLevel) bound + agreement : LocalContextReading resolve locals before.lctx (context.push domain) + reading : readScopedExpr? resolve locals source = some body.erase + run : RecM.infer source (methodsN fuel) before = .ok result after + conditionAgrees : condition = Certified.zeroCondition bodyLevel + +private def ForallInferenceTrace.bodyCheck {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} + {fuel : Nat} {before : TcState .anon} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body : KExpr .anon} {A B : AExpr β} {domainBound bodyBound : VLevel} + (trace : ForallInferenceTrace fuel before name bi domain body) + (opening : BinderOpeningSupport trace.domainState body) + (absent : (⟨trace.domainState.env.nextFVarId⟩ : FVarId) ∉ locals) + (domainTree : SynthesisInference resolve entries locals context bounds fuel before domain + A (.sort (readLevel trace.domainLevel)) domainBound) + (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) + (readLevel trace.domainLevel :: bounds) fuel trace.openedState trace.opened + B (.sort (readLevel trace.bodyLevel)) bodyBound) + (agreement : LocalContextReading resolve locals before.lctx context) + (domainReading : readScopedExpr? resolve locals domain = some A.erase) + (bodyReading : readScopedExpr? resolve locals body 1 = some B.erase) : + SynthesisForallBodyCheck resolve entries context bounds + (Certified.zeroCondition (readLevel trace.bodyLevel)) A B := by + have opened := openBinder_sound opening (trace.contextPreserved.symm ▸ agreement) + absent domainReading bodyReading trace.openRun + exact { + domainLevel := readLevel trace.domainLevel + bodyLevel := readLevel trace.bodyLevel + bound := bodyBound + locals := trace.fresh :: locals + fuel := fuel + before := trace.openedState + after := trace.bodyState + source := trace.opened + result := .sort trace.bodyLevel trace.bodyInfo + contextOrigin := .push .current domainTree agreement domainReading trace.domainRun + tree := bodyTree + agreement := opened.2.2.1 + reading := opened.2.1 + run := trace.bodyRun + conditionAgrees := rfl } + +def BinderInference.forallBodyCheck {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} + {condition : Certified.PropWhen} {domain body type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source + (.forallE condition domain body) type) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some (AExpr.forallE condition domain body).erase) : + SynthesisForallBodyCheck resolve entries context bounds condition domain body := by + cases support with + | forallE miss trace opening absent domainTree bodyTree => + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading + exact trace.bodyCheck opening absent (.known domainTree (.sort _)) (.known bodyTree (.sort _)) + (miss.localContext.symm ▸ agreement) domainReads bodyReads + +/-- Recover the recorded codomain check by inspecting the production +inference tree. This does not assume a new call on an inferred codomain. -/ +def SynthesisInference.forallBodyCheck {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} + {condition : Certified.PropWhen} {domain body type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source + (.forallE condition domain body) type level) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some (AExpr.forallE condition domain body).erase) : + SynthesisForallBodyCheck resolve entries context bounds condition domain body := by + cases support with + | known inference => exact inference.forallBodyCheck agreement reading + | reuseType inference => exact inference.forallBodyCheck agreement reading + | forallE miss trace opening absent domainTree bodyTree => + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading + exact trace.bodyCheck opening absent domainTree bodyTree + (miss.localContext.symm ▸ agreement) domainReads bodyReads + private theorem list_reverse_induction {α : Type u} {motive : List α → Prop} (nil : motive []) (append_singleton : ∀ tail last, motive tail → motive (tail ++ [last])) @@ -384,7 +674,7 @@ theorem SynthesisInference.soundWithSpine {β : Type u} obtain ⟨_, _, _, originSpine⟩ := typeTree.soundWithSpine (typeContextSupport.sound formed) typeAgreement typeReading typeRun obtain ⟨originConversion, originReducedTyped⟩ := originSpine.betaPrefix originPrefix - obtain ⟨conversion, reducedTyped⟩ := transport.sound originConversion originReducedTyped + obtain ⟨conversion, reducedTyped⟩ := transport.sound formed originConversion originReducedTyped obtain ⟨_, reducedReads, reducedCoherent⟩ := reduction.reading bodyTypeReads obtain ⟨closedReads, closedCoherent⟩ := abstractFVars_readScopedExpr? constructed bound reducedCoherent closingFaithful reducedReads @@ -419,6 +709,59 @@ theorem SynthesisContext.sound {β : Type u} {resolve : Address → Option (Cons | .extend prior extension => by intro index type bound found indexed exact extension.typing (prior.sound formed index type bound found indexed) + | .compose prior next => next.sound (prior.sound formed) +termination_by structural support + +theorem SynthesisTypeTransport.sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming origin entries : Model.Environment β} {incomingContext originContext context : Model.Context β} + {incomingBounds : List VLevel} {source reduced current result : AExpr β} {level bound : VLevel} + (support : SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries context current result bound) + (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) + (converted : ConversionClaim.{u,v} origin originContext source reduced) + (typed : TypingClaim.{u,v} origin originContext reduced (.sort level)) : + ConversionClaim.{u,v} entries context current result ∧ + TypingClaim.{u,v} entries context result (.sort bound) := + match support with + | .pure transport => transport.sound converted typed + | .map prior transport => by + obtain ⟨conversion, resultTyped⟩ := prior.sound formed converted typed + exact transport.sound conversion resultTyped + | .substituteAt prior argumentOrigin substitution => by + obtain ⟨conversion, resultTyped⟩ := prior.sound formed converted typed + have argumentAtDomain := argumentOrigin.sound formed + exact ⟨conversion.instAt argumentAtDomain substitution, resultTyped.instAt argumentAtDomain substitution⟩ +termination_by structural support + +theorem SynthesisTypingOrigin.sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} + (support : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) + (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : + TypingClaim.{u,v} entries context term type := + match support with + | .checked contextOrigin tree agreement reading accepted => + (tree.soundWithSpine (contextOrigin.sound formed) agreement reading accepted).2.1 + | .applicationArgument contextOrigin trace functionTree argumentTree agreement functionReading argumentReading + conditions hashPath comparisonFaithful => by + have contextFormation := contextOrigin.sound formed + have functionTypeReads := + (functionTree.soundWithSpine contextFormation agreement functionReading trace.functionRun).1 + have domainReads := (readScopedExpr?_all_parts functionTypeReads).1 + obtain ⟨argumentTypeReads, argumentTyped, _, _⟩ := + argumentTree.soundWithSpine contextFormation (trace.contextPreserved.symm ▸ agreement) + argumentReading trace.argumentRun + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (argumentTypeReads.symm.trans + ((beq_readScopedExpr? comparisonFaithful hashPath).trans domainReads))) conditions + exact sameType ▸ argumentTyped + | .weaken prior domain => typing_weaken (prior.sound formed) + | .instantiate prior arguments => typing_instL_context (prior.sound formed) arguments + | .appendContext prior outer => typing_append_context (prior.sound formed) outer + | .extend prior extension => extension.typing (prior.sound formed) + | .termEquivalent prior same => same.termTyping (prior.sound formed) + | .typeEquivalent prior same => same.typing (prior.sound formed) + | .substituteAt body value substitution => (body.sound formed).instAt (value.sound formed) substitution termination_by structural support end @@ -484,6 +827,12 @@ theorem SynthesisTypeCheck.sound {β : Type u} {resolve : Address → Option (Co TypingClaim.{u,v} entries [] type (.sort level) := (check.inference.closed_sound check.reading check.run).2.1 +def SynthesisTypeCheck.forallBody {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {condition : Certified.PropWhen} {domain body : AExpr β} {level : VLevel} + (check : SynthesisTypeCheck resolve entries (.forallE condition domain body) level) : + SynthesisForallBodyCheck resolve entries [] [] condition domain body := + check.inference.forallBodyCheck (.empty _ _) check.reading + /-- An instantiated constant can recover formation from an earlier actual declaration type check, with exact interface preservation and structural level congruence. -/ diff --git a/Ix/Theory/Model/BetaSpine.lean b/Ix/Theory/Model/BetaSpine.lean index d0a75524d..bbbb8680e 100644 --- a/Ix/Theory/Model/BetaSpine.lean +++ b/Ix/Theory/Model/BetaSpine.lean @@ -80,6 +80,40 @@ theorem instL_betaPrefix (count : Nat) (head : AExpr β) (arguments : List (AExp simp only [betaPrefix, instL, List.map_nil, List.map_cons, appN_nil, instL_appN, ih, instL_inst] +theorem lambdaDepth_le_inst (term argument : AExpr β) (cutoff : Nat) : + term.lambdaDepth ≤ (term.inst argument cutoff).lambdaDepth := by + induction term generalizing cutoff with + | lam condition domain body ihDomain ihBody => + exact Nat.add_le_add_right (ihBody (cutoff + 1)) 1 + | _ => exact Nat.zero_le _ + +theorem inst_appN (head : AExpr β) (arguments : List (AExpr β)) (value : AExpr β) (cutoff : Nat) : + (head.appN arguments).inst value cutoff = + (head.inst value cutoff).appN (arguments.map (inst · value cutoff)) := by + induction arguments generalizing head with + | nil => rfl + | cons argument arguments ih => simpa only [appN_cons, inst, List.map_cons] using ih (head.app argument) + +/-- Substitution preserves a reduction justified by the original lambda +prefix. Lambdas newly exposed beyond that prefix need their own origin. -/ +theorem inst_betaPrefix (count : Nat) (head : AExpr β) (arguments : List (AExpr β)) + (value : AExpr β) (cutoff : Nat) (enough : count ≤ head.lambdaDepth) : + (betaPrefix count head arguments).inst value cutoff = + betaPrefix count (head.inst value cutoff) (arguments.map (inst · value cutoff)) := by + induction count generalizing head arguments with + | zero => exact inst_appN _ _ _ _ + | succ count ih => + cases head with + | lam condition domain body => + cases arguments with + | nil => rfl + | cons argument arguments => + have remaining : count ≤ (body.inst argument).lambdaDepth := + Nat.le_trans (by simpa only [lambdaDepth, Nat.add_le_add_iff_right] using enough) + (lambdaDepth_le_inst body argument 0) + simp only [betaPrefix, inst, List.map_cons, ih _ _ remaining, inst_inst_zero] + | _ => simp [lambdaDepth] at enough + end AExpr /-- Each leading lambda agrees with the corresponding inferred Pi. diff --git a/Ix/Theory/Model/BetaSubstitution.lean b/Ix/Theory/Model/BetaSubstitution.lean index 9a6624381..7c77c774b 100644 --- a/Ix/Theory/Model/BetaSubstitution.lean +++ b/Ix/Theory/Model/BetaSubstitution.lean @@ -78,6 +78,102 @@ theorem liftN_inst_zero (term argument : AExpr β) (count cutoff : Nat) : (term.liftN count (cutoff + 1)).inst (argument.liftN count cutoff) := by simpa only [Nat.add_zero] using liftN_inst term argument count cutoff 0 +theorem liftN_liftN_merge (term : AExpr β) (first second lower upper : Nat) + (ordered : lower ≤ upper) (inside : upper ≤ first + lower) : + (term.liftN first lower).liftN second upper = term.liftN (first + second) lower := by + induction term generalizing lower upper with + | bvar index => + by_cases below : index < lower + · simp [liftN, liftVar, below, show index < upper by omega] + · simp [liftN, liftVar, below, show ¬ first + index < upper by omega, + Nat.add_assoc, Nat.add_left_comm] + | lam condition domain body ihDomain ihBody | forallE condition domain body ihDomain ihBody => + simp only [liftN, ihDomain lower upper ordered inside, + ihBody (lower + 1) (upper + 1) (by omega) (by omega)] + | _ => simp_all [liftN] + +/-- A substitution after an inserted block uses the correspondingly +shifted index. The substituted argument is lifted only once. -/ +theorem inst_liftN (term argument : AExpr β) (count lower upper : Nat) + (ordered : lower ≤ upper) : + (term.liftN count lower).inst argument (count + upper) = + (term.inst argument upper).liftN count lower := by + induction term generalizing lower upper with + | bvar index => + by_cases below : index < lower + · simp [liftN, liftVar, inst, instVar, below, + show index < upper by omega, show index < count + upper by omega] + · by_cases before : index < upper + · simp [liftN, liftVar, inst, instVar, below, before] + · by_cases equal : index = upper + · subst index + simp only [liftN, liftVar, if_neg below, inst, instVar, Nat.lt_irrefl, if_false, if_true] + simpa only [Nat.zero_add, Nat.add_comm] using + (liftN_liftN_merge argument upper count 0 lower (Nat.zero_le _) ordered).symm + · simp [liftN, liftVar, inst, instVar, below, before, equal, + show ¬ index - 1 < lower by omega] + omega + | lam condition domain body ihDomain ihBody | forallE condition domain body ihDomain ihBody => + simp only [liftN, inst, ihDomain lower upper ordered, + show count + upper + 1 = count + (upper + 1) by omega, + ihBody (lower + 1) (upper + 1) (by omega)] + | _ => simp_all [liftN, inst] + +/-- Removing any variable in an inserted block leaves a block with one +fewer variable; the term contains no occurrence to replace there. -/ +theorem inst_liftN_within (term argument : AExpr β) (count cutoff removed : Nat) + (lower : cutoff ≤ removed) (upper : removed ≤ count + cutoff) : + (term.liftN (count + 1) cutoff).inst argument removed = term.liftN count cutoff := by + induction term generalizing cutoff removed with + | bvar index => + by_cases below : index < cutoff + · simp [liftN, liftVar, inst, instVar, below, show index < removed by omega] + · simp [liftN, liftVar, inst, instVar, below, + show ¬ count + 1 + index < removed by omega, + show count + 1 + index ≠ removed by omega] + | lam condition domain body ihDomain ihBody | forallE condition domain body ihDomain ihBody => + simp only [liftN, inst, ihDomain cutoff removed lower upper, + ihBody (cutoff + 1) (removed + 1) (by omega) (by omega)] + | _ => simp_all [liftN, inst] + +/-- Compose substitutions in dependency order. The earlier argument is +itself substituted, and the later index skips the removed binder. -/ +theorem inst_inst (term first second : AExpr β) (cutoff depth : Nat) : + (term.inst first depth).inst second (cutoff + depth) = + (term.inst second (cutoff + depth + 1)).inst (first.inst second cutoff) depth := by + induction term generalizing depth with + | bvar index => + by_cases below : index < depth + · simp [inst, instVar, below, show index < cutoff + depth by omega, + show index < cutoff + depth + 1 by omega] + · by_cases equal : index = depth + · subst index + simp only [inst, instVar, Nat.lt_irrefl, if_false, if_true, + if_pos (show depth < cutoff + depth + 1 by omega)] + simpa only [Nat.add_comm] using inst_liftN first second depth 0 cutoff (Nat.zero_le _) + · by_cases before : index < cutoff + depth + 1 + · simp [inst, instVar, below, equal, before, + show index - 1 < cutoff + depth by omega] + · by_cases selected : index = cutoff + depth + 1 + · subst index + simp only [inst, instVar, Nat.lt_irrefl, if_false, if_true, if_neg below, + if_neg equal, Nat.add_sub_cancel] + have skipped := inst_liftN_within second (first.inst second cutoff) (cutoff + depth) 0 depth + (Nat.zero_le _) (by omega) + simpa only [Nat.add_zero] using skipped.symm + · simp [inst, instVar, below, equal, before, selected, + show ¬ index - 1 < cutoff + depth by omega, + show index - 1 ≠ cutoff + depth by omega, + show ¬ index - 1 < depth by omega, show index - 1 ≠ depth by omega] + | lam condition domain body ihDomain ihBody | forallE condition domain body ihDomain ihBody => + simp only [inst, ihDomain, ihBody, Nat.add_assoc] + | _ => simp_all [inst] + +theorem inst_inst_zero (term first second : AExpr β) (cutoff : Nat) : + (term.inst first).inst second cutoff = + (term.inst second (cutoff + 1)).inst (first.inst second cutoff) := by + simpa only [Nat.add_zero] using inst_inst term first second cutoff 0 + theorem inst_liftN_top (term argument : AExpr β) (count cutoff : Nat) : (term.liftN (count + 1) cutoff).inst argument (count + cutoff) = term.liftN count cutoff := by induction term generalizing cutoff with diff --git a/Ix/Theory/Model/Substitution.lean b/Ix/Theory/Model/Substitution.lean index f8141ec4e..371236328 100644 --- a/Ix/Theory/Model/Substitution.lean +++ b/Ix/Theory/Model/Substitution.lean @@ -66,25 +66,125 @@ theorem wellDenoted_inst_iff (term argument : AExpr β) (constants : Assignment ⟨wellDenoted_of_inst term argument constants levels env cutoff, wellDenoted_inst _ _ _ _ _ _ argumentValid⟩ +theorem Context.Valid.tail {constants : Assignment β V} {levels : List Nat} + {context : Context β} {domain : AExpr β} {env : Nat → V} + (valid : (context.push domain).Valid constants levels env) : + context.Valid constants levels (Valuation.skip 1 0 env) := by + intro index type found + have after := valid (index + 1) (type.liftN 1) (by + simp only [Context.push, List.getElem?_cons_succ, List.getElem?_map, found, Option.map_some]) + simpa only [wellDenoted_liftN, interp_liftN, Valuation.skip, Nat.not_lt_zero, ↓reduceIte, + Nat.add_comm 1 index] using after + +theorem Context.Valid.head {constants : Assignment β V} {levels : List Nat} + {context : Context β} {domain : AExpr β} {env : Nat → V} + (valid : (context.push domain).Valid constants levels env) : + WellDenoted constants levels (Valuation.skip 1 0 env) domain ∧ + env 0 ∈ˢ interp constants levels (Valuation.skip 1 0 env) domain := by + simpa only [wellDenoted_liftN, interp_liftN] using + valid 0 (domain.liftN 1) (by simp only [Context.push, List.getElem?_cons_zero]) + +/-- Remove an outer parameter while retaining later dependent binders. +Each retained binder's domain is substituted in its own preceding context; +the argument remains expressed in the base context throughout. -/ +inductive ContextSubstitution (base : Context β) (domain argument : AExpr β) : + Context β → Context β → Nat → Prop + | root : ContextSubstitution base domain argument (base.push domain) base 0 + | push {source target cutoff} (prior : ContextSubstitution base domain argument source target cutoff) + (binder : AExpr β) : + ContextSubstitution base domain argument (source.push binder) + (target.push (binder.inst argument cutoff)) (cutoff + 1) + +theorem ContextSubstitution.base_valid {base source target : Context β} + {domain argument : AExpr β} {cutoff : Nat} + (substitution : ContextSubstitution base domain argument source target cutoff) + {constants : Assignment β V} {levels : List Nat} {env : Nat → V} + (valid : target.Valid constants levels env) : + base.Valid constants levels (Valuation.skip cutoff 0 env) := by + induction substitution generalizing env with + | root => simpa only [Valuation.skip_zero] using valid + | @push source target cutoff prior binder ih => + have shifted : Valuation.skip cutoff 0 (Valuation.skip 1 0 env) = + Valuation.skip (cutoff + 1) 0 env := by + funext index + simp only [Valuation.skip, Nat.not_lt_zero, ↓reduceIte] + congr 1 + omega + exact shifted ▸ ih valid.tail + +theorem ContextSubstitution.source_valid {entries : Environment β} {base source target : Context β} + {domain argument : AExpr β} {cutoff : Nat} + (substitution : ContextSubstitution base domain argument source target cutoff) + (value : TypingClaim.{u,v} entries base argument domain) + {constants : Assignment β V} (realizes : Realizes constants entries) + {levels : List Nat} {env : Nat → V} (valid : target.Valid constants levels env) : + source.Valid constants levels + (Valuation.insert cutoff (interp constants levels (Valuation.skip cutoff 0 env) argument) env) := by + induction substitution generalizing env with + | root => + have argumentTyped := value V constants realizes levels env valid + simpa only [Valuation.skip_zero, Valuation.insert_zero] using + valid.push argumentTyped.2.1 argumentTyped.2.2 + | @push source target cutoff prior binder ih => + have tailValid := ih valid.tail + have originalDomain := wellDenoted_of_inst binder argument constants levels + (Valuation.skip 1 0 env) cutoff valid.head.1 + have originalMember := valid.head.2 + rw [interp_inst] at originalMember + have pushed := tailValid.push originalDomain originalMember + have envCons : Valuation.cons (env 0) (Valuation.skip 1 0 env) = env := by + funext index + cases index <;> simp [Valuation.skip, Nat.add_comm] + rw [← Valuation.insert_cons, ← Valuation.skip_succ_cons cutoff (env 0), envCons] at pushed + exact pushed + +/-- Substitute beneath an arbitrary retained dependent prefix. The context +relation updates every later parameter's type before that parameter is used. -/ +theorem TypingClaim.instAt {entries : Environment β} {base source target : Context β} + {term type argument domain : AExpr β} {cutoff : Nat} + (body : TypingClaim.{u,v} entries source term type) + (value : TypingClaim.{u,v} entries base argument domain) + (substitution : ContextSubstitution base domain argument source target cutoff) : + TypingClaim.{u,v} entries target (term.inst argument cutoff) (type.inst argument cutoff) := by + intro V _ constants realizes levels env valid + have argumentValid := (value V constants realizes levels _ (substitution.base_valid valid)).1 + obtain ⟨termValid, typeValid, member⟩ := + body V constants realizes levels _ (substitution.source_valid value realizes valid) + exact ⟨wellDenoted_inst _ _ _ _ _ _ argumentValid termValid, + wellDenoted_inst _ _ _ _ _ _ argumentValid typeValid, by simpa only [interp_inst] using member⟩ + +theorem CheckingClaim.instAt {entries : Environment β} {base source target : Context β} + {term type argument domain : AExpr β} {cutoff : Nat} + (body : CheckingClaim.{u,v} entries source term type) + (value : TypingClaim.{u,v} entries base argument domain) + (substitution : ContextSubstitution base domain argument source target cutoff) : + CheckingClaim.{u,v} entries target (term.inst argument cutoff) (type.inst argument cutoff) := by + intro V _ constants realizes levels env valid expected + have argumentValid := (value V constants realizes levels _ (substitution.base_valid valid)).1 + obtain ⟨termValid, member⟩ := + body V constants realizes levels _ (substitution.source_valid value realizes valid) + (wellDenoted_of_inst _ _ _ _ _ _ expected) + exact ⟨wellDenoted_inst _ _ _ _ _ _ argumentValid termValid, + by simpa only [interp_inst] using member⟩ + +theorem ConversionClaim.instAt {entries : Environment β} {base source target : Context β} + {left right argument domain : AExpr β} {cutoff : Nat} + (body : ConversionClaim.{u,v} entries source left right) + (value : TypingClaim.{u,v} entries base argument domain) + (substitution : ContextSubstitution base domain argument source target cutoff) : + ConversionClaim.{u,v} entries target (left.inst argument cutoff) (right.inst argument cutoff) := by + intro V _ constants realizes levels env valid + simpa only [interp_inst] using + body V constants realizes levels _ (substitution.source_valid value realizes valid) + /-- Instantiate a typed body using the actual domain of the removed binder. Both the substituted term and type retain hereditary validity. -/ theorem TypingClaim.inst {entries : Environment β} {context : Context β} {term type argument domain : AExpr β} (body : TypingClaim.{u,v} entries (context.push domain) term type) (value : TypingClaim.{u,v} entries context argument domain) : - TypingClaim.{u,v} entries context (term.inst argument) (type.inst argument) := by - intro V _ constants realizes levels env valid - obtain ⟨argumentValid, domainValid, argumentMember⟩ := value V constants realizes levels env valid - obtain ⟨termValid, typeValid, member⟩ := body V constants realizes levels - (Valuation.cons (interp constants levels env argument) env) (valid.push domainValid argumentMember) - refine ⟨?_, ?_, ?_⟩ - · apply wellDenoted_inst - · simpa using argumentValid - · simpa using termValid - · apply wellDenoted_inst - · simpa using argumentValid - · simpa using typeValid - · simpa only [interp_inst, Valuation.skip_zero, Valuation.insert_zero] using member + TypingClaim.{u,v} entries context (term.inst argument) (type.inst argument) := + body.instAt value .root /-- Checking may defer validity of its expected type, even through substitution: validity of the instantiated type supplies the body premise. -/ @@ -92,28 +192,14 @@ theorem CheckingClaim.inst {entries : Environment β} {context : Context β} {term type argument domain : AExpr β} (body : CheckingClaim.{u,v} entries (context.push domain) term type) (value : TypingClaim.{u,v} entries context argument domain) : - CheckingClaim.{u,v} entries context (term.inst argument) (type.inst argument) := by - intro V _ constants realizes levels env valid expected - obtain ⟨argumentValid, domainValid, argumentMember⟩ := value V constants realizes levels env valid - have typeValid := wellDenoted_of_inst type argument constants levels env 0 expected - obtain ⟨termValid, member⟩ := body V constants realizes levels - (Valuation.cons (interp constants levels env argument) env) (valid.push domainValid argumentMember) - (by simpa only [Valuation.skip_zero, Valuation.insert_zero] using typeValid) - refine ⟨?_, ?_⟩ - · apply wellDenoted_inst - · simpa using argumentValid - · simpa using termValid - · simpa only [interp_inst, Valuation.skip_zero, Valuation.insert_zero] using member + CheckingClaim.{u,v} entries context (term.inst argument) (type.inst argument) := + body.instAt value .root theorem ConversionClaim.inst {entries : Environment β} {context : Context β} {left right argument domain : AExpr β} (body : ConversionClaim.{u,v} entries (context.push domain) left right) (value : TypingClaim.{u,v} entries context argument domain) : - ConversionClaim.{u,v} entries context (left.inst argument) (right.inst argument) := by - intro V _ constants realizes levels env valid - have argumentTyped := value V constants realizes levels env valid - simpa only [interp_inst, Valuation.skip_zero, Valuation.insert_zero] using - body V constants realizes levels (Valuation.cons (interp constants levels env argument) env) - (valid.push argumentTyped.2.1 argumentTyped.2.2) + ConversionClaim.{u,v} entries context (left.inst argument) (right.inst argument) := + body.instAt value .root end Ix.Theory.Model diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index d3fa3dbda..9f5f8814f 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -682,6 +682,70 @@ private def cheapLambdaConstantResult : Bool := | .ok passed after => passed && after.lctx.size == 0 | .error _ _ => false +/-- Application substitutes into an earlier checked codomain containing a +beta redex. With two parameters, the second domain is `B x` and the final +carrier is `C x y`, so both substitutions affect the generated type. -/ +private def cheapApplicationType (level : Ixon.Univ) (universes : UInt64 := 0) + (dependent wrongArgument : Bool := false) : Ixon.Env × Address := Id.run do + let (env, carrier) := storeConst {} + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], #[level]⟩ + let arguments := if universes == 0 then #[] else #[0] + let (env, family) := storeConst env + ⟨.axio ⟨false, universes, .leanAll (.ref 0 arguments) (.sort 0)⟩, + #[], #[carrier], #[level]⟩ + let secondDomain := Ixon.Expr.app (.ref 1 arguments) (.var 0) + let (env, dependentFamily) := storeConst env + ⟨.axio ⟨false, universes, .leanAll (.ref 0 arguments) (.leanAll secondDomain (.sort 0))⟩, + #[], #[carrier, family], #[level]⟩ + let result := if dependent then + Ixon.Expr.app (.app (.ref 2 arguments) (.var 1)) (.var 0) + else .app (.ref 1 arguments) (.var 0) + let redex := Ixon.Expr.app (.leanLam (.sort 0) (.var 0)) result + let functionType := Ixon.Expr.leanAll (.ref 0 arguments) + (if dependent then .leanAll secondDomain redex else redex) + let (env, function) := storeConst env + ⟨.axio ⟨false, universes, functionType⟩, #[], #[carrier, family, dependentFamily], #[level]⟩ + let call := if dependent then + Ixon.Expr.app (.app (.ref 3 arguments) (.var 1)) (.var (if wrongArgument then 1 else 0)) + else .app (.ref 3 arguments) (if wrongArgument then .ref 0 arguments else .var 0) + return storeConst env + ⟨.defn ⟨.defn, .safe, universes, + .leanAll (.ref 0 arguments) (if dependent then .leanAll secondDomain result else result), + .leanLam (.ref 0 arguments) (if dependent then .leanLam secondDomain call else call)⟩, + #[], #[carrier, family, dependentFamily, function], #[level]⟩ + +private def cheapApplicationTypeResult (dependent : Bool) (level : Ixon.Univ := .zero) : Bool := + let (env, target) := cheapApplicationType level 0 dependent + let action : RecM .anon Bool := RecM.withLctxScope do + let concrete ← TcM.getConst (m := .anon) ⟨target, ()⟩ + let .defn _ _ _ _ _ _ type value _ _ := concrete | return false + let .lam name bi domain body _ := value | return false + let .all _ _ _ codomain _ := type | return false + let (opened, first, _) ← TcM.openBinderWithFV name bi domain body + let expected ← TcM.runIntern (instantiateRev codomain #[first]) + let (call, expected) ← if dependent then do + let .lam name bi domain body _ := opened | return false + let .all _ _ expectedDomain codomain _ := expected | return false + if domain != expectedDomain then return false + let (call, second, _) ← TcM.openBinderWithFV name bi domain body + let .app firstCall _ _ := call | return false + let .all _ _ partialDomain _ _ ← RecM.inferCall firstCall | return false + if partialDomain != domain then return false + let expected ← TcM.runIntern (instantiateRev codomain #[second]) + pure (call, expected) + else pure (opened, expected) + let original ← RecM.inferCall call + let reduced ← TcM.runIntern (cheapBetaReduce original) + let before ← get + let inferred ← RecM.inferCall value + let repeated ← RecM.inferCall value + let after ← get + return (cheapBetaPlan? original).isSome && original != reduced && reduced == expected && + inferred == type && repeated == type && after.lctx.size == before.lctx.size + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + private def applicationCases : TestSeq := test "application environment: Prop/Type identity calls and transitive theorem calls check" (allSucceeded applicationEnvironment 5 { clearEvery := 0 }) @@ -789,6 +853,28 @@ private def cheapLambdaCases : TestSeq := ++ test "lambda cheap beta admission: returning a carrier in place of its witness is rejected" (let (env, target) := cheapLambdaConstant .zero 0 true true; rowFailed env target) +private def cheapApplicationCases : TestSeq := + test "application type beta: substituted codomains check in Prop and Type" + (allSucceeded (cheapApplicationType .zero).1 5 && + allSucceeded (cheapApplicationType (.succ .zero)).1 5) + ++ test "application type beta: an earlier codomain check retains universe parameters" + (allSucceeded (cheapApplicationType (.var 0) 1).1 5) + ++ test "application type beta: the second parameter depends on the first argument" + (allSucceeded (cheapApplicationType .zero 0 true).1 5 && + allSucceeded (cheapApplicationType (.succ .zero) 0 true).1 5) + ++ test "application type beta: dependent parameters retain universe parameters" + (allSucceeded (cheapApplicationType (.var 0) 1 true).1 5) + ++ test "application type beta: dependent substitutions survive per-item cache clearing" + (allSucceeded (cheapApplicationType .zero 0 true).1 5 { clearEvery := 1 }) + ++ test "application type beta: a single argument changes the generated body type before abstraction" + (cheapApplicationTypeResult false && cheapApplicationTypeResult false (.succ .zero)) + ++ test "application type beta: two arguments update the later domain and the exact reduced result" + (cheapApplicationTypeResult true && cheapApplicationTypeResult true (.succ .zero)) + ++ test "application type beta: passing the carrier as its own witness is rejected" + (let (env, target) := cheapApplicationType .zero 0 false true; rowFailed env target) + ++ test "application type beta: the first argument cannot replace the dependent second argument" + (let (env, target) := cheapApplicationType .zero 0 true true; rowFailed env target) + /-- Call a polymorphic identity from a monomorphic function body. Universe indices select entries in the declaration's explicit level table. -/ private def storePolymorphicCall (env : Ixon.Env) (identity : Address) @@ -2381,6 +2467,7 @@ private def polymorphicDefinitionCases : TestSeq := public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, multiBetaCases, cheapLambdaCases, + cheapApplicationCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, sourceAgreementCases, sourceCacheCases, polymorphicDefinitionCases] diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 8a492ac37..92b1a4588 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -372,6 +372,16 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) follows the selected plan through substitution and interning, and lambda abstraction uses that reduction's output table. No new check of the generated result is assumed. + Retained codomain checks now also cross dependent term substitution. + `SynthesisTypeCheck.forallBody` extracts the actual codomain call from the + earlier function-type tree. `SynthesisTypingOrigin.applicationArgument` + retains the executed function and argument checks and their hash comparison; + the mutual soundness proof derives the argument's membership in that domain. + `ApplicationInferenceTrace.substituteTypeOriginAt` then transports the + original beta prefix beneath any remaining dependent parameters, updating + their domains with the same substitution. This transport is consumed by + `lamBeta`. No separate inference of the substituted type or semantic + argument-typing premise is required. - Applications use full mode, syntactic Pi exposure, an ordinary argument without an eager-reduction marker, and the hash-equality conversion path. Their witnesses retain the actual recursive calls, context preservation @@ -484,7 +494,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 497 exact theorem boundaries. The production +The consistency target checks 525 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -500,6 +510,11 @@ Their model-side level congruence introduces no native proof dependency. Semantic checking against a formed type and the new product-fibre universe bound also use only standard Lean axioms. The substitution rules for model typing, checking, and equality introduce no additional assumptions. +Their general `instAt` forms use `ContextSubstitution` to remove an outer +parameter and update every retained dependent domain. The beta-prefix +commutation theorem covers any cutoff while bounding reduction by the +original lambda prefix; lambdas newly exposed by substitution need their own +checking origins. `SynthesisInference.beta_peel_sound` derives beta-prefix equality and result typing from actual lambda and dependent argument inference. Its domain-shape proof retains information lost when proof values are identified. The original @@ -521,10 +536,13 @@ current context, to justify the changed cheap-beta result. The actual source reading determines the raw head and arguments; `CheapBetaSupport.reading` connects the selected prefix to the reduced syntax and final intern table. `SynthesisContext.sound` derives earlier contexts from their domain checks, -so the origin can precede additional binders. The public inference and -environment results include this case. Automatic origin construction for -arbitrary generated types, repeated reduction, and other conversion paths -remain open. +so the origin can precede additional binders. `SynthesisTypeTransport.sound` +and `SynthesisTypingOrigin.sound` are proved in that same recursion and carry +these checks through argument substitution, including later dependent +domains and the original context's universe instantiation. The public +inference and environment results include this case. Automatic origin +construction for arbitrary generated types, repeated reduction, and other +conversion paths remain open. Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, argument order, and rejection of wrong arities and out-of-range parameters. @@ -556,6 +574,13 @@ types, local checking origins beneath further binders, dependent prefixes, both application positions, and reused declaration types in Prop, Type, and at universe parameters. They also check cache clearing, scope cleanup, and rejection of a carrier returned in place of its witness. +Application-type beta regressions check an earlier function's substituted +codomain in Prop, Type, and at universe parameters. Two-argument cases use +`(x : A) → (y : B x) → ((fun T : Sort u => T) (C x y))`: they observe the +updated second domain, different original and reduced result hashes, and the +exact final lambda type. They cover cache clearing, reuse, scope cleanup, and +rejection of a carrier or first argument used in the wrong dependent domain. +These execution tests do not construct the general finite inference resources. Polymorphic-call regressions include Prop/Type instances in real function bodies, `max`/`imax` simplification inside Pi domains, closed nested references under active locals, separate cache keys for different universe instances, @@ -625,7 +650,7 @@ Definition-cycle regressions use content-addressed standalone and mutual declarations, including a self-justifying theorem, a two-member cycle, type cycles, lets, shared syntax, and binders. They check repeated member failures, acyclic forward references, cache clearing, and the partial/unsafe policy. -The unit suite contains 620 checks. The anonymous differential additionally +The unit suite contains 629 checks. The anonymous differential additionally serializes eight cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. diff --git a/docs/theory.md b/docs/theory.md index c5eb200e4..f159c5232 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -51,7 +51,8 @@ application suffix. The actual multi-argument structural-WHNF step reads the result through simultaneous substitution and interned suffix rebuilding, preserving intern coherence. The walk's bounds concern the original body and argument trees. Substitution also preserves the model's typing, checking, -and equality judgments. Selected cheap-beta plans preserve the same meaning +and equality judgments beneath any retained dependent parameters, updating +each later domain. Selected cheap-beta plans preserve the same meaning when an actual source check is available. Declaration admission includes declared types reduced through a lambda prefix and a remaining suffix: the declaration's own executed type check @@ -62,9 +63,15 @@ actual checking origin. The inference proof preserves checked lambda domains; the raw reader derives the selected prefix and reduced expression. A syntactic transport moves the original check through interface extension, local weakening, and universe instantiation, and earlier local contexts come from -their executed domain checks. The lambda closes the reduced type using the -actual final intern table. Automatic origin construction for arbitrary -generated types, general reduction, and conversion remain open. +their executed domain checks. It now also carries codomain checks through +term substitution: the earlier function-type tree supplies the actual +codomain call, while the executed function and argument checks and hash +comparison establish membership in the substituted parameter's domain. +The mutual inference proof preserves this origin beneath remaining dependent +parameters. The lambda closes the reduced type using the actual final intern +table. Reduction remains bounded by the original checked lambda prefix. +Automatic origin construction for arbitrary generated types, general +reduction, and conversion remain open. Safe definition admission also rejects circular justification in both Lean and Rust, including `theorem loop : P := loop` with only `P : Prop` assumed. The production dependency walk returns an order with a proved decreasing rank; From 2ce0631434ed473e44233fc71860a2bd71317707 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 04:37:15 -0400 Subject: [PATCH 28/63] Justify type lambdas exposed by dependent arguments A checked codomain can have a variable head and acquire its first lambda only when an application substitutes a function argument. Retain that codomain's actual argument checks and the supplied argument's checked lambda domains, then derive the new beta prefix in the inference soundness proof. Extract origins through nested function-type checks. Preserve local lookup and argument typing through earlier parameter substitutions, and transport the resulting reduction through later dependent arguments. The lamBeta case consumes the new origin alongside its existing checked-prefix path. Validation: 554 exact consistency boundaries, 644 kernel unit checks, check-theory, IxKernelVerify/IxCompileVerify, and git diff --check pass. The 15 new regressions cover both cheap-beta plans, universe parameters, dependent arguments, cache clearing/reuse, scope cleanup, and bad domains. --- Ix/Kernel/Verify/Consistency.lean | 9 +- Ix/Kernel/Verify/Consistency/Audit.lean | 34 +- .../Consistency/SynthesisInference.lean | 618 ++++++++++++++++-- Ix/Theory/Model/BetaSpine.lean | 99 +++ Ix/Theory/Model/BetaSubstitution.lean | 4 + Tests/Ix/Kernel/Consistency.lean | 169 ++++- docs/kernel-verification.md | 48 +- docs/theory.md | 10 +- 8 files changed, 914 insertions(+), 77 deletions(-) diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 1b4e32769..03e68f62a 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -94,8 +94,13 @@ instantiation. Earlier local contexts are reconstructed from their executed domain checks. Codomain origins are extracted from earlier function-type trees, and actual function/argument calls justify term substitution through them. The context relation updates later dependent parameters at any cutoff; -the original beta prefix is preserved by the same substitution. These -transports are proved sound in the inference recursion and consumed by the +the original beta prefix is preserved by the same substitution. Variable-headed +codomains retain their actual argument checks and local types, extracted +through nested function-type calls. Substituting a lambda for that head now +uses the argument's own checked prefix to justify the newly exposed beta +steps. Earlier parameter substitutions update the retained head type and +argument checks; later arguments continue transporting the reduction. These +origins are proved sound in the inference recursion and consumed by the lambda case. Abstraction uses the reduced type and the reduction's final intern table. Automatic origin construction for arbitrary generated types and general reduction remains open. diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 3079f4798..c539b01d5 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -387,6 +387,38 @@ private def substitutedOriginRoots : Array RootAllowance := #[ { root := ``SynthesisTypeCheck.forallBody, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } ] +private def exposedOriginRoots : Array RootAllowance := #[ + { root := ``Theory.Model.AExpr.liftN_zero, standardAxioms := #[``propext] }, + { root := ``Theory.Model.LambdaPrefix.liftN }, + { root := ``Theory.Model.LambdaPrefix.instL, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.ArgumentSpine.instAt, standardAxioms := standard }, + { root := ``Theory.Model.ContextSubstitution.removed_type, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.ContextSubstitution.instantiate_removed_type, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.ContextSubstitution.lookup_other, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.ContextSubstitution.lift_typing, standardAxioms := standard }, + { root := ``SynthesisArgumentSpineOrigin.weaken, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisArgumentSpineOrigin.instantiate, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisArgumentSpineOrigin.appendContext, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisArgumentSpineOrigin.extend, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisArgumentSpineOrigin.substituteAt, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisVariableSpineOrigin.appendContext, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisVariableSpineOrigin.instantiate, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisVariableSpineOrigin.extend, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisVariableSpineOrigin.substituteAt, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``ApplicationInferenceTrace.exposedTypeOriginAt, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``ApplicationInferenceTrace.substituteReductionOriginAt, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BinderInference.variableSpineOrigin, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.variableSpineOrigin, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisForallBodyCheck.variableSpine, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisCheckedOrigin.sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisArgumentSpineOrigin.sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisReductionOrigin.sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisScopedTypeCheck.sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisScopedTypeCheck.forallBody, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisScopedTypeCheck.variableSpine, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisTypeCheck.scoped, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -514,7 +546,7 @@ def roots : Array RootAllowance := #[ }) ++ productionRoots.map (fun root => { root, standardAxioms := standard, nativeAxioms := productionNative, forbiddenDependencies := forbiddenProduction -}) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots).map (fun allowance => { +}) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots ++ exposedOriginRoots).map (fun allowance => { allowance with forbiddenDependencies := forbiddenProduction }) ++ #[{ root := ``extend_atomic_definition, standardAxioms := standard }] diff --git a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean index 942d5a473..1fa42db93 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean @@ -118,9 +118,7 @@ inductive SynthesisInference {β : Type u} SynthesisInference resolve entries locals context bounds (fuel + 1) before (.lam name bi domain body info) (.lam condition A b) (.forallE condition A B) (.imax (readLevel trace.domainLevel) bodyLevel) | lamBeta {entries locals context bounds fuel before name bi domain body info A b condition - domainBoundLevel bodyLevel headCondition headDomain headBody arguments - earlier typeLocals typeContext typeBounds typeFuel typeBefore typeAfter typeSource typeResult - originCondition originDomain originBody originArguments originLevel originBound reducedLevel} + domainBoundLevel bodyLevel headCondition headDomain headBody arguments reducedLevel} (full : before.inferOnly = false) (miss : UncachedInference before (.lam name bi domain body info)) (trace : LambdaBodyTrace fuel miss.keyed name bi domain body) @@ -131,22 +129,8 @@ inductive SynthesisInference {β : Type u} (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) (readLevel trace.domainLevel :: bounds) fuel trace.openedState trace.opened b ((AExpr.lam headCondition headDomain headBody).appN arguments) bodyLevel) - (typeContextSupport : SynthesisContext resolve entries context bounds earlier typeContext typeBounds) - (typeTree : SynthesisInference resolve earlier typeLocals typeContext typeBounds typeFuel typeBefore - typeSource ((AExpr.lam originCondition originDomain originBody).appN originArguments) - (.sort originLevel) originBound) - (typeAgreement : LocalContextReading resolve typeLocals typeBefore.lctx typeContext) - (typeReading : readScopedExpr? resolve typeLocals typeSource = - some ((AExpr.lam originCondition originDomain originBody).appN originArguments).erase) - (typeRun : RecM.infer typeSource (methodsN typeFuel) typeBefore = .ok typeResult typeAfter) - (originPrefix : cheapBetaCount trace.bodyType ≤ originBody.lambdaDepth + 1) - (transport : SynthesisTypeTransport resolve entries context bounds earlier typeContext - ((AExpr.lam originCondition originDomain originBody).appN originArguments) - (AExpr.betaPrefix (cheapBetaCount trace.bodyType) (.lam originCondition originDomain originBody) - originArguments) originLevel entries (context.push A) - ((AExpr.lam headCondition headDomain headBody).appN arguments) - (AExpr.betaPrefix (cheapBetaCount trace.bodyType) (.lam headCondition headDomain headBody) arguments) - reducedLevel) + (origin : SynthesisReductionOrigin resolve entries context bounds entries (context.push A) + (.lam headCondition headDomain headBody) arguments (cheapBetaCount trace.bodyType) reducedLevel) (reduction : CheapBetaSupport trace.bodyType trace.bodyState.env.intern) (conditionAgrees : condition = Certified.zeroCondition reducedLevel) (constructed : trace.reduced.1.Constructed) @@ -229,29 +213,9 @@ of an earlier declaration's type. No semantic typing field is accepted. -/ inductive SynthesisTypingOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : Model.Environment β → Model.Context β → List VLevel → Model.Environment β → Model.Context β → AExpr β → AExpr β → Type u - | checked {incoming incomingContext incomingBounds entries context bounds locals fuel before after source result - term type level} - (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = some term.erase) - (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + | source {incoming incomingContext incomingBounds entries context term type} + (check : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type) : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type - | applicationArgument {incoming incomingContext incomingBounds entries context bounds locals fuel before fn arg - f a domain argumentType body condition functionLevel argumentLevel} - (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (trace : ApplicationInferenceTrace fuel before fn arg) - (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn - f (.forallE condition domain body) functionLevel) - (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState arg - a argumentType argumentLevel) - (agreement : LocalContextReading resolve locals before.lctx context) - (functionReading : readScopedExpr? resolve locals fn = some f.erase) - (argumentReading : readScopedExpr? resolve locals arg = some a.erase) - (conditions : argumentType.annotations = domain.annotations) - (hashPath : (trace.argumentType == trace.domain) = true) - (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context a domain | weaken {incoming incomingContext incomingBounds entries context term type} (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) (domain : AExpr β) : @@ -287,8 +251,244 @@ inductive SynthesisTypingOrigin {β : Type u} (resolve : Address → Option (Con SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries targetContext (term.inst argument cutoff) (type.inst argument cutoff) +/-- Actual inference calls retain the syntactic lambda domains of their +source term. Application arguments additionally retain the comparison with +the function's domain, before any later transport of this checking origin. -/ +inductive SynthesisCheckedOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → + Model.Environment β → Model.Context β → AExpr β → AExpr β → Type u + | checked {incoming incomingContext incomingBounds entries context bounds locals fuel before after source result + term type level} + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type + | applicationArgument {incoming incomingContext incomingBounds entries context bounds locals fuel before fn arg + f a domain argumentType body condition functionLevel argumentLevel} + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (trace : ApplicationInferenceTrace fuel before fn arg) + (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn + f (.forallE condition domain body) functionLevel) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState arg + a argumentType argumentLevel) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals arg = some a.erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) : + SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context a domain + | binderArgument {incoming incomingContext incomingBounds entries context locals fuel before fn arg + f a domain argumentType body condition} + (trace : ApplicationInferenceTrace fuel before fn arg) + (functionTree : BinderInference resolve entries locals context fuel before fn + f (.forallE condition domain body)) + (head : SynthesisHead f) + (argumentTree : BinderInference resolve entries locals context fuel trace.functionState arg a argumentType) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals arg = some a.erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) : + SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context a domain + +/-- The argument checks along an actual application spine, retained in +application order so later substitutions can update each dependent type. -/ +inductive SynthesisArgumentSpineOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → + Model.Environment β → Model.Context β → AExpr β → List (AExpr β) → AExpr β → Type u + | nil {incoming incomingContext incomingBounds entries context} (type : AExpr β) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context type [] type + | snoc {incoming incomingContext incomingBounds entries context start arguments condition domain body argument} + (prior : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context + start arguments (.forallE condition domain body)) + (checked : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context argument domain) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context + start (arguments ++ [argument]) (body.inst argument) + +/-- A beta reduction can come from an earlier checked lambda prefix, or +from an actual lambda argument substituted for the head of a checked +variable application. Both cases retain the checks of all applied arguments. -/ +inductive SynthesisReductionOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → + Model.Environment β → Model.Context β → AExpr β → List (AExpr β) → Nat → VLevel → Type u + | checked {incoming incomingContext incomingBounds entries context head arguments count level + earlier typeLocals typeContext typeBounds typeFuel typeBefore typeAfter typeSource typeResult + originCondition originDomain originBody originArguments originLevel originBound} + (typeContextSupport : SynthesisContext resolve incoming incomingContext incomingBounds + earlier typeContext typeBounds) + (typeTree : SynthesisInference resolve earlier typeLocals typeContext typeBounds typeFuel typeBefore + typeSource ((AExpr.lam originCondition originDomain originBody).appN originArguments) + (.sort originLevel) originBound) + (typeAgreement : LocalContextReading resolve typeLocals typeBefore.lctx typeContext) + (typeReading : readScopedExpr? resolve typeLocals typeSource = + some ((AExpr.lam originCondition originDomain originBody).appN originArguments).erase) + (typeRun : RecM.infer typeSource (methodsN typeFuel) typeBefore = .ok typeResult typeAfter) + (originPrefix : count ≤ originBody.lambdaDepth + 1) + (transport : SynthesisTypeTransport resolve incoming incomingContext incomingBounds earlier typeContext + ((AExpr.lam originCondition originDomain originBody).appN originArguments) + (AExpr.betaPrefix count (.lam originCondition originDomain originBody) originArguments) originLevel + entries context (head.appN arguments) (AExpr.betaPrefix count head arguments) level) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries context head arguments count level + | substitutedVariable {incoming incomingContext incomingBounds entries base source target + type domain argument arguments cutoff count level} + (spine : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries source type arguments (.sort level)) + (atIndex : source[cutoff]? = some type) + (checked : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries base argument domain) + (substitution : ContextSubstitution base domain argument source target cutoff) + (enough : count ≤ argument.lambdaDepth) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries target + (argument.liftN cutoff) (arguments.map (AExpr.inst · argument cutoff)) count level + | map {incoming incomingContext incomingBounds earlier priorContext head arguments count bound + entries context current currentArguments level} + (prior : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds + earlier priorContext head arguments count bound) + (transport : SynthesisTypeTransport resolve incoming incomingContext incomingBounds earlier priorContext + (head.appN arguments) (AExpr.betaPrefix count head arguments) bound entries context + (current.appN currentArguments) (AExpr.betaPrefix count current currentArguments) level) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds + entries context current currentArguments count level + end +/-- Earlier argument checks can cross another local binder without any +new inference of their lifted expressions. -/ +def SynthesisArgumentSpineOrigin.weaken {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries context start arguments result) (domain : AExpr β) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries (context.push domain) + (start.liftN 1) (arguments.map (AExpr.liftN 1 ·)) (result.liftN 1) := + match support with + | .nil _ => .nil _ + | .snoc prior checked => by + simpa only [List.map_append, List.map_cons, List.map_nil, AExpr.liftN_inst_zero] using + (prior.weaken domain).snoc (checked.weaken domain) +termination_by structural support + +def SynthesisArgumentSpineOrigin.instantiate {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries context start arguments result) (levels : List VLevel) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries (context.map (AExpr.instL levels)) (start.instL levels) + (arguments.map (AExpr.instL levels)) (result.instL levels) := + match support with + | .nil _ => .nil _ + | .snoc prior checked => by + simpa only [List.map_append, List.map_cons, List.map_nil, AExpr.instL_inst] using + (prior.instantiate levels).snoc (checked.instantiate levels) +termination_by structural support + +def SynthesisArgumentSpineOrigin.appendContext {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries context start arguments result) (outer : Model.Context β) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries (context ++ outer) start arguments result := + match support with + | .nil _ => .nil _ + | .snoc prior checked => (prior.appendContext outer).snoc (checked.appendContext outer) +termination_by structural support + +def SynthesisArgumentSpineOrigin.extend {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming earlier entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + earlier context start arguments result) (extension : InterfaceExtends earlier entries) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context start arguments result := + match support with + | .nil _ => .nil _ + | .snoc prior checked => (prior.extend extension).snoc (checked.extend extension) +termination_by structural support + +def SynthesisArgumentSpineOrigin.substituteAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext base source target : Model.Context β} + {incomingBounds : List VLevel} {start result domain argument : AExpr β} + {arguments : List (AExpr β)} {cutoff : Nat} + (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries source start arguments result) + (value : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries base argument domain) + (substitution : ContextSubstitution base domain argument source target cutoff) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries target + (start.inst argument cutoff) (arguments.map (AExpr.inst · argument cutoff)) (result.inst argument cutoff) := + match support with + | .nil _ => .nil _ + | .snoc prior checked => by + simpa only [List.map_append, List.map_cons, List.map_nil, AExpr.inst_inst_zero] using + (prior.substituteAt value substitution).snoc (checked.substituteAt value substitution) +termination_by structural support + +/-- The head of a checked variable application uses the exact local type; +its argument checks remain available after leaving the original scope. -/ +structure SynthesisVariableSpineOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) + (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) + (entries : Model.Environment β) (context : Model.Context β) (index : Nat) + (arguments : List (AExpr β)) (type : AExpr β) where + headType : AExpr β + atIndex : context[index]? = some headType + spine : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries context headType arguments type + +def SynthesisVariableSpineOrigin.appendContext {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {type : AExpr β} {arguments : List (AExpr β)} {index : Nat} + (support : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries context index arguments type) (outer : Model.Context β) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries (context ++ outer) index arguments type := + { headType := support.headType + atIndex := by + rw [List.getElem?_append_left (List.getElem?_eq_some_iff.mp support.atIndex).1] + exact support.atIndex + spine := support.spine.appendContext outer } + +def SynthesisVariableSpineOrigin.instantiate {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {type : AExpr β} {arguments : List (AExpr β)} {index : Nat} + (support : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries context index arguments type) (levels : List VLevel) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries (context.map (AExpr.instL levels)) index (arguments.map (AExpr.instL levels)) (type.instL levels) := + { headType := support.headType.instL levels + atIndex := by simp only [List.getElem?_map, support.atIndex, Option.map_some] + spine := support.spine.instantiate levels } + +def SynthesisVariableSpineOrigin.extend {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming earlier entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {type : AExpr β} {arguments : List (AExpr β)} {index : Nat} + (support : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + earlier context index arguments type) (extension : InterfaceExtends earlier entries) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries context index arguments type := + { headType := support.headType + atIndex := support.atIndex + spine := support.spine.extend extension } + +/-- Applying an earlier parameter preserves a later variable-headed +codomain and updates every retained argument check in its dependent context. -/ +def SynthesisVariableSpineOrigin.substituteAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext base source target : Model.Context β} + {incomingBounds : List VLevel} {type domain argument : AExpr β} + {arguments : List (AExpr β)} {index cutoff : Nat} + (support : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries source index arguments type) + (value : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries base argument domain) + (substitution : ContextSubstitution base domain argument source target cutoff) + (distinct : index ≠ cutoff) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries target + (if index < cutoff then index else index - 1) + (arguments.map (AExpr.inst · argument cutoff)) (type.inst argument cutoff) := + { headType := support.headType.inst argument cutoff + atIndex := substitution.lookup_other support.atIndex distinct + spine := support.spine.substituteAt value substitution } + /-- The checked argument transports an already justified original beta prefix to its substituted application result. The two resulting expressions are computed by the proved substitution laws. -/ @@ -345,8 +545,8 @@ def ApplicationInferenceTrace.substituteTypeOriginAt {β : Type u} {resolve : Ad (AExpr.betaPrefix count (head.inst argument cutoff) (arguments.map (AExpr.inst · argument cutoff))) bound := prior.substitutePrefixAt enough - (.applicationArgument argumentContext trace functionTree argumentTree agreement functionReading argumentReading - conditions hashPath comparisonFaithful) substitution + (.source (.applicationArgument argumentContext trace functionTree argumentTree agreement + functionReading argumentReading conditions hashPath comparisonFaithful)) substitution /-- Reuse a codomain check from an earlier closed function type at the actual application site. The old function parameter keeps index zero while @@ -385,6 +585,68 @@ def ApplicationInferenceTrace.substituteTypeOrigin {β : Type u} {resolve : Addr exact trace.substituteTypeOriginAt (.pure imported) enough argumentContext functionTree argumentTree agreement functionReading argumentReading conditions hashPath comparisonFaithful .root +/-- Substituting an actual lambda argument for a checked variable head +exposes its own checked prefix, even though the original type had no leading +lambda. Remaining dependent parameters are handled at the same cutoff. -/ +def ApplicationInferenceTrace.exposedTypeOriginAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} + {incomingContext context sourceContext targetContext : Model.Context β} + {incomingBounds bounds : List VLevel} {domain argumentType argument f body : AExpr β} + {condition : Certified.PropWhen} {arguments : List (AExpr β)} + {level functionBound argumentBound : VLevel} {count cutoff fuel : Nat} + {locals : List FVarId} {before : TcState .anon} {fn rawArgument : KExpr .anon} + (trace : ApplicationInferenceTrace fuel before fn rawArgument) + (origin : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries sourceContext cutoff arguments (.sort level)) + (enough : count ≤ argument.lambdaDepth) + (argumentContext : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn + f (.forallE condition domain body) functionBound) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState rawArgument + argument argumentType argumentBound) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals rawArgument = some argument.erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) + (substitution : ContextSubstitution context domain argument sourceContext targetContext cutoff) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries targetContext + (argument.liftN cutoff) (arguments.map (AExpr.inst · argument cutoff)) count level := + .substitutedVariable origin.spine origin.atIndex + (.applicationArgument argumentContext trace functionTree argumentTree agreement functionReading argumentReading + conditions hashPath comparisonFaithful) substitution enough + +/-- Later actual arguments continue transporting an exposed prefix and +its reduction, including through dependent domains still left open. -/ +def ApplicationInferenceTrace.substituteReductionOriginAt {β : Type u} + {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} + {incomingContext context sourceContext targetContext : Model.Context β} + {incomingBounds bounds : List VLevel} {head domain argumentType argument f body : AExpr β} + {condition : Certified.PropWhen} {arguments : List (AExpr β)} + {level functionBound argumentBound : VLevel} {count cutoff fuel : Nat} + {locals : List FVarId} {before : TcState .anon} {fn rawArgument : KExpr .anon} + (trace : ApplicationInferenceTrace fuel before fn rawArgument) + (origin : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds + entries sourceContext head arguments count level) + (enough : count ≤ head.lambdaDepth) + (argumentContext : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn + f (.forallE condition domain body) functionBound) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState rawArgument + argument argumentType argumentBound) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals rawArgument = some argument.erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) + (substitution : ContextSubstitution context domain argument sourceContext targetContext cutoff) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries targetContext + (head.inst argument cutoff) (arguments.map (AExpr.inst · argument cutoff)) count level := + origin.map (trace.substituteTypeOriginAt (.pure .refl) enough argumentContext functionTree argumentTree + agreement functionReading argumentReading conditions hashPath comparisonFaithful substitution) + /-- The actual codomain call retained inside a function-type check. Its local context is reconstructed from that same call's domain check. -/ structure SynthesisForallBodyCheck {β : Type u} (resolve : Address → Option (ConstRef β)) @@ -490,6 +752,102 @@ private theorem list_reverse_induction {α : Type u} {motive : List α → Prop} simpa only [List.reverse_cons] using append_singleton items.reverse item ih simpa using reversed values.reverse +private theorem not_variable_spine {β : Type u} {term : AExpr β} + (notApp : ∀ fn arg, term ≠ .app fn arg) (notVar : ∀ index, term ≠ .bvar index) + (index : Nat) (arguments : List (AExpr β)) : term ≠ (AExpr.bvar index).appN arguments := by + intro same + induction arguments using list_reverse_induction with + | nil => exact notVar index same + | append_singleton arguments argument ih => + exact notApp _ _ (by simpa only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] using same) + +private theorem bvar_variable_spine {β : Type u} {left right : Nat} {arguments : List (AExpr β)} + (same : AExpr.bvar left = (AExpr.bvar right).appN arguments) : arguments = [] ∧ left = right := by + induction arguments using list_reverse_induction with + | nil => exact ⟨rfl, AExpr.bvar.inj same⟩ + | append_singleton arguments argument ih => + simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] at same + cases same + +private theorem app_variable_spine {β : Type u} {fn arg : AExpr β} {index : Nat} + {arguments : List (AExpr β)} (same : fn.app arg = (AExpr.bvar index).appN arguments) : + arguments = arguments.dropLast ++ [arg] ∧ fn = (AExpr.bvar index).appN arguments.dropLast := by + induction arguments using list_reverse_induction with + | nil => cases same + | append_singleton arguments argument ih => + simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil, AExpr.app.injEq] at same + simp only [List.dropLast_concat] + exact ⟨by rw [same.2], same.1⟩ + +/-- Extract the actual argument calls from a checked variable application. +No type check on the generated substitutions is added. -/ +def BinderInference.variableSpineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source term type) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (index : Nat) (arguments : List (AExpr β)) (headEquals : term = (AExpr.bvar index).appN arguments) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries context index arguments type := + match support with + | .fvar _ _ atIndex => by + obtain ⟨rfl, rfl⟩ := bvar_variable_spine headEquals + exact ⟨_, atIndex, .nil _⟩ + | .app _ miss trace functionTree head argumentTree conditions hashPath comparisonFaithful + _ _ _ _ _ _ => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have parts := app_variable_spine headEquals + have prior := functionTree.variableSpineOrigin (incoming := incoming) (incomingContext := incomingContext) + (incomingBounds := incomingBounds) keyedAgreement functionReading index arguments.dropLast parts.2 + have spine := prior.spine.snoc (.source (.binderArgument trace functionTree head argumentTree + keyedAgreement functionReading argumentReading conditions hashPath comparisonFaithful)) + exact ⟨prior.headType, prior.atIndex, by simpa only [← parts.1] using spine⟩ + | .sort .. | .cachedSort .. | .const .. | .polymorphic .. | .cachedConst .. | .forallE .. | .lam .. => by + exact False.elim (not_variable_spine (by intro fn arg same; cases same) + (by intro index same; cases same) index arguments headEquals) +termination_by structural support + +def SynthesisInference.variableSpineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (index : Nat) (arguments : List (AExpr β)) (headEquals : term = (AExpr.bvar index).appN arguments) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries context index arguments type := + match support with + | .known inference _ | .reuseType inference .. | .fvar inference .. => + inference.variableSpineOrigin agreement reading index arguments headEquals + | .app _ miss trace functionTree argumentTree conditions hashPath comparisonFaithful _ _ _ _ _ _ => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have parts := app_variable_spine headEquals + have prior := functionTree.variableSpineOrigin contextOrigin keyedAgreement functionReading + index arguments.dropLast parts.2 + have spine := prior.spine.snoc (.source (.applicationArgument contextOrigin trace functionTree argumentTree + keyedAgreement functionReading argumentReading conditions hashPath comparisonFaithful)) + exact ⟨prior.headType, prior.atIndex, by simpa only [← parts.1] using spine⟩ + | .forallE .. | .lam .. | .lamBeta .. => by + exact False.elim (not_variable_spine (by intro fn arg same; cases same) + (by intro index same; cases same) index arguments headEquals) +termination_by structural support + +def SynthesisForallBodyCheck.variableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {condition : Certified.PropWhen} {domain : AExpr β} + {index : Nat} {arguments : List (AExpr β)} + (check : SynthesisForallBodyCheck resolve entries context bounds condition domain + ((AExpr.bvar index).appN arguments)) + (parentOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries + (context.push domain) index arguments (.sort check.bodyLevel) := + check.tree.variableSpineOrigin (parentOrigin.compose check.contextOrigin) check.agreement check.reading + index arguments rfl + theorem BinderInference.lambdaPrefix {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {fuel : Nat} @@ -510,7 +868,7 @@ theorem SynthesisInference.lambdaPrefix {β : Type u} | .known inference _ => inference.lambdaPrefix | .reuseType inference _ _ _ _ _ => inference.lambdaPrefix | .lam _ _ _ _ _ _ bodyTree _ _ _ _ _ _ => .lam bodyTree.lambdaPrefix - | .lamBeta _ _ _ _ _ _ bodyTree _ _ _ _ _ _ _ _ _ _ _ _ _ => by + | .lamBeta _ _ _ _ _ _ bodyTree _ _ _ _ _ _ _ => by have depth := bodyTree.lambdaPrefix.lambdaDepth_zero (AExpr.appN_ne_forallE (by intro condition domain body same; cases same) _) simpa only [AExpr.lambdaDepth, depth] using LambdaPrefix.lam (LambdaPrefix.zero _ _) @@ -657,8 +1015,7 @@ theorem SynthesisInference.soundWithSpine {β : Type u} rw [trace.output run, internExpr_readScopedExpr? (table := trace.abstracted.2) closedCoherent faithful] simp [LambdaInferenceTrace.abstracted, domainReads, closedReads, AExpr.erase] - | .lamBeta full miss trace opening absent domainTree bodyTree typeContextSupport typeTree - typeAgreement typeReading typeRun originPrefix transport reduction conditionAgrees + | .lamBeta full miss trace opening absent domainTree bodyTree origin reduction conditionAgrees constructed bound closingFaithful faithful => by obtain ⟨state, run⟩ := infer_uncached_success miss accepted rw [full] at run @@ -671,10 +1028,7 @@ theorem SynthesisInference.soundWithSpine {β : Type u} openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun obtain ⟨bodyTypeReads, bodyTyped, _, _⟩ := bodyTree.soundWithSpine (formed.push domainTyped) openedAgreement openedReads trace.bodyRun - obtain ⟨_, _, _, originSpine⟩ := typeTree.soundWithSpine - (typeContextSupport.sound formed) typeAgreement typeReading typeRun - obtain ⟨originConversion, originReducedTyped⟩ := originSpine.betaPrefix originPrefix - obtain ⟨conversion, reducedTyped⟩ := transport.sound formed originConversion originReducedTyped + obtain ⟨conversion, reducedTyped⟩ := origin.sound formed obtain ⟨_, reducedReads, reducedCoherent⟩ := reduction.reading bodyTypeReads obtain ⟨closedReads, closedCoherent⟩ := abstractFVars_readScopedExpr? constructed bound reducedCoherent closingFaithful reducedReads @@ -740,8 +1094,25 @@ theorem SynthesisTypingOrigin.sound {β : Type u} {resolve : Address → Option (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : TypingClaim.{u,v} entries context term type := match support with + | .source check => (check.sound formed).1 + | .weaken prior domain => typing_weaken (prior.sound formed) + | .instantiate prior arguments => typing_instL_context (prior.sound formed) arguments + | .appendContext prior outer => typing_append_context (prior.sound formed) outer + | .extend prior extension => extension.typing (prior.sound formed) + | .termEquivalent prior same => same.termTyping (prior.sound formed) + | .typeEquivalent prior same => same.typing (prior.sound formed) + | .substituteAt body value substitution => (body.sound formed).instAt (value.sound formed) substitution +termination_by structural support + +theorem SynthesisCheckedOrigin.sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} + (support : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type) + (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : + TypingClaim.{u,v} entries context term type ∧ LambdaPrefix term type term.lambdaDepth := + match support with | .checked contextOrigin tree agreement reading accepted => - (tree.soundWithSpine (contextOrigin.sound formed) agreement reading accepted).2.1 + ⟨(tree.soundWithSpine (contextOrigin.sound formed) agreement reading accepted).2.1, tree.lambdaPrefix⟩ | .applicationArgument contextOrigin trace functionTree argumentTree agreement functionReading argumentReading conditions hashPath comparisonFaithful => by have contextFormation := contextOrigin.sound formed @@ -754,14 +1125,59 @@ theorem SynthesisTypingOrigin.sound {β : Type u} {resolve : Address → Option have sameType := AExpr.eq_of_erase_annotations (Option.some.inj (argumentTypeReads.symm.trans ((beq_readScopedExpr? comparisonFaithful hashPath).trans domainReads))) conditions - exact sameType ▸ argumentTyped - | .weaken prior domain => typing_weaken (prior.sound formed) - | .instantiate prior arguments => typing_instL_context (prior.sound formed) arguments - | .appendContext prior outer => typing_append_context (prior.sound formed) outer - | .extend prior extension => extension.typing (prior.sound formed) - | .termEquivalent prior same => same.termTyping (prior.sound formed) - | .typeEquivalent prior same => same.typing (prior.sound formed) - | .substituteAt body value substitution => (body.sound formed).instAt (value.sound formed) substitution + exact ⟨sameType ▸ argumentTyped, sameType ▸ argumentTree.lambdaPrefix⟩ + | .binderArgument trace functionTree head argumentTree agreement functionReading argumentReading + conditions hashPath comparisonFaithful => by + obtain ⟨functionTypeReads, functionTyped⟩ := + functionTree.synthesis head agreement functionReading trace.functionRun + have domainReads := (readScopedExpr?_all_parts functionTypeReads).1 + obtain ⟨argumentTypeReads, argumentChecked⟩ := + argumentTree.sound (trace.contextPreserved.symm ▸ agreement) argumentReading trace.argumentRun + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (argumentTypeReads.symm.trans + ((beq_readScopedExpr? comparisonFaithful hashPath).trans domainReads))) conditions + have checked := sameType ▸ argumentChecked + refine ⟨?_, sameType ▸ argumentTree.lambdaPrefix⟩ + intro V _ constants realizes levels env valid + have domainValid := (functionTyped V constants realizes levels env valid).2.1.1 + have checkedAt := checked V constants realizes levels env valid domainValid + exact ⟨checkedAt.1, domainValid, checkedAt.2⟩ +termination_by structural support + +theorem SynthesisArgumentSpineOrigin.sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries context start arguments result) + (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : + ArgumentSpine.{u,v} entries context start arguments result := + match support with + | .nil _ => .nil _ + | .snoc prior checked => (prior.sound formed).append (.cons (checked.sound formed) (.nil _)) +termination_by structural support + +theorem SynthesisReductionOrigin.sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {head : AExpr β} {arguments : List (AExpr β)} {count : Nat} {level : VLevel} + (support : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds + entries context head arguments count level) + (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : + ConversionClaim.{u,v} entries context (head.appN arguments) (AExpr.betaPrefix count head arguments) ∧ + TypingClaim.{u,v} entries context (AExpr.betaPrefix count head arguments) (.sort level) := + match support with + | .checked typeContextSupport typeTree typeAgreement typeReading typeRun originPrefix transport => by + obtain ⟨_, _, _, originSpine⟩ := typeTree.soundWithSpine + (typeContextSupport.sound formed) typeAgreement typeReading typeRun + obtain ⟨originConversion, originReducedTyped⟩ := originSpine.betaPrefix originPrefix + exact transport.sound formed originConversion originReducedTyped + | .substitutedVariable spine atIndex checked substitution enough => by + obtain ⟨typed, leading⟩ := checked.sound formed + have instantiated := (spine.sound formed).instAt typed substitution + rw [substitution.instantiate_removed_type atIndex] at instantiated + exact ((leading.liftN _ 0).truncate enough).beta_sound (substitution.lift_typing typed) instantiated + | .map prior transport => by + obtain ⟨converted, typed⟩ := prior.sound formed + exact transport.sound formed converted typed termination_by structural support end @@ -807,6 +1223,69 @@ def SynthesisInference.ofSort {β : Type u} SynthesisInference resolve entries locals context bounds fuel before source term (.sort level) (.succ level) := .known inference (.sort level) +/-- An executed type check with its original local scope. Walking a +dependent function type keeps the actual domain checks that established +each nested context. -/ +structure SynthesisScopedTypeCheck {β : Type u} (resolve : Address → Option (ConstRef β)) + (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) + (entries : Model.Environment β) (type : AExpr β) where + context : Model.Context β + bounds : List VLevel + level : VLevel + bound : VLevel + locals : List FVarId + fuel : Nat + before : TcState .anon + after : TcState .anon + source : KExpr .anon + result : KExpr .anon + contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds + tree : SynthesisInference resolve entries locals context bounds fuel before source type (.sort level) bound + agreement : LocalContextReading resolve locals before.lctx context + reading : readScopedExpr? resolve locals source = some type.erase + run : RecM.infer source (methodsN fuel) before = .ok result after + +theorem SynthesisScopedTypeCheck.sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext : Model.Context β} + {incomingBounds : List VLevel} {type : AExpr β} + (check : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries type) + (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : + TypingClaim.{u,v} entries check.context type (.sort check.level) := + (check.tree.sound (check.contextOrigin.sound formed) check.agreement check.reading check.run).2.1 + +def SynthesisScopedTypeCheck.forallBody {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext : Model.Context β} + {incomingBounds : List VLevel} {condition : Certified.PropWhen} {domain body : AExpr β} + (check : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds + entries (.forallE condition domain body)) : + SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries body := by + let child := check.tree.forallBodyCheck check.agreement check.reading + exact { + context := Context.push domain check.context + bounds := child.domainLevel :: check.bounds + level := child.bodyLevel + bound := child.bound + locals := child.locals + fuel := child.fuel + before := child.before + after := child.after + source := child.source + result := child.result + contextOrigin := check.contextOrigin.compose child.contextOrigin + tree := child.tree + agreement := child.agreement + reading := child.reading + run := child.run } + +def SynthesisScopedTypeCheck.variableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext : Model.Context β} + {incomingBounds : List VLevel} {index : Nat} {arguments : List (AExpr β)} + (check : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds + entries ((AExpr.bvar index).appN arguments)) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries + check.context index arguments (.sort check.level) := + check.tree.variableSpineOrigin check.contextOrigin check.agreement check.reading index arguments rfl + /-- Package an executed closed type check so later declarations can retain its origin. The check itself may contain direct lambda applications. -/ structure SynthesisTypeCheck {β : Type u} (resolve : Address → Option (ConstRef β)) @@ -827,6 +1306,27 @@ theorem SynthesisTypeCheck.sound {β : Type u} {resolve : Address → Option (Co TypingClaim.{u,v} entries [] type (.sort level) := (check.inference.closed_sound check.reading check.run).2.1 +def SynthesisTypeCheck.scoped {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext : Model.Context β} + {incomingBounds : List VLevel} {type : AExpr β} {level : VLevel} + (check : SynthesisTypeCheck resolve entries type level) : + SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries type := + { context := [] + bounds := [] + level := level + bound := check.bound + locals := [] + fuel := check.fuel + before := check.before + after := check.after + source := check.source + result := check.result + contextOrigin := .empty entries + tree := check.inference + agreement := .empty _ _ + reading := check.reading + run := check.run } + def SynthesisTypeCheck.forallBody {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {condition : Certified.PropWhen} {domain body : AExpr β} {level : VLevel} (check : SynthesisTypeCheck resolve entries (.forallE condition domain body) level) : diff --git a/Ix/Theory/Model/BetaSpine.lean b/Ix/Theory/Model/BetaSpine.lean index bbbb8680e..132a824b9 100644 --- a/Ix/Theory/Model/BetaSpine.lean +++ b/Ix/Theory/Model/BetaSpine.lean @@ -133,6 +133,20 @@ theorem LambdaPrefix.inst {β : Type u} {term type : AExpr β} {count : Nat} | zero => exact .zero _ _ | lam inner ih => exact .lam (ih (cutoff + 1)) +theorem LambdaPrefix.liftN {β : Type u} {term type : AExpr β} {count : Nat} + (leading : LambdaPrefix term type count) (inserted cutoff : Nat) : + LambdaPrefix (term.liftN inserted cutoff) (type.liftN inserted cutoff) count := by + induction leading generalizing cutoff with + | zero => exact .zero _ _ + | lam inner ih => exact .lam (ih (cutoff + 1)) + +theorem LambdaPrefix.instL {β : Type u} {term type : AExpr β} {count : Nat} + (leading : LambdaPrefix term type count) (arguments : List VLevel) : + LambdaPrefix (term.instL arguments) (type.instL arguments) count := by + induction leading with + | zero => exact .zero _ _ + | lam inner ih => exact .lam ih + theorem LambdaPrefix.truncate {β : Type u} {term type : AExpr β} {total count : Nat} (leading : LambdaPrefix term type total) (enough : count ≤ total) : LambdaPrefix term type count := by @@ -242,6 +256,91 @@ theorem ArgumentSpine.typing {β : Type u} {entries : Environment β} {context : | nil => exact typed | cons argumentTyped tail ih => exact ih (typed.app argumentTyped) +/-- Substitution updates the type at every step of a dependent argument +spine, including the type expected by the next application. -/ +theorem ArgumentSpine.instAt {β : Type u} {entries : Environment β} + {base source target : Context β} {type result argument domain : AExpr β} + {arguments : List (AExpr β)} {cutoff : Nat} + (spine : ArgumentSpine.{u,v} entries source type arguments result) + (value : TypingClaim.{u,v} entries base argument domain) + (substitution : ContextSubstitution base domain argument source target cutoff) : + ArgumentSpine.{u,v} entries target (type.inst argument cutoff) + (arguments.map (AExpr.inst · argument cutoff)) (result.inst argument cutoff) := by + induction spine with + | nil => exact .nil _ + | cons typed tail ih => + exact .cons (typed.instAt value substitution) + (by simpa only [AExpr.inst_inst_zero] using ih) + +theorem ContextSubstitution.removed_type {β : Type u} {base source target : Context β} + {domain argument : AExpr β} {cutoff : Nat} + (substitution : ContextSubstitution base domain argument source target cutoff) : + source[cutoff]? = some (domain.liftN (cutoff + 1)) := by + induction substitution with + | root => rfl + | @push source target cutoff prior binder ih => + simp only [Context.push, List.getElem?_cons_succ, List.getElem?_map, ih, Option.map_some, + AExpr.liftN_liftN_merge domain (cutoff + 1) 1 0 0 (Nat.le_refl _) (Nat.zero_le _)] + +theorem ContextSubstitution.instantiate_removed_type {β : Type u} {base source target : Context β} + {domain argument type : AExpr β} {cutoff : Nat} + (substitution : ContextSubstitution base domain argument source target cutoff) + (atIndex : source[cutoff]? = some type) : + type.inst argument cutoff = domain.liftN cutoff := by + have same := Option.some.inj (atIndex.symm.trans substitution.removed_type) + rw [same] + exact AExpr.inst_liftN_within domain argument cutoff 0 cutoff (Nat.zero_le _) (by omega) + +/-- Every retained local keeps its substituted type. Indices beyond the +removed parameter decrease by one; later parameters keep their indices. -/ +theorem ContextSubstitution.lookup_other {β : Type u} {base source target : Context β} + {domain argument type : AExpr β} {cutoff index : Nat} + (substitution : ContextSubstitution base domain argument source target cutoff) + (atIndex : source[index]? = some type) (distinct : index ≠ cutoff) : + target[if index < cutoff then index else index - 1]? = some (type.inst argument cutoff) := by + induction substitution generalizing index type with + | root => + cases index with + | zero => exact False.elim (distinct rfl) + | succ index => + simp only [Context.push, List.getElem?_cons_succ, List.getElem?_map] at atIndex + obtain ⟨type, sourceLookup, rfl⟩ := Option.map_eq_some_iff.mp atIndex + simpa only [Nat.not_lt_zero, ↓reduceIte, Nat.add_sub_cancel, + AExpr.inst_liftN_within type argument 0 0 0 (Nat.le_refl _) (by omega), + AExpr.liftN_zero] using sourceLookup + | @push source target cutoff prior binder ih => + cases index with + | zero => + simp only [Context.push, List.getElem?_cons_zero, Option.some.injEq] at atIndex + subst type + simpa only [Nat.zero_lt_succ, ↓reduceIte, Context.push, List.getElem?_cons_zero, + Nat.add_comm 1 cutoff] using + congrArg some (AExpr.inst_liftN binder argument 1 0 cutoff (Nat.zero_le _)).symm + | succ index => + simp only [Context.push, List.getElem?_cons_succ, List.getElem?_map] at atIndex + obtain ⟨type, sourceLookup, rfl⟩ := Option.map_eq_some_iff.mp atIndex + have retained := ih sourceLookup (show index ≠ cutoff by omega) + have nextIndex : (if index + 1 < cutoff + 1 then index + 1 else index + 1 - 1) = + (if index < cutoff then index else index - 1) + 1 := by + split <;> split <;> omega + rw [nextIndex] + simp only [Context.push, List.getElem?_cons_succ, List.getElem?_map, retained, + Option.map_some] + exact congrArg some (by + simpa only [Nat.add_comm 1 cutoff] using + (AExpr.inst_liftN type argument 1 0 cutoff (Nat.zero_le _)).symm) + +/-- Values from the base context can be used beneath the retained prefix. +Their types are lifted by the same number of dependent binders. -/ +theorem ContextSubstitution.lift_typing {β : Type u} {entries : Environment β} + {base source target : Context β} {domain argument term type : AExpr β} {cutoff : Nat} + (substitution : ContextSubstitution base domain argument source target cutoff) + (typed : TypingClaim.{u,v} entries base term type) : + TypingClaim.{u,v} entries target (term.liftN cutoff) (type.liftN cutoff) := by + intro V _ constants realizes levels env valid + simpa only [wellDenoted_liftN, interp_liftN] using + typed V constants realizes levels _ (substitution.base_valid valid) + theorem ConversionClaim.appN {β : Type u} {entries : Environment β} {context : Context β} {left right : AExpr β} (same : ConversionClaim.{u,v} entries context left right) (arguments : List (AExpr β)) : diff --git a/Ix/Theory/Model/BetaSubstitution.lean b/Ix/Theory/Model/BetaSubstitution.lean index 7c77c774b..b33ff91a3 100644 --- a/Ix/Theory/Model/BetaSubstitution.lean +++ b/Ix/Theory/Model/BetaSubstitution.lean @@ -12,6 +12,10 @@ namespace Ix.Theory.Model.AExpr universe u +@[simp] theorem liftN_zero (term : AExpr β) (cutoff : Nat := 0) : + term.liftN 0 cutoff = term := by + induction term generalizing cutoff <;> simp_all [liftN, liftVar] + theorem instL_liftN (term : AExpr β) (levels : List VLevel) (count cutoff : Nat) : (term.liftN count cutoff).instL levels = (term.instL levels).liftN count cutoff := by induction term generalizing cutoff <;> simp_all [liftN, instL] diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 9f5f8814f..e431e9ee6 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -746,6 +746,135 @@ private def cheapApplicationTypeResult (dependent : Bool) (level : Ixon.Univ := | .ok passed after => passed && after.lctx.size == 0 | .error _ _ => false +/-- The checked function type has a variable-headed codomain `F A` or +`F A B`. Substituting a lambda for `F` creates the beta prefix. The function +itself is the dependent identity, so its source axioms only declare carriers. -/ +private def exposedLambdaType (level : Ixon.Univ) (universes : UInt64 := 0) + (shape wrong : Nat := 0) : Ixon.Env × Address := Id.run do + let (env, carrier) := storeConst {} + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], #[level]⟩ + let (env, otherCarrier) := storeConst env + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], #[level, .succ level]⟩ + let arguments := if universes == 0 then #[] else #[0] + let familyType := Ixon.Expr.leanAll (.sort 0) + (if shape == 2 then .leanAll (.sort 0) (.sort 0) else .sort 0) + let applied : Ixon.Expr → Ixon.Expr := fun head => + if shape == 2 then .app (.app head (.ref 0 arguments)) (.ref 1 arguments) + else .app head (.ref (if shape == 1 then 1 else 0) arguments) + let (env, function) := storeConst env + ⟨.defn ⟨.defn, .safe, universes, + .leanAll familyType (.leanAll (applied (.var 0)) (applied (.var 1))), + .leanLam familyType (.leanLam (applied (.var 0)) (.var 0))⟩, + #[], #[carrier, otherCarrier], #[level]⟩ + let family := Ixon.Expr.leanLam (.sort 0) + (if shape == 2 then .leanLam (.sort 0) (.var 1) + else if shape == 1 then .ref 0 arguments else .var 0) + let supplied := if wrong == 1 then Ixon.Expr.leanLam (.sort 0) (.sort 0) else family + return storeConst env + ⟨.defn ⟨.defn, .safe, universes, + .leanAll (applied family) (.ref 0 arguments), + .leanLam (applied family) + (.app (.app (.ref 2 arguments) supplied) (if wrong == 2 then .ref 0 arguments else .var 0))⟩, + #[], #[carrier, otherCarrier, function], #[level]⟩ + +private def exposedLambdaTypeResult (shape : Nat) (level : Ixon.Univ := .zero) : Bool := + let (env, target) := exposedLambdaType level 0 shape + let action : RecM .anon Bool := RecM.withLctxScope do + let concrete ← TcM.getConst (m := .anon) ⟨target, ()⟩ + let .defn _ _ _ _ _ _ type value _ _ := concrete | return false + let .lam name bi domain body _ := value | return false + let .all _ _ _ expected _ := type | return false + let (fn, arguments) := body.collectSpine + let some supplied := arguments[0]? | return false + let .all fnName fnBi fnDomain fnBody _ ← RecM.inferCall fn | return false + let suppliedType ← RecM.inferCall supplied + let (openedType, originalHead, _) ← TcM.openBinderWithFV fnName fnBi fnDomain fnBody + let .all _ _ _ originalCodomain _ := openedType | return false + let (call, _) ← TcM.openBinder name bi domain body + let generated ← RecM.inferCall call + let (generatedHead, generatedArguments) := generated.collectSpine + let reduced ← TcM.runIntern (cheapBetaReduce generated) + let before ← get + let inferred ← RecM.inferCall value + let repeated ← RecM.inferCall value + let after ← get + return originalCodomain.collectSpine.1 == originalHead && (cheapBetaPlan? originalCodomain).isNone && + suppliedType == fnDomain && generatedHead == supplied && (cheapBetaPlan? generated).isSome && + (peelLamsN generatedArguments.size generatedHead).2 == (if shape == 2 then 2 else 1) && + generated != reduced && reduced == expected && inferred == type && repeated == type && + before.lctx.size == after.lctx.size + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +/-- The function parameter depends on two earlier arguments. Its original +codomain is `F x`; the supplied constant family creates a new beta step only +after substituting the carrier and its witness. -/ +private def dependentExposedLambdaType (level : Ixon.Univ) (universes : UInt64 := 0) + (wrong : Bool := false) : Ixon.Env × Address := Id.run do + let arguments := if universes == 0 then #[] else #[0] + let (env, carrier) := storeConst {} + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], #[level]⟩ + let (env, witness) := storeConst env + ⟨.axio ⟨false, universes, .ref 0 arguments⟩, #[], #[carrier], #[level]⟩ + let familyType := Ixon.Expr.leanAll (.var 1) (.sort 0) + let (env, function) := storeConst env + ⟨.defn ⟨.defn, .safe, universes, + .leanAll (.sort 0) (.leanAll (.var 0) + (.leanAll familyType (.leanAll (.app (.var 0) (.var 1)) (.app (.var 1) (.var 2))))), + .leanLam (.sort 0) (.leanLam (.var 0) + (.leanLam familyType (.leanLam (.app (.var 0) (.var 1)) (.var 0))))⟩, + #[], #[], #[level]⟩ + let family := Ixon.Expr.leanLam (.ref 0 arguments) (.ref 0 arguments) + let supplied := if wrong then Ixon.Expr.leanLam (.sort 0) (.ref 0 arguments) else family + let domain := Ixon.Expr.app family (.ref 1 arguments) + return storeConst env + ⟨.defn ⟨.defn, .safe, universes, .leanAll domain (.ref 0 arguments), + .leanLam domain (.app (.app (.app (.app (.ref 2 arguments) + (.ref 0 arguments)) (.ref 1 arguments)) supplied) (.var 0))⟩, + #[], #[carrier, witness, function], #[level]⟩ + +private def dependentExposedLambdaTypeResult (level : Ixon.Univ) : Bool := + let (env, target) := dependentExposedLambdaType level + let action : RecM .anon Bool := RecM.withLctxScope do + let concrete ← TcM.getConst (m := .anon) ⟨target, ()⟩ + let .defn _ _ _ _ _ _ type value _ _ := concrete | return false + let .lam name bi domain body _ := value | return false + let .all _ _ _ expected _ := type | return false + let (fn, arguments) := body.collectSpine + let some carrier := arguments[0]? | return false + let some witness := arguments[1]? | return false + let some family := arguments[2]? | return false + let .all aName aBi aDomain aBody _ ← RecM.inferCall fn | return false + let (afterA, originalA, _) ← TcM.openBinderWithFV aName aBi aDomain aBody + let .all xName xBi xDomain xBody _ := afterA | return false + let (afterX, originalX, _) ← TcM.openBinderWithFV xName xBi xDomain xBody + let .all fName fBi fDomain fBody _ := afterX | return false + let .all _ _ originalDomain _ _ := fDomain | return false + let (afterF, originalF, _) ← TcM.openBinderWithFV fName fBi fDomain fBody + let .all _ _ _ originalCodomain _ := afterF | return false + let appliedPrefix := KExpr.mkAppN fn #[carrier, witness] + let .all _ _ specializedDomain _ _ ← RecM.inferCall appliedPrefix | return false + let .all _ _ specializedCarrier _ _ := specializedDomain | return false + let familyType ← RecM.inferCall family + let (call, _) ← TcM.openBinder name bi domain body + let generated ← RecM.inferCall call + let (generatedHead, generatedArguments) := generated.collectSpine + let reduced ← TcM.runIntern (cheapBetaReduce generated) + let before ← get + let inferred ← RecM.inferCall value + let repeated ← RecM.inferCall value + let after ← get + return originalDomain == originalA && xDomain == originalA && + originalCodomain.collectSpine == (originalF, #[originalX]) && + (cheapBetaPlan? originalCodomain).isNone && specializedCarrier == carrier && + specializedDomain == familyType && generatedHead == family && generatedArguments == #[witness] && + (cheapBetaPlan? generated).isSome && generated != reduced && reduced == expected && + inferred == type && repeated == type && before.lctx.size == after.lctx.size + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + private def applicationCases : TestSeq := test "application environment: Prop/Type identity calls and transitive theorem calls check" (allSucceeded applicationEnvironment 5 { clearEvery := 0 }) @@ -875,6 +1004,44 @@ private def cheapApplicationCases : TestSeq := ++ test "application type beta: the first argument cannot replace the dependent second argument" (let (env, target) := cheapApplicationType .zero 0 true true; rowFailed env target) +private def exposedLambdaCases : TestSeq := + test "exposed type lambda: a function parameter becomes a lambda in Prop and Type" + (allSucceeded (exposedLambdaType .zero).1 4 && allSucceeded (exposedLambdaType (.succ .zero)).1 4) + ++ test "exposed type lambda: the substituted lambda retains a different captured carrier" + (allSucceeded (exposedLambdaType .zero 0 1).1 4 && + allSucceeded (exposedLambdaType (.succ .zero) 0 1).1 4) + ++ test "exposed type lambda: the argument contributes two checked leading lambdas" + (allSucceeded (exposedLambdaType .zero 0 2).1 4 && + allSucceeded (exposedLambdaType (.succ .zero) 0 2).1 4) + ++ test "exposed type lambda: the new prefixes retain universe parameters" + (allSucceeded (exposedLambdaType (.var 0) 1).1 4 && + allSucceeded (exposedLambdaType (.var 0) 1 1).1 4 && + allSucceeded (exposedLambdaType (.var 0) 1 2).1 4) + ++ test "exposed type lambda: later dependent arguments work with fresh per-item caches" + (allSucceeded (exposedLambdaType .zero).1 4 { clearEvery := 1 } && + allSucceeded (exposedLambdaType .zero 0 2).1 4 { clearEvery := 1 }) + ++ test "exposed type lambda: a variable-headed original type gains one beta step" + (exposedLambdaTypeResult 0 && exposedLambdaTypeResult 0 (.succ .zero)) + ++ test "exposed type lambda: the closed-body plan preserves the captured carrier" + (exposedLambdaTypeResult 1 && exposedLambdaTypeResult 1 (.succ .zero)) + ++ test "exposed type lambda: the variable plan consumes the new two-lambda prefix in order" + (exposedLambdaTypeResult 2 && exposedLambdaTypeResult 2 (.succ .zero)) + ++ test "exposed type lambda: a lambda with the wrong function type is rejected" + (let (env, target) := exposedLambdaType .zero 0 0 1; rowFailed env target) + ++ test "exposed type lambda: a carrier cannot replace the later dependent witness" + (let (env, target) := exposedLambdaType .zero 0 2 2; rowFailed env target) + ++ test "exposed type lambda: earlier arguments specialize the function parameter in Prop and Type" + (allSucceeded (dependentExposedLambdaType .zero).1 4 && + allSucceeded (dependentExposedLambdaType (.succ .zero)).1 4) + ++ test "exposed type lambda: earlier dependent substitutions preserve universe parameters" + (allSucceeded (dependentExposedLambdaType (.var 0) 1).1 4) + ++ test "exposed type lambda: earlier dependent substitutions survive fresh per-item caches" + (allSucceeded (dependentExposedLambdaType .zero).1 4 { clearEvery := 1 }) + ++ test "exposed type lambda: the earlier carrier and witness reach the new beta step exactly" + (dependentExposedLambdaTypeResult .zero && dependentExposedLambdaTypeResult (.succ .zero)) + ++ test "exposed type lambda: a supplied family must use its specialized dependent domain" + (let (env, target) := dependentExposedLambdaType .zero 0 true; rowFailed env target) + /-- Call a polymorphic identity from a monomorphic function body. Universe indices select entries in the declaration's explicit level table. -/ private def storePolymorphicCall (env : Ixon.Env) (identity : Address) @@ -2467,7 +2634,7 @@ private def polymorphicDefinitionCases : TestSeq := public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, multiBetaCases, cheapLambdaCases, - cheapApplicationCases, + cheapApplicationCases, exposedLambdaCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, sourceAgreementCases, sourceCacheCases, polymorphicDefinitionCases] diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 92b1a4588..d97a7e4b6 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -364,9 +364,9 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) checks retain their source, states, successful run, and inference tree; interface extension preserves their entries exactly. These are execution resources, not semantic formation assumptions. The type checks can themselves - contain direct lambda applications. The `lamBeta` rule retains an actual - type-check tree when cheap beta changes the body's inferred type. Its source - and reduced syntax move together through interface extension, weakening, + contain direct lambda applications. The `lamBeta` rule retains a reduction + origin from actual checks when cheap beta changes the body's inferred type. + Its source and reduced syntax move together through interface extension, weakening, universe instantiation, and structural level congruence. Earlier local contexts are reconstructed from actual binder-domain checks. The reader follows the selected plan through substitution and interning, and lambda @@ -374,7 +374,7 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) generated result is assumed. Retained codomain checks now also cross dependent term substitution. `SynthesisTypeCheck.forallBody` extracts the actual codomain call from the - earlier function-type tree. `SynthesisTypingOrigin.applicationArgument` + earlier function-type tree. `SynthesisCheckedOrigin.applicationArgument` retains the executed function and argument checks and their hash comparison; the mutual soundness proof derives the argument's membership in that domain. `ApplicationInferenceTrace.substituteTypeOriginAt` then transports the @@ -382,6 +382,16 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) their domains with the same substitution. This transport is consumed by `lamBeta`. No separate inference of the substituted type or semantic argument-typing premise is required. + Variable-headed codomains also retain their actual argument checks. + `SynthesisScopedTypeCheck.forallBody` follows nested codomain calls and + their executed domain checks; `variableSpine` extracts the local head's + exact type and the argument calls in order. Earlier parameter substitutions + preserve that head's lookup and update its dependent type and arguments. + `ApplicationInferenceTrace.exposedTypeOriginAt` then uses the substituted + argument's own checked lambda domains to justify a newly exposed prefix. + `substituteReductionOriginAt` carries that reduction through later + arguments. The mutual `SynthesisReductionOrigin.sound` proof supplies the + same conversion and reduced-type formation consumed by `lamBeta`. - Applications use full mode, syntactic Pi exposure, an ordinary argument without an eager-reduction marker, and the hash-equality conversion path. Their witnesses retain the actual recursive calls, context preservation @@ -494,7 +504,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 525 exact theorem boundaries. The production +The consistency target checks 554 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -513,8 +523,8 @@ typing, checking, and equality introduce no additional assumptions. Their general `instAt` forms use `ContextSubstitution` to remove an outer parameter and update every retained dependent domain. The beta-prefix commutation theorem covers any cutoff while bounding reduction by the -original lambda prefix; lambdas newly exposed by substitution need their own -checking origins. +original lambda prefix. For a checked variable-headed application, a +substituted lambda's actual argument check supplies a new prefix origin. `SynthesisInference.beta_peel_sound` derives beta-prefix equality and result typing from actual lambda and dependent argument inference. Its domain-shape proof retains information lost when proof values are identified. The original @@ -531,8 +541,8 @@ check to justify conversion through such a prefix and suffix, and therefore reaches the environment model-extension and no-False roots. `SynthesisInference.soundWithSpine` retains checked lambda domains in the same recursion that proves typing and type formation. Its `lamBeta` case -uses an original executed type check, transported to the generated type's -current context, to justify the changed cheap-beta result. The actual source +uses retained executed checks, transported to the generated type's current +context, to justify the changed cheap-beta result. The actual source reading determines the raw head and arguments; `CheapBetaSupport.reading` connects the selected prefix to the reduced syntax and final intern table. `SynthesisContext.sound` derives earlier contexts from their domain checks, @@ -540,8 +550,15 @@ so the origin can precede additional binders. `SynthesisTypeTransport.sound` and `SynthesisTypingOrigin.sound` are proved in that same recursion and carry these checks through argument substitution, including later dependent domains and the original context's universe instantiation. The public -inference and environment results include this case. Automatic origin -construction for arbitrary generated types, repeated reduction, and other +inference and environment results include this case. `SynthesisCheckedOrigin` +additionally retains each actual argument's syntactic lambda domains, while +`SynthesisArgumentSpineOrigin` keeps the checks of the original variable +application's arguments. `SynthesisReductionOrigin.sound` combines them when +substitution exposes a lambda head. It proves the changed result's typing +and equality in the same mutual recursion, including earlier and later +dependent substitutions. These additions use the same two existing native +output-length proofs; their model lemmas use only standard Lean axioms. +Automatic origin construction for arbitrary generated types, repeated reduction, and other conversion paths remain open. Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, @@ -580,6 +597,13 @@ codomain in Prop, Type, and at universe parameters. Two-argument cases use updated second domain, different original and reduced result hashes, and the exact final lambda type. They cover cache clearing, reuse, scope cleanup, and rejection of a carrier or first argument used in the wrong dependent domain. +Exposed-lambda regressions start with checked variable-headed codomains +`F A`, `F A B`, and `F x`. They observe the new head, argument order, beta +count, and exact reduced result after supplying a lambda. The dependent +identity example `f A x F y` also checks that earlier arguments specialize +the later family's domain before its lambda creates the redex. Cases cover +Prop, Type, universe parameters, captured carriers, cache clearing/reuse, +scope cleanup, and rejected function types and dependent witnesses. These execution tests do not construct the general finite inference resources. Polymorphic-call regressions include Prop/Type instances in real function bodies, `max`/`imax` simplification inside Pi domains, closed nested references @@ -650,7 +674,7 @@ Definition-cycle regressions use content-addressed standalone and mutual declarations, including a self-justifying theorem, a two-member cycle, type cycles, lets, shared syntax, and binders. They check repeated member failures, acyclic forward references, cache clearing, and the partial/unsafe policy. -The unit suite contains 629 checks. The anonymous differential additionally +The unit suite contains 644 checks. The anonymous differential additionally serializes eight cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. diff --git a/docs/theory.md b/docs/theory.md index f159c5232..528c1eccf 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -68,8 +68,14 @@ term substitution: the earlier function-type tree supplies the actual codomain call, while the executed function and argument checks and hash comparison establish membership in the substituted parameter's domain. The mutual inference proof preserves this origin beneath remaining dependent -parameters. The lambda closes the reduced type using the actual final intern -table. Reduction remains bounded by the original checked lambda prefix. +parameters. A variable-headed codomain now retains its actual argument checks +and local head type through earlier parameter substitutions. When a lambda +argument replaces that head, its own checked domains justify the newly +exposed prefix. Nested codomain origins are extracted from the executed +function-type tree. Later arguments transport the resulting reduction through +the remaining dependent parameters. The lambda closes the reduced type using +the actual final intern table. Each reduction is bounded by the checked +prefix of its selected origin; further exposed prefixes need further origins. Automatic origin construction for arbitrary generated types, general reduction, and conversion remain open. Safe definition admission also rejects circular justification in both Lean and From 770271606397933c8b88a2a40dee7a2129892d27 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 04:53:48 -0400 Subject: [PATCH 29/63] Compose reduction origins for supplied lambda applications A substituted function argument may already apply some of its lambda head's parameters. Retain those actual argument checks and combine them, in order, with the checked arguments of the original variable-headed codomain. Lift existing arguments beneath retained dependent parameters and update the codomain's arguments and types through the same substitution. The mutual inference proof now justifies a selected beta prefix that consumes arguments from both origins, using the existing lamBeta admission path. Validation: 559 exact consistency boundaries, 655 kernel unit checks, check-theory, IxKernelVerify/IxCompileVerify, and git diff --check pass. Eleven new regressions cover both cheap-beta plans, dependent initial arguments, caller locals, universes, caches, and rejected argument types. --- Ix/Kernel/Verify/Consistency.lean | 6 +- Ix/Kernel/Verify/Consistency/Audit.lean | 7 +- .../Consistency/SynthesisInference.lean | 101 ++++++++++++++--- Ix/Theory/Model/BetaSpine.lean | 54 +++++++++ Tests/Ix/Kernel/Consistency.lean | 107 ++++++++++++++++++ docs/kernel-verification.md | 21 +++- docs/theory.md | 10 +- 7 files changed, 282 insertions(+), 24 deletions(-) diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 03e68f62a..c6f83a950 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -99,7 +99,11 @@ codomains retain their actual argument checks and local types, extracted through nested function-type calls. Substituting a lambda for that head now uses the argument's own checked prefix to justify the newly exposed beta steps. Earlier parameter substitutions update the retained head type and -argument checks; later arguments continue transporting the reduction. These +argument checks; later arguments continue transporting the reduction. +The supplied argument may itself be a lambda application: its existing +argument checks are lifted beneath the retained parameters and joined to +the original codomain's checks in application order. The actual selected +prefix can consume arguments from both origins. These origins are proved sound in the inference recursion and consumed by the lambda case. Abstraction uses the reduced type and the reduction's final intern table. Automatic origin construction for arbitrary generated types diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index c539b01d5..eb598ad86 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -416,7 +416,12 @@ private def exposedOriginRoots : Array RootAllowance := #[ { root := ``SynthesisScopedTypeCheck.sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, { root := ``SynthesisScopedTypeCheck.forallBody, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, { root := ``SynthesisScopedTypeCheck.variableSpine, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, - { root := ``SynthesisTypeCheck.scoped, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } + { root := ``SynthesisTypeCheck.scoped, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``Theory.Model.AExpr.inst_variable_appN, standardAxioms := #[``propext] }, + { root := ``Theory.Model.ContextSubstitution.lift_spine, standardAxioms := standard }, + { root := ``Theory.Model.LambdaSpineTyping.substituteHead, standardAxioms := standard }, + { root := ``SynthesisCheckedOrigin.soundWithSpine, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``ApplicationInferenceTrace.exposedApplicationOriginAt, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } ] def roots : Array RootAllowance := #[ diff --git a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean index 1fa42db93..bc79ed386 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean @@ -251,8 +251,8 @@ inductive SynthesisTypingOrigin {β : Type u} (resolve : Address → Option (Con SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries targetContext (term.inst argument cutoff) (type.inst argument cutoff) -/-- Actual inference calls retain the syntactic lambda domains of their -source term. Application arguments additionally retain the comparison with +/-- Actual inference calls retain their source's syntactic lambda domains +and argument checks. Application arguments additionally retain the comparison with the function's domain, before any later transport of this checking origin. -/ inductive SynthesisCheckedOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : Model.Environment β → Model.Context β → List VLevel → @@ -310,8 +310,8 @@ inductive SynthesisArgumentSpineOrigin {β : Type u} (resolve : Address → Opti start (arguments ++ [argument]) (body.inst argument) /-- A beta reduction can come from an earlier checked lambda prefix, or -from an actual lambda argument substituted for the head of a checked -variable application. Both cases retain the checks of all applied arguments. -/ +from an actual lambda or lambda application substituted for a checked +variable head. Both cases retain the checks of all applied arguments. -/ inductive SynthesisReductionOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : Model.Environment β → Model.Context β → List VLevel → Model.Environment β → Model.Context β → AExpr β → List (AExpr β) → Nat → VLevel → Type u @@ -343,6 +343,20 @@ inductive SynthesisReductionOrigin {β : Type u} (resolve : Address → Option ( (enough : count ≤ argument.lambdaDepth) : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries target (argument.liftN cutoff) (arguments.map (AExpr.inst · argument cutoff)) count level + | substitutedApplication {incoming incomingContext incomingBounds entries base source target + type domain binder body condition initialArguments arguments cutoff count level} + (spine : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries source type arguments (.sort level)) + (atIndex : source[cutoff]? = some type) + (checked : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries base + ((AExpr.lam condition binder body).appN initialArguments) domain) + (substitution : ContextSubstitution base domain + ((AExpr.lam condition binder body).appN initialArguments) source target cutoff) + (enough : count ≤ body.lambdaDepth + 1) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries target + ((AExpr.lam condition binder body).liftN cutoff) + (initialArguments.map (AExpr.liftN cutoff ·) ++ + arguments.map (AExpr.inst · ((AExpr.lam condition binder body).appN initialArguments) cutoff)) count level | map {incoming incomingContext incomingBounds earlier priorContext head arguments count bound entries context current currentArguments level} (prior : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds @@ -617,6 +631,42 @@ def ApplicationInferenceTrace.exposedTypeOriginAt {β : Type u} {resolve : Addre (.applicationArgument argumentContext trace functionTree argumentTree agreement functionReading argumentReading conditions hashPath comparisonFaithful) substitution enough +/-- A supplied lambda application contributes its existing arguments +before the original variable application's arguments. Both sets of checks +come from the actual inference calls retained at this application. -/ +def ApplicationInferenceTrace.exposedApplicationOriginAt {β : Type u} + {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} + {incomingContext context sourceContext targetContext : Model.Context β} + {incomingBounds bounds : List VLevel} {domain argumentType binder inner f body : AExpr β} + {condition headCondition : Certified.PropWhen} {initialArguments arguments : List (AExpr β)} + {level functionBound argumentBound : VLevel} {count cutoff fuel : Nat} + {locals : List FVarId} {before : TcState .anon} {fn rawArgument : KExpr .anon} + (trace : ApplicationInferenceTrace fuel before fn rawArgument) + (origin : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries sourceContext cutoff arguments (.sort level)) + (enough : count ≤ inner.lambdaDepth + 1) + (argumentContext : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn + f (.forallE condition domain body) functionBound) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState rawArgument + ((AExpr.lam headCondition binder inner).appN initialArguments) argumentType argumentBound) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals rawArgument = + some ((AExpr.lam headCondition binder inner).appN initialArguments).erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) + (substitution : ContextSubstitution context domain + ((AExpr.lam headCondition binder inner).appN initialArguments) sourceContext targetContext cutoff) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries targetContext + ((AExpr.lam headCondition binder inner).liftN cutoff) + (initialArguments.map (AExpr.liftN cutoff ·) ++ + arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments) cutoff)) count level := + .substitutedApplication origin.spine origin.atIndex + (.applicationArgument argumentContext trace functionTree argumentTree agreement functionReading argumentReading + conditions hashPath comparisonFaithful) substitution enough + /-- Later actual arguments continue transporting an exposed prefix and its reduction, including through dependent domains still left open. -/ def ApplicationInferenceTrace.substituteReductionOriginAt {β : Type u} @@ -1094,7 +1144,7 @@ theorem SynthesisTypingOrigin.sound {β : Type u} {resolve : Address → Option (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : TypingClaim.{u,v} entries context term type := match support with - | .source check => (check.sound formed).1 + | .source check => (check.soundWithSpine formed).1 | .weaken prior domain => typing_weaken (prior.sound formed) | .instantiate prior arguments => typing_instL_context (prior.sound formed) arguments | .appendContext prior outer => typing_append_context (prior.sound formed) outer @@ -1104,28 +1154,30 @@ theorem SynthesisTypingOrigin.sound {β : Type u} {resolve : Address → Option | .substituteAt body value substitution => (body.sound formed).instAt (value.sound formed) substitution termination_by structural support -theorem SynthesisCheckedOrigin.sound {β : Type u} {resolve : Address → Option (ConstRef β)} +theorem SynthesisCheckedOrigin.soundWithSpine {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} {incomingBounds : List VLevel} {term type : AExpr β} (support : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type) (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : - TypingClaim.{u,v} entries context term type ∧ LambdaPrefix term type term.lambdaDepth := + TypingClaim.{u,v} entries context term type ∧ LambdaPrefix term type term.lambdaDepth ∧ + LambdaSpineTyping.{u,v} entries context term type := match support with - | .checked contextOrigin tree agreement reading accepted => - ⟨(tree.soundWithSpine (contextOrigin.sound formed) agreement reading accepted).2.1, tree.lambdaPrefix⟩ + | .checked contextOrigin tree agreement reading accepted => by + obtain ⟨_, typed, _, spine⟩ := tree.soundWithSpine (contextOrigin.sound formed) agreement reading accepted + exact ⟨typed, tree.lambdaPrefix, spine⟩ | .applicationArgument contextOrigin trace functionTree argumentTree agreement functionReading argumentReading conditions hashPath comparisonFaithful => by have contextFormation := contextOrigin.sound formed have functionTypeReads := (functionTree.soundWithSpine contextFormation agreement functionReading trace.functionRun).1 have domainReads := (readScopedExpr?_all_parts functionTypeReads).1 - obtain ⟨argumentTypeReads, argumentTyped, _, _⟩ := + obtain ⟨argumentTypeReads, argumentTyped, _, argumentSpine⟩ := argumentTree.soundWithSpine contextFormation (trace.contextPreserved.symm ▸ agreement) argumentReading trace.argumentRun have sameType := AExpr.eq_of_erase_annotations (Option.some.inj (argumentTypeReads.symm.trans ((beq_readScopedExpr? comparisonFaithful hashPath).trans domainReads))) conditions - exact ⟨sameType ▸ argumentTyped, sameType ▸ argumentTree.lambdaPrefix⟩ + exact ⟨sameType ▸ argumentTyped, sameType ▸ argumentTree.lambdaPrefix, sameType ▸ argumentSpine⟩ | .binderArgument trace functionTree head argumentTree agreement functionReading argumentReading conditions hashPath comparisonFaithful => by obtain ⟨functionTypeReads, functionTyped⟩ := @@ -1137,11 +1189,13 @@ theorem SynthesisCheckedOrigin.sound {β : Type u} {resolve : Address → Option (Option.some.inj (argumentTypeReads.symm.trans ((beq_readScopedExpr? comparisonFaithful hashPath).trans domainReads))) conditions have checked := sameType ▸ argumentChecked - refine ⟨?_, sameType ▸ argumentTree.lambdaPrefix⟩ - intro V _ constants realizes levels env valid - have domainValid := (functionTyped V constants realizes levels env valid).2.1.1 - have checkedAt := checked V constants realizes levels env valid domainValid - exact ⟨checkedAt.1, domainValid, checkedAt.2⟩ + have typed : TypingClaim.{u,v} entries context term type := by + intro V _ constants realizes levels env valid + have domainValid := (functionTyped V constants realizes levels env valid).2.1.1 + have checkedAt := checked V constants realizes levels env valid domainValid + exact ⟨checkedAt.1, domainValid, checkedAt.2⟩ + exact ⟨typed, sameType ▸ argumentTree.lambdaPrefix, + sameType ▸ argumentTree.lambdaSpineTyping (sameType.symm ▸ typed)⟩ termination_by structural support theorem SynthesisArgumentSpineOrigin.sound {β : Type u} {resolve : Address → Option (ConstRef β)} @@ -1171,10 +1225,12 @@ theorem SynthesisReductionOrigin.sound {β : Type u} {resolve : Address → Opti obtain ⟨originConversion, originReducedTyped⟩ := originSpine.betaPrefix originPrefix exact transport.sound formed originConversion originReducedTyped | .substitutedVariable spine atIndex checked substitution enough => by - obtain ⟨typed, leading⟩ := checked.sound formed + obtain ⟨typed, leading, _⟩ := checked.soundWithSpine formed have instantiated := (spine.sound formed).instAt typed substitution rw [substitution.instantiate_removed_type atIndex] at instantiated exact ((leading.liftN _ 0).truncate enough).beta_sound (substitution.lift_typing typed) instantiated + | .substitutedApplication spine atIndex checked substitution enough => + (checked.soundWithSpine formed).2.2.substituteHead (spine.sound formed) atIndex substitution enough | .map prior transport => by obtain ⟨converted, typed⟩ := prior.sound formed exact transport.sound formed converted typed @@ -1182,6 +1238,17 @@ termination_by structural support end +/-- Ordinary argument typing and its syntactic leading domains remain +available without exposing the stronger application-spine result. -/ +theorem SynthesisCheckedOrigin.sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} + (support : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type) + (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : + TypingClaim.{u,v} entries context term type ∧ LambdaPrefix term type term.lambdaDepth := by + obtain ⟨typed, leading, _⟩ := support.soundWithSpine formed + exact ⟨typed, leading⟩ + /-- The public inference result projects ordinary typing and formation from the stronger induction that also retains checked lambda domains. -/ theorem SynthesisInference.sound {β : Type u} diff --git a/Ix/Theory/Model/BetaSpine.lean b/Ix/Theory/Model/BetaSpine.lean index 132a824b9..9f5c85731 100644 --- a/Ix/Theory/Model/BetaSpine.lean +++ b/Ix/Theory/Model/BetaSpine.lean @@ -94,6 +94,14 @@ theorem inst_appN (head : AExpr β) (arguments : List (AExpr β)) (value : AExpr | nil => rfl | cons argument arguments ih => simpa only [appN_cons, inst, List.map_cons] using ih (head.app argument) +/-- A substituted application contributes its own arguments before the +arguments already applied to the removed variable. -/ +theorem inst_variable_appN (head : AExpr β) (initialArguments arguments : List (AExpr β)) (cutoff : Nat) : + ((AExpr.bvar cutoff).appN arguments).inst (head.appN initialArguments) cutoff = + (head.liftN cutoff).appN (initialArguments.map (AExpr.liftN cutoff ·) ++ + arguments.map (AExpr.inst · (head.appN initialArguments) cutoff)) := by + simp only [inst_appN, inst, instVar, Nat.lt_irrefl, if_false, if_true, liftN_appN, appN_append] + /-- Substitution preserves a reduction justified by the original lambda prefix. Lambdas newly exposed beyond that prefix need their own origin. -/ theorem inst_betaPrefix (count : Nat) (head : AExpr β) (arguments : List (AExpr β)) @@ -341,6 +349,21 @@ theorem ContextSubstitution.lift_typing {β : Type u} {entries : Environment β} simpa only [wellDenoted_liftN, interp_liftN] using typed V constants realizes levels _ (substitution.base_valid valid) +/-- Every existing argument of a supplied value is lifted beneath the +same retained dependent parameters as the value's function head. -/ +theorem ContextSubstitution.lift_spine {β : Type u} {entries : Environment β} + {base source target : Context β} {domain argument start result : AExpr β} + {arguments : List (AExpr β)} {cutoff : Nat} + (substitution : ContextSubstitution base domain argument source target cutoff) + (spine : ArgumentSpine.{u,v} entries base start arguments result) : + ArgumentSpine.{u,v} entries target (start.liftN cutoff) + (arguments.map (AExpr.liftN cutoff ·)) (result.liftN cutoff) := by + induction spine with + | nil => exact .nil _ + | cons typed tail ih => + exact .cons (substitution.lift_typing typed) + (by simpa only [AExpr.liftN_inst_zero] using ih) + theorem ConversionClaim.appN {β : Type u} {entries : Environment β} {context : Context β} {left right : AExpr β} (same : ConversionClaim.{u,v} entries context left right) (arguments : List (AExpr β)) : @@ -450,6 +473,37 @@ theorem betaPrefix {β : Type u} {entries : Environment β} {context : Context obtain ⟨_, typed, leading, spine⟩ := source _ _ _ _ rfl exact (leading.truncate enough).beta_sound typed spine +/-- Substituting a lambda application for a variable head joins its +already checked arguments to the original application's checked arguments. +The combined prefix may consume arguments from both checks. -/ +theorem substituteHead {β : Type u} {entries : Environment β} + {base source target : Context β} {type domain binder body result : AExpr β} + {condition : PropWhen} {initialArguments arguments : List (AExpr β)} {cutoff count : Nat} + (value : LambdaSpineTyping.{u,v} entries base + ((AExpr.lam condition binder body).appN initialArguments) domain) + (spine : ArgumentSpine.{u,v} entries source type arguments result) + (atIndex : source[cutoff]? = some type) + (substitution : ContextSubstitution base domain + ((AExpr.lam condition binder body).appN initialArguments) source target cutoff) + (enough : count ≤ body.lambdaDepth + 1) : + ConversionClaim.{u,v} entries target + (((AExpr.lam condition binder body).liftN cutoff).appN + (initialArguments.map (AExpr.liftN cutoff ·) ++ + arguments.map (AExpr.inst · ((AExpr.lam condition binder body).appN initialArguments) cutoff))) + (AExpr.betaPrefix count ((AExpr.lam condition binder body).liftN cutoff) + (initialArguments.map (AExpr.liftN cutoff ·) ++ + arguments.map (AExpr.inst · ((AExpr.lam condition binder body).appN initialArguments) cutoff))) ∧ + TypingClaim.{u,v} entries target + (AExpr.betaPrefix count ((AExpr.lam condition binder body).liftN cutoff) + (initialArguments.map (AExpr.liftN cutoff ·) ++ + arguments.map (AExpr.inst · ((AExpr.lam condition binder body).appN initialArguments) cutoff))) + (result.inst ((AExpr.lam condition binder body).appN initialArguments) cutoff) := by + obtain ⟨_, headTyped, leading, initial⟩ := value _ _ _ _ rfl + have remaining := spine.instAt (initial.typing headTyped) substitution + rw [substitution.instantiate_removed_type atIndex] at remaining + exact ((leading.liftN _ 0).truncate enough).beta_sound + (substitution.lift_typing headTyped) ((substitution.lift_spine initial).append remaining) + end LambdaSpineTyping end Ix.Theory.Model diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index e431e9ee6..ac6854fc1 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -875,6 +875,83 @@ private def dependentExposedLambdaTypeResult (level : Ixon.Univ) : Bool := | .ok passed after => passed && after.lctx.size == 0 | .error _ _ => false +/-- Supplying a partial lambda application joins its existing arguments +to the variable-headed codomain's arguments. Dependent initial arguments +and caller locals exercise both substitution cutoffs and argument order. -/ +private def composedLambdaType (level : Ixon.Univ) (universes : UInt64 := 0) + (shape wrong : Nat := 0) : Ixon.Env × Address := Id.run do + let levels := if universes == 0 then #[] else #[0] + let (env, carrier) := storeConst {} + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], #[level]⟩ + let (env, otherCarrier) := storeConst env + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], #[level, .succ level]⟩ + let (env, witness) := storeConst env + ⟨.axio ⟨false, universes, .ref 0 levels⟩, #[], #[carrier], #[level]⟩ + let familyType := Ixon.Expr.leanAll (.sort 0) (.sort 0) + let (env, function) := storeConst env + ⟨.defn ⟨.defn, .safe, universes, + .leanAll familyType (.leanAll (.app (.var 0) (.ref 0 levels)) (.app (.var 1) (.ref 0 levels))), + .leanLam familyType (.leanLam (.app (.var 0) (.ref 0 levels)) (.var 0))⟩, + #[], #[otherCarrier], #[level]⟩ + let head := Ixon.Expr.leanLam (.sort 0) + (if shape >= 2 then .leanLam (.var 0) (.leanLam (.sort 0) (.var 2)) + else .leanLam (.sort 0) (if shape == 1 then .ref 0 levels else .var 1)) + let familyAt : Ixon.Expr → Ixon.Expr → Ixon.Expr := fun type value => + if shape >= 2 then .app (.app head type) value + else .app head (if shape == 1 then .ref 1 levels else type) + let family := familyAt (.ref 0 levels) (.ref 2 levels) + let domain := Ixon.Expr.app family (.ref 1 levels) + let supplied := if wrong == 1 then familyAt (.ref 0 levels) (.ref 0 levels) + else if wrong == 2 then + .app (.leanLam (.sort 0) (.leanLam (.sort 0) (.var 0))) (.ref 0 levels) + else family + let type := if shape == 3 then + Ixon.Expr.leanAll (.sort 0) (.leanAll (.var 0) + (.leanAll (.app (familyAt (.var 1) (.var 0)) (.ref 1 levels)) (.var 2))) + else .leanAll domain (.ref 0 levels) + let value := if shape == 3 then + Ixon.Expr.leanLam (.sort 0) (.leanLam (.var 0) + (.leanLam (.app (familyAt (.var 1) (.var 0)) (.ref 1 levels)) + (.app (.app (.ref 3 levels) (familyAt (.var 2) (.var 1))) (.var 0)))) + else .leanLam domain (.app (.app (.ref 3 levels) supplied) (.var 0)) + return storeConst env + ⟨.defn ⟨.defn, .safe, universes, type, value⟩, + #[], #[carrier, otherCarrier, witness, function], #[level]⟩ + +private def composedLambdaTypeResult (shape : Nat) (level : Ixon.Univ) : Bool := + let (env, target) := composedLambdaType level 0 shape + let action : RecM .anon Bool := RecM.withLctxScope do + let concrete ← TcM.getConst (m := .anon) ⟨target, ()⟩ + let .defn _ _ _ _ _ _ type value _ _ := concrete | return false + let .lam name bi domain body _ := value | return false + let .all _ _ _ expected _ := type | return false + let (fn, arguments) := body.collectSpine + let some supplied := arguments[0]? | return false + let .app .. := supplied | return false + let (suppliedHead, initialArguments) := supplied.collectSpine + let .all fnName fnBi fnDomain fnBody _ ← RecM.inferCall fn | return false + let suppliedType ← RecM.inferCall supplied + let (openedType, originalHead, _) ← TcM.openBinderWithFV fnName fnBi fnDomain fnBody + let .all _ _ _ originalCodomain _ := openedType | return false + let (call, _) ← TcM.openBinder name bi domain body + let generated ← RecM.inferCall call + let (generatedHead, generatedArguments) := generated.collectSpine + let consumed := (peelLamsN generatedArguments.size generatedHead).2 + let reduced ← TcM.runIntern (cheapBetaReduce generated) + let before ← get + let inferred ← RecM.inferCall value + let repeated ← RecM.inferCall value + let after ← get + return originalCodomain.collectSpine.1 == originalHead && (cheapBetaPlan? originalCodomain).isNone && + suppliedType == fnDomain && generatedHead == suppliedHead && generatedHead != supplied && + generatedArguments == initialArguments ++ originalCodomain.collectSpine.2 && + initialArguments.size == (if shape >= 2 then 2 else 1) && consumed == initialArguments.size + 1 && + (cheapBetaPlan? generated).isSome && generated != reduced && reduced == expected && + inferred == type && repeated == type && before.lctx.size == after.lctx.size + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + private def applicationCases : TestSeq := test "application environment: Prop/Type identity calls and transitive theorem calls check" (allSucceeded applicationEnvironment 5 { clearEvery := 0 }) @@ -1041,6 +1118,36 @@ private def exposedLambdaCases : TestSeq := (dependentExposedLambdaTypeResult .zero && dependentExposedLambdaTypeResult (.succ .zero)) ++ test "exposed type lambda: a supplied family must use its specialized dependent domain" (let (env, target) := dependentExposedLambdaType .zero 0 true; rowFailed env target) + ++ test "composed type lambda: existing and outer arguments share a prefix in Prop and Type" + (allSucceeded (composedLambdaType .zero).1 5 && + allSucceeded (composedLambdaType (.succ .zero)).1 5) + ++ test "composed type lambda: a closed body retains a captured carrier" + (allSucceeded (composedLambdaType .zero 0 1).1 5 && + allSucceeded (composedLambdaType (.succ .zero) 0 1).1 5) + ++ test "composed type lambda: existing dependent arguments keep their checked order" + (allSucceeded (composedLambdaType .zero 0 2).1 5 && + allSucceeded (composedLambdaType (.succ .zero) 0 2).1 5) + ++ test "composed type lambda: caller locals survive substitution beneath the remaining parameter" + (allSucceeded (composedLambdaType .zero 0 3).1 5 && + allSucceeded (composedLambdaType (.succ .zero) 0 3).1 5) + ++ test "composed type lambda: both plans and dependent initial arguments preserve universe parameters" + (allSucceeded (composedLambdaType (.var 0) 1).1 5 && + allSucceeded (composedLambdaType (.var 0) 1 1).1 5 && + allSucceeded (composedLambdaType (.var 0) 1 2).1 5 && + allSucceeded (composedLambdaType (.var 0) 1 3).1 5) + ++ test "composed type lambda: the shared spine survives fresh per-item caches" + (allSucceeded (composedLambdaType .zero).1 5 { clearEvery := 1 } && + allSucceeded (composedLambdaType .zero 0 3).1 5 { clearEvery := 1 }) + ++ test "composed type lambda: a variable plan consumes one argument from each origin" + (composedLambdaTypeResult 0 .zero && composedLambdaTypeResult 0 (.succ .zero)) + ++ test "composed type lambda: the closed-body plan uses the combined argument list" + (composedLambdaTypeResult 1 .zero && composedLambdaTypeResult 1 (.succ .zero)) + ++ test "composed type lambda: a three-step prefix preserves the dependent initial argument" + (composedLambdaTypeResult 2 .zero && composedLambdaTypeResult 2 (.succ .zero)) + ++ test "composed type lambda: an initial argument must inhabit its specialized domain" + (let (env, target) := composedLambdaType .zero 0 2 1; rowFailed env target) + ++ test "composed type lambda: swapping the selected argument cannot change the declared carrier" + (let (env, target) := composedLambdaType .zero 0 0 2; rowFailed env target) /-- Call a polymorphic identity from a monomorphic function body. Universe indices select entries in the declaration's explicit level table. -/ diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index d97a7e4b6..7b0f740a8 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -392,6 +392,14 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) `substituteReductionOriginAt` carries that reduction through later arguments. The mutual `SynthesisReductionOrigin.sound` proof supplies the same conversion and reduced-type formation consumed by `lamBeta`. + A supplied argument may already apply some of its lambda head's parameters. + `SynthesisCheckedOrigin.soundWithSpine` retains those checks as well as + the head's domains. `exposedApplicationOriginAt` joins them, in order, to + the original codomain's argument checks. For example, substituting + `(fun X : Sort u => fun Y : Sort u => X) A` for `F` in `F B` gives the + checked spine `[A, B]`. Its existing arguments are lifted beneath retained + parameters, and the codomain arguments receive the same substitution as + their dependent types. The selected prefix can consume both lists. - Applications use full mode, syntactic Pi exposure, an ordinary argument without an eager-reduction marker, and the hash-equality conversion path. Their witnesses retain the actual recursive calls, context preservation @@ -504,7 +512,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 554 exact theorem boundaries. The production +The consistency target checks 559 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -558,6 +566,10 @@ substitution exposes a lambda head. It proves the changed result's typing and equality in the same mutual recursion, including earlier and later dependent substitutions. These additions use the same two existing native output-length proofs; their model lemmas use only standard Lean axioms. +The stronger checked-origin result also retains an argument's existing +lambda-headed application spine. `LambdaSpineTyping.substituteHead` combines +it with the original variable application's spine under the retained context, +so a selected reduction can cross from one argument list into the other. Automatic origin construction for arbitrary generated types, repeated reduction, and other conversion paths remain open. Kernel unit regressions cover lazy loading, both inference policies, interning @@ -604,6 +616,11 @@ identity example `f A x F y` also checks that earlier arguments specialize the later family's domain before its lambda creates the redex. Cases cover Prop, Type, universe parameters, captured carriers, cache clearing/reuse, scope cleanup, and rejected function types and dependent witnesses. +Composition cases supply partial lambda applications. They check the exact +combined argument order and two- or three-lambda reduction counts for both +cheap-beta plans, including dependent initial arguments and caller locals. +They cover Prop, Type, universe parameters, cache clearing/reuse, and failures +for an ill-typed initial argument or a different selected carrier. These execution tests do not construct the general finite inference resources. Polymorphic-call regressions include Prop/Type instances in real function bodies, `max`/`imax` simplification inside Pi domains, closed nested references @@ -674,7 +691,7 @@ Definition-cycle regressions use content-addressed standalone and mutual declarations, including a self-justifying theorem, a two-member cycle, type cycles, lets, shared syntax, and binders. They check repeated member failures, acyclic forward references, cache clearing, and the partial/unsafe policy. -The unit suite contains 644 checks. The anonymous differential additionally +The unit suite contains 655 checks. The anonymous differential additionally serializes eight cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. diff --git a/docs/theory.md b/docs/theory.md index 528c1eccf..c26b39483 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -73,9 +73,13 @@ and local head type through earlier parameter substitutions. When a lambda argument replaces that head, its own checked domains justify the newly exposed prefix. Nested codomain origins are extracted from the executed function-type tree. Later arguments transport the resulting reduction through -the remaining dependent parameters. The lambda closes the reduced type using -the actual final intern table. Each reduction is bounded by the checked -prefix of its selected origin; further exposed prefixes need further origins. +the remaining dependent parameters. A supplied lambda application also keeps +its already checked arguments: they precede the original codomain's arguments +in the combined spine, with both sets of dependent types preserved. The +selected beta prefix can consume arguments from both origins. The lambda +closes the reduced type using the actual final intern table. Each reduction +is bounded by the checked prefix of its selected origin; further exposed +prefixes need further origins. Automatic origin construction for arbitrary generated types, general reduction, and conversion remain open. Safe definition admission also rejects circular justification in both Lean and From d700fd1a95a319566786355794c85c8ee666b544 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 05:31:11 -0400 Subject: [PATCH 30/63] Compose successive beta reductions from retained body checks --- Ix/Kernel/Verify/Consistency.lean | 10 +- Ix/Kernel/Verify/Consistency/Audit.lean | 16 +- Ix/Kernel/Verify/Consistency/Beta.lean | 114 +++++++++++++ Ix/Kernel/Verify/Consistency/BetaSpine.lean | 155 +++++++++++++++--- Ix/Kernel/Verify/Consistency/Production.lean | 69 ++++++++ .../Consistency/SynthesisInference.lean | 90 ++++++++++ Ix/Theory/Model/BetaSpine.lean | 29 ++++ Ix/Theory/Model/BetaSubstitution.lean | 16 ++ Tests/Ix/Kernel/Consistency.lean | 124 +++++++++++++- docs/kernel-verification.md | 29 +++- docs/theory.md | 11 +- 11 files changed, 626 insertions(+), 37 deletions(-) diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index c6f83a950..94abe42a1 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -106,8 +106,14 @@ the original codomain's checks in application order. The actual selected prefix can consume arguments from both origins. These origins are proved sound in the inference recursion and consumed by the lambda case. Abstraction uses the reduced type and the reduction's final -intern table. Automatic origin construction for arbitrary generated types -and general reduction remains open. +intern table. A lambda body that applies its parameter also retains its +actual argument checks, even when cheap beta changes the body's inferred +type. Its first beta result supplies typing at the current type; substituting +the supplied lambda supplies the next checked prefix. Two successive +prefixes therefore compose without inference of the intermediate term. +Retained origins justify the actual multi-argument WHNF step, and declaration +admission includes this two-prefix conversion. Automatic origin construction +for arbitrary generated types and general reduction remain open. Local cache hits agree with the actual declaration type. Constant hits agree with pure universe substitution of a loaded, admitted declaration; sort hits return the canonical successor sort. Application, forall, and lambda diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index eb598ad86..1d9e24329 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -424,6 +424,20 @@ private def exposedOriginRoots : Array RootAllowance := #[ { root := ``ApplicationInferenceTrace.exposedApplicationOriginAt, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } ] +private def repeatedBetaRoots : Array RootAllowance := #[ + { root := ``Theory.Model.AExpr.inst_liftN_self, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``Theory.Model.TypingClaim.lambdaBody, standardAxioms := standard }, + { root := ``Theory.Model.TypingClaim.termConv, standardAxioms := standard }, + { root := ``BinderInference.lambdaBodyVariableSpine, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.lambdaBodyVariableSpine, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.betaResultOrigin, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.betaNextOrigin, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.beta_twice_sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``beta_many_step_readScopedExpr?, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisReductionOrigin.beta_many_step, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``DefinitionBodyTrace.betaDeclaredTwiceSupport, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -551,7 +565,7 @@ def roots : Array RootAllowance := #[ }) ++ productionRoots.map (fun root => { root, standardAxioms := standard, nativeAxioms := productionNative, forbiddenDependencies := forbiddenProduction -}) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots ++ exposedOriginRoots).map (fun allowance => { +}) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots ++ exposedOriginRoots ++ repeatedBetaRoots).map (fun allowance => { allowance with forbiddenDependencies := forbiddenProduction }) ++ #[{ root := ``extend_atomic_definition, standardAxioms := standard }] diff --git a/Ix/Kernel/Verify/Consistency/Beta.lean b/Ix/Kernel/Verify/Consistency/Beta.lean index 524d455cd..7cd9ac7ca 100644 --- a/Ix/Kernel/Verify/Consistency/Beta.lean +++ b/Ix/Kernel/Verify/Consistency/Beta.lean @@ -56,6 +56,85 @@ private theorem BinderInference.no_direct_beta {β : Type u} cases support with | app _ _ _ _ head => cases head +private theorem lambda_inference_domain {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} + {condition inferredCondition : Certified.PropWhen} {domain body inferredDomain codomain : AExpr β} + {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source + (.lam condition domain body) (.forallE inferredCondition inferredDomain codomain) level) : + condition = inferredCondition ∧ domain = inferredDomain := by + obtain ⟨actualBody, same⟩ := support.lambda_type + cases same + exact ⟨rfl, rfl⟩ + +/-- A beta result retains the lambda's original check and the actual +argument check. No inference call on the substituted body is introduced. -/ +def SynthesisInference.betaResultOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {level : VLevel} + {condition : Certified.PropWhen} {domain body argument type : AExpr β} + (support : SynthesisInference resolve entries locals context bounds fuel before source + (.app (.lam condition domain body) argument) type level) + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = + some (AExpr.app (.lam condition domain body) argument).erase) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context (body.inst argument) type := by + cases support with + | known inference _ => exact False.elim inference.no_direct_beta + | reuseType inference => exact False.elim inference.no_direct_beta + | app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful + bodyConstructed argConstructed bodyBound argBound coherent faithful => + obtain ⟨rfl, rfl⟩ := lambda_inference_domain functionTree + obtain ⟨functionReads, argumentReads⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + exact (SynthesisTypingOrigin.lambdaBody + (.checked contextOrigin functionTree keyedAgreement functionReads trace.functionRun)).substituteAt + (.source (.applicationArgument contextOrigin trace functionTree argumentTree keyedAgreement + functionReads argumentReads conditions hashPath comparisonFaithful)) .root + +/-- When the lambda body applies its parameter, its actual body checks +supply the next reduction origin after beta exposes the supplied lambda. +The source's result type comes from the preceding check even if the body +was originally inferred at a type requiring cheap-beta conversion. -/ +def SynthesisInference.betaNextOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel count : Nat} + {before : TcState .anon} {source : KExpr .anon} {level bound : VLevel} + {condition headCondition : Certified.PropWhen} {domain binder inner : AExpr β} + {initialArguments arguments : List (AExpr β)} + (support : SynthesisInference resolve entries locals context bounds fuel before source + (.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)) (.sort level) bound) + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = + some (AExpr.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)).erase) + (enough : count ≤ inner.lambdaDepth + 1) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries context + (.lam headCondition binder inner) + (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments))) + count level := by + have resultOrigin := support.betaResultOrigin contextOrigin agreement reading + generalize resultEquation : AExpr.sort level = resultType at support + cases support with + | known inference _ => exact False.elim inference.no_direct_beta + | reuseType inference => exact False.elim inference.no_direct_beta + | app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful + bodyConstructed argConstructed bodyBound argBound coherent faithful => + obtain ⟨rfl, rfl⟩ := lambda_inference_domain functionTree + obtain ⟨functionReads, argumentReads⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨_, bodySpine⟩ := functionTree.lambdaBodyVariableSpine contextOrigin keyedAgreement functionReads + have flattened := SynthesisReductionOrigin.substitutedResult bodySpine.spine bodySpine.atIndex + (.applicationArgument contextOrigin trace functionTree argumentTree keyedAgreement functionReads argumentReads + conditions hashPath comparisonFaithful) .root enough + (by simpa only [AExpr.inst_variable_appN] using resultOrigin) + simpa only [AExpr.liftN_zero, List.map_id'] using flattened + /-- A supported successful inference of a source beta redex derives both equality with the substitution result and typing of that result. No run of inference on the generated substitution is required. -/ @@ -97,6 +176,41 @@ theorem SynthesisInference.beta_sound {β : Type u} exact ⟨ConversionClaim.beta functionTyped typedArgument, TypingClaim.betaResult functionTyped typedArgument⟩ +/-- Two successive beta prefixes can use different lambda origins. The +second comes from an actual supplied argument, rather than a fresh check +of the intermediate reduction result. -/ +theorem SynthesisInference.beta_twice_sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} + {locals : List FVarId} {fuel count : Nat} {before after : TcState .anon} {source result : KExpr .anon} + {level bound : VLevel} {condition headCondition : Certified.PropWhen} {domain binder inner : AExpr β} + {initialArguments arguments : List (AExpr β)} + (support : SynthesisInference resolve entries locals context bounds fuel before source + (.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)) (.sort level) bound) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = + some (AExpr.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)).erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) + (enough : count ≤ inner.lambdaDepth + 1) : + ConversionClaim.{u,v} entries context + (.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)) + (AExpr.betaPrefix count (.lam headCondition binder inner) + (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments)))) ∧ + TypingClaim.{u,v} entries context + (AExpr.betaPrefix count (.lam headCondition binder inner) + (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments)))) + (.sort level) := by + have first := support.beta_sound formed agreement reading accepted + have next := (support.betaNextOrigin .current agreement reading enough).sound formed + refine ⟨?_, next.2⟩ + apply ConversionClaim.trans (b := (AExpr.lam headCondition binder inner).appN + (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments)))) + ?_ next.1 + simpa only [AExpr.inst_variable_appN, AExpr.liftN_zero, List.map_id'] using first.1 + /-- The real one-argument structural-WHNF step returns the well-typed beta result justified by source inference. The production method table supplies the lambda-head callback; finite walker resources supply the exact result diff --git a/Ix/Kernel/Verify/Consistency/BetaSpine.lean b/Ix/Kernel/Verify/Consistency/BetaSpine.lean index 73c7eb156..5e7a79818 100644 --- a/Ix/Kernel/Verify/Consistency/BetaSpine.lean +++ b/Ix/Kernel/Verify/Consistency/BetaSpine.lean @@ -82,6 +82,73 @@ theorem SynthesisInference.beta_peel_sound {β : Type u} have result := (leading.truncate peeling.length_bound).beta_sound typed spine rwa [peeling.betaPrefix trailing] at result +/-- The actual beta step reads as a model beta prefix. This representation +fact depends on the executed peel, substitution and suffix reconstruction; +it applies equally to source expressions and generated intermediate terms. -/ +theorem beta_many_step_readScopedExpr? {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {rawFunction rawArgument : KExpr .anon} {appInfo : ExprInfo .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {rawDomain rawInner rawBody : KExpr .anon} {lambdaInfo : ExprInfo .anon} + {rawArguments consumed : Array (KExpr .anon)} + {condition : Certified.PropWhen} {domain inner : AExpr β} {arguments : List (AExpr β)} + (spine : (KExpr.app rawFunction rawArgument appInfo).collectSpine = + (.lam name bi rawDomain rawInner lambdaInfo, rawArguments)) + (headReads : readScopedExpr? resolve locals (.lam name bi rawDomain rawInner lambdaInfo) = + some (AExpr.lam condition domain inner).erase) + (argumentReads : rawArguments.toList.map (readScopedExpr? resolve locals ·) = + arguments.map (some ·.erase)) + (peeling : RecM.consumeBetaLams (.lam name bi rawDomain rawInner lambdaInfo) rawArguments = + (rawBody, consumed)) + (nonempty : (!consumed.isEmpty) = true) + (before : TcState .anon) (reductionFuel : Nat) (flags : WhnfFlags) + (walkerBounds : SimulSubstBounds rawBody consumed.reverse 0) + (coherent : before.env.intern.WF) + (walkerFaithful : KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ + KExpr.SimulSubstReach consumed.reverse rawBody 0 term) + (suffixFaithful : KExpr.CollisionFree fun term => + (simulSubst rawBody consumed.reverse 0 before.env.intern).2.ExprSupport term ∨ + term ∈ cheapBetaChainList (simulSubst rawBody consumed.reverse 0 before.env.intern).1 + (rawArguments.extract consumed.size rawArguments.size).toList) : + ∃ (result : KExpr .anon) (after : TcState .anon), + (RecM.whnfCoreWithFlagsStep (.app rawFunction rawArgument appInfo) flags).run + (methodsN (reductionFuel + 1)) before = .ok (.next result) after ∧ + readScopedExpr? resolve locals result = + some (AExpr.betaPrefix consumed.size (.lam condition domain inner) arguments).erase ∧ + consumed.size ≤ inner.lambdaDepth + 1 ∧ after.env.intern.WF := by + obtain ⟨rawPeel, consumedPrefix, consumedBound⟩ := RecM.BetaPeel.of_consume peeling + obtain ⟨body, modelPeel, bodyReads⟩ := betaPeel_readScopedExpr? rawPeel headReads + have sizeAgrees : rawArguments.size = arguments.length := by + have lengths := congrArg List.length argumentReads + simpa using lengths + have consumedSize : (arguments.take consumed.size).length = consumed.size := by + simp only [List.length_take] + omega + have consumedReads : consumed.toList.map (readScopedExpr? resolve locals ·) = + (arguments.take consumed.size).map (some ·.erase) := by + rw [consumedPrefix, List.map_take, argumentReads, List.map_take] + have suffixReads : (rawArguments.extract consumed.size rawArguments.size).toList.map + (readScopedExpr? resolve locals ·) = (arguments.drop consumed.size).map (some ·.erase) := by + rw [RecM.BetaPeel.remaining_eq_drop peeling, List.map_drop, argumentReads, List.map_drop] + obtain ⟨walkReads, walkCoherent⟩ := simulSubst_readScopedExpr? + (by simpa only [Array.size_reverse] using consumedSize.symm) + walkerBounds.1 walkerBounds.2.1 (by simpa using walkerBounds.2.2.2.1) + walkerBounds.2.2.1 coherent walkerFaithful + (by simpa only [Nat.zero_add, Array.length_toList, consumedSize] using bodyReads) + (argumentsReading_reverse_get consumedReads consumedSize.symm) + have peelingMeaning := LambdaPeel.betaPrefix (arguments := arguments.take consumed.size) + (by simpa only [consumedSize, Array.length_toList] using modelPeel) (arguments.drop consumed.size) + simp only [consumedSize, List.take_append_drop] at peelingMeaning + let walk := simulSubst rawBody consumed.reverse 0 before.env.intern + let middle := { before with env := { before.env with intern := walk.2 } } + obtain ⟨result, after, finish, resultReads, preserved⟩ := + finishAppResult_readScopedExpr? (before := middle) walkCoherent suffixFaithful walkReads suffixReads + (methodsN (reductionFuel + 1)) + refine ⟨result, after, ?_, ?_, ?_, preserved⟩ + · exact RecM.whnfCoreWithFlagsStep_betaMany spine rfl peeling nonempty rfl finish + · simpa only [peelingMeaning] using resultReads + · simpa only [Array.length_toList, AExpr.lambdaDepth] using modelPeel.length_bound + /-- The production multi-argument beta step preserves typing and model meaning. Its peeled body and consumed argument order come from the actual loop, and its suffix is rebuilt by the actual interned application chain. -/ @@ -129,39 +196,73 @@ theorem SynthesisInference.beta_many_step {β : Type u} TypingClaim.{u,v} entries context ((body.instRev (arguments.take consumed.size)).appN (arguments.drop consumed.size)) type ∧ after.env.intern.WF := by + obtain ⟨result, after, run, resultReads, enough, preserved⟩ := beta_many_step_readScopedExpr? + spine headReads argumentReads peeling nonempty before reductionFuel flags + walkerBounds coherent walkerFaithful suffixFaithful have reading := readScopedExpr?_collectSpine spine headReads argumentReads - obtain ⟨rawPeel, consumedPrefix, consumedBound⟩ := RecM.BetaPeel.of_consume peeling - obtain ⟨body, modelPeel, bodyReads⟩ := betaPeel_readScopedExpr? rawPeel headReads + obtain ⟨_, _, typed, leading, argumentsTyped⟩ := support.lambda_spine formed agreement reading accepted + have conversion := (leading.truncate enough).beta_sound typed argumentsTyped + obtain ⟨rawPeel, _, consumedBound⟩ := RecM.BetaPeel.of_consume peeling + obtain ⟨body, modelPeel, _⟩ := betaPeel_readScopedExpr? rawPeel headReads have sizeAgrees : rawArguments.size = arguments.length := by have lengths := congrArg List.length argumentReads simpa using lengths have consumedSize : (arguments.take consumed.size).length = consumed.size := by simp only [List.length_take] omega - have consumedReads : consumed.toList.map (readScopedExpr? resolve locals ·) = - (arguments.take consumed.size).map (some ·.erase) := by - rw [consumedPrefix, List.map_take, argumentReads, List.map_take] - have suffixReads : (rawArguments.extract consumed.size rawArguments.size).toList.map - (readScopedExpr? resolve locals ·) = (arguments.drop consumed.size).map (some ·.erase) := by - rw [RecM.BetaPeel.remaining_eq_drop peeling, List.map_drop, argumentReads, List.map_drop] - obtain ⟨walkReads, walkCoherent⟩ := simulSubst_readScopedExpr? - (by simpa only [Array.size_reverse] using consumedSize.symm) - walkerBounds.1 walkerBounds.2.1 (by simpa using walkerBounds.2.2.2.1) - walkerBounds.2.2.1 coherent walkerFaithful - (by simpa only [Nat.zero_add, Array.length_toList, consumedSize] using bodyReads) - (argumentsReading_reverse_get consumedReads consumedSize.symm) - have conversion := SynthesisInference.beta_peel_sound (consumed := arguments.take consumed.size) - (trailing := arguments.drop consumed.size) - (by simpa only [List.take_append_drop] using support) formed agreement - (by simpa only [List.take_append_drop] using reading) accepted - (by simpa only [consumedSize, Array.length_toList] using modelPeel) - let walk := simulSubst rawBody consumed.reverse 0 before.env.intern - let middle := { before with env := { before.env with intern := walk.2 } } - obtain ⟨result, after, finish, resultReads, preserved⟩ := - finishAppResult_readScopedExpr? (before := middle) walkCoherent suffixFaithful walkReads suffixReads - (methodsN (reductionFuel + 1)) - refine ⟨body, result, after, ?_, resultReads, ?_, conversion.2, preserved⟩ - · exact RecM.whnfCoreWithFlagsStep_betaMany spine rfl peeling nonempty rfl finish - · simpa only [List.take_append_drop] using conversion.1 + have peelingMeaning := LambdaPeel.betaPrefix (arguments := arguments.take consumed.size) + (by simpa only [consumedSize, Array.length_toList] using modelPeel) (arguments.drop consumed.size) + simp only [consumedSize, List.take_append_drop] at peelingMeaning + rw [peelingMeaning] at resultReads conversion + exact ⟨body, result, after, run, resultReads, conversion.1, conversion.2, preserved⟩ + +/-- A retained origin justifies the actual beta step on an intermediate +type. All inference calls belong to the origin; no new check of the +intermediate source or the generated result is required. -/ +theorem SynthesisReductionOrigin.beta_many_step {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {level : VLevel} + {rawFunction rawArgument : KExpr .anon} {appInfo : ExprInfo .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {rawDomain rawInner rawBody : KExpr .anon} {lambdaInfo : ExprInfo .anon} + {rawArguments consumed : Array (KExpr .anon)} + {condition : Certified.PropWhen} {domain inner : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds + entries context (.lam condition domain inner) arguments consumed.size level) + (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) + (spine : (KExpr.app rawFunction rawArgument appInfo).collectSpine = + (.lam name bi rawDomain rawInner lambdaInfo, rawArguments)) + (headReads : readScopedExpr? resolve locals (.lam name bi rawDomain rawInner lambdaInfo) = + some (AExpr.lam condition domain inner).erase) + (argumentReads : rawArguments.toList.map (readScopedExpr? resolve locals ·) = + arguments.map (some ·.erase)) + (peeling : RecM.consumeBetaLams (.lam name bi rawDomain rawInner lambdaInfo) rawArguments = + (rawBody, consumed)) + (nonempty : (!consumed.isEmpty) = true) + (before : TcState .anon) (reductionFuel : Nat) (flags : WhnfFlags) + (walkerBounds : SimulSubstBounds rawBody consumed.reverse 0) + (coherent : before.env.intern.WF) + (walkerFaithful : KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ + KExpr.SimulSubstReach consumed.reverse rawBody 0 term) + (suffixFaithful : KExpr.CollisionFree fun term => + (simulSubst rawBody consumed.reverse 0 before.env.intern).2.ExprSupport term ∨ + term ∈ cheapBetaChainList (simulSubst rawBody consumed.reverse 0 before.env.intern).1 + (rawArguments.extract consumed.size rawArguments.size).toList) : + ∃ (result : KExpr .anon) (after : TcState .anon), + (RecM.whnfCoreWithFlagsStep (.app rawFunction rawArgument appInfo) flags).run + (methodsN (reductionFuel + 1)) before = .ok (.next result) after ∧ + readScopedExpr? resolve locals result = + some (AExpr.betaPrefix consumed.size (.lam condition domain inner) arguments).erase ∧ + ConversionClaim.{u,v} entries context ((AExpr.lam condition domain inner).appN arguments) + (AExpr.betaPrefix consumed.size (.lam condition domain inner) arguments) ∧ + TypingClaim.{u,v} entries context + (AExpr.betaPrefix consumed.size (.lam condition domain inner) arguments) (.sort level) ∧ + after.env.intern.WF := by + obtain ⟨result, after, run, reading, _, preserved⟩ := beta_many_step_readScopedExpr? + spine headReads argumentReads peeling nonempty before reductionFuel flags + walkerBounds coherent walkerFaithful suffixFaithful + obtain ⟨conversion, typed⟩ := support.sound formed + exact ⟨result, after, run, reading, conversion, typed, preserved⟩ end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Production.lean b/Ix/Kernel/Verify/Consistency/Production.lean index 24e4ef3b6..7fd6fa7f5 100644 --- a/Ix/Kernel/Verify/Consistency/Production.lean +++ b/Ix/Kernel/Verify/Consistency/Production.lean @@ -454,6 +454,29 @@ inductive DefinitionCheckSupport {β : Type u} ((AExpr.lam condition domain inner).appN (consumed ++ trailing)).ReferencesIn entries) : DefinitionCheckSupport resolve entries trace value ((AExpr.lam condition domain inner).appN (consumed ++ trailing)) + | betaDeclaredTwice {value domain binder inner : AExpr β} {condition headCondition : Certified.PropWhen} + {initialArguments arguments : List (AExpr β)} {count : Nat} {level typeBound valueBound : VLevel} + (typeInference : SynthesisInference resolve entries [] [] [] fuel trace.validated input.type + (.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)) (.sort level) typeBound) + (valueInference : SynthesisInference resolve entries [] [] [] fuel trace.valueStart input.value + value (AExpr.betaPrefix count (.lam headCondition binder inner) + (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments)))) + valueBound) + (enough : count ≤ inner.lambdaDepth + 1) + (valueReading : readScopedExpr? resolve [] input.value = some value.erase) + (typeReading : readScopedExpr? resolve [] input.type = + some (AExpr.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)).erase) + (scope : value.Scope input.universes.toNat 0 ∧ + (AExpr.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)).Scope input.universes.toNat 0) + (references : value.ReferencesIn entries ∧ + (AExpr.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)).ReferencesIn entries) : + DefinitionCheckSupport resolve entries trace value + (.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)) theorem DefinitionCheckSupport.sound {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} @@ -485,6 +508,14 @@ theorem DefinitionCheckSupport.sound {β : Type u} exact ⟨readScopedExpr?_closed valueReading, readScopedExpr?_closed typeReading, scope.1, scope.2, references.1, references.2, valueTyped.conv typeTyped conversion.symm⟩ + | betaDeclaredTwice typeInference valueInference enough valueReading typeReading scope references => + obtain ⟨_, typeTyped, _⟩ := typeInference.closed_sound typeReading trace.typeRun + obtain ⟨conversion, _⟩ := typeInference.beta_twice_sound (.empty entries) (.empty _ _) + typeReading trace.typeRun enough + obtain ⟨_, valueTyped, _⟩ := valueInference.closed_sound valueReading trace.valueRun + exact ⟨readScopedExpr?_closed valueReading, readScopedExpr?_closed typeReading, + scope.1, scope.2, references.1, references.2, + valueTyped.conv typeTyped conversion.symm⟩ /-- Production validation supplies scope for the beta-converted declaration and its value. Both inference trees refer to the checks in this exact trace. -/ @@ -544,6 +575,44 @@ def DefinitionBodyTrace.betaDeclaredSpineSupport {β : Type u} {input : Definiti (trace.scopes typeCoverage valueCoverage collision valueReading typeReading valueConditions typeConditions) references +/-- The declared type can reduce through its outer lambda and then a +prefix of the supplied lambda. The exact declaration checks retain both +origins, including arguments already applied to the supplied lambda. -/ +def DefinitionBodyTrace.betaDeclaredTwiceSupport {β : Type u} {input : DefinitionInput} + {fuel : Nat} {before : TcState .anon} + (trace : DefinitionBodyTrace input (methodsN fuel) before) + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {value domain binder inner : AExpr β} {condition headCondition : Certified.PropWhen} + {initialArguments arguments : List (AExpr β)} {count : Nat} {level typeBound valueBound : VLevel} + {support : RunSupport} (typeCoverage : input.type.ValidationCoverage support) + (valueCoverage : input.value.ValidationCoverage support) + (collision : support.CollisionFree) + (typeInference : SynthesisInference resolve entries [] [] [] fuel trace.validated input.type + (.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)) (.sort level) typeBound) + (valueInference : SynthesisInference resolve entries [] [] [] fuel trace.valueStart input.value + value (AExpr.betaPrefix count (.lam headCondition binder inner) + (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments)))) + valueBound) + (enough : count ≤ inner.lambdaDepth + 1) + (valueReading : readScopedExpr? resolve [] input.value = some value.erase) + (typeReading : readScopedExpr? resolve [] input.type = + some (AExpr.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)).erase) + (valueConditions : ConditionsScoped input.universes.toNat value) + (typeConditions : ConditionsScoped input.universes.toNat + (.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments))) + (references : value.ReferencesIn entries ∧ + (AExpr.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)).ReferencesIn entries) : + DefinitionCheckSupport resolve entries trace value + (.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)) := + .betaDeclaredTwice typeInference valueInference enough valueReading typeReading + (trace.scopes typeCoverage valueCoverage collision valueReading typeReading + valueConditions typeConditions) references + /-- Operational support for the selected production definition fragment. Resources are required only at the states exposed by successful body traces. -/ structure AtomicDefinitionRun {β : Type u} (resolve : Address → Option (ConstRef β)) diff --git a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean index bc79ed386..76cd5af6c 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean @@ -216,6 +216,10 @@ inductive SynthesisTypingOrigin {β : Type u} (resolve : Address → Option (Con | source {incoming incomingContext incomingBounds entries context term type} (check : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type) : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type + | lambdaBody {incoming incomingContext incomingBounds entries context condition domain body codomain} + (check : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context + (.lam condition domain body) (.forallE condition domain codomain)) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries (context.push domain) body codomain | weaken {incoming incomingContext incomingBounds entries context term type} (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) (domain : AExpr β) : @@ -357,6 +361,25 @@ inductive SynthesisReductionOrigin {β : Type u} (resolve : Address → Option ( ((AExpr.lam condition binder body).liftN cutoff) (initialArguments.map (AExpr.liftN cutoff ·) ++ arguments.map (AExpr.inst · ((AExpr.lam condition binder body).appN initialArguments) cutoff)) count level + | substitutedResult {incoming incomingContext incomingBounds entries base source target + type domain binder body condition initialArguments arguments cutoff count resultType level} + (spine : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries source type arguments resultType) + (atIndex : source[cutoff]? = some type) + (checked : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries base + ((AExpr.lam condition binder body).appN initialArguments) domain) + (substitution : ContextSubstitution base domain + ((AExpr.lam condition binder body).appN initialArguments) source target cutoff) + (enough : count ≤ body.lambdaDepth + 1) + (sourceOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries target + (((AExpr.lam condition binder body).liftN cutoff).appN + (initialArguments.map (AExpr.liftN cutoff ·) ++ + arguments.map (AExpr.inst · ((AExpr.lam condition binder body).appN initialArguments) cutoff))) + (.sort level)) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries target + ((AExpr.lam condition binder body).liftN cutoff) + (initialArguments.map (AExpr.liftN cutoff ·) ++ + arguments.map (AExpr.inst · ((AExpr.lam condition binder body).appN initialArguments) cutoff)) count level | map {incoming incomingContext incomingBounds earlier priorContext head arguments count bound entries context current currentArguments level} (prior : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds @@ -898,6 +921,68 @@ def SynthesisForallBodyCheck.variableSpine {β : Type u} {resolve : Address → check.tree.variableSpineOrigin (parentOrigin.compose check.contextOrigin) check.agreement check.reading index arguments rfl +/-- Keep the actual variable-application checks inside a lambda body. +The body's inferred type is retained separately from the lambda's eventual +codomain, which synthesis may obtain by reducing that type. -/ +def BinderInference.lambdaBodyVariableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel index : Nat} + {before : TcState .anon} {source : KExpr .anon} {domain type : AExpr β} + {condition : Certified.PropWhen} {arguments : List (AExpr β)} + (support : BinderInference resolve entries locals context fuel before source + (.lam condition domain ((AExpr.bvar index).appN arguments)) type) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = + some (AExpr.lam condition domain ((AExpr.bvar index).appN arguments)).erase) : + Σ resultType, SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries (context.push domain) index arguments resultType := by + cases support with + | lam full miss trace opening absent bodyTree constructed bound coherent closingFaithful faithful => + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading + have domainAgreement := trace.contextPreserved.symm ▸ (miss.localContext.symm ▸ agreement) + obtain ⟨_, openedReads, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun + exact ⟨_, bodyTree.variableSpineOrigin openedAgreement openedReads index arguments rfl⟩ + +def SynthesisInference.lambdaBodyVariableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel index : Nat} + {before : TcState .anon} {source : KExpr .anon} {domain type : AExpr β} + {condition : Certified.PropWhen} {arguments : List (AExpr β)} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source + (.lam condition domain ((AExpr.bvar index).appN arguments)) type level) + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = + some (AExpr.lam condition domain ((AExpr.bvar index).appN arguments)).erase) : + Σ resultType, SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries (context.push domain) index arguments resultType := by + cases support with + | known inference _ => + exact inference.lambdaBodyVariableSpine agreement reading + | reuseType inference => + exact inference.lambdaBodyVariableSpine agreement reading + | lam full miss trace opening absent domainTree bodyTree conditionAgrees constructed bound coherent + closingFaithful faithful => + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + obtain ⟨_, openedReads, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun + exact ⟨_, bodyTree.variableSpineOrigin + (contextOrigin.push domainTree keyedAgreement domainReads trace.domainRun) + openedAgreement openedReads index arguments rfl⟩ + | lamBeta full miss trace opening absent domainTree bodyTree origin reduction conditionAgrees + constructed bound closingFaithful faithful => + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + obtain ⟨_, openedReads, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun + exact ⟨_, bodyTree.variableSpineOrigin + (contextOrigin.push domainTree keyedAgreement domainReads trace.domainRun) + openedAgreement openedReads index arguments rfl⟩ + theorem BinderInference.lambdaPrefix {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {fuel : Nat} @@ -1145,6 +1230,7 @@ theorem SynthesisTypingOrigin.sound {β : Type u} {resolve : Address → Option TypingClaim.{u,v} entries context term type := match support with | .source check => (check.soundWithSpine formed).1 + | .lambdaBody check => (check.soundWithSpine formed).1.lambdaBody | .weaken prior domain => typing_weaken (prior.sound formed) | .instantiate prior arguments => typing_instL_context (prior.sound formed) arguments | .appendContext prior outer => typing_append_context (prior.sound formed) outer @@ -1231,6 +1317,10 @@ theorem SynthesisReductionOrigin.sound {β : Type u} {resolve : Address → Opti exact ((leading.liftN _ 0).truncate enough).beta_sound (substitution.lift_typing typed) instantiated | .substitutedApplication spine atIndex checked substitution enough => (checked.soundWithSpine formed).2.2.substituteHead (spine.sound formed) atIndex substitution enough + | .substitutedResult spine atIndex checked substitution enough sourceOrigin => by + obtain ⟨converted, resultTyped⟩ := + (checked.soundWithSpine formed).2.2.substituteHead (spine.sound formed) atIndex substitution enough + exact ⟨converted, (sourceOrigin.sound formed).termConv resultTyped converted⟩ | .map prior transport => by obtain ⟨converted, typed⟩ := prior.sound formed exact transport.sound formed converted typed diff --git a/Ix/Theory/Model/BetaSpine.lean b/Ix/Theory/Model/BetaSpine.lean index 9f5c85731..3cc2f243c 100644 --- a/Ix/Theory/Model/BetaSpine.lean +++ b/Ix/Theory/Model/BetaSpine.lean @@ -124,6 +124,35 @@ theorem inst_betaPrefix (count : Nat) (head : AExpr β) (arguments : List (AExpr end AExpr +/-- The body's type follows from a typed lambda whose product retains +the same syntactic domain. The new context supplies its fresh argument. -/ +theorem TypingClaim.lambdaBody {β : Type u} {entries : Environment β} {context : Context β} + {condition : PropWhen} {domain body codomain : AExpr β} + (typed : TypingClaim.{u,v} entries context (.lam condition domain body) + (.forallE condition domain codomain)) : + TypingClaim.{u,v} entries (context.push domain) body codomain := by + have lifted : TypingClaim.{u,v} entries (context.push domain) + ((AExpr.lam condition domain body).liftN 1) ((AExpr.forallE condition domain codomain).liftN 1) := by + intro V _ constants realizes levels env valid + simpa only [wellDenoted_liftN, interp_liftN] using + typed V constants realizes levels _ valid.tail + have localTyped : TypingClaim.{u,v} entries (context.push domain) (.bvar 0) (domain.liftN 1) := + TypingClaim.bvar (by rfl) + simpa only [AExpr.liftN, AExpr.inst_liftN_self] using lifted.betaResult localTyped + +/-- A reduction with a valid result preserves every type already known +for its source, even when the reduction's argument spine has another result type. -/ +theorem TypingClaim.termConv {β : Type u} {entries : Environment β} {context : Context β} + {source result type otherType : AExpr β} + (typed : TypingClaim.{u,v} entries context source type) + (resultTyped : TypingClaim.{u,v} entries context result otherType) + (converted : ConversionClaim.{u,v} entries context source result) : + TypingClaim.{u,v} entries context result type := by + intro V _ constants realizes levels env valid + obtain ⟨_, typeValid, member⟩ := typed V constants realizes levels env valid + exact ⟨(resultTyped V constants realizes levels env valid).1, typeValid, + converted V constants realizes levels env valid ▸ member⟩ + /-- Each leading lambda agrees with the corresponding inferred Pi. The count limits this claim to the original syntactic prefix. -/ inductive LambdaPrefix {β : Type u} : AExpr β → AExpr β → Nat → Prop diff --git a/Ix/Theory/Model/BetaSubstitution.lean b/Ix/Theory/Model/BetaSubstitution.lean index b33ff91a3..2e83edc3f 100644 --- a/Ix/Theory/Model/BetaSubstitution.lean +++ b/Ix/Theory/Model/BetaSubstitution.lean @@ -16,6 +16,22 @@ universe u term.liftN 0 cutoff = term := by induction term generalizing cutoff <;> simp_all [liftN, liftVar] +/-- Opening a lifted binder with the corresponding fresh local leaves +the body's existing de Bruijn indices unchanged. -/ +theorem inst_liftN_self (term : AExpr β) (cutoff : Nat := 0) : + (term.liftN 1 (cutoff + 1)).inst (.bvar 0) cutoff = term := by + induction term generalizing cutoff with + | bvar index => + by_cases below : index < cutoff + · simp [liftN, liftVar, inst, instVar, below, show index < cutoff + 1 by omega] + · by_cases equal : index = cutoff + · subst index + simp [liftN, liftVar, inst, instVar, show cutoff < cutoff + 1 by omega] + · simp [liftN, liftVar, inst, instVar, + show ¬ index < cutoff + 1 by omega, show ¬ 1 + index < cutoff by omega, + show 1 + index ≠ cutoff by omega, show 1 + index - 1 = index by omega] + | _ => simp_all [liftN, inst, Nat.add_assoc] + theorem instL_liftN (term : AExpr β) (levels : List VLevel) (count cutoff : Nat) : (term.liftN count cutoff).instL levels = (term.instL levels).liftN count cutoff := by induction term generalizing cutoff <;> simp_all [liftN, instL] diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index ac6854fc1..458213ef7 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -952,6 +952,128 @@ private def composedLambdaTypeResult (shape : Nat) (level : Ixon.Univ) : Bool := | .ok passed after => passed && after.lctx.size == 0 | .error _ _ => false +/-- The outer lambda applies its parameter. Its first beta step exposes +the supplied lambda's separate prefix; the next step consumes that prefix. +Shape 4 also changes the outer body's inferred type from a beta sort to +the sort itself. Shape 6 retains an application suffix after the second step. -/ +private def repeatedBetaDeclaredType (level : Ixon.Univ) (universes : UInt64 := 0) + (shape wrong : Nat := 0) : Ixon.Env × Address := Id.run do + let levels := if universes == 0 then #[] else #[0] + let betaSort := Ixon.Expr.app (.leanLam (.sort 1) (.var 0)) (.sort 0) + let carrierSort := if shape == 4 then betaSort else .sort 0 + let (env, carrier) := storeConst {} + ⟨.axio ⟨false, universes, carrierSort⟩, #[], #[], #[level, .succ level]⟩ + let (env, otherCarrier) := storeConst env + ⟨.axio ⟨false, universes, carrierSort⟩, #[], #[], #[level, .succ level, .succ (.succ level)]⟩ + let familyType := Ixon.Expr.leanAll (.sort 0) (.sort 0) + let (env, family) := storeConst env + ⟨.axio ⟨false, universes, familyType⟩, #[], #[], #[level]⟩ + let (env, witness) := storeConst env + ⟨.axio ⟨false, universes, + if shape == 6 then .app (.ref 0 levels) (.ref 1 levels) else .ref 0 levels⟩, + #[], if shape == 6 then #[family, carrier] else #[carrier], #[level]⟩ + let domain := if shape == 4 then Ixon.Expr.leanAll betaSort betaSort + else if shape == 5 then .leanAll (.sort 0) (.leanAll (.var 0) (.sort 0)) + else familyType + let supplied := if wrong == 1 then Ixon.Expr.leanLam (.sort 1) (.var 0) + else if shape == 1 then .leanLam (.sort 0) (.ref 0 levels) + else if shape == 2 then .app (.leanLam (.sort 0) (.leanLam (.sort 0) (.var 1))) (.ref 0 levels) + else if shape == 3 then + .app (.app (.leanLam (.sort 0) (.leanLam (.var 0) (.leanLam (.sort 0) (.var 2)))) + (.ref 0 levels)) (.ref (if wrong == 3 then 0 else 2) levels) + else if shape == 4 then .app (.leanLam (.sort 1) (.leanLam (.var 0) (.var 0))) betaSort + else if shape == 5 then .leanLam (.sort 0) (.leanLam (.var 0) (.var 1)) + else if shape == 6 then .app (.leanLam familyType (.var 0)) (.ref 3 levels) + else .leanLam (.sort 0) (.var 0) + let body := if shape == 5 then + Ixon.Expr.app (.app (.var 0) (.ref 0 levels)) (.ref (if wrong == 3 then 0 else 2) levels) + else .app (.var 0) (.ref (if shape == 1 || shape == 2 || shape == 3 || wrong == 2 then 1 else 0) levels) + return storeConst env + ⟨.defn ⟨.defn, .safe, universes, .app (.leanLam domain body) supplied, .ref 2 levels⟩, + #[], #[carrier, otherCarrier, witness, family], #[level, .succ level]⟩ + +/-- Observe the two actual WHNF steps and the change of lambda head. +The second result must be exactly the value's already inferred type. -/ +private def repeatedBetaDeclaredResult (shape : Nat) (level : Ixon.Univ) : Bool := + let (env, target) := repeatedBetaDeclaredType level 0 shape + let action : RecM .anon Bool := do + let concrete ← TcM.getConst (m := .anon) ⟨target, ()⟩ + let .defn _ _ _ _ _ _ type value _ _ := concrete | return false + let .app outer supplied _ := type | return false + let .lam _ _ _ body _ := outer | return false + let expected ← RecM.inferCall value + let .sort .. ← RecM.inferCall type | return false + let (suppliedHead, initialArguments) := supplied.collectSpine + let firstConsumed := (RecM.consumeBetaLams type.collectSpine.1 type.collectSpine.2).2 + let before ← get + let .next middle ← RecM.whnfCoreWithFlagsStep type .DEF_EQ_CORE | return false + let (middleHead, middleArguments) := middle.collectSpine + let secondConsumed := (RecM.consumeBetaLams middleHead middleArguments).2 + let .next result ← RecM.whnfCoreWithFlagsStep middle .DEF_EQ_CORE | return false + let after ← get + let converted ← RecM.isDefEqCall expected type + return firstConsumed == #[supplied] && middleHead == suppliedHead && middleHead != outer && + middleArguments == initialArguments ++ body.collectSpine.2 && + secondConsumed.size == (if shape == 3 then 3 else if shape >= 2 && shape <= 5 then 2 else 1) && + result == expected && type != middle && middle != result && converted && + after.lctx.size == before.lctx.size && after.env.nextFVarId == before.env.nextFVarId + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed _ => passed + | .error _ _ => false + +private def repeatedBetaChangedBodyType (level : Ixon.Univ) : Bool := + let (env, target) := repeatedBetaDeclaredType level 0 4 + let action : RecM .anon Bool := RecM.withLctxScope do + let concrete ← TcM.getConst (m := .anon) ⟨target, ()⟩ + let .defn _ _ _ _ _ _ (.app outer _ _) _ _ _ := concrete | return false + let .lam name bi domain body _ := outer | return false + let .all _ _ _ codomain _ ← RecM.inferCall outer | return false + let (opened, _) ← TcM.openBinder name bi domain body + let bodyType ← RecM.inferCall opened + let reduced ← TcM.runIntern (cheapBetaReduce bodyType) + return bodyType != codomain && (cheapBetaPlan? bodyType).isSome && reduced == codomain && + match codomain with | .sort .. => true | _ => false + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +private def repeatedBetaCases : TestSeq := + test "successive beta: a supplied identity provides the second lambda origin in Prop and Type" + (allSucceeded (repeatedBetaDeclaredType .zero).1 5 && + allSucceeded (repeatedBetaDeclaredType (.succ .zero)).1 5) + ++ test "successive beta: a captured carrier survives a distinct body argument" + (allSucceeded (repeatedBetaDeclaredType .zero 0 1).1 5 && + allSucceeded (repeatedBetaDeclaredType (.succ .zero) 0 1).1 5) + ++ test "successive beta: a partial lambda joins its initial and body arguments" + (allSucceeded (repeatedBetaDeclaredType .zero 0 2).1 5 && + allSucceeded (repeatedBetaDeclaredType (.succ .zero) 0 2).1 5) + ++ test "successive beta: dependent initial and body arguments keep their order" + (allSucceeded (repeatedBetaDeclaredType .zero 0 3).1 5 && + allSucceeded (repeatedBetaDeclaredType (.succ .zero) 0 5).1 5) + ++ test "successive beta: changed body typing retains the original argument checks" + (allSucceeded (repeatedBetaDeclaredType .zero 0 4).1 5 && + allSucceeded (repeatedBetaDeclaredType (.succ .zero) 0 4).1 5) + ++ test "successive beta: the second prefix leaves the remaining family application" + (allSucceeded (repeatedBetaDeclaredType .zero 0 6).1 5 && + allSucceeded (repeatedBetaDeclaredType (.succ .zero) 0 6).1 5) + ++ test "successive beta: both origins and the changed body type retain universe parameters" + ((List.range 7).all fun shape => allSucceeded (repeatedBetaDeclaredType (.var 0) 1 shape).1 5) + ++ test "successive beta: the same declarations check with fresh per-item caches" + ((List.range 7).all fun shape => allSucceeded (repeatedBetaDeclaredType .zero 0 shape).1 5 { clearEvery := 1 }) + ++ test "successive beta: both WHNF steps return the exact expected type in Prop and Type" + ((List.range 7).all fun shape => repeatedBetaDeclaredResult shape .zero && + repeatedBetaDeclaredResult shape (.succ .zero)) + ++ test "successive beta: cheap beta changes the original body's type hash" + (repeatedBetaChangedBodyType .zero && repeatedBetaChangedBodyType (.succ .zero)) + ++ test "successive beta: a supplied lambda with a different domain is rejected" + (let (env, target) := repeatedBetaDeclaredType .zero 0 0 1; rowFailed env target) + ++ test "successive beta: selecting the other carrier cannot type the original witness" + (let (env, target) := repeatedBetaDeclaredType .zero 0 0 2; rowFailed env target) + ++ test "successive beta: initial and body arguments must inhabit their dependent domains" + (let (env, first) := repeatedBetaDeclaredType .zero 0 3 3 + let (otherEnv, second) := repeatedBetaDeclaredType .zero 0 5 3 + rowFailed env first && rowFailed otherEnv second) + private def applicationCases : TestSeq := test "application environment: Prop/Type identity calls and transitive theorem calls check" (allSucceeded applicationEnvironment 5 { clearEvery := 0 }) @@ -2741,7 +2863,7 @@ private def polymorphicDefinitionCases : TestSeq := public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, multiBetaCases, cheapLambdaCases, - cheapApplicationCases, exposedLambdaCases, + cheapApplicationCases, exposedLambdaCases, repeatedBetaCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, sourceAgreementCases, sourceCacheCases, polymorphicDefinitionCases] diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 7b0f740a8..94cc7c7cc 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -512,7 +512,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 559 exact theorem boundaries. The production +The consistency target checks 570 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -570,8 +570,21 @@ The stronger checked-origin result also retains an argument's existing lambda-headed application spine. `LambdaSpineTyping.substituteHead` combines it with the original variable application's spine under the retained context, so a selected reduction can cross from one argument list into the other. -Automatic origin construction for arbitrary generated types, repeated reduction, and other -conversion paths remain open. +`lambdaBodyVariableSpine` additionally extracts the actual argument checks +of a lambda body that applies its parameter. Their original result type can +differ from the lambda's codomain after cheap beta. `betaResultOrigin` +retains typing of the first substitution result at the current codomain; +`betaNextOrigin` combines it with the supplied lambda's checked prefix. +`beta_twice_sound` consequently composes two successive prefixes with +different lambda origins. `SynthesisReductionOrigin.beta_many_step` connects +the retained origin to the actual WHNF step on the intermediate type. +`DefinitionCheckSupport.betaDeclaredTwice` uses this conversion at the +existing declaration and environment admission boundary. Neither connector +requires inference of the intermediate term. The model's lambda-body and +term-conversion lemmas use only standard Lean axioms, and these production +roots retain the same two existing native output-length proofs. +Automatic origins for arbitrary generated types, further repeated reduction, +and other conversion paths remain open. Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, argument order, and rejection of wrong arities and out-of-range parameters. @@ -621,6 +634,14 @@ combined argument order and two- or three-lambda reduction counts for both cheap-beta plans, including dependent initial arguments and caller locals. They cover Prop, Type, universe parameters, cache clearing/reuse, and failures for an ill-typed initial argument or a different selected carrier. +Successive-beta cases observe two actual WHNF steps with distinct lambda +heads and require the second result to equal the value's inferred type. +They include captured carriers, partial applications, dependent initial and +body arguments, a remaining family-application suffix, universe parameters, +and cache clearing. One case changes the first lambda body's inferred type +from a beta redex to a sort, then consumes the supplied lambda's two-argument +prefix. Negative cases reject a wrong function domain, a different selected +carrier, and invalid dependent arguments from either origin. These execution tests do not construct the general finite inference resources. Polymorphic-call regressions include Prop/Type instances in real function bodies, `max`/`imax` simplification inside Pi domains, closed nested references @@ -691,7 +712,7 @@ Definition-cycle regressions use content-addressed standalone and mutual declarations, including a self-justifying theorem, a two-member cycle, type cycles, lets, shared syntax, and binders. They check repeated member failures, acyclic forward references, cache clearing, and the partial/unsafe policy. -The unit suite contains 655 checks. The anonymous differential additionally +The unit suite contains 668 checks. The anonymous differential additionally serializes eight cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. diff --git a/docs/theory.md b/docs/theory.md index c26b39483..63a0115ec 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -78,8 +78,15 @@ its already checked arguments: they precede the original codomain's arguments in the combined spine, with both sets of dependent types preserved. The selected beta prefix can consume arguments from both origins. The lambda closes the reduced type using the actual final intern table. Each reduction -is bounded by the checked prefix of its selected origin; further exposed -prefixes need further origins. +is bounded by the checked prefix of its selected origin. +Two successive prefixes now compose when the first lambda's body applies +its parameter and substitution exposes the supplied lambda. The body's +original argument checks remain available even if cheap beta changes its +inferred type. The first reduction retains typing at that current type; +the supplied lambda contributes the second prefix's domains and any initial +arguments. This supports the actual WHNF step on the intermediate term and +declaration admission through both reductions, without another inference +call on the intermediate term. Further exposed prefixes need further origins. Automatic origin construction for arbitrary generated types, general reduction, and conversion remain open. Safe definition admission also rejects circular justification in both Lean and From 3377f3293c0685afd2a8c982ea4a528e05f32e5f Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 06:16:35 -0400 Subject: [PATCH 31/63] Compose finite beta traces through production WHNF --- Ix/Kernel/Verify/Consistency.lean | 11 +- Ix/Kernel/Verify/Consistency/Audit.lean | 23 +- Ix/Kernel/Verify/Consistency/Beta.lean | 2 +- Ix/Kernel/Verify/Consistency/BetaTrace.lean | 195 +++++++++++++++++ Ix/Kernel/Verify/Consistency/BetaWhnf.lean | 205 ++++++++++++++++++ Ix/Kernel/Verify/Consistency/Production.lean | 74 ++++++- .../Consistency/SynthesisInference.lean | 138 +++++++++++- Tests/Ix/Kernel/Consistency.lean | 91 +++++++- docs/kernel-verification.md | 28 ++- docs/theory.md | 12 +- 10 files changed, 762 insertions(+), 17 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/BetaTrace.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaWhnf.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 94abe42a1..64c243ea2 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -33,6 +33,8 @@ import Ix.Kernel.Verify.Consistency.Simultaneous import Ix.Kernel.Verify.Consistency.SpineReading import Ix.Kernel.Verify.Consistency.CheapBetaReading import Ix.Kernel.Verify.Consistency.BetaSpine +import Ix.Kernel.Verify.Consistency.BetaTrace +import Ix.Kernel.Verify.Consistency.BetaWhnf import Ix.Kernel.Verify.Consistency.CheapBeta import Ix.Kernel.Verify.Consistency.Validation import Ix.Kernel.Verify.Consistency.RecursiveCache @@ -113,7 +115,14 @@ the supplied lambda supplies the next checked prefix. Two successive prefixes therefore compose without inference of the intermediate term. Retained origins justify the actual multi-argument WHNF step, and declaration admission includes this two-prefix conversion. Automatic origin construction -for arbitrary generated types and general reduction remain open. +for arbitrary generated types remains open. Finite beta traces now compose +any number of retained prefixes. An earlier result can supply a later lambda +or argument origin, and type transport preserves the original source type. +Application suffixes and dependent substitutions retain their checks. +The corresponding structural-WHNF trace computes each raw result and intern +table, then proves the actual uncached loop under its fuel bound. The same +traces justify definition conversion. Automatic construction of these finite +resources and the remaining WHNF/conversion paths remain open. Local cache hits agree with the actual declaration type. Constant hits agree with pure universe substitution of a loaded, admitted declaration; sort hits return the canonical successor sort. Application, forall, and lambda diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 1d9e24329..676e06928 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -438,6 +438,26 @@ private def repeatedBetaRoots : Array RootAllowance := #[ { root := ``DefinitionBodyTrace.betaDeclaredTwiceSupport, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } ] +private def betaTraceRoots : Array RootAllowance := #[ + { root := ``SynthesisTypingOrigin.applySpine, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTrace.applySpine, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTrace.beta, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BinderInference.spineOrigin, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.spineOrigin, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisSpineOrigin.betaTrace, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.betaSpineTrace, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTrace.sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaStep.run, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaStep.sourceReading, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaStep.reading, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaWhnfTrace.toBetaTrace, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaWhnfTrace.run, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaWhnfTrace.reading, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaWhnfTrace.uncached_sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``DefinitionBodyTrace.betaDeclaredTraceSupport, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``DefinitionBodyTrace.betaDeclaredWhnfSupport, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -565,7 +585,8 @@ def roots : Array RootAllowance := #[ }) ++ productionRoots.map (fun root => { root, standardAxioms := standard, nativeAxioms := productionNative, forbiddenDependencies := forbiddenProduction -}) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots ++ exposedOriginRoots ++ repeatedBetaRoots).map (fun allowance => { +}) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots ++ exposedOriginRoots ++ + repeatedBetaRoots ++ betaTraceRoots).map (fun allowance => { allowance with forbiddenDependencies := forbiddenProduction }) ++ #[{ root := ``extend_atomic_definition, standardAxioms := standard }] diff --git a/Ix/Kernel/Verify/Consistency/Beta.lean b/Ix/Kernel/Verify/Consistency/Beta.lean index 7cd9ac7ca..2c7cd340b 100644 --- a/Ix/Kernel/Verify/Consistency/Beta.lean +++ b/Ix/Kernel/Verify/Consistency/Beta.lean @@ -91,7 +91,7 @@ def SynthesisInference.betaResultOrigin {β : Type u} {resolve : Address → Opt obtain ⟨functionReads, argumentReads⟩ := readScopedExpr?_app_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement exact (SynthesisTypingOrigin.lambdaBody - (.checked contextOrigin functionTree keyedAgreement functionReads trace.functionRun)).substituteAt + (.source (.checked contextOrigin functionTree keyedAgreement functionReads trace.functionRun))).substituteAt (.source (.applicationArgument contextOrigin trace functionTree argumentTree keyedAgreement functionReads argumentReads conditions hashPath comparisonFaithful)) .root diff --git a/Ix/Kernel/Verify/Consistency/BetaTrace.lean b/Ix/Kernel/Verify/Consistency/BetaTrace.lean new file mode 100644 index 000000000..16e7133ca --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaTrace.lean @@ -0,0 +1,195 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaSpine + +/-! Beta traces built from actual inference calls and previously derived +typing origins. Each step retains its lambda domains and argument checks; +the next step can use the preceding result without another inference call. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- Apply retained argument checks to a generated function origin. -/ +def SynthesisTypingOrigin.applySpine {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {head headType type : AExpr β} {arguments : List (AExpr β)} + (origin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context head headType) + (spine : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries context headType arguments type) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context (head.appN arguments) type := + match spine with + | .nil _ => origin + | .snoc prior checked => by + simpa only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] using + (origin.applySpine prior).application checked +termination_by structural spine + +/-- A function reduction carries its original dependent argument checks +through the complete application suffix. -/ +def SynthesisBetaTrace.applySpine {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {source result headType type : AExpr β} {arguments : List (AExpr β)} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result headType) + (spine : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries context headType arguments type) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (source.appN arguments) (result.appN arguments) type := + match spine with + | .nil _ => trace + | .snoc prior checked => by + simpa only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] using + (trace.applySpine prior).application checked +termination_by structural spine + +/-- One beta step may use a lambda produced by an earlier trace. Its +retained product has the lambda's exact syntactic domain. -/ +def SynthesisBetaTrace.beta {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {condition : Certified.PropWhen} {domain body codomain argument : AExpr β} + (functionOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context + (.lam condition domain body) (.forallE condition domain codomain)) + (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context argument domain) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (.app (.lam condition domain body) argument) (body.inst argument) (codomain.inst argument) := by + simpa only [List.nil_append, AExpr.appN_cons, AExpr.appN_nil, AExpr.betaPrefix] using + SynthesisBetaTrace.prefix functionOrigin (.lam (.zero _ _)) + ((SynthesisArgumentSpineOrigin.nil _).snoc argumentOrigin) + +/-- The head's check and every argument check of an actual application +spine, retained as data for subsequent reductions and substitutions. -/ +structure SynthesisSpineOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) + (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) + (entries : Model.Environment β) (context : Model.Context β) + (head : AExpr β) (arguments : List (AExpr β)) (type : AExpr β) where + headType : AExpr β + headOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context head headType + leading : LambdaPrefix head headType head.lambdaDepth + argumentsOrigin : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries context headType arguments type + +private theorem appN_last {β : Type u} {head : AExpr β} {arguments : List (AExpr β)} + (nonempty : arguments ≠ []) : + head.appN arguments = (head.appN arguments.dropLast).app (arguments.getLast nonempty) := by + calc + head.appN arguments = head.appN (arguments.dropLast ++ [arguments.getLast nonempty]) := + congrArg (AExpr.appN head) (List.dropLast_concat_getLast nonempty).symm + _ = _ := by simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] + +private theorem nonapp_spine_empty {β : Type u} {term head : AExpr β} {arguments : List (AExpr β)} + (notApp : ∀ fn arg, term ≠ .app fn arg) (same : term = head.appN arguments) : arguments = [] := by + by_contra nonempty + exact notApp _ _ (same.trans (appN_last nonempty)) + +private theorem app_spine_parts {β : Type u} {fn arg head : AExpr β} {arguments : List (AExpr β)} + (nonempty : arguments ≠ []) (same : fn.app arg = head.appN arguments) : + arguments = arguments.dropLast ++ [arg] ∧ fn = head.appN arguments.dropLast := by + have parts := AExpr.app.inj (same.trans (appN_last nonempty)) + exact ⟨by rw [parts.2]; exact (List.dropLast_concat_getLast nonempty).symm, parts.1⟩ + +def BinderInference.spineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source term type) + (checked : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (head : AExpr β) (arguments : List (AExpr β)) (same : term = head.appN arguments) : + SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := + if empty : arguments = [] then by + subst arguments + simp only [AExpr.appN_nil] at same + subst head + exact ⟨type, .source checked, support.lambdaPrefix, .nil _⟩ + else match support with + | .app _ miss trace functionTree functionHead argumentTree conditions hashPath comparisonFaithful + _ _ _ _ _ _ => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have parts := app_spine_parts empty same + have prior := functionTree.spineOrigin (incoming := incoming) (incomingContext := incomingContext) + (incomingBounds := incomingBounds) + (.binderHead functionTree functionHead keyedAgreement functionReading trace.functionRun) + keyedAgreement functionReading head arguments.dropLast parts.2 + refine ⟨prior.headType, prior.headOrigin, prior.leading, ?_⟩ + simpa only [← parts.1] using prior.argumentsOrigin.snoc + (.source (.binderArgument trace functionTree functionHead argumentTree keyedAgreement + functionReading argumentReading conditions hashPath comparisonFaithful)) + | .sort .. | .cachedSort .. | .fvar .. | .const .. | .polymorphic .. | .cachedConst .. | .forallE .. | .lam .. => by + exact False.elim (empty (nonapp_spine_empty (by intro fn arg same; cases same) same)) +termination_by structural support + +/-- Recover every application argument and the head's original checked +lambda prefix from the executed source inference, including binder-backed +local and constant spines. -/ +def SynthesisInference.spineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) + (head : AExpr β) (arguments : List (AExpr β)) (same : term = head.appN arguments) : + SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := + if empty : arguments = [] then by + subst arguments + simp only [AExpr.appN_nil] at same + subst head + exact ⟨type, .source (.checked contextOrigin support agreement reading accepted), support.lambdaPrefix, .nil _⟩ + else match support with + | .known inference formation => + inference.spineOrigin (.checked contextOrigin (.known inference formation) agreement reading accepted) + agreement reading head arguments same + | .reuseType inference typeTree extension typeReading typeRun equivalent => + inference.spineOrigin + (.checked contextOrigin (.reuseType inference typeTree extension typeReading typeRun equivalent) + agreement reading accepted) agreement reading head arguments same + | .app _ miss trace functionTree argumentTree conditions hashPath comparisonFaithful _ _ _ _ _ _ => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have parts := app_spine_parts empty same + have prior := functionTree.spineOrigin contextOrigin keyedAgreement functionReading trace.functionRun + head arguments.dropLast parts.2 + refine ⟨prior.headType, prior.headOrigin, prior.leading, ?_⟩ + simpa only [← parts.1] using prior.argumentsOrigin.snoc + (.source (.applicationArgument contextOrigin trace functionTree argumentTree keyedAgreement + functionReading argumentReading conditions hashPath comparisonFaithful)) + | .fvar .. | .forallE .. | .lam .. | .lamBeta .. => by + exact False.elim (empty (nonapp_spine_empty (by intro fn arg same; cases same) same)) +termination_by structural support + +def SynthesisSpineOrigin.betaTrace {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {head type : AExpr β} {arguments : List (AExpr β)} {count : Nat} + (origin : SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type) + (enough : count ≤ head.lambdaDepth) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (head.appN arguments) (AExpr.betaPrefix count head arguments) type := + .prefix origin.headOrigin (origin.leading.truncate enough) origin.argumentsOrigin + +/-- Any original lambda prefix selected from the source inference starts +a composable trace whose result can be used as another typing origin. -/ +def SynthesisInference.betaSpineTrace {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel count : Nat} + {before after : TcState .anon} {source result : KExpr .anon} {head type : AExpr β} + {arguments : List (AExpr β)} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source (head.appN arguments) type level) + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some (head.appN arguments).erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) + (enough : count ≤ head.lambdaDepth) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (head.appN arguments) (AExpr.betaPrefix count head arguments) type := + (support.spineOrigin contextOrigin agreement reading accepted head arguments rfl).betaTrace enough + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnf.lean b/Ix/Kernel/Verify/Consistency/BetaWhnf.lean new file mode 100644 index 000000000..93bdb257f --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaWhnf.lean @@ -0,0 +1,205 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaTrace + +/-! Finite beta traces for the actual structural-WHNF loop. Every next +state is computed by production's simultaneous substitution and suffix +interning, and the loop consumes the same fuel as `runBounded`. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- Resources for one actual beta iteration. The raw output and next +state are computed below; no result reading or semantic equality is a field. -/ +structure SynthesisBetaStep {β : Type u} (resolve : Address → Option (ConstRef β)) + (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) + (entries : Model.Environment β) (context : Model.Context β) (locals : List FVarId) + (before : TcState .anon) (source : KExpr .anon) (term type : AExpr β) where + rawFunction : KExpr .anon + rawArgument : KExpr .anon + appInfo : ExprInfo .anon + sourceEq : source = .app rawFunction rawArgument appInfo + name : Mode.anon.F Name + bi : Mode.anon.F Lean.BinderInfo + rawDomain : KExpr .anon + rawInner : KExpr .anon + lambdaInfo : ExprInfo .anon + rawArguments : Array (KExpr .anon) + rawBody : KExpr .anon + consumed : Array (KExpr .anon) + condition : Certified.PropWhen + domain : AExpr β + inner : AExpr β + arguments : List (AExpr β) + modelSource : term = (AExpr.lam condition domain inner).appN arguments + meaning : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term + (AExpr.betaPrefix consumed.size (.lam condition domain inner) arguments) type + spine : (KExpr.app rawFunction rawArgument appInfo).collectSpine = + (.lam name bi rawDomain rawInner lambdaInfo, rawArguments) + headReads : readScopedExpr? resolve locals (.lam name bi rawDomain rawInner lambdaInfo) = + some (AExpr.lam condition domain inner).erase + argumentReads : rawArguments.toList.map (readScopedExpr? resolve locals ·) = arguments.map (some ·.erase) + peeling : RecM.consumeBetaLams (.lam name bi rawDomain rawInner lambdaInfo) rawArguments = (rawBody, consumed) + nonempty : (!consumed.isEmpty) = true + walkerBounds : SimulSubstBounds rawBody consumed.reverse 0 + walkerFaithful : KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ + KExpr.SimulSubstReach consumed.reverse rawBody 0 term + suffixFaithful : KExpr.CollisionFree fun term => + (simulSubst rawBody consumed.reverse 0 before.env.intern).2.ExprSupport term ∨ + term ∈ cheapBetaChainList (simulSubst rawBody consumed.reverse 0 before.env.intern).1 + (rawArguments.extract consumed.size rawArguments.size).toList + +namespace SynthesisBetaStep + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {before : TcState .anon} + {source : KExpr .anon} {term type : AExpr β} + +def output (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) : + KExpr .anon × InternTable .anon := + let walk := simulSubst step.rawBody step.consumed.reverse 0 before.env.intern + internAppChain walk.1 (step.rawArguments.extract step.consumed.size step.rawArguments.size).toList walk.2 + +def result (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) : + KExpr .anon := step.output.1 + +def after (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) : + TcState .anon := { before with env := { before.env with intern := step.output.2 } } + +def modelResult (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) : + AExpr β := AExpr.betaPrefix step.consumed.size (.lam step.condition step.domain step.inner) step.arguments + +theorem run (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) + (reductionFuel : Nat) (flags : WhnfFlags) : + (RecM.whnfCoreWithFlagsStep source flags).run (methodsN (reductionFuel + 1)) before = + .ok (.next step.result) step.after := by + simp only [step.sourceEq] + apply RecM.whnfCoreWithFlagsStep_betaMany step.spine rfl step.peeling step.nonempty rfl + rw [RecM.finishAppResult_eq_internAppChain] + rfl + +theorem sourceReading + (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) : + readScopedExpr? resolve locals source = some term.erase := by + simp only [step.sourceEq, step.modelSource] + exact readScopedExpr?_collectSpine step.spine step.headReads step.argumentReads + +theorem reading + (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) + (coherent : before.env.intern.WF) : + readScopedExpr? resolve locals step.result = some step.modelResult.erase ∧ step.after.env.intern.WF := by + obtain ⟨result, after, run, reading, _, preserved⟩ := beta_many_step_readScopedExpr? + step.spine step.headReads step.argumentReads step.peeling step.nonempty before 0 .FULL + step.walkerBounds coherent step.walkerFaithful step.suffixFaithful + have actual := step.run 0 .FULL + simp only [step.sourceEq] at actual + rw [actual] at run + cases run + exact ⟨reading, preserved⟩ + +end SynthesisBetaStep + +/-- A complete finite beta path through structural WHNF. The final +iteration returns its input; every preceding iteration has a computed +substitution result and intern table, with no postulated intermediate check. -/ +inductive SynthesisBetaWhnfTrace {β : Type u} (resolve : Address → Option (ConstRef β)) + (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) + (entries : Model.Environment β) (context : Model.Context β) (locals : List FVarId) + (reductionFuel : Nat) (flags : WhnfFlags) : + Nat → TcState .anon → KExpr .anon → AExpr β → TcState .anon → KExpr .anon → AExpr β → Type u + | done {before source term} + (finished : (RecM.whnfCoreWithFlagsStep source flags).run (methodsN (reductionFuel + 1)) before = + .ok (.done source) before) : + SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags + 0 before source term before source term + | next {steps before after source result term target type} + (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) + (rest : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags + steps step.after step.result step.modelResult after result target) : + SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags + (steps + 1) before source term after result target + +namespace SynthesisBetaWhnfTrace + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {reductionFuel steps : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} + +def toBetaTrace {steps : Nat} {before after : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (trace : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags + steps before source term after result target) {type : AExpr β} + (origin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term target type := + match trace with + | .done _ => .refl origin + | .next step rest => + let first := SynthesisBetaTrace.atType origin step.meaning + first.trans (rest.toBetaTrace (.reduced first)) +termination_by structural trace + +theorem run + (trace : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags + steps before source term after result target) {loopFuel : Nat} (enough : steps < loopFuel) : + (RecM.runBounded (fun current => RecM.whnfCoreWithFlagsStep current flags) loopFuel source).run + (methodsN (reductionFuel + 1)) before = .ok result after := by + induction trace generalizing loopFuel with + | done finished => + cases loopFuel with + | zero => omega + | succ loopFuel => + rw [RecM.runBounded, ReaderT.run_bind] + change EStateM.bind ((RecM.whnfCoreWithFlagsStep _ flags).run _) _ _ = _ + unfold EStateM.bind + rw [finished] + rfl + | next step rest ih => + cases loopFuel with + | zero => omega + | succ loopFuel => + rw [RecM.runBounded, ReaderT.run_bind] + change EStateM.bind ((RecM.whnfCoreWithFlagsStep _ flags).run _) _ _ = _ + unfold EStateM.bind + rw [step.run reductionFuel flags] + exact ih (by omega) + +theorem reading + (trace : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags + steps before source term after result target) + (sourceReading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) : + readScopedExpr? resolve locals result = some target.erase ∧ after.env.intern.WF := by + induction trace with + | done => exact ⟨sourceReading, coherent⟩ + | next step rest ih => + obtain ⟨reading, preserved⟩ := step.reading coherent + exact ih reading preserved + +/-- The production loop returns the final beta result with the original +source's type and a coherent intern table. The finite path uses the real +loop bound and the same recursive method table at every iteration. -/ +theorem uncached_sound + (trace : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags + steps before source term after result target) {type : AExpr β} + (origin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) + (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) + (sourceReading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) (enough : steps < maxWhnfCoreFuel.toNat) : + (RecM.whnfCoreWithFlagsUncached source flags).run (methodsN (reductionFuel + 1)) before = .ok result after ∧ + readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ TypingClaim.{u,v} entries context target type ∧ + after.env.intern.WF := by + obtain ⟨reading, preserved⟩ := trace.reading sourceReading coherent + obtain ⟨converted, typed⟩ := (trace.toBetaTrace origin).sound formed + exact ⟨trace.run enough, reading, converted, typed, preserved⟩ + +end SynthesisBetaWhnfTrace +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Production.lean b/Ix/Kernel/Verify/Consistency/Production.lean index 7fd6fa7f5..b598a0f83 100644 --- a/Ix/Kernel/Verify/Consistency/Production.lean +++ b/Ix/Kernel/Verify/Consistency/Production.lean @@ -6,7 +6,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Driver import Ix.Kernel.Verify.Consistency.Constant import Ix.Kernel.Verify.Consistency.SynthesisInference -import Ix.Kernel.Verify.Consistency.BetaSpine +import Ix.Kernel.Verify.Consistency.BetaWhnf import Ix.Kernel.Verify.Consistency.Validation /-! @@ -477,6 +477,17 @@ inductive DefinitionCheckSupport {β : Type u} DefinitionCheckSupport resolve entries trace value (.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) ((AExpr.lam headCondition binder inner).appN initialArguments)) + | betaDeclaredTrace {value type reduced : AExpr β} {level typeBound valueBound : VLevel} + (typeInference : SynthesisInference resolve entries [] [] [] fuel trace.validated input.type + type (.sort level) typeBound) + (valueInference : SynthesisInference resolve entries [] [] [] fuel trace.valueStart input.value + value reduced valueBound) + (reduction : SynthesisBetaTrace resolve entries [] [] entries [] type reduced (.sort level)) + (valueReading : readScopedExpr? resolve [] input.value = some value.erase) + (typeReading : readScopedExpr? resolve [] input.type = some type.erase) + (scope : value.Scope input.universes.toNat 0 ∧ type.Scope input.universes.toNat 0) + (references : value.ReferencesIn entries ∧ type.ReferencesIn entries) : + DefinitionCheckSupport resolve entries trace value type theorem DefinitionCheckSupport.sound {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} @@ -516,6 +527,13 @@ theorem DefinitionCheckSupport.sound {β : Type u} exact ⟨readScopedExpr?_closed valueReading, readScopedExpr?_closed typeReading, scope.1, scope.2, references.1, references.2, valueTyped.conv typeTyped conversion.symm⟩ + | betaDeclaredTrace typeInference valueInference reduction valueReading typeReading scope references => + obtain ⟨_, typeTyped, _⟩ := typeInference.closed_sound typeReading trace.typeRun + obtain ⟨conversion, _⟩ := reduction.sound (.empty entries) + obtain ⟨_, valueTyped, _⟩ := valueInference.closed_sound valueReading trace.valueRun + exact ⟨readScopedExpr?_closed valueReading, readScopedExpr?_closed typeReading, + scope.1, scope.2, references.1, references.2, + valueTyped.conv typeTyped conversion.symm⟩ /-- Production validation supplies scope for the beta-converted declaration and its value. Both inference trees refer to the checks in this exact trace. -/ @@ -613,6 +631,60 @@ def DefinitionBodyTrace.betaDeclaredTwiceSupport {β : Type u} {input : Definiti (trace.scopes typeCoverage valueCoverage collision valueReading typeReading valueConditions typeConditions) references +/-- A finite beta trace can connect the declaration's own type and value +checks through any number of successive prefixes. Source validation still +provides both scopes; each reduction retains its original checking calls. -/ +def DefinitionBodyTrace.betaDeclaredTraceSupport {β : Type u} {input : DefinitionInput} + {fuel : Nat} {before : TcState .anon} + (trace : DefinitionBodyTrace input (methodsN fuel) before) + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {value type reduced : AExpr β} {level typeBound valueBound : VLevel} + {support : RunSupport} (typeCoverage : input.type.ValidationCoverage support) + (valueCoverage : input.value.ValidationCoverage support) + (collision : support.CollisionFree) + (typeInference : SynthesisInference resolve entries [] [] [] fuel trace.validated input.type + type (.sort level) typeBound) + (valueInference : SynthesisInference resolve entries [] [] [] fuel trace.valueStart input.value + value reduced valueBound) + (reduction : SynthesisBetaTrace resolve entries [] [] entries [] type reduced (.sort level)) + (valueReading : readScopedExpr? resolve [] input.value = some value.erase) + (typeReading : readScopedExpr? resolve [] input.type = some type.erase) + (valueConditions : ConditionsScoped input.universes.toNat value) + (typeConditions : ConditionsScoped input.universes.toNat type) + (references : value.ReferencesIn entries ∧ type.ReferencesIn entries) : + DefinitionCheckSupport resolve entries trace value type := + .betaDeclaredTrace typeInference valueInference reduction valueReading typeReading + (trace.scopes typeCoverage valueCoverage collision valueReading typeReading + valueConditions typeConditions) references + +/-- A beta WHNF path supplies the declaration's conversion proof. Its +source typing comes from the declaration's actual type-inference call. -/ +def DefinitionBodyTrace.betaDeclaredWhnfSupport {β : Type u} {input : DefinitionInput} + {fuel : Nat} {before : TcState .anon} + (trace : DefinitionBodyTrace input (methodsN fuel) before) + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {value type reduced : AExpr β} {level typeBound valueBound : VLevel} + {support : RunSupport} (typeCoverage : input.type.ValidationCoverage support) + (valueCoverage : input.value.ValidationCoverage support) + (collision : support.CollisionFree) + (typeInference : SynthesisInference resolve entries [] [] [] fuel trace.validated input.type + type (.sort level) typeBound) + (valueInference : SynthesisInference resolve entries [] [] [] fuel trace.valueStart input.value + value reduced valueBound) + {reductionFuel steps : Nat} {flags : WhnfFlags} {after : TcState .anon} {result : KExpr .anon} + (reduction : SynthesisBetaWhnfTrace resolve entries [] [] entries [] [] reductionFuel flags + steps trace.conversionStart input.type type after result reduced) + (valueReading : readScopedExpr? resolve [] input.value = some value.erase) + (typeReading : readScopedExpr? resolve [] input.type = some type.erase) + (valueConditions : ConditionsScoped input.universes.toNat value) + (typeConditions : ConditionsScoped input.universes.toNat type) + (references : value.ReferencesIn entries ∧ type.ReferencesIn entries) : + DefinitionCheckSupport resolve entries trace value type := + trace.betaDeclaredTraceSupport typeCoverage valueCoverage collision typeInference valueInference + (reduction.toBetaTrace (.source (.checked .current typeInference + (LocalContextReading.empty _ _) typeReading trace.typeRun))) + valueReading typeReading valueConditions typeConditions references + /-- Operational support for the selected production definition fragment. Resources are required only at the states exposed by successful body traces. -/ structure AtomicDefinitionRun {β : Type u} (resolve : Address → Option (ConstRef β)) diff --git a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean index 76cd5af6c..032614f79 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean @@ -207,19 +207,40 @@ inductive SynthesisTypeTransport {β : Type u} (resolve : Address → Option (Co (current.inst argument cutoff) (result.inst argument cutoff) bound /-- Internal typing origins for generated expressions. Leaves are actual -source inference calls. Subsequent context and substitution steps retain -those calls, including when an argument must cross still-open parameters -of an earlier declaration's type. No semantic typing field is accepted. -/ +source inference calls. Application, conversion, finite beta traces, and +context substitutions retain those calls, including when an argument crosses +still-open parameters of an earlier type. No semantic typing field is accepted. -/ inductive SynthesisTypingOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : Model.Environment β → Model.Context β → List VLevel → Model.Environment β → Model.Context β → AExpr β → AExpr β → Type u | source {incoming incomingContext incomingBounds entries context term type} (check : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type) : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type + | inferredType {incoming incomingContext incomingBounds entries context bounds locals fuel before after source result + term type level} + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context type (.sort level) | lambdaBody {incoming incomingContext incomingBounds entries context condition domain body codomain} - (check : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context + (check : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context (.lam condition domain body) (.forallE condition domain codomain)) : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries (context.push domain) body codomain + | application {incoming incomingContext incomingBounds entries context function argument condition domain body} + (functionOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context + function (.forallE condition domain body)) + (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context argument domain) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context + (.app function argument) (body.inst argument) + | reduced {incoming incomingContext incomingBounds entries context source result type} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context result type + | convert {incoming incomingContext incomingBounds entries context term sourceType resultType level} + (value : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term sourceType) + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context sourceType resultType (.sort level)) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term resultType | weaken {incoming incomingContext incomingBounds entries context term type} (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) (domain : AExpr β) : @@ -269,6 +290,13 @@ inductive SynthesisCheckedOrigin {β : Type u} (resolve : Address → Option (Co (reading : readScopedExpr? resolve locals source = some term.erase) (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type + | binderHead {incoming incomingContext incomingBounds entries context locals fuel before after source result term type} + (tree : BinderInference resolve entries locals context fuel before source term type) + (head : SynthesisHead term) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type | applicationArgument {incoming incomingContext incomingBounds entries context bounds locals fuel before fn arg f a domain argumentType body condition functionLevel argumentLevel} (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) @@ -319,6 +347,10 @@ variable head. Both cases retain the checks of all applied arguments. -/ inductive SynthesisReductionOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : Model.Environment β → Model.Context β → List VLevel → Model.Environment β → Model.Context β → AExpr β → List (AExpr β) → Nat → VLevel → Type u + | traced {incoming incomingContext incomingBounds entries context head arguments count level} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (head.appN arguments) (AExpr.betaPrefix count head arguments) (.sort level)) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries context head arguments count level | checked {incoming incomingContext incomingBounds entries context head arguments count level earlier typeLocals typeContext typeBounds typeFuel typeBefore typeAfter typeSource typeResult originCondition originDomain originBody originArguments originLevel originBound} @@ -390,6 +422,57 @@ inductive SynthesisReductionOrigin {β : Type u} (resolve : Address → Option ( SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries context current currentArguments count level +/-- Finite beta traces retain the actual checks behind each lambda and +argument. Results can supply later typing origins, so composition never +requires an inference call on an intermediate expression. Types at adjacent +steps may differ; the trace preserves the type retained by its first step. -/ +inductive SynthesisBetaTrace {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → + Model.Environment β → Model.Context β → AExpr β → AExpr β → AExpr β → Type u + | refl {incoming incomingContext incomingBounds entries context term type} + (origin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term term type + | prefix {incoming incomingContext incomingBounds entries context head headType arguments count type} + (headOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context head headType) + (leading : LambdaPrefix head headType count) + (argumentsOrigin : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries context headType arguments type) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (head.appN arguments) (AExpr.betaPrefix count head arguments) type + | origin {incoming incomingContext incomingBounds entries context head arguments count level} + (retained : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds + entries context head arguments count level) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (head.appN arguments) (AExpr.betaPrefix count head arguments) (.sort level) + | trans {incoming incomingContext incomingBounds entries context source middle result type otherType} + (prior : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source middle type) + (next : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context middle result otherType) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type + | atType {incoming incomingContext incomingBounds entries context source result type otherType} + (sourceOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context source type) + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result otherType) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type + | application {incoming incomingContext incomingBounds entries context source result argument condition domain body} + (functionTrace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + source result (.forallE condition domain body)) + (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context argument domain) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (.app source argument) (.app result argument) (body.inst argument) + | argument {incoming incomingContext incomingBounds entries context function source result condition domain body otherType} + (functionOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context + function (.forallE condition domain body)) + (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context source domain) + (argumentTrace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result otherType) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (.app function source) (.app function result) (body.inst source) + | substituteAt {incoming incomingContext incomingBounds entries base sourceContext targetContext + domain source result type argument cutoff} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries sourceContext source result type) + (value : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries base argument domain) + (substitution : ContextSubstitution base domain argument sourceContext targetContext cutoff) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries targetContext + (source.inst argument cutoff) (result.inst argument cutoff) (type.inst argument cutoff) + end /-- Earlier argument checks can cross another local binder without any @@ -1230,7 +1313,14 @@ theorem SynthesisTypingOrigin.sound {β : Type u} {resolve : Address → Option TypingClaim.{u,v} entries context term type := match support with | .source check => (check.soundWithSpine formed).1 - | .lambdaBody check => (check.soundWithSpine formed).1.lambdaBody + | .inferredType contextOrigin tree agreement reading accepted => + (tree.soundWithSpine (contextOrigin.sound formed) agreement reading accepted).2.2.1 + | .lambdaBody check => (check.sound formed).lambdaBody + | .application functionOrigin argumentOrigin => (functionOrigin.sound formed).app (argumentOrigin.sound formed) + | .reduced trace => (trace.sound formed).2 + | .convert value trace => by + obtain ⟨converted, typeTyped⟩ := trace.sound formed + exact (value.sound formed).conv typeTyped converted | .weaken prior domain => typing_weaken (prior.sound formed) | .instantiate prior arguments => typing_instL_context (prior.sound formed) arguments | .appendContext prior outer => typing_append_context (prior.sound formed) outer @@ -1251,6 +1341,9 @@ theorem SynthesisCheckedOrigin.soundWithSpine {β : Type u} {resolve : Address | .checked contextOrigin tree agreement reading accepted => by obtain ⟨_, typed, _, spine⟩ := tree.soundWithSpine (contextOrigin.sound formed) agreement reading accepted exact ⟨typed, tree.lambdaPrefix, spine⟩ + | .binderHead tree head agreement reading accepted => by + have typed := (tree.synthesis head agreement reading accepted).2 + exact ⟨typed, tree.lambdaPrefix, tree.lambdaSpineTyping typed⟩ | .applicationArgument contextOrigin trace functionTree argumentTree agreement functionReading argumentReading conditions hashPath comparisonFaithful => by have contextFormation := contextOrigin.sound formed @@ -1305,6 +1398,7 @@ theorem SynthesisReductionOrigin.sound {β : Type u} {resolve : Address → Opti ConversionClaim.{u,v} entries context (head.appN arguments) (AExpr.betaPrefix count head arguments) ∧ TypingClaim.{u,v} entries context (AExpr.betaPrefix count head arguments) (.sort level) := match support with + | .traced trace => trace.sound formed | .checked typeContextSupport typeTree typeAgreement typeReading typeRun originPrefix transport => by obtain ⟨_, _, _, originSpine⟩ := typeTree.soundWithSpine (typeContextSupport.sound formed) typeAgreement typeReading typeRun @@ -1326,6 +1420,40 @@ theorem SynthesisReductionOrigin.sound {β : Type u} {resolve : Address → Opti exact transport.sound formed converted typed termination_by structural support +theorem SynthesisBetaTrace.sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {source result type : AExpr β} + (support : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type) + (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : + ConversionClaim.{u,v} entries context source result ∧ TypingClaim.{u,v} entries context result type := + match support with + | .refl origin => ⟨.refl _, origin.sound formed⟩ + | .prefix headOrigin leading argumentsOrigin => + leading.beta_sound (headOrigin.sound formed) (argumentsOrigin.sound formed) + | .origin retained => retained.sound formed + | .trans prior next => by + obtain ⟨first, middleTyped⟩ := prior.sound formed + obtain ⟨second, resultTyped⟩ := next.sound formed + exact ⟨first.trans second, middleTyped.termConv resultTyped second⟩ + | .atType sourceOrigin trace => by + obtain ⟨converted, resultTyped⟩ := trace.sound formed + exact ⟨converted, (sourceOrigin.sound formed).termConv resultTyped converted⟩ + | .application functionTrace argumentOrigin => by + obtain ⟨converted, functionTyped⟩ := functionTrace.sound formed + exact ⟨converted.app (.refl _), functionTyped.app (argumentOrigin.sound formed)⟩ + | .argument functionOrigin argumentOrigin argumentTrace => by + obtain ⟨converted, resultTyped⟩ := argumentTrace.sound formed + have functionTyped := functionOrigin.sound formed + have argumentTyped := argumentOrigin.sound formed + refine ⟨(ConversionClaim.refl _).app converted, ?_⟩ + exact (functionTyped.app argumentTyped).termConv + (functionTyped.app (argumentTyped.termConv resultTyped converted)) ((ConversionClaim.refl _).app converted) + | .substituteAt trace value substitution => by + obtain ⟨converted, resultTyped⟩ := trace.sound formed + have valueTyped := value.sound formed + exact ⟨converted.instAt valueTyped substitution, resultTyped.instAt valueTyped substitution⟩ +termination_by structural support + end /-- Ordinary argument typing and its syntactic leading domains remain diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 458213ef7..592c0d641 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -1074,6 +1074,95 @@ private def repeatedBetaCases : TestSeq := let (otherEnv, second) := repeatedBetaDeclaredType .zero 0 5 3 rowFailed env first && rowFailed otherEnv second) +/-- Every wrapper returns its function argument. The application suffix +survives each newly exposed prefix; the final prefix may consume a dependent +carrier/witness pair. The declared type always normalizes to the carrier. -/ +private def betaTraceDeclaredType (level : Ixon.Univ) (universes : UInt64 := 0) + (wrappers : Nat := 2) (dependent : Bool := false) (wrong : Nat := 0) : Ixon.Env × Address := Id.run do + let levels := if universes == 0 then #[] else #[0] + let (env, carrier) := storeConst {} + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], #[level]⟩ + let (env, otherCarrier) := storeConst env + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], #[level, .succ level]⟩ + let (env, witness) := storeConst env + ⟨.axio ⟨false, universes, .ref 0 levels⟩, #[], #[carrier], #[level]⟩ + let family := Ixon.Expr.leanAll (.sort 0) + (if dependent then .leanAll (.var 0) (.sort 0) else .sort 0) + let mut function := Ixon.Expr.leanLam (.sort 0) + (if dependent then .leanLam (.var 0) (.var 1) else .var 0) + for _ in List.range wrappers do + function := .app (.leanLam family (.var 0)) function + let mut type := Ixon.Expr.app function (.ref (if wrong == 1 then 1 else 0) levels) + if dependent then type := .app type (.ref (if wrong == 2 then 0 else 2) levels) + return storeConst env + ⟨.defn ⟨.defn, .safe, universes, type, .ref 2 levels⟩, + #[], #[carrier, otherCarrier, witness], #[level]⟩ + +/-- Compare individual production steps, the explicitly bounded driver, +and the uncached WHNF entry point from the same loaded state. An exact +reduction-count budget must exhaust before the final `.done` iteration. -/ +private def betaTraceLoopResult (level : Ixon.Univ) (wrappers : Nat) + (dependent : Bool) (flags : WhnfFlags) : Bool := + let (env, target) := betaTraceDeclaredType level 0 wrappers dependent + let action : RecM .anon Bool := do + let .defn _ _ _ _ _ _ type value _ _ ← TcM.getConst (m := .anon) ⟨target, ()⟩ | return false + let expected ← RecM.inferCall value + let .sort .. ← RecM.inferCall type | return false + let before ← get + let mut current := type + for index in List.range (wrappers + 1) do + let (head, arguments) := current.collectSpine + let consumed := (RecM.consumeBetaLams head arguments).2.size + if consumed != (if index == wrappers && dependent then 2 else 1) then return false + let .next next ← RecM.whnfCoreWithFlagsStep current flags | return false + if next == current then return false + current := next + let .done terminal ← RecM.whnfCoreWithFlagsStep current flags | return false + let stepped ← get + if terminal != expected then return false + set before + let exhausted ← try + let _ ← RecM.runBounded (fun term => RecM.whnfCoreWithFlagsStep term flags) (wrappers + 1) type + pure false + catch error => pure (match error with | .maxRecDepth => true | _ => false) + let exhaustedState ← get + set before + let bounded ← RecM.runBounded (fun term => RecM.whnfCoreWithFlagsStep term flags) (wrappers + 2) type + set before + let normalized ← RecM.whnfCoreWithFlagsUncached type flags + let after ← get + return exhausted && bounded == expected && normalized == expected && + after.env.intern.exprs.size == stepped.env.intern.exprs.size && + exhaustedState.env.intern.exprs.size == stepped.env.intern.exprs.size && + after.lctx.size == before.lctx.size && after.env.nextFVarId == before.env.nextFVarId + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +private def betaTraceCases : TestSeq := + test "beta trace: three prefixes preserve a returned function and its suffix in Prop and Type" + (allSucceeded (betaTraceDeclaredType .zero).1 4 && + allSucceeded (betaTraceDeclaredType (.succ .zero)).1 4) + ++ test "beta trace: twelve prefixes preserve dependent final arguments in Prop and Type" + (allSucceeded (betaTraceDeclaredType .zero 0 11 true).1 4 && + allSucceeded (betaTraceDeclaredType (.succ .zero) 0 11 true).1 4) + ++ test "beta trace: successive returned functions retain declaration universe parameters" + (allSucceeded (betaTraceDeclaredType (.var 0) 1 4).1 4 && + allSucceeded (betaTraceDeclaredType (.var 0) 1 4 true).1 4) + ++ test "beta trace: longer declarations check with fresh per-item caches" + (allSucceeded (betaTraceDeclaredType .zero 0 4).1 4 { clearEvery := 1 } && + allSucceeded (betaTraceDeclaredType (.succ .zero) 0 4 true).1 4 { clearEvery := 1 }) + ++ test "beta trace: both WHNF policies agree with individual steps and the exact loop bound" + ([0, 2, 5].all fun wrappers => [WhnfFlags.FULL, .DEF_EQ_CORE].all fun flags => + betaTraceLoopResult .zero wrappers false flags && betaTraceLoopResult (.succ .zero) wrappers false flags) + ++ test "beta trace: the final dependent prefix consumes two arguments before the done iteration" + ([0, 2, 5].all fun wrappers => [WhnfFlags.FULL, .DEF_EQ_CORE].all fun flags => + betaTraceLoopResult .zero wrappers true flags && betaTraceLoopResult (.succ .zero) wrappers true flags) + ++ test "beta trace: a different carrier after four prefixes cannot type the original witness" + (let (env, target) := betaTraceDeclaredType .zero 0 3 false 1; rowFailed env target) + ++ test "beta trace: a retained dependent argument must still inhabit its selected carrier" + (let (env, target) := betaTraceDeclaredType .zero 0 3 true 2; rowFailed env target) + private def applicationCases : TestSeq := test "application environment: Prop/Type identity calls and transitive theorem calls check" (allSucceeded applicationEnvironment 5 { clearEvery := 0 }) @@ -2863,7 +2952,7 @@ private def polymorphicDefinitionCases : TestSeq := public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, multiBetaCases, cheapLambdaCases, - cheapApplicationCases, exposedLambdaCases, repeatedBetaCases, + cheapApplicationCases, exposedLambdaCases, repeatedBetaCases, betaTraceCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, sourceAgreementCases, sourceCacheCases, polymorphicDefinitionCases] diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 94cc7c7cc..b21046735 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -512,7 +512,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 570 exact theorem boundaries. The production +The consistency target checks 587 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -583,8 +583,21 @@ existing declaration and environment admission boundary. Neither connector requires inference of the intermediate term. The model's lambda-body and term-conversion lemmas use only standard Lean axioms, and these production roots retain the same two existing native output-length proofs. -Automatic origins for arbitrary generated types, further repeated reduction, -and other conversion paths remain open. +`SynthesisBetaTrace` now composes any number of retained prefixes. A result +supplies a later function or argument origin, and composition preserves the +original type even when adjacent steps retain different types. Traces also +transport through application suffixes and dependent substitutions. Actual +source inference extracts the head and every argument check of its spine; +no intermediate inference call or semantic typing field is added. +`SynthesisBetaWhnfTrace.uncached_sound` connects finite paths to the real +structural-WHNF loop. Every beta step computes its raw result and next intern +table using production substitution and suffix interning. Reading and intern +coherence follow from the initial table and finite walker resources. The fuel +bound includes the final unchanged `.done` iteration. The same trace supplies +`DefinitionBodyTrace.betaDeclaredWhnfSupport` for declaration admission. +All seventeen new boundaries retain the same two existing native output-length +proofs. Automatic origins for arbitrary generated types, automatic finite +trace construction, and the remaining WHNF/conversion paths remain open. Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, argument order, and rejection of wrong arities and out-of-range parameters. @@ -642,6 +655,13 @@ and cache clearing. One case changes the first lambda body's inferred type from a beta redex to a sort, then consumes the supplied lambda's two-argument prefix. Negative cases reject a wrong function domain, a different selected carrier, and invalid dependent arguments from either origin. +Finite-trace cases cover three and twelve successive prefixes, retained +dependent final arguments, declaration universe parameters, and cleared +caches. Both WHNF policies compare individual production steps with the +bounded driver and uncached entry point. A budget equal to the number of +reductions exhausts before `.done`; one additional iteration returns the exact +inferred value type. Negative cases reject a different carrier and an invalid +dependent argument after several returned-function prefixes. These execution tests do not construct the general finite inference resources. Polymorphic-call regressions include Prop/Type instances in real function bodies, `max`/`imax` simplification inside Pi domains, closed nested references @@ -712,7 +732,7 @@ Definition-cycle regressions use content-addressed standalone and mutual declarations, including a self-justifying theorem, a two-member cycle, type cycles, lets, shared syntax, and binders. They check repeated member failures, acyclic forward references, cache clearing, and the partial/unsafe policy. -The unit suite contains 668 checks. The anonymous differential additionally +The unit suite contains 676 checks. The anonymous differential additionally serializes eight cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. diff --git a/docs/theory.md b/docs/theory.md index 63a0115ec..bb1dd65ab 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -86,9 +86,15 @@ inferred type. The first reduction retains typing at that current type; the supplied lambda contributes the second prefix's domains and any initial arguments. This supports the actual WHNF step on the intermediate term and declaration admission through both reductions, without another inference -call on the intermediate term. Further exposed prefixes need further origins. -Automatic origin construction for arbitrary generated types, general -reduction, and conversion remain open. +call on the intermediate term. Finite beta traces now compose any number of +retained prefixes, including generated functions and arguments, application +suffixes, and dependent substitutions. The result retains the original type +even when adjacent steps carry different types. A structural-WHNF trace +computes each raw substitution result and intern table and proves the actual +uncached loop under its bound, including the final unchanged iteration. +Declaration admission uses the same traces. Automatic origin and trace +construction for arbitrary generated types, the remaining reduction branches, +and general conversion remain open. Safe definition admission also rejects circular justification in both Lean and Rust, including `theorem loop : P := loop` with only `P : Prop` assumed. The production dependency walk returns an order with a proved decreasing rank; From d44456c6894e7c51159c747c67559da33e97b71b Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 07:09:30 -0400 Subject: [PATCH 32/63] Derive beta-step origins from source inference --- Ix/Kernel/Verify/Consistency.lean | 21 +- Ix/Kernel/Verify/Consistency/Audit.lean | 33 ++- .../Verify/Consistency/BetaInference.lean | 142 ++++++++++ Ix/Kernel/Verify/Consistency/BetaTrace.lean | 89 ++++++ Ix/Kernel/Verify/Consistency/BetaTyping.lean | 266 ++++++++++++++++++ Ix/Kernel/Verify/Consistency/BetaWhnf.lean | 87 ++++-- .../Verify/Consistency/BetaWhnfInference.lean | 92 ++++++ .../Verify/Consistency/ContextInsertion.lean | 82 ++++++ Ix/Kernel/Verify/Consistency/Production.lean | 56 +++- .../Consistency/SynthesisInference.lean | 21 +- Tests/Ix/Kernel/Consistency.lean | 137 ++++++++- docs/kernel-verification.md | 35 ++- docs/theory.md | 14 +- 13 files changed, 1037 insertions(+), 38 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/BetaInference.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaTyping.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean create mode 100644 Ix/Kernel/Verify/Consistency/ContextInsertion.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 64c243ea2..37e37bab5 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -26,6 +26,7 @@ import Ix.Kernel.Verify.Consistency.BinderOpening import Ix.Kernel.Verify.Consistency.Application import Ix.Kernel.Verify.Consistency.BinderInference import Ix.Kernel.Verify.Consistency.Formation +import Ix.Kernel.Verify.Consistency.ContextInsertion import Ix.Kernel.Verify.Consistency.SynthesisInference import Ix.Kernel.Verify.Consistency.BetaSubstitution import Ix.Kernel.Verify.Consistency.Beta @@ -35,6 +36,9 @@ import Ix.Kernel.Verify.Consistency.CheapBetaReading import Ix.Kernel.Verify.Consistency.BetaSpine import Ix.Kernel.Verify.Consistency.BetaTrace import Ix.Kernel.Verify.Consistency.BetaWhnf +import Ix.Kernel.Verify.Consistency.BetaTyping +import Ix.Kernel.Verify.Consistency.BetaInference +import Ix.Kernel.Verify.Consistency.BetaWhnfInference import Ix.Kernel.Verify.Consistency.CheapBeta import Ix.Kernel.Verify.Consistency.Validation import Ix.Kernel.Verify.Consistency.RecursiveCache @@ -114,15 +118,24 @@ type. Its first beta result supplies typing at the current type; substituting the supplied lambda supplies the next checked prefix. Two successive prefixes therefore compose without inference of the intermediate term. Retained origins justify the actual multi-argument WHNF step, and declaration -admission includes this two-prefix conversion. Automatic origin construction -for arbitrary generated types remains open. Finite beta traces now compose +admission includes this two-prefix conversion. Finite beta traces compose any number of retained prefixes. An earlier result can supply a later lambda or argument origin, and type transport preserves the original source type. Application suffixes and dependent substitutions retain their checks. The corresponding structural-WHNF trace computes each raw result and intern table, then proves the actual uncached loop under its fuel bound. The same -traces justify definition conversion. Automatic construction of these finite -resources and the remaining WHNF/conversion paths remain open. +traces justify definition conversion. Source inference now constructs a full +beta typing derivation retaining every lambda body and application child. +Dependent substitution rebuilds that derivation beneath retained binders, +so each generated result supplies the exact lambda domains for the next step. +Forward beta conversion preserves these domains when cheap beta changes a +lambda body's inferred type. Every finite head-beta path of the currently +supported inference fragment therefore derives all its semantic step origins +from the original check. The operational path contains only raw execution, +reading, and finite representation resources, and its automatic annotation +also supplies declaration admission. Constructing the initial inference and +operational resources for all accepted programs, and covering the remaining +WHNF/conversion paths, remain open. Local cache hits agree with the actual declaration type. Constant hits agree with pure universe substitution of a loaded, admitted declaration; sort hits return the canonical successor sort. Application, forall, and lambda diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 676e06928..a7d37de45 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -458,6 +458,37 @@ private def betaTraceRoots : Array RootAllowance := #[ { root := ``DefinitionBodyTrace.betaDeclaredWhnfSupport, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } ] +private def hereditaryBetaRoots : Array RootAllowance := #[ + { root := ``ContextInsertion.lookup, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``ContextInsertion.source_valid, standardAxioms := standard }, + { root := ``ContextInsertion.typing, standardAxioms := standard }, + { root := ``ContextInsertion.conversion, standardAxioms := standard }, + { root := ``SynthesisBetaTrace.rigid, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaSyntax.steps_betaPrefix, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``SynthesisBetaTyping.origin, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTyping.lambdaView, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTyping.weakenAt, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTyping.substituteAt, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTyping.lambdaPrefix, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTyping.betaStep, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTyping.betaSteps, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTyping.betaPrefix, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaSubstitutionContext.relation }, + { root := ``BetaSubstitutionContext.liftValue, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BinderInference.betaTyping, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.betaTyping, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.beta_steps_sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaStepPlan.run, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaStepPlan.sourceReading, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``BetaStepPlan.reading, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaStepPlan.counts, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``BetaStepPlan.betaTyping, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaWhnfTrace.annotate, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.beta_whnf_sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``DefinitionBodyTrace.betaDeclaredStepsSupport, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``DefinitionBodyTrace.betaDeclaredWhnfPathSupport, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -586,7 +617,7 @@ def roots : Array RootAllowance := #[ root, standardAxioms := standard, nativeAxioms := productionNative, forbiddenDependencies := forbiddenProduction }) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots ++ exposedOriginRoots ++ - repeatedBetaRoots ++ betaTraceRoots).map (fun allowance => { + repeatedBetaRoots ++ betaTraceRoots ++ hereditaryBetaRoots).map (fun allowance => { allowance with forbiddenDependencies := forbiddenProduction }) ++ #[{ root := ``extend_atomic_definition, standardAxioms := standard }] diff --git a/Ix/Kernel/Verify/Consistency/BetaInference.lean b/Ix/Kernel/Verify/Consistency/BetaInference.lean new file mode 100644 index 000000000..5fb34307f --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaInference.lean @@ -0,0 +1,142 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaTyping + +/-! Recover the complete head-beta typing derivation from the source +inference tree. No checks of intermediate reduction results are supplied. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +def BinderInference.betaTyping {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source term type) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type → + LocalContextReading resolve locals before.lctx context → + readScopedExpr? resolve locals source = some term.erase → + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type := + match support with + | .sort .. | .cachedSort .. | .const .. | .polymorphic .. | .cachedConst .. | .forallE .. => + fun origin _ _ => .atom origin (by constructor) + | .fvar _ _ atIndex => fun origin _ _ => .bvar origin atIndex + | .app _ miss trace functionTree head argumentTree conditions hashPath comparisonFaithful _ _ _ _ _ _ => + fun _ agreement reading => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have argumentAgreement := trace.contextPreserved.symm ▸ keyedAgreement + have functionTypeReads := + (BinderInference.synthesis.{u,u} functionTree head keyedAgreement functionReading trace.functionRun).1 + have argumentTypeReads := + (BinderInference.sound.{u,u} argumentTree argumentAgreement argumentReading trace.argumentRun).1 + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (argumentTypeReads.symm.trans + ((beq_readScopedExpr? comparisonFaithful hashPath).trans + (readScopedExpr?_all_parts functionTypeReads).1))) conditions + have functionOrigin := SynthesisTypingOrigin.source + (SynthesisCheckedOrigin.binderHead (incoming := incoming) (incomingContext := incomingContext) + (incomingBounds := incomingBounds) functionTree head keyedAgreement functionReading trace.functionRun) + have argumentOrigin := SynthesisTypingOrigin.source + (SynthesisCheckedOrigin.binderArgument (incoming := incoming) (incomingContext := incomingContext) + (incomingBounds := incomingBounds) trace functionTree head argumentTree keyedAgreement + functionReading argumentReading conditions hashPath comparisonFaithful) + exact .app (functionTree.betaTyping functionOrigin keyedAgreement functionReading) + (sameType ▸ argumentTree.betaTyping (sameType.symm ▸ argumentOrigin) argumentAgreement argumentReading) + | .lam _ miss trace opening absent bodyTree _ _ _ _ _ => fun origin agreement reading => by + obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_lam_parts reading + have domainAgreement := trace.contextPreserved.symm ▸ (miss.localContext.symm ▸ agreement) + obtain ⟨_, openedReading, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement absent domainReading bodyReading trace.openRun + exact .lam origin (bodyTree.betaTyping (.lambdaBody origin) openedAgreement openedReading) +termination_by structural support + +/-- Every currently supported source-inference branch supplies its own +beta derivation. Lambda inference's changed body type becomes a forward +conversion node, retaining the original body's complete checked structure. -/ +def SynthesisInference.betaTyping {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) : + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) → + LocalContextReading resolve locals before.lctx context → + readScopedExpr? resolve locals source = some term.erase → + RecM.infer source (methodsN fuel) before = .ok result after → + ContextFormation.{u,v} incoming incomingContext incomingBounds → + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type := + match support with + | node@(.known inference _) => fun contextOrigin agreement reading accepted _ => + inference.betaTyping (.source (.checked contextOrigin node agreement reading accepted)) agreement reading + | node@(.reuseType inference ..) => fun contextOrigin agreement reading accepted _ => + inference.betaTyping (.source (.checked contextOrigin node agreement reading accepted)) agreement reading + | node@(.fvar _ atIndex _) => fun contextOrigin agreement reading accepted _ => + .bvar (.source (.checked contextOrigin node agreement reading accepted)) atIndex + | node@(.forallE ..) => fun contextOrigin agreement reading accepted _ => + .atom (.source (.checked contextOrigin node agreement reading accepted)) (by constructor) + | .app _ miss trace functionTree argumentTree conditions hashPath comparisonFaithful _ _ _ _ _ _ => + fun contextOrigin agreement reading _ formed => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have argumentAgreement := trace.contextPreserved.symm ▸ keyedAgreement + have contextFormation := contextOrigin.sound formed + have functionTypeReads := + (functionTree.soundWithSpine contextFormation keyedAgreement functionReading trace.functionRun).1 + have argumentTypeReads := + (argumentTree.soundWithSpine contextFormation argumentAgreement argumentReading trace.argumentRun).1 + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (argumentTypeReads.symm.trans + ((beq_readScopedExpr? comparisonFaithful hashPath).trans + (readScopedExpr?_all_parts functionTypeReads).1))) conditions + exact .app (functionTree.betaTyping contextOrigin keyedAgreement functionReading trace.functionRun formed) + (sameType ▸ argumentTree.betaTyping contextOrigin argumentAgreement argumentReading trace.argumentRun formed) + | .lam full miss trace opening absent domainTree bodyTree conditionAgrees constructed bound coherent + closingFaithful faithful => + fun contextOrigin agreement reading accepted formed => by + let node := SynthesisInference.lam full miss trace opening absent domainTree bodyTree conditionAgrees + constructed bound coherent closingFaithful faithful + obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + obtain ⟨_, openedReading, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement absent domainReading bodyReading trace.openRun + exact .lam (.source (.checked contextOrigin node agreement reading accepted)) + (bodyTree.betaTyping (contextOrigin.push domainTree keyedAgreement domainReading trace.domainRun) + openedAgreement openedReading trace.bodyRun formed) + | .lamBeta full miss trace opening absent domainTree bodyTree reductionOrigin reduction conditionAgrees + constructed bound closingFaithful faithful => + fun contextOrigin agreement reading accepted formed => by + let node := SynthesisInference.lamBeta full miss trace opening absent domainTree bodyTree reductionOrigin reduction + conditionAgrees constructed bound closingFaithful faithful + obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + obtain ⟨_, openedReading, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement absent domainReading bodyReading trace.openRun + have inner := bodyTree.betaTyping + (contextOrigin.push domainTree keyedAgreement domainReading trace.domainRun) + openedAgreement openedReading trace.bodyRun formed + exact .lam (.source (.checked contextOrigin node agreement reading accepted)) + (.convert inner (.rebase contextOrigin (.origin reductionOrigin))) +termination_by structural support + +theorem SynthesisInference.beta_steps_sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} {locals : List FVarId} + {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) (count : Nat) : + ConversionClaim.{u,v} entries context term (BetaSyntax.steps count term) ∧ + TypingClaim.{u,v} entries context (BetaSyntax.steps count term) type := + ((support.betaTyping .current agreement reading accepted formed).betaSteps count).2.sound formed + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaTrace.lean b/Ix/Kernel/Verify/Consistency/BetaTrace.lean index 16e7133ca..f492e9c9a 100644 --- a/Ix/Kernel/Verify/Consistency/BetaTrace.lean +++ b/Ix/Kernel/Verify/Consistency/BetaTrace.lean @@ -192,4 +192,93 @@ def SynthesisInference.betaSpineTrace {β : Type u} {resolve : Address → Optio (head.appN arguments) (AExpr.betaPrefix count head arguments) type := (support.spineOrigin contextOrigin agreement reading accepted head arguments rfl).betaTrace enough +private theorem betaPrefix_eq_of_not_app {β : Type u} {head : AExpr β} + {arguments : List (AExpr β)} {count : Nat} + (notApp : ∀ fn arg, head.appN arguments ≠ .app fn arg) : + AExpr.betaPrefix count head arguments = head.appN arguments := by + cases arguments with + | nil => cases count <;> cases head <;> rfl + | cons argument arguments => + exact False.elim (notApp _ _ (appN_last (by simp))) + +/-- These traces reduce applications and their subapplications. They do +not change a source whose outer constructor is a lambda, product, or atom. +In particular, a forward beta conversion cannot change a lambda's product type. -/ +theorem SynthesisBetaTrace.rigid {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {source result type : AExpr β} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type) : + (∀ fn arg, source ≠ .app fn arg) → result = source := + match trace with + | .refl _ => fun _ => rfl + | .prefix .. | .origin .. => fun notApp => betaPrefix_eq_of_not_app notApp + | .trans prior next => fun notApp => by + have middle := prior.rigid notApp + exact (next.rigid (by simpa only [middle] using notApp)).trans middle + | .atType _ trace => trace.rigid + | .application .. | .argument .. => fun notApp => False.elim (notApp _ _ rfl) + | .substituteAt trace _ _ => fun notApp => by + have same := trace.rigid (by + intro fn arg equal + cases equal + exact notApp _ _ rfl) + rw [same] + | .weakenAt trace _ => fun notApp => by + have same := trace.rigid (by + intro fn arg equal + cases equal + exact notApp _ _ rfl) + rw [same] + | .rebase _ trace => trace.rigid +termination_by structural trace + +namespace BetaSyntax + +/-- One leftmost beta contraction, retaining the application suffix. +A term without a beta redex at its head is unchanged. -/ +def step : AExpr β → AExpr β + | .app (.lam _ _ body) argument => body.inst argument + | .app fn arg => .app (step fn) arg + | term => term + +def steps : Nat → AExpr β → AExpr β + | 0, term => term + | count + 1, term => steps count (step term) + +private theorem step_appN {β : Type u} (head : AExpr β) (arguments : List (AExpr β)) + (notLam : ∀ condition domain body, head ≠ .lam condition domain body) : + step (head.appN arguments) = (step head).appN arguments := by + induction arguments generalizing head with + | nil => rfl + | cons argument arguments ih => + rw [AExpr.appN_cons, ih (head.app argument) (by intro condition domain body same; cases same)] + cases head <;> simp_all [step, AExpr.appN_cons] + +theorem step_beta_appN {β : Type u} (condition : Certified.PropWhen) + (domain body argument : AExpr β) (arguments : List (AExpr β)) : + step ((AExpr.lam condition domain body).appN (argument :: arguments)) = + (body.inst argument).appN arguments := by + rw [AExpr.appN_cons, step_appN _ _ (by intro condition domain body same; cases same)] + rfl + +theorem steps_betaPrefix {β : Type u} (count : Nat) (head : AExpr β) (arguments : List (AExpr β)) + (leading : count ≤ head.lambdaDepth) (supplied : count ≤ arguments.length) : + steps count (head.appN arguments) = AExpr.betaPrefix count head arguments := by + induction count generalizing head arguments with + | zero => rfl + | succ count ih => + cases head with + | lam condition domain body => + cases arguments with + | nil => simp at supplied + | cons argument arguments => + simp only [steps, step_beta_appN, AExpr.betaPrefix] + apply ih + · exact Nat.le_trans (by simpa only [AExpr.lambdaDepth, Nat.add_le_add_iff_right] using leading) + (AExpr.lambdaDepth_le_inst body argument 0) + · simpa only [List.length_cons, Nat.add_le_add_iff_right] using supplied + | _ => simp [AExpr.lambdaDepth] at leading + +end BetaSyntax + end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaTyping.lean b/Ix/Kernel/Verify/Consistency/BetaTyping.lean new file mode 100644 index 000000000..61662d9b3 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaTyping.lean @@ -0,0 +1,266 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaTrace + +/-! Retain the checked lambda bodies and application arguments needed for +hereditary beta substitution. Generated results carry the same derivation, +so the next reduction does not require another inference call or origin. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- Constructors that remain outside head beta reduction after substitution. -/ +inductive BetaAtom {β : Type u} : AExpr β → Prop + | sort (level : VLevel) : BetaAtom (.sort level) + | const (ref : ConstRef β) (levels : List VLevel) : BetaAtom (.const ref levels) + | forallE (condition : Certified.PropWhen) (domain body : AExpr β) : BetaAtom (.forallE condition domain body) + | proj (ref : ConstRef β) (field : Nat) (major : AExpr β) : BetaAtom (.proj ref field major) + | natLit (value : Nat) : BetaAtom (.natLit value) + +theorem BetaAtom.liftN {β : Type u} {term : AExpr β} (atom : BetaAtom term) (count cutoff : Nat) : + BetaAtom (term.liftN count cutoff) := by + cases atom <;> constructor + +theorem BetaAtom.inst {β : Type u} {term : AExpr β} (atom : BetaAtom term) (argument : AExpr β) (cutoff : Nat) : + BetaAtom (term.inst argument cutoff) := by + cases atom <;> constructor + +theorem BetaAtom.not_lam {β : Type u} {term : AExpr β} (atom : BetaAtom term) + (condition : Certified.PropWhen) (domain body : AExpr β) : term ≠ .lam condition domain body := by + cases atom <;> intro same <;> cases same + +theorem BetaAtom.step {β : Type u} {term : AExpr β} (atom : BetaAtom term) : BetaSyntax.step term = term := by + cases atom <;> rfl + +/-- A typing derivation whose leaves retain actual checks. Lambda bodies +and both application children remain available after substitution. Forward +beta conversion changes the retained type without discarding this structure. -/ +inductive SynthesisBetaTyping {β : Type u} (resolve : Address → Option (ConstRef β)) + (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) + (entries : Model.Environment β) : Model.Context β → AExpr β → AExpr β → Type u + | atom {context term type} + (origin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) + (shape : BetaAtom term) : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type + | bvar {context index type} + (origin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context (.bvar index) type) + (atIndex : context[index]? = some type) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (.bvar index) type + | lam {context condition domain body codomain} + (origin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context + (.lam condition domain body) (.forallE condition domain codomain)) + (inner : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries (context.push domain) body codomain) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context + (.lam condition domain body) (.forallE condition domain codomain) + | app {context fn arg condition domain body} + (function : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context fn + (.forallE condition domain body)) + (argument : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context arg domain) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (.app fn arg) (body.inst arg) + | convert {context term sourceType resultType level} + (prior : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term sourceType) + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + sourceType resultType (.sort level)) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term resultType + +namespace SynthesisBetaTyping + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext : Model.Context β} {incomingBounds : List VLevel} + +def origin {context : Model.Context β} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type := + match typing with + | .atom origin _ | .bvar origin _ | .lam origin _ => origin + | .app function argument => .application function.origin argument.origin + | .convert prior trace => .convert prior.origin trace +termination_by structural typing + +structure LambdaView (context : Model.Context β) (condition : Certified.PropWhen) (domain body type : AExpr β) where + codomain : AExpr β + typeEq : type = .forallE condition domain codomain + inner : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries (context.push domain) body codomain + +def lambdaView {context : Model.Context β} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + {condition : Certified.PropWhen} → {domain body : AExpr β} → term = .lam condition domain body → + LambdaView (resolve := resolve) (incoming := incoming) (incomingContext := incomingContext) + (incomingBounds := incomingBounds) (entries := entries) context condition domain body type := + match typing with + | .atom _ shape => fun same => False.elim (shape.not_lam _ _ _ same) + | .bvar .. | .app .. => fun same => by cases same + | .lam _ inner => fun same => by cases same; exact ⟨_, rfl, inner⟩ + | .convert prior trace => fun same => + let view := prior.lambdaView same + ⟨view.codomain, (trace.rigid (by simp only [view.typeEq]; intro fn arg same; cases same)).trans view.typeEq, + view.inner⟩ +termination_by structural typing + +def weakenAt {source target : Model.Context β} {cutoff : Nat} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries source term type) + (insertion : ContextInsertion source target cutoff) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries target + (term.liftN 1 cutoff) (type.liftN 1 cutoff) := + match typing with + | .atom origin shape => .atom (.weakenAt origin insertion) (shape.liftN 1 cutoff) + | .bvar (index := index) origin found => by + have lifted := SynthesisTypingOrigin.weakenAt origin insertion + have selected := insertion.lookup found + by_cases below : index < cutoff + · simp only [AExpr.liftN, liftVar, below, if_true] at lifted selected ⊢ + exact .bvar lifted selected + · simp only [AExpr.liftN, liftVar, below, if_false, Nat.add_comm 1] at lifted selected ⊢ + exact .bvar lifted selected + | .lam origin inner => .lam (.weakenAt origin insertion) (inner.weakenAt (insertion.push _)) + | .app function argument => by + simpa only [AExpr.liftN, AExpr.liftN_inst_zero] using + SynthesisBetaTyping.app (function.weakenAt insertion) (argument.weakenAt insertion) + | .convert prior trace => .convert (prior.weakenAt insertion) (.weakenAt trace insertion) +termination_by structural typing + +end SynthesisBetaTyping + +/-- The internal substitution walker builds this data itself as it passes +binders. Erasure gives the existing model context-substitution relation. -/ +inductive BetaSubstitutionContext {β : Type u} (base : Model.Context β) (domain argument : AExpr β) : + Model.Context β → Model.Context β → Nat → Type u + | root : BetaSubstitutionContext base domain argument (base.push domain) base 0 + | push {source target cutoff} (prior : BetaSubstitutionContext base domain argument source target cutoff) + (binder : AExpr β) : + BetaSubstitutionContext base domain argument (source.push binder) + (target.push (binder.inst argument cutoff)) (cutoff + 1) + +theorem BetaSubstitutionContext.relation {β : Type u} {base source target : Model.Context β} + {domain argument : AExpr β} {cutoff : Nat} + (substitution : BetaSubstitutionContext base domain argument source target cutoff) : + ContextSubstitution base domain argument source target cutoff := + match substitution with + | .root => .root + | .push prior binder => .push prior.relation binder + +def BetaSubstitutionContext.liftValue {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext : Model.Context β} {incomingBounds : List VLevel} + {base source target : Model.Context β} {domain argument term type : AExpr β} {cutoff : Nat} + (substitution : BetaSubstitutionContext base domain argument source target cutoff) + (value : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries base term type) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries target + (term.liftN cutoff) (type.liftN cutoff) := + match substitution with + | .root => by simpa only [AExpr.liftN_zero] using value + | .push (target := target) (cutoff := cutoff) prior binder => by + have lifted := (prior.liftValue value).weakenAt (ContextInsertion.root target (binder.inst argument cutoff)) + simpa only [AExpr.liftN_liftN_merge term cutoff 1 0 0 (Nat.le_refl _) (Nat.zero_le _), + AExpr.liftN_liftN_merge type cutoff 1 0 0 (Nat.le_refl _) (Nat.zero_le _)] using lifted +termination_by structural substitution + +def SynthesisBetaTyping.substituteAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext : Model.Context β} {incomingBounds : List VLevel} + {base source target : Model.Context β} {domain argument term type : AExpr β} {cutoff : Nat} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries source term type) + (value : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries base argument domain) + (substitution : BetaSubstitutionContext base domain argument source target cutoff) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries target + (term.inst argument cutoff) (type.inst argument cutoff) := + match typing with + | .atom origin shape => .atom (.substituteAt origin value.origin substitution.relation) (shape.inst argument cutoff) + | .bvar (index := index) origin found => by + by_cases equal : index = cutoff + · subst index + have sameType := substitution.relation.instantiate_removed_type found + simpa only [AExpr.inst, AExpr.instVar, Nat.lt_irrefl, if_false, if_true, sameType] using + substitution.liftValue value + · have retained := substitution.relation.lookup_other found equal + have typed := SynthesisTypingOrigin.substituteAt origin value.origin substitution.relation + by_cases below : index < cutoff + · simp only [AExpr.inst, AExpr.instVar, below, if_true] at typed retained ⊢ + exact .bvar typed retained + · simp only [AExpr.inst, AExpr.instVar, below, equal, if_false] at typed retained ⊢ + exact .bvar typed retained + | .lam origin inner => + .lam (.substituteAt origin value.origin substitution.relation) + (inner.substituteAt value (substitution.push _)) + | .app function applied => by + simpa only [AExpr.inst, AExpr.inst_inst_zero] using + SynthesisBetaTyping.app (function.substituteAt value substitution) (applied.substituteAt value substitution) + | .convert prior trace => + .convert (prior.substituteAt value substitution) (.substituteAt trace value.origin substitution.relation) +termination_by structural typing + +theorem SynthesisBetaTyping.lambdaPrefix {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + LambdaPrefix term type term.lambdaDepth := + match typing with + | .atom _ shape => by cases shape <;> exact .zero _ _ + | .bvar .. | .app .. => .zero _ _ + | .lam _ inner => .lam inner.lambdaPrefix + | .convert (term := term) prior trace => by + have leading := prior.lambdaPrefix + cases term with + | lam condition domain body => + have view := prior.lambdaView rfl + have fixed := trace.rigid (by simp only [view.typeEq]; intro fn arg same; cases same) + simpa only [fixed] using leading + | _ => exact .zero _ _ +termination_by structural typing + +/-- Every head beta contraction computes its next typing derivation and +conversion from the retained source tree, including newly exposed lambdas. -/ +def SynthesisBetaTyping.betaStep {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (BetaSyntax.step term) type × + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term (BetaSyntax.step term) type := + match typing with + | .atom origin shape => by rw [shape.step]; exact ⟨.atom origin shape, .refl origin⟩ + | .bvar origin found => ⟨.bvar origin found, .refl origin⟩ + | .lam origin inner => ⟨.lam origin inner, .refl origin⟩ + | .app (fn := fn) function argument => by + have nextFunction := function.betaStep + cases fn with + | lam condition' domain' inner => + have view := function.lambdaView rfl + obtain ⟨rfl, rfl, codomainEq⟩ := AExpr.forallE.inj view.typeEq + refine ⟨?_, SynthesisBetaTrace.beta function.origin argument.origin⟩ + simpa only [codomainEq, BetaSyntax.step] using view.inner.substituteAt argument .root + | bvar | sort | const | app | forallE | proj | natLit => + obtain ⟨next, converted⟩ := nextFunction + exact ⟨.app next argument, .application converted argument.origin⟩ + | .convert prior trace => + let next := prior.betaStep + ⟨.convert next.1 trace, .atType (.convert prior.origin trace) next.2⟩ +termination_by structural typing + +def SynthesisBetaTyping.betaSteps {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} (count : Nat) {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (BetaSyntax.steps count term) type × + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term (BetaSyntax.steps count term) type := + match count with + | 0 => ⟨typing, .refl typing.origin⟩ + | count + 1 => + let first := typing.betaStep + let rest := first.1.betaSteps count + ⟨rest.1, first.2.trans rest.2⟩ + +def SynthesisBetaTyping.betaPrefix {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {head type : AExpr β} {arguments : List (AExpr β)} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (head.appN arguments) type) + (count : Nat) (leading : count ≤ head.lambdaDepth) (supplied : count ≤ arguments.length) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context + (AExpr.betaPrefix count head arguments) type × + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (head.appN arguments) (AExpr.betaPrefix count head arguments) type := by + simpa only [BetaSyntax.steps_betaPrefix count head arguments leading supplied] using typing.betaSteps count + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnf.lean b/Ix/Kernel/Verify/Consistency/BetaWhnf.lean index 93bdb257f..6a27b7f3b 100644 --- a/Ix/Kernel/Verify/Consistency/BetaWhnf.lean +++ b/Ix/Kernel/Verify/Consistency/BetaWhnf.lean @@ -16,11 +16,9 @@ open Theory Theory.Model universe u v /-- Resources for one actual beta iteration. The raw output and next -state are computed below; no result reading or semantic equality is a field. -/ -structure SynthesisBetaStep {β : Type u} (resolve : Address → Option (ConstRef β)) - (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) - (entries : Model.Environment β) (context : Model.Context β) (locals : List FVarId) - (before : TcState .anon) (source : KExpr .anon) (term type : AExpr β) where +state are computed below; no typing, reduction origin, or result reading is a field. -/ +structure BetaStepPlan {β : Type u} (resolve : Address → Option (ConstRef β)) (locals : List FVarId) + (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) where rawFunction : KExpr .anon rawArgument : KExpr .anon appInfo : ExprInfo .anon @@ -38,8 +36,6 @@ structure SynthesisBetaStep {β : Type u} (resolve : Address → Option (ConstRe inner : AExpr β arguments : List (AExpr β) modelSource : term = (AExpr.lam condition domain inner).appN arguments - meaning : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term - (AExpr.betaPrefix consumed.size (.lam condition domain inner) arguments) type spine : (KExpr.app rawFunction rawArgument appInfo).collectSpine = (.lam name bi rawDomain rawInner lambdaInfo, rawArguments) headReads : readScopedExpr? resolve locals (.lam name bi rawDomain rawInner lambdaInfo) = @@ -55,28 +51,26 @@ structure SynthesisBetaStep {β : Type u} (resolve : Address → Option (ConstRe term ∈ cheapBetaChainList (simulSubst rawBody consumed.reverse 0 before.env.intern).1 (rawArguments.extract consumed.size rawArguments.size).toList -namespace SynthesisBetaStep +namespace BetaStepPlan variable {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {locals : List FVarId} {before : TcState .anon} - {source : KExpr .anon} {term type : AExpr β} + {locals : List FVarId} {before : TcState .anon} {source : KExpr .anon} {term : AExpr β} -def output (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) : +def output (step : BetaStepPlan resolve locals before source term) : KExpr .anon × InternTable .anon := let walk := simulSubst step.rawBody step.consumed.reverse 0 before.env.intern internAppChain walk.1 (step.rawArguments.extract step.consumed.size step.rawArguments.size).toList walk.2 -def result (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) : +def result (step : BetaStepPlan resolve locals before source term) : KExpr .anon := step.output.1 -def after (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) : +def after (step : BetaStepPlan resolve locals before source term) : TcState .anon := { before with env := { before.env with intern := step.output.2 } } -def modelResult (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) : +def modelResult (step : BetaStepPlan resolve locals before source term) : AExpr β := AExpr.betaPrefix step.consumed.size (.lam step.condition step.domain step.inner) step.arguments -theorem run (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) +theorem run (step : BetaStepPlan resolve locals before source term) (reductionFuel : Nat) (flags : WhnfFlags) : (RecM.whnfCoreWithFlagsStep source flags).run (methodsN (reductionFuel + 1)) before = .ok (.next step.result) step.after := by @@ -86,13 +80,13 @@ theorem run (step : SynthesisBetaStep resolve incoming incomingContext incomingB rfl theorem sourceReading - (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) : + (step : BetaStepPlan resolve locals before source term) : readScopedExpr? resolve locals source = some term.erase := by simp only [step.sourceEq, step.modelSource] exact readScopedExpr?_collectSpine step.spine step.headReads step.argumentReads theorem reading - (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) + (step : BetaStepPlan resolve locals before source term) (coherent : before.env.intern.WF) : readScopedExpr? resolve locals step.result = some step.modelResult.erase ∧ step.after.env.intern.WF := by obtain ⟨result, after, run, reading, _, preserved⟩ := beta_many_step_readScopedExpr? @@ -104,6 +98,63 @@ theorem reading cases run exact ⟨reading, preserved⟩ +theorem counts (plan : BetaStepPlan resolve locals before source term) : + plan.consumed.size ≤ plan.inner.lambdaDepth + 1 ∧ plan.consumed.size ≤ plan.arguments.length := by + obtain ⟨peeled, _, rawBound⟩ := RecM.BetaPeel.of_consume plan.peeling + obtain ⟨_, modelPeel, _⟩ := betaPeel_readScopedExpr? peeled plan.headReads + have sizeAgrees : plan.rawArguments.size = plan.arguments.length := by + have lengths := congrArg List.length plan.argumentReads + simpa using lengths + exact ⟨by simpa only [Array.length_toList, AExpr.lambdaDepth] using modelPeel.length_bound, + sizeAgrees ▸ rawBound⟩ + +end BetaStepPlan + +/-- A raw beta plan together with its derived source-checking evidence. -/ +structure SynthesisBetaStep {β : Type u} (resolve : Address → Option (ConstRef β)) + (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) + (entries : Model.Environment β) (context : Model.Context β) (locals : List FVarId) + (before : TcState .anon) (source : KExpr .anon) (term type : AExpr β) + extends BetaStepPlan resolve locals before source term where + meaning : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term + (AExpr.betaPrefix consumed.size (.lam condition domain inner) arguments) type + +namespace SynthesisBetaStep + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {before : TcState .anon} + {source : KExpr .anon} {term type : AExpr β} + +def output (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) := + step.toBetaStepPlan.output + +def result (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) := + step.toBetaStepPlan.result + +def after (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) := + step.toBetaStepPlan.after + +def modelResult (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) := + step.toBetaStepPlan.modelResult + +theorem run (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) + (reductionFuel : Nat) (flags : WhnfFlags) : + (RecM.whnfCoreWithFlagsStep source flags).run (methodsN (reductionFuel + 1)) before = + .ok (.next step.result) step.after := + step.toBetaStepPlan.run reductionFuel flags + +theorem sourceReading + (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) : + readScopedExpr? resolve locals source = some term.erase := + step.toBetaStepPlan.sourceReading + +theorem reading + (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) + (coherent : before.env.intern.WF) : + readScopedExpr? resolve locals step.result = some step.modelResult.erase ∧ step.after.env.intern.WF := + step.toBetaStepPlan.reading coherent + end SynthesisBetaStep /-- A complete finite beta path through structural WHNF. The final diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean b/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean new file mode 100644 index 000000000..a1d5af338 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean @@ -0,0 +1,92 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaInference +import Ix.Kernel.Verify.Consistency.BetaWhnf + +/-! Derive every semantic beta-step origin from the original inference. +The supplied WHNF path contains only actual operational and representation +resources, including the finite substitution and interning collision domains. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +def BetaStepPlan.betaTyping {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {before : TcState .anon} + {source : KExpr .anon} {term type : AExpr β} + (plan : BetaStepPlan resolve locals before source term) + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context plan.modelResult type × + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term plan.modelResult type := by + have same : BetaSyntax.steps plan.consumed.size term = plan.modelResult := by + calc + BetaSyntax.steps plan.consumed.size term = + BetaSyntax.steps plan.consumed.size ((AExpr.lam plan.condition plan.domain plan.inner).appN plan.arguments) := + congrArg (BetaSyntax.steps plan.consumed.size) plan.modelSource + _ = plan.modelResult := BetaSyntax.steps_betaPrefix _ _ _ plan.counts.1 plan.counts.2 + exact same ▸ typing.betaSteps plan.consumed.size + +/-- A finite production path without semantic step origins. All intermediate +expressions and states are computed by its raw beta plans. -/ +inductive BetaWhnfTrace {β : Type u} (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (reductionFuel : Nat) (flags : WhnfFlags) : + Nat → TcState .anon → KExpr .anon → AExpr β → TcState .anon → KExpr .anon → AExpr β → Type u + | done {before source term} + (finished : (RecM.whnfCoreWithFlagsStep source flags).run (methodsN (reductionFuel + 1)) before = + .ok (.done source) before) : + BetaWhnfTrace resolve locals reductionFuel flags 0 before source term before source term + | next {steps before after source result term target} + (plan : BetaStepPlan resolve locals before source term) + (rest : BetaWhnfTrace resolve locals reductionFuel flags + steps plan.after plan.result plan.modelResult after result target) : + BetaWhnfTrace resolve locals reductionFuel flags (steps + 1) before source term after result target + +def BetaWhnfTrace.annotate {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {reductionFuel steps : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target type : AExpr β} + (trace : BetaWhnfTrace resolve locals reductionFuel flags steps before source term after result target) + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags + steps before source term after result target × + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context target type := + match trace with + | .done finished => ⟨.done finished, typing⟩ + | .next plan rest => + let first := plan.betaTyping typing + let step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals + _ _ _ type := { toBetaStepPlan := plan, meaning := first.2 } + let remaining := rest.annotate first.1 + ⟨.next step remaining.1, remaining.2⟩ +termination_by structural trace + +/-- The original inference supplies every later lambda and argument origin. +Only the operational WHNF path and finite representation resources remain as +inputs to the actual uncached-loop refinement. -/ +theorem SynthesisInference.beta_whnf_sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} {locals : List FVarId} + {fuel : Nat} {inferenceBefore inferenceAfter : TcState .anon} {source inferred : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel inferenceBefore source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals inferenceBefore.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) + {reductionFuel steps : Nat} {flags : WhnfFlags} {before after : TcState .anon} + {result : KExpr .anon} {target : AExpr β} + (path : BetaWhnfTrace resolve locals reductionFuel flags steps before source term after result target) + (coherent : before.env.intern.WF) (enough : steps < maxWhnfCoreFuel.toNat) : + (RecM.whnfCoreWithFlagsUncached source flags).run (methodsN (reductionFuel + 1)) before = .ok result after ∧ + readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ TypingClaim.{u,v} entries context target type ∧ + after.env.intern.WF := by + let typing := support.betaTyping .current agreement reading accepted formed + exact (path.annotate typing).1.uncached_sound typing.origin formed reading coherent enough + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/ContextInsertion.lean b/Ix/Kernel/Verify/Consistency/ContextInsertion.lean new file mode 100644 index 000000000..d9e14ba40 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/ContextInsertion.lean @@ -0,0 +1,82 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Formation + +/-! Insert a local beneath retained dependent binders. The same structural +relation lifts generated typing origins and their beta-conversion evidence. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +inductive ContextInsertion {β : Type u} : Model.Context β → Model.Context β → Nat → Prop + | root (context : Model.Context β) (domain : AExpr β) : + ContextInsertion context (context.push domain) 0 + | push {source target cutoff} (prior : ContextInsertion source target cutoff) (domain : AExpr β) : + ContextInsertion (source.push domain) (target.push (domain.liftN 1 cutoff)) (cutoff + 1) + +theorem ContextInsertion.lookup {β : Type u} {source target : Model.Context β} {cutoff index : Nat} + {type : AExpr β} (insertion : ContextInsertion source target cutoff) + (found : source[index]? = some type) : + target[if index < cutoff then index else index + 1]? = some (type.liftN 1 cutoff) := by + induction insertion generalizing index type with + | root context domain => + simp only [Nat.not_lt_zero, ↓reduceIte, Model.Context.push, + List.getElem?_cons_succ, List.getElem?_map, found, Option.map_some] + | @push source target cutoff prior domain ih => + cases index with + | zero => + simp only [Model.Context.push, List.getElem?_cons_zero, Option.some.injEq] at found + subst type + simp only [Nat.zero_lt_succ, ↓reduceIte, Model.Context.push, List.getElem?_cons_zero] + exact congrArg some (by + simpa only [Nat.add_comm 1 cutoff] using + AExpr.liftN_liftN_comm domain 1 cutoff 1 0 (Nat.zero_le _)) + | succ index => + simp only [Model.Context.push, List.getElem?_cons_succ, List.getElem?_map] at found + obtain ⟨type, atIndex, rfl⟩ := Option.map_eq_some_iff.mp found + have shifted : (if index + 1 < cutoff + 1 then index + 1 else index + 1 + 1) = + (if index < cutoff then index else index + 1) + 1 := by + split <;> split <;> omega + rw [shifted] + simp only [Model.Context.push, List.getElem?_cons_succ, List.getElem?_map, + ih atIndex, Option.map_some] + exact congrArg some (by + simpa only [Nat.add_comm 1 cutoff] using + AExpr.liftN_liftN_comm type 1 cutoff 1 0 (Nat.zero_le _)) + +theorem ContextInsertion.source_valid {β : Type u} {source target : Model.Context β} {cutoff : Nat} + (insertion : ContextInsertion source target cutoff) + {V : Type v} [SetTheory V] {constants : Assignment β V} {levels : List Nat} {env : Nat → V} + (valid : target.Valid constants levels env) : + source.Valid constants levels (Valuation.skip 1 cutoff env) := by + intro index type found + have selected := valid (if index < cutoff then index else index + 1) (type.liftN 1 cutoff) + (insertion.lookup found) + simpa only [wellDenoted_liftN, interp_liftN, Valuation.skip, Nat.add_comm 1 index, + apply_ite] using selected + +theorem ContextInsertion.typing {β : Type u} {entries : Model.Environment β} + {source target : Model.Context β} {cutoff : Nat} {term type : AExpr β} + (insertion : ContextInsertion source target cutoff) + (typed : TypingClaim.{u,v} entries source term type) : + TypingClaim.{u,v} entries target (term.liftN 1 cutoff) (type.liftN 1 cutoff) := by + intro V _ constants realizes levels env valid + simpa only [wellDenoted_liftN, interp_liftN] using + typed V constants realizes levels _ (insertion.source_valid valid) + +theorem ContextInsertion.conversion {β : Type u} {entries : Model.Environment β} + {source target : Model.Context β} {cutoff : Nat} {left right : AExpr β} + (insertion : ContextInsertion source target cutoff) + (converted : ConversionClaim.{u,v} entries source left right) : + ConversionClaim.{u,v} entries target (left.liftN 1 cutoff) (right.liftN 1 cutoff) := by + intro V _ constants realizes levels env valid + simpa only [interp_liftN] using + converted V constants realizes levels _ (insertion.source_valid valid) + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Production.lean b/Ix/Kernel/Verify/Consistency/Production.lean index b598a0f83..ea152420b 100644 --- a/Ix/Kernel/Verify/Consistency/Production.lean +++ b/Ix/Kernel/Verify/Consistency/Production.lean @@ -6,7 +6,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Driver import Ix.Kernel.Verify.Consistency.Constant import Ix.Kernel.Verify.Consistency.SynthesisInference -import Ix.Kernel.Verify.Consistency.BetaWhnf +import Ix.Kernel.Verify.Consistency.BetaWhnfInference import Ix.Kernel.Verify.Consistency.Validation /-! @@ -685,6 +685,60 @@ def DefinitionBodyTrace.betaDeclaredWhnfSupport {β : Type u} {input : Definitio (LocalContextReading.empty _ _) typeReading trace.typeRun))) valueReading typeReading valueConditions typeConditions references +/-- The declaration's actual type check constructs every successive beta +origin, including functions exposed by substituting earlier arguments. -/ +def DefinitionBodyTrace.betaDeclaredStepsSupport {β : Type u} {input : DefinitionInput} + {fuel : Nat} {before : TcState .anon} + (trace : DefinitionBodyTrace input (methodsN fuel) before) + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {value type : AExpr β} {level typeBound valueBound : VLevel} + {support : RunSupport} (typeCoverage : input.type.ValidationCoverage support) + (valueCoverage : input.value.ValidationCoverage support) + (collision : support.CollisionFree) + (typeInference : SynthesisInference resolve entries [] [] [] fuel trace.validated input.type + type (.sort level) typeBound) + (count : Nat) + (valueInference : SynthesisInference resolve entries [] [] [] fuel trace.valueStart input.value + value (BetaSyntax.steps count type) valueBound) + (valueReading : readScopedExpr? resolve [] input.value = some value.erase) + (typeReading : readScopedExpr? resolve [] input.type = some type.erase) + (valueConditions : ConditionsScoped input.universes.toNat value) + (typeConditions : ConditionsScoped input.universes.toNat type) + (references : value.ReferencesIn entries ∧ type.ReferencesIn entries) : + DefinitionCheckSupport resolve entries trace value type := + trace.betaDeclaredTraceSupport typeCoverage valueCoverage collision typeInference valueInference + ((SynthesisInference.betaTyping.{u,u} typeInference .current (.empty _ _) typeReading + trace.typeRun (.empty entries)).betaSteps count).2 + valueReading typeReading valueConditions typeConditions references + +/-- An operational WHNF path needs no semantic origins for its intermediate +terms: the declaration's original inference derives them all. -/ +def DefinitionBodyTrace.betaDeclaredWhnfPathSupport {β : Type u} {input : DefinitionInput} + {fuel : Nat} {before : TcState .anon} + (trace : DefinitionBodyTrace input (methodsN fuel) before) + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {value type reduced : AExpr β} {level typeBound valueBound : VLevel} + {support : RunSupport} (typeCoverage : input.type.ValidationCoverage support) + (valueCoverage : input.value.ValidationCoverage support) + (collision : support.CollisionFree) + (typeInference : SynthesisInference resolve entries [] [] [] fuel trace.validated input.type + type (.sort level) typeBound) + (valueInference : SynthesisInference resolve entries [] [] [] fuel trace.valueStart input.value + value reduced valueBound) + {reductionFuel steps : Nat} {flags : WhnfFlags} {after : TcState .anon} {result : KExpr .anon} + (reduction : BetaWhnfTrace resolve [] reductionFuel flags + steps trace.conversionStart input.type type after result reduced) + (valueReading : readScopedExpr? resolve [] input.value = some value.erase) + (typeReading : readScopedExpr? resolve [] input.type = some type.erase) + (valueConditions : ConditionsScoped input.universes.toNat value) + (typeConditions : ConditionsScoped input.universes.toNat type) + (references : value.ReferencesIn entries ∧ type.ReferencesIn entries) : + DefinitionCheckSupport resolve entries trace value type := + trace.betaDeclaredWhnfSupport typeCoverage valueCoverage collision typeInference valueInference + (reduction.annotate (SynthesisInference.betaTyping.{u,u} typeInference .current (.empty _ _) + typeReading trace.typeRun (.empty entries))).1 + valueReading typeReading valueConditions typeConditions references + /-- Operational support for the selected production definition fragment. Resources are required only at the states exposed by successful body traces. -/ structure AtomicDefinitionRun {β : Type u} (resolve : Address → Option (ConstRef β)) diff --git a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean index 032614f79..6c4fc15e8 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean @@ -3,7 +3,7 @@ Copyright (c) 2026 Argument Computer Corporation. SPDX-License-Identifier: MIT OR Apache-2.0 -/ -import Ix.Kernel.Verify.Consistency.Formation +import Ix.Kernel.Verify.Consistency.ContextInsertion import Ix.Kernel.Verify.Consistency.CheapBetaReading import Ix.Theory.Model.UniverseBounds import Ix.Theory.Model.BetaSpine @@ -246,6 +246,11 @@ inductive SynthesisTypingOrigin {β : Type u} (resolve : Address → Option (Con (domain : AExpr β) : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries (context.push domain) (term.liftN 1) (type.liftN 1) + | weakenAt {incoming incomingContext incomingBounds entries source target cutoff term type} + (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries source term type) + (insertion : ContextInsertion source target cutoff) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries target + (term.liftN 1 cutoff) (type.liftN 1 cutoff) | instantiate {incoming incomingContext incomingBounds entries context term type} (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) (arguments : List VLevel) : @@ -472,6 +477,15 @@ inductive SynthesisBetaTrace {β : Type u} (resolve : Address → Option (ConstR (substitution : ContextSubstitution base domain argument sourceContext targetContext cutoff) : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries targetContext (source.inst argument cutoff) (result.inst argument cutoff) (type.inst argument cutoff) + | weakenAt {incoming incomingContext incomingBounds entries sourceContext targetContext cutoff source result type} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries sourceContext source result type) + (insertion : ContextInsertion sourceContext targetContext cutoff) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries targetContext + (source.liftN 1 cutoff) (result.liftN 1 cutoff) (type.liftN 1 cutoff) + | rebase {incoming incomingContext incomingBounds middle middleContext middleBounds entries context source result type} + (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) + (trace : SynthesisBetaTrace resolve middle middleContext middleBounds entries context source result type) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type end @@ -1322,6 +1336,7 @@ theorem SynthesisTypingOrigin.sound {β : Type u} {resolve : Address → Option obtain ⟨converted, typeTyped⟩ := trace.sound formed exact (value.sound formed).conv typeTyped converted | .weaken prior domain => typing_weaken (prior.sound formed) + | .weakenAt prior insertion => insertion.typing (prior.sound formed) | .instantiate prior arguments => typing_instL_context (prior.sound formed) arguments | .appendContext prior outer => typing_append_context (prior.sound formed) outer | .extend prior extension => extension.typing (prior.sound formed) @@ -1452,6 +1467,10 @@ theorem SynthesisBetaTrace.sound {β : Type u} {resolve : Address → Option (Co obtain ⟨converted, resultTyped⟩ := trace.sound formed have valueTyped := value.sound formed exact ⟨converted.instAt valueTyped substitution, resultTyped.instAt valueTyped substitution⟩ + | .weakenAt trace insertion => by + obtain ⟨converted, resultTyped⟩ := trace.sound formed + exact ⟨insertion.conversion converted, insertion.typing resultTyped⟩ + | .rebase origin trace => trace.sound (origin.sound formed) termination_by structural support end diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 592c0d641..a2852e429 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -957,7 +957,7 @@ the supplied lambda's separate prefix; the next step consumes that prefix. Shape 4 also changes the outer body's inferred type from a beta sort to the sort itself. Shape 6 retains an application suffix after the second step. -/ private def repeatedBetaDeclaredType (level : Ixon.Univ) (universes : UInt64 := 0) - (shape wrong : Nat := 0) : Ixon.Env × Address := Id.run do + (shape wrong : Nat := 0) (wrappers : Nat := 0) : Ixon.Env × Address := Id.run do let levels := if universes == 0 then #[] else #[0] let betaSort := Ixon.Expr.app (.leanLam (.sort 1) (.var 0)) (.sort 0) let carrierSort := if shape == 4 then betaSort else .sort 0 @@ -975,7 +975,7 @@ private def repeatedBetaDeclaredType (level : Ixon.Univ) (universes : UInt64 := let domain := if shape == 4 then Ixon.Expr.leanAll betaSort betaSort else if shape == 5 then .leanAll (.sort 0) (.leanAll (.var 0) (.sort 0)) else familyType - let supplied := if wrong == 1 then Ixon.Expr.leanLam (.sort 1) (.var 0) + let mut supplied := if wrong == 1 then Ixon.Expr.leanLam (.sort 1) (.var 0) else if shape == 1 then .leanLam (.sort 0) (.ref 0 levels) else if shape == 2 then .app (.leanLam (.sort 0) (.leanLam (.sort 0) (.var 1))) (.ref 0 levels) else if shape == 3 then @@ -985,6 +985,8 @@ private def repeatedBetaDeclaredType (level : Ixon.Univ) (universes : UInt64 := else if shape == 5 then .leanLam (.sort 0) (.leanLam (.var 0) (.var 1)) else if shape == 6 then .app (.leanLam familyType (.var 0)) (.ref 3 levels) else .leanLam (.sort 0) (.var 0) + for _ in List.range wrappers do + supplied := .app (.leanLam domain (.var 0)) supplied let body := if shape == 5 then Ixon.Expr.app (.app (.var 0) (.ref 0 levels)) (.ref (if wrong == 3 then 0 else 2) levels) else .app (.var 0) (.ref (if shape == 1 || shape == 2 || shape == 3 || wrong == 2 then 1 else 0) levels) @@ -1021,8 +1023,8 @@ private def repeatedBetaDeclaredResult (shape : Nat) (level : Ixon.Univ) : Bool | .ok passed _ => passed | .error _ _ => false -private def repeatedBetaChangedBodyType (level : Ixon.Univ) : Bool := - let (env, target) := repeatedBetaDeclaredType level 0 4 +private def repeatedBetaChangedBodyType (level : Ixon.Univ) (wrappers : Nat := 0) : Bool := + let (env, target) := repeatedBetaDeclaredType level 0 4 0 wrappers let action : RecM .anon Bool := RecM.withLctxScope do let concrete ← TcM.getConst (m := .anon) ⟨target, ()⟩ let .defn _ _ _ _ _ _ (.app outer _ _) _ _ _ := concrete | return false @@ -1163,6 +1165,131 @@ private def betaTraceCases : TestSeq := ++ test "beta trace: a retained dependent argument must still inhabit its selected carrier" (let (env, target) := betaTraceDeclaredType .zero 0 3 true 2; rowFailed env target) +/-- Substitute a supplied function under two retained dependent binders, +then expose an arbitrary chain of returned functions. Shape 0 reduces the +declared type to a carrier, shape 1 reduces the value to its witness, and +shape 2 returns a lambda whose body still contains the supplied function. -/ +private def hereditaryBetaDeclaration (level : Ixon.Univ) (universes : UInt64 := 0) + (wrappers shape wrong : Nat := 0) : Ixon.Env × Address := Id.run do + let levels := if universes == 0 then #[] else #[0] + let (env, carrier) := storeConst {} + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], #[level]⟩ + let (env, otherCarrier) := storeConst env + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], #[level, .succ level]⟩ + let (env, witness) := storeConst env + ⟨.axio ⟨false, universes, .ref 0 levels⟩, #[], #[carrier], #[level]⟩ + let family := Ixon.Expr.leanAll (.sort 0) + (.leanAll (.var 0) (if shape == 0 then .sort 0 else .var 1)) + let mut supplied := Ixon.Expr.leanLam (.sort 0) + (.leanLam (.var 0) (if shape == 0 then .var 1 else .var 0)) + for _ in List.range wrappers do + supplied := .app (.leanLam family (.var 0)) supplied + let outer := Ixon.Expr.leanLam family + (.leanLam (.sort 0) (.leanLam (.var 0) (.app (.app (.var 2) (.var 1)) (.var 0)))) + let mut source := Ixon.Expr.app outer supplied + if shape != 2 then + source := .app (.app source (.ref (if wrong == 1 then 1 else 0) levels)) + (.ref (if wrong == 2 then 0 else 2) levels) + let type := if shape == 0 then source else if shape == 1 then .ref 0 levels else family + let value := if shape == 0 then .ref 2 levels else source + return storeConst env + ⟨.defn ⟨.defn, .safe, universes, type, value⟩, + #[], #[carrier, otherCarrier, witness], #[level]⟩ + +private def observeBetaPath (source expected : KExpr .anon) (counts : List Nat) + (flags : WhnfFlags) : RecM .anon Bool := do + let originalType ← RecM.inferCall source + let before ← get + let mut current := source + for count in counts do + let (head, arguments) := current.collectSpine + if (RecM.consumeBetaLams head arguments).2.size != count then return false + let .next next ← RecM.whnfCoreWithFlagsStep current flags | return false + if next == current then return false + current := next + let .done terminal ← RecM.whnfCoreWithFlagsStep current flags | return false + let stepped ← get + set before + let normalized ← RecM.whnfCoreWithFlagsUncached source flags + let after ← get + let terminalType ← RecM.inferCall terminal + let sameType ← RecM.isDefEqCall originalType terminalType + return terminal == expected && normalized == expected && sameType && + after.env.intern.exprs.size == stepped.env.intern.exprs.size && + after.lctx.size == before.lctx.size && after.env.nextFVarId == before.env.nextFVarId + +private def hereditaryBetaResult (level : Ixon.Univ) (wrappers shape : Nat) + (flags : WhnfFlags) : Bool := + let (env, target) := hereditaryBetaDeclaration level 0 wrappers shape + let action : RecM .anon Bool := do + let .defn _ _ _ _ _ _ type value _ _ ← TcM.getConst (m := .anon) ⟨target, ()⟩ | return false + let source := if shape == 0 then type else value + let (_, arguments) := source.collectSpine + if shape == 2 then + let .app _ supplied _ := source | return false + let originalType ← RecM.inferCall source + let before ← get + let .next result ← RecM.whnfCoreWithFlagsStep source flags | return false + let .lam _ _ _ (.lam _ _ _ (.app (.app captured _ _) _ _) _) _ := result | return false + let .done terminal ← RecM.whnfCoreWithFlagsStep result flags | return false + set before + let normalized ← RecM.whnfCoreWithFlagsUncached source flags + let resultType ← RecM.inferCall result + return captured == supplied && terminal == result && normalized == result && + originalType == type && resultType == type + else + let some witness := arguments[2]? | return false + let expected ← if shape == 0 then RecM.inferCall value else pure witness + observeBetaPath source expected (3 :: List.replicate wrappers 1 ++ [2]) flags + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +private def changedBodyBetaPath (level : Ixon.Univ) (wrappers : Nat) : Bool := + let (env, target) := repeatedBetaDeclaredType level 0 4 0 wrappers + let action : RecM .anon Bool := do + let .defn _ _ _ _ _ _ type value _ _ ← TcM.getConst (m := .anon) ⟨target, ()⟩ | return false + let expected ← RecM.inferCall value + observeBetaPath type expected (1 :: List.replicate wrappers 1 ++ [2]) .DEF_EQ_CORE + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +private def hereditaryBetaCases : TestSeq := + test "hereditary beta: supplied functions cross two dependent binders in Prop and Type" + ([0, 1, 2].all fun shape => [0, 4, 11].all fun wrappers => + allSucceeded (hereditaryBetaDeclaration .zero 0 wrappers shape).1 4 && + allSucceeded (hereditaryBetaDeclaration (.succ .zero) 0 wrappers shape).1 4) + ++ test "hereditary beta: retained dependent binders preserve declaration universe parameters" + ([0, 1, 2].all fun shape => allSucceeded (hereditaryBetaDeclaration (.var 0) 1 4 shape).1 4) + ++ test "hereditary beta: all three substitution shapes check with fresh per-item caches" + ([0, 1, 2].all fun shape => + allSucceeded (hereditaryBetaDeclaration .zero 0 4 shape).1 4 { clearEvery := 1 }) + ++ test "hereditary beta: both WHNF policies consume three binders and each later returned function" + ([0, 4, 11].all fun wrappers => [WhnfFlags.FULL, .DEF_EQ_CORE].all fun flags => + hereditaryBetaResult .zero wrappers 0 flags && hereditaryBetaResult (.succ .zero) wrappers 0 flags) + ++ test "hereditary beta: a term reduction preserves the original carrier type and returns its witness" + ([0, 4].all fun wrappers => [WhnfFlags.FULL, .DEF_EQ_CORE].all fun flags => + hereditaryBetaResult .zero wrappers 1 flags && hereditaryBetaResult (.succ .zero) wrappers 1 flags) + ++ test "hereditary beta: WHNF stops at a returned lambda retaining the supplied function in its body" + ([0, 4].all fun wrappers => [WhnfFlags.FULL, .DEF_EQ_CORE].all fun flags => + hereditaryBetaResult .zero wrappers 2 flags && hereditaryBetaResult (.succ .zero) wrappers 2 flags) + ++ test "hereditary beta: repeated returned functions preserve the lambda's changed body type" + ([1, 4, 11].all fun wrappers => + allSucceeded (repeatedBetaDeclaredType .zero 0 4 0 wrappers).1 5 && + allSucceeded (repeatedBetaDeclaredType (.succ .zero) 0 4 0 wrappers).1 5 && + repeatedBetaChangedBodyType .zero wrappers && changedBodyBetaPath .zero wrappers && + changedBodyBetaPath (.succ .zero) wrappers) + ++ test "hereditary beta: changed body types retain universe parameters and fresh-cache checks" + (allSucceeded (repeatedBetaDeclaredType (.var 0) 1 4 0 4).1 5 && + allSucceeded (repeatedBetaDeclaredType .zero 0 4 0 4).1 5 { clearEvery := 1 }) + ++ test "hereditary beta: substituting a different carrier rejects the retained dependent witness" + ([0, 1].all fun shape => let (env, target) := hereditaryBetaDeclaration .zero 0 4 shape 1 + rowFailed env target) + ++ test "hereditary beta: a carrier cannot inhabit its own retained witness domain" + ([0, 1].all fun shape => let (env, target) := hereditaryBetaDeclaration .zero 0 4 shape 2 + rowFailed env target) + private def applicationCases : TestSeq := test "application environment: Prop/Type identity calls and transitive theorem calls check" (allSucceeded applicationEnvironment 5 { clearEvery := 0 }) @@ -2952,7 +3079,7 @@ private def polymorphicDefinitionCases : TestSeq := public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, multiBetaCases, cheapLambdaCases, - cheapApplicationCases, exposedLambdaCases, repeatedBetaCases, betaTraceCases, + cheapApplicationCases, exposedLambdaCases, repeatedBetaCases, betaTraceCases, hereditaryBetaCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, sourceAgreementCases, sourceCacheCases, polymorphicDefinitionCases] diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index b21046735..71f762aa7 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -512,7 +512,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 587 exact theorem boundaries. The production +The consistency target checks 615 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -595,9 +595,27 @@ table using production substitution and suffix interning. Reading and intern coherence follow from the initial table and finite walker resources. The fuel bound includes the final unchanged `.done` iteration. The same trace supplies `DefinitionBodyTrace.betaDeclaredWhnfSupport` for declaration admission. -All seventeen new boundaries retain the same two existing native output-length -proofs. Automatic origins for arbitrary generated types, automatic finite -trace construction, and the remaining WHNF/conversion paths remain open. +These boundaries retain the same two existing native output-length proofs. +`SynthesisInference.betaTyping` now derives the complete head-beta typing +structure from every currently supported source-inference constructor. +`SynthesisBetaTyping` keeps each lambda body and both application children. +Its substitution operation rebuilds this structure beneath dependent binders, +using a proved context insertion to lift the supplied argument. Forward beta +type conversions preserve a lambda's exact Pi domain, including when cheap +beta changes its body's inferred type. `betaStep` computes both the next +typing derivation and conversion, so `betaSteps` handles any finite number +of contractions without additional intermediate checks or semantic origins. +`BetaStepPlan` contains only raw execution, reading, and finite representation +resources. `BetaWhnfTrace.annotate` derives every step's meaning from the +original typing derivation, and `SynthesisInference.beta_whnf_sound` proves +the actual uncached result's conversion, typing at the original type, reading, +and intern coherence. `DefinitionBodyTrace.betaDeclaredStepsSupport` and +`betaDeclaredWhnfPathSupport` carry these automatic origins into declaration +admission. The 28 additional audited boundaries introduce no axioms or native +proofs. This closes automatic semantic origins for finite head-beta paths of +the supported source-inference fragment. Initial inference trees, operational +paths, and representation resources remain explicit; deriving them for all +accepted programs and covering the remaining WHNF/conversion paths remain open. Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, argument order, and rejection of wrong arities and out-of-range parameters. @@ -662,6 +680,13 @@ bounded driver and uncached entry point. A budget equal to the number of reductions exhausts before `.done`; one additional iteration returns the exact inferred value type. Negative cases reject a different carrier and an invalid dependent argument after several returned-function prefixes. +Hereditary-beta cases substitute a supplied function beneath two dependent +binders before consuming both retained arguments. They cover up to twelve +later prefixes, reductions of types and ordinary terms, and WHNF stopping at +a returned lambda that still contains the supplied function. Further chains +retain a lambda body's changed cheap-beta type. Prop, Type, declaration +parameters, cleared caches, both WHNF policies, exact consumed-argument +counts, and rejected dependent substitutions are checked. These execution tests do not construct the general finite inference resources. Polymorphic-call regressions include Prop/Type instances in real function bodies, `max`/`imax` simplification inside Pi domains, closed nested references @@ -732,7 +757,7 @@ Definition-cycle regressions use content-addressed standalone and mutual declarations, including a self-justifying theorem, a two-member cycle, type cycles, lets, shared syntax, and binders. They check repeated member failures, acyclic forward references, cache clearing, and the partial/unsafe policy. -The unit suite contains 676 checks. The anonymous differential additionally +The unit suite contains 686 checks. The anonymous differential additionally serializes eight cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. diff --git a/docs/theory.md b/docs/theory.md index bb1dd65ab..3edc578f5 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -92,9 +92,17 @@ suffixes, and dependent substitutions. The result retains the original type even when adjacent steps carry different types. A structural-WHNF trace computes each raw substitution result and intern table and proves the actual uncached loop under its bound, including the final unchanged iteration. -Declaration admission uses the same traces. Automatic origin and trace -construction for arbitrary generated types, the remaining reduction branches, -and general conversion remain open. +Declaration admission uses the same traces. The currently supported source +inference trees now construct complete beta typing derivations, retaining +lambda bodies and both application children. Dependent substitution rebuilds +these derivations, including under retained binders. Each generated result +therefore supplies the exact lambda domains and argument origins for every +later head-beta step. This also handles a lambda whose body's inferred type +changes by cheap beta. A finite operational WHNF path needs only raw execution, +reading, and representation resources; its semantic origins and declaration +conversion follow automatically from the original inference. Constructing +the initial inference and operational resources for arbitrary accepted +programs, the remaining reduction branches, and general conversion remain open. Safe definition admission also rejects circular justification in both Lean and Rust, including `theorem loop : P := loop` with only `P : Prop` assumed. The production dependency walk returns an order with a proved decreasing rank; From 44fd6662c1a849cd183be0de15327273cf9c43d8 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 08:06:46 -0400 Subject: [PATCH 33/63] Prove beta Pi exposure in application inference --- Ix/Kernel/Verify/Consistency.lean | 22 +- .../Verify/Consistency/ApplicationWhnf.lean | 107 +++++ Ix/Kernel/Verify/Consistency/Audit.lean | 43 +- Ix/Kernel/Verify/Consistency/Beta.lean | 46 ++ .../Verify/Consistency/BetaInference.lean | 19 + .../Verify/Consistency/BetaPublicWhnf.lean | 452 ++++++++++++++++++ Ix/Kernel/Verify/Consistency/BetaSpine.lean | 67 --- Ix/Kernel/Verify/Consistency/BetaTrace.lean | 53 +- Ix/Kernel/Verify/Consistency/BetaWhnf.lean | 96 +--- .../Verify/Consistency/BetaWhnfInference.lean | 82 +++- .../Verify/Consistency/BetaWhnfPlan.lean | 182 +++++++ .../Verify/Consistency/SpineReading.lean | 68 +++ .../Consistency/SynthesisInference.lean | 199 +++++++- Ix/Kernel/Verify/Whnf.lean | 231 +-------- .../Verify/Whnf/Driver/CacheExecution.lean | 247 ++++++++++ Ix/Theory/Model/BetaSpine.lean | 88 +++- Tests/Ix/Kernel/Consistency.lean | 153 +++++- docs/kernel-verification.md | 44 +- 18 files changed, 1723 insertions(+), 476 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/ApplicationWhnf.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaWhnfPlan.lean create mode 100644 Ix/Kernel/Verify/Whnf/Driver/CacheExecution.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 37e37bab5..5004b12f1 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -24,6 +24,7 @@ import Ix.Kernel.Verify.Consistency.SourceOwnershipCheck import Ix.Kernel.Verify.Consistency.Context import Ix.Kernel.Verify.Consistency.BinderOpening import Ix.Kernel.Verify.Consistency.Application +import Ix.Kernel.Verify.Consistency.ApplicationWhnf import Ix.Kernel.Verify.Consistency.BinderInference import Ix.Kernel.Verify.Consistency.Formation import Ix.Kernel.Verify.Consistency.ContextInsertion @@ -36,6 +37,8 @@ import Ix.Kernel.Verify.Consistency.CheapBetaReading import Ix.Kernel.Verify.Consistency.BetaSpine import Ix.Kernel.Verify.Consistency.BetaTrace import Ix.Kernel.Verify.Consistency.BetaWhnf +import Ix.Kernel.Verify.Consistency.BetaWhnfPlan +import Ix.Kernel.Verify.Consistency.BetaPublicWhnf import Ix.Kernel.Verify.Consistency.BetaTyping import Ix.Kernel.Verify.Consistency.BetaInference import Ix.Kernel.Verify.Consistency.BetaWhnfInference @@ -133,16 +136,25 @@ lambda body's inferred type. Every finite head-beta path of the currently supported inference fragment therefore derives all its semantic step origins from the original check. The operational path contains only raw execution, reading, and finite representation resources, and its automatic annotation -also supplies declaration admission. Constructing the initial inference and -operational resources for all accepted programs, and covering the remaining -WHNF/conversion paths, remain open. +also supplies declaration admission. Public WHNF now computes the key states, +instrumentation, shared-fuel charge, and all three cache insertions for these +beta paths ending at a sort, Pi, or lambda. Pi exposure can execute such a +path or reuse its exact cached result without fuel. Application inference +uses that exposure between argument checks and derives the type conversion +from a retained actual check of the function type. Dependent codomain +substitution and all later beta origins preserve those argument checks. +The original synthesis admission theorem includes this application case. +Constructing initial inference and operational resources for all accepted +programs, other WHNF branches, mixed cache states, and general conversion +remain open. Local cache hits agree with the actual declaration type. Constant hits agree with pure universe substitution of a loaded, admitted declaration; sort hits return the canonical successor sort. Application, forall, and lambda nodes retain misses in every eligible cache partition. Full mode leaves the inference-only partition unconstrained at a miss. -Applications use syntactic Pi exposure, full argument checking, hash conversion, -and arguments without eager-reduction markers. Constant- and local-headed spines +Applications use syntactic or supported beta Pi exposure, full argument checking, +hash comparison of the argument with the exposed domain, and arguments without +eager-reduction markers. Constant- and local-headed spines can derive type validity from the admitted model or local context; their arguments may be lambdas. The synthesis rules also allow lambdas and their application results in function position, with derived formation bounds. diff --git a/Ix/Kernel/Verify/Consistency/ApplicationWhnf.lean b/Ix/Kernel/Verify/Consistency/ApplicationWhnf.lean new file mode 100644 index 000000000..c2ae62241 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/ApplicationWhnf.lean @@ -0,0 +1,107 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaPublicWhnf + +/-! The application branch's actual Pi-exposure call and its subsequent +argument check, comparison, and dependent codomain substitution. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +structure ApplicationWhnfInferenceTrace (fuel : Nat) (before : TcState .anon) + (fn arg : KExpr .anon) where + functionType : KExpr .anon + domain : KExpr .anon + codomain : KExpr .anon + functionState : TcState .anon + exposedState : TcState .anon + argumentType : KExpr .anon + argumentState : TcState .anon + comparedState : TcState .anon + functionRun : RecM.infer fn (methodsN fuel) before = .ok functionType functionState + exposureRun : (RecM.ensureForallDirect functionType).run (methodsN (fuel + 1)) functionState = + .ok (domain, codomain) exposedState + argumentRun : RecM.infer arg (methodsN fuel) exposedState = .ok argumentType argumentState + ordinary : TcM.isEagerReduce arg argumentState = .ok false argumentState + compareRun : RecM.isDefEq argumentType domain (methodsN fuel) argumentState = .ok true comparedState + contextPreserved : functionState.lctx = before.lctx + +theorem ApplicationWhnfInferenceTrace.output_state {fuel : Nat} {before after : TcState .anon} + {fn arg result : KExpr .anon} {info : ExprInfo .anon} + (trace : ApplicationWhnfInferenceTrace fuel before fn arg) + (accepted : RecM.inferUncached RecM.inferCall false (.app fn arg info) + (methodsN (fuel + 1)) before = .ok result after) : + result = (subst trace.codomain arg 0 trace.comparedState.env.intern).1 ∧ + after = { trace.comparedState with env := { trace.comparedState.env with + intern := (subst trace.codomain arg 0 trace.comparedState.env.intern).2 } } := by + change (RecM.inferUncached RecM.inferCall false (.app fn arg info)).run + (methodsN (fuel + 1)) before = .ok result after at accepted + unfold RecM.inferUncached at accepted + simp only [ReaderT.run_bind] at accepted + change EStateM.bind (RecM.infer fn (methodsN fuel)) _ before = _ at accepted + rw [EStateM.bind, trace.functionRun] at accepted + change EStateM.bind ((RecM.ensureForallDirect trace.functionType).run (methodsN (fuel + 1))) + _ trace.functionState = _ at accepted + rw [EStateM.bind, trace.exposureRun] at accepted + change EStateM.bind (RecM.infer arg (methodsN fuel)) _ trace.exposedState = _ at accepted + rw [EStateM.bind, trace.argumentRun] at accepted + change EStateM.bind (TcM.isEagerReduce arg) _ trace.argumentState = _ at accepted + rw [EStateM.bind, trace.ordinary] at accepted + change EStateM.bind (RecM.isDefEq trace.argumentType trace.domain (methodsN fuel)) + _ trace.argumentState = _ at accepted + rw [EStateM.bind, trace.compareRun] at accepted + cases accepted + exact ⟨rfl, rfl⟩ + +theorem ApplicationWhnfInferenceTrace.output {fuel : Nat} {before after : TcState .anon} + {fn arg result : KExpr .anon} {info : ExprInfo .anon} + (trace : ApplicationWhnfInferenceTrace fuel before fn arg) + (accepted : RecM.inferUncached RecM.inferCall false (.app fn arg info) + (methodsN (fuel + 1)) before = .ok result after) : + result = (subst trace.codomain arg 0 trace.comparedState.env.intern).1 := + (trace.output_state accepted).1 + +theorem ApplicationWhnfInferenceTrace.exposure_state {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {fuel : Nat} + {before : TcState .anon} {fn arg : KExpr .anon} {term domain body : AExpr β} {condition : Certified.PropWhen} + (trace : ApplicationWhnfInferenceTrace fuel before fn arg) + (exposure : BetaPiExposure resolve locals fuel trace.functionState trace.functionType term + condition domain body trace.domain trace.codomain) : trace.exposedState = exposure.after := by + have accepted := trace.exposureRun + rw [exposure.run] at accepted + exact (EStateM.Result.ok.inj accepted).2.symm + +theorem ApplicationWhnfInferenceTrace.exposure_context {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {fuel : Nat} + {before : TcState .anon} {fn arg : KExpr .anon} {term domain body : AExpr β} {condition : Certified.PropWhen} + (trace : ApplicationWhnfInferenceTrace fuel before fn arg) + (exposure : BetaPiExposure resolve locals fuel trace.functionState trace.functionType term + condition domain body trace.domain trace.codomain) : trace.exposedState.lctx = before.lctx := by + rw [trace.exposure_state exposure, exposure.context] + exact trace.contextPreserved + +/-- Build the Pi-exposure part from its public beta path. The argument +check begins in the computed post-exposure state. -/ +def ApplicationWhnfInferenceTrace.ofBeta {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {fuel : Nat} + {before functionState argumentState comparedState : TcState .anon} + {fn arg functionType domain codomain argumentType : KExpr .anon} + {term A B : AExpr β} {condition : Certified.PropWhen} + (functionRun : RecM.infer fn (methodsN fuel) before = .ok functionType functionState) + (exposure : BetaPiExposure resolve locals fuel functionState functionType term condition A B domain codomain) + (argumentRun : RecM.infer arg (methodsN fuel) exposure.after = .ok argumentType argumentState) + (ordinary : TcM.isEagerReduce arg argumentState = .ok false argumentState) + (compareRun : RecM.isDefEq argumentType domain (methodsN fuel) argumentState = .ok true comparedState) + (contextPreserved : functionState.lctx = before.lctx) : + ApplicationWhnfInferenceTrace fuel before fn arg := + { functionType, domain, codomain, functionState, exposedState := exposure.after, + argumentType, argumentState, comparedState, functionRun, exposureRun := exposure.run, + argumentRun, ordinary, compareRun, contextPreserved } + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index a7d37de45..6396895fd 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -489,6 +489,47 @@ private def hereditaryBetaRoots : Array RootAllowance := #[ { root := ``DefinitionBodyTrace.betaDeclaredWhnfPathSupport, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } ] +private def piExposureRoots : Array RootAllowance := #[ + { root := ``Theory.Model.AExpr.HeadRigid.map }, + { root := ``Theory.Model.ContextSubstitution.lift_conversion, standardAxioms := standard }, + { root := ``Theory.Model.LambdaSpineTyping.convert, standardAxioms := standard }, + { root := ``BetaWhnfTrace.run, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaWhnfTrace.reading, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaWhnfTrace.frame, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaWhnfTrace.first, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaStepPlan.not_transient, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``betaWhnfKey_run, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``betaWhnfKey_environment, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``betaWhnfKey_context, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``betaWhnfKey_native, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``betaWhnfKey_fuel, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``betaWhnfPrefix_run, standardAxioms := standard }, + { root := ``betaWhnfPrefix_fields, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``betaWhnfCharge_run, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``betaWhnfCharge_fields, standardAxioms := #[``propext, ``Quot.sound] }, + { root := ``BetaWhnfTerminal.noDelta_tail, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaWhnfTerminal.full_step, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaWhnfTerminal.noDelta_uncached, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaWhnfTerminal.full_uncached, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaPublicWhnf.coreKey_fields, standardAxioms := standard, nativeAxioms := #[expressionNative] }, + { root := ``BetaPublicWhnfPlan.run, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaPublicWhnfPlan.reading, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaPublicWhnfPlan.context, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaPublicWhnfPlan.cache_hit, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaPiExposure.run, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaPiExposure.reading, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaPiExposure.context, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``ApplicationWhnfInferenceTrace.output_state, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``ApplicationWhnfInferenceTrace.output, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``ApplicationWhnfInferenceTrace.exposure_state, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``ApplicationWhnfInferenceTrace.exposure_context, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``ApplicationWhnfInferenceTrace.ofBeta, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaPublicWhnfPlan.betaTrace, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaPiExposure.betaTrace, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaPiExposure.checkedTrace, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.beta_public_whnf_sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -617,7 +658,7 @@ def roots : Array RootAllowance := #[ root, standardAxioms := standard, nativeAxioms := productionNative, forbiddenDependencies := forbiddenProduction }) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots ++ exposedOriginRoots ++ - repeatedBetaRoots ++ betaTraceRoots ++ hereditaryBetaRoots).map (fun allowance => { + repeatedBetaRoots ++ betaTraceRoots ++ hereditaryBetaRoots ++ piExposureRoots).map (fun allowance => { allowance with forbiddenDependencies := forbiddenProduction }) ++ #[{ root := ``extend_atomic_definition, standardAxioms := standard }] diff --git a/Ix/Kernel/Verify/Consistency/Beta.lean b/Ix/Kernel/Verify/Consistency/Beta.lean index 2c7cd340b..c102c4f74 100644 --- a/Ix/Kernel/Verify/Consistency/Beta.lean +++ b/Ix/Kernel/Verify/Consistency/Beta.lean @@ -94,6 +94,20 @@ def SynthesisInference.betaResultOrigin {β : Type u} {resolve : Address → Opt (.source (.checked contextOrigin functionTree keyedAgreement functionReads trace.functionRun))).substituteAt (.source (.applicationArgument contextOrigin trace functionTree argumentTree keyedAgreement functionReads argumentReads conditions hashPath comparisonFaithful)) .root + | appBeta full miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath + comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful => + have same := reduction.rigid (by + obtain ⟨_, rfl⟩ := functionTree.lambda_type + intro fn arg same + cases same) + cases same + obtain ⟨rfl, rfl⟩ := lambda_inference_domain functionTree + obtain ⟨functionReads, argumentReads⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + exact (SynthesisTypingOrigin.lambdaBody + (.source (.checked contextOrigin functionTree keyedAgreement functionReads trace.functionRun))).substituteAt + (.source (.applicationBetaArgument contextOrigin trace functionTree exposure exposureCoherent argumentTree + keyedAgreement functionReads argumentReads conditions hashPath comparisonFaithful)) .root /-- When the lambda body applies its parameter, its actual body checks supply the next reduction origin after beta exposes the supplied lambda. @@ -134,6 +148,22 @@ def SynthesisInference.betaNextOrigin {β : Type u} {resolve : Address → Optio conditions hashPath comparisonFaithful) .root enough (by simpa only [AExpr.inst_variable_appN] using resultOrigin) simpa only [AExpr.liftN_zero, List.map_id'] using flattened + | appBeta full miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath + comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful => + have same := reduction.rigid (by + obtain ⟨_, rfl⟩ := functionTree.lambda_type + intro fn arg same + cases same) + cases same + obtain ⟨rfl, rfl⟩ := lambda_inference_domain functionTree + obtain ⟨functionReads, argumentReads⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨_, bodySpine⟩ := functionTree.lambdaBodyVariableSpine contextOrigin keyedAgreement functionReads + have flattened := SynthesisReductionOrigin.substitutedResult bodySpine.spine bodySpine.atIndex + (.applicationBetaArgument contextOrigin trace functionTree exposure exposureCoherent argumentTree + keyedAgreement functionReads argumentReads conditions hashPath comparisonFaithful) .root enough + (by simpa only [AExpr.inst_variable_appN] using resultOrigin) + simpa only [AExpr.liftN_zero, List.map_id'] using flattened /-- A supported successful inference of a source beta redex derives both equality with the substitution result and typing of that result. No run @@ -175,6 +205,22 @@ theorem SynthesisInference.beta_sound {β : Type u} have typedArgument := sameType ▸ argumentTyped exact ⟨ConversionClaim.beta functionTyped typedArgument, TypingClaim.betaResult functionTyped typedArgument⟩ + | appBeta full miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath + comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful => + have same := reduction.rigid (by + obtain ⟨_, rfl⟩ := functionTree.lambda_type + intro fn arg same + cases same) + cases same + obtain ⟨rfl, rfl⟩ := lambda_inference_domain functionTree + obtain ⟨fnReads, argReads⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨_, functionTyped, _⟩ := functionTree.sound formed keyedAgreement fnReads trace.functionRun + have checked := SynthesisCheckedOrigin.applicationBetaArgument .current trace functionTree exposure + exposureCoherent argumentTree keyedAgreement fnReads argReads conditions hashPath comparisonFaithful + have typedArgument := (checked.soundWithSpine formed).1 + exact ⟨ConversionClaim.beta functionTyped typedArgument, + TypingClaim.betaResult functionTyped typedArgument⟩ /-- Two successive beta prefixes can use different lambda origins. The second comes from an actual supplied argument, rather than a fresh check diff --git a/Ix/Kernel/Verify/Consistency/BetaInference.lean b/Ix/Kernel/Verify/Consistency/BetaInference.lean index 5fb34307f..0c52caeb8 100644 --- a/Ix/Kernel/Verify/Consistency/BetaInference.lean +++ b/Ix/Kernel/Verify/Consistency/BetaInference.lean @@ -96,6 +96,25 @@ def SynthesisInference.betaTyping {β : Type u} {resolve : Address → Option (C (readScopedExpr?_all_parts functionTypeReads).1))) conditions exact .app (functionTree.betaTyping contextOrigin keyedAgreement functionReading trace.functionRun formed) (sameType ▸ argumentTree.betaTyping contextOrigin argumentAgreement argumentReading trace.argumentRun formed) + | .appBeta _ miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath + comparisonFaithful _ _ _ _ _ _ => + fun contextOrigin agreement reading _ formed => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have argumentAgreement := (trace.exposure_context exposure).symm ▸ keyedAgreement + have contextFormation := contextOrigin.sound formed + have functionTypeReads := + (functionTree.soundWithSpine contextFormation keyedAgreement functionReading trace.functionRun).1 + have argumentTypeReads := + (argumentTree.soundWithSpine contextFormation argumentAgreement argumentReading trace.argumentRun).1 + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (argumentTypeReads.symm.trans + ((beq_readScopedExpr? comparisonFaithful hashPath).trans + (exposure.reading functionTypeReads exposureCoherent).1))) conditions + exact .app + (.convert (functionTree.betaTyping contextOrigin keyedAgreement functionReading trace.functionRun formed) + (.rebase contextOrigin reduction)) + (sameType ▸ argumentTree.betaTyping contextOrigin argumentAgreement argumentReading trace.argumentRun formed) | .lam full miss trace opening absent domainTree bodyTree conditionAgrees constructed bound coherent closingFaithful faithful => fun contextOrigin agreement reading accepted formed => by diff --git a/Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean b/Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean new file mode 100644 index 000000000..389598a58 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean @@ -0,0 +1,452 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaWhnfPlan +import Ix.Kernel.Verify.Whnf.Driver.CacheExecution + +/-! Connect raw beta paths to the public WHNF drivers, their cache writes, +and the Pi exposure used by application inference. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- Compute exactly the context-digest memoization performed by a WHNF key. -/ +def betaWhnfKey (source : KExpr .anon) (before : TcState .anon) : + (Address × Address) × TcState .anon := + if source.lbr == 0 || before.ctx.isEmpty then + ((source.addr, emptyCtxAddr), before) + else + let cacheKey := (before.ctxId, source.lbr) + match before.ctxAddrCache[cacheKey]? with + | some cached => ((source.addr, cached), before) + | none => + let digest := TcM.ctxAddrForLbrUncached before source.lbr + ((source.addr, digest), { before with ctxAddrCache := before.ctxAddrCache.insert cacheKey digest }) + +theorem betaWhnfKey_run (source : KExpr .anon) (before : TcState .anon) : + TcM.whnfKey source before = .ok (betaWhnfKey source before).1 (betaWhnfKey source before).2 := by + unfold TcM.whnfKey TcM.ctxAddrForLbr betaWhnfKey + change EStateM.bind (fun state => EStateM.bind (get : TcM .anon (TcState .anon)) _ state) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + by_cases fast : (source.lbr == 0 || before.ctx.isEmpty) = true + · simp only [if_pos fast]; rfl + · simp only [if_neg fast] + cases before.ctxAddrCache[(before.ctxId, source.lbr)]? <;> rfl + +theorem betaWhnfKey_environment (source : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey source before).2.env = before.env := by + unfold betaWhnfKey + split + · rfl + · dsimp only + split <;> rfl + +theorem betaWhnfKey_context (source : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey source before).2.lctx = before.lctx := by + unfold betaWhnfKey + split + · rfl + · dsimp only + split <;> rfl + +theorem betaWhnfKey_native (source : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey source before).2.inNativeReduce = before.inNativeReduce := by + unfold betaWhnfKey + split + · rfl + · dsimp only + split <;> rfl + +/-- Full WHNF's instrumentation changes only its optional call counter. -/ +def betaWhnfPrefix (before : TcState .anon) : TcState .anon := + if before.stats then { before with whnfCalls := before.whnfCalls + 1 } else before + +theorem betaWhnfPrefix_run (source : KExpr .anon) (before : TcState .anon) (methods : Methods .anon) : + (RecM.whnfWithNatSuccModePrefix source).run methods before = .ok () (betaWhnfPrefix before) := by + have traced : TcM.stepTrace "whnf+" (fun _ => TcM.addr8 source.addr) before = .ok () before := by + unfold TcM.stepTrace + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + split <;> rfl + unfold RecM.whnfWithNatSuccModePrefix + rw [ReaderT.run_bind] + change EStateM.bind (TcM.stepTrace "whnf+" (fun _ => TcM.addr8 source.addr)) _ before = _ + rw [EStateM.bind, traced] + change TcM.bumpStats (fun state => { state with whnfCalls := state.whnfCalls + 1 }) before = _ + unfold TcM.bumpStats betaWhnfPrefix + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + split <;> rfl + +/-- The miss charge performs one shared-fuel decrement and optionally +increments its miss counter. -/ +def betaWhnfCharge (before : TcState .anon) : TcState .anon := + if before.stats then + { before with whnfMisses := before.whnfMisses + 1, recFuel := before.recFuel - 1 } + else { before with recFuel := before.recFuel - 1 } + +theorem betaWhnfCharge_run (before : TcState .anon) (methods : Methods .anon) + (enough : (before.recFuel == 0) = false) : + (RecM.whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods before = + .ok () (betaWhnfCharge before) := by + unfold RecM.whnfWithNatSuccModeMissCharge + rw [ReaderT.run_bind] + change EStateM.bind (TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 })) + (fun _ => TcM.tick) before = _ + cases stats : before.stats + · have bumped : TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 }) before = + .ok () before := by + unfold TcM.bumpStats + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + rw [if_neg (by simp only [stats, Bool.false_eq_true, not_false_eq_true])]; rfl + rw [EStateM.bind, bumped] + change TcM.tick before = _ + unfold TcM.tick betaWhnfCharge + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + rw [enough, stats]; rfl + · have bumped : TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 }) before = + .ok () { before with whnfMisses := before.whnfMisses + 1 } := by + unfold TcM.bumpStats + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + rw [if_pos stats]; rfl + rw [EStateM.bind, bumped] + change TcM.tick { before with whnfMisses := before.whnfMisses + 1 } = _ + unfold TcM.tick betaWhnfCharge + change EStateM.bind (get : TcM .anon (TcState .anon)) _ { before with whnfMisses := before.whnfMisses + 1 } = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) + { before with whnfMisses := before.whnfMisses + 1 } = + .ok { before with whnfMisses := before.whnfMisses + 1 } + { before with whnfMisses := before.whnfMisses + 1 } from rfl] + rw [enough, stats]; rfl + +/-- Constructors on which both public drivers and all their reducer tails +stop without recursive callbacks or source lookup. -/ +inductive BetaWhnfTerminal : KExpr .anon → Prop + | sort (level : KUniv .anon) (info : ExprInfo .anon) : BetaWhnfTerminal (.sort level info) + | forallE (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) + (domain body : KExpr .anon) (info : ExprInfo .anon) : BetaWhnfTerminal (.all name bi domain body info) + | lam (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) + (domain body : KExpr .anon) (info : ExprInfo .anon) : BetaWhnfTerminal (.lam name bi domain body info) + +theorem BetaWhnfTerminal.noDelta_tail {source : KExpr .anon} (terminal : BetaWhnfTerminal source) + (methods : Methods .anon) (before : TcState .anon) (flags : WhnfFlags) (mode : NatSuccMode) : + (RecM.whnfNoDeltaReducersStep flags mode source).run methods before = .ok (.done source) before := by + cases terminal <;> + simp [RecM.whnfNoDeltaReducersStep, RecM.tryProjAppReduceFinished, RecM.tryProjAppReduce, + RecM.tryReduceBitvec, RecM.tryReduceNatWithSuccMode, RecM.tryReduceNative, + RecM.tryReduceString, RecM.tryReduceProjectionDefinition, RecM.tryQuotReduce, + KExpr.collectSpine, KExpr.collectSpine.go, RecM.prims, ReaderT.run_bind] + all_goals + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + cases before.noAccel <;> rfl + +theorem BetaWhnfTerminal.full_step {source result : KExpr .anon} + (terminal : BetaWhnfTerminal result) {methods : Methods .anon} {before after : TcState .anon} + (mode : NatSuccMode) (seen : Std.HashSet Address) + (reduced : (RecM.whnfNoDeltaImpl source .FULL mode).run methods before = .ok result after) : + (RecM.whnfWithNatSuccModeStep mode (source, seen)).run methods before = .ok (.done result) after := by + unfold RecM.whnfWithNatSuccModeStep + rw [ReaderT.run_bind] + change EStateM.bind ((RecM.whnfNoDeltaImpl source .FULL mode).run methods) _ before = _ + rw [EStateM.bind, reduced] + simp only + by_cases repeated : seen.contains result.addr = true + · rw [if_pos repeated]; rfl + · rw [if_neg repeated] + cases terminal <;> + simp [RecM.tryReduceNative, RecM.tryReduceBitvec, RecM.tryReduceNatWithSuccMode, + RecM.tryReduceDecidable, RecM.tryReduceString, RecM.tryNatOffsetStuck, + RecM.natOffsetStuckHead, RecM.deltaUnfoldOne, RecM.tryDeltaUnfold, + KExpr.collectSpine, KExpr.collectSpine.go, RecM.prims, ReaderT.run_bind] + all_goals + change EStateM.bind (get : TcM .anon (TcState .anon)) _ after = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) after = .ok after after from rfl] + cases native : after.noAccel + all_goals + change EStateM.bind (get : TcM .anon (TcState .anon)) _ after = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) after = .ok after after from rfl] + simp only [native] + rfl + +private theorem bounded_done {α γ : Type} (step : α → RecM .anon (RecM.BoundedStep α γ)) + {methods : Methods .anon} {before after : TcState .anon} {source : α} {result : γ} + (done : (step source).run methods before = .ok (.done result) after) + {fuel : Nat} (enough : 0 < fuel) : + (RecM.runBounded step fuel source).run methods before = .ok result after := by + cases fuel with + | zero => omega + | succ fuel => + rw [RecM.runBounded, ReaderT.run_bind] + change EStateM.bind ((step source).run methods) _ before = _ + rw [EStateM.bind, done] + rfl + +theorem BetaWhnfTerminal.noDelta_uncached {source result : KExpr .anon} + (terminal : BetaWhnfTerminal result) {methods : Methods .anon} {before after : TcState .anon} + (flags : WhnfFlags) (mode : NatSuccMode) + (reduced : (RecM.whnfCoreWithFlags source flags).run methods before = .ok result after) : + (RecM.whnfNoDeltaImplUncached source flags mode).run methods before = .ok result after := by + apply bounded_done (enough := by decide) + unfold RecM.whnfNoDeltaImplStep + rw [ReaderT.run_bind] + change EStateM.bind ((RecM.whnfCoreWithFlags source flags).run methods) _ before = _ + rw [EStateM.bind, reduced] + exact terminal.noDelta_tail methods after flags mode + +theorem BetaWhnfTerminal.full_uncached {source result : KExpr .anon} + (terminal : BetaWhnfTerminal result) {methods : Methods .anon} {before after : TcState .anon} + (mode : NatSuccMode) + (reduced : (RecM.whnfNoDeltaImpl source .FULL mode).run methods before = .ok result after) : + (RecM.whnfWithNatSuccModeUncached source mode).run methods before = .ok result after := + bounded_done _ (terminal.full_step mode {} reduced) (by decide) + +def BetaWhnfTrace.first {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {fuel steps : Nat} {flags : WhnfFlags} {before after : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (trace : BetaWhnfTrace resolve locals fuel flags steps before source term after result target) : + 0 < steps → BetaStepPlan resolve locals before source term := + match trace with + | .done _ => fun impossible => False.elim (Nat.not_lt_zero _ impossible) + | .next plan _ => fun _ => plan + +theorem BetaStepPlan.not_transient {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {before : TcState .anon} {source : KExpr .anon} {term : AExpr β} + (plan : BetaStepPlan resolve locals before source term) (methods : Methods .anon) (state : TcState .anon) : + (RecM.isTransientNatLiteralWork source).run methods state = .ok false state := by + simp only [plan.sourceEq, RecM.isTransientNatLiteralWork, RecM.isNatLiteralRecursorApp, + plan.spine, pure_bind] + rfl + +theorem betaWhnfKey_fuel (source : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey source before).2.recFuel = before.recFuel := by + unfold betaWhnfKey + split + · rfl + · dsimp only + split <;> rfl + +theorem betaWhnfPrefix_fields (before : TcState .anon) : + (betaWhnfPrefix before).env = before.env ∧ + (betaWhnfPrefix before).lctx = before.lctx ∧ + (betaWhnfPrefix before).inNativeReduce = before.inNativeReduce ∧ + (betaWhnfPrefix before).recFuel = before.recFuel := by + unfold betaWhnfPrefix + split <;> exact ⟨rfl, rfl, rfl, rfl⟩ + +theorem betaWhnfCharge_fields (before : TcState .anon) : + (betaWhnfCharge before).env = before.env ∧ + (betaWhnfCharge before).lctx = before.lctx ∧ + (betaWhnfCharge before).inNativeReduce = before.inNativeReduce := by + unfold betaWhnfCharge + split <;> exact ⟨rfl, rfl, rfl⟩ + +namespace BetaPublicWhnf + +def outerKey (source : KExpr .anon) (before : TcState .anon) := + betaWhnfKey source (betaWhnfPrefix before) + +def noDeltaKey (source : KExpr .anon) (before : TcState .anon) := + betaWhnfKey source (betaWhnfCharge (outerKey source before).2) + +def coreKey (source : KExpr .anon) (before : TcState .anon) := + betaWhnfKey source (noDeltaKey source before).2 + +def coreAfter (source result : KExpr .anon) (before reduced : TcState .anon) : TcState .anon := + { reduced with env := { reduced.env with + whnfCoreCache := reduced.env.whnfCoreCache.insert (coreKey source before).1 result } } + +def noDeltaAfter (source result : KExpr .anon) (before reduced : TcState .anon) : TcState .anon := + let state := coreAfter source result before reduced + { state with env := { state.env with + whnfNoDeltaCache := state.env.whnfNoDeltaCache.insert (noDeltaKey source before).1 result } } + +def after (source result : KExpr .anon) (before reduced : TcState .anon) : TcState .anon := + let state := noDeltaAfter source result before reduced + { state with env := { state.env with + whnfCache := state.env.whnfCache.insert (outerKey source before).1 result } } + +theorem coreKey_fields (source : KExpr .anon) (before : TcState .anon) : + (coreKey source before).2.env = before.env ∧ + (coreKey source before).2.lctx = before.lctx ∧ + (coreKey source before).2.inNativeReduce = before.inNativeReduce := by + simp only [coreKey, noDeltaKey, outerKey, betaWhnfKey_environment, betaWhnfKey_context, + betaWhnfKey_native, betaWhnfCharge_fields, betaWhnfPrefix_fields, and_self] + +end BetaPublicWhnf + +/-- A public beta reduction through the three cache-miss layers. The key +states, instrumentation, fuel charge, and final cache insertions are computed; +the only reduction resource is the raw beta path. -/ +structure BetaPublicWhnfPlan {β : Type u} (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (fuel : Nat) (before : TcState .anon) + (source : KExpr .anon) (term : AExpr β) (result : KExpr .anon) (target : AExpr β) where + steps : Nat + reduced : TcState .anon + path : BetaWhnfTrace resolve locals fuel .FULL steps + (BetaPublicWhnf.coreKey source before).2 source term reduced result target + moving : 0 < steps + enough : steps < maxWhnfCoreFuel.toNat + fuelAvailable : (before.recFuel == 0) = false + native : before.inNativeReduce = false + outerMiss : (BetaPublicWhnf.outerKey source before).2.env.whnfCache[ + (BetaPublicWhnf.outerKey source before).1]? = none + noDeltaMiss : (BetaPublicWhnf.noDeltaKey source before).2.env.whnfNoDeltaCache[ + (BetaPublicWhnf.noDeltaKey source before).1]? = none + coreMiss : (BetaPublicWhnf.coreKey source before).2.env.whnfCoreCache[ + (BetaPublicWhnf.coreKey source before).1]? = none + terminal : BetaWhnfTerminal result + +namespace BetaPublicWhnfPlan + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {fuel : Nat} {before : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} + +def after (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : TcState .anon := + BetaPublicWhnf.after source result before plan.reduced + +theorem run (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : + (RecM.whnf source).run (methodsN (fuel + 1)) before = .ok result plan.after := by + let first := plan.path.first plan.moving + have coreEntry : RecM.whnfCoreWithFlags source .FULL = RecM.whnfCoreWithFlagsNonLeaf source .FULL := by + rw [first.sourceEq]; rfl + have coreRun : (RecM.whnfCoreWithFlags source .FULL).run (methodsN (fuel + 1)) + (BetaPublicWhnf.noDeltaKey source before).2 = + .ok result (BetaPublicWhnf.coreAfter source result before plan.reduced) := by + rw [coreEntry] + exact RecM.whnfCoreWithFlagsNonLeaf_fullMiss rfl + (betaWhnfKey_run source _) (first.not_transient _ _) plan.coreMiss (plan.path.run plan.enough) + have reducedNative : plan.reduced.inNativeReduce = false := by + obtain ⟨table, frame⟩ := plan.path.frame + exact (congrArg TcState.inNativeReduce frame).trans + ((BetaPublicWhnf.coreKey_fields source before).2.2.trans plan.native) + have noDeltaEntry : RecM.whnfNoDeltaImpl source .FULL .collapse = + RecM.whnfNoDeltaImplNonLeaf source .FULL .collapse := by + rw [first.sourceEq]; rfl + have noDeltaRun : (RecM.whnfNoDeltaImpl source .FULL .collapse).run (methodsN (fuel + 1)) + (betaWhnfCharge (BetaPublicWhnf.outerKey source before).2) = + .ok result (BetaPublicWhnf.noDeltaAfter source result before plan.reduced) := by + rw [noDeltaEntry] + exact RecM.whnfNoDeltaImplNonLeaf_fullMiss rfl (betaWhnfKey_run source _) + (first.not_transient _ _) plan.noDeltaMiss + (plan.terminal.noDelta_uncached .FULL .collapse coreRun) reducedNative + have fullEntry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := by + rw [first.sourceEq]; rfl + rw [fullEntry] + exact RecM.whnfWithNatSuccModeNonLeaf_miss (betaWhnfPrefix_run source before _) + (betaWhnfKey_run source _) (first.not_transient _ _) plan.outerMiss + (betaWhnfCharge_run _ _ (by + simpa only [BetaPublicWhnf.outerKey, betaWhnfKey_fuel, (betaWhnfPrefix_fields before).2.2.2] using plan.fuelAvailable)) + (plan.terminal.full_uncached .collapse noDeltaRun) reducedNative + +theorem reading (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) + (sourceReading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) : + readScopedExpr? resolve locals result = some target.erase ∧ plan.after.env.intern.WF := by + exact plan.path.reading sourceReading ((BetaPublicWhnf.coreKey_fields source before).1.symm ▸ coherent) + +theorem context (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : + plan.after.lctx = before.lctx := by + obtain ⟨table, frame⟩ := plan.path.frame + exact (congrArg TcState.lctx frame).trans (BetaPublicWhnf.coreKey_fields source before).2.1 + +theorem cache_hit (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) + (methods : Methods .anon) (current : TcState .anon) + (hit : (BetaPublicWhnf.outerKey source current).2.env.whnfCache[ + (BetaPublicWhnf.outerKey source current).1]? = some result) : + (RecM.whnf source).run methods current = .ok result (BetaPublicWhnf.outerKey source current).2 := by + let first := plan.path.first plan.moving + have entry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := by + rw [first.sourceEq]; rfl + rw [entry] + exact RecM.whnfWithNatSuccModeNonLeaf_hit (betaWhnfPrefix_run source current _) + (betaWhnfKey_run source _) (first.not_transient _ _) hit + +end BetaPublicWhnfPlan + +/-- Pi exposure retains a raw beta path through public WHNF, or reuses +that path's exact result from the public cache. A cache hit consumes no fuel. -/ +inductive BetaPiExposure {β : Type u} (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (fuel : Nat) (before : TcState .anon) + (source : KExpr .anon) (term : AExpr β) (condition : Certified.PropWhen) (domain body : AExpr β) + (rawDomain rawBody : KExpr .anon) : Type u + | reduce {name bi info} + (plan : BetaPublicWhnfPlan resolve locals fuel before source term + (.all name bi rawDomain rawBody info) (.forallE condition domain body)) : + BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody + | cached {originFuel originBefore name bi info} + (origin : BetaPublicWhnfPlan resolve locals originFuel originBefore source term + (.all name bi rawDomain rawBody info) (.forallE condition domain body)) + (coherent : originBefore.env.intern.WF) + (hit : (BetaPublicWhnf.outerKey source before).2.env.whnfCache[ + (BetaPublicWhnf.outerKey source before).1]? = some (.all name bi rawDomain rawBody info)) : + BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody + +namespace BetaPiExposure + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {fuel : Nat} {before : TcState .anon} {source rawDomain rawBody : KExpr .anon} + {term domain body : AExpr β} {condition : Certified.PropWhen} + +def after (exposure : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody) : + TcState .anon := + match exposure with + | .reduce plan => plan.after + | .cached .. => (BetaPublicWhnf.outerKey source before).2 + +theorem run (exposure : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody) : + (RecM.ensureForallDirect source).run (methodsN (fuel + 1)) before = + .ok (rawDomain, rawBody) exposure.after := by + cases exposure with + | reduce plan => + have first := plan.path.first plan.moving + have entry : RecM.ensureForallDirect source = RecM.ensureForallWhnf source := by + rw [first.sourceEq]; rfl + rw [entry, RecM.ensureForallWhnf, ReaderT.run_bind] + change EStateM.bind ((RecM.whnf source).run (methodsN (fuel + 1))) _ before = _ + rw [EStateM.bind, plan.run] + rfl + | cached origin _ hit => + have first := origin.path.first origin.moving + have entry : RecM.ensureForallDirect source = RecM.ensureForallWhnf source := by + rw [first.sourceEq]; rfl + rw [entry, RecM.ensureForallWhnf, ReaderT.run_bind] + change EStateM.bind ((RecM.whnf source).run (methodsN (fuel + 1))) _ before = _ + rw [EStateM.bind, origin.cache_hit _ _ hit] + rfl + +theorem reading (exposure : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody) + (sourceReading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) : + readScopedExpr? resolve locals rawDomain = some domain.erase ∧ + readScopedExpr? resolve locals rawBody 1 = some body.erase ∧ exposure.after.env.intern.WF := by + cases exposure with + | reduce plan => + obtain ⟨reads, preserved⟩ := plan.reading sourceReading coherent + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reads + exact ⟨domainReads, bodyReads, preserved⟩ + | cached origin originCoherent _ => + obtain ⟨reads, _⟩ := origin.reading sourceReading originCoherent + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reads + refine ⟨domainReads, bodyReads, ?_⟩ + simpa only [after, BetaPublicWhnf.outerKey, betaWhnfKey_environment, + (betaWhnfPrefix_fields before).1] using coherent + +theorem context (exposure : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody) : + exposure.after.lctx = before.lctx := by + cases exposure with + | reduce plan => exact plan.context + | cached origin originCoherent hit => + simp only [after, BetaPublicWhnf.outerKey, betaWhnfKey_context, (betaWhnfPrefix_fields before).2.1] + +end BetaPiExposure + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaSpine.lean b/Ix/Kernel/Verify/Consistency/BetaSpine.lean index 5e7a79818..4fba2c5aa 100644 --- a/Ix/Kernel/Verify/Consistency/BetaSpine.lean +++ b/Ix/Kernel/Verify/Consistency/BetaSpine.lean @@ -82,73 +82,6 @@ theorem SynthesisInference.beta_peel_sound {β : Type u} have result := (leading.truncate peeling.length_bound).beta_sound typed spine rwa [peeling.betaPrefix trailing] at result -/-- The actual beta step reads as a model beta prefix. This representation -fact depends on the executed peel, substitution and suffix reconstruction; -it applies equally to source expressions and generated intermediate terms. -/ -theorem beta_many_step_readScopedExpr? {β : Type u} - {resolve : Address → Option (ConstRef β)} {locals : List FVarId} - {rawFunction rawArgument : KExpr .anon} {appInfo : ExprInfo .anon} - {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} - {rawDomain rawInner rawBody : KExpr .anon} {lambdaInfo : ExprInfo .anon} - {rawArguments consumed : Array (KExpr .anon)} - {condition : Certified.PropWhen} {domain inner : AExpr β} {arguments : List (AExpr β)} - (spine : (KExpr.app rawFunction rawArgument appInfo).collectSpine = - (.lam name bi rawDomain rawInner lambdaInfo, rawArguments)) - (headReads : readScopedExpr? resolve locals (.lam name bi rawDomain rawInner lambdaInfo) = - some (AExpr.lam condition domain inner).erase) - (argumentReads : rawArguments.toList.map (readScopedExpr? resolve locals ·) = - arguments.map (some ·.erase)) - (peeling : RecM.consumeBetaLams (.lam name bi rawDomain rawInner lambdaInfo) rawArguments = - (rawBody, consumed)) - (nonempty : (!consumed.isEmpty) = true) - (before : TcState .anon) (reductionFuel : Nat) (flags : WhnfFlags) - (walkerBounds : SimulSubstBounds rawBody consumed.reverse 0) - (coherent : before.env.intern.WF) - (walkerFaithful : KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ - KExpr.SimulSubstReach consumed.reverse rawBody 0 term) - (suffixFaithful : KExpr.CollisionFree fun term => - (simulSubst rawBody consumed.reverse 0 before.env.intern).2.ExprSupport term ∨ - term ∈ cheapBetaChainList (simulSubst rawBody consumed.reverse 0 before.env.intern).1 - (rawArguments.extract consumed.size rawArguments.size).toList) : - ∃ (result : KExpr .anon) (after : TcState .anon), - (RecM.whnfCoreWithFlagsStep (.app rawFunction rawArgument appInfo) flags).run - (methodsN (reductionFuel + 1)) before = .ok (.next result) after ∧ - readScopedExpr? resolve locals result = - some (AExpr.betaPrefix consumed.size (.lam condition domain inner) arguments).erase ∧ - consumed.size ≤ inner.lambdaDepth + 1 ∧ after.env.intern.WF := by - obtain ⟨rawPeel, consumedPrefix, consumedBound⟩ := RecM.BetaPeel.of_consume peeling - obtain ⟨body, modelPeel, bodyReads⟩ := betaPeel_readScopedExpr? rawPeel headReads - have sizeAgrees : rawArguments.size = arguments.length := by - have lengths := congrArg List.length argumentReads - simpa using lengths - have consumedSize : (arguments.take consumed.size).length = consumed.size := by - simp only [List.length_take] - omega - have consumedReads : consumed.toList.map (readScopedExpr? resolve locals ·) = - (arguments.take consumed.size).map (some ·.erase) := by - rw [consumedPrefix, List.map_take, argumentReads, List.map_take] - have suffixReads : (rawArguments.extract consumed.size rawArguments.size).toList.map - (readScopedExpr? resolve locals ·) = (arguments.drop consumed.size).map (some ·.erase) := by - rw [RecM.BetaPeel.remaining_eq_drop peeling, List.map_drop, argumentReads, List.map_drop] - obtain ⟨walkReads, walkCoherent⟩ := simulSubst_readScopedExpr? - (by simpa only [Array.size_reverse] using consumedSize.symm) - walkerBounds.1 walkerBounds.2.1 (by simpa using walkerBounds.2.2.2.1) - walkerBounds.2.2.1 coherent walkerFaithful - (by simpa only [Nat.zero_add, Array.length_toList, consumedSize] using bodyReads) - (argumentsReading_reverse_get consumedReads consumedSize.symm) - have peelingMeaning := LambdaPeel.betaPrefix (arguments := arguments.take consumed.size) - (by simpa only [consumedSize, Array.length_toList] using modelPeel) (arguments.drop consumed.size) - simp only [consumedSize, List.take_append_drop] at peelingMeaning - let walk := simulSubst rawBody consumed.reverse 0 before.env.intern - let middle := { before with env := { before.env with intern := walk.2 } } - obtain ⟨result, after, finish, resultReads, preserved⟩ := - finishAppResult_readScopedExpr? (before := middle) walkCoherent suffixFaithful walkReads suffixReads - (methodsN (reductionFuel + 1)) - refine ⟨result, after, ?_, ?_, ?_, preserved⟩ - · exact RecM.whnfCoreWithFlagsStep_betaMany spine rfl peeling nonempty rfl finish - · simpa only [peelingMeaning] using resultReads - · simpa only [Array.length_toList, AExpr.lambdaDepth] using modelPeel.length_bound - /-- The production multi-argument beta step preserves typing and model meaning. Its peeled body and consumed argument order come from the actual loop, and its suffix is rebuilt by the actual interned application chain. -/ diff --git a/Ix/Kernel/Verify/Consistency/BetaTrace.lean b/Ix/Kernel/Verify/Consistency/BetaTrace.lean index f492e9c9a..015c209bc 100644 --- a/Ix/Kernel/Verify/Consistency/BetaTrace.lean +++ b/Ix/Kernel/Verify/Consistency/BetaTrace.lean @@ -28,6 +28,7 @@ def SynthesisTypingOrigin.applySpine {β : Type u} {resolve : Address → Option | .snoc prior checked => by simpa only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] using (origin.applySpine prior).application checked + | .convert prior trace => .convert (origin.applySpine prior) trace termination_by structural spine /-- A function reduction carries its original dependent argument checks @@ -45,6 +46,7 @@ def SynthesisBetaTrace.applySpine {β : Type u} {resolve : Address → Option (C | .snoc prior checked => by simpa only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] using (trace.applySpine prior).application checked + | .convert prior typeTrace => .convertType (trace.applySpine prior) typeTrace termination_by structural spine /-- One beta step may use a lambda produced by an earlier trace. Its @@ -162,6 +164,17 @@ def SynthesisInference.spineOrigin {β : Type u} {resolve : Address → Option ( simpa only [← parts.1] using prior.argumentsOrigin.snoc (.source (.applicationArgument contextOrigin trace functionTree argumentTree keyedAgreement functionReading argumentReading conditions hashPath comparisonFaithful)) + | .appBeta _ miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath + comparisonFaithful _ _ _ _ _ _ => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have parts := app_spine_parts empty same + have prior := functionTree.spineOrigin contextOrigin keyedAgreement functionReading trace.functionRun + head arguments.dropLast parts.2 + refine ⟨prior.headType, prior.headOrigin, prior.leading, ?_⟩ + simpa only [← parts.1] using (prior.argumentsOrigin.convert (.rebase contextOrigin reduction)).snoc + (.source (.applicationBetaArgument contextOrigin trace functionTree exposure exposureCoherent argumentTree + keyedAgreement functionReading argumentReading conditions hashPath comparisonFaithful)) | .fvar .. | .forallE .. | .lam .. | .lamBeta .. => by exact False.elim (empty (nonapp_spine_empty (by intro fn arg same; cases same) same)) termination_by structural support @@ -192,46 +205,6 @@ def SynthesisInference.betaSpineTrace {β : Type u} {resolve : Address → Optio (head.appN arguments) (AExpr.betaPrefix count head arguments) type := (support.spineOrigin contextOrigin agreement reading accepted head arguments rfl).betaTrace enough -private theorem betaPrefix_eq_of_not_app {β : Type u} {head : AExpr β} - {arguments : List (AExpr β)} {count : Nat} - (notApp : ∀ fn arg, head.appN arguments ≠ .app fn arg) : - AExpr.betaPrefix count head arguments = head.appN arguments := by - cases arguments with - | nil => cases count <;> cases head <;> rfl - | cons argument arguments => - exact False.elim (notApp _ _ (appN_last (by simp))) - -/-- These traces reduce applications and their subapplications. They do -not change a source whose outer constructor is a lambda, product, or atom. -In particular, a forward beta conversion cannot change a lambda's product type. -/ -theorem SynthesisBetaTrace.rigid {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {source result type : AExpr β} - (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type) : - (∀ fn arg, source ≠ .app fn arg) → result = source := - match trace with - | .refl _ => fun _ => rfl - | .prefix .. | .origin .. => fun notApp => betaPrefix_eq_of_not_app notApp - | .trans prior next => fun notApp => by - have middle := prior.rigid notApp - exact (next.rigid (by simpa only [middle] using notApp)).trans middle - | .atType _ trace => trace.rigid - | .application .. | .argument .. => fun notApp => False.elim (notApp _ _ rfl) - | .substituteAt trace _ _ => fun notApp => by - have same := trace.rigid (by - intro fn arg equal - cases equal - exact notApp _ _ rfl) - rw [same] - | .weakenAt trace _ => fun notApp => by - have same := trace.rigid (by - intro fn arg equal - cases equal - exact notApp _ _ rfl) - rw [same] - | .rebase _ trace => trace.rigid -termination_by structural trace - namespace BetaSyntax /-- One leftmost beta contraction, retaining the application suffix. diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnf.lean b/Ix/Kernel/Verify/Consistency/BetaWhnf.lean index 6a27b7f3b..7be739881 100644 --- a/Ix/Kernel/Verify/Consistency/BetaWhnf.lean +++ b/Ix/Kernel/Verify/Consistency/BetaWhnf.lean @@ -4,6 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.BetaTrace +import Ix.Kernel.Verify.Consistency.BetaWhnfPlan /-! Finite beta traces for the actual structural-WHNF loop. Every next state is computed by production's simultaneous substitution and suffix @@ -15,101 +16,6 @@ open Theory Theory.Model universe u v -/-- Resources for one actual beta iteration. The raw output and next -state are computed below; no typing, reduction origin, or result reading is a field. -/ -structure BetaStepPlan {β : Type u} (resolve : Address → Option (ConstRef β)) (locals : List FVarId) - (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) where - rawFunction : KExpr .anon - rawArgument : KExpr .anon - appInfo : ExprInfo .anon - sourceEq : source = .app rawFunction rawArgument appInfo - name : Mode.anon.F Name - bi : Mode.anon.F Lean.BinderInfo - rawDomain : KExpr .anon - rawInner : KExpr .anon - lambdaInfo : ExprInfo .anon - rawArguments : Array (KExpr .anon) - rawBody : KExpr .anon - consumed : Array (KExpr .anon) - condition : Certified.PropWhen - domain : AExpr β - inner : AExpr β - arguments : List (AExpr β) - modelSource : term = (AExpr.lam condition domain inner).appN arguments - spine : (KExpr.app rawFunction rawArgument appInfo).collectSpine = - (.lam name bi rawDomain rawInner lambdaInfo, rawArguments) - headReads : readScopedExpr? resolve locals (.lam name bi rawDomain rawInner lambdaInfo) = - some (AExpr.lam condition domain inner).erase - argumentReads : rawArguments.toList.map (readScopedExpr? resolve locals ·) = arguments.map (some ·.erase) - peeling : RecM.consumeBetaLams (.lam name bi rawDomain rawInner lambdaInfo) rawArguments = (rawBody, consumed) - nonempty : (!consumed.isEmpty) = true - walkerBounds : SimulSubstBounds rawBody consumed.reverse 0 - walkerFaithful : KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ - KExpr.SimulSubstReach consumed.reverse rawBody 0 term - suffixFaithful : KExpr.CollisionFree fun term => - (simulSubst rawBody consumed.reverse 0 before.env.intern).2.ExprSupport term ∨ - term ∈ cheapBetaChainList (simulSubst rawBody consumed.reverse 0 before.env.intern).1 - (rawArguments.extract consumed.size rawArguments.size).toList - -namespace BetaStepPlan - -variable {β : Type u} {resolve : Address → Option (ConstRef β)} - {locals : List FVarId} {before : TcState .anon} {source : KExpr .anon} {term : AExpr β} - -def output (step : BetaStepPlan resolve locals before source term) : - KExpr .anon × InternTable .anon := - let walk := simulSubst step.rawBody step.consumed.reverse 0 before.env.intern - internAppChain walk.1 (step.rawArguments.extract step.consumed.size step.rawArguments.size).toList walk.2 - -def result (step : BetaStepPlan resolve locals before source term) : - KExpr .anon := step.output.1 - -def after (step : BetaStepPlan resolve locals before source term) : - TcState .anon := { before with env := { before.env with intern := step.output.2 } } - -def modelResult (step : BetaStepPlan resolve locals before source term) : - AExpr β := AExpr.betaPrefix step.consumed.size (.lam step.condition step.domain step.inner) step.arguments - -theorem run (step : BetaStepPlan resolve locals before source term) - (reductionFuel : Nat) (flags : WhnfFlags) : - (RecM.whnfCoreWithFlagsStep source flags).run (methodsN (reductionFuel + 1)) before = - .ok (.next step.result) step.after := by - simp only [step.sourceEq] - apply RecM.whnfCoreWithFlagsStep_betaMany step.spine rfl step.peeling step.nonempty rfl - rw [RecM.finishAppResult_eq_internAppChain] - rfl - -theorem sourceReading - (step : BetaStepPlan resolve locals before source term) : - readScopedExpr? resolve locals source = some term.erase := by - simp only [step.sourceEq, step.modelSource] - exact readScopedExpr?_collectSpine step.spine step.headReads step.argumentReads - -theorem reading - (step : BetaStepPlan resolve locals before source term) - (coherent : before.env.intern.WF) : - readScopedExpr? resolve locals step.result = some step.modelResult.erase ∧ step.after.env.intern.WF := by - obtain ⟨result, after, run, reading, _, preserved⟩ := beta_many_step_readScopedExpr? - step.spine step.headReads step.argumentReads step.peeling step.nonempty before 0 .FULL - step.walkerBounds coherent step.walkerFaithful step.suffixFaithful - have actual := step.run 0 .FULL - simp only [step.sourceEq] at actual - rw [actual] at run - cases run - exact ⟨reading, preserved⟩ - -theorem counts (plan : BetaStepPlan resolve locals before source term) : - plan.consumed.size ≤ plan.inner.lambdaDepth + 1 ∧ plan.consumed.size ≤ plan.arguments.length := by - obtain ⟨peeled, _, rawBound⟩ := RecM.BetaPeel.of_consume plan.peeling - obtain ⟨_, modelPeel, _⟩ := betaPeel_readScopedExpr? peeled plan.headReads - have sizeAgrees : plan.rawArguments.size = plan.arguments.length := by - have lengths := congrArg List.length plan.argumentReads - simpa using lengths - exact ⟨by simpa only [Array.length_toList, AExpr.lambdaDepth] using modelPeel.length_bound, - sizeAgrees ▸ rawBound⟩ - -end BetaStepPlan - /-- A raw beta plan together with its derived source-checking evidence. -/ structure SynthesisBetaStep {β : Type u} (resolve : Address → Option (ConstRef β)) (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean b/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean index a1d5af338..9966912da 100644 --- a/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean +++ b/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean @@ -32,21 +32,6 @@ def BetaStepPlan.betaTyping {β : Type u} {resolve : Address → Option (ConstRe _ = plan.modelResult := BetaSyntax.steps_betaPrefix _ _ _ plan.counts.1 plan.counts.2 exact same ▸ typing.betaSteps plan.consumed.size -/-- A finite production path without semantic step origins. All intermediate -expressions and states are computed by its raw beta plans. -/ -inductive BetaWhnfTrace {β : Type u} (resolve : Address → Option (ConstRef β)) - (locals : List FVarId) (reductionFuel : Nat) (flags : WhnfFlags) : - Nat → TcState .anon → KExpr .anon → AExpr β → TcState .anon → KExpr .anon → AExpr β → Type u - | done {before source term} - (finished : (RecM.whnfCoreWithFlagsStep source flags).run (methodsN (reductionFuel + 1)) before = - .ok (.done source) before) : - BetaWhnfTrace resolve locals reductionFuel flags 0 before source term before source term - | next {steps before after source result term target} - (plan : BetaStepPlan resolve locals before source term) - (rest : BetaWhnfTrace resolve locals reductionFuel flags - steps plan.after plan.result plan.modelResult after result target) : - BetaWhnfTrace resolve locals reductionFuel flags (steps + 1) before source term after result target - def BetaWhnfTrace.annotate {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} {incomingBounds : List VLevel} {locals : List FVarId} {reductionFuel steps : Nat} {flags : WhnfFlags} @@ -66,6 +51,50 @@ def BetaWhnfTrace.annotate {β : Type u} {resolve : Address → Option (ConstRef ⟨.next step remaining.1, remaining.2⟩ termination_by structural trace +/-- Annotating the raw public path derives its semantic trace from the +retained checking derivation, including every later beta step. -/ +def BetaPublicWhnfPlan.betaTrace {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target type : AExpr β} + (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term target type := + (plan.path.annotate typing).1.toBetaTrace typing.origin + +/-- Cache reuse retains the originating path's checked meaning. The +current cache lookup establishes which exact raw Pi result is returned. -/ +def BetaPiExposure.betaTrace {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source rawDomain rawBody : KExpr .anon} {term domain body type : AExpr β} {condition : Certified.PropWhen} + (exposure : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody) + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + term (.forallE condition domain body) type := + match exposure with + | .reduce plan => plan.betaTrace typing + | .cached origin _ _ => origin.betaTrace typing + +/-- An actual earlier check of the function type supplies the conversion +field of application inference. No check of the generated Pi is needed. -/ +def BetaPiExposure.checkedTrace {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {locals typeLocals : List FVarId} {fuel typeFuel : Nat} + {before typeBefore typeAfter : TcState .anon} {source rawDomain rawBody typeSource typeResult : KExpr .anon} + {term domain body : AExpr β} {condition : Certified.PropWhen} {level bound : VLevel} + (exposure : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody) + (typeTree : SynthesisInference resolve entries typeLocals context bounds typeFuel typeBefore typeSource + term (.sort level) bound) + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (agreement : LocalContextReading resolve typeLocals typeBefore.lctx context) + (reading : readScopedExpr? resolve typeLocals typeSource = some term.erase) + (accepted : RecM.infer typeSource (methodsN typeFuel) typeBefore = .ok typeResult typeAfter) + (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + term (.forallE condition domain body) (.sort level) := + exposure.betaTrace (typeTree.betaTyping contextOrigin agreement reading accepted formed) + /-- The original inference supplies every later lambda and argument origin. Only the operational WHNF path and finite representation resources remain as inputs to the actual uncached-loop refinement. -/ @@ -89,4 +118,27 @@ theorem SynthesisInference.beta_whnf_sound {β : Type u} {resolve : Address → let typing := support.betaTyping .current agreement reading accepted formed exact (path.annotate typing).1.uncached_sound typing.origin formed reading coherent enough +/-- The public WHNF call includes instrumentation, fuel, and all three +cache layers. Its beta result has the type derived by the original check. -/ +theorem SynthesisInference.beta_public_whnf_sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} {locals : List FVarId} + {fuel : Nat} {inferenceBefore inferenceAfter : TcState .anon} {source inferred : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel inferenceBefore source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals inferenceBefore.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) + {reductionFuel : Nat} {before : TcState .anon} {result : KExpr .anon} {target : AExpr β} + (plan : BetaPublicWhnfPlan resolve locals reductionFuel before source term result target) + (coherent : before.env.intern.WF) : + (RecM.whnf source).run (methodsN (reductionFuel + 1)) before = .ok result plan.after ∧ + readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ TypingClaim.{u,v} entries context target type ∧ + plan.after.env.intern.WF := by + obtain ⟨resultReading, preserved⟩ := plan.reading reading coherent + obtain ⟨converted, typed⟩ := + (plan.betaTrace (support.betaTyping .current agreement reading accepted formed)).sound formed + exact ⟨plan.run, resultReading, converted, typed, preserved⟩ + end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnfPlan.lean b/Ix/Kernel/Verify/Consistency/BetaWhnfPlan.lean new file mode 100644 index 000000000..940869d19 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaWhnfPlan.lean @@ -0,0 +1,182 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.SpineReading + +/-! Raw beta paths compute production results and states independently of +the source-inference and semantic-origin derivations. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- Resources for one actual beta iteration. The raw output and next +state are computed below; no typing, reduction origin, or result reading is a field. -/ +structure BetaStepPlan {β : Type u} (resolve : Address → Option (ConstRef β)) (locals : List FVarId) + (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) where + rawFunction : KExpr .anon + rawArgument : KExpr .anon + appInfo : ExprInfo .anon + sourceEq : source = .app rawFunction rawArgument appInfo + name : Mode.anon.F Name + bi : Mode.anon.F Lean.BinderInfo + rawDomain : KExpr .anon + rawInner : KExpr .anon + lambdaInfo : ExprInfo .anon + rawArguments : Array (KExpr .anon) + rawBody : KExpr .anon + consumed : Array (KExpr .anon) + condition : Certified.PropWhen + domain : AExpr β + inner : AExpr β + arguments : List (AExpr β) + modelSource : term = (AExpr.lam condition domain inner).appN arguments + spine : (KExpr.app rawFunction rawArgument appInfo).collectSpine = + (.lam name bi rawDomain rawInner lambdaInfo, rawArguments) + headReads : readScopedExpr? resolve locals (.lam name bi rawDomain rawInner lambdaInfo) = + some (AExpr.lam condition domain inner).erase + argumentReads : rawArguments.toList.map (readScopedExpr? resolve locals ·) = arguments.map (some ·.erase) + peeling : RecM.consumeBetaLams (.lam name bi rawDomain rawInner lambdaInfo) rawArguments = (rawBody, consumed) + nonempty : (!consumed.isEmpty) = true + walkerBounds : SimulSubstBounds rawBody consumed.reverse 0 + walkerFaithful : KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ + KExpr.SimulSubstReach consumed.reverse rawBody 0 term + suffixFaithful : KExpr.CollisionFree fun term => + (simulSubst rawBody consumed.reverse 0 before.env.intern).2.ExprSupport term ∨ + term ∈ cheapBetaChainList (simulSubst rawBody consumed.reverse 0 before.env.intern).1 + (rawArguments.extract consumed.size rawArguments.size).toList + +namespace BetaStepPlan + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {before : TcState .anon} {source : KExpr .anon} {term : AExpr β} + +def output (step : BetaStepPlan resolve locals before source term) : + KExpr .anon × InternTable .anon := + let walk := simulSubst step.rawBody step.consumed.reverse 0 before.env.intern + internAppChain walk.1 (step.rawArguments.extract step.consumed.size step.rawArguments.size).toList walk.2 + +def result (step : BetaStepPlan resolve locals before source term) : + KExpr .anon := step.output.1 + +def after (step : BetaStepPlan resolve locals before source term) : + TcState .anon := { before with env := { before.env with intern := step.output.2 } } + +def modelResult (step : BetaStepPlan resolve locals before source term) : + AExpr β := AExpr.betaPrefix step.consumed.size (.lam step.condition step.domain step.inner) step.arguments + +theorem run (step : BetaStepPlan resolve locals before source term) + (reductionFuel : Nat) (flags : WhnfFlags) : + (RecM.whnfCoreWithFlagsStep source flags).run (methodsN (reductionFuel + 1)) before = + .ok (.next step.result) step.after := by + simp only [step.sourceEq] + apply RecM.whnfCoreWithFlagsStep_betaMany step.spine rfl step.peeling step.nonempty rfl + rw [RecM.finishAppResult_eq_internAppChain] + rfl + +theorem sourceReading + (step : BetaStepPlan resolve locals before source term) : + readScopedExpr? resolve locals source = some term.erase := by + simp only [step.sourceEq, step.modelSource] + exact readScopedExpr?_collectSpine step.spine step.headReads step.argumentReads + +theorem reading + (step : BetaStepPlan resolve locals before source term) + (coherent : before.env.intern.WF) : + readScopedExpr? resolve locals step.result = some step.modelResult.erase ∧ step.after.env.intern.WF := by + obtain ⟨result, after, run, reading, _, preserved⟩ := beta_many_step_readScopedExpr? + step.spine step.headReads step.argumentReads step.peeling step.nonempty before 0 .FULL + step.walkerBounds coherent step.walkerFaithful step.suffixFaithful + have actual := step.run 0 .FULL + simp only [step.sourceEq] at actual + rw [actual] at run + cases run + exact ⟨reading, preserved⟩ + +theorem counts (plan : BetaStepPlan resolve locals before source term) : + plan.consumed.size ≤ plan.inner.lambdaDepth + 1 ∧ plan.consumed.size ≤ plan.arguments.length := by + obtain ⟨peeled, _, rawBound⟩ := RecM.BetaPeel.of_consume plan.peeling + obtain ⟨_, modelPeel, _⟩ := betaPeel_readScopedExpr? peeled plan.headReads + have sizeAgrees : plan.rawArguments.size = plan.arguments.length := by + have lengths := congrArg List.length plan.argumentReads + simpa using lengths + exact ⟨by simpa only [Array.length_toList, AExpr.lambdaDepth] using modelPeel.length_bound, + sizeAgrees ▸ rawBound⟩ + +end BetaStepPlan + +/-- A finite production path without semantic step origins. All intermediate +expressions and states are computed by its raw beta plans. -/ +inductive BetaWhnfTrace {β : Type u} (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (reductionFuel : Nat) (flags : WhnfFlags) : + Nat → TcState .anon → KExpr .anon → AExpr β → TcState .anon → KExpr .anon → AExpr β → Type u + | done {before source term} + (finished : (RecM.whnfCoreWithFlagsStep source flags).run (methodsN (reductionFuel + 1)) before = + .ok (.done source) before) : + BetaWhnfTrace resolve locals reductionFuel flags 0 before source term before source term + | next {steps before after source result term target} + (plan : BetaStepPlan resolve locals before source term) + (rest : BetaWhnfTrace resolve locals reductionFuel flags + steps plan.after plan.result plan.modelResult after result target) : + BetaWhnfTrace resolve locals reductionFuel flags (steps + 1) before source term after result target + +namespace BetaWhnfTrace + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {reductionFuel steps : Nat} {flags : WhnfFlags} {before after : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + +theorem run + (trace : BetaWhnfTrace resolve locals reductionFuel flags steps before source term after result target) + {loopFuel : Nat} (enough : steps < loopFuel) : + (RecM.runBounded (fun current => RecM.whnfCoreWithFlagsStep current flags) loopFuel source).run + (methodsN (reductionFuel + 1)) before = .ok result after := by + induction trace generalizing loopFuel with + | done finished => + cases loopFuel with + | zero => omega + | succ loopFuel => + rw [RecM.runBounded, ReaderT.run_bind] + change EStateM.bind ((RecM.whnfCoreWithFlagsStep _ flags).run _) _ _ = _ + unfold EStateM.bind + rw [finished] + rfl + | next step rest ih => + cases loopFuel with + | zero => omega + | succ loopFuel => + rw [RecM.runBounded, ReaderT.run_bind] + change EStateM.bind ((RecM.whnfCoreWithFlagsStep _ flags).run _) _ _ = _ + unfold EStateM.bind + rw [step.run reductionFuel flags] + exact ih (by omega) + +theorem reading + (trace : BetaWhnfTrace resolve locals reductionFuel flags steps before source term after result target) + (sourceReading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) : + readScopedExpr? resolve locals result = some target.erase ∧ after.env.intern.WF := by + induction trace with + | done => exact ⟨sourceReading, coherent⟩ + | next step rest ih => + obtain ⟨reading, preserved⟩ := step.reading coherent + exact ih reading preserved + +/-- A beta path changes only the intern table. All cache partitions, +locals, checking policies, and instrumentation fields retain their values. -/ +theorem frame + (trace : BetaWhnfTrace resolve locals reductionFuel flags steps before source term after result target) : + ∃ table, after = { before with env := { before.env with intern := table } } := by + induction trace with + | done => exact ⟨_, rfl⟩ + | next step rest ih => + obtain ⟨table, same⟩ := ih + exact ⟨table, same⟩ + +end BetaWhnfTrace + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SpineReading.lean b/Ix/Kernel/Verify/Consistency/SpineReading.lean index cbe2553ff..30f852238 100644 --- a/Ix/Kernel/Verify/Consistency/SpineReading.lean +++ b/Ix/Kernel/Verify/Consistency/SpineReading.lean @@ -279,4 +279,72 @@ theorem finishAppResult_readScopedExpr? {β : Type u} rw [RecM.finishAppResult_eq_internAppChain] rfl +/-- The actual beta step reads as a model beta prefix. This representation +fact depends on the executed peel, substitution and suffix reconstruction; +it applies equally to source expressions and generated intermediate terms. -/ +theorem beta_many_step_readScopedExpr? {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {rawFunction rawArgument : KExpr .anon} {appInfo : ExprInfo .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {rawDomain rawInner rawBody : KExpr .anon} {lambdaInfo : ExprInfo .anon} + {rawArguments consumed : Array (KExpr .anon)} + {condition : Certified.PropWhen} {domain inner : AExpr β} {arguments : List (AExpr β)} + (spine : (KExpr.app rawFunction rawArgument appInfo).collectSpine = + (.lam name bi rawDomain rawInner lambdaInfo, rawArguments)) + (headReads : readScopedExpr? resolve locals (.lam name bi rawDomain rawInner lambdaInfo) = + some (AExpr.lam condition domain inner).erase) + (argumentReads : rawArguments.toList.map (readScopedExpr? resolve locals ·) = + arguments.map (some ·.erase)) + (peeling : RecM.consumeBetaLams (.lam name bi rawDomain rawInner lambdaInfo) rawArguments = + (rawBody, consumed)) + (nonempty : (!consumed.isEmpty) = true) + (before : TcState .anon) (reductionFuel : Nat) (flags : WhnfFlags) + (walkerBounds : SimulSubstBounds rawBody consumed.reverse 0) + (coherent : before.env.intern.WF) + (walkerFaithful : KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ + KExpr.SimulSubstReach consumed.reverse rawBody 0 term) + (suffixFaithful : KExpr.CollisionFree fun term => + (simulSubst rawBody consumed.reverse 0 before.env.intern).2.ExprSupport term ∨ + term ∈ cheapBetaChainList (simulSubst rawBody consumed.reverse 0 before.env.intern).1 + (rawArguments.extract consumed.size rawArguments.size).toList) : + ∃ (result : KExpr .anon) (after : TcState .anon), + (RecM.whnfCoreWithFlagsStep (.app rawFunction rawArgument appInfo) flags).run + (methodsN (reductionFuel + 1)) before = .ok (.next result) after ∧ + readScopedExpr? resolve locals result = + some (AExpr.betaPrefix consumed.size (.lam condition domain inner) arguments).erase ∧ + consumed.size ≤ inner.lambdaDepth + 1 ∧ after.env.intern.WF := by + obtain ⟨rawPeel, consumedPrefix, consumedBound⟩ := RecM.BetaPeel.of_consume peeling + obtain ⟨body, modelPeel, bodyReads⟩ := betaPeel_readScopedExpr? rawPeel headReads + have sizeAgrees : rawArguments.size = arguments.length := by + have lengths := congrArg List.length argumentReads + simpa using lengths + have consumedSize : (arguments.take consumed.size).length = consumed.size := by + simp only [List.length_take] + omega + have consumedReads : consumed.toList.map (readScopedExpr? resolve locals ·) = + (arguments.take consumed.size).map (some ·.erase) := by + rw [consumedPrefix, List.map_take, argumentReads, List.map_take] + have suffixReads : (rawArguments.extract consumed.size rawArguments.size).toList.map + (readScopedExpr? resolve locals ·) = (arguments.drop consumed.size).map (some ·.erase) := by + rw [RecM.BetaPeel.remaining_eq_drop peeling, List.map_drop, argumentReads, List.map_drop] + obtain ⟨walkReads, walkCoherent⟩ := simulSubst_readScopedExpr? + (by simpa only [Array.size_reverse] using consumedSize.symm) + walkerBounds.1 walkerBounds.2.1 (by simpa using walkerBounds.2.2.2.1) + walkerBounds.2.2.1 coherent walkerFaithful + (by simpa only [Nat.zero_add, Array.length_toList, consumedSize] using bodyReads) + (argumentsReading_reverse_get consumedReads consumedSize.symm) + have peelingMeaning := LambdaPeel.betaPrefix (arguments := arguments.take consumed.size) + (by simpa only [consumedSize, Array.length_toList] using modelPeel) (arguments.drop consumed.size) + simp only [consumedSize, List.take_append_drop] at peelingMeaning + let walk := simulSubst rawBody consumed.reverse 0 before.env.intern + let middle := { before with env := { before.env with intern := walk.2 } } + obtain ⟨result, after, finish, resultReads, preserved⟩ := + finishAppResult_readScopedExpr? (before := middle) walkCoherent suffixFaithful walkReads suffixReads + (methodsN (reductionFuel + 1)) + refine ⟨result, after, ?_, ?_, ?_, preserved⟩ + · exact RecM.whnfCoreWithFlagsStep_betaMany spine rfl peeling nonempty rfl finish + · simpa only [peelingMeaning] using resultReads + · simpa only [Array.length_toList, AExpr.lambdaDepth] using modelPeel.length_bound + + end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean index 6c4fc15e8..66eba0eaf 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean @@ -5,6 +5,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Verify.Consistency.ContextInsertion import Ix.Kernel.Verify.Consistency.CheapBetaReading +import Ix.Kernel.Verify.Consistency.ApplicationWhnf import Ix.Theory.Model.UniverseBounds import Ix.Theory.Model.BetaSpine @@ -74,6 +75,32 @@ inductive SynthesisInference {β : Type u} KExpr.SubstReach arg trace.codomain 0 term) : SynthesisInference resolve entries locals context bounds (fuel + 1) before (.app fn arg info) (.app f a) (B.inst a) (applicationLevel functionLevel condition) + | appBeta {entries locals context bounds fuel before fn arg info f a T A A' B condition + functionLevel argumentLevel reductionLevel} + (full : before.inferOnly = false) + (miss : UncachedInference before (.app fn arg info)) + (trace : ApplicationWhnfInferenceTrace fuel miss.keyed fn arg) + (functionTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed fn + f T functionLevel) + (exposure : BetaPiExposure resolve locals fuel trace.functionState trace.functionType T + condition A B trace.domain trace.codomain) + (exposureCoherent : trace.functionState.env.intern.WF) + (reduction : SynthesisBetaTrace resolve entries context bounds entries context + T (.forallE condition A B) (.sort reductionLevel)) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.exposedState + arg a A' argumentLevel) + (conditions : A'.annotations = A.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) + (bodyConstructed : trace.codomain.Constructed) + (argConstructed : arg.Constructed) + (bodyBound : trace.codomain.size + 1 < UInt64.size) + (argBound : arg.size < UInt64.size) + (coherent : trace.comparedState.env.intern.WF) + (faithful : KExpr.CollisionFree fun term => trace.comparedState.env.intern.ExprSupport term ∨ + KExpr.SubstReach arg trace.codomain 0 term) : + SynthesisInference resolve entries locals context bounds (fuel + 1) before (.app fn arg info) + (.app f a) (B.inst a) (applicationLevel reductionLevel condition) | forallE {entries locals context bounds fuel before name bi domain body info A B domainBoundLevel bodyBoundLevel} (miss : UncachedInference before (.all name bi domain body info)) (trace : ForallInferenceTrace fuel miss.keyed name bi domain body) @@ -317,6 +344,23 @@ inductive SynthesisCheckedOrigin {β : Type u} (resolve : Address → Option (Co (hashPath : (trace.argumentType == trace.domain) = true) (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context a domain + | applicationBetaArgument {incoming incomingContext incomingBounds entries context bounds locals fuel before fn arg + f a T domain argumentType body condition functionLevel argumentLevel} + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (trace : ApplicationWhnfInferenceTrace fuel before fn arg) + (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn f T functionLevel) + (exposure : BetaPiExposure resolve locals fuel trace.functionState trace.functionType T + condition domain body trace.domain trace.codomain) + (exposureCoherent : trace.functionState.env.intern.WF) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.exposedState arg + a argumentType argumentLevel) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals arg = some a.erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) : + SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context a domain | binderArgument {incoming incomingContext incomingBounds entries context locals fuel before fn arg f a domain argumentType body condition} (trace : ApplicationInferenceTrace fuel before fn arg) @@ -345,6 +389,11 @@ inductive SynthesisArgumentSpineOrigin {β : Type u} (resolve : Address → Opti (checked : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context argument domain) : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context start (arguments ++ [argument]) (body.inst argument) + | convert {incoming incomingContext incomingBounds entries context start arguments source target level} + (prior : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context + start arguments source) + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source target (.sort level)) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context start arguments target /-- A beta reduction can come from an earlier checked lambda prefix, or from an actual lambda or lambda application substituted for a checked @@ -486,9 +535,79 @@ inductive SynthesisBetaTrace {β : Type u} (resolve : Address → Option (ConstR (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) (trace : SynthesisBetaTrace resolve middle middleContext middleBounds entries context source result type) : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type + | convertType {incoming incomingContext incomingBounds entries context source result sourceType targetType level} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result sourceType) + (typeTrace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + sourceType targetType (.sort level)) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result targetType + | instantiate {incoming incomingContext incomingBounds entries context source result type} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type) + (arguments : List VLevel) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries (context.map (AExpr.instL arguments)) + (source.instL arguments) (result.instL arguments) (type.instL arguments) + | appendContext {incoming incomingContext incomingBounds entries context source result type} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type) + (outer : Model.Context β) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries (context ++ outer) source result type + | extend {incoming incomingContext incomingBounds earlier entries context source result type} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds earlier context source result type) + (extension : InterfaceExtends earlier entries) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type end +private theorem trace_appN_last {β : Type u} {head : AExpr β} {arguments : List (AExpr β)} + (nonempty : arguments ≠ []) : + head.appN arguments = (head.appN arguments.dropLast).app (arguments.getLast nonempty) := by + calc + head.appN arguments = head.appN (arguments.dropLast ++ [arguments.getLast nonempty]) := + congrArg (AExpr.appN head) (List.dropLast_concat_getLast nonempty).symm + _ = _ := by simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] + +private theorem betaPrefix_eq_of_not_app {β : Type u} {head : AExpr β} + {arguments : List (AExpr β)} {count : Nat} + (notApp : ∀ fn arg, head.appN arguments ≠ .app fn arg) : + AExpr.betaPrefix count head arguments = head.appN arguments := by + cases arguments with + | nil => cases count <;> cases head <;> rfl + | cons argument arguments => + exact False.elim (notApp _ _ (trace_appN_last (by simp))) + +/-- These traces reduce applications and their subapplications. They do +not change a source whose outer constructor is a lambda, product, or atom. +In particular, a forward beta conversion cannot change a lambda's product type. -/ +theorem SynthesisBetaTrace.rigid {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {source result type : AExpr β} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type) : + (∀ fn arg, source ≠ .app fn arg) → result = source := + match trace with + | .refl _ => fun _ => rfl + | .prefix .. | .origin .. => fun notApp => betaPrefix_eq_of_not_app notApp + | .trans prior next => fun notApp => by + have middle := prior.rigid notApp + exact (next.rigid (by simpa only [middle] using notApp)).trans middle + | .atType _ trace => trace.rigid + | .application .. | .argument .. => fun notApp => False.elim (notApp _ _ rfl) + | .substituteAt trace _ _ => fun notApp => by + have same := trace.rigid (by + intro fn arg equal + cases equal + exact notApp _ _ rfl) + rw [same] + | .weakenAt trace _ => fun notApp => by + have same := trace.rigid (by + intro fn arg equal + cases equal + exact notApp _ _ rfl) + rw [same] + | .rebase _ trace => trace.rigid + | .convertType trace _ => trace.rigid + | .instantiate trace arguments => + AExpr.HeadRigid.map trace.rigid (AExpr.instL arguments) (by intros; rfl) + | .appendContext trace _ | .extend trace _ => trace.rigid +termination_by structural trace + /-- Earlier argument checks can cross another local binder without any new inference of their lifted expressions. -/ def SynthesisArgumentSpineOrigin.weaken {β : Type u} {resolve : Address → Option (ConstRef β)} @@ -503,6 +622,8 @@ def SynthesisArgumentSpineOrigin.weaken {β : Type u} {resolve : Address → Opt | .snoc prior checked => by simpa only [List.map_append, List.map_cons, List.map_nil, AExpr.liftN_inst_zero] using (prior.weaken domain).snoc (checked.weaken domain) + | .convert prior trace => + .convert (prior.weaken domain) (.weakenAt trace (ContextInsertion.root context domain)) termination_by structural support def SynthesisArgumentSpineOrigin.instantiate {β : Type u} {resolve : Address → Option (ConstRef β)} @@ -518,6 +639,7 @@ def SynthesisArgumentSpineOrigin.instantiate {β : Type u} {resolve : Address | .snoc prior checked => by simpa only [List.map_append, List.map_cons, List.map_nil, AExpr.instL_inst] using (prior.instantiate levels).snoc (checked.instantiate levels) + | .convert prior trace => .convert (prior.instantiate levels) (.instantiate trace levels) termination_by structural support def SynthesisArgumentSpineOrigin.appendContext {β : Type u} {resolve : Address → Option (ConstRef β)} @@ -530,6 +652,7 @@ def SynthesisArgumentSpineOrigin.appendContext {β : Type u} {resolve : Address match support with | .nil _ => .nil _ | .snoc prior checked => (prior.appendContext outer).snoc (checked.appendContext outer) + | .convert prior trace => .convert (prior.appendContext outer) (.appendContext trace outer) termination_by structural support def SynthesisArgumentSpineOrigin.extend {β : Type u} {resolve : Address → Option (ConstRef β)} @@ -541,6 +664,7 @@ def SynthesisArgumentSpineOrigin.extend {β : Type u} {resolve : Address → Opt match support with | .nil _ => .nil _ | .snoc prior checked => (prior.extend extension).snoc (checked.extend extension) + | .convert prior trace => .convert (prior.extend extension) (.extend trace extension) termination_by structural support def SynthesisArgumentSpineOrigin.substituteAt {β : Type u} {resolve : Address → Option (ConstRef β)} @@ -558,6 +682,7 @@ def SynthesisArgumentSpineOrigin.substituteAt {β : Type u} {resolve : Address | .snoc prior checked => by simpa only [List.map_append, List.map_cons, List.map_nil, AExpr.inst_inst_zero] using (prior.substituteAt value substitution).snoc (checked.substituteAt value substitution) + | .convert prior trace => .convert (prior.substituteAt value substitution) (.substituteAt trace value substitution) termination_by structural support /-- The head of a checked variable application uses the exact local type; @@ -1001,6 +1126,17 @@ def SynthesisInference.variableSpineOrigin {β : Type u} {resolve : Address → have spine := prior.spine.snoc (.source (.applicationArgument contextOrigin trace functionTree argumentTree keyedAgreement functionReading argumentReading conditions hashPath comparisonFaithful)) exact ⟨prior.headType, prior.atIndex, by simpa only [← parts.1] using spine⟩ + | .appBeta _ miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath + comparisonFaithful _ _ _ _ _ _ => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have parts := app_variable_spine headEquals + have prior := functionTree.variableSpineOrigin contextOrigin keyedAgreement functionReading + index arguments.dropLast parts.2 + have spine := (prior.spine.convert (.rebase contextOrigin reduction)).snoc + (.source (.applicationBetaArgument contextOrigin trace functionTree exposure exposureCoherent argumentTree + keyedAgreement functionReading argumentReading conditions hashPath comparisonFaithful)) + exact ⟨prior.headType, prior.atIndex, by simpa only [← parts.1] using spine⟩ | .forallE .. | .lam .. | .lamBeta .. => by exact False.elim (not_variable_spine (by intro fn arg same; cases same) (by intro index same; cases same) index arguments headEquals) @@ -1104,7 +1240,7 @@ theorem SynthesisInference.lambdaPrefix {β : Type u} have depth := bodyTree.lambdaPrefix.lambdaDepth_zero (AExpr.appN_ne_forallE (by intro condition domain body same; cases same) _) simpa only [AExpr.lambdaDepth, depth] using LambdaPrefix.lam (LambdaPrefix.zero _ _) - | .fvar .. | .app .. | .forallE .. => .zero _ _ + | .fvar .. | .app .. | .appBeta .. | .forallE .. => .zero _ _ termination_by structural support theorem SynthesisHead.appN_head {β : Type u} {head : AExpr β} {arguments : List (AExpr β)} @@ -1205,6 +1341,30 @@ theorem SynthesisInference.soundWithSpine {β : Type u} rw [trace.output run, AExpr.erase_inst] exact (subst_readScopedExpr? bodyConstructed argConstructed bodyBound argBound coherent faithful codomainReads argReads).1 + | .appBeta full miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath + comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful => by + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + rw [full] at run + obtain ⟨fnReads, argReads⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨functionTypeReads, functionTyped, _, functionSpine⟩ := + functionTree.soundWithSpine formed keyedAgreement fnReads trace.functionRun + obtain ⟨domainReads, codomainReads, _⟩ := exposure.reading functionTypeReads exposureCoherent + have argumentAgreement := (trace.exposure_context exposure).symm ▸ keyedAgreement + obtain ⟨argumentTypeReads, argumentTyped, _, _⟩ := + argumentTree.soundWithSpine formed argumentAgreement argReads trace.argumentRun + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (argumentTypeReads.symm.trans + ((beq_readScopedExpr? comparisonFaithful hashPath).trans domainReads))) conditions + obtain ⟨converted, functionFormed⟩ := reduction.sound formed + have exposedTyped := functionTyped.conv functionFormed converted + have checked := sameType ▸ argumentTyped.checking + refine ⟨?_, exposedTyped.appChecking checked, + exposedTyped.applicationType functionFormed checked, + (functionSpine.convert reduction.rigid converted functionFormed).app (sameType ▸ argumentTyped)⟩ + rw [trace.output run, AExpr.erase_inst] + exact (subst_readScopedExpr? bodyConstructed argConstructed bodyBound argBound + coherent faithful codomainReads argReads).1 | .forallE miss trace opening absent domainTree bodyTree levelFaithful domainBound bodyBound coherent faithful => by obtain ⟨state, run⟩ := infer_uncached_success miss accepted @@ -1372,6 +1532,19 @@ theorem SynthesisCheckedOrigin.soundWithSpine {β : Type u} {resolve : Address (Option.some.inj (argumentTypeReads.symm.trans ((beq_readScopedExpr? comparisonFaithful hashPath).trans domainReads))) conditions exact ⟨sameType ▸ argumentTyped, sameType ▸ argumentTree.lambdaPrefix, sameType ▸ argumentSpine⟩ + | .applicationBetaArgument contextOrigin trace functionTree exposure exposureCoherent argumentTree agreement + functionReading argumentReading conditions hashPath comparisonFaithful => by + have contextFormation := contextOrigin.sound formed + have functionTypeReads := + (functionTree.soundWithSpine contextFormation agreement functionReading trace.functionRun).1 + have domainReads := (exposure.reading functionTypeReads exposureCoherent).1 + obtain ⟨argumentTypeReads, argumentTyped, _, argumentSpine⟩ := + argumentTree.soundWithSpine contextFormation ((trace.exposure_context exposure).symm ▸ agreement) + argumentReading trace.argumentRun + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (argumentTypeReads.symm.trans + ((beq_readScopedExpr? comparisonFaithful hashPath).trans domainReads))) conditions + exact ⟨sameType ▸ argumentTyped, sameType ▸ argumentTree.lambdaPrefix, sameType ▸ argumentSpine⟩ | .binderArgument trace functionTree head argumentTree agreement functionReading argumentReading conditions hashPath comparisonFaithful => by obtain ⟨functionTypeReads, functionTyped⟩ := @@ -1402,6 +1575,10 @@ theorem SynthesisArgumentSpineOrigin.sound {β : Type u} {resolve : Address → match support with | .nil _ => .nil _ | .snoc prior checked => (prior.sound formed).append (.cons (checked.sound formed) (.nil _)) + | .convert prior trace => by + obtain ⟨converted, typed⟩ := trace.sound formed + simpa only [List.append_nil] using + (prior.sound formed).append (ArgumentSpine.convert trace.rigid converted typed (.nil _)) termination_by structural support theorem SynthesisReductionOrigin.sound {β : Type u} {resolve : Address → Option (ConstRef β)} @@ -1471,6 +1648,26 @@ theorem SynthesisBetaTrace.sound {β : Type u} {resolve : Address → Option (Co obtain ⟨converted, resultTyped⟩ := trace.sound formed exact ⟨insertion.conversion converted, insertion.typing resultTyped⟩ | .rebase origin trace => trace.sound (origin.sound formed) + | .convertType trace typeTrace => by + obtain ⟨converted, typed⟩ := trace.sound formed + obtain ⟨typeConversion, targetFormed⟩ := typeTrace.sound formed + exact ⟨converted, typed.conv targetFormed typeConversion⟩ + | .instantiate trace arguments => by + obtain ⟨converted, typed⟩ := trace.sound formed + refine ⟨?_, typing_instL_context typed arguments⟩ + intro V _ constants realizes levels env valid + simpa only [interp_instL] using + converted V constants realizes (arguments.map (VLevel.eval levels)) env (context_valid_instL valid) + | .appendContext trace outer => by + obtain ⟨converted, typed⟩ := trace.sound formed + refine ⟨?_, typing_append_context typed outer⟩ + intro V _ constants realizes levels env valid + exact converted V constants realizes levels env (context_valid_prefix valid) + | .extend trace extension => by + obtain ⟨converted, typed⟩ := trace.sound formed + refine ⟨?_, extension.typing typed⟩ + intro V _ constants realizes levels env valid + exact converted V constants (extension.realizes realizes) levels env valid termination_by structural support end diff --git a/Ix/Kernel/Verify/Whnf.lean b/Ix/Kernel/Verify/Whnf.lean index 3f4e0951f..db6e0754f 100644 --- a/Ix/Kernel/Verify/Whnf.lean +++ b/Ix/Kernel/Verify/Whnf.lean @@ -3,6 +3,7 @@ import Ix.Kernel.Verify.Inductive import Ix.Kernel.Verify.Run import Ix.Kernel.Verify.State import Ix.Kernel.Verify.Whnf.Beta.DirectStep +import Ix.Kernel.Verify.Whnf.Driver.CacheExecution open Ix.Theory (VLevel) @@ -2785,35 +2786,6 @@ theorem eval {methods : Methods .anon} {flags : WhnfFlags} end WhnfCoreKeyedEntry -/-- Exact full-policy cache-hit execution after key and transient checks. -/ -theorem whnfCoreWithFlagsNonLeaf_fullHit - {methods : Methods .anon} {s s₁ s₂ : TcState .anon} - {source cached : KExpr .anon} {flags : WhnfFlags} - {key : Address × Address} - (hfull : flags.isFull = true) - (hkey : TcM.whnfKey source s = .ok key s₁) - (htransient : (isTransientNatLiteralWork source).run methods s₁ = - .ok false s₂) - (hhit : s₂.env.whnfCoreCache[key]? = some cached) : - (whnfCoreWithFlagsNonLeaf source flags).run methods s = - .ok cached s₂ := by - unfold whnfCoreWithFlagsNonLeaf - rw [ReaderT.run_bind] - change EStateM.bind (TcM.whnfKey source) _ s = _ - unfold EStateM.bind - rw [hkey] - simp only - rw [ReaderT.run_bind] - change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₁ = _ - unfold EStateM.bind - rw [htransient] - simp [hfull] - change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₂ = _ - unfold EStateM.bind - rw [show (get : TcM .anon (TcState .anon)) s₂ = .ok s₂ s₂ from rfl] - simp only [hhit] - rfl - /-- Exact cheap-policy cache-hit execution after key and transient checks. -/ theorem whnfCoreWithFlagsNonLeaf_cheapHit {methods : Methods .anon} {s s₁ s₂ : TcState .anon} @@ -2843,43 +2815,6 @@ theorem whnfCoreWithFlagsNonLeaf_cheapHit simp only [hhit] rfl -/-- Exact full-policy miss execution, including the physical insertion. -/ -theorem whnfCoreWithFlagsNonLeaf_fullMiss - {methods : Methods .anon} {s s₁ s₂ s₃ : TcState .anon} - {source result : KExpr .anon} {flags : WhnfFlags} - {key : Address × Address} - (hfull : flags.isFull = true) - (hkey : TcM.whnfKey source s = .ok key s₁) - (htransient : (isTransientNatLiteralWork source).run methods s₁ = - .ok false s₂) - (hmiss : s₂.env.whnfCoreCache[key]? = none) - (hrun : (whnfCoreWithFlagsUncached source flags).run methods s₂ = - .ok result s₃) : - (whnfCoreWithFlagsNonLeaf source flags).run methods s = - .ok result {s₃ with env := {s₃.env with - whnfCoreCache := s₃.env.whnfCoreCache.insert key result}} := by - unfold whnfCoreWithFlagsNonLeaf - rw [ReaderT.run_bind] - change EStateM.bind (TcM.whnfKey source) _ s = _ - unfold EStateM.bind - rw [hkey] - simp only - rw [ReaderT.run_bind] - change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₁ = _ - unfold EStateM.bind - rw [htransient] - simp [hfull] - change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₂ = _ - unfold EStateM.bind - rw [show (get : TcM .anon (TcState .anon)) s₂ = .ok s₂ s₂ from rfl] - simp only [hmiss] - rw [ReaderT.run_bind] - change EStateM.bind - ((whnfCoreWithFlagsUncached source flags).run methods) _ s₂ = _ - unfold EStateM.bind - rw [hrun] - rfl - /-- Exact cheap-policy miss execution, including its separate insertion. -/ theorem whnfCoreWithFlagsNonLeaf_cheapMiss {methods : Methods .anon} {s s₁ s₂ s₃ : TcState .anon} @@ -3993,34 +3928,6 @@ private theorem natSuccMode_collapse_beq : private theorem natSuccMode_stuck_beq : (NatSuccMode.stuck == NatSuccMode.collapse) = false := rfl -theorem whnfNoDeltaImplNonLeaf_fullHit - {methods : Methods .anon} {s s₁ s₂ : TcState .anon} - {source cached : KExpr .anon} {flags : WhnfFlags} - {key : Address × Address} - (hfull : flags.isFull = true) - (hkey : TcM.whnfKey source s = .ok key s₁) - (htransient : (isTransientNatLiteralWork source).run methods s₁ = - .ok false s₂) - (hhit : s₂.env.whnfNoDeltaCache[key]? = some cached) : - (whnfNoDeltaImplNonLeaf source flags .collapse).run methods s = - .ok cached s₂ := by - unfold whnfNoDeltaImplNonLeaf - rw [ReaderT.run_bind] - change EStateM.bind (TcM.whnfKey source) _ s = _ - unfold EStateM.bind - rw [hkey] - simp only - rw [ReaderT.run_bind] - change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₁ = _ - unfold EStateM.bind - rw [htransient] - simp [natSuccMode_collapse_beq, hfull] - change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₂ = _ - unfold EStateM.bind - rw [show (get : TcM .anon (TcState .anon)) s₂ = .ok s₂ s₂ from rfl] - simp only [hhit] - rfl - theorem whnfNoDeltaImplNonLeaf_cheapHit {methods : Methods .anon} {s s₁ s₂ : TcState .anon} {source cached : KExpr .anon} {flags : WhnfFlags} @@ -4049,50 +3956,6 @@ theorem whnfNoDeltaImplNonLeaf_cheapHit simp only [hhit] rfl -theorem whnfNoDeltaImplNonLeaf_fullMiss - {methods : Methods .anon} {s s₁ s₂ s₃ : TcState .anon} - {source result : KExpr .anon} {flags : WhnfFlags} - {key : Address × Address} - (hfull : flags.isFull = true) - (hkey : TcM.whnfKey source s = .ok key s₁) - (htransient : (isTransientNatLiteralWork source).run methods s₁ = - .ok false s₂) - (hmiss : s₂.env.whnfNoDeltaCache[key]? = none) - (hrun : (whnfNoDeltaImplUncached source flags .collapse).run methods s₂ = - .ok result s₃) - (hnative : s₃.inNativeReduce = false) : - (whnfNoDeltaImplNonLeaf source flags .collapse).run methods s = - .ok result {s₃ with env := {s₃.env with - whnfNoDeltaCache := s₃.env.whnfNoDeltaCache.insert key result}} := by - unfold whnfNoDeltaImplNonLeaf - rw [ReaderT.run_bind] - change EStateM.bind (TcM.whnfKey source) _ s = _ - unfold EStateM.bind - rw [hkey] - simp only - rw [ReaderT.run_bind] - change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₁ = _ - unfold EStateM.bind - rw [htransient] - simp [natSuccMode_collapse_beq, hfull] - change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₂ = _ - unfold EStateM.bind - rw [show (get : TcM .anon (TcState .anon)) s₂ = .ok s₂ s₂ from rfl] - simp only [hmiss] - rw [ReaderT.run_bind] - change EStateM.bind - ((whnfNoDeltaImplUncached source flags .collapse).run methods) _ s₂ = _ - unfold EStateM.bind - rw [hrun] - simp only - rw [ReaderT.run_bind] - change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₃ = _ - unfold EStateM.bind - rw [show (get : TcM .anon (TcState .anon)) s₃ = .ok s₃ s₃ from rfl] - simp only - rw [if_pos hnative] - rfl - theorem whnfNoDeltaImplNonLeaf_cheapMiss {methods : Methods .anon} {s s₁ s₂ s₃ : TcState .anon} {source result : KExpr .anon} {flags : WhnfFlags} @@ -5253,98 +5116,6 @@ theorem whnfNoDeltaImpl_transient_acceptance /-! ### Exact full-WHNF instrumentation/cache equations -/ -theorem whnfWithNatSuccModeNonLeaf_hit - {methods : Methods .anon} {s s₁ s₂ s₃ : TcState .anon} - {source cached : KExpr .anon} {key : Address × Address} - (hprefix : (whnfWithNatSuccModePrefix source).run methods s = - .ok () s₁) - (hkey : TcM.whnfKey source s₁ = .ok key s₂) - (htransient : (isTransientNatLiteralWork source).run methods s₂ = - .ok false s₃) - (hhit : s₃.env.whnfCache[key]? = some cached) : - (whnfWithNatSuccModeNonLeaf source .collapse).run methods s = - .ok cached s₃ := by - unfold whnfWithNatSuccModeNonLeaf - rw [ReaderT.run_bind] - change EStateM.bind - ((whnfWithNatSuccModePrefix source).run methods) _ s = _ - unfold EStateM.bind - rw [hprefix] - simp only - rw [ReaderT.run_bind] - change EStateM.bind (TcM.whnfKey source) _ s₁ = _ - unfold EStateM.bind - rw [hkey] - simp only - rw [ReaderT.run_bind] - change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₂ = _ - unfold EStateM.bind - rw [htransient] - simp [natSuccMode_collapse_beq] - change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₃ = _ - unfold EStateM.bind - rw [show (get : TcM .anon (TcState .anon)) s₃ = .ok s₃ s₃ from rfl] - simp only [hhit] - rfl - -theorem whnfWithNatSuccModeNonLeaf_miss - {methods : Methods .anon} {s s₁ s₂ s₃ s₄ s₅ : TcState .anon} - {source result : KExpr .anon} {key : Address × Address} - (hprefix : (whnfWithNatSuccModePrefix source).run methods s = - .ok () s₁) - (hkey : TcM.whnfKey source s₁ = .ok key s₂) - (htransient : (isTransientNatLiteralWork source).run methods s₂ = - .ok false s₃) - (hmiss : s₃.env.whnfCache[key]? = none) - (hcharge : (whnfWithNatSuccModeMissCharge : RecM .anon Unit).run - methods s₃ = .ok () s₄) - (hrun : (whnfWithNatSuccModeUncached source .collapse).run methods s₄ = - .ok result s₅) - (hnative : s₅.inNativeReduce = false) : - (whnfWithNatSuccModeNonLeaf source .collapse).run methods s = - .ok result {s₅ with env := {s₅.env with - whnfCache := s₅.env.whnfCache.insert key result}} := by - unfold whnfWithNatSuccModeNonLeaf - rw [ReaderT.run_bind] - change EStateM.bind - ((whnfWithNatSuccModePrefix source).run methods) _ s = _ - unfold EStateM.bind - rw [hprefix] - simp only - rw [ReaderT.run_bind] - change EStateM.bind (TcM.whnfKey source) _ s₁ = _ - unfold EStateM.bind - rw [hkey] - simp only - rw [ReaderT.run_bind] - change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₂ = _ - unfold EStateM.bind - rw [htransient] - simp [natSuccMode_collapse_beq] - change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₃ = _ - unfold EStateM.bind - rw [show (get : TcM .anon (TcState .anon)) s₃ = .ok s₃ s₃ from rfl] - simp only [hmiss] - rw [ReaderT.run_bind] - change EStateM.bind - ((whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods) _ s₃ = _ - unfold EStateM.bind - rw [hcharge] - simp only - rw [ReaderT.run_bind] - change EStateM.bind - ((whnfWithNatSuccModeUncached source .collapse).run methods) _ s₄ = _ - unfold EStateM.bind - rw [hrun] - simp only - rw [ReaderT.run_bind] - change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₅ = _ - unfold EStateM.bind - rw [show (get : TcM .anon (TcState .anon)) s₅ = .ok s₅ s₅ from rfl] - simp only - rw [if_pos hnative] - rfl - /-- Stuck-succ full WHNF still pays the miss charge but bypasses both the outer cache read and write. -/ theorem whnfWithNatSuccModeNonLeaf_stuck diff --git a/Ix/Kernel/Verify/Whnf/Driver/CacheExecution.lean b/Ix/Kernel/Verify/Whnf/Driver/CacheExecution.lean new file mode 100644 index 000000000..6afa06f8f --- /dev/null +++ b/Ix/Kernel/Verify/Whnf/Driver/CacheExecution.lean @@ -0,0 +1,247 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Whnf + +/-! Pure execution equations for the structural, no-delta, and full WHNF +cache layers. These statements have no semantic-interface dependencies. -/ + +namespace Ix.Kernel.RecM + +private theorem natSuccMode_collapse_beq : + (NatSuccMode.collapse == NatSuccMode.collapse) = true := rfl + +/-- Exact full-policy cache-hit execution after key and transient checks. -/ +theorem whnfCoreWithFlagsNonLeaf_fullHit + {methods : Methods .anon} {s s₁ s₂ : TcState .anon} + {source cached : KExpr .anon} {flags : WhnfFlags} + {key : Address × Address} + (hfull : flags.isFull = true) + (hkey : TcM.whnfKey source s = .ok key s₁) + (htransient : (isTransientNatLiteralWork source).run methods s₁ = + .ok false s₂) + (hhit : s₂.env.whnfCoreCache[key]? = some cached) : + (whnfCoreWithFlagsNonLeaf source flags).run methods s = + .ok cached s₂ := by + unfold whnfCoreWithFlagsNonLeaf + rw [ReaderT.run_bind] + change EStateM.bind (TcM.whnfKey source) _ s = _ + unfold EStateM.bind + rw [hkey] + simp only + rw [ReaderT.run_bind] + change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₁ = _ + unfold EStateM.bind + rw [htransient] + simp [hfull] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₂ = _ + unfold EStateM.bind + rw [show (get : TcM .anon (TcState .anon)) s₂ = .ok s₂ s₂ from rfl] + simp only [hhit] + rfl + +/-- Exact full-policy miss execution, including the physical insertion. -/ +theorem whnfCoreWithFlagsNonLeaf_fullMiss + {methods : Methods .anon} {s s₁ s₂ s₃ : TcState .anon} + {source result : KExpr .anon} {flags : WhnfFlags} + {key : Address × Address} + (hfull : flags.isFull = true) + (hkey : TcM.whnfKey source s = .ok key s₁) + (htransient : (isTransientNatLiteralWork source).run methods s₁ = + .ok false s₂) + (hmiss : s₂.env.whnfCoreCache[key]? = none) + (hrun : (whnfCoreWithFlagsUncached source flags).run methods s₂ = + .ok result s₃) : + (whnfCoreWithFlagsNonLeaf source flags).run methods s = + .ok result {s₃ with env := {s₃.env with + whnfCoreCache := s₃.env.whnfCoreCache.insert key result}} := by + unfold whnfCoreWithFlagsNonLeaf + rw [ReaderT.run_bind] + change EStateM.bind (TcM.whnfKey source) _ s = _ + unfold EStateM.bind + rw [hkey] + simp only + rw [ReaderT.run_bind] + change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₁ = _ + unfold EStateM.bind + rw [htransient] + simp [hfull] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₂ = _ + unfold EStateM.bind + rw [show (get : TcM .anon (TcState .anon)) s₂ = .ok s₂ s₂ from rfl] + simp only [hmiss] + rw [ReaderT.run_bind] + change EStateM.bind + ((whnfCoreWithFlagsUncached source flags).run methods) _ s₂ = _ + unfold EStateM.bind + rw [hrun] + rfl + +theorem whnfNoDeltaImplNonLeaf_fullHit + {methods : Methods .anon} {s s₁ s₂ : TcState .anon} + {source cached : KExpr .anon} {flags : WhnfFlags} + {key : Address × Address} + (hfull : flags.isFull = true) + (hkey : TcM.whnfKey source s = .ok key s₁) + (htransient : (isTransientNatLiteralWork source).run methods s₁ = + .ok false s₂) + (hhit : s₂.env.whnfNoDeltaCache[key]? = some cached) : + (whnfNoDeltaImplNonLeaf source flags .collapse).run methods s = + .ok cached s₂ := by + unfold whnfNoDeltaImplNonLeaf + rw [ReaderT.run_bind] + change EStateM.bind (TcM.whnfKey source) _ s = _ + unfold EStateM.bind + rw [hkey] + simp only + rw [ReaderT.run_bind] + change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₁ = _ + unfold EStateM.bind + rw [htransient] + simp [natSuccMode_collapse_beq, hfull] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₂ = _ + unfold EStateM.bind + rw [show (get : TcM .anon (TcState .anon)) s₂ = .ok s₂ s₂ from rfl] + simp only [hhit] + rfl + +theorem whnfNoDeltaImplNonLeaf_fullMiss + {methods : Methods .anon} {s s₁ s₂ s₃ : TcState .anon} + {source result : KExpr .anon} {flags : WhnfFlags} + {key : Address × Address} + (hfull : flags.isFull = true) + (hkey : TcM.whnfKey source s = .ok key s₁) + (htransient : (isTransientNatLiteralWork source).run methods s₁ = + .ok false s₂) + (hmiss : s₂.env.whnfNoDeltaCache[key]? = none) + (hrun : (whnfNoDeltaImplUncached source flags .collapse).run methods s₂ = + .ok result s₃) + (hnative : s₃.inNativeReduce = false) : + (whnfNoDeltaImplNonLeaf source flags .collapse).run methods s = + .ok result {s₃ with env := {s₃.env with + whnfNoDeltaCache := s₃.env.whnfNoDeltaCache.insert key result}} := by + unfold whnfNoDeltaImplNonLeaf + rw [ReaderT.run_bind] + change EStateM.bind (TcM.whnfKey source) _ s = _ + unfold EStateM.bind + rw [hkey] + simp only + rw [ReaderT.run_bind] + change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₁ = _ + unfold EStateM.bind + rw [htransient] + simp [natSuccMode_collapse_beq, hfull] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₂ = _ + unfold EStateM.bind + rw [show (get : TcM .anon (TcState .anon)) s₂ = .ok s₂ s₂ from rfl] + simp only [hmiss] + rw [ReaderT.run_bind] + change EStateM.bind + ((whnfNoDeltaImplUncached source flags .collapse).run methods) _ s₂ = _ + unfold EStateM.bind + rw [hrun] + simp only + rw [ReaderT.run_bind] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₃ = _ + unfold EStateM.bind + rw [show (get : TcM .anon (TcState .anon)) s₃ = .ok s₃ s₃ from rfl] + simp only + rw [if_pos hnative] + rfl + +theorem whnfWithNatSuccModeNonLeaf_hit + {methods : Methods .anon} {s s₁ s₂ s₃ : TcState .anon} + {source cached : KExpr .anon} {key : Address × Address} + (hprefix : (whnfWithNatSuccModePrefix source).run methods s = + .ok () s₁) + (hkey : TcM.whnfKey source s₁ = .ok key s₂) + (htransient : (isTransientNatLiteralWork source).run methods s₂ = + .ok false s₃) + (hhit : s₃.env.whnfCache[key]? = some cached) : + (whnfWithNatSuccModeNonLeaf source .collapse).run methods s = + .ok cached s₃ := by + unfold whnfWithNatSuccModeNonLeaf + rw [ReaderT.run_bind] + change EStateM.bind + ((whnfWithNatSuccModePrefix source).run methods) _ s = _ + unfold EStateM.bind + rw [hprefix] + simp only + rw [ReaderT.run_bind] + change EStateM.bind (TcM.whnfKey source) _ s₁ = _ + unfold EStateM.bind + rw [hkey] + simp only + rw [ReaderT.run_bind] + change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₂ = _ + unfold EStateM.bind + rw [htransient] + simp [natSuccMode_collapse_beq] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₃ = _ + unfold EStateM.bind + rw [show (get : TcM .anon (TcState .anon)) s₃ = .ok s₃ s₃ from rfl] + simp only [hhit] + rfl + +theorem whnfWithNatSuccModeNonLeaf_miss + {methods : Methods .anon} {s s₁ s₂ s₃ s₄ s₅ : TcState .anon} + {source result : KExpr .anon} {key : Address × Address} + (hprefix : (whnfWithNatSuccModePrefix source).run methods s = + .ok () s₁) + (hkey : TcM.whnfKey source s₁ = .ok key s₂) + (htransient : (isTransientNatLiteralWork source).run methods s₂ = + .ok false s₃) + (hmiss : s₃.env.whnfCache[key]? = none) + (hcharge : (whnfWithNatSuccModeMissCharge : RecM .anon Unit).run + methods s₃ = .ok () s₄) + (hrun : (whnfWithNatSuccModeUncached source .collapse).run methods s₄ = + .ok result s₅) + (hnative : s₅.inNativeReduce = false) : + (whnfWithNatSuccModeNonLeaf source .collapse).run methods s = + .ok result {s₅ with env := {s₅.env with + whnfCache := s₅.env.whnfCache.insert key result}} := by + unfold whnfWithNatSuccModeNonLeaf + rw [ReaderT.run_bind] + change EStateM.bind + ((whnfWithNatSuccModePrefix source).run methods) _ s = _ + unfold EStateM.bind + rw [hprefix] + simp only + rw [ReaderT.run_bind] + change EStateM.bind (TcM.whnfKey source) _ s₁ = _ + unfold EStateM.bind + rw [hkey] + simp only + rw [ReaderT.run_bind] + change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₂ = _ + unfold EStateM.bind + rw [htransient] + simp [natSuccMode_collapse_beq] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₃ = _ + unfold EStateM.bind + rw [show (get : TcM .anon (TcState .anon)) s₃ = .ok s₃ s₃ from rfl] + simp only [hmiss] + rw [ReaderT.run_bind] + change EStateM.bind + ((whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods) _ s₃ = _ + unfold EStateM.bind + rw [hcharge] + simp only + rw [ReaderT.run_bind] + change EStateM.bind + ((whnfWithNatSuccModeUncached source .collapse).run methods) _ s₄ = _ + unfold EStateM.bind + rw [hrun] + simp only + rw [ReaderT.run_bind] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₅ = _ + unfold EStateM.bind + rw [show (get : TcM .anon (TcState .anon)) s₅ = .ok s₅ s₅ from rfl] + simp only + rw [if_pos hnative] + rfl + + +end Ix.Kernel.RecM diff --git a/Ix/Theory/Model/BetaSpine.lean b/Ix/Theory/Model/BetaSpine.lean index 3cc2f243c..76ea59224 100644 --- a/Ix/Theory/Model/BetaSpine.lean +++ b/Ix/Theory/Model/BetaSpine.lean @@ -264,8 +264,23 @@ theorem LambdaPeel.betaPrefix {β : Type u} {head body : AExpr β} simpa only [List.length_cons, List.cons_append, AExpr.betaPrefix, AExpr.instRev, Nat.zero_add] using result -/-- Types of arguments along the original application's dependent Pi -spine. Production inference will derive this helper from its actual calls. -/ +/-- A head reduction leaves every non-application source unchanged. This +retains a lambda's exact Pi domain across function-type exposure. -/ +def AExpr.HeadRigid {β : Type u} (source target : AExpr β) : Prop := + (∀ fn arg, source ≠ .app fn arg) → target = source + +theorem AExpr.HeadRigid.map {β γ : Type u} {source target : AExpr β} + (rigid : source.HeadRigid target) (f : AExpr β → AExpr γ) + (application : ∀ fn arg, f (.app fn arg) = .app (f fn) (f arg)) : + (f source).HeadRigid (f target) := by + intro notApp + apply congrArg f + apply rigid + intro fn arg same + exact notApp _ _ (by rw [same, application]) + +/-- Types of arguments along an application's dependent Pi spine. A +forward type conversion may expose the next Pi between argument checks. -/ inductive ArgumentSpine {β : Type u} (entries : Environment β) (context : Context β) : AExpr β → List (AExpr β) → AExpr β → Prop | nil (type : AExpr β) : ArgumentSpine entries context type [] type @@ -274,6 +289,12 @@ inductive ArgumentSpine {β : Type u} (entries : Environment β) (context : Cont (typed : TypingClaim.{u,v} entries context argument domain) (tail : ArgumentSpine entries context (codomain.inst argument) arguments result) : ArgumentSpine entries context (.forallE condition domain codomain) (argument :: arguments) result + | convert {source target result : AExpr β} {arguments : List (AExpr β)} {level : VLevel} + (rigid : source.HeadRigid target) + (converted : ConversionClaim.{u,v} entries context source target) + (formed : TypingClaim.{u,v} entries context target (.sort level)) + (tail : ArgumentSpine entries context target arguments result) : + ArgumentSpine entries context source arguments result theorem ArgumentSpine.append {β : Type u} {entries : Environment β} {context : Context β} {start middle finish : AExpr β} {left right : List (AExpr β)} @@ -283,6 +304,7 @@ theorem ArgumentSpine.append {β : Type u} {entries : Environment β} {context : induction first with | nil => exact second | cons typed tail ih => exact .cons typed (ih second) + | convert rigid converted formed tail ih => exact .convert rigid converted formed (ih second) theorem ArgumentSpine.typing {β : Type u} {entries : Environment β} {context : Context β} {head type result : AExpr β} {arguments : List (AExpr β)} @@ -292,6 +314,7 @@ theorem ArgumentSpine.typing {β : Type u} {entries : Environment β} {context : induction spine generalizing head with | nil => exact typed | cons argumentTyped tail ih => exact ih (typed.app argumentTyped) + | convert _ converted formed tail ih => exact ih (typed.conv formed converted) /-- Substitution updates the type at every step of a dependent argument spine, including the type expected by the next application. -/ @@ -308,6 +331,9 @@ theorem ArgumentSpine.instAt {β : Type u} {entries : Environment β} | cons typed tail ih => exact .cons (typed.instAt value substitution) (by simpa only [AExpr.inst_inst_zero] using ih) + | convert rigid converted formed tail ih => + exact .convert (rigid.map (AExpr.inst · argument cutoff) (by intros; rfl)) + (converted.instAt value substitution) (formed.instAt value substitution) ih theorem ContextSubstitution.removed_type {β : Type u} {base source target : Context β} {domain argument : AExpr β} {cutoff : Nat} @@ -378,6 +404,15 @@ theorem ContextSubstitution.lift_typing {β : Type u} {entries : Environment β} simpa only [wellDenoted_liftN, interp_liftN] using typed V constants realizes levels _ (substitution.base_valid valid) +theorem ContextSubstitution.lift_conversion {β : Type u} {entries : Environment β} + {base source target : Context β} {domain argument left right : AExpr β} {cutoff : Nat} + (substitution : ContextSubstitution base domain argument source target cutoff) + (converted : ConversionClaim.{u,v} entries base left right) : + ConversionClaim.{u,v} entries target (left.liftN cutoff) (right.liftN cutoff) := by + intro V _ constants realizes levels env valid + simpa only [interp_liftN] using + converted V constants realizes levels _ (substitution.base_valid valid) + /-- Every existing argument of a supplied value is lifted beneath the same retained dependent parameters as the value's function head. -/ theorem ContextSubstitution.lift_spine {β : Type u} {entries : Environment β} @@ -392,6 +427,9 @@ theorem ContextSubstitution.lift_spine {β : Type u} {entries : Environment β} | cons typed tail ih => exact .cons (substitution.lift_typing typed) (by simpa only [AExpr.liftN_inst_zero] using ih) + | convert rigid converted formed tail ih => + exact .convert (rigid.map (AExpr.liftN cutoff ·) (by intros; rfl)) + (substitution.lift_conversion converted) (substitution.lift_typing formed) ih theorem ConversionClaim.appN {β : Type u} {entries : Environment β} {context : Context β} {left right : AExpr β} (same : ConversionClaim.{u,v} entries context left right) @@ -411,18 +449,28 @@ theorem LambdaPrefix.beta_sound {β : Type u} {entries : Environment β} {contex ConversionClaim.{u,v} entries context (head.appN arguments) (AExpr.betaPrefix count head arguments) ∧ TypingClaim.{u,v} entries context (AExpr.betaPrefix count head arguments) result := by - induction count generalizing head type arguments with - | zero => exact ⟨.refl _, spine.typing typed⟩ - | succ count ih => - cases leading with - | lam inner => - cases spine with - | nil => exact ⟨.refl _, typed⟩ - | cons argumentTyped tail => - obtain ⟨conversion, resultTyped⟩ := ih (inner.inst _ 0) - (TypingClaim.betaResult typed argumentTyped) tail - exact ⟨((ConversionClaim.beta typed argumentTyped).appN _).trans conversion, - resultTyped⟩ + induction spine generalizing head count with + | nil => + have same : AExpr.betaPrefix count head [] = head := by cases count <;> cases head <;> rfl + rw [same] + exact ⟨.refl _, typed⟩ + | cons argumentTyped tail ih => + cases count with + | zero => exact ⟨.refl _, tail.typing (typed.app argumentTyped)⟩ + | succ count => + cases leading with + | lam inner => + obtain ⟨conversion, resultTyped⟩ := ih (inner.inst _ 0) (TypingClaim.betaResult typed argumentTyped) + exact ⟨((ConversionClaim.beta typed argumentTyped).appN _).trans conversion, resultTyped⟩ + | @convert source target result arguments level rigid converted formed tail ih => + cases count with + | zero => exact ⟨.refl _, tail.typing (typed.conv formed converted)⟩ + | succ count => + have same : target = source := by + cases leading + exact rigid (by intro fn arg same; cases same) + subst target + exact ih leading typed /-- Source inference retains the typing of every original lambda-headed spine. This internal result carries the domains needed by later reduction; @@ -490,6 +538,18 @@ theorem app {β : Type u} {entries : Environment β} {context : Context β} rw [← same.2] exact spine.append (.cons argument (.nil _)) +theorem convert {β : Type u} {entries : Environment β} {context : Context β} + {term source target : AExpr β} {level : VLevel} + (spine : LambdaSpineTyping.{u,v} entries context term source) + (rigid : source.HeadRigid target) + (converted : ConversionClaim.{u,v} entries context source target) + (formed : TypingClaim.{u,v} entries context target (.sort level)) : + LambdaSpineTyping.{u,v} entries context term target := by + intro condition domain body arguments same + obtain ⟨headType, typed, leading, argumentsTyped⟩ := spine _ _ _ _ same + refine ⟨headType, typed, leading, ?_⟩ + simpa only [List.append_nil] using argumentsTyped.append (.convert rigid converted formed (.nil _)) + theorem betaPrefix {β : Type u} {entries : Environment β} {context : Context β} {condition : PropWhen} {domain body type : AExpr β} {arguments : List (AExpr β)} {count : Nat} (source : LambdaSpineTyping.{u,v} entries context diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index a2852e429..4e0ea7de0 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -1290,6 +1290,157 @@ private def hereditaryBetaCases : TestSeq := ([0, 1].all fun shape => let (env, target) := hereditaryBetaDeclaration .zero 0 4 shape 2 rowFailed env target) +/-- The axiom's type exposes a Pi by beta reduction. In the dependent +variant its first application returns another beta redex, so the next +argument check must expose a second Pi. -/ +private def piExposureDeclaration (level : Ixon.Univ) (universes : UInt64 := 0) + (dependent : Bool := false) (wrong : Nat := 0) : Ixon.Env × Address := Id.run do + let levels := if universes == 0 then #[] else #[0] + let (env, carrier) := storeConst {} + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], #[level]⟩ + let (env, otherCarrier) := storeConst env + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], #[level, .succ level]⟩ + let (env, witness) := storeConst env + ⟨.axio ⟨false, universes, .ref 0 levels⟩, #[], #[carrier], #[level]⟩ + let family := Ixon.Expr.leanLam (.sort 0) (.leanAll (.var 0) (.var 1)) + let type := if dependent then + Ixon.Expr.app (.leanLam (.sort 0) (.leanAll (.sort 0) (.app family (.var 0)))) (.ref 0 levels) + else .app family (.ref 0 levels) + let (env, function) := storeConst env + ⟨.axio ⟨false, universes, type⟩, #[], #[carrier], #[level]⟩ + let fn := if dependent then Ixon.Expr.app (.ref 3 levels) (.ref (if wrong == 1 then 1 else 0) levels) + else .ref 3 levels + return storeConst env + ⟨.defn ⟨.defn, .safe, universes, .ref 0 levels, .app fn (.ref (if wrong == 2 then 0 else 2) levels)⟩, + #[], #[carrier, otherCarrier, witness, function], #[level]⟩ + +private def localPiExposure (level : Ixon.Univ) (universes : UInt64 := 0) + (wrong : Bool := false) : Ixon.Env × Address := + let family := Ixon.Expr.leanLam (.sort 0) (.leanAll (.var 0) (.var 1)) + let type := Ixon.Expr.leanAll (.sort 0) + (.leanAll (.var 0) (.leanAll (.app family (.var 1)) (.var 2))) + let value := Ixon.Expr.leanLam (.sort 0) + (.leanLam (.var 0) (.leanLam (.app family (.var 1)) (.app (.var 0) (.var (if wrong then 2 else 1))))) + storeConst {} ⟨.defn ⟨.defn, .safe, universes, type, value⟩, #[], #[], #[level]⟩ + +/-- Inspect the actual public cache writes and exact shared-fuel charge. +A second call uses the populated outer cache with zero fuel and no recursive +methods, even while native reduction is active. -/ +private def observePiExposure (source domain body : KExpr .anon) (instrumented noAccel : Bool) : RecM .anon Bool := do + let .app .. := source | return false + modify fun state => { state with + env := { state.env with whnfCache := {}, whnfNoDeltaCache := {}, whnfCoreCache := {} } + ctxAddrCache := {}, recFuel := 1, stats := instrumented, stepTrace := instrumented, + whnfCalls := 17, whnfMisses := 11, noAccel, inNativeReduce := false } + let before ← get + let (foundDomain, foundBody) ← RecM.ensureForallDirect source + let after ← get + let key ← TcM.whnfKey source + let expected := KExpr.mkAll () () domain body + let warm := { after with inNativeReduce := true } + match (RecM.ensureForallDirect source).run (methodsN 0) warm with + | .error _ _ => return false + | .ok (warmDomain, warmBody) reused => + return foundDomain == domain && foundBody == body && warmDomain == domain && warmBody == body && + after.env.whnfCache[key]? == some expected && after.env.whnfNoDeltaCache[key]? == some expected && + after.env.whnfCoreCache[key]? == some expected && after.recFuel == 0 && reused.recFuel == 0 && + after.whnfCalls == (if instrumented then 18 else 17) && + after.whnfMisses == (if instrumented then 12 else 11) && + reused.whnfCalls == (if instrumented then 19 else 17) && reused.whnfMisses == after.whnfMisses && + after.lctx.size == before.lctx.size && after.ctxId == before.ctxId && + after.env.nextFVarId == before.env.nextFVarId && reused.inNativeReduce && + after.ctxAddrCache.size == (if source.lbr == 0 || before.ctx.isEmpty then 0 else 1) && + reused.ctxAddrCache.size == after.ctxAddrCache.size && + reused.env.intern.exprs.size == after.env.intern.exprs.size + +private def piExposureInferenceResult (typeLevel dependent instrumented noAccel : Bool) : Bool := + let (env, target) := piExposureDeclaration (if typeLevel then .succ .zero else .zero) 0 dependent + let action : RecM .anon Bool := do + let .defn _ _ _ _ _ _ expected value _ _ ← TcM.getConst (m := .anon) ⟨target, ()⟩ | return false + let (head, arguments) := value.collectSpine + if dependent then + let firstType ← RecM.inferCall head + let sort := KExpr.mkSort (if typeLevel then levelOne else .mkZero) + let family := KExpr.mkLam () () sort (.mkAll () () (.mkVar 0 ()) (.mkVar 1 ())) + let before ← get + let exposed ← observePiExposure firstType sort (.mkApp family (.mkVar 0 ())) instrumented noAccel + set before + if !exposed || arguments.size != 2 then return false + let .app fn arg _ := value | return false + let functionType ← RecM.inferCall fn + let argumentType ← RecM.inferCall arg + let before ← get + let exposed ← observePiExposure functionType expected expected instrumented noAccel + set before + let result ← RecM.inferCall value + return exposed && argumentType == expected && result == expected + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +private def localPiExposureResult (typeLevel : Bool) : Bool := + let (env, target) := localPiExposure (if typeLevel then .succ .zero else .zero) + let action : RecM .anon Bool := RecM.withLctxScope do + let .defn _ _ _ _ _ _ _ value _ _ ← TcM.getConst (m := .anon) ⟨target, ()⟩ | return false + let mut opened := value + for _ in List.range 3 do + let .lam name bi domain body _ := opened | return false + opened := (← TcM.openBinder name bi domain body).1 + let .app fn arg _ := opened | return false + let functionType ← RecM.inferCall fn + let expected ← RecM.inferCall arg + let before ← get + let exposed ← observePiExposure functionType expected expected true true + set before + let result ← RecM.inferCall opened + return exposed && result == expected && before.lctx.size == 3 + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +private def legacyPiExposureKey : Bool := + let sort := KExpr.mkSort levelOne + let source := KExpr.mkApp (.mkLam () () sort (.mkAll () () (.mkVar 0 ()) (.mkVar 1 ()))) (.mkVar 0 ()) + let action : RecM .anon Bool := do + TcM.pushLocal sort + TcM.pushLocal sort + observePiExposure source (.mkVar 0 ()) (.mkVar 1 ()) true false + match TcM.runRec action (TcState.newLazyAnon {}) with + | .ok passed after => passed && after.ctx.size == 2 + | .error _ _ => false + +private def piExposureCases : TestSeq := + test "Pi exposure: beta function types check in Prop and Type" + (allSucceeded (piExposureDeclaration .zero).1 5 && + allSucceeded (piExposureDeclaration (.succ .zero)).1 5) + ++ test "Pi exposure: successive arguments each expose their dependent function type" + (allSucceeded (piExposureDeclaration .zero 0 true).1 5 && + allSucceeded (piExposureDeclaration (.succ .zero) 0 true).1 5) + ++ test "Pi exposure: function and argument types retain universe parameters" + (allSucceeded (piExposureDeclaration (.var 0) 1).1 5 && + allSucceeded (piExposureDeclaration (.var 0) 1 true).1 5) + ++ test "Pi exposure: declarations check with fresh per-item caches" + ([false, true].all fun dependent => + allSucceeded (piExposureDeclaration (.succ .zero) 0 dependent).1 5 { clearEvery := 1 }) + ++ test "Pi exposure: all three public caches contain the Pi and warm hits need no fuel" + ([false, true].all fun typeLevel => [false, true].all fun dependent => + [false, true].all fun instrumented => [false, true].all fun noAccel => + piExposureInferenceResult typeLevel dependent instrumented noAccel) + ++ test "Pi exposure: dependent local function types check beneath three binders" + (allSucceeded (localPiExposure .zero).1 1 && allSucceeded (localPiExposure (.succ .zero)).1 1 && + allSucceeded (localPiExposure (.var 0) 1).1 1 { clearEvery := 1 }) + ++ test "Pi exposure: public reduction preserves opened locals and scope cleanup" + (localPiExposureResult false && localPiExposureResult true) + ++ test "Pi exposure: legacy context keys memoize the reachable suffix" + legacyPiExposureKey + ++ test "Pi exposure: choosing another carrier rejects the dependent witness" + (let (env, target) := piExposureDeclaration .zero 0 true 1; rowFailed env target) + ++ test "Pi exposure: a carrier cannot replace the checked witness" + ([false, true].all fun dependent => + let (env, target) := piExposureDeclaration .zero 0 dependent 2; rowFailed env target) + ++ test "Pi exposure: a local carrier cannot inhabit its own function domain" + (let (env, target) := localPiExposure .zero 0 true; rowFailed env target) + private def applicationCases : TestSeq := test "application environment: Prop/Type identity calls and transitive theorem calls check" (allSucceeded applicationEnvironment 5 { clearEvery := 0 }) @@ -3079,7 +3230,7 @@ private def polymorphicDefinitionCases : TestSeq := public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, multiBetaCases, cheapLambdaCases, - cheapApplicationCases, exposedLambdaCases, repeatedBetaCases, betaTraceCases, hereditaryBetaCases, + cheapApplicationCases, exposedLambdaCases, repeatedBetaCases, betaTraceCases, hereditaryBetaCases, piExposureCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, sourceAgreementCases, sourceCacheCases, polymorphicDefinitionCases] diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 71f762aa7..5abd34b9d 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -400,8 +400,9 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) checked spine `[A, B]`. Its existing arguments are lifted beneath retained parameters, and the codomain arguments receive the same substitution as their dependent types. The selected prefix can consume both lists. -- Applications use full mode, syntactic Pi exposure, an ordinary argument - without an eager-reduction marker, and the hash-equality conversion path. +- Applications use full mode, syntactic or supported beta Pi exposure, an + ordinary argument without an eager-reduction marker, and hash comparison + with the exposed domain. Their witnesses retain the actual recursive calls, context preservation during function inference, and finite substitution resources. Function spines can start with locals or admitted constants, or use the synthesis @@ -412,9 +413,13 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) universe-substitution resources. Their result type is fixed by a pure `readInstantiatedType?` check with the substituted occurrence annotations, including when levels simplify. Monomorphic references retain - their simpler empty-substitution rule. Reduction to expose a Pi, eager - arguments, and automatic checking origins for arbitrary generated types - require further refinement. + their simpler empty-substitution rule. `appBeta` follows the actual public + WHNF exposure, including its computed state changes, and derives the + conversion from a retained actual check of the function type. The argument + check starts in the post-exposure state; dependent argument spines retain + conversions between successive applications. Other Pi-exposure reductions + and cache paths, eager arguments, and automatic checking origins for + arbitrary generated types require further refinement. - `DefinitionCheckSupport` permits the existing initial hash-equality path, with faithfulness of the compared expressions, or a beta-reducible declared type. For the beta case, the declaration's own type-inference tree and run @@ -512,7 +517,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 615 exact theorem boundaries. The production +The consistency target checks 653 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -616,6 +621,22 @@ proofs. This closes automatic semantic origins for finite head-beta paths of the supported source-inference fragment. Initial inference trees, operational paths, and representation resources remain explicit; deriving them for all accepted programs and covering the remaining WHNF/conversion paths remain open. +`BetaPublicWhnfPlan` connects a raw beta path to public WHNF when its result +is a sort, Pi, or lambda. It computes the context-key states, optional counters, +shared-fuel charge, and insertions into the structural, no-delta, and outer +caches. The reducer tails are proved to stop on these constructors without +additional callback or lookup premises. `BetaPiExposure` derives the actual +`ensureForallDirect` call and the returned domain/codomain readings. It either +executes a cold public path or reuses that path's exact result from the outer +cache. Cold paths require three cache misses and an inactive native-reduction +guard; warm outer-cache hits need no fuel. `checkedTrace` constructs the +conversion from a retained actual type check. `SynthesisInference.appBeta` +uses that conversion in the original inference recursion, preserving dependent +codomain substitution, checked lambda domains, and all later beta origins. +The existing synthesis admission and environment roots include this case. +These 38 additional audited boundaries introduce no axioms or native proofs. +Mixed cache states, general WHNF cache agreement, other reducers, and general +inference-resource construction remain open. Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, argument order, and rejection of wrong arities and out-of-range parameters. @@ -687,6 +708,14 @@ a returned lambda that still contains the supplied function. Further chains retain a lambda body's changed cheap-beta type. Prop, Type, declaration parameters, cleared caches, both WHNF policies, exact consumed-argument counts, and rejected dependent substitutions are checked. +Pi-exposure cases check beta function types in Prop, Type, and at universe +parameters, including a second exposure between dependent arguments and local +functions beneath three opened binders. They inspect all three public cache +writes, optional counters, the exact shared-fuel charge, and reuse with zero +fuel and no recursive methods. Both acceleration policies preserve these +results. Additional operational coverage checks context-key memoization for +legacy loose variables. Negative cases reject mismatched carriers and invalid +dependent witnesses. The scoped semantic reader still excludes loose variables. These execution tests do not construct the general finite inference resources. Polymorphic-call regressions include Prop/Type instances in real function bodies, `max`/`imax` simplification inside Pi domains, closed nested references @@ -757,7 +786,7 @@ Definition-cycle regressions use content-addressed standalone and mutual declarations, including a self-justifying theorem, a two-member cycle, type cycles, lets, shared syntax, and binders. They check repeated member failures, acyclic forward references, cache clearing, and the partial/unsafe policy. -The unit suite contains 686 checks. The anonymous differential additionally +The unit suite contains 697 checks. The anonymous differential additionally serializes eight cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. @@ -800,6 +829,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Source ownership, block registration, and finite preflight | [`Consistency/BlockOwnership.lean`](../Ix/Kernel/Verify/Consistency/BlockOwnership.lean), [`Consistency/SourceOwnershipCheck.lean`](../Ix/Kernel/Verify/Consistency/SourceOwnershipCheck.lean), [`SourceOwnership.lean`](../Ix/Kernel/SourceOwnership.lean) | | Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`Application.lean`](../Ix/Kernel/Verify/Consistency/Application.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | | Inferred type formation and direct lambda applications | [`Consistency/SynthesisInference.lean`](../Ix/Kernel/Verify/Consistency/SynthesisInference.lean), [`Formation.lean`](../Ix/Kernel/Verify/Consistency/Formation.lean), [`Model/UniverseBounds.lean`](../Ix/Theory/Model/UniverseBounds.lean) | +| Public beta WHNF, cache writes, and application Pi exposure | [`Consistency/BetaPublicWhnf.lean`](../Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean), [`ApplicationWhnf.lean`](../Ix/Kernel/Verify/Consistency/ApplicationWhnf.lean), [`BetaWhnfInference.lean`](../Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean) | | Retained type checks and changed cheap beta in lambda inference | [`Consistency/SynthesisInference.lean`](../Ix/Kernel/Verify/Consistency/SynthesisInference.lean), [`CheapBetaReading.lean`](../Ix/Kernel/Verify/Consistency/CheapBetaReading.lean), [`Formation.lean`](../Ix/Kernel/Verify/Consistency/Formation.lean) | | Source beta reduction and declaration conversion | [`Consistency/BetaSpine.lean`](../Ix/Kernel/Verify/Consistency/BetaSpine.lean), [`Simultaneous.lean`](../Ix/Kernel/Verify/Consistency/Simultaneous.lean), [`SpineReading.lean`](../Ix/Kernel/Verify/Consistency/SpineReading.lean), [`CheapBeta.lean`](../Ix/Kernel/Verify/Consistency/CheapBeta.lean), [`Model/BetaSpine.lean`](../Ix/Theory/Model/BetaSpine.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | From d849384119e093b91597ab22675ff54242b67b0f Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 08:48:15 -0400 Subject: [PATCH 34/63] Prove composite inference cache reuse Retain original synthesis trees behind cached application, Pi, and lambda checks so later beta proofs keep their checked domains, bodies, and dependent arguments. Derive full-cache publication and later replay from successful inference and proved frames, including public beta Pi exposure and changed cheap-beta lambda bodies. Validation: 663 exact consistency audit roots, 707 tc-unit checks, check-theory, and the broader kernel/compiler proof build pass. Existing axiom boundaries are unchanged. General cache histories and interface/context transport remain open. --- Ix/Kernel/Verify/Consistency.lean | 20 ++- Ix/Kernel/Verify/Consistency/Audit.lean | 10 +- Ix/Kernel/Verify/Consistency/Beta.lean | 52 +++++- .../Verify/Consistency/BetaInference.lean | 2 + Ix/Kernel/Verify/Consistency/BetaTrace.lean | 2 + .../Verify/Consistency/InferenceCache.lean | 34 ++++ .../Verify/Consistency/RecursiveCache.lean | 57 +++++- .../Verify/Consistency/RecursiveState.lean | 2 +- .../Verify/Consistency/SynthesisCache.lean | 92 ++++++++++ .../Consistency/SynthesisInference.lean | 26 +++ .../Verify/Consistency/WhnfCacheFrame.lean | 78 +++++++++ Tests/Ix/Kernel/Consistency.lean | 163 +++++++++++++++++- docs/kernel-verification.md | 39 ++++- docs/theory.md | 12 +- 14 files changed, 562 insertions(+), 27 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/SynthesisCache.lean create mode 100644 Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 5004b12f1..ab2fb4d9a 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -29,6 +29,7 @@ import Ix.Kernel.Verify.Consistency.BinderInference import Ix.Kernel.Verify.Consistency.Formation import Ix.Kernel.Verify.Consistency.ContextInsertion import Ix.Kernel.Verify.Consistency.SynthesisInference +import Ix.Kernel.Verify.Consistency.SynthesisCache import Ix.Kernel.Verify.Consistency.BetaSubstitution import Ix.Kernel.Verify.Consistency.Beta import Ix.Kernel.Verify.Consistency.Simultaneous @@ -39,6 +40,7 @@ import Ix.Kernel.Verify.Consistency.BetaTrace import Ix.Kernel.Verify.Consistency.BetaWhnf import Ix.Kernel.Verify.Consistency.BetaWhnfPlan import Ix.Kernel.Verify.Consistency.BetaPublicWhnf +import Ix.Kernel.Verify.Consistency.WhnfCacheFrame import Ix.Kernel.Verify.Consistency.BetaTyping import Ix.Kernel.Verify.Consistency.BetaInference import Ix.Kernel.Verify.Consistency.BetaWhnfInference @@ -149,9 +151,15 @@ programs, other WHNF branches, mixed cache states, and general conversion remain open. Local cache hits agree with the actual declaration type. Constant hits agree with pure universe substitution of a loaded, admitted declaration; -sort hits return the canonical successor sort. Application, forall, and lambda -nodes retain misses in every eligible cache partition. Full mode leaves the -inference-only partition unconstrained at a miss. +sort hits return the canonical successor sort. Applications, foralls, and +lambdas can reuse an earlier successful synthesis check. The cache node +retains its original inference tree, local reading, and execution, so lambda +domains and dependent codomain checks remain available to later beta proofs. +Successful full inference establishes the exact stored result; cache frames +derive its later selection under either checking policy, even with zero fuel. +These helpers keep the same semantic interface and annotated local context. +Cold composite nodes still require misses in every eligible partition; full +mode leaves the inference-only partition unconstrained at a miss. Applications use syntactic or supported beta Pi exposure, full argument checking, hash comparison of the argument with the exposed domain, and arguments without eager-reduction markers. Constant- and local-headed spines @@ -170,6 +178,12 @@ by recursive applications, dependent functions, and full-mode lambdas, including domain validation and outer cache writes. Successful inference preserves entries outside that footprint, loaded declarations, and checking policy. This derives later constant witnesses and sort leaves without new cache-hit observations. +The trace also covers beta Pi exposure and changed cheap-beta lambda bodies. +Public beta WHNF and Pi exposure preserve inference entries at every key; +their computed writes affect the WHNF caches. These frames also preserve the +earlier full composite checks. General semantic cache histories, context and +interface transport of composite entries, and automatic trace construction +remain open. Frames allow declaration growth while retaining every old declaration. Verified lazy loading derives such a frame on success and failure, including partial conversion state and fault deduplication. Standalone and block preparation have diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 6396895fd..617e48ea7 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -12,6 +12,7 @@ import Ix.Kernel.Verify.Consistency.SourceAgreement import Ix.Kernel.Verify.Consistency.SourceCache import Ix.Kernel.Verify.Consistency.Dependencies import Ix.Kernel.Verify.Consistency.CheapBeta +import Ix.Kernel.Verify.Consistency.SynthesisCache import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -139,6 +140,13 @@ private def recursiveCacheRoots : Array Lean.Name := #[ ``CachedConstantInferenceSupport.sound_after_inference, ``BinderInference.sortAfterInference ] +private def synthesisCacheRoots : Array Lean.Name := #[ + ``infer_full_success_cache, ``InferenceCacheHit.fromFullRun, ``infer_full_replay_closed, + ``SynthesisInference.reuseFull, ``SynthesisInference.reuseFullClosed, ``SynthesisInference.reuseFullAcross, + ``BetaPublicWhnfPlan.inference_frame, ``BetaPublicWhnfPlan.policy, + ``BetaPiExposure.inference_frame, ``BetaPiExposure.policy +] + private def lazyCacheRoots : Array Lean.Name := #[ ``ingressAnonStandalone_cache, ``ingressAnonAddrShallow_cache, ``lazyIngressAddr_cache, ``tryGetConst_standalone_cache, ``getConst_standalone_cache, @@ -650,7 +658,7 @@ def roots : Array RootAllowance := #[ }) ++ (binderWalkerRoots ++ cacheKeyRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction -}) ++ (atomicRoots ++ instantiationRoots ++ recursiveCacheRoots ++ lazyCacheRoots ++ +}) ++ (atomicRoots ++ instantiationRoots ++ recursiveCacheRoots ++ synthesisCacheRoots ++ lazyCacheRoots ++ ownedLoaderRoots ++ recursiveStateRoots ++ sourceAgreementRoots ++ sourceCacheRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction diff --git a/Ix/Kernel/Verify/Consistency/Beta.lean b/Ix/Kernel/Verify/Consistency/Beta.lean index c102c4f74..a35eb6ddc 100644 --- a/Ix/Kernel/Verify/Consistency/Beta.lean +++ b/Ix/Kernel/Verify/Consistency/Beta.lean @@ -41,10 +41,9 @@ theorem SynthesisInference.lambda_type {β : Type u} (support : SynthesisInference resolve entries locals context bounds fuel before source (.lam condition domain body) type level) : ∃ codomain, type = .forallE condition domain codomain := by - cases support with - | known inference => exact inference.lambda_type - | reuseType inference => exact inference.lambda_type - | lam | lamBeta => exact ⟨_, rfl⟩ + have leading := support.lambdaPrefix + cases leading with + | lam => exact ⟨_, rfl⟩ private theorem BinderInference.no_direct_beta {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} @@ -85,6 +84,8 @@ def SynthesisInference.betaResultOrigin {β : Type u} {resolve : Address → Opt cases support with | known inference _ => exact False.elim inference.no_direct_beta | reuseType inference => exact False.elim inference.no_direct_beta + | cached tree priorAgreement priorReading _ _ _ _ => + exact tree.betaResultOrigin contextOrigin priorAgreement priorReading | app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful => obtain ⟨rfl, rfl⟩ := lambda_inference_domain functionTree @@ -109,34 +110,38 @@ def SynthesisInference.betaResultOrigin {β : Type u} {resolve : Address → Opt (.source (.applicationBetaArgument contextOrigin trace functionTree exposure exposureCoherent argumentTree keyedAgreement functionReads argumentReads conditions hashPath comparisonFaithful)) .root +termination_by sizeOf support + /-- When the lambda body applies its parameter, its actual body checks supply the next reduction origin after beta exposes the supplied lambda. The source's result type comes from the preceding check even if the body was originally inferred at a type requiring cheap-beta conversion. -/ -def SynthesisInference.betaNextOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} +private def SynthesisInference.betaNextOriginAux {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel count : Nat} {before : TcState .anon} {source : KExpr .anon} {level bound : VLevel} - {condition headCondition : Certified.PropWhen} {domain binder inner : AExpr β} + {condition headCondition : Certified.PropWhen} {domain binder inner type : AExpr β} {initialArguments arguments : List (AExpr β)} (support : SynthesisInference resolve entries locals context bounds fuel before source (.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) - ((AExpr.lam headCondition binder inner).appN initialArguments)) (.sort level) bound) + ((AExpr.lam headCondition binder inner).appN initialArguments)) type bound) (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) (agreement : LocalContextReading resolve locals before.lctx context) (reading : readScopedExpr? resolve locals source = some (AExpr.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) ((AExpr.lam headCondition binder inner).appN initialArguments)).erase) - (enough : count ≤ inner.lambdaDepth + 1) : + (enough : count ≤ inner.lambdaDepth + 1) (resultEquation : AExpr.sort level = type) : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries context (.lam headCondition binder inner) (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments))) count level := by have resultOrigin := support.betaResultOrigin contextOrigin agreement reading - generalize resultEquation : AExpr.sort level = resultType at support + rw [← resultEquation] at resultOrigin cases support with | known inference _ => exact False.elim inference.no_direct_beta | reuseType inference => exact False.elim inference.no_direct_beta + | cached tree priorAgreement priorReading _ _ _ _ => + exact tree.betaNextOriginAux contextOrigin priorAgreement priorReading enough resultEquation | app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful => obtain ⟨rfl, rfl⟩ := lambda_inference_domain functionTree @@ -165,6 +170,31 @@ def SynthesisInference.betaNextOrigin {β : Type u} {resolve : Address → Optio (by simpa only [AExpr.inst_variable_appN] using resultOrigin) simpa only [AExpr.liftN_zero, List.map_id'] using flattened +termination_by sizeOf support + +/-- Retain the next lambda origin through any number of actual inference +cache hits, with the same source type and dependent argument checks. -/ +def SynthesisInference.betaNextOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel count : Nat} + {before : TcState .anon} {source : KExpr .anon} {level bound : VLevel} + {condition headCondition : Certified.PropWhen} {domain binder inner : AExpr β} + {initialArguments arguments : List (AExpr β)} + (support : SynthesisInference resolve entries locals context bounds fuel before source + (.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)) (.sort level) bound) + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = + some (AExpr.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)).erase) + (enough : count ≤ inner.lambdaDepth + 1) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries context + (.lam headCondition binder inner) + (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments))) + count level := + support.betaNextOriginAux contextOrigin agreement reading enough rfl + /-- A supported successful inference of a source beta redex derives both equality with the substitution result and typing of that result. No run of inference on the generated substitution is required. -/ @@ -186,6 +216,8 @@ theorem SynthesisInference.beta_sound {β : Type u} cases support with | known inference => exact False.elim inference.no_direct_beta | reuseType inference => exact False.elim inference.no_direct_beta + | cached tree priorAgreement priorReading priorRun _ _ _ => + exact tree.beta_sound formed priorAgreement priorReading priorRun | app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful => obtain ⟨codomain, sameProduct⟩ := functionTree.lambda_type @@ -222,6 +254,8 @@ theorem SynthesisInference.beta_sound {β : Type u} exact ⟨ConversionClaim.beta functionTyped typedArgument, TypingClaim.betaResult functionTyped typedArgument⟩ +termination_by sizeOf support + /-- Two successive beta prefixes can use different lambda origins. The second comes from an actual supplied argument, rather than a fresh check of the intermediate reduction result. -/ diff --git a/Ix/Kernel/Verify/Consistency/BetaInference.lean b/Ix/Kernel/Verify/Consistency/BetaInference.lean index 0c52caeb8..4b7285e3f 100644 --- a/Ix/Kernel/Verify/Consistency/BetaInference.lean +++ b/Ix/Kernel/Verify/Consistency/BetaInference.lean @@ -72,6 +72,8 @@ def SynthesisInference.betaTyping {β : Type u} {resolve : Address → Option (C ContextFormation.{u,v} incoming incomingContext incomingBounds → SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type := match support with + | .cached tree priorAgreement priorReading priorRun _ _ _ => fun contextOrigin _ _ _ formed => + tree.betaTyping contextOrigin priorAgreement priorReading priorRun formed | node@(.known inference _) => fun contextOrigin agreement reading accepted _ => inference.betaTyping (.source (.checked contextOrigin node agreement reading accepted)) agreement reading | node@(.reuseType inference ..) => fun contextOrigin agreement reading accepted _ => diff --git a/Ix/Kernel/Verify/Consistency/BetaTrace.lean b/Ix/Kernel/Verify/Consistency/BetaTrace.lean index 015c209bc..36c6ecd2e 100644 --- a/Ix/Kernel/Verify/Consistency/BetaTrace.lean +++ b/Ix/Kernel/Verify/Consistency/BetaTrace.lean @@ -147,6 +147,8 @@ def SynthesisInference.spineOrigin {β : Type u} {resolve : Address → Option ( subst head exact ⟨type, .source (.checked contextOrigin support agreement reading accepted), support.lambdaPrefix, .nil _⟩ else match support with + | .cached tree priorAgreement priorReading priorRun _ _ _ => + tree.spineOrigin contextOrigin priorAgreement priorReading priorRun head arguments same | .known inference formation => inference.spineOrigin (.checked contextOrigin (.known inference formation) agreement reading accepted) agreement reading head arguments same diff --git a/Ix/Kernel/Verify/Consistency/InferenceCache.lean b/Ix/Kernel/Verify/Consistency/InferenceCache.lean index 41a5019aa..b1653b109 100644 --- a/Ix/Kernel/Verify/Consistency/InferenceCache.lean +++ b/Ix/Kernel/Verify/Consistency/InferenceCache.lean @@ -334,4 +334,38 @@ theorem PreservesInferenceCache.withInferOnly {key : Address × Address} {action cases run : action {before with inferOnly := true} <;> rw [run] at frame <;> exact ⟨frame.full, frame.only, frame.constants⟩ +/-- A successful full inference leaves its exact result in the full cache, +whether the call reused an entry or executed the uncached branch. -/ +theorem infer_full_success_cache {source result : KExpr .anon} {methods : Methods .anon} + {before keyed after : TcState .anon} {key : Address × Address} + (full : before.inferOnly = false) + (keyRun : TcM.inferKey source before = .ok key keyed) + (accepted : RecM.infer source methods before = .ok result after) : + after.env.inferCache[key]? = some result := by + rcases observeInferenceCache keyRun with ⟨hit, keyEq, _⟩ | ⟨miss, keyEq, _⟩ + · rw [hit.run] at accepted + cases accepted + rcases hit.selected with found | ⟨_, only, _⟩ + · simpa only [keyEq] using found + · rw [full] at only + cases only + · obtain ⟨middle, _, written⟩ := infer_uncached_success_state miss accepted + rw [written, full] + simp only [Bool.false_eq_true, if_false, keyEq, Std.HashMap.getElem?_insert_self] + +/-- Actual full inference and a proved frame construct the later cache hit. +The later policy is unrestricted because the full cache has priority. -/ +def InferenceCacheHit.fromFullRun {source result : KExpr .anon} {methods : Methods .anon} + {before keyed after current currentKeyed : TcState .anon} {key : Address × Address} + (full : before.inferOnly = false) + (keyRun : TcM.inferKey source before = .ok key keyed) + (accepted : RecM.infer source methods before = .ok result after) + (frame : InferenceCacheFrame key after current) + (currentKeyRun : TcM.inferKey source current = .ok key currentKeyed) : + InferenceCacheHit current source := by + have keyFrame := PreservesInferenceCache.inferKey key source current + rw [currentKeyRun] at keyFrame + exact ⟨key, currentKeyed, result, currentKeyRun, + .inl (keyFrame.full.trans (frame.full.trans (infer_full_success_cache full keyRun accepted)))⟩ + end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean index fb2b247ec..05223854a 100644 --- a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean +++ b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean @@ -4,6 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.BinderInference +import Ix.Kernel.Verify.Consistency.WhnfCacheFrame import Ix.Kernel.Verify.Consistency.SourceOwnershipCheck /-! @@ -174,7 +175,7 @@ actual key; constant misses use a loaded source or verified standalone lazy loading, with finite walker resources at the actual post-lookup state. The lambda domain call is included even though semantic checking can omit its typing subtree once the declared type's formation has been established. -/ -inductive InferenceCacheTrace : Nat → TcState .anon → KExpr .anon → Type +inductive InferenceCacheTrace : Nat → TcState .anon → KExpr .anon → Type (u + 1) | hit {fuel before term} (hit : InferenceCacheHit before term) : InferenceCacheTrace fuel before term | sort {fuel before level info} (miss : UncachedInference before (.sort level info)) : @@ -199,6 +200,17 @@ inductive InferenceCacheTrace : Nat → TcState .anon → KExpr .anon → Type (functionTree : InferenceCacheTrace fuel miss.keyed fn) (argumentTree : InferenceCacheTrace fuel trace.functionState arg) : InferenceCacheTrace (fuel + 1) before (.app fn arg info) + | appBeta {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals fuel before fn arg info term condition domain body} + (full : before.inferOnly = false) + (miss : UncachedInference before (.app fn arg info)) + (trace : ApplicationWhnfInferenceTrace fuel miss.keyed fn arg) + (exposure : BetaPiExposure resolve locals fuel trace.functionState trace.functionType + term condition domain body trace.domain trace.codomain) + (hashPath : (trace.argumentType.addr == trace.domain.addr) = true) + (functionTree : InferenceCacheTrace fuel miss.keyed fn) + (argumentTree : InferenceCacheTrace fuel trace.exposedState arg) : + InferenceCacheTrace (fuel + 1) before (.app fn arg info) | forallE {fuel before name bi domain body info} (miss : UncachedInference before (.all name bi domain body info)) (trace : ForallInferenceTrace fuel miss.keyed name bi domain body) @@ -211,6 +223,12 @@ inductive InferenceCacheTrace : Nat → TcState .anon → KExpr .anon → Type (domainTree : InferenceCacheTrace fuel miss.keyed domain) (bodyTree : InferenceCacheTrace fuel trace.openedState trace.opened) : InferenceCacheTrace (fuel + 1) before (.lam name bi domain body info) + | lamBody {fuel before name bi domain body info} (full : before.inferOnly = false) + (miss : UncachedInference before (.lam name bi domain body info)) + (trace : LambdaBodyTrace fuel miss.keyed name bi domain body) + (domainTree : InferenceCacheTrace fuel miss.keyed domain) + (bodyTree : InferenceCacheTrace fuel trace.openedState trace.opened) : + InferenceCacheTrace (fuel + 1) before (.lam name bi domain body info) /-- The finite write footprint is computed from the operational tree. Cache hits contribute no key; recursive calls and each outer insertion are included. -/ @@ -218,7 +236,8 @@ def InferenceCacheTrace.writes {fuel : Nat} {before : TcState .anon} {term : KEx InferenceCacheTrace fuel before term → List (Address × Address) | .hit _ => [] | .sort miss | .fvar miss | .const miss .. | .lazyConst miss .. => [miss.key] - | .app _ miss _ _ first second | .forallE miss _ first second | .lam _ miss _ first second => + | .app _ miss _ _ first second | .appBeta _ miss _ _ _ first second | + .forallE miss _ first second | .lam _ miss _ first second | .lamBody _ miss _ first second => miss.key :: (first.writes ++ second.writes) /-- Leaf construction inspects the real cache policy; callers need not @@ -396,6 +415,24 @@ theorem InferenceCacheTrace.frame {fuel : Nat} {before after : TcState .anon} rw [state] exact ⟨(domainFrame.trans (opening.trans bodyFrame)).trans (.of_eq rfl rfl rfl), bodyPolicy.trans ((openBinder_policy trace.openRun).trans domainPolicy)⟩ + | appBeta full miss trace exposure hashPath functionTree argumentTree functionIH argumentIH => + simp only [writes, List.mem_cons, List.mem_append, not_or] at outside + apply infer_miss_frame miss (Ne.symm outside.1) accepted + intro middle run + rw [full] at run + obtain ⟨functionFrame, functionPolicy⟩ := functionIH outside.2.1 trace.functionRun + obtain ⟨argumentFrame, argumentPolicy⟩ := argumentIH outside.2.2 trace.argumentRun + have exposureFrame : InferenceCacheFrame key trace.functionState trace.exposedState := by + rw [trace.exposure_state exposure] + exact exposure.inference_frame key + have exposurePolicy : trace.exposedState.inferOnly = trace.functionState.inferOnly := by + rw [trace.exposure_state exposure] + exact exposure.policy + obtain ⟨comparisonFrame, comparisonPolicy⟩ := isDefEq_hash_frame hashPath trace.compareRun key + rw [(trace.output_state run).2] + exact ⟨(functionFrame.trans (exposureFrame.trans (argumentFrame.trans comparisonFrame))).trans + (.of_eq rfl rfl rfl), + comparisonPolicy.trans (argumentPolicy.trans (exposurePolicy.trans functionPolicy))⟩ | lam full miss trace domainTree bodyTree domainIH bodyIH => simp only [writes, List.mem_cons, List.mem_append, not_or] at outside apply infer_miss_frame miss (Ne.symm outside.1) accepted @@ -408,6 +445,18 @@ theorem InferenceCacheTrace.frame {fuel : Nat} {before after : TcState .anon} rw [state] exact ⟨(domainFrame.trans (opening.trans bodyFrame)).trans (.of_eq rfl rfl rfl), bodyPolicy.trans ((openBinder_policy trace.openRun).trans domainPolicy)⟩ + | lamBody full miss trace domainTree bodyTree domainIH bodyIH => + simp only [writes, List.mem_cons, List.mem_append, not_or] at outside + apply infer_miss_frame miss (Ne.symm outside.1) accepted + intro middle run + rw [full] at run + obtain ⟨domainFrame, domainPolicy⟩ := domainIH outside.2.1 trace.domainRun + obtain ⟨bodyFrame, bodyPolicy⟩ := bodyIH outside.2.2 trace.bodyRun + have opening := openBinder_frame key trace.openRun + have state := (trace.output_state run).2 + rw [state] + exact ⟨(domainFrame.trans (opening.trans bodyFrame)).trans (.of_eq rfl rfl rfl), + bodyPolicy.trans ((openBinder_policy trace.openRun).trans domainPolicy)⟩ /-- A verified constant call needs no separately constructed operational tree: the real key and cache selection build its hit or lazy-miss leaf. -/ @@ -421,8 +470,8 @@ theorem infer_verifiedConst_cache_frame {fuel : Nat} {before keyed after : TcSta (accepted : RecM.infer (.const id arguments info) (methodsN fuel) before = .ok result after) : InferenceCacheFrame watched before after ∧ after.inferOnly = before.inferOnly := by rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, keyEq, stateEq⟩ - · exact (InferenceCacheTrace.hit (fuel := fuel) hit).frame (by simp [InferenceCacheTrace.writes]) accepted - · let tree : InferenceCacheTrace fuel before (.const id arguments info) := + · exact (InferenceCacheTrace.hit.{0} (fuel := fuel) hit).frame (by simp [InferenceCacheTrace.writes]) accepted + · let tree : InferenceCacheTrace.{0} fuel before (.const id arguments info) := .lazyConst miss (by simpa only [stateEq] using loader) (by simpa only [stateEq] using resources) apply tree.frame _ accepted diff --git a/Ix/Kernel/Verify/Consistency/RecursiveState.lean b/Ix/Kernel/Verify/Consistency/RecursiveState.lean index 05d344521..a7281c8da 100644 --- a/Ix/Kernel/Verify/Consistency/RecursiveState.lean +++ b/Ix/Kernel/Verify/Consistency/RecursiveState.lean @@ -295,7 +295,7 @@ theorem OwnedInferenceTrace.preserves {source : Ixon.Env} {fuel : Nat} loader and post-lookup coherence from the single initial state invariant. -/ def OwnedInferenceTrace.toCacheTrace {source : Ixon.Env} {fuel : Nat} {before : TcState .anon} {term : KExpr .anon} (tree : OwnedInferenceTrace fuel before term) - (valid : InferenceStateInvariant source before) : InferenceCacheTrace fuel before term := + (valid : InferenceStateInvariant source before) : InferenceCacheTrace.{0} fuel before term := match tree with | .hit cached => .hit cached | .sort miss => .sort miss diff --git a/Ix/Kernel/Verify/Consistency/SynthesisCache.lean b/Ix/Kernel/Verify/Consistency/SynthesisCache.lean new file mode 100644 index 000000000..3ddb89179 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SynthesisCache.lean @@ -0,0 +1,92 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.SynthesisInference +import Ix.Kernel.Verify.Consistency.RecursiveCache + +/-! +Reuse the actual check behind a full inference-cache entry. The successful +call supplies the stored result, its reading follows from that check, and a +proved frame preserves the entry. The retained tree still supplies lambda +domains, dependent codomain checks, and subsequent beta derivations. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v w + +/-- A completed full check supplies a later synthesis cache node. Neither +cache selection nor the cached type's semantics is a new premise. The key +runs keep context-sensitive key computation explicit. -/ +def SynthesisInference.reuseFull {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {fuel nextFuel : Nat} + {before keyed after current currentKeyed : TcState .anon} {key : Address × Address} + {source result : KExpr .anon} {term type : AExpr β} {level : VLevel} + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (full : before.inferOnly = false) + (keyRun : TcM.inferKey source before = .ok key keyed) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) + (frame : InferenceCacheFrame key after current) + (currentKeyRun : TcM.inferKey source current = .ok key currentKeyed) : + SynthesisInference resolve entries locals context bounds nextFuel current source term type level := + .cached tree agreement reading accepted + (InferenceCacheHit.fromFullRun full keyRun accepted frame currentKeyRun) rfl + (tree.sound formed agreement reading accepted).1 + +/-- When the raw source has no loose variables, both keys are computed from +its address. Registered free variables may still occur in the source. -/ +def SynthesisInference.reuseFullClosed {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {fuel nextFuel : Nat} {before after current : TcState .anon} + {source result : KExpr .anon} {term type : AExpr β} {level : VLevel} + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (full : before.inferOnly = false) (closed : source.lbr = 0) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) + (frame : InferenceCacheFrame (source.addr, emptyCtxAddr) after current) : + SynthesisInference resolve entries locals context bounds nextFuel current source term type level := + tree.reuseFull formed agreement reading full (inferKey_closed closed before) accepted frame + (inferKey_closed closed current) + +/-- The preserved full entry executes immediately, at any method-table fuel +and under either current checking policy. -/ +theorem infer_full_replay_closed {source result : KExpr .anon} + {methods currentMethods : Methods .anon} {before after current : TcState .anon} + (full : before.inferOnly = false) (closed : source.lbr = 0) + (accepted : RecM.infer source methods before = .ok result after) + (frame : InferenceCacheFrame (source.addr, emptyCtxAddr) after current) : + RecM.infer source currentMethods current = .ok result current := + (InferenceCacheHit.fromFullRun full (inferKey_closed closed before) accepted frame + (inferKey_closed closed current)).run currentMethods + +/-- Intervening recursive inference computes its write footprint and the +required frame. Public beta Pi exposure and changed cheap-beta lambda bodies +are included in this operational trace. -/ +def SynthesisInference.reuseFullAcross {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {fuel nextFuel otherFuel : Nat} + {before after current : TcState .anon} {source result other otherResult : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (full : before.inferOnly = false) (closed : source.lbr = 0) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) + (intervening : InferenceCacheTrace.{w} otherFuel after other) + (outside : (source.addr, emptyCtxAddr) ∉ intervening.writes) + (otherRun : RecM.infer other (methodsN otherFuel) after = .ok otherResult current) : + SynthesisInference resolve entries locals context bounds nextFuel current source term type level := + tree.reuseFullClosed formed agreement reading full closed accepted (intervening.frame outside otherRun).1 + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean index 66eba0eaf..4cb61f852 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean @@ -40,6 +40,16 @@ inductive SynthesisInference {β : Type u} (inference : BinderInference resolve entries locals context fuel before source term type) (formation : TypeFormation resolve entries context type level) : SynthesisInference resolve entries locals context bounds fuel before source term type level + | cached {entries locals context bounds fuel before source term type level + priorLocals priorFuel priorBefore priorAfter priorResult} + (tree : SynthesisInference resolve entries priorLocals context bounds priorFuel priorBefore source term type level) + (priorAgreement : LocalContextReading resolve priorLocals priorBefore.lctx context) + (priorReading : readScopedExpr? resolve priorLocals source = some term.erase) + (priorRun : RecM.infer source (methodsN priorFuel) priorBefore = .ok priorResult priorAfter) + (hit : InferenceCacheHit before source) + (cacheMatch : hit.cached = priorResult) + (resultReading : readScopedExpr? resolve locals priorResult = some type.erase) : + SynthesisInference resolve entries locals context bounds fuel before source term type level | reuseType {earlier entries locals context bounds fuel before source term type typeFuel typeBefore typeAfter typeSource typeResult declaredType level typeBound arguments} (inference : BinderInference resolve entries locals context fuel before source term type) @@ -1030,10 +1040,13 @@ def SynthesisInference.forallBodyCheck {β : Type u} {resolve : Address → Opti cases support with | known inference => exact inference.forallBodyCheck agreement reading | reuseType inference => exact inference.forallBodyCheck agreement reading + | cached tree priorAgreement priorReading _ _ _ _ => + exact tree.forallBodyCheck priorAgreement priorReading | forallE miss trace opening absent domainTree bodyTree => obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading exact trace.bodyCheck opening absent domainTree bodyTree (miss.localContext.symm ▸ agreement) domainReads bodyReads +termination_by sizeOf support private theorem list_reverse_induction {α : Type u} {motive : List α → Prop} (nil : motive []) @@ -1117,6 +1130,8 @@ def SynthesisInference.variableSpineOrigin {β : Type u} {resolve : Address → match support with | .known inference _ | .reuseType inference .. | .fvar inference .. => inference.variableSpineOrigin agreement reading index arguments headEquals + | .cached tree priorAgreement priorReading _ _ _ _ => + tree.variableSpineOrigin contextOrigin priorAgreement priorReading index arguments headEquals | .app _ miss trace functionTree argumentTree conditions hashPath comparisonFaithful _ _ _ _ _ _ => by obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement @@ -1195,6 +1210,8 @@ def SynthesisInference.lambdaBodyVariableSpine {β : Type u} {resolve : Address exact inference.lambdaBodyVariableSpine agreement reading | reuseType inference => exact inference.lambdaBodyVariableSpine agreement reading + | cached tree priorAgreement priorReading _ _ _ _ => + exact tree.lambdaBodyVariableSpine contextOrigin priorAgreement priorReading | lam full miss trace opening absent domainTree bodyTree conditionAgrees constructed bound coherent closingFaithful faithful => obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading @@ -1216,6 +1233,8 @@ def SynthesisInference.lambdaBodyVariableSpine {β : Type u} {resolve : Address (contextOrigin.push domainTree keyedAgreement domainReads trace.domainRun) openedAgreement openedReads index arguments rfl⟩ +termination_by sizeOf support + theorem BinderInference.lambdaPrefix {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {fuel : Nat} @@ -1234,6 +1253,7 @@ theorem SynthesisInference.lambdaPrefix {β : Type u} LambdaPrefix term type term.lambdaDepth := match support with | .known inference _ => inference.lambdaPrefix + | .cached tree .. => tree.lambdaPrefix | .reuseType inference _ _ _ _ _ => inference.lambdaPrefix | .lam _ _ _ _ _ _ bodyTree _ _ _ _ _ _ => .lam bodyTree.lambdaPrefix | .lamBeta _ _ _ _ _ _ bodyTree _ _ _ _ _ _ _ => by @@ -1305,6 +1325,12 @@ theorem SynthesisInference.soundWithSpine {β : Type u} obtain ⟨reads, checked⟩ := inference.sound agreement reading accepted have typed := checked.typing formation.sound exact ⟨reads, typed, formation.sound, inference.lambdaSpineTyping typed⟩ + | .cached tree priorAgreement priorReading priorRun hit cacheMatch resultReading => by + obtain ⟨_, typed, formation, spine⟩ := + tree.soundWithSpine formed priorAgreement priorReading priorRun + rw [hit.run] at accepted + cases accepted + exact ⟨cacheMatch.symm ▸ resultReading, typed, formation, spine⟩ | .reuseType inference typeTree extension typeReading typeRun same => by obtain ⟨reads, checked⟩ := inference.sound agreement reading accepted obtain ⟨_, typeTyped, _, _⟩ := diff --git a/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean b/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean new file mode 100644 index 000000000..12a2805fe --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean @@ -0,0 +1,78 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.ApplicationWhnf +import Ix.Kernel.Verify.Consistency.InferenceCache + +/-! Public beta WHNF and Pi exposure preserve inference cache entries. +Their computed cache writes affect only the WHNF partitions. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +private theorem betaWhnfKey_policy (source : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey source before).2.inferOnly = before.inferOnly := by + unfold betaWhnfKey + split + · rfl + · dsimp only + split <;> rfl + +private theorem betaWhnfPrefix_policy (before : TcState .anon) : + (betaWhnfPrefix before).inferOnly = before.inferOnly := by + unfold betaWhnfPrefix + split <;> rfl + +private theorem betaWhnfCharge_policy (before : TcState .anon) : + (betaWhnfCharge before).inferOnly = before.inferOnly := by + unfold betaWhnfCharge + split <;> rfl + +theorem BetaPublicWhnfPlan.inference_frame {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) + (key : Address × Address) : InferenceCacheFrame key before plan.after := by + obtain ⟨table, reduced⟩ := plan.path.frame + apply InferenceCacheFrame.of_eq <;> + simp only [BetaPublicWhnfPlan.after, BetaPublicWhnf.after, BetaPublicWhnf.noDeltaAfter, + BetaPublicWhnf.coreAfter, reduced, (BetaPublicWhnf.coreKey_fields source before).1] + +theorem BetaPublicWhnfPlan.policy {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : + plan.after.inferOnly = before.inferOnly := by + obtain ⟨table, reduced⟩ := plan.path.frame + simp only [BetaPublicWhnfPlan.after, BetaPublicWhnf.after, BetaPublicWhnf.noDeltaAfter, + BetaPublicWhnf.coreAfter, reduced, BetaPublicWhnf.coreKey, BetaPublicWhnf.noDeltaKey, + BetaPublicWhnf.outerKey, betaWhnfKey_policy, betaWhnfPrefix_policy, betaWhnfCharge_policy] + +theorem BetaPiExposure.inference_frame {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source rawDomain rawBody : KExpr .anon} {term domain body : AExpr β} {condition : Certified.PropWhen} + (exposure : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody) + (key : Address × Address) : InferenceCacheFrame key before exposure.after := by + cases exposure with + | reduce plan => exact plan.inference_frame key + | cached origin coherent hit => + apply InferenceCacheFrame.of_eq <;> + simp only [BetaPiExposure.after, BetaPublicWhnf.outerKey, betaWhnfKey_environment, + (betaWhnfPrefix_fields before).1] + +theorem BetaPiExposure.policy {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source rawDomain rawBody : KExpr .anon} {term domain body : AExpr β} {condition : Certified.PropWhen} + (exposure : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody) : + exposure.after.inferOnly = before.inferOnly := by + cases exposure with + | reduce plan => exact plan.policy + | cached origin coherent hit => + simp only [BetaPiExposure.after, BetaPublicWhnf.outerKey, betaWhnfKey_policy, betaWhnfPrefix_policy] + +end Ix.Kernel.Consistency diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 4e0ea7de0..92c33d9d4 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -3139,6 +3139,167 @@ private def sourceCacheCases : TestSeq := ++ test "source cache: a forged same-key application violates source result agreement" sourceCacheForeignWrite +/-- Replay a full result with no recursive methods or shared fuel. A +different inference-only entry makes full-result priority observable. -/ +private def compositeReplayAt (source expected : KExpr .anon) (state : TcState .anon) : Bool := + let key := (source.addr, emptyCtxAddr) + let sentinel := KExpr.mkSort (m := .anon) (.mkSucc levelTwo) + source.lbr == 0 && expected != sentinel && state.env.inferCache[key]? == some expected && + [false, true].all fun inferOnly => + let before := { state with recFuel := 0, inferOnly, env := { state.env with + inferOnlyCache := state.env.inferOnlyCache.insert key sentinel } } + match (RecM.infer source).run (methodsN 0) before with + | .error _ _ => false + | .ok result after => result == expected && after.recFuel == 0 && + after.env.inferCache[key]? == some expected && after.env.inferOnlyCache[key]? == some sentinel && + after.env.intern.exprs.size == before.env.intern.exprs.size && + after.env.nextFVarId == before.env.nextFVarId && after.lctx.size == before.lctx.size && + after.ctxId == before.ctxId && after.deqCalls == before.deqCalls + +private def compositeCacheSource (shape : Nat) : KExpr .anon × KExpr .anon := + let sort := KExpr.mkSort (m := .anon) levelOne + let type := KExpr.mkAll () () sort sort + let lambda := KExpr.mkLam () () sort (.mkVar 0 ()) + if shape == 0 then (.mkApp lambda (.mkSort .mkZero), sort) + else if shape == 1 then (type, .mkSort levelTwo) + else (lambda, type) + +/-- Actual composite checks remain cached through a recursive call that +loads a polymorphic dependency, two scopes, and repeated hits. Clearing +invalidates the hit, and another full check establishes it again. -/ +private def compositeCacheHistory (shape : Nat) : Bool := + let (source, expected) := compositeCacheSource shape + let action : RecM .anon Bool := do + let initial ← RecM.inferCall source + if initial != expected || !compositeReplayAt source expected (← get) then return false + let unrelated := KExpr.mkApp (.mkConst ⟨polymorphicIdentity.2, ()⟩ #[levelOne]) (.mkSort .mkZero) + let _ ← RecM.inferCall unrelated + if !compositeReplayAt source expected (← get) then return false + for _ in [0, 1] do + let passed ← RecM.withLctxScope do + let _ ← TcM.openBinder () () (.mkSort levelOne) (.mkVar 0 ()) + let result ← RecM.inferCall source + return result == expected && compositeReplayAt source expected (← get) + if !passed then return false + modify fun state => { state with env := state.env.clearReductionCaches } + let cleared ← get + if cleared.env.inferCache[(source.addr, emptyCtxAddr)]?.isSome then return false + match (RecM.infer source).run (methodsN 0) cleared with + | .error .maxRecFuel _ => pure () + | _ => return false + let result ← RecM.inferCall source + return result == expected && compositeReplayAt source expected (← get) + match TcM.runRec action (TcState.newLazyAnon polymorphicIdentity.1) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +/-- A real inference-only check cannot justify a full-mode cache hit. The +subsequent validating call must execute before zero-fuel replay is possible. -/ +private def compositeOnlyCache (shape : Nat) : Bool := + let (source, expected) := compositeCacheSource shape + let action : RecM .anon Bool := do + let only ← RecM.inferOnlyCall source + let before ← get + let key := (source.addr, emptyCtxAddr) + if only != expected || before.env.inferCache[key]?.isSome || + before.env.inferOnlyCache[key]? != some expected then return false + match (RecM.infer source).run (methodsN 0) before with + | .error .maxRecFuel _ => pure () + | _ => return false + let full ← RecM.inferCall source + return full == expected && compositeReplayAt source expected (← get) + match TcM.runRec action (TcState.newLazyAnon {}) with + | .ok passed _ => passed + | .error _ _ => false + +/-- The watched lambda survives application inference with beta Pi +exposure or lambda inference whose body type changes under cheap beta. +The application case also repeats cold and warm public Pi exposure. -/ +private def compositeAcrossBetaInference (changedLambda typeLevel : Bool) : Bool := + let level := if typeLevel then Ixon.Univ.succ .zero else .zero + let (env, target) := if changedLambda then cheapLambdaConstant level 0 true + else piExposureDeclaration level + let (watched, watchedType) := compositeCacheSource 2 + let action : RecM .anon Bool := do + let initial ← RecM.inferCall watched + if initial != watchedType then return false + let .defn _ _ _ _ _ _ expected value _ _ ← TcM.getConst (m := .anon) ⟨target, ()⟩ | return false + let result ← RecM.inferCall value + if result != expected || !compositeReplayAt watched watchedType (← get) || + !compositeReplayAt value expected (← get) then return false + if changedLambda then + let .lam _ _ _ body _ := value | return false + let original ← RecM.inferCall body + let reduced ← TcM.runIntern (cheapBetaReduce original) + return original != reduced && compositeReplayAt value expected (← get) + else + let .app fn _ _ := value | return false + let functionType ← RecM.inferCall fn + let exposed ← observePiExposure functionType expected expected true false + if !exposed || !compositeReplayAt value expected (← get) then return false + let (domain, body) ← RecM.ensureForallDirect functionType + return domain == expected && body == expected && compositeReplayAt value expected (← get) + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +/-- A cached lambda is subsequently used as a checked function and reduced. +Its original full check must still supply the lambda's domain and body. -/ +private def compositeCachedLambdaBeta : Bool := + let (lambda, type) := compositeCacheSource 2 + let argument := KExpr.mkSort (m := .anon) .mkZero + let application := KExpr.mkApp lambda argument + let action : RecM .anon Bool := do + let result ← RecM.inferCall lambda + if result != type || !compositeReplayAt lambda type (← get) then return false + let applicationType ← RecM.inferCall application + let reduced ← RecM.whnfCoreWithFlagsUncached application .DEF_EQ_CORE + let reducedType ← RecM.inferCall reduced + return reduced == argument && applicationType == reducedType && + compositeReplayAt lambda type (← get) && compositeReplayAt application applicationType (← get) + match TcM.runRec action (TcState.newLazyAnon {}) with + | .ok passed _ => passed + | .error _ _ => false + +/-- Identical binder syntax opened in different scopes receives different +free-variable IDs, expression addresses, and cached dependent types. -/ +private def compositeLocalCacheKeys : Bool := + let action : RecM .anon Bool := do + let checkScope : RecM .anon (Bool × Address × Address) := RecM.withLctxScope do + let (carrier, _) ← TcM.openBinder () () (.mkSort levelOne) (.mkVar 0 ()) + let source := KExpr.mkLam () () carrier (.mkVar 0 ()) + let expected := KExpr.mkAll () () carrier carrier + let result ← RecM.inferCall source + return (result == expected && compositeReplayAt source expected (← get), source.addr, expected.addr) + let first ← checkScope + let second ← checkScope + return first.1 && second.1 && first.2.1 != second.2.1 && first.2.2 != second.2.2 + match TcM.runRec action (TcState.newLazyAnon {}) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +private def compositeCacheCases : TestSeq := + test "composite cache: applications survive lazy inference, scopes, replay, and clearing" + (compositeCacheHistory 0) + ++ test "composite cache: Pi checks survive lazy inference, scopes, replay, and clearing" + (compositeCacheHistory 1) + ++ test "composite cache: lambda checks survive lazy inference, scopes, replay, and clearing" + (compositeCacheHistory 2) + ++ test "composite cache: inference-only applications cannot supply full-mode hits" + (compositeOnlyCache 0) + ++ test "composite cache: inference-only Pi checks cannot supply full-mode hits" + (compositeOnlyCache 1) + ++ test "composite cache: inference-only lambdas cannot supply full-mode hits" + (compositeOnlyCache 2) + ++ test "composite cache: beta application and cold/warm Pi exposure preserve full results" + (compositeAcrossBetaInference false false && compositeAcrossBetaInference false true) + ++ test "composite cache: changed cheap-beta lambda inference preserves full results" + (compositeAcrossBetaInference true false && compositeAcrossBetaInference true true) + ++ test "composite cache: a reused lambda remains usable in later checked beta reduction" + compositeCachedLambdaBeta + ++ test "composite cache: fresh local IDs separate captured lambda keys and result types" + compositeLocalCacheKeys + /-- Definitions declare their own parameters. A two-parameter alias uses `max u v` to instantiate a one-parameter definition; a wrapper applies that alias beneath binders. Subsequent declarations specialize them at Prop and @@ -3233,6 +3394,6 @@ public def suite : List TestSeq := cheapApplicationCases, exposedLambdaCases, repeatedBetaCases, betaTraceCases, hereditaryBetaCases, piExposureCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, - sourceAgreementCases, sourceCacheCases, polymorphicDefinitionCases] + sourceAgreementCases, sourceCacheCases, compositeCacheCases, polymorphicDefinitionCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 5abd34b9d..180825ce1 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -292,9 +292,13 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) - Every source key occurs in the `buildAnonWork` result, and every work item represents an axiom or a definition. Lookup, routing, and reset witnesses identify the checked `KConst`. -- Application, forall, and lambda nodes in the inference witnesses miss every - eligible cache partition. Full mode may have a populated inference-only - partition. Sort nodes use a miss or a hit equal to the canonical successor +- Application, forall, and lambda nodes either execute a supported miss or + reuse an earlier successful synthesis check. Reuse retains that check's + tree and raw execution under the same semantic interface and annotated + context. A full call supplies the exact cache entry, and a proved frame + derives the later hit and result reading without a new semantic premise. + Cold nodes miss every eligible partition; full mode may have a populated + inference-only partition. Sort nodes use a miss or a hit equal to the canonical successor sort; a maintained agreement can construct that leaf's cache observation. Local-variable hits must match the current production declaration type. Constant nodes use either the existing miss rule or a selected cache hit. @@ -346,8 +350,9 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) checker operation and automatic trace construction remain open. Catalog constant and sort entries are preserved even when written by these recursive calls. General semantic cache invariants remain open. - The operational trace can frame any selected cache hit, while semantic typing - of composite hits remains outside `BinderInference`. + The operational trace can frame any selected cache hit. `SynthesisInference` + also retains and reuses actual checks of composite terms; `BinderInference` + keeps its existing constant, local, and sort hit rules. - Binder definitions supply finite inference trees for both the value and its separately checked declared type, exact source readings, closed annotated syntax, and references to the preceding interface. Recursive calls use the actual @@ -517,7 +522,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 653 exact theorem boundaries. The production +The consistency target checks 663 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -637,6 +642,20 @@ The existing synthesis admission and environment roots include this case. These 38 additional audited boundaries introduce no axioms or native proofs. Mixed cache states, general WHNF cache agreement, other reducers, and general inference-resource construction remain open. +`SynthesisInference.cached` retains the original tree behind an inference +cache hit. Its soundness and beta derivations reuse the actual lambda-body, +dependent codomain, and argument checks. `reuseFull` derives the cached result +from a successful full call and a cache frame; `reuseFullClosed` computes the +key for sources without loose variables, including registered free variables. +`reuseFullAcross` derives the frame from an intervening recursive trace and +its write footprint. Such traces now include beta Pi exposure and changed +cheap-beta lambda bodies. Public beta WHNF and Pi exposure preserve inference +entries through their computed WHNF-cache updates. `infer_full_replay_closed` +proves immediate reuse with any method table and either current checking +policy. These ten additional audited roots retain the existing axiom boundary. +Reuse currently keeps the same semantic interface and annotated local context; +general composite cache histories and their context/interface transport remain +open. Clearing invalidates the stored entry and requires a new full check. Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, argument order, and rejection of wrong arities and out-of-range parameters. @@ -786,7 +805,13 @@ Definition-cycle regressions use content-addressed standalone and mutual declarations, including a self-justifying theorem, a two-member cycle, type cycles, lets, shared syntax, and binders. They check repeated member failures, acyclic forward references, cache clearing, and the partial/unsafe policy. -The unit suite contains 697 checks. The anonymous differential additionally +Ten composite-cache regressions cover real application, Pi, and lambda checks, +zero-fuel replay under both policies, inference-only exclusion from full mode, +lazy loading, scope changes, clearing and repopulation, beta Pi exposure, +changed cheap-beta body types, later reduction of cached lambdas, and distinct +captured-local keys. These execute production; the proof resources are checked +separately by the consistency target. +The unit suite contains 707 checks. The anonymous differential additionally serializes eight cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. diff --git a/docs/theory.md b/docs/theory.md index 3edc578f5..9afc628ff 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -120,7 +120,17 @@ agreement constructs sort leaves. A finite operational trace now carries preservation through recursive applications, dependent types, and full-mode lambdas for keys absent from the recorded writes. It derives later constant witnesses and sort leaves without repeated cache-hit observations. Frames allow -new declarations while retaining old ones. The actual verified loader preserves +reuse of composite synthesis checks as well: a successful full call supplies +the exact cached result, and a later hit retains its original inference tree. +The same tree supplies the lambda domains, dependent codomain checks, and +body checks required by subsequent beta reductions. Reuse keeps the same +semantic interface and annotated context. Full-cache priority permits either +later checking policy and requires no recursion fuel. Cache frames derive +reuse across supported recursive inference, including beta Pi exposure and +changed cheap-beta lambda bodies; public beta WHNF preserves all inference +entries. General composite cache histories and context/interface transport +remain open. Frames allow new declarations while retaining old ones. +The actual verified loader preserves inference caches on success and failure, including partial intern progress and deduplicated faults. This covers standalone and mutual-block loading. Block publication requires every prepared entry to agree with any old declaration at From 95d07ce1da0aa90651770d1d4e1e6dffceeb2114 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 09:45:02 -0400 Subject: [PATCH 35/63] Transport retained synthesis cache checks Preserve complete executed inference checks through interface extension and local insertion, retaining Pi domains, codomains, lambda bodies, and dependent argument checks for subsequent beta reductions. Recover the original syntax from lifted application and binder shapes without semantic cache premises. Build concrete full-cache resources from successful inference, then derive replay through binder-opening and recursive-call frames. Materialize these resources in the original synthesis recursion, including its successive and hereditary beta, declaration-admission, and environment-model proofs. Validation: 705 exact consistency audit roots, 709 kernel unit checks, check-theory, and IxKernelVerify/IxCompileVerify. No axioms or native proofs added. General checker refinement and composite cache histories remain open. --- Ix/Kernel/Verify/Consistency.lean | 13 +- Ix/Kernel/Verify/Consistency/Audit.lean | 48 +- Ix/Kernel/Verify/Consistency/Beta.lean | 121 +++- .../Verify/Consistency/BetaInference.lean | 20 + Ix/Kernel/Verify/Consistency/BetaTrace.lean | 127 ---- Ix/Kernel/Verify/Consistency/BetaTyping.lean | 25 + .../Verify/Consistency/ContextTransport.lean | 195 ++++++ .../Verify/Consistency/SynthesisCache.lean | 123 ++++ .../Consistency/SynthesisInference.lean | 606 +++++++++++++++--- Tests/Ix/Kernel/Consistency.lean | 92 +++ docs/kernel-verification.md | 39 +- docs/theory.md | 15 +- 12 files changed, 1156 insertions(+), 268 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/ContextTransport.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index ab2fb4d9a..d45e27577 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -28,6 +28,7 @@ import Ix.Kernel.Verify.Consistency.ApplicationWhnf import Ix.Kernel.Verify.Consistency.BinderInference import Ix.Kernel.Verify.Consistency.Formation import Ix.Kernel.Verify.Consistency.ContextInsertion +import Ix.Kernel.Verify.Consistency.ContextTransport import Ix.Kernel.Verify.Consistency.SynthesisInference import Ix.Kernel.Verify.Consistency.SynthesisCache import Ix.Kernel.Verify.Consistency.BetaSubstitution @@ -157,7 +158,12 @@ retains its original inference tree, local reading, and execution, so lambda domains and dependent codomain checks remain available to later beta proofs. Successful full inference establishes the exact stored result; cache frames derive its later selection under either checking policy, even with zero fuel. -These helpers keep the same semantic interface and annotated local context. +The retained checks also cross interface growth and insertion of locals. +Structural inversion of lifted syntax recovers the original lambda head, +domain, body, and arguments, preserving Pi codomain checks and hereditary +beta origins. A concrete full-cache resource derives its readings and +selection from the earlier call and remains usable in the original synthesis +recursion after actual binder opening and supported recursive inference. Cold composite nodes still require misses in every eligible partition; full mode leaves the inference-only partition unconstrained at a miss. Applications use syntactic or supported beta Pi exposure, full argument checking, @@ -181,9 +187,8 @@ later constant witnesses and sort leaves without new cache-hit observations. The trace also covers beta Pi exposure and changed cheap-beta lambda bodies. Public beta WHNF and Pi exposure preserve inference entries at every key; their computed writes affect the WHNF caches. These frames also preserve the -earlier full composite checks. General semantic cache histories, context and -interface transport of composite entries, and automatic trace construction -remain open. +earlier full composite checks. General semantic cache histories, agreement +at arbitrary written keys, and automatic trace construction remain open. Frames allow declaration growth while retaining every old declaration. Verified lazy loading derives such a frame on success and failure, including partial conversion state and fault deduplication. Standalone and block preparation have diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 617e48ea7..d8d560bea 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -147,6 +147,52 @@ private def synthesisCacheRoots : Array Lean.Name := #[ ``BetaPiExposure.inference_frame, ``BetaPiExposure.policy ] +private def cacheTransportRoots : Array RootAllowance := #[ + { root := ``liftedSpineView, standardAxioms := #[``propext] }, + { root := ``liftedForallView }, + { root := ``liftedLambdaView }, + { root := ``liftedApplicationView }, + { root := ``liftedVariableSpineView, standardAxioms := #[``propext] }, + { root := ``liftN_sort_inv }, + { root := ``InterfaceExtends.conversion, standardAxioms := standard }, + { root := ``InterfaceExtends.argumentSpine, standardAxioms := standard }, + { root := ``InterfaceExtends.lambdaSpine, standardAxioms := standard }, + { root := ``ContextInsertion.argumentSpine, standardAxioms := standard }, + { root := ``ContextInsertion.lambdaSpine, standardAxioms := standard } +] ++ #[ + ``SynthesisRetainedCheck.forallBody, + ``SynthesisRetainedCheck.variableSpineOrigin, + ``SynthesisRetainedCheck.lambdaBodyVariableSpine, + ``SynthesisRetainedCheck.lambdaPrefix, + ``SynthesisRetainedCheck.origin, + ``SynthesisRetainedCheck.typeOrigin, + ``SynthesisRetainedCheck.spineOrigin, + ``SynthesisRetainedCheck.soundWithSpine, + ``SynthesisRetainedCheck.betaNextOrigin, + ``SynthesisRetainedCheck.betaTyping, + ``SynthesisVariableSpineOrigin.weakenAt, + ``SynthesisVariableSpineOrigin.rebase, + ``SynthesisArgumentSpineOrigin.weakenAt, + ``SynthesisArgumentSpineOrigin.rebase, + ``SynthesisSpineOrigin.extend, + ``SynthesisSpineOrigin.weakenAt, + ``SynthesisSpineOrigin.rebase, + ``SynthesisReductionOrigin.weakenAt, + ``SynthesisBetaTyping.extend, + ``SynthesisBetaTyping.rebase, + ``CachedSynthesisCheck.ofFull, + ``CachedSynthesisCheck.ofClosedFull, + ``CachedSynthesisCheck.frame, + ``CachedSynthesisCheck.extend, + ``CachedSynthesisCheck.weaken, + ``CachedSynthesisCheck.afterOpenBinder, + ``CachedSynthesisCheck.afterInference, + ``CachedSynthesisCheck.hit, + ``CachedSynthesisCheck.support, + ``CachedSynthesisCheck.run, + ``CachedSynthesisCheck.sound +].map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }) + private def lazyCacheRoots : Array Lean.Name := #[ ``ingressAnonStandalone_cache, ``ingressAnonAddrShallow_cache, ``lazyIngressAddr_cache, ``tryGetConst_standalone_cache, ``getConst_standalone_cache, @@ -666,7 +712,7 @@ def roots : Array RootAllowance := #[ root, standardAxioms := standard, nativeAxioms := productionNative, forbiddenDependencies := forbiddenProduction }) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots ++ exposedOriginRoots ++ - repeatedBetaRoots ++ betaTraceRoots ++ hereditaryBetaRoots ++ piExposureRoots).map (fun allowance => { + repeatedBetaRoots ++ betaTraceRoots ++ hereditaryBetaRoots ++ piExposureRoots ++ cacheTransportRoots).map (fun allowance => { allowance with forbiddenDependencies := forbiddenProduction }) ++ #[{ root := ``extend_atomic_definition, standardAxioms := standard }] diff --git a/Ix/Kernel/Verify/Consistency/Beta.lean b/Ix/Kernel/Verify/Consistency/Beta.lean index a35eb6ddc..2e5758e00 100644 --- a/Ix/Kernel/Verify/Consistency/Beta.lean +++ b/Ix/Kernel/Verify/Consistency/Beta.lean @@ -86,6 +86,11 @@ def SynthesisInference.betaResultOrigin {β : Type u} {resolve : Address → Opt | reuseType inference => exact False.elim inference.no_direct_beta | cached tree priorAgreement priorReading _ _ _ _ => exact tree.betaResultOrigin contextOrigin priorAgreement priorReading + | cachedFrom check _ _ => + have trace := (check.spineOrigin (.lam condition domain body) [argument] rfl).betaTrace + (count := 1) (by simp only [AExpr.lambdaDepth]; omega) + simpa only [AExpr.betaPrefix, AExpr.appN_cons, AExpr.appN_nil] using + SynthesisTypingOrigin.rebase contextOrigin (.reduced trace) | app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful => obtain ⟨rfl, rfl⟩ := lambda_inference_domain functionTree @@ -112,6 +117,21 @@ def SynthesisInference.betaResultOrigin {β : Type u} {resolve : Address → Opt termination_by sizeOf support +/-- Inserting a local preserves both ends of a retained reduction and +all arguments in its checked prefix. -/ +def SynthesisReductionOrigin.weakenAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext source target : Model.Context β} + {incomingBounds : List VLevel} {head : AExpr β} {arguments : List (AExpr β)} + {count cutoff : Nat} {level : VLevel} + (origin : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries source + head arguments count level) (insertion : ContextInsertion source target cutoff) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries target + (head.liftN 1 cutoff) (arguments.map (AExpr.liftN 1 · cutoff)) count level := + .traced (by simpa only [AExpr.liftN_appN, AExpr.liftN_betaPrefix, AExpr.liftN] using + SynthesisBetaTrace.weakenAt (.origin origin) insertion) + +mutual + /-- When the lambda body applies its parameter, its actual body checks supply the next reduction origin after beta exposes the supplied lambda. The source's result type comes from the preceding check even if the body @@ -120,30 +140,32 @@ private def SynthesisInference.betaNextOriginAux {β : Type u} {resolve : Addres {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel count : Nat} {before : TcState .anon} {source : KExpr .anon} {level bound : VLevel} - {condition headCondition : Certified.PropWhen} {domain binder inner type : AExpr β} + {condition headCondition : Certified.PropWhen} {term domain binder inner type : AExpr β} {initialArguments arguments : List (AExpr β)} - (support : SynthesisInference resolve entries locals context bounds fuel before source - (.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) - ((AExpr.lam headCondition binder inner).appN initialArguments)) type bound) + (support : SynthesisInference resolve entries locals context bounds fuel before source term type bound) (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = - some (AExpr.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) - ((AExpr.lam headCondition binder inner).appN initialArguments)).erase) + (reading : readScopedExpr? resolve locals source = some term.erase) (enough : count ≤ inner.lambdaDepth + 1) (resultEquation : AExpr.sort level = type) : + term = .app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments) → SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries context (.lam headCondition binder inner) (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments))) - count level := by - have resultOrigin := support.betaResultOrigin contextOrigin agreement reading - rw [← resultEquation] at resultOrigin - cases support with - | known inference _ => exact False.elim inference.no_direct_beta - | reuseType inference => exact False.elim inference.no_direct_beta - | cached tree priorAgreement priorReading _ _ _ _ => - exact tree.betaNextOriginAux contextOrigin priorAgreement priorReading enough resultEquation - | app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful - bodyConstructed argConstructed bodyBound argBound coherent faithful => + count level := + match support with + | .known inference _ | .reuseType inference .. => fun same => by + cases same + exact False.elim inference.no_direct_beta + | .cached tree priorAgreement priorReading _ _ _ _ => fun same => + tree.betaNextOriginAux contextOrigin priorAgreement priorReading enough resultEquation same + | .cachedFrom check _ _ => fun same => + .traced (.rebase contextOrigin (.origin (check.betaNextOrigin enough resultEquation same))) + | node@(.app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful + bodyConstructed argConstructed bodyBound argBound coherent faithful) => fun same => by + cases same + have resultOrigin := node.betaResultOrigin contextOrigin agreement reading + rw [← resultEquation] at resultOrigin obtain ⟨rfl, rfl⟩ := lambda_inference_domain functionTree obtain ⟨functionReads, argumentReads⟩ := readScopedExpr?_app_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement @@ -153,8 +175,11 @@ private def SynthesisInference.betaNextOriginAux {β : Type u} {resolve : Addres conditions hashPath comparisonFaithful) .root enough (by simpa only [AExpr.inst_variable_appN] using resultOrigin) simpa only [AExpr.liftN_zero, List.map_id'] using flattened - | appBeta full miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath - comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful => + | node@(.appBeta full miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath + comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful) => fun shape => by + cases shape + have resultOrigin := node.betaResultOrigin contextOrigin agreement reading + rw [← resultEquation] at resultOrigin have same := reduction.rigid (by obtain ⟨_, rfl⟩ := functionTree.lambda_type intro fn arg same @@ -170,7 +195,57 @@ private def SynthesisInference.betaNextOriginAux {β : Type u} {resolve : Addres (by simpa only [AExpr.inst_variable_appN] using resultOrigin) simpa only [AExpr.liftN_zero, List.map_id'] using flattened -termination_by sizeOf support + | .fvar .. | .forallE .. | .lam .. | .lamBeta .. => fun same => by cases same +termination_by structural support + +def SynthesisRetainedCheck.betaNextOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level bound : VLevel} {count : Nat} + {condition headCondition : Certified.PropWhen} {domain binder inner : AExpr β} + {initialArguments arguments : List (AExpr β)} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type bound) + (enough : count ≤ inner.lambdaDepth + 1) (resultEquation : AExpr.sort level = type) : + term = .app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments) → + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries context + (.lam headCondition binder inner) + (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments))) + count level := + match check with + | .source contextOrigin tree agreement reading _ => fun same => + tree.betaNextOriginAux contextOrigin agreement reading enough resultEquation same + | .extend prior extension => fun same => + (prior.betaNextOrigin enough resultEquation same).map (.pure (.extend .refl extension)) + | .rebase origin prior => fun same => + .traced (.rebase origin (.origin (prior.betaNextOrigin enough resultEquation same))) + | .weakenAt (cutoff := cutoff) prior insertion => fun same => by + let application := liftedApplicationView same + let lambda := liftedLambdaView application.functionEq.symm + let body := liftedVariableSpineView lambda.bodyEq.symm + let argument := liftedSpineView application.argumentEq.symm + let head := liftedLambdaView argument.headEq.symm + have originalIndex : body.originalIndex = 0 := by + have shifted := body.indexEq + simp only [liftVar] at shifted + split at shifted <;> omega + have bodyEq : lambda.originalBody = (AExpr.bvar 0).appN body.originalArguments := by + simpa only [originalIndex] using body.sourceEq + have functionEq := lambda.sourceEq.trans + (congrArg (AExpr.lam condition lambda.originalDomain) bodyEq) + have argumentEq := argument.sourceEq.trans + (congrArg (AExpr.appN · argument.originalArguments) head.sourceEq) + have originalEnough : count ≤ head.originalBody.lambdaDepth + 1 := by + simpa only [head.bodyEq, AExpr.lambdaDepth_liftN] using enough + have originalResult := (liftN_sort_inv resultEquation.symm).symm + let child := prior.betaNextOrigin originalEnough originalResult + (application.sourceEq.trans (congr (congrArg AExpr.app functionEq) argumentEq)) + have lifted := child.weakenAt insertion + simpa only [AExpr.liftN_appN, AExpr.liftN_betaPrefix, List.map_append, List.map_map, + Function.comp_def, AExpr.liftN_inst_zero, AExpr.liftN, head.domainEq, head.bodyEq, + argument.argumentsEq, body.argumentsEq] using lifted +termination_by structural check + +end /-- Retain the next lambda origin through any number of actual inference cache hits, with the same source type and dependent argument checks. -/ @@ -193,7 +268,7 @@ def SynthesisInference.betaNextOrigin {β : Type u} {resolve : Address → Optio (.lam headCondition binder inner) (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments))) count level := - support.betaNextOriginAux contextOrigin agreement reading enough rfl + support.betaNextOriginAux contextOrigin agreement reading enough rfl rfl /-- A supported successful inference of a source beta redex derives both equality with the substitution result and typing of that result. No run @@ -218,6 +293,10 @@ theorem SynthesisInference.beta_sound {β : Type u} | reuseType inference => exact False.elim inference.no_direct_beta | cached tree priorAgreement priorReading priorRun _ _ _ => exact tree.beta_sound formed priorAgreement priorReading priorRun + | cachedFrom check _ _ => + have spine := (check.soundWithSpine formed).2.2 + simpa only [AExpr.betaPrefix, AExpr.appN_cons, AExpr.appN_nil] using + spine.betaPrefix (arguments := [argument]) (count := 1) (by omega) | app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful => obtain ⟨codomain, sameProduct⟩ := functionTree.lambda_type diff --git a/Ix/Kernel/Verify/Consistency/BetaInference.lean b/Ix/Kernel/Verify/Consistency/BetaInference.lean index 4b7285e3f..edc7c919c 100644 --- a/Ix/Kernel/Verify/Consistency/BetaInference.lean +++ b/Ix/Kernel/Verify/Consistency/BetaInference.lean @@ -57,6 +57,8 @@ def BinderInference.betaTyping {β : Type u} {resolve : Address → Option (Cons exact .lam origin (bodyTree.betaTyping (.lambdaBody origin) openedAgreement openedReading) termination_by structural support +mutual + /-- Every currently supported source-inference branch supplies its own beta derivation. Lambda inference's changed body type becomes a forward conversion node, retaining the original body's complete checked structure. -/ @@ -74,6 +76,8 @@ def SynthesisInference.betaTyping {β : Type u} {resolve : Address → Option (C match support with | .cached tree priorAgreement priorReading priorRun _ _ _ => fun contextOrigin _ _ _ formed => tree.betaTyping contextOrigin priorAgreement priorReading priorRun formed + | .cachedFrom check _ _ => fun contextOrigin _ _ _ formed => + (check.betaTyping (contextOrigin.sound formed)).rebase contextOrigin | node@(.known inference _) => fun contextOrigin agreement reading accepted _ => inference.betaTyping (.source (.checked contextOrigin node agreement reading accepted)) agreement reading | node@(.reuseType inference ..) => fun contextOrigin agreement reading accepted _ => @@ -147,6 +151,22 @@ def SynthesisInference.betaTyping {β : Type u} {resolve : Address → Option (C (.convert inner (.rebase contextOrigin (.origin reductionOrigin))) termination_by structural support +def SynthesisRetainedCheck.betaTyping {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) : + ContextFormation.{u,v} incoming incomingContext incomingBounds → + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type := + match check with + | .source contextOrigin tree agreement reading accepted => fun formed => + tree.betaTyping contextOrigin agreement reading accepted formed + | .extend prior extension => fun formed => (prior.betaTyping formed).extend extension + | .weakenAt prior insertion => fun formed => (prior.betaTyping formed).weakenAt insertion + | .rebase origin prior => fun formed => (prior.betaTyping (origin.sound formed)).rebase origin +termination_by structural check + +end + theorem SynthesisInference.beta_steps_sound {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} diff --git a/Ix/Kernel/Verify/Consistency/BetaTrace.lean b/Ix/Kernel/Verify/Consistency/BetaTrace.lean index 36c6ecd2e..9062acfe0 100644 --- a/Ix/Kernel/Verify/Consistency/BetaTrace.lean +++ b/Ix/Kernel/Verify/Consistency/BetaTrace.lean @@ -63,133 +63,6 @@ def SynthesisBetaTrace.beta {β : Type u} {resolve : Address → Option (ConstRe SynthesisBetaTrace.prefix functionOrigin (.lam (.zero _ _)) ((SynthesisArgumentSpineOrigin.nil _).snoc argumentOrigin) -/-- The head's check and every argument check of an actual application -spine, retained as data for subsequent reductions and substitutions. -/ -structure SynthesisSpineOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) - (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) - (entries : Model.Environment β) (context : Model.Context β) - (head : AExpr β) (arguments : List (AExpr β)) (type : AExpr β) where - headType : AExpr β - headOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context head headType - leading : LambdaPrefix head headType head.lambdaDepth - argumentsOrigin : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds - entries context headType arguments type - -private theorem appN_last {β : Type u} {head : AExpr β} {arguments : List (AExpr β)} - (nonempty : arguments ≠ []) : - head.appN arguments = (head.appN arguments.dropLast).app (arguments.getLast nonempty) := by - calc - head.appN arguments = head.appN (arguments.dropLast ++ [arguments.getLast nonempty]) := - congrArg (AExpr.appN head) (List.dropLast_concat_getLast nonempty).symm - _ = _ := by simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] - -private theorem nonapp_spine_empty {β : Type u} {term head : AExpr β} {arguments : List (AExpr β)} - (notApp : ∀ fn arg, term ≠ .app fn arg) (same : term = head.appN arguments) : arguments = [] := by - by_contra nonempty - exact notApp _ _ (same.trans (appN_last nonempty)) - -private theorem app_spine_parts {β : Type u} {fn arg head : AExpr β} {arguments : List (AExpr β)} - (nonempty : arguments ≠ []) (same : fn.app arg = head.appN arguments) : - arguments = arguments.dropLast ++ [arg] ∧ fn = head.appN arguments.dropLast := by - have parts := AExpr.app.inj (same.trans (appN_last nonempty)) - exact ⟨by rw [parts.2]; exact (List.dropLast_concat_getLast nonempty).symm, parts.1⟩ - -def BinderInference.spineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} - {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} - (support : BinderInference resolve entries locals context fuel before source term type) - (checked : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = some term.erase) - (head : AExpr β) (arguments : List (AExpr β)) (same : term = head.appN arguments) : - SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := - if empty : arguments = [] then by - subst arguments - simp only [AExpr.appN_nil] at same - subst head - exact ⟨type, .source checked, support.lambdaPrefix, .nil _⟩ - else match support with - | .app _ miss trace functionTree functionHead argumentTree conditions hashPath comparisonFaithful - _ _ _ _ _ _ => by - obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - have parts := app_spine_parts empty same - have prior := functionTree.spineOrigin (incoming := incoming) (incomingContext := incomingContext) - (incomingBounds := incomingBounds) - (.binderHead functionTree functionHead keyedAgreement functionReading trace.functionRun) - keyedAgreement functionReading head arguments.dropLast parts.2 - refine ⟨prior.headType, prior.headOrigin, prior.leading, ?_⟩ - simpa only [← parts.1] using prior.argumentsOrigin.snoc - (.source (.binderArgument trace functionTree functionHead argumentTree keyedAgreement - functionReading argumentReading conditions hashPath comparisonFaithful)) - | .sort .. | .cachedSort .. | .fvar .. | .const .. | .polymorphic .. | .cachedConst .. | .forallE .. | .lam .. => by - exact False.elim (empty (nonapp_spine_empty (by intro fn arg same; cases same) same)) -termination_by structural support - -/-- Recover every application argument and the head's original checked -lambda prefix from the executed source inference, including binder-backed -local and constant spines. -/ -def SynthesisInference.spineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel : Nat} - {before after : TcState .anon} {source result : KExpr .anon} {term type : AExpr β} {level : VLevel} - (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) - (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = some term.erase) - (accepted : RecM.infer source (methodsN fuel) before = .ok result after) - (head : AExpr β) (arguments : List (AExpr β)) (same : term = head.appN arguments) : - SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := - if empty : arguments = [] then by - subst arguments - simp only [AExpr.appN_nil] at same - subst head - exact ⟨type, .source (.checked contextOrigin support agreement reading accepted), support.lambdaPrefix, .nil _⟩ - else match support with - | .cached tree priorAgreement priorReading priorRun _ _ _ => - tree.spineOrigin contextOrigin priorAgreement priorReading priorRun head arguments same - | .known inference formation => - inference.spineOrigin (.checked contextOrigin (.known inference formation) agreement reading accepted) - agreement reading head arguments same - | .reuseType inference typeTree extension typeReading typeRun equivalent => - inference.spineOrigin - (.checked contextOrigin (.reuseType inference typeTree extension typeReading typeRun equivalent) - agreement reading accepted) agreement reading head arguments same - | .app _ miss trace functionTree argumentTree conditions hashPath comparisonFaithful _ _ _ _ _ _ => by - obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - have parts := app_spine_parts empty same - have prior := functionTree.spineOrigin contextOrigin keyedAgreement functionReading trace.functionRun - head arguments.dropLast parts.2 - refine ⟨prior.headType, prior.headOrigin, prior.leading, ?_⟩ - simpa only [← parts.1] using prior.argumentsOrigin.snoc - (.source (.applicationArgument contextOrigin trace functionTree argumentTree keyedAgreement - functionReading argumentReading conditions hashPath comparisonFaithful)) - | .appBeta _ miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath - comparisonFaithful _ _ _ _ _ _ => by - obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - have parts := app_spine_parts empty same - have prior := functionTree.spineOrigin contextOrigin keyedAgreement functionReading trace.functionRun - head arguments.dropLast parts.2 - refine ⟨prior.headType, prior.headOrigin, prior.leading, ?_⟩ - simpa only [← parts.1] using (prior.argumentsOrigin.convert (.rebase contextOrigin reduction)).snoc - (.source (.applicationBetaArgument contextOrigin trace functionTree exposure exposureCoherent argumentTree - keyedAgreement functionReading argumentReading conditions hashPath comparisonFaithful)) - | .fvar .. | .forallE .. | .lam .. | .lamBeta .. => by - exact False.elim (empty (nonapp_spine_empty (by intro fn arg same; cases same) same)) -termination_by structural support - -def SynthesisSpineOrigin.betaTrace {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {head type : AExpr β} {arguments : List (AExpr β)} {count : Nat} - (origin : SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type) - (enough : count ≤ head.lambdaDepth) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context - (head.appN arguments) (AExpr.betaPrefix count head arguments) type := - .prefix origin.headOrigin (origin.leading.truncate enough) origin.argumentsOrigin - /-- Any original lambda prefix selected from the source inference starts a composable trace whose result can be used as another typing origin. -/ def SynthesisInference.betaSpineTrace {β : Type u} {resolve : Address → Option (ConstRef β)} diff --git a/Ix/Kernel/Verify/Consistency/BetaTyping.lean b/Ix/Kernel/Verify/Consistency/BetaTyping.lean index 61662d9b3..3ea1e2c64 100644 --- a/Ix/Kernel/Verify/Consistency/BetaTyping.lean +++ b/Ix/Kernel/Verify/Consistency/BetaTyping.lean @@ -124,6 +124,31 @@ def weakenAt {source target : Model.Context β} {cutoff : Nat} {term type : AExp | .convert prior trace => .convert (prior.weakenAt insertion) (.weakenAt trace insertion) termination_by structural typing +def extend {later : Model.Environment β} {context : Model.Context β} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) + (extension : InterfaceExtends entries later) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds later context term type := + match typing with + | .atom origin shape => .atom (origin.extend extension) shape + | .bvar origin found => .bvar (origin.extend extension) found + | .lam origin inner => .lam (origin.extend extension) (inner.extend extension) + | .app function argument => .app (function.extend extension) (argument.extend extension) + | .convert prior trace => .convert (prior.extend extension) (.extend trace extension) +termination_by structural typing + +def rebase {priorIncoming : Model.Environment β} {priorContext context : Model.Context β} + {priorBounds : List VLevel} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve priorIncoming priorContext priorBounds entries context term type) + (origin : SynthesisContext resolve incoming incomingContext incomingBounds priorIncoming priorContext priorBounds) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type := + match typing with + | .atom checked shape => .atom (.rebase origin checked) shape + | .bvar checked found => .bvar (.rebase origin checked) found + | .lam checked inner => .lam (.rebase origin checked) (inner.rebase origin) + | .app function argument => .app (function.rebase origin) (argument.rebase origin) + | .convert prior trace => .convert (prior.rebase origin) (.rebase origin trace) +termination_by structural typing + end SynthesisBetaTyping /-- The internal substitution walker builds this data itself as it passes diff --git a/Ix/Kernel/Verify/Consistency/ContextTransport.lean b/Ix/Kernel/Verify/Consistency/ContextTransport.lean new file mode 100644 index 000000000..06cf5fa54 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/ContextTransport.lean @@ -0,0 +1,195 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.ContextInsertion + +/-! Preserve checked application spines when an interface grows or a local +is inserted. Inverting the lifted syntax retains each original argument and +lambda domain, rather than trying to recover them from semantic typing. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +structure LiftedSpineView {β : Type u} (source head : AExpr β) (arguments : List (AExpr β)) + (count cutoff : Nat) where + originalHead : AExpr β + originalArguments : List (AExpr β) + sourceEq : source = originalHead.appN originalArguments + headEq : head = originalHead.liftN count cutoff + argumentsEq : arguments = originalArguments.map (AExpr.liftN count · cutoff) + +/-- Syntax inversion is data-producing: later retained checks can recover +their original head and arguments without a semantic or choice premise. -/ +def liftedSpineView {β : Type u} {source head : AExpr β} {arguments : List (AExpr β)} + {count cutoff : Nat} (same : source.liftN count cutoff = head.appN arguments) : + LiftedSpineView source head arguments count cutoff := by + by_cases empty : arguments = [] + · subst arguments + exact ⟨source, [], rfl, same.symm, rfl⟩ + · have last : head.appN arguments = + (head.appN arguments.dropLast).app (arguments.getLast empty) := by + calc + head.appN arguments = head.appN (arguments.dropLast ++ [arguments.getLast empty]) := + congrArg (AExpr.appN head) (List.dropLast_concat_getLast empty).symm + _ = _ := by simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] + rw [last] at same + cases source with + | app fn arg => + obtain ⟨functionEq, argumentEq⟩ := AExpr.app.inj same + let prior := liftedSpineView functionEq + refine ⟨prior.originalHead, prior.originalArguments ++ [arg], ?_, prior.headEq, ?_⟩ + · simpa only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] using + congrArg (AExpr.app · arg) prior.sourceEq + · rw [List.map_append, List.map_cons, List.map_nil, ← prior.argumentsEq, argumentEq] + exact (List.dropLast_concat_getLast empty).symm + | bvar | sort | const | lam | forallE | proj | natLit => cases same +termination_by structural source + +structure LiftedForallView {β : Type u} (source : AExpr β) (condition : Certified.PropWhen) + (domain body : AExpr β) (count cutoff : Nat) where + originalDomain : AExpr β + originalBody : AExpr β + sourceEq : source = .forallE condition originalDomain originalBody + domainEq : domain = originalDomain.liftN count cutoff + bodyEq : body = originalBody.liftN count (cutoff + 1) + +def liftedForallView {β : Type u} {source domain body : AExpr β} {condition : Certified.PropWhen} + {count cutoff : Nat} (same : source.liftN count cutoff = .forallE condition domain body) : + LiftedForallView source condition domain body count cutoff := by + cases source with + | forallE originalCondition originalDomain originalBody => + cases same + exact ⟨originalDomain, originalBody, rfl, rfl, rfl⟩ + | bvar | sort | const | app | lam | proj | natLit => cases same + +structure LiftedLambdaView {β : Type u} (source : AExpr β) (condition : Certified.PropWhen) + (domain body : AExpr β) (count cutoff : Nat) where + originalDomain : AExpr β + originalBody : AExpr β + sourceEq : source = .lam condition originalDomain originalBody + domainEq : domain = originalDomain.liftN count cutoff + bodyEq : body = originalBody.liftN count (cutoff + 1) + +def liftedLambdaView {β : Type u} {source domain body : AExpr β} {condition : Certified.PropWhen} + {count cutoff : Nat} (same : source.liftN count cutoff = .lam condition domain body) : + LiftedLambdaView source condition domain body count cutoff := by + cases source with + | lam originalCondition originalDomain originalBody => + cases same + exact ⟨originalDomain, originalBody, rfl, rfl, rfl⟩ + | bvar | sort | const | app | forallE | proj | natLit => cases same + +structure LiftedApplicationView {β : Type u} (source fn arg : AExpr β) (count cutoff : Nat) where + originalFunction : AExpr β + originalArgument : AExpr β + sourceEq : source = .app originalFunction originalArgument + functionEq : fn = originalFunction.liftN count cutoff + argumentEq : arg = originalArgument.liftN count cutoff + +def liftedApplicationView {β : Type u} {source fn arg : AExpr β} {count cutoff : Nat} + (same : source.liftN count cutoff = .app fn arg) : LiftedApplicationView source fn arg count cutoff := by + cases source with + | app originalFunction originalArgument => + cases same + exact ⟨originalFunction, originalArgument, rfl, rfl, rfl⟩ + | bvar | sort | const | lam | forallE | proj | natLit => cases same + +structure LiftedVariableSpineView {β : Type u} (source : AExpr β) (index : Nat) + (arguments : List (AExpr β)) (count cutoff : Nat) where + originalIndex : Nat + originalArguments : List (AExpr β) + sourceEq : source = (AExpr.bvar originalIndex).appN originalArguments + indexEq : index = liftVar count originalIndex cutoff + argumentsEq : arguments = originalArguments.map (AExpr.liftN count · cutoff) + +def liftedVariableSpineView {β : Type u} {source : AExpr β} {index count cutoff : Nat} + {arguments : List (AExpr β)} (same : source.liftN count cutoff = (AExpr.bvar index).appN arguments) : + LiftedVariableSpineView source index arguments count cutoff := by + let view := liftedSpineView same + have headEq := view.headEq + cases original : view.originalHead with + | bvar originalIndex => + rw [original] at headEq + refine ⟨originalIndex, view.originalArguments, ?_, AExpr.bvar.inj headEq, view.argumentsEq⟩ + simpa only [original] using view.sourceEq + | sort | const | app | lam | forallE | proj | natLit => + rw [original] at headEq + cases headEq + +theorem liftN_sort_inv {β : Type u} {source : AExpr β} {level : VLevel} {count cutoff : Nat} + (same : source.liftN count cutoff = .sort level) : source = .sort level := by + cases source <;> cases same + rfl + +theorem InterfaceExtends.conversion {β : Type u} {earlier later : Model.Environment β} + (extension : InterfaceExtends earlier later) {context : Model.Context β} {left right : AExpr β} + (converted : ConversionClaim.{u,v} earlier context left right) : + ConversionClaim.{u,v} later context left right := by + intro V _ constants realizes levels env valid + exact converted V constants (extension.realizes realizes) levels env valid + +theorem InterfaceExtends.argumentSpine {β : Type u} {earlier later : Model.Environment β} + (extension : InterfaceExtends earlier later) {context : Model.Context β} + {start result : AExpr β} {arguments : List (AExpr β)} + (spine : ArgumentSpine.{u,v} earlier context start arguments result) : + ArgumentSpine.{u,v} later context start arguments result := by + induction spine with + | nil => exact .nil _ + | cons checked rest ih => exact .cons (extension.typing checked) ih + | convert rigid converted formed rest ih => + exact .convert rigid (extension.conversion converted) (extension.typing formed) ih + +theorem InterfaceExtends.lambdaSpine {β : Type u} {earlier later : Model.Environment β} + (extension : InterfaceExtends earlier later) {context : Model.Context β} {term type : AExpr β} + (spine : LambdaSpineTyping.{u,v} earlier context term type) : + LambdaSpineTyping.{u,v} later context term type := by + intro condition domain body arguments same + obtain ⟨headType, typed, leading, checked⟩ := spine _ _ _ _ same + exact ⟨headType, extension.typing typed, leading, extension.argumentSpine checked⟩ + +theorem ContextInsertion.argumentSpine {β : Type u} {entries : Model.Environment β} + {source target : Model.Context β} {cutoff : Nat} + (insertion : ContextInsertion source target cutoff) + {start result : AExpr β} {arguments : List (AExpr β)} + (spine : ArgumentSpine.{u,v} entries source start arguments result) : + ArgumentSpine.{u,v} entries target (start.liftN 1 cutoff) + (arguments.map (AExpr.liftN 1 · cutoff)) (result.liftN 1 cutoff) := by + induction spine with + | nil => exact .nil _ + | cons checked rest ih => + simpa only [AExpr.liftN, List.map_cons] using + ArgumentSpine.cons (insertion.typing checked) + (by simpa only [AExpr.liftN_inst_zero] using ih) + | convert rigid converted formed rest ih => + exact .convert (AExpr.HeadRigid.map rigid (AExpr.liftN 1 · cutoff) (by intros; rfl)) + (insertion.conversion converted) (insertion.typing formed) ih + +theorem ContextInsertion.lambdaSpine {β : Type u} {entries : Model.Environment β} + {source target : Model.Context β} {cutoff : Nat} + (insertion : ContextInsertion source target cutoff) {term type : AExpr β} + (spine : LambdaSpineTyping.{u,v} entries source term type) : + LambdaSpineTyping.{u,v} entries target (term.liftN 1 cutoff) (type.liftN 1 cutoff) := by + intro condition domain body arguments same + let view := liftedSpineView same + have headEq := view.headEq + cases original : view.originalHead with + | lam priorCondition priorDomain priorBody => + rw [original] at headEq + obtain ⟨rfl, rfl, rfl⟩ := AExpr.lam.inj headEq + have sourceEq := view.sourceEq + rw [original] at sourceEq + obtain ⟨headType, typed, leading, checked⟩ := spine _ _ _ _ sourceEq + refine ⟨headType.liftN 1 cutoff, insertion.typing typed, ?_, ?_⟩ + · simpa only [AExpr.liftN, AExpr.lambdaDepth_liftN] using leading.liftN 1 cutoff + · rw [view.argumentsEq] + exact insertion.argumentSpine checked + | bvar | sort | const | app | forallE | proj | natLit => + rw [original] at headEq + cases headEq + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SynthesisCache.lean b/Ix/Kernel/Verify/Consistency/SynthesisCache.lean index 3ddb89179..01b0e6f23 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisCache.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisCache.lean @@ -89,4 +89,127 @@ def SynthesisInference.reuseFullAcross {β : Type u} {resolve : Address → Opti SynthesisInference resolve entries locals context bounds nextFuel current source term type level := tree.reuseFullClosed formed agreement reading full closed accepted (intervening.frame outside otherRun).1 +/-- The concrete full-cache entry and its actual checking provenance. +An empty anchor context lets the retained tree cross later interfaces and +local scopes without assuming semantic agreement for the cached answer. -/ +structure CachedSynthesisCheck {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (locals : List FVarId) (context : Model.Context β) + (state : TcState .anon) (source : KExpr .anon) (term type : AExpr β) (level : VLevel) where + result : KExpr .anon + closed : source.lbr = 0 + sourceReading : readScopedExpr? resolve locals source = some term.erase + resultReading : readScopedExpr? resolve locals result = some type.erase + check : SynthesisRetainedCheck resolve anchor [] [] entries context term type level + stored : state.env.inferCache[(source.addr, emptyCtxAddr)]? = some result + +/-- Full inference supplies the stored result and its reading. The anchor's +context is constructed from the executed domain checks of the source tree. -/ +def CachedSynthesisCheck.ofFull {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {fuel : Nat} {before after : TcState .anon} + {source result : KExpr .anon} {term type : AExpr β} {level : VLevel} + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (contextOrigin : SynthesisContext resolve anchor [] [] entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (full : before.inferOnly = false) (closed : source.lbr = 0) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + CachedSynthesisCheck resolve anchor entries locals context after source term type level := + ⟨result, closed, reading, + (tree.soundWithSpine.{u,u} (contextOrigin.sound (ContextFormation.empty anchor)) agreement reading accepted).1, + .source contextOrigin tree agreement reading accepted, + infer_full_success_cache full (inferKey_closed closed before) accepted⟩ + +/-- Closed declaration calls establish an anchor directly. -/ +def CachedSynthesisCheck.ofClosedFull {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {fuel : Nat} {before after : TcState .anon} + {source result : KExpr .anon} {term type : AExpr β} {level : VLevel} + (tree : SynthesisInference resolve entries [] [] [] fuel before source term type level) + (reading : readScopedExpr? resolve [] source = some term.erase) + (full : before.inferOnly = false) (closed : source.lbr = 0) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + CachedSynthesisCheck resolve entries entries [] [] after source term type level := + .ofFull tree .current (.empty _ _) reading full closed accepted + +namespace CachedSynthesisCheck + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} + {state : TcState .anon} {source : KExpr .anon} {term type : AExpr β} {level : VLevel} + +/-- A proved operational frame preserves the physical result at its key. -/ +def frame (cached : CachedSynthesisCheck resolve anchor entries locals context state source term type level) + {after : TcState .anon} (preserved : InferenceCacheFrame (source.addr, emptyCtxAddr) state after) : + CachedSynthesisCheck resolve anchor entries locals context after source term type level := + { cached with stored := preserved.full.trans cached.stored } + +/-- Declaration growth transports all retained source, domain, and body +checks while preserving the concrete cache entry and source readings. -/ +def extend (cached : CachedSynthesisCheck resolve anchor entries locals context state source term type level) + {later : Model.Environment β} (extension : InterfaceExtends entries later) : + CachedSynthesisCheck resolve anchor later locals context state source term type level := + { cached with check := cached.check.extend extension } + +/-- A fresh local shifts the annotation of every captured variable. The +same raw source and result remain readable, including beneath binders. -/ +def weaken (cached : CachedSynthesisCheck resolve anchor entries locals context state source term type level) + {fresh : FVarId} (absent : fresh ∉ locals) (domain : AExpr β) : + CachedSynthesisCheck resolve anchor entries (fresh :: locals) (context.push domain) state source + (term.liftN 1) (type.liftN 1) level := + { cached with + sourceReading := by simpa only [AExpr.erase_liftN] using readScopedExpr?_push absent cached.sourceReading + resultReading := by simpa only [AExpr.erase_liftN] using readScopedExpr?_push absent cached.resultReading + check := cached.check.weakenAt (.root context domain) } + +/-- Actual binder opening supplies the frame; the fresh identifier supplies +the reader transport. The caller's domain inference still supplies context +formation when this resource is used by a synthesis tree. -/ +def afterOpenBinder (cached : CachedSynthesisCheck resolve anchor entries locals context state source term type level) + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {rawDomain rawBody opened : KExpr .anon} {fresh : FVarId} {after : TcState .anon} + (domain : AExpr β) (absent : fresh ∉ locals) + (accepted : TcM.openBinder name bi rawDomain rawBody state = .ok (opened, fresh) after) : + CachedSynthesisCheck resolve anchor entries (fresh :: locals) (context.push domain) after source + (term.liftN 1) (type.liftN 1) level := by + have preserved := PreservesInferenceCache.openBinder (source.addr, emptyCtxAddr) name bi rawDomain rawBody state + rw [accepted] at preserved + exact (cached.frame preserved).weaken absent domain + +/-- Recursive inference derives its own write footprint and frame, including +lazy loading, beta Pi exposure, and changed cheap-beta lambda bodies. -/ +def afterInference (cached : CachedSynthesisCheck resolve anchor entries locals context state source term type level) + {fuel : Nat} {other result : KExpr .anon} {after : TcState .anon} + (trace : InferenceCacheTrace.{w} fuel state other) + (outside : (source.addr, emptyCtxAddr) ∉ trace.writes) + (accepted : RecM.infer other (methodsN fuel) state = .ok result after) : + CachedSynthesisCheck resolve anchor entries locals context after source term type level := + cached.frame (trace.frame outside accepted).1 + +/-- Selection follows from the retained full entry under either policy. +No new observation or agreement premise is supplied at reuse. -/ +def hit (cached : CachedSynthesisCheck resolve anchor entries locals context state source term type level) : + InferenceCacheHit state source := + ⟨(source.addr, emptyCtxAddr), state, cached.result, inferKey_closed cached.closed state, .inl cached.stored⟩ + +/-- Materialize the cache branch of the original synthesis recursion. +The stored check can already contain transported cache hits of its own. -/ +def support (cached : CachedSynthesisCheck resolve anchor entries locals context state source term type level) + (bounds : List VLevel) (fuel : Nat) : + SynthesisInference resolve entries locals context bounds fuel state source term type level := + .cachedFrom (.rebase (.empty anchor) cached.check) cached.hit cached.resultReading + +theorem run (cached : CachedSynthesisCheck resolve anchor entries locals context state source term type level) + (methods : Methods .anon) : RecM.infer source methods state = .ok cached.result state := + cached.hit.run methods + +/-- Replay is typed from its retained execution, even after the interface +and annotated context have changed. -/ +theorem sound (cached : CachedSynthesisCheck resolve anchor entries locals context state source term type level) : + TypingClaim.{u,v} entries context term type ∧ + TypingClaim.{u,v} entries context type (.sort level) ∧ + LambdaSpineTyping.{u,v} entries context term type := + cached.check.soundWithSpine (.empty anchor) + +end CachedSynthesisCheck + end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean index 4cb61f852..a07724726 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean @@ -3,7 +3,7 @@ Copyright (c) 2026 Argument Computer Corporation. SPDX-License-Identifier: MIT OR Apache-2.0 -/ -import Ix.Kernel.Verify.Consistency.ContextInsertion +import Ix.Kernel.Verify.Consistency.ContextTransport import Ix.Kernel.Verify.Consistency.CheapBetaReading import Ix.Kernel.Verify.Consistency.ApplicationWhnf import Ix.Theory.Model.UniverseBounds @@ -50,6 +50,11 @@ inductive SynthesisInference {β : Type u} (cacheMatch : hit.cached = priorResult) (resultReading : readScopedExpr? resolve locals priorResult = some type.erase) : SynthesisInference resolve entries locals context bounds fuel before source term type level + | cachedFrom {entries locals context bounds fuel before source term type level} + (check : SynthesisRetainedCheck resolve entries context bounds entries context term type level) + (hit : InferenceCacheHit before source) + (resultReading : readScopedExpr? resolve locals hit.cached = some type.erase) : + SynthesisInference resolve entries locals context bounds fuel before source term type level | reuseType {earlier entries locals context bounds fuel before source term type typeFuel typeBefore typeAfter typeSource typeResult declaredType level typeBound arguments} (inference : BinderInference resolve entries locals context fuel before source term type) @@ -183,6 +188,34 @@ inductive SynthesisInference {β : Type u} (AExpr.betaPrefix (cheapBetaCount trace.bodyType) (.lam headCondition headDomain headBody) arguments)) (.imax (readLevel trace.domainLevel) reducedLevel) +/-- A complete executed inference check transported to a later use site. +The source tree remains available beneath interface and context changes; +none of these constructors accepts semantic typing or conversion evidence. -/ +inductive SynthesisRetainedCheck {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → + Model.Environment β → Model.Context β → AExpr β → AExpr β → VLevel → Type u + | source {incoming incomingContext incomingBounds entries context bounds locals fuel before after + source result term type level} + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level + | extend {incoming incomingContext incomingBounds earlier entries context term type level} + (prior : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds earlier context term type level) + (extension : InterfaceExtends earlier entries) : + SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level + | weakenAt {incoming incomingContext incomingBounds entries source target cutoff term type level} + (prior : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries source term type level) + (insertion : ContextInsertion source target cutoff) : + SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries target + (term.liftN 1 cutoff) (type.liftN 1 cutoff) level + | rebase {incoming incomingContext incomingBounds middle middleContext middleBounds entries context term type level} + (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) + (prior : SynthesisRetainedCheck resolve middle middleContext middleBounds entries context term type level) : + SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level + /-- Contexts of retained checking origins come from the incoming context, the empty context, or actual checks of their binder domains. This records earlier checks even when a generated type is used several binders later. -/ @@ -301,6 +334,10 @@ inductive SynthesisTypingOrigin {β : Type u} (resolve : Address → Option (Con (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds earlier context term type) (extension : InterfaceExtends earlier entries) : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type + | rebase {incoming incomingContext incomingBounds middle middleContext middleBounds entries context term type} + (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) + (prior : SynthesisTypingOrigin resolve middle middleContext middleBounds entries context term type) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type | termEquivalent {incoming incomingContext incomingBounds entries context term term' type} (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) (same : AExpr.LevelEquivalent term term') : @@ -652,6 +689,33 @@ def SynthesisArgumentSpineOrigin.instantiate {β : Type u} {resolve : Address | .convert prior trace => .convert (prior.instantiate levels) (.instantiate trace levels) termination_by structural support +def SynthesisArgumentSpineOrigin.weakenAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext source target : Model.Context β} + {incomingBounds : List VLevel} {cutoff : Nat} {start result : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries source start arguments result) (insertion : ContextInsertion source target cutoff) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries target + (start.liftN 1 cutoff) (arguments.map (AExpr.liftN 1 · cutoff)) (result.liftN 1 cutoff) := + match support with + | .nil _ => .nil _ + | .snoc prior checked => by + simpa only [List.map_append, List.map_cons, List.map_nil, AExpr.liftN_inst_zero] using + (prior.weakenAt insertion).snoc (.weakenAt checked insertion) + | .convert prior trace => .convert (prior.weakenAt insertion) (.weakenAt trace insertion) +termination_by structural support + +def SynthesisArgumentSpineOrigin.rebase {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming middle entries : Model.Environment β} {incomingContext middleContext context : Model.Context β} + {incomingBounds middleBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} + (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) + (support : SynthesisArgumentSpineOrigin resolve middle middleContext middleBounds entries context start arguments result) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context start arguments result := + match support with + | .nil _ => .nil _ + | .snoc prior checked => (prior.rebase origin).snoc (.rebase origin checked) + | .convert prior trace => .convert (prior.rebase origin) (.rebase origin trace) +termination_by structural support + def SynthesisArgumentSpineOrigin.appendContext {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} {incomingBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} @@ -952,29 +1016,28 @@ def ApplicationInferenceTrace.substituteReductionOriginAt {β : Type u} origin.map (trace.substituteTypeOriginAt (.pure .refl) enough argumentContext functionTree argumentTree agreement functionReading argumentReading conditions hashPath comparisonFaithful substitution) -/-- The actual codomain call retained inside a function-type check. Its -local context is reconstructed from that same call's domain check. -/ -structure SynthesisForallBodyCheck {β : Type u} (resolve : Address → Option (ConstRef β)) - (entries : Model.Environment β) (context : Model.Context β) (bounds : List VLevel) +/-- The domain and codomain retain their actual checks after interface +growth or insertion beneath any number of dependent binders. -/ +structure SynthesisRetainedForall {β : Type u} (resolve : Address → Option (ConstRef β)) + (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) + (entries : Model.Environment β) (context : Model.Context β) (condition : Certified.PropWhen) (domain body : AExpr β) where domainLevel : VLevel bodyLevel : VLevel + domainBound : VLevel bound : VLevel - locals : List FVarId - fuel : Nat - before : TcState .anon - after : TcState .anon - source : KExpr .anon - result : KExpr .anon - contextOrigin : SynthesisContext resolve entries context bounds entries - (context.push domain) (domainLevel :: bounds) - tree : SynthesisInference resolve entries locals (context.push domain) (domainLevel :: bounds) - fuel before source body (.sort bodyLevel) bound - agreement : LocalContextReading resolve locals before.lctx (context.push domain) - reading : readScopedExpr? resolve locals source = some body.erase - run : RecM.infer source (methodsN fuel) before = .ok result after + domainCheck : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries + context domain (.sort domainLevel) domainBound + check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries + (context.push domain) body (.sort bodyLevel) bound conditionAgrees : condition = Certified.zeroCondition bodyLevel +/-- A function-type check at the caller's current inference context. -/ +abbrev SynthesisForallBodyCheck {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (context : Model.Context β) (bounds : List VLevel) + (condition : Certified.PropWhen) (domain body : AExpr β) := + SynthesisRetainedForall resolve entries context bounds entries context condition domain body + private def ForallInferenceTrace.bodyCheck {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} {before : TcState .anon} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} @@ -997,18 +1060,11 @@ private def ForallInferenceTrace.bodyCheck {β : Type u} {resolve : Address → exact { domainLevel := readLevel trace.domainLevel bodyLevel := readLevel trace.bodyLevel + domainBound := domainBound bound := bodyBound - locals := trace.fresh :: locals - fuel := fuel - before := trace.openedState - after := trace.bodyState - source := trace.opened - result := .sort trace.bodyLevel trace.bodyInfo - contextOrigin := .push .current domainTree agreement domainReading trace.domainRun - tree := bodyTree - agreement := opened.2.2.1 - reading := opened.2.1 - run := trace.bodyRun + domainCheck := .source .current domainTree agreement domainReading trace.domainRun + check := .source (.push .current domainTree agreement domainReading trace.domainRun) + bodyTree opened.2.2.1 opened.2.1 trace.bodyRun conditionAgrees := rfl } def BinderInference.forallBodyCheck {β : Type u} {resolve : Address → Option (ConstRef β)} @@ -1026,8 +1082,65 @@ def BinderInference.forallBodyCheck {β : Type u} {resolve : Address → Option exact trace.bodyCheck opening absent (.known domainTree (.sort _)) (.known bodyTree (.sort _)) (miss.localContext.symm ▸ agreement) domainReads bodyReads +mutual + /-- Recover the recorded codomain check by inspecting the production inference tree. This does not assume a new call on an inferred codomain. -/ +private def SynthesisInference.forallBodyCheckAux {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} + {condition : Certified.PropWhen} {term domain body type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) : + term = .forallE condition domain body → SynthesisForallBodyCheck resolve entries context bounds condition domain body := + match support with + | .known inference _ | .reuseType inference .. => fun same => by + cases same + exact inference.forallBodyCheck agreement reading + | .cached tree priorAgreement priorReading _ _ _ _ => fun same => + tree.forallBodyCheckAux priorAgreement priorReading same + | .cachedFrom check _ _ => fun same => check.forallBody same + | .forallE miss trace opening absent domainTree bodyTree .. => fun same => by + cases same + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading + exact trace.bodyCheck opening absent domainTree bodyTree + (miss.localContext.symm ▸ agreement) domainReads bodyReads + | .fvar .. | .app .. | .appBeta .. | .lam .. | .lamBeta .. => fun same => by cases same +termination_by structural support + +def SynthesisRetainedCheck.forallBody {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} + {condition : Certified.PropWhen} {domain body : AExpr β} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) : + term = .forallE condition domain body → + SynthesisRetainedForall resolve incoming incomingContext incomingBounds entries context condition domain body := + match check with + | .source contextOrigin tree agreement reading _ => fun same => + let child := tree.forallBodyCheckAux agreement reading same + ⟨child.domainLevel, child.bodyLevel, child.domainBound, child.bound, + .rebase contextOrigin child.domainCheck, .rebase contextOrigin child.check, + child.conditionAgrees⟩ + | .extend prior extension => fun same => + let child := prior.forallBody same + ⟨child.domainLevel, child.bodyLevel, child.domainBound, child.bound, + child.domainCheck.extend extension, child.check.extend extension, child.conditionAgrees⟩ + | .weakenAt prior insertion => fun same => by + let view := liftedForallView same + let child := prior.forallBody view.sourceEq + refine ⟨child.domainLevel, child.bodyLevel, child.domainBound, child.bound, ?_, ?_, child.conditionAgrees⟩ + · simpa only [view.domainEq, AExpr.liftN] using child.domainCheck.weakenAt insertion + · simpa only [view.domainEq, view.bodyEq, AExpr.liftN] using + child.check.weakenAt (insertion.push view.originalDomain) + | .rebase origin prior => fun same => + let child := prior.forallBody same + ⟨child.domainLevel, child.bodyLevel, child.domainBound, child.bound, + .rebase origin child.domainCheck, .rebase origin child.check, child.conditionAgrees⟩ +termination_by structural check + +end + def SynthesisInference.forallBodyCheck {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} @@ -1036,17 +1149,8 @@ def SynthesisInference.forallBodyCheck {β : Type u} {resolve : Address → Opti (.forallE condition domain body) type level) (agreement : LocalContextReading resolve locals before.lctx context) (reading : readScopedExpr? resolve locals source = some (AExpr.forallE condition domain body).erase) : - SynthesisForallBodyCheck resolve entries context bounds condition domain body := by - cases support with - | known inference => exact inference.forallBodyCheck agreement reading - | reuseType inference => exact inference.forallBodyCheck agreement reading - | cached tree priorAgreement priorReading _ _ _ _ => - exact tree.forallBodyCheck priorAgreement priorReading - | forallE miss trace opening absent domainTree bodyTree => - obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading - exact trace.bodyCheck opening absent domainTree bodyTree - (miss.localContext.symm ▸ agreement) domainReads bodyReads -termination_by sizeOf support + SynthesisForallBodyCheck resolve entries context bounds condition domain body := + support.forallBodyCheckAux agreement reading rfl private theorem list_reverse_induction {α : Type u} {motive : List α → Prop} (nil : motive []) @@ -1117,6 +1221,27 @@ def BinderInference.variableSpineOrigin {β : Type u} {resolve : Address → Opt (by intro index same; cases same) index arguments headEquals) termination_by structural support +def SynthesisVariableSpineOrigin.weakenAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext source target : Model.Context β} + {incomingBounds : List VLevel} {cutoff index : Nat} {arguments : List (AExpr β)} {type : AExpr β} + (spine : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries source index arguments type) + (insertion : ContextInsertion source target cutoff) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries target (liftVar 1 index cutoff) + (arguments.map (AExpr.liftN 1 · cutoff)) (type.liftN 1 cutoff) := + ⟨spine.headType.liftN 1 cutoff, + by simpa only [liftVar, Nat.add_comm 1] using insertion.lookup spine.atIndex, + spine.spine.weakenAt insertion⟩ + +def SynthesisVariableSpineOrigin.rebase {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming middle entries : Model.Environment β} {incomingContext middleContext context : Model.Context β} + {incomingBounds middleBounds : List VLevel} {index : Nat} {arguments : List (AExpr β)} {type : AExpr β} + (spine : SynthesisVariableSpineOrigin resolve middle middleContext middleBounds entries context index arguments type) + (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries context index arguments type := + ⟨spine.headType, spine.atIndex, spine.spine.rebase origin⟩ + +mutual + def SynthesisInference.variableSpineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel : Nat} @@ -1132,6 +1257,7 @@ def SynthesisInference.variableSpineOrigin {β : Type u} {resolve : Address → inference.variableSpineOrigin agreement reading index arguments headEquals | .cached tree priorAgreement priorReading _ _ _ _ => tree.variableSpineOrigin contextOrigin priorAgreement priorReading index arguments headEquals + | .cachedFrom check _ _ => (check.variableSpineOrigin index arguments headEquals).rebase contextOrigin | .app _ miss trace functionTree argumentTree conditions hashPath comparisonFaithful _ _ _ _ _ _ => by obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement @@ -1157,6 +1283,25 @@ def SynthesisInference.variableSpineOrigin {β : Type u} {resolve : Address → (by intro index same; cases same) index arguments headEquals) termination_by structural support +def SynthesisRetainedCheck.variableSpineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) + (index : Nat) (arguments : List (AExpr β)) (same : term = (AExpr.bvar index).appN arguments) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries context index arguments type := + match check with + | .source contextOrigin tree agreement reading _ => + tree.variableSpineOrigin contextOrigin agreement reading index arguments same + | .extend prior extension => (prior.variableSpineOrigin index arguments same).extend extension + | .weakenAt prior insertion => by + let view := liftedVariableSpineView same + let old := prior.variableSpineOrigin view.originalIndex view.originalArguments view.sourceEq + simpa only [view.indexEq, view.argumentsEq] using old.weakenAt insertion + | .rebase origin prior => (prior.variableSpineOrigin index arguments same).rebase origin +termination_by structural check + +end + def SynthesisForallBodyCheck.variableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} {incomingBounds bounds : List VLevel} {condition : Certified.PropWhen} {domain : AExpr β} @@ -1166,8 +1311,7 @@ def SynthesisForallBodyCheck.variableSpine {β : Type u} {resolve : Address → (parentOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries (context.push domain) index arguments (.sort check.bodyLevel) := - check.tree.variableSpineOrigin (parentOrigin.compose check.contextOrigin) check.agreement check.reading - index arguments rfl + (check.check.variableSpineOrigin index arguments rfl).rebase parentOrigin /-- Keep the actual variable-application checks inside a lambda body. The body's inferred type is retained separately from the lambda's eventual @@ -1192,28 +1336,35 @@ def BinderInference.lambdaBodyVariableSpine {β : Type u} {resolve : Address → openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun exact ⟨_, bodyTree.variableSpineOrigin openedAgreement openedReads index arguments rfl⟩ -def SynthesisInference.lambdaBodyVariableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} +mutual + +private def SynthesisInference.lambdaBodyVariableSpineAux {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel index : Nat} - {before : TcState .anon} {source : KExpr .anon} {domain type : AExpr β} + {before : TcState .anon} {source : KExpr .anon} {term domain type : AExpr β} {condition : Certified.PropWhen} {arguments : List (AExpr β)} {level : VLevel} - (support : SynthesisInference resolve entries locals context bounds fuel before source - (.lam condition domain ((AExpr.bvar index).appN arguments)) type level) + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = - some (AExpr.lam condition domain ((AExpr.bvar index).appN arguments)).erase) : - Σ resultType, SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds - entries (context.push domain) index arguments resultType := by - cases support with - | known inference _ => + (reading : readScopedExpr? resolve locals source = some term.erase) : + term = .lam condition domain ((AExpr.bvar index).appN arguments) → + Σ resultType, SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries (context.push domain) index arguments resultType := + match support with + | .known inference _ => fun same => by + cases same exact inference.lambdaBodyVariableSpine agreement reading - | reuseType inference => + | .reuseType inference .. => fun same => by + cases same exact inference.lambdaBodyVariableSpine agreement reading - | cached tree priorAgreement priorReading _ _ _ _ => - exact tree.lambdaBodyVariableSpine contextOrigin priorAgreement priorReading - | lam full miss trace opening absent domainTree bodyTree conditionAgrees constructed bound coherent - closingFaithful faithful => + | .cached tree priorAgreement priorReading _ _ _ _ => fun same => + tree.lambdaBodyVariableSpineAux contextOrigin priorAgreement priorReading same + | .cachedFrom check _ _ => fun same => + let child := check.lambdaBodyVariableSpine same + ⟨child.1, child.2.rebase contextOrigin⟩ + | .lam full miss trace opening absent domainTree bodyTree conditionAgrees constructed bound coherent + closingFaithful faithful => fun same => by + cases same obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement @@ -1222,8 +1373,9 @@ def SynthesisInference.lambdaBodyVariableSpine {β : Type u} {resolve : Address exact ⟨_, bodyTree.variableSpineOrigin (contextOrigin.push domainTree keyedAgreement domainReads trace.domainRun) openedAgreement openedReads index arguments rfl⟩ - | lamBeta full miss trace opening absent domainTree bodyTree origin reduction conditionAgrees - constructed bound closingFaithful faithful => + | .lamBeta full miss trace opening absent domainTree bodyTree origin reduction conditionAgrees + constructed bound closingFaithful faithful => fun same => by + cases same obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement @@ -1233,7 +1385,52 @@ def SynthesisInference.lambdaBodyVariableSpine {β : Type u} {resolve : Address (contextOrigin.push domainTree keyedAgreement domainReads trace.domainRun) openedAgreement openedReads index arguments rfl⟩ -termination_by sizeOf support + | .fvar .. | .app .. | .appBeta .. | .forallE .. => fun same => by cases same +termination_by structural support + +def SynthesisRetainedCheck.lambdaBodyVariableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type domain : AExpr β} {level : VLevel} + {condition : Certified.PropWhen} {index : Nat} {arguments : List (AExpr β)} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) : + term = .lam condition domain ((AExpr.bvar index).appN arguments) → + Σ resultType, SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries (context.push domain) index arguments resultType := + match check with + | .source contextOrigin tree agreement reading _ => fun same => + tree.lambdaBodyVariableSpineAux contextOrigin agreement reading same + | .extend prior extension => fun same => + let child := prior.lambdaBodyVariableSpine same + ⟨child.1, child.2.extend extension⟩ + | .weakenAt (cutoff := cutoff) prior insertion => fun same => by + let lambda := liftedLambdaView same + let body := liftedVariableSpineView lambda.bodyEq.symm + let child := prior.lambdaBodyVariableSpine + (lambda.sourceEq.trans (congrArg (AExpr.lam condition lambda.originalDomain) body.sourceEq)) + refine ⟨child.1.liftN 1 (cutoff + 1), ?_⟩ + simpa only [lambda.domainEq, body.indexEq, body.argumentsEq] using + child.2.weakenAt (insertion.push lambda.originalDomain) + | .rebase origin prior => fun same => + let child := prior.lambdaBodyVariableSpine same + ⟨child.1, child.2.rebase origin⟩ +termination_by structural check + +end + +def SynthesisInference.lambdaBodyVariableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel index : Nat} + {before : TcState .anon} {source : KExpr .anon} {domain type : AExpr β} + {condition : Certified.PropWhen} {arguments : List (AExpr β)} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source + (.lam condition domain ((AExpr.bvar index).appN arguments)) type level) + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = + some (AExpr.lam condition domain ((AExpr.bvar index).appN arguments)).erase) : + Σ resultType, SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries (context.push domain) index arguments resultType := + support.lambdaBodyVariableSpineAux contextOrigin agreement reading rfl theorem BinderInference.lambdaPrefix {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} @@ -1245,6 +1442,8 @@ theorem BinderInference.lambdaPrefix {β : Type u} | lam _ _ _ _ _ _ _ _ _ _ _ ih => exact .lam ih | _ => exact .zero _ _ +mutual + theorem SynthesisInference.lambdaPrefix {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} @@ -1254,6 +1453,7 @@ theorem SynthesisInference.lambdaPrefix {β : Type u} match support with | .known inference _ => inference.lambdaPrefix | .cached tree .. => tree.lambdaPrefix + | .cachedFrom check _ _ => check.lambdaPrefix | .reuseType inference _ _ _ _ _ => inference.lambdaPrefix | .lam _ _ _ _ _ _ bodyTree _ _ _ _ _ _ => .lam bodyTree.lambdaPrefix | .lamBeta _ _ _ _ _ _ bodyTree _ _ _ _ _ _ _ => by @@ -1263,6 +1463,222 @@ theorem SynthesisInference.lambdaPrefix {β : Type u} | .fvar .. | .app .. | .appBeta .. | .forallE .. => .zero _ _ termination_by structural support +theorem SynthesisRetainedCheck.lambdaPrefix {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) : + LambdaPrefix term type term.lambdaDepth := + match check with + | .source _ tree _ _ _ => tree.lambdaPrefix + | .extend prior _ | .rebase _ prior => prior.lambdaPrefix + | .weakenAt prior _ => by + simpa only [AExpr.lambdaDepth_liftN] using prior.lambdaPrefix.liftN 1 _ +termination_by structural check + +end + +def SynthesisRetainedCheck.origin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type := + match check with + | .source contextOrigin tree agreement reading accepted => + .source (.checked contextOrigin tree agreement reading accepted) + | .extend prior extension => prior.origin.extend extension + | .weakenAt prior insertion => .weakenAt prior.origin insertion + | .rebase origin prior => .rebase origin prior.origin +termination_by structural check + +def SynthesisRetainedCheck.typeOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context type (.sort level) := + match check with + | .source contextOrigin tree agreement reading accepted => + .inferredType contextOrigin tree agreement reading accepted + | .extend prior extension => prior.typeOrigin.extend extension + | .weakenAt prior insertion => .weakenAt prior.typeOrigin insertion + | .rebase origin prior => .rebase origin prior.typeOrigin +termination_by structural check + +/-- The head's check and every argument check of an actual application +spine, retained as data for subsequent reductions and substitutions. -/ +structure SynthesisSpineOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) + (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) + (entries : Model.Environment β) (context : Model.Context β) + (head : AExpr β) (arguments : List (AExpr β)) (type : AExpr β) where + headType : AExpr β + headOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context head headType + leading : LambdaPrefix head headType head.lambdaDepth + argumentsOrigin : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries context headType arguments type + +private theorem appN_last {β : Type u} {head : AExpr β} {arguments : List (AExpr β)} + (nonempty : arguments ≠ []) : + head.appN arguments = (head.appN arguments.dropLast).app (arguments.getLast nonempty) := by + calc + head.appN arguments = head.appN (arguments.dropLast ++ [arguments.getLast nonempty]) := + congrArg (AExpr.appN head) (List.dropLast_concat_getLast nonempty).symm + _ = _ := by simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] + +private theorem nonapp_spine_empty {β : Type u} {term head : AExpr β} {arguments : List (AExpr β)} + (notApp : ∀ fn arg, term ≠ .app fn arg) (same : term = head.appN arguments) : arguments = [] := by + by_contra nonempty + exact notApp _ _ (same.trans (appN_last nonempty)) + +private theorem app_spine_parts {β : Type u} {fn arg head : AExpr β} {arguments : List (AExpr β)} + (nonempty : arguments ≠ []) (same : fn.app arg = head.appN arguments) : + arguments = arguments.dropLast ++ [arg] ∧ fn = head.appN arguments.dropLast := by + have parts := AExpr.app.inj (same.trans (appN_last nonempty)) + exact ⟨by rw [parts.2]; exact (List.dropLast_concat_getLast nonempty).symm, parts.1⟩ + +def BinderInference.spineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source term type) + (checked : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (head : AExpr β) (arguments : List (AExpr β)) (same : term = head.appN arguments) : + SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := + if empty : arguments = [] then by + subst arguments + simp only [AExpr.appN_nil] at same + subst head + exact ⟨type, .source checked, support.lambdaPrefix, .nil _⟩ + else match support with + | .app _ miss trace functionTree functionHead argumentTree conditions hashPath comparisonFaithful + _ _ _ _ _ _ => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have parts := app_spine_parts empty same + have prior := functionTree.spineOrigin (incoming := incoming) (incomingContext := incomingContext) + (incomingBounds := incomingBounds) + (.binderHead functionTree functionHead keyedAgreement functionReading trace.functionRun) + keyedAgreement functionReading head arguments.dropLast parts.2 + refine ⟨prior.headType, prior.headOrigin, prior.leading, ?_⟩ + simpa only [← parts.1] using prior.argumentsOrigin.snoc + (.source (.binderArgument trace functionTree functionHead argumentTree keyedAgreement + functionReading argumentReading conditions hashPath comparisonFaithful)) + | .sort .. | .cachedSort .. | .fvar .. | .const .. | .polymorphic .. | .cachedConst .. | .forallE .. | .lam .. => by + exact False.elim (empty (nonapp_spine_empty (by intro fn arg same; cases same) same)) +termination_by structural support + +def SynthesisSpineOrigin.extend {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming earlier entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {head type : AExpr β} {arguments : List (AExpr β)} + (spine : SynthesisSpineOrigin resolve incoming incomingContext incomingBounds earlier context head arguments type) + (extension : InterfaceExtends earlier entries) : + SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := + ⟨spine.headType, spine.headOrigin.extend extension, spine.leading, spine.argumentsOrigin.extend extension⟩ + +def SynthesisSpineOrigin.weakenAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext source target : Model.Context β} + {incomingBounds : List VLevel} {cutoff : Nat} {head type : AExpr β} {arguments : List (AExpr β)} + (spine : SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries source head arguments type) + (insertion : ContextInsertion source target cutoff) : + SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries target (head.liftN 1 cutoff) + (arguments.map (AExpr.liftN 1 · cutoff)) (type.liftN 1 cutoff) := + ⟨spine.headType.liftN 1 cutoff, .weakenAt spine.headOrigin insertion, + by simpa only [AExpr.lambdaDepth_liftN] using spine.leading.liftN 1 cutoff, + spine.argumentsOrigin.weakenAt insertion⟩ + +def SynthesisSpineOrigin.rebase {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming middle entries : Model.Environment β} {incomingContext middleContext context : Model.Context β} + {incomingBounds middleBounds : List VLevel} {head type : AExpr β} {arguments : List (AExpr β)} + (spine : SynthesisSpineOrigin resolve middle middleContext middleBounds entries context head arguments type) + (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) : + SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := + ⟨spine.headType, .rebase origin spine.headOrigin, spine.leading, spine.argumentsOrigin.rebase origin⟩ + +mutual + +/-- Recover every application argument and the head's original checked +lambda prefix from the executed source inference, including binder-backed +local and constant spines. -/ +def SynthesisInference.spineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) + (head : AExpr β) (arguments : List (AExpr β)) (same : term = head.appN arguments) : + SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := + if empty : arguments = [] then by + subst arguments + simp only [AExpr.appN_nil] at same + subst head + exact ⟨type, .source (.checked contextOrigin support agreement reading accepted), support.lambdaPrefix, .nil _⟩ + else match support with + | .cached tree priorAgreement priorReading priorRun _ _ _ => + tree.spineOrigin contextOrigin priorAgreement priorReading priorRun head arguments same + | .cachedFrom check _ _ => (check.spineOrigin head arguments same).rebase contextOrigin + | .known inference formation => + inference.spineOrigin (.checked contextOrigin (.known inference formation) agreement reading accepted) + agreement reading head arguments same + | .reuseType inference typeTree extension typeReading typeRun equivalent => + inference.spineOrigin + (.checked contextOrigin (.reuseType inference typeTree extension typeReading typeRun equivalent) + agreement reading accepted) agreement reading head arguments same + | .app _ miss trace functionTree argumentTree conditions hashPath comparisonFaithful _ _ _ _ _ _ => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have parts := app_spine_parts empty same + have prior := functionTree.spineOrigin contextOrigin keyedAgreement functionReading trace.functionRun + head arguments.dropLast parts.2 + refine ⟨prior.headType, prior.headOrigin, prior.leading, ?_⟩ + simpa only [← parts.1] using prior.argumentsOrigin.snoc + (.source (.applicationArgument contextOrigin trace functionTree argumentTree keyedAgreement + functionReading argumentReading conditions hashPath comparisonFaithful)) + | .appBeta _ miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath + comparisonFaithful _ _ _ _ _ _ => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have parts := app_spine_parts empty same + have prior := functionTree.spineOrigin contextOrigin keyedAgreement functionReading trace.functionRun + head arguments.dropLast parts.2 + refine ⟨prior.headType, prior.headOrigin, prior.leading, ?_⟩ + simpa only [← parts.1] using (prior.argumentsOrigin.convert (.rebase contextOrigin reduction)).snoc + (.source (.applicationBetaArgument contextOrigin trace functionTree exposure exposureCoherent argumentTree + keyedAgreement functionReading argumentReading conditions hashPath comparisonFaithful)) + | .fvar .. | .forallE .. | .lam .. | .lamBeta .. => by + exact False.elim (empty (nonapp_spine_empty (by intro fn arg same; cases same) same)) +termination_by structural support + +def SynthesisRetainedCheck.spineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) + (head : AExpr β) (arguments : List (AExpr β)) (same : term = head.appN arguments) : + SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := + match check with + | .source contextOrigin tree agreement reading accepted => + tree.spineOrigin contextOrigin agreement reading accepted head arguments same + | .extend prior extension => (prior.spineOrigin head arguments same).extend extension + | .weakenAt prior insertion => by + let view := liftedSpineView same + let old := prior.spineOrigin view.originalHead view.originalArguments view.sourceEq + simpa only [view.headEq, view.argumentsEq] using old.weakenAt insertion + | .rebase origin prior => (prior.spineOrigin head arguments same).rebase origin +termination_by structural check + +end + +def SynthesisSpineOrigin.betaTrace {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {head type : AExpr β} {arguments : List (AExpr β)} {count : Nat} + (origin : SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type) + (enough : count ≤ head.lambdaDepth) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (head.appN arguments) (AExpr.betaPrefix count head arguments) type := + .prefix origin.headOrigin (origin.leading.truncate enough) origin.argumentsOrigin + theorem SynthesisHead.appN_head {β : Type u} {head : AExpr β} {arguments : List (AExpr β)} (support : SynthesisHead (head.appN arguments)) : SynthesisHead head := by induction arguments generalizing head with @@ -1331,6 +1747,11 @@ theorem SynthesisInference.soundWithSpine {β : Type u} rw [hit.run] at accepted cases accepted exact ⟨cacheMatch.symm ▸ resultReading, typed, formation, spine⟩ + | .cachedFrom check hit resultReading => by + obtain ⟨typed, formation, spine⟩ := check.soundWithSpine formed + rw [hit.run] at accepted + cases accepted + exact ⟨resultReading, typed, formation, spine⟩ | .reuseType inference typeTree extension typeReading typeRun same => by obtain ⟨reads, checked⟩ := inference.sound agreement reading accepted obtain ⟨_, typeTyped, _, _⟩ := @@ -1505,6 +1926,25 @@ theorem SynthesisTypeTransport.sound {β : Type u} {resolve : Address → Option exact ⟨conversion.instAt argumentAtDomain substitution, resultTyped.instAt argumentAtDomain substitution⟩ termination_by structural support +theorem SynthesisRetainedCheck.soundWithSpine {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) + (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : + TypingClaim.{u,v} entries context term type ∧ TypingClaim.{u,v} entries context type (.sort level) ∧ + LambdaSpineTyping.{u,v} entries context term type := + match check with + | .source contextOrigin tree agreement reading accepted => + (tree.soundWithSpine (contextOrigin.sound formed) agreement reading accepted).2 + | .extend prior extension => by + obtain ⟨typed, typeFormed, spine⟩ := prior.soundWithSpine formed + exact ⟨extension.typing typed, extension.typing typeFormed, extension.lambdaSpine spine⟩ + | .weakenAt prior insertion => by + obtain ⟨typed, typeFormed, spine⟩ := prior.soundWithSpine formed + exact ⟨insertion.typing typed, insertion.typing typeFormed, insertion.lambdaSpine spine⟩ + | .rebase origin prior => prior.soundWithSpine (origin.sound formed) +termination_by structural check + theorem SynthesisTypingOrigin.sound {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} {incomingBounds : List VLevel} {term type : AExpr β} @@ -1526,6 +1966,7 @@ theorem SynthesisTypingOrigin.sound {β : Type u} {resolve : Address → Option | .instantiate prior arguments => typing_instL_context (prior.sound formed) arguments | .appendContext prior outer => typing_append_context (prior.sound formed) outer | .extend prior extension => extension.typing (prior.sound formed) + | .rebase origin prior => prior.sound (origin.sound formed) | .termEquivalent prior same => same.termTyping (prior.sound formed) | .typeEquivalent prior same => same.typing (prior.sound formed) | .substituteAt body value substitution => (body.sound formed).instAt (value.sound formed) substitution @@ -1757,20 +2198,9 @@ structure SynthesisScopedTypeCheck {β : Type u} (resolve : Address → Option ( (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) (entries : Model.Environment β) (type : AExpr β) where context : Model.Context β - bounds : List VLevel level : VLevel bound : VLevel - locals : List FVarId - fuel : Nat - before : TcState .anon - after : TcState .anon - source : KExpr .anon - result : KExpr .anon - contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds - tree : SynthesisInference resolve entries locals context bounds fuel before source type (.sort level) bound - agreement : LocalContextReading resolve locals before.lctx context - reading : readScopedExpr? resolve locals source = some type.erase - run : RecM.infer source (methodsN fuel) before = .ok result after + check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context type (.sort level) bound theorem SynthesisScopedTypeCheck.sound {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext : Model.Context β} @@ -1778,7 +2208,7 @@ theorem SynthesisScopedTypeCheck.sound {β : Type u} {resolve : Address → Opti (check : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries type) (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : TypingClaim.{u,v} entries check.context type (.sort check.level) := - (check.tree.sound (check.contextOrigin.sound formed) check.agreement check.reading check.run).2.1 + (check.check.soundWithSpine formed).1 def SynthesisScopedTypeCheck.forallBody {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext : Model.Context β} @@ -1786,23 +2216,12 @@ def SynthesisScopedTypeCheck.forallBody {β : Type u} {resolve : Address → Opt (check : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries (.forallE condition domain body)) : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries body := by - let child := check.tree.forallBodyCheck check.agreement check.reading + let child := check.check.forallBody rfl exact { context := Context.push domain check.context - bounds := child.domainLevel :: check.bounds level := child.bodyLevel bound := child.bound - locals := child.locals - fuel := child.fuel - before := child.before - after := child.after - source := child.source - result := child.result - contextOrigin := check.contextOrigin.compose child.contextOrigin - tree := child.tree - agreement := child.agreement - reading := child.reading - run := child.run } + check := child.check } def SynthesisScopedTypeCheck.variableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext : Model.Context β} @@ -1811,7 +2230,7 @@ def SynthesisScopedTypeCheck.variableSpine {β : Type u} {resolve : Address → entries ((AExpr.bvar index).appN arguments)) : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries check.context index arguments (.sort check.level) := - check.tree.variableSpineOrigin check.contextOrigin check.agreement check.reading index arguments rfl + check.check.variableSpineOrigin index arguments rfl /-- Package an executed closed type check so later declarations can retain its origin. The check itself may contain direct lambda applications. -/ @@ -1839,20 +2258,9 @@ def SynthesisTypeCheck.scoped {β : Type u} {resolve : Address → Option (Const (check : SynthesisTypeCheck resolve entries type level) : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries type := { context := [] - bounds := [] level := level bound := check.bound - locals := [] - fuel := check.fuel - before := check.before - after := check.after - source := check.source - result := check.result - contextOrigin := .empty entries - tree := check.inference - agreement := .empty _ _ - reading := check.reading - run := check.run } + check := .source (.empty entries) check.inference (.empty _ _) check.reading check.run } def SynthesisTypeCheck.forallBody {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {condition : Certified.PropWhen} {domain body : AExpr β} {level : VLevel} diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 92c33d9d4..796c5b733 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -3278,6 +3278,93 @@ private def compositeLocalCacheKeys : Bool := | .ok passed after => passed && after.lctx.size == 0 | .error _ _ => false +/-- A cached dependent lambda and its Pi type capture two locals. New +loaded declarations and two more dependent locals intervene before the +lambda is applied and reduced; the same raw cache entries remain valid. -/ +private def compositeCapturedTransport (typeLevel : Bool) : Bool := + let sortLevel := if typeLevel then levelOne else KUniv.mkZero + let sort := KExpr.mkSort (m := .anon) sortLevel + let action : RecM .anon Bool := RecM.withLctxScope do + let (carrier, _) ← TcM.openBinder () () sort (.mkVar 0 ()) + let (family, _) ← TcM.openBinder () () (.mkAll () () carrier sort) (.mkVar 0 ()) + let innerDomain := KExpr.mkApp family (.mkVar 0 ()) + let source := KExpr.mkLam () () carrier (.mkLam () () innerDomain (.mkVar 0 ())) + let expected := KExpr.mkAll () () carrier + (.mkAll () () innerDomain (.mkApp family (.mkVar 1 ()))) + let piType ← RecM.inferCall expected + let initial ← RecM.inferCall source + let before ← get + if initial != expected || !compositeReplayAt source expected before || + !compositeReplayAt expected piType before then return false + let unrelated := KExpr.mkApp (.mkConst ⟨polymorphicIdentity.2, ()⟩ #[levelOne]) (.mkSort .mkZero) + let _ ← RecM.inferCall unrelated + if (← get).env.consts.size ≤ before.env.consts.size then return false + let used ← RecM.withLctxScope do + let (argument, _) ← TcM.openBinder () () carrier (.mkVar 0 ()) + let argumentType := KExpr.mkApp family argument + let (witness, _) ← TcM.openBinder () () argumentType (.mkVar 0 ()) + let replayed ← RecM.inferCall source + let checkedPi ← RecM.inferCall expected + if replayed != expected || checkedPi != piType || + !compositeReplayAt source expected (← get) || !compositeReplayAt expected piType (← get) then + return false + let application := KExpr.mkApp (.mkApp source argument) witness + let inferred ← RecM.inferCall application + let reduced ← RecM.whnfCoreWithFlagsUncached application .DEF_EQ_CORE + let reducedType ← RecM.inferCall reduced + return inferred == argumentType && reduced == witness && reducedType == argumentType && + compositeReplayAt source expected (← get) && compositeReplayAt expected piType (← get) && + compositeReplayAt application argumentType (← get) + if !used || (← get).lctx.size != before.lctx.size then return false + modify fun state => { state with env := state.env.clearReductionCaches } + match (RecM.infer source).run (methodsN 0) (← get) with + | .error .maxRecFuel _ => pure () + | _ => return false + let rebuiltPi ← RecM.inferCall expected + let rebuilt ← RecM.inferCall source + return rebuilt == expected && rebuiltPi == piType && compositeReplayAt source expected (← get) && + compositeReplayAt expected piType (← get) + match TcM.runRec action (TcState.newLazyAnon polymorphicIdentity.1) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +/-- The cached Pi retains its variable-headed codomain before declaration +growth and local insertion. Supplying the previously checked lambda then +creates the generated beta redex used by lambda inference. -/ +private def compositeCodomainTransport (shape : Nat) (level : Ixon.Univ) : Bool := + let (env, target) := exposedLambdaType level 0 shape + let (env, extra) := storeConst env + ⟨.axio ⟨false, 0, .sort 0⟩, #[], #[], #[.succ (.succ level)]⟩ + let action : RecM .anon Bool := RecM.withLctxScope do + let .defn _ _ _ _ _ _ expected value _ _ ← TcM.getConst (m := .anon) ⟨target, ()⟩ | return false + let .lam name bi domain body _ := value | return false + let .all _ _ _ codomain _ := expected | return false + let (fn, arguments) := body.collectSpine + let some supplied := arguments[0]? | return false + let functionType ← RecM.inferCall fn + let typeSort ← RecM.inferCall functionType + let suppliedType ← RecM.inferCall supplied + let before ← get + if !compositeReplayAt functionType typeSort before || !compositeReplayAt supplied suppliedType before then + return false + let _ ← RecM.inferCall (.mkConst ⟨extra, ()⟩ #[]) + if (← get).env.consts.size ≤ before.env.consts.size then return false + let (call, _) ← TcM.openBinder name bi domain body + let (carrier, _) ← TcM.openBinder () () (.mkSort levelOne) (.mkVar 0 ()) + let _ ← TcM.openBinder () () carrier (.mkVar 0 ()) + let typeReplayed ← RecM.inferCall functionType + let lambdaReplayed ← RecM.inferCall supplied + if typeReplayed != typeSort || lambdaReplayed != suppliedType then return false + let generated ← RecM.inferCall call + let reduced ← TcM.runIntern (cheapBetaReduce generated) + let inferred ← RecM.inferCall value + return (cheapBetaPlan? generated).isSome && generated != reduced && reduced == codomain && + inferred == expected && compositeReplayAt functionType typeSort (← get) && + compositeReplayAt supplied suppliedType (← get) && compositeReplayAt value expected (← get) + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + private def compositeCacheCases : TestSeq := test "composite cache: applications survive lazy inference, scopes, replay, and clearing" (compositeCacheHistory 0) @@ -3299,6 +3386,11 @@ private def compositeCacheCases : TestSeq := compositeCachedLambdaBeta ++ test "composite cache: fresh local IDs separate captured lambda keys and result types" compositeLocalCacheKeys + ++ test "composite cache: captured dependent lambdas survive model growth, nested locals, beta, and clearing" + (compositeCapturedTransport false && compositeCapturedTransport true) + ++ test "composite cache: transported Pi codomain and supplied-lambda checks justify later cheap beta" + ([0, 1, 2].all fun shape => compositeCodomainTransport shape .zero && + compositeCodomainTransport shape (.succ .zero)) /-- Definitions declare their own parameters. A two-parameter alias uses `max u v` to instantiate a one-parameter definition; a wrapper applies that diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 180825ce1..5210750bd 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -294,9 +294,10 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) identify the checked `KConst`. - Application, forall, and lambda nodes either execute a supported miss or reuse an earlier successful synthesis check. Reuse retains that check's - tree and raw execution under the same semantic interface and annotated - context. A full call supplies the exact cache entry, and a proved frame - derives the later hit and result reading without a new semantic premise. + tree and raw execution through interface growth and insertion of fresh + locals. A full call supplies the exact cache entry, and a proved frame + derives the later hit. Reader transport shifts captured variables in the + source and result without a new semantic cache premise. Cold nodes miss every eligible partition; full mode may have a populated inference-only partition. Sort nodes use a miss or a hit equal to the canonical successor sort; a maintained agreement can construct that leaf's cache observation. @@ -522,7 +523,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 663 exact theorem boundaries. The production +The consistency target checks 705 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -653,9 +654,23 @@ cheap-beta lambda bodies. Public beta WHNF and Pi exposure preserve inference entries through their computed WHNF-cache updates. `infer_full_replay_closed` proves immediate reuse with any method table and either current checking policy. These ten additional audited roots retain the existing axiom boundary. -Reuse currently keeps the same semantic interface and annotated local context; -general composite cache histories and their context/interface transport remain -open. Clearing invalidates the stored entry and requires a new full check. +`SynthesisInference.cachedFrom` also accepts the original check after interface +growth and insertion of locals. `SynthesisRetainedCheck` transports its actual +inference tree, readings, and execution. Structural inversion of lifted syntax +recovers the original lambda head, domain, body, and arguments. Pi checks retain +both domain and codomain checks; lambda bodies and variable-headed codomains +retain their dependent argument checks. These origins are used by the same +synthesis, successive-beta, hereditary-beta, declaration, and environment +proofs. No semantic typing or conversion field is added to inference support. +`CachedSynthesisCheck.ofFull` records the concrete full entry and derives its +reading. Its empty anchor context comes from actual source and domain checks. +`extend`, `afterOpenBinder`, and `afterInference` transport the resource through +interface growth and proved operational frames, including captured locals. +`support` constructs the new inference branch; `run` proves immediate replay +under either policy and at any method-table fuel. General composite cache +histories, agreement at arbitrary written keys, and initial inference-resource +construction remain open. Clearing invalidates the stored entry and requires +a new full check. Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, argument order, and rejection of wrong arities and out-of-range parameters. @@ -805,13 +820,15 @@ Definition-cycle regressions use content-addressed standalone and mutual declarations, including a self-justifying theorem, a two-member cycle, type cycles, lets, shared syntax, and binders. They check repeated member failures, acyclic forward references, cache clearing, and the partial/unsafe policy. -Ten composite-cache regressions cover real application, Pi, and lambda checks, +Twelve composite-cache regressions cover real application, Pi, and lambda checks, zero-fuel replay under both policies, inference-only exclusion from full mode, lazy loading, scope changes, clearing and repopulation, beta Pi exposure, changed cheap-beta body types, later reduction of cached lambdas, and distinct -captured-local keys. These execute production; the proof resources are checked -separately by the consistency target. -The unit suite contains 707 checks. The anonymous differential additionally +captured-local keys. They also combine declaration growth with nested dependent +locals, application and beta reduction of cached lambdas, and reuse of Pi +codomain checks before generated cheap beta. These execute production; the +proof resources are checked separately by the consistency target. +The unit suite contains 709 checks. The anonymous differential additionally serializes eight cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. diff --git a/docs/theory.md b/docs/theory.md index 9afc628ff..3902a7ddc 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -123,13 +123,18 @@ witnesses and sort leaves without repeated cache-hit observations. Frames allow reuse of composite synthesis checks as well: a successful full call supplies the exact cached result, and a later hit retains its original inference tree. The same tree supplies the lambda domains, dependent codomain checks, and -body checks required by subsequent beta reductions. Reuse keeps the same -semantic interface and annotated context. Full-cache priority permits either -later checking policy and requires no recursion fuel. Cache frames derive +body checks required by subsequent beta reductions. Those checks now survive +interface growth and insertion of locals, including shifts of captured variables +beneath dependent binders. Retained Pi checks preserve both their domain and +codomain checks. Full-cache priority permits either later checking policy and +requires no recursion fuel. Cache frames derive reuse across supported recursive inference, including beta Pi exposure and changed cheap-beta lambda bodies; public beta WHNF preserves all inference -entries. General composite cache histories and context/interface transport -remain open. Frames allow new declarations while retaining old ones. +entries. A concrete retained cache resource derives its result reading from +full inference and constructs the transported hit in the original synthesis +recursion. General composite histories, agreement at arbitrary written keys, +and initial resource construction remain open. Frames allow new declarations +while retaining old ones. The actual verified loader preserves inference caches on success and failure, including partial intern progress and deduplicated faults. This covers standalone and mutual-block loading. Block From c9ba57cd9223f2e1f291cc094c6db5caa8fd0d8c Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 10:35:22 -0400 Subject: [PATCH 36/63] Record inference cache execution histories --- Ix/Kernel/Verify/Consistency.lean | 17 +- Ix/Kernel/Verify/Consistency/Audit.lean | 34 +- .../Verify/Consistency/CacheExecution.lean | 290 ++++++++++++++++++ .../Verify/Consistency/CacheHistory.lean | 129 ++++++++ .../Verify/Consistency/RecursiveCache.lean | 89 +++++- .../Verify/Consistency/SynthesisCache.lean | 12 +- .../Consistency/SynthesisCacheExecution.lean | 224 ++++++++++++++ .../Consistency/SynthesisCacheHistory.lean | 215 +++++++++++++ .../Verify/Consistency/WhnfCacheFrame.lean | 26 ++ Tests/Ix/Kernel/Consistency.lean | 88 ++++++ docs/kernel-verification.md | 44 ++- docs/theory.md | 16 +- 12 files changed, 1162 insertions(+), 22 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/CacheExecution.lean create mode 100644 Ix/Kernel/Verify/Consistency/CacheHistory.lean create mode 100644 Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean create mode 100644 Ix/Kernel/Verify/Consistency/SynthesisCacheHistory.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index d45e27577..7eb70f2d3 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -31,6 +31,8 @@ import Ix.Kernel.Verify.Consistency.ContextInsertion import Ix.Kernel.Verify.Consistency.ContextTransport import Ix.Kernel.Verify.Consistency.SynthesisInference import Ix.Kernel.Verify.Consistency.SynthesisCache +import Ix.Kernel.Verify.Consistency.SynthesisCacheHistory +import Ix.Kernel.Verify.Consistency.SynthesisCacheExecution import Ix.Kernel.Verify.Consistency.BetaSubstitution import Ix.Kernel.Verify.Consistency.Beta import Ix.Kernel.Verify.Consistency.Simultaneous @@ -48,6 +50,8 @@ import Ix.Kernel.Verify.Consistency.BetaWhnfInference import Ix.Kernel.Verify.Consistency.CheapBeta import Ix.Kernel.Verify.Consistency.Validation import Ix.Kernel.Verify.Consistency.RecursiveCache +import Ix.Kernel.Verify.Consistency.CacheExecution +import Ix.Kernel.Verify.Consistency.CacheHistory import Ix.Kernel.Verify.Consistency.RecursiveState import Ix.Kernel.Verify.Consistency.ConversionRecipe import Ix.Kernel.Verify.Consistency.SourceAgreement @@ -187,8 +191,17 @@ later constant witnesses and sort leaves without new cache-hit observations. The trace also covers beta Pi exposure and changed cheap-beta lambda bodies. Public beta WHNF and Pi exposure preserve inference entries at every key; their computed writes affect the WHNF caches. These frames also preserve the -earlier full composite checks. General semantic cache histories, agreement -at arbitrary written keys, and automatic trace construction remain open. +earlier full composite checks. Every initially populated full key is outside +the writes by cache priority. An execution-ordered event fold reconstructs +both complete inference maps, including new entries. Histories start empty and +preserve the actual producing calls through inference, policy/scope changes, +verified loading on either outcome, and clearing. The original rich synthesis +trees supply their full-publication checks; older checking-only wrappers need +annotations for omitted child calls. Under finite query/history collision data, +selection recovers the original source and materializes its retained check in +its original context, including interface transport. Arbitrary execution and +resource construction, later context compatibility after scope exit, and +the other inference and conversion/cache paths remain open. Frames allow declaration growth while retaining every old declaration. Verified lazy loading derives such a frame on success and failure, including partial conversion state and fault deduplication. Standalone and block preparation have diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index d8d560bea..07b853cc9 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -13,6 +13,7 @@ import Ix.Kernel.Verify.Consistency.SourceCache import Ix.Kernel.Verify.Consistency.Dependencies import Ix.Kernel.Verify.Consistency.CheapBeta import Ix.Kernel.Verify.Consistency.SynthesisCache +import Ix.Kernel.Verify.Consistency.SynthesisCacheExecution import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -147,6 +148,34 @@ private def synthesisCacheRoots : Array Lean.Name := #[ ``BetaPiExposure.inference_frame, ``BetaPiExposure.policy ] +private def cacheHistoryRoots : Array Lean.Name := #[ + ``InferenceCacheTrace.populated_outside, ``InferenceCacheTrace.populated_frame, + ``BetaPublicWhnfPlan.inference_maps, ``BetaPiExposure.inference_maps, + ``InferenceCacheEvent.ofRun, ``InferenceCacheEvent.fullStep, ``InferenceCacheEvent.onlyStep, + ``InferenceCacheEvent.applyFull, ``InferenceCacheEvent.applyOnly, + ``InferenceCacheEvent.applyFull_nil, ``InferenceCacheEvent.applyOnly_nil, + ``InferenceCacheEvent.applyFull_append, ``InferenceCacheEvent.applyOnly_append, + ``InferenceCacheEvent.applyFull_origin, ``InferenceCacheEvent.applyOnly_origin, + ``InferenceCacheTrace.events, ``InferenceCacheTrace.childEvents, ``InferenceCacheTrace.cache_maps, + ``InferenceCacheHistory.ofMaps, ``InferenceCacheHistory.afterInference, + ``InferenceCacheHistory.policy, ``InferenceCacheHistory.truncate, ``InferenceCacheHistory.afterInferKey, + ``InferenceCacheHistory.openBinder, ``InferenceCacheHistory.getConst, ``InferenceCacheHistory.clear, + ``InferenceCacheHistory.full_origin, ``InferenceCacheHistory.only_origin, + ``InferenceCacheHistory.KeyData, ``InferenceCacheHistory.KeyData.same, ``InferenceCacheHistory.selected_origin, + ``SynthesisEventCheck.ofSource, ``SynthesisEventCheck.extend, ``SynthesisEventCheck.retained, + ``SynthesisEventCheck.result_reading, ``SynthesisEventCheck.cached, + ``SynthesisEventChecks, ``SynthesisEventChecks.nil, ``SynthesisEventChecks.append, + ``SynthesisEventChecks.singleton, ``SynthesisEventChecks.extend, + ``SynthesisCacheHistory.ofMaps, ``SynthesisCacheHistory.extend, ``SynthesisCacheHistory.afterInference, + ``SynthesisCacheHistory.policy, ``SynthesisCacheHistory.truncate, ``SynthesisCacheHistory.openBinder, + ``SynthesisCacheHistory.getConst, ``SynthesisCacheHistory.clear, + ``SynthesisCacheHistory.select, ``SynthesisCacheHistory.observe, + ``SynthesisCacheSupplement.ofLeaf, ``SynthesisCacheSupplement.sortOfKey, + ``SynthesisCacheSupplement.fvarOfKey, ``SynthesisCacheSupplement.verifiedConstOfKey, + ``SynthesisCacheSupplement.complete, ``SynthesisInference.CacheData, + ``SynthesisInference.cacheExecution, ``SynthesisCacheHistory.afterSynthesis +] + private def cacheTransportRoots : Array RootAllowance := #[ { root := ``liftedSpineView, standardAxioms := #[``propext] }, { root := ``liftedForallView }, @@ -275,7 +304,8 @@ private def sourceCacheRoots : Array Lean.Name := #[ private def sourceCacheInitialRoots : Array Lean.Name := #[ ``SourceCacheAgreement.empty, ``SourceCacheInvariant.ofCheckedSource, - ``SourceCacheHistory.invariant, ``infer_const_history_sound + ``SourceCacheHistory.invariant, ``infer_const_history_sound, + ``InferenceCacheHistory.initial, ``SynthesisCacheHistory.initial ] private def productionRoots : Array Lean.Name := #[ @@ -704,7 +734,7 @@ def roots : Array RootAllowance := #[ }) ++ (binderWalkerRoots ++ cacheKeyRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction -}) ++ (atomicRoots ++ instantiationRoots ++ recursiveCacheRoots ++ synthesisCacheRoots ++ lazyCacheRoots ++ +}) ++ (atomicRoots ++ instantiationRoots ++ recursiveCacheRoots ++ synthesisCacheRoots ++ cacheHistoryRoots ++ lazyCacheRoots ++ ownedLoaderRoots ++ recursiveStateRoots ++ sourceAgreementRoots ++ sourceCacheRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction diff --git a/Ix/Kernel/Verify/Consistency/CacheExecution.lean b/Ix/Kernel/Verify/Consistency/CacheExecution.lean new file mode 100644 index 000000000..803896bd2 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/CacheExecution.lean @@ -0,0 +1,290 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.RecursiveCache + +/-! The exact inference-cache writes of a supported recursive execution. +Every event retains the call that produced its stored result. Folding the +events reconstructs both complete maps, including newly written keys. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +/-- One actual miss and its successful call, whose final operation publishes +this result under the policy captured at entry. -/ +structure InferenceCacheEvent where + fuel : Nat + before : TcState .anon + after : TcState .anon + source : KExpr .anon + result : KExpr .anon + miss : UncachedInference before source + accepted : RecM.infer source (methodsN fuel) before = .ok result after + +namespace InferenceCacheEvent + +abbrev CacheMap := Std.HashMap (Address × Address) (KExpr .anon) + +def ofRun {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} + (miss : UncachedInference before source) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : InferenceCacheEvent := + ⟨fuel, before, after, source, result, miss, accepted⟩ + +def fullStep (event : InferenceCacheEvent) (cache : CacheMap) : CacheMap := + if event.before.inferOnly then cache else cache.insert event.miss.key event.result + +def onlyStep (event : InferenceCacheEvent) (cache : CacheMap) : CacheMap := + if event.before.inferOnly then cache.insert event.miss.key event.result else cache + +def applyFull (events : List InferenceCacheEvent) (cache : CacheMap) : CacheMap := + events.foldl (fun cache event => event.fullStep cache) cache + +def applyOnly (events : List InferenceCacheEvent) (cache : CacheMap) : CacheMap := + events.foldl (fun cache event => event.onlyStep cache) cache + +@[simp] theorem applyFull_nil (cache : CacheMap) : applyFull [] cache = cache := rfl +@[simp] theorem applyOnly_nil (cache : CacheMap) : applyOnly [] cache = cache := rfl + +theorem applyFull_append (first second : List InferenceCacheEvent) (cache : CacheMap) : + applyFull (first ++ second) cache = applyFull second (applyFull first cache) := + List.foldl_append + +theorem applyOnly_append (first second : List InferenceCacheEvent) (cache : CacheMap) : + applyOnly (first ++ second) cache = applyOnly second (applyOnly first cache) := + List.foldl_append + +/-- A present value was already present or was published by a recorded full +check. Repeated writes are allowed and the result is the selected value. -/ +theorem applyFull_origin (events : List InferenceCacheEvent) (cache : CacheMap) + {key : Address × Address} {result : KExpr .anon} + (stored : (applyFull events cache)[key]? = some result) : + cache[key]? = some result ∨ ∃ event ∈ events, + event.before.inferOnly = false ∧ event.miss.key = key ∧ event.result = result := by + induction events generalizing cache with + | nil => exact .inl stored + | cons event events ih => + rcases ih (event.fullStep cache) stored with old | ⟨written, member, policy, same, value⟩ + · unfold fullStep at old + split at old + · exact .inl old + · rename_i full + by_cases same : event.miss.key = key + · rw [same, Std.HashMap.getElem?_insert_self] at old + exact .inr ⟨event, .head _, Bool.eq_false_iff.mpr full, same, Option.some.inj old⟩ + · exact .inl (by simpa [Std.HashMap.getElem?_insert, same] using old) + · exact .inr ⟨written, .tail _ member, policy, same, value⟩ + +theorem applyOnly_origin (events : List InferenceCacheEvent) (cache : CacheMap) + {key : Address × Address} {result : KExpr .anon} + (stored : (applyOnly events cache)[key]? = some result) : + cache[key]? = some result ∨ ∃ event ∈ events, + event.before.inferOnly = true ∧ event.miss.key = key ∧ event.result = result := by + induction events generalizing cache with + | nil => exact .inl stored + | cons event events ih => + rcases ih (event.onlyStep cache) stored with old | ⟨written, member, policy, same, value⟩ + · unfold onlyStep at old + split at old + · rename_i policy + by_cases same : event.miss.key = key + · rw [same, Std.HashMap.getElem?_insert_self] at old + exact .inr ⟨event, .head _, policy, same, Option.some.inj old⟩ + · exact .inl (by simpa [Std.HashMap.getElem?_insert, same] using old) + · exact .inl old + · exact .inr ⟨written, .tail _ member, policy, same, value⟩ + +end InferenceCacheEvent + +/-- Child calls precede their parent's final publication. Hits contribute +no events; lazy loading and beta exposure preserve both inference maps. -/ +def InferenceCacheTrace.events {fuel : Nat} {before after : TcState .anon} + {term result : KExpr .anon} (tree : InferenceCacheTrace.{u} fuel before term) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : List InferenceCacheEvent := + match tree with + | .hit _ => [] + | .sort miss | .fvar miss | .const miss .. | .lazyConst miss .. => [.ofRun miss accepted] + | .app _ miss trace _ functionTree argumentTree => + functionTree.events trace.functionRun ++ argumentTree.events trace.argumentRun ++ [.ofRun miss accepted] + | .appBeta _ miss trace _ _ functionTree argumentTree => + functionTree.events trace.functionRun ++ argumentTree.events trace.argumentRun ++ [.ofRun miss accepted] + | .forallE miss trace domainTree bodyTree => + domainTree.events trace.domainRun ++ bodyTree.events trace.bodyRun ++ [.ofRun miss accepted] + | .lam _ miss trace domainTree bodyTree => + domainTree.events trace.domainRun ++ bodyTree.events trace.bodyRun ++ [.ofRun miss accepted] + | .lamBody _ miss trace domainTree bodyTree => + domainTree.events trace.domainRun ++ bodyTree.events trace.bodyRun ++ [.ofRun miss accepted] +termination_by structural tree + +/-- Actual child publications, before the enclosing miss writes its result. -/ +def InferenceCacheTrace.childEvents {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} : + InferenceCacheTrace.{u} fuel before source → List InferenceCacheEvent + | .hit _ | .sort _ | .fvar _ | .const .. | .lazyConst .. => [] + | .app _ _ trace _ first second => first.events trace.functionRun ++ second.events trace.argumentRun + | .appBeta _ _ trace _ _ first second => first.events trace.functionRun ++ second.events trace.argumentRun + | .forallE _ trace first second => first.events trace.domainRun ++ second.events trace.bodyRun + | .lam _ _ trace first second => first.events trace.domainRun ++ second.events trace.bodyRun + | .lamBody _ _ trace first second => first.events trace.domainRun ++ second.events trace.bodyRun + +private theorem openBinder_maps {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body opened : KExpr .anon} {fresh : FVarId} {before after : TcState .anon} + (accepted : TcM.openBinder name bi domain body before = .ok (opened, fresh) after) : + after.env.inferCache = before.env.inferCache ∧ after.env.inferOnlyCache = before.env.inferOnlyCache := by + rw [openBinder_eq] at accepted + split at accepted + · cases accepted + exact ⟨rfl, rfl⟩ + · contradiction + +private theorem hash_maps {left right : KExpr .anon} {methods : Methods .anon} + {before after : TcState .anon} (equal : (left.addr == right.addr) = true) + (accepted : RecM.isDefEq left right methods before = .ok true after) : + after.env.inferCache = before.env.inferCache ∧ after.env.inferOnlyCache = before.env.inferOnlyCache := by + rw [isDefEq_hash_state equal] at accepted + split at accepted <;> cases accepted <;> exact ⟨rfl, rfl⟩ + +private theorem miss_maps {fuel : Nat} {before after : TcState .anon} {term result : KExpr .anon} + (miss : UncachedInference before term) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) + (priorEvents : List InferenceCacheEvent) + (effects : ∀ middle, + RecM.inferUncached RecM.inferCall before.inferOnly term (methodsN fuel) miss.keyed = .ok result middle → + middle.env.inferCache = InferenceCacheEvent.applyFull priorEvents miss.keyed.env.inferCache ∧ + middle.env.inferOnlyCache = InferenceCacheEvent.applyOnly priorEvents miss.keyed.env.inferOnlyCache) : + after.env.inferCache = InferenceCacheEvent.applyFull (priorEvents ++ [.ofRun miss accepted]) before.env.inferCache ∧ + after.env.inferOnlyCache = InferenceCacheEvent.applyOnly (priorEvents ++ [.ofRun miss accepted]) + before.env.inferOnlyCache := by + obtain ⟨middle, run, written⟩ := infer_uncached_success_state miss accepted + obtain ⟨full, only⟩ := effects middle run + rw [InferenceCacheEvent.applyFull_append, InferenceCacheEvent.applyOnly_append] + change after.env.inferCache = (if before.inferOnly then + InferenceCacheEvent.applyFull priorEvents before.env.inferCache else + (InferenceCacheEvent.applyFull priorEvents before.env.inferCache).insert miss.key result) ∧ + after.env.inferOnlyCache = (if before.inferOnly then + (InferenceCacheEvent.applyOnly priorEvents before.env.inferOnlyCache).insert miss.key result else + InferenceCacheEvent.applyOnly priorEvents before.env.inferOnlyCache) + rw [written] + rw [inferKey_environment miss.keyRun] at full only + cases before.inferOnly <;> simp only [Bool.false_eq_true, if_false, if_true] <;> + constructor <;> first | rw [full] | rw [only] + +/-- The complete maps are exactly the fold of executed writes. No catalog, +watched-key exclusion, cache agreement, or collision premise is supplied. -/ +theorem InferenceCacheTrace.cache_maps {fuel : Nat} {before after : TcState .anon} + {term result : KExpr .anon} (tree : InferenceCacheTrace.{u} fuel before term) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + after.env.inferCache = InferenceCacheEvent.applyFull (tree.events accepted) before.env.inferCache ∧ + after.env.inferOnlyCache = InferenceCacheEvent.applyOnly (tree.events accepted) before.env.inferOnlyCache := by + induction tree generalizing result after with + | hit hit => + rw [hit.run] at accepted + cases accepted + simp only [events, InferenceCacheEvent.applyFull_nil, InferenceCacheEvent.applyOnly_nil, + inferKey_environment hit.keyRun, and_self] + | @sort fuel before level info miss => + apply miss_maps miss accepted [] + intro middle run + change EStateM.Result.ok + (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).1 + {miss.keyed with env := {miss.keyed.env with intern := + (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).2}} = .ok result middle at run + cases run + exact ⟨rfl, rfl⟩ + | @fvar fuel before id name info miss => + apply miss_maps miss accepted [] + intro middle run + change (RecM.inferUncached RecM.inferCall before.inferOnly (.fvar id name info)).run (methodsN fuel) miss.keyed = _ at run + unfold RecM.inferUncached at run + simp only [ReaderT.run_bind] at run + change EStateM.bind (get : TcM .anon (TcState .anon)) _ miss.keyed = _ at run + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) miss.keyed = .ok miss.keyed miss.keyed from rfl] at run + dsimp only at run + split at run + · cases run + exact ⟨rfl, rfl⟩ + · contradiction + | const miss concrete loaded resources => + apply miss_maps miss accepted [] + intro middle run + obtain ⟨actual, foundState, got, _, instantiated⟩ := inferUncached_const_instantiation run + rw [getConst_loaded loaded] at got + cases got + have post := TcM.instantiateUnivParams_wf resources.faithful + (fun _ h => Or.inr h) ⟨resources.coherent, fun _ h => Or.inl h⟩ + rw [instantiated] at post + rw [post.2.2.1] + exact ⟨rfl, rfl⟩ + | lazyConst miss loader resources => + apply miss_maps miss accepted [] + intro middle run + obtain ⟨concrete, foundState, got, _, instantiated⟩ := inferUncached_const_instantiation run + have lookup := getConst_verified_cache loader + rw [got] at lookup + have resource := resources concrete foundState got + have post := TcM.instantiateUnivParams_wf resource.faithful + (fun _ h => Or.inr h) ⟨resource.coherent, fun _ h => Or.inl h⟩ + rw [instantiated] at post + rw [post.2.2.1] + exact ⟨lookup.environment.full, lookup.environment.only⟩ + | app full miss trace hashPath functionTree argumentTree functionIH argumentIH => + apply miss_maps miss accepted (functionTree.events trace.functionRun ++ argumentTree.events trace.argumentRun) + intro middle run + rw [full] at run + obtain ⟨functionFull, functionOnly⟩ := functionIH trace.functionRun + obtain ⟨argumentFull, argumentOnly⟩ := argumentIH trace.argumentRun + obtain ⟨comparisonFull, comparisonOnly⟩ := hash_maps hashPath trace.compareRun + rw [(trace.output_state run).2] + simp only [InferenceCacheEvent.applyFull_append, InferenceCacheEvent.applyOnly_append] + exact ⟨comparisonFull.trans (argumentFull.trans (congrArg _ functionFull)), + comparisonOnly.trans (argumentOnly.trans (congrArg _ functionOnly))⟩ + | appBeta full miss trace exposure hashPath functionTree argumentTree functionIH argumentIH => + apply miss_maps miss accepted (functionTree.events trace.functionRun ++ argumentTree.events trace.argumentRun) + intro middle run + rw [full] at run + obtain ⟨functionFull, functionOnly⟩ := functionIH trace.functionRun + obtain ⟨argumentFull, argumentOnly⟩ := argumentIH trace.argumentRun + obtain ⟨comparisonFull, comparisonOnly⟩ := hash_maps hashPath trace.compareRun + have exposureMaps := exposure.inference_maps + rw [← trace.exposure_state exposure] at exposureMaps + rw [(trace.output_state run).2] + simp only [InferenceCacheEvent.applyFull_append, InferenceCacheEvent.applyOnly_append] + exact ⟨comparisonFull.trans (argumentFull.trans (congrArg _ (exposureMaps.1.trans functionFull))), + comparisonOnly.trans (argumentOnly.trans (congrArg _ (exposureMaps.2.trans functionOnly)))⟩ + | forallE miss trace domainTree bodyTree domainIH bodyIH => + apply miss_maps miss accepted (domainTree.events trace.domainRun ++ bodyTree.events trace.bodyRun) + intro middle run + obtain ⟨domainFull, domainOnly⟩ := domainIH trace.domainRun + obtain ⟨bodyFull, bodyOnly⟩ := bodyIH trace.bodyRun + obtain ⟨openedFull, openedOnly⟩ := openBinder_maps trace.openRun + rw [(trace.output_state run).2] + simp only [InferenceCacheEvent.applyFull_append, InferenceCacheEvent.applyOnly_append] + exact ⟨bodyFull.trans (congrArg _ (openedFull.trans domainFull)), + bodyOnly.trans (congrArg _ (openedOnly.trans domainOnly))⟩ + | lam full miss trace domainTree bodyTree domainIH bodyIH => + apply miss_maps miss accepted (domainTree.events trace.domainRun ++ bodyTree.events trace.bodyRun) + intro middle run + rw [full] at run + obtain ⟨domainFull, domainOnly⟩ := domainIH trace.domainRun + obtain ⟨bodyFull, bodyOnly⟩ := bodyIH trace.bodyRun + obtain ⟨openedFull, openedOnly⟩ := openBinder_maps trace.openRun + rw [(trace.output_state run).2] + simp only [InferenceCacheEvent.applyFull_append, InferenceCacheEvent.applyOnly_append] + exact ⟨bodyFull.trans (congrArg _ (openedFull.trans domainFull)), + bodyOnly.trans (congrArg _ (openedOnly.trans domainOnly))⟩ + | lamBody full miss trace domainTree bodyTree domainIH bodyIH => + apply miss_maps miss accepted (domainTree.events trace.domainRun ++ bodyTree.events trace.bodyRun) + intro middle run + rw [full] at run + obtain ⟨domainFull, domainOnly⟩ := domainIH trace.domainRun + obtain ⟨bodyFull, bodyOnly⟩ := bodyIH trace.bodyRun + obtain ⟨openedFull, openedOnly⟩ := openBinder_maps trace.openRun + rw [(trace.output_state run).2] + simp only [InferenceCacheEvent.applyFull_append, InferenceCacheEvent.applyOnly_append] + exact ⟨bodyFull.trans (congrArg _ (openedFull.trans domainFull)), + bodyOnly.trans (congrArg _ (openedOnly.trans domainOnly))⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/CacheHistory.lean b/Ix/Kernel/Verify/Consistency/CacheHistory.lean new file mode 100644 index 000000000..9926fb640 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/CacheHistory.lean @@ -0,0 +1,129 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.CacheExecution +import Ix.Kernel.Verify.Consistency.SourceCache + +/-! Histories of both complete inference caches, beginning with empty maps. +Every present value has an actual successful miss in the recorded execution. +The history retains original call states when local scopes or policy change. -/ + +namespace Ix.Kernel.Consistency + +universe u + +structure InferenceCacheHistory (state : TcState .anon) where + events : List InferenceCacheEvent + full : state.env.inferCache = InferenceCacheEvent.applyFull events ∅ + only : state.env.inferOnlyCache = InferenceCacheEvent.applyOnly events ∅ + +namespace InferenceCacheHistory + +def initial (source : Ixon.Env) : InferenceCacheHistory (TcState.newLazyAnon source) := + ⟨[], rfl, rfl⟩ + +def ofMaps {before after : TcState .anon} (history : InferenceCacheHistory before) + (full : after.env.inferCache = before.env.inferCache) + (only : after.env.inferOnlyCache = before.env.inferOnlyCache) : InferenceCacheHistory after := + ⟨history.events, full.trans history.full, only.trans history.only⟩ + +/-- The exact recursive execution appends its actual publications. -/ +def afterInference {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} + (history : InferenceCacheHistory before) (tree : InferenceCacheTrace.{u} fuel before source) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : InferenceCacheHistory after := + ⟨history.events ++ tree.events accepted, + by rw [InferenceCacheEvent.applyFull_append, (tree.cache_maps accepted).1, history.full], + by rw [InferenceCacheEvent.applyOnly_append, (tree.cache_maps accepted).2, history.only]⟩ + +def policy {state : TcState .anon} (history : InferenceCacheHistory state) (policy : Bool) : + InferenceCacheHistory {state with inferOnly := policy} := history.ofMaps rfl rfl + +def truncate {state : TcState .anon} (history : InferenceCacheHistory state) (size : Nat) : + InferenceCacheHistory {state with lctx := state.lctx.truncate size} := history.ofMaps rfl rfl + +def afterInferKey {before after : TcState .anon} {source : KExpr .anon} {key : Address × Address} + (history : InferenceCacheHistory before) + (accepted : TcM.inferKey source before = .ok key after) : InferenceCacheHistory after := + history.ofMaps (congrArg KEnv.inferCache (inferKey_environment accepted)) + (congrArg KEnv.inferOnlyCache (inferKey_environment accepted)) + +def openBinder {before after : TcState .anon} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body opened : KExpr .anon} {fresh : FVarId} (history : InferenceCacheHistory before) + (accepted : TcM.openBinder name bi domain body before = .ok (opened, fresh) after) : + InferenceCacheHistory after := + ⟨history.events, + by rw [openBinder_eq] at accepted; split at accepted + · cases accepted; exact history.full + · contradiction, + by rw [openBinder_eq] at accepted; split at accepted + · cases accepted; exact history.only + · contradiction⟩ + +/-- Standalone or block loading retains the history on both outcomes. -/ +def getConst {before : TcState .anon} {id : KId .anon} (history : InferenceCacheHistory before) + (loader : VerifiedLazySupport before id.addr) : + match TcM.getConst id before with + | .ok _ after | .error _ after => InferenceCacheHistory after := by + have preserved := getConst_verified_cache loader + cases run : TcM.getConst id before <;> rw [run] at preserved <;> + exact history.ofMaps preserved.environment.full preserved.environment.only + +/-- Clearing begins a new cache epoch; discarded entries need no provenance. -/ +def clear {state : TcState .anon} (_history : InferenceCacheHistory state) : + InferenceCacheHistory {state with env := state.env.clearReductionCaches} := ⟨[], rfl, rfl⟩ + +theorem full_origin {state : TcState .anon} (history : InferenceCacheHistory state) + {key : Address × Address} {result : KExpr .anon} + (stored : state.env.inferCache[key]? = some result) : + ∃ event ∈ history.events, + event.before.inferOnly = false ∧ event.miss.key = key ∧ event.result = result := by + rw [history.full] at stored + rcases InferenceCacheEvent.applyFull_origin history.events ∅ stored with empty | origin + · simp at empty + · exact origin + +theorem only_origin {state : TcState .anon} (history : InferenceCacheHistory state) + {key : Address × Address} {result : KExpr .anon} + (stored : state.env.inferOnlyCache[key]? = some result) : + ∃ event ∈ history.events, + event.before.inferOnly = true ∧ event.miss.key = key ∧ event.result = result := by + rw [history.only] at stored + rcases InferenceCacheEvent.applyOnly_origin history.events ∅ stored with empty | origin + · simp at empty + · exact origin + +/-- Collision data concerns just the query and the finite recorded inputs. -/ +def KeyData {state : TcState .anon} (history : InferenceCacheHistory state) (source : KExpr .anon) : Prop := + KExpr.CollisionFree fun candidate => candidate = source ∨ + ∃ event ∈ history.events, candidate = event.source + +theorem KeyData.same {state keyed : TcState .anon} {history : InferenceCacheHistory state} + {source : KExpr .anon} {key : Address × Address} (data : history.KeyData source) + {event : InferenceCacheEvent} (member : event ∈ history.events) + (keyRun : TcM.inferKey source state = .ok key keyed) (same : event.miss.key = key) : + event.source = source := by + have address : event.source.addr = source.addr := + (inferKey_address event.miss.keyRun).symm.trans + ((congrArg Prod.fst same).trans (inferKey_address keyRun)) + simpa only [KExpr.eraseMeta_anon] using + data (Or.inr ⟨event, member, rfl⟩) (Or.inl rfl) address + +/-- The selected value comes from an actual inference of the same source. +The old context and policy remain available in the returned event. -/ +theorem selected_origin {state : TcState .anon} {source : KExpr .anon} + (history : InferenceCacheHistory state) (data : history.KeyData source) + (hit : InferenceCacheHit state source) : + ∃ event ∈ history.events, event.source = source ∧ event.result = hit.cached := by + rcases hit.selected with full | ⟨_, _, only⟩ + · rw [inferKey_environment hit.keyRun] at full + obtain ⟨event, member, _, same, result⟩ := history.full_origin full + exact ⟨event, member, data.same member hit.keyRun same, result⟩ + · rw [inferKey_environment hit.keyRun] at only + obtain ⟨event, member, _, same, result⟩ := history.only_origin only + exact ⟨event, member, data.same member hit.keyRun same, result⟩ + +end InferenceCacheHistory + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean index 05223854a..24fccc892 100644 --- a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean +++ b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean @@ -13,7 +13,9 @@ import Ix.Kernel.Verify.Consistency.SourceOwnershipCheck Finite operational trees follow the actual smaller method table, recording the keys written by successful misses. An entry outside those writes and its loaded declaration survive the entire inference, so a closed cached witness -can be reused afterward. No semantic typing or per-call cache frame is an input. +can be reused afterward. Every initially populated full key is proved to be +outside those writes by cache priority. No semantic typing or per-call cache +frame is an input. -/ namespace Ix.Kernel.Consistency @@ -458,6 +460,91 @@ theorem InferenceCacheTrace.frame {fuel : Nat} {before after : TcState .anon} exact ⟨(domainFrame.trans (opening.trans bodyFrame)).trans (.of_eq rfl rfl rfl), bodyPolicy.trans ((openBinder_policy trace.openRun).trans domainPolicy)⟩ +/-- A key already occupied in the full cache cannot be missed. Key +memoization leaves the maps unchanged, and full entries precede both policies. -/ +private theorem UncachedInference.ne_populated {before : TcState .anon} {source result : KExpr .anon} + {key : Address × Address} (miss : UncachedInference before source) + (stored : before.env.inferCache[key]? = some result) : key ≠ miss.key := by + intro same + have keyed : miss.keyed.env.inferCache[key]? = some result := by + rw [inferKey_environment miss.keyRun] + exact stored + rw [same, miss.fullMiss] at keyed + cases keyed + +/-- The write footprint excludes every full entry present at call entry. +This is derived from selection and the actual child calls, with no collision +or disjoint-write premise. Full entries are eligible under either policy. -/ +theorem InferenceCacheTrace.populated_outside {fuel : Nat} {before : TcState .anon} + {term cached : KExpr .anon} {key : Address × Address} + (tree : InferenceCacheTrace.{u} fuel before term) + (stored : before.env.inferCache[key]? = some cached) : key ∉ tree.writes := by + induction tree with + | hit => simp [writes] + | sort miss | fvar miss | const miss concrete loaded resources | lazyConst miss loader resources => + simpa only [writes, List.mem_singleton] using miss.ne_populated stored + | app full miss trace hashPath functionTree argumentTree functionIH argumentIH => + have keyed : miss.keyed.env.inferCache[key]? = some cached := by + rw [inferKey_environment miss.keyRun] + exact stored + have first := functionIH keyed + have next := (functionTree.frame first trace.functionRun).1.full.trans keyed + exact by + simpa only [writes, List.mem_cons, List.mem_append, not_or] using + ⟨miss.ne_populated stored, first, argumentIH next⟩ + | appBeta full miss trace exposure hashPath functionTree argumentTree functionIH argumentIH => + have keyed : miss.keyed.env.inferCache[key]? = some cached := by + rw [inferKey_environment miss.keyRun] + exact stored + have first := functionIH keyed + have inferred := (functionTree.frame first trace.functionRun).1.full.trans keyed + have next : trace.exposedState.env.inferCache[key]? = some cached := by + rw [trace.exposure_state exposure] + exact (exposure.inference_frame key).full.trans inferred + exact by + simpa only [writes, List.mem_cons, List.mem_append, not_or] using + ⟨miss.ne_populated stored, first, argumentIH next⟩ + | forallE miss trace domainTree bodyTree domainIH bodyIH => + have keyed : miss.keyed.env.inferCache[key]? = some cached := by + rw [inferKey_environment miss.keyRun] + exact stored + have first := domainIH keyed + have inferred := (domainTree.frame first trace.domainRun).1.full.trans keyed + have next := (openBinder_frame key trace.openRun).full.trans inferred + exact by + simpa only [writes, List.mem_cons, List.mem_append, not_or] using + ⟨miss.ne_populated stored, first, bodyIH next⟩ + | lam full miss trace domainTree bodyTree domainIH bodyIH => + have keyed : miss.keyed.env.inferCache[key]? = some cached := by + rw [inferKey_environment miss.keyRun] + exact stored + have first := domainIH keyed + have inferred := (domainTree.frame first trace.domainRun).1.full.trans keyed + have next := (openBinder_frame key trace.openRun).full.trans inferred + exact by + simpa only [writes, List.mem_cons, List.mem_append, not_or] using + ⟨miss.ne_populated stored, first, bodyIH next⟩ + | lamBody full miss trace domainTree bodyTree domainIH bodyIH => + have keyed : miss.keyed.env.inferCache[key]? = some cached := by + rw [inferKey_environment miss.keyRun] + exact stored + have first := domainIH keyed + have inferred := (domainTree.frame first trace.domainRun).1.full.trans keyed + have next := (openBinder_frame key trace.openRun).full.trans inferred + exact by + simpa only [writes, List.mem_cons, List.mem_append, not_or] using + ⟨miss.ne_populated stored, first, bodyIH next⟩ + +/-- A successful recursive call preserves both partitions at each initially +populated full key, together with every previously loaded declaration. -/ +theorem InferenceCacheTrace.populated_frame {fuel : Nat} {before after : TcState .anon} + {term result cached : KExpr .anon} {key : Address × Address} + (tree : InferenceCacheTrace.{u} fuel before term) + (stored : before.env.inferCache[key]? = some cached) + (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : + InferenceCacheFrame key before after ∧ after.inferOnly = before.inferOnly := + tree.frame (tree.populated_outside stored) accepted + /-- A verified constant call needs no separately constructed operational tree: the real key and cache selection build its hit or lazy-miss leaf. -/ theorem infer_verifiedConst_cache_frame {fuel : Nat} {before keyed after : TcState .anon} diff --git a/Ix/Kernel/Verify/Consistency/SynthesisCache.lean b/Ix/Kernel/Verify/Consistency/SynthesisCache.lean index 01b0e6f23..cbfe852e3 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisCache.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisCache.lean @@ -84,10 +84,11 @@ def SynthesisInference.reuseFullAcross {β : Type u} {resolve : Address → Opti (full : before.inferOnly = false) (closed : source.lbr = 0) (accepted : RecM.infer source (methodsN fuel) before = .ok result after) (intervening : InferenceCacheTrace.{w} otherFuel after other) - (outside : (source.addr, emptyCtxAddr) ∉ intervening.writes) (otherRun : RecM.infer other (methodsN otherFuel) after = .ok otherResult current) : SynthesisInference resolve entries locals context bounds nextFuel current source term type level := - tree.reuseFullClosed formed agreement reading full closed accepted (intervening.frame outside otherRun).1 + tree.reuseFullClosed formed agreement reading full closed accepted + (intervening.populated_frame + (infer_full_success_cache full (inferKey_closed closed before) accepted) otherRun).1 /-- The concrete full-cache entry and its actual checking provenance. An empty anchor context lets the retained tree cross later interfaces and @@ -175,15 +176,14 @@ def afterOpenBinder (cached : CachedSynthesisCheck resolve anchor entries locals rw [accepted] at preserved exact (cached.frame preserved).weaken absent domain -/-- Recursive inference derives its own write footprint and frame, including -lazy loading, beta Pi exposure, and changed cheap-beta lambda bodies. -/ +/-- A populated full entry survives recursive inference, including calls at +its own key. Cache priority derives the required write exclusion. -/ def afterInference (cached : CachedSynthesisCheck resolve anchor entries locals context state source term type level) {fuel : Nat} {other result : KExpr .anon} {after : TcState .anon} (trace : InferenceCacheTrace.{w} fuel state other) - (outside : (source.addr, emptyCtxAddr) ∉ trace.writes) (accepted : RecM.infer other (methodsN fuel) state = .ok result after) : CachedSynthesisCheck resolve anchor entries locals context after source term type level := - cached.frame (trace.frame outside accepted).1 + cached.frame (trace.populated_frame cached.stored accepted).1 /-- Selection follows from the retained full entry under either policy. No new observation or agreement premise is supplied at reuse. -/ diff --git a/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean b/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean new file mode 100644 index 000000000..c27967003 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean @@ -0,0 +1,224 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.SynthesisCacheHistory + +/-! The original synthesis recursion supplies the annotations of actual cache +publications. Supplementary data concerns only operational resources and child +calls omitted by the older, checking-only BinderInference interface. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +structure SynthesisCacheRun {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) {fuel : Nat} {before after : TcState .anon} + {source result : KExpr .anon} + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) where + trace : InferenceCacheTrace.{u} fuel before source + checks : SynthesisEventChecks resolve anchor entries (trace.events accepted) + +/-- The older checking-only interface can omit executed domain checks and +loader resources. This supplement records its actual operational tree and +the checks of its child publications. The root check is supplied separately +by the original SynthesisInference node. -/ +structure SynthesisCacheSupplement {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (fuel : Nat) (state : TcState .anon) (source : KExpr .anon) where + trace : InferenceCacheTrace.{u} fuel state source + children : SynthesisEventChecks resolve anchor entries trace.childEvents + +namespace SynthesisCacheSupplement + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} + +def ofLeaf (trace : InferenceCacheTrace.{u} fuel before source) (leaf : trace.childEvents = []) : + SynthesisCacheSupplement resolve anchor entries fuel before source := ⟨trace, leaf.symm ▸ .nil⟩ + +/-- Primitive cache observations require no supplementary checking trees. -/ +def sortOfKey {keyed : TcState .anon} {level : KUniv .anon} {info : ExprInfo .anon} + {key : Address × Address} (run : TcM.inferKey (.sort level info) before = .ok key keyed) : + SynthesisCacheSupplement resolve anchor entries fuel before (.sort level info) := + ofLeaf (.sortOfKey run) (by + unfold InferenceCacheTrace.sortOfKey + rcases observeInferenceCache run with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ <;> rfl) + +def fvarOfKey {keyed : TcState .anon} {id : FVarId} {name : Mode.anon.F Name} {info : ExprInfo .anon} + {key : Address × Address} (run : TcM.inferKey (.fvar id name info) before = .ok key keyed) : + SynthesisCacheSupplement resolve anchor entries fuel before (.fvar id name info) := + ofLeaf (.fvarOfKey run) (by + unfold InferenceCacheTrace.fvarOfKey + rcases observeInferenceCache run with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ <;> rfl) + +def verifiedConstOfKey {keyed : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {key : Address × Address} + (run : TcM.inferKey (.const id arguments info) before = .ok key keyed) + (loader : VerifiedLazySupport keyed id.addr) + (resources : ∀ concrete loaded, TcM.getConst id keyed = .ok concrete loaded → + UniverseInstantiationSupport loaded concrete.ty arguments) : + SynthesisCacheSupplement resolve anchor entries fuel before (.const id arguments info) := + ofLeaf (.verifiedConstOfKey run loader resources) (by + unfold InferenceCacheTrace.verifiedConstOfKey + rcases observeInferenceCache run with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ <;> rfl) + +def complete (data : SynthesisCacheSupplement resolve anchor entries fuel before source) + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} + {term type : AExpr β} {level : VLevel} {result : KExpr .anon} {after : TcState .anon} + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (contextOrigin : SynthesisContext resolve anchor [] [] entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisCacheRun resolve anchor entries accepted := + { trace := data.trace + checks := by + rcases data with ⟨trace, children⟩ + cases trace with + | hit => exact .nil + | sort miss | fvar miss | const miss concrete loaded resources | lazyConst miss loader resources => + exact .singleton (.ofSource tree contextOrigin agreement reading miss accepted) + | app full miss trace hashPath functionTree argumentTree => + exact children.append (.singleton (.ofSource tree contextOrigin agreement reading miss accepted)) + | appBeta full miss trace exposure hashPath functionTree argumentTree => + exact children.append (.singleton (.ofSource tree contextOrigin agreement reading miss accepted)) + | forallE miss trace domainTree bodyTree => + exact children.append (.singleton (.ofSource tree contextOrigin agreement reading miss accepted)) + | lam full miss trace domainTree bodyTree => + exact children.append (.singleton (.ofSource tree contextOrigin agreement reading miss accepted)) + | lamBody full miss trace domainTree bodyTree => + exact children.append (.singleton (.ofSource tree contextOrigin agreement reading miss accepted)) } + +end SynthesisCacheSupplement + +/-- Rich synthesis nodes already retain both actual children. Additional +checking annotations are needed only at the older BinderInference wrappers; +primitive leaves supply none. Existing cache hits add no publications. -/ +def SynthesisInference.CacheData {β : Type u} {resolve : Address → Option (ConstRef β)} + (anchor : Model.Environment β) {entries : Model.Environment β} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} {before : TcState .anon} + {source : KExpr .anon} {term type : AExpr β} {level : VLevel} + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) : Type (u + 1) := + match tree with + | .known .. | .reuseType .. | .fvar .. => SynthesisCacheSupplement resolve anchor entries fuel before source + | .cached .. | .cachedFrom .. => PUnit + | .app _ _ _ first second .. | .appBeta _ _ _ first _ _ _ second .. => + first.CacheData anchor × second.CacheData anchor + | .forallE _ _ _ _ first second .. | .lam _ _ _ _ _ first second .. | + .lamBeta _ _ _ _ _ first second .. => first.CacheData anchor × second.CacheData anchor +termination_by structural tree + +/-- Extract every full publication's checking origin from the original +synthesis recursion. Binder contexts come from its executed domain checks, +and all child readings and local agreements are derived along the same run. -/ +def SynthesisInference.cacheExecution {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) : + tree.CacheData anchor → + SynthesisContext resolve anchor [] [] entries context bounds → + LocalContextReading resolve locals before.lctx context → + readScopedExpr? resolve locals source = some term.erase → + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) → + SynthesisCacheRun resolve anchor entries accepted := + match tree with + | .known inference formation => fun data contextOrigin agreement reading accepted => + data.complete (.known inference formation) contextOrigin agreement reading accepted + | .reuseType inference typeTree extension typeReading typeRun same => + fun data contextOrigin agreement reading accepted => + data.complete (.reuseType inference typeTree extension typeReading typeRun same) + contextOrigin agreement reading accepted + | .fvar inference atIndex boundAtIndex => fun data contextOrigin agreement reading accepted => + data.complete (.fvar inference atIndex boundAtIndex) contextOrigin agreement reading accepted + | .cached _ _ _ _ hit _ _ | .cachedFrom _ hit _ => fun _ _ _ _ _ => ⟨.hit hit, .nil⟩ + | .app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful + bodyConstructed argConstructed bodyBound argBound coherent faithful => + fun data contextOrigin agreement reading accepted => by + obtain ⟨functionReads, argumentReads⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + let functionRun := functionTree.cacheExecution data.1 contextOrigin keyedAgreement functionReads trace.functionRun + let argumentRun := argumentTree.cacheExecution data.2 contextOrigin + (trace.contextPreserved.symm ▸ keyedAgreement) argumentReads trace.argumentRun + exact (SynthesisCacheSupplement.mk (.app full miss trace hashPath functionRun.trace argumentRun.trace) + (functionRun.checks.append argumentRun.checks)).complete + (.app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful + bodyConstructed argConstructed bodyBound argBound coherent faithful) + contextOrigin agreement reading accepted + | .appBeta full miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath + comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful => + fun data contextOrigin agreement reading accepted => by + obtain ⟨functionReads, argumentReads⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + let functionRun := functionTree.cacheExecution data.1 contextOrigin keyedAgreement functionReads trace.functionRun + let argumentRun := argumentTree.cacheExecution data.2 contextOrigin + ((trace.exposure_context exposure).symm ▸ keyedAgreement) argumentReads trace.argumentRun + exact (SynthesisCacheSupplement.mk (.appBeta full miss trace exposure hashPath functionRun.trace argumentRun.trace) + (functionRun.checks.append argumentRun.checks)).complete + (.appBeta full miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath + comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful) + contextOrigin agreement reading accepted + | .forallE miss trace opening absent domainTree bodyTree levelFaithful domainBound bodyBound coherent faithful => + fun data contextOrigin agreement reading accepted => by + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + let domainRun := domainTree.cacheExecution data.1 contextOrigin keyedAgreement domainReads trace.domainRun + obtain ⟨_, openedReads, openedAgreement, _⟩ := openBinder_sound opening + (trace.contextPreserved.symm ▸ keyedAgreement) absent domainReads bodyReads trace.openRun + let bodyRun := bodyTree.cacheExecution data.2 + (.push contextOrigin domainTree keyedAgreement domainReads trace.domainRun) openedAgreement openedReads trace.bodyRun + exact (SynthesisCacheSupplement.mk (.forallE miss trace domainRun.trace bodyRun.trace) + (domainRun.checks.append bodyRun.checks)).complete + (.forallE miss trace opening absent domainTree bodyTree levelFaithful domainBound bodyBound coherent faithful) + contextOrigin agreement reading accepted + | .lam full miss trace opening absent domainTree bodyTree conditionAgrees constructed bound coherent + closingFaithful faithful => + fun data contextOrigin agreement reading accepted => by + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + let domainRun := domainTree.cacheExecution data.1 contextOrigin keyedAgreement domainReads trace.domainRun + obtain ⟨_, openedReads, openedAgreement, _⟩ := openBinder_sound opening + (trace.contextPreserved.symm ▸ keyedAgreement) absent domainReads bodyReads trace.openRun + let bodyRun := bodyTree.cacheExecution data.2 + (.push contextOrigin domainTree keyedAgreement domainReads trace.domainRun) openedAgreement openedReads trace.bodyRun + exact (SynthesisCacheSupplement.mk (.lam full miss trace domainRun.trace bodyRun.trace) + (domainRun.checks.append bodyRun.checks)).complete + (.lam full miss trace opening absent domainTree bodyTree conditionAgrees constructed bound coherent + closingFaithful faithful) contextOrigin agreement reading accepted + | .lamBeta full miss trace opening absent domainTree bodyTree origin reduction conditionAgrees + constructed bound closingFaithful faithful => + fun data contextOrigin agreement reading accepted => by + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + let domainRun := domainTree.cacheExecution data.1 contextOrigin keyedAgreement domainReads trace.domainRun + obtain ⟨_, openedReads, openedAgreement, _⟩ := openBinder_sound opening + (trace.contextPreserved.symm ▸ keyedAgreement) absent domainReads bodyReads trace.openRun + let bodyRun := bodyTree.cacheExecution data.2 + (.push contextOrigin domainTree keyedAgreement domainReads trace.domainRun) openedAgreement openedReads trace.bodyRun + exact (SynthesisCacheSupplement.mk (.lamBody full miss trace domainRun.trace bodyRun.trace) + (domainRun.checks.append bodyRun.checks)).complete + (.lamBeta full miss trace opening absent domainTree bodyTree origin reduction conditionAgrees + constructed bound closingFaithful faithful) contextOrigin agreement reading accepted +termination_by structural tree + +/-- A successful synthesis call extends the complete typed cache history. +New event annotations are extracted from its original checking tree. -/ +def SynthesisCacheHistory.afterSynthesis {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (history : SynthesisCacheHistory resolve anchor entries before) + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (data : tree.CacheData anchor) + (contextOrigin : SynthesisContext resolve anchor [] [] entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisCacheHistory resolve anchor entries after := + let executed := tree.cacheExecution data contextOrigin agreement reading accepted + history.afterInference executed.trace accepted executed.checks + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SynthesisCacheHistory.lean b/Ix/Kernel/Verify/Consistency/SynthesisCacheHistory.lean new file mode 100644 index 000000000..b45c49a70 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SynthesisCacheHistory.lean @@ -0,0 +1,215 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.CacheHistory +import Ix.Kernel.Verify.Consistency.SynthesisCache + +/-! Full-cache history retains the original executed synthesis checks. +Annotations are attached to actual publication events; the exact map history +then finds their origins at any selected full-cache key. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- An annotation of an actual earlier call. It retains the complete original +tree and the domain checks that formed its context. Interface growth does not +discard those origins. Cached semantics and result readings are derived. -/ +structure SynthesisEventCheck {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (event : InferenceCacheEvent) where + earlier : Model.Environment β + locals : List FVarId + context : Model.Context β + bounds : List VLevel + term : AExpr β + type : AExpr β + level : VLevel + tree : SynthesisInference resolve earlier locals context bounds event.fuel event.before event.source term type level + contextOrigin : SynthesisContext resolve anchor [] [] earlier context bounds + extension : InterfaceExtends earlier entries + agreement : LocalContextReading resolve locals event.before.lctx context + reading : readScopedExpr? resolve locals event.source = some term.erase + +namespace SynthesisEventCheck + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {event : InferenceCacheEvent} + +def ofSource {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} + {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (contextOrigin : SynthesisContext resolve anchor [] [] entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (miss : UncachedInference before source) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisEventCheck resolve anchor entries (.ofRun miss accepted) := + ⟨entries, locals, context, bounds, term, type, level, tree, contextOrigin, .refl _, agreement, reading⟩ + +def extend (check : SynthesisEventCheck resolve anchor entries event) + {later : Model.Environment β} (extension : InterfaceExtends entries later) : + SynthesisEventCheck resolve anchor later event := + { check with extension := check.extension.trans extension } + +def retained (check : SynthesisEventCheck resolve anchor entries event) : + SynthesisRetainedCheck resolve anchor [] [] entries check.context check.term check.type check.level := + (SynthesisRetainedCheck.source check.contextOrigin check.tree check.agreement check.reading event.accepted).extend + check.extension + +theorem result_reading (check : SynthesisEventCheck resolve anchor entries event) : + readScopedExpr? resolve check.locals event.result = some check.type.erase := + (check.tree.soundWithSpine.{u,u} (check.contextOrigin.sound (.empty _)) + check.agreement check.reading event.accepted).1 + +/-- Selection supplies the physical slot; the retained original call supplies +all checking evidence and the cached result's reading. -/ +def cached (check : SynthesisEventCheck resolve anchor entries event) + {state : TcState .anon} (closed : event.source.lbr = 0) + (stored : state.env.inferCache[(event.source.addr, emptyCtxAddr)]? = some event.result) : + CachedSynthesisCheck resolve anchor entries check.locals check.context state event.source + check.term check.type check.level := + ⟨event.result, closed, check.reading, check.result_reading, check.retained, stored⟩ + +end SynthesisEventCheck + +/-- Only full publications need full checking trees. Inference-only values +still have operational origins in the underlying history. -/ +def SynthesisEventChecks {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (events : List InferenceCacheEvent) : Prop := + ∀ event ∈ events, event.before.inferOnly = false → Nonempty (SynthesisEventCheck resolve anchor entries event) + +namespace SynthesisEventChecks + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {anchor entries : Model.Environment β} + +theorem nil : SynthesisEventChecks resolve anchor entries [] := by intro event member; cases member + +theorem append {first second : List InferenceCacheEvent} + (left : SynthesisEventChecks resolve anchor entries first) + (right : SynthesisEventChecks resolve anchor entries second) : + SynthesisEventChecks resolve anchor entries (first ++ second) := by + intro event member full + rcases List.mem_append.mp member with old | next + · exact left event old full + · exact right event next full + +theorem singleton {event : InferenceCacheEvent} (check : SynthesisEventCheck resolve anchor entries event) : + SynthesisEventChecks resolve anchor entries [event] := by + intro candidate member full + have same := List.mem_singleton.mp member + subst candidate + exact ⟨check⟩ + +theorem extend {events : List InferenceCacheEvent} (checks : SynthesisEventChecks resolve anchor entries events) + {later : Model.Environment β} (extension : InterfaceExtends entries later) : + SynthesisEventChecks resolve anchor later events := by + intro event member full + obtain ⟨check⟩ := checks event member full + exact ⟨check.extend extension⟩ + +end SynthesisEventChecks + +/-- A complete map history plus the actual original checks behind every full +publication. The original local contexts survive scope exits as provenance. -/ +structure SynthesisCacheHistory {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (state : TcState .anon) where + execution : InferenceCacheHistory state + checks : SynthesisEventChecks resolve anchor entries execution.events + +/-- A selected full value, with the original context and all retained checks. +The caller can subsequently use the proved interface and local transports. -/ +structure SynthesisCacheSelection {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (state : TcState .anon) (source result : KExpr .anon) where + locals : List FVarId + context : Model.Context β + term : AExpr β + type : AExpr β + level : VLevel + cached : CachedSynthesisCheck resolve anchor entries locals context state source term type level + result_eq : cached.result = result + +namespace SynthesisCacheHistory + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {state : TcState .anon} + +def initial (source : Ixon.Env) : SynthesisCacheHistory resolve anchor entries (TcState.newLazyAnon source) := + ⟨.initial source, .nil⟩ + +def ofMaps (history : SynthesisCacheHistory resolve anchor entries state) {after : TcState .anon} + (full : after.env.inferCache = state.env.inferCache) + (only : after.env.inferOnlyCache = state.env.inferOnlyCache) : + SynthesisCacheHistory resolve anchor entries after := ⟨history.execution.ofMaps full only, history.checks⟩ + +def extend (history : SynthesisCacheHistory resolve anchor entries state) + {later : Model.Environment β} (extension : InterfaceExtends entries later) : + SynthesisCacheHistory resolve anchor later state := ⟨history.execution, history.checks.extend extension⟩ + +def afterInference (history : SynthesisCacheHistory resolve anchor entries state) + {fuel : Nat} {after : TcState .anon} {source result : KExpr .anon} + (tree : InferenceCacheTrace.{v} fuel state source) + (accepted : RecM.infer source (methodsN fuel) state = .ok result after) + (checks : SynthesisEventChecks resolve anchor entries (tree.events accepted)) : + SynthesisCacheHistory resolve anchor entries after := + ⟨history.execution.afterInference tree accepted, history.checks.append checks⟩ + +def policy (history : SynthesisCacheHistory resolve anchor entries state) (policy : Bool) : + SynthesisCacheHistory resolve anchor entries {state with inferOnly := policy} := history.ofMaps rfl rfl + +def truncate (history : SynthesisCacheHistory resolve anchor entries state) (size : Nat) : + SynthesisCacheHistory resolve anchor entries {state with lctx := state.lctx.truncate size} := history.ofMaps rfl rfl + +def openBinder (history : SynthesisCacheHistory resolve anchor entries state) + {after : TcState .anon} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body opened : KExpr .anon} {fresh : FVarId} + (accepted : TcM.openBinder name bi domain body state = .ok (opened, fresh) after) : + SynthesisCacheHistory resolve anchor entries after := ⟨history.execution.openBinder accepted, history.checks⟩ + +def getConst (history : SynthesisCacheHistory resolve anchor entries state) {id : KId .anon} + (loader : VerifiedLazySupport state id.addr) : + match TcM.getConst id state with + | .ok _ after | .error _ after => SynthesisCacheHistory resolve anchor entries after := by + have preserved := getConst_verified_cache loader + cases run : TcM.getConst id state <;> rw [run] at preserved <;> + exact history.ofMaps preserved.environment.full preserved.environment.only + +def clear (_history : SynthesisCacheHistory resolve anchor entries state) : + SynthesisCacheHistory resolve anchor entries {state with env := state.env.clearReductionCaches} := + ⟨_history.execution.clear, .nil⟩ + +/-- Every selected full entry materializes its original synthesis cache +interface. The source is recovered from the finite collision data; no cached +typing, result reading, earlier run, or catalog membership is supplied. -/ +theorem select (history : SynthesisCacheHistory resolve anchor entries state) + {source result : KExpr .anon} (closed : source.lbr = 0) + (data : history.execution.KeyData source) + (stored : state.env.inferCache[(source.addr, emptyCtxAddr)]? = some result) : + Nonempty (SynthesisCacheSelection resolve anchor entries state source result) := by + obtain ⟨event, member, full, same, value⟩ := history.execution.full_origin stored + obtain ⟨check⟩ := history.checks event member full + have sourceEq := data.same member (inferKey_closed closed state) same + have entry : state.env.inferCache[(event.source.addr, emptyCtxAddr)]? = some event.result := by + rwa [sourceEq, value] + let cached := check.cached (sourceEq.symm ▸ closed) entry + refine ⟨⟨check.locals, check.context, check.term, check.type, check.level, sourceEq ▸ cached, ?_⟩⟩ + cases sourceEq + exact value + +/-- Observe the actual map. A full hit has a history-derived cache interface +under either checking policy, including when the method table has no fuel. -/ +theorem observe (history : SynthesisCacheHistory resolve anchor entries state) + {source : KExpr .anon} (closed : source.lbr = 0) (data : history.execution.KeyData source) : + state.env.inferCache[(source.addr, emptyCtxAddr)]? = none ∨ + ∃ result, Nonempty (SynthesisCacheSelection resolve anchor entries state source result) := by + cases stored : state.env.inferCache[(source.addr, emptyCtxAddr)]? with + | none => exact .inl rfl + | some result => exact .inr ⟨result, history.select closed data stored⟩ + +end SynthesisCacheHistory + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean b/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean index 12a2805fe..6943941ff 100644 --- a/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean +++ b/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean @@ -75,4 +75,30 @@ theorem BetaPiExposure.policy {β : Type u} {resolve : Address → Option (Const | cached origin coherent hit => simp only [BetaPiExposure.after, BetaPublicWhnf.outerKey, betaWhnfKey_policy, betaWhnfPrefix_policy] +/-- Public beta WHNF preserves the complete inference maps. This gives an +exact update trace for its surrounding inference, including keys written there. -/ +theorem BetaPublicWhnfPlan.inference_maps {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : + plan.after.env.inferCache = before.env.inferCache ∧ + plan.after.env.inferOnlyCache = before.env.inferOnlyCache := by + obtain ⟨table, reduced⟩ := plan.path.frame + constructor <;> + simp only [BetaPublicWhnfPlan.after, BetaPublicWhnf.after, BetaPublicWhnf.noDeltaAfter, + BetaPublicWhnf.coreAfter, reduced, (BetaPublicWhnf.coreKey_fields source before).1] + +theorem BetaPiExposure.inference_maps {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source rawDomain rawBody : KExpr .anon} {term domain body : AExpr β} {condition : Certified.PropWhen} + (exposure : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody) : + exposure.after.env.inferCache = before.env.inferCache ∧ + exposure.after.env.inferOnlyCache = before.env.inferOnlyCache := by + cases exposure with + | reduce plan => exact plan.inference_maps + | cached origin coherent hit => + constructor <;> + simp only [BetaPiExposure.after, BetaPublicWhnf.outerKey, betaWhnfKey_environment, + (betaWhnfPrefix_fields before).1] + end Ix.Kernel.Consistency diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 796c5b733..7457c4953 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -3365,6 +3365,90 @@ private def compositeCodomainTransport (shape : Nat) (level : Ixon.Univ) : Bool | .ok passed after => passed && after.lctx.size == 0 | .error _ _ => false +/-- Compare both entire maps, including temporary-local and composite keys. -/ +private def exactInferenceCaches (full only : List (KExpr .anon × KExpr .anon)) (state : TcState .anon) : Bool := + let expected (entries : List (KExpr .anon × KExpr .anon)) := entries.foldl + (fun (cache : Std.HashMap (Address × Address) (KExpr .anon)) (source, result) => + cache.insert (source.addr, emptyCtxAddr) result) ∅ + let sameMap (actual predicted : Std.HashMap (Address × Address) (KExpr .anon)) := + actual.size == predicted.size && predicted.toList.all fun (key, result) => actual[key]? == some result + sameMap state.env.inferCache (expected full) && sameMap state.env.inferOnlyCache (expected only) + +/-- Whole-map expectations follow actual child calls and parent publications. +Entries from exited local scopes remain recorded; loading and partial failure +preserve both maps, and clearing begins a new set of publications. -/ +private def compositeExactCacheHistory (initialOnly : Bool) : Bool := + let (source, warm) := polymorphicIdentity + let (source, bad) := storeConst source + ⟨.defn ⟨.defn, .safe, 0, .sort 0, .share 9⟩, #[], #[], + #[.succ (.succ (.succ (.succ (.succ .zero))))]⟩ + let prop := KExpr.mkSort (m := .anon) .mkZero + let sort := KExpr.mkSort (m := .anon) levelOne + let sortTwo := KExpr.mkSort (m := .anon) levelTwo + let pi := KExpr.mkAll () () sort sort + let lambda := KExpr.mkLam () () sort (.mkVar 0 ()) + let application := KExpr.mkApp lambda prop + let first := [(sort, sortTwo), (pi, sortTwo)] + let only := if initialOnly then first else [] + let action : RecM .anon Bool := do + if !exactInferenceCaches [] [] (← get) then return false + let inferredPi ← if initialOnly then RecM.inferOnlyCall pi else RecM.inferCall pi + if inferredPi != sortTwo || !exactInferenceCaches (if initialOnly then [] else first) only (← get) then + return false + let lambdaLocal := KExpr.mkFVar ⟨(← get).env.nextFVarId⟩ () + let inferredLambda ← RecM.inferCall lambda + let checked := (if initialOnly then [] else first) ++ [(sort, sortTwo), (lambdaLocal, sort), (lambda, pi)] + if inferredLambda != pi || !exactInferenceCaches checked only (← get) then return false + let inferredApplication ← RecM.inferCall application + let applied := checked ++ [(prop, sort), (application, sort)] + if inferredApplication != sort || !exactInferenceCaches applied only (← get) then return false + let fullPi ← RecM.inferCall pi + let completed := applied ++ [(pi, sortTwo)] + let onlyReplay ← RecM.inferOnlyCall application + if fullPi != sortTwo || onlyReplay != sort || !exactInferenceCaches completed only (← get) then return false + let (scopedLocal, scopePassed) ← RecM.withLctxScope do + let (scopedLocal, _) ← TcM.openBinder () () sort (.mkVar 0 ()) + let localType ← RecM.inferCall scopedLocal + return (scopedLocal, localType == sort && exactInferenceCaches (completed ++ [(scopedLocal, sort)]) only (← get)) + let retained := completed ++ [(scopedLocal, sort)] + if !scopePassed || (← get).lctx.size != 0 || !exactInferenceCaches retained only (← get) then return false + let _ ← TcM.getConst (m := .anon) ⟨warm, ()⟩ + if !exactInferenceCaches retained only (← get) then return false + let rejected ← try + let _ ← TcM.getConst (m := .anon) ⟨bad, ()⟩ + pure false + catch _ => pure true + if !rejected || !(← get).faultedAddrs.contains bad || !exactInferenceCaches retained only (← get) then return false + modify fun state => {state with env := state.env.clearReductionCaches} + if !exactInferenceCaches [] [] (← get) then return false + let nextLocal := KExpr.mkFVar ⟨(← get).env.nextFVarId⟩ () + let rebuilt ← RecM.inferCall application + return rebuilt == sort && nextLocal != lambdaLocal && exactInferenceCaches + [(sort, sortTwo), (nextLocal, sort), (lambda, pi), (prop, sort), (application, sort)] [] (← get) + match TcM.runRec action {TcState.newLazyAnon source with stats := true} with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +/-- The occupied-key frame needs no collision assumption: even a forged +different source hits the old full entry before executing recursive writes. +Semantic reuse separately requires the history's finite collision data. -/ +private def compositeOccupiedForeignKey : Bool := + let (source, addr) := polymorphicIdentity + let constant := KExpr.mkConst (m := .anon) ⟨addr, ()⟩ #[levelOne] + let prop := KExpr.mkSort (m := .anon) .mkZero + let forged := KExpr.app constant prop {(KExpr.mkApp constant prop).info with addr := constant.addr} + match TcM.infer constant (TcState.newLazyAnon source) with + | .error _ _ => false + | .ok result state => + let before := {state with recFuel := 0, env := {state.env with + inferOnlyCache := state.env.inferOnlyCache.insert (constant.addr, emptyCtxAddr) prop}} + [false, true].all fun inferOnly => + match (RecM.infer forged).run (methodsN 0) {before with inferOnly} with + | .error _ _ => false + | .ok replay after => replay == result && result == identityType && + exactInferenceCaches [(constant, identityType)] [(constant, prop)] after && + after.env.nextFVarId == before.env.nextFVarId && after.recFuel == 0 + private def compositeCacheCases : TestSeq := test "composite cache: applications survive lazy inference, scopes, replay, and clearing" (compositeCacheHistory 0) @@ -3391,6 +3475,10 @@ private def compositeCacheCases : TestSeq := ++ test "composite cache: transported Pi codomain and supplied-lambda checks justify later cheap beta" ([0, 1, 2].all fun shape => compositeCodomainTransport shape .zero && compositeCodomainTransport shape (.succ .zero)) + ++ test "cache history: both complete maps retain every child and parent publication across scopes, loading, and clearing" + (compositeExactCacheHistory false && compositeExactCacheHistory true) + ++ test "cache history: occupied full keys hit before recursive writes even for different syntax" + compositeOccupiedForeignKey /-- Definitions declare their own parameters. A two-parameter alias uses `max u v` to instantiate a one-parameter definition; a wrapper applies that diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 5210750bd..66f6a92e9 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -330,7 +330,9 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) and any already loaded declarations at their keys. This admits partially loaded blocks without assuming a cache frame or a final-state invariant. A separate `InferenceCacheTrace` derives a frame for an entire supported - recursive call at any key outside its computed writes. It shares the existing + recursive call at any key outside its computed writes. Full-cache priority + proves that every initially populated full key satisfies this exclusion. + It shares the existing application and binder execution traces and additionally follows lambda-domain inference. Constant misses use already-loaded declarations or verified standalone/block loading, with finite universe-walker resources after lookup; @@ -523,7 +525,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 705 exact theorem boundaries. The production +The consistency target checks 766 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -649,7 +651,7 @@ dependent codomain, and argument checks. `reuseFull` derives the cached result from a successful full call and a cache frame; `reuseFullClosed` computes the key for sources without loose variables, including registered free variables. `reuseFullAcross` derives the frame from an intervening recursive trace and -its write footprint. Such traces now include beta Pi exposure and changed +the earlier full entry, without a write-exclusion premise. Such traces include beta Pi exposure and changed cheap-beta lambda bodies. Public beta WHNF and Pi exposure preserve inference entries through their computed WHNF-cache updates. `infer_full_replay_closed` proves immediate reuse with any method table and either current checking @@ -667,10 +669,31 @@ reading. Its empty anchor context comes from actual source and domain checks. `extend`, `afterOpenBinder`, and `afterInference` transport the resource through interface growth and proved operational frames, including captured locals. `support` constructs the new inference branch; `run` proves immediate replay -under either policy and at any method-table fuel. General composite cache -histories, agreement at arbitrary written keys, and initial inference-resource -construction remain open. Clearing invalidates the stored entry and requires -a new full check. +under either policy and at any method-table fuel. Clearing invalidates the +stored entry and requires a new full check. +`InferenceCacheTrace.events` records every actual miss and its successful call, +in child-before-parent publication order. `cache_maps` reconstructs both complete +physical maps by folding these events. `InferenceCacheHistory` starts with empty +caches and preserves the reconstruction through supported inference, policy +changes, binder scopes, verified loading on both outcomes, and clearing. Every +present value consequently has an actual producing call, including at newly +written keys. This operational result needs no catalog or collision premise. +`SynthesisInference.cacheExecution` extracts full-publication annotations from +the original application, Pi, lambda, beta-exposure, and changed-beta trees. +Child readings and contexts follow their actual calls and domain checks. +Primitive leaves need only operational resources; older `BinderInference` +wrappers additionally supply checking annotations for child calls they omitted. +`SynthesisCacheHistory` retains these trees through interface growth and scope +changes. Selection uses collision freedom over the query and recorded inputs +to recover the same source, then builds `CachedSynthesisCheck` with its derived +result reading and all retained beta origins. This covers all full-cache keys +in the supported history, without a separate cached-typing or earlier-run +premise at selection. The selected check retains its original local context; +using entries from exited scopes in arbitrary later contexts still needs a +proved context relation. General execution/resource construction, missing +checking-only child annotations, other inference branches, and general WHNF +and conversion cache histories remain open. These 61 new audited roots retain +the existing production axioms and introduce no new axiom or native proof. Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, argument order, and rejection of wrong arities and out-of-range parameters. @@ -828,7 +851,12 @@ captured-local keys. They also combine declaration growth with nested dependent locals, application and beta reduction of cached lambdas, and reuse of Pi codomain checks before generated cheap beta. These execute production; the proof resources are checked separately by the consistency target. -The unit suite contains 709 checks. The anonymous differential additionally +Two further cache-history regressions compare both entire maps, including child, +parent, and exited-local entries, through policy changes, successful and failed +loading, clearing, and repopulation. A forged different input at an occupied +full key confirms that priority preserves the maps even without collision +freedom; semantic source recovery separately requires finite collision data. +The unit suite contains 711 checks. The anonymous differential additionally serializes eight cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. diff --git a/docs/theory.md b/docs/theory.md index 3902a7ddc..8d5d27aa3 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -132,9 +132,19 @@ reuse across supported recursive inference, including beta Pi exposure and changed cheap-beta lambda bodies; public beta WHNF preserves all inference entries. A concrete retained cache resource derives its result reading from full inference and constructs the transported hit in the original synthesis -recursion. General composite histories, agreement at arbitrary written keys, -and initial resource construction remain open. Frames allow new declarations -while retaining old ones. +recursion. Initially populated full keys survive supported recursive calls by +cache priority, without a write-exclusion premise. An exact event fold now +reconstructs both complete inference maps from actual child and parent calls. +Histories begin empty and retain the producing call at every present key through +inference, policy changes, scopes, verified loading on both outcomes, and clearing. +The original rich synthesis tree supplies all full-publication checks; the older +checking-only wrappers need supplementary annotations for omitted child calls. +Finite collision data over the query and historical inputs recovers the original +source. Selection then derives its cached result reading and complete retained +check, including the original local context and later interface transport. +Arbitrary execution construction, compatibility with later contexts after scope +exit, and the other inference and conversion/cache paths remain open. Frames +allow new declarations while retaining old ones. The actual verified loader preserves inference caches on success and failure, including partial intern progress and deduplicated faults. This covers standalone and mutual-block loading. Block From 913a96b0704796226fd15d6cccb49e5d80e32c00 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 11:24:17 -0400 Subject: [PATCH 37/63] Prove let inference in production consistency --- Ix/Kernel/Verify/Consistency.lean | 18 ++ Ix/Kernel/Verify/Consistency/Application.lean | 23 +- Ix/Kernel/Verify/Consistency/Audit.lean | 36 ++- .../Verify/Consistency/BinderOpening.lean | 22 +- .../Verify/Consistency/CacheExecution.lean | 21 ++ .../Verify/Consistency/CacheHistory.lean | 6 + .../Verify/Consistency/CheapBetaReading.lean | 3 +- .../Verify/Consistency/Dependencies.lean | 28 ++- Ix/Kernel/Verify/Consistency/LetCache.lean | 83 +++++++ .../Verify/Consistency/LetInference.lean | 139 ++++++++++++ Ix/Kernel/Verify/Consistency/LetOpening.lean | 96 ++++++++ .../Verify/Consistency/LetSynthesis.lean | 213 ++++++++++++++++++ Ix/Kernel/Verify/Consistency/Production.lean | 43 ++++ .../Verify/Consistency/RecursiveCache.lean | 46 ++++ Ix/Kernel/Verify/Consistency/ScopedExpr.lean | 54 ++++- .../Verify/Consistency/ScopedInstUniv.lean | 12 +- .../Verify/Consistency/Simultaneous.lean | 18 +- .../Verify/Consistency/SpineReading.lean | 28 ++- .../Consistency/SynthesisCacheExecution.lean | 2 + Ix/Kernel/Verify/Consistency/Validation.lean | 9 +- Ix/Kernel/Verify/Infer/CheapBetaPlan.lean | 16 ++ Ix/Theory/Model/Annotated.lean | 21 ++ Ix/Theory/Model/BetaSubstitution.lean | 69 ++++++ Tests/Ix/Kernel/Consistency.lean | 123 +++++++++- Tests/Theory/certified-foundation.txt | 2 +- docs/kernel-verification.md | 39 +++- docs/theory.md | 16 ++ 27 files changed, 1157 insertions(+), 29 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/LetCache.lean create mode 100644 Ix/Kernel/Verify/Consistency/LetInference.lean create mode 100644 Ix/Kernel/Verify/Consistency/LetOpening.lean create mode 100644 Ix/Kernel/Verify/Consistency/LetSynthesis.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 7eb70f2d3..9159df4e6 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -23,6 +23,10 @@ import Ix.Kernel.Verify.Consistency.BlockOwnership import Ix.Kernel.Verify.Consistency.SourceOwnershipCheck import Ix.Kernel.Verify.Consistency.Context import Ix.Kernel.Verify.Consistency.BinderOpening +import Ix.Kernel.Verify.Consistency.LetOpening +import Ix.Kernel.Verify.Consistency.LetInference +import Ix.Kernel.Verify.Consistency.LetSynthesis +import Ix.Kernel.Verify.Consistency.LetCache import Ix.Kernel.Verify.Consistency.Application import Ix.Kernel.Verify.Consistency.ApplicationWhnf import Ix.Kernel.Verify.Consistency.BinderInference @@ -154,6 +158,16 @@ The original synthesis admission theorem includes this application case. Constructing initial inference and operational resources for all accepted programs, other WHNF branches, mixed cache states, and general conversion remain open. +Full-mode let checks retain their original domain, value, and opened-body +synthesis checks. The scoped reader and actual opening, abstraction, and +substitution walkers interpret nested lets beneath locals and binders. Value +comparison identifies the declared domain; substitution preserves the complete +body checking derivation and inferred-type origin, including beta redexes +exposed by the value. The selected cheap-beta operation supplies the returned +type. Declaration admission consumes the same check and validation calls. +The three children remain in the existing SynthesisInference fragment; +arbitrary recursive let composition and automatic resource construction +remain open. Local cache hits agree with the actual declaration type. Constant hits agree with pure universe substitution of a loaded, admitted declaration; sort hits return the canonical successor sort. Applications, foralls, and @@ -202,6 +216,10 @@ selection recovers the original source and materializes its retained check in its original context, including interface transport. Arbitrary execution and resource construction, later context compatibility after scope exit, and the other inference and conversion/cache paths remain open. +The operational cache trace includes full lets and their three child calls. +The original let check and child cache data derive the full event fold and raw +history, preserving initially populated full keys. Retaining let roots in the +typed synthesis history still needs their recursive inference integration. Frames allow declaration growth while retaining every old declaration. Verified lazy loading derives such a frame on success and failure, including partial conversion state and fault deduplication. Standalone and block preparation have diff --git a/Ix/Kernel/Verify/Consistency/Application.lean b/Ix/Kernel/Verify/Consistency/Application.lean index 9af94b268..d57464ef7 100644 --- a/Ix/Kernel/Verify/Consistency/Application.lean +++ b/Ix/Kernel/Verify/Consistency/Application.lean @@ -66,7 +66,17 @@ theorem readScopedExpr?_liftSpec obtain ⟨ref, resolved, reading⟩ := bind_success reading cases reading simp [KExpr.liftSpec, readScopedExpr?, resolved, VExpr.liftN] - | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | str _ _ _ => contradiction + | letE name domain value body nonDep info ihDomain ihValue ihBody => + obtain ⟨A, v, b, domainReads, valueReads, bodyReads, rfl⟩ := readScopedExpr?_let_parts reading + simp only [KExpr.size] at bound + have next := depth_succ (depth := depth) (by omega) + have bodyOut := ihBody (depth := depth + 1) (by rw [next]; omega) + (by simpa only [next] using bodyReads) + simp only [next] at bodyOut + simp [KExpr.liftSpec, ihDomain (by omega) domainReads, ihValue (by omega) valueReads, + show depth.toNat + shift.toNat + 1 = depth.toNat + 1 + shift.toNat by omega, + bodyOut, VExpr.liftN_inst_zero] | app fn arg info hf ha => rw [readScopedExpr?] at reading obtain ⟨f, fReads, reading⟩ := bind_success reading @@ -141,7 +151,16 @@ theorem readScopedExpr?_substSpec obtain ⟨ref, resolved, bodyReads⟩ := bind_success bodyReads cases bodyReads simp [KExpr.substSpec, readScopedExpr?, resolved, VExpr.inst] - | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | str _ _ _ => contradiction + | letE name domain value body nonDep info ihDomain ihValue ihBody => + obtain ⟨A, v, b, domainReads, valueReads, innerReads, rfl⟩ := readScopedExpr?_let_parts bodyReads + simp only [KExpr.size] at bound + have next := depth_succ (depth := depth) (by omega) + have bodyOut := ihBody (depth := depth + 1) (by rw [next]; omega) + (by simpa only [next] using innerReads) + simp only [next] at bodyOut + simp [KExpr.substSpec, ihDomain (by omega) domainReads, ihValue (by omega) valueReads, + bodyOut, VExpr.inst_inst_zero] | app fn value info hf ha => rw [readScopedExpr?] at bodyReads obtain ⟨f, fReads, bodyReads⟩ := bind_success bodyReads diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 07b853cc9..26d3ae371 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -14,6 +14,7 @@ import Ix.Kernel.Verify.Consistency.Dependencies import Ix.Kernel.Verify.Consistency.CheapBeta import Ix.Kernel.Verify.Consistency.SynthesisCache import Ix.Kernel.Verify.Consistency.SynthesisCacheExecution +import Ix.Kernel.Verify.Consistency.LetCache import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -79,6 +80,17 @@ private def formationRoots : Array Lean.Name := #[ ``Theory.Model.ConversionClaim.inst ] +private def letRoots : Array Lean.Name := #[ + ``LetInferenceTrace.abstracted, ``LetInferenceTrace.substituted, ``LetInferenceTrace.reduced, + ``LetInferenceTrace.after, ``LetInferenceTrace.output_state, ``LetInferenceTrace.SubstitutionSupport, + ``LetInferenceTrace.substituted_reading, ``LetTypeReduction.reading, ``LetTypeReduction.trace, + ``LetInferenceCheck.source_reading, ``LetInferenceCheck.substituted_type_origin, + ``LetInferenceCheck.betaTyping, ``LetInferenceCheck.sound, ``LetInferenceCheck.closed_sound, + ``LetInferenceCheck.beta_steps_sound, ``DefinitionBodyTrace.letSupport, + ``InferenceCacheHistory.openLet, ``LetInferenceCheck.CacheData, ``LetInferenceCheck.cacheTrace, + ``LetInferenceCheck.cache_maps, ``LetInferenceCheck.cacheHistory +] + private def instantiationRoots : Array Lean.Name := #[ ``instUnivSpec_readExpr?, ``instUnivSpec_readExpr?_withScope, ``instantiateUnivParams_readExpr?, ``instantiateUnivParams_readAnnotated, @@ -680,6 +692,28 @@ def roots : Array RootAllowance := #[ { root := ``readScopedExpr?_lam_parts }, { root := ``readScopedExpr?_app_parts }, { root := ``readScopedExpr?_all_parts }, + { root := ``readScopedExpr?_let_parts, forbiddenDependencies := forbiddenProduction }, + { root := ``Theory.Model.AExpr.erase_surjective, forbiddenDependencies := forbiddenProduction }, + { root := ``Theory.VExpr.ClosedN.liftN, standardAxioms := #[``propext, ``Quot.sound], + forbiddenDependencies := forbiddenProduction }, + { root := ``Theory.VExpr.ClosedN.inst, standardAxioms := #[``propext, ``Quot.sound], + forbiddenDependencies := forbiddenProduction }, + { root := ``Theory.VExpr.liftN_inst_zero, standardAxioms := #[``propext, ``Quot.sound], + forbiddenDependencies := forbiddenProduction }, + { root := ``Theory.VExpr.inst_inst_zero, standardAxioms := #[``propext, ``Quot.sound], + forbiddenDependencies := forbiddenProduction }, + { root := ``Theory.VExpr.instRevAt_inst_zero, standardAxioms := #[``propext, ``Quot.sound], + forbiddenDependencies := forbiddenProduction }, + { root := ``readScopedExpr?_mkLet, standardAxioms := #[``propext, ``Classical.choice], + nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction }, + { root := ``cheapBetaPlan?_head_lambda, standardAxioms := standard, + nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction }, + { root := ``openLet_eq, standardAxioms := standard, + nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction }, + { root := ``openLet_inference_state, standardAxioms := standard, + nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction }, + { root := ``openLet_sound, standardAxioms := standard, + nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction }, { root := ``beq_readExpr?, standardAxioms := #[``propext, ``Quot.sound] }, { root := ``internExpr_readExpr?, standardAxioms := #[``propext, ``Quot.sound] }, { root := ``ModelTyping.sort, standardAxioms := standard, @@ -734,7 +768,7 @@ def roots : Array RootAllowance := #[ }) ++ (binderWalkerRoots ++ cacheKeyRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction -}) ++ (atomicRoots ++ instantiationRoots ++ recursiveCacheRoots ++ synthesisCacheRoots ++ cacheHistoryRoots ++ lazyCacheRoots ++ +}) ++ (atomicRoots ++ letRoots ++ instantiationRoots ++ recursiveCacheRoots ++ synthesisCacheRoots ++ cacheHistoryRoots ++ lazyCacheRoots ++ ownedLoaderRoots ++ recursiveStateRoots ++ sourceAgreementRoots ++ sourceCacheRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction diff --git a/Ix/Kernel/Verify/Consistency/BinderOpening.lean b/Ix/Kernel/Verify/Consistency/BinderOpening.lean index 16c35701e..2ddb0f52c 100644 --- a/Ix/Kernel/Verify/Consistency/BinderOpening.lean +++ b/Ix/Kernel/Verify/Consistency/BinderOpening.lean @@ -71,7 +71,16 @@ theorem readScopedExpr?_instantiateRevSpec simp [KExpr.instantiateRevSpec, readScopedExpr?, localIndex?_fresh absent found, Nat.add_assoc, Nat.add_comm 1] | sort _ _ | const _ _ _ | nat _ _ _ => exact reading - | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | str _ _ _ => contradiction + | letE name domain value body nonDep info ihDomain ihValue ihBody => + obtain ⟨A, v, b, domainReads, valueReads, bodyReads, rfl⟩ := readScopedExpr?_let_parts reading + simp only [KExpr.size] at bound + have next := depth_succ (depth := depth) (by omega) + have bodyOut := ihBody (depth := depth + 1) (by rw [next]; omega) + (by simpa only [next] using bodyReads) + simp only [next] at bodyOut + simp [KExpr.instantiateRevSpec, ihDomain (by omega) domainReads, + ihValue (by omega) valueReads, bodyOut] | app fn arg info hf ha => rw [readScopedExpr?] at reading obtain ⟨f, fReads, reading⟩ := bind_success reading @@ -151,7 +160,16 @@ theorem readScopedExpr?_abstractFVarsSpec readScopedExpr?, found, Nat.add_assoc, Nat.add_comm 1] | sort _ _ | const _ _ _ | nat _ _ _ => exact reading - | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | str _ _ _ => contradiction + | letE name domain value body nonDep info ihDomain ihValue ihBody => + obtain ⟨A, v, b, domainReads, valueReads, bodyReads, rfl⟩ := readScopedExpr?_let_parts reading + simp only [KExpr.size] at bound + have next := depth_succ (depth := depth) (by omega) + have bodyOut := ihBody (depth := depth + 1) (by rw [next]; omega) + (by simpa only [next] using bodyReads) + simp only [next] at bodyOut + simp [KExpr.abstractFVarsSpec, ihDomain (by omega) domainReads, + ihValue (by omega) valueReads, bodyOut] | app fn arg info hf ha => rw [readScopedExpr?] at reading obtain ⟨f, fReads, reading⟩ := bind_success reading diff --git a/Ix/Kernel/Verify/Consistency/CacheExecution.lean b/Ix/Kernel/Verify/Consistency/CacheExecution.lean index 803896bd2..9c28a833e 100644 --- a/Ix/Kernel/Verify/Consistency/CacheExecution.lean +++ b/Ix/Kernel/Verify/Consistency/CacheExecution.lean @@ -118,6 +118,9 @@ def InferenceCacheTrace.events {fuel : Nat} {before after : TcState .anon} domainTree.events trace.domainRun ++ bodyTree.events trace.bodyRun ++ [.ofRun miss accepted] | .lamBody _ miss trace domainTree bodyTree => domainTree.events trace.domainRun ++ bodyTree.events trace.bodyRun ++ [.ofRun miss accepted] + | .letE _ miss trace _ domainTree valueTree bodyTree => + domainTree.events trace.domainRun ++ valueTree.events trace.valueRun ++ + bodyTree.events trace.bodyRun ++ [.ofRun miss accepted] termination_by structural tree /-- Actual child publications, before the enclosing miss writes its result. -/ @@ -129,6 +132,8 @@ def InferenceCacheTrace.childEvents {fuel : Nat} {before : TcState .anon} {sourc | .forallE _ trace first second => first.events trace.domainRun ++ second.events trace.bodyRun | .lam _ _ trace first second => first.events trace.domainRun ++ second.events trace.bodyRun | .lamBody _ _ trace first second => first.events trace.domainRun ++ second.events trace.bodyRun + | .letE _ _ trace _ first second third => + first.events trace.domainRun ++ second.events trace.valueRun ++ third.events trace.bodyRun private theorem openBinder_maps {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body opened : KExpr .anon} {fresh : FVarId} {before after : TcState .anon} @@ -286,5 +291,21 @@ theorem InferenceCacheTrace.cache_maps {fuel : Nat} {before after : TcState .ano simp only [InferenceCacheEvent.applyFull_append, InferenceCacheEvent.applyOnly_append] exact ⟨bodyFull.trans (congrArg _ (openedFull.trans domainFull)), bodyOnly.trans (congrArg _ (openedOnly.trans domainOnly))⟩ + | letE full miss trace hashPath domainTree valueTree bodyTree domainIH valueIH bodyIH => + apply miss_maps miss accepted (domainTree.events trace.domainRun ++ valueTree.events trace.valueRun ++ + bodyTree.events trace.bodyRun) + intro middle run + rw [full] at run + obtain ⟨domainFull, domainOnly⟩ := domainIH trace.domainRun + obtain ⟨valueFull, valueOnly⟩ := valueIH trace.valueRun + obtain ⟨bodyFull, bodyOnly⟩ := bodyIH trace.bodyRun + obtain ⟨comparisonFull, comparisonOnly⟩ := hash_maps hashPath trace.compareRun + have opened := openLet_inference_state trace.openRun + rw [(trace.output_state run).2] + simp only [InferenceCacheEvent.applyFull_append, InferenceCacheEvent.applyOnly_append] + exact ⟨bodyFull.trans (congrArg _ (opened.1.trans + (comparisonFull.trans (valueFull.trans (congrArg _ domainFull))))), + bodyOnly.trans (congrArg _ (opened.2.1.trans + (comparisonOnly.trans (valueOnly.trans (congrArg _ domainOnly)))))⟩ end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/CacheHistory.lean b/Ix/Kernel/Verify/Consistency/CacheHistory.lean index 9926fb640..3c400c529 100644 --- a/Ix/Kernel/Verify/Consistency/CacheHistory.lean +++ b/Ix/Kernel/Verify/Consistency/CacheHistory.lean @@ -61,6 +61,12 @@ def openBinder {before after : TcState .anon} {name : Mode.anon.F Name} {bi : Mo · cases accepted; exact history.only · contradiction⟩ +def openLet {before after : TcState .anon} {name : Mode.anon.F Name} + {domain value body opened : KExpr .anon} {fresh : FVarId} (history : InferenceCacheHistory before) + (accepted : TcM.openLet name domain value body before = .ok (opened, fresh) after) : + InferenceCacheHistory after := + history.ofMaps (openLet_inference_state accepted).1 (openLet_inference_state accepted).2.1 + /-- Standalone or block loading retains the history on both outcomes. -/ def getConst {before : TcState .anon} {id : KId .anon} (history : InferenceCacheHistory before) (loader : VerifiedLazySupport before id.addr) : diff --git a/Ix/Kernel/Verify/Consistency/CheapBetaReading.lean b/Ix/Kernel/Verify/Consistency/CheapBetaReading.lean index 240878a80..028479da5 100644 --- a/Ix/Kernel/Verify/Consistency/CheapBetaReading.lean +++ b/Ix/Kernel/Verify/Consistency/CheapBetaReading.lean @@ -39,7 +39,8 @@ theorem CheapBetaSupport.reading {β : Type u} readScopedExpr? resolve locals (cheapBetaReduce source table).1 = some (AExpr.betaPrefix (cheapBetaCount source) (.lam condition domain inner) arguments).erase ∧ (cheapBetaReduce source table).2.WF := by - obtain ⟨headReads, argumentReads⟩ := readScopedExpr?_lambda_spine reading + obtain ⟨headReads, argumentReads⟩ := + readScopedExpr?_lambda_spine (cheapBetaPlan?_head_lambda support.selected) reading obtain ⟨head, rawBody, args, count, spine, peeling, countBound, base, trailing⟩ := cheapBetaPlan?_simul support.selected support.bounds have counted : cheapBetaCount source = count := by diff --git a/Ix/Kernel/Verify/Consistency/Dependencies.lean b/Ix/Kernel/Verify/Consistency/Dependencies.lean index 00127187e..24041d693 100644 --- a/Ix/Kernel/Verify/Consistency/Dependencies.lean +++ b/Ix/Kernel/Verify/Consistency/Dependencies.lean @@ -27,6 +27,25 @@ private theorem allReferences_erase (available : ConstRef β → Prop) (source : AllReferences available source.erase ↔ ∀ ref ∈ source.references, available ref := by induction source <;> simp_all [AllReferences, AExpr.erase, AExpr.references, or_imp, forall_and] +private theorem allReferences_liftN {available : ConstRef β → Prop} + {source : VExpr β} (references : AllReferences available source) (count cutoff : Nat) : + AllReferences available (source.liftN count cutoff) := by + induction source generalizing cutoff <;> simp_all [VExpr.liftN, AllReferences] + +private theorem allReferences_inst {available : ConstRef β → Prop} + {body value : VExpr β} (bodyReferences : AllReferences available body) + (valueReferences : AllReferences available value) (cutoff : Nat) : + AllReferences available (body.inst value cutoff) := by + induction body generalizing cutoff with + | bvar index => + simp only [VExpr.inst, VExpr.instVar] + split + · trivial + · split + · exact allReferences_liftN valueReferences cutoff 0 + · trivial + | _ => simp_all [VExpr.inst, AllReferences] + private theorem option_bind_success {α γ : Type _} {action : Option α} {next : α → Option γ} {result : γ} (run : action.bind next = some result) : ∃ intermediate, action = some intermediate ∧ next intermediate = some result := by @@ -51,7 +70,14 @@ private theorem readScopedExpr?_allReferences {available : ConstRef β → Prop} cases equality trivial | sort _ _ | nat _ _ _ => cases reading; trivial - | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | str _ _ _ => contradiction + | letE name domain value body nonDep info _ ihValue ihBody => + obtain ⟨A, v, b, _, valueReads, bodyReads, rfl⟩ := readScopedExpr?_let_parts reading + exact allReferences_inst + (ihBody bodyReads (fun id reference => references id + (.child (by simp [DefinitionReferences.Children]) reference))) + (ihValue valueReads (fun id reference => references id + (.child (by simp [DefinitionReferences.Children]) reference))) 0 | const id levels info => rw [readScopedExpr?] at reading obtain ⟨ref, resolved, reading⟩ := option_bind_success reading diff --git a/Ix/Kernel/Verify/Consistency/LetCache.lean b/Ix/Kernel/Verify/Consistency/LetCache.lean new file mode 100644 index 000000000..88b25b2fc --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/LetCache.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.LetSynthesis +import Ix.Kernel.Verify.Consistency.SynthesisCacheExecution + +/-! Derive a let's whole-cache execution from the same three child checks +that establish its typing. Wrapper annotations remain the existing child +resources; no additional operational tree is assumed for the let branch. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +namespace LetInferenceCheck + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} + {fuel : Nat} {before : TcState .anon} {name : Mode.anon.F Name} {domain value body : KExpr .anon} + {nonDep : Bool} {info : ExprInfo .anon} {A val b B resultType : AExpr β} {level : VLevel} + +structure CacheData + (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level) (anchor : Model.Environment β) where + domain : check.domainTree.CacheData anchor + value : check.valueTree.CacheData anchor + body : check.bodyTree.CacheData anchor + +/-- Original synthesis trees supply all three operational children. The +root miss and comparison are taken directly from the checked let execution. -/ +def cacheTrace + (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level) + (data : check.CacheData anchor) + (contextOrigin : SynthesisContext resolve anchor [] [] entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) : + InferenceCacheTrace.{u} (fuel + 1) before (.letE name domain value body nonDep info) := by + have keyedAgreement := check.miss.localContext.symm ▸ agreement + let domainRun := check.domainTree.cacheExecution data.domain contextOrigin keyedAgreement + check.domainReading check.execution.domainRun + let valueRun := check.valueTree.cacheExecution data.value contextOrigin + (check.execution.domainContext.symm ▸ keyedAgreement) check.valueReading check.execution.valueRun + have opened := openLet_sound check.opening (check.execution.openingContext.symm ▸ keyedAgreement) + check.absent check.domainReading check.bodyReading check.execution.openRun + let bodyRun := check.bodyTree.cacheExecution data.body + (.push contextOrigin check.domainTree keyedAgreement check.domainReading check.execution.domainRun) + opened.2.2.1 opened.2.1 check.execution.bodyRun + exact .letE check.full check.miss check.execution check.hashPath domainRun.trace valueRun.trace bodyRun.trace + +/-- The complete maps include every child write followed by the let's own +publication. Scope exit does not discard the recorded local entries. -/ +theorem cache_maps {result : KExpr .anon} {after : TcState .anon} + (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level) + (data : check.CacheData anchor) + (contextOrigin : SynthesisContext resolve anchor [] [] entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (accepted : RecM.infer (.letE name domain value body nonDep info) (methodsN (fuel + 1)) before = + .ok result after) : + after.env.inferCache = InferenceCacheEvent.applyFull + ((check.cacheTrace data contextOrigin agreement).events accepted) before.env.inferCache ∧ + after.env.inferOnlyCache = InferenceCacheEvent.applyOnly + ((check.cacheTrace data contextOrigin agreement).events accepted) before.env.inferOnlyCache := + (check.cacheTrace data contextOrigin agreement).cache_maps accepted + +def cacheHistory {result : KExpr .anon} {after : TcState .anon} + (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level) + (data : check.CacheData anchor) + (contextOrigin : SynthesisContext resolve anchor [] [] entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (history : InferenceCacheHistory before) + (accepted : RecM.infer (.letE name domain value body nonDep info) (methodsN (fuel + 1)) before = + .ok result after) : InferenceCacheHistory after := + history.afterInference (check.cacheTrace data contextOrigin agreement) accepted + +end LetInferenceCheck + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/LetInference.lean b/Ix/Kernel/Verify/Consistency/LetInference.lean new file mode 100644 index 000000000..c309bda12 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/LetInference.lean @@ -0,0 +1,139 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.LetOpening +import Ix.Kernel.Verify.Consistency.InferenceCache +import Ix.Kernel.Verify.Consistency.CheapBetaReading + +/-! The full production let branch checks the declared type and value, +opens a let local, and infers the body. Its result is obtained by abstraction, +value substitution, and the selected cheap-beta operation, in that order. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +/-- Actual recursive calls and their states. Context equalities concern +representation only; no child typing or returned-type reading is assumed. -/ +structure LetInferenceTrace (fuel : Nat) (before : TcState .anon) + (name : Mode.anon.F Name) (domain value body : KExpr .anon) where + domainLevel : KUniv .anon + domainInfo : ExprInfo .anon + domainState : TcState .anon + valueType : KExpr .anon + valueState : TcState .anon + comparedState : TcState .anon + opened : KExpr .anon + fresh : FVarId + openedState : TcState .anon + bodyType : KExpr .anon + bodyState : TcState .anon + domainRun : RecM.infer domain (methodsN fuel) before = + .ok (.sort domainLevel domainInfo) domainState + valueRun : RecM.infer value (methodsN fuel) domainState = .ok valueType valueState + compareRun : RecM.isDefEq valueType domain (methodsN fuel) valueState = .ok true comparedState + openRun : TcM.openLet name domain value body comparedState = .ok (opened, fresh) openedState + bodyRun : RecM.infer opened (methodsN fuel) openedState = .ok bodyType bodyState + domainContext : domainState.lctx = before.lctx + openingContext : comparedState.lctx = before.lctx + +namespace LetInferenceTrace + +variable {fuel : Nat} {before : TcState .anon} {name : Mode.anon.F Name} + {domain value body : KExpr .anon} + +def abstracted (trace : LetInferenceTrace fuel before name domain value body) := + abstractFVars trace.bodyType #[trace.fresh] trace.bodyState.env.intern + +def substituted (trace : LetInferenceTrace fuel before name domain value body) := + subst trace.abstracted.1 value 0 trace.abstracted.2 + +def reduced (trace : LetInferenceTrace fuel before name domain value body) := + cheapBetaReduce trace.substituted.1 trace.substituted.2 + +def after (trace : LetInferenceTrace fuel before name domain value body) : TcState .anon := + {trace.bodyState with + env := {trace.bodyState.env with intern := trace.reduced.2} + lctx := trace.bodyState.lctx.truncate trace.comparedState.lctx.size} + +private theorem withLctxScope_success {action : RecM .anon α} + {methods : Methods .anon} {before finished : TcState .anon} {result : α} + (accepted : action.withLctxScope.run methods before = .ok result finished) : + ∃ middle, action.run methods before = .ok result middle ∧ + finished = {middle with lctx := middle.lctx.truncate before.lctx.size} := by + rw [withLctxScope_eq] at accepted + cases step : action.run methods before with + | error error failed => rw [step] at accepted; contradiction + | ok value middle => rw [step] at accepted; cases accepted; exact ⟨middle, rfl, rfl⟩ + +/-- Invert the production branch through all three calls and both walkers. +Scope cleanup retains the final intern table and all child cache writes. -/ +theorem output_state {result : KExpr .anon} {finished : TcState .anon} + {nonDep : Bool} {info : ExprInfo .anon} + (trace : LetInferenceTrace fuel before name domain value body) + (accepted : RecM.inferUncached RecM.inferCall false (.letE name domain value body nonDep info) + (methodsN (fuel + 1)) before = .ok result finished) : + result = trace.reduced.1 ∧ finished = trace.after := by + change (RecM.inferUncached RecM.inferCall false (.letE name domain value body nonDep info)).run + (methodsN (fuel + 1)) before = _ at accepted + unfold RecM.inferUncached at accepted + simp only [Bool.not_false, if_true, ReaderT.run_bind] at accepted + change EStateM.bind (RecM.infer domain (methodsN fuel)) _ before = _ at accepted + rw [EStateM.bind, trace.domainRun] at accepted + change EStateM.bind (RecM.infer value (methodsN fuel)) _ trace.domainState = _ at accepted + rw [EStateM.bind, trace.valueRun] at accepted + change EStateM.bind (RecM.isDefEq trace.valueType domain (methodsN fuel)) _ trace.valueState = _ at accepted + rw [EStateM.bind, trace.compareRun] at accepted + change (RecM.withLctxScope _).run (methodsN (fuel + 1)) trace.comparedState = _ at accepted + obtain ⟨middle, accepted, cleanup⟩ := withLctxScope_success accepted + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted + change EStateM.bind (TcM.openLet name domain value body) _ trace.comparedState = + .ok result middle at accepted + rw [EStateM.bind, trace.openRun] at accepted + have bodyRun : (RecM.inferCall trace.opened).run (methodsN (fuel + 1)) trace.openedState = + .ok trace.bodyType trace.bodyState := trace.bodyRun + change EStateM.bind ((RecM.inferCall trace.opened).run (methodsN (fuel + 1))) + _ trace.openedState = .ok result middle at accepted + rw [EStateM.bind, bodyRun] at accepted + cases accepted + exact ⟨rfl, cleanup⟩ + +/-- Finite resources for the two production binder walkers. The second +walker uses the table produced by the first one, not the entry table. -/ +structure SubstitutionSupport (trace : LetInferenceTrace fuel before name domain value body) : Prop where + bodyConstructed : trace.bodyType.Constructed + bodyBound : trace.bodyType.size + 1 < UInt64.size + coherent : trace.bodyState.env.intern.WF + closingFaithful : KExpr.CollisionFree fun term => trace.bodyState.env.intern.ExprSupport term ∨ + KExpr.AbstractReach ((∅ : Std.HashMap FVarId UInt64).insert trace.fresh 0) 1 trace.bodyType 0 term + abstractedConstructed : trace.abstracted.1.Constructed + abstractedBound : trace.abstracted.1.size + 1 < UInt64.size + valueConstructed : value.Constructed + valueBound : value.size < UInt64.size + substitutionFaithful : KExpr.CollisionFree fun term => trace.abstracted.2.ExprSupport term ∨ + KExpr.SubstReach value trace.abstracted.1 0 term + +/-- Abstraction and substitution remove the fresh let local from the +inferred body type, using the exact value supplied to the production branch. -/ +theorem substituted_reading {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {B v : AExpr β} + (trace : LetInferenceTrace fuel before name domain value body) + (support : trace.SubstitutionSupport) + (bodyReading : readScopedExpr? resolve (trace.fresh :: locals) trace.bodyType = some B.erase) + (valueReading : readScopedExpr? resolve locals value = some v.erase) : + readScopedExpr? resolve locals trace.substituted.1 = some (B.inst v).erase ∧ + trace.substituted.2.WF := by + obtain ⟨closedReading, closedCoherent⟩ := abstractFVars_readScopedExpr? + support.bodyConstructed support.bodyBound support.coherent support.closingFaithful bodyReading + obtain ⟨resultReading, resultCoherent⟩ := subst_readScopedExpr? + support.abstractedConstructed support.valueConstructed support.abstractedBound support.valueBound + closedCoherent support.substitutionFaithful closedReading valueReading + exact ⟨by simpa only [substituted, abstracted, AExpr.erase_inst] using resultReading, resultCoherent⟩ + +end LetInferenceTrace + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/LetOpening.lean b/Ix/Kernel/Verify/Consistency/LetOpening.lean new file mode 100644 index 000000000..db54f7baa --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/LetOpening.lean @@ -0,0 +1,96 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BinderOpening + +/-! Let opening allocates the same fresh representation as binder opening, +while retaining the value in the actual local declaration. The reader uses +the declaration's type; typing the value is a separate inference obligation. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +theorem openLet_eq (name : Mode.anon.F Name) (type value body : KExpr .anon) + (before : TcState .anon) : + TcM.openLet name type value body before = + if before.env.nextFVarId.toNat + 1 < UInt64.size then + let fresh : FVarId := ⟨before.env.nextFVarId⟩ + let internedLocal := before.env.intern.internExpr (KExpr.mkFVar fresh name) + let opened := instantiateRev body #[internedLocal.1] internedLocal.2 + .ok (opened.1, fresh) {before with + env := {before.env with + nextFVarId := before.env.nextFVarId + 1 + intern := opened.2} + lctx := before.lctx.push fresh (.ldecl name type value)} + else .error (.other "free-variable id space exhausted") before := by + unfold TcM.openLet + change EStateM.bind TcM.freshFVarId _ before = _ + rw [EStateM.bind, TcM.freshFVarId] + by_cases room : before.env.nextFVarId.toNat + 1 < UInt64.size + · simp only [room, if_true] + rfl + · simp only [room, if_false] + +/-- Let opening changes the local context and intern table while preserving +both complete inference maps, loaded declarations, and checking policy. -/ +theorem openLet_inference_state {name : Mode.anon.F Name} {type value body opened : KExpr .anon} + {fresh : FVarId} {before after : TcState .anon} + (accepted : TcM.openLet name type value body before = .ok (opened, fresh) after) : + after.env.inferCache = before.env.inferCache ∧ + after.env.inferOnlyCache = before.env.inferOnlyCache ∧ + after.env.consts = before.env.consts ∧ after.inferOnly = before.inferOnly := by + rw [openLet_eq] at accepted + split at accepted + · cases accepted + exact ⟨rfl, rfl, rfl, rfl⟩ + · contradiction + +/-- The production let local and its opened body implement dependent +context extension, including bodies containing nested lets. -/ +theorem openLet_sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {context : Model.Context β} {type value body opened : KExpr .anon} + {A : AExpr β} {b : VExpr β} {fresh : FVarId} {before after : TcState .anon} + {name : Mode.anon.F Name} + (support : BinderOpeningSupport before body) + (agreement : LocalContextReading resolve locals before.lctx context) + (absent : (⟨before.env.nextFVarId⟩ : FVarId) ∉ locals) + (typeReads : readScopedExpr? resolve locals type = some A.erase) + (bodyReads : readScopedExpr? resolve locals body 1 = some b) + (accepted : TcM.openLet name type value body before = .ok (opened, fresh) after) : + fresh = ⟨before.env.nextFVarId⟩ ∧ + readScopedExpr? resolve (fresh :: locals) opened = some b ∧ + LocalContextReading resolve (fresh :: locals) after.lctx (context.push A) ∧ + after.env.intern.WF := by + have nameUnit : name = () := Subsingleton.elim _ _ + subst name + have interned : (before.env.intern.internExpr + (KExpr.mkFVar ⟨before.env.nextFVarId⟩ ())).1 = + KExpr.mkFVar ⟨before.env.nextFVarId⟩ () := by + have keyFaithful : KExpr.KeyCollisionFree (fun term => + before.env.intern.ExprSupport term ∨ term = KExpr.mkFVar ⟨before.env.nextFVarId⟩ ()) := + KExpr.keyCollisionFree_anon.mpr + (support.faithful.mono fun term h => h.elim Or.inl (fun equal => .inr (.inl equal))) + simpa only [KExpr.eraseMeta_anon] using + before.env.intern.internExpr_eraseMeta support.coherent keyFaithful + have walk := instantiateRev_spec (fvars := #[KExpr.mkFVar ⟨before.env.nextFVarId⟩ ()]) + support.faithful support.constructed (by simpa using support.bound) + (fun _ reached => .inr (.inr reached)) + (support.coherent.internExpr (KExpr.mkFVar ⟨before.env.nextFVarId⟩ ())) + (fun _ member => (InternTable.ExprSupport.of_internExpr member).elim + Or.inl (fun equal => .inr (.inl equal))) + rw [openLet_eq] at accepted + split at accepted + · simp only [interned] at accepted + cases accepted + refine ⟨rfl, ?_, agreement.push (decl := .ldecl () type value) absent typeReads, walk.2.1⟩ + rw [walk.1] + exact readScopedExpr?_instantiateRevSpec absent (by simpa using support.bound) bodyReads + · contradiction + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/LetSynthesis.lean b/Ix/Kernel/Verify/Consistency/LetSynthesis.lean new file mode 100644 index 000000000..1c46ffb7a --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/LetSynthesis.lean @@ -0,0 +1,213 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.LetInference +import Ix.Kernel.Verify.Consistency.BetaInference + +/-! Connect the full let branch to its original domain, value, and body +checks. The generated term retains their complete beta derivations after +substitution. No semantic typing or conversion is supplied as a resource. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- The actual final cheap-beta choice, with an original checking origin +for a selected reduction. The unchanged case needs only the planner result. -/ +inductive LetTypeReduction {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (context : Model.Context β) (bounds : List VLevel) + (source : KExpr .anon) (table : InternTable .anon) (level : VLevel) : + AExpr β → AExpr β → Type u + | unchanged {term : AExpr β} (plan : cheapBetaPlan? source = none) : + LetTypeReduction resolve entries context bounds source table level term term + | beta {condition : Certified.PropWhen} {domain body : AExpr β} {arguments : List (AExpr β)} + (support : CheapBetaSupport source table) + (origin : SynthesisBetaTrace resolve entries context bounds entries context + ((AExpr.lam condition domain body).appN arguments) + (AExpr.betaPrefix (cheapBetaCount source) (.lam condition domain body) arguments) (.sort level)) : + LetTypeReduction resolve entries context bounds source table level + ((AExpr.lam condition domain body).appN arguments) + (AExpr.betaPrefix (cheapBetaCount source) (.lam condition domain body) arguments) + +theorem LetTypeReduction.reading {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} + {locals : List FVarId} {source : KExpr .anon} {table : InternTable .anon} + {level : VLevel} {term result : AExpr β} + (reduction : LetTypeReduction resolve entries context bounds source table level term result) + (coherent : table.WF) + (reading : readScopedExpr? resolve locals source = some term.erase) : + readScopedExpr? resolve locals (cheapBetaReduce source table).1 = some result.erase ∧ + (cheapBetaReduce source table).2.WF := by + cases reduction with + | unchanged plan => + rw [cheapBetaReduce, plan] + exact ⟨reading, coherent⟩ + | beta support _ => exact (support.reading reading).2 + +def LetTypeReduction.trace {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} + {source : KExpr .anon} {table : InternTable .anon} {level : VLevel} {term result : AExpr β} + (reduction : LetTypeReduction resolve entries context bounds source table level term result) + (original : SynthesisTypingOrigin resolve entries context bounds entries context term (.sort level)) : + SynthesisBetaTrace resolve entries context bounds entries context term result (.sort level) := + match reduction with + | .unchanged _ => .refl original + | .beta _ origin => origin + +/-- All three children are the original executed synthesis checks. Because +let erasure loses its constructor, their separate source readings are retained +explicitly. The comparison relates the inferred value type to the declaration. -/ +structure LetInferenceCheck {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (locals : List FVarId) (context : Model.Context β) + (bounds : List VLevel) (fuel : Nat) (before : TcState .anon) + (name : Mode.anon.F Name) (domain value body : KExpr .anon) (nonDep : Bool) (info : ExprInfo .anon) + (A v b B resultType : AExpr β) (level : VLevel) where + full : before.inferOnly = false + miss : UncachedInference before (.letE name domain value body nonDep info) + execution : LetInferenceTrace fuel miss.keyed name domain value body + opening : BinderOpeningSupport execution.comparedState body + absent : (⟨execution.comparedState.env.nextFVarId⟩ : FVarId) ∉ locals + domainBound : VLevel + valueLevel : VLevel + valueType : AExpr β + domainTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed domain + A (.sort (readLevel execution.domainLevel)) domainBound + valueTree : SynthesisInference resolve entries locals context bounds fuel execution.domainState value + v valueType valueLevel + bodyTree : SynthesisInference resolve entries (execution.fresh :: locals) (context.push A) + (readLevel execution.domainLevel :: bounds) fuel execution.openedState execution.opened b B level + domainReading : readScopedExpr? resolve locals domain = some A.erase + valueReading : readScopedExpr? resolve locals value = some v.erase + bodyReading : readScopedExpr? resolve locals body 1 = some b.erase + conditions : valueType.annotations = A.annotations + hashPath : (execution.valueType == domain) = true + comparisonFaithful : execution.valueType.AddrFaithful domain + substitution : execution.SubstitutionSupport + reduction : LetTypeReduction resolve entries context bounds execution.substituted.1 execution.substituted.2 + level (B.inst v) resultType + +namespace LetInferenceCheck + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} + {fuel : Nat} {before : TcState .anon} {name : Mode.anon.F Name} {domain value body : KExpr .anon} + {nonDep : Bool} {info : ExprInfo .anon} {A val b B resultType : AExpr β} {level : VLevel} + +theorem source_reading + (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level) : + readScopedExpr? resolve locals (.letE name domain value body nonDep info) = some (b.inst val).erase := by + simp [readScopedExpr?, check.domainReading, check.valueReading, check.bodyReading, AExpr.erase_inst] + +private theorem value_type + (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) : check.valueType = A := by + have valueAgreement := check.execution.domainContext.symm ▸ (check.miss.localContext.symm ▸ agreement) + have returned := (check.valueTree.soundWithSpine formed valueAgreement check.valueReading + check.execution.valueRun).1 + exact AExpr.eq_of_erase_annotations + (Option.some.inj (returned.symm.trans + ((beq_readScopedExpr? check.comparisonFaithful check.hashPath).trans check.domainReading))) check.conditions + +/-- Typing origins for the substituted inferred type come from the original +body inference and the value comparison, with the actual domain check forming +the body's context. -/ +def substituted_type_origin + (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) : + SynthesisTypingOrigin resolve entries context bounds entries context (B.inst val) (.sort level) := by + have keyedAgreement := check.miss.localContext.symm ▸ agreement + have valueAgreement := check.execution.domainContext.symm ▸ keyedAgreement + have opened := openLet_sound check.opening (check.execution.openingContext.symm ▸ keyedAgreement) + check.absent check.domainReading check.bodyReading check.execution.openRun + have bodyOrigin := SynthesisTypingOrigin.inferredType + (SynthesisContext.push .current check.domainTree keyedAgreement check.domainReading check.execution.domainRun) + check.bodyTree opened.2.2.1 opened.2.1 check.execution.bodyRun + have valueOrigin := SynthesisTypingOrigin.source + (SynthesisCheckedOrigin.checked .current check.valueTree valueAgreement check.valueReading check.execution.valueRun) + have same := check.value_type formed agreement + exact .substituteAt bodyOrigin (same ▸ valueOrigin) .root + +/-- Substitution retains the body's entire beta derivation, including a +lambda introduced by substituting the value for the let-bound variable. -/ +def betaTyping + (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) : + SynthesisBetaTyping resolve entries context bounds entries context (b.inst val) resultType := by + have keyedAgreement := check.miss.localContext.symm ▸ agreement + have valueAgreement := check.execution.domainContext.symm ▸ keyedAgreement + have opened := openLet_sound check.opening (check.execution.openingContext.symm ▸ keyedAgreement) + check.absent check.domainReading check.bodyReading check.execution.openRun + have inner := check.bodyTree.betaTyping + (SynthesisContext.push .current check.domainTree keyedAgreement check.domainReading check.execution.domainRun) + opened.2.2.1 opened.2.1 check.execution.bodyRun formed + have argument := check.valueTree.betaTyping .current valueAgreement check.valueReading check.execution.valueRun formed + have same := check.value_type formed agreement + exact .convert (inner.substituteAt (same ▸ argument) .root) + (check.reduction.trace (check.substituted_type_origin formed agreement)) + +/-- Full-mode let success returns the checked type after the actual closing, +substitution, cheap reduction, scope cleanup, and outer cache publication. -/ +theorem sound {result : KExpr .anon} {after : TcState .anon} + (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (accepted : RecM.infer (.letE name domain value body nonDep info) (methodsN (fuel + 1)) before = + .ok result after) : + readScopedExpr? resolve locals result = some resultType.erase ∧ + TypingClaim.{u,v} entries context (b.inst val) resultType ∧ + TypingClaim.{u,v} entries context resultType (.sort level) := by + have keyedAgreement := check.miss.localContext.symm ▸ agreement + obtain ⟨_, domainTyped, _, _⟩ := check.domainTree.soundWithSpine formed keyedAgreement + check.domainReading check.execution.domainRun + have opened := openLet_sound check.opening (check.execution.openingContext.symm ▸ keyedAgreement) + check.absent check.domainReading check.bodyReading check.execution.openRun + obtain ⟨bodyTypeReading, _, _, _⟩ := check.bodyTree.soundWithSpine (formed.push domainTyped) + opened.2.2.1 opened.2.1 check.execution.bodyRun + obtain ⟨substitutedReading, substitutedCoherent⟩ := + check.execution.substituted_reading check.substitution bodyTypeReading check.valueReading + have reducedReading := check.reduction.reading substitutedCoherent substitutedReading + obtain ⟨middle, uncached⟩ := infer_uncached_success check.miss accepted + rw [check.full] at uncached + have output := (check.execution.output_state uncached).1 + refine ⟨?_, (check.betaTyping formed agreement).origin.sound formed, ?_⟩ + · rw [output] + exact reducedReading.1 + · exact ((check.reduction.trace (check.substituted_type_origin formed agreement)).sound formed).2 + +theorem closed_sound {result : KExpr .anon} {after : TcState .anon} + (check : LetInferenceCheck resolve entries [] [] [] fuel before name domain value body nonDep info + A val b B resultType level) + (accepted : RecM.infer (.letE name domain value body nonDep info) (methodsN (fuel + 1)) before = + .ok result after) : + readExpr? resolve result = some resultType.erase ∧ + TypingClaim.{u,v} entries [] (b.inst val) resultType ∧ + TypingClaim.{u,v} entries [] resultType (.sort level) := by + obtain ⟨reading, typed, formed⟩ := check.sound (.empty entries) (LocalContextReading.empty _ _) accepted + exact ⟨readScopedExpr?_closed reading, typed, formed⟩ + +/-- Every further head-beta step uses the substituted original checks, +without asking inference to check any generated intermediate expression. -/ +theorem beta_steps_sound + (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) (count : Nat) : + ConversionClaim.{u,v} entries context (b.inst val) (BetaSyntax.steps count (b.inst val)) ∧ + TypingClaim.{u,v} entries context (BetaSyntax.steps count (b.inst val)) resultType := + ((check.betaTyping formed agreement).betaSteps count).2.sound formed + +end LetInferenceCheck + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Production.lean b/Ix/Kernel/Verify/Consistency/Production.lean index ea152420b..180bb24b3 100644 --- a/Ix/Kernel/Verify/Consistency/Production.lean +++ b/Ix/Kernel/Verify/Consistency/Production.lean @@ -7,6 +7,7 @@ import Ix.Kernel.Driver import Ix.Kernel.Verify.Consistency.Constant import Ix.Kernel.Verify.Consistency.SynthesisInference import Ix.Kernel.Verify.Consistency.BetaWhnfInference +import Ix.Kernel.Verify.Consistency.LetSynthesis import Ix.Kernel.Verify.Consistency.Validation /-! @@ -175,6 +176,16 @@ inductive DefinitionBodySupport {β : Type u} (scope : body.Scope universes 0 ∧ type.Scope universes 0) (references : body.ReferencesIn entries ∧ type.ReferencesIn entries) : DefinitionBodySupport resolve entries methods before declared universes term body type + | letE {fuel : Nat} {name : Mode.anon.F Name} {domain value body : KExpr .anon} + {nonDep : Bool} {info : ExprInfo .anon} {A val b B type : AExpr β} {level : VLevel} + (tied : methods = methodsN (fuel + 1)) + (check : LetInferenceCheck resolve entries [] [] [] fuel before name domain value body nonDep info + A val b B type level) + (typeReading : readScopedExpr? resolve [] declared = some type.erase) + (scope : (b.inst val).Scope universes 0 ∧ type.Scope universes 0) + (references : (b.inst val).ReferencesIn entries ∧ type.ReferencesIn entries) : + DefinitionBodySupport resolve entries methods before declared universes + (.letE name domain value body nonDep info) (b.inst val) type theorem DefinitionBodySupport.sound {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} @@ -230,6 +241,11 @@ theorem DefinitionBodySupport.sound {β : Type u} obtain ⟨_, valueTyped, _⟩ := valueInference.closed_sound valueReading accepted exact ⟨readScopedExpr?_closed valueReading, readScopedExpr?_closed typeReading, scope.1, scope.2, references.1, references.2, valueTyped⟩ + | letE tied check typeReading scope references => + subst methods + obtain ⟨_, valueTyped, _⟩ := check.closed_sound accepted + exact ⟨readScopedExpr?_closed check.source_reading, readScopedExpr?_closed typeReading, + scope.1, scope.2, references.1, references.2, valueTyped⟩ /-- The execution prefix through value conversion. A successful member check also passes the subsequent safety checks. -/ @@ -410,6 +426,33 @@ def DefinitionBodyTrace.synthesisSupport {β : Type u} {input : DefinitionInput} valueConditions typeConditions) references +/-- Let admission uses its three executed child checks. Production +validation establishes the scope of the substituted reading of the value. -/ +def DefinitionBodyTrace.letSupport {β : Type u} {input : DefinitionInput} + {fuel : Nat} {before : TcState .anon} + (trace : DefinitionBodyTrace input (methodsN (fuel + 1)) before) + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {name : Mode.anon.F Name} {domain value body : KExpr .anon} {nonDep : Bool} {info : ExprInfo .anon} + {A val b B type : AExpr β} {level : VLevel} + {support : RunSupport} (typeCoverage : input.type.ValidationCoverage support) + (valueCoverage : input.value.ValidationCoverage support) (collision : support.CollisionFree) + (source : input.value = .letE name domain value body nonDep info) + (check : LetInferenceCheck resolve entries [] [] [] fuel trace.valueStart name domain value body nonDep info + A val b B type level) + (typeReading : readScopedExpr? resolve [] input.type = some type.erase) + (valueConditions : ConditionsScoped input.universes.toNat (b.inst val)) + (typeConditions : ConditionsScoped input.universes.toNat type) + (references : (b.inst val).ReferencesIn entries ∧ type.ReferencesIn entries) : + DefinitionBodySupport resolve entries (methodsN (fuel + 1)) trace.valueStart input.type + input.universes.toNat input.value (b.inst val) type := by + have valueReading : readScopedExpr? resolve [] input.value = some (b.inst val).erase := by + rw [source] + exact check.source_reading + have sourceScope := trace.scopes typeCoverage valueCoverage collision valueReading typeReading + valueConditions typeConditions + rw [source] + exact .letE rfl check typeReading sourceScope references + /-- The actual declaration trace determines the type and value checks used to justify conversion. A beta case reuses the declared type's executed inference; it does not request another check of a generated type. -/ diff --git a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean index 24fccc892..6b4c5aaf0 100644 --- a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean +++ b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean @@ -4,6 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.BinderInference +import Ix.Kernel.Verify.Consistency.LetInference import Ix.Kernel.Verify.Consistency.WhnfCacheFrame import Ix.Kernel.Verify.Consistency.SourceOwnershipCheck @@ -148,6 +149,13 @@ private theorem openBinder_frame {name : Mode.anon.F Name} rw [run] at frame exact frame +private theorem openLet_frame {name : Mode.anon.F Name} {domain value body opened : KExpr .anon} + {fresh : FVarId} {before after : TcState .anon} (key : Address × Address) + (run : TcM.openLet name domain value body before = .ok (opened, fresh) after) : + InferenceCacheFrame key before after := by + have state := openLet_inference_state run + exact .of_eq (congrArg (·[key]?) state.1) (congrArg (·[key]?) state.2.1) state.2.2.1 + private theorem infer_miss_frame {term result : KExpr .anon} {methods : Methods .anon} {before after : TcState .anon} {key : Address × Address} (miss : UncachedInference before term) (different : miss.key ≠ key) @@ -231,6 +239,14 @@ inductive InferenceCacheTrace : Nat → TcState .anon → KExpr .anon → Type ( (domainTree : InferenceCacheTrace fuel miss.keyed domain) (bodyTree : InferenceCacheTrace fuel trace.openedState trace.opened) : InferenceCacheTrace (fuel + 1) before (.lam name bi domain body info) + | letE {fuel before name domain value body nonDep info} (full : before.inferOnly = false) + (miss : UncachedInference before (.letE name domain value body nonDep info)) + (trace : LetInferenceTrace fuel miss.keyed name domain value body) + (hashPath : (trace.valueType.addr == domain.addr) = true) + (domainTree : InferenceCacheTrace fuel miss.keyed domain) + (valueTree : InferenceCacheTrace fuel trace.domainState value) + (bodyTree : InferenceCacheTrace fuel trace.openedState trace.opened) : + InferenceCacheTrace (fuel + 1) before (.letE name domain value body nonDep info) /-- The finite write footprint is computed from the operational tree. Cache hits contribute no key; recursive calls and each outer insertion are included. -/ @@ -241,6 +257,8 @@ def InferenceCacheTrace.writes {fuel : Nat} {before : TcState .anon} {term : KEx | .app _ miss _ _ first second | .appBeta _ miss _ _ _ first second | .forallE miss _ first second | .lam _ miss _ first second | .lamBody _ miss _ first second => miss.key :: (first.writes ++ second.writes) + | .letE _ miss _ _ first second third => + miss.key :: (first.writes ++ (second.writes ++ third.writes)) /-- Leaf construction inspects the real cache policy; callers need not provide a separate hit/miss observation for a sort. -/ @@ -459,6 +477,21 @@ theorem InferenceCacheTrace.frame {fuel : Nat} {before after : TcState .anon} rw [state] exact ⟨(domainFrame.trans (opening.trans bodyFrame)).trans (.of_eq rfl rfl rfl), bodyPolicy.trans ((openBinder_policy trace.openRun).trans domainPolicy)⟩ + | letE full miss trace hashPath domainTree valueTree bodyTree domainIH valueIH bodyIH => + simp only [writes, List.mem_cons, List.mem_append, not_or] at outside + apply infer_miss_frame miss (Ne.symm outside.1) accepted + intro middle run + rw [full] at run + obtain ⟨domainFrame, domainPolicy⟩ := domainIH outside.2.1 trace.domainRun + obtain ⟨valueFrame, valuePolicy⟩ := valueIH outside.2.2.1 trace.valueRun + obtain ⟨bodyFrame, bodyPolicy⟩ := bodyIH outside.2.2.2 trace.bodyRun + obtain ⟨comparisonFrame, comparisonPolicy⟩ := isDefEq_hash_frame hashPath trace.compareRun key + have opening := openLet_frame key trace.openRun + rw [(trace.output_state run).2] + exact ⟨(domainFrame.trans (valueFrame.trans (comparisonFrame.trans + (opening.trans bodyFrame)))).trans (.of_eq rfl rfl rfl), + bodyPolicy.trans ((openLet_inference_state trace.openRun).2.2.2.trans + (comparisonPolicy.trans (valuePolicy.trans domainPolicy)))⟩ /-- A key already occupied in the full cache cannot be missed. Key memoization leaves the maps unchanged, and full entries precede both policies. -/ @@ -534,6 +567,19 @@ theorem InferenceCacheTrace.populated_outside {fuel : Nat} {before : TcState .an exact by simpa only [writes, List.mem_cons, List.mem_append, not_or] using ⟨miss.ne_populated stored, first, bodyIH next⟩ + | letE full miss trace hashPath domainTree valueTree bodyTree domainIH valueIH bodyIH => + have keyed : miss.keyed.env.inferCache[key]? = some cached := by + rw [inferKey_environment miss.keyRun] + exact stored + have first := domainIH keyed + have inferred := (domainTree.frame first trace.domainRun).1.full.trans keyed + have second := valueIH inferred + have checked := (valueTree.frame second trace.valueRun).1.full.trans inferred + have compared := (isDefEq_hash_frame hashPath trace.compareRun key).1.full.trans checked + have next := (openLet_frame key trace.openRun).full.trans compared + exact by + simpa only [writes, List.mem_cons, List.mem_append, not_or] using + ⟨miss.ne_populated stored, first, second, bodyIH next⟩ /-- A successful recursive call preserves both partitions at each initially populated full key, together with every previously loaded declaration. -/ diff --git a/Ix/Kernel/Verify/Consistency/ScopedExpr.lean b/Ix/Kernel/Verify/Consistency/ScopedExpr.lean index 82dd5ebff..2c1c71d2e 100644 --- a/Ix/Kernel/Verify/Consistency/ScopedExpr.lean +++ b/Ix/Kernel/Verify/Consistency/ScopedExpr.lean @@ -5,6 +5,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Verify.Consistency.Expr import Ix.Theory.Model.LevelCongruence +import Ix.Theory.Model.BetaSubstitution /-! # Reading opened binders @@ -12,8 +13,8 @@ import Ix.Theory.Model.LevelCongruence The production binder paths use free variables for their active locals. `readScopedExpr?` maps those identifiers to model de Bruijn indices, keeping syntactically bound variables distinct from active locals. Unknown free -variables and loose legacy variables fail. This binder fragment also excludes -lets and strings; the original closed reader remains available independently. +variables and loose legacy variables fail. Lets read by substituting their +value into their body, as in the closed reader. Strings remain unsupported. The local identifier list is newest first, matching `Model.Context.push`. No local declaration's type or semantic validity is assumed by this reader. @@ -81,11 +82,16 @@ def readScopedExpr? (resolve : Address → Option (ConstRef β)) (locals : List | .all _ _ domain body _, depth => do return .forallE (← readScopedExpr? resolve locals domain depth) (← readScopedExpr? resolve locals body (depth + 1)) + | .letE _ domain value body _ _, depth => do + let _ ← readScopedExpr? resolve locals domain depth + let value ← readScopedExpr? resolve locals value depth + let body ← readScopedExpr? resolve locals body (depth + 1) + return body.inst value | .prj id index value _, depth => do return .proj (← resolve id.addr) index.toNat (← readScopedExpr? resolve locals value depth) | .nat value _ _, _ => some (.natLit value) - | .letE .., _ | .str .., _ => none + | .str .., _ => none @[simp] theorem readScopedExpr?_mkVar (resolve : Address → Option (ConstRef β)) (locals : List FVarId) (index : UInt64) (name : m.F Name) (depth : Nat) : @@ -127,6 +133,15 @@ def readScopedExpr? (resolve : Address → Option (ConstRef β)) (locals : List return .proj (← resolve id.addr) index.toNat (← readScopedExpr? resolve locals value depth) := rfl +@[simp] theorem readScopedExpr?_mkLet (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (name : m.F Name) (domain value body : KExpr m) + (nonDep : Bool) (depth : Nat) : + readScopedExpr? resolve locals (KExpr.mkLet name domain value body nonDep) depth = do + let _ ← readScopedExpr? resolve locals domain depth + let value ← readScopedExpr? resolve locals value depth + let body ← readScopedExpr? resolve locals body (depth + 1) + return body.inst value := rfl + private theorem option_bind_success {α γ : Type _} {action : Option α} {next : α → Option γ} {result : γ} (run : action.bind next = some result) : ∃ intermediate, action = some intermediate ∧ next intermediate = some result := by @@ -134,6 +149,23 @@ private theorem option_bind_success {α γ : Type _} {action : Option α} | none => contradiction | some value => exact ⟨value, rfl, run⟩ +/-- Let erasure is substitution, so successful reading retains separate +readings of the declared type, value, and body rather than a constructor view. -/ +theorem readScopedExpr?_let_parts {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {name : m.F Name} {domain value body : KExpr m} + {nonDep : Bool} {info : ExprInfo m} {source : VExpr β} {depth : Nat} + (reading : readScopedExpr? resolve locals (.letE name domain value body nonDep info) + depth = some source) : + ∃ A v b, readScopedExpr? resolve locals domain depth = some A ∧ + readScopedExpr? resolve locals value depth = some v ∧ + readScopedExpr? resolve locals body (depth + 1) = some b ∧ source = b.inst v := by + rw [readScopedExpr?] at reading + obtain ⟨A, domainReads, reading⟩ := option_bind_success reading + obtain ⟨v, valueReads, reading⟩ := option_bind_success reading + obtain ⟨b, bodyReads, reading⟩ := option_bind_success reading + cases reading + exact ⟨A, v, b, domainReads, valueReads, bodyReads, rfl⟩ + theorem readScopedExpr?_lam_parts {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {name : m.F Name} {bi : m.F Lean.BinderInfo} {domain body : KExpr m} {info : ExprInfo m} {A b : VExpr β} {depth : Nat} @@ -180,7 +212,10 @@ theorem readScopedExpr?_weaken_closed {resolve : Address → Option (ConstRef β readScopedExpr? resolve locals term depth = some source := by induction term generalizing source depth with | var _ _ _ | sort _ _ | const _ _ _ | nat _ _ _ => exact reading - | fvar _ _ _ | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | fvar _ _ _ | str _ _ _ => contradiction + | letE name domain value body nonDep info ihDomain ihValue ihBody => + obtain ⟨A, v, b, domainReads, valueReads, bodyReads, rfl⟩ := readScopedExpr?_let_parts reading + simp [readScopedExpr?, ihDomain domainReads, ihValue valueReads, ihBody bodyReads] | app fn arg info hf ha | lam _ _ fn arg info hf ha | all _ _ fn arg info hf ha => rw [readScopedExpr?] at reading obtain ⟨f, fReads, reading⟩ := option_bind_success reading @@ -205,7 +240,10 @@ theorem readScopedExpr?_closed {resolve : Address → Option (ConstRef β)} split at reading · exact reading · contradiction - | fvar _ _ _ | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | fvar _ _ _ | str _ _ _ => contradiction + | letE name domain value body nonDep info ihDomain ihValue ihBody => + obtain ⟨A, v, b, domainReads, valueReads, bodyReads, rfl⟩ := readScopedExpr?_let_parts reading + simp [readExpr?, ihDomain domainReads, ihValue valueReads, ihBody bodyReads] | sort _ _ | const _ _ _ | nat _ _ _ => exact reading | app fn arg info hf ha | lam _ _ fn arg info hf ha | all _ _ fn arg info hf ha => rw [readScopedExpr?] at reading @@ -268,7 +306,11 @@ theorem readScopedExpr?_push {resolve : Address → Option (ConstRef β)} cases reading simp [readScopedExpr?, localIndex?_fresh absent found, VExpr.liftN, liftVar, Nat.not_lt.mpr (Nat.le_add_right depth index), Nat.add_assoc, Nat.add_comm 1] - | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | str _ _ _ => contradiction + | letE name domain value body nonDep info ihDomain ihValue ihBody => + obtain ⟨A, v, b, domainReads, valueReads, bodyReads, rfl⟩ := readScopedExpr?_let_parts reading + simp [readScopedExpr?, ihDomain domainReads, ihValue valueReads, ihBody bodyReads, + VExpr.liftN_inst_zero] | sort level info => cases reading; rfl | nat value name info => cases reading; rfl | const id levels info => diff --git a/Ix/Kernel/Verify/Consistency/ScopedInstUniv.lean b/Ix/Kernel/Verify/Consistency/ScopedInstUniv.lean index 1cb237e01..bc84f034b 100644 --- a/Ix/Kernel/Verify/Consistency/ScopedInstUniv.lean +++ b/Ix/Kernel/Verify/Consistency/ScopedInstUniv.lean @@ -51,7 +51,17 @@ theorem instUnivSpec_scoped_eq {resolve : Address → Option (ConstRef β)} split at reading next bound => simp [bound, readExpr?] · contradiction - | fvar _ _ _ | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | fvar _ _ _ | str _ _ _ => contradiction + | letE name domain value body nonDep info ihDomain ihValue ihBody => + obtain ⟨A, v, b, domainReads, valueReads, bodyReads, _⟩ := readScopedExpr?_let_parts reading + rw [KExpr.instUnivSpec] at run + obtain ⟨domain', domainRun, run⟩ := except_bind_success run + obtain ⟨value', valueRun, run⟩ := except_bind_success run + obtain ⟨body', bodyRun, run⟩ := except_bind_success run + cases run + simp only [readScopedExpr?_mkLet] + rw [ihDomain domainReads domainRun, ihValue valueReads valueRun, ihBody bodyReads bodyRun] + rfl | nat _ _ _ => cases run; rfl | sort _ _ | const _ _ _ => rw [KExpr.instUnivSpec] at run diff --git a/Ix/Kernel/Verify/Consistency/Simultaneous.lean b/Ix/Kernel/Verify/Consistency/Simultaneous.lean index c001dedfa..669076d0f 100644 --- a/Ix/Kernel/Verify/Consistency/Simultaneous.lean +++ b/Ix/Kernel/Verify/Consistency/Simultaneous.lean @@ -94,7 +94,23 @@ theorem readScopedExpr?_simulSubstSpec {β : Type u} obtain ⟨ref, resolved, bodyReads⟩ := bind_success bodyReads cases source <;> cases bodyReads simp [KExpr.simulSubstSpec, readScopedExpr?, resolved, AExpr.erase] - | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | str _ _ _ => contradiction + | letE name domain value body nonDep info ihDomain ihValue ihBody => + obtain ⟨A, v, b, domainReads, valueReads, innerReads, erased⟩ := readScopedExpr?_let_parts bodyReads + obtain ⟨domainSource, rfl⟩ := AExpr.erase_surjective A + obtain ⟨valueSource, rfl⟩ := AExpr.erase_surjective v + obtain ⟨bodySource, rfl⟩ := AExpr.erase_surjective b + simp only [KExpr.size] at bound + have next : (depth + 1).toNat = depth.toNat + 1 := by + rw [UInt64.toNat_add, show (1 : UInt64).toNat = 1 from rfl, + Nat.mod_eq_of_lt (show depth.toNat + 1 < UInt64.size by omega)] + have innerOut := ihBody (depth := depth + 1) (by rw [next]; omega) + (by simpa only [next, Nat.add_assoc, Nat.add_comm, Nat.add_left_comm] using innerReads) argumentReads + simp only [next] at innerOut + simp only [AExpr.erase_instRevAt, erased, VExpr.instRevAt_inst_zero] + simp [KExpr.simulSubstSpec, AExpr.erase_instRevAt, + ihDomain (depth := depth) (by omega) domainReads argumentReads, + ihValue (depth := depth) (by omega) valueReads argumentReads, innerOut] | app fn arg info ihFn ihArg => rw [readScopedExpr?] at bodyReads obtain ⟨f, fnReads, bodyReads⟩ := bind_success bodyReads diff --git a/Ix/Kernel/Verify/Consistency/SpineReading.lean b/Ix/Kernel/Verify/Consistency/SpineReading.lean index 30f852238..b4bb32c67 100644 --- a/Ix/Kernel/Verify/Consistency/SpineReading.lean +++ b/Ix/Kernel/Verify/Consistency/SpineReading.lean @@ -99,6 +99,8 @@ theorem readScopedExpr?_collectSpine {β : Type u} private theorem readScopedExpr?_app_inv {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {source : KExpr .anon} {fn arg : VExpr β} + (headLambda : ∃ name bi domain body info, + (RecM.appSpineView source).1 = .lam name bi domain body info) (reading : readScopedExpr? resolve locals source = some (.app fn arg)) : ∃ rawFn rawArg info, source = .app rawFn rawArg info ∧ readScopedExpr? resolve locals rawFn = some fn ∧ @@ -120,11 +122,16 @@ private theorem readScopedExpr?_app_inv {β : Type u} obtain ⟨_, _, reading⟩ := bind_success reading obtain ⟨_, _, same⟩ := bind_success reading cases same + | letE => + obtain ⟨_, _, _, _, _, same⟩ := headLambda + cases same | _ => simp [readScopedExpr?] at reading private theorem readScopedExpr?_lam_inv {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {source : KExpr .anon} {domain body : VExpr β} + (headLambda : ∃ name bi domain body info, + (RecM.appSpineView source).1 = .lam name bi domain body info) (reading : readScopedExpr? resolve locals source = some (.lam domain body)) : ∃ name bi rawDomain rawBody info, source = .lam name bi rawDomain rawBody info := by cases source with @@ -144,6 +151,9 @@ private theorem readScopedExpr?_lam_inv {β : Type u} obtain ⟨_, _, reading⟩ := bind_success reading obtain ⟨_, _, same⟩ := bind_success reading cases same + | letE => + obtain ⟨_, _, _, _, _, same⟩ := headLambda + cases same | _ => simp [readScopedExpr?] at reading private theorem list_reverse_induction {α : Type u} {motive : List α → Prop} @@ -158,31 +168,37 @@ private theorem list_reverse_induction {α : Type u} {motive : List α → Prop} simpa only [List.reverse_cons] using append_singleton items.reverse item ih simpa using reversed values.reverse -/-- The reading of the whole generated type determines the actual raw -head and argument readings. No additional inference of its components is -needed when the kernel collects that type's lambda-headed spine. -/ +/-- When the production spine has a syntactic lambda head, its whole +reading determines the head and argument readings. A lambda hidden inside +a let does not establish this premise; the production planner supplies it. -/ theorem readScopedExpr?_lambda_spine {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {source : KExpr .anon} {condition : Certified.PropWhen} {domain body : AExpr β} {arguments : List (AExpr β)} + (rawHeadLambda : ∃ name bi rawDomain rawBody info, + source.collectSpine.1 = .lam name bi rawDomain rawBody info) (reading : readScopedExpr? resolve locals source = some ((AExpr.lam condition domain body).appN arguments).erase) : readScopedExpr? resolve locals source.collectSpine.1 = some (AExpr.lam condition domain body).erase ∧ source.collectSpine.2.toList.map (readScopedExpr? resolve locals ·) = arguments.map (some ·.erase) := by + have headLambda : ∃ name bi rawDomain rawBody info, + (RecM.appSpineView source).1 = .lam name bi rawDomain rawBody info := by + simpa only [(RecM.appSpineView_collectSpine source).1] using rawHeadLambda + clear rawHeadLambda have view : readScopedExpr? resolve locals (RecM.appSpineView source).1 = some (AExpr.lam condition domain body).erase ∧ (RecM.appSpineView source).2.map (readScopedExpr? resolve locals ·) = arguments.map (some ·.erase) := by induction arguments using list_reverse_induction generalizing source with | nil => - obtain ⟨_, _, _, _, _, rfl⟩ := readScopedExpr?_lam_inv reading + obtain ⟨_, _, _, _, _, rfl⟩ := readScopedExpr?_lam_inv headLambda reading exact ⟨reading, rfl⟩ | append_singleton arguments argument ih => simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil, AExpr.erase] at reading - obtain ⟨fn, arg, info, rfl, fnReads, argReads⟩ := readScopedExpr?_app_inv reading - obtain ⟨headReads, argumentReads⟩ := ih fnReads + obtain ⟨fn, arg, info, rfl, fnReads, argReads⟩ := readScopedExpr?_app_inv headLambda reading + obtain ⟨headReads, argumentReads⟩ := ih fnReads headLambda exact ⟨headReads, by simp only [RecM.appSpineView, List.map_append, List.map_cons, List.map_nil, argumentReads, argReads]⟩ simpa only [(RecM.appSpineView_collectSpine source).1, diff --git a/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean b/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean index c27967003..591992d10 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean @@ -90,6 +90,8 @@ def complete (data : SynthesisCacheSupplement resolve anchor entries fuel before | lam full miss trace domainTree bodyTree => exact children.append (.singleton (.ofSource tree contextOrigin agreement reading miss accepted)) | lamBody full miss trace domainTree bodyTree => + exact children.append (.singleton (.ofSource tree contextOrigin agreement reading miss accepted)) + | letE full miss trace hashPath domainTree valueTree bodyTree => exact children.append (.singleton (.ofSource tree contextOrigin agreement reading miss accepted)) } end SynthesisCacheSupplement diff --git a/Ix/Kernel/Verify/Consistency/Validation.lean b/Ix/Kernel/Verify/Consistency/Validation.lean index 6b68ffcd5..eb46be5c7 100644 --- a/Ix/Kernel/Verify/Consistency/Validation.lean +++ b/Ix/Kernel/Verify/Consistency/Validation.lean @@ -53,7 +53,14 @@ theorem readScopedExpr?_scope {resolve : Address → Option (ConstRef β)} split at reading next bound => cases reading; exact ⟨trivial, bound⟩ next => contradiction - | fvar _ _ _ | letE _ _ _ _ _ _ _ _ _ | str _ _ _ => contradiction + | fvar _ _ _ | str _ _ _ => contradiction + | letE name domain value body nonDep info _ ihValue ihBody => + obtain ⟨A, v, b, _, valueReads, bodyReads, rfl⟩ := readScopedExpr?_let_parts reading + have valueScope := ihValue valueReads validSyntax.2.1 + have bodyScope := ihBody bodyReads validSyntax.2.2 + exact ⟨bodyScope.1.inst valueScope.1 0, by + simpa only [Nat.add_zero] using + VExpr.ClosedN.inst (cutoff := 0) (by simpa using bodyScope.2) valueScope.2⟩ | sort level info => cases reading exact ⟨readLevel_eq level ▸ validSyntax.toVLevel_wf, trivial⟩ diff --git a/Ix/Kernel/Verify/Infer/CheapBetaPlan.lean b/Ix/Kernel/Verify/Infer/CheapBetaPlan.lean index 6ab0940e9..27e210f4d 100644 --- a/Ix/Kernel/Verify/Infer/CheapBetaPlan.lean +++ b/Ix/Kernel/Verify/Infer/CheapBetaPlan.lean @@ -77,6 +77,22 @@ private theorem toNat_toUInt64_cheapBeta (n : Nat) : unfold Nat.toUInt64 rfl +/-- Selection witnesses a syntactic lambda head. Reading a let by +substitution may expose a model lambda without selecting a kernel plan. -/ +theorem cheapBetaPlan?_head_lambda {source : KExpr .anon} {plan : CheapBetaPlan .anon} + (selected : cheapBetaPlan? source = some plan) : + ∃ name bi domain body info, + source.collectSpine.1 = .lam name bi domain body info := by + cases source with + | app fn arg info => + simp only [cheapBetaPlan?] at selected + generalize spine : (KExpr.app fn arg info).collectSpine = collected at selected ⊢ + obtain ⟨head, arguments⟩ := collected + cases head with + | lam name bi domain body info => exact ⟨name, bi, domain, body, info, rfl⟩ + | _ => contradiction + | _ => contradiction + /-- A successful cheap-beta plan is exactly the simultaneous substitution of the consumed lambda prefix followed by the untouched application suffix. This is the arithmetic seam behind the selected-variable fast path: the diff --git a/Ix/Theory/Model/Annotated.lean b/Ix/Theory/Model/Annotated.lean index cf7bc21af..530ac0719 100644 --- a/Ix/Theory/Model/Annotated.lean +++ b/Ix/Theory/Model/Annotated.lean @@ -44,6 +44,27 @@ def erase : AExpr β → VExpr β | .proj r i e => .proj r i e.erase | .natLit v => .natLit v +/-- Every raw expression has a structural annotation. This says nothing +about the validity of the chosen binder conditions. -/ +theorem erase_surjective : Function.Surjective (erase : AExpr β → VExpr β) := by + intro term + induction term with + | bvar index => exact ⟨.bvar index, rfl⟩ + | sort level => exact ⟨.sort level, rfl⟩ + | const ref levels => exact ⟨.const ref levels, rfl⟩ + | natLit value => exact ⟨.natLit value, rfl⟩ + | app fn arg ihFn ihArg => + obtain ⟨f, rfl⟩ := ihFn + obtain ⟨a, rfl⟩ := ihArg + exact ⟨.app f a, rfl⟩ + | lam domain body ihDomain ihBody | forallE domain body ihDomain ihBody => + obtain ⟨A, rfl⟩ := ihDomain + obtain ⟨b, rfl⟩ := ihBody + first | exact ⟨.lam .always A b, rfl⟩ | exact ⟨.forallE .always A b, rfl⟩ + | proj ref field major ih => + obtain ⟨value, rfl⟩ := ih + exact ⟨.proj ref field value, rfl⟩ + theorem eq_const_of_erase_eq {e : AExpr β} {r : ConstRef β} {ls : List VLevel} (h : e.erase = .const r ls) : e = .const r ls := by cases e <;> simp_all [erase] diff --git a/Ix/Theory/Model/BetaSubstitution.lean b/Ix/Theory/Model/BetaSubstitution.lean index 2e83edc3f..e346bed1b 100644 --- a/Ix/Theory/Model/BetaSubstitution.lean +++ b/Ix/Theory/Model/BetaSubstitution.lean @@ -337,3 +337,72 @@ theorem instRevAt_bvar_selected (arguments : List (AExpr β)) (index cutoff : Na | cons argument arguments ih => simp only [instRevAt, inst, ih] end Ix.Theory.Model.AExpr + +namespace Ix.Theory.VExpr + +open Model + +/-- Inserting variables preserves closure in the correspondingly larger +context, including beneath syntactic binders. -/ +theorem ClosedN.liftN {term : VExpr β} {depth : Nat} (closed : term.ClosedN depth) + (count cutoff : Nat) : (term.liftN count cutoff).ClosedN (count + depth) := by + induction term generalizing depth cutoff with + | bvar index => + simp only [VExpr.liftN, liftVar, ClosedN] + split <;> simp only [ClosedN] at closed ⊢ <;> omega + | lam domain body ihDomain ihBody | forallE domain body ihDomain ihBody => + exact ⟨ihDomain closed.1 cutoff, by + simpa only [Nat.add_assoc] using ihBody closed.2 (cutoff + 1)⟩ + | _ => simp_all [VExpr.liftN, ClosedN] + +/-- Substitution removes one binder while retaining the binders below the +substitution cutoff. The argument belongs to the outer context. -/ +theorem ClosedN.inst {term argument : VExpr β} {depth cutoff : Nat} + (bodyClosed : term.ClosedN (depth + cutoff + 1)) + (argumentClosed : argument.ClosedN depth) : + (term.inst argument cutoff).ClosedN (depth + cutoff) := by + induction term generalizing cutoff with + | bvar index => + simp only [VExpr.inst, instVar] + split + next below => exact Nat.lt_of_lt_of_le below (by omega) + next => + split + · simpa only [Nat.add_comm] using argumentClosed.liftN cutoff 0 + · simp only [ClosedN] at bodyClosed ⊢; omega + | lam domain body ihDomain ihBody | forallE domain body ihDomain ihBody => + exact ⟨ihDomain bodyClosed.1, by + simpa only [Nat.add_assoc] using ihBody (cutoff := cutoff + 1) + (by simpa only [Nat.add_assoc] using bodyClosed.2)⟩ + | _ => simp_all [VExpr.inst, ClosedN] + +/-- The raw substitution laws follow from structural erasure, independently +of whether any binder annotation is semantically valid. -/ +theorem liftN_inst_zero (term argument : VExpr β) (count cutoff : Nat) : + (term.inst argument).liftN count cutoff = + (term.liftN count (cutoff + 1)).inst (argument.liftN count cutoff) := by + obtain ⟨body, rfl⟩ := AExpr.erase_surjective term + obtain ⟨value, rfl⟩ := AExpr.erase_surjective argument + simpa only [AExpr.erase_liftN, AExpr.erase_inst] using + congrArg AExpr.erase (AExpr.liftN_inst_zero body value count cutoff) + +theorem inst_inst_zero (term first second : VExpr β) (cutoff : Nat) : + (term.inst first).inst second cutoff = + (term.inst second (cutoff + 1)).inst (first.inst second cutoff) := by + obtain ⟨body, rfl⟩ := AExpr.erase_surjective term + obtain ⟨value, rfl⟩ := AExpr.erase_surjective first + obtain ⟨argument, rfl⟩ := AExpr.erase_surjective second + simpa only [AExpr.erase_inst] using + congrArg AExpr.erase (AExpr.inst_inst_zero body value argument cutoff) + +theorem instRevAt_inst_zero (term value : VExpr β) (arguments : List (VExpr β)) + (cutoff : Nat) : + (term.inst value).instRevAt arguments cutoff = + (term.instRevAt arguments (cutoff + 1)).inst (value.instRevAt arguments cutoff) := by + induction arguments generalizing term value with + | nil => rfl + | cons argument arguments ih => + simp only [instRevAt, inst_inst_zero, ih, + show cutoff + arguments.length + 1 = cutoff + 1 + arguments.length by omega] + +end Ix.Theory.VExpr diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 7457c4953..e698db2dd 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -3449,6 +3449,127 @@ private def compositeOccupiedForeignKey : Bool := exactInferenceCaches [(constant, identityType)] [(constant, prop)] after && after.env.nextFVarId == before.env.nextFVarId && after.recFuel == 0 +/-- Let inference publishes its domain, value, opened body and parent in +the full map. Temporary-local entries survive cleanup, while replay at zero +fuel allocates no local and clearing forces fresh child publications. -/ +private def letExactCacheHistory : Bool := + let prop := KExpr.mkSort (m := .anon) .mkZero + let sort := KExpr.mkSort (m := .anon) levelOne + let sortTwo := KExpr.mkSort (m := .anon) levelTwo + let body := KExpr.mkLam () () (.mkVar 0 ()) (.mkVar 0 ()) + let source := KExpr.mkLet () sort prop body false + let expected := KExpr.mkAll () () prop prop + let action : RecM .anon Bool := do + let checkFresh : RecM .anon Bool := do + let start ← get + let letLocal := KExpr.mkFVar ⟨start.env.nextFVarId⟩ () + let lambdaLocal := KExpr.mkFVar ⟨start.env.nextFVarId + 1⟩ () + let openedBody := KExpr.mkLam () () letLocal (.mkVar 0 ()) + let bodyType := KExpr.mkAll () () letLocal letLocal + let result ← RecM.inferCall source + let finished ← get + return result == expected && finished.lctx.size == start.lctx.size && + finished.env.nextFVarId == start.env.nextFVarId + 2 && finished.deqCalls > start.deqCalls && + exactInferenceCaches [(sort, sortTwo), (prop, sort), (letLocal, sort), (lambdaLocal, letLocal), + (openedBody, bodyType), (source, expected)] [] finished + if !(← checkFresh) then return false + let checked ← get + for policy in [false, true] do + match (RecM.infer source).run (methodsN 0) {checked with inferOnly := policy} with + | .ok result replayed => + if result != expected || replayed.env.nextFVarId != checked.env.nextFVarId || + replayed.env.inferCache.size != checked.env.inferCache.size || + replayed.env.inferOnlyCache.size != 0 then return false + | .error _ _ => return false + modify fun state => {state with env := state.env.clearReductionCaches} + return ← checkFresh + match TcM.runRec action {TcState.newLazyAnon {} with stats := true} with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +/-- The inferred body type is a let-local application. Closing and value +substitution expose a lambda; cheap beta returns the declared proposition. -/ +private def letGeneratedBetaEnvironment : Ixon.Env × Address := Id.run do + let domain := Ixon.Expr.leanAll (.sort 0) (.sort 0) + let identity := Ixon.Expr.leanLam (.sort 0) (.var 0) + let (env, proposition) := storeConst {} + ⟨.axio ⟨false, 0, .sort 0⟩, #[], #[], #[.zero]⟩ + let (env, witness) := storeConst env + ⟨.axio ⟨false, 0, .leanAll domain (.app (.var 0) (.ref 0 #[]))⟩, + #[], #[proposition], #[.zero]⟩ + return storeConst env + ⟨.defn ⟨.thm, .safe, 0, .ref 0 #[], + .letE false domain identity (.app (.ref 1 #[]) (.var 0))⟩, + #[], #[proposition, witness], #[.zero]⟩ + +private def letGeneratedTypeBeta : Bool := + let (source, target) := letGeneratedBetaEnvironment + let action : RecM .anon Bool := do + let .defn _ _ _ _ _ _ expected term _ _ ← TcM.getConst (m := .anon) ⟨target, ()⟩ | return false + let .letE name domain value body _ _ := term | return false + let .sort .. ← RecM.inferCall domain | return false + let valueType ← RecM.inferCall value + if valueType != domain then return false + let changed ← RecM.withLctxScope do + let (opened, fresh) ← TcM.openLet name domain value body + let bodyType ← RecM.inferCall opened + let closed ← TcM.runIntern (abstractFVars bodyType #[fresh]) + let substituted ← TcM.runIntern (subst closed value 0) + let reduced ← TcM.runIntern (cheapBetaReduce substituted) + return bodyType.hasFVars && bodyType != expected && substituted != expected && + (cheapBetaPlan? substituted).isSome && reduced == expected && !reduced.hasFVars + if !changed || (← get).lctx.size != 0 then return false + let result ← RecM.inferCall term + return result == expected && compositeReplayAt term expected (← get) + match TcM.runRec action (TcState.newLazyAnon source) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +private def letNestedCapture : Bool := + let sort := KExpr.mkSort (m := .anon) levelOne + let action : RecM .anon Bool := RecM.withLctxScope do + let (carrier, _) ← TcM.openBinder () () sort (.mkVar 0 ()) + let inner := KExpr.mkLet () sort (.mkVar 0 ()) + (.mkLam () () (.mkVar 0 ()) (.mkVar 0 ())) false + let term := KExpr.mkLet () sort carrier inner false + let result ← RecM.inferCall term + let expected := KExpr.mkAll () () carrier carrier + return result == expected && result.hasFVars && (← get).lctx.size == 1 && + compositeReplayAt term expected (← get) + match TcM.runRec action (TcState.newLazyAnon {}) with + | .ok passed after => passed && after.lctx.size == 0 + | .error _ _ => false + +private def letFailureCleanup : Bool := + let prop := KExpr.mkSort (m := .anon) .mkZero + let sort := KExpr.mkSort (m := .anon) levelOne + let sortTwo := KExpr.mkSort (m := .anon) levelTwo + let sortThree := KExpr.mkSort (m := .anon) (.mkSucc levelTwo) + let badValue := KExpr.mkLet () prop prop (.mkVar 0 ()) false + let badBody := KExpr.mkLet () sort prop (.mkFVar ⟨99⟩ ()) false + let rejectsValue := match TcM.infer badValue (TcState.newLazyAnon {}) with + | .error .declTypeMismatch after => after.lctx.size == 0 && after.env.nextFVarId == 0 && + exactInferenceCaches [(prop, sort)] [(sort, sortTwo), (sortTwo, sortThree)] after + | _ => false + let cleansBody := match TcM.infer badBody (TcState.newLazyAnon {}) with + | .error _ after => after.lctx.size == 0 && after.env.nextFVarId == 1 && + exactInferenceCaches [(sort, sortTwo), (prop, sort)] [] after + | _ => false + rejectsValue && cleansBody + +private def letCases : TestSeq := + test "let inference: dependent type substitution retains exact child caches, replay, and fresh rebuilding" + letExactCacheHistory + ++ test "let inference: substitution exposes the generated type's selected cheap-beta redex" + letGeneratedTypeBeta + ++ test "let inference: generated beta types reach theorem admission with persistent and cleared caches" + (allSucceeded letGeneratedBetaEnvironment.1 3 { clearEvery := 0 } && + allSucceeded letGeneratedBetaEnvironment.1 3 { clearEvery := 1 }) + ++ test "let inference: nested lets preserve an older captured dependent local through both closures" + letNestedCapture + ++ test "let inference: bad values are checked and failed bodies restore scope without publishing the parent" + letFailureCleanup + private def compositeCacheCases : TestSeq := test "composite cache: applications survive lazy inference, scopes, replay, and clearing" (compositeCacheHistory 0) @@ -3574,6 +3695,6 @@ public def suite : List TestSeq := cheapApplicationCases, exposedLambdaCases, repeatedBetaCases, betaTraceCases, hereditaryBetaCases, piExposureCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, - sourceAgreementCases, sourceCacheCases, compositeCacheCases, polymorphicDefinitionCases] + sourceAgreementCases, sourceCacheCases, compositeCacheCases, letCases, polymorphicDefinitionCases] end Tests.Kernel.Consistency diff --git a/Tests/Theory/certified-foundation.txt b/Tests/Theory/certified-foundation.txt index df3ab3819..f6d3cd72a 100644 --- a/Tests/Theory/certified-foundation.txt +++ b/Tests/Theory/certified-foundation.txt @@ -10666,4 +10666,4 @@ compiler recursion worker Ix.Theory.Model.SetTheory.Tower.PropS._unsafe_rec Model.SetTheory.Mem A Model.SetTheory.univZero ∧ ∀ (a : V), Model.SetTheory.Mem a A → Model.SetTheory.Tower.PropS._unsafe_rec (B a) Certified project runtime audit OK: 101 compiler recursion workers inventoried; no other unsafe/partial declarations, extern, implemented_by, computed_field, or csimp replacements in the mathematical import graph -Certified foundation audit OK: 6568 declarations; 145 named roots; explicit SetTheory hypothesis; baseline axioms only +Certified foundation audit OK: 6569 declarations; 145 named roots; explicit SetTheory hypothesis; baseline axioms only diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 66f6a92e9..8ab3e271e 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -32,8 +32,10 @@ increasing sequence of strongly inaccessible cardinals. addresses to explicit store references, preserves projections and natural literals, and substitutes let values. This closed reader excludes free variables, unresolved addresses, and string literals. The binder reader - `readScopedExpr?` maps registered free variables to model context indices; - unknown locals, loose legacy variables, lets, and strings fail that reader. + `readScopedExpr?` maps registered free variables to model context indices + and substitutes let values beneath active locals and binders. Unknown locals, + loose legacy variables, and strings fail that reader. Opening, abstraction, + term/universe substitution, scope, and reference proofs include nested lets. - Hash equality and intern-table reuse preserve that reading under their stated address/key collision assumptions. Metadata cannot change it. - `inferUncached_sort_sound` interprets an actual successful execution of the @@ -95,6 +97,18 @@ increasing sequence of strongly inaccessible cardinals. `AxiomObservation.synthesisTypeCheck` extracts an axiom's type check from successful `checkEnvAnon` rows; `StandalonePrefix.definitionTypeCheck` extracts a definition's check from public declaration success. +- `LetInferenceCheck.sound` connects the full production let branch to its + original declared-type, value, and opened-body synthesis checks. The actual + value-type hash comparison establishes domain agreement. Fresh let opening, + type abstraction, value substitution, and the selected cheap-beta operation + give the returned type and its formation bound. Substituting the original + checking derivations retains lambda domains and argument checks for later + beta steps, including lambdas exposed by the substituted value. + `DefinitionBodyTrace.letSupport` uses the same declaration's validation and + inference calls to include let bodies in model extension. The three children + still belong to the existing `SynthesisInference` fragment; composing lets + arbitrarily into that recursive datatype remains open. Finite execution, + collision, walker, and selected beta-origin resources remain explicit. - `instantiateUnivParams_readScopedAnnotated` brings the actual substituted declaration type into any active local context, preserving its closed term scope and occurrence annotations. `infer_const_scoped_annotated` uses this @@ -469,7 +483,7 @@ the run. They supply no typing or checker-soundness premise. The proof extracts the validation, type-inference, theorem-guard, value-inference, and conversion steps from public success, then derives body typing to extend the preceding model. General automatic witness construction, broader application and -lambda paths, lets, inductives, coordinated blocks, and other +lambda paths, arbitrary recursive let inference, inductives, coordinated blocks, and other conversion paths remain outside the fragment. The declaration's exact universe arity is carried from production lookup into its model entry. Model extension interprets the checked body at every universe instance, retaining old @@ -525,7 +539,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 766 exact theorem boundaries. The production +The consistency target checks 799 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -694,6 +708,16 @@ proved context relation. General execution/resource construction, missing checking-only child annotations, other inference branches, and general WHNF and conversion cache histories remain open. These 61 new audited roots retain the existing production axioms and introduce no new axiom or native proof. +Full-mode lets now extend the operational history with their declared-type, +value, and opened-body events followed by the parent publication. The same +`LetInferenceCheck` and its three children's cache data derive this trace, +the exact fold for both maps, and a history containing every child and parent +write. Opening and scope cleanup retain those maps. Initially occupied full +keys remain protected by cache priority without a collision premise. The +typed `SynthesisCacheHistory` still needs the let case integrated into its +recursive inference origins before it can retain a let root. The 33 additional +let and representation audit boundaries preserve the existing axiom policy. + Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, argument order, and rejection of wrong arities and out-of-range parameters. @@ -856,7 +880,12 @@ parent, and exited-local entries, through policy changes, successful and failed loading, clearing, and repopulation. A forged different input at an occupied full key confirms that priority preserves the maps even without collision freedom; semantic source recovery separately requires finite collision data. -The unit suite contains 711 checks. The anonymous differential additionally +Five let regressions check exact child/parent cache maps, zero-fuel replay under +both policies, clearing and rebuilding, cheap beta in the generated type, +theorem admission with both cache-clearing settings, nested capture avoidance, +and cleanup after value-comparison and body-inference failures. They exercise +production independently of the finite resources used by the proof. +The unit suite contains 716 checks. The anonymous differential additionally serializes eight cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. diff --git a/docs/theory.md b/docs/theory.md index 8d5d27aa3..8c3010c76 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -103,6 +103,17 @@ reading, and representation resources; its semantic origins and declaration conversion follow automatically from the original inference. Constructing the initial inference and operational resources for arbitrary accepted programs, the remaining reduction branches, and general conversion remain open. +Full-mode let inference now uses its original declared-type, value, and opened +body checks. The scoped reader interprets a let by value substitution, and the +actual opening, abstraction, and substitution walkers preserve that reading +beneath locals and binders. The successful value-type hash comparison supplies +the domain agreement needed to substitute both the body's typing derivation +and its inferred-type origin. This retains all subsequent beta origins, including +a lambda introduced by replacing the let variable. The actual final cheap-beta +choice determines the returned type. Declaration admission includes these let +bodies using the same validation and inference calls. The three child checks +remain in the existing recursive synthesis fragment; arbitrary recursive let +composition and automatic construction of its finite resources remain open. Safe definition admission also rejects circular justification in both Lean and Rust, including `theorem loop : P := loop` with only `P : Prop` assumed. The production dependency walk returns an order with a proved decreasing rank; @@ -142,6 +153,11 @@ checking-only wrappers need supplementary annotations for omitted child calls. Finite collision data over the query and historical inputs recovers the original source. Selection then derives its cached result reading and complete retained check, including the original local context and later interface transport. +The raw execution history also covers full-mode lets, with all three child +calls before the parent publication. Their original let check and child cache +data derive both entire maps and the history. Retaining a let root in the typed +synthesis history still requires its integration into the recursive checking +datatype. Arbitrary execution construction, compatibility with later contexts after scope exit, and the other inference and conversion/cache paths remain open. Frames allow new declarations while retaining old ones. From e1a03ee5a7f938c5045fe34c409788c5e20028b3 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 11:41:20 -0400 Subject: [PATCH 38/63] Cover safe recursion and circular declaration boundaries --- Tests/Ix/Kernel/DefinitionDependencies.lean | 38 +++++++- Tests/Ix/Kernel/SafeRecursion.lean | 98 +++++++++++++++++++++ Tests/Main.lean | 3 + docs/kernel-verification.md | 29 +++++- docs/theory.md | 8 +- 5 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 Tests/Ix/Kernel/SafeRecursion.lean diff --git a/Tests/Ix/Kernel/DefinitionDependencies.lean b/Tests/Ix/Kernel/DefinitionDependencies.lean index 7f73534fb..9e08ac98a 100644 --- a/Tests/Ix/Kernel/DefinitionDependencies.lean +++ b/Tests/Ix/Kernel/DefinitionDependencies.lean @@ -4,7 +4,8 @@ public import Tests.Ix.Kernel.IxonFixtures /-! Definition-cycle regressions through the real content-addressed source loader -and public checker. The only axiom in each negative logical example is `P : Prop`. +and public checker. The logical examples either have no axioms or assume only +`P : Prop`. A separate internal-state test checks cycles across block boundaries. -/ namespace Tests.Kernel.DefinitionDependencies @@ -14,6 +15,13 @@ open LSpec Ix.Kernel Tests.Kernel.Fixtures private def propSource : Ixon.Env × Address := storeConst {} ⟨.axio ⟨false, 0, .sort 0⟩, #[], #[], #[.zero]⟩ +/-- A hash-verified declaration of every proposition, with no source axioms. +The relative self-reference is encoded without any assumed hash collision. -/ +private def axiomFreeCycleSource (kind : Ix.DefKind) : Ixon.Env × Address := + storeConst {} + ⟨.defn ⟨kind, .safe, 0, .leanAll (.sort 0) (.var 0), .recur 0 #[]⟩, + #[], #[], #[.zero]⟩ + private def cycleSource (kind : Ix.DefKind) (asBlock : Bool) (value : Ixon.Expr := .recur 0 #[]) (safety : Ix.DefinitionSafety := .safe) (sharing : Array Ixon.Expr := #[]) : Ixon.Env × Address := @@ -93,9 +101,30 @@ private def nonlogicalPasses (safety : Ix.DefinitionSafety) : Bool := | .ok rows => rows.size == 2 && rows.all (·.err?.isNone) | .error _ => false +/-- These deliberately constructed internal keys are not content hashes. +The dependency traversal must follow edges across coordinated blocks too. -/ +private def rejectsSeparatedBlocks : Bool := + let first : KId .anon := ⟨Address.blake3 "separated-cycle-first".toUTF8, ()⟩ + let second : KId .anon := ⟨Address.blake3 "separated-cycle-second".toUTF8, ()⟩ + let type := KExpr.mkAll () () (.mkSort .mkZero) (.mkVar 0 ()) + let declaration (block target : KId .anon) : KConst .anon := + .defn () () .defn .safe (.regular 0) 0 type (.mkConst target #[]) () block + let env := ({} : AnonEnv) + |>.insert first (declaration first second) + |>.insert second (declaration second first) + |>.insertBlock first #[first] + |>.insertBlock second #[second] + [first, second].all fun requested => + match TcM.checkConst requested (.ofEnvAnon env) with + | .error (.other message) _ => ("cyclic definition dependency").isPrefixOf message + | _ => false + /-- Serialized-source regressions shared with the Rust differential suite. The last two fields are the exact target count and expected failure count. -/ public def parityFixtures : Array (String × Ixon.Env × Nat × Nat) := #[ + ("axiom-free-theorem-cycle", (axiomFreeCycleSource .thm).1, 1, 1), + ("axiom-free-definition-cycle", (axiomFreeCycleSource .defn).1, 1, 1), + ("axiom-free-opaque-cycle", (axiomFreeCycleSource .opaq).1, 1, 1), ("standalone-cycle", (cycleSource .thm false).1, 2, 1), ("one-member-cycle", (cycleSource .thm true).1, 2, 1), ("mutual-cycle", mutualCycle.1, 3, 2), @@ -107,7 +136,12 @@ public def parityFixtures : Array (String × Ixon.Env × Nat × Nat) := #[ ] public def suite : List TestSeq := [ - test "definition dependencies: a standalone theorem cannot justify itself" + ([Ix.DefKind.defn, .thm, .opaq].foldl (init := .done) fun tests kind => + tests ++ test s!"definition dependencies: an axiom-free {repr kind} cannot prove every proposition by self-reference" + (rejectsCycle (axiomFreeCycleSource kind))) + ++ test "definition dependencies: a cycle across separate internal blocks is rejected" + rejectsSeparatedBlocks + ++ test "definition dependencies: a standalone theorem cannot justify itself" (rejectsCycle (cycleSource .thm false)) ++ test "definition dependencies: a one-member mutual theorem cannot justify itself" (rejectsCycle (cycleSource .thm true)) diff --git a/Tests/Ix/Kernel/SafeRecursion.lean b/Tests/Ix/Kernel/SafeRecursion.lean new file mode 100644 index 000000000..36d66a714 --- /dev/null +++ b/Tests/Ix/Kernel/SafeRecursion.lean @@ -0,0 +1,98 @@ +module + +public import Tests.Ix.Kernel.AnonDiff + +/-! +Termination-checked source recursion must survive export and both host +kernels' safe-definition dependency guards. These positive cases complement +the raw Ixon self-reference and mutual-cycle rejection tests. +-/ + +public section + +namespace Tests.Kernel.SafeRecursion + +open LSpec Ix.Kernel + +namespace Fixtures + +def countStruct : Nat → Nat + | 0 => 0 + | n + 1 => countStruct n + 1 +termination_by structural n => n + +def countWellFounded (n : Nat) : Nat := + if h : n = 0 then 0 else countWellFounded (n - 1) + 1 +termination_by n +decreasing_by omega + +mutual + def even : Nat → Bool + | 0 => true + | n + 1 => odd n + termination_by structural n => n + + def odd : Nat → Bool + | 0 => false + | n + 1 => even n + termination_by structural n => n +end + +end Fixtures + +private def checkCase (env : Lean.Environment) (label : String) + (seeds : List Lean.Name) : IO Nat := do + for seed in seeds do + let some (.defnInfo declaration) := env.find? seed + | throw <| IO.userError s!"missing elaborated definition {seed}" + unless declaration.safety == .safe do + throw <| IO.userError s!"{seed} is not a safe definition" + if declaration.type.getUsedConstantsAsSet.contains seed || + declaration.value.getUsedConstantsAsSet.contains seed then + throw <| IO.userError s!"elaboration retained a direct self-reference in {seed}" + let constants := AnonDiff.closureOf env seeds + let directory ← IO.FS.createTempDir + let path := directory / s!"safe-recursion-{label}.ixe" + try + let status ← Ix.CompileM.rsCompileEnvBytesFFI constants path.toString false + unless status.ungrounded.isEmpty do + throw <| IO.userError s!"compilation omitted {status.ungrounded.size} declarations" + let bytes ← IO.FS.readBinFile path + let source ← IO.ofExcept (Ixon.deEnv bytes) + let leanRows ← IO.ofExcept (checkEnvAnon source { verifyHashes := true }) + let rustRows ← Ix.KernelCheck.rsCheckAnonFFI path.toString true "" + if leanRows.isEmpty || leanRows.size != rustRows.size then + throw <| IO.userError s!"target coverage differs: Lean {leanRows.size}, Rust {rustRows.size}" + -- Require successful checking, not merely agreement between two rejections. + for row in leanRows do + if let some error := row.err? then + throw <| IO.userError s!"Lean rejected {row.addr}: {error}" + for (address, error) in rustRows do + if let some error := error then + throw <| IO.userError s!"Rust rejected {address}: {error.message}" + unless leanRows.any (fun row => toString row.addr == address) do + throw <| IO.userError s!"Rust checked an unmatched address {address}" + -- Each requested definition must actually be present and checked. + for seed in seeds do + let some address := source.getAddr? (Ix.Name.fromLeanName seed) + | throw <| IO.userError s!"export omitted {seed}" + unless leanRows.any (fun row => row.addr == address) && + rustRows.any (fun row => row.1 == toString address) do + throw <| IO.userError s!"no checking result for {seed}" + return leanRows.size + finally + IO.FS.removeDirAll directory + +def suite : List TestSeq := + [([("structural", [``Fixtures.countStruct]), + ("well-founded", [``Fixtures.countWellFounded]), + ("mutual", [``Fixtures.even, ``Fixtures.odd])].foldl (init := .done) + fun tests (label, seeds) => tests ++ + .individualIO s!"safe recursion survives both kernels: {label}" none (do + let env ← get_env! + let checked ← checkCase env label seeds + return (true, checked, 0, none)) .done)] + +end Tests.Kernel.SafeRecursion + +end diff --git a/Tests/Main.lean b/Tests/Main.lean index e6aca38b7..cabfd233c 100644 --- a/Tests/Main.lean +++ b/Tests/Main.lean @@ -43,6 +43,7 @@ import Tests.Ix.Kernel.CheckTests import Tests.Ix.Kernel.Consistency import Tests.Ix.Kernel.DefinitionDependencies import Tests.Ix.Kernel.AnonDiff +import Tests.Ix.Kernel.SafeRecursion import Tests.Ix.Kernel.InitScale import Tests.Ix.Kernel.TutorialTc import Tests.Ix.Kernel.CheckerRoundtrip @@ -112,7 +113,9 @@ def primarySuites : Std.HashMap String (List LSpec.TestSeq) := .ofList [ ++ Tests.Kernel.InferDefEq.suite ++ Tests.Kernel.CheckTests.suite ++ Tests.Kernel.Consistency.suite ++ Tests.Kernel.DefinitionDependencies.suite + ++ Tests.Kernel.SafeRecursion.suite ++ Tests.Kernel.Roundtrip.unitTests ++ Tests.Kernel.IngressMeta.unitTests), + ("tc-safe-recursion", Tests.Kernel.SafeRecursion.suite), ] /-- Ignored test suites - expensive, run only when explicitly requested. These require significant RAM -/ diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 8ab3e271e..4958184fc 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -464,7 +464,15 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) projection heads. It rejects cycles and exhaustion of its shared one-million step bound. Axioms, inductives, constructors, and recursors are leaves with separate admission rules; partial and unsafe definitions retain their safety - policy. Acyclic mutual definitions remain supported. + policy. Acyclic mutual definitions remain supported. The guard acts on stored + global references: Lean elaborates safe source recursion into recursor + applications, including well-founded recursion through `WellFounded.fix`. + Recursive calls supplied as local arguments do not cite the definition's own + global address. Structural, well-founded, and mutual source recursion are + covered by exported examples checked successfully by both host kernels. + Negative examples also reject an axiom-free declaration of `∀ P : Prop, P` + whose entire value is its own relative reference, and cycles across separate + blocks in a deliberately constructed internal environment. - `DefinitionDependencies.order_sound` derives a concrete dependency order from the production walk. Every entry is fresh and its collected dependencies precede it; `Ordered.wellFounded` derives a decreasing natural-number rank. @@ -475,6 +483,14 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) safe validation, and `.referencesIn` connects that order to the references of the model's reading. Admission of every entry in that order still needs the broader body-typing and model-extension proofs. +- A cached block verdict needs the original completed check and preserved + declarations. `checkCoordinatedBlock` replays a cached success before checking + the body; low-level `KEnv.insert` does not invalidate that verdict. An internal + probe that replaces an already checked declaration can therefore replay + success for the replacement, which fails when checked with an empty verdict + cache. This probe bypasses fresh source ingress. The full theorem must derive + verdict validity from the initial state, immutable loading, completed checks, + and failure isolation; arbitrary internal states cannot supply that premise. - `checkEnvAnon` returns `.ok results` **and every result row has no error**. The outer `.ok` alone does not mean that the declarations passed. @@ -867,6 +883,13 @@ Definition-cycle regressions use content-addressed standalone and mutual declarations, including a self-justifying theorem, a two-member cycle, type cycles, lets, shared syntax, and binders. They check repeated member failures, acyclic forward references, cache clearing, and the partial/unsafe policy. +Three axiom-free cases reject self-justifying definitions, theorems, and opaque +declarations of `∀ P : Prop, P`; a separate internal-state case checks that +dependency traversal crosses block boundaries. Positive source-recursion +regressions export complete structural, well-founded, and mutual dependency +closures with partial output disabled. Both hosts must successfully check every +target, including each requested fixture. Matching rejections or omitted exports +cannot pass. These also run separately as `tc-safe-recursion`. Twelve composite-cache regressions cover real application, Pi, and lambda checks, zero-fuel replay under both policies, inference-only exclusion from full mode, lazy loading, scope changes, clearing and repopulation, beta Pi exposure, @@ -885,8 +908,8 @@ both policies, clearing and rebuilding, cheap beta in the generated type, theorem admission with both cache-clearing settings, nested capture avoidance, and cleanup after value-comparison and body-inference failures. They exercise production independently of the finite resources used by the proof. -The unit suite contains 716 checks. The anonymous differential additionally -serializes eight cycle-policy fixtures and checks exact target sets, verdicts, +The unit suite contains 723 checks. The anonymous differential additionally +serializes eleven cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. ## Certified host adapters diff --git a/docs/theory.md b/docs/theory.md index 8c3010c76..d65f35c83 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -120,7 +120,13 @@ production dependency walk returns an order with a proved decreasing rank; finite collision freedom ensures its memoized collector includes every syntax reference. Successful validation exposes that order to the model-reference proof. Constructing interpretations for all coordinated declarations remains -part of the full checker refinement. +part of the full checker refinement. Safe source recursion is elaborated into +recursor applications and remains supported; the rejected cycles refer directly +to global declaration addresses. A cached block verdict additionally needs +provenance from a completed check of the same declarations. Replacing a checked +declaration in internal state while retaining its cached verdict bypasses a new +body check, so the general proof must establish immutable loading and cache +validity from the actual fresh-state execution. Constant cache hits derive the same typing from concrete agreement with pure universe substitution of a loaded declaration; sort hits use the canonical successor sort. Sort and already-loaded constant inference preserve agreement From 10c038b68667a86a0fec2f4aea7ed279c19baa39 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 12:05:34 -0400 Subject: [PATCH 39/63] Derive local-state preservation through the recursive checker --- .../Check/UniverseInstantiationPolicy.lean | 263 +---- Ix/Kernel/Verify/Consistency/Audit.lean | 468 +++++++++ .../Verify/Consistency/DefEqLocalState.lean | 894 +++++++++++++++++ .../Verify/Consistency/InferenceCache.lean | 15 + .../Consistency/InferenceLocalState.lean | 403 ++++++++ .../Verify/Consistency/IngressLocalState.lean | 38 + Ix/Kernel/Verify/Consistency/LetCache.lean | 6 +- .../Verify/Consistency/LetInference.lean | 41 +- .../Verify/Consistency/LetSynthesis.lean | 37 +- Ix/Kernel/Verify/Consistency/LocalScope.lean | 162 ++++ Ix/Kernel/Verify/Consistency/LocalState.lean | 268 ++++++ .../Verify/Consistency/LocalStateEffects.lean | 315 ++++++ .../Verify/Consistency/LocalStateReading.lean | 87 ++ .../Verify/Consistency/LocalStateTactic.lean | 152 +++ .../Consistency/ProjectionLocalState.lean | 231 +++++ .../Consistency/RecursiveLocalState.lean | 88 ++ .../Verify/Consistency/WhnfLocalState.lean | 896 ++++++++++++++++++ Ix/Kernel/Verify/Ctx.lean | 133 +-- Ix/Kernel/Verify/IngressState.lean | 337 +++++++ Ix/Kernel/Verify/LocalContext.lean | 150 +++ Ix/Kernel/Verify/LocalScope.lean | 171 ++++ Ix/Kernel/Verify/UniverseInternOnly.lean | 319 +++++++ docs/kernel-verification.md | 19 +- docs/theory.md | 9 + 24 files changed, 5107 insertions(+), 395 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/DefEqLocalState.lean create mode 100644 Ix/Kernel/Verify/Consistency/InferenceLocalState.lean create mode 100644 Ix/Kernel/Verify/Consistency/IngressLocalState.lean create mode 100644 Ix/Kernel/Verify/Consistency/LocalScope.lean create mode 100644 Ix/Kernel/Verify/Consistency/LocalState.lean create mode 100644 Ix/Kernel/Verify/Consistency/LocalStateEffects.lean create mode 100644 Ix/Kernel/Verify/Consistency/LocalStateReading.lean create mode 100644 Ix/Kernel/Verify/Consistency/LocalStateTactic.lean create mode 100644 Ix/Kernel/Verify/Consistency/ProjectionLocalState.lean create mode 100644 Ix/Kernel/Verify/Consistency/RecursiveLocalState.lean create mode 100644 Ix/Kernel/Verify/Consistency/WhnfLocalState.lean create mode 100644 Ix/Kernel/Verify/IngressState.lean create mode 100644 Ix/Kernel/Verify/LocalContext.lean create mode 100644 Ix/Kernel/Verify/LocalScope.lean create mode 100644 Ix/Kernel/Verify/UniverseInternOnly.lean diff --git a/Ix/Kernel/Verify/Check/UniverseInstantiationPolicy.lean b/Ix/Kernel/Verify/Check/UniverseInstantiationPolicy.lean index 5fde07bc6..99400997e 100644 --- a/Ix/Kernel/Verify/Check/UniverseInstantiationPolicy.lean +++ b/Ix/Kernel/Verify/Check/UniverseInstantiationPolicy.lean @@ -1,4 +1,5 @@ import Ix.Kernel.Verify.Check.InferencePolicy +import Ix.Kernel.Verify.UniverseInternOnly /-! # Inference-policy frame for universe instantiation @@ -9,10 +10,9 @@ but its operational noninterference fact does not: the walk can throw while substituting a universe, and otherwise changes only its private memo table and the kernel intern table. -This module proves that unconditional operational fact directly over the -production walker. It covers memo hits, every expression constructor, the -constant-universe array loop, recursive child failures, interning, and memo -writes on both outcomes. +The shared exact state-effect proof in `UniverseInternOnly` covers memo +hits, every expression constructor, the constant-universe array loop, +recursive child failures, interning, and memo writes on both outcomes. -/ namespace Ix.Kernel @@ -30,251 +30,18 @@ theorem map {x : TcM .anon alpha} (hx : x.PreservesInferOnly) rw [← bind_pure_comp] exact bind hx fun value => pure (f value) -private def StatePreservesInferOnly - (x : StateT sigma (TcM .anon) alpha) : Prop := - ∀ memo, (x.run memo).PreservesInferOnly +/-- The stronger exact intern-table frame preserves the inference policy. -/ +theorem ofInternOnly {action : TcM .anon alpha} (only : action.InternOnly) : + action.PreservesInferOnly := by + intro before + have changed := only before + cases run : action before <;> rw [run] at changed <;> + obtain ⟨table, exactState⟩ := changed <;> rw [exactState] <;> rfl -private theorem statePure (value : alpha) : - StatePreservesInferOnly - (Pure.pure value : StateT sigma (TcM .anon) alpha) := by - intro memo - simp only [StateT.run_pure] - exact TcM.PreservesInferOnly.pure _ - -private theorem stateBind {x : StateT sigma (TcM .anon) alpha} - {f : alpha → StateT sigma (TcM .anon) beta} - (hx : StatePreservesInferOnly x) - (hf : ∀ value, StatePreservesInferOnly (f value)) : - StatePreservesInferOnly (x >>= f) := by - intro memo - simp only [StateT.run_bind] - apply TcM.PreservesInferOnly.bind (hx memo) - intro pair - exact hf pair.1 pair.2 - -private theorem stateGet : - StatePreservesInferOnly - (MonadState.get : StateT sigma (TcM .anon) sigma) := by - intro memo - simp only [StateT.run_get] - exact TcM.PreservesInferOnly.pure _ - -private theorem stateModify (f : sigma → sigma) : - StatePreservesInferOnly - (_root_.modify f : StateT sigma (TcM .anon) PUnit) := by - intro memo - simp only [StateT.run_modify] - exact TcM.PreservesInferOnly.pure _ - -private theorem stateLift {x : TcM .anon alpha} - (hx : x.PreservesInferOnly) : - StatePreservesInferOnly - (monadLift x : StateT sigma (TcM .anon) alpha) := by - intro memo - simp only [StateT.run_monadLift] - apply TcM.PreservesInferOnly.bind hx - intro value - exact TcM.PreservesInferOnly.pure _ - -private theorem stateForInArray - (items : Array alpha) (initial : beta) - (step : alpha → beta → - StateT sigma (TcM .anon) (ForInStep beta)) - (hstep : ∀ item state, - StatePreservesInferOnly (step item state)) : - StatePreservesInferOnly (forIn items initial step) := by - rcases items with ⟨items⟩ - simp only [List.forIn_toArray] - induction items generalizing initial with - | nil => - simp - exact statePure initial - | cons item rest ih => - rw [List.forIn_cons] - apply stateBind (hstep item initial) - intro action - cases action with - | done result => exact statePure result - | yield next => exact ih next - -private theorem stateInternMemo (key : Address) (result : KExpr .anon) : - StatePreservesInferOnly (do - let interned ← monadLift (TcM.intern result) - _root_.modify fun memo : Std.HashMap Address (KExpr .anon) => - memo.insert key interned - Pure.pure interned) := by - apply stateBind (stateLift (runIntern _)) - intro interned - apply stateBind (stateModify _) - intro _ - exact statePure interned - -/-- Universe instantiation cannot change the inference-policy bit. The -statement is unconditional because collision freedom is relevant to the -walker's semantic result, not to which `TcState` fields it can mutate. -/ -theorem instantiateUnivParams (e : KExpr .anon) - (us : Array (KUniv .anon)) : - (TcM.instantiateUnivParams e us).PreservesInferOnly := by - unfold TcM.instantiateUnivParams - split - · exact pure e - · have hinner : ∀ (source : KExpr .anon), - StatePreservesInferOnly (TcM.instUnivInner source us) := by - intro source - induction source with - | var idx name info => - simp only [TcM.instUnivInner] - apply stateBind stateGet - intro memo - split - · exact statePure _ - · apply stateBind (stateModify _) - intro _ - exact statePure (KExpr.var idx name info) - | fvar id name info => - simp only [TcM.instUnivInner] - apply stateBind stateGet - intro memo - split - · exact statePure _ - · apply stateBind (stateModify _) - intro _ - exact statePure (KExpr.fvar id name info) - | sort u info => - simp only [TcM.instUnivInner] - apply stateBind stateGet - intro memo - split - · exact statePure _ - · apply stateBind (stateLift (ofExcept (substUniv u us))) - intro resultUniv - apply stateBind (statePure (KExpr.mkSort resultUniv)) - intro result - exact stateInternMemo (KExpr.sort u info).addr result - | const id levels info => - rw [TcM.instUnivInner] - simp (config := { proj := false }) only [] - apply stateBind stateGet - intro memo - split - · exact statePure _ - · apply stateBind - (stateForInArray levels (Array.mkEmpty levels.size) - (fun level current => do - let instantiated ← - monadLift (TcM.ofExcept (substUniv level us)) - let next := current.push instantiated - Pure.pure PUnit.unit - Pure.pure (ForInStep.yield next)) - (by - intro level current - apply stateBind - (stateLift (ofExcept (substUniv level us))) - intro instantiated - exact statePure - (ForInStep.yield (current.push instantiated)))) - intro newLevels - apply stateBind (statePure (KExpr.mkConst id newLevels)) - intro result - exact stateInternMemo (KExpr.const id levels info).addr result - | app f a info ihf iha => - rw [TcM.instUnivInner] - simp (config := { proj := false }) only [] - apply stateBind stateGet - intro memo - split - · exact statePure _ - · apply stateBind ihf - intro resultF - apply stateBind iha - intro resultA - apply stateBind (statePure (KExpr.mkApp resultF resultA)) - intro result - exact stateInternMemo (KExpr.app f a info).addr result - | lam name bi ty body info ihty ihbody => - rw [TcM.instUnivInner] - simp (config := { proj := false }) only [] - apply stateBind stateGet - intro memo - split - · exact statePure _ - · apply stateBind ihty - intro resultTy - apply stateBind ihbody - intro resultBody - apply stateBind - (statePure (KExpr.mkLam name bi resultTy resultBody)) - intro result - exact stateInternMemo (KExpr.lam name bi ty body info).addr result - | all name bi ty body info ihty ihbody => - rw [TcM.instUnivInner] - simp (config := { proj := false }) only [] - apply stateBind stateGet - intro memo - split - · exact statePure _ - · apply stateBind ihty - intro resultTy - apply stateBind ihbody - intro resultBody - apply stateBind - (statePure (KExpr.mkAll name bi resultTy resultBody)) - intro result - exact stateInternMemo (KExpr.all name bi ty body info).addr result - | letE name ty value body nondep info ihty ihvalue ihbody => - rw [TcM.instUnivInner] - simp (config := { proj := false }) only [] - apply stateBind stateGet - intro memo - split - · exact statePure _ - · apply stateBind ihty - intro resultTy - apply stateBind ihvalue - intro resultValue - apply stateBind ihbody - intro resultBody - apply stateBind - (statePure - (KExpr.mkLet name resultTy resultValue resultBody nondep)) - intro result - exact stateInternMemo - (KExpr.letE name ty value body nondep info).addr result - | prj id field value info ih => - rw [TcM.instUnivInner] - simp (config := { proj := false }) only [] - apply stateBind stateGet - intro memo - split - · exact statePure _ - · apply stateBind ih - intro resultValue - apply stateBind - (statePure (KExpr.mkPrj id field resultValue)) - intro result - exact stateInternMemo (KExpr.prj id field value info).addr result - | nat value blob info => - simp only [TcM.instUnivInner] - apply stateBind stateGet - intro memo - split - · exact statePure _ - · apply stateBind (stateModify _) - intro _ - exact statePure (KExpr.nat value blob info) - | str value blob info => - simp only [TcM.instUnivInner] - apply stateBind stateGet - intro memo - split - · exact statePure _ - · apply stateBind (stateModify _) - intro _ - exact statePure (KExpr.str value blob info) - have hrun := hinner e ({} : Std.HashMap Address (KExpr .anon)) - unfold StateT.run' - exact map hrun Prod.fst +/-- Universe instantiation cannot change the inference-policy bit. -/ +theorem instantiateUnivParams (e : KExpr .anon) (us : Array (KUniv .anon)) : + (TcM.instantiateUnivParams e us).PreservesInferOnly := + ofInternOnly (TcM.InternOnly.instantiateUnivParams e us) end TcM.PreservesInferOnly - end Ix.Kernel diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 26d3ae371..e562bfd7c 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -626,6 +626,450 @@ private def piExposureRoots : Array RootAllowance := #[ { root := ``SynthesisInference.beta_public_whnf_sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } ] +private def localScopeFrameRoots : Array Lean.Name := #[ + ``LocalContext.Equiv.refl, ``LocalContext.Equiv.symm, ``LocalContext.Equiv.trans, + ``LocalContext.Equiv.size, ``LocalContext.Equiv.find?, ``LocalContext.Equiv.wf, + ``LocalContext.Extension.trans, ``LocalContext.Extension.size_le, + ``PreservesLocalExtension.pure, ``PreservesLocalExtension.throw, + ``PreservesLocalExtension.bind, ``PreservesLocalExtension.runIntern, + ``PreservesLocalExtension.withInferOnly +] + +private def localStateFrameRoots : Array Lean.Name := #[ + ``TcM.InternOnly.pure, + ``TcM.InternOnly.throw, + ``TcM.InternOnly.bind, + ``TcM.InternOnly.runIntern, + ``TcM.InternOnly.ofExcept, + ``TcM.InternOnly.map, + ``LocalContext.IdsBelow.mono, + ``LocalContext.IdsBelow.equiv, + ``LocalContext.IdsBelow.fresh, + ``LocalStateFrame.refl, + ``LocalStateFrame.trans, + ``LocalStateFrame.invariant, + ``LocalStateExtension.refl, + ``LocalStateExtension.trans, + ``LocalStateExtension.of_frame, + ``FramesLocalState.preserves, + ``FramesLocalState.pure, + ``FramesLocalState.throw, + ``FramesLocalState.runIntern, + ``FramesLocalState.of_internOnly, + ``FramesLocalState.bind, + ``PreservesLocalState.bind, + ``FramesLocalState.get, + ``FramesLocalState.modify, + ``FramesLocalState.intern, + ``FramesLocalState.withInferOnly, + ``FramesLocalState.cacheInferResult, + ``FramesLocalState.isEagerReduce, + ``FramesLocalState.prims, + ``FramesLocalState.lazyIngressAddr, + ``FramesLocalState.tryGetConst, + ``FramesLocalState.getConst +] + +private def localStateMapRoots : Array Lean.Name := #[ + ``LocalContext.IdsBelow.empty, + ``LocalContext.IdsBelow.push +] + +private def localStateWalkerRoots : Array Lean.Name := #[ + ``LocalStateExtension.restore, + ``PreservesLocalState.withLctxScope, + ``PreservesLocalState.openLet, + ``PreservesLocalState.openBinder, + ``FramesLocalState.ctxAddrForLbr, + ``FramesLocalState.inferKey, + ``FramesLocalState.lookupVar +] + +private def localStateOperationalRoots : Array Lean.Name := #[ + ``TcM.InternOnly.instantiateUnivParams, + ``FramesLocalState.instantiateUnivParams, + ``FramesLocalState.ensureSortWhnf, + ``FramesLocalState.ensureSortDirect, + ``FramesLocalState.ensureForallWhnf, + ``FramesLocalState.ensureForallDirect, + ``inferUncached_framesLocalState, + ``FramesLocalState.inferWith, + ``infer_framesLocalState, + ``FramesLocalState.peelProjForall, ``FramesLocalState.instantiateProjParamStep, + ``FramesLocalState.instantiateProjParams, ``FramesLocalState.inductiveAppBinderStep, + ``FramesLocalState.inductiveAppBinders, ``FramesLocalState.inductiveAppResultIsProp, + ``FramesLocalState.inductiveAppIsProp, ``FramesLocalState.inferProjFieldStep, + ``FramesLocalState.inferProjFieldsLoopStep, ``FramesLocalState.inferProjFields, + ``FramesLocalState.inferProj, ``infer_framesLocalState_of_whnf +] + +private def ingressFrameRoots : Array Lean.Name := #[ + ``KEnv.IngressFrame.refl, + ``KEnv.IngressFrame.trans, + ``KEnv.IngressFrame.counter, + ``KEnv.IngressFrame.insert, + ``KEnv.IngressFrame.insertBlock, + ``KEnv.IngressFrame.foldl, + ``KEnv.IngressFrame.insertEntriesState, + ``KEnv.IngressFrame.insertMutsEntriesState, + ``IngressM.FramesState.pure, + ``IngressM.FramesState.throw, + ``IngressM.FramesState.get, + ``IngressM.FramesState.modifyGet, + ``IngressM.FramesState.liftExcept, + ``IngressM.FramesState.bind, + ``IngressM.FramesState.internE, + ``IngressM.FramesState.internU, + ``IngressM.FramesState.forInList, + ``IngressM.FramesState.forInArray, + ``IngressM.FramesState.forInList', + ``ConvM.FramesState.pure, + ``ConvM.FramesState.throw, + ``ConvM.FramesState.get, + ``ConvM.FramesState.modify, + ``ConvM.FramesState.bind, + ``ConvM.FramesState.lift, + ``ConvM.FramesState.forInList, + ``ConvM.FramesState.forInArray +] + +private def ingressMapRoots : Array Lean.Name := #[ + ``IngressM.FramesState.forInRange, + ``IngressM.FramesState.forInRange', + ``IngressM.FramesState.guardReserved, + ``IngressM.FramesState.insertStandaloneEntries, + ``IngressM.FramesState.insertMutsEntries, + ``ConvM.FramesState.forInRange +] + +private def ingressLevelRoots : Array Lean.Name := #[ + ``IngressM.FramesState.ingressUnivTree, + ``ConvM.FramesState.ingressUnivIdx, + ``ConvM.FramesState.ingressUnivArgs +] + +private def ingressOperationalRoots : Array Lean.Name := #[ + ``IngressM.FramesState.ingressDefnAnon, + ``IngressM.FramesState.ingressRecursorAnon, + ``IngressM.FramesState.ingressAnonInductive, + ``IngressM.FramesState.ingressAnonStandalone, + ``IngressM.FramesState.prepareAnonBlock, + ``IngressM.FramesState.ingressAnonBlockWithTrace, + ``IngressM.FramesState.ingressAnonBlock, + ``IngressM.FramesState.ingressAnonAddrShallow, + ``ConvM.FramesState.ingressExpr, + ``LoaderCounterMonotone.ingressAnonAddrShallow +] + +private def recursiveStateFrameRoots : Array Lean.Name := #[ + ``FramesLocalState.ofWF, + ``FramesLocalState.tryCatch, + ``FramesLocalState.tryFinally, + ``FramesLocalState.map, + ``FramesLocalState.modifyGet, + ``FramesLocalState.tick, + ``FramesLocalState.stepTrace, + ``FramesLocalState.bumpStats, + ``FramesLocalState.isLetVar, + ``FramesLocalState.tryGetBlock, + ``FramesLocalState.runBounded, + ``FramesLocalState.forInList, + ``FramesLocalState.forInArray, + ``FramesLocalState.withCheapRecursionDepth, + ``FramesLocalState.whnfRec, + ``FramesLocalState.whnfModeRec, + ``FramesLocalState.whnfCoreFlagsRec, + ``FramesLocalState.inferOnlyRec, + ``FramesLocalState.tryOptional, + ``FramesLocalState.pureRec, + ``FramesLocalState.throwRec, + ``FramesLocalState.throwExceptRec, + ``FramesLocalState.getRec, + ``FramesLocalState.liftRec, + ``FramesLocalState.liftSelf, + ``FramesLocalState.mapRec, + ``FramesLocalState.bindRead, + ``FramesLocalState.tryProbe, + ``FramesLocalState.bindRec, + ``FramesLocalState.bindTcM, + ``FramesLocalState.tryFinallyRec, + ``FramesLocalState.tryCatchRec, + ``FramesLocalState.tryCatchExceptRec, + ``FramesLocalState.modifyRec, + ``FramesLocalState.forInListTcM, + ``FramesLocalState.isNatBinArithAddr, + ``FramesLocalState.isNatBinPredAddr, + ``FramesLocalState.boolLitValue, + ``FramesLocalState.isNatStuckRecursorAddr, + ``FramesLocalState.saveDepth, + ``FramesLocalState.enterDispatch, + ``FramesLocalState.exitDispatch, + ``FramesLocalState.callIsDefEq, + ``FramesLocalState.isNatLiteralRecursorApp, + ``FramesLocalState.natRecLiteralParts, + ``FramesLocalState.isStuckNatPredicateProbe, + ``FramesLocalState.discoverBlockInductives, + ``FramesLocalState.cacheIsRec, + ``FramesLocalState.eraseCachedIsRec, + ``FramesLocalState.isNatSuccSpine, + ``FramesLocalState.recordNatSuccStuck, + ``FramesLocalState.whnfNatReducerArg, + ``FramesLocalState.inferDecidableProp, + ``FramesLocalState.whnfWithNatSuccModeMissCharge, + ``FramesLocalState.isDefEqCall, + ``FramesLocalState.inferOnlyCall, + ``FramesLocalState.withEquiv, + ``FramesLocalState.allDefEqSpineArgsList, + ``FramesLocalState.isNatLike, + ``FramesLocalState.isNatZero, + ``FramesLocalState.isBoolTrue, + ``FramesLocalState.boolTrueReductionAllowed, + ``FramesLocalState.isDelta, + ``FramesLocalState.classifyDeltaHead, + ``FramesLocalState.isRegular, + ``FramesLocalState.defRankId, + ``FramesLocalState.rankDeltaHead, + ``FramesLocalState.allDefEqSpineArgs, + ``FramesLocalState.tryDefEqWhnfApp, + ``FramesLocalState.tryDefEqApp +] + +private def recursiveStateMapRoots : Array Lean.Name := #[ + ``FramesLocalState.forInRange, + ``FramesLocalState.pushLocal, + ``FramesLocalState.pushLet, + ``FramesLocalState.popLocal, + ``FramesLocalState.restoreDepthGo, + ``FramesLocalState.restoreDepth, + ``FramesLocalState.peelMajorForalls, + ``FramesLocalState.scanMajorInductiveStep, + ``FramesLocalState.scanMajorInductive, + ``FramesLocalState.getMajorInductiveId, + ``FramesLocalState.computeIsRecParamStepAfterWhnf, + ``FramesLocalState.computeIsRecParamStep, + ``FramesLocalState.computeIsRecFieldStepAfterWhnf, + ``FramesLocalState.computeIsRecFieldStep, + ``FramesLocalState.computeIsRecCtor, + ``FramesLocalState.computeIsRec, + ``FramesLocalState.computedIsRecClassify, + ``FramesLocalState.computedIsRecMiss, + ``FramesLocalState.computedIsRec, + ``FramesLocalState.isStructLike, + ``FramesLocalState.whnfWithNatSuccModePrefix, + ``FramesLocalState.trySameHeadSpine, + ``FramesLocalState.trySameHeadSpineSpeculative, + ``FramesLocalState.isUnitLikeInductive +] + +private def recursiveStateExprRoots : Array Lean.Name := #[ + ``FramesLocalState.lookupLetVal, + ``FramesLocalState.whnfKey, + ``FramesLocalState.mkNatSucc, + ``FramesLocalState.mkNatAdd, + ``FramesLocalState.natToConstructor, + ``FramesLocalState.evalNatOffsetLiteralFuel, + ``FramesLocalState.natOffsetFuel, + ``FramesLocalState.finishAppResult, + ``FramesLocalState.strLitListToConstructor, + ``FramesLocalState.natOffset, + ``FramesLocalState.natOffsetOrZero, + ``FramesLocalState.evalNatOffsetLiteral, + ``FramesLocalState.natOffsetDecompose, + ``FramesLocalState.natOffsetRebuild, + ``FramesLocalState.strLitToConstructor, + ``FramesLocalState.internIntLit, + ``FramesLocalState.applyIotaArg, + ``FramesLocalState.applyIotaArgs, + ``FramesLocalState.isTransientNatLiteralWork, + ``FramesLocalState.cleanupNatOffsetMajor, + ``FramesLocalState.projectDecidableFinValMinor, + ``FramesLocalState.tryReduceFinValDecidableRec, + ``FramesLocalState.tryReduceProjectionDefinition, + ``FramesLocalState.bitvecOfNatArgs, + ``FramesLocalState.charOfNatExpr, + ``FramesLocalState.tryReduceStringLiteral, + ``FramesLocalState.tryReduceString, + ``FramesLocalState.tryProjReduceTail, + ``FramesLocalState.tryProjPrepare, + ``FramesLocalState.tryProjReduce, + ``FramesLocalState.tryProjAppReduce, + ``FramesLocalState.tryProjAppReduceFinished, + ``FramesLocalState.finishStructEtaFields, + ``FramesLocalState.finishStructEtaResult, + ``FramesLocalState.verifyKSynthCandidate, + ``FramesLocalState.selectKSynthCandidate, + ``FramesLocalState.tryReduceNatSuccPeelMiss, + ``FramesLocalState.tryReduceNatSuccPeelAfterKey, + ``FramesLocalState.tryReduceNatSuccPeel, + ``FramesLocalState.tryReduceNatSuccAfterWhnf, + ``FramesLocalState.isNatSuccIhStep, + ``FramesLocalState.tryReduceNatSuccLinearRec, + ``FramesLocalState.tryReduceNatSuccIterStep, + ``FramesLocalState.tryReduceNatSuccIter, + ``FramesLocalState.tryReduceNatPredicate, + ``FramesLocalState.tryReduceNatWithSuccMode, + ``FramesLocalState.tryReduceNat, + ``FramesLocalState.tryNatOffsetStuck, + ``FramesLocalState.buildNatDecidableTrue, + ``FramesLocalState.buildNatDecidableFalse, + ``FramesLocalState.tryNormalizeIntDecidable, + ``FramesLocalState.tryQuotReduce, + ``FramesLocalState.tryEvalNatValueForPredFuel, + ``FramesLocalState.tryEvalNatValueForPred, + ``FramesLocalState.tryReduceBitvecToNat, + ``FramesLocalState.bitvecToNatExpr, + ``FramesLocalState.tryReduceBitvecUlt, + ``FramesLocalState.tryReduceBitvecLtProp, + ``FramesLocalState.tryReduceBitvec, + ``openLetWithFV_eq, + ``PreservesLocalState.openLetWithFV, + ``FramesLocalState.defEqCtxKey, + ``FramesLocalState.quickBinder, + ``FramesLocalState.tryDefEqWhnfLet, + ``FramesLocalState.tryEtaStructFields, + ``FramesLocalState.natSuccOf, + ``FramesLocalState.quickDefEq, + ``FramesLocalState.finishDefEqLazyDeltaStep, + ``FramesLocalState.trySameHeadSpineCached, + ``FramesLocalState.tryDefEqWhnfStructural, + ``FramesLocalState.isDefEqNatAfterLiteral, + ``FramesLocalState.isDefEqNat, + ``FramesLocalState.tryDefEqWhnfNat, + ``FramesLocalState.tryDefEqOffsetAfterCandidates, + ``FramesLocalState.tryDefEqOffsetAfterZeroMiss, + ``FramesLocalState.tryDefEqOffsetAfterLiteral, + ``FramesLocalState.tryDefEqOffset, + ``FramesLocalState.tryStringLitExpansion, + ``FramesLocalState.tryDefEqWhnfStringAfterGuard, + ``FramesLocalState.tryDefEqWhnfString, + ``FramesLocalState.compareEtaExpansion, + ``FramesLocalState.finishLazyDeltaReductionStep +] + +private def recursiveStateOperationalRoots : Array Lean.Name := #[ + ``FramesLocalState.unfoldConstValue, + ``FramesLocalState.tryDeltaUnfold, + ``FramesLocalState.tryReduceNativeMarker, + ``FramesLocalState.applyIotaRule, + ``FramesLocalState.tryApplyIotaCtor, + ``FramesLocalState.finishStructEtaAfterSort, + ``FramesLocalState.tryStructEtaAfterInductive, + ``FramesLocalState.tryStructEtaIota, + ``FramesLocalState.synthCtorWhenK, + ``FramesLocalState.tryIotaCtorOrStructEta, + ``FramesLocalState.tryIotaAfterCleanup, + ``FramesLocalState.tryIotaAfterMajorWhnf, + ``FramesLocalState.tryIotaWithFlags, + ``FramesLocalState.deltaUnfoldOne, + ``FramesLocalState.tryReduceDecidable, + ``FramesLocalState.tryReduceNative, + ``FramesLocalState.whnfCoreWithFlagsStep, + ``FramesLocalState.whnfCoreWithFlagsUncached, + ``FramesLocalState.whnfCoreWithFlagsNonLeaf, + ``FramesLocalState.whnfCoreWithFlags, + ``FramesLocalState.whnfCore, + ``FramesLocalState.whnfCoreForDefEq, + ``FramesLocalState.whnfNoDeltaReducersStep, + ``FramesLocalState.whnfNoDeltaImplStep, + ``FramesLocalState.whnfNoDeltaImplUncached, + ``FramesLocalState.whnfNoDeltaImplNonLeaf, + ``FramesLocalState.whnfNoDeltaImpl, + ``FramesLocalState.whnfNoDelta, + ``FramesLocalState.whnfNoDeltaForDefEq, + ``FramesLocalState.whnfWithNatSuccModeStep, + ``FramesLocalState.whnfWithNatSuccModeUncached, + ``FramesLocalState.whnfWithNatSuccModeNonLeaf, + ``FramesLocalState.whnfWithNatSuccMode, + ``FramesLocalState.whnf, + ``FramesLocalState.etaExpansionBaseLoop, + ``FramesLocalState.whnfIsBoolTrue, + ``FramesLocalState.defEqLazyDeltaStepAfterSameHeadMiss, + ``FramesLocalState.defEqLazyDeltaStepWithLeftDelta, + ``FramesLocalState.defEqLazyDeltaStepWithRightDelta, + ``FramesLocalState.defEqLazyDeltaStepWithEqualRank, + ``FramesLocalState.defEqLazyDeltaStepAfterProjectionMiss, + ``FramesLocalState.classifyPropTypeUncached, + ``FramesLocalState.isPropType, + ``FramesLocalState.tryProofIrrel, + ``FramesLocalState.isDefEqWhnfAfterUnit, + ``FramesLocalState.tryDefEqUnit, + ``FramesLocalState.isDefEqWhnfAfterStructEta, + ``FramesLocalState.tryEtaExpansionAfterGuard, + ``FramesLocalState.tryEtaExpansion, + ``FramesLocalState.tryDefEqWhnfEtaAfterGuard, + ``FramesLocalState.tryDefEqWhnfEta, + ``FramesLocalState.normalizeEtaStructSource, + ``FramesLocalState.etaExpansionBase, + ``FramesLocalState.tryEtaStructAfterTypes, + ``FramesLocalState.tryEtaStructAfterConstructor, + ``FramesLocalState.tryEtaStructAfterNormalization, + ``FramesLocalState.tryEtaStruct, + ``FramesLocalState.tryDefEqWhnfStructEta, + ``FramesLocalState.isDefEqWhnfAfterString, + ``FramesLocalState.isDefEqWhnfAfterEta, + ``FramesLocalState.isDefEqWhnfAfterNat, + ``FramesLocalState.isDefEqWhnfAfterStructural, + ``FramesLocalState.isDefEqWhnf, + ``FramesLocalState.etaExpansionBaseAfterValue, + ``FramesLocalState.etaExpansionBaseAfterProjection, + ``FramesLocalState.lazyDeltaReductionStepWithLeftDelta, + ``FramesLocalState.lazyDeltaReductionStepWithRightDelta, + ``FramesLocalState.lazyDeltaReductionStepAfterSameHeadMiss, + ``FramesLocalState.lazyDeltaReductionStepWithEqualRank, + ``FramesLocalState.lazyDeltaReductionStepWithBothDelta, + ``FramesLocalState.tryUnfoldProjApp, + ``FramesLocalState.defEqLazyDeltaStepAfterDeltaClassification, + ``FramesLocalState.defEqLazyDeltaStepAfterAcceleratorMiss, + ``FramesLocalState.defEqLazyDeltaStepAfterNatMiss, + ``FramesLocalState.defEqLazyDeltaStepAfterOffsetMiss, + ``FramesLocalState.defEqLazyDeltaStep, + ``FramesLocalState.runDefEqLazyDelta, + ``FramesLocalState.lazyDeltaReductionStepAfterActive, + ``FramesLocalState.lazyDeltaReductionStepAfterClassification, + ``FramesLocalState.lazyDeltaReductionStep, + ``FramesLocalState.lazyDeltaProjReduction, + ``FramesLocalState.tryStructuralCongruence, + ``FramesLocalState.isDefEqAfterLazyDeltaStopped, + ``FramesLocalState.isDefEqInnerAfterProofIrrelevance, + ``FramesLocalState.isDefEqInnerAfterNoDeltaPass, + ``FramesLocalState.isDefEqInnerAfterCorePass, + ``FramesLocalState.isDefEqInnerAfterStringExpansion, + ``FramesLocalState.isDefEqInnerAfterBoolTrue, + ``FramesLocalState.isDefEqInnerAfterFirstBoolGuardMiss, + ``FramesLocalState.isDefEqInnerAfterQuick, + ``FramesLocalState.isDefEqInner, + ``FramesLocalState.isDefEqAfterRootCacheMiss, + ``FramesLocalState.isDefEqAfterDirectCacheMiss, + ``FramesLocalState.isDefEq, + ``MethodsLocalState.methodsN, + ``FramesLocalState.runRec, + ``TcM.whnf_framesLocalState, + ``TcM.whnfCore_framesLocalState, + ``TcM.whnfNoDelta_framesLocalState, + ``TcM.infer_framesLocalState, + ``TcM.isDefEq_framesLocalState, + ``TcM.ensureSort_framesLocalState, + ``TcM.ensureForall_framesLocalState +] + +private def localRestorationRoots : Array Lean.Name := #[ + ``LocalContext.Equiv.truncate, ``LocalContext.truncate_push, + ``LocalContext.truncate_push_le, ``LocalContext.Extension.restore, + ``withLctxScope_restores, ``withLctxScope_error_restores, + ``openLet_extends, ``openBinder_extends, + ``PreservesLocalExtension.inferKey, ``PreservesLocalExtension.withLctxScope, + ``inferKey_total +] + +private def modelLocalStateRoots : Array Lean.Name := #[ + ``infer_methodsN_framesLocalState, ``isDefEq_methodsN_framesLocalState, + ``infer_localReading, ``LetInferenceTrace.domainFrame, + ``LetInferenceTrace.valueFrame, ``LetInferenceTrace.openingFrame, + ``LetInferenceTrace.domainContext, ``LetInferenceTrace.openingContext, + ``LetInferenceTrace.restores, ``LetInferenceCheck.keyedValid, + ``LetInferenceCheck.absent +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -778,6 +1222,30 @@ def roots : Array RootAllowance := #[ }) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots ++ exposedOriginRoots ++ repeatedBetaRoots ++ betaTraceRoots ++ hereditaryBetaRoots ++ piExposureRoots ++ cacheTransportRoots).map (fun allowance => { allowance with forbiddenDependencies := forbiddenProduction }) + ++ (localScopeFrameRoots ++ localStateFrameRoots ++ recursiveStateFrameRoots ++ ingressFrameRoots ++ + #[``LocalContextReading.congr, ``FramesLocalState.ok, ``FramesLocalState.error, + ``IngressM.FramesState.runIntern, ``LocalStateInvariant.freshReading]).map (fun root => { + root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction }) + ++ (localStateMapRoots ++ recursiveStateMapRoots ++ ingressMapRoots ++ + #[``LocalContext.Equiv.push]).map (fun root => { + root, standardAxioms := standard, forbiddenDependencies := forbiddenProduction }) + ++ (localStateWalkerRoots ++ recursiveStateExprRoots ++ localRestorationRoots).map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative], + forbiddenDependencies := forbiddenProduction }) + ++ ingressLevelRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[levelNative], + forbiddenDependencies := forbiddenProduction }) + ++ (localStateOperationalRoots ++ recursiveStateOperationalRoots ++ ingressOperationalRoots ++ + modelLocalStateRoots ++ #[``infer_localContext]).map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], + forbiddenDependencies := forbiddenProduction }) + ++ #[ + { root := ``LocalStateInvariant.newLazyAnon, standardAxioms := standard, + nativeAxioms := #[expressionNative, levelNative, nameNative], + forbiddenDependencies := forbiddenProduction }, + { root := ``localIndex?_of_mem, standardAxioms := #[``propext], + forbiddenDependencies := forbiddenProduction } + ] ++ #[{ root := ``extend_atomic_definition, standardAxioms := standard }] run_cmd Kernel.Verify.Audit.check roots diff --git a/Ix/Kernel/Verify/Consistency/DefEqLocalState.lean b/Ix/Kernel/Verify/Consistency/DefEqLocalState.lean new file mode 100644 index 000000000..52a4b5c18 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/DefEqLocalState.lean @@ -0,0 +1,894 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.WhnfLocalState + +/-! +# Structural local-state preservation for production conversion + +Every direct definitional-equality phase preserves the local declarations, +fresh-variable bound and installed loader on success and error. The binder +and let cases first extend the context with the actual guarded allocation, +then restore it through production scope cleanup. Finite spine and structure +eta workers use induction; recursive method calls use the predecessor table. +Cache and equivalence-manager operations preserve this structural state, +without asserting the semantic correctness of their entries or answers. +-/ + +namespace Ix.Kernel.Consistency + +theorem openLetWithFV_eq (name : Mode.anon.F Name) (type value body : KExpr .anon) + (before : TcState .anon) : + TcM.openLetWithFV name type value body before = + if before.env.nextFVarId.toNat + 1 < UInt64.size then + let fresh : FVarId := ⟨before.env.nextFVarId⟩ + let internedLocal := before.env.intern.internExpr (KExpr.mkFVar fresh name) + let opened := instantiateRev body #[internedLocal.1] internedLocal.2 + .ok (opened.1, internedLocal.1, fresh) {before with + env := {before.env with + nextFVarId := before.env.nextFVarId + 1 + intern := opened.2} + lctx := before.lctx.push fresh (.ldecl name type value)} + else .error (.other "free-variable id space exhausted") before := by + unfold TcM.openLetWithFV + change EStateM.bind TcM.freshFVarId _ before = _ + rw [EStateM.bind, TcM.freshFVarId] + by_cases room : before.env.nextFVarId.toNat + 1 < UInt64.size + · simp only [room, if_true] + rfl + · simp only [room, if_false] + +theorem PreservesLocalState.openLetWithFV + (name : Mode.anon.F Name) (type value body : KExpr .anon) : + PreservesLocalState (TcM.openLetWithFV name type value body) := by + intro before valid + rw [openLetWithFV_eq] + by_cases room : before.env.nextFVarId.toNat + 1 < UInt64.size + · rw [if_pos room] + have increment : (before.env.nextFVarId + 1).toNat = before.env.nextFVarId.toNat + 1 := by + rw [UInt64.toNat_add, show (1 : UInt64).toNat = 1 from rfl, Nat.mod_eq_of_lt room] + exact ⟨⟨valid.coherent.push valid.allocated.fresh, valid.allocated.push _ room, valid.loader⟩, + by change before.env.nextFVarId.toNat ≤ (before.env.nextFVarId + 1).toNat + rw [increment]; omega, .push _ (.refl _) valid.allocated.fresh, rfl⟩ + · rw [if_neg room]; exact .refl valid + +namespace FramesLocalState + +attribute [local local_state_frame] FramesLocalState.ctxAddrForLbr + +attribute [local irreducible] TcM.intern TcM.runIntern + RecM.whnf RecM.whnfCore RecM.whnfNoDelta RecM.whnfCoreForDefEq + RecM.whnfNoDeltaForDefEq RecM.strLitToConstructor + +@[local_state_frame] theorem isDefEqCall {methods : Methods .anon} + (recursive : MethodsLocalState methods) (left right : KExpr .anon) : + FramesLocalState ((RecM.isDefEqCall left right).run methods) := recursive.isDefEq left right + +@[local_state_frame] theorem inferOnlyCall {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) : + FramesLocalState ((RecM.inferOnlyCall term).run methods) := withInferOnly (recursive.infer term) + +@[local_state_frame] theorem defEqCtxKey (left right : KExpr .anon) : + FramesLocalState (TcM.defEqCtxKey left right) := ctxAddrForLbr _ + +@[local_state_frame] theorem withEquiv (update : EquivManager → α × EquivManager) : + FramesLocalState (TcM.withEquiv (m := .anon) update) := by + unfold TcM.withEquiv + apply bind + · apply modifyGet + exact fun _ => ⟨Nat.le_refl _, .refl _, rfl⟩ + · intro manager + local_state + +@[local_state_frame] theorem quickBinder {methods : Methods .anon} + (recursive : MethodsLocalState methods) (name : Mode.anon.F Name) + (bi : Mode.anon.F Lean.BinderInfo) (type1 body1 type2 body2 : KExpr .anon) : + FramesLocalState ((RecM.quickBinder name bi type1 body1 type2 body2).run methods) := by + unfold RecM.quickBinder + local_state + apply PreservesLocalState.withLctxScope + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + apply PreservesLocalState.bind (PreservesLocalState.openBinder name bi type1 body1) + rintro ⟨opened, fresh⟩ + apply FramesLocalState.preserves + local_state + +@[local_state_frame] theorem tryDefEqWhnfLet {methods : Methods .anon} + (recursive : MethodsLocalState methods) (name : Mode.anon.F Name) + (type1 value1 body1 type2 value2 body2 : KExpr .anon) : + FramesLocalState ((RecM.tryDefEqWhnfLet name type1 value1 body1 type2 value2 body2).run methods) := by + unfold RecM.tryDefEqWhnfLet + local_state + apply PreservesLocalState.withLctxScope + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + apply PreservesLocalState.bind (PreservesLocalState.openLetWithFV name type1 value1 body1) + rintro ⟨opened, fv, fresh⟩ + apply FramesLocalState.preserves + local_state + +@[local_state_frame] theorem allDefEqSpineArgsList {methods : Methods .anon} + (recursive : MethodsLocalState methods) (pairs : List (KExpr .anon × KExpr .anon)) : + FramesLocalState ((RecM.allDefEqSpineArgsList pairs).run methods) := by + induction pairs with + | nil => exact pure _ + | cons pair pairs ih => + rcases pair with ⟨left, right⟩ + rw [RecM.allDefEqSpineArgsList] + local_state + +@[local_state_frame] theorem tryEtaStructFields {methods : Methods .anon} + (recursive : MethodsLocalState methods) (ind : KId .anon) (params : Nat) + (term : KExpr .anon) (args : Array (KExpr .anon)) (fuel field : Nat) : + FramesLocalState ((RecM.tryEtaStructFields ind params term args fuel field).run methods) := by + induction fuel generalizing field with + | zero => exact pure _ + | succ fuel ih => + rw [RecM.tryEtaStructFields] + local_state + +@[local_state_frame] theorem etaExpansionBaseLoop {methods : Methods .anon} + (recursive : MethodsLocalState methods) (ind : KId .anon) (params : Nat) + (args : Array (KExpr .anon)) (fuel field : Nat) (base : Option (KExpr .anon)) : + FramesLocalState ((RecM.etaExpansionBaseLoop ind params args fuel field base).run methods) := by + induction fuel generalizing field base with + | zero => + rw [RecM.etaExpansionBaseLoop] + exact pure _ + | succ fuel ih => + rw [RecM.etaExpansionBaseLoop] + simp only [RecM.etaExpansionBaseAfterProjection] + local_state + all_goals unfold RecM.etaExpansionBaseAfterValue + all_goals local_state + + +@[local_state_frame] theorem isNatLike {methods : Methods .anon} + (e : KExpr .anon) : + FramesLocalState ((RecM.isNatLike e).run methods) := by + unfold RecM.isNatLike + local_state + +attribute [local irreducible] RecM.isNatLike + +@[local_state_frame] theorem isNatZero {methods : Methods .anon} + (e : KExpr .anon) : + FramesLocalState ((RecM.isNatZero e).run methods) := by + unfold RecM.isNatZero + local_state + +attribute [local irreducible] RecM.isNatZero + +@[local_state_frame] theorem natSuccOf {methods : Methods .anon} + (e : KExpr .anon) : + FramesLocalState ((RecM.natSuccOf e).run methods) := by + unfold RecM.natSuccOf + local_state + +attribute [local irreducible] RecM.natSuccOf + +@[local_state_frame] theorem isBoolTrue {methods : Methods .anon} + (e : KExpr .anon) : + FramesLocalState ((RecM.isBoolTrue e).run methods) := by + unfold RecM.isBoolTrue + local_state + +attribute [local irreducible] RecM.isBoolTrue + +@[local_state_frame] theorem boolTrueReductionAllowed {methods : Methods .anon} + (e : KExpr .anon) : + FramesLocalState ((RecM.boolTrueReductionAllowed e).run methods) := by + unfold RecM.boolTrueReductionAllowed + local_state + +attribute [local irreducible] RecM.boolTrueReductionAllowed + +@[local_state_frame] theorem whnfIsBoolTrue {methods : Methods .anon} + (recursive : MethodsLocalState methods) (e : KExpr .anon) : + FramesLocalState ((RecM.whnfIsBoolTrue e).run methods) := by + unfold RecM.whnfIsBoolTrue + local_state + +attribute [local irreducible] RecM.whnfIsBoolTrue + +@[local_state_frame] theorem isDelta {methods : Methods .anon} + (id : KId .anon) : + FramesLocalState ((RecM.isDelta id).run methods) := by + unfold RecM.isDelta + local_state + +attribute [local irreducible] RecM.isDelta + +@[local_state_frame] theorem classifyDeltaHead {methods : Methods .anon} + (e : KExpr .anon) : + FramesLocalState ((RecM.classifyDeltaHead e).run methods) := by + unfold RecM.classifyDeltaHead + local_state + +attribute [local irreducible] RecM.classifyDeltaHead + +@[local_state_frame] theorem isRegular {methods : Methods .anon} + (id : KId .anon) : + FramesLocalState ((RecM.isRegular id).run methods) := by + unfold RecM.isRegular + local_state + +attribute [local irreducible] RecM.isRegular + +@[local_state_frame] theorem defRankId {methods : Methods .anon} + (id : KId .anon) : + FramesLocalState ((RecM.defRankId id).run methods) := by + unfold RecM.defRankId + local_state + +attribute [local irreducible] RecM.defRankId + +@[local_state_frame] theorem rankDeltaHead {methods : Methods .anon} + (head : Option (KId .anon)) : + FramesLocalState ((RecM.rankDeltaHead head).run methods) := by + unfold RecM.rankDeltaHead + local_state + +attribute [local irreducible] RecM.rankDeltaHead + +@[local_state_frame] theorem quickDefEq {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.quickDefEq a b).run methods) := by + unfold RecM.quickDefEq + local_state + +attribute [local irreducible] RecM.quickDefEq + +@[local_state_frame] theorem finishDefEqLazyDeltaStep {methods : Methods .anon} + (recursive : MethodsLocalState methods) (wa wb : KExpr .anon) : + FramesLocalState ((RecM.finishDefEqLazyDeltaStep wa wb).run methods) := by + unfold RecM.finishDefEqLazyDeltaStep + local_state + +attribute [local irreducible] RecM.finishDefEqLazyDeltaStep + +@[local_state_frame] theorem defEqLazyDeltaStepAfterSameHeadMiss {methods : Methods .anon} + (recursive : MethodsLocalState methods) (wa0 wb0 : KExpr .anon) : + FramesLocalState ((RecM.defEqLazyDeltaStepAfterSameHeadMiss wa0 wb0).run methods) := by + unfold RecM.defEqLazyDeltaStepAfterSameHeadMiss + local_state + +attribute [local irreducible] RecM.defEqLazyDeltaStepAfterSameHeadMiss + +@[local_state_frame] theorem defEqLazyDeltaStepWithLeftDelta {methods : Methods .anon} + (recursive : MethodsLocalState methods) (wa wb : KExpr .anon) : + FramesLocalState ((RecM.defEqLazyDeltaStepWithLeftDelta wa wb).run methods) := by + unfold RecM.defEqLazyDeltaStepWithLeftDelta + local_state + +attribute [local irreducible] RecM.defEqLazyDeltaStepWithLeftDelta + +@[local_state_frame] theorem defEqLazyDeltaStepWithRightDelta {methods : Methods .anon} + (recursive : MethodsLocalState methods) (wa wb : KExpr .anon) : + FramesLocalState ((RecM.defEqLazyDeltaStepWithRightDelta wa wb).run methods) := by + unfold RecM.defEqLazyDeltaStepWithRightDelta + local_state + +attribute [local irreducible] RecM.defEqLazyDeltaStepWithRightDelta + +@[local_state_frame] theorem allDefEqSpineArgs {methods : Methods .anon} + (recursive : MethodsLocalState methods) (pairs : Array (KExpr .anon × KExpr .anon)) : + FramesLocalState ((RecM.allDefEqSpineArgs pairs).run methods) := by + unfold RecM.allDefEqSpineArgs + local_state + +attribute [local irreducible] RecM.allDefEqSpineArgs + +@[local_state_frame] theorem trySameHeadSpine {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.trySameHeadSpine a b).run methods) := by + unfold RecM.trySameHeadSpine + local_state + +attribute [local irreducible] RecM.trySameHeadSpine + +@[local_state_frame] theorem trySameHeadSpineSpeculative {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.trySameHeadSpineSpeculative a b).run methods) := by + unfold RecM.trySameHeadSpineSpeculative + local_state + +attribute [local irreducible] RecM.trySameHeadSpineSpeculative + +@[local_state_frame] theorem trySameHeadSpineCached {methods : Methods .anon} + (recursive : MethodsLocalState methods) (speculative : Bool) (left right : KExpr .anon) : + FramesLocalState ((RecM.trySameHeadSpineCached speculative left right).run methods) := by + unfold RecM.trySameHeadSpineCached + local_state + +attribute [local irreducible] RecM.trySameHeadSpineCached + +@[local_state_frame] theorem defEqLazyDeltaStepWithEqualRank {methods : Methods .anon} + (recursive : MethodsLocalState methods) (wa0 wb0 : KExpr .anon) + (aHead bHead : Option (KId .anon)) : + FramesLocalState ((RecM.defEqLazyDeltaStepWithEqualRank wa0 wb0 aHead bHead).run methods) := by + unfold RecM.defEqLazyDeltaStepWithEqualRank + local_state + +attribute [local irreducible] RecM.defEqLazyDeltaStepWithEqualRank + +@[local_state_frame] theorem defEqLazyDeltaStepAfterProjectionMiss {methods : Methods .anon} + (recursive : MethodsLocalState methods) (wa0 wb0 : KExpr .anon) + (aHead bHead : Option (KId .anon)) (aDelta bDelta : Bool) : + FramesLocalState ((RecM.defEqLazyDeltaStepAfterProjectionMiss wa0 wb0 aHead bHead aDelta bDelta).run methods) := by + unfold RecM.defEqLazyDeltaStepAfterProjectionMiss + local_state + +attribute [local irreducible] RecM.defEqLazyDeltaStepAfterProjectionMiss + +@[local_state_frame] theorem tryDefEqWhnfApp {methods : Methods .anon} + (recursive : MethodsLocalState methods) (f1 a1 f2 a2 : KExpr .anon) : + FramesLocalState ((RecM.tryDefEqWhnfApp f1 a1 f2 a2).run methods) := by + unfold RecM.tryDefEqWhnfApp + local_state + +attribute [local irreducible] RecM.tryDefEqWhnfApp + +@[local_state_frame] theorem tryDefEqWhnfStructural {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.tryDefEqWhnfStructural a b).run methods) := by + unfold RecM.tryDefEqWhnfStructural + local_state + +attribute [local irreducible] RecM.tryDefEqWhnfStructural + +@[local_state_frame] theorem classifyPropTypeUncached {methods : Methods .anon} + (recursive : MethodsLocalState methods) (ty : KExpr .anon) : + FramesLocalState ((RecM.classifyPropTypeUncached ty).run methods) := by + unfold RecM.classifyPropTypeUncached + local_state + +attribute [local irreducible] RecM.classifyPropTypeUncached + +@[local_state_frame] theorem isPropType {methods : Methods .anon} + (recursive : MethodsLocalState methods) (ty : KExpr .anon) : + FramesLocalState ((RecM.isPropType ty).run methods) := by + unfold RecM.isPropType + local_state + +attribute [local irreducible] RecM.isPropType + +@[local_state_frame] theorem tryProofIrrel {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.tryProofIrrel a b).run methods) := by + unfold RecM.tryProofIrrel + local_state + +attribute [local irreducible] RecM.tryProofIrrel + +@[local_state_frame] theorem isDefEqWhnfAfterUnit {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.isDefEqWhnfAfterUnit a b).run methods) := by + unfold RecM.isDefEqWhnfAfterUnit + local_state + +attribute [local irreducible] RecM.isDefEqWhnfAfterUnit + +@[local_state_frame] theorem isUnitLikeInductive {methods : Methods .anon} + (indId : KId .anon) : + FramesLocalState ((RecM.isUnitLikeInductive indId).run methods) := by + unfold RecM.isUnitLikeInductive + local_state + +attribute [local irreducible] RecM.isUnitLikeInductive + +@[local_state_frame] theorem tryDefEqUnit {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.tryDefEqUnit a b).run methods) := by + unfold RecM.tryDefEqUnit + local_state + +attribute [local irreducible] RecM.tryDefEqUnit + +@[local_state_frame] theorem isDefEqWhnfAfterStructEta {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.isDefEqWhnfAfterStructEta a b).run methods) := by + unfold RecM.isDefEqWhnfAfterStructEta + local_state + +attribute [local irreducible] RecM.isDefEqWhnfAfterStructEta + +@[local_state_frame] theorem isDefEqNatAfterLiteral {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.isDefEqNatAfterLiteral a b).run methods) := by + unfold RecM.isDefEqNatAfterLiteral + local_state + +attribute [local irreducible] RecM.isDefEqNatAfterLiteral + +@[local_state_frame] theorem isDefEqNat {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.isDefEqNat a b).run methods) := by + unfold RecM.isDefEqNat + local_state + +attribute [local irreducible] RecM.isDefEqNat + +@[local_state_frame] theorem tryDefEqWhnfNat {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.tryDefEqWhnfNat a b).run methods) := by + unfold RecM.tryDefEqWhnfNat + local_state + +attribute [local irreducible] RecM.tryDefEqWhnfNat + +@[local_state_frame] theorem tryDefEqOffsetAfterCandidates {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.tryDefEqOffsetAfterCandidates a b).run methods) := by + unfold RecM.tryDefEqOffsetAfterCandidates + local_state + +attribute [local irreducible] RecM.tryDefEqOffsetAfterCandidates + +@[local_state_frame] theorem tryDefEqOffsetAfterZeroMiss {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.tryDefEqOffsetAfterZeroMiss a b).run methods) := by + unfold RecM.tryDefEqOffsetAfterZeroMiss + local_state + +attribute [local irreducible] RecM.tryDefEqOffsetAfterZeroMiss + +@[local_state_frame] theorem tryDefEqOffsetAfterLiteral {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.tryDefEqOffsetAfterLiteral a b).run methods) := by + unfold RecM.tryDefEqOffsetAfterLiteral + local_state + +attribute [local irreducible] RecM.tryDefEqOffsetAfterLiteral + +@[local_state_frame] theorem tryDefEqOffset {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.tryDefEqOffset a b).run methods) := by + unfold RecM.tryDefEqOffset + local_state + +attribute [local irreducible] RecM.tryDefEqOffset + +@[local_state_frame] theorem tryStringLitExpansion {methods : Methods .anon} + (recursive : MethodsLocalState methods) (t s : KExpr .anon) : + FramesLocalState ((RecM.tryStringLitExpansion t s).run methods) := by + unfold RecM.tryStringLitExpansion + local_state + +attribute [local irreducible] RecM.tryStringLitExpansion + +@[local_state_frame] theorem tryDefEqWhnfStringAfterGuard {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.tryDefEqWhnfStringAfterGuard a b).run methods) := by + unfold RecM.tryDefEqWhnfStringAfterGuard + local_state + +attribute [local irreducible] RecM.tryDefEqWhnfStringAfterGuard + +@[local_state_frame] theorem tryDefEqWhnfString {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.tryDefEqWhnfString a b).run methods) := by + unfold RecM.tryDefEqWhnfString + local_state + +attribute [local irreducible] RecM.tryDefEqWhnfString + +@[local_state_frame] theorem compareEtaExpansion {methods : Methods .anon} + (recursive : MethodsLocalState methods) (t s : KExpr .anon) (name : Mode.anon.F Name) + (bi : Mode.anon.F Lean.BinderInfo) (ty : KExpr .anon) : + FramesLocalState ((RecM.compareEtaExpansion t s name bi ty).run methods) := by + unfold RecM.compareEtaExpansion + local_state + +attribute [local irreducible] RecM.compareEtaExpansion + +@[local_state_frame] theorem tryEtaExpansionAfterGuard {methods : Methods .anon} + (recursive : MethodsLocalState methods) (t s : KExpr .anon) : + FramesLocalState ((RecM.tryEtaExpansionAfterGuard t s).run methods) := by + unfold RecM.tryEtaExpansionAfterGuard + local_state + +attribute [local irreducible] RecM.tryEtaExpansionAfterGuard + +@[local_state_frame] theorem tryEtaExpansion {methods : Methods .anon} + (recursive : MethodsLocalState methods) (t s : KExpr .anon) : + FramesLocalState ((RecM.tryEtaExpansion t s).run methods) := by + unfold RecM.tryEtaExpansion + local_state + +attribute [local irreducible] RecM.tryEtaExpansion + +@[local_state_frame] theorem tryDefEqWhnfEtaAfterGuard {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.tryDefEqWhnfEtaAfterGuard a b).run methods) := by + unfold RecM.tryDefEqWhnfEtaAfterGuard + local_state + +attribute [local irreducible] RecM.tryDefEqWhnfEtaAfterGuard + +@[local_state_frame] theorem tryDefEqWhnfEta {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.tryDefEqWhnfEta a b).run methods) := by + unfold RecM.tryDefEqWhnfEta + local_state + +attribute [local irreducible] RecM.tryDefEqWhnfEta + +@[local_state_frame] theorem normalizeEtaStructSource {methods : Methods .anon} + (recursive : MethodsLocalState methods) (t : KExpr .anon) : + FramesLocalState ((RecM.normalizeEtaStructSource t).run methods) := by + unfold RecM.normalizeEtaStructSource + local_state + +attribute [local irreducible] RecM.normalizeEtaStructSource + +@[local_state_frame] theorem etaExpansionBase {methods : Methods .anon} + (recursive : MethodsLocalState methods) (inductId : KId .anon) (numParams numFields : Nat) + (args : Array (KExpr .anon)) : + FramesLocalState ((RecM.etaExpansionBase inductId numParams numFields args).run methods) := by + unfold RecM.etaExpansionBase + local_state + +attribute [local irreducible] RecM.etaExpansionBase + +@[local_state_frame] theorem tryEtaStructAfterTypes {methods : Methods .anon} + (recursive : MethodsLocalState methods) (inductId : KId .anon) (numParams numFields : Nat) + (tNorm : KExpr .anon) (sArgs : Array (KExpr .anon)) : + FramesLocalState ((RecM.tryEtaStructAfterTypes inductId numParams numFields tNorm sArgs).run methods) := by + unfold RecM.tryEtaStructAfterTypes + local_state + +attribute [local irreducible] RecM.tryEtaStructAfterTypes + +@[local_state_frame] theorem tryEtaStructAfterConstructor {methods : Methods .anon} + (recursive : MethodsLocalState methods) (inductId : KId .anon) + (numParams numFields : Nat) (tNorm s : KExpr .anon) + (sArgs : Array (KExpr .anon)) : + FramesLocalState ((RecM.tryEtaStructAfterConstructor inductId numParams numFields tNorm s sArgs).run methods) := by + unfold RecM.tryEtaStructAfterConstructor + local_state + +attribute [local irreducible] RecM.tryEtaStructAfterConstructor + +@[local_state_frame] theorem tryEtaStructAfterNormalization {methods : Methods .anon} + (recursive : MethodsLocalState methods) (tNorm s : KExpr .anon) : + FramesLocalState ((RecM.tryEtaStructAfterNormalization tNorm s).run methods) := by + unfold RecM.tryEtaStructAfterNormalization + local_state + +attribute [local irreducible] RecM.tryEtaStructAfterNormalization + +@[local_state_frame] theorem tryEtaStruct {methods : Methods .anon} + (recursive : MethodsLocalState methods) (t s : KExpr .anon) : + FramesLocalState ((RecM.tryEtaStruct t s).run methods) := by + unfold RecM.tryEtaStruct + local_state + +attribute [local irreducible] RecM.tryEtaStruct + +@[local_state_frame] theorem tryDefEqWhnfStructEta {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.tryDefEqWhnfStructEta a b).run methods) := by + unfold RecM.tryDefEqWhnfStructEta + local_state + +attribute [local irreducible] RecM.tryDefEqWhnfStructEta + +@[local_state_frame] theorem isDefEqWhnfAfterString {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.isDefEqWhnfAfterString a b).run methods) := by + unfold RecM.isDefEqWhnfAfterString + local_state + +attribute [local irreducible] RecM.isDefEqWhnfAfterString + +@[local_state_frame] theorem isDefEqWhnfAfterEta {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.isDefEqWhnfAfterEta a b).run methods) := by + unfold RecM.isDefEqWhnfAfterEta + local_state + +attribute [local irreducible] RecM.isDefEqWhnfAfterEta + +@[local_state_frame] theorem isDefEqWhnfAfterNat {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.isDefEqWhnfAfterNat a b).run methods) := by + unfold RecM.isDefEqWhnfAfterNat + local_state + +attribute [local irreducible] RecM.isDefEqWhnfAfterNat + +@[local_state_frame] theorem isDefEqWhnfAfterStructural {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.isDefEqWhnfAfterStructural a b).run methods) := by + unfold RecM.isDefEqWhnfAfterStructural + local_state + +attribute [local irreducible] RecM.isDefEqWhnfAfterStructural + +@[local_state_frame] theorem isDefEqWhnf {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.isDefEqWhnf a b).run methods) := by + unfold RecM.isDefEqWhnf + local_state + +attribute [local irreducible] RecM.isDefEqWhnf + +@[local_state_frame] theorem etaExpansionBaseAfterValue {methods : Methods .anon} + (recursive : MethodsLocalState methods) (inductId : KId .anon) (numParams : Nat) + (args : Array (KExpr .anon)) (fuel fieldIdx : Nat) + (base : Option (KExpr .anon)) (value : KExpr .anon) : + FramesLocalState ((RecM.etaExpansionBaseAfterValue inductId numParams args fuel fieldIdx base value).run methods) := by + unfold RecM.etaExpansionBaseAfterValue + local_state + + +@[local_state_frame] theorem etaExpansionBaseAfterProjection {methods : Methods .anon} + (recursive : MethodsLocalState methods) (inductId : KId .anon) (numParams : Nat) + (args : Array (KExpr .anon)) (fuel fieldIdx : Nat) + (base : Option (KExpr .anon)) (value : KExpr .anon) : + FramesLocalState ((RecM.etaExpansionBaseAfterProjection inductId numParams args fuel fieldIdx base value).run methods) := by + unfold RecM.etaExpansionBaseAfterProjection + local_state + + +@[local_state_frame] theorem tryDefEqApp {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.tryDefEqApp a b).run methods) := by + unfold RecM.tryDefEqApp + local_state + +attribute [local irreducible] RecM.tryDefEqApp + +@[local_state_frame] theorem finishLazyDeltaReductionStep {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.finishLazyDeltaReductionStep a b).run methods) := by + unfold RecM.finishLazyDeltaReductionStep + local_state + +attribute [local irreducible] RecM.finishLazyDeltaReductionStep + +@[local_state_frame] theorem lazyDeltaReductionStepWithLeftDelta {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.lazyDeltaReductionStepWithLeftDelta a b).run methods) := by + unfold RecM.lazyDeltaReductionStepWithLeftDelta + local_state + +attribute [local irreducible] RecM.lazyDeltaReductionStepWithLeftDelta + +@[local_state_frame] theorem lazyDeltaReductionStepWithRightDelta {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.lazyDeltaReductionStepWithRightDelta a b).run methods) := by + unfold RecM.lazyDeltaReductionStepWithRightDelta + local_state + +attribute [local irreducible] RecM.lazyDeltaReductionStepWithRightDelta + +@[local_state_frame] theorem lazyDeltaReductionStepAfterSameHeadMiss {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a0 b0 : KExpr .anon) : + FramesLocalState ((RecM.lazyDeltaReductionStepAfterSameHeadMiss a0 b0).run methods) := by + unfold RecM.lazyDeltaReductionStepAfterSameHeadMiss + local_state + +attribute [local irreducible] RecM.lazyDeltaReductionStepAfterSameHeadMiss + +@[local_state_frame] theorem lazyDeltaReductionStepWithEqualRank {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a0 b0 : KExpr .anon) + (aId bId : KId .anon) : + FramesLocalState ((RecM.lazyDeltaReductionStepWithEqualRank a0 b0 aId bId).run methods) := by + unfold RecM.lazyDeltaReductionStepWithEqualRank + local_state + +attribute [local irreducible] RecM.lazyDeltaReductionStepWithEqualRank + +@[local_state_frame] theorem lazyDeltaReductionStepWithBothDelta {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) + (aHead bHead : Option (KId .anon)) : + FramesLocalState ((RecM.lazyDeltaReductionStepWithBothDelta a b aHead bHead).run methods) := by + unfold RecM.lazyDeltaReductionStepWithBothDelta + local_state + +attribute [local irreducible] RecM.lazyDeltaReductionStepWithBothDelta + +@[local_state_frame] theorem tryUnfoldProjApp {methods : Methods .anon} + (recursive : MethodsLocalState methods) (e : KExpr .anon) : + FramesLocalState ((RecM.tryUnfoldProjApp e).run methods) := by + unfold RecM.tryUnfoldProjApp + local_state + +attribute [local irreducible] RecM.tryUnfoldProjApp + +@[local_state_frame] theorem defEqLazyDeltaStepAfterDeltaClassification {methods : Methods .anon} + (recursive : MethodsLocalState methods) (wa0 wb0 : KExpr .anon) + (aHead bHead : Option (KId .anon)) (aDelta bDelta : Bool) : + FramesLocalState ((RecM.defEqLazyDeltaStepAfterDeltaClassification wa0 wb0 aHead bHead aDelta bDelta).run methods) := by + unfold RecM.defEqLazyDeltaStepAfterDeltaClassification + local_state + +attribute [local irreducible] RecM.defEqLazyDeltaStepAfterDeltaClassification + +@[local_state_frame] theorem defEqLazyDeltaStepAfterAcceleratorMiss {methods : Methods .anon} + (recursive : MethodsLocalState methods) (wa0 wb0 : KExpr .anon) : + FramesLocalState ((RecM.defEqLazyDeltaStepAfterAcceleratorMiss wa0 wb0).run methods) := by + unfold RecM.defEqLazyDeltaStepAfterAcceleratorMiss + local_state + +attribute [local irreducible] RecM.defEqLazyDeltaStepAfterAcceleratorMiss + +@[local_state_frame] theorem defEqLazyDeltaStepAfterNatMiss {methods : Methods .anon} + (recursive : MethodsLocalState methods) (wa0 wb0 : KExpr .anon) : + FramesLocalState ((RecM.defEqLazyDeltaStepAfterNatMiss wa0 wb0).run methods) := by + unfold RecM.defEqLazyDeltaStepAfterNatMiss + local_state + +attribute [local irreducible] RecM.defEqLazyDeltaStepAfterNatMiss + +@[local_state_frame] theorem defEqLazyDeltaStepAfterOffsetMiss {methods : Methods .anon} + (recursive : MethodsLocalState methods) (state : KExpr .anon × KExpr .anon) : + FramesLocalState ((RecM.defEqLazyDeltaStepAfterOffsetMiss state).run methods) := by + unfold RecM.defEqLazyDeltaStepAfterOffsetMiss + local_state + +attribute [local irreducible] RecM.defEqLazyDeltaStepAfterOffsetMiss + +@[local_state_frame] theorem defEqLazyDeltaStep {methods : Methods .anon} + (recursive : MethodsLocalState methods) (state : KExpr .anon × KExpr .anon) : + FramesLocalState ((RecM.defEqLazyDeltaStep state).run methods) := by + unfold RecM.defEqLazyDeltaStep + local_state + +attribute [local irreducible] RecM.defEqLazyDeltaStep + +@[local_state_frame] theorem runDefEqLazyDelta {methods : Methods .anon} + (recursive : MethodsLocalState methods) (wa wb : KExpr .anon) : + FramesLocalState ((RecM.runDefEqLazyDelta wa wb).run methods) := by + unfold RecM.runDefEqLazyDelta + local_state + +attribute [local irreducible] RecM.runDefEqLazyDelta + +@[local_state_frame] theorem lazyDeltaReductionStepAfterActive {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a0 b0 : KExpr .anon) + (aHead bHead : Option (KId .anon)) (aDelta bDelta : Bool) : + FramesLocalState ((RecM.lazyDeltaReductionStepAfterActive a0 b0 aHead bHead aDelta bDelta).run methods) := by + unfold RecM.lazyDeltaReductionStepAfterActive + local_state + +attribute [local irreducible] RecM.lazyDeltaReductionStepAfterActive + +@[local_state_frame] theorem lazyDeltaReductionStepAfterClassification {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a0 b0 : KExpr .anon) + (aHead bHead : Option (KId .anon)) (aDelta bDelta : Bool) : + FramesLocalState ((RecM.lazyDeltaReductionStepAfterClassification a0 b0 aHead bHead aDelta bDelta).run methods) := by + unfold RecM.lazyDeltaReductionStepAfterClassification + local_state + +attribute [local irreducible] RecM.lazyDeltaReductionStepAfterClassification + +@[local_state_frame] theorem lazyDeltaReductionStep {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a0 b0 : KExpr .anon) : + FramesLocalState ((RecM.lazyDeltaReductionStep a0 b0).run methods) := by + unfold RecM.lazyDeltaReductionStep + local_state + +attribute [local irreducible] RecM.lazyDeltaReductionStep + +@[local_state_frame] theorem lazyDeltaProjReduction {methods : Methods .anon} + (recursive : MethodsLocalState methods) (structId : KId .anon) (field : UInt64) + (a0 b0 : KExpr .anon) : + FramesLocalState ((RecM.lazyDeltaProjReduction structId field a0 b0).run methods) := by + unfold RecM.lazyDeltaProjReduction + local_state + +attribute [local irreducible] RecM.lazyDeltaProjReduction + +@[local_state_frame] theorem tryStructuralCongruence {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.tryStructuralCongruence a b).run methods) := by + unfold RecM.tryStructuralCongruence + local_state + +attribute [local irreducible] RecM.tryStructuralCongruence + +@[local_state_frame] theorem isDefEqAfterLazyDeltaStopped {methods : Methods .anon} + (recursive : MethodsLocalState methods) (wa wb : KExpr .anon) : + FramesLocalState ((RecM.isDefEqAfterLazyDeltaStopped wa wb).run methods) := by + unfold RecM.isDefEqAfterLazyDeltaStopped + local_state + +attribute [local irreducible] RecM.isDefEqAfterLazyDeltaStopped + +@[local_state_frame] theorem isDefEqInnerAfterProofIrrelevance {methods : Methods .anon} + (recursive : MethodsLocalState methods) (wa wb : KExpr .anon) : + FramesLocalState ((RecM.isDefEqInnerAfterProofIrrelevance wa wb).run methods) := by + unfold RecM.isDefEqInnerAfterProofIrrelevance + local_state + +attribute [local irreducible] RecM.isDefEqInnerAfterProofIrrelevance + +@[local_state_frame] theorem isDefEqInnerAfterNoDeltaPass {methods : Methods .anon} + (recursive : MethodsLocalState methods) (wa wb : KExpr .anon) : + FramesLocalState ((RecM.isDefEqInnerAfterNoDeltaPass wa wb).run methods) := by + unfold RecM.isDefEqInnerAfterNoDeltaPass + local_state + +attribute [local irreducible] RecM.isDefEqInnerAfterNoDeltaPass + +@[local_state_frame] theorem isDefEqInnerAfterCorePass {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.isDefEqInnerAfterCorePass a b).run methods) := by + unfold RecM.isDefEqInnerAfterCorePass + local_state + +attribute [local irreducible] RecM.isDefEqInnerAfterCorePass + +@[local_state_frame] theorem isDefEqInnerAfterStringExpansion {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.isDefEqInnerAfterStringExpansion a b).run methods) := by + unfold RecM.isDefEqInnerAfterStringExpansion + local_state + +attribute [local irreducible] RecM.isDefEqInnerAfterStringExpansion + +@[local_state_frame] theorem isDefEqInnerAfterBoolTrue {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.isDefEqInnerAfterBoolTrue a b).run methods) := by + unfold RecM.isDefEqInnerAfterBoolTrue + local_state + +attribute [local irreducible] RecM.isDefEqInnerAfterBoolTrue + +@[local_state_frame] theorem isDefEqInnerAfterFirstBoolGuardMiss {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.isDefEqInnerAfterFirstBoolGuardMiss a b).run methods) := by + unfold RecM.isDefEqInnerAfterFirstBoolGuardMiss + local_state + +attribute [local irreducible] RecM.isDefEqInnerAfterFirstBoolGuardMiss + +@[local_state_frame] theorem isDefEqInnerAfterQuick {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.isDefEqInnerAfterQuick a b).run methods) := by + unfold RecM.isDefEqInnerAfterQuick + local_state + +attribute [local irreducible] RecM.isDefEqInnerAfterQuick + +@[local_state_frame] theorem isDefEqInner {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.isDefEqInner a b).run methods) := by + unfold RecM.isDefEqInner + local_state + +attribute [local irreducible] RecM.isDefEqInner + +@[local_state_frame] theorem isDefEqAfterRootCacheMiss {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) (aKey bKey : EqKey) + (cacheKey : Address × Address × Address) (cheapMode : Bool) : + FramesLocalState ((RecM.isDefEqAfterRootCacheMiss a b aKey bKey cacheKey cheapMode).run methods) := by + unfold RecM.isDefEqAfterRootCacheMiss + local_state + +attribute [local irreducible] RecM.isDefEqAfterRootCacheMiss + +@[local_state_frame] theorem isDefEqAfterDirectCacheMiss {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) (eqCtx : Address) + (aKey bKey : EqKey) (cacheKey : Address × Address × Address) + (cheapMode : Bool) : + FramesLocalState ((RecM.isDefEqAfterDirectCacheMiss a b eqCtx aKey bKey cacheKey cheapMode).run methods) := by + unfold RecM.isDefEqAfterDirectCacheMiss + local_state + +attribute [local irreducible] RecM.isDefEqAfterDirectCacheMiss + +@[local_state_frame] theorem isDefEq {methods : Methods .anon} + (recursive : MethodsLocalState methods) (a b : KExpr .anon) : + FramesLocalState ((RecM.isDefEq a b).run methods) := by + unfold RecM.isDefEq + local_state + +attribute [local irreducible] RecM.isDefEq + +end FramesLocalState +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/InferenceCache.lean b/Ix/Kernel/Verify/Consistency/InferenceCache.lean index b1653b109..91dccaf26 100644 --- a/Ix/Kernel/Verify/Consistency/InferenceCache.lean +++ b/Ix/Kernel/Verify/Consistency/InferenceCache.lean @@ -30,6 +30,21 @@ theorem inferKey_closed {term : KExpr .anon} (closed : term.lbr = 0) rw [closed] rfl +/-- Computing the production inference key always succeeds, including +context-address cache misses. -/ +theorem inferKey_total (term : KExpr .anon) (before : TcState .anon) : + ∃ key after, TcM.inferKey term before = .ok key after := by + unfold TcM.inferKey TcM.ctxAddrForLbr + change ∃ key after, EStateM.bind (fun state => + EStateM.bind (get : TcM .anon (TcState .anon)) _ state) _ before = .ok key after + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = + .ok before before from rfl] + by_cases fast : (term.lbr == 0 || before.ctx.isEmpty) = true + · rw [if_pos fast] + exact ⟨_, _, rfl⟩ + · rw [if_neg fast] + cases cached : before.ctxAddrCache[(before.ctxId, term.lbr)]? <;> exact ⟨_, _, rfl⟩ + /-- The exact cache entry eligible at production's computed inference key. An inference-only result cannot override a full result or serve full mode. -/ structure InferenceCacheHit (before : TcState .anon) (term : KExpr .anon) where diff --git a/Ix/Kernel/Verify/Consistency/InferenceLocalState.lean b/Ix/Kernel/Verify/Consistency/InferenceLocalState.lean new file mode 100644 index 000000000..8a91b0435 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/InferenceLocalState.lean @@ -0,0 +1,403 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.LocalState + +/-! +# Local-state preservation by general inference + +Every uncached constructor and both inference-cache partitions preserve +caller locals and the allocation-counter bound, including partial failures. +Scope bodies may append fresh declarations; actual cleanup restores the +incoming context. Universe instantiation changes only the intern table, and +constant lookup uses the maintained loader-counter contract. + +Recursive inference, reduction, conversion and projection remain contracts +of the mutual execution proof. These structural effects do not assert the +semantic validity of computed or cached types. The production ingress +loader must separately establish its counter contract. +-/ + +namespace Ix.Kernel.Consistency +namespace FramesLocalState + +theorem get : FramesLocalState (get : TcM .anon (TcState .anon)) := fun _ _ => .refl _ + +theorem modify (f : TcState .anon → TcState .anon) + (frame : ∀ before, LocalStateFrame before (f before)) : + FramesLocalState (modify f : TcM .anon PUnit) := fun before _ => frame before + +theorem intern (term : KExpr .anon) : FramesLocalState (TcM.intern term) := + runIntern (internExprM term) + +theorem withInferOnly {action : TcM .anon α} (body : FramesLocalState action) : + FramesLocalState (TcM.withInferOnly action) := by + intro before valid + rw [withInferOnly_eq] + have inner := body {before with inferOnly := true} + ⟨valid.coherent, valid.allocated, valid.loader⟩ + cases run : action {before with inferOnly := true} <;> + rw [run] at inner <;> exact ⟨inner.counter, inner.context, inner.loader⟩ + +theorem ctxAddrForLbr (lbr : UInt64) : FramesLocalState (TcM.ctxAddrForLbr (m := .anon) lbr) := by + unfold TcM.ctxAddrForLbr + apply bind get + intro state + split + · exact pure _ + · dsimp only + split + · exact pure _ + · apply bind (modify + (fun later => {later with ctxAddrCache := + (later.ctxAddrCache.insert (state.ctxId, lbr) (TcM.ctxAddrForLbrUncached state lbr))}) + (fun _ => ⟨Nat.le_refl _, .refl _, rfl⟩)) + intro _ + exact pure _ + +theorem inferKey (term : KExpr .anon) : FramesLocalState (TcM.inferKey term) := + bind (ctxAddrForLbr term.lbr) (fun _ => pure _) + +theorem cacheInferResult (inferOnly : Bool) (key : Address × Address) (type : KExpr .anon) + (methods : Methods .anon) : + FramesLocalState ((RecM.cacheInferResult inferOnly key type).run methods) := by + intro before _ + simp only [ReaderT.run, cacheInferResult_eq] + cases inferOnly <;> exact ⟨Nat.le_refl _, .refl _, rfl⟩ + +theorem lookupVar (index : UInt64) : FramesLocalState (TcM.lookupVar (m := .anon) index) := by + unfold TcM.lookupVar + apply bind get + intro state + dsimp only + split + · exact throw _ + · exact runIntern _ + +theorem isEagerReduce (term : KExpr .anon) : FramesLocalState (TcM.isEagerReduce term) := by + unfold TcM.isEagerReduce + generalize term.collectSpine = pair + rcases pair with ⟨head, args⟩ + dsimp only + split + · exact pure _ + · split + · apply bind get + intro state + exact pure _ + · exact pure _ + +theorem prims (methods : Methods .anon) : FramesLocalState ((RecM.prims (m := .anon)).run methods) := by + unfold RecM.prims + simp only [ReaderT.run_bind] + exact bind get (fun _ => pure _) + +theorem lazyIngressAddr (addr : Address) : FramesLocalState (TcM.lazyIngressAddr (m := .anon) addr) := by + intro before valid + unfold TcM.lazyIngressAddr + cases loader : before.lazyFault with + | none => exact .refl _ + | some fault => + dsimp only + by_cases already : before.faultedAddrs.contains addr = true + · rw [if_pos already]; exact .refl _ + · rw [if_neg already] + have counter := valid.loader fault loader addr before.env + cases run : fault addr before.env <;> rw [run] at counter <;> + exact ⟨counter, .refl _, loader.symm⟩ + +theorem tryGetConst (id : KId .anon) : FramesLocalState (TcM.tryGetConst id) := by + unfold TcM.tryGetConst + apply bind get + intro state + cases found : state.env.get? id with + | some constant => exact pure _ + | none => + apply bind get + intro checked + apply bind (lazyIngressAddr id.addr) + intro _ + apply bind get + intro loaded + cases found : loaded.env.get? id with + | some constant => exact pure _ + | none => + cases checked.lazyFault.isSome + · exact pure _ + · exact throw _ + +theorem getConst (id : KId .anon) : FramesLocalState (TcM.getConst id) := by + unfold TcM.getConst + apply bind (tryGetConst id) + intro result + cases result + · exact throw _ + · exact pure _ + +theorem ensureSortWhnf {methods : Methods .anon} + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) (type : KExpr .anon) : + FramesLocalState ((RecM.ensureSortWhnf type).run methods) := by + unfold RecM.ensureSortWhnf + simp only [ReaderT.run_bind] + apply bind (whnf type) + intro result + cases result <;> first | exact pure _ | exact throw _ + +theorem ensureSortDirect {methods : Methods .anon} + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) (type : KExpr .anon) : + FramesLocalState ((RecM.ensureSortDirect type).run methods) := by + cases type <;> simp only [RecM.ensureSortDirect] + all_goals first | exact pure _ | exact ensureSortWhnf whnf _ + +theorem ensureForallWhnf {methods : Methods .anon} + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) (type : KExpr .anon) : + FramesLocalState ((RecM.ensureForallWhnf type).run methods) := by + unfold RecM.ensureForallWhnf + simp only [ReaderT.run_bind] + apply bind (whnf type) + intro result + cases result <;> first | exact pure _ | exact throw _ + +theorem ensureForallDirect {methods : Methods .anon} + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) (type : KExpr .anon) : + FramesLocalState ((RecM.ensureForallDirect type).run methods) := by + cases type <;> simp only [RecM.ensureForallDirect] + all_goals first | exact pure _ | exact ensureForallWhnf whnf _ + +end FramesLocalState + +/-- Every uncached inference branch restores caller locals under the local +state contracts of its recursive calls and lookup/projection helpers. -/ +theorem inferUncached_framesLocalState + {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} {methods : Methods .anon} + (recursive : ∀ term, FramesLocalState ((inferRec term).run methods)) + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) + (conversion : ∀ left right, FramesLocalState ((RecM.isDefEqCall left right).run methods)) + (projection : ∀ id field major type, + FramesLocalState ((RecM.inferProj id field major type).run methods)) + (inferOnly : Bool) (term : KExpr .anon) : + FramesLocalState ((RecM.inferUncached inferRec inferOnly term).run methods) := by + cases term with + | var index name info => simpa [RecM.inferUncached] using FramesLocalState.lookupVar index + | fvar id name info => + unfold RecM.inferUncached + simp only [ReaderT.run_bind] + apply FramesLocalState.bind FramesLocalState.get + intro state + split + · exact FramesLocalState.pure _ + · exact FramesLocalState.throw _ + | sort level info => + simpa [RecM.inferUncached, TcM.intern] using + FramesLocalState.runIntern (internExprM (KExpr.mkSort (KUniv.mkSucc level))) + | const id levels info => + unfold RecM.inferUncached + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + apply FramesLocalState.bind (FramesLocalState.getConst id) + intro constant + split + · exact FramesLocalState.throw _ + · exact FramesLocalState.instantiateUnivParams constant.ty levels + | nat value raw info => + unfold RecM.inferUncached + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + apply FramesLocalState.bind (FramesLocalState.prims methods) + intro primitives + simpa using FramesLocalState.intern (.mkConst primitives.nat #[]) + | str value raw info => + unfold RecM.inferUncached + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + apply FramesLocalState.bind (FramesLocalState.prims methods) + intro primitives + simpa using FramesLocalState.intern (.mkConst primitives.string #[]) + | prj id field major info => + unfold RecM.inferUncached + simp only [ReaderT.run_bind] + exact FramesLocalState.bind (recursive major) (projection id field major) + | app function argument info => + unfold RecM.inferUncached + simp only [ReaderT.run_bind] + apply FramesLocalState.bind (recursive function) + intro functionType + apply FramesLocalState.bind (FramesLocalState.ensureForallDirect whnf functionType) + intro pair + rcases pair with ⟨domain, codomain⟩ + cases inferOnly with + | true => exact FramesLocalState.runIntern _ + | false => + apply FramesLocalState.bind (recursive argument) + intro argumentType + apply FramesLocalState.bind (FramesLocalState.isEagerReduce argument) + intro eager + cases eager with + | false => + simp only [Bool.false_eq_true, if_false] + apply FramesLocalState.bind (conversion argumentType domain) + intro equal + cases equal with + | true => exact FramesLocalState.runIntern _ + | false => + apply FramesLocalState.bind FramesLocalState.get + intro state + intro before valid + exact .refl _ + | true => + simp only [if_true] + change FramesLocalState ((do + modify fun state : TcState .anon => {state with eagerReduce := true} + let equal ← RecM.isDefEqCall argumentType domain + modify fun state : TcState .anon => {state with eagerReduce := false} + if !equal then + throw (.appTypeMismatch argumentType domain (← get).ctx.size) + TcM.runIntern (subst codomain argument 0) : + RecM .anon (KExpr .anon)).run methods) + simp only [ReaderT.run_bind] + apply FramesLocalState.bind (FramesLocalState.modify + (fun state => {state with eagerReduce := true}) (fun _ => ⟨Nat.le_refl _, .refl _, rfl⟩)) + intro _ + apply FramesLocalState.bind (conversion argumentType domain) + intro equal + apply FramesLocalState.bind (FramesLocalState.modify + (fun state => {state with eagerReduce := false}) (fun _ => ⟨Nat.le_refl _, .refl _, rfl⟩)) + intro _ + cases equal with + | true => exact FramesLocalState.runIntern _ + | false => + apply FramesLocalState.bind FramesLocalState.get + intro state + intro before valid + exact .refl _ + | lam name bi domain body info => + unfold RecM.inferUncached + have scope : FramesLocalState ((RecM.withLctxScope (m := .anon) do + let (opened, fresh) ← TcM.openBinder name bi domain body + let bodyType ← inferRec opened + let reduced ← TcM.runIntern (cheapBetaReduce bodyType) + let abstracted ← TcM.runIntern (abstractFVars reduced #[fresh]) + TcM.intern (.mkAll RecM.anonN RecM.anonBi domain abstracted)).run methods) := by + apply PreservesLocalState.withLctxScope + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + apply PreservesLocalState.bind (PreservesLocalState.openBinder name bi domain body) + intro pair + rcases pair with ⟨opened, fresh⟩ + apply PreservesLocalState.bind (recursive opened).preserves + intro bodyType + apply PreservesLocalState.bind (FramesLocalState.runIntern _).preserves + intro reduced + apply PreservesLocalState.bind (FramesLocalState.runIntern _).preserves + intro abstracted + simpa using (FramesLocalState.intern (.mkAll RecM.anonN RecM.anonBi domain abstracted)).preserves + cases inferOnly with + | true => exact scope + | false => + simp only [Bool.not_false, if_true, ReaderT.run_bind] + exact FramesLocalState.bind (recursive domain) fun domainType => + FramesLocalState.bind (FramesLocalState.ensureSortDirect whnf domainType) fun _ => scope + | all name bi domain body info => + unfold RecM.inferUncached + simp only [ReaderT.run_bind] + apply FramesLocalState.bind (recursive domain) + intro domainType + apply FramesLocalState.bind (FramesLocalState.ensureSortDirect whnf domainType) + intro domainLevel + apply PreservesLocalState.withLctxScope + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + apply PreservesLocalState.bind (PreservesLocalState.openBinder name bi domain body) + intro pair + rcases pair with ⟨opened, fresh⟩ + apply PreservesLocalState.bind (recursive opened).preserves + intro bodyType + apply PreservesLocalState.bind (FramesLocalState.ensureSortDirect whnf bodyType).preserves + intro bodyLevel + simpa using (FramesLocalState.intern (.mkSort (.mkIMax domainLevel bodyLevel))).preserves + | letE name domain value body nonDep info => + unfold RecM.inferUncached + have scope : FramesLocalState ((RecM.withLctxScope (m := .anon) do + let (opened, fresh) ← TcM.openLet name domain value body + let bodyType ← inferRec opened + let abstracted ← TcM.runIntern (abstractFVars bodyType #[fresh]) + let substituted ← TcM.runIntern (subst abstracted value 0) + TcM.runIntern (cheapBetaReduce substituted)).run methods) := by + apply PreservesLocalState.withLctxScope + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + apply PreservesLocalState.bind (PreservesLocalState.openLet name domain value body) + intro pair + rcases pair with ⟨opened, fresh⟩ + apply PreservesLocalState.bind (recursive opened).preserves + intro bodyType + apply PreservesLocalState.bind (FramesLocalState.runIntern _).preserves + intro abstracted + apply PreservesLocalState.bind (FramesLocalState.runIntern _).preserves + intro substituted + exact (FramesLocalState.runIntern _).preserves + cases inferOnly with + | true => exact scope + | false => + simp only [Bool.not_false, if_true, ReaderT.run_bind] + apply FramesLocalState.bind (recursive domain) + intro domainType + apply FramesLocalState.bind (FramesLocalState.ensureSortDirect whnf domainType) + intro level + apply FramesLocalState.bind (recursive value) + intro valueType + apply FramesLocalState.bind (conversion valueType domain) + intro equal + cases equal with + | true => exact scope + | false => exact FramesLocalState.throw _ + +namespace FramesLocalState + +/-- Both cache partitions and the actual outer write preserve the local +frame of the uncached action. The statement also covers failed inference. -/ +theorem inferWith {inferRec : KExpr .anon → RecM .anon (KExpr .anon)} + {methods : Methods .anon} (term : KExpr .anon) + (uncached : ∀ inferOnly, + FramesLocalState ((RecM.inferUncached inferRec inferOnly term).run methods)) : + FramesLocalState ((RecM.inferWith inferRec term).run methods) := by + unfold RecM.inferWith + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + apply bind get + intro state + apply bind (inferKey term) + intro key + apply bind get + intro checked + split + · exact pure _ + · have miss : FramesLocalState ((do + let type ← RecM.inferUncached inferRec state.inferOnly term + RecM.cacheInferResult state.inferOnly key type + Pure.pure type).run methods) := by + simp only [ReaderT.run_bind] + exact bind (uncached state.inferOnly) fun type => + bind (cacheInferResult state.inferOnly key type methods) fun _ => pure type + split + · simp only [ReaderT.run_bind] + apply bind get + intro checkedOnly + split + · exact pure _ + · exact miss + · exact miss + +end FramesLocalState + +/-- The production one-layer inference body restores locals and maintains +the allocation bound in both policies, including hits and partial errors. +Only recursive inference, reduction, conversion and projection contracts +remain for the mutual execution proof. -/ +theorem infer_framesLocalState {methods : Methods .anon} + (recursive : ∀ term, FramesLocalState (methods.infer term)) + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) + (conversion : ∀ left right, FramesLocalState ((RecM.isDefEqCall left right).run methods)) + (projection : ∀ id field major type, + FramesLocalState ((RecM.inferProj id field major type).run methods)) + (term : KExpr .anon) : FramesLocalState ((RecM.infer term).run methods) := + FramesLocalState.inferWith term fun inferOnly => + inferUncached_framesLocalState (inferRec := RecM.inferCall) (methods := methods) + recursive whnf conversion projection inferOnly term + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/IngressLocalState.lean b/Ix/Kernel/Verify/Consistency/IngressLocalState.lean new file mode 100644 index 000000000..38dc4fe10 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/IngressLocalState.lean @@ -0,0 +1,38 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.LocalState +import Ix.Kernel.Verify.IngressState +import Ix.Kernel.Driver + +/-! +# Production loader establishes the structural local invariant + +The actual anonymous loader preserves every checker-owned environment field +on both outcomes. In particular, it cannot rewind the fresh-variable counter. +The lazy driver's initial state therefore establishes the structural local +invariant without a separate callback-effect assumption. Declaration meaning, +intern coherence and semantic cache preservation remain separate obligations. +-/ + +namespace Ix.Kernel.Consistency + +theorem LoaderCounterMonotone.ingressAnonAddrShallow (env : Ixon.Env) (verify : Bool) : + LoaderCounterMonotone (some fun addr => ingressAnonAddrShallow env addr verify) := by + intro fault installed addr before + cases installed + dsimp only + have frame := IngressM.FramesState.ingressAnonAddrShallow env addr verify before + cases run : _root_.Ix.Kernel.ingressAnonAddrShallow env addr verify before <;> rw [run] at frame <;> + exact Nat.le_of_eq (congrArg UInt64.toNat frame.counter.symm) + +/-- The structural local invariant is established by the actual lazy driver, +including its concrete loader. It needs no loader-effect premise. -/ +theorem LocalStateInvariant.newLazyAnon (env : Ixon.Env) (verify : Bool) : + LocalStateInvariant (TcState.newLazyAnon env verify) := + ⟨LocalContext.WF.empty, LocalContext.IdsBelow.empty _, + LoaderCounterMonotone.ingressAnonAddrShallow env verify⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/LetCache.lean b/Ix/Kernel/Verify/Consistency/LetCache.lean index 88b25b2fc..47b067ec3 100644 --- a/Ix/Kernel/Verify/Consistency/LetCache.lean +++ b/Ix/Kernel/Verify/Consistency/LetCache.lean @@ -43,9 +43,9 @@ def cacheTrace let domainRun := check.domainTree.cacheExecution data.domain contextOrigin keyedAgreement check.domainReading check.execution.domainRun let valueRun := check.valueTree.cacheExecution data.value contextOrigin - (check.execution.domainContext.symm ▸ keyedAgreement) check.valueReading check.execution.valueRun - have opened := openLet_sound check.opening (check.execution.openingContext.symm ▸ keyedAgreement) - check.absent check.domainReading check.bodyReading check.execution.openRun + (keyedAgreement.congr (check.execution.domainContext check.keyedValid).symm) check.valueReading check.execution.valueRun + have opened := openLet_sound check.opening (keyedAgreement.congr (check.execution.openingContext check.keyedValid).symm) + (check.absent agreement) check.domainReading check.bodyReading check.execution.openRun let bodyRun := check.bodyTree.cacheExecution data.body (.push contextOrigin check.domainTree keyedAgreement check.domainReading check.execution.domainRun) opened.2.2.1 opened.2.1 check.execution.bodyRun diff --git a/Ix/Kernel/Verify/Consistency/LetInference.lean b/Ix/Kernel/Verify/Consistency/LetInference.lean index c309bda12..644ac6cb3 100644 --- a/Ix/Kernel/Verify/Consistency/LetInference.lean +++ b/Ix/Kernel/Verify/Consistency/LetInference.lean @@ -6,6 +6,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Verify.Consistency.LetOpening import Ix.Kernel.Verify.Consistency.InferenceCache import Ix.Kernel.Verify.Consistency.CheapBetaReading +import Ix.Kernel.Verify.Consistency.LocalStateReading /-! The full production let branch checks the declared type and value, opens a let local, and infers the body. Its result is obtained by abstraction, @@ -17,8 +18,8 @@ open Theory Theory.Model universe u -/-- Actual recursive calls and their states. Context equalities concern -representation only; no child typing or returned-type reading is assumed. -/ +/-- Actual recursive calls and their states. Scope restoration is derived +from these runs; no child typing or returned-type reading is assumed. -/ structure LetInferenceTrace (fuel : Nat) (before : TcState .anon) (name : Mode.anon.F Name) (domain value body : KExpr .anon) where domainLevel : KUniv .anon @@ -38,14 +39,34 @@ structure LetInferenceTrace (fuel : Nat) (before : TcState .anon) compareRun : RecM.isDefEq valueType domain (methodsN fuel) valueState = .ok true comparedState openRun : TcM.openLet name domain value body comparedState = .ok (opened, fresh) openedState bodyRun : RecM.infer opened (methodsN fuel) openedState = .ok bodyType bodyState - domainContext : domainState.lctx = before.lctx - openingContext : comparedState.lctx = before.lctx namespace LetInferenceTrace variable {fuel : Nat} {before : TcState .anon} {name : Mode.anon.F Name} {domain value body : KExpr .anon} +theorem domainFrame (trace : LetInferenceTrace fuel before name domain value body) + (valid : LocalStateInvariant before) : LocalStateFrame before trace.domainState := + (infer_methodsN_framesLocalState fuel domain).ok valid trace.domainRun + +theorem valueFrame (trace : LetInferenceTrace fuel before name domain value body) + (valid : LocalStateInvariant before) : LocalStateFrame before trace.valueState := + (trace.domainFrame valid).trans ((infer_methodsN_framesLocalState fuel value).ok + ((trace.domainFrame valid).invariant valid) trace.valueRun) + +theorem openingFrame (trace : LetInferenceTrace fuel before name domain value body) + (valid : LocalStateInvariant before) : LocalStateFrame before trace.comparedState := + (trace.valueFrame valid).trans ((isDefEq_methodsN_framesLocalState fuel trace.valueType domain).ok + ((trace.valueFrame valid).invariant valid) trace.compareRun) + +theorem domainContext (trace : LetInferenceTrace fuel before name domain value body) + (valid : LocalStateInvariant before) : trace.domainState.lctx.Equiv before.lctx := + (trace.domainFrame valid).context + +theorem openingContext (trace : LetInferenceTrace fuel before name domain value body) + (valid : LocalStateInvariant before) : trace.comparedState.lctx.Equiv before.lctx := + (trace.openingFrame valid).context + def abstracted (trace : LetInferenceTrace fuel before name domain value body) := abstractFVars trace.bodyType #[trace.fresh] trace.bodyState.env.intern @@ -60,6 +81,18 @@ def after (trace : LetInferenceTrace fuel before name domain value body) : TcSta env := {trace.bodyState.env with intern := trace.reduced.2} lctx := trace.bodyState.lctx.truncate trace.comparedState.lctx.size} +/-- The whole let scope restores the incoming declarations while retaining +the fresh counter and the body computation's final intern table. -/ +theorem restores (trace : LetInferenceTrace fuel before name domain value body) + (valid : LocalStateInvariant before) : LocalStateFrame before trace.after := by + have compared := trace.openingFrame valid + have opened := PreservesLocalState.openLet name domain value body trace.comparedState + (compared.invariant valid) + rw [trace.openRun] at opened + have bodyFrame := (infer_methodsN_framesLocalState fuel trace.opened).ok opened.valid trace.bodyRun + have restored := (opened.trans (.of_frame opened.valid bodyFrame)).restore + exact compared.trans ⟨restored.counter, restored.context, restored.loader⟩ + private theorem withLctxScope_success {action : RecM .anon α} {methods : Methods .anon} {before finished : TcState .anon} {result : α} (accepted : action.withLctxScope.run methods before = .ok result finished) : diff --git a/Ix/Kernel/Verify/Consistency/LetSynthesis.lean b/Ix/Kernel/Verify/Consistency/LetSynthesis.lean index 1c46ffb7a..4b93eded4 100644 --- a/Ix/Kernel/Verify/Consistency/LetSynthesis.lean +++ b/Ix/Kernel/Verify/Consistency/LetSynthesis.lean @@ -67,10 +67,10 @@ structure LetInferenceCheck {β : Type u} (resolve : Address → Option (ConstRe (name : Mode.anon.F Name) (domain value body : KExpr .anon) (nonDep : Bool) (info : ExprInfo .anon) (A v b B resultType : AExpr β) (level : VLevel) where full : before.inferOnly = false + localState : LocalStateInvariant before miss : UncachedInference before (.letE name domain value body nonDep info) execution : LetInferenceTrace fuel miss.keyed name domain value body opening : BinderOpeningSupport execution.comparedState body - absent : (⟨execution.comparedState.env.nextFVarId⟩ : FVarId) ∉ locals domainBound : VLevel valueLevel : VLevel valueType : AExpr β @@ -97,6 +97,23 @@ variable {β : Type u} {resolve : Address → Option (ConstRef β)} {fuel : Nat} {before : TcState .anon} {name : Mode.anon.F Name} {domain value body : KExpr .anon} {nonDep : Bool} {info : ExprInfo .anon} {A val b B resultType : AExpr β} {level : VLevel} +/-- The initial invariant survives production's key computation. -/ +theorem keyedValid + (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level) : LocalStateInvariant check.miss.keyed := + ((FramesLocalState.inferKey _).ok check.localState check.miss.keyRun).invariant check.localState + +/-- All recursive checks preserve the counter bound; the selected let id +therefore cannot name an existing model local. -/ +theorem absent + (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level) + (agreement : LocalContextReading resolve locals before.lctx context) : + (⟨check.execution.comparedState.env.nextFVarId⟩ : FVarId) ∉ locals := by + have frame := check.execution.openingFrame check.keyedValid + exact (frame.invariant check.keyedValid).freshReading + ((check.miss.localContext.symm ▸ agreement).congr frame.context.symm) + theorem source_reading (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info A val b B resultType level) : @@ -108,7 +125,7 @@ private theorem value_type A val b B resultType level) (formed : ContextFormation.{u,v} entries context bounds) (agreement : LocalContextReading resolve locals before.lctx context) : check.valueType = A := by - have valueAgreement := check.execution.domainContext.symm ▸ (check.miss.localContext.symm ▸ agreement) + have valueAgreement := (check.miss.localContext.symm ▸ agreement).congr (check.execution.domainContext check.keyedValid).symm have returned := (check.valueTree.soundWithSpine formed valueAgreement check.valueReading check.execution.valueRun).1 exact AExpr.eq_of_erase_annotations @@ -125,9 +142,9 @@ def substituted_type_origin (agreement : LocalContextReading resolve locals before.lctx context) : SynthesisTypingOrigin resolve entries context bounds entries context (B.inst val) (.sort level) := by have keyedAgreement := check.miss.localContext.symm ▸ agreement - have valueAgreement := check.execution.domainContext.symm ▸ keyedAgreement - have opened := openLet_sound check.opening (check.execution.openingContext.symm ▸ keyedAgreement) - check.absent check.domainReading check.bodyReading check.execution.openRun + have valueAgreement := keyedAgreement.congr (check.execution.domainContext check.keyedValid).symm + have opened := openLet_sound check.opening (keyedAgreement.congr (check.execution.openingContext check.keyedValid).symm) + (check.absent agreement) check.domainReading check.bodyReading check.execution.openRun have bodyOrigin := SynthesisTypingOrigin.inferredType (SynthesisContext.push .current check.domainTree keyedAgreement check.domainReading check.execution.domainRun) check.bodyTree opened.2.2.1 opened.2.1 check.execution.bodyRun @@ -145,9 +162,9 @@ def betaTyping (agreement : LocalContextReading resolve locals before.lctx context) : SynthesisBetaTyping resolve entries context bounds entries context (b.inst val) resultType := by have keyedAgreement := check.miss.localContext.symm ▸ agreement - have valueAgreement := check.execution.domainContext.symm ▸ keyedAgreement - have opened := openLet_sound check.opening (check.execution.openingContext.symm ▸ keyedAgreement) - check.absent check.domainReading check.bodyReading check.execution.openRun + have valueAgreement := keyedAgreement.congr (check.execution.domainContext check.keyedValid).symm + have opened := openLet_sound check.opening (keyedAgreement.congr (check.execution.openingContext check.keyedValid).symm) + (check.absent agreement) check.domainReading check.bodyReading check.execution.openRun have inner := check.bodyTree.betaTyping (SynthesisContext.push .current check.domainTree keyedAgreement check.domainReading check.execution.domainRun) opened.2.2.1 opened.2.1 check.execution.bodyRun formed @@ -171,8 +188,8 @@ theorem sound {result : KExpr .anon} {after : TcState .anon} have keyedAgreement := check.miss.localContext.symm ▸ agreement obtain ⟨_, domainTyped, _, _⟩ := check.domainTree.soundWithSpine formed keyedAgreement check.domainReading check.execution.domainRun - have opened := openLet_sound check.opening (check.execution.openingContext.symm ▸ keyedAgreement) - check.absent check.domainReading check.bodyReading check.execution.openRun + have opened := openLet_sound check.opening (keyedAgreement.congr (check.execution.openingContext check.keyedValid).symm) + (check.absent agreement) check.domainReading check.bodyReading check.execution.openRun obtain ⟨bodyTypeReading, _, _, _⟩ := check.bodyTree.soundWithSpine (formed.push domainTyped) opened.2.2.1 opened.2.1 check.execution.bodyRun obtain ⟨substitutedReading, substitutedCoherent⟩ := diff --git a/Ix/Kernel/Verify/Consistency/LocalScope.lean b/Ix/Kernel/Verify/Consistency/LocalScope.lean new file mode 100644 index 000000000..44de3dae2 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/LocalScope.lean @@ -0,0 +1,162 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.LocalScope +import Ix.Kernel.Verify.Consistency.LetOpening +import Ix.Kernel.Verify.Consistency.InferenceCache + +/-! +# Restoring model locals at production scope boundaries + +Actual scope cleanup restores every caller lookup after a body that only +extends the local context, on both success and failure. Model readers use +this fact to restore the context present before binder and let scopes. +The recursive execution proof must establish the body extension invariant. +-/ + +namespace Ix.Kernel.Consistency +open Theory Theory.Model +universe u v + +/-- Actual scope cleanup restores the incoming context after an arbitrary +successful body which only extended its local declarations. -/ +theorem withLctxScope_restores {action : RecM .anon α} {methods : Methods .anon} + {before after : TcState .anon} {result : α} + (extended : ∀ state, action.run methods before = .ok result state → + before.lctx.Extension state.lctx) + (run : (RecM.withLctxScope action).run methods before = .ok result after) : + after.lctx.Equiv before.lctx := by + rw [withLctxScope_eq] at run + cases bodyRun : action.run methods before with + | error error state => rw [bodyRun] at run; contradiction + | ok value state => + rw [bodyRun] at run + cases run + exact (extended state bodyRun).restore + +/-- The same restoration applies to the partial state returned on failure. -/ +theorem withLctxScope_error_restores {action : RecM .anon α} {methods : Methods .anon} + {before after : TcState .anon} {error : TcError .anon} + (extended : ∀ state, action.run methods before = .error error state → + before.lctx.Extension state.lctx) + (run : (RecM.withLctxScope action).run methods before = .error error after) : + after.lctx.Equiv before.lctx := by + rw [withLctxScope_eq] at run + cases bodyRun : action.run methods before with + | ok value state => rw [bodyRun] at run; contradiction + | error failure state => + rw [bodyRun] at run + cases run + exact (extended state bodyRun).restore + +theorem openLet_extends {name : Mode.anon.F Name} {type value body opened : KExpr .anon} + {fresh : FVarId} {before after : TcState .anon} + (absent : before.lctx.index[(⟨before.env.nextFVarId⟩ : FVarId)]? = none) + (run : TcM.openLet name type value body before = .ok (opened, fresh) after) : + before.lctx.Extension after.lctx := by + rw [openLet_eq] at run + split at run + · cases run; exact .push _ (.refl _) absent + · contradiction + +theorem openBinder_extends {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {type body opened : KExpr .anon} {fresh : FVarId} {before after : TcState .anon} + (absent : before.lctx.index[(⟨before.env.nextFVarId⟩ : FVarId)]? = none) + (run : TcM.openBinder name bi type body before = .ok (opened, fresh) after) : + before.lctx.Extension after.lctx := by + rw [openBinder_eq] at run + split at run + · cases run; exact .push _ (.refl _) absent + · contradiction + +/-- Neither successful nor failed execution removes or changes an incoming +local declaration. Extra fresh declarations are restored by enclosing scopes. -/ +def PreservesLocalExtension (action : TcM .anon α) : Prop := + ∀ before, match action before with + | .ok _ after | .error _ after => before.lctx.Extension after.lctx + +namespace PreservesLocalExtension + +theorem pure (value : α) : PreservesLocalExtension (Pure.pure value) := fun _ => .refl _ + +theorem throw (error : TcError .anon) : + PreservesLocalExtension (throw error : TcM .anon α) := fun _ => .refl _ + +theorem bind {action : TcM .anon α} {next : α → TcM .anon γ} + (first : PreservesLocalExtension action) + (rest : ∀ value, PreservesLocalExtension (next value)) : + PreservesLocalExtension (action >>= next) := by + intro before + have intermediate := first before + change match EStateM.bind action next before with + | .ok _ after | .error _ after => before.lctx.Extension after.lctx + cases run : action before with + | error error after => + rw [EStateM.bind, run] + simpa only [run] using intermediate + | ok value after => + rw [run] at intermediate + rw [EStateM.bind, run] + dsimp only + have final := rest value after + cases finished : next value after <;> + rw [finished] at final <;> exact intermediate.trans final + +theorem runIntern (action : InternM .anon α) : + PreservesLocalExtension (TcM.runIntern action) := fun _ => .refl _ + +theorem inferKey (term : KExpr .anon) : PreservesLocalExtension (TcM.inferKey term) := by + intro before + obtain ⟨key, keyed, run⟩ := inferKey_total term before + rw [run] + change before.lctx.Extension keyed.lctx + rw [inferKey_lctx run] + exact .refl _ + +theorem withInferOnly {action : TcM .anon α} (body : PreservesLocalExtension action) : + PreservesLocalExtension (TcM.withInferOnly action) := by + intro before + rw [withInferOnly_eq] + have inner := body {before with inferOnly := true} + cases run : action {before with inferOnly := true} <;> + simpa only [run] using inner + +theorem withLctxScope {action : RecM .anon α} {methods : Methods .anon} + (body : PreservesLocalExtension (action.run methods)) : + PreservesLocalExtension ((RecM.withLctxScope action).run methods) := by + intro before + rw [withLctxScope_eq] + have inner := body before + cases run : action.run methods before <;> rw [run] at inner <;> + exact .equiv (.refl _) inner.restore.symm + +end PreservesLocalExtension + +/-- The actual inference cache shell preserves the caller's local context +whenever its uncached branch does. Both eligible partitions and the outer +write are handled directly from execution. -/ +theorem infer_localContext + {before after : TcState .anon} {term result : KExpr .anon} {methods : Methods .anon} + (uncached : ∀ key keyed type finished, + TcM.inferKey term before = .ok key keyed → + RecM.inferUncached RecM.inferCall before.inferOnly term methods keyed = + .ok type finished → + finished.lctx.Equiv keyed.lctx) + (run : RecM.infer term methods before = .ok result after) : + after.lctx.Equiv before.lctx := by + obtain ⟨key, keyed, keyRun⟩ := inferKey_total term before + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ + · rw [hit.run methods] at run + cases run + rw [inferKey_lctx hit.keyRun] + exact .refl _ + · obtain ⟨finished, bodyRun, written⟩ := infer_uncached_success_state miss run + have bodySame := uncached miss.key miss.keyed result finished miss.keyRun bodyRun + have same : after.lctx = finished.lctx := by + rw [written] + cases before.inferOnly <;> rfl + simpa only [same, miss.localContext] using bodySame + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/LocalState.lean b/Ix/Kernel/Verify/Consistency/LocalState.lean new file mode 100644 index 000000000..6ed6872d7 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/LocalState.lean @@ -0,0 +1,268 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.LocalScope +import Ix.Kernel.Verify.UniverseInternOnly + +/-! +# Maintained structural local state + +Coherent lookups and the allocation-counter bound justify freshness at each +actual binder opening. The installed lazy loader must preserve that bound +by never rewinding the counter. Interning preserves the state; successful opening +extends it under the production overflow guard; scope cleanup restores the +caller context while retaining the larger allocation counter. The same +contracts cover partial failure states and compose through monadic binds. + +This is the structural local component of the general semantic state +invariant. It does not establish inference, reduction or cache meaning. +-/ + +namespace Ix.Kernel.LocalContext + +private local instance : LawfulBEq FVarId where + eq_of_beq := by + intro left right equal + cases left + cases right + congr 1 + exact eq_of_beq equal + rfl {a} := by + cases a with + | mk x => show (x == x) = true; exact beq_self_eq_true x + +/-- Every indexed local was allocated before the actual next identifier. -/ +def IdsBelow (context : LocalContext m) (next : UInt64) : Prop := + ∀ (id : FVarId) (position : Nat), context.index[id]? = some position → + id.id.toNat < next.toNat + +namespace IdsBelow + +theorem empty (next : UInt64) : IdsBelow ({} : LocalContext m) next := by + intro id position found + simp at found + +theorem mono {context : LocalContext m} {next later : UInt64} + (bound : context.IdsBelow next) (order : next.toNat ≤ later.toNat) : context.IdsBelow later := by + intro id position found + exact Nat.lt_of_lt_of_le (bound id position found) order + +theorem equiv {left right : LocalContext m} {next : UInt64} + (bound : left.IdsBelow next) (same : left.Equiv right) : right.IdsBelow next := by + intro id position found + exact bound id position ((same.index id).trans found) + +theorem fresh {context : LocalContext m} {next : UInt64} (bound : context.IdsBelow next) : + context.index[(⟨next⟩ : FVarId)]? = none := by + cases found : context.index[(⟨next⟩ : FVarId)]? with + | none => rfl + | some position => exact False.elim (Nat.lt_irrefl _ (bound _ _ found)) + +theorem push {context : LocalContext m} {next : UInt64} + (bound : context.IdsBelow next) (decl : LocalDecl m) + (room : next.toNat + 1 < UInt64.size) : + (context.push ⟨next⟩ decl).IdsBelow (next + 1) := by + have increment : (next + 1).toNat = next.toNat + 1 := by + rw [UInt64.toNat_add, show (1 : UInt64).toNat = 1 from rfl, Nat.mod_eq_of_lt room] + intro id position found + simp only [LocalContext.push, Std.HashMap.getElem?_insert] at found + rw [increment] + split at found + next equal => + have equal : (⟨next⟩ : FVarId) = id := eq_of_beq equal + subst id + exact Nat.lt_succ_self _ + next different => exact Nat.lt_trans (bound _ _ found) (Nat.lt_succ_self _) + +end IdsBelow +end Ix.Kernel.LocalContext + +namespace Ix.Kernel.Consistency +open Theory Theory.Model +universe u v + +/-- The installed lazy loader never rewinds the allocation counter. The +production ingress loader must establish this property on both outcomes. -/ +def LoaderCounterMonotone (loader : Option (Address → EStateM String (KEnv .anon) Bool)) : Prop := + ∀ fault, loader = some fault → ∀ addr before, + match fault addr before with + | .ok _ after | .error _ after => before.nextFVarId.toNat ≤ after.nextFVarId.toNat + +/-- Structural component of the general state invariant: local lookup is +coherent, every live identifier precedes the allocation counter, and lazy +loading never rewinds that counter. -/ +structure LocalStateInvariant (state : TcState .anon) : Prop where + coherent : state.lctx.WF + allocated : state.lctx.IdsBelow state.env.nextFVarId + loader : LoaderCounterMonotone state.lazyFault + +/-- A scoped operation can allocate fresh identifiers but restores all +incoming local declarations and lookups. -/ +structure LocalStateFrame (before after : TcState .anon) : Prop where + counter : before.env.nextFVarId.toNat ≤ after.env.nextFVarId.toNat + context : after.lctx.Equiv before.lctx + loader : after.lazyFault = before.lazyFault + +namespace LocalStateFrame + +theorem refl (state : TcState .anon) : LocalStateFrame state state := ⟨Nat.le_refl _, .refl _, rfl⟩ + +theorem trans {before middle after : TcState .anon} + (first : LocalStateFrame before middle) (second : LocalStateFrame middle after) : + LocalStateFrame before after := + ⟨Nat.le_trans first.counter second.counter, second.context.trans first.context, + second.loader.trans first.loader⟩ + +theorem invariant {before after : TcState .anon} + (frame : LocalStateFrame before after) (valid : LocalStateInvariant before) : + LocalStateInvariant after := + ⟨frame.context.symm.wf valid.coherent, + (valid.allocated.equiv frame.context.symm).mono frame.counter, + by rw [frame.loader]; exact valid.loader⟩ + +end LocalStateFrame + +/-- A body may leave fresh local declarations for its enclosing scope to +remove. Both returned states, including failures, must satisfy this effect. -/ +structure LocalStateExtension (before after : TcState .anon) : Prop where + valid : LocalStateInvariant after + counter : before.env.nextFVarId.toNat ≤ after.env.nextFVarId.toNat + context : before.lctx.Extension after.lctx + loader : after.lazyFault = before.lazyFault + +namespace LocalStateExtension + +theorem refl {state : TcState .anon} (valid : LocalStateInvariant state) : + LocalStateExtension state state := ⟨valid, Nat.le_refl _, .refl _, rfl⟩ + +theorem trans {before middle after : TcState .anon} + (first : LocalStateExtension before middle) (second : LocalStateExtension middle after) : + LocalStateExtension before after := + ⟨second.valid, Nat.le_trans first.counter second.counter, first.context.trans second.context, + second.loader.trans first.loader⟩ + +theorem of_frame {before after : TcState .anon} (valid : LocalStateInvariant before) + (frame : LocalStateFrame before after) : LocalStateExtension before after := + ⟨frame.invariant valid, frame.counter, .equiv (.refl _) frame.context.symm, frame.loader⟩ + +theorem restore {before after : TcState .anon} (effect : LocalStateExtension before after) : + LocalStateFrame before {after with lctx := after.lctx.truncate before.lctx.size} := + ⟨effect.counter, effect.context.restore, effect.loader⟩ + +end LocalStateExtension + +/-- Prefix preservation under the structural local-state invariant. -/ +def PreservesLocalState (action : TcM .anon α) : Prop := + ∀ before, LocalStateInvariant before → match action before with + | .ok _ after | .error _ after => LocalStateExtension before after + +/-- Scoped preservation under the same invariant. The ending invariant is +derived from the restored context and monotone counter. -/ +def FramesLocalState (action : TcM .anon α) : Prop := + ∀ before, LocalStateInvariant before → match action before with + | .ok _ after | .error _ after => LocalStateFrame before after + +namespace FramesLocalState + +theorem preserves {action : TcM .anon α} (framed : FramesLocalState action) : + PreservesLocalState action := by + intro before valid + have frame := framed before valid + cases run : action before <;> rw [run] at frame <;> exact .of_frame valid frame + +theorem pure (value : α) : FramesLocalState (Pure.pure value) := fun _ _ => .refl _ + +theorem throw (error : TcError .anon) : FramesLocalState (throw error : TcM .anon α) := + fun _ _ => .refl _ + +theorem runIntern (action : InternM .anon α) : FramesLocalState (TcM.runIntern action) := + fun _ _ => ⟨Nat.le_refl _, .refl _, rfl⟩ + +theorem of_internOnly {action : TcM .anon α} (only : action.InternOnly) : + FramesLocalState action := by + intro before _ + have changed := only before + cases run : action before <;> rw [run] at changed <;> + obtain ⟨table, exactState⟩ := changed <;> rw [exactState] <;> + exact ⟨Nat.le_refl _, .refl _, rfl⟩ + +theorem instantiateUnivParams (type : KExpr .anon) (levels : Array (KUniv .anon)) : + FramesLocalState (TcM.instantiateUnivParams type levels) := + of_internOnly (TcM.InternOnly.instantiateUnivParams type levels) + +theorem bind {action : TcM .anon α} {next : α → TcM .anon γ} + (first : FramesLocalState action) (rest : ∀ value, FramesLocalState (next value)) : + FramesLocalState (action >>= next) := by + intro before valid + have intermediate := first before valid + change match EStateM.bind action next before with + | .ok _ after | .error _ after => LocalStateFrame before after + cases run : action before with + | error error after => rw [EStateM.bind, run]; simpa only [run] using intermediate + | ok value after => + rw [run] at intermediate + rw [EStateM.bind, run] + dsimp only + have final := rest value after (intermediate.invariant valid) + cases finished : next value after <;> rw [finished] at final <;> exact intermediate.trans final + +end FramesLocalState + +namespace PreservesLocalState + +theorem bind {action : TcM .anon α} {next : α → TcM .anon γ} + (first : PreservesLocalState action) (rest : ∀ value, PreservesLocalState (next value)) : + PreservesLocalState (action >>= next) := by + intro before valid + have intermediate := first before valid + change match EStateM.bind action next before with + | .ok _ after | .error _ after => LocalStateExtension before after + cases run : action before with + | error error after => rw [EStateM.bind, run]; simpa only [run] using intermediate + | ok value after => + rw [run] at intermediate + rw [EStateM.bind, run] + dsimp only + have final := rest value after intermediate.valid + cases finished : next value after <;> rw [finished] at final <;> exact intermediate.trans final + +/-- Scope cleanup turns a prefix-preserving body into a framed computation, +including the body's partially updated failure state. -/ +theorem withLctxScope {action : RecM .anon α} {methods : Methods .anon} + (body : PreservesLocalState (action.run methods)) : + FramesLocalState ((RecM.withLctxScope action).run methods) := by + intro before valid + rw [withLctxScope_eq] + have inner := body before valid + cases run : action.run methods before <;> rw [run] at inner <;> exact inner.restore + +theorem openLet (name : Mode.anon.F Name) (type value body : KExpr .anon) : + PreservesLocalState (TcM.openLet name type value body) := by + intro before valid + rw [openLet_eq] + by_cases room : before.env.nextFVarId.toNat + 1 < UInt64.size + · rw [if_pos room] + have increment : (before.env.nextFVarId + 1).toNat = before.env.nextFVarId.toNat + 1 := by + rw [UInt64.toNat_add, show (1 : UInt64).toNat = 1 from rfl, Nat.mod_eq_of_lt room] + exact ⟨⟨valid.coherent.push valid.allocated.fresh, valid.allocated.push _ room, valid.loader⟩, + by change before.env.nextFVarId.toNat ≤ (before.env.nextFVarId + 1).toNat + rw [increment]; omega, .push _ (.refl _) valid.allocated.fresh, rfl⟩ + · rw [if_neg room]; exact .refl valid + +theorem openBinder (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) + (type body : KExpr .anon) : PreservesLocalState (TcM.openBinder name bi type body) := by + intro before valid + rw [openBinder_eq] + by_cases room : before.env.nextFVarId.toNat + 1 < UInt64.size + · rw [if_pos room] + have increment : (before.env.nextFVarId + 1).toNat = before.env.nextFVarId.toNat + 1 := by + rw [UInt64.toNat_add, show (1 : UInt64).toNat = 1 from rfl, Nat.mod_eq_of_lt room] + exact ⟨⟨valid.coherent.push valid.allocated.fresh, valid.allocated.push _ room, valid.loader⟩, + by change before.env.nextFVarId.toNat ≤ (before.env.nextFVarId + 1).toNat + rw [increment]; omega, .push _ (.refl _) valid.allocated.fresh, rfl⟩ + · rw [if_neg room]; exact .refl valid + +end PreservesLocalState +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/LocalStateEffects.lean b/Ix/Kernel/Verify/Consistency/LocalStateEffects.lean new file mode 100644 index 000000000..2bae99425 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/LocalStateEffects.lean @@ -0,0 +1,315 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.InferenceLocalState +import Ix.Kernel.Verify.Monad + +/-! +# Local-state effects of reduction and conversion control flow + +Both outcomes preserve the caller's local declarations, allocation bound and +installed loader. These rules cover caught failures, cleanup, finite loops, +keys, fuel and policy scopes. Recursive callback frames describe a strictly +smaller method table. The reduction and conversion proofs compose these rules +to close each finite production table. +-/ + +namespace Ix.Kernel.Consistency + +namespace FramesLocalState + +theorem ofWF {action : TcM .anon α} + (framed : ∀ before, TcM.WF + (fun after => LocalStateInvariant after ∧ LocalStateFrame before after) + before action (fun _ _ => True)) : FramesLocalState action := by + intro before valid + have result := framed before ⟨valid, .refl _⟩ + cases run : action before <;> rw [run] at result <;> exact result.1.2 + +theorem tryCatch {action : TcM .anon α} {handler : TcError .anon → TcM .anon α} + (body : FramesLocalState action) (caught : ∀ error, FramesLocalState (handler error)) : + FramesLocalState (tryCatch action handler) := by + intro before valid + have first := body before valid + change match EStateM.tryCatch action handler before with + | .ok _ after | .error _ after => LocalStateFrame before after + cases run : action before with + | ok value after => + rw [EStateM.tryCatch, run] + simpa only [run] using first + | error error middle => + rw [run] at first + rw [EStateM.tryCatch, run] + change match handler error middle with + | .ok _ after | .error _ after => LocalStateFrame before after + have next := caught error middle (first.invariant valid) + cases finished : handler error middle <;> rw [finished] at next <;> exact first.trans next + +private theorem tryFinally_eq (action : TcM .anon α) (cleanup : TcM .anon β) + (before : TcState .anon) : + tryFinally action cleanup before = + match action before with + | .ok value middle => + match cleanup middle with + | .ok _ after => .ok value after + | .error error after => .error error after + | .error error middle => + match cleanup middle with + | .ok _ after => .error error after + | .error cleanupError after => .error cleanupError after := by + unfold tryFinally + change EStateM.map (fun value : α × β => value.1) + (tryFinally' action (fun _ => cleanup)) before = _ + unfold EStateM.map MonadFinally.tryFinally' EStateM.instMonadFinally + cases run : action before <;> simp only [run] <;> cases finished : cleanup _ <;> rfl + +theorem tryFinally {action : TcM .anon α} {cleanup : TcM .anon β} + (body : FramesLocalState action) (clean : FramesLocalState cleanup) : + FramesLocalState (tryFinally action cleanup) := by + intro before valid + rw [tryFinally_eq] + have first := body before valid + cases run : action before <;> rw [run] at first <;> dsimp only <;> + have next := clean _ (first.invariant valid) <;> + cases finished : cleanup _ <;> rw [finished] at next <;> exact first.trans next + +theorem map (f : α → β) {action : TcM .anon α} (body : FramesLocalState action) : + FramesLocalState (f <$> action) := by + simpa only [map_eq_pure_bind] using bind body (fun value => pure (f value)) + +theorem modifyGet {f : TcState .anon → α × TcState .anon} + (framed : ∀ before, LocalStateFrame before (f before).2) : + FramesLocalState (MonadStateOf.modifyGet f : TcM .anon α) := + fun before _ => framed before + +theorem tick : FramesLocalState (TcM.tick (m := .anon)) := by + apply ofWF + intro before + apply TcM.WF.mono (TcM.tick.wf (fun state valid => + ⟨⟨valid.1.coherent, valid.1.allocated, valid.1.loader⟩, + ⟨valid.2.counter, valid.2.context, valid.2.loader⟩⟩)) + · intros; trivial + · intros; trivial + +theorem stepTrace (tag : String) (payload : Unit → String) : + FramesLocalState (TcM.stepTrace (m := .anon) tag payload) := by + unfold TcM.stepTrace + apply bind get + intro state + split <;> exact pure _ + +theorem bumpStats (update : TcState .anon → TcState .anon) + (framed : ∀ before, LocalStateFrame before (update before)) : + FramesLocalState (TcM.bumpStats update) := by + unfold TcM.bumpStats + apply bind get + intro state + split + · exact modify update framed + · exact pure _ + +theorem isLetVar (index : UInt64) : FramesLocalState (TcM.isLetVar (m := .anon) index) := by + unfold TcM.isLetVar + apply bind get + intro state + dsimp only + split <;> exact pure _ + +theorem lookupLetVal (index : UInt64) : FramesLocalState (TcM.lookupLetVal (m := .anon) index) := by + unfold TcM.lookupLetVal + apply bind get + intro state + dsimp only + split + · exact pure _ + · split + · exact pure _ + · exact bind (runIntern _) (fun _ => pure _) + +theorem whnfKey (term : KExpr .anon) : FramesLocalState (TcM.whnfKey term) := + bind (ctxAddrForLbr term.lbr) (fun _ => pure _) + +theorem tryGetBlock (id : KId .anon) : FramesLocalState (TcM.tryGetBlock id) := by + unfold TcM.tryGetBlock + apply bind get + intro state + split + · exact pure _ + · exact bind (lazyIngressAddr id.addr) fun _ => bind get fun _ => pure _ + +theorem runBounded {methods : Methods .anon} + {step : σ → RecM .anon (RecM.BoundedStep σ α)} + (framed : ∀ state, FramesLocalState ((step state).run methods)) : + ∀ fuel state, FramesLocalState ((RecM.runBounded step fuel state).run methods) + | 0, _ => throw _ + | fuel + 1, state => by + rw [RecM.runBounded, ReaderT.run_bind] + apply bind (framed state) + intro next + cases next with + | next value => exact runBounded framed fuel value + | done value => exact pure _ + +theorem forInList {methods : Methods .anon} (items : List α) (initial : β) + (step : α → β → RecM .anon (ForInStep β)) + (framed : ∀ item value, FramesLocalState ((step item value).run methods)) : + FramesLocalState ((forIn items initial step).run methods) := by + induction items generalizing initial with + | nil => exact pure _ + | cons item rest ih => + rw [List.forIn_cons, ReaderT.run_bind] + apply bind (framed item initial) + intro next + cases next with + | done value => exact pure _ + | yield value => exact ih value + +theorem forInRange {methods : Methods .anon} (range : Std.Legacy.Range) (initial : α) + (step : Nat → α → RecM .anon (ForInStep α)) + (framed : ∀ index value, FramesLocalState ((step index value).run methods)) : + FramesLocalState ((forIn range initial step).run methods) := by + rw [Std.Legacy.Range.forIn_eq_forIn_range'] + exact forInList _ _ _ framed + +theorem forInArray {methods : Methods .anon} (items : Array α) (initial : β) + (step : α → β → RecM .anon (ForInStep β)) + (framed : ∀ item value, FramesLocalState ((step item value).run methods)) : + FramesLocalState ((forIn items initial step).run methods) := by + rcases items with ⟨items⟩ + rw [List.forIn_toArray] + exact forInList _ _ _ framed + +theorem withCheapRecursionDepth {methods : Methods .anon} {action : RecM .anon α} + (body : FramesLocalState (action.run methods)) : + FramesLocalState ((RecM.withCheapRecursionDepth action).run methods) := by + unfold RecM.withCheapRecursionDepth + simp only [ReaderT.run_bind] + apply bind + · apply modify; exact fun _ => ⟨Nat.le_refl _, .refl _, rfl⟩ + · intro _ + change FramesLocalState (_root_.tryFinally (action.run methods) + (_root_.modify fun state => {state with cheapRecursionDepth := state.cheapRecursionDepth - 1})) + apply tryFinally body + apply modify; exact fun _ => ⟨Nat.le_refl _, .refl _, rfl⟩ + +end FramesLocalState + +structure MethodsLocalState (methods : Methods .anon) : Prop where + whnf : ∀ term, FramesLocalState (methods.whnf term) + whnfCore : ∀ term, FramesLocalState (methods.whnfCore term) + whnfMode : ∀ term mode, FramesLocalState (methods.whnfMode term mode) + whnfCoreFlags : ∀ term flags, FramesLocalState (methods.whnfCoreFlags term flags) + infer : ∀ term, FramesLocalState (methods.infer term) + isDefEq : ∀ left right, FramesLocalState (methods.isDefEq left right) + +namespace FramesLocalState + +theorem whnfRec {methods : Methods .anon} (recursive : MethodsLocalState methods) + (term : KExpr .anon) : FramesLocalState ((RecM.whnfRec term).run methods) := recursive.whnf term + +theorem whnfModeRec {methods : Methods .anon} (recursive : MethodsLocalState methods) + (term : KExpr .anon) (mode : NatSuccMode) : + FramesLocalState ((RecM.whnfModeRec term mode).run methods) := recursive.whnfMode term mode + +theorem whnfCoreFlagsRec {methods : Methods .anon} (recursive : MethodsLocalState methods) + (term : KExpr .anon) (flags : WhnfFlags) : + FramesLocalState ((RecM.whnfCoreFlagsRec term flags).run methods) := recursive.whnfCoreFlags term flags + +theorem inferOnlyRec {methods : Methods .anon} (recursive : MethodsLocalState methods) + (term : KExpr .anon) : FramesLocalState ((RecM.inferOnlyRec term).run methods) := + withInferOnly (recursive.infer term) + +theorem tryOptional {methods : Methods .anon} {action : RecM .anon α} + (body : FramesLocalState (action.run methods)) : + FramesLocalState ((RecM.tryOptional action).run methods) := by + unfold RecM.tryOptional RecM.try? + exact tryCatch (bind body (fun value => pure (some value))) (fun _ => pure none) + +end FramesLocalState +end Ix.Kernel.Consistency + +namespace Ix.Kernel.Consistency.FramesLocalState + +theorem pureRec {methods : Methods .anon} (value : α) : + FramesLocalState ((Pure.pure value : RecM .anon α).run methods) := pure value + +theorem throwRec {methods : Methods .anon} (error : TcError .anon) : + FramesLocalState ((MonadExceptOf.throw error : RecM .anon α).run methods) := throw error + +theorem throwExceptRec {methods : Methods .anon} (error : TcError .anon) : + FramesLocalState ((MonadExcept.throw error : RecM .anon α).run methods) := throw error + +theorem getRec {methods : Methods .anon} : + FramesLocalState ((MonadState.get : RecM .anon (TcState .anon)).run methods) := get + +theorem liftRec {methods : Methods .anon} {action : TcM .anon α} + (body : FramesLocalState action) : + FramesLocalState ((monadLift action : RecM .anon α).run methods) := body + +theorem liftSelf {action : TcM .anon α} (body : FramesLocalState action) : + FramesLocalState (monadLift action : TcM .anon α) := body + +theorem mapRec {methods : Methods .anon} (f : α → β) {action : RecM .anon α} + (body : FramesLocalState (action.run methods)) : + FramesLocalState ((f <$> action).run methods) := map f body + +theorem bindRead {methods : Methods .anon} {next : Methods .anon → RecM .anon α} + (body : FramesLocalState ((next methods).run methods)) : + FramesLocalState (((read : RecM .anon (Methods .anon)) >>= next).run methods) := body + +theorem tryProbe {methods : Methods .anon} {action : RecM .anon α} + (body : FramesLocalState (action.run methods)) : + FramesLocalState ((RecM.try? action).run methods) := tryOptional body + +theorem bindRec {methods : Methods .anon} {action : RecM .anon α} {next : α → RecM .anon β} + (body : FramesLocalState (action.run methods)) + (rest : ∀ value, FramesLocalState ((next value).run methods)) : + FramesLocalState ((action >>= next).run methods) := bind body rest + +theorem bindTcM {methods : Methods .anon} {action : TcM .anon α} + {next : α → RecM .anon β} + (body : FramesLocalState action) + (rest : ∀ value, FramesLocalState ((next value).run methods)) : + FramesLocalState ((do let value ← action; next value : RecM .anon β).run methods) := by + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + exact bind body rest + +theorem tryFinallyRec {methods : Methods .anon} {action : RecM .anon α} {cleanup : RecM .anon β} + (body : FramesLocalState (action.run methods)) + (clean : FramesLocalState (cleanup.run methods)) : + FramesLocalState ((_root_.tryFinally action cleanup).run methods) := by + change FramesLocalState (_root_.tryFinally (action.run methods) (cleanup.run methods)) + exact tryFinally body clean + +theorem tryCatchRec {methods : Methods .anon} {action : RecM .anon α} + {handler : TcError .anon → RecM .anon α} + (body : FramesLocalState (action.run methods)) + (caught : ∀ error, FramesLocalState ((handler error).run methods)) : + FramesLocalState ((MonadExceptOf.tryCatch action handler).run methods) := tryCatch body caught + +theorem tryCatchExceptRec {methods : Methods .anon} {action : RecM .anon α} + {handler : TcError .anon → RecM .anon α} + (body : FramesLocalState (action.run methods)) + (caught : ∀ error, FramesLocalState ((handler error).run methods)) : + FramesLocalState ((MonadExcept.tryCatch action handler).run methods) := tryCatch body caught + +theorem modifyRec {methods : Methods .anon} {f : TcState .anon → TcState .anon} + (framed : ∀ before, LocalStateFrame before (f before)) : + FramesLocalState ((_root_.modify f : RecM .anon PUnit).run methods) := modify f framed + +theorem forInListTcM (items : List α) (initial : β) (step : α → β → TcM .anon (ForInStep β)) + (framed : ∀ item value, FramesLocalState (step item value)) : + FramesLocalState (forIn items initial step) := by + induction items generalizing initial with + | nil => exact pure _ + | cons item rest ih => + rw [List.forIn_cons] + apply bind (framed item initial) + intro next + cases next with + | done value => exact pure _ + | yield value => exact ih value + +end Ix.Kernel.Consistency.FramesLocalState diff --git a/Ix/Kernel/Verify/Consistency/LocalStateReading.lean b/Ix/Kernel/Verify/Consistency/LocalStateReading.lean new file mode 100644 index 000000000..af7dc791b --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/LocalStateReading.lean @@ -0,0 +1,87 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.RecursiveLocalState +import Ix.Kernel.Verify.Consistency.IngressLocalState + +/-! Connect the maintained structural invariant to the model's local reader. +Observable scope restoration preserves that reader, and the allocation bound +proves freshness for every registered model local. These facts hold for the +actual recursive checker, including its installed production loader. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model +universe u + +theorem LocalContextReading.congr {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {context : Model.Context β} {left right : LocalContext .anon} + (agreement : LocalContextReading resolve locals left context) (same : left.Equiv right) : + LocalContextReading resolve locals right context := by + refine ⟨agreement.distinct, agreement.length, ?_⟩ + intro id index registered + obtain ⟨decl, type, found, position, reading⟩ := agreement.lookup id index registered + exact ⟨decl, type, (same.find? id).symm.trans found, position, reading⟩ + +theorem localIndex?_of_mem {locals : List FVarId} {id : FVarId} (member : id ∈ locals) : + ∃ index, localIndex? locals id = some index := by + induction locals with + | nil => contradiction + | cons head rest ih => + by_cases equal : id = head + · exact ⟨0, by simp [localIndex?, equal]⟩ + · obtain ⟨index, found⟩ := ih ((List.mem_cons.mp member).resolve_left equal) + exact ⟨index + 1, by simp [localIndex?, equal, found]⟩ + +/-- Freshness comes from the maintained production counter, even when the +reader registers only part of the concrete context. -/ +theorem LocalStateInvariant.freshReading {β : Type u} + {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {context : Model.Context β} {state : TcState .anon} + (valid : LocalStateInvariant state) + (agreement : LocalContextReading resolve locals state.lctx context) : + (⟨state.env.nextFVarId⟩ : FVarId) ∉ locals := by + intro member + obtain ⟨index, registered⟩ := localIndex?_of_mem member + obtain ⟨decl, _, found, _, _⟩ := agreement.lookup _ index registered + simp [LocalContext.find?, valid.allocated.fresh] at found + +theorem FramesLocalState.ok {action : TcM .anon α} + (framed : FramesLocalState action) {before after : TcState .anon} {value : α} + (valid : LocalStateInvariant before) (run : action before = .ok value after) : + LocalStateFrame before after := by + have frame := framed before valid + rw [run] at frame + exact frame + +theorem FramesLocalState.error {action : TcM .anon α} + (framed : FramesLocalState action) {before after : TcState .anon} {error : TcError .anon} + (valid : LocalStateInvariant before) (run : action before = .error error after) : + LocalStateFrame before after := by + have frame := framed before valid + rw [run] at frame + exact frame + +theorem infer_methodsN_framesLocalState (fuel : Nat) (term : KExpr .anon) : + FramesLocalState (RecM.infer term (methodsN fuel)) := + infer_framesLocalState_of_whnf (MethodsLocalState.methodsN fuel).infer + (FramesLocalState.whnf (MethodsLocalState.methodsN fuel)) + (MethodsLocalState.methodsN fuel).isDefEq term + +theorem isDefEq_methodsN_framesLocalState (fuel : Nat) (left right : KExpr .anon) : + FramesLocalState (RecM.isDefEq left right (methodsN fuel)) := + FramesLocalState.isDefEq (MethodsLocalState.methodsN fuel) left right + +/-- A caller's model context survives the complete public inference entry. -/ +theorem infer_localReading {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {context : Model.Context β} {before after : TcState .anon} + {term type : KExpr .anon} (valid : LocalStateInvariant before) + (agreement : LocalContextReading resolve locals before.lctx context) + (run : TcM.infer term before = .ok type after) : + LocalStateInvariant after ∧ LocalContextReading resolve locals after.lctx context := by + have frame := (TcM.infer_framesLocalState term).ok valid run + exact ⟨frame.invariant valid, agreement.congr frame.context.symm⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/LocalStateTactic.lean b/Ix/Kernel/Verify/Consistency/LocalStateTactic.lean new file mode 100644 index 000000000..c76b11214 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/LocalStateTactic.lean @@ -0,0 +1,152 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.LocalStateEffects +import Lean.LabelAttribute + +/-! +# Proof search for structural local-state effects + +The tactic composes proved frame rules, unfolds finite monadic control flow, +and applies named helper lemmas or local induction premises. It only introduces +explicit quantifiers; it never unfolds a frame into an unconstrained outcome. +All generated proof terms are checked by Lean. Recursive source workers still +need induction, and recursive method calls need the smaller table's frame. +-/ + +namespace Ix.Kernel.Consistency + +register_label_attr local_state_frame + +private def frameHead : Lean.Expr → Option Lean.Name + | .forallE _ _ body _ => frameHead body + | .mdata _ body => frameHead body + | type => do + if !type.getAppFn.isConstOf ``FramesLocalState then none else do + let action ← type.getAppArgs.back? + let action ← if action.getAppFn.isConstOf ``ReaderT.run then + action.getAppArgs[action.getAppArgs.size - 2]? + else some action + match action.getAppFn with + | .const name _ => some name + | .fvar id => some id.name + | _ => none + +open Lean Elab Tactic in +elab "local_frame_goal" : tactic => do + unless (← Lean.instantiateMVars (← getMainTarget)).consumeMData.getAppFn.isConstOf ``FramesLocalState do + throwError "expected a local-state computation frame" + +open Lean Elab Tactic in +elab "local_relation_goal" : tactic => do + unless (← Lean.instantiateMVars (← getMainTarget)).consumeMData.getAppFn.isConstOf ``LocalStateFrame do + throwError "expected a relation between checker states" + +open Lean Elab Tactic in +elab "local_frame_head " head:ident : tactic => do + unless frameHead (← Lean.instantiateMVars (← getMainTarget)) == some head.getId.eraseMacroScopes do + throwError "different computation head" + +open Lean Elab Tactic in +elab "local_frame_intro" : tactic => do + unless (← Lean.instantiateMVars (← getMainTarget)).consumeMData.isForall do + throwError "expected an explicit universal quantifier" + evalTactic (← `(tactic| intro)) + +open Lean Elab Tactic Meta in +elab "local_frame_assumption" : tactic => withMainContext do + let goal ← getMainGoal + for decl in ← getLCtx do + unless decl.isImplementationDetail do + let saved ← saveState + try + if (← withReducible (goal.apply decl.toExpr)).isEmpty then + replaceMainGoal [] + return + catch _ => pure () + saved.restore + throwError "no local premise closes this frame" + +open Lean Elab Tactic Meta in +elab "local_frame_lemma" : tactic => withMainContext do + let goal ← getMainGoal + let some head := frameHead (← Lean.instantiateMVars (← goal.getType)) | + throwError "expected a named computation" + for name in (← labelled `local_state_frame).reverse do + if frameHead (← getConstInfo name).type != some head then continue + let saved ← saveState + try + let goals ← withReducible (goal.apply (← mkConstWithFreshMVarLevels name)) + for subgoal in goals do + subgoal.withContext subgoal.assumption + replaceMainGoal [] + return + catch _ => saved.restore + throwError "no registered lemma closes this frame" + +macro "local_state" : tactic => `(tactic| + with_reducible repeat' first + | assumption + | local_frame_assumption + | (local_relation_goal; exact ⟨Nat.le_refl _, .refl _, rfl⟩) + | local_frame_intro + | (local_frame_goal; first + | local_frame_lemma + | apply FramesLocalState.bindRead + | exact FramesLocalState.pureRec _ + | exact FramesLocalState.throwRec _ + | exact FramesLocalState.throwExceptRec _ + | exact FramesLocalState.getRec + | (local_frame_head MonadLiftT.monadLift; apply FramesLocalState.liftRec) + | (local_frame_head MonadLiftT.monadLift; apply FramesLocalState.liftSelf) + | (with_reducible apply FramesLocalState.bindRec) + | (with_reducible apply FramesLocalState.bind) + | (local_frame_head Pure.pure; exact FramesLocalState.pure _) + | (local_frame_head MonadExceptOf.throw; exact FramesLocalState.throw _) + | exact FramesLocalState.intern _ + | exact FramesLocalState.runIntern _ + | exact FramesLocalState.get + | exact FramesLocalState.instantiateUnivParams _ _ + | exact FramesLocalState.prims _ + | exact FramesLocalState.tryGetConst _ + | exact FramesLocalState.getConst _ + | exact FramesLocalState.tryGetBlock _ + | exact FramesLocalState.inferKey _ + | exact FramesLocalState.whnfKey _ + | exact FramesLocalState.tick + | exact FramesLocalState.isLetVar _ + | exact FramesLocalState.lookupLetVal _ + | exact FramesLocalState.stepTrace _ _ + | exact FramesLocalState.whnfRec (by assumption) _ + | exact FramesLocalState.whnfModeRec (by assumption) _ _ + | exact FramesLocalState.whnfCoreFlagsRec (by assumption) _ _ + | exact FramesLocalState.inferOnlyRec (by assumption) _ + | exact MethodsLocalState.infer (by assumption) _ + | apply FramesLocalState.modifyRec + | apply FramesLocalState.modify + | apply FramesLocalState.bumpStats + | apply FramesLocalState.bindRec + | apply FramesLocalState.bind + | apply FramesLocalState.map + | apply FramesLocalState.mapRec + | apply FramesLocalState.forInList + | apply FramesLocalState.forInRange + | apply FramesLocalState.forInArray + | apply FramesLocalState.forInListTcM + | apply FramesLocalState.tryFinallyRec + | apply FramesLocalState.tryCatchRec + | apply FramesLocalState.tryCatchExceptRec + | apply FramesLocalState.tryFinally + | apply FramesLocalState.tryCatch + | apply FramesLocalState.runBounded + | apply FramesLocalState.withCheapRecursionDepth + | apply FramesLocalState.withInferOnly + | apply FramesLocalState.tryOptional + | apply FramesLocalState.tryProbe + | split + | dsimp only + | dsimp only [ReaderT.run_bind, ReaderT.run_monadLift])) + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/ProjectionLocalState.lean b/Ix/Kernel/Verify/Consistency/ProjectionLocalState.lean new file mode 100644 index 000000000..eb46a1dbb --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/ProjectionLocalState.lean @@ -0,0 +1,231 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.InferenceLocalState + +/-! +# Local-state preservation by projection inference + +The production parameter and field traversals preserve the caller's locals, +allocation bound and installed loader, on both success and partial failure. +The proof includes inductive result-sort classification, dependent field +substitution and Prop elimination checks. Recursive inference and direct +WHNF remain the smaller method contracts; lookup and universe-instantiation +effects are derived from the maintained local-state invariant. + +This closes the projection premise of the general inference state frame. +Projection typing and the model interpretation of inductive declarations +remain separate semantic obligations. +-/ + +namespace Ix.Kernel.Consistency.FramesLocalState + +private theorem forInList {methods : Methods .anon} (items : List α) (initial : β) + (step : α → β → RecM .anon (ForInStep β)) + (framed : ∀ item value, FramesLocalState ((step item value).run methods)) : + FramesLocalState ((forIn items initial step).run methods) := by + induction items generalizing initial with + | nil => exact pure _ + | cons item rest ih => + rw [List.forIn_cons] + simp only [ReaderT.run_bind] + apply bind (framed item initial) + intro next + cases next with + | done value => exact pure _ + | yield value => exact ih value + +private theorem forInRange {methods : Methods .anon} (range : Std.Legacy.Range) (initial : α) + (step : Nat → α → RecM .anon (ForInStep α)) + (framed : ∀ index value, FramesLocalState ((step index value).run methods)) : + FramesLocalState ((forIn range initial step).run methods) := by + rw [Std.Legacy.Range.forIn_eq_forIn_range'] + exact forInList _ _ _ framed + +theorem peelProjForall {methods : Methods .anon} + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) + (type : KExpr .anon) (error : String) : + FramesLocalState ((RecM.peelProjForall type error).run methods) := by + cases type <;> simp only [RecM.peelProjForall, ReaderT.run_bind] + case all => exact pure _ + all_goals + apply bind (whnf _) + intro result + cases result <;> first | exact pure _ | exact throw _ + +theorem instantiateProjParamStep {methods : Methods .anon} + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) + (args : Array (KExpr .anon)) (index : Nat) (type : KExpr .anon) : + FramesLocalState ((RecM.instantiateProjParamStep args index type).run methods) := by + unfold RecM.instantiateProjParamStep + simp only [ReaderT.run_bind] + apply bind (peelProjForall whnf _ _) + intro pair + split + · exact bind (runIntern _) (fun _ => pure _) + · exact throw _ + +theorem instantiateProjParams {methods : Methods .anon} + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) + (args : Array (KExpr .anon)) (numParams : Nat) (type : KExpr .anon) : + FramesLocalState ((RecM.instantiateProjParams args numParams type).run methods) := + forInRange _ _ _ (instantiateProjParamStep whnf args) + +theorem inductiveAppBinderStep {methods : Methods .anon} + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) + (type : KExpr .anon) : + FramesLocalState ((RecM.inductiveAppBinderStep type).run methods) := by + unfold RecM.inductiveAppBinderStep + simp only [ReaderT.run_bind] + apply bind (whnf type) + intro result + cases result <;> first | exact pure _ | exact throw _ + +theorem inductiveAppBinders {methods : Methods .anon} + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) + (binders : Nat) (type : KExpr .anon) : + FramesLocalState ((RecM.inductiveAppBinders binders type).run methods) := + forInRange _ _ _ (fun _ => inductiveAppBinderStep whnf) + +theorem inductiveAppResultIsProp {methods : Methods .anon} + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) + (type : KExpr .anon) : + FramesLocalState ((RecM.inductiveAppResultIsProp type).run methods) := by + unfold RecM.inductiveAppResultIsProp + simp only [ReaderT.run_bind] + exact bind (whnf type) fun sortType => bind (ensureSortDirect whnf sortType) fun _ => pure _ + +theorem inductiveAppIsProp {methods : Methods .anon} + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) + (id : KId .anon) (levels : Array (KUniv .anon)) (binders : Nat) : + FramesLocalState ((RecM.inductiveAppIsProp id levels binders).run methods) := by + unfold RecM.inductiveAppIsProp + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + apply bind (tryGetConst id) + intro constant + split + · simp only [ReaderT.run_bind, pure_bind, ReaderT.run_monadLift] + apply bind (instantiateUnivParams _ levels) + intro instantiated + exact bind (inductiveAppBinders whnf binders instantiated) (inductiveAppResultIsProp whnf) + · exact fun before _ => .refl _ + +theorem inferProjFieldStep {methods : Methods .anon} + (recursive : ∀ term, FramesLocalState (methods.infer term)) + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) + (id : KId .anon) (field : UInt64) (value : KExpr .anon) + (prop : Bool) (index : Nat) (type : KExpr .anon) : + FramesLocalState ((RecM.inferProjFieldStep id field value prop index type).run methods) := by + unfold RecM.inferProjFieldStep + simp only [ReaderT.run_bind] + apply bind (peelProjForall whnf _ _) + intro pair + rcases pair with ⟨domain, body⟩ + split + · cases prop with + | false => exact pure _ + | true => + simp only [if_true, ReaderT.run_bind] + apply bind (recursive domain) + intro domainType + apply bind (ensureSortDirect whnf domainType) + intro level + split + · exact throw _ + · exact pure _ + · have tail : FramesLocalState ((do + let projection ← TcM.intern (.mkPrj id index.toUInt64 value) + let result ← TcM.runIntern (subst body projection 0) + return ForInStep.yield result : RecM .anon (ForInStep (KExpr .anon))).run methods) := by + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + exact bind (intern _) fun _ => bind (runIntern _) fun _ => pure _ + cases prop with + | false => exact tail + | true => + simp only [if_true, ReaderT.run_bind] + apply bind (recursive domain) + intro domainType + apply bind (ensureSortDirect whnf domainType) + intro level + split + · exact throw _ + · exact tail + +theorem inferProjFieldsLoopStep {methods : Methods .anon} + (recursive : ∀ term, FramesLocalState (methods.infer term)) + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) + (id : KId .anon) (field : UInt64) (value : KExpr .anon) + (prop : Bool) (index : Nat) (state : Option (KExpr .anon) × KExpr .anon) : + FramesLocalState ((RecM.inferProjFieldsLoopStep id field value prop index state).run methods) := by + unfold RecM.inferProjFieldsLoopStep + simp only [ReaderT.run_bind] + exact bind (inferProjFieldStep recursive whnf id field value prop index state.2) + fun next => by cases next <;> exact pure _ + +theorem inferProjFields {methods : Methods .anon} + (recursive : ∀ term, FramesLocalState (methods.infer term)) + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) + (id : KId .anon) (field : UInt64) (value : KExpr .anon) + (prop : Bool) (type : KExpr .anon) : + FramesLocalState ((RecM.inferProjFields id field value prop type).run methods) := by + unfold RecM.inferProjFields + simp only [ReaderT.run_bind] + apply bind (forInRange _ _ _ (inferProjFieldsLoopStep recursive whnf id field value prop)) + intro pair + cases pair.1 <;> first | exact pure _ | exact throw _ + +theorem inferProj {methods : Methods .anon} + (recursive : ∀ term, FramesLocalState (methods.infer term)) + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) + (id : KId .anon) (field : UInt64) (value type : KExpr .anon) : + FramesLocalState ((RecM.inferProj id field value type).run methods) := by + unfold RecM.inferProj + simp only [ReaderT.run_bind] + apply bind (whnf type) + intro reduced + generalize reduced.collectSpine = pair + rcases pair with ⟨head, args⟩ + dsimp only + split + · split + · exact throw _ + · simp only [ReaderT.run_bind, ReaderT.run_monadLift] + apply bind (tryGetConst _) + intro constant + split + · simp only [pure_bind] + split + · exact fun before _ => .refl _ + · simp only [ReaderT.run_bind, ReaderT.run_monadLift] + apply bind (inductiveAppIsProp whnf _ _ _) + intro prop + apply bind (tryGetConst _) + intro constructor + cases constructor with + | none => exact fun before _ => .refl _ + | some constant => + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + apply bind (instantiateUnivParams constant.ty _) + intro instantiated + apply bind (instantiateProjParams whnf args _ instantiated) + intro parameterized + exact inferProjFields recursive whnf id field value prop parameterized + · exact fun before _ => .refl _ + · exact throw _ + +end Ix.Kernel.Consistency.FramesLocalState + +namespace Ix.Kernel.Consistency + +/-- Projection inference is discharged by the same smaller inference table +and direct reduction contract as the rest of the production inference body. -/ +theorem infer_framesLocalState_of_whnf {methods : Methods .anon} + (recursive : ∀ term, FramesLocalState (methods.infer term)) + (whnf : ∀ term, FramesLocalState ((RecM.whnf term).run methods)) + (conversion : ∀ left right, FramesLocalState (methods.isDefEq left right)) + (term : KExpr .anon) : FramesLocalState ((RecM.infer term).run methods) := + infer_framesLocalState recursive whnf conversion (FramesLocalState.inferProj recursive whnf) term + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/RecursiveLocalState.lean b/Ix/Kernel/Verify/Consistency/RecursiveLocalState.lean new file mode 100644 index 000000000..4a4931a89 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/RecursiveLocalState.lean @@ -0,0 +1,88 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.DefEqLocalState +import Ix.Kernel.Verify.Consistency.ProjectionLocalState + +/-! +# Structural local-state preservation for the complete recursive knot + +Induction on the actual production method-table depth closes the six mutually +dependent state contracts. Public entries select that table from the current +recursive fuel, exactly as `TcM.runRec` does. There are no recursive-method or +loader-effect premises beyond the maintained initial local-state invariant. + +The result preserves local declarations, monotone fresh allocation and the +installed loader on both outcomes. Reduction, conversion and inference still +need their separate semantic correctness proofs for general consistency. +-/ + +namespace Ix.Kernel.Consistency + +/-- Every finite production table satisfies the complete structural contract. +The successor case uses only the strictly smaller table's callbacks. -/ +theorem MethodsLocalState.methodsN (depth : Nat) : + MethodsLocalState (_root_.Ix.Kernel.methodsN (m := .anon) depth) := by + induction depth with + | zero => + exact { + whnf := fun _ => FramesLocalState.throw _ + whnfCore := fun _ => FramesLocalState.throw _ + whnfMode := fun _ _ => FramesLocalState.throw _ + whnfCoreFlags := fun _ _ => FramesLocalState.throw _ + infer := fun _ => FramesLocalState.throw _ + isDefEq := fun _ _ => FramesLocalState.throw _ } + | succ depth recursive => + exact { + whnf := FramesLocalState.whnf recursive + whnfCore := FramesLocalState.whnfCore recursive + whnfMode := FramesLocalState.whnfWithNatSuccMode recursive + whnfCoreFlags := FramesLocalState.whnfCoreWithFlags recursive + infer := infer_framesLocalState_of_whnf recursive.infer + (FramesLocalState.whnf recursive) recursive.isDefEq + isDefEq := FramesLocalState.isDefEq recursive } + +/-- Running from arbitrary current fuel uses a proved finite table, including +the exhausted-depth table and every error-side partial state. -/ +theorem FramesLocalState.runRec {action : RecM .anon α} + (framed : ∀ methods, MethodsLocalState methods → FramesLocalState (action.run methods)) : + FramesLocalState (TcM.runRec action) := by + intro before valid + exact framed (_root_.Ix.Kernel.methodsN before.recFuel.toNat) + (MethodsLocalState.methodsN before.recFuel.toNat) before valid + +end Ix.Kernel.Consistency + +namespace Ix.Kernel.TcM + +open Consistency + +theorem whnf_framesLocalState (term : KExpr .anon) : FramesLocalState (whnf term) := + FramesLocalState.runRec fun _ recursive => FramesLocalState.whnf recursive term + +theorem whnfCore_framesLocalState (term : KExpr .anon) : FramesLocalState (whnfCore term) := + FramesLocalState.runRec fun _ recursive => FramesLocalState.whnfCore recursive term + +theorem whnfNoDelta_framesLocalState (term : KExpr .anon) : FramesLocalState (whnfNoDelta term) := + FramesLocalState.runRec fun _ recursive => FramesLocalState.whnfNoDelta recursive term + +theorem infer_framesLocalState (term : KExpr .anon) : FramesLocalState (infer term) := + FramesLocalState.runRec fun _ recursive => + infer_framesLocalState_of_whnf recursive.infer + (FramesLocalState.whnf recursive) recursive.isDefEq term + +theorem isDefEq_framesLocalState (left right : KExpr .anon) : + FramesLocalState (isDefEq left right) := + FramesLocalState.runRec fun _ recursive => FramesLocalState.isDefEq recursive left right + +theorem ensureSort_framesLocalState (term : KExpr .anon) : FramesLocalState (ensureSort term) := + FramesLocalState.runRec fun _ recursive => + FramesLocalState.ensureSortDirect (FramesLocalState.whnf recursive) term + +theorem ensureForall_framesLocalState (term : KExpr .anon) : FramesLocalState (ensureForall term) := + FramesLocalState.runRec fun _ recursive => + FramesLocalState.ensureForallDirect (FramesLocalState.whnf recursive) term + +end Ix.Kernel.TcM diff --git a/Ix/Kernel/Verify/Consistency/WhnfLocalState.lean b/Ix/Kernel/Verify/Consistency/WhnfLocalState.lean new file mode 100644 index 000000000..584d7dfae --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/WhnfLocalState.lean @@ -0,0 +1,896 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.LocalStateTactic + +/-! +# Structural local-state preservation for production reduction + +The actual full, structural and no-delta WHNF drivers preserve the caller's +local declarations and installed loader and never decrease the fresh-variable +counter, on success and error. The proofs cover primitive reduction, String +expansion, projection, iota, recursor classification, caches, finite workers, +and cleanup. Only calls through the recursive method table require a frame +premise; every direct reduction helper is discharged here. + +This is the structural state component of consistency. It does not assert +the semantic validity of a cached expression, reduction, or loaded declaration. +-/ + +namespace Ix.Kernel.Consistency + +namespace FramesLocalState + +-- Apply the proved intern effects without expanding the intern implementation +-- while elaborating a long sequence of requests. +attribute [local irreducible] TcM.intern TcM.runIntern + +@[local_state_frame] theorem mkNatSucc {methods : Methods .anon} + (pred : KExpr .anon) : + FramesLocalState ((RecM.mkNatSucc pred).run methods) := by + unfold RecM.mkNatSucc + local_state + +@[local_state_frame] theorem mkNatAdd {methods : Methods .anon} + (left right : KExpr .anon) : + FramesLocalState ((RecM.mkNatAdd left right).run methods) := by + unfold RecM.mkNatAdd + local_state + +@[local_state_frame] theorem isNatBinArithAddr {methods : Methods .anon} + (addr : Address) : + FramesLocalState ((RecM.isNatBinArithAddr addr).run methods) := by + unfold RecM.isNatBinArithAddr + local_state + +@[local_state_frame] theorem isNatBinPredAddr {methods : Methods .anon} + (addr : Address) : + FramesLocalState ((RecM.isNatBinPredAddr addr).run methods) := by + unfold RecM.isNatBinPredAddr + local_state + +@[local_state_frame] theorem boolLitValue {methods : Methods .anon} + (term : KExpr .anon) : + FramesLocalState ((RecM.boolLitValue term).run methods) := by + unfold RecM.boolLitValue + local_state + +@[local_state_frame] theorem isNatStuckRecursorAddr {methods : Methods .anon} + (addr : Address) : + FramesLocalState ((RecM.isNatStuckRecursorAddr addr).run methods) := by + unfold RecM.isNatStuckRecursorAddr + local_state + + +@[local_state_frame] theorem unfoldConstValue {methods : Methods .anon} (head value : KExpr .anon) + (levels : Array (KUniv .anon)) : + FramesLocalState ((RecM.unfoldConstValue head value levels).run methods) := by + unfold RecM.unfoldConstValue + local_state + +@[local_state_frame] theorem natToConstructor {methods : Methods .anon} (value : Nat) : + FramesLocalState ((RecM.natToConstructor (m := .anon) value).run methods) := by + unfold RecM.natToConstructor + local_state + +@[local_state_frame] theorem tryDeltaUnfold {methods : Methods .anon} (term : KExpr .anon) : + FramesLocalState ((RecM.tryDeltaUnfold term).run methods) := by + unfold RecM.tryDeltaUnfold + local_state + +@[local_state_frame] theorem pushLocal (type : KExpr .anon) : FramesLocalState (TcM.pushLocal type) := + fun _ _ => ⟨Nat.le_refl _, .refl _, rfl⟩ + +@[local_state_frame] theorem pushLet (type value : KExpr .anon) : FramesLocalState (TcM.pushLet type value) := + fun _ _ => ⟨Nat.le_refl _, .refl _, rfl⟩ + +@[local_state_frame] theorem popLocal : FramesLocalState (TcM.popLocal (m := .anon)) := + fun _ _ => ⟨Nat.le_refl _, .refl _, rfl⟩ + +@[local_state_frame] theorem saveDepth : FramesLocalState (TcM.saveDepth (m := .anon)) := + fun _ _ => .refl _ + +@[local_state_frame] theorem enterDispatch : FramesLocalState (RecM.enterDispatch (m := .anon)) := by + apply ofWF + intro before + unfold RecM.enterDispatch + apply TcM.WF.bind + (Q₁ := fun observed after => observed = before ∧ after = before) + (TcM.WF.get fun _ => ⟨rfl, rfl⟩) + rintro observed after ⟨rfl, rfl⟩ + dsimp only + split + · exact TcM.WF.throw (fun _ => trivial) + · exact TcM.WF.set (fun valid => + ⟨⟨valid.1.coherent, valid.1.allocated, valid.1.loader⟩, + ⟨valid.2.counter, valid.2.context, valid.2.loader⟩⟩) (fun _ => trivial) + +@[local_state_frame] theorem exitDispatch : FramesLocalState (RecM.exitDispatch (m := .anon)) := + fun _ _ => ⟨Nat.le_refl _, .refl _, rfl⟩ + +@[local_state_frame] theorem callIsDefEq {methods : Methods .anon} (recursive : MethodsLocalState methods) + (left right : KExpr .anon) : + FramesLocalState ((RecM.callIsDefEq left right).run methods) := by + unfold RecM.callIsDefEq + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + apply bind enterDispatch + intro _ + change FramesLocalState (_root_.tryFinally (methods.isDefEq left right) (RecM.exitDispatch (m := .anon))) + exact tryFinally (recursive.isDefEq left right) exitDispatch + +@[local_state_frame] theorem evalNatOffsetLiteralFuel {methods : Methods .anon} (fuel : Nat) (term : KExpr .anon) : + FramesLocalState ((RecM.evalNatOffsetLiteralFuel fuel term).run methods) := by + induction fuel generalizing term with + | zero => exact pure _ + | succ fuel ih => + rw [RecM.evalNatOffsetLiteralFuel] + local_state + +@[local_state_frame] theorem natOffsetFuel {methods : Methods .anon} (fuel : Nat) (term : KExpr .anon) : + FramesLocalState ((RecM.natOffsetFuel fuel term).run methods) := by + induction fuel generalizing term with + | zero => exact pure _ + | succ fuel ih => + have literal := evalNatOffsetLiteralFuel (methods := methods) fuel + rw [RecM.natOffsetFuel] + rcases spine : term.collectSpine with ⟨head, args⟩ + dsimp only + cases head <;> dsimp only + all_goals try exact pure _ + simp only [ReaderT.run_bind] + apply bind (prims methods) + intro primitives + split + · exact bind (ih _) (fun _ => pure _) + · split + · apply bind (literal _) + intro result + cases result with + | none => exact pure _ + | some value => exact bind (ih _) (fun _ => pure _) + · exact pure _ + +@[local_state_frame] theorem finishAppResult {methods : Methods .anon} (term : KExpr .anon) + (args : Array (KExpr .anon)) (consumed : Nat) : + FramesLocalState ((RecM.finishAppResult term args consumed).run methods) := by + unfold RecM.finishAppResult + local_state + +@[local_state_frame] theorem tryReduceNativeMarker {methods : Methods .anon} (recursive : MethodsLocalState methods) + (primitives : Primitives .anon) (isBool : Bool) (id : KId .anon) (levels : Array (KUniv .anon)) : + FramesLocalState ((RecM.tryReduceNativeMarker primitives isBool id levels).run methods) := by + unfold RecM.tryReduceNativeMarker + local_state + + +@[local_state_frame] theorem strLitListToConstructor {methods : Methods .anon} + (charOfNat cons : KExpr .anon) (chars : List Char) (tail : KExpr .anon) : + FramesLocalState ((RecM.strLitListToConstructor charOfNat cons chars tail).run methods) := by + induction chars generalizing tail with + | nil => exact pure _ + | cons char chars ih => + rw [RecM.strLitListToConstructor] + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + exact bind (intern _) fun _ => bind (intern _) fun _ => + bind (intern _) fun _ => bind (intern _) ih + +@[local_state_frame] theorem natOffset {methods : Methods .anon} + (term : KExpr .anon) (depth : Nat) : + FramesLocalState ((RecM.natOffset term depth).run methods) := by + unfold RecM.natOffset + local_state + +@[local_state_frame] theorem natOffsetOrZero {methods : Methods .anon} + (term : KExpr .anon) (depth : Nat) : + FramesLocalState ((RecM.natOffsetOrZero term depth).run methods) := by + unfold RecM.natOffsetOrZero + local_state + +@[local_state_frame] theorem evalNatOffsetLiteral {methods : Methods .anon} + (term : KExpr .anon) (depth : Nat) : + FramesLocalState ((RecM.evalNatOffsetLiteral term depth).run methods) := by + unfold RecM.evalNatOffsetLiteral + local_state + +@[local_state_frame] theorem natOffsetDecompose {methods : Methods .anon} + (term : KExpr .anon) : + FramesLocalState ((RecM.natOffsetDecompose term).run methods) := by + unfold RecM.natOffsetDecompose + local_state + +@[local_state_frame] theorem natOffsetRebuild {methods : Methods .anon} + (base : Option (KExpr .anon)) (offset : Nat) : + FramesLocalState ((RecM.natOffsetRebuild base offset).run methods) := by + unfold RecM.natOffsetRebuild + local_state + +@[local_state_frame] theorem strLitToConstructor {methods : Methods .anon} + (value : String) : + FramesLocalState ((RecM.strLitToConstructor value).run methods) := by + unfold RecM.strLitToConstructor + with_reducible refine bindRec (prims methods) ?_ + intro p + refine bindTcM (intern (.mkConst p.charType #[])) ?_ + intro charType + refine bindTcM (intern (.mkConst p.charOfNat #[])) ?_ + intro charOfNat + refine bindTcM (intern (.mkConst p.stringOfList #[])) ?_ + intro stringMk + refine bindTcM (intern (.mkConst p.listNil #[.mkZero])) ?_ + intro listNil + refine bindTcM (intern (KExpr.mkApp listNil charType)) ?_ + intro nil + refine bindTcM (intern (.mkConst p.listCons #[.mkZero])) ?_ + intro listCons + refine bindTcM (intern (KExpr.mkApp listCons charType)) ?_ + intro cons + refine bindRec (strLitListToConstructor charOfNat cons value.toList.reverse nil) ?_ + intro list + exact liftRec (intern (KExpr.mkApp stringMk list)) + +@[local_state_frame] theorem internIntLit {methods : Methods .anon} + (value : _root_.Int) : + FramesLocalState ((RecM.internIntLit value).run methods) := by + unfold RecM.internIntLit + local_state + +@[local_state_frame] theorem applyIotaArg {methods : Methods .anon} + (term arg : KExpr .anon) (transient : Bool) : + FramesLocalState ((RecM.applyIotaArg term arg transient).run methods) := by + unfold RecM.applyIotaArg + local_state + +@[local_state_frame] theorem applyIotaArgs {methods : Methods .anon} + (term : KExpr .anon) (args : Array (KExpr .anon)) (transient : Bool) : + FramesLocalState ((RecM.applyIotaArgs term args transient).run methods) := by + unfold RecM.applyIotaArgs + local_state + +@[local_state_frame] theorem applyIotaRule {methods : Methods .anon} + (rule : RecRule .anon) (levels : Array (KUniv .anon)) + (recursor : IotaInfo .anon) (spine args : Array (KExpr .anon)) (fields : Nat) (transient : Bool) : + FramesLocalState ((RecM.applyIotaRule rule levels recursor spine args fields transient).run methods) := by + unfold RecM.applyIotaRule + local_state + +@[local_state_frame] theorem tryApplyIotaCtor {methods : Methods .anon} + (recursor : IotaInfo .anon) (levels : Array (KUniv .anon)) + (spine args : Array (KExpr .anon)) (index fields : Nat) (transient : Bool) : + FramesLocalState ((RecM.tryApplyIotaCtor recursor levels spine args index fields transient).run methods) := by + unfold RecM.tryApplyIotaCtor + local_state + +@[local_state_frame] theorem isNatLiteralRecursorApp {methods : Methods .anon} + (term : KExpr .anon) : + FramesLocalState ((RecM.isNatLiteralRecursorApp term).run methods) := by + unfold RecM.isNatLiteralRecursorApp + local_state + +@[local_state_frame] theorem isTransientNatLiteralWork {methods : Methods .anon} + (term : KExpr .anon) : + FramesLocalState ((RecM.isTransientNatLiteralWork term).run methods) := by + unfold RecM.isTransientNatLiteralWork + local_state + +@[local_state_frame] theorem cleanupNatOffsetMajor {methods : Methods .anon} + (term : KExpr .anon) : + FramesLocalState ((RecM.cleanupNatOffsetMajor term).run methods) := by + unfold RecM.cleanupNatOffsetMajor + local_state + +@[local_state_frame] theorem projectDecidableFinValMinor {methods : Methods .anon} + (id : KId .anon) (field : UInt64) (minor : KExpr .anon) : + FramesLocalState ((RecM.projectDecidableFinValMinor id field minor).run methods) := by + unfold RecM.projectDecidableFinValMinor + local_state + +@[local_state_frame] theorem tryReduceFinValDecidableRec {methods : Methods .anon} + (id : KId .anon) (field : UInt64) + (head : KExpr .anon) (args : Array (KExpr .anon)) : + FramesLocalState ((RecM.tryReduceFinValDecidableRec id field head args).run methods) := by + unfold RecM.tryReduceFinValDecidableRec + local_state + +@[local_state_frame] theorem tryReduceProjectionDefinition {methods : Methods .anon} + (term : KExpr .anon) : + FramesLocalState ((RecM.tryReduceProjectionDefinition term).run methods) := by + unfold RecM.tryReduceProjectionDefinition + local_state + +@[local_state_frame] theorem natRecLiteralParts {methods : Methods .anon} + (term : KExpr .anon) : + FramesLocalState ((RecM.natRecLiteralParts term).run methods) := by + unfold RecM.natRecLiteralParts + local_state + +@[local_state_frame] theorem isStuckNatPredicateProbe {methods : Methods .anon} + (term : KExpr .anon) : + FramesLocalState ((RecM.isStuckNatPredicateProbe term).run methods) := by + unfold RecM.isStuckNatPredicateProbe + local_state + +@[local_state_frame] theorem bitvecOfNatArgs {methods : Methods .anon} + (term : KExpr .anon) : + FramesLocalState ((RecM.bitvecOfNatArgs term).run methods) := by + unfold RecM.bitvecOfNatArgs + local_state + +@[local_state_frame] theorem charOfNatExpr {methods : Methods .anon} + (value : Nat) : + FramesLocalState ((RecM.charOfNatExpr value).run methods) := by + unfold RecM.charOfNatExpr + local_state + +@[local_state_frame] theorem tryReduceStringLiteral {methods : Methods .anon} + (primitives : Primitives .anon) (id : KId .anon) (value : String) : + FramesLocalState ((RecM.tryReduceStringLiteral primitives id value).run methods) := by + unfold RecM.tryReduceStringLiteral + local_state + +@[local_state_frame] theorem tryReduceString {methods : Methods .anon} + (term : KExpr .anon) : + FramesLocalState ((RecM.tryReduceString term).run methods) := by + unfold RecM.tryReduceString + local_state + +@[local_state_frame] theorem discoverBlockInductives {methods : Methods .anon} + (block : KId .anon) : + FramesLocalState ((RecM.discoverBlockInductives block).run methods) := by + unfold RecM.discoverBlockInductives + local_state + +@[local_state_frame] theorem tryProjReduceTail {methods : Methods .anon} + (id : KId .anon) (field : UInt64) (value : KExpr .anon) : + FramesLocalState ((RecM.tryProjReduceTail id field value).run methods) := by + unfold RecM.tryProjReduceTail + local_state + +@[local_state_frame] theorem tryProjPrepare {methods : Methods .anon} + (recursive : MethodsLocalState methods) (value : KExpr .anon) : + FramesLocalState ((RecM.tryProjPrepare value).run methods) := by + rw [RecM.tryProjPrepare_eq] + local_state + +@[local_state_frame] theorem tryProjReduce {methods : Methods .anon} + (recursive : MethodsLocalState methods) (id : KId .anon) (field : UInt64) (value : KExpr .anon) : + FramesLocalState ((RecM.tryProjReduce id field value).run methods) := by + rw [RecM.tryProjReduce_eq] + local_state + +@[local_state_frame] theorem tryProjAppReduce {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) (flags : WhnfFlags) : + FramesLocalState ((RecM.tryProjAppReduce term flags).run methods) := by + unfold RecM.tryProjAppReduce + local_state + +@[local_state_frame] theorem tryProjAppReduceFinished {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) (flags : WhnfFlags) : + FramesLocalState ((RecM.tryProjAppReduceFinished term flags).run methods) := by + unfold RecM.tryProjAppReduceFinished + local_state + + +@[local_state_frame] theorem restoreDepthGo (saved fuel : Nat) : + FramesLocalState (TcM.restoreDepth.go (m := .anon) saved fuel) := by + induction fuel with + | zero => exact pure _ + | succ fuel ih => + rw [TcM.restoreDepth.go] + local_state + +@[local_state_frame] theorem restoreDepth (saved : Nat) : + FramesLocalState (TcM.restoreDepth (m := .anon) saved) := by + unfold TcM.restoreDepth + local_state + +@[local_state_frame] theorem peelMajorForalls {methods : Methods .anon} + (recursive : MethodsLocalState methods) (fuel : Nat) (type : KExpr .anon) : + FramesLocalState ((RecM.peelMajorForalls fuel type).run methods) := by + induction fuel generalizing type with + | zero => exact pure _ + | succ fuel ih => + rw [RecM.peelMajorForalls] + local_state + +@[local_state_frame] theorem scanMajorInductiveStep {methods : Methods .anon} + (next : KExpr .anon → RecM .anon (KId .anon)) + (framed : ∀ type, FramesLocalState ((next type).run methods)) (type : KExpr .anon) : + FramesLocalState ((RecM.scanMajorInductiveStep next type).run methods) := by + unfold RecM.scanMajorInductiveStep + local_state + +@[local_state_frame] theorem scanMajorInductive {methods : Methods .anon} + (recursive : MethodsLocalState methods) (fuel : Nat) (type : KExpr .anon) : + FramesLocalState ((RecM.scanMajorInductive fuel type).run methods) := by + induction fuel generalizing type with + | zero => exact throw _ + | succ fuel ih => + rw [RecM.scanMajorInductive, ReaderT.run_bind] + exact bind (whnfRec recursive type) (scanMajorInductiveStep _ ih) + +@[local_state_frame] theorem getMajorInductiveId {methods : Methods .anon} + (recursive : MethodsLocalState methods) (type : KExpr .anon) (skip : UInt64) : + FramesLocalState ((RecM.getMajorInductiveId type skip).run methods) := by + unfold RecM.getMajorInductiveId + local_state + +@[local_state_frame] theorem computeIsRecParamStepAfterWhnf {methods : Methods .anon} + (type reduced : KExpr .anon) : + FramesLocalState ((RecM.computeIsRecParamStepAfterWhnf type reduced).run methods) := by + unfold RecM.computeIsRecParamStepAfterWhnf + local_state + +@[local_state_frame] theorem computeIsRecParamStep {methods : Methods .anon} + (recursive : MethodsLocalState methods) (type : KExpr .anon) : + FramesLocalState ((RecM.computeIsRecParamStep type).run methods) := by + unfold RecM.computeIsRecParamStep + local_state + +@[local_state_frame] theorem computeIsRecFieldStepAfterWhnf {methods : Methods .anon} + (blocks : Array Address) (reduced : KExpr .anon) : + FramesLocalState ((RecM.computeIsRecFieldStepAfterWhnf blocks reduced).run methods) := by + unfold RecM.computeIsRecFieldStepAfterWhnf + local_state + +@[local_state_frame] theorem computeIsRecFieldStep {methods : Methods .anon} + (recursive : MethodsLocalState methods) (blocks : Array Address) (type : KExpr .anon) : + FramesLocalState ((RecM.computeIsRecFieldStep blocks type).run methods) := by + unfold RecM.computeIsRecFieldStep + local_state + +@[local_state_frame] theorem computeIsRecCtor {methods : Methods .anon} + (recursive : MethodsLocalState methods) (type : KExpr .anon) + (params : Nat) (blocks : Array Address) : + FramesLocalState ((RecM.computeIsRecCtor type params blocks).run methods) := by + unfold RecM.computeIsRecCtor + local_state + +@[local_state_frame] theorem computeIsRec {methods : Methods .anon} + (recursive : MethodsLocalState methods) (ctors : Array (KId .anon)) + (params : Nat) (blocks : Array Address) : + FramesLocalState ((RecM.computeIsRec ctors params blocks).run methods) := by + unfold RecM.computeIsRec + local_state + +@[local_state_frame] theorem cacheIsRec {methods : Methods .anon} + (ind : KId .anon) (value : Bool) : + FramesLocalState ((RecM.cacheIsRec ind value).run methods) := by + unfold RecM.cacheIsRec + local_state + +@[local_state_frame] theorem eraseCachedIsRec {methods : Methods .anon} (ind : KId .anon) : + FramesLocalState ((RecM.eraseCachedIsRec ind).run methods) := by + unfold RecM.eraseCachedIsRec + local_state + +@[local_state_frame] theorem computedIsRecClassify {methods : Methods .anon} + (recursive : MethodsLocalState methods) (ind : KId .anon) (ctors : Array (KId .anon)) + (params : Nat) (blocks : Array Address) : + FramesLocalState ((RecM.computedIsRecClassify ind ctors params blocks).run methods) := by + unfold RecM.computedIsRecClassify + local_state + +@[local_state_frame] theorem computedIsRecMiss {methods : Methods .anon} + (recursive : MethodsLocalState methods) (ind : KId .anon) (params : UInt64) + (ctors : Array (KId .anon)) (block : KId .anon) : + FramesLocalState ((RecM.computedIsRecMiss ind params ctors block).run methods) := by + unfold RecM.computedIsRecMiss + local_state + +@[local_state_frame] theorem computedIsRec {methods : Methods .anon} + (recursive : MethodsLocalState methods) (ind : KId .anon) : + FramesLocalState ((RecM.computedIsRec ind).run methods) := by + unfold RecM.computedIsRec + local_state + +@[local_state_frame] theorem isStructLike {methods : Methods .anon} + (recursive : MethodsLocalState methods) (ind : KId .anon) : + FramesLocalState ((RecM.isStructLike ind).run methods) := by + unfold RecM.isStructLike + local_state + +@[local_state_frame] theorem finishStructEtaFields {methods : Methods .anon} + (ind : KId .anon) (major : KExpr .anon) (fuel field : Nat) (result : KExpr .anon) : + FramesLocalState ((RecM.finishStructEtaFields ind major fuel field result).run methods) := by + induction fuel generalizing field result with + | zero => exact pure _ + | succ fuel ih => + rw [RecM.finishStructEtaFields] + local_state + +@[local_state_frame] theorem finishStructEtaResult {methods : Methods .anon} + (ind : KId .anon) (major rhs : KExpr .anon) (fields : UInt64) + (prefixArgs trailing : Array (KExpr .anon)) : + FramesLocalState ((RecM.finishStructEtaResult ind major rhs fields prefixArgs trailing).run methods) := by + unfold RecM.finishStructEtaResult + local_state + +@[local_state_frame] theorem finishStructEtaAfterSort {methods : Methods .anon} + (levels : Array (KUniv .anon)) (spine : Array (KExpr .anon)) + (recursor : IotaInfo .anon) (rule : RecRule .anon) (ind : KId .anon) + (major sort : KExpr .anon) : + FramesLocalState ((RecM.finishStructEtaAfterSort levels spine recursor rule ind major sort).run methods) := by + unfold RecM.finishStructEtaAfterSort + local_state + +@[local_state_frame] theorem tryStructEtaAfterInductive {methods : Methods .anon} + (recursive : MethodsLocalState methods) (levels : Array (KUniv .anon)) (spine : Array (KExpr .anon)) + (recursor : IotaInfo .anon) (rule : RecRule .anon) (ind : KId .anon) : + FramesLocalState ((RecM.tryStructEtaAfterInductive levels spine recursor rule ind).run methods) := by + unfold RecM.tryStructEtaAfterInductive + local_state + +@[local_state_frame] theorem tryStructEtaIota {methods : Methods .anon} + (recursive : MethodsLocalState methods) (id : KId .anon) (recursor : IotaInfo .anon) + (levels : Array (KUniv .anon)) (spine : Array (KExpr .anon)) : + FramesLocalState ((RecM.tryStructEtaIota id recursor levels spine).run methods) := by + unfold RecM.tryStructEtaIota + local_state + +@[local_state_frame] theorem verifyKSynthCandidate {methods : Methods .anon} + (recursive : MethodsLocalState methods) (type : KExpr .anon) (ctor : KId .anon) (levels : Array (KUniv .anon)) + (args : Array (KExpr .anon)) (params : Nat) : + FramesLocalState ((RecM.verifyKSynthCandidate type ctor levels args params).run methods) := by + unfold RecM.verifyKSynthCandidate + local_state + +@[local_state_frame] theorem selectKSynthCandidate {methods : Methods .anon} + (recursive : MethodsLocalState methods) (type : KExpr .anon) (head : KId .anon) (levels : Array (KUniv .anon)) + (args : Array (KExpr .anon)) (ind : KId .anon) (params : Nat) : + FramesLocalState ((RecM.selectKSynthCandidate type head levels args ind params).run methods) := by + unfold RecM.selectKSynthCandidate + local_state + +@[local_state_frame] theorem synthCtorWhenK {methods : Methods .anon} + (recursive : MethodsLocalState methods) (major : KExpr .anon) (id : KId .anon) + (recursor : IotaInfo .anon) (levels : Array (KUniv .anon)) : + FramesLocalState ((RecM.synthCtorWhenK major id recursor levels).run methods) := by + unfold RecM.synthCtorWhenK + local_state + +@[local_state_frame] theorem tryIotaCtorOrStructEta {methods : Methods .anon} + (recursive : MethodsLocalState methods) (id : KId .anon) (recursor : IotaInfo .anon) + (levels : Array (KUniv .anon)) (spine : Array (KExpr .anon)) + (major : KExpr .anon) (transient : Bool) : + FramesLocalState ((RecM.tryIotaCtorOrStructEta id recursor levels spine major transient).run methods) := by + unfold RecM.tryIotaCtorOrStructEta + local_state + +@[local_state_frame] theorem tryIotaAfterCleanup {methods : Methods .anon} + (recursive : MethodsLocalState methods) (flags : WhnfFlags) (id : KId .anon) (recursor : IotaInfo .anon) + (levels : Array (KUniv .anon)) (spine : Array (KExpr .anon)) + (major : KExpr .anon) (transient : Bool) : + FramesLocalState ((RecM.tryIotaAfterCleanup flags id recursor levels spine major transient).run methods) := by + unfold RecM.tryIotaAfterCleanup + local_state + +@[local_state_frame] theorem tryIotaAfterMajorWhnf {methods : Methods .anon} + (recursive : MethodsLocalState methods) (flags : WhnfFlags) (id : KId .anon) (recursor : IotaInfo .anon) + (levels : Array (KUniv .anon)) (spine : Array (KExpr .anon)) + (major : KExpr .anon) : + FramesLocalState ((RecM.tryIotaAfterMajorWhnf flags id recursor levels spine major).run methods) := by + unfold RecM.tryIotaAfterMajorWhnf + local_state + +@[local_state_frame] theorem tryIotaWithFlags {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) (flags : WhnfFlags) : + FramesLocalState ((RecM.tryIotaWithFlags term flags).run methods) := by + unfold RecM.tryIotaWithFlags + local_state + + +@[local_state_frame] theorem deltaUnfoldOne {methods : Methods .anon} + (term : KExpr .anon) : + FramesLocalState ((RecM.deltaUnfoldOne term).run methods) := by + unfold RecM.deltaUnfoldOne + local_state + +@[local_state_frame] theorem isNatSuccSpine {methods : Methods .anon} + (term : KExpr .anon) : + FramesLocalState ((RecM.isNatSuccSpine term).run methods) := by + unfold RecM.isNatSuccSpine + local_state + +@[local_state_frame] theorem recordNatSuccStuck {methods : Methods .anon} + (visited : Array (Address × Address)) : + FramesLocalState ((RecM.recordNatSuccStuck visited).run methods) := by + unfold RecM.recordNatSuccStuck + local_state + +@[local_state_frame] theorem tryReduceNatSuccPeelMiss {methods : Methods .anon} + (reduced current : KExpr .anon) (offset : Nat) + (visited : Array (Address × Address)) (key : Address × Address) : + FramesLocalState ((RecM.tryReduceNatSuccPeelMiss reduced current offset visited key).run methods) := by + unfold RecM.tryReduceNatSuccPeelMiss + local_state + +@[local_state_frame] theorem tryReduceNatSuccPeelAfterKey {methods : Methods .anon} + (reduced current : KExpr .anon) (offset : Nat) + (visited : Array (Address × Address)) (key : Address × Address) : + FramesLocalState ((RecM.tryReduceNatSuccPeelAfterKey reduced current offset visited key).run methods) := by + unfold RecM.tryReduceNatSuccPeelAfterKey + local_state + +@[local_state_frame] theorem tryReduceNatSuccPeel {methods : Methods .anon} + (reduced current : KExpr .anon) (offset : Nat) + (visited : Array (Address × Address)) : + FramesLocalState ((RecM.tryReduceNatSuccPeel reduced current offset visited).run methods) := by + unfold RecM.tryReduceNatSuccPeel + local_state + +@[local_state_frame] theorem tryReduceNatSuccAfterWhnf {methods : Methods .anon} + (reduced : KExpr .anon) (offset : Nat) + (visited : Array (Address × Address)) : + FramesLocalState ((RecM.tryReduceNatSuccAfterWhnf reduced offset visited).run methods) := by + unfold RecM.tryReduceNatSuccAfterWhnf + local_state + +@[local_state_frame] theorem isNatSuccIhStep {methods : Methods .anon} + (recursive : MethodsLocalState methods) (step : KExpr .anon) : + FramesLocalState ((RecM.isNatSuccIhStep step).run methods) := by + unfold RecM.isNatSuccIhStep + local_state + +@[local_state_frame] theorem tryReduceNatSuccLinearRec {methods : Methods .anon} + (recursive : MethodsLocalState methods) (arg : KExpr .anon) (offset : Nat) : + FramesLocalState ((RecM.tryReduceNatSuccLinearRec arg offset).run methods) := by + unfold RecM.tryReduceNatSuccLinearRec + local_state + +@[local_state_frame] theorem tryReduceNatSuccIterStep {methods : Methods .anon} + (recursive : MethodsLocalState methods) (state : KExpr .anon × Nat × Array (Address × Address)) : + FramesLocalState ((RecM.tryReduceNatSuccIterStep state).run methods) := by + unfold RecM.tryReduceNatSuccIterStep + local_state + +@[local_state_frame] theorem tryReduceNatSuccIter {methods : Methods .anon} + (recursive : MethodsLocalState methods) (arg : KExpr .anon) : + FramesLocalState ((RecM.tryReduceNatSuccIter arg).run methods) := by + unfold RecM.tryReduceNatSuccIter + local_state + +@[local_state_frame] theorem whnfNatReducerArg {methods : Methods .anon} + (recursive : MethodsLocalState methods) (arg : KExpr .anon) : + FramesLocalState ((RecM.whnfNatReducerArg arg).run methods) := by + unfold RecM.whnfNatReducerArg + local_state + +@[local_state_frame] theorem tryReduceNatPredicate {methods : Methods .anon} + (recursive : MethodsLocalState methods) (addr : Address) (args : Array (KExpr .anon)) : + FramesLocalState ((RecM.tryReduceNatPredicate addr args).run methods) := by + unfold RecM.tryReduceNatPredicate + local_state + +@[local_state_frame] theorem tryReduceNatWithSuccMode {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) (mode : NatSuccMode) : + FramesLocalState ((RecM.tryReduceNatWithSuccMode term mode).run methods) := by + unfold RecM.tryReduceNatWithSuccMode + local_state + +@[local_state_frame] theorem tryReduceNat {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) : + FramesLocalState ((RecM.tryReduceNat term).run methods) := by + unfold RecM.tryReduceNat + local_state + +@[local_state_frame] theorem tryNatOffsetStuck {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) : + FramesLocalState ((RecM.tryNatOffsetStuck term).run methods) := by + unfold RecM.tryNatOffsetStuck + local_state + +@[local_state_frame] theorem buildNatDecidableTrue {methods : Methods .anon} + (primitives : Primitives .anon) (prop : KExpr .anon) + (args : Array (KExpr .anon)) (proofFn : KId .anon) (level : KUniv .anon) : + FramesLocalState ((RecM.buildNatDecidableTrue primitives prop args proofFn level).run methods) := by + unfold RecM.buildNatDecidableTrue + local_state + +@[local_state_frame] theorem buildNatDecidableFalse {methods : Methods .anon} + (primitives : Primitives .anon) (prop : KExpr .anon) + (args : Array (KExpr .anon)) (proofFn : KId .anon) (level : KUniv .anon) : + FramesLocalState ((RecM.buildNatDecidableFalse primitives prop args proofFn level).run methods) := by + unfold RecM.buildNatDecidableFalse + local_state + +@[local_state_frame] theorem inferDecidableProp {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) : + FramesLocalState ((RecM.inferDecidableProp term).run methods) := by + unfold RecM.inferDecidableProp + local_state + +@[local_state_frame] theorem tryNormalizeIntDecidable {methods : Methods .anon} + (recursive : MethodsLocalState methods) (addr : Address) (args : Array (KExpr .anon)) : + FramesLocalState ((RecM.tryNormalizeIntDecidable addr args).run methods) := by + unfold RecM.tryNormalizeIntDecidable + local_state + +@[local_state_frame] theorem tryReduceDecidable {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) : + FramesLocalState ((RecM.tryReduceDecidable term).run methods) := by + unfold RecM.tryReduceDecidable + local_state + +@[local_state_frame] theorem tryQuotReduce {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) : + FramesLocalState ((RecM.tryQuotReduce term).run methods) := by + unfold RecM.tryQuotReduce + local_state + + +@[local_state_frame] theorem tryEvalNatValueForPredFuel {methods : Methods .anon} + (recursive : MethodsLocalState methods) (fuel : Nat) (term : KExpr .anon) : + FramesLocalState ((RecM.tryEvalNatValueForPredFuel fuel term).run methods) := by + induction fuel generalizing term with + | zero => exact pure _ + | succ fuel ih => + rw [RecM.tryEvalNatValueForPredFuel] + local_state + +@[local_state_frame] theorem tryEvalNatValueForPred {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) (depth : Nat) : + FramesLocalState ((RecM.tryEvalNatValueForPred term depth).run methods) := by + unfold RecM.tryEvalNatValueForPred + local_state + +@[local_state_frame] theorem tryReduceBitvecToNat {methods : Methods .anon} + (recursive : MethodsLocalState methods) (value : KExpr .anon) : + FramesLocalState ((RecM.tryReduceBitvecToNat value).run methods) := by + unfold RecM.tryReduceBitvecToNat + local_state + +@[local_state_frame] theorem bitvecToNatExpr {methods : Methods .anon} + (recursive : MethodsLocalState methods) (width value : KExpr .anon) : + FramesLocalState ((RecM.bitvecToNatExpr width value).run methods) := by + unfold RecM.bitvecToNatExpr + local_state + +@[local_state_frame] theorem tryReduceBitvecUlt {methods : Methods .anon} + (recursive : MethodsLocalState methods) (width left right : KExpr .anon) : + FramesLocalState ((RecM.tryReduceBitvecUlt width left right).run methods) := by + unfold RecM.tryReduceBitvecUlt + local_state + +@[local_state_frame] theorem tryReduceBitvecLtProp {methods : Methods .anon} + (recursive : MethodsLocalState methods) (prop : KExpr .anon) : + FramesLocalState ((RecM.tryReduceBitvecLtProp prop).run methods) := by + unfold RecM.tryReduceBitvecLtProp + local_state + +@[local_state_frame] theorem tryReduceBitvec {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) : + FramesLocalState ((RecM.tryReduceBitvec term).run methods) := by + unfold RecM.tryReduceBitvec + local_state + +@[local_state_frame] theorem tryReduceNative {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) : + FramesLocalState ((RecM.tryReduceNative term).run methods) := by + unfold RecM.tryReduceNative + local_state + + +@[local_state_frame] theorem whnfCoreWithFlagsStep {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) (flags : WhnfFlags) : + FramesLocalState ((RecM.whnfCoreWithFlagsStep term flags).run methods) := by + unfold RecM.whnfCoreWithFlagsStep + local_state + +@[local_state_frame] theorem whnfCoreWithFlagsUncached {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) (flags : WhnfFlags) : + FramesLocalState ((RecM.whnfCoreWithFlagsUncached term flags).run methods) := by + unfold RecM.whnfCoreWithFlagsUncached + local_state + +@[local_state_frame] theorem whnfCoreWithFlagsNonLeaf {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) (flags : WhnfFlags) : + FramesLocalState ((RecM.whnfCoreWithFlagsNonLeaf term flags).run methods) := by + unfold RecM.whnfCoreWithFlagsNonLeaf + local_state + +@[local_state_frame] theorem whnfCoreWithFlags {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) (flags : WhnfFlags) : + FramesLocalState ((RecM.whnfCoreWithFlags term flags).run methods) := by + unfold RecM.whnfCoreWithFlags + local_state + +@[local_state_frame] theorem whnfCore {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) : + FramesLocalState ((RecM.whnfCore term).run methods) := by + unfold RecM.whnfCore + local_state + +@[local_state_frame] theorem whnfCoreForDefEq {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) : + FramesLocalState ((RecM.whnfCoreForDefEq term).run methods) := by + unfold RecM.whnfCoreForDefEq + local_state + +@[local_state_frame] theorem whnfNoDeltaReducersStep {methods : Methods .anon} + (recursive : MethodsLocalState methods) (flags : WhnfFlags) (mode : NatSuccMode) (term : KExpr .anon) : + FramesLocalState ((RecM.whnfNoDeltaReducersStep flags mode term).run methods) := by + unfold RecM.whnfNoDeltaReducersStep + local_state + +@[local_state_frame] theorem whnfNoDeltaImplStep {methods : Methods .anon} + (recursive : MethodsLocalState methods) (flags : WhnfFlags) (mode : NatSuccMode) (term : KExpr .anon) : + FramesLocalState ((RecM.whnfNoDeltaImplStep flags mode term).run methods) := by + unfold RecM.whnfNoDeltaImplStep + local_state + +@[local_state_frame] theorem whnfNoDeltaImplUncached {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) (flags : WhnfFlags) (mode : NatSuccMode) : + FramesLocalState ((RecM.whnfNoDeltaImplUncached term flags mode).run methods) := by + unfold RecM.whnfNoDeltaImplUncached + local_state + +@[local_state_frame] theorem whnfNoDeltaImplNonLeaf {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) (flags : WhnfFlags) (mode : NatSuccMode) : + FramesLocalState ((RecM.whnfNoDeltaImplNonLeaf term flags mode).run methods) := by + unfold RecM.whnfNoDeltaImplNonLeaf + local_state + +@[local_state_frame] theorem whnfNoDeltaImpl {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) (flags : WhnfFlags) (mode : NatSuccMode) : + FramesLocalState ((RecM.whnfNoDeltaImpl term flags mode).run methods) := by + unfold RecM.whnfNoDeltaImpl + local_state + +@[local_state_frame] theorem whnfNoDelta {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) : + FramesLocalState ((RecM.whnfNoDelta term).run methods) := by + unfold RecM.whnfNoDelta + local_state + +@[local_state_frame] theorem whnfNoDeltaForDefEq {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) : + FramesLocalState ((RecM.whnfNoDeltaForDefEq term).run methods) := by + unfold RecM.whnfNoDeltaForDefEq + local_state + +@[local_state_frame] theorem whnfWithNatSuccModeStep {methods : Methods .anon} + (recursive : MethodsLocalState methods) (mode : NatSuccMode) (state : KExpr .anon × Std.HashSet Address) : + FramesLocalState ((RecM.whnfWithNatSuccModeStep mode state).run methods) := by + unfold RecM.whnfWithNatSuccModeStep + local_state + +@[local_state_frame] theorem whnfWithNatSuccModeUncached {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) (mode : NatSuccMode) : + FramesLocalState ((RecM.whnfWithNatSuccModeUncached term mode).run methods) := by + unfold RecM.whnfWithNatSuccModeUncached + local_state + +@[local_state_frame] theorem whnfWithNatSuccModePrefix {methods : Methods .anon} + (term : KExpr .anon) : + FramesLocalState ((RecM.whnfWithNatSuccModePrefix term).run methods) := by + unfold RecM.whnfWithNatSuccModePrefix + local_state + +@[local_state_frame] theorem whnfWithNatSuccModeMissCharge {methods : Methods .anon} + : + FramesLocalState ((RecM.whnfWithNatSuccModeMissCharge (m := .anon)).run methods) := by + unfold RecM.whnfWithNatSuccModeMissCharge + local_state + +@[local_state_frame] theorem whnfWithNatSuccModeNonLeaf {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) (mode : NatSuccMode) : + FramesLocalState ((RecM.whnfWithNatSuccModeNonLeaf term mode).run methods) := by + unfold RecM.whnfWithNatSuccModeNonLeaf + local_state + +@[local_state_frame] theorem whnfWithNatSuccMode {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) (mode : NatSuccMode) : + FramesLocalState ((RecM.whnfWithNatSuccMode term mode).run methods) := by + unfold RecM.whnfWithNatSuccMode + local_state + +@[local_state_frame] theorem whnf {methods : Methods .anon} + (recursive : MethodsLocalState methods) (term : KExpr .anon) : + FramesLocalState ((RecM.whnf term).run methods) := by + unfold RecM.whnf + local_state + +end FramesLocalState +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Ctx.lean b/Ix/Kernel/Verify/Ctx.lean index 6fbce5403..483085ca5 100644 --- a/Ix/Kernel/Verify/Ctx.lean +++ b/Ix/Kernel/Verify/Ctx.lean @@ -1,5 +1,5 @@ import Ix.Kernel.Verify.State -import Ix.Kernel.Lctx +import Ix.Kernel.Verify.LocalContext /-! # Dual-context reconciliation: `CtxRecon` @@ -30,9 +30,9 @@ in `nextFVarId` (`.toNat` form; overflow bounds are the caller's obligation, per walker discipline), so they survive sub-call truncate-restores, and `incr` yields the fvar `Nodup` directly (bypassing upstream's map-coherence route). `lctx.index` coherence is -the `LocalContext.WF` inductive (upstream `LocalContext.WF` re-key); -its full `find?`-correspondence kit is the NEXT slice, alongside the -`lookupVar`/`Δ.find? (.inl i)` bvar-side bridge. +the extensional `LocalContext.WF` lookup invariant; +its `find?` correspondence is shared in `Verify/LocalContext`, alongside the +`lookupVar`/`Δ.find? (.inl i)` bvar-side bridge below. Step lemmas are hypothesis-style (fields of the post-state given by equations) so they are robust to record-update spelling; the pop @@ -111,131 +111,6 @@ def KVLCtx.bvarLets : KVLCtx → Nat {fv : FVarId × List FVarId} {d : VLocalDecl} : KVLCtx.bvarLets ((some fv, d) :: Δ) = KVLCtx.bvarLets Δ := rfl -/-! ### Concrete `LocalContext` well-formedness - -The invariant is deliberately extensional in the hash-map representation: -every successful index lookup must point to a declaration carrying the same -id. An earlier push-history inductive was too strong for production -`truncate`: `erase (insert map key value) key` is lookup-equivalent to `map` -when the key was absent, but `Std.HashMap` does not promise representation -equality after that mutation pair. -/ - -structure LocalContext.WF {m : Mode} (lctx : LocalContext m) : Prop where - sound : ∀ {fv : FVarId} {i : Nat}, lctx.index[fv]? = some i → - ∃ d, lctx.decls[i]? = some (fv, d) - -protected theorem LocalContext.WF.empty : - LocalContext.WF ({} : LocalContext m) where - sound := by simp - -protected theorem LocalContext.WF.push {m : Mode} - {lctx : LocalContext m} {fv : FVarId} {d : LocalDecl m} - (h : lctx.WF) (hfree : lctx.index[fv]? = none) : - (lctx.push fv d).WF where - sound := by - have _hfree := hfree - intro queried i hi - simp only [LocalContext.push] at hi ⊢ - rw [Std.HashMap.getElem?_insert] at hi - split at hi - · next heq => - cases hi - have hid : fv = queried := eq_of_beq heq - subst queried - refine ⟨d, ?_⟩ - rw [Array.getElem?_push] - simp - · obtain ⟨decl, hd⟩ := h.sound hi - refine ⟨decl, ?_⟩ - rw [Array.getElem?_push, if_neg] - · exact hd - · intro hieq - subst i - obtain ⟨hlt, _⟩ := Array.getElem?_eq_some_iff.mp hd - omega - -theorem LocalContext.WF.mem_of_index {m : Mode} - {lctx : LocalContext m} (h : lctx.WF) {fv : FVarId} {i : Nat} - (hi : lctx.index[fv]? = some i) : - ∃ p ∈ lctx.decls.toList, p.1 = fv := by - obtain ⟨d, hd⟩ := h.sound hi - refine ⟨(fv, d), ?_, rfl⟩ - apply List.mem_of_getElem? - rw [Array.getElem?_toList] - exact hd - -theorem LocalContext.WF.index_lt {m : Mode} {lctx : LocalContext m} - (h : lctx.WF) {fv : FVarId} {i : Nat} - (hi : lctx.index[fv]? = some i) : i < lctx.decls.size := by - obtain ⟨d, hd⟩ := h.sound hi - exact (Array.getElem?_eq_some_iff.mp hd).choose - -/-- The index is positionally coherent: a hit points at an entry - carrying exactly the queried id. -/ -theorem LocalContext.WF.getElem?_of_index {m : Mode} - {lctx : LocalContext m} (h : lctx.WF) {fv : FVarId} {i : Nat} - (hi : lctx.index[fv]? = some i) : - ∃ d, lctx.decls[i]? = some (fv, d) := - h.sound hi - -/-- Truncating a one-entry extension produces the exact declaration-array -prefix and an index whose remaining hits are still sound. The index is not -claimed equal to any earlier hash-map value. -/ -theorem LocalContext.truncate_pred_eval {m : Mode} - {lctx : LocalContext m} {len : Nat} - (hsize : lctx.decls.size = len + 1) : - lctx.truncate len = - { decls := lctx.decls.pop - index := lctx.index.erase lctx.decls.back!.1 } := by - unfold LocalContext.truncate - simp [hsize, LocalContext.truncate.go] - -theorem LocalContext.WF.truncate_pred {m : Mode} - {lctx : LocalContext m} {len : Nat} - (h : lctx.WF) (hsize : lctx.decls.size = len + 1) : - (lctx.truncate len).WF := by - rw [LocalContext.truncate_pred_eval hsize] - constructor - intro fv i hi - change (lctx.index.erase lctx.decls.back!.1)[fv]? = some i at hi - rw [Std.HashMap.getElem?_erase] at hi - split at hi - · contradiction - · next hne => - obtain ⟨d, hd⟩ := h.sound hi - by_cases hlt : i < lctx.decls.pop.size - · refine ⟨d, ?_⟩ - rw [Array.getElem?_pop, if_pos (by simpa using hlt)] - exact hd - · have hiOld : i < lctx.decls.size := - (Array.getElem?_eq_some_iff.mp hd).choose - have hiLast : i = lctx.decls.size - 1 := by - simp only [Array.size_pop] at hlt - omega - subst i - obtain ⟨hiBound, hget⟩ := Array.getElem?_eq_some_iff.mp hd - have hback : lctx.decls.back! = (fv, d) := by - simp only [Array.back!] - rw [getElem!_pos lctx.decls (lctx.decls.size - 1) hiBound] - exact hget - exfalso - rw [hback] at hne - simp at hne - -/-- Unpack the concrete `find?` read into a positional hit. -/ -theorem LocalContext.WF.find?_pos {m : Mode} {lctx : LocalContext m} - (h : lctx.WF) {fv : FVarId} {d : LocalDecl m} - (hf : lctx.find? fv = some d) : - ∃ i, i < lctx.decls.size ∧ lctx.decls[i]? = some (fv, d) := by - match hi : lctx.index[fv]? with - | none => simp [LocalContext.find?, hi] at hf - | some i => - obtain ⟨d', hd⟩ := h.getElem?_of_index hi - have hlt := h.index_lt hi - refine ⟨i, hlt, ?_⟩ - simp [LocalContext.find?, hi, hd] at hf - rw [hd, hf] - /-! ### Per-declaration translation (upstream `TrLocalDecl`) -/ variable (env : VEnv) (uvars : Nat) (nameOf : Address → Option Lean.Name) diff --git a/Ix/Kernel/Verify/IngressState.lean b/Ix/Kernel/Verify/IngressState.lean new file mode 100644 index 000000000..4bc289529 --- /dev/null +++ b/Ix/Kernel/Verify/IngressState.lean @@ -0,0 +1,337 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Ingress + +/-! +# Exact state effects of anonymous ingress + +Conversion and publication may change loaded declarations, block membership +and intern tables. Every other checker-owned environment field, including +all inference and reduction caches and the fresh-variable counter, is +preserved on both success and partial failure. The proof follows the actual +intern-only conversion boundary and declaration publication operations. + +Source interpretation, address integrity and finite intern collision bounds +remain separate semantic obligations. +-/ + +namespace Ix.Kernel + +/-- Ingress owns the loaded declarations, block index and intern table. -/ +def KEnv.IngressFrame (before after : AnonEnv) : Prop := + ∃ consts blocks intern, after = {before with consts, blocks, intern} + +namespace KEnv.IngressFrame + +theorem refl (state : AnonEnv) : KEnv.IngressFrame state state := + ⟨state.consts, state.blocks, state.intern, rfl⟩ + +theorem trans {before middle after : AnonEnv} + (first : KEnv.IngressFrame before middle) (second : KEnv.IngressFrame middle after) : KEnv.IngressFrame before after := by + obtain ⟨consts, blocks, intern, first⟩ := first + obtain ⟨newConsts, newBlocks, newIntern, second⟩ := second + refine ⟨newConsts, newBlocks, newIntern, ?_⟩ + rw [second, first] + +theorem counter {before after : AnonEnv} (frame : KEnv.IngressFrame before after) : + after.nextFVarId = before.nextFVarId := by + obtain ⟨_, _, _, rfl⟩ := frame + rfl + +theorem insert (before : AnonEnv) (id : KId .anon) (constant : KConst .anon) : + KEnv.IngressFrame before (before.insert id constant) := + ⟨_, before.blocks, before.intern, rfl⟩ + +theorem insertBlock (before : AnonEnv) (id : KId .anon) (members : Array (KId .anon)) : + KEnv.IngressFrame before (before.insertBlock id members) := + ⟨before.consts, _, before.intern, rfl⟩ + +theorem foldl (items : List α) (before : AnonEnv) (step : AnonEnv → α → AnonEnv) + (framed : ∀ state item, KEnv.IngressFrame state (step state item)) : + KEnv.IngressFrame before (items.foldl step before) := by + induction items generalizing before with + | nil => exact .refl _ + | cons item rest ih => exact (framed before item).trans (ih (step before item)) + +end KEnv.IngressFrame + +def IngressM.FramesState (action : IngressM α) : Prop := + ∀ before, match action before with + | .ok _ after | .error _ after => KEnv.IngressFrame before after + +namespace IngressM.FramesState + +theorem pure (value : α) : IngressM.FramesState (Pure.pure value) := fun _ => .refl _ +theorem throw (error : String) : IngressM.FramesState (throw error : IngressM α) := fun _ => .refl _ +theorem get : IngressM.FramesState (get : IngressM AnonEnv) := fun _ => .refl _ + +theorem modifyGet (f : AnonEnv → α × AnonEnv) + (frame : ∀ before, KEnv.IngressFrame before (f before).2) : + IngressM.FramesState (MonadStateOf.modifyGet f : IngressM α) := fun before => frame before + +theorem liftExcept (result : Except String α) : IngressM.FramesState (IngressM.liftExcept result) := by + cases result <;> first | exact pure _ | exact throw _ + +theorem bind {action : IngressM α} {next : α → IngressM β} + (first : IngressM.FramesState action) (rest : ∀ value, IngressM.FramesState (next value)) : IngressM.FramesState (action >>= next) := by + intro before + have frame := first before + change match EStateM.bind action next before with + | .ok _ after | .error _ after => KEnv.IngressFrame before after + cases run : action before with + | error error after => rw [EStateM.bind, run]; simpa only [run] using frame + | ok value after => + rw [run] at frame + rw [EStateM.bind, run] + dsimp only + have final := rest value after + cases finished : next value after <;> rw [finished] at final <;> exact frame.trans final + +theorem internE (expr : KExpr .anon) : IngressM.FramesState (IngressM.internE expr) := + fun before => ⟨before.consts, before.blocks, _, rfl⟩ + +theorem internU (level : KUniv .anon) : IngressM.FramesState (IngressM.internU level) := + fun before => ⟨before.consts, before.blocks, _, rfl⟩ + +theorem forInList (items : List α) (initial : β) (step : α → β → IngressM (ForInStep β)) + (framed : ∀ item value, IngressM.FramesState (step item value)) : IngressM.FramesState (forIn items initial step) := by + induction items generalizing initial with + | nil => exact pure _ + | cons item rest ih => + rw [List.forIn_cons] + apply bind (framed item initial) + intro next + cases next with + | done value => exact pure _ + | yield value => exact ih value + +theorem forInRange (range : Std.Legacy.Range) (initial : α) + (step : Nat → α → IngressM (ForInStep α)) + (framed : ∀ index value, IngressM.FramesState (step index value)) : IngressM.FramesState (forIn range initial step) := by + rw [Std.Legacy.Range.forIn_eq_forIn_range'] + exact forInList _ _ _ framed + +theorem forInArray (items : Array α) (initial : β) (step : α → β → IngressM (ForInStep β)) + (framed : ∀ item value, IngressM.FramesState (step item value)) : + IngressM.FramesState (forIn items initial step) := by + rcases items with ⟨items⟩ + simp only [List.forIn_toArray] + exact forInList _ _ _ framed + +theorem forInList' (items : List α) (initial : β) + (step : (item : α) → item ∈ items → β → IngressM (ForInStep β)) + (framed : ∀ item member value, IngressM.FramesState (step item member value)) : + IngressM.FramesState (forIn' items initial step) := by + induction items generalizing initial with + | nil => exact pure _ + | cons item rest ih => + rw [List.forIn'_cons] + apply bind (framed item (by simp) initial) + intro next + cases next with + | done value => exact pure _ + | yield value => exact ih value _ (fun _ _ _ => framed _ _ _) + +theorem forInRange' (range : Std.Legacy.Range) (initial : α) + (step : (index : Nat) → index ∈ range → α → IngressM (ForInStep α)) + (framed : ∀ index member value, IngressM.FramesState (step index member value)) : + IngressM.FramesState (forIn' range initial step) := by + rw [Std.Legacy.Range.forIn'_eq_forIn'_range'] + exact forInList' _ _ _ (fun _ _ _ => framed _ _ _) + +/-- Conversion has access only to the intern table; errors retain its partial +updates while preserving every checker-owned environment field. -/ +theorem runIntern (action : InternIngressM α) : + IngressM.FramesState (IngressM.runIntern action) := by + intro before + unfold IngressM.runIntern + cases action before.intern <;> exact ⟨before.consts, before.blocks, _, rfl⟩ + +theorem ingressUnivTree (root : Ixon.Univ) : + IngressM.FramesState (_root_.Ix.Kernel.ingressUnivTree root) := + runIntern _ + +end IngressM.FramesState + +def ConvM.FramesState (action : ConvM α) : Prop := ∀ memo, IngressM.FramesState (action.run memo) + +namespace ConvM.FramesState + +theorem pure (value : α) : ConvM.FramesState (Pure.pure value) := by + intro memo + exact IngressM.FramesState.pure _ + +theorem throw (error : String) : ConvM.FramesState (throw error : ConvM α) := by + intro memo + exact IngressM.FramesState.throw _ + +theorem get : ConvM.FramesState (get : ConvM ConvState) := by + intro memo + exact IngressM.FramesState.pure _ + +theorem modify (f : ConvState → ConvState) : ConvM.FramesState (_root_.modify f : ConvM PUnit) := by + intro memo + exact IngressM.FramesState.pure _ + +theorem bind {action : ConvM α} {next : α → ConvM β} + (first : ConvM.FramesState action) (rest : ∀ value, ConvM.FramesState (next value)) : + ConvM.FramesState (action >>= next) := by + intro memo + simp only [StateT.run_bind] + exact IngressM.FramesState.bind (first memo) (fun pair => rest pair.1 pair.2) + +theorem lift {action : IngressM α} (frame : IngressM.FramesState action) : + ConvM.FramesState (monadLift action : ConvM α) := by + intro memo + simp only [StateT.run_monadLift] + exact IngressM.FramesState.bind frame (fun _ => IngressM.FramesState.pure _) + +theorem forInList (items : List α) (initial : β) (step : α → β → ConvM (ForInStep β)) + (framed : ∀ item value, ConvM.FramesState (step item value)) : ConvM.FramesState (forIn items initial step) := by + induction items generalizing initial with + | nil => exact pure _ + | cons item rest ih => + rw [List.forIn_cons] + apply bind (framed item initial) + intro next + cases next with + | done value => exact pure _ + | yield value => exact ih value + +theorem forInRange (range : Std.Legacy.Range) (initial : α) + (step : Nat → α → ConvM (ForInStep α)) + (framed : ∀ index value, ConvM.FramesState (step index value)) : ConvM.FramesState (forIn range initial step) := by + rw [Std.Legacy.Range.forIn_eq_forIn_range'] + exact forInList _ _ _ framed + +theorem forInArray (items : Array α) (initial : β) (step : α → β → ConvM (ForInStep β)) + (framed : ∀ item value, ConvM.FramesState (step item value)) : ConvM.FramesState (forIn items initial step) := by + rcases items with ⟨items⟩ + simp only [List.forIn_toArray] + exact forInList _ _ _ framed + +theorem ingressUnivIdx (ctx : IngressCtx) (index : UInt64) : + ConvM.FramesState (_root_.Ix.Kernel.ingressUnivIdx ctx index) := + fun _ => IngressM.FramesState.runIntern _ + +theorem ingressUnivArgs (ctx : IngressCtx) (indices : Array UInt64) : + ConvM.FramesState (_root_.Ix.Kernel.ingressUnivArgs ctx indices) := + fun _ => IngressM.FramesState.runIntern _ + +theorem ingressExpr (env : Ixon.Env) (ctx : IngressCtx) (root : Ixon.Expr) : + ConvM.FramesState (_root_.Ix.Kernel.ingressExpr env ctx root) := + fun _ => IngressM.FramesState.runIntern _ + +end ConvM.FramesState + +namespace KEnv.IngressFrame + +theorem insertEntriesState (before : AnonEnv) (entries : Array Entry) : + KEnv.IngressFrame before (_root_.Ix.Kernel.insertEntriesState before entries) := + foldl _ _ _ (fun state entry => insert state entry.1 entry.2) + +theorem insertMutsEntriesState (before : AnonEnv) (entries : Array Entry) : + KEnv.IngressFrame before (_root_.Ix.Kernel.insertMutsEntriesState before entries) := by + unfold _root_.Ix.Kernel.insertMutsEntriesState + dsimp only + split + · exact (insertBlock _ _ _).trans (insertEntriesState _ _) + · exact insertEntriesState _ _ + +end KEnv.IngressFrame + +namespace IngressM.FramesState + +theorem guardReserved (entries : Array Entry) : + IngressM.FramesState (_root_.Ix.Kernel.guardReserved entries) := + liftExcept _ + +theorem insertStandaloneEntries (entries : Array Entry) : + IngressM.FramesState (_root_.Ix.Kernel.insertStandaloneEntries entries) := by + unfold _root_.Ix.Kernel.insertStandaloneEntries + apply bind (guardReserved entries) + intro _ + apply modifyGet + intro before + dsimp only + rw [← Array.foldl_toList] + exact KEnv.IngressFrame.foldl _ _ _ fun state entry => + (KEnv.IngressFrame.insert state entry.1 entry.2).trans + (KEnv.IngressFrame.insertBlock _ _ _) + +theorem insertMutsEntries (entries : Array Entry) : + IngressM.FramesState (_root_.Ix.Kernel.insertMutsEntries entries) := by + unfold _root_.Ix.Kernel.insertMutsEntries + exact bind (guardReserved entries) fun _ => modifyGet _ fun before => + KEnv.IngressFrame.insertMutsEntriesState before entries + +theorem ingressDefnAnon (env : Ixon.Env) (defn : Ixon.Definition) + (id : KId .anon) (constant : Ixon.Constant) (block : KId .anon) + (mutCtx : Array (KId .anon)) (hints : Option Lean.ReducibilityHints) : + IngressM.FramesState (_root_.Ix.Kernel.ingressDefnAnon env defn id constant block mutCtx hints) := by + unfold _root_.Ix.Kernel.ingressDefnAnon + exact bind (runIntern _) (fun _ => pure _) + +theorem ingressRecursorAnon (env : Ixon.Env) (recursor : Ixon.Recursor) + (id : KId .anon) (constant : Ixon.Constant) (block : KId .anon) + (mutCtx : Array (KId .anon)) : + IngressM.FramesState (_root_.Ix.Kernel.ingressRecursorAnon env recursor id constant block mutCtx) := by + unfold _root_.Ix.Kernel.ingressRecursorAnon + exact bind (runIntern _) (fun _ => pure _) + +theorem ingressAnonInductive (env : Ixon.Env) (ind : Ixon.Inductive) + (id : KId .anon) (constant : Ixon.Constant) (block : KId .anon) + (index : UInt64) (ctorAddrs : Array Address) (mutCtx : Array (KId .anon)) : + IngressM.FramesState + (_root_.Ix.Kernel.ingressAnonInductive env ind id constant block index ctorAddrs mutCtx) := + runIntern _ + +theorem ingressAnonStandalone (env : Ixon.Env) (addr : Address) (constant : Ixon.Constant) : + IngressM.FramesState (_root_.Ix.Kernel.ingressAnonStandalone env addr constant) := by + unfold _root_.Ix.Kernel.ingressAnonStandalone + exact bind (runIntern _) fun _ => bind (insertStandaloneEntries _) (fun _ => pure _) + +theorem prepareAnonBlock (env : Ixon.Env) (constant : Ixon.Constant) (addr : Address) : + IngressM.FramesState (_root_.Ix.Kernel.prepareAnonBlock env constant addr) := + runIntern _ + +theorem ingressAnonBlockWithTrace (env : Ixon.Env) (constant : Ixon.Constant) (addr : Address) : + IngressM.FramesState (_root_.Ix.Kernel.ingressAnonBlockWithTrace env constant addr) := by + unfold _root_.Ix.Kernel.ingressAnonBlockWithTrace + exact bind (prepareAnonBlock env constant addr) fun trace => + bind (insertMutsEntries trace.allEntries) fun _ => pure _ + +theorem ingressAnonBlock (env : Ixon.Env) (constant : Ixon.Constant) (addr : Address) : + IngressM.FramesState (_root_.Ix.Kernel.ingressAnonBlock env constant addr) := by + unfold _root_.Ix.Kernel.ingressAnonBlock + exact bind (ingressAnonBlockWithTrace env constant addr) fun _ => pure _ + +/-- The actual lazy callback changes only ingress-owned fields, for every +input and both outcomes. No callback-specific frame is assumed. -/ +theorem ingressAnonAddrShallow (env : Ixon.Env) (addr : Address) (verify : Bool) : + IngressM.FramesState (_root_.Ix.Kernel.ingressAnonAddrShallow env addr verify) := by + unfold _root_.Ix.Kernel.ingressAnonAddrShallow + apply bind (liftExcept _) + intro found + cases found with + | none => exact pure _ + | some constant => + dsimp only + split + · apply bind get + intro before + split + · exact pure _ + · apply bind (liftExcept _) + intro block + cases block with + | none => exact fun _ => .refl _ + | some blockConstant => exact bind (ingressAnonBlock _ _ _) fun _ => pure _ + · exact bind (ingressAnonStandalone _ _ _) fun _ => pure _ + +end IngressM.FramesState + +end Ix.Kernel diff --git a/Ix/Kernel/Verify/LocalContext.lean b/Ix/Kernel/Verify/LocalContext.lean new file mode 100644 index 000000000..bfe2c765c --- /dev/null +++ b/Ix/Kernel/Verify/LocalContext.lean @@ -0,0 +1,150 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Lctx +import Std.Data.HashMap.Lemmas + +/-! +# Concrete local-context coherence + +Every successful index lookup points at a declaration carrying the queried +identifier. These structural facts are shared by the direct set-model proof +and the named translation, without importing either semantic development. +They concern the actual production context, including truncation. +-/ + +namespace Ix.Kernel + +private local instance : LawfulBEq FVarId where + eq_of_beq := by + intro left right equal + cases left + cases right + congr 1 + exact eq_of_beq equal + rfl {a} := by + cases a with + | mk x => show (x == x) = true; exact beq_self_eq_true x + +/-! The invariant is extensional in the index map. Truncation need not +reconstruct a previous hash-map representation to preserve it. -/ + +structure LocalContext.WF {m : Mode} (lctx : LocalContext m) : Prop where + sound : ∀ {fv : FVarId} {i : Nat}, lctx.index[fv]? = some i → + ∃ d, lctx.decls[i]? = some (fv, d) + +protected theorem LocalContext.WF.empty : + LocalContext.WF ({} : LocalContext m) where + sound := by simp + +protected theorem LocalContext.WF.push {m : Mode} + {lctx : LocalContext m} {fv : FVarId} {d : LocalDecl m} + (h : lctx.WF) (hfree : lctx.index[fv]? = none) : + (lctx.push fv d).WF where + sound := by + have _hfree := hfree + intro queried i hi + simp only [LocalContext.push] at hi ⊢ + rw [Std.HashMap.getElem?_insert] at hi + split at hi + · next heq => + cases hi + have hid : fv = queried := eq_of_beq heq + subst queried + refine ⟨d, ?_⟩ + rw [Array.getElem?_push] + simp + · obtain ⟨decl, hd⟩ := h.sound hi + refine ⟨decl, ?_⟩ + rw [Array.getElem?_push, if_neg] + · exact hd + · intro hieq + subst i + obtain ⟨hlt, _⟩ := Array.getElem?_eq_some_iff.mp hd + omega + +theorem LocalContext.WF.mem_of_index {m : Mode} + {lctx : LocalContext m} (h : lctx.WF) {fv : FVarId} {i : Nat} + (hi : lctx.index[fv]? = some i) : + ∃ p ∈ lctx.decls.toList, p.1 = fv := by + obtain ⟨d, hd⟩ := h.sound hi + refine ⟨(fv, d), ?_, rfl⟩ + apply List.mem_of_getElem? + rw [Array.getElem?_toList] + exact hd + +theorem LocalContext.WF.index_lt {m : Mode} {lctx : LocalContext m} + (h : lctx.WF) {fv : FVarId} {i : Nat} + (hi : lctx.index[fv]? = some i) : i < lctx.decls.size := by + obtain ⟨d, hd⟩ := h.sound hi + exact (Array.getElem?_eq_some_iff.mp hd).choose + +/-- The index is positionally coherent: a hit points at an entry + carrying exactly the queried id. -/ +theorem LocalContext.WF.getElem?_of_index {m : Mode} + {lctx : LocalContext m} (h : lctx.WF) {fv : FVarId} {i : Nat} + (hi : lctx.index[fv]? = some i) : + ∃ d, lctx.decls[i]? = some (fv, d) := + h.sound hi + +/-- Truncating a one-entry extension produces the exact declaration-array +prefix and an index whose remaining hits are still sound. The index is not +claimed equal to any earlier hash-map value. -/ +theorem LocalContext.truncate_pred_eval {m : Mode} + {lctx : LocalContext m} {len : Nat} + (hsize : lctx.decls.size = len + 1) : + lctx.truncate len = + { decls := lctx.decls.pop + index := lctx.index.erase lctx.decls.back!.1 } := by + unfold LocalContext.truncate + simp [hsize, LocalContext.truncate.go] + +theorem LocalContext.WF.truncate_pred {m : Mode} + {lctx : LocalContext m} {len : Nat} + (h : lctx.WF) (hsize : lctx.decls.size = len + 1) : + (lctx.truncate len).WF := by + rw [LocalContext.truncate_pred_eval hsize] + constructor + intro fv i hi + change (lctx.index.erase lctx.decls.back!.1)[fv]? = some i at hi + rw [Std.HashMap.getElem?_erase] at hi + split at hi + · contradiction + · next hne => + obtain ⟨d, hd⟩ := h.sound hi + by_cases hlt : i < lctx.decls.pop.size + · refine ⟨d, ?_⟩ + rw [Array.getElem?_pop, if_pos (by simpa using hlt)] + exact hd + · have hiOld : i < lctx.decls.size := + (Array.getElem?_eq_some_iff.mp hd).choose + have hiLast : i = lctx.decls.size - 1 := by + simp only [Array.size_pop] at hlt + omega + subst i + obtain ⟨hiBound, hget⟩ := Array.getElem?_eq_some_iff.mp hd + have hback : lctx.decls.back! = (fv, d) := by + simp only [Array.back!] + rw [getElem!_pos lctx.decls (lctx.decls.size - 1) hiBound] + exact hget + exfalso + rw [hback] at hne + simp at hne + +/-- Unpack the concrete `find?` read into a positional hit. -/ +theorem LocalContext.WF.find?_pos {m : Mode} {lctx : LocalContext m} + (h : lctx.WF) {fv : FVarId} {d : LocalDecl m} + (hf : lctx.find? fv = some d) : + ∃ i, i < lctx.decls.size ∧ lctx.decls[i]? = some (fv, d) := by + match hi : lctx.index[fv]? with + | none => simp [LocalContext.find?, hi] at hf + | some i => + obtain ⟨d', hd⟩ := h.getElem?_of_index hi + have hlt := h.index_lt hi + refine ⟨i, hlt, ?_⟩ + simp [LocalContext.find?, hi, hd] at hf + rw [hd, hf] + +end Ix.Kernel diff --git a/Ix/Kernel/Verify/LocalScope.lean b/Ix/Kernel/Verify/LocalScope.lean new file mode 100644 index 000000000..9102152d6 --- /dev/null +++ b/Ix/Kernel/Verify/LocalScope.lean @@ -0,0 +1,171 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.LocalContext + +/-! +# Observable local contexts and fresh extensions + +Scope restoration preserves the ordered declarations and every index lookup. +Hash-map representation need not be identical after insertion and erasure. +Fresh extensions compose through nested pushes and scope truncation. +-/ + +namespace Ix.Kernel.LocalContext + +private local instance : LawfulBEq FVarId where + eq_of_beq := by + intro left right equal + cases left + cases right + congr 1 + exact eq_of_beq equal + rfl {a} := by + cases a with + | mk x => show (x == x) = true; exact beq_self_eq_true x + +/-- The ordered declarations and every observable index lookup agree. +Physical hash-map representation is deliberately not part of the relation. -/ +structure Equiv (left right : LocalContext m) : Prop where + decls : left.decls = right.decls + index : ∀ id : FVarId, left.index[id]? = right.index[id]? + +namespace Equiv + +theorem refl (context : LocalContext m) : Equiv context context := ⟨rfl, fun _ => rfl⟩ + +theorem symm {left right : LocalContext m} (same : Equiv left right) : Equiv right left := + ⟨same.decls.symm, fun id => (same.index id).symm⟩ + +theorem trans {first second third : LocalContext m} + (left : Equiv first second) (right : Equiv second third) : Equiv first third := + ⟨left.decls.trans right.decls, fun id => (left.index id).trans (right.index id)⟩ + +theorem size {left right : LocalContext m} (same : Equiv left right) : left.size = right.size := + congrArg Array.size same.decls + +theorem find? {left right : LocalContext m} (same : Equiv left right) (id : FVarId) : + left.find? id = right.find? id := by + simp only [LocalContext.find?, same.index, same.decls] + +theorem wf {left right : LocalContext m} (same : Equiv left right) (valid : left.WF) : right.WF := by + constructor + intro id position found + rw [← same.index] at found + obtain ⟨decl, hit⟩ := valid.sound found + exact ⟨decl, same.decls ▸ hit⟩ + +theorem push {left right : LocalContext m} (same : Equiv left right) + (id : FVarId) (decl : LocalDecl m) : Equiv (left.push id decl) (right.push id decl) := by + constructor + · simp only [LocalContext.push, same.decls] + · intro queried + simp only [LocalContext.push, Std.HashMap.getElem?_insert, same.decls, same.index] + +private theorem truncate_go {left right : LocalContext m} (same : Equiv left right) + (len fuel : Nat) : Equiv + (LocalContext.truncate.go len left.decls left.index fuel) + (LocalContext.truncate.go len right.decls right.index fuel) := by + induction fuel generalizing left right with + | zero => exact same + | succ fuel ih => + simp only [LocalContext.truncate.go, same.decls] + split + · apply ih (left := ⟨right.decls.pop, left.index.erase right.decls.back!.1⟩) + (right := ⟨right.decls.pop, right.index.erase right.decls.back!.1⟩) + exact ⟨rfl, fun id => by simp only [Std.HashMap.getElem?_erase, same.index]⟩ + · exact ⟨rfl, same.index⟩ + +theorem truncate {left right : LocalContext m} (same : Equiv left right) (len : Nat) : + Equiv (left.truncate len) (right.truncate len) := by + unfold LocalContext.truncate + simpa only [same.decls] using truncate_go same len (left.decls.size - len) + +end Equiv + +/-- Popping a fresh push restores the old declarations and all old lookups. -/ +theorem truncate_push {context : LocalContext m} {id : FVarId} + (fresh : context.index[id]? = none) (decl : LocalDecl m) : + Equiv ((context.push id decl).truncate context.size) context := by + rw [LocalContext.truncate_pred_eval (by simp [LocalContext.push, LocalContext.size])] + constructor + · simp [LocalContext.push] + · intro queried + simp only [LocalContext.push, Array.back!_push, Std.HashMap.getElem?_erase, + Std.HashMap.getElem?_insert] + split + next equal => + have equal : id = queried := eq_of_beq equal + subst queried + exact fresh.symm + next different => rfl + +/-- A fresh final push can also be removed while restoring an older scope. -/ +theorem truncate_push_le {context : LocalContext m} {id : FVarId} {len : Nat} + (fresh : context.index[id]? = none) (decl : LocalDecl m) (bound : len ≤ context.size) : + Equiv ((context.push id decl).truncate len) (context.truncate len) := by + unfold LocalContext.truncate + simp only [LocalContext.push, Array.size_push] + have fuel : context.decls.size + 1 - len = (context.decls.size - len) + 1 := by + change len ≤ context.decls.size at bound + omega + rw [fuel, LocalContext.truncate.go, if_pos (by + simp only [Array.size_push] + change len < context.size + 1 + omega)] + simp only [Array.back!_push, Array.pop_push] + apply Equiv.truncate_go (left := ⟨context.decls, (context.index.insert id context.decls.size).erase id⟩) + (right := context) + refine ⟨rfl, ?_⟩ + intro queried + simp only [Std.HashMap.getElem?_erase, Std.HashMap.getElem?_insert] + split + next equal => + have equal : id = queried := eq_of_beq equal + subst queried + exact fresh.symm + next different => rfl + +/-- A context is extended only by fresh pushes and by observable context +equivalence. Nested scope cleanup can therefore be composed without exposing +hash-map implementation details. -/ +inductive Extension : LocalContext m → LocalContext m → Prop + | refl (context : LocalContext m) : Extension context context + | push {before current : LocalContext m} {id : FVarId} (decl : LocalDecl m) + (previous : Extension before current) (fresh : current.index[id]? = none) : + Extension before (current.push id decl) + | equiv {before current after : LocalContext m} + (previous : Extension before current) (same : Equiv current after) : Extension before after + +namespace Extension + +theorem trans {before middle after : LocalContext m} + (first : Extension before middle) (second : Extension middle after) : Extension before after := by + induction second with + | refl => exact first + | push decl _ fresh ih => exact .push decl ih fresh + | equiv _ same ih => exact .equiv ih same + +theorem size_le {before after : LocalContext m} (extended : Extension before after) : + before.size ≤ after.size := by + induction extended with + | refl => exact Nat.le_refl _ + | push decl previous fresh ih => + simp only [LocalContext.size, LocalContext.push, Array.size_push] at * + omega + | equiv previous same ih => rw [← same.size]; exact ih + +theorem restore {before after : LocalContext m} (extended : Extension before after) : + Equiv (after.truncate before.size) before := by + induction extended with + | refl => + simpa [LocalContext.truncate, LocalContext.size, LocalContext.truncate.go] using + Equiv.refl before + | push decl previous fresh ih => exact (truncate_push_le fresh decl previous.size_le).trans ih + | equiv previous same ih => exact (same.symm.truncate _).trans ih + +end Extension + +end Ix.Kernel.LocalContext diff --git a/Ix/Kernel/Verify/UniverseInternOnly.lean b/Ix/Kernel/Verify/UniverseInternOnly.lean new file mode 100644 index 000000000..a69f39661 --- /dev/null +++ b/Ix/Kernel/Verify/UniverseInternOnly.lean @@ -0,0 +1,319 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Monad + +/-! +# Exact state effects of universe instantiation + +The production memoized walker changes only its private scratch map and the +expression intern table. This exact frame holds for cache hits, all syntax +constructors, the level-array loop, and partial failures. It needs no hash or +semantic assumptions, and supports both the named policy proof and the direct +set-model state proof. + +The structural walker proof is shared from the earlier inference-policy +development in `Check/UniverseInstantiationPolicy.lean`. +-/ + +namespace Ix.Kernel + +/-- The action changes only the expression intern table, on success and on +failure. Its result may depend on that table; this contract concerns effects. -/ +def TcM.InternOnly (action : TcM .anon α) : Prop := + ∀ before, match action before with + | .ok _ after | .error _ after => + ∃ table, after = {before with env := {before.env with intern := table}} + +namespace TcM.InternOnly + +theorem pure (value : α) : (Pure.pure value : TcM .anon α).InternOnly := + fun before => ⟨before.env.intern, rfl⟩ + +theorem throw (error : TcError .anon) : (throw error : TcM .anon α).InternOnly := + fun before => ⟨before.env.intern, rfl⟩ + +theorem bind {action : TcM .anon α} {next : α → TcM .anon γ} + (first : action.InternOnly) (rest : ∀ value, (next value).InternOnly) : + (action >>= next).InternOnly := by + intro before + have intermediate := first before + change match EStateM.bind action next before with + | .ok _ after | .error _ after => + ∃ table, after = {before with env := {before.env with intern := table}} + cases run : action before with + | error error after => rw [EStateM.bind, run]; simpa only [run] using intermediate + | ok value after => + rw [run] at intermediate + obtain ⟨table, changed⟩ := intermediate + rw [EStateM.bind, run] + dsimp only + have final := rest value after + cases finished : next value after <;> rw [finished] at final <;> + obtain ⟨newTable, changedAgain⟩ := final <;> + exact ⟨newTable, by rw [changedAgain, changed]⟩ + +theorem runIntern (action : InternM .anon α) : (TcM.runIntern action).InternOnly := + fun before => ⟨(action before.env.intern).2, rfl⟩ + +theorem ofExcept (value : Except (TcError .anon) alpha) : + (TcM.ofExcept value).InternOnly := by + cases value with + | ok value => exact pure value + | error err => exact throw err + +theorem map {x : TcM .anon alpha} (hx : x.InternOnly) + (f : alpha → beta) : (f <$> x).InternOnly := by + rw [← bind_pure_comp] + exact bind hx fun value => pure (f value) + +private def StateInternOnly + (x : StateT sigma (TcM .anon) alpha) : Prop := + ∀ memo, (x.run memo).InternOnly + +private theorem statePure (value : alpha) : + StateInternOnly + (Pure.pure value : StateT sigma (TcM .anon) alpha) := by + intro memo + simp only [StateT.run_pure] + exact TcM.InternOnly.pure _ + +private theorem stateBind {x : StateT sigma (TcM .anon) alpha} + {f : alpha → StateT sigma (TcM .anon) beta} + (hx : StateInternOnly x) + (hf : ∀ value, StateInternOnly (f value)) : + StateInternOnly (x >>= f) := by + intro memo + simp only [StateT.run_bind] + apply TcM.InternOnly.bind (hx memo) + intro pair + exact hf pair.1 pair.2 + +private theorem stateGet : + StateInternOnly + (MonadState.get : StateT sigma (TcM .anon) sigma) := by + intro memo + simp only [StateT.run_get] + exact TcM.InternOnly.pure _ + +private theorem stateModify (f : sigma → sigma) : + StateInternOnly + (_root_.modify f : StateT sigma (TcM .anon) PUnit) := by + intro memo + simp only [StateT.run_modify] + exact TcM.InternOnly.pure _ + +private theorem stateLift {x : TcM .anon alpha} + (hx : x.InternOnly) : + StateInternOnly + (monadLift x : StateT sigma (TcM .anon) alpha) := by + intro memo + simp only [StateT.run_monadLift] + apply TcM.InternOnly.bind hx + intro value + exact TcM.InternOnly.pure _ + +private theorem stateForInArray + (items : Array alpha) (initial : beta) + (step : alpha → beta → + StateT sigma (TcM .anon) (ForInStep beta)) + (hstep : ∀ item state, + StateInternOnly (step item state)) : + StateInternOnly (forIn items initial step) := by + rcases items with ⟨items⟩ + simp only [List.forIn_toArray] + induction items generalizing initial with + | nil => + simp + exact statePure initial + | cons item rest ih => + rw [List.forIn_cons] + apply stateBind (hstep item initial) + intro action + cases action with + | done result => exact statePure result + | yield next => exact ih next + +private theorem stateInternMemo (key : Address) (result : KExpr .anon) : + StateInternOnly (do + let interned ← monadLift (TcM.intern result) + _root_.modify fun memo : Std.HashMap Address (KExpr .anon) => + memo.insert key interned + Pure.pure interned) := by + apply stateBind (stateLift (runIntern _)) + intro interned + apply stateBind (stateModify _) + intro _ + exact statePure interned + +/-- Universe instantiation changes only the actual expression intern table. +The statement is unconditional: collision freedom constrains the returned +syntax, while this theorem covers the walker's state effects. -/ +theorem instantiateUnivParams (e : KExpr .anon) + (us : Array (KUniv .anon)) : + (TcM.instantiateUnivParams e us).InternOnly := by + unfold TcM.instantiateUnivParams + split + · exact pure e + · have hinner : ∀ (source : KExpr .anon), + StateInternOnly (TcM.instUnivInner source us) := by + intro source + induction source with + | var idx name info => + simp only [TcM.instUnivInner] + apply stateBind stateGet + intro memo + split + · exact statePure _ + · apply stateBind (stateModify _) + intro _ + exact statePure (KExpr.var idx name info) + | fvar id name info => + simp only [TcM.instUnivInner] + apply stateBind stateGet + intro memo + split + · exact statePure _ + · apply stateBind (stateModify _) + intro _ + exact statePure (KExpr.fvar id name info) + | sort u info => + simp only [TcM.instUnivInner] + apply stateBind stateGet + intro memo + split + · exact statePure _ + · apply stateBind (stateLift (ofExcept (substUniv u us))) + intro resultUniv + apply stateBind (statePure (KExpr.mkSort resultUniv)) + intro result + exact stateInternMemo (KExpr.sort u info).addr result + | const id levels info => + rw [TcM.instUnivInner] + simp (config := { proj := false }) only [] + apply stateBind stateGet + intro memo + split + · exact statePure _ + · apply stateBind + (stateForInArray levels (Array.mkEmpty levels.size) + (fun level current => do + let instantiated ← + monadLift (TcM.ofExcept (substUniv level us)) + let next := current.push instantiated + Pure.pure PUnit.unit + Pure.pure (ForInStep.yield next)) + (by + intro level current + apply stateBind + (stateLift (ofExcept (substUniv level us))) + intro instantiated + exact statePure + (ForInStep.yield (current.push instantiated)))) + intro newLevels + apply stateBind (statePure (KExpr.mkConst id newLevels)) + intro result + exact stateInternMemo (KExpr.const id levels info).addr result + | app f a info ihf iha => + rw [TcM.instUnivInner] + simp (config := { proj := false }) only [] + apply stateBind stateGet + intro memo + split + · exact statePure _ + · apply stateBind ihf + intro resultF + apply stateBind iha + intro resultA + apply stateBind (statePure (KExpr.mkApp resultF resultA)) + intro result + exact stateInternMemo (KExpr.app f a info).addr result + | lam name bi ty body info ihty ihbody => + rw [TcM.instUnivInner] + simp (config := { proj := false }) only [] + apply stateBind stateGet + intro memo + split + · exact statePure _ + · apply stateBind ihty + intro resultTy + apply stateBind ihbody + intro resultBody + apply stateBind + (statePure (KExpr.mkLam name bi resultTy resultBody)) + intro result + exact stateInternMemo (KExpr.lam name bi ty body info).addr result + | all name bi ty body info ihty ihbody => + rw [TcM.instUnivInner] + simp (config := { proj := false }) only [] + apply stateBind stateGet + intro memo + split + · exact statePure _ + · apply stateBind ihty + intro resultTy + apply stateBind ihbody + intro resultBody + apply stateBind + (statePure (KExpr.mkAll name bi resultTy resultBody)) + intro result + exact stateInternMemo (KExpr.all name bi ty body info).addr result + | letE name ty value body nondep info ihty ihvalue ihbody => + rw [TcM.instUnivInner] + simp (config := { proj := false }) only [] + apply stateBind stateGet + intro memo + split + · exact statePure _ + · apply stateBind ihty + intro resultTy + apply stateBind ihvalue + intro resultValue + apply stateBind ihbody + intro resultBody + apply stateBind + (statePure + (KExpr.mkLet name resultTy resultValue resultBody nondep)) + intro result + exact stateInternMemo + (KExpr.letE name ty value body nondep info).addr result + | prj id field value info ih => + rw [TcM.instUnivInner] + simp (config := { proj := false }) only [] + apply stateBind stateGet + intro memo + split + · exact statePure _ + · apply stateBind ih + intro resultValue + apply stateBind + (statePure (KExpr.mkPrj id field resultValue)) + intro result + exact stateInternMemo (KExpr.prj id field value info).addr result + | nat value blob info => + simp only [TcM.instUnivInner] + apply stateBind stateGet + intro memo + split + · exact statePure _ + · apply stateBind (stateModify _) + intro _ + exact statePure (KExpr.nat value blob info) + | str value blob info => + simp only [TcM.instUnivInner] + apply stateBind stateGet + intro memo + split + · exact statePure _ + · apply stateBind (stateModify _) + intro _ + exact statePure (KExpr.str value blob info) + have hrun := hinner e ({} : Std.HashMap Address (KExpr .anon)) + unfold StateT.run' + exact map hrun Prod.fst + +end TcM.InternOnly + +end Ix.Kernel diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 4958184fc..77da2039b 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -109,6 +109,21 @@ increasing sequence of strongly inaccessible cardinals. still belong to the existing `SynthesisInference` fragment; composing lets arbitrarily into that recursive datatype remains open. Finite execution, collision, walker, and selected beta-origin resources remain explicit. +- `MethodsLocalState.methodsN` proves structural local-state preservation for + every production recursive table, including all inference, reduction, and + conversion branches. The public inference, WHNF, conversion, and sort/forall + exposure entries preserve coherent local lookup, the allocation-counter + bound, and the installed loader on both success and partial failure. Scope + cleanup restores the declaration array and every index lookup; it need not + restore the hash map's physical representation. The actual lazy loader + preserves the counter, so `TcState.newLazyAnon` establishes this invariant + without a callback-effect premise. `LocalContextReading.congr` transports + the model context through scope restoration, and the invariant proves that + the next allocated id is absent from its registered locals. Let inference + now derives its domain/value/comparison frames, freshness, and final scope + restoration from the actual calls. Its trace no longer assumes local-context + equalities. This structural result does not establish semantic inference, + reduction, or cache validity for the unsupported branches. - `instantiateUnivParams_readScopedAnnotated` brings the actual substituted declaration type into any active local context, preserving its closed term scope and occurrence annotations. `infer_const_scoped_annotated` uses this @@ -555,7 +570,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 799 exact theorem boundaries. The production +The consistency target checks 1,230 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -942,6 +957,8 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Cache invariants and sort cache typing | [`Consistency/InferenceCache.lean`](../Ix/Kernel/Verify/Consistency/InferenceCache.lean), [`SortCache.lean`](../Ix/Kernel/Verify/Consistency/SortCache.lean) | | Recursive cache preservation and witness reuse | [`Consistency/RecursiveCache.lean`](../Ix/Kernel/Verify/Consistency/RecursiveCache.lean) | | Ownership and intern coherence through recursive inference | [`Consistency/RecursiveState.lean`](../Ix/Kernel/Verify/Consistency/RecursiveState.lean) | +| Local-state preservation through the complete recursive checker | [`Consistency/RecursiveLocalState.lean`](../Ix/Kernel/Verify/Consistency/RecursiveLocalState.lean), [`LocalStateReading.lean`](../Ix/Kernel/Verify/Consistency/LocalStateReading.lean) | +| Concrete loader effects and initial local invariant | [`IngressState.lean`](../Ix/Kernel/Verify/IngressState.lean), [`Consistency/IngressLocalState.lean`](../Ix/Kernel/Verify/Consistency/IngressLocalState.lean) | | Exact source conversion and finite candidate inventories | [`SourceConversion.lean`](../Ix/Kernel/SourceConversion.lean), [`Consistency/ConversionRecipe.lean`](../Ix/Kernel/Verify/Consistency/ConversionRecipe.lean) | | Standalone source/model agreement through lookup and inference | [`Consistency/SourceAgreement.lean`](../Ix/Kernel/Verify/Consistency/SourceAgreement.lean) | | Source cache agreement from empty-state execution histories | [`Consistency/SourceCache.lean`](../Ix/Kernel/Verify/Consistency/SourceCache.lean) | diff --git a/docs/theory.md b/docs/theory.md index d65f35c83..ccbd3074e 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -114,6 +114,15 @@ choice determines the returned type. Declaration admission includes these let bodies using the same validation and inference calls. The three child checks remain in the existing recursive synthesis fragment; arbitrary recursive let composition and automatic construction of its finite resources remain open. +Structural local-state preservation now covers the complete recursive checker +on both success and failure. The actual loader and initial state establish +coherent lookup and a bound on allocated identifiers. Recursive calls retain +that bound, and scope cleanup restores all incoming declarations and lookups. +The model reader transports through this observable restoration. Let inference +therefore derives its fresh identifier and intermediate contexts from execution, +using one initial invariant instead of separate freshness and context premises. +This closes the structural local-state component; general semantic state, +reduction, conversion, and cache preservation remain separate obligations. Safe definition admission also rejects circular justification in both Lean and Rust, including `theorem loop : P := loop` with only `P : Prop` assumed. The production dependency walk returns an order with a proved decreasing rank; From f1e71c5013f213f53ba10f7009794e400a6ad70c Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 12:24:27 -0400 Subject: [PATCH 40/63] Derive inference contexts and binder freshness from execution --- Ix/Kernel/Verify/Consistency/Application.lean | 25 ++++- .../Verify/Consistency/ApplicationWhnf.lean | 46 +++++++--- Ix/Kernel/Verify/Consistency/Audit.lean | 13 ++- Ix/Kernel/Verify/Consistency/Beta.lean | 2 +- .../Verify/Consistency/BetaInference.lean | 28 +++--- .../Verify/Consistency/BinderInference.lean | 91 ++++++++++++++++--- .../Consistency/SynthesisCacheExecution.lean | 26 +++--- .../Consistency/SynthesisInference.lean | 68 +++++++------- docs/kernel-verification.md | 22 +++-- docs/theory.md | 6 ++ 10 files changed, 231 insertions(+), 96 deletions(-) diff --git a/Ix/Kernel/Verify/Consistency/Application.lean b/Ix/Kernel/Verify/Consistency/Application.lean index d57464ef7..5e347d272 100644 --- a/Ix/Kernel/Verify/Consistency/Application.lean +++ b/Ix/Kernel/Verify/Consistency/Application.lean @@ -5,6 +5,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Verify.Consistency.BinderOpening import Ix.Kernel.Knot +import Ix.Kernel.Verify.Consistency.LocalStateReading /-! # Production application and dependent substitution @@ -214,6 +215,7 @@ compares the inferred argument type with the domain. Eager markers are outside this trace; the comparison and substitution states are the actual ones. -/ structure ApplicationInferenceTrace (fuel : Nat) (before : TcState .anon) (fn arg : KExpr .anon) where + localState : LocalStateInvariant before name : Mode.anon.F Name bi : Mode.anon.F Lean.BinderInfo domain : KExpr .anon @@ -229,7 +231,28 @@ structure ApplicationInferenceTrace (fuel : Nat) (before : TcState .anon) ordinary : TcM.isEagerReduce arg argumentState = .ok false argumentState compareRun : RecM.isDefEq argumentType domain (methodsN fuel) argumentState = .ok true comparedState - contextPreserved : functionState.lctx = before.lctx + +/-- Scope restoration follows from the actual function-inference call. -/ +theorem ApplicationInferenceTrace.functionFrame {fuel : Nat} {before : TcState .anon} + {fn arg : KExpr .anon} (trace : ApplicationInferenceTrace fuel before fn arg) : + LocalStateFrame before trace.functionState := + (infer_methodsN_framesLocalState fuel fn).ok trace.localState trace.functionRun + +theorem ApplicationInferenceTrace.contextPreserved {fuel : Nat} {before : TcState .anon} + {fn arg : KExpr .anon} (trace : ApplicationInferenceTrace fuel before fn arg) : + trace.functionState.lctx.Equiv before.lctx := trace.functionFrame.context + +theorem ApplicationInferenceTrace.argumentFrame {fuel : Nat} {before : TcState .anon} + {fn arg : KExpr .anon} (trace : ApplicationInferenceTrace fuel before fn arg) : + LocalStateFrame before trace.argumentState := + trace.functionFrame.trans ((infer_methodsN_framesLocalState fuel arg).ok + (trace.functionFrame.invariant trace.localState) trace.argumentRun) + +theorem ApplicationInferenceTrace.comparedFrame {fuel : Nat} {before : TcState .anon} + {fn arg : KExpr .anon} (trace : ApplicationInferenceTrace fuel before fn arg) : + LocalStateFrame before trace.comparedState := + trace.argumentFrame.trans ((isDefEq_methodsN_framesLocalState fuel trace.argumentType trace.domain).ok + (trace.argumentFrame.invariant trace.localState) trace.compareRun) /-- Inverting the successful production branch reaches its exact interned codomain substitution after the real argument check. -/ diff --git a/Ix/Kernel/Verify/Consistency/ApplicationWhnf.lean b/Ix/Kernel/Verify/Consistency/ApplicationWhnf.lean index c2ae62241..212f9dd5f 100644 --- a/Ix/Kernel/Verify/Consistency/ApplicationWhnf.lean +++ b/Ix/Kernel/Verify/Consistency/ApplicationWhnf.lean @@ -16,6 +16,7 @@ universe u v structure ApplicationWhnfInferenceTrace (fuel : Nat) (before : TcState .anon) (fn arg : KExpr .anon) where + localState : LocalStateInvariant before functionType : KExpr .anon domain : KExpr .anon codomain : KExpr .anon @@ -30,7 +31,35 @@ structure ApplicationWhnfInferenceTrace (fuel : Nat) (before : TcState .anon) argumentRun : RecM.infer arg (methodsN fuel) exposedState = .ok argumentType argumentState ordinary : TcM.isEagerReduce arg argumentState = .ok false argumentState compareRun : RecM.isDefEq argumentType domain (methodsN fuel) argumentState = .ok true comparedState - contextPreserved : functionState.lctx = before.lctx + +theorem ApplicationWhnfInferenceTrace.functionFrame {fuel : Nat} {before : TcState .anon} + {fn arg : KExpr .anon} (trace : ApplicationWhnfInferenceTrace fuel before fn arg) : + LocalStateFrame before trace.functionState := + (infer_methodsN_framesLocalState fuel fn).ok trace.localState trace.functionRun + +theorem ApplicationWhnfInferenceTrace.contextPreserved {fuel : Nat} {before : TcState .anon} + {fn arg : KExpr .anon} (trace : ApplicationWhnfInferenceTrace fuel before fn arg) : + trace.functionState.lctx.Equiv before.lctx := trace.functionFrame.context + +/-- The actual exposure call preserves local state for every reduction path. -/ +theorem ApplicationWhnfInferenceTrace.exposedFrame {fuel : Nat} {before : TcState .anon} + {fn arg : KExpr .anon} (trace : ApplicationWhnfInferenceTrace fuel before fn arg) : + LocalStateFrame before trace.exposedState := + trace.functionFrame.trans + ((FramesLocalState.ensureForallDirect (FramesLocalState.whnf (MethodsLocalState.methodsN (fuel + 1))) + trace.functionType).ok (trace.functionFrame.invariant trace.localState) trace.exposureRun) + +theorem ApplicationWhnfInferenceTrace.argumentFrame {fuel : Nat} {before : TcState .anon} + {fn arg : KExpr .anon} (trace : ApplicationWhnfInferenceTrace fuel before fn arg) : + LocalStateFrame before trace.argumentState := + trace.exposedFrame.trans ((infer_methodsN_framesLocalState fuel arg).ok + (trace.exposedFrame.invariant trace.localState) trace.argumentRun) + +theorem ApplicationWhnfInferenceTrace.comparedFrame {fuel : Nat} {before : TcState .anon} + {fn arg : KExpr .anon} (trace : ApplicationWhnfInferenceTrace fuel before fn arg) : + LocalStateFrame before trace.comparedState := + trace.argumentFrame.trans ((isDefEq_methodsN_framesLocalState fuel trace.argumentType trace.domain).ok + (trace.argumentFrame.invariant trace.localState) trace.compareRun) theorem ApplicationWhnfInferenceTrace.output_state {fuel : Nat} {before after : TcState .anon} {fn arg result : KExpr .anon} {info : ExprInfo .anon} @@ -77,14 +106,9 @@ theorem ApplicationWhnfInferenceTrace.exposure_state {β : Type u} rw [exposure.run] at accepted exact (EStateM.Result.ok.inj accepted).2.symm -theorem ApplicationWhnfInferenceTrace.exposure_context {β : Type u} - {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {fuel : Nat} - {before : TcState .anon} {fn arg : KExpr .anon} {term domain body : AExpr β} {condition : Certified.PropWhen} - (trace : ApplicationWhnfInferenceTrace fuel before fn arg) - (exposure : BetaPiExposure resolve locals fuel trace.functionState trace.functionType term - condition domain body trace.domain trace.codomain) : trace.exposedState.lctx = before.lctx := by - rw [trace.exposure_state exposure, exposure.context] - exact trace.contextPreserved +theorem ApplicationWhnfInferenceTrace.exposure_context {fuel : Nat} {before : TcState .anon} + {fn arg : KExpr .anon} (trace : ApplicationWhnfInferenceTrace fuel before fn arg) : + trace.exposedState.lctx.Equiv before.lctx := trace.exposedFrame.context /-- Build the Pi-exposure part from its public beta path. The argument check begins in the computed post-exposure state. -/ @@ -98,10 +122,10 @@ def ApplicationWhnfInferenceTrace.ofBeta {β : Type u} (argumentRun : RecM.infer arg (methodsN fuel) exposure.after = .ok argumentType argumentState) (ordinary : TcM.isEagerReduce arg argumentState = .ok false argumentState) (compareRun : RecM.isDefEq argumentType domain (methodsN fuel) argumentState = .ok true comparedState) - (contextPreserved : functionState.lctx = before.lctx) : + (localState : LocalStateInvariant before) : ApplicationWhnfInferenceTrace fuel before fn arg := { functionType, domain, codomain, functionState, exposedState := exposure.after, argumentType, argumentState, comparedState, functionRun, exposureRun := exposure.run, - argumentRun, ordinary, compareRun, contextPreserved } + argumentRun, ordinary, compareRun, localState } end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index e562bfd7c..223f01745 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -1067,7 +1067,18 @@ private def modelLocalStateRoots : Array Lean.Name := #[ ``LetInferenceTrace.valueFrame, ``LetInferenceTrace.openingFrame, ``LetInferenceTrace.domainContext, ``LetInferenceTrace.openingContext, ``LetInferenceTrace.restores, ``LetInferenceCheck.keyedValid, - ``LetInferenceCheck.absent + ``LetInferenceCheck.absent, + ``ApplicationInferenceTrace.functionFrame, ``ApplicationInferenceTrace.contextPreserved, + ``ApplicationInferenceTrace.argumentFrame, ``ApplicationInferenceTrace.comparedFrame, + ``ApplicationWhnfInferenceTrace.functionFrame, ``ApplicationWhnfInferenceTrace.contextPreserved, + ``ApplicationWhnfInferenceTrace.exposedFrame, ``ApplicationWhnfInferenceTrace.argumentFrame, + ``ApplicationWhnfInferenceTrace.comparedFrame, + ``ForallInferenceTrace.domainFrame, ``ForallInferenceTrace.domainValid, + ``ForallInferenceTrace.contextPreserved, ``ForallInferenceTrace.absent, + ``LambdaBodyTrace.domainFrame, ``LambdaBodyTrace.domainValid, + ``LambdaBodyTrace.contextPreserved, ``LambdaBodyTrace.absent, + ``LambdaInferenceTrace.domainValid, ``LambdaInferenceTrace.contextPreserved, + ``LambdaInferenceTrace.absent ] def roots : Array RootAllowance := #[ diff --git a/Ix/Kernel/Verify/Consistency/Beta.lean b/Ix/Kernel/Verify/Consistency/Beta.lean index 2e5758e00..648802622 100644 --- a/Ix/Kernel/Verify/Consistency/Beta.lean +++ b/Ix/Kernel/Verify/Consistency/Beta.lean @@ -306,7 +306,7 @@ theorem SynthesisInference.beta_sound {β : Type u} obtain ⟨functionTypeReads, functionTyped, _⟩ := functionTree.sound formed keyedAgreement fnReads trace.functionRun obtain ⟨domainReads, _⟩ := readScopedExpr?_all_parts functionTypeReads - have argumentAgreement := trace.contextPreserved.symm ▸ keyedAgreement + have argumentAgreement := keyedAgreement.congr trace.contextPreserved.symm obtain ⟨argumentTypeReads, argumentTyped, _⟩ := argumentTree.sound formed argumentAgreement argReads trace.argumentRun have sameReading := beq_readScopedExpr? (resolve := resolve) (locals := locals) diff --git a/Ix/Kernel/Verify/Consistency/BetaInference.lean b/Ix/Kernel/Verify/Consistency/BetaInference.lean index edc7c919c..2ea06371c 100644 --- a/Ix/Kernel/Verify/Consistency/BetaInference.lean +++ b/Ix/Kernel/Verify/Consistency/BetaInference.lean @@ -31,7 +31,7 @@ def BinderInference.betaTyping {β : Type u} {resolve : Address → Option (Cons fun _ agreement reading => by obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement - have argumentAgreement := trace.contextPreserved.symm ▸ keyedAgreement + have argumentAgreement := keyedAgreement.congr trace.contextPreserved.symm have functionTypeReads := (BinderInference.synthesis.{u,u} functionTree head keyedAgreement functionReading trace.functionRun).1 have argumentTypeReads := @@ -49,11 +49,11 @@ def BinderInference.betaTyping {β : Type u} {resolve : Address → Option (Cons functionReading argumentReading conditions hashPath comparisonFaithful) exact .app (functionTree.betaTyping functionOrigin keyedAgreement functionReading) (sameType ▸ argumentTree.betaTyping (sameType.symm ▸ argumentOrigin) argumentAgreement argumentReading) - | .lam _ miss trace opening absent bodyTree _ _ _ _ _ => fun origin agreement reading => by + | .lam _ miss trace opening bodyTree _ _ _ _ _ => fun origin agreement reading => by obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_lam_parts reading - have domainAgreement := trace.contextPreserved.symm ▸ (miss.localContext.symm ▸ agreement) + have domainAgreement := (miss.localContext.symm ▸ agreement).congr trace.contextPreserved.symm obtain ⟨_, openedReading, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement absent domainReading bodyReading trace.openRun + openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReading bodyReading trace.openRun exact .lam origin (bodyTree.betaTyping (.lambdaBody origin) openedAgreement openedReading) termination_by structural support @@ -90,7 +90,7 @@ def SynthesisInference.betaTyping {β : Type u} {resolve : Address → Option (C fun contextOrigin agreement reading _ formed => by obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement - have argumentAgreement := trace.contextPreserved.symm ▸ keyedAgreement + have argumentAgreement := keyedAgreement.congr trace.contextPreserved.symm have contextFormation := contextOrigin.sound formed have functionTypeReads := (functionTree.soundWithSpine contextFormation keyedAgreement functionReading trace.functionRun).1 @@ -107,7 +107,7 @@ def SynthesisInference.betaTyping {β : Type u} {resolve : Address → Option (C fun contextOrigin agreement reading _ formed => by obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement - have argumentAgreement := (trace.exposure_context exposure).symm ▸ keyedAgreement + have argumentAgreement := keyedAgreement.congr trace.exposure_context.symm have contextFormation := contextOrigin.sound formed have functionTypeReads := (functionTree.soundWithSpine contextFormation keyedAgreement functionReading trace.functionRun).1 @@ -121,29 +121,29 @@ def SynthesisInference.betaTyping {β : Type u} {resolve : Address → Option (C (.convert (functionTree.betaTyping contextOrigin keyedAgreement functionReading trace.functionRun formed) (.rebase contextOrigin reduction)) (sameType ▸ argumentTree.betaTyping contextOrigin argumentAgreement argumentReading trace.argumentRun formed) - | .lam full miss trace opening absent domainTree bodyTree conditionAgrees constructed bound coherent + | .lam full miss trace opening domainTree bodyTree conditionAgrees constructed bound coherent closingFaithful faithful => fun contextOrigin agreement reading accepted formed => by - let node := SynthesisInference.lam full miss trace opening absent domainTree bodyTree conditionAgrees + let node := SynthesisInference.lam full miss trace opening domainTree bodyTree conditionAgrees constructed bound coherent closingFaithful faithful obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_lam_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement - have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm obtain ⟨_, openedReading, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement absent domainReading bodyReading trace.openRun + openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReading bodyReading trace.openRun exact .lam (.source (.checked contextOrigin node agreement reading accepted)) (bodyTree.betaTyping (contextOrigin.push domainTree keyedAgreement domainReading trace.domainRun) openedAgreement openedReading trace.bodyRun formed) - | .lamBeta full miss trace opening absent domainTree bodyTree reductionOrigin reduction conditionAgrees + | .lamBeta full miss trace opening domainTree bodyTree reductionOrigin reduction conditionAgrees constructed bound closingFaithful faithful => fun contextOrigin agreement reading accepted formed => by - let node := SynthesisInference.lamBeta full miss trace opening absent domainTree bodyTree reductionOrigin reduction + let node := SynthesisInference.lamBeta full miss trace opening domainTree bodyTree reductionOrigin reduction conditionAgrees constructed bound closingFaithful faithful obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_lam_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement - have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm obtain ⟨_, openedReading, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement absent domainReading bodyReading trace.openRun + openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReading bodyReading trace.openRun have inner := bodyTree.betaTyping (contextOrigin.push domainTree keyedAgreement domainReading trace.domainRun) openedAgreement openedReading trace.bodyRun formed diff --git a/Ix/Kernel/Verify/Consistency/BinderInference.lean b/Ix/Kernel/Verify/Consistency/BinderInference.lean index 50b332c4b..aa49303bf 100644 --- a/Ix/Kernel/Verify/Consistency/BinderInference.lean +++ b/Ix/Kernel/Verify/Consistency/BinderInference.lean @@ -12,8 +12,9 @@ import Ix.Theory.Model.Checking # Production dependent function inference Finite inference trees follow the production method table's decreasing fuel. -Their premises record cache observations, actual execution prefixes, local-context -frames, finite interning support, and index bounds. Semantic checking is +Their premises record cache observations, actual execution prefixes, initial +structural local state, finite interning support, and index bounds. Scope frames +and freshness follow from the actual recursive calls. Semantic checking is derived from these trees. A declaration's separate type inference supplies the expected type's hereditary validity before checking becomes typing. -/ @@ -78,6 +79,7 @@ Both sort exposures take the production syntactic fast path. -/ structure ForallInferenceTrace (fuel : Nat) (before : TcState .anon) (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) (domain body : KExpr .anon) where + localState : LocalStateInvariant before domainLevel : KUniv .anon domainInfo : ExprInfo .anon domainState : TcState .anon @@ -92,7 +94,30 @@ structure ForallInferenceTrace (fuel : Nat) (before : TcState .anon) openRun : TcM.openBinder name bi domain body domainState = .ok (opened, fresh) openedState bodyRun : RecM.infer opened (methodsN fuel) openedState = .ok (.sort bodyLevel bodyInfo) bodyState - contextPreserved : domainState.lctx = before.lctx + +theorem ForallInferenceTrace.domainFrame {fuel : Nat} {before : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body : KExpr .anon} + (trace : ForallInferenceTrace fuel before name bi domain body) : + LocalStateFrame before trace.domainState := + (infer_methodsN_framesLocalState fuel domain).ok trace.localState trace.domainRun + +theorem ForallInferenceTrace.domainValid {fuel : Nat} {before : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body : KExpr .anon} + (trace : ForallInferenceTrace fuel before name bi domain body) : + LocalStateInvariant trace.domainState := trace.domainFrame.invariant trace.localState + +theorem ForallInferenceTrace.contextPreserved {fuel : Nat} {before : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body : KExpr .anon} + (trace : ForallInferenceTrace fuel before name bi domain body) : + trace.domainState.lctx.Equiv before.lctx := trace.domainFrame.context + +theorem ForallInferenceTrace.absent {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} {before : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body : KExpr .anon} + (trace : ForallInferenceTrace fuel before name bi domain body) + (agreement : LocalContextReading resolve locals before.lctx context) : + (⟨trace.domainState.env.nextFVarId⟩ : FVarId) ∉ locals := + trace.domainValid.freshReading (agreement.congr trace.contextPreserved.symm) /-- Inversion reaches the actual final interning operation after both recursive calls, sort exposures, and binder opening. -/ @@ -142,6 +167,7 @@ theorem ForallInferenceTrace.output {fuel : Nat} {before after : TcState .anon} structure LambdaBodyTrace (fuel : Nat) (before : TcState .anon) (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) (domain body : KExpr .anon) where + localState : LocalStateInvariant before domainLevel : KUniv .anon domainInfo : ExprInfo .anon domainState : TcState .anon @@ -154,7 +180,30 @@ structure LambdaBodyTrace (fuel : Nat) (before : TcState .anon) .ok (.sort domainLevel domainInfo) domainState openRun : TcM.openBinder name bi domain body domainState = .ok (opened, fresh) openedState bodyRun : RecM.infer opened (methodsN fuel) openedState = .ok bodyType bodyState - contextPreserved : domainState.lctx = before.lctx + +theorem LambdaBodyTrace.domainFrame {fuel : Nat} {before : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body : KExpr .anon} + (trace : LambdaBodyTrace fuel before name bi domain body) : + LocalStateFrame before trace.domainState := + (infer_methodsN_framesLocalState fuel domain).ok trace.localState trace.domainRun + +theorem LambdaBodyTrace.domainValid {fuel : Nat} {before : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body : KExpr .anon} + (trace : LambdaBodyTrace fuel before name bi domain body) : + LocalStateInvariant trace.domainState := trace.domainFrame.invariant trace.localState + +theorem LambdaBodyTrace.contextPreserved {fuel : Nat} {before : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body : KExpr .anon} + (trace : LambdaBodyTrace fuel before name bi domain body) : + trace.domainState.lctx.Equiv before.lctx := trace.domainFrame.context + +theorem LambdaBodyTrace.absent {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} {before : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body : KExpr .anon} + (trace : LambdaBodyTrace fuel before name bi domain body) + (agreement : LocalContextReading resolve locals before.lctx context) : + (⟨trace.domainState.env.nextFVarId⟩ : FVarId) ∉ locals := + trace.domainValid.freshReading (agreement.congr trace.contextPreserved.symm) /-- The original no-op specialization of the general lambda-body trace. -/ structure LambdaInferenceTrace (fuel : Nat) (before : TcState .anon) @@ -162,6 +211,24 @@ structure LambdaInferenceTrace (fuel : Nat) (before : TcState .anon) (domain body : KExpr .anon) extends LambdaBodyTrace fuel before name bi domain body where betaUnchanged : cheapBetaPlan? bodyType = none +theorem LambdaInferenceTrace.domainValid {fuel : Nat} {before : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body : KExpr .anon} + (trace : LambdaInferenceTrace fuel before name bi domain body) : + LocalStateInvariant trace.domainState := trace.toLambdaBodyTrace.domainValid + +theorem LambdaInferenceTrace.contextPreserved {fuel : Nat} {before : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body : KExpr .anon} + (trace : LambdaInferenceTrace fuel before name bi domain body) : + trace.domainState.lctx.Equiv before.lctx := trace.toLambdaBodyTrace.contextPreserved + +theorem LambdaInferenceTrace.absent {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} {before : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body : KExpr .anon} + (trace : LambdaInferenceTrace fuel before name bi domain body) + (agreement : LocalContextReading resolve locals before.lctx context) : + (⟨trace.domainState.env.nextFVarId⟩ : FVarId) ∉ locals := + trace.toLambdaBodyTrace.absent agreement + def LambdaBodyTrace.reduced {fuel : Nat} {before : TcState .anon} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body : KExpr .anon} (trace : LambdaBodyTrace fuel before name bi domain body) := @@ -332,7 +399,6 @@ inductive BinderInference {β : Type u} (miss : UncachedInference before (.all name bi domain body info)) (trace : ForallInferenceTrace fuel miss.keyed name bi domain body) (opening : BinderOpeningSupport trace.domainState body) - (absent : (⟨trace.domainState.env.nextFVarId⟩ : FVarId) ∉ locals) (domainTree : BinderInference resolve entries locals context fuel miss.keyed domain A (.sort (readLevel trace.domainLevel))) (bodyTree : BinderInference resolve entries (trace.fresh :: locals) (context.push A) @@ -353,7 +419,6 @@ inductive BinderInference {β : Type u} (miss : UncachedInference before (.lam name bi domain body info)) (trace : LambdaInferenceTrace fuel miss.keyed name bi domain body) (opening : BinderOpeningSupport trace.domainState body) - (absent : (⟨trace.domainState.env.nextFVarId⟩ : FVarId) ∉ locals) (bodyTree : BinderInference resolve entries (trace.fresh :: locals) (context.push A) fuel trace.openedState trace.opened b B) (constructed : trace.bodyType.Constructed) @@ -440,7 +505,7 @@ theorem BinderInference.soundWithSynthesis {β : Type u} have keyedAgreement := miss.localContext.symm ▸ agreement obtain ⟨functionTypeReads, _, functionTyped⟩ := ihFunction keyedAgreement fnReads trace.functionRun obtain ⟨domainReads, codomainReads⟩ := readScopedExpr?_all_parts functionTypeReads - have argumentAgreement := trace.contextPreserved.symm ▸ keyedAgreement + have argumentAgreement := keyedAgreement.congr trace.contextPreserved.symm obtain ⟨argumentTypeReads, argumentChecked, _⟩ := ihArgument argumentAgreement argReads trace.argumentRun have sameReading := beq_readScopedExpr? (resolve := resolve) (locals := locals) (depth := 0) comparisonFaithful hashPath @@ -452,15 +517,15 @@ theorem BinderInference.soundWithSynthesis {β : Type u} rw [trace.output run, AExpr.erase_inst] exact (subst_readScopedExpr? bodyConstructed argConstructed bodyBound argBound coherent faithful codomainReads argReads).1 - | forallE miss trace opening absent domainTree bodyTree levelFaithful domainBound bodyBound + | forallE miss trace opening domainTree bodyTree levelFaithful domainBound bodyBound coherent faithful ihDomain ihBody => obtain ⟨state, run⟩ := infer_uncached_success miss accepted obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement obtain ⟨_, domainChecked, _⟩ := ihDomain keyedAgreement domainReads trace.domainRun - have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm obtain ⟨_, openedReads, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun + openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReads bodyReads trace.openRun obtain ⟨_, bodyChecked, _⟩ := ihBody openedAgreement openedReads trace.bodyRun refine ⟨?_, ?_, fun head => by cases head⟩ · rw [trace.output run, internExpr_readScopedExpr? coherent faithful] @@ -470,15 +535,15 @@ theorem BinderInference.soundWithSynthesis {β : Type u} (Theory.VLevel.equiv_def.mpr fun levels => (Theory.VLevel.equiv_def.mp (readLevel_mkIMax levelFaithful domainBound bodyBound) levels).symm) |>.typing formed).checking - | lam full miss trace opening absent bodyTree constructed bound coherent closingFaithful + | lam full miss trace opening bodyTree constructed bound coherent closingFaithful faithful ihBody => obtain ⟨state, run⟩ := infer_uncached_success miss accepted rw [full] at run obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement - have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm obtain ⟨_, openedReads, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun + openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReads bodyReads trace.openRun obtain ⟨bodyTypeReads, bodyChecked, _⟩ := ihBody openedAgreement openedReads trace.bodyRun obtain ⟨closedReads, closedCoherent⟩ := abstractFVars_readScopedExpr? constructed bound coherent closingFaithful bodyTypeReads diff --git a/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean b/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean index 591992d10..4d560c8ba 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean @@ -109,8 +109,8 @@ def SynthesisInference.CacheData {β : Type u} {resolve : Address → Option (Co | .cached .. | .cachedFrom .. => PUnit | .app _ _ _ first second .. | .appBeta _ _ _ first _ _ _ second .. => first.CacheData anchor × second.CacheData anchor - | .forallE _ _ _ _ first second .. | .lam _ _ _ _ _ first second .. | - .lamBeta _ _ _ _ _ first second .. => first.CacheData anchor × second.CacheData anchor + | .forallE _ _ _ first second .. | .lam _ _ _ _ first second .. | + .lamBeta _ _ _ _ first second .. => first.CacheData anchor × second.CacheData anchor termination_by structural tree /-- Extract every full publication's checking origin from the original @@ -144,7 +144,7 @@ def SynthesisInference.cacheExecution {β : Type u} {resolve : Address → Optio have keyedAgreement := miss.localContext.symm ▸ agreement let functionRun := functionTree.cacheExecution data.1 contextOrigin keyedAgreement functionReads trace.functionRun let argumentRun := argumentTree.cacheExecution data.2 contextOrigin - (trace.contextPreserved.symm ▸ keyedAgreement) argumentReads trace.argumentRun + (keyedAgreement.congr trace.contextPreserved.symm) argumentReads trace.argumentRun exact (SynthesisCacheSupplement.mk (.app full miss trace hashPath functionRun.trace argumentRun.trace) (functionRun.checks.append argumentRun.checks)).complete (.app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful @@ -157,52 +157,52 @@ def SynthesisInference.cacheExecution {β : Type u} {resolve : Address → Optio have keyedAgreement := miss.localContext.symm ▸ agreement let functionRun := functionTree.cacheExecution data.1 contextOrigin keyedAgreement functionReads trace.functionRun let argumentRun := argumentTree.cacheExecution data.2 contextOrigin - ((trace.exposure_context exposure).symm ▸ keyedAgreement) argumentReads trace.argumentRun + (keyedAgreement.congr trace.exposure_context.symm) argumentReads trace.argumentRun exact (SynthesisCacheSupplement.mk (.appBeta full miss trace exposure hashPath functionRun.trace argumentRun.trace) (functionRun.checks.append argumentRun.checks)).complete (.appBeta full miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful) contextOrigin agreement reading accepted - | .forallE miss trace opening absent domainTree bodyTree levelFaithful domainBound bodyBound coherent faithful => + | .forallE miss trace opening domainTree bodyTree levelFaithful domainBound bodyBound coherent faithful => fun data contextOrigin agreement reading accepted => by obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement let domainRun := domainTree.cacheExecution data.1 contextOrigin keyedAgreement domainReads trace.domainRun obtain ⟨_, openedReads, openedAgreement, _⟩ := openBinder_sound opening - (trace.contextPreserved.symm ▸ keyedAgreement) absent domainReads bodyReads trace.openRun + (keyedAgreement.congr trace.contextPreserved.symm) (trace.absent keyedAgreement) domainReads bodyReads trace.openRun let bodyRun := bodyTree.cacheExecution data.2 (.push contextOrigin domainTree keyedAgreement domainReads trace.domainRun) openedAgreement openedReads trace.bodyRun exact (SynthesisCacheSupplement.mk (.forallE miss trace domainRun.trace bodyRun.trace) (domainRun.checks.append bodyRun.checks)).complete - (.forallE miss trace opening absent domainTree bodyTree levelFaithful domainBound bodyBound coherent faithful) + (.forallE miss trace opening domainTree bodyTree levelFaithful domainBound bodyBound coherent faithful) contextOrigin agreement reading accepted - | .lam full miss trace opening absent domainTree bodyTree conditionAgrees constructed bound coherent + | .lam full miss trace opening domainTree bodyTree conditionAgrees constructed bound coherent closingFaithful faithful => fun data contextOrigin agreement reading accepted => by obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement let domainRun := domainTree.cacheExecution data.1 contextOrigin keyedAgreement domainReads trace.domainRun obtain ⟨_, openedReads, openedAgreement, _⟩ := openBinder_sound opening - (trace.contextPreserved.symm ▸ keyedAgreement) absent domainReads bodyReads trace.openRun + (keyedAgreement.congr trace.contextPreserved.symm) (trace.absent keyedAgreement) domainReads bodyReads trace.openRun let bodyRun := bodyTree.cacheExecution data.2 (.push contextOrigin domainTree keyedAgreement domainReads trace.domainRun) openedAgreement openedReads trace.bodyRun exact (SynthesisCacheSupplement.mk (.lam full miss trace domainRun.trace bodyRun.trace) (domainRun.checks.append bodyRun.checks)).complete - (.lam full miss trace opening absent domainTree bodyTree conditionAgrees constructed bound coherent + (.lam full miss trace opening domainTree bodyTree conditionAgrees constructed bound coherent closingFaithful faithful) contextOrigin agreement reading accepted - | .lamBeta full miss trace opening absent domainTree bodyTree origin reduction conditionAgrees + | .lamBeta full miss trace opening domainTree bodyTree origin reduction conditionAgrees constructed bound closingFaithful faithful => fun data contextOrigin agreement reading accepted => by obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement let domainRun := domainTree.cacheExecution data.1 contextOrigin keyedAgreement domainReads trace.domainRun obtain ⟨_, openedReads, openedAgreement, _⟩ := openBinder_sound opening - (trace.contextPreserved.symm ▸ keyedAgreement) absent domainReads bodyReads trace.openRun + (keyedAgreement.congr trace.contextPreserved.symm) (trace.absent keyedAgreement) domainReads bodyReads trace.openRun let bodyRun := bodyTree.cacheExecution data.2 (.push contextOrigin domainTree keyedAgreement domainReads trace.domainRun) openedAgreement openedReads trace.bodyRun exact (SynthesisCacheSupplement.mk (.lamBody full miss trace domainRun.trace bodyRun.trace) (domainRun.checks.append bodyRun.checks)).complete - (.lamBeta full miss trace opening absent domainTree bodyTree origin reduction conditionAgrees + (.lamBeta full miss trace opening domainTree bodyTree origin reduction conditionAgrees constructed bound closingFaithful faithful) contextOrigin agreement reading accepted termination_by structural tree diff --git a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean index a07724726..8d493ec11 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean @@ -120,7 +120,6 @@ inductive SynthesisInference {β : Type u} (miss : UncachedInference before (.all name bi domain body info)) (trace : ForallInferenceTrace fuel miss.keyed name bi domain body) (opening : BinderOpeningSupport trace.domainState body) - (absent : (⟨trace.domainState.env.nextFVarId⟩ : FVarId) ∉ locals) (domainTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed domain A (.sort (readLevel trace.domainLevel)) domainBoundLevel) (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) @@ -143,7 +142,6 @@ inductive SynthesisInference {β : Type u} (miss : UncachedInference before (.lam name bi domain body info)) (trace : LambdaInferenceTrace fuel miss.keyed name bi domain body) (opening : BinderOpeningSupport trace.domainState body) - (absent : (⟨trace.domainState.env.nextFVarId⟩ : FVarId) ∉ locals) (domainTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed domain A (.sort (readLevel trace.domainLevel)) domainBoundLevel) (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) @@ -165,7 +163,6 @@ inductive SynthesisInference {β : Type u} (miss : UncachedInference before (.lam name bi domain body info)) (trace : LambdaBodyTrace fuel miss.keyed name bi domain body) (opening : BinderOpeningSupport trace.domainState body) - (absent : (⟨trace.domainState.env.nextFVarId⟩ : FVarId) ∉ locals) (domainTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed domain A (.sort (readLevel trace.domainLevel)) domainBoundLevel) (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) @@ -1044,7 +1041,6 @@ private def ForallInferenceTrace.bodyCheck {β : Type u} {resolve : Address → {domain body : KExpr .anon} {A B : AExpr β} {domainBound bodyBound : VLevel} (trace : ForallInferenceTrace fuel before name bi domain body) (opening : BinderOpeningSupport trace.domainState body) - (absent : (⟨trace.domainState.env.nextFVarId⟩ : FVarId) ∉ locals) (domainTree : SynthesisInference resolve entries locals context bounds fuel before domain A (.sort (readLevel trace.domainLevel)) domainBound) (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) @@ -1055,8 +1051,8 @@ private def ForallInferenceTrace.bodyCheck {β : Type u} {resolve : Address → (bodyReading : readScopedExpr? resolve locals body 1 = some B.erase) : SynthesisForallBodyCheck resolve entries context bounds (Certified.zeroCondition (readLevel trace.bodyLevel)) A B := by - have opened := openBinder_sound opening (trace.contextPreserved.symm ▸ agreement) - absent domainReading bodyReading trace.openRun + have opened := openBinder_sound opening (agreement.congr trace.contextPreserved.symm) + (trace.absent agreement) domainReading bodyReading trace.openRun exact { domainLevel := readLevel trace.domainLevel bodyLevel := readLevel trace.bodyLevel @@ -1077,9 +1073,9 @@ def BinderInference.forallBodyCheck {β : Type u} {resolve : Address → Option (reading : readScopedExpr? resolve locals source = some (AExpr.forallE condition domain body).erase) : SynthesisForallBodyCheck resolve entries context bounds condition domain body := by cases support with - | forallE miss trace opening absent domainTree bodyTree => + | forallE miss trace opening domainTree bodyTree => obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading - exact trace.bodyCheck opening absent (.known domainTree (.sort _)) (.known bodyTree (.sort _)) + exact trace.bodyCheck opening (.known domainTree (.sort _)) (.known bodyTree (.sort _)) (miss.localContext.symm ▸ agreement) domainReads bodyReads mutual @@ -1101,10 +1097,10 @@ private def SynthesisInference.forallBodyCheckAux {β : Type u} {resolve : Addre | .cached tree priorAgreement priorReading _ _ _ _ => fun same => tree.forallBodyCheckAux priorAgreement priorReading same | .cachedFrom check _ _ => fun same => check.forallBody same - | .forallE miss trace opening absent domainTree bodyTree .. => fun same => by + | .forallE miss trace opening domainTree bodyTree .. => fun same => by cases same obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading - exact trace.bodyCheck opening absent domainTree bodyTree + exact trace.bodyCheck opening domainTree bodyTree (miss.localContext.symm ▸ agreement) domainReads bodyReads | .fvar .. | .app .. | .appBeta .. | .lam .. | .lamBeta .. => fun same => by cases same termination_by structural support @@ -1329,11 +1325,11 @@ def BinderInference.lambdaBodyVariableSpine {β : Type u} {resolve : Address → Σ resultType, SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries (context.push domain) index arguments resultType := by cases support with - | lam full miss trace opening absent bodyTree constructed bound coherent closingFaithful faithful => + | lam full miss trace opening bodyTree constructed bound coherent closingFaithful faithful => obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading - have domainAgreement := trace.contextPreserved.symm ▸ (miss.localContext.symm ▸ agreement) + have domainAgreement := (miss.localContext.symm ▸ agreement).congr trace.contextPreserved.symm obtain ⟨_, openedReads, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun + openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReads bodyReads trace.openRun exact ⟨_, bodyTree.variableSpineOrigin openedAgreement openedReads index arguments rfl⟩ mutual @@ -1362,25 +1358,25 @@ private def SynthesisInference.lambdaBodyVariableSpineAux {β : Type u} {resolve | .cachedFrom check _ _ => fun same => let child := check.lambdaBodyVariableSpine same ⟨child.1, child.2.rebase contextOrigin⟩ - | .lam full miss trace opening absent domainTree bodyTree conditionAgrees constructed bound coherent + | .lam full miss trace opening domainTree bodyTree conditionAgrees constructed bound coherent closingFaithful faithful => fun same => by cases same obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement - have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm obtain ⟨_, openedReads, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun + openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReads bodyReads trace.openRun exact ⟨_, bodyTree.variableSpineOrigin (contextOrigin.push domainTree keyedAgreement domainReads trace.domainRun) openedAgreement openedReads index arguments rfl⟩ - | .lamBeta full miss trace opening absent domainTree bodyTree origin reduction conditionAgrees + | .lamBeta full miss trace opening domainTree bodyTree origin reduction conditionAgrees constructed bound closingFaithful faithful => fun same => by cases same obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement - have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm obtain ⟨_, openedReads, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun + openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReads bodyReads trace.openRun exact ⟨_, bodyTree.variableSpineOrigin (contextOrigin.push domainTree keyedAgreement domainReads trace.domainRun) openedAgreement openedReads index arguments rfl⟩ @@ -1439,7 +1435,7 @@ theorem BinderInference.lambdaPrefix {β : Type u} (support : BinderInference resolve entries locals context fuel before source term type) : LambdaPrefix term type term.lambdaDepth := by induction support with - | lam _ _ _ _ _ _ _ _ _ _ _ ih => exact .lam ih + | lam _ _ _ _ _ _ _ _ _ _ ih => exact .lam ih | _ => exact .zero _ _ mutual @@ -1455,8 +1451,8 @@ theorem SynthesisInference.lambdaPrefix {β : Type u} | .cached tree .. => tree.lambdaPrefix | .cachedFrom check _ _ => check.lambdaPrefix | .reuseType inference _ _ _ _ _ => inference.lambdaPrefix - | .lam _ _ _ _ _ _ bodyTree _ _ _ _ _ _ => .lam bodyTree.lambdaPrefix - | .lamBeta _ _ _ _ _ _ bodyTree _ _ _ _ _ _ _ => by + | .lam _ _ _ _ _ bodyTree _ _ _ _ _ _ => .lam bodyTree.lambdaPrefix + | .lamBeta _ _ _ _ _ bodyTree _ _ _ _ _ _ _ => by have depth := bodyTree.lambdaPrefix.lambdaDepth_zero (AExpr.appN_ne_forallE (by intro condition domain body same; cases same) _) simpa only [AExpr.lambdaDepth, depth] using LambdaPrefix.lam (LambdaPrefix.zero _ _) @@ -1774,7 +1770,7 @@ theorem SynthesisInference.soundWithSpine {β : Type u} obtain ⟨functionTypeReads, functionTyped, functionFormed, functionSpine⟩ := functionTree.soundWithSpine formed keyedAgreement fnReads trace.functionRun obtain ⟨domainReads, codomainReads⟩ := readScopedExpr?_all_parts functionTypeReads - have argumentAgreement := trace.contextPreserved.symm ▸ keyedAgreement + have argumentAgreement := keyedAgreement.congr trace.contextPreserved.symm obtain ⟨argumentTypeReads, argumentTyped, _, _⟩ := argumentTree.soundWithSpine formed argumentAgreement argReads trace.argumentRun have sameReading := beq_readScopedExpr? (resolve := resolve) (locals := locals) @@ -1797,7 +1793,7 @@ theorem SynthesisInference.soundWithSpine {β : Type u} obtain ⟨functionTypeReads, functionTyped, _, functionSpine⟩ := functionTree.soundWithSpine formed keyedAgreement fnReads trace.functionRun obtain ⟨domainReads, codomainReads, _⟩ := exposure.reading functionTypeReads exposureCoherent - have argumentAgreement := (trace.exposure_context exposure).symm ▸ keyedAgreement + have argumentAgreement := keyedAgreement.congr trace.exposure_context.symm obtain ⟨argumentTypeReads, argumentTyped, _, _⟩ := argumentTree.soundWithSpine formed argumentAgreement argReads trace.argumentRun have sameType := AExpr.eq_of_erase_annotations @@ -1812,16 +1808,16 @@ theorem SynthesisInference.soundWithSpine {β : Type u} rw [trace.output run, AExpr.erase_inst] exact (subst_readScopedExpr? bodyConstructed argConstructed bodyBound argBound coherent faithful codomainReads argReads).1 - | .forallE miss trace opening absent domainTree bodyTree levelFaithful domainBound bodyBound + | .forallE miss trace opening domainTree bodyTree levelFaithful domainBound bodyBound coherent faithful => by obtain ⟨state, run⟩ := infer_uncached_success miss accepted obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement obtain ⟨_, domainTyped, _, _⟩ := domainTree.soundWithSpine formed keyedAgreement domainReads trace.domainRun - have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm obtain ⟨_, openedReads, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun + openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReads bodyReads trace.openRun obtain ⟨_, bodyTyped, _, _⟩ := bodyTree.soundWithSpine (formed.push domainTyped) openedAgreement openedReads trace.bodyRun refine ⟨?_, ?_, TypingClaim.sort _, @@ -1833,7 +1829,7 @@ theorem SynthesisInference.soundWithSpine {β : Type u} (Theory.VLevel.equiv_def.mpr fun levels => (Theory.VLevel.equiv_def.mp (readLevel_mkIMax levelFaithful domainBound bodyBound) levels).symm) |>.typing (TypingClaim.forallE domainTyped bodyTyped rfl) - | .lam full miss trace opening absent domainTree bodyTree conditionAgrees constructed bound coherent + | .lam full miss trace opening domainTree bodyTree conditionAgrees constructed bound coherent closingFaithful faithful => by obtain ⟨state, run⟩ := infer_uncached_success miss accepted rw [full] at run @@ -1841,9 +1837,9 @@ theorem SynthesisInference.soundWithSpine {β : Type u} have keyedAgreement := miss.localContext.symm ▸ agreement obtain ⟨_, domainTyped, _, _⟩ := domainTree.soundWithSpine formed keyedAgreement domainReads trace.domainRun - have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm obtain ⟨_, openedReads, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun + openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReads bodyReads trace.openRun obtain ⟨bodyTypeReads, bodyTyped, bodyFormed, _⟩ := bodyTree.soundWithSpine (formed.push domainTyped) openedAgreement openedReads trace.bodyRun obtain ⟨closedReads, closedCoherent⟩ := abstractFVars_readScopedExpr? constructed bound coherent @@ -1854,7 +1850,7 @@ theorem SynthesisInference.soundWithSpine {β : Type u} rw [trace.output run, internExpr_readScopedExpr? (table := trace.abstracted.2) closedCoherent faithful] simp [LambdaInferenceTrace.abstracted, domainReads, closedReads, AExpr.erase] - | .lamBeta full miss trace opening absent domainTree bodyTree origin reduction conditionAgrees + | .lamBeta full miss trace opening domainTree bodyTree origin reduction conditionAgrees constructed bound closingFaithful faithful => by obtain ⟨state, run⟩ := infer_uncached_success miss accepted rw [full] at run @@ -1862,9 +1858,9 @@ theorem SynthesisInference.soundWithSpine {β : Type u} have keyedAgreement := miss.localContext.symm ▸ agreement obtain ⟨_, domainTyped, _, _⟩ := domainTree.soundWithSpine formed keyedAgreement domainReads trace.domainRun - have domainAgreement := trace.contextPreserved.symm ▸ keyedAgreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm obtain ⟨_, openedReads, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement absent domainReads bodyReads trace.openRun + openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReads bodyReads trace.openRun obtain ⟨bodyTypeReads, bodyTyped, _, _⟩ := bodyTree.soundWithSpine (formed.push domainTyped) openedAgreement openedReads trace.bodyRun obtain ⟨conversion, reducedTyped⟩ := origin.sound formed @@ -1993,7 +1989,7 @@ theorem SynthesisCheckedOrigin.soundWithSpine {β : Type u} {resolve : Address (functionTree.soundWithSpine contextFormation agreement functionReading trace.functionRun).1 have domainReads := (readScopedExpr?_all_parts functionTypeReads).1 obtain ⟨argumentTypeReads, argumentTyped, _, argumentSpine⟩ := - argumentTree.soundWithSpine contextFormation (trace.contextPreserved.symm ▸ agreement) + argumentTree.soundWithSpine contextFormation (agreement.congr trace.contextPreserved.symm) argumentReading trace.argumentRun have sameType := AExpr.eq_of_erase_annotations (Option.some.inj (argumentTypeReads.symm.trans @@ -2006,7 +2002,7 @@ theorem SynthesisCheckedOrigin.soundWithSpine {β : Type u} {resolve : Address (functionTree.soundWithSpine contextFormation agreement functionReading trace.functionRun).1 have domainReads := (exposure.reading functionTypeReads exposureCoherent).1 obtain ⟨argumentTypeReads, argumentTyped, _, argumentSpine⟩ := - argumentTree.soundWithSpine contextFormation ((trace.exposure_context exposure).symm ▸ agreement) + argumentTree.soundWithSpine contextFormation (agreement.congr trace.exposure_context.symm) argumentReading trace.argumentRun have sameType := AExpr.eq_of_erase_annotations (Option.some.inj (argumentTypeReads.symm.trans @@ -2018,7 +2014,7 @@ theorem SynthesisCheckedOrigin.soundWithSpine {β : Type u} {resolve : Address functionTree.synthesis head agreement functionReading trace.functionRun have domainReads := (readScopedExpr?_all_parts functionTypeReads).1 obtain ⟨argumentTypeReads, argumentChecked⟩ := - argumentTree.sound (trace.contextPreserved.symm ▸ agreement) argumentReading trace.argumentRun + argumentTree.sound (agreement.congr trace.contextPreserved.symm) argumentReading trace.argumentRun have sameType := AExpr.eq_of_erase_annotations (Option.some.inj (argumentTypeReads.symm.trans ((beq_readScopedExpr? comparisonFaithful hashPath).trans domainReads))) conditions diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 77da2039b..30acab654 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -122,7 +122,15 @@ increasing sequence of strongly inaccessible cardinals. the next allocated id is absent from its registered locals. Let inference now derives its domain/value/comparison frames, freshness, and final scope restoration from the actual calls. Its trace no longer assumes local-context - equalities. This structural result does not establish semantic inference, + equalities. Forall, lambda, and application traces likewise carry the initial + structural invariant and derive context restoration from their recorded + calls. Binder and synthesis constructors no longer supply a separate + freshness proof. Application Pi exposure derives its context frame for the + actual reduction call, independently of the supported beta path. Retained + codomain and lambda-body checks, beta derivations, and cache histories all + use these derived facts. Constructing the complete traces and their initial + state resources from arbitrary accepted programs remains open. + This structural result does not establish semantic inference, reduction, or cache validity for the unsupported branches. - `instantiateUnivParams_readScopedAnnotated` brings the actual substituted declaration type into any active local context, preserving its closed term @@ -388,8 +396,9 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) - Binder definitions supply finite inference trees for both the value and its separately checked declared type, exact source readings, closed annotated syntax, and references to the preceding interface. Recursive calls use the actual - smaller method table. Binder-opening and abstraction resources cover fresh - ids, context preservation during domain inference, intern-table coherence, + smaller method table. The initial structural local invariant supplies fresh + ids and context preservation through the actual domain call. Binder-opening + and abstraction resources retain intern-table coherence, finite collision freedom, and bounds preventing index overflow. Sort exposures are syntactic; lambdas use full mode and the unchanged cheap-beta path. `CheckingClaim` derives body validity and membership once declared-type @@ -440,8 +449,9 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) - Applications use full mode, syntactic or supported beta Pi exposure, an ordinary argument without an eager-reduction marker, and hash comparison with the exposed domain. - Their witnesses retain the actual recursive calls, context preservation - during function inference, and finite substitution resources. Function + Their witnesses retain the actual recursive calls, the initial structural + local invariant, and finite substitution resources. The function and exposure + calls derive the contexts used by argument inference. Function spines can start with locals or admitted constants, or use the synthesis rules for direct lambdas and returned functions. Polymorphic constants supply lazy-lookup agreement on misses (derived from source for bound standalones) @@ -570,7 +580,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 1,230 exact theorem boundaries. The production +The consistency target checks 1,250 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model diff --git a/docs/theory.md b/docs/theory.md index ccbd3074e..e5d6d1c8c 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -121,6 +121,12 @@ that bound, and scope cleanup restores all incoming declarations and lookups. The model reader transports through this observable restoration. Let inference therefore derives its fresh identifier and intermediate contexts from execution, using one initial invariant instead of separate freshness and context premises. +The same derived context transport now serves forall, lambda, and application +inference, including the Pi-exposure call before an argument check. Their +traces retain the initial structural invariant; binder and synthesis nodes +derive freshness instead of storing a separate proof. Retained beta origins +and inference-cache histories use that same transport. General construction +of these traces from accepted execution remains open. This closes the structural local-state component; general semantic state, reduction, conversion, and cache preservation remain separate obligations. Safe definition admission also rejects circular justification in both Lean and From c67db1fb9ea390939204f98c86658602a0635c6a Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 13:20:15 -0400 Subject: [PATCH 41/63] Integrate recursive let inference and retained cache origins --- Ix/Kernel/Verify/Consistency.lean | 30 +- Ix/Kernel/Verify/Consistency/Audit.lean | 37 +- Ix/Kernel/Verify/Consistency/Beta.lean | 259 +-- .../Verify/Consistency/BetaChecking.lean | 34 + .../Verify/Consistency/BetaInference.lean | 157 +- Ix/Kernel/Verify/Consistency/BetaTrace.lean | 63 - Ix/Kernel/Verify/Consistency/BetaTyping.lean | 286 +-- .../Verify/Consistency/BetaWhnfInference.lean | 9 +- .../Verify/Consistency/BinderMeaning.lean | 72 + Ix/Kernel/Verify/Consistency/LetCache.lean | 30 + .../Verify/Consistency/LetInference.lean | 22 + .../Verify/Consistency/LetSynthesis.lean | 91 +- Ix/Kernel/Verify/Consistency/Production.lean | 8 +- .../Consistency/SynthesisCacheExecution.lean | 20 + .../Consistency/SynthesisDerivation.lean | 385 ++++ .../Consistency/SynthesisInference.lean | 1907 ++--------------- .../Verify/Consistency/SynthesisMeaning.lean | 178 ++ .../Verify/Consistency/SynthesisReading.lean | 109 + .../Consistency/SynthesisReduction.lean | 139 ++ .../Verify/Consistency/SynthesisShapes.lean | 424 ++++ .../Verify/Consistency/SynthesisSource.lean | 216 ++ .../Verify/Consistency/SynthesisSupport.lean | 1161 ++++++++++ Tests/Ix/Kernel/Consistency.lean | 52 + docs/kernel-verification.md | 61 +- docs/theory.md | 24 +- 25 files changed, 3150 insertions(+), 2624 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/BetaChecking.lean create mode 100644 Ix/Kernel/Verify/Consistency/BinderMeaning.lean create mode 100644 Ix/Kernel/Verify/Consistency/SynthesisDerivation.lean create mode 100644 Ix/Kernel/Verify/Consistency/SynthesisMeaning.lean create mode 100644 Ix/Kernel/Verify/Consistency/SynthesisReading.lean create mode 100644 Ix/Kernel/Verify/Consistency/SynthesisReduction.lean create mode 100644 Ix/Kernel/Verify/Consistency/SynthesisShapes.lean create mode 100644 Ix/Kernel/Verify/Consistency/SynthesisSource.lean create mode 100644 Ix/Kernel/Verify/Consistency/SynthesisSupport.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 9159df4e6..2b5a4f8b4 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -73,7 +73,7 @@ transport keeps its representation, arithmetic, and dependency assumptions explicit. A production `checkEnvAnon` fragment preserves models of its axiom set for aliases, universe terms, instances of earlier constants, and closed function bodies built from sorts, locals, -polymorphic references, applications, dependent functions, and full-mode lambdas +polymorphic references, applications, dependent functions, full-mode lambdas, and lets under the stated execution resources. Definitions may declare their own universe parameters; model entries retain the exact arity and interpretations at every instance. @@ -139,7 +139,8 @@ Application suffixes and dependent substitutions retain their checks. The corresponding structural-WHNF trace computes each raw result and intern table, then proves the actual uncached loop under its fuel bound. The same traces justify definition conversion. Source inference now constructs a full -beta typing derivation retaining every lambda body and application child. +beta typing derivation retaining every lambda body, application child, and +checked function-type domain and codomain. Dependent substitution rebuilds that derivation beneath retained binders, so each generated result supplies the exact lambda domains for the next step. Forward beta conversion preserves these domains when cheap beta changes a @@ -165,20 +166,26 @@ comparison identifies the declared domain; substitution preserves the complete body checking derivation and inferred-type origin, including beta redexes exposed by the value. The selected cheap-beta operation supplies the returned type. Declaration admission consumes the same check and validation calls. -The three children remain in the existing SynthesisInference fragment; -arbitrary recursive let composition and automatic resource construction -remain open. +The original SynthesisInference datatype now includes this recursive let case, +so lets compose in its children and surrounding binders, applications, and +cache hits. Complete derivations retain function-type children after dependent +substitution exposes their constructor. Returned syntax readings are derived +before the synthesis semantic induction, without a context-formation premise. +The induction proves its own hereditary semantic invariant; source support and +reconstruction do not assume it, and the audit enforces that boundary. +Automatic construction of the initial execution and representation resources +remains open. Local cache hits agree with the actual declaration type. Constant hits agree with pure universe substitution of a loaded, admitted declaration; -sort hits return the canonical successor sort. Applications, foralls, and -lambdas can reuse an earlier successful synthesis check. The cache node +sort hits return the canonical successor sort. Applications, foralls, lambdas, +and lets can reuse an earlier successful synthesis check. The cache node retains its original inference tree, local reading, and execution, so lambda domains and dependent codomain checks remain available to later beta proofs. Successful full inference establishes the exact stored result; cache frames derive its later selection under either checking policy, even with zero fuel. The retained checks also cross interface growth and insertion of locals. -Structural inversion of lifted syntax recovers the original lambda head, -domain, body, and arguments, preserving Pi codomain checks and hereditary +Complete derivations recover the lambda head, domain, body, and arguments +after lifting and substitution, preserving Pi codomain checks and hereditary beta origins. A concrete full-cache resource derives its readings and selection from the earlier call and remains usable in the original synthesis recursion after actual binder opening and supported recursive inference. @@ -218,8 +225,9 @@ resource construction, later context compatibility after scope exit, and the other inference and conversion/cache paths remain open. The operational cache trace includes full lets and their three child calls. The original let check and child cache data derive the full event fold and raw -history, preserving initially populated full keys. Retaining let roots in the -typed synthesis history still needs their recursive inference integration. +history, preserving initially populated full keys. The typed synthesis history +also retains let roots and descendants through their original recursive checks, +so later full-cache selection recovers those checks and their beta origins. Frames allow declaration growth while retaining every old declaration. Verified lazy loading derives such a frame on success and failure, including partial conversion state and fault deduplication. Standalone and block preparation have diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 223f01745..4522c5307 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -54,6 +54,7 @@ private def atomicRoots : Array Lean.Name := #[ ``ForallInferenceTrace.output, ``LambdaInferenceTrace.output, ``BinderInference.sound, ``inferUncached_monomorphic_const_scoped, ``ApplicationInferenceTrace.output, ``BinderInference.soundWithSynthesis, ``BinderInference.synthesis, + ``BinderInference.hereditary, ``SynthesisInference.soundWithHereditary, ``CheckedType.sound, ``TypeFormation.sound, ``SynthesisInference.sound, ``SynthesisInference.closed_sound, ``SynthesisInference.ofSort, ``SynthesisTypeCheck.sound, ``SynthesisInference.ofTypeCheck, @@ -77,7 +78,11 @@ private def formationRoots : Array Lean.Name := #[ ``ContextFormation.empty, ``ContextFormation.push, ``Theory.Model.wellDenoted_of_inst, ``Theory.Model.wellDenoted_inst_iff, ``Theory.Model.TypingClaim.inst, ``Theory.Model.CheckingClaim.inst, - ``Theory.Model.ConversionClaim.inst + ``Theory.Model.ConversionClaim.inst, + ``HereditaryTyping, ``HereditaryTyping.typing, ``HereditaryTyping.lambdaType, + ``HereditaryTyping.lambdaPrefix, ``HereditaryTyping.lambdaSpine, + ``HereditaryTyping.weakenAt, ``HereditaryTyping.extend, ``HereditaryTyping.liftValue, + ``HereditaryTyping.substituteAt ] private def letRoots : Array Lean.Name := #[ @@ -88,7 +93,17 @@ private def letRoots : Array Lean.Name := #[ ``LetInferenceCheck.betaTyping, ``LetInferenceCheck.sound, ``LetInferenceCheck.closed_sound, ``LetInferenceCheck.beta_steps_sound, ``DefinitionBodyTrace.letSupport, ``InferenceCacheHistory.openLet, ``LetInferenceCheck.CacheData, ``LetInferenceCheck.cacheTrace, - ``LetInferenceCheck.cache_maps, ``LetInferenceCheck.cacheHistory + ``LetInferenceCheck.cache_maps, ``LetInferenceCheck.cacheHistory, + ``LetTypeReduction.rigid, ``LetTypeReduction.sound, ``LetInferenceTrace.opened_reading, + ``LetInferenceCheck.asSynthesis, ``LetInferenceCheck.CacheData.asSynthesis, + ``LetInferenceCheck.cacheExecution, ``LetInferenceCheck.synthesisCacheHistory +] + +/-- Structural source reconstruction precedes the semantic induction. +Its support and reader cannot assume the resulting hereditary invariant. -/ +private def recursiveLetShapeRoots : Array Lean.Name := #[ + ``SynthesisInference.letE, ``SynthesisCheckedOrigin.binderType, + ``SynthesisInference.outputReading, ``SynthesisBetaTyping.forallE ] private def instantiationRoots : Array Lean.Name := #[ @@ -209,6 +224,7 @@ private def cacheTransportRoots : Array RootAllowance := #[ ``SynthesisRetainedCheck.typeOrigin, ``SynthesisRetainedCheck.spineOrigin, ``SynthesisRetainedCheck.soundWithSpine, + ``SynthesisRetainedCheck.soundWithHereditary, ``SynthesisRetainedCheck.betaNextOrigin, ``SynthesisRetainedCheck.betaTyping, ``SynthesisVariableSpineOrigin.weakenAt, @@ -563,6 +579,13 @@ private def hereditaryBetaRoots : Array RootAllowance := #[ { root := ``BetaSyntax.steps_betaPrefix, standardAxioms := #[``propext, ``Quot.sound] }, { root := ``SynthesisBetaTyping.origin, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, { root := ``SynthesisBetaTyping.lambdaView, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTyping.ForallView, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTyping.forallView, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTyping.ForallView.sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTyping.ForallView.variableSpine, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTyping.spineOrigin, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTyping.variableSpineOrigin, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisBetaTyping.lambdaBodyVariableSpine, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, { root := ``SynthesisBetaTyping.weakenAt, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, { root := ``SynthesisBetaTyping.substituteAt, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, { root := ``SynthesisBetaTyping.lambdaPrefix, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, @@ -572,7 +595,8 @@ private def hereditaryBetaRoots : Array RootAllowance := #[ { root := ``BetaSubstitutionContext.relation }, { root := ``BetaSubstitutionContext.liftValue, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, { root := ``BinderInference.betaTyping, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, - { root := ``SynthesisInference.betaTyping, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``SynthesisInference.betaTyping, standardAxioms := standard, + nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := #[``HereditaryTyping] }, { root := ``SynthesisInference.beta_steps_sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, { root := ``BetaStepPlan.run, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, { root := ``BetaStepPlan.sourceReading, standardAxioms := standard, nativeAxioms := #[expressionNative] }, @@ -1169,6 +1193,8 @@ def roots : Array RootAllowance := #[ nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction }, { root := ``openLet_sound, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction }, + { root := ``UncachedInference.keyedLocalState, standardAxioms := standard, + nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction }, { root := ``beq_readExpr?, standardAxioms := #[``propext, ``Quot.sound] }, { root := ``internExpr_readExpr?, standardAxioms := #[``propext, ``Quot.sound] }, { root := ``ModelTyping.sort, standardAxioms := standard, @@ -1232,7 +1258,10 @@ def roots : Array RootAllowance := #[ forbiddenDependencies := forbiddenProduction }) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots ++ exposedOriginRoots ++ repeatedBetaRoots ++ betaTraceRoots ++ hereditaryBetaRoots ++ piExposureRoots ++ cacheTransportRoots).map (fun allowance => { - allowance with forbiddenDependencies := forbiddenProduction }) + allowance with forbiddenDependencies := allowance.forbiddenDependencies ++ forbiddenProduction }) + ++ recursiveLetShapeRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], + forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ (localScopeFrameRoots ++ localStateFrameRoots ++ recursiveStateFrameRoots ++ ingressFrameRoots ++ #[``LocalContextReading.congr, ``FramesLocalState.ok, ``FramesLocalState.error, ``IngressM.FramesState.runIntern, ``LocalStateInvariant.freshReading]).map (fun root => { diff --git a/Ix/Kernel/Verify/Consistency/Beta.lean b/Ix/Kernel/Verify/Consistency/Beta.lean index 648802622..5770afd96 100644 --- a/Ix/Kernel/Verify/Consistency/Beta.lean +++ b/Ix/Kernel/Verify/Consistency/Beta.lean @@ -36,86 +36,33 @@ theorem BinderInference.lambda_type {β : Type u} theorem SynthesisInference.lambda_type {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} - {before : TcState .anon} {source : KExpr .anon} {level : VLevel} + {before after : TcState .anon} {source result : KExpr .anon} {level : VLevel} {condition : Certified.PropWhen} {domain body type : AExpr β} (support : SynthesisInference resolve entries locals context bounds fuel before source - (.lam condition domain body) type level) : + (.lam condition domain body) type level) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some (AExpr.lam condition domain body).erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : ∃ codomain, type = .forallE condition domain codomain := by - have leading := support.lambdaPrefix - cases leading with - | lam => exact ⟨_, rfl⟩ - -private theorem BinderInference.no_direct_beta {β : Type u} - {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} - {locals : List FVarId} {context : Model.Context β} {fuel : Nat} - {before : TcState .anon} {source : KExpr .anon} - {condition : Certified.PropWhen} {domain body argument type : AExpr β} - (support : BinderInference resolve entries locals context fuel before source - (.app (.lam condition domain body) argument) type) : False := by - cases support with - | app _ _ _ _ head => cases head + let view := (support.betaTyping .current agreement reading accepted).lambdaView rfl + exact ⟨view.codomain, view.typeEq⟩ -private theorem lambda_inference_domain {β : Type u} {resolve : Address → Option (ConstRef β)} - {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} - {bounds : List VLevel} {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} - {condition inferredCondition : Certified.PropWhen} {domain body inferredDomain codomain : AExpr β} - {level : VLevel} - (support : SynthesisInference resolve entries locals context bounds fuel before source - (.lam condition domain body) (.forallE inferredCondition inferredDomain codomain) level) : - condition = inferredCondition ∧ domain = inferredDomain := by - obtain ⟨actualBody, same⟩ := support.lambda_type - cases same - exact ⟨rfl, rfl⟩ - -/-- A beta result retains the lambda's original check and the actual -argument check. No inference call on the substituted body is introduced. -/ +/-- The complete retained derivation supplies the original lambda body and +argument even when a let or cache lookup exposes the source redex. -/ def SynthesisInference.betaResultOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel : Nat} - {before : TcState .anon} {source : KExpr .anon} {level : VLevel} + {before after : TcState .anon} {source result : KExpr .anon} {level : VLevel} {condition : Certified.PropWhen} {domain body argument type : AExpr β} (support : SynthesisInference resolve entries locals context bounds fuel before source (.app (.lam condition domain body) argument) type level) (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) (agreement : LocalContextReading resolve locals before.lctx context) (reading : readScopedExpr? resolve locals source = - some (AExpr.app (.lam condition domain body) argument).erase) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context (body.inst argument) type := by - cases support with - | known inference _ => exact False.elim inference.no_direct_beta - | reuseType inference => exact False.elim inference.no_direct_beta - | cached tree priorAgreement priorReading _ _ _ _ => - exact tree.betaResultOrigin contextOrigin priorAgreement priorReading - | cachedFrom check _ _ => - have trace := (check.spineOrigin (.lam condition domain body) [argument] rfl).betaTrace - (count := 1) (by simp only [AExpr.lambdaDepth]; omega) - simpa only [AExpr.betaPrefix, AExpr.appN_cons, AExpr.appN_nil] using - SynthesisTypingOrigin.rebase contextOrigin (.reduced trace) - | app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful - bodyConstructed argConstructed bodyBound argBound coherent faithful => - obtain ⟨rfl, rfl⟩ := lambda_inference_domain functionTree - obtain ⟨functionReads, argumentReads⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - exact (SynthesisTypingOrigin.lambdaBody - (.source (.checked contextOrigin functionTree keyedAgreement functionReads trace.functionRun))).substituteAt - (.source (.applicationArgument contextOrigin trace functionTree argumentTree keyedAgreement - functionReads argumentReads conditions hashPath comparisonFaithful)) .root - | appBeta full miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath - comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful => - have same := reduction.rigid (by - obtain ⟨_, rfl⟩ := functionTree.lambda_type - intro fn arg same - cases same) - cases same - obtain ⟨rfl, rfl⟩ := lambda_inference_domain functionTree - obtain ⟨functionReads, argumentReads⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - exact (SynthesisTypingOrigin.lambdaBody - (.source (.checked contextOrigin functionTree keyedAgreement functionReads trace.functionRun))).substituteAt - (.source (.applicationBetaArgument contextOrigin trace functionTree exposure exposureCoherent argumentTree - keyedAgreement functionReads argumentReads conditions hashPath comparisonFaithful)) .root - -termination_by sizeOf support + some (AExpr.app (.lam condition domain body) argument).erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context (body.inst argument) type := + .reduced (support.betaTyping contextOrigin agreement reading accepted).betaStep.2 /-- Inserting a local preserves both ends of a retained reduction and all arguments in its checked prefix. -/ @@ -130,129 +77,35 @@ def SynthesisReductionOrigin.weakenAt {β : Type u} {resolve : Address → Optio .traced (by simpa only [AExpr.liftN_appN, AExpr.liftN_betaPrefix, AExpr.liftN] using SynthesisBetaTrace.weakenAt (.origin origin) insertion) -mutual - -/-- When the lambda body applies its parameter, its actual body checks -supply the next reduction origin after beta exposes the supplied lambda. -The source's result type comes from the preceding check even if the body -was originally inferred at a type requiring cheap-beta conversion. -/ -private def SynthesisInference.betaNextOriginAux {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel count : Nat} - {before : TcState .anon} {source : KExpr .anon} {level bound : VLevel} - {condition headCondition : Certified.PropWhen} {term domain binder inner type : AExpr β} - {initialArguments arguments : List (AExpr β)} - (support : SynthesisInference resolve entries locals context bounds fuel before source term type bound) - (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = some term.erase) - (enough : count ≤ inner.lambdaDepth + 1) (resultEquation : AExpr.sort level = type) : - term = .app (.lam condition domain ((AExpr.bvar 0).appN arguments)) - ((AExpr.lam headCondition binder inner).appN initialArguments) → - SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries context - (.lam headCondition binder inner) - (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments))) - count level := - match support with - | .known inference _ | .reuseType inference .. => fun same => by - cases same - exact False.elim inference.no_direct_beta - | .cached tree priorAgreement priorReading _ _ _ _ => fun same => - tree.betaNextOriginAux contextOrigin priorAgreement priorReading enough resultEquation same - | .cachedFrom check _ _ => fun same => - .traced (.rebase contextOrigin (.origin (check.betaNextOrigin enough resultEquation same))) - | node@(.app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful - bodyConstructed argConstructed bodyBound argBound coherent faithful) => fun same => by - cases same - have resultOrigin := node.betaResultOrigin contextOrigin agreement reading - rw [← resultEquation] at resultOrigin - obtain ⟨rfl, rfl⟩ := lambda_inference_domain functionTree - obtain ⟨functionReads, argumentReads⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - obtain ⟨_, bodySpine⟩ := functionTree.lambdaBodyVariableSpine contextOrigin keyedAgreement functionReads - have flattened := SynthesisReductionOrigin.substitutedResult bodySpine.spine bodySpine.atIndex - (.applicationArgument contextOrigin trace functionTree argumentTree keyedAgreement functionReads argumentReads - conditions hashPath comparisonFaithful) .root enough - (by simpa only [AExpr.inst_variable_appN] using resultOrigin) - simpa only [AExpr.liftN_zero, List.map_id'] using flattened - | node@(.appBeta full miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath - comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful) => fun shape => by - cases shape - have resultOrigin := node.betaResultOrigin contextOrigin agreement reading - rw [← resultEquation] at resultOrigin - have same := reduction.rigid (by - obtain ⟨_, rfl⟩ := functionTree.lambda_type - intro fn arg same - cases same) - cases same - obtain ⟨rfl, rfl⟩ := lambda_inference_domain functionTree - obtain ⟨functionReads, argumentReads⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - obtain ⟨_, bodySpine⟩ := functionTree.lambdaBodyVariableSpine contextOrigin keyedAgreement functionReads - have flattened := SynthesisReductionOrigin.substitutedResult bodySpine.spine bodySpine.atIndex - (.applicationBetaArgument contextOrigin trace functionTree exposure exposureCoherent argumentTree - keyedAgreement functionReads argumentReads conditions hashPath comparisonFaithful) .root enough - (by simpa only [AExpr.inst_variable_appN] using resultOrigin) - simpa only [AExpr.liftN_zero, List.map_id'] using flattened - - | .fvar .. | .forallE .. | .lam .. | .lamBeta .. => fun same => by cases same -termination_by structural support - +/-- Substitution keeps the next exposed lambda's entire derivation, +so a subsequent prefix can consume any of its retained argument checks. -/ def SynthesisRetainedCheck.betaNextOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} {incomingBounds : List VLevel} {term type : AExpr β} {level bound : VLevel} {count : Nat} {condition headCondition : Certified.PropWhen} {domain binder inner : AExpr β} {initialArguments arguments : List (AExpr β)} (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type bound) - (enough : count ≤ inner.lambdaDepth + 1) (resultEquation : AExpr.sort level = type) : - term = .app (.lam condition domain ((AExpr.bvar 0).appN arguments)) - ((AExpr.lam headCondition binder inner).appN initialArguments) → + (enough : count ≤ inner.lambdaDepth + 1) (resultEquation : AExpr.sort level = type) + (same : term = .app (.lam condition domain ((AExpr.bvar 0).appN arguments)) + ((AExpr.lam headCondition binder inner).appN initialArguments)) : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries context (.lam headCondition binder inner) (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments))) - count level := - match check with - | .source contextOrigin tree agreement reading _ => fun same => - tree.betaNextOriginAux contextOrigin agreement reading enough resultEquation same - | .extend prior extension => fun same => - (prior.betaNextOrigin enough resultEquation same).map (.pure (.extend .refl extension)) - | .rebase origin prior => fun same => - .traced (.rebase origin (.origin (prior.betaNextOrigin enough resultEquation same))) - | .weakenAt (cutoff := cutoff) prior insertion => fun same => by - let application := liftedApplicationView same - let lambda := liftedLambdaView application.functionEq.symm - let body := liftedVariableSpineView lambda.bodyEq.symm - let argument := liftedSpineView application.argumentEq.symm - let head := liftedLambdaView argument.headEq.symm - have originalIndex : body.originalIndex = 0 := by - have shifted := body.indexEq - simp only [liftVar] at shifted - split at shifted <;> omega - have bodyEq : lambda.originalBody = (AExpr.bvar 0).appN body.originalArguments := by - simpa only [originalIndex] using body.sourceEq - have functionEq := lambda.sourceEq.trans - (congrArg (AExpr.lam condition lambda.originalDomain) bodyEq) - have argumentEq := argument.sourceEq.trans - (congrArg (AExpr.appN · argument.originalArguments) head.sourceEq) - have originalEnough : count ≤ head.originalBody.lambdaDepth + 1 := by - simpa only [head.bodyEq, AExpr.lambdaDepth_liftN] using enough - have originalResult := (liftN_sort_inv resultEquation.symm).symm - let child := prior.betaNextOrigin originalEnough originalResult - (application.sourceEq.trans (congr (congrArg AExpr.app functionEq) argumentEq)) - have lifted := child.weakenAt insertion - simpa only [AExpr.liftN_appN, AExpr.liftN_betaPrefix, List.map_append, List.map_map, - Function.comp_def, AExpr.liftN_inst_zero, AExpr.liftN, head.domainEq, head.bodyEq, - argument.argumentsEq, body.argumentsEq] using lifted -termination_by structural check + count level := by + subst term + subst type + have next := check.betaTyping.betaStep.1 + have flattened : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context + ((AExpr.lam headCondition binder inner).appN + (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments)))) + (.sort level) := by + simpa only [BetaSyntax.step, AExpr.inst_variable_appN, AExpr.liftN_zero, List.map_id'] using next + exact .traced ((flattened.spineOrigin _ _ rfl).betaTrace enough) -end - -/-- Retain the next lambda origin through any number of actual inference -cache hits, with the same source type and dependent argument checks. -/ def SynthesisInference.betaNextOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel count : Nat} - {before : TcState .anon} {source : KExpr .anon} {level bound : VLevel} + {before after : TcState .anon} {source result : KExpr .anon} {level bound : VLevel} {condition headCondition : Certified.PropWhen} {domain binder inner : AExpr β} {initialArguments arguments : List (AExpr β)} (support : SynthesisInference resolve entries locals context bounds fuel before source @@ -263,12 +116,13 @@ def SynthesisInference.betaNextOrigin {β : Type u} {resolve : Address → Optio (reading : readScopedExpr? resolve locals source = some (AExpr.app (.lam condition domain ((AExpr.bvar 0).appN arguments)) ((AExpr.lam headCondition binder inner).appN initialArguments)).erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) (enough : count ≤ inner.lambdaDepth + 1) : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries context (.lam headCondition binder inner) (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments))) count level := - support.betaNextOriginAux contextOrigin agreement reading enough rfl rfl + (SynthesisRetainedCheck.source contextOrigin support agreement reading accepted).betaNextOrigin enough rfl rfl /-- A supported successful inference of a source beta redex derives both equality with the substitution result and typing of that result. No run @@ -288,52 +142,7 @@ theorem SynthesisInference.beta_sound {β : Type u} ConversionClaim.{u,v} entries context (.app (.lam condition domain body) argument) (body.inst argument) ∧ TypingClaim.{u,v} entries context (body.inst argument) type := by - cases support with - | known inference => exact False.elim inference.no_direct_beta - | reuseType inference => exact False.elim inference.no_direct_beta - | cached tree priorAgreement priorReading priorRun _ _ _ => - exact tree.beta_sound formed priorAgreement priorReading priorRun - | cachedFrom check _ _ => - have spine := (check.soundWithSpine formed).2.2 - simpa only [AExpr.betaPrefix, AExpr.appN_cons, AExpr.appN_nil] using - spine.betaPrefix (arguments := [argument]) (count := 1) (by omega) - | app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful - bodyConstructed argConstructed bodyBound argBound coherent faithful => - obtain ⟨codomain, sameProduct⟩ := functionTree.lambda_type - cases sameProduct - obtain ⟨fnReads, argReads⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - obtain ⟨functionTypeReads, functionTyped, _⟩ := - functionTree.sound formed keyedAgreement fnReads trace.functionRun - obtain ⟨domainReads, _⟩ := readScopedExpr?_all_parts functionTypeReads - have argumentAgreement := keyedAgreement.congr trace.contextPreserved.symm - obtain ⟨argumentTypeReads, argumentTyped, _⟩ := - argumentTree.sound formed argumentAgreement argReads trace.argumentRun - have sameReading := beq_readScopedExpr? (resolve := resolve) (locals := locals) - (depth := 0) comparisonFaithful hashPath - have sameType := AExpr.eq_of_erase_annotations - (Option.some.inj (argumentTypeReads.symm.trans (sameReading.trans domainReads))) conditions - have typedArgument := sameType ▸ argumentTyped - exact ⟨ConversionClaim.beta functionTyped typedArgument, - TypingClaim.betaResult functionTyped typedArgument⟩ - | appBeta full miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath - comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful => - have same := reduction.rigid (by - obtain ⟨_, rfl⟩ := functionTree.lambda_type - intro fn arg same - cases same) - cases same - obtain ⟨rfl, rfl⟩ := lambda_inference_domain functionTree - obtain ⟨fnReads, argReads⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - obtain ⟨_, functionTyped, _⟩ := functionTree.sound formed keyedAgreement fnReads trace.functionRun - have checked := SynthesisCheckedOrigin.applicationBetaArgument .current trace functionTree exposure - exposureCoherent argumentTree keyedAgreement fnReads argReads conditions hashPath comparisonFaithful - have typedArgument := (checked.soundWithSpine formed).1 - exact ⟨ConversionClaim.beta functionTyped typedArgument, - TypingClaim.betaResult functionTyped typedArgument⟩ - -termination_by sizeOf support + exact (support.betaTyping .current agreement reading accepted).betaStep.2.sound formed /-- Two successive beta prefixes can use different lambda origins. The second comes from an actual supplied argument, rather than a fresh check @@ -363,7 +172,7 @@ theorem SynthesisInference.beta_twice_sound {β : Type u} {resolve : Address → (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments)))) (.sort level) := by have first := support.beta_sound formed agreement reading accepted - have next := (support.betaNextOrigin .current agreement reading enough).sound formed + have next := (support.betaNextOrigin .current agreement reading accepted enough).sound formed refine ⟨?_, next.2⟩ apply ConversionClaim.trans (b := (AExpr.lam headCondition binder inner).appN (initialArguments ++ arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments)))) diff --git a/Ix/Kernel/Verify/Consistency/BetaChecking.lean b/Ix/Kernel/Verify/Consistency/BetaChecking.lean new file mode 100644 index 000000000..31733f027 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaChecking.lean @@ -0,0 +1,34 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaTyping + +/-! Recover syntactic checking evidence from complete retained derivations. +These operations also apply after dependent substitution and head reduction, +when the resulting constructor can come from a substituted value. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +namespace SynthesisBetaTyping + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext : Model.Context β} {incomingBounds : List VLevel} + +theorem ForallView.sound {context : Model.Context β} {condition : Certified.PropWhen} {domain body : AExpr β} + (view : ForallView (resolve := resolve) (incoming := incoming) (incomingContext := incomingContext) + (incomingBounds := incomingBounds) (entries := entries) context condition domain body) + (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : + TypingClaim.{u,v} entries context domain (.sort view.domainLevel) ∧ + TypingClaim.{u,v} entries (context.push domain) body (.sort view.bodyLevel) := + ⟨view.domainCheck.origin.sound formed, view.bodyCheck.origin.sound formed⟩ + + +end SynthesisBetaTyping + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaInference.lean b/Ix/Kernel/Verify/Consistency/BetaInference.lean index 2ea06371c..e51375e2a 100644 --- a/Ix/Kernel/Verify/Consistency/BetaInference.lean +++ b/Ix/Kernel/Verify/Consistency/BetaInference.lean @@ -3,7 +3,7 @@ Copyright (c) 2026 Argument Computer Corporation. SPDX-License-Identifier: MIT OR Apache-2.0 -/ -import Ix.Kernel.Verify.Consistency.BetaTyping +import Ix.Kernel.Verify.Consistency.BetaChecking /-! Recover the complete head-beta typing derivation from the source inference tree. No checks of intermediate reduction results are supplied. -/ @@ -14,159 +14,6 @@ open Theory Theory.Model universe u v -def BinderInference.betaTyping {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} - {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} - (support : BinderInference resolve entries locals context fuel before source term type) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type → - LocalContextReading resolve locals before.lctx context → - readScopedExpr? resolve locals source = some term.erase → - SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type := - match support with - | .sort .. | .cachedSort .. | .const .. | .polymorphic .. | .cachedConst .. | .forallE .. => - fun origin _ _ => .atom origin (by constructor) - | .fvar _ _ atIndex => fun origin _ _ => .bvar origin atIndex - | .app _ miss trace functionTree head argumentTree conditions hashPath comparisonFaithful _ _ _ _ _ _ => - fun _ agreement reading => by - obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - have argumentAgreement := keyedAgreement.congr trace.contextPreserved.symm - have functionTypeReads := - (BinderInference.synthesis.{u,u} functionTree head keyedAgreement functionReading trace.functionRun).1 - have argumentTypeReads := - (BinderInference.sound.{u,u} argumentTree argumentAgreement argumentReading trace.argumentRun).1 - have sameType := AExpr.eq_of_erase_annotations - (Option.some.inj (argumentTypeReads.symm.trans - ((beq_readScopedExpr? comparisonFaithful hashPath).trans - (readScopedExpr?_all_parts functionTypeReads).1))) conditions - have functionOrigin := SynthesisTypingOrigin.source - (SynthesisCheckedOrigin.binderHead (incoming := incoming) (incomingContext := incomingContext) - (incomingBounds := incomingBounds) functionTree head keyedAgreement functionReading trace.functionRun) - have argumentOrigin := SynthesisTypingOrigin.source - (SynthesisCheckedOrigin.binderArgument (incoming := incoming) (incomingContext := incomingContext) - (incomingBounds := incomingBounds) trace functionTree head argumentTree keyedAgreement - functionReading argumentReading conditions hashPath comparisonFaithful) - exact .app (functionTree.betaTyping functionOrigin keyedAgreement functionReading) - (sameType ▸ argumentTree.betaTyping (sameType.symm ▸ argumentOrigin) argumentAgreement argumentReading) - | .lam _ miss trace opening bodyTree _ _ _ _ _ => fun origin agreement reading => by - obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_lam_parts reading - have domainAgreement := (miss.localContext.symm ▸ agreement).congr trace.contextPreserved.symm - obtain ⟨_, openedReading, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReading bodyReading trace.openRun - exact .lam origin (bodyTree.betaTyping (.lambdaBody origin) openedAgreement openedReading) -termination_by structural support - -mutual - -/-- Every currently supported source-inference branch supplies its own -beta derivation. Lambda inference's changed body type becomes a forward -conversion node, retaining the original body's complete checked structure. -/ -def SynthesisInference.betaTyping {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel : Nat} - {before after : TcState .anon} {source result : KExpr .anon} {term type : AExpr β} {level : VLevel} - (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) : - (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) → - LocalContextReading resolve locals before.lctx context → - readScopedExpr? resolve locals source = some term.erase → - RecM.infer source (methodsN fuel) before = .ok result after → - ContextFormation.{u,v} incoming incomingContext incomingBounds → - SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type := - match support with - | .cached tree priorAgreement priorReading priorRun _ _ _ => fun contextOrigin _ _ _ formed => - tree.betaTyping contextOrigin priorAgreement priorReading priorRun formed - | .cachedFrom check _ _ => fun contextOrigin _ _ _ formed => - (check.betaTyping (contextOrigin.sound formed)).rebase contextOrigin - | node@(.known inference _) => fun contextOrigin agreement reading accepted _ => - inference.betaTyping (.source (.checked contextOrigin node agreement reading accepted)) agreement reading - | node@(.reuseType inference ..) => fun contextOrigin agreement reading accepted _ => - inference.betaTyping (.source (.checked contextOrigin node agreement reading accepted)) agreement reading - | node@(.fvar _ atIndex _) => fun contextOrigin agreement reading accepted _ => - .bvar (.source (.checked contextOrigin node agreement reading accepted)) atIndex - | node@(.forallE ..) => fun contextOrigin agreement reading accepted _ => - .atom (.source (.checked contextOrigin node agreement reading accepted)) (by constructor) - | .app _ miss trace functionTree argumentTree conditions hashPath comparisonFaithful _ _ _ _ _ _ => - fun contextOrigin agreement reading _ formed => by - obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - have argumentAgreement := keyedAgreement.congr trace.contextPreserved.symm - have contextFormation := contextOrigin.sound formed - have functionTypeReads := - (functionTree.soundWithSpine contextFormation keyedAgreement functionReading trace.functionRun).1 - have argumentTypeReads := - (argumentTree.soundWithSpine contextFormation argumentAgreement argumentReading trace.argumentRun).1 - have sameType := AExpr.eq_of_erase_annotations - (Option.some.inj (argumentTypeReads.symm.trans - ((beq_readScopedExpr? comparisonFaithful hashPath).trans - (readScopedExpr?_all_parts functionTypeReads).1))) conditions - exact .app (functionTree.betaTyping contextOrigin keyedAgreement functionReading trace.functionRun formed) - (sameType ▸ argumentTree.betaTyping contextOrigin argumentAgreement argumentReading trace.argumentRun formed) - | .appBeta _ miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath - comparisonFaithful _ _ _ _ _ _ => - fun contextOrigin agreement reading _ formed => by - obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - have argumentAgreement := keyedAgreement.congr trace.exposure_context.symm - have contextFormation := contextOrigin.sound formed - have functionTypeReads := - (functionTree.soundWithSpine contextFormation keyedAgreement functionReading trace.functionRun).1 - have argumentTypeReads := - (argumentTree.soundWithSpine contextFormation argumentAgreement argumentReading trace.argumentRun).1 - have sameType := AExpr.eq_of_erase_annotations - (Option.some.inj (argumentTypeReads.symm.trans - ((beq_readScopedExpr? comparisonFaithful hashPath).trans - (exposure.reading functionTypeReads exposureCoherent).1))) conditions - exact .app - (.convert (functionTree.betaTyping contextOrigin keyedAgreement functionReading trace.functionRun formed) - (.rebase contextOrigin reduction)) - (sameType ▸ argumentTree.betaTyping contextOrigin argumentAgreement argumentReading trace.argumentRun formed) - | .lam full miss trace opening domainTree bodyTree conditionAgrees constructed bound coherent - closingFaithful faithful => - fun contextOrigin agreement reading accepted formed => by - let node := SynthesisInference.lam full miss trace opening domainTree bodyTree conditionAgrees - constructed bound coherent closingFaithful faithful - obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_lam_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm - obtain ⟨_, openedReading, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReading bodyReading trace.openRun - exact .lam (.source (.checked contextOrigin node agreement reading accepted)) - (bodyTree.betaTyping (contextOrigin.push domainTree keyedAgreement domainReading trace.domainRun) - openedAgreement openedReading trace.bodyRun formed) - | .lamBeta full miss trace opening domainTree bodyTree reductionOrigin reduction conditionAgrees - constructed bound closingFaithful faithful => - fun contextOrigin agreement reading accepted formed => by - let node := SynthesisInference.lamBeta full miss trace opening domainTree bodyTree reductionOrigin reduction - conditionAgrees constructed bound closingFaithful faithful - obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_lam_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm - obtain ⟨_, openedReading, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReading bodyReading trace.openRun - have inner := bodyTree.betaTyping - (contextOrigin.push domainTree keyedAgreement domainReading trace.domainRun) - openedAgreement openedReading trace.bodyRun formed - exact .lam (.source (.checked contextOrigin node agreement reading accepted)) - (.convert inner (.rebase contextOrigin (.origin reductionOrigin))) -termination_by structural support - -def SynthesisRetainedCheck.betaTyping {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} - (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) : - ContextFormation.{u,v} incoming incomingContext incomingBounds → - SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type := - match check with - | .source contextOrigin tree agreement reading accepted => fun formed => - tree.betaTyping contextOrigin agreement reading accepted formed - | .extend prior extension => fun formed => (prior.betaTyping formed).extend extension - | .weakenAt prior insertion => fun formed => (prior.betaTyping formed).weakenAt insertion - | .rebase origin prior => fun formed => (prior.betaTyping (origin.sound formed)).rebase origin -termination_by structural check - -end - theorem SynthesisInference.beta_steps_sound {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} @@ -178,6 +25,6 @@ theorem SynthesisInference.beta_steps_sound {β : Type u} {resolve : Address → (accepted : RecM.infer source (methodsN fuel) before = .ok result after) (count : Nat) : ConversionClaim.{u,v} entries context term (BetaSyntax.steps count term) ∧ TypingClaim.{u,v} entries context (BetaSyntax.steps count term) type := - ((support.betaTyping .current agreement reading accepted formed).betaSteps count).2.sound formed + ((support.betaTyping .current agreement reading accepted).betaSteps count).2.sound formed end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaTrace.lean b/Ix/Kernel/Verify/Consistency/BetaTrace.lean index 9062acfe0..a628d3bba 100644 --- a/Ix/Kernel/Verify/Consistency/BetaTrace.lean +++ b/Ix/Kernel/Verify/Consistency/BetaTrace.lean @@ -49,20 +49,6 @@ def SynthesisBetaTrace.applySpine {β : Type u} {resolve : Address → Option (C | .convert prior typeTrace => .convertType (trace.applySpine prior) typeTrace termination_by structural spine -/-- One beta step may use a lambda produced by an earlier trace. Its -retained product has the lambda's exact syntactic domain. -/ -def SynthesisBetaTrace.beta {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {condition : Certified.PropWhen} {domain body codomain argument : AExpr β} - (functionOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context - (.lam condition domain body) (.forallE condition domain codomain)) - (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context argument domain) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context - (.app (.lam condition domain body) argument) (body.inst argument) (codomain.inst argument) := by - simpa only [List.nil_append, AExpr.appN_cons, AExpr.appN_nil, AExpr.betaPrefix] using - SynthesisBetaTrace.prefix functionOrigin (.lam (.zero _ _)) - ((SynthesisArgumentSpineOrigin.nil _).snoc argumentOrigin) - /-- Any original lambda prefix selected from the source inference starts a composable trace whose result can be used as another typing origin. -/ def SynthesisInference.betaSpineTrace {β : Type u} {resolve : Address → Option (ConstRef β)} @@ -80,53 +66,4 @@ def SynthesisInference.betaSpineTrace {β : Type u} {resolve : Address → Optio (head.appN arguments) (AExpr.betaPrefix count head arguments) type := (support.spineOrigin contextOrigin agreement reading accepted head arguments rfl).betaTrace enough -namespace BetaSyntax - -/-- One leftmost beta contraction, retaining the application suffix. -A term without a beta redex at its head is unchanged. -/ -def step : AExpr β → AExpr β - | .app (.lam _ _ body) argument => body.inst argument - | .app fn arg => .app (step fn) arg - | term => term - -def steps : Nat → AExpr β → AExpr β - | 0, term => term - | count + 1, term => steps count (step term) - -private theorem step_appN {β : Type u} (head : AExpr β) (arguments : List (AExpr β)) - (notLam : ∀ condition domain body, head ≠ .lam condition domain body) : - step (head.appN arguments) = (step head).appN arguments := by - induction arguments generalizing head with - | nil => rfl - | cons argument arguments ih => - rw [AExpr.appN_cons, ih (head.app argument) (by intro condition domain body same; cases same)] - cases head <;> simp_all [step, AExpr.appN_cons] - -theorem step_beta_appN {β : Type u} (condition : Certified.PropWhen) - (domain body argument : AExpr β) (arguments : List (AExpr β)) : - step ((AExpr.lam condition domain body).appN (argument :: arguments)) = - (body.inst argument).appN arguments := by - rw [AExpr.appN_cons, step_appN _ _ (by intro condition domain body same; cases same)] - rfl - -theorem steps_betaPrefix {β : Type u} (count : Nat) (head : AExpr β) (arguments : List (AExpr β)) - (leading : count ≤ head.lambdaDepth) (supplied : count ≤ arguments.length) : - steps count (head.appN arguments) = AExpr.betaPrefix count head arguments := by - induction count generalizing head arguments with - | zero => rfl - | succ count ih => - cases head with - | lam condition domain body => - cases arguments with - | nil => simp at supplied - | cons argument arguments => - simp only [steps, step_beta_appN, AExpr.betaPrefix] - apply ih - · exact Nat.le_trans (by simpa only [AExpr.lambdaDepth, Nat.add_le_add_iff_right] using leading) - (AExpr.lambdaDepth_le_inst body argument 0) - · simpa only [List.length_cons, Nat.add_le_add_iff_right] using supplied - | _ => simp [AExpr.lambdaDepth] at leading - -end BetaSyntax - end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaTyping.lean b/Ix/Kernel/Verify/Consistency/BetaTyping.lean index 3ea1e2c64..a5acef2a0 100644 --- a/Ix/Kernel/Verify/Consistency/BetaTyping.lean +++ b/Ix/Kernel/Verify/Consistency/BetaTyping.lean @@ -5,287 +5,5 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Verify.Consistency.BetaTrace -/-! Retain the checked lambda bodies and application arguments needed for -hereditary beta substitution. Generated results carry the same derivation, -so the next reduction does not require another inference call or origin. -/ - -namespace Ix.Kernel.Consistency - -open Theory Theory.Model - -universe u v - -/-- Constructors that remain outside head beta reduction after substitution. -/ -inductive BetaAtom {β : Type u} : AExpr β → Prop - | sort (level : VLevel) : BetaAtom (.sort level) - | const (ref : ConstRef β) (levels : List VLevel) : BetaAtom (.const ref levels) - | forallE (condition : Certified.PropWhen) (domain body : AExpr β) : BetaAtom (.forallE condition domain body) - | proj (ref : ConstRef β) (field : Nat) (major : AExpr β) : BetaAtom (.proj ref field major) - | natLit (value : Nat) : BetaAtom (.natLit value) - -theorem BetaAtom.liftN {β : Type u} {term : AExpr β} (atom : BetaAtom term) (count cutoff : Nat) : - BetaAtom (term.liftN count cutoff) := by - cases atom <;> constructor - -theorem BetaAtom.inst {β : Type u} {term : AExpr β} (atom : BetaAtom term) (argument : AExpr β) (cutoff : Nat) : - BetaAtom (term.inst argument cutoff) := by - cases atom <;> constructor - -theorem BetaAtom.not_lam {β : Type u} {term : AExpr β} (atom : BetaAtom term) - (condition : Certified.PropWhen) (domain body : AExpr β) : term ≠ .lam condition domain body := by - cases atom <;> intro same <;> cases same - -theorem BetaAtom.step {β : Type u} {term : AExpr β} (atom : BetaAtom term) : BetaSyntax.step term = term := by - cases atom <;> rfl - -/-- A typing derivation whose leaves retain actual checks. Lambda bodies -and both application children remain available after substitution. Forward -beta conversion changes the retained type without discarding this structure. -/ -inductive SynthesisBetaTyping {β : Type u} (resolve : Address → Option (ConstRef β)) - (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) - (entries : Model.Environment β) : Model.Context β → AExpr β → AExpr β → Type u - | atom {context term type} - (origin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) - (shape : BetaAtom term) : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type - | bvar {context index type} - (origin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context (.bvar index) type) - (atIndex : context[index]? = some type) : - SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (.bvar index) type - | lam {context condition domain body codomain} - (origin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context - (.lam condition domain body) (.forallE condition domain codomain)) - (inner : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries (context.push domain) body codomain) : - SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context - (.lam condition domain body) (.forallE condition domain codomain) - | app {context fn arg condition domain body} - (function : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context fn - (.forallE condition domain body)) - (argument : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context arg domain) : - SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (.app fn arg) (body.inst arg) - | convert {context term sourceType resultType level} - (prior : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term sourceType) - (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context - sourceType resultType (.sort level)) : - SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term resultType - -namespace SynthesisBetaTyping - -variable {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext : Model.Context β} {incomingBounds : List VLevel} - -def origin {context : Model.Context β} {term type : AExpr β} - (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type := - match typing with - | .atom origin _ | .bvar origin _ | .lam origin _ => origin - | .app function argument => .application function.origin argument.origin - | .convert prior trace => .convert prior.origin trace -termination_by structural typing - -structure LambdaView (context : Model.Context β) (condition : Certified.PropWhen) (domain body type : AExpr β) where - codomain : AExpr β - typeEq : type = .forallE condition domain codomain - inner : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries (context.push domain) body codomain - -def lambdaView {context : Model.Context β} {term type : AExpr β} - (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : - {condition : Certified.PropWhen} → {domain body : AExpr β} → term = .lam condition domain body → - LambdaView (resolve := resolve) (incoming := incoming) (incomingContext := incomingContext) - (incomingBounds := incomingBounds) (entries := entries) context condition domain body type := - match typing with - | .atom _ shape => fun same => False.elim (shape.not_lam _ _ _ same) - | .bvar .. | .app .. => fun same => by cases same - | .lam _ inner => fun same => by cases same; exact ⟨_, rfl, inner⟩ - | .convert prior trace => fun same => - let view := prior.lambdaView same - ⟨view.codomain, (trace.rigid (by simp only [view.typeEq]; intro fn arg same; cases same)).trans view.typeEq, - view.inner⟩ -termination_by structural typing - -def weakenAt {source target : Model.Context β} {cutoff : Nat} {term type : AExpr β} - (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries source term type) - (insertion : ContextInsertion source target cutoff) : - SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries target - (term.liftN 1 cutoff) (type.liftN 1 cutoff) := - match typing with - | .atom origin shape => .atom (.weakenAt origin insertion) (shape.liftN 1 cutoff) - | .bvar (index := index) origin found => by - have lifted := SynthesisTypingOrigin.weakenAt origin insertion - have selected := insertion.lookup found - by_cases below : index < cutoff - · simp only [AExpr.liftN, liftVar, below, if_true] at lifted selected ⊢ - exact .bvar lifted selected - · simp only [AExpr.liftN, liftVar, below, if_false, Nat.add_comm 1] at lifted selected ⊢ - exact .bvar lifted selected - | .lam origin inner => .lam (.weakenAt origin insertion) (inner.weakenAt (insertion.push _)) - | .app function argument => by - simpa only [AExpr.liftN, AExpr.liftN_inst_zero] using - SynthesisBetaTyping.app (function.weakenAt insertion) (argument.weakenAt insertion) - | .convert prior trace => .convert (prior.weakenAt insertion) (.weakenAt trace insertion) -termination_by structural typing - -def extend {later : Model.Environment β} {context : Model.Context β} {term type : AExpr β} - (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) - (extension : InterfaceExtends entries later) : - SynthesisBetaTyping resolve incoming incomingContext incomingBounds later context term type := - match typing with - | .atom origin shape => .atom (origin.extend extension) shape - | .bvar origin found => .bvar (origin.extend extension) found - | .lam origin inner => .lam (origin.extend extension) (inner.extend extension) - | .app function argument => .app (function.extend extension) (argument.extend extension) - | .convert prior trace => .convert (prior.extend extension) (.extend trace extension) -termination_by structural typing - -def rebase {priorIncoming : Model.Environment β} {priorContext context : Model.Context β} - {priorBounds : List VLevel} {term type : AExpr β} - (typing : SynthesisBetaTyping resolve priorIncoming priorContext priorBounds entries context term type) - (origin : SynthesisContext resolve incoming incomingContext incomingBounds priorIncoming priorContext priorBounds) : - SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type := - match typing with - | .atom checked shape => .atom (.rebase origin checked) shape - | .bvar checked found => .bvar (.rebase origin checked) found - | .lam checked inner => .lam (.rebase origin checked) (inner.rebase origin) - | .app function argument => .app (function.rebase origin) (argument.rebase origin) - | .convert prior trace => .convert (prior.rebase origin) (.rebase origin trace) -termination_by structural typing - -end SynthesisBetaTyping - -/-- The internal substitution walker builds this data itself as it passes -binders. Erasure gives the existing model context-substitution relation. -/ -inductive BetaSubstitutionContext {β : Type u} (base : Model.Context β) (domain argument : AExpr β) : - Model.Context β → Model.Context β → Nat → Type u - | root : BetaSubstitutionContext base domain argument (base.push domain) base 0 - | push {source target cutoff} (prior : BetaSubstitutionContext base domain argument source target cutoff) - (binder : AExpr β) : - BetaSubstitutionContext base domain argument (source.push binder) - (target.push (binder.inst argument cutoff)) (cutoff + 1) - -theorem BetaSubstitutionContext.relation {β : Type u} {base source target : Model.Context β} - {domain argument : AExpr β} {cutoff : Nat} - (substitution : BetaSubstitutionContext base domain argument source target cutoff) : - ContextSubstitution base domain argument source target cutoff := - match substitution with - | .root => .root - | .push prior binder => .push prior.relation binder - -def BetaSubstitutionContext.liftValue {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext : Model.Context β} {incomingBounds : List VLevel} - {base source target : Model.Context β} {domain argument term type : AExpr β} {cutoff : Nat} - (substitution : BetaSubstitutionContext base domain argument source target cutoff) - (value : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries base term type) : - SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries target - (term.liftN cutoff) (type.liftN cutoff) := - match substitution with - | .root => by simpa only [AExpr.liftN_zero] using value - | .push (target := target) (cutoff := cutoff) prior binder => by - have lifted := (prior.liftValue value).weakenAt (ContextInsertion.root target (binder.inst argument cutoff)) - simpa only [AExpr.liftN_liftN_merge term cutoff 1 0 0 (Nat.le_refl _) (Nat.zero_le _), - AExpr.liftN_liftN_merge type cutoff 1 0 0 (Nat.le_refl _) (Nat.zero_le _)] using lifted -termination_by structural substitution - -def SynthesisBetaTyping.substituteAt {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext : Model.Context β} {incomingBounds : List VLevel} - {base source target : Model.Context β} {domain argument term type : AExpr β} {cutoff : Nat} - (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries source term type) - (value : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries base argument domain) - (substitution : BetaSubstitutionContext base domain argument source target cutoff) : - SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries target - (term.inst argument cutoff) (type.inst argument cutoff) := - match typing with - | .atom origin shape => .atom (.substituteAt origin value.origin substitution.relation) (shape.inst argument cutoff) - | .bvar (index := index) origin found => by - by_cases equal : index = cutoff - · subst index - have sameType := substitution.relation.instantiate_removed_type found - simpa only [AExpr.inst, AExpr.instVar, Nat.lt_irrefl, if_false, if_true, sameType] using - substitution.liftValue value - · have retained := substitution.relation.lookup_other found equal - have typed := SynthesisTypingOrigin.substituteAt origin value.origin substitution.relation - by_cases below : index < cutoff - · simp only [AExpr.inst, AExpr.instVar, below, if_true] at typed retained ⊢ - exact .bvar typed retained - · simp only [AExpr.inst, AExpr.instVar, below, equal, if_false] at typed retained ⊢ - exact .bvar typed retained - | .lam origin inner => - .lam (.substituteAt origin value.origin substitution.relation) - (inner.substituteAt value (substitution.push _)) - | .app function applied => by - simpa only [AExpr.inst, AExpr.inst_inst_zero] using - SynthesisBetaTyping.app (function.substituteAt value substitution) (applied.substituteAt value substitution) - | .convert prior trace => - .convert (prior.substituteAt value substitution) (.substituteAt trace value.origin substitution.relation) -termination_by structural typing - -theorem SynthesisBetaTyping.lambdaPrefix {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {term type : AExpr β} - (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : - LambdaPrefix term type term.lambdaDepth := - match typing with - | .atom _ shape => by cases shape <;> exact .zero _ _ - | .bvar .. | .app .. => .zero _ _ - | .lam _ inner => .lam inner.lambdaPrefix - | .convert (term := term) prior trace => by - have leading := prior.lambdaPrefix - cases term with - | lam condition domain body => - have view := prior.lambdaView rfl - have fixed := trace.rigid (by simp only [view.typeEq]; intro fn arg same; cases same) - simpa only [fixed] using leading - | _ => exact .zero _ _ -termination_by structural typing - -/-- Every head beta contraction computes its next typing derivation and -conversion from the retained source tree, including newly exposed lambdas. -/ -def SynthesisBetaTyping.betaStep {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {term type : AExpr β} - (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : - SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (BetaSyntax.step term) type × - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term (BetaSyntax.step term) type := - match typing with - | .atom origin shape => by rw [shape.step]; exact ⟨.atom origin shape, .refl origin⟩ - | .bvar origin found => ⟨.bvar origin found, .refl origin⟩ - | .lam origin inner => ⟨.lam origin inner, .refl origin⟩ - | .app (fn := fn) function argument => by - have nextFunction := function.betaStep - cases fn with - | lam condition' domain' inner => - have view := function.lambdaView rfl - obtain ⟨rfl, rfl, codomainEq⟩ := AExpr.forallE.inj view.typeEq - refine ⟨?_, SynthesisBetaTrace.beta function.origin argument.origin⟩ - simpa only [codomainEq, BetaSyntax.step] using view.inner.substituteAt argument .root - | bvar | sort | const | app | forallE | proj | natLit => - obtain ⟨next, converted⟩ := nextFunction - exact ⟨.app next argument, .application converted argument.origin⟩ - | .convert prior trace => - let next := prior.betaStep - ⟨.convert next.1 trace, .atType (.convert prior.origin trace) next.2⟩ -termination_by structural typing - -def SynthesisBetaTyping.betaSteps {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} (count : Nat) {term type : AExpr β} - (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : - SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (BetaSyntax.steps count term) type × - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term (BetaSyntax.steps count term) type := - match count with - | 0 => ⟨typing, .refl typing.origin⟩ - | count + 1 => - let first := typing.betaStep - let rest := first.1.betaSteps count - ⟨rest.1, first.2.trans rest.2⟩ - -def SynthesisBetaTyping.betaPrefix {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {head type : AExpr β} {arguments : List (AExpr β)} - (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (head.appN arguments) type) - (count : Nat) (leading : count ≤ head.lambdaDepth) (supplied : count ≤ arguments.length) : - SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context - (AExpr.betaPrefix count head arguments) type × - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context - (head.appN arguments) (AExpr.betaPrefix count head arguments) type := by - simpa only [BetaSyntax.steps_betaPrefix count head arguments leading supplied] using typing.betaSteps count - -end Ix.Kernel.Consistency +/-! Public beta-typing interface. The retained derivation and its structural +reduction operations are shared with recursive source inference. -/ diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean b/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean index 9966912da..06803d064 100644 --- a/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean +++ b/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean @@ -89,11 +89,10 @@ def BetaPiExposure.checkedTrace {β : Type u} {resolve : Address → Option (Con (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) (agreement : LocalContextReading resolve typeLocals typeBefore.lctx context) (reading : readScopedExpr? resolve typeLocals typeSource = some term.erase) - (accepted : RecM.infer typeSource (methodsN typeFuel) typeBefore = .ok typeResult typeAfter) - (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : + (accepted : RecM.infer typeSource (methodsN typeFuel) typeBefore = .ok typeResult typeAfter) : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term (.forallE condition domain body) (.sort level) := - exposure.betaTrace (typeTree.betaTyping contextOrigin agreement reading accepted formed) + exposure.betaTrace (typeTree.betaTyping contextOrigin agreement reading accepted) /-- The original inference supplies every later lambda and argument origin. Only the operational WHNF path and finite representation resources remain as @@ -115,7 +114,7 @@ theorem SynthesisInference.beta_whnf_sound {β : Type u} {resolve : Address → readScopedExpr? resolve locals result = some target.erase ∧ ConversionClaim.{u,v} entries context term target ∧ TypingClaim.{u,v} entries context target type ∧ after.env.intern.WF := by - let typing := support.betaTyping .current agreement reading accepted formed + let typing := support.betaTyping .current agreement reading accepted exact (path.annotate typing).1.uncached_sound typing.origin formed reading coherent enough /-- The public WHNF call includes instrumentation, fuel, and all three @@ -138,7 +137,7 @@ theorem SynthesisInference.beta_public_whnf_sound {β : Type u} {resolve : Addre plan.after.env.intern.WF := by obtain ⟨resultReading, preserved⟩ := plan.reading reading coherent obtain ⟨converted, typed⟩ := - (plan.betaTrace (support.betaTyping .current agreement reading accepted formed)).sound formed + (plan.betaTrace (support.betaTyping .current agreement reading accepted)).sound formed exact ⟨plan.run, resultReading, converted, typed, preserved⟩ end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BinderMeaning.lean b/Ix/Kernel/Verify/Consistency/BinderMeaning.lean new file mode 100644 index 000000000..d0739a647 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BinderMeaning.lean @@ -0,0 +1,72 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.SynthesisMeaning + +/-! The earlier checking interface also supplies the hereditary semantic +invariant, once its expected type has been formed by the caller. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +theorem BinderInference.hereditary {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {locals : List FVarId} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source term type) : + TypingClaim.{u,v} entries context term type → + LocalContextReading resolve locals before.lctx context → + readScopedExpr? resolve locals source = some term.erase → + HereditaryTyping.{u,v} entries context term type := + match support with + | .sort .. | .cachedSort .. | .const .. | .polymorphic .. | .cachedConst .. => + fun typed _ _ => .atom typed (by constructor) + | .fvar _ _ atIndex => fun typed _ _ => .bvar typed atIndex + | .forallE miss trace opening domainTree bodyTree .. => fun typed agreement reading => by + obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_all_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm + obtain ⟨_, openedReading, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement (trace.absent keyedAgreement) + domainReading bodyReading trace.openRun + have domainTyped := (BinderInference.sound.{u,v} domainTree keyedAgreement domainReading trace.domainRun).2.typing + (TypingClaim.sort _) + have bodyTyped := (BinderInference.sound.{u,v} bodyTree openedAgreement openedReading trace.bodyRun).2.typing + (TypingClaim.sort _) + exact .forallE typed (domainTree.hereditary domainTyped keyedAgreement domainReading) + (bodyTree.hereditary bodyTyped openedAgreement openedReading) rfl + | .app (a := a) (A := A) _ miss trace functionTree head argumentTree conditions hashPath comparisonFaithful _ _ _ _ _ _ => + fun _ agreement reading => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have argumentAgreement := keyedAgreement.congr trace.contextPreserved.symm + obtain ⟨functionTypeReads, functionTyped⟩ := + BinderInference.synthesis.{u,v} functionTree head keyedAgreement functionReading trace.functionRun + obtain ⟨argumentTypeReads, argumentChecked⟩ := + BinderInference.sound.{u,v} argumentTree argumentAgreement argumentReading trace.argumentRun + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (argumentTypeReads.symm.trans + ((beq_readScopedExpr? comparisonFaithful hashPath).trans + (readScopedExpr?_all_parts functionTypeReads).1))) conditions + have checked := sameType ▸ argumentChecked + have argumentTyped : TypingClaim.{u,v} entries context a A := by + intro V _ constants realizes levels env valid + have domainValid := (functionTyped V constants realizes levels env valid).2.1.1 + have checkedAt := checked V constants realizes levels env valid domainValid + exact ⟨checkedAt.1, domainValid, checkedAt.2⟩ + exact .app (functionTree.hereditary functionTyped keyedAgreement functionReading) + (sameType ▸ argumentTree.hereditary (sameType.symm ▸ argumentTyped) argumentAgreement argumentReading) + | .lam _ miss trace opening bodyTree _ _ _ _ _ => fun typed agreement reading => by + obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm + obtain ⟨_, openedReading, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement (trace.absent keyedAgreement) domainReading bodyReading trace.openRun + exact .lam typed (bodyTree.hereditary typed.lambdaBody openedAgreement openedReading) +termination_by structural support + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/LetCache.lean b/Ix/Kernel/Verify/Consistency/LetCache.lean index 47b067ec3..3c43843be 100644 --- a/Ix/Kernel/Verify/Consistency/LetCache.lean +++ b/Ix/Kernel/Verify/Consistency/LetCache.lean @@ -30,6 +30,23 @@ structure CacheData value : check.valueTree.CacheData anchor body : check.bodyTree.CacheData anchor +def CacheData.asSynthesis + {check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level} (data : check.CacheData anchor) : check.asSynthesis.CacheData anchor := + ⟨data.domain, data.value, data.body⟩ + +/-- The let's own publication now retains its original recursive checking +tree, in addition to the three children's typed publications. -/ +def cacheExecution {result : KExpr .anon} {after : TcState .anon} + (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level) + (data : check.CacheData anchor) + (contextOrigin : SynthesisContext resolve anchor [] [] entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (accepted : RecM.infer (.letE name domain value body nonDep info) (methodsN (fuel + 1)) before = + .ok result after) : SynthesisCacheRun resolve anchor entries accepted := + check.asSynthesis.cacheExecution data.asSynthesis contextOrigin agreement check.source_reading accepted + /-- Original synthesis trees supply all three operational children. The root miss and comparison are taken directly from the checked let execution. -/ def cacheTrace @@ -78,6 +95,19 @@ def cacheHistory {result : KExpr .anon} {after : TcState .anon} .ok result after) : InferenceCacheHistory after := history.afterInference (check.cacheTrace data contextOrigin agreement) accepted +/-- Typed history includes the root let and can reconstruct its retained +check at a later full-cache hit. -/ +def synthesisCacheHistory {result : KExpr .anon} {after : TcState .anon} + (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level) + (data : check.CacheData anchor) + (contextOrigin : SynthesisContext resolve anchor [] [] entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (history : SynthesisCacheHistory resolve anchor entries before) + (accepted : RecM.infer (.letE name domain value body nonDep info) (methodsN (fuel + 1)) before = + .ok result after) : SynthesisCacheHistory resolve anchor entries after := + history.afterSynthesis check.asSynthesis data.asSynthesis contextOrigin agreement check.source_reading accepted + end LetInferenceCheck end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/LetInference.lean b/Ix/Kernel/Verify/Consistency/LetInference.lean index 644ac6cb3..eaacb4761 100644 --- a/Ix/Kernel/Verify/Consistency/LetInference.lean +++ b/Ix/Kernel/Verify/Consistency/LetInference.lean @@ -18,6 +18,11 @@ open Theory Theory.Model universe u +theorem UncachedInference.keyedLocalState {before : TcState .anon} {source : KExpr .anon} + (miss : UncachedInference before source) (valid : LocalStateInvariant before) : + LocalStateInvariant miss.keyed := + ((FramesLocalState.inferKey _).ok valid miss.keyRun).invariant valid + /-- Actual recursive calls and their states. Scope restoration is derived from these runs; no child typing or returned-type reading is assumed. -/ structure LetInferenceTrace (fuel : Nat) (before : TcState .anon) @@ -67,6 +72,23 @@ theorem openingContext (trace : LetInferenceTrace fuel before name domain value (valid : LocalStateInvariant before) : trace.comparedState.lctx.Equiv before.lctx := (trace.openingFrame valid).context +/-- All consumers use the same executed opening and its derived freshness. -/ +theorem opened_reading {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {context : Model.Context β} {A : AExpr β} {b : VExpr β} + (trace : LetInferenceTrace fuel before name domain value body) + (opening : BinderOpeningSupport trace.comparedState body) + (valid : LocalStateInvariant before) + (agreement : LocalContextReading resolve locals before.lctx context) + (domainReading : readScopedExpr? resolve locals domain = some A.erase) + (bodyReading : readScopedExpr? resolve locals body 1 = some b) : + readScopedExpr? resolve (trace.fresh :: locals) trace.opened = some b ∧ + LocalContextReading resolve (trace.fresh :: locals) trace.openedState.lctx (context.push A) ∧ + trace.openedState.env.intern.WF := by + have frame := trace.openingFrame valid + have openingAgreement := agreement.congr frame.context.symm + exact (openLet_sound opening openingAgreement + ((frame.invariant valid).freshReading openingAgreement) domainReading bodyReading trace.openRun).2 + def abstracted (trace : LetInferenceTrace fuel before name domain value body) := abstractFVars trace.bodyType #[trace.fresh] trace.bodyState.env.intern diff --git a/Ix/Kernel/Verify/Consistency/LetSynthesis.lean b/Ix/Kernel/Verify/Consistency/LetSynthesis.lean index 4b93eded4..d1660f103 100644 --- a/Ix/Kernel/Verify/Consistency/LetSynthesis.lean +++ b/Ix/Kernel/Verify/Consistency/LetSynthesis.lean @@ -16,48 +16,6 @@ open Theory Theory.Model universe u v -/-- The actual final cheap-beta choice, with an original checking origin -for a selected reduction. The unchanged case needs only the planner result. -/ -inductive LetTypeReduction {β : Type u} (resolve : Address → Option (ConstRef β)) - (entries : Model.Environment β) (context : Model.Context β) (bounds : List VLevel) - (source : KExpr .anon) (table : InternTable .anon) (level : VLevel) : - AExpr β → AExpr β → Type u - | unchanged {term : AExpr β} (plan : cheapBetaPlan? source = none) : - LetTypeReduction resolve entries context bounds source table level term term - | beta {condition : Certified.PropWhen} {domain body : AExpr β} {arguments : List (AExpr β)} - (support : CheapBetaSupport source table) - (origin : SynthesisBetaTrace resolve entries context bounds entries context - ((AExpr.lam condition domain body).appN arguments) - (AExpr.betaPrefix (cheapBetaCount source) (.lam condition domain body) arguments) (.sort level)) : - LetTypeReduction resolve entries context bounds source table level - ((AExpr.lam condition domain body).appN arguments) - (AExpr.betaPrefix (cheapBetaCount source) (.lam condition domain body) arguments) - -theorem LetTypeReduction.reading {β : Type u} {resolve : Address → Option (ConstRef β)} - {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} - {locals : List FVarId} {source : KExpr .anon} {table : InternTable .anon} - {level : VLevel} {term result : AExpr β} - (reduction : LetTypeReduction resolve entries context bounds source table level term result) - (coherent : table.WF) - (reading : readScopedExpr? resolve locals source = some term.erase) : - readScopedExpr? resolve locals (cheapBetaReduce source table).1 = some result.erase ∧ - (cheapBetaReduce source table).2.WF := by - cases reduction with - | unchanged plan => - rw [cheapBetaReduce, plan] - exact ⟨reading, coherent⟩ - | beta support _ => exact (support.reading reading).2 - -def LetTypeReduction.trace {β : Type u} {resolve : Address → Option (ConstRef β)} - {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} - {source : KExpr .anon} {table : InternTable .anon} {level : VLevel} {term result : AExpr β} - (reduction : LetTypeReduction resolve entries context bounds source table level term result) - (original : SynthesisTypingOrigin resolve entries context bounds entries context term (.sort level)) : - SynthesisBetaTrace resolve entries context bounds entries context term result (.sort level) := - match reduction with - | .unchanged _ => .refl original - | .beta _ origin => origin - /-- All three children are the original executed synthesis checks. Because let erasure loses its constructor, their separate source readings are retained explicitly. The comparison relates the inferred value type to the declaration. -/ @@ -97,11 +55,22 @@ variable {β : Type u} {resolve : Address → Option (ConstRef β)} {fuel : Nat} {before : TcState .anon} {name : Mode.anon.F Name} {domain value body : KExpr .anon} {nonDep : Bool} {info : ExprInfo .anon} {A val b B resultType : AExpr β} {level : VLevel} +/-- The original recursive synthesis datatype includes the let and all +three children, so surrounding binders and applications retain its origin. -/ +def asSynthesis + (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level) : + SynthesisInference resolve entries locals context bounds (fuel + 1) before + (.letE name domain value body nonDep info) (b.inst val) resultType level := + .letE check.full check.localState check.miss check.execution check.opening + check.domainTree check.valueTree check.bodyTree check.domainReading check.valueReading check.bodyReading + check.conditions check.hashPath check.comparisonFaithful check.substitution check.reduction + /-- The initial invariant survives production's key computation. -/ theorem keyedValid (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info A val b B resultType level) : LocalStateInvariant check.miss.keyed := - ((FramesLocalState.inferKey _).ok check.localState check.miss.keyRun).invariant check.localState + check.miss.keyedLocalState check.localState /-- All recursive checks preserve the counter bound; the selected let id therefore cannot name an existing model local. -/ @@ -123,11 +92,9 @@ theorem source_reading private theorem value_type (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info A val b B resultType level) - (formed : ContextFormation.{u,v} entries context bounds) (agreement : LocalContextReading resolve locals before.lctx context) : check.valueType = A := by have valueAgreement := (check.miss.localContext.symm ▸ agreement).congr (check.execution.domainContext check.keyedValid).symm - have returned := (check.valueTree.soundWithSpine formed valueAgreement check.valueReading - check.execution.valueRun).1 + have returned := check.valueTree.outputReading valueAgreement check.valueReading check.execution.valueRun exact AExpr.eq_of_erase_annotations (Option.some.inj (returned.symm.trans ((beq_readScopedExpr? check.comparisonFaithful check.hashPath).trans check.domainReading))) check.conditions @@ -138,7 +105,6 @@ the body's context. -/ def substituted_type_origin (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info A val b B resultType level) - (formed : ContextFormation.{u,v} entries context bounds) (agreement : LocalContextReading resolve locals before.lctx context) : SynthesisTypingOrigin resolve entries context bounds entries context (B.inst val) (.sort level) := by have keyedAgreement := check.miss.localContext.symm ▸ agreement @@ -150,7 +116,7 @@ def substituted_type_origin check.bodyTree opened.2.2.1 opened.2.1 check.execution.bodyRun have valueOrigin := SynthesisTypingOrigin.source (SynthesisCheckedOrigin.checked .current check.valueTree valueAgreement check.valueReading check.execution.valueRun) - have same := check.value_type formed agreement + have same := check.value_type agreement exact .substituteAt bodyOrigin (same ▸ valueOrigin) .root /-- Substitution retains the body's entire beta derivation, including a @@ -158,7 +124,6 @@ lambda introduced by substituting the value for the let-bound variable. -/ def betaTyping (check : LetInferenceCheck resolve entries locals context bounds fuel before name domain value body nonDep info A val b B resultType level) - (formed : ContextFormation.{u,v} entries context bounds) (agreement : LocalContextReading resolve locals before.lctx context) : SynthesisBetaTyping resolve entries context bounds entries context (b.inst val) resultType := by have keyedAgreement := check.miss.localContext.symm ▸ agreement @@ -167,11 +132,11 @@ def betaTyping (check.absent agreement) check.domainReading check.bodyReading check.execution.openRun have inner := check.bodyTree.betaTyping (SynthesisContext.push .current check.domainTree keyedAgreement check.domainReading check.execution.domainRun) - opened.2.2.1 opened.2.1 check.execution.bodyRun formed - have argument := check.valueTree.betaTyping .current valueAgreement check.valueReading check.execution.valueRun formed - have same := check.value_type formed agreement + opened.2.2.1 opened.2.1 check.execution.bodyRun + have argument := check.valueTree.betaTyping .current valueAgreement check.valueReading check.execution.valueRun + have same := check.value_type agreement exact .convert (inner.substituteAt (same ▸ argument) .root) - (check.reduction.trace (check.substituted_type_origin formed agreement)) + (check.reduction.trace (check.substituted_type_origin agreement)) /-- Full-mode let success returns the checked type after the actual closing, substitution, cheap reduction, scope cleanup, and outer cache publication. -/ @@ -185,23 +150,7 @@ theorem sound {result : KExpr .anon} {after : TcState .anon} readScopedExpr? resolve locals result = some resultType.erase ∧ TypingClaim.{u,v} entries context (b.inst val) resultType ∧ TypingClaim.{u,v} entries context resultType (.sort level) := by - have keyedAgreement := check.miss.localContext.symm ▸ agreement - obtain ⟨_, domainTyped, _, _⟩ := check.domainTree.soundWithSpine formed keyedAgreement - check.domainReading check.execution.domainRun - have opened := openLet_sound check.opening (keyedAgreement.congr (check.execution.openingContext check.keyedValid).symm) - (check.absent agreement) check.domainReading check.bodyReading check.execution.openRun - obtain ⟨bodyTypeReading, _, _, _⟩ := check.bodyTree.soundWithSpine (formed.push domainTyped) - opened.2.2.1 opened.2.1 check.execution.bodyRun - obtain ⟨substitutedReading, substitutedCoherent⟩ := - check.execution.substituted_reading check.substitution bodyTypeReading check.valueReading - have reducedReading := check.reduction.reading substitutedCoherent substitutedReading - obtain ⟨middle, uncached⟩ := infer_uncached_success check.miss accepted - rw [check.full] at uncached - have output := (check.execution.output_state uncached).1 - refine ⟨?_, (check.betaTyping formed agreement).origin.sound formed, ?_⟩ - · rw [output] - exact reducedReading.1 - · exact ((check.reduction.trace (check.substituted_type_origin formed agreement)).sound formed).2 + exact check.asSynthesis.sound formed agreement check.source_reading accepted theorem closed_sound {result : KExpr .anon} {after : TcState .anon} (check : LetInferenceCheck resolve entries [] [] [] fuel before name domain value body nonDep info @@ -223,7 +172,7 @@ theorem beta_steps_sound (agreement : LocalContextReading resolve locals before.lctx context) (count : Nat) : ConversionClaim.{u,v} entries context (b.inst val) (BetaSyntax.steps count (b.inst val)) ∧ TypingClaim.{u,v} entries context (BetaSyntax.steps count (b.inst val)) resultType := - ((check.betaTyping formed agreement).betaSteps count).2.sound formed + ((check.betaTyping agreement).betaSteps count).2.sound formed end LetInferenceCheck diff --git a/Ix/Kernel/Verify/Consistency/Production.lean b/Ix/Kernel/Verify/Consistency/Production.lean index 180bb24b3..08d398cf5 100644 --- a/Ix/Kernel/Verify/Consistency/Production.lean +++ b/Ix/Kernel/Verify/Consistency/Production.lean @@ -750,8 +750,8 @@ def DefinitionBodyTrace.betaDeclaredStepsSupport {β : Type u} {input : Definiti (references : value.ReferencesIn entries ∧ type.ReferencesIn entries) : DefinitionCheckSupport resolve entries trace value type := trace.betaDeclaredTraceSupport typeCoverage valueCoverage collision typeInference valueInference - ((SynthesisInference.betaTyping.{u,u} typeInference .current (.empty _ _) typeReading - trace.typeRun (.empty entries)).betaSteps count).2 + ((SynthesisInference.betaTyping.{u} typeInference .current (.empty _ _) typeReading + trace.typeRun).betaSteps count).2 valueReading typeReading valueConditions typeConditions references /-- An operational WHNF path needs no semantic origins for its intermediate @@ -778,8 +778,8 @@ def DefinitionBodyTrace.betaDeclaredWhnfPathSupport {β : Type u} {input : Defin (references : value.ReferencesIn entries ∧ type.ReferencesIn entries) : DefinitionCheckSupport resolve entries trace value type := trace.betaDeclaredWhnfSupport typeCoverage valueCoverage collision typeInference valueInference - (reduction.annotate (SynthesisInference.betaTyping.{u,u} typeInference .current (.empty _ _) - typeReading trace.typeRun (.empty entries))).1 + (reduction.annotate (SynthesisInference.betaTyping.{u} typeInference .current (.empty _ _) + typeReading trace.typeRun)).1 valueReading typeReading valueConditions typeConditions references /-- Operational support for the selected production definition fragment. diff --git a/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean b/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean index 4d560c8ba..fab9b2183 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean @@ -111,6 +111,8 @@ def SynthesisInference.CacheData {β : Type u} {resolve : Address → Option (Co first.CacheData anchor × second.CacheData anchor | .forallE _ _ _ first second .. | .lam _ _ _ _ first second .. | .lamBeta _ _ _ _ first second .. => first.CacheData anchor × second.CacheData anchor + | .letE _ _ _ _ _ domain value body .. => + domain.CacheData anchor × value.CacheData anchor × body.CacheData anchor termination_by structural tree /-- Extract every full publication's checking origin from the original @@ -204,6 +206,24 @@ def SynthesisInference.cacheExecution {β : Type u} {resolve : Address → Optio (domainRun.checks.append bodyRun.checks)).complete (.lamBeta full miss trace opening domainTree bodyTree origin reduction conditionAgrees constructed bound closingFaithful faithful) contextOrigin agreement reading accepted + | .letE full localState miss trace opening domainTree valueTree bodyTree domainReading valueReading bodyReading + conditions hashPath comparisonFaithful substitution reduction => + fun data contextOrigin agreement reading accepted => by + have keyedValid := miss.keyedLocalState localState + have keyedAgreement := miss.localContext.symm ▸ agreement + let domainRun := domainTree.cacheExecution data.1 contextOrigin keyedAgreement domainReading trace.domainRun + let valueRun := valueTree.cacheExecution data.2.1 contextOrigin + (keyedAgreement.congr (trace.domainContext keyedValid).symm) valueReading trace.valueRun + obtain ⟨openedReading, openedAgreement, _⟩ := + trace.opened_reading opening keyedValid keyedAgreement domainReading bodyReading + let bodyRun := bodyTree.cacheExecution data.2.2 + (.push contextOrigin domainTree keyedAgreement domainReading trace.domainRun) + openedAgreement openedReading trace.bodyRun + exact (SynthesisCacheSupplement.mk + (.letE full miss trace hashPath domainRun.trace valueRun.trace bodyRun.trace) + ((domainRun.checks.append valueRun.checks).append bodyRun.checks)).complete + (.letE full localState miss trace opening domainTree valueTree bodyTree domainReading valueReading bodyReading + conditions hashPath comparisonFaithful substitution reduction) contextOrigin agreement reading accepted termination_by structural tree /-- A successful synthesis call extends the complete typed cache history. diff --git a/Ix/Kernel/Verify/Consistency/SynthesisDerivation.lean b/Ix/Kernel/Verify/Consistency/SynthesisDerivation.lean new file mode 100644 index 000000000..a0f618b8c --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SynthesisDerivation.lean @@ -0,0 +1,385 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.SynthesisSupport + +/-! Complete syntactic derivations retained by source inference. +Dependent substitution keeps binder children and application arguments. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- Constructors that remain outside head beta reduction after substitution. -/ +inductive BetaAtom {β : Type u} : AExpr β → Prop + | sort (level : VLevel) : BetaAtom (.sort level) + | const (ref : ConstRef β) (levels : List VLevel) : BetaAtom (.const ref levels) + | proj (ref : ConstRef β) (field : Nat) (major : AExpr β) : BetaAtom (.proj ref field major) + | natLit (value : Nat) : BetaAtom (.natLit value) + +theorem BetaAtom.liftN {β : Type u} {term : AExpr β} (atom : BetaAtom term) (count cutoff : Nat) : + BetaAtom (term.liftN count cutoff) := by + cases atom <;> constructor + +theorem BetaAtom.inst {β : Type u} {term : AExpr β} (atom : BetaAtom term) (argument : AExpr β) (cutoff : Nat) : + BetaAtom (term.inst argument cutoff) := by + cases atom <;> constructor + +theorem BetaAtom.not_lam {β : Type u} {term : AExpr β} (atom : BetaAtom term) + (condition : Certified.PropWhen) (domain body : AExpr β) : term ≠ .lam condition domain body := by + cases atom <;> intro same <;> cases same + +/-- A typing derivation whose leaves retain actual checks. Function-type +domains and codomains, lambda bodies, and both application children remain +available after substitution. Forward beta conversion changes the retained +type without discarding this structure. -/ +inductive SynthesisBetaTyping {β : Type u} (resolve : Address → Option (ConstRef β)) + (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) + (entries : Model.Environment β) : Model.Context β → AExpr β → AExpr β → Type u + | atom {context term type} + (origin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) + (shape : BetaAtom term) : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type + | bvar {context index type} + (origin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context (.bvar index) type) + (atIndex : context[index]? = some type) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (.bvar index) type + | forallE {context condition domain body type domainLevel bodyLevel} + (origin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context + (.forallE condition domain body) type) + (domainCheck : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context + domain (.sort domainLevel)) + (bodyCheck : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries (context.push domain) + body (.sort bodyLevel)) + (conditionAgrees : condition = Certified.zeroCondition bodyLevel) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context + (.forallE condition domain body) type + | lam {context condition domain body codomain} + (origin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context + (.lam condition domain body) (.forallE condition domain codomain)) + (inner : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries (context.push domain) body codomain) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context + (.lam condition domain body) (.forallE condition domain codomain) + | app {context fn arg condition domain body} + (function : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context fn + (.forallE condition domain body)) + (argument : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context arg domain) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (.app fn arg) (body.inst arg) + | convert {context term sourceType resultType level} + (prior : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term sourceType) + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + sourceType resultType (.sort level)) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term resultType + +namespace SynthesisBetaTyping + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext : Model.Context β} {incomingBounds : List VLevel} + +def origin {context : Model.Context β} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type := + match typing with + | .atom origin _ | .bvar origin _ | .forallE origin _ _ _ | .lam origin _ => origin + | .app function argument => .application function.origin argument.origin + | .convert prior trace => .convert prior.origin trace +termination_by structural typing + +structure LambdaView (context : Model.Context β) (condition : Certified.PropWhen) (domain body type : AExpr β) where + codomain : AExpr β + typeEq : type = .forallE condition domain codomain + inner : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries (context.push domain) body codomain + +def lambdaView {context : Model.Context β} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + {condition : Certified.PropWhen} → {domain body : AExpr β} → term = .lam condition domain body → + LambdaView (resolve := resolve) (incoming := incoming) (incomingContext := incomingContext) + (incomingBounds := incomingBounds) (entries := entries) context condition domain body type := + match typing with + | .atom _ shape => fun same => False.elim (shape.not_lam _ _ _ same) + | .bvar .. | .forallE .. | .app .. => fun same => by cases same + | .lam _ inner => fun same => by cases same; exact ⟨_, rfl, inner⟩ + | .convert prior trace => fun same => + let view := prior.lambdaView same + ⟨view.codomain, (trace.rigid (by simp only [view.typeEq]; intro fn arg same; cases same)).trans view.typeEq, + view.inner⟩ +termination_by structural typing + +def weakenAt {source target : Model.Context β} {cutoff : Nat} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries source term type) + (insertion : ContextInsertion source target cutoff) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries target + (term.liftN 1 cutoff) (type.liftN 1 cutoff) := + match typing with + | .atom origin shape => .atom (.weakenAt origin insertion) (shape.liftN 1 cutoff) + | .bvar (index := index) origin found => by + have lifted := SynthesisTypingOrigin.weakenAt origin insertion + have selected := insertion.lookup found + by_cases below : index < cutoff + · simp only [AExpr.liftN, liftVar, below, if_true] at lifted selected ⊢ + exact .bvar lifted selected + · simp only [AExpr.liftN, liftVar, below, if_false, Nat.add_comm 1] at lifted selected ⊢ + exact .bvar lifted selected + | .forallE origin domainCheck bodyCheck agrees => + .forallE (.weakenAt origin insertion) (domainCheck.weakenAt insertion) + (bodyCheck.weakenAt (insertion.push _)) agrees + | .lam origin inner => .lam (.weakenAt origin insertion) (inner.weakenAt (insertion.push _)) + | .app function argument => by + simpa only [AExpr.liftN, AExpr.liftN_inst_zero] using + SynthesisBetaTyping.app (function.weakenAt insertion) (argument.weakenAt insertion) + | .convert prior trace => .convert (prior.weakenAt insertion) (.weakenAt trace insertion) +termination_by structural typing + +def extend {later : Model.Environment β} {context : Model.Context β} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) + (extension : InterfaceExtends entries later) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds later context term type := + match typing with + | .atom origin shape => .atom (origin.extend extension) shape + | .bvar origin found => .bvar (origin.extend extension) found + | .forallE origin domainCheck bodyCheck agrees => + .forallE (origin.extend extension) (domainCheck.extend extension) (bodyCheck.extend extension) agrees + | .lam origin inner => .lam (origin.extend extension) (inner.extend extension) + | .app function argument => .app (function.extend extension) (argument.extend extension) + | .convert prior trace => .convert (prior.extend extension) (.extend trace extension) +termination_by structural typing + +def rebase {priorIncoming : Model.Environment β} {priorContext context : Model.Context β} + {priorBounds : List VLevel} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve priorIncoming priorContext priorBounds entries context term type) + (origin : SynthesisContext resolve incoming incomingContext incomingBounds priorIncoming priorContext priorBounds) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type := + match typing with + | .atom checked shape => .atom (.rebase origin checked) shape + | .bvar checked found => .bvar (.rebase origin checked) found + | .forallE checked domainCheck bodyCheck agrees => + .forallE (.rebase origin checked) (domainCheck.rebase origin) (bodyCheck.rebase origin) agrees + | .lam checked inner => .lam (.rebase origin checked) (inner.rebase origin) + | .app function argument => .app (function.rebase origin) (argument.rebase origin) + | .convert prior trace => .convert (prior.rebase origin) (.rebase origin trace) +termination_by structural typing + +end SynthesisBetaTyping + +/-- The internal substitution walker builds this data itself as it passes +binders. Erasure gives the existing model context-substitution relation. -/ +inductive BetaSubstitutionContext {β : Type u} (base : Model.Context β) (domain argument : AExpr β) : + Model.Context β → Model.Context β → Nat → Type u + | root : BetaSubstitutionContext base domain argument (base.push domain) base 0 + | push {source target cutoff} (prior : BetaSubstitutionContext base domain argument source target cutoff) + (binder : AExpr β) : + BetaSubstitutionContext base domain argument (source.push binder) + (target.push (binder.inst argument cutoff)) (cutoff + 1) + +theorem BetaSubstitutionContext.relation {β : Type u} {base source target : Model.Context β} + {domain argument : AExpr β} {cutoff : Nat} + (substitution : BetaSubstitutionContext base domain argument source target cutoff) : + ContextSubstitution base domain argument source target cutoff := + match substitution with + | .root => .root + | .push prior binder => .push prior.relation binder + +def BetaSubstitutionContext.liftValue {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext : Model.Context β} {incomingBounds : List VLevel} + {base source target : Model.Context β} {domain argument term type : AExpr β} {cutoff : Nat} + (substitution : BetaSubstitutionContext base domain argument source target cutoff) + (value : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries base term type) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries target + (term.liftN cutoff) (type.liftN cutoff) := + match substitution with + | .root => by simpa only [AExpr.liftN_zero] using value + | .push (target := target) (cutoff := cutoff) prior binder => by + have lifted := (prior.liftValue value).weakenAt (ContextInsertion.root target (binder.inst argument cutoff)) + simpa only [AExpr.liftN_liftN_merge term cutoff 1 0 0 (Nat.le_refl _) (Nat.zero_le _), + AExpr.liftN_liftN_merge type cutoff 1 0 0 (Nat.le_refl _) (Nat.zero_le _)] using lifted +termination_by structural substitution + +def SynthesisBetaTyping.substituteAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext : Model.Context β} {incomingBounds : List VLevel} + {base source target : Model.Context β} {domain argument term type : AExpr β} {cutoff : Nat} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries source term type) + (value : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries base argument domain) + (substitution : BetaSubstitutionContext base domain argument source target cutoff) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries target + (term.inst argument cutoff) (type.inst argument cutoff) := + match typing with + | .atom origin shape => .atom (.substituteAt origin value.origin substitution.relation) (shape.inst argument cutoff) + | .bvar (index := index) origin found => by + by_cases equal : index = cutoff + · subst index + have sameType := substitution.relation.instantiate_removed_type found + simpa only [AExpr.inst, AExpr.instVar, Nat.lt_irrefl, if_false, if_true, sameType] using + substitution.liftValue value + · have retained := substitution.relation.lookup_other found equal + have typed := SynthesisTypingOrigin.substituteAt origin value.origin substitution.relation + by_cases below : index < cutoff + · simp only [AExpr.inst, AExpr.instVar, below, if_true] at typed retained ⊢ + exact .bvar typed retained + · simp only [AExpr.inst, AExpr.instVar, below, equal, if_false] at typed retained ⊢ + exact .bvar typed retained + | .forallE origin domainCheck bodyCheck agrees => + .forallE (.substituteAt origin value.origin substitution.relation) + (domainCheck.substituteAt value substitution) + (bodyCheck.substituteAt value (substitution.push _)) agrees + | .lam origin inner => + .lam (.substituteAt origin value.origin substitution.relation) + (inner.substituteAt value (substitution.push _)) + | .app function applied => by + simpa only [AExpr.inst, AExpr.inst_inst_zero] using + SynthesisBetaTyping.app (function.substituteAt value substitution) (applied.substituteAt value substitution) + | .convert prior trace => + .convert (prior.substituteAt value substitution) (.substituteAt trace value.origin substitution.relation) +termination_by structural typing + +theorem SynthesisBetaTyping.lambdaPrefix {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + LambdaPrefix term type term.lambdaDepth := + match typing with + | .atom _ shape => by cases shape <;> exact .zero _ _ + | .bvar .. | .forallE .. | .app .. => .zero _ _ + | .lam _ inner => .lam inner.lambdaPrefix + | .convert (term := term) prior trace => by + have leading := prior.lambdaPrefix + cases term with + | lam condition domain body => + have view := prior.lambdaView rfl + have fixed := trace.rigid (by simp only [view.typeEq]; intro fn arg same; cases same) + simpa only [fixed] using leading + | _ => exact .zero _ _ +termination_by structural typing + +namespace SynthesisBetaTyping + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext : Model.Context β} {incomingBounds : List VLevel} + +/-- Both children keep their original checking derivations, including all +substitutions that exposed the function type. -/ +structure ForallView (context : Model.Context β) (condition : Certified.PropWhen) (domain body : AExpr β) where + domainLevel : VLevel + bodyLevel : VLevel + domainCheck : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context + domain (.sort domainLevel) + bodyCheck : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries (context.push domain) + body (.sort bodyLevel) + conditionAgrees : condition = Certified.zeroCondition bodyLevel + +def forallView {context : Model.Context β} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + {condition : Certified.PropWhen} → {domain body : AExpr β} → term = .forallE condition domain body → + ForallView (resolve := resolve) (incoming := incoming) (incomingContext := incomingContext) + (incomingBounds := incomingBounds) (entries := entries) context condition domain body := + match typing with + | .atom _ shape => fun same => False.elim (by cases shape <;> cases same) + | .bvar .. | .lam .. | .app .. => fun same => by cases same + | .forallE _ domainCheck bodyCheck agrees => fun same => by + cases same + exact ⟨_, _, domainCheck, bodyCheck, agrees⟩ + | .convert prior _ => fun same => prior.forallView same +termination_by structural typing + +private theorem appN_last {β : Type u} {head : AExpr β} {arguments : List (AExpr β)} + (nonempty : arguments ≠ []) : + head.appN arguments = (head.appN arguments.dropLast).app (arguments.getLast nonempty) := by + calc + head.appN arguments = head.appN (arguments.dropLast ++ [arguments.getLast nonempty]) := + congrArg (AExpr.appN head) (List.dropLast_concat_getLast nonempty).symm + _ = _ := by simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] + +private theorem nonapp_spine_empty {β : Type u} {term head : AExpr β} {arguments : List (AExpr β)} + (notApp : ∀ fn arg, term ≠ .app fn arg) (same : term = head.appN arguments) : arguments = [] := by + by_contra nonempty + exact notApp _ _ (same.trans (appN_last nonempty)) + +private theorem app_spine_parts {β : Type u} {fn arg head : AExpr β} {arguments : List (AExpr β)} + (nonempty : arguments ≠ []) (same : fn.app arg = head.appN arguments) : + arguments = arguments.dropLast ++ [arg] ∧ fn = head.appN arguments.dropLast := by + have parts := AExpr.app.inj (same.trans (appN_last nonempty)) + exact ⟨by rw [parts.2]; exact (List.dropLast_concat_getLast nonempty).symm, parts.1⟩ + +/-- Recover every argument check, including those introduced by replacing +a variable with a complete application. No new inference calls are needed. -/ +def spineOrigin {context : Model.Context β} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) + (head : AExpr β) (arguments : List (AExpr β)) (same : term = head.appN arguments) : + SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := + if empty : arguments = [] then by + subst arguments + simp only [AExpr.appN_nil] at same + subst term + exact ⟨type, typing.origin, typing.lambdaPrefix, .nil _⟩ + else match typing with + | .app function argument => by + have parts := app_spine_parts empty same + have prior := function.spineOrigin head arguments.dropLast parts.2 + refine ⟨prior.headType, prior.headOrigin, prior.leading, ?_⟩ + simpa only [← parts.1] using prior.argumentsOrigin.snoc argument.origin + | .convert prior trace => + let child := prior.spineOrigin head arguments same + ⟨child.headType, child.headOrigin, child.leading, child.argumentsOrigin.convert trace⟩ + | .atom _ shape => by + exact False.elim (empty (nonapp_spine_empty + (by cases shape <;> intro fn arg same <;> cases same) same)) + | .bvar .. | .forallE .. | .lam .. => by + exact False.elim (empty (nonapp_spine_empty (by intro fn arg same; cases same) same)) +termination_by structural typing + +/-- The head lookup and dependent argument checks survive substitutions, +including replacement of the old head by a different variable spine. -/ +def variableSpineOrigin {context : Model.Context β} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) + (index : Nat) (arguments : List (AExpr β)) (same : term = (AExpr.bvar index).appN arguments) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries context index arguments type := + match typing with + | .bvar _ found => by + have empty := nonapp_spine_empty (by intro fn arg same; cases same) same + subst arguments + simp only [AExpr.appN_nil] at same + cases same + exact ⟨_, found, .nil _⟩ + | .app function argument => by + have nonempty : arguments ≠ [] := by intro empty; subst arguments; cases same + have parts := app_spine_parts nonempty same + have prior := function.variableSpineOrigin index arguments.dropLast parts.2 + refine ⟨prior.headType, prior.atIndex, ?_⟩ + simpa only [← parts.1] using prior.spine.snoc argument.origin + | .convert prior trace => + let child := prior.variableSpineOrigin index arguments same + ⟨child.headType, child.atIndex, child.spine.convert trace⟩ + | .atom _ shape => by + have empty := nonapp_spine_empty (by cases shape <;> intro fn arg same <;> cases same) same + subst arguments + exact False.elim (by cases shape <;> cases same) + | .forallE .. | .lam .. => by + have empty := nonapp_spine_empty (by intro fn arg same; cases same) same + subst arguments + cases same +termination_by structural typing + +def lambdaBodyVariableSpine {context : Model.Context β} {term type domain : AExpr β} + {condition : Certified.PropWhen} {index : Nat} {arguments : List (AExpr β)} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) + (same : term = .lam condition domain ((AExpr.bvar index).appN arguments)) : + Σ resultType, SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries (context.push domain) index arguments resultType := + let view := typing.lambdaView same + ⟨view.codomain, view.inner.variableSpineOrigin index arguments rfl⟩ + +def ForallView.variableSpine {context : Model.Context β} {condition : Certified.PropWhen} {domain : AExpr β} + {index : Nat} {arguments : List (AExpr β)} + (view : ForallView (resolve := resolve) (incoming := incoming) (incomingContext := incomingContext) + (incomingBounds := incomingBounds) (entries := entries) context condition domain + ((AExpr.bvar index).appN arguments)) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries (context.push domain) + index arguments (.sort view.bodyLevel) := + view.bodyCheck.variableSpineOrigin index arguments rfl + +end SynthesisBetaTyping + + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean index 8d493ec11..6466e274b 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean @@ -3,23 +3,10 @@ Copyright (c) 2026 Argument Computer Corporation. SPDX-License-Identifier: MIT OR Apache-2.0 -/ -import Ix.Kernel.Verify.Consistency.ContextTransport -import Ix.Kernel.Verify.Consistency.CheapBetaReading -import Ix.Kernel.Verify.Consistency.ApplicationWhnf -import Ix.Theory.Model.UniverseBounds -import Ix.Theory.Model.BetaSpine +import Ix.Kernel.Verify.Consistency.SynthesisShapes -/-! -Inference with formation of the returned type. Local bounds come from the -executed checks of binder domains; external type bounds retain their actual -checking origin. Applications recover a codomain bound from an inhabited -product. Lambdas therefore synthesize full typing, including when they occur -directly in function position. - -The recorded level is an upper bound for the inferred type. It preserves the -exact zero condition, but its positive value need not be the sort the kernel -would infer on a separate call. No such extra call is assumed. --/ +/-! Recursive inference, retained source derivations, and their semantic +soundness at the original production calls. -/ namespace Ix.Kernel.Consistency @@ -29,1696 +16,10 @@ universe u v mutual -/-- Finite support for the actual production calls, enriched with the -origins and syntactic levels needed to derive formation. There are no -semantic typing or conversion fields. -/ -inductive SynthesisInference {β : Type u} - (resolve : Address → Option (ConstRef β)) : - Model.Environment β → List FVarId → Model.Context β → List VLevel → Nat → TcState .anon → KExpr .anon → - AExpr β → AExpr β → VLevel → Type u - | known {entries locals context bounds fuel before source term type level} - (inference : BinderInference resolve entries locals context fuel before source term type) - (formation : TypeFormation resolve entries context type level) : - SynthesisInference resolve entries locals context bounds fuel before source term type level - | cached {entries locals context bounds fuel before source term type level - priorLocals priorFuel priorBefore priorAfter priorResult} - (tree : SynthesisInference resolve entries priorLocals context bounds priorFuel priorBefore source term type level) - (priorAgreement : LocalContextReading resolve priorLocals priorBefore.lctx context) - (priorReading : readScopedExpr? resolve priorLocals source = some term.erase) - (priorRun : RecM.infer source (methodsN priorFuel) priorBefore = .ok priorResult priorAfter) - (hit : InferenceCacheHit before source) - (cacheMatch : hit.cached = priorResult) - (resultReading : readScopedExpr? resolve locals priorResult = some type.erase) : - SynthesisInference resolve entries locals context bounds fuel before source term type level - | cachedFrom {entries locals context bounds fuel before source term type level} - (check : SynthesisRetainedCheck resolve entries context bounds entries context term type level) - (hit : InferenceCacheHit before source) - (resultReading : readScopedExpr? resolve locals hit.cached = some type.erase) : - SynthesisInference resolve entries locals context bounds fuel before source term type level - | reuseType {earlier entries locals context bounds fuel before source term type - typeFuel typeBefore typeAfter typeSource typeResult declaredType level typeBound arguments} - (inference : BinderInference resolve entries locals context fuel before source term type) - (typeTree : SynthesisInference resolve earlier [] [] [] typeFuel typeBefore typeSource - declaredType (.sort level) typeBound) - (extension : InterfaceExtends earlier entries) - (typeReading : readScopedExpr? resolve [] typeSource = some declaredType.erase) - (typeRun : RecM.infer typeSource (methodsN typeFuel) typeBefore = .ok typeResult typeAfter) - (same : AExpr.LevelEquivalent (declaredType.instL arguments) type) : - SynthesisInference resolve entries locals context bounds fuel before source term type (level.inst arguments) - | fvar {entries locals context bounds fuel before source index type level} - (inference : BinderInference resolve entries locals context fuel before source (.bvar index) type) - (atIndex : context[index]? = some type) - (boundAtIndex : bounds[index]? = some level) : - SynthesisInference resolve entries locals context bounds fuel before source (.bvar index) type level - | app {entries locals context bounds fuel before fn arg info f a A A' B condition functionLevel argumentLevel} - (full : before.inferOnly = false) - (miss : UncachedInference before (.app fn arg info)) - (trace : ApplicationInferenceTrace fuel miss.keyed fn arg) - (functionTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed fn - f (.forallE condition A B) functionLevel) - (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState - arg a A' argumentLevel) - (conditions : A'.annotations = A.annotations) - (hashPath : (trace.argumentType == trace.domain) = true) - (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) - (bodyConstructed : trace.codomain.Constructed) - (argConstructed : arg.Constructed) - (bodyBound : trace.codomain.size + 1 < UInt64.size) - (argBound : arg.size < UInt64.size) - (coherent : trace.comparedState.env.intern.WF) - (faithful : KExpr.CollisionFree fun term => trace.comparedState.env.intern.ExprSupport term ∨ - KExpr.SubstReach arg trace.codomain 0 term) : - SynthesisInference resolve entries locals context bounds (fuel + 1) before (.app fn arg info) - (.app f a) (B.inst a) (applicationLevel functionLevel condition) - | appBeta {entries locals context bounds fuel before fn arg info f a T A A' B condition - functionLevel argumentLevel reductionLevel} - (full : before.inferOnly = false) - (miss : UncachedInference before (.app fn arg info)) - (trace : ApplicationWhnfInferenceTrace fuel miss.keyed fn arg) - (functionTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed fn - f T functionLevel) - (exposure : BetaPiExposure resolve locals fuel trace.functionState trace.functionType T - condition A B trace.domain trace.codomain) - (exposureCoherent : trace.functionState.env.intern.WF) - (reduction : SynthesisBetaTrace resolve entries context bounds entries context - T (.forallE condition A B) (.sort reductionLevel)) - (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.exposedState - arg a A' argumentLevel) - (conditions : A'.annotations = A.annotations) - (hashPath : (trace.argumentType == trace.domain) = true) - (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) - (bodyConstructed : trace.codomain.Constructed) - (argConstructed : arg.Constructed) - (bodyBound : trace.codomain.size + 1 < UInt64.size) - (argBound : arg.size < UInt64.size) - (coherent : trace.comparedState.env.intern.WF) - (faithful : KExpr.CollisionFree fun term => trace.comparedState.env.intern.ExprSupport term ∨ - KExpr.SubstReach arg trace.codomain 0 term) : - SynthesisInference resolve entries locals context bounds (fuel + 1) before (.app fn arg info) - (.app f a) (B.inst a) (applicationLevel reductionLevel condition) - | forallE {entries locals context bounds fuel before name bi domain body info A B domainBoundLevel bodyBoundLevel} - (miss : UncachedInference before (.all name bi domain body info)) - (trace : ForallInferenceTrace fuel miss.keyed name bi domain body) - (opening : BinderOpeningSupport trace.domainState body) - (domainTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed domain - A (.sort (readLevel trace.domainLevel)) domainBoundLevel) - (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) - (readLevel trace.domainLevel :: bounds) fuel trace.openedState trace.opened - B (.sort (readLevel trace.bodyLevel)) bodyBoundLevel) - (levelFaithful : ∀ a b, - (KUniv.Sub a trace.domainLevel ∨ KUniv.Sub a trace.bodyLevel) → - (KUniv.Sub b trace.domainLevel ∨ KUniv.Sub b trace.bodyLevel) → a.AddrFaithful b) - (domainBound : trace.domainLevel.size < UInt64.size) - (bodyBound : trace.bodyLevel.size < UInt64.size) - (coherent : trace.bodyState.env.intern.WF) - (faithful : KExpr.KeyCollisionFree fun term => trace.bodyState.env.intern.ExprSupport term ∨ - term = KExpr.mkSort (KUniv.mkIMax trace.domainLevel trace.bodyLevel)) : - SynthesisInference resolve entries locals context bounds (fuel + 1) before (.all name bi domain body info) - (.forallE (Certified.zeroCondition (readLevel trace.bodyLevel)) A B) - (.sort (readLevel (KUniv.mkIMax trace.domainLevel trace.bodyLevel))) - (.succ (readLevel (KUniv.mkIMax trace.domainLevel trace.bodyLevel))) - | lam {entries locals context bounds fuel before name bi domain body info A b B condition domainBoundLevel bodyLevel} - (full : before.inferOnly = false) - (miss : UncachedInference before (.lam name bi domain body info)) - (trace : LambdaInferenceTrace fuel miss.keyed name bi domain body) - (opening : BinderOpeningSupport trace.domainState body) - (domainTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed domain - A (.sort (readLevel trace.domainLevel)) domainBoundLevel) - (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) - (readLevel trace.domainLevel :: bounds) fuel trace.openedState trace.opened b B bodyLevel) - (conditionAgrees : condition = Certified.zeroCondition bodyLevel) - (constructed : trace.bodyType.Constructed) - (bound : trace.bodyType.size + 1 < UInt64.size) - (coherent : trace.bodyState.env.intern.WF) - (closingFaithful : KExpr.CollisionFree fun term => trace.bodyState.env.intern.ExprSupport term ∨ - KExpr.AbstractReach ((∅ : Std.HashMap FVarId UInt64).insert trace.fresh 0) - 1 trace.bodyType 0 term) - (faithful : KExpr.KeyCollisionFree fun term => trace.abstracted.2.ExprSupport term ∨ - term = KExpr.mkAll () () domain trace.abstracted.1) : - SynthesisInference resolve entries locals context bounds (fuel + 1) before (.lam name bi domain body info) - (.lam condition A b) (.forallE condition A B) (.imax (readLevel trace.domainLevel) bodyLevel) - | lamBeta {entries locals context bounds fuel before name bi domain body info A b condition - domainBoundLevel bodyLevel headCondition headDomain headBody arguments reducedLevel} - (full : before.inferOnly = false) - (miss : UncachedInference before (.lam name bi domain body info)) - (trace : LambdaBodyTrace fuel miss.keyed name bi domain body) - (opening : BinderOpeningSupport trace.domainState body) - (domainTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed domain - A (.sort (readLevel trace.domainLevel)) domainBoundLevel) - (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) - (readLevel trace.domainLevel :: bounds) fuel trace.openedState trace.opened b - ((AExpr.lam headCondition headDomain headBody).appN arguments) bodyLevel) - (origin : SynthesisReductionOrigin resolve entries context bounds entries (context.push A) - (.lam headCondition headDomain headBody) arguments (cheapBetaCount trace.bodyType) reducedLevel) - (reduction : CheapBetaSupport trace.bodyType trace.bodyState.env.intern) - (conditionAgrees : condition = Certified.zeroCondition reducedLevel) - (constructed : trace.reduced.1.Constructed) - (bound : trace.reduced.1.size + 1 < UInt64.size) - (closingFaithful : KExpr.CollisionFree fun term => trace.reduced.2.ExprSupport term ∨ - KExpr.AbstractReach ((∅ : Std.HashMap FVarId UInt64).insert trace.fresh 0) - 1 trace.reduced.1 0 term) - (faithful : KExpr.KeyCollisionFree fun term => trace.abstracted.2.ExprSupport term ∨ - term = KExpr.mkAll () () domain trace.abstracted.1) : - SynthesisInference resolve entries locals context bounds (fuel + 1) before (.lam name bi domain body info) - (.lam condition A b) - (.forallE condition A - (AExpr.betaPrefix (cheapBetaCount trace.bodyType) (.lam headCondition headDomain headBody) arguments)) - (.imax (readLevel trace.domainLevel) reducedLevel) - -/-- A complete executed inference check transported to a later use site. -The source tree remains available beneath interface and context changes; -none of these constructors accepts semantic typing or conversion evidence. -/ -inductive SynthesisRetainedCheck {β : Type u} (resolve : Address → Option (ConstRef β)) : - Model.Environment β → Model.Context β → List VLevel → - Model.Environment β → Model.Context β → AExpr β → AExpr β → VLevel → Type u - | source {incoming incomingContext incomingBounds entries context bounds locals fuel before after - source result term type level} - (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = some term.erase) - (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : - SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level - | extend {incoming incomingContext incomingBounds earlier entries context term type level} - (prior : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds earlier context term type level) - (extension : InterfaceExtends earlier entries) : - SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level - | weakenAt {incoming incomingContext incomingBounds entries source target cutoff term type level} - (prior : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries source term type level) - (insertion : ContextInsertion source target cutoff) : - SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries target - (term.liftN 1 cutoff) (type.liftN 1 cutoff) level - | rebase {incoming incomingContext incomingBounds middle middleContext middleBounds entries context term type level} - (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) - (prior : SynthesisRetainedCheck resolve middle middleContext middleBounds entries context term type level) : - SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level - -/-- Contexts of retained checking origins come from the incoming context, -the empty context, or actual checks of their binder domains. This records -earlier checks even when a generated type is used several binders later. -/ -inductive SynthesisContext {β : Type u} (resolve : Address → Option (ConstRef β)) : - Model.Environment β → Model.Context β → List VLevel → - Model.Environment β → Model.Context β → List VLevel → Type u - | current {entries context bounds} : - SynthesisContext resolve entries context bounds entries context bounds - | empty {entries context bounds} (earlier : Model.Environment β) : - SynthesisContext resolve entries context bounds earlier [] [] - | push {entries context bounds earlier priorContext priorBounds locals fuel before after source result - domain level bound} - (prior : SynthesisContext resolve entries context bounds earlier priorContext priorBounds) - (domainTree : SynthesisInference resolve earlier locals priorContext priorBounds fuel before source - domain (.sort level) bound) - (agreement : LocalContextReading resolve locals before.lctx priorContext) - (reading : readScopedExpr? resolve locals source = some domain.erase) - (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : - SynthesisContext resolve entries context bounds earlier (priorContext.push domain) (level :: priorBounds) - | extend {entries context bounds earlier later priorContext priorBounds} - (prior : SynthesisContext resolve entries context bounds earlier priorContext priorBounds) - (extension : InterfaceExtends earlier later) : - SynthesisContext resolve entries context bounds later priorContext priorBounds - | compose {entries context bounds middle middleContext middleBounds earlier priorContext priorBounds} - (prior : SynthesisContext resolve entries context bounds middle middleContext middleBounds) - (next : SynthesisContext resolve middle middleContext middleBounds earlier priorContext priorBounds) : - SynthesisContext resolve entries context bounds earlier priorContext priorBounds - -/-- A checking origin may also cross a dependent term substitution. The -substituted argument retains its actual inference call, whose typing is -derived in the same recursion as the enclosing lambda. -/ -inductive SynthesisTypeTransport {β : Type u} (resolve : Address → Option (ConstRef β)) : - Model.Environment β → Model.Context β → List VLevel → - Model.Environment β → Model.Context β → AExpr β → AExpr β → VLevel → - Model.Environment β → Model.Context β → AExpr β → AExpr β → VLevel → Type u - | pure {incoming incomingContext incomingBounds origin originContext source reduced level - entries context current result bound} - (transport : TypeReductionTransport origin originContext source reduced level - entries context current result bound) : - SynthesisTypeTransport resolve incoming incomingContext incomingBounds - origin originContext source reduced level entries context current result bound - | map {incoming incomingContext incomingBounds origin originContext source reduced level - middle middleContext current result bound entries context current' result' bound'} - (prior : SynthesisTypeTransport resolve incoming incomingContext incomingBounds - origin originContext source reduced level middle middleContext current result bound) - (transport : TypeReductionTransport middle middleContext current result bound - entries context current' result' bound') : - SynthesisTypeTransport resolve incoming incomingContext incomingBounds - origin originContext source reduced level entries context current' result' bound' - | substituteAt {incoming incomingContext incomingBounds origin originContext source reduced level - entries base sourceContext targetContext current result bound domain argument cutoff} - (prior : SynthesisTypeTransport resolve incoming incomingContext incomingBounds - origin originContext source reduced level entries sourceContext current result bound) - (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds - entries base argument domain) - (substitution : ContextSubstitution base domain argument sourceContext targetContext cutoff) : - SynthesisTypeTransport resolve incoming incomingContext incomingBounds - origin originContext source reduced level entries targetContext - (current.inst argument cutoff) (result.inst argument cutoff) bound - -/-- Internal typing origins for generated expressions. Leaves are actual -source inference calls. Application, conversion, finite beta traces, and -context substitutions retain those calls, including when an argument crosses -still-open parameters of an earlier type. No semantic typing field is accepted. -/ -inductive SynthesisTypingOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : - Model.Environment β → Model.Context β → List VLevel → - Model.Environment β → Model.Context β → AExpr β → AExpr β → Type u - | source {incoming incomingContext incomingBounds entries context term type} - (check : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type - | inferredType {incoming incomingContext incomingBounds entries context bounds locals fuel before after source result - term type level} - (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = some term.erase) - (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context type (.sort level) - | lambdaBody {incoming incomingContext incomingBounds entries context condition domain body codomain} - (check : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context - (.lam condition domain body) (.forallE condition domain codomain)) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries (context.push domain) body codomain - | application {incoming incomingContext incomingBounds entries context function argument condition domain body} - (functionOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context - function (.forallE condition domain body)) - (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context argument domain) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context - (.app function argument) (body.inst argument) - | reduced {incoming incomingContext incomingBounds entries context source result type} - (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context result type - | convert {incoming incomingContext incomingBounds entries context term sourceType resultType level} - (value : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term sourceType) - (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context sourceType resultType (.sort level)) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term resultType - | weaken {incoming incomingContext incomingBounds entries context term type} - (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) - (domain : AExpr β) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries (context.push domain) - (term.liftN 1) (type.liftN 1) - | weakenAt {incoming incomingContext incomingBounds entries source target cutoff term type} - (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries source term type) - (insertion : ContextInsertion source target cutoff) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries target - (term.liftN 1 cutoff) (type.liftN 1 cutoff) - | instantiate {incoming incomingContext incomingBounds entries context term type} - (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) - (arguments : List VLevel) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds - entries (context.map (AExpr.instL arguments)) (term.instL arguments) (type.instL arguments) - | appendContext {incoming incomingContext incomingBounds entries context term type} - (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) - (outer : Model.Context β) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries (context ++ outer) term type - | extend {incoming incomingContext incomingBounds earlier entries context term type} - (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds earlier context term type) - (extension : InterfaceExtends earlier entries) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type - | rebase {incoming incomingContext incomingBounds middle middleContext middleBounds entries context term type} - (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) - (prior : SynthesisTypingOrigin resolve middle middleContext middleBounds entries context term type) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type - | termEquivalent {incoming incomingContext incomingBounds entries context term term' type} - (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) - (same : AExpr.LevelEquivalent term term') : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term' type - | typeEquivalent {incoming incomingContext incomingBounds entries context term type type'} - (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) - (same : AExpr.LevelEquivalent type type') : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type' - | substituteAt {incoming incomingContext incomingBounds entries base sourceContext targetContext - domain term type argument cutoff} - (body : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds - entries sourceContext term type) - (value : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries base argument domain) - (substitution : ContextSubstitution base domain argument sourceContext targetContext cutoff) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries targetContext - (term.inst argument cutoff) (type.inst argument cutoff) - -/-- Actual inference calls retain their source's syntactic lambda domains -and argument checks. Application arguments additionally retain the comparison with -the function's domain, before any later transport of this checking origin. -/ -inductive SynthesisCheckedOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : - Model.Environment β → Model.Context β → List VLevel → - Model.Environment β → Model.Context β → AExpr β → AExpr β → Type u - | checked {incoming incomingContext incomingBounds entries context bounds locals fuel before after source result - term type level} - (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = some term.erase) - (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : - SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type - | binderHead {incoming incomingContext incomingBounds entries context locals fuel before after source result term type} - (tree : BinderInference resolve entries locals context fuel before source term type) - (head : SynthesisHead term) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = some term.erase) - (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : - SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type - | applicationArgument {incoming incomingContext incomingBounds entries context bounds locals fuel before fn arg - f a domain argumentType body condition functionLevel argumentLevel} - (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (trace : ApplicationInferenceTrace fuel before fn arg) - (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn - f (.forallE condition domain body) functionLevel) - (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState arg - a argumentType argumentLevel) - (agreement : LocalContextReading resolve locals before.lctx context) - (functionReading : readScopedExpr? resolve locals fn = some f.erase) - (argumentReading : readScopedExpr? resolve locals arg = some a.erase) - (conditions : argumentType.annotations = domain.annotations) - (hashPath : (trace.argumentType == trace.domain) = true) - (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) : - SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context a domain - | applicationBetaArgument {incoming incomingContext incomingBounds entries context bounds locals fuel before fn arg - f a T domain argumentType body condition functionLevel argumentLevel} - (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (trace : ApplicationWhnfInferenceTrace fuel before fn arg) - (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn f T functionLevel) - (exposure : BetaPiExposure resolve locals fuel trace.functionState trace.functionType T - condition domain body trace.domain trace.codomain) - (exposureCoherent : trace.functionState.env.intern.WF) - (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.exposedState arg - a argumentType argumentLevel) - (agreement : LocalContextReading resolve locals before.lctx context) - (functionReading : readScopedExpr? resolve locals fn = some f.erase) - (argumentReading : readScopedExpr? resolve locals arg = some a.erase) - (conditions : argumentType.annotations = domain.annotations) - (hashPath : (trace.argumentType == trace.domain) = true) - (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) : - SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context a domain - | binderArgument {incoming incomingContext incomingBounds entries context locals fuel before fn arg - f a domain argumentType body condition} - (trace : ApplicationInferenceTrace fuel before fn arg) - (functionTree : BinderInference resolve entries locals context fuel before fn - f (.forallE condition domain body)) - (head : SynthesisHead f) - (argumentTree : BinderInference resolve entries locals context fuel trace.functionState arg a argumentType) - (agreement : LocalContextReading resolve locals before.lctx context) - (functionReading : readScopedExpr? resolve locals fn = some f.erase) - (argumentReading : readScopedExpr? resolve locals arg = some a.erase) - (conditions : argumentType.annotations = domain.annotations) - (hashPath : (trace.argumentType == trace.domain) = true) - (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) : - SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context a domain - -/-- The argument checks along an actual application spine, retained in -application order so later substitutions can update each dependent type. -/ -inductive SynthesisArgumentSpineOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : - Model.Environment β → Model.Context β → List VLevel → - Model.Environment β → Model.Context β → AExpr β → List (AExpr β) → AExpr β → Type u - | nil {incoming incomingContext incomingBounds entries context} (type : AExpr β) : - SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context type [] type - | snoc {incoming incomingContext incomingBounds entries context start arguments condition domain body argument} - (prior : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context - start arguments (.forallE condition domain body)) - (checked : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context argument domain) : - SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context - start (arguments ++ [argument]) (body.inst argument) - | convert {incoming incomingContext incomingBounds entries context start arguments source target level} - (prior : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context - start arguments source) - (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source target (.sort level)) : - SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context start arguments target - -/-- A beta reduction can come from an earlier checked lambda prefix, or -from an actual lambda or lambda application substituted for a checked -variable head. Both cases retain the checks of all applied arguments. -/ -inductive SynthesisReductionOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : - Model.Environment β → Model.Context β → List VLevel → - Model.Environment β → Model.Context β → AExpr β → List (AExpr β) → Nat → VLevel → Type u - | traced {incoming incomingContext incomingBounds entries context head arguments count level} - (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context - (head.appN arguments) (AExpr.betaPrefix count head arguments) (.sort level)) : - SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries context head arguments count level - | checked {incoming incomingContext incomingBounds entries context head arguments count level - earlier typeLocals typeContext typeBounds typeFuel typeBefore typeAfter typeSource typeResult - originCondition originDomain originBody originArguments originLevel originBound} - (typeContextSupport : SynthesisContext resolve incoming incomingContext incomingBounds - earlier typeContext typeBounds) - (typeTree : SynthesisInference resolve earlier typeLocals typeContext typeBounds typeFuel typeBefore - typeSource ((AExpr.lam originCondition originDomain originBody).appN originArguments) - (.sort originLevel) originBound) - (typeAgreement : LocalContextReading resolve typeLocals typeBefore.lctx typeContext) - (typeReading : readScopedExpr? resolve typeLocals typeSource = - some ((AExpr.lam originCondition originDomain originBody).appN originArguments).erase) - (typeRun : RecM.infer typeSource (methodsN typeFuel) typeBefore = .ok typeResult typeAfter) - (originPrefix : count ≤ originBody.lambdaDepth + 1) - (transport : SynthesisTypeTransport resolve incoming incomingContext incomingBounds earlier typeContext - ((AExpr.lam originCondition originDomain originBody).appN originArguments) - (AExpr.betaPrefix count (.lam originCondition originDomain originBody) originArguments) originLevel - entries context (head.appN arguments) (AExpr.betaPrefix count head arguments) level) : - SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries context head arguments count level - | substitutedVariable {incoming incomingContext incomingBounds entries base source target - type domain argument arguments cutoff count level} - (spine : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds - entries source type arguments (.sort level)) - (atIndex : source[cutoff]? = some type) - (checked : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries base argument domain) - (substitution : ContextSubstitution base domain argument source target cutoff) - (enough : count ≤ argument.lambdaDepth) : - SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries target - (argument.liftN cutoff) (arguments.map (AExpr.inst · argument cutoff)) count level - | substitutedApplication {incoming incomingContext incomingBounds entries base source target - type domain binder body condition initialArguments arguments cutoff count level} - (spine : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds - entries source type arguments (.sort level)) - (atIndex : source[cutoff]? = some type) - (checked : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries base - ((AExpr.lam condition binder body).appN initialArguments) domain) - (substitution : ContextSubstitution base domain - ((AExpr.lam condition binder body).appN initialArguments) source target cutoff) - (enough : count ≤ body.lambdaDepth + 1) : - SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries target - ((AExpr.lam condition binder body).liftN cutoff) - (initialArguments.map (AExpr.liftN cutoff ·) ++ - arguments.map (AExpr.inst · ((AExpr.lam condition binder body).appN initialArguments) cutoff)) count level - | substitutedResult {incoming incomingContext incomingBounds entries base source target - type domain binder body condition initialArguments arguments cutoff count resultType level} - (spine : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds - entries source type arguments resultType) - (atIndex : source[cutoff]? = some type) - (checked : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries base - ((AExpr.lam condition binder body).appN initialArguments) domain) - (substitution : ContextSubstitution base domain - ((AExpr.lam condition binder body).appN initialArguments) source target cutoff) - (enough : count ≤ body.lambdaDepth + 1) - (sourceOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries target - (((AExpr.lam condition binder body).liftN cutoff).appN - (initialArguments.map (AExpr.liftN cutoff ·) ++ - arguments.map (AExpr.inst · ((AExpr.lam condition binder body).appN initialArguments) cutoff))) - (.sort level)) : - SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries target - ((AExpr.lam condition binder body).liftN cutoff) - (initialArguments.map (AExpr.liftN cutoff ·) ++ - arguments.map (AExpr.inst · ((AExpr.lam condition binder body).appN initialArguments) cutoff)) count level - | map {incoming incomingContext incomingBounds earlier priorContext head arguments count bound - entries context current currentArguments level} - (prior : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds - earlier priorContext head arguments count bound) - (transport : SynthesisTypeTransport resolve incoming incomingContext incomingBounds earlier priorContext - (head.appN arguments) (AExpr.betaPrefix count head arguments) bound entries context - (current.appN currentArguments) (AExpr.betaPrefix count current currentArguments) level) : - SynthesisReductionOrigin resolve incoming incomingContext incomingBounds - entries context current currentArguments count level - -/-- Finite beta traces retain the actual checks behind each lambda and -argument. Results can supply later typing origins, so composition never -requires an inference call on an intermediate expression. Types at adjacent -steps may differ; the trace preserves the type retained by its first step. -/ -inductive SynthesisBetaTrace {β : Type u} (resolve : Address → Option (ConstRef β)) : - Model.Environment β → Model.Context β → List VLevel → - Model.Environment β → Model.Context β → AExpr β → AExpr β → AExpr β → Type u - | refl {incoming incomingContext incomingBounds entries context term type} - (origin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term term type - | prefix {incoming incomingContext incomingBounds entries context head headType arguments count type} - (headOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context head headType) - (leading : LambdaPrefix head headType count) - (argumentsOrigin : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds - entries context headType arguments type) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context - (head.appN arguments) (AExpr.betaPrefix count head arguments) type - | origin {incoming incomingContext incomingBounds entries context head arguments count level} - (retained : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds - entries context head arguments count level) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context - (head.appN arguments) (AExpr.betaPrefix count head arguments) (.sort level) - | trans {incoming incomingContext incomingBounds entries context source middle result type otherType} - (prior : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source middle type) - (next : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context middle result otherType) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type - | atType {incoming incomingContext incomingBounds entries context source result type otherType} - (sourceOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context source type) - (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result otherType) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type - | application {incoming incomingContext incomingBounds entries context source result argument condition domain body} - (functionTrace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context - source result (.forallE condition domain body)) - (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context argument domain) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context - (.app source argument) (.app result argument) (body.inst argument) - | argument {incoming incomingContext incomingBounds entries context function source result condition domain body otherType} - (functionOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context - function (.forallE condition domain body)) - (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context source domain) - (argumentTrace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result otherType) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context - (.app function source) (.app function result) (body.inst source) - | substituteAt {incoming incomingContext incomingBounds entries base sourceContext targetContext - domain source result type argument cutoff} - (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries sourceContext source result type) - (value : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries base argument domain) - (substitution : ContextSubstitution base domain argument sourceContext targetContext cutoff) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries targetContext - (source.inst argument cutoff) (result.inst argument cutoff) (type.inst argument cutoff) - | weakenAt {incoming incomingContext incomingBounds entries sourceContext targetContext cutoff source result type} - (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries sourceContext source result type) - (insertion : ContextInsertion sourceContext targetContext cutoff) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries targetContext - (source.liftN 1 cutoff) (result.liftN 1 cutoff) (type.liftN 1 cutoff) - | rebase {incoming incomingContext incomingBounds middle middleContext middleBounds entries context source result type} - (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) - (trace : SynthesisBetaTrace resolve middle middleContext middleBounds entries context source result type) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type - | convertType {incoming incomingContext incomingBounds entries context source result sourceType targetType level} - (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result sourceType) - (typeTrace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context - sourceType targetType (.sort level)) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result targetType - | instantiate {incoming incomingContext incomingBounds entries context source result type} - (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type) - (arguments : List VLevel) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries (context.map (AExpr.instL arguments)) - (source.instL arguments) (result.instL arguments) (type.instL arguments) - | appendContext {incoming incomingContext incomingBounds entries context source result type} - (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type) - (outer : Model.Context β) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries (context ++ outer) source result type - | extend {incoming incomingContext incomingBounds earlier entries context source result type} - (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds earlier context source result type) - (extension : InterfaceExtends earlier entries) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type - -end - -private theorem trace_appN_last {β : Type u} {head : AExpr β} {arguments : List (AExpr β)} - (nonempty : arguments ≠ []) : - head.appN arguments = (head.appN arguments.dropLast).app (arguments.getLast nonempty) := by - calc - head.appN arguments = head.appN (arguments.dropLast ++ [arguments.getLast nonempty]) := - congrArg (AExpr.appN head) (List.dropLast_concat_getLast nonempty).symm - _ = _ := by simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] - -private theorem betaPrefix_eq_of_not_app {β : Type u} {head : AExpr β} - {arguments : List (AExpr β)} {count : Nat} - (notApp : ∀ fn arg, head.appN arguments ≠ .app fn arg) : - AExpr.betaPrefix count head arguments = head.appN arguments := by - cases arguments with - | nil => cases count <;> cases head <;> rfl - | cons argument arguments => - exact False.elim (notApp _ _ (trace_appN_last (by simp))) - -/-- These traces reduce applications and their subapplications. They do -not change a source whose outer constructor is a lambda, product, or atom. -In particular, a forward beta conversion cannot change a lambda's product type. -/ -theorem SynthesisBetaTrace.rigid {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {source result type : AExpr β} - (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type) : - (∀ fn arg, source ≠ .app fn arg) → result = source := - match trace with - | .refl _ => fun _ => rfl - | .prefix .. | .origin .. => fun notApp => betaPrefix_eq_of_not_app notApp - | .trans prior next => fun notApp => by - have middle := prior.rigid notApp - exact (next.rigid (by simpa only [middle] using notApp)).trans middle - | .atType _ trace => trace.rigid - | .application .. | .argument .. => fun notApp => False.elim (notApp _ _ rfl) - | .substituteAt trace _ _ => fun notApp => by - have same := trace.rigid (by - intro fn arg equal - cases equal - exact notApp _ _ rfl) - rw [same] - | .weakenAt trace _ => fun notApp => by - have same := trace.rigid (by - intro fn arg equal - cases equal - exact notApp _ _ rfl) - rw [same] - | .rebase _ trace => trace.rigid - | .convertType trace _ => trace.rigid - | .instantiate trace arguments => - AExpr.HeadRigid.map trace.rigid (AExpr.instL arguments) (by intros; rfl) - | .appendContext trace _ | .extend trace _ => trace.rigid -termination_by structural trace - -/-- Earlier argument checks can cross another local binder without any -new inference of their lifted expressions. -/ -def SynthesisArgumentSpineOrigin.weaken {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} - (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds - entries context start arguments result) (domain : AExpr β) : - SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries (context.push domain) - (start.liftN 1) (arguments.map (AExpr.liftN 1 ·)) (result.liftN 1) := - match support with - | .nil _ => .nil _ - | .snoc prior checked => by - simpa only [List.map_append, List.map_cons, List.map_nil, AExpr.liftN_inst_zero] using - (prior.weaken domain).snoc (checked.weaken domain) - | .convert prior trace => - .convert (prior.weaken domain) (.weakenAt trace (ContextInsertion.root context domain)) -termination_by structural support - -def SynthesisArgumentSpineOrigin.instantiate {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} - (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds - entries context start arguments result) (levels : List VLevel) : - SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds - entries (context.map (AExpr.instL levels)) (start.instL levels) - (arguments.map (AExpr.instL levels)) (result.instL levels) := - match support with - | .nil _ => .nil _ - | .snoc prior checked => by - simpa only [List.map_append, List.map_cons, List.map_nil, AExpr.instL_inst] using - (prior.instantiate levels).snoc (checked.instantiate levels) - | .convert prior trace => .convert (prior.instantiate levels) (.instantiate trace levels) -termination_by structural support - -def SynthesisArgumentSpineOrigin.weakenAt {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext source target : Model.Context β} - {incomingBounds : List VLevel} {cutoff : Nat} {start result : AExpr β} {arguments : List (AExpr β)} - (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds - entries source start arguments result) (insertion : ContextInsertion source target cutoff) : - SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries target - (start.liftN 1 cutoff) (arguments.map (AExpr.liftN 1 · cutoff)) (result.liftN 1 cutoff) := - match support with - | .nil _ => .nil _ - | .snoc prior checked => by - simpa only [List.map_append, List.map_cons, List.map_nil, AExpr.liftN_inst_zero] using - (prior.weakenAt insertion).snoc (.weakenAt checked insertion) - | .convert prior trace => .convert (prior.weakenAt insertion) (.weakenAt trace insertion) -termination_by structural support - -def SynthesisArgumentSpineOrigin.rebase {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming middle entries : Model.Environment β} {incomingContext middleContext context : Model.Context β} - {incomingBounds middleBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} - (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) - (support : SynthesisArgumentSpineOrigin resolve middle middleContext middleBounds entries context start arguments result) : - SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context start arguments result := - match support with - | .nil _ => .nil _ - | .snoc prior checked => (prior.rebase origin).snoc (.rebase origin checked) - | .convert prior trace => .convert (prior.rebase origin) (.rebase origin trace) -termination_by structural support - -def SynthesisArgumentSpineOrigin.appendContext {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} - (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds - entries context start arguments result) (outer : Model.Context β) : - SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds - entries (context ++ outer) start arguments result := - match support with - | .nil _ => .nil _ - | .snoc prior checked => (prior.appendContext outer).snoc (checked.appendContext outer) - | .convert prior trace => .convert (prior.appendContext outer) (.appendContext trace outer) -termination_by structural support - -def SynthesisArgumentSpineOrigin.extend {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming earlier entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} - (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds - earlier context start arguments result) (extension : InterfaceExtends earlier entries) : - SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context start arguments result := - match support with - | .nil _ => .nil _ - | .snoc prior checked => (prior.extend extension).snoc (checked.extend extension) - | .convert prior trace => .convert (prior.extend extension) (.extend trace extension) -termination_by structural support - -def SynthesisArgumentSpineOrigin.substituteAt {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext base source target : Model.Context β} - {incomingBounds : List VLevel} {start result domain argument : AExpr β} - {arguments : List (AExpr β)} {cutoff : Nat} - (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds - entries source start arguments result) - (value : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries base argument domain) - (substitution : ContextSubstitution base domain argument source target cutoff) : - SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries target - (start.inst argument cutoff) (arguments.map (AExpr.inst · argument cutoff)) (result.inst argument cutoff) := - match support with - | .nil _ => .nil _ - | .snoc prior checked => by - simpa only [List.map_append, List.map_cons, List.map_nil, AExpr.inst_inst_zero] using - (prior.substituteAt value substitution).snoc (checked.substituteAt value substitution) - | .convert prior trace => .convert (prior.substituteAt value substitution) (.substituteAt trace value substitution) -termination_by structural support - -/-- The head of a checked variable application uses the exact local type; -its argument checks remain available after leaving the original scope. -/ -structure SynthesisVariableSpineOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) - (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) - (entries : Model.Environment β) (context : Model.Context β) (index : Nat) - (arguments : List (AExpr β)) (type : AExpr β) where - headType : AExpr β - atIndex : context[index]? = some headType - spine : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds - entries context headType arguments type - -def SynthesisVariableSpineOrigin.appendContext {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {type : AExpr β} {arguments : List (AExpr β)} {index : Nat} - (support : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds - entries context index arguments type) (outer : Model.Context β) : - SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds - entries (context ++ outer) index arguments type := - { headType := support.headType - atIndex := by - rw [List.getElem?_append_left (List.getElem?_eq_some_iff.mp support.atIndex).1] - exact support.atIndex - spine := support.spine.appendContext outer } - -def SynthesisVariableSpineOrigin.instantiate {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {type : AExpr β} {arguments : List (AExpr β)} {index : Nat} - (support : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds - entries context index arguments type) (levels : List VLevel) : - SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds - entries (context.map (AExpr.instL levels)) index (arguments.map (AExpr.instL levels)) (type.instL levels) := - { headType := support.headType.instL levels - atIndex := by simp only [List.getElem?_map, support.atIndex, Option.map_some] - spine := support.spine.instantiate levels } - -def SynthesisVariableSpineOrigin.extend {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming earlier entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {type : AExpr β} {arguments : List (AExpr β)} {index : Nat} - (support : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds - earlier context index arguments type) (extension : InterfaceExtends earlier entries) : - SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries context index arguments type := - { headType := support.headType - atIndex := support.atIndex - spine := support.spine.extend extension } - -/-- Applying an earlier parameter preserves a later variable-headed -codomain and updates every retained argument check in its dependent context. -/ -def SynthesisVariableSpineOrigin.substituteAt {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext base source target : Model.Context β} - {incomingBounds : List VLevel} {type domain argument : AExpr β} - {arguments : List (AExpr β)} {index cutoff : Nat} - (support : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds - entries source index arguments type) - (value : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries base argument domain) - (substitution : ContextSubstitution base domain argument source target cutoff) - (distinct : index ≠ cutoff) : - SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries target - (if index < cutoff then index else index - 1) - (arguments.map (AExpr.inst · argument cutoff)) (type.inst argument cutoff) := - { headType := support.headType.inst argument cutoff - atIndex := substitution.lookup_other support.atIndex distinct - spine := support.spine.substituteAt value substitution } - -/-- The checked argument transports an already justified original beta -prefix to its substituted application result. The two resulting expressions -are computed by the proved substitution laws. -/ -def SynthesisTypeTransport.substitutePrefixAt {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming origin entries : Model.Environment β} - {incomingContext originContext base sourceContext targetContext : Model.Context β} - {incomingBounds : List VLevel} {source reduced head domain argument : AExpr β} - {arguments : List (AExpr β)} {level bound : VLevel} {count cutoff : Nat} - (prior : SynthesisTypeTransport resolve incoming incomingContext incomingBounds - origin originContext source reduced level entries sourceContext - (head.appN arguments) (AExpr.betaPrefix count head arguments) bound) - (enough : count ≤ head.lambdaDepth) - (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds - entries base argument domain) - (substitution : ContextSubstitution base domain argument sourceContext targetContext cutoff) : - SynthesisTypeTransport resolve incoming incomingContext incomingBounds - origin originContext source reduced level entries targetContext - ((head.inst argument cutoff).appN (arguments.map (AExpr.inst · argument cutoff))) - (AExpr.betaPrefix count (head.inst argument cutoff) - (arguments.map (AExpr.inst · argument cutoff))) bound := by - simpa only [AExpr.inst_appN, AExpr.inst_betaPrefix count head arguments argument cutoff enough] using - prior.substituteAt argumentOrigin substitution - -/-- Follow an actual argument call through the remaining dependent -parameters of a previously checked function type. The context relation -computes their updated domains at the same substitution cutoff. -/ -def ApplicationInferenceTrace.substituteTypeOriginAt {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming origin entries : Model.Environment β} - {incomingContext originContext context sourceContext targetContext : Model.Context β} - {incomingBounds bounds : List VLevel} {source reduced head domain argumentType argument f body : AExpr β} - {condition : Certified.PropWhen} {arguments : List (AExpr β)} - {level bound functionBound argumentBound : VLevel} {count cutoff fuel : Nat} - {locals : List FVarId} {before : TcState .anon} {fn rawArgument : KExpr .anon} - (trace : ApplicationInferenceTrace fuel before fn rawArgument) - (prior : SynthesisTypeTransport resolve incoming incomingContext incomingBounds - origin originContext source reduced level entries sourceContext - (head.appN arguments) (AExpr.betaPrefix count head arguments) bound) - (enough : count ≤ head.lambdaDepth) - (argumentContext : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn - f (.forallE condition domain body) functionBound) - (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState rawArgument - argument argumentType argumentBound) - (agreement : LocalContextReading resolve locals before.lctx context) - (functionReading : readScopedExpr? resolve locals fn = some f.erase) - (argumentReading : readScopedExpr? resolve locals rawArgument = some argument.erase) - (conditions : argumentType.annotations = domain.annotations) - (hashPath : (trace.argumentType == trace.domain) = true) - (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) - (substitution : ContextSubstitution context domain argument sourceContext targetContext cutoff) : - SynthesisTypeTransport resolve incoming incomingContext incomingBounds - origin originContext source reduced level entries targetContext - ((head.inst argument cutoff).appN (arguments.map (AExpr.inst · argument cutoff))) - (AExpr.betaPrefix count (head.inst argument cutoff) - (arguments.map (AExpr.inst · argument cutoff))) bound := - prior.substitutePrefixAt enough - (.source (.applicationArgument argumentContext trace functionTree argumentTree agreement - functionReading argumentReading conditions hashPath comparisonFaithful)) substitution - -/-- Reuse a codomain check from an earlier closed function type at the -actual application site. The old function parameter keeps index zero while -the caller's locals are added outside it, then the actual argument call -supplies the substitution. -/ -def ApplicationInferenceTrace.substituteTypeOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming origin entries : Model.Environment β} {incomingContext originContext context : Model.Context β} - {incomingBounds bounds : List VLevel} {source reduced head domain argumentType argument f body : AExpr β} - {condition : Certified.PropWhen} {arguments : List (AExpr β)} - {level bound functionBound argumentBound : VLevel} {count fuel : Nat} - {locals : List FVarId} {before : TcState .anon} {fn rawArgument : KExpr .anon} - (trace : ApplicationInferenceTrace fuel before fn rawArgument) - (prior : TypeReductionTransport origin originContext source reduced level - entries (Context.push domain []) - (head.appN arguments) (AExpr.betaPrefix count head arguments) bound) - (enough : count ≤ head.lambdaDepth) - (argumentContext : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn - f (.forallE condition domain body) functionBound) - (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState rawArgument - argument argumentType argumentBound) - (agreement : LocalContextReading resolve locals before.lctx context) - (functionReading : readScopedExpr? resolve locals fn = some f.erase) - (argumentReading : readScopedExpr? resolve locals rawArgument = some argument.erase) - (conditions : argumentType.annotations = domain.annotations) - (hashPath : (trace.argumentType == trace.domain) = true) - (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) : - SynthesisTypeTransport resolve incoming incomingContext incomingBounds - origin originContext source reduced level entries context - ((head.inst argument).appN (arguments.map (AExpr.inst · argument))) - (AExpr.betaPrefix count (head.inst argument) (arguments.map (AExpr.inst · argument))) bound := by - have imported : TypeReductionTransport origin originContext source reduced level - entries (context.push domain) (head.appN arguments) (AExpr.betaPrefix count head arguments) bound := by - simpa only [Context.push, List.map_nil, List.cons_append, List.nil_append] using - prior.appendContext (context.map (AExpr.liftN 1 ·)) - exact trace.substituteTypeOriginAt (.pure imported) enough argumentContext functionTree argumentTree - agreement functionReading argumentReading conditions hashPath comparisonFaithful .root - -/-- Substituting an actual lambda argument for a checked variable head -exposes its own checked prefix, even though the original type had no leading -lambda. Remaining dependent parameters are handled at the same cutoff. -/ -def ApplicationInferenceTrace.exposedTypeOriginAt {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} - {incomingContext context sourceContext targetContext : Model.Context β} - {incomingBounds bounds : List VLevel} {domain argumentType argument f body : AExpr β} - {condition : Certified.PropWhen} {arguments : List (AExpr β)} - {level functionBound argumentBound : VLevel} {count cutoff fuel : Nat} - {locals : List FVarId} {before : TcState .anon} {fn rawArgument : KExpr .anon} - (trace : ApplicationInferenceTrace fuel before fn rawArgument) - (origin : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds - entries sourceContext cutoff arguments (.sort level)) - (enough : count ≤ argument.lambdaDepth) - (argumentContext : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn - f (.forallE condition domain body) functionBound) - (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState rawArgument - argument argumentType argumentBound) - (agreement : LocalContextReading resolve locals before.lctx context) - (functionReading : readScopedExpr? resolve locals fn = some f.erase) - (argumentReading : readScopedExpr? resolve locals rawArgument = some argument.erase) - (conditions : argumentType.annotations = domain.annotations) - (hashPath : (trace.argumentType == trace.domain) = true) - (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) - (substitution : ContextSubstitution context domain argument sourceContext targetContext cutoff) : - SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries targetContext - (argument.liftN cutoff) (arguments.map (AExpr.inst · argument cutoff)) count level := - .substitutedVariable origin.spine origin.atIndex - (.applicationArgument argumentContext trace functionTree argumentTree agreement functionReading argumentReading - conditions hashPath comparisonFaithful) substitution enough - -/-- A supplied lambda application contributes its existing arguments -before the original variable application's arguments. Both sets of checks -come from the actual inference calls retained at this application. -/ -def ApplicationInferenceTrace.exposedApplicationOriginAt {β : Type u} - {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} - {incomingContext context sourceContext targetContext : Model.Context β} - {incomingBounds bounds : List VLevel} {domain argumentType binder inner f body : AExpr β} - {condition headCondition : Certified.PropWhen} {initialArguments arguments : List (AExpr β)} - {level functionBound argumentBound : VLevel} {count cutoff fuel : Nat} - {locals : List FVarId} {before : TcState .anon} {fn rawArgument : KExpr .anon} - (trace : ApplicationInferenceTrace fuel before fn rawArgument) - (origin : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds - entries sourceContext cutoff arguments (.sort level)) - (enough : count ≤ inner.lambdaDepth + 1) - (argumentContext : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn - f (.forallE condition domain body) functionBound) - (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState rawArgument - ((AExpr.lam headCondition binder inner).appN initialArguments) argumentType argumentBound) - (agreement : LocalContextReading resolve locals before.lctx context) - (functionReading : readScopedExpr? resolve locals fn = some f.erase) - (argumentReading : readScopedExpr? resolve locals rawArgument = - some ((AExpr.lam headCondition binder inner).appN initialArguments).erase) - (conditions : argumentType.annotations = domain.annotations) - (hashPath : (trace.argumentType == trace.domain) = true) - (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) - (substitution : ContextSubstitution context domain - ((AExpr.lam headCondition binder inner).appN initialArguments) sourceContext targetContext cutoff) : - SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries targetContext - ((AExpr.lam headCondition binder inner).liftN cutoff) - (initialArguments.map (AExpr.liftN cutoff ·) ++ - arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments) cutoff)) count level := - .substitutedApplication origin.spine origin.atIndex - (.applicationArgument argumentContext trace functionTree argumentTree agreement functionReading argumentReading - conditions hashPath comparisonFaithful) substitution enough - -/-- Later actual arguments continue transporting an exposed prefix and -its reduction, including through dependent domains still left open. -/ -def ApplicationInferenceTrace.substituteReductionOriginAt {β : Type u} - {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} - {incomingContext context sourceContext targetContext : Model.Context β} - {incomingBounds bounds : List VLevel} {head domain argumentType argument f body : AExpr β} - {condition : Certified.PropWhen} {arguments : List (AExpr β)} - {level functionBound argumentBound : VLevel} {count cutoff fuel : Nat} - {locals : List FVarId} {before : TcState .anon} {fn rawArgument : KExpr .anon} - (trace : ApplicationInferenceTrace fuel before fn rawArgument) - (origin : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds - entries sourceContext head arguments count level) - (enough : count ≤ head.lambdaDepth) - (argumentContext : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn - f (.forallE condition domain body) functionBound) - (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState rawArgument - argument argumentType argumentBound) - (agreement : LocalContextReading resolve locals before.lctx context) - (functionReading : readScopedExpr? resolve locals fn = some f.erase) - (argumentReading : readScopedExpr? resolve locals rawArgument = some argument.erase) - (conditions : argumentType.annotations = domain.annotations) - (hashPath : (trace.argumentType == trace.domain) = true) - (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) - (substitution : ContextSubstitution context domain argument sourceContext targetContext cutoff) : - SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries targetContext - (head.inst argument cutoff) (arguments.map (AExpr.inst · argument cutoff)) count level := - origin.map (trace.substituteTypeOriginAt (.pure .refl) enough argumentContext functionTree argumentTree - agreement functionReading argumentReading conditions hashPath comparisonFaithful substitution) - -/-- The domain and codomain retain their actual checks after interface -growth or insertion beneath any number of dependent binders. -/ -structure SynthesisRetainedForall {β : Type u} (resolve : Address → Option (ConstRef β)) - (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) - (entries : Model.Environment β) (context : Model.Context β) - (condition : Certified.PropWhen) (domain body : AExpr β) where - domainLevel : VLevel - bodyLevel : VLevel - domainBound : VLevel - bound : VLevel - domainCheck : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries - context domain (.sort domainLevel) domainBound - check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries - (context.push domain) body (.sort bodyLevel) bound - conditionAgrees : condition = Certified.zeroCondition bodyLevel - -/-- A function-type check at the caller's current inference context. -/ -abbrev SynthesisForallBodyCheck {β : Type u} (resolve : Address → Option (ConstRef β)) - (entries : Model.Environment β) (context : Model.Context β) (bounds : List VLevel) - (condition : Certified.PropWhen) (domain body : AExpr β) := - SynthesisRetainedForall resolve entries context bounds entries context condition domain body - -private def ForallInferenceTrace.bodyCheck {β : Type u} {resolve : Address → Option (ConstRef β)} - {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} - {fuel : Nat} {before : TcState .anon} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} - {domain body : KExpr .anon} {A B : AExpr β} {domainBound bodyBound : VLevel} - (trace : ForallInferenceTrace fuel before name bi domain body) - (opening : BinderOpeningSupport trace.domainState body) - (domainTree : SynthesisInference resolve entries locals context bounds fuel before domain - A (.sort (readLevel trace.domainLevel)) domainBound) - (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) - (readLevel trace.domainLevel :: bounds) fuel trace.openedState trace.opened - B (.sort (readLevel trace.bodyLevel)) bodyBound) - (agreement : LocalContextReading resolve locals before.lctx context) - (domainReading : readScopedExpr? resolve locals domain = some A.erase) - (bodyReading : readScopedExpr? resolve locals body 1 = some B.erase) : - SynthesisForallBodyCheck resolve entries context bounds - (Certified.zeroCondition (readLevel trace.bodyLevel)) A B := by - have opened := openBinder_sound opening (agreement.congr trace.contextPreserved.symm) - (trace.absent agreement) domainReading bodyReading trace.openRun - exact { - domainLevel := readLevel trace.domainLevel - bodyLevel := readLevel trace.bodyLevel - domainBound := domainBound - bound := bodyBound - domainCheck := .source .current domainTree agreement domainReading trace.domainRun - check := .source (.push .current domainTree agreement domainReading trace.domainRun) - bodyTree opened.2.2.1 opened.2.1 trace.bodyRun - conditionAgrees := rfl } - -def BinderInference.forallBodyCheck {β : Type u} {resolve : Address → Option (ConstRef β)} - {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} - {bounds : List VLevel} {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} - {condition : Certified.PropWhen} {domain body type : AExpr β} - (support : BinderInference resolve entries locals context fuel before source - (.forallE condition domain body) type) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = some (AExpr.forallE condition domain body).erase) : - SynthesisForallBodyCheck resolve entries context bounds condition domain body := by - cases support with - | forallE miss trace opening domainTree bodyTree => - obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading - exact trace.bodyCheck opening (.known domainTree (.sort _)) (.known bodyTree (.sort _)) - (miss.localContext.symm ▸ agreement) domainReads bodyReads - -mutual - -/-- Recover the recorded codomain check by inspecting the production -inference tree. This does not assume a new call on an inferred codomain. -/ -private def SynthesisInference.forallBodyCheckAux {β : Type u} {resolve : Address → Option (ConstRef β)} - {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} - {bounds : List VLevel} {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} - {condition : Certified.PropWhen} {term domain body type : AExpr β} {level : VLevel} - (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = some term.erase) : - term = .forallE condition domain body → SynthesisForallBodyCheck resolve entries context bounds condition domain body := - match support with - | .known inference _ | .reuseType inference .. => fun same => by - cases same - exact inference.forallBodyCheck agreement reading - | .cached tree priorAgreement priorReading _ _ _ _ => fun same => - tree.forallBodyCheckAux priorAgreement priorReading same - | .cachedFrom check _ _ => fun same => check.forallBody same - | .forallE miss trace opening domainTree bodyTree .. => fun same => by - cases same - obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading - exact trace.bodyCheck opening domainTree bodyTree - (miss.localContext.symm ▸ agreement) domainReads bodyReads - | .fvar .. | .app .. | .appBeta .. | .lam .. | .lamBeta .. => fun same => by cases same -termination_by structural support - -def SynthesisRetainedCheck.forallBody {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} - {condition : Certified.PropWhen} {domain body : AExpr β} - (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) : - term = .forallE condition domain body → - SynthesisRetainedForall resolve incoming incomingContext incomingBounds entries context condition domain body := - match check with - | .source contextOrigin tree agreement reading _ => fun same => - let child := tree.forallBodyCheckAux agreement reading same - ⟨child.domainLevel, child.bodyLevel, child.domainBound, child.bound, - .rebase contextOrigin child.domainCheck, .rebase contextOrigin child.check, - child.conditionAgrees⟩ - | .extend prior extension => fun same => - let child := prior.forallBody same - ⟨child.domainLevel, child.bodyLevel, child.domainBound, child.bound, - child.domainCheck.extend extension, child.check.extend extension, child.conditionAgrees⟩ - | .weakenAt prior insertion => fun same => by - let view := liftedForallView same - let child := prior.forallBody view.sourceEq - refine ⟨child.domainLevel, child.bodyLevel, child.domainBound, child.bound, ?_, ?_, child.conditionAgrees⟩ - · simpa only [view.domainEq, AExpr.liftN] using child.domainCheck.weakenAt insertion - · simpa only [view.domainEq, view.bodyEq, AExpr.liftN] using - child.check.weakenAt (insertion.push view.originalDomain) - | .rebase origin prior => fun same => - let child := prior.forallBody same - ⟨child.domainLevel, child.bodyLevel, child.domainBound, child.bound, - .rebase origin child.domainCheck, .rebase origin child.check, child.conditionAgrees⟩ -termination_by structural check - -end - -def SynthesisInference.forallBodyCheck {β : Type u} {resolve : Address → Option (ConstRef β)} - {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} - {bounds : List VLevel} {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} - {condition : Certified.PropWhen} {domain body type : AExpr β} {level : VLevel} - (support : SynthesisInference resolve entries locals context bounds fuel before source - (.forallE condition domain body) type level) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = some (AExpr.forallE condition domain body).erase) : - SynthesisForallBodyCheck resolve entries context bounds condition domain body := - support.forallBodyCheckAux agreement reading rfl - -private theorem list_reverse_induction {α : Type u} {motive : List α → Prop} - (nil : motive []) - (append_singleton : ∀ tail last, motive tail → motive (tail ++ [last])) - (values : List α) : motive values := by - have reversed : ∀ items : List α, motive items.reverse := by - intro items - induction items with - | nil => exact nil - | cons item items ih => - simpa only [List.reverse_cons] using append_singleton items.reverse item ih - simpa using reversed values.reverse - -private theorem not_variable_spine {β : Type u} {term : AExpr β} - (notApp : ∀ fn arg, term ≠ .app fn arg) (notVar : ∀ index, term ≠ .bvar index) - (index : Nat) (arguments : List (AExpr β)) : term ≠ (AExpr.bvar index).appN arguments := by - intro same - induction arguments using list_reverse_induction with - | nil => exact notVar index same - | append_singleton arguments argument ih => - exact notApp _ _ (by simpa only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] using same) - -private theorem bvar_variable_spine {β : Type u} {left right : Nat} {arguments : List (AExpr β)} - (same : AExpr.bvar left = (AExpr.bvar right).appN arguments) : arguments = [] ∧ left = right := by - induction arguments using list_reverse_induction with - | nil => exact ⟨rfl, AExpr.bvar.inj same⟩ - | append_singleton arguments argument ih => - simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] at same - cases same - -private theorem app_variable_spine {β : Type u} {fn arg : AExpr β} {index : Nat} - {arguments : List (AExpr β)} (same : fn.app arg = (AExpr.bvar index).appN arguments) : - arguments = arguments.dropLast ++ [arg] ∧ fn = (AExpr.bvar index).appN arguments.dropLast := by - induction arguments using list_reverse_induction with - | nil => cases same - | append_singleton arguments argument ih => - simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil, AExpr.app.injEq] at same - simp only [List.dropLast_concat] - exact ⟨by rw [same.2], same.1⟩ - -/-- Extract the actual argument calls from a checked variable application. -No type check on the generated substitutions is added. -/ -def BinderInference.variableSpineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} - {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} - (support : BinderInference resolve entries locals context fuel before source term type) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = some term.erase) - (index : Nat) (arguments : List (AExpr β)) (headEquals : term = (AExpr.bvar index).appN arguments) : - SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries context index arguments type := - match support with - | .fvar _ _ atIndex => by - obtain ⟨rfl, rfl⟩ := bvar_variable_spine headEquals - exact ⟨_, atIndex, .nil _⟩ - | .app _ miss trace functionTree head argumentTree conditions hashPath comparisonFaithful - _ _ _ _ _ _ => by - obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - have parts := app_variable_spine headEquals - have prior := functionTree.variableSpineOrigin (incoming := incoming) (incomingContext := incomingContext) - (incomingBounds := incomingBounds) keyedAgreement functionReading index arguments.dropLast parts.2 - have spine := prior.spine.snoc (.source (.binderArgument trace functionTree head argumentTree - keyedAgreement functionReading argumentReading conditions hashPath comparisonFaithful)) - exact ⟨prior.headType, prior.atIndex, by simpa only [← parts.1] using spine⟩ - | .sort .. | .cachedSort .. | .const .. | .polymorphic .. | .cachedConst .. | .forallE .. | .lam .. => by - exact False.elim (not_variable_spine (by intro fn arg same; cases same) - (by intro index same; cases same) index arguments headEquals) -termination_by structural support - -def SynthesisVariableSpineOrigin.weakenAt {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext source target : Model.Context β} - {incomingBounds : List VLevel} {cutoff index : Nat} {arguments : List (AExpr β)} {type : AExpr β} - (spine : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries source index arguments type) - (insertion : ContextInsertion source target cutoff) : - SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries target (liftVar 1 index cutoff) - (arguments.map (AExpr.liftN 1 · cutoff)) (type.liftN 1 cutoff) := - ⟨spine.headType.liftN 1 cutoff, - by simpa only [liftVar, Nat.add_comm 1] using insertion.lookup spine.atIndex, - spine.spine.weakenAt insertion⟩ - -def SynthesisVariableSpineOrigin.rebase {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming middle entries : Model.Environment β} {incomingContext middleContext context : Model.Context β} - {incomingBounds middleBounds : List VLevel} {index : Nat} {arguments : List (AExpr β)} {type : AExpr β} - (spine : SynthesisVariableSpineOrigin resolve middle middleContext middleBounds entries context index arguments type) - (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) : - SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries context index arguments type := - ⟨spine.headType, spine.atIndex, spine.spine.rebase origin⟩ - -mutual - -def SynthesisInference.variableSpineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel : Nat} - {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} {level : VLevel} - (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) - (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = some term.erase) - (index : Nat) (arguments : List (AExpr β)) (headEquals : term = (AExpr.bvar index).appN arguments) : - SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries context index arguments type := - match support with - | .known inference _ | .reuseType inference .. | .fvar inference .. => - inference.variableSpineOrigin agreement reading index arguments headEquals - | .cached tree priorAgreement priorReading _ _ _ _ => - tree.variableSpineOrigin contextOrigin priorAgreement priorReading index arguments headEquals - | .cachedFrom check _ _ => (check.variableSpineOrigin index arguments headEquals).rebase contextOrigin - | .app _ miss trace functionTree argumentTree conditions hashPath comparisonFaithful _ _ _ _ _ _ => by - obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - have parts := app_variable_spine headEquals - have prior := functionTree.variableSpineOrigin contextOrigin keyedAgreement functionReading - index arguments.dropLast parts.2 - have spine := prior.spine.snoc (.source (.applicationArgument contextOrigin trace functionTree argumentTree - keyedAgreement functionReading argumentReading conditions hashPath comparisonFaithful)) - exact ⟨prior.headType, prior.atIndex, by simpa only [← parts.1] using spine⟩ - | .appBeta _ miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath - comparisonFaithful _ _ _ _ _ _ => by - obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - have parts := app_variable_spine headEquals - have prior := functionTree.variableSpineOrigin contextOrigin keyedAgreement functionReading - index arguments.dropLast parts.2 - have spine := (prior.spine.convert (.rebase contextOrigin reduction)).snoc - (.source (.applicationBetaArgument contextOrigin trace functionTree exposure exposureCoherent argumentTree - keyedAgreement functionReading argumentReading conditions hashPath comparisonFaithful)) - exact ⟨prior.headType, prior.atIndex, by simpa only [← parts.1] using spine⟩ - | .forallE .. | .lam .. | .lamBeta .. => by - exact False.elim (not_variable_spine (by intro fn arg same; cases same) - (by intro index same; cases same) index arguments headEquals) -termination_by structural support - -def SynthesisRetainedCheck.variableSpineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} - (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) - (index : Nat) (arguments : List (AExpr β)) (same : term = (AExpr.bvar index).appN arguments) : - SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries context index arguments type := - match check with - | .source contextOrigin tree agreement reading _ => - tree.variableSpineOrigin contextOrigin agreement reading index arguments same - | .extend prior extension => (prior.variableSpineOrigin index arguments same).extend extension - | .weakenAt prior insertion => by - let view := liftedVariableSpineView same - let old := prior.variableSpineOrigin view.originalIndex view.originalArguments view.sourceEq - simpa only [view.indexEq, view.argumentsEq] using old.weakenAt insertion - | .rebase origin prior => (prior.variableSpineOrigin index arguments same).rebase origin -termination_by structural check - -end - -def SynthesisForallBodyCheck.variableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds bounds : List VLevel} {condition : Certified.PropWhen} {domain : AExpr β} - {index : Nat} {arguments : List (AExpr β)} - (check : SynthesisForallBodyCheck resolve entries context bounds condition domain - ((AExpr.bvar index).appN arguments)) - (parentOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) : - SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries - (context.push domain) index arguments (.sort check.bodyLevel) := - (check.check.variableSpineOrigin index arguments rfl).rebase parentOrigin - -/-- Keep the actual variable-application checks inside a lambda body. -The body's inferred type is retained separately from the lambda's eventual -codomain, which synthesis may obtain by reducing that type. -/ -def BinderInference.lambdaBodyVariableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {locals : List FVarId} {fuel index : Nat} - {before : TcState .anon} {source : KExpr .anon} {domain type : AExpr β} - {condition : Certified.PropWhen} {arguments : List (AExpr β)} - (support : BinderInference resolve entries locals context fuel before source - (.lam condition domain ((AExpr.bvar index).appN arguments)) type) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = - some (AExpr.lam condition domain ((AExpr.bvar index).appN arguments)).erase) : - Σ resultType, SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds - entries (context.push domain) index arguments resultType := by - cases support with - | lam full miss trace opening bodyTree constructed bound coherent closingFaithful faithful => - obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading - have domainAgreement := (miss.localContext.symm ▸ agreement).congr trace.contextPreserved.symm - obtain ⟨_, openedReads, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReads bodyReads trace.openRun - exact ⟨_, bodyTree.variableSpineOrigin openedAgreement openedReads index arguments rfl⟩ - -mutual - -private def SynthesisInference.lambdaBodyVariableSpineAux {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel index : Nat} - {before : TcState .anon} {source : KExpr .anon} {term domain type : AExpr β} - {condition : Certified.PropWhen} {arguments : List (AExpr β)} {level : VLevel} - (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) - (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = some term.erase) : - term = .lam condition domain ((AExpr.bvar index).appN arguments) → - Σ resultType, SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds - entries (context.push domain) index arguments resultType := - match support with - | .known inference _ => fun same => by - cases same - exact inference.lambdaBodyVariableSpine agreement reading - | .reuseType inference .. => fun same => by - cases same - exact inference.lambdaBodyVariableSpine agreement reading - | .cached tree priorAgreement priorReading _ _ _ _ => fun same => - tree.lambdaBodyVariableSpineAux contextOrigin priorAgreement priorReading same - | .cachedFrom check _ _ => fun same => - let child := check.lambdaBodyVariableSpine same - ⟨child.1, child.2.rebase contextOrigin⟩ - | .lam full miss trace opening domainTree bodyTree conditionAgrees constructed bound coherent - closingFaithful faithful => fun same => by - cases same - obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm - obtain ⟨_, openedReads, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReads bodyReads trace.openRun - exact ⟨_, bodyTree.variableSpineOrigin - (contextOrigin.push domainTree keyedAgreement domainReads trace.domainRun) - openedAgreement openedReads index arguments rfl⟩ - | .lamBeta full miss trace opening domainTree bodyTree origin reduction conditionAgrees - constructed bound closingFaithful faithful => fun same => by - cases same - obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm - obtain ⟨_, openedReads, openedAgreement, _⟩ := - openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReads bodyReads trace.openRun - exact ⟨_, bodyTree.variableSpineOrigin - (contextOrigin.push domainTree keyedAgreement domainReads trace.domainRun) - openedAgreement openedReads index arguments rfl⟩ - - | .fvar .. | .app .. | .appBeta .. | .forallE .. => fun same => by cases same -termination_by structural support - -def SynthesisRetainedCheck.lambdaBodyVariableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {term type domain : AExpr β} {level : VLevel} - {condition : Certified.PropWhen} {index : Nat} {arguments : List (AExpr β)} - (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) : - term = .lam condition domain ((AExpr.bvar index).appN arguments) → - Σ resultType, SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds - entries (context.push domain) index arguments resultType := - match check with - | .source contextOrigin tree agreement reading _ => fun same => - tree.lambdaBodyVariableSpineAux contextOrigin agreement reading same - | .extend prior extension => fun same => - let child := prior.lambdaBodyVariableSpine same - ⟨child.1, child.2.extend extension⟩ - | .weakenAt (cutoff := cutoff) prior insertion => fun same => by - let lambda := liftedLambdaView same - let body := liftedVariableSpineView lambda.bodyEq.symm - let child := prior.lambdaBodyVariableSpine - (lambda.sourceEq.trans (congrArg (AExpr.lam condition lambda.originalDomain) body.sourceEq)) - refine ⟨child.1.liftN 1 (cutoff + 1), ?_⟩ - simpa only [lambda.domainEq, body.indexEq, body.argumentsEq] using - child.2.weakenAt (insertion.push lambda.originalDomain) - | .rebase origin prior => fun same => - let child := prior.lambdaBodyVariableSpine same - ⟨child.1, child.2.rebase origin⟩ -termination_by structural check - -end - -def SynthesisInference.lambdaBodyVariableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel index : Nat} - {before : TcState .anon} {source : KExpr .anon} {domain type : AExpr β} - {condition : Certified.PropWhen} {arguments : List (AExpr β)} {level : VLevel} - (support : SynthesisInference resolve entries locals context bounds fuel before source - (.lam condition domain ((AExpr.bvar index).appN arguments)) type level) - (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = - some (AExpr.lam condition domain ((AExpr.bvar index).appN arguments)).erase) : - Σ resultType, SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds - entries (context.push domain) index arguments resultType := - support.lambdaBodyVariableSpineAux contextOrigin agreement reading rfl - -theorem BinderInference.lambdaPrefix {β : Type u} - {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} - {locals : List FVarId} {context : Model.Context β} {fuel : Nat} - {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} - (support : BinderInference resolve entries locals context fuel before source term type) : - LambdaPrefix term type term.lambdaDepth := by - induction support with - | lam _ _ _ _ _ _ _ _ _ _ ih => exact .lam ih - | _ => exact .zero _ _ - -mutual - -theorem SynthesisInference.lambdaPrefix {β : Type u} - {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} - {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} - {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} {level : VLevel} - (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) : - LambdaPrefix term type term.lambdaDepth := - match support with - | .known inference _ => inference.lambdaPrefix - | .cached tree .. => tree.lambdaPrefix - | .cachedFrom check _ _ => check.lambdaPrefix - | .reuseType inference _ _ _ _ _ => inference.lambdaPrefix - | .lam _ _ _ _ _ bodyTree _ _ _ _ _ _ => .lam bodyTree.lambdaPrefix - | .lamBeta _ _ _ _ _ bodyTree _ _ _ _ _ _ _ => by - have depth := bodyTree.lambdaPrefix.lambdaDepth_zero - (AExpr.appN_ne_forallE (by intro condition domain body same; cases same) _) - simpa only [AExpr.lambdaDepth, depth] using LambdaPrefix.lam (LambdaPrefix.zero _ _) - | .fvar .. | .app .. | .appBeta .. | .forallE .. => .zero _ _ -termination_by structural support - -theorem SynthesisRetainedCheck.lambdaPrefix {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} - (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) : - LambdaPrefix term type term.lambdaDepth := - match check with - | .source _ tree _ _ _ => tree.lambdaPrefix - | .extend prior _ | .rebase _ prior => prior.lambdaPrefix - | .weakenAt prior _ => by - simpa only [AExpr.lambdaDepth_liftN] using prior.lambdaPrefix.liftN 1 _ -termination_by structural check - -end - -def SynthesisRetainedCheck.origin {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} - (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type := - match check with - | .source contextOrigin tree agreement reading accepted => - .source (.checked contextOrigin tree agreement reading accepted) - | .extend prior extension => prior.origin.extend extension - | .weakenAt prior insertion => .weakenAt prior.origin insertion - | .rebase origin prior => .rebase origin prior.origin -termination_by structural check - -def SynthesisRetainedCheck.typeOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} - (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) : - SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context type (.sort level) := - match check with - | .source contextOrigin tree agreement reading accepted => - .inferredType contextOrigin tree agreement reading accepted - | .extend prior extension => prior.typeOrigin.extend extension - | .weakenAt prior insertion => .weakenAt prior.typeOrigin insertion - | .rebase origin prior => .rebase origin prior.typeOrigin -termination_by structural check - -/-- The head's check and every argument check of an actual application -spine, retained as data for subsequent reductions and substitutions. -/ -structure SynthesisSpineOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) - (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) - (entries : Model.Environment β) (context : Model.Context β) - (head : AExpr β) (arguments : List (AExpr β)) (type : AExpr β) where - headType : AExpr β - headOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context head headType - leading : LambdaPrefix head headType head.lambdaDepth - argumentsOrigin : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds - entries context headType arguments type - -private theorem appN_last {β : Type u} {head : AExpr β} {arguments : List (AExpr β)} - (nonempty : arguments ≠ []) : - head.appN arguments = (head.appN arguments.dropLast).app (arguments.getLast nonempty) := by - calc - head.appN arguments = head.appN (arguments.dropLast ++ [arguments.getLast nonempty]) := - congrArg (AExpr.appN head) (List.dropLast_concat_getLast nonempty).symm - _ = _ := by simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] - -private theorem nonapp_spine_empty {β : Type u} {term head : AExpr β} {arguments : List (AExpr β)} - (notApp : ∀ fn arg, term ≠ .app fn arg) (same : term = head.appN arguments) : arguments = [] := by - by_contra nonempty - exact notApp _ _ (same.trans (appN_last nonempty)) - -private theorem app_spine_parts {β : Type u} {fn arg head : AExpr β} {arguments : List (AExpr β)} - (nonempty : arguments ≠ []) (same : fn.app arg = head.appN arguments) : - arguments = arguments.dropLast ++ [arg] ∧ fn = head.appN arguments.dropLast := by - have parts := AExpr.app.inj (same.trans (appN_last nonempty)) - exact ⟨by rw [parts.2]; exact (List.dropLast_concat_getLast nonempty).symm, parts.1⟩ - -def BinderInference.spineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} - {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} - (support : BinderInference resolve entries locals context fuel before source term type) - (checked : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = some term.erase) - (head : AExpr β) (arguments : List (AExpr β)) (same : term = head.appN arguments) : - SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := - if empty : arguments = [] then by - subst arguments - simp only [AExpr.appN_nil] at same - subst head - exact ⟨type, .source checked, support.lambdaPrefix, .nil _⟩ - else match support with - | .app _ miss trace functionTree functionHead argumentTree conditions hashPath comparisonFaithful - _ _ _ _ _ _ => by - obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - have parts := app_spine_parts empty same - have prior := functionTree.spineOrigin (incoming := incoming) (incomingContext := incomingContext) - (incomingBounds := incomingBounds) - (.binderHead functionTree functionHead keyedAgreement functionReading trace.functionRun) - keyedAgreement functionReading head arguments.dropLast parts.2 - refine ⟨prior.headType, prior.headOrigin, prior.leading, ?_⟩ - simpa only [← parts.1] using prior.argumentsOrigin.snoc - (.source (.binderArgument trace functionTree functionHead argumentTree keyedAgreement - functionReading argumentReading conditions hashPath comparisonFaithful)) - | .sort .. | .cachedSort .. | .fvar .. | .const .. | .polymorphic .. | .cachedConst .. | .forallE .. | .lam .. => by - exact False.elim (empty (nonapp_spine_empty (by intro fn arg same; cases same) same)) -termination_by structural support - -def SynthesisSpineOrigin.extend {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming earlier entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {head type : AExpr β} {arguments : List (AExpr β)} - (spine : SynthesisSpineOrigin resolve incoming incomingContext incomingBounds earlier context head arguments type) - (extension : InterfaceExtends earlier entries) : - SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := - ⟨spine.headType, spine.headOrigin.extend extension, spine.leading, spine.argumentsOrigin.extend extension⟩ - -def SynthesisSpineOrigin.weakenAt {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext source target : Model.Context β} - {incomingBounds : List VLevel} {cutoff : Nat} {head type : AExpr β} {arguments : List (AExpr β)} - (spine : SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries source head arguments type) - (insertion : ContextInsertion source target cutoff) : - SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries target (head.liftN 1 cutoff) - (arguments.map (AExpr.liftN 1 · cutoff)) (type.liftN 1 cutoff) := - ⟨spine.headType.liftN 1 cutoff, .weakenAt spine.headOrigin insertion, - by simpa only [AExpr.lambdaDepth_liftN] using spine.leading.liftN 1 cutoff, - spine.argumentsOrigin.weakenAt insertion⟩ - -def SynthesisSpineOrigin.rebase {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming middle entries : Model.Environment β} {incomingContext middleContext context : Model.Context β} - {incomingBounds middleBounds : List VLevel} {head type : AExpr β} {arguments : List (AExpr β)} - (spine : SynthesisSpineOrigin resolve middle middleContext middleBounds entries context head arguments type) - (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) : - SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := - ⟨spine.headType, .rebase origin spine.headOrigin, spine.leading, spine.argumentsOrigin.rebase origin⟩ - -mutual - -/-- Recover every application argument and the head's original checked -lambda prefix from the executed source inference, including binder-backed -local and constant spines. -/ -def SynthesisInference.spineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel : Nat} - {before after : TcState .anon} {source result : KExpr .anon} {term type : AExpr β} {level : VLevel} - (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) - (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) - (agreement : LocalContextReading resolve locals before.lctx context) - (reading : readScopedExpr? resolve locals source = some term.erase) - (accepted : RecM.infer source (methodsN fuel) before = .ok result after) - (head : AExpr β) (arguments : List (AExpr β)) (same : term = head.appN arguments) : - SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := - if empty : arguments = [] then by - subst arguments - simp only [AExpr.appN_nil] at same - subst head - exact ⟨type, .source (.checked contextOrigin support agreement reading accepted), support.lambdaPrefix, .nil _⟩ - else match support with - | .cached tree priorAgreement priorReading priorRun _ _ _ => - tree.spineOrigin contextOrigin priorAgreement priorReading priorRun head arguments same - | .cachedFrom check _ _ => (check.spineOrigin head arguments same).rebase contextOrigin - | .known inference formation => - inference.spineOrigin (.checked contextOrigin (.known inference formation) agreement reading accepted) - agreement reading head arguments same - | .reuseType inference typeTree extension typeReading typeRun equivalent => - inference.spineOrigin - (.checked contextOrigin (.reuseType inference typeTree extension typeReading typeRun equivalent) - agreement reading accepted) agreement reading head arguments same - | .app _ miss trace functionTree argumentTree conditions hashPath comparisonFaithful _ _ _ _ _ _ => by - obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - have parts := app_spine_parts empty same - have prior := functionTree.spineOrigin contextOrigin keyedAgreement functionReading trace.functionRun - head arguments.dropLast parts.2 - refine ⟨prior.headType, prior.headOrigin, prior.leading, ?_⟩ - simpa only [← parts.1] using prior.argumentsOrigin.snoc - (.source (.applicationArgument contextOrigin trace functionTree argumentTree keyedAgreement - functionReading argumentReading conditions hashPath comparisonFaithful)) - | .appBeta _ miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath - comparisonFaithful _ _ _ _ _ _ => by - obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading - have keyedAgreement := miss.localContext.symm ▸ agreement - have parts := app_spine_parts empty same - have prior := functionTree.spineOrigin contextOrigin keyedAgreement functionReading trace.functionRun - head arguments.dropLast parts.2 - refine ⟨prior.headType, prior.headOrigin, prior.leading, ?_⟩ - simpa only [← parts.1] using (prior.argumentsOrigin.convert (.rebase contextOrigin reduction)).snoc - (.source (.applicationBetaArgument contextOrigin trace functionTree exposure exposureCoherent argumentTree - keyedAgreement functionReading argumentReading conditions hashPath comparisonFaithful)) - | .fvar .. | .forallE .. | .lam .. | .lamBeta .. => by - exact False.elim (empty (nonapp_spine_empty (by intro fn arg same; cases same) same)) -termination_by structural support - -def SynthesisRetainedCheck.spineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} - (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) - (head : AExpr β) (arguments : List (AExpr β)) (same : term = head.appN arguments) : - SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := - match check with - | .source contextOrigin tree agreement reading accepted => - tree.spineOrigin contextOrigin agreement reading accepted head arguments same - | .extend prior extension => (prior.spineOrigin head arguments same).extend extension - | .weakenAt prior insertion => by - let view := liftedSpineView same - let old := prior.spineOrigin view.originalHead view.originalArguments view.sourceEq - simpa only [view.headEq, view.argumentsEq] using old.weakenAt insertion - | .rebase origin prior => (prior.spineOrigin head arguments same).rebase origin -termination_by structural check - -end - -def SynthesisSpineOrigin.betaTrace {β : Type u} {resolve : Address → Option (ConstRef β)} - {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} - {incomingBounds : List VLevel} {head type : AExpr β} {arguments : List (AExpr β)} {count : Nat} - (origin : SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type) - (enough : count ≤ head.lambdaDepth) : - SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context - (head.appN arguments) (AExpr.betaPrefix count head arguments) type := - .prefix origin.headOrigin (origin.leading.truncate enough) origin.argumentsOrigin - -theorem SynthesisHead.appN_head {β : Type u} {head : AExpr β} {arguments : List (AExpr β)} - (support : SynthesisHead (head.appN arguments)) : SynthesisHead head := by - induction arguments generalizing head with - | nil => exact support - | cons argument arguments ih => - have applied := ih support - cases applied with - | app head => exact head - -private theorem BinderInference.no_lambda_spine {β : Type u} - {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} - {locals : List FVarId} {context : Model.Context β} {fuel : Nat} - {before : TcState .anon} {source : KExpr .anon} - {condition : Certified.PropWhen} {domain body type : AExpr β} {arguments : List (AExpr β)} - (support : BinderInference resolve entries locals context fuel before source - ((AExpr.lam condition domain body).appN arguments) type) - (nonempty : arguments ≠ []) : False := by - induction arguments using list_reverse_induction with - | nil => exact nonempty rfl - | append_singleton arguments argument ih => - simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] at support - cases support with - | app _ _ _ _ head => cases head.appN_head - -theorem BinderInference.lambdaSpineTyping {β : Type u} - {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} - {locals : List FVarId} {context : Model.Context β} {fuel : Nat} - {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} - (support : BinderInference resolve entries locals context fuel before source term type) - (typed : TypingClaim.{u,v} entries context term type) : - LambdaSpineTyping.{u,v} entries context term type := by - intro condition domain body arguments same - subst term - by_cases empty : arguments = [] - · subst arguments - exact ⟨type, typed, support.lambdaPrefix, .nil _⟩ - · exact False.elim (support.no_lambda_spine empty) - -mutual - /-- Simultaneous soundness of the term and formation of its returned type. The context hypothesis is discharged by actual domain inference at each binder, and by the empty context at the production declaration boundary. -/ -theorem SynthesisInference.soundWithSpine {β : Type u} +theorem SynthesisInference.soundWithHereditary {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} @@ -1731,36 +32,34 @@ theorem SynthesisInference.soundWithSpine {β : Type u} readScopedExpr? resolve locals result = some type.erase ∧ TypingClaim.{u,v} entries context term type ∧ TypingClaim.{u,v} entries context type (.sort level) ∧ - LambdaSpineTyping.{u,v} entries context term type := + HereditaryTyping.{u,v} entries context term type := match support with | .known inference formation => by obtain ⟨reads, checked⟩ := inference.sound agreement reading accepted have typed := checked.typing formation.sound - exact ⟨reads, typed, formation.sound, inference.lambdaSpineTyping typed⟩ + exact ⟨reads, typed, formation.sound, inference.hereditary typed agreement reading⟩ | .cached tree priorAgreement priorReading priorRun hit cacheMatch resultReading => by obtain ⟨_, typed, formation, spine⟩ := - tree.soundWithSpine formed priorAgreement priorReading priorRun + tree.soundWithHereditary formed priorAgreement priorReading priorRun rw [hit.run] at accepted cases accepted exact ⟨cacheMatch.symm ▸ resultReading, typed, formation, spine⟩ | .cachedFrom check hit resultReading => by - obtain ⟨typed, formation, spine⟩ := check.soundWithSpine formed + obtain ⟨typed, formation, spine⟩ := check.soundWithHereditary formed rw [hit.run] at accepted cases accepted exact ⟨resultReading, typed, formation, spine⟩ | .reuseType inference typeTree extension typeReading typeRun same => by obtain ⟨reads, checked⟩ := inference.sound agreement reading accepted obtain ⟨_, typeTyped, _, _⟩ := - typeTree.soundWithSpine (.empty _) (.empty _ _) typeReading typeRun + typeTree.soundWithHereditary (.empty _) (.empty _ _) typeReading typeRun have typeFormed := same.termTyping (typing_instL_closed (context := context) (extension.typing typeTyped) _) have typed := checked.typing typeFormed - exact ⟨reads, typed, typeFormed, inference.lambdaSpineTyping typed⟩ + exact ⟨reads, typed, typeFormed, inference.hereditary typed agreement reading⟩ | .fvar inference atIndex boundAtIndex => by obtain ⟨reads, typed⟩ := inference.synthesis (.bvar _) agreement reading accepted - exact ⟨reads, typed, formed _ _ _ atIndex boundAtIndex, - LambdaSpineTyping.non_application (by intro fn arg same; cases same) - (by intro condition domain body same; cases same)⟩ + exact ⟨reads, typed, formed _ _ _ atIndex boundAtIndex, .bvar typed atIndex⟩ | .app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful => by obtain ⟨state, run⟩ := infer_uncached_success miss accepted @@ -1768,11 +67,11 @@ theorem SynthesisInference.soundWithSpine {β : Type u} obtain ⟨fnReads, argReads⟩ := readScopedExpr?_app_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement obtain ⟨functionTypeReads, functionTyped, functionFormed, functionSpine⟩ := - functionTree.soundWithSpine formed keyedAgreement fnReads trace.functionRun + functionTree.soundWithHereditary formed keyedAgreement fnReads trace.functionRun obtain ⟨domainReads, codomainReads⟩ := readScopedExpr?_all_parts functionTypeReads have argumentAgreement := keyedAgreement.congr trace.contextPreserved.symm - obtain ⟨argumentTypeReads, argumentTyped, _, _⟩ := - argumentTree.soundWithSpine formed argumentAgreement argReads trace.argumentRun + obtain ⟨argumentTypeReads, argumentTyped, _, argumentSpine⟩ := + argumentTree.soundWithHereditary formed argumentAgreement argReads trace.argumentRun have sameReading := beq_readScopedExpr? (resolve := resolve) (locals := locals) (depth := 0) comparisonFaithful hashPath have sameType := AExpr.eq_of_erase_annotations @@ -1780,7 +79,7 @@ theorem SynthesisInference.soundWithSpine {β : Type u} have checked := sameType ▸ argumentTyped.checking refine ⟨?_, functionTyped.appChecking checked, functionTyped.applicationType functionFormed checked, - functionSpine.app (sameType ▸ argumentTyped)⟩ + .app functionSpine (sameType ▸ argumentSpine)⟩ rw [trace.output run, AExpr.erase_inst] exact (subst_readScopedExpr? bodyConstructed argConstructed bodyBound argBound coherent faithful codomainReads argReads).1 @@ -1791,11 +90,11 @@ theorem SynthesisInference.soundWithSpine {β : Type u} obtain ⟨fnReads, argReads⟩ := readScopedExpr?_app_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement obtain ⟨functionTypeReads, functionTyped, _, functionSpine⟩ := - functionTree.soundWithSpine formed keyedAgreement fnReads trace.functionRun + functionTree.soundWithHereditary formed keyedAgreement fnReads trace.functionRun obtain ⟨domainReads, codomainReads, _⟩ := exposure.reading functionTypeReads exposureCoherent have argumentAgreement := keyedAgreement.congr trace.exposure_context.symm - obtain ⟨argumentTypeReads, argumentTyped, _, _⟩ := - argumentTree.soundWithSpine formed argumentAgreement argReads trace.argumentRun + obtain ⟨argumentTypeReads, argumentTyped, _, argumentSpine⟩ := + argumentTree.soundWithHereditary formed argumentAgreement argReads trace.argumentRun have sameType := AExpr.eq_of_erase_annotations (Option.some.inj (argumentTypeReads.symm.trans ((beq_readScopedExpr? comparisonFaithful hashPath).trans domainReads))) conditions @@ -1804,7 +103,7 @@ theorem SynthesisInference.soundWithSpine {β : Type u} have checked := sameType ▸ argumentTyped.checking refine ⟨?_, exposedTyped.appChecking checked, exposedTyped.applicationType functionFormed checked, - (functionSpine.convert reduction.rigid converted functionFormed).app (sameType ▸ argumentTyped)⟩ + .app (.convert functionSpine reduction.rigid converted functionFormed) (sameType ▸ argumentSpine)⟩ rw [trace.output run, AExpr.erase_inst] exact (subst_readScopedExpr? bodyConstructed argConstructed bodyBound argBound coherent faithful codomainReads argReads).1 @@ -1813,22 +112,20 @@ theorem SynthesisInference.soundWithSpine {β : Type u} obtain ⟨state, run⟩ := infer_uncached_success miss accepted obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement - obtain ⟨_, domainTyped, _, _⟩ := - domainTree.soundWithSpine formed keyedAgreement domainReads trace.domainRun + obtain ⟨_, domainTyped, _, domainSpine⟩ := + domainTree.soundWithHereditary formed keyedAgreement domainReads trace.domainRun have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm obtain ⟨_, openedReads, openedAgreement, _⟩ := openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReads bodyReads trace.openRun - obtain ⟨_, bodyTyped, _, _⟩ := - bodyTree.soundWithSpine (formed.push domainTyped) openedAgreement openedReads trace.bodyRun - refine ⟨?_, ?_, TypingClaim.sort _, - LambdaSpineTyping.non_application (by intro fn arg same; cases same) - (by intro condition domain body same; cases same)⟩ + obtain ⟨_, bodyTyped, _, bodySpine⟩ := + bodyTree.soundWithHereditary (formed.push domainTyped) openedAgreement openedReads trace.bodyRun + have typed := AExpr.LevelEquivalent.sort + (Theory.VLevel.equiv_def.mpr fun levels => + (Theory.VLevel.equiv_def.mp (readLevel_mkIMax levelFaithful domainBound bodyBound) + levels).symm) |>.typing (TypingClaim.forallE domainTyped bodyTyped rfl) + refine ⟨?_, typed, TypingClaim.sort _, .forallE typed domainSpine bodySpine rfl⟩ · rw [trace.output run, internExpr_readScopedExpr? coherent faithful] rfl - · exact AExpr.LevelEquivalent.sort - (Theory.VLevel.equiv_def.mpr fun levels => - (Theory.VLevel.equiv_def.mp (readLevel_mkIMax levelFaithful domainBound bodyBound) - levels).symm) |>.typing (TypingClaim.forallE domainTyped bodyTyped rfl) | .lam full miss trace opening domainTree bodyTree conditionAgrees constructed bound coherent closingFaithful faithful => by obtain ⟨state, run⟩ := infer_uncached_success miss accepted @@ -1836,17 +133,17 @@ theorem SynthesisInference.soundWithSpine {β : Type u} obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement obtain ⟨_, domainTyped, _, _⟩ := - domainTree.soundWithSpine formed keyedAgreement domainReads trace.domainRun + domainTree.soundWithHereditary formed keyedAgreement domainReads trace.domainRun have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm obtain ⟨_, openedReads, openedAgreement, _⟩ := openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReads bodyReads trace.openRun - obtain ⟨bodyTypeReads, bodyTyped, bodyFormed, _⟩ := - bodyTree.soundWithSpine (formed.push domainTyped) openedAgreement openedReads trace.bodyRun + obtain ⟨bodyTypeReads, bodyTyped, bodyFormed, bodySpine⟩ := + bodyTree.soundWithHereditary (formed.push domainTyped) openedAgreement openedReads trace.bodyRun obtain ⟨closedReads, closedCoherent⟩ := abstractFVars_readScopedExpr? constructed bound coherent closingFaithful bodyTypeReads have typed := TypingClaim.lam domainTyped bodyFormed bodyTyped conditionAgrees refine ⟨?_, typed, TypingClaim.forallE domainTyped bodyFormed conditionAgrees, - LambdaSpineTyping.lam typed (.lam bodyTree.lambdaPrefix)⟩ + .lam typed bodySpine⟩ rw [trace.output run, internExpr_readScopedExpr? (table := trace.abstracted.2) closedCoherent faithful] simp [LambdaInferenceTrace.abstracted, domainReads, closedReads, AExpr.erase] @@ -1857,30 +154,63 @@ theorem SynthesisInference.soundWithSpine {β : Type u} obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement obtain ⟨_, domainTyped, _, _⟩ := - domainTree.soundWithSpine formed keyedAgreement domainReads trace.domainRun + domainTree.soundWithHereditary formed keyedAgreement domainReads trace.domainRun have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm obtain ⟨_, openedReads, openedAgreement, _⟩ := openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReads bodyReads trace.openRun - obtain ⟨bodyTypeReads, bodyTyped, _, _⟩ := - bodyTree.soundWithSpine (formed.push domainTyped) openedAgreement openedReads trace.bodyRun + obtain ⟨bodyTypeReads, bodyTyped, _, bodySpine⟩ := + bodyTree.soundWithHereditary (formed.push domainTyped) openedAgreement openedReads trace.bodyRun obtain ⟨conversion, reducedTyped⟩ := origin.sound formed obtain ⟨_, reducedReads, reducedCoherent⟩ := reduction.reading bodyTypeReads obtain ⟨closedReads, closedCoherent⟩ := abstractFVars_readScopedExpr? constructed bound reducedCoherent closingFaithful reducedReads have typed := TypingClaim.lam domainTyped reducedTyped (bodyTyped.conv reducedTyped conversion) conditionAgrees - have bodyDepth := bodyTree.lambdaPrefix.lambdaDepth_zero - (AExpr.appN_ne_forallE (by intro condition domain body same; cases same) _) refine ⟨?_, typed, TypingClaim.forallE domainTyped reducedTyped conditionAgrees, - LambdaSpineTyping.lam typed ?_⟩ + .lam typed (.convert bodySpine (SynthesisBetaTrace.rigid (.origin origin)) conversion reducedTyped)⟩ · rw [trace.output run, internExpr_readScopedExpr? (table := trace.abstracted.2) closedCoherent faithful] simp [LambdaBodyTrace.abstracted, LambdaBodyTrace.reduced, domainReads, AExpr.erase] at ⊢ closedReads exact closedReads - · simpa only [bodyDepth] using LambdaPrefix.lam (LambdaPrefix.zero _ _) + | .letE full localState miss trace opening domainTree valueTree bodyTree domainReading valueReading bodyReading + conditions hashPath comparisonFaithful substitution reduction => by + let node := SynthesisInference.letE full localState miss trace opening domainTree valueTree bodyTree + domainReading valueReading bodyReading conditions hashPath comparisonFaithful substitution reduction + have keyedValid := miss.keyedLocalState localState + have keyedAgreement := miss.localContext.symm ▸ agreement + have valueAgreement := keyedAgreement.congr (trace.domainContext keyedValid).symm + obtain ⟨_, domainTyped, _, _⟩ := + domainTree.soundWithHereditary formed keyedAgreement domainReading trace.domainRun + obtain ⟨valueTypeReading, valueTyped, _, valueHereditary⟩ := + valueTree.soundWithHereditary formed valueAgreement valueReading trace.valueRun + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (valueTypeReading.symm.trans + ((beq_readScopedExpr? comparisonFaithful hashPath).trans domainReading))) conditions + obtain ⟨openedReading, openedAgreement, _⟩ := + trace.opened_reading opening keyedValid keyedAgreement domainReading bodyReading + obtain ⟨_, bodyTyped, bodyFormed, bodyHereditary⟩ := + bodyTree.soundWithHereditary (formed.push domainTyped) openedAgreement openedReading trace.bodyRun + have valueAtDomain := sameType ▸ valueTyped + obtain ⟨converted, typeFormed⟩ := reduction.sound formed (bodyFormed.instAt valueAtDomain .root) + exact ⟨node.outputReading agreement reading accepted, + (bodyTyped.instAt valueAtDomain .root).conv typeFormed converted, typeFormed, + .convert (bodyHereditary.substituteAt (sameType ▸ valueHereditary) .root) + reduction.rigid converted typeFormed⟩ termination_by structural support +theorem LetTypeReduction.sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} + {source : KExpr .anon} {table : InternTable .anon} {level : VLevel} {term result : AExpr β} + (reduction : LetTypeReduction resolve entries context bounds source table level term result) + (formed : ContextFormation.{u,v} entries context bounds) + (original : TypingClaim.{u,v} entries context term (.sort level)) : + ConversionClaim.{u,v} entries context term result ∧ TypingClaim.{u,v} entries context result (.sort level) := + match reduction with + | .unchanged _ => ⟨.refl _, original⟩ + | .beta _ origin => origin.sound formed +termination_by structural reduction + theorem SynthesisContext.sound {β : Type u} {resolve : Address → Option (ConstRef β)} {entries earlier : Model.Environment β} {context priorContext : Model.Context β} {bounds priorBounds : List VLevel} @@ -1893,7 +223,7 @@ theorem SynthesisContext.sound {β : Type u} {resolve : Address → Option (Cons | .push prior domainTree agreement reading accepted => by have priorFormation := prior.sound formed obtain ⟨_, domainTyped, _, _⟩ := - domainTree.soundWithSpine priorFormation agreement reading accepted + domainTree.soundWithHereditary priorFormation agreement reading accepted exact priorFormation.push domainTyped | .extend prior extension => by intro index type bound found indexed @@ -1922,23 +252,23 @@ theorem SynthesisTypeTransport.sound {β : Type u} {resolve : Address → Option exact ⟨conversion.instAt argumentAtDomain substitution, resultTyped.instAt argumentAtDomain substitution⟩ termination_by structural support -theorem SynthesisRetainedCheck.soundWithSpine {β : Type u} {resolve : Address → Option (ConstRef β)} +theorem SynthesisRetainedCheck.soundWithHereditary {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : TypingClaim.{u,v} entries context term type ∧ TypingClaim.{u,v} entries context type (.sort level) ∧ - LambdaSpineTyping.{u,v} entries context term type := + HereditaryTyping.{u,v} entries context term type := match check with | .source contextOrigin tree agreement reading accepted => - (tree.soundWithSpine (contextOrigin.sound formed) agreement reading accepted).2 + (tree.soundWithHereditary (contextOrigin.sound formed) agreement reading accepted).2 | .extend prior extension => by - obtain ⟨typed, typeFormed, spine⟩ := prior.soundWithSpine formed - exact ⟨extension.typing typed, extension.typing typeFormed, extension.lambdaSpine spine⟩ + obtain ⟨typed, typeFormed, spine⟩ := prior.soundWithHereditary formed + exact ⟨extension.typing typed, extension.typing typeFormed, spine.extend extension⟩ | .weakenAt prior insertion => by - obtain ⟨typed, typeFormed, spine⟩ := prior.soundWithSpine formed - exact ⟨insertion.typing typed, insertion.typing typeFormed, insertion.lambdaSpine spine⟩ - | .rebase origin prior => prior.soundWithSpine (origin.sound formed) + obtain ⟨typed, typeFormed, spine⟩ := prior.soundWithHereditary formed + exact ⟨insertion.typing typed, insertion.typing typeFormed, spine.weakenAt insertion⟩ + | .rebase origin prior => prior.soundWithHereditary (origin.sound formed) termination_by structural check theorem SynthesisTypingOrigin.sound {β : Type u} {resolve : Address → Option (ConstRef β)} @@ -1950,7 +280,7 @@ theorem SynthesisTypingOrigin.sound {β : Type u} {resolve : Address → Option match support with | .source check => (check.soundWithSpine formed).1 | .inferredType contextOrigin tree agreement reading accepted => - (tree.soundWithSpine (contextOrigin.sound formed) agreement reading accepted).2.2.1 + (tree.soundWithHereditary (contextOrigin.sound formed) agreement reading accepted).2.2.1 | .lambdaBody check => (check.sound formed).lambdaBody | .application functionOrigin argumentOrigin => (functionOrigin.sound formed).app (argumentOrigin.sound formed) | .reduced trace => (trace.sound formed).2 @@ -1977,37 +307,40 @@ theorem SynthesisCheckedOrigin.soundWithSpine {β : Type u} {resolve : Address LambdaSpineTyping.{u,v} entries context term type := match support with | .checked contextOrigin tree agreement reading accepted => by - obtain ⟨_, typed, _, spine⟩ := tree.soundWithSpine (contextOrigin.sound formed) agreement reading accepted - exact ⟨typed, tree.lambdaPrefix, spine⟩ + obtain ⟨_, typed, _, spine⟩ := tree.soundWithHereditary (contextOrigin.sound formed) agreement reading accepted + exact ⟨typed, spine.lambdaPrefix, spine.lambdaSpine⟩ | .binderHead tree head agreement reading accepted => by have typed := (tree.synthesis head agreement reading accepted).2 exact ⟨typed, tree.lambdaPrefix, tree.lambdaSpineTyping typed⟩ + | .binderType tree agreement reading accepted => by + have typed := (tree.sound agreement reading accepted).2.typing (TypingClaim.sort _) + exact ⟨typed, tree.lambdaPrefix, tree.lambdaSpineTyping typed⟩ | .applicationArgument contextOrigin trace functionTree argumentTree agreement functionReading argumentReading conditions hashPath comparisonFaithful => by have contextFormation := contextOrigin.sound formed have functionTypeReads := - (functionTree.soundWithSpine contextFormation agreement functionReading trace.functionRun).1 + (functionTree.soundWithHereditary contextFormation agreement functionReading trace.functionRun).1 have domainReads := (readScopedExpr?_all_parts functionTypeReads).1 obtain ⟨argumentTypeReads, argumentTyped, _, argumentSpine⟩ := - argumentTree.soundWithSpine contextFormation (agreement.congr trace.contextPreserved.symm) + argumentTree.soundWithHereditary contextFormation (agreement.congr trace.contextPreserved.symm) argumentReading trace.argumentRun have sameType := AExpr.eq_of_erase_annotations (Option.some.inj (argumentTypeReads.symm.trans ((beq_readScopedExpr? comparisonFaithful hashPath).trans domainReads))) conditions - exact ⟨sameType ▸ argumentTyped, sameType ▸ argumentTree.lambdaPrefix, sameType ▸ argumentSpine⟩ + exact ⟨sameType ▸ argumentTyped, sameType ▸ argumentSpine.lambdaPrefix, sameType ▸ argumentSpine.lambdaSpine⟩ | .applicationBetaArgument contextOrigin trace functionTree exposure exposureCoherent argumentTree agreement functionReading argumentReading conditions hashPath comparisonFaithful => by have contextFormation := contextOrigin.sound formed have functionTypeReads := - (functionTree.soundWithSpine contextFormation agreement functionReading trace.functionRun).1 + (functionTree.soundWithHereditary contextFormation agreement functionReading trace.functionRun).1 have domainReads := (exposure.reading functionTypeReads exposureCoherent).1 obtain ⟨argumentTypeReads, argumentTyped, _, argumentSpine⟩ := - argumentTree.soundWithSpine contextFormation (agreement.congr trace.exposure_context.symm) + argumentTree.soundWithHereditary contextFormation (agreement.congr trace.exposure_context.symm) argumentReading trace.argumentRun have sameType := AExpr.eq_of_erase_annotations (Option.some.inj (argumentTypeReads.symm.trans ((beq_readScopedExpr? comparisonFaithful hashPath).trans domainReads))) conditions - exact ⟨sameType ▸ argumentTyped, sameType ▸ argumentTree.lambdaPrefix, sameType ▸ argumentSpine⟩ + exact ⟨sameType ▸ argumentTyped, sameType ▸ argumentSpine.lambdaPrefix, sameType ▸ argumentSpine.lambdaSpine⟩ | .binderArgument trace functionTree head argumentTree agreement functionReading argumentReading conditions hashPath comparisonFaithful => by obtain ⟨functionTypeReads, functionTyped⟩ := @@ -2055,9 +388,9 @@ theorem SynthesisReductionOrigin.sound {β : Type u} {resolve : Address → Opti match support with | .traced trace => trace.sound formed | .checked typeContextSupport typeTree typeAgreement typeReading typeRun originPrefix transport => by - obtain ⟨_, _, _, originSpine⟩ := typeTree.soundWithSpine + obtain ⟨_, _, _, originSpine⟩ := typeTree.soundWithHereditary (typeContextSupport.sound formed) typeAgreement typeReading typeRun - obtain ⟨originConversion, originReducedTyped⟩ := originSpine.betaPrefix originPrefix + obtain ⟨originConversion, originReducedTyped⟩ := originSpine.lambdaSpine.betaPrefix originPrefix exact transport.sound formed originConversion originReducedTyped | .substitutedVariable spine atIndex checked substitution enough => by obtain ⟨typed, leading, _⟩ := checked.soundWithSpine formed @@ -2135,6 +468,35 @@ termination_by structural support end +/-- The hereditary invariant supplies the existing application-spine +contract, including after a let substitutes an entire checked derivation. -/ +theorem SynthesisInference.soundWithSpine {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + readScopedExpr? resolve locals result = some type.erase ∧ + TypingClaim.{u,v} entries context term type ∧ + TypingClaim.{u,v} entries context type (.sort level) ∧ + LambdaSpineTyping.{u,v} entries context term type := by + obtain ⟨reads, typed, typeFormed, hereditary⟩ := support.soundWithHereditary formed agreement reading accepted + exact ⟨reads, typed, typeFormed, hereditary.lambdaSpine⟩ + +theorem SynthesisRetainedCheck.soundWithSpine {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) + (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : + TypingClaim.{u,v} entries context term type ∧ TypingClaim.{u,v} entries context type (.sort level) ∧ + LambdaSpineTyping.{u,v} entries context term type := by + obtain ⟨typed, typeFormed, hereditary⟩ := check.soundWithHereditary formed + exact ⟨typed, typeFormed, hereditary.lambdaSpine⟩ + /-- Ordinary argument typing and its syntactic leading domains remain available without exposing the stronger application-spine result. -/ theorem SynthesisCheckedOrigin.sound {β : Type u} {resolve : Address → Option (ConstRef β)} @@ -2187,16 +549,15 @@ def SynthesisInference.ofSort {β : Type u} SynthesisInference resolve entries locals context bounds fuel before source term (.sort level) (.succ level) := .known inference (.sort level) -/-- An executed type check with its original local scope. Walking a -dependent function type keeps the actual domain checks that established -each nested context. -/ +/-- A retained type derivation with its local scope. Dependent substitution +keeps the original checking origins for each function-type child, so walking +the type still recovers its checked domains and codomains. -/ structure SynthesisScopedTypeCheck {β : Type u} (resolve : Address → Option (ConstRef β)) (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) (entries : Model.Environment β) (type : AExpr β) where context : Model.Context β level : VLevel - bound : VLevel - check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context type (.sort level) bound + check : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context type (.sort level) theorem SynthesisScopedTypeCheck.sound {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext : Model.Context β} @@ -2204,7 +565,7 @@ theorem SynthesisScopedTypeCheck.sound {β : Type u} {resolve : Address → Opti (check : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries type) (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : TypingClaim.{u,v} entries check.context type (.sort check.level) := - (check.check.soundWithSpine formed).1 + check.check.origin.sound formed def SynthesisScopedTypeCheck.forallBody {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext : Model.Context β} @@ -2212,12 +573,11 @@ def SynthesisScopedTypeCheck.forallBody {β : Type u} {resolve : Address → Opt (check : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries (.forallE condition domain body)) : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries body := by - let child := check.check.forallBody rfl + let child := check.check.forallView rfl exact { context := Context.push domain check.context level := child.bodyLevel - bound := child.bound - check := child.check } + check := child.bodyCheck } def SynthesisScopedTypeCheck.variableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext : Model.Context β} @@ -2255,14 +615,13 @@ def SynthesisTypeCheck.scoped {β : Type u} {resolve : Address → Option (Const SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries type := { context := [] level := level - bound := check.bound - check := .source (.empty entries) check.inference (.empty _ _) check.reading check.run } + check := check.inference.betaTyping (.empty entries) (.empty _ _) check.reading check.run } def SynthesisTypeCheck.forallBody {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {condition : Certified.PropWhen} {domain body : AExpr β} {level : VLevel} (check : SynthesisTypeCheck resolve entries (.forallE condition domain body) level) : SynthesisForallBodyCheck resolve entries [] [] condition domain body := - check.inference.forallBodyCheck (.empty _ _) check.reading + check.inference.forallBodyCheck (.empty _ _) check.reading check.run /-- An instantiated constant can recover formation from an earlier actual declaration type check, with exact interface preservation and structural diff --git a/Ix/Kernel/Verify/Consistency/SynthesisMeaning.lean b/Ix/Kernel/Verify/Consistency/SynthesisMeaning.lean new file mode 100644 index 000000000..206142eae --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SynthesisMeaning.lean @@ -0,0 +1,178 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.SynthesisDerivation + +/-! The semantic induction invariant retains checked binder children and +application arguments. It is proved from production inference; no source +support constructor takes this invariant as an input. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- A strengthening of typing that is closed under dependent substitution. +The syntactic children are essential when substitution exposes a lambda. -/ +inductive HereditaryTyping {β : Type u} (entries : Model.Environment β) : + Model.Context β → AExpr β → AExpr β → Prop + | atom {context term type} (typed : TypingClaim.{u,v} entries context term type) + (shape : BetaAtom term) : HereditaryTyping entries context term type + | bvar {context index type} (typed : TypingClaim.{u,v} entries context (.bvar index) type) + (atIndex : context[index]? = some type) : HereditaryTyping entries context (.bvar index) type + | forallE {context condition domain body type domainLevel bodyLevel} + (typed : TypingClaim.{u,v} entries context (.forallE condition domain body) type) + (domainCheck : HereditaryTyping entries context domain (.sort domainLevel)) + (bodyCheck : HereditaryTyping entries (context.push domain) body (.sort bodyLevel)) + (conditionAgrees : condition = Certified.zeroCondition bodyLevel) : + HereditaryTyping entries context (.forallE condition domain body) type + | lam {context condition domain body codomain} + (typed : TypingClaim.{u,v} entries context (.lam condition domain body) (.forallE condition domain codomain)) + (inner : HereditaryTyping entries (context.push domain) body codomain) : + HereditaryTyping entries context (.lam condition domain body) (.forallE condition domain codomain) + | app {context fn arg condition domain body} + (function : HereditaryTyping entries context fn (.forallE condition domain body)) + (argument : HereditaryTyping entries context arg domain) : + HereditaryTyping entries context (.app fn arg) (body.inst arg) + | convert {context term sourceType resultType level} + (prior : HereditaryTyping entries context term sourceType) + (rigid : AExpr.HeadRigid sourceType resultType) + (converted : ConversionClaim.{u,v} entries context sourceType resultType) + (formed : TypingClaim.{u,v} entries context resultType (.sort level)) : + HereditaryTyping entries context term resultType + +namespace HereditaryTyping + +variable {β : Type u} {entries : Model.Environment β} + +theorem typing {context : Model.Context β} {term type : AExpr β} + (checked : HereditaryTyping.{u,v} entries context term type) : + TypingClaim.{u,v} entries context term type := by + induction checked with + | atom typed | bvar typed | forallE typed | lam typed => exact typed + | app _ _ function argument => exact function.appChecking argument.checking + | convert _ _ converted formed prior => exact prior.conv formed converted + +theorem lambdaType {context : Model.Context β} {term type : AExpr β} + (checked : HereditaryTyping.{u,v} entries context term type) + {condition : Certified.PropWhen} {domain body : AExpr β} + (same : term = .lam condition domain body) : + ∃ codomain, type = .forallE condition domain codomain := by + induction checked with + | atom _ shape => exact False.elim (shape.not_lam _ _ _ same) + | bvar | forallE | app => cases same + | lam => cases same; exact ⟨_, rfl⟩ + | convert _ rigid _ _ ih => + obtain ⟨codomain, typeEq⟩ := ih same + exact ⟨codomain, (rigid (by simp only [typeEq]; intro fn arg same; cases same)).trans typeEq⟩ + +theorem lambdaPrefix {context : Model.Context β} {term type : AExpr β} + (checked : HereditaryTyping.{u,v} entries context term type) : + LambdaPrefix term type term.lambdaDepth := by + induction checked with + | atom _ shape => cases shape <;> exact .zero _ _ + | bvar | forallE | app => exact .zero _ _ + | lam _ _ ih => exact .lam ih + | @convert context term sourceType resultType level prior rigid converted formed ih => + cases term with + | lam condition domain body => + obtain ⟨_, typeEq⟩ := prior.lambdaType rfl + have fixed := rigid (by simp only [typeEq]; intro fn arg same; cases same) + simpa only [fixed] using ih + | _ => exact .zero _ _ + +theorem lambdaSpine {context : Model.Context β} {term type : AExpr β} + (checked : HereditaryTyping.{u,v} entries context term type) : + LambdaSpineTyping.{u,v} entries context term type := by + induction checked with + | atom _ shape => + exact .non_application (by cases shape <;> intro fn arg same <;> cases same) shape.not_lam + | bvar | forallE => + exact .non_application (by intro fn arg same; cases same) (by intro condition domain body same; cases same) + | @lam context condition domain body codomain typed inner _ => + exact .lam typed (.lam inner.lambdaPrefix) + | app function argument functionSpine _ => exact functionSpine.app argument.typing + | convert _ rigid converted formed prior => exact prior.convert rigid converted formed + +theorem weakenAt {source target : Model.Context β} {cutoff : Nat} {term type : AExpr β} + (checked : HereditaryTyping.{u,v} entries source term type) + (insertion : ContextInsertion source target cutoff) : + HereditaryTyping.{u,v} entries target (term.liftN 1 cutoff) (type.liftN 1 cutoff) := by + induction checked generalizing target cutoff with + | atom typed shape => exact .atom (insertion.typing typed) (shape.liftN 1 cutoff) + | bvar typed found => + exact .bvar (insertion.typing typed) (by simpa only [liftVar, Nat.add_comm 1] using insertion.lookup found) + | forallE typed _ _ agrees domain body => + exact .forallE (insertion.typing typed) (domain insertion) (body (insertion.push _)) agrees + | lam typed _ inner => exact .lam (insertion.typing typed) (inner (insertion.push _)) + | app _ _ function argument => + simpa only [AExpr.liftN, AExpr.liftN_inst_zero] using + HereditaryTyping.app (function insertion) (argument insertion) + | convert _ rigid converted formed prior => + exact .convert (prior insertion) + (AExpr.HeadRigid.map rigid (AExpr.liftN 1 · cutoff) (by intros; rfl)) + (insertion.conversion converted) (insertion.typing formed) + +theorem extend {later : Model.Environment β} {context : Model.Context β} {term type : AExpr β} + (checked : HereditaryTyping.{u,v} entries context term type) (extension : InterfaceExtends entries later) : + HereditaryTyping.{u,v} later context term type := by + induction checked with + | atom typed shape => exact .atom (extension.typing typed) shape + | bvar typed found => exact .bvar (extension.typing typed) found + | forallE typed _ _ agrees domain body => exact .forallE (extension.typing typed) domain body agrees + | lam typed _ inner => exact .lam (extension.typing typed) inner + | app _ _ function argument => exact .app function argument + | convert _ rigid converted formed prior => + exact .convert prior rigid (extension.conversion converted) (extension.typing formed) + +end HereditaryTyping + +theorem HereditaryTyping.liftValue {β : Type u} {entries : Model.Environment β} + {base source target : Model.Context β} {domain argument term type : AExpr β} {cutoff : Nat} + (substitution : ContextSubstitution base domain argument source target cutoff) + (value : HereditaryTyping.{u,v} entries base term type) : + HereditaryTyping.{u,v} entries target (term.liftN cutoff) (type.liftN cutoff) := by + induction substitution with + | root => simpa only [AExpr.liftN_zero] using value + | @push source target cutoff prior binder ih => + have lifted := ih.weakenAt (ContextInsertion.root target (binder.inst argument cutoff)) + simpa only [AExpr.liftN_liftN_merge term cutoff 1 0 0 (Nat.le_refl _) (Nat.zero_le _), + AExpr.liftN_liftN_merge type cutoff 1 0 0 (Nat.le_refl _) (Nat.zero_le _)] using lifted + +theorem HereditaryTyping.substituteAt {β : Type u} {entries : Model.Environment β} + {base source target : Model.Context β} {domain argument term type : AExpr β} {cutoff : Nat} + (checked : HereditaryTyping.{u,v} entries source term type) + (value : HereditaryTyping.{u,v} entries base argument domain) + (substitution : ContextSubstitution base domain argument source target cutoff) : + HereditaryTyping.{u,v} entries target (term.inst argument cutoff) (type.inst argument cutoff) := by + induction checked generalizing target cutoff with + | atom typed shape => exact .atom (typed.instAt value.typing substitution) (shape.inst argument cutoff) + | @bvar context index type typed found => + by_cases equal : index = cutoff + · subst index + have sameType := substitution.instantiate_removed_type found + simpa only [AExpr.inst, AExpr.instVar, Nat.lt_irrefl, if_false, if_true, sameType] using + HereditaryTyping.liftValue substitution value + · have retained := substitution.lookup_other found equal + have substituted := typed.instAt value.typing substitution + by_cases below : index < cutoff + · simp only [AExpr.inst, AExpr.instVar, below, if_true] at substituted retained ⊢ + exact .bvar substituted retained + · simp only [AExpr.inst, AExpr.instVar, below, equal, if_false] at substituted retained ⊢ + exact .bvar substituted retained + | forallE typed _ _ agrees domainCheck bodyCheck => + exact .forallE (typed.instAt value.typing substitution) (domainCheck substitution) + (bodyCheck (substitution.push _)) agrees + | lam typed _ inner => exact .lam (typed.instAt value.typing substitution) (inner (substitution.push _)) + | app _ _ function applied => + simpa only [AExpr.inst, AExpr.inst_inst_zero] using + HereditaryTyping.app (function substitution) (applied substitution) + | convert _ rigid converted formed prior => + exact .convert (prior substitution) + (AExpr.HeadRigid.map rigid (AExpr.inst · argument cutoff) (by intros; rfl)) + (converted.instAt value.typing substitution) (formed.instAt value.typing substitution) + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SynthesisReading.lean b/Ix/Kernel/Verify/Consistency/SynthesisReading.lean new file mode 100644 index 000000000..baa46a245 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SynthesisReading.lean @@ -0,0 +1,109 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.SynthesisSupport + +/-! Recover returned syntax readings before the synthesis semantic induction. +The caller supplies no context-formation or semantic typing evidence. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +theorem SynthesisInference.outputReading {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + readScopedExpr? resolve locals result = some type.erase := + match support with + | .known inference _ | .reuseType inference .. | .fvar inference .. => + (BinderInference.sound.{u,u} inference agreement reading accepted).1 + | .cached _ _ _ _ hit cacheMatch resultReading => by + rw [hit.run] at accepted + cases accepted + exact cacheMatch.symm ▸ resultReading + | .cachedFrom _ hit resultReading => by + rw [hit.run] at accepted + cases accepted + exact resultReading + | .app full miss trace functionTree _ _ _ _ + bodyConstructed argConstructed bodyBound argBound coherent faithful => by + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + rw [full] at run + obtain ⟨fnReads, argReads⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have functionTypeReads := functionTree.outputReading keyedAgreement fnReads trace.functionRun + obtain ⟨_, codomainReads⟩ := readScopedExpr?_all_parts functionTypeReads + rw [trace.output run, AExpr.erase_inst] + exact (subst_readScopedExpr? bodyConstructed argConstructed bodyBound argBound + coherent faithful codomainReads argReads).1 + | .appBeta full miss trace functionTree exposure exposureCoherent _ _ _ _ _ + bodyConstructed argConstructed bodyBound argBound coherent faithful => by + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + rw [full] at run + obtain ⟨fnReads, argReads⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have functionTypeReads := functionTree.outputReading keyedAgreement fnReads trace.functionRun + obtain ⟨_, codomainReads, _⟩ := exposure.reading functionTypeReads exposureCoherent + rw [trace.output run, AExpr.erase_inst] + exact (subst_readScopedExpr? bodyConstructed argConstructed bodyBound argBound + coherent faithful codomainReads argReads).1 + | .forallE miss trace _ _ _ _ _ _ coherent faithful => by + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + rw [trace.output run, internExpr_readScopedExpr? coherent faithful] + rfl + | .lam full miss trace opening _ bodyTree _ constructed bound coherent closingFaithful faithful => by + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + rw [full] at run + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm + obtain ⟨_, openedReads, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement (trace.absent keyedAgreement) domainReads bodyReads trace.openRun + have bodyTypeReads := bodyTree.outputReading openedAgreement openedReads trace.bodyRun + obtain ⟨closedReads, closedCoherent⟩ := abstractFVars_readScopedExpr? constructed bound coherent + closingFaithful bodyTypeReads + rw [trace.output run, + internExpr_readScopedExpr? (table := trace.abstracted.2) closedCoherent faithful] + simp [LambdaInferenceTrace.abstracted, domainReads, closedReads, AExpr.erase] + | .lamBeta full miss trace opening _ bodyTree _ reduction _ constructed bound closingFaithful faithful => by + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + rw [full] at run + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm + obtain ⟨_, openedReads, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement (trace.absent keyedAgreement) domainReads bodyReads trace.openRun + have bodyTypeReads := bodyTree.outputReading openedAgreement openedReads trace.bodyRun + obtain ⟨_, reducedReads, reducedCoherent⟩ := reduction.reading bodyTypeReads + obtain ⟨closedReads, closedCoherent⟩ := abstractFVars_readScopedExpr? constructed bound + reducedCoherent closingFaithful reducedReads + rw [trace.output run, + internExpr_readScopedExpr? (table := trace.abstracted.2) closedCoherent faithful] + simp [LambdaBodyTrace.abstracted, LambdaBodyTrace.reduced, domainReads, AExpr.erase] at ⊢ closedReads + exact closedReads + | .letE full localState miss trace opening _ _ bodyTree domainReading valueReading bodyReading + _ _ _ substitution reduction => by + have keyedValid := miss.keyedLocalState localState + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨openedReading, openedAgreement, _⟩ := + trace.opened_reading opening keyedValid keyedAgreement domainReading bodyReading + have bodyTypeReading := bodyTree.outputReading openedAgreement openedReading trace.bodyRun + obtain ⟨substitutedReading, substitutedCoherent⟩ := + trace.substituted_reading substitution bodyTypeReading valueReading + obtain ⟨middle, uncached⟩ := infer_uncached_success miss accepted + rw [full] at uncached + rw [(trace.output_state uncached).1] + exact (reduction.reading substitutedCoherent substitutedReading).1 +termination_by structural support + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SynthesisReduction.lean b/Ix/Kernel/Verify/Consistency/SynthesisReduction.lean new file mode 100644 index 000000000..a04bab9d7 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SynthesisReduction.lean @@ -0,0 +1,139 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.SynthesisDerivation + +/-! Retain the checked lambda bodies and application arguments needed for +hereditary beta substitution. Generated results carry the same derivation, +so the next reduction does not require another inference call or origin. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +namespace BetaSyntax + +/-- One leftmost beta contraction, retaining the application suffix. +A term without a beta redex at its head is unchanged. -/ +def step : AExpr β → AExpr β + | .app (.lam _ _ body) argument => body.inst argument + | .app fn arg => .app (step fn) arg + | term => term + +def steps : Nat → AExpr β → AExpr β + | 0, term => term + | count + 1, term => steps count (step term) + +private theorem step_appN {β : Type u} (head : AExpr β) (arguments : List (AExpr β)) + (notLam : ∀ condition domain body, head ≠ .lam condition domain body) : + step (head.appN arguments) = (step head).appN arguments := by + induction arguments generalizing head with + | nil => rfl + | cons argument arguments ih => + rw [AExpr.appN_cons, ih (head.app argument) (by intro condition domain body same; cases same)] + cases head <;> simp_all [step, AExpr.appN_cons] + +theorem step_beta_appN {β : Type u} (condition : Certified.PropWhen) + (domain body argument : AExpr β) (arguments : List (AExpr β)) : + step ((AExpr.lam condition domain body).appN (argument :: arguments)) = + (body.inst argument).appN arguments := by + rw [AExpr.appN_cons, step_appN _ _ (by intro condition domain body same; cases same)] + rfl + +theorem steps_betaPrefix {β : Type u} (count : Nat) (head : AExpr β) (arguments : List (AExpr β)) + (leading : count ≤ head.lambdaDepth) (supplied : count ≤ arguments.length) : + steps count (head.appN arguments) = AExpr.betaPrefix count head arguments := by + induction count generalizing head arguments with + | zero => rfl + | succ count ih => + cases head with + | lam condition domain body => + cases arguments with + | nil => simp at supplied + | cons argument arguments => + simp only [steps, step_beta_appN, AExpr.betaPrefix] + apply ih + · exact Nat.le_trans (by simpa only [AExpr.lambdaDepth, Nat.add_le_add_iff_right] using leading) + (AExpr.lambdaDepth_le_inst body argument 0) + · simpa only [List.length_cons, Nat.add_le_add_iff_right] using supplied + | _ => simp [AExpr.lambdaDepth] at leading + +end BetaSyntax + + +/-- One beta step may use a lambda produced by an earlier trace. Its +retained product has the lambda's exact syntactic domain. -/ +def SynthesisBetaTrace.beta {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {condition : Certified.PropWhen} {domain body codomain argument : AExpr β} + (functionOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context + (.lam condition domain body) (.forallE condition domain codomain)) + (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context argument domain) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (.app (.lam condition domain body) argument) (body.inst argument) (codomain.inst argument) := by + simpa only [List.nil_append, AExpr.appN_cons, AExpr.appN_nil, AExpr.betaPrefix] using + SynthesisBetaTrace.prefix functionOrigin (.lam (.zero _ _)) + ((SynthesisArgumentSpineOrigin.nil _).snoc argumentOrigin) + +theorem BetaAtom.step {β : Type u} {term : AExpr β} (atom : BetaAtom term) : BetaSyntax.step term = term := by + cases atom <;> rfl + +/-- Every head beta contraction computes its next typing derivation and +conversion from the retained source tree, including newly exposed lambdas. -/ +def SynthesisBetaTyping.betaStep {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (BetaSyntax.step term) type × + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term (BetaSyntax.step term) type := + match typing with + | .atom origin shape => by rw [shape.step]; exact ⟨.atom origin shape, .refl origin⟩ + | .bvar origin found => ⟨.bvar origin found, .refl origin⟩ + | .forallE origin domainCheck bodyCheck agrees => + ⟨.forallE origin domainCheck bodyCheck agrees, .refl origin⟩ + | .lam origin inner => ⟨.lam origin inner, .refl origin⟩ + | .app (fn := fn) function argument => by + have nextFunction := function.betaStep + cases fn with + | lam condition' domain' inner => + have view := function.lambdaView rfl + obtain ⟨rfl, rfl, codomainEq⟩ := AExpr.forallE.inj view.typeEq + refine ⟨?_, SynthesisBetaTrace.beta function.origin argument.origin⟩ + simpa only [codomainEq, BetaSyntax.step] using view.inner.substituteAt argument .root + | bvar | sort | const | app | forallE | proj | natLit => + obtain ⟨next, converted⟩ := nextFunction + exact ⟨.app next argument, .application converted argument.origin⟩ + | .convert prior trace => + let next := prior.betaStep + ⟨.convert next.1 trace, .atType (.convert prior.origin trace) next.2⟩ +termination_by structural typing + +def SynthesisBetaTyping.betaSteps {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} (count : Nat) {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (BetaSyntax.steps count term) type × + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term (BetaSyntax.steps count term) type := + match count with + | 0 => ⟨typing, .refl typing.origin⟩ + | count + 1 => + let first := typing.betaStep + let rest := first.1.betaSteps count + ⟨rest.1, first.2.trans rest.2⟩ + +def SynthesisBetaTyping.betaPrefix {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {head type : AExpr β} {arguments : List (AExpr β)} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (head.appN arguments) type) + (count : Nat) (leading : count ≤ head.lambdaDepth) (supplied : count ≤ arguments.length) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context + (AExpr.betaPrefix count head arguments) type × + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (head.appN arguments) (AExpr.betaPrefix count head arguments) type := by + simpa only [BetaSyntax.steps_betaPrefix count head arguments leading supplied] using typing.betaSteps count + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SynthesisShapes.lean b/Ix/Kernel/Verify/Consistency/SynthesisShapes.lean new file mode 100644 index 000000000..552134c4a --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SynthesisShapes.lean @@ -0,0 +1,424 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.SynthesisSource + +/-! Recover function-type children, lambda bodies, and application spines +from complete retained derivations, including after let substitution. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- The domain and codomain retain their checking origins after interface +growth, local insertion, and dependent term substitution. -/ +abbrev SynthesisRetainedForall {β : Type u} (resolve : Address → Option (ConstRef β)) + (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) + (entries : Model.Environment β) (context : Model.Context β) + (condition : Certified.PropWhen) (domain body : AExpr β) := + SynthesisBetaTyping.ForallView (resolve := resolve) (incoming := incoming) + (incomingContext := incomingContext) (incomingBounds := incomingBounds) + (entries := entries) context condition domain body + +/-- A function-type check at the caller's current inference context. -/ +abbrev SynthesisForallBodyCheck {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (context : Model.Context β) (bounds : List VLevel) + (condition : Certified.PropWhen) (domain body : AExpr β) := + SynthesisRetainedForall resolve entries context bounds entries context condition domain body + +private def ForallInferenceTrace.bodyCheck {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} + {fuel : Nat} {before : TcState .anon} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body : KExpr .anon} {A B : AExpr β} {domainBound bodyBound : VLevel} + (trace : ForallInferenceTrace fuel before name bi domain body) + (opening : BinderOpeningSupport trace.domainState body) + (domainTree : SynthesisInference resolve entries locals context bounds fuel before domain + A (.sort (readLevel trace.domainLevel)) domainBound) + (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) + (readLevel trace.domainLevel :: bounds) fuel trace.openedState trace.opened + B (.sort (readLevel trace.bodyLevel)) bodyBound) + (agreement : LocalContextReading resolve locals before.lctx context) + (domainReading : readScopedExpr? resolve locals domain = some A.erase) + (bodyReading : readScopedExpr? resolve locals body 1 = some B.erase) : + SynthesisForallBodyCheck resolve entries context bounds + (Certified.zeroCondition (readLevel trace.bodyLevel)) A B := by + have opened := openBinder_sound opening (agreement.congr trace.contextPreserved.symm) + (trace.absent agreement) domainReading bodyReading trace.openRun + exact { + domainLevel := readLevel trace.domainLevel + bodyLevel := readLevel trace.bodyLevel + domainCheck := domainTree.betaTyping .current agreement domainReading trace.domainRun + bodyCheck := bodyTree.betaTyping (.push .current domainTree agreement domainReading trace.domainRun) + opened.2.2.1 opened.2.1 trace.bodyRun + conditionAgrees := rfl } + +def BinderInference.forallBodyCheck {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} + {condition : Certified.PropWhen} {domain body type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source + (.forallE condition domain body) type) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some (AExpr.forallE condition domain body).erase) : + SynthesisForallBodyCheck resolve entries context bounds condition domain body := by + cases support with + | forallE miss trace opening domainTree bodyTree => + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading + exact trace.bodyCheck opening (.known domainTree (.sort _)) (.known bodyTree (.sort _)) + (miss.localContext.symm ▸ agreement) domainReads bodyReads + +/-- Function-type children come from the complete retained derivation, +including a function type exposed by dependent substitution. -/ +def SynthesisInference.forallBodyCheck {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} + {condition : Certified.PropWhen} {domain body type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source + (.forallE condition domain body) type level) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some (AExpr.forallE condition domain body).erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisForallBodyCheck resolve entries context bounds condition domain body := + (support.betaTyping .current agreement reading accepted).forallView rfl + +def SynthesisRetainedCheck.forallBody {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} + {condition : Certified.PropWhen} {domain body : AExpr β} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) + (same : term = .forallE condition domain body) : + SynthesisRetainedForall resolve incoming incomingContext incomingBounds entries context condition domain body := + check.betaTyping.forallView same + +private theorem list_reverse_induction {α : Type u} {motive : List α → Prop} + (nil : motive []) + (append_singleton : ∀ tail last, motive tail → motive (tail ++ [last])) + (values : List α) : motive values := by + have reversed : ∀ items : List α, motive items.reverse := by + intro items + induction items with + | nil => exact nil + | cons item items ih => + simpa only [List.reverse_cons] using append_singleton items.reverse item ih + simpa using reversed values.reverse + +private theorem not_variable_spine {β : Type u} {term : AExpr β} + (notApp : ∀ fn arg, term ≠ .app fn arg) (notVar : ∀ index, term ≠ .bvar index) + (index : Nat) (arguments : List (AExpr β)) : term ≠ (AExpr.bvar index).appN arguments := by + intro same + induction arguments using list_reverse_induction with + | nil => exact notVar index same + | append_singleton arguments argument ih => + exact notApp _ _ (by simpa only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] using same) + +private theorem bvar_variable_spine {β : Type u} {left right : Nat} {arguments : List (AExpr β)} + (same : AExpr.bvar left = (AExpr.bvar right).appN arguments) : arguments = [] ∧ left = right := by + induction arguments using list_reverse_induction with + | nil => exact ⟨rfl, AExpr.bvar.inj same⟩ + | append_singleton arguments argument ih => + simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] at same + cases same + +private theorem app_variable_spine {β : Type u} {fn arg : AExpr β} {index : Nat} + {arguments : List (AExpr β)} (same : fn.app arg = (AExpr.bvar index).appN arguments) : + arguments = arguments.dropLast ++ [arg] ∧ fn = (AExpr.bvar index).appN arguments.dropLast := by + induction arguments using list_reverse_induction with + | nil => cases same + | append_singleton arguments argument ih => + simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil, AExpr.app.injEq] at same + simp only [List.dropLast_concat] + exact ⟨by rw [same.2], same.1⟩ + +/-- Extract the actual argument calls from a checked variable application. +No type check on the generated substitutions is added. -/ +def BinderInference.variableSpineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source term type) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (index : Nat) (arguments : List (AExpr β)) (headEquals : term = (AExpr.bvar index).appN arguments) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries context index arguments type := + match support with + | .fvar _ _ atIndex => by + obtain ⟨rfl, rfl⟩ := bvar_variable_spine headEquals + exact ⟨_, atIndex, .nil _⟩ + | .app _ miss trace functionTree head argumentTree conditions hashPath comparisonFaithful + _ _ _ _ _ _ => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have parts := app_variable_spine headEquals + have prior := functionTree.variableSpineOrigin (incoming := incoming) (incomingContext := incomingContext) + (incomingBounds := incomingBounds) keyedAgreement functionReading index arguments.dropLast parts.2 + have spine := prior.spine.snoc (.source (.binderArgument trace functionTree head argumentTree + keyedAgreement functionReading argumentReading conditions hashPath comparisonFaithful)) + exact ⟨prior.headType, prior.atIndex, by simpa only [← parts.1] using spine⟩ + | .sort .. | .cachedSort .. | .const .. | .polymorphic .. | .cachedConst .. | .forallE .. | .lam .. => by + exact False.elim (not_variable_spine (by intro fn arg same; cases same) + (by intro index same; cases same) index arguments headEquals) +termination_by structural support + + +def SynthesisInference.variableSpineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) + (index : Nat) (arguments : List (AExpr β)) (same : term = (AExpr.bvar index).appN arguments) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries context index arguments type := + (support.betaTyping contextOrigin agreement reading accepted).variableSpineOrigin index arguments same + +def SynthesisRetainedCheck.variableSpineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) + (index : Nat) (arguments : List (AExpr β)) (same : term = (AExpr.bvar index).appN arguments) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries context index arguments type := + check.betaTyping.variableSpineOrigin index arguments same + +def SynthesisForallBodyCheck.variableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {condition : Certified.PropWhen} {domain : AExpr β} + {index : Nat} {arguments : List (AExpr β)} + (check : SynthesisForallBodyCheck resolve entries context bounds condition domain + ((AExpr.bvar index).appN arguments)) + (parentOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries + (context.push domain) index arguments (.sort check.bodyLevel) := + (check.bodyCheck.variableSpineOrigin index arguments rfl).rebase parentOrigin + +/-- Keep the actual variable-application checks inside a lambda body. +The body's inferred type is retained separately from the lambda's eventual +codomain, which synthesis may obtain by reducing that type. -/ +def BinderInference.lambdaBodyVariableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel index : Nat} + {before : TcState .anon} {source : KExpr .anon} {domain type : AExpr β} + {condition : Certified.PropWhen} {arguments : List (AExpr β)} + (support : BinderInference resolve entries locals context fuel before source + (.lam condition domain ((AExpr.bvar index).appN arguments)) type) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = + some (AExpr.lam condition domain ((AExpr.bvar index).appN arguments)).erase) : + Σ resultType, SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries (context.push domain) index arguments resultType := by + cases support with + | lam full miss trace opening bodyTree constructed bound coherent closingFaithful faithful => + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading + have domainAgreement := (miss.localContext.symm ▸ agreement).congr trace.contextPreserved.symm + obtain ⟨_, openedReads, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReads bodyReads trace.openRun + exact ⟨_, bodyTree.variableSpineOrigin openedAgreement openedReads index arguments rfl⟩ + +def SynthesisInference.lambdaBodyVariableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel index : Nat} + {before after : TcState .anon} {source result : KExpr .anon} {domain type : AExpr β} + {condition : Certified.PropWhen} {arguments : List (AExpr β)} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source + (.lam condition domain ((AExpr.bvar index).appN arguments)) type level) + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = + some (AExpr.lam condition domain ((AExpr.bvar index).appN arguments)).erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + Σ resultType, SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries (context.push domain) index arguments resultType := + (support.betaTyping contextOrigin agreement reading accepted).lambdaBodyVariableSpine rfl + +def SynthesisRetainedCheck.lambdaBodyVariableSpine {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type domain : AExpr β} {level : VLevel} + {condition : Certified.PropWhen} {index : Nat} {arguments : List (AExpr β)} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) + (same : term = .lam condition domain ((AExpr.bvar index).appN arguments)) : + Σ resultType, SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries (context.push domain) index arguments resultType := + check.betaTyping.lambdaBodyVariableSpine same + +theorem BinderInference.lambdaPrefix {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source term type) : + LambdaPrefix term type term.lambdaDepth := by + induction support with + | lam _ _ _ _ _ _ _ _ _ _ ih => exact .lam ih + | _ => exact .zero _ _ + +theorem SynthesisInference.lambdaPrefix {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + LambdaPrefix term type term.lambdaDepth := + (support.betaTyping .current agreement reading accepted).lambdaPrefix + +theorem SynthesisRetainedCheck.lambdaPrefix {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) : + LambdaPrefix term type term.lambdaDepth := + check.betaTyping.lambdaPrefix + +def SynthesisRetainedCheck.origin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type := + match check with + | .source contextOrigin tree agreement reading accepted => + .source (.checked contextOrigin tree agreement reading accepted) + | .extend prior extension => prior.origin.extend extension + | .weakenAt prior insertion => .weakenAt prior.origin insertion + | .rebase origin prior => .rebase origin prior.origin +termination_by structural check + +def SynthesisRetainedCheck.typeOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context type (.sort level) := + match check with + | .source contextOrigin tree agreement reading accepted => + .inferredType contextOrigin tree agreement reading accepted + | .extend prior extension => prior.typeOrigin.extend extension + | .weakenAt prior insertion => .weakenAt prior.typeOrigin insertion + | .rebase origin prior => .rebase origin prior.typeOrigin +termination_by structural check + + +private theorem appN_last {β : Type u} {head : AExpr β} {arguments : List (AExpr β)} + (nonempty : arguments ≠ []) : + head.appN arguments = (head.appN arguments.dropLast).app (arguments.getLast nonempty) := by + calc + head.appN arguments = head.appN (arguments.dropLast ++ [arguments.getLast nonempty]) := + congrArg (AExpr.appN head) (List.dropLast_concat_getLast nonempty).symm + _ = _ := by simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] + +private theorem nonapp_spine_empty {β : Type u} {term head : AExpr β} {arguments : List (AExpr β)} + (notApp : ∀ fn arg, term ≠ .app fn arg) (same : term = head.appN arguments) : arguments = [] := by + by_contra nonempty + exact notApp _ _ (same.trans (appN_last nonempty)) + +private theorem app_spine_parts {β : Type u} {fn arg head : AExpr β} {arguments : List (AExpr β)} + (nonempty : arguments ≠ []) (same : fn.app arg = head.appN arguments) : + arguments = arguments.dropLast ++ [arg] ∧ fn = head.appN arguments.dropLast := by + have parts := AExpr.app.inj (same.trans (appN_last nonempty)) + exact ⟨by rw [parts.2]; exact (List.dropLast_concat_getLast nonempty).symm, parts.1⟩ + +def BinderInference.spineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source term type) + (checked : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (head : AExpr β) (arguments : List (AExpr β)) (same : term = head.appN arguments) : + SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := + if empty : arguments = [] then by + subst arguments + simp only [AExpr.appN_nil] at same + subst head + exact ⟨type, .source checked, support.lambdaPrefix, .nil _⟩ + else match support with + | .app _ miss trace functionTree functionHead argumentTree conditions hashPath comparisonFaithful + _ _ _ _ _ _ => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have parts := app_spine_parts empty same + have prior := functionTree.spineOrigin (incoming := incoming) (incomingContext := incomingContext) + (incomingBounds := incomingBounds) + (.binderHead functionTree functionHead keyedAgreement functionReading trace.functionRun) + keyedAgreement functionReading head arguments.dropLast parts.2 + refine ⟨prior.headType, prior.headOrigin, prior.leading, ?_⟩ + simpa only [← parts.1] using prior.argumentsOrigin.snoc + (.source (.binderArgument trace functionTree functionHead argumentTree keyedAgreement + functionReading argumentReading conditions hashPath comparisonFaithful)) + | .sort .. | .cachedSort .. | .fvar .. | .const .. | .polymorphic .. | .cachedConst .. | .forallE .. | .lam .. => by + exact False.elim (empty (nonapp_spine_empty (by intro fn arg same; cases same) same)) +termination_by structural support + + +def SynthesisInference.spineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) + (head : AExpr β) (arguments : List (AExpr β)) (same : term = head.appN arguments) : + SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := + (support.betaTyping contextOrigin agreement reading accepted).spineOrigin head arguments same + +def SynthesisRetainedCheck.spineOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) + (head : AExpr β) (arguments : List (AExpr β)) (same : term = head.appN arguments) : + SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := + check.betaTyping.spineOrigin head arguments same + +def SynthesisSpineOrigin.betaTrace {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {head type : AExpr β} {arguments : List (AExpr β)} {count : Nat} + (origin : SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type) + (enough : count ≤ head.lambdaDepth) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (head.appN arguments) (AExpr.betaPrefix count head arguments) type := + .prefix origin.headOrigin (origin.leading.truncate enough) origin.argumentsOrigin + +theorem SynthesisHead.appN_head {β : Type u} {head : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisHead (head.appN arguments)) : SynthesisHead head := by + induction arguments generalizing head with + | nil => exact support + | cons argument arguments ih => + have applied := ih support + cases applied with + | app head => exact head + +private theorem BinderInference.no_lambda_spine {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} + {condition : Certified.PropWhen} {domain body type : AExpr β} {arguments : List (AExpr β)} + (support : BinderInference resolve entries locals context fuel before source + ((AExpr.lam condition domain body).appN arguments) type) + (nonempty : arguments ≠ []) : False := by + induction arguments using list_reverse_induction with + | nil => exact nonempty rfl + | append_singleton arguments argument ih => + simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] at support + cases support with + | app _ _ _ _ head => cases head.appN_head + +theorem BinderInference.lambdaSpineTyping {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source term type) + (typed : TypingClaim.{u,v} entries context term type) : + LambdaSpineTyping.{u,v} entries context term type := by + intro condition domain body arguments same + subst term + by_cases empty : arguments = [] + · subst arguments + exact ⟨type, typed, support.lambdaPrefix, .nil _⟩ + · exact False.elim (support.no_lambda_spine empty) + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SynthesisSource.lean b/Ix/Kernel/Verify/Consistency/SynthesisSource.lean new file mode 100644 index 000000000..0f9670e4b --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SynthesisSource.lean @@ -0,0 +1,216 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.SynthesisReduction +import Ix.Kernel.Verify.Consistency.SynthesisReading +import Ix.Kernel.Verify.Consistency.BinderMeaning + +/-! Recover the complete head-beta typing derivation from the source +inference tree. No checks of intermediate reduction results are supplied. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +def BinderInference.betaTyping {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {term type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source term type) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type → + LocalContextReading resolve locals before.lctx context → + readScopedExpr? resolve locals source = some term.erase → + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type := + match support with + | .sort .. | .cachedSort .. | .const .. | .polymorphic .. | .cachedConst .. => + fun origin _ _ => .atom origin (by constructor) + | .forallE miss trace opening domainTree bodyTree .. => fun origin agreement reading => by + obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_all_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm + obtain ⟨_, openedReading, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement (trace.absent keyedAgreement) + domainReading bodyReading trace.openRun + exact .forallE origin + (domainTree.betaTyping (.source (.binderType domainTree keyedAgreement domainReading trace.domainRun)) + keyedAgreement domainReading) + (bodyTree.betaTyping (.source (.binderType bodyTree openedAgreement openedReading trace.bodyRun)) + openedAgreement openedReading) rfl + | .fvar _ _ atIndex => fun origin _ _ => .bvar origin atIndex + | .app _ miss trace functionTree head argumentTree conditions hashPath comparisonFaithful _ _ _ _ _ _ => + fun _ agreement reading => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have argumentAgreement := keyedAgreement.congr trace.contextPreserved.symm + have functionTypeReads := + (BinderInference.synthesis.{u,u} functionTree head keyedAgreement functionReading trace.functionRun).1 + have argumentTypeReads := + (BinderInference.sound.{u,u} argumentTree argumentAgreement argumentReading trace.argumentRun).1 + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (argumentTypeReads.symm.trans + ((beq_readScopedExpr? comparisonFaithful hashPath).trans + (readScopedExpr?_all_parts functionTypeReads).1))) conditions + have functionOrigin := SynthesisTypingOrigin.source + (SynthesisCheckedOrigin.binderHead (incoming := incoming) (incomingContext := incomingContext) + (incomingBounds := incomingBounds) functionTree head keyedAgreement functionReading trace.functionRun) + have argumentOrigin := SynthesisTypingOrigin.source + (SynthesisCheckedOrigin.binderArgument (incoming := incoming) (incomingContext := incomingContext) + (incomingBounds := incomingBounds) trace functionTree head argumentTree keyedAgreement + functionReading argumentReading conditions hashPath comparisonFaithful) + exact .app (functionTree.betaTyping functionOrigin keyedAgreement functionReading) + (sameType ▸ argumentTree.betaTyping (sameType.symm ▸ argumentOrigin) argumentAgreement argumentReading) + | .lam _ miss trace opening bodyTree _ _ _ _ _ => fun origin agreement reading => by + obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_lam_parts reading + have domainAgreement := (miss.localContext.symm ▸ agreement).congr trace.contextPreserved.symm + obtain ⟨_, openedReading, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReading bodyReading trace.openRun + exact .lam origin (bodyTree.betaTyping (.lambdaBody origin) openedAgreement openedReading) +termination_by structural support + +mutual + +/-- Every currently supported source-inference branch supplies its own +beta derivation. Lambda inference's changed body type becomes a forward +conversion node, retaining the original body's complete checked structure. -/ +def SynthesisInference.betaTyping {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) : + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) → + LocalContextReading resolve locals before.lctx context → + readScopedExpr? resolve locals source = some term.erase → + RecM.infer source (methodsN fuel) before = .ok result after → + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type := + match support with + | .cached tree priorAgreement priorReading priorRun _ _ _ => fun contextOrigin _ _ _ => + tree.betaTyping contextOrigin priorAgreement priorReading priorRun + | .cachedFrom check _ _ => fun contextOrigin _ _ _ => + check.betaTyping.rebase contextOrigin + | node@(.known inference _) => fun contextOrigin agreement reading accepted => + inference.betaTyping (.source (.checked contextOrigin node agreement reading accepted)) agreement reading + | node@(.reuseType inference ..) => fun contextOrigin agreement reading accepted => + inference.betaTyping (.source (.checked contextOrigin node agreement reading accepted)) agreement reading + | node@(.fvar _ atIndex _) => fun contextOrigin agreement reading accepted => + .bvar (.source (.checked contextOrigin node agreement reading accepted)) atIndex + | .forallE miss trace opening domainTree bodyTree levelFaithful domainBound bodyBound coherent faithful => + fun contextOrigin agreement reading accepted => by + let node := SynthesisInference.forallE miss trace opening domainTree bodyTree levelFaithful + domainBound bodyBound coherent faithful + obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_all_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm + obtain ⟨_, openedReading, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement (trace.absent keyedAgreement) + domainReading bodyReading trace.openRun + exact .forallE (.source (.checked contextOrigin node agreement reading accepted)) + (domainTree.betaTyping contextOrigin keyedAgreement domainReading trace.domainRun) + (bodyTree.betaTyping (contextOrigin.push domainTree keyedAgreement domainReading trace.domainRun) + openedAgreement openedReading trace.bodyRun) rfl + | .app _ miss trace functionTree argumentTree conditions hashPath comparisonFaithful _ _ _ _ _ _ => + fun contextOrigin agreement reading _ => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have argumentAgreement := keyedAgreement.congr trace.contextPreserved.symm + have functionTypeReads := + functionTree.outputReading keyedAgreement functionReading trace.functionRun + have argumentTypeReads := + argumentTree.outputReading argumentAgreement argumentReading trace.argumentRun + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (argumentTypeReads.symm.trans + ((beq_readScopedExpr? comparisonFaithful hashPath).trans + (readScopedExpr?_all_parts functionTypeReads).1))) conditions + exact .app (functionTree.betaTyping contextOrigin keyedAgreement functionReading trace.functionRun) + (sameType ▸ argumentTree.betaTyping contextOrigin argumentAgreement argumentReading trace.argumentRun) + | .appBeta _ miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath + comparisonFaithful _ _ _ _ _ _ => + fun contextOrigin agreement reading _ => by + obtain ⟨functionReading, argumentReading⟩ := readScopedExpr?_app_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have argumentAgreement := keyedAgreement.congr trace.exposure_context.symm + have functionTypeReads := + functionTree.outputReading keyedAgreement functionReading trace.functionRun + have argumentTypeReads := + argumentTree.outputReading argumentAgreement argumentReading trace.argumentRun + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (argumentTypeReads.symm.trans + ((beq_readScopedExpr? comparisonFaithful hashPath).trans + (exposure.reading functionTypeReads exposureCoherent).1))) conditions + exact .app + (.convert (functionTree.betaTyping contextOrigin keyedAgreement functionReading trace.functionRun) + (.rebase contextOrigin reduction)) + (sameType ▸ argumentTree.betaTyping contextOrigin argumentAgreement argumentReading trace.argumentRun) + | .lam full miss trace opening domainTree bodyTree conditionAgrees constructed bound coherent + closingFaithful faithful => + fun contextOrigin agreement reading accepted => by + let node := SynthesisInference.lam full miss trace opening domainTree bodyTree conditionAgrees + constructed bound coherent closingFaithful faithful + obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm + obtain ⟨_, openedReading, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReading bodyReading trace.openRun + exact .lam (.source (.checked contextOrigin node agreement reading accepted)) + (bodyTree.betaTyping (contextOrigin.push domainTree keyedAgreement domainReading trace.domainRun) + openedAgreement openedReading trace.bodyRun) + | .lamBeta full miss trace opening domainTree bodyTree reductionOrigin reduction conditionAgrees + constructed bound closingFaithful faithful => + fun contextOrigin agreement reading accepted => by + let node := SynthesisInference.lamBeta full miss trace opening domainTree bodyTree reductionOrigin reduction + conditionAgrees constructed bound closingFaithful faithful + obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm + obtain ⟨_, openedReading, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement (trace.domainValid.freshReading domainAgreement) domainReading bodyReading trace.openRun + have inner := bodyTree.betaTyping + (contextOrigin.push domainTree keyedAgreement domainReading trace.domainRun) + openedAgreement openedReading trace.bodyRun + exact .lam (.source (.checked contextOrigin node agreement reading accepted)) + (.convert inner (.rebase contextOrigin (.origin reductionOrigin))) + | .letE _ localState miss trace opening domainTree valueTree bodyTree domainReading valueReading bodyReading + conditions hashPath comparisonFaithful _ reduction => fun contextOrigin agreement _ _ => by + have keyedValid := miss.keyedLocalState localState + have keyedAgreement := miss.localContext.symm ▸ agreement + have valueAgreement := keyedAgreement.congr (trace.domainContext keyedValid).symm + obtain ⟨openedReading, openedAgreement, _⟩ := + trace.opened_reading opening keyedValid keyedAgreement domainReading bodyReading + have valueTypeReading := valueTree.outputReading valueAgreement valueReading trace.valueRun + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (valueTypeReading.symm.trans + ((beq_readScopedExpr? comparisonFaithful hashPath).trans domainReading))) conditions + have bodyContext := SynthesisContext.push .current domainTree keyedAgreement domainReading trace.domainRun + have bodyTypeOrigin := SynthesisTypingOrigin.inferredType bodyContext bodyTree + openedAgreement openedReading trace.bodyRun + have valueOrigin := SynthesisTypingOrigin.source + (SynthesisCheckedOrigin.checked .current valueTree valueAgreement valueReading trace.valueRun) + have substitutedType := SynthesisTypingOrigin.substituteAt bodyTypeOrigin (sameType ▸ valueOrigin) + ContextSubstitution.root + exact .convert + ((bodyTree.betaTyping (contextOrigin.push domainTree keyedAgreement domainReading trace.domainRun) + openedAgreement openedReading trace.bodyRun).substituteAt + (sameType ▸ valueTree.betaTyping contextOrigin valueAgreement valueReading trace.valueRun) .root) + (.rebase contextOrigin (reduction.trace substitutedType)) +termination_by structural support + +def SynthesisRetainedCheck.betaTyping {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} + (check : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type := + match check with + | .source contextOrigin tree agreement reading accepted => + tree.betaTyping contextOrigin agreement reading accepted + | .extend prior extension => prior.betaTyping.extend extension + | .weakenAt prior insertion => prior.betaTyping.weakenAt insertion + | .rebase origin prior => prior.betaTyping.rebase origin +termination_by structural check + +end + + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SynthesisSupport.lean b/Ix/Kernel/Verify/Consistency/SynthesisSupport.lean new file mode 100644 index 000000000..7eab3943d --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SynthesisSupport.lean @@ -0,0 +1,1161 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.ContextTransport +import Ix.Kernel.Verify.Consistency.CheapBetaReading +import Ix.Kernel.Verify.Consistency.ApplicationWhnf +import Ix.Kernel.Verify.Consistency.LetInference +import Ix.Theory.Model.UniverseBounds +import Ix.Theory.Model.BetaSpine + +/-! +Inference with formation of the returned type. Local bounds come from the +executed checks of binder domains; external type bounds retain their actual +checking origin. Applications recover a codomain bound from an inhabited +product. Lambdas therefore synthesize full typing, including when they occur +directly in function position. + +The recorded level is an upper bound for the inferred type. It preserves the +exact zero condition, but its positive value need not be the sort the kernel +would infer on a separate call. No such extra call is assumed. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +mutual + +/-- Finite support for the actual production calls, enriched with the +origins and syntactic levels needed to derive formation. There are no +semantic typing or conversion fields. -/ +inductive SynthesisInference {β : Type u} + (resolve : Address → Option (ConstRef β)) : + Model.Environment β → List FVarId → Model.Context β → List VLevel → Nat → TcState .anon → KExpr .anon → + AExpr β → AExpr β → VLevel → Type u + | known {entries locals context bounds fuel before source term type level} + (inference : BinderInference resolve entries locals context fuel before source term type) + (formation : TypeFormation resolve entries context type level) : + SynthesisInference resolve entries locals context bounds fuel before source term type level + | cached {entries locals context bounds fuel before source term type level + priorLocals priorFuel priorBefore priorAfter priorResult} + (tree : SynthesisInference resolve entries priorLocals context bounds priorFuel priorBefore source term type level) + (priorAgreement : LocalContextReading resolve priorLocals priorBefore.lctx context) + (priorReading : readScopedExpr? resolve priorLocals source = some term.erase) + (priorRun : RecM.infer source (methodsN priorFuel) priorBefore = .ok priorResult priorAfter) + (hit : InferenceCacheHit before source) + (cacheMatch : hit.cached = priorResult) + (resultReading : readScopedExpr? resolve locals priorResult = some type.erase) : + SynthesisInference resolve entries locals context bounds fuel before source term type level + | cachedFrom {entries locals context bounds fuel before source term type level} + (check : SynthesisRetainedCheck resolve entries context bounds entries context term type level) + (hit : InferenceCacheHit before source) + (resultReading : readScopedExpr? resolve locals hit.cached = some type.erase) : + SynthesisInference resolve entries locals context bounds fuel before source term type level + | reuseType {earlier entries locals context bounds fuel before source term type + typeFuel typeBefore typeAfter typeSource typeResult declaredType level typeBound arguments} + (inference : BinderInference resolve entries locals context fuel before source term type) + (typeTree : SynthesisInference resolve earlier [] [] [] typeFuel typeBefore typeSource + declaredType (.sort level) typeBound) + (extension : InterfaceExtends earlier entries) + (typeReading : readScopedExpr? resolve [] typeSource = some declaredType.erase) + (typeRun : RecM.infer typeSource (methodsN typeFuel) typeBefore = .ok typeResult typeAfter) + (same : AExpr.LevelEquivalent (declaredType.instL arguments) type) : + SynthesisInference resolve entries locals context bounds fuel before source term type (level.inst arguments) + | fvar {entries locals context bounds fuel before source index type level} + (inference : BinderInference resolve entries locals context fuel before source (.bvar index) type) + (atIndex : context[index]? = some type) + (boundAtIndex : bounds[index]? = some level) : + SynthesisInference resolve entries locals context bounds fuel before source (.bvar index) type level + | app {entries locals context bounds fuel before fn arg info f a A A' B condition functionLevel argumentLevel} + (full : before.inferOnly = false) + (miss : UncachedInference before (.app fn arg info)) + (trace : ApplicationInferenceTrace fuel miss.keyed fn arg) + (functionTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed fn + f (.forallE condition A B) functionLevel) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState + arg a A' argumentLevel) + (conditions : A'.annotations = A.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) + (bodyConstructed : trace.codomain.Constructed) + (argConstructed : arg.Constructed) + (bodyBound : trace.codomain.size + 1 < UInt64.size) + (argBound : arg.size < UInt64.size) + (coherent : trace.comparedState.env.intern.WF) + (faithful : KExpr.CollisionFree fun term => trace.comparedState.env.intern.ExprSupport term ∨ + KExpr.SubstReach arg trace.codomain 0 term) : + SynthesisInference resolve entries locals context bounds (fuel + 1) before (.app fn arg info) + (.app f a) (B.inst a) (applicationLevel functionLevel condition) + | appBeta {entries locals context bounds fuel before fn arg info f a T A A' B condition + functionLevel argumentLevel reductionLevel} + (full : before.inferOnly = false) + (miss : UncachedInference before (.app fn arg info)) + (trace : ApplicationWhnfInferenceTrace fuel miss.keyed fn arg) + (functionTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed fn + f T functionLevel) + (exposure : BetaPiExposure resolve locals fuel trace.functionState trace.functionType T + condition A B trace.domain trace.codomain) + (exposureCoherent : trace.functionState.env.intern.WF) + (reduction : SynthesisBetaTrace resolve entries context bounds entries context + T (.forallE condition A B) (.sort reductionLevel)) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.exposedState + arg a A' argumentLevel) + (conditions : A'.annotations = A.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) + (bodyConstructed : trace.codomain.Constructed) + (argConstructed : arg.Constructed) + (bodyBound : trace.codomain.size + 1 < UInt64.size) + (argBound : arg.size < UInt64.size) + (coherent : trace.comparedState.env.intern.WF) + (faithful : KExpr.CollisionFree fun term => trace.comparedState.env.intern.ExprSupport term ∨ + KExpr.SubstReach arg trace.codomain 0 term) : + SynthesisInference resolve entries locals context bounds (fuel + 1) before (.app fn arg info) + (.app f a) (B.inst a) (applicationLevel reductionLevel condition) + | forallE {entries locals context bounds fuel before name bi domain body info A B domainBoundLevel bodyBoundLevel} + (miss : UncachedInference before (.all name bi domain body info)) + (trace : ForallInferenceTrace fuel miss.keyed name bi domain body) + (opening : BinderOpeningSupport trace.domainState body) + (domainTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed domain + A (.sort (readLevel trace.domainLevel)) domainBoundLevel) + (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) + (readLevel trace.domainLevel :: bounds) fuel trace.openedState trace.opened + B (.sort (readLevel trace.bodyLevel)) bodyBoundLevel) + (levelFaithful : ∀ a b, + (KUniv.Sub a trace.domainLevel ∨ KUniv.Sub a trace.bodyLevel) → + (KUniv.Sub b trace.domainLevel ∨ KUniv.Sub b trace.bodyLevel) → a.AddrFaithful b) + (domainBound : trace.domainLevel.size < UInt64.size) + (bodyBound : trace.bodyLevel.size < UInt64.size) + (coherent : trace.bodyState.env.intern.WF) + (faithful : KExpr.KeyCollisionFree fun term => trace.bodyState.env.intern.ExprSupport term ∨ + term = KExpr.mkSort (KUniv.mkIMax trace.domainLevel trace.bodyLevel)) : + SynthesisInference resolve entries locals context bounds (fuel + 1) before (.all name bi domain body info) + (.forallE (Certified.zeroCondition (readLevel trace.bodyLevel)) A B) + (.sort (readLevel (KUniv.mkIMax trace.domainLevel trace.bodyLevel))) + (.succ (readLevel (KUniv.mkIMax trace.domainLevel trace.bodyLevel))) + | lam {entries locals context bounds fuel before name bi domain body info A b B condition domainBoundLevel bodyLevel} + (full : before.inferOnly = false) + (miss : UncachedInference before (.lam name bi domain body info)) + (trace : LambdaInferenceTrace fuel miss.keyed name bi domain body) + (opening : BinderOpeningSupport trace.domainState body) + (domainTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed domain + A (.sort (readLevel trace.domainLevel)) domainBoundLevel) + (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) + (readLevel trace.domainLevel :: bounds) fuel trace.openedState trace.opened b B bodyLevel) + (conditionAgrees : condition = Certified.zeroCondition bodyLevel) + (constructed : trace.bodyType.Constructed) + (bound : trace.bodyType.size + 1 < UInt64.size) + (coherent : trace.bodyState.env.intern.WF) + (closingFaithful : KExpr.CollisionFree fun term => trace.bodyState.env.intern.ExprSupport term ∨ + KExpr.AbstractReach ((∅ : Std.HashMap FVarId UInt64).insert trace.fresh 0) + 1 trace.bodyType 0 term) + (faithful : KExpr.KeyCollisionFree fun term => trace.abstracted.2.ExprSupport term ∨ + term = KExpr.mkAll () () domain trace.abstracted.1) : + SynthesisInference resolve entries locals context bounds (fuel + 1) before (.lam name bi domain body info) + (.lam condition A b) (.forallE condition A B) (.imax (readLevel trace.domainLevel) bodyLevel) + | lamBeta {entries locals context bounds fuel before name bi domain body info A b condition + domainBoundLevel bodyLevel headCondition headDomain headBody arguments reducedLevel} + (full : before.inferOnly = false) + (miss : UncachedInference before (.lam name bi domain body info)) + (trace : LambdaBodyTrace fuel miss.keyed name bi domain body) + (opening : BinderOpeningSupport trace.domainState body) + (domainTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed domain + A (.sort (readLevel trace.domainLevel)) domainBoundLevel) + (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) + (readLevel trace.domainLevel :: bounds) fuel trace.openedState trace.opened b + ((AExpr.lam headCondition headDomain headBody).appN arguments) bodyLevel) + (origin : SynthesisReductionOrigin resolve entries context bounds entries (context.push A) + (.lam headCondition headDomain headBody) arguments (cheapBetaCount trace.bodyType) reducedLevel) + (reduction : CheapBetaSupport trace.bodyType trace.bodyState.env.intern) + (conditionAgrees : condition = Certified.zeroCondition reducedLevel) + (constructed : trace.reduced.1.Constructed) + (bound : trace.reduced.1.size + 1 < UInt64.size) + (closingFaithful : KExpr.CollisionFree fun term => trace.reduced.2.ExprSupport term ∨ + KExpr.AbstractReach ((∅ : Std.HashMap FVarId UInt64).insert trace.fresh 0) + 1 trace.reduced.1 0 term) + (faithful : KExpr.KeyCollisionFree fun term => trace.abstracted.2.ExprSupport term ∨ + term = KExpr.mkAll () () domain trace.abstracted.1) : + SynthesisInference resolve entries locals context bounds (fuel + 1) before (.lam name bi domain body info) + (.lam condition A b) + (.forallE condition A + (AExpr.betaPrefix (cheapBetaCount trace.bodyType) (.lam headCondition headDomain headBody) arguments)) + (.imax (readLevel trace.domainLevel) reducedLevel) + + | letE {entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level domainBound valueLevel valueType} + (full : before.inferOnly = false) + (localState : LocalStateInvariant before) + (miss : UncachedInference before (.letE name domain value body nonDep info)) + (trace : LetInferenceTrace fuel miss.keyed name domain value body) + (opening : BinderOpeningSupport trace.comparedState body) + (domainTree : SynthesisInference resolve entries locals context bounds fuel miss.keyed domain + A (.sort (readLevel trace.domainLevel)) domainBound) + (valueTree : SynthesisInference resolve entries locals context bounds fuel trace.domainState value + val valueType valueLevel) + (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) + (readLevel trace.domainLevel :: bounds) fuel trace.openedState trace.opened b B level) + (domainReading : readScopedExpr? resolve locals domain = some A.erase) + (valueReading : readScopedExpr? resolve locals value = some val.erase) + (bodyReading : readScopedExpr? resolve locals body 1 = some b.erase) + (conditions : valueType.annotations = A.annotations) + (hashPath : (trace.valueType == domain) = true) + (comparisonFaithful : trace.valueType.AddrFaithful domain) + (substitution : trace.SubstitutionSupport) + (reduction : LetTypeReduction resolve entries context bounds trace.substituted.1 trace.substituted.2 + level (B.inst val) resultType) : + SynthesisInference resolve entries locals context bounds (fuel + 1) before + (.letE name domain value body nonDep info) (b.inst val) resultType level + +/-- A complete executed inference check transported to a later use site. +The source tree remains available beneath interface and context changes; +none of these constructors accepts semantic typing or conversion evidence. -/ +inductive SynthesisRetainedCheck {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → + Model.Environment β → Model.Context β → AExpr β → AExpr β → VLevel → Type u + | source {incoming incomingContext incomingBounds entries context bounds locals fuel before after + source result term type level} + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level + | extend {incoming incomingContext incomingBounds earlier entries context term type level} + (prior : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds earlier context term type level) + (extension : InterfaceExtends earlier entries) : + SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level + | weakenAt {incoming incomingContext incomingBounds entries source target cutoff term type level} + (prior : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries source term type level) + (insertion : ContextInsertion source target cutoff) : + SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries target + (term.liftN 1 cutoff) (type.liftN 1 cutoff) level + | rebase {incoming incomingContext incomingBounds middle middleContext middleBounds entries context term type level} + (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) + (prior : SynthesisRetainedCheck resolve middle middleContext middleBounds entries context term type level) : + SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context term type level + +/-- Contexts of retained checking origins come from the incoming context, +the empty context, or actual checks of their binder domains. This records +earlier checks even when a generated type is used several binders later. -/ +inductive SynthesisContext {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → + Model.Environment β → Model.Context β → List VLevel → Type u + | current {entries context bounds} : + SynthesisContext resolve entries context bounds entries context bounds + | empty {entries context bounds} (earlier : Model.Environment β) : + SynthesisContext resolve entries context bounds earlier [] [] + | push {entries context bounds earlier priorContext priorBounds locals fuel before after source result + domain level bound} + (prior : SynthesisContext resolve entries context bounds earlier priorContext priorBounds) + (domainTree : SynthesisInference resolve earlier locals priorContext priorBounds fuel before source + domain (.sort level) bound) + (agreement : LocalContextReading resolve locals before.lctx priorContext) + (reading : readScopedExpr? resolve locals source = some domain.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisContext resolve entries context bounds earlier (priorContext.push domain) (level :: priorBounds) + | extend {entries context bounds earlier later priorContext priorBounds} + (prior : SynthesisContext resolve entries context bounds earlier priorContext priorBounds) + (extension : InterfaceExtends earlier later) : + SynthesisContext resolve entries context bounds later priorContext priorBounds + | compose {entries context bounds middle middleContext middleBounds earlier priorContext priorBounds} + (prior : SynthesisContext resolve entries context bounds middle middleContext middleBounds) + (next : SynthesisContext resolve middle middleContext middleBounds earlier priorContext priorBounds) : + SynthesisContext resolve entries context bounds earlier priorContext priorBounds + +/-- A checking origin may also cross a dependent term substitution. The +substituted argument retains its actual inference call, whose typing is +derived in the same recursion as the enclosing lambda. -/ +inductive SynthesisTypeTransport {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → + Model.Environment β → Model.Context β → AExpr β → AExpr β → VLevel → + Model.Environment β → Model.Context β → AExpr β → AExpr β → VLevel → Type u + | pure {incoming incomingContext incomingBounds origin originContext source reduced level + entries context current result bound} + (transport : TypeReductionTransport origin originContext source reduced level + entries context current result bound) : + SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries context current result bound + | map {incoming incomingContext incomingBounds origin originContext source reduced level + middle middleContext current result bound entries context current' result' bound'} + (prior : SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level middle middleContext current result bound) + (transport : TypeReductionTransport middle middleContext current result bound + entries context current' result' bound') : + SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries context current' result' bound' + | substituteAt {incoming incomingContext incomingBounds origin originContext source reduced level + entries base sourceContext targetContext current result bound domain argument cutoff} + (prior : SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries sourceContext current result bound) + (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds + entries base argument domain) + (substitution : ContextSubstitution base domain argument sourceContext targetContext cutoff) : + SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries targetContext + (current.inst argument cutoff) (result.inst argument cutoff) bound + +/-- Internal typing origins for generated expressions. Leaves are actual +source inference calls. Application, conversion, finite beta traces, and +context substitutions retain those calls, including when an argument crosses +still-open parameters of an earlier type. No semantic typing field is accepted. -/ +inductive SynthesisTypingOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → + Model.Environment β → Model.Context β → AExpr β → AExpr β → Type u + | source {incoming incomingContext incomingBounds entries context term type} + (check : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type + | inferredType {incoming incomingContext incomingBounds entries context bounds locals fuel before after source result + term type level} + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context type (.sort level) + | lambdaBody {incoming incomingContext incomingBounds entries context condition domain body codomain} + (check : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context + (.lam condition domain body) (.forallE condition domain codomain)) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries (context.push domain) body codomain + | application {incoming incomingContext incomingBounds entries context function argument condition domain body} + (functionOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context + function (.forallE condition domain body)) + (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context argument domain) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context + (.app function argument) (body.inst argument) + | reduced {incoming incomingContext incomingBounds entries context source result type} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context result type + | convert {incoming incomingContext incomingBounds entries context term sourceType resultType level} + (value : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term sourceType) + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context sourceType resultType (.sort level)) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term resultType + | weaken {incoming incomingContext incomingBounds entries context term type} + (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) + (domain : AExpr β) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries (context.push domain) + (term.liftN 1) (type.liftN 1) + | weakenAt {incoming incomingContext incomingBounds entries source target cutoff term type} + (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries source term type) + (insertion : ContextInsertion source target cutoff) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries target + (term.liftN 1 cutoff) (type.liftN 1 cutoff) + | instantiate {incoming incomingContext incomingBounds entries context term type} + (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) + (arguments : List VLevel) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds + entries (context.map (AExpr.instL arguments)) (term.instL arguments) (type.instL arguments) + | appendContext {incoming incomingContext incomingBounds entries context term type} + (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) + (outer : Model.Context β) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries (context ++ outer) term type + | extend {incoming incomingContext incomingBounds earlier entries context term type} + (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds earlier context term type) + (extension : InterfaceExtends earlier entries) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type + | rebase {incoming incomingContext incomingBounds middle middleContext middleBounds entries context term type} + (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) + (prior : SynthesisTypingOrigin resolve middle middleContext middleBounds entries context term type) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type + | termEquivalent {incoming incomingContext incomingBounds entries context term term' type} + (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) + (same : AExpr.LevelEquivalent term term') : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term' type + | typeEquivalent {incoming incomingContext incomingBounds entries context term type type'} + (prior : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) + (same : AExpr.LevelEquivalent type type') : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type' + | substituteAt {incoming incomingContext incomingBounds entries base sourceContext targetContext + domain term type argument cutoff} + (body : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds + entries sourceContext term type) + (value : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries base argument domain) + (substitution : ContextSubstitution base domain argument sourceContext targetContext cutoff) : + SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries targetContext + (term.inst argument cutoff) (type.inst argument cutoff) + +/-- Actual inference calls retain their source's syntactic lambda domains +and argument checks. Application arguments additionally retain the comparison with +the function's domain, before any later transport of this checking origin. -/ +inductive SynthesisCheckedOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → + Model.Environment β → Model.Context β → AExpr β → AExpr β → Type u + | checked {incoming incomingContext incomingBounds entries context bounds locals fuel before after source result + term type level} + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type + | binderHead {incoming incomingContext incomingBounds entries context locals fuel before after source result term type} + (tree : BinderInference resolve entries locals context fuel before source term type) + (head : SynthesisHead term) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term type + | binderType {incoming incomingContext incomingBounds entries context locals fuel before after source result term level} + (tree : BinderInference resolve entries locals context fuel before source term (.sort level)) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : + SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context term (.sort level) + | applicationArgument {incoming incomingContext incomingBounds entries context bounds locals fuel before fn arg + f a domain argumentType body condition functionLevel argumentLevel} + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (trace : ApplicationInferenceTrace fuel before fn arg) + (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn + f (.forallE condition domain body) functionLevel) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState arg + a argumentType argumentLevel) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals arg = some a.erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) : + SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context a domain + | applicationBetaArgument {incoming incomingContext incomingBounds entries context bounds locals fuel before fn arg + f a T domain argumentType body condition functionLevel argumentLevel} + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (trace : ApplicationWhnfInferenceTrace fuel before fn arg) + (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn f T functionLevel) + (exposure : BetaPiExposure resolve locals fuel trace.functionState trace.functionType T + condition domain body trace.domain trace.codomain) + (exposureCoherent : trace.functionState.env.intern.WF) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.exposedState arg + a argumentType argumentLevel) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals arg = some a.erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) : + SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context a domain + | binderArgument {incoming incomingContext incomingBounds entries context locals fuel before fn arg + f a domain argumentType body condition} + (trace : ApplicationInferenceTrace fuel before fn arg) + (functionTree : BinderInference resolve entries locals context fuel before fn + f (.forallE condition domain body)) + (head : SynthesisHead f) + (argumentTree : BinderInference resolve entries locals context fuel trace.functionState arg a argumentType) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals arg = some a.erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) : + SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries context a domain + +/-- The argument checks along an actual application spine, retained in +application order so later substitutions can update each dependent type. -/ +inductive SynthesisArgumentSpineOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → + Model.Environment β → Model.Context β → AExpr β → List (AExpr β) → AExpr β → Type u + | nil {incoming incomingContext incomingBounds entries context} (type : AExpr β) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context type [] type + | snoc {incoming incomingContext incomingBounds entries context start arguments condition domain body argument} + (prior : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context + start arguments (.forallE condition domain body)) + (checked : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context argument domain) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context + start (arguments ++ [argument]) (body.inst argument) + | convert {incoming incomingContext incomingBounds entries context start arguments source target level} + (prior : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context + start arguments source) + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source target (.sort level)) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context start arguments target + +/-- A beta reduction can come from an earlier checked lambda prefix, or +from an actual lambda or lambda application substituted for a checked +variable head. Both cases retain the checks of all applied arguments. -/ +inductive SynthesisReductionOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → + Model.Environment β → Model.Context β → AExpr β → List (AExpr β) → Nat → VLevel → Type u + | traced {incoming incomingContext incomingBounds entries context head arguments count level} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (head.appN arguments) (AExpr.betaPrefix count head arguments) (.sort level)) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries context head arguments count level + | checked {incoming incomingContext incomingBounds entries context head arguments count level + earlier typeLocals typeContext typeBounds typeFuel typeBefore typeAfter typeSource typeResult + originCondition originDomain originBody originArguments originLevel originBound} + (typeContextSupport : SynthesisContext resolve incoming incomingContext incomingBounds + earlier typeContext typeBounds) + (typeTree : SynthesisInference resolve earlier typeLocals typeContext typeBounds typeFuel typeBefore + typeSource ((AExpr.lam originCondition originDomain originBody).appN originArguments) + (.sort originLevel) originBound) + (typeAgreement : LocalContextReading resolve typeLocals typeBefore.lctx typeContext) + (typeReading : readScopedExpr? resolve typeLocals typeSource = + some ((AExpr.lam originCondition originDomain originBody).appN originArguments).erase) + (typeRun : RecM.infer typeSource (methodsN typeFuel) typeBefore = .ok typeResult typeAfter) + (originPrefix : count ≤ originBody.lambdaDepth + 1) + (transport : SynthesisTypeTransport resolve incoming incomingContext incomingBounds earlier typeContext + ((AExpr.lam originCondition originDomain originBody).appN originArguments) + (AExpr.betaPrefix count (.lam originCondition originDomain originBody) originArguments) originLevel + entries context (head.appN arguments) (AExpr.betaPrefix count head arguments) level) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries context head arguments count level + | substitutedVariable {incoming incomingContext incomingBounds entries base source target + type domain argument arguments cutoff count level} + (spine : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries source type arguments (.sort level)) + (atIndex : source[cutoff]? = some type) + (checked : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries base argument domain) + (substitution : ContextSubstitution base domain argument source target cutoff) + (enough : count ≤ argument.lambdaDepth) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries target + (argument.liftN cutoff) (arguments.map (AExpr.inst · argument cutoff)) count level + | substitutedApplication {incoming incomingContext incomingBounds entries base source target + type domain binder body condition initialArguments arguments cutoff count level} + (spine : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries source type arguments (.sort level)) + (atIndex : source[cutoff]? = some type) + (checked : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries base + ((AExpr.lam condition binder body).appN initialArguments) domain) + (substitution : ContextSubstitution base domain + ((AExpr.lam condition binder body).appN initialArguments) source target cutoff) + (enough : count ≤ body.lambdaDepth + 1) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries target + ((AExpr.lam condition binder body).liftN cutoff) + (initialArguments.map (AExpr.liftN cutoff ·) ++ + arguments.map (AExpr.inst · ((AExpr.lam condition binder body).appN initialArguments) cutoff)) count level + | substitutedResult {incoming incomingContext incomingBounds entries base source target + type domain binder body condition initialArguments arguments cutoff count resultType level} + (spine : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries source type arguments resultType) + (atIndex : source[cutoff]? = some type) + (checked : SynthesisCheckedOrigin resolve incoming incomingContext incomingBounds entries base + ((AExpr.lam condition binder body).appN initialArguments) domain) + (substitution : ContextSubstitution base domain + ((AExpr.lam condition binder body).appN initialArguments) source target cutoff) + (enough : count ≤ body.lambdaDepth + 1) + (sourceOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries target + (((AExpr.lam condition binder body).liftN cutoff).appN + (initialArguments.map (AExpr.liftN cutoff ·) ++ + arguments.map (AExpr.inst · ((AExpr.lam condition binder body).appN initialArguments) cutoff))) + (.sort level)) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries target + ((AExpr.lam condition binder body).liftN cutoff) + (initialArguments.map (AExpr.liftN cutoff ·) ++ + arguments.map (AExpr.inst · ((AExpr.lam condition binder body).appN initialArguments) cutoff)) count level + | map {incoming incomingContext incomingBounds earlier priorContext head arguments count bound + entries context current currentArguments level} + (prior : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds + earlier priorContext head arguments count bound) + (transport : SynthesisTypeTransport resolve incoming incomingContext incomingBounds earlier priorContext + (head.appN arguments) (AExpr.betaPrefix count head arguments) bound entries context + (current.appN currentArguments) (AExpr.betaPrefix count current currentArguments) level) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds + entries context current currentArguments count level + +/-- Finite beta traces retain the actual checks behind each lambda and +argument. Results can supply later typing origins, so composition never +requires an inference call on an intermediate expression. Types at adjacent +steps may differ; the trace preserves the type retained by its first step. -/ +inductive SynthesisBetaTrace {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → + Model.Environment β → Model.Context β → AExpr β → AExpr β → AExpr β → Type u + | refl {incoming incomingContext incomingBounds entries context term type} + (origin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term term type + | prefix {incoming incomingContext incomingBounds entries context head headType arguments count type} + (headOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context head headType) + (leading : LambdaPrefix head headType count) + (argumentsOrigin : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries context headType arguments type) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (head.appN arguments) (AExpr.betaPrefix count head arguments) type + | origin {incoming incomingContext incomingBounds entries context head arguments count level} + (retained : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds + entries context head arguments count level) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (head.appN arguments) (AExpr.betaPrefix count head arguments) (.sort level) + | trans {incoming incomingContext incomingBounds entries context source middle result type otherType} + (prior : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source middle type) + (next : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context middle result otherType) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type + | atType {incoming incomingContext incomingBounds entries context source result type otherType} + (sourceOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context source type) + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result otherType) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type + | application {incoming incomingContext incomingBounds entries context source result argument condition domain body} + (functionTrace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + source result (.forallE condition domain body)) + (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context argument domain) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (.app source argument) (.app result argument) (body.inst argument) + | argument {incoming incomingContext incomingBounds entries context function source result condition domain body otherType} + (functionOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context + function (.forallE condition domain body)) + (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context source domain) + (argumentTrace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result otherType) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (.app function source) (.app function result) (body.inst source) + | substituteAt {incoming incomingContext incomingBounds entries base sourceContext targetContext + domain source result type argument cutoff} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries sourceContext source result type) + (value : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries base argument domain) + (substitution : ContextSubstitution base domain argument sourceContext targetContext cutoff) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries targetContext + (source.inst argument cutoff) (result.inst argument cutoff) (type.inst argument cutoff) + | weakenAt {incoming incomingContext incomingBounds entries sourceContext targetContext cutoff source result type} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries sourceContext source result type) + (insertion : ContextInsertion sourceContext targetContext cutoff) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries targetContext + (source.liftN 1 cutoff) (result.liftN 1 cutoff) (type.liftN 1 cutoff) + | rebase {incoming incomingContext incomingBounds middle middleContext middleBounds entries context source result type} + (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) + (trace : SynthesisBetaTrace resolve middle middleContext middleBounds entries context source result type) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type + | convertType {incoming incomingContext incomingBounds entries context source result sourceType targetType level} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result sourceType) + (typeTrace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + sourceType targetType (.sort level)) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result targetType + | instantiate {incoming incomingContext incomingBounds entries context source result type} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type) + (arguments : List VLevel) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries (context.map (AExpr.instL arguments)) + (source.instL arguments) (result.instL arguments) (type.instL arguments) + | appendContext {incoming incomingContext incomingBounds entries context source result type} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type) + (outer : Model.Context β) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries (context ++ outer) source result type + | extend {incoming incomingContext incomingBounds earlier entries context source result type} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds earlier context source result type) + (extension : InterfaceExtends earlier entries) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type + +/-- The actual final cheap-beta choice, with an original checking origin +for a selected reduction. The unchanged case needs only the planner result. -/ +inductive LetTypeReduction {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → Model.Context β → List VLevel → KExpr .anon → InternTable .anon → VLevel → + AExpr β → AExpr β → Type u + | unchanged {entries context bounds source table level term} + (plan : cheapBetaPlan? source = none) : + LetTypeReduction resolve entries context bounds source table level term term + | beta {entries context bounds source table level condition domain body arguments} + (support : CheapBetaSupport source table) + (origin : SynthesisBetaTrace resolve entries context bounds entries context + ((AExpr.lam condition domain body).appN arguments) + (AExpr.betaPrefix (cheapBetaCount source) (.lam condition domain body) arguments) (.sort level)) : + LetTypeReduction resolve entries context bounds source table level + ((AExpr.lam condition domain body).appN arguments) + (AExpr.betaPrefix (cheapBetaCount source) (.lam condition domain body) arguments) + +end + +theorem LetTypeReduction.reading {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} + {locals : List FVarId} {source : KExpr .anon} {table : InternTable .anon} + {level : VLevel} {term result : AExpr β} + (reduction : LetTypeReduction resolve entries context bounds source table level term result) + (coherent : table.WF) + (reading : readScopedExpr? resolve locals source = some term.erase) : + readScopedExpr? resolve locals (cheapBetaReduce source table).1 = some result.erase ∧ + (cheapBetaReduce source table).2.WF := by + cases reduction with + | unchanged plan => + rw [cheapBetaReduce, plan] + exact ⟨reading, coherent⟩ + | beta support _ => exact (support.reading reading).2 + +def LetTypeReduction.trace {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} + {source : KExpr .anon} {table : InternTable .anon} {level : VLevel} {term result : AExpr β} + (reduction : LetTypeReduction resolve entries context bounds source table level term result) + (original : SynthesisTypingOrigin resolve entries context bounds entries context term (.sort level)) : + SynthesisBetaTrace resolve entries context bounds entries context term result (.sort level) := + match reduction with + | .unchanged _ => .refl original + | .beta _ origin => origin + +private theorem trace_appN_last {β : Type u} {head : AExpr β} {arguments : List (AExpr β)} + (nonempty : arguments ≠ []) : + head.appN arguments = (head.appN arguments.dropLast).app (arguments.getLast nonempty) := by + calc + head.appN arguments = head.appN (arguments.dropLast ++ [arguments.getLast nonempty]) := + congrArg (AExpr.appN head) (List.dropLast_concat_getLast nonempty).symm + _ = _ := by simp only [AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] + +private theorem betaPrefix_eq_of_not_app {β : Type u} {head : AExpr β} + {arguments : List (AExpr β)} {count : Nat} + (notApp : ∀ fn arg, head.appN arguments ≠ .app fn arg) : + AExpr.betaPrefix count head arguments = head.appN arguments := by + cases arguments with + | nil => cases count <;> cases head <;> rfl + | cons argument arguments => + exact False.elim (notApp _ _ (trace_appN_last (by simp))) + +/-- These traces reduce applications and their subapplications. They do +not change a source whose outer constructor is a lambda, product, or atom. +In particular, a forward beta conversion cannot change a lambda's product type. -/ +theorem SynthesisBetaTrace.rigid {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {source result type : AExpr β} + (trace : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context source result type) : + (∀ fn arg, source ≠ .app fn arg) → result = source := + match trace with + | .refl _ => fun _ => rfl + | .prefix .. | .origin .. => fun notApp => betaPrefix_eq_of_not_app notApp + | .trans prior next => fun notApp => by + have middle := prior.rigid notApp + exact (next.rigid (by simpa only [middle] using notApp)).trans middle + | .atType _ trace => trace.rigid + | .application .. | .argument .. => fun notApp => False.elim (notApp _ _ rfl) + | .substituteAt trace _ _ => fun notApp => by + have same := trace.rigid (by + intro fn arg equal + cases equal + exact notApp _ _ rfl) + rw [same] + | .weakenAt trace _ => fun notApp => by + have same := trace.rigid (by + intro fn arg equal + cases equal + exact notApp _ _ rfl) + rw [same] + | .rebase _ trace => trace.rigid + | .convertType trace _ => trace.rigid + | .instantiate trace arguments => + AExpr.HeadRigid.map trace.rigid (AExpr.instL arguments) (by intros; rfl) + | .appendContext trace _ | .extend trace _ => trace.rigid +termination_by structural trace + +/-- The selected cheap reduction preserves every non-application type. -/ +theorem LetTypeReduction.rigid {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} + {source : KExpr .anon} {table : InternTable .anon} {level : VLevel} {term result : AExpr β} + (reduction : LetTypeReduction resolve entries context bounds source table level term result) : + AExpr.HeadRigid term result := + match reduction with + | .unchanged _ => fun _ => rfl + | .beta _ origin => origin.rigid + +/-- Earlier argument checks can cross another local binder without any +new inference of their lifted expressions. -/ +def SynthesisArgumentSpineOrigin.weaken {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries context start arguments result) (domain : AExpr β) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries (context.push domain) + (start.liftN 1) (arguments.map (AExpr.liftN 1 ·)) (result.liftN 1) := + match support with + | .nil _ => .nil _ + | .snoc prior checked => by + simpa only [List.map_append, List.map_cons, List.map_nil, AExpr.liftN_inst_zero] using + (prior.weaken domain).snoc (checked.weaken domain) + | .convert prior trace => + .convert (prior.weaken domain) (.weakenAt trace (ContextInsertion.root context domain)) +termination_by structural support + +def SynthesisArgumentSpineOrigin.instantiate {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries context start arguments result) (levels : List VLevel) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries (context.map (AExpr.instL levels)) (start.instL levels) + (arguments.map (AExpr.instL levels)) (result.instL levels) := + match support with + | .nil _ => .nil _ + | .snoc prior checked => by + simpa only [List.map_append, List.map_cons, List.map_nil, AExpr.instL_inst] using + (prior.instantiate levels).snoc (checked.instantiate levels) + | .convert prior trace => .convert (prior.instantiate levels) (.instantiate trace levels) +termination_by structural support + +def SynthesisArgumentSpineOrigin.weakenAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext source target : Model.Context β} + {incomingBounds : List VLevel} {cutoff : Nat} {start result : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries source start arguments result) (insertion : ContextInsertion source target cutoff) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries target + (start.liftN 1 cutoff) (arguments.map (AExpr.liftN 1 · cutoff)) (result.liftN 1 cutoff) := + match support with + | .nil _ => .nil _ + | .snoc prior checked => by + simpa only [List.map_append, List.map_cons, List.map_nil, AExpr.liftN_inst_zero] using + (prior.weakenAt insertion).snoc (.weakenAt checked insertion) + | .convert prior trace => .convert (prior.weakenAt insertion) (.weakenAt trace insertion) +termination_by structural support + +def SynthesisArgumentSpineOrigin.rebase {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming middle entries : Model.Environment β} {incomingContext middleContext context : Model.Context β} + {incomingBounds middleBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} + (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) + (support : SynthesisArgumentSpineOrigin resolve middle middleContext middleBounds entries context start arguments result) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context start arguments result := + match support with + | .nil _ => .nil _ + | .snoc prior checked => (prior.rebase origin).snoc (.rebase origin checked) + | .convert prior trace => .convert (prior.rebase origin) (.rebase origin trace) +termination_by structural support + +def SynthesisArgumentSpineOrigin.appendContext {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries context start arguments result) (outer : Model.Context β) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries (context ++ outer) start arguments result := + match support with + | .nil _ => .nil _ + | .snoc prior checked => (prior.appendContext outer).snoc (checked.appendContext outer) + | .convert prior trace => .convert (prior.appendContext outer) (.appendContext trace outer) +termination_by structural support + +def SynthesisArgumentSpineOrigin.extend {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming earlier entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {start result : AExpr β} {arguments : List (AExpr β)} + (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + earlier context start arguments result) (extension : InterfaceExtends earlier entries) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries context start arguments result := + match support with + | .nil _ => .nil _ + | .snoc prior checked => (prior.extend extension).snoc (checked.extend extension) + | .convert prior trace => .convert (prior.extend extension) (.extend trace extension) +termination_by structural support + +def SynthesisArgumentSpineOrigin.substituteAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext base source target : Model.Context β} + {incomingBounds : List VLevel} {start result domain argument : AExpr β} + {arguments : List (AExpr β)} {cutoff : Nat} + (support : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries source start arguments result) + (value : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries base argument domain) + (substitution : ContextSubstitution base domain argument source target cutoff) : + SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds entries target + (start.inst argument cutoff) (arguments.map (AExpr.inst · argument cutoff)) (result.inst argument cutoff) := + match support with + | .nil _ => .nil _ + | .snoc prior checked => by + simpa only [List.map_append, List.map_cons, List.map_nil, AExpr.inst_inst_zero] using + (prior.substituteAt value substitution).snoc (checked.substituteAt value substitution) + | .convert prior trace => .convert (prior.substituteAt value substitution) (.substituteAt trace value substitution) +termination_by structural support + +/-- The head of a checked variable application uses the exact local type; +its argument checks remain available after leaving the original scope. -/ +structure SynthesisVariableSpineOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) + (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) + (entries : Model.Environment β) (context : Model.Context β) (index : Nat) + (arguments : List (AExpr β)) (type : AExpr β) where + headType : AExpr β + atIndex : context[index]? = some headType + spine : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries context headType arguments type + +def SynthesisVariableSpineOrigin.appendContext {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {type : AExpr β} {arguments : List (AExpr β)} {index : Nat} + (support : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries context index arguments type) (outer : Model.Context β) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries (context ++ outer) index arguments type := + { headType := support.headType + atIndex := by + rw [List.getElem?_append_left (List.getElem?_eq_some_iff.mp support.atIndex).1] + exact support.atIndex + spine := support.spine.appendContext outer } + +def SynthesisVariableSpineOrigin.instantiate {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {type : AExpr β} {arguments : List (AExpr β)} {index : Nat} + (support : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries context index arguments type) (levels : List VLevel) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries (context.map (AExpr.instL levels)) index (arguments.map (AExpr.instL levels)) (type.instL levels) := + { headType := support.headType.instL levels + atIndex := by simp only [List.getElem?_map, support.atIndex, Option.map_some] + spine := support.spine.instantiate levels } + +def SynthesisVariableSpineOrigin.extend {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming earlier entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {type : AExpr β} {arguments : List (AExpr β)} {index : Nat} + (support : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + earlier context index arguments type) (extension : InterfaceExtends earlier entries) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries context index arguments type := + { headType := support.headType + atIndex := support.atIndex + spine := support.spine.extend extension } + +/-- Applying an earlier parameter preserves a later variable-headed +codomain and updates every retained argument check in its dependent context. -/ +def SynthesisVariableSpineOrigin.substituteAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext base source target : Model.Context β} + {incomingBounds : List VLevel} {type domain argument : AExpr β} + {arguments : List (AExpr β)} {index cutoff : Nat} + (support : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries source index arguments type) + (value : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries base argument domain) + (substitution : ContextSubstitution base domain argument source target cutoff) + (distinct : index ≠ cutoff) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries target + (if index < cutoff then index else index - 1) + (arguments.map (AExpr.inst · argument cutoff)) (type.inst argument cutoff) := + { headType := support.headType.inst argument cutoff + atIndex := substitution.lookup_other support.atIndex distinct + spine := support.spine.substituteAt value substitution } + +/-- The checked argument transports an already justified original beta +prefix to its substituted application result. The two resulting expressions +are computed by the proved substitution laws. -/ +def SynthesisTypeTransport.substitutePrefixAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming origin entries : Model.Environment β} + {incomingContext originContext base sourceContext targetContext : Model.Context β} + {incomingBounds : List VLevel} {source reduced head domain argument : AExpr β} + {arguments : List (AExpr β)} {level bound : VLevel} {count cutoff : Nat} + (prior : SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries sourceContext + (head.appN arguments) (AExpr.betaPrefix count head arguments) bound) + (enough : count ≤ head.lambdaDepth) + (argumentOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds + entries base argument domain) + (substitution : ContextSubstitution base domain argument sourceContext targetContext cutoff) : + SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries targetContext + ((head.inst argument cutoff).appN (arguments.map (AExpr.inst · argument cutoff))) + (AExpr.betaPrefix count (head.inst argument cutoff) + (arguments.map (AExpr.inst · argument cutoff))) bound := by + simpa only [AExpr.inst_appN, AExpr.inst_betaPrefix count head arguments argument cutoff enough] using + prior.substituteAt argumentOrigin substitution + +/-- Follow an actual argument call through the remaining dependent +parameters of a previously checked function type. The context relation +computes their updated domains at the same substitution cutoff. -/ +def ApplicationInferenceTrace.substituteTypeOriginAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming origin entries : Model.Environment β} + {incomingContext originContext context sourceContext targetContext : Model.Context β} + {incomingBounds bounds : List VLevel} {source reduced head domain argumentType argument f body : AExpr β} + {condition : Certified.PropWhen} {arguments : List (AExpr β)} + {level bound functionBound argumentBound : VLevel} {count cutoff fuel : Nat} + {locals : List FVarId} {before : TcState .anon} {fn rawArgument : KExpr .anon} + (trace : ApplicationInferenceTrace fuel before fn rawArgument) + (prior : SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries sourceContext + (head.appN arguments) (AExpr.betaPrefix count head arguments) bound) + (enough : count ≤ head.lambdaDepth) + (argumentContext : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn + f (.forallE condition domain body) functionBound) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState rawArgument + argument argumentType argumentBound) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals rawArgument = some argument.erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) + (substitution : ContextSubstitution context domain argument sourceContext targetContext cutoff) : + SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries targetContext + ((head.inst argument cutoff).appN (arguments.map (AExpr.inst · argument cutoff))) + (AExpr.betaPrefix count (head.inst argument cutoff) + (arguments.map (AExpr.inst · argument cutoff))) bound := + prior.substitutePrefixAt enough + (.source (.applicationArgument argumentContext trace functionTree argumentTree agreement + functionReading argumentReading conditions hashPath comparisonFaithful)) substitution + +/-- Reuse a codomain check from an earlier closed function type at the +actual application site. The old function parameter keeps index zero while +the caller's locals are added outside it, then the actual argument call +supplies the substitution. -/ +def ApplicationInferenceTrace.substituteTypeOrigin {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming origin entries : Model.Environment β} {incomingContext originContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {source reduced head domain argumentType argument f body : AExpr β} + {condition : Certified.PropWhen} {arguments : List (AExpr β)} + {level bound functionBound argumentBound : VLevel} {count fuel : Nat} + {locals : List FVarId} {before : TcState .anon} {fn rawArgument : KExpr .anon} + (trace : ApplicationInferenceTrace fuel before fn rawArgument) + (prior : TypeReductionTransport origin originContext source reduced level + entries (Context.push domain []) + (head.appN arguments) (AExpr.betaPrefix count head arguments) bound) + (enough : count ≤ head.lambdaDepth) + (argumentContext : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn + f (.forallE condition domain body) functionBound) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState rawArgument + argument argumentType argumentBound) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals rawArgument = some argument.erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) : + SynthesisTypeTransport resolve incoming incomingContext incomingBounds + origin originContext source reduced level entries context + ((head.inst argument).appN (arguments.map (AExpr.inst · argument))) + (AExpr.betaPrefix count (head.inst argument) (arguments.map (AExpr.inst · argument))) bound := by + have imported : TypeReductionTransport origin originContext source reduced level + entries (context.push domain) (head.appN arguments) (AExpr.betaPrefix count head arguments) bound := by + simpa only [Context.push, List.map_nil, List.cons_append, List.nil_append] using + prior.appendContext (context.map (AExpr.liftN 1 ·)) + exact trace.substituteTypeOriginAt (.pure imported) enough argumentContext functionTree argumentTree + agreement functionReading argumentReading conditions hashPath comparisonFaithful .root + +/-- Substituting an actual lambda argument for a checked variable head +exposes its own checked prefix, even though the original type had no leading +lambda. Remaining dependent parameters are handled at the same cutoff. -/ +def ApplicationInferenceTrace.exposedTypeOriginAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} + {incomingContext context sourceContext targetContext : Model.Context β} + {incomingBounds bounds : List VLevel} {domain argumentType argument f body : AExpr β} + {condition : Certified.PropWhen} {arguments : List (AExpr β)} + {level functionBound argumentBound : VLevel} {count cutoff fuel : Nat} + {locals : List FVarId} {before : TcState .anon} {fn rawArgument : KExpr .anon} + (trace : ApplicationInferenceTrace fuel before fn rawArgument) + (origin : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries sourceContext cutoff arguments (.sort level)) + (enough : count ≤ argument.lambdaDepth) + (argumentContext : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn + f (.forallE condition domain body) functionBound) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState rawArgument + argument argumentType argumentBound) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals rawArgument = some argument.erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) + (substitution : ContextSubstitution context domain argument sourceContext targetContext cutoff) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries targetContext + (argument.liftN cutoff) (arguments.map (AExpr.inst · argument cutoff)) count level := + .substitutedVariable origin.spine origin.atIndex + (.applicationArgument argumentContext trace functionTree argumentTree agreement functionReading argumentReading + conditions hashPath comparisonFaithful) substitution enough + +/-- A supplied lambda application contributes its existing arguments +before the original variable application's arguments. Both sets of checks +come from the actual inference calls retained at this application. -/ +def ApplicationInferenceTrace.exposedApplicationOriginAt {β : Type u} + {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} + {incomingContext context sourceContext targetContext : Model.Context β} + {incomingBounds bounds : List VLevel} {domain argumentType binder inner f body : AExpr β} + {condition headCondition : Certified.PropWhen} {initialArguments arguments : List (AExpr β)} + {level functionBound argumentBound : VLevel} {count cutoff fuel : Nat} + {locals : List FVarId} {before : TcState .anon} {fn rawArgument : KExpr .anon} + (trace : ApplicationInferenceTrace fuel before fn rawArgument) + (origin : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds + entries sourceContext cutoff arguments (.sort level)) + (enough : count ≤ inner.lambdaDepth + 1) + (argumentContext : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn + f (.forallE condition domain body) functionBound) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState rawArgument + ((AExpr.lam headCondition binder inner).appN initialArguments) argumentType argumentBound) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals rawArgument = + some ((AExpr.lam headCondition binder inner).appN initialArguments).erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) + (substitution : ContextSubstitution context domain + ((AExpr.lam headCondition binder inner).appN initialArguments) sourceContext targetContext cutoff) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries targetContext + ((AExpr.lam headCondition binder inner).liftN cutoff) + (initialArguments.map (AExpr.liftN cutoff ·) ++ + arguments.map (AExpr.inst · ((AExpr.lam headCondition binder inner).appN initialArguments) cutoff)) count level := + .substitutedApplication origin.spine origin.atIndex + (.applicationArgument argumentContext trace functionTree argumentTree agreement functionReading argumentReading + conditions hashPath comparisonFaithful) substitution enough + +/-- Later actual arguments continue transporting an exposed prefix and +its reduction, including through dependent domains still left open. -/ +def ApplicationInferenceTrace.substituteReductionOriginAt {β : Type u} + {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} + {incomingContext context sourceContext targetContext : Model.Context β} + {incomingBounds bounds : List VLevel} {head domain argumentType argument f body : AExpr β} + {condition : Certified.PropWhen} {arguments : List (AExpr β)} + {level functionBound argumentBound : VLevel} {count cutoff fuel : Nat} + {locals : List FVarId} {before : TcState .anon} {fn rawArgument : KExpr .anon} + (trace : ApplicationInferenceTrace fuel before fn rawArgument) + (origin : SynthesisReductionOrigin resolve incoming incomingContext incomingBounds + entries sourceContext head arguments count level) + (enough : count ≤ head.lambdaDepth) + (argumentContext : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) + (functionTree : SynthesisInference resolve entries locals context bounds fuel before fn + f (.forallE condition domain body) functionBound) + (argumentTree : SynthesisInference resolve entries locals context bounds fuel trace.functionState rawArgument + argument argumentType argumentBound) + (agreement : LocalContextReading resolve locals before.lctx context) + (functionReading : readScopedExpr? resolve locals fn = some f.erase) + (argumentReading : readScopedExpr? resolve locals rawArgument = some argument.erase) + (conditions : argumentType.annotations = domain.annotations) + (hashPath : (trace.argumentType == trace.domain) = true) + (comparisonFaithful : trace.argumentType.AddrFaithful trace.domain) + (substitution : ContextSubstitution context domain argument sourceContext targetContext cutoff) : + SynthesisReductionOrigin resolve incoming incomingContext incomingBounds entries targetContext + (head.inst argument cutoff) (arguments.map (AExpr.inst · argument cutoff)) count level := + origin.map (trace.substituteTypeOriginAt (.pure .refl) enough argumentContext functionTree argumentTree + agreement functionReading argumentReading conditions hashPath comparisonFaithful substitution) + +def SynthesisVariableSpineOrigin.weakenAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext source target : Model.Context β} + {incomingBounds : List VLevel} {cutoff index : Nat} {arguments : List (AExpr β)} {type : AExpr β} + (spine : SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries source index arguments type) + (insertion : ContextInsertion source target cutoff) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries target (liftVar 1 index cutoff) + (arguments.map (AExpr.liftN 1 · cutoff)) (type.liftN 1 cutoff) := + ⟨spine.headType.liftN 1 cutoff, + by simpa only [liftVar, Nat.add_comm 1] using insertion.lookup spine.atIndex, + spine.spine.weakenAt insertion⟩ + +def SynthesisVariableSpineOrigin.rebase {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming middle entries : Model.Environment β} {incomingContext middleContext context : Model.Context β} + {incomingBounds middleBounds : List VLevel} {index : Nat} {arguments : List (AExpr β)} {type : AExpr β} + (spine : SynthesisVariableSpineOrigin resolve middle middleContext middleBounds entries context index arguments type) + (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) : + SynthesisVariableSpineOrigin resolve incoming incomingContext incomingBounds entries context index arguments type := + ⟨spine.headType, spine.atIndex, spine.spine.rebase origin⟩ + + +/-- The head's check and every argument check of an actual application +spine, retained as data for subsequent reductions and substitutions. -/ +structure SynthesisSpineOrigin {β : Type u} (resolve : Address → Option (ConstRef β)) + (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) + (entries : Model.Environment β) (context : Model.Context β) + (head : AExpr β) (arguments : List (AExpr β)) (type : AExpr β) where + headType : AExpr β + headOrigin : SynthesisTypingOrigin resolve incoming incomingContext incomingBounds entries context head headType + leading : LambdaPrefix head headType head.lambdaDepth + argumentsOrigin : SynthesisArgumentSpineOrigin resolve incoming incomingContext incomingBounds + entries context headType arguments type + + +def SynthesisSpineOrigin.extend {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming earlier entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {head type : AExpr β} {arguments : List (AExpr β)} + (spine : SynthesisSpineOrigin resolve incoming incomingContext incomingBounds earlier context head arguments type) + (extension : InterfaceExtends earlier entries) : + SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := + ⟨spine.headType, spine.headOrigin.extend extension, spine.leading, spine.argumentsOrigin.extend extension⟩ + +def SynthesisSpineOrigin.weakenAt {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext source target : Model.Context β} + {incomingBounds : List VLevel} {cutoff : Nat} {head type : AExpr β} {arguments : List (AExpr β)} + (spine : SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries source head arguments type) + (insertion : ContextInsertion source target cutoff) : + SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries target (head.liftN 1 cutoff) + (arguments.map (AExpr.liftN 1 · cutoff)) (type.liftN 1 cutoff) := + ⟨spine.headType.liftN 1 cutoff, .weakenAt spine.headOrigin insertion, + by simpa only [AExpr.lambdaDepth_liftN] using spine.leading.liftN 1 cutoff, + spine.argumentsOrigin.weakenAt insertion⟩ + +def SynthesisSpineOrigin.rebase {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming middle entries : Model.Environment β} {incomingContext middleContext context : Model.Context β} + {incomingBounds middleBounds : List VLevel} {head type : AExpr β} {arguments : List (AExpr β)} + (spine : SynthesisSpineOrigin resolve middle middleContext middleBounds entries context head arguments type) + (origin : SynthesisContext resolve incoming incomingContext incomingBounds middle middleContext middleBounds) : + SynthesisSpineOrigin resolve incoming incomingContext incomingBounds entries context head arguments type := + ⟨spine.headType, .rebase origin spine.headOrigin, spine.leading, spine.argumentsOrigin.rebase origin⟩ + + +end Ix.Kernel.Consistency diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index e698db2dd..e612a7935 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -3557,6 +3557,51 @@ private def letFailureCleanup : Bool := | _ => false rejectsValue && cleansBody +private def recursiveLetPositions : Bool := + let prop := KExpr.mkSort (m := .anon) .mkZero + let sort := KExpr.mkSort (m := .anon) levelOne + let sortTwo := KExpr.mkSort (m := .anon) levelTwo + let alias := KExpr.mkLet () sort prop (.mkVar 0 ()) false + let functionType := KExpr.mkAll () () sort sort + let functionValue := KExpr.mkLam () () sort (.mkVar 0 ()) + let function := KExpr.mkLet () functionType functionValue (.mkVar 0 ()) false + let samples := [ + (KExpr.mkLam () () alias (.mkVar 0 ()), KExpr.mkAll () () alias alias), + (KExpr.mkLam () () prop (.mkLet () prop (.mkVar 0 ()) (.mkVar 0 ()) false), + KExpr.mkAll () () prop prop), + (KExpr.mkAll () () sort (.mkLet () sort (.mkVar 0 ()) (.mkVar 0 ()) false), sortTwo), + (KExpr.mkApp function prop, sort), + (KExpr.mkLet () sort alias (.mkLam () () (.mkVar 0 ()) (.mkVar 0 ())) false, + KExpr.mkAll () () alias alias)] + samples.all fun (source, expected) => + match TcM.infer source (TcState.newLazyAnon {}) with + | .ok result after => result == expected && after.lctx.size == 0 && + after.env.inferCache[(source.addr, emptyCtxAddr)]? == some expected && + compositeReplayAt source expected after + | .error _ _ => false + +private def recursiveLetEnvironment : Ixon.Env := Id.run do + let alias := Ixon.Expr.letE false (.sort 1) (.sort 0) (.var 0) + let firstType := Ixon.Expr.leanAll alias alias + let firstValue := Ixon.Expr.leanLam alias (.var 0) + let (env, _) := storeConst {} + ⟨.defn ⟨.defn, .safe, 0, firstType, firstValue⟩, #[], #[], #[.zero, .succ .zero]⟩ + let secondType := Ixon.Expr.leanAll (.sort 0) (.sort 0) + let secondValue := Ixon.Expr.leanLam (.sort 0) (.letE false (.sort 0) (.var 0) (.var 0)) + let (env, _) := storeConst env + ⟨.defn ⟨.defn, .safe, 0, secondType, secondValue⟩, #[], #[], #[.zero]⟩ + return env + +private def recursiveLetFailureCleanup : Bool := + let prop := KExpr.mkSort (m := .anon) .mkZero + let invalid := KExpr.mkLet () prop prop (.mkVar 0 ()) false + let source := KExpr.mkLam () () prop invalid + match TcM.infer source (TcState.newLazyAnon {}) with + | .error .declTypeMismatch after => after.lctx.size == 0 && after.env.nextFVarId == 1 && + !after.env.inferCache.contains (source.addr, emptyCtxAddr) && + !after.env.inferCache.contains (invalid.addr, emptyCtxAddr) + | _ => false + private def letCases : TestSeq := test "let inference: dependent type substitution retains exact child caches, replay, and fresh rebuilding" letExactCacheHistory @@ -3569,6 +3614,13 @@ private def letCases : TestSeq := letNestedCapture ++ test "let inference: bad values are checked and failed bodies restore scope without publishing the parent" letFailureCleanup + ++ test "recursive let inference: binder domains, binder bodies, function positions, and nested values compose" + recursiveLetPositions + ++ test "recursive let admission: composite declared types and bodies survive persistent and cleared caches" + (allSucceeded recursiveLetEnvironment 2 { clearEvery := 0 } && + allSucceeded recursiveLetEnvironment 2 { clearEvery := 1 }) + ++ test "recursive let inference: an invalid inner value unwinds both scopes without parent publication" + recursiveLetFailureCleanup private def compositeCacheCases : TestSeq := test "composite cache: applications survive lazy inference, scopes, replay, and clearing" diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 30acab654..ad0a6d38c 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -97,18 +97,21 @@ increasing sequence of strongly inaccessible cardinals. `AxiomObservation.synthesisTypeCheck` extracts an axiom's type check from successful `checkEnvAnon` rows; `StandalonePrefix.definitionTypeCheck` extracts a definition's check from public declaration success. -- `LetInferenceCheck.sound` connects the full production let branch to its +- `SynthesisInference.letE` connects the full production let branch to its original declared-type, value, and opened-body synthesis checks. The actual value-type hash comparison establishes domain agreement. Fresh let opening, type abstraction, value substitution, and the selected cheap-beta operation give the returned type and its formation bound. Substituting the original checking derivations retains lambda domains and argument checks for later - beta steps, including lambdas exposed by the substituted value. + beta steps, including lambdas and function types exposed by the substituted + value. The original recursive datatype now composes lets in binder domains + and bodies, function and argument positions, and other lets. Function-type + derivations retain both checked children through dependent substitution. `DefinitionBodyTrace.letSupport` uses the same declaration's validation and - inference calls to include let bodies in model extension. The three children - still belong to the existing `SynthesisInference` fragment; composing lets - arbitrarily into that recursive datatype remains open. Finite execution, - collision, walker, and selected beta-origin resources remain explicit. + inference calls to include let bodies in model extension; + `LetInferenceCheck.asSynthesis` connects that interface to the recursive case. + Finite execution, collision, walker, and selected beta-origin resources + remain explicit, and their general automatic construction remains open. - `MethodsLocalState.methodsN` proves structural local-state preservation for every production recursive table, including all inference, reduction, and conversion branches. The public inference, WHNF, conversion, and sort/forall @@ -524,7 +527,7 @@ the run. They supply no typing or checker-soundness premise. The proof extracts the validation, type-inference, theorem-guard, value-inference, and conversion steps from public success, then derives body typing to extend the preceding model. General automatic witness construction, broader application and -lambda paths, arbitrary recursive let inference, inductives, coordinated blocks, and other +lambda paths, inductives, coordinated blocks, and other conversion paths remain outside the fragment. The declaration's exact universe arity is carried from production lookup into its model entry. Model extension interprets the checked body at every universe instance, retaining old @@ -580,7 +583,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 1,250 exact theorem boundaries. The production +The consistency target checks 1,281 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -666,7 +669,8 @@ bound includes the final unchanged `.done` iteration. The same trace supplies These boundaries retain the same two existing native output-length proofs. `SynthesisInference.betaTyping` now derives the complete head-beta typing structure from every currently supported source-inference constructor. -`SynthesisBetaTyping` keeps each lambda body and both application children. +`SynthesisBetaTyping` keeps each lambda body, both application children, and +both checked children of a function type. Its substitution operation rebuilds this structure beneath dependent binders, using a proved context insertion to lift the supplied argument. Forward beta type conversions preserve a lambda's exact Pi domain, including when cheap @@ -684,6 +688,17 @@ proofs. This closes automatic semantic origins for finite head-beta paths of the supported source-inference fragment. Initial inference trees, operational paths, and representation resources remain explicit; deriving them for all accepted programs and covering the remaining WHNF/conversion paths remain open. +Recursive lets use the same derivation: substituting the value into the body +retains every checked child, including a function type exposed by substitution. +`SynthesisInference.outputReading` derives returned syntax readings without a +context-formation premise, so the source derivation can identify compared types +before the semantic induction. `soundWithHereditary` derives an internal +semantic invariant closed under dependent substitution and then recovers the +existing typing, formation, and lambda-spine contracts. Source support contains +no field for this invariant. The audit explicitly forbids it in the recursive +let constructor, returned-reading proof, function-type derivation constructor, +and source derivation producer. The 31 additional boundaries preserve every +previous exact axiom profile and introduce no axioms or native proofs. `BetaPublicWhnfPlan` connects a raw beta path to public WHNF when its result is a sort, Pi, or lambda. It computes the context-key states, optional counters, shared-fuel charge, and insertions into the structural, no-delta, and outer @@ -713,8 +728,8 @@ proves immediate reuse with any method table and either current checking policy. These ten additional audited roots retain the existing axiom boundary. `SynthesisInference.cachedFrom` also accepts the original check after interface growth and insertion of locals. `SynthesisRetainedCheck` transports its actual -inference tree, readings, and execution. Structural inversion of lifted syntax -recovers the original lambda head, domain, body, and arguments. Pi checks retain +inference tree, readings, and execution. Complete derivations recover the +lambda head, domain, body, and arguments after lifting and substitution. Pi checks retain both domain and codomain checks; lambda bodies and variable-headed codomains retain their dependent argument checks. These origins are used by the same synthesis, successive-beta, hereditary-beta, declaration, and environment @@ -734,7 +749,7 @@ changes, binder scopes, verified loading on both outcomes, and clearing. Every present value consequently has an actual producing call, including at newly written keys. This operational result needs no catalog or collision premise. `SynthesisInference.cacheExecution` extracts full-publication annotations from -the original application, Pi, lambda, beta-exposure, and changed-beta trees. +the original application, Pi, lambda, let, beta-exposure, and changed-beta trees. Child readings and contexts follow their actual calls and domain checks. Primitive leaves need only operational resources; older `BinderInference` wrappers additionally supply checking annotations for child calls they omitted. @@ -754,10 +769,11 @@ value, and opened-body events followed by the parent publication. The same `LetInferenceCheck` and its three children's cache data derive this trace, the exact fold for both maps, and a history containing every child and parent write. Opening and scope cleanup retain those maps. Initially occupied full -keys remain protected by cache priority without a collision premise. The -typed `SynthesisCacheHistory` still needs the let case integrated into its -recursive inference origins before it can retain a let root. The 33 additional -let and representation audit boundaries preserve the existing axiom policy. +keys remain protected by cache priority without a collision premise. +`SynthesisInference.cacheExecution` now retains the let root and all three +children in the typed `SynthesisCacheHistory` as well. Later full-cache +selection reconstructs the same recursive let check and its beta origins, +including when surrounding inference nodes contain lets. Kernel unit regressions cover lazy loading, both inference policies, interning reuse, dependent function types, shared references, lets, `imax` simplification, @@ -928,12 +944,14 @@ parent, and exited-local entries, through policy changes, successful and failed loading, clearing, and repopulation. A forged different input at an occupied full key confirms that priority preserves the maps even without collision freedom; semantic source recovery separately requires finite collision data. -Five let regressions check exact child/parent cache maps, zero-fuel replay under +Eight let regressions check exact child/parent cache maps, zero-fuel replay under both policies, clearing and rebuilding, cheap beta in the generated type, theorem admission with both cache-clearing settings, nested capture avoidance, -and cleanup after value-comparison and body-inference failures. They exercise -production independently of the finite resources used by the proof. -The unit suite contains 723 checks. The anonymous differential additionally +and cleanup after value-comparison and body-inference failures. They include +lets in binder domains and bodies, function positions, nested values, composite +declaration types, and failures inside nested scopes. They exercise production +independently of the finite resources used by the proof. +The unit suite contains 726 checks. The anonymous differential additionally serializes eleven cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. @@ -978,6 +996,9 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Source ownership, block registration, and finite preflight | [`Consistency/BlockOwnership.lean`](../Ix/Kernel/Verify/Consistency/BlockOwnership.lean), [`Consistency/SourceOwnershipCheck.lean`](../Ix/Kernel/Verify/Consistency/SourceOwnershipCheck.lean), [`SourceOwnership.lean`](../Ix/Kernel/SourceOwnership.lean) | | Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`Application.lean`](../Ix/Kernel/Verify/Consistency/Application.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | | Inferred type formation and direct lambda applications | [`Consistency/SynthesisInference.lean`](../Ix/Kernel/Verify/Consistency/SynthesisInference.lean), [`Formation.lean`](../Ix/Kernel/Verify/Consistency/Formation.lean), [`Model/UniverseBounds.lean`](../Ix/Theory/Model/UniverseBounds.lean) | +| Recursive synthesis support and returned syntax readings | [`Consistency/SynthesisSupport.lean`](../Ix/Kernel/Verify/Consistency/SynthesisSupport.lean), [`SynthesisReading.lean`](../Ix/Kernel/Verify/Consistency/SynthesisReading.lean), [`SynthesisSource.lean`](../Ix/Kernel/Verify/Consistency/SynthesisSource.lean) | +| Retained derivations, dependent substitution, and semantic induction | [`Consistency/SynthesisDerivation.lean`](../Ix/Kernel/Verify/Consistency/SynthesisDerivation.lean), [`SynthesisShapes.lean`](../Ix/Kernel/Verify/Consistency/SynthesisShapes.lean), [`SynthesisReduction.lean`](../Ix/Kernel/Verify/Consistency/SynthesisReduction.lean), [`SynthesisMeaning.lean`](../Ix/Kernel/Verify/Consistency/SynthesisMeaning.lean), [`BinderMeaning.lean`](../Ix/Kernel/Verify/Consistency/BinderMeaning.lean), [`BetaChecking.lean`](../Ix/Kernel/Verify/Consistency/BetaChecking.lean) | +| Let inference and typed cache history | [`Consistency/LetInference.lean`](../Ix/Kernel/Verify/Consistency/LetInference.lean), [`LetSynthesis.lean`](../Ix/Kernel/Verify/Consistency/LetSynthesis.lean), [`LetCache.lean`](../Ix/Kernel/Verify/Consistency/LetCache.lean), [`SynthesisCacheExecution.lean`](../Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean) | | Public beta WHNF, cache writes, and application Pi exposure | [`Consistency/BetaPublicWhnf.lean`](../Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean), [`ApplicationWhnf.lean`](../Ix/Kernel/Verify/Consistency/ApplicationWhnf.lean), [`BetaWhnfInference.lean`](../Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean) | | Retained type checks and changed cheap beta in lambda inference | [`Consistency/SynthesisInference.lean`](../Ix/Kernel/Verify/Consistency/SynthesisInference.lean), [`CheapBetaReading.lean`](../Ix/Kernel/Verify/Consistency/CheapBetaReading.lean), [`Formation.lean`](../Ix/Kernel/Verify/Consistency/Formation.lean) | | Source beta reduction and declaration conversion | [`Consistency/BetaSpine.lean`](../Ix/Kernel/Verify/Consistency/BetaSpine.lean), [`Simultaneous.lean`](../Ix/Kernel/Verify/Consistency/Simultaneous.lean), [`SpineReading.lean`](../Ix/Kernel/Verify/Consistency/SpineReading.lean), [`CheapBeta.lean`](../Ix/Kernel/Verify/Consistency/CheapBeta.lean), [`Model/BetaSpine.lean`](../Ix/Theory/Model/BetaSpine.lean) | diff --git a/docs/theory.md b/docs/theory.md index e5d6d1c8c..6e6741bd4 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -94,7 +94,8 @@ computes each raw substitution result and intern table and proves the actual uncached loop under its bound, including the final unchanged iteration. Declaration admission uses the same traces. The currently supported source inference trees now construct complete beta typing derivations, retaining -lambda bodies and both application children. Dependent substitution rebuilds +lambda bodies, both application children, and both checked children of a +function type. Dependent substitution rebuilds these derivations, including under retained binders. Each generated result therefore supplies the exact lambda domains and argument origins for every later head-beta step. This also handles a lambda whose body's inferred type @@ -110,10 +111,16 @@ beneath locals and binders. The successful value-type hash comparison supplies the domain agreement needed to substitute both the body's typing derivation and its inferred-type origin. This retains all subsequent beta origins, including a lambda introduced by replacing the let variable. The actual final cheap-beta -choice determines the returned type. Declaration admission includes these let -bodies using the same validation and inference calls. The three child checks -remain in the existing recursive synthesis fragment; arbitrary recursive let -composition and automatic construction of its finite resources remain open. +choice determines the returned type. The original recursive synthesis datatype +now includes this let case, so its children and surrounding binders, applications, +and cache hits can contain further lets. Declaration admission uses the same +recursive checks and validation calls. Complete retained derivations recover +function-type domains and codomains after substitution exposes their constructor. +Returned syntax readings follow without a context-formation premise; the +semantic induction then derives an invariant closed under dependent substitution. +Source support and reconstruction do not assume that invariant, and the audit +enforces this boundary. Automatic construction of the initial execution and +finite representation resources remains open. Structural local-state preservation now covers the complete recursive checker on both success and failure. The actual loader and initial state establish coherent lookup and a bound on allocated identifiers. Recursive calls retain @@ -176,9 +183,10 @@ source. Selection then derives its cached result reading and complete retained check, including the original local context and later interface transport. The raw execution history also covers full-mode lets, with all three child calls before the parent publication. Their original let check and child cache -data derive both entire maps and the history. Retaining a let root in the typed -synthesis history still requires its integration into the recursive checking -datatype. +data derive both entire maps and the history. The typed synthesis history now +retains the let root and its descendants through the same recursive checking +datatype. Later full-cache selection reconstructs these checks with their +complete beta origins, including lets inside other inference nodes. Arbitrary execution construction, compatibility with later contexts after scope exit, and the other inference and conversion/cache paths remain open. Frames allow new declarations while retaining old ones. From e2a8863f81afd00e94b7c413ee63989f14925e53 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 14:12:49 -0400 Subject: [PATCH 42/63] Integrate sort exposure into recursive inference proofs --- Ix/Kernel/Verify/Consistency.lean | 8 + Ix/Kernel/Verify/Consistency/Audit.lean | 27 +- .../Verify/Consistency/BetaPublicWhnf.lean | 71 +++ .../Verify/Consistency/BetaWhnfInference.lean | 44 ++ .../Verify/Consistency/CacheExecution.lean | 53 ++ .../Verify/Consistency/RecursiveCache.lean | 113 +++- .../Verify/Consistency/SortInference.lean | 559 ++++++++++++++++++ .../Consistency/SynthesisCacheExecution.lean | 91 +++ .../Consistency/SynthesisInference.lean | 91 +++ .../Verify/Consistency/SynthesisReading.lean | 36 ++ .../Verify/Consistency/SynthesisSource.lean | 66 +++ .../Verify/Consistency/SynthesisSupport.lean | 118 ++++ .../Verify/Consistency/WhnfCacheFrame.lean | 36 ++ Tests/Ix/Kernel/Consistency.lean | 123 +++- docs/kernel-verification.md | 31 +- docs/theory.md | 11 + 16 files changed, 1471 insertions(+), 7 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/SortInference.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 2b5a4f8b4..39e40baba 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -51,6 +51,7 @@ import Ix.Kernel.Verify.Consistency.WhnfCacheFrame import Ix.Kernel.Verify.Consistency.BetaTyping import Ix.Kernel.Verify.Consistency.BetaInference import Ix.Kernel.Verify.Consistency.BetaWhnfInference +import Ix.Kernel.Verify.Consistency.SortInference import Ix.Kernel.Verify.Consistency.CheapBeta import Ix.Kernel.Verify.Consistency.Validation import Ix.Kernel.Verify.Consistency.RecursiveCache @@ -77,6 +78,13 @@ polymorphic references, applications, dependent functions, full-mode lambdas, an under the stated execution resources. Definitions may declare their own universe parameters; model entries retain the exact arity and interpretations at every instance. +Recursive forall, lambda, and let rules also expose sorts through the supported +public beta WHNF path or its outer-cache hit. Successful production calls +determine their raw child traces. Retained checks justify conversion from the +original inferred type to the exposed sort and supply the binder's level. +Source derivations and typed cache histories retain the original child check; +sort exposure leaves both inference-cache maps unchanged. General construction +of the finite representation and reduction resources remains open. Safe definition admission now checks its reachable definition dependencies. The actual traversal has proved root coverage and an order with a decreasing natural-number rank. Finite collision freedom justifies complete reference diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 4522c5307..69b5084c8 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -65,7 +65,7 @@ private def atomicRoots : Array Lean.Name := #[ ``DefinitionBodyTrace.checkedType, ``DefinitionBodyTrace.synthesisTypeCheck, ``DefinitionBodyTrace.synthesisSupport, ``AxiomTypeTrace.synthesisTypeCheck, ``DefinitionCheckSupport.sound, ``DefinitionBodyTrace.betaDeclaredSupport, - ``AtomicDefinitionRun.ofHash + ``AtomicDefinitionRun.ofHash, ``SynthesisSortCheck.soundWithHereditary, ``SynthesisSortCheck.sound ] private def formationRoots : Array Lean.Name := #[ @@ -106,6 +106,29 @@ private def recursiveLetShapeRoots : Array Lean.Name := #[ ``SynthesisInference.outputReading, ``SynthesisBetaTyping.forallE ] +/-- Executed sort exposure, its recursive source resources, and its cache +history must be constructed before the semantic hereditary invariant. -/ +private def sortExposureRoots : Array Lean.Name := #[ + ``BetaSortExposure.run, ``BetaSortExposure.coherent, ``BetaSortExposure.context, + ``BetaSortExposure.betaTrace, ``BetaSortExposure.checkedTrace, ``BetaSortExposure.inference_frame, + ``BetaSortExposure.policy, ``BetaSortExposure.inference_maps, ``SortInferenceTrace.direct, + ``SortInferenceTrace.success, ``SortInferenceTrace.inferenceFrame, ``SortInferenceTrace.frame, + ``SortInferenceTrace.exposure_state, ``SortInferenceTrace.exposure_frame, ``SortInferenceTrace.exposure_policy, + ``SortInferenceTrace.exposure_maps, ``ForallSortInferenceTrace.domainFrame, ``ForallSortInferenceTrace.opened_reading, + ``ForallSortInferenceTrace.success, ``ForallSortInferenceTrace.ofSuccess, ``ForallSortInferenceTrace.ofInference, + ``ForallSortInferenceTrace.output_state, ``LambdaSortInferenceTrace.domainFrame, ``LambdaSortInferenceTrace.opened_reading, + ``LambdaSortInferenceTrace.success, ``LambdaSortInferenceTrace.ofSuccess, ``LambdaSortInferenceTrace.ofInference, + ``LambdaSortInferenceTrace.output_state, ``LetSortInferenceTrace.domainFrame, ``LetSortInferenceTrace.valueFrame, + ``LetSortInferenceTrace.openingFrame, ``LetSortInferenceTrace.domainContext, ``LetSortInferenceTrace.openingContext, + ``LetSortInferenceTrace.opened_reading, ``LetSortInferenceTrace.restores, ``LetSortInferenceTrace.success, + ``LetSortInferenceTrace.ofSuccess, ``LetSortInferenceTrace.ofInference, ``LetSortInferenceTrace.output_state, + ``LetSortInferenceTrace.substituted_reading, ``SynthesisInference.forallSort, ``SynthesisInference.lamSort, + ``SynthesisInference.letSort, ``SynthesisContext.pushSort, ``SynthesisSortCheck.checked, + ``SynthesisSortCheck.direct, ``SynthesisSortCheck.ofRetainedType, ``SynthesisSortCheck.betaTyping, + ``SynthesisSortCheck.CacheData, ``SynthesisSortCheck.cacheExecution, ``InferenceCacheTrace.forallSort, + ``InferenceCacheTrace.lamSort, ``InferenceCacheTrace.letSort +] + private def instantiationRoots : Array Lean.Name := #[ ``instUnivSpec_readExpr?, ``instUnivSpec_readExpr?_withScope, ``instantiateUnivParams_readExpr?, ``instantiateUnivParams_readAnnotated, @@ -1259,7 +1282,7 @@ def roots : Array RootAllowance := #[ }) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots ++ exposedOriginRoots ++ repeatedBetaRoots ++ betaTraceRoots ++ hereditaryBetaRoots ++ piExposureRoots ++ cacheTransportRoots).map (fun allowance => { allowance with forbiddenDependencies := allowance.forbiddenDependencies ++ forbiddenProduction }) - ++ recursiveLetShapeRoots.map (fun root => { + ++ (recursiveLetShapeRoots ++ sortExposureRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ (localScopeFrameRoots ++ localStateFrameRoots ++ recursiveStateFrameRoots ++ ingressFrameRoots ++ diff --git a/Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean b/Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean index 389598a58..287d4f5fa 100644 --- a/Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean +++ b/Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean @@ -449,4 +449,75 @@ theorem context (exposure : BetaPiExposure resolve locals fuel before source ter end BetaPiExposure +/-- Sort exposure follows the syntactic branch, a public beta reduction, +or an exact cached result of that reduction. The level is the one returned +by the actual exposure call. -/ +inductive BetaSortExposure {β : Type u} (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (fuel : Nat) (before : TcState .anon) : + KExpr .anon → AExpr β → KUniv .anon → Type u + | direct (level : KUniv .anon) (info : ExprInfo .anon) : + BetaSortExposure resolve locals fuel before (.sort level info) (.sort (readLevel level)) level + | reduce {source term level info} + (plan : BetaPublicWhnfPlan resolve locals fuel before source term + (.sort level info) (.sort (readLevel level))) : + BetaSortExposure resolve locals fuel before source term level + | cached {originFuel originBefore source term level info} + (origin : BetaPublicWhnfPlan resolve locals originFuel originBefore source term + (.sort level info) (.sort (readLevel level))) + (hit : (BetaPublicWhnf.outerKey source before).2.env.whnfCache[ + (BetaPublicWhnf.outerKey source before).1]? = some (.sort level info)) : + BetaSortExposure resolve locals fuel before source term level + +namespace BetaSortExposure + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} {term : AExpr β} {level : KUniv .anon} + +def after (exposure : BetaSortExposure resolve locals fuel before source term level) : TcState .anon := + match exposure with + | .direct .. => before + | .reduce plan => plan.after + | .cached .. => (BetaPublicWhnf.outerKey source before).2 + +theorem run (exposure : BetaSortExposure resolve locals fuel before source term level) : + (RecM.ensureSortDirect source).run (methodsN (fuel + 1)) before = .ok level exposure.after := by + cases exposure with + | direct => rfl + | reduce plan => + have first := plan.path.first plan.moving + have entry : RecM.ensureSortDirect source = RecM.ensureSortWhnf source := by + rw [first.sourceEq]; rfl + rw [entry, RecM.ensureSortWhnf, ReaderT.run_bind] + change EStateM.bind ((RecM.whnf source).run (methodsN (fuel + 1))) _ before = _ + rw [EStateM.bind, plan.run] + rfl + | cached origin hit => + have first := origin.path.first origin.moving + have entry : RecM.ensureSortDirect source = RecM.ensureSortWhnf source := by + rw [first.sourceEq]; rfl + rw [entry, RecM.ensureSortWhnf, ReaderT.run_bind] + change EStateM.bind ((RecM.whnf source).run (methodsN (fuel + 1))) _ before = _ + rw [EStateM.bind, origin.cache_hit _ _ hit] + rfl + +theorem coherent (exposure : BetaSortExposure resolve locals fuel before source term level) + (reading : readScopedExpr? resolve locals source = some term.erase) + (initial : before.env.intern.WF) : exposure.after.env.intern.WF := by + cases exposure with + | direct => exact initial + | reduce plan => exact (plan.reading reading initial).2 + | cached origin hit => + simpa only [after, BetaPublicWhnf.outerKey, betaWhnfKey_environment, + (betaWhnfPrefix_fields before).1] using initial + +theorem context (exposure : BetaSortExposure resolve locals fuel before source term level) : + exposure.after.lctx = before.lctx := by + cases exposure with + | direct => rfl + | reduce plan => exact plan.context + | cached origin hit => + simp only [after, BetaPublicWhnf.outerKey, betaWhnfKey_context, (betaWhnfPrefix_fields before).2.1] + +end BetaSortExposure + end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean b/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean index 06803d064..b01d4d3f9 100644 --- a/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean +++ b/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean @@ -76,6 +76,50 @@ def BetaPiExposure.betaTrace {β : Type u} {resolve : Address → Option (ConstR | .reduce plan => plan.betaTrace typing | .cached origin _ _ => origin.betaTrace typing +/-- Sort exposure obtains its meaning from the same complete source +derivation as public beta WHNF; a direct sort uses reflexivity. -/ +def BetaSortExposure.betaTrace {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source : KExpr .anon} {term type : AExpr β} {level : KUniv .anon} + (exposure : BetaSortExposure resolve locals fuel before source term level) + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + term (.sort (readLevel level)) type := + match exposure with + | .direct .. => .refl typing.origin + | .reduce plan => plan.betaTrace typing + | .cached origin _ => origin.betaTrace typing + +/-- Earlier type checks retain every beta origin, including after interface +growth or insertion beneath binders. No check of the exposed sort is required. -/ +def BetaSortExposure.checkedTrace {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source : KExpr .anon} {term : AExpr β} {level : KUniv .anon} {typeLevel typeBound : VLevel} + (exposure : BetaSortExposure resolve locals fuel before source term level) + (checked : SynthesisRetainedCheck resolve incoming incomingContext incomingBounds entries context + term (.sort typeLevel) typeBound) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + term (.sort (readLevel level)) (.sort typeLevel) := + exposure.betaTrace checked.betaTyping + +/-- Construct the sort-check resource from actual term inference and an +earlier check of its returned type. Its formation origin selects the bound +used by the enclosing inference, independently of the earlier check's bound. -/ +def SynthesisSortCheck.ofRetainedType {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} {locals : List FVarId} + {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} {trace : SortInferenceTrace fuel before source} + {term type : AExpr β} {bound typeLevel typeBound : VLevel} + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type bound) + (exposure : trace.Exposure resolve locals type) + (checked : SynthesisRetainedCheck resolve entries context bounds entries context type (.sort typeLevel) typeBound) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) : + SynthesisSortCheck resolve entries locals context bounds trace term := + .checked tree exposure + (.atType (.inferredType .current tree agreement reading trace.inferRun) (exposure.checkedTrace checked)) + /-- An actual earlier check of the function type supplies the conversion field of application inference. No check of the generated Pi is needed. -/ def BetaPiExposure.checkedTrace {β : Type u} {resolve : Address → Option (ConstRef β)} diff --git a/Ix/Kernel/Verify/Consistency/CacheExecution.lean b/Ix/Kernel/Verify/Consistency/CacheExecution.lean index 9c28a833e..356dcf995 100644 --- a/Ix/Kernel/Verify/Consistency/CacheExecution.lean +++ b/Ix/Kernel/Verify/Consistency/CacheExecution.lean @@ -121,6 +121,13 @@ def InferenceCacheTrace.events {fuel : Nat} {before after : TcState .anon} | .letE _ miss trace _ domainTree valueTree bodyTree => domainTree.events trace.domainRun ++ valueTree.events trace.valueRun ++ bodyTree.events trace.bodyRun ++ [.ofRun miss accepted] + | .forallSort miss trace _ _ domainTree bodyTree => + domainTree.events trace.domainCheck.inferRun ++ bodyTree.events trace.bodyCheck.inferRun ++ [.ofRun miss accepted] + | .lamSort _ miss trace _ domainTree bodyTree => + domainTree.events trace.domainCheck.inferRun ++ bodyTree.events trace.bodyRun ++ [.ofRun miss accepted] + | .letSort _ miss trace _ _ domainTree valueTree bodyTree => + domainTree.events trace.domainCheck.inferRun ++ valueTree.events trace.valueRun ++ + bodyTree.events trace.bodyRun ++ [.ofRun miss accepted] termination_by structural tree /-- Actual child publications, before the enclosing miss writes its result. -/ @@ -134,6 +141,10 @@ def InferenceCacheTrace.childEvents {fuel : Nat} {before : TcState .anon} {sourc | .lamBody _ _ trace first second => first.events trace.domainRun ++ second.events trace.bodyRun | .letE _ _ trace _ first second third => first.events trace.domainRun ++ second.events trace.valueRun ++ third.events trace.bodyRun + | .forallSort _ trace _ _ first second => first.events trace.domainCheck.inferRun ++ second.events trace.bodyCheck.inferRun + | .lamSort _ _ trace _ first second => first.events trace.domainCheck.inferRun ++ second.events trace.bodyRun + | .letSort _ _ trace _ _ first second third => + first.events trace.domainCheck.inferRun ++ second.events trace.valueRun ++ third.events trace.bodyRun private theorem openBinder_maps {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {domain body opened : KExpr .anon} {fresh : FVarId} {before after : TcState .anon} @@ -307,5 +318,47 @@ theorem InferenceCacheTrace.cache_maps {fuel : Nat} {before after : TcState .ano (comparisonFull.trans (valueFull.trans (congrArg _ domainFull))))), bodyOnly.trans (congrArg _ (opened.2.1.trans (comparisonOnly.trans (valueOnly.trans (congrArg _ domainOnly)))))⟩ + | forallSort miss trace domainExposure bodyExposure domainTree bodyTree domainIH bodyIH => + apply miss_maps miss accepted + (domainTree.events trace.domainCheck.inferRun ++ bodyTree.events trace.bodyCheck.inferRun) + intro middle run + obtain ⟨domainFull, domainOnly⟩ := domainIH trace.domainCheck.inferRun + obtain ⟨bodyFull, bodyOnly⟩ := bodyIH trace.bodyCheck.inferRun + obtain ⟨domainExposedFull, domainExposedOnly⟩ := trace.domainCheck.exposure_maps domainExposure + obtain ⟨bodyExposedFull, bodyExposedOnly⟩ := trace.bodyCheck.exposure_maps bodyExposure + obtain ⟨openedFull, openedOnly⟩ := openBinder_maps trace.openRun + rw [(trace.output_state run).2] + simp only [InferenceCacheEvent.applyFull_append, InferenceCacheEvent.applyOnly_append] + exact ⟨bodyExposedFull.trans (bodyFull.trans (congrArg _ (openedFull.trans (domainExposedFull.trans domainFull)))), + bodyExposedOnly.trans (bodyOnly.trans (congrArg _ (openedOnly.trans (domainExposedOnly.trans domainOnly))))⟩ + | lamSort full miss trace domainExposure domainTree bodyTree domainIH bodyIH => + apply miss_maps miss accepted (domainTree.events trace.domainCheck.inferRun ++ bodyTree.events trace.bodyRun) + intro middle run + rw [full] at run + obtain ⟨domainFull, domainOnly⟩ := domainIH trace.domainCheck.inferRun + obtain ⟨bodyFull, bodyOnly⟩ := bodyIH trace.bodyRun + obtain ⟨domainExposedFull, domainExposedOnly⟩ := trace.domainCheck.exposure_maps domainExposure + obtain ⟨openedFull, openedOnly⟩ := openBinder_maps trace.openRun + rw [(trace.output_state run).2] + simp only [InferenceCacheEvent.applyFull_append, InferenceCacheEvent.applyOnly_append] + exact ⟨bodyFull.trans (congrArg _ (openedFull.trans (domainExposedFull.trans domainFull))), + bodyOnly.trans (congrArg _ (openedOnly.trans (domainExposedOnly.trans domainOnly)))⟩ + | letSort full miss trace domainExposure hashPath domainTree valueTree bodyTree domainIH valueIH bodyIH => + apply miss_maps miss accepted (domainTree.events trace.domainCheck.inferRun ++ valueTree.events trace.valueRun ++ + bodyTree.events trace.bodyRun) + intro middle run + rw [full] at run + obtain ⟨domainFull, domainOnly⟩ := domainIH trace.domainCheck.inferRun + obtain ⟨valueFull, valueOnly⟩ := valueIH trace.valueRun + obtain ⟨bodyFull, bodyOnly⟩ := bodyIH trace.bodyRun + obtain ⟨domainExposedFull, domainExposedOnly⟩ := trace.domainCheck.exposure_maps domainExposure + obtain ⟨comparisonFull, comparisonOnly⟩ := hash_maps hashPath trace.compareRun + have opened := openLet_inference_state trace.openRun + rw [(trace.output_state run).2] + simp only [InferenceCacheEvent.applyFull_append, InferenceCacheEvent.applyOnly_append] + exact ⟨bodyFull.trans (congrArg _ (opened.1.trans + (comparisonFull.trans (valueFull.trans (congrArg _ (domainExposedFull.trans domainFull)))))), + bodyOnly.trans (congrArg _ (opened.2.1.trans + (comparisonOnly.trans (valueOnly.trans (congrArg _ (domainExposedOnly.trans domainOnly))))))⟩ end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean index 6b4c5aaf0..51ee121e3 100644 --- a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean +++ b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean @@ -6,6 +6,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Verify.Consistency.BinderInference import Ix.Kernel.Verify.Consistency.LetInference import Ix.Kernel.Verify.Consistency.WhnfCacheFrame +import Ix.Kernel.Verify.Consistency.SortInference import Ix.Kernel.Verify.Consistency.SourceOwnershipCheck /-! @@ -247,6 +248,33 @@ inductive InferenceCacheTrace : Nat → TcState .anon → KExpr .anon → Type ( (valueTree : InferenceCacheTrace fuel trace.domainState value) (bodyTree : InferenceCacheTrace fuel trace.openedState trace.opened) : InferenceCacheTrace (fuel + 1) before (.letE name domain value body nonDep info) + | forallSort {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals fuel before name bi domain body info domainType bodyType} + (miss : UncachedInference before (.all name bi domain body info)) + (trace : ForallSortInferenceTrace fuel miss.keyed name bi domain body) + (domainExposure : trace.domainCheck.Exposure resolve locals domainType) + (bodyExposure : trace.bodyCheck.Exposure resolve (trace.fresh :: locals) bodyType) + (domainTree : InferenceCacheTrace fuel miss.keyed domain) + (bodyTree : InferenceCacheTrace fuel trace.openedState trace.opened) : + InferenceCacheTrace (fuel + 1) before (.all name bi domain body info) + | lamSort {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals fuel before name bi domain body info domainType} (full : before.inferOnly = false) + (miss : UncachedInference before (.lam name bi domain body info)) + (trace : LambdaSortInferenceTrace fuel miss.keyed name bi domain body) + (domainExposure : trace.domainCheck.Exposure resolve locals domainType) + (domainTree : InferenceCacheTrace fuel miss.keyed domain) + (bodyTree : InferenceCacheTrace fuel trace.openedState trace.opened) : + InferenceCacheTrace (fuel + 1) before (.lam name bi domain body info) + | letSort {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals fuel before name domain value body nonDep info domainType} (full : before.inferOnly = false) + (miss : UncachedInference before (.letE name domain value body nonDep info)) + (trace : LetSortInferenceTrace fuel miss.keyed name domain value body) + (domainExposure : trace.domainCheck.Exposure resolve locals domainType) + (hashPath : (trace.valueType.addr == domain.addr) = true) + (domainTree : InferenceCacheTrace fuel miss.keyed domain) + (valueTree : InferenceCacheTrace fuel trace.domainCheck.after value) + (bodyTree : InferenceCacheTrace fuel trace.openedState trace.opened) : + InferenceCacheTrace (fuel + 1) before (.letE name domain value body nonDep info) /-- The finite write footprint is computed from the operational tree. Cache hits contribute no key; recursive calls and each outer insertion are included. -/ @@ -255,9 +283,10 @@ def InferenceCacheTrace.writes {fuel : Nat} {before : TcState .anon} {term : KEx | .hit _ => [] | .sort miss | .fvar miss | .const miss .. | .lazyConst miss .. => [miss.key] | .app _ miss _ _ first second | .appBeta _ miss _ _ _ first second | - .forallE miss _ first second | .lam _ miss _ first second | .lamBody _ miss _ first second => + .forallE miss _ first second | .lam _ miss _ first second | .lamBody _ miss _ first second | + .forallSort miss _ _ _ first second | .lamSort _ miss _ _ first second => miss.key :: (first.writes ++ second.writes) - | .letE _ miss _ _ first second third => + | .letE _ miss _ _ first second third | .letSort _ miss _ _ _ first second third => miss.key :: (first.writes ++ (second.writes ++ third.writes)) /-- Leaf construction inspects the real cache policy; callers need not @@ -492,6 +521,50 @@ theorem InferenceCacheTrace.frame {fuel : Nat} {before after : TcState .anon} (opening.trans bodyFrame)))).trans (.of_eq rfl rfl rfl), bodyPolicy.trans ((openLet_inference_state trace.openRun).2.2.2.trans (comparisonPolicy.trans (valuePolicy.trans domainPolicy)))⟩ + | forallSort miss trace domainExposure bodyExposure domainTree bodyTree domainIH bodyIH => + simp only [writes, List.mem_cons, List.mem_append, not_or] at outside + apply infer_miss_frame miss (Ne.symm outside.1) accepted + intro middle run + obtain ⟨domainFrame, domainPolicy⟩ := domainIH outside.2.1 trace.domainCheck.inferRun + obtain ⟨bodyFrame, bodyPolicy⟩ := bodyIH outside.2.2 trace.bodyCheck.inferRun + have domainExposed := trace.domainCheck.exposure_frame domainExposure key + have bodyExposed := trace.bodyCheck.exposure_frame bodyExposure key + have opening := openBinder_frame key trace.openRun + rw [(trace.output_state run).2] + exact ⟨(domainFrame.trans (domainExposed.trans (opening.trans (bodyFrame.trans bodyExposed)))).trans + (.of_eq rfl rfl rfl), + (trace.bodyCheck.exposure_policy bodyExposure).trans (bodyPolicy.trans + ((openBinder_policy trace.openRun).trans ((trace.domainCheck.exposure_policy domainExposure).trans domainPolicy)))⟩ + | lamSort full miss trace domainExposure domainTree bodyTree domainIH bodyIH => + simp only [writes, List.mem_cons, List.mem_append, not_or] at outside + apply infer_miss_frame miss (Ne.symm outside.1) accepted + intro middle run + rw [full] at run + obtain ⟨domainFrame, domainPolicy⟩ := domainIH outside.2.1 trace.domainCheck.inferRun + obtain ⟨bodyFrame, bodyPolicy⟩ := bodyIH outside.2.2 trace.bodyRun + have domainExposed := trace.domainCheck.exposure_frame domainExposure key + have opening := openBinder_frame key trace.openRun + rw [(trace.output_state run).2] + exact ⟨(domainFrame.trans (domainExposed.trans (opening.trans bodyFrame))).trans (.of_eq rfl rfl rfl), + bodyPolicy.trans ((openBinder_policy trace.openRun).trans + ((trace.domainCheck.exposure_policy domainExposure).trans domainPolicy))⟩ + | letSort full miss trace domainExposure hashPath domainTree valueTree bodyTree domainIH valueIH bodyIH => + simp only [writes, List.mem_cons, List.mem_append, not_or] at outside + apply infer_miss_frame miss (Ne.symm outside.1) accepted + intro middle run + rw [full] at run + obtain ⟨domainFrame, domainPolicy⟩ := domainIH outside.2.1 trace.domainCheck.inferRun + obtain ⟨valueFrame, valuePolicy⟩ := valueIH outside.2.2.1 trace.valueRun + obtain ⟨bodyFrame, bodyPolicy⟩ := bodyIH outside.2.2.2 trace.bodyRun + obtain ⟨comparisonFrame, comparisonPolicy⟩ := isDefEq_hash_frame hashPath trace.compareRun key + have domainExposed := trace.domainCheck.exposure_frame domainExposure key + have opening := openLet_frame key trace.openRun + rw [(trace.output_state run).2] + exact ⟨(domainFrame.trans (domainExposed.trans (valueFrame.trans (comparisonFrame.trans + (opening.trans bodyFrame))))).trans (.of_eq rfl rfl rfl), + bodyPolicy.trans ((openLet_inference_state trace.openRun).2.2.2.trans + (comparisonPolicy.trans (valuePolicy.trans + ((trace.domainCheck.exposure_policy domainExposure).trans domainPolicy))))⟩ /-- A key already occupied in the full cache cannot be missed. Key memoization leaves the maps unchanged, and full entries precede both policies. -/ @@ -580,6 +653,42 @@ theorem InferenceCacheTrace.populated_outside {fuel : Nat} {before : TcState .an exact by simpa only [writes, List.mem_cons, List.mem_append, not_or] using ⟨miss.ne_populated stored, first, second, bodyIH next⟩ + | forallSort miss trace domainExposure bodyExposure domainTree bodyTree domainIH bodyIH => + have keyed : miss.keyed.env.inferCache[key]? = some cached := by + rw [inferKey_environment miss.keyRun] + exact stored + have first := domainIH keyed + have inferred := (domainTree.frame first trace.domainCheck.inferRun).1.full.trans keyed + have exposed := (trace.domainCheck.exposure_frame domainExposure key).full.trans inferred + have next := (openBinder_frame key trace.openRun).full.trans exposed + exact by + simpa only [writes, List.mem_cons, List.mem_append, not_or] using + ⟨miss.ne_populated stored, first, bodyIH next⟩ + | lamSort full miss trace domainExposure domainTree bodyTree domainIH bodyIH => + have keyed : miss.keyed.env.inferCache[key]? = some cached := by + rw [inferKey_environment miss.keyRun] + exact stored + have first := domainIH keyed + have inferred := (domainTree.frame first trace.domainCheck.inferRun).1.full.trans keyed + have exposed := (trace.domainCheck.exposure_frame domainExposure key).full.trans inferred + have next := (openBinder_frame key trace.openRun).full.trans exposed + exact by + simpa only [writes, List.mem_cons, List.mem_append, not_or] using + ⟨miss.ne_populated stored, first, bodyIH next⟩ + | letSort full miss trace domainExposure hashPath domainTree valueTree bodyTree domainIH valueIH bodyIH => + have keyed : miss.keyed.env.inferCache[key]? = some cached := by + rw [inferKey_environment miss.keyRun] + exact stored + have first := domainIH keyed + have inferred := (domainTree.frame first trace.domainCheck.inferRun).1.full.trans keyed + have exposed := (trace.domainCheck.exposure_frame domainExposure key).full.trans inferred + have second := valueIH exposed + have checked := (valueTree.frame second trace.valueRun).1.full.trans exposed + have compared := (isDefEq_hash_frame hashPath trace.compareRun key).1.full.trans checked + have next := (openLet_frame key trace.openRun).full.trans compared + exact by + simpa only [writes, List.mem_cons, List.mem_append, not_or] using + ⟨miss.ne_populated stored, first, second, bodyIH next⟩ /-- A successful recursive call preserves both partitions at each initially populated full key, together with every previously loaded declaration. -/ diff --git a/Ix/Kernel/Verify/Consistency/SortInference.lean b/Ix/Kernel/Verify/Consistency/SortInference.lean new file mode 100644 index 000000000..2b142ee83 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SortInference.lean @@ -0,0 +1,559 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BinderInference +import Ix.Kernel.Verify.Consistency.LetInference +import Ix.Kernel.Verify.Consistency.WhnfCacheFrame + +/-! Actual binder inference calls with explicit sort exposure. The inferred +type may require reduction before the checker obtains its universe level. +Context restoration and freshness follow from the complete recursive checker. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +/-- The recursive inference call and the following real sort-exposure call. +No syntactic shape or semantic typing is assumed for the inferred expression. -/ +structure SortInferenceTrace (fuel : Nat) (before : TcState .anon) (source : KExpr .anon) where + inferred : KExpr .anon + inferredState : TcState .anon + level : KUniv .anon + after : TcState .anon + inferRun : RecM.infer source (methodsN fuel) before = .ok inferred inferredState + exposureRun : (RecM.ensureSortDirect inferred).run (methodsN (fuel + 1)) inferredState = .ok level after + +namespace SortInferenceTrace + +variable {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} + +def direct {level : KUniv .anon} {info : ExprInfo .anon} {after : TcState .anon} + (accepted : RecM.infer source (methodsN fuel) before = .ok (.sort level info) after) : + SortInferenceTrace fuel before source := + ⟨.sort level info, after, level, after, accepted, rfl⟩ + +/-- Successful execution determines the inferred type, exposure level, +and both intermediate states; callers need not supply those observations. -/ +theorem success {next : KUniv .anon → RecM .anon α} {result : α} {finished : TcState .anon} + (accepted : (RecM.inferCall source >>= fun type => RecM.ensureSortDirect type >>= next).run + (methodsN (fuel + 1)) before = .ok result finished) : + ∃ trace : SortInferenceTrace fuel before source, + (next trace.level).run (methodsN (fuel + 1)) trace.after = .ok result finished := by + simp only [ReaderT.run_bind] at accepted + change EStateM.bind (RecM.infer source (methodsN fuel)) _ before = _ at accepted + cases inferredRun : RecM.infer source (methodsN fuel) before with + | error error failed => rw [EStateM.bind, inferredRun] at accepted; contradiction + | ok type inferredState => + rw [EStateM.bind, inferredRun] at accepted + change EStateM.bind ((RecM.ensureSortDirect type).run (methodsN (fuel + 1))) _ inferredState = _ at accepted + cases exposedRun : (RecM.ensureSortDirect type).run (methodsN (fuel + 1)) inferredState with + | error error failed => rw [EStateM.bind, exposedRun] at accepted; contradiction + | ok level after => + rw [EStateM.bind, exposedRun] at accepted + exact ⟨⟨type, inferredState, level, after, inferredRun, exposedRun⟩, accepted⟩ + +theorem inferenceFrame (trace : SortInferenceTrace fuel before source) + (valid : LocalStateInvariant before) : LocalStateFrame before trace.inferredState := + (infer_methodsN_framesLocalState fuel source).ok valid trace.inferRun + +theorem frame (trace : SortInferenceTrace fuel before source) + (valid : LocalStateInvariant before) : LocalStateFrame before trace.after := + (trace.inferenceFrame valid).trans + ((FramesLocalState.ensureSortDirect (FramesLocalState.whnf (MethodsLocalState.methodsN (fuel + 1))) + trace.inferred).ok ((trace.inferenceFrame valid).invariant valid) trace.exposureRun) + +abbrev Exposure {β : Type u} (trace : SortInferenceTrace fuel before source) + (resolve : Address → Option (ConstRef β)) (locals : List FVarId) (term : AExpr β) := + BetaSortExposure resolve locals fuel trace.inferredState trace.inferred term trace.level + +theorem exposure_state {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {term : AExpr β} (trace : SortInferenceTrace fuel before source) + (exposure : trace.Exposure resolve locals term) : trace.after = exposure.after := by + have run := trace.exposureRun + rw [exposure.run] at run + exact (EStateM.Result.ok.inj run).2.symm + +theorem exposure_frame {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {term : AExpr β} (trace : SortInferenceTrace fuel before source) + (exposure : trace.Exposure resolve locals term) (key : Address × Address) : + InferenceCacheFrame key trace.inferredState trace.after := + trace.exposure_state exposure ▸ exposure.inference_frame key + +theorem exposure_policy {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {term : AExpr β} (trace : SortInferenceTrace fuel before source) + (exposure : trace.Exposure resolve locals term) : trace.after.inferOnly = trace.inferredState.inferOnly := + trace.exposure_state exposure ▸ exposure.policy + +theorem exposure_maps {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {term : AExpr β} (trace : SortInferenceTrace fuel before source) + (exposure : trace.Exposure resolve locals term) : + trace.after.env.inferCache = trace.inferredState.env.inferCache ∧ + trace.after.env.inferOnlyCache = trace.inferredState.env.inferOnlyCache := + trace.exposure_state exposure ▸ exposure.inference_maps + +end SortInferenceTrace + +private theorem withLctxScope_success {action : RecM .anon α} + {methods : Methods .anon} {before finished : TcState .anon} {result : α} + (accepted : action.withLctxScope.run methods before = .ok result finished) : + ∃ middle, action.run methods before = .ok result middle ∧ + finished = {middle with lctx := middle.lctx.truncate before.lctx.size} := by + rw [withLctxScope_eq] at accepted + cases step : action.run methods before with + | error error failed => rw [step] at accepted; contradiction + | ok value middle => rw [step] at accepted; cases accepted; exact ⟨middle, rfl, rfl⟩ + +/-- Both dependent-function children may need WHNF to expose their sort. -/ +structure ForallSortInferenceTrace (fuel : Nat) (before : TcState .anon) + (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) + (domain body : KExpr .anon) where + localState : LocalStateInvariant before + domainCheck : SortInferenceTrace fuel before domain + opened : KExpr .anon + fresh : FVarId + openedState : TcState .anon + openRun : TcM.openBinder name bi domain body domainCheck.after = .ok (opened, fresh) openedState + bodyCheck : SortInferenceTrace fuel openedState opened + +namespace ForallSortInferenceTrace + +variable {fuel : Nat} {before : TcState .anon} {name : Mode.anon.F Name} + {bi : Mode.anon.F Lean.BinderInfo} {domain body : KExpr .anon} + +theorem domainFrame (trace : ForallSortInferenceTrace fuel before name bi domain body) : + LocalStateFrame before trace.domainCheck.after := trace.domainCheck.frame trace.localState + +theorem opened_reading {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {context : Model.Context β} {A : AExpr β} {b : VExpr β} + (trace : ForallSortInferenceTrace fuel before name bi domain body) + (opening : BinderOpeningSupport trace.domainCheck.after body) + (agreement : LocalContextReading resolve locals before.lctx context) + (domainReading : readScopedExpr? resolve locals domain = some A.erase) + (bodyReading : readScopedExpr? resolve locals body 1 = some b) : + readScopedExpr? resolve (trace.fresh :: locals) trace.opened = some b ∧ + LocalContextReading resolve (trace.fresh :: locals) trace.openedState.lctx (context.push A) ∧ + trace.openedState.env.intern.WF := by + have frame := trace.domainFrame + have domainAgreement := agreement.congr frame.context.symm + exact (openBinder_sound opening domainAgreement + ((frame.invariant trace.localState).freshReading domainAgreement) domainReading bodyReading trace.openRun).2 + +def interned (trace : ForallSortInferenceTrace fuel before name bi domain body) := + trace.bodyCheck.after.env.intern.internExpr + (KExpr.mkSort (KUniv.mkIMax trace.domainCheck.level trace.bodyCheck.level)) + +def after (trace : ForallSortInferenceTrace fuel before name bi domain body) : TcState .anon := + {trace.bodyCheck.after with + env := {trace.bodyCheck.after.env with intern := trace.interned.2} + lctx := trace.bodyCheck.after.lctx.truncate trace.domainCheck.after.lctx.size} + +/-- Invert successful production inference to recover the complete call +trace, including either sort-exposure branch at both children. -/ +theorem success {finished : TcState .anon} {result : KExpr .anon} + {info : ExprInfo .anon} {inferOnly : Bool} (valid : LocalStateInvariant before) + (accepted : RecM.inferUncached RecM.inferCall inferOnly (.all name bi domain body info) + (methodsN (fuel + 1)) before = .ok result finished) : + Nonempty (ForallSortInferenceTrace fuel before name bi domain body) := by + change (RecM.inferUncached RecM.inferCall inferOnly (.all name bi domain body info)).run + (methodsN (fuel + 1)) before = _ at accepted + unfold RecM.inferUncached at accepted + obtain ⟨domainCheck, accepted⟩ := SortInferenceTrace.success accepted + change (RecM.withLctxScope _).run (methodsN (fuel + 1)) domainCheck.after = _ at accepted + obtain ⟨middle, accepted, _⟩ := withLctxScope_success accepted + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted + change EStateM.bind (TcM.openBinder name bi domain body) _ domainCheck.after = _ at accepted + cases openRun : TcM.openBinder name bi domain body domainCheck.after with + | error error failed => rw [EStateM.bind, openRun] at accepted; contradiction + | ok pair openedState => + rcases pair with ⟨opened, fresh⟩ + rw [EStateM.bind, openRun] at accepted + obtain ⟨bodyCheck, _⟩ := SortInferenceTrace.success (fuel := fuel) (before := openedState) + (source := opened) (next := fun level => + liftM (TcM.intern (KExpr.mkSort (KUniv.mkIMax domainCheck.level level)))) accepted + exact ⟨⟨valid, domainCheck, opened, fresh, openedState, openRun, bodyCheck⟩⟩ + +noncomputable def ofSuccess {finished : TcState .anon} {result : KExpr .anon} + {info : ExprInfo .anon} {inferOnly : Bool} (valid : LocalStateInvariant before) + (accepted : RecM.inferUncached RecM.inferCall inferOnly (.all name bi domain body info) + (methodsN (fuel + 1)) before = .ok result finished) : + ForallSortInferenceTrace fuel before name bi domain body := + Classical.choice (success valid accepted) + +/-- A successful cache miss supplies the branch trace and its initial +local invariant from the ordinary inference entry point. -/ +noncomputable def ofInference {finished : TcState .anon} {result : KExpr .anon} {info : ExprInfo .anon} + (miss : UncachedInference before (.all name bi domain body info)) + (valid : LocalStateInvariant before) + (accepted : RecM.infer (.all name bi domain body info) (methodsN (fuel + 1)) before = .ok result finished) : + ForallSortInferenceTrace fuel miss.keyed name bi domain body := + Classical.choice (by + obtain ⟨middle, run⟩ := infer_uncached_success miss accepted + exact success (miss.keyedLocalState valid) run) + +theorem output_state {finished : TcState .anon} {result : KExpr .anon} + {info : ExprInfo .anon} {inferOnly : Bool} + (trace : ForallSortInferenceTrace fuel before name bi domain body) + (accepted : RecM.inferUncached RecM.inferCall inferOnly (.all name bi domain body info) + (methodsN (fuel + 1)) before = .ok result finished) : + result = trace.interned.1 ∧ finished = trace.after := by + change (RecM.inferUncached RecM.inferCall inferOnly (.all name bi domain body info)).run + (methodsN (fuel + 1)) before = _ at accepted + unfold RecM.inferUncached at accepted + simp only [ReaderT.run_bind] at accepted + change EStateM.bind (RecM.infer domain (methodsN fuel)) _ before = _ at accepted + rw [EStateM.bind, trace.domainCheck.inferRun] at accepted + change EStateM.bind ((RecM.ensureSortDirect trace.domainCheck.inferred).run (methodsN (fuel + 1))) + _ trace.domainCheck.inferredState = _ at accepted + rw [EStateM.bind, trace.domainCheck.exposureRun] at accepted + change (RecM.withLctxScope _).run (methodsN (fuel + 1)) trace.domainCheck.after = _ at accepted + obtain ⟨middle, accepted, cleanup⟩ := withLctxScope_success accepted + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted + change EStateM.bind (TcM.openBinder name bi domain body) _ trace.domainCheck.after = + .ok result middle at accepted + rw [EStateM.bind, trace.openRun] at accepted + change EStateM.bind (RecM.infer trace.opened (methodsN fuel)) _ trace.openedState = _ at accepted + rw [EStateM.bind, trace.bodyCheck.inferRun] at accepted + change EStateM.bind ((RecM.ensureSortDirect trace.bodyCheck.inferred).run (methodsN (fuel + 1))) + _ trace.bodyCheck.inferredState = _ at accepted + rw [EStateM.bind, trace.bodyCheck.exposureRun] at accepted + cases accepted + exact ⟨rfl, cleanup⟩ + +end ForallSortInferenceTrace + +/-- Lambda inference validates its domain through the actual sort exposure, +then infers its opened body before cheap beta and abstraction. -/ +structure LambdaSortInferenceTrace (fuel : Nat) (before : TcState .anon) + (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) + (domain body : KExpr .anon) where + localState : LocalStateInvariant before + domainCheck : SortInferenceTrace fuel before domain + opened : KExpr .anon + fresh : FVarId + openedState : TcState .anon + bodyType : KExpr .anon + bodyState : TcState .anon + openRun : TcM.openBinder name bi domain body domainCheck.after = .ok (opened, fresh) openedState + bodyRun : RecM.infer opened (methodsN fuel) openedState = .ok bodyType bodyState + +namespace LambdaSortInferenceTrace + +variable {fuel : Nat} {before : TcState .anon} {name : Mode.anon.F Name} + {bi : Mode.anon.F Lean.BinderInfo} {domain body : KExpr .anon} + +theorem domainFrame (trace : LambdaSortInferenceTrace fuel before name bi domain body) : + LocalStateFrame before trace.domainCheck.after := trace.domainCheck.frame trace.localState + +theorem opened_reading {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {context : Model.Context β} {A : AExpr β} {b : VExpr β} + (trace : LambdaSortInferenceTrace fuel before name bi domain body) + (opening : BinderOpeningSupport trace.domainCheck.after body) + (agreement : LocalContextReading resolve locals before.lctx context) + (domainReading : readScopedExpr? resolve locals domain = some A.erase) + (bodyReading : readScopedExpr? resolve locals body 1 = some b) : + readScopedExpr? resolve (trace.fresh :: locals) trace.opened = some b ∧ + LocalContextReading resolve (trace.fresh :: locals) trace.openedState.lctx (context.push A) ∧ + trace.openedState.env.intern.WF := by + have frame := trace.domainFrame + have domainAgreement := agreement.congr frame.context.symm + exact (openBinder_sound opening domainAgreement + ((frame.invariant trace.localState).freshReading domainAgreement) domainReading bodyReading trace.openRun).2 + +def reduced (trace : LambdaSortInferenceTrace fuel before name bi domain body) := + cheapBetaReduce trace.bodyType trace.bodyState.env.intern + +def abstracted (trace : LambdaSortInferenceTrace fuel before name bi domain body) := + abstractFVars trace.reduced.1 #[trace.fresh] trace.reduced.2 + +def interned (trace : LambdaSortInferenceTrace fuel before name bi domain body) := + trace.abstracted.2.internExpr (KExpr.mkAll () () domain trace.abstracted.1) + +def after (trace : LambdaSortInferenceTrace fuel before name bi domain body) : TcState .anon := + {trace.bodyState with + env := {trace.bodyState.env with intern := trace.interned.2} + lctx := trace.bodyState.lctx.truncate trace.domainCheck.after.lctx.size} + +theorem success {finished : TcState .anon} {result : KExpr .anon} {info : ExprInfo .anon} + (valid : LocalStateInvariant before) + (accepted : RecM.inferUncached RecM.inferCall false (.lam name bi domain body info) + (methodsN (fuel + 1)) before = .ok result finished) : + Nonempty (LambdaSortInferenceTrace fuel before name bi domain body) := by + change (RecM.inferUncached RecM.inferCall false (.lam name bi domain body info)).run + (methodsN (fuel + 1)) before = _ at accepted + unfold RecM.inferUncached at accepted + simp only [Bool.not_false, if_true] at accepted + obtain ⟨domainCheck, accepted⟩ := SortInferenceTrace.success accepted + change (RecM.withLctxScope _).run (methodsN (fuel + 1)) domainCheck.after = _ at accepted + obtain ⟨middle, accepted, _⟩ := withLctxScope_success accepted + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted + change EStateM.bind (TcM.openBinder name bi domain body) _ domainCheck.after = _ at accepted + cases openRun : TcM.openBinder name bi domain body domainCheck.after with + | error error failed => rw [EStateM.bind, openRun] at accepted; contradiction + | ok pair openedState => + rcases pair with ⟨opened, fresh⟩ + rw [EStateM.bind, openRun] at accepted + change EStateM.bind (RecM.infer opened (methodsN fuel)) _ openedState = _ at accepted + cases bodyRun : RecM.infer opened (methodsN fuel) openedState with + | error error failed => rw [EStateM.bind, bodyRun] at accepted; contradiction + | ok bodyType bodyState => + exact ⟨⟨valid, domainCheck, opened, fresh, openedState, bodyType, bodyState, openRun, bodyRun⟩⟩ + +noncomputable def ofSuccess {finished : TcState .anon} {result : KExpr .anon} {info : ExprInfo .anon} + (valid : LocalStateInvariant before) + (accepted : RecM.inferUncached RecM.inferCall false (.lam name bi domain body info) + (methodsN (fuel + 1)) before = .ok result finished) : + LambdaSortInferenceTrace fuel before name bi domain body := Classical.choice (success valid accepted) + +noncomputable def ofInference {finished : TcState .anon} {result : KExpr .anon} {info : ExprInfo .anon} + (full : before.inferOnly = false) + (miss : UncachedInference before (.lam name bi domain body info)) + (valid : LocalStateInvariant before) + (accepted : RecM.infer (.lam name bi domain body info) (methodsN (fuel + 1)) before = .ok result finished) : + LambdaSortInferenceTrace fuel miss.keyed name bi domain body := + Classical.choice (by + obtain ⟨middle, run⟩ := infer_uncached_success miss accepted + rw [full] at run + exact success (miss.keyedLocalState valid) run) + +theorem output_state {finished : TcState .anon} {result : KExpr .anon} {info : ExprInfo .anon} + (trace : LambdaSortInferenceTrace fuel before name bi domain body) + (accepted : RecM.inferUncached RecM.inferCall false (.lam name bi domain body info) + (methodsN (fuel + 1)) before = .ok result finished) : + result = trace.interned.1 ∧ finished = trace.after := by + change (RecM.inferUncached RecM.inferCall false (.lam name bi domain body info)).run + (methodsN (fuel + 1)) before = _ at accepted + unfold RecM.inferUncached at accepted + simp only [Bool.not_false, if_true, ReaderT.run_bind] at accepted + change EStateM.bind (RecM.infer domain (methodsN fuel)) _ before = _ at accepted + rw [EStateM.bind, trace.domainCheck.inferRun] at accepted + change EStateM.bind ((RecM.ensureSortDirect trace.domainCheck.inferred).run (methodsN (fuel + 1))) + _ trace.domainCheck.inferredState = _ at accepted + rw [EStateM.bind, trace.domainCheck.exposureRun] at accepted + change (RecM.withLctxScope _).run (methodsN (fuel + 1)) trace.domainCheck.after = _ at accepted + obtain ⟨middle, accepted, cleanup⟩ := withLctxScope_success accepted + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted + change EStateM.bind (TcM.openBinder name bi domain body) _ trace.domainCheck.after = + .ok result middle at accepted + rw [EStateM.bind, trace.openRun] at accepted + change EStateM.bind (RecM.infer trace.opened (methodsN fuel)) _ trace.openedState = _ at accepted + rw [EStateM.bind, trace.bodyRun] at accepted + cases accepted + exact ⟨rfl, cleanup⟩ + +end LambdaSortInferenceTrace + +/-- Full let inference records domain sort exposure before the value check. -/ +structure LetSortInferenceTrace (fuel : Nat) (before : TcState .anon) + (name : Mode.anon.F Name) (domain value body : KExpr .anon) where + domainCheck : SortInferenceTrace fuel before domain + valueType : KExpr .anon + valueState : TcState .anon + comparedState : TcState .anon + opened : KExpr .anon + fresh : FVarId + openedState : TcState .anon + bodyType : KExpr .anon + bodyState : TcState .anon + valueRun : RecM.infer value (methodsN fuel) domainCheck.after = .ok valueType valueState + compareRun : RecM.isDefEq valueType domain (methodsN fuel) valueState = .ok true comparedState + openRun : TcM.openLet name domain value body comparedState = .ok (opened, fresh) openedState + bodyRun : RecM.infer opened (methodsN fuel) openedState = .ok bodyType bodyState + +namespace LetSortInferenceTrace + +variable {fuel : Nat} {before : TcState .anon} {name : Mode.anon.F Name} + {domain value body : KExpr .anon} + +theorem domainFrame (trace : LetSortInferenceTrace fuel before name domain value body) + (valid : LocalStateInvariant before) : LocalStateFrame before trace.domainCheck.after := + trace.domainCheck.frame valid + +theorem valueFrame (trace : LetSortInferenceTrace fuel before name domain value body) + (valid : LocalStateInvariant before) : LocalStateFrame before trace.valueState := + (trace.domainFrame valid).trans ((infer_methodsN_framesLocalState fuel value).ok + ((trace.domainFrame valid).invariant valid) trace.valueRun) + +theorem openingFrame (trace : LetSortInferenceTrace fuel before name domain value body) + (valid : LocalStateInvariant before) : LocalStateFrame before trace.comparedState := + (trace.valueFrame valid).trans ((isDefEq_methodsN_framesLocalState fuel trace.valueType domain).ok + ((trace.valueFrame valid).invariant valid) trace.compareRun) + +theorem domainContext (trace : LetSortInferenceTrace fuel before name domain value body) + (valid : LocalStateInvariant before) : trace.domainCheck.after.lctx.Equiv before.lctx := + (trace.domainFrame valid).context + +theorem openingContext (trace : LetSortInferenceTrace fuel before name domain value body) + (valid : LocalStateInvariant before) : trace.comparedState.lctx.Equiv before.lctx := + (trace.openingFrame valid).context + +/-- All consumers use the same executed opening and its derived freshness. -/ +theorem opened_reading {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {context : Model.Context β} {A : AExpr β} {b : VExpr β} + (trace : LetSortInferenceTrace fuel before name domain value body) + (opening : BinderOpeningSupport trace.comparedState body) + (valid : LocalStateInvariant before) + (agreement : LocalContextReading resolve locals before.lctx context) + (domainReading : readScopedExpr? resolve locals domain = some A.erase) + (bodyReading : readScopedExpr? resolve locals body 1 = some b) : + readScopedExpr? resolve (trace.fresh :: locals) trace.opened = some b ∧ + LocalContextReading resolve (trace.fresh :: locals) trace.openedState.lctx (context.push A) ∧ + trace.openedState.env.intern.WF := by + have frame := trace.openingFrame valid + have openingAgreement := agreement.congr frame.context.symm + exact (openLet_sound opening openingAgreement + ((frame.invariant valid).freshReading openingAgreement) domainReading bodyReading trace.openRun).2 + +def abstracted (trace : LetSortInferenceTrace fuel before name domain value body) := + abstractFVars trace.bodyType #[trace.fresh] trace.bodyState.env.intern + +def substituted (trace : LetSortInferenceTrace fuel before name domain value body) := + subst trace.abstracted.1 value 0 trace.abstracted.2 + +def reduced (trace : LetSortInferenceTrace fuel before name domain value body) := + cheapBetaReduce trace.substituted.1 trace.substituted.2 + +def after (trace : LetSortInferenceTrace fuel before name domain value body) : TcState .anon := + {trace.bodyState with + env := {trace.bodyState.env with intern := trace.reduced.2} + lctx := trace.bodyState.lctx.truncate trace.comparedState.lctx.size} + +/-- The whole let scope restores the incoming declarations while retaining +the fresh counter and the body computation's final intern table. -/ +theorem restores (trace : LetSortInferenceTrace fuel before name domain value body) + (valid : LocalStateInvariant before) : LocalStateFrame before trace.after := by + have compared := trace.openingFrame valid + have opened := PreservesLocalState.openLet name domain value body trace.comparedState + (compared.invariant valid) + rw [trace.openRun] at opened + have bodyFrame := (infer_methodsN_framesLocalState fuel trace.opened).ok opened.valid trace.bodyRun + have restored := (opened.trans (.of_frame opened.valid bodyFrame)).restore + exact compared.trans ⟨restored.counter, restored.context, restored.loader⟩ + +/-- Invert the production branch through all three calls and both walkers. +Scope cleanup retains the final intern table and all child cache writes. -/ +theorem success {result : KExpr .anon} {finished : TcState .anon} + {nonDep : Bool} {info : ExprInfo .anon} + (accepted : RecM.inferUncached RecM.inferCall false (.letE name domain value body nonDep info) + (methodsN (fuel + 1)) before = .ok result finished) : + Nonempty (LetSortInferenceTrace fuel before name domain value body) := by + change (RecM.inferUncached RecM.inferCall false (.letE name domain value body nonDep info)).run + (methodsN (fuel + 1)) before = _ at accepted + unfold RecM.inferUncached at accepted + simp only [Bool.not_false, if_true] at accepted + obtain ⟨domainCheck, accepted⟩ := SortInferenceTrace.success accepted + change EStateM.bind (RecM.infer value (methodsN fuel)) _ domainCheck.after = _ at accepted + cases valueRun : RecM.infer value (methodsN fuel) domainCheck.after with + | error error failed => rw [EStateM.bind, valueRun] at accepted; contradiction + | ok valueType valueState => + rw [EStateM.bind, valueRun] at accepted + change EStateM.bind (RecM.isDefEq valueType domain (methodsN fuel)) _ valueState = _ at accepted + cases compareRun : RecM.isDefEq valueType domain (methodsN fuel) valueState with + | error error failed => rw [EStateM.bind, compareRun] at accepted; contradiction + | ok equal comparedState => + cases equal + · rw [EStateM.bind, compareRun] at accepted; contradiction + · rw [EStateM.bind, compareRun] at accepted + change (RecM.withLctxScope _).run (methodsN (fuel + 1)) comparedState = _ at accepted + obtain ⟨middle, accepted, _⟩ := withLctxScope_success accepted + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted + change EStateM.bind (TcM.openLet name domain value body) _ comparedState = _ at accepted + cases openRun : TcM.openLet name domain value body comparedState with + | error error failed => rw [EStateM.bind, openRun] at accepted; contradiction + | ok pair openedState => + rcases pair with ⟨opened, fresh⟩ + rw [EStateM.bind, openRun] at accepted + change EStateM.bind (RecM.infer opened (methodsN fuel)) _ openedState = _ at accepted + cases bodyRun : RecM.infer opened (methodsN fuel) openedState with + | error error failed => rw [EStateM.bind, bodyRun] at accepted; contradiction + | ok bodyType bodyState => + exact ⟨⟨domainCheck, valueType, valueState, comparedState, opened, fresh, openedState, + bodyType, bodyState, valueRun, compareRun, openRun, bodyRun⟩⟩ + +noncomputable def ofSuccess {result : KExpr .anon} {finished : TcState .anon} + {nonDep : Bool} {info : ExprInfo .anon} + (accepted : RecM.inferUncached RecM.inferCall false (.letE name domain value body nonDep info) + (methodsN (fuel + 1)) before = .ok result finished) : + LetSortInferenceTrace fuel before name domain value body := Classical.choice (success accepted) + +noncomputable def ofInference {result : KExpr .anon} {finished : TcState .anon} + {nonDep : Bool} {info : ExprInfo .anon} (full : before.inferOnly = false) + (miss : UncachedInference before (.letE name domain value body nonDep info)) + (accepted : RecM.infer (.letE name domain value body nonDep info) (methodsN (fuel + 1)) before = + .ok result finished) : LetSortInferenceTrace fuel miss.keyed name domain value body := + Classical.choice (by + obtain ⟨middle, run⟩ := infer_uncached_success miss accepted + rw [full] at run + exact success run) + +theorem output_state {result : KExpr .anon} {finished : TcState .anon} + {nonDep : Bool} {info : ExprInfo .anon} + (trace : LetSortInferenceTrace fuel before name domain value body) + (accepted : RecM.inferUncached RecM.inferCall false (.letE name domain value body nonDep info) + (methodsN (fuel + 1)) before = .ok result finished) : + result = trace.reduced.1 ∧ finished = trace.after := by + change (RecM.inferUncached RecM.inferCall false (.letE name domain value body nonDep info)).run + (methodsN (fuel + 1)) before = _ at accepted + unfold RecM.inferUncached at accepted + simp only [Bool.not_false, if_true, ReaderT.run_bind] at accepted + change EStateM.bind (RecM.infer domain (methodsN fuel)) _ before = _ at accepted + rw [EStateM.bind, trace.domainCheck.inferRun] at accepted + change EStateM.bind ((RecM.ensureSortDirect trace.domainCheck.inferred).run (methodsN (fuel + 1))) + _ trace.domainCheck.inferredState = _ at accepted + rw [EStateM.bind, trace.domainCheck.exposureRun] at accepted + change EStateM.bind (RecM.infer value (methodsN fuel)) _ trace.domainCheck.after = _ at accepted + rw [EStateM.bind, trace.valueRun] at accepted + change EStateM.bind (RecM.isDefEq trace.valueType domain (methodsN fuel)) _ trace.valueState = _ at accepted + rw [EStateM.bind, trace.compareRun] at accepted + change (RecM.withLctxScope _).run (methodsN (fuel + 1)) trace.comparedState = _ at accepted + obtain ⟨middle, accepted, cleanup⟩ := withLctxScope_success accepted + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at accepted + change EStateM.bind (TcM.openLet name domain value body) _ trace.comparedState = + .ok result middle at accepted + rw [EStateM.bind, trace.openRun] at accepted + have bodyRun : (RecM.inferCall trace.opened).run (methodsN (fuel + 1)) trace.openedState = + .ok trace.bodyType trace.bodyState := trace.bodyRun + change EStateM.bind ((RecM.inferCall trace.opened).run (methodsN (fuel + 1))) + _ trace.openedState = .ok result middle at accepted + rw [EStateM.bind, bodyRun] at accepted + cases accepted + exact ⟨rfl, cleanup⟩ + +/-- Finite resources for the two production binder walkers. The second +walker uses the table produced by the first one, not the entry table. -/ +structure SubstitutionSupport (trace : LetSortInferenceTrace fuel before name domain value body) : Prop where + bodyConstructed : trace.bodyType.Constructed + bodyBound : trace.bodyType.size + 1 < UInt64.size + coherent : trace.bodyState.env.intern.WF + closingFaithful : KExpr.CollisionFree fun term => trace.bodyState.env.intern.ExprSupport term ∨ + KExpr.AbstractReach ((∅ : Std.HashMap FVarId UInt64).insert trace.fresh 0) 1 trace.bodyType 0 term + abstractedConstructed : trace.abstracted.1.Constructed + abstractedBound : trace.abstracted.1.size + 1 < UInt64.size + valueConstructed : value.Constructed + valueBound : value.size < UInt64.size + substitutionFaithful : KExpr.CollisionFree fun term => trace.abstracted.2.ExprSupport term ∨ + KExpr.SubstReach value trace.abstracted.1 0 term + +/-- Abstraction and substitution remove the fresh let local from the +inferred body type, using the exact value supplied to the production branch. -/ +theorem substituted_reading {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {B v : AExpr β} + (trace : LetSortInferenceTrace fuel before name domain value body) + (support : trace.SubstitutionSupport) + (bodyReading : readScopedExpr? resolve (trace.fresh :: locals) trace.bodyType = some B.erase) + (valueReading : readScopedExpr? resolve locals value = some v.erase) : + readScopedExpr? resolve locals trace.substituted.1 = some (B.inst v).erase ∧ + trace.substituted.2.WF := by + obtain ⟨closedReading, closedCoherent⟩ := abstractFVars_readScopedExpr? + support.bodyConstructed support.bodyBound support.coherent support.closingFaithful bodyReading + obtain ⟨resultReading, resultCoherent⟩ := subst_readScopedExpr? + support.abstractedConstructed support.valueConstructed support.abstractedBound support.valueBound + closedCoherent support.substitutionFaithful closedReading valueReading + exact ⟨by simpa only [substituted, abstracted, AExpr.erase_inst] using resultReading, resultCoherent⟩ + +end LetSortInferenceTrace + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean b/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean index fab9b2183..9631a19dd 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean @@ -92,10 +92,18 @@ def complete (data : SynthesisCacheSupplement resolve anchor entries fuel before | lamBody full miss trace domainTree bodyTree => exact children.append (.singleton (.ofSource tree contextOrigin agreement reading miss accepted)) | letE full miss trace hashPath domainTree valueTree bodyTree => + exact children.append (.singleton (.ofSource tree contextOrigin agreement reading miss accepted)) + | forallSort miss trace domainExposure bodyExposure domainTree bodyTree => + exact children.append (.singleton (.ofSource tree contextOrigin agreement reading miss accepted)) + | lamSort full miss trace domainExposure domainTree bodyTree => + exact children.append (.singleton (.ofSource tree contextOrigin agreement reading miss accepted)) + | letSort full miss trace domainExposure hashPath domainTree valueTree bodyTree => exact children.append (.singleton (.ofSource tree contextOrigin agreement reading miss accepted)) } end SynthesisCacheSupplement +mutual + /-- Rich synthesis nodes already retain both actual children. Additional checking annotations are needed only at the older BinderInference wrappers; primitive leaves supply none. Existing cache hits add no publications. -/ @@ -113,8 +121,25 @@ def SynthesisInference.CacheData {β : Type u} {resolve : Address → Option (Co .lamBeta _ _ _ _ first second .. => first.CacheData anchor × second.CacheData anchor | .letE _ _ _ _ _ domain value body .. => domain.CacheData anchor × value.CacheData anchor × body.CacheData anchor + | .forallSort _ _ _ domain body .. => domain.CacheData anchor × body.CacheData anchor + | .lamSort _ _ _ _ domain body .. => domain.CacheData anchor × body.CacheData anchor + | .letSort _ _ _ _ _ domain value body .. => + domain.CacheData anchor × value.CacheData anchor × body.CacheData anchor termination_by structural tree +def SynthesisSortCheck.CacheData {β : Type u} {resolve : Address → Option (ConstRef β)} + (anchor : Model.Environment β) {entries : Model.Environment β} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} {before : TcState .anon} + {source : KExpr .anon} {trace : SortInferenceTrace fuel before source} {term : AExpr β} + (check : SynthesisSortCheck resolve entries locals context bounds trace term) : Type (u + 1) := + match check with + | .checked tree .. => tree.CacheData anchor +termination_by structural check + +end + +mutual + /-- Extract every full publication's checking origin from the original synthesis recursion. Binder contexts come from its executed domain checks, and all child readings and local agreements are derived along the same run. -/ @@ -224,8 +249,74 @@ def SynthesisInference.cacheExecution {β : Type u} {resolve : Address → Optio ((domainRun.checks.append valueRun.checks).append bodyRun.checks)).complete (.letE full localState miss trace opening domainTree valueTree bodyTree domainReading valueReading bodyReading conditions hashPath comparisonFaithful substitution reduction) contextOrigin agreement reading accepted + | .forallSort miss trace opening domainCheck bodyCheck levelFaithful domainBound bodyBound coherent faithful => + fun data contextOrigin agreement reading accepted => by + obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_all_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + let domainRun := domainCheck.cacheExecution data.1 contextOrigin keyedAgreement domainReading + obtain ⟨openedReading, openedAgreement, _⟩ := + trace.opened_reading opening keyedAgreement domainReading bodyReading + let bodyRun := bodyCheck.cacheExecution data.2 + (.pushSort contextOrigin domainCheck keyedAgreement domainReading) openedAgreement openedReading + exact (SynthesisCacheSupplement.mk + (.forallSort miss trace domainCheck.exposure bodyCheck.exposure domainRun.trace bodyRun.trace) + (domainRun.checks.append bodyRun.checks)).complete + (.forallSort miss trace opening domainCheck bodyCheck levelFaithful domainBound bodyBound coherent faithful) + contextOrigin agreement reading accepted + | .lamSort full miss trace opening domainCheck bodyTree reduction conditionAgrees + constructed bound coherent closingFaithful faithful => fun data contextOrigin agreement reading accepted => by + obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + let domainRun := domainCheck.cacheExecution data.1 contextOrigin keyedAgreement domainReading + obtain ⟨openedReading, openedAgreement, _⟩ := + trace.opened_reading opening keyedAgreement domainReading bodyReading + let bodyRun := bodyTree.cacheExecution data.2 + (.pushSort contextOrigin domainCheck keyedAgreement domainReading) + openedAgreement openedReading trace.bodyRun + exact (SynthesisCacheSupplement.mk + (.lamSort full miss trace domainCheck.exposure domainRun.trace bodyRun.trace) + (domainRun.checks.append bodyRun.checks)).complete + (.lamSort full miss trace opening domainCheck bodyTree reduction conditionAgrees + constructed bound coherent closingFaithful faithful) contextOrigin agreement reading accepted + | .letSort full localState miss trace opening domainCheck valueTree bodyTree domainReading valueReading bodyReading + conditions hashPath comparisonFaithful substitution reduction => + fun data contextOrigin agreement reading accepted => by + have keyedValid := miss.keyedLocalState localState + have keyedAgreement := miss.localContext.symm ▸ agreement + let domainRun := domainCheck.cacheExecution data.1 contextOrigin keyedAgreement domainReading + let valueRun := valueTree.cacheExecution data.2.1 contextOrigin + (keyedAgreement.congr (trace.domainContext keyedValid).symm) valueReading trace.valueRun + obtain ⟨openedReading, openedAgreement, _⟩ := + trace.opened_reading opening keyedValid keyedAgreement domainReading bodyReading + let bodyRun := bodyTree.cacheExecution data.2.2 + (.pushSort contextOrigin domainCheck keyedAgreement domainReading) + openedAgreement openedReading trace.bodyRun + exact (SynthesisCacheSupplement.mk + (.letSort full miss trace domainCheck.exposure hashPath domainRun.trace valueRun.trace bodyRun.trace) + ((domainRun.checks.append valueRun.checks).append bodyRun.checks)).complete + (.letSort full localState miss trace opening domainCheck valueTree bodyTree domainReading valueReading bodyReading + conditions hashPath comparisonFaithful substitution reduction) contextOrigin agreement reading accepted termination_by structural tree +/-- Sort exposure adds no inference-cache writes. The producing inference +event retains the original tree at its actual, possibly unreduced, result type. -/ +def SynthesisSortCheck.cacheExecution {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} + {trace : SortInferenceTrace fuel before source} {term : AExpr β} + (check : SynthesisSortCheck resolve entries locals context bounds trace term) : + check.CacheData anchor → + SynthesisContext resolve anchor [] [] entries context bounds → + LocalContextReading resolve locals before.lctx context → + readScopedExpr? resolve locals source = some term.erase → + SynthesisCacheRun resolve anchor entries trace.inferRun := + match check with + | .checked tree .. => fun data contextOrigin agreement reading => + tree.cacheExecution data contextOrigin agreement reading trace.inferRun +termination_by structural check + +end + /-- A successful synthesis call extends the complete typed cache history. New event annotations are extracted from its original checking tree. -/ def SynthesisCacheHistory.afterSynthesis {β : Type u} {resolve : Address → Option (ConstRef β)} diff --git a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean index 6466e274b..72570ab20 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean @@ -197,8 +197,84 @@ theorem SynthesisInference.soundWithHereditary {β : Type u} (bodyTyped.instAt valueAtDomain .root).conv typeFormed converted, typeFormed, .convert (bodyHereditary.substituteAt (sameType ▸ valueHereditary) .root) reduction.rigid converted typeFormed⟩ + | .forallSort miss trace opening domainCheck bodyCheck levelFaithful domainBound bodyBound + coherent faithful => by + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨domainTyped, domainHereditary⟩ := + domainCheck.soundWithHereditary formed keyedAgreement domainReads + obtain ⟨openedReads, openedAgreement, _⟩ := + trace.opened_reading opening keyedAgreement domainReads bodyReads + obtain ⟨bodyTyped, bodyHereditary⟩ := + bodyCheck.soundWithHereditary (formed.push domainTyped) openedAgreement openedReads + have typed := AExpr.LevelEquivalent.sort + (Theory.VLevel.equiv_def.mpr fun levels => + (Theory.VLevel.equiv_def.mp (readLevel_mkIMax levelFaithful domainBound bodyBound) + levels).symm) |>.typing (TypingClaim.forallE domainTyped bodyTyped rfl) + let node := SynthesisInference.forallSort miss trace opening domainCheck bodyCheck + levelFaithful domainBound bodyBound coherent faithful + exact ⟨node.outputReading agreement reading accepted, typed, TypingClaim.sort _, + .forallE typed domainHereditary bodyHereditary rfl⟩ + | .lamSort full miss trace opening domainCheck bodyTree reduction conditionAgrees + constructed bound coherent closingFaithful faithful => by + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨domainTyped, _⟩ := domainCheck.soundWithHereditary formed keyedAgreement domainReads + obtain ⟨openedReads, openedAgreement, _⟩ := + trace.opened_reading opening keyedAgreement domainReads bodyReads + obtain ⟨_, bodyTyped, bodyFormed, bodyHereditary⟩ := + bodyTree.soundWithHereditary (formed.push domainTyped) openedAgreement openedReads trace.bodyRun + obtain ⟨converted, reducedFormed⟩ := reduction.sound (formed.push domainTyped) bodyFormed + have typed := TypingClaim.lam domainTyped reducedFormed + (bodyTyped.conv reducedFormed converted) conditionAgrees + let node := SynthesisInference.lamSort full miss trace opening domainCheck bodyTree reduction + conditionAgrees constructed bound coherent closingFaithful faithful + exact ⟨node.outputReading agreement reading accepted, typed, + TypingClaim.forallE domainTyped reducedFormed conditionAgrees, + .lam typed (.convert bodyHereditary reduction.rigid converted reducedFormed)⟩ + | .letSort full localState miss trace opening domainCheck valueTree bodyTree domainReading valueReading bodyReading + conditions hashPath comparisonFaithful substitution reduction => by + let node := SynthesisInference.letSort full localState miss trace opening domainCheck valueTree bodyTree + domainReading valueReading bodyReading conditions hashPath comparisonFaithful substitution reduction + have keyedValid := miss.keyedLocalState localState + have keyedAgreement := miss.localContext.symm ▸ agreement + have valueAgreement := keyedAgreement.congr (trace.domainContext keyedValid).symm + obtain ⟨domainTyped, _⟩ := domainCheck.soundWithHereditary formed keyedAgreement domainReading + obtain ⟨valueTypeReading, valueTyped, _, valueHereditary⟩ := + valueTree.soundWithHereditary formed valueAgreement valueReading trace.valueRun + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (valueTypeReading.symm.trans + ((beq_readScopedExpr? comparisonFaithful hashPath).trans domainReading))) conditions + obtain ⟨openedReading, openedAgreement, _⟩ := + trace.opened_reading opening keyedValid keyedAgreement domainReading bodyReading + obtain ⟨_, bodyTyped, bodyFormed, bodyHereditary⟩ := + bodyTree.soundWithHereditary (formed.push domainTyped) openedAgreement openedReading trace.bodyRun + have valueAtDomain := sameType ▸ valueTyped + obtain ⟨converted, typeFormed⟩ := reduction.sound formed (bodyFormed.instAt valueAtDomain .root) + exact ⟨node.outputReading agreement reading accepted, + (bodyTyped.instAt valueAtDomain .root).conv typeFormed converted, typeFormed, + .convert (bodyHereditary.substituteAt (sameType ▸ valueHereditary) .root) + reduction.rigid converted typeFormed⟩ termination_by structural support +/-- The actual inferred-type conversion establishes the sort used to form +the next binder context; the checked source derivation survives that conversion. -/ +theorem SynthesisSortCheck.soundWithHereditary {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} + {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} {trace : SortInferenceTrace fuel before source} + {term : AExpr β} (check : SynthesisSortCheck resolve entries locals context bounds trace term) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) : + TypingClaim.{u,v} entries context term (.sort (readLevel trace.level)) ∧ + HereditaryTyping.{u,v} entries context term (.sort (readLevel trace.level)) := + match check with + | .checked tree _ reduction => by + obtain ⟨_, typed, _, hereditary⟩ := tree.soundWithHereditary formed agreement reading trace.inferRun + obtain ⟨converted, typeFormed⟩ := reduction.sound formed + exact ⟨typed.conv typeFormed converted, .convert hereditary reduction.rigid converted typeFormed⟩ +termination_by structural check + theorem LetTypeReduction.sound {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} {source : KExpr .anon} {table : InternTable .anon} {level : VLevel} {term result : AExpr β} @@ -225,6 +301,9 @@ theorem SynthesisContext.sound {β : Type u} {resolve : Address → Option (Cons obtain ⟨_, domainTyped, _, _⟩ := domainTree.soundWithHereditary priorFormation agreement reading accepted exact priorFormation.push domainTyped + | .pushSort prior check agreement reading => by + have priorFormation := prior.sound formed + exact priorFormation.push (check.soundWithHereditary priorFormation agreement reading).1 | .extend prior extension => by intro index type bound found indexed exact extension.typing (prior.sound formed index type bound found indexed) @@ -468,6 +547,18 @@ termination_by structural support end +/-- The sort used by binder inference is justified after the actual exposure +of the returned type, without requiring that return to be a syntactic sort. -/ +theorem SynthesisSortCheck.sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} + {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} {trace : SortInferenceTrace fuel before source} + {term : AExpr β} (check : SynthesisSortCheck resolve entries locals context bounds trace term) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) : + TypingClaim.{u,v} entries context term (.sort (readLevel trace.level)) := + (check.soundWithHereditary formed agreement reading).1 + /-- The hereditary invariant supplies the existing application-spine contract, including after a let substitutes an entire checked derivation. -/ theorem SynthesisInference.soundWithSpine {β : Type u} diff --git a/Ix/Kernel/Verify/Consistency/SynthesisReading.lean b/Ix/Kernel/Verify/Consistency/SynthesisReading.lean index baa46a245..d12e77c78 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisReading.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisReading.lean @@ -104,6 +104,42 @@ theorem SynthesisInference.outputReading {β : Type u} rw [full] at uncached rw [(trace.output_state uncached).1] exact (reduction.reading substitutedCoherent substitutedReading).1 + | .forallSort miss trace _ _ _ _ _ _ coherent faithful => by + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + rw [(trace.output_state run).1] + change readScopedExpr? resolve locals (trace.bodyCheck.after.env.intern.internExpr _).1 = _ + rw [internExpr_readScopedExpr? coherent faithful] + rfl + | .lamSort full miss trace opening _ bodyTree reduction _ constructed bound coherent closingFaithful faithful => by + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + rw [full] at run + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨openedReads, openedAgreement, _⟩ := + trace.opened_reading opening keyedAgreement domainReads bodyReads + have bodyTypeReads := bodyTree.outputReading openedAgreement openedReads trace.bodyRun + obtain ⟨reducedReads, reducedCoherent⟩ := reduction.reading coherent bodyTypeReads + obtain ⟨closedReads, closedCoherent⟩ := abstractFVars_readScopedExpr? constructed bound + reducedCoherent closingFaithful reducedReads + rw [(trace.output_state run).1] + change readScopedExpr? resolve locals (trace.abstracted.2.internExpr _).1 = _ + rw [internExpr_readScopedExpr? (table := trace.abstracted.2) closedCoherent faithful] + simp [LambdaSortInferenceTrace.abstracted, LambdaSortInferenceTrace.reduced, domainReads, + AExpr.erase] at ⊢ closedReads + exact closedReads + | .letSort full localState miss trace opening _ _ bodyTree domainReading valueReading bodyReading + _ _ _ substitution reduction => by + have keyedValid := miss.keyedLocalState localState + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨openedReading, openedAgreement, _⟩ := + trace.opened_reading opening keyedValid keyedAgreement domainReading bodyReading + have bodyTypeReading := bodyTree.outputReading openedAgreement openedReading trace.bodyRun + obtain ⟨substitutedReading, substitutedCoherent⟩ := + trace.substituted_reading substitution bodyTypeReading valueReading + obtain ⟨middle, uncached⟩ := infer_uncached_success miss accepted + rw [full] at uncached + rw [(trace.output_state uncached).1] + exact (reduction.reading substitutedCoherent substitutedReading).1 termination_by structural support end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/SynthesisSource.lean b/Ix/Kernel/Verify/Consistency/SynthesisSource.lean index 0f9670e4b..304c2e5c4 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisSource.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisSource.lean @@ -195,8 +195,74 @@ def SynthesisInference.betaTyping {β : Type u} {resolve : Address → Option (C openedAgreement openedReading trace.bodyRun).substituteAt (sameType ▸ valueTree.betaTyping contextOrigin valueAgreement valueReading trace.valueRun) .root) (.rebase contextOrigin (reduction.trace substitutedType)) + | .forallSort miss trace opening domainCheck bodyCheck levelFaithful domainBound bodyBound coherent faithful => + fun contextOrigin agreement reading accepted => by + let node := SynthesisInference.forallSort miss trace opening domainCheck bodyCheck + levelFaithful domainBound bodyBound coherent faithful + obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_all_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨openedReading, openedAgreement, _⟩ := + trace.opened_reading opening keyedAgreement domainReading bodyReading + exact .forallE (.source (.checked contextOrigin node agreement reading accepted)) + (domainCheck.betaTyping contextOrigin keyedAgreement domainReading) + (bodyCheck.betaTyping (.pushSort contextOrigin domainCheck keyedAgreement domainReading) + openedAgreement openedReading) rfl + | .lamSort full miss trace opening domainCheck bodyTree reduction conditionAgrees + constructed bound coherent closingFaithful faithful => fun contextOrigin agreement reading accepted => by + let node := SynthesisInference.lamSort full miss trace opening domainCheck bodyTree reduction + conditionAgrees constructed bound coherent closingFaithful faithful + obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨openedReading, openedAgreement, _⟩ := + trace.opened_reading opening keyedAgreement domainReading bodyReading + have bodyContext := SynthesisContext.pushSort contextOrigin domainCheck keyedAgreement domainReading + have typeOrigin := SynthesisTypingOrigin.inferredType .current bodyTree + openedAgreement openedReading trace.bodyRun + exact .lam (.source (.checked contextOrigin node agreement reading accepted)) + (.convert (bodyTree.betaTyping bodyContext openedAgreement openedReading trace.bodyRun) + (.rebase bodyContext (reduction.trace typeOrigin))) + | .letSort _ localState miss trace opening domainCheck valueTree bodyTree domainReading valueReading bodyReading + conditions hashPath comparisonFaithful _ reduction => fun contextOrigin agreement _ _ => by + have keyedValid := miss.keyedLocalState localState + have keyedAgreement := miss.localContext.symm ▸ agreement + have valueAgreement := keyedAgreement.congr (trace.domainContext keyedValid).symm + obtain ⟨openedReading, openedAgreement, _⟩ := + trace.opened_reading opening keyedValid keyedAgreement domainReading bodyReading + have valueTypeReading := valueTree.outputReading valueAgreement valueReading trace.valueRun + have sameType := AExpr.eq_of_erase_annotations + (Option.some.inj (valueTypeReading.symm.trans + ((beq_readScopedExpr? comparisonFaithful hashPath).trans domainReading))) conditions + have bodyContext := SynthesisContext.pushSort .current domainCheck keyedAgreement domainReading + have bodyTypeOrigin := SynthesisTypingOrigin.inferredType bodyContext bodyTree + openedAgreement openedReading trace.bodyRun + have valueOrigin := SynthesisTypingOrigin.source + (SynthesisCheckedOrigin.checked .current valueTree valueAgreement valueReading trace.valueRun) + have substitutedType := SynthesisTypingOrigin.substituteAt bodyTypeOrigin (sameType ▸ valueOrigin) + ContextSubstitution.root + exact .convert + ((bodyTree.betaTyping (contextOrigin.pushSort domainCheck keyedAgreement domainReading) + openedAgreement openedReading trace.bodyRun).substituteAt + (sameType ▸ valueTree.betaTyping contextOrigin valueAgreement valueReading trace.valueRun) .root) + (.rebase contextOrigin (reduction.trace substitutedType)) termination_by structural support +/-- The exposed sort is justified by the original inferred-type conversion; +the source derivation retains the checked term before and after that conversion. -/ +def SynthesisSortCheck.betaTyping {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds bounds : List VLevel} {locals : List FVarId} {fuel : Nat} + {before : TcState .anon} {source : KExpr .anon} {trace : SortInferenceTrace fuel before source} + {term : AExpr β} (check : SynthesisSortCheck resolve entries locals context bounds trace term) : + (contextOrigin : SynthesisContext resolve incoming incomingContext incomingBounds entries context bounds) → + LocalContextReading resolve locals before.lctx context → + readScopedExpr? resolve locals source = some term.erase → + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context + term (.sort (readLevel trace.level)) := + match check with + | .checked tree _ reduction => fun contextOrigin agreement reading => + .convert (tree.betaTyping contextOrigin agreement reading trace.inferRun) (.rebase contextOrigin reduction) +termination_by structural check + def SynthesisRetainedCheck.betaTyping {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} {incomingBounds : List VLevel} {term type : AExpr β} {level : VLevel} diff --git a/Ix/Kernel/Verify/Consistency/SynthesisSupport.lean b/Ix/Kernel/Verify/Consistency/SynthesisSupport.lean index 7eab3943d..caad85345 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisSupport.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisSupport.lean @@ -7,6 +7,7 @@ import Ix.Kernel.Verify.Consistency.ContextTransport import Ix.Kernel.Verify.Consistency.CheapBetaReading import Ix.Kernel.Verify.Consistency.ApplicationWhnf import Ix.Kernel.Verify.Consistency.LetInference +import Ix.Kernel.Verify.Consistency.SortInference import Ix.Theory.Model.UniverseBounds import Ix.Theory.Model.BetaSpine @@ -211,6 +212,87 @@ inductive SynthesisInference {β : Type u} SynthesisInference resolve entries locals context bounds (fuel + 1) before (.letE name domain value body nonDep info) (b.inst val) resultType level + | forallSort {entries locals context bounds fuel before name bi domain body info A B} + (miss : UncachedInference before (.all name bi domain body info)) + (trace : ForallSortInferenceTrace fuel miss.keyed name bi domain body) + (opening : BinderOpeningSupport trace.domainCheck.after body) + (domainCheck : SynthesisSortCheck resolve entries locals context bounds trace.domainCheck A) + (bodyCheck : SynthesisSortCheck resolve entries (trace.fresh :: locals) (context.push A) + (readLevel trace.domainCheck.level :: bounds) trace.bodyCheck B) + (levelFaithful : ∀ a b, + (KUniv.Sub a trace.domainCheck.level ∨ KUniv.Sub a trace.bodyCheck.level) → + (KUniv.Sub b trace.domainCheck.level ∨ KUniv.Sub b trace.bodyCheck.level) → a.AddrFaithful b) + (domainBound : trace.domainCheck.level.size < UInt64.size) + (bodyBound : trace.bodyCheck.level.size < UInt64.size) + (coherent : trace.bodyCheck.after.env.intern.WF) + (faithful : KExpr.KeyCollisionFree fun term => trace.bodyCheck.after.env.intern.ExprSupport term ∨ + term = KExpr.mkSort (KUniv.mkIMax trace.domainCheck.level trace.bodyCheck.level)) : + SynthesisInference resolve entries locals context bounds (fuel + 1) before (.all name bi domain body info) + (.forallE (Certified.zeroCondition (readLevel trace.bodyCheck.level)) A B) + (.sort (readLevel (KUniv.mkIMax trace.domainCheck.level trace.bodyCheck.level))) + (.succ (readLevel (KUniv.mkIMax trace.domainCheck.level trace.bodyCheck.level))) + + | lamSort {entries locals context bounds fuel before name bi domain body info A b B B' condition bodyLevel} + (full : before.inferOnly = false) + (miss : UncachedInference before (.lam name bi domain body info)) + (trace : LambdaSortInferenceTrace fuel miss.keyed name bi domain body) + (opening : BinderOpeningSupport trace.domainCheck.after body) + (domainCheck : SynthesisSortCheck resolve entries locals context bounds trace.domainCheck A) + (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) + (readLevel trace.domainCheck.level :: bounds) fuel trace.openedState trace.opened b B bodyLevel) + (reduction : LetTypeReduction resolve entries (context.push A) (readLevel trace.domainCheck.level :: bounds) + trace.bodyType trace.bodyState.env.intern bodyLevel B B') + (conditionAgrees : condition = Certified.zeroCondition bodyLevel) + (constructed : trace.reduced.1.Constructed) + (bound : trace.reduced.1.size + 1 < UInt64.size) + (coherent : trace.bodyState.env.intern.WF) + (closingFaithful : KExpr.CollisionFree fun term => trace.reduced.2.ExprSupport term ∨ + KExpr.AbstractReach ((∅ : Std.HashMap FVarId UInt64).insert trace.fresh 0) + 1 trace.reduced.1 0 term) + (faithful : KExpr.KeyCollisionFree fun term => trace.abstracted.2.ExprSupport term ∨ + term = KExpr.mkAll () () domain trace.abstracted.1) : + SynthesisInference resolve entries locals context bounds (fuel + 1) before (.lam name bi domain body info) + (.lam condition A b) (.forallE condition A B') (.imax (readLevel trace.domainCheck.level) bodyLevel) + + | letSort {entries locals context bounds fuel before name domain value body nonDep info + A val b B resultType level valueLevel valueType} + (full : before.inferOnly = false) + (localState : LocalStateInvariant before) + (miss : UncachedInference before (.letE name domain value body nonDep info)) + (trace : LetSortInferenceTrace fuel miss.keyed name domain value body) + (opening : BinderOpeningSupport trace.comparedState body) + (domainCheck : SynthesisSortCheck resolve entries locals context bounds trace.domainCheck A) + (valueTree : SynthesisInference resolve entries locals context bounds fuel trace.domainCheck.after value + val valueType valueLevel) + (bodyTree : SynthesisInference resolve entries (trace.fresh :: locals) (context.push A) + (readLevel trace.domainCheck.level :: bounds) fuel trace.openedState trace.opened b B level) + (domainReading : readScopedExpr? resolve locals domain = some A.erase) + (valueReading : readScopedExpr? resolve locals value = some val.erase) + (bodyReading : readScopedExpr? resolve locals body 1 = some b.erase) + (conditions : valueType.annotations = A.annotations) + (hashPath : (trace.valueType == domain) = true) + (comparisonFaithful : trace.valueType.AddrFaithful domain) + (substitution : trace.SubstitutionSupport) + (reduction : LetTypeReduction resolve entries context bounds trace.substituted.1 trace.substituted.2 + level (B.inst val) resultType) : + SynthesisInference resolve entries locals context bounds (fuel + 1) before + (.letE name domain value body nonDep info) (b.inst val) resultType level + +/-- A sort check retains inference at its actual returned type and the +executed exposure of that type. Its conversion comes from source checking +origins, and supplies no semantic typing premise. -/ +inductive SynthesisSortCheck {β : Type u} (resolve : Address → Option (ConstRef β)) : + Model.Environment β → List FVarId → Model.Context β → List VLevel → + {fuel : Nat} → {before : TcState .anon} → {source : KExpr .anon} → + SortInferenceTrace fuel before source → AExpr β → Type u + | checked {entries locals context bounds fuel before source term type bound} + {trace : SortInferenceTrace fuel before source} + (tree : SynthesisInference resolve entries locals context bounds fuel before source term type bound) + (exposure : trace.Exposure resolve locals type) + (reduction : SynthesisBetaTrace resolve entries context bounds entries context + type (.sort (readLevel trace.level)) (.sort bound)) : + SynthesisSortCheck resolve entries locals context bounds trace term + /-- A complete executed inference check transported to a later use site. The source tree remains available beneath interface and context changes; none of these constructors accepts semantic typing or conversion evidence. -/ @@ -258,6 +340,14 @@ inductive SynthesisContext {β : Type u} (resolve : Address → Option (ConstRef (reading : readScopedExpr? resolve locals source = some domain.erase) (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : SynthesisContext resolve entries context bounds earlier (priorContext.push domain) (level :: priorBounds) + | pushSort {entries context bounds earlier priorContext priorBounds locals fuel before source + domain} {trace : SortInferenceTrace fuel before source} + (prior : SynthesisContext resolve entries context bounds earlier priorContext priorBounds) + (check : SynthesisSortCheck resolve earlier locals priorContext priorBounds trace domain) + (agreement : LocalContextReading resolve locals before.lctx priorContext) + (reading : readScopedExpr? resolve locals source = some domain.erase) : + SynthesisContext resolve entries context bounds earlier (priorContext.push domain) + (readLevel trace.level :: priorBounds) | extend {entries context bounds earlier later priorContext priorBounds} (prior : SynthesisContext resolve entries context bounds earlier priorContext priorBounds) (extension : InterfaceExtends earlier later) : @@ -649,6 +739,34 @@ inductive LetTypeReduction {β : Type u} (resolve : Address → Option (ConstRef end +namespace SynthesisSortCheck + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} + {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} + {trace : SortInferenceTrace fuel before source} {term : AExpr β} + +def inferredType (check : SynthesisSortCheck resolve entries locals context bounds trace term) : AExpr β := + match check with + | .checked (type := type) .. => type + +def exposure (check : SynthesisSortCheck resolve entries locals context bounds trace term) : + trace.Exposure resolve locals check.inferredType := + match check with + | .checked _ exposure _ => exposure + +/-- Syntactic sort results use their original type-formation origin. -/ +def direct {level : KUniv .anon} {info : ExprInfo .anon} {after : TcState .anon} {bound : VLevel} + (tree : SynthesisInference resolve entries locals context bounds fuel before source + term (.sort (readLevel level)) bound) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok (.sort level info) after) : + SynthesisSortCheck resolve entries locals context bounds (SortInferenceTrace.direct accepted) term := + .checked tree (.direct level info) (.refl (.inferredType .current tree agreement reading accepted)) + +end SynthesisSortCheck + theorem LetTypeReduction.reading {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} {locals : List FVarId} {source : KExpr .anon} {table : InternTable .anon} diff --git a/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean b/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean index 6943941ff..3853838c0 100644 --- a/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean +++ b/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean @@ -101,4 +101,40 @@ theorem BetaPiExposure.inference_maps {β : Type u} {resolve : Address → Optio simp only [BetaPiExposure.after, BetaPublicWhnf.outerKey, betaWhnfKey_environment, (betaWhnfPrefix_fields before).1] +namespace BetaSortExposure + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} {term : AExpr β} {level : KUniv .anon} + +theorem inference_frame (exposure : BetaSortExposure resolve locals fuel before source term level) + (key : Address × Address) : InferenceCacheFrame key before exposure.after := by + cases exposure with + | direct => exact .refl key _ + | reduce plan => exact plan.inference_frame key + | cached origin hit => + apply InferenceCacheFrame.of_eq <;> + simp only [after, BetaPublicWhnf.outerKey, betaWhnfKey_environment, + (betaWhnfPrefix_fields before).1] + +theorem policy (exposure : BetaSortExposure resolve locals fuel before source term level) : + exposure.after.inferOnly = before.inferOnly := by + cases exposure with + | direct => rfl + | reduce plan => exact plan.policy + | cached origin hit => + simp only [after, BetaPublicWhnf.outerKey, betaWhnfKey_policy, betaWhnfPrefix_policy] + +theorem inference_maps (exposure : BetaSortExposure resolve locals fuel before source term level) : + exposure.after.env.inferCache = before.env.inferCache ∧ + exposure.after.env.inferOnlyCache = before.env.inferOnlyCache := by + cases exposure with + | direct => exact ⟨rfl, rfl⟩ + | reduce plan => exact plan.inference_maps + | cached origin hit => + constructor <;> + simp only [after, BetaPublicWhnf.outerKey, betaWhnfKey_environment, + (betaWhnfPrefix_fields before).1] + +end BetaSortExposure + end Ix.Kernel.Consistency diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index e612a7935..c94425e91 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -3602,6 +3602,127 @@ private def recursiveLetFailureCleanup : Bool := !after.env.inferCache.contains (invalid.addr, emptyCtxAddr) | _ => false +private def sortExposureEnvironment (level : Ixon.Univ) (universes : UInt64 := 0) : + Ixon.Env × Array Address := Id.run do + -- References pass the parameter itself, even when the tested level is composite. + let arguments := if universes == 0 then #[] else #[2] + let levels := #[level, .succ level] ++ if universes == 0 then #[] else #[.var 0] + let betaSort := Ixon.Expr.app (.leanLam (.sort 1) (.var 0)) (.sort 0) + let (env, carrier) := storeConst {} + ⟨.axio ⟨false, universes, betaSort⟩, #[], #[], levels⟩ + let (env, witness) := storeConst env + ⟨.axio ⟨false, universes, .ref 0 arguments⟩, #[], #[carrier], levels⟩ + let (env, otherCarrier) := storeConst env + ⟨.axio ⟨false, universes, .sort 0⟩, #[], #[], levels⟩ + let betaCarrier := Ixon.Expr.app (.leanLam (.sort 0) (.var 0)) (.ref 0 arguments) + let (env, betaWitness) := storeConst env + ⟨.axio ⟨false, universes, betaCarrier⟩, #[], #[otherCarrier], levels⟩ + let domain := Ixon.Expr.ref 0 arguments + let identityType := Ixon.Expr.leanAll domain domain + let samples := [ + (identityType, Ixon.Expr.leanLam domain (.var 0)), + (Ixon.Expr.sort 0, identityType), + (domain, Ixon.Expr.letE false domain (.ref 1 arguments) (.var 0)), + (identityType, Ixon.Expr.leanLam domain (.letE false domain (.var 0) (.var 0))), + (Ixon.Expr.leanAll domain (.ref 2 arguments), Ixon.Expr.leanLam domain (.ref 3 arguments))] + let mut env := env + let mut targets := #[] + for (type, value) in samples do + let (next, target) := storeConst env + ⟨.defn ⟨.defn, .safe, universes, type, value⟩, + #[], #[carrier, witness, otherCarrier, betaWitness], levels⟩ + env := next + targets := targets.push target + return (env, targets) + +/-- The domain's full inference cache keeps its original beta type while +sort exposure writes the normalized sort only to the three WHNF caches. -/ +private def sortExposureInferencePaths (typeLevel warm instrumented noAccel : Bool) : Bool := + let level := if typeLevel then levelOne else KUniv.mkZero + let (env, targets) := sortExposureEnvironment (if typeLevel then .succ .zero else .zero) + (List.range 5).all fun shape => + let action : RecM .anon Bool := do + let .defn _ _ _ _ _ _ expected value _ _ ← TcM.getConst (m := .anon) ⟨targets[shape]!, ()⟩ | return false + let domain := match value with + | .lam _ _ domain _ _ | .all _ _ domain _ _ | .letE _ domain _ _ _ _ => domain + | _ => value + let original ← RecM.inferCall domain + let .app .. := original | return false + let bodyType ← match shape, value with + | 4, .lam _ _ _ (.const id _ _) _ => pure (← TcM.getConst id).ty + | _, _ => pure original + modify fun state => { state with recFuel := 1, inNativeReduce := false, stats := instrumented, noAccel } + if warm then + let exposed ← RecM.ensureSortDirect original + if exposed != level then return false + let warmed := { (← get) with inNativeReduce := true } + match (RecM.ensureSortDirect original).run (methodsN 0) warmed with + | .error _ _ => return false + | .ok exposed reused => + if exposed != level || reused.recFuel != 0 || !reused.inNativeReduce || + !exactInferenceCaches [(domain, original)] [] reused then return false + set reused + let result ← RecM.inferCall value + let after ← get + let first := KExpr.mkFVar (m := .anon) ⟨0⟩ () + let second := KExpr.mkFVar (m := .anon) ⟨1⟩ () + let children := match shape, value with + | 0, _ => [(first, domain)] + | 1, _ => [] + | 2, .letE _ _ witness _ _ _ => [(witness, domain), (first, domain)] + | 3, _ => [(first, domain), (second, domain), + (KExpr.mkLet () domain first (.mkVar 0 ()) false, domain)] + | 4, .lam _ _ _ body _ => [(body, bodyType)] + | _, _ => [] + let sort := KExpr.mkSort (m := .anon) level + let key := (original.addr, emptyCtxAddr) + return result == expected && after.recFuel == 0 && after.lctx.size == 0 && + after.env.nextFVarId == (if shape == 3 then 2 else 1) && after.inNativeReduce == warm && + after.env.whnfCache[key]? == some sort && after.env.whnfNoDeltaCache[key]? == some sort && + after.env.whnfCoreCache[key]? == some sort && + exactInferenceCaches ((domain, original) :: (children ++ [(value, expected)])) [] after && + compositeReplayAt value expected after + match TcM.runRec action (TcState.newLazyAnon env) with + | .ok passed _ => passed + | .error _ _ => false + +private def sortExposureFailures : Bool := + let betaPi := Ixon.Expr.app (.leanLam (.sort 1) (.leanAll (.var 0) (.var 1))) (.sort 0) + let (env, carrierAddr) := storeConst {} + ⟨.axio ⟨false, 0, betaPi⟩, #[], #[], #[.zero, .succ .zero]⟩ + (List.range 3).all fun shape => + let carrier := KExpr.mkConst (m := .anon) ⟨carrierAddr, ()⟩ #[] + let prop := KExpr.mkSort (m := .anon) .mkZero + let source := if shape == 0 then KExpr.mkLam () () carrier (.mkVar 0 ()) + else if shape == 1 then KExpr.mkLet () carrier prop (.mkVar 0 ()) false + else KExpr.mkAll () () prop carrier + match TcM.getConst (m := .anon) ⟨carrierAddr, ()⟩ (TcState.newLazyAnon env) with + | .error _ _ => false + | .ok concrete loaded => + match TcM.infer source loaded with + | .error .typeExpected failed => + let children := if shape == 2 then [(prop, KExpr.mkSort levelOne)] else [] + failed.lctx.size == 0 && failed.env.nextFVarId == (if shape == 2 then 1 else 0) && + failed.env.whnfCache[(concrete.ty.addr, emptyCtxAddr)]? == some (KExpr.mkAll () () prop prop) && + exactInferenceCaches ((carrier, concrete.ty) :: children) [] failed + | _ => false + +private def sortExposureCases : TestSeq := + test "sort exposure: dependent types, lambdas, and lets check in Prop and Type under both cache policies" + ([Ixon.Univ.zero, .succ .zero].all fun level => + [0, 1].all fun clearEvery => allSucceeded (sortExposureEnvironment level).1 9 { clearEvery }) + ++ test "sort exposure: binder validation and changed body beta retain declaration universe parameters" + ([Ixon.Univ.var 0, .max (.var 0) (.succ (.var 0))].all fun level => + [0, 1].all fun clearEvery => allSucceeded (sortExposureEnvironment level 1).1 9 { clearEvery }) + ++ test "sort exposure: cold binder checks preserve exact child caches and publish their returned types" + ([false, true].all fun typeLevel => [false, true].all fun instrumented => + [false, true].all fun noAccel => sortExposureInferencePaths typeLevel false instrumented noAccel) + ++ test "sort exposure: warm binder checks and zero-method exposure need no remaining reduction fuel" + ([false, true].all fun typeLevel => [false, true].all fun instrumented => + [false, true].all fun noAccel => sortExposureInferencePaths typeLevel true instrumented noAccel) + ++ test "sort exposure: a reduced Pi is rejected as a sort and failed binder scopes restore locals" + sortExposureFailures + private def letCases : TestSeq := test "let inference: dependent type substitution retains exact child caches, replay, and fresh rebuilding" letExactCacheHistory @@ -3747,6 +3868,6 @@ public def suite : List TestSeq := cheapApplicationCases, exposedLambdaCases, repeatedBetaCases, betaTraceCases, hereditaryBetaCases, piExposureCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, - sourceAgreementCases, sourceCacheCases, compositeCacheCases, letCases, polymorphicDefinitionCases] + sourceAgreementCases, sourceCacheCases, compositeCacheCases, letCases, sortExposureCases, polymorphicDefinitionCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index ad0a6d38c..feb27ddc9 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -112,6 +112,19 @@ increasing sequence of strongly inaccessible cardinals. `LetInferenceCheck.asSynthesis` connects that interface to the recursive case. Finite execution, collision, walker, and selected beta-origin resources remain explicit, and their general automatic construction remains open. +- `SortInferenceTrace` records inference followed by the actual sort-exposure + call. Successful forall, lambda, and let branches supply these observations + through `ofInference`; both children of a forall may need reduction to expose + their sorts. `SynthesisSortCheck` retains each child's original inference + and converts its returned type using checked beta origins. The recursive + synthesis rules form binder contexts from the exposed level and preserve + later beta derivations and typed cache histories. Exposure supports direct + sorts, the existing public beta WHNF path, and its outer-cache hit. It + preserves both inference-cache maps, so a child publication retains its + original, possibly unreduced, type. Earlier retained type checks construct + the conversion resource without a semantic typing premise. Finite reader, + collision, reduction, and source-checking resources remain explicit; + arbitrary reduction and automatic construction of those resources remain open. - `MethodsLocalState.methodsN` proves structural local-state preservation for every production recursive table, including all inference, reduction, and conversion branches. The public inference, WHNF, conversion, and sort/forall @@ -421,6 +434,11 @@ def callReturned.{u} (A : Sort u) (a : A) : A := ((fun x : A => fun y : A => x) follows the selected plan through substitution and interning, and lambda abstraction uses that reduction's output table. No new check of the generated result is assumed. + The `forallSort`, `lamSort`, and `letSort` cases also admit domains whose + inferred types need supported public beta WHNF or a recorded cache hit to + expose their sort. Forall bodies use the same sort-check resource. Their + successful production calls determine all intermediate observations, while + representation resources and retained reduction origins remain explicit. Retained codomain checks now also cross dependent term substitution. `SynthesisTypeCheck.forallBody` extracts the actual codomain call from the earlier function-type tree. `SynthesisCheckedOrigin.applicationArgument` @@ -583,7 +601,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 1,281 exact theorem boundaries. The production +The consistency target checks 1,336 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -592,6 +610,9 @@ extension lemma and `ModelTyping.no_false` use only `propext`, The production roots additionally forbid the abstract `CheckSuccessSound`/`SupportedCheckFragment` interfaces and the independent certificate validator in their dependency closures. +Sort-exposure traces, source resources, beta derivation producers, and cache +history constructors additionally forbid the semantic hereditary invariant; +the soundness induction derives that invariant after source reconstruction. The polymorphic inference, substitution, and binder inference roots retain only the two existing expression/universe output-length proofs, alongside the standard Lean axioms. @@ -951,7 +972,12 @@ and cleanup after value-comparison and body-inference failures. They include lets in binder domains and bodies, function positions, nested values, composite declaration types, and failures inside nested scopes. They exercise production independently of the finite resources used by the proof. -The unit suite contains 726 checks. The anonymous differential additionally +Five sort-exposure regressions cover forall, lambda, and let checks in Prop, +Type, and parameterized universes; persistent and cleared caches; exact child +publications; warm exposure at zero method fuel; changed lambda-body beta; +and rejection of a reduced function type with local-scope cleanup. They also +exercise instrumentation and acceleration flags. +The unit suite contains 731 checks. The anonymous differential additionally serializes eleven cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. @@ -997,6 +1023,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Dependent binders and function bodies | [`Consistency/BinderInference.lean`](../Ix/Kernel/Verify/Consistency/BinderInference.lean), [`Application.lean`](../Ix/Kernel/Verify/Consistency/Application.lean), [`BinderOpening.lean`](../Ix/Kernel/Verify/Consistency/BinderOpening.lean), [`Context.lean`](../Ix/Kernel/Verify/Consistency/Context.lean), [`Model/Checking.lean`](../Ix/Theory/Model/Checking.lean) | | Inferred type formation and direct lambda applications | [`Consistency/SynthesisInference.lean`](../Ix/Kernel/Verify/Consistency/SynthesisInference.lean), [`Formation.lean`](../Ix/Kernel/Verify/Consistency/Formation.lean), [`Model/UniverseBounds.lean`](../Ix/Theory/Model/UniverseBounds.lean) | | Recursive synthesis support and returned syntax readings | [`Consistency/SynthesisSupport.lean`](../Ix/Kernel/Verify/Consistency/SynthesisSupport.lean), [`SynthesisReading.lean`](../Ix/Kernel/Verify/Consistency/SynthesisReading.lean), [`SynthesisSource.lean`](../Ix/Kernel/Verify/Consistency/SynthesisSource.lean) | +| Executed sort exposure and recursive binder checks | [`Consistency/SortInference.lean`](../Ix/Kernel/Verify/Consistency/SortInference.lean), [`BetaPublicWhnf.lean`](../Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean), [`BetaWhnfInference.lean`](../Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean) | | Retained derivations, dependent substitution, and semantic induction | [`Consistency/SynthesisDerivation.lean`](../Ix/Kernel/Verify/Consistency/SynthesisDerivation.lean), [`SynthesisShapes.lean`](../Ix/Kernel/Verify/Consistency/SynthesisShapes.lean), [`SynthesisReduction.lean`](../Ix/Kernel/Verify/Consistency/SynthesisReduction.lean), [`SynthesisMeaning.lean`](../Ix/Kernel/Verify/Consistency/SynthesisMeaning.lean), [`BinderMeaning.lean`](../Ix/Kernel/Verify/Consistency/BinderMeaning.lean), [`BetaChecking.lean`](../Ix/Kernel/Verify/Consistency/BetaChecking.lean) | | Let inference and typed cache history | [`Consistency/LetInference.lean`](../Ix/Kernel/Verify/Consistency/LetInference.lean), [`LetSynthesis.lean`](../Ix/Kernel/Verify/Consistency/LetSynthesis.lean), [`LetCache.lean`](../Ix/Kernel/Verify/Consistency/LetCache.lean), [`SynthesisCacheExecution.lean`](../Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean) | | Public beta WHNF, cache writes, and application Pi exposure | [`Consistency/BetaPublicWhnf.lean`](../Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean), [`ApplicationWhnf.lean`](../Ix/Kernel/Verify/Consistency/ApplicationWhnf.lean), [`BetaWhnfInference.lean`](../Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean) | diff --git a/docs/theory.md b/docs/theory.md index 6e6741bd4..2c3414e45 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -121,6 +121,17 @@ semantic induction then derives an invariant closed under dependent substitution Source support and reconstruction do not assume that invariant, and the audit enforces this boundary. Automatic construction of the initial execution and finite representation resources remains open. +Recursive forall, lambda, and let inference now also checks domains whose +inferred type needs reduction to expose a sort; a forall's body uses the same +resource. Successful production calls determine the child inference, exposure, +opening, comparison, and body observations. The original child check and a +retained check of its returned type justify conversion to the exposed sort, +which supplies the binder context's universe level. The source derivation and +typed cache history retain the original inference at its unreduced type. +Exposure supports syntactic sorts, the existing public beta WHNF path, and +that path's outer-cache hit. It changes neither inference-cache map. Finite +reading, representation, and reduction resources still need general construction; +other reduction and cache paths remain open. Structural local-state preservation now covers the complete recursive checker on both success and failure. The actual loader and initial state establish coherent lookup and a bound on allocated identifiers. Recursive calls retain From e02ba90f56da4d77989ab2e82733a17efb02c3e3 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 14:58:48 -0400 Subject: [PATCH 43/63] Prove beta reduction with mixed WHNF cache states --- Ix/Kernel/Verify/Consistency.lean | 14 +- Ix/Kernel/Verify/Consistency/Audit.lean | 75 ++- .../Consistency/BetaCacheExecution.lean | 488 ++++++++++++++++++ .../Verify/Consistency/BetaCacheKeys.lean | 63 +++ .../Verify/Consistency/BetaPublicWhnf.lean | 400 ++------------ .../Consistency/BetaPublicWhnfPlan.lean | 376 ++++++++++++++ .../Verify/Consistency/BetaWhnfInference.lean | 110 ++++ .../Verify/Consistency/WhnfCacheFrame.lean | 10 + .../Verify/Whnf/Driver/CacheExecution.lean | 114 ++++ Tests/Ix/Kernel/Consistency.lean | 126 ++++- docs/kernel-verification.md | 52 +- docs/theory.md | 8 +- 12 files changed, 1444 insertions(+), 392 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaCacheKeys.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaPublicWhnfPlan.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 39e40baba..c56cb446c 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -79,7 +79,7 @@ under the stated execution resources. Definitions may declare their own universe parameters; model entries retain the exact arity and interpretations at every instance. Recursive forall, lambda, and let rules also expose sorts through the supported -public beta WHNF path or its outer-cache hit. Successful production calls +public beta WHNF path, including hits at any of its three cache layers. Successful production calls determine their raw child traces. Retained checks justify conversion from the original inferred type to the exposed sort and supply the binder's level. Source derivations and typed cache histories retain the original child check; @@ -157,15 +157,19 @@ supported inference fragment therefore derives all its semantic step origins from the original check. The operational path contains only raw execution, reading, and finite representation resources, and its automatic annotation also supplies declaration admission. Public WHNF now computes the key states, -instrumentation, shared-fuel charge, and all three cache insertions for these -beta paths ending at a sort, Pi, or lambda. Pi exposure can execute such a -path or reuse its exact cached result without fuel. Application inference +instrumentation, shared-fuel charge, and guarded cache insertions for these +beta paths ending at a sort, Pi, or lambda. Each of the three cache layers +may retain a prior executed result. Native reduction suppresses new writes +at the no-delta and outer layers; the structural layer still publishes. +Replay derives the next hit from that publication and preserves memoized +context keys. Only an outer miss charges shared fuel. Pi exposure uses the +same complete cache-layer execution. Application inference uses that exposure between argument checks and derives the type conversion from a retained actual check of the function type. Dependent codomain substitution and all later beta origins preserve those argument checks. The original synthesis admission theorem includes this application case. Constructing initial inference and operational resources for all accepted -programs, other WHNF branches, mixed cache states, and general conversion +programs, other WHNF branches, general semantic cache agreement, and conversion remain open. Full-mode let checks retain their original domain, value, and opened-body synthesis checks. The scoped reader and actual opening, abstraction, and diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 69b5084c8..a84f8f554 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -673,6 +673,69 @@ private def piExposureRoots : Array RootAllowance := #[ { root := ``SynthesisInference.beta_public_whnf_sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] } ] +/-- Cache producers and replay must precede semantic hereditary typing. +The operational layer records raw executions and derives published hits. -/ +private def mixedCacheFrameRoots : Array Lean.Name := #[ + ``BetaCacheFrame.refl, + ``BetaCacheFrame.trans, + ``BetaCacheFrame.instrument, + ``BetaCacheFrame.charge, + ``BetaCacheExecution.writeNoDelta_frame, + ``BetaCacheExecution.writeFull_frame, + ``BetaCacheExecution.writeNoDelta_intern, + ``BetaCacheExecution.writeFull_intern +] + +private def mixedCacheKeyRoots : Array Lean.Name := #[ + ``betaWhnfKey_congr, + ``betaWhnfKey_replay, + ``betaWhnfKey_prefix, + ``betaWhnfKey_charge, + ``BetaCacheFrame.key, + ``BetaCacheExecution.writeNoDelta_key, + ``BetaCacheExecution.writeFull_key +] + +private def mixedCacheExecutionRoots : Array Lean.Name := #[ + ``BetaCoreExecution.after, + ``BetaCoreExecution.first, + ``BetaCoreExecution.terminal, + ``BetaCoreExecution.run, + ``BetaCoreExecution.reading, + ``BetaCoreExecution.frame, + ``BetaCoreExecution.stable_key, + ``BetaCoreExecution.published, + ``BetaCoreExecution.replay, + ``BetaCoreExecution.replay_run, + ``BetaCoreExecution.betaTrace, + ``BetaNoDeltaExecution.after, + ``BetaNoDeltaExecution.first, + ``BetaNoDeltaExecution.terminal, + ``BetaNoDeltaExecution.run, + ``BetaNoDeltaExecution.reading, + ``BetaNoDeltaExecution.frame, + ``BetaNoDeltaExecution.stable_key, + ``BetaNoDeltaExecution.published, + ``BetaNoDeltaExecution.replay, + ``BetaNoDeltaExecution.replay_run, + ``BetaNoDeltaExecution.betaTrace, + ``BetaPublicExecution.after, + ``BetaPublicExecution.first, + ``BetaPublicExecution.terminal, + ``BetaPublicExecution.run, + ``BetaPublicExecution.reading, + ``BetaPublicExecution.frame, + ``BetaPublicExecution.stable_key, + ``BetaPublicExecution.published, + ``BetaPublicExecution.replay, + ``BetaPublicExecution.replay_run, + ``BetaPublicExecution.betaTrace, + ``BetaPublicWhnfPlan.execution, + ``BetaPublicWhnfPlan.execution_after, + ``RecM.whnfNoDeltaImplNonLeaf_fullMiss_conditional, + ``RecM.whnfWithNatSuccModeNonLeaf_miss_conditional +] + private def localScopeFrameRoots : Array Lean.Name := #[ ``LocalContext.Equiv.refl, ``LocalContext.Equiv.symm, ``LocalContext.Equiv.trans, ``LocalContext.Equiv.size, ``LocalContext.Equiv.find?, ``LocalContext.Equiv.wf, @@ -1282,7 +1345,17 @@ def roots : Array RootAllowance := #[ }) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots ++ exposedOriginRoots ++ repeatedBetaRoots ++ betaTraceRoots ++ hereditaryBetaRoots ++ piExposureRoots ++ cacheTransportRoots).map (fun allowance => { allowance with forbiddenDependencies := allowance.forbiddenDependencies ++ forbiddenProduction }) - ++ (recursiveLetShapeRoots ++ sortExposureRoots).map (fun root => { + ++ mixedCacheFrameRoots.map (fun root => { + root, standardAxioms := #[``propext, ``Quot.sound], + forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) + ++ mixedCacheKeyRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative], + forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) + ++ #[``SynthesisInference.beta_core_execution_sound, ``SynthesisInference.beta_noDelta_execution_sound, + ``SynthesisInference.beta_public_execution_sound].map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], + forbiddenDependencies := forbiddenProduction }) + ++ (recursiveLetShapeRoots ++ sortExposureRoots ++ mixedCacheExecutionRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ (localScopeFrameRoots ++ localStateFrameRoots ++ recursiveStateFrameRoots ++ ingressFrameRoots ++ diff --git a/Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean b/Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean new file mode 100644 index 000000000..a9c31635b --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean @@ -0,0 +1,488 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaCacheKeys + +/-! Executed beta reduction and retained origins at all three WHNF cache layers. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +/-- State shared by surrounding inference is unchanged by these beta paths. +The three reduction caches, intern table, key memoization, and WHNF counters +are tracked by the computed result state instead. -/ +structure BetaCacheFrame (before after : TcState .anon) : Prop where + constants : after.env.consts = before.env.consts + full : after.env.inferCache = before.env.inferCache + only : after.env.inferOnlyCache = before.env.inferOnlyCache + context : after.lctx = before.lctx + policy : after.inferOnly = before.inferOnly + native : after.inNativeReduce = before.inNativeReduce + +namespace BetaCacheFrame + +theorem refl (before : TcState .anon) : BetaCacheFrame before before := + ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩ + +theorem trans {before middle after : TcState .anon} + (first : BetaCacheFrame before middle) (second : BetaCacheFrame middle after) : + BetaCacheFrame before after := + ⟨second.constants.trans first.constants, second.full.trans first.full, + second.only.trans first.only, second.context.trans first.context, + second.policy.trans first.policy, second.native.trans first.native⟩ + +theorem key (source : KExpr .anon) (before : TcState .anon) : + BetaCacheFrame before (betaWhnfKey source before).2 := by + unfold betaWhnfKey + split + · exact .refl _ + · dsimp only + split <;> exact ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩ + +theorem instrument (before : TcState .anon) : BetaCacheFrame before (betaWhnfPrefix before) := by + unfold betaWhnfPrefix + split <;> exact ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩ + +theorem charge (before : TcState .anon) : BetaCacheFrame before (betaWhnfCharge before) := by + unfold betaWhnfCharge + split <;> exact ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩ + +end BetaCacheFrame + +namespace BetaCacheExecution + +def writeNoDelta (key : Address × Address) (result : KExpr .anon) (before : TcState .anon) : TcState .anon := + if before.inNativeReduce then before else + {before with env := {before.env with whnfNoDeltaCache := before.env.whnfNoDeltaCache.insert key result}} + +def writeFull (key : Address × Address) (result : KExpr .anon) (before : TcState .anon) : TcState .anon := + if before.inNativeReduce then before else + {before with env := {before.env with whnfCache := before.env.whnfCache.insert key result}} + +theorem writeNoDelta_frame (key : Address × Address) (result : KExpr .anon) (before : TcState .anon) : + BetaCacheFrame before (writeNoDelta key result before) := by + unfold writeNoDelta + split <;> exact ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩ + +theorem writeFull_frame (key : Address × Address) (result : KExpr .anon) (before : TcState .anon) : + BetaCacheFrame before (writeFull key result before) := by + unfold writeFull + split <;> exact ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩ + +theorem writeNoDelta_intern (key : Address × Address) (result : KExpr .anon) (before : TcState .anon) : + (writeNoDelta key result before).env.intern = before.env.intern := by + unfold writeNoDelta + split <;> rfl + +theorem writeFull_intern (key : Address × Address) (result : KExpr .anon) (before : TcState .anon) : + (writeFull key result before).env.intern = before.env.intern := by + unfold writeFull + split <;> rfl + +theorem writeNoDelta_key (source result : KExpr .anon) (key : Address × Address) (before : TcState .anon) : + (betaWhnfKey source (writeNoDelta key result before)).1 = (betaWhnfKey source before).1 := by + unfold writeNoDelta + split <;> exact betaWhnfKey_congr source rfl rfl rfl rfl + +theorem writeFull_key (source result : KExpr .anon) (key : Address × Address) (before : TcState .anon) : + (betaWhnfKey source (writeFull key result before)).1 = (betaWhnfKey source before).1 := by + unfold writeFull + split <;> exact betaWhnfKey_congr source rfl rfl rfl rfl + +end BetaCacheExecution + +/-- Structural beta execution either runs its raw path or retains the +execution that produced the exact cached result. Cache presence supplies no +semantic typing assumption. -/ +inductive BetaCoreExecution {β : Type u} (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) : Nat → TcState .anon → KExpr .anon → AExpr β → KExpr .anon → AExpr β → Type u + | reduce {fuel before source term result target reduced steps} + (path : BetaWhnfTrace resolve locals fuel .FULL steps + (betaWhnfKey source before).2 source term reduced result target) + (moving : 0 < steps) + (enough : steps < maxWhnfCoreFuel.toNat) + (miss : (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? = none) + (terminal : BetaWhnfTerminal result) : + BetaCoreExecution resolve locals fuel before source term result target + | cached {fuel before source term result target originFuel originBefore} + (origin : BetaCoreExecution resolve locals originFuel originBefore source term result target) + (coherent : originBefore.env.intern.WF) + (hit : (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? = some result) : + BetaCoreExecution resolve locals fuel before source term result target + +namespace BetaCoreExecution + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {fuel : Nat} {before : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} + +def after (execution : BetaCoreExecution resolve locals fuel before source term result target) : TcState .anon := + match execution with + | .reduce (reduced := reduced) _ _ _ _ _ => + {reduced with env := {reduced.env with + whnfCoreCache := reduced.env.whnfCoreCache.insert (betaWhnfKey source before).1 result}} + | .cached .. => (betaWhnfKey source before).2 + +/-- A retained first step gives the source's lambda spine at every later +cache use. It does not require repeating reduction in the later state. -/ +def first {fuel : Nat} {before : TcState .anon} + (execution : BetaCoreExecution resolve locals fuel before source term result target) : + Σ state, BetaStepPlan resolve locals state source term := + match execution with + | .reduce path moving .. => ⟨_, path.first moving⟩ + | .cached origin .. => origin.first + +theorem terminal {fuel : Nat} {before : TcState .anon} + (execution : BetaCoreExecution resolve locals fuel before source term result target) : + BetaWhnfTerminal result := + match execution with + | .reduce _ _ _ _ terminal => terminal + | .cached origin .. => origin.terminal + +theorem run (execution : BetaCoreExecution resolve locals fuel before source term result target) : + (RecM.whnfCore source).run (methodsN (fuel + 1)) before = .ok result execution.after := by + have entry : RecM.whnfCore source = RecM.whnfCoreWithFlagsNonLeaf source .FULL := by + rw [execution.first.2.sourceEq]; rfl + rw [entry] + cases execution with + | reduce path moving enough miss terminal => + exact RecM.whnfCoreWithFlagsNonLeaf_fullMiss rfl (betaWhnfKey_run source _) + ((path.first moving).not_transient _ _) miss (path.run enough) + | cached origin coherent hit => + exact RecM.whnfCoreWithFlagsNonLeaf_fullHit rfl (betaWhnfKey_run source _) + (origin.first.2.not_transient _ _) hit + +theorem reading (execution : BetaCoreExecution resolve locals fuel before source term result target) + (sourceReading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) : + readScopedExpr? resolve locals result = some target.erase ∧ execution.after.env.intern.WF := by + induction execution with + | reduce path moving enough miss terminal => + exact path.reading sourceReading ((betaWhnfKey_environment _ _).symm ▸ coherent) + | cached origin initial hit ih => + refine ⟨(ih sourceReading initial).1, ?_⟩ + simpa only [after, betaWhnfKey_environment] using coherent + +theorem frame (execution : BetaCoreExecution resolve locals fuel before source term result target) : + BetaCacheFrame before execution.after := by + cases execution with + | reduce path moving enough miss terminal => + obtain ⟨table, reduced⟩ := path.frame + have keyed := BetaCacheFrame.key source before + simp only [after, reduced] + exact ⟨keyed.constants, keyed.full, keyed.only, keyed.context, keyed.policy, keyed.native⟩ + | cached => exact .key source before + +theorem stable_key (execution : BetaCoreExecution resolve locals fuel before source term result target) : + (betaWhnfKey source execution.after).1 = (betaWhnfKey source before).1 := by + cases execution with + | reduce path moving enough miss terminal => + obtain ⟨table, reduced⟩ := path.frame + simp only [after, reduced] + apply Eq.trans (b := (betaWhnfKey source (betaWhnfKey source before).2).1) + · exact betaWhnfKey_congr source rfl rfl rfl rfl + · exact congrArg Prod.fst (betaWhnfKey_replay source before) + | cached => exact congrArg Prod.fst (betaWhnfKey_replay source before) + +/-- Structural WHNF publishes on a miss even while native reduction is +active; a hit retains the same entry. -/ +theorem published (execution : BetaCoreExecution resolve locals fuel before source term result target) : + execution.after.env.whnfCoreCache[(betaWhnfKey source before).1]? = some result := by + cases execution with + | reduce => simp only [after, Std.HashMap.getElem?_insert_self] + | cached origin coherent hit => exact hit + +/-- The producing execution supplies the later hit, including a newly +memoized context suffix. No cache-equality observation is supplied. -/ +def replay (execution : BetaCoreExecution resolve locals fuel before source term result target) + (coherent : before.env.intern.WF) (nextFuel : Nat) : + BetaCoreExecution resolve locals nextFuel execution.after source term result target := + .cached execution coherent (by + rw [betaWhnfKey_environment, execution.stable_key] + exact execution.published) + +/-- An actual published hit needs no recursive methods or shared fuel. -/ +theorem replay_run (execution : BetaCoreExecution resolve locals fuel before source term result target) + (methods : Methods .anon) : + (RecM.whnfCore source).run methods execution.after = + .ok result (betaWhnfKey source execution.after).2 := by + have entry : RecM.whnfCore source = RecM.whnfCoreWithFlagsNonLeaf source .FULL := by + rw [execution.first.2.sourceEq]; rfl + rw [entry] + exact RecM.whnfCoreWithFlagsNonLeaf_fullHit rfl (betaWhnfKey_run source _) + (execution.first.2.not_transient _ _) (by + rw [betaWhnfKey_environment, execution.stable_key] + exact execution.published) + +end BetaCoreExecution + +/-- The no-delta layer may use its own retained result or an executed +structural reduction, which may itself be a cache hit. -/ +inductive BetaNoDeltaExecution {β : Type u} (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) : Nat → TcState .anon → KExpr .anon → AExpr β → KExpr .anon → AExpr β → Type u + | reduce {fuel before source term result target} + (core : BetaCoreExecution resolve locals fuel (betaWhnfKey source before).2 source term result target) + (miss : (betaWhnfKey source before).2.env.whnfNoDeltaCache[(betaWhnfKey source before).1]? = none) : + BetaNoDeltaExecution resolve locals fuel before source term result target + | cached {fuel before source term result target originFuel originBefore} + (origin : BetaNoDeltaExecution resolve locals originFuel originBefore source term result target) + (coherent : originBefore.env.intern.WF) + (hit : (betaWhnfKey source before).2.env.whnfNoDeltaCache[(betaWhnfKey source before).1]? = some result) : + BetaNoDeltaExecution resolve locals fuel before source term result target + +namespace BetaNoDeltaExecution + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {fuel : Nat} {before : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} + +def after (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) : TcState .anon := + match execution with + | .reduce core _ => BetaCacheExecution.writeNoDelta (betaWhnfKey source before).1 result core.after + | .cached .. => (betaWhnfKey source before).2 + +def first {fuel : Nat} {before : TcState .anon} + (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) : + Σ state, BetaStepPlan resolve locals state source term := + match execution with + | .reduce core _ => core.first + | .cached origin .. => origin.first + +theorem terminal {fuel : Nat} {before : TcState .anon} + (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) : + BetaWhnfTerminal result := + match execution with + | .reduce core _ => core.terminal + | .cached origin .. => origin.terminal + +theorem run (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) : + (RecM.whnfNoDelta source).run (methodsN (fuel + 1)) before = .ok result execution.after := by + have entry : RecM.whnfNoDelta source = RecM.whnfNoDeltaImplNonLeaf source .FULL .collapse := by + rw [execution.first.2.sourceEq]; rfl + rw [entry] + cases execution with + | reduce core miss => + exact RecM.whnfNoDeltaImplNonLeaf_fullMiss_conditional rfl (betaWhnfKey_run source _) + (core.first.2.not_transient _ _) miss (core.terminal.noDelta_uncached .FULL .collapse core.run) + | cached origin coherent hit => + exact RecM.whnfNoDeltaImplNonLeaf_fullHit rfl (betaWhnfKey_run source _) + (origin.first.2.not_transient _ _) hit + +theorem reading (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) + (sourceReading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) : + readScopedExpr? resolve locals result = some target.erase ∧ execution.after.env.intern.WF := by + induction execution with + | reduce core miss => + obtain ⟨reading, preserved⟩ := core.reading sourceReading ((betaWhnfKey_environment _ _).symm ▸ coherent) + exact ⟨reading, (BetaCacheExecution.writeNoDelta_intern _ _ _).symm ▸ preserved⟩ + | cached origin initial hit ih => + refine ⟨(ih sourceReading initial).1, ?_⟩ + simpa only [after, betaWhnfKey_environment] using coherent + +theorem frame (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) : + BetaCacheFrame before execution.after := by + cases execution with + | reduce core miss => + exact (BetaCacheFrame.key source before).trans + (core.frame.trans (BetaCacheExecution.writeNoDelta_frame _ _ _)) + | cached => exact .key source before + +theorem stable_key (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) : + (betaWhnfKey source execution.after).1 = (betaWhnfKey source before).1 := by + cases execution with + | reduce core miss => + exact (BetaCacheExecution.writeNoDelta_key _ _ _ _).trans + (core.stable_key.trans (congrArg Prod.fst (betaWhnfKey_replay source before))) + | cached => exact congrArg Prod.fst (betaWhnfKey_replay source before) + +theorem published (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) + (inactive : before.inNativeReduce = false) : + execution.after.env.whnfNoDeltaCache[(betaWhnfKey source before).1]? = some result := by + cases execution with + | reduce core miss => + have native : core.after.inNativeReduce = false := + core.frame.native.trans ((betaWhnfKey_native _ _).trans inactive) + simp only [after, BetaCacheExecution.writeNoDelta, native, Bool.false_eq_true, + if_false, Std.HashMap.getElem?_insert_self] + | cached origin coherent hit => exact hit + +def replay (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) + (coherent : before.env.intern.WF) (inactive : before.inNativeReduce = false) (nextFuel : Nat) : + BetaNoDeltaExecution resolve locals nextFuel execution.after source term result target := + .cached execution coherent (by + rw [betaWhnfKey_environment, execution.stable_key] + exact execution.published inactive) + +theorem replay_run (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) + (inactive : before.inNativeReduce = false) (methods : Methods .anon) : + (RecM.whnfNoDelta source).run methods execution.after = + .ok result (betaWhnfKey source execution.after).2 := by + have entry : RecM.whnfNoDelta source = RecM.whnfNoDeltaImplNonLeaf source .FULL .collapse := by + rw [execution.first.2.sourceEq]; rfl + rw [entry] + exact RecM.whnfNoDeltaImplNonLeaf_fullHit rfl (betaWhnfKey_run source _) + (execution.first.2.not_transient _ _) (by + rw [betaWhnfKey_environment, execution.stable_key] + exact execution.published inactive) + +end BetaNoDeltaExecution + +/-- Public WHNF composes its own cache policy with the two lower layers. +Only an outer miss charges shared fuel. Native reduction can suppress writes +at either upper layer without invalidating the executed beta result. -/ +inductive BetaPublicExecution {β : Type u} (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) : Nat → TcState .anon → KExpr .anon → AExpr β → KExpr .anon → AExpr β → Type u + | reduce {fuel before source term result target} + (inner : BetaNoDeltaExecution resolve locals fuel + (betaWhnfCharge (BetaPublicWhnf.outerKey source before).2) source term result target) + (miss : (BetaPublicWhnf.outerKey source before).2.env.whnfCache[ + (BetaPublicWhnf.outerKey source before).1]? = none) + (fuelAvailable : (before.recFuel == 0) = false) : + BetaPublicExecution resolve locals fuel before source term result target + | cached {fuel before source term result target originFuel originBefore} + (origin : BetaPublicExecution resolve locals originFuel originBefore source term result target) + (coherent : originBefore.env.intern.WF) + (hit : (BetaPublicWhnf.outerKey source before).2.env.whnfCache[ + (BetaPublicWhnf.outerKey source before).1]? = some result) : + BetaPublicExecution resolve locals fuel before source term result target + +namespace BetaPublicExecution + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {fuel : Nat} {before : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} + +def after (execution : BetaPublicExecution resolve locals fuel before source term result target) : TcState .anon := + match execution with + | .reduce inner .. => BetaCacheExecution.writeFull (BetaPublicWhnf.outerKey source before).1 result inner.after + | .cached .. => (BetaPublicWhnf.outerKey source before).2 + +def first {fuel : Nat} {before : TcState .anon} + (execution : BetaPublicExecution resolve locals fuel before source term result target) : + Σ state, BetaStepPlan resolve locals state source term := + match execution with + | .reduce inner .. => inner.first + | .cached origin .. => origin.first + +theorem terminal {fuel : Nat} {before : TcState .anon} + (execution : BetaPublicExecution resolve locals fuel before source term result target) : + BetaWhnfTerminal result := + match execution with + | .reduce inner .. => inner.terminal + | .cached origin .. => origin.terminal + +theorem run (execution : BetaPublicExecution resolve locals fuel before source term result target) : + (RecM.whnf source).run (methodsN (fuel + 1)) before = .ok result execution.after := by + have entry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := by + rw [execution.first.2.sourceEq]; rfl + rw [entry] + cases execution with + | reduce inner miss fuelAvailable => + exact RecM.whnfWithNatSuccModeNonLeaf_miss_conditional (betaWhnfPrefix_run source before _) + (betaWhnfKey_run source _) (inner.first.2.not_transient _ _) miss + (betaWhnfCharge_run _ _ (by + simpa only [BetaPublicWhnf.outerKey, betaWhnfKey_fuel, + (betaWhnfPrefix_fields before).2.2.2] using fuelAvailable)) + (inner.terminal.full_uncached .collapse inner.run) + | cached origin coherent hit => + exact RecM.whnfWithNatSuccModeNonLeaf_hit (betaWhnfPrefix_run source before _) + (betaWhnfKey_run source _) (origin.first.2.not_transient _ _) hit + +theorem reading (execution : BetaPublicExecution resolve locals fuel before source term result target) + (sourceReading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) : + readScopedExpr? resolve locals result = some target.erase ∧ execution.after.env.intern.WF := by + induction execution with + | @reduce fuel before source term result target inner miss fuelAvailable => + have initial : (betaWhnfCharge (BetaPublicWhnf.outerKey source before).2).env.intern.WF := by + simpa only [BetaPublicWhnf.outerKey, betaWhnfKey_environment, + betaWhnfCharge_fields, betaWhnfPrefix_fields] using coherent + obtain ⟨reading, preserved⟩ := inner.reading sourceReading initial + exact ⟨reading, (BetaCacheExecution.writeFull_intern _ _ _).symm ▸ preserved⟩ + | cached origin initial hit ih => + refine ⟨(ih sourceReading initial).1, ?_⟩ + simpa only [after, BetaPublicWhnf.outerKey, betaWhnfKey_environment, + betaWhnfPrefix_fields] using coherent + +theorem frame (execution : BetaPublicExecution resolve locals fuel before source term result target) : + BetaCacheFrame before execution.after := by + have keyed : BetaCacheFrame before (BetaPublicWhnf.outerKey source before).2 := + (BetaCacheFrame.instrument before).trans (.key source _) + cases execution with + | reduce inner miss fuelAvailable => + exact keyed.trans ((BetaCacheFrame.charge _).trans + (inner.frame.trans (BetaCacheExecution.writeFull_frame _ _ _))) + | cached => exact keyed + +theorem stable_key (execution : BetaPublicExecution resolve locals fuel before source term result target) : + (betaWhnfKey source execution.after).1 = (betaWhnfKey source before).1 := by + have keyed : (betaWhnfKey source (BetaPublicWhnf.outerKey source before).2).1 = + (betaWhnfKey source before).1 := + (congrArg Prod.fst (betaWhnfKey_replay source (betaWhnfPrefix before))).trans + (betaWhnfKey_prefix source before) + cases execution with + | reduce inner miss fuelAvailable => + exact (BetaCacheExecution.writeFull_key _ _ _ _).trans + (inner.stable_key.trans ((betaWhnfKey_charge _ _).trans keyed)) + | cached => exact keyed + +theorem published (execution : BetaPublicExecution resolve locals fuel before source term result target) + (inactive : before.inNativeReduce = false) : + execution.after.env.whnfCache[(betaWhnfKey source before).1]? = some result := by + have key : (BetaPublicWhnf.outerKey source before).1 = (betaWhnfKey source before).1 := + betaWhnfKey_prefix source before + cases execution with + | reduce inner miss fuelAvailable => + have native : inner.after.inNativeReduce = false := by + rw [inner.frame.native, BetaPublicWhnf.outerKey] + simpa only [betaWhnfCharge_fields, betaWhnfKey_native, betaWhnfPrefix_fields] using inactive + simp only [after, BetaCacheExecution.writeFull, native, Bool.false_eq_true, + if_false, key, Std.HashMap.getElem?_insert_self] + | cached origin coherent hit => simpa only [after, key] using hit + +def replay (execution : BetaPublicExecution resolve locals fuel before source term result target) + (coherent : before.env.intern.WF) (inactive : before.inNativeReduce = false) (nextFuel : Nat) : + BetaPublicExecution resolve locals nextFuel execution.after source term result target := + .cached execution coherent (by + rw [BetaPublicWhnf.outerKey, betaWhnfKey_environment, (betaWhnfPrefix_fields _).1, + betaWhnfKey_prefix, execution.stable_key] + exact execution.published inactive) + +theorem replay_run (execution : BetaPublicExecution resolve locals fuel before source term result target) + (inactive : before.inNativeReduce = false) (methods : Methods .anon) : + (RecM.whnf source).run methods execution.after = + .ok result (BetaPublicWhnf.outerKey source execution.after).2 := by + have entry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := by + rw [execution.first.2.sourceEq]; rfl + rw [entry] + exact RecM.whnfWithNatSuccModeNonLeaf_hit (betaWhnfPrefix_run source _ _) + (betaWhnfKey_run source _) (execution.first.2.not_transient _ _) (by + rw [BetaPublicWhnf.outerKey, betaWhnfKey_environment, (betaWhnfPrefix_fields _).1, + betaWhnfKey_prefix, execution.stable_key] + exact execution.published inactive) + +end BetaPublicExecution + +/-- The earlier three-miss interface is a special case of the complete +cache-layer execution. No new observation or typing premise is needed. -/ +def BetaPublicWhnfPlan.execution {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : + BetaPublicExecution resolve locals fuel before source term result target := + .reduce (.reduce (.reduce plan.path plan.moving plan.enough plan.coreMiss plan.terminal) + plan.noDeltaMiss) plan.outerMiss plan.fuelAvailable + +theorem BetaPublicWhnfPlan.execution_after {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : + plan.execution.after = plan.after := by + have executed := plan.execution.run + rw [plan.run] at executed + exact (EStateM.Result.ok.inj executed).2.symm + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaCacheKeys.lean b/Ix/Kernel/Verify/Consistency/BetaCacheKeys.lean new file mode 100644 index 000000000..b9d25c9a2 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaCacheKeys.lean @@ -0,0 +1,63 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaPublicWhnfPlan + +/-! WHNF key stability through beta reduction and cache publication. These +facts concern the actual memoized key, including loose-variable contexts. -/ + +namespace Ix.Kernel.Consistency + +private theorem suffixStep_congr {before after : TcState .anon} + (context : after.ctx = before.ctx) (values : after.letVals = before.letVals) (need : Nat) : + TcM.ctxSuffixNeedStep after need = TcM.ctxSuffixNeedStep before need := by + unfold TcM.ctxSuffixNeedStep + simp only [context, values] + +private theorem suffixNeed_congr {before after : TcState .anon} + (context : after.ctx = before.ctx) (values : after.letVals = before.letVals) : + ∀ fuel need, TcM.ctxSuffixNeed after fuel need = TcM.ctxSuffixNeed before fuel need + | 0, _ => rfl + | fuel + 1, need => by + simp only [TcM.ctxSuffixNeed, suffixStep_congr context values] + split + · rfl + · exact suffixNeed_congr context values fuel _ + +theorem betaWhnfKey_congr (source : KExpr .anon) {before after : TcState .anon} + (context : after.ctx = before.ctx) (values : after.letVals = before.letVals) + (identity : after.ctxId = before.ctxId) (memo : after.ctxAddrCache = before.ctxAddrCache) : + (betaWhnfKey source after).1 = (betaWhnfKey source before).1 := by + have digest (radius : UInt64) : TcM.ctxAddrForLbrUncached after radius = + TcM.ctxAddrForLbrUncached before radius := by + unfold TcM.ctxAddrForLbrUncached + simp only [context, suffixNeed_congr context values, values, identity] + unfold betaWhnfKey + simp only [context, identity, memo, digest] + by_cases fast : (source.lbr == 0 || before.ctx.isEmpty) = true + · simp only [fast, if_true] + · simp only [fast] + cases before.ctxAddrCache[(before.ctxId, source.lbr)]? <;> rfl + +/-- A freshly memoized digest is reused exactly on the next key lookup. +No collision or semantic cache premise is needed for this execution fact. -/ +theorem betaWhnfKey_replay (source : KExpr .anon) (before : TcState .anon) : + betaWhnfKey source (betaWhnfKey source before).2 = betaWhnfKey source before := by + by_cases fast : (source.lbr == 0 || before.ctx.isEmpty) = true + · simp [betaWhnfKey, fast] + · cases memo : before.ctxAddrCache[(before.ctxId, source.lbr)]? <;> + simp [betaWhnfKey, fast, memo] + +theorem betaWhnfKey_prefix (source : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey source (betaWhnfPrefix before)).1 = (betaWhnfKey source before).1 := by + unfold betaWhnfPrefix + split <;> exact betaWhnfKey_congr source rfl rfl rfl rfl + +theorem betaWhnfKey_charge (source : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey source (betaWhnfCharge before)).1 = (betaWhnfKey source before).1 := by + unfold betaWhnfCharge + split <;> exact betaWhnfKey_congr source rfl rfl rfl rfl + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean b/Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean index 287d4f5fa..7b5e39275 100644 --- a/Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean +++ b/Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean @@ -3,11 +3,10 @@ Copyright (c) 2026 Argument Computer Corporation. SPDX-License-Identifier: MIT OR Apache-2.0 -/ -import Ix.Kernel.Verify.Consistency.BetaWhnfPlan -import Ix.Kernel.Verify.Whnf.Driver.CacheExecution +import Ix.Kernel.Verify.Consistency.BetaCacheExecution -/-! Connect raw beta paths to the public WHNF drivers, their cache writes, -and the Pi exposure used by application inference. -/ +/-! Pi and sort exposure follow public beta WHNF, including retained +reduction origins at each cache layer. -/ namespace Ix.Kernel.Consistency @@ -15,365 +14,8 @@ open Theory Theory.Model universe u v -/-- Compute exactly the context-digest memoization performed by a WHNF key. -/ -def betaWhnfKey (source : KExpr .anon) (before : TcState .anon) : - (Address × Address) × TcState .anon := - if source.lbr == 0 || before.ctx.isEmpty then - ((source.addr, emptyCtxAddr), before) - else - let cacheKey := (before.ctxId, source.lbr) - match before.ctxAddrCache[cacheKey]? with - | some cached => ((source.addr, cached), before) - | none => - let digest := TcM.ctxAddrForLbrUncached before source.lbr - ((source.addr, digest), { before with ctxAddrCache := before.ctxAddrCache.insert cacheKey digest }) - -theorem betaWhnfKey_run (source : KExpr .anon) (before : TcState .anon) : - TcM.whnfKey source before = .ok (betaWhnfKey source before).1 (betaWhnfKey source before).2 := by - unfold TcM.whnfKey TcM.ctxAddrForLbr betaWhnfKey - change EStateM.bind (fun state => EStateM.bind (get : TcM .anon (TcState .anon)) _ state) _ before = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] - by_cases fast : (source.lbr == 0 || before.ctx.isEmpty) = true - · simp only [if_pos fast]; rfl - · simp only [if_neg fast] - cases before.ctxAddrCache[(before.ctxId, source.lbr)]? <;> rfl - -theorem betaWhnfKey_environment (source : KExpr .anon) (before : TcState .anon) : - (betaWhnfKey source before).2.env = before.env := by - unfold betaWhnfKey - split - · rfl - · dsimp only - split <;> rfl - -theorem betaWhnfKey_context (source : KExpr .anon) (before : TcState .anon) : - (betaWhnfKey source before).2.lctx = before.lctx := by - unfold betaWhnfKey - split - · rfl - · dsimp only - split <;> rfl - -theorem betaWhnfKey_native (source : KExpr .anon) (before : TcState .anon) : - (betaWhnfKey source before).2.inNativeReduce = before.inNativeReduce := by - unfold betaWhnfKey - split - · rfl - · dsimp only - split <;> rfl - -/-- Full WHNF's instrumentation changes only its optional call counter. -/ -def betaWhnfPrefix (before : TcState .anon) : TcState .anon := - if before.stats then { before with whnfCalls := before.whnfCalls + 1 } else before - -theorem betaWhnfPrefix_run (source : KExpr .anon) (before : TcState .anon) (methods : Methods .anon) : - (RecM.whnfWithNatSuccModePrefix source).run methods before = .ok () (betaWhnfPrefix before) := by - have traced : TcM.stepTrace "whnf+" (fun _ => TcM.addr8 source.addr) before = .ok () before := by - unfold TcM.stepTrace - change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] - split <;> rfl - unfold RecM.whnfWithNatSuccModePrefix - rw [ReaderT.run_bind] - change EStateM.bind (TcM.stepTrace "whnf+" (fun _ => TcM.addr8 source.addr)) _ before = _ - rw [EStateM.bind, traced] - change TcM.bumpStats (fun state => { state with whnfCalls := state.whnfCalls + 1 }) before = _ - unfold TcM.bumpStats betaWhnfPrefix - change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] - split <;> rfl - -/-- The miss charge performs one shared-fuel decrement and optionally -increments its miss counter. -/ -def betaWhnfCharge (before : TcState .anon) : TcState .anon := - if before.stats then - { before with whnfMisses := before.whnfMisses + 1, recFuel := before.recFuel - 1 } - else { before with recFuel := before.recFuel - 1 } - -theorem betaWhnfCharge_run (before : TcState .anon) (methods : Methods .anon) - (enough : (before.recFuel == 0) = false) : - (RecM.whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods before = - .ok () (betaWhnfCharge before) := by - unfold RecM.whnfWithNatSuccModeMissCharge - rw [ReaderT.run_bind] - change EStateM.bind (TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 })) - (fun _ => TcM.tick) before = _ - cases stats : before.stats - · have bumped : TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 }) before = - .ok () before := by - unfold TcM.bumpStats - change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] - rw [if_neg (by simp only [stats, Bool.false_eq_true, not_false_eq_true])]; rfl - rw [EStateM.bind, bumped] - change TcM.tick before = _ - unfold TcM.tick betaWhnfCharge - change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] - rw [enough, stats]; rfl - · have bumped : TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 }) before = - .ok () { before with whnfMisses := before.whnfMisses + 1 } := by - unfold TcM.bumpStats - change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] - rw [if_pos stats]; rfl - rw [EStateM.bind, bumped] - change TcM.tick { before with whnfMisses := before.whnfMisses + 1 } = _ - unfold TcM.tick betaWhnfCharge - change EStateM.bind (get : TcM .anon (TcState .anon)) _ { before with whnfMisses := before.whnfMisses + 1 } = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) - { before with whnfMisses := before.whnfMisses + 1 } = - .ok { before with whnfMisses := before.whnfMisses + 1 } - { before with whnfMisses := before.whnfMisses + 1 } from rfl] - rw [enough, stats]; rfl - -/-- Constructors on which both public drivers and all their reducer tails -stop without recursive callbacks or source lookup. -/ -inductive BetaWhnfTerminal : KExpr .anon → Prop - | sort (level : KUniv .anon) (info : ExprInfo .anon) : BetaWhnfTerminal (.sort level info) - | forallE (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) - (domain body : KExpr .anon) (info : ExprInfo .anon) : BetaWhnfTerminal (.all name bi domain body info) - | lam (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) - (domain body : KExpr .anon) (info : ExprInfo .anon) : BetaWhnfTerminal (.lam name bi domain body info) - -theorem BetaWhnfTerminal.noDelta_tail {source : KExpr .anon} (terminal : BetaWhnfTerminal source) - (methods : Methods .anon) (before : TcState .anon) (flags : WhnfFlags) (mode : NatSuccMode) : - (RecM.whnfNoDeltaReducersStep flags mode source).run methods before = .ok (.done source) before := by - cases terminal <;> - simp [RecM.whnfNoDeltaReducersStep, RecM.tryProjAppReduceFinished, RecM.tryProjAppReduce, - RecM.tryReduceBitvec, RecM.tryReduceNatWithSuccMode, RecM.tryReduceNative, - RecM.tryReduceString, RecM.tryReduceProjectionDefinition, RecM.tryQuotReduce, - KExpr.collectSpine, KExpr.collectSpine.go, RecM.prims, ReaderT.run_bind] - all_goals - change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] - cases before.noAccel <;> rfl - -theorem BetaWhnfTerminal.full_step {source result : KExpr .anon} - (terminal : BetaWhnfTerminal result) {methods : Methods .anon} {before after : TcState .anon} - (mode : NatSuccMode) (seen : Std.HashSet Address) - (reduced : (RecM.whnfNoDeltaImpl source .FULL mode).run methods before = .ok result after) : - (RecM.whnfWithNatSuccModeStep mode (source, seen)).run methods before = .ok (.done result) after := by - unfold RecM.whnfWithNatSuccModeStep - rw [ReaderT.run_bind] - change EStateM.bind ((RecM.whnfNoDeltaImpl source .FULL mode).run methods) _ before = _ - rw [EStateM.bind, reduced] - simp only - by_cases repeated : seen.contains result.addr = true - · rw [if_pos repeated]; rfl - · rw [if_neg repeated] - cases terminal <;> - simp [RecM.tryReduceNative, RecM.tryReduceBitvec, RecM.tryReduceNatWithSuccMode, - RecM.tryReduceDecidable, RecM.tryReduceString, RecM.tryNatOffsetStuck, - RecM.natOffsetStuckHead, RecM.deltaUnfoldOne, RecM.tryDeltaUnfold, - KExpr.collectSpine, KExpr.collectSpine.go, RecM.prims, ReaderT.run_bind] - all_goals - change EStateM.bind (get : TcM .anon (TcState .anon)) _ after = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) after = .ok after after from rfl] - cases native : after.noAccel - all_goals - change EStateM.bind (get : TcM .anon (TcState .anon)) _ after = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) after = .ok after after from rfl] - simp only [native] - rfl - -private theorem bounded_done {α γ : Type} (step : α → RecM .anon (RecM.BoundedStep α γ)) - {methods : Methods .anon} {before after : TcState .anon} {source : α} {result : γ} - (done : (step source).run methods before = .ok (.done result) after) - {fuel : Nat} (enough : 0 < fuel) : - (RecM.runBounded step fuel source).run methods before = .ok result after := by - cases fuel with - | zero => omega - | succ fuel => - rw [RecM.runBounded, ReaderT.run_bind] - change EStateM.bind ((step source).run methods) _ before = _ - rw [EStateM.bind, done] - rfl - -theorem BetaWhnfTerminal.noDelta_uncached {source result : KExpr .anon} - (terminal : BetaWhnfTerminal result) {methods : Methods .anon} {before after : TcState .anon} - (flags : WhnfFlags) (mode : NatSuccMode) - (reduced : (RecM.whnfCoreWithFlags source flags).run methods before = .ok result after) : - (RecM.whnfNoDeltaImplUncached source flags mode).run methods before = .ok result after := by - apply bounded_done (enough := by decide) - unfold RecM.whnfNoDeltaImplStep - rw [ReaderT.run_bind] - change EStateM.bind ((RecM.whnfCoreWithFlags source flags).run methods) _ before = _ - rw [EStateM.bind, reduced] - exact terminal.noDelta_tail methods after flags mode - -theorem BetaWhnfTerminal.full_uncached {source result : KExpr .anon} - (terminal : BetaWhnfTerminal result) {methods : Methods .anon} {before after : TcState .anon} - (mode : NatSuccMode) - (reduced : (RecM.whnfNoDeltaImpl source .FULL mode).run methods before = .ok result after) : - (RecM.whnfWithNatSuccModeUncached source mode).run methods before = .ok result after := - bounded_done _ (terminal.full_step mode {} reduced) (by decide) - -def BetaWhnfTrace.first {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} - {fuel steps : Nat} {flags : WhnfFlags} {before after : TcState .anon} - {source result : KExpr .anon} {term target : AExpr β} - (trace : BetaWhnfTrace resolve locals fuel flags steps before source term after result target) : - 0 < steps → BetaStepPlan resolve locals before source term := - match trace with - | .done _ => fun impossible => False.elim (Nat.not_lt_zero _ impossible) - | .next plan _ => fun _ => plan - -theorem BetaStepPlan.not_transient {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} - {before : TcState .anon} {source : KExpr .anon} {term : AExpr β} - (plan : BetaStepPlan resolve locals before source term) (methods : Methods .anon) (state : TcState .anon) : - (RecM.isTransientNatLiteralWork source).run methods state = .ok false state := by - simp only [plan.sourceEq, RecM.isTransientNatLiteralWork, RecM.isNatLiteralRecursorApp, - plan.spine, pure_bind] - rfl - -theorem betaWhnfKey_fuel (source : KExpr .anon) (before : TcState .anon) : - (betaWhnfKey source before).2.recFuel = before.recFuel := by - unfold betaWhnfKey - split - · rfl - · dsimp only - split <;> rfl - -theorem betaWhnfPrefix_fields (before : TcState .anon) : - (betaWhnfPrefix before).env = before.env ∧ - (betaWhnfPrefix before).lctx = before.lctx ∧ - (betaWhnfPrefix before).inNativeReduce = before.inNativeReduce ∧ - (betaWhnfPrefix before).recFuel = before.recFuel := by - unfold betaWhnfPrefix - split <;> exact ⟨rfl, rfl, rfl, rfl⟩ - -theorem betaWhnfCharge_fields (before : TcState .anon) : - (betaWhnfCharge before).env = before.env ∧ - (betaWhnfCharge before).lctx = before.lctx ∧ - (betaWhnfCharge before).inNativeReduce = before.inNativeReduce := by - unfold betaWhnfCharge - split <;> exact ⟨rfl, rfl, rfl⟩ - -namespace BetaPublicWhnf - -def outerKey (source : KExpr .anon) (before : TcState .anon) := - betaWhnfKey source (betaWhnfPrefix before) - -def noDeltaKey (source : KExpr .anon) (before : TcState .anon) := - betaWhnfKey source (betaWhnfCharge (outerKey source before).2) - -def coreKey (source : KExpr .anon) (before : TcState .anon) := - betaWhnfKey source (noDeltaKey source before).2 - -def coreAfter (source result : KExpr .anon) (before reduced : TcState .anon) : TcState .anon := - { reduced with env := { reduced.env with - whnfCoreCache := reduced.env.whnfCoreCache.insert (coreKey source before).1 result } } - -def noDeltaAfter (source result : KExpr .anon) (before reduced : TcState .anon) : TcState .anon := - let state := coreAfter source result before reduced - { state with env := { state.env with - whnfNoDeltaCache := state.env.whnfNoDeltaCache.insert (noDeltaKey source before).1 result } } - -def after (source result : KExpr .anon) (before reduced : TcState .anon) : TcState .anon := - let state := noDeltaAfter source result before reduced - { state with env := { state.env with - whnfCache := state.env.whnfCache.insert (outerKey source before).1 result } } - -theorem coreKey_fields (source : KExpr .anon) (before : TcState .anon) : - (coreKey source before).2.env = before.env ∧ - (coreKey source before).2.lctx = before.lctx ∧ - (coreKey source before).2.inNativeReduce = before.inNativeReduce := by - simp only [coreKey, noDeltaKey, outerKey, betaWhnfKey_environment, betaWhnfKey_context, - betaWhnfKey_native, betaWhnfCharge_fields, betaWhnfPrefix_fields, and_self] - -end BetaPublicWhnf - -/-- A public beta reduction through the three cache-miss layers. The key -states, instrumentation, fuel charge, and final cache insertions are computed; -the only reduction resource is the raw beta path. -/ -structure BetaPublicWhnfPlan {β : Type u} (resolve : Address → Option (ConstRef β)) - (locals : List FVarId) (fuel : Nat) (before : TcState .anon) - (source : KExpr .anon) (term : AExpr β) (result : KExpr .anon) (target : AExpr β) where - steps : Nat - reduced : TcState .anon - path : BetaWhnfTrace resolve locals fuel .FULL steps - (BetaPublicWhnf.coreKey source before).2 source term reduced result target - moving : 0 < steps - enough : steps < maxWhnfCoreFuel.toNat - fuelAvailable : (before.recFuel == 0) = false - native : before.inNativeReduce = false - outerMiss : (BetaPublicWhnf.outerKey source before).2.env.whnfCache[ - (BetaPublicWhnf.outerKey source before).1]? = none - noDeltaMiss : (BetaPublicWhnf.noDeltaKey source before).2.env.whnfNoDeltaCache[ - (BetaPublicWhnf.noDeltaKey source before).1]? = none - coreMiss : (BetaPublicWhnf.coreKey source before).2.env.whnfCoreCache[ - (BetaPublicWhnf.coreKey source before).1]? = none - terminal : BetaWhnfTerminal result - -namespace BetaPublicWhnfPlan - -variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} - {fuel : Nat} {before : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} - -def after (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : TcState .anon := - BetaPublicWhnf.after source result before plan.reduced - -theorem run (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : - (RecM.whnf source).run (methodsN (fuel + 1)) before = .ok result plan.after := by - let first := plan.path.first plan.moving - have coreEntry : RecM.whnfCoreWithFlags source .FULL = RecM.whnfCoreWithFlagsNonLeaf source .FULL := by - rw [first.sourceEq]; rfl - have coreRun : (RecM.whnfCoreWithFlags source .FULL).run (methodsN (fuel + 1)) - (BetaPublicWhnf.noDeltaKey source before).2 = - .ok result (BetaPublicWhnf.coreAfter source result before plan.reduced) := by - rw [coreEntry] - exact RecM.whnfCoreWithFlagsNonLeaf_fullMiss rfl - (betaWhnfKey_run source _) (first.not_transient _ _) plan.coreMiss (plan.path.run plan.enough) - have reducedNative : plan.reduced.inNativeReduce = false := by - obtain ⟨table, frame⟩ := plan.path.frame - exact (congrArg TcState.inNativeReduce frame).trans - ((BetaPublicWhnf.coreKey_fields source before).2.2.trans plan.native) - have noDeltaEntry : RecM.whnfNoDeltaImpl source .FULL .collapse = - RecM.whnfNoDeltaImplNonLeaf source .FULL .collapse := by - rw [first.sourceEq]; rfl - have noDeltaRun : (RecM.whnfNoDeltaImpl source .FULL .collapse).run (methodsN (fuel + 1)) - (betaWhnfCharge (BetaPublicWhnf.outerKey source before).2) = - .ok result (BetaPublicWhnf.noDeltaAfter source result before plan.reduced) := by - rw [noDeltaEntry] - exact RecM.whnfNoDeltaImplNonLeaf_fullMiss rfl (betaWhnfKey_run source _) - (first.not_transient _ _) plan.noDeltaMiss - (plan.terminal.noDelta_uncached .FULL .collapse coreRun) reducedNative - have fullEntry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := by - rw [first.sourceEq]; rfl - rw [fullEntry] - exact RecM.whnfWithNatSuccModeNonLeaf_miss (betaWhnfPrefix_run source before _) - (betaWhnfKey_run source _) (first.not_transient _ _) plan.outerMiss - (betaWhnfCharge_run _ _ (by - simpa only [BetaPublicWhnf.outerKey, betaWhnfKey_fuel, (betaWhnfPrefix_fields before).2.2.2] using plan.fuelAvailable)) - (plan.terminal.full_uncached .collapse noDeltaRun) reducedNative - -theorem reading (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) - (sourceReading : readScopedExpr? resolve locals source = some term.erase) - (coherent : before.env.intern.WF) : - readScopedExpr? resolve locals result = some target.erase ∧ plan.after.env.intern.WF := by - exact plan.path.reading sourceReading ((BetaPublicWhnf.coreKey_fields source before).1.symm ▸ coherent) - -theorem context (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : - plan.after.lctx = before.lctx := by - obtain ⟨table, frame⟩ := plan.path.frame - exact (congrArg TcState.lctx frame).trans (BetaPublicWhnf.coreKey_fields source before).2.1 - -theorem cache_hit (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) - (methods : Methods .anon) (current : TcState .anon) - (hit : (BetaPublicWhnf.outerKey source current).2.env.whnfCache[ - (BetaPublicWhnf.outerKey source current).1]? = some result) : - (RecM.whnf source).run methods current = .ok result (BetaPublicWhnf.outerKey source current).2 := by - let first := plan.path.first plan.moving - have entry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := by - rw [first.sourceEq]; rfl - rw [entry] - exact RecM.whnfWithNatSuccModeNonLeaf_hit (betaWhnfPrefix_run source current _) - (betaWhnfKey_run source _) (first.not_transient _ _) hit - -end BetaPublicWhnfPlan - -/-- Pi exposure retains a raw beta path through public WHNF, or reuses -that path's exact result from the public cache. A cache hit consumes no fuel. -/ +/-- Pi exposure retains raw beta execution through public WHNF and its three +cache layers. Only an outer miss consumes shared fuel. -/ inductive BetaPiExposure {β : Type u} (resolve : Address → Option (ConstRef β)) (locals : List FVarId) (fuel : Nat) (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) (condition : Certified.PropWhen) (domain body : AExpr β) @@ -382,6 +24,10 @@ inductive BetaPiExposure {β : Type u} (resolve : Address → Option (ConstRef (plan : BetaPublicWhnfPlan resolve locals fuel before source term (.all name bi rawDomain rawBody info) (.forallE condition domain body)) : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody + | execute {name bi info} + (execution : BetaPublicExecution resolve locals fuel before source term + (.all name bi rawDomain rawBody info) (.forallE condition domain body)) : + BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody | cached {originFuel originBefore name bi info} (origin : BetaPublicWhnfPlan resolve locals originFuel originBefore source term (.all name bi rawDomain rawBody info) (.forallE condition domain body)) @@ -401,6 +47,7 @@ def after (exposure : BetaPiExposure resolve locals fuel before source term cond match exposure with | .reduce plan => plan.after | .cached .. => (BetaPublicWhnf.outerKey source before).2 + | .execute execution => execution.after theorem run (exposure : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody) : (RecM.ensureForallDirect source).run (methodsN (fuel + 1)) before = @@ -422,6 +69,13 @@ theorem run (exposure : BetaPiExposure resolve locals fuel before source term co change EStateM.bind ((RecM.whnf source).run (methodsN (fuel + 1))) _ before = _ rw [EStateM.bind, origin.cache_hit _ _ hit] rfl + | execute execution => + have entry : RecM.ensureForallDirect source = RecM.ensureForallWhnf source := by + rw [execution.first.2.sourceEq]; rfl + rw [entry, RecM.ensureForallWhnf, ReaderT.run_bind] + change EStateM.bind ((RecM.whnf source).run (methodsN (fuel + 1))) _ before = _ + rw [EStateM.bind, execution.run] + rfl theorem reading (exposure : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody) (sourceReading : readScopedExpr? resolve locals source = some term.erase) @@ -439,11 +93,16 @@ theorem reading (exposure : BetaPiExposure resolve locals fuel before source ter refine ⟨domainReads, bodyReads, ?_⟩ simpa only [after, BetaPublicWhnf.outerKey, betaWhnfKey_environment, (betaWhnfPrefix_fields before).1] using coherent + | execute execution => + obtain ⟨reads, preserved⟩ := execution.reading sourceReading coherent + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reads + exact ⟨domainReads, bodyReads, preserved⟩ theorem context (exposure : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody) : exposure.after.lctx = before.lctx := by cases exposure with | reduce plan => exact plan.context + | execute execution => exact execution.frame.context | cached origin originCoherent hit => simp only [after, BetaPublicWhnf.outerKey, betaWhnfKey_context, (betaWhnfPrefix_fields before).2.1] @@ -461,6 +120,11 @@ inductive BetaSortExposure {β : Type u} (resolve : Address → Option (ConstRef (plan : BetaPublicWhnfPlan resolve locals fuel before source term (.sort level info) (.sort (readLevel level))) : BetaSortExposure resolve locals fuel before source term level + + | execute {source term level info} + (execution : BetaPublicExecution resolve locals fuel before source term + (.sort level info) (.sort (readLevel level))) : + BetaSortExposure resolve locals fuel before source term level | cached {originFuel originBefore source term level info} (origin : BetaPublicWhnfPlan resolve locals originFuel originBefore source term (.sort level info) (.sort (readLevel level))) @@ -478,6 +142,7 @@ def after (exposure : BetaSortExposure resolve locals fuel before source term le | .direct .. => before | .reduce plan => plan.after | .cached .. => (BetaPublicWhnf.outerKey source before).2 + | .execute execution => execution.after theorem run (exposure : BetaSortExposure resolve locals fuel before source term level) : (RecM.ensureSortDirect source).run (methodsN (fuel + 1)) before = .ok level exposure.after := by @@ -499,6 +164,13 @@ theorem run (exposure : BetaSortExposure resolve locals fuel before source term change EStateM.bind ((RecM.whnf source).run (methodsN (fuel + 1))) _ before = _ rw [EStateM.bind, origin.cache_hit _ _ hit] rfl + | execute execution => + have entry : RecM.ensureSortDirect source = RecM.ensureSortWhnf source := by + rw [execution.first.2.sourceEq]; rfl + rw [entry, RecM.ensureSortWhnf, ReaderT.run_bind] + change EStateM.bind ((RecM.whnf source).run (methodsN (fuel + 1))) _ before = _ + rw [EStateM.bind, execution.run] + rfl theorem coherent (exposure : BetaSortExposure resolve locals fuel before source term level) (reading : readScopedExpr? resolve locals source = some term.erase) @@ -506,6 +178,7 @@ theorem coherent (exposure : BetaSortExposure resolve locals fuel before source cases exposure with | direct => exact initial | reduce plan => exact (plan.reading reading initial).2 + | execute execution => exact (execution.reading reading initial).2 | cached origin hit => simpa only [after, BetaPublicWhnf.outerKey, betaWhnfKey_environment, (betaWhnfPrefix_fields before).1] using initial @@ -515,6 +188,7 @@ theorem context (exposure : BetaSortExposure resolve locals fuel before source t cases exposure with | direct => rfl | reduce plan => exact plan.context + | execute execution => exact execution.frame.context | cached origin hit => simp only [after, BetaPublicWhnf.outerKey, betaWhnfKey_context, (betaWhnfPrefix_fields before).2.1] diff --git a/Ix/Kernel/Verify/Consistency/BetaPublicWhnfPlan.lean b/Ix/Kernel/Verify/Consistency/BetaPublicWhnfPlan.lean new file mode 100644 index 000000000..ed737887b --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaPublicWhnfPlan.lean @@ -0,0 +1,376 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaWhnfPlan +import Ix.Kernel.Verify.Whnf.Driver.CacheExecution + +/-! Raw beta paths through the public WHNF drivers and their exact cold-cache +state updates. Cached execution composes these primitives in BetaCacheExecution. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- Compute exactly the context-digest memoization performed by a WHNF key. -/ +def betaWhnfKey (source : KExpr .anon) (before : TcState .anon) : + (Address × Address) × TcState .anon := + if source.lbr == 0 || before.ctx.isEmpty then + ((source.addr, emptyCtxAddr), before) + else + let cacheKey := (before.ctxId, source.lbr) + match before.ctxAddrCache[cacheKey]? with + | some cached => ((source.addr, cached), before) + | none => + let digest := TcM.ctxAddrForLbrUncached before source.lbr + ((source.addr, digest), { before with ctxAddrCache := before.ctxAddrCache.insert cacheKey digest }) + +theorem betaWhnfKey_run (source : KExpr .anon) (before : TcState .anon) : + TcM.whnfKey source before = .ok (betaWhnfKey source before).1 (betaWhnfKey source before).2 := by + unfold TcM.whnfKey TcM.ctxAddrForLbr betaWhnfKey + change EStateM.bind (fun state => EStateM.bind (get : TcM .anon (TcState .anon)) _ state) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + by_cases fast : (source.lbr == 0 || before.ctx.isEmpty) = true + · simp only [if_pos fast]; rfl + · simp only [if_neg fast] + cases before.ctxAddrCache[(before.ctxId, source.lbr)]? <;> rfl + +theorem betaWhnfKey_environment (source : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey source before).2.env = before.env := by + unfold betaWhnfKey + split + · rfl + · dsimp only + split <;> rfl + +theorem betaWhnfKey_context (source : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey source before).2.lctx = before.lctx := by + unfold betaWhnfKey + split + · rfl + · dsimp only + split <;> rfl + +theorem betaWhnfKey_native (source : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey source before).2.inNativeReduce = before.inNativeReduce := by + unfold betaWhnfKey + split + · rfl + · dsimp only + split <;> rfl + +/-- Full WHNF's instrumentation changes only its optional call counter. -/ +def betaWhnfPrefix (before : TcState .anon) : TcState .anon := + if before.stats then { before with whnfCalls := before.whnfCalls + 1 } else before + +theorem betaWhnfPrefix_run (source : KExpr .anon) (before : TcState .anon) (methods : Methods .anon) : + (RecM.whnfWithNatSuccModePrefix source).run methods before = .ok () (betaWhnfPrefix before) := by + have traced : TcM.stepTrace "whnf+" (fun _ => TcM.addr8 source.addr) before = .ok () before := by + unfold TcM.stepTrace + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + split <;> rfl + unfold RecM.whnfWithNatSuccModePrefix + rw [ReaderT.run_bind] + change EStateM.bind (TcM.stepTrace "whnf+" (fun _ => TcM.addr8 source.addr)) _ before = _ + rw [EStateM.bind, traced] + change TcM.bumpStats (fun state => { state with whnfCalls := state.whnfCalls + 1 }) before = _ + unfold TcM.bumpStats betaWhnfPrefix + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + split <;> rfl + +/-- The miss charge performs one shared-fuel decrement and optionally +increments its miss counter. -/ +def betaWhnfCharge (before : TcState .anon) : TcState .anon := + if before.stats then + { before with whnfMisses := before.whnfMisses + 1, recFuel := before.recFuel - 1 } + else { before with recFuel := before.recFuel - 1 } + +theorem betaWhnfCharge_run (before : TcState .anon) (methods : Methods .anon) + (enough : (before.recFuel == 0) = false) : + (RecM.whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods before = + .ok () (betaWhnfCharge before) := by + unfold RecM.whnfWithNatSuccModeMissCharge + rw [ReaderT.run_bind] + change EStateM.bind (TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 })) + (fun _ => TcM.tick) before = _ + cases stats : before.stats + · have bumped : TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 }) before = + .ok () before := by + unfold TcM.bumpStats + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + rw [if_neg (by simp only [stats, Bool.false_eq_true, not_false_eq_true])]; rfl + rw [EStateM.bind, bumped] + change TcM.tick before = _ + unfold TcM.tick betaWhnfCharge + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + rw [enough, stats]; rfl + · have bumped : TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 }) before = + .ok () { before with whnfMisses := before.whnfMisses + 1 } := by + unfold TcM.bumpStats + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + rw [if_pos stats]; rfl + rw [EStateM.bind, bumped] + change TcM.tick { before with whnfMisses := before.whnfMisses + 1 } = _ + unfold TcM.tick betaWhnfCharge + change EStateM.bind (get : TcM .anon (TcState .anon)) _ { before with whnfMisses := before.whnfMisses + 1 } = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) + { before with whnfMisses := before.whnfMisses + 1 } = + .ok { before with whnfMisses := before.whnfMisses + 1 } + { before with whnfMisses := before.whnfMisses + 1 } from rfl] + rw [enough, stats]; rfl + +/-- Constructors on which both public drivers and all their reducer tails +stop without recursive callbacks or source lookup. -/ +inductive BetaWhnfTerminal : KExpr .anon → Prop + | sort (level : KUniv .anon) (info : ExprInfo .anon) : BetaWhnfTerminal (.sort level info) + | forallE (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) + (domain body : KExpr .anon) (info : ExprInfo .anon) : BetaWhnfTerminal (.all name bi domain body info) + | lam (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) + (domain body : KExpr .anon) (info : ExprInfo .anon) : BetaWhnfTerminal (.lam name bi domain body info) + +theorem BetaWhnfTerminal.noDelta_tail {source : KExpr .anon} (terminal : BetaWhnfTerminal source) + (methods : Methods .anon) (before : TcState .anon) (flags : WhnfFlags) (mode : NatSuccMode) : + (RecM.whnfNoDeltaReducersStep flags mode source).run methods before = .ok (.done source) before := by + cases terminal <;> + simp [RecM.whnfNoDeltaReducersStep, RecM.tryProjAppReduceFinished, RecM.tryProjAppReduce, + RecM.tryReduceBitvec, RecM.tryReduceNatWithSuccMode, RecM.tryReduceNative, + RecM.tryReduceString, RecM.tryReduceProjectionDefinition, RecM.tryQuotReduce, + KExpr.collectSpine, KExpr.collectSpine.go, RecM.prims, ReaderT.run_bind] + all_goals + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + cases before.noAccel <;> rfl + +theorem BetaWhnfTerminal.full_step {source result : KExpr .anon} + (terminal : BetaWhnfTerminal result) {methods : Methods .anon} {before after : TcState .anon} + (mode : NatSuccMode) (seen : Std.HashSet Address) + (reduced : (RecM.whnfNoDeltaImpl source .FULL mode).run methods before = .ok result after) : + (RecM.whnfWithNatSuccModeStep mode (source, seen)).run methods before = .ok (.done result) after := by + unfold RecM.whnfWithNatSuccModeStep + rw [ReaderT.run_bind] + change EStateM.bind ((RecM.whnfNoDeltaImpl source .FULL mode).run methods) _ before = _ + rw [EStateM.bind, reduced] + simp only + by_cases repeated : seen.contains result.addr = true + · rw [if_pos repeated]; rfl + · rw [if_neg repeated] + cases terminal <;> + simp [RecM.tryReduceNative, RecM.tryReduceBitvec, RecM.tryReduceNatWithSuccMode, + RecM.tryReduceDecidable, RecM.tryReduceString, RecM.tryNatOffsetStuck, + RecM.natOffsetStuckHead, RecM.deltaUnfoldOne, RecM.tryDeltaUnfold, + KExpr.collectSpine, KExpr.collectSpine.go, RecM.prims, ReaderT.run_bind] + all_goals + change EStateM.bind (get : TcM .anon (TcState .anon)) _ after = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) after = .ok after after from rfl] + cases native : after.noAccel + all_goals + change EStateM.bind (get : TcM .anon (TcState .anon)) _ after = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) after = .ok after after from rfl] + simp only [native] + rfl + +private theorem bounded_done {α γ : Type} (step : α → RecM .anon (RecM.BoundedStep α γ)) + {methods : Methods .anon} {before after : TcState .anon} {source : α} {result : γ} + (done : (step source).run methods before = .ok (.done result) after) + {fuel : Nat} (enough : 0 < fuel) : + (RecM.runBounded step fuel source).run methods before = .ok result after := by + cases fuel with + | zero => omega + | succ fuel => + rw [RecM.runBounded, ReaderT.run_bind] + change EStateM.bind ((step source).run methods) _ before = _ + rw [EStateM.bind, done] + rfl + +theorem BetaWhnfTerminal.noDelta_uncached {source result : KExpr .anon} + (terminal : BetaWhnfTerminal result) {methods : Methods .anon} {before after : TcState .anon} + (flags : WhnfFlags) (mode : NatSuccMode) + (reduced : (RecM.whnfCoreWithFlags source flags).run methods before = .ok result after) : + (RecM.whnfNoDeltaImplUncached source flags mode).run methods before = .ok result after := by + apply bounded_done (enough := by decide) + unfold RecM.whnfNoDeltaImplStep + rw [ReaderT.run_bind] + change EStateM.bind ((RecM.whnfCoreWithFlags source flags).run methods) _ before = _ + rw [EStateM.bind, reduced] + exact terminal.noDelta_tail methods after flags mode + +theorem BetaWhnfTerminal.full_uncached {source result : KExpr .anon} + (terminal : BetaWhnfTerminal result) {methods : Methods .anon} {before after : TcState .anon} + (mode : NatSuccMode) + (reduced : (RecM.whnfNoDeltaImpl source .FULL mode).run methods before = .ok result after) : + (RecM.whnfWithNatSuccModeUncached source mode).run methods before = .ok result after := + bounded_done _ (terminal.full_step mode {} reduced) (by decide) + +def BetaWhnfTrace.first {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {fuel steps : Nat} {flags : WhnfFlags} {before after : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (trace : BetaWhnfTrace resolve locals fuel flags steps before source term after result target) : + 0 < steps → BetaStepPlan resolve locals before source term := + match trace with + | .done _ => fun impossible => False.elim (Nat.not_lt_zero _ impossible) + | .next plan _ => fun _ => plan + +theorem BetaStepPlan.not_transient {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {before : TcState .anon} {source : KExpr .anon} {term : AExpr β} + (plan : BetaStepPlan resolve locals before source term) (methods : Methods .anon) (state : TcState .anon) : + (RecM.isTransientNatLiteralWork source).run methods state = .ok false state := by + simp only [plan.sourceEq, RecM.isTransientNatLiteralWork, RecM.isNatLiteralRecursorApp, + plan.spine, pure_bind] + rfl + +theorem betaWhnfKey_fuel (source : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey source before).2.recFuel = before.recFuel := by + unfold betaWhnfKey + split + · rfl + · dsimp only + split <;> rfl + +theorem betaWhnfPrefix_fields (before : TcState .anon) : + (betaWhnfPrefix before).env = before.env ∧ + (betaWhnfPrefix before).lctx = before.lctx ∧ + (betaWhnfPrefix before).inNativeReduce = before.inNativeReduce ∧ + (betaWhnfPrefix before).recFuel = before.recFuel := by + unfold betaWhnfPrefix + split <;> exact ⟨rfl, rfl, rfl, rfl⟩ + +theorem betaWhnfCharge_fields (before : TcState .anon) : + (betaWhnfCharge before).env = before.env ∧ + (betaWhnfCharge before).lctx = before.lctx ∧ + (betaWhnfCharge before).inNativeReduce = before.inNativeReduce := by + unfold betaWhnfCharge + split <;> exact ⟨rfl, rfl, rfl⟩ + +namespace BetaPublicWhnf + +def outerKey (source : KExpr .anon) (before : TcState .anon) := + betaWhnfKey source (betaWhnfPrefix before) + +def noDeltaKey (source : KExpr .anon) (before : TcState .anon) := + betaWhnfKey source (betaWhnfCharge (outerKey source before).2) + +def coreKey (source : KExpr .anon) (before : TcState .anon) := + betaWhnfKey source (noDeltaKey source before).2 + +def coreAfter (source result : KExpr .anon) (before reduced : TcState .anon) : TcState .anon := + { reduced with env := { reduced.env with + whnfCoreCache := reduced.env.whnfCoreCache.insert (coreKey source before).1 result } } + +def noDeltaAfter (source result : KExpr .anon) (before reduced : TcState .anon) : TcState .anon := + let state := coreAfter source result before reduced + { state with env := { state.env with + whnfNoDeltaCache := state.env.whnfNoDeltaCache.insert (noDeltaKey source before).1 result } } + +def after (source result : KExpr .anon) (before reduced : TcState .anon) : TcState .anon := + let state := noDeltaAfter source result before reduced + { state with env := { state.env with + whnfCache := state.env.whnfCache.insert (outerKey source before).1 result } } + +theorem coreKey_fields (source : KExpr .anon) (before : TcState .anon) : + (coreKey source before).2.env = before.env ∧ + (coreKey source before).2.lctx = before.lctx ∧ + (coreKey source before).2.inNativeReduce = before.inNativeReduce := by + simp only [coreKey, noDeltaKey, outerKey, betaWhnfKey_environment, betaWhnfKey_context, + betaWhnfKey_native, betaWhnfCharge_fields, betaWhnfPrefix_fields, and_self] + +end BetaPublicWhnf + +/-- A public beta reduction through the three cache-miss layers. The key +states, instrumentation, fuel charge, and final cache insertions are computed; +the only reduction resource is the raw beta path. -/ +structure BetaPublicWhnfPlan {β : Type u} (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (fuel : Nat) (before : TcState .anon) + (source : KExpr .anon) (term : AExpr β) (result : KExpr .anon) (target : AExpr β) where + steps : Nat + reduced : TcState .anon + path : BetaWhnfTrace resolve locals fuel .FULL steps + (BetaPublicWhnf.coreKey source before).2 source term reduced result target + moving : 0 < steps + enough : steps < maxWhnfCoreFuel.toNat + fuelAvailable : (before.recFuel == 0) = false + native : before.inNativeReduce = false + outerMiss : (BetaPublicWhnf.outerKey source before).2.env.whnfCache[ + (BetaPublicWhnf.outerKey source before).1]? = none + noDeltaMiss : (BetaPublicWhnf.noDeltaKey source before).2.env.whnfNoDeltaCache[ + (BetaPublicWhnf.noDeltaKey source before).1]? = none + coreMiss : (BetaPublicWhnf.coreKey source before).2.env.whnfCoreCache[ + (BetaPublicWhnf.coreKey source before).1]? = none + terminal : BetaWhnfTerminal result + +namespace BetaPublicWhnfPlan + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {fuel : Nat} {before : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} + +def after (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : TcState .anon := + BetaPublicWhnf.after source result before plan.reduced + +theorem run (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : + (RecM.whnf source).run (methodsN (fuel + 1)) before = .ok result plan.after := by + let first := plan.path.first plan.moving + have coreEntry : RecM.whnfCoreWithFlags source .FULL = RecM.whnfCoreWithFlagsNonLeaf source .FULL := by + rw [first.sourceEq]; rfl + have coreRun : (RecM.whnfCoreWithFlags source .FULL).run (methodsN (fuel + 1)) + (BetaPublicWhnf.noDeltaKey source before).2 = + .ok result (BetaPublicWhnf.coreAfter source result before plan.reduced) := by + rw [coreEntry] + exact RecM.whnfCoreWithFlagsNonLeaf_fullMiss rfl + (betaWhnfKey_run source _) (first.not_transient _ _) plan.coreMiss (plan.path.run plan.enough) + have reducedNative : plan.reduced.inNativeReduce = false := by + obtain ⟨table, frame⟩ := plan.path.frame + exact (congrArg TcState.inNativeReduce frame).trans + ((BetaPublicWhnf.coreKey_fields source before).2.2.trans plan.native) + have noDeltaEntry : RecM.whnfNoDeltaImpl source .FULL .collapse = + RecM.whnfNoDeltaImplNonLeaf source .FULL .collapse := by + rw [first.sourceEq]; rfl + have noDeltaRun : (RecM.whnfNoDeltaImpl source .FULL .collapse).run (methodsN (fuel + 1)) + (betaWhnfCharge (BetaPublicWhnf.outerKey source before).2) = + .ok result (BetaPublicWhnf.noDeltaAfter source result before plan.reduced) := by + rw [noDeltaEntry] + exact RecM.whnfNoDeltaImplNonLeaf_fullMiss rfl (betaWhnfKey_run source _) + (first.not_transient _ _) plan.noDeltaMiss + (plan.terminal.noDelta_uncached .FULL .collapse coreRun) reducedNative + have fullEntry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := by + rw [first.sourceEq]; rfl + rw [fullEntry] + exact RecM.whnfWithNatSuccModeNonLeaf_miss (betaWhnfPrefix_run source before _) + (betaWhnfKey_run source _) (first.not_transient _ _) plan.outerMiss + (betaWhnfCharge_run _ _ (by + simpa only [BetaPublicWhnf.outerKey, betaWhnfKey_fuel, (betaWhnfPrefix_fields before).2.2.2] using plan.fuelAvailable)) + (plan.terminal.full_uncached .collapse noDeltaRun) reducedNative + +theorem reading (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) + (sourceReading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) : + readScopedExpr? resolve locals result = some target.erase ∧ plan.after.env.intern.WF := by + exact plan.path.reading sourceReading ((BetaPublicWhnf.coreKey_fields source before).1.symm ▸ coherent) + +theorem context (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : + plan.after.lctx = before.lctx := by + obtain ⟨table, frame⟩ := plan.path.frame + exact (congrArg TcState.lctx frame).trans (BetaPublicWhnf.coreKey_fields source before).2.1 + +theorem cache_hit (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) + (methods : Methods .anon) (current : TcState .anon) + (hit : (BetaPublicWhnf.outerKey source current).2.env.whnfCache[ + (BetaPublicWhnf.outerKey source current).1]? = some result) : + (RecM.whnf source).run methods current = .ok result (BetaPublicWhnf.outerKey source current).2 := by + let first := plan.path.first plan.moving + have entry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := by + rw [first.sourceEq]; rfl + rw [entry] + exact RecM.whnfWithNatSuccModeNonLeaf_hit (betaWhnfPrefix_run source current _) + (betaWhnfKey_run source _) (first.not_transient _ _) hit + +end BetaPublicWhnfPlan + + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean b/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean index b01d4d3f9..bcef88b6f 100644 --- a/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean +++ b/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean @@ -62,6 +62,45 @@ def BetaPublicWhnfPlan.betaTrace {β : Type u} {resolve : Address → Option (Co SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term target type := (plan.path.annotate typing).1.toBetaTrace typing.origin +/-- The cached result retains the executed beta origin; the source's current +checking derivation supplies its meaning in the current model context. -/ +def BetaCoreExecution.betaTrace {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target type : AExpr β} + (execution : BetaCoreExecution resolve locals fuel before source term result target) + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term target type := + match execution with + | .reduce path .. => (path.annotate typing).1.toBetaTrace typing.origin + | .cached origin .. => origin.betaTrace typing + +/-- The cached result retains the executed beta origin; the source's current +checking derivation supplies its meaning in the current model context. -/ +def BetaNoDeltaExecution.betaTrace {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target type : AExpr β} + (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term target type := + match execution with + | .reduce core _ => core.betaTrace typing + | .cached origin .. => origin.betaTrace typing + +/-- The cached result retains the executed beta origin; the source's current +checking derivation supplies its meaning in the current model context. -/ +def BetaPublicExecution.betaTrace {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target type : AExpr β} + (execution : BetaPublicExecution resolve locals fuel before source term result target) + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term target type := + match execution with + | .reduce inner .. => inner.betaTrace typing + | .cached origin .. => origin.betaTrace typing + /-- Cache reuse retains the originating path's checked meaning. The current cache lookup establishes which exact raw Pi result is returned. -/ def BetaPiExposure.betaTrace {β : Type u} {resolve : Address → Option (ConstRef β)} @@ -75,6 +114,7 @@ def BetaPiExposure.betaTrace {β : Type u} {resolve : Address → Option (ConstR match exposure with | .reduce plan => plan.betaTrace typing | .cached origin _ _ => origin.betaTrace typing + | .execute execution => execution.betaTrace typing /-- Sort exposure obtains its meaning from the same complete source derivation as public beta WHNF; a direct sort uses reflexivity. -/ @@ -90,6 +130,7 @@ def BetaSortExposure.betaTrace {β : Type u} {resolve : Address → Option (Cons | .direct .. => .refl typing.origin | .reduce plan => plan.betaTrace typing | .cached origin _ => origin.betaTrace typing + | .execute execution => execution.betaTrace typing /-- Earlier type checks retain every beta origin, including after interface growth or insertion beneath binders. No check of the exposed sort is required. -/ @@ -184,4 +225,73 @@ theorem SynthesisInference.beta_public_whnf_sound {β : Type u} {resolve : Addre (plan.betaTrace (support.betaTyping .current agreement reading accepted)).sound formed exact ⟨plan.run, resultReading, converted, typed, preserved⟩ +/-- Executed cached reduction preserves the type proved by the original +source check, including when a lower layer supplied the result. -/ +theorem SynthesisInference.beta_core_execution_sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} {locals : List FVarId} + {fuel : Nat} {inferenceBefore inferenceAfter : TcState .anon} {source inferred : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel inferenceBefore source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals inferenceBefore.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) + {reductionFuel : Nat} {before : TcState .anon} {result : KExpr .anon} {target : AExpr β} + (plan : BetaCoreExecution resolve locals reductionFuel before source term result target) + (coherent : before.env.intern.WF) : + (RecM.whnfCore source).run (methodsN (reductionFuel + 1)) before = .ok result plan.after ∧ + readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ TypingClaim.{u,v} entries context target type ∧ + plan.after.env.intern.WF := by + obtain ⟨resultReading, preserved⟩ := plan.reading reading coherent + obtain ⟨converted, typed⟩ := + (plan.betaTrace (support.betaTyping .current agreement reading accepted)).sound formed + exact ⟨plan.run, resultReading, converted, typed, preserved⟩ + +/-- Executed cached reduction preserves the type proved by the original +source check, including when a lower layer supplied the result. -/ +theorem SynthesisInference.beta_noDelta_execution_sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} {locals : List FVarId} + {fuel : Nat} {inferenceBefore inferenceAfter : TcState .anon} {source inferred : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel inferenceBefore source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals inferenceBefore.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) + {reductionFuel : Nat} {before : TcState .anon} {result : KExpr .anon} {target : AExpr β} + (plan : BetaNoDeltaExecution resolve locals reductionFuel before source term result target) + (coherent : before.env.intern.WF) : + (RecM.whnfNoDelta source).run (methodsN (reductionFuel + 1)) before = .ok result plan.after ∧ + readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ TypingClaim.{u,v} entries context target type ∧ + plan.after.env.intern.WF := by + obtain ⟨resultReading, preserved⟩ := plan.reading reading coherent + obtain ⟨converted, typed⟩ := + (plan.betaTrace (support.betaTyping .current agreement reading accepted)).sound formed + exact ⟨plan.run, resultReading, converted, typed, preserved⟩ + +/-- Executed cached reduction preserves the type proved by the original +source check, including when a lower layer supplied the result. -/ +theorem SynthesisInference.beta_public_execution_sound {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} {locals : List FVarId} + {fuel : Nat} {inferenceBefore inferenceAfter : TcState .anon} {source inferred : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel inferenceBefore source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals inferenceBefore.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) + {reductionFuel : Nat} {before : TcState .anon} {result : KExpr .anon} {target : AExpr β} + (plan : BetaPublicExecution resolve locals reductionFuel before source term result target) + (coherent : before.env.intern.WF) : + (RecM.whnf source).run (methodsN (reductionFuel + 1)) before = .ok result plan.after ∧ + readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ TypingClaim.{u,v} entries context target type ∧ + plan.after.env.intern.WF := by + obtain ⟨resultReading, preserved⟩ := plan.reading reading coherent + obtain ⟨converted, typed⟩ := + (plan.betaTrace (support.betaTyping .current agreement reading accepted)).sound formed + exact ⟨plan.run, resultReading, converted, typed, preserved⟩ + end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean b/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean index 3853838c0..aef96e7dc 100644 --- a/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean +++ b/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean @@ -60,6 +60,9 @@ theorem BetaPiExposure.inference_frame {β : Type u} {resolve : Address → Opti (key : Address × Address) : InferenceCacheFrame key before exposure.after := by cases exposure with | reduce plan => exact plan.inference_frame key + | execute execution => + exact .of_eq (congrArg (·[key]?) execution.frame.full) + (congrArg (·[key]?) execution.frame.only) execution.frame.constants | cached origin coherent hit => apply InferenceCacheFrame.of_eq <;> simp only [BetaPiExposure.after, BetaPublicWhnf.outerKey, betaWhnfKey_environment, @@ -72,6 +75,7 @@ theorem BetaPiExposure.policy {β : Type u} {resolve : Address → Option (Const exposure.after.inferOnly = before.inferOnly := by cases exposure with | reduce plan => exact plan.policy + | execute execution => exact execution.frame.policy | cached origin coherent hit => simp only [BetaPiExposure.after, BetaPublicWhnf.outerKey, betaWhnfKey_policy, betaWhnfPrefix_policy] @@ -96,6 +100,7 @@ theorem BetaPiExposure.inference_maps {β : Type u} {resolve : Address → Optio exposure.after.env.inferOnlyCache = before.env.inferOnlyCache := by cases exposure with | reduce plan => exact plan.inference_maps + | execute execution => exact ⟨execution.frame.full, execution.frame.only⟩ | cached origin coherent hit => constructor <;> simp only [BetaPiExposure.after, BetaPublicWhnf.outerKey, betaWhnfKey_environment, @@ -111,6 +116,9 @@ theorem inference_frame (exposure : BetaSortExposure resolve locals fuel before cases exposure with | direct => exact .refl key _ | reduce plan => exact plan.inference_frame key + | execute execution => + exact .of_eq (congrArg (·[key]?) execution.frame.full) + (congrArg (·[key]?) execution.frame.only) execution.frame.constants | cached origin hit => apply InferenceCacheFrame.of_eq <;> simp only [after, BetaPublicWhnf.outerKey, betaWhnfKey_environment, @@ -121,6 +129,7 @@ theorem policy (exposure : BetaSortExposure resolve locals fuel before source te cases exposure with | direct => rfl | reduce plan => exact plan.policy + | execute execution => exact execution.frame.policy | cached origin hit => simp only [after, BetaPublicWhnf.outerKey, betaWhnfKey_policy, betaWhnfPrefix_policy] @@ -130,6 +139,7 @@ theorem inference_maps (exposure : BetaSortExposure resolve locals fuel before s cases exposure with | direct => exact ⟨rfl, rfl⟩ | reduce plan => exact plan.inference_maps + | execute execution => exact ⟨execution.frame.full, execution.frame.only⟩ | cached origin hit => constructor <;> simp only [after, BetaPublicWhnf.outerKey, betaWhnfKey_environment, diff --git a/Ix/Kernel/Verify/Whnf/Driver/CacheExecution.lean b/Ix/Kernel/Verify/Whnf/Driver/CacheExecution.lean index 6afa06f8f..33e09fa6f 100644 --- a/Ix/Kernel/Verify/Whnf/Driver/CacheExecution.lean +++ b/Ix/Kernel/Verify/Whnf/Driver/CacheExecution.lean @@ -244,4 +244,118 @@ theorem whnfWithNatSuccModeNonLeaf_miss rfl +/-- The native-reduction guard controls publication, including when the +underlying result came from a lower cache. -/ +theorem whnfNoDeltaImplNonLeaf_fullMiss_conditional + {methods : Methods .anon} {s s₁ s₂ s₃ : TcState .anon} + {source result : KExpr .anon} {flags : WhnfFlags} + {key : Address × Address} + (hfull : flags.isFull = true) + (hkey : TcM.whnfKey source s = .ok key s₁) + (htransient : (isTransientNatLiteralWork source).run methods s₁ = + .ok false s₂) + (hmiss : s₂.env.whnfNoDeltaCache[key]? = none) + (hrun : (whnfNoDeltaImplUncached source flags .collapse).run methods s₂ = + .ok result s₃) : + (whnfNoDeltaImplNonLeaf source flags .collapse).run methods s = + .ok result (if s₃.inNativeReduce then s₃ else + {s₃ with env := {s₃.env with + whnfNoDeltaCache := s₃.env.whnfNoDeltaCache.insert key result}}) := by + unfold whnfNoDeltaImplNonLeaf + rw [ReaderT.run_bind] + change EStateM.bind (TcM.whnfKey source) _ s = _ + unfold EStateM.bind + rw [hkey] + simp only + rw [ReaderT.run_bind] + change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₁ = _ + unfold EStateM.bind + rw [htransient] + simp [natSuccMode_collapse_beq, hfull] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₂ = _ + unfold EStateM.bind + rw [show (get : TcM .anon (TcState .anon)) s₂ = .ok s₂ s₂ from rfl] + simp only [hmiss] + rw [ReaderT.run_bind] + change EStateM.bind + ((whnfNoDeltaImplUncached source flags .collapse).run methods) _ s₂ = _ + unfold EStateM.bind + rw [hrun] + simp only + rw [ReaderT.run_bind] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₃ = _ + unfold EStateM.bind + rw [show (get : TcM .anon (TcState .anon)) s₃ = .ok s₃ s₃ from rfl] + simp only + by_cases native : s₃.inNativeReduce = false + · rw [if_pos native, if_neg (by simp only [native, Bool.false_eq_true, not_false_eq_true])] + rfl + · have active : s₃.inNativeReduce = true := by cases value : s₃.inNativeReduce <;> simp_all + rw [if_neg native, if_pos active] + rfl + +/-- Full WHNF charges a miss even when a lower cache supplies the result; +its final native guard controls only publication. -/ +theorem whnfWithNatSuccModeNonLeaf_miss_conditional + {methods : Methods .anon} {s s₁ s₂ s₃ s₄ s₅ : TcState .anon} + {source result : KExpr .anon} {key : Address × Address} + (hprefix : (whnfWithNatSuccModePrefix source).run methods s = + .ok () s₁) + (hkey : TcM.whnfKey source s₁ = .ok key s₂) + (htransient : (isTransientNatLiteralWork source).run methods s₂ = + .ok false s₃) + (hmiss : s₃.env.whnfCache[key]? = none) + (hcharge : (whnfWithNatSuccModeMissCharge : RecM .anon Unit).run + methods s₃ = .ok () s₄) + (hrun : (whnfWithNatSuccModeUncached source .collapse).run methods s₄ = + .ok result s₅) : + (whnfWithNatSuccModeNonLeaf source .collapse).run methods s = + .ok result (if s₅.inNativeReduce then s₅ else + {s₅ with env := {s₅.env with + whnfCache := s₅.env.whnfCache.insert key result}}) := by + unfold whnfWithNatSuccModeNonLeaf + rw [ReaderT.run_bind] + change EStateM.bind + ((whnfWithNatSuccModePrefix source).run methods) _ s = _ + unfold EStateM.bind + rw [hprefix] + simp only + rw [ReaderT.run_bind] + change EStateM.bind (TcM.whnfKey source) _ s₁ = _ + unfold EStateM.bind + rw [hkey] + simp only + rw [ReaderT.run_bind] + change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₂ = _ + unfold EStateM.bind + rw [htransient] + simp [natSuccMode_collapse_beq] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₃ = _ + unfold EStateM.bind + rw [show (get : TcM .anon (TcState .anon)) s₃ = .ok s₃ s₃ from rfl] + simp only [hmiss] + rw [ReaderT.run_bind] + change EStateM.bind + ((whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods) _ s₃ = _ + unfold EStateM.bind + rw [hcharge] + simp only + rw [ReaderT.run_bind] + change EStateM.bind + ((whnfWithNatSuccModeUncached source .collapse).run methods) _ s₄ = _ + unfold EStateM.bind + rw [hrun] + simp only + rw [ReaderT.run_bind] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₅ = _ + unfold EStateM.bind + rw [show (get : TcM .anon (TcState .anon)) s₅ = .ok s₅ s₅ from rfl] + simp only + by_cases native : s₅.inNativeReduce = false + · rw [if_pos native, if_neg (by simp only [native, Bool.false_eq_true, not_false_eq_true])] + rfl + · have active : s₅.inNativeReduce = true := by cases value : s₅.inNativeReduce <;> simp_all + rw [if_neg native, if_pos active] + rfl + end Ix.Kernel.RecM diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index c94425e91..c9ea11f19 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -3637,7 +3637,8 @@ private def sortExposureEnvironment (level : Ixon.Univ) (universes : UInt64 := 0 /-- The domain's full inference cache keeps its original beta type while sort exposure writes the normalized sort only to the three WHNF caches. -/ -private def sortExposureInferencePaths (typeLevel warm instrumented noAccel : Bool) : Bool := +private def sortExposureInferencePaths (typeLevel warm instrumented noAccel : Bool) + (lowerWarm : Nat := 0) : Bool := let level := if typeLevel then levelOne else KUniv.mkZero let (env, targets) := sortExposureEnvironment (if typeLevel then .succ .zero else .zero) (List.range 5).all fun shape => @@ -3652,6 +3653,11 @@ private def sortExposureInferencePaths (typeLevel warm instrumented noAccel : Bo | 4, .lam _ _ _ (.const id _ _) _ => pure (← TcM.getConst id).ty | _, _ => pure original modify fun state => { state with recFuel := 1, inNativeReduce := false, stats := instrumented, noAccel } + if lowerWarm != 0 then + let normalized ← if lowerWarm == 1 then RecM.whnfCore original else RecM.whnfNoDelta original + if normalized != KExpr.mkSort level then return false + if lowerWarm == 2 then + modify fun state => { state with env := { state.env with whnfCoreCache := {} } } if warm then let exposed ← RecM.ensureSortDirect original if exposed != level then return false @@ -3679,13 +3685,120 @@ private def sortExposureInferencePaths (typeLevel warm instrumented noAccel : Bo return result == expected && after.recFuel == 0 && after.lctx.size == 0 && after.env.nextFVarId == (if shape == 3 then 2 else 1) && after.inNativeReduce == warm && after.env.whnfCache[key]? == some sort && after.env.whnfNoDeltaCache[key]? == some sort && - after.env.whnfCoreCache[key]? == some sort && + after.env.whnfCoreCache[key]? == (if lowerWarm == 2 then none else some sort) && exactInferenceCaches ((domain, original) :: (children ++ [(value, expected)])) [] after && compositeReplayAt value expected after match TcM.runRec action (TcState.newLazyAnon env) with | .ok passed _ => passed | .error _ _ => false +private inductive WhnfWarmLayer + | cold | core | noDelta | full + deriving BEq + +/-- Warm entries come from the actual layer that produces them. Keep +unrelated entries in every map and discard lower entries when isolating an +upper hit. The trial starts with fresh key memoization. -/ +private def mixedWhnfCaches (shape : Nat) (layer : WhnfWarmLayer) + (legacy native instrumented noAccel inferOnly : Bool) : Bool := + let prop := KExpr.mkSort (m := .anon) .mkZero + let sort := KExpr.mkSort (m := .anon) levelOne + let argument := if legacy then KExpr.mkVar 0 () else prop + let body := if shape == 0 then prop else if shape == 1 then + KExpr.mkAll () () (.mkVar 0 ()) (.mkVar 1 ()) + else KExpr.mkLam () () (.mkVar 0 ()) (.mkVar 1 ()) + let expected := if shape == 0 then prop else if shape == 1 then + KExpr.mkAll () () argument (if legacy then .mkVar 1 () else prop) + else KExpr.mkLam () () argument (if legacy then .mkVar 1 () else prop) + let source := KExpr.mkApp (.mkLam () () sort body) argument + let sameMap (actual predicted : Std.HashMap (Address × Address) (KExpr .anon)) := + actual.size == predicted.size && predicted.toList.all fun (key, result) => actual[key]? == some result + let action : RecM .anon Bool := do + if legacy then + TcM.pushLocal sort + TcM.pushLocal sort + let seed : Std.HashMap (Address × Address) (KExpr .anon) := + (∅ : Std.HashMap (Address × Address) (KExpr .anon)).insert (prop.addr, emptyCtxAddr) prop + modify fun state => { state with env := { state.env with + whnfCache := seed, whnfNoDeltaCache := seed, whnfCoreCache := seed, + whnfNoDeltaCheapCache := seed, whnfCoreCheapCache := seed, + inferCache := state.env.inferCache.insert (prop.addr, emptyCtxAddr) sort, + inferOnlyCache := state.env.inferOnlyCache.insert (sort.addr, emptyCtxAddr) (.mkSort levelTwo) } } + match layer with + | .cold => pure () + | .core => if (← RecM.whnfCore source) != expected then return false + | .noDelta => + if (← RecM.whnfNoDelta source) != expected then return false + modify fun state => { state with env := { state.env with whnfCoreCache := seed } } + | .full => + if (← RecM.whnf source) != expected then return false + modify fun state => { state with env := { state.env with whnfCoreCache := seed, whnfNoDeltaCache := seed } } + modify fun state => { state with + ctxAddrCache := {}, + recFuel := if layer == .full then 0 else 1, inNativeReduce := native, + stats := instrumented, stepTrace := instrumented, whnfCalls := 17, whnfMisses := 11, noAccel, inferOnly } + let before ← get + let methods := if layer == .full then methodsN 0 else methodsN 2 + match (RecM.whnf source).run methods before with + | .error _ _ => return false + | .ok result after => + let .ok key keyed := TcM.whnfKey source after | return false + let core := if layer == .cold || layer == .core then before.env.whnfCoreCache.insert key expected + else before.env.whnfCoreCache + let noDelta := if native || layer == .full then before.env.whnfNoDeltaCache + else before.env.whnfNoDeltaCache.insert key expected + let full := if native || layer == .full then before.env.whnfCache + else before.env.whnfCache.insert key expected + let replayRun := match layer with + | .cold | .core => (RecM.whnfCore source).run (methodsN 0) after + | .noDelta => (RecM.whnfNoDelta source).run (methodsN 0) after + | .full => (RecM.whnf source).run (methodsN 0) after + let .ok replay replayed := replayRun | return false + return result == expected && replay == expected && after.recFuel == 0 && replayed.recFuel == 0 && + after.whnfCalls == (if instrumented then 18 else 17) && + after.whnfMisses == (if instrumented && layer != .full then 12 else 11) && + sameMap after.env.whnfCoreCache core && sameMap after.env.whnfNoDeltaCache noDelta && + sameMap after.env.whnfCache full && sameMap after.env.whnfCoreCheapCache before.env.whnfCoreCheapCache && + sameMap after.env.whnfNoDeltaCheapCache before.env.whnfNoDeltaCheapCache && + sameMap after.env.inferCache before.env.inferCache && sameMap after.env.inferOnlyCache before.env.inferOnlyCache && + after.lctx.size == before.lctx.size && after.ctx.size == before.ctx.size && after.ctxId == before.ctxId && + after.env.nextFVarId == before.env.nextFVarId && after.inNativeReduce == native && after.inferOnly == inferOnly && + after.ctxAddrCache.size == (if legacy then 1 else 0) && keyed.ctxAddrCache.size == after.ctxAddrCache.size && + sameMap replayed.env.whnfCoreCache core && sameMap replayed.env.whnfNoDeltaCache noDelta && + sameMap replayed.env.whnfCache full && replayed.env.intern.exprs.size == after.env.intern.exprs.size + match TcM.runRec action (TcState.newLazyAnon {}) with + | .ok passed _ => passed + | .error _ _ => false + +private def mixedWhnfFuelFailure (layer : WhnfWarmLayer) : Bool := + let prop := KExpr.mkSort (m := .anon) .mkZero + let source := KExpr.mkApp (.mkLam () () (.mkSort levelOne) (.mkVar 0 ())) prop + let action : RecM .anon Bool := do + match layer with + | .core => discard <| RecM.whnfCore source + | .noDelta => discard <| RecM.whnfNoDelta source + | _ => pure () + modify fun state => { state with recFuel := 0, stats := true, whnfCalls := 17, whnfMisses := 11 } + let before ← get + match (RecM.whnf source).run (methodsN 2) before with + | .error .maxRecFuel after => + return after.recFuel == 0 && after.whnfCalls == 18 && after.whnfMisses == 12 && + after.env.whnfCache.size == 0 && after.env.whnfCoreCache.size == before.env.whnfCoreCache.size && + after.env.whnfNoDeltaCache.size == before.env.whnfNoDeltaCache.size + | _ => return false + match TcM.runRec action (TcState.newLazyAnon {}) with + | .ok passed _ => passed + | .error _ _ => false + +private def mixedWhnfCases : TestSeq := + [WhnfWarmLayer.cold, .core, .noDelta, .full].foldl (fun suite layer => suite ++ + test s!"mixed WHNF: layer {reprStr (match layer with | .cold => 0 | .core => 1 | .noDelta => 2 | .full => 3)} preserves exact maps, fuel, and keys" + ((List.range 3).all fun shape => [false, true].all fun legacy => [false, true].all fun native => + [false, true].all fun instrumented => [false, true].all fun noAccel => [false, true].all fun inferOnly => + mixedWhnfCaches shape layer legacy native instrumented noAccel inferOnly)) + (test "mixed WHNF: lower cache hits do not bypass the public miss fuel check" + ([WhnfWarmLayer.cold, .core, .noDelta].all mixedWhnfFuelFailure)) + private def sortExposureFailures : Bool := let betaPi := Ixon.Expr.app (.leanLam (.sort 1) (.leanAll (.var 0) (.var 1))) (.sort 0) let (env, carrierAddr) := storeConst {} @@ -3720,6 +3833,12 @@ private def sortExposureCases : TestSeq := ++ test "sort exposure: warm binder checks and zero-method exposure need no remaining reduction fuel" ([false, true].all fun typeLevel => [false, true].all fun instrumented => [false, true].all fun noAccel => sortExposureInferencePaths typeLevel true instrumented noAccel) + ++ test "sort exposure: a core hit retains original child types through binder, lambda, and let inference" + ([false, true].all fun typeLevel => [false, true].all fun instrumented => + [false, true].all fun noAccel => sortExposureInferencePaths typeLevel false instrumented noAccel 1) + ++ test "sort exposure: an isolated no-delta hit publishes the outer result and preserves exited-scope histories" + ([false, true].all fun typeLevel => [false, true].all fun instrumented => + [false, true].all fun noAccel => sortExposureInferencePaths typeLevel false instrumented noAccel 2) ++ test "sort exposure: a reduced Pi is rejected as a sort and failed binder scopes restore locals" sortExposureFailures @@ -3868,6 +3987,7 @@ public def suite : List TestSeq := cheapApplicationCases, exposedLambdaCases, repeatedBetaCases, betaTraceCases, hereditaryBetaCases, piExposureCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, - sourceAgreementCases, sourceCacheCases, compositeCacheCases, letCases, sortExposureCases, polymorphicDefinitionCases] + sourceAgreementCases, sourceCacheCases, compositeCacheCases, letCases, sortExposureCases, mixedWhnfCases, + polymorphicDefinitionCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index feb27ddc9..d91144176 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -119,7 +119,7 @@ increasing sequence of strongly inaccessible cardinals. and converts its returned type using checked beta origins. The recursive synthesis rules form binder contexts from the exposed level and preserve later beta derivations and typed cache histories. Exposure supports direct - sorts, the existing public beta WHNF path, and its outer-cache hit. It + sorts and the public beta WHNF path with hits at any of its three cache layers. It preserves both inference-cache maps, so a child publication retains its original, possibly unreduced, type. Earlier retained type checks construct the conversion resource without a semantic typing premise. Finite reader, @@ -601,7 +601,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 1,336 exact theorem boundaries. The production +The consistency target checks 1,391 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -610,8 +610,9 @@ extension lemma and `ModelTyping.no_false` use only `propext`, The production roots additionally forbid the abstract `CheckSuccessSound`/`SupportedCheckFragment` interfaces and the independent certificate validator in their dependency closures. -Sort-exposure traces, source resources, beta derivation producers, and cache -history constructors additionally forbid the semantic hereditary invariant; +Sort-exposure traces, source resources, beta derivation producers, cache-layer +executions, replay, and inference-cache history constructors additionally forbid +the semantic hereditary invariant; the soundness induction derives that invariant after source reconstruction. The polymorphic inference, substitution, and binder inference roots retain only the two existing @@ -720,22 +721,30 @@ no field for this invariant. The audit explicitly forbids it in the recursive let constructor, returned-reading proof, function-type derivation constructor, and source derivation producer. The 31 additional boundaries preserve every previous exact axiom profile and introduce no axioms or native proofs. -`BetaPublicWhnfPlan` connects a raw beta path to public WHNF when its result -is a sort, Pi, or lambda. It computes the context-key states, optional counters, -shared-fuel charge, and insertions into the structural, no-delta, and outer -caches. The reducer tails are proved to stop on these constructors without -additional callback or lookup premises. `BetaPiExposure` derives the actual -`ensureForallDirect` call and the returned domain/codomain readings. It either -executes a cold public path or reuses that path's exact result from the outer -cache. Cold paths require three cache misses and an inactive native-reduction -guard; warm outer-cache hits need no fuel. `checkedTrace` constructs the +`BetaCoreExecution`, `BetaNoDeltaExecution`, and `BetaPublicExecution` connect +raw beta paths ending at a sort, Pi, or lambda to the three actual WHNF layers. +Each layer either executes its reducer or retains the execution that produced +its exact cached result. This covers partially populated caches and either +native-reduction guard value. A core miss always publishes; the two upper +layers suppress new writes during native reduction. Only a public outer miss +charges shared fuel, including when a lower cache supplies its result. The +computed state retains key memoization, counters, and every cache update while +preserving both inference maps, constants, local context, and mode flags. +Publication and key-stability proofs derive later hits directly from earlier +executions, including loose-variable context digests. Those hits run without +recursive methods or fuel. The scoped semantic reader still excludes loose +variables. The earlier three-miss `BetaPublicWhnfPlan` embeds into this execution. +The reducer tails stop on the supported terminal constructors without extra +callback or lookup premises. Pi and sort exposure use these executions in the +original inference recursion. `checkedTrace` constructs the conversion from a retained actual type check. `SynthesisInference.appBeta` uses that conversion in the original inference recursion, preserving dependent codomain substitution, checked lambda domains, and all later beta origins. The existing synthesis admission and environment roots include this case. -These 38 additional audited boundaries introduce no axioms or native proofs. -Mixed cache states, general WHNF cache agreement, other reducers, and general -inference-resource construction remain open. +These boundaries introduce no axioms or native proofs. Cached beta traces +derive their meaning from the original source check, with no semantic typing +field in the cache execution. General WHNF cache agreement, other reducers, +and automatic inference-resource construction remain open. `SynthesisInference.cached` retains the original tree behind an inference cache hit. Its soundness and beta derivations reuse the actual lambda-body, dependent codomain, and argument checks. `reuseFull` derives the cached result @@ -876,6 +885,13 @@ results. Additional operational coverage checks context-key memoization for legacy loose variables. Negative cases reject mismatched carriers and invalid dependent witnesses. The scoped semantic reader still excludes loose variables. These execution tests do not construct the general finite inference resources. +Mixed-cache regressions warm each layer through its actual producer and check +sort, Pi, and lambda results across native guards, instrumentation, acceleration +policies, inference modes, and loose-variable contexts. Whole-map comparisons +check preserved unrelated entries, exact writes, fuel charges, and zero-method +replay. Lower hits cannot bypass an exhausted public miss budget. Binder, lambda, +and let inference also run with isolated core or no-delta hits while retaining +original child types and cache histories from exited scopes. Polymorphic-call regressions include Prop/Type instances in real function bodies, `max`/`imax` simplification inside Pi domains, closed nested references under active locals, separate cache keys for different universe instances, @@ -977,7 +993,7 @@ Type, and parameterized universes; persistent and cleared caches; exact child publications; warm exposure at zero method fuel; changed lambda-body beta; and rejection of a reduced function type with local-scope cleanup. They also exercise instrumentation and acceleration flags. -The unit suite contains 731 checks. The anonymous differential additionally +The unit suite contains 738 checks. The anonymous differential additionally serializes eleven cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. @@ -1026,7 +1042,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Executed sort exposure and recursive binder checks | [`Consistency/SortInference.lean`](../Ix/Kernel/Verify/Consistency/SortInference.lean), [`BetaPublicWhnf.lean`](../Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean), [`BetaWhnfInference.lean`](../Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean) | | Retained derivations, dependent substitution, and semantic induction | [`Consistency/SynthesisDerivation.lean`](../Ix/Kernel/Verify/Consistency/SynthesisDerivation.lean), [`SynthesisShapes.lean`](../Ix/Kernel/Verify/Consistency/SynthesisShapes.lean), [`SynthesisReduction.lean`](../Ix/Kernel/Verify/Consistency/SynthesisReduction.lean), [`SynthesisMeaning.lean`](../Ix/Kernel/Verify/Consistency/SynthesisMeaning.lean), [`BinderMeaning.lean`](../Ix/Kernel/Verify/Consistency/BinderMeaning.lean), [`BetaChecking.lean`](../Ix/Kernel/Verify/Consistency/BetaChecking.lean) | | Let inference and typed cache history | [`Consistency/LetInference.lean`](../Ix/Kernel/Verify/Consistency/LetInference.lean), [`LetSynthesis.lean`](../Ix/Kernel/Verify/Consistency/LetSynthesis.lean), [`LetCache.lean`](../Ix/Kernel/Verify/Consistency/LetCache.lean), [`SynthesisCacheExecution.lean`](../Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean) | -| Public beta WHNF, cache writes, and application Pi exposure | [`Consistency/BetaPublicWhnf.lean`](../Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean), [`ApplicationWhnf.lean`](../Ix/Kernel/Verify/Consistency/ApplicationWhnf.lean), [`BetaWhnfInference.lean`](../Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean) | +| Public beta WHNF, cache writes and replay, and Pi/sort exposure | [`BetaCacheExecution.lean`](../Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean), [`BetaCacheKeys.lean`](../Ix/Kernel/Verify/Consistency/BetaCacheKeys.lean), [`BetaPublicWhnf.lean`](../Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean), [`BetaWhnfInference.lean`](../Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean) | | Retained type checks and changed cheap beta in lambda inference | [`Consistency/SynthesisInference.lean`](../Ix/Kernel/Verify/Consistency/SynthesisInference.lean), [`CheapBetaReading.lean`](../Ix/Kernel/Verify/Consistency/CheapBetaReading.lean), [`Formation.lean`](../Ix/Kernel/Verify/Consistency/Formation.lean) | | Source beta reduction and declaration conversion | [`Consistency/BetaSpine.lean`](../Ix/Kernel/Verify/Consistency/BetaSpine.lean), [`Simultaneous.lean`](../Ix/Kernel/Verify/Consistency/Simultaneous.lean), [`SpineReading.lean`](../Ix/Kernel/Verify/Consistency/SpineReading.lean), [`CheapBeta.lean`](../Ix/Kernel/Verify/Consistency/CheapBeta.lean), [`Model/BetaSpine.lean`](../Ix/Theory/Model/BetaSpine.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | diff --git a/docs/theory.md b/docs/theory.md index 2c3414e45..9f6c3793f 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -128,8 +128,12 @@ opening, comparison, and body observations. The original child check and a retained check of its returned type justify conversion to the exposed sort, which supplies the binder context's universe level. The source derivation and typed cache history retain the original inference at its unreduced type. -Exposure supports syntactic sorts, the existing public beta WHNF path, and -that path's outer-cache hit. It changes neither inference-cache map. Finite +Exposure supports syntactic sorts and public beta WHNF with hits at any of +its three cache layers. Each cached result retains its actual producing +execution, whose meaning follows from the original source check. Native +reduction suppresses upper-layer writes; only an outer miss charges fuel. +Publication and key stability derive later replay without recursive methods. +These paths change neither inference-cache map. Finite reading, representation, and reduction resources still need general construction; other reduction and cache paths remain open. Structural local-state preservation now covers the complete recursive checker From dd43935d59c1e332e05bca449f553ed603b1742b Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 15:49:37 -0400 Subject: [PATCH 44/63] Construct beta WHNF traces from successful source runs --- Ix/Kernel/Verify/Consistency.lean | 8 +- Ix/Kernel/Verify/Consistency/Audit.lean | 52 ++++- .../Consistency/BetaCacheConstruction.lean | 184 +++++++++++++++ .../Consistency/BetaExposureConstruction.lean | 77 ++++++ .../Consistency/BetaSourceInference.lean | 106 +++++++++ .../Consistency/BetaStepConstruction.lean | 220 ++++++++++++++++++ .../Consistency/BetaTraceConstruction.lean | 127 ++++++++++ Ix/Kernel/Verify/Infer/ExposureSuccess.lean | 47 ++++ Ix/Kernel/Verify/Whnf/Driver/Success.lean | 146 ++++++++++++ docs/kernel-verification.md | 28 ++- docs/theory.md | 16 +- 11 files changed, 997 insertions(+), 14 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/BetaCacheConstruction.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaExposureConstruction.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaSourceInference.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaStepConstruction.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean create mode 100644 Ix/Kernel/Verify/Infer/ExposureSuccess.lean create mode 100644 Ix/Kernel/Verify/Whnf/Driver/Success.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index c56cb446c..3df68cae4 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -51,6 +51,8 @@ import Ix.Kernel.Verify.Consistency.WhnfCacheFrame import Ix.Kernel.Verify.Consistency.BetaTyping import Ix.Kernel.Verify.Consistency.BetaInference import Ix.Kernel.Verify.Consistency.BetaWhnfInference +import Ix.Kernel.Verify.Consistency.BetaSourceInference +import Ix.Kernel.Verify.Consistency.BetaExposureConstruction import Ix.Kernel.Verify.Consistency.SortInference import Ix.Kernel.Verify.Consistency.CheapBeta import Ix.Kernel.Verify.Consistency.Validation @@ -162,7 +164,11 @@ beta paths ending at a sort, Pi, or lambda. Each of the three cache layers may retain a prior executed result. Native reduction suppresses new writes at the no-delta and outer layers; the structural layer still publishes. Replay derives the next hit from that publication and preserves memoized -context keys. Only an outer miss charges shared fuel. Pi exposure uses the +context keys. Successful beta calls now reconstruct their raw paths from +the original source reading and finite arithmetic and hash resources. The +actual run supplies the intermediate steps, iteration bound, cache choices, +fuel charge, and final state. Cached entries retain their producing executions. +Only an outer miss charges shared fuel. Pi exposure uses the same complete cache-layer execution. Application inference uses that exposure between argument checks and derives the type conversion from a retained actual check of the function type. Dependent codomain diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index a84f8f554..485fd9831 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -15,6 +15,8 @@ import Ix.Kernel.Verify.Consistency.CheapBeta import Ix.Kernel.Verify.Consistency.SynthesisCache import Ix.Kernel.Verify.Consistency.SynthesisCacheExecution import Ix.Kernel.Verify.Consistency.LetCache +import Ix.Kernel.Verify.Consistency.BetaSourceInference +import Ix.Kernel.Verify.Consistency.BetaExposureConstruction import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -736,6 +738,43 @@ private def mixedCacheExecutionRoots : Array Lean.Name := #[ ``RecM.whnfWithNatSuccModeNonLeaf_miss_conditional ] +/-- Source reconstruction and success inversion must not depend on the +semantic invariant that the original inference soundness theorem derives. -/ +private def betaSourceExprRoots : Array Lean.Name := #[ + ``BetaStepSource.selected_not_transient, + ``BetaStepSource.peeled, + ``BetaStepSource.substituted, + ``BetaStepSource.output, + ``BetaStepSource.after, + ``BetaStepSource.Resources, + ``BetaStepSource.construct, + ``BetaStepSource.construct_result, + ``BetaStepSource.construct_after, + ``BetaWhnfSource.Resources, + ``BetaWhnfSource.Resources.first +] + +private def betaSourceExecutionRoots : Array Lean.Name := #[ + ``BetaStepSource.construct_run, + ``BetaWhnfTerminal.core_step, + ``BetaWhnfSource.Witness, + ``BetaWhnfSource.construct, + ``BetaCoreExecution.exists_of_miss_success, + ``BetaWhnfSource.CoreResources, + ``BetaWhnfSource.NoDeltaResources, + ``BetaWhnfSource.PublicResources, + ``BetaCoreExecution.exists_of_success, + ``BetaNoDeltaExecution.exists_of_success, + ``BetaPublicExecution.exists_of_success, + ``BetaSortExposure.exists_of_success, + ``BetaPiExposure.exists_of_success, + ``RecM.whnfCoreWithFlagsNonLeaf_fullMiss_success, + ``RecM.whnfNoDeltaImplNonLeaf_fullMiss_core_success, + ``RecM.whnfWithNatSuccModeNonLeaf_miss_noDelta_success, + ``RecM.ensureSortWhnf_success, + ``RecM.ensureForallWhnf_success +] + private def localScopeFrameRoots : Array Lean.Name := #[ ``LocalContext.Equiv.refl, ``LocalContext.Equiv.symm, ``LocalContext.Equiv.trans, ``LocalContext.Equiv.size, ``LocalContext.Equiv.find?, ``LocalContext.Equiv.wf, @@ -1345,17 +1384,22 @@ def roots : Array RootAllowance := #[ }) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots ++ exposedOriginRoots ++ repeatedBetaRoots ++ betaTraceRoots ++ hereditaryBetaRoots ++ piExposureRoots ++ cacheTransportRoots).map (fun allowance => { allowance with forbiddenDependencies := allowance.forbiddenDependencies ++ forbiddenProduction }) - ++ mixedCacheFrameRoots.map (fun root => { + ++ #[``BetaStepSource.selected, ``BetaStepSource.selected_app].map (fun root => { + root, standardAxioms := #[``propext], + forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) + ++ (mixedCacheFrameRoots ++ #[``betaWhnfCharge_success]).map (fun root => { root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) - ++ mixedCacheKeyRoots.map (fun root => { + ++ (mixedCacheKeyRoots ++ betaSourceExprRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ #[``SynthesisInference.beta_core_execution_sound, ``SynthesisInference.beta_noDelta_execution_sound, - ``SynthesisInference.beta_public_execution_sound].map (fun root => { + ``SynthesisInference.beta_public_execution_sound, + ``SynthesisInference.beta_whnf_of_success, ``SynthesisInference.beta_core_of_success, + ``SynthesisInference.beta_noDelta_of_success, ``SynthesisInference.beta_public_of_success].map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) - ++ (recursiveLetShapeRoots ++ sortExposureRoots ++ mixedCacheExecutionRoots).map (fun root => { + ++ (recursiveLetShapeRoots ++ sortExposureRoots ++ mixedCacheExecutionRoots ++ betaSourceExecutionRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ (localScopeFrameRoots ++ localStateFrameRoots ++ recursiveStateFrameRoots ++ ingressFrameRoots ++ diff --git a/Ix/Kernel/Verify/Consistency/BetaCacheConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaCacheConstruction.lean new file mode 100644 index 000000000..8dffa6210 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaCacheConstruction.lean @@ -0,0 +1,184 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaTraceConstruction + +/-! Construct the three cache-layer witnesses from actual successful calls. +Inputs describe finite raw resources and the origins of stored entries; the +current cache lookup, fuel charge, reduction path, and result are recovered. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +namespace BetaWhnfSource + +/-- A structural hit has an actual producing execution. A miss needs only +the finite resources along the source's computed beta orbit. -/ +structure CoreResources {β : Type u} (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) : Prop where + origins : ∀ cached, + (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? = some cached → + ∃ originFuel originBefore target, + ∃ _ : BetaCoreExecution resolve locals originFuel originBefore source term cached target, + originBefore.env.intern.WF + cold : (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? = none → + Resources maxWhnfCoreFuel.toNat (betaWhnfKey source before).2 source + +structure NoDeltaResources {β : Type u} (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) : Prop where + origins : ∀ cached, + (betaWhnfKey source before).2.env.whnfNoDeltaCache[(betaWhnfKey source before).1]? = some cached → + ∃ originFuel originBefore target, + ∃ _ : BetaNoDeltaExecution resolve locals originFuel originBefore source term cached target, + originBefore.env.intern.WF + cold : (betaWhnfKey source before).2.env.whnfNoDeltaCache[(betaWhnfKey source before).1]? = none → + CoreResources resolve locals (betaWhnfKey source before).2 source term + +structure PublicResources {β : Type u} (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) : Prop where + origins : ∀ cached, + (BetaPublicWhnf.outerKey source before).2.env.whnfCache[(BetaPublicWhnf.outerKey source before).1]? = some cached → + ∃ originFuel originBefore target, + ∃ _ : BetaPublicExecution resolve locals originFuel originBefore source term cached target, + originBefore.env.intern.WF + cold : (BetaPublicWhnf.outerKey source before).2.env.whnfCache[ + (BetaPublicWhnf.outerKey source before).1]? = none → + NoDeltaResources resolve locals (betaWhnfCharge (BetaPublicWhnf.outerKey source before).2) source term + +end BetaWhnfSource + +theorem betaWhnfCharge_success {before after : TcState .anon} {methods : Methods .anon} + (accepted : (RecM.whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods before = .ok () after) : + (before.recFuel == 0) = false ∧ after = betaWhnfCharge before := by + have enough : (before.recFuel == 0) = false := by + apply Bool.eq_false_iff.mpr + intro empty + unfold RecM.whnfWithNatSuccModeMissCharge at accepted + rw [ReaderT.run_bind] at accepted + change EStateM.bind (TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 })) + (fun _ => TcM.tick) before = _ at accepted + let bumped := if before.stats then { before with whnfMisses := before.whnfMisses + 1 } else before + have bumpRun : TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 }) before = + .ok () bumped := by + unfold TcM.bumpStats + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + by_cases stats : before.stats = true + · simp only [bumped, if_pos stats]; rfl + · simp only [bumped, if_neg stats]; rfl + have exhausted : (bumped.recFuel == 0) = true := by + dsimp only [bumped] + split <;> exact empty + have failed : TcM.tick bumped = .error .maxRecFuel bumped := by + unfold TcM.tick + change EStateM.bind (get : TcM .anon (TcState .anon)) _ bumped = _ + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) bumped = .ok bumped bumped from rfl] + simp only + rw [exhausted] + rfl + rw [EStateM.bind, bumpRun] at accepted + change TcM.tick bumped = _ at accepted + rw [failed] at accepted + cases accepted + exact ⟨enough, (EStateM.Result.ok.inj ((betaWhnfCharge_run before methods enough).symm.trans accepted)).2.symm⟩ + +/-- The actual lookup selects its branch; successful execution supplies the +result and exact state, including an entry supplied by an earlier run. -/ +theorem BetaCoreExecution.exists_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} + {source result : KExpr .anon} {term : AExpr β} + (chosen : BetaStepSource.selected source = true) + (resources : BetaWhnfSource.CoreResources resolve locals before source term) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) + (accepted : (RecM.whnfCore source).run (methodsN (fuel + 1)) before = .ok result after) : + ∃ target, ∃ execution : BetaCoreExecution resolve locals fuel before source term result target, + execution.after = after := by + cases observed : (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? with + | none => exact exists_of_miss_success chosen (resources.cold observed) reading coherent observed accepted + | some cached => + obtain ⟨originFuel, originBefore, target, origin, initial⟩ := resources.origins cached observed + let execution : BetaCoreExecution resolve locals fuel before source term cached target := + .cached origin initial observed + obtain ⟨rfl, finalEq⟩ := EStateM.Result.ok.inj (execution.run.symm.trans accepted) + exact ⟨target, execution, finalEq⟩ + +theorem BetaNoDeltaExecution.exists_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} + {source result : KExpr .anon} {term : AExpr β} + (chosen : BetaStepSource.selected source = true) + (resources : BetaWhnfSource.NoDeltaResources resolve locals before source term) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) + (accepted : (RecM.whnfNoDelta source).run (methodsN (fuel + 1)) before = .ok result after) : + ∃ target, ∃ execution : BetaNoDeltaExecution resolve locals fuel before source term result target, + execution.after = after := by + cases observed : (betaWhnfKey source before).2.env.whnfNoDeltaCache[(betaWhnfKey source before).1]? with + | none => + have entry : RecM.whnfNoDelta source = RecM.whnfNoDeltaImplNonLeaf source .FULL .collapse := by + obtain ⟨_, _, _, rfl⟩ := BetaStepSource.selected_app chosen + rfl + have direct := accepted + rw [entry] at direct + obtain ⟨coreResult, coreAfter, coreRun⟩ := RecM.whnfNoDeltaImplNonLeaf_fullMiss_core_success rfl + (betaWhnfKey_run source before) (BetaStepSource.selected_not_transient chosen _ _) observed direct + have initial : (betaWhnfKey source before).2.env.intern.WF := by + simpa only [betaWhnfKey_environment] using coherent + obtain ⟨target, core, _⟩ := BetaCoreExecution.exists_of_success chosen + (resources.cold observed) reading initial coreRun + let execution : BetaNoDeltaExecution resolve locals fuel before source term coreResult target := + .reduce core observed + obtain ⟨rfl, finalEq⟩ := EStateM.Result.ok.inj (execution.run.symm.trans accepted) + exact ⟨target, execution, finalEq⟩ + | some cached => + obtain ⟨originFuel, originBefore, target, origin, initial⟩ := resources.origins cached observed + let execution : BetaNoDeltaExecution resolve locals fuel before source term cached target := + .cached origin initial observed + obtain ⟨rfl, finalEq⟩ := EStateM.Result.ok.inj (execution.run.symm.trans accepted) + exact ⟨target, execution, finalEq⟩ + +theorem BetaPublicExecution.exists_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} + {source result : KExpr .anon} {term : AExpr β} + (chosen : BetaStepSource.selected source = true) + (resources : BetaWhnfSource.PublicResources resolve locals before source term) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) + (accepted : (RecM.whnf source).run (methodsN (fuel + 1)) before = .ok result after) : + ∃ target, ∃ execution : BetaPublicExecution resolve locals fuel before source term result target, + execution.after = after := by + cases observed : (BetaPublicWhnf.outerKey source before).2.env.whnfCache[ + (BetaPublicWhnf.outerKey source before).1]? with + | none => + have entry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := by + obtain ⟨_, _, _, rfl⟩ := BetaStepSource.selected_app chosen + rfl + have direct := accepted + rw [entry] at direct + obtain ⟨charged, noDeltaResult, noDeltaAfter, charge, noDeltaRun⟩ := + RecM.whnfWithNatSuccModeNonLeaf_miss_noDelta_success (betaWhnfPrefix_run source before _) + (betaWhnfKey_run source _) (BetaStepSource.selected_not_transient chosen _ _) observed direct + obtain ⟨fuelAvailable, rfl⟩ := betaWhnfCharge_success charge + have initial : (betaWhnfCharge (BetaPublicWhnf.outerKey source before).2).env.intern.WF := by + simpa only [BetaPublicWhnf.outerKey, betaWhnfCharge_fields, betaWhnfKey_environment, + betaWhnfPrefix_fields] using coherent + obtain ⟨target, inner, _⟩ := BetaNoDeltaExecution.exists_of_success chosen + (resources.cold observed) reading initial noDeltaRun + let execution : BetaPublicExecution resolve locals fuel before source term noDeltaResult target := + .reduce inner observed (by + simpa only [BetaPublicWhnf.outerKey, betaWhnfKey_fuel, betaWhnfPrefix_fields] using fuelAvailable) + obtain ⟨rfl, finalEq⟩ := EStateM.Result.ok.inj (execution.run.symm.trans accepted) + exact ⟨target, execution, finalEq⟩ + | some cached => + obtain ⟨originFuel, originBefore, target, origin, initial⟩ := resources.origins cached observed + let execution : BetaPublicExecution resolve locals fuel before source term cached target := + .cached origin initial observed + obtain ⟨rfl, finalEq⟩ := EStateM.Result.ok.inj (execution.run.symm.trans accepted) + exact ⟨target, execution, finalEq⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaExposureConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaExposureConstruction.lean new file mode 100644 index 000000000..6dfcf484f --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaExposureConstruction.lean @@ -0,0 +1,77 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaCacheConstruction +import Ix.Kernel.Verify.Consistency.BetaPublicWhnf +import Ix.Kernel.Verify.Infer.ExposureSuccess + +/-! Recover the Pi and sort exposure witnesses used by the original +inference traces from source resources and actual successful exposure. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +private theorem sort_reading_shape {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {level : KUniv .anon} {info : ExprInfo .anon} {target : AExpr β} + (reading : readScopedExpr? resolve locals (.sort level info) = some target.erase) : + target = .sort (readLevel level) := by + cases target <;> simp_all [readScopedExpr?, AExpr.erase] + +private theorem forall_reading_shape {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {name bi} {rawDomain rawBody : KExpr .anon} {info : ExprInfo .anon} + {target : AExpr β} + (reading : readScopedExpr? resolve locals (.all name bi rawDomain rawBody info) = some target.erase) : + ∃ condition domain body, target = .forallE condition domain body := by + cases target with + | forallE condition domain body => exact ⟨condition, domain, body, rfl⟩ + | _ => + cases domainReads : readScopedExpr? resolve locals rawDomain <;> + cases bodyReads : readScopedExpr? resolve locals rawBody 1 <;> + simp [readScopedExpr?, domainReads, bodyReads, AExpr.erase] at reading + +theorem BetaSortExposure.exists_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} + {source : KExpr .anon} {term : AExpr β} {level : KUniv .anon} + (chosen : BetaStepSource.selected source = true) + (resources : BetaWhnfSource.PublicResources resolve locals before source term) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) + (accepted : (RecM.ensureSortDirect source).run (methodsN (fuel + 1)) before = .ok level after) : + ∃ exposure : BetaSortExposure resolve locals fuel before source term level, exposure.after = after := by + have entry : RecM.ensureSortDirect source = RecM.ensureSortWhnf source := by + obtain ⟨_, _, _, rfl⟩ := BetaStepSource.selected_app chosen + rfl + rw [entry] at accepted + obtain ⟨info, rawRun⟩ := RecM.ensureSortWhnf_success accepted + obtain ⟨target, execution, stateEq⟩ := BetaPublicExecution.exists_of_success chosen resources reading coherent rawRun + have resultReading := (execution.reading reading coherent).1 + obtain rfl := sort_reading_shape resultReading + exact ⟨.execute execution, stateEq⟩ + +theorem BetaPiExposure.exists_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} + {source rawDomain rawBody : KExpr .anon} {term : AExpr β} + (chosen : BetaStepSource.selected source = true) + (resources : BetaWhnfSource.PublicResources resolve locals before source term) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) + (accepted : (RecM.ensureForallDirect source).run (methodsN (fuel + 1)) before = .ok (rawDomain, rawBody) after) : + ∃ condition domain body, + ∃ exposure : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody, + exposure.after = after := by + have entry : RecM.ensureForallDirect source = RecM.ensureForallWhnf source := by + obtain ⟨_, _, _, rfl⟩ := BetaStepSource.selected_app chosen + rfl + rw [entry] at accepted + obtain ⟨name, bi, info, rawRun⟩ := RecM.ensureForallWhnf_success accepted + obtain ⟨target, execution, stateEq⟩ := BetaPublicExecution.exists_of_success chosen resources reading coherent rawRun + have resultReading := (execution.reading reading coherent).1 + obtain ⟨condition, domain, body, rfl⟩ := forall_reading_shape resultReading + exact ⟨condition, domain, body, .execute execution, stateEq⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaSourceInference.lean b/Ix/Kernel/Verify/Consistency/BetaSourceInference.lean new file mode 100644 index 000000000..3af88f3d8 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaSourceInference.lean @@ -0,0 +1,106 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaCacheConstruction +import Ix.Kernel.Verify.Consistency.BetaWhnfInference + +/-! Successful production beta calls preserve the type established by the +original inference. Their operational traces, iteration counts, intermediate +readings, and annotations are constructed from source resources. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +theorem SynthesisInference.beta_whnf_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} {locals : List FVarId} + {fuel : Nat} {inferenceBefore inferenceAfter : TcState .anon} {source inferred : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel inferenceBefore source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals inferenceBefore.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (inferredRun : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) + {reductionFuel : Nat} {flags : WhnfFlags} {before after : TcState .anon} {result : KExpr .anon} + (resources : BetaWhnfSource.Resources maxWhnfCoreFuel.toNat before source) + (coherent : before.env.intern.WF) + (accepted : (RecM.whnfCoreWithFlagsUncached source flags).run (methodsN (reductionFuel + 1)) before = + .ok result after) : + ∃ target, BetaWhnfTerminal result ∧ readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ TypingClaim.{u,v} entries context target type ∧ + after.env.intern.WF := by + let witness := BetaWhnfSource.construct resources reading coherent accepted + exact ⟨witness.target, witness.terminal, + (support.beta_whnf_sound formed agreement reading inferredRun witness.trace coherent witness.enough).2⟩ + +theorem SynthesisInference.beta_core_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} {locals : List FVarId} + {fuel : Nat} {inferenceBefore inferenceAfter : TcState .anon} {source inferred : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel inferenceBefore source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals inferenceBefore.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (inferredRun : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) + {reductionFuel : Nat} {before after : TcState .anon} {result : KExpr .anon} + (chosen : BetaStepSource.selected source = true) + (resources : BetaWhnfSource.CoreResources resolve locals before source term) + (coherent : before.env.intern.WF) + (accepted : (RecM.whnfCore source).run (methodsN (reductionFuel + 1)) before = .ok result after) : + ∃ target, BetaWhnfTerminal result ∧ readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ TypingClaim.{u,v} entries context target type ∧ + after.env.intern.WF := by + obtain ⟨target, execution, stateEq⟩ := BetaCoreExecution.exists_of_success chosen resources reading coherent accepted + refine ⟨target, execution.terminal, ?_⟩ + simpa only [stateEq] using + (support.beta_core_execution_sound formed agreement reading inferredRun execution coherent).2 + +theorem SynthesisInference.beta_noDelta_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} {locals : List FVarId} + {fuel : Nat} {inferenceBefore inferenceAfter : TcState .anon} {source inferred : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel inferenceBefore source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals inferenceBefore.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (inferredRun : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) + {reductionFuel : Nat} {before after : TcState .anon} {result : KExpr .anon} + (chosen : BetaStepSource.selected source = true) + (resources : BetaWhnfSource.NoDeltaResources resolve locals before source term) + (coherent : before.env.intern.WF) + (accepted : (RecM.whnfNoDelta source).run (methodsN (reductionFuel + 1)) before = .ok result after) : + ∃ target, BetaWhnfTerminal result ∧ readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ TypingClaim.{u,v} entries context target type ∧ + after.env.intern.WF := by + obtain ⟨target, execution, stateEq⟩ := BetaNoDeltaExecution.exists_of_success chosen resources reading coherent accepted + refine ⟨target, execution.terminal, ?_⟩ + simpa only [stateEq] using + (support.beta_noDelta_execution_sound formed agreement reading inferredRun execution coherent).2 + +theorem SynthesisInference.beta_public_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} {locals : List FVarId} + {fuel : Nat} {inferenceBefore inferenceAfter : TcState .anon} {source inferred : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel inferenceBefore source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals inferenceBefore.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (inferredRun : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) + {reductionFuel : Nat} {before after : TcState .anon} {result : KExpr .anon} + (chosen : BetaStepSource.selected source = true) + (resources : BetaWhnfSource.PublicResources resolve locals before source term) + (coherent : before.env.intern.WF) + (accepted : (RecM.whnf source).run (methodsN (reductionFuel + 1)) before = .ok result after) : + ∃ target, BetaWhnfTerminal result ∧ readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ TypingClaim.{u,v} entries context target type ∧ + after.env.intern.WF := by + obtain ⟨target, execution, stateEq⟩ := BetaPublicExecution.exists_of_success chosen resources reading coherent accepted + refine ⟨target, execution.terminal, ?_⟩ + simpa only [stateEq] using + (support.beta_public_execution_sound formed agreement reading inferredRun execution coherent).2 + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaStepConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaStepConstruction.lean new file mode 100644 index 000000000..f0a2f2e2c --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaStepConstruction.lean @@ -0,0 +1,220 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaWhnfPlan + +/-! Construct beta-step witnesses from the source expression. The caller +supplies only its reading and finite arithmetic and interning resources. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +namespace BetaStepSource + +def selected (source : KExpr .anon) : Bool := + match source with + | .app .. => match source.collectSpine.1 with + | .lam .. => true + | _ => false + | _ => false + +theorem selected_app {source : KExpr .anon} (chosen : selected source = true) : + ∃ fn arg info, source = .app fn arg info := by + cases source with + | app fn arg info => exact ⟨fn, arg, info, rfl⟩ + | _ => cases chosen + +theorem selected_not_transient {source : KExpr .anon} (chosen : selected source = true) + (methods : Methods .anon) (before : TcState .anon) : + (RecM.isTransientNatLiteralWork source).run methods before = .ok false before := by + obtain ⟨fn, arg, info, rfl⟩ := selected_app chosen + simp only [selected] at chosen + generalize headEq : (KExpr.app fn arg info).collectSpine.1 = head at chosen + cases head with + | lam name bi domain body lambdaInfo => + generalize argumentsEq : (KExpr.app fn arg info).collectSpine.2 = arguments + have spine : (KExpr.app fn arg info).collectSpine = + (.lam name bi domain body lambdaInfo, arguments) := + Prod.ext headEq argumentsEq + simp only [RecM.isTransientNatLiteralWork, RecM.isNatLiteralRecursorApp, spine, pure_bind] + rfl + | _ => cases chosen + +def peeled (source : KExpr .anon) : KExpr .anon × Array (KExpr .anon) := + RecM.consumeBetaLams source.collectSpine.1 source.collectSpine.2 + +def substituted (source : KExpr .anon) (before : TcState .anon) : KExpr .anon × InternTable .anon := + simulSubst (peeled source).1 (peeled source).2.reverse 0 before.env.intern + +def output (source : KExpr .anon) (before : TcState .anon) : KExpr .anon × InternTable .anon := + let result := substituted source before + internAppChain result.1 + (source.collectSpine.2.extract (peeled source).2.size source.collectSpine.2.size).toList result.2 + +def after (source : KExpr .anon) (before : TcState .anon) : TcState .anon := + { before with env := { before.env with intern := (output source before).2 } } + +/-- All candidates are computed by production's own peeling, substitution, +and suffix-building operations. No parsed spine or execution equation is a field. -/ +structure Resources (source : KExpr .anon) (before : TcState .anon) : Prop where + bounds : SimulSubstBounds (peeled source).1 (peeled source).2.reverse 0 + substitutionFaithful : KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ + KExpr.SimulSubstReach (peeled source).2.reverse (peeled source).1 0 term + suffixFaithful : KExpr.CollisionFree fun term => (substituted source before).2.ExprSupport term ∨ + term ∈ cheapBetaChainList (substituted source before).1 + (source.collectSpine.2.extract (peeled source).2.size source.collectSpine.2.size).toList + +private def modelSpine {β : Type u} : AExpr β → AExpr β × List (AExpr β) + | .app fn arg => let (head, arguments) := modelSpine fn; (head, arguments ++ [arg]) + | term => (term, []) + +private theorem modelSpine_rebuild {β : Type u} (term : AExpr β) : + term = (modelSpine term).1.appN (modelSpine term).2 := by + induction term with + | app fn arg ihFn ihArg => + simp only [modelSpine, AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] + exact congrArg (AExpr.app · arg) ihFn + | _ => rfl + +private theorem app_reading_shape {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fn arg : KExpr .anon} {info : ExprInfo .anon} {term : AExpr β} + (reading : readScopedExpr? resolve locals (.app fn arg info) = some term.erase) : + ∃ f a, term = .app f a ∧ readScopedExpr? resolve locals fn = some f.erase ∧ + readScopedExpr? resolve locals arg = some a.erase := by + cases term with + | app f a => exact ⟨f, a, rfl, readScopedExpr?_app_parts reading⟩ + | _ => + cases hf : readScopedExpr? resolve locals fn <;> + cases ha : readScopedExpr? resolve locals arg <;> + simp [readScopedExpr?, hf, ha, AExpr.erase] at reading + +private theorem modelSpine_lambda {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {source : KExpr .anon} {term : AExpr β} + (headLambda : ∃ name bi domain body info, (RecM.appSpineView source).1 = .lam name bi domain body info) + (reading : readScopedExpr? resolve locals source = some term.erase) : + ∃ condition domain body, (modelSpine term).1 = .lam condition domain body := by + induction source generalizing term with + | app fn arg info ihFn ihArg => + obtain ⟨f, a, rfl, fnReads, _⟩ := app_reading_shape reading + simpa only [modelSpine] using ihFn (term := f) headLambda fnReads + | lam name bi rawDomain rawBody info ihDomain ihBody => + cases term with + | lam condition domain body => exact ⟨condition, domain, body, rfl⟩ + | _ => + cases hd : readScopedExpr? resolve locals rawDomain <;> + cases hb : readScopedExpr? resolve locals rawBody 1 <;> + simp [readScopedExpr?, hd, hb, AExpr.erase] at reading + | _ => obtain ⟨_, _, _, _, _, impossible⟩ := headLambda; cases impossible + +private theorem consumed_size (fuel : Nat) (current : KExpr .anon) + (arguments consumed : Array (KExpr .anon)) : + consumed.size ≤ (RecM.consumeBetaLamsFuel fuel current arguments consumed).2.size := by + induction fuel generalizing current consumed with + | zero => exact Nat.le_refl _ + | succ fuel ih => + unfold RecM.consumeBetaLamsFuel + split + · exact Nat.le_refl _ + · cases current with + | lam => exact Nat.le_trans (by simp) (ih _ _) + | _ => exact Nat.le_refl _ + +private theorem consumed_nonempty {name bi rawDomain rawBody info} {arguments : Array (KExpr .anon)} + (nonempty : 0 < arguments.size) : + (!(RecM.consumeBetaLams (.lam name bi rawDomain rawBody info) arguments).2.isEmpty) = true := by + have size : arguments.size = (arguments.size - 1) + 1 := by omega + have positive : 0 < (RecM.consumeBetaLams (.lam name bi rawDomain rawBody info) arguments).2.size := by + unfold RecM.consumeBetaLams + rw [size, RecM.consumeBetaLamsFuel] + simp only [Array.mkEmpty, Array.size_empty, show ¬ 0 ≥ arguments.size from by omega, if_false] + have bound := consumed_size (arguments.size - 1) rawBody arguments + ((Array.mkEmpty arguments.size).push arguments[0]!) + exact Nat.lt_of_lt_of_le Nat.zero_lt_one (by + simpa only [Array.size_push, Array.mkEmpty, Array.size_empty, Nat.zero_add] using bound) + have notEmpty : (RecM.consumeBetaLams (.lam name bi rawDomain rawBody info) arguments).2.isEmpty = false := by + apply Bool.eq_false_iff.mpr + intro empty + have zero := Array.isEmpty_iff_size_eq_zero.mp empty + omega + simp only [notEmpty, Bool.not_false] + +/-- The source reading determines all annotated spine components. Actual +collection and peeling determine every raw component and consumed argument. -/ +def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {source : KExpr .anon} {term : AExpr β} {before : TcState .anon} + (chosen : selected source = true) (reading : readScopedExpr? resolve locals source = some term.erase) + (resources : Resources source before) : + { plan : BetaStepPlan resolve locals before source term // plan.output = output source before } := by + cases source with + | app fn arg info => + simp only [selected] at chosen + generalize rawHead : (KExpr.app fn arg info).collectSpine.1 = head at chosen + cases head with + | lam name bi rawDomain rawInner lambdaInfo => + have rawLambda : ∃ name bi domain body info, + (RecM.appSpineView (.app fn arg info)).1 = .lam name bi domain body info := + ⟨name, bi, rawDomain, rawInner, lambdaInfo, + (RecM.appSpineView_collectSpine (.app fn arg info)).1.symm.trans rawHead⟩ + have modelLambda := modelSpine_lambda (source := .app fn arg info) (term := term) rawLambda reading + generalize modelHead : (modelSpine term).1 = head at modelLambda + cases head with + | lam condition domain inner => + have modelSource : term = (AExpr.lam condition domain inner).appN (modelSpine term).2 := by + rw [← modelHead]; exact modelSpine_rebuild term + have rawLambda' : ∃ name bi domain body info, + (KExpr.app fn arg info).collectSpine.1 = .lam name bi domain body info := + ⟨name, bi, rawDomain, rawInner, lambdaInfo, rawHead⟩ + have reads := readScopedExpr?_lambda_spine rawLambda' (modelSource ▸ reading) + have nonempty : 0 < (KExpr.app fn arg info).collectSpine.2.size := by + have size := congrArg List.length (RecM.appSpineView_collectSpine (.app fn arg info)).2 + simp only [Array.length_toList, List.length_append, List.length_singleton] at size + omega + refine ⟨{ + rawFunction := fn, rawArgument := arg, appInfo := info, sourceEq := rfl, + name, bi, rawDomain, rawInner, lambdaInfo, + rawArguments := (KExpr.app fn arg info).collectSpine.2, + rawBody := (peeled (.app fn arg info)).1, consumed := (peeled (.app fn arg info)).2, + condition, domain, inner, arguments := (modelSpine term).2, modelSource, + spine := Prod.ext rawHead rfl, headReads := rawHead ▸ reads.1, argumentReads := reads.2, + peeling := by simp only [peeled, rawHead], + nonempty := by simpa only [peeled, rawHead] using consumed_nonempty nonempty, + walkerBounds := resources.bounds, walkerFaithful := resources.substitutionFaithful, + suffixFaithful := resources.suffixFaithful + }, ?_⟩ + rfl + | _ => exfalso; obtain ⟨_, _, _, impossible⟩ := modelLambda; cases impossible + | _ => cases chosen + | _ => cases chosen + +theorem construct_result {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {source : KExpr .anon} {term : AExpr β} {before : TcState .anon} + (chosen : selected source = true) (reading : readScopedExpr? resolve locals source = some term.erase) + (resources : Resources source before) : + (construct chosen reading resources).1.result = (output source before).1 := + congrArg Prod.fst (construct chosen reading resources).2 + +theorem construct_after {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {source : KExpr .anon} {term : AExpr β} {before : TcState .anon} + (chosen : selected source = true) (reading : readScopedExpr? resolve locals source = some term.erase) + (resources : Resources source before) : + (construct chosen reading resources).1.after = after source before := + congrArg (fun result : KExpr .anon × InternTable .anon => + { before with env := { before.env with intern := result.2 } }) (construct chosen reading resources).2 + +theorem construct_run {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {source : KExpr .anon} {term : AExpr β} {before : TcState .anon} + (chosen : selected source = true) (reading : readScopedExpr? resolve locals source = some term.erase) + (resources : Resources source before) (fuel : Nat) (flags : WhnfFlags) : + (RecM.whnfCoreWithFlagsStep source flags).run (methodsN (fuel + 1)) before = + .ok (.next (output source before).1) (after source before) := by + have executed := (construct chosen reading resources).1.run fuel flags + simpa only [construct_result, construct_after] using executed + +end BetaStepSource + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean new file mode 100644 index 000000000..77cb1db04 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean @@ -0,0 +1,127 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaStepConstruction +import Ix.Kernel.Verify.Consistency.BetaCacheExecution +import Ix.Kernel.Verify.Whnf.Driver.Success + +/-! Recover finite beta traces from actual successful WHNF runs. Branch +coverage and finite representation data concern only computed raw syntax; +the execution supplies termination and the returned expression and state. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +theorem BetaWhnfTerminal.core_step {source : KExpr .anon} (terminal : BetaWhnfTerminal source) + (methods : Methods .anon) (before : TcState .anon) (flags : WhnfFlags) : + (RecM.whnfCoreWithFlagsStep source flags).run methods before = .ok (.done source) before := by + cases terminal <;> rfl + +namespace BetaWhnfSource + +/-- At each computed beta step, retain only finite walker and hash bounds. +A non-beta branch must be one of the supported terminal constructors. The +zero-fuel case requires no successful result; success is supplied separately +by the actual production run. -/ +def Resources : Nat → TcState .anon → KExpr .anon → Prop + | 0, _, _ => True + | fuel + 1, before, source => + if BetaStepSource.selected source then + BetaStepSource.Resources source before ∧ + Resources fuel (BetaStepSource.after source before) (BetaStepSource.output source before).1 + else BetaWhnfTerminal source + +theorem Resources.first {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} + (resources : Resources fuel before source) (positive : 0 < fuel) + (chosen : BetaStepSource.selected source = true) : BetaStepSource.Resources source before := by + cases fuel with + | zero => omega + | succ fuel => + exact (show BetaStepSource.Resources source before ∧ + Resources fuel (BetaStepSource.after source before) (BetaStepSource.output source before).1 from by + simpa only [Resources, chosen, if_true] using resources).1 + +/-- The annotation and iteration count are outputs of reconstruction. +The trace's endpoints are the actual result and state of the bounded run. -/ +structure Witness {β : Type u} (resolve : Address → Option (ConstRef β)) (locals : List FVarId) + (reductionFuel loopFuel : Nat) (flags : WhnfFlags) (before : TcState .anon) + (source : KExpr .anon) (term : AExpr β) (result : KExpr .anon) (after : TcState .anon) where + steps : Nat + target : AExpr β + trace : BetaWhnfTrace resolve locals reductionFuel flags steps before source term after result target + enough : steps < loopFuel + terminal : BetaWhnfTerminal result + moving : BetaStepSource.selected source = true → 0 < steps + +/-- Successful execution determines the complete raw trace. No intermediate +readings, model expressions, step equations, or iteration count are inputs. -/ +def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {reductionFuel loopFuel : Nat} {flags : WhnfFlags} {before after : TcState .anon} + {source result : KExpr .anon} {term : AExpr β} + (resources : Resources loopFuel before source) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) + (accepted : (RecM.runBounded (fun current => RecM.whnfCoreWithFlagsStep current flags) + loopFuel source).run (methodsN (reductionFuel + 1)) before = .ok result after) : + Witness resolve locals reductionFuel loopFuel flags before source term result after := by + induction loopFuel generalizing before source term with + | zero => cases accepted + | succ loopFuel ih => + by_cases chosen : BetaStepSource.selected source = true + · have data : BetaStepSource.Resources source before ∧ + Resources loopFuel (BetaStepSource.after source before) (BetaStepSource.output source before).1 := by + simpa only [Resources, chosen, if_true] using resources + let first := BetaStepSource.construct chosen reading data.1 + have nextResources : Resources loopFuel first.1.after first.1.result := by + simpa only [first, BetaStepSource.construct_after, BetaStepSource.construct_result] using data.2 + obtain ⟨nextReading, nextCoherent⟩ := first.1.reading coherent + rw [RecM.runBounded, ReaderT.run_bind] at accepted + change EStateM.bind ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before = _ at accepted + rw [EStateM.bind, first.1.run reductionFuel flags] at accepted + let rest := ih nextResources nextReading nextCoherent accepted + exact ⟨rest.steps + 1, rest.target, .next first.1 rest.trace, + Nat.add_lt_add_right rest.enough 1, rest.terminal, fun _ => Nat.zero_lt_succ _⟩ + · have terminal : BetaWhnfTerminal source := by + simpa [Resources, chosen] using resources + have finished := terminal.core_step (methodsN (reductionFuel + 1)) before flags + rw [RecM.runBounded, ReaderT.run_bind] at accepted + change EStateM.bind ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before = _ at accepted + rw [EStateM.bind, finished] at accepted + obtain ⟨rfl, rfl⟩ := EStateM.Result.ok.inj accepted + exact ⟨0, term, .done finished, Nat.zero_lt_succ _, terminal, + fun impossible => False.elim (chosen impossible)⟩ + +end BetaWhnfSource + +/-- Successful structural WHNF on a miss supplies the uncached execution, +then source reconstruction supplies the full reduction and cache witness. -/ +theorem BetaCoreExecution.exists_of_miss_success {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} + {source result : KExpr .anon} {term : AExpr β} + (chosen : BetaStepSource.selected source = true) + (resources : BetaWhnfSource.Resources maxWhnfCoreFuel.toNat (betaWhnfKey source before).2 source) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) + (miss : (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? = none) + (accepted : (RecM.whnfCore source).run (methodsN (fuel + 1)) before = .ok result after) : + ∃ target, ∃ execution : BetaCoreExecution resolve locals fuel before source term result target, + execution.after = after := by + let first := BetaStepSource.construct chosen reading (resources.first (by decide) chosen) + have entry : RecM.whnfCore source = RecM.whnfCoreWithFlagsNonLeaf source .FULL := by + rw [first.1.sourceEq]; rfl + rw [entry] at accepted + obtain ⟨reduced, rawRun, afterEq⟩ := RecM.whnfCoreWithFlagsNonLeaf_fullMiss_success rfl + (betaWhnfKey_run source before) (first.1.not_transient _ _) miss accepted + have initial : (betaWhnfKey source before).2.env.intern.WF := by + simpa only [betaWhnfKey_environment] using coherent + let reconstructed := BetaWhnfSource.construct resources reading initial rawRun + let execution : BetaCoreExecution resolve locals fuel before source term result reconstructed.target := + .reduce reconstructed.trace (reconstructed.moving chosen) reconstructed.enough miss reconstructed.terminal + exact ⟨reconstructed.target, execution, afterEq.symm⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Infer/ExposureSuccess.lean b/Ix/Kernel/Verify/Infer/ExposureSuccess.lean new file mode 100644 index 000000000..2410f494f --- /dev/null +++ b/Ix/Kernel/Verify/Infer/ExposureSuccess.lean @@ -0,0 +1,47 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Infer + +/-! Successful exposure recovers the actual WHNF result and final state. -/ + +namespace Ix.Kernel.RecM + +theorem ensureSortWhnf_success {methods : Methods .anon} {before after : TcState .anon} + {source : KExpr .anon} {level : KUniv .anon} + (accepted : (ensureSortWhnf source).run methods before = .ok level after) : + ∃ info, (whnf source).run methods before = .ok (.sort level info) after := by + unfold ensureSortWhnf at accepted + rw [ReaderT.run_bind] at accepted + change EStateM.bind ((whnf source).run methods) _ before = _ at accepted + cases raw : (whnf source).run methods before with + | error error failed => simp only [EStateM.bind, raw] at accepted; cases accepted + | ok result middle => + rw [EStateM.bind, raw] at accepted + cases result with + | sort actual info => + obtain ⟨rfl, rfl⟩ := EStateM.Result.ok.inj accepted + exact ⟨info, rfl⟩ + | _ => cases accepted + +theorem ensureForallWhnf_success {methods : Methods .anon} {before after : TcState .anon} + {source domain body : KExpr .anon} + (accepted : (ensureForallWhnf source).run methods before = .ok (domain, body) after) : + ∃ name bi info, (whnf source).run methods before = .ok (.all name bi domain body info) after := by + unfold ensureForallWhnf at accepted + rw [ReaderT.run_bind] at accepted + change EStateM.bind ((whnf source).run methods) _ before = _ at accepted + cases raw : (whnf source).run methods before with + | error error failed => simp only [EStateM.bind, raw] at accepted; cases accepted + | ok result middle => + rw [EStateM.bind, raw] at accepted + cases result with + | all name bi actualDomain actualBody info => + obtain ⟨parts, rfl⟩ := EStateM.Result.ok.inj accepted + obtain ⟨rfl, rfl⟩ := Prod.mk.inj parts + exact ⟨name, bi, info, rfl⟩ + | _ => cases accepted + +end Ix.Kernel.RecM diff --git a/Ix/Kernel/Verify/Whnf/Driver/Success.lean b/Ix/Kernel/Verify/Whnf/Driver/Success.lean new file mode 100644 index 000000000..438e53a06 --- /dev/null +++ b/Ix/Kernel/Verify/Whnf/Driver/Success.lean @@ -0,0 +1,146 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Whnf + +/-! Recover the actual subcalls of successful WHNF drivers. These inversions +use only the production monad and cache control flow. -/ + +namespace Ix.Kernel.RecM + +private theorem bind_success {α γ : Type} {action : TcM .anon α} + {next : α → TcM .anon γ} {before after : TcState .anon} {result : γ} + (run : EStateM.bind action next before = .ok result after) : + ∃ value middle, action before = .ok value middle ∧ next value middle = .ok result after := by + unfold EStateM.bind at run + cases step : action before with + | error error state => rw [step] at run; contradiction + | ok value middle => rw [step] at run; exact ⟨value, middle, rfl, run⟩ + +private theorem runBounded_first_success {α γ : Type} + {step : α → RecM .anon (BoundedStep α γ)} {fuel : Nat} + {methods : Methods .anon} {before after : TcState .anon} {source : α} {result : γ} + (accepted : (runBounded step fuel source).run methods before = .ok result after) : + ∃ outcome middle, (step source).run methods before = .ok outcome middle := by + cases fuel with + | zero => cases accepted + | succ fuel => + rw [runBounded, ReaderT.run_bind] at accepted + change EStateM.bind ((step source).run methods) _ before = _ at accepted + obtain ⟨outcome, middle, executed, _⟩ := bind_success accepted + exact ⟨outcome, middle, executed⟩ + +/-- A successful structural-cache miss determines the actual uncached run +and the precise state immediately before its publication. -/ +theorem whnfCoreWithFlagsNonLeaf_fullMiss_success + {methods : Methods .anon} {s s₁ s₂ after : TcState .anon} + {source result : KExpr .anon} {flags : WhnfFlags} {key : Address × Address} + (hfull : flags.isFull = true) + (hkey : TcM.whnfKey source s = .ok key s₁) + (htransient : (isTransientNatLiteralWork source).run methods s₁ = .ok false s₂) + (hmiss : s₂.env.whnfCoreCache[key]? = none) + (accepted : (whnfCoreWithFlagsNonLeaf source flags).run methods s = .ok result after) : + ∃ reduced, (whnfCoreWithFlagsUncached source flags).run methods s₂ = .ok result reduced ∧ + after = { reduced with env := { reduced.env with whnfCoreCache := reduced.env.whnfCoreCache.insert key result } } := by + unfold whnfCoreWithFlagsNonLeaf at accepted + rw [ReaderT.run_bind] at accepted + change EStateM.bind (TcM.whnfKey source) _ s = _ at accepted + rw [EStateM.bind, hkey] at accepted + simp only at accepted + rw [ReaderT.run_bind] at accepted + change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₁ = _ at accepted + rw [EStateM.bind, htransient] at accepted + simp [hfull] at accepted + change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₂ = _ at accepted + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) s₂ = .ok s₂ s₂ from rfl] at accepted + simp only [hmiss] at accepted + rw [ReaderT.run_bind] at accepted + change EStateM.bind ((whnfCoreWithFlagsUncached source flags).run methods) _ s₂ = _ at accepted + cases raw : (whnfCoreWithFlagsUncached source flags).run methods s₂ with + | error error failed => simp only [EStateM.bind, raw] at accepted; cases accepted + | ok value reduced => + rw [EStateM.bind, raw] at accepted + change EStateM.Result.ok value { reduced with env := { reduced.env with + whnfCoreCache := reduced.env.whnfCoreCache.insert key value } } = .ok result after at accepted + obtain ⟨rfl, afterEq⟩ := EStateM.Result.ok.inj accepted + exact ⟨reduced, rfl, afterEq.symm⟩ + + +/-- A successful no-delta miss includes a successful first structural call. +The theorem makes no assumption about which later reducer terminates. -/ +theorem whnfNoDeltaImplNonLeaf_fullMiss_core_success + {methods : Methods .anon} {s s₁ s₂ after : TcState .anon} + {source result : KExpr .anon} {flags : WhnfFlags} {key : Address × Address} + (hfull : flags.isFull = true) + (hkey : TcM.whnfKey source s = .ok key s₁) + (htransient : (isTransientNatLiteralWork source).run methods s₁ = .ok false s₂) + (hmiss : s₂.env.whnfNoDeltaCache[key]? = none) + (accepted : (whnfNoDeltaImplNonLeaf source flags .collapse).run methods s = .ok result after) : + ∃ coreResult coreAfter, (whnfCoreWithFlags source flags).run methods s₂ = .ok coreResult coreAfter := by + unfold whnfNoDeltaImplNonLeaf at accepted + rw [ReaderT.run_bind] at accepted + change EStateM.bind (TcM.whnfKey source) _ s = _ at accepted + rw [EStateM.bind, hkey] at accepted + simp only at accepted + rw [ReaderT.run_bind] at accepted + change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₁ = _ at accepted + rw [EStateM.bind, htransient] at accepted + simp [hfull, show (NatSuccMode.collapse == NatSuccMode.collapse) = true from rfl] at accepted + change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₂ = _ at accepted + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) s₂ = .ok s₂ s₂ from rfl] at accepted + simp only [hmiss] at accepted + rw [ReaderT.run_bind] at accepted + change EStateM.bind ((whnfNoDeltaImplUncached source flags .collapse).run methods) _ s₂ = _ at accepted + obtain ⟨_, _, raw, _⟩ := bind_success accepted + obtain ⟨_, _, first⟩ := runBounded_first_success raw + unfold whnfNoDeltaImplStep at first + rw [ReaderT.run_bind] at first + change EStateM.bind ((whnfCoreWithFlags source flags).run methods) _ s₂ = _ at first + obtain ⟨coreResult, coreAfter, executed, _⟩ := bind_success first + exact ⟨coreResult, coreAfter, executed⟩ + +/-- A successful public miss exposes its actual charge and first no-delta +call, regardless of the later native guard or cache publication. -/ +theorem whnfWithNatSuccModeNonLeaf_miss_noDelta_success + {methods : Methods .anon} {s s₁ s₂ s₃ after : TcState .anon} + {source result : KExpr .anon} {key : Address × Address} + (hprefix : (whnfWithNatSuccModePrefix source).run methods s = .ok () s₁) + (hkey : TcM.whnfKey source s₁ = .ok key s₂) + (htransient : (isTransientNatLiteralWork source).run methods s₂ = .ok false s₃) + (hmiss : s₃.env.whnfCache[key]? = none) + (accepted : (whnfWithNatSuccModeNonLeaf source .collapse).run methods s = .ok result after) : + ∃ charged noDeltaResult noDeltaAfter, + (whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods s₃ = .ok () charged ∧ + (whnfNoDeltaImpl source .FULL .collapse).run methods charged = .ok noDeltaResult noDeltaAfter := by + unfold whnfWithNatSuccModeNonLeaf at accepted + rw [ReaderT.run_bind] at accepted + change EStateM.bind ((whnfWithNatSuccModePrefix source).run methods) _ s = _ at accepted + rw [EStateM.bind, hprefix] at accepted + simp only at accepted + rw [ReaderT.run_bind] at accepted + change EStateM.bind (TcM.whnfKey source) _ s₁ = _ at accepted + rw [EStateM.bind, hkey] at accepted + simp only at accepted + rw [ReaderT.run_bind] at accepted + change EStateM.bind ((isTransientNatLiteralWork source).run methods) _ s₂ = _ at accepted + rw [EStateM.bind, htransient] at accepted + simp [show (NatSuccMode.collapse == NatSuccMode.collapse) = true from rfl] at accepted + change EStateM.bind (get : TcM .anon (TcState .anon)) _ s₃ = _ at accepted + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) s₃ = .ok s₃ s₃ from rfl] at accepted + simp only [hmiss] at accepted + rw [ReaderT.run_bind] at accepted + change EStateM.bind ((whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods) _ s₃ = _ at accepted + obtain ⟨⟨⟩, charged, charge, rest⟩ := bind_success accepted + rw [ReaderT.run_bind] at rest + change EStateM.bind ((whnfWithNatSuccModeUncached source .collapse).run methods) _ charged = _ at rest + obtain ⟨_, _, raw, _⟩ := bind_success rest + obtain ⟨_, _, first⟩ := runBounded_first_success raw + unfold whnfWithNatSuccModeStep at first + rw [ReaderT.run_bind] at first + change EStateM.bind ((whnfNoDeltaImpl source .FULL .collapse).run methods) _ charged = _ at first + obtain ⟨noDeltaResult, noDeltaAfter, executed, _⟩ := bind_success first + exact ⟨charged, noDeltaResult, noDeltaAfter, charge, executed⟩ + +end Ix.Kernel.RecM diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index d91144176..d82c7670c 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -601,7 +601,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 1,391 exact theorem boundaries. The production +The consistency target checks 1,427 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -707,9 +707,10 @@ and intern coherence. `DefinitionBodyTrace.betaDeclaredStepsSupport` and `betaDeclaredWhnfPathSupport` carry these automatic origins into declaration admission. The 28 additional audited boundaries introduce no axioms or native proofs. This closes automatic semantic origins for finite head-beta paths of -the supported source-inference fragment. Initial inference trees, operational -paths, and representation resources remain explicit; deriving them for all -accepted programs and covering the remaining WHNF/conversion paths remain open. +the supported source-inference fragment. Initial inference trees and +representation resources remain explicit. The source construction below +recovers operational beta paths from successful calls; arbitrary accepted +programs and the remaining WHNF/conversion paths remain open. Recursive lets use the same derivation: substituting the value into the body retains every checked child, including a function type exposed by substitution. `SynthesisInference.outputReading` derives returned syntax readings without a @@ -745,6 +746,25 @@ These boundaries introduce no axioms or native proofs. Cached beta traces derive their meaning from the original source check, with no semantic typing field in the cache execution. General WHNF cache agreement, other reducers, and automatic inference-resource construction remain open. +`BetaStepSource.construct` derives every raw and annotated spine component, +lambda prefix, and consumed argument from the source reading. Its inputs +contain only arithmetic bounds and collision freedom on the computed finite +substitution and suffix candidates. `BetaWhnfSource.construct` reconstructs +the complete path and iteration count from an actual successful bounded run; +intermediate expressions, readings, states, and a separate termination +witness are no longer supplied. The raw branch resource restricts the path to +head beta followed by a sort, Pi, or lambda. +The three cache-layer `exists_of_success` theorems observe their actual cache +lookups and recover the successful lower calls, including the public fuel +charge. Populated entries still require retained producing executions. +`SynthesisInference.beta_public_of_success` and its lower-layer counterparts +derive conversion, typing at the original inferred type, the returned reading, +and final intern coherence from these constructed traces. Successful Pi and +sort exposure similarly constructs the original exposure witnesses and their +returned annotations. The 36 additional audited boundaries introduce no +axiom or native proof; all 32 raw construction roots forbid the semantic +hereditary invariant. General source-resource and WHNF-cache construction +remain open. `SynthesisInference.cached` retains the original tree behind an inference cache hit. Its soundness and beta derivations reuse the actual lambda-body, dependent codomain, and argument checks. `reuseFull` derives the cached result diff --git a/docs/theory.md b/docs/theory.md index 9f6c3793f..e20cf1165 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -99,11 +99,17 @@ function type. Dependent substitution rebuilds these derivations, including under retained binders. Each generated result therefore supplies the exact lambda domains and argument origins for every later head-beta step. This also handles a lambda whose body's inferred type -changes by cheap beta. A finite operational WHNF path needs only raw execution, -reading, and representation resources; its semantic origins and declaration -conversion follow automatically from the original inference. Constructing -the initial inference and operational resources for arbitrary accepted -programs, the remaining reduction branches, and general conversion remain open. +changes by cheap beta. Successful beta WHNF now reconstructs the complete +operational path from the source reading and finite arithmetic and hashing +resources. Its intermediate readings, step count, and exact final state are +outputs of the construction. The actual three-layer call determines its cache +branches and fuel charge; populated entries retain earlier producing executions. +The original inference supplies the reconstructed path's semantic origins, +so the returned term preserves its original type. Successful sort and Pi +exposure also reconstructs the witnesses used by the original inference. +Constructing initial inference resources for arbitrary accepted programs, +general WHNF cache origins, the remaining reducers, and general conversion +remain open. Full-mode let inference now uses its original declared-type, value, and opened body checks. The scoped reader interprets a let by value substitution, and the actual opening, abstraction, and substitution walkers preserve that reading From a415fadaaa7c321e9788c5dde195fc70d13a528a Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 16:11:06 -0400 Subject: [PATCH 45/63] Prove explicit-let steps in beta WHNF traces --- Ix/Kernel/Verify/Consistency.lean | 6 + Ix/Kernel/Verify/Consistency/Audit.lean | 43 ++++++- .../Consistency/BetaCacheConstruction.lean | 20 ++- .../Consistency/BetaCacheExecution.lean | 56 ++++----- .../Consistency/BetaExposureConstruction.lean | 14 +-- .../Verify/Consistency/BetaPublicWhnf.lean | 20 ++- .../Consistency/BetaPublicWhnfPlan.lean | 24 ++-- .../Consistency/BetaSourceInference.lean | 8 +- .../Consistency/BetaStepConstruction.lean | 8 ++ .../Consistency/BetaTraceConstruction.lean | 60 ++++++--- Ix/Kernel/Verify/Consistency/BetaWhnf.lean | 25 +++- .../Verify/Consistency/BetaWhnfInference.lean | 3 + .../Verify/Consistency/BetaWhnfPlan.lean | 31 ++++- Ix/Kernel/Verify/Consistency/LetWhnfPlan.lean | 117 ++++++++++++++++++ .../Consistency/StructuralWhnfEntry.lean | 56 +++++++++ Tests/Ix/Kernel/Consistency.lean | 97 +++++++++++++-- docs/kernel-verification.md | 25 +++- docs/theory.md | 7 ++ 18 files changed, 500 insertions(+), 120 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/LetWhnfPlan.lean create mode 100644 Ix/Kernel/Verify/Consistency/StructuralWhnfEntry.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 3df68cae4..d1cbf5ed2 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -45,6 +45,8 @@ import Ix.Kernel.Verify.Consistency.CheapBetaReading import Ix.Kernel.Verify.Consistency.BetaSpine import Ix.Kernel.Verify.Consistency.BetaTrace import Ix.Kernel.Verify.Consistency.BetaWhnf +import Ix.Kernel.Verify.Consistency.StructuralWhnfEntry +import Ix.Kernel.Verify.Consistency.LetWhnfPlan import Ix.Kernel.Verify.Consistency.BetaWhnfPlan import Ix.Kernel.Verify.Consistency.BetaPublicWhnf import Ix.Kernel.Verify.Consistency.WhnfCacheFrame @@ -168,6 +170,10 @@ context keys. Successful beta calls now reconstruct their raw paths from the original source reading and finite arithmetic and hash resources. The actual run supplies the intermediate steps, iteration bound, cache choices, fuel charge, and final state. Cached entries retain their producing executions. +Explicit-let substitution now composes with these beta steps in the same +trace and cache executions. It preserves the scoped reading and the original +annotated term, so later beta steps retain their existing typing derivation. +This also covers let-based Pi/sort exposure and declaration conversion. Only an outer miss charges shared fuel. Pi exposure uses the same complete cache-layer execution. Application inference uses that exposure between argument checks and derives the type conversion diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 485fd9831..9b095d4e8 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -775,6 +775,42 @@ private def betaSourceExecutionRoots : Array Lean.Name := #[ ``RecM.ensureForallWhnf_success ] +private def letWhnfSyntaxRoots : Array Lean.Name := #[ + ``StructuralWhnfEntry, + ``LetStepSource.selected, + ``LetStepSource.parts, + ``LetStepSource.selected_entry, + ``BetaStepSource.selected_entry, + ``BetaWhnfSource.selected, + ``BetaWhnfSource.selected_entry +] + +private def letWhnfExprRoots : Array Lean.Name := #[ + ``StructuralWhnfEntry.not_transient, + ``LetStepPlan, + ``LetStepPlan.output, + ``LetStepPlan.result, + ``LetStepPlan.after, + ``LetStepPlan.entry, + ``LetStepPlan.reading, + ``LetStepSource.output, + ``LetStepSource.after, + ``LetStepSource.Resources, + ``LetStepSource.construct, + ``LetStepSource.construct_result, + ``LetStepSource.construct_after, + ``BetaStepPlan.entry +] + +private def letWhnfExecutionRoots : Array Lean.Name := #[ + ``StructuralWhnfEntry.core, + ``StructuralWhnfEntry.noDelta, + ``StructuralWhnfEntry.full, + ``StructuralWhnfEntry.sort, + ``StructuralWhnfEntry.forallE, + ``LetStepPlan.run +] + private def localScopeFrameRoots : Array Lean.Name := #[ ``LocalContext.Equiv.refl, ``LocalContext.Equiv.symm, ``LocalContext.Equiv.trans, ``LocalContext.Equiv.size, ``LocalContext.Equiv.find?, ``LocalContext.Equiv.wf, @@ -1384,13 +1420,13 @@ def roots : Array RootAllowance := #[ }) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots ++ exposedOriginRoots ++ repeatedBetaRoots ++ betaTraceRoots ++ hereditaryBetaRoots ++ piExposureRoots ++ cacheTransportRoots).map (fun allowance => { allowance with forbiddenDependencies := allowance.forbiddenDependencies ++ forbiddenProduction }) - ++ #[``BetaStepSource.selected, ``BetaStepSource.selected_app].map (fun root => { + ++ (#[``BetaStepSource.selected, ``BetaStepSource.selected_app] ++ letWhnfSyntaxRoots).map (fun root => { root, standardAxioms := #[``propext], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ (mixedCacheFrameRoots ++ #[``betaWhnfCharge_success]).map (fun root => { root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) - ++ (mixedCacheKeyRoots ++ betaSourceExprRoots).map (fun root => { + ++ (mixedCacheKeyRoots ++ betaSourceExprRoots ++ letWhnfExprRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ #[``SynthesisInference.beta_core_execution_sound, ``SynthesisInference.beta_noDelta_execution_sound, @@ -1399,7 +1435,8 @@ def roots : Array RootAllowance := #[ ``SynthesisInference.beta_noDelta_of_success, ``SynthesisInference.beta_public_of_success].map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) - ++ (recursiveLetShapeRoots ++ sortExposureRoots ++ mixedCacheExecutionRoots ++ betaSourceExecutionRoots).map (fun root => { + ++ (recursiveLetShapeRoots ++ sortExposureRoots ++ mixedCacheExecutionRoots ++ betaSourceExecutionRoots ++ + letWhnfExecutionRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ (localScopeFrameRoots ++ localStateFrameRoots ++ recursiveStateFrameRoots ++ ingressFrameRoots ++ diff --git a/Ix/Kernel/Verify/Consistency/BetaCacheConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaCacheConstruction.lean index 8dffa6210..bf8ebc505 100644 --- a/Ix/Kernel/Verify/Consistency/BetaCacheConstruction.lean +++ b/Ix/Kernel/Verify/Consistency/BetaCacheConstruction.lean @@ -92,7 +92,7 @@ result and exact state, including an entry supplied by an earlier run. -/ theorem BetaCoreExecution.exists_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} {term : AExpr β} - (chosen : BetaStepSource.selected source = true) + (chosen : BetaWhnfSource.selected source = true) (resources : BetaWhnfSource.CoreResources resolve locals before source term) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) @@ -111,7 +111,7 @@ theorem BetaCoreExecution.exists_of_success {β : Type u} {resolve : Address → theorem BetaNoDeltaExecution.exists_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} {term : AExpr β} - (chosen : BetaStepSource.selected source = true) + (chosen : BetaWhnfSource.selected source = true) (resources : BetaWhnfSource.NoDeltaResources resolve locals before source term) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) @@ -120,13 +120,12 @@ theorem BetaNoDeltaExecution.exists_of_success {β : Type u} {resolve : Address execution.after = after := by cases observed : (betaWhnfKey source before).2.env.whnfNoDeltaCache[(betaWhnfKey source before).1]? with | none => - have entry : RecM.whnfNoDelta source = RecM.whnfNoDeltaImplNonLeaf source .FULL .collapse := by - obtain ⟨_, _, _, rfl⟩ := BetaStepSource.selected_app chosen - rfl + have entry : RecM.whnfNoDelta source = RecM.whnfNoDeltaImplNonLeaf source .FULL .collapse := + (BetaWhnfSource.selected_entry chosen).noDelta .FULL .collapse have direct := accepted rw [entry] at direct obtain ⟨coreResult, coreAfter, coreRun⟩ := RecM.whnfNoDeltaImplNonLeaf_fullMiss_core_success rfl - (betaWhnfKey_run source before) (BetaStepSource.selected_not_transient chosen _ _) observed direct + (betaWhnfKey_run source before) ((BetaWhnfSource.selected_entry chosen).not_transient _ _) observed direct have initial : (betaWhnfKey source before).2.env.intern.WF := by simpa only [betaWhnfKey_environment] using coherent obtain ⟨target, core, _⟩ := BetaCoreExecution.exists_of_success chosen @@ -145,7 +144,7 @@ theorem BetaNoDeltaExecution.exists_of_success {β : Type u} {resolve : Address theorem BetaPublicExecution.exists_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} {term : AExpr β} - (chosen : BetaStepSource.selected source = true) + (chosen : BetaWhnfSource.selected source = true) (resources : BetaWhnfSource.PublicResources resolve locals before source term) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) @@ -155,14 +154,13 @@ theorem BetaPublicExecution.exists_of_success {β : Type u} {resolve : Address cases observed : (BetaPublicWhnf.outerKey source before).2.env.whnfCache[ (BetaPublicWhnf.outerKey source before).1]? with | none => - have entry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := by - obtain ⟨_, _, _, rfl⟩ := BetaStepSource.selected_app chosen - rfl + have entry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := + (BetaWhnfSource.selected_entry chosen).full .collapse have direct := accepted rw [entry] at direct obtain ⟨charged, noDeltaResult, noDeltaAfter, charge, noDeltaRun⟩ := RecM.whnfWithNatSuccModeNonLeaf_miss_noDelta_success (betaWhnfPrefix_run source before _) - (betaWhnfKey_run source _) (BetaStepSource.selected_not_transient chosen _ _) observed direct + (betaWhnfKey_run source _) ((BetaWhnfSource.selected_entry chosen).not_transient _ _) observed direct obtain ⟨fuelAvailable, rfl⟩ := betaWhnfCharge_success charge have initial : (betaWhnfCharge (BetaPublicWhnf.outerKey source before).2).env.intern.WF := by simpa only [BetaPublicWhnf.outerKey, betaWhnfCharge_fields, betaWhnfKey_environment, diff --git a/Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean b/Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean index a9c31635b..fad5aa8d2 100644 --- a/Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean +++ b/Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean @@ -5,7 +5,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Verify.Consistency.BetaCacheKeys -/-! Executed beta reduction and retained origins at all three WHNF cache layers. -/ +/-! Executed beta/let reduction and retained origins at all three WHNF cache layers. -/ namespace Ix.Kernel.Consistency @@ -96,7 +96,7 @@ theorem writeFull_key (source result : KExpr .anon) (key : Address × Address) ( end BetaCacheExecution -/-- Structural beta execution either runs its raw path or retains the +/-- Structural beta/let execution either runs its raw path or retains the execution that produced the exact cached result. Cache presence supplies no semantic typing assumption. -/ inductive BetaCoreExecution {β : Type u} (resolve : Address → Option (ConstRef β)) @@ -127,13 +127,13 @@ def after (execution : BetaCoreExecution resolve locals fuel before source term whnfCoreCache := reduced.env.whnfCoreCache.insert (betaWhnfKey source before).1 result}} | .cached .. => (betaWhnfKey source before).2 -/-- A retained first step gives the source's lambda spine at every later -cache use. It does not require repeating reduction in the later state. -/ -def first {fuel : Nat} {before : TcState .anon} +/-- A retained first step identifies the source's nonleaf branch at every +later cache use. It does not require repeating reduction in the later state. -/ +theorem first {fuel : Nat} {before : TcState .anon} (execution : BetaCoreExecution resolve locals fuel before source term result target) : - Σ state, BetaStepPlan resolve locals state source term := + StructuralWhnfEntry source := match execution with - | .reduce path moving .. => ⟨_, path.first moving⟩ + | .reduce path moving .. => path.first moving | .cached origin .. => origin.first theorem terminal {fuel : Nat} {before : TcState .anon} @@ -145,8 +145,7 @@ theorem terminal {fuel : Nat} {before : TcState .anon} theorem run (execution : BetaCoreExecution resolve locals fuel before source term result target) : (RecM.whnfCore source).run (methodsN (fuel + 1)) before = .ok result execution.after := by - have entry : RecM.whnfCore source = RecM.whnfCoreWithFlagsNonLeaf source .FULL := by - rw [execution.first.2.sourceEq]; rfl + have entry : RecM.whnfCore source = RecM.whnfCoreWithFlagsNonLeaf source .FULL := execution.first.core .FULL rw [entry] cases execution with | reduce path moving enough miss terminal => @@ -154,7 +153,7 @@ theorem run (execution : BetaCoreExecution resolve locals fuel before source ter ((path.first moving).not_transient _ _) miss (path.run enough) | cached origin coherent hit => exact RecM.whnfCoreWithFlagsNonLeaf_fullHit rfl (betaWhnfKey_run source _) - (origin.first.2.not_transient _ _) hit + (origin.first.not_transient _ _) hit theorem reading (execution : BetaCoreExecution resolve locals fuel before source term result target) (sourceReading : readScopedExpr? resolve locals source = some term.erase) @@ -210,11 +209,10 @@ theorem replay_run (execution : BetaCoreExecution resolve locals fuel before sou (methods : Methods .anon) : (RecM.whnfCore source).run methods execution.after = .ok result (betaWhnfKey source execution.after).2 := by - have entry : RecM.whnfCore source = RecM.whnfCoreWithFlagsNonLeaf source .FULL := by - rw [execution.first.2.sourceEq]; rfl + have entry : RecM.whnfCore source = RecM.whnfCoreWithFlagsNonLeaf source .FULL := execution.first.core .FULL rw [entry] exact RecM.whnfCoreWithFlagsNonLeaf_fullHit rfl (betaWhnfKey_run source _) - (execution.first.2.not_transient _ _) (by + (execution.first.not_transient _ _) (by rw [betaWhnfKey_environment, execution.stable_key] exact execution.published) @@ -244,9 +242,9 @@ def after (execution : BetaNoDeltaExecution resolve locals fuel before source te | .reduce core _ => BetaCacheExecution.writeNoDelta (betaWhnfKey source before).1 result core.after | .cached .. => (betaWhnfKey source before).2 -def first {fuel : Nat} {before : TcState .anon} +theorem first {fuel : Nat} {before : TcState .anon} (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) : - Σ state, BetaStepPlan resolve locals state source term := + StructuralWhnfEntry source := match execution with | .reduce core _ => core.first | .cached origin .. => origin.first @@ -260,16 +258,15 @@ theorem terminal {fuel : Nat} {before : TcState .anon} theorem run (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) : (RecM.whnfNoDelta source).run (methodsN (fuel + 1)) before = .ok result execution.after := by - have entry : RecM.whnfNoDelta source = RecM.whnfNoDeltaImplNonLeaf source .FULL .collapse := by - rw [execution.first.2.sourceEq]; rfl + have entry : RecM.whnfNoDelta source = RecM.whnfNoDeltaImplNonLeaf source .FULL .collapse := execution.first.noDelta .FULL .collapse rw [entry] cases execution with | reduce core miss => exact RecM.whnfNoDeltaImplNonLeaf_fullMiss_conditional rfl (betaWhnfKey_run source _) - (core.first.2.not_transient _ _) miss (core.terminal.noDelta_uncached .FULL .collapse core.run) + (core.first.not_transient _ _) miss (core.terminal.noDelta_uncached .FULL .collapse core.run) | cached origin coherent hit => exact RecM.whnfNoDeltaImplNonLeaf_fullHit rfl (betaWhnfKey_run source _) - (origin.first.2.not_transient _ _) hit + (origin.first.not_transient _ _) hit theorem reading (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) (sourceReading : readScopedExpr? resolve locals source = some term.erase) @@ -321,11 +318,10 @@ theorem replay_run (execution : BetaNoDeltaExecution resolve locals fuel before (inactive : before.inNativeReduce = false) (methods : Methods .anon) : (RecM.whnfNoDelta source).run methods execution.after = .ok result (betaWhnfKey source execution.after).2 := by - have entry : RecM.whnfNoDelta source = RecM.whnfNoDeltaImplNonLeaf source .FULL .collapse := by - rw [execution.first.2.sourceEq]; rfl + have entry : RecM.whnfNoDelta source = RecM.whnfNoDeltaImplNonLeaf source .FULL .collapse := execution.first.noDelta .FULL .collapse rw [entry] exact RecM.whnfNoDeltaImplNonLeaf_fullHit rfl (betaWhnfKey_run source _) - (execution.first.2.not_transient _ _) (by + (execution.first.not_transient _ _) (by rw [betaWhnfKey_environment, execution.stable_key] exact execution.published inactive) @@ -360,9 +356,9 @@ def after (execution : BetaPublicExecution resolve locals fuel before source ter | .reduce inner .. => BetaCacheExecution.writeFull (BetaPublicWhnf.outerKey source before).1 result inner.after | .cached .. => (BetaPublicWhnf.outerKey source before).2 -def first {fuel : Nat} {before : TcState .anon} +theorem first {fuel : Nat} {before : TcState .anon} (execution : BetaPublicExecution resolve locals fuel before source term result target) : - Σ state, BetaStepPlan resolve locals state source term := + StructuralWhnfEntry source := match execution with | .reduce inner .. => inner.first | .cached origin .. => origin.first @@ -376,20 +372,19 @@ theorem terminal {fuel : Nat} {before : TcState .anon} theorem run (execution : BetaPublicExecution resolve locals fuel before source term result target) : (RecM.whnf source).run (methodsN (fuel + 1)) before = .ok result execution.after := by - have entry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := by - rw [execution.first.2.sourceEq]; rfl + have entry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := execution.first.full .collapse rw [entry] cases execution with | reduce inner miss fuelAvailable => exact RecM.whnfWithNatSuccModeNonLeaf_miss_conditional (betaWhnfPrefix_run source before _) - (betaWhnfKey_run source _) (inner.first.2.not_transient _ _) miss + (betaWhnfKey_run source _) (inner.first.not_transient _ _) miss (betaWhnfCharge_run _ _ (by simpa only [BetaPublicWhnf.outerKey, betaWhnfKey_fuel, (betaWhnfPrefix_fields before).2.2.2] using fuelAvailable)) (inner.terminal.full_uncached .collapse inner.run) | cached origin coherent hit => exact RecM.whnfWithNatSuccModeNonLeaf_hit (betaWhnfPrefix_run source before _) - (betaWhnfKey_run source _) (origin.first.2.not_transient _ _) hit + (betaWhnfKey_run source _) (origin.first.not_transient _ _) hit theorem reading (execution : BetaPublicExecution resolve locals fuel before source term result target) (sourceReading : readScopedExpr? resolve locals source = some term.erase) @@ -455,11 +450,10 @@ theorem replay_run (execution : BetaPublicExecution resolve locals fuel before s (inactive : before.inNativeReduce = false) (methods : Methods .anon) : (RecM.whnf source).run methods execution.after = .ok result (BetaPublicWhnf.outerKey source execution.after).2 := by - have entry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := by - rw [execution.first.2.sourceEq]; rfl + have entry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := execution.first.full .collapse rw [entry] exact RecM.whnfWithNatSuccModeNonLeaf_hit (betaWhnfPrefix_run source _ _) - (betaWhnfKey_run source _) (execution.first.2.not_transient _ _) (by + (betaWhnfKey_run source _) (execution.first.not_transient _ _) (by rw [BetaPublicWhnf.outerKey, betaWhnfKey_environment, (betaWhnfPrefix_fields _).1, betaWhnfKey_prefix, execution.stable_key] exact execution.published inactive) diff --git a/Ix/Kernel/Verify/Consistency/BetaExposureConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaExposureConstruction.lean index 6dfcf484f..d88618e91 100644 --- a/Ix/Kernel/Verify/Consistency/BetaExposureConstruction.lean +++ b/Ix/Kernel/Verify/Consistency/BetaExposureConstruction.lean @@ -37,15 +37,14 @@ private theorem forall_reading_shape {β : Type u} {resolve : Address → Option theorem BetaSortExposure.exists_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source : KExpr .anon} {term : AExpr β} {level : KUniv .anon} - (chosen : BetaStepSource.selected source = true) + (chosen : BetaWhnfSource.selected source = true) (resources : BetaWhnfSource.PublicResources resolve locals before source term) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) (accepted : (RecM.ensureSortDirect source).run (methodsN (fuel + 1)) before = .ok level after) : ∃ exposure : BetaSortExposure resolve locals fuel before source term level, exposure.after = after := by - have entry : RecM.ensureSortDirect source = RecM.ensureSortWhnf source := by - obtain ⟨_, _, _, rfl⟩ := BetaStepSource.selected_app chosen - rfl + have entry : RecM.ensureSortDirect source = RecM.ensureSortWhnf source := + (BetaWhnfSource.selected_entry chosen).sort rw [entry] at accepted obtain ⟨info, rawRun⟩ := RecM.ensureSortWhnf_success accepted obtain ⟨target, execution, stateEq⟩ := BetaPublicExecution.exists_of_success chosen resources reading coherent rawRun @@ -56,7 +55,7 @@ theorem BetaSortExposure.exists_of_success {β : Type u} {resolve : Address → theorem BetaPiExposure.exists_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source rawDomain rawBody : KExpr .anon} {term : AExpr β} - (chosen : BetaStepSource.selected source = true) + (chosen : BetaWhnfSource.selected source = true) (resources : BetaWhnfSource.PublicResources resolve locals before source term) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) @@ -64,9 +63,8 @@ theorem BetaPiExposure.exists_of_success {β : Type u} {resolve : Address → Op ∃ condition domain body, ∃ exposure : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody, exposure.after = after := by - have entry : RecM.ensureForallDirect source = RecM.ensureForallWhnf source := by - obtain ⟨_, _, _, rfl⟩ := BetaStepSource.selected_app chosen - rfl + have entry : RecM.ensureForallDirect source = RecM.ensureForallWhnf source := + (BetaWhnfSource.selected_entry chosen).forallE rw [entry] at accepted obtain ⟨name, bi, info, rawRun⟩ := RecM.ensureForallWhnf_success accepted obtain ⟨target, execution, stateEq⟩ := BetaPublicExecution.exists_of_success chosen resources reading coherent rawRun diff --git a/Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean b/Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean index 7b5e39275..517f3fcea 100644 --- a/Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean +++ b/Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean @@ -5,7 +5,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Verify.Consistency.BetaCacheExecution -/-! Pi and sort exposure follow public beta WHNF, including retained +/-! Pi and sort exposure follow public beta/let WHNF, including retained reduction origins at each cache layer. -/ namespace Ix.Kernel.Consistency @@ -55,23 +55,20 @@ theorem run (exposure : BetaPiExposure resolve locals fuel before source term co cases exposure with | reduce plan => have first := plan.path.first plan.moving - have entry : RecM.ensureForallDirect source = RecM.ensureForallWhnf source := by - rw [first.sourceEq]; rfl + have entry : RecM.ensureForallDirect source = RecM.ensureForallWhnf source := first.forallE rw [entry, RecM.ensureForallWhnf, ReaderT.run_bind] change EStateM.bind ((RecM.whnf source).run (methodsN (fuel + 1))) _ before = _ rw [EStateM.bind, plan.run] rfl | cached origin _ hit => have first := origin.path.first origin.moving - have entry : RecM.ensureForallDirect source = RecM.ensureForallWhnf source := by - rw [first.sourceEq]; rfl + have entry : RecM.ensureForallDirect source = RecM.ensureForallWhnf source := first.forallE rw [entry, RecM.ensureForallWhnf, ReaderT.run_bind] change EStateM.bind ((RecM.whnf source).run (methodsN (fuel + 1))) _ before = _ rw [EStateM.bind, origin.cache_hit _ _ hit] rfl | execute execution => - have entry : RecM.ensureForallDirect source = RecM.ensureForallWhnf source := by - rw [execution.first.2.sourceEq]; rfl + have entry : RecM.ensureForallDirect source = RecM.ensureForallWhnf source := execution.first.forallE rw [entry, RecM.ensureForallWhnf, ReaderT.run_bind] change EStateM.bind ((RecM.whnf source).run (methodsN (fuel + 1))) _ before = _ rw [EStateM.bind, execution.run] @@ -150,23 +147,20 @@ theorem run (exposure : BetaSortExposure resolve locals fuel before source term | direct => rfl | reduce plan => have first := plan.path.first plan.moving - have entry : RecM.ensureSortDirect source = RecM.ensureSortWhnf source := by - rw [first.sourceEq]; rfl + have entry : RecM.ensureSortDirect source = RecM.ensureSortWhnf source := first.sort rw [entry, RecM.ensureSortWhnf, ReaderT.run_bind] change EStateM.bind ((RecM.whnf source).run (methodsN (fuel + 1))) _ before = _ rw [EStateM.bind, plan.run] rfl | cached origin hit => have first := origin.path.first origin.moving - have entry : RecM.ensureSortDirect source = RecM.ensureSortWhnf source := by - rw [first.sourceEq]; rfl + have entry : RecM.ensureSortDirect source = RecM.ensureSortWhnf source := first.sort rw [entry, RecM.ensureSortWhnf, ReaderT.run_bind] change EStateM.bind ((RecM.whnf source).run (methodsN (fuel + 1))) _ before = _ rw [EStateM.bind, origin.cache_hit _ _ hit] rfl | execute execution => - have entry : RecM.ensureSortDirect source = RecM.ensureSortWhnf source := by - rw [execution.first.2.sourceEq]; rfl + have entry : RecM.ensureSortDirect source = RecM.ensureSortWhnf source := execution.first.sort rw [entry, RecM.ensureSortWhnf, ReaderT.run_bind] change EStateM.bind ((RecM.whnf source).run (methodsN (fuel + 1))) _ before = _ rw [EStateM.bind, execution.run] diff --git a/Ix/Kernel/Verify/Consistency/BetaPublicWhnfPlan.lean b/Ix/Kernel/Verify/Consistency/BetaPublicWhnfPlan.lean index ed737887b..365ef0f77 100644 --- a/Ix/Kernel/Verify/Consistency/BetaPublicWhnfPlan.lean +++ b/Ix/Kernel/Verify/Consistency/BetaPublicWhnfPlan.lean @@ -6,7 +6,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Verify.Consistency.BetaWhnfPlan import Ix.Kernel.Verify.Whnf.Driver.CacheExecution -/-! Raw beta paths through the public WHNF drivers and their exact cold-cache +/-! Raw beta/let paths through the public WHNF drivers and their exact cold-cache state updates. Cached execution composes these primitives in BetaCacheExecution. -/ namespace Ix.Kernel.Consistency @@ -209,14 +209,15 @@ theorem BetaWhnfTerminal.full_uncached {source result : KExpr .anon} (RecM.whnfWithNatSuccModeUncached source mode).run methods before = .ok result after := bounded_done _ (terminal.full_step mode {} reduced) (by decide) -def BetaWhnfTrace.first {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} +theorem BetaWhnfTrace.first {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {fuel steps : Nat} {flags : WhnfFlags} {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} (trace : BetaWhnfTrace resolve locals fuel flags steps before source term after result target) : - 0 < steps → BetaStepPlan resolve locals before source term := + 0 < steps → StructuralWhnfEntry source := match trace with | .done _ => fun impossible => False.elim (Nat.not_lt_zero _ impossible) - | .next plan _ => fun _ => plan + | .next plan _ => fun _ => plan.entry + | .zeta plan _ => fun _ => plan.entry theorem BetaStepPlan.not_transient {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {before : TcState .anon} {source : KExpr .anon} {term : AExpr β} @@ -285,7 +286,7 @@ end BetaPublicWhnf /-- A public beta reduction through the three cache-miss layers. The key states, instrumentation, fuel charge, and final cache insertions are computed; -the only reduction resource is the raw beta path. -/ +the only reduction resource is the raw beta/let path. -/ structure BetaPublicWhnfPlan {β : Type u} (resolve : Address → Option (ConstRef β)) (locals : List FVarId) (fuel : Nat) (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) (result : KExpr .anon) (target : AExpr β) where @@ -316,8 +317,8 @@ def after (plan : BetaPublicWhnfPlan resolve locals fuel before source term resu theorem run (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : (RecM.whnf source).run (methodsN (fuel + 1)) before = .ok result plan.after := by let first := plan.path.first plan.moving - have coreEntry : RecM.whnfCoreWithFlags source .FULL = RecM.whnfCoreWithFlagsNonLeaf source .FULL := by - rw [first.sourceEq]; rfl + have coreEntry : RecM.whnfCoreWithFlags source .FULL = RecM.whnfCoreWithFlagsNonLeaf source .FULL := + first.core .FULL have coreRun : (RecM.whnfCoreWithFlags source .FULL).run (methodsN (fuel + 1)) (BetaPublicWhnf.noDeltaKey source before).2 = .ok result (BetaPublicWhnf.coreAfter source result before plan.reduced) := by @@ -329,8 +330,7 @@ theorem run (plan : BetaPublicWhnfPlan resolve locals fuel before source term re exact (congrArg TcState.inNativeReduce frame).trans ((BetaPublicWhnf.coreKey_fields source before).2.2.trans plan.native) have noDeltaEntry : RecM.whnfNoDeltaImpl source .FULL .collapse = - RecM.whnfNoDeltaImplNonLeaf source .FULL .collapse := by - rw [first.sourceEq]; rfl + RecM.whnfNoDeltaImplNonLeaf source .FULL .collapse := first.noDelta .FULL .collapse have noDeltaRun : (RecM.whnfNoDeltaImpl source .FULL .collapse).run (methodsN (fuel + 1)) (betaWhnfCharge (BetaPublicWhnf.outerKey source before).2) = .ok result (BetaPublicWhnf.noDeltaAfter source result before plan.reduced) := by @@ -338,8 +338,7 @@ theorem run (plan : BetaPublicWhnfPlan resolve locals fuel before source term re exact RecM.whnfNoDeltaImplNonLeaf_fullMiss rfl (betaWhnfKey_run source _) (first.not_transient _ _) plan.noDeltaMiss (plan.terminal.noDelta_uncached .FULL .collapse coreRun) reducedNative - have fullEntry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := by - rw [first.sourceEq]; rfl + have fullEntry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := first.full .collapse rw [fullEntry] exact RecM.whnfWithNatSuccModeNonLeaf_miss (betaWhnfPrefix_run source before _) (betaWhnfKey_run source _) (first.not_transient _ _) plan.outerMiss @@ -364,8 +363,7 @@ theorem cache_hit (plan : BetaPublicWhnfPlan resolve locals fuel before source t (BetaPublicWhnf.outerKey source current).1]? = some result) : (RecM.whnf source).run methods current = .ok result (BetaPublicWhnf.outerKey source current).2 := by let first := plan.path.first plan.moving - have entry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := by - rw [first.sourceEq]; rfl + have entry : RecM.whnf source = RecM.whnfWithNatSuccModeNonLeaf source .collapse := first.full .collapse rw [entry] exact RecM.whnfWithNatSuccModeNonLeaf_hit (betaWhnfPrefix_run source current _) (betaWhnfKey_run source _) (first.not_transient _ _) hit diff --git a/Ix/Kernel/Verify/Consistency/BetaSourceInference.lean b/Ix/Kernel/Verify/Consistency/BetaSourceInference.lean index 3af88f3d8..5c6497683 100644 --- a/Ix/Kernel/Verify/Consistency/BetaSourceInference.lean +++ b/Ix/Kernel/Verify/Consistency/BetaSourceInference.lean @@ -6,7 +6,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Verify.Consistency.BetaCacheConstruction import Ix.Kernel.Verify.Consistency.BetaWhnfInference -/-! Successful production beta calls preserve the type established by the +/-! Successful production beta/let calls preserve the type established by the original inference. Their operational traces, iteration counts, intermediate readings, and annotations are constructed from source resources. -/ @@ -47,7 +47,7 @@ theorem SynthesisInference.beta_core_of_success {β : Type u} {resolve : Address (reading : readScopedExpr? resolve locals source = some term.erase) (inferredRun : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) {reductionFuel : Nat} {before after : TcState .anon} {result : KExpr .anon} - (chosen : BetaStepSource.selected source = true) + (chosen : BetaWhnfSource.selected source = true) (resources : BetaWhnfSource.CoreResources resolve locals before source term) (coherent : before.env.intern.WF) (accepted : (RecM.whnfCore source).run (methodsN (reductionFuel + 1)) before = .ok result after) : @@ -69,7 +69,7 @@ theorem SynthesisInference.beta_noDelta_of_success {β : Type u} {resolve : Addr (reading : readScopedExpr? resolve locals source = some term.erase) (inferredRun : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) {reductionFuel : Nat} {before after : TcState .anon} {result : KExpr .anon} - (chosen : BetaStepSource.selected source = true) + (chosen : BetaWhnfSource.selected source = true) (resources : BetaWhnfSource.NoDeltaResources resolve locals before source term) (coherent : before.env.intern.WF) (accepted : (RecM.whnfNoDelta source).run (methodsN (reductionFuel + 1)) before = .ok result after) : @@ -91,7 +91,7 @@ theorem SynthesisInference.beta_public_of_success {β : Type u} {resolve : Addre (reading : readScopedExpr? resolve locals source = some term.erase) (inferredRun : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) {reductionFuel : Nat} {before after : TcState .anon} {result : KExpr .anon} - (chosen : BetaStepSource.selected source = true) + (chosen : BetaWhnfSource.selected source = true) (resources : BetaWhnfSource.PublicResources resolve locals before source term) (coherent : before.env.intern.WF) (accepted : (RecM.whnf source).run (methodsN (reductionFuel + 1)) before = .ok result after) : diff --git a/Ix/Kernel/Verify/Consistency/BetaStepConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaStepConstruction.lean index f0a2f2e2c..fa0ae48c1 100644 --- a/Ix/Kernel/Verify/Consistency/BetaStepConstruction.lean +++ b/Ix/Kernel/Verify/Consistency/BetaStepConstruction.lean @@ -29,6 +29,14 @@ theorem selected_app {source : KExpr .anon} (chosen : selected source = true) : | app fn arg info => exact ⟨fn, arg, info, rfl⟩ | _ => cases chosen +theorem selected_entry {source : KExpr .anon} (chosen : selected source = true) : StructuralWhnfEntry source := by + obtain ⟨fn, arg, info, rfl⟩ := selected_app chosen + simp only [selected] at chosen + generalize headEq : (KExpr.app fn arg info).collectSpine.1 = head at chosen + cases head with + | lam name bi domain body lambdaInfo => exact .beta (Prod.ext headEq rfl) + | _ => cases chosen + theorem selected_not_transient {source : KExpr .anon} (chosen : selected source = true) (methods : Methods .anon) (before : TcState .anon) : (RecM.isTransientNatLiteralWork source).run methods before = .ok false before := by diff --git a/Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean index 77cb1db04..c8519c1cc 100644 --- a/Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean +++ b/Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean @@ -7,7 +7,7 @@ import Ix.Kernel.Verify.Consistency.BetaStepConstruction import Ix.Kernel.Verify.Consistency.BetaCacheExecution import Ix.Kernel.Verify.Whnf.Driver.Success -/-! Recover finite beta traces from actual successful WHNF runs. Branch +/-! Recover finite beta/let traces from actual successful WHNF runs. Branch coverage and finite representation data concern only computed raw syntax; the execution supplies termination and the returned expression and state. -/ @@ -24,8 +24,16 @@ theorem BetaWhnfTerminal.core_step {source : KExpr .anon} (terminal : BetaWhnfTe namespace BetaWhnfSource -/-- At each computed beta step, retain only finite walker and hash bounds. -A non-beta branch must be one of the supported terminal constructors. The +def selected (source : KExpr .anon) : Bool := + BetaStepSource.selected source || LetStepSource.selected source + +theorem selected_entry {source : KExpr .anon} (chosen : selected source = true) : StructuralWhnfEntry source := by + by_cases beta : BetaStepSource.selected source = true + · exact BetaStepSource.selected_entry beta + · exact LetStepSource.selected_entry (by simpa [selected, beta] using chosen) + +/-- At each computed beta or let step, retain only finite walker and hash bounds. +Any other branch must be one of the supported terminal constructors. The zero-fuel case requires no successful result; success is supplied separately by the actual production run. -/ def Resources : Nat → TcState .anon → KExpr .anon → Prop @@ -34,6 +42,9 @@ def Resources : Nat → TcState .anon → KExpr .anon → Prop if BetaStepSource.selected source then BetaStepSource.Resources source before ∧ Resources fuel (BetaStepSource.after source before) (BetaStepSource.output source before).1 + else if LetStepSource.selected source then + LetStepSource.Resources source before ∧ + Resources fuel (LetStepSource.after source before) (LetStepSource.output source before).1 else BetaWhnfTerminal source theorem Resources.first {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} @@ -56,7 +67,7 @@ structure Witness {β : Type u} (resolve : Address → Option (ConstRef β)) (lo trace : BetaWhnfTrace resolve locals reductionFuel flags steps before source term after result target enough : steps < loopFuel terminal : BetaWhnfTerminal result - moving : BetaStepSource.selected source = true → 0 < steps + moving : selected source = true → 0 < steps /-- Successful execution determines the complete raw trace. No intermediate readings, model expressions, step equations, or iteration count are inputs. -/ @@ -86,15 +97,29 @@ def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals let rest := ih nextResources nextReading nextCoherent accepted exact ⟨rest.steps + 1, rest.target, .next first.1 rest.trace, Nat.add_lt_add_right rest.enough 1, rest.terminal, fun _ => Nat.zero_lt_succ _⟩ - · have terminal : BetaWhnfTerminal source := by - simpa [Resources, chosen] using resources - have finished := terminal.core_step (methodsN (reductionFuel + 1)) before flags - rw [RecM.runBounded, ReaderT.run_bind] at accepted - change EStateM.bind ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before = _ at accepted - rw [EStateM.bind, finished] at accepted - obtain ⟨rfl, rfl⟩ := EStateM.Result.ok.inj accepted - exact ⟨0, term, .done finished, Nat.zero_lt_succ _, terminal, - fun impossible => False.elim (chosen impossible)⟩ + · by_cases zeta : LetStepSource.selected source = true + · have data : LetStepSource.Resources source before ∧ + Resources loopFuel (LetStepSource.after source before) (LetStepSource.output source before).1 := by + simpa [Resources, chosen, zeta] using resources + let first := LetStepSource.construct zeta data.1 + have nextResources : Resources loopFuel first.1.after first.1.result := by + simpa only [first, LetStepSource.construct_after, LetStepSource.construct_result] using data.2 + obtain ⟨nextReading, nextCoherent⟩ := first.1.reading reading coherent + rw [RecM.runBounded, ReaderT.run_bind] at accepted + change EStateM.bind ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before = _ at accepted + rw [EStateM.bind, first.1.run (methodsN (reductionFuel + 1)) flags] at accepted + let rest := ih nextResources nextReading nextCoherent accepted + exact ⟨rest.steps + 1, rest.target, .zeta first.1 rest.trace, + Nat.add_lt_add_right rest.enough 1, rest.terminal, fun _ => Nat.zero_lt_succ _⟩ + · have terminal : BetaWhnfTerminal source := by + simpa [Resources, chosen, zeta] using resources + have finished := terminal.core_step (methodsN (reductionFuel + 1)) before flags + rw [RecM.runBounded, ReaderT.run_bind] at accepted + change EStateM.bind ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before = _ at accepted + rw [EStateM.bind, finished] at accepted + obtain ⟨rfl, rfl⟩ := EStateM.Result.ok.inj accepted + exact ⟨0, term, .done finished, Nat.zero_lt_succ _, terminal, + fun impossible => by simp [selected, chosen, zeta] at impossible⟩ end BetaWhnfSource @@ -103,7 +128,7 @@ then source reconstruction supplies the full reduction and cache witness. -/ theorem BetaCoreExecution.exists_of_miss_success {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} {term : AExpr β} - (chosen : BetaStepSource.selected source = true) + (chosen : BetaWhnfSource.selected source = true) (resources : BetaWhnfSource.Resources maxWhnfCoreFuel.toNat (betaWhnfKey source before).2 source) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) @@ -111,12 +136,11 @@ theorem BetaCoreExecution.exists_of_miss_success {β : Type u} {resolve : Addres (accepted : (RecM.whnfCore source).run (methodsN (fuel + 1)) before = .ok result after) : ∃ target, ∃ execution : BetaCoreExecution resolve locals fuel before source term result target, execution.after = after := by - let first := BetaStepSource.construct chosen reading (resources.first (by decide) chosen) - have entry : RecM.whnfCore source = RecM.whnfCoreWithFlagsNonLeaf source .FULL := by - rw [first.1.sourceEq]; rfl + let first := BetaWhnfSource.selected_entry chosen + have entry : RecM.whnfCore source = RecM.whnfCoreWithFlagsNonLeaf source .FULL := first.core .FULL rw [entry] at accepted obtain ⟨reduced, rawRun, afterEq⟩ := RecM.whnfCoreWithFlagsNonLeaf_fullMiss_success rfl - (betaWhnfKey_run source before) (first.1.not_transient _ _) miss accepted + (betaWhnfKey_run source before) (first.not_transient _ _) miss accepted have initial : (betaWhnfKey source before).2.env.intern.WF := by simpa only [betaWhnfKey_environment] using coherent let reconstructed := BetaWhnfSource.construct resources reading initial rawRun diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnf.lean b/Ix/Kernel/Verify/Consistency/BetaWhnf.lean index 7be739881..2dfd47262 100644 --- a/Ix/Kernel/Verify/Consistency/BetaWhnf.lean +++ b/Ix/Kernel/Verify/Consistency/BetaWhnf.lean @@ -6,8 +6,8 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Verify.Consistency.BetaTrace import Ix.Kernel.Verify.Consistency.BetaWhnfPlan -/-! Finite beta traces for the actual structural-WHNF loop. Every next -state is computed by production's simultaneous substitution and suffix +/-! Finite beta/let traces for the actual structural-WHNF loop. Every next +state is computed by production's single or simultaneous substitution and suffix interning, and the loop consumes the same fuel as `runBounded`. -/ namespace Ix.Kernel.Consistency @@ -63,7 +63,7 @@ theorem reading end SynthesisBetaStep -/-- A complete finite beta path through structural WHNF. The final +/-- A complete finite beta path, including explicit lets, through structural WHNF. The final iteration returns its input; every preceding iteration has a computed substitution result and intern table, with no postulated intermediate check. -/ inductive SynthesisBetaWhnfTrace {β : Type u} (resolve : Address → Option (ConstRef β)) @@ -82,6 +82,12 @@ inductive SynthesisBetaWhnfTrace {β : Type u} (resolve : Address → Option (Co steps step.after step.result step.modelResult after result target) : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags (steps + 1) before source term after result target + | zeta {steps before after source result term target} + (step : LetStepPlan before source) + (rest : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags + steps step.after step.result term after result target) : + SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags + (steps + 1) before source term after result target namespace SynthesisBetaWhnfTrace @@ -101,6 +107,7 @@ def toBetaTrace {steps : Nat} {before after : TcState .anon} | .next step rest => let first := SynthesisBetaTrace.atType origin step.meaning first.trans (rest.toBetaTrace (.reduced first)) + | .zeta _ rest => rest.toBetaTrace origin termination_by structural trace theorem run @@ -127,6 +134,15 @@ theorem run unfold EStateM.bind rw [step.run reductionFuel flags] exact ih (by omega) + | zeta step rest ih => + cases loopFuel with + | zero => omega + | succ loopFuel => + rw [RecM.runBounded, ReaderT.run_bind] + change EStateM.bind ((RecM.whnfCoreWithFlagsStep _ flags).run _) _ _ = _ + unfold EStateM.bind + rw [step.run (methodsN (reductionFuel + 1)) flags] + exact ih (by omega) theorem reading (trace : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags @@ -139,6 +155,9 @@ theorem reading | next step rest ih => obtain ⟨reading, preserved⟩ := step.reading coherent exact ih reading preserved + | zeta step rest ih => + obtain ⟨reading, preserved⟩ := step.reading sourceReading coherent + exact ih reading preserved /-- The production loop returns the final beta result with the original source's type and a coherent intern table. The finite path uses the real diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean b/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean index bcef88b6f..ee3fcedab 100644 --- a/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean +++ b/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean @@ -49,6 +49,9 @@ def BetaWhnfTrace.annotate {β : Type u} {resolve : Address → Option (ConstRef _ _ _ type := { toBetaStepPlan := plan, meaning := first.2 } let remaining := rest.annotate first.1 ⟨.next step remaining.1, remaining.2⟩ + | .zeta plan rest => + let remaining := rest.annotate typing + ⟨.zeta plan remaining.1, remaining.2⟩ termination_by structural trace /-- Annotating the raw public path derives its semantic trace from the diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnfPlan.lean b/Ix/Kernel/Verify/Consistency/BetaWhnfPlan.lean index 940869d19..ab4e938e3 100644 --- a/Ix/Kernel/Verify/Consistency/BetaWhnfPlan.lean +++ b/Ix/Kernel/Verify/Consistency/BetaWhnfPlan.lean @@ -4,8 +4,9 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.SpineReading +import Ix.Kernel.Verify.Consistency.LetWhnfPlan -/-! Raw beta paths compute production results and states independently of +/-! Raw beta and explicit-let paths compute production results and states independently of the source-inference and semantic-origin derivations. -/ namespace Ix.Kernel.Consistency @@ -69,6 +70,10 @@ def after (step : BetaStepPlan resolve locals before source term) : def modelResult (step : BetaStepPlan resolve locals before source term) : AExpr β := AExpr.betaPrefix step.consumed.size (.lam step.condition step.domain step.inner) step.arguments +theorem entry (step : BetaStepPlan resolve locals before source term) : StructuralWhnfEntry source := by + rw [step.sourceEq] + exact .beta step.spine + theorem run (step : BetaStepPlan resolve locals before source term) (reductionFuel : Nat) (flags : WhnfFlags) : (RecM.whnfCoreWithFlagsStep source flags).run (methodsN (reductionFuel + 1)) before = @@ -110,7 +115,7 @@ theorem counts (plan : BetaStepPlan resolve locals before source term) : end BetaStepPlan /-- A finite production path without semantic step origins. All intermediate -expressions and states are computed by its raw beta plans. -/ +expressions and states are computed by its raw beta and let plans. -/ inductive BetaWhnfTrace {β : Type u} (resolve : Address → Option (ConstRef β)) (locals : List FVarId) (reductionFuel : Nat) (flags : WhnfFlags) : Nat → TcState .anon → KExpr .anon → AExpr β → TcState .anon → KExpr .anon → AExpr β → Type u @@ -123,6 +128,11 @@ inductive BetaWhnfTrace {β : Type u} (resolve : Address → Option (ConstRef β (rest : BetaWhnfTrace resolve locals reductionFuel flags steps plan.after plan.result plan.modelResult after result target) : BetaWhnfTrace resolve locals reductionFuel flags (steps + 1) before source term after result target + | zeta {steps before after source result term target} + (plan : LetStepPlan before source) + (rest : BetaWhnfTrace resolve locals reductionFuel flags + steps plan.after plan.result term after result target) : + BetaWhnfTrace resolve locals reductionFuel flags (steps + 1) before source term after result target namespace BetaWhnfTrace @@ -154,6 +164,15 @@ theorem run unfold EStateM.bind rw [step.run reductionFuel flags] exact ih (by omega) + | zeta step rest ih => + cases loopFuel with + | zero => omega + | succ loopFuel => + rw [RecM.runBounded, ReaderT.run_bind] + change EStateM.bind ((RecM.whnfCoreWithFlagsStep _ flags).run _) _ _ = _ + unfold EStateM.bind + rw [step.run (methodsN (reductionFuel + 1)) flags] + exact ih (by omega) theorem reading (trace : BetaWhnfTrace resolve locals reductionFuel flags steps before source term after result target) @@ -165,8 +184,11 @@ theorem reading | next step rest ih => obtain ⟨reading, preserved⟩ := step.reading coherent exact ih reading preserved + | zeta step rest ih => + obtain ⟨reading, preserved⟩ := step.reading sourceReading coherent + exact ih reading preserved -/-- A beta path changes only the intern table. All cache partitions, +/-- A beta/let path changes only the intern table. All cache partitions, locals, checking policies, and instrumentation fields retain their values. -/ theorem frame (trace : BetaWhnfTrace resolve locals reductionFuel flags steps before source term after result target) : @@ -176,6 +198,9 @@ theorem frame | next step rest ih => obtain ⟨table, same⟩ := ih exact ⟨table, same⟩ + | zeta step rest ih => + obtain ⟨table, same⟩ := ih + exact ⟨table, same⟩ end BetaWhnfTrace diff --git a/Ix/Kernel/Verify/Consistency/LetWhnfPlan.lean b/Ix/Kernel/Verify/Consistency/LetWhnfPlan.lean new file mode 100644 index 000000000..d1357a0cf --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/LetWhnfPlan.lean @@ -0,0 +1,117 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Application +import Ix.Kernel.Verify.Consistency.StructuralWhnfEntry + +/-! Explicit-let WHNF uses production substitution. Its scoped reading +is unchanged, so later reductions retain the original annotated term. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +structure LetStepPlan (before : TcState .anon) (source : KExpr .anon) where + name : Mode.anon.F Name + domain : KExpr .anon + value : KExpr .anon + body : KExpr .anon + nonDep : Bool + info : ExprInfo .anon + sourceEq : source = .letE name domain value body nonDep info + bodyConstructed : body.Constructed + valueConstructed : value.Constructed + bodyBound : body.size + 1 < UInt64.size + valueBound : value.size < UInt64.size + faithful : KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ + KExpr.SubstReach value body 0 term + +namespace LetStepPlan + +variable {before : TcState .anon} {source : KExpr .anon} + +def output (plan : LetStepPlan before source) : KExpr .anon × InternTable .anon := + subst plan.body plan.value 0 before.env.intern + +def result (plan : LetStepPlan before source) : KExpr .anon := plan.output.1 + +def after (plan : LetStepPlan before source) : TcState .anon := + {before with env := {before.env with intern := plan.output.2}} + +theorem entry (plan : LetStepPlan before source) : StructuralWhnfEntry source := + plan.sourceEq ▸ .letE _ _ _ _ _ _ + +theorem run (plan : LetStepPlan before source) (methods : Methods .anon) (flags : WhnfFlags) : + (RecM.whnfCoreWithFlagsStep source flags).run methods before = .ok (.next plan.result) plan.after := by + simp only [plan.sourceEq] + rfl + +theorem reading {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {term : AExpr β} (plan : LetStepPlan before source) + (sourceReading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) : + readScopedExpr? resolve locals plan.result = some term.erase ∧ plan.after.env.intern.WF := by + rw [plan.sourceEq] at sourceReading + obtain ⟨_, value, body, _, valueReads, bodyReads, same⟩ := readScopedExpr?_let_parts sourceReading + obtain ⟨reads, preserved⟩ := subst_readScopedExpr? plan.bodyConstructed plan.valueConstructed + plan.bodyBound plan.valueBound coherent plan.faithful bodyReads valueReads + exact ⟨same ▸ reads, preserved⟩ + +end LetStepPlan + +namespace LetStepSource + +def selected : KExpr .anon → Bool + | .letE .. => true + | _ => false + +def parts : KExpr .anon → KExpr .anon × KExpr .anon + | .letE _ _ value body _ _ => (body, value) + | source => (source, source) + +def output (source : KExpr .anon) (before : TcState .anon) : KExpr .anon × InternTable .anon := + subst (parts source).1 (parts source).2 0 before.env.intern + +def after (source : KExpr .anon) (before : TcState .anon) : TcState .anon := + {before with env := {before.env with intern := (output source before).2}} + +structure Resources (source : KExpr .anon) (before : TcState .anon) : Prop where + bodyConstructed : (parts source).1.Constructed + valueConstructed : (parts source).2.Constructed + bodyBound : (parts source).1.size + 1 < UInt64.size + valueBound : (parts source).2.size < UInt64.size + faithful : KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ + KExpr.SubstReach (parts source).2 (parts source).1 0 term + +theorem selected_entry {source : KExpr .anon} (chosen : selected source = true) : StructuralWhnfEntry source := by + cases source with + | letE name domain value body nonDep info => exact .letE name domain value body nonDep info + | _ => cases chosen + +def construct {source : KExpr .anon} {before : TcState .anon} + (chosen : selected source = true) (resources : Resources source before) : + {plan : LetStepPlan before source // plan.output = output source before} := by + cases source with + | letE name domain value body nonDep info => + exact ⟨⟨name, domain, value, body, nonDep, info, rfl, resources.bodyConstructed, + resources.valueConstructed, resources.bodyBound, resources.valueBound, resources.faithful⟩, rfl⟩ + | _ => cases chosen + +theorem construct_result {source : KExpr .anon} {before : TcState .anon} + (chosen : selected source = true) (resources : Resources source before) : + (construct chosen resources).1.result = (output source before).1 := + congrArg Prod.fst (construct chosen resources).2 + +theorem construct_after {source : KExpr .anon} {before : TcState .anon} + (chosen : selected source = true) (resources : Resources source before) : + (construct chosen resources).1.after = after source before := + congrArg (fun result : KExpr .anon × InternTable .anon => + {before with env := {before.env with intern := result.2}}) (construct chosen resources).2 + +end LetStepSource + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/StructuralWhnfEntry.lean b/Ix/Kernel/Verify/Consistency/StructuralWhnfEntry.lean new file mode 100644 index 000000000..f484b46e2 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/StructuralWhnfEntry.lean @@ -0,0 +1,56 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Infer + +/-! Syntax that enters all three WHNF cache bodies without transient +literal work. This classification contains no typing or execution resource. -/ + +namespace Ix.Kernel.Consistency + +inductive StructuralWhnfEntry : KExpr .anon → Prop + | beta {fn arg : KExpr .anon} {appInfo : ExprInfo .anon} + {name bi domain body info arguments} + (spine : (KExpr.app fn arg appInfo).collectSpine = (.lam name bi domain body info, arguments)) : + StructuralWhnfEntry (.app fn arg appInfo) + | letE (name : Mode.anon.F Name) (domain value body : KExpr .anon) + (nonDep : Bool) (info : ExprInfo .anon) : + StructuralWhnfEntry (.letE name domain value body nonDep info) + +namespace StructuralWhnfEntry + +variable {source : KExpr .anon} + +theorem core (entry : StructuralWhnfEntry source) (flags : WhnfFlags) : + RecM.whnfCoreWithFlags source flags = RecM.whnfCoreWithFlagsNonLeaf source flags := by + cases entry <;> rfl + +theorem noDelta (entry : StructuralWhnfEntry source) (flags : WhnfFlags) (mode : NatSuccMode) : + RecM.whnfNoDeltaImpl source flags mode = RecM.whnfNoDeltaImplNonLeaf source flags mode := by + cases entry <;> rfl + +theorem full (entry : StructuralWhnfEntry source) (mode : NatSuccMode) : + RecM.whnfWithNatSuccMode source mode = RecM.whnfWithNatSuccModeNonLeaf source mode := by + cases entry <;> rfl + +theorem sort (entry : StructuralWhnfEntry source) : + RecM.ensureSortDirect source = RecM.ensureSortWhnf source := by + cases entry <;> rfl + +theorem forallE (entry : StructuralWhnfEntry source) : + RecM.ensureForallDirect source = RecM.ensureForallWhnf source := by + cases entry <;> rfl + +theorem not_transient (entry : StructuralWhnfEntry source) (methods : Methods .anon) (before : TcState .anon) : + (RecM.isTransientNatLiteralWork source).run methods before = .ok false before := by + cases entry with + | beta spine => + simp only [RecM.isTransientNatLiteralWork, RecM.isNatLiteralRecursorApp, spine, pure_bind] + rfl + | letE => rfl + +end StructuralWhnfEntry + +end Ix.Kernel.Consistency diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index c9ea11f19..cc31c9d0b 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -3602,12 +3602,13 @@ private def recursiveLetFailureCleanup : Bool := !after.env.inferCache.contains (invalid.addr, emptyCtxAddr) | _ => false -private def sortExposureEnvironment (level : Ixon.Univ) (universes : UInt64 := 0) : +private def sortExposureEnvironment (level : Ixon.Univ) (universes : UInt64 := 0) (withZeta : Bool := false) : Ixon.Env × Array Address := Id.run do -- References pass the parameter itself, even when the tested level is composite. let arguments := if universes == 0 then #[] else #[2] let levels := #[level, .succ level] ++ if universes == 0 then #[] else #[.var 0] let betaSort := Ixon.Expr.app (.leanLam (.sort 1) (.var 0)) (.sort 0) + let betaSort := if withZeta then Ixon.Expr.letE false (.sort 1) betaSort (.var 0) else betaSort let (env, carrier) := storeConst {} ⟨.axio ⟨false, universes, betaSort⟩, #[], #[], levels⟩ let (env, witness) := storeConst env @@ -3638,9 +3639,9 @@ private def sortExposureEnvironment (level : Ixon.Univ) (universes : UInt64 := 0 /-- The domain's full inference cache keeps its original beta type while sort exposure writes the normalized sort only to the three WHNF caches. -/ private def sortExposureInferencePaths (typeLevel warm instrumented noAccel : Bool) - (lowerWarm : Nat := 0) : Bool := + (lowerWarm : Nat := 0) (withZeta : Bool := false) : Bool := let level := if typeLevel then levelOne else KUniv.mkZero - let (env, targets) := sortExposureEnvironment (if typeLevel then .succ .zero else .zero) + let (env, targets) := sortExposureEnvironment (if typeLevel then .succ .zero else .zero) 0 withZeta (List.range 5).all fun shape => let action : RecM .anon Bool := do let .defn _ _ _ _ _ _ expected value _ _ ← TcM.getConst (m := .anon) ⟨targets[shape]!, ()⟩ | return false @@ -3648,7 +3649,10 @@ private def sortExposureInferencePaths (typeLevel warm instrumented noAccel : Bo | .lam _ _ domain _ _ | .all _ _ domain _ _ | .letE _ domain _ _ _ _ => domain | _ => value let original ← RecM.inferCall domain - let .app .. := original | return false + if withZeta then + let .letE .. := original | return false + else + let .app .. := original | return false let bodyType ← match shape, value with | 4, .lam _ _ _ (.const id _ _) _ => pure (← TcM.getConst id).ty | _, _ => pure original @@ -3700,7 +3704,7 @@ private inductive WhnfWarmLayer unrelated entries in every map and discard lower entries when isolating an upper hit. The trial starts with fresh key memoization. -/ private def mixedWhnfCaches (shape : Nat) (layer : WhnfWarmLayer) - (legacy native instrumented noAccel inferOnly : Bool) : Bool := + (legacy native instrumented noAccel inferOnly : Bool) (withZeta : Bool := false) : Bool := let prop := KExpr.mkSort (m := .anon) .mkZero let sort := KExpr.mkSort (m := .anon) levelOne let argument := if legacy then KExpr.mkVar 0 () else prop @@ -3710,7 +3714,9 @@ private def mixedWhnfCaches (shape : Nat) (layer : WhnfWarmLayer) let expected := if shape == 0 then prop else if shape == 1 then KExpr.mkAll () () argument (if legacy then .mkVar 1 () else prop) else KExpr.mkLam () () argument (if legacy then .mkVar 1 () else prop) - let source := KExpr.mkApp (.mkLam () () sort body) argument + let source := if withZeta then KExpr.mkLet () sort argument + (.mkApp (.mkLam () () sort (.mkLet () sort (.mkVar 0 ()) body false)) (.mkVar 0 ())) false + else KExpr.mkApp (.mkLam () () sort body) argument let sameMap (actual predicted : Std.HashMap (Address × Address) (KExpr .anon)) := actual.size == predicted.size && predicted.toList.all fun (key, result) => actual[key]? == some result let action : RecM .anon Bool := do @@ -3770,9 +3776,10 @@ private def mixedWhnfCaches (shape : Nat) (layer : WhnfWarmLayer) | .ok passed _ => passed | .error _ _ => false -private def mixedWhnfFuelFailure (layer : WhnfWarmLayer) : Bool := +private def mixedWhnfFuelFailure (layer : WhnfWarmLayer) (withZeta : Bool := false) : Bool := let prop := KExpr.mkSort (m := .anon) .mkZero let source := KExpr.mkApp (.mkLam () () (.mkSort levelOne) (.mkVar 0 ())) prop + let source := if withZeta then KExpr.mkLet () (.mkSort levelOne) source (.mkVar 0 ()) false else source let action : RecM .anon Bool := do match layer with | .core => discard <| RecM.whnfCore source @@ -3797,10 +3804,78 @@ private def mixedWhnfCases : TestSeq := [false, true].all fun instrumented => [false, true].all fun noAccel => [false, true].all fun inferOnly => mixedWhnfCaches shape layer legacy native instrumented noAccel inferOnly)) (test "mixed WHNF: lower cache hits do not bypass the public miss fuel check" - ([WhnfWarmLayer.cold, .core, .noDelta].all mixedWhnfFuelFailure)) + ([WhnfWarmLayer.cold, .core, .noDelta].all fun layer => mixedWhnfFuelFailure layer)) + +/-- Alternate two explicit lets with beta. A dependent result forces +substitution beneath its remaining binder, and every reduction counts toward +the actual bounded loop before its final unchanged iteration. -/ +private def letWhnfLoopResult (typeLevel : Bool) (flags : WhnfFlags) (shape : Nat) : Bool := + let carrier := KExpr.mkSort (m := .anon) (if typeLevel then levelOne else .mkZero) + let carrierType := KExpr.mkSort (m := .anon) (if typeLevel then levelTwo else levelOne) + let body := if shape == 0 then KExpr.mkVar 0 () else if shape == 1 then + KExpr.mkAll () () (.mkVar 0 ()) (.mkVar 1 ()) + else KExpr.mkLam () () (.mkVar 0 ()) (.mkVar 0 ()) + let expected := if shape == 0 then carrier else if shape == 1 then + KExpr.mkAll () () carrier carrier + else KExpr.mkLam () () carrier (.mkVar 0 ()) + let source := KExpr.mkLet () carrierType carrier + (.mkApp (.mkLam () () carrierType (.mkLet () carrierType (.mkVar 0 ()) body false)) (.mkVar 0 ())) false + let action : RecM .anon Bool := do + let inferred ← RecM.inferCall source + let expectedType ← RecM.inferCall expected + if !(← RecM.isDefEq inferred expectedType) then return false + let before ← get + let mut current := source + for isLet in [true, false, true] do + if isLet then + let .letE .. := current | return false + else + let .app .. := current | return false + let .next next ← RecM.whnfCoreWithFlagsStep current flags | return false + current := next + let .done terminal ← RecM.whnfCoreWithFlagsStep current flags | return false + let stepped ← get + if !sameSourceExpr terminal expected then return false + match (RecM.runBounded (fun term => RecM.whnfCoreWithFlagsStep term flags) 3 source).run (methodsN 2) before with + | .error .maxRecDepth exhausted => + if exhausted.env.intern.exprs.size != stepped.env.intern.exprs.size then return false + | _ => return false + match (RecM.runBounded (fun term => RecM.whnfCoreWithFlagsStep term flags) 4 source).run (methodsN 2) before with + | .ok result after => + return sameSourceExpr result expected && after.env.intern.exprs.size == stepped.env.intern.exprs.size && + after.recFuel == before.recFuel && after.lctx.size == before.lctx.size && + after.env.nextFVarId == before.env.nextFVarId + | _ => return false + match TcM.runRec action (TcState.newLazyAnon {}) with + | .ok passed _ => passed + | .error _ _ => false -private def sortExposureFailures : Bool := +private def letWhnfCases : TestSeq := + [WhnfWarmLayer.cold, .core, .noDelta, .full].foldl (fun suite layer => suite ++ + test s!"let WHNF: layer {reprStr (match layer with | .cold => 0 | .core => 1 | .noDelta => 2 | .full => 3)} preserves exact maps across let-beta-let reduction" + ((List.range 3).all fun shape => [false, true].all fun legacy => [false, true].all fun native => + [false, true].all fun instrumented => [false, true].all fun noAccel => [false, true].all fun inferOnly => + mixedWhnfCaches shape layer legacy native instrumented noAccel inferOnly true)) + (test "let WHNF: lower cache hits still require the public miss charge" + ([WhnfWarmLayer.cold, .core, .noDelta].all fun layer => mixedWhnfFuelFailure layer true) + ++ test "let WHNF: every let and beta step consumes one loop iteration before the final done" + ([false, true].all fun typeLevel => [WhnfFlags.FULL, .DEF_EQ_CORE].all fun flags => + (List.range 3).all fun shape => letWhnfLoopResult typeLevel flags shape)) + ++ test "let sort exposure: all binder forms retain their unreduced original type across cache layers" + ([false, true].all fun typeLevel => [false, true].all fun instrumented => [false, true].all fun noAccel => + [0, 1, 2].all fun lowerWarm => sortExposureInferencePaths typeLevel false instrumented noAccel lowerWarm true) + ++ test "let sort exposure: retained outer hits work without recursive methods or remaining fuel" + ([false, true].all fun typeLevel => [false, true].all fun instrumented => [false, true].all fun noAccel => + sortExposureInferencePaths typeLevel true instrumented noAccel 0 true) + ++ test "let sort exposure: declarations retain parameters under persistent and cleared caches" + ([0, 1].all fun clearEvery => + allSucceeded (sortExposureEnvironment .zero 0 true).1 9 { clearEvery } && + allSucceeded (sortExposureEnvironment (.succ .zero) 0 true).1 9 { clearEvery } && + allSucceeded (sortExposureEnvironment (.var 0) 1 true).1 9 { clearEvery }) + +private def sortExposureFailures (withZeta : Bool := false) : Bool := let betaPi := Ixon.Expr.app (.leanLam (.sort 1) (.leanAll (.var 0) (.var 1))) (.sort 0) + let betaPi := if withZeta then Ixon.Expr.letE false (.sort 1) betaPi (.var 0) else betaPi let (env, carrierAddr) := storeConst {} ⟨.axio ⟨false, 0, betaPi⟩, #[], #[], #[.zero, .succ .zero]⟩ (List.range 3).all fun shape => @@ -3841,6 +3916,8 @@ private def sortExposureCases : TestSeq := [false, true].all fun noAccel => sortExposureInferencePaths typeLevel false instrumented noAccel 2) ++ test "sort exposure: a reduced Pi is rejected as a sort and failed binder scopes restore locals" sortExposureFailures + ++ test "let sort exposure: a let returning a Pi is rejected and failed binder scopes restore locals" + (sortExposureFailures true) private def letCases : TestSeq := test "let inference: dependent type substitution retains exact child caches, replay, and fresh rebuilding" @@ -3987,7 +4064,7 @@ public def suite : List TestSeq := cheapApplicationCases, exposedLambdaCases, repeatedBetaCases, betaTraceCases, hereditaryBetaCases, piExposureCases, polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, - sourceAgreementCases, sourceCacheCases, compositeCacheCases, letCases, sortExposureCases, mixedWhnfCases, + sourceAgreementCases, sourceCacheCases, compositeCacheCases, letCases, sortExposureCases, mixedWhnfCases, letWhnfCases, polymorphicDefinitionCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index d82c7670c..251b2c96a 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -601,7 +601,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 1,427 exact theorem boundaries. The production +The consistency target checks 1,454 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -753,7 +753,7 @@ substitution and suffix candidates. `BetaWhnfSource.construct` reconstructs the complete path and iteration count from an actual successful bounded run; intermediate expressions, readings, states, and a separate termination witness are no longer supplied. The raw branch resource restricts the path to -head beta followed by a sort, Pi, or lambda. +head beta and explicit lets followed by a sort, Pi, or lambda. The three cache-layer `exists_of_success` theorems observe their actual cache lookups and recover the successful lower calls, including the public fuel charge. Populated entries still require retained producing executions. @@ -765,6 +765,21 @@ returned annotations. The 36 additional audited boundaries introduce no axiom or native proof; all 32 raw construction roots forbid the semantic hereditary invariant. General source-resource and WHNF-cache construction remain open. +`LetStepSource.construct` supplies explicit-let steps in that same trace. +Production's single substitution preserves the scoped reading, since the +reader already substitutes the let value into its body. The annotated term +and its complete original typing derivation therefore pass unchanged to the +next beta step. Lets may precede, follow, or alternate with beta prefixes; +both count toward the actual loop bound, including the final `.done` +iteration. Each step changes only the intern table. +`StructuralWhnfEntry` derives the nonleaf and non-transient facts for both +source forms. The existing three cache executions, publication/replay proofs, +Pi/sort exposures, declaration conversion, and success-based constructors now +include these mixed paths. All 27 added raw boundaries forbid the semantic +hereditary invariant; the existing roots retain their exact axiom profiles. +No new axiom or native proof is introduced. Reducing stored local let values and +reducing a non-lambda application head still require further invariants and +proofs; this extension covers explicit-let structural steps. `SynthesisInference.cached` retains the original tree behind an inference cache hit. Its soundness and beta derivations reuse the actual lambda-body, dependent codomain, and argument checks. `reuseFull` derives the cached result @@ -1013,7 +1028,11 @@ Type, and parameterized universes; persistent and cleared caches; exact child publications; warm exposure at zero method fuel; changed lambda-body beta; and rejection of a reduced function type with local-scope cleanup. They also exercise instrumentation and acceleration flags. -The unit suite contains 738 checks. The anonymous differential additionally +Ten let-WHNF regressions cover alternating let/beta steps, dependent results, +exact loop exhaustion, all cache layers and native guards, zero-method +replay, original inference-cache types during sort exposure, declaration +universe parameters, and rejection with local-scope cleanup. +The unit suite contains 748 checks. The anonymous differential additionally serializes eleven cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. diff --git a/docs/theory.md b/docs/theory.md index e20cf1165..d72f047a3 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -107,6 +107,13 @@ branches and fuel charge; populated entries retain earlier producing executions. The original inference supplies the reconstructed path's semantic origins, so the returned term preserves its original type. Successful sort and Pi exposure also reconstructs the witnesses used by the original inference. +Explicit lets now compose with beta in these same traces. Single substitution +preserves the scoped reading and annotated term, so every later beta step +keeps the derivation obtained from the original check. The actual loop counts +each let and beta iteration before its final unchanged step. Cache execution, +Pi/sort exposure, and declaration conversion all include these mixed paths. +Stored local let values and reduction of non-lambda application heads remain +outside this construction. Constructing initial inference resources for arbitrary accepted programs, general WHNF cache origins, the remaining reducers, and general conversion remain open. From a85571623569df3e185879ab2b0ce7bdbd1058f2 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 17:10:10 -0400 Subject: [PATCH 46/63] Prove recursive application heads in beta WHNF traces --- Ix/Kernel/Verify/Consistency.lean | 9 + .../Verify/Consistency/AppSpineSource.lean | 68 +++++ Ix/Kernel/Verify/Consistency/Audit.lean | 106 ++++++- .../Consistency/BetaCacheConstruction.lean | 18 +- .../Consistency/BetaCacheExecution.lean | 64 +---- .../Verify/Consistency/BetaCacheKeys.lean | 96 ++++++- .../Verify/Consistency/BetaCoreCache.lean | 138 ++++++++++ .../Consistency/BetaExposureConstruction.lean | 4 +- .../Consistency/BetaHeadConstruction.lean | 96 +++++++ .../Verify/Consistency/BetaHeadStepPlan.lean | 65 +++++ .../Verify/Consistency/BetaPrefixPlan.lean | 118 ++++++++ .../Verify/Consistency/BetaPrefixSource.lean | 134 +++++++++ .../Consistency/BetaPublicWhnfPlan.lean | 235 +--------------- .../Consistency/BetaSourceInference.lean | 8 +- .../Consistency/BetaStepConstruction.lean | 35 +-- .../Consistency/BetaTraceConstruction.lean | 196 +++++++++---- Ix/Kernel/Verify/Consistency/BetaWhnf.lean | 97 +++---- .../Verify/Consistency/BetaWhnfInference.lean | 43 ++- .../Verify/Consistency/BetaWhnfPlan.lean | 258 +++++++++++------- .../Verify/Consistency/BetaWhnfState.lean | 233 ++++++++++++++++ .../Consistency/StructuralWhnfEntry.lean | 6 + .../Consistency/SynthesisAppCongruence.lean | 64 +++++ .../Verify/Consistency/WhnfCacheFrame.lean | 26 +- Tests/Ix/Kernel/Consistency.lean | 152 ++++++++++- docs/kernel-verification.md | 36 ++- docs/theory.md | 10 +- 26 files changed, 1729 insertions(+), 586 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/AppSpineSource.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaCoreCache.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaHeadConstruction.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaHeadStepPlan.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaPrefixPlan.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaPrefixSource.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaWhnfState.lean create mode 100644 Ix/Kernel/Verify/Consistency/SynthesisAppCongruence.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index d1cbf5ed2..ab9c76a3a 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -48,6 +48,9 @@ import Ix.Kernel.Verify.Consistency.BetaWhnf import Ix.Kernel.Verify.Consistency.StructuralWhnfEntry import Ix.Kernel.Verify.Consistency.LetWhnfPlan import Ix.Kernel.Verify.Consistency.BetaWhnfPlan +import Ix.Kernel.Verify.Consistency.BetaHeadStepPlan +import Ix.Kernel.Verify.Consistency.BetaHeadConstruction +import Ix.Kernel.Verify.Consistency.SynthesisAppCongruence import Ix.Kernel.Verify.Consistency.BetaPublicWhnf import Ix.Kernel.Verify.Consistency.WhnfCacheFrame import Ix.Kernel.Verify.Consistency.BetaTyping @@ -174,6 +177,12 @@ Explicit-let substitution now composes with these beta steps in the same trace and cache executions. It preserves the scoped reading and the original annotated term, so later beta steps retain their existing typing derivation. This also covers let-based Pi/sort exposure and declaration conversion. +Recursive application heads now use the same trace, with separate method +depth and loop bounds and actual full/cheap cache writes or retained hits. +Source reconstruction includes cold explicit-let heads returning lambdas, +including nested head calls. The original typing derivation supplies the +head conversion while preserving every checked argument. Key frames preserve +all queries when a recursive call memoizes a different legacy context radius. Only an outer miss charges shared fuel. Pi exposure uses the same complete cache-layer execution. Application inference uses that exposure between argument checks and derives the type conversion diff --git a/Ix/Kernel/Verify/Consistency/AppSpineSource.lean b/Ix/Kernel/Verify/Consistency/AppSpineSource.lean new file mode 100644 index 000000000..030d92696 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/AppSpineSource.lean @@ -0,0 +1,68 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.SpineReading + +/-! Recover annotations for the raw application spine. A let at its head +may already read as an application, so the raw spine determines where to stop. -/ + +namespace Ix.Kernel.Consistency.AppSpineSource + +open Theory Theory.Model + +universe u + +def parts {β : Type u} : KExpr .anon → AExpr β → AExpr β × List (AExpr β) + | .app fn _ _, .app f a => let (head, arguments) := parts fn f; (head, arguments ++ [a]) + | _, term => (term, []) + +private theorem app_shape {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fn arg : KExpr .anon} {info : ExprInfo .anon} {term : AExpr β} + (reading : readScopedExpr? resolve locals (.app fn arg info) = some term.erase) : + ∃ f a, term = .app f a ∧ readScopedExpr? resolve locals fn = some f.erase ∧ + readScopedExpr? resolve locals arg = some a.erase := by + cases term with + | app f a => exact ⟨f, a, rfl, readScopedExpr?_app_parts reading⟩ + | _ => + cases hf : readScopedExpr? resolve locals fn <;> + cases ha : readScopedExpr? resolve locals arg <;> + simp [readScopedExpr?, hf, ha, AExpr.erase] at reading + +private theorem view {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {source : KExpr .anon} {term : AExpr β} + (reading : readScopedExpr? resolve locals source = some term.erase) : + term = (parts source term).1.appN (parts source term).2 ∧ + readScopedExpr? resolve locals (RecM.appSpineView source).1 = some (parts source term).1.erase ∧ + (RecM.appSpineView source).2.map (readScopedExpr? resolve locals ·) = + (parts source term).2.map (some ·.erase) := by + induction source generalizing term with + | app fn arg info ihFn ihArg => + obtain ⟨f, a, rfl, fnReads, argReads⟩ := app_shape reading + obtain ⟨rebuilt, headReads, argumentReads⟩ := ihFn fnReads + refine ⟨?_, headReads, ?_⟩ + · simpa only [parts, AExpr.appN_append, AExpr.appN_cons, AExpr.appN_nil] using + congrArg (AExpr.app · a) rebuilt + · simp only [parts, RecM.appSpineView, List.map_append, List.map_cons, List.map_nil, + argumentReads, argReads] + | _ => exact ⟨rfl, reading, rfl⟩ + +theorem reading {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {source : KExpr .anon} {term : AExpr β} + (reads : readScopedExpr? resolve locals source = some term.erase) : + term = (parts source term).1.appN (parts source term).2 ∧ + readScopedExpr? resolve locals source.collectSpine.1 = some (parts source term).1.erase ∧ + source.collectSpine.2.toList.map (readScopedExpr? resolve locals ·) = + (parts source term).2.map (some ·.erase) := by + obtain ⟨rebuilt, headReads, argumentReads⟩ := view reads + exact ⟨rebuilt, (RecM.appSpineView_collectSpine source).1.symm ▸ headReads, + (RecM.appSpineView_collectSpine source).2.symm ▸ argumentReads⟩ + +theorem nonempty (fn arg : KExpr .anon) (info : ExprInfo .anon) : + 0 < (KExpr.app fn arg info).collectSpine.2.size := by + have size := congrArg List.length (RecM.appSpineView_collectSpine (.app fn arg info)).2 + simp only [Array.length_toList, List.length_append, List.length_singleton] at size + omega + +end Ix.Kernel.Consistency.AppSpineSource diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 9b095d4e8..f52bfb709 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -625,7 +625,7 @@ private def hereditaryBetaRoots : Array RootAllowance := #[ { root := ``SynthesisInference.beta_steps_sound, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, { root := ``BetaStepPlan.run, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, { root := ``BetaStepPlan.sourceReading, standardAxioms := standard, nativeAxioms := #[expressionNative] }, - { root := ``BetaStepPlan.reading, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, + { root := ``BetaStepPlan.reading, standardAxioms := standard, nativeAxioms := #[expressionNative] }, { root := ``BetaStepPlan.counts, standardAxioms := standard, nativeAxioms := #[expressionNative] }, { root := ``BetaStepPlan.betaTyping, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, { root := ``BetaWhnfTrace.annotate, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative] }, @@ -678,17 +678,17 @@ private def piExposureRoots : Array RootAllowance := #[ /-- Cache producers and replay must precede semantic hereditary typing. The operational layer records raw executions and derives published hits. -/ private def mixedCacheFrameRoots : Array Lean.Name := #[ + ``BetaCacheExecution.writeNoDelta_intern, + ``BetaCacheExecution.writeFull_intern +] + +private def mixedCacheKeyRoots : Array Lean.Name := #[ ``BetaCacheFrame.refl, ``BetaCacheFrame.trans, ``BetaCacheFrame.instrument, ``BetaCacheFrame.charge, ``BetaCacheExecution.writeNoDelta_frame, ``BetaCacheExecution.writeFull_frame, - ``BetaCacheExecution.writeNoDelta_intern, - ``BetaCacheExecution.writeFull_intern -] - -private def mixedCacheKeyRoots : Array Lean.Name := #[ ``betaWhnfKey_congr, ``betaWhnfKey_replay, ``betaWhnfKey_prefix, @@ -749,12 +749,12 @@ private def betaSourceExprRoots : Array Lean.Name := #[ ``BetaStepSource.Resources, ``BetaStepSource.construct, ``BetaStepSource.construct_result, - ``BetaStepSource.construct_after, - ``BetaWhnfSource.Resources, - ``BetaWhnfSource.Resources.first + ``BetaStepSource.construct_after ] private def betaSourceExecutionRoots : Array Lean.Name := #[ + ``BetaWhnfSource.Resources, + ``BetaWhnfSource.Resources.first, ``BetaStepSource.construct_run, ``BetaWhnfTerminal.core_step, ``BetaWhnfSource.Witness, @@ -811,6 +811,84 @@ private def letWhnfExecutionRoots : Array Lean.Name := #[ ``LetStepPlan.run ] +/-- Recursive head calls retain their own method depth, full/cheap cache +effects, and source-derived beta continuation. No head annotation or raw +execution proof may depend on the semantic hereditary invariant. -/ +private def headWhnfSyntaxRoots : Array Lean.Name := #[ + ``AppSpineSource.parts, + ``BetaPrefixSource.selected, + ``BetaHeadStepSource.selected, + ``BetaHeadStepSource.selected_app, + ``BetaHeadStepSource.selected_head, + ``BetaHeadStepSource.selected_entry +] + +private def headWhnfFrameRoots : Array Lean.Name := #[ + ``AppSpineSource.reading, + ``AppSpineSource.nonempty, + ``BetaCoreCache.lookup, + ``BetaCoreCache.write, + ``BetaCoreCache.intern +] + +private def headWhnfExprRoots : Array Lean.Name := #[ + ``BetaPrefixPlan, + ``BetaPrefixPlan.rawLambda, + ``BetaPrefixPlan.modelLambda, + ``BetaPrefixPlan.modelInput, + ``BetaPrefixPlan.output, + ``BetaPrefixPlan.result, + ``BetaPrefixPlan.after, + ``BetaPrefixPlan.modelResult, + ``BetaPrefixPlan.counts, + ``BetaPrefixPlan.reading, + ``BetaPrefixSource.peeled, + ``BetaPrefixSource.substituted, + ``BetaPrefixSource.output, + ``BetaPrefixSource.after, + ``BetaPrefixSource.Resources, + ``BetaPrefixSource.consumed_nonempty, + ``BetaPrefixSource.Witness, + ``BetaPrefixSource.construct, + ``BetaPrefixSource.Witness.result, + ``BetaPrefixSource.Witness.after, + ``BetaHeadStepPlan, + ``BetaHeadStepPlan.rawLambda, + ``BetaHeadStepPlan.modelLambda, + ``BetaHeadStepPlan.result, + ``BetaHeadStepPlan.after, + ``BetaHeadStepPlan.modelResult, + ``BetaHeadStepPlan.entry, + ``BetaHeadStepPlan.sourceReading, + ``BetaHeadStepPlan.reading, + ``BetaHeadStepSource.Witness, + ``BetaHeadStepSource.construct, + ``BetaCoreCache.frame, + ``betaWhnfKey_key, + ``BetaCacheFrame.intern, + ``BetaCacheFrame.core, + ``BetaCacheFrame.cheap +] + +private def headWhnfExecutionRoots : Array Lean.Name := #[ + ``BetaPrefixPlan.run, + ``BetaHeadStepPlan.run, + ``BetaHeadStepSource.head_of_success, + ``BetaCoreCache.hit, + ``BetaCoreCache.miss, + ``BetaCoreCache.miss_success, + ``SynthesisBetaTyping.mapFunction, + ``SynthesisBetaTyping.mapHead, + ``BetaHeadReduction, + ``BetaHeadReduction.entry, + ``BetaHeadReduction.run, + ``BetaHeadReduction.reading, + ``BetaHeadReduction.frame, + ``BetaPrefixPlan.betaTyping, + ``BetaHeadReduction.annotate, + ``SynthesisBetaWhnfTrace.toRawTrace +] + private def localScopeFrameRoots : Array Lean.Name := #[ ``LocalContext.Equiv.refl, ``LocalContext.Equiv.symm, ``LocalContext.Equiv.trans, ``LocalContext.Equiv.size, ``LocalContext.Equiv.find?, ``LocalContext.Equiv.wf, @@ -1420,13 +1498,13 @@ def roots : Array RootAllowance := #[ }) ++ (betaRoots ++ typeOriginRoots ++ substitutedOriginRoots ++ exposedOriginRoots ++ repeatedBetaRoots ++ betaTraceRoots ++ hereditaryBetaRoots ++ piExposureRoots ++ cacheTransportRoots).map (fun allowance => { allowance with forbiddenDependencies := allowance.forbiddenDependencies ++ forbiddenProduction }) - ++ (#[``BetaStepSource.selected, ``BetaStepSource.selected_app] ++ letWhnfSyntaxRoots).map (fun root => { + ++ (#[``BetaStepSource.selected, ``BetaStepSource.selected_app] ++ letWhnfSyntaxRoots ++ headWhnfSyntaxRoots).map (fun root => { root, standardAxioms := #[``propext], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) - ++ (mixedCacheFrameRoots ++ #[``betaWhnfCharge_success]).map (fun root => { + ++ (mixedCacheFrameRoots ++ headWhnfFrameRoots ++ #[``betaWhnfCharge_success]).map (fun root => { root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) - ++ (mixedCacheKeyRoots ++ betaSourceExprRoots ++ letWhnfExprRoots).map (fun root => { + ++ (mixedCacheKeyRoots ++ betaSourceExprRoots ++ letWhnfExprRoots ++ headWhnfExprRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ #[``SynthesisInference.beta_core_execution_sound, ``SynthesisInference.beta_noDelta_execution_sound, @@ -1436,7 +1514,7 @@ def roots : Array RootAllowance := #[ root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) ++ (recursiveLetShapeRoots ++ sortExposureRoots ++ mixedCacheExecutionRoots ++ betaSourceExecutionRoots ++ - letWhnfExecutionRoots).map (fun root => { + letWhnfExecutionRoots ++ headWhnfExecutionRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ (localScopeFrameRoots ++ localStateFrameRoots ++ recursiveStateFrameRoots ++ ingressFrameRoots ++ @@ -1457,6 +1535,8 @@ def roots : Array RootAllowance := #[ root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) ++ #[ + { root := ``BetaCoreCache.published, standardAxioms := standard, + forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }, { root := ``LocalStateInvariant.newLazyAnon, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative, nameNative], forbiddenDependencies := forbiddenProduction }, diff --git a/Ix/Kernel/Verify/Consistency/BetaCacheConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaCacheConstruction.lean index bf8ebc505..d2ebb5e92 100644 --- a/Ix/Kernel/Verify/Consistency/BetaCacheConstruction.lean +++ b/Ix/Kernel/Verify/Consistency/BetaCacheConstruction.lean @@ -20,27 +20,27 @@ namespace BetaWhnfSource /-- A structural hit has an actual producing execution. A miss needs only the finite resources along the source's computed beta orbit. -/ structure CoreResources {β : Type u} (resolve : Address → Option (ConstRef β)) - (locals : List FVarId) (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) : Prop where + (locals : List FVarId) (fuel : Nat) (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) : Prop where origins : ∀ cached, (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? = some cached → ∃ originFuel originBefore target, ∃ _ : BetaCoreExecution resolve locals originFuel originBefore source term cached target, originBefore.env.intern.WF cold : (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? = none → - Resources maxWhnfCoreFuel.toNat (betaWhnfKey source before).2 source + Resources (fuel + 1) .FULL maxWhnfCoreFuel.toNat (betaWhnfKey source before).2 source structure NoDeltaResources {β : Type u} (resolve : Address → Option (ConstRef β)) - (locals : List FVarId) (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) : Prop where + (locals : List FVarId) (fuel : Nat) (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) : Prop where origins : ∀ cached, (betaWhnfKey source before).2.env.whnfNoDeltaCache[(betaWhnfKey source before).1]? = some cached → ∃ originFuel originBefore target, ∃ _ : BetaNoDeltaExecution resolve locals originFuel originBefore source term cached target, originBefore.env.intern.WF cold : (betaWhnfKey source before).2.env.whnfNoDeltaCache[(betaWhnfKey source before).1]? = none → - CoreResources resolve locals (betaWhnfKey source before).2 source term + CoreResources resolve locals fuel (betaWhnfKey source before).2 source term structure PublicResources {β : Type u} (resolve : Address → Option (ConstRef β)) - (locals : List FVarId) (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) : Prop where + (locals : List FVarId) (fuel : Nat) (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) : Prop where origins : ∀ cached, (BetaPublicWhnf.outerKey source before).2.env.whnfCache[(BetaPublicWhnf.outerKey source before).1]? = some cached → ∃ originFuel originBefore target, @@ -48,7 +48,7 @@ structure PublicResources {β : Type u} (resolve : Address → Option (ConstRef originBefore.env.intern.WF cold : (BetaPublicWhnf.outerKey source before).2.env.whnfCache[ (BetaPublicWhnf.outerKey source before).1]? = none → - NoDeltaResources resolve locals (betaWhnfCharge (BetaPublicWhnf.outerKey source before).2) source term + NoDeltaResources resolve locals fuel (betaWhnfCharge (BetaPublicWhnf.outerKey source before).2) source term end BetaWhnfSource @@ -93,7 +93,7 @@ theorem BetaCoreExecution.exists_of_success {β : Type u} {resolve : Address → {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} {term : AExpr β} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.CoreResources resolve locals before source term) + (resources : BetaWhnfSource.CoreResources resolve locals fuel before source term) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) (accepted : (RecM.whnfCore source).run (methodsN (fuel + 1)) before = .ok result after) : @@ -112,7 +112,7 @@ theorem BetaNoDeltaExecution.exists_of_success {β : Type u} {resolve : Address {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} {term : AExpr β} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.NoDeltaResources resolve locals before source term) + (resources : BetaWhnfSource.NoDeltaResources resolve locals fuel before source term) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) (accepted : (RecM.whnfNoDelta source).run (methodsN (fuel + 1)) before = .ok result after) : @@ -145,7 +145,7 @@ theorem BetaPublicExecution.exists_of_success {β : Type u} {resolve : Address {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} {term : AExpr β} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.PublicResources resolve locals before source term) + (resources : BetaWhnfSource.PublicResources resolve locals fuel before source term) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) (accepted : (RecM.whnf source).run (methodsN (fuel + 1)) before = .ok result after) : diff --git a/Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean b/Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean index fad5aa8d2..bb6f91018 100644 --- a/Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean +++ b/Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean @@ -3,7 +3,7 @@ Copyright (c) 2026 Argument Computer Corporation. SPDX-License-Identifier: MIT OR Apache-2.0 -/ -import Ix.Kernel.Verify.Consistency.BetaCacheKeys +import Ix.Kernel.Verify.Consistency.BetaPublicWhnfPlan /-! Executed beta/let reduction and retained origins at all three WHNF cache layers. -/ @@ -13,47 +13,6 @@ open Theory Theory.Model universe u -/-- State shared by surrounding inference is unchanged by these beta paths. -The three reduction caches, intern table, key memoization, and WHNF counters -are tracked by the computed result state instead. -/ -structure BetaCacheFrame (before after : TcState .anon) : Prop where - constants : after.env.consts = before.env.consts - full : after.env.inferCache = before.env.inferCache - only : after.env.inferOnlyCache = before.env.inferOnlyCache - context : after.lctx = before.lctx - policy : after.inferOnly = before.inferOnly - native : after.inNativeReduce = before.inNativeReduce - -namespace BetaCacheFrame - -theorem refl (before : TcState .anon) : BetaCacheFrame before before := - ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩ - -theorem trans {before middle after : TcState .anon} - (first : BetaCacheFrame before middle) (second : BetaCacheFrame middle after) : - BetaCacheFrame before after := - ⟨second.constants.trans first.constants, second.full.trans first.full, - second.only.trans first.only, second.context.trans first.context, - second.policy.trans first.policy, second.native.trans first.native⟩ - -theorem key (source : KExpr .anon) (before : TcState .anon) : - BetaCacheFrame before (betaWhnfKey source before).2 := by - unfold betaWhnfKey - split - · exact .refl _ - · dsimp only - split <;> exact ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩ - -theorem instrument (before : TcState .anon) : BetaCacheFrame before (betaWhnfPrefix before) := by - unfold betaWhnfPrefix - split <;> exact ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩ - -theorem charge (before : TcState .anon) : BetaCacheFrame before (betaWhnfCharge before) := by - unfold betaWhnfCharge - split <;> exact ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩ - -end BetaCacheFrame - namespace BetaCacheExecution def writeNoDelta (key : Address × Address) (result : KExpr .anon) (before : TcState .anon) : TcState .anon := @@ -67,12 +26,12 @@ def writeFull (key : Address × Address) (result : KExpr .anon) (before : TcStat theorem writeNoDelta_frame (key : Address × Address) (result : KExpr .anon) (before : TcState .anon) : BetaCacheFrame before (writeNoDelta key result before) := by unfold writeNoDelta - split <;> exact ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩ + split <;> exact ⟨rfl, rfl, rfl, rfl, rfl, rfl, fun source => betaWhnfKey_congr source rfl rfl rfl rfl⟩ theorem writeFull_frame (key : Address × Address) (result : KExpr .anon) (before : TcState .anon) : BetaCacheFrame before (writeFull key result before) := by unfold writeFull - split <;> exact ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩ + split <;> exact ⟨rfl, rfl, rfl, rfl, rfl, rfl, fun source => betaWhnfKey_congr source rfl rfl rfl rfl⟩ theorem writeNoDelta_intern (key : Address × Address) (result : KExpr .anon) (before : TcState .anon) : (writeNoDelta key result before).env.intern = before.env.intern := by @@ -102,7 +61,7 @@ semantic typing assumption. -/ inductive BetaCoreExecution {β : Type u} (resolve : Address → Option (ConstRef β)) (locals : List FVarId) : Nat → TcState .anon → KExpr .anon → AExpr β → KExpr .anon → AExpr β → Type u | reduce {fuel before source term result target reduced steps} - (path : BetaWhnfTrace resolve locals fuel .FULL steps + (path : BetaWhnfTrace resolve locals (fuel + 1) .FULL steps (betaWhnfKey source before).2 source term reduced result target) (moving : 0 < steps) (enough : steps < maxWhnfCoreFuel.toNat) @@ -170,22 +129,11 @@ theorem frame (execution : BetaCoreExecution resolve locals fuel before source t BetaCacheFrame before execution.after := by cases execution with | reduce path moving enough miss terminal => - obtain ⟨table, reduced⟩ := path.frame - have keyed := BetaCacheFrame.key source before - simp only [after, reduced] - exact ⟨keyed.constants, keyed.full, keyed.only, keyed.context, keyed.policy, keyed.native⟩ + exact (BetaCacheFrame.key source before).trans (path.frame.trans (.core _ _ _)) | cached => exact .key source before theorem stable_key (execution : BetaCoreExecution resolve locals fuel before source term result target) : - (betaWhnfKey source execution.after).1 = (betaWhnfKey source before).1 := by - cases execution with - | reduce path moving enough miss terminal => - obtain ⟨table, reduced⟩ := path.frame - simp only [after, reduced] - apply Eq.trans (b := (betaWhnfKey source (betaWhnfKey source before).2).1) - · exact betaWhnfKey_congr source rfl rfl rfl rfl - · exact congrArg Prod.fst (betaWhnfKey_replay source before) - | cached => exact congrArg Prod.fst (betaWhnfKey_replay source before) + (betaWhnfKey source execution.after).1 = (betaWhnfKey source before).1 := execution.frame.keys source /-- Structural WHNF publishes on a miss even while native reduction is active; a hit retains the same entry. -/ diff --git a/Ix/Kernel/Verify/Consistency/BetaCacheKeys.lean b/Ix/Kernel/Verify/Consistency/BetaCacheKeys.lean index b9d25c9a2..d33a4b06a 100644 --- a/Ix/Kernel/Verify/Consistency/BetaCacheKeys.lean +++ b/Ix/Kernel/Verify/Consistency/BetaCacheKeys.lean @@ -3,7 +3,8 @@ Copyright (c) 2026 Argument Computer Corporation. SPDX-License-Identifier: MIT OR Apache-2.0 -/ -import Ix.Kernel.Verify.Consistency.BetaPublicWhnfPlan +import Ix.Kernel.Verify.Consistency.BetaWhnfState +import Ix.Kernel.Verify.Expr /-! WHNF key stability through beta reduction and cache publication. These facts concern the actual memoized key, including loose-variable contexts. -/ @@ -26,16 +27,19 @@ private theorem suffixNeed_congr {before after : TcState .anon} · rfl · exact suffixNeed_congr context values fuel _ +private theorem digest_congr {before after : TcState .anon} + (context : after.ctx = before.ctx) (values : after.letVals = before.letVals) + (identity : after.ctxId = before.ctxId) (radius : UInt64) : + TcM.ctxAddrForLbrUncached after radius = TcM.ctxAddrForLbrUncached before radius := by + unfold TcM.ctxAddrForLbrUncached + simp only [context, suffixNeed_congr context values, values, identity] + theorem betaWhnfKey_congr (source : KExpr .anon) {before after : TcState .anon} (context : after.ctx = before.ctx) (values : after.letVals = before.letVals) (identity : after.ctxId = before.ctxId) (memo : after.ctxAddrCache = before.ctxAddrCache) : (betaWhnfKey source after).1 = (betaWhnfKey source before).1 := by - have digest (radius : UInt64) : TcM.ctxAddrForLbrUncached after radius = - TcM.ctxAddrForLbrUncached before radius := by - unfold TcM.ctxAddrForLbrUncached - simp only [context, suffixNeed_congr context values, values, identity] unfold betaWhnfKey - simp only [context, identity, memo, digest] + simp only [context, identity, memo, digest_congr context values identity] by_cases fast : (source.lbr == 0 || before.ctx.isEmpty) = true · simp only [fast, if_true] · simp only [fast] @@ -50,6 +54,28 @@ theorem betaWhnfKey_replay (source : KExpr .anon) (before : TcState .anon) : · cases memo : before.ctxAddrCache[(before.ctxId, source.lbr)]? <;> simp [betaWhnfKey, fast, memo] +/-- Memoizing a head's context suffix preserves every surrounding WHNF key, +including a different loose-variable radius. Existing entries may be arbitrary. -/ +theorem betaWhnfKey_key (source query : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey query (betaWhnfKey source before).2).1 = (betaWhnfKey query before).1 := by + by_cases fast : (source.lbr == 0 || before.ctx.isEmpty) = true + · simp [betaWhnfKey, fast] + · cases memo : before.ctxAddrCache[(before.ctxId, source.lbr)]? with + | some cached => simp [betaWhnfKey, fast, memo] + | none => + by_cases radius : query.lbr = source.lbr + · simp [betaWhnfKey, fast, memo, radius] + · have different : (before.ctxId, query.lbr) ≠ (before.ctxId, source.lbr) := by + intro same + exact radius (congrArg Prod.snd same) + have digest := digest_congr (before := before) + (after := {before with ctxAddrCache := before.ctxAddrCache.insert (before.ctxId, source.lbr) (TcM.ctxAddrForLbrUncached before source.lbr)}) rfl rfl rfl query.lbr + by_cases queryFast : (query.lbr == 0 || before.ctx.isEmpty) = true + all_goals + cases queryMemo : before.ctxAddrCache[(before.ctxId, query.lbr)]? <;> + simp [betaWhnfKey, fast, memo, Std.HashMap.getElem?_insert, + Ne.symm different, queryFast, queryMemo, digest] + theorem betaWhnfKey_prefix (source : KExpr .anon) (before : TcState .anon) : (betaWhnfKey source (betaWhnfPrefix before)).1 = (betaWhnfKey source before).1 := by unfold betaWhnfPrefix @@ -60,4 +86,62 @@ theorem betaWhnfKey_charge (source : KExpr .anon) (before : TcState .anon) : unfold betaWhnfCharge split <;> exact betaWhnfKey_congr source rfl rfl rfl rfl +/-- State shared by surrounding inference is unchanged by these beta paths. +The reduction caches, intern table, key memoization, and WHNF counters +are tracked by the computed result state instead. -/ +structure BetaCacheFrame (before after : TcState .anon) : Prop where + constants : after.env.consts = before.env.consts + full : after.env.inferCache = before.env.inferCache + only : after.env.inferOnlyCache = before.env.inferOnlyCache + context : after.lctx = before.lctx + policy : after.inferOnly = before.inferOnly + native : after.inNativeReduce = before.inNativeReduce + keys (source : KExpr .anon) : (betaWhnfKey source after).1 = (betaWhnfKey source before).1 + +namespace BetaCacheFrame + +theorem refl (before : TcState .anon) : BetaCacheFrame before before := + ⟨rfl, rfl, rfl, rfl, rfl, rfl, fun _ => rfl⟩ + +theorem trans {before middle after : TcState .anon} + (first : BetaCacheFrame before middle) (second : BetaCacheFrame middle after) : + BetaCacheFrame before after := + ⟨second.constants.trans first.constants, second.full.trans first.full, + second.only.trans first.only, second.context.trans first.context, + second.policy.trans first.policy, second.native.trans first.native, + fun source => (second.keys source).trans (first.keys source)⟩ + +theorem key (source : KExpr .anon) (before : TcState .anon) : + BetaCacheFrame before (betaWhnfKey source before).2 := by + refine ⟨?_, ?_, ?_, ?_, ?_, ?_, fun query => betaWhnfKey_key source query before⟩ + all_goals + unfold betaWhnfKey + split + · rfl + · dsimp only; split <;> rfl + +theorem instrument (before : TcState .anon) : BetaCacheFrame before (betaWhnfPrefix before) := by + unfold betaWhnfPrefix + split <;> exact ⟨rfl, rfl, rfl, rfl, rfl, rfl, fun source => betaWhnfKey_congr source rfl rfl rfl rfl⟩ + +theorem charge (before : TcState .anon) : BetaCacheFrame before (betaWhnfCharge before) := by + unfold betaWhnfCharge + split <;> exact ⟨rfl, rfl, rfl, rfl, rfl, rfl, fun source => betaWhnfKey_congr source rfl rfl rfl rfl⟩ + +theorem intern (before : TcState .anon) (table : InternTable .anon) : + BetaCacheFrame before {before with env := {before.env with intern := table}} := + ⟨rfl, rfl, rfl, rfl, rfl, rfl, fun source => betaWhnfKey_congr source rfl rfl rfl rfl⟩ + +theorem core (before : TcState .anon) (key : Address × Address) (result : KExpr .anon) : + BetaCacheFrame before {before with env := {before.env with + whnfCoreCache := before.env.whnfCoreCache.insert key result}} := + ⟨rfl, rfl, rfl, rfl, rfl, rfl, fun source => betaWhnfKey_congr source rfl rfl rfl rfl⟩ + +theorem cheap (before : TcState .anon) (key : Address × Address) (result : KExpr .anon) : + BetaCacheFrame before {before with env := {before.env with + whnfCoreCheapCache := before.env.whnfCoreCheapCache.insert key result}} := + ⟨rfl, rfl, rfl, rfl, rfl, rfl, fun source => betaWhnfKey_congr source rfl rfl rfl rfl⟩ + +end BetaCacheFrame + end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaCoreCache.lean b/Ix/Kernel/Verify/Consistency/BetaCoreCache.lean new file mode 100644 index 000000000..5ca5f0d29 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaCoreCache.lean @@ -0,0 +1,138 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaCacheKeys + +/-! Structural WHNF's full and cheap cache partitions. Recursive head calls +use the original flags and publish even during native reduction. -/ + +namespace Ix.Kernel.Consistency.BetaCoreCache + +def lookup (flags : WhnfFlags) (key : Address × Address) (before : TcState .anon) : Option (KExpr .anon) := + if flags.isFull then before.env.whnfCoreCache[key]? else before.env.whnfCoreCheapCache[key]? + +def write (flags : WhnfFlags) (key : Address × Address) (result : KExpr .anon) (before : TcState .anon) : TcState .anon := + if flags.isFull then + {before with env := {before.env with whnfCoreCache := before.env.whnfCoreCache.insert key result}} + else + {before with env := {before.env with whnfCoreCheapCache := before.env.whnfCoreCheapCache.insert key result}} + +theorem frame (flags : WhnfFlags) (key : Address × Address) (result : KExpr .anon) (before : TcState .anon) : + BetaCacheFrame before (write flags key result before) := by + unfold write + split + · exact .core _ _ _ + · exact .cheap _ _ _ + +theorem intern (flags : WhnfFlags) (key : Address × Address) (result : KExpr .anon) (before : TcState .anon) : + (write flags key result before).env.intern = before.env.intern := by + unfold write + split <;> rfl + +theorem published (flags : WhnfFlags) (key : Address × Address) (result : KExpr .anon) (before : TcState .anon) : + lookup flags key (write flags key result before) = some result := by + unfold lookup write + split <;> simp only [Std.HashMap.getElem?_insert_self] + +theorem hit {methods : Methods .anon} {before : TcState .anon} {source result : KExpr .anon} + (flags : WhnfFlags) (entry : StructuralWhnfEntry source) + (found : lookup flags (betaWhnfKey source before).1 (betaWhnfKey source before).2 = some result) : + (RecM.whnfCoreWithFlags source flags).run methods before = + .ok result (betaWhnfKey source before).2 := by + rw [entry.core flags] + unfold RecM.whnfCoreWithFlagsNonLeaf + rw [ReaderT.run_bind] + change EStateM.bind (TcM.whnfKey source) _ before = _ + rw [EStateM.bind, betaWhnfKey_run] + simp only + rw [ReaderT.run_bind] + change EStateM.bind ((RecM.isTransientNatLiteralWork source).run methods) _ _ = _ + rw [EStateM.bind, entry.not_transient] + cases full : flags.isFull <;> simp only [Bool.false_eq_true, Bool.not_false, if_true, if_false] + all_goals + rw [ReaderT.run_bind] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ _ = _ + rw [EStateM.bind] + simp only [show (get : TcM .anon (TcState .anon)) (betaWhnfKey source before).2 = + .ok (betaWhnfKey source before).2 (betaWhnfKey source before).2 from rfl] + simp only [lookup, full, Bool.false_eq_true, if_true, if_false] at found + rw [found] + rfl + +theorem miss {methods : Methods .anon} {before after : TcState .anon} {source result : KExpr .anon} + (flags : WhnfFlags) (entry : StructuralWhnfEntry source) + (absent : lookup flags (betaWhnfKey source before).1 (betaWhnfKey source before).2 = none) + (reduced : (RecM.whnfCoreWithFlagsUncached source flags).run methods + (betaWhnfKey source before).2 = .ok result after) : + (RecM.whnfCoreWithFlags source flags).run methods before = + .ok result (write flags (betaWhnfKey source before).1 result after) := by + rw [entry.core flags] + unfold RecM.whnfCoreWithFlagsNonLeaf + rw [ReaderT.run_bind] + change EStateM.bind (TcM.whnfKey source) _ before = _ + rw [EStateM.bind, betaWhnfKey_run] + simp only + rw [ReaderT.run_bind] + change EStateM.bind ((RecM.isTransientNatLiteralWork source).run methods) _ _ = _ + rw [EStateM.bind, entry.not_transient] + cases full : flags.isFull <;> simp only [Bool.false_eq_true, Bool.not_false, if_true, if_false] + all_goals + rw [ReaderT.run_bind] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ _ = _ + rw [EStateM.bind] + simp only [show (get : TcM .anon (TcState .anon)) (betaWhnfKey source before).2 = + .ok (betaWhnfKey source before).2 (betaWhnfKey source before).2 from rfl] + simp only [lookup, full, Bool.false_eq_true, if_true, if_false] at absent + rw [absent] + simp only + rw [ReaderT.run_bind] + change EStateM.bind ((RecM.whnfCoreWithFlagsUncached source flags).run methods) _ _ = _ + rw [EStateM.bind, reduced] + simp only [write, full, Bool.false_eq_true, if_true, if_false] + rfl + +/-- A successful miss determines the uncached result and its state before +publication, for the full and cheap partitions alike. -/ +theorem miss_success {methods : Methods .anon} {before after : TcState .anon} {source result : KExpr .anon} + (flags : WhnfFlags) (entry : StructuralWhnfEntry source) + (absent : lookup flags (betaWhnfKey source before).1 (betaWhnfKey source before).2 = none) + (accepted : (RecM.whnfCoreWithFlags source flags).run methods before = .ok result after) : + ∃ reduced, (RecM.whnfCoreWithFlagsUncached source flags).run methods + (betaWhnfKey source before).2 = .ok result reduced ∧ + after = write flags (betaWhnfKey source before).1 result reduced := by + rw [entry.core flags] at accepted + unfold RecM.whnfCoreWithFlagsNonLeaf at accepted + rw [ReaderT.run_bind] at accepted + change EStateM.bind (TcM.whnfKey source) _ before = _ at accepted + rw [EStateM.bind, betaWhnfKey_run] at accepted + simp only at accepted + rw [ReaderT.run_bind] at accepted + change EStateM.bind ((RecM.isTransientNatLiteralWork source).run methods) _ _ = _ at accepted + rw [EStateM.bind, entry.not_transient] at accepted + cases full : flags.isFull <;> + simp only [full, Bool.false_eq_true, Bool.not_false, if_true, if_false] at accepted + all_goals + rw [ReaderT.run_bind] at accepted + change EStateM.bind (get : TcM .anon (TcState .anon)) _ _ = _ at accepted + rw [EStateM.bind] at accepted + simp only [show (get : TcM .anon (TcState .anon)) (betaWhnfKey source before).2 = + .ok (betaWhnfKey source before).2 (betaWhnfKey source before).2 from rfl] at accepted + simp only [lookup, full, Bool.false_eq_true, if_true, if_false] at absent + rw [absent] at accepted + simp only at accepted + rw [ReaderT.run_bind] at accepted + change EStateM.bind ((RecM.whnfCoreWithFlagsUncached source flags).run methods) _ _ = _ at accepted + cases raw : (RecM.whnfCoreWithFlagsUncached source flags).run methods (betaWhnfKey source before).2 with + | error error failed => simp only [EStateM.bind, raw] at accepted; cases accepted + | ok value reduced => + rw [EStateM.bind, raw] at accepted + have exactRun : EStateM.Result.ok (ε := TcError .anon) value (write flags (betaWhnfKey source before).1 value reduced) = + .ok result after := by + simp only [write, full, Bool.false_eq_true, if_true, if_false] + exact Eq.trans (by rfl) accepted + obtain ⟨rfl, afterEq⟩ := EStateM.Result.ok.inj exactRun + exact ⟨reduced, rfl, afterEq.symm⟩ + +end Ix.Kernel.Consistency.BetaCoreCache diff --git a/Ix/Kernel/Verify/Consistency/BetaExposureConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaExposureConstruction.lean index d88618e91..614ec2698 100644 --- a/Ix/Kernel/Verify/Consistency/BetaExposureConstruction.lean +++ b/Ix/Kernel/Verify/Consistency/BetaExposureConstruction.lean @@ -38,7 +38,7 @@ theorem BetaSortExposure.exists_of_success {β : Type u} {resolve : Address → {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source : KExpr .anon} {term : AExpr β} {level : KUniv .anon} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.PublicResources resolve locals before source term) + (resources : BetaWhnfSource.PublicResources resolve locals fuel before source term) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) (accepted : (RecM.ensureSortDirect source).run (methodsN (fuel + 1)) before = .ok level after) : @@ -56,7 +56,7 @@ theorem BetaPiExposure.exists_of_success {β : Type u} {resolve : Address → Op {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source rawDomain rawBody : KExpr .anon} {term : AExpr β} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.PublicResources resolve locals before source term) + (resources : BetaWhnfSource.PublicResources resolve locals fuel before source term) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) (accepted : (RecM.ensureForallDirect source).run (methodsN (fuel + 1)) before = .ok (rawDomain, rawBody) after) : diff --git a/Ix/Kernel/Verify/Consistency/BetaHeadConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaHeadConstruction.lean new file mode 100644 index 000000000..1d8c01c38 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaHeadConstruction.lean @@ -0,0 +1,96 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaHeadStepPlan +import Ix.Kernel.Verify.Consistency.BetaPrefixSource +import Ix.Kernel.Verify.Consistency.AppSpineSource +import Ix.Kernel.Verify.Consistency.LetWhnfPlan + +/-! Reconstruct a beta continuation after an explicit-let application head. +The returned lambda and state come from the actual recursive WHNF call. -/ + +namespace Ix.Kernel.Consistency.BetaHeadStepSource + +open Theory Theory.Model + +universe u + +def selected (source : KExpr .anon) : Bool := + match source with + | .app .. => LetStepSource.selected source.collectSpine.1 + | _ => false + +theorem selected_app {source : KExpr .anon} (chosen : selected source = true) : + ∃ fn arg info, source = .app fn arg info := by + cases source with + | app fn arg info => exact ⟨fn, arg, info, rfl⟩ + | _ => cases chosen + +theorem selected_head {source : KExpr .anon} (chosen : selected source = true) : + LetStepSource.selected source.collectSpine.1 = true := by + obtain ⟨_, _, _, rfl⟩ := selected_app chosen + exact chosen + +theorem selected_entry {source : KExpr .anon} (chosen : selected source = true) : StructuralWhnfEntry source := by + obtain ⟨_, _, _, rfl⟩ := selected_app chosen + exact .application (Prod.ext rfl rfl) (LetStepSource.selected_entry chosen) + +structure Witness {β : Type u} (resolve : Address → Option (ConstRef β)) (locals : List FVarId) + (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) + (head : KExpr .anon) (target : AExpr β) where + plan : BetaHeadStepPlan resolve locals before source term + sourceHead : plan.rawHead = source.collectSpine.1 + sourceTerm : plan.headTerm = (AppSpineSource.parts source term).1 + resultHead : plan.rawLambda = head + resultTerm : plan.modelLambda = target + result : plan.result = (BetaPrefixSource.output head source.collectSpine.2 before).1 + after : plan.after = BetaPrefixSource.after head source.collectSpine.2 before + +def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {before : TcState .anon} {source head : KExpr .anon} {term target : AExpr β} + (chosen : selected source = true) (reading : readScopedExpr? resolve locals source = some term.erase) + (returned : BetaPrefixSource.selected head = true) + (headReads : readScopedExpr? resolve locals head = some target.erase) + (resources : BetaPrefixSource.Resources head source.collectSpine.2 before) : + Witness resolve locals before source term head target := by + cases source with + | app fn arg info => + let parsed := AppSpineSource.reading reading + let built := BetaPrefixSource.construct returned headReads parsed.2.2 + (AppSpineSource.nonempty fn arg info) resources + exact ⟨{ + toBetaPrefixPlan := built.plan, + rawFunction := fn, rawArgument := arg, appInfo := info, sourceEq := rfl, + rawHead := (KExpr.app fn arg info).collectSpine.1, + headTerm := (AppSpineSource.parts (.app fn arg info) term).1, + modelSource := by rw [built.modelArgs]; exact parsed.1, + spine := Prod.ext rfl built.rawArgs.symm, + headEntry := LetStepSource.selected_entry chosen, sourceHeadReads := parsed.2.1 + }, rfl, rfl, built.raw, built.model, built.result, built.after⟩ + | _ => cases chosen + +/-- Successful application evaluation includes a successful head callback. +This also rules out method depth zero without an extra resource premise. -/ +theorem head_of_success {methods : Methods .anon} {flags : WhnfFlags} {before after : TcState .anon} + {fn arg result : KExpr .anon} {info : ExprInfo .anon} {loopFuel : Nat} + (accepted : (RecM.runBounded (fun current => RecM.whnfCoreWithFlagsStep current flags) + loopFuel (.app fn arg info)).run methods before = .ok result after) : + ∃ head middle, methods.whnfCoreFlags (KExpr.app fn arg info).collectSpine.1 flags before = .ok head middle := by + cases loopFuel with + | zero => cases accepted + | succ loopFuel => + rw [RecM.runBounded, ReaderT.run_bind] at accepted + change EStateM.bind ((RecM.whnfCoreWithFlagsStep (.app fn arg info) flags).run methods) _ before = _ at accepted + cases step : (RecM.whnfCoreWithFlagsStep (.app fn arg info) flags).run methods before with + | error error failed => rw [EStateM.bind, step] at accepted; cases accepted + | ok action middle => + unfold RecM.whnfCoreWithFlagsStep at step + rw [ReaderT.run_bind] at step + change EStateM.bind (methods.whnfCoreFlags (KExpr.app fn arg info).collectSpine.1 flags) _ before = _ at step + cases called : methods.whnfCoreFlags (KExpr.app fn arg info).collectSpine.1 flags before with + | error error failed => rw [EStateM.bind, called] at step; cases step + | ok head headAfter => exact ⟨head, headAfter, rfl⟩ + +end Ix.Kernel.Consistency.BetaHeadStepSource diff --git a/Ix/Kernel/Verify/Consistency/BetaHeadStepPlan.lean b/Ix/Kernel/Verify/Consistency/BetaHeadStepPlan.lean new file mode 100644 index 000000000..2a3f6f227 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaHeadStepPlan.lean @@ -0,0 +1,65 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaPrefixPlan +import Ix.Kernel.Verify.Consistency.StructuralWhnfEntry + +/-! An application whose recursive head callback returns a lambda. +The prefix starts in the callback's resulting state, including cache writes. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +structure BetaHeadStepPlan {β : Type u} (resolve : Address → Option (ConstRef β)) (locals : List FVarId) + (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) + extends BetaPrefixPlan resolve locals before where + rawFunction : KExpr .anon + rawArgument : KExpr .anon + appInfo : ExprInfo .anon + sourceEq : source = .app rawFunction rawArgument appInfo + rawHead : KExpr .anon + headTerm : AExpr β + modelSource : term = headTerm.appN arguments + spine : (KExpr.app rawFunction rawArgument appInfo).collectSpine = (rawHead, rawArguments) + headEntry : StructuralWhnfEntry rawHead + sourceHeadReads : readScopedExpr? resolve locals rawHead = some headTerm.erase + +namespace BetaHeadStepPlan + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {before : TcState .anon} {source : KExpr .anon} {term : AExpr β} + +def rawLambda (plan : BetaHeadStepPlan resolve locals before source term) := plan.toBetaPrefixPlan.rawLambda +def modelLambda (plan : BetaHeadStepPlan resolve locals before source term) := plan.toBetaPrefixPlan.modelLambda +def result (plan : BetaHeadStepPlan resolve locals before source term) := plan.toBetaPrefixPlan.result +def after (plan : BetaHeadStepPlan resolve locals before source term) := plan.toBetaPrefixPlan.after +def modelResult (plan : BetaHeadStepPlan resolve locals before source term) := plan.toBetaPrefixPlan.modelResult + +theorem entry (plan : BetaHeadStepPlan resolve locals before source term) : StructuralWhnfEntry source := by + rw [plan.sourceEq] + exact .application plan.spine plan.headEntry + +theorem sourceReading (plan : BetaHeadStepPlan resolve locals before source term) : + readScopedExpr? resolve locals source = some term.erase := by + simp only [plan.sourceEq, plan.modelSource] + exact readScopedExpr?_collectSpine plan.spine plan.sourceHeadReads plan.argumentReads + +theorem reading (plan : BetaHeadStepPlan resolve locals before source term) (coherent : before.env.intern.WF) : + readScopedExpr? resolve locals plan.result = some plan.modelResult.erase ∧ plan.after.env.intern.WF := + plan.toBetaPrefixPlan.reading coherent + +theorem run (plan : BetaHeadStepPlan resolve locals before source term) {methods : Methods .anon} + {initial : TcState .anon} {flags : WhnfFlags} + (headRun : methods.whnfCoreFlags plan.rawHead flags initial = .ok plan.rawLambda before) : + (RecM.whnfCoreWithFlagsStep source flags).run methods initial = .ok (.next plan.result) plan.after := by + simp only [plan.sourceEq] + exact plan.toBetaPrefixPlan.run plan.spine headRun + +end BetaHeadStepPlan + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaPrefixPlan.lean b/Ix/Kernel/Verify/Consistency/BetaPrefixPlan.lean new file mode 100644 index 000000000..dfdc8d6c4 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaPrefixPlan.lean @@ -0,0 +1,118 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.SpineReading + +/-! The common lambda-prefix operation after a WHNF head callback. +Production peeling, substitution, and suffix interning compute its result. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +structure BetaPrefixPlan {β : Type u} (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (before : TcState .anon) where + name : Mode.anon.F Name + bi : Mode.anon.F Lean.BinderInfo + rawDomain : KExpr .anon + rawInner : KExpr .anon + lambdaInfo : ExprInfo .anon + rawArguments : Array (KExpr .anon) + rawBody : KExpr .anon + consumed : Array (KExpr .anon) + condition : Certified.PropWhen + domain : AExpr β + inner : AExpr β + arguments : List (AExpr β) + headReads : readScopedExpr? resolve locals (.lam name bi rawDomain rawInner lambdaInfo) = + some (AExpr.lam condition domain inner).erase + argumentReads : rawArguments.toList.map (readScopedExpr? resolve locals ·) = arguments.map (some ·.erase) + peeling : RecM.consumeBetaLams (.lam name bi rawDomain rawInner lambdaInfo) rawArguments = (rawBody, consumed) + nonempty : (!consumed.isEmpty) = true + walkerBounds : SimulSubstBounds rawBody consumed.reverse 0 + walkerFaithful : KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ + KExpr.SimulSubstReach consumed.reverse rawBody 0 term + suffixFaithful : KExpr.CollisionFree fun term => + (simulSubst rawBody consumed.reverse 0 before.env.intern).2.ExprSupport term ∨ + term ∈ cheapBetaChainList (simulSubst rawBody consumed.reverse 0 before.env.intern).1 + (rawArguments.extract consumed.size rawArguments.size).toList + +namespace BetaPrefixPlan + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {before : TcState .anon} + +def rawLambda (plan : BetaPrefixPlan resolve locals before) : KExpr .anon := + .lam plan.name plan.bi plan.rawDomain plan.rawInner plan.lambdaInfo + +def modelLambda (plan : BetaPrefixPlan resolve locals before) : AExpr β := + .lam plan.condition plan.domain plan.inner + +def modelInput (plan : BetaPrefixPlan resolve locals before) : AExpr β := + plan.modelLambda.appN plan.arguments + +def output (plan : BetaPrefixPlan resolve locals before) : KExpr .anon × InternTable .anon := + let walk := simulSubst plan.rawBody plan.consumed.reverse 0 before.env.intern + internAppChain walk.1 (plan.rawArguments.extract plan.consumed.size plan.rawArguments.size).toList walk.2 + +def result (plan : BetaPrefixPlan resolve locals before) : KExpr .anon := plan.output.1 + +def after (plan : BetaPrefixPlan resolve locals before) : TcState .anon := + {before with env := {before.env with intern := plan.output.2}} + +def modelResult (plan : BetaPrefixPlan resolve locals before) : AExpr β := + AExpr.betaPrefix plan.consumed.size plan.modelLambda plan.arguments + +theorem counts (plan : BetaPrefixPlan resolve locals before) : + plan.consumed.size ≤ plan.inner.lambdaDepth + 1 ∧ plan.consumed.size ≤ plan.arguments.length := by + obtain ⟨peeled, _, rawBound⟩ := RecM.BetaPeel.of_consume plan.peeling + obtain ⟨_, modelPeel, _⟩ := betaPeel_readScopedExpr? peeled plan.headReads + have sizeAgrees : plan.rawArguments.size = plan.arguments.length := by + have lengths := congrArg List.length plan.argumentReads + simpa using lengths + exact ⟨by simpa only [Array.length_toList, AExpr.lambdaDepth] using modelPeel.length_bound, + sizeAgrees ▸ rawBound⟩ + +theorem reading (plan : BetaPrefixPlan resolve locals before) (coherent : before.env.intern.WF) : + readScopedExpr? resolve locals plan.result = some plan.modelResult.erase ∧ plan.after.env.intern.WF := by + obtain ⟨rawPeel, consumedPrefix, consumedBound⟩ := RecM.BetaPeel.of_consume plan.peeling + obtain ⟨body, modelPeel, bodyReads⟩ := betaPeel_readScopedExpr? rawPeel plan.headReads + have consumedSize : (plan.arguments.take plan.consumed.size).length = plan.consumed.size := by + simp only [List.length_take, Nat.min_eq_left plan.counts.2] + have consumedReads : plan.consumed.toList.map (readScopedExpr? resolve locals ·) = + (plan.arguments.take plan.consumed.size).map (some ·.erase) := by + rw [consumedPrefix, List.map_take, plan.argumentReads, List.map_take] + have suffixReads : (plan.rawArguments.extract plan.consumed.size plan.rawArguments.size).toList.map + (readScopedExpr? resolve locals ·) = (plan.arguments.drop plan.consumed.size).map (some ·.erase) := by + rw [RecM.BetaPeel.remaining_eq_drop plan.peeling, List.map_drop, plan.argumentReads, List.map_drop] + obtain ⟨walkReads, walkCoherent⟩ := simulSubst_readScopedExpr? + (by simpa only [Array.size_reverse] using consumedSize.symm) + plan.walkerBounds.1 plan.walkerBounds.2.1 (by simpa using plan.walkerBounds.2.2.2.1) + plan.walkerBounds.2.2.1 coherent plan.walkerFaithful + (by simpa only [Nat.zero_add, Array.length_toList, consumedSize] using bodyReads) + (argumentsReading_reverse_get consumedReads consumedSize.symm) + have peelingMeaning := LambdaPeel.betaPrefix (arguments := plan.arguments.take plan.consumed.size) + (by simpa only [consumedSize, Array.length_toList] using modelPeel) (plan.arguments.drop plan.consumed.size) + simp only [consumedSize, List.take_append_drop] at peelingMeaning + obtain ⟨reads, preserved⟩ := internAppChain_readScopedExpr? walkCoherent plan.suffixFaithful walkReads suffixReads + exact ⟨by simpa only [result, output, modelResult, modelLambda, peelingMeaning] using reads, preserved⟩ + +/-- This equation permits a recursively reduced head and retains its exact +post-call state before simultaneous substitution starts. -/ +theorem run (plan : BetaPrefixPlan resolve locals before) {methods : Methods .anon} + {initial : TcState .anon} {fn arg head : KExpr .anon} {info : ExprInfo .anon} {flags : WhnfFlags} + (spine : (KExpr.app fn arg info).collectSpine = (head, plan.rawArguments)) + (headRun : methods.whnfCoreFlags head flags initial = .ok plan.rawLambda before) : + (RecM.whnfCoreWithFlagsStep (.app fn arg info) flags).run methods initial = + .ok (.next plan.result) plan.after := by + apply RecM.whnfCoreWithFlagsStep_betaMany spine headRun plan.peeling plan.nonempty rfl + rw [RecM.finishAppResult_eq_internAppChain] + rfl + +end BetaPrefixPlan + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaPrefixSource.lean b/Ix/Kernel/Verify/Consistency/BetaPrefixSource.lean new file mode 100644 index 000000000..65e45514b --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaPrefixSource.lean @@ -0,0 +1,134 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaPrefixPlan + +/-! Construct the shared beta prefix from a returned raw lambda and the +original application arguments, using finite walker and interning resources. -/ + +namespace Ix.Kernel.Consistency.BetaPrefixSource + +open Theory Theory.Model + +universe u + +def selected : KExpr .anon → Bool + | .lam .. => true + | _ => false + +def peeled (head : KExpr .anon) (arguments : Array (KExpr .anon)) := + RecM.consumeBetaLams head arguments + +def substituted (head : KExpr .anon) (arguments : Array (KExpr .anon)) (before : TcState .anon) := + simulSubst (peeled head arguments).1 (peeled head arguments).2.reverse 0 before.env.intern + +def output (head : KExpr .anon) (arguments : Array (KExpr .anon)) (before : TcState .anon) := + let walk := substituted head arguments before + internAppChain walk.1 (arguments.extract (peeled head arguments).2.size arguments.size).toList walk.2 + +def after (head : KExpr .anon) (arguments : Array (KExpr .anon)) (before : TcState .anon) : TcState .anon := + {before with env := {before.env with intern := (output head arguments before).2}} + +structure Resources (head : KExpr .anon) (arguments : Array (KExpr .anon)) (before : TcState .anon) : Prop where + bounds : SimulSubstBounds (peeled head arguments).1 (peeled head arguments).2.reverse 0 + substitutionFaithful : KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ + KExpr.SimulSubstReach (peeled head arguments).2.reverse (peeled head arguments).1 0 term + suffixFaithful : KExpr.CollisionFree fun term => (substituted head arguments before).2.ExprSupport term ∨ + term ∈ cheapBetaChainList (substituted head arguments before).1 + (arguments.extract (peeled head arguments).2.size arguments.size).toList + +private theorem consumed_size (fuel : Nat) (current : KExpr .anon) + (arguments consumed : Array (KExpr .anon)) : + consumed.size ≤ (RecM.consumeBetaLamsFuel fuel current arguments consumed).2.size := by + induction fuel generalizing current consumed with + | zero => exact Nat.le_refl _ + | succ fuel ih => + unfold RecM.consumeBetaLamsFuel + split + · exact Nat.le_refl _ + · cases current with + | lam => exact Nat.le_trans (by simp) (ih _ _) + | _ => exact Nat.le_refl _ + +theorem consumed_nonempty {name bi rawDomain rawBody info} {arguments : Array (KExpr .anon)} + (nonempty : 0 < arguments.size) : + (!(RecM.consumeBetaLams (.lam name bi rawDomain rawBody info) arguments).2.isEmpty) = true := by + have size : arguments.size = (arguments.size - 1) + 1 := by omega + have positive : 0 < (RecM.consumeBetaLams (.lam name bi rawDomain rawBody info) arguments).2.size := by + unfold RecM.consumeBetaLams + rw [size, RecM.consumeBetaLamsFuel] + simp only [Array.mkEmpty, Array.size_empty, show ¬ 0 ≥ arguments.size from by omega, if_false] + have bound := consumed_size (arguments.size - 1) rawBody arguments + ((Array.mkEmpty arguments.size).push arguments[0]!) + exact Nat.lt_of_lt_of_le Nat.zero_lt_one (by + simpa only [Array.size_push, Array.mkEmpty, Array.size_empty, Nat.zero_add] using bound) + have notEmpty : (RecM.consumeBetaLams (.lam name bi rawDomain rawBody info) arguments).2.isEmpty = false := by + apply Bool.eq_false_iff.mpr + intro empty + have zero := Array.isEmpty_iff_size_eq_zero.mp empty + omega + simp only [notEmpty, Bool.not_false] + +private theorem lambda_shape {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {rawDomain rawBody : KExpr .anon} {info : ExprInfo .anon} {term : AExpr β} + (reading : readScopedExpr? resolve locals (.lam name bi rawDomain rawBody info) = some term.erase) : + ∃ condition domain body, term = .lam condition domain body := by + cases term with + | lam condition domain body => exact ⟨condition, domain, body, rfl⟩ + | _ => + cases hd : readScopedExpr? resolve locals rawDomain <;> + cases hb : readScopedExpr? resolve locals rawBody 1 <;> + simp [readScopedExpr?, hd, hb, AExpr.erase] at reading + +structure Witness {β : Type u} (resolve : Address → Option (ConstRef β)) (locals : List FVarId) + (before : TcState .anon) (head : KExpr .anon) (term : AExpr β) + (rawArguments : Array (KExpr .anon)) (arguments : List (AExpr β)) where + plan : BetaPrefixPlan resolve locals before + raw : plan.rawLambda = head + model : plan.modelLambda = term + rawArgs : plan.rawArguments = rawArguments + modelArgs : plan.arguments = arguments + output : plan.output = BetaPrefixSource.output head rawArguments before + +def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {before : TcState .anon} {head : KExpr .anon} {term : AExpr β} + {rawArguments : Array (KExpr .anon)} {arguments : List (AExpr β)} + (chosen : selected head = true) (reading : readScopedExpr? resolve locals head = some term.erase) + (argumentReads : rawArguments.toList.map (readScopedExpr? resolve locals ·) = arguments.map (some ·.erase)) + (supplied : 0 < rawArguments.size) (resources : Resources head rawArguments before) : + Witness resolve locals before head term rawArguments arguments := by + cases head with + | lam name bi rawDomain rawInner lambdaInfo => + have shape := lambda_shape reading + cases term with + | lam condition domain inner => + exact ⟨{ + name, bi, rawDomain, rawInner, lambdaInfo, rawArguments, + rawBody := (peeled (.lam name bi rawDomain rawInner lambdaInfo) rawArguments).1, + consumed := (peeled (.lam name bi rawDomain rawInner lambdaInfo) rawArguments).2, + condition, domain, inner, arguments, headReads := reading, argumentReads, + peeling := rfl, nonempty := consumed_nonempty supplied, + walkerBounds := resources.bounds, walkerFaithful := resources.substitutionFaithful, + suffixFaithful := resources.suffixFaithful + }, rfl, rfl, rfl, rfl, rfl⟩ + | _ => exfalso; obtain ⟨_, _, _, impossible⟩ := shape; cases impossible + | _ => cases chosen + +theorem Witness.result {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {before : TcState .anon} {head : KExpr .anon} {term : AExpr β} + {rawArguments : Array (KExpr .anon)} {arguments : List (AExpr β)} + (witness : Witness resolve locals before head term rawArguments arguments) : + witness.plan.result = (BetaPrefixSource.output head rawArguments before).1 := congrArg Prod.fst witness.output + +theorem Witness.after {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {before : TcState .anon} {head : KExpr .anon} {term : AExpr β} + {rawArguments : Array (KExpr .anon)} {arguments : List (AExpr β)} + (witness : Witness resolve locals before head term rawArguments arguments) : + witness.plan.after = BetaPrefixSource.after head rawArguments before := + congrArg (fun result : KExpr .anon × InternTable .anon => + {before with env := {before.env with intern := result.2}}) witness.output + +end Ix.Kernel.Consistency.BetaPrefixSource diff --git a/Ix/Kernel/Verify/Consistency/BetaPublicWhnfPlan.lean b/Ix/Kernel/Verify/Consistency/BetaPublicWhnfPlan.lean index 365ef0f77..b952cf8b7 100644 --- a/Ix/Kernel/Verify/Consistency/BetaPublicWhnfPlan.lean +++ b/Ix/Kernel/Verify/Consistency/BetaPublicWhnfPlan.lean @@ -15,210 +15,6 @@ open Theory Theory.Model universe u v -/-- Compute exactly the context-digest memoization performed by a WHNF key. -/ -def betaWhnfKey (source : KExpr .anon) (before : TcState .anon) : - (Address × Address) × TcState .anon := - if source.lbr == 0 || before.ctx.isEmpty then - ((source.addr, emptyCtxAddr), before) - else - let cacheKey := (before.ctxId, source.lbr) - match before.ctxAddrCache[cacheKey]? with - | some cached => ((source.addr, cached), before) - | none => - let digest := TcM.ctxAddrForLbrUncached before source.lbr - ((source.addr, digest), { before with ctxAddrCache := before.ctxAddrCache.insert cacheKey digest }) - -theorem betaWhnfKey_run (source : KExpr .anon) (before : TcState .anon) : - TcM.whnfKey source before = .ok (betaWhnfKey source before).1 (betaWhnfKey source before).2 := by - unfold TcM.whnfKey TcM.ctxAddrForLbr betaWhnfKey - change EStateM.bind (fun state => EStateM.bind (get : TcM .anon (TcState .anon)) _ state) _ before = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] - by_cases fast : (source.lbr == 0 || before.ctx.isEmpty) = true - · simp only [if_pos fast]; rfl - · simp only [if_neg fast] - cases before.ctxAddrCache[(before.ctxId, source.lbr)]? <;> rfl - -theorem betaWhnfKey_environment (source : KExpr .anon) (before : TcState .anon) : - (betaWhnfKey source before).2.env = before.env := by - unfold betaWhnfKey - split - · rfl - · dsimp only - split <;> rfl - -theorem betaWhnfKey_context (source : KExpr .anon) (before : TcState .anon) : - (betaWhnfKey source before).2.lctx = before.lctx := by - unfold betaWhnfKey - split - · rfl - · dsimp only - split <;> rfl - -theorem betaWhnfKey_native (source : KExpr .anon) (before : TcState .anon) : - (betaWhnfKey source before).2.inNativeReduce = before.inNativeReduce := by - unfold betaWhnfKey - split - · rfl - · dsimp only - split <;> rfl - -/-- Full WHNF's instrumentation changes only its optional call counter. -/ -def betaWhnfPrefix (before : TcState .anon) : TcState .anon := - if before.stats then { before with whnfCalls := before.whnfCalls + 1 } else before - -theorem betaWhnfPrefix_run (source : KExpr .anon) (before : TcState .anon) (methods : Methods .anon) : - (RecM.whnfWithNatSuccModePrefix source).run methods before = .ok () (betaWhnfPrefix before) := by - have traced : TcM.stepTrace "whnf+" (fun _ => TcM.addr8 source.addr) before = .ok () before := by - unfold TcM.stepTrace - change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] - split <;> rfl - unfold RecM.whnfWithNatSuccModePrefix - rw [ReaderT.run_bind] - change EStateM.bind (TcM.stepTrace "whnf+" (fun _ => TcM.addr8 source.addr)) _ before = _ - rw [EStateM.bind, traced] - change TcM.bumpStats (fun state => { state with whnfCalls := state.whnfCalls + 1 }) before = _ - unfold TcM.bumpStats betaWhnfPrefix - change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] - split <;> rfl - -/-- The miss charge performs one shared-fuel decrement and optionally -increments its miss counter. -/ -def betaWhnfCharge (before : TcState .anon) : TcState .anon := - if before.stats then - { before with whnfMisses := before.whnfMisses + 1, recFuel := before.recFuel - 1 } - else { before with recFuel := before.recFuel - 1 } - -theorem betaWhnfCharge_run (before : TcState .anon) (methods : Methods .anon) - (enough : (before.recFuel == 0) = false) : - (RecM.whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods before = - .ok () (betaWhnfCharge before) := by - unfold RecM.whnfWithNatSuccModeMissCharge - rw [ReaderT.run_bind] - change EStateM.bind (TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 })) - (fun _ => TcM.tick) before = _ - cases stats : before.stats - · have bumped : TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 }) before = - .ok () before := by - unfold TcM.bumpStats - change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] - rw [if_neg (by simp only [stats, Bool.false_eq_true, not_false_eq_true])]; rfl - rw [EStateM.bind, bumped] - change TcM.tick before = _ - unfold TcM.tick betaWhnfCharge - change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] - rw [enough, stats]; rfl - · have bumped : TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 }) before = - .ok () { before with whnfMisses := before.whnfMisses + 1 } := by - unfold TcM.bumpStats - change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] - rw [if_pos stats]; rfl - rw [EStateM.bind, bumped] - change TcM.tick { before with whnfMisses := before.whnfMisses + 1 } = _ - unfold TcM.tick betaWhnfCharge - change EStateM.bind (get : TcM .anon (TcState .anon)) _ { before with whnfMisses := before.whnfMisses + 1 } = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) - { before with whnfMisses := before.whnfMisses + 1 } = - .ok { before with whnfMisses := before.whnfMisses + 1 } - { before with whnfMisses := before.whnfMisses + 1 } from rfl] - rw [enough, stats]; rfl - -/-- Constructors on which both public drivers and all their reducer tails -stop without recursive callbacks or source lookup. -/ -inductive BetaWhnfTerminal : KExpr .anon → Prop - | sort (level : KUniv .anon) (info : ExprInfo .anon) : BetaWhnfTerminal (.sort level info) - | forallE (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) - (domain body : KExpr .anon) (info : ExprInfo .anon) : BetaWhnfTerminal (.all name bi domain body info) - | lam (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) - (domain body : KExpr .anon) (info : ExprInfo .anon) : BetaWhnfTerminal (.lam name bi domain body info) - -theorem BetaWhnfTerminal.noDelta_tail {source : KExpr .anon} (terminal : BetaWhnfTerminal source) - (methods : Methods .anon) (before : TcState .anon) (flags : WhnfFlags) (mode : NatSuccMode) : - (RecM.whnfNoDeltaReducersStep flags mode source).run methods before = .ok (.done source) before := by - cases terminal <;> - simp [RecM.whnfNoDeltaReducersStep, RecM.tryProjAppReduceFinished, RecM.tryProjAppReduce, - RecM.tryReduceBitvec, RecM.tryReduceNatWithSuccMode, RecM.tryReduceNative, - RecM.tryReduceString, RecM.tryReduceProjectionDefinition, RecM.tryQuotReduce, - KExpr.collectSpine, KExpr.collectSpine.go, RecM.prims, ReaderT.run_bind] - all_goals - change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] - cases before.noAccel <;> rfl - -theorem BetaWhnfTerminal.full_step {source result : KExpr .anon} - (terminal : BetaWhnfTerminal result) {methods : Methods .anon} {before after : TcState .anon} - (mode : NatSuccMode) (seen : Std.HashSet Address) - (reduced : (RecM.whnfNoDeltaImpl source .FULL mode).run methods before = .ok result after) : - (RecM.whnfWithNatSuccModeStep mode (source, seen)).run methods before = .ok (.done result) after := by - unfold RecM.whnfWithNatSuccModeStep - rw [ReaderT.run_bind] - change EStateM.bind ((RecM.whnfNoDeltaImpl source .FULL mode).run methods) _ before = _ - rw [EStateM.bind, reduced] - simp only - by_cases repeated : seen.contains result.addr = true - · rw [if_pos repeated]; rfl - · rw [if_neg repeated] - cases terminal <;> - simp [RecM.tryReduceNative, RecM.tryReduceBitvec, RecM.tryReduceNatWithSuccMode, - RecM.tryReduceDecidable, RecM.tryReduceString, RecM.tryNatOffsetStuck, - RecM.natOffsetStuckHead, RecM.deltaUnfoldOne, RecM.tryDeltaUnfold, - KExpr.collectSpine, KExpr.collectSpine.go, RecM.prims, ReaderT.run_bind] - all_goals - change EStateM.bind (get : TcM .anon (TcState .anon)) _ after = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) after = .ok after after from rfl] - cases native : after.noAccel - all_goals - change EStateM.bind (get : TcM .anon (TcState .anon)) _ after = _ - simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) after = .ok after after from rfl] - simp only [native] - rfl - -private theorem bounded_done {α γ : Type} (step : α → RecM .anon (RecM.BoundedStep α γ)) - {methods : Methods .anon} {before after : TcState .anon} {source : α} {result : γ} - (done : (step source).run methods before = .ok (.done result) after) - {fuel : Nat} (enough : 0 < fuel) : - (RecM.runBounded step fuel source).run methods before = .ok result after := by - cases fuel with - | zero => omega - | succ fuel => - rw [RecM.runBounded, ReaderT.run_bind] - change EStateM.bind ((step source).run methods) _ before = _ - rw [EStateM.bind, done] - rfl - -theorem BetaWhnfTerminal.noDelta_uncached {source result : KExpr .anon} - (terminal : BetaWhnfTerminal result) {methods : Methods .anon} {before after : TcState .anon} - (flags : WhnfFlags) (mode : NatSuccMode) - (reduced : (RecM.whnfCoreWithFlags source flags).run methods before = .ok result after) : - (RecM.whnfNoDeltaImplUncached source flags mode).run methods before = .ok result after := by - apply bounded_done (enough := by decide) - unfold RecM.whnfNoDeltaImplStep - rw [ReaderT.run_bind] - change EStateM.bind ((RecM.whnfCoreWithFlags source flags).run methods) _ before = _ - rw [EStateM.bind, reduced] - exact terminal.noDelta_tail methods after flags mode - -theorem BetaWhnfTerminal.full_uncached {source result : KExpr .anon} - (terminal : BetaWhnfTerminal result) {methods : Methods .anon} {before after : TcState .anon} - (mode : NatSuccMode) - (reduced : (RecM.whnfNoDeltaImpl source .FULL mode).run methods before = .ok result after) : - (RecM.whnfWithNatSuccModeUncached source mode).run methods before = .ok result after := - bounded_done _ (terminal.full_step mode {} reduced) (by decide) - -theorem BetaWhnfTrace.first {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} - {fuel steps : Nat} {flags : WhnfFlags} {before after : TcState .anon} - {source result : KExpr .anon} {term target : AExpr β} - (trace : BetaWhnfTrace resolve locals fuel flags steps before source term after result target) : - 0 < steps → StructuralWhnfEntry source := - match trace with - | .done _ => fun impossible => False.elim (Nat.not_lt_zero _ impossible) - | .next plan _ => fun _ => plan.entry - | .zeta plan _ => fun _ => plan.entry - theorem BetaStepPlan.not_transient {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {before : TcState .anon} {source : KExpr .anon} {term : AExpr β} (plan : BetaStepPlan resolve locals before source term) (methods : Methods .anon) (state : TcState .anon) : @@ -227,29 +23,6 @@ theorem BetaStepPlan.not_transient {β : Type u} {resolve : Address → Option ( plan.spine, pure_bind] rfl -theorem betaWhnfKey_fuel (source : KExpr .anon) (before : TcState .anon) : - (betaWhnfKey source before).2.recFuel = before.recFuel := by - unfold betaWhnfKey - split - · rfl - · dsimp only - split <;> rfl - -theorem betaWhnfPrefix_fields (before : TcState .anon) : - (betaWhnfPrefix before).env = before.env ∧ - (betaWhnfPrefix before).lctx = before.lctx ∧ - (betaWhnfPrefix before).inNativeReduce = before.inNativeReduce ∧ - (betaWhnfPrefix before).recFuel = before.recFuel := by - unfold betaWhnfPrefix - split <;> exact ⟨rfl, rfl, rfl, rfl⟩ - -theorem betaWhnfCharge_fields (before : TcState .anon) : - (betaWhnfCharge before).env = before.env ∧ - (betaWhnfCharge before).lctx = before.lctx ∧ - (betaWhnfCharge before).inNativeReduce = before.inNativeReduce := by - unfold betaWhnfCharge - split <;> exact ⟨rfl, rfl, rfl⟩ - namespace BetaPublicWhnf def outerKey (source : KExpr .anon) (before : TcState .anon) := @@ -292,7 +65,7 @@ structure BetaPublicWhnfPlan {β : Type u} (resolve : Address → Option (ConstR (source : KExpr .anon) (term : AExpr β) (result : KExpr .anon) (target : AExpr β) where steps : Nat reduced : TcState .anon - path : BetaWhnfTrace resolve locals fuel .FULL steps + path : BetaWhnfTrace resolve locals (fuel + 1) .FULL steps (BetaPublicWhnf.coreKey source before).2 source term reduced result target moving : 0 < steps enough : steps < maxWhnfCoreFuel.toNat @@ -326,8 +99,7 @@ theorem run (plan : BetaPublicWhnfPlan resolve locals fuel before source term re exact RecM.whnfCoreWithFlagsNonLeaf_fullMiss rfl (betaWhnfKey_run source _) (first.not_transient _ _) plan.coreMiss (plan.path.run plan.enough) have reducedNative : plan.reduced.inNativeReduce = false := by - obtain ⟨table, frame⟩ := plan.path.frame - exact (congrArg TcState.inNativeReduce frame).trans + exact plan.path.frame.native.trans ((BetaPublicWhnf.coreKey_fields source before).2.2.trans plan.native) have noDeltaEntry : RecM.whnfNoDeltaImpl source .FULL .collapse = RecM.whnfNoDeltaImplNonLeaf source .FULL .collapse := first.noDelta .FULL .collapse @@ -354,8 +126,7 @@ theorem reading (plan : BetaPublicWhnfPlan resolve locals fuel before source ter theorem context (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : plan.after.lctx = before.lctx := by - obtain ⟨table, frame⟩ := plan.path.frame - exact (congrArg TcState.lctx frame).trans (BetaPublicWhnf.coreKey_fields source before).2.1 + exact plan.path.frame.context.trans (BetaPublicWhnf.coreKey_fields source before).2.1 theorem cache_hit (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) (methods : Methods .anon) (current : TcState .anon) diff --git a/Ix/Kernel/Verify/Consistency/BetaSourceInference.lean b/Ix/Kernel/Verify/Consistency/BetaSourceInference.lean index 5c6497683..f604d3f96 100644 --- a/Ix/Kernel/Verify/Consistency/BetaSourceInference.lean +++ b/Ix/Kernel/Verify/Consistency/BetaSourceInference.lean @@ -26,7 +26,7 @@ theorem SynthesisInference.beta_whnf_of_success {β : Type u} {resolve : Address (reading : readScopedExpr? resolve locals source = some term.erase) (inferredRun : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) {reductionFuel : Nat} {flags : WhnfFlags} {before after : TcState .anon} {result : KExpr .anon} - (resources : BetaWhnfSource.Resources maxWhnfCoreFuel.toNat before source) + (resources : BetaWhnfSource.Resources (reductionFuel + 1) flags maxWhnfCoreFuel.toNat before source) (coherent : before.env.intern.WF) (accepted : (RecM.whnfCoreWithFlagsUncached source flags).run (methodsN (reductionFuel + 1)) before = .ok result after) : @@ -48,7 +48,7 @@ theorem SynthesisInference.beta_core_of_success {β : Type u} {resolve : Address (inferredRun : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) {reductionFuel : Nat} {before after : TcState .anon} {result : KExpr .anon} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.CoreResources resolve locals before source term) + (resources : BetaWhnfSource.CoreResources resolve locals reductionFuel before source term) (coherent : before.env.intern.WF) (accepted : (RecM.whnfCore source).run (methodsN (reductionFuel + 1)) before = .ok result after) : ∃ target, BetaWhnfTerminal result ∧ readScopedExpr? resolve locals result = some target.erase ∧ @@ -70,7 +70,7 @@ theorem SynthesisInference.beta_noDelta_of_success {β : Type u} {resolve : Addr (inferredRun : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) {reductionFuel : Nat} {before after : TcState .anon} {result : KExpr .anon} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.NoDeltaResources resolve locals before source term) + (resources : BetaWhnfSource.NoDeltaResources resolve locals reductionFuel before source term) (coherent : before.env.intern.WF) (accepted : (RecM.whnfNoDelta source).run (methodsN (reductionFuel + 1)) before = .ok result after) : ∃ target, BetaWhnfTerminal result ∧ readScopedExpr? resolve locals result = some target.erase ∧ @@ -92,7 +92,7 @@ theorem SynthesisInference.beta_public_of_success {β : Type u} {resolve : Addre (inferredRun : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) {reductionFuel : Nat} {before after : TcState .anon} {result : KExpr .anon} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.PublicResources resolve locals before source term) + (resources : BetaWhnfSource.PublicResources resolve locals reductionFuel before source term) (coherent : before.env.intern.WF) (accepted : (RecM.whnf source).run (methodsN (reductionFuel + 1)) before = .ok result after) : ∃ target, BetaWhnfTerminal result ∧ readScopedExpr? resolve locals result = some target.erase ∧ diff --git a/Ix/Kernel/Verify/Consistency/BetaStepConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaStepConstruction.lean index fa0ae48c1..627a01a93 100644 --- a/Ix/Kernel/Verify/Consistency/BetaStepConstruction.lean +++ b/Ix/Kernel/Verify/Consistency/BetaStepConstruction.lean @@ -4,6 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.BetaWhnfPlan +import Ix.Kernel.Verify.Consistency.BetaPrefixSource /-! Construct beta-step witnesses from the source expression. The caller supplies only its reading and finite arithmetic and interning resources. -/ @@ -119,38 +120,6 @@ private theorem modelSpine_lambda {β : Type u} {resolve : Address → Option (C simp [readScopedExpr?, hd, hb, AExpr.erase] at reading | _ => obtain ⟨_, _, _, _, _, impossible⟩ := headLambda; cases impossible -private theorem consumed_size (fuel : Nat) (current : KExpr .anon) - (arguments consumed : Array (KExpr .anon)) : - consumed.size ≤ (RecM.consumeBetaLamsFuel fuel current arguments consumed).2.size := by - induction fuel generalizing current consumed with - | zero => exact Nat.le_refl _ - | succ fuel ih => - unfold RecM.consumeBetaLamsFuel - split - · exact Nat.le_refl _ - · cases current with - | lam => exact Nat.le_trans (by simp) (ih _ _) - | _ => exact Nat.le_refl _ - -private theorem consumed_nonempty {name bi rawDomain rawBody info} {arguments : Array (KExpr .anon)} - (nonempty : 0 < arguments.size) : - (!(RecM.consumeBetaLams (.lam name bi rawDomain rawBody info) arguments).2.isEmpty) = true := by - have size : arguments.size = (arguments.size - 1) + 1 := by omega - have positive : 0 < (RecM.consumeBetaLams (.lam name bi rawDomain rawBody info) arguments).2.size := by - unfold RecM.consumeBetaLams - rw [size, RecM.consumeBetaLamsFuel] - simp only [Array.mkEmpty, Array.size_empty, show ¬ 0 ≥ arguments.size from by omega, if_false] - have bound := consumed_size (arguments.size - 1) rawBody arguments - ((Array.mkEmpty arguments.size).push arguments[0]!) - exact Nat.lt_of_lt_of_le Nat.zero_lt_one (by - simpa only [Array.size_push, Array.mkEmpty, Array.size_empty, Nat.zero_add] using bound) - have notEmpty : (RecM.consumeBetaLams (.lam name bi rawDomain rawBody info) arguments).2.isEmpty = false := by - apply Bool.eq_false_iff.mpr - intro empty - have zero := Array.isEmpty_iff_size_eq_zero.mp empty - omega - simp only [notEmpty, Bool.not_false] - /-- The source reading determines all annotated spine components. Actual collection and peeling determine every raw component and consumed argument. -/ def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} @@ -190,7 +159,7 @@ def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals condition, domain, inner, arguments := (modelSpine term).2, modelSource, spine := Prod.ext rawHead rfl, headReads := rawHead ▸ reads.1, argumentReads := reads.2, peeling := by simp only [peeled, rawHead], - nonempty := by simpa only [peeled, rawHead] using consumed_nonempty nonempty, + nonempty := by simpa only [peeled, rawHead] using BetaPrefixSource.consumed_nonempty nonempty, walkerBounds := resources.bounds, walkerFaithful := resources.substitutionFaithful, suffixFaithful := resources.suffixFaithful }, ?_⟩ diff --git a/Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean index c8519c1cc..3ddf7da27 100644 --- a/Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean +++ b/Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean @@ -4,6 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.BetaStepConstruction +import Ix.Kernel.Verify.Consistency.BetaHeadConstruction import Ix.Kernel.Verify.Consistency.BetaCacheExecution import Ix.Kernel.Verify.Whnf.Driver.Success @@ -25,37 +26,56 @@ theorem BetaWhnfTerminal.core_step {source : KExpr .anon} (terminal : BetaWhnfTe namespace BetaWhnfSource def selected (source : KExpr .anon) : Bool := - BetaStepSource.selected source || LetStepSource.selected source + BetaStepSource.selected source || LetStepSource.selected source || BetaHeadStepSource.selected source theorem selected_entry {source : KExpr .anon} (chosen : selected source = true) : StructuralWhnfEntry source := by by_cases beta : BetaStepSource.selected source = true · exact BetaStepSource.selected_entry beta - · exact LetStepSource.selected_entry (by simpa [selected, beta] using chosen) - -/-- At each computed beta or let step, retain only finite walker and hash bounds. -Any other branch must be one of the supported terminal constructors. The -zero-fuel case requires no successful result; success is supplied separately -by the actual production run. -/ -def Resources : Nat → TcState .anon → KExpr .anon → Prop - | 0, _, _ => True - | fuel + 1, before, source => + · by_cases zeta : LetStepSource.selected source = true + · exact LetStepSource.selected_entry zeta + · exact BetaHeadStepSource.selected_entry (by simpa [selected, beta, zeta] using chosen) + +/-- Raw resources follow actual method depth and loop fuel. A recursive head +miss gets its own bounded orbit; only successful callbacks require a lambda +and finite resources for its beta continuation. No annotations or intermediate +execution equations are supplied. -/ +def Resources (reductionFuel : Nat) (flags : WhnfFlags) (loopFuel : Nat) + (before : TcState .anon) (source : KExpr .anon) : Prop := + match loopFuel with + | 0 => True + | loopFuel + 1 => if BetaStepSource.selected source then BetaStepSource.Resources source before ∧ - Resources fuel (BetaStepSource.after source before) (BetaStepSource.output source before).1 + Resources reductionFuel flags loopFuel (BetaStepSource.after source before) (BetaStepSource.output source before).1 else if LetStepSource.selected source then LetStepSource.Resources source before ∧ - Resources fuel (LetStepSource.after source before) (LetStepSource.output source before).1 + Resources reductionFuel flags loopFuel (LetStepSource.after source before) (LetStepSource.output source before).1 + else if BetaHeadStepSource.selected source then + match reductionFuel with + | 0 => True + | fuel + 1 => + BetaCoreCache.lookup flags (betaWhnfKey source.collectSpine.1 before).1 + (betaWhnfKey source.collectSpine.1 before).2 = none ∧ + Resources fuel flags maxWhnfCoreFuel.toNat (betaWhnfKey source.collectSpine.1 before).2 source.collectSpine.1 ∧ + ∀ head middle, + (RecM.whnfCoreWithFlags source.collectSpine.1 flags).run (methodsN fuel) before = .ok head middle → + BetaPrefixSource.selected head = true ∧ + BetaPrefixSource.Resources head source.collectSpine.2 middle ∧ + Resources (fuel + 1) flags loopFuel (BetaPrefixSource.after head source.collectSpine.2 middle) + (BetaPrefixSource.output head source.collectSpine.2 middle).1 else BetaWhnfTerminal source +termination_by (reductionFuel, loopFuel) -theorem Resources.first {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} - (resources : Resources fuel before source) (positive : 0 < fuel) +theorem Resources.first {reductionFuel fuel : Nat} {flags : WhnfFlags} {before : TcState .anon} {source : KExpr .anon} + (resources : Resources reductionFuel flags fuel before source) (positive : 0 < fuel) (chosen : BetaStepSource.selected source = true) : BetaStepSource.Resources source before := by cases fuel with | zero => omega | succ fuel => + rw [Resources.eq_def] at resources exact (show BetaStepSource.Resources source before ∧ - Resources fuel (BetaStepSource.after source before) (BetaStepSource.output source before).1 from by - simpa only [Resources, chosen, if_true] using resources).1 + Resources reductionFuel flags fuel (BetaStepSource.after source before) (BetaStepSource.output source before).1 from by + simpa only [chosen, if_true] using resources).1 /-- The annotation and iteration count are outputs of reconstruction. The trace's endpoints are the actual result and state of the bounded run. -/ @@ -69,57 +89,133 @@ structure Witness {β : Type u} (resolve : Address → Option (ConstRef β)) (lo terminal : BetaWhnfTerminal result moving : selected source = true → 0 < steps -/-- Successful execution determines the complete raw trace. No intermediate -readings, model expressions, step equations, or iteration count are inputs. -/ +/-- Successful execution determines both nested head calls and the outer +path. Each recursive callback uses the predecessor method table. -/ def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {reductionFuel loopFuel : Nat} {flags : WhnfFlags} {before after : TcState .anon} {source result : KExpr .anon} {term : AExpr β} - (resources : Resources loopFuel before source) + (resources : Resources reductionFuel flags loopFuel before source) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) (accepted : (RecM.runBounded (fun current => RecM.whnfCoreWithFlagsStep current flags) - loopFuel source).run (methodsN (reductionFuel + 1)) before = .ok result after) : + loopFuel source).run (methodsN reductionFuel) before = .ok result after) : Witness resolve locals reductionFuel loopFuel flags before source term result after := by - induction loopFuel generalizing before source term with + cases loopFuel with | zero => cases accepted - | succ loopFuel ih => + | succ loopFuel => + rw [Resources.eq_def] at resources by_cases chosen : BetaStepSource.selected source = true - · have data : BetaStepSource.Resources source before ∧ - Resources loopFuel (BetaStepSource.after source before) (BetaStepSource.output source before).1 := by - simpa only [Resources, chosen, if_true] using resources - let first := BetaStepSource.construct chosen reading data.1 - have nextResources : Resources loopFuel first.1.after first.1.result := by - simpa only [first, BetaStepSource.construct_after, BetaStepSource.construct_result] using data.2 - obtain ⟨nextReading, nextCoherent⟩ := first.1.reading coherent - rw [RecM.runBounded, ReaderT.run_bind] at accepted - change EStateM.bind ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before = _ at accepted - rw [EStateM.bind, first.1.run reductionFuel flags] at accepted - let rest := ih nextResources nextReading nextCoherent accepted - exact ⟨rest.steps + 1, rest.target, .next first.1 rest.trace, - Nat.add_lt_add_right rest.enough 1, rest.terminal, fun _ => Nat.zero_lt_succ _⟩ + · cases reductionFuel with + | zero => + exfalso + obtain ⟨fn, arg, info, rfl⟩ := BetaStepSource.selected_app chosen + obtain ⟨_, _, impossible⟩ := BetaHeadStepSource.head_of_success accepted + cases impossible + | succ fuel => + have data : BetaStepSource.Resources source before ∧ + Resources (fuel + 1) flags loopFuel (BetaStepSource.after source before) (BetaStepSource.output source before).1 := by + simpa only [chosen, if_true] using resources + let first := BetaStepSource.construct chosen reading data.1 + have nextResources : Resources (fuel + 1) flags loopFuel first.1.after first.1.result := by + simpa only [first, BetaStepSource.construct_after, BetaStepSource.construct_result] using data.2 + obtain ⟨nextReading, nextCoherent⟩ := first.1.reading coherent + rw [RecM.runBounded, ReaderT.run_bind] at accepted + change EStateM.bind ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before = _ at accepted + rw [EStateM.bind, first.1.run fuel flags] at accepted + let rest := construct nextResources nextReading nextCoherent accepted + exact ⟨rest.steps + 1, rest.target, .next first.1 rest.trace, + Nat.add_lt_add_right rest.enough 1, rest.terminal, fun _ => Nat.zero_lt_succ _⟩ · by_cases zeta : LetStepSource.selected source = true · have data : LetStepSource.Resources source before ∧ - Resources loopFuel (LetStepSource.after source before) (LetStepSource.output source before).1 := by - simpa [Resources, chosen, zeta] using resources + Resources reductionFuel flags loopFuel (LetStepSource.after source before) (LetStepSource.output source before).1 := by + simpa [chosen, zeta] using resources let first := LetStepSource.construct zeta data.1 - have nextResources : Resources loopFuel first.1.after first.1.result := by + have nextResources : Resources reductionFuel flags loopFuel first.1.after first.1.result := by simpa only [first, LetStepSource.construct_after, LetStepSource.construct_result] using data.2 obtain ⟨nextReading, nextCoherent⟩ := first.1.reading reading coherent rw [RecM.runBounded, ReaderT.run_bind] at accepted change EStateM.bind ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before = _ at accepted - rw [EStateM.bind, first.1.run (methodsN (reductionFuel + 1)) flags] at accepted - let rest := ih nextResources nextReading nextCoherent accepted + rw [EStateM.bind, first.1.run (methodsN reductionFuel) flags] at accepted + let rest := construct nextResources nextReading nextCoherent accepted exact ⟨rest.steps + 1, rest.target, .zeta first.1 rest.trace, Nat.add_lt_add_right rest.enough 1, rest.terminal, fun _ => Nat.zero_lt_succ _⟩ - · have terminal : BetaWhnfTerminal source := by - simpa [Resources, chosen, zeta] using resources - have finished := terminal.core_step (methodsN (reductionFuel + 1)) before flags - rw [RecM.runBounded, ReaderT.run_bind] at accepted - change EStateM.bind ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before = _ at accepted - rw [EStateM.bind, finished] at accepted - obtain ⟨rfl, rfl⟩ := EStateM.Result.ok.inj accepted - exact ⟨0, term, .done finished, Nat.zero_lt_succ _, terminal, - fun impossible => by simp [selected, chosen, zeta] at impossible⟩ + · by_cases headed : BetaHeadStepSource.selected source = true + · cases reductionFuel with + | zero => + exfalso + obtain ⟨fn, arg, info, rfl⟩ := BetaHeadStepSource.selected_app headed + obtain ⟨_, _, impossible⟩ := BetaHeadStepSource.head_of_success accepted + cases impossible + | succ fuel => + have data : + BetaCoreCache.lookup flags (betaWhnfKey source.collectSpine.1 before).1 + (betaWhnfKey source.collectSpine.1 before).2 = none ∧ + Resources fuel flags maxWhnfCoreFuel.toNat (betaWhnfKey source.collectSpine.1 before).2 source.collectSpine.1 ∧ + ∀ head middle, + (RecM.whnfCoreWithFlags source.collectSpine.1 flags).run (methodsN fuel) before = .ok head middle → + BetaPrefixSource.selected head = true ∧ + BetaPrefixSource.Resources head source.collectSpine.2 middle ∧ + Resources (fuel + 1) flags loopFuel (BetaPrefixSource.after head source.collectSpine.2 middle) + (BetaPrefixSource.output head source.collectSpine.2 middle).1 := by + simpa [chosen, zeta, headed] using resources + have headChosen := BetaHeadStepSource.selected_head headed + have headEntry := LetStepSource.selected_entry headChosen + have parsed := AppSpineSource.reading reading + have headCoherent : (betaWhnfKey source.collectSpine.1 before).2.env.intern.WF := + (betaWhnfKey_environment _ _).symm ▸ coherent + cases called : (RecM.whnfCoreWithFlags source.collectSpine.1 flags).run (methodsN fuel) before with + | error error failed => + exfalso + obtain ⟨fn, arg, info, rfl⟩ := BetaHeadStepSource.selected_app headed + obtain ⟨_, _, success⟩ := BetaHeadStepSource.head_of_success accepted + change (RecM.whnfCoreWithFlags (KExpr.app fn arg info).collectSpine.1 flags).run + (methodsN fuel) before = _ at success + rw [called] at success + cases success + | ok head middle => + have tailData := data.2.2 head middle called + cases rawRun : (RecM.whnfCoreWithFlagsUncached source.collectSpine.1 flags).run + (methodsN fuel) (betaWhnfKey source.collectSpine.1 before).2 with + | error error failed => + exfalso + obtain ⟨_, success, _⟩ := BetaCoreCache.miss_success flags headEntry data.1 called + rw [rawRun] at success + cases success + | ok returned rawAfter => + let headPath := construct data.2.1 parsed.2.1 headCoherent rawRun + have moving : selected source.collectSpine.1 = true := by + simp [selected, headChosen] + let call := BetaHeadReduction.reduce headPath.trace (headPath.moving moving) headPath.enough data.1 + have same := call.run + rw [called] at same + obtain ⟨rfl, rfl⟩ := EStateM.Result.ok.inj same + have headReading := (call.reading parsed.2.1 coherent).1 + let built := BetaHeadStepSource.construct headed reading tailData.1 headReading tailData.2.1 + have aligned : BetaHeadReduction resolve locals fuel flags before built.plan.rawHead built.plan.headTerm + (BetaCoreCache.write flags (betaWhnfKey source.collectSpine.1 before).1 head rawAfter) + built.plan.rawLambda built.plan.modelLambda := by + rw [built.sourceHead, built.sourceTerm, built.resultHead, built.resultTerm] + exact call + have nextResources : Resources (fuel + 1) flags loopFuel built.plan.after built.plan.result := by + simpa only [built.after, built.result] using tailData.2.2 + obtain ⟨nextReading, nextCoherent⟩ := built.plan.reading (call.reading parsed.2.1 coherent).2 + rw [RecM.runBounded, ReaderT.run_bind] at accepted + change EStateM.bind ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before = _ at accepted + rw [EStateM.bind, built.plan.run aligned.run] at accepted + let rest := construct nextResources nextReading nextCoherent accepted + exact ⟨rest.steps + 1, rest.target, .head built.plan aligned rest.trace, + Nat.add_lt_add_right rest.enough 1, rest.terminal, fun _ => Nat.zero_lt_succ _⟩ + · have terminal : BetaWhnfTerminal source := by + simpa [chosen, zeta, headed] using resources + have finished := terminal.core_step (methodsN reductionFuel) before flags + rw [RecM.runBounded, ReaderT.run_bind] at accepted + change EStateM.bind ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before = _ at accepted + rw [EStateM.bind, finished] at accepted + obtain ⟨rfl, rfl⟩ := EStateM.Result.ok.inj accepted + exact ⟨0, term, .done finished, Nat.zero_lt_succ _, terminal, + fun impossible => by simp [selected, chosen, zeta, headed] at impossible⟩ +termination_by (reductionFuel, loopFuel) +decreasing_by all_goals simp_wf; omega end BetaWhnfSource @@ -129,7 +225,7 @@ theorem BetaCoreExecution.exists_of_miss_success {β : Type u} {resolve : Addres {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} {term : AExpr β} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.Resources maxWhnfCoreFuel.toNat (betaWhnfKey source before).2 source) + (resources : BetaWhnfSource.Resources (fuel + 1) .FULL maxWhnfCoreFuel.toNat (betaWhnfKey source before).2 source) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) (miss : (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? = none) diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnf.lean b/Ix/Kernel/Verify/Consistency/BetaWhnf.lean index 2dfd47262..c867fe9c5 100644 --- a/Ix/Kernel/Verify/Consistency/BetaWhnf.lean +++ b/Ix/Kernel/Verify/Consistency/BetaWhnf.lean @@ -63,30 +63,37 @@ theorem reading end SynthesisBetaStep -/-- A complete finite beta path, including explicit lets, through structural WHNF. The final -iteration returns its input; every preceding iteration has a computed -substitution result and intern table, with no postulated intermediate check. -/ +/-- A finite structural path annotated from the original source check. +Recursive head calls retain their real cache execution and derived conversion. -/ inductive SynthesisBetaWhnfTrace {β : Type u} (resolve : Address → Option (ConstRef β)) (incoming : Model.Environment β) (incomingContext : Model.Context β) (incomingBounds : List VLevel) - (entries : Model.Environment β) (context : Model.Context β) (locals : List FVarId) - (reductionFuel : Nat) (flags : WhnfFlags) : - Nat → TcState .anon → KExpr .anon → AExpr β → TcState .anon → KExpr .anon → AExpr β → Type u - | done {before source term} - (finished : (RecM.whnfCoreWithFlagsStep source flags).run (methodsN (reductionFuel + 1)) before = + (entries : Model.Environment β) (context : Model.Context β) (locals : List FVarId) : + Nat → WhnfFlags → Nat → TcState .anon → KExpr .anon → AExpr β → TcState .anon → KExpr .anon → AExpr β → Type u + | done {fuel flags before source term} + (finished : (RecM.whnfCoreWithFlagsStep source flags).run (methodsN fuel) before = .ok (.done source) before) : - SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags + SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals fuel flags 0 before source term before source term - | next {steps before after source result term target type} + | next {fuel flags steps before after source result term target type} (step : SynthesisBetaStep resolve incoming incomingContext incomingBounds entries context locals before source term type) - (rest : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags + (rest : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals (fuel + 1) flags steps step.after step.result step.modelResult after result target) : - SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags + SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals (fuel + 1) flags (steps + 1) before source term after result target - | zeta {steps before after source result term target} + | zeta {fuel flags steps before after source result term target} (step : LetStepPlan before source) - (rest : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags + (rest : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals fuel flags steps step.after step.result term after result target) : - SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags + SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals fuel flags + (steps + 1) before source term after result target + | head {fuel flags steps before middle after source result term target type} + (plan : BetaHeadStepPlan resolve locals middle source term) + (call : BetaHeadReduction resolve locals fuel flags + before plan.rawHead plan.headTerm middle plan.rawLambda plan.modelLambda) + (meaning : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term plan.modelResult type) + (rest : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals (fuel + 1) flags + steps plan.after plan.result plan.modelResult after result target) : + SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals (fuel + 1) flags (steps + 1) before source term after result target namespace SynthesisBetaWhnfTrace @@ -96,7 +103,7 @@ variable {β : Type u} {resolve : Address → Option (ConstRef β)} {incomingBounds : List VLevel} {locals : List FVarId} {reductionFuel steps : Nat} {flags : WhnfFlags} {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} -def toBetaTrace {steps : Nat} {before after : TcState .anon} +def toBetaTrace {reductionFuel steps : Nat} {flags : WhnfFlags} {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} (trace : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags steps before source term after result target) {type : AExpr β} @@ -108,56 +115,36 @@ def toBetaTrace {steps : Nat} {before after : TcState .anon} let first := SynthesisBetaTrace.atType origin step.meaning first.trans (rest.toBetaTrace (.reduced first)) | .zeta _ rest => rest.toBetaTrace origin + | .head _ _ meaning rest => + let first := SynthesisBetaTrace.atType origin meaning + first.trans (rest.toBetaTrace (.reduced first)) +termination_by structural trace + +def toRawTrace {reductionFuel steps : Nat} {flags : WhnfFlags} {before after : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (trace : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags + steps before source term after result target) : + BetaWhnfTrace resolve locals reductionFuel flags steps before source term after result target := + match trace with + | .done finished => .done finished + | .next step rest => .next step.toBetaStepPlan rest.toRawTrace + | .zeta plan rest => .zeta plan rest.toRawTrace + | .head plan call _ rest => .head plan call rest.toRawTrace termination_by structural trace theorem run (trace : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags steps before source term after result target) {loopFuel : Nat} (enough : steps < loopFuel) : (RecM.runBounded (fun current => RecM.whnfCoreWithFlagsStep current flags) loopFuel source).run - (methodsN (reductionFuel + 1)) before = .ok result after := by - induction trace generalizing loopFuel with - | done finished => - cases loopFuel with - | zero => omega - | succ loopFuel => - rw [RecM.runBounded, ReaderT.run_bind] - change EStateM.bind ((RecM.whnfCoreWithFlagsStep _ flags).run _) _ _ = _ - unfold EStateM.bind - rw [finished] - rfl - | next step rest ih => - cases loopFuel with - | zero => omega - | succ loopFuel => - rw [RecM.runBounded, ReaderT.run_bind] - change EStateM.bind ((RecM.whnfCoreWithFlagsStep _ flags).run _) _ _ = _ - unfold EStateM.bind - rw [step.run reductionFuel flags] - exact ih (by omega) - | zeta step rest ih => - cases loopFuel with - | zero => omega - | succ loopFuel => - rw [RecM.runBounded, ReaderT.run_bind] - change EStateM.bind ((RecM.whnfCoreWithFlagsStep _ flags).run _) _ _ = _ - unfold EStateM.bind - rw [step.run (methodsN (reductionFuel + 1)) flags] - exact ih (by omega) + (methodsN reductionFuel) before = .ok result after := trace.toRawTrace.run enough theorem reading (trace : SynthesisBetaWhnfTrace resolve incoming incomingContext incomingBounds entries context locals reductionFuel flags steps before source term after result target) (sourceReading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) : - readScopedExpr? resolve locals result = some target.erase ∧ after.env.intern.WF := by - induction trace with - | done => exact ⟨sourceReading, coherent⟩ - | next step rest ih => - obtain ⟨reading, preserved⟩ := step.reading coherent - exact ih reading preserved - | zeta step rest ih => - obtain ⟨reading, preserved⟩ := step.reading sourceReading coherent - exact ih reading preserved + readScopedExpr? resolve locals result = some target.erase ∧ after.env.intern.WF := + trace.toRawTrace.reading sourceReading coherent /-- The production loop returns the final beta result with the original source's type and a coherent intern table. The finite path uses the real @@ -169,7 +156,7 @@ theorem uncached_sound (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) (sourceReading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) (enough : steps < maxWhnfCoreFuel.toNat) : - (RecM.whnfCoreWithFlagsUncached source flags).run (methodsN (reductionFuel + 1)) before = .ok result after ∧ + (RecM.whnfCoreWithFlagsUncached source flags).run (methodsN reductionFuel) before = .ok result after ∧ readScopedExpr? resolve locals result = some target.erase ∧ ConversionClaim.{u,v} entries context term target ∧ TypingClaim.{u,v} entries context target type ∧ after.env.intern.WF := by diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean b/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean index ee3fcedab..50253ed20 100644 --- a/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean +++ b/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean @@ -5,6 +5,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Verify.Consistency.BetaInference import Ix.Kernel.Verify.Consistency.BetaWhnf +import Ix.Kernel.Verify.Consistency.SynthesisAppCongruence /-! Derive every semantic beta-step origin from the original inference. The supplied WHNF path contains only actual operational and representation @@ -32,6 +33,17 @@ def BetaStepPlan.betaTyping {β : Type u} {resolve : Address → Option (ConstRe _ = plan.modelResult := BetaSyntax.steps_betaPrefix _ _ _ plan.counts.1 plan.counts.2 exact same ▸ typing.betaSteps plan.consumed.size +def BetaPrefixPlan.betaTyping {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {before : TcState .anon} {type : AExpr β} + (plan : BetaPrefixPlan resolve locals before) + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context plan.modelInput type) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context plan.modelResult type × + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context plan.modelInput plan.modelResult type := + typing.betaPrefix plan.consumed.size plan.counts.1 plan.counts.2 + +mutual + def BetaWhnfTrace.annotate {β : Type u} {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} {incomingBounds : List VLevel} {locals : List FVarId} {reductionFuel steps : Nat} {flags : WhnfFlags} @@ -52,8 +64,37 @@ def BetaWhnfTrace.annotate {β : Type u} {resolve : Address → Option (ConstRef | .zeta plan rest => let remaining := rest.annotate typing ⟨.zeta plan remaining.1, remaining.2⟩ + | .head plan call rest => by + have starting : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context + (plan.headTerm.appN plan.arguments) type := plan.modelSource ▸ typing + let first := starting.mapHead plan.arguments (target := plan.modelLambda) + (fun checked => BetaHeadReduction.annotate call checked) + let contracted := plan.toBetaPrefixPlan.betaTyping first.1 + have meaning : SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + term plan.modelResult type := by + exact Eq.mp (congrArg (fun source => SynthesisBetaTrace resolve incoming incomingContext incomingBounds + entries context source plan.modelResult type) plan.modelSource.symm) (first.2.trans contracted.2) + let remaining := BetaWhnfTrace.annotate rest contracted.1 + exact ⟨.head plan call meaning remaining.1, remaining.2⟩ termination_by structural trace +def BetaHeadReduction.annotate {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} {locals : List FVarId} {fuel : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target type : AExpr β} + (call : BetaHeadReduction resolve locals fuel flags before source term after result target) + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context target type × + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term target type := + match call with + | .reduce path .. => + let annotated := BetaWhnfTrace.annotate path typing + ⟨annotated.2, annotated.1.toBetaTrace typing.origin⟩ + | .cached origin .. => BetaHeadReduction.annotate origin typing +termination_by structural call + +end + /-- Annotating the raw public path derives its semantic trace from the retained checking derivation, including every later beta step. -/ def BetaPublicWhnfPlan.betaTrace {β : Type u} {resolve : Address → Option (ConstRef β)} @@ -196,7 +237,7 @@ theorem SynthesisInference.beta_whnf_sound {β : Type u} {resolve : Address → (accepted : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) {reductionFuel steps : Nat} {flags : WhnfFlags} {before after : TcState .anon} {result : KExpr .anon} {target : AExpr β} - (path : BetaWhnfTrace resolve locals reductionFuel flags steps before source term after result target) + (path : BetaWhnfTrace resolve locals (reductionFuel + 1) flags steps before source term after result target) (coherent : before.env.intern.WF) (enough : steps < maxWhnfCoreFuel.toNat) : (RecM.whnfCoreWithFlagsUncached source flags).run (methodsN (reductionFuel + 1)) before = .ok result after ∧ readScopedExpr? resolve locals result = some target.erase ∧ diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnfPlan.lean b/Ix/Kernel/Verify/Consistency/BetaWhnfPlan.lean index ab4e938e3..a60adad8d 100644 --- a/Ix/Kernel/Verify/Consistency/BetaWhnfPlan.lean +++ b/Ix/Kernel/Verify/Consistency/BetaWhnfPlan.lean @@ -3,8 +3,10 @@ Copyright (c) 2026 Argument Computer Corporation. SPDX-License-Identifier: MIT OR Apache-2.0 -/ -import Ix.Kernel.Verify.Consistency.SpineReading +import Ix.Kernel.Verify.Consistency.BetaPrefixPlan import Ix.Kernel.Verify.Consistency.LetWhnfPlan +import Ix.Kernel.Verify.Consistency.BetaCoreCache +import Ix.Kernel.Verify.Consistency.BetaHeadStepPlan /-! Raw beta and explicit-let paths compute production results and states independently of the source-inference and semantic-origin derivations. -/ @@ -15,41 +17,17 @@ open Theory Theory.Model universe u v -/-- Resources for one actual beta iteration. The raw output and next -state are computed below; no typing, reduction origin, or result reading is a field. -/ +/-- A lambda already at the source spine uses the shared prefix operation. -/ structure BetaStepPlan {β : Type u} (resolve : Address → Option (ConstRef β)) (locals : List FVarId) - (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) where + (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) + extends BetaPrefixPlan resolve locals before where rawFunction : KExpr .anon rawArgument : KExpr .anon appInfo : ExprInfo .anon sourceEq : source = .app rawFunction rawArgument appInfo - name : Mode.anon.F Name - bi : Mode.anon.F Lean.BinderInfo - rawDomain : KExpr .anon - rawInner : KExpr .anon - lambdaInfo : ExprInfo .anon - rawArguments : Array (KExpr .anon) - rawBody : KExpr .anon - consumed : Array (KExpr .anon) - condition : Certified.PropWhen - domain : AExpr β - inner : AExpr β - arguments : List (AExpr β) modelSource : term = (AExpr.lam condition domain inner).appN arguments spine : (KExpr.app rawFunction rawArgument appInfo).collectSpine = (.lam name bi rawDomain rawInner lambdaInfo, rawArguments) - headReads : readScopedExpr? resolve locals (.lam name bi rawDomain rawInner lambdaInfo) = - some (AExpr.lam condition domain inner).erase - argumentReads : rawArguments.toList.map (readScopedExpr? resolve locals ·) = arguments.map (some ·.erase) - peeling : RecM.consumeBetaLams (.lam name bi rawDomain rawInner lambdaInfo) rawArguments = (rawBody, consumed) - nonempty : (!consumed.isEmpty) = true - walkerBounds : SimulSubstBounds rawBody consumed.reverse 0 - walkerFaithful : KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ - KExpr.SimulSubstReach consumed.reverse rawBody 0 term - suffixFaithful : KExpr.CollisionFree fun term => - (simulSubst rawBody consumed.reverse 0 before.env.intern).2.ExprSupport term ∨ - term ∈ cheapBetaChainList (simulSubst rawBody consumed.reverse 0 before.env.intern).1 - (rawArguments.extract consumed.size rawArguments.size).toList namespace BetaStepPlan @@ -92,116 +70,202 @@ theorem sourceReading theorem reading (step : BetaStepPlan resolve locals before source term) (coherent : before.env.intern.WF) : - readScopedExpr? resolve locals step.result = some step.modelResult.erase ∧ step.after.env.intern.WF := by - obtain ⟨result, after, run, reading, _, preserved⟩ := beta_many_step_readScopedExpr? - step.spine step.headReads step.argumentReads step.peeling step.nonempty before 0 .FULL - step.walkerBounds coherent step.walkerFaithful step.suffixFaithful - have actual := step.run 0 .FULL - simp only [step.sourceEq] at actual - rw [actual] at run - cases run - exact ⟨reading, preserved⟩ + readScopedExpr? resolve locals step.result = some step.modelResult.erase ∧ step.after.env.intern.WF := + step.toBetaPrefixPlan.reading coherent theorem counts (plan : BetaStepPlan resolve locals before source term) : - plan.consumed.size ≤ plan.inner.lambdaDepth + 1 ∧ plan.consumed.size ≤ plan.arguments.length := by - obtain ⟨peeled, _, rawBound⟩ := RecM.BetaPeel.of_consume plan.peeling - obtain ⟨_, modelPeel, _⟩ := betaPeel_readScopedExpr? peeled plan.headReads - have sizeAgrees : plan.rawArguments.size = plan.arguments.length := by - have lengths := congrArg List.length plan.argumentReads - simpa using lengths - exact ⟨by simpa only [Array.length_toList, AExpr.lambdaDepth] using modelPeel.length_bound, - sizeAgrees ▸ rawBound⟩ + plan.consumed.size ≤ plan.inner.lambdaDepth + 1 ∧ plan.consumed.size ≤ plan.arguments.length := + plan.toBetaPrefixPlan.counts end BetaStepPlan -/-- A finite production path without semantic step origins. All intermediate -expressions and states are computed by its raw beta and let plans. -/ +mutual + +/-- A finite structural-WHNF path. Its fuel is the actual method-table depth; +recursive head calls run at the predecessor depth and retain their cache effects. -/ inductive BetaWhnfTrace {β : Type u} (resolve : Address → Option (ConstRef β)) - (locals : List FVarId) (reductionFuel : Nat) (flags : WhnfFlags) : - Nat → TcState .anon → KExpr .anon → AExpr β → TcState .anon → KExpr .anon → AExpr β → Type u - | done {before source term} - (finished : (RecM.whnfCoreWithFlagsStep source flags).run (methodsN (reductionFuel + 1)) before = + (locals : List FVarId) : + Nat → WhnfFlags → Nat → TcState .anon → KExpr .anon → AExpr β → TcState .anon → KExpr .anon → AExpr β → Type u + | done {fuel flags before source term} + (finished : (RecM.whnfCoreWithFlagsStep source flags).run (methodsN fuel) before = .ok (.done source) before) : - BetaWhnfTrace resolve locals reductionFuel flags 0 before source term before source term - | next {steps before after source result term target} + BetaWhnfTrace resolve locals fuel flags 0 before source term before source term + | next {fuel flags steps before after source result term target} (plan : BetaStepPlan resolve locals before source term) - (rest : BetaWhnfTrace resolve locals reductionFuel flags + (rest : BetaWhnfTrace resolve locals (fuel + 1) flags steps plan.after plan.result plan.modelResult after result target) : - BetaWhnfTrace resolve locals reductionFuel flags (steps + 1) before source term after result target - | zeta {steps before after source result term target} + BetaWhnfTrace resolve locals (fuel + 1) flags (steps + 1) before source term after result target + | zeta {fuel flags steps before after source result term target} (plan : LetStepPlan before source) - (rest : BetaWhnfTrace resolve locals reductionFuel flags + (rest : BetaWhnfTrace resolve locals fuel flags steps plan.after plan.result term after result target) : - BetaWhnfTrace resolve locals reductionFuel flags (steps + 1) before source term after result target + BetaWhnfTrace resolve locals fuel flags (steps + 1) before source term after result target + | head {fuel flags steps before middle after source result term target} + (plan : BetaHeadStepPlan resolve locals middle source term) + (call : BetaHeadReduction resolve locals fuel flags + before plan.rawHead plan.headTerm middle plan.rawLambda plan.modelLambda) + (rest : BetaWhnfTrace resolve locals (fuel + 1) flags + steps plan.after plan.result plan.modelResult after result target) : + BetaWhnfTrace resolve locals (fuel + 1) flags (steps + 1) before source term after result target + +/-- A recursive structural head call, including its full/cheap cache lookup. +A retained hit carries the producing call, so its meaning can be reconstructed. -/ +inductive BetaHeadReduction {β : Type u} (resolve : Address → Option (ConstRef β)) (locals : List FVarId) : + Nat → WhnfFlags → TcState .anon → KExpr .anon → AExpr β → TcState .anon → KExpr .anon → AExpr β → Type u + | reduce {fuel flags steps before reduced source result term target} + (path : BetaWhnfTrace resolve locals fuel flags steps + (betaWhnfKey source before).2 source term reduced result target) + (moving : 0 < steps) + (enough : steps < maxWhnfCoreFuel.toNat) + (miss : BetaCoreCache.lookup flags (betaWhnfKey source before).1 (betaWhnfKey source before).2 = none) : + BetaHeadReduction resolve locals fuel flags before source term + (BetaCoreCache.write flags (betaWhnfKey source before).1 result reduced) result target + | cached {fuel flags before source result term target originFuel originBefore originAfter} + (origin : BetaHeadReduction resolve locals originFuel flags originBefore source term originAfter result target) + (coherent : originBefore.env.intern.WF) + (hit : BetaCoreCache.lookup flags (betaWhnfKey source before).1 (betaWhnfKey source before).2 = some result) : + BetaHeadReduction resolve locals fuel flags before source term (betaWhnfKey source before).2 result target -namespace BetaWhnfTrace +end variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} - {reductionFuel steps : Nat} {flags : WhnfFlags} {before after : TcState .anon} - {source result : KExpr .anon} {term target : AExpr β} -theorem run +theorem BetaWhnfTrace.first {reductionFuel steps : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} + (trace : BetaWhnfTrace resolve locals reductionFuel flags steps before source term after result target) : + 0 < steps → StructuralWhnfEntry source := + match trace with + | .done _ => fun impossible => False.elim (Nat.not_lt_zero _ impossible) + | .next plan _ => fun _ => plan.entry + | .zeta plan _ => fun _ => plan.entry + | .head plan _ _ => fun _ => plan.entry + +theorem BetaHeadReduction.entry {reductionFuel : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} + (call : BetaHeadReduction resolve locals reductionFuel flags before source term after result target) : + StructuralWhnfEntry source := + match call with + | .reduce path moving .. => path.first moving + | .cached origin .. => origin.entry + +mutual + +theorem BetaWhnfTrace.run {reductionFuel steps : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} (trace : BetaWhnfTrace resolve locals reductionFuel flags steps before source term after result target) {loopFuel : Nat} (enough : steps < loopFuel) : (RecM.runBounded (fun current => RecM.whnfCoreWithFlagsStep current flags) loopFuel source).run - (methodsN (reductionFuel + 1)) before = .ok result after := by - induction trace generalizing loopFuel with - | done finished => + (methodsN reductionFuel) before = .ok result after := + match trace with + | .done finished => by cases loopFuel with | zero => omega | succ loopFuel => rw [RecM.runBounded, ReaderT.run_bind] change EStateM.bind ((RecM.whnfCoreWithFlagsStep _ flags).run _) _ _ = _ - unfold EStateM.bind - rw [finished] + rw [EStateM.bind, finished] rfl - | next step rest ih => + | .next step rest => by + cases loopFuel with + | zero => omega + | succ loopFuel => + rw [RecM.runBounded, ReaderT.run_bind] + change EStateM.bind ((RecM.whnfCoreWithFlagsStep _ flags).run _) _ _ = _ + rw [EStateM.bind, step.run _ flags] + exact BetaWhnfTrace.run rest (by omega) + | .zeta step rest => by cases loopFuel with | zero => omega | succ loopFuel => rw [RecM.runBounded, ReaderT.run_bind] change EStateM.bind ((RecM.whnfCoreWithFlagsStep _ flags).run _) _ _ = _ - unfold EStateM.bind - rw [step.run reductionFuel flags] - exact ih (by omega) - | zeta step rest ih => + rw [EStateM.bind, step.run (methodsN reductionFuel) flags] + exact BetaWhnfTrace.run rest (by omega) + | .head plan call rest => by cases loopFuel with | zero => omega | succ loopFuel => rw [RecM.runBounded, ReaderT.run_bind] change EStateM.bind ((RecM.whnfCoreWithFlagsStep _ flags).run _) _ _ = _ - unfold EStateM.bind - rw [step.run (methodsN (reductionFuel + 1)) flags] - exact ih (by omega) + rw [EStateM.bind, plan.run (BetaHeadReduction.run call)] + exact BetaWhnfTrace.run rest (by omega) +termination_by structural trace -theorem reading +theorem BetaHeadReduction.run {reductionFuel : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} + (call : BetaHeadReduction resolve locals reductionFuel flags before source term after result target) : + (RecM.whnfCoreWithFlags source flags).run (methodsN reductionFuel) before = .ok result after := + match call with + | .reduce path moving enough miss => by + exact BetaCoreCache.miss flags (path.first moving) miss (BetaWhnfTrace.run path enough) + | .cached origin coherent hit => by exact BetaCoreCache.hit flags origin.entry hit +termination_by structural call + +end + +mutual + +theorem BetaWhnfTrace.reading {reductionFuel steps : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} (trace : BetaWhnfTrace resolve locals reductionFuel flags steps before source term after result target) (sourceReading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) : - readScopedExpr? resolve locals result = some target.erase ∧ after.env.intern.WF := by - induction trace with - | done => exact ⟨sourceReading, coherent⟩ - | next step rest ih => + readScopedExpr? resolve locals result = some target.erase ∧ after.env.intern.WF := + match trace with + | .done _ => by exact ⟨sourceReading, coherent⟩ + | .next step rest => by obtain ⟨reading, preserved⟩ := step.reading coherent - exact ih reading preserved - | zeta step rest ih => + exact BetaWhnfTrace.reading rest reading preserved + | .zeta step rest => by obtain ⟨reading, preserved⟩ := step.reading sourceReading coherent - exact ih reading preserved + exact BetaWhnfTrace.reading rest reading preserved + | .head plan call rest => by + obtain ⟨_, headCoherent⟩ := BetaHeadReduction.reading call plan.sourceHeadReads coherent + obtain ⟨reading, preserved⟩ := plan.reading headCoherent + exact BetaWhnfTrace.reading rest reading preserved +termination_by structural trace -/-- A beta/let path changes only the intern table. All cache partitions, -locals, checking policies, and instrumentation fields retain their values. -/ -theorem frame +theorem BetaHeadReduction.reading {reductionFuel : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} + (call : BetaHeadReduction resolve locals reductionFuel flags before source term after result target) + (sourceReading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) : + readScopedExpr? resolve locals result = some target.erase ∧ after.env.intern.WF := + match call with + | .reduce path moving enough miss => by + obtain ⟨reads, preserved⟩ := BetaWhnfTrace.reading path sourceReading ((betaWhnfKey_environment _ _).symm ▸ coherent) + exact ⟨reads, (BetaCoreCache.intern _ _ _ _).symm ▸ preserved⟩ + | .cached origin initial hit => by + exact ⟨(BetaHeadReduction.reading origin sourceReading initial).1, (betaWhnfKey_environment _ _).symm ▸ coherent⟩ +termination_by structural call + +end + +mutual + +/-- Recursive structural reduction preserves inference state and every +surrounding cache key while retaining the actual head-call cache writes. -/ +theorem BetaWhnfTrace.frame {reductionFuel steps : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} (trace : BetaWhnfTrace resolve locals reductionFuel flags steps before source term after result target) : - ∃ table, after = { before with env := { before.env with intern := table } } := by - induction trace with - | done => exact ⟨_, rfl⟩ - | next step rest ih => - obtain ⟨table, same⟩ := ih - exact ⟨table, same⟩ - | zeta step rest ih => - obtain ⟨table, same⟩ := ih - exact ⟨table, same⟩ - -end BetaWhnfTrace + BetaCacheFrame before after := + match trace with + | .done _ => by exact .refl _ + | .next step rest => by exact (BetaCacheFrame.intern _ _).trans (BetaWhnfTrace.frame rest) + | .zeta step rest => by exact (BetaCacheFrame.intern _ _).trans (BetaWhnfTrace.frame rest) + | .head plan call rest => by exact (BetaHeadReduction.frame call).trans ((BetaCacheFrame.intern _ _).trans (BetaWhnfTrace.frame rest)) +termination_by structural trace + +theorem BetaHeadReduction.frame {reductionFuel : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} + (call : BetaHeadReduction resolve locals reductionFuel flags before source term after result target) : + BetaCacheFrame before after := + match call with + | .reduce path moving enough miss => by + exact (BetaCacheFrame.key _ _).trans ((BetaWhnfTrace.frame path).trans (BetaCoreCache.frame _ _ _ _)) + | .cached .. => by exact .key _ _ +termination_by structural call + +end end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnfState.lean b/Ix/Kernel/Verify/Consistency/BetaWhnfState.lean new file mode 100644 index 000000000..698fe7eed --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaWhnfState.lean @@ -0,0 +1,233 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.StructuralWhnfEntry +import Ix.Kernel.Verify.Whnf.Driver.CacheExecution + +/-! Computed WHNF key, instrumentation, cache states, and terminal branches. +These operational primitives do not depend on a reduction trace. -/ + +namespace Ix.Kernel.Consistency + +universe u v + +/-- Compute exactly the context-digest memoization performed by a WHNF key. -/ +def betaWhnfKey (source : KExpr .anon) (before : TcState .anon) : + (Address × Address) × TcState .anon := + if source.lbr == 0 || before.ctx.isEmpty then + ((source.addr, emptyCtxAddr), before) + else + let cacheKey := (before.ctxId, source.lbr) + match before.ctxAddrCache[cacheKey]? with + | some cached => ((source.addr, cached), before) + | none => + let digest := TcM.ctxAddrForLbrUncached before source.lbr + ((source.addr, digest), { before with ctxAddrCache := before.ctxAddrCache.insert cacheKey digest }) + +theorem betaWhnfKey_run (source : KExpr .anon) (before : TcState .anon) : + TcM.whnfKey source before = .ok (betaWhnfKey source before).1 (betaWhnfKey source before).2 := by + unfold TcM.whnfKey TcM.ctxAddrForLbr betaWhnfKey + change EStateM.bind (fun state => EStateM.bind (get : TcM .anon (TcState .anon)) _ state) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + by_cases fast : (source.lbr == 0 || before.ctx.isEmpty) = true + · simp only [if_pos fast]; rfl + · simp only [if_neg fast] + cases before.ctxAddrCache[(before.ctxId, source.lbr)]? <;> rfl + +theorem betaWhnfKey_environment (source : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey source before).2.env = before.env := by + unfold betaWhnfKey + split + · rfl + · dsimp only + split <;> rfl + +theorem betaWhnfKey_context (source : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey source before).2.lctx = before.lctx := by + unfold betaWhnfKey + split + · rfl + · dsimp only + split <;> rfl + +theorem betaWhnfKey_native (source : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey source before).2.inNativeReduce = before.inNativeReduce := by + unfold betaWhnfKey + split + · rfl + · dsimp only + split <;> rfl + +/-- Full WHNF's instrumentation changes only its optional call counter. -/ +def betaWhnfPrefix (before : TcState .anon) : TcState .anon := + if before.stats then { before with whnfCalls := before.whnfCalls + 1 } else before + +theorem betaWhnfPrefix_run (source : KExpr .anon) (before : TcState .anon) (methods : Methods .anon) : + (RecM.whnfWithNatSuccModePrefix source).run methods before = .ok () (betaWhnfPrefix before) := by + have traced : TcM.stepTrace "whnf+" (fun _ => TcM.addr8 source.addr) before = .ok () before := by + unfold TcM.stepTrace + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + split <;> rfl + unfold RecM.whnfWithNatSuccModePrefix + rw [ReaderT.run_bind] + change EStateM.bind (TcM.stepTrace "whnf+" (fun _ => TcM.addr8 source.addr)) _ before = _ + rw [EStateM.bind, traced] + change TcM.bumpStats (fun state => { state with whnfCalls := state.whnfCalls + 1 }) before = _ + unfold TcM.bumpStats betaWhnfPrefix + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + split <;> rfl + +/-- The miss charge performs one shared-fuel decrement and optionally +increments its miss counter. -/ +def betaWhnfCharge (before : TcState .anon) : TcState .anon := + if before.stats then + { before with whnfMisses := before.whnfMisses + 1, recFuel := before.recFuel - 1 } + else { before with recFuel := before.recFuel - 1 } + +theorem betaWhnfCharge_run (before : TcState .anon) (methods : Methods .anon) + (enough : (before.recFuel == 0) = false) : + (RecM.whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods before = + .ok () (betaWhnfCharge before) := by + unfold RecM.whnfWithNatSuccModeMissCharge + rw [ReaderT.run_bind] + change EStateM.bind (TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 })) + (fun _ => TcM.tick) before = _ + cases stats : before.stats + · have bumped : TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 }) before = + .ok () before := by + unfold TcM.bumpStats + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + rw [if_neg (by simp only [stats, Bool.false_eq_true, not_false_eq_true])]; rfl + rw [EStateM.bind, bumped] + change TcM.tick before = _ + unfold TcM.tick betaWhnfCharge + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + rw [enough, stats]; rfl + · have bumped : TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 }) before = + .ok () { before with whnfMisses := before.whnfMisses + 1 } := by + unfold TcM.bumpStats + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + rw [if_pos stats]; rfl + rw [EStateM.bind, bumped] + change TcM.tick { before with whnfMisses := before.whnfMisses + 1 } = _ + unfold TcM.tick betaWhnfCharge + change EStateM.bind (get : TcM .anon (TcState .anon)) _ { before with whnfMisses := before.whnfMisses + 1 } = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) + { before with whnfMisses := before.whnfMisses + 1 } = + .ok { before with whnfMisses := before.whnfMisses + 1 } + { before with whnfMisses := before.whnfMisses + 1 } from rfl] + rw [enough, stats]; rfl + +/-- Constructors on which both public drivers and all their reducer tails +stop without recursive callbacks or source lookup. -/ +inductive BetaWhnfTerminal : KExpr .anon → Prop + | sort (level : KUniv .anon) (info : ExprInfo .anon) : BetaWhnfTerminal (.sort level info) + | forallE (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) + (domain body : KExpr .anon) (info : ExprInfo .anon) : BetaWhnfTerminal (.all name bi domain body info) + | lam (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) + (domain body : KExpr .anon) (info : ExprInfo .anon) : BetaWhnfTerminal (.lam name bi domain body info) + +theorem BetaWhnfTerminal.noDelta_tail {source : KExpr .anon} (terminal : BetaWhnfTerminal source) + (methods : Methods .anon) (before : TcState .anon) (flags : WhnfFlags) (mode : NatSuccMode) : + (RecM.whnfNoDeltaReducersStep flags mode source).run methods before = .ok (.done source) before := by + cases terminal <;> + simp [RecM.whnfNoDeltaReducersStep, RecM.tryProjAppReduceFinished, RecM.tryProjAppReduce, + RecM.tryReduceBitvec, RecM.tryReduceNatWithSuccMode, RecM.tryReduceNative, + RecM.tryReduceString, RecM.tryReduceProjectionDefinition, RecM.tryQuotReduce, + KExpr.collectSpine, KExpr.collectSpine.go, RecM.prims, ReaderT.run_bind] + all_goals + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + cases before.noAccel <;> rfl + +theorem BetaWhnfTerminal.full_step {source result : KExpr .anon} + (terminal : BetaWhnfTerminal result) {methods : Methods .anon} {before after : TcState .anon} + (mode : NatSuccMode) (seen : Std.HashSet Address) + (reduced : (RecM.whnfNoDeltaImpl source .FULL mode).run methods before = .ok result after) : + (RecM.whnfWithNatSuccModeStep mode (source, seen)).run methods before = .ok (.done result) after := by + unfold RecM.whnfWithNatSuccModeStep + rw [ReaderT.run_bind] + change EStateM.bind ((RecM.whnfNoDeltaImpl source .FULL mode).run methods) _ before = _ + rw [EStateM.bind, reduced] + simp only + by_cases repeated : seen.contains result.addr = true + · rw [if_pos repeated]; rfl + · rw [if_neg repeated] + cases terminal <;> + simp [RecM.tryReduceNative, RecM.tryReduceBitvec, RecM.tryReduceNatWithSuccMode, + RecM.tryReduceDecidable, RecM.tryReduceString, RecM.tryNatOffsetStuck, + RecM.natOffsetStuckHead, RecM.deltaUnfoldOne, RecM.tryDeltaUnfold, + KExpr.collectSpine, KExpr.collectSpine.go, RecM.prims, ReaderT.run_bind] + all_goals + change EStateM.bind (get : TcM .anon (TcState .anon)) _ after = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) after = .ok after after from rfl] + cases native : after.noAccel + all_goals + change EStateM.bind (get : TcM .anon (TcState .anon)) _ after = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) after = .ok after after from rfl] + simp only [native] + rfl + +private theorem bounded_done {α γ : Type} (step : α → RecM .anon (RecM.BoundedStep α γ)) + {methods : Methods .anon} {before after : TcState .anon} {source : α} {result : γ} + (done : (step source).run methods before = .ok (.done result) after) + {fuel : Nat} (enough : 0 < fuel) : + (RecM.runBounded step fuel source).run methods before = .ok result after := by + cases fuel with + | zero => omega + | succ fuel => + rw [RecM.runBounded, ReaderT.run_bind] + change EStateM.bind ((step source).run methods) _ before = _ + rw [EStateM.bind, done] + rfl + +theorem BetaWhnfTerminal.noDelta_uncached {source result : KExpr .anon} + (terminal : BetaWhnfTerminal result) {methods : Methods .anon} {before after : TcState .anon} + (flags : WhnfFlags) (mode : NatSuccMode) + (reduced : (RecM.whnfCoreWithFlags source flags).run methods before = .ok result after) : + (RecM.whnfNoDeltaImplUncached source flags mode).run methods before = .ok result after := by + apply bounded_done (enough := by decide) + unfold RecM.whnfNoDeltaImplStep + rw [ReaderT.run_bind] + change EStateM.bind ((RecM.whnfCoreWithFlags source flags).run methods) _ before = _ + rw [EStateM.bind, reduced] + exact terminal.noDelta_tail methods after flags mode + +theorem BetaWhnfTerminal.full_uncached {source result : KExpr .anon} + (terminal : BetaWhnfTerminal result) {methods : Methods .anon} {before after : TcState .anon} + (mode : NatSuccMode) + (reduced : (RecM.whnfNoDeltaImpl source .FULL mode).run methods before = .ok result after) : + (RecM.whnfWithNatSuccModeUncached source mode).run methods before = .ok result after := + bounded_done _ (terminal.full_step mode {} reduced) (by decide) + +theorem betaWhnfKey_fuel (source : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey source before).2.recFuel = before.recFuel := by + unfold betaWhnfKey + split + · rfl + · dsimp only + split <;> rfl + +theorem betaWhnfPrefix_fields (before : TcState .anon) : + (betaWhnfPrefix before).env = before.env ∧ + (betaWhnfPrefix before).lctx = before.lctx ∧ + (betaWhnfPrefix before).inNativeReduce = before.inNativeReduce ∧ + (betaWhnfPrefix before).recFuel = before.recFuel := by + unfold betaWhnfPrefix + split <;> exact ⟨rfl, rfl, rfl, rfl⟩ + +theorem betaWhnfCharge_fields (before : TcState .anon) : + (betaWhnfCharge before).env = before.env ∧ + (betaWhnfCharge before).lctx = before.lctx ∧ + (betaWhnfCharge before).inNativeReduce = before.inNativeReduce := by + unfold betaWhnfCharge + split <;> exact ⟨rfl, rfl, rfl⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/StructuralWhnfEntry.lean b/Ix/Kernel/Verify/Consistency/StructuralWhnfEntry.lean index f484b46e2..87c1eabb3 100644 --- a/Ix/Kernel/Verify/Consistency/StructuralWhnfEntry.lean +++ b/Ix/Kernel/Verify/Consistency/StructuralWhnfEntry.lean @@ -18,6 +18,9 @@ inductive StructuralWhnfEntry : KExpr .anon → Prop | letE (name : Mode.anon.F Name) (domain value body : KExpr .anon) (nonDep : Bool) (info : ExprInfo .anon) : StructuralWhnfEntry (.letE name domain value body nonDep info) + | application {fn arg head : KExpr .anon} {info : ExprInfo .anon} {arguments} + (spine : (KExpr.app fn arg info).collectSpine = (head, arguments)) + (entry : StructuralWhnfEntry head) : StructuralWhnfEntry (.app fn arg info) namespace StructuralWhnfEntry @@ -50,6 +53,9 @@ theorem not_transient (entry : StructuralWhnfEntry source) (methods : Methods .a simp only [RecM.isTransientNatLiteralWork, RecM.isNatLiteralRecursorApp, spine, pure_bind] rfl | letE => rfl + | application spine headEntry => + simp only [RecM.isTransientNatLiteralWork, RecM.isNatLiteralRecursorApp, spine] + cases headEntry <;> rfl end StructuralWhnfEntry diff --git a/Ix/Kernel/Verify/Consistency/SynthesisAppCongruence.lean b/Ix/Kernel/Verify/Consistency/SynthesisAppCongruence.lean new file mode 100644 index 000000000..0bec856db --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SynthesisAppCongruence.lean @@ -0,0 +1,64 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaTyping + +/-! Reapply the original checked argument spine after a function reduction. +Dependent result types and existing type conversions remain in the derivation. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +namespace SynthesisBetaTyping + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext context : Model.Context β} + {incomingBounds : List VLevel} + +def mapFunction {term type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type) + (head target argument : AExpr β) (same : term = .app head argument) + (reduce : {headType : AExpr β} → + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context head headType → + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context target headType × + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context head target headType) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (.app target argument) type × + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context term (.app target argument) type := + match typing with + | .atom _ shape => False.elim (by cases shape <;> cases same) + | .bvar .. | .forallE .. | .lam .. => by cases same + | .app function checked => by + cases same + let next := reduce function + exact ⟨.app next.1 checked, .application next.2 checked.origin⟩ + | .convert prior trace => + let next := prior.mapFunction head target argument same reduce + ⟨.convert next.1 trace, .convertType next.2 trace⟩ +termination_by structural typing + +/-- The original dependent spine is retained while the recursive callback +reduces its head at whichever type the source derivation assigned it. -/ +def mapHead (arguments : List (AExpr β)) {head target type : AExpr β} + (typing : SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context + (head.appN arguments) type) + (reduce : {headType : AExpr β} → + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context head headType → + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context target headType × + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context head target headType) : + SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context (target.appN arguments) type × + SynthesisBetaTrace resolve incoming incomingContext incomingBounds entries context + (head.appN arguments) (target.appN arguments) type := + match arguments with + | [] => reduce typing + | argument :: arguments => + mapHead arguments (head := .app head argument) (target := .app target argument) typing + (fun checked => checked.mapFunction head target argument rfl reduce) + +end SynthesisBetaTyping + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean b/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean index aef96e7dc..9d6117f98 100644 --- a/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean +++ b/Ix/Kernel/Verify/Consistency/WhnfCacheFrame.lean @@ -38,19 +38,22 @@ theorem BetaPublicWhnfPlan.inference_frame {β : Type u} {resolve : Address → {source result : KExpr .anon} {term target : AExpr β} (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) (key : Address × Address) : InferenceCacheFrame key before plan.after := by - obtain ⟨table, reduced⟩ := plan.path.frame - apply InferenceCacheFrame.of_eq <;> - simp only [BetaPublicWhnfPlan.after, BetaPublicWhnf.after, BetaPublicWhnf.noDeltaAfter, - BetaPublicWhnf.coreAfter, reduced, (BetaPublicWhnf.coreKey_fields source before).1] + apply InferenceCacheFrame.of_eq + · change plan.reduced.env.inferCache[key]? = before.env.inferCache[key]? + rw [plan.path.frame.full, (BetaPublicWhnf.coreKey_fields source before).1] + · change plan.reduced.env.inferOnlyCache[key]? = before.env.inferOnlyCache[key]? + rw [plan.path.frame.only, (BetaPublicWhnf.coreKey_fields source before).1] + · change plan.reduced.env.consts = before.env.consts + rw [plan.path.frame.constants, (BetaPublicWhnf.coreKey_fields source before).1] theorem BetaPublicWhnfPlan.policy {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {fuel : Nat} {before : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : plan.after.inferOnly = before.inferOnly := by - obtain ⟨table, reduced⟩ := plan.path.frame - simp only [BetaPublicWhnfPlan.after, BetaPublicWhnf.after, BetaPublicWhnf.noDeltaAfter, - BetaPublicWhnf.coreAfter, reduced, BetaPublicWhnf.coreKey, BetaPublicWhnf.noDeltaKey, + change plan.reduced.inferOnly = before.inferOnly + rw [plan.path.frame.policy] + simp only [BetaPublicWhnf.coreKey, BetaPublicWhnf.noDeltaKey, BetaPublicWhnf.outerKey, betaWhnfKey_policy, betaWhnfPrefix_policy, betaWhnfCharge_policy] theorem BetaPiExposure.inference_frame {β : Type u} {resolve : Address → Option (ConstRef β)} @@ -87,10 +90,11 @@ theorem BetaPublicWhnfPlan.inference_maps {β : Type u} {resolve : Address → O (plan : BetaPublicWhnfPlan resolve locals fuel before source term result target) : plan.after.env.inferCache = before.env.inferCache ∧ plan.after.env.inferOnlyCache = before.env.inferOnlyCache := by - obtain ⟨table, reduced⟩ := plan.path.frame - constructor <;> - simp only [BetaPublicWhnfPlan.after, BetaPublicWhnf.after, BetaPublicWhnf.noDeltaAfter, - BetaPublicWhnf.coreAfter, reduced, (BetaPublicWhnf.coreKey_fields source before).1] + constructor + · change plan.reduced.env.inferCache = before.env.inferCache + rw [plan.path.frame.full, (BetaPublicWhnf.coreKey_fields source before).1] + · change plan.reduced.env.inferOnlyCache = before.env.inferOnlyCache + rw [plan.path.frame.only, (BetaPublicWhnf.coreKey_fields source before).1] theorem BetaPiExposure.inference_maps {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {fuel : Nat} {before : TcState .anon} diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index cc31c9d0b..a91062d93 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -3602,12 +3602,17 @@ private def recursiveLetFailureCleanup : Bool := !after.env.inferCache.contains (invalid.addr, emptyCtxAddr) | _ => false -private def sortExposureEnvironment (level : Ixon.Univ) (universes : UInt64 := 0) (withZeta : Bool := false) : +private def sortExposureEnvironment (level : Ixon.Univ) (universes : UInt64 := 0) (withZeta : Bool := false) + (withHead : Bool := false) : Ixon.Env × Array Address := Id.run do -- References pass the parameter itself, even when the tested level is composite. let arguments := if universes == 0 then #[] else #[2] let levels := #[level, .succ level] ++ if universes == 0 then #[] else #[.var 0] - let betaSort := Ixon.Expr.app (.leanLam (.sort 1) (.var 0)) (.sort 0) + let function := Ixon.Expr.leanLam (.sort 1) (.var 0) + let functionType := Ixon.Expr.leanAll (.sort 1) (.sort 1) + let head := if withHead then Ixon.Expr.letE false functionType + (.app (.leanLam functionType (.var 0)) function) (.var 0) else function + let betaSort := Ixon.Expr.app head (.sort 0) let betaSort := if withZeta then Ixon.Expr.letE false (.sort 1) betaSort (.var 0) else betaSort let (env, carrier) := storeConst {} ⟨.axio ⟨false, universes, betaSort⟩, #[], #[], levels⟩ @@ -3639,9 +3644,9 @@ private def sortExposureEnvironment (level : Ixon.Univ) (universes : UInt64 := 0 /-- The domain's full inference cache keeps its original beta type while sort exposure writes the normalized sort only to the three WHNF caches. -/ private def sortExposureInferencePaths (typeLevel warm instrumented noAccel : Bool) - (lowerWarm : Nat := 0) (withZeta : Bool := false) : Bool := + (lowerWarm : Nat := 0) (withZeta : Bool := false) (withHead : Bool := false) : Bool := let level := if typeLevel then levelOne else KUniv.mkZero - let (env, targets) := sortExposureEnvironment (if typeLevel then .succ .zero else .zero) 0 withZeta + let (env, targets) := sortExposureEnvironment (if typeLevel then .succ .zero else .zero) 0 withZeta withHead (List.range 5).all fun shape => let action : RecM .anon Bool := do let .defn _ _ _ _ _ _ expected value _ _ ← TcM.getConst (m := .anon) ⟨targets[shape]!, ()⟩ | return false @@ -3850,6 +3855,115 @@ private def letWhnfLoopResult (typeLevel : Bool) (flags : WhnfFlags) (shape : Na | .ok passed _ => passed | .error _ _ => false +/-- Recursive head calls may contain their own beta steps and cache writes. +Their method depth is separate from the enclosing two-iteration loop. -/ +private def headWhnfLoopResult (depth : Nat) (typeLevel : Bool) (flags : WhnfFlags) + (shape : Nat) (warm native : Bool) : Bool := + let carrier := KExpr.mkSort (m := .anon) (if typeLevel then levelOne else .mkZero) + let carrierType := KExpr.mkSort (m := .anon) (if typeLevel then levelTwo else levelOne) + let body := if shape == 0 then KExpr.mkVar 0 () else if shape == 1 then + KExpr.mkAll () () (.mkVar 0 ()) (.mkVar 1 ()) + else KExpr.mkLam () () (.mkVar 0 ()) (.mkVar 0 ()) + let expected := if shape == 0 then carrier else if shape == 1 then + KExpr.mkAll () () carrier carrier + else KExpr.mkLam () () carrier (.mkVar 0 ()) + let function := KExpr.mkLam () () carrierType body + let sameMap (actual predicted : Std.HashMap (Address × Address) (KExpr .anon)) := + actual.size == predicted.size && predicted.toList.all fun (key, result) => actual[key]? == some result + let action : RecM .anon Bool := do + let functionType ← RecM.inferCall function + let identity := KExpr.mkLam () () functionType (.mkVar 0 ()) + let identityType := KExpr.mkAll () () functionType functionType + let inner := KExpr.mkLet () identityType + (.mkApp (.mkLam () () identityType (.mkVar 0 ())) identity) (.mkVar 0 ()) false + let value := if depth == 0 then function else if depth == 1 then + KExpr.mkApp identity function else KExpr.mkApp inner function + let head := KExpr.mkLet () functionType value (.mkVar 0 ()) false + let source := KExpr.mkApp head carrier + let inferred ← RecM.inferCall source + let expectedType ← RecM.inferCall expected + if !(← RecM.isDefEq inferred expectedType) then return false + let seed : Std.HashMap (Address × Address) (KExpr .anon) := + (∅ : Std.HashMap (Address × Address) (KExpr .anon)).insert (carrierType.addr, emptyCtxAddr) carrierType + modify fun state => { state with + recFuel := 0, inNativeReduce := native, ctxAddrCache := {}, + env := { state.env with + whnfCache := seed, whnfNoDeltaCache := seed, whnfCoreCache := seed, + whnfNoDeltaCheapCache := seed, whnfCoreCheapCache := seed } } + if warm then + if (← RecM.whnfCoreWithFlags head flags) != function then return false + let before ← get + let budget := if warm then 1 else depth + 1 + let headKey := (head.addr, emptyCtxAddr) + let parentKey := (source.addr, emptyCtxAddr) + let writes := if depth == 2 then (seed.insert (inner.addr, emptyCtxAddr) identity).insert headKey function + else seed.insert headKey function + let maps (after : TcState .anon) (selected : Std.HashMap (Address × Address) (KExpr .anon)) := + sameMap after.env.whnfCoreCache (if flags.isFull then selected else seed) && + sameMap after.env.whnfCoreCheapCache (if flags.isFull then seed else selected) && + sameMap after.env.whnfNoDeltaCache seed && sameMap after.env.whnfNoDeltaCheapCache seed && + sameMap after.env.whnfCache seed && sameMap after.env.inferCache before.env.inferCache && + sameMap after.env.inferOnlyCache before.env.inferOnlyCache + match (RecM.runBounded (fun term => RecM.whnfCoreWithFlagsStep term flags) 2 source).run + (methodsN (budget - 1)) before with + | .error .maxRecFuel _ => pure () + | _ => return false + let .error .maxRecDepth exhausted := + (RecM.runBounded (fun term => RecM.whnfCoreWithFlagsStep term flags) 1 source).run + (methodsN budget) before | return false + if !maps exhausted writes then return false + let .ok rawResult reduced := (RecM.runBounded (fun term => RecM.whnfCoreWithFlagsStep term flags) 2 source).run + (methodsN budget) before | return false + if !sameSourceExpr rawResult expected || !maps reduced writes || + reduced.env.intern.exprs.size != exhausted.env.intern.exprs.size then return false + match (RecM.whnfCoreWithFlags source flags).run (methodsN budget) before with + | .error _ _ => return false + | .ok result after => + let published := writes.insert parentKey result + let .ok replay reused := (RecM.whnfCoreWithFlags source flags).run (methodsN 0) after | return false + return sameSourceExpr result expected && replay == result && maps after published && maps reused published && + after.recFuel == 0 && reused.recFuel == 0 && after.inNativeReduce == native && + after.lctx.size == before.lctx.size && after.env.nextFVarId == before.env.nextFVarId && + after.ctxAddrCache.size == 0 && after.env.intern.exprs.size == reduced.env.intern.exprs.size && + reused.env.intern.exprs.size == after.env.intern.exprs.size + match TcM.runRec action (TcState.newLazyAnon {}) with + | .ok passed _ => passed + | .error _ _ => false + +/-- The parent, its head, and a third query use different legacy radii. +Memoizing the head must preserve even a preexisting parent digest. -/ +private def headWhnfLegacyKeys (retained : Bool) : Bool := + let sort := KExpr.mkSort (m := .anon) levelOne + let function := KExpr.mkLam () () sort (.mkLam () () sort (.mkVar 2 ())) + let functionType := KExpr.mkAll () () sort (.mkAll () () sort sort) + let head := KExpr.mkLet () functionType function (.mkVar 0 ()) false + let source := KExpr.mkApp head (.mkVar 2 ()) + let query := KExpr.mkVar (m := .anon) 1 () + let expected := KExpr.mkLam () () sort (.mkVar 1 ()) + let action : RecM .anon Bool := do + for _ in [0, 1, 2] do TcM.pushLocal sort + modify fun state => { state with recFuel := 0, ctxAddrCache := + if retained then (∅ : Std.HashMap (Address × UInt64) Address).insert (state.ctxId, source.lbr) sort.addr else {} } + let before ← get + let .ok parentKey _ := TcM.whnfKey source before | return false + let .ok headKey _ := TcM.whnfKey head before | return false + let .ok queryKey _ := TcM.whnfKey query before | return false + let .ok result after := (RecM.whnfCore source).run (methodsN 1) before | return false + let .ok parentAgain _ := TcM.whnfKey source after | return false + let .ok headAgain _ := TcM.whnfKey head after | return false + let .ok queryAgain _ := TcM.whnfKey query after | return false + let .ok replay reused := (RecM.whnfCore source).run (methodsN 0) after | return false + return source.lbr == 3 && head.lbr == 1 && query.lbr == 2 && sameSourceExpr result expected && replay == result && + parentAgain == parentKey && headAgain == headKey && queryAgain == queryKey && + after.ctxAddrCache.size == 2 && reused.ctxAddrCache.size == 2 && + after.env.whnfCoreCache.size == 2 && after.env.whnfCoreCache[headKey]? == some function && + after.env.whnfCoreCache[parentKey]? == some result && after.ctxId == before.ctxId && + after.ctx.size == before.ctx.size && after.recFuel == 0 && + (!retained || parentKey.2 == sort.addr) + match TcM.runRec action (TcState.newLazyAnon {}) with + | .ok passed _ => passed + | .error _ _ => false + private def letWhnfCases : TestSeq := [WhnfWarmLayer.cold, .core, .noDelta, .full].foldl (fun suite layer => suite ++ test s!"let WHNF: layer {reprStr (match layer with | .cold => 0 | .core => 1 | .noDelta => 2 | .full => 3)} preserves exact maps across let-beta-let reduction" @@ -3873,8 +3987,10 @@ private def letWhnfCases : TestSeq := allSucceeded (sortExposureEnvironment (.succ .zero) 0 true).1 9 { clearEvery } && allSucceeded (sortExposureEnvironment (.var 0) 1 true).1 9 { clearEvery }) -private def sortExposureFailures (withZeta : Bool := false) : Bool := - let betaPi := Ixon.Expr.app (.leanLam (.sort 1) (.leanAll (.var 0) (.var 1))) (.sort 0) +private def sortExposureFailures (withZeta : Bool := false) (withHead : Bool := false) : Bool := + let function := Ixon.Expr.leanLam (.sort 1) (.leanAll (.var 0) (.var 1)) + let head := if withHead then Ixon.Expr.letE false (.leanAll (.sort 1) (.sort 1)) function (.var 0) else function + let betaPi := Ixon.Expr.app head (.sort 0) let betaPi := if withZeta then Ixon.Expr.letE false (.sort 1) betaPi (.var 0) else betaPi let (env, carrierAddr) := storeConst {} ⟨.axio ⟨false, 0, betaPi⟩, #[], #[], #[.zero, .succ .zero]⟩ @@ -3919,6 +4035,29 @@ private def sortExposureCases : TestSeq := ++ test "let sort exposure: a let returning a Pi is rejected and failed binder scopes restore locals" (sortExposureFailures true) +private def headWhnfCases : TestSeq := + (List.range 3).foldl (fun suite depth => suite ++ + [false, true].foldl (fun suite warm => suite ++ + test s!"application head WHNF: depth {depth}, warm {warm}, preserves nested writes and both fuel bounds" + ([false, true].all fun typeLevel => [WhnfFlags.FULL, .DEF_EQ_CORE].all fun flags => + (List.range 3).all fun shape => [false, true].all fun native => + headWhnfLoopResult depth typeLevel flags shape warm native)) .done) + (test "application head WHNF: distinct legacy radii retain all keys through head memoization" + ([false, true].all headWhnfLegacyKeys)) + ++ test "application head sort exposure: nested callbacks retain all binder child types across cache layers" + ([false, true].all fun typeLevel => [false, true].all fun instrumented => [false, true].all fun noAccel => + [0, 1, 2].all fun lowerWarm => sortExposureInferencePaths typeLevel false instrumented noAccel lowerWarm false true) + ++ test "application head sort exposure: outer hits replay without recursive methods or fuel" + ([false, true].all fun typeLevel => [false, true].all fun instrumented => [false, true].all fun noAccel => + sortExposureInferencePaths typeLevel true instrumented noAccel 0 false true) + ++ test "application head sort exposure: nested heads and lets reach parameterized declaration admission" + ([0, 1].all fun clearEvery => [false, true].all fun withZeta => + allSucceeded (sortExposureEnvironment .zero 0 withZeta true).1 9 { clearEvery } && + allSucceeded (sortExposureEnvironment (.succ .zero) 0 withZeta true).1 9 { clearEvery } && + allSucceeded (sortExposureEnvironment (.var 0) 1 withZeta true).1 9 { clearEvery }) + ++ test "application head sort exposure: a returned Pi is rejected and failed scopes restore locals" + (sortExposureFailures false true) + private def letCases : TestSeq := test "let inference: dependent type substitution retains exact child caches, replay, and fresh rebuilding" letExactCacheHistory @@ -4065,6 +4204,7 @@ public def suite : List TestSeq := polymorphicApplicationCases, constantCacheCases, cacheInvariantCases, recursiveCacheCases, lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, sourceAgreementCases, sourceCacheCases, compositeCacheCases, letCases, sortExposureCases, mixedWhnfCases, letWhnfCases, + headWhnfCases, polymorphicDefinitionCases] end Tests.Kernel.Consistency diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 251b2c96a..9380d7984 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -601,7 +601,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 1,454 exact theorem boundaries. The production +The consistency target checks 1,518 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -753,7 +753,8 @@ substitution and suffix candidates. `BetaWhnfSource.construct` reconstructs the complete path and iteration count from an actual successful bounded run; intermediate expressions, readings, states, and a separate termination witness are no longer supplied. The raw branch resource restricts the path to -head beta and explicit lets followed by a sort, Pi, or lambda. +head beta, explicit lets, and the recursive application heads described below, +followed by a sort, Pi, or lambda. The three cache-layer `exists_of_success` theorems observe their actual cache lookups and recover the successful lower calls, including the public fuel charge. Populated entries still require retained producing executions. @@ -771,15 +772,32 @@ reader already substitutes the let value into its body. The annotated term and its complete original typing derivation therefore pass unchanged to the next beta step. Lets may precede, follow, or alternate with beta prefixes; both count toward the actual loop bound, including the final `.done` -iteration. Each step changes only the intern table. +iteration. These direct substitutions change only the intern table. `StructuralWhnfEntry` derives the nonleaf and non-transient facts for both source forms. The existing three cache executions, publication/replay proofs, Pi/sort exposures, declaration conversion, and success-based constructors now include these mixed paths. All 27 added raw boundaries forbid the semantic hereditary invariant; the existing roots retain their exact axiom profiles. -No new axiom or native proof is introduced. Reducing stored local let values and -reducing a non-lambda application head still require further invariants and -proofs; this extension covers explicit-let structural steps. +No new axiom or native proof is introduced. +`BetaHeadReduction` extends the original trace with recursive application-head +calls. A callback either executes its own bounded path or reuses a retained +producing execution. Its full or cheap core cache publishes even during native +reduction. `BetaPrefixPlan` computes the beta continuation in the returned state, +and `AppSpineSource` preserves the raw argument boundary even when reading a let +head exposes another application. `SynthesisBetaTyping.mapHead` derives the head +conversion from the original derivation while retaining every argument check +and type conversion. Each callback uses the predecessor method table; its loop +budget is independent of the enclosing step count. +The generalized cache frame preserves every query's key as recursive calls +memoize different legacy context radii, including previously cached digests. +`BetaWhnfSource.construct` reconstructs cold explicit-let heads that return +lambdas, including nested head calls, using finite raw resources and successful +execution. The existing inference, Pi/sort exposure, and declaration conversion +theorems consume those traces. Retained cached heads are supported by the raw +trace and its automatic semantic annotation; constructing them automatically +from source resources remains open. Stored local let values and other head +reducers also remain outside this construction. All 64 added boundaries forbid +the semantic hereditary invariant and introduce no axiom or native proof. `SynthesisInference.cached` retains the original tree behind an inference cache hit. Its soundness and beta derivations reuse the actual lambda-body, dependent codomain, and argument checks. `reuseFull` derives the cached result @@ -1032,7 +1050,11 @@ Ten let-WHNF regressions cover alternating let/beta steps, dependent results, exact loop exhaustion, all cache layers and native guards, zero-method replay, original inference-cache types during sort exposure, declaration universe parameters, and rejection with local-scope cleanup. -The unit suite contains 748 checks. The anonymous differential additionally +Eleven application-head regressions cover nested callbacks, cold and warmed +full/cheap caches, native reduction, separate method and loop exhaustion, +partial cache writes on failure, distinct legacy key radii, binder inference, +parameterized declaration admission, and rejection of a Pi where a sort is required. +The unit suite contains 759 checks. The anonymous differential additionally serializes eleven cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. diff --git a/docs/theory.md b/docs/theory.md index d72f047a3..06ac95c85 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -112,8 +112,14 @@ preserves the scoped reading and annotated term, so every later beta step keeps the derivation obtained from the original check. The actual loop counts each let and beta iteration before its final unchanged step. Cache execution, Pi/sort exposure, and declaration conversion all include these mixed paths. -Stored local let values and reduction of non-lambda application heads remain -outside this construction. +Application heads can now reduce recursively before the enclosing beta step. +The same trace retains each callback's actual cache writes or earlier producing +execution, and its method depth is separate from the outer loop bound. +The original derivation supplies the head conversion and preserves the checks +of every argument. Source reconstruction covers cold explicit-let heads that +return lambdas, including nested head calls. Stored local let values and other +head reducers remain outside that construction; automatic reconstruction of +cached head calls still requires further work. Constructing initial inference resources for arbitrary accepted programs, general WHNF cache origins, the remaining reducers, and general conversion remain open. From c073407397e425ae3c506976016f9ef92b31c2d9 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 17:46:14 -0400 Subject: [PATCH 47/63] Rebuild cached WHNF annotations from current source readings --- Ix/Kernel/Verify/Consistency.lean | 15 +- Ix/Kernel/Verify/Consistency/Audit.lean | 44 ++++- .../Consistency/BetaCacheConstruction.lean | 120 +++++++++--- .../Consistency/BetaCacheReannotation.lean | 76 ++++++++ .../Consistency/BetaExposureConstruction.lean | 4 +- .../Consistency/BetaHeadConstruction.lean | 20 +- .../Verify/Consistency/BetaHeadOrigin.lean | 111 +++++++++++ .../Verify/Consistency/BetaReannotation.lean | 173 ++++++++++++++++++ .../Consistency/BetaSourceInference.lean | 8 +- .../Consistency/BetaTraceConstruction.lean | 170 +++++++++++------ .../Verify/Consistency/BetaWhnfPlan.lean | 4 +- Tests/Ix/Kernel/Consistency.lean | 20 +- docs/kernel-verification.md | 42 +++-- docs/theory.md | 12 +- 14 files changed, 698 insertions(+), 121 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/BetaCacheReannotation.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaHeadOrigin.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaReannotation.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index ab9c76a3a..1c85de63d 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -50,6 +50,9 @@ import Ix.Kernel.Verify.Consistency.LetWhnfPlan import Ix.Kernel.Verify.Consistency.BetaWhnfPlan import Ix.Kernel.Verify.Consistency.BetaHeadStepPlan import Ix.Kernel.Verify.Consistency.BetaHeadConstruction +import Ix.Kernel.Verify.Consistency.BetaReannotation +import Ix.Kernel.Verify.Consistency.BetaHeadOrigin +import Ix.Kernel.Verify.Consistency.BetaCacheReannotation import Ix.Kernel.Verify.Consistency.SynthesisAppCongruence import Ix.Kernel.Verify.Consistency.BetaPublicWhnf import Ix.Kernel.Verify.Consistency.WhnfCacheFrame @@ -179,10 +182,14 @@ annotated term, so later beta steps retain their existing typing derivation. This also covers let-based Pi/sort exposure and declaration conversion. Recursive application heads now use the same trace, with separate method depth and loop bounds and actual full/cheap cache writes or retained hits. -Source reconstruction includes cold explicit-let heads returning lambdas, -including nested head calls. The original typing derivation supplies the -head conversion while preserving every checked argument. Key frames preserve -all queries when a recursive call memoizes a different legacy context radius. +Source reconstruction includes explicit-let heads returning lambdas, including +nested fresh and cached head calls. Retained producers are reannotated from +the current reading, even across different resolvers and local contexts, +while preserving the actual result, state, fuel, and iteration count. The +same reconstruction applies at all three cache layers, and publication +derives resources for later replay. The original typing derivation supplies +the head conversion while preserving every checked argument. Key frames +preserve all queries when a recursive call memoizes a different legacy context radius. Only an outer miss charges shared fuel. Pi exposure uses the same complete cache-layer execution. Application inference uses that exposure between argument checks and derives the type conversion diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index f52bfb709..e8c7d777f 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -889,6 +889,45 @@ private def headWhnfExecutionRoots : Array Lean.Name := #[ ``SynthesisBetaWhnfTrace.toRawTrace ] +/-- Replay reconstructs annotations from the current source reading. Neither +the raw producer nor its reconstruction may assume semantic typing. -/ +private def betaReannotationExprRoots : Array Lean.Name := #[ + ``BetaPrefixPlan.sourceResources, + ``BetaPrefixPlan.sourceOutput, + ``BetaStepPlan.selected, + ``BetaStepPlan.sourceResources, + ``BetaStepPlan.sourceOutput, + ``BetaStepPlan.sourceAfter, + ``BetaHeadStepPlan.sourceSpine, + ``BetaHeadStepPlan.sourceResources, + ``BetaHeadStepPlan.sourceOutput, + ``BetaHeadStepPlan.sourceAfter, + ``BetaHeadStepSource.constructOfEntry +] + +private def betaReannotationExecutionRoots : Array Lean.Name := #[ + ``BetaWhnfTrace.reannotate, + ``BetaHeadReduction.reannotate, + ``BetaHeadCacheOrigin, + ``BetaHeadReduction.origin, + ``BetaHeadReduction.published, + ``BetaHeadReduction.replay, + ``BetaHeadReduction.replay_run, + ``BetaHeadCacheOrigin.replay, + ``BetaWhnfSource.HeadOrigins, + ``BetaWhnfSource.HeadOrigins.absent, + ``BetaWhnfSource.HeadOrigins.ofCall, + ``BetaWhnfSource.HeadOrigins.replay, + ``BetaCoreExecution.reannotate, + ``BetaNoDeltaExecution.reannotate, + ``BetaPublicExecution.reannotate, + ``BetaHeadReduction.exists_of_success, + ``BetaWhnfSource.HeadOrigins.of_success, + ``BetaWhnfSource.CoreResources.ofExecution, + ``BetaWhnfSource.NoDeltaResources.ofExecution, + ``BetaWhnfSource.PublicResources.ofExecution +] + private def localScopeFrameRoots : Array Lean.Name := #[ ``LocalContext.Equiv.refl, ``LocalContext.Equiv.symm, ``LocalContext.Equiv.trans, ``LocalContext.Equiv.size, ``LocalContext.Equiv.find?, ``LocalContext.Equiv.wf, @@ -1504,7 +1543,8 @@ def roots : Array RootAllowance := #[ ++ (mixedCacheFrameRoots ++ headWhnfFrameRoots ++ #[``betaWhnfCharge_success]).map (fun root => { root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) - ++ (mixedCacheKeyRoots ++ betaSourceExprRoots ++ letWhnfExprRoots ++ headWhnfExprRoots).map (fun root => { + ++ (mixedCacheKeyRoots ++ betaSourceExprRoots ++ letWhnfExprRoots ++ headWhnfExprRoots ++ + betaReannotationExprRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ #[``SynthesisInference.beta_core_execution_sound, ``SynthesisInference.beta_noDelta_execution_sound, @@ -1514,7 +1554,7 @@ def roots : Array RootAllowance := #[ root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) ++ (recursiveLetShapeRoots ++ sortExposureRoots ++ mixedCacheExecutionRoots ++ betaSourceExecutionRoots ++ - letWhnfExecutionRoots ++ headWhnfExecutionRoots).map (fun root => { + letWhnfExecutionRoots ++ headWhnfExecutionRoots ++ betaReannotationExecutionRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ (localScopeFrameRoots ++ localStateFrameRoots ++ recursiveStateFrameRoots ++ ingressFrameRoots ++ diff --git a/Ix/Kernel/Verify/Consistency/BetaCacheConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaCacheConstruction.lean index d2ebb5e92..c9afb021f 100644 --- a/Ix/Kernel/Verify/Consistency/BetaCacheConstruction.lean +++ b/Ix/Kernel/Verify/Consistency/BetaCacheConstruction.lean @@ -4,6 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.BetaTraceConstruction +import Ix.Kernel.Verify.Consistency.BetaCacheReannotation /-! Construct the three cache-layer witnesses from actual successful calls. Inputs describe finite raw resources and the origins of stored entries; the @@ -17,38 +18,96 @@ universe u namespace BetaWhnfSource -/-- A structural hit has an actual producing execution. A miss needs only -the finite resources along the source's computed beta orbit. -/ +/-- A structural hit has an actual producing execution whose annotations +need not match the current check. A miss needs the finite raw orbit resources. -/ structure CoreResources {β : Type u} (resolve : Address → Option (ConstRef β)) - (locals : List FVarId) (fuel : Nat) (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) : Prop where + (locals : List FVarId) (fuel : Nat) (before : TcState .anon) (source : KExpr .anon) : Prop where origins : ∀ cached, (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? = some cached → - ∃ originFuel originBefore target, - ∃ _ : BetaCoreExecution resolve locals originFuel originBefore source term cached target, + ∃ originResolve : Address → Option (ConstRef β), ∃ originLocals originFuel originBefore originTerm target, + ∃ _ : BetaCoreExecution originResolve originLocals originFuel originBefore source originTerm cached target, originBefore.env.intern.WF cold : (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? = none → - Resources (fuel + 1) .FULL maxWhnfCoreFuel.toNat (betaWhnfKey source before).2 source + Resources resolve locals (fuel + 1) .FULL maxWhnfCoreFuel.toNat (betaWhnfKey source before).2 source structure NoDeltaResources {β : Type u} (resolve : Address → Option (ConstRef β)) - (locals : List FVarId) (fuel : Nat) (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) : Prop where + (locals : List FVarId) (fuel : Nat) (before : TcState .anon) (source : KExpr .anon) : Prop where origins : ∀ cached, (betaWhnfKey source before).2.env.whnfNoDeltaCache[(betaWhnfKey source before).1]? = some cached → - ∃ originFuel originBefore target, - ∃ _ : BetaNoDeltaExecution resolve locals originFuel originBefore source term cached target, + ∃ originResolve : Address → Option (ConstRef β), ∃ originLocals originFuel originBefore originTerm target, + ∃ _ : BetaNoDeltaExecution originResolve originLocals originFuel originBefore source originTerm cached target, originBefore.env.intern.WF cold : (betaWhnfKey source before).2.env.whnfNoDeltaCache[(betaWhnfKey source before).1]? = none → - CoreResources resolve locals fuel (betaWhnfKey source before).2 source term + CoreResources resolve locals fuel (betaWhnfKey source before).2 source structure PublicResources {β : Type u} (resolve : Address → Option (ConstRef β)) - (locals : List FVarId) (fuel : Nat) (before : TcState .anon) (source : KExpr .anon) (term : AExpr β) : Prop where + (locals : List FVarId) (fuel : Nat) (before : TcState .anon) (source : KExpr .anon) : Prop where origins : ∀ cached, (BetaPublicWhnf.outerKey source before).2.env.whnfCache[(BetaPublicWhnf.outerKey source before).1]? = some cached → - ∃ originFuel originBefore target, - ∃ _ : BetaPublicExecution resolve locals originFuel originBefore source term cached target, + ∃ originResolve : Address → Option (ConstRef β), ∃ originLocals originFuel originBefore originTerm target, + ∃ _ : BetaPublicExecution originResolve originLocals originFuel originBefore source originTerm cached target, originBefore.env.intern.WF cold : (BetaPublicWhnf.outerKey source before).2.env.whnfCache[ (BetaPublicWhnf.outerKey source before).1]? = none → - NoDeltaResources resolve locals fuel (betaWhnfCharge (BetaPublicWhnf.outerKey source before).2) source term + NoDeltaResources resolve locals fuel (betaWhnfCharge (BetaPublicWhnf.outerKey source before).2) source + +theorem CoreResources.ofExecution {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (execution : BetaCoreExecution resolve locals fuel before source term result target) + (coherent : before.env.intern.WF) (nextFuel : Nat) : + CoreResources resolve locals nextFuel execution.after source := by + have hit : (betaWhnfKey source execution.after).2.env.whnfCoreCache[ + (betaWhnfKey source execution.after).1]? = some result := by + rw [betaWhnfKey_environment, execution.stable_key] + exact execution.published + constructor + · intro cached found + rw [hit] at found + cases Option.some.inj found + exact ⟨resolve, locals, fuel, before, term, target, execution, coherent⟩ + · intro miss + rw [hit] at miss + cases miss + +theorem NoDeltaResources.ofExecution {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) + (coherent : before.env.intern.WF) (inactive : before.inNativeReduce = false) (nextFuel : Nat) : + NoDeltaResources resolve locals nextFuel execution.after source := by + have hit : (betaWhnfKey source execution.after).2.env.whnfNoDeltaCache[ + (betaWhnfKey source execution.after).1]? = some result := by + rw [betaWhnfKey_environment, execution.stable_key] + exact execution.published inactive + constructor + · intro cached found + rw [hit] at found + cases Option.some.inj found + exact ⟨resolve, locals, fuel, before, term, target, execution, coherent⟩ + · intro miss + rw [hit] at miss + cases miss + +theorem PublicResources.ofExecution {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (execution : BetaPublicExecution resolve locals fuel before source term result target) + (coherent : before.env.intern.WF) (inactive : before.inNativeReduce = false) (nextFuel : Nat) : + PublicResources resolve locals nextFuel execution.after source := by + have hit : (BetaPublicWhnf.outerKey source execution.after).2.env.whnfCache[ + (BetaPublicWhnf.outerKey source execution.after).1]? = some result := by + rw [BetaPublicWhnf.outerKey, betaWhnfKey_environment, (betaWhnfPrefix_fields _).1, + betaWhnfKey_prefix, execution.stable_key] + exact execution.published inactive + constructor + · intro cached found + rw [hit] at found + cases Option.some.inj found + exact ⟨resolve, locals, fuel, before, term, target, execution, coherent⟩ + · intro miss + rw [hit] at miss + cases miss end BetaWhnfSource @@ -93,7 +152,7 @@ theorem BetaCoreExecution.exists_of_success {β : Type u} {resolve : Address → {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} {term : AExpr β} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.CoreResources resolve locals fuel before source term) + (resources : BetaWhnfSource.CoreResources resolve locals fuel before source) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) (accepted : (RecM.whnfCore source).run (methodsN (fuel + 1)) before = .ok result after) : @@ -102,17 +161,18 @@ theorem BetaCoreExecution.exists_of_success {β : Type u} {resolve : Address → cases observed : (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? with | none => exact exists_of_miss_success chosen (resources.cold observed) reading coherent observed accepted | some cached => - obtain ⟨originFuel, originBefore, target, origin, initial⟩ := resources.origins cached observed - let execution : BetaCoreExecution resolve locals fuel before source term cached target := - .cached origin initial observed + obtain ⟨originResolve, originLocals, originFuel, originBefore, originTerm, target, origin, initial⟩ := resources.origins cached observed + let rebuilt := origin.reannotate reading initial + let execution : BetaCoreExecution resolve locals fuel before source term cached rebuilt.1 := + .cached rebuilt.2.1 initial observed obtain ⟨rfl, finalEq⟩ := EStateM.Result.ok.inj (execution.run.symm.trans accepted) - exact ⟨target, execution, finalEq⟩ + exact ⟨rebuilt.1, execution, finalEq⟩ theorem BetaNoDeltaExecution.exists_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} {term : AExpr β} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.NoDeltaResources resolve locals fuel before source term) + (resources : BetaWhnfSource.NoDeltaResources resolve locals fuel before source) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) (accepted : (RecM.whnfNoDelta source).run (methodsN (fuel + 1)) before = .ok result after) : @@ -135,17 +195,18 @@ theorem BetaNoDeltaExecution.exists_of_success {β : Type u} {resolve : Address obtain ⟨rfl, finalEq⟩ := EStateM.Result.ok.inj (execution.run.symm.trans accepted) exact ⟨target, execution, finalEq⟩ | some cached => - obtain ⟨originFuel, originBefore, target, origin, initial⟩ := resources.origins cached observed - let execution : BetaNoDeltaExecution resolve locals fuel before source term cached target := - .cached origin initial observed + obtain ⟨originResolve, originLocals, originFuel, originBefore, originTerm, target, origin, initial⟩ := resources.origins cached observed + let rebuilt := origin.reannotate reading initial + let execution : BetaNoDeltaExecution resolve locals fuel before source term cached rebuilt.1 := + .cached rebuilt.2.1 initial observed obtain ⟨rfl, finalEq⟩ := EStateM.Result.ok.inj (execution.run.symm.trans accepted) - exact ⟨target, execution, finalEq⟩ + exact ⟨rebuilt.1, execution, finalEq⟩ theorem BetaPublicExecution.exists_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} {term : AExpr β} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.PublicResources resolve locals fuel before source term) + (resources : BetaWhnfSource.PublicResources resolve locals fuel before source) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) (accepted : (RecM.whnf source).run (methodsN (fuel + 1)) before = .ok result after) : @@ -173,10 +234,11 @@ theorem BetaPublicExecution.exists_of_success {β : Type u} {resolve : Address obtain ⟨rfl, finalEq⟩ := EStateM.Result.ok.inj (execution.run.symm.trans accepted) exact ⟨target, execution, finalEq⟩ | some cached => - obtain ⟨originFuel, originBefore, target, origin, initial⟩ := resources.origins cached observed - let execution : BetaPublicExecution resolve locals fuel before source term cached target := - .cached origin initial observed + obtain ⟨originResolve, originLocals, originFuel, originBefore, originTerm, target, origin, initial⟩ := resources.origins cached observed + let rebuilt := origin.reannotate reading initial + let execution : BetaPublicExecution resolve locals fuel before source term cached rebuilt.1 := + .cached rebuilt.2.1 initial observed obtain ⟨rfl, finalEq⟩ := EStateM.Result.ok.inj (execution.run.symm.trans accepted) - exact ⟨target, execution, finalEq⟩ + exact ⟨rebuilt.1, execution, finalEq⟩ end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaCacheReannotation.lean b/Ix/Kernel/Verify/Consistency/BetaCacheReannotation.lean new file mode 100644 index 000000000..71921e6f2 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaCacheReannotation.lean @@ -0,0 +1,76 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaCacheExecution +import Ix.Kernel.Verify.Consistency.BetaReannotation + +/-! Rebuild the annotations of a retained WHNF execution at every cache +layer. Its original returned expression and complete final state are preserved. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + +def BetaCoreExecution.reannotate {γ : Type v} {originResolve : Address → Option (ConstRef γ)} + {originLocals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr γ} + (execution : BetaCoreExecution originResolve originLocals fuel before source term result target) + {current : AExpr β} (reading : readScopedExpr? resolve locals source = some current.erase) + (coherent : before.env.intern.WF) : + Σ output, { rebuilt : BetaCoreExecution resolve locals fuel before source current result output // + rebuilt.after = execution.after } := + match execution with + | .reduce path moving enough miss terminal => + let rebuilt := path.reannotate reading ((betaWhnfKey_environment _ _).symm ▸ coherent) + ⟨rebuilt.1, ⟨.reduce rebuilt.2 moving enough miss terminal, rfl⟩⟩ + | .cached origin initial hit => + let rebuilt := BetaCoreExecution.reannotate origin reading initial + ⟨rebuilt.1, ⟨.cached rebuilt.2.1 initial hit, rfl⟩⟩ +termination_by structural execution + +def BetaNoDeltaExecution.reannotate {γ : Type v} {originResolve : Address → Option (ConstRef γ)} + {originLocals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr γ} + (execution : BetaNoDeltaExecution originResolve originLocals fuel before source term result target) + {current : AExpr β} (reading : readScopedExpr? resolve locals source = some current.erase) + (coherent : before.env.intern.WF) : + Σ output, { rebuilt : BetaNoDeltaExecution resolve locals fuel before source current result output // + rebuilt.after = execution.after } := + match execution with + | .reduce core miss => + let rebuilt := core.reannotate reading ((betaWhnfKey_environment _ _).symm ▸ coherent) + ⟨rebuilt.1, ⟨.reduce rebuilt.2.1 miss, + congrArg (BetaCacheExecution.writeNoDelta (betaWhnfKey source before).1 result) rebuilt.2.2⟩⟩ + | .cached origin initial hit => + let rebuilt := BetaNoDeltaExecution.reannotate origin reading initial + ⟨rebuilt.1, ⟨.cached rebuilt.2.1 initial hit, rfl⟩⟩ +termination_by structural execution + +def BetaPublicExecution.reannotate {γ : Type v} {originResolve : Address → Option (ConstRef γ)} + {originLocals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr γ} + (execution : BetaPublicExecution originResolve originLocals fuel before source term result target) + {current : AExpr β} (reading : readScopedExpr? resolve locals source = some current.erase) + (coherent : before.env.intern.WF) : + Σ output, { rebuilt : BetaPublicExecution resolve locals fuel before source current result output // + rebuilt.after = execution.after } := + match execution with + | .reduce inner miss fuelAvailable => by + have initial : (betaWhnfCharge (BetaPublicWhnf.outerKey source before).2).env.intern.WF := by + simpa only [BetaPublicWhnf.outerKey, betaWhnfCharge_fields, betaWhnfKey_environment, + betaWhnfPrefix_fields] using coherent + let rebuilt := inner.reannotate reading initial + exact ⟨rebuilt.1, ⟨.reduce rebuilt.2.1 miss fuelAvailable, + congrArg (BetaCacheExecution.writeFull (BetaPublicWhnf.outerKey source before).1 result) rebuilt.2.2⟩⟩ + | .cached origin initial hit => + let rebuilt := BetaPublicExecution.reannotate origin reading initial + ⟨rebuilt.1, ⟨.cached rebuilt.2.1 initial hit, rfl⟩⟩ +termination_by structural execution + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaExposureConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaExposureConstruction.lean index 614ec2698..6946126b1 100644 --- a/Ix/Kernel/Verify/Consistency/BetaExposureConstruction.lean +++ b/Ix/Kernel/Verify/Consistency/BetaExposureConstruction.lean @@ -38,7 +38,7 @@ theorem BetaSortExposure.exists_of_success {β : Type u} {resolve : Address → {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source : KExpr .anon} {term : AExpr β} {level : KUniv .anon} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.PublicResources resolve locals fuel before source term) + (resources : BetaWhnfSource.PublicResources resolve locals fuel before source) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) (accepted : (RecM.ensureSortDirect source).run (methodsN (fuel + 1)) before = .ok level after) : @@ -56,7 +56,7 @@ theorem BetaPiExposure.exists_of_success {β : Type u} {resolve : Address → Op {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source rawDomain rawBody : KExpr .anon} {term : AExpr β} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.PublicResources resolve locals fuel before source term) + (resources : BetaWhnfSource.PublicResources resolve locals fuel before source) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) (accepted : (RecM.ensureForallDirect source).run (methodsN (fuel + 1)) before = .ok (rawDomain, rawBody) after) : diff --git a/Ix/Kernel/Verify/Consistency/BetaHeadConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaHeadConstruction.lean index 1d8c01c38..facdaae05 100644 --- a/Ix/Kernel/Verify/Consistency/BetaHeadConstruction.lean +++ b/Ix/Kernel/Verify/Consistency/BetaHeadConstruction.lean @@ -48,9 +48,11 @@ structure Witness {β : Type u} (resolve : Address → Option (ConstRef β)) (lo result : plan.result = (BetaPrefixSource.output head source.collectSpine.2 before).1 after : plan.after = BetaPrefixSource.after head source.collectSpine.2 before -def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} +def constructOfEntry {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {before : TcState .anon} {source head : KExpr .anon} {term target : AExpr β} - (chosen : selected source = true) (reading : readScopedExpr? resolve locals source = some term.erase) + (application : ∃ fn arg info, source = .app fn arg info) + (entry : StructuralWhnfEntry source.collectSpine.1) + (reading : readScopedExpr? resolve locals source = some term.erase) (returned : BetaPrefixSource.selected head = true) (headReads : readScopedExpr? resolve locals head = some target.erase) (resources : BetaPrefixSource.Resources head source.collectSpine.2 before) : @@ -67,9 +69,19 @@ def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals headTerm := (AppSpineSource.parts (.app fn arg info) term).1, modelSource := by rw [built.modelArgs]; exact parsed.1, spine := Prod.ext rfl built.rawArgs.symm, - headEntry := LetStepSource.selected_entry chosen, sourceHeadReads := parsed.2.1 + headEntry := entry, sourceHeadReads := parsed.2.1 }, rfl, rfl, built.raw, built.model, built.result, built.after⟩ - | _ => cases chosen + | _ => exfalso; obtain ⟨_, _, _, impossible⟩ := application; cases impossible + +def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {before : TcState .anon} {source head : KExpr .anon} {term target : AExpr β} + (chosen : selected source = true) (reading : readScopedExpr? resolve locals source = some term.erase) + (returned : BetaPrefixSource.selected head = true) + (headReads : readScopedExpr? resolve locals head = some target.erase) + (resources : BetaPrefixSource.Resources head source.collectSpine.2 before) : + Witness resolve locals before source term head target := + constructOfEntry (selected_app chosen) (LetStepSource.selected_entry (selected_head chosen)) + reading returned headReads resources /-- Successful application evaluation includes a successful head callback. This also rules out method depth zero without an extra resource premise. -/ diff --git a/Ix/Kernel/Verify/Consistency/BetaHeadOrigin.lean b/Ix/Kernel/Verify/Consistency/BetaHeadOrigin.lean new file mode 100644 index 000000000..c19ab34df --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaHeadOrigin.lean @@ -0,0 +1,111 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaReannotation + +/-! Retained cache provenance is independent of a later source annotation. +Each entry stores an actual producing call and its initial intern coherence. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +structure BetaHeadCacheOrigin {β : Type u} (source result : KExpr .anon) where + resolve : Address → Option (ConstRef β) + locals : List FVarId + fuel : Nat + flags : WhnfFlags + before : TcState .anon + after : TcState .anon + term : AExpr β + target : AExpr β + call : BetaHeadReduction resolve locals fuel flags before source term after result target + coherent : before.env.intern.WF + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + +namespace BetaHeadReduction + +def origin {fuel : Nat} {flags : WhnfFlags} {before after : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (call : BetaHeadReduction resolve locals fuel flags before source term after result target) + (coherent : before.env.intern.WF) : BetaHeadCacheOrigin (β := β) source result := + ⟨resolve, locals, fuel, flags, before, after, term, target, call, coherent⟩ + +theorem published {fuel : Nat} {flags : WhnfFlags} {before after : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (call : BetaHeadReduction resolve locals fuel flags before source term after result target) : + BetaCoreCache.lookup flags (betaWhnfKey source after).1 (betaWhnfKey source after).2 = some result := by + simp only [call.frame.keys source, BetaCoreCache.lookup, betaWhnfKey_environment] + cases call with + | reduce path moving enough miss => + simpa only [BetaCoreCache.lookup] using BetaCoreCache.published flags _ result _ + | cached origin coherent hit => + simpa only [BetaCoreCache.lookup, betaWhnfKey_environment] using hit + +def replay {fuel : Nat} {flags : WhnfFlags} {before after : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (call : BetaHeadReduction resolve locals fuel flags before source term after result target) + (coherent : before.env.intern.WF) : + BetaHeadReduction resolve locals 0 flags after source term (betaWhnfKey source after).2 result target := + .cached call coherent call.published + +theorem replay_run {fuel : Nat} {flags : WhnfFlags} {before after : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (call : BetaHeadReduction resolve locals fuel flags before source term after result target) + (coherent : before.env.intern.WF) : + (RecM.whnfCoreWithFlags source flags).run (methodsN 0) after = + .ok result (betaWhnfKey source after).2 := (call.replay coherent).run + +end BetaHeadReduction + +/-- The present source reading rebuilds all annotations in the retained +producer. The observed lookup selects the result and current cache partition. -/ +def BetaHeadCacheOrigin.replay {γ : Type v} {source result : KExpr .anon} + (origin : BetaHeadCacheOrigin (β := γ) source result) + {fuel : Nat} {flags : WhnfFlags} {before : TcState .anon} {current : AExpr β} + (reading : readScopedExpr? resolve locals source = some current.erase) + (hit : BetaCoreCache.lookup flags (betaWhnfKey source before).1 (betaWhnfKey source before).2 = some result) : + Σ target, BetaHeadReduction resolve locals fuel flags before source current (betaWhnfKey source before).2 result target := + let rebuilt := origin.call.reannotate reading origin.coherent + ⟨rebuilt.1, .cached rebuilt.2 origin.coherent hit⟩ + +namespace BetaWhnfSource + +def HeadOrigins {β : Type u} (flags : WhnfFlags) (before : TcState .anon) (source : KExpr .anon) : Prop := + ∀ cached, BetaCoreCache.lookup flags (betaWhnfKey source before).1 (betaWhnfKey source before).2 = some cached → + Nonempty (BetaHeadCacheOrigin (β := β) source cached) + +theorem HeadOrigins.absent {flags : WhnfFlags} {before : TcState .anon} {source : KExpr .anon} + (miss : BetaCoreCache.lookup flags (betaWhnfKey source before).1 (betaWhnfKey source before).2 = none) : + HeadOrigins (β := β) flags before source := by + intro cached found + rw [miss] at found + cases found + +theorem HeadOrigins.ofCall {fuel : Nat} {flags : WhnfFlags} {before after : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (call : BetaHeadReduction resolve locals fuel flags before source term after result target) + (coherent : before.env.intern.WF) : HeadOrigins (β := β) flags after source := by + intro cached found + rw [call.published] at found + cases Option.some.inj found + exact ⟨call.origin coherent⟩ + +/-- Choice only selects a retained producing execution. Its fresh annotation +and replay trace are then constructed by the raw reannotation theorem. -/ +noncomputable def HeadOrigins.replay {flags : WhnfFlags} {before : TcState .anon} + {source result : KExpr .anon} (origins : HeadOrigins (β := β) flags before source) + {fuel : Nat} {current : AExpr β} + (reading : readScopedExpr? resolve locals source = some current.erase) + (hit : BetaCoreCache.lookup flags (betaWhnfKey source before).1 (betaWhnfKey source before).2 = some result) : + Σ target, BetaHeadReduction resolve locals fuel flags before source current (betaWhnfKey source before).2 result target := + (Classical.choice (origins result hit)).replay reading hit + +end BetaWhnfSource + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaReannotation.lean b/Ix/Kernel/Verify/Consistency/BetaReannotation.lean new file mode 100644 index 000000000..d91cf508d --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaReannotation.lean @@ -0,0 +1,173 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaStepConstruction +import Ix.Kernel.Verify.Consistency.BetaHeadConstruction + +/-! Recover fresh source annotations along an existing raw reduction. +Cache provenance concerns the actual expression and execution; the current +source check supplies annotations independently of the producing check. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +namespace BetaPrefixPlan + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {before : TcState .anon} + +theorem sourceResources (plan : BetaPrefixPlan resolve locals before) : + BetaPrefixSource.Resources plan.rawLambda plan.rawArguments before := by + refine ⟨?_, ?_, ?_⟩ + · simpa only [BetaPrefixSource.peeled, rawLambda, plan.peeling] using plan.walkerBounds + · simpa only [BetaPrefixSource.peeled, rawLambda, plan.peeling] using plan.walkerFaithful + · simpa only [BetaPrefixSource.substituted, BetaPrefixSource.peeled, rawLambda, plan.peeling] using plan.suffixFaithful + +theorem sourceOutput (plan : BetaPrefixPlan resolve locals before) : + plan.output = BetaPrefixSource.output plan.rawLambda plan.rawArguments before := by + simp only [BetaPrefixSource.output, BetaPrefixSource.substituted, BetaPrefixSource.peeled, + rawLambda, plan.peeling, output] + +end BetaPrefixPlan + +namespace BetaStepPlan + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {before : TcState .anon} {source : KExpr .anon} {term : AExpr β} + +theorem selected (plan : BetaStepPlan resolve locals before source term) : BetaStepSource.selected source = true := by + simp only [plan.sourceEq, BetaStepSource.selected, plan.spine] + +theorem sourceResources (plan : BetaStepPlan resolve locals before source term) : + BetaStepSource.Resources source before := by + refine ⟨?_, ?_, ?_⟩ + · simpa only [BetaStepSource.peeled, plan.sourceEq, plan.spine, plan.peeling] using plan.walkerBounds + · simpa only [BetaStepSource.peeled, plan.sourceEq, plan.spine, plan.peeling] using plan.walkerFaithful + · simpa only [BetaStepSource.substituted, BetaStepSource.peeled, plan.sourceEq, plan.spine, plan.peeling] + using plan.suffixFaithful + +theorem sourceOutput (plan : BetaStepPlan resolve locals before source term) : + plan.output = BetaStepSource.output source before := by + simp only [BetaStepSource.output, BetaStepSource.substituted, BetaStepSource.peeled, + plan.sourceEq, plan.spine, plan.peeling, output] + +theorem sourceAfter (plan : BetaStepPlan resolve locals before source term) : + plan.after = BetaStepSource.after source before := by + rw [after, BetaStepSource.after, plan.sourceOutput] + +end BetaStepPlan + +namespace BetaHeadStepPlan + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {before : TcState .anon} {source : KExpr .anon} {term : AExpr β} + +theorem sourceSpine (plan : BetaHeadStepPlan resolve locals before source term) : + source.collectSpine = (plan.rawHead, plan.rawArguments) := + (congrArg KExpr.collectSpine plan.sourceEq).trans plan.spine + +theorem sourceResources (plan : BetaHeadStepPlan resolve locals before source term) : + BetaPrefixSource.Resources plan.rawLambda source.collectSpine.2 before := by + rw [plan.sourceSpine] + exact plan.toBetaPrefixPlan.sourceResources + +theorem sourceOutput (plan : BetaHeadStepPlan resolve locals before source term) : + plan.toBetaPrefixPlan.output = BetaPrefixSource.output plan.rawLambda source.collectSpine.2 before := by + rw [plan.sourceSpine] + exact plan.toBetaPrefixPlan.sourceOutput + +theorem sourceAfter (plan : BetaHeadStepPlan resolve locals before source term) : + plan.after = BetaPrefixSource.after plan.rawLambda source.collectSpine.2 before := by + change {before with env := {before.env with intern := plan.toBetaPrefixPlan.output.2}} = _ + rw [plan.sourceOutput] + rfl + +end BetaHeadStepPlan + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + +mutual + +/-- Fresh annotations are recovered from the current reading. The executed +expressions, intermediate states, method depth, and step count stay fixed. -/ +def BetaWhnfTrace.reannotate {γ : Type v} {originResolve : Address → Option (ConstRef γ)} + {originLocals : List FVarId} {fuel steps : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr γ} + (trace : BetaWhnfTrace originResolve originLocals fuel flags steps before source term after result target) + {current : AExpr β} (reading : readScopedExpr? resolve locals source = some current.erase) + (coherent : before.env.intern.WF) : + Σ output, BetaWhnfTrace resolve locals fuel flags steps before source current after result output := + match trace with + | .done finished => ⟨current, .done finished⟩ + | .next plan rest => by + let built := BetaStepSource.construct plan.selected reading plan.sourceResources + have resultEq : built.1.result = plan.result := + (BetaStepSource.construct_result plan.selected reading plan.sourceResources).trans + (congrArg Prod.fst plan.sourceOutput).symm + have afterEq : built.1.after = plan.after := + (BetaStepSource.construct_after plan.selected reading plan.sourceResources).trans plan.sourceAfter.symm + obtain ⟨nextReading, nextCoherent⟩ := built.1.reading coherent + rw [resultEq] at nextReading + rw [afterEq] at nextCoherent + let remaining := BetaWhnfTrace.reannotate rest nextReading nextCoherent + refine ⟨remaining.1, .next built.1 ?_⟩ + rw [afterEq, resultEq] + exact remaining.2 + | .zeta plan rest => by + obtain ⟨nextReading, nextCoherent⟩ := plan.reading reading coherent + let remaining := BetaWhnfTrace.reannotate rest nextReading nextCoherent + exact ⟨remaining.1, .zeta plan remaining.2⟩ + | .head (fuel := depth) (middle := middle) plan call rest => by + have parsed := AppSpineSource.reading reading + have headReading : readScopedExpr? resolve locals plan.rawHead = + some (AppSpineSource.parts source current).1.erase := by + simpa only [plan.sourceSpine] using parsed.2.1 + let head := BetaHeadReduction.reannotate call headReading coherent + obtain ⟨returnedReading, headCoherent⟩ := head.2.reading headReading coherent + let built := BetaHeadStepSource.constructOfEntry + ⟨plan.rawFunction, plan.rawArgument, plan.appInfo, plan.sourceEq⟩ + (by rw [plan.sourceSpine]; exact plan.headEntry) reading + (show BetaPrefixSource.selected plan.rawLambda = true from rfl) + returnedReading plan.sourceResources + have aligned : BetaHeadReduction resolve locals depth flags before built.plan.rawHead built.plan.headTerm + middle built.plan.rawLambda built.plan.modelLambda := by + rw [built.sourceHead, built.sourceTerm, built.resultHead, built.resultTerm, plan.sourceSpine] + exact head.2 + have resultEq : built.plan.result = plan.result := + built.result.trans (congrArg Prod.fst plan.sourceOutput).symm + have afterEq : built.plan.after = plan.after := built.after.trans plan.sourceAfter.symm + obtain ⟨nextReading, nextCoherent⟩ := built.plan.reading headCoherent + rw [resultEq] at nextReading + rw [afterEq] at nextCoherent + let remaining := BetaWhnfTrace.reannotate rest nextReading nextCoherent + refine ⟨remaining.1, .head built.plan aligned ?_⟩ + rw [afterEq, resultEq] + exact remaining.2 +termination_by structural trace + +/-- A retained producing call can be reused with the annotations recovered +from any current reading of its exact source expression. -/ +def BetaHeadReduction.reannotate {γ : Type v} {originResolve : Address → Option (ConstRef γ)} + {originLocals : List FVarId} {fuel : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr γ} + (call : BetaHeadReduction originResolve originLocals fuel flags before source term after result target) + {current : AExpr β} (reading : readScopedExpr? resolve locals source = some current.erase) + (coherent : before.env.intern.WF) : + Σ output, BetaHeadReduction resolve locals fuel flags before source current after result output := + match call with + | .reduce path moving enough miss => + let rebuilt := BetaWhnfTrace.reannotate path reading ((betaWhnfKey_environment _ _).symm ▸ coherent) + ⟨rebuilt.1, .reduce rebuilt.2 moving enough miss⟩ + | .cached origin initial hit => + let rebuilt := BetaHeadReduction.reannotate origin reading initial + ⟨rebuilt.1, .cached rebuilt.2 initial hit⟩ +termination_by structural call + +end + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaSourceInference.lean b/Ix/Kernel/Verify/Consistency/BetaSourceInference.lean index f604d3f96..2263c6de5 100644 --- a/Ix/Kernel/Verify/Consistency/BetaSourceInference.lean +++ b/Ix/Kernel/Verify/Consistency/BetaSourceInference.lean @@ -26,7 +26,7 @@ theorem SynthesisInference.beta_whnf_of_success {β : Type u} {resolve : Address (reading : readScopedExpr? resolve locals source = some term.erase) (inferredRun : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) {reductionFuel : Nat} {flags : WhnfFlags} {before after : TcState .anon} {result : KExpr .anon} - (resources : BetaWhnfSource.Resources (reductionFuel + 1) flags maxWhnfCoreFuel.toNat before source) + (resources : BetaWhnfSource.Resources resolve locals (reductionFuel + 1) flags maxWhnfCoreFuel.toNat before source) (coherent : before.env.intern.WF) (accepted : (RecM.whnfCoreWithFlagsUncached source flags).run (methodsN (reductionFuel + 1)) before = .ok result after) : @@ -48,7 +48,7 @@ theorem SynthesisInference.beta_core_of_success {β : Type u} {resolve : Address (inferredRun : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) {reductionFuel : Nat} {before after : TcState .anon} {result : KExpr .anon} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.CoreResources resolve locals reductionFuel before source term) + (resources : BetaWhnfSource.CoreResources resolve locals reductionFuel before source) (coherent : before.env.intern.WF) (accepted : (RecM.whnfCore source).run (methodsN (reductionFuel + 1)) before = .ok result after) : ∃ target, BetaWhnfTerminal result ∧ readScopedExpr? resolve locals result = some target.erase ∧ @@ -70,7 +70,7 @@ theorem SynthesisInference.beta_noDelta_of_success {β : Type u} {resolve : Addr (inferredRun : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) {reductionFuel : Nat} {before after : TcState .anon} {result : KExpr .anon} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.NoDeltaResources resolve locals reductionFuel before source term) + (resources : BetaWhnfSource.NoDeltaResources resolve locals reductionFuel before source) (coherent : before.env.intern.WF) (accepted : (RecM.whnfNoDelta source).run (methodsN (reductionFuel + 1)) before = .ok result after) : ∃ target, BetaWhnfTerminal result ∧ readScopedExpr? resolve locals result = some target.erase ∧ @@ -92,7 +92,7 @@ theorem SynthesisInference.beta_public_of_success {β : Type u} {resolve : Addre (inferredRun : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) {reductionFuel : Nat} {before after : TcState .anon} {result : KExpr .anon} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.PublicResources resolve locals reductionFuel before source term) + (resources : BetaWhnfSource.PublicResources resolve locals reductionFuel before source) (coherent : before.env.intern.WF) (accepted : (RecM.whnf source).run (methodsN (reductionFuel + 1)) before = .ok result after) : ∃ target, BetaWhnfTerminal result ∧ readScopedExpr? resolve locals result = some target.erase ∧ diff --git a/Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean b/Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean index 3ddf7da27..1261a0758 100644 --- a/Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean +++ b/Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean @@ -5,6 +5,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Verify.Consistency.BetaStepConstruction import Ix.Kernel.Verify.Consistency.BetaHeadConstruction +import Ix.Kernel.Verify.Consistency.BetaHeadOrigin import Ix.Kernel.Verify.Consistency.BetaCacheExecution import Ix.Kernel.Verify.Whnf.Driver.Success @@ -35,46 +36,49 @@ theorem selected_entry {source : KExpr .anon} (chosen : selected source = true) · exact LetStepSource.selected_entry zeta · exact BetaHeadStepSource.selected_entry (by simpa [selected, beta, zeta] using chosen) -/-- Raw resources follow actual method depth and loop fuel. A recursive head -miss gets its own bounded orbit; only successful callbacks require a lambda -and finite resources for its beta continuation. No annotations or intermediate -execution equations are supplied. -/ -def Resources (reductionFuel : Nat) (flags : WhnfFlags) (loopFuel : Nat) +/-- Raw resources follow actual method depth and loop fuel. Hits retain an +earlier producing call independently of its annotations; a head miss gets its +own bounded orbit. Successful callbacks require finite beta continuation +resources, without an intermediate inference or semantic typing premise. -/ +def Resources {β : Type u} (resolve : Address → Option (ConstRef β)) (locals : List FVarId) + (reductionFuel : Nat) (flags : WhnfFlags) (loopFuel : Nat) (before : TcState .anon) (source : KExpr .anon) : Prop := match loopFuel with | 0 => True | loopFuel + 1 => if BetaStepSource.selected source then BetaStepSource.Resources source before ∧ - Resources reductionFuel flags loopFuel (BetaStepSource.after source before) (BetaStepSource.output source before).1 + Resources resolve locals reductionFuel flags loopFuel (BetaStepSource.after source before) (BetaStepSource.output source before).1 else if LetStepSource.selected source then LetStepSource.Resources source before ∧ - Resources reductionFuel flags loopFuel (LetStepSource.after source before) (LetStepSource.output source before).1 + Resources resolve locals reductionFuel flags loopFuel (LetStepSource.after source before) (LetStepSource.output source before).1 else if BetaHeadStepSource.selected source then match reductionFuel with | 0 => True | fuel + 1 => - BetaCoreCache.lookup flags (betaWhnfKey source.collectSpine.1 before).1 - (betaWhnfKey source.collectSpine.1 before).2 = none ∧ - Resources fuel flags maxWhnfCoreFuel.toNat (betaWhnfKey source.collectSpine.1 before).2 source.collectSpine.1 ∧ + HeadOrigins (β := β) flags before source.collectSpine.1 ∧ + (BetaCoreCache.lookup flags (betaWhnfKey source.collectSpine.1 before).1 + (betaWhnfKey source.collectSpine.1 before).2 = none → + Resources resolve locals fuel flags maxWhnfCoreFuel.toNat (betaWhnfKey source.collectSpine.1 before).2 source.collectSpine.1) ∧ ∀ head middle, (RecM.whnfCoreWithFlags source.collectSpine.1 flags).run (methodsN fuel) before = .ok head middle → BetaPrefixSource.selected head = true ∧ BetaPrefixSource.Resources head source.collectSpine.2 middle ∧ - Resources (fuel + 1) flags loopFuel (BetaPrefixSource.after head source.collectSpine.2 middle) + Resources resolve locals (fuel + 1) flags loopFuel (BetaPrefixSource.after head source.collectSpine.2 middle) (BetaPrefixSource.output head source.collectSpine.2 middle).1 else BetaWhnfTerminal source termination_by (reductionFuel, loopFuel) -theorem Resources.first {reductionFuel fuel : Nat} {flags : WhnfFlags} {before : TcState .anon} {source : KExpr .anon} - (resources : Resources reductionFuel flags fuel before source) (positive : 0 < fuel) +theorem Resources.first {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {reductionFuel fuel : Nat} {flags : WhnfFlags} {before : TcState .anon} {source : KExpr .anon} + (resources : Resources resolve locals reductionFuel flags fuel before source) (positive : 0 < fuel) (chosen : BetaStepSource.selected source = true) : BetaStepSource.Resources source before := by cases fuel with | zero => omega | succ fuel => rw [Resources.eq_def] at resources exact (show BetaStepSource.Resources source before ∧ - Resources reductionFuel flags fuel (BetaStepSource.after source before) (BetaStepSource.output source before).1 from by + Resources resolve locals reductionFuel flags fuel (BetaStepSource.after source before) (BetaStepSource.output source before).1 from by simpa only [chosen, if_true] using resources).1 /-- The annotation and iteration count are outputs of reconstruction. @@ -91,10 +95,10 @@ structure Witness {β : Type u} (resolve : Address → Option (ConstRef β)) (lo /-- Successful execution determines both nested head calls and the outer path. Each recursive callback uses the predecessor method table. -/ -def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} +noncomputable def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {reductionFuel loopFuel : Nat} {flags : WhnfFlags} {before after : TcState .anon} {source result : KExpr .anon} {term : AExpr β} - (resources : Resources reductionFuel flags loopFuel before source) + (resources : Resources resolve locals reductionFuel flags loopFuel before source) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) (accepted : (RecM.runBounded (fun current => RecM.whnfCoreWithFlagsStep current flags) @@ -113,10 +117,10 @@ def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals cases impossible | succ fuel => have data : BetaStepSource.Resources source before ∧ - Resources (fuel + 1) flags loopFuel (BetaStepSource.after source before) (BetaStepSource.output source before).1 := by + Resources resolve locals (fuel + 1) flags loopFuel (BetaStepSource.after source before) (BetaStepSource.output source before).1 := by simpa only [chosen, if_true] using resources let first := BetaStepSource.construct chosen reading data.1 - have nextResources : Resources (fuel + 1) flags loopFuel first.1.after first.1.result := by + have nextResources : Resources resolve locals (fuel + 1) flags loopFuel first.1.after first.1.result := by simpa only [first, BetaStepSource.construct_after, BetaStepSource.construct_result] using data.2 obtain ⟨nextReading, nextCoherent⟩ := first.1.reading coherent rw [RecM.runBounded, ReaderT.run_bind] at accepted @@ -127,10 +131,10 @@ def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals Nat.add_lt_add_right rest.enough 1, rest.terminal, fun _ => Nat.zero_lt_succ _⟩ · by_cases zeta : LetStepSource.selected source = true · have data : LetStepSource.Resources source before ∧ - Resources reductionFuel flags loopFuel (LetStepSource.after source before) (LetStepSource.output source before).1 := by + Resources resolve locals reductionFuel flags loopFuel (LetStepSource.after source before) (LetStepSource.output source before).1 := by simpa [chosen, zeta] using resources let first := LetStepSource.construct zeta data.1 - have nextResources : Resources reductionFuel flags loopFuel first.1.after first.1.result := by + have nextResources : Resources resolve locals reductionFuel flags loopFuel first.1.after first.1.result := by simpa only [first, LetStepSource.construct_after, LetStepSource.construct_result] using data.2 obtain ⟨nextReading, nextCoherent⟩ := first.1.reading reading coherent rw [RecM.runBounded, ReaderT.run_bind] at accepted @@ -148,14 +152,16 @@ def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals cases impossible | succ fuel => have data : - BetaCoreCache.lookup flags (betaWhnfKey source.collectSpine.1 before).1 - (betaWhnfKey source.collectSpine.1 before).2 = none ∧ - Resources fuel flags maxWhnfCoreFuel.toNat (betaWhnfKey source.collectSpine.1 before).2 source.collectSpine.1 ∧ + HeadOrigins (β := β) flags before source.collectSpine.1 ∧ + (BetaCoreCache.lookup flags (betaWhnfKey source.collectSpine.1 before).1 + (betaWhnfKey source.collectSpine.1 before).2 = none → + Resources resolve locals fuel flags maxWhnfCoreFuel.toNat + (betaWhnfKey source.collectSpine.1 before).2 source.collectSpine.1) ∧ ∀ head middle, (RecM.whnfCoreWithFlags source.collectSpine.1 flags).run (methodsN fuel) before = .ok head middle → BetaPrefixSource.selected head = true ∧ BetaPrefixSource.Resources head source.collectSpine.2 middle ∧ - Resources (fuel + 1) flags loopFuel (BetaPrefixSource.after head source.collectSpine.2 middle) + Resources resolve locals (fuel + 1) flags loopFuel (BetaPrefixSource.after head source.collectSpine.2 middle) (BetaPrefixSource.output head source.collectSpine.2 middle).1 := by simpa [chosen, zeta, headed] using resources have headChosen := BetaHeadStepSource.selected_head headed @@ -174,37 +180,50 @@ def construct {β : Type u} {resolve : Address → Option (ConstRef β)} {locals cases success | ok head middle => have tailData := data.2.2 head middle called - cases rawRun : (RecM.whnfCoreWithFlagsUncached source.collectSpine.1 flags).run - (methodsN fuel) (betaWhnfKey source.collectSpine.1 before).2 with - | error error failed => - exfalso - obtain ⟨_, success, _⟩ := BetaCoreCache.miss_success flags headEntry data.1 called - rw [rawRun] at success - cases success - | ok returned rawAfter => - let headPath := construct data.2.1 parsed.2.1 headCoherent rawRun - have moving : selected source.collectSpine.1 = true := by - simp [selected, headChosen] - let call := BetaHeadReduction.reduce headPath.trace (headPath.moving moving) headPath.enough data.1 - have same := call.run - rw [called] at same - obtain ⟨rfl, rfl⟩ := EStateM.Result.ok.inj same - have headReading := (call.reading parsed.2.1 coherent).1 - let built := BetaHeadStepSource.construct headed reading tailData.1 headReading tailData.2.1 - have aligned : BetaHeadReduction resolve locals fuel flags before built.plan.rawHead built.plan.headTerm - (BetaCoreCache.write flags (betaWhnfKey source.collectSpine.1 before).1 head rawAfter) - built.plan.rawLambda built.plan.modelLambda := by - rw [built.sourceHead, built.sourceTerm, built.resultHead, built.resultTerm] + let retained : Σ target, BetaHeadReduction resolve locals fuel flags before + source.collectSpine.1 (AppSpineSource.parts source term).1 middle head target := by + cases observed : BetaCoreCache.lookup flags (betaWhnfKey source.collectSpine.1 before).1 + (betaWhnfKey source.collectSpine.1 before).2 with + | some cached => + let call := data.1.replay (fuel := fuel) parsed.2.1 observed + have same := call.2.run + rw [called] at same + obtain ⟨rfl, rfl⟩ := EStateM.Result.ok.inj same exact call - have nextResources : Resources (fuel + 1) flags loopFuel built.plan.after built.plan.result := by - simpa only [built.after, built.result] using tailData.2.2 - obtain ⟨nextReading, nextCoherent⟩ := built.plan.reading (call.reading parsed.2.1 coherent).2 - rw [RecM.runBounded, ReaderT.run_bind] at accepted - change EStateM.bind ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before = _ at accepted - rw [EStateM.bind, built.plan.run aligned.run] at accepted - let rest := construct nextResources nextReading nextCoherent accepted - exact ⟨rest.steps + 1, rest.target, .head built.plan aligned rest.trace, - Nat.add_lt_add_right rest.enough 1, rest.terminal, fun _ => Nat.zero_lt_succ _⟩ + | none => + cases rawRun : (RecM.whnfCoreWithFlagsUncached source.collectSpine.1 flags).run + (methodsN fuel) (betaWhnfKey source.collectSpine.1 before).2 with + | error error failed => + exfalso + obtain ⟨_, success, _⟩ := BetaCoreCache.miss_success flags headEntry observed called + rw [rawRun] at success + cases success + | ok returned rawAfter => + let headPath := construct (data.2.1 observed) parsed.2.1 headCoherent rawRun + have moving : selected source.collectSpine.1 = true := by + simp [selected, headChosen] + let call := BetaHeadReduction.reduce headPath.trace + (headPath.moving moving) headPath.enough observed + have same := call.run + rw [called] at same + obtain ⟨rfl, rfl⟩ := EStateM.Result.ok.inj same + exact ⟨headPath.target, call⟩ + let call := retained.2 + have headReading := (call.reading parsed.2.1 coherent).1 + let built := BetaHeadStepSource.construct headed reading tailData.1 headReading tailData.2.1 + have aligned : BetaHeadReduction resolve locals fuel flags before built.plan.rawHead built.plan.headTerm + middle built.plan.rawLambda built.plan.modelLambda := by + rw [built.sourceHead, built.sourceTerm, built.resultHead, built.resultTerm] + exact call + have nextResources : Resources resolve locals (fuel + 1) flags loopFuel built.plan.after built.plan.result := by + simpa only [built.after, built.result] using tailData.2.2 + obtain ⟨nextReading, nextCoherent⟩ := built.plan.reading (call.reading parsed.2.1 coherent).2 + rw [RecM.runBounded, ReaderT.run_bind] at accepted + change EStateM.bind ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before = _ at accepted + rw [EStateM.bind, built.plan.run aligned.run] at accepted + let rest := construct nextResources nextReading nextCoherent accepted + exact ⟨rest.steps + 1, rest.target, .head built.plan aligned rest.trace, + Nat.add_lt_add_right rest.enough 1, rest.terminal, fun _ => Nat.zero_lt_succ _⟩ · have terminal : BetaWhnfTerminal source := by simpa [chosen, zeta, headed] using resources have finished := terminal.core_step (methodsN reductionFuel) before flags @@ -219,13 +238,54 @@ decreasing_by all_goals simp_wf; omega end BetaWhnfSource +/-- A successful structural call constructs its provenance at the actual +method depth, including zero-depth hits and let-only misses. -/ +theorem BetaHeadReduction.exists_of_success {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {flags : WhnfFlags} {before after : TcState .anon} + {source result : KExpr .anon} {term : AExpr β} + (chosen : BetaWhnfSource.selected source = true) + (origins : BetaWhnfSource.HeadOrigins (β := β) flags before source) + (cold : BetaCoreCache.lookup flags (betaWhnfKey source before).1 (betaWhnfKey source before).2 = none → + BetaWhnfSource.Resources resolve locals fuel flags maxWhnfCoreFuel.toNat (betaWhnfKey source before).2 source) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) + (accepted : (RecM.whnfCoreWithFlags source flags).run (methodsN fuel) before = .ok result after) : + ∃ target, Nonempty (BetaHeadReduction resolve locals fuel flags before source term after result target) := by + cases observed : BetaCoreCache.lookup flags (betaWhnfKey source before).1 (betaWhnfKey source before).2 with + | some cached => + let call := origins.replay (fuel := fuel) reading observed + obtain ⟨rfl, rfl⟩ := EStateM.Result.ok.inj (call.2.run.symm.trans accepted) + exact ⟨call.1, ⟨call.2⟩⟩ + | none => + obtain ⟨reduced, rawRun, afterEq⟩ := + BetaCoreCache.miss_success flags (BetaWhnfSource.selected_entry chosen) observed accepted + let path := BetaWhnfSource.construct (cold observed) reading ((betaWhnfKey_environment _ _).symm ▸ coherent) rawRun + let call := BetaHeadReduction.reduce path.trace (path.moving chosen) path.enough observed + exact ⟨path.target, afterEq.symm ▸ Nonempty.intro call⟩ + +/-- Provenance for a newly published entry follows from its successful +production call, without a current-annotation equality premise. -/ +theorem BetaWhnfSource.HeadOrigins.of_success {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {flags : WhnfFlags} {before after : TcState .anon} + {source result : KExpr .anon} {term : AExpr β} + (chosen : BetaWhnfSource.selected source = true) + (origins : BetaWhnfSource.HeadOrigins (β := β) flags before source) + (cold : BetaCoreCache.lookup flags (betaWhnfKey source before).1 (betaWhnfKey source before).2 = none → + BetaWhnfSource.Resources resolve locals fuel flags maxWhnfCoreFuel.toNat (betaWhnfKey source before).2 source) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) + (accepted : (RecM.whnfCoreWithFlags source flags).run (methodsN fuel) before = .ok result after) : + BetaWhnfSource.HeadOrigins (β := β) flags after source := by + obtain ⟨target, ⟨call⟩⟩ := BetaHeadReduction.exists_of_success chosen origins cold reading coherent accepted + exact .ofCall call coherent + /-- Successful structural WHNF on a miss supplies the uncached execution, then source reconstruction supplies the full reduction and cache witness. -/ theorem BetaCoreExecution.exists_of_miss_success {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} {term : AExpr β} (chosen : BetaWhnfSource.selected source = true) - (resources : BetaWhnfSource.Resources (fuel + 1) .FULL maxWhnfCoreFuel.toNat (betaWhnfKey source before).2 source) + (resources : BetaWhnfSource.Resources resolve locals (fuel + 1) .FULL maxWhnfCoreFuel.toNat (betaWhnfKey source before).2 source) (reading : readScopedExpr? resolve locals source = some term.erase) (coherent : before.env.intern.WF) (miss : (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? = none) diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnfPlan.lean b/Ix/Kernel/Verify/Consistency/BetaWhnfPlan.lean index a60adad8d..51c47c86f 100644 --- a/Ix/Kernel/Verify/Consistency/BetaWhnfPlan.lean +++ b/Ix/Kernel/Verify/Consistency/BetaWhnfPlan.lean @@ -120,8 +120,8 @@ inductive BetaHeadReduction {β : Type u} (resolve : Address → Option (ConstRe (miss : BetaCoreCache.lookup flags (betaWhnfKey source before).1 (betaWhnfKey source before).2 = none) : BetaHeadReduction resolve locals fuel flags before source term (BetaCoreCache.write flags (betaWhnfKey source before).1 result reduced) result target - | cached {fuel flags before source result term target originFuel originBefore originAfter} - (origin : BetaHeadReduction resolve locals originFuel flags originBefore source term originAfter result target) + | cached {fuel flags before source result term target originFuel originFlags originBefore originAfter} + (origin : BetaHeadReduction resolve locals originFuel originFlags originBefore source term originAfter result target) (coherent : originBefore.env.intern.WF) (hit : BetaCoreCache.lookup flags (betaWhnfKey source before).1 (betaWhnfKey source before).2 = some result) : BetaHeadReduction resolve locals fuel flags before source term (betaWhnfKey source before).2 result target diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index a91062d93..03b00ae26 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -3858,7 +3858,7 @@ private def letWhnfLoopResult (typeLevel : Bool) (flags : WhnfFlags) (shape : Na /-- Recursive head calls may contain their own beta steps and cache writes. Their method depth is separate from the enclosing two-iteration loop. -/ private def headWhnfLoopResult (depth : Nat) (typeLevel : Bool) (flags : WhnfFlags) - (shape : Nat) (warm native : Bool) : Bool := + (shape : Nat) (warm native : Bool) (innerWarm otherPartition : Bool := false) : Bool := let carrier := KExpr.mkSort (m := .anon) (if typeLevel then levelOne else .mkZero) let carrierType := KExpr.mkSort (m := .anon) (if typeLevel then levelTwo else levelOne) let body := if shape == 0 then KExpr.mkVar 0 () else if shape == 1 then @@ -3890,17 +3890,21 @@ private def headWhnfLoopResult (depth : Nat) (typeLevel : Bool) (flags : WhnfFla env := { state.env with whnfCache := seed, whnfNoDeltaCache := seed, whnfCoreCache := seed, whnfNoDeltaCheapCache := seed, whnfCoreCheapCache := seed } } + if depth == 2 && innerWarm then + let innerFlags := if otherPartition then + (if flags.isFull then WhnfFlags.DEF_EQ_CORE else .FULL) else flags + if (← RecM.whnfCoreWithFlags inner innerFlags) != identity then return false if warm then if (← RecM.whnfCoreWithFlags head flags) != function then return false let before ← get - let budget := if warm then 1 else depth + 1 + let budget := if warm then 1 else if depth == 2 && innerWarm && !otherPartition then 2 else depth + 1 let headKey := (head.addr, emptyCtxAddr) let parentKey := (source.addr, emptyCtxAddr) let writes := if depth == 2 then (seed.insert (inner.addr, emptyCtxAddr) identity).insert headKey function else seed.insert headKey function let maps (after : TcState .anon) (selected : Std.HashMap (Address × Address) (KExpr .anon)) := - sameMap after.env.whnfCoreCache (if flags.isFull then selected else seed) && - sameMap after.env.whnfCoreCheapCache (if flags.isFull then seed else selected) && + sameMap after.env.whnfCoreCache (if flags.isFull then selected else before.env.whnfCoreCache) && + sameMap after.env.whnfCoreCheapCache (if flags.isFull then before.env.whnfCoreCheapCache else selected) && sameMap after.env.whnfNoDeltaCache seed && sameMap after.env.whnfNoDeltaCheapCache seed && sameMap after.env.whnfCache seed && sameMap after.env.inferCache before.env.inferCache && sameMap after.env.inferOnlyCache before.env.inferOnlyCache @@ -4044,6 +4048,14 @@ private def headWhnfCases : TestSeq := headWhnfLoopResult depth typeLevel flags shape warm native)) .done) (test "application head WHNF: distinct legacy radii retain all keys through head memoization" ([false, true].all headWhnfLegacyKeys)) + ++ test "application head WHNF: an isolated inner hit reduces method depth and supplies later parent hits" + ([false, true].all fun typeLevel => [WhnfFlags.FULL, .DEF_EQ_CORE].all fun flags => + (List.range 3).all fun shape => [false, true].all fun warm => [false, true].all fun native => + headWhnfLoopResult 2 typeLevel flags shape warm native true) + ++ test "application head WHNF: an inner hit in the other partition preserves the cold method bound" + ([false, true].all fun typeLevel => [WhnfFlags.FULL, .DEF_EQ_CORE].all fun flags => + (List.range 3).all fun shape => [false, true].all fun native => + headWhnfLoopResult 2 typeLevel flags shape false native true true) ++ test "application head sort exposure: nested callbacks retain all binder child types across cache layers" ([false, true].all fun typeLevel => [false, true].all fun instrumented => [false, true].all fun noAccel => [0, 1, 2].all fun lowerWarm => sortExposureInferencePaths typeLevel false instrumented noAccel lowerWarm false true) diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 9380d7984..3d13651d1 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -601,7 +601,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 1,518 exact theorem boundaries. The production +The consistency target checks 1,549 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -790,14 +790,31 @@ and type conversion. Each callback uses the predecessor method table; its loop budget is independent of the enclosing step count. The generalized cache frame preserves every query's key as recursive calls memoize different legacy context radii, including previously cached digests. -`BetaWhnfSource.construct` reconstructs cold explicit-let heads that return -lambdas, including nested head calls, using finite raw resources and successful -execution. The existing inference, Pi/sort exposure, and declaration conversion -theorems consume those traces. Retained cached heads are supported by the raw -trace and its automatic semantic annotation; constructing them automatically -from source resources remains open. Stored local let values and other head -reducers also remain outside this construction. All 64 added boundaries forbid -the semantic hereditary invariant and introduce no axiom or native proof. +`BetaWhnfSource.construct` reconstructs explicit-let heads that return lambdas, +including nested fresh and cached head calls, using finite raw resources and +successful execution. The existing inference, Pi/sort exposure, and declaration +conversion theorems consume those traces. Stored local let values and other +head reducers remain outside this construction. All 64 application-head +boundaries forbid the semantic hereditary invariant and introduce no axiom or +native proof. +`BetaWhnfTrace.reannotate` and `BetaHeadReduction.reannotate` reconstruct every +annotation from the current source reading, preserving the exact raw source, +result, intermediate states, method depth, and iteration count. The earlier +producer may use a different resolver, local list, and annotation type. +`BetaHeadCacheOrigin` retains that producer and its initial intern coherence; +the actual lookup determines the returned expression and current partition. +Successful calls construct head origins through `HeadOrigins.of_success`, and +publication derives zero-method replay. The source constructor uses these +origins at hits and reconstructs the bounded path at misses, including mixed +nested calls. Classical choice selects only a retained producing execution; +the current annotations are then constructed from the current reading. +Reannotation also preserves complete executions at all three WHNF cache layers. +Their source resources no longer contain the current annotated term or require +matching producer annotations, resolver, or locals. The three `ofExecution` +constructors derive replay resources from actual publication. These 31 further +audited boundaries forbid the semantic hereditary invariant and add no axiom +or native proof. General construction and preservation of source resources and +cache origins through arbitrary checker histories remain open. `SynthesisInference.cached` retains the original tree behind an inference cache hit. Its soundness and beta derivations reuse the actual lambda-body, dependent codomain, and argument checks. `reuseFull` derives the cached result @@ -1050,11 +1067,13 @@ Ten let-WHNF regressions cover alternating let/beta steps, dependent results, exact loop exhaustion, all cache layers and native guards, zero-method replay, original inference-cache types during sort exposure, declaration universe parameters, and rejection with local-scope cleanup. -Eleven application-head regressions cover nested callbacks, cold and warmed +Thirteen application-head regressions cover nested callbacks, cold and warmed full/cheap caches, native reduction, separate method and loop exhaustion, partial cache writes on failure, distinct legacy key radii, binder inference, parameterized declaration admission, and rejection of a Pi where a sort is required. -The unit suite contains 759 checks. The anonymous differential additionally +An isolated inner hit reduces the required method depth and supplies later +parent hits; an entry in the other cache partition preserves the cold bound. +The unit suite contains 761 checks. The anonymous differential additionally serializes eleven cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. @@ -1104,6 +1123,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Retained derivations, dependent substitution, and semantic induction | [`Consistency/SynthesisDerivation.lean`](../Ix/Kernel/Verify/Consistency/SynthesisDerivation.lean), [`SynthesisShapes.lean`](../Ix/Kernel/Verify/Consistency/SynthesisShapes.lean), [`SynthesisReduction.lean`](../Ix/Kernel/Verify/Consistency/SynthesisReduction.lean), [`SynthesisMeaning.lean`](../Ix/Kernel/Verify/Consistency/SynthesisMeaning.lean), [`BinderMeaning.lean`](../Ix/Kernel/Verify/Consistency/BinderMeaning.lean), [`BetaChecking.lean`](../Ix/Kernel/Verify/Consistency/BetaChecking.lean) | | Let inference and typed cache history | [`Consistency/LetInference.lean`](../Ix/Kernel/Verify/Consistency/LetInference.lean), [`LetSynthesis.lean`](../Ix/Kernel/Verify/Consistency/LetSynthesis.lean), [`LetCache.lean`](../Ix/Kernel/Verify/Consistency/LetCache.lean), [`SynthesisCacheExecution.lean`](../Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean) | | Public beta WHNF, cache writes and replay, and Pi/sort exposure | [`BetaCacheExecution.lean`](../Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean), [`BetaCacheKeys.lean`](../Ix/Kernel/Verify/Consistency/BetaCacheKeys.lean), [`BetaPublicWhnf.lean`](../Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean), [`BetaWhnfInference.lean`](../Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean) | +| Fresh annotations and retained WHNF producer reconstruction | [`BetaReannotation.lean`](../Ix/Kernel/Verify/Consistency/BetaReannotation.lean), [`BetaHeadOrigin.lean`](../Ix/Kernel/Verify/Consistency/BetaHeadOrigin.lean), [`BetaCacheReannotation.lean`](../Ix/Kernel/Verify/Consistency/BetaCacheReannotation.lean), [`BetaTraceConstruction.lean`](../Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean) | | Retained type checks and changed cheap beta in lambda inference | [`Consistency/SynthesisInference.lean`](../Ix/Kernel/Verify/Consistency/SynthesisInference.lean), [`CheapBetaReading.lean`](../Ix/Kernel/Verify/Consistency/CheapBetaReading.lean), [`Formation.lean`](../Ix/Kernel/Verify/Consistency/Formation.lean) | | Source beta reduction and declaration conversion | [`Consistency/BetaSpine.lean`](../Ix/Kernel/Verify/Consistency/BetaSpine.lean), [`Simultaneous.lean`](../Ix/Kernel/Verify/Consistency/Simultaneous.lean), [`SpineReading.lean`](../Ix/Kernel/Verify/Consistency/SpineReading.lean), [`CheapBeta.lean`](../Ix/Kernel/Verify/Consistency/CheapBeta.lean), [`Model/BetaSpine.lean`](../Ix/Theory/Model/BetaSpine.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | diff --git a/docs/theory.md b/docs/theory.md index 06ac95c85..311b9a0a7 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -116,10 +116,14 @@ Application heads can now reduce recursively before the enclosing beta step. The same trace retains each callback's actual cache writes or earlier producing execution, and its method depth is separate from the outer loop bound. The original derivation supplies the head conversion and preserves the checks -of every argument. Source reconstruction covers cold explicit-let heads that -return lambdas, including nested head calls. Stored local let values and other -head reducers remain outside that construction; automatic reconstruction of -cached head calls still requires further work. +of every argument. Source reconstruction covers explicit-let heads that return +lambdas, including nested fresh and cached head calls. Retained producers are +reannotated from the current source reading, even with different resolvers, +local lists, and annotation types. This preserves the exact raw result, +intermediate states, method depth, and iteration count. All three WHNF cache +layers support this reconstruction, and actual publication derives resources +for later replay. Stored local let values and other head reducers remain +outside that construction. Constructing initial inference resources for arbitrary accepted programs, general WHNF cache origins, the remaining reducers, and general conversion remain open. From 1ab4b183f9f30681b12bb3f40cb6cd161e905ab3 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Mon, 14 Sep 2026 18:27:57 -0400 Subject: [PATCH 48/63] Derive WHNF cache origins from complete publication histories --- Ix/Kernel/Verify/Consistency.lean | 15 ++ Ix/Kernel/Verify/Consistency/Audit.lean | 55 ++++- .../Verify/Consistency/BetaCacheEvent.lean | 187 +++++++++++++++ .../Consistency/BetaCacheExecution.lean | 19 +- .../Verify/Consistency/BetaCacheHistory.lean | 220 ++++++++++++++++++ .../Verify/Consistency/BetaCacheKeys.lean | 8 + .../Consistency/BetaCachePublications.lean | 187 +++++++++++++++ .../Consistency/BetaCacheReannotation.lean | 3 + .../Consistency/BetaHistoryInference.lean | 74 ++++++ .../Verify/Consistency/BetaHistorySource.lean | 188 +++++++++++++++ .../Verify/Consistency/BetaWhnfInference.lean | 1 + Tests/Ix/Kernel/Consistency.lean | 130 +++++++++++ docs/kernel-verification.md | 36 ++- docs/theory.md | 10 + 14 files changed, 1125 insertions(+), 8 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/BetaCacheEvent.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaCacheHistory.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaCachePublications.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaHistoryInference.lean create mode 100644 Ix/Kernel/Verify/Consistency/BetaHistorySource.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 1c85de63d..20fb3f04c 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -53,6 +53,11 @@ import Ix.Kernel.Verify.Consistency.BetaHeadConstruction import Ix.Kernel.Verify.Consistency.BetaReannotation import Ix.Kernel.Verify.Consistency.BetaHeadOrigin import Ix.Kernel.Verify.Consistency.BetaCacheReannotation +import Ix.Kernel.Verify.Consistency.BetaCacheEvent +import Ix.Kernel.Verify.Consistency.BetaCachePublications +import Ix.Kernel.Verify.Consistency.BetaCacheHistory +import Ix.Kernel.Verify.Consistency.BetaHistorySource +import Ix.Kernel.Verify.Consistency.BetaHistoryInference import Ix.Kernel.Verify.Consistency.SynthesisAppCongruence import Ix.Kernel.Verify.Consistency.BetaPublicWhnf import Ix.Kernel.Verify.Consistency.WhnfCacheFrame @@ -190,6 +195,16 @@ same reconstruction applies at all three cache layers, and publication derives resources for later replay. The original typing derivation supplies the head conversion while preserving every checked argument. Key frames preserve all queries when a recursive call memoizes a different legacy context radius. +Complete WHNF histories now retain every actual publication and reconstruct +all five maps, including recursive head writes. Hits preserve the history; +native guards suppress the same upper writes as production. Binder/let +opening and scope cleanup retain entries from exited scopes. Loop exhaustion +keeps the completed prefix's publications, and clearing begins an empty +history. Finite collision data identifies the queried source among the +recorded inputs. The resulting provenance supplies all three cache layers' +origin resources, including public replay of zero-depth head producers. +Successful source reconstruction, Pi/sort exposure, and semantic public +reduction preserve the updated history for later calls. Only an outer miss charges shared fuel. Pi exposure uses the same complete cache-layer execution. Application inference uses that exposure between argument checks and derives the type conversion diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index e8c7d777f..1cdb3a8a7 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -17,6 +17,7 @@ import Ix.Kernel.Verify.Consistency.SynthesisCacheExecution import Ix.Kernel.Verify.Consistency.LetCache import Ix.Kernel.Verify.Consistency.BetaSourceInference import Ix.Kernel.Verify.Consistency.BetaExposureConstruction +import Ix.Kernel.Verify.Consistency.BetaHistoryInference import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -928,6 +929,46 @@ private def betaReannotationExecutionRoots : Array Lean.Name := #[ ``BetaWhnfSource.PublicResources.ofExecution ] +/-- Complete WHNF histories retain raw producing calls. Current typing is +derived only after finite source-key selection and fresh annotation. -/ +private def whnfHistoryFrameRoots : Array Lean.Name := #[ + ``WhnfCachePartition.cache, ``WhnfCachePartition.intern, + ``WhnfCachePartition.instrument, ``WhnfCachePartition.charge, + ``WhnfCachePartition.writeCore, ``WhnfCachePartition.writeNoDelta, + ``WhnfCachePartition.writeFull, ``WhnfCachePartition.lookupCore +] + +private def whnfHistoryExprRoots : Array Lean.Name := #[ + ``betaWhnfKey_address, ``WhnfCachePartition.key +] + +private def whnfHistoryExecutionRoots : Array Lean.Name := #[ + ``BetaCoreExecution.cachedHead, ``BetaCoreExecution.headReduction, + ``BetaCacheEventOrigin, ``BetaCacheEventOrigin.address, ``BetaCacheEvent, + ``BetaCacheEvent.head, ``BetaCacheEvent.noDelta, ``BetaCacheEvent.full, + ``BetaCacheEvent.step, ``BetaCacheEvent.apply, ``BetaCacheEvent.apply_nil, + ``BetaCacheEvent.apply_append, ``BetaCacheEvent.apply_origin, + ``BetaWhnfTrace.cacheEvents, ``BetaHeadReduction.cacheEvents, + ``BetaWhnfTrace.cache_maps, ``BetaHeadReduction.cache_maps, + ``BetaCoreExecution.cacheEvents, ``BetaCoreExecution.cache_maps, + ``BetaNoDeltaExecution.cacheEvents, ``BetaNoDeltaExecution.cache_maps, + ``BetaPublicExecution.cacheEvents, ``BetaPublicExecution.cache_maps, + ``BetaCacheHistory, ``BetaCacheHistory.ofMaps, ``BetaCacheHistory.append, + ``BetaCacheHistory.intern, ``BetaCacheHistory.key, ``BetaCacheHistory.instrument, + ``BetaCacheHistory.charge, ``BetaCacheHistory.policy, ``BetaCacheHistory.truncate, + ``BetaCacheHistory.openBinder, ``BetaCacheHistory.openLet, ``BetaCacheHistory.withLctxScope, + ``BetaCacheHistory.clear, ``BetaCacheHistory.afterTrace, ``BetaCacheHistory.afterHead, + ``BetaCacheHistory.afterCore, ``BetaCacheHistory.afterNoDelta, ``BetaCacheHistory.afterPublic, + ``BetaCacheHistory.origin, ``BetaCacheHistory.KeyData, ``BetaCacheHistory.KeyData.same, + ``BetaCacheHistory.selected_origin, ``BetaCacheHistory.selected, + ``BetaWhnfTrace.boundedCacheHistory, ``BetaCacheEventOrigin.terminal, + ``BetaCacheEventOrigin.headOrigin, ``BetaCacheEventOrigin.noDeltaOrigin, ``BetaCacheEventOrigin.fullOrigin, + ``BetaCacheHistory.headOrigins, ``BetaCacheHistory.coreResources, + ``BetaCacheHistory.noDeltaResources, ``BetaCacheHistory.publicResources, + ``BetaCoreExecution.exists_of_history, ``BetaNoDeltaExecution.exists_of_history, + ``BetaPublicExecution.exists_of_history, ``BetaPiExposure.cacheHistory, ``BetaSortExposure.cacheHistory +] + private def localScopeFrameRoots : Array Lean.Name := #[ ``LocalContext.Equiv.refl, ``LocalContext.Equiv.symm, ``LocalContext.Equiv.trans, ``LocalContext.Equiv.size, ``LocalContext.Equiv.find?, ``LocalContext.Equiv.wf, @@ -1540,21 +1581,24 @@ def roots : Array RootAllowance := #[ ++ (#[``BetaStepSource.selected, ``BetaStepSource.selected_app] ++ letWhnfSyntaxRoots ++ headWhnfSyntaxRoots).map (fun root => { root, standardAxioms := #[``propext], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) - ++ (mixedCacheFrameRoots ++ headWhnfFrameRoots ++ #[``betaWhnfCharge_success]).map (fun root => { + ++ #[``WhnfCachePartition, ``WhnfCachePartition.ofFlags].map (fun root => { + root, forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) + ++ (mixedCacheFrameRoots ++ headWhnfFrameRoots ++ whnfHistoryFrameRoots ++ #[``betaWhnfCharge_success]).map (fun root => { root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ (mixedCacheKeyRoots ++ betaSourceExprRoots ++ letWhnfExprRoots ++ headWhnfExprRoots ++ - betaReannotationExprRoots).map (fun root => { + betaReannotationExprRoots ++ whnfHistoryExprRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ #[``SynthesisInference.beta_core_execution_sound, ``SynthesisInference.beta_noDelta_execution_sound, ``SynthesisInference.beta_public_execution_sound, ``SynthesisInference.beta_whnf_of_success, ``SynthesisInference.beta_core_of_success, - ``SynthesisInference.beta_noDelta_of_success, ``SynthesisInference.beta_public_of_success].map (fun root => { + ``SynthesisInference.beta_noDelta_of_success, ``SynthesisInference.beta_public_of_success, + ``SynthesisInference.beta_public_of_history].map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) ++ (recursiveLetShapeRoots ++ sortExposureRoots ++ mixedCacheExecutionRoots ++ betaSourceExecutionRoots ++ - letWhnfExecutionRoots ++ headWhnfExecutionRoots ++ betaReannotationExecutionRoots).map (fun root => { + letWhnfExecutionRoots ++ headWhnfExecutionRoots ++ betaReannotationExecutionRoots ++ whnfHistoryExecutionRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ (localScopeFrameRoots ++ localStateFrameRoots ++ recursiveStateFrameRoots ++ ingressFrameRoots ++ @@ -1575,6 +1619,9 @@ def roots : Array RootAllowance := #[ root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) ++ #[ + { root := ``BetaCacheHistory.initial, standardAxioms := standard, + nativeAxioms := #[expressionNative, levelNative, nameNative], + forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }, { root := ``BetaCoreCache.published, standardAxioms := standard, forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }, { root := ``LocalStateInvariant.newLazyAnon, standardAxioms := standard, diff --git a/Ix/Kernel/Verify/Consistency/BetaCacheEvent.lean b/Ix/Kernel/Verify/Consistency/BetaCacheEvent.lean new file mode 100644 index 000000000..105560940 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaCacheEvent.lean @@ -0,0 +1,187 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaCacheExecution +import Ix.Kernel.Verify.Consistency.BetaHeadOrigin + +/-! Publications retain raw producing calls independently of later source +annotations. Folding their events describes all five complete WHNF maps. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +inductive WhnfCachePartition where + | full | noDelta | noDeltaCheap | core | coreCheap + deriving DecidableEq + +namespace WhnfCachePartition + +abbrev CacheMap := Std.HashMap (Address × Address) (KExpr .anon) + +def ofFlags (flags : WhnfFlags) : WhnfCachePartition := + if flags.isFull then .core else .coreCheap + +def cache (partition : WhnfCachePartition) (state : TcState .anon) : CacheMap := + match partition with + | .full => state.env.whnfCache + | .noDelta => state.env.whnfNoDeltaCache + | .noDeltaCheap => state.env.whnfNoDeltaCheapCache + | .core => state.env.whnfCoreCache + | .coreCheap => state.env.whnfCoreCheapCache + +theorem key (partition : WhnfCachePartition) (source : KExpr .anon) (state : TcState .anon) : + partition.cache (betaWhnfKey source state).2 = partition.cache state := by + cases partition <;> simp only [cache, betaWhnfKey_environment] + +theorem intern (partition : WhnfCachePartition) (state : TcState .anon) (table : InternTable .anon) : + partition.cache {state with env := {state.env with intern := table}} = partition.cache state := by + cases partition <;> rfl + +theorem instrument (partition : WhnfCachePartition) (state : TcState .anon) : + partition.cache (betaWhnfPrefix state) = partition.cache state := by + unfold betaWhnfPrefix + split <;> cases partition <;> rfl + +theorem charge (partition : WhnfCachePartition) (state : TcState .anon) : + partition.cache (betaWhnfCharge state) = partition.cache state := by + unfold betaWhnfCharge + split <;> cases partition <;> rfl + +theorem writeCore (partition : WhnfCachePartition) (flags : WhnfFlags) + (key : Address × Address) (result : KExpr .anon) (state : TcState .anon) : + partition.cache (BetaCoreCache.write flags key result state) = + if ofFlags flags = partition then (partition.cache state).insert key result else partition.cache state := by + cases full : flags.isFull <;> cases partition <;> + simp only [BetaCoreCache.write, full, ofFlags, cache, Bool.false_eq_true, if_false, if_true, + reduceCtorEq] + +theorem writeNoDelta (partition : WhnfCachePartition) + (key : Address × Address) (result : KExpr .anon) (state : TcState .anon) : + partition.cache (BetaCacheExecution.writeNoDelta key result state) = + if state.inNativeReduce then partition.cache state else + if .noDelta = partition then (partition.cache state).insert key result else partition.cache state := by + unfold BetaCacheExecution.writeNoDelta + split <;> cases partition <;> simp only [cache, reduceCtorEq, if_false, if_true] + +theorem writeFull (partition : WhnfCachePartition) + (key : Address × Address) (result : KExpr .anon) (state : TcState .anon) : + partition.cache (BetaCacheExecution.writeFull key result state) = + if state.inNativeReduce then partition.cache state else + if .full = partition then (partition.cache state).insert key result else partition.cache state := by + unfold BetaCacheExecution.writeFull + split <;> cases partition <;> simp only [cache, reduceCtorEq, if_false, if_true] + +theorem lookupCore (flags : WhnfFlags) (key : Address × Address) (state : TcState .anon) : + BetaCoreCache.lookup flags key state = ((ofFlags flags).cache state)[key]? := by + cases full : flags.isFull <;> simp only [BetaCoreCache.lookup, ofFlags, cache, full, Bool.false_eq_true, if_false, if_true] + +end WhnfCachePartition + +/-- Public structural calls and recursive head calls share the same cache. +Their retained executions therefore provide interchangeable raw origins. -/ +def BetaCoreExecution.headReduction {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (execution : BetaCoreExecution resolve locals fuel before source term result target) : + BetaHeadReduction resolve locals (fuel + 1) .FULL before source term execution.after result target := + match execution with + | .reduce path moving enough miss _ => .reduce path moving enough miss + | .cached origin coherent hit => .cached origin.headReduction coherent hit + | .cachedHead origin coherent _ hit => .cached origin coherent hit +termination_by structural execution + +/-- Raw provenance of a stored result. The source and result annotations +belong to the producing check; no current semantic typing is assumed. -/ +inductive BetaCacheEventOrigin {β : Type u} : + WhnfCachePartition → (Address × Address) → KExpr .anon → KExpr .anon → Type u + | head {resolve locals fuel flags before after source result term target} + (call : BetaHeadReduction (β := β) resolve locals fuel flags before source term after result target) + (coherent : before.env.intern.WF) (terminal : BetaWhnfTerminal result) : + BetaCacheEventOrigin (WhnfCachePartition.ofFlags flags) (betaWhnfKey source before).1 source result + | noDelta {resolve locals fuel before source result term target} + (execution : BetaNoDeltaExecution (β := β) resolve locals fuel before source term result target) + (coherent : before.env.intern.WF) (inactive : before.inNativeReduce = false) : + BetaCacheEventOrigin .noDelta (betaWhnfKey source before).1 source result + | full {resolve locals fuel before source result term target} + (execution : BetaPublicExecution (β := β) resolve locals fuel before source term result target) + (coherent : before.env.intern.WF) (inactive : before.inNativeReduce = false) : + BetaCacheEventOrigin .full (BetaPublicWhnf.outerKey source before).1 source result + +theorem BetaCacheEventOrigin.address {β : Type u} {partition : WhnfCachePartition} + {key : Address × Address} {source result : KExpr .anon} + (origin : BetaCacheEventOrigin (β := β) partition key source result) : key.1 = source.addr := by + cases origin with + | head | noDelta => exact betaWhnfKey_address _ _ + | full => exact betaWhnfKey_address _ _ + +structure BetaCacheEvent (β : Type u) where + partition : WhnfCachePartition + key : Address × Address + source : KExpr .anon + result : KExpr .anon + origin : BetaCacheEventOrigin (β := β) partition key source result + +namespace BetaCacheEvent + +variable {β : Type u} + +def head {resolve locals fuel flags before after source result term target} + (call : BetaHeadReduction (β := β) resolve locals fuel flags before source term after result target) + (coherent : before.env.intern.WF) (terminal : BetaWhnfTerminal result) : BetaCacheEvent β := + ⟨_, _, source, result, .head call coherent terminal⟩ + +def noDelta {resolve locals fuel before source result term target} + (execution : BetaNoDeltaExecution (β := β) resolve locals fuel before source term result target) + (coherent : before.env.intern.WF) (inactive : before.inNativeReduce = false) : BetaCacheEvent β := + ⟨_, _, source, result, .noDelta execution coherent inactive⟩ + +def full {resolve locals fuel before source result term target} + (execution : BetaPublicExecution (β := β) resolve locals fuel before source term result target) + (coherent : before.env.intern.WF) (inactive : before.inNativeReduce = false) : BetaCacheEvent β := + ⟨_, _, source, result, .full execution coherent inactive⟩ + +def step (event : BetaCacheEvent β) (partition : WhnfCachePartition) + (cache : WhnfCachePartition.CacheMap) : WhnfCachePartition.CacheMap := + if event.partition = partition then cache.insert event.key event.result else cache + +def apply (events : List (BetaCacheEvent β)) (partition : WhnfCachePartition) + (cache : WhnfCachePartition.CacheMap) : WhnfCachePartition.CacheMap := + events.foldl (fun cache event => event.step partition cache) cache + +@[simp] theorem apply_nil (partition : WhnfCachePartition) (cache : WhnfCachePartition.CacheMap) : + apply ([] : List (BetaCacheEvent β)) partition cache = cache := rfl + +theorem apply_append (first second : List (BetaCacheEvent β)) (partition : WhnfCachePartition) + (cache : WhnfCachePartition.CacheMap) : + apply (first ++ second) partition cache = apply second partition (apply first partition cache) := + List.foldl_append + +/-- Repeated writes and unrelated keys are retained. A selected value was +already present or has a producing call in this finite event list. -/ +theorem apply_origin (events : List (BetaCacheEvent β)) (partition : WhnfCachePartition) + (cache : WhnfCachePartition.CacheMap) {key : Address × Address} {result : KExpr .anon} + (stored : (apply events partition cache)[key]? = some result) : + cache[key]? = some result ∨ ∃ event ∈ events, + event.partition = partition ∧ event.key = key ∧ event.result = result := by + induction events generalizing cache with + | nil => exact .inl stored + | cons event events ih => + rcases ih (event.step partition cache) stored with old | ⟨written, member, part, same, value⟩ + · unfold step at old + split at old + · rename_i part + by_cases same : event.key = key + · rw [same, Std.HashMap.getElem?_insert_self] at old + exact .inr ⟨event, .head _, part, same, Option.some.inj old⟩ + · exact .inl (by simpa [Std.HashMap.getElem?_insert, same] using old) + · exact .inl old + · exact .inr ⟨written, .tail _ member, part, same, value⟩ + +end BetaCacheEvent + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean b/Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean index bb6f91018..64e30a9c9 100644 --- a/Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean +++ b/Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean @@ -73,6 +73,12 @@ inductive BetaCoreExecution {β : Type u} (resolve : Address → Option (ConstRe (coherent : originBefore.env.intern.WF) (hit : (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? = some result) : BetaCoreExecution resolve locals fuel before source term result target + | cachedHead {fuel before source term result target originFuel originFlags originBefore originAfter} + (origin : BetaHeadReduction resolve locals originFuel originFlags originBefore source term originAfter result target) + (coherent : originBefore.env.intern.WF) + (terminal : BetaWhnfTerminal result) + (hit : (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? = some result) : + BetaCoreExecution resolve locals fuel before source term result target namespace BetaCoreExecution @@ -84,7 +90,7 @@ def after (execution : BetaCoreExecution resolve locals fuel before source term | .reduce (reduced := reduced) _ _ _ _ _ => {reduced with env := {reduced.env with whnfCoreCache := reduced.env.whnfCoreCache.insert (betaWhnfKey source before).1 result}} - | .cached .. => (betaWhnfKey source before).2 + | .cached .. | .cachedHead .. => (betaWhnfKey source before).2 /-- A retained first step identifies the source's nonleaf branch at every later cache use. It does not require repeating reduction in the later state. -/ @@ -94,6 +100,7 @@ theorem first {fuel : Nat} {before : TcState .anon} match execution with | .reduce path moving .. => path.first moving | .cached origin .. => origin.first + | .cachedHead origin .. => origin.entry theorem terminal {fuel : Nat} {before : TcState .anon} (execution : BetaCoreExecution resolve locals fuel before source term result target) : @@ -101,6 +108,7 @@ theorem terminal {fuel : Nat} {before : TcState .anon} match execution with | .reduce _ _ _ _ terminal => terminal | .cached origin .. => origin.terminal + | .cachedHead _ _ terminal _ => terminal theorem run (execution : BetaCoreExecution resolve locals fuel before source term result target) : (RecM.whnfCore source).run (methodsN (fuel + 1)) before = .ok result execution.after := by @@ -113,6 +121,9 @@ theorem run (execution : BetaCoreExecution resolve locals fuel before source ter | cached origin coherent hit => exact RecM.whnfCoreWithFlagsNonLeaf_fullHit rfl (betaWhnfKey_run source _) (origin.first.not_transient _ _) hit + | cachedHead origin coherent terminal hit => + exact RecM.whnfCoreWithFlagsNonLeaf_fullHit rfl (betaWhnfKey_run source _) + (origin.entry.not_transient _ _) hit theorem reading (execution : BetaCoreExecution resolve locals fuel before source term result target) (sourceReading : readScopedExpr? resolve locals source = some term.erase) @@ -124,13 +135,16 @@ theorem reading (execution : BetaCoreExecution resolve locals fuel before source | cached origin initial hit ih => refine ⟨(ih sourceReading initial).1, ?_⟩ simpa only [after, betaWhnfKey_environment] using coherent + | cachedHead origin initial terminal hit => + refine ⟨(origin.reading sourceReading initial).1, ?_⟩ + simpa only [after, betaWhnfKey_environment] using coherent theorem frame (execution : BetaCoreExecution resolve locals fuel before source term result target) : BetaCacheFrame before execution.after := by cases execution with | reduce path moving enough miss terminal => exact (BetaCacheFrame.key source before).trans (path.frame.trans (.core _ _ _)) - | cached => exact .key source before + | cached | cachedHead => exact .key source before theorem stable_key (execution : BetaCoreExecution resolve locals fuel before source term result target) : (betaWhnfKey source execution.after).1 = (betaWhnfKey source before).1 := execution.frame.keys source @@ -142,6 +156,7 @@ theorem published (execution : BetaCoreExecution resolve locals fuel before sour cases execution with | reduce => simp only [after, Std.HashMap.getElem?_insert_self] | cached origin coherent hit => exact hit + | cachedHead origin coherent terminal hit => exact hit /-- The producing execution supplies the later hit, including a newly memoized context suffix. No cache-equality observation is supplied. -/ diff --git a/Ix/Kernel/Verify/Consistency/BetaCacheHistory.lean b/Ix/Kernel/Verify/Consistency/BetaCacheHistory.lean new file mode 100644 index 000000000..1da8883d5 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaCacheHistory.lean @@ -0,0 +1,220 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaCachePublications +import Ix.Kernel.Verify.Consistency.LetOpening +import Ix.Kernel.Verify.Consistency.InferenceCache + +/-! A complete WHNF history begins with empty maps and retains the actual +producers behind every later hit, including entries from exited scopes. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +structure BetaCacheHistory (β : Type u) (state : TcState .anon) where + events : List (BetaCacheEvent β) + maps (partition : WhnfCachePartition) : partition.cache state = BetaCacheEvent.apply events partition ∅ + +namespace BetaCacheHistory + +variable {β : Type u} {state : TcState .anon} + +def initial (source : Ixon.Env) : BetaCacheHistory β (TcState.newLazyAnon source) := + ⟨[], fun partition => by cases partition <;> rfl⟩ + +def ofMaps (history : BetaCacheHistory β state) {after : TcState .anon} + (preserved : ∀ partition : WhnfCachePartition, partition.cache after = partition.cache state) : + BetaCacheHistory β after := + ⟨history.events, fun partition => (preserved partition).trans (history.maps partition)⟩ + +def append (history : BetaCacheHistory β state) {after : TcState .anon} (events : List (BetaCacheEvent β)) + (effects : ∀ partition : WhnfCachePartition, + partition.cache after = BetaCacheEvent.apply events partition (partition.cache state)) : + BetaCacheHistory β after := + ⟨history.events ++ events, fun partition => by + rw [BetaCacheEvent.apply_append, effects partition, history.maps partition]⟩ + +def intern (history : BetaCacheHistory β state) (table : InternTable .anon) : + BetaCacheHistory β {state with env := {state.env with intern := table}} := + history.ofMaps (fun partition => partition.intern _ _) + +def key (history : BetaCacheHistory β state) (source : KExpr .anon) : + BetaCacheHistory β (betaWhnfKey source state).2 := history.ofMaps (fun partition => partition.key _ _) + +def instrument (history : BetaCacheHistory β state) : BetaCacheHistory β (betaWhnfPrefix state) := + history.ofMaps (fun partition => partition.instrument _) + +def charge (history : BetaCacheHistory β state) : BetaCacheHistory β (betaWhnfCharge state) := + history.ofMaps (fun partition => partition.charge _) + +def policy (history : BetaCacheHistory β state) (policy : Bool) : + BetaCacheHistory β {state with inferOnly := policy} := + history.ofMaps (fun partition => by cases partition <;> rfl) + +def truncate (history : BetaCacheHistory β state) (size : Nat) : + BetaCacheHistory β {state with lctx := state.lctx.truncate size} := + history.ofMaps (fun partition => by cases partition <;> rfl) + +def openBinder {after : TcState .anon} {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body opened : KExpr .anon} {fresh : FVarId} (history : BetaCacheHistory β state) + (accepted : TcM.openBinder name bi domain body state = .ok (opened, fresh) after) : + BetaCacheHistory β after := by + rw [openBinder_eq] at accepted + split at accepted + · cases accepted + exact history.ofMaps (fun partition => by cases partition <;> rfl) + · contradiction + +def openLet {after : TcState .anon} {name : Mode.anon.F Name} + {domain value body opened : KExpr .anon} {fresh : FVarId} (history : BetaCacheHistory β state) + (accepted : TcM.openLet name domain value body state = .ok (opened, fresh) after) : + BetaCacheHistory β after := by + rw [openLet_eq] at accepted + split at accepted + · cases accepted + exact history.ofMaps (fun partition => by cases partition <;> rfl) + · contradiction + +def withLctxScope {α : Type} (action : RecM .anon α) (methods : Methods .anon) + (preserved : match action.run methods state with + | .ok _ after | .error _ after => BetaCacheHistory β after) : + match (RecM.withLctxScope action).run methods state with + | .ok _ after | .error _ after => BetaCacheHistory β after := by + rw [withLctxScope_eq] + cases run : action.run methods state <;> rw [run] at preserved <;> + exact preserved.truncate state.lctx.size + +def clear (_history : BetaCacheHistory β state) : + BetaCacheHistory β {state with env := state.env.clearReductionCaches} := + ⟨[], fun partition => by cases partition <;> rfl⟩ + +def afterTrace {resolve locals fuel flags steps source result term target after} + (history : BetaCacheHistory β state) + (trace : BetaWhnfTrace (β := β) resolve locals fuel flags steps state source term after result target) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : state.env.intern.WF) : BetaCacheHistory β after := + history.append (trace.cacheEvents reading coherent) (trace.cache_maps reading coherent) + +def afterHead {resolve locals fuel flags source result term target after} + (history : BetaCacheHistory β state) + (call : BetaHeadReduction (β := β) resolve locals fuel flags state source term after result target) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : state.env.intern.WF) (terminal : BetaWhnfTerminal result) : BetaCacheHistory β after := + history.append (call.cacheEvents reading coherent terminal) (call.cache_maps reading coherent terminal) + +def afterCore {resolve locals fuel source result term target} + (history : BetaCacheHistory β state) + (execution : BetaCoreExecution (β := β) resolve locals fuel state source term result target) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : state.env.intern.WF) : BetaCacheHistory β execution.after := + history.append (execution.cacheEvents reading coherent) (execution.cache_maps reading coherent) + +def afterNoDelta {resolve locals fuel source result term target} + (history : BetaCacheHistory β state) + (execution : BetaNoDeltaExecution (β := β) resolve locals fuel state source term result target) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : state.env.intern.WF) : BetaCacheHistory β execution.after := + history.append (execution.cacheEvents reading coherent) (execution.cache_maps reading coherent) + +def afterPublic {resolve locals fuel source result term target} + (history : BetaCacheHistory β state) + (execution : BetaPublicExecution (β := β) resolve locals fuel state source term result target) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : state.env.intern.WF) : BetaCacheHistory β execution.after := + history.append (execution.cacheEvents reading coherent) (execution.cache_maps reading coherent) + +theorem origin (history : BetaCacheHistory β state) {partition : WhnfCachePartition} + {key : Address × Address} {result : KExpr .anon} + (stored : (partition.cache state)[key]? = some result) : + ∃ event ∈ history.events, event.partition = partition ∧ event.key = key ∧ event.result = result := by + rw [history.maps partition] at stored + rcases BetaCacheEvent.apply_origin history.events partition ∅ stored with empty | produced + · simp at empty + · exact produced + +/-- Only the current query and finitely many actual publication inputs +need collision freedom. Scope annotations are recovered separately. -/ +def KeyData (history : BetaCacheHistory β state) (source : KExpr .anon) : Prop := + KExpr.CollisionFree fun candidate => candidate = source ∨ + ∃ event ∈ history.events, candidate = event.source + +theorem KeyData.same {history : BetaCacheHistory β state} {source : KExpr .anon} + (data : history.KeyData source) {event : BetaCacheEvent β} (member : event ∈ history.events) + {key : Address × Address} (address : key.1 = source.addr) (same : event.key = key) : + event.source = source := by + have equal : event.source.addr = source.addr := + event.origin.address.symm.trans ((congrArg Prod.fst same).trans address) + simpa only [KExpr.eraseMeta_anon] using data (Or.inr ⟨event, member, rfl⟩) (Or.inl rfl) equal + +theorem selected_origin (history : BetaCacheHistory β state) {source : KExpr .anon} + (data : history.KeyData source) {partition : WhnfCachePartition} {key : Address × Address} + {result : KExpr .anon} (address : key.1 = source.addr) + (stored : (partition.cache state)[key]? = some result) : + ∃ event ∈ history.events, + event.partition = partition ∧ event.key = key ∧ event.source = source ∧ event.result = result := by + obtain ⟨event, member, partition, same, result⟩ := history.origin stored + exact ⟨event, member, partition, same, data.same member address same, result⟩ + +theorem selected (history : BetaCacheHistory β state) {source : KExpr .anon} + (data : history.KeyData source) {partition : WhnfCachePartition} {key : Address × Address} + {result : KExpr .anon} (address : key.1 = source.addr) + (stored : (partition.cache state)[key]? = some result) : + Nonempty (BetaCacheEventOrigin (β := β) partition key source result) := by + obtain ⟨event, _, part, same, sourceEq, resultEq⟩ := history.selected_origin data address stored + rw [← part, ← same, ← sourceEq, ← resultEq] + exact ⟨event.origin⟩ + +end BetaCacheHistory + +/-- Exhausting the outer loop retains exactly the completed prefix's +publications, including whole successful head callbacks. The same history +also covers successful execution when the loop budget is sufficient. -/ +def BetaWhnfTrace.boundedCacheHistory {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel steps : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} + (trace : BetaWhnfTrace resolve locals fuel flags steps before source term after result target) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) (history : BetaCacheHistory β before) (loopFuel : Nat) : + match (RecM.runBounded (fun current => RecM.whnfCoreWithFlagsStep current flags) loopFuel source).run + (methodsN fuel) before with + | .ok _ final | .error _ final => BetaCacheHistory β final := by + cases loopFuel with + | zero => exact history + | succ remaining => + match trace with + | .done finished => + rw [RecM.runBounded, ReaderT.run_bind] + change match EStateM.bind (β := KExpr .anon) ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before with + | .ok _ final | .error _ final => BetaCacheHistory β final + rw [EStateM.bind, finished] + exact history + | .next plan rest => + have next := plan.reading coherent + rw [RecM.runBounded, ReaderT.run_bind] + change match EStateM.bind (β := KExpr .anon) ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before with + | .ok _ final | .error _ final => BetaCacheHistory β final + rw [EStateM.bind, plan.run _ flags] + exact rest.boundedCacheHistory next.1 next.2 (history.intern _) remaining + | .zeta plan rest => + have next := plan.reading reading coherent + rw [RecM.runBounded, ReaderT.run_bind] + change match EStateM.bind (β := KExpr .anon) ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before with + | .ok _ final | .error _ final => BetaCacheHistory β final + rw [EStateM.bind, plan.run _ flags] + exact rest.boundedCacheHistory next.1 next.2 (history.intern _) remaining + | .head plan call rest => + have next := plan.reading (call.reading plan.sourceHeadReads coherent).2 + rw [RecM.runBounded, ReaderT.run_bind] + change match EStateM.bind (β := KExpr .anon) ((RecM.whnfCoreWithFlagsStep source flags).run _) _ before with + | .ok _ final | .error _ final => BetaCacheHistory β final + rw [EStateM.bind, plan.run call.run] + exact rest.boundedCacheHistory next.1 next.2 + ((history.afterHead call plan.sourceHeadReads coherent (.lam _ _ _ _ _)).intern _) remaining +termination_by structural trace + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaCacheKeys.lean b/Ix/Kernel/Verify/Consistency/BetaCacheKeys.lean index d33a4b06a..a78ae3e9b 100644 --- a/Ix/Kernel/Verify/Consistency/BetaCacheKeys.lean +++ b/Ix/Kernel/Verify/Consistency/BetaCacheKeys.lean @@ -54,6 +54,14 @@ theorem betaWhnfKey_replay (source : KExpr .anon) (before : TcState .anon) : · cases memo : before.ctxAddrCache[(before.ctxId, source.lbr)]? <;> simp [betaWhnfKey, fast, memo] +theorem betaWhnfKey_address (source : KExpr .anon) (before : TcState .anon) : + (betaWhnfKey source before).1.1 = source.addr := by + unfold betaWhnfKey + split + · rfl + · dsimp only + split <;> rfl + /-- Memoizing a head's context suffix preserves every surrounding WHNF key, including a different loose-variable radius. Existing entries may be arbitrary. -/ theorem betaWhnfKey_key (source query : KExpr .anon) (before : TcState .anon) : diff --git a/Ix/Kernel/Verify/Consistency/BetaCachePublications.lean b/Ix/Kernel/Verify/Consistency/BetaCachePublications.lean new file mode 100644 index 000000000..bae884519 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaCachePublications.lean @@ -0,0 +1,187 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaCacheEvent + +/-! Extract every actual WHNF publication, including recursive head calls. +The original reading supplies intern coherence along the same raw path. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + +mutual + +def BetaWhnfTrace.cacheEvents {fuel steps : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} + (trace : BetaWhnfTrace resolve locals fuel flags steps before source term after result target) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) : List (BetaCacheEvent β) := + match trace with + | .done _ => [] + | .next plan rest => + let next := plan.reading coherent + BetaWhnfTrace.cacheEvents rest next.1 next.2 + | .zeta plan rest => + let next := plan.reading reading coherent + BetaWhnfTrace.cacheEvents rest next.1 next.2 + | .head plan call rest => + let next := plan.reading (call.reading plan.sourceHeadReads coherent).2 + BetaHeadReduction.cacheEvents call plan.sourceHeadReads coherent (.lam _ _ _ _ _) ++ + BetaWhnfTrace.cacheEvents rest next.1 next.2 +termination_by structural trace + +def BetaHeadReduction.cacheEvents {fuel : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} + (call : BetaHeadReduction resolve locals fuel flags before source term after result target) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) (terminal : BetaWhnfTerminal result) : List (BetaCacheEvent β) := + match call with + | .reduce path moving enough miss => + BetaWhnfTrace.cacheEvents path reading ((betaWhnfKey_environment _ _).symm ▸ coherent) ++ + [.head (.reduce path moving enough miss) coherent terminal] + | .cached .. => [] +termination_by structural call + +end + +mutual + +theorem BetaWhnfTrace.cache_maps {fuel steps : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} + (trace : BetaWhnfTrace resolve locals fuel flags steps before source term after result target) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) (partition : WhnfCachePartition) : + partition.cache after = BetaCacheEvent.apply (trace.cacheEvents reading coherent) partition (partition.cache before) := + match trace with + | .done _ => rfl + | .next plan rest => by + have next := plan.reading coherent + simpa only [BetaWhnfTrace.cacheEvents, BetaStepPlan.after, WhnfCachePartition.intern] using + BetaWhnfTrace.cache_maps rest next.1 next.2 partition + | .zeta plan rest => by + have next := plan.reading reading coherent + simpa only [BetaWhnfTrace.cacheEvents, LetStepPlan.after, WhnfCachePartition.intern] using + BetaWhnfTrace.cache_maps rest next.1 next.2 partition + | .head plan call rest => by + have next := plan.reading (call.reading plan.sourceHeadReads coherent).2 + rw [BetaWhnfTrace.cacheEvents, BetaCacheEvent.apply_append, + BetaWhnfTrace.cache_maps rest next.1 next.2 partition] + have unchanged : partition.cache plan.after = partition.cache _ := partition.intern _ _ + rw [unchanged, BetaHeadReduction.cache_maps call plan.sourceHeadReads coherent (.lam _ _ _ _ _) partition] +termination_by structural trace + +theorem BetaHeadReduction.cache_maps {fuel : Nat} {flags : WhnfFlags} + {before after : TcState .anon} {source result : KExpr .anon} {term target : AExpr β} + (call : BetaHeadReduction resolve locals fuel flags before source term after result target) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) (terminal : BetaWhnfTerminal result) (partition : WhnfCachePartition) : + partition.cache after = BetaCacheEvent.apply (call.cacheEvents reading coherent terminal) partition (partition.cache before) := + match call with + | .reduce path moving enough miss => by + rw [BetaHeadReduction.cacheEvents, BetaCacheEvent.apply_append, partition.writeCore, + BetaWhnfTrace.cache_maps path reading ((betaWhnfKey_environment _ _).symm ▸ coherent) partition, + partition.key] + rfl + | .cached .. => partition.key _ _ +termination_by structural call + +end + +def BetaCoreExecution.cacheEvents {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (execution : BetaCoreExecution resolve locals fuel before source term result target) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) : List (BetaCacheEvent β) := + execution.headReduction.cacheEvents reading coherent execution.terminal + +theorem BetaCoreExecution.cache_maps {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (execution : BetaCoreExecution resolve locals fuel before source term result target) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) (partition : WhnfCachePartition) : + partition.cache execution.after = BetaCacheEvent.apply (execution.cacheEvents reading coherent) partition (partition.cache before) := + execution.headReduction.cache_maps reading coherent execution.terminal partition + +def BetaNoDeltaExecution.cacheEvents {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) : List (BetaCacheEvent β) := + match execution with + | .reduce core miss => + core.cacheEvents reading ((betaWhnfKey_environment _ _).symm ▸ coherent) ++ + if inactive : before.inNativeReduce = false then [.noDelta (.reduce core miss) coherent inactive] else [] + | .cached .. => [] + +theorem BetaNoDeltaExecution.cache_maps {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (execution : BetaNoDeltaExecution resolve locals fuel before source term result target) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) (partition : WhnfCachePartition) : + partition.cache execution.after = BetaCacheEvent.apply (execution.cacheEvents reading coherent) partition (partition.cache before) := by + cases execution with + | reduce core miss => + have native := core.frame.native.trans (betaWhnfKey_native source before) + rw [after, partition.writeNoDelta, native, cacheEvents, BetaCacheEvent.apply_append] + rw [core.cache_maps reading ((betaWhnfKey_environment _ _).symm ▸ coherent) partition, partition.key] + by_cases inactive : before.inNativeReduce = false + · rw [dif_pos inactive] + simp only [BetaCacheEvent.apply, List.foldl_cons, List.foldl_nil, BetaCacheEvent.step, + BetaCacheEvent.noDelta, inactive, Bool.false_eq_true, if_false] + · rw [dif_neg inactive] + have active : before.inNativeReduce = true := by + cases value : before.inNativeReduce <;> simp_all + simp only [active, if_true, BetaCacheEvent.apply_nil] + | cached => exact partition.key _ _ + +def BetaPublicExecution.cacheEvents {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (execution : BetaPublicExecution resolve locals fuel before source term result target) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) : List (BetaCacheEvent β) := + match execution with + | .reduce inner miss enough => + have initial : (betaWhnfCharge (BetaPublicWhnf.outerKey source before).2).env.intern.WF := by + simpa only [BetaPublicWhnf.outerKey, betaWhnfCharge_fields, betaWhnfKey_environment, + betaWhnfPrefix_fields] using coherent + inner.cacheEvents reading initial ++ + if inactive : before.inNativeReduce = false then [.full (.reduce inner miss enough) coherent inactive] else [] + | .cached .. => [] + +theorem BetaPublicExecution.cache_maps {fuel : Nat} {before : TcState .anon} + {source result : KExpr .anon} {term target : AExpr β} + (execution : BetaPublicExecution resolve locals fuel before source term result target) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) (partition : WhnfCachePartition) : + partition.cache execution.after = BetaCacheEvent.apply (execution.cacheEvents reading coherent) partition (partition.cache before) := by + cases execution with + | reduce inner miss enough => + have initial : (betaWhnfCharge (BetaPublicWhnf.outerKey source before).2).env.intern.WF := by + simpa only [BetaPublicWhnf.outerKey, betaWhnfCharge_fields, betaWhnfKey_environment, + betaWhnfPrefix_fields] using coherent + have native : inner.after.inNativeReduce = before.inNativeReduce := by + rw [inner.frame.native] + simp only [BetaPublicWhnf.outerKey, betaWhnfCharge_fields, betaWhnfKey_native, betaWhnfPrefix_fields] + rw [after, partition.writeFull, native, cacheEvents, BetaCacheEvent.apply_append, + inner.cache_maps reading initial partition, partition.charge] + simp only [BetaPublicWhnf.outerKey, partition.key, partition.instrument] + by_cases inactive : before.inNativeReduce = false + · rw [dif_pos inactive] + simp only [BetaCacheEvent.apply, List.foldl_cons, List.foldl_nil, BetaCacheEvent.step, + BetaCacheEvent.full, inactive, Bool.false_eq_true, if_false, BetaPublicWhnf.outerKey] + · rw [dif_neg inactive] + have active : before.inNativeReduce = true := by + cases value : before.inNativeReduce <;> simp_all + simp only [active, if_true, BetaCacheEvent.apply_nil] + | cached => + simp only [after, cacheEvents, BetaCacheEvent.apply_nil, BetaPublicWhnf.outerKey, + partition.key, partition.instrument] + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaCacheReannotation.lean b/Ix/Kernel/Verify/Consistency/BetaCacheReannotation.lean index 71921e6f2..493858220 100644 --- a/Ix/Kernel/Verify/Consistency/BetaCacheReannotation.lean +++ b/Ix/Kernel/Verify/Consistency/BetaCacheReannotation.lean @@ -32,6 +32,9 @@ def BetaCoreExecution.reannotate {γ : Type v} {originResolve : Address → Opti | .cached origin initial hit => let rebuilt := BetaCoreExecution.reannotate origin reading initial ⟨rebuilt.1, ⟨.cached rebuilt.2.1 initial hit, rfl⟩⟩ + | .cachedHead origin initial terminal hit => + let rebuilt := origin.reannotate reading initial + ⟨rebuilt.1, ⟨.cachedHead rebuilt.2 initial terminal hit, rfl⟩⟩ termination_by structural execution def BetaNoDeltaExecution.reannotate {γ : Type v} {originResolve : Address → Option (ConstRef γ)} diff --git a/Ix/Kernel/Verify/Consistency/BetaHistoryInference.lean b/Ix/Kernel/Verify/Consistency/BetaHistoryInference.lean new file mode 100644 index 000000000..b91cad978 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaHistoryInference.lean @@ -0,0 +1,74 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaHistorySource +import Ix.Kernel.Verify.Consistency.BetaSourceInference +import Ix.Kernel.Verify.Consistency.BetaExposureConstruction + +/-! Original source checking supplies the semantics of a cache history. +Pi and sort exposure preserve that same operational history for later calls. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +def BetaPiExposure.cacheHistory {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source rawDomain rawBody : KExpr .anon} {term domain body : AExpr β} {condition : Certified.PropWhen} + (exposure : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody) + (history : BetaCacheHistory β before) (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) : BetaCacheHistory β exposure.after := by + cases exposure with + | reduce plan => + simpa only [BetaPiExposure.after, plan.execution_after] using history.afterPublic plan.execution reading coherent + | execute execution => exact history.afterPublic execution reading coherent + | cached => exact history.instrument.key source + +def BetaSortExposure.cacheHistory {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before : TcState .anon} + {source : KExpr .anon} {term : AExpr β} {level : KUniv .anon} + (exposure : BetaSortExposure resolve locals fuel before source term level) + (history : BetaCacheHistory β before) (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) : BetaCacheHistory β exposure.after := by + cases exposure with + | direct => exact history + | reduce plan => + simpa only [BetaSortExposure.after, plan.execution_after] using history.afterPublic plan.execution reading coherent + | execute execution => exact history.afterPublic execution reading coherent + | cached => exact history.instrument.key source + +/-- A successful public call consumes one retained history and returns the +history of its complete result state. Individual cache typing, annotation, +and producing-execution premises are recovered by selection. -/ +theorem SynthesisInference.beta_public_of_history {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} {bounds : List VLevel} {locals : List FVarId} + {fuel : Nat} {inferenceBefore inferenceAfter : TcState .anon} {source inferred : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel inferenceBefore source term type level) + (formed : ContextFormation.{u,v} entries context bounds) + (agreement : LocalContextReading resolve locals inferenceBefore.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (inferredRun : RecM.infer source (methodsN fuel) inferenceBefore = .ok inferred inferenceAfter) + {reductionFuel : Nat} {before after : TcState .anon} {result : KExpr .anon} + (chosen : BetaWhnfSource.selected source = true) + (history : BetaCacheHistory β before) (data : history.KeyData source) + (cold : (BetaPublicWhnf.outerKey source before).2.env.whnfCache[(BetaPublicWhnf.outerKey source before).1]? = none → + (BetaPublicWhnf.noDeltaKey source before).2.env.whnfNoDeltaCache[(BetaPublicWhnf.noDeltaKey source before).1]? = none → + (BetaPublicWhnf.coreKey source before).2.env.whnfCoreCache[(BetaPublicWhnf.coreKey source before).1]? = none → + BetaWhnfSource.Resources resolve locals (reductionFuel + 1) .FULL maxWhnfCoreFuel.toNat (BetaPublicWhnf.coreKey source before).2 source) + (coherent : before.env.intern.WF) + (accepted : (RecM.whnf source).run (methodsN (reductionFuel + 1)) before = .ok result after) : + ∃ target, BetaWhnfTerminal result ∧ readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ TypingClaim.{u,v} entries context target type ∧ + after.env.intern.WF ∧ Nonempty (BetaCacheHistory β after) := by + obtain ⟨target, execution, stateEq, preserved⟩ := + BetaPublicExecution.exists_of_history chosen history data cold reading coherent accepted + have sound := (support.beta_public_execution_sound formed agreement reading inferredRun execution coherent).2 + rw [stateEq] at sound + exact ⟨target, execution.terminal, sound.1, sound.2.1, sound.2.2.1, sound.2.2.2, preserved⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaHistorySource.lean b/Ix/Kernel/Verify/Consistency/BetaHistorySource.lean new file mode 100644 index 000000000..11491dca3 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaHistorySource.lean @@ -0,0 +1,188 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaCacheHistory +import Ix.Kernel.Verify.Consistency.BetaCacheConstruction + +/-! A single retained map history supplies the origins at every cache layer. +The caller provides finite collision data and the resources needed on misses. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +namespace BetaCacheEventOrigin + +variable {β : Type u} {partition : WhnfCachePartition} {key : Address × Address} + {source result : KExpr .anon} + +theorem terminal (origin : BetaCacheEventOrigin (β := β) partition key source result) : + BetaWhnfTerminal result := + match origin with + | .head _ _ terminal => terminal + | .noDelta execution .. => execution.terminal + | .full execution .. => execution.terminal + +theorem headOrigin (origin : BetaCacheEventOrigin (β := β) partition key source result) + (core : partition = .core ∨ partition = .coreCheap) : Nonempty (BetaHeadCacheOrigin (β := β) source result) := + match origin with + | .head call coherent _ => ⟨call.origin coherent⟩ + | .noDelta .. => by rcases core with impossible | impossible <;> cases impossible + | .full .. => by rcases core with impossible | impossible <;> cases impossible + +theorem noDeltaOrigin (origin : BetaCacheEventOrigin (β := β) partition key source result) + (selected : partition = .noDelta) : + ∃ resolve : Address → Option (ConstRef β), ∃ locals fuel before term target, + ∃ _ : BetaNoDeltaExecution resolve locals fuel before source term result target, before.env.intern.WF := + match origin with + | .head (flags := flags) _ _ _ => by + cases full : flags.isFull <;> simp only [WhnfCachePartition.ofFlags, full, Bool.false_eq_true, if_false, if_true] at selected + all_goals cases selected + | .noDelta execution coherent _ => ⟨_, _, _, _, _, _, execution, coherent⟩ + | .full .. => by cases selected + +theorem fullOrigin (origin : BetaCacheEventOrigin (β := β) partition key source result) + (selected : partition = .full) : + ∃ resolve : Address → Option (ConstRef β), ∃ locals fuel before term target, + ∃ _ : BetaPublicExecution resolve locals fuel before source term result target, before.env.intern.WF := + match origin with + | .head (flags := flags) _ _ _ => by + cases full : flags.isFull <;> simp only [WhnfCachePartition.ofFlags, full, Bool.false_eq_true, if_false, if_true] at selected + all_goals cases selected + | .noDelta .. => by cases selected + | .full execution coherent _ => ⟨_, _, _, _, _, _, execution, coherent⟩ + +end BetaCacheEventOrigin + +namespace BetaCacheHistory + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + {fuel : Nat} {state : TcState .anon} {source : KExpr .anon} + +theorem headOrigins (history : BetaCacheHistory β state) (data : history.KeyData source) (flags : WhnfFlags) : + BetaWhnfSource.HeadOrigins (β := β) flags state source := by + intro cached found + rw [WhnfCachePartition.lookupCore, WhnfCachePartition.key] at found + obtain ⟨origin⟩ := history.selected data (betaWhnfKey_address source state) found + apply origin.headOrigin + cases full : flags.isFull <;> simp [WhnfCachePartition.ofFlags, full] + +/-- A full structural hit can originate at a recursive head call, including +one that ran with zero method depth. The current replay constructs the +ordinary structural witness without assuming matching annotations. -/ +theorem coreResources (history : BetaCacheHistory β state) (data : history.KeyData source) + (coherent : state.env.intern.WF) + (cold : (betaWhnfKey source state).2.env.whnfCoreCache[(betaWhnfKey source state).1]? = none → + BetaWhnfSource.Resources resolve locals (fuel + 1) .FULL maxWhnfCoreFuel.toNat (betaWhnfKey source state).2 source) : + BetaWhnfSource.CoreResources resolve locals fuel state source := by + refine ⟨?_, cold⟩ + intro cached found + have stored : (WhnfCachePartition.core.cache state)[(betaWhnfKey source state).1]? = some cached := by + simpa only [WhnfCachePartition.cache, betaWhnfKey_environment] using found + obtain ⟨origin⟩ := history.selected data (betaWhnfKey_address source state) stored + obtain ⟨producer⟩ := origin.headOrigin (.inl rfl) + exact ⟨producer.resolve, producer.locals, 0, state, producer.term, producer.target, + .cachedHead producer.call producer.coherent origin.terminal found, coherent⟩ + +theorem noDeltaResources (history : BetaCacheHistory β state) (data : history.KeyData source) + (coherent : state.env.intern.WF) + (cold : (betaWhnfKey source state).2.env.whnfNoDeltaCache[(betaWhnfKey source state).1]? = none → + (betaWhnfKey source state).2.env.whnfCoreCache[(betaWhnfKey source state).1]? = none → + BetaWhnfSource.Resources resolve locals (fuel + 1) .FULL maxWhnfCoreFuel.toNat (betaWhnfKey source state).2 source) : + BetaWhnfSource.NoDeltaResources resolve locals fuel state source := by + constructor + · intro cached found + have stored : (WhnfCachePartition.noDelta.cache state)[(betaWhnfKey source state).1]? = some cached := by + simpa only [WhnfCachePartition.cache, betaWhnfKey_environment] using found + obtain ⟨origin⟩ := history.selected data (betaWhnfKey_address source state) stored + exact origin.noDeltaOrigin rfl + · intro missing + apply (history.key source).coreResources data ((betaWhnfKey_environment _ _).symm ▸ coherent) + intro coreMissing + simpa only [betaWhnfKey_replay] using cold missing (by simpa only [betaWhnfKey_replay] using coreMissing) + +theorem publicResources (history : BetaCacheHistory β state) (data : history.KeyData source) + (coherent : state.env.intern.WF) + (cold : (BetaPublicWhnf.outerKey source state).2.env.whnfCache[(BetaPublicWhnf.outerKey source state).1]? = none → + (BetaPublicWhnf.noDeltaKey source state).2.env.whnfNoDeltaCache[(BetaPublicWhnf.noDeltaKey source state).1]? = none → + (BetaPublicWhnf.coreKey source state).2.env.whnfCoreCache[(BetaPublicWhnf.coreKey source state).1]? = none → + BetaWhnfSource.Resources resolve locals (fuel + 1) .FULL maxWhnfCoreFuel.toNat (BetaPublicWhnf.coreKey source state).2 source) : + BetaWhnfSource.PublicResources resolve locals fuel state source := by + constructor + · intro cached found + have stored : (WhnfCachePartition.full.cache state)[(BetaPublicWhnf.outerKey source state).1]? = some cached := by + simpa only [WhnfCachePartition.cache, BetaPublicWhnf.outerKey, betaWhnfKey_environment, betaWhnfPrefix_fields] using found + obtain ⟨origin⟩ := history.selected data (betaWhnfKey_address source (betaWhnfPrefix state)) stored + exact origin.fullOrigin rfl + · intro missing + have initial : (betaWhnfCharge (BetaPublicWhnf.outerKey source state).2).env.intern.WF := by + simpa only [BetaPublicWhnf.outerKey, betaWhnfCharge_fields, betaWhnfKey_environment, betaWhnfPrefix_fields] using coherent + apply ((history.instrument.key source).charge).noDeltaResources data initial + intro noDeltaMissing coreMissing + have missingCore : (BetaPublicWhnf.coreKey source state).2.env.whnfCoreCache[ + (BetaPublicWhnf.coreKey source state).1]? = none := by + simpa only [BetaPublicWhnf.coreKey, BetaPublicWhnf.noDeltaKey, BetaPublicWhnf.outerKey, betaWhnfKey_replay] using coreMissing + simpa only [BetaPublicWhnf.coreKey, BetaPublicWhnf.noDeltaKey, BetaPublicWhnf.outerKey, betaWhnfKey_replay] using + cold missing noDeltaMissing missingCore + +end BetaCacheHistory + +theorem BetaCoreExecution.exists_of_history {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} + {source result : KExpr .anon} {term : AExpr β} + (chosen : BetaWhnfSource.selected source = true) + (history : BetaCacheHistory β before) (data : history.KeyData source) + (cold : (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? = none → + BetaWhnfSource.Resources resolve locals (fuel + 1) .FULL maxWhnfCoreFuel.toNat (betaWhnfKey source before).2 source) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) + (accepted : (RecM.whnfCore source).run (methodsN (fuel + 1)) before = .ok result after) : + ∃ target, ∃ execution : BetaCoreExecution resolve locals fuel before source term result target, + execution.after = after ∧ Nonempty (BetaCacheHistory β after) := by + obtain ⟨target, execution, stateEq⟩ := exists_of_success chosen (history.coreResources data coherent cold) reading coherent accepted + refine ⟨target, execution, stateEq, ?_⟩ + rw [← stateEq] + exact ⟨history.afterCore execution reading coherent⟩ + +theorem BetaNoDeltaExecution.exists_of_history {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} + {source result : KExpr .anon} {term : AExpr β} + (chosen : BetaWhnfSource.selected source = true) + (history : BetaCacheHistory β before) (data : history.KeyData source) + (cold : (betaWhnfKey source before).2.env.whnfNoDeltaCache[(betaWhnfKey source before).1]? = none → + (betaWhnfKey source before).2.env.whnfCoreCache[(betaWhnfKey source before).1]? = none → + BetaWhnfSource.Resources resolve locals (fuel + 1) .FULL maxWhnfCoreFuel.toNat (betaWhnfKey source before).2 source) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) + (accepted : (RecM.whnfNoDelta source).run (methodsN (fuel + 1)) before = .ok result after) : + ∃ target, ∃ execution : BetaNoDeltaExecution resolve locals fuel before source term result target, + execution.after = after ∧ Nonempty (BetaCacheHistory β after) := by + obtain ⟨target, execution, stateEq⟩ := exists_of_success chosen (history.noDeltaResources data coherent cold) reading coherent accepted + refine ⟨target, execution, stateEq, ?_⟩ + rw [← stateEq] + exact ⟨history.afterNoDelta execution reading coherent⟩ + +theorem BetaPublicExecution.exists_of_history {β : Type u} {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {fuel : Nat} {before after : TcState .anon} + {source result : KExpr .anon} {term : AExpr β} + (chosen : BetaWhnfSource.selected source = true) + (history : BetaCacheHistory β before) (data : history.KeyData source) + (cold : (BetaPublicWhnf.outerKey source before).2.env.whnfCache[(BetaPublicWhnf.outerKey source before).1]? = none → + (BetaPublicWhnf.noDeltaKey source before).2.env.whnfNoDeltaCache[(BetaPublicWhnf.noDeltaKey source before).1]? = none → + (BetaPublicWhnf.coreKey source before).2.env.whnfCoreCache[(BetaPublicWhnf.coreKey source before).1]? = none → + BetaWhnfSource.Resources resolve locals (fuel + 1) .FULL maxWhnfCoreFuel.toNat (BetaPublicWhnf.coreKey source before).2 source) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : before.env.intern.WF) + (accepted : (RecM.whnf source).run (methodsN (fuel + 1)) before = .ok result after) : + ∃ target, ∃ execution : BetaPublicExecution resolve locals fuel before source term result target, + execution.after = after ∧ Nonempty (BetaCacheHistory β after) := by + obtain ⟨target, execution, stateEq⟩ := exists_of_success chosen (history.publicResources data coherent cold) reading coherent accepted + refine ⟨target, execution, stateEq, ?_⟩ + rw [← stateEq] + exact ⟨history.afterPublic execution reading coherent⟩ + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean b/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean index 50253ed20..11727f615 100644 --- a/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean +++ b/Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean @@ -118,6 +118,7 @@ def BetaCoreExecution.betaTrace {β : Type u} {resolve : Address → Option (Con match execution with | .reduce path .. => (path.annotate typing).1.toBetaTrace typing.origin | .cached origin .. => origin.betaTrace typing + | .cachedHead origin .. => (origin.annotate typing).2 /-- The cached result retains the executed beta origin; the source's current checking derivation supplies its meaning in the current model context. -/ diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 03b00ae26..9c8b2fe8a 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -3968,6 +3968,130 @@ private def headWhnfLegacyKeys (retained : Bool) : Bool := | .ok passed _ => passed | .error _ _ => false +/-- Compare every WHNF partition, including keys from exited local scopes. -/ +private def exactWhnfCaches (full noDelta noDeltaCheap core coreCheap : List (KExpr .anon × KExpr .anon)) + (state : TcState .anon) : Bool := + let expected (entries : List (KExpr .anon × KExpr .anon)) := entries.foldl + (fun (cache : Std.HashMap (Address × Address) (KExpr .anon)) (source, result) => + cache.insert (source.addr, emptyCtxAddr) result) ∅ + let sameMap (actual predicted : Std.HashMap (Address × Address) (KExpr .anon)) := + actual.size == predicted.size && predicted.toList.all fun (key, result) => actual[key]? == some result + sameMap state.env.whnfCache (expected full) && sameMap state.env.whnfNoDeltaCache (expected noDelta) && + sameMap state.env.whnfNoDeltaCheapCache (expected noDeltaCheap) && + sameMap state.env.whnfCoreCache (expected core) && sameMap state.env.whnfCoreCheapCache (expected coreCheap) + +/-- A history mixes real publications and hits across all supported layers. +Partial loop failure and a failing local scope retain their completed writes; +clearing discards them, and a new call rebuilds the required entries. -/ +private def whnfExactCacheHistory (typeLevel nativeFirst inferOnly noAccel : Bool) : Bool := + let carrier := KExpr.mkSort (m := .anon) (if typeLevel then levelOne else .mkZero) + let carrierType := KExpr.mkSort (m := .anon) (if typeLevel then levelTwo else levelOne) + let identity := KExpr.mkLam () () carrierType (.mkVar 0 ()) + let identityType := KExpr.mkAll () () carrierType carrierType + let head := KExpr.mkLet () identityType identity (.mkVar 0 ()) false + let parent := KExpr.mkApp head carrier + let other := KExpr.mkApp (.mkLam () () carrierType (.mkAll () () (.mkVar 0 ()) (.mkVar 1 ()))) carrier + let otherResult := KExpr.mkAll () () carrier carrier + let builder := KExpr.mkLam () () carrierType (.mkLam () () (.mkVar 0 ()) (.mkVar 0 ())) + let builderType := KExpr.mkAll () () carrierType (.mkAll () () (.mkVar 0 ()) (.mkVar 1 ())) + let builderHead := KExpr.mkLet () builderType builder (.mkVar 0 ()) false + let maps (full noDelta core cheap : List (KExpr .anon × KExpr .anon)) (state : TcState .anon) := + exactWhnfCaches full noDelta [] core cheap state && exactInferenceCaches [] [] state + let action : RecM .anon Bool := do + modify fun state => {state with recFuel := 0, stats := true, inNativeReduce := nativeFirst, inferOnly, noAccel} + if !maps [] [] [] [] (← get) then return false + let .ok warmed afterHead := (RecM.whnfCore head).run (methodsN 0) (← get) | return false + set afterHead + let first := [(head, identity)] + if warmed != identity || !maps [] [] first [] (← get) then return false + let .error .maxRecDepth exhausted := + (RecM.runBounded (fun term => RecM.whnfCoreWithFlagsStep term .DEF_EQ_CORE) 1 parent).run + (methodsN 1) (← get) | return false + set exhausted + if !maps [] [] first first (← get) then return false + modify fun state => {state with recFuel := 1} + let .ok reduced afterParent := (RecM.whnf parent).run (methodsN 1) (← get) | return false + set afterParent + let published := [(parent, carrier)] + let upper := if nativeFirst then [] else published + let core := first ++ published + if reduced != carrier || (← get).recFuel != 0 || !maps upper upper core first (← get) then return false + let .ok different afterOther := (RecM.whnfCoreWithFlags other .DEF_EQ_CORE).run (methodsN 1) (← get) | return false + set afterOther + let cheap := first ++ [(other, otherResult)] + if different != otherResult || !maps upper upper core cheap (← get) then return false + let runScope (expectedLocal : KExpr .anon) (failAfter : Bool) : RecM .anon Bool := do + let scopeAction : RecM .anon Bool := RecM.withLctxScope do + let (openedCarrier, _) ← TcM.openBinder () () carrierType (.mkVar 0 ()) + if openedCarrier != expectedLocal then return false + let source := KExpr.mkApp builderHead openedCarrier + let expected := KExpr.mkLam () () openedCarrier (.mkVar 0 ()) + modify fun state => {state with recFuel := 1, inNativeReduce := false, inferOnly := !state.inferOnly} + let .ok value after := (RecM.whnf source).run (methodsN 1) (← get) | return false + set after + if !sameSourceExpr value expected then return false + if failAfter then throw (.other "WHNF history scope failure") + return true + if failAfter then + try + discard scopeAction + return false + catch error => + return match error with + | .other message => message == "WHNF history scope failure" + | _ => false + else scopeAction + let firstLocal := KExpr.mkFVar ⟨(← get).env.nextFVarId⟩ () + if !(← runScope firstLocal false) then return false + let firstScoped := (KExpr.mkApp builderHead firstLocal, KExpr.mkLam () () firstLocal (.mkVar 0 ())) + let scopedCore := core ++ [(builderHead, builder), firstScoped] + let scopedUpper := upper ++ [firstScoped] + if (← get).lctx.size != 0 || !maps scopedUpper scopedUpper scopedCore cheap (← get) then return false + let secondLocal := KExpr.mkFVar ⟨(← get).env.nextFVarId⟩ () + if secondLocal == firstLocal || !(← runScope secondLocal true) then return false + let secondScoped := (KExpr.mkApp builderHead secondLocal, KExpr.mkLam () () secondLocal (.mkVar 0 ())) + let finalCore := scopedCore ++ [secondScoped] + let finalUpper := scopedUpper ++ [secondScoped] + if (← get).lctx.size != 0 || !maps finalUpper finalUpper finalCore cheap (← get) then return false + modify fun state => {state with recFuel := if nativeFirst then 1 else 0} + let .ok replay afterReplay := (RecM.whnf parent).run (methodsN 0) (← get) | return false + set afterReplay + let replayedUpper := finalUpper ++ published + if replay != carrier || (← get).recFuel != 0 || !maps replayedUpper replayedUpper finalCore cheap (← get) then return false + let nextLocal := (← get).env.nextFVarId + modify fun state => {state with recFuel := 1, env := state.env.clearReductionCaches} + let cleared ← get + if !maps [] [] [] [] cleared then return false + match (RecM.whnf parent).run (methodsN 0) cleared with + | .error .maxRecFuel failed => if !maps [] [] [] [] failed then return false + | _ => return false + let .ok rebuilt afterRebuild := (RecM.whnf parent).run (methodsN 1) cleared | return false + let .ok lastReplay final := (RecM.whnf parent).run (methodsN 0) afterRebuild | return false + return rebuilt == carrier && lastReplay == carrier && maps published published core [] final && + final.recFuel == 0 && final.lctx.size == 0 && final.env.nextFVarId == nextLocal && final.inferOnly == inferOnly + match TcM.runRec action (TcState.newLazyAnon {}) with + | .ok passed _ => passed + | .error _ _ => false + +/-- Lookup alone cannot identify the query's source: forged metadata can +hit a real prior publication. Clearing exposes the different computation. -/ +private def whnfForeignHistoryKey (native : Bool) : Bool := Id.run do + let prop := KExpr.mkSort (m := .anon) .mkZero + let sort := KExpr.mkSort (m := .anon) levelOne + let source := KExpr.mkApp (.mkLam () () sort (.mkVar 0 ())) prop + let alien := KExpr.mkApp (.mkLam () () sort (.mkAll () () (.mkVar 0 ()) (.mkVar 1 ()))) prop + let forged := match alien with + | .app function argument info => KExpr.app function argument {info with addr := source.addr} + | _ => alien + let expected := KExpr.mkAll () () prop prop + let before := {TcState.newLazyAnon {} with recFuel := 1} + let .ok result stored := (RecM.whnf source).run (methodsN 1) before | return false + let .ok replay after := (RecM.whnf forged).run (methodsN 0) {stored with recFuel := 0, inNativeReduce := native} | return false + let .ok fresh _ := (RecM.whnf forged).run (methodsN 1) + {after with recFuel := 1, inNativeReduce := false, env := after.env.clearReductionCaches} | return false + return result == prop && replay == prop && !sameSourceExpr source forged && fresh == expected && + exactWhnfCaches [(source, prop)] [(source, prop)] [] [(source, prop)] [] after && after.recFuel == 0 + private def letWhnfCases : TestSeq := [WhnfWarmLayer.cold, .core, .noDelta, .full].foldl (fun suite layer => suite ++ test s!"let WHNF: layer {reprStr (match layer with | .cold => 0 | .core => 1 | .noDelta => 2 | .full => 3)} preserves exact maps across let-beta-let reduction" @@ -4056,6 +4180,12 @@ private def headWhnfCases : TestSeq := ([false, true].all fun typeLevel => [WhnfFlags.FULL, .DEF_EQ_CORE].all fun flags => (List.range 3).all fun shape => [false, true].all fun native => headWhnfLoopResult 2 typeLevel flags shape false native true true) + ++ test "WHNF cache history: complete maps survive scopes, partial failures, intervening calls, and clearing" + ([false, true].all fun typeLevel => [false, true].all fun native => + [false, true].all fun inferOnly => [false, true].all fun noAccel => + whnfExactCacheHistory typeLevel native inferOnly noAccel) + ++ test "WHNF cache history: forged source metadata demonstrates the finite collision boundary" + ([false, true].all whnfForeignHistoryKey) ++ test "application head sort exposure: nested callbacks retain all binder child types across cache layers" ([false, true].all fun typeLevel => [false, true].all fun instrumented => [false, true].all fun noAccel => [0, 1, 2].all fun lowerWarm => sortExposureInferencePaths typeLevel false instrumented noAccel lowerWarm false true) diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index 3d13651d1..af2c97bf7 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -601,7 +601,7 @@ lake test --wfail -- tc-unit lake -d Models/SetTheory build --wfail ``` -The consistency target checks 1,549 exact theorem boundaries. The production +The consistency target checks 1,623 exact theorem boundaries. The production environment roots retain four existing generated output-length proofs, reached through expression/universe construction, names, and the full production method table. They introduce no new native proofs. The model @@ -815,6 +815,33 @@ constructors derive replay resources from actual publication. These 31 further audited boundaries forbid the semantic hereditary invariant and add no axiom or native proof. General construction and preservation of source resources and cache origins through arbitrary checker histories remain open. +`BetaCacheHistory` now derives the provenance of every selected WHNF entry +from empty initial maps and actual supported publications. `BetaCacheEvent` +retains each producing call, its original annotations and intern coherence, +the physical partition and key, and the returned expression. Folding the +events reconstructs all five complete maps. Recursive head publications +precede their enclosing calls; hits add no events, and native reduction +suppresses only the same upper writes as production. The cheap no-delta map +is preserved by this fragment. Repeated writes and unrelated keys are included. +Interning, key memoization, instrumentation, fuel charges, binder/let opening, +and scope cleanup preserve the history. Entries from exited scopes retain +their original producers. `boundedCacheHistory` also follows an actual outer +loop failure and retains completed head calls and other preceding writes; +its supported head callbacks still require successful traces. Clearing the +production reduction caches starts a new empty history. +The history's finite `KeyData` identifies the queried expression among the +recorded inputs. Selection then supplies head origins and the origin fields +of all three public cache-resource layers. `BetaCoreExecution.cachedHead` +permits a public structural hit to reuse a recursive head producer, including +one that ran with zero method depth. `exists_of_history` reconstructs actual +successful calls and returns their final histories. Pi/sort exposure retains +the same history, and `SynthesisInference.beta_public_of_history` derives +conversion, typing, result reading, and the updated history from the original +inference. These 74 boundaries introduce no axiom or native proof; the 73 raw +boundaries forbid the semantic hereditary invariant. General history +construction through arbitrary inference, loading, other reducers, and +failures inside head callbacks remains open, as does automatic construction +of the finite resources needed on misses. `SynthesisInference.cached` retains the original tree behind an inference cache hit. Its soundness and beta derivations reuse the actual lambda-body, dependent codomain, and argument checks. `reuseFull` derives the cached result @@ -1073,7 +1100,11 @@ partial cache writes on failure, distinct legacy key radii, binder inference, parameterized declaration admission, and rejection of a Pi where a sort is required. An isolated inner hit reduces the required method depth and supplies later parent hits; an entry in the other cache partition preserves the cold bound. -The unit suite contains 761 checks. The anonymous differential additionally +Two WHNF-history regressions compare all five maps across independent calls, +native guards, successful and failing scopes, partial loop failure, zero-method +replay, clearing, and rebuilding. A forged query at an occupied key demonstrates +why semantic selection needs finite collision data. +The unit suite contains 763 checks. The anonymous differential additionally serializes eleven cycle-policy fixtures and checks exact target sets, verdicts, failure counts, and cycle diagnostics in both implementations. @@ -1124,6 +1155,7 @@ The VM pilot is preserved in the frozen archive and excluded from the host gate. | Let inference and typed cache history | [`Consistency/LetInference.lean`](../Ix/Kernel/Verify/Consistency/LetInference.lean), [`LetSynthesis.lean`](../Ix/Kernel/Verify/Consistency/LetSynthesis.lean), [`LetCache.lean`](../Ix/Kernel/Verify/Consistency/LetCache.lean), [`SynthesisCacheExecution.lean`](../Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean) | | Public beta WHNF, cache writes and replay, and Pi/sort exposure | [`BetaCacheExecution.lean`](../Ix/Kernel/Verify/Consistency/BetaCacheExecution.lean), [`BetaCacheKeys.lean`](../Ix/Kernel/Verify/Consistency/BetaCacheKeys.lean), [`BetaPublicWhnf.lean`](../Ix/Kernel/Verify/Consistency/BetaPublicWhnf.lean), [`BetaWhnfInference.lean`](../Ix/Kernel/Verify/Consistency/BetaWhnfInference.lean) | | Fresh annotations and retained WHNF producer reconstruction | [`BetaReannotation.lean`](../Ix/Kernel/Verify/Consistency/BetaReannotation.lean), [`BetaHeadOrigin.lean`](../Ix/Kernel/Verify/Consistency/BetaHeadOrigin.lean), [`BetaCacheReannotation.lean`](../Ix/Kernel/Verify/Consistency/BetaCacheReannotation.lean), [`BetaTraceConstruction.lean`](../Ix/Kernel/Verify/Consistency/BetaTraceConstruction.lean) | +| Complete WHNF maps and source-derived cache origins | [`BetaCacheEvent.lean`](../Ix/Kernel/Verify/Consistency/BetaCacheEvent.lean), [`BetaCachePublications.lean`](../Ix/Kernel/Verify/Consistency/BetaCachePublications.lean), [`BetaCacheHistory.lean`](../Ix/Kernel/Verify/Consistency/BetaCacheHistory.lean), [`BetaHistorySource.lean`](../Ix/Kernel/Verify/Consistency/BetaHistorySource.lean), [`BetaHistoryInference.lean`](../Ix/Kernel/Verify/Consistency/BetaHistoryInference.lean) | | Retained type checks and changed cheap beta in lambda inference | [`Consistency/SynthesisInference.lean`](../Ix/Kernel/Verify/Consistency/SynthesisInference.lean), [`CheapBetaReading.lean`](../Ix/Kernel/Verify/Consistency/CheapBetaReading.lean), [`Formation.lean`](../Ix/Kernel/Verify/Consistency/Formation.lean) | | Source beta reduction and declaration conversion | [`Consistency/BetaSpine.lean`](../Ix/Kernel/Verify/Consistency/BetaSpine.lean), [`Simultaneous.lean`](../Ix/Kernel/Verify/Consistency/Simultaneous.lean), [`SpineReading.lean`](../Ix/Kernel/Verify/Consistency/SpineReading.lean), [`CheapBeta.lean`](../Ix/Kernel/Verify/Consistency/CheapBeta.lean), [`Model/BetaSpine.lean`](../Ix/Theory/Model/BetaSpine.lean) | | Production environment fragment and relative axiom policy | [`Consistency/Environment.lean`](../Ix/Kernel/Verify/Consistency/Environment.lean), [`Production.lean`](../Ix/Kernel/Verify/Consistency/Production.lean) | diff --git a/docs/theory.md b/docs/theory.md index 311b9a0a7..dabcda5c0 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -124,6 +124,16 @@ intermediate states, method depth, and iteration count. All three WHNF cache layers support this reconstruction, and actual publication derives resources for later replay. Stored local let values and other head reducers remain outside that construction. +Complete WHNF histories now retain the producing calls for every publication +and reconstruct all five actual cache maps. They survive supported reduction, +binder/let opening, and scope cleanup; outer-loop exhaustion retains the +completed prefix's writes. Finite collision data identifies a queried source +among the recorded inputs, and the current reading reconstructs its producer's +annotations. This supplies the origin resources of public WHNF, including hits +produced by zero-depth head calls. The original source derivation then proves +conversion and typing while returning the history for subsequent calls. +Clearing starts a new empty history. Arbitrary inference and loader histories, +other reducers, and failures within head callbacks still require further work. Constructing initial inference resources for arbitrary accepted programs, general WHNF cache origins, the remaining reducers, and general conversion remain open. From 3a3ad242c21675698b5a64e5ab1004f1aee21f51 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 15 Sep 2026 06:50:16 -0400 Subject: [PATCH 49/63] Preserve WHNF histories and intern coherence through inference Every supported inference node now carries the WHNF cache history data and the intern coherence of its actual run: `InferenceCacheTrace.WhnfData` holds the exposure readings, `whnfHistory` composes retained publications along the real intermediate states, and `outputCoherent` derives coherence at the returned state. The synthesis cache execution supplies both from the original recursion, sort exposures preserve coherence via `SynthesisSortCheck.afterCoherent`, and ingress cache extension keeps all five WHNF maps. The `subst_spec` bound at application nodes is discharged through `simpa`, since omega treats `(0 : UInt64).toNat` as opaque. The new boundaries are audited alongside the WHNF history roots. --- Ix/Kernel/Verify/Consistency.lean | 8 +- Ix/Kernel/Verify/Consistency/Audit.lean | 28 +- .../Verify/Consistency/BetaHistoryState.lean | 118 ++++++++ Ix/Kernel/Verify/Consistency/BlockCache.lean | 5 +- .../Consistency/InferenceWhnfHistory.lean | 184 +++++++++++++ Ix/Kernel/Verify/Consistency/LazyCache.lean | 22 +- .../Consistency/SynthesisCacheExecution.lean | 91 ++++-- .../Consistency/SynthesisCoherence.lean | 259 ++++++++++++++++++ docs/theory.md | 6 +- 9 files changed, 683 insertions(+), 38 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/BetaHistoryState.lean create mode 100644 Ix/Kernel/Verify/Consistency/InferenceWhnfHistory.lean create mode 100644 Ix/Kernel/Verify/Consistency/SynthesisCoherence.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 20fb3f04c..313bb2e8f 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -58,6 +58,9 @@ import Ix.Kernel.Verify.Consistency.BetaCachePublications import Ix.Kernel.Verify.Consistency.BetaCacheHistory import Ix.Kernel.Verify.Consistency.BetaHistorySource import Ix.Kernel.Verify.Consistency.BetaHistoryInference +import Ix.Kernel.Verify.Consistency.BetaHistoryState +import Ix.Kernel.Verify.Consistency.InferenceWhnfHistory +import Ix.Kernel.Verify.Consistency.SynthesisCoherence import Ix.Kernel.Verify.Consistency.SynthesisAppCongruence import Ix.Kernel.Verify.Consistency.BetaPublicWhnf import Ix.Kernel.Verify.Consistency.WhnfCacheFrame @@ -204,7 +207,10 @@ history. Finite collision data identifies the queried source among the recorded inputs. The resulting provenance supplies all three cache layers' origin resources, including public replay of zero-depth head producers. Successful source reconstruction, Pi/sort exposure, and semantic public -reduction preserve the updated history for later calls. +reduction preserve the updated history for later calls. Verified standalone +and block loading preserve all five WHNF maps, every supported inference +node carries the complete WHNF history through its actual intermediate +states, and each returned state retains intern-table coherence. Only an outer miss charges shared fuel. Pi exposure uses the same complete cache-layer execution. Application inference uses that exposure between argument checks and derives the type conversion diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 1cdb3a8a7..61b400794 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -969,6 +969,26 @@ private def whnfHistoryExecutionRoots : Array Lean.Name := #[ ``BetaPublicExecution.exists_of_history, ``BetaPiExposure.cacheHistory, ``BetaSortExposure.cacheHistory ] +/-- Complete WHNF histories and intern coherence survive the supported +inference recursion. Exposure inputs derive their readings from the executed +source checks; no semantic typing invariant is assumed. -/ +private def inferenceHistoryFrameRoots : Array Lean.Name := #[ + ``LazyLookupFrame.whnf_maps, ``BetaHistoryReading +] + +private def inferenceHistoryExprRoots : Array Lean.Name := #[ + ``UncachedInference.keyedCoherent +] + +private def inferenceHistoryExecutionRoots : Array Lean.Name := #[ + ``BetaCacheHistory.afterInferKey, ``BetaCacheHistory.getConst, ``BetaCacheHistory.hashConversion, + ``BetaCacheHistory.afterMiss, ``BetaHistoryReading.afterPublic, ``BetaHistoryReading.afterPi, + ``BetaHistoryReading.afterSort, ``InferenceCacheTrace.WhnfData, ``InferenceCacheTrace.whnfHistory, + ``infer_miss_coherent, ``InferenceCacheTrace.const_coherent, ``InferenceCacheTrace.fvar_coherent, + ``BinderInference.outputCoherent, ``SynthesisInference.outputCoherent, + ``SynthesisSortCheck.historyReading, ``SynthesisSortCheck.afterCoherent +] + private def localScopeFrameRoots : Array Lean.Name := #[ ``LocalContext.Equiv.refl, ``LocalContext.Equiv.symm, ``LocalContext.Equiv.trans, ``LocalContext.Equiv.size, ``LocalContext.Equiv.find?, ``LocalContext.Equiv.wf, @@ -1583,11 +1603,12 @@ def roots : Array RootAllowance := #[ forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ #[``WhnfCachePartition, ``WhnfCachePartition.ofFlags].map (fun root => { root, forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) - ++ (mixedCacheFrameRoots ++ headWhnfFrameRoots ++ whnfHistoryFrameRoots ++ #[``betaWhnfCharge_success]).map (fun root => { + ++ (mixedCacheFrameRoots ++ headWhnfFrameRoots ++ whnfHistoryFrameRoots ++ inferenceHistoryFrameRoots ++ + #[``betaWhnfCharge_success]).map (fun root => { root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ (mixedCacheKeyRoots ++ betaSourceExprRoots ++ letWhnfExprRoots ++ headWhnfExprRoots ++ - betaReannotationExprRoots ++ whnfHistoryExprRoots).map (fun root => { + betaReannotationExprRoots ++ whnfHistoryExprRoots ++ inferenceHistoryExprRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ #[``SynthesisInference.beta_core_execution_sound, ``SynthesisInference.beta_noDelta_execution_sound, @@ -1598,7 +1619,8 @@ def roots : Array RootAllowance := #[ root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) ++ (recursiveLetShapeRoots ++ sortExposureRoots ++ mixedCacheExecutionRoots ++ betaSourceExecutionRoots ++ - letWhnfExecutionRoots ++ headWhnfExecutionRoots ++ betaReannotationExecutionRoots ++ whnfHistoryExecutionRoots).map (fun root => { + letWhnfExecutionRoots ++ headWhnfExecutionRoots ++ betaReannotationExecutionRoots ++ whnfHistoryExecutionRoots ++ + inferenceHistoryExecutionRoots).map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction ++ #[``HereditaryTyping] }) ++ (localScopeFrameRoots ++ localStateFrameRoots ++ recursiveStateFrameRoots ++ ingressFrameRoots ++ diff --git a/Ix/Kernel/Verify/Consistency/BetaHistoryState.lean b/Ix/Kernel/Verify/Consistency/BetaHistoryState.lean new file mode 100644 index 000000000..ddf260b98 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/BetaHistoryState.lean @@ -0,0 +1,118 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaHistoryInference +import Ix.Kernel.Verify.Consistency.CacheExecution + +/-! WHNF histories survive the non-reducing parts of actual inference and +verified source loading. Exposure readings can use the current annotation +type even when the retained operational trace used a different one. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +theorem LazyLookupFrame.whnf_maps {before after : TcState .anon} + (frame : LazyLookupFrame before after) (partition : WhnfCachePartition) : + partition.cache after = partition.cache before := by + cases partition + · exact frame.environment.whnfFull + · exact frame.environment.whnfNoDelta + · exact frame.environment.whnfNoDeltaCheap + · exact frame.environment.whnfCore + · exact frame.environment.whnfCoreCheap + +namespace BetaCacheHistory + +variable {β : Type u} {before after : TcState .anon} + +def afterInferKey {source : KExpr .anon} {key : Address × Address} + (history : BetaCacheHistory β before) + (accepted : TcM.inferKey source before = .ok key after) : BetaCacheHistory β after := + history.ofMaps (fun partition => by + cases partition <;> simp only [WhnfCachePartition.cache, inferKey_environment accepted]) + +/-- Verified standalone and block materialization preserve every map, +including after partial conversion and failed registration. -/ +def getConst {id : KId .anon} (history : BetaCacheHistory β before) + (loader : VerifiedLazySupport before id.addr) : + match TcM.getConst id before with + | .ok _ after | .error _ after => BetaCacheHistory β after := by + have preserved := getConst_verified_cache loader + cases run : TcM.getConst id before <;> rw [run] at preserved <;> + exact history.ofMaps preserved.whnf_maps + +def hashConversion {left right : KExpr .anon} {methods : Methods .anon} + (history : BetaCacheHistory β before) (equal : (left.addr == right.addr) = true) + (accepted : RecM.isDefEq left right methods before = .ok true after) : BetaCacheHistory β after := by + rw [isDefEq_hash_state equal] at accepted + split at accepted <;> cases accepted <;> + exact history.ofMaps (fun partition => by cases partition <;> rfl) + +/-- The inference wrapper memoizes a key and publishes only to an inference +cache. Its actual body supplies the history before that final publication. -/ +noncomputable def afterMiss {fuel : Nat} {source result : KExpr .anon} + (history : BetaCacheHistory β before) (miss : UncachedInference before source) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) + (body : ∀ middle, + RecM.inferUncached RecM.inferCall before.inferOnly source (methodsN fuel) miss.keyed = .ok result middle → + BetaCacheHistory β miss.keyed → BetaCacheHistory β middle) : BetaCacheHistory β after := by + apply Classical.choice + obtain ⟨middle, run, written⟩ := infer_uncached_success_state miss accepted + let reduced := body middle run (history.afterInferKey miss.keyRun) + refine ⟨reduced.ofMaps ?_⟩ + intro partition + rw [written] + split <;> cases partition <;> rfl + +end BetaCacheHistory + +/-- Raw annotation resources at an actual exposure input. No semantic type +or cache-origin premise is included. -/ +structure BetaHistoryReading (β : Type u) (state : TcState .anon) (source : KExpr .anon) where + resolve : Address → Option (ConstRef β) + locals : List FVarId + term : AExpr β + reading : readScopedExpr? resolve locals source = some term.erase + coherent : state.env.intern.WF + +namespace BetaHistoryReading + +variable {β : Type u} {γ : Type v} {before : TcState .anon} {source : KExpr .anon} + {resolve : Address → Option (ConstRef γ)} {locals : List FVarId} {fuel : Nat} + +def afterPublic {term target : AExpr γ} {result : KExpr .anon} + (data : BetaHistoryReading β before source) + (execution : BetaPublicExecution resolve locals fuel before source term result target) + (history : BetaCacheHistory β before) : BetaCacheHistory β execution.after := by + let rebuilt := execution.reannotate data.reading data.coherent + exact rebuilt.2.2 ▸ history.afterPublic rebuilt.2.1 data.reading data.coherent + +def afterPi {term domain body : AExpr γ} {condition : Certified.PropWhen} + {rawDomain rawBody : KExpr .anon} (data : BetaHistoryReading β before source) + (exposure : BetaPiExposure resolve locals fuel before source term condition domain body rawDomain rawBody) + (history : BetaCacheHistory β before) : BetaCacheHistory β exposure.after := by + cases exposure with + | reduce plan => + simpa only [BetaPiExposure.after, plan.execution_after] using data.afterPublic plan.execution history + | execute execution => exact data.afterPublic execution history + | cached => exact history.instrument.key source + +def afterSort {term : AExpr γ} {level : KUniv .anon} + (data : BetaHistoryReading β before source) + (exposure : BetaSortExposure resolve locals fuel before source term level) + (history : BetaCacheHistory β before) : BetaCacheHistory β exposure.after := by + cases exposure with + | direct => exact history + | reduce plan => + simpa only [BetaSortExposure.after, plan.execution_after] using data.afterPublic plan.execution history + | execute execution => exact data.afterPublic execution history + | cached => exact history.instrument.key source + +end BetaHistoryReading + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/BlockCache.lean b/Ix/Kernel/Verify/Consistency/BlockCache.lean index 3945c6867..534182cfa 100644 --- a/Ix/Kernel/Verify/Consistency/BlockCache.lean +++ b/Ix/Kernel/Verify/Consistency/BlockCache.lean @@ -46,7 +46,8 @@ private theorem insert_retains_original {before current : AnonEnv} (frame : IngressCacheExtension before current) (entry : Entry) (compatible : ∀ old, before.get? entry.1 = some old → entry.2 = old) : IngressCacheExtension before (current.insert entry.1 entry.2) := by - refine ⟨frame.full, frame.only, ?_, frame.freshIds⟩ + refine ⟨frame.full, frame.only, ?_, frame.freshIds, frame.whnfFull, frame.whnfNoDelta, + frame.whnfNoDeltaCheap, frame.whnfCore, frame.whnfCoreCheap⟩ intro id concrete loaded by_cases same : entry.1 = id · rw [← same] at loaded ⊢ @@ -74,7 +75,7 @@ theorem insertMutsEntriesState_cache {before : AnonEnv} {entries : Array Entry} apply foldl_insert_retains_original before entries.toList · intro entry member old loaded exact compatible entry (by simpa using member) old loaded - · split <;> exact ⟨rfl, rfl, fun _ _ found => found, rfl⟩ + · split <;> exact ⟨rfl, rfl, fun _ _ found => found, rfl, rfl, rfl, rfl, rfl, rfl⟩ /-- Reserved-address validation retains the exact environment on both outcomes. -/ theorem guardReserved_state (entries : Array Entry) (before : AnonEnv) : diff --git a/Ix/Kernel/Verify/Consistency/InferenceWhnfHistory.lean b/Ix/Kernel/Verify/Consistency/InferenceWhnfHistory.lean new file mode 100644 index 000000000..5eb0f211c --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/InferenceWhnfHistory.lean @@ -0,0 +1,184 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.BetaHistoryState + +/-! The existing inference execution tree preserves complete WHNF histories. +Only actual Pi/sort exposure inputs need supplementary readings; the original +synthesis tree can derive them from its returned-type readings. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +def InferenceCacheTrace.WhnfData (β : Type u) {fuel : Nat} {state : TcState .anon} {source : KExpr .anon} : + InferenceCacheTrace.{u} fuel state source → Type (u + 1) + | .hit .. | .sort .. | .fvar .. | .const .. | .lazyConst .. => PUnit + | .app _ _ _ _ first second | .forallE _ _ first second | + .lam _ _ _ first second | .lamBody _ _ _ first second => first.WhnfData β × second.WhnfData β + | .appBeta _ _ trace _ _ first second => + first.WhnfData β × BetaHistoryReading β trace.functionState trace.functionType × second.WhnfData β + | .letE _ _ _ _ first second third => first.WhnfData β × second.WhnfData β × third.WhnfData β + | .forallSort _ trace _ _ first second => + first.WhnfData β × BetaHistoryReading β trace.domainCheck.inferredState trace.domainCheck.inferred × + second.WhnfData β × BetaHistoryReading β trace.bodyCheck.inferredState trace.bodyCheck.inferred + | .lamSort _ _ trace _ first second => + first.WhnfData β × BetaHistoryReading β trace.domainCheck.inferredState trace.domainCheck.inferred × second.WhnfData β + | .letSort _ _ trace _ _ first second third => + first.WhnfData β × BetaHistoryReading β trace.domainCheck.inferredState trace.domainCheck.inferred × + second.WhnfData β × third.WhnfData β +termination_by structural tree => tree + +/-- Every successful supported inference retains the earlier WHNF producers +and appends its actual exposure publications. Recursive children, lazy loads, +substitution, inference-cache writes, and scope cleanup are composed along +their real intermediate states. -/ +noncomputable def InferenceCacheTrace.whnfHistory {β : Type u} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} + (tree : InferenceCacheTrace.{u} fuel before source) : + tree.WhnfData β → BetaCacheHistory β before → + RecM.infer source (methodsN fuel) before = .ok result after → BetaCacheHistory β after := + match tree with + | .hit cached => fun _ history accepted => by + rw [cached.run] at accepted + cases accepted + exact history.afterInferKey cached.keyRun + | .sort (level := level) miss => fun _ history accepted => + history.afterMiss miss accepted fun middle run keyed => by + change EStateM.Result.ok + (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).1 + {miss.keyed with env := {miss.keyed.env with intern := + (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).2}} = .ok result middle at run + cases run + exact keyed.intern _ + | .fvar miss => fun _ history accepted => + history.afterMiss miss accepted fun middle run keyed => by + change (RecM.inferUncached RecM.inferCall before.inferOnly _).run (methodsN fuel) miss.keyed = _ at run + unfold RecM.inferUncached at run + simp only [ReaderT.run_bind] at run + change EStateM.bind (get : TcM .anon (TcState .anon)) _ miss.keyed = _ at run + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) miss.keyed = .ok miss.keyed miss.keyed from rfl] at run + dsimp only at run + split at run + · cases run + exact keyed + · contradiction + | .const miss concrete loaded resources => fun _ history accepted => + history.afterMiss miss accepted fun middle run keyed => by + apply Classical.choice + obtain ⟨actual, foundState, got, _, instantiated⟩ := inferUncached_const_instantiation run + rw [getConst_loaded loaded] at got + cases got + have post := TcM.instantiateUnivParams_wf resources.faithful + (fun _ h => Or.inr h) ⟨resources.coherent, fun _ h => Or.inl h⟩ + rw [instantiated] at post + rw [post.2.2.1] + exact ⟨keyed.intern _⟩ + | .lazyConst miss loader resources => fun _ history accepted => + history.afterMiss miss accepted fun middle run keyed => by + apply Classical.choice + obtain ⟨concrete, foundState, got, _, instantiated⟩ := inferUncached_const_instantiation run + have loaded := keyed.getConst loader + rw [got] at loaded + have resource := resources concrete foundState got + have post := TcM.instantiateUnivParams_wf resource.faithful + (fun _ h => Or.inr h) ⟨resource.coherent, fun _ h => Or.inl h⟩ + rw [instantiated] at post + rw [post.2.2.1] + exact ⟨loaded.intern _⟩ + | .app full miss trace hashPath first second => fun data history accepted => + history.afterMiss miss accepted fun middle run keyed => by + rw [full] at run + let functionHistory := first.whnfHistory data.1 keyed trace.functionRun + let argumentHistory := second.whnfHistory data.2 functionHistory trace.argumentRun + let compared := argumentHistory.hashConversion hashPath trace.compareRun + rw [(trace.output_state run).2] + exact compared.ofMaps (fun partition => by cases partition <;> rfl) + | .appBeta full miss trace exposure hashPath first second => fun data history accepted => + history.afterMiss miss accepted fun middle run keyed => by + rw [full] at run + let functionHistory := first.whnfHistory data.1 keyed trace.functionRun + have exposed : BetaCacheHistory β trace.exposedState := by + rw [trace.exposure_state exposure] + exact data.2.1.afterPi exposure functionHistory + let argumentHistory := second.whnfHistory data.2.2 exposed trace.argumentRun + let compared := argumentHistory.hashConversion hashPath trace.compareRun + rw [(trace.output_state run).2] + exact compared.ofMaps (fun partition => by cases partition <;> rfl) + | .forallE miss trace first second => fun data history accepted => + history.afterMiss miss accepted fun middle run keyed => by + let domainHistory := first.whnfHistory data.1 keyed trace.domainRun + let opened := domainHistory.openBinder trace.openRun + let bodyHistory := second.whnfHistory data.2 opened trace.bodyRun + rw [(trace.output_state run).2] + exact bodyHistory.ofMaps (fun partition => by cases partition <;> rfl) + | .lam full miss trace first second => fun data history accepted => + history.afterMiss miss accepted fun middle run keyed => by + rw [full] at run + let domainHistory := first.whnfHistory data.1 keyed trace.domainRun + let opened := domainHistory.openBinder trace.openRun + let bodyHistory := second.whnfHistory data.2 opened trace.bodyRun + rw [(trace.output_state run).2] + exact bodyHistory.ofMaps (fun partition => by cases partition <;> rfl) + | .lamBody full miss trace first second => fun data history accepted => + history.afterMiss miss accepted fun middle run keyed => by + rw [full] at run + let domainHistory := first.whnfHistory data.1 keyed trace.domainRun + let opened := domainHistory.openBinder trace.openRun + let bodyHistory := second.whnfHistory data.2 opened trace.bodyRun + rw [(trace.output_state run).2] + exact bodyHistory.ofMaps (fun partition => by cases partition <;> rfl) + | .letE full miss trace hashPath first second third => fun data history accepted => + history.afterMiss miss accepted fun middle run keyed => by + rw [full] at run + let domainHistory := first.whnfHistory data.1 keyed trace.domainRun + let valueHistory := second.whnfHistory data.2.1 domainHistory trace.valueRun + let compared := valueHistory.hashConversion hashPath trace.compareRun + let opened := compared.openLet trace.openRun + let bodyHistory := third.whnfHistory data.2.2 opened trace.bodyRun + rw [(trace.output_state run).2] + exact bodyHistory.ofMaps (fun partition => by cases partition <;> rfl) + | .forallSort miss trace domainExposure bodyExposure first second => fun data history accepted => + history.afterMiss miss accepted fun middle run keyed => by + let domainHistory := first.whnfHistory data.1 keyed trace.domainCheck.inferRun + have domainExposed : BetaCacheHistory β trace.domainCheck.after := by + rw [trace.domainCheck.exposure_state domainExposure] + exact data.2.1.afterSort domainExposure domainHistory + let opened := domainExposed.openBinder trace.openRun + let bodyHistory := second.whnfHistory data.2.2.1 opened trace.bodyCheck.inferRun + have bodyExposed : BetaCacheHistory β trace.bodyCheck.after := by + rw [trace.bodyCheck.exposure_state bodyExposure] + exact data.2.2.2.afterSort bodyExposure bodyHistory + rw [(trace.output_state run).2] + exact bodyExposed.ofMaps (fun partition => by cases partition <;> rfl) + | .lamSort full miss trace domainExposure first second => fun data history accepted => + history.afterMiss miss accepted fun middle run keyed => by + rw [full] at run + let domainHistory := first.whnfHistory data.1 keyed trace.domainCheck.inferRun + have domainExposed : BetaCacheHistory β trace.domainCheck.after := by + rw [trace.domainCheck.exposure_state domainExposure] + exact data.2.1.afterSort domainExposure domainHistory + let opened := domainExposed.openBinder trace.openRun + let bodyHistory := second.whnfHistory data.2.2 opened trace.bodyRun + rw [(trace.output_state run).2] + exact bodyHistory.ofMaps (fun partition => by cases partition <;> rfl) + | .letSort full miss trace domainExposure hashPath first second third => fun data history accepted => + history.afterMiss miss accepted fun middle run keyed => by + rw [full] at run + let domainHistory := first.whnfHistory data.1 keyed trace.domainCheck.inferRun + have domainExposed : BetaCacheHistory β trace.domainCheck.after := by + rw [trace.domainCheck.exposure_state domainExposure] + exact data.2.1.afterSort domainExposure domainHistory + let valueHistory := second.whnfHistory data.2.2.1 domainExposed trace.valueRun + let compared := valueHistory.hashConversion hashPath trace.compareRun + let opened := compared.openLet trace.openRun + let bodyHistory := third.whnfHistory data.2.2.2 opened trace.bodyRun + rw [(trace.output_state run).2] + exact bodyHistory.ofMaps (fun partition => by cases partition <;> rfl) +termination_by structural tree + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/LazyCache.lean b/Ix/Kernel/Verify/Consistency/LazyCache.lean index abc349577..4f92e3347 100644 --- a/Ix/Kernel/Verify/Consistency/LazyCache.lean +++ b/Ix/Kernel/Verify/Consistency/LazyCache.lean @@ -7,10 +7,10 @@ import Ix.Kernel.Verify.Consistency.ConstantCache import Ix.Kernel.Ingress /-! -# Inference caches across verified standalone lazy loading +# Reduction caches across verified standalone lazy loading Conversion accesses only intern tables. Registration inserts one fresh -declaration, so existing declarations and inference caches survive both +declaration, so existing declarations and all reduction caches survive both successful loading and errors with partial conversion progress. -/ @@ -42,26 +42,34 @@ structure IngressCacheExtension (before after : AnonEnv) : Prop where only : after.inferOnlyCache = before.inferOnlyCache constants : ∀ id concrete, before.get? id = some concrete → after.get? id = some concrete freshIds : after.nextFVarId = before.nextFVarId + whnfFull : after.whnfCache = before.whnfCache + whnfNoDelta : after.whnfNoDeltaCache = before.whnfNoDeltaCache + whnfNoDeltaCheap : after.whnfNoDeltaCheapCache = before.whnfNoDeltaCheapCache + whnfCore : after.whnfCoreCache = before.whnfCoreCache + whnfCoreCheap : after.whnfCoreCheapCache = before.whnfCoreCheapCache theorem IngressCacheExtension.refl (env : AnonEnv) : IngressCacheExtension env env := - ⟨rfl, rfl, fun _ _ found => found, rfl⟩ + ⟨rfl, rfl, fun _ _ found => found, rfl, rfl, rfl, rfl, rfl, rfl⟩ theorem IngressCacheExtension.intern (env : AnonEnv) (it : InternTable .anon) : IngressCacheExtension env {env with intern := it} := - ⟨rfl, rfl, fun _ _ found => found, rfl⟩ + ⟨rfl, rfl, fun _ _ found => found, rfl, rfl, rfl, rfl, rfl, rfl⟩ theorem IngressCacheExtension.trans {before middle after : AnonEnv} (first : IngressCacheExtension before middle) (second : IngressCacheExtension middle after) : IngressCacheExtension before after := ⟨second.full.trans first.full, second.only.trans first.only, fun id concrete found => second.constants id concrete (first.constants id concrete found), - second.freshIds.trans first.freshIds⟩ + second.freshIds.trans first.freshIds, + second.whnfFull.trans first.whnfFull, second.whnfNoDelta.trans first.whnfNoDelta, + second.whnfNoDeltaCheap.trans first.whnfNoDeltaCheap, second.whnfCore.trans first.whnfCore, + second.whnfCoreCheap.trans first.whnfCoreCheap⟩ /-- A fresh single-entry registration retains every old declaration. -/ theorem IngressCacheExtension.insert {env : AnonEnv} {id : KId .anon} (fresh : env.get? id = none) (concrete : KConst .anon) : IngressCacheExtension env ((env.insert id concrete).insertBlock id #[id]) := by - refine ⟨rfl, rfl, ?_, rfl⟩ + refine ⟨rfl, rfl, ?_, rfl, rfl, rfl, rfl, rfl, rfl⟩ intro old value loaded have different : id ≠ old := by intro equal @@ -88,7 +96,7 @@ theorem insertStandaloneEntries_singleton (id : KId .anon) (concrete : KConst .a simp [insertStandaloneEntries, guardReserved, checkReserved, IngressM.liftExcept] cases reservedMarkerName id.addr <;> rfl -/-- Fresh standalone ingress preserves old declarations and both caches, +/-- Fresh standalone ingress preserves old declarations and all seven caches, including conversion errors and rejection by the reserved-address guard. -/ theorem ingressAnonStandalone_cache (source : Ixon.Env) (addr : Address) (constant : Ixon.Constant) (before : AnonEnv) diff --git a/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean b/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean index 9631a19dd..092ba6194 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean @@ -4,6 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.Consistency.SynthesisCacheHistory +import Ix.Kernel.Verify.Consistency.SynthesisCoherence /-! The original synthesis recursion supplies the annotations of actual cache publications. Supplementary data concerns only operational resources and child @@ -21,6 +22,8 @@ structure SynthesisCacheRun {β : Type u} (resolve : Address → Option (ConstRe (accepted : RecM.infer source (methodsN fuel) before = .ok result after) where trace : InferenceCacheTrace.{u} fuel before source checks : SynthesisEventChecks resolve anchor entries (trace.events accepted) + whnf : before.env.intern.WF → trace.WhnfData β + coherent : before.env.intern.WF → after.env.intern.WF /-- The older checking-only interface can omit executed domain checks and loader resources. This supplement records its actual operational tree and @@ -30,14 +33,16 @@ structure SynthesisCacheSupplement {β : Type u} (resolve : Address → Option ( (anchor entries : Model.Environment β) (fuel : Nat) (state : TcState .anon) (source : KExpr .anon) where trace : InferenceCacheTrace.{u} fuel state source children : SynthesisEventChecks resolve anchor entries trace.childEvents + whnf : state.env.intern.WF → trace.WhnfData β namespace SynthesisCacheSupplement variable {β : Type u} {resolve : Address → Option (ConstRef β)} {anchor entries : Model.Environment β} {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} -def ofLeaf (trace : InferenceCacheTrace.{u} fuel before source) (leaf : trace.childEvents = []) : - SynthesisCacheSupplement resolve anchor entries fuel before source := ⟨trace, leaf.symm ▸ .nil⟩ +def ofLeaf (trace : InferenceCacheTrace.{u} fuel before source) (leaf : trace.childEvents = []) + (whnf : before.env.intern.WF → trace.WhnfData β) : + SynthesisCacheSupplement resolve anchor entries fuel before source := ⟨trace, leaf.symm ▸ .nil, whnf⟩ /-- Primitive cache observations require no supplementary checking trees. -/ def sortOfKey {keyed : TcState .anon} {level : KUniv .anon} {info : ExprInfo .anon} @@ -45,14 +50,20 @@ def sortOfKey {keyed : TcState .anon} {level : KUniv .anon} {info : ExprInfo .an SynthesisCacheSupplement resolve anchor entries fuel before (.sort level info) := ofLeaf (.sortOfKey run) (by unfold InferenceCacheTrace.sortOfKey - rcases observeInferenceCache run with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ <;> rfl) + rcases observeInferenceCache run with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ <;> rfl) (by + intro _ + unfold InferenceCacheTrace.sortOfKey + rcases observeInferenceCache run with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ <;> exact PUnit.unit) def fvarOfKey {keyed : TcState .anon} {id : FVarId} {name : Mode.anon.F Name} {info : ExprInfo .anon} {key : Address × Address} (run : TcM.inferKey (.fvar id name info) before = .ok key keyed) : SynthesisCacheSupplement resolve anchor entries fuel before (.fvar id name info) := ofLeaf (.fvarOfKey run) (by unfold InferenceCacheTrace.fvarOfKey - rcases observeInferenceCache run with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ <;> rfl) + rcases observeInferenceCache run with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ <;> rfl) (by + intro _ + unfold InferenceCacheTrace.fvarOfKey + rcases observeInferenceCache run with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ <;> exact PUnit.unit) def verifiedConstOfKey {keyed : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} {key : Address × Address} @@ -63,7 +74,10 @@ def verifiedConstOfKey {keyed : TcState .anon} {id : KId .anon} {arguments : Arr SynthesisCacheSupplement resolve anchor entries fuel before (.const id arguments info) := ofLeaf (.verifiedConstOfKey run loader resources) (by unfold InferenceCacheTrace.verifiedConstOfKey - rcases observeInferenceCache run with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ <;> rfl) + rcases observeInferenceCache run with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ <;> rfl) (by + intro _ + unfold InferenceCacheTrace.verifiedConstOfKey + rcases observeInferenceCache run with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ <;> exact PUnit.unit) def complete (data : SynthesisCacheSupplement resolve anchor entries fuel before source) {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} @@ -75,8 +89,10 @@ def complete (data : SynthesisCacheSupplement resolve anchor entries fuel before (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : SynthesisCacheRun resolve anchor entries accepted := { trace := data.trace + whnf := data.whnf + coherent := fun initial => tree.outputCoherent data.trace initial agreement reading accepted checks := by - rcases data with ⟨trace, children⟩ + rcases data with ⟨trace, children, whnf⟩ cases trace with | hit => exact .nil | sort miss | fvar miss | const miss concrete loaded resources | lazyConst miss loader resources => @@ -163,7 +179,11 @@ def SynthesisInference.cacheExecution {β : Type u} {resolve : Address → Optio contextOrigin agreement reading accepted | .fvar inference atIndex boundAtIndex => fun data contextOrigin agreement reading accepted => data.complete (.fvar inference atIndex boundAtIndex) contextOrigin agreement reading accepted - | .cached _ _ _ _ hit _ _ | .cachedFrom _ hit _ => fun _ _ _ _ _ => ⟨.hit hit, .nil⟩ + | .cached _ _ _ _ hit _ _ | .cachedFrom _ hit _ => fun _ _ _ _ accepted => + ⟨.hit hit, .nil, fun _ => PUnit.unit, fun initial => by + rw [hit.run] at accepted + cases accepted + simpa only [inferKey_environment hit.keyRun] using initial⟩ | .app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful => fun data contextOrigin agreement reading accepted => by @@ -173,7 +193,9 @@ def SynthesisInference.cacheExecution {β : Type u} {resolve : Address → Optio let argumentRun := argumentTree.cacheExecution data.2 contextOrigin (keyedAgreement.congr trace.contextPreserved.symm) argumentReads trace.argumentRun exact (SynthesisCacheSupplement.mk (.app full miss trace hashPath functionRun.trace argumentRun.trace) - (functionRun.checks.append argumentRun.checks)).complete + (functionRun.checks.append argumentRun.checks) (fun initial => + let keyed := miss.keyedCoherent initial + (functionRun.whnf keyed, argumentRun.whnf (functionRun.coherent keyed)))).complete (.app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful) contextOrigin agreement reading accepted @@ -185,8 +207,15 @@ def SynthesisInference.cacheExecution {β : Type u} {resolve : Address → Optio let functionRun := functionTree.cacheExecution data.1 contextOrigin keyedAgreement functionReads trace.functionRun let argumentRun := argumentTree.cacheExecution data.2 contextOrigin (keyedAgreement.congr trace.exposure_context.symm) argumentReads trace.argumentRun + have functionReading := functionTree.outputReading keyedAgreement functionReads trace.functionRun exact (SynthesisCacheSupplement.mk (.appBeta full miss trace exposure hashPath functionRun.trace argumentRun.trace) - (functionRun.checks.append argumentRun.checks)).complete + (functionRun.checks.append argumentRun.checks) (fun initial => + let keyed := miss.keyedCoherent initial + let functionCoherent := functionRun.coherent keyed + (functionRun.whnf keyed, ⟨_, _, _, functionReading, functionCoherent⟩, + argumentRun.whnf (by + rw [trace.exposure_state exposure] + exact (exposure.reading functionReading functionCoherent).2.2)))).complete (.appBeta full miss trace functionTree exposure exposureCoherent reduction argumentTree conditions hashPath comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful) contextOrigin agreement reading accepted @@ -195,12 +224,13 @@ def SynthesisInference.cacheExecution {β : Type u} {resolve : Address → Optio obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_all_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement let domainRun := domainTree.cacheExecution data.1 contextOrigin keyedAgreement domainReads trace.domainRun - obtain ⟨_, openedReads, openedAgreement, _⟩ := openBinder_sound opening + obtain ⟨_, openedReads, openedAgreement, openedCoherent⟩ := openBinder_sound opening (keyedAgreement.congr trace.contextPreserved.symm) (trace.absent keyedAgreement) domainReads bodyReads trace.openRun let bodyRun := bodyTree.cacheExecution data.2 (.push contextOrigin domainTree keyedAgreement domainReads trace.domainRun) openedAgreement openedReads trace.bodyRun exact (SynthesisCacheSupplement.mk (.forallE miss trace domainRun.trace bodyRun.trace) - (domainRun.checks.append bodyRun.checks)).complete + (domainRun.checks.append bodyRun.checks) (fun initial => + (domainRun.whnf (miss.keyedCoherent initial), bodyRun.whnf openedCoherent))).complete (.forallE miss trace opening domainTree bodyTree levelFaithful domainBound bodyBound coherent faithful) contextOrigin agreement reading accepted | .lam full miss trace opening domainTree bodyTree conditionAgrees constructed bound coherent @@ -209,12 +239,13 @@ def SynthesisInference.cacheExecution {β : Type u} {resolve : Address → Optio obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement let domainRun := domainTree.cacheExecution data.1 contextOrigin keyedAgreement domainReads trace.domainRun - obtain ⟨_, openedReads, openedAgreement, _⟩ := openBinder_sound opening + obtain ⟨_, openedReads, openedAgreement, openedCoherent⟩ := openBinder_sound opening (keyedAgreement.congr trace.contextPreserved.symm) (trace.absent keyedAgreement) domainReads bodyReads trace.openRun let bodyRun := bodyTree.cacheExecution data.2 (.push contextOrigin domainTree keyedAgreement domainReads trace.domainRun) openedAgreement openedReads trace.bodyRun exact (SynthesisCacheSupplement.mk (.lam full miss trace domainRun.trace bodyRun.trace) - (domainRun.checks.append bodyRun.checks)).complete + (domainRun.checks.append bodyRun.checks) (fun initial => + (domainRun.whnf (miss.keyedCoherent initial), bodyRun.whnf openedCoherent))).complete (.lam full miss trace opening domainTree bodyTree conditionAgrees constructed bound coherent closingFaithful faithful) contextOrigin agreement reading accepted | .lamBeta full miss trace opening domainTree bodyTree origin reduction conditionAgrees @@ -223,12 +254,13 @@ def SynthesisInference.cacheExecution {β : Type u} {resolve : Address → Optio obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement let domainRun := domainTree.cacheExecution data.1 contextOrigin keyedAgreement domainReads trace.domainRun - obtain ⟨_, openedReads, openedAgreement, _⟩ := openBinder_sound opening + obtain ⟨_, openedReads, openedAgreement, openedCoherent⟩ := openBinder_sound opening (keyedAgreement.congr trace.contextPreserved.symm) (trace.absent keyedAgreement) domainReads bodyReads trace.openRun let bodyRun := bodyTree.cacheExecution data.2 (.push contextOrigin domainTree keyedAgreement domainReads trace.domainRun) openedAgreement openedReads trace.bodyRun exact (SynthesisCacheSupplement.mk (.lamBody full miss trace domainRun.trace bodyRun.trace) - (domainRun.checks.append bodyRun.checks)).complete + (domainRun.checks.append bodyRun.checks) (fun initial => + (domainRun.whnf (miss.keyedCoherent initial), bodyRun.whnf openedCoherent))).complete (.lamBeta full miss trace opening domainTree bodyTree origin reduction conditionAgrees constructed bound closingFaithful faithful) contextOrigin agreement reading accepted | .letE full localState miss trace opening domainTree valueTree bodyTree domainReading valueReading bodyReading @@ -239,14 +271,16 @@ def SynthesisInference.cacheExecution {β : Type u} {resolve : Address → Optio let domainRun := domainTree.cacheExecution data.1 contextOrigin keyedAgreement domainReading trace.domainRun let valueRun := valueTree.cacheExecution data.2.1 contextOrigin (keyedAgreement.congr (trace.domainContext keyedValid).symm) valueReading trace.valueRun - obtain ⟨openedReading, openedAgreement, _⟩ := + obtain ⟨openedReading, openedAgreement, openedCoherent⟩ := trace.opened_reading opening keyedValid keyedAgreement domainReading bodyReading let bodyRun := bodyTree.cacheExecution data.2.2 (.push contextOrigin domainTree keyedAgreement domainReading trace.domainRun) openedAgreement openedReading trace.bodyRun exact (SynthesisCacheSupplement.mk (.letE full miss trace hashPath domainRun.trace valueRun.trace bodyRun.trace) - ((domainRun.checks.append valueRun.checks).append bodyRun.checks)).complete + ((domainRun.checks.append valueRun.checks).append bodyRun.checks) (fun initial => + let keyed := miss.keyedCoherent initial + (domainRun.whnf keyed, valueRun.whnf (domainRun.coherent keyed), bodyRun.whnf openedCoherent))).complete (.letE full localState miss trace opening domainTree valueTree bodyTree domainReading valueReading bodyReading conditions hashPath comparisonFaithful substitution reduction) contextOrigin agreement reading accepted | .forallSort miss trace opening domainCheck bodyCheck levelFaithful domainBound bodyBound coherent faithful => @@ -254,13 +288,17 @@ def SynthesisInference.cacheExecution {β : Type u} {resolve : Address → Optio obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_all_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement let domainRun := domainCheck.cacheExecution data.1 contextOrigin keyedAgreement domainReading - obtain ⟨openedReading, openedAgreement, _⟩ := + obtain ⟨openedReading, openedAgreement, openedCoherent⟩ := trace.opened_reading opening keyedAgreement domainReading bodyReading let bodyRun := bodyCheck.cacheExecution data.2 (.pushSort contextOrigin domainCheck keyedAgreement domainReading) openedAgreement openedReading exact (SynthesisCacheSupplement.mk (.forallSort miss trace domainCheck.exposure bodyCheck.exposure domainRun.trace bodyRun.trace) - (domainRun.checks.append bodyRun.checks)).complete + (domainRun.checks.append bodyRun.checks) (fun initial => + let keyed := miss.keyedCoherent initial + (domainRun.whnf keyed, domainCheck.historyReading keyedAgreement domainReading (domainRun.coherent keyed), + bodyRun.whnf openedCoherent, + bodyCheck.historyReading openedAgreement openedReading (bodyRun.coherent openedCoherent)))).complete (.forallSort miss trace opening domainCheck bodyCheck levelFaithful domainBound bodyBound coherent faithful) contextOrigin agreement reading accepted | .lamSort full miss trace opening domainCheck bodyTree reduction conditionAgrees @@ -268,14 +306,17 @@ def SynthesisInference.cacheExecution {β : Type u} {resolve : Address → Optio obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_lam_parts reading have keyedAgreement := miss.localContext.symm ▸ agreement let domainRun := domainCheck.cacheExecution data.1 contextOrigin keyedAgreement domainReading - obtain ⟨openedReading, openedAgreement, _⟩ := + obtain ⟨openedReading, openedAgreement, openedCoherent⟩ := trace.opened_reading opening keyedAgreement domainReading bodyReading let bodyRun := bodyTree.cacheExecution data.2 (.pushSort contextOrigin domainCheck keyedAgreement domainReading) openedAgreement openedReading trace.bodyRun exact (SynthesisCacheSupplement.mk (.lamSort full miss trace domainCheck.exposure domainRun.trace bodyRun.trace) - (domainRun.checks.append bodyRun.checks)).complete + (domainRun.checks.append bodyRun.checks) (fun initial => + let keyed := miss.keyedCoherent initial + (domainRun.whnf keyed, domainCheck.historyReading keyedAgreement domainReading (domainRun.coherent keyed), + bodyRun.whnf openedCoherent))).complete (.lamSort full miss trace opening domainCheck bodyTree reduction conditionAgrees constructed bound coherent closingFaithful faithful) contextOrigin agreement reading accepted | .letSort full localState miss trace opening domainCheck valueTree bodyTree domainReading valueReading bodyReading @@ -286,14 +327,18 @@ def SynthesisInference.cacheExecution {β : Type u} {resolve : Address → Optio let domainRun := domainCheck.cacheExecution data.1 contextOrigin keyedAgreement domainReading let valueRun := valueTree.cacheExecution data.2.1 contextOrigin (keyedAgreement.congr (trace.domainContext keyedValid).symm) valueReading trace.valueRun - obtain ⟨openedReading, openedAgreement, _⟩ := + obtain ⟨openedReading, openedAgreement, openedCoherent⟩ := trace.opened_reading opening keyedValid keyedAgreement domainReading bodyReading let bodyRun := bodyTree.cacheExecution data.2.2 (.pushSort contextOrigin domainCheck keyedAgreement domainReading) openedAgreement openedReading trace.bodyRun exact (SynthesisCacheSupplement.mk (.letSort full miss trace domainCheck.exposure hashPath domainRun.trace valueRun.trace bodyRun.trace) - ((domainRun.checks.append valueRun.checks).append bodyRun.checks)).complete + ((domainRun.checks.append valueRun.checks).append bodyRun.checks) (fun initial => + let keyed := miss.keyedCoherent initial + (domainRun.whnf keyed, domainCheck.historyReading keyedAgreement domainReading (domainRun.coherent keyed), + valueRun.whnf (domainCheck.afterCoherent keyedAgreement domainReading (domainRun.coherent keyed)), + bodyRun.whnf openedCoherent))).complete (.letSort full localState miss trace opening domainCheck valueTree bodyTree domainReading valueReading bodyReading conditions hashPath comparisonFaithful substitution reduction) contextOrigin agreement reading accepted termination_by structural tree diff --git a/Ix/Kernel/Verify/Consistency/SynthesisCoherence.lean b/Ix/Kernel/Verify/Consistency/SynthesisCoherence.lean new file mode 100644 index 000000000..af33fb356 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/SynthesisCoherence.lean @@ -0,0 +1,259 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.InferenceWhnfHistory + +/-! Original checking resources preserve intern coherence at the returned +state. The operational cache tree supplies loader and universe-walk data at +primitive constants; no semantic typing invariant is assumed. -/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u + +theorem UncachedInference.keyedCoherent {before : TcState .anon} {source : KExpr .anon} + (miss : UncachedInference before source) (initial : before.env.intern.WF) : miss.keyed.env.intern.WF := by + simpa only [inferKey_environment miss.keyRun] using initial + +theorem infer_miss_coherent {fuel : Nat} {before after : TcState .anon} {source result : KExpr .anon} + (miss : UncachedInference before source) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) + (body : ∀ middle, + RecM.inferUncached RecM.inferCall before.inferOnly source (methodsN fuel) miss.keyed = .ok result middle → + middle.env.intern.WF) : after.env.intern.WF := by + obtain ⟨middle, run, written⟩ := infer_uncached_success_state miss accepted + have coherent := body middle run + rw [written] + split <;> exact coherent + +theorem InferenceCacheTrace.const_coherent {fuel : Nat} {before after : TcState .anon} + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} {result : KExpr .anon} + (tree : InferenceCacheTrace.{u} fuel before (.const id arguments info)) (coherent : before.env.intern.WF) + (accepted : RecM.infer (.const id arguments info) (methodsN fuel) before = .ok result after) : + after.env.intern.WF := by + cases tree with + | hit cached => + rw [cached.run] at accepted + cases accepted + simpa only [inferKey_environment cached.keyRun] using coherent + | const miss concrete loaded resources => + apply infer_miss_coherent miss accepted + intro middle run + obtain ⟨actual, foundState, got, _, instantiated⟩ := inferUncached_const_instantiation run + rw [getConst_loaded loaded] at got + cases got + have post := TcM.instantiateUnivParams_wf resources.faithful + (fun _ h => Or.inr h) ⟨resources.coherent, fun _ h => Or.inl h⟩ + rw [instantiated] at post + exact post.1.1 + | lazyConst miss loader resources => + apply infer_miss_coherent miss accepted + intro middle run + obtain ⟨concrete, foundState, got, _, instantiated⟩ := inferUncached_const_instantiation run + have resource := resources concrete foundState got + have post := TcM.instantiateUnivParams_wf resource.faithful + (fun _ h => Or.inr h) ⟨resource.coherent, fun _ h => Or.inl h⟩ + rw [instantiated] at post + exact post.1.1 + +theorem InferenceCacheTrace.fvar_coherent {fuel : Nat} {before after : TcState .anon} + {id : FVarId} {name : Mode.anon.F Name} {info : ExprInfo .anon} {result : KExpr .anon} + (tree : InferenceCacheTrace.{u} fuel before (.fvar id name info)) (coherent : before.env.intern.WF) + (accepted : RecM.infer (.fvar id name info) (methodsN fuel) before = .ok result after) : + after.env.intern.WF := by + cases tree with + | hit cached => + rw [cached.run] at accepted + cases accepted + simpa only [inferKey_environment cached.keyRun] using coherent + | fvar miss => + apply infer_miss_coherent miss accepted + intro middle run + change (RecM.inferUncached RecM.inferCall before.inferOnly (.fvar id name info)).run (methodsN fuel) miss.keyed = _ at run + unfold RecM.inferUncached at run + simp only [ReaderT.run_bind] at run + change EStateM.bind (get : TcM .anon (TcState .anon)) _ miss.keyed = _ at run + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) miss.keyed = .ok miss.keyed miss.keyed from rfl] at run + dsimp only at run + split at run + · cases run + simpa only [inferKey_environment miss.keyRun] using coherent + · contradiction + +theorem BinderInference.outputCoherent {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} {term type : AExpr β} + (support : BinderInference resolve entries locals context fuel before source term type) + (tree : InferenceCacheTrace.{u} fuel before source) (initial : before.env.intern.WF) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : after.env.intern.WF := by + cases support with + | @sort locals context fuel before level info miss coherent faithful => + apply infer_miss_coherent miss accepted + intro middle run + change EStateM.Result.ok (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).1 + {miss.keyed with env := {miss.keyed.env with intern := + (miss.keyed.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).2}} = .ok result middle at run + cases run + exact coherent.internExpr _ + | cachedSort cached canonical => + rw [cached.run] at accepted + cases accepted + simpa only [inferKey_environment cached.keyRun] using initial + | fvar => exact tree.fvar_coherent initial accepted + | const | polymorphic | cachedConst => exact tree.const_coherent initial accepted + | app full miss trace functionTree head argumentTree conditions hashPath comparisonFaithful + bodyConstructed argConstructed bodyBound argBound coherent faithful => + apply infer_miss_coherent miss accepted + intro middle run + rw [full] at run + rw [(trace.output_state run).2] + exact (subst_spec (depth := 0) faithful bodyConstructed argConstructed + (by simpa using (show trace.codomain.size < UInt64.size by omega)) argBound + (fun _ h => Or.inr h) coherent (fun _ h => Or.inl h)).2.1 + | forallE miss trace opening domainTree bodyTree levelFaithful domainBound bodyBound coherent faithful => + apply infer_miss_coherent miss accepted + intro middle run + rw [(trace.output_state run).2] + exact coherent.internExpr _ + | lam full miss trace opening bodyTree constructed bound coherent closingFaithful faithful => + apply infer_miss_coherent miss accepted + intro middle run + rw [full] at run + have closed := (abstractFVars_singleton_spec constructed (by omega) closingFaithful coherent + (fun _ h => Or.inl h) (fun _ h => Or.inr h)).2 + rw [(trace.output_state run).2] + exact closed.internExpr _ + +/-- Coherence follows the actual final construction of each checking node. +In particular, returning a cached type uses the current table's coherence. -/ +theorem SynthesisInference.outputCoherent {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {fuel : Nat} + {before after : TcState .anon} {source result : KExpr .anon} + {term type : AExpr β} {level : VLevel} + (support : SynthesisInference resolve entries locals context bounds fuel before source term type level) + (tree : InferenceCacheTrace.{u} fuel before source) (initial : before.env.intern.WF) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : after.env.intern.WF := + match support with + | .known inference _ | .reuseType inference .. | .fvar inference .. => + inference.outputCoherent tree initial accepted + | .cached _ _ _ _ observed _ _ | .cachedFrom _ observed _ => by + rw [observed.run] at accepted + cases accepted + simpa only [inferKey_environment observed.keyRun] using initial + | .app full miss trace _ _ _ _ _ bodyConstructed argConstructed bodyBound argBound coherent faithful | + .appBeta full miss trace _ _ _ _ _ _ _ _ bodyConstructed argConstructed bodyBound argBound coherent faithful => by + apply infer_miss_coherent miss accepted + intro middle run + rw [full] at run + rw [(trace.output_state run).2] + exact (subst_spec (depth := 0) faithful bodyConstructed argConstructed + (by simpa using (show trace.codomain.size < UInt64.size by omega)) argBound + (fun _ h => Or.inr h) coherent (fun _ h => Or.inl h)).2.1 + | .forallE miss trace _ _ _ _ _ _ coherent _ | + .forallSort miss trace _ _ _ _ _ _ coherent _ => by + apply infer_miss_coherent miss accepted + intro middle run + rw [(trace.output_state run).2] + exact coherent.internExpr _ + | .lam full miss trace _ _ _ _ constructed bound coherent closingFaithful _ => by + apply infer_miss_coherent miss accepted + intro middle run + rw [full] at run + have closed := (abstractFVars_singleton_spec constructed (by omega) closingFaithful coherent + (fun _ h => Or.inl h) (fun _ h => Or.inr h)).2 + rw [(trace.output_state run).2] + exact closed.internExpr _ + | .lamBeta full miss trace opening _ bodyTree _ reduction _ constructed bound closingFaithful _ => by + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + have domainAgreement := keyedAgreement.congr trace.contextPreserved.symm + obtain ⟨_, openedReads, openedAgreement, _⟩ := + openBinder_sound opening domainAgreement (trace.absent keyedAgreement) domainReads bodyReads trace.openRun + have bodyTypeReads := bodyTree.outputReading openedAgreement openedReads trace.bodyRun + obtain ⟨_, reducedReads, reducedCoherent⟩ := reduction.reading bodyTypeReads + have closed := (abstractFVars_singleton_spec constructed (by omega) closingFaithful reducedCoherent + (fun _ h => Or.inl h) (fun _ h => Or.inr h)).2 + apply infer_miss_coherent miss accepted + intro middle run + rw [full] at run + rw [(trace.output_state run).2] + exact closed.internExpr _ + | .letE full localState miss trace opening _ _ bodyTree domainReading valueReading bodyReading + _ _ _ substitution reduction => by + have keyedValid := miss.keyedLocalState localState + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨openedReading, openedAgreement, _⟩ := + trace.opened_reading opening keyedValid keyedAgreement domainReading bodyReading + have bodyTypeReading := bodyTree.outputReading openedAgreement openedReading trace.bodyRun + obtain ⟨substitutedReading, substitutedCoherent⟩ := + trace.substituted_reading substitution bodyTypeReading valueReading + have reduced := (reduction.reading substitutedCoherent substitutedReading).2 + apply infer_miss_coherent miss accepted + intro middle run + rw [full] at run + rw [(trace.output_state run).2] + exact reduced + | .lamSort full miss trace opening _ bodyTree reduction _ constructed bound coherent closingFaithful _ => by + obtain ⟨domainReads, bodyReads⟩ := readScopedExpr?_lam_parts reading + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨openedReads, openedAgreement, _⟩ := trace.opened_reading opening keyedAgreement domainReads bodyReads + have bodyTypeReads := bodyTree.outputReading openedAgreement openedReads trace.bodyRun + obtain ⟨reducedReads, reducedCoherent⟩ := reduction.reading coherent bodyTypeReads + have closed := (abstractFVars_singleton_spec constructed (by omega) closingFaithful reducedCoherent + (fun _ h => Or.inl h) (fun _ h => Or.inr h)).2 + apply infer_miss_coherent miss accepted + intro middle run + rw [full] at run + rw [(trace.output_state run).2] + exact closed.internExpr _ + | .letSort full localState miss trace opening _ _ bodyTree domainReading valueReading bodyReading + _ _ _ substitution reduction => by + have keyedValid := miss.keyedLocalState localState + have keyedAgreement := miss.localContext.symm ▸ agreement + obtain ⟨openedReading, openedAgreement, _⟩ := + trace.opened_reading opening keyedValid keyedAgreement domainReading bodyReading + have bodyTypeReading := bodyTree.outputReading openedAgreement openedReading trace.bodyRun + obtain ⟨substitutedReading, substitutedCoherent⟩ := + trace.substituted_reading substitution bodyTypeReading valueReading + have reduced := (reduction.reading substitutedCoherent substitutedReading).2 + apply infer_miss_coherent miss accepted + intro middle run + rw [full] at run + rw [(trace.output_state run).2] + exact reduced + +/-- The inferred type's reading comes from the executed source check. -/ +def SynthesisSortCheck.historyReading {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} + {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} + {trace : SortInferenceTrace fuel before source} {term : AExpr β} + (check : SynthesisSortCheck resolve entries locals context bounds trace term) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : trace.inferredState.env.intern.WF) : BetaHistoryReading β trace.inferredState trace.inferred := + match check with + | .checked tree .. => ⟨resolve, locals, _, tree.outputReading agreement reading trace.inferRun, coherent⟩ + +/-- Sort exposure preserves coherence at the executed check's exit state. -/ +theorem SynthesisSortCheck.afterCoherent {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} + {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} + {trace : SortInferenceTrace fuel before source} {term : AExpr β} + (check : SynthesisSortCheck resolve entries locals context bounds trace term) + (agreement : LocalContextReading resolve locals before.lctx context) + (reading : readScopedExpr? resolve locals source = some term.erase) + (coherent : trace.inferredState.env.intern.WF) : trace.after.env.intern.WF := + match check with + | .checked tree exposure _ => by + rw [trace.exposure_state exposure] + exact exposure.coherent (tree.outputReading agreement reading trace.inferRun) coherent + +end Ix.Kernel.Consistency diff --git a/docs/theory.md b/docs/theory.md index dabcda5c0..19bc473b4 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -132,8 +132,10 @@ among the recorded inputs, and the current reading reconstructs its producer's annotations. This supplies the origin resources of public WHNF, including hits produced by zero-depth head calls. The original source derivation then proves conversion and typing while returning the history for subsequent calls. -Clearing starts a new empty history. Arbitrary inference and loader histories, -other reducers, and failures within head callbacks still require further work. +Clearing starts a new empty history. Verified loading and every supported +inference node now preserve these histories, and each returned state retains +intern coherence. Other reducers and failures within head callbacks still +require further work. Constructing initial inference resources for arbitrary accepted programs, general WHNF cache origins, the remaining reducers, and general conversion remain open. From 7d0a277ae6995e21138fddab895a75692cc1ca61 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 15 Sep 2026 09:51:02 -0400 Subject: [PATCH 50/63] Decouple consistency proofs from the named specification Relocate the two helpers that the direct consistency library reached inside `Ix.Theory.Named`: the lemmas over the shared `Ix.Theory.VLevel` move to the Ix-authored `Ix.Theory.VLevelLemmas` (the named-parameter `ofLevel` conversion stays in `Ix.Theory.Named.VLevel`), and the exact axiom traversal with `#guard_named_axioms` moves from `Ix.Theory.Named.Std.AxiomAudit` to `Ix.Kernel.Verify.Audit.AxiomAudit`. `Ix.Kernel.Verify.Level`, `Audit.Basic`, and the 32 named-tree importers are repointed; the theory import manifest, `IxTheory` globs, and the provenance test follow the moves. Two consistency proofs name `Option.bind_eq_some_iff` explicitly, which `Named.Std.Basic` had registered as a global simp lemma. `Ix.Kernel.Verify.Consistency.Audit` now also fails if any module under `Ix.Theory.Named` is in its import closure, so the separation is enforced rather than incidental. Split the CI gate: `lake build IxKernelVerify` and the `Audit.Completed`/`Conditional`/`Statements`/`SorryFrontier` manifests move from ci.yml and merge-tests.yml to the non-required, manually or weekly triggered named-spec-verification.yml; the required jobs keep `IxKernelConsistency` (--wfail), `IxCompileVerify`, check-theory, check-certified, and tc-unit. The lakefile comments and the trust-checks section of docs/kernel-verification.md describe the named track as legacy and being retired in favour of the set model. --- .github/workflows/ci.yml | 7 +- .github/workflows/merge-tests.yml | 13 ++- .github/workflows/named-spec-verification.yml | 49 ++++++++++++ .../Verify/Audit}/AxiomAudit.lean | 20 +++-- Ix/Kernel/Verify/Audit/Basic.lean | 10 +-- Ix/Kernel/Verify/Consistency/Audit.lean | 11 +++ .../Consistency/SynthesisInference.lean | 2 +- .../Verify/Consistency/SynthesisReading.lean | 5 +- Ix/Kernel/Verify/Level.lean | 9 ++- .../Fixtures/ProjectionExpressibility.lean | 2 +- Ix/Theory/Named/Inductive.lean | 2 +- Ix/Theory/Named/InductiveFixtures.lean | 2 +- Ix/Theory/Named/Literals.lean | 2 +- Ix/Theory/Named/LocalContext.lean | 2 +- Ix/Theory/Named/MutualInductiveFixtures.lean | 2 +- Ix/Theory/Named/Projection.lean | 2 +- Ix/Theory/Named/Reference/Inductive/Add.lean | 2 +- .../Reference/Inductive/EliminationTrace.lean | 2 +- .../Reference/Inductive/ValidationTrace.lean | 2 +- Ix/Theory/Named/SingletonParity.lean | 2 +- Ix/Theory/Named/Typing/Env.lean | 2 +- .../Named/Typing/InductiveCertificate.lean | 2 +- Ix/Theory/Named/Typing/InductiveLemmas.lean | 2 +- Ix/Theory/Named/Typing/InductivePattern.lean | 2 +- .../Named/Typing/InductivePatternWF.lean | 2 +- Ix/Theory/Named/VLevel.lean | 64 ++------------- Ix/Theory/Named/Verify/Environment/Basic.lean | 2 +- .../Environment/ConstructorValidation.lean | 2 +- .../Named/Verify/Environment/Elimination.lean | 2 +- .../Environment/IndexedVecCandidate.lean | 2 +- .../Environment/IndexedVecOuterReplay.lean | 2 +- .../Environment/IndexedVecSemanticReplay.lean | 2 +- .../Verify/Environment/InductiveFixtures.lean | 2 +- .../Named/Verify/Environment/Lemmas.lean | 2 +- .../Environment/MutualInductiveFixtures.lean | 2 +- .../Verify/Environment/NestedReplay.lean | 2 +- .../Verify/Environment/Normalization.lean | 2 +- .../Environment/NormalizationMatrix.lean | 2 +- .../Environment/SingletonParityMatrix.lean | 2 +- .../Environment/SingletonParityReplay.lean | 2 +- .../Named/Verify/TypeChecker/InferType.lean | 2 +- Ix/Theory/Named/Verify/Typing/Lemmas.lean | 2 +- Ix/Theory/VLevelLemmas.lean | 79 +++++++++++++++++++ Tests/Theory/ImportManifest.lean | 3 +- Tests/Theory/Provenance.lean | 3 +- docs/kernel-verification.md | 15 +++- lakefile.lean | 39 +++++---- 47 files changed, 252 insertions(+), 141 deletions(-) create mode 100644 .github/workflows/named-spec-verification.yml rename Ix/{Theory/Named/Std => Kernel/Verify/Audit}/AxiomAudit.lean (84%) create mode 100644 Ix/Theory/VLevelLemmas.lean diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db8944612..507f3481a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,8 @@ jobs: build-args: "--wfail -v" test: false # build-all checks production targets and the consistency foundation - # with --wfail. Named implementation proofs have a separate trust gate. A + # with --wfail. The legacy named-specification proofs are not part of + # this gate; named-spec-verification.yml builds them on demand. A # warning in any target (exes, benchmarks, Apps) — not just the default lib # lean-action builds above — fails CI. - name: Build all targets @@ -105,8 +106,8 @@ jobs: with: auto-config: false use-github-cache: false - - name: Check implementation proofs and exact trust manifests - run: lake build IxKernelVerify IxCompileVerify + - name: Check compiler implementation proofs + run: lake build IxCompileVerify - name: Check direct kernel consistency refinement run: lake build --wfail IxKernelConsistency - name: Check consistency model and exact foundation manifest diff --git a/.github/workflows/merge-tests.yml b/.github/workflows/merge-tests.yml index 420798b0b..9bb4d38dc 100644 --- a/.github/workflows/merge-tests.yml +++ b/.github/workflows/merge-tests.yml @@ -147,18 +147,15 @@ jobs: --suppressions=.github/valgrind.supp \ .lake/build/bin/IxTests ffi - - name: Check Ix.Kernel exported theorem trust manifest + # The legacy named-specification proofs (`IxKernelVerify` with its exact + # trust manifests and sorry frontier) are not required here; the + # named-spec-verification workflow builds them on demand. + - name: Build Ix compiler verification if: ${{ matrix.kind == 'tc' }} - run: lake build Ix.Kernel.Verify.Audit.Completed Ix.Kernel.Verify.Audit.Conditional Ix.Kernel.Verify.Audit.Statements - - name: Build Ix.Kernel formal verification - if: ${{ matrix.kind == 'tc' }} - run: lake build IxKernelVerify IxCompileVerify + run: lake build IxCompileVerify - name: Check Ix.Kernel consistency refinement if: ${{ matrix.kind == 'tc' }} run: lake build --wfail IxKernelConsistency - - name: Check Ix.Kernel verification sorry frontier - if: ${{ matrix.kind == 'tc' }} - run: lake build Ix.Kernel.Verify.Audit.SorryFrontier - name: Test Ix.Kernel unit and adversarial fixtures if: ${{ matrix.kind == 'tc' }} run: lake test --wfail -- tc-unit diff --git a/.github/workflows/named-spec-verification.yml b/.github/workflows/named-spec-verification.yml new file mode 100644 index 000000000..608cef4d4 --- /dev/null +++ b/.github/workflows/named-spec-verification.yml @@ -0,0 +1,49 @@ +name: Named specification verification + +# Legacy, non-required track: the Lean4Lean-derived named specification +# (`Ix.Theory.Named`) and the `Ix.Kernel.Verify` implementation proofs +# stated against it, with their exact trust manifests and sorry frontier. +# The kernel proof gate required on every PR is `lake build --wfail +# IxKernelConsistency` in ci.yml, whose audit rejects any `Ix.Theory.Named` +# module in its import closure. This track is being retired in favour of the +# set model, so it runs on demand and on a weekly schedule only; its audit +# numbers are frozen at the counts recorded in the manifests it builds. + +on: + workflow_dispatch: + schedule: + # Weekly, Monday 06:00 UTC. + - cron: '0 6 * * 1' + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + named-spec: + runs-on: warp-ubuntu-latest-x64-16x + steps: + - uses: actions/checkout@v7 + - uses: ./.github/actions/setup-rust-toolchain + # Same `.lake` cache as ci.yml (`./.lake`, per-sha key with prefix + # fallback): the newest CI build of the toolchain/manifest pair is + # restored and Lake rebuilds whatever differs. Restore-only: this + # workflow never publishes a cache entry that ci.yml could restore. + - uses: actions/cache/restore@v6 + with: + path: ./.lake + key: lake-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('lean-toolchain') }}-${{ hashFiles('lake-manifest.json') }}-${{ github.sha }} + restore-keys: lake-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('lean-toolchain') }}-${{ hashFiles('lake-manifest.json') }}- + - uses: leanprover/lean-action@v1 + with: + auto-config: false + use-github-cache: false + - name: Build named-specification implementation proofs + run: lake build IxKernelVerify + - name: Check Ix.Kernel exported theorem trust manifest + run: lake build Ix.Kernel.Verify.Audit.Completed Ix.Kernel.Verify.Audit.Conditional Ix.Kernel.Verify.Audit.Statements + - name: Check Ix.Kernel verification sorry frontier + run: lake build Ix.Kernel.Verify.Audit.SorryFrontier diff --git a/Ix/Theory/Named/Std/AxiomAudit.lean b/Ix/Kernel/Verify/Audit/AxiomAudit.lean similarity index 84% rename from Ix/Theory/Named/Std/AxiomAudit.lean rename to Ix/Kernel/Verify/Audit/AxiomAudit.lean index 816ae3cbb..ba0e3c48d 100644 --- a/Ix/Theory/Named/Std/AxiomAudit.lean +++ b/Ix/Kernel/Verify/Audit/AxiomAudit.lean @@ -7,16 +7,22 @@ import Lean.Elab.Command import Lean.Util.FoldConsts /-! -# Exact axiom checks for named-specification proofs +# Exact axiom checks for kernel verification proofs The checked sets use resolved declaration names, independent of pretty-print width and dependency traversal order. Both missing and additional axioms fail the check; a namespace migration cannot silently widen the recorded boundary. + +This traversal is shared by the direct consistency audit +(`Ix.Kernel.Verify.Audit.Basic`) and by the legacy named-specification +proofs, which use the `#guard_named_axioms` command below. It depends on no +`Ix.Theory` module, so the consistency build can import it without reaching +the named specification. -/ open Lean Elab Command -namespace Ix.Theory.Named.AxiomAudit +namespace Ix.Kernel.Verify.Audit.AxiomAudit /-- Traverse checked declarations directly. Imported axiom summaries can omit dependencies of mutually recursive declaration groups. -/ @@ -74,7 +80,7 @@ def collectCached (env : Environment) (root : Name) (cache : Cache) : State × C def collect (env : Environment) (root : Name) : State := (collectCached env root {}).1 -end Ix.Theory.Named.AxiomAudit +end Ix.Kernel.Verify.Audit.AxiomAudit syntax (name := guardNamedAxioms) "#guard_named_axioms " ident " [" ident,* "]" : command @@ -84,7 +90,7 @@ elab_rules : command let rootName ← liftCoreM <| realizeGlobalConstNoOverloadWithInfo root let expected ← axioms.getElems.mapM fun axiomSyntax => liftCoreM <| realizeGlobalConstNoOverloadWithInfo axiomSyntax - let actual := (Ix.Theory.Named.AxiomAudit.collect (← getEnv) rootName).axioms + let actual := (Ix.Kernel.Verify.Audit.AxiomAudit.collect (← getEnv) rootName).axioms unless actual.qsort Name.lt == expected.qsort Name.lt do let missing := expected.filter (!actual.contains ·) let additional := actual.filter (!expected.contains ·) @@ -104,10 +110,10 @@ private inductive AuditFixture : Prop where run_cmd do let env ← getEnv - let (first, cache) := Ix.Theory.Named.AxiomAudit.collectCached env + let (first, cache) := Ix.Kernel.Verify.Audit.AxiomAudit.collectCached env ``AuditFixture.plain {} - let (independent, cache) := Ix.Theory.Named.AxiomAudit.collectCached env ``Eq.refl cache - let (sibling, _) := Ix.Theory.Named.AxiomAudit.collectCached env + let (independent, cache) := Ix.Kernel.Verify.Audit.AxiomAudit.collectCached env ``Eq.refl cache + let (sibling, _) := Ix.Kernel.Verify.Audit.AxiomAudit.collectCached env ``AuditFixture.withAxiom cache unless first.axioms == #[``propext] && independent.axioms.isEmpty && sibling.axioms == #[``propext] do diff --git a/Ix/Kernel/Verify/Audit/Basic.lean b/Ix/Kernel/Verify/Audit/Basic.lean index 69b1991bb..63ef4f69c 100644 --- a/Ix/Kernel/Verify/Audit/Basic.lean +++ b/Ix/Kernel/Verify/Audit/Basic.lean @@ -1,7 +1,7 @@ import Lean.Elab.Command import Lean.PrivateName import Lean.Util.FoldConsts -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit /-! # Exact trust-boundary auditing for `Ix.Kernel.Verify` @@ -67,8 +67,8 @@ private def sortNames (xs : Array Name) : Array Name := namespace DependencyAudit -abbrev State := Ix.Theory.Named.AxiomAudit.State -abbrev collect := Ix.Theory.Named.AxiomAudit.collect +abbrev State := Ix.Kernel.Verify.Audit.AxiomAudit.State +abbrev collect := Ix.Kernel.Verify.Audit.AxiomAudit.collect end DependencyAudit @@ -153,13 +153,13 @@ instead of being silently audited twice. -/ def check (allowances : Array RootAllowance) : CommandElabM Unit := do let env ← getEnv let mut roots : NameSet := {} - let mut cache : Ix.Theory.Named.AxiomAudit.Cache := {} + let mut cache : Ix.Kernel.Verify.Audit.AxiomAudit.Cache := {} for allowance in allowances do if roots.contains allowance.root then throwError m!"duplicate axiom-audit root: {allowance.root}" roots := roots.insert allowance.root let (dependencies, nextCache) := - Ix.Theory.Named.AxiomAudit.collectCached env allowance.root cache + Ix.Kernel.Verify.Audit.AxiomAudit.collectCached env allowance.root cache cache := nextCache checkOne allowance dependencies logInfo m!"Ix.Kernel verification trust audit passed for {allowances.size} theorem roots" diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 61b400794..5f720f5d7 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -1656,4 +1656,15 @@ def roots : Array RootAllowance := #[ run_cmd Kernel.Verify.Audit.check roots +/-! The direct consistency roots must stay independent of the retired named +specification. Every module in this audit's import closure is checked by +name, so a shared helper cannot reintroduce that dependency unnoticed. -/ +open Lean in +run_cmd do + let modules := (← getEnv).allImportedModuleNames + let named := modules.filter (`Ix.Theory.Named).isPrefixOf + unless named.isEmpty do + throwError m!"consistency roots import named-specification modules: {named.toList}" + logInfo m!"Ix.Kernel consistency import audit passed: {modules.size} imported modules, none under Ix.Theory.Named" + end Ix.Kernel.Consistency.Audit diff --git a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean index 72570ab20..7c404becb 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean @@ -171,7 +171,7 @@ theorem SynthesisInference.soundWithHereditary {β : Type u} · rw [trace.output run, internExpr_readScopedExpr? (table := trace.abstracted.2) closedCoherent faithful] simp [LambdaBodyTrace.abstracted, LambdaBodyTrace.reduced, domainReads, - AExpr.erase] at ⊢ closedReads + AExpr.erase, Option.bind_eq_some_iff] at ⊢ closedReads exact closedReads | .letE full localState miss trace opening domainTree valueTree bodyTree domainReading valueReading bodyReading conditions hashPath comparisonFaithful substitution reduction => by diff --git a/Ix/Kernel/Verify/Consistency/SynthesisReading.lean b/Ix/Kernel/Verify/Consistency/SynthesisReading.lean index d12e77c78..2478f6b66 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisReading.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisReading.lean @@ -89,7 +89,8 @@ theorem SynthesisInference.outputReading {β : Type u} reducedCoherent closingFaithful reducedReads rw [trace.output run, internExpr_readScopedExpr? (table := trace.abstracted.2) closedCoherent faithful] - simp [LambdaBodyTrace.abstracted, LambdaBodyTrace.reduced, domainReads, AExpr.erase] at ⊢ closedReads + simp [LambdaBodyTrace.abstracted, LambdaBodyTrace.reduced, domainReads, AExpr.erase, + Option.bind_eq_some_iff] at ⊢ closedReads exact closedReads | .letE full localState miss trace opening _ _ bodyTree domainReading valueReading bodyReading _ _ _ substitution reduction => by @@ -125,7 +126,7 @@ theorem SynthesisInference.outputReading {β : Type u} change readScopedExpr? resolve locals (trace.abstracted.2.internExpr _).1 = _ rw [internExpr_readScopedExpr? (table := trace.abstracted.2) closedCoherent faithful] simp [LambdaSortInferenceTrace.abstracted, LambdaSortInferenceTrace.reduced, domainReads, - AExpr.erase] at ⊢ closedReads + AExpr.erase, Option.bind_eq_some_iff] at ⊢ closedReads exact closedReads | .letSort full localState miss trace opening _ _ bodyTree domainReading valueReading bodyReading _ _ _ substitution reduction => by diff --git a/Ix/Kernel/Verify/Level.lean b/Ix/Kernel/Verify/Level.lean index 3dfb8d4b5..c74d6e4e2 100644 --- a/Ix/Kernel/Verify/Level.lean +++ b/Ix/Kernel/Verify/Level.lean @@ -1,13 +1,14 @@ import Ix.Kernel.Level import Batteries.Recycling.RBTree.Lemmas -import Ix.Theory.Named.VLevel +import Ix.Theory.VLevelLemmas /-! # Universe soundness against `Ix.Theory.VLevel` -These proofs use classic imports to combine the named specification's helper -lemmas with the exposed production definitions in `Ix.Kernel`. The `rfl` -lemmas below verify unfolding across that module boundary. +These proofs combine the shared universe lemmas of `Ix.Theory.VLevelLemmas` +with the exposed production definitions in `Ix.Kernel`; no module of the +named specification is imported. The `rfl` lemmas below verify unfolding +across that module boundary. `KUniv` and `VLevel` align constructor-for-constructor (both carry positional params), so the translation `toVLevel` is a total structural diff --git a/Ix/Theory/Named/Fixtures/ProjectionExpressibility.lean b/Ix/Theory/Named/Fixtures/ProjectionExpressibility.lean index b06bed1fb..9d53fcc5b 100644 --- a/Ix/Theory/Named/Fixtures/ProjectionExpressibility.lean +++ b/Ix/Theory/Named/Fixtures/ProjectionExpressibility.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Meta import Ix.Theory.Named.Projection import Ix.Theory.Named.Typing.InductiveLemmas diff --git a/Ix/Theory/Named/Inductive.lean b/Ix/Theory/Named/Inductive.lean index de7598221..05fd898c6 100644 --- a/Ix/Theory/Named/Inductive.lean +++ b/Ix/Theory/Named/Inductive.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.VDecl import Ix.Theory.Named.Typing.Basic diff --git a/Ix/Theory/Named/InductiveFixtures.lean b/Ix/Theory/Named/InductiveFixtures.lean index 480b6e8aa..8cdd76d70 100644 --- a/Ix/Theory/Named/InductiveFixtures.lean +++ b/Ix/Theory/Named/InductiveFixtures.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Inductive import Ix.Theory.Named.Meta import Ix.Theory.Named.Typing.InductiveLemmas diff --git a/Ix/Theory/Named/Literals.lean b/Ix/Theory/Named/Literals.lean index 47a31b000..a507a3b0b 100644 --- a/Ix/Theory/Named/Literals.lean +++ b/Ix/Theory/Named/Literals.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Inductive import Ix.Theory.Named.Typing.Strong diff --git a/Ix/Theory/Named/LocalContext.lean b/Ix/Theory/Named/LocalContext.lean index 43edc5356..1377cc235 100644 --- a/Ix/Theory/Named/LocalContext.lean +++ b/Ix/Theory/Named/LocalContext.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Typing.UniqueTyping open Ix.Theory (VLevel) diff --git a/Ix/Theory/Named/MutualInductiveFixtures.lean b/Ix/Theory/Named/MutualInductiveFixtures.lean index c6548c444..47c33b267 100644 --- a/Ix/Theory/Named/MutualInductiveFixtures.lean +++ b/Ix/Theory/Named/MutualInductiveFixtures.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Inductive import Ix.Theory.Named.Meta diff --git a/Ix/Theory/Named/Projection.lean b/Ix/Theory/Named/Projection.lean index c3df2c34c..0bb8ced24 100644 --- a/Ix/Theory/Named/Projection.lean +++ b/Ix/Theory/Named/Projection.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Typing.InductivePatternWF open Ix.Theory (VLevel) diff --git a/Ix/Theory/Named/Reference/Inductive/Add.lean b/Ix/Theory/Named/Reference/Inductive/Add.lean index 7a7c29a38..0832f38b6 100644 --- a/Ix/Theory/Named/Reference/Inductive/Add.lean +++ b/Ix/Theory/Named/Reference/Inductive/Add.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Batteries.Data.List.Basic import Ix.Theory.Named.Reference.Environment.Basic import Ix.Theory.Named.Reference.TypeChecker diff --git a/Ix/Theory/Named/Reference/Inductive/EliminationTrace.lean b/Ix/Theory/Named/Reference/Inductive/EliminationTrace.lean index 3295fdb18..97551f422 100644 --- a/Ix/Theory/Named/Reference/Inductive/EliminationTrace.lean +++ b/Ix/Theory/Named/Reference/Inductive/EliminationTrace.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Reference.Inductive.ValidationTrace namespace Ix.Theory.Named diff --git a/Ix/Theory/Named/Reference/Inductive/ValidationTrace.lean b/Ix/Theory/Named/Reference/Inductive/ValidationTrace.lean index b757d208d..8a7cc94da 100644 --- a/Ix/Theory/Named/Reference/Inductive/ValidationTrace.lean +++ b/Ix/Theory/Named/Reference/Inductive/ValidationTrace.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Reference.Inductive.Add set_option linter.unusedSimpArgs false diff --git a/Ix/Theory/Named/SingletonParity.lean b/Ix/Theory/Named/SingletonParity.lean index aa1b08e7a..526164738 100644 --- a/Ix/Theory/Named/SingletonParity.lean +++ b/Ix/Theory/Named/SingletonParity.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.InductiveFixtures open Ix.Theory (VLevel) diff --git a/Ix/Theory/Named/Typing/Env.lean b/Ix/Theory/Named/Typing/Env.lean index b3e009d29..a7f69d056 100644 --- a/Ix/Theory/Named/Typing/Env.lean +++ b/Ix/Theory/Named/Typing/Env.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Typing.Basic import Ix.Theory.Named.VDecl import Ix.Theory.Named.Quot diff --git a/Ix/Theory/Named/Typing/InductiveCertificate.lean b/Ix/Theory/Named/Typing/InductiveCertificate.lean index 991c81b97..ca524e73d 100644 --- a/Ix/Theory/Named/Typing/InductiveCertificate.lean +++ b/Ix/Theory/Named/Typing/InductiveCertificate.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Typing.EnvLemmas import Ix.Theory.Named.Typing.InductivePattern import Ix.Theory.Named.Typing.NestedInductiveLemmas diff --git a/Ix/Theory/Named/Typing/InductiveLemmas.lean b/Ix/Theory/Named/Typing/InductiveLemmas.lean index 398afdeec..99832ed38 100644 --- a/Ix/Theory/Named/Typing/InductiveLemmas.lean +++ b/Ix/Theory/Named/Typing/InductiveLemmas.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Typing.Lemmas import Ix.Theory.Named.Typing.Env import Ix.Theory.Named.Typing.Meta diff --git a/Ix/Theory/Named/Typing/InductivePattern.lean b/Ix/Theory/Named/Typing/InductivePattern.lean index 75a95627f..3d8ae270d 100644 --- a/Ix/Theory/Named/Typing/InductivePattern.lean +++ b/Ix/Theory/Named/Typing/InductivePattern.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Typing.InductiveLemmas import Ix.Theory.Named.Typing.Pattern diff --git a/Ix/Theory/Named/Typing/InductivePatternWF.lean b/Ix/Theory/Named/Typing/InductivePatternWF.lean index ab8ef68d9..b43e5b706 100644 --- a/Ix/Theory/Named/Typing/InductivePatternWF.lean +++ b/Ix/Theory/Named/Typing/InductivePatternWF.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Typing.InductivePattern import Ix.Theory.Named.Typing.UniqueTyping diff --git a/Ix/Theory/Named/VLevel.lean b/Ix/Theory/Named/VLevel.lean index e2c9b26bf..ed1d00102 100644 --- a/Ix/Theory/Named/VLevel.lean +++ b/Ix/Theory/Named/VLevel.lean @@ -4,9 +4,14 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.VLevel +import Ix.Theory.VLevelLemmas import Ix.Theory.Named.Std.Basic +/-! The order, equivalence, and well-formedness lemmas over the shared +`Ix.Theory.VLevel` live in `Ix.Theory.VLevelLemmas`. This module keeps only +the conversion from Lean's named universe parameters, which the named +specification alone uses. -/ + open Ix.Theory (VLevel) namespace Ix.Theory.Named @@ -16,58 +21,6 @@ end Ix.Theory.Named namespace Ix.Theory.VLevel open Ix.Theory.Named -theorem le_trans {a b c : VLevel} (h1 : a ≤ b) (h2 : b ≤ c) : a ≤ c := - fun _ => Nat.le_trans (h1 _) (h2 _) - -theorem zero_le : zero ≤ a := fun _ => Nat.zero_le _ - -theorem le_succ : a ≤ succ a := fun _ => Nat.le_succ _ - -theorem succ_le_succ (h : a ≤ b) : succ a ≤ succ b := fun _ => Nat.succ_le_succ (h _) - -theorem le_max_left : a ≤ max a b := fun _ => Nat.le_max_left .. -theorem le_max_right : b ≤ max a b := fun _ => Nat.le_max_right .. - -theorem equiv_def' {a b : VLevel} : a ≈ b ↔ a.eval = b.eval := .rfl -theorem equiv_congr_left {a b c : VLevel} (h : a ≈ b) : a ≈ c ↔ b ≈ c := - iff_of_eq (congrArg (· = _) h) - -theorem equiv_congr_right {a b c : VLevel} (h : a ≈ b) : c ≈ a ↔ c ≈ b := - iff_of_eq (congrArg (_ = ·) h) - -theorem succ_congr_iff {a b : VLevel} : succ a ≈ succ b ↔ a ≈ b := by - simp [equiv_def, eval] - -theorem max_congr (h₁ : a₁ ≈ b₁) (h₂ : a₂ ≈ b₂) : max a₁ a₂ ≈ max b₁ b₂ := by - simp_all [equiv_def, eval] - -theorem max_comm : max a b ≈ max b a := by simp [equiv_def, eval, Nat.max_comm] - -theorem LE.max_eq_left (h : b.LE a) : max a b ≈ a := by - simp [equiv_def, eval, Nat.max_eq_left (h _)] - -theorem LE.max_eq_right (h : a.LE b) : max a b ≈ b := by - simp [equiv_def, eval, Nat.max_eq_right (h _)] - -theorem max_self : max a a ≈ a := by simp [equiv_def, eval] - -theorem zero_imax : imax zero a ≈ a := by - simp [equiv_def, eval, natIMax, eq_comm (b := 0)] - -theorem imax_zero : imax a zero ≈ zero := by simp [equiv_def, eval, natIMax] - -theorem imax_eq_zero : imax a b ≈ zero ↔ b ≈ zero := by - simp [equiv_def, eval, natIMax] - refine ⟨fun H ls => ?_, fun H ls hn => nomatch hn (H ls)⟩ - exact Decidable.byContradiction fun h => h (H ls h).2 - -def IsNeverZero (a : VLevel) : Prop := ∀ ls, a.eval ls ≠ 0 - -theorem IsNeverZero.imax_eq_max (h : IsNeverZero b) : imax a b ≈ max a b := by - simp_all [equiv_def, eval, natIMax, IsNeverZero] - -theorem id_WF : ∀ l ∈ (List.range u).map param, l.WF u := by simp [WF] - variable (ls : List Name) in def ofLevel : Lean.Level → Option VLevel | .zero => return .zero @@ -92,8 +45,3 @@ theorem WF.of_mapM_ofLevel (h : List.mapM (VLevel.ofLevel Us) us = some us') have ⟨_, _, h⟩ := h.forall_exists_r _ hl; exact .of_ofLevel h end Ix.Theory.VLevel - -/-- The internal universe operation agrees definitionally with Lean's -natural-number operation used by the reference implementation proofs. -/ -@[simp] theorem Ix.Theory.VLevel.natIMax_eq_core (a b : Nat) : - Ix.Theory.VLevel.natIMax a b = Lean.Nat.imax a b := rfl diff --git a/Ix/Theory/Named/Verify/Environment/Basic.lean b/Ix/Theory/Named/Verify/Environment/Basic.lean index 3166422c0..e53d44847 100644 --- a/Ix/Theory/Named/Verify/Environment/Basic.lean +++ b/Ix/Theory/Named/Verify/Environment/Basic.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Verify.LocalContext import Ix.Theory.Named.Typing.EnvLemmas diff --git a/Ix/Theory/Named/Verify/Environment/ConstructorValidation.lean b/Ix/Theory/Named/Verify/Environment/ConstructorValidation.lean index 375fdd742..eef5fc8c6 100644 --- a/Ix/Theory/Named/Verify/Environment/ConstructorValidation.lean +++ b/Ix/Theory/Named/Verify/Environment/ConstructorValidation.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Verify.Environment.Normalization open Ix.Theory (VLevel) diff --git a/Ix/Theory/Named/Verify/Environment/Elimination.lean b/Ix/Theory/Named/Verify/Environment/Elimination.lean index 6ab6559b4..ee02f0a0e 100644 --- a/Ix/Theory/Named/Verify/Environment/Elimination.lean +++ b/Ix/Theory/Named/Verify/Environment/Elimination.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Reference.Inductive.EliminationTrace import Ix.Theory.Named.Inductive diff --git a/Ix/Theory/Named/Verify/Environment/IndexedVecCandidate.lean b/Ix/Theory/Named/Verify/Environment/IndexedVecCandidate.lean index 8d305f876..5582c700c 100644 --- a/Ix/Theory/Named/Verify/Environment/IndexedVecCandidate.lean +++ b/Ix/Theory/Named/Verify/Environment/IndexedVecCandidate.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Verify.Environment.InductiveFixtures namespace Ix.Theory.Named.InductiveReplayFixtures diff --git a/Ix/Theory/Named/Verify/Environment/IndexedVecOuterReplay.lean b/Ix/Theory/Named/Verify/Environment/IndexedVecOuterReplay.lean index 6ccc4cd64..2a9cc9435 100644 --- a/Ix/Theory/Named/Verify/Environment/IndexedVecOuterReplay.lean +++ b/Ix/Theory/Named/Verify/Environment/IndexedVecOuterReplay.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Verify.Environment.IndexedVecConsReplay import Ix.Theory.Named.Verify.Name diff --git a/Ix/Theory/Named/Verify/Environment/IndexedVecSemanticReplay.lean b/Ix/Theory/Named/Verify/Environment/IndexedVecSemanticReplay.lean index d153fb0f5..2cdc10374 100644 --- a/Ix/Theory/Named/Verify/Environment/IndexedVecSemanticReplay.lean +++ b/Ix/Theory/Named/Verify/Environment/IndexedVecSemanticReplay.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Verify.Environment.IndexedVecOuterReplay /-! diff --git a/Ix/Theory/Named/Verify/Environment/InductiveFixtures.lean b/Ix/Theory/Named/Verify/Environment/InductiveFixtures.lean index b6efeea82..89cbd207a 100644 --- a/Ix/Theory/Named/Verify/Environment/InductiveFixtures.lean +++ b/Ix/Theory/Named/Verify/Environment/InductiveFixtures.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Verify.Environment.Lemmas import Ix.Theory.Named.Verify.Environment.ConstructorValidation import Ix.Theory.Named.Reference.Inductive.Add diff --git a/Ix/Theory/Named/Verify/Environment/Lemmas.lean b/Ix/Theory/Named/Verify/Environment/Lemmas.lean index d82bd8ecb..8ed7e862c 100644 --- a/Ix/Theory/Named/Verify/Environment/Lemmas.lean +++ b/Ix/Theory/Named/Verify/Environment/Lemmas.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Std.SMap import Ix.Theory.Named.Reference.Declaration import Ix.Theory.Named.Verify.Environment.Basic diff --git a/Ix/Theory/Named/Verify/Environment/MutualInductiveFixtures.lean b/Ix/Theory/Named/Verify/Environment/MutualInductiveFixtures.lean index 4fa9ec2de..3919cd3d5 100644 --- a/Ix/Theory/Named/Verify/Environment/MutualInductiveFixtures.lean +++ b/Ix/Theory/Named/Verify/Environment/MutualInductiveFixtures.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Verify.Environment.InductiveFixtures import Ix.Theory.Named.MutualInductiveFixtures diff --git a/Ix/Theory/Named/Verify/Environment/NestedReplay.lean b/Ix/Theory/Named/Verify/Environment/NestedReplay.lean index e23eb4c19..71582911b 100644 --- a/Ix/Theory/Named/Verify/Environment/NestedReplay.lean +++ b/Ix/Theory/Named/Verify/Environment/NestedReplay.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Verify.Environment.SingletonParityReplay import Ix.Theory.Named.Verify.Environment.NestedTransformation diff --git a/Ix/Theory/Named/Verify/Environment/Normalization.lean b/Ix/Theory/Named/Verify/Environment/Normalization.lean index 68a92acb1..abf978ec9 100644 --- a/Ix/Theory/Named/Verify/Environment/Normalization.lean +++ b/Ix/Theory/Named/Verify/Environment/Normalization.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Verify.TypeChecker import Ix.Theory.Named.Reference.Inductive.ValidationTrace diff --git a/Ix/Theory/Named/Verify/Environment/NormalizationMatrix.lean b/Ix/Theory/Named/Verify/Environment/NormalizationMatrix.lean index abedd29a3..498fe9fda 100644 --- a/Ix/Theory/Named/Verify/Environment/NormalizationMatrix.lean +++ b/Ix/Theory/Named/Verify/Environment/NormalizationMatrix.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Verify.Environment.InductiveFixtures open Ix.Theory (VLevel) diff --git a/Ix/Theory/Named/Verify/Environment/SingletonParityMatrix.lean b/Ix/Theory/Named/Verify/Environment/SingletonParityMatrix.lean index 73df928be..b2c70eaa3 100644 --- a/Ix/Theory/Named/Verify/Environment/SingletonParityMatrix.lean +++ b/Ix/Theory/Named/Verify/Environment/SingletonParityMatrix.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.SingletonParity import Ix.Theory.Named.Verify.Environment.ConstructorValidityMatrix import Ix.Theory.Named.Verify.Environment.EliminationFixturesEqNat diff --git a/Ix/Theory/Named/Verify/Environment/SingletonParityReplay.lean b/Ix/Theory/Named/Verify/Environment/SingletonParityReplay.lean index 14ef0134e..6b606ed92 100644 --- a/Ix/Theory/Named/Verify/Environment/SingletonParityReplay.lean +++ b/Ix/Theory/Named/Verify/Environment/SingletonParityReplay.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Verify.Environment.SingletonParityMatrix open Ix.Theory (VLevel) diff --git a/Ix/Theory/Named/Verify/TypeChecker/InferType.lean b/Ix/Theory/Named/Verify/TypeChecker/InferType.lean index a16d77f19..af4f4b2ff 100644 --- a/Ix/Theory/Named/Verify/TypeChecker/InferType.lean +++ b/Ix/Theory/Named/Verify/TypeChecker/InferType.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Ix.Theory.Named.Verify.TypeChecker.Reduce open Ix.Theory (VLevel) diff --git a/Ix/Theory/Named/Verify/Typing/Lemmas.lean b/Ix/Theory/Named/Verify/Typing/Lemmas.lean index 7e4160ac5..a09b4196c 100644 --- a/Ix/Theory/Named/Verify/Typing/Lemmas.lean +++ b/Ix/Theory/Named/Verify/Typing/Lemmas.lean @@ -4,7 +4,7 @@ SPDX-License-Identifier: Apache-2.0 Source attribution and revision: Ix/Theory/Named/NOTICE. -/ -import Ix.Theory.Named.Std.AxiomAudit +import Ix.Kernel.Verify.Audit.AxiomAudit import Batteries.Data.String.Lemmas import Ix.Theory.Named.Verify.Typing.Expr import Ix.Theory.Named.Verify.Expr diff --git a/Ix/Theory/VLevelLemmas.lean b/Ix/Theory/VLevelLemmas.lean new file mode 100644 index 000000000..145ef9c88 --- /dev/null +++ b/Ix/Theory/VLevelLemmas.lean @@ -0,0 +1,79 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Lean.Level +import Ix.Theory.VLevel + +/-! +# Additional universe-level lemmas + +Order, equivalence, and well-formedness facts about the shared `VLevel` +semantics, together with the definitional bridge to Lean's natural-number +`imax`. Everything here is stated over `Ix.Theory.VLevel` alone, so the +kernel proofs can use these lemmas without importing any module of the +named specification, which is being retired. The named-parameter conversion +`ofLevel` stays with that specification in `Ix.Theory.Named.VLevel`. +-/ + +namespace Ix.Theory.VLevel + +theorem le_trans {a b c : VLevel} (h1 : a ≤ b) (h2 : b ≤ c) : a ≤ c := + fun _ => Nat.le_trans (h1 _) (h2 _) + +theorem zero_le : zero ≤ a := fun _ => Nat.zero_le _ + +theorem le_succ : a ≤ succ a := fun _ => Nat.le_succ _ + +theorem succ_le_succ (h : a ≤ b) : succ a ≤ succ b := fun _ => Nat.succ_le_succ (h _) + +theorem le_max_left : a ≤ max a b := fun _ => Nat.le_max_left .. +theorem le_max_right : b ≤ max a b := fun _ => Nat.le_max_right .. + +theorem equiv_def' {a b : VLevel} : a ≈ b ↔ a.eval = b.eval := .rfl +theorem equiv_congr_left {a b c : VLevel} (h : a ≈ b) : a ≈ c ↔ b ≈ c := + iff_of_eq (congrArg (· = _) h) + +theorem equiv_congr_right {a b c : VLevel} (h : a ≈ b) : c ≈ a ↔ c ≈ b := + iff_of_eq (congrArg (_ = ·) h) + +theorem succ_congr_iff {a b : VLevel} : succ a ≈ succ b ↔ a ≈ b := by + simp [equiv_def, eval] + +theorem max_congr (h₁ : a₁ ≈ b₁) (h₂ : a₂ ≈ b₂) : max a₁ a₂ ≈ max b₁ b₂ := by + simp_all [equiv_def, eval] + +theorem max_comm : max a b ≈ max b a := by simp [equiv_def, eval, Nat.max_comm] + +theorem LE.max_eq_left (h : b.LE a) : max a b ≈ a := by + simp [equiv_def, eval, Nat.max_eq_left (h _)] + +theorem LE.max_eq_right (h : a.LE b) : max a b ≈ b := by + simp [equiv_def, eval, Nat.max_eq_right (h _)] + +theorem max_self : max a a ≈ a := by simp [equiv_def, eval] + +theorem zero_imax : imax zero a ≈ a := by + simp [equiv_def, eval, natIMax, eq_comm (b := 0)] + +theorem imax_zero : imax a zero ≈ zero := by simp [equiv_def, eval, natIMax] + +theorem imax_eq_zero : imax a b ≈ zero ↔ b ≈ zero := by + simp [equiv_def, eval, natIMax] + refine ⟨fun H ls => ?_, fun H ls hn => nomatch hn (H ls)⟩ + exact Decidable.byContradiction fun h => h (H ls h).2 + +def IsNeverZero (a : VLevel) : Prop := ∀ ls, a.eval ls ≠ 0 + +theorem IsNeverZero.imax_eq_max (h : IsNeverZero b) : imax a b ≈ max a b := by + simp_all [equiv_def, eval, natIMax, IsNeverZero] + +theorem id_WF : ∀ l ∈ (List.range u).map param, l.WF u := by simp [WF] + +end Ix.Theory.VLevel + +/-- The internal universe operation agrees definitionally with Lean's +natural-number operation used by the reference implementation proofs. -/ +@[simp] theorem Ix.Theory.VLevel.natIMax_eq_core (a b : Nat) : + Ix.Theory.VLevel.natIMax a b = Lean.Nat.imax a b := rfl diff --git a/Tests/Theory/ImportManifest.lean b/Tests/Theory/ImportManifest.lean index 59b729c78..f02bb85a2 100644 --- a/Tests/Theory/ImportManifest.lean +++ b/Tests/Theory/ImportManifest.lean @@ -19,7 +19,8 @@ def conLecheRevision : String := "86cd20a65660d757cedc81561a44579099b565d0" def authored : Array String := #[ "Ix/Theory/Model/Checking.lean", "Ix/Theory/Model/LevelCongruence.lean", "Ix/Theory/Model/UniverseBounds.lean", "Ix/Theory/Model/Substitution.lean", - "Ix/Theory/Model/BetaSubstitution.lean", "Ix/Theory/Model/BetaSpine.lean" + "Ix/Theory/Model/BetaSubstitution.lean", "Ix/Theory/Model/BetaSpine.lean", + "Ix/Theory/VLevelLemmas.lean" ] structure SourceFile where diff --git a/Tests/Theory/Provenance.lean b/Tests/Theory/Provenance.lean index 0c0c4fc79..761db302c 100644 --- a/Tests/Theory/Provenance.lean +++ b/Tests/Theory/Provenance.lean @@ -26,8 +26,7 @@ def main (args : List String) : IO Unit := do let named := actual.filter (·.startsWith "Ix/Theory/Named/") sameFiles ((actual.filter (!·.startsWith "Ix/Theory/Named/")).push "Ix/Theory.lean") (selected.map (·.target) ++ authored) - sameFiles named ((Tests.Theory.NamedManifest.selected.map (·.target)).push - "Ix/Theory/Named/Std/AxiomAudit.lean") + sameFiles named (Tests.Theory.NamedManifest.selected.map (·.target)) need (← (FilePath.mk "Ix/Theory/Named/LICENSE").pathExists) "missing named-specification Apache license" need (← (FilePath.mk "Ix/Theory/Named/NOTICE").pathExists) "missing named-specification attribution" for path in selected.map (·.target) ++ authored do diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index af2c97bf7..cc2c28a7f 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -588,9 +588,18 @@ lake run check-kernel --with-model It combines the following checks. Omit `--with-model` to skip the separate Mathlib package. Ordinary PR CI covers the root checks in its build, theory, -and test jobs. The model has a separate workflow triggered by changes to the -package, interface, or configuration; the merge queue adds the expensive -parity corpus. +and test jobs, with one exception: the named-specification build +(`lake build IxKernelVerify` together with the +`Ix.Kernel.Verify.Audit.Completed`, `Conditional`, `Statements`, and +`SorryFrontier` manifests) is no longer part of the required PR gate. That +track is being retired in favour of the set model, and it now runs in the +non-required `named-spec-verification` workflow (manual dispatch or weekly +schedule). The required kernel proof gate is +`lake build --wfail IxKernelConsistency`; its audit additionally fails if any +`Ix.Theory.Named` module is in the import closure of the consistency roots. +Required CI still builds `IxCompileVerify` on its own. The model has a +separate workflow triggered by changes to the package, interface, or +configuration; the merge queue adds the expensive parity corpus. ```sh lake build IxKernelVerify IxCompileVerify diff --git a/lakefile.lean b/lakefile.lean index e2c94dad0..b05967722 100644 --- a/lakefile.lean +++ b/lakefile.lean @@ -233,14 +233,18 @@ target ix_native_decide_dynlib pkg : Dynlib := do (pkg.buildDir / nameToSharedLib "ix_native_decide") (boxedObjs.push ixCdylib) #[] -/- Formal verification of `Ix.Kernel` against the internal named specification. -Non-default: `lake build ix` never -touches it, and `build-all` (the lint driver) skips it by name because its -internal named-specification proofs still emit named `sorry` warnings — `lake lint +/- Legacy formal verification of `Ix.Kernel` against the internal named +specification (`Ix.Theory.Named`, Lean4Lean-derived). This track is being +retired in favour of the set model (`Ix.Theory.Model`); the required kernel +proof gate is `IxKernelConsistency` below, whose audit rejects any +`Ix.Theory.Named` module in its import closure. Non-default: `lake build ix` +never touches it, and `build-all` (the lint driver) skips it by name because +its named-specification proofs still emit named `sorry` warnings — `lake lint -- --wfail` would otherwise fail even though the Ix verification source has -no local `sorry` tokens. Required CI builds it separately without `--wfail`, -audits the exact local sorry frontier, and checks exact per-root transitive -axiom plus direct-`sorryAx`-origin manifests. Dev loop: +no local `sorry` tokens. It is not part of required CI: the non-required +`named-spec-verification` workflow (manual or weekly) builds it without +`--wfail`, audits the exact local sorry frontier, and checks exact per-root +transitive axiom plus direct-`sorryAx`-origin manifests. Dev loop: `lake build IxKernelVerify`; focused trust audit: `lake build Ix.Kernel.Verify.Audit.Completed Ix.Kernel.Verify.Audit.Conditional Ix.Kernel.Verify.Audit.Statements`. -/ @@ -262,10 +266,13 @@ lean_lib IxKernelConsistency where section IxCompileVerify -/- Formal verification of the Lean-to-Ixon compiler against the same -internal named-specification endpoint as `IxKernelVerify`. Kept as a separate non-default -library so compiler proofs cannot accidentally inherit checker acceptance -theorems as their specification. -/ +/- Formal verification of the Lean-to-Ixon compiler. It still targets the +legacy named-specification syntax (`Ix.Theory.Named.VExpr`) that +`IxKernelVerify` uses, so it transitively builds part of that non-required +track until the compiler relation is retargeted to the set model. Required CI +builds it on its own (`lake build IxCompileVerify`). Kept as a separate +non-default library so compiler proofs cannot accidentally inherit checker +acceptance theorems as their specification. -/ lean_lib IxCompileVerify where globs := #[.submodules `Ix.Compile.Verify] @@ -328,9 +335,11 @@ script "build-all" (args) := do let pkg ← getRootPackage let libNames := pkg.configTargets LeanLib.configKind |>.map (·.name.toString) let exeNames := pkg.configTargets LeanExe.configKind |>.map (·.name.toString) - -- The named specification and its implementation proofs retain an audited - -- frontier. CI builds them separately without `--wfail`. The set model, - -- direct consistency roots, and certified adapters are checked strictly. + -- The legacy named specification and the implementation proofs stated + -- against it retain an audited frontier; the non-required + -- named-spec-verification workflow builds them without `--wfail`, and + -- required CI builds `IxCompileVerify` on its own. The set model, direct + -- consistency roots, and certified adapters are checked strictly. let allNames := (libNames ++ exeNames |>.toList).filter fun name => name != "IxKernelVerify" && name != "IxCompileVerify" && name != "IxTheoryNamed" for name in allNames do @@ -352,7 +361,7 @@ lean_lib IxTheory where globs := #[.one `Ix.Theory, .one `Ix.Theory.Certified, .one `Ix.Theory.Const, .one `Ix.Theory.Expr, .one `Ix.Theory.Quot, .one `Ix.Theory.Ref, .one `Ix.Theory.Rename, .one `Ix.Theory.Store, - .one `Ix.Theory.VLevel, .submodules `Ix.Theory.Certificate, + .one `Ix.Theory.VLevel, .one `Ix.Theory.VLevelLemmas, .submodules `Ix.Theory.Certificate, .submodules `Ix.Theory.Certified, .submodules `Ix.Theory.Inductive, .submodules `Ix.Theory.Model, .submodules `Ix.Theory.Std] From 2d94fc56d68bab91bc11ad56947298a4857db106 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 15 Sep 2026 10:02:06 -0400 Subject: [PATCH 51/63] Retarget compiler proofs to the set-model syntax IxonExprRel and SourceExprRel now relate Ixon and Ix source expressions to Ix.Theory.VExpr Address, resolving addresses to ConstRef and reading against a Model.Environment index, with native proj/natLit and a shared string-literal expansion (StringRefs.stringLiteral) that mirrors the kernel's strLitToConstructor. The projection relation, universe count, and local context indices are dropped; all 143 audited roots keep their names and allowances. Named.Std.Basic and Named.Verify.QSort uses are replaced by attributed local helpers, and a new NamedFree audit fails the build if Ix.Compile.Verify imports anything under Ix.Theory.Named. Docs, the lakefile comment, and plans/wp-c-status.md describe the new endpoint. --- Ix/Compile/Verify/Audit/NamedFree.lean | 35 +++ Ix/Compile/Verify/Audit/SorryFrontier.lean | 4 +- Ix/Compile/Verify/Audit/Statements.lean | 4 +- Ix/Compile/Verify/Catalog.lean | 18 +- Ix/Compile/Verify/CompileExpr.lean | 86 +++---- Ix/Compile/Verify/CompilePreseed.lean | 10 +- Ix/Compile/Verify/CompileUniv.lean | 2 +- Ix/Compile/Verify/IxonValue.lean | 270 ++++++++++----------- Ix/Compile/Verify/QSort.lean | 128 ++++++++++ Ix/Compile/Verify/Reference.lean | 20 +- Ix/Compile/Verify/SourceValue.lean | 155 ++++++------ Ix/Compile/Verify/Statements.lean | 20 +- Ix/Compile/Verify/StdLemmas.lean | 52 ++++ Ix/Compile/Verify/StringLiteral.lean | 94 +++++++ docs/kernel-verification.md | 8 +- lakefile.lean | 14 +- 16 files changed, 619 insertions(+), 301 deletions(-) create mode 100644 Ix/Compile/Verify/Audit/NamedFree.lean create mode 100644 Ix/Compile/Verify/QSort.lean create mode 100644 Ix/Compile/Verify/StdLemmas.lean create mode 100644 Ix/Compile/Verify/StringLiteral.lean diff --git a/Ix/Compile/Verify/Audit/NamedFree.lean b/Ix/Compile/Verify/Audit/NamedFree.lean new file mode 100644 index 000000000..5db52a316 --- /dev/null +++ b/Ix/Compile/Verify/Audit/NamedFree.lean @@ -0,0 +1,35 @@ +import Ix.Compile.Verify.Statements + +/-! +# Named-specification import quarantine + +The compiler proofs target the set model. Fail the build if the import +closure of the public compiler-verification frontier contains any module +under `Ix.Theory.Named`. + +This check lives apart from `Audit.Statements` because the shared axiom-audit +mechanism (`Ix.Kernel.Verify.Audit.Basic`) still imports the Ix-authored +helper `Ix.Theory.Named.Std.AxiomAudit`; that helper is scheduled to leave the +named tree (plan WP-N stage 0), after which this check can move into the +statement audit. +-/ + +open Lean Lean.Elab.Command + +namespace Ix.Compile.Verify.Audit + +def checkNamedFree : CommandElabM Unit := do + let env ← getEnv + let offenders := env.allImportedModuleNames.filter fun name => + (`Ix.Theory.Named).isPrefixOf name + if offenders.isEmpty then + logInfo m!"Ix.Compile.Verify imports no module under Ix.Theory.Named" + else + let body := String.intercalate "\n" <| offenders.toList.map fun name => + s!" {name}" + throwError m!"Ix.Compile.Verify reaches the retired named specification — \ + {offenders.size} module(s) under Ix.Theory.Named are imported:\n{body}" + +run_cmd checkNamedFree + +end Ix.Compile.Verify.Audit diff --git a/Ix/Compile/Verify/Audit/SorryFrontier.lean b/Ix/Compile/Verify/Audit/SorryFrontier.lean index 0595ae8b1..330eb7dc7 100644 --- a/Ix/Compile/Verify/Audit/SorryFrontier.lean +++ b/Ix/Compile/Verify/Audit/SorryFrontier.lean @@ -4,8 +4,8 @@ import Ix.Compile.Verify.Audit.Statements # Compiler-verification source sorry frontier Fail the build if any declaration emitted from an `Ix.Compile.Verify` source -module directly references `sorryAx`. Named-specification debt is handled by -per-root transitive manifests rather than being confused with local source +module directly references `sorryAx`. Transitive debt from imported libraries +is handled by per-root manifests rather than being confused with local source placeholders. -/ diff --git a/Ix/Compile/Verify/Audit/Statements.lean b/Ix/Compile/Verify/Audit/Statements.lean index d30f4c7a4..d4468b8ea 100644 --- a/Ix/Compile/Verify/Audit/Statements.lean +++ b/Ix/Compile/Verify/Audit/Statements.lean @@ -8,7 +8,9 @@ These roots cover the first expression-level square, concrete catalog integrity, and the production compiler's finite-table transitions. The explicit `KernelSourceWitness` assumption is data supplied to later theorems, not a global axiom, and no compiler theorem may inherit checker acceptance as -a premise. +a premise. The value relations target the set model (`Ix.Theory.Model`); +`Audit.NamedFree` separately checks that the proof modules import nothing +under `Ix.Theory.Named`. -/ namespace Ix.Compile.Verify.Audit.Statements diff --git a/Ix/Compile/Verify/Catalog.lean b/Ix/Compile/Verify/Catalog.lean index 0f931e270..4b8b2ff2c 100644 --- a/Ix/Compile/Verify/Catalog.lean +++ b/Ix/Compile/Verify/Catalog.lean @@ -393,7 +393,7 @@ structure Catalog.Finite (catalog : Catalog) : Prop where memberAddrs : FinitelySupported catalog.memberAddrs /-- In-memory catalog integrity. This is representation -well-formedness, not Ix.Theory.Named `VEnv.WF`. -/ +well-formedness, not set-model realization of the environment index. -/ structure Catalog.WF (catalog : Catalog) : Prop where finite : catalog.Finite constants : ∀ {addr constant}, catalog.constants addr = some constant → @@ -407,7 +407,7 @@ structure Catalog.WF (catalog : Catalog) : Prop where ∀ addr ∈ addrs, ∃ constant, catalog.constants addr = some constant def Catalog.empty : Catalog where - nameOf := fun _ => none + resolve := fun _ => none blobs := fun _ => none theorem Catalog.empty_finite : Catalog.empty.Finite := @@ -432,15 +432,15 @@ theorem Catalog.empty_wf : Catalog.empty.WF := by change (none : Option (Array Address)) = some addrs at h cases h -/-- Immutable view of a concrete `Ixon.Env`. `nameOf` and mutual member +/-- Immutable view of a concrete `Ixon.Env`. `resolve` and mutual member addresses remain explicit semantic inputs because the wire environment stores -Ix names and projection constants, not Ix.Theory.Named names or a redundant member -array. -/ +Ix names and projection constants, not set-model block references or a +redundant member array. -/ def Catalog.ofEnv (env : Ixon.Env) - (nameOf : Address → Option Lean.Name) + (resolve : Address → Option (Ix.Theory.ConstRef Address)) (memberAddrs : Address → Option (Array Address) := fun _ => none) : Catalog where - nameOf := nameOf + resolve := resolve constants := env.getConst? blobs := env.getBlob? named := env.getNamed? @@ -461,11 +461,11 @@ structure EnvLookupFaithful (env : Ixon.Env) : Prop where proof-only mutual-member view needs an explicit finite witness. Structural key faithfulness is explicit because these maps use digest equality. -/ theorem Catalog.ofEnv_finite (env : Ixon.Env) - (nameOf : Address → Option Lean.Name) + (resolve : Address → Option (Ix.Theory.ConstRef Address)) (memberAddrs : Address → Option (Array Address)) (hlookup : EnvLookupFaithful env) (hmembers : FinitelySupported memberAddrs) : - (Catalog.ofEnv env nameOf memberAddrs).Finite := by + (Catalog.ofEnv env resolve memberAddrs).Finite := by refine ⟨?_, FinitelySupported.hashMap env.blobs hlookup.blobs, FinitelySupported.hashMap env.named hlookup.named, FinitelySupported.hashMap env.names hlookup.names, diff --git a/Ix/Compile/Verify/CompileExpr.lean b/Ix/Compile/Verify/CompileExpr.lean index 85347ceab..78e867acf 100644 --- a/Ix/Compile/Verify/CompileExpr.lean +++ b/Ix/Compile/Verify/CompileExpr.lean @@ -24,7 +24,7 @@ closes the complete ordinary-expression tree: sorts, arbitrary-universe local and external constants, recursive projections, literals, structural composition, and arbitrary metadata maps including recursive syntax values. The proof covers warm caches, universe spelling patches, blob/name commits, -and independent Ix.Theory.Named values. Its strengthened frontier also relates the +and independent set-model values. Its strengthened frontier also relates the returned `UInt64` root, including the encoded KV map, to the append-only presentation arena under an explicit no-wrap capacity premise. -/ @@ -1309,11 +1309,10 @@ theorem compileExpr_run_sort_refines exact hrun /-- The production sort result therefore denotes the same independent -Ix.Theory.Named value as the source sort. -/ +set-model value as the source sort. -/ theorem compileExpr_run_sort_value - {venv : Ix.Theory.Named.VEnv} {sctx : SourceCtx} {catalog : Catalog} - {dctx : DecodeCtx} {trProj : ProjectionRel} - {uvars : Nat} {locals : List Ix.Theory.Named.VExpr} + {entries : Ix.Theory.Model.Environment Address} {sctx : SourceCtx} + {catalog : Catalog} {dctx : DecodeCtx} {strings : StringRefs Address} (compileEnv : Ix.CompileM.CompileEnv) (blockEnv : Ix.CompileM.BlockEnv) (snapshot : Ix.CompileM.BlockState) {levelSupport : Ix.Level → Prop} @@ -1324,19 +1323,19 @@ theorem compileExpr_run_sort_value (hctx : RefCompileCtxRel (frozenRefCompileCtx compileEnv blockEnv snapshot) sctx catalog dctx) {state : Ix.CompileM.BlockState} {level : Ix.Level} {hash : Address} - {raw : Ixon.Univ} {idx : UInt64} {value : Ix.Theory.Named.VExpr} + {raw : Ixon.Univ} {idx : UInt64} {value : Ix.Theory.VExpr Address} (hlevel : levelSupport level) (hstate : FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state) (hraw : compileUnivRef (univParamIndex blockEnv.univCtx) level = some raw) (hpreseed : snapshot.univsIndex.get? (Ixon.canonUniv raw) = some idx) - (hsource : SourceExprRel (uvars := uvars) venv sctx trProj locals + (hsource : SourceExprRel entries sctx strings (.sort level hash) value) : ∃ root state', Ix.CompileM.CompileM.run compileEnv blockEnv state (Ix.CompileM.compileExpr (.sort level hash)) = .ok ((.sort idx, root), state') ∧ FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state' ∧ - IxonExprRel (uvars := uvars) venv catalog dctx trProj locals + IxonExprRel entries catalog dctx strings (.sort idx) value := by obtain ⟨root, state', hrun, hstate'⟩ := compileExpr_run_sort_refines compileEnv blockEnv snapshot hfree hclosed @@ -2097,9 +2096,8 @@ theorem compileExpr_run_constEmpty_ref_refines Ix.CompileM.exprCompileDepth] using hrun theorem compileExpr_run_constEmpty_recur_value - {venv : Ix.Theory.Named.VEnv} {sctx : SourceCtx} {catalog : Catalog} - {dctx : DecodeCtx} {trProj : ProjectionRel} - {uvars : Nat} {locals : List Ix.Theory.Named.VExpr} + {entries : Ix.Theory.Model.Environment Address} {sctx : SourceCtx} + {catalog : Catalog} {dctx : DecodeCtx} {strings : StringRefs Address} (compileEnv : Ix.CompileM.CompileEnv) (blockEnv : Ix.CompileM.BlockEnv) (snapshot : Ix.CompileM.BlockState) {levelSupport : Ix.Level → Prop} @@ -2108,17 +2106,17 @@ theorem compileExpr_run_constEmpty_recur_value (hctx : RefCompileCtxRel (frozenRefCompileCtx compileEnv blockEnv snapshot) sctx catalog dctx) {state : Ix.CompileM.BlockState} {name : Ix.Name} {hash : Address} - {recIdx : Nat} {value : Ix.Theory.Named.VExpr} + {recIdx : Nat} {value : Ix.Theory.VExpr Address} (hstate : FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state) (hmut : blockEnv.mutCtx.get? name = some recIdx) - (hsource : SourceExprRel (uvars := uvars) venv sctx trProj locals + (hsource : SourceExprRel entries sctx strings (.const name #[] hash) value) : ∃ root state', Ix.CompileM.CompileM.run compileEnv blockEnv state (Ix.CompileM.compileExpr (.const name #[] hash)) = .ok ((.recur recIdx.toUInt64 #[], root), state') ∧ FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state' ∧ - IxonExprRel (uvars := uvars) venv catalog dctx trProj locals + IxonExprRel entries catalog dctx strings (.recur recIdx.toUInt64 #[]) value := by obtain ⟨root, state', hrun, hstate'⟩ := compileExpr_run_constEmpty_recur_refines compileEnv blockEnv snapshot hfree @@ -2138,9 +2136,8 @@ theorem compileExpr_run_constEmpty_recur_value compileExprRef_value hctx hsource href⟩ theorem compileExpr_run_constEmpty_ref_value - {venv : Ix.Theory.Named.VEnv} {sctx : SourceCtx} {catalog : Catalog} - {dctx : DecodeCtx} {trProj : ProjectionRel} - {uvars : Nat} {locals : List Ix.Theory.Named.VExpr} + {entries : Ix.Theory.Model.Environment Address} {sctx : SourceCtx} + {catalog : Catalog} {dctx : DecodeCtx} {strings : StringRefs Address} (compileEnv : Ix.CompileM.CompileEnv) (blockEnv : Ix.CompileM.BlockEnv) (snapshot : Ix.CompileM.BlockState) {levelSupport : Ix.Level → Prop} @@ -2149,19 +2146,19 @@ theorem compileExpr_run_constEmpty_ref_value (hctx : RefCompileCtxRel (frozenRefCompileCtx compileEnv blockEnv snapshot) sctx catalog dctx) {state : Ix.CompileM.BlockState} {name : Ix.Name} {hash addr : Address} - {refIdx : UInt64} {value : Ix.Theory.Named.VExpr} + {refIdx : UInt64} {value : Ix.Theory.VExpr Address} (hstate : FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state) (hmut : blockEnv.mutCtx.get? name = none) (hresolve : resolveConstAddr? compileEnv snapshot name = some addr) (hpreseed : snapshot.refsIndex.get? addr = some refIdx) - (hsource : SourceExprRel (uvars := uvars) venv sctx trProj locals + (hsource : SourceExprRel entries sctx strings (.const name #[] hash) value) : ∃ root state', Ix.CompileM.CompileM.run compileEnv blockEnv state (Ix.CompileM.compileExpr (.const name #[] hash)) = .ok ((.ref refIdx #[], root), state') ∧ FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state' ∧ - IxonExprRel (uvars := uvars) venv catalog dctx trProj locals + IxonExprRel entries catalog dctx strings (.ref refIdx #[]) value := by obtain ⟨root, state', hrun, hstate'⟩ := compileExpr_run_constEmpty_ref_refines compileEnv blockEnv snapshot hfree @@ -2317,9 +2314,8 @@ theorem compileExpr_run_lit_refines Ix.CompileM.exprCompileDepth] using hrun theorem compileExpr_run_lit_value - {venv : Ix.Theory.Named.VEnv} {sctx : SourceCtx} {catalog : Catalog} - {dctx : DecodeCtx} {trProj : ProjectionRel} - {uvars : Nat} {locals : List Ix.Theory.Named.VExpr} + {entries : Ix.Theory.Model.Environment Address} {sctx : SourceCtx} + {catalog : Catalog} {dctx : DecodeCtx} {strings : StringRefs Address} (compileEnv : Ix.CompileM.CompileEnv) (blockEnv : Ix.CompileM.BlockEnv) (snapshot : Ix.CompileM.BlockState) {levelSupport : Ix.Level → Prop} @@ -2328,17 +2324,17 @@ theorem compileExpr_run_lit_value (hctx : RefCompileCtxRel (frozenRefCompileCtx compileEnv blockEnv snapshot) sctx catalog dctx) {state : Ix.CompileM.BlockState} {literal : Lean.Literal} {hash : Address} - {refIdx : UInt64} {value : Ix.Theory.Named.VExpr} + {refIdx : UInt64} {value : Ix.Theory.VExpr Address} (hstate : FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state) (hpreseed : snapshot.refsIndex.get? (literalAddress literal) = some refIdx) - (hsource : SourceExprRel (uvars := uvars) venv sctx trProj locals + (hsource : SourceExprRel entries sctx strings (.lit literal hash) value) : ∃ root state', Ix.CompileM.CompileM.run compileEnv blockEnv state (Ix.CompileM.compileExpr (.lit literal hash)) = .ok ((literalExpr literal refIdx, root), state') ∧ FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state' ∧ - IxonExprRel (uvars := uvars) venv catalog dctx trProj locals + IxonExprRel entries catalog dctx strings (literalExpr literal refIdx) value := by obtain ⟨root, state', hrun, hstate'⟩ := compileExpr_run_lit_refines compileEnv blockEnv snapshot hfree @@ -2460,28 +2456,28 @@ theorem compileExpr_run_structural_refines exact hrun /-- The production result in the structural fragment therefore denotes the -same independent Ix.Theory.Named value as its named Ix source. -/ +same independent set-model value as its named Ix source. -/ theorem compileExpr_run_structural_value - {venv : Ix.Theory.Named.VEnv} {sctx : SourceCtx} {catalog : Catalog} - {dctx : DecodeCtx} {ctx : RefCompileCtx} {trProj : ProjectionRel} - {uvars : Nat} {locals : List Ix.Theory.Named.VExpr} + {entries : Ix.Theory.Model.Environment Address} {sctx : SourceCtx} + {catalog : Catalog} {dctx : DecodeCtx} {ctx : RefCompileCtx} + {strings : StringRefs Address} (compileEnv : Ix.CompileM.CompileEnv) (blockEnv : Ix.CompileM.BlockEnv) (hfree : compileEnv.surgeryFree = true) (hfaithful : ExprKeyFaithfulOn StructuralExpr) (hctx : RefCompileCtxRel ctx sctx catalog dctx) {state : Ix.CompileM.BlockState} {source : Ix.Expr} - {target : Ixon.Expr} {value : Ix.Theory.Named.VExpr} + {target : Ixon.Expr} {value : Ix.Theory.VExpr Address} (hstruct : StructuralExpr source) (hstate : StructuralExprCacheWF ctx state) - (hsource : SourceExprRel (uvars := uvars) venv sctx trProj locals source value) + (hsource : SourceExprRel entries sctx strings source value) (href : compileExprRef ctx source = some target) : ∃ root state', Ix.CompileM.CompileM.run compileEnv blockEnv state (Ix.CompileM.compileExpr source) = .ok ((target, root), state') ∧ StructuralExprCacheWF ctx state' ∧ - IxonExprRel (uvars := uvars) venv catalog dctx trProj locals target value := by + IxonExprRel entries catalog dctx strings target value := by obtain ⟨root, state', hrun, hstate'⟩ := compileExpr_run_structural_refines compileEnv blockEnv ctx hfree hfaithful hstruct hstate href @@ -4140,12 +4136,11 @@ theorem compileExpr_run_ordinary_wireWF hlevelFaithful hexprFaithful hsource hstate href exact ⟨root, state', hrun, hstate', compileExprRef_wireWF hbound href⟩ -/-- Complete ordinary compilation preserves the independent Ix.Theory.Named value +/-- Complete ordinary compilation preserves the independent set-model value assigned to the source expression. -/ theorem compileExpr_run_ordinary_value - {venv : Ix.Theory.Named.VEnv} {sctx : SourceCtx} {catalog : Catalog} - {dctx : DecodeCtx} {trProj : ProjectionRel} - {uvars : Nat} {locals : List Ix.Theory.Named.VExpr} + {entries : Ix.Theory.Model.Environment Address} {sctx : SourceCtx} + {catalog : Catalog} {dctx : DecodeCtx} {strings : StringRefs Address} (compileEnv : Ix.CompileM.CompileEnv) (blockEnv : Ix.CompileM.BlockEnv) (snapshot : Ix.CompileM.BlockState) {levelSupport : Ix.Level → Prop} @@ -4156,10 +4151,10 @@ theorem compileExpr_run_ordinary_value (hctx : RefCompileCtxRel (frozenRefCompileCtx compileEnv blockEnv snapshot) sctx catalog dctx) {state : Ix.CompileM.BlockState} {source : Ix.Expr} - {target : Ixon.Expr} {value : Ix.Theory.Named.VExpr} + {target : Ixon.Expr} {value : Ix.Theory.VExpr Address} (hordinary : SupportedOrdinaryExpr levelSupport source) (hstate : FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state) - (hsource : SourceExprRel (uvars := uvars) venv sctx trProj locals source value) + (hsource : SourceExprRel entries sctx strings source value) (href : compileExprRef (frozenRefCompileCtx compileEnv blockEnv snapshot) source = some target) : ∃ root state', @@ -4167,7 +4162,7 @@ theorem compileExpr_run_ordinary_value (Ix.CompileM.compileExpr source) = .ok ((target, root), state') ∧ FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state' ∧ - IxonExprRel (uvars := uvars) venv catalog dctx trProj locals + IxonExprRel entries catalog dctx strings target value := by obtain ⟨root, state', hrun, hstate'⟩ := compileExpr_run_ordinary_refines compileEnv blockEnv snapshot hfree hclosed @@ -4320,9 +4315,8 @@ theorem compileExpr_run_ordinary_arena_refines /-- The strengthened public theorem exposes canonical value preservation and the faithful presentation sidecar in one result. -/ theorem compileExpr_run_ordinary_arena_value - {venv : Ix.Theory.Named.VEnv} {sctx : SourceCtx} {catalog : Catalog} - {dctx : DecodeCtx} {trProj : ProjectionRel} - {uvars : Nat} {locals : List Ix.Theory.Named.VExpr} + {entries : Ix.Theory.Model.Environment Address} {sctx : SourceCtx} + {catalog : Catalog} {dctx : DecodeCtx} {strings : StringRefs Address} (compileEnv : Ix.CompileM.CompileEnv) (blockEnv : Ix.CompileM.BlockEnv) (snapshot : Ix.CompileM.BlockState) {levelSupport : Ix.Level → Prop} @@ -4333,12 +4327,12 @@ theorem compileExpr_run_ordinary_arena_value (hctx : RefCompileCtxRel (frozenRefCompileCtx compileEnv blockEnv snapshot) sctx catalog dctx) {state : Ix.CompileM.BlockState} {source : Ix.Expr} - {target : Ixon.Expr} {value : Ix.Theory.Named.VExpr} + {target : Ixon.Expr} {value : Ix.Theory.VExpr Address} (hordinary : SupportedOrdinaryExpr levelSupport source) (hstate : FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state) (harena : ArenaCacheWF state) (hroom : state.arena.nodes.size + exprArenaCost source < UInt64.size) - (hsource : SourceExprRel (uvars := uvars) venv sctx trProj locals source value) + (hsource : SourceExprRel entries sctx strings source value) (href : compileExprRef (frozenRefCompileCtx compileEnv blockEnv snapshot) source = some target) : ∃ root state', @@ -4348,7 +4342,7 @@ theorem compileExpr_run_ordinary_arena_value FrozenExprStateWF compileEnv blockEnv levelSupport snapshot state' ∧ ArenaCacheWF state' ∧ ArenaCompileRel source root state.arena state'.arena ∧ - IxonExprRel (uvars := uvars) venv catalog dctx trProj locals + IxonExprRel entries catalog dctx strings target value := by obtain ⟨root, state', hrun, hstate', hcache', harena'⟩ := compileExpr_run_ordinary_arena_refines compileEnv blockEnv snapshot hfree diff --git a/Ix/Compile/Verify/CompilePreseed.lean b/Ix/Compile/Verify/CompilePreseed.lean index 5fee400be..7617918dc 100644 --- a/Ix/Compile/Verify/CompilePreseed.lean +++ b/Ix/Compile/Verify/CompilePreseed.lean @@ -1,5 +1,5 @@ import Ix.Compile.Verify.CompileConstantCodec -import Ix.Theory.Named.Verify.QSort +import Ix.Compile.Verify.QSort /-! # Production expression-table preseeding @@ -3914,7 +3914,7 @@ theorem preseedExprTables_of_collect_run_ready_wireWF have hrefPerm : sortedRefs.toList.Perm refs.toList := by dsimp only [sortedRefs] exact Array.perm_iff_toList_perm.mp - (Array.qsort_perm (fun a b : Address => a.cmpBytes b == .lt) + (QSort.qsort_perm (fun a b : Address => a.cmpBytes b == .lt) 0 (refs.size - 1) refs) have hsortedRefsWire : ∀ addr ∈ sortedRefs.toList, addr.hash.size = 32 := by @@ -3972,7 +3972,7 @@ theorem preseedExprTables_of_collect_run_ready_wireWF have hunivPerm : sortedUnivs.toList.Perm keyed.toList := by dsimp only [sortedUnivs] exact Array.perm_iff_toList_perm.mp - (Array.qsort_perm + (QSort.qsort_perm (fun (a b : ByteArray × Ixon.Univ) => Ix.CompileM.byteArrayCmp a.1 b.1 == .lt) 0 (keyed.size - 1) keyed) @@ -4160,7 +4160,7 @@ theorem preseedExprTables_singleton_run_ready_wireWF have hrefPerm : sortedRefs.toList.Perm refs.toList := by dsimp only [sortedRefs] exact Array.perm_iff_toList_perm.mp - (Array.qsort_perm (fun a b : Address => a.cmpBytes b == .lt) + (QSort.qsort_perm (fun a b : Address => a.cmpBytes b == .lt) 0 (refs.size - 1) refs) have hsortedRefsWire : ∀ addr ∈ sortedRefs.toList, addr.hash.size = 32 := by @@ -4225,7 +4225,7 @@ theorem preseedExprTables_singleton_run_ready_wireWF have hunivPerm : sortedUnivs.toList.Perm keyed.toList := by dsimp only [sortedUnivs] exact Array.perm_iff_toList_perm.mp - (Array.qsort_perm + (QSort.qsort_perm (fun (a b : ByteArray × Ixon.Univ) => Ix.CompileM.byteArrayCmp a.1 b.1 == .lt) 0 (keyed.size - 1) keyed) diff --git a/Ix/Compile/Verify/CompileUniv.lean b/Ix/Compile/Verify/CompileUniv.lean index f548ee31b..b8b2f2860 100644 --- a/Ix/Compile/Verify/CompileUniv.lean +++ b/Ix/Compile/Verify/CompileUniv.lean @@ -691,7 +691,7 @@ theorem compileAndInternUnivCanon_run_refines rw [run_bind compileEnv blockEnv canonState _ _, horiginalRun] rfl -/-- The production result therefore has the independent Ix.Theory.Named universe +/-- The production result therefore has the independent set-model universe value assigned to the named source level. -/ theorem compileUniv_run_value (compileEnv : Ix.CompileM.CompileEnv) (blockEnv : Ix.CompileM.BlockEnv) diff --git a/Ix/Compile/Verify/IxonValue.lean b/Ix/Compile/Verify/IxonValue.lean index 41f25cd50..6eb7afb52 100644 --- a/Ix/Compile/Verify/IxonValue.lean +++ b/Ix/Compile/Verify/IxonValue.lean @@ -1,15 +1,26 @@ import Ix.Ixon -import Ix.Theory.Named.Literals -import Ix.Theory.Named.Typing.Env +import Ix.Theory.Expr +import Ix.Theory.Model.Environment +import Ix.Compile.Verify.StdLemmas +import Ix.Compile.Verify.StringLiteral -open Ix.Theory (VLevel) +open Ix.Theory (VLevel VExpr ConstRef) /-! -# Ixon v2 expressions and Ix.Theory.Named values +# Ixon v2 expressions and set-model values This is the first compiler-facing semantic boundary. It interprets an Ixon -expression directly as a Ix.Theory.Named `VExpr`; it does not run Ix.Kernel and does not -use checker acceptance as a specification. +expression directly as a set-model `Ix.Theory.VExpr Address`; it does not run +Ix.Kernel and does not use checker acceptance as a specification. + +The reading follows the conventions of the consistency reader +`Ix.Kernel.Consistency.readExpr?`: constants resolve by content address to a +block reference `ConstRef Address`, projections keep their structure reference +and field index, natural-number literals are native, a let is read by +substitution, and a string literal is the constructor expansion of +`StringRefs.stringLiteral`. Constant occurrences additionally require an +entry in the set-model environment index with matching universe arity; this +is the only use of the environment, so the relation is monotone in it. The relation is table-aware. It resolves universe, reference, mutual-member, sharing, and literal indices against an explicit immutable context. A cyclic @@ -17,21 +28,26 @@ sharing table has no finite derivation. Lambda usage and forall usage/ownership are intentionally absent from the semantic premises: ordinary Lean compilation inhabits `.many`/`.shared`, while v2 annotations remain available to later substructural passes without changing the Lean meaning. + +The former named-specification relation carried a local context and a +declaration-supplied projection relation `trProj uvars locals name field val +out` because that syntax had no projection constructor. The set-model syntax +has `VExpr.proj`, so the local context, universe count, and projection +parameter are gone: downstream theorems that only threaded them through can +drop those indices. -/ namespace Ix.Compile.Verify -open Ix.Theory.Named (VConstant VEnv VExpr) - /-- Immutable semantic views needed to interpret an Ixon expression. -/ structure Catalog where - /-- Resolve a content address to its Theory declaration name. -/ - nameOf : Address → Option Lean.Name + /-- Resolve a content address to its set-model block reference. -/ + resolve : Address → Option (ConstRef Address) /-- Resolve literal content addresses to their committed bytes. -/ blobs : Address → Option ByteArray /-- Resolve canonical constant payloads. This stored view is deliberately - separate from `nameOf`: content integrity and semantic naming are distinct - obligations. -/ + separate from `resolve`: content integrity and semantic reference are + distinct obligations. -/ constants : Address → Option Ixon.Constant := fun _ => none /-- Resolve source-facing named registrations. -/ named : Ix.Name → Option Ixon.Named := fun _ => none @@ -70,124 +86,108 @@ def DecodeCtx.univArgs? (ctx : DecodeCtx) (idxs : Array UInt64) : Option (List VLevel) := idxs.toList.mapM ctx.univ? -/-- Projection interpretation is supplied by the surrounding declaration -model. Its universe/local-context indices match the existing raw Theory -boundary, while this module remains independent of Ix.Kernel. -/ -abbrev ProjectionRel := - Nat → List VExpr → Lean.Name → Nat → VExpr → VExpr → Prop - -namespace ProjectionRel - -/-- Projection-free fixtures use an uninhabited projection relation. -/ -def none : ProjectionRel := fun _ _ _ _ _ _ => False - -end ProjectionRel - -/-- Direct semantic relation from table-indexed Ixon syntax to Ix.Theory.Named +/-- Direct semantic relation from table-indexed Ixon syntax to set-model syntax. This is a raw representation relation: typing and source-kernel well-formedness are separate obligations. -/ -inductive IxonExprRel (venv : VEnv) (catalog : Catalog) (dctx : DecodeCtx) - (trProj : ProjectionRel) {uvars : Nat} : - List VExpr → Ixon.Expr → VExpr → Prop where - | var {locals : List VExpr} {idx : UInt64} : - IxonExprRel venv catalog dctx trProj locals (.var idx) (.bvar idx.toNat) - | sort {locals : List VExpr} {idx : UInt64} {u : VLevel} : +inductive IxonExprRel (entries : Ix.Theory.Model.Environment Address) + (catalog : Catalog) (dctx : DecodeCtx) (strings : StringRefs Address) : + Ixon.Expr → VExpr Address → Prop where + | var {idx : UInt64} : + IxonExprRel entries catalog dctx strings (.var idx) (.bvar idx.toNat) + | sort {idx : UInt64} {u : VLevel} : dctx.univ? idx = some u → - IxonExprRel venv catalog dctx trProj locals (.sort idx) (.sort u) - | ref {locals : List VExpr} {refIdx : UInt64} - {univIdxs : Array UInt64} {addr : Address} {name : Lean.Name} - {ci : VConstant} {us : List VLevel} : + IxonExprRel entries catalog dctx strings (.sort idx) (.sort u) + | ref {refIdx : UInt64} {univIdxs : Array UInt64} {addr : Address} + {ref : ConstRef Address} {entry : Ix.Theory.Model.ConstantEntry Address} + {us : List VLevel} : dctx.refs[refIdx.toNat]? = some addr → - catalog.nameOf addr = some name → - venv.constants name = some ci → + catalog.resolve addr = some ref → + entries ref = some entry → dctx.univArgs? univIdxs = some us → - us.length = ci.uvars → - IxonExprRel venv catalog dctx trProj locals (.ref refIdx univIdxs) - (.const name us) - | recur {locals : List VExpr} {recIdx : UInt64} - {univIdxs : Array UInt64} {addr : Address} {name : Lean.Name} - {ci : VConstant} {us : List VLevel} : + us.length = entry.universes → + IxonExprRel entries catalog dctx strings (.ref refIdx univIdxs) + (.const ref us) + | recur {recIdx : UInt64} {univIdxs : Array UInt64} {addr : Address} + {ref : ConstRef Address} {entry : Ix.Theory.Model.ConstantEntry Address} + {us : List VLevel} : dctx.mutAddrs[recIdx.toNat]? = some addr → - catalog.nameOf addr = some name → - venv.constants name = some ci → + catalog.resolve addr = some ref → + entries ref = some entry → dctx.univArgs? univIdxs = some us → - us.length = ci.uvars → - IxonExprRel venv catalog dctx trProj locals (.recur recIdx univIdxs) - (.const name us) - | app {locals : List VExpr} {fn arg : Ixon.Expr} {fn' arg' : VExpr} : - IxonExprRel venv catalog dctx trProj locals fn fn' → - IxonExprRel venv catalog dctx trProj locals arg arg' → - IxonExprRel venv catalog dctx trProj locals (.app fn arg) (.app fn' arg') - | lam {locals : List VExpr} {uses : Ixon.Uses} {ty body : Ixon.Expr} - {ty' body' : VExpr} : - IxonExprRel venv catalog dctx trProj locals ty ty' → - IxonExprRel venv catalog dctx trProj (ty' :: locals) body body' → - IxonExprRel venv catalog dctx trProj locals (.lam uses ty body) + us.length = entry.universes → + IxonExprRel entries catalog dctx strings (.recur recIdx univIdxs) + (.const ref us) + | app {fn arg : Ixon.Expr} {fn' arg' : VExpr Address} : + IxonExprRel entries catalog dctx strings fn fn' → + IxonExprRel entries catalog dctx strings arg arg' → + IxonExprRel entries catalog dctx strings (.app fn arg) (.app fn' arg') + | lam {uses : Ixon.Uses} {ty body : Ixon.Expr} {ty' body' : VExpr Address} : + IxonExprRel entries catalog dctx strings ty ty' → + IxonExprRel entries catalog dctx strings body body' → + IxonExprRel entries catalog dctx strings (.lam uses ty body) (.lam ty' body') - | all {locals : List VExpr} {uses : Ixon.Uses} {owned : Ixon.Owned} - {ty body : Ixon.Expr} {ty' body' : VExpr} : - IxonExprRel venv catalog dctx trProj locals ty ty' → - IxonExprRel venv catalog dctx trProj (ty' :: locals) body body' → - IxonExprRel venv catalog dctx trProj locals (.all uses owned ty body) + | all {uses : Ixon.Uses} {owned : Ixon.Owned} {ty body : Ixon.Expr} + {ty' body' : VExpr Address} : + IxonExprRel entries catalog dctx strings ty ty' → + IxonExprRel entries catalog dctx strings body body' → + IxonExprRel entries catalog dctx strings (.all uses owned ty body) (.forallE ty' body') - | letE {locals : List VExpr} {nonDep : Bool} {ty val body : Ixon.Expr} - {ty' val' body' : VExpr} : - IxonExprRel venv catalog dctx trProj locals ty ty' → - IxonExprRel venv catalog dctx trProj locals val val' → - IxonExprRel venv catalog dctx trProj (ty' :: locals) body body' → - IxonExprRel venv catalog dctx trProj locals (.letE nonDep ty val body) + | letE {nonDep : Bool} {ty val body : Ixon.Expr} + {ty' val' body' : VExpr Address} : + IxonExprRel entries catalog dctx strings ty ty' → + IxonExprRel entries catalog dctx strings val val' → + IxonExprRel entries catalog dctx strings body body' → + IxonExprRel entries catalog dctx strings (.letE nonDep ty val body) (body'.inst val') - | prj {locals : List VExpr} {typeRefIdx field : UInt64} - {val : Ixon.Expr} {addr : Address} {name : Lean.Name} - {ci : VConstant} {val' out : VExpr} : + | prj {typeRefIdx field : UInt64} {val : Ixon.Expr} {addr : Address} + {ref : ConstRef Address} {entry : Ix.Theory.Model.ConstantEntry Address} + {val' : VExpr Address} : dctx.refs[typeRefIdx.toNat]? = some addr → - catalog.nameOf addr = some name → - venv.constants name = some ci → - IxonExprRel venv catalog dctx trProj locals val val' → - trProj uvars locals name field.toNat val' out → - IxonExprRel venv catalog dctx trProj locals - (.prj typeRefIdx field val) out - | nat {locals : List VExpr} {refIdx : UInt64} {addr : Address} - {bytes : ByteArray} : + catalog.resolve addr = some ref → + entries ref = some entry → + IxonExprRel entries catalog dctx strings val val' → + IxonExprRel entries catalog dctx strings (.prj typeRefIdx field val) + (.proj ref field.toNat val') + | nat {refIdx : UInt64} {addr : Address} {bytes : ByteArray} : dctx.refs[refIdx.toNat]? = some addr → catalog.blobs addr = some bytes → - IxonExprRel venv catalog dctx trProj locals (.nat refIdx) + IxonExprRel entries catalog dctx strings (.nat refIdx) (.natLit (Nat.fromBytesLE bytes.data)) - | str {locals : List VExpr} {refIdx : UInt64} {addr : Address} - {bytes : ByteArray} {value : String} : + | str {refIdx : UInt64} {addr : Address} {bytes : ByteArray} + {value : String} : dctx.refs[refIdx.toNat]? = some addr → catalog.blobs addr = some bytes → String.fromUTF8? bytes = some value → - IxonExprRel venv catalog dctx trProj locals (.str refIdx) - (.trLiteral (.strVal value)) - | share {locals : List VExpr} {idx : UInt64} {expansion : Ixon.Expr} - {value : VExpr} : + IxonExprRel entries catalog dctx strings (.str refIdx) + (strings.stringLiteral value) + | share {idx : UInt64} {expansion : Ixon.Expr} {value : VExpr Address} : dctx.sharing[idx.toNat]? = some expansion → - IxonExprRel venv catalog dctx trProj locals expansion value → - IxonExprRel venv catalog dctx trProj locals (.share idx) value + IxonExprRel entries catalog dctx strings expansion value → + IxonExprRel entries catalog dctx strings (.share idx) value namespace IxonExprRel -/-- The representation relation is monotone in the trusted Theory -environment; only resolved constant-table witnesses are transported. -/ -theorem mono {venv venv' : VEnv} (henv : venv ≤ venv') - {catalog : Catalog} {dctx : DecodeCtx} {trProj : ProjectionRel} - {uvars : Nat} {locals : List VExpr} {expr : Ixon.Expr} {value : VExpr} - (h : IxonExprRel (uvars := uvars) venv catalog dctx trProj locals expr value) : - IxonExprRel (uvars := uvars) venv' catalog dctx trProj locals expr value := by +/-- The representation relation is monotone in the set-model environment +index; only resolved entry witnesses are transported. -/ +theorem mono {entries entries' : Ix.Theory.Model.Environment Address} + (henv : ∀ r e, entries r = some e → entries' r = some e) + {catalog : Catalog} {dctx : DecodeCtx} {strings : StringRefs Address} + {expr : Ixon.Expr} {value : VExpr Address} + (h : IxonExprRel entries catalog dctx strings expr value) : + IxonExprRel entries' catalog dctx strings expr value := by induction h with | var => exact .var | sort hidx => exact .sort hidx - | ref href hname hconst hunivs harity => - exact .ref href hname (henv.constants hconst) hunivs harity - | recur href hname hconst hunivs harity => - exact .recur href hname (henv.constants hconst) hunivs harity + | ref href hres hentry hunivs harity => + exact .ref href hres (henv _ _ hentry) hunivs harity + | recur href hres hentry hunivs harity => + exact .recur href hres (henv _ _ hentry) hunivs harity | app _ _ ihfn iharg => exact .app ihfn iharg | lam _ _ ihty ihbody => exact .lam ihty ihbody | all _ _ ihty ihbody => exact .all ihty ihbody | letE _ _ _ ihty ihval ihbody => exact .letE ihty ihval ihbody - | prj href hname hconst _ hproj ihval => - exact .prj href hname (henv.constants hconst) ihval hproj + | prj href hres hentry _ ihval => + exact .prj href hres (henv _ _ hentry) ihval | nat href hblob => exact .nat href hblob | str href hblob hutf8 => exact .str href hblob hutf8 | share href _ ih => exact .share href ih @@ -245,45 +245,42 @@ theorem eraseBinderModes_eq_self_of_leanFragment {expr : Ixon.Expr} namespace IxonExprRel -/-- Erasing v2 modes preserves every direct Theory value derivation. -/ -theorem eraseModes {venv : VEnv} {catalog : Catalog} {dctx : DecodeCtx} - {trProj : ProjectionRel} {uvars : Nat} {locals : List VExpr} - {expr : Ixon.Expr} {value : VExpr} - (h : IxonExprRel (uvars := uvars) venv catalog dctx trProj locals expr value) : - IxonExprRel (uvars := uvars) venv catalog dctx trProj locals - (eraseBinderModes expr) value := by +/-- Erasing v2 modes preserves every direct set-model value derivation. -/ +theorem eraseModes {entries : Ix.Theory.Model.Environment Address} + {catalog : Catalog} {dctx : DecodeCtx} {strings : StringRefs Address} + {expr : Ixon.Expr} {value : VExpr Address} + (h : IxonExprRel entries catalog dctx strings expr value) : + IxonExprRel entries catalog dctx strings (eraseBinderModes expr) value := by induction h with | var => exact .var | sort hidx => exact .sort hidx - | ref href hname hconst hunivs harity => - exact .ref href hname hconst hunivs harity - | recur href hname hconst hunivs harity => - exact .recur href hname hconst hunivs harity + | ref href hres hentry hunivs harity => + exact .ref href hres hentry hunivs harity + | recur href hres hentry hunivs harity => + exact .recur href hres hentry hunivs harity | app _ _ ihfn iharg => exact .app ihfn iharg | lam _ _ ihty ihbody => exact .lam ihty ihbody | all _ _ ihty ihbody => exact .all ihty ihbody | letE _ _ _ ihty ihval ihbody => exact .letE ihty ihval ihbody - | prj href hname hconst _ hproj ihval => - exact .prj href hname hconst ihval hproj + | prj href hres hentry _ ihval => exact .prj href hres hentry ihval | nat href hblob => exact .nat href hblob | str href hblob hutf8 => exact .str href hblob hutf8 | share href hexp _ => exact .share href hexp /-- A derivation for the conservative erasure can be decorated with the original v2 modes. No semantic evidence is invented or discarded. -/ -theorem of_eraseModes {venv : VEnv} {catalog : Catalog} {dctx : DecodeCtx} - {trProj : ProjectionRel} {uvars : Nat} {locals : List VExpr} - {expr : Ixon.Expr} {value : VExpr} - (h : IxonExprRel (uvars := uvars) venv catalog dctx trProj locals - (eraseBinderModes expr) value) : - IxonExprRel (uvars := uvars) venv catalog dctx trProj locals expr value := by - induction expr generalizing locals value with +theorem of_eraseModes {entries : Ix.Theory.Model.Environment Address} + {catalog : Catalog} {dctx : DecodeCtx} {strings : StringRefs Address} + {expr : Ixon.Expr} {value : VExpr Address} + (h : IxonExprRel entries catalog dctx strings (eraseBinderModes expr) + value) : + IxonExprRel entries catalog dctx strings expr value := by + induction expr generalizing value with | sort | var | ref | recur | str | nat | share => simpa [eraseBinderModes] using h | prj typeIdx field val ih => cases h with - | prj href hname hconst hval hproj => - exact .prj href hname hconst (ih hval) hproj + | prj href hres hentry hval => exact .prj href hres hentry (ih hval) | app fn arg ihfn iharg => cases h with | app hfn harg => exact .app (ihfn hfn) (iharg harg) @@ -299,21 +296,22 @@ theorem of_eraseModes {venv : VEnv} {catalog : Catalog} {dctx : DecodeCtx} exact .letE (ihty hty) (ihval hval) (ihbody hbody) /-- V2 annotations are semantically inert at the Lean compiler boundary. -/ -theorem eraseModes_iff {venv : VEnv} {catalog : Catalog} {dctx : DecodeCtx} - {trProj : ProjectionRel} {uvars : Nat} {locals : List VExpr} - {expr : Ixon.Expr} {value : VExpr} : - IxonExprRel (uvars := uvars) venv catalog dctx trProj locals - (eraseBinderModes expr) value ↔ - IxonExprRel (uvars := uvars) venv catalog dctx trProj locals expr value := +theorem eraseModes_iff {entries : Ix.Theory.Model.Environment Address} + {catalog : Catalog} {dctx : DecodeCtx} {strings : StringRefs Address} + {expr : Ixon.Expr} {value : VExpr Address} : + IxonExprRel entries catalog dctx strings (eraseBinderModes expr) value ↔ + IxonExprRel entries catalog dctx strings expr value := ⟨of_eraseModes, eraseModes⟩ end IxonExprRel -/-- Honest boundary for source-kernel meaning while upstream Ix.Theory.Named -construction remains incomplete. Compiler theorems consume this explicit -witness; no axiom is needed for the structural Ixon conversion itself. -/ -structure KernelSourceWitness where - venv : VEnv - wf : venv.WF +/-- Honest boundary for source-kernel meaning: a set-model realization of the +environment index against which the compiler relations are read. Compiler +theorems consume this explicit witness; no axiom is needed for the structural +Ixon conversion itself. -/ +structure KernelSourceWitness (V : Type v) [Ix.Theory.Model.SetTheory V] where + entries : Ix.Theory.Model.Environment Address + constants : Ix.Theory.Model.Assignment Address V + realizes : Ix.Theory.Model.Realizes constants entries end Ix.Compile.Verify diff --git a/Ix/Compile/Verify/QSort.lean b/Ix/Compile/Verify/QSort.lean new file mode 100644 index 000000000..f91d37c3a --- /dev/null +++ b/Ix/Compile/Verify/QSort.lean @@ -0,0 +1,128 @@ +/- +Adapted for Ix from `Ix/Theory/Named/Verify/QSort.lean` (the named tree's +adaptation of lean4lean's `Lean4Lean/Verify/QSort.lean`): only the size and +permutation half of the verification is kept, under the compiler-verification +namespace. The named tree's Apache license is in `Ix/Theory/Named/LICENSE`. +SPDX-License-Identifier: Apache-2.0 +-/ + +/- +Copyright (c) 2025 Lean FRO, LLC. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Kim Morrison +-/ +module +public import Init.Data.Array.Basic +import all Init.Data.Array.QSort.Basic + +/-! +# Permutation property of `Array.qsort` + +Adapted from the verification in leanprover/lean4#14658 +(tests/elab/grind_qsort.lean on the `qsort_verification` branch). + +The theorems are: +* `size_qsort : (qsort as lt lo hi).size = as.size` +* `qsort_perm : qsort as lt lo hi ~ as` +-/ +namespace Ix.Compile.Verify.QSort + +open Array List Vector + +attribute [grind =] Vector.toArray_perm_iff +attribute [grind =] Vector.perm_toArray_iff + +attribute [grind .] Vector.swap_perm + +attribute [grind .] List.Perm.refl +attribute [grind .] Array.Perm.refl +attribute [grind .] Vector.Perm.refl + +grind_pattern List.Perm.trans => l₁ ~ l₂, l₁ ~ l₃ +grind_pattern Array.Perm.trans => xs ~ ys, xs ~ zs +grind_pattern Vector.Perm.trans => xs ~ ys, xs ~ zs + +/-- Variant of `List.Perm.take` specifying the permutation is constant after `i` elementwise. -/ +theorem List.Perm.take_of_getElem {l₁ l₂ : List α} (h : l₁ ~ l₂) {i : Nat} + (w : ∀ j, i ≤ j → (_ : j < l₁.length) → l₁[j] = l₂[j]'(by have := h.length_eq; omega)) : + l₁.take i ~ l₂.take i := by + apply h.take_of_getElem? + intro j hij + by_cases h_length₁ : j < l₁.length + <;> have h_length₂ := h.length_eq ▸ h_length₁ + <;> grind + +/-- Variant of `List.Perm.drop` specifying the permutation is constant before `i` elementwise. -/ +theorem List.Perm.drop_of_getElem {l₁ l₂ : List α} (h : l₁ ~ l₂) {i : Nat} + (w : ∀ j, j < i → (_ : j < l₁.length) → l₁[j] = l₂[j]'(by have := h.length_eq; omega)) : + l₁.drop i ~ l₂.drop i := by + apply h.drop_of_getElem? + intro j hij + by_cases h_length₁ : j < l₁.length + <;> have h_length₂ := h.length_eq ▸ h_length₁ + <;> grind + +private theorem getElem_mk {l : List α} {i : Nat} (h : i < l.length) : + (Array.mk l)[i]'(by simpa using h) = l[i] := by + rw [← Array.getElem_toList] + +theorem Array.Perm.extract' {xs ys : Array α} (h : xs ~ ys) {lo hi : Nat} + (wlo : ∀ i, i < lo → (_ : i < xs.size) → xs[i] = ys[i]'(by have := h.size_eq; omega)) + (whi : ∀ i, hi ≤ i → (_ : i < xs.size) → xs[i] = ys[i]'(by have := h.size_eq; omega)) : + xs.extract lo hi ~ ys.extract lo hi := by + rcases xs with ⟨xs⟩ + rcases ys with ⟨ys⟩ + simp_all only [Array.perm_iff_toList_perm, List.extract_toArray] + apply List.Perm.take_of_getElem + (w := fun i h₁ h₂ => by + rw [List.getElem_drop, List.getElem_drop, ← getElem_mk, ← getElem_mk] + exact whi (lo + i) (by omega) (by grind)) + apply List.Perm.drop_of_getElem + (w := fun i h₁ h₂ => by + rw [← getElem_mk, ← getElem_mk] + exact wlo i h₁ (by grind)) + simpa using List.perm_iff_toArray_perm.mpr h + +theorem Vector.Perm.extract' {xs ys : Vector α n} (h : xs ~ ys) {lo hi : Nat} + (wlo : ∀ i, i < lo → (_ : i < n) → xs[i] = ys[i]) (whi : ∀ i, hi ≤ i → (_ : i < n) → xs[i] = ys[i]) : + xs.extract lo hi ~ ys.extract lo hi := by + rcases xs with ⟨xs, rfl⟩ + rcases ys with ⟨ys, h⟩ + exact ⟨Array.Perm.extract' h.toArray (by simpa using wlo) (by simpa using whi)⟩ + +attribute [grind .] Array.Perm.extract' +attribute [grind .] Vector.Perm.extract' + +variable (lt : α → α → Bool) (lo hi : Nat) + +@[simp, grind =] public theorem size_qsort (as : Array α) : + (qsort as lt lo hi).size = as.size := by + grind [qsort] + +private theorem qpartition_loop_perm (as : Vector α n) + (hhi : hi < n) (ilo : lo ≤ i) (ik : i ≤ k) (w : k ≤ hi) : + (qpartition.loop lt lo hi hhi pivot as i k).2 ~ as := by + fun_induction qpartition.loop with grind + +@[local grind .] +private theorem qpartition_perm + (as : Vector α n) (w : lo ≤ hi) (hlo : lo < n) (hhi : hi < n) : + (qpartition as lt lo hi).2 ~ as := by + unfold qpartition + refine Vector.Perm.trans (qpartition_loop_perm ..) ?_ + repeat' first + | split + | grind + | refine Vector.Perm.trans (Vector.swap_perm ..) ?_ + +private theorem qsort_sort_perm + (as : Vector α n) (w : lo ≤ hi) (hlo : lo < n) (hhi : hi < n) : + qsort.sort lt as lo hi w hlo hhi ~ as := by + fun_induction qsort.sort with grind + +grind_pattern qsort_sort_perm => qsort.sort lt as lo hi w hlo hhi + +public theorem qsort_perm (as : Array α) : qsort as lt lo hi ~ as := by + grind [qsort] + +end Ix.Compile.Verify.QSort diff --git a/Ix/Compile/Verify/Reference.lean b/Ix/Compile/Verify/Reference.lean index 327e47377..b3cf015ae 100644 --- a/Ix/Compile/Verify/Reference.lean +++ b/Ix/Compile/Verify/Reference.lean @@ -1,6 +1,5 @@ import Ix.Compile.Verify.Catalog import Ix.Environment -import Ix.Theory.Named.Std.Basic open Ix.Theory (VLevel) @@ -156,6 +155,21 @@ def compileExprRef (ctx : RefCompileCtx) : Ix.Expr → Option Ixon.Expr return .prj (← ctx.refIndex typeName) field.toUInt64 (← compileExprRef ctx val) +/-- A successful `List.mapM` into `Option` preserves the input length. -/ +private theorem list_mapM_length_of_eq_some {f : α → Option β} + {xs : List α} {ys : List β} (h : xs.mapM f = some ys) : + ys.length = xs.length := by + induction xs generalizing ys with + | nil => + simp only [List.mapM_nil, pure, Option.some.injEq] at h + subst h + rfl + | cons x xs ih => + simp only [List.mapM_cons, Bind.bind, pure, Option.bind_eq_some_iff, + Option.some.injEq] at h + obtain ⟨y, _, ys', hys', rfl⟩ := h + simp [ih hys'] + /-- A successful `Array.mapM` preserves the input length. -/ private theorem array_mapM_size_of_eq_some {f : α → Option β} {xs : Array α} {ys : Array β} (h : xs.mapM f = some ys) : @@ -163,9 +177,7 @@ private theorem array_mapM_size_of_eq_some {f : α → Option β} have hmapped := congrArg (Option.map Array.toList) h change Array.toList <$> xs.mapM f = Option.map Array.toList (some ys) at hmapped rw [Array.toList_mapM] at hmapped - have hlength := Ix.Theory.Named.List.Forall₂.length_eq - (Ix.Theory.Named.List.mapM_eq_some.mp hmapped) - simpa using hlength.symm + simpa using list_mapM_length_of_eq_some hmapped /-- Reference compilation preserves the three root-spine lengths used by the Ixon expression wire format. -/ diff --git a/Ix/Compile/Verify/SourceValue.lean b/Ix/Compile/Verify/SourceValue.lean index 54051328a..9bae6a1ad 100644 --- a/Ix/Compile/Verify/SourceValue.lean +++ b/Ix/Compile/Verify/SourceValue.lean @@ -1,24 +1,24 @@ import Ix.Compile.Verify.Reference -open Ix.Theory (VLevel) +open Ix.Theory (VLevel VExpr ConstRef) /-! # Source-to-Ixon value preservation This module closes the first expression-level compiler square. `SourceExprRel` -gives a named `Ix.Expr` an independent Ix.Theory.Named meaning. `RefCompileCtxRel` -states that the finite indices chosen by `compileExprRef` point at the same -universes, names, and literal bytes in the target tables. The preservation -theorem then constructs `IxonExprRel` for the exact compiler result. +gives a named `Ix.Expr` an independent set-model meaning, read with the same +conventions as `IxonExprRel`. `RefCompileCtxRel` states that the finite +indices chosen by `compileExprRef` point at the same universes, block +references, and literal bytes in the target tables. The preservation theorem +then constructs `IxonExprRel` for the exact compiler result. -/ namespace Ix.Compile.Verify -open Ix.Theory.Named (VConstant VEnv VExpr) - /-- Independent semantic interpretation choices for named source syntax. -/ structure SourceCtx where - nameOf : Ix.Name → Lean.Name + /-- The set-model block reference denoted by a source constant name. -/ + refOf : Ix.Name → ConstRef Address univ? : Ix.Level → Option VLevel def SourceCtx.univArgs? (ctx : SourceCtx) (levels : Array Ix.Level) : @@ -27,64 +27,61 @@ def SourceCtx.univArgs? (ctx : SourceCtx) (levels : Array Ix.Level) : /-- Raw semantic relation for the ordinary named-Ix compiler input. Hash well-formedness and typing are separate source-witness obligations. -/ -inductive SourceExprRel (venv : VEnv) (sctx : SourceCtx) - (trProj : ProjectionRel) {uvars : Nat} : - List VExpr → Ix.Expr → VExpr → Prop where - | bvar {locals : List VExpr} {idx : Nat} {hash : Address} : - SourceExprRel venv sctx trProj locals (.bvar idx hash) +inductive SourceExprRel (entries : Ix.Theory.Model.Environment Address) + (sctx : SourceCtx) (strings : StringRefs Address) : + Ix.Expr → VExpr Address → Prop where + | bvar {idx : Nat} {hash : Address} : + SourceExprRel entries sctx strings (.bvar idx hash) (.bvar idx.toUInt64.toNat) - | sort {locals : List VExpr} {level : Ix.Level} {hash : Address} - {u : VLevel} : + | sort {level : Ix.Level} {hash : Address} {u : VLevel} : sctx.univ? level = some u → - SourceExprRel venv sctx trProj locals (.sort level hash) (.sort u) - | const {locals : List VExpr} {name : Ix.Name} {levels : Array Ix.Level} - {hash : Address} {ci : VConstant} {us : List VLevel} : - venv.constants (sctx.nameOf name) = some ci → + SourceExprRel entries sctx strings (.sort level hash) (.sort u) + | const {name : Ix.Name} {levels : Array Ix.Level} {hash : Address} + {entry : Ix.Theory.Model.ConstantEntry Address} {us : List VLevel} : + entries (sctx.refOf name) = some entry → sctx.univArgs? levels = some us → - us.length = ci.uvars → - SourceExprRel venv sctx trProj locals (.const name levels hash) - (.const (sctx.nameOf name) us) - | app {locals : List VExpr} {fn arg : Ix.Expr} {hash : Address} - {fn' arg' : VExpr} : - SourceExprRel venv sctx trProj locals fn fn' → - SourceExprRel venv sctx trProj locals arg arg' → - SourceExprRel venv sctx trProj locals (.app fn arg hash) (.app fn' arg') - | lam {locals : List VExpr} {name : Ix.Name} {ty body : Ix.Expr} - {bi : Lean.BinderInfo} {hash : Address} {ty' body' : VExpr} : - SourceExprRel venv sctx trProj locals ty ty' → - SourceExprRel venv sctx trProj (ty' :: locals) body body' → - SourceExprRel venv sctx trProj locals (.lam name ty body bi hash) + us.length = entry.universes → + SourceExprRel entries sctx strings (.const name levels hash) + (.const (sctx.refOf name) us) + | app {fn arg : Ix.Expr} {hash : Address} {fn' arg' : VExpr Address} : + SourceExprRel entries sctx strings fn fn' → + SourceExprRel entries sctx strings arg arg' → + SourceExprRel entries sctx strings (.app fn arg hash) (.app fn' arg') + | lam {name : Ix.Name} {ty body : Ix.Expr} {bi : Lean.BinderInfo} + {hash : Address} {ty' body' : VExpr Address} : + SourceExprRel entries sctx strings ty ty' → + SourceExprRel entries sctx strings body body' → + SourceExprRel entries sctx strings (.lam name ty body bi hash) (.lam ty' body') - | all {locals : List VExpr} {name : Ix.Name} {ty body : Ix.Expr} - {bi : Lean.BinderInfo} {hash : Address} {ty' body' : VExpr} : - SourceExprRel venv sctx trProj locals ty ty' → - SourceExprRel venv sctx trProj (ty' :: locals) body body' → - SourceExprRel venv sctx trProj locals (.forallE name ty body bi hash) + | all {name : Ix.Name} {ty body : Ix.Expr} {bi : Lean.BinderInfo} + {hash : Address} {ty' body' : VExpr Address} : + SourceExprRel entries sctx strings ty ty' → + SourceExprRel entries sctx strings body body' → + SourceExprRel entries sctx strings (.forallE name ty body bi hash) (.forallE ty' body') - | letE {locals : List VExpr} {name : Ix.Name} {ty val body : Ix.Expr} - {nonDep : Bool} {hash : Address} {ty' val' body' : VExpr} : - SourceExprRel venv sctx trProj locals ty ty' → - SourceExprRel venv sctx trProj locals val val' → - SourceExprRel venv sctx trProj (ty' :: locals) body body' → - SourceExprRel venv sctx trProj locals - (.letE name ty val body nonDep hash) (body'.inst val') - | nat {locals : List VExpr} {value : Nat} {hash : Address} : - SourceExprRel venv sctx trProj locals (.lit (.natVal value) hash) + | letE {name : Ix.Name} {ty val body : Ix.Expr} {nonDep : Bool} + {hash : Address} {ty' val' body' : VExpr Address} : + SourceExprRel entries sctx strings ty ty' → + SourceExprRel entries sctx strings val val' → + SourceExprRel entries sctx strings body body' → + SourceExprRel entries sctx strings (.letE name ty val body nonDep hash) + (body'.inst val') + | nat {value : Nat} {hash : Address} : + SourceExprRel entries sctx strings (.lit (.natVal value) hash) (.natLit value) - | str {locals : List VExpr} {value : String} {hash : Address} : - SourceExprRel venv sctx trProj locals (.lit (.strVal value) hash) - (.trLiteral (.strVal value)) - | mdata {locals : List VExpr} {data : Array (Ix.Name × Ix.DataValue)} - {inner : Ix.Expr} {hash : Address} {value : VExpr} : - SourceExprRel venv sctx trProj locals inner value → - SourceExprRel venv sctx trProj locals (.mdata data inner hash) value - | prj {locals : List VExpr} {typeName : Ix.Name} {field : Nat} - {val : Ix.Expr} {hash : Address} {ci : VConstant} {val' out : VExpr} : - venv.constants (sctx.nameOf typeName) = some ci → - SourceExprRel venv sctx trProj locals val val' → - trProj uvars locals (sctx.nameOf typeName) field.toUInt64.toNat val' out → - SourceExprRel venv sctx trProj locals - (.proj typeName field val hash) out + | str {value : String} {hash : Address} : + SourceExprRel entries sctx strings (.lit (.strVal value) hash) + (strings.stringLiteral value) + | mdata {data : Array (Ix.Name × Ix.DataValue)} {inner : Ix.Expr} + {hash : Address} {value : VExpr Address} : + SourceExprRel entries sctx strings inner value → + SourceExprRel entries sctx strings (.mdata data inner hash) value + | prj {typeName : Ix.Name} {field : Nat} {val : Ix.Expr} {hash : Address} + {entry : Ix.Theory.Model.ConstantEntry Address} {val' : VExpr Address} : + entries (sctx.refOf typeName) = some entry → + SourceExprRel entries sctx strings val val' → + SourceExprRel entries sctx strings (.proj typeName field val hash) + (.proj (sctx.refOf typeName) field.toUInt64.toNat val') /-- The reference compiler's index choices resolve to the source meaning in one concrete target context. -/ @@ -100,10 +97,10 @@ structure RefCompileCtxRel (compile : RefCompileCtx) (source : SourceCtx) dctx.univArgs? idxs = some us ref : ∀ {name idx}, compile.refIndex name = some idx → ∃ addr, dctx.refs[idx.toNat]? = some addr ∧ - catalog.nameOf addr = some (source.nameOf name) + catalog.resolve addr = some (source.refOf name) recur : ∀ {name idx}, compile.mutIndex name = some idx → ∃ addr, dctx.mutAddrs[idx.toNat]? = some addr ∧ - catalog.nameOf addr = some (source.nameOf name) + catalog.resolve addr = some (source.refOf name) nat : ∀ {value idx}, compile.literalRef (.natVal value) = some idx → ∃ addr bytes, dctx.refs[idx.toNat]? = some addr ∧ @@ -116,15 +113,15 @@ structure RefCompileCtxRel (compile : RefCompileCtx) (source : SourceCtx) String.fromUTF8? bytes = some value /-- Ordinary reference compilation preserves the independently stated -Ix.Theory.Named value. -/ -theorem compileExprRef_value {venv : VEnv} {sctx : SourceCtx} - {catalog : Catalog} {dctx : DecodeCtx} {compile : RefCompileCtx} - {trProj : ProjectionRel} {uvars : Nat} {locals : List VExpr} - {source : Ix.Expr} {target : Ixon.Expr} {value : VExpr} +set-model value. -/ +theorem compileExprRef_value {entries : Ix.Theory.Model.Environment Address} + {sctx : SourceCtx} {catalog : Catalog} {dctx : DecodeCtx} + {compile : RefCompileCtx} {strings : StringRefs Address} + {source : Ix.Expr} {target : Ixon.Expr} {value : VExpr Address} (hctx : RefCompileCtxRel compile sctx catalog dctx) - (hsource : SourceExprRel (uvars := uvars) venv sctx trProj locals source value) + (hsource : SourceExprRel entries sctx strings source value) (hcompile : compileExprRef compile source = some target) : - IxonExprRel (uvars := uvars) venv catalog dctx trProj locals target value := by + IxonExprRel entries catalog dctx strings target value := by induction hsource generalizing target with | bvar => simp [compileExprRef] at hcompile @@ -134,19 +131,19 @@ theorem compileExprRef_value {venv : VEnv} {sctx : SourceCtx} simp [compileExprRef] at hcompile rcases hcompile with ⟨idx, hidx, rfl⟩ exact .sort (hctx.univ hidx hvalue) - | const hconst hvalues harity => + | const hentry hvalues harity => simp [compileExprRef] at hcompile rcases hcompile with ⟨idxs, hidxs, hcompile⟩ split at hcompile · rename_i idx hmut simp at hcompile subst target - rcases hctx.recur hmut with ⟨addr, href, hname⟩ - exact .recur href hname hconst (hctx.univArgs hidxs hvalues) harity + rcases hctx.recur hmut with ⟨addr, href, hres⟩ + exact .recur href hres hentry (hctx.univArgs hidxs hvalues) harity · simp at hcompile rcases hcompile with ⟨idx, hidx, rfl⟩ - rcases hctx.ref hidx with ⟨addr, href, hname⟩ - exact .ref href hname hconst (hctx.univArgs hidxs hvalues) harity + rcases hctx.ref hidx with ⟨addr, href, hres⟩ + exact .ref href hres hentry (hctx.univArgs hidxs hvalues) harity | app _ _ ihfn iharg => simp [compileExprRef] at hcompile rcases hcompile with ⟨fn, hfn, arg, harg, rfl⟩ @@ -167,18 +164,18 @@ theorem compileExprRef_value {venv : VEnv} {sctx : SourceCtx} simp [compileExprRef] at hcompile rcases hcompile with ⟨idx, hidx, rfl⟩ rcases hctx.nat hidx with ⟨addr, bytes, href, hblob, hvalue⟩ - simpa [hvalue] using IxonExprRel.nat (venv := venv) (trProj := trProj) - href hblob + simpa [hvalue] using + IxonExprRel.nat (entries := entries) (strings := strings) href hblob | str => simp [compileExprRef] at hcompile rcases hcompile with ⟨idx, hidx, rfl⟩ rcases hctx.str hidx with ⟨addr, bytes, href, hblob, hvalue⟩ exact .str href hblob hvalue | mdata _ ih => exact ih hcompile - | prj hconst _ hproj ihval => + | prj hentry _ ihval => simp [compileExprRef] at hcompile rcases hcompile with ⟨typeIdx, htype, val, hval, rfl⟩ - rcases hctx.ref htype with ⟨addr, href, hname⟩ - exact .prj href hname hconst (ihval hval) hproj + rcases hctx.ref htype with ⟨addr, href, hres⟩ + exact .prj href hres hentry (ihval hval) end Ix.Compile.Verify diff --git a/Ix/Compile/Verify/Statements.lean b/Ix/Compile/Verify/Statements.lean index 5e53765a5..5cfc7a4de 100644 --- a/Ix/Compile/Verify/Statements.lean +++ b/Ix/Compile/Verify/Statements.lean @@ -27,7 +27,7 @@ import Ix.Compile.Verify.SourceValue /-! # Public compiler-verification frontier -The first slice exports a direct, table-aware Ixon-to-Ix.Theory.Named relation, the +The first slice exports a direct, table-aware Ixon-to-set-model relation, the constructive theorem that v2 binder modes do not change the related Theory value, a total ordinary-fragment reference compiler, and proofs that its universe values are preserved and its expression outputs inhabit the @@ -158,14 +158,14 @@ digest-key faithfulness, well-addressed v2 expression tables and constants, and refinement proofs for the production reference/universe interning operations through `CompileM.run`. Production `compileUniv` is structurally total and refines the reference compiler while preserving both memo-cache -soundness and the independent Ix.Theory.Named universe value. In surgery-free +soundness and the independent set-model universe value. In surgery-free environments, production `compileExpr` now selects a kernel-visible total path; its recursive structural fragment refines `compileExprRef`, preserves a sound collision-disciplined expression cache, retains flattened App-spine -semantics, and composes with the independent Ix.Theory.Named expression value. A +semantics, and composes with the independent set-model expression value. A frozen-preseed state relation now closes the complete ordinary-expression tree through the actual production dispatcher, including arbitrary-universe -local and external constants, recursive projections, and their Ix.Theory.Named +local and external constants, recursive projections, and their set-model value corollary. The strengthened theorem also exposes a structural `ArenaRel` for the returned metadata root, preserves every warm-cache root under append-only growth, and makes the `UInt64` arena-capacity boundary @@ -209,8 +209,12 @@ all three `MutConst` member tags, and counted mutual blocks complete the production `ConstantInfo` grammar, yielding a top-level constant round trip for every variant in the explicit wire domain, with arbitrary canonical application, lambda, and forall spines in every expression payload. -`KernelSourceWitness` is the sole -upstream source-semantics boundary; later compiler-preservation slices take it -as an explicit hypothesis until Ix.Theory.Named can construct it for a replayed Lean -environment. +All expression values live in the set-model syntax `Ix.Theory.VExpr Address`, +read with the conventions of the consistency reader +`Ix.Kernel.Consistency.readExpr?` and indexed by an `Ix.Theory.Model.Environment`; +no module of this library imports `Ix.Theory.Named` (enforced by +`Audit.NamedFree`). `KernelSourceWitness`, a set-model realization of that +environment index, is the sole upstream source-semantics boundary; later +compiler-preservation slices take it as an explicit hypothesis until the +consistency track constructs it for a replayed Lean environment. -/ diff --git a/Ix/Compile/Verify/StdLemmas.lean b/Ix/Compile/Verify/StdLemmas.lean new file mode 100644 index 000000000..086c8eb93 --- /dev/null +++ b/Ix/Compile/Verify/StdLemmas.lean @@ -0,0 +1,52 @@ +/- +Adapted for Ix from the standard-library-shaped helpers in +`Ix/Theory/Named/Std/Basic.lean` (the named tree's Lean4Lean adaptation; its +Apache license is in `Ix/Theory/Named/LICENSE`). Only the pieces the compiler +proofs rely on are kept. +SPDX-License-Identifier: Apache-2.0 +-/ + +import Batteries.Data.Array.Lemmas +import Std.Data.HashMap.Basic + +/-! +# Standard-library helpers for the compiler proofs + +The compiler proofs unfold `Option`-monadic reference compilers with `simp` +and expect binds against `some` to become existentials; the hash-map caches +use product and list keys. These attributes and instances used to arrive +through the retired named specification's import closure and are now stated +here directly. +-/ + +namespace Ix.Compile.Verify + +attribute [simp] Option.bind_eq_some_iff List.filterMap_cons + +instance [BEq α] [PartialEquivBEq α] [BEq β] [PartialEquivBEq β] : + PartialEquivBEq (α × β) where + symm := by simp [(· == ·)]; grind [BEq.symm] + trans := by simp [(· == ·)]; grind [BEq.trans] + +instance [BEq α] [EquivBEq α] [BEq β] [EquivBEq β] : EquivBEq (α × β) where + rfl := by simp [(· == ·)] + +instance [BEq α] [Hashable α] [LawfulHashable α] [BEq β] [Hashable β] + [LawfulHashable β] : LawfulHashable (α × β) where + hash_eq a b h := by + simp [(· == ·)] at h + simp [hash, LawfulHashable.hash_eq _ _ h.1, LawfulHashable.hash_eq _ _ h.2] + +instance [BEq α] [PartialEquivBEq α] : PartialEquivBEq (List α) where + symm := by + simp [(· == ·)]; intro a b + induction a generalizing b <;> cases b <;> simp [List.beq]; grind [BEq.symm] + trans := by + simp [(· == ·)]; intro a b c + induction a generalizing b c <;> cases b <;> simp [List.beq] + cases c <;> simp [List.beq]; grind [BEq.trans] + +instance [BEq α] [EquivBEq α] : EquivBEq (List α) where + rfl {a} := by simp [(· == ·)]; induction a <;> simp [List.beq, *] + +end Ix.Compile.Verify diff --git a/Ix/Compile/Verify/StringLiteral.lean b/Ix/Compile/Verify/StringLiteral.lean new file mode 100644 index 000000000..3844055b9 --- /dev/null +++ b/Ix/Compile/Verify/StringLiteral.lean @@ -0,0 +1,94 @@ +import Ix.Theory.Expr + +/-! +# String literals as set-model expressions + +The set-model syntax `Ix.Theory.VExpr` has a native natural-number literal but +no string literal. Production `Ix.Kernel` expands a string literal on demand +(`Ix.Kernel.strLitToConstructor` in `Ix/Kernel/Whnf.lean`) into the +application spine + +``` +String.ofList (List.cons.{0} Char (Char.ofNat n₁) (… (List.nil.{0} Char))) +``` + +where each `nᵢ` is the natural-number literal of one code point in source +order. `strLitToConstructor` builds `List.nil.{0} Char` and `List.cons.{0} Char` +once, then `strLitListToConstructor` folds the reversed character list, +prepending `List.cons.{0} Char (Char.ofNat nᵢ)` at each step, so the final list +is in source order. This module states that expansion once, over already +resolved block references, so that the compiler relation (`IxonExprRel`, +`SourceExprRel`) and the kernel-side reading (plan item WP6, extending +`Ix.Kernel.Consistency.readExpr?`) use one definition: reading the kernel's +expansion with `readExpr? resolve` yields exactly `StringRefs.stringLiteral` +for the references obtained by `StringRefs.ofResolve?` on the addresses in +`Ix.Kernel.PrimAddrs` (`charType`, `charOfNat`, `stringOfList`, `listNil`, +`listCons`). + +The definitions are generic in the block identifier `β` so that they can move +into `Ix/Theory/` unchanged when the kernel side adopts them. +-/ + +namespace Ix.Compile.Verify + +open Ix.Theory (VExpr ConstRef) + +universe u v + +/-- Resolved references of the five constants used by string-literal +expansion. -/ +structure StringRefs (β : Type u) where + /-- `Char` -/ + char : ConstRef β + /-- `Char.ofNat` -/ + charOfNat : ConstRef β + /-- `String.ofList` -/ + stringOfList : ConstRef β + /-- `List.nil` -/ + listNil : ConstRef β + /-- `List.cons` -/ + listCons : ConstRef β + +namespace StringRefs + +variable {β : Type u} (refs : StringRefs β) + +/-- `Char`, applied to no universe levels. -/ +def charType : VExpr β := .const refs.char [] + +/-- `Char.ofNat n` with the code point as a native literal. -/ +def charLit (c : Char) : VExpr β := + .app (.const refs.charOfNat []) (.natLit c.toNat) + +/-- `List.nil.{0} Char`. -/ +def nil : VExpr β := .app (.const refs.listNil [.zero]) refs.charType + +/-- `List.cons.{0} Char`, awaiting its head and tail. -/ +def cons : VExpr β := .app (.const refs.listCons [.zero]) refs.charType + +/-- The character list in source order. -/ +def charList : List Char → VExpr β + | [] => refs.nil + | c :: cs => .app (.app refs.cons (refs.charLit c)) (charList cs) + +/-- The set-model reading of a string literal: `String.ofList` applied to the +source-ordered character list. -/ +def stringLiteral (s : String) : VExpr β := + .app (.const refs.stringOfList []) (refs.charList s.toList) + +/-- Resolve the five expansion constants from their content addresses. The +kernel side instantiates `resolve` with its store resolution and the addresses +with the corresponding `Ix.Kernel.PrimAddrs` fields. -/ +def ofResolve? {α : Type v} (resolve : α → Option (ConstRef β)) + (char charOfNat stringOfList listNil listCons : α) : + Option (StringRefs β) := do + return { + char := ← resolve char + charOfNat := ← resolve charOfNat + stringOfList := ← resolve stringOfList + listNil := ← resolve listNil + listCons := ← resolve listCons } + +end StringRefs + +end Ix.Compile.Verify diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index cc2c28a7f..f4ca8949e 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -2,9 +2,11 @@ `Ix.Kernel` is the production Lean checker. `Ix.Kernel.Verify` contains its implementation proofs; `Ix.Compile.Verify` contains the Lean-to-Ixon compiler -proofs. Their named specification and reference implementation lemmas are local -under `Ix.Theory.Named`. Building or checking them requires no external -formalization repository. +proofs, stated against the set-model syntax and environment in +`Ix.Theory.Model` and importing nothing under `Ix.Theory.Named`. The named +specification and reference implementation lemmas of the kernel implementation +proofs are local under `Ix.Theory.Named`. Building or checking them requires no +external formalization repository. Certified acceptance has model-construction and relative consistency theorems, with a concrete set-model construction in the separate Mathlib package. A diff --git a/lakefile.lean b/lakefile.lean index b05967722..504e3d09c 100644 --- a/lakefile.lean +++ b/lakefile.lean @@ -266,13 +266,13 @@ lean_lib IxKernelConsistency where section IxCompileVerify -/- Formal verification of the Lean-to-Ixon compiler. It still targets the -legacy named-specification syntax (`Ix.Theory.Named.VExpr`) that -`IxKernelVerify` uses, so it transitively builds part of that non-required -track until the compiler relation is retargeted to the set model. Required CI -builds it on its own (`lake build IxCompileVerify`). Kept as a separate -non-default library so compiler proofs cannot accidentally inherit checker -acceptance theorems as their specification. -/ +/- Formal verification of the Lean-to-Ixon compiler against the set-model +syntax and environment (`Ix.Theory.Model`), the same endpoint as +`IxKernelConsistency`; it imports nothing under `Ix.Theory.Named` (enforced +by `Ix.Compile.Verify.Audit.NamedFree`). Required CI builds it on its own +with `--wfail`. Kept as a separate non-default library so compiler proofs +cannot accidentally inherit checker acceptance theorems as their +specification. -/ lean_lib IxCompileVerify where globs := #[.submodules `Ix.Compile.Verify] From 922ff229d38e6f7de6ef06c235f349f4a71e6d56 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 15 Sep 2026 10:11:41 -0400 Subject: [PATCH 52/63] Build compiler proofs strictly in every gate The compiler verification library no longer imports the named specification, so it builds warning-free. Include it in build-all, build it with --wfail in check-kernel and both CI workflows, and keep the legacy named-specification build as a separate non-strict local step. --- .github/workflows/ci.yml | 2 +- .github/workflows/merge-tests.yml | 2 +- lakefile.lean | 11 ++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 507f3481a..b0fc0361b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,7 +107,7 @@ jobs: auto-config: false use-github-cache: false - name: Check compiler implementation proofs - run: lake build IxCompileVerify + run: lake build --wfail IxCompileVerify - name: Check direct kernel consistency refinement run: lake build --wfail IxKernelConsistency - name: Check consistency model and exact foundation manifest diff --git a/.github/workflows/merge-tests.yml b/.github/workflows/merge-tests.yml index 9bb4d38dc..2c2e5ecc3 100644 --- a/.github/workflows/merge-tests.yml +++ b/.github/workflows/merge-tests.yml @@ -152,7 +152,7 @@ jobs: # named-spec-verification workflow builds them on demand. - name: Build Ix compiler verification if: ${{ matrix.kind == 'tc' }} - run: lake build IxCompileVerify + run: lake build --wfail IxCompileVerify - name: Check Ix.Kernel consistency refinement if: ${{ matrix.kind == 'tc' }} run: lake build --wfail IxKernelConsistency diff --git a/lakefile.lean b/lakefile.lean index 504e3d09c..789c03081 100644 --- a/lakefile.lean +++ b/lakefile.lean @@ -337,11 +337,11 @@ script "build-all" (args) := do let exeNames := pkg.configTargets LeanExe.configKind |>.map (·.name.toString) -- The legacy named specification and the implementation proofs stated -- against it retain an audited frontier; the non-required - -- named-spec-verification workflow builds them without `--wfail`, and - -- required CI builds `IxCompileVerify` on its own. The set model, direct - -- consistency roots, and certified adapters are checked strictly. + -- named-spec-verification workflow builds them without `--wfail`. The set + -- model, direct consistency roots, compiler proofs, and certified adapters + -- are checked strictly. let allNames := (libNames ++ exeNames |>.toList).filter fun name => - name != "IxKernelVerify" && name != "IxCompileVerify" && name != "IxTheoryNamed" + name != "IxKernelVerify" && name != "IxTheoryNamed" for name in allNames do IO.println s!"Building: {name}" let child ← IO.Process.spawn { @@ -489,7 +489,8 @@ script "check-kernel" (args) := do unless args.isEmpty || args == ["--with-model"] do IO.eprintln "usage: lake run check-kernel [--with-model]" return 2 - run "lake" #["build", "IxKernelVerify", "IxCompileVerify"] + run "lake" #["build", "--wfail", "IxCompileVerify"] + run "lake" #["build", "IxKernelVerify"] run "lake" #["build", "--wfail", "IxKernelConsistency"] run "lake" #["run", "check-theory"] run "lake" #["run", "check-certified"] From 32863ec09bd2cf98db6c014c670d8434be77a16f Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 15 Sep 2026 10:20:39 -0400 Subject: [PATCH 53/63] Bundle run assumptions and the checker state invariant RunAssumptions (source, cfg, domain): checked source ownership, verifyHashes = true, expression and universe collision freedom over the run's finite inventory, and size bounds for expressions and universes, with derived collision, faithfulness, RunSupport, ConversionData, and StandaloneConversionData resources. CheckerInvariant (resolve, anchor, entries, source, catalog, locals, context, bounds, state): SourceCacheInvariant (ownership, loader, blocks, coherence, source and catalog agreement), SynthesisCacheHistory, BetaCacheHistory, LocalStateInvariant, LocalContextReading, the context's SynthesisContext origin, and ReductionCacheSemantics (WHNF partitions, DefEq partitions, equivalence-manager edges, unfold cache, is-prop cache stated over ConversionClaim/TypingClaim; empty maps satisfy them). Proved: initial at TcState.newLazyAnon under the run assumptions; preservation through getConst on both outcomes, inferKey/whnfKey/ defEqCtxKey, openBinder/openLet, scope exit and withLctxScope, cache clearing, per-item reset, and policy changes; sort and free-variable inference restated as invariant preservation with model typing. --- Ix/Kernel/Verify/Consistency.lean | 12 + Ix/Kernel/Verify/Consistency/Audit.lean | 64 ++ Ix/Kernel/Verify/Consistency/Invariant.lean | 903 ++++++++++++++++++ .../Verify/Consistency/RunAssumptions.lean | 135 +++ 4 files changed, 1114 insertions(+) create mode 100644 Ix/Kernel/Verify/Consistency/Invariant.lean create mode 100644 Ix/Kernel/Verify/Consistency/RunAssumptions.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 313bb2e8f..b24c57a4a 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -82,6 +82,8 @@ import Ix.Kernel.Verify.Consistency.SourceCache import Ix.Kernel.Verify.Consistency.Production import Ix.Kernel.Verify.Consistency.Dependencies import Ix.Kernel.Verify.Consistency.Environment +import Ix.Kernel.Verify.Consistency.RunAssumptions +import Ix.Kernel.Verify.Consistency.Invariant import Ix.Kernel.Verify.Consistency.Audit /-! @@ -336,5 +338,15 @@ follows from the source binding and history without fresh cache witnesses. Model admission, mutual-member interpretations, general semantic cache invariants, finite execution resources, and automatic trace construction remain obligations. +One run-level record now collects the finite source check, hash verification, +and the collision and size resources over a run's finite syntax inventory. One +checker-state invariant bundles ownership, coherence, source and catalog +agreement, both cache histories, the local state and its reading, the +context's synthesis origin, and semantic agreement of the remaining reduction +memos. It holds at the driver's initial state, survives lookup on both +outcomes, key computation, binder and let opening, scope exit, cache clearing, +per-item reset, and policy changes, and sort and free-variable inference are +restated as its preservation. Re-expressing the other supported branches and +deriving the atomic run records from it remain open. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 5f720f5d7..bd55630d2 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -18,6 +18,7 @@ import Ix.Kernel.Verify.Consistency.LetCache import Ix.Kernel.Verify.Consistency.BetaSourceInference import Ix.Kernel.Verify.Consistency.BetaExposureConstruction import Ix.Kernel.Verify.Consistency.BetaHistoryInference +import Ix.Kernel.Verify.Consistency.Invariant import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -1444,6 +1445,55 @@ private def modelLocalStateRoots : Array Lean.Name := #[ ``LambdaInferenceTrace.absent ] +/-- Finite inventories and the DefEq partition index. -/ +private def wp1PureRoots : Array Lean.Name := #[``RunDomain, ``DefEqCachePartition] + +/-- Run assumptions, their derived resources, and the semantic memo predicates +with their map transports. -/ +private def wp1RunRoots : Array Lean.Name := #[ + ``RunAssumptions, ``RunAssumptions.ownership, ``RunAssumptions.collisionFree, + ``RunAssumptions.keyCollisionFree, ``RunAssumptions.addrFaithful, + ``RunAssumptions.universeCollisionFree, ``RunAssumptions.universeAddrFaithful, + ``RunAssumptions.supportCollisionFree, ``RunAssumptions.conversionData, ``RunAssumptions.bound, + ``WhnfCacheSemantics, ``WhnfCacheSemantics.ofMaps, ``WhnfCacheSemantics.ofEmpty, + ``DefEqCacheSemantics, ``DefEqCacheSemantics.ofMaps, ``DefEqCacheSemantics.ofEmpty, + ``EqKeyConversion, ``EquivManagerSemantics, ``EquivManagerSemantics.empty, + ``UnfoldCacheSemantics.ofEmpty, ``IsPropCacheSemantics, ``IsPropCacheSemantics.ofMap, + ``IsPropCacheSemantics.ofEmpty, ``ReductionCacheSemantics, ``ReductionCacheSemantics.ofMapsManager, + ``ReductionCacheSemantics.ofMaps, ``ReductionCacheSemantics.ofEmpty, ``ReductionCacheSemantics.clear, + ``reset_eq +] + +private def wp1PropextRoots : Array Lean.Name := #[ + ``DefEqCachePartition.cache, ``UnfoldCacheSemantics, ``UnfoldCacheSemantics.ofMap +] + +/-- Context-digest key computations reach expression construction only. -/ +private def wp1KeyRoots : Array Lean.Name := #[ + ``ctxAddrForLbr_state, ``inferKey_state, ``whnfKey_state, ``defEqCtxKey_state +] + +/-- The bundled checker invariant and its preservation through lookup, keys, +opening, scope exit, clearing, reset, policy, and the sort and local leaves. -/ +private def wp1InvariantRoots : Array Lean.Name := #[ + ``CheckerInvariant, ``CheckerInvariant.sourceState, ``CheckerInvariant.inference, + ``CheckerInvariant.coherent, ``CheckerInvariant.installed, ``CheckerInvariant.blocks, + ``CheckerInvariant.ownership, ``CheckerInvariant.owned, ``CheckerInvariant.agreement, + ``CheckerInvariant.cache, ``CheckerInvariant.inferenceHistory, ``CheckerInvariant.ofMaps, + ``CheckerInvariant.ofCtxAddrCache, ``CheckerInvariant.inferKey, ``CheckerInvariant.whnfKey, + ``CheckerInvariant.defEqCtxKey, ``CheckerInvariant.policy, ``CheckerInvariant.clearReductionCaches, + ``CheckerInvariant.reset, ``CheckerInvariant.getConst, ``CheckerInvariant.openBinder, + ``CheckerInvariant.openLet, ``CheckerInvariant.exitScope, ``CheckerInvariant.withLctxScope, + ``CheckerInvariant.inferSort, ``CheckerInvariant.inferFVar, ``getConst_ingressFrame, + ``InferenceStateInvariant.openLet, ``RunAssumptions.standaloneConversionData +] + +/-- The driver's initial state reaches the production loader's name construction. -/ +private def wp1InitialRoots : Array Lean.Name := #[ + ``CheckerInvariant.initial, ``CheckerInvariant.initialLoopState, ``ReductionCacheSemantics.initial, + ``RunAssumptions.initialState, ``RunAssumptions.initialLoopState +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -1653,6 +1703,20 @@ def roots : Array RootAllowance := #[ forbiddenDependencies := forbiddenProduction } ] ++ #[{ root := ``extend_atomic_definition, standardAxioms := standard }] + ++ wp1PureRoots.map (fun root => { root, forbiddenDependencies := forbiddenProduction }) + ++ wp1RunRoots.map (fun root => { + root, standardAxioms := standard, forbiddenDependencies := forbiddenProduction }) + ++ wp1PropextRoots.map (fun root => { + root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction }) + ++ wp1KeyRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative], + forbiddenDependencies := forbiddenProduction }) + ++ wp1InvariantRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], + forbiddenDependencies := forbiddenProduction }) + ++ wp1InitialRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative, nameNative], + forbiddenDependencies := forbiddenProduction }) run_cmd Kernel.Verify.Audit.check roots diff --git a/Ix/Kernel/Verify/Consistency/Invariant.lean b/Ix/Kernel/Verify/Consistency/Invariant.lean new file mode 100644 index 000000000..0aa9db9fd --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Invariant.lean @@ -0,0 +1,903 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.IngressState +import Ix.Kernel.Verify.Consistency.RunAssumptions +import Ix.Kernel.Verify.Consistency.SourceCache +import Ix.Kernel.Verify.Consistency.SynthesisCacheHistory +import Ix.Kernel.Verify.Consistency.BetaCacheHistory +import Ix.Kernel.Verify.Consistency.LocalStateReading +import Ix.Kernel.Verify.Consistency.SortCache + +/-! +# The checker state invariant + +One record bundles the state facts that the refinement currently threads +separately: source ownership, the installed verified loader, block +registration, intern coherence, standalone source agreement, catalog cache +agreement, both inference-cache histories with their retained synthesis +checks, the complete WHNF history, the structural local state, the local +context reading, and the synthesis origin of the current context. Five new +`Prop` fields state semantic agreement of the WHNF partitions, the two DefEq +caches, the equivalence manager, the unfold cache, and the is-prop cache in +terms of `ConversionClaim`/`TypingClaim`; empty maps satisfy them trivially. +The invariant holds at the driver's initial state under `RunAssumptions` and +is preserved by lookup on both outcomes, key computation, binder and let +opening, scope exit, cache clearing, per-item reset, and policy changes. +Sort and free-variable inference are restated as invariant preservation. +Not recorded: let-value substitution agreement and a reading of every loaded +declaration into the interface, which have no definition yet, and policy-flag +consistency. Per-lookup conversion data, cache key collision data, and binder +walker data remain explicit premises, each an instance of `RunAssumptions`. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-! ### Semantic agreement of the reduction caches -/ + +/-- Every stored WHNF result is convertible to, and retains every type of, a +source expression with the key's address, read in some local context. -/ +def WhnfCacheSemantics {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (state : TcState .anon) : Prop := + ∀ (partition : WhnfCachePartition) (key : Address × Address) (result : KExpr .anon), + (partition.cache state)[key]? = some result → + ∃ (locals : List FVarId) (context : Model.Context β) (source : KExpr .anon) + (term target : AExpr β), + source.addr = key.1 ∧ + readScopedExpr? resolve locals source = some term.erase ∧ + readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ + ∀ type, TypingClaim.{u,v} entries context term type → + TypingClaim.{u,v} entries context target type + +theorem WhnfCacheSemantics.ofMaps {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {before after : TcState .anon} + (valid : WhnfCacheSemantics.{u,v} resolve entries before) + (preserved : ∀ partition : WhnfCachePartition, partition.cache after = partition.cache before) : + WhnfCacheSemantics.{u,v} resolve entries after := by + intro partition key result stored + rw [preserved partition] at stored + exact valid partition key result stored + +theorem WhnfCacheSemantics.ofEmpty {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) {state : TcState .anon} + (empty : ∀ partition : WhnfCachePartition, partition.cache state = ∅) : + WhnfCacheSemantics.{u,v} resolve entries state := by + intro partition key result stored + rw [empty partition] at stored + simp at stored + +/-- The two definitional-equality caches. -/ +inductive DefEqCachePartition where + | full | cheap + deriving DecidableEq + +def DefEqCachePartition.cache : DefEqCachePartition → TcState .anon → + Std.HashMap (Address × Address × Address) Bool + | .full, state => state.env.defEqCache + | .cheap, state => state.env.defEqCheapCache + +/-- Every positive DefEq entry records two expressions with the key's +addresses whose readings are convertible in some local context. Negative +entries carry no claim. -/ +def DefEqCacheSemantics {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (state : TcState .anon) : Prop := + ∀ (partition : DefEqCachePartition) (key : Address × Address × Address), + (partition.cache state)[key]? = some true → + ∃ (locals : List FVarId) (context : Model.Context β) (left right : KExpr .anon) + (a b : AExpr β), + left.addr = key.1 ∧ right.addr = key.2.1 ∧ + readScopedExpr? resolve locals left = some a.erase ∧ + readScopedExpr? resolve locals right = some b.erase ∧ + ConversionClaim.{u,v} entries context a b + +theorem DefEqCacheSemantics.ofMaps {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {before after : TcState .anon} + (valid : DefEqCacheSemantics.{u,v} resolve entries before) + (preserved : ∀ partition : DefEqCachePartition, partition.cache after = partition.cache before) : + DefEqCacheSemantics.{u,v} resolve entries after := by + intro partition key stored + rw [preserved partition] at stored + exact valid partition key stored + +theorem DefEqCacheSemantics.ofEmpty {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) {state : TcState .anon} + (empty : ∀ partition : DefEqCachePartition, partition.cache state = ∅) : + DefEqCacheSemantics.{u,v} resolve entries state := by + intro partition key stored + rw [empty partition] at stored + simp at stored + +/-- A justified union-find edge joins two keys of the same context scope whose +expressions read to convertible terms in some local context. -/ +def EqKeyConversion {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (left right : EqKey) : Prop := + left.ctxAddr = right.ctxAddr ∧ left.lbr = right.lbr ∧ + ∃ (locals : List FVarId) (context : Model.Context β) (a b : KExpr .anon) (ta tb : AExpr β), + a.addr = left.exprAddr ∧ b.addr = right.exprAddr ∧ + a.lbr = left.exprLbr ∧ b.lbr = right.exprLbr ∧ + readScopedExpr? resolve locals a = some ta.erase ∧ + readScopedExpr? resolve locals b = some tb.erase ∧ + ConversionClaim.{u,v} entries context ta tb + +/-- Every parent link of the production union-find forest is a justified edge. +This is the `edge` field of the named track's `EquivManager.WF`, restated here +so the library does not import that module. -/ +def EquivManagerSemantics {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (manager : EquivManager) : Prop := + ∀ node, node < manager.parent.size → + EqKeyConversion.{u,v} resolve entries manager.nodeToKey[node]! manager.nodeToKey[manager.parent[node]!]! + +theorem EquivManagerSemantics.empty {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} : EquivManagerSemantics.{u,v} resolve entries {} := + fun _ bound => (Nat.not_lt_zero _ bound).elim + +/-- Every unfold entry is the universe instantiation of an admitted body at +the head constant whose address keys it. -/ +def UnfoldCacheSemantics {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (state : TcState .anon) : Prop := + ∀ (addr : Address) (value : KExpr .anon), state.env.unfoldCache[addr]? = some value → + ∃ (id : KId .anon) (arguments : Array (KUniv .anon)) (info : ExprInfo .anon) + (ref : ConstRef β) (entry : ConstantEntry β) (body : AExpr β), + (KExpr.const id arguments info).addr = addr ∧ + resolve id.addr = some ref ∧ entries ref = some entry ∧ entry.body = some body ∧ + entry.universes = arguments.size ∧ + readExpr? resolve value = some (body.instL (arguments.toList.map readLevel)).erase + +theorem UnfoldCacheSemantics.ofMap {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {before after : TcState .anon} + (valid : UnfoldCacheSemantics resolve entries before) + (preserved : after.env.unfoldCache = before.env.unfoldCache) : + UnfoldCacheSemantics resolve entries after := by + intro addr value stored + rw [preserved] at stored + exact valid addr value stored + +theorem UnfoldCacheSemantics.ofEmpty {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) {state : TcState .anon} (empty : state.env.unfoldCache = ∅) : + UnfoldCacheSemantics resolve entries state := by + intro addr value stored + rw [empty] at stored + simp at stored + +/-- Every positive is-prop entry records a type with the key's address that +is a proposition in some local context. Negative entries carry no claim. -/ +def IsPropCacheSemantics {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (state : TcState .anon) : Prop := + ∀ key : Address × Address, state.env.isPropCache[key]? = some true → + ∃ (locals : List FVarId) (context : Model.Context β) (source : KExpr .anon) (term : AExpr β), + source.addr = key.1 ∧ readScopedExpr? resolve locals source = some term.erase ∧ + TypingClaim.{u,v} entries context term (.sort .zero) + +theorem IsPropCacheSemantics.ofMap {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {before after : TcState .anon} + (valid : IsPropCacheSemantics.{u,v} resolve entries before) + (preserved : after.env.isPropCache = before.env.isPropCache) : + IsPropCacheSemantics.{u,v} resolve entries after := by + intro key stored + rw [preserved] at stored + exact valid key stored + +theorem IsPropCacheSemantics.ofEmpty {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) {state : TcState .anon} (empty : state.env.isPropCache = ∅) : + IsPropCacheSemantics.{u,v} resolve entries state := by + intro key stored + rw [empty] at stored + simp at stored + +/-- Semantic agreement of every reduction memo not covered by an execution history. -/ +structure ReductionCacheSemantics {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (state : TcState .anon) : Prop where + whnf : WhnfCacheSemantics.{u,v} resolve entries state + defEq : DefEqCacheSemantics.{u,v} resolve entries state + equivalence : EquivManagerSemantics.{u,v} resolve entries state.equivManager + unfold : UnfoldCacheSemantics resolve entries state + isProp : IsPropCacheSemantics.{u,v} resolve entries state + +namespace ReductionCacheSemantics + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + +theorem ofMapsManager {before after : TcState .anon} + (valid : ReductionCacheSemantics.{u,v} resolve entries before) + (whnf : ∀ partition : WhnfCachePartition, partition.cache after = partition.cache before) + (defEq : ∀ partition : DefEqCachePartition, partition.cache after = partition.cache before) + (manager : EquivManagerSemantics.{u,v} resolve entries after.equivManager) + (unfold : after.env.unfoldCache = before.env.unfoldCache) + (isProp : after.env.isPropCache = before.env.isPropCache) : + ReductionCacheSemantics.{u,v} resolve entries after := + ⟨valid.whnf.ofMaps whnf, valid.defEq.ofMaps defEq, manager, valid.unfold.ofMap unfold, + valid.isProp.ofMap isProp⟩ + +theorem ofMaps {before after : TcState .anon} + (valid : ReductionCacheSemantics.{u,v} resolve entries before) + (whnf : ∀ partition : WhnfCachePartition, partition.cache after = partition.cache before) + (defEq : ∀ partition : DefEqCachePartition, partition.cache after = partition.cache before) + (manager : after.equivManager = before.equivManager) + (unfold : after.env.unfoldCache = before.env.unfoldCache) + (isProp : after.env.isPropCache = before.env.isPropCache) : + ReductionCacheSemantics.{u,v} resolve entries after := + valid.ofMapsManager whnf defEq (by rw [manager]; exact valid.equivalence) unfold isProp + +/-- Empty memos and any well-formed manager satisfy the agreement. -/ +theorem ofEmpty {state : TcState .anon} + (whnf : ∀ partition : WhnfCachePartition, partition.cache state = ∅) + (defEq : ∀ partition : DefEqCachePartition, partition.cache state = ∅) + (manager : EquivManagerSemantics.{u,v} resolve entries state.equivManager) + (unfold : state.env.unfoldCache = ∅) (isProp : state.env.isPropCache = ∅) : + ReductionCacheSemantics.{u,v} resolve entries state := + ⟨.ofEmpty resolve entries whnf, .ofEmpty resolve entries defEq, manager, + .ofEmpty resolve entries unfold, .ofEmpty resolve entries isProp⟩ + +theorem initial (source : Ixon.Env) : + ReductionCacheSemantics.{u,v} resolve entries (TcState.newLazyAnon source) := + ofEmpty (fun partition => by cases partition <;> rfl) (fun partition => by cases partition <;> rfl) + EquivManagerSemantics.empty rfl rfl + +/-- Clearing empties every memo and retains the equivalence manager. -/ +theorem clear {state : TcState .anon} (valid : ReductionCacheSemantics.{u,v} resolve entries state) : + ReductionCacheSemantics.{u,v} resolve entries {state with env := state.env.clearReductionCaches} := + ofEmpty (fun partition => by cases partition <;> rfl) (fun partition => by cases partition <;> rfl) + valid.equivalence rfl rfl + +end ReductionCacheSemantics + +/-! ### Operational state equations -/ + +/-- Context-digest computation changes only its memo table. -/ +theorem ctxAddrForLbr_state {lbr : UInt64} {before after : TcState .anon} {addr : Address} + (run : TcM.ctxAddrForLbr lbr before = .ok addr after) : + after = {before with ctxAddrCache := after.ctxAddrCache} := by + unfold TcM.ctxAddrForLbr at run + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ at run + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] at run + dsimp only at run + by_cases fast : (lbr == 0 || before.ctx.isEmpty) = true + · rw [if_pos fast] at run + cases run + rfl + · rw [if_neg fast] at run + cases cached : before.ctxAddrCache[(before.ctxId, lbr)]? <;> + rw [cached] at run <;> cases run <;> rfl + +theorem inferKey_state {term : KExpr .anon} {before after : TcState .anon} {key : Address × Address} + (run : TcM.inferKey term before = .ok key after) : + after = {before with ctxAddrCache := after.ctxAddrCache} := by + unfold TcM.inferKey at run + change EStateM.bind (TcM.ctxAddrForLbr term.lbr) _ before = _ at run + rw [EStateM.bind] at run + cases digest : TcM.ctxAddrForLbr term.lbr before with + | error err failed => rw [digest] at run; contradiction + | ok addr keyed => + rw [digest] at run + cases run + exact ctxAddrForLbr_state digest + +theorem whnfKey_state {term : KExpr .anon} {before after : TcState .anon} {key : Address × Address} + (run : TcM.whnfKey term before = .ok key after) : + after = {before with ctxAddrCache := after.ctxAddrCache} := by + unfold TcM.whnfKey at run + change EStateM.bind (TcM.ctxAddrForLbr term.lbr) _ before = _ at run + rw [EStateM.bind] at run + cases digest : TcM.ctxAddrForLbr term.lbr before with + | error err failed => rw [digest] at run; contradiction + | ok addr keyed => + rw [digest] at run + cases run + exact ctxAddrForLbr_state digest + +theorem defEqCtxKey_state {left right : KExpr .anon} {before after : TcState .anon} {addr : Address} + (run : TcM.defEqCtxKey left right before = .ok addr after) : + after = {before with ctxAddrCache := after.ctxAddrCache} := + ctxAddrForLbr_state run + +/-- The exact per-item reset. -/ +theorem reset_eq (before : TcState .anon) : + TcM.reset before = .ok () {before with + ctx := #[], letVals := #[], numLetBindings := 0, ctxId := emptyCtxAddr, ctxIdStack := #[], + equivManager := {}, inferOnly := false, inNativeReduce := false, cheapRecursionDepth := 0, + eagerReduce := false, defEqDepth := 0, defEqPeak := 0, dispatchDepth := 0, + recFuel := before.fuelBudget, ctxAddrCache := {}, lctx := {}} := rfl + +private theorem lazyIngressAddr_ingressFrame {before : TcState .anon} {addr : Address} + (source : Ixon.Env) (verify : Bool) + (installed : before.lazyFault = some (fun address => ingressAnonAddrShallow source address verify)) : + match TcM.lazyIngressAddr addr before with + | .ok _ after | .error _ after => KEnv.IngressFrame before.env after.env := by + unfold TcM.lazyIngressAddr + rw [installed] + dsimp only + by_cases faulted : before.faultedAddrs.contains addr = true + · rw [if_pos faulted] + exact .refl _ + · rw [if_neg faulted] + have frame := IngressM.FramesState.ingressAnonAddrShallow source addr verify before.env + cases run : ingressAnonAddrShallow source addr verify before.env <;> + rw [run] at frame <;> exact frame + +private theorem tryGetConst_ingressFrame {before : TcState .anon} {id : KId .anon} + (source : Ixon.Env) (verify : Bool) + (installed : before.lazyFault = some (fun address => ingressAnonAddrShallow source address verify)) : + match TcM.tryGetConst id before with + | .ok _ after | .error _ after => KEnv.IngressFrame before.env after.env := by + unfold TcM.tryGetConst + change (match (EStateM.bind (get : TcM .anon (TcState .anon)) _ : + TcM .anon (Option (KConst .anon))) before with + | .ok _ after | .error _ after => KEnv.IngressFrame before.env after.env) + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + dsimp only + cases before.env.get? id with + | some concrete => exact .refl _ + | none => + change (match (EStateM.bind (TcM.lazyIngressAddr id.addr) _ : + TcM .anon (Option (KConst .anon))) before with + | .ok _ after | .error _ after => KEnv.IngressFrame before.env after.env) + have preserved := lazyIngressAddr_ingressFrame (addr := id.addr) source verify installed + cases fault : TcM.lazyIngressAddr id.addr before with + | error err after => + rw [EStateM.bind, fault] + simpa only [fault] using preserved + | ok value after => + rw [fault] at preserved + rw [EStateM.bind, fault] + change (match (EStateM.bind (get : TcM .anon (TcState .anon)) _ : + TcM .anon (Option (KConst .anon))) after with + | .ok _ state | .error _ state => KEnv.IngressFrame before.env state.env) + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) after = .ok after after from rfl] + dsimp only + cases after.env.get? id with + | some concrete => exact preserved + | none => cases before.lazyFault.isSome <;> exact preserved + +/-- Lookup through the installed production loader changes only the +ingress-owned environment fields on both outcomes. -/ +theorem getConst_ingressFrame {before : TcState .anon} {id : KId .anon} + (source : Ixon.Env) (verify : Bool) + (installed : before.lazyFault = some (fun address => ingressAnonAddrShallow source address verify)) : + match TcM.getConst id before with + | .ok _ after | .error _ after => KEnv.IngressFrame before.env after.env := by + unfold TcM.getConst + change (match (EStateM.bind (TcM.tryGetConst id) _ : TcM .anon (KConst .anon)) before with + | .ok _ after | .error _ after => KEnv.IngressFrame before.env after.env) + have preserved := tryGetConst_ingressFrame (id := id) source verify installed + cases tried : TcM.tryGetConst id before with + | error err after => + rw [EStateM.bind, tried] + simpa only [tried] using preserved + | ok optional after => + rw [tried] at preserved + rw [EStateM.bind, tried] + cases optional <;> exact preserved + +/-- Let opening retains ownership, block registration, and coherence exactly +as binder opening does; the value is stored, not walked. -/ +theorem InferenceStateInvariant.openLet {source : Ixon.Env} {before after : TcState .anon} + {name : Mode.anon.F Name} {domain value body opened : KExpr .anon} {fresh : FVarId} + (valid : InferenceStateInvariant source before) (data : BinderOpeningData before body) + (run : TcM.openLet name domain value body before = .ok (opened, fresh) after) : + InferenceStateInvariant source after := by + have nameUnit : name = () := Subsingleton.elim _ _ + subst name + have interned : (before.env.intern.internExpr + (KExpr.mkFVar ⟨before.env.nextFVarId⟩ ())).1 = + KExpr.mkFVar ⟨before.env.nextFVarId⟩ () := by + have faithful := KExpr.keyCollisionFree_anon.mpr + (data.faithful.mono (fun _ h => h.elim Or.inl (fun equal => .inr (.inl equal))) : + KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ + term = KExpr.mkFVar ⟨before.env.nextFVarId⟩ ()) + simpa only [KExpr.eraseMeta_anon] using + before.env.intern.internExpr_eraseMeta valid.coherent faithful + have walk := instantiateRev_spec (fvars := #[KExpr.mkFVar ⟨before.env.nextFVarId⟩ ()]) + data.faithful data.constructed (by simpa using data.bound) + (fun _ reached => .inr (.inr reached)) + (valid.coherent.internExpr (KExpr.mkFVar ⟨before.env.nextFVarId⟩ ())) + (fun _ member => (InternTable.ExprSupport.of_internExpr member).elim + Or.inl (fun equal => .inr (.inl equal))) + rw [openLet_eq] at run + split at run + · simp only [interned] at run + cases run + exact valid.ofMaps rfl rfl rfl walk.2.1 + · contradiction + +/-! ### The invariant -/ + +/-- The bundled checker state invariant. The parameters fix the run: the +resolver, the anchor and current interfaces, the verified source, the source +cache catalog, and the current local context with its model reading and +synthesis bounds. -/ +structure CheckerInvariant {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (source : Ixon.Env) + (catalog : List (SourceCacheRequest source)) (locals : List FVarId) + (context : Model.Context β) (bounds : List VLevel) (state : TcState .anon) : Prop where + /-- Ownership, installed loader, block registration, intern coherence, + standalone source agreement, and catalog cache agreement. -/ + sourceCache : SourceCacheInvariant catalog state + /-- Both inference-cache maps with the retained synthesis checks of every full publication. -/ + synthesis : Nonempty (SynthesisCacheHistory resolve anchor entries state) + /-- The five WHNF maps with their producing executions. -/ + whnf : Nonempty (BetaCacheHistory β state) + /-- Coherent local lookup, fresh-id bound, and monotone loader counter. -/ + structural : LocalStateInvariant state + /-- The active locals read to the model context. -/ + reading : LocalContextReading resolve locals state.lctx context + /-- The current context was formed from the empty context by checked domains. -/ + origin : Nonempty (SynthesisContext resolve anchor [] [] entries context bounds) + /-- Semantic agreement of the remaining reduction memos. -/ + semantics : ReductionCacheSemantics.{u,v} resolve entries state + +namespace CheckerInvariant + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +section Projections + +variable {state : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) +include valid + +theorem sourceState : SourceStateInvariant source state := valid.sourceCache.state + +theorem inference : InferenceStateInvariant source state := valid.sourceCache.state.state + +theorem coherent : state.env.intern.WF := valid.inference.coherent + +theorem installed : + state.lazyFault = some (fun addr => ingressAnonAddrShallow source addr true) := + valid.inference.installed + +theorem blocks : LoadedBlockInvariant source state.env := valid.inference.blocks + +theorem ownership : SourceOwnership source := valid.inference.ownership + +def owned : OwnedLazySupport state := valid.inference.owned + +theorem agreement : StandaloneSourceAgreement source state.env := valid.sourceCache.state.agreement + +theorem cache : SourceCacheAgreement catalog state := valid.sourceCache.cache + +theorem inferenceHistory : Nonempty (InferenceCacheHistory state) := + valid.synthesis.elim fun history => ⟨history.execution⟩ + +end Projections + +/-- The driver's initial checker satisfies the invariant under the run assumptions. -/ +theorem initial {cfg : CheckCfg} {domain : RunDomain} (assumptions : RunAssumptions source cfg domain) + (resolve : Address → Option (ConstRef β)) (anchor entries : Model.Environment β) + (catalog : List (SourceCacheRequest source)) : + CheckerInvariant.{u,v} resolve anchor entries source catalog [] [] [] + (TcState.newLazyAnon source cfg.verifyHashes) := by + rw [assumptions.verify] + exact { + sourceCache := .ofCheckedSource source catalog assumptions.checked + synthesis := ⟨.initial source⟩ + whnf := ⟨.initial source⟩ + structural := LocalStateInvariant.newLazyAnon source true + reading := .empty resolve _ + origin := ⟨.empty entries⟩ + semantics := .initial source } + +theorem initialLoopState {cfg : CheckCfg} {domain : RunDomain} + (assumptions : RunAssumptions source cfg domain) + (resolve : Address → Option (ConstRef β)) (anchor entries : Model.Environment β) + (catalog : List (SourceCacheRequest source)) : + CheckerInvariant.{u,v} resolve anchor entries source catalog [] [] [] + (initialAnonCheckLoopState source cfg).checker := + initial assumptions resolve anchor entries catalog + +/-- Operations that retain every map, the loaded declarations, coherence, and +the local context up to lookup equivalence preserve the invariant. -/ +theorem ofMaps {before after : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (frame : LocalStateFrame before after) + (constants : after.env.consts = before.env.consts) + (blocks : after.env.blocks = before.env.blocks) + (coherent : after.env.intern.WF) + (full : after.env.inferCache = before.env.inferCache) + (only : after.env.inferOnlyCache = before.env.inferOnlyCache) + (whnf : ∀ partition : WhnfCachePartition, partition.cache after = partition.cache before) + (defEq : ∀ partition : DefEqCachePartition, partition.cache after = partition.cache before) + (manager : after.equivManager = before.equivManager) + (unfold : after.env.unfoldCache = before.env.unfoldCache) + (isProp : after.env.isPropCache = before.env.isPropCache) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after where + sourceCache := ⟨valid.sourceState.ofMaps frame.loader constants blocks coherent, + valid.cache.ofMaps full only constants⟩ + synthesis := valid.synthesis.elim fun history => ⟨history.ofMaps full only⟩ + whnf := valid.whnf.elim fun history => ⟨history.ofMaps whnf⟩ + structural := frame.invariant valid.structural + reading := valid.reading.congr frame.context.symm + origin := valid.origin + semantics := valid.semantics.ofMaps whnf defEq manager unfold isProp + +theorem ofCtxAddrCache {before : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (memo : Std.HashMap (Address × UInt64) Address) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {before with ctxAddrCache := memo} := + valid.ofMaps ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl valid.coherent rfl rfl + (fun partition => by cases partition <;> rfl) (fun partition => by cases partition <;> rfl) + rfl rfl rfl + +theorem inferKey {before after : TcState .anon} {term : KExpr .anon} {key : Address × Address} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (run : TcM.inferKey term before = .ok key after) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after := by + rw [inferKey_state run] + exact valid.ofCtxAddrCache _ + +theorem whnfKey {before after : TcState .anon} {term : KExpr .anon} {key : Address × Address} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (run : TcM.whnfKey term before = .ok key after) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after := by + rw [whnfKey_state run] + exact valid.ofCtxAddrCache _ + +theorem defEqCtxKey {before after : TcState .anon} {left right : KExpr .anon} {addr : Address} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (run : TcM.defEqCtxKey left right before = .ok addr after) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after := by + rw [defEqCtxKey_state run] + exact valid.ofCtxAddrCache _ + +theorem policy {before : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (policy : Bool) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {before with inferOnly := policy} := + valid.ofMaps ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl valid.coherent rfl rfl + (fun partition => by cases partition <;> rfl) (fun partition => by cases partition <;> rfl) + rfl rfl rfl + +/-- The driver's periodic clearing begins empty histories and memos. -/ +theorem clearReductionCaches {before : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {before with env := before.env.clearReductionCaches} where + sourceCache := valid.sourceCache.clearReductionCaches + synthesis := valid.synthesis.elim fun history => ⟨history.clear⟩ + whnf := valid.whnf.elim fun history => ⟨history.clear⟩ + structural := ⟨valid.structural.coherent, valid.structural.allocated, valid.structural.loader⟩ + reading := valid.reading + origin := valid.origin + semantics := valid.semantics.clear + +/-- Per-item reset empties the local context and the equivalence manager and +retains every environment map. -/ +theorem reset {before after : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (run : TcM.reset before = .ok () after) : + CheckerInvariant.{u,v} resolve anchor entries source catalog [] [] [] after := by + rw [reset_eq] at run + cases run + exact { + sourceCache := ⟨valid.sourceState.ofMaps rfl rfl rfl valid.coherent, valid.cache.ofMaps rfl rfl rfl⟩ + synthesis := valid.synthesis.elim fun history => ⟨history.ofMaps rfl rfl⟩ + whnf := valid.whnf.elim fun history => ⟨history.ofMaps fun partition => by cases partition <;> rfl⟩ + structural := ⟨LocalContext.WF.empty, LocalContext.IdsBelow.empty _, valid.structural.loader⟩ + reading := .empty resolve _ + origin := ⟨.empty entries⟩ + semantics := valid.semantics.ofMapsManager (fun partition => by cases partition <;> rfl) + (fun partition => by cases partition <;> rfl) EquivManagerSemantics.empty rfl rfl } + +private theorem afterLookup {before after : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (sourceCache : SourceCacheInvariant catalog after) + (synthesis : Nonempty (SynthesisCacheHistory resolve anchor entries after)) + (frame : LazyLookupFrame before after) (ingress : KEnv.IngressFrame before.env after.env) + (structural : LocalStateFrame before after) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after := by + obtain ⟨consts, blocks, intern, envEq⟩ := ingress + have checker := frame.checker + exact { + sourceCache, synthesis + whnf := valid.whnf.elim fun history => ⟨history.ofMaps fun partition => by + cases partition <;> simp only [WhnfCachePartition.cache, envEq]⟩ + structural := structural.invariant valid.structural + reading := valid.reading.congr structural.context.symm + origin := valid.origin + semantics := valid.semantics.ofMaps + (fun partition => by cases partition <;> simp only [WhnfCachePartition.cache, envEq]) + (fun partition => by cases partition <;> simp only [DefEqCachePartition.cache, envEq]) + (by rw [checker]) (by rw [envEq]) (by rw [envEq]) } + +/-- Lookup retains the invariant on both outcomes, including partially +completed conversion and publication before an unknown-root error. -/ +theorem getConst {before : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (id : KId .anon) (data : StandaloneConversionData source id.addr before.env) : + match TcM.getConst id before with + | .ok _ after | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after := by + have sourceCache := valid.sourceCache.getConst data + have synthesis : match TcM.getConst id before with + | .ok _ after | .error _ after => Nonempty (SynthesisCacheHistory resolve anchor entries after) := by + refine valid.synthesis.elim fun history => ?_ + have preserved := history.getConst (valid.owned.toVerified id.addr) + cases run : TcM.getConst id before <;> rw [run] at preserved <;> exact ⟨preserved⟩ + have frame := getConst_owned (id := id) valid.owned + have ingress := getConst_ingressFrame (id := id) source true valid.installed + have structural := FramesLocalState.getConst id before valid.structural + cases run : TcM.getConst id before <;> + rw [run] at sourceCache synthesis frame ingress structural <;> + exact valid.afterLookup sourceCache synthesis frame.1 ingress structural + +/-- Binder opening pushes the checked domain onto the model context; the +caller supplies the extended context's synthesis origin. -/ +theorem openBinder {before after : TcState .anon} {name : Mode.anon.F Name} + {bi : Mode.anon.F Lean.BinderInfo} {domain body opened : KExpr .anon} {fresh : FVarId} + {A : AExpr β} {level : VLevel} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (data : BinderOpeningData before body) + (typeReads : readScopedExpr? resolve locals domain = some A.erase) + (pushed : Nonempty (SynthesisContext resolve anchor [] [] entries (context.push A) (level :: bounds))) + (run : TcM.openBinder name bi domain body before = .ok (opened, fresh) after) : + CheckerInvariant.{u,v} resolve anchor entries source catalog (fresh :: locals) (context.push A) + (level :: bounds) after := by + have sourceCache := valid.sourceCache.openBinder data run + have synthesis : Nonempty (SynthesisCacheHistory resolve anchor entries after) := + valid.synthesis.elim fun history => ⟨history.openBinder run⟩ + have whnf : Nonempty (BetaCacheHistory β after) := + valid.whnf.elim fun history => ⟨history.openBinder run⟩ + have effect := PreservesLocalState.openBinder name bi domain body before valid.structural + rw [run] at effect + have absent := valid.structural.freshReading valid.reading + rw [openBinder_eq] at run + split at run + · cases run + exact { + sourceCache, synthesis, whnf + structural := effect.valid + reading := valid.reading.push absent typeReads + origin := pushed + semantics := valid.semantics.ofMaps (fun partition => by cases partition <;> rfl) + (fun partition => by cases partition <;> rfl) rfl rfl rfl } + · contradiction + +/-- Let opening pushes the declared type; the value is retained in the local +declaration and its substitution agreement remains a separate obligation. -/ +theorem openLet {before after : TcState .anon} {name : Mode.anon.F Name} + {domain value body opened : KExpr .anon} {fresh : FVarId} {A : AExpr β} {level : VLevel} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (data : BinderOpeningData before body) + (typeReads : readScopedExpr? resolve locals domain = some A.erase) + (pushed : Nonempty (SynthesisContext resolve anchor [] [] entries (context.push A) (level :: bounds))) + (run : TcM.openLet name domain value body before = .ok (opened, fresh) after) : + CheckerInvariant.{u,v} resolve anchor entries source catalog (fresh :: locals) (context.push A) + (level :: bounds) after := by + have maps := openLet_inference_state run + have sourceCache : SourceCacheInvariant catalog after := + ⟨⟨valid.inference.openLet data run, valid.agreement.ofMap maps.2.2.1⟩, + valid.cache.ofMaps maps.1 maps.2.1 maps.2.2.1⟩ + have synthesis := valid.synthesis.elim fun history => + (⟨⟨history.execution.openLet run, history.checks⟩⟩ : + Nonempty (SynthesisCacheHistory resolve anchor entries after)) + have whnf : Nonempty (BetaCacheHistory β after) := + valid.whnf.elim fun history => ⟨history.openLet run⟩ + have effect := PreservesLocalState.openLet name domain value body before valid.structural + rw [run] at effect + have absent := valid.structural.freshReading valid.reading + rw [openLet_eq] at run + split at run + · cases run + exact { + sourceCache, synthesis, whnf + structural := effect.valid + reading := valid.reading.push absent typeReads + origin := pushed + semantics := valid.semantics.ofMaps (fun partition => by cases partition <;> rfl) + (fun partition => by cases partition <;> rfl) rfl rfl rfl } + · contradiction + +/-- Scope exit restores the caller's locals from any body state that only +extended the local context, retaining the body's other state updates. -/ +theorem exitScope {before state : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + {locals' : List FVarId} {context' : Model.Context β} {bounds' : List VLevel} + (extended : CheckerInvariant.{u,v} resolve anchor entries source catalog locals' context' bounds' state) + (extension : before.lctx.Extension state.lctx) + (counter : before.env.nextFVarId.toNat ≤ state.env.nextFVarId.toNat) + (loader : state.lazyFault = before.lazyFault) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {state with lctx := state.lctx.truncate before.lctx.size} := + let frame : LocalStateFrame before {state with lctx := state.lctx.truncate before.lctx.size} := + (LocalStateExtension.mk extended.structural counter extension loader).restore + { sourceCache := extended.sourceCache.truncate _ + synthesis := extended.synthesis.elim fun history => ⟨history.truncate _⟩ + whnf := extended.whnf.elim fun history => ⟨history.truncate _⟩ + structural := frame.invariant valid.structural + reading := valid.reading.congr frame.context.symm + origin := valid.origin + semantics := extended.semantics.ofMaps (fun partition => by cases partition <;> rfl) + (fun partition => by cases partition <;> rfl) rfl rfl rfl } + +/-- A scoped body that establishes the invariant for its extended context on +both outcomes yields the caller's invariant after the actual scope cleanup. -/ +theorem withLctxScope {α : Type} {action : RecM .anon α} {methods : Methods .anon} + {before : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (body : match action.run methods before with + | .ok _ state | .error _ state => + (∃ (locals' : List FVarId) (context' : Model.Context β) (bounds' : List VLevel), + CheckerInvariant.{u,v} resolve anchor entries source catalog locals' context' bounds' state) ∧ + before.lctx.Extension state.lctx ∧ + before.env.nextFVarId.toNat ≤ state.env.nextFVarId.toNat ∧ + state.lazyFault = before.lazyFault) : + match (RecM.withLctxScope action).run methods before with + | .ok _ after | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after := by + rw [withLctxScope_eq] + cases run : action.run methods before <;> rw [run] at body <;> + obtain ⟨⟨locals', context', bounds', extended⟩, extension, counter, loader⟩ := body <;> + exact valid.exitScope extended extension counter loader + +/-! ### Demonstrations: sort and free-variable inference -/ + +/-- A successful closed-sort inference changes only the intern table and the +two inference maps. -/ +private theorem infer_sort_state {level : KUniv .anon} {fuel : Nat} {result : KExpr .anon} + {before after : TcState .anon} + (accepted : RecM.infer (KExpr.mkSort level) (methodsN fuel) before = .ok result after) : + ∃ table full only, after = {before with env := {before.env with + intern := table, inferCache := full, inferOnlyCache := only}} := by + have keyRun := inferKey_closed (term := KExpr.mkSort level) rfl before + rcases observeInferenceCache keyRun with ⟨hit, _, stateEq⟩ | ⟨miss, _, stateEq⟩ + · rw [hit.run (methodsN fuel)] at accepted + cases accepted + rw [stateEq] + exact ⟨before.env.intern, before.env.inferCache, before.env.inferOnlyCache, rfl⟩ + · obtain ⟨state, run, written⟩ := infer_uncached_success_state miss accepted + rw [stateEq] at run + change EStateM.Result.ok + (before.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).1 + {before with env := {before.env with intern := + (before.env.intern.internExpr (KExpr.mkSort (KUniv.mkSucc level))).2}} = + .ok result state at run + cases run + rw [written] + cases policy : before.inferOnly + · simp only [Bool.false_eq_true, ↓reduceIte] + exact ⟨_, _, before.env.inferOnlyCache, rfl⟩ + · simp only [↓reduceIte] + exact ⟨_, before.env.inferCache, _, rfl⟩ + +/-- Sort inference preserves the invariant and returns the canonical +successor sort, whose typing is derived from the maintained catalog agreement +on a hit and from the actual intern step on a miss. -/ +theorem inferSort {level : KUniv .anon} {fuel : Nat} {result : KExpr .anon} + {before after : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (member : SourceCacheRequest.sort level ∈ catalog) + (keyData : SourceCacheKeyData catalog (KExpr.mkSort level)) + (faithful : KExpr.KeyCollisionFree fun term => before.env.intern.ExprSupport term ∨ + term = KExpr.mkSort (KUniv.mkSucc level)) + (accepted : RecM.infer (KExpr.mkSort level) (methodsN fuel) before = .ok result after) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + result = KExpr.mkSort (KUniv.mkSucc level) ∧ + ScopedModelTyping.{u,v} resolve entries locals context (KExpr.mkSort level) result := by + have keyRun := inferKey_closed (term := KExpr.mkSort level) rfl before + obtain ⟨canonical, _⟩ := infer_sort_cache_agreement keyRun + (valid.cache (.sort level) member).correct valid.coherent faithful accepted + refine ⟨?_, canonical, ⟨.sort (readLevel level), .sort (.succ (readLevel level)), rfl, + by rw [canonical]; simp [AExpr.erase], TypingClaim.sort _⟩⟩ + have sourceCache : SourceCacheInvariant catalog after := by + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, _, stateEq⟩ + · exact (OwnedInferenceTrace.hit (fuel := fuel) hit).preservesSourceCache valid.sourceCache + trivial trivial accepted + · exact (OwnedInferenceTrace.sort (fuel := fuel) miss).preservesSourceCache valid.sourceCache + trivial (And.intro keyData (by rw [stateEq]; exact faithful)) accepted + have synthesis : Nonempty (SynthesisCacheHistory resolve anchor entries after) := by + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, _, stateEq⟩ + · exact valid.synthesis.elim fun history => + ⟨history.afterInference (InferenceCacheTrace.hit hit : InferenceCacheTrace.{0} fuel before _) + accepted .nil⟩ + · refine valid.synthesis.elim fun history => valid.origin.elim fun origin => ?_ + have tree : SynthesisInference resolve entries locals context bounds fuel before + (KExpr.mkSort level) (.sort (readLevel level)) (.sort (.succ (readLevel level))) + (.succ (.succ (readLevel level))) := + .known (.sort miss (by rw [stateEq]; exact valid.coherent) (by rw [stateEq]; exact faithful)) + (.sort _) + exact ⟨history.afterInference + (InferenceCacheTrace.sort miss : InferenceCacheTrace.{0} fuel before _) accepted + (.singleton (SynthesisEventCheck.ofSource tree origin valid.reading rfl miss accepted))⟩ + obtain ⟨table, full, only, stateEq⟩ := infer_sort_state accepted + subst stateEq + exact { + sourceCache, synthesis + whnf := valid.whnf.elim fun history => ⟨history.ofMaps fun partition => by cases partition <;> rfl⟩ + structural := ⟨valid.structural.coherent, valid.structural.allocated, valid.structural.loader⟩ + reading := valid.reading + origin := valid.origin + semantics := valid.semantics.ofMaps (fun partition => by cases partition <;> rfl) + (fun partition => by cases partition <;> rfl) rfl rfl rfl } + +/-- A successful free-variable inference changes only the context-digest memo +and the two inference maps. -/ +private theorem infer_fvar_state {id : FVarId} {name : Mode.anon.F Name} {info : ExprInfo .anon} + {fuel : Nat} {result : KExpr .anon} {before after : TcState .anon} + (support : FVarInferenceSupport before id name info) + (accepted : RecM.infer (.fvar id name info) (methodsN fuel) before = .ok result after) : + ∃ memo full only, after = {before with ctxAddrCache := memo, env := {before.env with + inferCache := full, inferOnlyCache := only}} := by + have keyed := inferKey_state support.keyRun + rcases observeInferenceCache support.keyRun with ⟨hit, _, stateEq⟩ | ⟨miss, _, stateEq⟩ + · rw [hit.run (methodsN fuel)] at accepted + cases accepted + rw [stateEq, keyed] + exact ⟨_, before.env.inferCache, before.env.inferOnlyCache, rfl⟩ + · obtain ⟨state, run, written⟩ := infer_uncached_success_state miss accepted + change (RecM.inferUncached RecM.inferCall before.inferOnly (.fvar id name info)).run + (methodsN fuel) miss.keyed = _ at run + unfold RecM.inferUncached at run + simp only [ReaderT.run_bind] at run + change EStateM.bind (get : TcM .anon (TcState .anon)) _ miss.keyed = _ at run + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) miss.keyed = + .ok miss.keyed miss.keyed from rfl] at run + dsimp only at run + split at run + · cases run + rw [written, stateEq, keyed] + cases policy : before.inferOnly + · simp only [Bool.false_eq_true, ↓reduceIte] + exact ⟨_, _, before.env.inferOnlyCache, rfl⟩ + · simp only [↓reduceIte] + exact ⟨_, before.env.inferCache, _, rfl⟩ + · contradiction + +/-- Free-variable inference preserves the invariant and returns the actual +declaration type, read at the registered model index. -/ +theorem inferFVar {id : FVarId} {name : Mode.anon.F Name} {info : ExprInfo .anon} {index : Nat} + {A : AExpr β} {level : VLevel} {fuel : Nat} {result : KExpr .anon} {before after : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (support : FVarInferenceSupport before id name info) + (registered : localIndex? locals id = some index) + (atIndex : context[index]? = some A) (boundAtIndex : bounds[index]? = some level) + (keyData : SourceCacheKeyData catalog (.fvar id name info)) + (accepted : RecM.infer (.fvar id name info) (methodsN fuel) before = .ok result after) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + readScopedExpr? resolve locals result = some A.erase ∧ + TypingClaim.{u,v} entries context (.bvar index) A := by + obtain ⟨reading, typed⟩ := support.sound (entries := entries) valid.reading registered atIndex accepted + refine ⟨?_, reading, typed⟩ + have sourceCache : SourceCacheInvariant catalog after := by + rcases observeInferenceCache support.keyRun with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ + · exact (OwnedInferenceTrace.hit (fuel := fuel) hit).preservesSourceCache valid.sourceCache + trivial trivial accepted + · exact (OwnedInferenceTrace.fvar (fuel := fuel) miss).preservesSourceCache valid.sourceCache + trivial keyData accepted + have synthesis : Nonempty (SynthesisCacheHistory resolve anchor entries after) := by + rcases observeInferenceCache support.keyRun with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ + · exact valid.synthesis.elim fun history => + ⟨history.afterInference (InferenceCacheTrace.hit hit : InferenceCacheTrace.{0} fuel before _) + accepted .nil⟩ + · refine valid.synthesis.elim fun history => valid.origin.elim fun origin => ?_ + have tree : SynthesisInference resolve entries locals context bounds fuel before + (.fvar id name info) (.bvar index) A level := + .fvar (.fvar support registered atIndex) atIndex boundAtIndex + exact ⟨history.afterInference + (InferenceCacheTrace.fvar miss : InferenceCacheTrace.{0} fuel before _) accepted + (.singleton (SynthesisEventCheck.ofSource tree origin valid.reading + (by simp [readScopedExpr?, registered, AExpr.erase]) miss accepted))⟩ + obtain ⟨memo, full, only, stateEq⟩ := infer_fvar_state support accepted + subst stateEq + exact { + sourceCache, synthesis + whnf := valid.whnf.elim fun history => ⟨history.ofMaps fun partition => by cases partition <;> rfl⟩ + structural := ⟨valid.structural.coherent, valid.structural.allocated, valid.structural.loader⟩ + reading := valid.reading + origin := valid.origin + semantics := valid.semantics.ofMaps (fun partition => by cases partition <;> rfl) + (fun partition => by cases partition <;> rfl) rfl rfl rfl } + +end CheckerInvariant + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/RunAssumptions.lean b/Ix/Kernel/Verify/Consistency/RunAssumptions.lean new file mode 100644 index 000000000..f33765cd0 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/RunAssumptions.lean @@ -0,0 +1,135 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Driver +import Ix.Kernel.Verify.Support +import Ix.Kernel.Verify.Consistency.ConversionRecipe +import Ix.Kernel.Verify.Consistency.SourceOwnershipCheck +import Ix.Kernel.Verify.Consistency.SourceAgreement + +/-! +# Run-level assumptions + +One record collects the hypotheses that the refinement lemmas currently take +one at a time: the finite source ownership check, hash verification, and +collision freedom and arithmetic bounds over the finite syntax inventory of a +run. The inventory is an explicit parameter; the final theorem instantiates it +with the actual expressions and universes reached by `checkEnvAnon`. Every +consumer needs only membership in that inventory, so no injectivity of content +addresses beyond the inventory is assumed. Context-digest collision freedom has +no consumer in this library yet and is deliberately not recorded here. +-/ + +namespace Ix.Kernel.Consistency + +/-- The finite syntax inventory of one run: every expression and universe term +the walkers key, intern, compare, or construct, and the additive walker budget +(binder depth plus simultaneous-argument count) of any walker call. -/ +structure RunDomain where + expressions : List (KExpr .anon) + universes : List (KUniv .anon) + slack : Nat + +/-- Run-level hypotheses. Each field names the lemma family consuming it. -/ +structure RunAssumptions (source : Ixon.Env) (cfg : CheckCfg) (domain : RunDomain) : Prop where + /-- Finite source ownership: `SourceOwnership.ofCheck`, `OwnedLazySupport.ofCheckedSource`, + `InferenceStateInvariant.ofCheckedSource`, `SourceCacheInvariant.ofCheckedSource`. -/ + checked : sourceOwnershipCheck source = true + /-- Hash verification on: identifies the driver's initial state with + `TcState.newLazyAnon source`, whose loader is the one named by + `InferenceStateInvariant.installed` and `VerifiedLazySupport.installed`. -/ + verify : cfg.verifyHashes = true + /-- Expression collision freedom over the inventory: every `faithful` field + (`BinderOpeningData`, `BinderOpeningSupport`, `ApplicationSubstitutionData`, + `LambdaClosingData`, `ConstantInstantiationData`, `ConversionData`), the cache + key data (`InferenceCacheHistory.KeyData`, `BetaCacheHistory.KeyData`, + `SourceCacheKeyData`), `RunSupport.CollisionFree.expr`, and pairwise + `KExpr.AddrFaithful` at hash comparisons. -/ + expressions : KExpr.CollisionFree (· ∈ domain.expressions) + /-- Universe collision freedom: `ConversionData.universes`, + `RunSupport.CollisionFree.univ`, and pairwise `KUniv.AddrFaithful`. -/ + universes : KUniv.CollisionFree (· ∈ domain.universes) + /-- Walker size bounds: `BinderOpeningData.bound`, `ApplicationSubstitutionData` + bounds, `LambdaClosingData.bound`, and the lifting/substitution specifications + (`instantiateRev_spec`, `subst_spec`, simultaneous substitution). -/ + sizes : ∀ term ∈ domain.expressions, term.size + domain.slack < UInt64.size + /-- Universe size bounds: universe construction and instantiation walkers + (`InstUniv`, `Level`, and the level bounds of `SynthesisSupport`). -/ + levels : ∀ level ∈ domain.universes, level.size < UInt64.size + +namespace RunAssumptions + +variable {source : Ixon.Env} {cfg : CheckCfg} {domain : RunDomain} + (assumptions : RunAssumptions source cfg domain) +include assumptions + +theorem ownership : SourceOwnership source := .ofCheck assumptions.checked + +/-- The driver's initial checker is the verified lazy state. -/ +theorem initialState : TcState.newLazyAnon source cfg.verifyHashes = TcState.newLazyAnon source := by + rw [assumptions.verify] + +theorem initialLoopState : + (initialAnonCheckLoopState source cfg).checker = TcState.newLazyAnon source := + assumptions.initialState + +/-- Any support drawn from the inventory is collision free. -/ +theorem collisionFree {support : KExpr .anon → Prop} + (covered : ∀ term, support term → term ∈ domain.expressions) : KExpr.CollisionFree support := + assumptions.expressions.mono covered + +theorem keyCollisionFree {support : KExpr .anon → Prop} + (covered : ∀ term, support term → term ∈ domain.expressions) : KExpr.KeyCollisionFree support := + KExpr.keyCollisionFree_anon.mpr (assumptions.collisionFree covered) + +theorem addrFaithful {left right : KExpr .anon} (leftMember : left ∈ domain.expressions) + (rightMember : right ∈ domain.expressions) : left.AddrFaithful right := + assumptions.expressions.addrFaithful leftMember rightMember + +theorem universeCollisionFree {support : KUniv .anon → Prop} + (covered : ∀ level, support level → level ∈ domain.universes) : KUniv.CollisionFree support := + assumptions.universes.mono covered + +theorem universeAddrFaithful {left right : KUniv .anon} (leftMember : left ∈ domain.universes) + (rightMember : right ∈ domain.universes) : left.AddrFaithful right := + assumptions.universes.addrFaithful leftMember rightMember + +/-- The validation run support of `Production.lean` is covered by the inventory. -/ +theorem supportCollisionFree (support : RunSupport) + (exprs : ∀ term, support term → term ∈ domain.expressions) + (univs : ∀ level, support.univ level → level ∈ domain.universes) : support.CollisionFree := + ⟨assumptions.collisionFree exprs, assumptions.universeCollisionFree univs⟩ + +/-- Source conversion data at a table whose support and candidates are in the inventory. -/ +theorem conversionData {α : Type} (recipe : ConversionRecipe α) (table : InternTable .anon) + (exprs : ∀ term, table.ExprSupport term → term ∈ domain.expressions) + (recipeExprs : ∀ term ∈ recipe.exprs, term ∈ domain.expressions) + (univs : ∀ level, table.UnivSupport level → level ∈ domain.universes) + (recipeUnivs : ∀ level ∈ recipe.univs, level ∈ domain.universes) : + ConversionData recipe table := + ⟨assumptions.collisionFree fun _ h => h.elim (exprs _) (recipeExprs _), + assumptions.universeCollisionFree fun _ h => h.elim (univs _) (recipeUnivs _)⟩ + +/-- Lookup data for every standalone whose predicted conversion candidates are in the inventory. -/ +theorem standaloneConversionData (addr : Address) (env : AnonEnv) + (exprs : ∀ term, env.intern.ExprSupport term → term ∈ domain.expressions) + (univs : ∀ level, env.intern.UnivSupport level → level ∈ domain.universes) + (recipeExprs : ∀ constant, getConstVerified source addr true = .ok (some constant) → + ∀ term ∈ (ConversionRecipe.standalone source addr constant).exprs, term ∈ domain.expressions) + (recipeUnivs : ∀ constant, getConstVerified source addr true = .ok (some constant) → + ∀ level ∈ (ConversionRecipe.standalone source addr constant).univs, level ∈ domain.universes) : + StandaloneConversionData source addr env := by + intro constant verified _ + exact assumptions.conversionData _ _ exprs (recipeExprs constant verified) univs + (recipeUnivs constant verified) + +/-- A walker bound whose additive budget is within the run's slack. -/ +theorem bound {term : KExpr .anon} (member : term ∈ domain.expressions) {extra : Nat} + (budget : extra ≤ domain.slack) : term.size + extra < UInt64.size := + Nat.lt_of_le_of_lt (Nat.add_le_add_left budget _) (assumptions.sizes term member) + +end RunAssumptions + +end Ix.Kernel.Consistency From 5a694f435d07a7bc61edbb98341afaaee83cdda5 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 15 Sep 2026 11:09:02 -0400 Subject: [PATCH 54/63] fix(deps): update rustls for RUSTSEC-2026-0285 Bump rustls to 0.23.45 so cargo-deny's advisory check passes; the same lockfile update as on jcb/aiur-soundness. --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c8c03e78f..a6af4c5ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -843,7 +843,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3355,14 +3355,14 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.40" +version = "0.23.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" dependencies = [ "log", "once_cell", @@ -3385,9 +3385,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "ring", "rustls-pki-types", @@ -3425,7 +3425,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b55fb86dfd3a2f5f76ea78310a88f96c4ea21a3031f8d212443d56123fd0521" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] From e5925a2ba522c966081ed91acc5e44914a170551 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 15 Sep 2026 11:18:13 -0400 Subject: [PATCH 55/63] Refresh verification docs and remove stale references Point the validation commands in the kernel docs at the required consistency gate and the strict compiler build, mark the named build as legacy, update Rust comments that still cited the old Ix.Tc module paths, and delete the unused IO.Ref union-find module. --- Ix/Kernel/Equiv.lean | 2 +- Ix/UnionFind.lean | 46 ------------------- crates/common/src/prim_addrs.rs | 2 +- .../aux_gen/source_name_hints_reference.rs | 2 +- crates/ffi/src/kernel.rs | 2 +- crates/ffi/src/lean_build.rs | 2 +- crates/ixon/src/canon_univ.rs | 12 ++--- crates/ixon/src/diff.rs | 2 +- crates/ixvm-codegen/src/aiur_ixvm_witness.rs | 2 +- crates/kernel/src/def_eq.rs | 4 +- crates/kernel/src/def_eq/application.rs | 2 +- crates/kernel/src/def_eq/binders.rs | 2 +- docs/kernel-recursion.md | 7 ++- docs/kernel-verification.md | 7 +-- 14 files changed, 26 insertions(+), 68 deletions(-) delete mode 100644 Ix/UnionFind.lean diff --git a/Ix/Kernel/Equiv.lean b/Ix/Kernel/Equiv.lean index ac83a1ec1..df877f9d2 100644 --- a/Ix/Kernel/Equiv.lean +++ b/Ix/Kernel/Equiv.lean @@ -11,7 +11,7 @@ hash, the requested context-suffix radius, and the expression's intrinsic local-binder radius. Pure port: operations return the updated manager (path halving mutates on -reads). Do not reuse the `IO.Ref`-based `Ix.UnionFind`. +reads); the former `IO.Ref`-based `Ix.UnionFind` module was removed as unused. -/ public section diff --git a/Ix/UnionFind.lean b/Ix/UnionFind.lean deleted file mode 100644 index 83c2631e8..000000000 --- a/Ix/UnionFind.lean +++ /dev/null @@ -1,46 +0,0 @@ -module - -public section - -structure UnionFind where - parent : IO.Ref (Array Nat) - rank : IO.Ref (Array Nat) - -namespace UnionFind - -def init (n: Nat) : IO UnionFind := do - let parent <- IO.mkRef (Array.range n) - let rank <- IO.mkRef (Array.replicate n 0) - return ⟨parent, rank⟩ - -partial def find (uf: UnionFind) (x: Nat) : IO Nat := do - let parent <- uf.parent.get - let p := parent[x]! - if p == x then return x - else do - let root <- find uf p - uf.parent.modify fun arr => arr.set! x root - return root - -def union (uf : UnionFind) (x y: Nat) : IO Unit := do - let rx <- find uf x - let ry <- find uf y - if rx == ry then return () - let rank <- uf.rank.get - let rankX := rank[rx]! - let rankY := rank[ry]! - if rankX < rankY then - uf.parent.modify fun arr => arr.set! rx ry - else if rankX > rankY then - uf.parent.modify fun arr => arr.set! ry rx - else - uf.parent.modify fun arr => arr.set! ry rx - uf.rank.modify fun arr => arr.set! rx (rankX + 1) - -def unionMany (uf: UnionFind) (pairs: Array (Nat × Nat)) : IO Unit := do - for (x, y) in pairs do - union uf x y - -end UnionFind - -end diff --git a/crates/common/src/prim_addrs.rs b/crates/common/src/prim_addrs.rs index 661dc145e..c299da373 100644 --- a/crates/common/src/prim_addrs.rs +++ b/crates/common/src/prim_addrs.rs @@ -10,7 +10,7 @@ //! Regenerate with `lake test -- --ignored rust-kernel-build-primitives`, which //! dumps the current `(name, hex)` pairs — paste the updated lines into //! `PrimAddrs::new`. `lake test -- prim-addrs` pins this table against -//! the Lean mirror in `Ix/Tc/Primitive.lean`. +//! the Lean mirror in `Ix/Kernel/Primitive.lean`. use std::sync::LazyLock; diff --git a/crates/compile/src/compile/aux_gen/source_name_hints_reference.rs b/crates/compile/src/compile/aux_gen/source_name_hints_reference.rs index 77ab0bede..498eaf067 100644 --- a/crates/compile/src/compile/aux_gen/source_name_hints_reference.rs +++ b/crates/compile/src/compile/aux_gen/source_name_hints_reference.rs @@ -10,7 +10,7 @@ pub(super) fn source_name_hint_candidate(expr: &LeanExpr) -> bool { /// Name-erased structural content key for the source-name hint map. /// -/// Mirrors the equivalence of the Lean pipeline's `Ix.Tc.KExpr` content +/// Mirrors the equivalence of the Lean pipeline's `Ix.Kernel.KExpr` content /// addresses (`toKexprStatic ... |>.addr` in `Ix/AuxGen/Kernel.lean`) and /// of the kernel's `ExprKey`/`structural_eq`: display names, binder /// names, binder infos, and mdata are excluded; `Const`/`Prj` contribute diff --git a/crates/ffi/src/kernel.rs b/crates/ffi/src/kernel.rs index 02525767a..b9bb7cb3d 100644 --- a/crates/ffi/src/kernel.rs +++ b/crates/ffi/src/kernel.rs @@ -969,7 +969,7 @@ unsafe extern "C" { fn lean_internal_set_thread_stack_size(size: usize); } -/// `Ix.Tc.setLeanThreadStackSize : USize → BaseIO Unit` +/// `Ix.Kernel.setLeanThreadStackSize : USize → BaseIO Unit` /// /// ABI adapter: the runtime setter is a bare `void(size_t)`, so Lean can't /// `@[extern]` it directly. The pure-Lean parallel checker calls this before diff --git a/crates/ffi/src/lean_build.rs b/crates/ffi/src/lean_build.rs index 94971078f..445a11ba7 100644 --- a/crates/ffi/src/lean_build.rs +++ b/crates/ffi/src/lean_build.rs @@ -25,7 +25,7 @@ //! //! Expression encoding is iterative (explicit frame stack): Init-scale //! terms overflow default runtime stacks (same reason -//! `Ix/Tc/EgressLean.lean` uses a stack machine). +//! `Ix/Kernel/EgressLean.lean` uses a stack machine). //! //! Known hole, deliberate: `DataValue::OfInt` inside mdata is rejected //! with an error. Lean's runtime `Int` is scalar-or-mpz (no exported diff --git a/crates/ixon/src/canon_univ.rs b/crates/ixon/src/canon_univ.rs index 66d347bb5..6bddd28af 100644 --- a/crates/ixon/src/canon_univ.rs +++ b/crates/ixon/src/canon_univ.rs @@ -6,7 +6,7 @@ //! compilers, the Tc egress, and probes can all use it (the three kernel //! `NormLevel` implementations stay untouched and serve as the P4 //! oracle). Mirrors `Ix/IxonUniv.lean`; the Géran machinery is the -//! transliteration of `Ix/Tc/Level.lean:227-472` / +//! transliteration of `Ix/Kernel/Level.lean:227-472` / //! `crates/kernel/src/level.rs` onto `ixon::univ::Univ`. //! //! The frozen kernel `mk*` rule set (M1–M8 / I1–I6, from Lean's @@ -79,7 +79,7 @@ fn peel_offset(u: &Arc) -> (Arc, u64) { } } -/// `mkMax` (Lean `kernel/level.cpp:81-103`; `Ix/Tc/Level.lean` `mkMax`; +/// `mkMax` (Lean `kernel/level.cpp:81-103`; `Ix/Kernel/Level.lean` `mkMax`; /// `crates/kernel/src/level.rs` `KUniv::max`) transliterated to `Univ`. /// First applicable rule wins: M1 numerals → the larger (ties → `a`); /// M2 `max a a = a`; M3/M4 zero sides; M5/M6 absorption; M7 same-base @@ -143,7 +143,7 @@ pub fn n_imax(a: Arc, b: Arc) -> Arc { /// The kernel-rebuild closure on stored trees: bottom-up rebuild through /// the simplifying constructors — exactly what anon/meta ingress does. -/// Mirrors `Ix.Tc.reduceIxonUniv`. A non-fixpoint entry reaches the +/// Mirrors `Ix.Kernel.reduceIxonUniv`. A non-fixpoint entry reaches the /// kernel changed (the stage-1 decoration-presence test), and P6 pins /// that this rebuild refines into the Géran classes. pub fn reduce_univ(u: &Arc) -> Arc { @@ -159,7 +159,7 @@ pub fn reduce_univ(u: &Arc) -> Arc { } // ============================================================================ -// Géran canonical form (Ix/Tc/Level.lean:231-431 on Univ) +// Géran canonical form (Ix/Kernel/Level.lean:231-431 on Univ) // ============================================================================ /// An imax-conditioning chain: sorted param indices. @@ -185,7 +185,7 @@ pub type NormLevel = BTreeMap; /// Insert `(idx, k)` into the sorted var list, max-merging offsets. `k` /// must be the current succ-accumulator (the classic port bug is -/// dropping it — `Ix/Tc/Level.lean:249-252`). +/// dropping it — `Ix/Kernel/Level.lean:249-252`). fn node_add_var(n: &mut Node, idx: u64, k: u64) { match n.vars.iter().position(|v| idx <= v.0) { Some(p) => { @@ -309,7 +309,7 @@ fn is_subset(p2: &[u64], p1: &[u64]) -> bool { } /// Keep only the `xs` entries not dominated by a `ys` entry (merge-walk -/// over sorted var lists). Mirrors `Ix.Tc.Level.subsumeVars`. +/// over sorted var lists). Mirrors `Ix.Kernel.Level.subsumeVars`. fn subsume_vars(xs: &[(u64, u64)], ys: &[(u64, u64)]) -> Vec<(u64, u64)> { let mut out = Vec::new(); let (mut xi, mut yi) = (0, 0); diff --git a/crates/ixon/src/diff.rs b/crates/ixon/src/diff.rs index 3e39b4d5e..2dfd9db11 100644 --- a/crates/ixon/src/diff.rs +++ b/crates/ixon/src/diff.rs @@ -2630,7 +2630,7 @@ mod tests { // ========================================================================== // // Prototype `canonUniv` = draft linearizer ∘ subsumption ∘ normalizeAux - // (the Géran machinery ported from `Ix/Tc/Level.lean:227-472` / + // (the Géran machinery ported from `Ix/Kernel/Level.lean:227-472` / // `crates/kernel/src/level.rs`) plus the frozen kernel `mk*` reducer, // both transliterated onto `ixon::univ::Univ`. Promoted to the real // implementation in stage 2 (the linearizer gating construction is the diff --git a/crates/ixvm-codegen/src/aiur_ixvm_witness.rs b/crates/ixvm-codegen/src/aiur_ixvm_witness.rs index 715ebb2db..6f6f1cfad 100644 --- a/crates/ixvm-codegen/src/aiur_ixvm_witness.rs +++ b/crates/ixvm-codegen/src/aiur_ixvm_witness.rs @@ -217,7 +217,7 @@ pub fn build_claim_check_witness( /// Every primitive address the kernel can name, as the Lean seeder sees /// them: the parity table plus the reserved reduction markers. Built from /// `ix_common::prim_addrs::PrimAddrs`, which the `prim-addrs` parity test -/// pins against `Ix.Tc.PrimAddrs`, so both seeders cover the same set by +/// pins against `Ix.Kernel.PrimAddrs`, so both seeders cover the same set by /// construction. fn prim_addrs() -> Vec
{ let mut v: Vec
= PrimAddrs::lean_parity_table() diff --git a/crates/kernel/src/def_eq.rs b/crates/kernel/src/def_eq.rs index 8e0010007..581e6b6b2 100644 --- a/crates/kernel/src/def_eq.rs +++ b/crates/kernel/src/def_eq.rs @@ -87,9 +87,9 @@ const PROJECTION_PROBE_FUEL: u64 = 4_096; /// Step journal (`IX_STEP_TRACE=1`): one `[deq] ~ ` line /// per `is_def_eq` entry (plus `[whnf+]` lines in whnf.rs), mirroring the -/// Lean kernel's `IX_TC_STEP_TRACE` journal (`Ix.Tc` / `TcM.stepTrace`). +/// Lean kernel's `IX_TC_STEP_TRACE` journal (`Ix.Kernel` / `TcM.stepTrace`). /// Diffing the two sequences localizes a behavioral divergence at the -/// first fork (workflow in `Ix/Tc/ParCheck.lean`). Unscoped by design — +/// first fork (workflow in `Ix/Kernel/ParCheck.lean`). Unscoped by design — /// pair with a seeded single-constant run (`--consts `). pub(crate) static IX_STEP_TRACE: crate::EnvFlag = crate::EnvFlag::new(|| crate::env_var("IX_STEP_TRACE").is_ok()); diff --git a/crates/kernel/src/def_eq/application.rs b/crates/kernel/src/def_eq/application.rs index a0682f29a..1c9b0f77c 100644 --- a/crates/kernel/src/def_eq/application.rs +++ b/crates/kernel/src/def_eq/application.rs @@ -3,7 +3,7 @@ //! Invariant: a Finish frame is reached ONLY after both child comparisons //! succeeded in the frame's original context. Thus every published equality //! follows from application congruence, not from a pending/visited-pair guess. -//! See Ix/Tc/Verify/DefEq/SpineArguments.lean (TrAppSpine.defEq_of_zip) for the +//! See Ix/Kernel/Verify/DefEq/SpineArguments.lean (TrAppSpine.defEq_of_zip) for the //! corresponding semantic rule. That theorem does not certify this Rust loop. //! //! No binders are opened here. All non-App pairs use ordinary conversion, diff --git a/crates/kernel/src/def_eq/binders.rs b/crates/kernel/src/def_eq/binders.rs index 4e7e9f00c..bdb0bad9b 100644 --- a/crates/kernel/src/def_eq/binders.rs +++ b/crates/kernel/src/def_eq/binders.rs @@ -3,7 +3,7 @@ //! Domains are compared under the already-accepted prefix, before adding //! the SAME fresh local to both sides. The terminal bodies are opened once. //! Each accepted binder therefore follows the existing single-binder rule. -//! See Ix/Tc/Verify/DefEq/Structural.lean (quickBinder_wf) and the lamDF / +//! See Ix/Kernel/Verify/DefEq/Structural.lean (quickBinder_wf) and the lamDF / //! forallEDF rules in lean4lean. Those proofs do not certify this Rust loop. //! //! This is not a complete conversion procedure for binder terms: skipping diff --git a/docs/kernel-recursion.md b/docs/kernel-recursion.md index 717c80a87..e769643f4 100644 --- a/docs/kernel-recursion.md +++ b/docs/kernel-recursion.md @@ -160,12 +160,15 @@ that consume no runtime fuel; semantic preservation is a further obligation. ## Validation -Build the implementation proofs and run the kernel regressions: +Build the required kernel proof gate and run the kernel regressions: ```sh -lake build IxKernelVerify +lake build --wfail IxKernelConsistency lake test --wfail -- tc-unit ``` +The legacy named-specification proofs build with `lake build IxKernelVerify`; +they are no longer part of the required PR gate. + The [kernel verification guide](kernel-verification.md#trust-checks) describes the exact trust audits and the complete local validation command. diff --git a/docs/kernel-verification.md b/docs/kernel-verification.md index f4ca8949e..4268caa61 100644 --- a/docs/kernel-verification.md +++ b/docs/kernel-verification.md @@ -599,12 +599,13 @@ non-required `named-spec-verification` workflow (manual dispatch or weekly schedule). The required kernel proof gate is `lake build --wfail IxKernelConsistency`; its audit additionally fails if any `Ix.Theory.Named` module is in the import closure of the consistency roots. -Required CI still builds `IxCompileVerify` on its own. The model has a -separate workflow triggered by changes to the package, interface, or +Required CI builds `IxCompileVerify` on its own with `--wfail`. The model has +a separate workflow triggered by changes to the package, interface, or configuration; the merge queue adds the expensive parity corpus. ```sh -lake build IxKernelVerify IxCompileVerify +lake build --wfail IxCompileVerify +lake build IxKernelVerify # legacy named track, non-strict, not required in CI lake build --wfail IxKernelConsistency lake run check-theory lake run check-certified From a2381cecef8bc52b5d66f6c783cc5dbe2694a10c Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 15 Sep 2026 11:27:13 -0400 Subject: [PATCH 56/63] State the method soundness contracts and their induction Add Consistency/Contracts.lean. SoundReduction: from an invariant state and a readable term with a typed annotated reading, every outcome preserves CheckerInvariant and a successful result reads to an annotated term that is convertible to the source and retains every type of the source. SoundConversion: on two such operands, true establishes ConversionClaim of the readings; false and errors preserve the invariant. SoundInference: full mode returns ScopedModelTyping of source and result; inference-only mode, given an already checked annotated reading, returns a reading of one of its types; errors preserve the invariant. WhnfContract, DefEqContract, and InferContract state these per method field and MethodContracts bundles the six clauses in the layout of MethodsLocalState. MethodContracts.zero proves the exhausted table, which fails every call without touching the state. StepContracts n holds the one-layer obligations of the six bodies installed by methodsN (n + 1) under the depth-n contracts; MethodContracts.succ unfolds methodsN (n + 1) and MethodContracts.methodsN closes every depth by induction. The bodies remain open. Worked instances: DefEqContract.hashPath derives the conversion clause of the hash-equality fast path from isDefEq_hash_state under address faithfulness and annotation agreement of the two readings, and InferContract.sortPath restates CheckerInvariant.inferSort as the full-mode inference clause. The umbrella module imports the new file and the audit pins all twenty roots. --- Ix/Kernel/Verify/Consistency.lean | 6 + Ix/Kernel/Verify/Consistency/Audit.lean | 17 + Ix/Kernel/Verify/Consistency/Contracts.lean | 325 ++++++++++++++++++++ 3 files changed, 348 insertions(+) create mode 100644 Ix/Kernel/Verify/Consistency/Contracts.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index b24c57a4a..976aafc96 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -84,6 +84,7 @@ import Ix.Kernel.Verify.Consistency.Dependencies import Ix.Kernel.Verify.Consistency.Environment import Ix.Kernel.Verify.Consistency.RunAssumptions import Ix.Kernel.Verify.Consistency.Invariant +import Ix.Kernel.Verify.Consistency.Contracts import Ix.Kernel.Verify.Consistency.Audit /-! @@ -348,5 +349,10 @@ outcomes, key computation, binder and let opening, scope exit, cache clearing, per-item reset, and policy changes, and sort and free-variable inference are restated as its preservation. Re-expressing the other supported branches and deriving the atomic run records from it remain open. +The soundness of reduction, conversion, and inference is stated as one mutual +contract per method table over that invariant, closed by induction on the +production method-table depth from the exhausted table and per-depth one-layer +obligations that remain open; the conversion hash path and sort inference are +instances. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index bd55630d2..fbfc79dc5 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -19,6 +19,7 @@ import Ix.Kernel.Verify.Consistency.BetaSourceInference import Ix.Kernel.Verify.Consistency.BetaExposureConstruction import Ix.Kernel.Verify.Consistency.BetaHistoryInference import Ix.Kernel.Verify.Consistency.Invariant +import Ix.Kernel.Verify.Consistency.Contracts import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -1494,6 +1495,19 @@ private def wp1InitialRoots : Array Lean.Name := #[ ``RunAssumptions.initialState, ``RunAssumptions.initialLoopState ] +/-- The per-method soundness contracts over the checker invariant, their +per-table bundle, the exhausted base case, the depth induction, and the +conversion hash-path and sort-inference instances. Every statement mentions +the invariant, so all reach the same expression and level constructions. -/ +private def wp1ContractRoots : Array Lean.Name := #[ + ``ReductionPost, ``ConversionPost, ``FullInferencePost, ``InferenceOnlyPost, + ``SoundReduction, ``SoundConversion, ``SoundInference, + ``WhnfContract, ``DefEqContract, ``InferContract, ``MethodContracts, ``StepContracts, + ``SoundReduction.throw, ``SoundConversion.throw, ``SoundInference.throw, + ``MethodContracts.zero, ``MethodContracts.succ, ``MethodContracts.methodsN, + ``DefEqContract.hashPath, ``InferContract.sortPath +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -1717,6 +1731,9 @@ def roots : Array RootAllowance := #[ ++ wp1InitialRoots.map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative, nameNative], forbiddenDependencies := forbiddenProduction }) + ++ wp1ContractRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], + forbiddenDependencies := forbiddenProduction }) run_cmd Kernel.Verify.Audit.check roots diff --git a/Ix/Kernel/Verify/Consistency/Contracts.lean b/Ix/Kernel/Verify/Consistency/Contracts.lean new file mode 100644 index 000000000..c7291fcab --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Contracts.lean @@ -0,0 +1,325 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Invariant + +/-! +# Method soundness contracts and their induction + +The production checker is a recursive knot. Every table `methodsN (n + 1)` +installs the six one-layer bodies with their recursive callbacks bound to +`methodsN n`; reduction calls conversion and inference, conversion calls +reduction and inference, and inference calls both. Their soundness is +therefore one mutual statement proved by induction on the depth, exactly as +`MethodsLocalState.methodsN` closes the structural contract. + +This module states that mutual statement over the checker state invariant. A +reduction call from an invariant state on a readable term whose annotated +reading is typed returns an invariant state and a readable result convertible +to the source that retains every type of the source. A conversion call +answering `true` on two such operands establishes conversion of their +annotated readings. Full-mode inference returns a typed annotated reading of +the source and its type; inference-only mode returns a type of an annotated +reading the caller has already checked, which is how production uses that +mode. Every outcome, including every error, preserves the invariant. The +contracts are bundled per table in the layout of `MethodsLocalState`, the +exhausted table satisfies them because it fails without touching the state, +and the successor step unfolds `methodsN (n + 1)` to its bodies. The one-layer +obligations remain `StepContracts`; discharging them for the reduction, +conversion, and inference bodies is the remaining conversion and reduction +work. + +Two instances show the shape composes. The hash-equality fast path of +conversion is sound under pairwise address faithfulness and agreement of the +binder annotations of the two readings, since the model interprets binder +conditions. Closed sort inference is the full-mode inference clause restated +from `CheckerInvariant.inferSort`. The contracts carry no collision, size, or +annotation resources of their own; those remain per-call premises, each an +instance of `RunAssumptions`. Matching a memo hit to a caller's annotated +reading needs the memo semantics of the invariant to record that annotation, +which they do not yet. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +section Postconditions + +variable {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (source : Ixon.Env) + (catalog : List (SourceCacheRequest source)) (locals : List FVarId) + (context : Model.Context β) (bounds : List VLevel) + +/-- One reduction outcome at an annotated reading of the source: the invariant +holds afterwards, and a successful result has an annotated reading that is +convertible to the source and retains every type of the source. -/ +def ReductionPost (term : AExpr β) : + EStateM.Result (TcError .anon) (TcState .anon) (KExpr .anon) → Prop + | .ok result after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + ∃ target : AExpr β, readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ + ∀ type, TypingClaim.{u,v} entries context term type → + TypingClaim.{u,v} entries context target type + | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + +/-- One conversion outcome at annotated readings of the two operands: the +invariant holds afterwards, and the answer `true` establishes conversion. -/ +def ConversionPost (left right : AExpr β) : + EStateM.Result (TcError .anon) (TcState .anon) Bool → Prop + | .ok true after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + ConversionClaim.{u,v} entries context left right + | .ok false after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + +/-- One full-mode inference outcome: the invariant holds afterwards, and a +successful result is a typed annotated reading of the source and its type. -/ +def FullInferencePost (input : KExpr .anon) : + EStateM.Result (TcError .anon) (TcState .anon) (KExpr .anon) → Prop + | .ok result after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + ScopedModelTyping.{u,v} resolve entries locals context input result + | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + +/-- One inference-only outcome at an already checked annotated reading of the +source: the invariant holds afterwards, and a successful result reads to a +type of that same annotated term. -/ +def InferenceOnlyPost (term : AExpr β) : + EStateM.Result (TcError .anon) (TcState .anon) (KExpr .anon) → Prop + | .ok result after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + ∃ type : AExpr β, readScopedExpr? resolve locals result = some type.erase ∧ + TypingClaim.{u,v} entries context term type + | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + +end Postconditions + +section Contracts + +variable {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (source : Ixon.Env) + (catalog : List (SourceCacheRequest source)) + +/-- A reduction call is sound in every scope: from an invariant state and a +readable source whose annotated reading is typed, its outcome satisfies +`ReductionPost` at that reading. -/ +def SoundReduction (input : KExpr .anon) (action : TcM .anon (KExpr .anon)) : Prop := + ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (before : TcState .anon) (term : AExpr β), + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before → + readScopedExpr? resolve locals input = some term.erase → + (∃ type, TypingClaim.{u,v} entries context term type) → + ReductionPost.{u,v} resolve anchor entries source catalog locals context bounds term (action before) + +/-- A conversion call is sound in every scope: from an invariant state and two +readable operands whose annotated readings are typed, its outcome satisfies +`ConversionPost` at those readings. -/ +def SoundConversion (left right : KExpr .anon) (action : TcM .anon Bool) : Prop := + ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (before : TcState .anon) (a b : AExpr β), + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before → + readScopedExpr? resolve locals left = some a.erase → + (∃ type, TypingClaim.{u,v} entries context a type) → + readScopedExpr? resolve locals right = some b.erase → + (∃ type, TypingClaim.{u,v} entries context b type) → + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b (action before) + +/-- An inference call is sound in every scope under both checking policies. +Full mode needs only a readable source; inference-only mode is given an +already checked annotated reading of the source. -/ +structure SoundInference (input : KExpr .anon) (action : TcM .anon (KExpr .anon)) : Prop where + full : ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (before : TcState .anon) (reading : VExpr β), + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before → + before.inferOnly = false → + readScopedExpr? resolve locals input = some reading → + FullInferencePost.{u,v} resolve anchor entries source catalog locals context bounds input + (action before) + only : ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (before : TcState .anon) (term type : AExpr β), + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before → + before.inferOnly = true → + readScopedExpr? resolve locals input = some term.erase → + TypingClaim.{u,v} entries context term type → + InferenceOnlyPost.{u,v} resolve anchor entries source catalog locals context bounds term + (action before) + +/-- The reduction contract of a method table, one clause per reduction field. -/ +structure WhnfContract (methods : Methods .anon) : Prop where + whnf : ∀ term, SoundReduction.{u,v} resolve anchor entries source catalog term (methods.whnf term) + whnfCore : ∀ term, + SoundReduction.{u,v} resolve anchor entries source catalog term (methods.whnfCore term) + whnfMode : ∀ term mode, + SoundReduction.{u,v} resolve anchor entries source catalog term (methods.whnfMode term mode) + whnfCoreFlags : ∀ term flags, + SoundReduction.{u,v} resolve anchor entries source catalog term (methods.whnfCoreFlags term flags) + +/-- The conversion contract of a method table. -/ +structure DefEqContract (methods : Methods .anon) : Prop where + isDefEq : ∀ left right, + SoundConversion.{u,v} resolve anchor entries source catalog left right (methods.isDefEq left right) + +/-- The inference contract of a method table. -/ +structure InferContract (methods : Methods .anon) : Prop where + infer : ∀ term, SoundInference.{u,v} resolve anchor entries source catalog term (methods.infer term) + +/-- The six method contracts of one table, in the layout of `MethodsLocalState`. -/ +structure MethodContracts (methods : Methods .anon) : Prop extends + WhnfContract.{u,v} resolve anchor entries source catalog methods, + DefEqContract.{u,v} resolve anchor entries source catalog methods, + InferContract.{u,v} resolve anchor entries source catalog methods + +/-- The one-layer obligations at one depth: each production body that +`methodsN (depth + 1)` installs satisfies its contract whenever its recursive +callbacks `methodsN depth` satisfy theirs. Discharging these fields is the +conversion and reduction work. -/ +structure StepContracts (depth : Nat) : Prop where + whnf : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth) → + ∀ term, SoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnf term).run (methodsN depth)) + whnfCore : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth) → + ∀ term, SoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfCore term).run (methodsN depth)) + whnfMode : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth) → + ∀ term mode, SoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfWithNatSuccMode term mode).run (methodsN depth)) + whnfCoreFlags : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth) → + ∀ term flags, SoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfCoreWithFlags term flags).run (methodsN depth)) + infer : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth) → + ∀ term, SoundInference.{u,v} resolve anchor entries source catalog term + ((RecM.infer term).run (methodsN depth)) + isDefEq : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth) → + ∀ left right, SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEq left right).run (methodsN depth)) + +end Contracts + +section Failure + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} + +/-- A call that fails without touching the state is a sound reduction. -/ +theorem SoundReduction.throw {input : KExpr .anon} {error : TcError .anon} : + SoundReduction.{u,v} resolve anchor entries source catalog input (throw error) := + fun _ _ _ _ _ valid _ _ => valid + +/-- A call that fails without touching the state is a sound conversion. -/ +theorem SoundConversion.throw {left right : KExpr .anon} {error : TcError .anon} : + SoundConversion.{u,v} resolve anchor entries source catalog left right (throw error) := + fun _ _ _ _ _ _ valid _ _ _ _ => valid + +/-- A call that fails without touching the state is a sound inference. -/ +theorem SoundInference.throw {input : KExpr .anon} {error : TcError .anon} : + SoundInference.{u,v} resolve anchor entries source catalog input (throw error) where + full := fun _ _ _ _ _ valid _ _ => valid + only := fun _ _ _ _ _ _ valid _ _ _ => valid + +end Failure + +namespace MethodContracts + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} + +/-- The exhausted table fails every call without touching the state. -/ +theorem zero : + MethodContracts.{u,v} resolve anchor entries source catalog (_root_.Ix.Kernel.methodsN 0) where + whnf := fun _ => SoundReduction.throw + whnfCore := fun _ => SoundReduction.throw + whnfMode := fun _ _ => SoundReduction.throw + whnfCoreFlags := fun _ _ => SoundReduction.throw + isDefEq := fun _ _ => SoundConversion.throw + infer := fun _ => SoundInference.throw + +/-- One induction step: `methodsN (depth + 1)` installs exactly the bodies +whose contracts `StepContracts depth` supplies from the smaller table's. -/ +theorem succ {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog + (_root_.Ix.Kernel.methodsN depth)) + (step : StepContracts.{u,v} resolve anchor entries source catalog depth) : + MethodContracts.{u,v} resolve anchor entries source catalog + (_root_.Ix.Kernel.methodsN (depth + 1)) where + whnf := step.whnf recursive + whnfCore := step.whnfCore recursive + whnfMode := step.whnfMode recursive + whnfCoreFlags := step.whnfCoreFlags recursive + isDefEq := step.isDefEq recursive + infer := step.infer recursive + +/-- Every finite production table satisfies the contracts once every one-layer +obligation is discharged. -/ +theorem methodsN (steps : ∀ depth, StepContracts.{u,v} resolve anchor entries source catalog depth) : + ∀ depth, MethodContracts.{u,v} resolve anchor entries source catalog + (_root_.Ix.Kernel.methodsN depth) + | 0 => zero + | depth + 1 => (methodsN steps depth).succ (steps depth) + +end MethodContracts + +section Instances + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +/-- The hash-equality fast path of conversion. Address faithfulness identifies +the two readings and agreement of their binder annotations identifies the +annotated terms, so the answer `true` is reflexive conversion after only the +statistics update. -/ +theorem DefEqContract.hashPath {left right : KExpr .anon} {a b : AExpr β} + {methods : Methods .anon} {before after : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (faithful : left.AddrFaithful right) (conditions : a.annotations = b.annotations) + (equal : (left.addr == right.addr) = true) + (leftReads : readScopedExpr? resolve locals left = some a.erase) + (rightReads : readScopedExpr? resolve locals right = some b.erase) + (accepted : RecM.isDefEq left right methods before = .ok true after) : + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b + (.ok true after) := by + have sameReading := beq_readScopedExpr? (resolve := resolve) (locals := locals) (depth := 0) + faithful (by rw [KExpr.beq_def]; exact equal) + have erased : a.erase = b.erase := + Option.some.inj (leftReads.symm.trans (sameReading.trans rightReads)) + cases AExpr.eq_of_erase_annotations erased conditions + refine And.intro ?_ (ConversionClaim.refl a) + rw [isDefEq_hash_state equal] at accepted + split at accepted <;> cases accepted + · exact valid.ofMaps ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl valid.coherent rfl rfl + (fun partition => by cases partition <;> rfl) (fun partition => by cases partition <;> rfl) + rfl rfl rfl + · exact valid + +/-- Closed sort inference is the full-mode inference clause at a sort: +`CheckerInvariant.inferSort` returns the invariant and the typed reading. -/ +theorem InferContract.sortPath {level : KUniv .anon} {fuel : Nat} {result : KExpr .anon} + {before after : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (member : SourceCacheRequest.sort level ∈ catalog) + (keyData : SourceCacheKeyData catalog (KExpr.mkSort level)) + (faithful : KExpr.KeyCollisionFree fun term => before.env.intern.ExprSupport term ∨ + term = KExpr.mkSort (KUniv.mkSucc level)) + (accepted : RecM.infer (KExpr.mkSort level) (methodsN fuel) before = .ok result after) : + FullInferencePost.{u,v} resolve anchor entries source catalog locals context bounds + (KExpr.mkSort level) (.ok result after) := by + obtain ⟨preserved, _, typed⟩ := valid.inferSort member keyData faithful accepted + exact And.intro preserved typed + +end Instances + +end Ix.Kernel.Consistency From 6eb11c6846e29889c9d7f539dee23302bf3aa0cf Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 15 Sep 2026 11:36:34 -0400 Subject: [PATCH 57/63] Prove natural-literal inference against the primitive Nat entry Add Consistency/Literals: PrimitiveNatBinding (static binding of the installed primitive Nat address to an admitted entry with a natural fact, monomorphic, closed sort type), its typing/formation/typeReading lemmas, readScopedExpr?_mkConst, readExpr?_mkConst, inferKey_prims, UncachedInference.prims, inferUncached_nat_run, inferUncached_nat_sound, inferUncached_nat_modelTyping, infer_nat_sound, infer_nat_cached_sound, infer_nat_cache_agreement, and infer_nat_cache_frame. Add the literal leaves BinderInference.natLit/cachedNatLit with natOfAgreement, SynthesisInference.natLit, InferenceCacheTrace.nat, OwnedInferenceTrace.nat, and SourceCacheRequest.nat, with natOfKey constructors, BinderInference.natFromSourceCache, and the case of every recursion on those trees (soundness, hereditary meaning, readings, derivations, shapes, coherence, cache execution, frames, events, WHNF history, source and cache preservation). Restate the branch as CheckerInvariant.inferNat, audit every new root, and add literal regression tests. --- Ix/Kernel/Verify/Consistency.lean | 5 + Ix/Kernel/Verify/Consistency/Audit.lean | 41 +++ .../Verify/Consistency/BinderInference.lean | 47 +++ .../Verify/Consistency/BinderMeaning.lean | 3 +- .../Verify/Consistency/CacheExecution.lean | 9 +- .../Consistency/InferenceWhnfHistory.lean | 6 +- Ix/Kernel/Verify/Consistency/Invariant.lean | 77 +++++ Ix/Kernel/Verify/Consistency/Literals.lean | 312 ++++++++++++++++++ .../Verify/Consistency/RecursiveCache.lean | 20 +- .../Verify/Consistency/RecursiveState.lean | 18 +- .../Verify/Consistency/SourceAgreement.lean | 7 +- Ix/Kernel/Verify/Consistency/SourceCache.lean | 65 ++++ .../Consistency/SynthesisCacheExecution.lean | 17 +- .../Consistency/SynthesisCoherence.lean | 11 +- .../Consistency/SynthesisInference.lean | 3 + .../Verify/Consistency/SynthesisReading.lean | 2 +- .../Verify/Consistency/SynthesisShapes.lean | 6 +- .../Verify/Consistency/SynthesisSource.lean | 5 +- .../Verify/Consistency/SynthesisSupport.lean | 6 + Tests/Ix/Kernel/Consistency.lean | 98 +++++- 20 files changed, 742 insertions(+), 16 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/Literals.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 976aafc96..bccfb17f9 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -15,6 +15,7 @@ import Ix.Kernel.Verify.Consistency.ScopedInstUniv import Ix.Kernel.Verify.Consistency.ScopedConstant import Ix.Kernel.Verify.Consistency.InferenceCache import Ix.Kernel.Verify.Consistency.SortCache +import Ix.Kernel.Verify.Consistency.Literals import Ix.Kernel.Verify.Consistency.ConstantCache import Ix.Kernel.Verify.Consistency.LazyCache import Ix.Kernel.Verify.Consistency.BlockCache @@ -354,5 +355,9 @@ contract per method table over that invariant, closed by induction on the production method-table depth from the exhausted table and per-depth one-layer obligations that remain open; the conversion hash path and sort inference are instances. +Natural-number literals infer to the interned primitive `Nat` constant; a +static binding of that address to an admitted entry with a `natural` fact +types the literal in both cache partitions, through the synthesis, cache +trace, source-cache, and history recursions, and as invariant preservation. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index fbfc79dc5..6c1fd93bc 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -1508,6 +1508,38 @@ private def wp1ContractRoots : Array Lean.Name := #[ ``DefEqContract.hashPath, ``InferContract.sortPath ] +/-- The static primitive `Nat` binding and its address transport are pure data. -/ +private def literalPureRoots : Array Lean.Name := #[ + ``PrimitiveNatBinding, ``PrimitiveNatBinding.ofPrims, ``PrimitiveNatBinding.ofPrims_ref, + ``PrimitiveNatBinding.ofPrims_level +] + +/-- The model literal rule and the bound entry's formation reach only the set model. -/ +private def literalModelRoots : Array Lean.Name := #[ + ``PrimitiveNatBinding.typing, ``PrimitiveNatBinding.formation +] + +/-- Literal readings and key computation reach expression construction only. -/ +private def literalExprRoots : Array Lean.Name := #[ + ``PrimitiveNatBinding.typeReading, ``readScopedExpr?_mkConst, ``readExpr?_mkConst, + ``inferKey_prims, ``UncachedInference.prims +] + +/-- Natural-number literal inference at both cache partitions, the literal +leaves of the inference, cache-trace, and source-cache trees (audited with +their sort siblings' universe construction), and the restatement as invariant +preservation. -/ +private def literalRoots : Array Lean.Name := #[ + ``inferUncached_nat_run, ``inferUncached_nat_sound, ``inferUncached_nat_modelTyping, + ``infer_nat_sound, ``infer_nat_cached_sound, ``infer_nat_cache_agreement, ``infer_nat_cache_frame, + ``BinderInference.natLit, ``BinderInference.cachedNatLit, ``BinderInference.natOfAgreement, + ``SynthesisInference.natLit, ``SynthesisCacheSupplement.natOfKey, + ``InferenceCacheTrace.nat, ``InferenceCacheTrace.natOfKey, + ``OwnedInferenceTrace.nat, ``OwnedInferenceTrace.natOfKey, + ``SourceCacheRequest.nat, ``BinderInference.natFromSourceCache, + ``CheckerInvariant.inferNat +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -1734,6 +1766,15 @@ def roots : Array RootAllowance := #[ ++ wp1ContractRoots.map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) + ++ literalPureRoots.map (fun root => { root, forbiddenDependencies := forbiddenProduction }) + ++ literalModelRoots.map (fun root => { + root, standardAxioms := standard, forbiddenDependencies := forbiddenProduction }) + ++ literalExprRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative], + forbiddenDependencies := forbiddenProduction }) + ++ literalRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], + forbiddenDependencies := forbiddenProduction }) run_cmd Kernel.Verify.Audit.check roots diff --git a/Ix/Kernel/Verify/Consistency/BinderInference.lean b/Ix/Kernel/Verify/Consistency/BinderInference.lean index aa49303bf..5b86b0307 100644 --- a/Ix/Kernel/Verify/Consistency/BinderInference.lean +++ b/Ix/Kernel/Verify/Consistency/BinderInference.lean @@ -6,6 +6,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 import Ix.Kernel.Verify.Consistency.Application import Ix.Kernel.Verify.Consistency.ConstantCache import Ix.Kernel.Verify.Consistency.SortCache +import Ix.Kernel.Verify.Consistency.Literals import Ix.Theory.Model.Checking /-! @@ -348,6 +349,20 @@ inductive BinderInference {β : Type u} (canonical : hit.cached = KExpr.mkSort (KUniv.mkSucc level)) : BinderInference resolve entries locals context fuel before (.sort level info) (.sort (readLevel level)) (.sort (.succ (readLevel level))) + | natLit {locals context fuel before value blob info} + (miss : UncachedInference before (.nat value blob info)) + (binding : PrimitiveNatBinding resolve entries before.prims) + (coherent : miss.keyed.env.intern.WF) + (faithful : KExpr.KeyCollisionFree fun term => + miss.keyed.env.intern.ExprSupport term ∨ term = KExpr.mkConst before.prims.nat #[]) : + BinderInference resolve entries locals context fuel before (.nat value blob info) + (.natLit value) (.const binding.ref []) + | cachedNatLit {locals context fuel before value blob info} + (hit : InferenceCacheHit before (.nat value blob info)) + (binding : PrimitiveNatBinding resolve entries before.prims) + (canonical : hit.cached = KExpr.mkConst before.prims.nat #[]) : + BinderInference resolve entries locals context fuel before (.nat value blob info) + (.natLit value) (.const binding.ref []) | fvar {locals context fuel before id name info index A} (cache : FVarInferenceSupport before id name info) (registered : localIndex? locals id = some index) @@ -454,6 +469,28 @@ def BinderInference.sortOfAgreement {β : Type u} · exact .sort miss (by simpa only [stateEq] using coherent) (by simpa only [stateEq] using faithful) +/-- Construct the literal leaf from a maintained cache invariant at its key. +The production key and eligible hit/miss observation are derived. -/ +def BinderInference.natOfAgreement {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {before : TcState .anon} {value : Nat} {blob : Address} {info : ExprInfo .anon} + (closed : (KExpr.nat value blob info).lbr = 0) + (binding : PrimitiveNatBinding resolve entries before.prims) + (agreement : InferenceCacheAgreement before ((KExpr.nat value blob info).addr, emptyCtxAddr) + (KExpr.mkConst before.prims.nat #[])) + (coherent : before.env.intern.WF) + (faithful : KExpr.KeyCollisionFree fun term => before.env.intern.ExprSupport term ∨ + term = KExpr.mkConst before.prims.nat #[]) : + BinderInference resolve entries locals context fuel before (.nat value blob info) + (.natLit value) (.const binding.ref []) := by + rcases observeInferenceCache (inferKey_closed closed before) with + ⟨hit, keyEq, stateEq⟩ | ⟨miss, _, stateEq⟩ + · refine .cachedNatLit hit binding (InferenceCacheAgreement.selected hit ?_) + simpa only [keyEq, stateEq] using agreement + · exact .natLit miss binding (by simpa only [stateEq] using coherent) + (by simpa only [stateEq] using faithful) + /-- Successful production inference reads the expected model type and checks the actual source term against it. No recursive semantic premise is supplied by the caller: induction follows the finite operational support tree. -/ @@ -481,6 +518,16 @@ theorem BinderInference.soundWithSynthesis {β : Type u} | cachedSort hit canonical => obtain ⟨typeReads, typed⟩ := infer_sort_cached_sound hit canonical accepted exact ⟨typeReads, typed.checking, fun head => by cases head⟩ + | @natLit locals context fuel before value blob info miss binding coherent faithful => + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + obtain ⟨_, _, typeReads, typed⟩ := inferUncached_nat_sound (context := context) locals + (binding.ofPrims (congrArg Primitives.nat miss.prims.symm)) coherent + (by rw [miss.prims]; exact faithful) run + exact ⟨typeReads, typed.checking, fun head => by cases head⟩ + | @cachedNatLit locals context fuel before value blob info hit binding canonical => + obtain ⟨typeReads, typed⟩ := + infer_nat_cached_sound (context := context) hit binding canonical accepted + exact ⟨typeReads, typed.checking, fun head => by cases head⟩ | fvar cache registered atIndex => obtain ⟨typeReads, typed⟩ := cache.sound agreement registered atIndex accepted exact ⟨typeReads, typed.checking, fun _ => typed⟩ diff --git a/Ix/Kernel/Verify/Consistency/BinderMeaning.lean b/Ix/Kernel/Verify/Consistency/BinderMeaning.lean index d0739a647..e3fb7edfc 100644 --- a/Ix/Kernel/Verify/Consistency/BinderMeaning.lean +++ b/Ix/Kernel/Verify/Consistency/BinderMeaning.lean @@ -23,7 +23,8 @@ theorem BinderInference.hereditary {β : Type u} {resolve : Address → Option ( readScopedExpr? resolve locals source = some term.erase → HereditaryTyping.{u,v} entries context term type := match support with - | .sort .. | .cachedSort .. | .const .. | .polymorphic .. | .cachedConst .. => + | .sort .. | .cachedSort .. | .natLit .. | .cachedNatLit .. | .const .. | .polymorphic .. | + .cachedConst .. => fun typed _ _ => .atom typed (by constructor) | .fvar _ _ atIndex => fun typed _ _ => .bvar typed atIndex | .forallE miss trace opening domainTree bodyTree .. => fun typed agreement reading => by diff --git a/Ix/Kernel/Verify/Consistency/CacheExecution.lean b/Ix/Kernel/Verify/Consistency/CacheExecution.lean index 356dcf995..87715ed7a 100644 --- a/Ix/Kernel/Verify/Consistency/CacheExecution.lean +++ b/Ix/Kernel/Verify/Consistency/CacheExecution.lean @@ -107,7 +107,7 @@ def InferenceCacheTrace.events {fuel : Nat} {before after : TcState .anon} (accepted : RecM.infer term (methodsN fuel) before = .ok result after) : List InferenceCacheEvent := match tree with | .hit _ => [] - | .sort miss | .fvar miss | .const miss .. | .lazyConst miss .. => [.ofRun miss accepted] + | .sort miss | .fvar miss | .nat miss | .const miss .. | .lazyConst miss .. => [.ofRun miss accepted] | .app _ miss trace _ functionTree argumentTree => functionTree.events trace.functionRun ++ argumentTree.events trace.argumentRun ++ [.ofRun miss accepted] | .appBeta _ miss trace _ _ functionTree argumentTree => @@ -133,7 +133,7 @@ termination_by structural tree /-- Actual child publications, before the enclosing miss writes its result. -/ def InferenceCacheTrace.childEvents {fuel : Nat} {before : TcState .anon} {source : KExpr .anon} : InferenceCacheTrace.{u} fuel before source → List InferenceCacheEvent - | .hit _ | .sort _ | .fvar _ | .const .. | .lazyConst .. => [] + | .hit _ | .sort _ | .fvar _ | .nat _ | .const .. | .lazyConst .. => [] | .app _ _ trace _ first second => first.events trace.functionRun ++ second.events trace.argumentRun | .appBeta _ _ trace _ _ first second => first.events trace.functionRun ++ second.events trace.argumentRun | .forallE _ trace first second => first.events trace.domainRun ++ second.events trace.bodyRun @@ -223,6 +223,11 @@ theorem InferenceCacheTrace.cache_maps {fuel : Nat} {before after : TcState .ano · cases run exact ⟨rfl, rfl⟩ · contradiction + | @nat fuel before value blob info miss => + apply miss_maps miss accepted [] + intro middle run + obtain ⟨_, rfl⟩ := inferUncached_nat_run run + exact ⟨rfl, rfl⟩ | const miss concrete loaded resources => apply miss_maps miss accepted [] intro middle run diff --git a/Ix/Kernel/Verify/Consistency/InferenceWhnfHistory.lean b/Ix/Kernel/Verify/Consistency/InferenceWhnfHistory.lean index 5eb0f211c..5cbd5288b 100644 --- a/Ix/Kernel/Verify/Consistency/InferenceWhnfHistory.lean +++ b/Ix/Kernel/Verify/Consistency/InferenceWhnfHistory.lean @@ -17,7 +17,7 @@ universe u def InferenceCacheTrace.WhnfData (β : Type u) {fuel : Nat} {state : TcState .anon} {source : KExpr .anon} : InferenceCacheTrace.{u} fuel state source → Type (u + 1) - | .hit .. | .sort .. | .fvar .. | .const .. | .lazyConst .. => PUnit + | .hit .. | .sort .. | .fvar .. | .nat .. | .const .. | .lazyConst .. => PUnit | .app _ _ _ _ first second | .forallE _ _ first second | .lam _ _ _ first second | .lamBody _ _ _ first second => first.WhnfData β × second.WhnfData β | .appBeta _ _ trace _ _ first second => @@ -67,6 +67,10 @@ noncomputable def InferenceCacheTrace.whnfHistory {β : Type u} {fuel : Nat} · cases run exact keyed · contradiction + | .nat miss => fun _ history accepted => + history.afterMiss miss accepted fun middle run keyed => by + obtain ⟨_, rfl⟩ := inferUncached_nat_run run + exact keyed.intern _ | .const miss concrete loaded resources => fun _ history accepted => history.afterMiss miss accepted fun middle run keyed => by apply Classical.choice diff --git a/Ix/Kernel/Verify/Consistency/Invariant.lean b/Ix/Kernel/Verify/Consistency/Invariant.lean index 0aa9db9fd..4e64ae2e6 100644 --- a/Ix/Kernel/Verify/Consistency/Invariant.lean +++ b/Ix/Kernel/Verify/Consistency/Invariant.lean @@ -898,6 +898,83 @@ theorem inferFVar {id : FVarId} {name : Mode.anon.F Name} {info : ExprInfo .anon semantics := valid.semantics.ofMaps (fun partition => by cases partition <;> rfl) (fun partition => by cases partition <;> rfl) rfl rfl rfl } +/-! ### Demonstration: natural-number literal inference -/ + +/-- A successful closed-literal inference changes only the intern table and +the two inference maps. -/ +private theorem infer_nat_state {value : Nat} {fuel : Nat} {result : KExpr .anon} + {before after : TcState .anon} + (accepted : RecM.infer (KExpr.mkNatLit value) (methodsN fuel) before = .ok result after) : + ∃ table full only, after = {before with env := {before.env with + intern := table, inferCache := full, inferOnlyCache := only}} := by + have keyRun := inferKey_closed (term := KExpr.mkNatLit value) rfl before + rcases observeInferenceCache keyRun with ⟨hit, _, stateEq⟩ | ⟨miss, _, stateEq⟩ + · rw [hit.run (methodsN fuel)] at accepted + cases accepted + rw [stateEq] + exact ⟨before.env.intern, before.env.inferCache, before.env.inferOnlyCache, rfl⟩ + · obtain ⟨state, run, written⟩ := infer_uncached_success_state miss accepted + rw [stateEq] at run + obtain ⟨rfl, rfl⟩ := inferUncached_nat_run run + rw [written] + cases policy : before.inferOnly + · simp only [Bool.false_eq_true, ↓reduceIte] + exact ⟨_, _, before.env.inferOnlyCache, rfl⟩ + · simp only [↓reduceIte] + exact ⟨_, before.env.inferCache, _, rfl⟩ + +/-- Literal inference preserves the invariant and returns the primitive `Nat` +constant, whose typing comes from the static binding of that address; the +returned tree is derived from the maintained catalog agreement on a hit and +from the actual intern step on a miss. Every catalog request for this literal +names the run's primitive. -/ +theorem inferNat {value : Nat} {fuel : Nat} {result : KExpr .anon} {before after : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (binding : PrimitiveNatBinding resolve entries before.prims) + (member : SourceCacheRequest.nat value before.prims.nat ∈ catalog) + (unique : ∀ prim, SourceCacheRequest.nat value prim ∈ catalog → prim = before.prims.nat) + (keyData : SourceCacheKeyData catalog (KExpr.mkNatLit value)) + (faithful : KExpr.KeyCollisionFree fun term => before.env.intern.ExprSupport term ∨ + term = KExpr.mkConst before.prims.nat #[]) + (accepted : RecM.infer (KExpr.mkNatLit value) (methodsN fuel) before = .ok result after) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + result = KExpr.mkConst before.prims.nat #[] ∧ + ScopedModelTyping.{u,v} resolve entries locals context (KExpr.mkNatLit value) result := by + have keyRun := inferKey_closed (term := KExpr.mkNatLit value) rfl before + obtain ⟨canonical, _⟩ := infer_nat_cache_agreement keyRun + (valid.cache (.nat value before.prims.nat) member).correct valid.coherent faithful accepted + refine ⟨?_, canonical, ⟨.natLit value, .const binding.ref [], rfl, + by rw [canonical]; exact binding.typeReading locals, binding.typing context value⟩⟩ + have sourceCache : SourceCacheInvariant catalog after := by + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, _, stateEq⟩ + · exact (OwnedInferenceTrace.hit (fuel := fuel) hit).preservesSourceCache valid.sourceCache + trivial trivial accepted + · exact (OwnedInferenceTrace.nat (fuel := fuel) miss).preservesSourceCache valid.sourceCache + trivial ⟨keyData, by rw [stateEq]; exact unique, by rw [stateEq]; exact faithful⟩ accepted + have synthesis : Nonempty (SynthesisCacheHistory resolve anchor entries after) := by + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, _, stateEq⟩ + · exact valid.synthesis.elim fun history => + ⟨history.afterInference (InferenceCacheTrace.hit hit : InferenceCacheTrace.{0} fuel before _) + accepted .nil⟩ + · refine valid.synthesis.elim fun history => valid.origin.elim fun origin => ?_ + have tree : SynthesisInference resolve entries locals context bounds fuel before + (KExpr.mkNatLit value) (.natLit value) (.const binding.ref []) binding.level := + .natLit binding (.natLit miss binding (by rw [stateEq]; exact valid.coherent) + (by rw [stateEq]; exact faithful)) + exact ⟨history.afterInference + (InferenceCacheTrace.nat miss : InferenceCacheTrace.{0} fuel before _) accepted + (.singleton (SynthesisEventCheck.ofSource tree origin valid.reading rfl miss accepted))⟩ + obtain ⟨table, full, only, stateEq⟩ := infer_nat_state accepted + subst stateEq + exact { + sourceCache, synthesis + whnf := valid.whnf.elim fun history => ⟨history.ofMaps fun partition => by cases partition <;> rfl⟩ + structural := ⟨valid.structural.coherent, valid.structural.allocated, valid.structural.loader⟩ + reading := valid.reading + origin := valid.origin + semantics := valid.semantics.ofMaps (fun partition => by cases partition <;> rfl) + (fun partition => by cases partition <;> rfl) rfl rfl rfl } + end CheckerInvariant end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Literals.lean b/Ix/Kernel/Verify/Consistency/Literals.lean new file mode 100644 index 000000000..7e5f78e81 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Literals.lean @@ -0,0 +1,312 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.InferenceCache + +/-! +# Natural-number literal inference + +The production dispatcher types a literal by the interned constant at the +primitive `Nat` address installed in the checker state. `PrimitiveNatBinding` +is the static binding of that address to a model entry carrying a `natural` +fact: like `StandaloneModelBinding` it inspects no mutable checker state, and +the final theorem will discharge it from the admission of the primitive `Nat` +block. The uncached branch is refined exactly; the public branch is refined +through both cache partitions, with agreement and frame lemmas at the +literal's key mirroring the sort ones. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-- The primitive `Nat` address resolves to an admitted entry whose facts +contain the natural-number producer. The entry is monomorphic, and its type is +a closed sort so the literal's returned type has a formation level. The +binding is static data about the resolver, interface, and primitive table, +like `StandaloneModelBinding`: it inspects no checker state or cache. The +final theorem discharges it from the admission of the primitive `Nat` block +and its model-side facts (plan items WP5 and WP6); until then it is a premise +of every literal-inference statement. -/ +structure PrimitiveNatBinding {β : Type u} {m : Mode} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (prims : Primitives m) where + ref : ConstRef β + entry : ConstantEntry β + zero : ConstRef β + succ : ConstRef β + level : VLevel + resolved : resolve prims.nat.addr = some ref + found : entries ref = some entry + natural : .natural zero succ ∈ entry.facts + monomorphic : entry.universes = 0 + typeSort : entry.type = .sort level + closed : level.WF 0 + +namespace PrimitiveNatBinding + +variable {β : Type u} {m : Mode} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {prims : Primitives m} + +/-- The binding depends on the primitive table only through the `Nat` address. -/ +def ofPrims (binding : PrimitiveNatBinding resolve entries prims) {prims' : Primitives m} + (same : prims.nat = prims'.nat) : PrimitiveNatBinding resolve entries prims' := + { binding with resolved := by rw [← same]; exact binding.resolved } + +@[simp] theorem ofPrims_ref (binding : PrimitiveNatBinding resolve entries prims) + {prims' : Primitives m} (same : prims.nat = prims'.nat) : + (binding.ofPrims same).ref = binding.ref := rfl + +@[simp] theorem ofPrims_level (binding : PrimitiveNatBinding resolve entries prims) + {prims' : Primitives m} (same : prims.nat = prims'.nat) : + (binding.ofPrims same).level = binding.level := rfl + +/-- The model's literal rule at the bound entry. -/ +theorem typing (binding : PrimitiveNatBinding resolve entries prims) + (context : Model.Context β) (value : Nat) : + TypingClaim.{u,v} entries context (.natLit value) (.const binding.ref []) := + TypingClaim.natLit binding.found binding.natural binding.monomorphic value + +/-- The literal's type is formed at the entry's closed sort level. -/ +theorem formation (binding : PrimitiveNatBinding resolve entries prims) (context : Model.Context β) : + TypingClaim.{u,v} entries context (.const binding.ref []) (.sort binding.level) := by + have typed := TypingClaim.const (Γ := context) (ls := []) binding.found + (by simpa using binding.monomorphic.symm) + have stable : binding.entry.type.instL [] = .sort binding.level := by + rw [binding.typeSort] + change AExpr.sort (binding.level.inst []) = _ + rw [show ([] : List VLevel) = VLevel.params 0 from rfl, VLevel.inst_id binding.closed] + simpa only [stable] using typed + +end PrimitiveNatBinding + +/-- The production constant smart constructor reads as the resolved reference +with its universe arguments, in every local context. -/ +@[simp] theorem readScopedExpr?_mkConst (resolve : Address → Option (ConstRef β)) + (locals : List FVarId) (id : KId m) (levels : Array (KUniv m)) (depth : Nat) : + readScopedExpr? resolve locals (KExpr.mkConst id levels) depth = do + return .const (← resolve id.addr) (levels.toList.map readLevel) := rfl + +@[simp] theorem readExpr?_mkConst (resolve : Address → Option (ConstRef β)) + (id : KId m) (levels : Array (KUniv m)) : + readExpr? resolve (KExpr.mkConst id levels) = do + return .const (← resolve id.addr) (levels.toList.map readLevel) := rfl + +/-- The bound `Nat` constant reads as the bound reference. -/ +theorem PrimitiveNatBinding.typeReading {β : Type u} {m : Mode} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {prims : Primitives m} (binding : PrimitiveNatBinding resolve entries prims) + (locals : List FVarId) (depth : Nat := 0) : + readScopedExpr? resolve locals (KExpr.mkConst prims.nat #[]) depth = + some (AExpr.const binding.ref []).erase := by + simp [binding.resolved, AExpr.erase] + +/-- Key computation changes only the context-digest memo; the primitive table +installed at construction is retained. -/ +theorem inferKey_prims {term : KExpr .anon} {before after : TcState .anon} + {key : Address × Address} + (run : TcM.inferKey term before = .ok key after) : after.prims = before.prims := by + unfold TcM.inferKey at run + change EStateM.bind (TcM.ctxAddrForLbr term.lbr) _ before = _ at run + unfold TcM.ctxAddrForLbr at run + change EStateM.bind (fun state => EStateM.bind (get : TcM .anon (TcState .anon)) + _ state) _ before = _ at run + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = + .ok before before from rfl] at run + by_cases fast : (term.lbr == 0 || before.ctx.isEmpty) = true + · rw [if_pos fast] at run + cases run; rfl + · rw [if_neg fast] at run + cases cached : before.ctxAddrCache[(before.ctxId, term.lbr)]? with + | none => rw [cached] at run; cases run; rfl + | some address => rw [cached] at run; cases run; rfl + +theorem UncachedInference.prims {term : KExpr .anon} {before : TcState .anon} + (miss : UncachedInference before term) : miss.keyed.prims = before.prims := + inferKey_prims miss.keyRun + +/-- The exact operational content of the literal branch: it interns the +constant at the installed primitive `Nat` address and updates only the intern +table. -/ +theorem inferUncached_nat_run {m : Mode} + {inferRec : KExpr m → RecM m (KExpr m)} {inferOnly : Bool} + {methods : Methods m} {before after : TcState m} + {value : Nat} {blob : Address} {info : ExprInfo m} {type : KExpr m} + (accepted : RecM.inferUncached inferRec inferOnly (.nat value blob info) methods before = + .ok type after) : + type = (before.env.intern.internExpr (KExpr.mkConst before.prims.nat #[])).1 ∧ + after = { before with env := { before.env with intern := + (before.env.intern.internExpr (KExpr.mkConst before.prims.nat #[])).2 } } := by + change EStateM.Result.ok + (before.env.intern.internExpr (KExpr.mkConst before.prims.nat #[])).1 + { before with env := { before.env with intern := + (before.env.intern.internExpr (KExpr.mkConst before.prims.nat #[])).2 } } = + .ok type after at accepted + cases accepted + exact ⟨rfl, rfl⟩ + +/-- Every successful execution of the production literal branch returns the +interned primitive `Nat` constant, whose scoped reading is the bound +reference, and the model literal rule types the source at that reference. +The intern-table premises are its concrete key coherence and collision +freedom on the table support plus the newly constructed constant. -/ +theorem inferUncached_nat_sound {β : Type u} {m : Mode} + {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} (locals : List FVarId) + {inferRec : KExpr m → RecM m (KExpr m)} {inferOnly : Bool} + {methods : Methods m} {before after : TcState m} + {value : Nat} {blob : Address} {info : ExprInfo m} {type : KExpr m} + (binding : PrimitiveNatBinding resolve entries before.prims) + (coherent : before.env.intern.WF) + (faithful : KExpr.KeyCollisionFree fun e => + before.env.intern.ExprSupport e ∨ e = KExpr.mkConst before.prims.nat #[]) + (accepted : RecM.inferUncached inferRec inferOnly (.nat value blob info) methods before = + .ok type after) : + type = (before.env.intern.internExpr (KExpr.mkConst before.prims.nat #[])).1 ∧ + after = { before with env := { before.env with intern := + (before.env.intern.internExpr (KExpr.mkConst before.prims.nat #[])).2 } } ∧ + readScopedExpr? resolve locals type = some (AExpr.const binding.ref []).erase ∧ + TypingClaim.{u,v} entries context (.natLit value) (.const binding.ref []) := by + obtain ⟨rfl, rfl⟩ := inferUncached_nat_run accepted + refine ⟨rfl, rfl, ?_, binding.typing context value⟩ + rw [internExpr_readScopedExpr? coherent faithful] + exact binding.typeReading locals + +/-- The same branch in the closed model-typing postcondition used by the +sort branch. -/ +theorem inferUncached_nat_modelTyping {β : Type u} {m : Mode} + {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {context : Model.Context β} + {inferRec : KExpr m → RecM m (KExpr m)} {inferOnly : Bool} + {methods : Methods m} {before after : TcState m} + {value : Nat} {blob : Address} {info : ExprInfo m} {type : KExpr m} + (binding : PrimitiveNatBinding resolve entries before.prims) + (coherent : before.env.intern.WF) + (faithful : KExpr.KeyCollisionFree fun e => + before.env.intern.ExprSupport e ∨ e = KExpr.mkConst before.prims.nat #[]) + (accepted : RecM.inferUncached inferRec inferOnly (.nat value blob info) methods before = + .ok type after) : + ModelTyping.{u,v} resolve entries context (.nat value blob info) type := by + obtain ⟨rfl, rfl⟩ := inferUncached_nat_run accepted + refine ⟨.natLit value, .const binding.ref [], rfl, ?_, binding.typing context value⟩ + rw [internExpr_readExpr? coherent faithful] + simp [binding.resolved, AExpr.erase] + +/-- Literal inference through both cache lookups and the final cache write, +under the same explicit miss boundary as constant inference. In anonymous mode +the interned constant is exactly the smart-constructed one. -/ +theorem infer_nat_sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} + {value : Nat} {blob : Address} {info : ExprInfo .anon} + {methods : Methods .anon} {before after : TcState .anon} {result : KExpr .anon} + (miss : UncachedInference before (.nat value blob info)) + (binding : PrimitiveNatBinding resolve entries before.prims) + (coherent : miss.keyed.env.intern.WF) + (faithful : KExpr.KeyCollisionFree fun term => miss.keyed.env.intern.ExprSupport term ∨ + term = KExpr.mkConst before.prims.nat #[]) + (accepted : RecM.infer (.nat value blob info) methods before = .ok result after) : + result = KExpr.mkConst before.prims.nat #[] ∧ + readScopedExpr? resolve locals result = some (AExpr.const binding.ref []).erase ∧ + ScopedModelTyping.{u,v} resolve entries locals context (.nat value blob info) result := by + obtain ⟨state, run⟩ := infer_uncached_success miss accepted + have prims := miss.prims + obtain ⟨rfl, rfl⟩ := inferUncached_nat_run run + rw [prims] + have canonical := miss.keyed.env.intern.internExpr_eraseMeta coherent faithful + simp only [KExpr.eraseMeta_anon] at canonical + refine ⟨canonical, ?_, ?_⟩ + · rw [canonical] + exact binding.typeReading locals + · refine ⟨.natLit value, .const binding.ref [], rfl, ?_, binding.typing context value⟩ + rw [canonical] + exact binding.typeReading locals + +/-- A cached literal whose entry is the primitive `Nat` constant synthesizes +that constant under any active locals. No intern-table resources are needed +when the selected result is cached. -/ +theorem infer_nat_cached_sound {β : Type u} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} + {before after : TcState .anon} {value : Nat} {blob : Address} {info : ExprInfo .anon} + {methods : Methods .anon} {result : KExpr .anon} + (hit : InferenceCacheHit before (.nat value blob info)) + (binding : PrimitiveNatBinding resolve entries before.prims) + (canonical : hit.cached = KExpr.mkConst before.prims.nat #[]) + (accepted : RecM.infer (.nat value blob info) methods before = .ok result after) : + readScopedExpr? resolve locals result = some (AExpr.const binding.ref []).erase ∧ + TypingClaim.{u,v} entries context (.natLit value) (.const binding.ref []) := by + rw [hit.run methods] at accepted + cases accepted + rw [canonical] + exact ⟨binding.typeReading locals, binding.typing context value⟩ + +/-- Both policies return the exact primitive `Nat` constant and retain +agreement at the literal's key. The miss branch establishes this through +interning and the real cache write; the hit branch reads it from the +maintained invariant. -/ +theorem infer_nat_cache_agreement {before keyed after : TcState .anon} + {value : Nat} {blob : Address} {info : ExprInfo .anon} {key : Address × Address} + {methods : Methods .anon} {result : KExpr .anon} + (keyRun : TcM.inferKey (.nat value blob info) before = .ok key keyed) + (agreement : InferenceCacheAgreement keyed key (KExpr.mkConst before.prims.nat #[])) + (coherent : keyed.env.intern.WF) + (faithful : KExpr.KeyCollisionFree fun term => keyed.env.intern.ExprSupport term ∨ + term = KExpr.mkConst before.prims.nat #[]) + (accepted : RecM.infer (.nat value blob info) methods before = .ok result after) : + result = KExpr.mkConst before.prims.nat #[] ∧ + InferenceCacheAgreement after key (KExpr.mkConst before.prims.nat #[]) := by + have prims := inferKey_prims keyRun + rcases observeInferenceCache keyRun with ⟨hit, keyEq, stateEq⟩ | ⟨miss, keyEq, stateEq⟩ + · rw [hit.run methods] at accepted + cases accepted + refine ⟨InferenceCacheAgreement.selected hit ?_, ?_⟩ + · simpa only [keyEq, stateEq] using agreement + · simpa only [stateEq] using agreement + · obtain ⟨state, run, written⟩ := infer_uncached_success_state miss accepted + rw [stateEq] at run + obtain ⟨rfl, rfl⟩ := inferUncached_nat_run run + have keyedFaithful : KExpr.KeyCollisionFree fun term => keyed.env.intern.ExprSupport term ∨ + term = KExpr.mkConst keyed.prims.nat #[] := by + rw [prims]; exact faithful + have canonical := keyed.env.intern.internExpr_eraseMeta coherent keyedFaithful + simp only [KExpr.eraseMeta_anon] at canonical + refine ⟨canonical.trans (by rw [prims]), ?_⟩ + have unchanged : InferenceCacheAgreement + { keyed with env := { keyed.env with intern := + (keyed.env.intern.internExpr (KExpr.mkConst keyed.prims.nat #[])).2 } } + key (KExpr.mkConst before.prims.nat #[]) := ⟨agreement.full, agreement.only⟩ + apply unchanged.write (policy := before.inferOnly) (methods := methods) + rw [cacheInferResult_eq] + rw [keyEq, canonical] at written + rw [written, prims] + +/-- A successful literal call preserves every other cache key and all loaded +declarations. This frame is operational and needs no typing or cache +agreement premise, even when the call writes a new result at its own key. -/ +theorem infer_nat_cache_frame {before keyed after : TcState .anon} + {value : Nat} {blob : Address} {info : ExprInfo .anon} {key other : Address × Address} + {methods : Methods .anon} {result : KExpr .anon} + (keyRun : TcM.inferKey (.nat value blob info) before = .ok key keyed) + (different : key ≠ other) + (accepted : RecM.infer (.nat value blob info) methods before = .ok result after) : + InferenceCacheFrame other before after := by + have keyFrame := PreservesInferenceCache.inferKey other (.nat value blob info) before + rw [keyRun] at keyFrame + rcases observeInferenceCache keyRun with ⟨hit, keyEq, stateEq⟩ | ⟨miss, keyEq, stateEq⟩ + · rw [hit.run methods] at accepted + cases accepted + simpa only [stateEq] using keyFrame + · obtain ⟨state, run, written⟩ := infer_uncached_success_state miss accepted + rw [stateEq] at run + obtain ⟨rfl, rfl⟩ := inferUncached_nat_run run + apply keyFrame.trans + rw [keyEq] at written + rw [written] + cases policy : before.inferOnly <;> apply InferenceCacheFrame.of_eq <;> + simp [Std.HashMap.getElem?_insert, different] + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean index 51ee121e3..f7029652d 100644 --- a/Ix/Kernel/Verify/Consistency/RecursiveCache.lean +++ b/Ix/Kernel/Verify/Consistency/RecursiveCache.lean @@ -193,6 +193,8 @@ inductive InferenceCacheTrace : Nat → TcState .anon → KExpr .anon → Type ( InferenceCacheTrace fuel before (.sort level info) | fvar {fuel before id name info} (miss : UncachedInference before (.fvar id name info)) : InferenceCacheTrace fuel before (.fvar id name info) + | nat {fuel before value blob info} (miss : UncachedInference before (.nat value blob info)) : + InferenceCacheTrace fuel before (.nat value blob info) | const {fuel before id arguments info} (miss : UncachedInference before (.const id arguments info)) (concrete : KConst .anon) (loaded : miss.keyed.env.get? id = some concrete) @@ -281,7 +283,7 @@ hits contribute no key; recursive calls and each outer insertion are included. - def InferenceCacheTrace.writes {fuel : Nat} {before : TcState .anon} {term : KExpr .anon} : InferenceCacheTrace fuel before term → List (Address × Address) | .hit _ => [] - | .sort miss | .fvar miss | .const miss .. | .lazyConst miss .. => [miss.key] + | .sort miss | .fvar miss | .nat miss | .const miss .. | .lazyConst miss .. => [miss.key] | .app _ miss _ _ first second | .appBeta _ miss _ _ _ first second | .forallE miss _ first second | .lam _ miss _ first second | .lamBody _ miss _ first second | .forallSort miss _ _ _ first second | .lamSort _ miss _ _ first second => @@ -307,6 +309,14 @@ def InferenceCacheTrace.fvarOfKey {fuel : Nat} {before keyed : TcState .anon} · exact .hit hit · exact .fvar miss +def InferenceCacheTrace.natOfKey {fuel : Nat} {before keyed : TcState .anon} + {value : Nat} {blob : Address} {info : ExprInfo .anon} {key : Address × Address} + (keyRun : TcM.inferKey (.nat value blob info) before = .ok key keyed) : + InferenceCacheTrace fuel before (.nat value blob info) := by + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ + · exact .hit hit + · exact .nat miss + def InferenceCacheTrace.constOfKey {fuel : Nat} {before keyed : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} {key : Address × Address} {concrete : KConst .anon} @@ -429,6 +439,12 @@ theorem InferenceCacheTrace.frame {fuel : Nat} {before after : TcState .anon} split at run · cases run; exact ⟨.refl key _, rfl⟩ · contradiction + | @nat fuel before value blob info miss => + simp only [writes, List.mem_singleton] at outside + apply infer_miss_frame miss (Ne.symm outside) accepted + intro middle run + obtain ⟨_, rfl⟩ := inferUncached_nat_run run + exact ⟨.of_eq rfl rfl rfl, rfl⟩ | const miss concrete loaded resources => simp only [writes, List.mem_singleton] at outside apply infer_miss_frame miss (Ne.symm outside) accepted @@ -587,7 +603,7 @@ theorem InferenceCacheTrace.populated_outside {fuel : Nat} {before : TcState .an (stored : before.env.inferCache[key]? = some cached) : key ∉ tree.writes := by induction tree with | hit => simp [writes] - | sort miss | fvar miss | const miss concrete loaded resources | lazyConst miss loader resources => + | sort miss | fvar miss | nat miss | const miss concrete loaded resources | lazyConst miss loader resources => simpa only [writes, List.mem_singleton] using miss.ne_populated stored | app full miss trace hashPath functionTree argumentTree functionIH argumentIH => have keyed : miss.keyed.env.inferCache[key]? = some cached := by diff --git a/Ix/Kernel/Verify/Consistency/RecursiveState.lean b/Ix/Kernel/Verify/Consistency/RecursiveState.lean index a7281c8da..4d026a893 100644 --- a/Ix/Kernel/Verify/Consistency/RecursiveState.lean +++ b/Ix/Kernel/Verify/Consistency/RecursiveState.lean @@ -182,6 +182,8 @@ inductive OwnedInferenceTrace : Nat → TcState .anon → KExpr .anon → Type OwnedInferenceTrace fuel before (.sort level info) | fvar {fuel before id name info} (miss : UncachedInference before (.fvar id name info)) : OwnedInferenceTrace fuel before (.fvar id name info) + | nat {fuel before value blob info} (miss : UncachedInference before (.nat value blob info)) : + OwnedInferenceTrace fuel before (.nat value blob info) | const {fuel before id arguments info} (miss : UncachedInference before (.const id arguments info)) (data : ConstantInstantiationData miss.keyed id arguments) : @@ -213,7 +215,7 @@ inductive OwnedInferenceTrace : Nat → TcState .anon → KExpr .anon → Type def OwnedInferenceTrace.writes {fuel : Nat} {before : TcState .anon} {term : KExpr .anon} : OwnedInferenceTrace fuel before term → List (Address × Address) | .hit _ => [] - | .sort miss | .fvar miss | .const miss _ => [miss.key] + | .sort miss | .fvar miss | .nat miss | .const miss _ => [miss.key] | .app _ miss _ _ _ first second | .forallE miss _ _ first second | .lam _ miss _ _ _ first second => miss.key :: (first.writes ++ second.writes) @@ -253,6 +255,11 @@ theorem OwnedInferenceTrace.preserves {source : Ixon.Env} {fuel : Nat} split at run · cases run; exact keyed · contradiction + | @nat fuel before value blob info miss => + apply invariant_miss valid miss accepted + intro middle run keyed + obtain ⟨_, rfl⟩ := inferUncached_nat_run run + exact keyed.ofMaps rfl rfl rfl (keyed.coherent.internExpr _) | @const fuel before id arguments info miss data => apply invariant_miss valid miss accepted intro middle run keyed @@ -300,6 +307,7 @@ def OwnedInferenceTrace.toCacheTrace {source : Ixon.Env} {fuel : Nat} | .hit cached => .hit cached | .sort miss => .sort miss | .fvar miss => .fvar miss + | .nat miss => .nat miss | @OwnedInferenceTrace.const _ _ id _ _ miss data => let keyed := valid.afterInferKey miss.keyRun let loader := keyed.owned.toVerified id.addr @@ -356,6 +364,14 @@ def OwnedInferenceTrace.fvarOfKey {fuel : Nat} {before keyed : TcState .anon} · exact .hit hit · exact .fvar miss +def OwnedInferenceTrace.natOfKey {fuel : Nat} {before keyed : TcState .anon} + {value : Nat} {blob : Address} {info : ExprInfo .anon} {key : Address × Address} + (keyRun : TcM.inferKey (.nat value blob info) before = .ok key keyed) : + OwnedInferenceTrace fuel before (.nat value blob info) := by + rcases observeInferenceCache keyRun with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ + · exact .hit hit + · exact .nat miss + def OwnedInferenceTrace.constOfKey {fuel : Nat} {before keyed : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} {key : Address × Address} diff --git a/Ix/Kernel/Verify/Consistency/SourceAgreement.lean b/Ix/Kernel/Verify/Consistency/SourceAgreement.lean index e78140b9e..f8b0cce72 100644 --- a/Ix/Kernel/Verify/Consistency/SourceAgreement.lean +++ b/Ix/Kernel/Verify/Consistency/SourceAgreement.lean @@ -501,7 +501,7 @@ agreement assumption at any recursive boundary. -/ def OwnedInferenceTrace.SourceData {fuel : Nat} {before : TcState .anon} {term : KExpr .anon} (tree : OwnedInferenceTrace fuel before term) (source : Ixon.Env) : Prop := match tree with - | .hit _ | .sort _ | .fvar _ => True + | .hit _ | .sort _ | .fvar _ | .nat _ => True | @OwnedInferenceTrace.const _ _ id _ _ miss _ => StandaloneConversionData source id.addr miss.keyed.env | .app _ _ _ _ _ first second | .forallE _ _ _ first second | @@ -564,6 +564,11 @@ theorem OwnedInferenceTrace.preservesSource {source : Ixon.Env} {fuel : Nat} split at run · cases run; exact keyed · contradiction + | @nat fuel before value blob info miss => + apply source_invariant_miss valid miss accepted + intro middle run keyed + obtain ⟨_, rfl⟩ := inferUncached_nat_run run + exact keyed.ofMaps rfl rfl rfl (keyed.state.coherent.internExpr _) | @const fuel before id arguments info miss data => apply source_invariant_miss valid miss accepted intro middle run keyed diff --git a/Ix/Kernel/Verify/Consistency/SourceCache.lean b/Ix/Kernel/Verify/Consistency/SourceCache.lean index 05c715340..a6fdf1482 100644 --- a/Ix/Kernel/Verify/Consistency/SourceCache.lean +++ b/Ix/Kernel/Verify/Consistency/SourceCache.lean @@ -30,14 +30,18 @@ inductive SourceCacheRequest (source : Ixon.Env) where (predicted : predictStandalone? source id.addr = .ok (some declaration)) (arity : declaration.lvls.toNat = arguments.size) (substitution : KExpr.instantiateUnivParamsSpec declaration.ty arguments = .ok result) + /-- A closed natural-number literal, typed by the named primitive `Nat` constant. -/ + | nat (value : Nat) (prim : KId .anon) def SourceCacheRequest.term {source : Ixon.Env} : SourceCacheRequest source → KExpr .anon | .sort level => .mkSort level | .const id arguments .. => .mkConst id arguments + | .nat value _ => .mkNatLit value def SourceCacheRequest.result {source : Ixon.Env} : SourceCacheRequest source → KExpr .anon | .sort level => .mkSort (.mkSucc level) | .const _ _ _ result .. => result + | .nat _ prim => .mkConst prim #[] def SourceCacheRequest.key {source : Ixon.Env} (request : SourceCacheRequest source) : Address × Address := (request.term.addr, emptyCtxAddr) @@ -51,6 +55,7 @@ def SourceCacheRequest.Loaded {source : Ixon.Env} (request : SourceCacheRequest match request with | .sort _ => True | .const id _ declaration .. => before.env.get? id = some declaration + | .nat .. => True theorem SourceCacheRequest.Loaded.frame {source : Ixon.Env} {request : SourceCacheRequest source} {before after : TcState .anon} (loaded : request.Loaded before) @@ -59,6 +64,7 @@ theorem SourceCacheRequest.Loaded.frame {source : Ixon.Env} {request : SourceCac cases request with | sort => trivial | const => exact constants _ _ loaded + | nat => trivial theorem SourceCacheRequest.Loaded.ofMap {source : Ixon.Env} {request : SourceCacheRequest source} {before after : TcState .anon} (loaded : request.Loaded before) @@ -199,6 +205,9 @@ private theorem request_sort {source : Ixon.Env} {request : SourceCacheRequest s | const id arguments declaration result predicted arity substitution => change KExpr.sort level info = KExpr.const id arguments _ at equal cases equal + | nat value prim => + change KExpr.sort level info = KExpr.nat value _ _ at equal + cases equal private theorem request_const {source : Ixon.Env} {request : SourceCacheRequest source} {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} @@ -215,6 +224,28 @@ private theorem request_const {source : Ixon.Env} {request : SourceCacheRequest change KExpr.const id arguments info = KExpr.const other levels _ at equal obtain ⟨rfl, rfl, _⟩ := KExpr.const.inj equal exact ⟨declaration, predicted, arity, substitution, fun _ loaded => loaded⟩ + | nat value prim => + change KExpr.const id arguments info = KExpr.nat value _ _ at equal + cases equal + +/-- A literal request at a literal's key names the same value; its expected +type is the constant at the request's primitive. -/ +private theorem request_nat {source : Ixon.Env} {request : SourceCacheRequest source} + {value : Nat} {blob : Address} {info : ExprInfo .anon} (before : TcState .anon) + (equal : KExpr.nat value blob info = request.term) : + ∃ prim, request = .nat value prim ∧ request.result = KExpr.mkConst prim #[] ∧ + request.Loaded before := by + cases request with + | sort level => + change KExpr.nat value blob info = KExpr.sort level _ at equal + cases equal + | const id arguments declaration result predicted arity substitution => + change KExpr.nat value blob info = KExpr.const id arguments _ at equal + cases equal + | nat other prim => + change KExpr.nat value blob info = KExpr.nat other _ _ at equal + obtain ⟨rfl, _, _⟩ := KExpr.nat.inj equal + exact ⟨prim, rfl, rfl, trivial⟩ /-- Recursive nodes contain only finite key-collision data and the proposed sort result's intern domain. Constant walkers already supply their finite @@ -229,6 +260,11 @@ def OwnedInferenceTrace.CacheData {source : Ixon.Env} (catalog : List (SourceCac SourceCacheKeyData catalog term ∧ KExpr.KeyCollisionFree fun candidate => miss.keyed.env.intern.ExprSupport candidate ∨ candidate = KExpr.mkSort (KUniv.mkSucc level) | .fvar _ | .const _ _ => SourceCacheKeyData catalog term + | @OwnedInferenceTrace.nat _ _ value _ _ miss => + SourceCacheKeyData catalog term ∧ + (∀ prim, SourceCacheRequest.nat value prim ∈ catalog → prim = miss.keyed.prims.nat) ∧ + KExpr.KeyCollisionFree fun candidate => miss.keyed.env.intern.ExprSupport candidate ∨ + candidate = KExpr.mkConst miss.keyed.prims.nat #[] | .app _ _ _ _ _ first second | .forallE _ _ _ first second | .lam _ _ _ _ _ first second => SourceCacheKeyData catalog term ∧ first.CacheData catalog ∧ second.CacheData catalog @@ -309,6 +345,18 @@ theorem OwnedInferenceTrace.preservesCache {source : Ixon.Env} have equal := cacheData.same member miss.keyRun same cases request <;> cases equal · contradiction + | @nat fuel before value blob info miss => + apply source_cache_miss valid agreement miss accepted + intro middle run keyed cache + obtain ⟨rfl, rfl⟩ := inferUncached_nat_run run + refine ⟨cache.ofMaps rfl rfl rfl, ?_⟩ + intro request member same + have equal := cacheData.1.same member miss.keyRun same + obtain ⟨prim, rfl, typeEq, loaded⟩ := request_nat _ equal + refine ⟨?_, loaded⟩ + rw [typeEq, cacheData.2.1 prim member] + simpa only [KExpr.eraseMeta_anon] using + miss.keyed.env.intern.internExpr_eraseMeta keyed.state.coherent cacheData.2.2 | @const fuel before id arguments info miss data => apply source_cache_miss valid agreement miss accepted intro middle run keyed cache @@ -584,6 +632,23 @@ def BinderInference.sortFromSourceCache {β : Type u} {source : Ixon.Env} (.sort (readLevel level)) (.sort (.succ (readLevel level))) := .sortOfAgreement rfl (valid.cache (.sort level) member).correct valid.state.state.coherent faithful +/-- A literal leaf from catalog agreement at the literal's key. The catalog +request names the run's primitive `Nat` constant; the binding supplies the +model entry behind it. -/ +def BinderInference.natFromSourceCache {β : Type u} {source : Ixon.Env} + {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} {fuel : Nat} + {catalog : List (SourceCacheRequest source)} {before : TcState .anon} {value : Nat} + (valid : SourceCacheInvariant catalog before) + (binding : PrimitiveNatBinding resolve entries before.prims) + (member : SourceCacheRequest.nat value before.prims.nat ∈ catalog) + (faithful : KExpr.KeyCollisionFree fun term => before.env.intern.ExprSupport term ∨ + term = KExpr.mkConst before.prims.nat #[]) : + BinderInference resolve entries locals context fuel before (.mkNatLit value) + (.natLit value) (.const binding.ref []) := + .natOfAgreement rfl binding (valid.cache (.nat value before.prims.nat) member).correct + valid.state.state.coherent faithful + /-- Actual constant inference is model-typed after a history beginning with empty caches. Both cache partitions and the miss path are handled by actual selection; no initial cache agreement or post-lookup reading is assumed. -/ diff --git a/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean b/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean index 092ba6194..ddbb8d1ca 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisCacheExecution.lean @@ -65,6 +65,16 @@ def fvarOfKey {keyed : TcState .anon} {id : FVarId} {name : Mode.anon.F Name} {i unfold InferenceCacheTrace.fvarOfKey rcases observeInferenceCache run with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ <;> exact PUnit.unit) +def natOfKey {keyed : TcState .anon} {value : Nat} {blob : Address} {info : ExprInfo .anon} + {key : Address × Address} (run : TcM.inferKey (.nat value blob info) before = .ok key keyed) : + SynthesisCacheSupplement resolve anchor entries fuel before (.nat value blob info) := + ofLeaf (.natOfKey run) (by + unfold InferenceCacheTrace.natOfKey + rcases observeInferenceCache run with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ <;> rfl) (by + intro _ + unfold InferenceCacheTrace.natOfKey + rcases observeInferenceCache run with ⟨hit, _, _⟩ | ⟨miss, _, _⟩ <;> exact PUnit.unit) + def verifiedConstOfKey {keyed : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} {key : Address × Address} (run : TcM.inferKey (.const id arguments info) before = .ok key keyed) @@ -95,7 +105,7 @@ def complete (data : SynthesisCacheSupplement resolve anchor entries fuel before rcases data with ⟨trace, children, whnf⟩ cases trace with | hit => exact .nil - | sort miss | fvar miss | const miss concrete loaded resources | lazyConst miss loader resources => + | sort miss | fvar miss | nat miss | const miss concrete loaded resources | lazyConst miss loader resources => exact .singleton (.ofSource tree contextOrigin agreement reading miss accepted) | app full miss trace hashPath functionTree argumentTree => exact children.append (.singleton (.ofSource tree contextOrigin agreement reading miss accepted)) @@ -129,7 +139,8 @@ def SynthesisInference.CacheData {β : Type u} {resolve : Address → Option (Co {source : KExpr .anon} {term type : AExpr β} {level : VLevel} (tree : SynthesisInference resolve entries locals context bounds fuel before source term type level) : Type (u + 1) := match tree with - | .known .. | .reuseType .. | .fvar .. => SynthesisCacheSupplement resolve anchor entries fuel before source + | .known .. | .reuseType .. | .fvar .. | .natLit .. => + SynthesisCacheSupplement resolve anchor entries fuel before source | .cached .. | .cachedFrom .. => PUnit | .app _ _ _ first second .. | .appBeta _ _ _ first _ _ _ second .. => first.CacheData anchor × second.CacheData anchor @@ -179,6 +190,8 @@ def SynthesisInference.cacheExecution {β : Type u} {resolve : Address → Optio contextOrigin agreement reading accepted | .fvar inference atIndex boundAtIndex => fun data contextOrigin agreement reading accepted => data.complete (.fvar inference atIndex boundAtIndex) contextOrigin agreement reading accepted + | .natLit binding inference => fun data contextOrigin agreement reading accepted => + data.complete (.natLit binding inference) contextOrigin agreement reading accepted | .cached _ _ _ _ hit _ _ | .cachedFrom _ hit _ => fun _ _ _ _ accepted => ⟨.hit hit, .nil, fun _ => PUnit.unit, fun initial => by rw [hit.run] at accepted diff --git a/Ix/Kernel/Verify/Consistency/SynthesisCoherence.lean b/Ix/Kernel/Verify/Consistency/SynthesisCoherence.lean index af33fb356..663ddc790 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisCoherence.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisCoherence.lean @@ -104,6 +104,15 @@ theorem BinderInference.outputCoherent {β : Type u} rw [cached.run] at accepted cases accepted simpa only [inferKey_environment cached.keyRun] using initial + | natLit miss binding coherent faithful => + apply infer_miss_coherent miss accepted + intro middle run + obtain ⟨_, rfl⟩ := inferUncached_nat_run run + exact coherent.internExpr _ + | cachedNatLit cached binding canonical => + rw [cached.run] at accepted + cases accepted + simpa only [inferKey_environment cached.keyRun] using initial | fvar => exact tree.fvar_coherent initial accepted | const | polymorphic | cachedConst => exact tree.const_coherent initial accepted | app full miss trace functionTree head argumentTree conditions hashPath comparisonFaithful @@ -142,7 +151,7 @@ theorem SynthesisInference.outputCoherent {β : Type u} (reading : readScopedExpr? resolve locals source = some term.erase) (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : after.env.intern.WF := match support with - | .known inference _ | .reuseType inference .. | .fvar inference .. => + | .known inference _ | .reuseType inference .. | .fvar inference .. | .natLit _ inference => inference.outputCoherent tree initial accepted | .cached _ _ _ _ observed _ _ | .cachedFrom _ observed _ => by rw [observed.run] at accepted diff --git a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean index 7c404becb..2c3c81013 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisInference.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisInference.lean @@ -60,6 +60,9 @@ theorem SynthesisInference.soundWithHereditary {β : Type u} | .fvar inference atIndex boundAtIndex => by obtain ⟨reads, typed⟩ := inference.synthesis (.bvar _) agreement reading accepted exact ⟨reads, typed, formed _ _ _ atIndex boundAtIndex, .bvar typed atIndex⟩ + | .natLit binding inference => by + have reads := (BinderInference.sound.{u,v} inference agreement reading accepted).1 + exact ⟨reads, binding.typing _ _, binding.formation _, .atom (binding.typing _ _) (.natLit _)⟩ | .app full miss trace functionTree argumentTree conditions hashPath comparisonFaithful bodyConstructed argConstructed bodyBound argBound coherent faithful => by obtain ⟨state, run⟩ := infer_uncached_success miss accepted diff --git a/Ix/Kernel/Verify/Consistency/SynthesisReading.lean b/Ix/Kernel/Verify/Consistency/SynthesisReading.lean index 2478f6b66..7dbb26ea7 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisReading.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisReading.lean @@ -25,7 +25,7 @@ theorem SynthesisInference.outputReading {β : Type u} (accepted : RecM.infer source (methodsN fuel) before = .ok result after) : readScopedExpr? resolve locals result = some type.erase := match support with - | .known inference _ | .reuseType inference .. | .fvar inference .. => + | .known inference _ | .reuseType inference .. | .fvar inference .. | .natLit _ inference => (BinderInference.sound.{u,u} inference agreement reading accepted).1 | .cached _ _ _ _ hit cacheMatch resultReading => by rw [hit.run] at accepted diff --git a/Ix/Kernel/Verify/Consistency/SynthesisShapes.lean b/Ix/Kernel/Verify/Consistency/SynthesisShapes.lean index 552134c4a..1365fa8a5 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisShapes.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisShapes.lean @@ -158,7 +158,8 @@ def BinderInference.variableSpineOrigin {β : Type u} {resolve : Address → Opt have spine := prior.spine.snoc (.source (.binderArgument trace functionTree head argumentTree keyedAgreement functionReading argumentReading conditions hashPath comparisonFaithful)) exact ⟨prior.headType, prior.atIndex, by simpa only [← parts.1] using spine⟩ - | .sort .. | .cachedSort .. | .const .. | .polymorphic .. | .cachedConst .. | .forallE .. | .lam .. => by + | .sort .. | .cachedSort .. | .natLit .. | .cachedNatLit .. | .const .. | .polymorphic .. | + .cachedConst .. | .forallE .. | .lam .. => by exact False.elim (not_variable_spine (by intro fn arg same; cases same) (by intro index same; cases same) index arguments headEquals) termination_by structural support @@ -348,7 +349,8 @@ def BinderInference.spineOrigin {β : Type u} {resolve : Address → Option (Con simpa only [← parts.1] using prior.argumentsOrigin.snoc (.source (.binderArgument trace functionTree functionHead argumentTree keyedAgreement functionReading argumentReading conditions hashPath comparisonFaithful)) - | .sort .. | .cachedSort .. | .fvar .. | .const .. | .polymorphic .. | .cachedConst .. | .forallE .. | .lam .. => by + | .sort .. | .cachedSort .. | .natLit .. | .cachedNatLit .. | .fvar .. | .const .. | .polymorphic .. | + .cachedConst .. | .forallE .. | .lam .. => by exact False.elim (empty (nonapp_spine_empty (by intro fn arg same; cases same) same)) termination_by structural support diff --git a/Ix/Kernel/Verify/Consistency/SynthesisSource.lean b/Ix/Kernel/Verify/Consistency/SynthesisSource.lean index 304c2e5c4..0cdb07a9f 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisSource.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisSource.lean @@ -26,7 +26,8 @@ def BinderInference.betaTyping {β : Type u} {resolve : Address → Option (Cons readScopedExpr? resolve locals source = some term.erase → SynthesisBetaTyping resolve incoming incomingContext incomingBounds entries context term type := match support with - | .sort .. | .cachedSort .. | .const .. | .polymorphic .. | .cachedConst .. => + | .sort .. | .cachedSort .. | .natLit .. | .cachedNatLit .. | .const .. | .polymorphic .. | + .cachedConst .. => fun origin _ _ => .atom origin (by constructor) | .forallE miss trace opening domainTree bodyTree .. => fun origin agreement reading => by obtain ⟨domainReading, bodyReading⟩ := readScopedExpr?_all_parts reading @@ -97,6 +98,8 @@ def SynthesisInference.betaTyping {β : Type u} {resolve : Address → Option (C inference.betaTyping (.source (.checked contextOrigin node agreement reading accepted)) agreement reading | node@(.fvar _ atIndex _) => fun contextOrigin agreement reading accepted => .bvar (.source (.checked contextOrigin node agreement reading accepted)) atIndex + | node@(.natLit _ inference) => fun contextOrigin agreement reading accepted => + inference.betaTyping (.source (.checked contextOrigin node agreement reading accepted)) agreement reading | .forallE miss trace opening domainTree bodyTree levelFaithful domainBound bodyBound coherent faithful => fun contextOrigin agreement reading accepted => by let node := SynthesisInference.forallE miss trace opening domainTree bodyTree levelFaithful diff --git a/Ix/Kernel/Verify/Consistency/SynthesisSupport.lean b/Ix/Kernel/Verify/Consistency/SynthesisSupport.lean index caad85345..053842dc3 100644 --- a/Ix/Kernel/Verify/Consistency/SynthesisSupport.lean +++ b/Ix/Kernel/Verify/Consistency/SynthesisSupport.lean @@ -72,6 +72,12 @@ inductive SynthesisInference {β : Type u} (atIndex : context[index]? = some type) (boundAtIndex : bounds[index]? = some level) : SynthesisInference resolve entries locals context bounds fuel before source (.bvar index) type level + | natLit {entries locals context bounds fuel before value blob info} + (binding : PrimitiveNatBinding resolve entries before.prims) + (inference : BinderInference resolve entries locals context fuel before (.nat value blob info) + (.natLit value) (.const binding.ref [])) : + SynthesisInference resolve entries locals context bounds fuel before (.nat value blob info) + (.natLit value) (.const binding.ref []) binding.level | app {entries locals context bounds fuel before fn arg info f a A A' B condition functionLevel argumentLevel} (full : before.inferOnly = false) (miss : UncachedInference before (.app fn arg info)) diff --git a/Tests/Ix/Kernel/Consistency.lean b/Tests/Ix/Kernel/Consistency.lean index 9c8b2fe8a..f7af8edc6 100644 --- a/Tests/Ix/Kernel/Consistency.lean +++ b/Tests/Ix/Kernel/Consistency.lean @@ -4340,6 +4340,102 @@ private def polymorphicDefinitionCases : TestSeq := | .error (.univParamMismatch expected actual) _ => expected == 2 && actual == 1 | _ => false : Bool) +/-- A one-inductive block `N : Sort 1` standing in for `Nat` at a fresh +address, the literal blob for `3`, and four definitions over it: +`three : N := 3`, `family : N → Sort 1 := fun _ => N`, `typed : family 3 := 3` +(a literal in the declared type), and `bad : Sort 1 := 3`. The canonical +`Nat` address needs the compiled prelude block, so the checker's primitive +table is pointed at `N` instead. -/ +private def literalEnvironment : + Ixon.Env × Address × Address × Address × Address := Id.run do + let ind : Ixon.Inductive := + ⟨false, 0, 0, 0, .sort 0, #[⟨false, 0, 0, 0, 0, .recur 0 #[]⟩]⟩ + let block : Ixon.Constant := ⟨.muts #[.indc ind], #[], #[], #[sort1U]⟩ + let (env, blockAddr) := storeMutsWithProjs {} block + let natAddr := Address.blake3 (Ixon.serConstant ⟨.iPrj ⟨0, blockAddr⟩, #[], #[], #[]⟩) + let (env, blobAddr) := env.storeBlob ⟨(3 : Nat).toBytesLE⟩ + let (env, three) := storeConst env + ⟨.defn ⟨.defn, .safe, 0, .ref 0 #[], .nat 1⟩, #[], #[natAddr, blobAddr], #[]⟩ + let (env, family) := storeConst env + ⟨.defn ⟨.defn, .safe, 0, .leanAll (.ref 0 #[]) (.sort 0), .leanLam (.ref 0 #[]) (.ref 0 #[])⟩, + #[], #[natAddr], #[sort1U]⟩ + let (env, typed) := storeConst env + ⟨.defn ⟨.defn, .safe, 0, .app (.ref 1 #[]) (.nat 2), .nat 2⟩, + #[], #[natAddr, family, blobAddr], #[]⟩ + let (env, bad) := storeConst env + ⟨.defn ⟨.defn, .safe, 0, .sort 0, .nat 0⟩, #[], #[blobAddr], #[sort1U]⟩ + return (env, natAddr, three, typed, bad) + +/-- The lazy anon state whose primitive `Nat` is the fixture's inductive. -/ +private def literalState (env : Ixon.Env) (natAddr : Address) : TcState .anon := + { TcState.newLazyAnon env with prims := { Primitives.ofAnonAddrs with nat := ⟨natAddr, ()⟩ } } + +private def literalNatConst (natAddr : Address) : KExpr .anon := .mkConst ⟨natAddr, ()⟩ #[] + +/-- A literal infers to exactly the interned primitive `Nat` constant, in one +full-cache entry at the literal's key. -/ +private def literalInference : Bool := + let (env, natAddr, _, _, _) := literalEnvironment + match TcM.infer (.mkNatLit 3) (literalState env natAddr) with + | .ok type after => type.addr == (literalNatConst natAddr).addr && after.env.inferCache.size == 1 + | .error _ _ => false + +/-- A repeated literal is served from the same single full-cache entry. -/ +private def literalCacheReuse : Bool := + let (env, natAddr, _, _, _) := literalEnvironment + let expected := literalNatConst natAddr + let action : TcM .anon Bool := do + let first ← TcM.infer (.mkNatLit 3) + let second ← TcM.infer (.mkNatLit 3) + let key ← TcM.inferKey (.mkNatLit 3) + let state ← get + return first.addr == expected.addr && second.addr == expected.addr && + state.env.inferCache.size == 1 && + (state.env.inferCache[key]?.map (·.addr)) == some expected.addr + match action (literalState env natAddr) with + | .ok passed _ => passed + | .error _ _ => false + +/-- Clearing the reduction caches drops the entry; re-inference restores it. -/ +private def literalCacheClearing : Bool := + let (env, natAddr, _, _, _) := literalEnvironment + let expected := literalNatConst natAddr + let action : TcM .anon Bool := do + let _ ← TcM.infer (.mkNatLit 3) + modify fun state => { state with env := state.env.clearReductionCaches } + let cleared ← get + let again ← TcM.infer (.mkNatLit 3) + let state ← get + return cleared.env.inferCache.isEmpty && again.addr == expected.addr && + state.env.inferCache.size == 1 + match action (literalState env natAddr) with + | .ok passed _ => passed + | .error _ _ => false + +/-- `three : N := 3` and `typed : family 3 := 3` pass the per-item check. -/ +private def literalDefinitionsCheck : Bool := + let (env, natAddr, three, typed, _) := literalEnvironment + let action : TcM .anon Unit := do + TcM.checkConst ⟨three, ()⟩ + TcM.checkConst ⟨typed, ()⟩ + match action (literalState env natAddr) with + | .ok () _ => true + | .error _ _ => false + +/-- `bad : Sort 1 := 3` fails: the literal's type is the primitive `Nat`. -/ +private def literalMismatchRejected : Bool := + let (env, natAddr, _, _, bad) := literalEnvironment + match TcM.checkConst ⟨bad, ()⟩ (literalState env natAddr) with + | .ok () _ => false + | .error _ _ => true + +private def literalCases : TestSeq := + test "natural literal: inference returns the interned primitive Nat constant" literalInference + ++ test "natural literal: a repeated literal reuses its single full-cache entry" literalCacheReuse + ++ test "natural literal: clearing drops the entry and re-inference restores it" literalCacheClearing + ++ test "natural literal: a literal body and a literal in the declared type check" literalDefinitionsCheck + ++ test "natural literal: a literal does not inhabit a sort" literalMismatchRejected + public def suite : List TestSeq := [cases, polymorphicCases, specializationCases, binderCases, applicationCases, multiBetaCases, cheapLambdaCases, cheapApplicationCases, exposedLambdaCases, repeatedBetaCases, betaTraceCases, hereditaryBetaCases, piExposureCases, @@ -4347,6 +4443,6 @@ public def suite : List TestSeq := lazyCacheCases, blockCacheCases, ingressCoherenceCases, sourceOwnershipCases, recursiveStateCases, sourceAgreementCases, sourceCacheCases, compositeCacheCases, letCases, sortExposureCases, mixedWhnfCases, letWhnfCases, headWhnfCases, - polymorphicDefinitionCases] + polymorphicDefinitionCases, literalCases] end Tests.Kernel.Consistency From 841d78e8b06c0141f69f551deb723ec607dd1ad6 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 15 Sep 2026 12:58:56 -0400 Subject: [PATCH 58/63] Derive model references from the Ixon environment Add Consistency/Resolution with the canonical map `Ixon.Env.resolve`: standalones resolve to `.member addr 0` (a one-member block at their own address, as the loader registers and the certified reader stores them), projection records to `.member block idx` and `.ctor block idx cidx` through a member-kind check, agreeing with `Ix.Certified.resolveReference?`. The `Locates` characterization gives injectivity on standalone coordinates and, under `ProjectionsCanonical`, on the whole domain; under the finite contract `SourceMaterializes`, the standalone items of `buildAnonWork` are exactly the addresses with a standalone coordinate; generated block projection addresses resolve to their coordinates under `ProjectionsStored`. Derive the static bindings from the source: `StandaloneModelBinding.ofSource` from the standalone prediction, and `PrimitiveNatBinding.ofLocated`, `.ofStandalone`, `.ofInductive` from the stored records at the primitive address. Restate the environment theorems with `resolve := env.resolve`: `ResolvedDefinitionPlan`, `ResolvedAxiomObservation`, and `ResolvedEnvironmentFragment` force every reference to its canonical coordinate and derive the former `resolved`, `fresh`, and `installed` premises, giving `checkEnvAnon_preserves_model_resolved`, `checkEnvAnon_represents_source_resolved`, and `checkEnvAnon_no_false_resolved`. Audit the new roots and add fixture regressions for the map. --- Ix/Kernel/Verify/Consistency.lean | 7 + Ix/Kernel/Verify/Consistency/Audit.lean | 69 + Ix/Kernel/Verify/Consistency/Resolution.lean | 1183 ++++++++++++++++++ Tests/Ix/Kernel/Resolution.lean | 54 + Tests/Main.lean | 3 +- 5 files changed, 1315 insertions(+), 1 deletion(-) create mode 100644 Ix/Kernel/Verify/Consistency/Resolution.lean create mode 100644 Tests/Ix/Kernel/Resolution.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index bccfb17f9..89df95ab6 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -16,6 +16,7 @@ import Ix.Kernel.Verify.Consistency.ScopedConstant import Ix.Kernel.Verify.Consistency.InferenceCache import Ix.Kernel.Verify.Consistency.SortCache import Ix.Kernel.Verify.Consistency.Literals +import Ix.Kernel.Verify.Consistency.Resolution import Ix.Kernel.Verify.Consistency.ConstantCache import Ix.Kernel.Verify.Consistency.LazyCache import Ix.Kernel.Verify.Consistency.BlockCache @@ -359,5 +360,11 @@ Natural-number literals infer to the interned primitive `Nat` constant; a static binding of that address to an admitted entry with a `natural` fact types the literal in both cache partitions, through the synthesis, cache trace, source-cache, and history recursions, and as invariant preservation. +Model references are derived from the source environment: the canonical map +`Ixon.Env.resolve` places standalones in their own one-member block and +projections at their block coordinates, agrees with the certified adapter, is +injective on standalone coordinates, enumerates exactly the driver's standalone +items under a finite materialization contract, derives both static bindings, and +restates the environment theorems with every reference at its canonical coordinate. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 6c1fd93bc..e8fd352a9 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -9,6 +9,7 @@ import Ix.Kernel.Verify.Consistency.Environment import Ix.Kernel.Verify.Consistency.RecursiveCache import Ix.Kernel.Verify.Consistency.RecursiveState import Ix.Kernel.Verify.Consistency.SourceAgreement +import Ix.Kernel.Verify.Consistency.Resolution import Ix.Kernel.Verify.Consistency.SourceCache import Ix.Kernel.Verify.Consistency.Dependencies import Ix.Kernel.Verify.Consistency.CheapBeta @@ -1540,6 +1541,61 @@ private def literalRoots : Array Lean.Name := #[ ``CheckerInvariant.inferNat ] +/-- Canonical reference maps and standalone/projection classification are pure. -/ +private def resolutionDataRoots : Array Lean.Name := #[ + ``memberRecord, ``AxiomSpec.Canonical, ``DefinitionSpec.Canonical, ``CanonicalOutside +] + +private def resolutionClassificationRoots : Array Lean.Name := #[ + ``Ixon.ConstantInfo.Standalone, ``Ixon.ConstantInfo.Projection, + ``Ixon.ConstantInfo.not_projection_of_standalone, ``ingressBlockAddr?_eq_none_iff, + ``WorkPosition.mem +] + +/-- The canonical map over the stored source constants, its location +characterization, injectivity on standalone coordinates, agreement with the +certified adapter, the derived static bindings, and the canonical axiom +interface reach only the source environment's finite maps. -/ +private def resolutionSourceRoots : Array Lean.Name := #[ + ``Ixon.Env.mutMember?, ``Ixon.Env.resolveInfo, ``Ixon.Env.resolve, ``Ixon.Env.Locates, + ``Ixon.Env.resolve_stored, ``Ixon.Env.resolve_eq_some_iff, ``Ixon.Env.resolve_standalone, + ``Ixon.Env.mutMember?_some, ``Ixon.Env.Locates.stored, ``Ixon.Env.Locates.block_stored, + ``Ixon.Env.resolve_member_self_iff, ``Ixon.Env.recordOf, ``Ixon.Env.Locates.standalone_or_record, + ``Ixon.Env.recordOf_member_some, ``Ixon.Env.Standalone.no_member, ``Ixon.Env.Locates.same_ref, + ``Ixon.Env.resolve_standalone_injective, ``Ixon.LazyConstant.get?_of_get, + ``lazy_of_getConst?, ``getConst?_of_lazy, ``certified_resolveReference?_eq, + ``SourceMaterializes, ``sourceMaterializesCheck, ``mutMember?_of_stored, + ``PrimitiveNatBinding.ofLocated, ``PrimitiveNatBinding.ofStandalone, + ``PrimitiveNatBinding.ofInductive, ``axiomEnvironment_canonical, ``axiomEnvironment_installed +] + +/-- Source-key membership, the verified loader, generated projection addresses, +full injectivity, the finite materialization contract, and the exact standalone +enumeration of `buildAnonWork` reach hashing and the ordered key enumeration. -/ +private def resolutionEnumerationRoots : Array Lean.Name := #[ + ``mem_keys_of_getConst?, ``resolve_mem_keys, ``resolve_block_mem_keys, + ``ofConstantInfo_standalone_iff, ``getConst?_of_verified, ``projectionAddr?, + ``ProjectionsCanonical, ``projectionAddr?_of_projection, ``resolve_injective, + ``SourceMaterializes.ofCheck, ``buildAnonWorkItem_of_standalone, ``buildAnonWorkItem_standalone, + ``resolve_of_work_standalone, ``mem_orderedAnonConstAddrs, ``work_standalone_of_resolve, + ``work_standalone_iff, ``memberAddr, ``ProjectionsStored, ``resolve_memberAddr, + ``resolve_ctorProjAddr, ``resolve_anonBlockTargets +] + +/-- Source prediction reaches expression and universe construction. -/ +private def resolutionPredictionRoots : Array Lean.Name := #[ + ``resolve_of_predictStandalone?, ``StandaloneModelBinding.ofSource +] + +/-- The environment theorems with every reference at its canonical coordinate +reach the production driver like the atomic fragment. -/ +private def resolutionProductionRoots : Array Lean.Name := #[ + ``ResolvedDefinitionPlan, ``ResolvedDefinitionPlan.atomic, ``ResolvedAxiomObservation, + ``ResolvedAxiomObservation.atomic, ``ResolvedEnvironmentFragment, + ``ResolvedEnvironmentFragment.atomic, ``checkEnvAnon_preserves_model_resolved, + ``checkEnvAnon_represents_source_resolved, ``checkEnvAnon_no_false_resolved +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -1775,6 +1831,19 @@ def roots : Array RootAllowance := #[ ++ literalRoots.map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) + ++ resolutionDataRoots.map (fun root => { root, forbiddenDependencies := forbiddenProduction }) + ++ resolutionClassificationRoots.map (fun root => { + root, standardAxioms := #[``propext], forbiddenDependencies := forbiddenProduction }) + ++ resolutionSourceRoots.map (fun root => { + root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction }) + ++ resolutionEnumerationRoots.map (fun root => { + root, standardAxioms := standard, forbiddenDependencies := forbiddenProduction }) + ++ resolutionPredictionRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], + forbiddenDependencies := forbiddenProduction }) + ++ resolutionProductionRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := productionNative, + forbiddenDependencies := forbiddenProduction }) run_cmd Kernel.Verify.Audit.check roots diff --git a/Ix/Kernel/Verify/Consistency/Resolution.lean b/Ix/Kernel/Verify/Consistency/Resolution.lean new file mode 100644 index 000000000..bd93c5378 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Resolution.lean @@ -0,0 +1,1183 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Environment +import Ix.Kernel.Verify.Consistency.SourceAgreement +import Ix.Kernel.Verify.Consistency.Literals +import Ix.Certified.Ixon +import Ix.Compile.Verify.QSort + +/-! +# Canonical model references derived from the source environment + +`Ixon.Env.resolve` maps a source address to its model coordinate using only +the constants stored in the source environment, following the certified +adapter's convention (`Ix.Certified.resolveReference?`, proved to agree below): + +* a standalone constant (definition, recursor, axiom, quotient) is a + one-member block at its own address, `.member addr 0`, exactly as the + production loader registers it (`insertStandaloneEntries`) and as the + certified reader stores it (`Ix.Certified.readBlocks?`); +* a projection record `⟨idx, block⟩` resolves to `.member block idx` when the + Muts constant at `block` stores a member of the matching kind at `idx`, and a + constructor projection resolves to `.ctor block idx cidx` when that + constructor exists; +* Muts blocks themselves, absent addresses, and records that do not + materialize resolve to nothing. + +The map is injective on standalone coordinates and, whenever every stored +projection record sits at its generated address (`ProjectionsCanonical`), on +its whole domain. Under the finite source contract `SourceMaterializes`, the +standalone items enumerated by `buildAnonWork` are exactly the addresses with +a standalone coordinate, and the generated projection addresses of a stored +block resolve to their member and constructor coordinates when the block's +projection records are stored (`ProjectionsStored`). Source prediction of a +standalone declaration determines its coordinate, so the static bindings +`StandaloneModelBinding` and `PrimitiveNatBinding` are derived from the +source, and the environment theorems are restated with `β := Address` and +`resolve := env.resolve`: their `resolved`, `fresh`, and `installed` premises +become proofs. + +The compiler's catalog (`Ix.Compile.Verify.Catalog`) keeps `resolve` as an +explicit input. The intended agreement, not proved here because that module +currently reaches the retired named specification, is that under +`Catalog.WF (Catalog.ofEnv env env.resolve members)` every stored projection +record is resolved by `env.resolve` to the coordinate its payload names: +`ConstantProjectionWF` is exactly the member-kind check performed by +`Ixon.Env.resolveInfo`. +-/ + +namespace Ixon + +namespace ConstantInfo + +/-- Records the production driver checks as their own single-member block. -/ +def Standalone : ConstantInfo → Prop + | .defn _ | .recr _ | .axio _ | .quot _ => True + | _ => False + +/-- Records that project one member or constructor of a Muts block. -/ +def Projection : ConstantInfo → Prop + | .dPrj _ | .rPrj _ | .iPrj _ | .cPrj _ => True + | _ => False + +/-- A record is never both standalone and a projection. -/ +theorem not_projection_of_standalone {info : ConstantInfo} (standalone : info.Standalone) : + ¬ info.Projection := by + cases info <;> simp_all [Standalone, Projection] + +end ConstantInfo + +namespace Env + +open Ix.Theory + +/-- The member stored at `index` of the Muts constant at `block`. -/ +def mutMember? (env : Env) (block : Address) (index : UInt64) : Option MutConst := + match env.getConst? block with + | none => none + | some source => + match source.info with + | .muts members => members[index.toNat]? + | _ => none + +/-- The coordinate of a record stored at `addr`. Projection payloads must name +a member of the matching kind, as in `Ix.Certified.resolveReference?`. -/ +def resolveInfo (env : Env) (addr : Address) : ConstantInfo → Option (ConstRef Address) + | .defn _ | .recr _ | .axio _ | .quot _ => some (.member addr 0) + | .dPrj projection => + match env.mutMember? projection.block projection.idx with + | some (.defn _) => some (.member projection.block projection.idx.toNat) + | _ => none + | .rPrj projection => + match env.mutMember? projection.block projection.idx with + | some (.recr _) => some (.member projection.block projection.idx.toNat) + | _ => none + | .iPrj projection => + match env.mutMember? projection.block projection.idx with + | some (.indc _) => some (.member projection.block projection.idx.toNat) + | _ => none + | .cPrj projection => + match env.mutMember? projection.block projection.idx with + | some (.indc family) => + match family.ctors[projection.cidx.toNat]? with + | some _ => some (.ctor projection.block projection.idx.toNat projection.cidx.toNat) + | none => none + | _ => none + | .muts _ => none + +/-- The canonical model reference of a source address: a pure function of the +stored constants. Standalones are one-member blocks at their own address; +projections resolve through their payload into the owning Muts block. -/ +def resolve (env : Env) (addr : Address) : Option (ConstRef Address) := + match env.getConst? addr with + | none => none + | some source => env.resolveInfo addr source.info + +/-- The stored records that determine a coordinate. -/ +inductive Locates (env : Env) : Address → ConstRef Address → Prop + | standalone {addr : Address} {constant : Constant} : + env.getConst? addr = some constant → constant.info.Standalone → + Locates env addr (.member addr 0) + | defn {addr : Address} {constant : Constant} {projection : DefinitionProj} + {definition : Definition} : + env.getConst? addr = some constant → constant.info = .dPrj projection → + env.mutMember? projection.block projection.idx = some (.defn definition) → + Locates env addr (.member projection.block projection.idx.toNat) + | recr {addr : Address} {constant : Constant} {projection : RecursorProj} + {recursor : Recursor} : + env.getConst? addr = some constant → constant.info = .rPrj projection → + env.mutMember? projection.block projection.idx = some (.recr recursor) → + Locates env addr (.member projection.block projection.idx.toNat) + | indc {addr : Address} {constant : Constant} {projection : InductiveProj} + {family : Inductive} : + env.getConst? addr = some constant → constant.info = .iPrj projection → + env.mutMember? projection.block projection.idx = some (.indc family) → + Locates env addr (.member projection.block projection.idx.toNat) + | ctor {addr : Address} {constant : Constant} {projection : ConstructorProj} + {family : Inductive} {constructor : Constructor} : + env.getConst? addr = some constant → constant.info = .cPrj projection → + env.mutMember? projection.block projection.idx = some (.indc family) → + family.ctors[projection.cidx.toNat]? = some constructor → + Locates env addr (.ctor projection.block projection.idx.toNat projection.cidx.toNat) + +/-- Resolution reads the stored record. -/ +theorem resolve_stored {env : Env} {addr : Address} {constant : Constant} + (stored : env.getConst? addr = some constant) : + env.resolve addr = env.resolveInfo addr constant.info := by + unfold resolve + rw [stored] + +/-- Resolution is exactly location by stored records. -/ +theorem resolve_eq_some_iff {env : Env} {addr : Address} {ref : ConstRef Address} : + env.resolve addr = some ref ↔ env.Locates addr ref := by + constructor + · intro resolved + obtain ⟨constant, stored, resolved⟩ : ∃ constant, env.getConst? addr = some constant ∧ + env.resolveInfo addr constant.info = some ref := by + unfold resolve at resolved + revert resolved + cases stored : env.getConst? addr with + | none => intro resolved; cases resolved + | some constant => intro resolved; exact ⟨constant, rfl, resolved⟩ + revert resolved + cases info : constant.info with + | defn _ | recr _ | axio _ | quot _ => + simp only [resolveInfo] + intro resolved + cases resolved + exact .standalone stored (by rw [info]; trivial) + | muts _ => simp [resolveInfo] + | dPrj projection => + simp only [resolveInfo] + cases member : env.mutMember? projection.block projection.idx with + | none => simp + | some found => + cases found with + | defn definition => + intro resolved + cases resolved + exact .defn stored info member + | indc _ | recr _ => simp + | rPrj projection => + simp only [resolveInfo] + cases member : env.mutMember? projection.block projection.idx with + | none => simp + | some found => + cases found with + | recr recursor => + intro resolved + cases resolved + exact .recr stored info member + | indc _ | defn _ => simp + | iPrj projection => + simp only [resolveInfo] + cases member : env.mutMember? projection.block projection.idx with + | none => simp + | some found => + cases found with + | indc family => + intro resolved + cases resolved + exact .indc stored info member + | recr _ | defn _ => simp + | cPrj projection => + simp only [resolveInfo] + cases member : env.mutMember? projection.block projection.idx with + | none => simp + | some found => + cases found with + | indc family => + dsimp only + intro resolved + split at resolved + · rename_i found constructor + cases resolved + exact .ctor stored info member constructor + · cases resolved + | recr _ | defn _ => simp + · intro located + cases located with + | @standalone constant stored standalone => + rw [resolve_stored stored] + cases info : constant.info <;> simp_all [resolveInfo, ConstantInfo.Standalone] + | defn stored info member => simp [resolve, resolveInfo, stored, info, member] + | recr stored info member => simp [resolve, resolveInfo, stored, info, member] + | indc stored info member => simp [resolve, resolveInfo, stored, info, member] + | ctor stored info member constructor => + simp [resolve, resolveInfo, stored, info, member, constructor] + +/-- A stored standalone record resolves to its own one-member block. -/ +theorem resolve_standalone {env : Env} {addr : Address} {constant : Constant} + (stored : env.getConst? addr = some constant) (standalone : constant.info.Standalone) : + env.resolve addr = some (.member addr 0) := + resolve_eq_some_iff.mpr (.standalone stored standalone) + +/-- Only Muts blocks have members. -/ +theorem mutMember?_some {env : Env} {block : Address} {index : UInt64} {member : MutConst} + (found : env.mutMember? block index = some member) : + ∃ constant members, env.getConst? block = some constant ∧ + constant.info = .muts members ∧ members[index.toNat]? = some member := by + obtain ⟨constant, stored, found⟩ : ∃ constant, env.getConst? block = some constant ∧ + (match constant.info with + | .muts members => members[index.toNat]? + | _ => none) = some member := by + unfold mutMember? at found + revert found + cases stored : env.getConst? block with + | none => intro found; cases found + | some constant => intro found; exact ⟨constant, rfl, found⟩ + revert found + cases info : constant.info with + | muts members => intro found; exact ⟨constant, members, stored, info, found⟩ + | _ => simp + +/-- Every located address is stored. -/ +theorem Locates.stored {env : Env} {addr : Address} {ref : ConstRef Address} + (located : env.Locates addr ref) : ∃ constant, env.getConst? addr = some constant := by + cases located <;> exact ⟨_, by assumption⟩ + +/-- The block of every coordinate is stored. -/ +theorem Locates.block_stored {env : Env} {addr : Address} {ref : ConstRef Address} + (located : env.Locates addr ref) : ∃ constant, env.getConst? ref.block = some constant := by + cases located with + | standalone stored _ => exact ⟨_, stored⟩ + | defn _ _ member | recr _ _ member | indc _ _ member | ctor _ _ member _ => + obtain ⟨constant, _, stored, _, _⟩ := mutMember?_some member + exact ⟨constant, stored⟩ + +/-- The standalone coordinate of an address is reached only by its own +standalone record: a projection cannot name a standalone as its block. -/ +theorem resolve_member_self_iff {env : Env} {addr : Address} : + env.resolve addr = some (.member addr 0) ↔ + ∃ constant, env.getConst? addr = some constant ∧ constant.info.Standalone := by + constructor + · intro resolved + generalize target : (ConstRef.member addr 0 : ConstRef Address) = ref at resolved + cases resolve_eq_some_iff.mp resolved with + | standalone stored standalone => exact ⟨_, stored, standalone⟩ + | defn stored info member | recr stored info member | indc stored info member => + obtain ⟨blockConstant, members, blockStored, blockInfo, _⟩ := mutMember?_some member + rw [← (ConstRef.member.inj target).1] at blockStored + cases Option.some.inj (stored.symm.trans blockStored) + rw [info] at blockInfo + cases blockInfo + | ctor => cases target + · rintro ⟨constant, stored, standalone⟩ + exact resolve_standalone stored standalone + +/-- The projection record that resolves to a reference, determined by the +member found at that reference. -/ +def recordOf (env : Env) : ConstRef Address → Option ConstantInfo + | .member block index => + match env.mutMember? block index.toUInt64 with + | some (.defn _) => some (.dPrj ⟨index.toUInt64, block⟩) + | some (.recr _) => some (.rPrj ⟨index.toUInt64, block⟩) + | some (.indc _) => some (.iPrj ⟨index.toUInt64, block⟩) + | none => none + | .ctor block index cidx => + match env.mutMember? block index.toUInt64 with + | some (.indc family) => + match family.ctors[cidx.toUInt64.toNat]? with + | some _ => some (.cPrj ⟨index.toUInt64, cidx.toUInt64, block⟩) + | none => none + | _ => none + +/-- A located address stores either the standalone record of its own block or +the unique projection record of its coordinate. -/ +theorem Locates.standalone_or_record {env : Env} {addr : Address} {ref : ConstRef Address} + (located : env.Locates addr ref) : + (ref = .member addr 0 ∧ ∃ constant, env.getConst? addr = some constant ∧ + constant.info.Standalone) ∨ + (∃ constant, env.getConst? addr = some constant ∧ constant.info.Projection ∧ + env.recordOf ref = some constant.info) := by + cases located with + | standalone stored standalone => exact .inl ⟨rfl, _, stored, standalone⟩ + | @defn constant projection _ stored info member => + refine .inr ⟨constant, stored, by rw [info]; trivial, ?_⟩ + simp only [recordOf, Nat.toUInt64_eq, UInt64.ofNat_toNat, member, info] + | @recr constant projection _ stored info member => + refine .inr ⟨constant, stored, by rw [info]; trivial, ?_⟩ + simp only [recordOf, Nat.toUInt64_eq, UInt64.ofNat_toNat, member, info] + | @indc constant projection _ stored info member => + refine .inr ⟨constant, stored, by rw [info]; trivial, ?_⟩ + simp only [recordOf, Nat.toUInt64_eq, UInt64.ofNat_toNat, member, info] + | @ctor constant projection _ _ stored info member constructor => + refine .inr ⟨constant, stored, by rw [info]; trivial, ?_⟩ + simp only [recordOf, Nat.toUInt64_eq, UInt64.ofNat_toNat, member, constructor, info] + +/-- A record for a member coordinate needs a member at that coordinate. -/ +theorem recordOf_member_some {env : Env} {block : Address} {index : Nat} {record : ConstantInfo} + (found : env.recordOf (.member block index) = some record) : + ∃ member, env.mutMember? block index.toUInt64 = some member := by + simp only [recordOf] at found + revert found + cases env.mutMember? block index.toUInt64 with + | none => intro found; cases found + | some member => intro _; exact ⟨member, rfl⟩ + +/-- A standalone record cannot own members. -/ +theorem Standalone.no_member {env : Env} {addr : Address} {constant : Constant} + {index : UInt64} {member : MutConst} + (stored : env.getConst? addr = some constant) (standalone : constant.info.Standalone) + (found : env.mutMember? addr index = some member) : False := by + obtain ⟨blockConstant, members, blockStored, blockInfo, _⟩ := mutMember?_some found + cases Option.some.inj (stored.symm.trans blockStored) + rw [blockInfo] at standalone + exact standalone + +/-- Two addresses with one coordinate are equal or store identical projection records. -/ +theorem Locates.same_ref {env : Env} {addr addr' : Address} {ref : ConstRef Address} + (left : env.Locates addr ref) (right : env.Locates addr' ref) : + addr = addr' ∨ ∃ constant constant', env.getConst? addr = some constant ∧ + env.getConst? addr' = some constant' ∧ constant.info.Projection ∧ + constant.info = constant'.info := by + rcases left.standalone_or_record with ⟨same, constant, stored, standalone⟩ | + ⟨constant, stored, projection, record⟩ + · subst same + rcases right.standalone_or_record with ⟨same', _⟩ | ⟨_, _, _, record'⟩ + · exact .inl (ConstRef.member.inj same').1 + · obtain ⟨member, found⟩ := recordOf_member_some record' + exact (Standalone.no_member stored standalone found).elim + · rcases right.standalone_or_record with ⟨same', constant', stored', standalone'⟩ | + ⟨constant', stored', projection', record'⟩ + · subst same' + obtain ⟨member, found⟩ := recordOf_member_some record + exact (Standalone.no_member stored' standalone' found).elim + · exact .inr ⟨constant, constant', stored, stored', projection, + Option.some.inj (record.symm.trans record')⟩ + +/-- Resolution is injective on standalone coordinates. -/ +theorem resolve_standalone_injective {env : Env} {addr addr' : Address} + (left : env.resolve addr = some (.member addr 0)) + (right : env.resolve addr' = some (.member addr 0)) : addr' = addr := by + rcases Locates.same_ref (resolve_eq_some_iff.mp left) (resolve_eq_some_iff.mp right) with + same | ⟨constant, _, stored, _, projection, _⟩ + · exact same.symm + · obtain ⟨found, foundStored, standalone⟩ := resolve_member_self_iff.mp left + cases Option.some.inj (stored.symm.trans foundStored) + exact absurd projection (ConstantInfo.not_projection_of_standalone standalone) + +end Env + +end Ixon + +namespace Ixon.LazyConstant + +/-- Materialization that surfaces no error also succeeds silently. -/ +theorem get?_of_get {lazy : LazyConstant} {constant : Constant} (materialized : lazy.get = .ok constant) : + lazy.get? = some constant := by + unfold get at materialized + unfold get? + cases cache : lazy.cache with + | none => + rw [cache] at materialized + simp only [materialized] + rfl + | some cached => + rw [cache] at materialized + cases materialized + rfl + +end Ixon.LazyConstant + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe v + +private instance : LawfulBEq ByteArray where + eq_of_beq {left right} h := by + cases left + cases right + exact congrArg ByteArray.mk (eq_of_beq h) + rfl {bytes} := beq_self_eq_true bytes.data + +private instance : LawfulBEq Address where + eq_of_beq {left right} h := by + cases left + cases right + exact congrArg Address.mk (eq_of_beq h) + rfl {addr} := by + cases addr + exact beq_self_eq_true (α := ByteArray) _ + +private instance : LawfulHashable Address where + hash_eq left right h := by rw [eq_of_beq h] + +/-! ### Stored records -/ + +/-- A materialized constant comes from a stored lazy entry. -/ +theorem lazy_of_getConst? {env : Ixon.Env} {addr : Address} {constant : Ixon.Constant} + (stored : env.getConst? addr = some constant) : + ∃ lazy, env.consts[addr]? = some lazy ∧ lazy.get? = some constant := by + simp only [Ixon.Env.getConst?, Std.HashMap.get?_eq_getElem?] at stored + revert stored + cases lookup : env.consts[addr]? with + | none => intro stored; cases stored + | some lazy => intro stored; exact ⟨lazy, rfl, stored⟩ + +/-- A stored lazy entry that materializes is the source constant. -/ +theorem getConst?_of_lazy {env : Ixon.Env} {addr : Address} {lazy : Ixon.LazyConstant} + {constant : Ixon.Constant} (lookup : env.consts[addr]? = some lazy) + (materialized : lazy.get = .ok constant) : env.getConst? addr = some constant := by + simp only [Ixon.Env.getConst?, Std.HashMap.get?_eq_getElem?, lookup, Option.bind_some] + exact Ixon.LazyConstant.get?_of_get materialized + +/-- Every stored constant is a source key. -/ +theorem mem_keys_of_getConst? {env : Ixon.Env} {addr : Address} {constant : Ixon.Constant} + (stored : env.getConst? addr = some constant) : addr ∈ env.consts.keys := by + obtain ⟨lazy, lookup, _⟩ := lazy_of_getConst? stored + rw [Std.HashMap.mem_keys, ← Std.HashMap.isSome_getElem?_iff_mem, lookup] + rfl + +/-- Every resolved address is a source key. -/ +theorem resolve_mem_keys {env : Ixon.Env} {addr : Address} {ref : ConstRef Address} + (resolved : env.resolve addr = some ref) : addr ∈ env.consts.keys := by + obtain ⟨constant, stored⟩ := (Ixon.Env.resolve_eq_some_iff.mp resolved).stored + exact mem_keys_of_getConst? stored + +/-- The block of every coordinate is a source key. -/ +theorem resolve_block_mem_keys {env : Ixon.Env} {addr : Address} {ref : ConstRef Address} + (resolved : env.resolve addr = some ref) : ref.block ∈ env.consts.keys := by + obtain ⟨constant, stored⟩ := (Ixon.Env.resolve_eq_some_iff.mp resolved).block_stored + exact mem_keys_of_getConst? stored + +/-! ### Agreement with the certified adapter -/ + +/-- The certified adapter's coordinate function is the canonical map whenever +its object list agrees with the environment's stored constants. -/ +theorem certified_resolveReference?_eq {env : Ixon.Env} {objects : Ix.Certified.Objects} + (agree : ∀ addr, Ix.Certified.lookup objects addr = env.getConst? addr) (addr : Address) : + Ix.Certified.resolveReference? objects addr = env.resolve addr := by + have member : ∀ block index, + Ix.Certified.mutMember? objects block index = env.mutMember? block index := by + intro block index + simp only [Ix.Certified.mutMember?, Ixon.Env.mutMember?, agree, Option.bind_eq_bind] + cases env.getConst? block with + | none => rfl + | some source => + simp only [Option.bind_some] + cases source.info <;> rfl + simp only [Ix.Certified.resolveReference?, Ixon.Env.resolve, agree, Option.bind_eq_bind] + cases env.getConst? addr with + | none => rfl + | some source => + simp only [Option.bind_some] + cases source.info with + | defn _ | recr _ | axio _ | quot _ | muts _ => rfl + | dPrj projection => + simp only [Ixon.Env.resolveInfo, member] + cases env.mutMember? projection.block projection.idx with + | none => rfl + | some found => cases found <;> rfl + | rPrj projection => + simp only [Ixon.Env.resolveInfo, member] + cases env.mutMember? projection.block projection.idx with + | none => rfl + | some found => cases found <;> rfl + | iPrj projection => + simp only [Ixon.Env.resolveInfo, member] + cases env.mutMember? projection.block projection.idx with + | none => rfl + | some found => cases found <;> rfl + | cPrj projection => + simp only [Ixon.Env.resolveInfo, member] + cases env.mutMember? projection.block projection.idx with + | none => rfl + | some found => + cases found with + | indc family => + simp only [Option.bind_some] + cases family.ctors[projection.cidx.toNat]? <;> rfl + | defn _ | recr _ => rfl + +/-! ### Standalone classification -/ + +/-- The loader's block routing is absent exactly at standalone records. -/ +theorem ingressBlockAddr?_eq_none_iff {addr : Address} {info : Ixon.ConstantInfo} : + ingressBlockAddr? addr info = none ↔ info.Standalone := by + cases info <;> simp [ingressBlockAddr?, Ixon.ConstantInfo.Standalone] + +/-- The driver emits a standalone item exactly at standalone records. -/ +theorem ofConstantInfo_standalone_iff {addr : Address} {info : Ixon.ConstantInfo} : + AnonWorkItem.ofConstantInfo addr info = some (.standalone addr) ↔ info.Standalone := by + cases info with + | muts members => + simp only [AnonWorkItem.ofConstantInfo, Ixon.ConstantInfo.Standalone, iff_false] + split <;> simp + | _ => simp [AnonWorkItem.ofConstantInfo, Ixon.ConstantInfo.Standalone] + +/-- The verified loader returns the stored constant. -/ +theorem getConst?_of_verified {env : Ixon.Env} {addr : Address} {constant : Ixon.Constant} + (verified : getConstVerified env addr true = .ok (some constant)) : + env.getConst? addr = some constant := by + unfold getConstVerified at verified + revert verified + cases lookup : env.consts[addr]? with + | none => intro verified; cases verified + | some lazy => + simp only [Bool.true_or, if_true] + by_cases mismatch : (Address.blake3 lazy.rawBytes != addr) = true + · simp only [mismatch, if_true] + intro verified + cases verified + · simp only [mismatch, Bool.false_eq_true, if_false] + cases materialized : lazy.get with + | error err => intro verified; cases verified + | ok found => + intro verified + simp only [Pure.pure, Except.pure, Except.ok.injEq, + Option.some.injEq] at verified + subst verified + exact getConst?_of_lazy lookup materialized + +/-- A predicted standalone declaration has the canonical standalone coordinate. -/ +theorem resolve_of_predictStandalone? {env : Ixon.Env} {addr : Address} {expected : KConst .anon} + (predicted : predictStandalone? env addr = .ok (some expected)) : + env.resolve addr = some (.member addr 0) := by + obtain ⟨constant, verified, standalone, _⟩ := predictStandalone?_some predicted + exact Ixon.Env.resolve_standalone (getConst?_of_verified verified) + (ingressBlockAddr?_eq_none_iff.mp standalone) + +/-! ### Injectivity -/ + +/-- The address the compiler stores and the kernel computes for a projection record. -/ +def projectionAddr? : Ixon.ConstantInfo → Option Address + | .dPrj projection => some (defnProjAddr projection.block projection.idx) + | .rPrj projection => some (recrProjAddr projection.block projection.idx) + | .iPrj projection => some (indcProjAddr projection.block projection.idx) + | .cPrj projection => some (ctorProjAddr projection.block projection.idx projection.cidx) + | _ => none + +/-- Every stored projection record sits at its generated address, as the +compiler stores it. -/ +def ProjectionsCanonical (env : Ixon.Env) : Prop := + ∀ ⦃addr : Address⦄ ⦃constant : Ixon.Constant⦄ ⦃generated : Address⦄, + env.getConst? addr = some constant → projectionAddr? constant.info = some generated → + addr = generated + +theorem projectionAddr?_of_projection {info : Ixon.ConstantInfo} (projection : info.Projection) : + ∃ generated, projectionAddr? info = some generated := by + cases info <;> simp_all [projectionAddr?, Ixon.ConstantInfo.Projection] + +/-- With canonical projection addresses, resolution is injective on its domain. -/ +theorem resolve_injective {env : Ixon.Env} (canonical : ProjectionsCanonical env) + {addr addr' : Address} {ref : ConstRef Address} + (left : env.resolve addr = some ref) (right : env.resolve addr' = some ref) : addr = addr' := by + rcases Ixon.Env.Locates.same_ref (Ixon.Env.resolve_eq_some_iff.mp left) + (Ixon.Env.resolve_eq_some_iff.mp right) with + same | ⟨constant, constant', stored, stored', projection, same⟩ + · exact same + · obtain ⟨generated, address⟩ := projectionAddr?_of_projection projection + rw [canonical stored address, canonical stored' (same ▸ address)] + +/-! ### Enumeration -/ + +/-- Every stored key materializes, with the cheap tag naming its record. This +finite source contract relates the driver's tag dispatch to the stored records. -/ +def SourceMaterializes (env : Ixon.Env) : Prop := + ∀ ⦃addr : Address⦄ ⦃lazy : Ixon.LazyConstant⦄, env.consts[addr]? = some lazy → + ∃ constant, lazy.get = .ok constant ∧ lazy.peekTag = .ok (constantInfoTag constant.info) + +/-- Finite preflight for `SourceMaterializes`. -/ +def sourceMaterializesCheck (env : Ixon.Env) : Bool := + env.consts.toList.all fun (_, lazy) => + match lazy.get, lazy.peekTag with + | .ok constant, .ok tag => decide (tag = constantInfoTag constant.info) + | _, _ => false + +theorem SourceMaterializes.ofCheck {env : Ixon.Env} (checked : sourceMaterializesCheck env = true) : + SourceMaterializes env := by + intro addr lazy stored + have row := List.all_eq_true.mp checked (addr, lazy) + (Std.HashMap.mem_toList_iff_getElem?_eq_some.mpr stored) + dsimp only at row + revert row + cases materialized : lazy.get with + | error err => simp + | ok constant => + cases tag : lazy.peekTag with + | error err => simp + | ok found => + simp only [decide_eq_true_eq] + intro row + exact ⟨constant, rfl, by rw [row]⟩ + +/-- A materializing standalone key is classified as its own standalone item. -/ +theorem buildAnonWorkItem_of_standalone {env : Ixon.Env} {addr : Address} + {lazy : Ixon.LazyConstant} {constant : Ixon.Constant} + (materializes : SourceMaterializes env) (lookup : env.consts[addr]? = some lazy) + (materialized : lazy.get = .ok constant) (standalone : constant.info.Standalone) : + buildAnonWorkItem env addr = .ok (some (.standalone addr)) := by + obtain ⟨found, foundMaterialized, tag⟩ := materializes lookup + cases Except.ok.inj (materialized.symm.trans foundMaterialized) + unfold buildAnonWorkItem + simp only [Std.HashMap.get?_eq_getElem?, lookup, tag] + cases info : constant.info <;> simp_all [constantInfoTag, Ixon.ConstantInfo.Standalone, + Bind.bind, Except.bind, Pure.pure, Except.pure] + +/-- A standalone item is emitted only at a materializing standalone key, which +then has the canonical standalone coordinate. -/ +theorem buildAnonWorkItem_standalone {env : Ixon.Env} {addr item : Address} + (materializes : SourceMaterializes env) + (built : buildAnonWorkItem env addr = .ok (some (.standalone item))) : + item = addr ∧ env.resolve addr = some (.member addr 0) := by + unfold buildAnonWorkItem at built + revert built + cases lookup : env.consts.get? addr with + | none => intro built; cases built + | some lazy => + obtain ⟨constant, materialized, tag⟩ := materializes lookup + have stored := getConst?_of_lazy lookup materialized + simp only [tag] + cases info : constant.info with + | defn _ | recr _ | axio _ | quot _ => + simp only [constantInfoTag, Bind.bind, Except.bind, Pure.pure, Except.pure, + Except.ok.injEq, Option.some.injEq, AnonWorkItem.standalone.injEq] + intro same + exact ⟨same.symm, Ixon.Env.resolve_standalone stored (by rw [info]; trivial)⟩ + | iPrj _ | cPrj _ | rPrj _ | dPrj _ => + simp [constantInfoTag, Bind.bind, Except.bind, Pure.pure, Except.pure] + | muts members => + simp only [constantInfoTag, materialized, info, AnonWorkItem.ofConstantInfo, Bind.bind, + Except.bind, Pure.pure, Except.pure] + split <;> simp + +private theorem filterMapM_ok_mem {α β : Type} {f : α → Except IngressErr (Option β)} : + ∀ {xs : List α} {ys : List β}, xs.filterMapM f = .ok ys → ∀ y ∈ ys, ∃ x ∈ xs, f x = .ok (some y) + | [], ys, run, y, mem => by + simp only [List.filterMapM_nil] at run + cases run + cases mem + | x :: xs, ys, run, y, mem => by + rw [List.filterMapM_cons] at run + revert run + cases step : f x with + | error err => intro run; cases run + | ok optional => + cases optional with + | none => + intro run + obtain ⟨x', mem', run'⟩ := filterMapM_ok_mem run y mem + exact ⟨x', List.mem_cons_of_mem _ mem', run'⟩ + | some b => + simp only [Bind.bind, Except.bind] + revert mem + cases rest : xs.filterMapM f with + | error err => intro mem run; cases run + | ok rest' => + intro mem run + cases run + rcases List.mem_cons.mp mem with rfl | mem' + · exact ⟨x, List.mem_cons_self .., step⟩ + · obtain ⟨x', mem', run'⟩ := filterMapM_ok_mem rest y mem' + exact ⟨x', List.mem_cons_of_mem _ mem', run'⟩ + +private theorem filterMapM_ok_of_mem {α β : Type} {f : α → Except IngressErr (Option β)} : + ∀ {xs : List α} {ys : List β}, xs.filterMapM f = .ok ys → ∀ x ∈ xs, ∀ y, f x = .ok (some y) → + y ∈ ys + | [], _, _, _, mem, _, _ => by cases mem + | x :: xs, ys, run, x', mem, y, step => by + rw [List.filterMapM_cons] at run + revert run + rcases List.mem_cons.mp mem with rfl | mem' + · rw [step] + simp only [Bind.bind, Except.bind] + cases rest : xs.filterMapM f with + | error err => intro run; cases run + | ok rest' => + intro run + cases run + exact List.mem_cons_self .. + · cases first : f x with + | error err => intro run; cases run + | ok optional => + cases optional with + | none => + intro run + exact filterMapM_ok_of_mem run x' mem' y step + | some b => + simp only [Bind.bind, Except.bind] + cases rest : xs.filterMapM f with + | error err => intro run; cases run + | ok rest' => + intro run + cases run + exact List.mem_cons_of_mem _ (filterMapM_ok_of_mem rest x' mem' y step) + +/-- The production enumeration is the list enumeration in key order. -/ +private theorem buildAnonWork_list {env : Ixon.Env} {work : Array AnonWorkItem} + (enumerated : buildAnonWork env = .ok work) : + (orderedAnonConstAddrs env).toList.filterMapM (buildAnonWorkItem env) = .ok work.toList := by + unfold buildAnonWork at enumerated + rw [← Array.toArray_toList (xs := orderedAnonConstAddrs env), List.filterMapM_toArray] + at enumerated + revert enumerated + cases run : (orderedAnonConstAddrs env).toList.filterMapM (buildAnonWorkItem env) with + | error err => intro enumerated; cases enumerated + | ok items => + intro enumerated + cases enumerated + rfl + +/-- A standalone item of the enumerated work has the canonical standalone coordinate. -/ +theorem resolve_of_work_standalone {env : Ixon.Env} {work : Array AnonWorkItem} {addr : Address} + (materializes : SourceMaterializes env) (enumerated : buildAnonWork env = .ok work) + (listed : AnonWorkItem.standalone addr ∈ work) : + env.resolve addr = some (.member addr 0) := by + obtain ⟨source, _, built⟩ := filterMapM_ok_mem (buildAnonWork_list enumerated) _ + (Array.mem_def.mp listed) + obtain ⟨same, resolved⟩ := buildAnonWorkItem_standalone materializes built + subst same + exact resolved + +/-- Every source key is visited by the ordered enumeration. -/ +theorem mem_orderedAnonConstAddrs {env : Ixon.Env} {addr : Address} : + addr ∈ orderedAnonConstAddrs env ↔ addr ∈ env.consts.keys := by + rw [orderedAnonConstAddrs, (Ix.Compile.Verify.QSort.qsort_perm _ _ _ _).mem_iff, + List.mem_toArray] + +/-- An address with the canonical standalone coordinate is enumerated as a standalone item. -/ +theorem work_standalone_of_resolve {env : Ixon.Env} {work : Array AnonWorkItem} {addr : Address} + (materializes : SourceMaterializes env) (enumerated : buildAnonWork env = .ok work) + (resolved : env.resolve addr = some (.member addr 0)) : + AnonWorkItem.standalone addr ∈ work := by + obtain ⟨constant, stored, standalone⟩ := Ixon.Env.resolve_member_self_iff.mp resolved + obtain ⟨lazy, lookup, materialized⟩ := lazy_of_getConst? stored + obtain ⟨found, foundMaterialized, _⟩ := materializes lookup + have same : found = constant := + Option.some.inj ((Ixon.LazyConstant.get?_of_get foundMaterialized).symm.trans materialized) + subst same + have ordered : addr ∈ (orderedAnonConstAddrs env).toList := by + rw [← Array.mem_def, mem_orderedAnonConstAddrs] + exact mem_keys_of_getConst? stored + exact Array.mem_def.mpr (filterMapM_ok_of_mem (buildAnonWork_list enumerated) addr ordered _ + (buildAnonWorkItem_of_standalone materializes lookup foundMaterialized standalone)) + +/-- Under the finite source contract, the enumerated standalone items are +exactly the addresses with a standalone coordinate. -/ +theorem work_standalone_iff {env : Ixon.Env} {work : Array AnonWorkItem} {addr : Address} + (materializes : SourceMaterializes env) (enumerated : buildAnonWork env = .ok work) : + AnonWorkItem.standalone addr ∈ work ↔ env.resolve addr = some (.member addr 0) := + ⟨resolve_of_work_standalone materializes enumerated, + work_standalone_of_resolve materializes enumerated⟩ + +/-- A work position lists its item. -/ +theorem WorkPosition.mem {work : Array AnonWorkItem} {item : AnonWorkItem} + (position : WorkPosition work item) : item ∈ work := by + rw [Array.mem_def, position.split] + simp + +/-! ### Block members -/ + +/-- The projection record generated for a block member. -/ +def memberRecord (block : Address) (index : UInt64) : Ixon.MutConst → Ixon.ConstantInfo + | .defn _ => .dPrj ⟨index, block⟩ + | .recr _ => .rPrj ⟨index, block⟩ + | .indc _ => .iPrj ⟨index, block⟩ + +/-- The projection address generated for a block member, the head of +`anonMemberTargets`. -/ +def memberAddr (block : Address) (index : UInt64) : Ixon.MutConst → Address + | .defn _ => defnProjAddr block index + | .recr _ => recrProjAddr block index + | .indc _ => indcProjAddr block index + +/-- Every generated projection address of a stored block stores its own +record, as the compiler emits them. -/ +structure ProjectionsStored (env : Ixon.Env) : Prop where + member : ∀ ⦃block : Address⦄ ⦃constant : Ixon.Constant⦄ ⦃members : Array Ixon.MutConst⦄ + ⦃index : UInt64⦄ ⦃member : Ixon.MutConst⦄, + env.getConst? block = some constant → constant.info = .muts members → + members[index.toNat]? = some member → + ∃ record, env.getConst? (memberAddr block index member) = some record ∧ + record.info = memberRecord block index member + ctor : ∀ ⦃block : Address⦄ ⦃constant : Ixon.Constant⦄ ⦃members : Array Ixon.MutConst⦄ + ⦃index : UInt64⦄ ⦃family : Ixon.Inductive⦄ ⦃cidx : UInt64⦄ ⦃constructor : Ixon.Constructor⦄, + env.getConst? block = some constant → constant.info = .muts members → + members[index.toNat]? = some (.indc family) → family.ctors[cidx.toNat]? = some constructor → + ∃ record, env.getConst? (ctorProjAddr block index cidx) = some record ∧ + record.info = .cPrj ⟨index, cidx, block⟩ + +/-- A stored block's members are found through `mutMember?`. -/ +theorem mutMember?_of_stored {env : Ixon.Env} {block : Address} {constant : Ixon.Constant} + {members : Array Ixon.MutConst} {index : UInt64} {member : Ixon.MutConst} + (stored : env.getConst? block = some constant) (info : constant.info = .muts members) + (found : members[index.toNat]? = some member) : + env.mutMember? block index = some member := by + simp [Ixon.Env.mutMember?, stored, info, found] + +/-- The generated member address resolves to the member coordinate. -/ +theorem resolve_memberAddr {env : Ixon.Env} (projections : ProjectionsStored env) + {block : Address} {constant : Ixon.Constant} {members : Array Ixon.MutConst} + {index : UInt64} {member : Ixon.MutConst} + (stored : env.getConst? block = some constant) (info : constant.info = .muts members) + (found : members[index.toNat]? = some member) : + env.resolve (memberAddr block index member) = some (.member block index.toNat) := by + obtain ⟨record, recordStored, recordInfo⟩ := projections.member stored info found + have located := mutMember?_of_stored stored info found + apply Ixon.Env.resolve_eq_some_iff.mpr + cases member with + | defn _ => exact .defn recordStored recordInfo located + | recr _ => exact .recr recordStored recordInfo located + | indc _ => exact .indc recordStored recordInfo located + +/-- The generated constructor address resolves to the constructor coordinate. -/ +theorem resolve_ctorProjAddr {env : Ixon.Env} (projections : ProjectionsStored env) + {block : Address} {constant : Ixon.Constant} {members : Array Ixon.MutConst} + {index : UInt64} {family : Ixon.Inductive} {cidx : UInt64} {constructor : Ixon.Constructor} + (stored : env.getConst? block = some constant) (info : constant.info = .muts members) + (found : members[index.toNat]? = some (.indc family)) + (ctorFound : family.ctors[cidx.toNat]? = some constructor) : + env.resolve (ctorProjAddr block index cidx) = some (.ctor block index.toNat cidx.toNat) := by + obtain ⟨record, recordStored, recordInfo⟩ := projections.ctor stored info found ctorFound + exact Ixon.Env.resolve_eq_some_iff.mpr + (.ctor recordStored recordInfo (mutMember?_of_stored stored info found) ctorFound) + +private theorem toUInt64_toNat_of_lt {index : Nat} (bound : index < UInt64.size) : + index.toUInt64.toNat = index := by + simp [bound] + +/-- Every enumerated target of a stored block resolves into that block. Index +bounds are the arithmetic run assumptions of the projection payloads. -/ +theorem resolve_anonBlockTargets {env : Ixon.Env} (projections : ProjectionsStored env) + {block : Address} {constant : Ixon.Constant} {members : Array Ixon.MutConst} + (stored : env.getConst? block = some constant) (info : constant.info = .muts members) + (bounded : members.size ≤ UInt64.size) + (ctorsBounded : ∀ family, .indc family ∈ members → family.ctors.size ≤ UInt64.size) : + ∀ target ∈ anonBlockTargets block members, ∃ ref, env.resolve target = some ref ∧ + ref.block = block := by + intro target listed + obtain ⟨index, inRange, listed⟩ := Array.mem_flatMap.mp listed + have indexLt : index < members.size := Array.mem_range.mp inRange + have indexBound : index < UInt64.size := Nat.lt_of_lt_of_le indexLt bounded + rw [getElem!_pos members index indexLt] at listed + have found : members[index.toUInt64.toNat]? = some members[index] := by + rw [toUInt64_toNat_of_lt indexBound] + exact Array.getElem?_eq_some_iff.mpr ⟨indexLt, rfl⟩ + have familyMem : ∀ family, members[index] = .indc family → Ixon.MutConst.indc family ∈ members := by + intro family same + rw [← same] + exact Array.getElem_mem indexLt + revert listed found familyMem + cases member : members[index] with + | defn definition => + intro listed found _ + simp only [anonMemberTargets, Array.mem_singleton] at listed + subst listed + exact ⟨_, resolve_memberAddr projections stored info found, rfl⟩ + | recr recursor => + intro listed found _ + simp only [anonMemberTargets, Array.mem_singleton] at listed + subst listed + exact ⟨_, resolve_memberAddr projections stored info found, rfl⟩ + | indc family => + intro listed found familyMem + simp only [anonMemberTargets, Array.mem_append, Array.mem_singleton, Array.mem_map, + Array.mem_range] at listed + rcases listed with same | ⟨cidx, cidxLt, same⟩ + · subst same + exact ⟨_, resolve_memberAddr projections stored info found, rfl⟩ + · subst same + have cidxBound : cidx < UInt64.size := + Nat.lt_of_lt_of_le cidxLt (ctorsBounded family (familyMem family rfl)) + have ctorFound : family.ctors[cidx.toUInt64.toNat]? = some family.ctors[cidx] := by + rw [toUInt64_toNat_of_lt cidxBound] + exact Array.getElem?_eq_some_iff.mpr ⟨cidxLt, rfl⟩ + exact ⟨_, resolve_ctorProjAddr projections stored info found ctorFound, rfl⟩ + +/-! ### Derived static bindings -/ + +/-- A standalone model binding under the canonical map needs only the source +prediction, the admitted entry, and the reading of the predicted type. -/ +def StandaloneModelBinding.ofSource {source : Ixon.Env} {entries : Model.Environment Address} + {id : KId .anon} {entry : ConstantEntry Address} (constant : KConst .anon) + (predicted : predictStandalone? source id.addr = .ok (some constant)) + (found : entries (.member id.addr 0) = some entry) + (universes : constant.lvls.toNat = entry.universes) + (reading : readScopedExpr? source.resolve [] constant.ty = some entry.type.erase) : + StandaloneModelBinding source source.resolve entries id (.member id.addr 0) entry := + { constant, predicted, resolved := resolve_of_predictStandalone? predicted, found, universes, + reading } + +/-- The primitive `Nat` binding under the canonical map, from the stored +records locating the primitive address. -/ +def PrimitiveNatBinding.ofLocated {source : Ixon.Env} {m : Mode} + {entries : Model.Environment Address} {prims : Primitives m} {ref : ConstRef Address} + (located : source.Locates prims.nat.addr ref) (entry : ConstantEntry Address) + (zero succ : ConstRef Address) (level : VLevel) (found : entries ref = some entry) + (natural : .natural zero succ ∈ entry.facts) (monomorphic : entry.universes = 0) + (typeSort : entry.type = .sort level) (closed : level.WF 0) : + PrimitiveNatBinding source.resolve entries prims := + { ref, entry, zero, succ, level, resolved := Ixon.Env.resolve_eq_some_iff.mpr located, found, + natural, monomorphic, typeSort, closed } + +/-- The primitive `Nat` address stores a standalone record. -/ +def PrimitiveNatBinding.ofStandalone {source : Ixon.Env} {m : Mode} + {entries : Model.Environment Address} {prims : Primitives m} {constant : Ixon.Constant} + (stored : source.getConst? prims.nat.addr = some constant) + (standalone : constant.info.Standalone) (entry : ConstantEntry Address) + (zero succ : ConstRef Address) (level : VLevel) + (found : entries (.member prims.nat.addr 0) = some entry) + (natural : .natural zero succ ∈ entry.facts) (monomorphic : entry.universes = 0) + (typeSort : entry.type = .sort level) (closed : level.WF 0) : + PrimitiveNatBinding source.resolve entries prims := + .ofLocated (.standalone stored standalone) entry zero succ level found natural monomorphic + typeSort closed + +/-- The primitive `Nat` address stores an inductive projection into a block +member. -/ +def PrimitiveNatBinding.ofInductive {source : Ixon.Env} {m : Mode} + {entries : Model.Environment Address} {prims : Primitives m} {constant : Ixon.Constant} + {projection : Ixon.InductiveProj} {family : Ixon.Inductive} + (stored : source.getConst? prims.nat.addr = some constant) + (info : constant.info = .iPrj projection) + (member : source.mutMember? projection.block projection.idx = some (.indc family)) + (entry : ConstantEntry Address) (zero succ : ConstRef Address) (level : VLevel) + (found : entries (.member projection.block projection.idx.toNat) = some entry) + (natural : .natural zero succ ∈ entry.facts) (monomorphic : entry.universes = 0) + (typeSort : entry.type = .sort level) (closed : level.WF 0) : + PrimitiveNatBinding source.resolve entries prims := + .ofLocated (.indc stored info member) entry zero succ level found natural monomorphic + typeSort closed + +/-! ### Environment theorems with the canonical map -/ + +/-- A declaration's reference is its canonical standalone coordinate. -/ +def AxiomSpec.Canonical (spec : AxiomSpec Address) : Prop := + spec.ref = .member spec.id.addr 0 + +def DefinitionSpec.Canonical (spec : DefinitionSpec Address) : Prop := + spec.ref = .member spec.input.id.addr 0 + +/-- Every entry of an interface is at a canonical coordinate whose address is +not among the pending declarations. -/ +def CanonicalOutside (entries : Model.Environment Address) (pending : List Address) : Prop := + ∀ ref entry, entries ref = some entry → ∃ addr, ref = .member addr 0 ∧ addr ∉ pending + +/-- Canonical axiom interfaces contain only canonical coordinates. -/ +theorem axiomEnvironment_canonical {axioms : List (AxiomSpec Address)} + (canonical : ∀ spec ∈ axioms, spec.Canonical) {ref : ConstRef Address} + {entry : ConstantEntry Address} (present : axiomEnvironment axioms ref = some entry) : + ∃ spec ∈ axioms, ref = .member spec.id.addr 0 := by + induction axioms with + | nil => cases present + | cons spec rest ih => + simp only [axiomEnvironment, Model.Environment.insert] at present + split at present + · rename_i same + exact ⟨spec, List.mem_cons_self .., same.trans (canonical spec (List.mem_cons_self ..))⟩ + · obtain ⟨found, listed, same⟩ := ih (fun other listed => canonical other + (List.mem_cons_of_mem _ listed)) present + exact ⟨found, List.mem_cons_of_mem _ listed, same⟩ + +/-- With distinct addresses, every listed canonical axiom keeps its own entry. -/ +theorem axiomEnvironment_installed {axioms : List (AxiomSpec Address)} + (canonical : ∀ spec ∈ axioms, spec.Canonical) + (distinct : (axioms.map (·.id.addr)).Nodup) {spec : AxiomSpec Address} + (listed : spec ∈ axioms) : axiomEnvironment axioms spec.ref = some spec.entry := by + induction axioms with + | nil => cases listed + | cons first rest ih => + simp only [List.map_cons, List.nodup_cons, List.mem_map] at distinct + rcases List.mem_cons.mp listed with same | later + · subst same + exact Model.Environment.insert_same .. + · have different : spec.ref ≠ first.ref := by + intro same + rw [canonical spec listed, canonical first (List.mem_cons_self ..)] at same + exact distinct.1 ⟨spec, later, (ConstRef.member.inj same).1⟩ + simp only [axiomEnvironment, Model.Environment.insert, if_neg different] + exact ih (fun other listed => canonical other (List.mem_cons_of_mem _ listed)) distinct.2 + later + +/-- Definitions in dependency order under the canonical map. The reference of +every definition is forced to its standalone coordinate; the freshness of that +coordinate and its resolution are derived, not assumed. -/ +inductive ResolvedDefinitionPlan (env : Ixon.Env) (cfg : CheckCfg) (work : Array AnonWorkItem) : + Model.Environment Address → List (DefinitionSpec Address) → Model.Environment Address → Prop + | nil (entries) : ResolvedDefinitionPlan env cfg work entries [] entries + | cons {before after : Model.Environment Address} {rest : List (DefinitionSpec Address)} + (spec : DefinitionSpec Address) (canonical : spec.Canonical) + (position : WorkPosition work (.standalone spec.input.id.addr)) + (run : AtomicDefinitionRun env.resolve before spec.input + (position.state env cfg).checker spec.body spec.type) + (tail : ResolvedDefinitionPlan env cfg work (before.insert spec.ref spec.entry) rest after) : + ResolvedDefinitionPlan env cfg work before (spec :: rest) after + +/-- A canonical plan over distinct addresses is an atomic plan for the +canonical map: resolution follows from enumeration of the work position, and +freshness from the distinctness of the pending addresses. -/ +theorem ResolvedDefinitionPlan.atomic {env : Ixon.Env} {cfg : CheckCfg} {work : Array AnonWorkItem} + {before after : Model.Environment Address} {definitions : List (DefinitionSpec Address)} + (plan : ResolvedDefinitionPlan env cfg work before definitions after) + (materializes : SourceMaterializes env) (enumerated : buildAnonWork env = .ok work) + (bound : CanonicalOutside before (definitions.map (·.input.id.addr))) + (distinct : (definitions.map (·.input.id.addr)).Nodup) : + AtomicDefinitionPlan env cfg work env.resolve before definitions after := by + induction plan with + | nil entries => exact .nil entries + | @cons before after rest spec canonical position run tail ih => + simp only [List.map_cons, List.nodup_cons] at distinct + have resolved : env.resolve spec.input.id.addr = some spec.ref := by + rw [canonical] + exact resolve_of_work_standalone materializes enumerated position.mem + have fresh : before spec.ref = none := by + cases present : before spec.ref with + | none => rfl + | some entry => + obtain ⟨addr, same, absent⟩ := bound spec.ref entry present + rw [canonical] at same + obtain ⟨same, _⟩ := ConstRef.member.inj same + subst same + exact (absent (List.mem_map.mpr ⟨spec, List.mem_cons_self .., rfl⟩)).elim + refine .cons spec resolved fresh position run (ih ?_ distinct.2) + intro ref entry present + by_cases same : ref = spec.ref + · subst same + exact ⟨spec.input.id.addr, canonical, distinct.1⟩ + · have old : before ref = some entry := by + simpa only [Model.Environment.insert, if_neg same] using present + obtain ⟨addr, refEq, absent⟩ := bound ref entry old + exact ⟨addr, refEq, fun listed => absent (List.mem_cons_of_mem _ listed)⟩ + +/-- Syntactic provenance of an axiom at a work position, with its reference +forced to the canonical coordinate. -/ +structure ResolvedAxiomObservation (env : Ixon.Env) (cfg : CheckCfg) (work : Array AnonWorkItem) + (spec : AxiomSpec Address) where + position : WorkPosition work (.standalone spec.id.addr) + path : StandalonePrefix spec.id (position.state env cfg).checker spec.constant + reads : readExpr? env.resolve spec.sourceType = some spec.type.erase + +/-- Resolution and installation of a canonical axiom are derived. -/ +def ResolvedAxiomObservation.atomic {env : Ixon.Env} {cfg : CheckCfg} {work : Array AnonWorkItem} + {spec : AxiomSpec Address} (observation : ResolvedAxiomObservation env cfg work spec) + (materializes : SourceMaterializes env) (enumerated : buildAnonWork env = .ok work) + (canonical : spec.Canonical) {entries : Model.Environment Address} + (installed : entries spec.ref = some spec.entry) : + AxiomObservation env cfg work env.resolve entries spec := + { position := observation.position, path := observation.path, + resolved := by + rw [canonical] + exact resolve_of_work_standalone materializes enumerated observation.position.mem + installed, reads := observation.reads } + +/-- The production environment fragment with every reference forced to its +canonical coordinate. Compared with `AtomicEnvironmentFragment`, the reference +map is `env.resolve`, and the resolution, freshness, and installation premises +are replaced by the finite source contract and distinctness of the listed +addresses. -/ +structure ResolvedEnvironmentFragment (env : Ixon.Env) (cfg : CheckCfg) where + work : Array AnonWorkItem + enumerated : buildAnonWork env = .ok work + materializes : SourceMaterializes env + axioms : List (AxiomSpec Address) + definitions : List (DefinitionSpec Address) + entries : Model.Environment Address + canonical : ∀ spec ∈ axioms, spec.Canonical + distinct : (axioms.map (·.id.addr) ++ definitions.map (·.input.id.addr)).Nodup + axiomRuns : ∀ spec ∈ axioms, Nonempty (ResolvedAxiomObservation env cfg work spec) + plan : ResolvedDefinitionPlan env cfg work (axiomEnvironment axioms) definitions entries + sourceCovered : ∀ addr ∈ env.consts.keys, .standalone addr ∈ work + workCovered : ∀ item ∈ work, + (∃ spec ∈ axioms, item = .standalone spec.id.addr) ∨ + (∃ spec ∈ definitions, item = .standalone spec.input.id.addr) + +/-- The canonical fragment is an atomic fragment for `env.resolve`. -/ +def ResolvedEnvironmentFragment.atomic {env : Ixon.Env} {cfg : CheckCfg} + (fragment : ResolvedEnvironmentFragment env cfg) : + AtomicEnvironmentFragment env cfg env.resolve where + work := fragment.work + enumerated := fragment.enumerated + axioms := fragment.axioms + definitions := fragment.definitions + entries := fragment.entries + axiomRuns spec listed := + let ⟨observation⟩ := fragment.axiomRuns spec listed + ⟨observation.atomic fragment.materializes fragment.enumerated (fragment.canonical spec listed) + (axiomEnvironment_installed fragment.canonical + (List.nodup_append.mp fragment.distinct).1 listed)⟩ + plan := fragment.plan.atomic fragment.materializes fragment.enumerated + (by + intro ref entry present + obtain ⟨spec, listed, same⟩ := axiomEnvironment_canonical fragment.canonical present + refine ⟨spec.id.addr, same, fun pending => ?_⟩ + exact (List.nodup_append.mp fragment.distinct).2.2 _ (List.mem_map.mpr ⟨spec, listed, rfl⟩) + _ pending rfl) + (List.nodup_append.mp fragment.distinct).2.1 + sourceCovered := fragment.sourceCovered + workCovered := fragment.workCovered + +/-- A successful `checkEnvAnon` run in the canonical fragment extends every +model of its axiom set while preserving all axiom interpretations. -/ +theorem checkEnvAnon_preserves_model_resolved {env : Ixon.Env} {cfg : CheckCfg} + (fragment : ResolvedEnvironmentFragment env cfg) + (wellFormed : (axiomEnvironment fragment.axioms).WF) + {results : Array CheckResult} (accepted : checkEnvAnon env cfg = .ok results) + (succeeded : ∀ result ∈ results, result.err? = none) : + fragment.entries.WF ∧ + PreservesModels.{0,v} (axiomEnvironment fragment.axioms) fragment.entries := + checkEnvAnon_atomic_preserves_model fragment.atomic wellFormed accepted succeeded + +/-- Every source address receives the interface entry at its canonical +standalone coordinate, reading the declaration reached by production lookup. -/ +theorem checkEnvAnon_represents_source_resolved {env : Ixon.Env} {cfg : CheckCfg} + (fragment : ResolvedEnvironmentFragment env cfg) + (wellFormed : (axiomEnvironment fragment.axioms).WF) + {results : Array CheckResult} (accepted : checkEnvAnon env cfg = .ok results) + (succeeded : ∀ result ∈ results, result.err? = none) : + ∀ addr ∈ env.consts.keys, ∃ entry, fragment.entries (.member addr 0) = some entry ∧ + ∃ concrete : KConst .anon, + (∃ before after, TcM.checkConst (⟨addr, ()⟩ : KId .anon) before = .ok () after ∧ + Nonempty (StandalonePrefix (⟨addr, ()⟩ : KId .anon) before concrete)) ∧ + DeclarationReading env.resolve concrete entry := by + intro addr present + obtain ⟨ref, entry, resolved, installed, rest⟩ := + checkEnvAnon_atomic_represents_source fragment.atomic wellFormed accepted succeeded addr present + have canonical := resolve_of_work_standalone fragment.materializes fragment.enumerated + (fragment.sourceCovered addr present) + cases Option.some.inj (resolved.symm.trans canonical) + exact ⟨entry, installed, rest⟩ + +/-- No declaration can inhabit an axiom type interpreted as empty. -/ +theorem checkEnvAnon_no_false_resolved {env : Ixon.Env} {cfg : CheckCfg} + (fragment : ResolvedEnvironmentFragment env cfg) + (wellFormed : (axiomEnvironment fragment.axioms).WF) + {results : Array CheckResult} (accepted : checkEnvAnon env cfg = .ok results) + (succeeded : ∀ result ∈ results, result.err? = none) + {V : Type v} [SetTheory V] (axiomValues : Assignment Address V) + (axiomModel : Realizes axiomValues (axiomEnvironment fragment.axioms)) + {falseAddr : Address} {falseEntry : ConstantEntry Address} + (hasFalse : axiomEnvironment fragment.axioms (.member falseAddr 0) = some falseEntry) + (falseEmpty : axiomValues (.member falseAddr 0) [] = SetTheory.empty) + {ref : ConstRef Address} {entry : ConstantEntry Address} + (present : fragment.entries ref = some entry) + (isFalse : entry.type.erase = .const (.member falseAddr 0) []) : False := + checkEnvAnon_atomic_no_false fragment.atomic wellFormed accepted succeeded axiomValues + axiomModel hasFalse falseEmpty present isFalse + +end Ix.Kernel.Consistency diff --git a/Tests/Ix/Kernel/Resolution.lean b/Tests/Ix/Kernel/Resolution.lean new file mode 100644 index 000000000..31a46f241 --- /dev/null +++ b/Tests/Ix/Kernel/Resolution.lean @@ -0,0 +1,54 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import LSpec +import Ix.Kernel.Verify.Consistency.Resolution +import Tests.Ix.Kernel.IxonFixtures + +/-! +Regressions for the canonical reference map `Ixon.Env.resolve` on hand-built +fixture environments. This file is not a `module` because the proof library +it exercises is not one. +-/ + +namespace Tests.Kernel.Resolution + +open LSpec Ix.Kernel Tests.Kernel.Fixtures + +/-- Standalones are one-member blocks at their own address, projections resolve +by block position and member kind, and Muts blocks, absent projections, and +mismatched records resolve to nothing. -/ +private def cases : TestSeq := + let (standaloneEnv, axiomAddr, definitionAddr) := envIdA + let (mutualEnv, mutualBlock) := envMutualDefs + let (inductiveEnv, inductiveBlock) := envInductive + let (_, carrier) := envA + let (mismatched, wrongKind) := + storeConst mutualEnv ⟨.rPrj ⟨0, mutualBlock⟩, #[], #[], #[]⟩ + test "resolution: standalone keys resolve to their own one-member block" + (standaloneEnv.resolve axiomAddr == some (.member axiomAddr 0) && + standaloneEnv.resolve definitionAddr == some (.member definitionAddr 0) && + standaloneEnv.consts.keys.all fun addr => + standaloneEnv.resolve addr == some (.member addr 0)) + ++ test "resolution: mutual definitions resolve to their block positions" + (mutualEnv.resolve (defnProjAddr mutualBlock 0) == some (.member mutualBlock 0) && + mutualEnv.resolve (defnProjAddr mutualBlock 1) == some (.member mutualBlock 1) && + mutualEnv.resolve carrier == some (.member carrier 0)) + ++ test "resolution: a Muts block, an absent projection, and a mismatched record resolve to nothing" + (mutualEnv.resolve mutualBlock == none && + mutualEnv.resolve (defnProjAddr mutualBlock 2) == none && + mismatched.resolve wrongKind == none) + ++ test "resolution: inductive and constructor projections resolve to their coordinates" + (inductiveEnv.resolve (indcProjAddr inductiveBlock 0) == some (.member inductiveBlock 0) && + inductiveEnv.resolve (ctorProjAddr inductiveBlock 0 0) == some (.ctor inductiveBlock 0 0) && + inductiveEnv.resolve (ctorProjAddr inductiveBlock 0 1) == none) + ++ test "resolution: the finite materialization check accepts the fixtures" + (Ix.Kernel.Consistency.sourceMaterializesCheck standaloneEnv && + Ix.Kernel.Consistency.sourceMaterializesCheck mutualEnv && + Ix.Kernel.Consistency.sourceMaterializesCheck inductiveEnv) + +def suite : List TestSeq := [cases] + +end Tests.Kernel.Resolution diff --git a/Tests/Main.lean b/Tests/Main.lean index cabfd233c..bad78914c 100644 --- a/Tests/Main.lean +++ b/Tests/Main.lean @@ -37,6 +37,7 @@ import Tests.Ix.BenchMeasures import Tests.Ix.Kernel.Unit import Tests.Ix.Kernel.Substrate import Tests.Ix.Kernel.IxonFixtures +import Tests.Ix.Kernel.Resolution import Tests.Ix.Kernel.WhnfTests import Tests.Ix.Kernel.InferDefEq import Tests.Ix.Kernel.CheckTests @@ -111,7 +112,7 @@ def primarySuites : Std.HashMap String (List LSpec.TestSeq) := .ofList [ ("tc-unit", Tests.Kernel.Unit.suite ++ Tests.Kernel.Substrate.suite ++ Tests.Kernel.Fixtures.suite ++ Tests.Kernel.WhnfTests.suite ++ Tests.Kernel.InferDefEq.suite ++ Tests.Kernel.CheckTests.suite - ++ Tests.Kernel.Consistency.suite + ++ Tests.Kernel.Consistency.suite ++ Tests.Kernel.Resolution.suite ++ Tests.Kernel.DefinitionDependencies.suite ++ Tests.Kernel.SafeRecursion.suite ++ Tests.Kernel.Roundtrip.unitTests ++ Tests.Kernel.IngressMeta.unitTests), From 31ca582dd4aead5cf8d2089e3c083bc53e3797cc Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 15 Sep 2026 12:36:41 -0400 Subject: [PATCH 59/63] Prove the non-reducing DefEq tiers under the contracts Strengthen the DefEq memo semantics of the checker invariant: a positive cache entry or union-find edge is a recorded conversion (AddressConversion) between annotated typed readings at the registration and context digest where it was proved, and the equivalence manager is EquivManager.WF at the chain closure of such edges (EqKeyChain), so the verified union-find facts apply. Fix the replayed lint warnings of Verify/EquivalenceManager. DefEqMemo: exact run equations of the entry's primitives, invariant preservation through path halving, cache insertion, and union, and the recording of a proved conversion at the key's origin. DefEqQuick: the quick structural tier (sorts by univEq_sound, matching binders through the actual common-local opening with typing transported across the domain conversion), the constant-instance level congruence, and the argument-spine loop. DefEqTiers: the charged recursive tail, the guarded representative probe (justified only under EqKey.rootCacheScopeMatches, composing the two representative chains with the root edge), the entry with its hash path, manager query, and both cache partitions, and isDefEq_direct_tiers, the StepContracts.isDefEq field modulo DefEqSeamAssumptions. Remaining seam hypotheses: the tail isDefEqInnerAfterQuick under its contract; DefEqMemoTransport, reading a certified chain at the caller's registration; agreement of the binder annotations of same-raw readings on the hash path and of the root annotations of an accepted binder pair; hereditary typing of binder domains and bodies; the synthesis origin of the pushed context. DefEqTierResources collects the finite collision and walker data over a support of compared operands. --- Ix/Kernel/Verify/Consistency.lean | 10 + Ix/Kernel/Verify/Consistency/Audit.lean | 76 +- Ix/Kernel/Verify/Consistency/DefEqMemo.lean | 395 +++++++ Ix/Kernel/Verify/Consistency/DefEqQuick.lean | 792 ++++++++++++++ Ix/Kernel/Verify/Consistency/DefEqTiers.lean | 1000 ++++++++++++++++++ Ix/Kernel/Verify/Consistency/Invariant.lean | 78 +- Ix/Kernel/Verify/EquivalenceManager.lean | 26 +- 7 files changed, 2335 insertions(+), 42 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/DefEqMemo.lean create mode 100644 Ix/Kernel/Verify/Consistency/DefEqQuick.lean create mode 100644 Ix/Kernel/Verify/Consistency/DefEqTiers.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 89df95ab6..e35eac712 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -87,6 +87,9 @@ import Ix.Kernel.Verify.Consistency.Environment import Ix.Kernel.Verify.Consistency.RunAssumptions import Ix.Kernel.Verify.Consistency.Invariant import Ix.Kernel.Verify.Consistency.Contracts +import Ix.Kernel.Verify.Consistency.DefEqMemo +import Ix.Kernel.Verify.Consistency.DefEqQuick +import Ix.Kernel.Verify.Consistency.DefEqTiers import Ix.Kernel.Verify.Consistency.Audit /-! @@ -366,5 +369,12 @@ projections at their block coordinates, agrees with the certified adapter, is injective on standalone coordinates, enumerates exactly the driver's standalone items under a finite materialization contract, derives both static bindings, and restates the environment theorems with every reference at its canonical coordinate. +The non-reducing conversion tiers are proved under those contracts: positive +equivalence-manager and DefEq-cache answers certify chains of recorded +conversions, every memo update preserves the invariant, and the quick +structural tier composes universe equality and the common-local binder +comparison; the entry is assembled modulo the reducing tiers, the transport +of recorded chains to the caller's registration, the binder annotation +discipline, and hereditary typing of binder operands. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index e8fd352a9..c6fcb0e96 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -21,6 +21,7 @@ import Ix.Kernel.Verify.Consistency.BetaExposureConstruction import Ix.Kernel.Verify.Consistency.BetaHistoryInference import Ix.Kernel.Verify.Consistency.Invariant import Ix.Kernel.Verify.Consistency.Contracts +import Ix.Kernel.Verify.Consistency.DefEqTiers import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -1458,12 +1459,8 @@ private def wp1RunRoots : Array Lean.Name := #[ ``RunAssumptions.universeCollisionFree, ``RunAssumptions.universeAddrFaithful, ``RunAssumptions.supportCollisionFree, ``RunAssumptions.conversionData, ``RunAssumptions.bound, ``WhnfCacheSemantics, ``WhnfCacheSemantics.ofMaps, ``WhnfCacheSemantics.ofEmpty, - ``DefEqCacheSemantics, ``DefEqCacheSemantics.ofMaps, ``DefEqCacheSemantics.ofEmpty, - ``EqKeyConversion, ``EquivManagerSemantics, ``EquivManagerSemantics.empty, ``UnfoldCacheSemantics.ofEmpty, ``IsPropCacheSemantics, ``IsPropCacheSemantics.ofMap, - ``IsPropCacheSemantics.ofEmpty, ``ReductionCacheSemantics, ``ReductionCacheSemantics.ofMapsManager, - ``ReductionCacheSemantics.ofMaps, ``ReductionCacheSemantics.ofEmpty, ``ReductionCacheSemantics.clear, - ``reset_eq + ``IsPropCacheSemantics.ofEmpty, ``reset_eq ] private def wp1PropextRoots : Array Lean.Name := #[ @@ -1596,6 +1593,62 @@ private def resolutionProductionRoots : Array Lean.Name := #[ ``checkEnvAnon_represents_source_resolved, ``checkEnvAnon_no_false_resolved ] +/-- The DefEq memo semantics of the invariant and their transports: recorded +conversions, justified edges and their chains, the manager representation, +both cache partitions, and every memo update of the direct tiers. These reach +expression construction through the recorded context-digest run. -/ +private def wp3MemoRoots : Array Lean.Name := #[ + ``AddressConversion, ``EqKeyConversion, ``EqKeyChain, ``EqKeyChain.equivalence, + ``EquivManagerSemantics, ``EquivManagerSemantics.empty, ``DefEqCacheSemantics, + ``DefEqCacheSemantics.ofMaps, ``DefEqCacheSemantics.ofEmpty, ``ReductionCacheSemantics, + ``ReductionCacheSemantics.ofMapsManager, ``ReductionCacheSemantics.ofMaps, + ``ReductionCacheSemantics.ofEmpty, ``ReductionCacheSemantics.clear, + ``AddressConversion.symm, ``AddressConversion.canonical, ``EquivManagerSemantics.isEquiv, + ``EquivManagerSemantics.findRootKeys, ``EquivManagerSemantics.addEquiv, + ``DefEqCacheSemantics.setManager, ``DefEqCacheSemantics.ofInsert, ``DefEqCacheSemantics.hitEdge, + ``DefEqCacheSemantics.rootEdge, ``ctxAddrForLbr_ok, ``defEqCtxKey_def, + ``QuickBinderData, ``QuickBinderData.left, ``openBinder_support, ``quickDefEq_eq, + ``scopedResult, ``withLctxScope_run +] + +/-- The direct conversion tiers under the contracts: invariant preservation +through the entry's bookkeeping, the recorded-conversion origin and transport +premise, the binder premises and resources, the quick structural tier, the +argument-spine loop, the charged tail, the representative probe, and the +assembled entry modulo the seam. -/ +private def wp3DirectTierRoots : Array Lean.Name := #[ + ``CheckerInvariant.ofDefEqMemos, ``CheckerInvariant.ofDefEqUpdate, ``CheckerInvariant.ofDefEqScalars, + ``CheckerInvariant.addEquiv, ``CheckerInvariant.copyToCheap, ``CheckerInvariant.promoteCheapHit, + ``CheckerInvariant.afterBumpStats, ``CheckerInvariant.tickOutcome, ``CheckerInvariant.internStep, + ``CheckerInvariant.runInternStep, ``DefEqKeyOrigin, ``DefEqKeyOrigin.ofRun, ``DefEqMemoTransport, + ``AddressConversion.ofClaim, ``EqKeyConversion.ofClaim, ``DefEqBinderAssumptions, + ``DefEqTierResources, ``QuickBinderPost, ``quickBinder_sound, ``quickDefEq_sound, + ``SpineConversionPost, ``allDefEqSpineArgsList_sound, ``allDefEqSpineArgs_sound, + ``isDefEqAfterRootCacheMiss_sound, ``isDefEqAfterDirectCacheMiss_sound, ``DefEqSeamAssumptions, + ``isDefEqInner_sound, ``isDefEq_sound, ``isDefEq_direct_tiers, ``StepContracts.isDefEq_of_direct_tiers +] + +/-- Model-level congruences, readings, and monadic run equations of the direct +tiers that reach no production construction. -/ +private def wp3PureRoots : Array Lean.Name := #[ + ``SpineReadings, ``ConversionClaim.constLevels, ``sameDefEqUniverses_sound, + ``ConversionClaim.constInstances, ``EqKey.rootCacheScopeMatches_eq, ``TypingClaim.pushConverted, + ``contextValid_pushConverted +] + +/-- Run equations of the entry's primitive operations and of the monad. -/ +private def wp3PropextRoots : Array Lean.Name := #[ + ``stepTrace_eq, ``bumpStats_eq, ``tick_eq, ``withEquiv_eq, ``canonicalPair_cases, + ``intern_eq, ``runIntern_eq, ``isDefEqCall_run, ``allDefEqSpineArgsList_cons, ``modify_run, + ``get_run, ``pure_run, ``throw_run, ``tryExcept_run +] + +/-- Outcome-directed monadic reasoning and shape inversions of readings. -/ +private def wp3AxiomFreeRoots : Array Lean.Name := #[ + ``EStateM.bind_cases, ``readScopedExpr?_sort_annotated, ``readScopedExpr?_lam_annotated, + ``readScopedExpr?_all_annotated +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -1844,6 +1897,19 @@ def roots : Array RootAllowance := #[ ++ resolutionProductionRoots.map (fun root => { root, standardAxioms := standard, nativeAxioms := productionNative, forbiddenDependencies := forbiddenProduction }) + ++ wp3MemoRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative], + forbiddenDependencies := forbiddenProduction }) + ++ wp3DirectTierRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], + forbiddenDependencies := forbiddenProduction }) + ++ wp3PureRoots.map (fun root => { + root, standardAxioms := standard, forbiddenDependencies := forbiddenProduction }) + ++ wp3PropextRoots.map (fun root => { + root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction }) + ++ wp3AxiomFreeRoots.map (fun root => { root, forbiddenDependencies := forbiddenProduction }) + ++ #[{ root := ``readScopedExpr?_const_annotated, standardAxioms := #[``propext], + forbiddenDependencies := forbiddenProduction }] run_cmd Kernel.Verify.Audit.check roots diff --git a/Ix/Kernel/Verify/Consistency/DefEqMemo.lean b/Ix/Kernel/Verify/Consistency/DefEqMemo.lean new file mode 100644 index 000000000..eb28c4107 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/DefEqMemo.lean @@ -0,0 +1,395 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Contracts +import Batteries.Data.UInt + +/-! +# DefEq memo semantics under the checker invariant + +The conversion entry consults three memos before any reduction: the +equivalence manager, the full DefEq cache, and the cheap DefEq cache. This +module states what each positive answer certifies in the invariant's terms, a +chain of justified union-find edges between the two operand keys, and proves +that every memo update production performs preserves the invariant: path +halving during a query, insertion of a proved answer into either cache, and +union of the two keys after a positive answer. + +A chain is recorded existentially at the registration where it was proved. +Reading it back at the caller's registration is the conversion instance of +the context-digest boundary and is stated once as `DefEqMemoTransport`; the +tier proofs consume that premise and supply nothing weaker. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-! ### Exact run equations of the entry's non-recursive operations -/ + +theorem stepTrace_eq (tag : String) (payload : Unit → String) (state : TcState .anon) : + TcM.stepTrace tag payload state = .ok () state := by + unfold TcM.stepTrace + change EStateM.bind (get : TcM .anon (TcState .anon)) _ state = _ + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) state = .ok state state from rfl] + dsimp only + split <;> rfl + +theorem bumpStats_eq (update : TcState .anon → TcState .anon) (state : TcState .anon) : + TcM.bumpStats update state = .ok () (if state.stats then update state else state) := by + unfold TcM.bumpStats + change EStateM.bind (get : TcM .anon (TcState .anon)) _ state = _ + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) state = .ok state state from rfl] + dsimp only + by_cases enabled : state.stats = true + · rw [if_pos enabled, if_pos enabled]; rfl + · rw [if_neg enabled, if_neg enabled]; rfl + +theorem tick_eq (state : TcState .anon) : + TcM.tick state = if state.recFuel == 0 then .error .maxRecFuel state + else .ok () {state with recFuel := state.recFuel - 1} := by + unfold TcM.tick + change EStateM.bind (get : TcM .anon (TcState .anon)) _ state = _ + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) state = .ok state state from rfl] + dsimp only + split <;> rfl + +/-- The manager operation is state-pure outside the union-find field. -/ +theorem withEquiv_eq {α : Type} (update : EquivManager → α × EquivManager) (state : TcState .anon) : + TcM.withEquiv update state = .ok (update state.equivManager).1 + {state with equivManager := (update state.equivManager).2} := by + unfold TcM.withEquiv + rcases result : update state.equivManager with ⟨value, manager⟩ + change EStateM.bind (fun current : TcState .anon => + .ok current.equivManager {current with equivManager := {}}) _ state = _ + unfold EStateM.bind + simp only + rw [result] + rfl + +/-- Context-digest computation never fails. -/ +theorem ctxAddrForLbr_ok (lbr : UInt64) (state : TcState .anon) : + ∃ addr after, TcM.ctxAddrForLbr lbr state = .ok addr after := by + unfold TcM.ctxAddrForLbr + change ∃ addr after, EStateM.bind (get : TcM .anon (TcState .anon)) _ state = .ok addr after + rw [EStateM.bind, show (get : TcM .anon (TcState .anon)) state = .ok state state from rfl] + dsimp only + by_cases fast : (lbr == 0 || state.ctx.isEmpty) = true + · rw [if_pos fast] + exact ⟨_, _, rfl⟩ + · rw [if_neg fast] + cases state.ctxAddrCache[(state.ctxId, lbr)]? <;> exact ⟨_, _, rfl⟩ + +theorem defEqCtxKey_def (left right : KExpr .anon) : + TcM.defEqCtxKey left right = TcM.ctxAddrForLbr (max left.lbr right.lbr) := rfl + +theorem canonicalPair_cases (left right : Address) : + canonicalPair left right = (left, right) ∨ canonicalPair left right = (right, left) := by + unfold canonicalPair + split + · exact .inl rfl + · exact .inr rfl + +private theorem max_comm_uint64 (left right : UInt64) : max left right = max right left := + UInt64.toNat_inj.mp (by simp only [UInt64.toNat_max, Nat.max_comm]) + +/-- The runtime guard of a root-derived cache probe, decoded. -/ +theorem EqKey.rootCacheScopeMatches_eq {left right : EqKey} {ctxAddr : Address} {lbr : UInt64} + (guard : left.rootCacheScopeMatches right ctxAddr lbr = true) : + left.ctxAddr = ctxAddr ∧ right.ctxAddr = ctxAddr ∧ left.lbr = lbr ∧ right.lbr = lbr := by + simp only [EqKey.rootCacheScopeMatches, Bool.and_eq_true, beq_iff_eq] at guard + exact ⟨guard.1.1.1.1, guard.1.1.1.2, guard.1.1.2, guard.1.2⟩ + +/-! ### Invariant preservation through memo updates -/ + +namespace CheckerInvariant + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +/-- Operations that retain every map except the DefEq memos and the +equivalence manager preserve the invariant once the updated memos are proved +sound. -/ +theorem ofDefEqMemos {before after : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (frame : LocalStateFrame before after) + (constants : after.env.consts = before.env.consts) + (blocks : after.env.blocks = before.env.blocks) + (coherent : after.env.intern.WF) + (full : after.env.inferCache = before.env.inferCache) + (only : after.env.inferOnlyCache = before.env.inferOnlyCache) + (whnf : ∀ partition : WhnfCachePartition, partition.cache after = partition.cache before) + (defEq : DefEqCacheSemantics.{u,v} resolve entries after) + (manager : EquivManagerSemantics.{u,v} resolve entries after.equivManager) + (unfold : after.env.unfoldCache = before.env.unfoldCache) + (isProp : after.env.isPropCache = before.env.isPropCache) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after where + sourceCache := ⟨valid.sourceState.ofMaps frame.loader constants blocks coherent, + valid.cache.ofMaps full only constants⟩ + synthesis := valid.synthesis.elim fun history => ⟨history.ofMaps full only⟩ + whnf := valid.whnf.elim fun history => ⟨history.ofMaps whnf⟩ + structural := frame.invariant valid.structural + reading := valid.reading.congr frame.context.symm + origin := valid.origin + semantics := ⟨valid.semantics.whnf.ofMaps whnf, defEq, manager, + valid.semantics.unfold.ofMap unfold, valid.semantics.isProp.ofMap isProp⟩ + +/-- A state that differs from a valid one only in the two DefEq memos, the +equivalence manager, and fields outside the environment and local context +satisfies the invariant once the memos and manager are proved sound. -/ +theorem ofDefEqUpdate {before after : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (env : ∃ full cheap, after.env = + {before.env with defEqCache := full, defEqCheapCache := cheap}) + (lctx : after.lctx = before.lctx) + (loader : after.lazyFault = before.lazyFault) + (defEq : DefEqCacheSemantics.{u,v} resolve entries after) + (manager : EquivManagerSemantics.{u,v} resolve entries after.equivManager) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after := by + obtain ⟨full, cheap, env⟩ := env + exact valid.ofDefEqMemos ⟨by rw [env]; exact Nat.le_refl _, by rw [lctx]; exact .refl _, loader⟩ + (by rw [env]) (by rw [env]) (by rw [env]; exact valid.coherent) (by rw [env]) (by rw [env]) + (fun partition => by cases partition <;> simp only [WhnfCachePartition.cache, env]) + defEq manager (by rw [env]) (by rw [env]) + +/-- A state that differs only outside the environment maps, the manager, and +the local context; both memos keep their semantics. -/ +theorem ofDefEqScalars {before after : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (env : after.env = before.env) (lctx : after.lctx = before.lctx) + (loader : after.lazyFault = before.lazyFault) + (manager : after.equivManager = before.equivManager) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after := + valid.ofDefEqUpdate ⟨before.env.defEqCache, before.env.defEqCheapCache, by rw [env]⟩ lctx loader + (valid.semantics.defEq.ofMaps fun partition => by + cases partition <;> simp only [DefEqCachePartition.cache, env]) + (by rw [manager]; exact valid.semantics.equivalence) + +end CheckerInvariant + +/-! ### What the memos certify -/ + +section Semantics + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + +theorem AddressConversion.symm {left right ctxAddr : Address} + (recorded : AddressConversion.{u,v} resolve entries left right ctxAddr) : + AddressConversion.{u,v} resolve entries right left ctxAddr := by + obtain ⟨state, locals, context, a, b, ta, tb, aAddr, bAddr, structural, reading, digest, aReads, + bReads, aTyped, bTyped, claim⟩ := recorded + exact ⟨state, locals, context, b, a, tb, ta, bAddr, aAddr, structural, reading, + by rw [max_comm_uint64]; exact digest, bReads, aReads, bTyped, aTyped, claim.symm⟩ + +/-- A positive equivalence query certifies a chain between the two keys, and +its path halving keeps the manager valid. -/ +theorem EquivManagerSemantics.isEquiv {manager : EquivManager} + (valid : EquivManagerSemantics.{u,v} resolve entries manager) (left right : EqKey) : + EquivManagerSemantics.{u,v} resolve entries (manager.isEquiv left right).2 ∧ + ((manager.isEquiv left right).1 = true → EqKeyChain.{u,v} resolve entries left right) := + EquivManager.WF.isEquiv (EqKeyChain.equivalence resolve entries) valid left right + +/-- Both representative lookups keep the manager valid and relate each +returned representative to its query by a chain. -/ +theorem EquivManagerSemantics.findRootKeys {manager : EquivManager} + (valid : EquivManagerSemantics.{u,v} resolve entries manager) (left right : EqKey) : + let result := + let (leftRoot, manager) := manager.findRootKey left + let (rightRoot, manager) := manager.findRootKey right + ((leftRoot, rightRoot), manager) + EquivManagerSemantics.{u,v} resolve entries result.2 ∧ + (∀ root, result.1.1 = some root → EqKeyChain.{u,v} resolve entries left root) ∧ + (∀ root, result.1.2 = some root → EqKeyChain.{u,v} resolve entries right root) := + EquivManager.WF.findRootKeys (EqKeyChain.equivalence resolve entries) valid left right + +/-- Recording a certified chain keeps the manager valid. -/ +theorem EquivManagerSemantics.addEquiv {manager : EquivManager} + (valid : EquivManagerSemantics.{u,v} resolve entries manager) {left right : EqKey} + (chain : EqKeyChain.{u,v} resolve entries left right) : + EquivManagerSemantics.{u,v} resolve entries (manager.addEquiv left right) := + EquivManager.WF.addEquiv (EqKeyChain.equivalence resolve entries) valid chain + +/-- Replacing the manager leaves both cache partitions unchanged. -/ +theorem DefEqCacheSemantics.setManager {state : TcState .anon} + (valid : DefEqCacheSemantics.{u,v} resolve entries state) (manager : EquivManager) : + DefEqCacheSemantics.{u,v} resolve entries {state with equivManager := manager} := + valid.ofMaps fun partition => by cases partition <;> rfl + +/-- Inserting an answer at one key into either partition preserves the cache +semantics once a positive answer is recorded at that key. -/ +theorem DefEqCacheSemantics.ofInsert {before after : TcState .anon} + (valid : DefEqCacheSemantics.{u,v} resolve entries before) + (key : Address × Address × Address) (value : Bool) + (full : after.env.defEqCache = before.env.defEqCache ∨ + after.env.defEqCache = before.env.defEqCache.insert key value) + (cheap : after.env.defEqCheapCache = before.env.defEqCheapCache ∨ + after.env.defEqCheapCache = before.env.defEqCheapCache.insert key value) + (justified : value = true → AddressConversion.{u,v} resolve entries key.1 key.2.1 key.2.2) : + DefEqCacheSemantics.{u,v} resolve entries after := by + intro partition stored hit + have inserted : ∀ (map : Std.HashMap (Address × Address × Address) Bool), + (∀ stored : Address × Address × Address, map[stored]? = some true → + AddressConversion.{u,v} resolve entries stored.1 stored.2.1 stored.2.2) → + (map.insert key value)[stored]? = some true → + AddressConversion.{u,v} resolve entries stored.1 stored.2.1 stored.2.2 := by + intro map old found + rw [Std.HashMap.getElem?_insert] at found + split at found + · next same => + rw [← eq_of_beq same] + exact justified (Option.some.inj found) + · exact old stored found + cases partition with + | full => + rcases full with same | written + · exact valid .full stored (by simpa only [DefEqCachePartition.cache, same] using hit) + · exact inserted _ (valid .full) (by simpa only [DefEqCachePartition.cache, written] using hit) + | cheap => + rcases cheap with same | written + · exact valid .cheap stored (by simpa only [DefEqCachePartition.cache, same] using hit) + · exact inserted _ (valid .cheap) (by simpa only [DefEqCachePartition.cache, written] using hit) +/-- A positive direct-cache hit at the canonical key of a pair is a justified +edge between the pair's keys. -/ +theorem DefEqCacheSemantics.hitEdge {state : TcState .anon} + (valid : DefEqCacheSemantics.{u,v} resolve entries state) (partition : DefEqCachePartition) + {left right : KExpr .anon} {lo hi ctxAddr : Address} + (canonical : canonicalPair left.addr right.addr = (lo, hi)) + (hit : (partition.cache state)[(lo, hi, ctxAddr)]? = some true) : + EqKeyConversion.{u,v} resolve entries ⟨left.addr, ctxAddr, max left.lbr right.lbr, left.lbr⟩ + ⟨right.addr, ctxAddr, max left.lbr right.lbr, right.lbr⟩ := by + have recorded := valid partition _ hit + refine ⟨rfl, rfl, ?_⟩ + rcases canonicalPair_cases left.addr right.addr with same | swapped + · rw [canonical] at same + obtain ⟨rfl, rfl⟩ := Prod.mk.inj same + exact recorded + · rw [canonical] at swapped + obtain ⟨rfl, rfl⟩ := Prod.mk.inj swapped + exact recorded.symm + +/-- A positive cache hit at the canonical key of two representatives that +pass the scope guard is a justified edge between the representatives. -/ +theorem DefEqCacheSemantics.rootEdge {state : TcState .anon} + (valid : DefEqCacheSemantics.{u,v} resolve entries state) (partition : DefEqCachePartition) + {leftRoot rightRoot : EqKey} {lo hi ctxAddr : Address} {lbr : UInt64} + (scope : leftRoot.rootCacheScopeMatches rightRoot ctxAddr lbr = true) + (canonical : canonicalPair leftRoot.exprAddr rightRoot.exprAddr = (lo, hi)) + (hit : (partition.cache state)[(lo, hi, ctxAddr)]? = some true) : + EqKeyConversion.{u,v} resolve entries leftRoot rightRoot := by + obtain ⟨leftCtx, rightCtx, leftLbr, rightLbr⟩ := EqKey.rootCacheScopeMatches_eq scope + have recorded := valid partition _ hit + refine ⟨leftCtx.trans rightCtx.symm, leftLbr.trans rightLbr.symm, ?_⟩ + rw [leftCtx] + rcases canonicalPair_cases leftRoot.exprAddr rightRoot.exprAddr with same | swapped + · rw [canonical] at same + obtain ⟨rfl, rfl⟩ := Prod.mk.inj same + exact recorded + · rw [canonical] at swapped + obtain ⟨rfl, rfl⟩ := Prod.mk.inj swapped + exact recorded.symm + +end Semantics + +/-- Recording a certified chain in the manager preserves the invariant. -/ +theorem CheckerInvariant.addEquiv {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} {state : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) + {left right : EqKey} (chain : EqKeyChain.{u,v} resolve entries left right) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {state with equivManager := state.equivManager.addEquiv left right} := + valid.ofDefEqUpdate ⟨_, _, rfl⟩ rfl rfl (valid.semantics.defEq.setManager _) + (valid.semantics.equivalence.addEquiv chain) + +/-! ### Recording a proved conversion -/ + +section Recording + +variable {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (source : Ixon.Env) + (catalog : List (SourceCacheRequest source)) + +/-- Where a comparison's context key was computed: an invariant state at the +caller's registration whose actual digest at the pair's joint radius is the +key. -/ +def DefEqKeyOrigin (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (left right : KExpr .anon) (ctxAddr : Address) : Prop := + ∃ (state after : TcState .anon), + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state ∧ + TcM.ctxAddrForLbr (max left.lbr right.lbr) state = .ok ctxAddr after + +/-- Transport of a certified chain between the keys of two operands to the +caller's registration. A chain is recorded existentially at the registrations +where its edges were proved; identifying those sources, annotated readings, +and local contexts with the caller's is the conversion instance of the +context-digest boundary and is not derivable from the state maps. The memo +tiers consume exactly this premise. -/ +def DefEqMemoTransport : Prop := + ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (left right : KExpr .anon) (a b : AExpr β) (ctxAddr : Address), + DefEqKeyOrigin.{u,v} resolve anchor entries source catalog locals context bounds + left right ctxAddr → + readScopedExpr? resolve locals left = some a.erase → + (∃ type, TypingClaim.{u,v} entries context a type) → + readScopedExpr? resolve locals right = some b.erase → + (∃ type, TypingClaim.{u,v} entries context b type) → + EqKeyChain.{u,v} resolve entries ⟨left.addr, ctxAddr, max left.lbr right.lbr, left.lbr⟩ + ⟨right.addr, ctxAddr, max left.lbr right.lbr, right.lbr⟩ → + ConversionClaim.{u,v} entries context a b + +end Recording + +section RecordingLemmas + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} + +/-- Key computation from an invariant state establishes its own origin. -/ +theorem DefEqKeyOrigin.ofRun {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} + {left right : KExpr .anon} {ctxAddr : Address} {before after : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (run : TcM.defEqCtxKey left right before = .ok ctxAddr after) : + DefEqKeyOrigin.{u,v} resolve anchor entries source catalog locals context bounds left right ctxAddr := + ⟨before, after, valid, run⟩ + +/-- A conversion proved at the registration where the key was computed is a +recorded conversion of the pair's addresses at that key. -/ +theorem AddressConversion.ofClaim {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {left right : KExpr .anon} {ctxAddr : Address} {a b : AExpr β} + (origin : DefEqKeyOrigin.{u,v} resolve anchor entries source catalog locals context bounds + left right ctxAddr) + (leftReads : readScopedExpr? resolve locals left = some a.erase) + (rightReads : readScopedExpr? resolve locals right = some b.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context a type) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context b type) + (claim : ConversionClaim.{u,v} entries context a b) : + AddressConversion.{u,v} resolve entries left.addr right.addr ctxAddr := by + obtain ⟨state, after, valid, digest⟩ := origin + exact ⟨state, locals, context, left, right, a, b, rfl, rfl, valid.structural, valid.reading, + ⟨after, digest⟩, leftReads, rightReads, leftTyped, rightTyped, claim⟩ + +/-- The same conversion as a justified edge between the pair's keys. -/ +theorem EqKeyConversion.ofClaim {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {left right : KExpr .anon} {ctxAddr : Address} {a b : AExpr β} + (origin : DefEqKeyOrigin.{u,v} resolve anchor entries source catalog locals context bounds + left right ctxAddr) + (leftReads : readScopedExpr? resolve locals left = some a.erase) + (rightReads : readScopedExpr? resolve locals right = some b.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context a type) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context b type) + (claim : ConversionClaim.{u,v} entries context a b) : + EqKeyConversion.{u,v} resolve entries ⟨left.addr, ctxAddr, max left.lbr right.lbr, left.lbr⟩ + ⟨right.addr, ctxAddr, max left.lbr right.lbr, right.lbr⟩ := + ⟨rfl, rfl, AddressConversion.ofClaim origin leftReads rightReads leftTyped rightTyped claim⟩ + +end RecordingLemmas + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/DefEqQuick.lean b/Ix/Kernel/Verify/Consistency/DefEqQuick.lean new file mode 100644 index 000000000..8391f4624 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/DefEqQuick.lean @@ -0,0 +1,792 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.DefEqMemo + +/-! +# The quick structural DefEq tier under the contracts + +Tier 1 of production conversion compares sorts by universe equality and +matching binders by opening both bodies with one common fresh local. This +module proves it sound against the checker invariant with the recursive +conversion callback abstracted by its contract: universe equality is +`univEq_sound` read into the model, and a binder comparison composes the +domain answer, the actual production binder opening, and the body answer into +lambda or dependent-function congruence. Every other constructor pair returns +`false` without touching the state. + +The binder case consumes premises the contracts do not yet supply, stated in +`DefEqBinderAssumptions`: typing of the domains and bodies of a typed binder, +agreement of the two root annotations of a binder pair the tier accepts, and +the synthesis origin of the pushed context. Constant-instance and +argument-spine congruence are proved here for the later same-head tiers. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-! ### Outcome-directed reasoning through the monad -/ + +theorem EStateM.bind_cases {ε σ α γ : Type} {action : EStateM ε σ α} {next : α → EStateM ε σ γ} + {state : σ} {post : EStateM.Result ε σ γ → Prop} + (ok : ∀ value after, action state = .ok value after → post (next value after)) + (error : ∀ err after, action state = .error err after → post (.error err after)) : + post ((action >>= next) state) := by + change post (EStateM.bind action next state) + unfold EStateM.bind + cases run : action state with + | ok value after => exact ok value after run + | error err after => exact error err after run + +/-- Scope cleanup as a pure map on outcomes. -/ +def scopedResult {α : Type} (before : TcState .anon) : + EStateM.Result (TcError .anon) (TcState .anon) α → + EStateM.Result (TcError .anon) (TcState .anon) α + | .ok value after => .ok value {after with lctx := after.lctx.truncate before.lctx.size} + | .error err after => .error err {after with lctx := after.lctx.truncate before.lctx.size} + +theorem withLctxScope_run {α : Type} (action : RecM .anon α) (methods : Methods .anon) + (before : TcState .anon) : + (RecM.withLctxScope action).run methods before = scopedResult before (action.run methods before) := by + rw [withLctxScope_eq] + cases action.run methods before <;> rfl + +theorem intern_eq (term : KExpr .anon) (state : TcState .anon) : + TcM.intern term state = .ok (state.env.intern.internExpr term).1 + {state with env := {state.env with intern := (state.env.intern.internExpr term).2}} := rfl + +theorem runIntern_eq {α : Type} (action : InternM .anon α) (state : TcState .anon) : + TcM.runIntern action state = .ok (action state.env.intern).1 + {state with env := {state.env with intern := (action state.env.intern).2}} := rfl + +theorem isDefEqCall_run (left right : KExpr .anon) (methods : Methods .anon) : + (RecM.isDefEqCall left right).run methods = methods.isDefEq left right := rfl + +/-! ### Annotated readings of the compared shapes -/ + +section Readings + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + +private theorem bind_some {α γ : Type _} {action : Option α} {next : α → Option γ} {result : γ} + (run : action.bind next = some result) : + ∃ value, action = some value ∧ next value = some result := by + cases action with + | none => contradiction + | some value => exact ⟨value, rfl, run⟩ + +theorem readScopedExpr?_sort_annotated {level : KUniv .anon} {info : ExprInfo .anon} {a : AExpr β} + (reading : readScopedExpr? resolve locals (.sort level info) = some a.erase) : + a = .sort (readLevel level) := by + have erased : a.erase = .sort (readLevel level) := (Option.some.inj reading).symm + cases a <;> simp only [AExpr.erase] at erased <;> cases erased + rfl + +theorem readScopedExpr?_const_annotated {id : KId .anon} {levels : Array (KUniv .anon)} + {info : ExprInfo .anon} {a : AExpr β} + (reading : readScopedExpr? resolve locals (.const id levels info) = some a.erase) : + ∃ ref, resolve id.addr = some ref ∧ a = .const ref (levels.toList.map readLevel) := by + rw [readScopedExpr?] at reading + obtain ⟨ref, resolved, reading⟩ := bind_some reading + exact ⟨ref, resolved, AExpr.eq_const_of_erase_eq (Option.some.inj reading).symm⟩ + +theorem readScopedExpr?_lam_annotated {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body : KExpr .anon} {info : ExprInfo .anon} {a : AExpr β} + (reading : readScopedExpr? resolve locals (.lam name bi domain body info) = some a.erase) : + ∃ (condition : Certified.PropWhen) (A b : AExpr β), a = .lam condition A b ∧ + readScopedExpr? resolve locals domain = some A.erase ∧ + readScopedExpr? resolve locals body 1 = some b.erase := by + rw [readScopedExpr?] at reading + obtain ⟨A', domainReads, reading⟩ := bind_some reading + obtain ⟨b', bodyReads, reading⟩ := bind_some reading + have erased : a.erase = .lam A' b' := (Option.some.inj reading).symm + cases a <;> simp only [AExpr.erase] at erased <;> cases erased + exact ⟨_, _, _, rfl, domainReads, bodyReads⟩ + +theorem readScopedExpr?_all_annotated {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body : KExpr .anon} {info : ExprInfo .anon} {a : AExpr β} + (reading : readScopedExpr? resolve locals (.all name bi domain body info) = some a.erase) : + ∃ (condition : Certified.PropWhen) (A B : AExpr β), a = .forallE condition A B ∧ + readScopedExpr? resolve locals domain = some A.erase ∧ + readScopedExpr? resolve locals body 1 = some B.erase := by + rw [readScopedExpr?] at reading + obtain ⟨A', domainReads, reading⟩ := bind_some reading + obtain ⟨B', bodyReads, reading⟩ := bind_some reading + have erased : a.erase = .forallE A' B' := (Option.some.inj reading).symm + cases a <;> simp only [AExpr.erase] at erased <;> cases erased + exact ⟨_, _, _, rfl, domainReads, bodyReads⟩ + +end Readings + +/-! ### Constant instances with pointwise equal universes -/ + +section Levels + +variable {β : Type u} {entries : Model.Environment β} {context : Model.Context β} + +/-- The model has no rule for two instances of one constant at pointwise +equivalent levels; the interpretation depends only on the evaluated levels. -/ +theorem ConversionClaim.constLevels {ref : ConstRef β} {left right : List VLevel} + (levels : ∀ values, left.map (VLevel.eval values) = right.map (VLevel.eval values)) : + ConversionClaim.{u,v} entries context (.const ref left) (.const ref right) := by + intro V _ constants _ values env _ + exact congrArg (constants ref) (levels values) + +private theorem zip_levels : ∀ (left right : List (KUniv .anon)), left.length = right.length → + RecM.allDefEqUniversesList (left.zip right) = true → + (∀ u ∈ left, ∀ v ∈ right, u.AddrFaithful v ∧ u.size < UInt64.size ∧ v.size < UInt64.size) → + ∀ values, (left.map readLevel).map (VLevel.eval values) = + (right.map readLevel).map (VLevel.eval values) + | [], [], _, _, _, _ => rfl + | [], _ :: _, lengths, _, _, _ => absurd lengths (by simp) + | _ :: _, [], lengths, _, _, _ => absurd lengths (by simp) + | l :: ls, r :: rs, lengths, accepted, resources, values => by + simp only [List.zip_cons_cons, RecM.allDefEqUniversesList, Bool.and_eq_true] at accepted + obtain ⟨faithful, boundL, boundR⟩ := + resources l (List.mem_cons_self ..) r (List.mem_cons_self ..) + have head := Theory.VLevel.equiv_def.mp (univEq_sound faithful boundL boundR accepted.1) values + have tail := zip_levels ls rs (by simpa using lengths) accepted.2 + (fun u hu v hv => resources u (List.mem_cons_of_mem _ hu) v (List.mem_cons_of_mem _ hv)) values + simp only [List.map_cons, head, tail] + +/-- The production universe gate of constant-headed comparisons decides +pointwise level equality under faithfulness and size bounds of the levels. -/ +theorem sameDefEqUniverses_sound {left right : Array (KUniv .anon)} + (accepted : RecM.sameDefEqUniverses left right = true) + (resources : ∀ u ∈ left, ∀ v ∈ right, u.AddrFaithful v ∧ u.size < UInt64.size ∧ v.size < UInt64.size) : + ∀ values, (left.toList.map readLevel).map (VLevel.eval values) = + (right.toList.map readLevel).map (VLevel.eval values) := by + simp only [RecM.sameDefEqUniverses, Bool.and_eq_true, beq_iff_eq] at accepted + obtain ⟨sizes, pairs⟩ := accepted + rw [Array.toList_zip] at pairs + exact zip_levels left.toList right.toList (by simpa using sizes) pairs + (fun u hu v hv => resources u (by simpa using hu) v (by simpa using hv)) + +/-- Two readable instances of one constant that pass the universe gate are +convertible. -/ +theorem ConversionClaim.constInstances {resolve : Address → Option (ConstRef β)} + {locals : List FVarId} {leftId rightId : KId .anon} {leftUs rightUs : Array (KUniv .anon)} + {leftInfo rightInfo : ExprInfo .anon} {a b : AExpr β} + (leftReads : readScopedExpr? resolve locals (.const leftId leftUs leftInfo) = some a.erase) + (rightReads : readScopedExpr? resolve locals (.const rightId rightUs rightInfo) = some b.erase) + (sameHead : leftId.addr = rightId.addr) + (accepted : RecM.sameDefEqUniverses leftUs rightUs = true) + (resources : ∀ u ∈ leftUs, ∀ v ∈ rightUs, + u.AddrFaithful v ∧ u.size < UInt64.size ∧ v.size < UInt64.size) : + ConversionClaim.{u,v} entries context a b := by + obtain ⟨ref, resolved, aEq⟩ := readScopedExpr?_const_annotated leftReads + obtain ⟨ref', resolved', bEq⟩ := readScopedExpr?_const_annotated rightReads + rw [sameHead] at resolved + cases Option.some.inj (resolved.symm.trans resolved') + subst aEq bEq + exact ConversionClaim.constLevels (sameDefEqUniverses_sound accepted resources) + +end Levels + +/-! ### Typing across a converted binder domain -/ + +section Domains + +variable {β : Type u} {entries : Model.Environment β} {context : Model.Context β} + +theorem contextValid_pushConverted {V : Type v} [SetTheory V] {constants : Assignment β V} + {levels : List Nat} {A A' : AExpr β} {env : Nat → V} + (valid : (context.push A).Valid constants levels env) + (denoted : WellDenoted constants levels (Valuation.skip 1 0 env) A') + (same : interp constants levels (Valuation.skip 1 0 env) A = + interp constants levels (Valuation.skip 1 0 env) A') : + (context.push A').Valid constants levels env := by + intro index B found + cases index with + | zero => + simp only [Model.Context.push, List.getElem?_cons_zero, Option.some.injEq] at found + subst found + have head := valid 0 (A.liftN 1) rfl + rw [wellDenoted_liftN, interp_liftN] at head ⊢ + exact ⟨denoted, same ▸ head.2⟩ + | succ index => + exact valid (index + 1) B + (by simpa only [Model.Context.push, List.getElem?_cons_succ] using found) + +/-- A term typed under the second domain of a binder pair is typed under the +first once the domains are convertible and the second is a type. -/ +theorem TypingClaim.pushConverted {A A' term type : AExpr β} {level : VLevel} + (formed : TypingClaim.{u,v} entries context A' (.sort level)) + (converted : ConversionClaim.{u,v} entries context A A') + (typed : TypingClaim.{u,v} entries (context.push A') term type) : + TypingClaim.{u,v} entries (context.push A) term type := by + intro V _ constants realizes levels env valid + have tail := context_valid_tail valid + exact typed V constants realizes levels env + (contextValid_pushConverted valid (formed V constants realizes levels _ tail).1 + (converted V constants realizes levels _ tail)) + +end Domains + +/-! ### Resources and boundary premises of the quick tier -/ + +/-- Finite resources of one binder comparison at the state where the left +binder is opened: both bodies are constructed and bounded, and the intern +table, the fresh local, and both instantiation reaches are collision free. -/ +structure QuickBinderData (state : TcState .anon) (body1 body2 : KExpr .anon) : Prop where + constructed1 : body1.Constructed + constructed2 : body2.Constructed + bound1 : body1.size + 1 < UInt64.size + bound2 : body2.size + 1 < UInt64.size + faithful : KExpr.CollisionFree fun term => + state.env.intern.ExprSupport term ∨ term = KExpr.mkFVar ⟨state.env.nextFVarId⟩ () ∨ + KExpr.InstRevReach #[KExpr.mkFVar ⟨state.env.nextFVarId⟩ ()] body1 0 term ∨ + KExpr.InstRevReach #[KExpr.mkFVar ⟨state.env.nextFVarId⟩ ()] body2 0 term + +theorem QuickBinderData.left {state : TcState .anon} {body1 body2 : KExpr .anon} + (data : QuickBinderData state body1 body2) : BinderOpeningData state body1 := + ⟨data.constructed1, data.bound1, data.faithful.mono fun _ h => + h.elim Or.inl fun h => h.elim (fun equal => .inr (.inl equal)) fun reached => + .inr (.inr (.inl reached))⟩ + +section Premises + +variable {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (source : Ixon.Env) + (catalog : List (SourceCacheRequest source)) + +/-- Premises of the binder case that the contracts do not supply: hereditary +typing of a typed binder's domain and body, agreement of the root annotations +of an accepted binder pair, and the synthesis origin of the pushed context. The +set model interprets annotations, so the agreement is a discipline on the +readings and not a consequence of typing. -/ +structure DefEqBinderAssumptions : Prop where + lamHereditary : ∀ (context : Model.Context β) (condition : Certified.PropWhen) + (domain body type : AExpr β), + TypingClaim.{u,v} entries context (.lam condition domain body) type → + (∃ level, TypingClaim.{u,v} entries context domain (.sort level)) ∧ + (∃ codomain, TypingClaim.{u,v} entries (context.push domain) body codomain) + piHereditary : ∀ (context : Model.Context β) (condition : Certified.PropWhen) + (domain body type : AExpr β), + TypingClaim.{u,v} entries context (.forallE condition domain body) type → + (∃ level, TypingClaim.{u,v} entries context domain (.sort level)) ∧ + (∃ level, TypingClaim.{u,v} entries (context.push domain) body (.sort level)) + lamAnnotations : ∀ (context : Model.Context β) (left right : Certified.PropWhen) + (domain domain' body body' : AExpr β), + (∃ type, TypingClaim.{u,v} entries context (.lam left domain body) type) → + (∃ type, TypingClaim.{u,v} entries context (.lam right domain' body') type) → + ConversionClaim.{u,v} entries context domain domain' → + ConversionClaim.{u,v} entries (context.push domain) body body' → left = right + piAnnotations : ∀ (context : Model.Context β) (left right : Certified.PropWhen) + (domain domain' body body' : AExpr β), + (∃ type, TypingClaim.{u,v} entries context (.forallE left domain body) type) → + (∃ type, TypingClaim.{u,v} entries context (.forallE right domain' body') type) → + ConversionClaim.{u,v} entries context domain domain' → + ConversionClaim.{u,v} entries (context.push domain) body body' → left = right + binderOrigin : ∀ (context : Model.Context β) (bounds : List VLevel) (domain : AExpr β) + (level : VLevel), + Nonempty (SynthesisContext resolve anchor [] [] entries context bounds) → + TypingClaim.{u,v} entries context domain (.sort level) → + Nonempty (SynthesisContext resolve anchor [] [] entries (context.push domain) (level :: bounds)) + +/-- Finite resources of the direct tiers over a support of compared operands, +each an instance of `RunAssumptions`: pairwise address faithfulness, universe +faithfulness and bounds at sorts, and binder-opening data at every invariant +state. -/ +structure DefEqTierResources (support : KExpr .anon → Prop) : Prop where + faithful : ∀ left right, support left → support right → left.AddrFaithful right + sorts : ∀ (u w : KUniv .anon) (infoU infoW : ExprInfo .anon), + support (.sort u infoU) → support (.sort w infoW) → + u.AddrFaithful w ∧ u.size < UInt64.size ∧ w.size < UInt64.size + lamData : ∀ (state : TcState .anon) (locals : List FVarId) (context : Model.Context β) + (bounds : List VLevel), + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + ∀ (name1 name2 : Mode.anon.F Name) (bi1 bi2 : Mode.anon.F Lean.BinderInfo) + (ty1 body1 ty2 body2 : KExpr .anon) (info1 info2 : ExprInfo .anon), + support (.lam name1 bi1 ty1 body1 info1) → support (.lam name2 bi2 ty2 body2 info2) → + QuickBinderData state body1 body2 + piData : ∀ (state : TcState .anon) (locals : List FVarId) (context : Model.Context β) + (bounds : List VLevel), + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + ∀ (name1 name2 : Mode.anon.F Name) (bi1 bi2 : Mode.anon.F Lean.BinderInfo) + (ty1 body1 ty2 body2 : KExpr .anon) (info1 info2 : ExprInfo .anon), + support (.all name1 bi1 ty1 body1 info1) → support (.all name2 bi2 ty2 body2 info2) → + QuickBinderData state body1 body2 + +/-- The binder tier's outcome at annotated readings of both domains and +bodies: the answer `true` establishes the domain conversion and the body +conversion under the first domain. -/ +def QuickBinderPost (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (A₁ A₂ b₁ b₂ : AExpr β) : EStateM.Result (TcError .anon) (TcState .anon) Bool → Prop + | .ok true after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + ConversionClaim.{u,v} entries context A₁ A₂ ∧ + ConversionClaim.{u,v} entries (context.push A₁) b₁ b₂ + | .ok false after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + +end Premises + +/-! ### The binder tier -/ + +section Binders + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +/-- Interning one expression preserves the invariant once the updated table is coherent. -/ +theorem CheckerInvariant.internStep {state : TcState .anon} {term : KExpr .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) + (coherent : (state.env.intern.internExpr term).2.WF) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {state with env := {state.env with intern := (state.env.intern.internExpr term).2}} := + valid.ofMaps ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl coherent rfl rfl + (fun partition => by cases partition <;> rfl) (fun partition => by cases partition <;> rfl) + rfl rfl rfl + +/-- Running an intern-table action preserves the invariant once the updated table is coherent. -/ +theorem CheckerInvariant.runInternStep {α : Type} {state : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) + (action : InternM .anon α) (coherent : (action state.env.intern).2.WF) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {state with env := {state.env with intern := (action state.env.intern).2}} := + valid.ofMaps ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl coherent rfl rfl + (fun partition => by cases partition <;> rfl) (fun partition => by cases partition <;> rfl) + rfl rfl rfl + +/-- Successful binder opening from a coherent state, with the exact fresh +identifier, the opened body, the updated table's coherence and support, and +the unchanged fields the scope exit needs. -/ +theorem openBinder_support {before after : TcState .anon} {name : Mode.anon.F Name} + {bi : Mode.anon.F Lean.BinderInfo} {type body opened : KExpr .anon} {fresh : FVarId} + (data : BinderOpeningData before body) (coherent : before.env.intern.WF) + (run : TcM.openBinder name bi type body before = .ok (opened, fresh) after) : + fresh = ⟨before.env.nextFVarId⟩ ∧ + opened = KExpr.instantiateRevSpec body #[KExpr.mkFVar ⟨before.env.nextFVarId⟩ ()] 0 ∧ + after.env.intern.WF ∧ + (∀ term, after.env.intern.ExprSupport term → before.env.intern.ExprSupport term ∨ + term = KExpr.mkFVar ⟨before.env.nextFVarId⟩ () ∨ + KExpr.InstRevReach #[KExpr.mkFVar ⟨before.env.nextFVarId⟩ ()] body 0 term) := by + have nameUnit : name = () := Subsingleton.elim _ _ + have biUnit : bi = () := Subsingleton.elim _ _ + subst nameUnit biUnit + have interned : (before.env.intern.internExpr + (KExpr.mkFVar ⟨before.env.nextFVarId⟩ ())).1 = + KExpr.mkFVar ⟨before.env.nextFVarId⟩ () := by + have faithful := KExpr.keyCollisionFree_anon.mpr + (data.faithful.mono (fun _ h => h.elim Or.inl (fun equal => .inr (.inl equal))) : + KExpr.CollisionFree fun term => before.env.intern.ExprSupport term ∨ + term = KExpr.mkFVar ⟨before.env.nextFVarId⟩ ()) + simpa only [KExpr.eraseMeta_anon] using + before.env.intern.internExpr_eraseMeta coherent faithful + have walk := instantiateRev_spec (fvars := #[KExpr.mkFVar ⟨before.env.nextFVarId⟩ ()]) + data.faithful data.constructed (by simpa using data.bound) + (fun _ reached => .inr (.inr reached)) + (coherent.internExpr (KExpr.mkFVar ⟨before.env.nextFVarId⟩ ())) + (fun _ member => (InternTable.ExprSupport.of_internExpr member).elim + Or.inl (fun equal => .inr (.inl equal))) + rw [openBinder_eq] at run + split at run + · simp only [interned] at run + cases run + exact ⟨rfl, walk.1, walk.2.1, walk.2.2⟩ + · contradiction + +/-- The common-local binder comparison. The domain answer comes from the +recursive contract, the left body is opened by production, the right body is +instantiated with the same local, and the body answer is taken under the +first domain after transporting the right body's typing across the domain +conversion. Scope exit restores the caller's registration on every outcome. -/ +theorem quickBinder_sound {methods : Methods .anon} + (recursive : DefEqContract.{u,v} resolve anchor entries source catalog methods) + (frames : MethodsLocalState methods) {before : TcState .anon} + {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} {ty1 body1 ty2 body2 : KExpr .anon} + {A₁ A₂ b₁ b₂ : AExpr β} {l₁ l₂ : VLevel} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (data : ∀ state, + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + QuickBinderData state body1 body2) + (ty1Reads : readScopedExpr? resolve locals ty1 = some A₁.erase) + (ty2Reads : readScopedExpr? resolve locals ty2 = some A₂.erase) + (body1Reads : readScopedExpr? resolve locals body1 1 = some b₁.erase) + (body2Reads : readScopedExpr? resolve locals body2 1 = some b₂.erase) + (ty1Typed : TypingClaim.{u,v} entries context A₁ (.sort l₁)) + (ty2Typed : TypingClaim.{u,v} entries context A₂ (.sort l₂)) + (body1Typed : ∃ type, TypingClaim.{u,v} entries (context.push A₁) b₁ type) + (body2Typed : ∃ type, TypingClaim.{u,v} entries (context.push A₂) b₂ type) + (pushed : Nonempty (SynthesisContext resolve anchor [] [] entries (context.push A₁) (l₁ :: bounds))) : + QuickBinderPost.{u,v} resolve anchor entries source catalog locals context bounds A₁ A₂ b₁ b₂ + ((RecM.quickBinder name bi ty1 body1 ty2 body2).run methods before) := by + have nameUnit : name = () := Subsingleton.elim _ _ + subst nameUnit + have domains := recursive.isDefEq ty1 ty2 locals context bounds before A₁ A₂ valid ty1Reads + ⟨_, ty1Typed⟩ ty2Reads ⟨_, ty2Typed⟩ + unfold RecM.quickBinder + simp only [ReaderT.run_bind, isDefEqCall_run] + refine EStateM.bind_cases ?_ ?_ + · intro answer middle domainRun + rw [domainRun] at domains + cases answer with + | false => + simp only [Bool.not_false, ↓reduceIte] + exact domains + | true => + simp only [Bool.not_true, Bool.false_eq_true, ↓reduceIte] + obtain ⟨valid', domainClaim⟩ := domains + rw [withLctxScope_run] + simp only [ReaderT.run_bind, ReaderT.run_monadLift, isDefEqCall_run] + refine EStateM.bind_cases (post := fun result => QuickBinderPost.{u,v} resolve anchor entries + source catalog locals context bounds A₁ A₂ b₁ b₂ (scopedResult middle result)) ?_ ?_ + · rintro ⟨opened, fresh⟩ afterOpen openRun + have binderData := data middle valid' + have absent := valid'.structural.freshReading valid'.reading + have openRun' : TcM.openBinder () bi ty1 body1 middle = .ok (opened, fresh) afterOpen := + openRun + have extension := PreservesLocalState.openBinder () bi ty1 body1 middle valid'.structural + rw [openRun'] at extension + obtain ⟨freshEq, openedEq, afterWF, afterSupport⟩ := + openBinder_support binderData.left valid'.coherent openRun' + subst freshEq + have validExt := valid'.openBinder binderData.left ty1Reads pushed openRun' + have openedReads : readScopedExpr? resolve (⟨middle.env.nextFVarId⟩ :: locals) opened = + some b₁.erase := by + rw [openedEq] + exact readScopedExpr?_instantiateRevSpec absent (by simpa using binderData.bound1) body1Reads + have keyFaithful : KExpr.KeyCollisionFree fun term => + afterOpen.env.intern.ExprSupport term ∨ + term = KExpr.mkFVar ⟨middle.env.nextFVarId⟩ () := + KExpr.keyCollisionFree_anon.mpr (binderData.faithful.mono fun term h => + h.elim (fun member => (afterSupport term member).elim Or.inl fun h => + h.elim (fun equal => .inr (.inl equal)) fun reached => .inr (.inr (.inl reached))) + fun equal => .inr (.inl equal)) + have interned : (afterOpen.env.intern.internExpr + (KExpr.mkFVar ⟨middle.env.nextFVarId⟩ ())).1 = + KExpr.mkFVar ⟨middle.env.nextFVarId⟩ () := by + simpa only [KExpr.eraseMeta_anon] using + afterOpen.env.intern.internExpr_eraseMeta afterWF keyFaithful + dsimp only + refine EStateM.bind_cases (post := fun result => QuickBinderPost.{u,v} resolve anchor entries + source catalog locals context bounds A₁ A₂ b₁ b₂ (scopedResult middle result)) ?_ ?_ + · intro fv afterIntern internRun + have internRun' : TcM.intern (KExpr.mkFVar ⟨middle.env.nextFVarId⟩ ()) afterOpen = + .ok fv afterIntern := internRun + rw [intern_eq] at internRun' + obtain ⟨fvEq, stateEq₁⟩ := EStateM.Result.ok.inj internRun' + rw [interned] at fvEq + subst fvEq + have table₃ := afterWF.internExpr (KExpr.mkFVar ⟨middle.env.nextFVarId⟩ ()) + have internEq : afterIntern.env.intern = + (afterOpen.env.intern.internExpr (KExpr.mkFVar ⟨middle.env.nextFVarId⟩ ())).2 := by + rw [← stateEq₁] + have validIntern : CheckerInvariant.{u,v} resolve anchor entries source catalog + (⟨middle.env.nextFVarId⟩ :: locals) (context.push A₁) (l₁ :: bounds) afterIntern := by + rw [← stateEq₁] + exact validExt.internStep table₃ + have walk := instantiateRev_spec (fvars := #[KExpr.mkFVar ⟨middle.env.nextFVarId⟩ ()]) + binderData.faithful binderData.constructed2 (by simpa using binderData.bound2) + (fun _ reached => .inr (.inr (.inr reached))) table₃ + (fun term member => (InternTable.ExprSupport.of_internExpr member).elim + (fun member => (afterSupport term member).elim Or.inl fun h => + h.elim (fun equal => .inr (.inl equal)) fun reached => .inr (.inr (.inl reached))) + fun equal => .inr (.inl equal)) + rw [← internEq] at walk + refine EStateM.bind_cases (post := fun result => QuickBinderPost.{u,v} resolve anchor + entries source catalog locals context bounds A₁ A₂ b₁ b₂ (scopedResult middle result)) + ?_ ?_ + · intro b2Open afterInst instRun + have instRun' : TcM.runIntern + (instantiateRev body2 #[KExpr.mkFVar ⟨middle.env.nextFVarId⟩ ()]) afterIntern = + .ok b2Open afterInst := instRun + rw [runIntern_eq] at instRun' + obtain ⟨b2OpenEq, stateEq₂⟩ := EStateM.Result.ok.inj instRun' + rw [walk.1] at b2OpenEq + subst b2OpenEq + have b2OpenReads : readScopedExpr? resolve (⟨middle.env.nextFVarId⟩ :: locals) + (KExpr.instantiateRevSpec body2 #[KExpr.mkFVar ⟨middle.env.nextFVarId⟩ ()] 0) = + some b₂.erase := + readScopedExpr?_instantiateRevSpec absent (by simpa using binderData.bound2) body2Reads + have validInst : CheckerInvariant.{u,v} resolve anchor entries source catalog + (⟨middle.env.nextFVarId⟩ :: locals) (context.push A₁) (l₁ :: bounds) afterInst := by + rw [← stateEq₂] + exact validIntern.runInternStep _ walk.2.1 + have body2Typed' : ∃ type, TypingClaim.{u,v} entries (context.push A₁) b₂ type := + body2Typed.imp fun _ typed => TypingClaim.pushConverted ty2Typed domainClaim typed + have inner := recursive.isDefEq opened + (KExpr.instantiateRevSpec body2 #[KExpr.mkFVar ⟨middle.env.nextFVarId⟩ ()] 0) + (⟨middle.env.nextFVarId⟩ :: locals) (context.push A₁) (l₁ :: bounds) afterInst b₁ b₂ + validInst openedReads body1Typed b2OpenReads body2Typed' + have innerFrame := frames.isDefEq opened + (KExpr.instantiateRevSpec body2 #[KExpr.mkFVar ⟨middle.env.nextFVarId⟩ ()] 0) + afterInst validInst.structural + have internFrame : LocalStateFrame afterOpen afterInst := by + subst stateEq₂ stateEq₁ + exact ⟨Nat.le_refl _, .refl _, rfl⟩ + have exit : ∀ final, LocalStateFrame afterInst final → + LocalStateExtension middle final := + fun final frame => extension.trans (LocalStateExtension.of_frame validExt.structural + (internFrame.trans frame)) + cases innerRun : methods.isDefEq opened + (KExpr.instantiateRevSpec body2 #[KExpr.mkFVar ⟨middle.env.nextFVarId⟩ ()] 0) + afterInst with + | ok answer final => + rw [innerRun] at inner innerFrame + have restored := exit final innerFrame + cases answer with + | false => + exact valid'.exitScope inner restored.context restored.counter restored.loader + | true => + exact ⟨valid'.exitScope inner.1 restored.context restored.counter restored.loader, + domainClaim, inner.2⟩ + | error err final => + rw [innerRun] at inner innerFrame + have restored := exit final innerFrame + exact valid'.exitScope inner restored.context restored.counter restored.loader + · intro err after instRun + have instRun' : TcM.runIntern (instantiateRev body2 #[KExpr.mkFVar ⟨middle.env.nextFVarId⟩ ()]) _ = + .error err after := instRun + rw [runIntern_eq] at instRun' + cases instRun' + · intro err after internRun + have internRun' : TcM.intern (KExpr.mkFVar ⟨middle.env.nextFVarId⟩ ()) afterOpen = + .error err after := internRun + rw [intern_eq] at internRun' + cases internRun' + · intro err afterOpen openRun + have openRun' : TcM.openBinder () bi ty1 body1 middle = .error err afterOpen := openRun + rw [openBinder_eq] at openRun' + split at openRun' + · cases openRun' + · cases openRun' + exact valid'.exitScope valid' (.refl _) (Nat.le_refl _) rfl + · intro err after domainRun + rw [domainRun] at domains + exact domains + +end Binders + +/-! ### The quick tier -/ + +section Quick + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} + +/-- The exact shape dispatch of the quick tier. -/ +theorem quickDefEq_eq (left right : KExpr .anon) (methods : Methods .anon) (before : TcState .anon) : + (RecM.quickDefEq left right).run methods before = + match left, right with + | .sort u1 _, .sort u2 _ => .ok (univEq u1 u2) before + | .lam name bi ty1 body1 _, .lam _ _ ty2 body2 _ => + (RecM.quickBinder name bi ty1 body1 ty2 body2).run methods before + | .all name bi ty1 body1 _, .all _ _ ty2 body2 _ => + (RecM.quickBinder name bi ty1 body1 ty2 body2).run methods before + | _, _ => .ok false before := by + unfold RecM.quickDefEq + cases left <;> cases right <;> rfl + +/-- Tier 1: sorts by universe equality, matching binders by the common-local +comparison, and every other shape pair rejected without state change. -/ +theorem quickDefEq_sound {methods : Methods .anon} {support : KExpr .anon → Prop} + (recursive : DefEqContract.{u,v} resolve anchor entries source catalog methods) + (frames : MethodsLocalState methods) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqTierResources.{u,v} resolve anchor entries source catalog support) + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {before : TcState .anon} + {left right : KExpr .anon} {a b : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftMember : support left) (rightMember : support right) + (leftReads : readScopedExpr? resolve locals left = some a.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context a type) + (rightReads : readScopedExpr? resolve locals right = some b.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context b type) : + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b + ((RecM.quickDefEq left right).run methods before) := by + rw [quickDefEq_eq] + split + · rename_i u1 info1 u2 info2 + cases accepted : univEq u1 u2 + · exact valid + · obtain ⟨faithful, boundU, boundV⟩ := resources.sorts u1 u2 info1 info2 leftMember rightMember + have aEq := readScopedExpr?_sort_annotated leftReads + have bEq := readScopedExpr?_sort_annotated rightReads + subst aEq bEq + exact ⟨valid, ConversionClaim.sort + (Theory.VLevel.equiv_def.mp (univEq_sound faithful boundU boundV accepted))⟩ + · rename_i name1 bi1 ty1 body1 info1 name2 bi2 ty2 body2 info2 + obtain ⟨p, A₁, b₁, aEq, ty1Reads, body1Reads⟩ := readScopedExpr?_lam_annotated leftReads + obtain ⟨q, A₂, b₂, bEq, ty2Reads, body2Reads⟩ := readScopedExpr?_lam_annotated rightReads + subst aEq bEq + obtain ⟨T₁, aTyped⟩ := leftTyped + obtain ⟨T₂, bTyped⟩ := rightTyped + obtain ⟨⟨l₁, ty1Typed⟩, body1Typed⟩ := binders.lamHereditary context p A₁ b₁ T₁ aTyped + obtain ⟨⟨l₂, ty2Typed⟩, body2Typed⟩ := binders.lamHereditary context q A₂ b₂ T₂ bTyped + have pushed := binders.binderOrigin context bounds A₁ l₁ valid.origin ty1Typed + have post := quickBinder_sound (name := name1) (bi := bi1) recursive frames valid + (fun state valid' => resources.lamData state locals context bounds valid' name1 name2 bi1 bi2 + ty1 body1 ty2 body2 info1 info2 leftMember rightMember) + ty1Reads ty2Reads body1Reads body2Reads ty1Typed ty2Typed body1Typed body2Typed pushed + cases run : (RecM.quickBinder name1 bi1 ty1 body1 ty2 body2).run methods before with + | ok answer after => + rw [run] at post + cases answer with + | false => exact post + | true => + obtain ⟨valid', domainClaim, bodyClaim⟩ := post + have same := binders.lamAnnotations context p q A₁ A₂ b₁ b₂ ⟨T₁, aTyped⟩ ⟨T₂, bTyped⟩ + domainClaim bodyClaim + subst same + exact ⟨valid', ConversionClaim.lam ty1Typed domainClaim bodyClaim⟩ + | error err after => + rw [run] at post + exact post + · rename_i name1 bi1 ty1 body1 info1 name2 bi2 ty2 body2 info2 + obtain ⟨p, A₁, B₁, aEq, ty1Reads, body1Reads⟩ := readScopedExpr?_all_annotated leftReads + obtain ⟨q, A₂, B₂, bEq, ty2Reads, body2Reads⟩ := readScopedExpr?_all_annotated rightReads + subst aEq bEq + obtain ⟨T₁, aTyped⟩ := leftTyped + obtain ⟨T₂, bTyped⟩ := rightTyped + obtain ⟨⟨l₁, ty1Typed⟩, ⟨s₁, body1Typed⟩⟩ := binders.piHereditary context p A₁ B₁ T₁ aTyped + obtain ⟨⟨l₂, ty2Typed⟩, ⟨s₂, body2Typed⟩⟩ := binders.piHereditary context q A₂ B₂ T₂ bTyped + have pushed := binders.binderOrigin context bounds A₁ l₁ valid.origin ty1Typed + have post := quickBinder_sound (name := name1) (bi := bi1) recursive frames valid + (fun state valid' => resources.piData state locals context bounds valid' name1 name2 bi1 bi2 + ty1 body1 ty2 body2 info1 info2 leftMember rightMember) + ty1Reads ty2Reads body1Reads body2Reads ty1Typed ty2Typed ⟨_, body1Typed⟩ ⟨_, body2Typed⟩ pushed + cases run : (RecM.quickBinder name1 bi1 ty1 body1 ty2 body2).run methods before with + | ok answer after => + rw [run] at post + cases answer with + | false => exact post + | true => + obtain ⟨valid', domainClaim, bodyClaim⟩ := post + have same := binders.piAnnotations context p q A₁ A₂ B₁ B₂ ⟨T₁, aTyped⟩ ⟨T₂, bTyped⟩ + domainClaim bodyClaim + subst same + exact ⟨valid', ConversionClaim.forallE ty1Typed domainClaim bodyClaim⟩ + | error err after => + rw [run] at post + exact post + · exact valid + +end Quick + +/-! ### Argument spines -/ + +section Spines + +variable {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (source : Ixon.Env) + (catalog : List (SourceCacheRequest source)) (locals : List FVarId) + (context : Model.Context β) (bounds : List VLevel) + +/-- Typed annotated readings of an argument-pair list, in order. -/ +inductive SpineReadings : List (KExpr .anon × KExpr .anon) → List (AExpr β × AExpr β) → Prop + | nil : SpineReadings [] [] + | cons {left right : KExpr .anon} {a b : AExpr β} {pairs : List (KExpr .anon × KExpr .anon)} + {terms : List (AExpr β × AExpr β)} + (leftReads : readScopedExpr? resolve locals left = some a.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context a type) + (rightReads : readScopedExpr? resolve locals right = some b.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context b type) + (rest : SpineReadings pairs terms) : + SpineReadings ((left, right) :: pairs) ((a, b) :: terms) + +/-- The spine loop's outcome: `true` establishes every pairwise conversion. -/ +def SpineConversionPost (terms : List (AExpr β × AExpr β)) : + EStateM.Result (TcError .anon) (TcState .anon) Bool → Prop + | .ok true after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + ∀ term ∈ terms, ConversionClaim.{u,v} entries context term.1 term.2 + | .ok false after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + +end Spines + +section SpineLemmas + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +theorem allDefEqSpineArgsList_cons (left right : KExpr .anon) + (rest : List (KExpr .anon × KExpr .anon)) (methods : Methods .anon) (before : TcState .anon) : + (RecM.allDefEqSpineArgsList ((left, right) :: rest)).run methods before = + match methods.isDefEq left right before with + | .error err after => .error err after + | .ok false after => .ok false after + | .ok true after => (RecM.allDefEqSpineArgsList rest).run methods after := by + rw [RecM.allDefEqSpineArgsList] + simp only [ReaderT.run_bind, isDefEqCall_run] + change EStateM.bind (methods.isDefEq left right) _ before = _ + unfold EStateM.bind + cases methods.isDefEq left right before with + | error err after => rfl + | ok answer after => cases answer <;> rfl + +/-- Left-to-right argument comparison through the recursive contract. -/ +theorem allDefEqSpineArgsList_sound {methods : Methods .anon} + (recursive : DefEqContract.{u,v} resolve anchor entries source catalog methods) + {pairs : List (KExpr .anon × KExpr .anon)} {terms : List (AExpr β × AExpr β)} + (readings : SpineReadings.{u,v} resolve entries locals context pairs terms) : + ∀ {before : TcState .anon}, + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before → + SpineConversionPost.{u,v} resolve anchor entries source catalog locals context bounds terms + ((RecM.allDefEqSpineArgsList pairs).run methods before) := by + induction readings with + | nil => + intro before valid + exact ⟨valid, fun _ member => nomatch member⟩ + | @cons left right a b pairs terms leftReads leftTyped rightReads rightTyped _ ih => + intro before valid + rw [allDefEqSpineArgsList_cons] + have post := recursive.isDefEq left right locals context bounds before a b valid leftReads + leftTyped rightReads rightTyped + cases run : methods.isDefEq left right before with + | error err after => + rw [run] at post + exact post + | ok answer after => + rw [run] at post + cases answer with + | false => exact post + | true => + obtain ⟨valid', claim⟩ := post + have tail := ih valid' + dsimp only + cases run' : (RecM.allDefEqSpineArgsList pairs).run methods after with + | error err final => + rw [run'] at tail + exact tail + | ok answer' final => + rw [run'] at tail + cases answer' with + | false => exact tail + | true => + obtain ⟨valid'', claims⟩ := tail + refine ⟨valid'', fun term member => ?_⟩ + rcases List.mem_cons.mp member with rfl | member + · exact claim + · exact claims term member + +/-- The array entry point is the list loop on the array's elements. -/ +theorem allDefEqSpineArgs_sound {methods : Methods .anon} + (recursive : DefEqContract.{u,v} resolve anchor entries source catalog methods) + {pairs : Array (KExpr .anon × KExpr .anon)} {terms : List (AExpr β × AExpr β)} + (readings : SpineReadings.{u,v} resolve entries locals context pairs.toList terms) + {before : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) : + SpineConversionPost.{u,v} resolve anchor entries source catalog locals context bounds terms + ((RecM.allDefEqSpineArgs pairs).run methods before) := + allDefEqSpineArgsList_sound recursive readings valid + +end SpineLemmas + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/DefEqTiers.lean b/Ix/Kernel/Verify/Consistency/DefEqTiers.lean new file mode 100644 index 000000000..f7f77a620 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/DefEqTiers.lean @@ -0,0 +1,1000 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.DefEqQuick + +/-! +# The non-reducing DefEq tiers under the contracts + +Production conversion answers from three memos before any reduction, then +charges fuel and depth and runs the recursive tiers. This module proves every +tier up to the quick structural probe against the checker invariant with the +recursive callbacks abstracted by their contracts: + +* the entry: statistics, the hash path, the equivalence-manager query with its + path halving, the full cache, the cheap cache with its promotion, and the + copies between partitions; +* the guarded representative probe after both partitions miss, whose hit + composes the two representative chains with the root edge and records the + original pair; +* the charged tail: fuel and depth accounting, the inner tiers under caught + errors, and the manager and cache writes of a positive answer; +* the inner tiers: the quick structural probe of `DefEqQuick`, then the + production seam `isDefEqInnerAfterQuick` under its contract. + +Each proof follows the exact production control flow through the monad, +outcome by outcome, and preserves the invariant on every `false` and error +path. `DefEqSeamAssumptions` collects what remains: the reducing tail, the +transport of recorded chains to the caller's registration, the annotation +discipline of the hash path, and the binder premises. `DefEqTierResources` +collects the finite collision and walker data, each an instance of +`RunAssumptions`. `isDefEq_direct_tiers` is the `StepContracts.isDefEq` +field modulo those premises. +-/ +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-! ### Run equations of the recursive monad's primitives -/ + +theorem modify_run (update : TcState .anon → TcState .anon) (methods : Methods .anon) + (state : TcState .anon) : + (modify update : RecM .anon PUnit).run methods state = .ok ⟨⟩ (update state) := rfl + +theorem get_run (methods : Methods .anon) (state : TcState .anon) : + (get : RecM .anon (TcState .anon)).run methods state = .ok state state := rfl + +theorem pure_run {α : Type} (value : α) (methods : Methods .anon) (state : TcState .anon) : + (pure value : RecM .anon α).run methods state = .ok value state := rfl + +theorem throw_run {α : Type} (err : TcError .anon) (methods : Methods .anon) (state : TcState .anon) : + (throw err : RecM .anon α).run methods state = .error err state := rfl +/-- The caught-error wrapper of the inner tiers as a pure map on outcomes. -/ +theorem tryExcept_run {α : Type} (action : RecM .anon α) (methods : Methods .anon) + (state : TcState .anon) : + (tryCatch (do let value ← action; pure (Except.ok value)) + (fun err => pure (Except.error err)) : RecM .anon (Except (TcError .anon) α)).run methods state = + match action.run methods state with + | .ok value after => .ok (.ok value) after + | .error err after => .ok (.error err) after := by + change EStateM.tryCatch (EStateM.bind (action.run methods) _) _ state = _ + unfold EStateM.tryCatch EStateM.bind + cases action.run methods state <;> rfl + +/-! ### Invariant preservation through the entry's bookkeeping -/ + +namespace CheckerInvariant + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +/-- An optional statistics update outside the environment, manager, and local +context preserves the invariant. -/ +theorem afterBumpStats {before after : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (update : TcState .anon → TcState .anon) + (env : ∀ state, (update state).env = state.env) + (lctx : ∀ state, (update state).lctx = state.lctx) + (loader : ∀ state, (update state).lazyFault = state.lazyFault) + (manager : ∀ state, (update state).equivManager = state.equivManager) + (run : TcM.bumpStats update before = .ok () after) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after := by + rw [bumpStats_eq] at run + cases run + split + · exact valid.ofDefEqScalars (env _) (lctx _) (loader _) (manager _) + · exact valid + +/-- Fuel accounting preserves the invariant on both outcomes. -/ +theorem tickOutcome {before : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) : + match TcM.tick before with + | .ok _ after | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after := by + rw [tick_eq] + by_cases fuel : (before.recFuel == 0) = true + · rw [if_pos fuel] + exact valid + · rw [if_neg fuel] + exact valid.ofDefEqScalars rfl rfl rfl rfl + +end CheckerInvariant + +/-- A recorded conversion of a pair is recorded at the pair's canonical key. -/ +theorem AddressConversion.canonical {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {left right lo hi ctxAddr : Address} + (canonical : canonicalPair left right = (lo, hi)) + (recorded : AddressConversion.{u,v} resolve entries left right ctxAddr) : + AddressConversion.{u,v} resolve entries lo hi ctxAddr := by + rcases canonicalPair_cases left right with same | swapped + · rw [canonical] at same + obtain ⟨rfl, rfl⟩ := Prod.mk.inj same + exact recorded + · rw [canonical] at swapped + obtain ⟨rfl, rfl⟩ := Prod.mk.inj swapped + exact recorded.symm + +/-! ### The charged recursive tail -/ + +section Charged + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +/-- After every O(1) exit misses: statistics, fuel, the depth guard, the inner +tiers under caught errors, depth restoration, and the recording of a positive +answer in the manager and the caches. The inner tiers are abstracted by their +outcome at the caller's readings. -/ +theorem isDefEqAfterRootCacheMiss_sound {methods : Methods .anon} {before : TcState .anon} + {a b : KExpr .anon} {ta tb : AExpr β} {eqCtx lo hi : Address} {cheapMode : Bool} + (inner : ∀ state, + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds ta tb + ((RecM.isDefEqInner a b).run methods state)) + (origin : DefEqKeyOrigin.{u,v} resolve anchor entries source catalog locals context bounds a b eqCtx) + (canonical : canonicalPair a.addr b.addr = (lo, hi)) + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftReads : readScopedExpr? resolve locals a = some ta.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context ta type) + (rightReads : readScopedExpr? resolve locals b = some tb.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context tb type) : + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds ta tb + ((RecM.isDefEqAfterRootCacheMiss a b ⟨a.addr, eqCtx, max a.lbr b.lbr, a.lbr⟩ + ⟨b.addr, eqCtx, max a.lbr b.lbr, b.lbr⟩ (lo, hi, eqCtx) cheapMode).run methods before) := by + have recordEdge : ConversionClaim.{u,v} entries context ta tb → + EqKeyChain.{u,v} resolve entries ⟨a.addr, eqCtx, max a.lbr b.lbr, a.lbr⟩ + ⟨b.addr, eqCtx, max a.lbr b.lbr, b.lbr⟩ := + fun claim => .edge (EqKeyConversion.ofClaim origin leftReads rightReads leftTyped rightTyped claim) + have recordKey : ConversionClaim.{u,v} entries context ta tb → + AddressConversion.{u,v} resolve entries lo hi eqCtx := + fun claim => (AddressConversion.ofClaim origin leftReads rightReads leftTyped rightTyped claim).canonical + canonical + unfold RecM.isDefEqAfterRootCacheMiss + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + refine EStateM.bind_cases ?_ ?_ + · intro value s₀ statsRun + cases value + have valid₀ := valid.afterBumpStats (fun s => {s with deqMisses := s.deqMisses + 1}) + (fun _ => rfl) (fun _ => rfl) (fun _ => rfl) (fun _ => rfl) statsRun + try dsimp only + refine EStateM.bind_cases ?_ ?_ + · intro value s₁ tickRun + cases value + have valid₁ : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds + s₁ := by + have outcome := valid₀.tickOutcome + have tickRun' : TcM.tick s₀ = .ok () s₁ := tickRun + rw [tickRun'] at outcome + exact outcome + try dsimp only + refine EStateM.bind_cases ?_ ?_ + · intro value s₂ bumpRun + rw [modify_run] at bumpRun + obtain ⟨_, bumpEq⟩ := EStateM.Result.ok.inj bumpRun + have valid₂ : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context + bounds s₂ := by + subst bumpEq + exact valid₁.ofDefEqScalars rfl rfl rfl rfl + try dsimp only + refine EStateM.bind_cases ?_ ?_ + · intro current s₂' getRun + rw [get_run] at getRun + obtain ⟨currentEq, stateEq⟩ := EStateM.Result.ok.inj getRun + subst currentEq stateEq + try dsimp only + split + · simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro value s₃ restoreRun + rw [modify_run] at restoreRun + obtain ⟨_, restoreEq⟩ := EStateM.Result.ok.inj restoreRun + try dsimp only + refine EStateM.bind_cases ?_ ?_ + · intro value s h + rw [throw_run] at h + cases h + · intro err s h + rw [throw_run] at h + cases h + subst restoreEq + exact valid₂.ofDefEqScalars rfl rfl rfl rfl + · intro err s h + rw [modify_run] at h + cases h + · simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro result s₃ catchRun + rw [tryExcept_run] at catchRun + have innerPost := inner s₂ valid₂ + cases innerRun : (RecM.isDefEqInner a b).run methods s₂ with + | ok answer s₃' => + rw [innerRun] at catchRun innerPost + dsimp only at catchRun + obtain ⟨resultEq, stateEq⟩ := EStateM.Result.ok.inj catchRun + subst resultEq stateEq + try dsimp only + simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro value s₄ restoreRun + rw [modify_run] at restoreRun + obtain ⟨_, restoreEq⟩ := EStateM.Result.ok.inj restoreRun + try dsimp only + refine EStateM.bind_cases ?_ ?_ + · intro answer' s₄' pureRun + rw [pure_run] at pureRun + obtain ⟨answerEq, stateEq⟩ := EStateM.Result.ok.inj pureRun + subst answerEq stateEq + cases answer with + | false => + have valid₄ : CheckerInvariant.{u,v} resolve anchor entries source catalog + locals context bounds s₄ := by + subst restoreEq + exact innerPost.ofDefEqScalars rfl rfl rfl rfl + simp only [Bool.false_eq_true, ↓reduceIte] + split + · simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro value s₅ writeRun + rw [modify_run] at writeRun + obtain ⟨_, writeEq⟩ := EStateM.Result.ok.inj writeRun + try dsimp only + rw [pure_run] + subst writeEq + exact valid₄.ofDefEqUpdate ⟨_, _, rfl⟩ rfl rfl + (valid₄.semantics.defEq.ofInsert (lo, hi, eqCtx) false (.inl rfl) + (.inr rfl) (fun h => nomatch h)) + valid₄.semantics.equivalence + · intro err s h + rw [modify_run] at h + cases h + · simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro value s₅ writeRun + rw [modify_run] at writeRun + obtain ⟨_, writeEq⟩ := EStateM.Result.ok.inj writeRun + try dsimp only + rw [pure_run] + subst writeEq + exact valid₄.ofDefEqUpdate ⟨_, _, rfl⟩ rfl rfl + (valid₄.semantics.defEq.ofInsert (lo, hi, eqCtx) false (.inr rfl) + (.inl rfl) (fun h => nomatch h)) + valid₄.semantics.equivalence + · intro err s h + rw [modify_run] at h + cases h + | true => + obtain ⟨valid₃, claim⟩ := innerPost + have valid₄ : CheckerInvariant.{u,v} resolve anchor entries source catalog + locals context bounds s₄ := by + subst restoreEq + exact valid₃.ofDefEqScalars rfl rfl rfl rfl + simp only [↓reduceIte] + simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro value s₅ unionRun + rw [modify_run] at unionRun + obtain ⟨_, unionEq⟩ := EStateM.Result.ok.inj unionRun + have valid₅ : CheckerInvariant.{u,v} resolve anchor entries source catalog + locals context bounds s₅ := by + subst unionEq + exact valid₄.addEquiv (recordEdge claim) + try dsimp only + split + · simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro value s₆ writeRun + rw [modify_run] at writeRun + obtain ⟨_, writeEq⟩ := EStateM.Result.ok.inj writeRun + try dsimp only + rw [pure_run] + subst writeEq + exact ⟨valid₅.ofDefEqUpdate ⟨_, _, rfl⟩ rfl rfl + (valid₅.semantics.defEq.ofInsert (lo, hi, eqCtx) true (.inr rfl) + (.inr rfl) (fun _ => recordKey claim)) + valid₅.semantics.equivalence, claim⟩ + · intro err s h + rw [modify_run] at h + cases h + · simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro value s₆ writeRun + rw [modify_run] at writeRun + obtain ⟨_, writeEq⟩ := EStateM.Result.ok.inj writeRun + try dsimp only + rw [pure_run] + subst writeEq + exact ⟨valid₅.ofDefEqUpdate ⟨_, _, rfl⟩ rfl rfl + (valid₅.semantics.defEq.ofInsert (lo, hi, eqCtx) true (.inr rfl) + (.inl rfl) (fun _ => recordKey claim)) + valid₅.semantics.equivalence, claim⟩ + · intro err s h + rw [modify_run] at h + cases h + · intro err s h + rw [modify_run] at h + cases h + · intro err s h + rw [pure_run] at h + cases h + · intro err s h + rw [modify_run] at h + cases h + | error err s₃' => + rw [innerRun] at catchRun innerPost + dsimp only at catchRun + obtain ⟨resultEq, stateEq⟩ := EStateM.Result.ok.inj catchRun + subst resultEq stateEq + try dsimp only + simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro value s₄ restoreRun + rw [modify_run] at restoreRun + obtain ⟨_, restoreEq⟩ := EStateM.Result.ok.inj restoreRun + try dsimp only + refine EStateM.bind_cases ?_ ?_ + · intro answer s h + rw [throw_run] at h + cases h + · intro err' s h + rw [throw_run] at h + cases h + subst restoreEq + exact innerPost.ofDefEqScalars rfl rfl rfl rfl + · intro err' s h + rw [modify_run] at h + cases h + · intro err s h + rw [tryExcept_run] at h + cases innerRun : (RecM.isDefEqInner a b).run methods s₂ <;> rw [innerRun] at h <;> + dsimp only at h <;> cases h + · intro err s h + rw [get_run] at h + cases h + · intro err s h + rw [modify_run] at h + cases h + · intro err s₁ tickRun + have outcome := valid₀.tickOutcome + have tickRun' : TcM.tick s₀ = .error err s₁ := tickRun + rw [tickRun'] at outcome + exact outcome + · intro err s₀ statsRun + have statsRun' : TcM.bumpStats _ before = .error err s₀ := statsRun + rw [bumpStats_eq] at statsRun' + cases statsRun' + + +/-! ### The guarded representative probe -/ + +/-- After both direct partitions miss: representatives of both keys, the +scope guard, the root-derived cache probe in the full partition and, in cheap +mode, the cheap partition, the copy of a hit to the original key, and the +manager union after a positive hit. A hit composes the two representative +chains with the root edge into a chain between the original keys, which the +transport premise reads at the caller's registration. -/ +theorem isDefEqAfterDirectCacheMiss_sound {methods : Methods .anon} {before : TcState .anon} + {a b : KExpr .anon} {ta tb : AExpr β} {eqCtx lo hi : Address} {cheapMode : Bool} + (transport : DefEqMemoTransport.{u,v} resolve anchor entries source catalog) + (inner : ∀ state, + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds ta tb + ((RecM.isDefEqInner a b).run methods state)) + (origin : DefEqKeyOrigin.{u,v} resolve anchor entries source catalog locals context bounds a b eqCtx) + (canonical : canonicalPair a.addr b.addr = (lo, hi)) + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftReads : readScopedExpr? resolve locals a = some ta.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context ta type) + (rightReads : readScopedExpr? resolve locals b = some tb.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context tb type) : + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds ta tb + ((RecM.isDefEqAfterDirectCacheMiss a b eqCtx ⟨a.addr, eqCtx, max a.lbr b.lbr, a.lbr⟩ + ⟨b.addr, eqCtx, max a.lbr b.lbr, b.lbr⟩ (lo, hi, eqCtx) cheapMode).run methods before) := by + have tail : ∀ state, + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds ta tb + ((RecM.isDefEqAfterRootCacheMiss a b ⟨a.addr, eqCtx, max a.lbr b.lbr, a.lbr⟩ + ⟨b.addr, eqCtx, max a.lbr b.lbr, b.lbr⟩ (lo, hi, eqCtx) cheapMode).run methods state) := + fun _ valid' => isDefEqAfterRootCacheMiss_sound inner origin canonical valid' leftReads leftTyped + rightReads rightTyped + have recordKey : ConversionClaim.{u,v} entries context ta tb → + AddressConversion.{u,v} resolve entries lo hi eqCtx := + fun claim => (AddressConversion.ofClaim origin leftReads rightReads leftTyped rightTyped claim).canonical + canonical + have read : EqKeyChain.{u,v} resolve entries ⟨a.addr, eqCtx, max a.lbr b.lbr, a.lbr⟩ + ⟨b.addr, eqCtx, max a.lbr b.lbr, b.lbr⟩ → ConversionClaim.{u,v} entries context ta tb := + transport locals context bounds a b ta tb eqCtx origin leftReads leftTyped rightReads rightTyped + unfold RecM.isDefEqAfterDirectCacheMiss + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + refine EStateM.bind_cases ?_ ?_ + · rintro ⟨aRoot?, bRoot?⟩ s₁ rootsRun + have rootsRun' : TcM.withEquiv _ before = .ok (aRoot?, bRoot?) s₁ := rootsRun + rw [withEquiv_eq] at rootsRun' + obtain ⟨rootsEq, stateEq⟩ := EStateM.Result.ok.inj rootsRun' + have roots := valid.semantics.equivalence.findRootKeys ⟨a.addr, eqCtx, max a.lbr b.lbr, a.lbr⟩ + ⟨b.addr, eqCtx, max a.lbr b.lbr, b.lbr⟩ + have valid₁ : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds + s₁ := by + subst stateEq + exact valid.ofDefEqUpdate ⟨_, _, rfl⟩ rfl rfl + (valid.semantics.defEq.ofMaps fun partition => by cases partition <;> rfl) roots.1 + have leftChain : ∀ root, aRoot? = some root → + EqKeyChain.{u,v} resolve entries ⟨a.addr, eqCtx, max a.lbr b.lbr, a.lbr⟩ root := + fun root found => roots.2.1 root (by rw [rootsEq]; exact found) + have rightChain : ∀ root, bRoot? = some root → + EqKeyChain.{u,v} resolve entries ⟨b.addr, eqCtx, max a.lbr b.lbr, b.lbr⟩ root := + fun root found => roots.2.2 root (by rw [rootsEq]; exact found) + try dsimp only + cases aRoot? with + | none => exact tail s₁ valid₁ + | some aRoot => + cases bRoot? with + | none => exact tail s₁ valid₁ + | some bRoot => + try dsimp only + split + · split + · rename_i _ scope + rcases canonicalRoots : canonicalPair aRoot.exprAddr bRoot.exprAddr with ⟨rlo, rhi⟩ + try dsimp only + have rootChain : ∀ partition : DefEqCachePartition, + (partition.cache s₁)[(rlo, rhi, eqCtx)]? = some true → + EqKeyChain.{u,v} resolve entries ⟨a.addr, eqCtx, max a.lbr b.lbr, a.lbr⟩ + ⟨b.addr, eqCtx, max a.lbr b.lbr, b.lbr⟩ := fun partition hit => + .trans (leftChain aRoot rfl) (.trans + (.edge (valid₁.semantics.defEq.rootEdge partition scope canonicalRoots hit)) + (.symm (rightChain bRoot rfl))) + simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro current s₂ getRun + rw [get_run] at getRun + obtain ⟨currentEq, stateEq⟩ := EStateM.Result.ok.inj getRun + subst currentEq stateEq + try dsimp only + cases fullHit : s₁.env.defEqCache[(rlo, rhi, eqCtx)]? with + | some cached => + try dsimp only + simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro cached? s₃ pureRun + rw [pure_run] at pureRun + obtain ⟨cachedEq, stateEq⟩ := EStateM.Result.ok.inj pureRun + subst cachedEq stateEq + try dsimp only + cases cached with + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro value s₄ writeRun + rw [modify_run] at writeRun + obtain ⟨_, writeEq⟩ := EStateM.Result.ok.inj writeRun + try dsimp only + rw [pure_run] + subst writeEq + exact valid₁.ofDefEqUpdate ⟨_, _, rfl⟩ rfl rfl + (valid₁.semantics.defEq.ofInsert (lo, hi, eqCtx) false (.inr rfl) + (by split <;> first | exact .inr rfl | exact .inl rfl) + (fun h => nomatch h)) + valid₁.semantics.equivalence + · intro err s h + rw [modify_run] at h + cases h + | true => + have claim := read (rootChain .full fullHit) + simp only [Bool.false_eq_true, ↓reduceIte] + simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro value s₄ writeRun + rw [modify_run] at writeRun + obtain ⟨_, writeEq⟩ := EStateM.Result.ok.inj writeRun + have valid₄ : CheckerInvariant.{u,v} resolve anchor entries source catalog + locals context bounds s₄ := by + subst writeEq + exact valid₁.ofDefEqUpdate ⟨_, _, rfl⟩ rfl rfl + (valid₁.semantics.defEq.ofInsert (lo, hi, eqCtx) true (.inr rfl) + (by split <;> first | exact .inr rfl | exact .inl rfl) + (fun _ => recordKey claim)) + valid₁.semantics.equivalence + try dsimp only + refine EStateM.bind_cases ?_ ?_ + · intro value s₅ unionRun + rw [modify_run] at unionRun + obtain ⟨_, unionEq⟩ := EStateM.Result.ok.inj unionRun + try dsimp only + rw [pure_run] + subst unionEq + exact ⟨valid₄.addEquiv (rootChain .full fullHit), claim⟩ + · intro err s h + rw [modify_run] at h + cases h + · intro err s h + rw [modify_run] at h + cases h + · intro err s h + rw [pure_run] at h + cases h + | none => + try dsimp only + split + · simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro current s₃ getRun + rw [get_run] at getRun + obtain ⟨currentEq, stateEq⟩ := EStateM.Result.ok.inj getRun + subst currentEq stateEq + try dsimp only + cases cheapHit : s₁.env.defEqCheapCache[(rlo, rhi, eqCtx)]? with + | some cached => + try dsimp only + simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro cached? s₄ pureRun + rw [pure_run] at pureRun + obtain ⟨cachedEq, stateEq⟩ := EStateM.Result.ok.inj pureRun + subst cachedEq stateEq + try dsimp only + cases cached with + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro value s₅ writeRun + rw [modify_run] at writeRun + obtain ⟨_, writeEq⟩ := EStateM.Result.ok.inj writeRun + try dsimp only + rw [pure_run] + subst writeEq + exact valid₁.ofDefEqUpdate ⟨_, _, rfl⟩ rfl rfl + (valid₁.semantics.defEq.ofInsert (lo, hi, eqCtx) false (.inl rfl) + (.inr rfl) (fun h => nomatch h)) + valid₁.semantics.equivalence + · intro err s h + rw [modify_run] at h + cases h + | true => + have claim := read (rootChain .cheap cheapHit) + simp only [↓reduceIte] + simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro value s₅ writeRun + rw [modify_run] at writeRun + obtain ⟨_, writeEq⟩ := EStateM.Result.ok.inj writeRun + have valid₅ : CheckerInvariant.{u,v} resolve anchor entries source + catalog locals context bounds s₅ := by + subst writeEq + exact valid₁.ofDefEqUpdate ⟨_, _, rfl⟩ rfl rfl + (valid₁.semantics.defEq.ofInsert (lo, hi, eqCtx) true (.inr rfl) + (.inr rfl) (fun _ => recordKey claim)) + valid₁.semantics.equivalence + try dsimp only + refine EStateM.bind_cases ?_ ?_ + · intro value s₆ unionRun + rw [modify_run] at unionRun + obtain ⟨_, unionEq⟩ := EStateM.Result.ok.inj unionRun + have valid₆ : CheckerInvariant.{u,v} resolve anchor entries source + catalog locals context bounds s₆ := by + subst unionEq + exact valid₅.addEquiv (rootChain .cheap cheapHit) + try dsimp only + rw [pure_run] + exact ⟨valid₆, claim⟩ + · intro err s h + rw [modify_run] at h + cases h + · intro err s h + rw [modify_run] at h + cases h + · intro err s h + rw [pure_run] at h + cases h + | none => + try dsimp only + simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro cached? s₄ pureRun + rw [pure_run] at pureRun + obtain ⟨cachedEq, stateEq⟩ := EStateM.Result.ok.inj pureRun + subst cachedEq stateEq + try dsimp only + exact tail s₁ valid₁ + · intro err s h + rw [pure_run] at h + cases h + · intro err s h + rw [get_run] at h + cases h + · simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro cached? s₃ pureRun + rw [pure_run] at pureRun + obtain ⟨cachedEq, stateEq⟩ := EStateM.Result.ok.inj pureRun + subst cachedEq stateEq + try dsimp only + exact tail s₁ valid₁ + · intro err s h + rw [pure_run] at h + cases h + · intro err s h + rw [get_run] at h + cases h + · exact tail s₁ valid₁ + · exact tail s₁ valid₁ + · intro err s h + have h' : TcM.withEquiv _ before = .error err s := h + rw [withEquiv_eq] at h' + cases h' + + +end Charged + +/-! ### Direct cache hits at the entry -/ + +section EntryWrites + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +/-- Copying a full-partition answer into the cheap partition preserves the +invariant: a positive answer is already recorded at that key. -/ +theorem CheckerInvariant.copyToCheap {state : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) + (key : Address × Address × Address) (answer : Bool) + (hit : state.env.defEqCache[key]? = some answer) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {state with env := {state.env with defEqCheapCache := state.env.defEqCheapCache.insert key answer}} := + valid.ofDefEqUpdate ⟨_, _, rfl⟩ rfl rfl + (valid.semantics.defEq.ofInsert key answer (.inl rfl) (.inr rfl) fun positive => + valid.semantics.defEq .full key (positive ▸ hit)) + valid.semantics.equivalence + +/-- Promoting a positive cheap-partition answer to the full partition and the +manager preserves the invariant. -/ +theorem CheckerInvariant.promoteCheapHit {state : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) + (key : Address × Address × Address) (hit : state.env.defEqCheapCache[key]? = some true) + {left right : EqKey} (chain : EqKeyChain.{u,v} resolve entries left right) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {state with + env := {state.env with defEqCache := state.env.defEqCache.insert key true} + equivManager := state.equivManager.addEquiv left right} := + valid.ofDefEqUpdate ⟨_, _, rfl⟩ rfl rfl + (valid.semantics.defEq.ofInsert key true (.inr rfl) (.inl rfl) fun _ => + valid.semantics.defEq .cheap key hit) + (valid.semantics.equivalence.addEquiv chain) + +end EntryWrites + +/-! ### The seam and the assembled entry -/ + +section Seams + +variable {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (source : Ixon.Env) + (catalog : List (SourceCacheRequest source)) + +/-- What the direct tiers leave open. `inner` is the remaining recursive tail +after the quick structural probe, the obligation of the reducing tiers. +`transport` reads a certified chain of memo edges at the caller's +registration. `sameRawAnnotations` is the annotation discipline of the hash +path: readings of one raw term that the checker compares carry equal binder +annotations. `binders` are the binder-case premises. The last three are +boundaries of the set model rather than tier obligations: the model interprets +binder annotations, records memo entries at the registration where they were +proved, and supplies only whole-term typing to the contracts. -/ +structure DefEqSeamAssumptions (depth : Nat) : Prop where + inner : ∀ left right, SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqInnerAfterQuick left right).run (methodsN depth)) + transport : DefEqMemoTransport.{u,v} resolve anchor entries source catalog + sameRawAnnotations : ∀ (context : Model.Context β) (a b : AExpr β), a.erase = b.erase → + (∃ type, TypingClaim.{u,v} entries context a type) → + (∃ type, TypingClaim.{u,v} entries context b type) → a.annotations = b.annotations + binders : DefEqBinderAssumptions.{u,v} resolve anchor entries + +end Seams + +section Entry + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {support : KExpr .anon → Prop} + +/-- The recursive inner tiers: the quick structural probe, then the +remaining tail under its contract. -/ +theorem isDefEqInner_sound {methods : Methods .anon} + (recursive : DefEqContract.{u,v} resolve anchor entries source catalog methods) + (frames : MethodsLocalState methods) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqTierResources.{u,v} resolve anchor entries source catalog support) + (inner : ∀ left right, SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqInnerAfterQuick left right).run methods)) + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {before : TcState .anon} + {left right : KExpr .anon} {a b : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftMember : support left) (rightMember : support right) + (leftReads : readScopedExpr? resolve locals left = some a.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context a type) + (rightReads : readScopedExpr? resolve locals right = some b.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context b type) : + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b + ((RecM.isDefEqInner left right).run methods before) := by + have quick := quickDefEq_sound recursive frames binders resources valid leftMember rightMember + leftReads leftTyped rightReads rightTyped + unfold RecM.isDefEqInner + simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro answer middle quickRun + rw [quickRun] at quick + cases answer with + | true => + simp only [↓reduceIte] + rw [pure_run] + exact quick + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + exact inner left right locals context bounds middle a b quick leftReads leftTyped rightReads + rightTyped + · intro err middle quickRun + rw [quickRun] at quick + exact quick + +/-- The conversion entry under the contracts of its recursive callbacks: the +hash path, the equivalence-manager query, both direct cache partitions, the +representative probe, and the charged recursive tail through the quick +structural tier, with the deeper tiers abstracted by `inner`. -/ +theorem isDefEq_sound {methods : Methods .anon} + (recursive : DefEqContract.{u,v} resolve anchor entries source catalog methods) + (frames : MethodsLocalState methods) + (transport : DefEqMemoTransport.{u,v} resolve anchor entries source catalog) + (sameRawAnnotations : ∀ (context : Model.Context β) (a b : AExpr β), a.erase = b.erase → + (∃ type, TypingClaim.{u,v} entries context a type) → + (∃ type, TypingClaim.{u,v} entries context b type) → a.annotations = b.annotations) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqTierResources.{u,v} resolve anchor entries source catalog support) + (inner : ∀ left right, SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqInnerAfterQuick left right).run methods)) + (left right : KExpr .anon) (leftMember : support left) (rightMember : support right) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEq left right).run methods) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + have innerPost : ∀ state, + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b + ((RecM.isDefEqInner left right).run methods state) := + fun _ valid' => isDefEqInner_sound recursive frames binders resources inner valid' leftMember + rightMember leftReads leftTyped rightReads rightTyped + unfold RecM.isDefEq + simp only [ReaderT.run_bind, ReaderT.run_monadLift] + refine EStateM.bind_cases ?_ ?_ + · intro value s₀ traceRun + have traceRun' : TcM.stepTrace _ _ before = .ok value s₀ := traceRun + rw [stepTrace_eq] at traceRun' + obtain ⟨_, stateEq⟩ := EStateM.Result.ok.inj traceRun' + subst stateEq + try dsimp only + refine EStateM.bind_cases ?_ ?_ + · intro value s₁ statsRun + cases value + have valid₁ := valid.afterBumpStats (fun s => {s with deqCalls := s.deqCalls + 1}) + (fun _ => rfl) (fun _ => rfl) (fun _ => rfl) (fun _ => rfl) statsRun + try dsimp only + split + · rename_i equal + rw [pure_run] + have same := beq_readScopedExpr? (resolve := resolve) (locals := locals) (depth := 0) + (resources.faithful left right leftMember rightMember) (by rw [KExpr.beq_def]; exact equal) + have erased : a.erase = b.erase := + Option.some.inj (leftReads.symm.trans (same.trans rightReads)) + cases AExpr.eq_of_erase_annotations erased + (sameRawAnnotations context a b erased leftTyped rightTyped) + exact ⟨valid₁, ConversionClaim.refl a⟩ + · simp only [ReaderT.run_bind, ReaderT.run_monadLift] + refine EStateM.bind_cases ?_ ?_ + · intro eqCtx s₂ keyRun + have keyRun' : TcM.defEqCtxKey left right s₁ = .ok eqCtx s₂ := keyRun + have valid₂ := valid₁.defEqCtxKey keyRun' + have origin := DefEqKeyOrigin.ofRun valid₁ keyRun' + have read : EqKeyChain.{u,v} resolve entries ⟨left.addr, eqCtx, max left.lbr right.lbr, left.lbr⟩ + ⟨right.addr, eqCtx, max left.lbr right.lbr, right.lbr⟩ → + ConversionClaim.{u,v} entries context a b := + transport locals context bounds left right a b eqCtx origin leftReads leftTyped rightReads + rightTyped + try dsimp only + refine EStateM.bind_cases ?_ ?_ + · intro isEq s₃ queryRun + have queryRun' : TcM.withEquiv _ s₂ = .ok isEq s₃ := queryRun + rw [withEquiv_eq] at queryRun' + obtain ⟨isEqEq, stateEq⟩ := EStateM.Result.ok.inj queryRun' + have query := valid₂.semantics.equivalence.isEquiv + ⟨left.addr, eqCtx, max left.lbr right.lbr, left.lbr⟩ + ⟨right.addr, eqCtx, max left.lbr right.lbr, right.lbr⟩ + have valid₃ : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context + bounds s₃ := by + subst stateEq + exact valid₂.ofDefEqUpdate ⟨_, _, rfl⟩ rfl rfl (valid₂.semantics.defEq.setManager _) + query.1 + try dsimp only + cases isEq with + | true => + simp only [↓reduceIte] + rw [pure_run] + exact ⟨valid₃, read (query.2 isEqEq)⟩ + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + rcases canonical : canonicalPair left.addr right.addr with ⟨lo, hi⟩ + try dsimp only + simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro current s₃' getRun + rw [get_run] at getRun + obtain ⟨currentEq, stateEq⟩ := EStateM.Result.ok.inj getRun + subst currentEq stateEq + try dsimp only + refine EStateM.bind_cases ?_ ?_ + · intro current s₃' getRun + rw [get_run] at getRun + obtain ⟨currentEq, stateEq⟩ := EStateM.Result.ok.inj getRun + subst currentEq stateEq + try dsimp only + cases fullHit : s₃.env.defEqCache[(lo, hi, eqCtx)]? with + | some cached => + have hitChain : cached = true → + EqKeyChain.{u,v} resolve entries + ⟨left.addr, eqCtx, max left.lbr right.lbr, left.lbr⟩ + ⟨right.addr, eqCtx, max left.lbr right.lbr, right.lbr⟩ := + fun positive => .edge (valid₃.semantics.defEq.hitEdge .full canonical + (positive ▸ fullHit)) + try dsimp only + split + · simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro value s₄ copyRun + rw [modify_run] at copyRun + obtain ⟨_, stateEq⟩ := EStateM.Result.ok.inj copyRun + subst stateEq + have valid₄ := valid₃.copyToCheap (lo, hi, eqCtx) cached fullHit + try dsimp only + cases cached with + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + rw [pure_run] + exact valid₄ + | true => + simp only [↓reduceIte] + simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro value s₅ unionRun + rw [modify_run] at unionRun + obtain ⟨_, stateEq⟩ := EStateM.Result.ok.inj unionRun + subst stateEq + try dsimp only + rw [pure_run] + exact ⟨valid₄.addEquiv (hitChain rfl), read (hitChain rfl)⟩ + · intro err s h + rw [modify_run] at h + cases h + · intro err s h + rw [modify_run] at h + cases h + · cases cached with + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + rw [pure_run] + exact valid₃ + | true => + simp only [↓reduceIte] + simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro value s₄ unionRun + rw [modify_run] at unionRun + obtain ⟨_, stateEq⟩ := EStateM.Result.ok.inj unionRun + subst stateEq + try dsimp only + rw [pure_run] + exact ⟨valid₃.addEquiv (hitChain rfl), read (hitChain rfl)⟩ + · intro err s h + rw [modify_run] at h + cases h + | none => + try dsimp only + split + · simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro current s₃' getRun + rw [get_run] at getRun + obtain ⟨currentEq, stateEq⟩ := EStateM.Result.ok.inj getRun + subst currentEq stateEq + try dsimp only + cases cheapHit : s₃.env.defEqCheapCache[(lo, hi, eqCtx)]? with + | some cached => + try dsimp only + cases cached with + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + rw [pure_run] + exact valid₃ + | true => + have chain : EqKeyChain.{u,v} resolve entries + ⟨left.addr, eqCtx, max left.lbr right.lbr, left.lbr⟩ + ⟨right.addr, eqCtx, max left.lbr right.lbr, right.lbr⟩ := + .edge (valid₃.semantics.defEq.hitEdge .cheap canonical cheapHit) + simp only [↓reduceIte] + simp only [ReaderT.run_bind] + refine EStateM.bind_cases ?_ ?_ + · intro value s₄ promoteRun + rw [modify_run] at promoteRun + obtain ⟨_, stateEq⟩ := EStateM.Result.ok.inj promoteRun + subst stateEq + try dsimp only + rw [pure_run] + exact ⟨valid₃.promoteCheapHit (lo, hi, eqCtx) cheapHit chain, + read chain⟩ + · intro err s h + rw [modify_run] at h + cases h + | none => + try dsimp only + exact isDefEqAfterDirectCacheMiss_sound transport innerPost origin + canonical valid₃ leftReads leftTyped rightReads rightTyped + · intro err s h + rw [get_run] at h + cases h + · exact isDefEqAfterDirectCacheMiss_sound transport innerPost origin canonical + valid₃ leftReads leftTyped rightReads rightTyped + · intro err s h + rw [get_run] at h + cases h + · intro err s h + rw [get_run] at h + cases h + · intro err s h + have h' : TcM.withEquiv _ s₂ = .error err s := h + rw [withEquiv_eq] at h' + cases h' + · intro err s h + have h' : TcM.ctxAddrForLbr (max left.lbr right.lbr) s₁ = .error err s := h + obtain ⟨addr, after, ok⟩ := ctxAddrForLbr_ok (max left.lbr right.lbr) s₁ + rw [ok] at h' + cases h' + · intro err s h + have h' : TcM.bumpStats _ before = .error err s := h + rw [bumpStats_eq] at h' + cases h' + · intro err s h + have h' : TcM.stepTrace _ _ before = .error err s := h + rw [stepTrace_eq] at h' + cases h' + +/-- The `StepContracts.isDefEq` field modulo the seam: every operand pair in +the resource support, at the production table of the recursive callbacks. -/ +theorem isDefEq_direct_tiers {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqSeamAssumptions.{u,v} resolve anchor entries source catalog depth) + (resources : DefEqTierResources.{u,v} resolve anchor entries source catalog support) + (left right : KExpr .anon) (leftMember : support left) (rightMember : support right) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEq left right).run (methodsN depth)) := + isDefEq_sound recursive.toDefEqContract (MethodsLocalState.methodsN depth) seams.transport + seams.sameRawAnnotations seams.binders resources seams.inner left right leftMember rightMember + +/-- The exact field shape, with the resources stated for every expression. -/ +theorem StepContracts.isDefEq_of_direct_tiers {depth : Nat} + (seams : DefEqSeamAssumptions.{u,v} resolve anchor entries source catalog depth) + (resources : DefEqTierResources.{u,v} resolve anchor entries source catalog fun _ => True) : + MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth) → + ∀ left right, SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEq left right).run (methodsN depth)) := + fun recursive left right => isDefEq_direct_tiers recursive seams resources left right trivial trivial + +end Entry + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/Invariant.lean b/Ix/Kernel/Verify/Consistency/Invariant.lean index 4e64ae2e6..251c24802 100644 --- a/Ix/Kernel/Verify/Consistency/Invariant.lean +++ b/Ix/Kernel/Verify/Consistency/Invariant.lean @@ -4,6 +4,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 -/ import Ix.Kernel.Verify.IngressState +import Ix.Kernel.Verify.EquivalenceManager import Ix.Kernel.Verify.Consistency.RunAssumptions import Ix.Kernel.Verify.Consistency.SourceCache import Ix.Kernel.Verify.Consistency.SynthesisCacheHistory @@ -83,19 +84,34 @@ def DefEqCachePartition.cache : DefEqCachePartition → TcState .anon → | .full, state => state.env.defEqCache | .cheap, state => state.env.defEqCheapCache -/-- Every positive DefEq entry records two expressions with the key's -addresses whose readings are convertible in some local context. Negative -entries carry no claim. -/ +/-- A recorded conversion between two expression addresses at a context +digest. Some structurally valid state registers a local context in which +expressions with the two addresses read to typed annotated terms that are +convertible there, and the digest is that state's actual context address at +the pair's joint radius. The state, registration, sources, and annotated +readings are existential, so a consumer at another registration needs an +explicit transport premise: the conversion instance of the context-digest +boundary of `docs/tc-context-digest-collision-boundary.md`. -/ +def AddressConversion {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (left right ctxAddr : Address) : Prop := + ∃ (state : TcState .anon) (locals : List FVarId) (context : Model.Context β) + (a b : KExpr .anon) (ta tb : AExpr β), + a.addr = left ∧ b.addr = right ∧ + LocalStateInvariant state ∧ LocalContextReading resolve locals state.lctx context ∧ + (∃ after, TcM.ctxAddrForLbr (max a.lbr b.lbr) state = .ok ctxAddr after) ∧ + readScopedExpr? resolve locals a = some ta.erase ∧ + readScopedExpr? resolve locals b = some tb.erase ∧ + (∃ type, TypingClaim.{u,v} entries context ta type) ∧ + (∃ type, TypingClaim.{u,v} entries context tb type) ∧ + ConversionClaim.{u,v} entries context ta tb + +/-- Every positive DefEq entry records a conversion between its two addresses +at its context digest. Negative entries carry no claim. -/ def DefEqCacheSemantics {β : Type u} (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) (state : TcState .anon) : Prop := ∀ (partition : DefEqCachePartition) (key : Address × Address × Address), (partition.cache state)[key]? = some true → - ∃ (locals : List FVarId) (context : Model.Context β) (left right : KExpr .anon) - (a b : AExpr β), - left.addr = key.1 ∧ right.addr = key.2.1 ∧ - readScopedExpr? resolve locals left = some a.erase ∧ - readScopedExpr? resolve locals right = some b.erase ∧ - ConversionClaim.{u,v} entries context a b + AddressConversion.{u,v} resolve entries key.1 key.2.1 key.2.2 theorem DefEqCacheSemantics.ofMaps {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {before after : TcState .anon} @@ -114,29 +130,43 @@ theorem DefEqCacheSemantics.ofEmpty {β : Type u} (resolve : Address → Option rw [empty partition] at stored simp at stored -/-- A justified union-find edge joins two keys of the same context scope whose -expressions read to convertible terms in some local context. -/ +/-- A justified union-find edge joins two keys of the same context scope +through a recorded conversion of their expression addresses at that scope's +digest. -/ def EqKeyConversion {β : Type u} (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) (left right : EqKey) : Prop := left.ctxAddr = right.ctxAddr ∧ left.lbr = right.lbr ∧ - ∃ (locals : List FVarId) (context : Model.Context β) (a b : KExpr .anon) (ta tb : AExpr β), - a.addr = left.exprAddr ∧ b.addr = right.exprAddr ∧ - a.lbr = left.exprLbr ∧ b.lbr = right.exprLbr ∧ - readScopedExpr? resolve locals a = some ta.erase ∧ - readScopedExpr? resolve locals b = some tb.erase ∧ - ConversionClaim.{u,v} entries context ta tb - -/-- Every parent link of the production union-find forest is a justified edge. -This is the `edge` field of the named track's `EquivManager.WF`, restated here -so the library does not import that module. -/ + AddressConversion.{u,v} resolve entries left.exprAddr right.exprAddr left.ctxAddr + +/-- What a positive manager answer certifies: the two keys are joined by a +chain of justified edges. This is the equivalence relation the production +union-find represents; path halving and union compose edges along it. -/ +inductive EqKeyChain {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) : EqKey → EqKey → Prop + | refl (key : EqKey) : EqKeyChain resolve entries key key + | edge {left right : EqKey} (justified : EqKeyConversion.{u,v} resolve entries left right) : + EqKeyChain resolve entries left right + | symm {left right : EqKey} (chain : EqKeyChain resolve entries left right) : + EqKeyChain resolve entries right left + | trans {left middle right : EqKey} (first : EqKeyChain resolve entries left middle) + (second : EqKeyChain resolve entries middle right) : EqKeyChain resolve entries left right + +theorem EqKeyChain.equivalence {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) : Equivalence (EqKeyChain.{u,v} resolve entries) := + ⟨.refl, .symm, .trans⟩ + +/-- The production union-find forest represents the chain relation: every +parent link is a chain of justified edges, parents are in bounds, and every +key lookup resolves to an in-bounds node carrying that key. This is +`EquivManager.WF` at the chain relation, so the verified union-find +operations apply. -/ def EquivManagerSemantics {β : Type u} (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) (manager : EquivManager) : Prop := - ∀ node, node < manager.parent.size → - EqKeyConversion.{u,v} resolve entries manager.nodeToKey[node]! manager.nodeToKey[manager.parent[node]!]! + EquivManager.WF (EqKeyChain.{u,v} resolve entries) manager theorem EquivManagerSemantics.empty {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} : EquivManagerSemantics.{u,v} resolve entries {} := - fun _ bound => (Nat.not_lt_zero _ bound).elim + EquivManager.WF.empty /-- Every unfold entry is the universe instantiation of an admitted body at the head constant whose address keys it. -/ diff --git a/Ix/Kernel/Verify/EquivalenceManager.lean b/Ix/Kernel/Verify/EquivalenceManager.lean index 0cadc80b9..06a75ced4 100644 --- a/Ix/Kernel/Verify/EquivalenceManager.lean +++ b/Ix/Kernel/Verify/EquivalenceManager.lean @@ -60,7 +60,7 @@ private theorem Array.getElemBang_setBang private theorem Array.getElemBang_push {α : Type} [Inhabited α] (xs : Array α) (v : α) (i : Nat) (hi : i < (xs.push v).size) : - (xs.push v)[i]! = if h : i < xs.size then xs[i]! else v := by + (xs.push v)[i]! = if _ : i < xs.size then xs[i]! else v := by by_cases h : i < xs.size · simp [getElem!_def, Array.getElem?_push, h, Nat.ne_of_lt h] · have hieq : i = xs.size := by @@ -170,7 +170,7 @@ theorem findGo have hgp : gp < parent.size := h.parent_lt hp have hparent' : ParentSound R labels parent' := h.halve hR hnode have hsize : parent'.size = parent.size := by - simp [parent', Array.size_set!] + simp [parent'] have hgp' : gp < parent'.size := by simpa [hsize] have hread : parent'[node]! = gp := by rw [Array.getElemBang_setBang parent node node gp hnode hnode] @@ -313,7 +313,7 @@ theorem nodeForKey {R : EqKey → EqKey → Prop} (hR : Equivalence R) rw [Array.getElemBang_push em.parent node i (by simpa using hi)] split · exact Nat.lt_succ_of_lt (h.parents.parent_lt ‹_›) - · simpa [node] using hi + · simp [node] at hi ⊢ · intro i hi simp only [em', Array.size_push] at hi change R (em.nodeToKey.push key)[i]! @@ -342,7 +342,7 @@ theorem nodeForKey {R : EqKey → EqKey → Prop} (hR : Equivalence R) simp only [node] at hiOld ⊢ omega subst i - simp [em', node, hlabelSize, hR.refl] + simp [node, hlabelSize, hR.refl] refine ⟨⟨hparents, ?_⟩, ?_, ?_⟩ · intro other otherNode hlookup rw [Std.HashMap.getElem?_insert] at hlookup @@ -352,7 +352,7 @@ theorem nodeForKey {R : EqKey → EqKey → Prop} (hR : Equivalence R) subst other cases hlookup constructor - · simp [em', node] + · simp · have hlast := Array.getElemBang_push em.nodeToKey key em.parent.size (by simp [hlabelSize]) have hnot : ¬em.parent.size < em.nodeToKey.size := by @@ -410,10 +410,10 @@ theorem union {R : EqKey → EqKey → Prop} (hR : Equivalence R) have hroots : R em2.nodeToKey[ra]! em2.nodeToKey[rb]! := by rw [hlabels2, hlabels1] exact hR.trans (hR.symm hra) (hR.trans hab hrb) - simp only [hfa, hfb] + simp only [hfb] split · simpa using hfindB.1 - · simp only [Id.run, pure_bind] + · simp only [Id.run] have hraBound : ra < em2.parent.size := by rw [← hfindB.1.parents.size_eq, hlabels2, hfindA.1.parents.size_eq] @@ -442,12 +442,12 @@ theorem isEquiv {R : EqKey → EqKey → Prop} (hR : Equivalence R) exact hR.refl _ · next hne => cases hmap1 : em.keyToNode[k1]? with - | none => simp [hmap1, h] + | none => simp [h] | some n1 => cases hmap2 : em.keyToNode[k2]? with - | none => simp [hmap1, hmap2, h] + | none => simp [h] | some n2 => - simp only [hmap1, hmap2] + simp only have hn1 := h.keyToNode hmap1 have hn2 := h.keyToNode hmap2 have hfind1 := h.find hR hn1.1 @@ -464,7 +464,7 @@ theorem isEquiv {R : EqKey → EqKey → Prop} (hR : Equivalence R) have hfind2 := hfind1.1.find hR hn2' rcases hf2 : em1.find n2 with ⟨r2, em2⟩ rw [hf2] at hfind2 - simp only [hf1, hf2] + simp only refine ⟨hfind2.1, fun hroots => ?_⟩ have hr : r1 = r2 := eq_of_beq hroots have hk1 : R k1 em.nodeToKey[r1]! := by @@ -495,7 +495,7 @@ theorem findRootKey {R : EqKey → EqKey → Prop} (hR : Equivalence R) have hfind := h.find hR hnode.1 rcases hf : em.find node with ⟨root, em1⟩ rw [hf] at hfind - simp only [hf] + simp only refine ⟨hfind.1, ?_⟩ intro rootKey hroot have hrootBound : root < em1.nodeToKey.size := by @@ -526,7 +526,7 @@ theorem findRootKeys {R : EqKey → EqKey → Prop} (hR : Equivalence R) have hright := hleft.1.findRootKey hR right rcases hrightRun : em1.findRootKey right with ⟨rightRoot, em2⟩ rw [hrightRun] at hright - simp only [hleftRun, hrightRun] + simp only [hrightRun] exact ⟨hright.1, hleft.2, hright.2⟩ /-- Recording one already-justified equivalence preserves manager validity. -/ From df8640a7d16203838cb60e63fa4a30721a0f2aa6 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 15 Sep 2026 12:36:41 -0400 Subject: [PATCH 60/63] Prove WHNF cache layers, loops, and delta under the contracts Add Consistency/CheckedTyping (hereditary typing with canonical atom types: projects to HereditaryTyping and typed lambda spines; closed under context insertion, dependent substitution, spine head rewriting, beta prefixes, and constant unfolding), Consistency/WhnfGeneric (GenericReduction: scope-generic reduction soundness; ReductionInvariant: CheckerInvariant minus the beta-only WHNF history plus noAccel; GenericSoundReduction/SoundStep/SoundReducer/ GenericWhnfContract; memo hit/insert/publish for the five WHNF maps and the unfold map; runBounded_sound over any loop state with a term view; the accelerated probes are absent under noAccel), Consistency/WhnfLayers (PreservesInvariant combinators, isTransientNatLiteralWork through lazy lookup, the miss charge, whnfCoreWithFlagsNonLeaf_sound, whnfNoDeltaImplNonLeaf_sound, whnfWithNatSuccModeNonLeaf_sound), and Consistency/WhnfSteps (DefinitionBinding, WhnfSeamAssumptions, the unfold memo and tryDeltaUnfold/deltaUnfoldOne as sound reducers, the structural step with leaves, loose variables, local and explicit lets, multi-argument beta and head rebuilding after the recursive head call, the no-delta reducer tail and step, the full step with the seen set and delta, and the assembly whnfCoreWithFlags_sound/whnfNoDeltaImpl_sound/whnfWithNatSuccMode_sound/ whnf_sound/whnfCore_sound with GenericWhnfContract.succ/methodsN). Invariant.lean: WhnfCacheSemantics is restated over GenericReduction with CheckedTyping, since a single-scope semantic entry cannot serve an address-keyed hit in another scope and semantic typing admits proof-regime applications whose beta reduct is not a conversion; UnfoldCacheSemantics records a closed reading up to universe level equivalence, which is what the instantiation walker provides. Remaining seam hypotheses (WhnfSeamAssumptions): lookups, hits, wellFormed, bodies (checked typing of instantiated admitted bodies), definitions (loaded definition bindings), unfoldFaithful, instantiation, chains, betaResources, letValue (local let-value agreement), letResources, projection, iota, projApp, nat, string, projectionDefinition, quot, natOffsetStuck. --- Ix/Kernel/Verify/Consistency.lean | 11 + Ix/Kernel/Verify/Consistency/Audit.lean | 83 ++ .../Verify/Consistency/CheckedTyping.lean | 415 ++++++ Ix/Kernel/Verify/Consistency/Invariant.lean | 40 +- Ix/Kernel/Verify/Consistency/WhnfGeneric.lean | 699 +++++++++ Ix/Kernel/Verify/Consistency/WhnfLayers.lean | 669 +++++++++ Ix/Kernel/Verify/Consistency/WhnfSteps.lean | 1292 +++++++++++++++++ 7 files changed, 3196 insertions(+), 13 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/CheckedTyping.lean create mode 100644 Ix/Kernel/Verify/Consistency/WhnfGeneric.lean create mode 100644 Ix/Kernel/Verify/Consistency/WhnfLayers.lean create mode 100644 Ix/Kernel/Verify/Consistency/WhnfSteps.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index e35eac712..e4db968e1 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -90,6 +90,10 @@ import Ix.Kernel.Verify.Consistency.Contracts import Ix.Kernel.Verify.Consistency.DefEqMemo import Ix.Kernel.Verify.Consistency.DefEqQuick import Ix.Kernel.Verify.Consistency.DefEqTiers +import Ix.Kernel.Verify.Consistency.CheckedTyping +import Ix.Kernel.Verify.Consistency.WhnfGeneric +import Ix.Kernel.Verify.Consistency.WhnfLayers +import Ix.Kernel.Verify.Consistency.WhnfSteps import Ix.Kernel.Verify.Consistency.Audit /-! @@ -376,5 +380,12 @@ structural tier composes universe equality and the common-local binder comparison; the entry is assembled modulo the reducing tiers, the transport of recorded chains to the caller's registration, the binder annotation discipline, and hereditary typing of binder operands. +The WHNF bodies are proved against scope-generic reduction contracts over a +history-free reduction invariant with a checked (hereditary, canonical-atom) +typing premise: the five memo layers, the bounded loops, leaves, explicit and +local lets, multi-argument beta and head rebuilding after the recursive head +call, and delta unfolding through the unfold memo are closed by induction on +the method-table depth, and the projection, iota, literal, and quotient +reducers remain seams collected in one assumption record per depth. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index c6fcb0e96..78356bd5c 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -22,6 +22,7 @@ import Ix.Kernel.Verify.Consistency.BetaHistoryInference import Ix.Kernel.Verify.Consistency.Invariant import Ix.Kernel.Verify.Consistency.Contracts import Ix.Kernel.Verify.Consistency.DefEqTiers +import Ix.Kernel.Verify.Consistency.WhnfSteps import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -1649,6 +1650,75 @@ private def wp3AxiomFreeRoots : Array Lean.Name := #[ ``readScopedExpr?_all_annotated ] +/-- WP4a: checked typing (hereditary typing with canonical atoms), the +scope-generic reduction contracts over the history-free reduction invariant, +the five WHNF memo layers, the bounded loops, and the WHNF step cases with +delta, assembled per method-table depth under `WhnfSeamAssumptions`. -/ +private def wp4PureRoots : Array Lean.Name := #[ + ``Ix.Kernel.Consistency.AExpr.appN_append, ``DefinitionBinding +] + +private def wp4PropextRoots : Array Lean.Name := #[``constant_reading] + +private def wp4QuotRoots : Array Lean.Name := #[ + ``Ix.Kernel.Consistency.AExpr.liftN_of_scope, ``Ix.Kernel.Consistency.AExpr.inst_of_scope, + ``Ix.Kernel.Consistency.AExpr.liftN_instL_of_scope, ``Ix.Kernel.Consistency.AExpr.inst_instL_of_scope, + ``betaWhnfBump, ``betaWhnfBump_run, ``tick_run, ``tryGetConst_loaded, ``UnfoldCacheSemantics.hit, + ``whnfWithNatSuccModeMissCharge_run, ``lambda_reading, ``runIntern_bind +] + +private def wp4StandardRoots : Array Lean.Name := #[ + ``CheckedTyping, ``CheckedTyping.typing, ``CheckedTyping.toHereditary, ``CheckedTyping.lambdaSpine, + ``CheckedTyping.weakenAt, ``CheckedTyping.liftValue, ``CheckedTyping.substituteAt, + ``CheckedTyping.lamType, ``CheckedTyping.appHead, ``CheckedTyping.headTyped, + ``CheckedTyping.rewriteApp, ``CheckedTyping.rewriteHead, ``CheckedTyping.betaStep, + ``CheckedTyping.betaPrefix, ``CheckedTyping.unfoldConst, ``CheckedTyping.constArity, + ``GenericReduction, ``GenericReduction.refl, ``GenericReduction.trans, ``GenericReduction.typing, + ``WhnfCacheSemantics.hit, ``WhnfCacheSemantics.insert, ``UnfoldCacheSemantics.insert, + ``levelEquivalent_conversion, ``constant_spine_arity, ``spine_arity_of_reading +] + +private def wp4ExpressionRoots : Array Lean.Name := #[ + ``internLoop_eq_foldlM, ``internLoop_run, ``tryReduceBitvec_noAccel, ``BetaWalkerResources, + ``consumeBetaLamsFuel_size, ``consumeBetaLams_lam_nonempty, ``finishAppResult_run +] + +private def wp4ProductionRoots : Array Lean.Name := #[ + ``ReductionInvariant, ``ReductionInvariant.ofChecker, ``ReductionInvariant.sourceState, + ``ReductionInvariant.inference, ``ReductionInvariant.coherent, ``ReductionInvariant.installed, + ``ReductionInvariant.owned, ``ReductionInvariant.cache, ``ReductionInvariant.ofMaps, + ``ReductionInvariant.ofIntern, ``ReductionInvariant.ofCtxAddrCache, ``ReductionInvariant.whnfKey, + ``ReductionInvariant.instrument, ``ReductionInvariant.charge, ``ReductionInvariant.getConst, + ``ReductionInvariant.tryGetConst, ``ReductionInvariant.bump, ``ReductionInvariant.tick, + ``ReductionInvariant.publishWhnf, ``ReductionInvariant.publishCore, + ``ReductionInvariant.publishCoreCheap, ``ReductionInvariant.publishNoDelta, + ``ReductionInvariant.publishNoDeltaCheap, ``ReductionInvariant.publishFull, + ``ReductionInvariant.publishUnfold, + ``ReductionOutcome, ``GenericSoundReduction, ``StepOutcome, ``SoundStep, ``ReducerOutcome, + ``SoundReducer, ``GenericWhnfContract, ``GenericSoundReduction.throw, ``GenericSoundReduction.pure, + ``GenericWhnfContract.zero, ``ReductionOutcome.scoped, ``runBounded_sound, + ``tryReduceNative_noAccel, ``tryReduceDecidable_noAccel, + ``LookupData, ``WhnfHitData, ``PreservesInvariant, ``PreservesInvariant.pure, + ``PreservesInvariant.throw, ``PreservesInvariant.bind, ``PreservesInvariant.get, + ``PreservesInvariant.tryGetConst, ``PreservesInvariant.getConst, ``PreservesInvariant.of_run, + ``PreservesInvariant.prims, ``PreservesInvariant.runPure, + ``isNatLiteralRecursorApp_preserves, ``isTransientNatLiteralWork_preserves, + ``ReductionOutcome.bind, ``ReductionOutcome.pure, + ``whnfCoreWithFlagsNonLeaf_sound, ``whnfNoDeltaImplNonLeaf_sound, + ``whnfWithNatSuccModeMissCharge_preserves, ``whnfWithNatSuccModeNonLeaf_sound, + ``WhnfSeamAssumptions, ``ReducerOutcome.absent, ``ReducerOutcome.found, + ``ReducerOutcome.bindPreserving, ``ReducerOutcome.bindProbe, + ``unfoldConstValue_ok, ``unfoldConstValue_error, ``unfoldConstValue_preserves, + ``unfolded_spine_reduction, ``unfolded_constant_reduction, + ``tryDeltaUnfold_sound, ``deltaUnfoldOne_sound, + ``StepOutcome.bindProbe, ``StepOutcome.bindReduction, ``StepOutcome.transport, + ``beta_prefix_reduction, ``rebuilt_spine_reduction, ``whnfCoreWithFlagsStep_app_sound, + ``whnfCoreWithFlagsStep_sound, ``whnfNoDeltaReducersStep_sound, ``whnfNoDeltaImplStep_sound, + ``whnfWithNatSuccModeStep_sound, ``whnfCoreWithFlags_sound, ``whnfCore_sound, + ``whnfNoDeltaImpl_sound, ``whnfWithNatSuccMode_sound, ``whnf_sound, + ``GenericWhnfContract.succ, ``GenericWhnfContract.methodsN +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -1910,6 +1980,19 @@ def roots : Array RootAllowance := #[ ++ wp3AxiomFreeRoots.map (fun root => { root, forbiddenDependencies := forbiddenProduction }) ++ #[{ root := ``readScopedExpr?_const_annotated, standardAxioms := #[``propext], forbiddenDependencies := forbiddenProduction }] + ++ wp4PureRoots.map (fun root => { root, forbiddenDependencies := forbiddenProduction }) + ++ wp4PropextRoots.map (fun root => { + root, standardAxioms := #[``propext], forbiddenDependencies := forbiddenProduction }) + ++ wp4QuotRoots.map (fun root => { + root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction }) + ++ wp4StandardRoots.map (fun root => { + root, standardAxioms := standard, forbiddenDependencies := forbiddenProduction }) + ++ wp4ExpressionRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative], + forbiddenDependencies := forbiddenProduction }) + ++ wp4ProductionRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], + forbiddenDependencies := forbiddenProduction }) run_cmd Kernel.Verify.Audit.check roots diff --git a/Ix/Kernel/Verify/Consistency/CheckedTyping.lean b/Ix/Kernel/Verify/Consistency/CheckedTyping.lean new file mode 100644 index 000000000..e56107fc6 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/CheckedTyping.lean @@ -0,0 +1,415 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.SynthesisMeaning + +/-! +# Checked typing: the reduction premise + +Reduction soundness cannot be stated over plain semantic typing. In the +proof regime a lambda denotes the canonical proof point, so +`TypingClaim` accepts an application `(fun h : True => h) 5` whose beta +reduct `5` is not convertible to it; the model's `ConversionClaim.beta` needs +the argument typed at the lambda's own domain. `HereditaryTyping` retains +those checks, but its atom rule types a constant at any semantic type, so +delta unfolding cannot transport a derivation to the unfolded body. + +`CheckedTyping` is hereditary typing whose atoms carry their canonical types: +a sort at its successor, a constant at its declared instantiated type, a +natural literal at the primitive type, and a projection at a semantic type. +It projects to `HereditaryTyping`, hence to typed lambda spines and semantic +typing, and it is closed under context insertion, dependent substitution, +rewriting the head of an application spine at every type, beta prefixes, +and replacing a constant by any term checked at its declared type. These are +the closure properties the WHNF step cases use; each is proved here once. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-! ### Closed annotated terms -/ + +theorem AExpr.liftN_of_scope {β : Type u} : ∀ {e : AExpr β} {n depth : Nat}, + e.Scope n depth → ∀ (count cutoff : Nat), depth ≤ cutoff → e.liftN count cutoff = e + | .bvar i, _, depth, scope, count, cutoff, below => by + simp only [AExpr.Scope] at scope + simp [AExpr.liftN, liftVar, show i < cutoff by omega] + | .sort _, _, _, _, _, _, _ => rfl + | .const _ _, _, _, _, _, _, _ => rfl + | .natLit _, _, _, _, _, _, _ => rfl + | .app f a, n, depth, scope, count, cutoff, below => by + simp only [AExpr.liftN, AExpr.liftN_of_scope scope.1 count cutoff below, + AExpr.liftN_of_scope scope.2 count cutoff below] + | .lam _ a b, n, depth, scope, count, cutoff, below => by + simp only [AExpr.liftN, AExpr.liftN_of_scope scope.2.1 count cutoff below, + AExpr.liftN_of_scope scope.2.2 count (cutoff + 1) (by omega)] + | .forallE _ a b, n, depth, scope, count, cutoff, below => by + simp only [AExpr.liftN, AExpr.liftN_of_scope scope.2.1 count cutoff below, + AExpr.liftN_of_scope scope.2.2 count (cutoff + 1) (by omega)] + | .proj _ _ e, n, depth, scope, count, cutoff, below => by + simp only [AExpr.liftN, AExpr.liftN_of_scope (e := e) scope count cutoff below] + +theorem AExpr.inst_of_scope {β : Type u} : ∀ {e : AExpr β} {n depth : Nat}, + e.Scope n depth → ∀ (argument : AExpr β) (cutoff : Nat), depth ≤ cutoff → + e.inst argument cutoff = e + | .bvar i, _, depth, scope, argument, cutoff, below => by + simp only [AExpr.Scope] at scope + simp [AExpr.inst, AExpr.instVar, show i < cutoff by omega] + | .sort _, _, _, _, _, _, _ => rfl + | .const _ _, _, _, _, _, _, _ => rfl + | .natLit _, _, _, _, _, _, _ => rfl + | .app f a, n, depth, scope, argument, cutoff, below => by + simp only [AExpr.inst, AExpr.inst_of_scope scope.1 argument cutoff below, + AExpr.inst_of_scope scope.2 argument cutoff below] + | .lam _ a b, n, depth, scope, argument, cutoff, below => by + simp only [AExpr.inst, AExpr.inst_of_scope scope.2.1 argument cutoff below, + AExpr.inst_of_scope scope.2.2 argument (cutoff + 1) (by omega)] + | .forallE _ a b, n, depth, scope, argument, cutoff, below => by + simp only [AExpr.inst, AExpr.inst_of_scope scope.2.1 argument cutoff below, + AExpr.inst_of_scope scope.2.2 argument (cutoff + 1) (by omega)] + | .proj _ _ e, n, depth, scope, argument, cutoff, below => by + simp only [AExpr.inst, AExpr.inst_of_scope (e := e) scope argument cutoff below] + +/-- Universe instantiation leaves every bound variable in place, so a closed +type stays closed after instantiation at any universe arguments. -/ +theorem AExpr.liftN_instL_of_scope {β : Type u} : ∀ {e : AExpr β} {n depth : Nat}, + e.Scope n depth → ∀ (levels : List VLevel) (count cutoff : Nat), depth ≤ cutoff → + (e.instL levels).liftN count cutoff = e.instL levels + | .bvar i, _, depth, scope, levels, count, cutoff, below => by + simp only [AExpr.Scope] at scope + simp [AExpr.instL, AExpr.liftN, liftVar, show i < cutoff by omega] + | .sort _, _, _, _, _, _, _, _ => rfl + | .const _ _, _, _, _, _, _, _, _ => rfl + | .natLit _, _, _, _, _, _, _, _ => rfl + | .app f a, n, depth, scope, levels, count, cutoff, below => by + simp only [AExpr.instL, AExpr.liftN, AExpr.liftN_instL_of_scope scope.1 levels count cutoff below, + AExpr.liftN_instL_of_scope scope.2 levels count cutoff below] + | .lam _ a b, n, depth, scope, levels, count, cutoff, below => by + simp only [AExpr.instL, AExpr.liftN, AExpr.liftN_instL_of_scope scope.2.1 levels count cutoff below, + AExpr.liftN_instL_of_scope scope.2.2 levels count (cutoff + 1) (by omega)] + | .forallE _ a b, n, depth, scope, levels, count, cutoff, below => by + simp only [AExpr.instL, AExpr.liftN, AExpr.liftN_instL_of_scope scope.2.1 levels count cutoff below, + AExpr.liftN_instL_of_scope scope.2.2 levels count (cutoff + 1) (by omega)] + | .proj _ _ e, n, depth, scope, levels, count, cutoff, below => by + simp only [AExpr.instL, AExpr.liftN, AExpr.liftN_instL_of_scope (e := e) scope levels count cutoff below] + +theorem AExpr.inst_instL_of_scope {β : Type u} : ∀ {e : AExpr β} {n depth : Nat}, + e.Scope n depth → ∀ (levels : List VLevel) (argument : AExpr β) (cutoff : Nat), depth ≤ cutoff → + (e.instL levels).inst argument cutoff = e.instL levels + | .bvar i, _, depth, scope, levels, argument, cutoff, below => by + simp only [AExpr.Scope] at scope + simp [AExpr.instL, AExpr.inst, AExpr.instVar, show i < cutoff by omega] + | .sort _, _, _, _, _, _, _, _ => rfl + | .const _ _, _, _, _, _, _, _, _ => rfl + | .natLit _, _, _, _, _, _, _, _ => rfl + | .app f a, n, depth, scope, levels, argument, cutoff, below => by + simp only [AExpr.instL, AExpr.inst, AExpr.inst_instL_of_scope scope.1 levels argument cutoff below, + AExpr.inst_instL_of_scope scope.2 levels argument cutoff below] + | .lam _ a b, n, depth, scope, levels, argument, cutoff, below => by + simp only [AExpr.instL, AExpr.inst, AExpr.inst_instL_of_scope scope.2.1 levels argument cutoff below, + AExpr.inst_instL_of_scope scope.2.2 levels argument (cutoff + 1) (by omega)] + | .forallE _ a b, n, depth, scope, levels, argument, cutoff, below => by + simp only [AExpr.instL, AExpr.inst, AExpr.inst_instL_of_scope scope.2.1 levels argument cutoff below, + AExpr.inst_instL_of_scope scope.2.2 levels argument (cutoff + 1) (by omega)] + | .proj _ _ e, n, depth, scope, levels, argument, cutoff, below => by + simp only [AExpr.instL, AExpr.inst, AExpr.inst_instL_of_scope (e := e) scope levels argument cutoff below] + +theorem AExpr.appN_append {β : Type u} (head : AExpr β) (first second : List (AExpr β)) : + head.appN (first ++ second) = (head.appN first).appN second := by + induction first generalizing head with + | nil => rfl + | cons argument first ih => simpa only [List.cons_append, AExpr.appN_cons] using ih (head.app argument) + +/-! ### The derivation form -/ + +/-- Hereditary typing with canonical atom types. Sorts, constants, and +natural literals are typed exactly as the checker infers them; projections +carry a semantic type; binders, applications, and conversions retain their +children as `HereditaryTyping` does. -/ +inductive CheckedTyping {β : Type u} (entries : Model.Environment β) : + Model.Context β → AExpr β → AExpr β → Prop + | sort {context : Model.Context β} (level : VLevel) : + CheckedTyping entries context (.sort level) (.sort (.succ level)) + | const {context : Model.Context β} {ref : ConstRef β} {entry : ConstantEntry β} + {levels : List VLevel} (found : entries ref = some entry) + (arity : levels.length = entry.universes) : + CheckedTyping entries context (.const ref levels) (entry.type.instL levels) + | natLit {context : Model.Context β} {ref zero succ : ConstRef β} {entry : ConstantEntry β} + (found : entries ref = some entry) (fact : .natural zero succ ∈ entry.facts) + (arity : entry.universes = 0) (value : Nat) : + CheckedTyping entries context (.natLit value) (.const ref []) + | proj {context : Model.Context β} {ref : ConstRef β} {field : Nat} {major type : AExpr β} + (typed : TypingClaim.{u,v} entries context (.proj ref field major) type) : + CheckedTyping entries context (.proj ref field major) type + | bvar {context : Model.Context β} {index : Nat} {type : AExpr β} + (typed : TypingClaim.{u,v} entries context (.bvar index) type) + (atIndex : context[index]? = some type) : + CheckedTyping entries context (.bvar index) type + | forallE {context : Model.Context β} {condition : Certified.PropWhen} + {domain body type : AExpr β} {domainLevel bodyLevel : VLevel} + (typed : TypingClaim.{u,v} entries context (.forallE condition domain body) type) + (domainCheck : CheckedTyping entries context domain (.sort domainLevel)) + (bodyCheck : CheckedTyping entries (context.push domain) body (.sort bodyLevel)) + (conditionAgrees : condition = Certified.zeroCondition bodyLevel) : + CheckedTyping entries context (.forallE condition domain body) type + | lam {context : Model.Context β} {condition : Certified.PropWhen} {domain body codomain : AExpr β} + (typed : TypingClaim.{u,v} entries context (.lam condition domain body) + (.forallE condition domain codomain)) + (inner : CheckedTyping entries (context.push domain) body codomain) : + CheckedTyping entries context (.lam condition domain body) (.forallE condition domain codomain) + | app {context : Model.Context β} {fn arg : AExpr β} {condition : Certified.PropWhen} + {domain body : AExpr β} + (function : CheckedTyping entries context fn (.forallE condition domain body)) + (argument : CheckedTyping entries context arg domain) : + CheckedTyping entries context (.app fn arg) (body.inst arg) + | convert {context : Model.Context β} {term sourceType resultType : AExpr β} {level : VLevel} + (prior : CheckedTyping entries context term sourceType) + (rigid : AExpr.HeadRigid sourceType resultType) + (converted : ConversionClaim.{u,v} entries context sourceType resultType) + (formed : TypingClaim.{u,v} entries context resultType (.sort level)) : + CheckedTyping entries context term resultType + +namespace CheckedTyping + +variable {β : Type u} {entries : Model.Environment β} + +theorem typing {context : Model.Context β} {term type : AExpr β} + (checked : CheckedTyping.{u,v} entries context term type) : + TypingClaim.{u,v} entries context term type := by + induction checked with + | sort level => exact TypingClaim.sort level + | const found arity => exact TypingClaim.const found arity + | natLit found fact arity value => exact TypingClaim.natLit found fact arity value + | proj typed | bvar typed | forallE typed | lam typed => exact typed + | app _ _ function argument => exact TypingClaim.app function argument + | convert _ _ converted formed prior => exact prior.conv formed converted + +theorem toHereditary {context : Model.Context β} {term type : AExpr β} + (checked : CheckedTyping.{u,v} entries context term type) : + HereditaryTyping.{u,v} entries context term type := by + induction checked with + | sort level => exact .atom (TypingClaim.sort level) (.sort level) + | const found arity => exact .atom (TypingClaim.const found arity) (.const _ _) + | natLit found fact arity value => exact .atom (TypingClaim.natLit found fact arity value) (.natLit value) + | proj typed => exact .atom typed (.proj _ _ _) + | bvar typed atIndex => exact .bvar typed atIndex + | forallE typed _ _ agrees domain body => exact .forallE typed domain body agrees + | lam typed _ inner => exact .lam typed inner + | app _ _ function argument => exact .app function argument + | convert _ rigid converted formed prior => exact .convert prior rigid converted formed + +theorem lambdaSpine {context : Model.Context β} {term type : AExpr β} + (checked : CheckedTyping.{u,v} entries context term type) : + LambdaSpineTyping.{u,v} entries context term type := + checked.toHereditary.lambdaSpine + +/-! ### Context insertion and dependent substitution -/ + +theorem weakenAt (wellFormed : entries.WF) {source target : Model.Context β} {cutoff : Nat} + {term type : AExpr β} (checked : CheckedTyping.{u,v} entries source term type) + (insertion : ContextInsertion source target cutoff) : + CheckedTyping.{u,v} entries target (term.liftN 1 cutoff) (type.liftN 1 cutoff) := by + induction checked generalizing target cutoff with + | sort level => exact .sort level + | const found arity => + rw [AExpr.liftN_instL_of_scope (wellFormed.typeScope _ _ found) _ 1 cutoff (Nat.zero_le _)] + exact .const found arity + | natLit found fact arity value => exact .natLit found fact arity value + | proj typed => exact .proj (insertion.typing typed) + | bvar typed found => + exact .bvar (insertion.typing typed) + (by simpa only [liftVar, Nat.add_comm 1] using insertion.lookup found) + | forallE typed _ _ agrees domain body => + exact .forallE (insertion.typing typed) (domain insertion) (body (insertion.push _)) agrees + | lam typed _ inner => exact .lam (insertion.typing typed) (inner (insertion.push _)) + | app _ _ function argument => + simpa only [AExpr.liftN, AExpr.liftN_inst_zero] using + CheckedTyping.app (function insertion) (argument insertion) + | convert _ rigid converted formed prior => + exact .convert (prior insertion) + (AExpr.HeadRigid.map rigid (AExpr.liftN 1 · cutoff) (by intros; rfl)) + (insertion.conversion converted) (insertion.typing formed) + +theorem liftValue (wellFormed : entries.WF) {base source target : Model.Context β} + {domain argument term type : AExpr β} {cutoff : Nat} + (substitution : ContextSubstitution base domain argument source target cutoff) + (value : CheckedTyping.{u,v} entries base term type) : + CheckedTyping.{u,v} entries target (term.liftN cutoff) (type.liftN cutoff) := by + induction substitution with + | root => simpa only [AExpr.liftN_zero] using value + | @push source target cutoff prior binder ih => + have lifted := ih.weakenAt wellFormed (ContextInsertion.root target (binder.inst argument cutoff)) + simpa only [AExpr.liftN_liftN_merge term cutoff 1 0 0 (Nat.le_refl _) (Nat.zero_le _), + AExpr.liftN_liftN_merge type cutoff 1 0 0 (Nat.le_refl _) (Nat.zero_le _)] using lifted + +theorem substituteAt (wellFormed : entries.WF) {base source target : Model.Context β} + {domain argument term type : AExpr β} {cutoff : Nat} + (checked : CheckedTyping.{u,v} entries source term type) + (value : CheckedTyping.{u,v} entries base argument domain) + (substitution : ContextSubstitution base domain argument source target cutoff) : + CheckedTyping.{u,v} entries target (term.inst argument cutoff) (type.inst argument cutoff) := by + induction checked generalizing target cutoff with + | sort level => exact .sort level + | const found arity => + rw [AExpr.inst_instL_of_scope (wellFormed.typeScope _ _ found) _ argument cutoff (Nat.zero_le _)] + exact .const found arity + | natLit found fact arity value => exact .natLit found fact arity value + | proj typed => exact .proj (typed.instAt value.typing substitution) + | @bvar context index type typed found => + by_cases equal : index = cutoff + · subst index + have sameType := substitution.instantiate_removed_type found + simpa only [AExpr.inst, AExpr.instVar, Nat.lt_irrefl, if_false, if_true, sameType] using + liftValue wellFormed substitution value + · have retained := substitution.lookup_other found equal + have substituted := typed.instAt value.typing substitution + by_cases below : index < cutoff + · simp only [AExpr.inst, AExpr.instVar, below, if_true] at substituted retained ⊢ + exact .bvar substituted retained + · simp only [AExpr.inst, AExpr.instVar, below, equal, if_false] at substituted retained ⊢ + exact .bvar substituted retained + | forallE typed _ _ agrees domainCheck bodyCheck => + exact .forallE (typed.instAt value.typing substitution) (domainCheck substitution) + (bodyCheck (substitution.push _)) agrees + | lam typed _ inner => exact .lam (typed.instAt value.typing substitution) (inner (substitution.push _)) + | app _ _ function applied => + simpa only [AExpr.inst, AExpr.inst_inst_zero] using + CheckedTyping.app (function substitution) (applied substitution) + | convert _ rigid converted formed prior => + exact .convert (prior substitution) + (AExpr.HeadRigid.map rigid (AExpr.inst · argument cutoff) (by intros; rfl)) + (converted.instAt value.typing substitution) (formed.instAt value.typing substitution) + +/-! ### Inversions -/ + +theorem lamType {context : Model.Context β} {term type : AExpr β} + (checked : CheckedTyping.{u,v} entries context term type) + {condition : Certified.PropWhen} {domain body : AExpr β} + (same : term = .lam condition domain body) : + ∃ codomain, type = .forallE condition domain codomain ∧ + CheckedTyping.{u,v} entries (context.push domain) body codomain := by + induction checked with + | sort | const | natLit | proj | bvar | forallE | app => cases same + | lam _ inner _ => cases same; exact ⟨_, rfl, inner⟩ + | convert _ rigid _ _ ih => + obtain ⟨codomain, typeEq, inner⟩ := ih same + exact ⟨codomain, (rigid (by simp only [typeEq]; intro fn arg same; cases same)).trans typeEq, inner⟩ + +theorem appHead {context : Model.Context β} {term type : AExpr β} + (checked : CheckedTyping.{u,v} entries context term type) + {fn arg : AExpr β} (same : term = .app fn arg) : + ∃ headType, CheckedTyping.{u,v} entries context fn headType := by + induction checked with + | sort | const | natLit | proj | bvar | forallE | lam => cases same + | app function _ _ _ => cases same; exact ⟨_, function⟩ + | convert _ _ _ _ ih => exact ih same + +/-- The head of every checked application spine is checked at some type. -/ +theorem headTyped {context : Model.Context β} {head type : AExpr β} : + ∀ {arguments : List (AExpr β)}, + CheckedTyping.{u,v} entries context (head.appN arguments) type → + ∃ headType, CheckedTyping.{u,v} entries context head headType + | [], checked => ⟨type, checked⟩ + | argument :: arguments, checked => by + obtain ⟨applied, spine⟩ := headTyped (arguments := arguments) checked + exact spine.appHead rfl + +/-! ### Rewriting the head of a spine -/ + +theorem rewriteApp {context : Model.Context β} {term type : AExpr β} + (checked : CheckedTyping.{u,v} entries context term type) + {fn fn' arg : AExpr β} (same : term = .app fn arg) + (rewrite : ∀ headType, CheckedTyping.{u,v} entries context fn headType → + CheckedTyping.{u,v} entries context fn' headType) : + CheckedTyping.{u,v} entries context (.app fn' arg) type := by + induction checked with + | sort | const | natLit | proj | bvar | forallE | lam => cases same + | app function argument _ _ => cases same; exact .app (rewrite _ function) argument + | convert _ rigid converted formed ih => exact .convert (ih same rewrite) rigid converted formed + +/-- Replacing the head of a spine by a term checked at every type of the head +retains the spine's checked typing at its type. -/ +theorem rewriteHead {context : Model.Context β} {head head' type : AExpr β} + (rewrite : ∀ headType, CheckedTyping.{u,v} entries context head headType → + CheckedTyping.{u,v} entries context head' headType) : + ∀ {arguments : List (AExpr β)}, + CheckedTyping.{u,v} entries context (head.appN arguments) type → + CheckedTyping.{u,v} entries context (head'.appN arguments) type + | [], checked => rewrite _ checked + | argument :: arguments, checked => + rewriteHead (head := head.app argument) (head' := head'.app argument) + (fun _ applied => applied.rewriteApp rfl rewrite) (arguments := arguments) checked + +/-! ### Beta and delta -/ + +theorem betaStep (wellFormed : entries.WF) {context : Model.Context β} {term type : AExpr β} + (checked : CheckedTyping.{u,v} entries context term type) + {condition : Certified.PropWhen} {domain body argument : AExpr β} + (same : term = .app (.lam condition domain body) argument) : + CheckedTyping.{u,v} entries context (body.inst argument) type := by + induction checked with + | sort | const | natLit | proj | bvar | forallE | lam => cases same + | app function argument' _ _ => + cases same + obtain ⟨codomain, typeEq, inner⟩ := function.lamType rfl + cases typeEq + exact inner.substituteAt wellFormed argument' .root + | convert _ rigid converted formed ih => exact .convert (ih same) rigid converted formed + +/-- Every beta prefix of a checked spine is checked at the spine's type. -/ +theorem betaPrefix (wellFormed : entries.WF) {context : Model.Context β} {type : AExpr β} : + ∀ (count : Nat) (head : AExpr β) (arguments : List (AExpr β)), + CheckedTyping.{u,v} entries context (head.appN arguments) type → + CheckedTyping.{u,v} entries context (AExpr.betaPrefix count head arguments) type + | 0, _, _, checked => checked + | count + 1, .lam _ _ body, argument :: arguments, checked => + betaPrefix wellFormed count (body.inst argument) arguments + (rewriteHead (fun _ applied => applied.betaStep wellFormed rfl) checked) + | _ + 1, .lam _ _ _, [], checked => checked + | _ + 1, .bvar _, _, checked | _ + 1, .sort _, _, checked + | _ + 1, .const _ _, _, checked | _ + 1, .app _ _, _, checked + | _ + 1, .forallE _ _ _, _, checked | _ + 1, .proj _ _ _, _, checked + | _ + 1, .natLit _, _, checked => checked + +/-- A constant may be replaced by any term checked at its declared +instantiated type, at every type the constant is checked at. -/ +theorem unfoldConst {context : Model.Context β} {term type : AExpr β} + (checked : CheckedTyping.{u,v} entries context term type) + {ref : ConstRef β} {levels : List VLevel} {entry : ConstantEntry β} {output : AExpr β} + (same : term = .const ref levels) (found : entries ref = some entry) + (unfolded : CheckedTyping.{u,v} entries context output (entry.type.instL levels)) : + CheckedTyping.{u,v} entries context output type := by + induction checked with + | sort | natLit | proj | bvar | forallE | lam | app => cases same + | const found' _ => + cases same + cases Option.some.inj (found'.symm.trans found) + exact unfolded + | convert _ rigid converted formed ih => exact .convert (ih same unfolded) rigid converted formed + +end CheckedTyping + +end Ix.Kernel.Consistency + +namespace Ix.Kernel.Consistency.CheckedTyping + +open Theory Theory.Model + +universe u v + +/-- A checked constant instance has the arity of its admitted entry. -/ +theorem constArity {β : Type u} {entries : Model.Environment β} {context : Model.Context β} + {term type : AExpr β} (checked : CheckedTyping.{u,v} entries context term type) + {ref : ConstRef β} {levels : List VLevel} (same : term = .const ref levels) : + ∃ entry, entries ref = some entry ∧ levels.length = entry.universes := by + induction checked with + | sort | natLit | proj | bvar | forallE | lam | app => cases same + | const found arity => cases same; exact ⟨_, found, arity⟩ + | convert _ _ _ _ ih => exact ih same + +end Ix.Kernel.Consistency.CheckedTyping diff --git a/Ix/Kernel/Verify/Consistency/Invariant.lean b/Ix/Kernel/Verify/Consistency/Invariant.lean index 251c24802..b13b58f5f 100644 --- a/Ix/Kernel/Verify/Consistency/Invariant.lean +++ b/Ix/Kernel/Verify/Consistency/Invariant.lean @@ -11,6 +11,7 @@ import Ix.Kernel.Verify.Consistency.SynthesisCacheHistory import Ix.Kernel.Verify.Consistency.BetaCacheHistory import Ix.Kernel.Verify.Consistency.LocalStateReading import Ix.Kernel.Verify.Consistency.SortCache +import Ix.Kernel.Verify.Consistency.CheckedTyping /-! # The checker state invariant @@ -42,20 +43,31 @@ universe u v /-! ### Semantic agreement of the reduction caches -/ -/-- Every stored WHNF result is convertible to, and retains every type of, a -source expression with the key's address, read in some local context. -/ +/-- A result is a scope-generic sound reduction of a source: in every local +scope where the source reads to a checked annotated term, the result reads to +a convertible annotated term that is checked at every type of the source. +Checked typing is the premise strong enough for beta reduction; plain +semantic typing admits proof-regime applications whose reduct is not a +conversion. Quantifying over scopes is what makes an address-keyed memo +sound across the binder scopes of one run. -/ +def GenericReduction {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (source result : KExpr .anon) : Prop := + ∀ (locals : List FVarId) (context : Model.Context β) (term : AExpr β), + readScopedExpr? resolve locals source = some term.erase → + (∃ type, CheckedTyping.{u,v} entries context term type) → + ∃ target : AExpr β, readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ + ∀ type, CheckedTyping.{u,v} entries context term type → + CheckedTyping.{u,v} entries context target type + +/-- Every stored WHNF result is a scope-generic sound reduction of a source +expression with the key's address. -/ def WhnfCacheSemantics {β : Type u} (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) (state : TcState .anon) : Prop := ∀ (partition : WhnfCachePartition) (key : Address × Address) (result : KExpr .anon), (partition.cache state)[key]? = some result → - ∃ (locals : List FVarId) (context : Model.Context β) (source : KExpr .anon) - (term target : AExpr β), - source.addr = key.1 ∧ - readScopedExpr? resolve locals source = some term.erase ∧ - readScopedExpr? resolve locals result = some target.erase ∧ - ConversionClaim.{u,v} entries context term target ∧ - ∀ type, TypingClaim.{u,v} entries context term type → - TypingClaim.{u,v} entries context target type + ∃ source : KExpr .anon, source.addr = key.1 ∧ + GenericReduction.{u,v} resolve entries source result theorem WhnfCacheSemantics.ofMaps {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {before after : TcState .anon} @@ -168,17 +180,19 @@ theorem EquivManagerSemantics.empty {β : Type u} {resolve : Address → Option {entries : Model.Environment β} : EquivManagerSemantics.{u,v} resolve entries {} := EquivManager.WF.empty -/-- Every unfold entry is the universe instantiation of an admitted body at +/-- Every unfold entry reads, up to the universe simplifications of the actual +instantiation walker, to the universe instantiation of an admitted body at the head constant whose address keys it. -/ def UnfoldCacheSemantics {β : Type u} (resolve : Address → Option (ConstRef β)) (entries : Model.Environment β) (state : TcState .anon) : Prop := ∀ (addr : Address) (value : KExpr .anon), state.env.unfoldCache[addr]? = some value → ∃ (id : KId .anon) (arguments : Array (KUniv .anon)) (info : ExprInfo .anon) - (ref : ConstRef β) (entry : ConstantEntry β) (body : AExpr β), + (ref : ConstRef β) (entry : ConstantEntry β) (body output : AExpr β), (KExpr.const id arguments info).addr = addr ∧ resolve id.addr = some ref ∧ entries ref = some entry ∧ entry.body = some body ∧ entry.universes = arguments.size ∧ - readExpr? resolve value = some (body.instL (arguments.toList.map readLevel)).erase + readScopedExpr? resolve [] value = some output.erase ∧ + AExpr.LevelEquivalent (body.instL (arguments.toList.map readLevel)) output theorem UnfoldCacheSemantics.ofMap {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} {before after : TcState .anon} diff --git a/Ix/Kernel/Verify/Consistency/WhnfGeneric.lean b/Ix/Kernel/Verify/Consistency/WhnfGeneric.lean new file mode 100644 index 000000000..561778bb8 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/WhnfGeneric.lean @@ -0,0 +1,699 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Contracts +import Ix.Kernel.Verify.Consistency.BetaWhnfState + +/-! +# Scope-generic reduction contracts and the reduction invariant + +The reduction contracts of `Contracts.lean` state a memo hit at the scope of +the caller, but every WHNF memo is keyed by address alone once binders are +opened through free variables, so a hit may be served in a scope other than +the one that produced it. Semantic conversion at one scope does not transport +to another (a scope may hypothesize an empty type), so a per-entry memo +invariant must be stated for every scope at once. `GenericReduction` +(in `Invariant.lean`) is that statement, and `GenericSoundReduction` is the +contract whose successful outcomes are generic; it implies the scoped +`ReductionPost` conversion clause at any scope. + +Two further departures from `CheckerInvariant` are forced by the production +code. Its WHNF history field admits only beta, let, and head-call +publications, so a delta result cannot be published under it; +`ReductionInvariant` keeps every other field and replaces the history by the +semantic memo agreement. Its acceleration flag is a run configuration, so the +invariant records `noAccel = true` and every operation preserves it. The +bounded loop driver is treated once, over any loop state with a term view, +covering the structural loop and the `(term, seen)` state of the full loop. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-! ### The reduction invariant -/ + +/-- `CheckerInvariant` without its beta-only WHNF history, plus the +acceleration configuration. -/ +structure ReductionInvariant {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (source : Ixon.Env) + (catalog : List (SourceCacheRequest source)) (locals : List FVarId) + (context : Model.Context β) (bounds : List VLevel) (state : TcState .anon) : Prop where + sourceCache : SourceCacheInvariant catalog state + synthesis : Nonempty (SynthesisCacheHistory resolve anchor entries state) + structural : LocalStateInvariant state + reading : LocalContextReading resolve locals state.lctx context + origin : Nonempty (SynthesisContext resolve anchor [] [] entries context bounds) + semantics : ReductionCacheSemantics.{u,v} resolve entries state + accelerationsOff : state.noAccel = true + +namespace ReductionInvariant + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +theorem ofChecker {state : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) + (off : state.noAccel = true) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state := + ⟨valid.sourceCache, valid.synthesis, valid.structural, valid.reading, valid.origin, + valid.semantics, off⟩ + +section Projections + +variable {state : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) +include valid + +theorem sourceState : SourceStateInvariant source state := valid.sourceCache.state + +theorem inference : InferenceStateInvariant source state := valid.sourceCache.state.state + +theorem coherent : state.env.intern.WF := valid.inference.coherent + +theorem installed : + state.lazyFault = some (fun addr => ingressAnonAddrShallow source addr true) := + valid.inference.installed + +def owned : OwnedLazySupport state := valid.inference.owned + +theorem cache : SourceCacheAgreement catalog state := valid.sourceCache.cache + +end Projections + +/-- Operations that retain every map, the loaded declarations, coherence, +the acceleration flag, and the local context up to lookup equivalence. -/ +theorem ofMaps {before after : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (frame : LocalStateFrame before after) + (constants : after.env.consts = before.env.consts) + (blocks : after.env.blocks = before.env.blocks) + (coherent : after.env.intern.WF) + (full : after.env.inferCache = before.env.inferCache) + (only : after.env.inferOnlyCache = before.env.inferOnlyCache) + (whnf : ∀ partition : WhnfCachePartition, partition.cache after = partition.cache before) + (defEq : ∀ partition : DefEqCachePartition, partition.cache after = partition.cache before) + (manager : after.equivManager = before.equivManager) + (unfold : after.env.unfoldCache = before.env.unfoldCache) + (isProp : after.env.isPropCache = before.env.isPropCache) + (off : after.noAccel = before.noAccel) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after where + sourceCache := ⟨valid.sourceState.ofMaps frame.loader constants blocks coherent, + valid.cache.ofMaps full only constants⟩ + synthesis := valid.synthesis.elim fun history => ⟨history.ofMaps full only⟩ + structural := frame.invariant valid.structural + reading := valid.reading.congr frame.context.symm + origin := valid.origin + semantics := valid.semantics.ofMaps whnf defEq manager unfold isProp + accelerationsOff := off.trans valid.accelerationsOff + +/-- A new intern table is the only change of an intern-only operation. -/ +theorem ofIntern {before : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + {table : InternTable .anon} (coherent : table.WF) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {before with env := {before.env with intern := table}} := + valid.ofMaps ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl coherent rfl rfl + (fun partition => by cases partition <;> rfl) (fun partition => by cases partition <;> rfl) + rfl rfl rfl rfl + +theorem ofCtxAddrCache {before : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (memo : Std.HashMap (Address × UInt64) Address) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {before with ctxAddrCache := memo} := + valid.ofMaps ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl valid.coherent rfl rfl + (fun partition => by cases partition <;> rfl) (fun partition => by cases partition <;> rfl) + rfl rfl rfl rfl + +theorem whnfKey {before : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (term : KExpr .anon) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds + (betaWhnfKey term before).2 := by + have keyed := whnfKey_state (betaWhnfKey_run term before) + rw [keyed] + exact valid.ofCtxAddrCache _ + +/-- The full-WHNF instrumentation prefix and miss charge touch only counters +and the shared fuel. -/ +theorem instrument {before : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds + (betaWhnfPrefix before) := by + unfold betaWhnfPrefix + split + · exact valid.ofMaps ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl valid.coherent rfl rfl + (fun partition => by cases partition <;> rfl) (fun partition => by cases partition <;> rfl) + rfl rfl rfl rfl + · exact valid + +theorem charge {before : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds + (betaWhnfCharge before) := by + unfold betaWhnfCharge + split <;> exact valid.ofMaps ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl valid.coherent rfl rfl + (fun partition => by cases partition <;> rfl) (fun partition => by cases partition <;> rfl) + rfl rfl rfl rfl + +private theorem afterLookup {before after : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (sourceCache : SourceCacheInvariant catalog after) + (synthesis : Nonempty (SynthesisCacheHistory resolve anchor entries after)) + (frame : LazyLookupFrame before after) (ingress : KEnv.IngressFrame before.env after.env) + (structural : LocalStateFrame before after) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after := by + obtain ⟨consts, blocks, intern, envEq⟩ := ingress + have checker := frame.checker + exact { + sourceCache, synthesis + structural := structural.invariant valid.structural + reading := valid.reading.congr structural.context.symm + origin := valid.origin + semantics := valid.semantics.ofMaps + (fun partition => by cases partition <;> simp only [WhnfCachePartition.cache, envEq]) + (fun partition => by cases partition <;> simp only [DefEqCachePartition.cache, envEq]) + (by rw [checker]) (by rw [envEq]) (by rw [envEq]) + accelerationsOff := by rw [checker]; exact valid.accelerationsOff } + +/-- Lookup retains the invariant on both outcomes. -/ +theorem getConst {before : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (id : KId .anon) (data : StandaloneConversionData source id.addr before.env) : + match TcM.getConst id before with + | .ok _ after | .error _ after => + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after := by + have sourceCache := valid.sourceCache.getConst data + have synthesis : match TcM.getConst id before with + | .ok _ after | .error _ after => Nonempty (SynthesisCacheHistory resolve anchor entries after) := by + refine valid.synthesis.elim fun history => ?_ + have preserved := history.getConst (valid.owned.toVerified id.addr) + cases run : TcM.getConst id before <;> rw [run] at preserved <;> exact ⟨preserved⟩ + have frame := getConst_owned (id := id) valid.owned + have ingress := getConst_ingressFrame (id := id) source true valid.installed + have structural := FramesLocalState.getConst id before valid.structural + cases run : TcM.getConst id before <;> + rw [run] at sourceCache synthesis frame ingress structural <;> + exact valid.afterLookup sourceCache synthesis frame.1 ingress structural + +/-- `getConst` is `tryGetConst` followed by a pure match, so the invariant +after a `tryGetConst` outcome is the invariant after the matching `getConst` +outcome. -/ +theorem tryGetConst {before : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (id : KId .anon) (data : StandaloneConversionData source id.addr before.env) : + match TcM.tryGetConst id before with + | .ok _ after | .error _ after => + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after := by + have post := valid.getConst id data + unfold TcM.getConst at post + change (match (EStateM.bind (TcM.tryGetConst id) _ : TcM .anon (KConst .anon)) before with + | .ok _ after | .error _ after => + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after) at post + cases tried : TcM.tryGetConst id before with + | error err after => + rw [EStateM.bind, tried] at post + exact post + | ok optional after => + rw [EStateM.bind, tried] at post + cases optional <;> exact post + +end ReductionInvariant + +/-! ### The generic contracts -/ + +section Contracts + +variable {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (source : Ixon.Env) + (catalog : List (SourceCacheRequest source)) + +/-- One reduction outcome from an invariant state: the invariant holds +afterwards, and a successful result is a scope-generic reduction of the input. -/ +def ReductionOutcome (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (input : KExpr .anon) : + EStateM.Result (TcError .anon) (TcState .anon) (KExpr .anon) → Prop + | .ok result after => + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + GenericReduction.{u,v} resolve entries input result + | .error _ after => + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + +/-- The scope-generic reduction contract of one call. -/ +def GenericSoundReduction (input : KExpr .anon) (action : TcM .anon (KExpr .anon)) : Prop := + ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (before : TcState .anon) (term : AExpr β), + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before → + readScopedExpr? resolve locals input = some term.erase → + (∃ type, CheckedTyping.{u,v} entries context term type) → + ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (action before) + +/-- One iteration outcome of a bounded loop whose state has a term view. -/ +def StepOutcome {σ : Type} (view : σ → KExpr .anon) (locals : List FVarId) + (context : Model.Context β) (bounds : List VLevel) (current : σ) : + EStateM.Result (TcError .anon) (TcState .anon) (RecM.BoundedStep σ (KExpr .anon)) → Prop + | .ok (.next next) after => + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + GenericReduction.{u,v} resolve entries (view current) (view next) + | .ok (.done result) after => + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + GenericReduction.{u,v} resolve entries (view current) result + | .error _ after => + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + +/-- A loop step is sound when, from an invariant state whose current term +reads to a checked annotated term, it continues or finishes with a generic +reduction of the current term, or fails with the invariant. -/ +def SoundStep {σ : Type} (view : σ → KExpr .anon) + (step : σ → TcM .anon (RecM.BoundedStep σ (KExpr .anon))) : Prop := + ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (before : TcState .anon) (term : AExpr β) (current : σ), + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before → + readScopedExpr? resolve locals (view current) = some term.erase → + (∃ type, CheckedTyping.{u,v} entries context term type) → + StepOutcome.{u,v} resolve anchor entries source catalog view locals context bounds current + (step current before) + +/-- One probe outcome: absence and errors preserve the invariant, and a +produced expression is a generic reduction of the input. -/ +def ReducerOutcome (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (input : KExpr .anon) : + EStateM.Result (TcError .anon) (TcState .anon) (Option (KExpr .anon)) → Prop + | .ok none after => + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + | .ok (some reduced) after => + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + GenericReduction.{u,v} resolve entries input reduced + | .error _ after => + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + +/-- A reducer probe is sound when every outcome from an invariant state on a +readable checked input satisfies `ReducerOutcome`. -/ +def SoundReducer (probe : KExpr .anon → TcM .anon (Option (KExpr .anon))) : Prop := + ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (before : TcState .anon) (term : AExpr β) (input : KExpr .anon), + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before → + readScopedExpr? resolve locals input = some term.erase → + (∃ type, CheckedTyping.{u,v} entries context term type) → + ReducerOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (probe input before) + +/-- The four reduction fields of a method table, generically sound. -/ +structure GenericWhnfContract (methods : Methods .anon) : Prop where + whnf : ∀ term, GenericSoundReduction.{u,v} resolve anchor entries source catalog term + (methods.whnf term) + whnfCore : ∀ term, GenericSoundReduction.{u,v} resolve anchor entries source catalog term + (methods.whnfCore term) + whnfMode : ∀ term mode, GenericSoundReduction.{u,v} resolve anchor entries source catalog term + (methods.whnfMode term mode) + whnfCoreFlags : ∀ term flags, GenericSoundReduction.{u,v} resolve anchor entries source catalog term + (methods.whnfCoreFlags term flags) + +end Contracts + +/-! ### Composition of generic reductions -/ + +section Generic + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + +theorem GenericReduction.refl (source : KExpr .anon) : + GenericReduction.{u,v} resolve entries source source := + fun _ _ term reads _ => ⟨term, reads, .refl _, fun _ checked => checked⟩ + +theorem GenericReduction.trans {a b c : KExpr .anon} + (first : GenericReduction.{u,v} resolve entries a b) + (second : GenericReduction.{u,v} resolve entries b c) : + GenericReduction.{u,v} resolve entries a c := by + intro locals context term reads typed + obtain ⟨middle, readsMiddle, converted, preserved⟩ := first locals context term reads typed + obtain ⟨type, checked⟩ := typed + obtain ⟨target, readsTarget, converted', preserved'⟩ := + second locals context middle readsMiddle ⟨type, preserved type checked⟩ + exact ⟨target, readsTarget, converted.trans converted', + fun type checked => preserved' type (preserved type checked)⟩ + +/-- A generic reduction of a readable checked source, at any scope, retains +every semantic type of the source: the reduct is denoted, since it is +checked, and interprets equally. -/ +theorem GenericReduction.typing {source result : KExpr .anon} + (reduction : GenericReduction.{u,v} resolve entries source result) + {locals : List FVarId} {context : Model.Context β} {term : AExpr β} + (reads : readScopedExpr? resolve locals source = some term.erase) + (typed : ∃ type, CheckedTyping.{u,v} entries context term type) : + ∃ target : AExpr β, readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ + ∀ type, TypingClaim.{u,v} entries context term type → + TypingClaim.{u,v} entries context target type := by + obtain ⟨target, readsTarget, converted, preserved⟩ := reduction locals context term reads typed + refine ⟨target, readsTarget, converted, ?_⟩ + obtain ⟨checkedType, checked⟩ := typed + have targetTyped := (preserved checkedType checked).typing + intro type sourceTyped V _ constants realizes levels env valid + obtain ⟨_, typeValid, member⟩ := sourceTyped V constants realizes levels env valid + exact ⟨(targetTyped V constants realizes levels env valid).1, typeValid, + converted V constants realizes levels env valid ▸ member⟩ + +end Generic + +/-! ### Contract combinators -/ + +section Combinators + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} + +theorem GenericSoundReduction.throw {input : KExpr .anon} {error : TcError .anon} : + GenericSoundReduction.{u,v} resolve anchor entries source catalog input (throw error) := + fun _ _ _ _ _ valid _ _ => valid + +theorem GenericSoundReduction.pure {input : KExpr .anon} : + GenericSoundReduction.{u,v} resolve anchor entries source catalog input (pure input) := + fun _ _ _ _ _ valid _ _ => ⟨valid, .refl _⟩ + +/-- The exhausted table fails every reduction call without touching the state. -/ +theorem GenericWhnfContract.zero : + GenericWhnfContract.{u,v} resolve anchor entries source catalog (methodsN 0) where + whnf := fun _ => GenericSoundReduction.throw + whnfCore := fun _ => GenericSoundReduction.throw + whnfMode := fun _ _ => GenericSoundReduction.throw + whnfCoreFlags := fun _ _ => GenericSoundReduction.throw + +/-- The scoped conversion clause of `ReductionPost` follows from a generic +outcome at any scope whose reading is checked. -/ +theorem ReductionOutcome.scoped {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {input result : KExpr .anon} {after : TcState .anon} {term : AExpr β} + (outcome : ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (.ok result after)) + (reads : readScopedExpr? resolve locals input = some term.erase) + (typed : ∃ type, CheckedTyping.{u,v} entries context term type) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + ∃ target : AExpr β, readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ + ∀ type, TypingClaim.{u,v} entries context term type → + TypingClaim.{u,v} entries context target type := + ⟨outcome.1, outcome.2.typing reads typed⟩ + +end Combinators + +/-! ### Memo hits and publications -/ + +section Memo + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +/-- A stored WHNF result serves any query with the key's address, once the +query is identified with the recorded source. The identification premise is +the collision resource of this hit; a run inventory of memo sources would +discharge it from `RunAssumptions`. -/ +theorem WhnfCacheSemantics.hit {state : TcState .anon} + (valid : WhnfCacheSemantics.{u,v} resolve entries state) {partition : WhnfCachePartition} + {key : Address × Address} {result query : KExpr .anon} + (found : (partition.cache state)[key]? = some result) (address : key.1 = query.addr) + (faithful : ∀ source : KExpr .anon, source.addr = query.addr → + GenericReduction.{u,v} resolve entries source result → source = query) : + GenericReduction.{u,v} resolve entries query result := by + obtain ⟨recorded, recordedAddress, reduction⟩ := valid partition key result found + rw [faithful recorded (recordedAddress.trans address) reduction] at reduction + exact reduction + +/-- Publishing a generic reduction at its source's key retains the semantics. -/ +theorem WhnfCacheSemantics.insert {before after : TcState .anon} + (valid : WhnfCacheSemantics.{u,v} resolve entries before) (partition : WhnfCachePartition) + {key : Address × Address} {input result : KExpr .anon} (address : input.addr = key.1) + (reduction : GenericReduction.{u,v} resolve entries input result) + (written : ∀ other : WhnfCachePartition, other.cache after = + if other = partition then (partition.cache before).insert key result else other.cache before) : + WhnfCacheSemantics.{u,v} resolve entries after := by + intro other stored value found + rw [written other] at found + by_cases same : other = partition + · rw [if_pos same] at found + by_cases sameKey : key = stored + · subst sameKey + rw [Std.HashMap.getElem?_insert_self] at found + cases found + exact ⟨input, address, reduction⟩ + · rw [Std.HashMap.getElem?_insert, if_neg (fun equal => sameKey (eq_of_beq equal))] at found + subst same + exact valid other stored value found + · rw [if_neg same] at found + exact valid other stored value found + +/-- A stored unfold entry at the head's address is the recorded instantiation +of the recorded head, once the head is identified with it. -/ +theorem UnfoldCacheSemantics.hit {state : TcState .anon} + (valid : UnfoldCacheSemantics resolve entries state) + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} {value : KExpr .anon} + (found : state.env.unfoldCache[(KExpr.const id arguments info).addr]? = some value) + (faithful : ∀ (id' : KId .anon) (arguments' : Array (KUniv .anon)) (info' : ExprInfo .anon), + (KExpr.const id' arguments' info').addr = (KExpr.const id arguments info).addr → + KExpr.const id' arguments' info' = KExpr.const id arguments info) : + ∃ (ref : ConstRef β) (entry : ConstantEntry β) (body output : AExpr β), + resolve id.addr = some ref ∧ entries ref = some entry ∧ entry.body = some body ∧ + entry.universes = arguments.size ∧ + readScopedExpr? resolve [] value = some output.erase ∧ + AExpr.LevelEquivalent (body.instL (arguments.toList.map readLevel)) output := by + obtain ⟨id', arguments', info', ref, entry, body, output, address, resolved, entryFound, bodyFound, + universes, reads, same⟩ := valid _ value found + cases faithful id' arguments' info' address + exact ⟨ref, entry, body, output, resolved, entryFound, bodyFound, universes, reads, same⟩ + +theorem UnfoldCacheSemantics.insert {before : TcState .anon} + (valid : UnfoldCacheSemantics resolve entries before) + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} {value : KExpr .anon} + {ref : ConstRef β} {entry : ConstantEntry β} {body output : AExpr β} + (resolved : resolve id.addr = some ref) (entryFound : entries ref = some entry) + (bodyFound : entry.body = some body) (universes : entry.universes = arguments.size) + (reads : readScopedExpr? resolve [] value = some output.erase) + (same : AExpr.LevelEquivalent (body.instL (arguments.toList.map readLevel)) output) : + UnfoldCacheSemantics resolve entries {before with env := {before.env with + unfoldCache := before.env.unfoldCache.insert (KExpr.const id arguments info).addr value}} := by + intro addr stored found + change (before.env.unfoldCache.insert (KExpr.const id arguments info).addr value)[addr]? = + some stored at found + by_cases sameKey : (KExpr.const id arguments info).addr = addr + · subst sameKey + rw [Std.HashMap.getElem?_insert_self] at found + cases found + exact ⟨id, arguments, info, ref, entry, body, output, rfl, resolved, entryFound, bodyFound, + universes, reads, same⟩ + · rw [Std.HashMap.getElem?_insert, if_neg (fun equal => sameKey (eq_of_beq equal))] at found + exact valid addr stored found + +/-- Publishing into one WHNF partition: the invariant's other components are +untouched and the memo agreement absorbs the new entry. -/ +theorem ReductionInvariant.publishWhnf {before after : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (partition : WhnfCachePartition) {key : Address × Address} {input result : KExpr .anon} + (address : input.addr = key.1) + (reduction : GenericReduction.{u,v} resolve entries input result) + (written : ∀ other : WhnfCachePartition, other.cache after = + if other = partition then (partition.cache before).insert key result else other.cache before) + (frame : LocalStateFrame before after) + (constants : after.env.consts = before.env.consts) + (blocks : after.env.blocks = before.env.blocks) + (intern : after.env.intern = before.env.intern) + (full : after.env.inferCache = before.env.inferCache) + (only : after.env.inferOnlyCache = before.env.inferOnlyCache) + (defEq : ∀ partition : DefEqCachePartition, partition.cache after = partition.cache before) + (manager : after.equivManager = before.equivManager) + (unfold : after.env.unfoldCache = before.env.unfoldCache) + (isProp : after.env.isPropCache = before.env.isPropCache) + (off : after.noAccel = before.noAccel) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after where + sourceCache := ⟨valid.sourceState.ofMaps frame.loader constants blocks + (intern ▸ valid.coherent), valid.cache.ofMaps full only constants⟩ + synthesis := valid.synthesis.elim fun history => ⟨history.ofMaps full only⟩ + structural := frame.invariant valid.structural + reading := valid.reading.congr frame.context.symm + origin := valid.origin + semantics := ⟨valid.semantics.whnf.insert partition address reduction written, + valid.semantics.defEq.ofMaps defEq, by rw [manager]; exact valid.semantics.equivalence, + valid.semantics.unfold.ofMap unfold, valid.semantics.isProp.ofMap isProp⟩ + accelerationsOff := off.trans valid.accelerationsOff + +theorem ReductionInvariant.publishCore {before : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + {key : Address × Address} {input result : KExpr .anon} (address : input.addr = key.1) + (reduction : GenericReduction.{u,v} resolve entries input result) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {before with env := {before.env with + whnfCoreCache := before.env.whnfCoreCache.insert key result}} := + valid.publishWhnf .core address reduction (fun other => by cases other <;> rfl) + ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl rfl rfl rfl (fun partition => by cases partition <;> rfl) + rfl rfl rfl rfl + +theorem ReductionInvariant.publishCoreCheap {before : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + {key : Address × Address} {input result : KExpr .anon} (address : input.addr = key.1) + (reduction : GenericReduction.{u,v} resolve entries input result) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {before with env := {before.env with + whnfCoreCheapCache := before.env.whnfCoreCheapCache.insert key result}} := + valid.publishWhnf .coreCheap address reduction (fun other => by cases other <;> rfl) + ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl rfl rfl rfl (fun partition => by cases partition <;> rfl) + rfl rfl rfl rfl + +theorem ReductionInvariant.publishNoDelta {before : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + {key : Address × Address} {input result : KExpr .anon} (address : input.addr = key.1) + (reduction : GenericReduction.{u,v} resolve entries input result) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {before with env := {before.env with + whnfNoDeltaCache := before.env.whnfNoDeltaCache.insert key result}} := + valid.publishWhnf .noDelta address reduction (fun other => by cases other <;> rfl) + ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl rfl rfl rfl (fun partition => by cases partition <;> rfl) + rfl rfl rfl rfl + +theorem ReductionInvariant.publishNoDeltaCheap {before : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + {key : Address × Address} {input result : KExpr .anon} (address : input.addr = key.1) + (reduction : GenericReduction.{u,v} resolve entries input result) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {before with env := {before.env with + whnfNoDeltaCheapCache := before.env.whnfNoDeltaCheapCache.insert key result}} := + valid.publishWhnf .noDeltaCheap address reduction (fun other => by cases other <;> rfl) + ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl rfl rfl rfl (fun partition => by cases partition <;> rfl) + rfl rfl rfl rfl + +theorem ReductionInvariant.publishFull {before : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + {key : Address × Address} {input result : KExpr .anon} (address : input.addr = key.1) + (reduction : GenericReduction.{u,v} resolve entries input result) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {before with env := {before.env with whnfCache := before.env.whnfCache.insert key result}} := + valid.publishWhnf .full address reduction (fun other => by cases other <;> rfl) + ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl rfl rfl rfl (fun partition => by cases partition <;> rfl) + rfl rfl rfl rfl + +/-- Publishing an unfold entry at the head constant's address. -/ +theorem ReductionInvariant.publishUnfold {before : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} {value : KExpr .anon} + {ref : ConstRef β} {entry : ConstantEntry β} {body output : AExpr β} + (resolved : resolve id.addr = some ref) (entryFound : entries ref = some entry) + (bodyFound : entry.body = some body) (universes : entry.universes = arguments.size) + (reads : readScopedExpr? resolve [] value = some output.erase) + (same : AExpr.LevelEquivalent (body.instL (arguments.toList.map readLevel)) output) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {before with env := {before.env with + unfoldCache := before.env.unfoldCache.insert (KExpr.const id arguments info).addr value}} where + sourceCache := ⟨valid.sourceState.ofMaps rfl rfl rfl valid.coherent, valid.cache.ofMaps rfl rfl rfl⟩ + synthesis := valid.synthesis.elim fun history => ⟨history.ofMaps rfl rfl⟩ + structural := ⟨valid.structural.coherent, valid.structural.allocated, valid.structural.loader⟩ + reading := valid.reading + origin := valid.origin + semantics := ⟨valid.semantics.whnf.ofMaps (fun partition => by cases partition <;> rfl), + valid.semantics.defEq.ofMaps (fun partition => by cases partition <;> rfl), + valid.semantics.equivalence, + valid.semantics.unfold.insert resolved entryFound bodyFound universes reads same, + valid.semantics.isProp.ofMap rfl⟩ + accelerationsOff := valid.accelerationsOff + +end Memo + +/-! ### The bounded loop -/ + +section Loop + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} + +/-- A bounded loop of sound steps is a generically sound reduction of its +start term: successful iterations compose by transitivity, and exhaustion +fails without touching the state. -/ +theorem runBounded_sound {σ : Type} {view : σ → KExpr .anon} + {step : σ → RecM .anon (RecM.BoundedStep σ (KExpr .anon))} {methods : Methods .anon} + (sound : SoundStep.{u,v} resolve anchor entries source catalog view + (fun current => (step current).run methods)) : + ∀ (fuel : Nat) (start : σ), GenericSoundReduction.{u,v} resolve anchor entries source catalog + (view start) ((RecM.runBounded step fuel start).run methods) + | 0, start => fun _ _ _ _ _ valid _ _ => valid + | fuel + 1, start => by + intro locals context bounds before term valid reads typed + have outcome := sound locals context bounds before term start valid reads typed + change StepOutcome.{u,v} resolve anchor entries source catalog view locals context bounds start + ((step start).run methods before) at outcome + rw [RecM.runBounded, ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds + (view start) (EStateM.bind ((step start).run methods) _ before) + cases run : (step start).run methods before with + | error error after => + rw [run] at outcome + rw [EStateM.bind, run] + exact outcome + | ok outcomeValue after => + rw [run] at outcome + rw [EStateM.bind, run] + cases outcomeValue with + | next next => + obtain ⟨valid', reduction⟩ := outcome + obtain ⟨middle, readsMiddle, _, preserved⟩ := + reduction locals context term reads typed + obtain ⟨type, checked⟩ := typed + have rest := runBounded_sound sound fuel next locals context bounds after middle + valid' readsMiddle ⟨type, preserved type checked⟩ + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context + bounds (view start) ((RecM.runBounded step fuel next).run methods after) + cases final : (RecM.runBounded step fuel next).run methods after with + | error error last => rw [final] at rest; exact rest + | ok result last => + rw [final] at rest + exact ⟨rest.1, reduction.trans rest.2⟩ + | done result => exact outcome + +end Loop + +/-! ### Accelerations under `noAccel` -/ + +section Accelerations + +variable {methods : Methods .anon} {state : TcState .anon} + +private theorem get_run (state : TcState .anon) : + (get : TcM .anon (TcState .anon)) state = .ok state state := rfl + +theorem tryReduceNative_noAccel (off : state.noAccel = true) (term : KExpr .anon) : + (RecM.tryReduceNative term).run methods state = .ok none state := by + unfold RecM.tryReduceNative + rw [ReaderT.run_bind] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ state = _ + rw [EStateM.bind, get_run] + simp only [off, ↓reduceIte] + rfl + +theorem tryReduceBitvec_noAccel (off : state.noAccel = true) (term : KExpr .anon) : + (RecM.tryReduceBitvec term).run methods state = .ok none state := by + unfold RecM.tryReduceBitvec + rw [ReaderT.run_bind] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ state = _ + rw [EStateM.bind, get_run] + simp only [off, ↓reduceIte] + rfl + +theorem tryReduceDecidable_noAccel (off : state.noAccel = true) (term : KExpr .anon) : + (RecM.tryReduceDecidable term).run methods state = .ok none state := by + unfold RecM.tryReduceDecidable + rw [ReaderT.run_bind] + change EStateM.bind (get : TcM .anon (TcState .anon)) _ state = _ + rw [EStateM.bind, get_run] + simp only [off, ↓reduceIte] + rfl + +end Accelerations + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/WhnfLayers.lean b/Ix/Kernel/Verify/Consistency/WhnfLayers.lean new file mode 100644 index 000000000..c170d0abb --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/WhnfLayers.lean @@ -0,0 +1,669 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.WhnfGeneric + +/-! +# The three WHNF cache layers under the generic contracts + +`whnfCoreWithFlagsNonLeaf`, `whnfNoDeltaImplNonLeaf`, and +`whnfWithNatSuccModeNonLeaf` wrap their bounded loops with key computation, +the transient natural-literal check, a memo lookup, and a guarded +publication. Each layer is a generically sound reduction whenever its +uncached body is: a hit is served by the memo semantics, a miss runs the +body and publishes its generic result, and every failure inherits the +body's or the lookup's invariant. The transient check consults the +environment through the lazy loader, so it needs the per-lookup conversion +data that `RunAssumptions` supplies; hits need the identification of the +query with the recorded source, the collision resource of this library. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +section Data + +variable {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (source : Ixon.Env) + (catalog : List (SourceCacheRequest source)) + +/-- Conversion data for every lookup from an invariant state: the standalone +conversion resource of `RunAssumptions`, quantified over the run's states. -/ +def LookupData : Prop := + ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (state : TcState .anon), + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + ∀ addr, StandaloneConversionData source addr state.env + +/-- Every WHNF memo entry consulted at a query with its key's address was +produced from that query. This is the key collision resource of the memo, +stated as the identification of the recorded source with the query. -/ +def WhnfHitData : Prop := + ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (state : TcState .anon), + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + ∀ (partition : WhnfCachePartition) (key : Address × Address) (result query : KExpr .anon), + (partition.cache state)[key]? = some result → key.1 = query.addr → + ∀ recorded : KExpr .anon, recorded.addr = query.addr → + GenericReduction.{u,v} resolve entries recorded result → recorded = query + +/-- Invariant preservation on both outcomes of a checker action. -/ +def PreservesInvariant {α : Type} (action : TcM .anon α) : Prop := + ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (before : TcState .anon), + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before → + match action before with + | .ok _ after | .error _ after => + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + +end Data + +section Combinators + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} + +namespace PreservesInvariant + +theorem pure {α : Type} (value : α) : + PreservesInvariant.{u,v} resolve anchor entries source catalog (Pure.pure value : TcM .anon α) := + fun _ _ _ _ valid => valid + +theorem throw {α : Type} (error : TcError .anon) : + PreservesInvariant.{u,v} resolve anchor entries source catalog (throw error : TcM .anon α) := + fun _ _ _ _ valid => valid + +theorem bind {α γ : Type} {action : TcM .anon α} {next : α → TcM .anon γ} + (first : PreservesInvariant.{u,v} resolve anchor entries source catalog action) + (rest : ∀ value, PreservesInvariant.{u,v} resolve anchor entries source catalog (next value)) : + PreservesInvariant.{u,v} resolve anchor entries source catalog (action >>= next) := by + intro locals context bounds before valid + have intermediate := first locals context bounds before valid + change match EStateM.bind action next before with + | .ok _ after | .error _ after => + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + cases run : action before with + | error error after => + rw [EStateM.bind, run] + simpa only [run] using intermediate + | ok value after => + rw [run] at intermediate + rw [EStateM.bind, run] + exact rest value locals context bounds after intermediate + +theorem get : + PreservesInvariant.{u,v} resolve anchor entries source catalog (get : TcM .anon (TcState .anon)) := + fun _ _ _ _ valid => valid + +theorem tryGetConst (lookups : LookupData.{u,v} resolve anchor entries source catalog) (id : KId .anon) : + PreservesInvariant.{u,v} resolve anchor entries source catalog (TcM.tryGetConst id) := by + intro locals context bounds before valid + have post := valid.tryGetConst id (lookups locals context bounds before valid id.addr) + cases run : TcM.tryGetConst id before <;> rw [run] at post <;> exact post + +theorem getConst (lookups : LookupData.{u,v} resolve anchor entries source catalog) (id : KId .anon) : + PreservesInvariant.{u,v} resolve anchor entries source catalog (TcM.getConst id) := by + intro locals context bounds before valid + have post := valid.getConst id (lookups locals context bounds before valid id.addr) + cases run : TcM.getConst id before <;> rw [run] at post <;> exact post + +/-- Any action whose two outcomes are given by cases can be closed by +inspecting the run. -/ +theorem of_run {α : Type} {action : TcM .anon α} + (run : ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (before : TcState .anon), + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before → + ∀ after, (∃ value, action before = .ok value after) ∨ (∃ error, action before = .error error after) → + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after) : + PreservesInvariant.{u,v} resolve anchor entries source catalog action := by + intro locals context bounds before valid + cases outcome : action before with + | ok value after => exact run locals context bounds before valid after (.inl ⟨value, outcome⟩) + | error error after => exact run locals context bounds before valid after (.inr ⟨error, outcome⟩) + +end PreservesInvariant + +end Combinators + +/-! ### The transient natural-literal check -/ + +section Transient + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {methods : Methods .anon} + +theorem PreservesInvariant.prims : + PreservesInvariant.{u,v} resolve anchor entries source catalog + (ReaderT.run (RecM.prims : RecM .anon (Primitives .anon)) methods) := + fun _ _ _ _ valid => valid + +theorem PreservesInvariant.runPure {α : Type} (value : α) : + PreservesInvariant.{u,v} resolve anchor entries source catalog + (ReaderT.run (Pure.pure value : RecM .anon α) methods) := + fun _ _ _ _ valid => valid + +theorem isNatLiteralRecursorApp_preserves + (lookups : LookupData.{u,v} resolve anchor entries source catalog) (term : KExpr .anon) : + PreservesInvariant.{u,v} resolve anchor entries source catalog + ((RecM.isNatLiteralRecursorApp term).run methods) := by + unfold RecM.isNatLiteralRecursorApp + cases spine : term.collectSpine with + | mk head arguments => + cases head with + | const id levels info => + dsimp only + rw [ReaderT.run_bind] + apply PreservesInvariant.bind PreservesInvariant.prims + intro primitives + split + · exact PreservesInvariant.runPure _ + · rw [ReaderT.run_bind, ReaderT.run_monadLift] + apply PreservesInvariant.bind (PreservesInvariant.tryGetConst lookups id) + intro found + split + · split <;> exact PreservesInvariant.runPure _ + · exact PreservesInvariant.runPure _ + | _ => exact PreservesInvariant.runPure _ + +theorem isTransientNatLiteralWork_preserves + (lookups : LookupData.{u,v} resolve anchor entries source catalog) (term : KExpr .anon) : + PreservesInvariant.{u,v} resolve anchor entries source catalog + ((RecM.isTransientNatLiteralWork term).run methods) := by + unfold RecM.isTransientNatLiteralWork + rw [ReaderT.run_bind] + apply PreservesInvariant.bind (isNatLiteralRecursorApp_preserves lookups term) + intro literal + split + · exact PreservesInvariant.runPure _ + · cases spine : term.collectSpine with + | mk head arguments => + cases head with + | const id levels info => + dsimp only + rw [ReaderT.run_bind] + apply PreservesInvariant.bind PreservesInvariant.prims + intro primitives + split + · exact isNatLiteralRecursorApp_preserves lookups _ + · exact PreservesInvariant.runPure _ + | _ => exact PreservesInvariant.runPure _ + +end Transient + +/-! ### Sequencing a body with its publication -/ + +section Sequencing + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +/-- A body outcome followed by a continuation that, from the body's invariant +state and generic result, yields an outcome for the same input. -/ +theorem ReductionOutcome.bind {input : KExpr .anon} {action : TcM .anon (KExpr .anon)} + {next : KExpr .anon → TcM .anon (KExpr .anon)} {before : TcState .anon} + (first : ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (action before)) + (rest : ∀ result middle, + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds middle → + GenericReduction.{u,v} resolve entries input result → + ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (next result middle)) : + ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (EStateM.bind action next before) := by + cases run : action before with + | error error after => + rw [EStateM.bind, run] + simpa only [run] using first + | ok result after => + rw [run] at first + rw [EStateM.bind, run] + exact rest result after first.1 first.2 + +/-- A pure return of a generic result. -/ +theorem ReductionOutcome.pure {input result : KExpr .anon} {state : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) + (reduction : GenericReduction.{u,v} resolve entries input result) : + ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (.ok result state) := + ⟨valid, reduction⟩ + +end Sequencing + +/-! ### The structural layer -/ + +section Core + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {methods : Methods .anon} + +private theorem get_run (state : TcState .anon) : + (get : TcM .anon (TcState .anon)) state = .ok state state := rfl + +/-- The key/transient/lookup/publication layer of structural WHNF is +generically sound whenever its bounded loop is. -/ +theorem whnfCoreWithFlagsNonLeaf_sound + (lookups : LookupData.{u,v} resolve anchor entries source catalog) + (hits : WhnfHitData.{u,v} resolve anchor entries source catalog) + (body : ∀ term flags, GenericSoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfCoreWithFlagsUncached term flags).run methods)) + (term : KExpr .anon) (flags : WhnfFlags) : + GenericSoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfCoreWithFlagsNonLeaf term flags).run methods) := by + intro locals context bounds before reading valid reads typed + unfold RecM.whnfCoreWithFlagsNonLeaf + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind (TcM.whnfKey term) _ before) + rw [EStateM.bind, betaWhnfKey_run] + have keyed := valid.whnfKey term + have address : (betaWhnfKey term before).1.1 = term.addr := betaWhnfKey_address term before + generalize (betaWhnfKey term before).1 = key at address ⊢ + generalize (betaWhnfKey term before).2 = keyedState at keyed ⊢ + dsimp only + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind ((RecM.isTransientNatLiteralWork term).run methods) _ keyedState) + have transient := isTransientNatLiteralWork_preserves (methods := methods) lookups term + locals context bounds keyedState keyed + cases transientRun : (RecM.isTransientNatLiteralWork term).run methods keyedState with + | error error failed => + rw [EStateM.bind, transientRun] + simp only [transientRun] at transient + exact transient + | ok transientWork checked => + rw [transientRun] at transient + simp only [] at transient + rw [EStateM.bind, transientRun] + dsimp only + cases full : flags.isFull <;> cases transientWork <;> + simp only [Bool.not_false, Bool.not_true, Bool.false_eq_true, ↓reduceIte] + · -- cheap partition, lookup then publication + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind (get : TcM .anon (TcState .anon)) _ checked) + rw [EStateM.bind, get_run] + dsimp only + cases lookup : checked.env.whnfCoreCheapCache[key]? with + | some cached => + exact ReductionOutcome.pure transient (transient.semantics.whnf.hit (partition := .coreCheap) + lookup address (hits locals context bounds checked transient .coreCheap key cached term lookup address)) + | none => + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind ((RecM.whnfCoreWithFlagsUncached term flags).run methods) _ checked) + apply ReductionOutcome.bind (body term flags locals context bounds checked reading transient reads typed) + intro result reduced valid' reduction + exact ReductionOutcome.pure (valid'.publishCoreCheap address.symm reduction) reduction + · -- cheap partition, transient work: no lookup, no publication + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind ((RecM.whnfCoreWithFlagsUncached term flags).run methods) _ checked) + apply ReductionOutcome.bind (body term flags locals context bounds checked reading transient reads typed) + intro result reduced valid' reduction + exact ReductionOutcome.pure valid' reduction + · -- full partition, lookup then publication + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind (get : TcM .anon (TcState .anon)) _ checked) + rw [EStateM.bind, get_run] + dsimp only + cases lookup : checked.env.whnfCoreCache[key]? with + | some cached => + exact ReductionOutcome.pure transient (transient.semantics.whnf.hit (partition := .core) + lookup address (hits locals context bounds checked transient .core key cached term lookup address)) + | none => + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind ((RecM.whnfCoreWithFlagsUncached term flags).run methods) _ checked) + apply ReductionOutcome.bind (body term flags locals context bounds checked reading transient reads typed) + intro result reduced valid' reduction + exact ReductionOutcome.pure (valid'.publishCore address.symm reduction) reduction + · -- full partition, transient work + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind ((RecM.whnfCoreWithFlagsUncached term flags).run methods) _ checked) + apply ReductionOutcome.bind (body term flags locals context bounds checked reading transient reads typed) + intro result reduced valid' reduction + exact ReductionOutcome.pure valid' reduction + +end Core + +/-! ### The no-delta layer -/ + +section NoDelta + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {methods : Methods .anon} + +private theorem get_run' (state : TcState .anon) : + (get : TcM .anon (TcState .anon)) state = .ok state state := rfl + +private theorem collapse_beq : (NatSuccMode.collapse == NatSuccMode.collapse) = true := rfl +private theorem stuck_beq : (NatSuccMode.stuck == NatSuccMode.collapse) = false := rfl + +/-- The key/transient/lookup/publication layer of no-delta WHNF is +generically sound whenever its bounded loop is. Only the collapsing mode +consults or publishes the memo, and publication is suppressed inside native +reduction and for transient natural-literal work. -/ +theorem whnfNoDeltaImplNonLeaf_sound + (lookups : LookupData.{u,v} resolve anchor entries source catalog) + (hits : WhnfHitData.{u,v} resolve anchor entries source catalog) + (body : ∀ term flags mode, GenericSoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfNoDeltaImplUncached term flags mode).run methods)) + (term : KExpr .anon) (flags : WhnfFlags) (mode : NatSuccMode) : + GenericSoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfNoDeltaImplNonLeaf term flags mode).run methods) := by + intro locals context bounds before reading valid reads typed + unfold RecM.whnfNoDeltaImplNonLeaf + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind (TcM.whnfKey term) _ before) + rw [EStateM.bind, betaWhnfKey_run] + have keyed := valid.whnfKey term + have address : (betaWhnfKey term before).1.1 = term.addr := betaWhnfKey_address term before + generalize (betaWhnfKey term before).1 = key at address ⊢ + generalize (betaWhnfKey term before).2 = keyedState at keyed ⊢ + dsimp only + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind ((RecM.isTransientNatLiteralWork term).run methods) _ keyedState) + have transient := isTransientNatLiteralWork_preserves (methods := methods) lookups term + locals context bounds keyedState keyed + cases transientRun : (RecM.isTransientNatLiteralWork term).run methods keyedState with + | error error failed => + rw [EStateM.bind, transientRun] + simp only [transientRun] at transient + exact transient + | ok transientWork checked => + rw [transientRun] at transient + simp only [] at transient + rw [EStateM.bind, transientRun] + dsimp only + cases mode <;> cases transientWork <;> + simp only [collapse_beq, stuck_beq, Bool.not_false, Bool.not_true, Bool.and_false, + Bool.and_true, Bool.false_eq_true, ↓reduceIte] + · -- collapse, non-transient: lookup and guarded publication + split <;> rename_i full + · rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind (get : TcM .anon (TcState .anon)) _ checked) + rw [EStateM.bind, get_run'] + dsimp only + cases lookup : checked.env.whnfNoDeltaCache[key]? with + | some cached => + exact ReductionOutcome.pure transient (transient.semantics.whnf.hit (partition := .noDelta) + lookup address (hits locals context bounds checked transient .noDelta key cached term + lookup address)) + | none => + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds + term (EStateM.bind ((RecM.whnfNoDeltaImplUncached term flags .collapse).run methods) _ checked) + apply ReductionOutcome.bind + (body term flags .collapse locals context bounds checked reading transient reads typed) + intro result reduced valid' reduction + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds + term (EStateM.bind (get : TcM .anon (TcState .anon)) _ reduced) + rw [EStateM.bind, get_run'] + dsimp only + split + · exact ReductionOutcome.pure (valid'.publishNoDelta address.symm reduction) reduction + · exact ReductionOutcome.pure valid' reduction + · rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind (get : TcM .anon (TcState .anon)) _ checked) + rw [EStateM.bind, get_run'] + dsimp only + cases lookup : checked.env.whnfNoDeltaCheapCache[key]? with + | some cached => + exact ReductionOutcome.pure transient (transient.semantics.whnf.hit (partition := .noDeltaCheap) + lookup address (hits locals context bounds checked transient .noDeltaCheap key cached term + lookup address)) + | none => + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds + term (EStateM.bind ((RecM.whnfNoDeltaImplUncached term flags .collapse).run methods) _ checked) + apply ReductionOutcome.bind + (body term flags .collapse locals context bounds checked reading transient reads typed) + intro result reduced valid' reduction + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds + term (EStateM.bind (get : TcM .anon (TcState .anon)) _ reduced) + rw [EStateM.bind, get_run'] + dsimp only + split + · exact ReductionOutcome.pure (valid'.publishNoDeltaCheap address.symm reduction) reduction + · exact ReductionOutcome.pure valid' reduction + all_goals + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind ((RecM.whnfNoDeltaImplUncached term flags _).run methods) _ checked) + apply ReductionOutcome.bind + (body term flags _ locals context bounds checked reading transient reads typed) + intro result reduced valid' reduction + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind (get : TcM .anon (TcState .anon)) _ reduced) + rw [EStateM.bind, get_run'] + exact ReductionOutcome.pure valid' reduction + +end NoDelta + +/-! ### The full layer -/ + +section Full + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} {methods : Methods .anon} + +/-- The optional miss counter bump. -/ +def betaWhnfBump (before : TcState .anon) : TcState .anon := + if before.stats then { before with whnfMisses := before.whnfMisses + 1 } else before + +theorem betaWhnfBump_run (before : TcState .anon) : + TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 }) before = + .ok () (betaWhnfBump before) := by + unfold TcM.bumpStats betaWhnfBump + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + split <;> rfl + +theorem tick_run (before : TcState .anon) : + TcM.tick before = if (before.recFuel == 0) = true then .error .maxRecFuel before + else .ok () { before with recFuel := before.recFuel - 1 } := by + unfold TcM.tick + change EStateM.bind (get : TcM .anon (TcState .anon)) _ before = _ + simp only [EStateM.bind, show (get : TcM .anon (TcState .anon)) before = .ok before before from rfl] + split <;> rfl + +theorem ReductionInvariant.bump {before : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds + (betaWhnfBump before) := by + unfold betaWhnfBump + split + · exact valid.ofMaps ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl valid.coherent rfl rfl + (fun partition => by cases partition <;> rfl) (fun partition => by cases partition <;> rfl) + rfl rfl rfl rfl + · exact valid + +theorem ReductionInvariant.tick {before : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds + { before with recFuel := before.recFuel - 1 } := + valid.ofMaps ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl valid.coherent rfl rfl + (fun partition => by cases partition <;> rfl) (fun partition => by cases partition <;> rfl) + rfl rfl rfl rfl + +/-- The miss charge bumps the optional counter, then ticks. -/ +theorem whnfWithNatSuccModeMissCharge_run (before : TcState .anon) : + (RecM.whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods before = + if ((betaWhnfBump before).recFuel == 0) = true then .error .maxRecFuel (betaWhnfBump before) + else .ok () { betaWhnfBump before with recFuel := (betaWhnfBump before).recFuel - 1 } := by + unfold RecM.whnfWithNatSuccModeMissCharge + rw [ReaderT.run_bind] + change EStateM.bind (TcM.bumpStats (fun state => { state with whnfMisses := state.whnfMisses + 1 })) + (fun _ => TcM.tick) before = _ + rw [EStateM.bind, betaWhnfBump_run] + exact tick_run _ + +/-- The miss charge preserves the invariant on both outcomes: exhausted fuel +fails after the optional counter bump. -/ +theorem whnfWithNatSuccModeMissCharge_preserves : + PreservesInvariant.{u,v} resolve anchor entries source catalog + ((RecM.whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods) := by + intro locals context bounds before valid + rw [whnfWithNatSuccModeMissCharge_run] + by_cases exhausted : ((betaWhnfBump before).recFuel == 0) = true + · rw [if_pos exhausted] + exact valid.bump + · rw [if_neg exhausted] + exact valid.bump.tick + +private theorem get_run'' (state : TcState .anon) : + (get : TcM .anon (TcState .anon)) state = .ok state state := rfl + +private theorem collapse_beq' : (NatSuccMode.collapse == NatSuccMode.collapse) = true := rfl +private theorem stuck_beq' : (NatSuccMode.stuck == NatSuccMode.collapse) = false := rfl + +/-- The instrumented full-WHNF layer is generically sound whenever its +bounded loop is. The memo is consulted and published only in the collapsing +mode, outside native reduction, and for non-transient work. -/ +theorem whnfWithNatSuccModeNonLeaf_sound + (lookups : LookupData.{u,v} resolve anchor entries source catalog) + (hits : WhnfHitData.{u,v} resolve anchor entries source catalog) + (body : ∀ term mode, GenericSoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfWithNatSuccModeUncached term mode).run methods)) + (term : KExpr .anon) (mode : NatSuccMode) : + GenericSoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfWithNatSuccModeNonLeaf term mode).run methods) := by + intro locals context bounds before reading valid reads typed + unfold RecM.whnfWithNatSuccModeNonLeaf + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind ((RecM.whnfWithNatSuccModePrefix term).run methods) _ before) + rw [EStateM.bind, betaWhnfPrefix_run] + have instrumented := valid.instrument + generalize betaWhnfPrefix before = traced at instrumented ⊢ + dsimp only + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind (TcM.whnfKey term) _ traced) + rw [EStateM.bind, betaWhnfKey_run] + have keyed := instrumented.whnfKey term + have address : (betaWhnfKey term traced).1.1 = term.addr := betaWhnfKey_address term traced + generalize (betaWhnfKey term traced).1 = key at address ⊢ + generalize (betaWhnfKey term traced).2 = keyedState at keyed ⊢ + dsimp only + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind ((RecM.isTransientNatLiteralWork term).run methods) _ keyedState) + have transient := isTransientNatLiteralWork_preserves (methods := methods) lookups term + locals context bounds keyedState keyed + cases transientRun : (RecM.isTransientNatLiteralWork term).run methods keyedState with + | error error failed => + rw [EStateM.bind, transientRun] + simp only [transientRun] at transient + exact transient + | ok transientWork checked => + rw [transientRun] at transient + simp only [] at transient + rw [EStateM.bind, transientRun] + dsimp only + have miss : ∀ (guard : TcState .anon → Bool) (state : TcState .anon), + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind ((RecM.whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods) + (fun _ => ReaderT.run (do + let cur ← RecM.whnfWithNatSuccModeUncached term mode + let current ← get + if guard current then + modify fun s => { s with env := { s.env with + whnfCache := s.env.whnfCache.insert key cur } } + pure cur) methods) state) := by + intro guard state valid + have charged := whnfWithNatSuccModeMissCharge_preserves (methods := methods) + locals context bounds state valid + cases chargeRun : (RecM.whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods state with + | error error failed => + rw [EStateM.bind, chargeRun] + simp only [chargeRun] at charged + exact charged + | ok _ charging => + rw [chargeRun] at charged + simp only [] at charged + rw [EStateM.bind, chargeRun] + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind ((RecM.whnfWithNatSuccModeUncached term mode).run methods) _ charging) + apply ReductionOutcome.bind + (body term mode locals context bounds charging reading charged reads typed) + intro result reduced valid' reduction + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind (get : TcM .anon (TcState .anon)) _ reduced) + rw [EStateM.bind, get_run''] + dsimp only + split + · exact ReductionOutcome.pure (valid'.publishFull address.symm reduction) reduction + · exact ReductionOutcome.pure valid' reduction + cases mode <;> cases transientWork <;> + simp only [collapse_beq', stuck_beq', Bool.not_false, Bool.not_true, Bool.and_false, + Bool.and_true, Bool.false_eq_true, ↓reduceIte] + · -- collapse, non-transient: lookup, then charge and guarded publication + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind (get : TcM .anon (TcState .anon)) _ checked) + rw [EStateM.bind, get_run''] + dsimp only + cases lookup : checked.env.whnfCache[key]? with + | some cached => + exact ReductionOutcome.pure transient (transient.semantics.whnf.hit (partition := .full) + lookup address (hits locals context bounds checked transient .full key cached term + lookup address)) + | none => + rw [ReaderT.run_bind] + exact miss (fun current => !current.inNativeReduce) checked transient + all_goals + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind ((RecM.whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods) _ checked) + have charged := whnfWithNatSuccModeMissCharge_preserves (methods := methods) + locals context bounds checked transient + cases chargeRun : (RecM.whnfWithNatSuccModeMissCharge : RecM .anon Unit).run methods checked with + | error error failed => + rw [EStateM.bind, chargeRun] + simp only [chargeRun] at charged + exact charged + | ok _ charging => + rw [chargeRun] at charged + simp only [] at charged + rw [EStateM.bind, chargeRun] + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind ((RecM.whnfWithNatSuccModeUncached term _).run methods) _ charging) + apply ReductionOutcome.bind + (body term _ locals context bounds charging reading charged reads typed) + intro result reduced valid' reduction + rw [ReaderT.run_bind] + change ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds term + (EStateM.bind (get : TcM .anon (TcState .anon)) _ reduced) + rw [EStateM.bind, get_run''] + exact ReductionOutcome.pure valid' reduction + +end Full + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/WhnfSteps.lean b/Ix/Kernel/Verify/Consistency/WhnfSteps.lean new file mode 100644 index 000000000..199d41a5b --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/WhnfSteps.lean @@ -0,0 +1,1292 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.WhnfLayers +import Ix.Kernel.Verify.Consistency.ScopedInstUniv +import Ix.Kernel.Verify.Consistency.LetWhnfPlan +import Ix.Kernel.Verify.Consistency.AppSpineSource +import Ix.Kernel.Verify.Consistency.BetaPrefixPlan + +/-! +# WHNF step cases under the generic contracts + +The step bodies of the three bounded WHNF loops are proved sound one branch +at a time. Leaves finish reflexively; a loose bound variable never reads; +an explicit let substitutes its value, which the reader already did; a +let-bound free variable needs the local let-value agreement the invariant +does not record; delta unfolding of a definition is `ConversionClaim.delta` +through the unfold memo and the universe instantiation walker, with checked +typing of the unfolded body a model-side obligation; the accelerated +reducers return nothing under `noAccel`; an application runs its head through +the recursive method table, then either beta-reduces a returned lambda by the +simultaneous substitution of the beta track or rebuilds the spine. Projection, +iota, and the literal and quotient reducers remain seams, collected with the +walker and memo resources in `WhnfSeamAssumptions` so that later work +discharges them field by field. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-! ### Static bindings and seams -/ + +section Seams + +variable {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (source : Ixon.Env) + (catalog : List (SourceCacheRequest source)) + +/-- A loaded definition's binding to its admitted model entry: the resolved +reference, the entry with its body, and the closed reading of the loaded +value as that body. -/ +structure DefinitionBinding (id : KId .anon) (value : KExpr .anon) (ref : ConstRef β) + (entry : ConstantEntry β) (body : AExpr β) : Prop where + resolved : resolve id.addr = some ref + found : entries ref = some entry + bodyFound : entry.body = some body + reading : readScopedExpr? resolve [] value = some body.erase + +/-- Walker resources of one multi-argument beta step: the simultaneous +substitution bounds and collision freedom, and collision freedom of the +interned argument suffix chain. -/ +structure BetaWalkerResources (state : TcState .anon) (rawBody : KExpr .anon) + (consumed rawArguments : Array (KExpr .anon)) : Prop where + bounds : SimulSubstBounds rawBody consumed.reverse 0 + faithful : KExpr.CollisionFree fun term => state.env.intern.ExprSupport term ∨ + KExpr.SimulSubstReach consumed.reverse rawBody 0 term + suffix : KExpr.CollisionFree fun term => + (simulSubst rawBody consumed.reverse 0 state.env.intern).2.ExprSupport term ∨ + term ∈ cheapBetaChainList (simulSubst rawBody consumed.reverse 0 state.env.intern).1 + (rawArguments.extract consumed.size rawArguments.size).toList + +/-- The remaining obligations of the WHNF bodies, per method table. Each is +either a run resource (lookup data, walker resources, memo identification), +a model-side fact (checked bodies), or a reducer seam left to later work. -/ +structure WhnfSeamAssumptions (methods : Methods .anon) : Prop where + /-- Standalone conversion data for every lookup from an invariant state. -/ + lookups : LookupData.{u,v} resolve anchor entries source catalog + /-- Every consulted WHNF memo entry was produced from the query. -/ + hits : WhnfHitData.{u,v} resolve anchor entries source catalog + /-- Syntactic closure of the admitted interface. -/ + wellFormed : entries.WF + /-- Every admitted body, instantiated and read through the walker's + universe simplifications, is checked at the declared instantiated type in + every context. This is the model-side obligation of delta. -/ + bodies : ∀ (context : Model.Context β) (ref : ConstRef β) (entry : ConstantEntry β) + (body output : AExpr β) (levels : List VLevel), + entries ref = some entry → entry.body = some body → levels.length = entry.universes → + AExpr.LevelEquivalent (body.instL levels) output → + CheckedTyping.{u,v} entries context output (entry.type.instL levels) + /-- Every loaded reducible definition has a static binding. -/ + definitions : ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (state : TcState .anon) (id : KId .anon) (name : Mode.anon.F Name) + (levelParams : Mode.anon.F (Array Name)) (kind : Ix.DefKind) (safety : Ix.DefinitionSafety) + (hints : Lean.ReducibilityHints) (lvls : UInt64) (ty val : KExpr .anon) + (leanAll : Mode.anon.F (Array (KId .anon))) (block : KId .anon), + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + state.env.get? id = some (.defn name levelParams kind safety hints lvls ty val leanAll block) → + kind ≠ .opaq → ∃ ref entry body, DefinitionBinding resolve entries id val ref entry body + /-- Every consulted unfold entry was produced from the head that keys it. -/ + unfoldFaithful : ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (state : TcState .anon) (id : KId .anon) (arguments : Array (KUniv .anon)) (info : ExprInfo .anon) + (value : KExpr .anon), + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + state.env.unfoldCache[(KExpr.const id arguments info).addr]? = some value → + ∀ (id' : KId .anon) (arguments' : Array (KUniv .anon)) (info' : ExprInfo .anon), + (KExpr.const id' arguments' info').addr = (KExpr.const id arguments info).addr → + KExpr.const id' arguments' info' = KExpr.const id arguments info + /-- Walker resources of universe instantiation at every invariant state. -/ + instantiation : ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (state : TcState .anon) (term : KExpr .anon) (arguments : Array (KUniv .anon)), + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + UniverseInstantiationSupport state term arguments + /-- Collision freedom of the interned application chains. -/ + chains : ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (state : TcState .anon) (head : KExpr .anon) (arguments : List (KExpr .anon)), + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + KExpr.CollisionFree fun term => state.env.intern.ExprSupport term ∨ + term ∈ cheapBetaChainList head arguments + /-- Let-value agreement of the local context: a let-bound free variable is + a generic reduction to its stored value. -/ + letValue : ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (state : TcState .anon) (id : FVarId) (name declName : Mode.anon.F Name) (info : ExprInfo .anon) + (type value : KExpr .anon), + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + state.lctx.find? id = some (.ldecl declName type value) → + GenericReduction.{u,v} resolve entries (.fvar id name info) value + /-- Walker resources of explicit-let substitution. -/ + letResources : ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (state : TcState .anon) (term : KExpr .anon), + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + LetStepSource.selected term = true → LetStepSource.Resources term state + /-- The projection branch of the structural step. -/ + projection : ∀ (flags : WhnfFlags) (locals : List FVarId) (context : Model.Context β) + (bounds : List VLevel) (before : TcState .anon) (term : AExpr β) (id : KId .anon) + (field : UInt64) (value : KExpr .anon) (info : ExprInfo .anon), + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before → + readScopedExpr? resolve locals (.prj id field value info) = some term.erase → + (∃ type, CheckedTyping.{u,v} entries context term type) → + StepOutcome.{u,v} resolve anchor entries source catalog (fun current : KExpr .anon => current) locals context bounds + (.prj id field value info) ((RecM.whnfCoreWithFlagsStep (.prj id field value info) flags).run methods before) + /-- Walker resources of every multi-argument beta step at an invariant state. -/ + betaResources : ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (state : TcState .anon) (rawBody : KExpr .anon) (consumed rawArguments : Array (KExpr .anon)), + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + BetaWalkerResources state rawBody consumed rawArguments + /-- Iota reduction with the structural flags. -/ + iota : ∀ flags, SoundReducer.{u,v} resolve anchor entries source catalog + (fun term => (RecM.tryIotaWithFlags term flags).run methods) + /-- The reducer probes of the no-delta and full loops. -/ + projApp : ∀ flags, SoundReducer.{u,v} resolve anchor entries source catalog + (fun term => (RecM.tryProjAppReduceFinished term flags).run methods) + nat : ∀ mode, SoundReducer.{u,v} resolve anchor entries source catalog + (fun term => (RecM.tryReduceNatWithSuccMode term mode).run methods) + string : SoundReducer.{u,v} resolve anchor entries source catalog + (fun term => (RecM.tryReduceString term).run methods) + projectionDefinition : SoundReducer.{u,v} resolve anchor entries source catalog + (fun term => (RecM.tryReduceProjectionDefinition term).run methods) + quot : SoundReducer.{u,v} resolve anchor entries source catalog + (fun term => (RecM.tryQuotReduce term).run methods) + natOffsetStuck : SoundReducer.{u,v} resolve anchor entries source catalog + (fun term => (RecM.tryNatOffsetStuck term).run methods) + +end Seams + +/-! ### Probe combinators -/ + +section Probes + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +theorem ReducerOutcome.absent {input : KExpr .anon} {state : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) : + ReducerOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (.ok none state) := valid + +theorem ReducerOutcome.found {input reduced : KExpr .anon} {state : TcState .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) + (reduction : GenericReduction.{u,v} resolve entries input reduced) : + ReducerOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (.ok (some reduced) state) := ⟨valid, reduction⟩ + +/-- An invariant-preserving prefix followed by a continuation that knows the +prefix's run equation. -/ +theorem ReducerOutcome.bindPreserving {α : Type} {input : KExpr .anon} {action : TcM .anon α} + {next : α → TcM .anon (Option (KExpr .anon))} {before : TcState .anon} + (first : PreservesInvariant.{u,v} resolve anchor entries source catalog action) + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (rest : ∀ value middle, action before = .ok value middle → + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds middle → + ReducerOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (next value middle)) : + ReducerOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (EStateM.bind action next before) := by + have intermediate := first locals context bounds before valid + cases run : action before with + | error error after => + rw [EStateM.bind, run] + simp only [run] at intermediate + exact intermediate + | ok value after => + simp only [run] at intermediate + rw [EStateM.bind, run] + exact rest value after run intermediate + +/-- A probe outcome followed by a continuation on its absence. -/ +theorem ReducerOutcome.bindProbe {input : KExpr .anon} {probe : TcM .anon (Option (KExpr .anon))} + {next : Option (KExpr .anon) → TcM .anon (Option (KExpr .anon))} {before : TcState .anon} + (first : ReducerOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (probe before)) + (found : ∀ reduced middle, + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds middle → + GenericReduction.{u,v} resolve entries input reduced → + ReducerOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (next (Option.some reduced) middle)) + (absent : ∀ middle, + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds middle → + ReducerOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (next Option.none middle)) : + ReducerOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (EStateM.bind probe next before) := by + cases run : probe before with + | error error after => + rw [EStateM.bind, run] + simp only [run] at first + exact first + | ok value after => + rw [run] at first + rw [EStateM.bind, run] + cases value with + | none => exact absent after first + | some reduced => exact found reduced after first.1 first.2 + +end Probes + +/-! ### Delta -/ + +section Delta + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {methods : Methods .anon} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} + +private theorem get_run (state : TcState .anon) : + (get : TcM .anon (TcState .anon)) state = .ok state state := rfl + +theorem levelEquivalent_conversion {left right : AExpr β} + (same : AExpr.LevelEquivalent left right) (context : Model.Context β) : + ConversionClaim.{u,v} entries context left right := + fun _ _ constants _ levels env _ => same.interp constants levels env + +/-- A successful `tryGetConst` leaves the declaration loaded. -/ +theorem tryGetConst_loaded {id : KId .anon} {concrete : KConst .anon} {before after : TcState .anon} + (run : TcM.tryGetConst id before = .ok (some concrete) after) : + after.env.get? id = some concrete := by + apply getConst_result_loaded (id := id) (before := before) + unfold TcM.getConst + change EStateM.bind (TcM.tryGetConst id) _ before = _ + rw [EStateM.bind, run] + rfl + +/-- The interning loop of delta unfolding is a monadic fold. -/ +theorem internLoop_eq_foldlM (base : KExpr .anon) (arguments : Array (KExpr .anon)) : + (forIn (m := RecM .anon) arguments base fun arg acc => do + let result ← liftM (TcM.intern (KExpr.mkApp acc arg)) + pure (ForInStep.yield result)) = + arguments.foldlM (fun acc arg => liftM (TcM.intern (KExpr.mkApp acc arg))) base := by + simp [Array.forIn_yield_eq_foldlM] + +/-- The interning loop of delta unfolding is the interned application chain. -/ +theorem internLoop_run (base : KExpr .anon) (arguments : Array (KExpr .anon)) : + (forIn (m := RecM .anon) arguments base fun arg acc => do + let result ← liftM (TcM.intern (KExpr.mkApp acc arg)) + pure (ForInStep.yield result)).run methods = + TcM.runIntern (internAppChain base arguments.toList) := by + funext before + rw [internLoop_eq_foldlM, ← Array.foldlM_toList] + generalize arguments.toList = remaining + induction remaining generalizing base before with + | nil => rfl + | cons argument remaining ih => + rw [List.foldlM_cons, ReaderT.run_bind, ReaderT.run_monadLift] + change EStateM.bind (TcM.intern (KExpr.mkApp base argument)) _ before = _ + unfold EStateM.bind TcM.intern TcM.runIntern + exact ih _ _ + +/-- The unfold memo: a hit is the recorded instantiation of the head, a miss +runs the universe instantiation walker and publishes its result. -/ +theorem unfoldConstValue_ok (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + {state after : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {value result : KExpr .anon} {ref : ConstRef β} {entry : ConstantEntry β} + {body : AExpr β} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) + (binding : DefinitionBinding resolve entries id value ref entry body) + (arity : arguments.size = entry.universes) + (run : (RecM.unfoldConstValue (.const id arguments info) value arguments).run methods state = + .ok result after) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + ∃ output : AExpr β, readScopedExpr? resolve [] result = some output.erase ∧ + AExpr.LevelEquivalent (body.instL (arguments.toList.map readLevel)) output := by + unfold RecM.unfoldConstValue at run + rw [ReaderT.run_bind] at run + change EStateM.bind (get : TcM .anon (TcState .anon)) _ state = _ at run + rw [EStateM.bind, get_run] at run + dsimp only at run + cases lookup : state.env.unfoldCache[(KExpr.const id arguments info).addr]? with + | some cached => + rw [lookup] at run + change EStateM.Result.ok cached state = _ at run + cases run + obtain ⟨ref', entry', body', output, resolved', found', bodyFound', _, reads, same⟩ := + valid.semantics.unfold.hit lookup + (seams.unfoldFaithful _ _ _ _ _ _ _ _ valid lookup) + cases Option.some.inj (resolved'.symm.trans binding.resolved) + cases Option.some.inj (found'.symm.trans binding.found) + cases Option.some.inj (bodyFound'.symm.trans binding.bodyFound) + exact ⟨valid, output, reads, same⟩ + | none => + rw [lookup] at run + dsimp only at run + rw [ReaderT.run_bind, ReaderT.run_monadLift] at run + change EStateM.bind (TcM.instantiateUnivParams value arguments) _ state = _ at run + have support := seams.instantiation locals context bounds state value arguments valid + have post := TcM.instantiateUnivParams_wf support.faithful (fun _ reach => Or.inr reach) + ⟨support.coherent, fun _ member => Or.inl member⟩ + cases instantiated : TcM.instantiateUnivParams value arguments state with + | error error failed => + rw [EStateM.bind, instantiated] at run + cases run + | ok unfolded walked => + rw [instantiated] at post + rw [EStateM.bind, instantiated] at run + change EStateM.Result.ok unfolded {walked with env := {walked.env with + unfoldCache := walked.env.unfoldCache.insert (KExpr.const id arguments info).addr unfolded}} = + _ at run + cases run + obtain ⟨⟨coherent, _⟩, _, stateEq, _⟩ := post + have levelScope := (seams.wellFormed.bodyScope ref entry binding.found body binding.bodyFound).erase.1 + rw [← arity] at levelScope + obtain ⟨output, reads, same⟩ := instantiateUnivParams_readScopedAnnotated (locals := []) + support levelScope binding.reading instantiated + have valid' := valid.ofIntern (table := walked.env.intern) coherent + rw [← stateEq] at valid' + exact ⟨valid'.publishUnfold binding.resolved binding.found binding.bodyFound arity.symm reads same, + output, reads, same⟩ + +theorem unfoldConstValue_error (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + {state after : TcState .anon} {id : KId .anon} {arguments : Array (KUniv .anon)} + {info : ExprInfo .anon} {value : KExpr .anon} {error : TcError .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) + (run : (RecM.unfoldConstValue (.const id arguments info) value arguments).run methods state = + .error error after) : + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds after := by + unfold RecM.unfoldConstValue at run + rw [ReaderT.run_bind] at run + change EStateM.bind (get : TcM .anon (TcState .anon)) _ state = _ at run + rw [EStateM.bind, get_run] at run + dsimp only at run + cases lookup : state.env.unfoldCache[(KExpr.const id arguments info).addr]? with + | some cached => + rw [lookup] at run + change EStateM.Result.ok cached state = _ at run + cases run + | none => + rw [lookup] at run + dsimp only at run + rw [ReaderT.run_bind, ReaderT.run_monadLift] at run + change EStateM.bind (TcM.instantiateUnivParams value arguments) _ state = _ at run + have support := seams.instantiation locals context bounds state value arguments valid + have post := TcM.instantiateUnivParams_wf support.faithful (fun _ reach => Or.inr reach) + ⟨support.coherent, fun _ member => Or.inl member⟩ + cases instantiated : TcM.instantiateUnivParams value arguments state with + | error failure failed => + rw [instantiated] at post + rw [EStateM.bind, instantiated] at run + cases run + obtain ⟨⟨coherent, _⟩, stateEq, _⟩ := post + have valid' := valid.ofIntern (table := _) coherent + rw [← stateEq] at valid' + exact valid' + | ok unfolded walked => + rw [EStateM.bind, instantiated] at run + change EStateM.Result.ok unfolded {walked with env := {walked.env with + unfoldCache := walked.env.unfoldCache.insert (KExpr.const id arguments info).addr unfolded}} = + _ at run + cases run + +/-- Invariant preservation of the unfold memo on both outcomes. -/ +theorem unfoldConstValue_preserves + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + {id : KId .anon} {arguments : Array (KUniv .anon)} {info : ExprInfo .anon} {value : KExpr .anon} + {ref : ConstRef β} {entry : ConstantEntry β} {body : AExpr β} + (binding : DefinitionBinding resolve entries id value ref entry body) + (arity : arguments.size = entry.universes) : + PreservesInvariant.{u,v} resolve anchor entries source catalog + ((RecM.unfoldConstValue (.const id arguments info) value arguments).run methods) := by + intro locals context bounds state valid + cases run : (RecM.unfoldConstValue (.const id arguments info) value arguments).run methods state with + | ok result after => exact (unfoldConstValue_ok seams valid binding arity run).1 + | error error after => exact unfoldConstValue_error seams valid run + +/-- A constant whose reading is a constant instance reads to that instance. -/ +theorem constant_reading {id : KId .anon} {levels : Array (KUniv .anon)} {info : ExprInfo .anon} + {head : AExpr β} {ref : ConstRef β} (resolved : resolve id.addr = some ref) + (reads : readScopedExpr? resolve locals (KExpr.const id levels info) = some head.erase) : + head = .const ref (levels.toList.map readLevel) := by + simp [readScopedExpr?, resolved] at reads + cases head <;> simp_all [AExpr.erase] + +/-- The head of a checked constant spine has the arity of its entry. -/ +theorem constant_spine_arity {term : AExpr β} {ref : ConstRef β} {levels : List VLevel} + {arguments : List (AExpr β)} {entry : ConstantEntry β} + (same : term = (AExpr.const ref levels).appN arguments) + (typed : ∃ type, CheckedTyping.{u,v} entries context term type) + (found : entries ref = some entry) : levels.length = entry.universes := by + obtain ⟨type, checked⟩ := typed + rw [same] at checked + obtain ⟨headType, headChecked⟩ := checked.headTyped + obtain ⟨entry', found', arity⟩ := headChecked.constArity rfl + cases Option.some.inj (found'.symm.trans found) + exact arity + +/-- Unfolding a definition instance under an application spine is a generic +reduction: delta, universe level equivalence, and application congruence, +with checked typing of the unfolded body supplied by the interface. -/ +theorem unfolded_spine_reduction + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + {state : TcState .anon} {input : KExpr .anon} {id : KId .anon} {levels : Array (KUniv .anon)} + {info : ExprInfo .anon} {arguments : Array (KExpr .anon)} {unfolded : KExpr .anon} + {value : KExpr .anon} {ref : ConstRef β} {entry : ConstantEntry β} {body output : AExpr β} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) + (spine : input.collectSpine = (KExpr.const id levels info, arguments)) + (binding : DefinitionBinding resolve entries id value ref entry body) + (readsOutput : readScopedExpr? resolve [] unfolded = some output.erase) + (same : AExpr.LevelEquivalent (body.instL (levels.toList.map readLevel)) output) : + GenericReduction.{u,v} resolve entries input + (internAppChain unfolded arguments.toList state.env.intern).1 := by + intro locals' context' term' reads' typed' + obtain ⟨termEq, headReads, argumentReads⟩ := AppSpineSource.reading reads' + rw [spine] at headReads argumentReads + generalize AppSpineSource.parts input term' = parts at termEq headReads argumentReads + obtain ⟨head', arguments'⟩ := parts + have headEq := constant_reading binding.resolved headReads + subst headEq + subst termEq + have arity := constant_spine_arity rfl typed' binding.found + obtain ⟨reads, _⟩ := internAppChain_readScopedExpr? valid.coherent + (seams.chains locals context bounds state unfolded arguments.toList valid) + (readScopedExpr?_weaken_closed readsOutput locals') argumentReads + refine ⟨output.appN arguments', reads, ?_, ?_⟩ + · exact ConversionClaim.appN ((ConversionClaim.delta binding.found binding.bodyFound arity).trans + (levelEquivalent_conversion same context')) _ + · intro type checked + exact CheckedTyping.rewriteHead (fun _ headChecked => headChecked.unfoldConst rfl binding.found + (seams.bodies context' ref entry body output _ binding.found binding.bodyFound arity same)) checked + +/-- Unfolding a bare definition instance is a generic reduction. -/ +theorem unfolded_constant_reduction + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + {id : KId .anon} {levels : Array (KUniv .anon)} {info : ExprInfo .anon} {unfolded : KExpr .anon} + {value : KExpr .anon} {ref : ConstRef β} {entry : ConstantEntry β} {body output : AExpr β} + (binding : DefinitionBinding resolve entries id value ref entry body) + (readsOutput : readScopedExpr? resolve [] unfolded = some output.erase) + (same : AExpr.LevelEquivalent (body.instL (levels.toList.map readLevel)) output) : + GenericReduction.{u,v} resolve entries (KExpr.const id levels info) unfolded := by + intro locals' context' term' reads' typed' + have headEq := constant_reading binding.resolved reads' + have arity := constant_spine_arity (arguments := []) headEq typed' binding.found + refine ⟨output, readScopedExpr?_weaken_closed readsOutput locals', ?_, ?_⟩ + · rw [headEq] + exact (ConversionClaim.delta binding.found binding.bodyFound arity).trans (levelEquivalent_conversion same context') + · intro type checked + rw [headEq] at checked + exact checked.unfoldConst rfl binding.found + (seams.bodies context' ref entry body output _ binding.found binding.bodyFound arity same) + +/-- The arity of a constant-headed spine, from the current scope's reading and typing. -/ +theorem spine_arity_of_reading {input : KExpr .anon} {term : AExpr β} {id : KId .anon} + {levels : Array (KUniv .anon)} {info : ExprInfo .anon} {arguments : Array (KExpr .anon)} + {ref : ConstRef β} {entry : ConstantEntry β} + (reads : readScopedExpr? resolve locals input = some term.erase) + (typed : ∃ type, CheckedTyping.{u,v} entries context term type) + (spine : input.collectSpine = (KExpr.const id levels info, arguments)) + (resolved : resolve id.addr = some ref) (found : entries ref = some entry) : + levels.size = entry.universes := by + obtain ⟨termEq, headReads, _⟩ := AppSpineSource.reading reads + rw [spine] at headReads + have headEq := constant_reading resolved headReads + rw [headEq] at termEq + have arity := constant_spine_arity termEq typed found + simpa only [List.length_map, Array.length_toList] using arity + +/-- Delta unfolding of a head-applied or bare definition instance. -/ +theorem tryDeltaUnfold_sound + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) : + SoundReducer.{u,v} resolve anchor entries source catalog + (fun input => (RecM.tryDeltaUnfold input).run methods) := by + intro locals context bounds before term input valid reads typed + dsimp only + unfold RecM.tryDeltaUnfold + cases spine : input.collectSpine with + | mk head arguments => + cases head with + | const id levels info => + dsimp only + rw [ReaderT.run_bind, ReaderT.run_monadLift] + change ReducerOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (EStateM.bind (TcM.tryGetConst id) _ before) + apply ReducerOutcome.bindPreserving (PreservesInvariant.tryGetConst seams.lookups id) valid + intro found middle lookup valid₁ + cases found with + | none => exact ReducerOutcome.absent valid₁ + | some constant => + cases constant with + | defn name levelParams kind safety hints lvls ty val leanAll block => + have loaded := tryGetConst_loaded lookup + have unfold : ∀ (proceed : kind ≠ .opaq), + ReducerOutcome.{u,v} resolve anchor entries source catalog locals context bounds + input (ReaderT.run (do + let val ← pure val + let val ← RecM.unfoldConstValue (KExpr.const id levels info) val levels + let acc ← forIn arguments val fun arg acc => do + let result ← liftM (TcM.intern (KExpr.mkApp acc arg)) + pure (ForInStep.yield result) + pure (some acc)) methods middle) := by + intro proceed + obtain ⟨ref, entry, body, binding⟩ := seams.definitions locals context bounds middle + id name levelParams kind safety hints lvls ty val leanAll block valid₁ loaded proceed + have arity := spine_arity_of_reading reads typed spine binding.resolved binding.found + rw [pure_bind, ReaderT.run_bind] + change ReducerOutcome.{u,v} resolve anchor entries source catalog locals context + bounds input (EStateM.bind + ((RecM.unfoldConstValue (KExpr.const id levels info) val levels).run methods) _ middle) + apply ReducerOutcome.bindPreserving + (unfoldConstValue_preserves (info := info) seams binding arity) valid₁ + intro unfolded reduced run valid₂ + obtain ⟨_, output, readsOutput, same⟩ := unfoldConstValue_ok seams valid₁ binding arity run + rw [ReaderT.run_bind, internLoop_run] + obtain ⟨_, coherent⟩ := internAppChain_readScopedExpr? valid₂.coherent + (seams.chains locals context bounds reduced unfolded arguments.toList valid₂) + (readScopedExpr?_weaken_closed readsOutput locals) + (by + obtain ⟨_, _, argumentReads⟩ := AppSpineSource.reading reads + rw [spine] at argumentReads + exact argumentReads) + exact ReducerOutcome.found (valid₂.ofIntern coherent) + (unfolded_spine_reduction seams valid₂ spine binding readsOutput same) + dsimp only + cases kind with + | opaq => exact ReducerOutcome.absent valid₁ + | defn => exact unfold (by intro same; cases same) + | thm => exact unfold (by intro same; cases same) + | _ => exact ReducerOutcome.absent valid₁ + | _ => exact ReducerOutcome.absent valid + +/-- One delta step: the spine unfolding, then the bare-constant retry. -/ +theorem deltaUnfoldOne_sound + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) : + SoundReducer.{u,v} resolve anchor entries source catalog + (fun input => (RecM.deltaUnfoldOne input).run methods) := by + intro locals context bounds before term input valid reads typed + dsimp only + unfold RecM.deltaUnfoldOne + rw [ReaderT.run_bind] + change ReducerOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (EStateM.bind ((RecM.tryDeltaUnfold input).run methods) _ before) + apply ReducerOutcome.bindProbe (tryDeltaUnfold_sound seams locals context bounds before term input valid + reads typed) + · intro reduced middle valid₁ reduction + exact ReducerOutcome.found valid₁ reduction + · intro middle valid₁ + cases input with + | const id levels info => + dsimp only + rw [ReaderT.run_bind, ReaderT.run_monadLift] + change ReducerOutcome.{u,v} resolve anchor entries source catalog locals context bounds + (KExpr.const id levels info) (EStateM.bind (TcM.tryGetConst id) _ middle) + apply ReducerOutcome.bindPreserving (PreservesInvariant.tryGetConst seams.lookups id) valid₁ + intro found looked lookup valid₂ + cases found with + | none => exact ReducerOutcome.absent valid₂ + | some constant => + cases constant with + | defn name levelParams kind safety hints lvls ty val leanAll block => + have loaded := tryGetConst_loaded lookup + have unfold : ∀ (proceed : kind ≠ .opaq), + ReducerOutcome.{u,v} resolve anchor entries source catalog locals context bounds + (KExpr.const id levels info) (ReaderT.run (do + let unfolded ← RecM.unfoldConstValue (KExpr.const id levels info) val levels + pure (some unfolded)) methods looked) := by + intro proceed + obtain ⟨ref, entry, body, binding⟩ := seams.definitions locals context bounds looked + id name levelParams kind safety hints lvls ty val leanAll block valid₂ loaded proceed + have arity := spine_arity_of_reading (id := id) (levels := levels) (info := info) + (arguments := #[]) reads typed (by simp [KExpr.collectSpine, KExpr.collectSpine.go]) + binding.resolved binding.found + rw [ReaderT.run_bind] + change ReducerOutcome.{u,v} resolve anchor entries source catalog locals context + bounds (KExpr.const id levels info) (EStateM.bind + ((RecM.unfoldConstValue (KExpr.const id levels info) val levels).run methods) _ looked) + apply ReducerOutcome.bindPreserving + (unfoldConstValue_preserves (info := info) seams binding arity) valid₂ + intro unfolded reduced run valid₃ + obtain ⟨_, output, readsOutput, same⟩ := + unfoldConstValue_ok seams valid₂ binding arity run + exact ReducerOutcome.found valid₃ + (unfolded_constant_reduction seams binding readsOutput same) + dsimp only + cases kind with + | opaq => exact ReducerOutcome.absent valid₂ + | defn => exact unfold (by intro same; cases same) + | thm => exact unfold (by intro same; cases same) + | _ => exact ReducerOutcome.absent valid₂ + | _ => exact ReducerOutcome.absent valid₁ + +end Delta + +/-! ### Step combinators -/ + +section StepCombinators + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +/-- A probe outcome followed by a step continuation on each answer. -/ +theorem StepOutcome.bindProbe {σ : Type} {view : σ → KExpr .anon} {current : σ} {input : KExpr .anon} + {probe : TcM .anon (Option (KExpr .anon))} + {next : Option (KExpr .anon) → TcM .anon (RecM.BoundedStep σ (KExpr .anon))} {before : TcState .anon} + (first : ReducerOutcome.{u,v} resolve anchor entries source catalog locals context bounds input + (probe before)) + (found : ∀ reduced middle, + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds middle → + GenericReduction.{u,v} resolve entries input reduced → + StepOutcome.{u,v} resolve anchor entries source catalog view locals context bounds current + (next (Option.some reduced) middle)) + (absent : ∀ middle, + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds middle → + StepOutcome.{u,v} resolve anchor entries source catalog view locals context bounds current + (next Option.none middle)) : + StepOutcome.{u,v} resolve anchor entries source catalog view locals context bounds current + (EStateM.bind probe next before) := by + cases run : probe before with + | error error after => + rw [EStateM.bind, run] + simp only [run] at first + exact first + | ok value after => + rw [run] at first + rw [EStateM.bind, run] + cases value with + | none => exact absent after first + | some reduced => exact found reduced after first.1 first.2 + +/-- A reduction outcome followed by a step continuation on its result. -/ +theorem StepOutcome.bindReduction {σ : Type} {view : σ → KExpr .anon} {current : σ} + {action : TcM .anon (KExpr .anon)} + {next : KExpr .anon → TcM .anon (RecM.BoundedStep σ (KExpr .anon))} {before : TcState .anon} + (first : ReductionOutcome.{u,v} resolve anchor entries source catalog locals context bounds + (view current) (action before)) + (rest : ∀ result middle, + ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds middle → + GenericReduction.{u,v} resolve entries (view current) result → + StepOutcome.{u,v} resolve anchor entries source catalog view locals context bounds current + (next result middle)) : + StepOutcome.{u,v} resolve anchor entries source catalog view locals context bounds current + (EStateM.bind action next before) := by + cases run : action before with + | error error after => + rw [EStateM.bind, run] + simp only [run] at first + exact first + | ok result after => + rw [run] at first + rw [EStateM.bind, run] + exact rest result after first.1 first.2 + +/-- A step outcome at an intermediate term transports along a generic +reduction from the current term. -/ +theorem StepOutcome.transport {current reduced : KExpr .anon} + {outcome : EStateM.Result (TcError .anon) (TcState .anon) (RecM.BoundedStep (KExpr .anon) (KExpr .anon))} + (later : StepOutcome.{u,v} resolve anchor entries source catalog (fun term : KExpr .anon => term) + locals context bounds reduced outcome) + (reduction : GenericReduction.{u,v} resolve entries current reduced) : + StepOutcome.{u,v} resolve anchor entries source catalog (fun term : KExpr .anon => term) + locals context bounds current outcome := by + cases outcome with + | error error after => exact later + | ok value after => + cases value with + | next next => exact ⟨later.1, reduction.trans later.2⟩ + | done result => exact ⟨later.1, reduction.trans later.2⟩ + +end StepCombinators + +/-! ### The application branch -/ + +section ApplicationStep + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {methods : Methods .anon} + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} + +theorem consumeBetaLamsFuel_size : + ∀ (fuel : Nat) (body : KExpr .anon) (args consumed : Array (KExpr .anon)), + consumed.size ≤ (RecM.consumeBetaLamsFuel fuel body args consumed).2.size + | 0, _, _, _ => Nat.le_refl _ + | fuel + 1, body, args, consumed => by + unfold RecM.consumeBetaLamsFuel + split + · exact Nat.le_refl _ + · cases body with + | lam _ _ _ inner _ => + exact Nat.le_trans (by simp) (consumeBetaLamsFuel_size fuel inner args (consumed.push _)) + | _ => exact Nat.le_refl _ + +/-- Peeling a lambda against a nonempty spine consumes at least one argument. -/ +theorem consumeBetaLams_lam_nonempty {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body rawBody : KExpr .anon} {info : ExprInfo .anon} {args consumed : Array (KExpr .anon)} + (nonempty : 0 < args.size) + (peeling : RecM.consumeBetaLams (.lam name bi domain body info) args = (rawBody, consumed)) : + (!consumed.isEmpty) = true := by + unfold RecM.consumeBetaLams at peeling + obtain ⟨size, sizeEq⟩ : ∃ size, args.size = size + 1 := ⟨args.size - 1, by omega⟩ + rw [sizeEq] at peeling + have zero : (Array.mkEmpty (size + 1) : Array (KExpr .anon)).size = 0 := rfl + have first : RecM.consumeBetaLamsFuel (size + 1) (.lam name bi domain body info) args + (Array.mkEmpty (size + 1) : Array (KExpr .anon)) = RecM.consumeBetaLamsFuel size body args + ((Array.mkEmpty (size + 1) : Array (KExpr .anon)).push + args[(Array.mkEmpty (size + 1) : Array (KExpr .anon)).size]!) := by + rw [RecM.consumeBetaLamsFuel.eq_2, if_neg (by rw [zero, sizeEq]; omega)] + rw [first] at peeling + have bound := consumeBetaLamsFuel_size size body args + ((Array.mkEmpty (size + 1) : Array (KExpr .anon)).push + args[(Array.mkEmpty (size + 1) : Array (KExpr .anon)).size]!) + rw [peeling, Array.size_push, zero] at bound + cases empty : consumed.isEmpty with + | false => rfl + | true => + rw [Array.isEmpty_iff] at empty + subst empty + simp at bound + +/-- The annotated reading of a lambda is a lambda. -/ +theorem lambda_reading {name : Mode.anon.F Name} {bi : Mode.anon.F Lean.BinderInfo} + {domain body : KExpr .anon} {info : ExprInfo .anon} {target : AExpr β} + (reads : readScopedExpr? resolve locals (.lam name bi domain body info) = some target.erase) : + ∃ (condition : Certified.PropWhen) (A b : AExpr β), target = .lam condition A b := by + cases target with + | lam condition A b => exact ⟨condition, A, b, rfl⟩ + | _ => + cases hd : readScopedExpr? resolve locals domain <;> + cases hb : readScopedExpr? resolve locals body 1 <;> + simp [readScopedExpr?, hd, hb, AExpr.erase] at reads + +/-- The argument suffix loop, point-free. -/ +theorem finishAppResult_run (result : KExpr .anon) (args : Array (KExpr .anon)) (consumed : Nat) : + (RecM.finishAppResult result args consumed).run methods = + TcM.runIntern (internAppChain result (args.extract consumed args.size).toList) := by + funext before + exact RecM.finishAppResult_eq_internAppChain result args consumed methods before + +/-- Binding an intern-table action is its result at the updated table. -/ +theorem runIntern_bind {α γ : Type} (action : InternM .anon α) (next : α → TcM .anon γ) + (state : TcState .anon) : + (TcM.runIntern action >>= next) state = + next (action state.env.intern).1 + {state with env := {state.env with intern := (action state.env.intern).2}} := rfl + +/-- Multi-argument beta after the head callback returned a lambda: the +production output reads as the model beta prefix at every scope, is +convertible to the source by head conversion and beta, and is checked at +every type of the source. -/ +theorem beta_prefix_reduction + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + {middle : TcState .anon} {fn arg head rawBody : KExpr .anon} {info : ExprInfo .anon} + {args consumed : Array (KExpr .anon)} {name : Mode.anon.F Name} + {bi : Mode.anon.F Lean.BinderInfo} {domain body : KExpr .anon} {lamInfo : ExprInfo .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds middle) + (spine : (KExpr.app fn arg info).collectSpine = (head, args)) + (peeling : RecM.consumeBetaLams (.lam name bi domain body lamInfo) args = (rawBody, consumed)) + (nonempty : (!consumed.isEmpty) = true) + (headReduction : GenericReduction.{u,v} resolve entries head (.lam name bi domain body lamInfo)) : + GenericReduction.{u,v} resolve entries (.app fn arg info) + (internAppChain (simulSubst rawBody consumed.reverse 0 middle.env.intern).1 + (args.extract consumed.size args.size).toList + (simulSubst rawBody consumed.reverse 0 middle.env.intern).2).1 := by + intro locals' context' term' reads' typed' + obtain ⟨termEq, headReads, argumentReads⟩ := AppSpineSource.reading reads' + rw [spine] at headReads argumentReads + generalize AppSpineSource.parts (KExpr.app fn arg info) term' = parts at termEq headReads argumentReads + obtain ⟨headTerm, arguments⟩ := parts + subst termEq + obtain ⟨T, checked⟩ := typed' + obtain ⟨headType, headChecked⟩ := checked.headTyped + obtain ⟨lambda, lambdaReads, headConverted, headPreserved⟩ := + headReduction locals' context' headTerm headReads ⟨headType, headChecked⟩ + obtain ⟨condition, A, b, rfl⟩ := lambda_reading lambdaReads + have resources := seams.betaResources locals context bounds middle rawBody consumed args valid + let plan : BetaPrefixPlan resolve locals' middle := { + name, bi, rawDomain := domain, rawInner := body, lambdaInfo := lamInfo, rawArguments := args, + rawBody, consumed, condition, domain := A, inner := b, arguments + headReads := lambdaReads, argumentReads, peeling, nonempty + walkerBounds := resources.bounds, walkerFaithful := resources.faithful + suffixFaithful := resources.suffix } + obtain ⟨reads, _⟩ := plan.reading valid.coherent + have spineChecked : CheckedTyping.{u,v} entries context' ((AExpr.lam condition A b).appN arguments) T := + CheckedTyping.rewriteHead (fun _ headChecked' => headPreserved _ headChecked') checked + obtain ⟨converted, _⟩ := LambdaSpineTyping.betaPrefix (count := consumed.size) + spineChecked.lambdaSpine plan.counts.1 + refine ⟨plan.modelResult, reads, ?_, ?_⟩ + · exact (ConversionClaim.appN headConverted arguments).trans converted + · intro type checked' + exact CheckedTyping.betaPrefix seams.wellFormed consumed.size (.lam condition A b) arguments + (CheckedTyping.rewriteHead (fun _ headChecked' => headPreserved _ headChecked') checked') + +/-- Rebuilding the spine on a reduced head is a generic reduction by head +conversion and application congruence. -/ +theorem rebuilt_spine_reduction + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + {middle : TcState .anon} {fn arg head reduced : KExpr .anon} {info : ExprInfo .anon} + {args : Array (KExpr .anon)} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds middle) + (spine : (KExpr.app fn arg info).collectSpine = (head, args)) + (headReduction : GenericReduction.{u,v} resolve entries head reduced) : + GenericReduction.{u,v} resolve entries (.app fn arg info) + (internAppChain reduced (args.extract 0 args.size).toList middle.env.intern).1 := by + intro locals' context' term' reads' typed' + obtain ⟨termEq, headReads, argumentReads⟩ := AppSpineSource.reading reads' + rw [spine] at headReads argumentReads + generalize AppSpineSource.parts (KExpr.app fn arg info) term' = parts at termEq headReads argumentReads + obtain ⟨headTerm, arguments⟩ := parts + subst termEq + obtain ⟨T, checked⟩ := typed' + obtain ⟨headType, headChecked⟩ := checked.headTyped + obtain ⟨target, targetReads, headConverted, headPreserved⟩ := + headReduction locals' context' headTerm headReads ⟨headType, headChecked⟩ + obtain ⟨reads, _⟩ := internAppChain_readScopedExpr? valid.coherent + (seams.chains locals context bounds middle reduced (args.extract 0 args.size).toList valid) + targetReads (by simpa only [Array.extract_size] using argumentReads) + exact ⟨target.appN arguments, reads, ConversionClaim.appN headConverted arguments, + fun _ checked' => CheckedTyping.rewriteHead (fun _ headChecked' => headPreserved _ headChecked') checked'⟩ + +/-- The application branch of the structural step: the recursive head call +through the method table, then multi-argument beta on a lambda, head +rebuilding with one iota attempt, or iota on the original spine. -/ +theorem whnfCoreWithFlagsStep_app_sound + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + (callbacks : GenericWhnfContract.{u,v} resolve anchor entries source catalog methods) + (flags : WhnfFlags) {before : TcState .anon} {term : AExpr β} {fn arg : KExpr .anon} + {info : ExprInfo .anon} + (valid : ReductionInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (reads : readScopedExpr? resolve locals (.app fn arg info) = some term.erase) + (typed : ∃ type, CheckedTyping.{u,v} entries context term type) : + StepOutcome.{u,v} resolve anchor entries source catalog (fun current : KExpr .anon => current) + locals context bounds (.app fn arg info) + ((RecM.whnfCoreWithFlagsStep (.app fn arg info) flags).run methods before) := by + obtain ⟨termEq, headReads, argumentReads⟩ := AppSpineSource.reading reads + have nonempty := AppSpineSource.nonempty fn arg info + generalize spine : (KExpr.app fn arg info).collectSpine = pair at headReads argumentReads nonempty + obtain ⟨head, args⟩ := pair + generalize AppSpineSource.parts (KExpr.app fn arg info) term = parts at termEq headReads argumentReads + obtain ⟨headTerm, arguments⟩ := parts + have headTyped : ∃ headType, CheckedTyping.{u,v} entries context headTerm headType := by + obtain ⟨T, checked⟩ := typed + rw [termEq] at checked + exact checked.headTyped + have outcome := callbacks.whnfCoreFlags head flags locals context bounds before headTerm valid + headReads headTyped + cases headRun : methods.whnfCoreFlags head flags before with + | error error failed => + rw [headRun] at outcome + unfold RecM.whnfCoreWithFlagsStep + rw [ReaderT.run_bind, spine] + change StepOutcome.{u,v} resolve anchor entries source catalog (fun current : KExpr .anon => current) + locals context bounds (.app fn arg info) (EStateM.bind (methods.whnfCoreFlags head flags) _ before) + rw [EStateM.bind, headRun] + exact outcome + | ok reduced middle => + rw [headRun] at outcome + obtain ⟨valid', headReduction⟩ := outcome + have rebuiltReduction := rebuilt_spine_reduction seams valid' spine headReduction + obtain ⟨target, targetReads, _, _⟩ := headReduction locals context headTerm headReads headTyped + obtain ⟨_, coherence⟩ := internAppChain_readScopedExpr? valid'.coherent + (seams.chains locals context bounds middle reduced (args.extract 0 args.size).toList valid') + targetReads (by simpa only [Array.extract_size] using argumentReads) + obtain ⟨rebuiltTerm, rebuiltReads, _, rebuiltPreserved⟩ := + rebuiltReduction locals context term reads typed + have rebuiltTyped : ∃ type, CheckedTyping.{u,v} entries context rebuiltTerm type := by + obtain ⟨T, checked⟩ := typed + exact ⟨T, rebuiltPreserved T checked⟩ + cases reduced with + | lam name bi domain body lamInfo => + cases peeling : RecM.consumeBetaLams (.lam name bi domain body lamInfo) args with + | mk rawBody consumed => + have nonemptyConsumed := consumeBetaLams_lam_nonempty nonempty peeling + obtain ⟨condition, A, b, rfl⟩ := lambda_reading targetReads + have resources := seams.betaResources locals context bounds middle rawBody consumed args valid' + let plan : BetaPrefixPlan resolve locals middle := { + name, bi, rawDomain := domain, rawInner := body, lambdaInfo := lamInfo, rawArguments := args, + rawBody, consumed, condition, domain := A, inner := b, arguments + headReads := targetReads, argumentReads, peeling, nonempty := nonemptyConsumed + walkerBounds := resources.bounds, walkerFaithful := resources.faithful + suffixFaithful := resources.suffix } + rw [plan.run spine headRun] + exact ⟨valid'.ofIntern (plan.reading valid'.coherent).2, + beta_prefix_reduction seams valid' spine peeling nonemptyConsumed headReduction⟩ + | _ => + unfold RecM.whnfCoreWithFlagsStep + rw [ReaderT.run_bind, spine] + change StepOutcome.{u,v} resolve anchor entries source catalog (fun current : KExpr .anon => current) + locals context bounds (.app fn arg info) + (EStateM.bind (methods.whnfCoreFlags head flags) _ before) + rw [EStateM.bind, headRun] + dsimp only + split + · rw [ReaderT.run_bind, finishAppResult_run, runIntern_bind] + dsimp only + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog (fun current : KExpr .anon => current) + locals context bounds (.app fn arg info) + (EStateM.bind ((RecM.tryIotaWithFlags _ flags).run methods) _ _) + apply StepOutcome.bindProbe (seams.iota flags locals context bounds _ rebuiltTerm _ + (valid'.ofIntern coherence) rebuiltReads rebuiltTyped) + · intro next state valid₃ stepped + exact ⟨valid₃, rebuiltReduction.trans stepped⟩ + · intro state valid₃ + exact ⟨valid₃, rebuiltReduction⟩ + · rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog (fun current : KExpr .anon => current) + locals context bounds (.app fn arg info) + (EStateM.bind ((RecM.tryIotaWithFlags (.app fn arg info) flags).run methods) _ middle) + apply StepOutcome.bindProbe + (seams.iota flags locals context bounds middle term (.app fn arg info) valid' reads typed) + · intro next state valid₃ stepped + exact ⟨valid₃, stepped⟩ + · intro state valid₃ + exact ⟨valid₃, .refl _⟩ + +end ApplicationStep + +/-! ### The structural step -/ + +section CoreStep + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {methods : Methods .anon} + +private theorem get_run' (state : TcState .anon) : + (get : TcM .anon (TcState .anon)) state = .ok state state := rfl + +/-- The structural step: leaves finish, a loose bound variable does not read, +a let-bound free variable is its value, an explicit let substitutes, and the +projection and application branches are seams. -/ +theorem whnfCoreWithFlagsStep_sound + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + (callbacks : GenericWhnfContract.{u,v} resolve anchor entries source catalog methods) (flags : WhnfFlags) : + SoundStep.{u,v} resolve anchor entries source catalog (fun term : KExpr .anon => term) + (fun current => (RecM.whnfCoreWithFlagsStep current flags).run methods) := by + intro locals context bounds before term current valid reads typed + dsimp only at reads ⊢ + cases current with + | var index name info => simp [readScopedExpr?] at reads + | fvar id name info => + unfold RecM.whnfCoreWithFlagsStep + dsimp only + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog (fun term : KExpr .anon => term) + locals context bounds (KExpr.fvar id name info) (EStateM.bind (get : TcM .anon (TcState .anon)) _ before) + rw [EStateM.bind, get_run'] + dsimp only + cases found : before.lctx.find? id with + | none => exact ⟨valid, .refl _⟩ + | some decl => + cases decl with + | cdecl _ _ _ => exact ⟨valid, .refl _⟩ + | ldecl declName type value => + exact ⟨valid, seams.letValue locals context bounds before id name declName info type value + valid found⟩ + | sort _ _ | all _ _ _ _ _ | lam _ _ _ _ _ | nat _ _ _ | str _ _ _ | const _ _ _ => + exact ⟨valid, .refl _⟩ + | prj id field value info => + exact seams.projection flags locals context bounds before term id field value info valid reads typed + | letE name domain value body nonDep info => + have resources := seams.letResources locals context bounds before + (.letE name domain value body nonDep info) valid rfl + obtain ⟨plan, _⟩ := LetStepSource.construct rfl resources + rw [plan.run methods flags] + obtain ⟨_, coherent⟩ := plan.reading reads valid.coherent + refine ⟨valid.ofIntern coherent, ?_⟩ + intro locals' context' term' reads' _ + exact ⟨term', (plan.reading reads' valid.coherent).1, .refl _, fun _ checked => checked⟩ + | app fn arg info => + exact whnfCoreWithFlagsStep_app_sound seams callbacks flags valid reads typed + +end CoreStep + +/-! ### The no-delta step -/ + +section NoDeltaStep + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {methods : Methods .anon} + +/-- The reducer tail of a no-delta iteration, in production order; the +accelerated probes are absent under `noAccel`. -/ +theorem whnfNoDeltaReducersStep_sound + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + (flags : WhnfFlags) (mode : NatSuccMode) : + SoundStep.{u,v} resolve anchor entries source catalog (fun term : KExpr .anon => term) + (fun current => (RecM.whnfNoDeltaReducersStep flags mode current).run methods) := by + intro locals context bounds before term current valid reads typed + dsimp only at reads ⊢ + unfold RecM.whnfNoDeltaReducersStep + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog (fun term : KExpr .anon => term) + locals context bounds current + (EStateM.bind ((RecM.tryProjAppReduceFinished current flags).run methods) _ before) + apply StepOutcome.bindProbe (seams.projApp flags locals context bounds before term current valid reads typed) + · intro reduced middle valid' reduction + exact ⟨valid', reduction⟩ + intro middle valid' + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog (fun term : KExpr .anon => term) + locals context bounds current + (EStateM.bind ((RecM.tryReduceBitvec current).run methods) _ middle) + rw [EStateM.bind, tryReduceBitvec_noAccel valid'.accelerationsOff] + dsimp only + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog (fun term : KExpr .anon => term) + locals context bounds current + (EStateM.bind ((RecM.tryReduceNatWithSuccMode current mode).run methods) _ middle) + apply StepOutcome.bindProbe (seams.nat mode locals context bounds middle term current valid' reads typed) + · intro reduced middle' valid'' reduction + exact ⟨valid'', reduction⟩ + intro middle' valid'' + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog (fun term : KExpr .anon => term) + locals context bounds current + (EStateM.bind ((RecM.tryReduceNative current).run methods) _ middle') + rw [EStateM.bind, tryReduceNative_noAccel valid''.accelerationsOff] + dsimp only + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog (fun term : KExpr .anon => term) + locals context bounds current + (EStateM.bind ((RecM.tryReduceString current).run methods) _ middle') + apply StepOutcome.bindProbe (seams.string locals context bounds middle' term current valid'' reads typed) + · intro reduced state valid₃ reduction + exact ⟨valid₃, reduction⟩ + intro state valid₃ + by_cases full : flags.isFull = true + · rw [if_pos full, ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog (fun term : KExpr .anon => term) + locals context bounds current + (EStateM.bind ((RecM.tryReduceProjectionDefinition current).run methods) _ state) + apply StepOutcome.bindProbe + (seams.projectionDefinition locals context bounds state term current valid₃ reads typed) + · intro reduced final valid₄ reduction + exact ⟨valid₄, reduction⟩ + · intro final valid₄ + dsimp only + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog (fun term : KExpr .anon => term) + locals context bounds current + (EStateM.bind ((RecM.tryQuotReduce current).run methods) _ final) + apply StepOutcome.bindProbe (seams.quot locals context bounds final term current valid₄ reads typed) + · intro reduced last valid₅ reduction + exact ⟨valid₅, reduction⟩ + · intro last valid₅ + exact ⟨valid₅, .refl _⟩ + · rw [if_neg full] + dsimp only + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog (fun term : KExpr .anon => term) + locals context bounds current + (EStateM.bind ((RecM.tryQuotReduce current).run methods) _ state) + apply StepOutcome.bindProbe (seams.quot locals context bounds state term current valid₃ reads typed) + · intro reduced last valid₅ reduction + exact ⟨valid₅, reduction⟩ + · intro last valid₅ + exact ⟨valid₅, .refl _⟩ + +/-- One no-delta iteration: the same-layer structural call, then the reducers. -/ +theorem whnfNoDeltaImplStep_sound + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + (core : ∀ term flags, GenericSoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfCoreWithFlags term flags).run methods)) + (flags : WhnfFlags) (mode : NatSuccMode) : + SoundStep.{u,v} resolve anchor entries source catalog (fun term : KExpr .anon => term) + (fun current => (RecM.whnfNoDeltaImplStep flags mode current).run methods) := by + intro locals context bounds before term current valid reads typed + dsimp only at reads ⊢ + unfold RecM.whnfNoDeltaImplStep + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog (fun term : KExpr .anon => term) + locals context bounds current + (EStateM.bind ((RecM.whnfCoreWithFlags current flags).run methods) _ before) + apply StepOutcome.bindReduction (core current flags locals context bounds before term valid reads typed) + intro reduced middle valid' reduction + obtain ⟨target, readsTarget, _, preserved⟩ := reduction locals context term reads typed + obtain ⟨type, checked⟩ := typed + exact (whnfNoDeltaReducersStep_sound seams flags mode locals context bounds middle target reduced valid' + readsTarget ⟨type, preserved type checked⟩).transport reduction + +end NoDeltaStep + +/-! ### The full step -/ + +section FullStep + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {methods : Methods .anon} + +/-- One full-WHNF iteration: the same-layer no-delta call, the cycle check, +the literal reducers, the stuck offset, and one delta step. -/ +theorem whnfWithNatSuccModeStep_sound + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + (noDelta : ∀ term flags mode, GenericSoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfNoDeltaImpl term flags mode).run methods)) + (mode : NatSuccMode) : + SoundStep.{u,v} resolve anchor entries source catalog Prod.fst + (fun state => (RecM.whnfWithNatSuccModeStep mode state).run methods) := by + intro locals context bounds before term state valid reads typed + obtain ⟨current, seen⟩ := state + dsimp only at reads ⊢ + unfold RecM.whnfWithNatSuccModeStep + dsimp only + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog Prod.fst locals context bounds + (current, seen) (EStateM.bind ((RecM.whnfNoDeltaImpl current .FULL mode).run methods) _ before) + apply StepOutcome.bindReduction + (noDelta current .FULL mode locals context bounds before term valid reads typed) + intro reduced middle valid' reduction + obtain ⟨target, readsTarget, _, preserved⟩ := reduction locals context term reads typed + obtain ⟨type, checked⟩ := typed + have typed' : ∃ type, CheckedTyping.{u,v} entries context target type := ⟨type, preserved type checked⟩ + by_cases repeated : seen.contains reduced.addr = true + · rw [if_pos repeated] + exact ⟨valid', reduction⟩ + rw [if_neg repeated, ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog Prod.fst locals context bounds + (current, seen) (EStateM.bind ((RecM.tryReduceNative reduced).run methods) _ middle) + rw [EStateM.bind, tryReduceNative_noAccel valid'.accelerationsOff] + dsimp only + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog Prod.fst locals context bounds + (current, seen) (EStateM.bind ((RecM.tryReduceBitvec reduced).run methods) _ middle) + rw [EStateM.bind, tryReduceBitvec_noAccel valid'.accelerationsOff] + dsimp only + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog Prod.fst locals context bounds + (current, seen) (EStateM.bind ((RecM.tryReduceNatWithSuccMode reduced mode).run methods) _ middle) + apply StepOutcome.bindProbe + (seams.nat mode locals context bounds middle target reduced valid' readsTarget typed') + · intro next state valid'' stepped + exact ⟨valid'', reduction.trans stepped⟩ + intro state valid'' + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog Prod.fst locals context bounds + (current, seen) (EStateM.bind ((RecM.tryReduceDecidable reduced).run methods) _ state) + rw [EStateM.bind, tryReduceDecidable_noAccel valid''.accelerationsOff] + dsimp only + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog Prod.fst locals context bounds + (current, seen) (EStateM.bind ((RecM.tryReduceString reduced).run methods) _ state) + apply StepOutcome.bindProbe + (seams.string locals context bounds state target reduced valid'' readsTarget typed') + · intro next state' valid₃ stepped + exact ⟨valid₃, reduction.trans stepped⟩ + intro state' valid₃ + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog Prod.fst locals context bounds + (current, seen) (EStateM.bind ((RecM.tryNatOffsetStuck reduced).run methods) _ state') + apply StepOutcome.bindProbe + (seams.natOffsetStuck locals context bounds state' target reduced valid₃ readsTarget typed') + · intro stuck state'' valid₄ stepped + exact ⟨valid₄, reduction.trans stepped⟩ + intro state'' valid₄ + rw [ReaderT.run_bind] + change StepOutcome.{u,v} resolve anchor entries source catalog Prod.fst locals context bounds + (current, seen) (EStateM.bind ((RecM.deltaUnfoldOne reduced).run methods) _ state'') + apply StepOutcome.bindProbe + (deltaUnfoldOne_sound seams locals context bounds state'' target reduced valid₄ readsTarget typed') + · intro unfolded final valid₅ stepped + exact ⟨valid₅, reduction.trans stepped⟩ + · intro final valid₅ + exact ⟨valid₅, reduction⟩ + +end FullStep + +/-! ### Assembly -/ + +section Assembly + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {methods : Methods .anon} + +/-- Structural WHNF with flags: leaves return, a loose variable does not +read, and every other form runs the cache layer over the sound loop. -/ +theorem whnfCoreWithFlags_sound + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + (callbacks : GenericWhnfContract.{u,v} resolve anchor entries source catalog methods) (flags : WhnfFlags) + (term : KExpr .anon) : + GenericSoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfCoreWithFlags term flags).run methods) := by + have layer := whnfCoreWithFlagsNonLeaf_sound (methods := methods) seams.lookups seams.hits + (fun term flags => runBounded_sound (whnfCoreWithFlagsStep_sound seams callbacks flags) _ term) + intro locals context bounds before reading valid reads typed + cases term with + | var index name info => simp [readScopedExpr?] at reads + | sort _ _ | all _ _ _ _ _ | lam _ _ _ _ _ | nat _ _ _ | str _ _ _ | const _ _ _ => + exact ⟨valid, .refl _⟩ + | fvar id name info => exact layer _ flags locals context bounds before reading valid reads typed + | prj id field value info => exact layer _ flags locals context bounds before reading valid reads typed + | letE name domain value body nonDep info => + exact layer _ flags locals context bounds before reading valid reads typed + | app fn arg info => exact layer _ flags locals context bounds before reading valid reads typed + +theorem whnfCore_sound + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + (callbacks : GenericWhnfContract.{u,v} resolve anchor entries source catalog methods) (term : KExpr .anon) : + GenericSoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfCore term).run methods) := + whnfCoreWithFlags_sound seams callbacks .FULL term + +/-- No-delta WHNF: the cache layer over the loop of sound iterations. -/ +theorem whnfNoDeltaImpl_sound + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + (callbacks : GenericWhnfContract.{u,v} resolve anchor entries source catalog methods) + (flags : WhnfFlags) (mode : NatSuccMode) (term : KExpr .anon) : + GenericSoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfNoDeltaImpl term flags mode).run methods) := by + have layer := whnfNoDeltaImplNonLeaf_sound (methods := methods) seams.lookups seams.hits + (fun term flags mode => runBounded_sound + (whnfNoDeltaImplStep_sound seams (fun term flags => whnfCoreWithFlags_sound seams callbacks flags term) flags mode) _ term) + intro locals context bounds before reading valid reads typed + cases term with + | var index name info => simp [readScopedExpr?] at reads + | sort _ _ | all _ _ _ _ _ | lam _ _ _ _ _ | nat _ _ _ | str _ _ _ => exact ⟨valid, .refl _⟩ + | const _ _ _ | fvar _ _ _ | prj _ _ _ _ | letE _ _ _ _ _ _ | app _ _ _ => + exact layer _ flags mode locals context bounds before reading valid reads typed + +/-- Full WHNF in either successor mode: the instrumented cache layer over +the loop of sound iterations started with an empty cycle set. -/ +theorem whnfWithNatSuccMode_sound + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + (callbacks : GenericWhnfContract.{u,v} resolve anchor entries source catalog methods) + (mode : NatSuccMode) (term : KExpr .anon) : + GenericSoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfWithNatSuccMode term mode).run methods) := by + have layer := whnfWithNatSuccModeNonLeaf_sound (methods := methods) seams.lookups seams.hits + (fun term mode => runBounded_sound + (whnfWithNatSuccModeStep_sound seams (fun term flags mode => whnfNoDeltaImpl_sound seams callbacks flags mode term) mode) _ (term, {})) + intro locals context bounds before reading valid reads typed + cases term with + | var index name info => simp [readScopedExpr?] at reads + | sort _ _ | all _ _ _ _ _ | lam _ _ _ _ _ | nat _ _ _ | str _ _ _ => exact ⟨valid, .refl _⟩ + | const _ _ _ | fvar _ _ _ | prj _ _ _ _ | letE _ _ _ _ _ _ | app _ _ _ => + exact layer _ mode locals context bounds before reading valid reads typed + +theorem whnf_sound + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog methods) + (callbacks : GenericWhnfContract.{u,v} resolve anchor entries source catalog methods) (term : KExpr .anon) : + GenericSoundReduction.{u,v} resolve anchor entries source catalog term ((RecM.whnf term).run methods) := + whnfWithNatSuccMode_sound seams callbacks .collapse term + +/-- One induction step of the reduction contracts: the bodies that +`methodsN (depth + 1)` installs are sound under the seams at `methodsN depth`. -/ +theorem GenericWhnfContract.succ {depth : Nat} + (recursive : GenericWhnfContract.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog (methodsN depth)) : + GenericWhnfContract.{u,v} resolve anchor entries source catalog (methodsN (depth + 1)) where + whnf := fun term => whnf_sound seams recursive term + whnfCore := fun term => whnfCore_sound seams recursive term + whnfMode := fun term mode => whnfWithNatSuccMode_sound seams recursive mode term + whnfCoreFlags := fun term flags => whnfCoreWithFlags_sound seams recursive flags term + +/-- Every finite production table satisfies the reduction contracts under +the seams at every depth. -/ +theorem GenericWhnfContract.methodsN + (seams : ∀ depth, WhnfSeamAssumptions.{u,v} resolve anchor entries source catalog (methodsN depth)) : + ∀ depth, GenericWhnfContract.{u,v} resolve anchor entries source catalog (methodsN depth) + | 0 => GenericWhnfContract.zero + | depth + 1 => GenericWhnfContract.succ (GenericWhnfContract.methodsN seams depth) (seams depth) + +end Assembly + +end Ix.Kernel.Consistency From 3f7e85d20b02e7e72c064dc47d832fd0eae48590 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 15 Sep 2026 13:34:24 -0400 Subject: [PATCH 61/63] Prove the reducing DefEq tiers modulo reducer seams Consistency/DefEqReducing, DefEqFinal, and DefEqLazyDelta prove the tiers after the quick structural probe under the smaller table's contracts: the eager Bool.true shortcut in both directions, the vacuous string expansion, the cheap structural-core and no-delta passes, proof irrelevance through the memoized proposition classifier, the lazy-delta loop (finishing checks, same-head spine congruence, the speculative fuel slice, the rejection cache as a memo probe, lookup-based classification and ranking, one- and two-sided unfolding), the stopped continuation (structural congruence, second structural pass, spine comparison), and the final tier (structural prefix, eta guard chain, string, structure eta, unit-like, proof irrelevance). isDefEqInnerAfterQuick_sound discharges the inner field of DefEqSeamAssumptions and StepContracts.isDefEq_of_reducing_tiers is the field-shaped corollary. DefEqReducingSeams fields: whnf, whnfCore, whnfCoreWithFlags, whnfNoDeltaImpl (the reduction bodies at this table), whnfPrims, reduceNat, reduceNative, reduceDecidable, unfoldProjApp, deltaUnfoldOne, offset, projectionDelta, whnfNat, structEta, unit, whnfLet, compareEta, appHereditary, typeFormation, isPropTransport. DefEqReducingResources adds per-lookup conversion data to the direct tiers' resources. Audit root groups pin every new root; the umbrella imports the three modules. --- Ix/Kernel/Verify/Consistency.lean | 7 + Ix/Kernel/Verify/Consistency/Audit.lean | 79 + Ix/Kernel/Verify/Consistency/DefEqFinal.lean | 821 ++++++++++ .../Verify/Consistency/DefEqLazyDelta.lean | 1335 +++++++++++++++++ .../Verify/Consistency/DefEqReducing.lean | 1325 ++++++++++++++++ 5 files changed, 3567 insertions(+) create mode 100644 Ix/Kernel/Verify/Consistency/DefEqFinal.lean create mode 100644 Ix/Kernel/Verify/Consistency/DefEqLazyDelta.lean create mode 100644 Ix/Kernel/Verify/Consistency/DefEqReducing.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index e4db968e1..8b25f0e9f 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -94,6 +94,9 @@ import Ix.Kernel.Verify.Consistency.CheckedTyping import Ix.Kernel.Verify.Consistency.WhnfGeneric import Ix.Kernel.Verify.Consistency.WhnfLayers import Ix.Kernel.Verify.Consistency.WhnfSteps +import Ix.Kernel.Verify.Consistency.DefEqReducing +import Ix.Kernel.Verify.Consistency.DefEqFinal +import Ix.Kernel.Verify.Consistency.DefEqLazyDelta import Ix.Kernel.Verify.Consistency.Audit /-! @@ -387,5 +390,9 @@ local lets, multi-argument beta and head rebuilding after the recursive head call, and delta unfolding through the unfold memo are closed by induction on the method-table depth, and the projection, iota, literal, and quotient reducers remain seams collected in one assumption record per depth. +discipline, and hereditary typing of binder operands. The reducing tiers after +the quick probe, from the eager `Bool.true` shortcut through the cheap passes, +proof irrelevance, the lazy-delta loop, and the final WHNF tier, are proved +modulo the reducer seams, which discharges that reducing-tail obligation. General checker soundness remains outside this fragment. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 78356bd5c..03dc81374 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -23,6 +23,7 @@ import Ix.Kernel.Verify.Consistency.Invariant import Ix.Kernel.Verify.Consistency.Contracts import Ix.Kernel.Verify.Consistency.DefEqTiers import Ix.Kernel.Verify.Consistency.WhnfSteps +import Ix.Kernel.Verify.Consistency.DefEqLazyDelta import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -1719,6 +1720,74 @@ private def wp4ProductionRoots : Array Lean.Name := #[ ``GenericWhnfContract.succ, ``GenericWhnfContract.methodsN ] +/-- The reducing conversion tiers under the contracts: the outcome shapes and +seams of the reducing tail, invariant preservation through lookups and the +is-prop and rejection-cache writes, the cheap reducers, proof irrelevance, +the upper chain, the final tier, the lazy-delta loop, and the assembled +body with its `StepContracts` corollary. These reach expression construction +through the production bodies they run. -/ +private def wp3ReducingSeamRoots : Array Lean.Name := #[ + ``CheckerInvariant.ofDefEqFailure, ``CheckerInvariant.ofIsPropInsert, + ``CheckerInvariant.tryGetConst, ``ConversionPost.ite, ``ConversionPost.optionalThen, + ``ConversionPost.pureFalse, ``ConversionPost.pureTrue, ``DefEqReducingResources, + ``DefEqReducingSeams, ``DefEqReducingSeams.formed, ``DefEqSeamAssumptions.ofReducing, + ``IsPropKeyOrigin, ``KeepsInvariant, ``KeepsInvariant.lookup, ``LazyDeltaPost, + ``LazyDeltaPost.lift, ``LazyPairInvariant, ``LazyPairInvariant.lift, ``LazyPairInvariant.refl, + ``LazyStepPost, ``LazyStepPost.lift, ``LazyStepPost.ofCallLeft, ``LazyStepPost.ofCallRight, + ``LazyStepPost.optionalLeft, ``LazyStepPost.optionalRight, ``OptionalConversionPost, + ``OptionalConversionPost.answer, ``OptionalConversionPost.ite, ``OptionalConversionPost.pureNone, + ``OptionalConversionPost.pureSome, ``OptionalReductionPost, ``PropositionPost, + ``SoundOptionalConversion, ``SoundOptionalReduction, ``SoundReduction.withCheapRecursionDepth, + ``StepContracts.isDefEq_of_reducing_tiers, ``classifyDeltaHead_keeps, + ``classifyPropTypeUncached_sound, ``defEqLazyDeltaStepAfterAcceleratorMiss_sound, + ``defEqLazyDeltaStepAfterDeltaClassification_sound, ``defEqLazyDeltaStepAfterNatMiss_sound, + ``defEqLazyDeltaStepAfterOffsetMiss_sound, ``defEqLazyDeltaStepAfterProjectionMiss_sound, + ``defEqLazyDeltaStepAfterSameHeadMiss_sound, ``defEqLazyDeltaStepWithEqualRank_sound, + ``defEqLazyDeltaStepWithLeftDelta_sound, ``defEqLazyDeltaStepWithRightDelta_sound, + ``defEqLazyDeltaStep_sound, ``defRankId_keeps, ``finishDefEqLazyDeltaStep_sound, + ``isDefEqAfterLazyDeltaStopped_sound, ``isDefEqInnerAfterBoolTrue_sound_of_tail, + ``isDefEqInnerAfterCorePass_sound_of_tail, ``isDefEqInnerAfterNoDeltaPass_sound_of_tail, + ``isDefEqInnerAfterProofIrrelevance_sound, ``isDefEqInnerAfterQuick_sound, + ``isDefEqInnerAfterQuick_sound_of_tail, ``isDefEqInnerAfterStringExpansion_sound_of_tail, + ``isDefEqWhnfAfterEta_sound, ``isDefEqWhnfAfterNat_sound, ``isDefEqWhnfAfterString_sound, + ``isDefEqWhnfAfterStructEta_sound, ``isDefEqWhnfAfterStructural_sound, ``isDefEqWhnf_sound, + ``isDelta_keeps, ``isPropType_sound, ``isRegular_keeps, ``rankDeltaHead_keeps, + ``runDefEqLazyDelta_sound, ``tryDefEqApp_sound, ``tryDefEqWhnfApp_sound, + ``tryDefEqWhnfEtaAfterGuard_sound, ``tryDefEqWhnfEta_sound, ``tryDefEqWhnfString_sound, + ``tryDefEqWhnfStructural_sound, ``tryEtaExpansionAfterGuard_sound, ``tryEtaExpansion_sound, + ``tryInferOnly_sound, ``tryProofIrrel_sound, ``trySameHeadSpineCached_sound, + ``trySameHeadSpineSpeculative_sound, ``trySameHeadSpine_sound, ``tryStructuralCongruence_sound, + ``whnfCoreForDefEq_def, ``whnfCoreForDefEq_sound, ``whnfNoDeltaForDefEq_def, + ``whnfNoDeltaForDefEq_sound +] + +/-- Model-level congruences and readings of the reducing tiers that reach no +production construction: spine congruence and hereditary typing, the hash +path of a reduced pair, and the `Bool.true` reading. -/ +private def wp3ReducingPureRoots : Array Lean.Name := #[ + ``AExpr.appN_typed, ``ConversionClaim.appN, ``ConversionClaim.ofAddrEq, ``SameRawAnnotations, + ``SpineReadings.ofLists, ``isBoolTrue_reading, ``spine_parts +] + +/-- Run equations of the reducing tiers' primitives and the bounded-loop driver. -/ +private def wp3ReducingPropextRoots : Array Lean.Name := #[ + ``RecM.ite_post, ``boolTrueAllowed, ``boolTrueReductionAllowed_run, ``inferOnlyCall_run, + ``isBoolTrue_run, ``Ix.Kernel.Consistency.monadLift_self, ``prims_run, ``runBoundedInvariant_sound, + ``try?_run, ``tryFinally_run, ``tryInferOnly_run, ``withCheapRecursionDepth_run +] + +/-- The `Bool.true` recognizer, the string guard, and the legacy-variable reading. -/ +private def wp3ReducingRecognizerRoots : Array Lean.Name := #[ + ``hasStringLiteralPair_eq_false, ``isBoolTrueAnswer, ``readScopedExpr?_var_none +] + +/-- Outcome-directed bind equations and shape inversions of readings. -/ +private def wp3ReducingAxiomFreeRoots : Array Lean.Name := #[ + ``Ix.Kernel.Consistency.EStateM.run_bind_error, ``Ix.Kernel.Consistency.EStateM.run_bind_ok, + ``readScopedExpr?_app_annotated, ``readScopedExpr?_nat_annotated, ``readScopedExpr?_prj_annotated, + ``readScopedExpr?_str_none +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -1993,6 +2062,16 @@ def roots : Array RootAllowance := #[ ++ wp4ProductionRoots.map (fun root => { root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], forbiddenDependencies := forbiddenProduction }) + ++ wp3ReducingSeamRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], + forbiddenDependencies := forbiddenProduction }) + ++ wp3ReducingPureRoots.map (fun root => { + root, standardAxioms := standard, forbiddenDependencies := forbiddenProduction }) + ++ wp3ReducingPropextRoots.map (fun root => { + root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction }) + ++ wp3ReducingRecognizerRoots.map (fun root => { + root, standardAxioms := #[``propext], forbiddenDependencies := forbiddenProduction }) + ++ wp3ReducingAxiomFreeRoots.map (fun root => { root, forbiddenDependencies := forbiddenProduction }) run_cmd Kernel.Verify.Audit.check roots diff --git a/Ix/Kernel/Verify/Consistency/DefEqFinal.lean b/Ix/Kernel/Verify/Consistency/DefEqFinal.lean new file mode 100644 index 000000000..d7b5d2bcd --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/DefEqFinal.lean @@ -0,0 +1,821 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.DefEqReducing + +/-! +# The final DefEq tiers under the contracts + +After lazy delta stops, production runs post-delta structural congruence, a +second structural pass through `whnfCore`, the application-spine comparison, +and the final WHNF tier: the constructor-directed structural prefix, Nat +bridging, lambda eta, string expansion, structure eta, unit-like types, and +proof irrelevance. This module proves each tier sound against the checker +invariant with the recursive callbacks abstracted by the smaller table's +contracts and the reducer seams of `DefEqReducingSeams`: + +* the structural prefix decides sorts by universe equality, constants by the + universe gate, applications by the recursive callback on both parts, and + matching binders by the common-local comparison; legacy variables and + string literals have no reading, so their branches are vacuous, and the + let branch is the `whnfLet` seam; +* lambda eta infers and normalizes the non-lambda operand's type through the + smaller table and the reducer seam, converts the operand to the exposed + dependent function type, and compares the constructed expansion through the + `compareEta` seam; `ConversionClaim.eta` is the seam implementer's tool; +* the string, Nat, structure-eta, and unit-like phases are the corresponding + seams, and the last fallback is proof irrelevance; +* post-delta congruence compares constants by the universe gate and + projections through the projection-delta seam; the spine comparison + composes the head callback with the argument loop. + +Every branch that answers by address equality uses the hash path under +address faithfulness and the annotation discipline. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-! ### Branch combinators on outcomes -/ + +section Branches + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} {methods : Methods .anon} + {before : TcState .anon} {a b : AExpr β} + +theorem ConversionPost.ite {c : Prop} [Decidable c] {left right : RecM .anon Bool} + (thenSound : c → ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds + a b (left.run methods before)) + (elseSound : ¬ c → ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds + a b (right.run methods before)) : + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b + ((if c then left else right).run methods before) := by + by_cases h : c + · rw [if_pos h] + exact thenSound h + · rw [if_neg h] + exact elseSound h + +theorem OptionalConversionPost.ite {c : Prop} [Decidable c] {left right : RecM .anon (Option Bool)} + (thenSound : c → OptionalConversionPost.{u,v} resolve anchor entries source catalog locals context + bounds a b (left.run methods before)) + (elseSound : ¬ c → OptionalConversionPost.{u,v} resolve anchor entries source catalog locals context + bounds a b (right.run methods before)) : + OptionalConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b + ((if c then left else right).run methods before) := by + by_cases h : c + · rw [if_pos h] + exact thenSound h + · rw [if_neg h] + exact elseSound h + +theorem ConversionPost.pureTrue + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (claim : ConversionClaim.{u,v} entries context a b) : + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b + ((pure true : RecM .anon Bool).run methods before) := by + rw [pure_run] + exact ⟨valid, claim⟩ + +theorem ConversionPost.pureFalse + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) : + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b + ((pure false : RecM .anon Bool).run methods before) := by + rw [pure_run] + exact valid + +theorem OptionalConversionPost.pureNone + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) : + OptionalConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b + ((pure none : RecM .anon (Option Bool)).run methods before) := by + rw [pure_run] + exact valid + +theorem OptionalConversionPost.pureSome + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (answer : Bool) (claim : answer = true → ConversionClaim.{u,v} entries context a b) : + OptionalConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b + ((pure (some answer) : RecM .anon (Option Bool)).run methods before) := by + rw [pure_run] + cases answer with + | true => exact ⟨valid, claim rfl⟩ + | false => exact valid + +/-- A terminal optional answer is a conversion answer. -/ +theorem OptionalConversionPost.answer {answer : Bool} {after : TcState .anon} + (post : OptionalConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b + (.ok (some answer) after)) : + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b + (.ok answer after) := by + cases answer <;> exact post + +/-- The production shape `match ← probe with | some answer => return answer | none => rest`. -/ +theorem ConversionPost.optionalThen {probe : RecM .anon (Option Bool)} {rest : RecM .anon Bool} + (probeSound : OptionalConversionPost.{u,v} resolve anchor entries source catalog locals context bounds + a b (probe.run methods before)) + (restSound : ∀ state, + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b + (rest.run methods state)) : + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b + ((probe >>= fun answer? => match answer? with + | some answer => pure answer + | none => rest).run methods before) := by + simp only [ReaderT.run_bind] + cases run : probe.run methods before with + | error err after => + rw [EStateM.run_bind_error run] + rw [run] at probeSound + exact probeSound + | ok answer? after => + rw [EStateM.run_bind_ok run] + rw [run] at probeSound + cases answer? with + | none => exact restSound after probeSound + | some answer => + try dsimp only + rw [pure_run] + exact probeSound.answer + +end Branches + +/-! ### The constructor-directed structural prefix -/ + +section Structural + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} + +/-- The short-circuiting application branch: both parts through the recursive callback. -/ +theorem tryDefEqWhnfApp_sound {methods : Methods .anon} + (recursive : DefEqContract.{u,v} resolve anchor entries source catalog methods) + (appHereditary : ∀ (context : Model.Context β) (fn arg type : AExpr β), + TypingClaim.{u,v} entries context (.app fn arg) type → + (∃ type, TypingClaim.{u,v} entries context fn type) ∧ + (∃ type, TypingClaim.{u,v} entries context arg type)) + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {before : TcState .anon} + {f1 a1 f2 a2 : KExpr .anon} {info1 info2 : ExprInfo .anon} {a b : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftReads : readScopedExpr? resolve locals (.app f1 a1 info1) = some a.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context a type) + (rightReads : readScopedExpr? resolve locals (.app f2 a2 info2) = some b.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context b type) : + OptionalConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b + ((RecM.tryDefEqWhnfApp f1 a1 f2 a2).run methods before) := by + obtain ⟨F1, A1, aEq, f1Reads, a1Reads⟩ := readScopedExpr?_app_annotated leftReads + obtain ⟨F2, A2, bEq, f2Reads, a2Reads⟩ := readScopedExpr?_app_annotated rightReads + subst aEq bEq + obtain ⟨T1, typed1⟩ := leftTyped + obtain ⟨T2, typed2⟩ := rightTyped + obtain ⟨f1Typed, a1Typed⟩ := appHereditary context F1 A1 T1 typed1 + obtain ⟨f2Typed, a2Typed⟩ := appHereditary context F2 A2 T2 typed2 + unfold RecM.tryDefEqWhnfApp + simp only [ReaderT.run_bind, isDefEqCall_run] + have heads := recursive.isDefEq f1 f2 locals context bounds before F1 F2 valid f1Reads f1Typed f2Reads + f2Typed + cases runF : methods.isDefEq f1 f2 before with + | error err s₁ => + rw [EStateM.run_bind_error runF] + rw [runF] at heads + exact heads + | ok headAnswer s₁ => + rw [EStateM.run_bind_ok runF] + rw [runF] at heads + cases headAnswer with + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + exact OptionalConversionPost.pureNone heads + | true => + obtain ⟨valid₁, headClaim⟩ := heads + simp only [↓reduceIte] + simp only [ReaderT.run_bind, isDefEqCall_run] + have args := recursive.isDefEq a1 a2 locals context bounds s₁ A1 A2 valid₁ a1Reads a1Typed + a2Reads a2Typed + cases runA : methods.isDefEq a1 a2 s₁ with + | error err s₂ => + rw [EStateM.run_bind_error runA] + rw [runA] at args + exact args + | ok argAnswer s₂ => + rw [EStateM.run_bind_ok runA] + rw [runA] at args + cases argAnswer with + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + exact OptionalConversionPost.pureNone args + | true => + simp only [↓reduceIte] + exact OptionalConversionPost.pureSome args.1 true + (fun _ => ConversionClaim.app headClaim args.2) + +/-- The constructor-directed prefix of the final tier. -/ +theorem tryDefEqWhnfStructural_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (left right : KExpr .anon) : + SoundOptionalConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.tryDefEqWhnfStructural left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.tryDefEqWhnfStructural + split + · rename_i u1 info1 u2 info2 + rw [pure_run] + cases accepted : univEq u1 u2 + · exact valid + · obtain ⟨faithful, boundU, boundV⟩ := resources.tiers.sorts u1 u2 info1 info2 trivial trivial + have aEq := readScopedExpr?_sort_annotated leftReads + have bEq := readScopedExpr?_sort_annotated rightReads + subst aEq bEq + exact ⟨valid, ConversionClaim.sort + (Theory.VLevel.equiv_def.mp (univEq_sound faithful boundU boundV accepted))⟩ + · rw [readScopedExpr?_var_none] at leftReads + cases leftReads + · rename_i id1 us1 info1 id2 us2 info2 + refine OptionalConversionPost.ite ?_ ?_ + · intro gate + simp only [Bool.and_eq_true, beq_iff_eq] at gate + exact OptionalConversionPost.pureSome valid true fun _ => + ConversionClaim.constInstances leftReads rightReads gate.1 gate.2 + (fun u _ v _ => resources.tiers.sorts u v info1 info2 trivial trivial) + · intro _ + exact OptionalConversionPost.pureNone valid + · rename_i f1 a1 info1 f2 a2 info2 + have post := tryDefEqWhnfApp_sound recursive.toDefEqContract seams.appHereditary + (info1 := info1) (info2 := info2) valid leftReads leftTyped rightReads rightTyped + exact post + · rename_i name1 bi1 ty1 body1 info1 name2 bi2 ty2 body2 info2 + have quick := quickDefEq_sound recursive.toDefEqContract (MethodsLocalState.methodsN depth) binders + resources.tiers valid trivial trivial leftReads leftTyped rightReads rightTyped + rw [quickDefEq_eq] at quick + dsimp only at quick + simp only [ReaderT.run_bind] + cases run : (RecM.quickBinder name1 bi1 ty1 body1 ty2 body2).run (methodsN depth) before with + | error err after => + rw [EStateM.run_bind_error run] + rw [run] at quick + exact quick + | ok answer after => + rw [EStateM.run_bind_ok run] + rw [run] at quick + cases answer with + | true => + simp only [↓reduceIte] + exact OptionalConversionPost.pureSome quick.1 true fun _ => quick.2 + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + exact OptionalConversionPost.pureNone quick + · rename_i name1 bi1 ty1 body1 info1 name2 bi2 ty2 body2 info2 + have quick := quickDefEq_sound recursive.toDefEqContract (MethodsLocalState.methodsN depth) binders + resources.tiers valid trivial trivial leftReads leftTyped rightReads rightTyped + rw [quickDefEq_eq] at quick + dsimp only at quick + simp only [ReaderT.run_bind] + cases run : (RecM.quickBinder name1 bi1 ty1 body1 ty2 body2).run (methodsN depth) before with + | error err after => + rw [EStateM.run_bind_error run] + rw [run] at quick + exact quick + | ok answer after => + rw [EStateM.run_bind_ok run] + rw [run] at quick + cases answer with + | true => + simp only [↓reduceIte] + exact OptionalConversionPost.pureSome quick.1 true fun _ => quick.2 + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + exact OptionalConversionPost.pureNone quick + · rename_i name1 ty1 v1 body1 nonDep1 info1 name2 ty2 v2 body2 nonDep2 info2 + have post := seams.whnfLet name1 ty1 v1 body1 ty2 v2 body2 nonDep1 nonDep2 info1 info2 locals context + bounds before a b valid leftReads leftTyped rightReads rightTyped + exact post + · rename_i v1 blob1 info1 v2 blob2 info2 + rw [pure_run] + cases equal : v1 == v2 + · exact valid + · have aEq := readScopedExpr?_nat_annotated leftReads + have bEq := readScopedExpr?_nat_annotated rightReads + subst aEq bEq + rw [beq_iff_eq] at equal + subst equal + exact ⟨valid, ConversionClaim.refl _⟩ + · rw [readScopedExpr?_str_none] at leftReads + cases leftReads + · exact OptionalConversionPost.pureNone valid + +end Structural + +/-! ### Lambda eta -/ + +section Eta + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} + +set_option maxHeartbeats 1000000 in +/-- After the syntactic guard: the non-lambda operand's type is inferred and +normalized to a dependent function type, and the constructed expansion is +compared through the seam. -/ +theorem tryEtaExpansionAfterGuard_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (t s : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog t s + ((RecM.tryEtaExpansionAfterGuard t s).run (methodsN depth)) := by + intro locals context bounds before T S valid tReads tTyped sReads sTyped + obtain ⟨Ts, typedS⟩ := sTyped + unfold RecM.tryEtaExpansionAfterGuard + simp only [ReaderT.run_bind] + have probe := tryInferOnly_sound (methods := methodsN depth) recursive.toInferContract valid sReads typedS + cases runI : (RecM.try? (RecM.inferOnlyCall s)).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runI] + rw [runI] at probe + exact probe + | ok type? s₁ => + rw [EStateM.run_bind_ok runI] + rw [runI] at probe + cases type? with + | none => + try dsimp only + exact ConversionPost.pureFalse probe + | some sType => + obtain ⟨valid₁, Sty, styReads, typedSty⟩ := probe + obtain ⟨level, formedSty⟩ := seams.typeFormation context S Sty typedSty + try dsimp only + simp only [ReaderT.run_bind] + have reduced := seams.whnf sType locals context bounds s₁ Sty valid₁ styReads ⟨_, formedSty⟩ + cases runW : (RecM.whnf sType).run (methodsN depth) s₁ with + | error err s₂ => + have caught : (RecM.try? (RecM.whnf sType)).run (methodsN depth) s₁ = .ok none s₂ := by + rw [try?_run, runW] + rw [EStateM.run_bind_ok caught] + rw [runW] at reduced + try dsimp only + exact ConversionPost.pureFalse reduced + | ok w s₂ => + have caught : (RecM.try? (RecM.whnf sType)).run (methodsN depth) s₁ = .ok (some w) s₂ := by + rw [try?_run, runW] + rw [EStateM.run_bind_ok caught] + rw [runW] at reduced + obtain ⟨valid₂, W, wReads, claimW, typesW⟩ := reduced + try dsimp only + split + · rename_i name bi ty body info + obtain ⟨condition, A, B, wEq, tyReads, _⟩ := readScopedExpr?_all_annotated wReads + subst wEq + have branch : ConversionPost.{u,v} resolve anchor entries source catalog locals context + bounds T S ((RecM.compareEtaExpansion t s name bi ty).run (methodsN depth) s₂) := + seams.compareEta t s name bi ty locals context bounds s₂ T S A B condition valid₂ + tReads tTyped sReads tyReads + (TypingClaim.conv typedSty (typesW _ formedSty) claimW) + exact branch + · exact ConversionPost.pureFalse valid₂ + +/-- Lambda eta: a lambda against a non-lambda. -/ +theorem tryEtaExpansion_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (t s : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog t s + ((RecM.tryEtaExpansion t s).run (methodsN depth)) := by + intro locals context bounds before T S valid tReads tTyped sReads sTyped + unfold RecM.tryEtaExpansion + try dsimp only + refine ConversionPost.ite (fun _ => ConversionPost.pureFalse valid) fun _ => ?_ + exact tryEtaExpansionAfterGuard_sound recursive seams t s locals context bounds before T S valid tReads + tTyped sReads sTyped + +/-- Both eta directions after the outer guard. -/ +theorem tryDefEqWhnfEtaAfterGuard_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (left right : KExpr .anon) : + SoundOptionalConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.tryDefEqWhnfEtaAfterGuard left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.tryDefEqWhnfEtaAfterGuard + simp only [ReaderT.run_bind] + have first := tryEtaExpansion_sound recursive seams left right locals context bounds before a b valid + leftReads leftTyped rightReads rightTyped + cases run₁ : (RecM.tryEtaExpansion left right).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error run₁] + rw [run₁] at first + exact first + | ok answer s₁ => + rw [EStateM.run_bind_ok run₁] + rw [run₁] at first + cases answer with + | true => + simp only [↓reduceIte] + exact OptionalConversionPost.pureSome first.1 true fun _ => first.2 + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + simp only [ReaderT.run_bind] + have second := tryEtaExpansion_sound recursive seams right left locals context bounds s₁ b a first + rightReads rightTyped leftReads leftTyped + cases run₂ : (RecM.tryEtaExpansion right left).run (methodsN depth) s₁ with + | error err s₂ => + rw [EStateM.run_bind_error run₂] + rw [run₂] at second + exact second + | ok answer s₂ => + rw [EStateM.run_bind_ok run₂] + rw [run₂] at second + cases answer with + | true => + simp only [↓reduceIte] + exact OptionalConversionPost.pureSome second.1 true fun _ => second.2.symm + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + exact OptionalConversionPost.pureNone second + +/-- The lambda-eta phase: the guard only selects the ordered attempts. -/ +theorem tryDefEqWhnfEta_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (left right : KExpr .anon) : + SoundOptionalConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.tryDefEqWhnfEta left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.tryDefEqWhnfEta + try dsimp only + refine OptionalConversionPost.ite (fun _ => ?_) fun _ => OptionalConversionPost.pureNone valid + exact tryDefEqWhnfEtaAfterGuard_sound recursive seams left right locals context bounds before a b valid + leftReads leftTyped rightReads rightTyped + +/-- String expansion is never reached on readable operands. -/ +theorem tryDefEqWhnfString_sound {methods : Methods .anon} (left right : KExpr .anon) : + SoundOptionalConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.tryDefEqWhnfString left right).run methods) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.tryDefEqWhnfString + rw [hasStringLiteralPair_eq_false leftReads rightReads] + simp only [Bool.false_eq_true, ↓reduceIte] + exact OptionalConversionPost.pureNone valid + +end Eta + +/-! ### The final tier assembled -/ + +section Final + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} + +/-- Unit-like types, then proof irrelevance. -/ +theorem isDefEqWhnfAfterStructEta_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqWhnfAfterStructEta left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.isDefEqWhnfAfterStructEta + simp only [ReaderT.run_bind] + have unit := seams.unit left right locals context bounds before a b valid leftReads leftTyped rightReads + rightTyped + cases run : (RecM.tryDefEqUnit left right).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error run] + rw [run] at unit + exact unit + | ok answer s₁ => + rw [EStateM.run_bind_ok run] + rw [run] at unit + cases answer with + | true => + simp only [↓reduceIte] + exact ConversionPost.pureTrue unit.1 unit.2 + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + unfold RecM.isDefEqWhnfAfterUnit + exact tryProofIrrel_sound recursive seams resources left right locals context bounds s₁ a b unit + leftReads leftTyped rightReads rightTyped + +/-- Structure eta, then the unit-like and proof-irrelevance fallbacks. -/ +theorem isDefEqWhnfAfterString_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqWhnfAfterString left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.isDefEqWhnfAfterString + exact ConversionPost.optionalThen + (seams.structEta left right locals context bounds before a b valid leftReads leftTyped rightReads + rightTyped) + (fun state valid' => isDefEqWhnfAfterStructEta_sound recursive seams resources left right locals context + bounds state a b valid' leftReads leftTyped rightReads rightTyped) + +/-- String expansion (vacuous), then the remaining fallbacks. -/ +theorem isDefEqWhnfAfterEta_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqWhnfAfterEta left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.isDefEqWhnfAfterEta + exact ConversionPost.optionalThen + (tryDefEqWhnfString_sound left right locals context bounds before a b valid leftReads leftTyped + rightReads rightTyped) + (fun state valid' => isDefEqWhnfAfterString_sound recursive seams resources left right locals context + bounds state a b valid' leftReads leftTyped rightReads rightTyped) + +/-- Lambda eta, then the remaining fallbacks. -/ +theorem isDefEqWhnfAfterNat_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqWhnfAfterNat left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.isDefEqWhnfAfterNat + exact ConversionPost.optionalThen + (tryDefEqWhnfEta_sound recursive seams left right locals context bounds before a b valid leftReads + leftTyped rightReads rightTyped) + (fun state valid' => isDefEqWhnfAfterEta_sound recursive seams resources left right locals context + bounds state a b valid' leftReads leftTyped rightReads rightTyped) + +/-- Nat bridging, then the remaining fallbacks. -/ +theorem isDefEqWhnfAfterStructural_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqWhnfAfterStructural left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.isDefEqWhnfAfterStructural + exact ConversionPost.optionalThen + (seams.whnfNat left right locals context bounds before a b valid leftReads leftTyped rightReads + rightTyped) + (fun state valid' => isDefEqWhnfAfterNat_sound recursive seams resources left right locals context + bounds state a b valid' leftReads leftTyped rightReads rightTyped) + +/-- Tier 5: the structural prefix, then the ordered fallbacks. -/ +theorem isDefEqWhnf_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqWhnf left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.isDefEqWhnf + exact ConversionPost.optionalThen + (tryDefEqWhnfStructural_sound recursive seams binders resources left right locals context bounds before + a b valid leftReads leftTyped rightReads rightTyped) + (fun state valid' => isDefEqWhnfAfterStructural_sound recursive seams resources left right locals + context bounds state a b valid' leftReads leftTyped rightReads rightTyped) + +end Final + +/-! ### The stopped continuation -/ + +section Stopped + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} + +/-- Post-delta structural congruence: constants by the universe gate, +projections through the projection-delta seam; legacy variables have no +reading. -/ +theorem tryStructuralCongruence_sound {depth : Nat} + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.tryStructuralCongruence left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.tryStructuralCongruence + split + · rename_i id1 us1 info1 id2 us2 info2 + rw [pure_run] + cases gate : (id1.addr == id2.addr && RecM.sameDefEqUniverses us1 us2) + · exact valid + · simp only [Bool.and_eq_true, beq_iff_eq] at gate + exact ⟨valid, ConversionClaim.constInstances leftReads rightReads gate.1 gate.2 + (fun u _ v _ => resources.tiers.sorts u v info1 info2 trivial trivial)⟩ + · rw [readScopedExpr?_var_none] at leftReads + cases leftReads + · rename_i id1 f1 v1 info1 id2 f2 v2 info2 + refine ConversionPost.ite (fun _ => ConversionPost.pureFalse valid) fun mismatch => ?_ + simp only [Bool.or_eq_true, bne_iff_ne, ne_eq, not_or, Classical.not_not] at mismatch + obtain ⟨sameHead, sameField⟩ := mismatch + subst sameField + obtain ⟨ref, va, resolved, aEq, vaReads⟩ := readScopedExpr?_prj_annotated leftReads + obtain ⟨ref', vb, resolved', bEq, vbReads⟩ := readScopedExpr?_prj_annotated rightReads + have resolvedAt : resolve id2.addr = some ref := by rw [← sameHead]; exact resolved + cases Option.some.inj (resolvedAt.symm.trans resolved') + have rightReads' : readScopedExpr? resolve locals (.prj id1 f1 v2 info2) = some b.erase := by + rw [bEq] + simp [readScopedExpr?, resolved, vbReads, AExpr.erase] + exact seams.projectionDelta id1 f1 v1 v2 info1 info2 locals context bounds before a b valid leftReads + leftTyped rightReads' rightTyped + · exact ConversionPost.pureFalse valid + +/-- The application-spine comparison: equal spine sizes, the heads through +the recursive callback, then the argument loop. -/ +theorem tryDefEqApp_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.tryDefEqApp left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.tryDefEqApp + try dsimp only + refine ConversionPost.ite (fun _ => ConversionPost.pureFalse valid) fun _ => ?_ + rcases spineA : left.collectSpine with ⟨aHead, aArgs⟩ + rcases spineB : right.collectSpine with ⟨bHead, bArgs⟩ + try dsimp only + refine ConversionPost.ite (fun _ => ConversionPost.pureFalse valid) fun sizes => ?_ + have sizes' : aArgs.size = bArgs.size := by simpa using sizes + obtain ⟨aEq, bEq, aHeadReads, bHeadReads, aHeadTyped, bHeadTyped, lengths, readings⟩ := + spine_parts seams.appHereditary leftReads leftTyped rightReads rightTyped + (by rw [spineA, spineB]; exact sizes') + rw [spineA] at aHeadReads readings + rw [spineB] at bHeadReads readings + dsimp only at aHeadReads bHeadReads readings + simp only [ReaderT.run_bind, isDefEqCall_run] + have heads := recursive.isDefEq aHead bHead locals context bounds before _ _ valid aHeadReads aHeadTyped + bHeadReads bHeadTyped + cases runH : (methodsN depth).isDefEq aHead bHead before with + | error err s₁ => + rw [EStateM.run_bind_error runH] + rw [runH] at heads + exact heads + | ok answer s₁ => + rw [EStateM.run_bind_ok runH] + rw [runH] at heads + cases answer with + | false => + simp only [Bool.not_false, ↓reduceIte] + exact ConversionPost.pureFalse heads + | true => + obtain ⟨valid₁, headClaim⟩ := heads + simp only [Bool.not_true, Bool.false_eq_true, ↓reduceIte] + have spine := allDefEqSpineArgs_sound recursive.toDefEqContract (pairs := aArgs.zip bArgs) + (by rw [Array.toList_zip]; exact readings) valid₁ + cases runS : (RecM.allDefEqSpineArgs (aArgs.zip bArgs)).run (methodsN depth) s₁ with + | error err s₂ => + rw [runS] at spine + exact spine + | ok answer s₂ => + rw [runS] at spine + cases answer with + | false => exact spine + | true => + obtain ⟨valid₂, claims⟩ := spine + refine ⟨valid₂, ?_⟩ + rw [aEq, bEq] + exact ConversionClaim.appN headClaim lengths claims + +/-- After lazy delta stops: structural congruence, the second structural +pass, the address and quick checks, the spine comparison, and the final tier. -/ +theorem isDefEqAfterLazyDeltaStopped_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqAfterLazyDeltaStopped left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.isDefEqAfterLazyDeltaStopped + simp only [ReaderT.run_bind] + have congruence := tryStructuralCongruence_sound seams resources left right locals context bounds before + a b valid leftReads leftTyped rightReads rightTyped + cases runC : (RecM.tryStructuralCongruence left right).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runC] + rw [runC] at congruence + exact congruence + | ok answer s₁ => + rw [EStateM.run_bind_ok runC] + rw [runC] at congruence + cases answer with + | true => + simp only [↓reduceIte] + exact ConversionPost.pureTrue congruence.1 congruence.2 + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + simp only [ReaderT.run_bind] + have reducedA := seams.whnfCore left locals context bounds s₁ a congruence leftReads leftTyped + cases runA : (RecM.whnfCore left).run (methodsN depth) s₁ with + | error err s₂ => + rw [EStateM.run_bind_error runA] + rw [runA] at reducedA + exact reducedA + | ok ca s₂ => + rw [EStateM.run_bind_ok runA] + rw [runA] at reducedA + obtain ⟨valid₂, Ca, caReads, claimA, typesA⟩ := reducedA + have caTyped : ∃ type, TypingClaim.{u,v} entries context Ca type := + leftTyped.imp fun _ => typesA _ + have reducedB := seams.whnfCore right locals context bounds s₂ b valid₂ rightReads rightTyped + cases runB : (RecM.whnfCore right).run (methodsN depth) s₂ with + | error err s₃ => + rw [EStateM.run_bind_error runB] + rw [runB] at reducedB + exact reducedB + | ok cb s₃ => + rw [EStateM.run_bind_ok runB] + rw [runB] at reducedB + obtain ⟨valid₃, Cb, cbReads, claimB, typesB⟩ := reducedB + have cbTyped : ∃ type, TypingClaim.{u,v} entries context Cb type := + rightTyped.imp fun _ => typesB _ + have lift : ∀ {result : EStateM.Result (TcError .anon) (TcState .anon) Bool}, + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds + Ca Cb result → + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds + a b result := by + intro result post + cases result with + | error err after => exact post + | ok answer after => + cases answer with + | false => exact post + | true => exact ⟨post.1, claimA.trans (post.2.trans claimB.symm)⟩ + try dsimp only + refine ConversionPost.ite (fun _ => ?_) fun _ => ?_ + · simp only [isDefEqCall_run] + have conv := recursive.isDefEq ca cb locals context bounds s₃ Ca Cb valid₃ caReads + caTyped cbReads cbTyped + cases runD : (methodsN depth).isDefEq ca cb s₃ with + | error err s₄ => + rw [runD] at conv + exact lift conv + | ok answer s₄ => + rw [runD] at conv + exact lift conv + · refine ConversionPost.ite (fun same => ?_) fun _ => ?_ + · exact ConversionPost.pureTrue valid₃ (claimA.trans ((ConversionClaim.ofAddrEq + (resources.tiers.faithful ca cb trivial trivial) annotations same caReads caTyped + cbReads cbTyped).trans claimB.symm)) + · simp only [ReaderT.run_bind] + have quick := quickDefEq_sound recursive.toDefEqContract + (MethodsLocalState.methodsN depth) binders resources.tiers valid₃ trivial trivial + caReads caTyped cbReads cbTyped + cases runQ : (RecM.quickDefEq ca cb).run (methodsN depth) s₃ with + | error err s₄ => + rw [EStateM.run_bind_error runQ] + rw [runQ] at quick + exact quick + | ok answer s₄ => + rw [EStateM.run_bind_ok runQ] + rw [runQ] at quick + cases answer with + | true => + simp only [↓reduceIte] + exact ConversionPost.pureTrue quick.1 + (claimA.trans (quick.2.trans claimB.symm)) + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + simp only [ReaderT.run_bind] + have app := tryDefEqApp_sound recursive seams ca cb locals context bounds s₄ + Ca Cb quick caReads caTyped cbReads cbTyped + cases runP : (RecM.tryDefEqApp ca cb).run (methodsN depth) s₄ with + | error err s₅ => + rw [EStateM.run_bind_error runP] + rw [runP] at app + exact app + | ok answer s₅ => + rw [EStateM.run_bind_ok runP] + rw [runP] at app + cases answer with + | true => + simp only [↓reduceIte] + exact ConversionPost.pureTrue app.1 + (claimA.trans (app.2.trans claimB.symm)) + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + exact lift (isDefEqWhnf_sound recursive seams binders resources ca cb + locals context bounds s₅ Ca Cb app caReads caTyped cbReads cbTyped) + +end Stopped + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/DefEqLazyDelta.lean b/Ix/Kernel/Verify/Consistency/DefEqLazyDelta.lean new file mode 100644 index 000000000..4d1250275 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/DefEqLazyDelta.lean @@ -0,0 +1,1335 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.DefEqFinal + +/-! +# The lazy-delta tier and the assembled reducing tiers + +Tier 4 of production conversion is a bounded loop over operand pairs: the +Nat-offset probe, the gated Nat reducers, the accelerators, delta +classification of both heads, the asymmetric projection-app probe, rank +dispatch, the guarded same-head spine attempt, one- or two-sided unfolding +followed by no-delta normalization, and the finishing address and quick +checks. This module proves the loop sound against the checker invariant: the +loop state carries readable, typed operands convertible to the originals, an +answer `true` carries their conversion, and a stopped pair keeps the state +invariant. The optional reducers and probes are the seams of +`DefEqReducingSeams`; the structural pieces are proved: the finishing checks +through the hash path and the quick tier, the same-head spine comparison by +constant-instance and argument congruence, the speculative attempt's fuel +bookkeeping, the rejection-only cache as a memo probe whose write carries no +claim, and every lookup-based classification and ranking. + +The last section assembles `isDefEqInnerAfterQuick_sound` from the upper +chain, the loop, and the stopped continuation, and states the corollary that +discharges the `inner` field of `DefEqSeamAssumptions`: the conversion body +is sound modulo `DefEqReducingSeams`, the memo transport, the annotation +discipline, the binder premises, and the finite resources. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-! ### Outcomes of the loop -/ + +section LoopPosts + +variable {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (source : Ixon.Env) + (catalog : List (SourceCacheRequest source)) (locals : List FVarId) + (context : Model.Context β) (bounds : List VLevel) + +/-- The loop state: an invariant state and readable, typed operands +convertible to the originals. -/ +def LazyPairInvariant (a b : AExpr β) (pair : KExpr .anon × KExpr .anon) (state : TcState .anon) : + Prop := + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state ∧ + ∃ x y : AExpr β, + readScopedExpr? resolve locals pair.1 = some x.erase ∧ + (∃ type, TypingClaim.{u,v} entries context x type) ∧ + readScopedExpr? resolve locals pair.2 = some y.erase ∧ + (∃ type, TypingClaim.{u,v} entries context y type) ∧ + ConversionClaim.{u,v} entries context a x ∧ ConversionClaim.{u,v} entries context b y + +/-- The loop's outcome: an answer `true` carries the conversion, a stopped +pair is a loop state. -/ +def LazyDeltaPost (a b : AExpr β) : + EStateM.Result (TcError .anon) (TcState .anon) (LazyDeltaLoopResult .anon) → Prop + | .ok (.answer true) after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + ConversionClaim.{u,v} entries context a b + | .ok (.answer false) after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + | .ok (.stopped left right) after => + LazyPairInvariant.{u,v} resolve anchor entries source catalog locals context bounds a b + (left, right) after + | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + +/-- One iteration's outcome: a continued pair is a loop state, a terminal +result is a loop outcome. -/ +def LazyStepPost (a b : AExpr β) : + EStateM.Result (TcError .anon) (TcState .anon) + (RecM.BoundedStep (KExpr .anon × KExpr .anon) (LazyDeltaLoopResult .anon)) → Prop + | .ok (.next pair) after => + LazyPairInvariant.{u,v} resolve anchor entries source catalog locals context bounds a b pair after + | .ok (.done result) after => + LazyDeltaPost.{u,v} resolve anchor entries source catalog locals context bounds a b + (.ok result after) + | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + +end LoopPosts + +section LoopLemmas + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +theorem LazyPairInvariant.refl {state : TcState .anon} {left right : KExpr .anon} {x y : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) + (leftReads : readScopedExpr? resolve locals left = some x.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context x type) + (rightReads : readScopedExpr? resolve locals right = some y.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context y type) : + LazyPairInvariant.{u,v} resolve anchor entries source catalog locals context bounds x y + (left, right) state := + ⟨valid, x, y, leftReads, leftTyped, rightReads, rightTyped, ConversionClaim.refl x, + ConversionClaim.refl y⟩ + +theorem LazyPairInvariant.lift {a b x y : AExpr β} {pair : KExpr .anon × KExpr .anon} + {state : TcState .anon} + (claimA : ConversionClaim.{u,v} entries context a x) (claimB : ConversionClaim.{u,v} entries context b y) + (invariant : LazyPairInvariant.{u,v} resolve anchor entries source catalog locals context bounds x y + pair state) : + LazyPairInvariant.{u,v} resolve anchor entries source catalog locals context bounds a b pair state := by + obtain ⟨valid, x', y', xReads, xTyped, yReads, yTyped, claimX, claimY⟩ := invariant + exact ⟨valid, x', y', xReads, xTyped, yReads, yTyped, claimA.trans claimX, claimB.trans claimY⟩ + +theorem LazyDeltaPost.lift {a b x y : AExpr β} + {result : EStateM.Result (TcError .anon) (TcState .anon) (LazyDeltaLoopResult .anon)} + (claimA : ConversionClaim.{u,v} entries context a x) (claimB : ConversionClaim.{u,v} entries context b y) + (post : LazyDeltaPost.{u,v} resolve anchor entries source catalog locals context bounds x y result) : + LazyDeltaPost.{u,v} resolve anchor entries source catalog locals context bounds a b result := by + cases result with + | error err after => exact post + | ok value after => + cases value with + | answer answer => + cases answer with + | false => exact post + | true => exact ⟨post.1, claimA.trans (post.2.trans claimB.symm)⟩ + | stopped left right => exact LazyPairInvariant.lift claimA claimB post + +theorem LazyStepPost.lift {a b x y : AExpr β} + {result : EStateM.Result (TcError .anon) (TcState .anon) + (RecM.BoundedStep (KExpr .anon × KExpr .anon) (LazyDeltaLoopResult .anon))} + (claimA : ConversionClaim.{u,v} entries context a x) (claimB : ConversionClaim.{u,v} entries context b y) + (post : LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y result) : + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds a b result := by + cases result with + | error err after => exact post + | ok value after => + cases value with + | next pair => exact LazyPairInvariant.lift claimA claimB post + | done result => exact LazyDeltaPost.lift claimA claimB post + +/-- A branch on any decidable condition, for any outcome property. -/ +theorem RecM.ite_post {α : Type} {Post : EStateM.Result (TcError .anon) (TcState .anon) α → Prop} + {c : Prop} [Decidable c] {left right : RecM .anon α} {methods : Methods .anon} {before : TcState .anon} + (thenSound : c → Post (left.run methods before)) (elseSound : ¬ c → Post (right.run methods before)) : + Post ((if c then left else right).run methods before) := by + by_cases h : c + · rw [if_pos h] + exact thenSound h + · rw [if_neg h] + exact elseSound h + +/-- An answer through the recursive callback on a reduced left operand. -/ +theorem LazyStepPost.ofCallLeft {methods : Methods .anon} + (recursive : DefEqContract.{u,v} resolve anchor entries source catalog methods) + {before : TcState .anon} {left right : KExpr .anon} {x x' y : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (claim : ConversionClaim.{u,v} entries context x x') + (leftReads : readScopedExpr? resolve locals left = some x'.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context x' type) + (rightReads : readScopedExpr? resolve locals right = some y.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context y type) : + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.isDefEqCall left right >>= fun answer => + pure (RecM.BoundedStep.done (LazyDeltaLoopResult.answer answer))).run methods before) := by + simp only [ReaderT.run_bind, isDefEqCall_run] + have conv := recursive.isDefEq left right locals context bounds before x' y valid leftReads leftTyped + rightReads rightTyped + cases run : methods.isDefEq left right before with + | error err after => + rw [EStateM.run_bind_error run] + rw [run] at conv + exact conv + | ok answer after => + rw [EStateM.run_bind_ok run] + rw [run] at conv + rw [pure_run] + cases answer with + | false => exact conv + | true => exact ⟨conv.1, claim.trans conv.2⟩ + +/-- An answer through the recursive callback on a reduced right operand. -/ +theorem LazyStepPost.ofCallRight {methods : Methods .anon} + (recursive : DefEqContract.{u,v} resolve anchor entries source catalog methods) + {before : TcState .anon} {left right : KExpr .anon} {x y y' : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (claim : ConversionClaim.{u,v} entries context y y') + (leftReads : readScopedExpr? resolve locals left = some x.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context x type) + (rightReads : readScopedExpr? resolve locals right = some y'.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context y' type) : + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.isDefEqCall left right >>= fun answer => + pure (RecM.BoundedStep.done (LazyDeltaLoopResult.answer answer))).run methods before) := by + simp only [ReaderT.run_bind, isDefEqCall_run] + have conv := recursive.isDefEq left right locals context bounds before x y' valid leftReads leftTyped + rightReads rightTyped + cases run : methods.isDefEq left right before with + | error err after => + rw [EStateM.run_bind_error run] + rw [run] at conv + exact conv + | ok answer after => + rw [EStateM.run_bind_ok run] + rw [run] at conv + rw [pure_run] + cases answer with + | false => exact conv + | true => exact ⟨conv.1, conv.2.trans claim.symm⟩ + +end LoopLemmas + +/-! ### Lookups -/ + +section Lookups + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +theorem isDelta_keeps {methods : Methods .anon} {before : TcState .anon} + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (id : KId .anon) : + KeepsInvariant.{u,v} resolve anchor entries source catalog locals context bounds + ((RecM.isDelta id).run methods before) := by + unfold RecM.isDelta + simp only [ReaderT.run_bind, ReaderT.run_monadLift, monadLift_self] + refine KeepsInvariant.lookup valid (resources.lookup before locals context bounds valid id.addr) ?_ + intro value state + split <;> (try split) <;> exact ⟨_, rfl⟩ + +theorem isRegular_keeps {methods : Methods .anon} {before : TcState .anon} + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (id : KId .anon) : + KeepsInvariant.{u,v} resolve anchor entries source catalog locals context bounds + ((RecM.isRegular id).run methods before) := by + unfold RecM.isRegular + simp only [ReaderT.run_bind, ReaderT.run_monadLift, monadLift_self] + refine KeepsInvariant.lookup valid (resources.lookup before locals context bounds valid id.addr) ?_ + intro value state + split <;> exact ⟨_, rfl⟩ + +theorem defRankId_keeps {methods : Methods .anon} {before : TcState .anon} + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (id : KId .anon) : + KeepsInvariant.{u,v} resolve anchor entries source catalog locals context bounds + ((RecM.defRankId id).run methods before) := by + unfold RecM.defRankId + simp only [ReaderT.run_bind, ReaderT.run_monadLift, monadLift_self] + refine KeepsInvariant.lookup valid (resources.lookup before locals context bounds valid id.addr) ?_ + intro value state + split <;> (try split) <;> (try split) <;> exact ⟨_, rfl⟩ + +theorem classifyDeltaHead_keeps {methods : Methods .anon} {before : TcState .anon} + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (term : KExpr .anon) : + KeepsInvariant.{u,v} resolve anchor entries source catalog locals context bounds + ((RecM.classifyDeltaHead term).run methods before) := by + unfold RecM.classifyDeltaHead + cases headConstId term with + | some id => exact isDelta_keeps resources valid id + | none => + rw [pure_run] + exact valid + +theorem rankDeltaHead_keeps {methods : Methods .anon} {before : TcState .anon} + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (head : Option (KId .anon)) : + KeepsInvariant.{u,v} resolve anchor entries source catalog locals context bounds + ((RecM.rankDeltaHead head).run methods before) := by + unfold RecM.rankDeltaHead + cases head with + | some id => exact defRankId_keeps resources valid id + | none => + rw [pure_run] + exact valid + +end Lookups + +/-! ### The same-head spine attempt -/ + +section SameHead + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} + +/-- Two spines with one constant head that pass the universe gate compare +by argument congruence. -/ +theorem trySameHeadSpine_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (left right : KExpr .anon) : + SoundOptionalConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.trySameHeadSpine left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.trySameHeadSpine + rcases spineA : left.collectSpine with ⟨aHead, aArgs⟩ + rcases spineB : right.collectSpine with ⟨bHead, bArgs⟩ + try dsimp only + cases aHead + case const aId aUs aInfo => + cases bHead + case const bId bUs bInfo => + try dsimp only + refine RecM.ite_post (fun _ => OptionalConversionPost.pureNone valid) fun mismatch => ?_ + simp at mismatch + obtain ⟨sameHead, sizes⟩ := mismatch + refine RecM.ite_post (fun _ => OptionalConversionPost.pureNone valid) fun gate => ?_ + simp at gate + obtain ⟨aEq, bEq, aHeadReads, bHeadReads, _, _, lengths, readings⟩ := + spine_parts seams.appHereditary leftReads leftTyped rightReads rightTyped + (by rw [spineA, spineB]; exact sizes) + rw [spineA] at aHeadReads readings + rw [spineB] at bHeadReads readings + try simp only [ReaderT.run_bind] + have spine := allDefEqSpineArgs_sound recursive.toDefEqContract (pairs := aArgs.zip bArgs) + (by rw [Array.toList_zip]; exact readings) valid + cases runS : (RecM.allDefEqSpineArgs (aArgs.zip bArgs)).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runS] + rw [runS] at spine + exact spine + | ok answer s₁ => + rw [EStateM.run_bind_ok runS] + rw [runS] at spine + cases answer with + | false => + simp only [Bool.not_false, ↓reduceIte] + exact OptionalConversionPost.pureNone spine + | true => + obtain ⟨valid₁, claims⟩ := spine + simp only [Bool.not_true, Bool.false_eq_true, ↓reduceIte] + refine OptionalConversionPost.pureSome valid₁ true fun _ => ?_ + rw [aEq, bEq] + exact ConversionClaim.appN + (ConversionClaim.constInstances aHeadReads bHeadReads sameHead gate + (fun u _ v _ => resources.tiers.sorts u v aInfo bInfo trivial trivial)) + lengths claims + all_goals + try dsimp only + exact OptionalConversionPost.pureNone valid + all_goals + try dsimp only + exact OptionalConversionPost.pureNone valid + +/-- The speculative attempt: a local fuel slice around the attempt, restored +afterwards, with exhaustion reported as a miss. -/ +theorem trySameHeadSpineSpeculative_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (left right : KExpr .anon) : + SoundOptionalConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.trySameHeadSpineSpeculative left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.trySameHeadSpineSpeculative + try simp only [ReaderT.run_bind] + rw [EStateM.run_bind_ok (get_run (methodsN depth) before)] + try dsimp only + refine RecM.ite_post (fun _ => OptionalConversionPost.pureNone valid) fun _ => ?_ + try simp only [ReaderT.run_bind] + rw [EStateM.run_bind_ok (modify_run _ (methodsN depth) before)] + try dsimp only + have valid₁ : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {before with recFuel := min before.recFuel sameHeadSpeculationAttemptFuel} := + valid.ofDefEqScalars rfl rfl rfl rfl + have attempt := trySameHeadSpine_sound recursive seams resources left right locals context bounds + {before with recFuel := min before.recFuel sameHeadSpeculationAttemptFuel} a b valid₁ leftReads + leftTyped rightReads rightTyped + have caught := tryExcept_run (RecM.trySameHeadSpine left right) (methodsN depth) + {before with recFuel := min before.recFuel sameHeadSpeculationAttemptFuel} + cases runT : (RecM.trySameHeadSpine left right).run (methodsN depth) + {before with recFuel := min before.recFuel sameHeadSpeculationAttemptFuel} with + | error err s₁ => + rw [runT] at caught attempt + rw [EStateM.run_bind_ok caught] + try dsimp only + try simp only [ReaderT.run_bind] + rw [EStateM.run_bind_ok (get_run (methodsN depth) s₁)] + try dsimp only + rw [EStateM.run_bind_ok (modify_run _ (methodsN depth) s₁)] + try dsimp only + cases err <;> (try dsimp only) <;> first + | (rw [pure_run]; exact attempt.ofDefEqScalars rfl rfl rfl rfl) + | (rw [throw_run]; exact attempt.ofDefEqScalars rfl rfl rfl rfl) + | ok answer s₁ => + rw [runT] at caught attempt + rw [EStateM.run_bind_ok caught] + try dsimp only + try simp only [ReaderT.run_bind] + rw [EStateM.run_bind_ok (get_run (methodsN depth) s₁)] + try dsimp only + rw [EStateM.run_bind_ok (modify_run _ (methodsN depth) s₁)] + try dsimp only + rw [pure_run] + cases answer with + | none => exact attempt.ofDefEqScalars rfl rfl rfl rfl + | some answer => + cases answer with + | false => exact attempt.ofDefEqScalars rfl rfl rfl rfl + | true => exact ⟨attempt.1.ofDefEqScalars rfl rfl rfl rfl, attempt.2⟩ + +/-- The guarded attempt behind the rejection-only cache: a cached rejection +skips the attempt, a genuine miss records it, and the record carries no claim. -/ +theorem trySameHeadSpineCached_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (speculative : Bool) (left right : KExpr .anon) : + SoundOptionalConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.trySameHeadSpineCached speculative left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.trySameHeadSpineCached + simp only [ReaderT.run_bind, ReaderT.run_monadLift, monadLift_self] + obtain ⟨ctxAddr, s₁, keyRun⟩ := ctxAddrForLbr_ok (max left.lbr right.lbr) before + have keyRun' : TcM.defEqCtxKey left right before = .ok ctxAddr s₁ := keyRun + rw [EStateM.run_bind_ok keyRun'] + have valid₁ := valid.defEqCtxKey keyRun' + try dsimp only + rw [EStateM.run_bind_ok (get_run (methodsN depth) s₁)] + try dsimp only + refine RecM.ite_post (fun _ => OptionalConversionPost.pureNone valid₁) fun _ => ?_ + have attempt : OptionalConversionPost.{u,v} resolve anchor entries source catalog locals context bounds + a b ((if speculative = true then RecM.trySameHeadSpineSpeculative left right + else RecM.trySameHeadSpine left right).run (methodsN depth) s₁) := + RecM.ite_post + (fun _ => trySameHeadSpineSpeculative_sound recursive seams resources left right locals context + bounds s₁ a b valid₁ leftReads leftTyped rightReads rightTyped) + (fun _ => trySameHeadSpine_sound recursive seams resources left right locals context bounds s₁ a b + valid₁ leftReads leftTyped rightReads rightTyped) + try simp only [ReaderT.run_bind] + cases runA : (if speculative = true then RecM.trySameHeadSpineSpeculative left right + else RecM.trySameHeadSpine left right).run (methodsN depth) s₁ with + | error err s₂ => + rw [EStateM.run_bind_error runA] + rw [runA] at attempt + exact attempt + | ok answer s₂ => + rw [EStateM.run_bind_ok runA] + rw [runA] at attempt + cases answer with + | some answer => + try dsimp only + rw [pure_run] + exact attempt + | none => + try dsimp only + try simp only [ReaderT.run_bind] + rw [EStateM.run_bind_ok (modify_run _ (methodsN depth) s₂)] + try dsimp only + rw [pure_run] + exact attempt.ofDefEqFailure _ + +end SameHead + +/-! ### The loop iteration -/ + +section Iteration + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +/-- The finishing checks of a productive iteration. -/ +theorem finishDefEqLazyDeltaStep_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + {before : TcState .anon} {left right : KExpr .anon} {x y : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftReads : readScopedExpr? resolve locals left = some x.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context x type) + (rightReads : readScopedExpr? resolve locals right = some y.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context y type) : + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.finishDefEqLazyDeltaStep left right).run (methodsN depth) before) := by + unfold RecM.finishDefEqLazyDeltaStep + refine RecM.ite_post (fun same => ?_) fun _ => ?_ + · rw [pure_run] + exact ⟨valid, ConversionClaim.ofAddrEq (resources.tiers.faithful left right trivial trivial) annotations + same leftReads leftTyped rightReads rightTyped⟩ + · simp only [ReaderT.run_bind] + have quick := quickDefEq_sound recursive.toDefEqContract (MethodsLocalState.methodsN depth) binders + resources.tiers valid trivial trivial leftReads leftTyped rightReads rightTyped + cases runQ : (RecM.quickDefEq left right).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runQ] + rw [runQ] at quick + exact quick + | ok answer s₁ => + rw [EStateM.run_bind_ok runQ] + rw [runQ] at quick + cases answer with + | true => + simp only [↓reduceIte] + rw [pure_run] + exact ⟨quick.1, quick.2⟩ + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + rw [pure_run] + exact LazyPairInvariant.refl quick leftReads leftTyped rightReads rightTyped + +/-- One-sided unfolding of the left operand. -/ +theorem defEqLazyDeltaStepWithLeftDelta_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + {before : TcState .anon} {left right : KExpr .anon} {x y : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftReads : readScopedExpr? resolve locals left = some x.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context x type) + (rightReads : readScopedExpr? resolve locals right = some y.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context y type) : + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.defEqLazyDeltaStepWithLeftDelta left right).run (methodsN depth) before) := by + unfold RecM.defEqLazyDeltaStepWithLeftDelta + try simp only [ReaderT.run_bind] + have unfolded := seams.deltaUnfoldOne left locals context bounds before x valid leftReads leftTyped + cases runU : (RecM.deltaUnfoldOne left).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runU] + rw [runU] at unfolded + exact unfolded + | ok unfolded? s₁ => + rw [EStateM.run_bind_ok runU] + rw [runU] at unfolded + cases unfolded? with + | none => + try dsimp only + rw [pure_run] + exact LazyPairInvariant.refl unfolded leftReads leftTyped rightReads rightTyped + | some u => + obtain ⟨valid₁, U, uReads, claimXU, typesU⟩ := unfolded + try dsimp only + try simp only [ReaderT.run_bind] + have reduced := whnfNoDeltaForDefEq_sound seams u locals context bounds s₁ U valid₁ uReads + (leftTyped.imp fun _ => typesU _) + cases runR : (RecM.whnfNoDeltaForDefEq u).run (methodsN depth) s₁ with + | error err s₂ => + rw [EStateM.run_bind_error runR] + rw [runR] at reduced + exact reduced + | ok r s₂ => + rw [EStateM.run_bind_ok runR] + rw [runR] at reduced + obtain ⟨valid₂, R, rReads, claimUR, typesR⟩ := reduced + try dsimp only + exact (finishDefEqLazyDeltaStep_sound recursive annotations binders resources valid₂ rReads + (leftTyped.imp fun _ typed => typesR _ (typesU _ typed)) rightReads rightTyped).lift + (claimXU.trans claimUR) (ConversionClaim.refl y) + +/-- One-sided unfolding of the right operand. -/ +theorem defEqLazyDeltaStepWithRightDelta_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + {before : TcState .anon} {left right : KExpr .anon} {x y : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftReads : readScopedExpr? resolve locals left = some x.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context x type) + (rightReads : readScopedExpr? resolve locals right = some y.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context y type) : + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.defEqLazyDeltaStepWithRightDelta left right).run (methodsN depth) before) := by + unfold RecM.defEqLazyDeltaStepWithRightDelta + try simp only [ReaderT.run_bind] + have unfolded := seams.deltaUnfoldOne right locals context bounds before y valid rightReads rightTyped + cases runU : (RecM.deltaUnfoldOne right).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runU] + rw [runU] at unfolded + exact unfolded + | ok unfolded? s₁ => + rw [EStateM.run_bind_ok runU] + rw [runU] at unfolded + cases unfolded? with + | none => + try dsimp only + rw [pure_run] + exact LazyPairInvariant.refl unfolded leftReads leftTyped rightReads rightTyped + | some u => + obtain ⟨valid₁, U, uReads, claimYU, typesU⟩ := unfolded + try dsimp only + try simp only [ReaderT.run_bind] + have reduced := whnfNoDeltaForDefEq_sound seams u locals context bounds s₁ U valid₁ uReads + (rightTyped.imp fun _ => typesU _) + cases runR : (RecM.whnfNoDeltaForDefEq u).run (methodsN depth) s₁ with + | error err s₂ => + rw [EStateM.run_bind_error runR] + rw [runR] at reduced + exact reduced + | ok r s₂ => + rw [EStateM.run_bind_ok runR] + rw [runR] at reduced + obtain ⟨valid₂, R, rReads, claimUR, typesR⟩ := reduced + try dsimp only + exact (finishDefEqLazyDeltaStep_sound recursive annotations binders resources valid₂ leftReads + leftTyped rReads (rightTyped.imp fun _ typed => typesR _ (typesU _ typed))).lift + (ConversionClaim.refl x) (claimYU.trans claimUR) + +/-- Two-sided unfolding after the same-head attempt misses. -/ +theorem defEqLazyDeltaStepAfterSameHeadMiss_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + {before : TcState .anon} {left right : KExpr .anon} {x y : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftReads : readScopedExpr? resolve locals left = some x.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context x type) + (rightReads : readScopedExpr? resolve locals right = some y.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context y type) : + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.defEqLazyDeltaStepAfterSameHeadMiss left right).run (methodsN depth) before) := by + unfold RecM.defEqLazyDeltaStepAfterSameHeadMiss + try simp only [ReaderT.run_bind] + have unfoldedA := seams.deltaUnfoldOne left locals context bounds before x valid leftReads leftTyped + cases runA : (RecM.deltaUnfoldOne left).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runA] + rw [runA] at unfoldedA + exact unfoldedA + | ok ua? s₁ => + rw [EStateM.run_bind_ok runA] + rw [runA] at unfoldedA + have validA : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds s₁ := by + cases ua? with + | none => exact unfoldedA + | some _ => exact unfoldedA.1 + try dsimp only + try simp only [ReaderT.run_bind] + have unfoldedB := seams.deltaUnfoldOne right locals context bounds s₁ y validA rightReads rightTyped + cases runB : (RecM.deltaUnfoldOne right).run (methodsN depth) s₁ with + | error err s₂ => + rw [EStateM.run_bind_error runB] + rw [runB] at unfoldedB + exact unfoldedB + | ok ub? s₂ => + rw [EStateM.run_bind_ok runB] + rw [runB] at unfoldedB + try dsimp only + cases ua? with + | none => + cases ub? with + | none => + try dsimp only + rw [pure_run] + exact LazyPairInvariant.refl unfoldedB leftReads leftTyped rightReads rightTyped + | some ub => + obtain ⟨valid₂, U, uReads, claimYU, typesU⟩ := unfoldedB + try dsimp only + try simp only [ReaderT.run_bind] + have reduced := whnfNoDeltaForDefEq_sound seams ub locals context bounds s₂ U valid₂ uReads + (rightTyped.imp fun _ => typesU _) + cases runR : (RecM.whnfNoDeltaForDefEq ub).run (methodsN depth) s₂ with + | error err s₃ => + rw [EStateM.run_bind_error runR] + rw [runR] at reduced + exact reduced + | ok r s₃ => + rw [EStateM.run_bind_ok runR] + rw [runR] at reduced + obtain ⟨valid₃, R, rReads, claimUR, typesR⟩ := reduced + try dsimp only + exact (finishDefEqLazyDeltaStep_sound recursive annotations binders resources valid₃ + leftReads leftTyped rReads + (rightTyped.imp fun _ typed => typesR _ (typesU _ typed))).lift + (ConversionClaim.refl x) (claimYU.trans claimUR) + | some ua => + obtain ⟨_, UA, uaReads, claimXUA, typesUA⟩ := unfoldedA + cases ub? with + | none => + try dsimp only + try simp only [ReaderT.run_bind] + have reduced := whnfNoDeltaForDefEq_sound seams ua locals context bounds s₂ UA unfoldedB + uaReads (leftTyped.imp fun _ => typesUA _) + cases runR : (RecM.whnfNoDeltaForDefEq ua).run (methodsN depth) s₂ with + | error err s₃ => + rw [EStateM.run_bind_error runR] + rw [runR] at reduced + exact reduced + | ok r s₃ => + rw [EStateM.run_bind_ok runR] + rw [runR] at reduced + obtain ⟨valid₃, R, rReads, claimUR, typesR⟩ := reduced + try dsimp only + exact (finishDefEqLazyDeltaStep_sound recursive annotations binders resources valid₃ + rReads (leftTyped.imp fun _ typed => typesR _ (typesUA _ typed)) rightReads + rightTyped).lift (claimXUA.trans claimUR) (ConversionClaim.refl y) + | some ub => + obtain ⟨valid₂, UB, ubReads, claimYUB, typesUB⟩ := unfoldedB + try dsimp only + try simp only [ReaderT.run_bind] + have reducedA := whnfNoDeltaForDefEq_sound seams ua locals context bounds s₂ UA valid₂ + uaReads (leftTyped.imp fun _ => typesUA _) + cases runRA : (RecM.whnfNoDeltaForDefEq ua).run (methodsN depth) s₂ with + | error err s₃ => + rw [EStateM.run_bind_error runRA] + rw [runRA] at reducedA + exact reducedA + | ok ra s₃ => + rw [EStateM.run_bind_ok runRA] + rw [runRA] at reducedA + obtain ⟨valid₃, RA, raReads, claimRA, typesRA⟩ := reducedA + try dsimp only + try simp only [ReaderT.run_bind] + have reducedB := whnfNoDeltaForDefEq_sound seams ub locals context bounds s₃ UB valid₃ + ubReads (rightTyped.imp fun _ => typesUB _) + cases runRB : (RecM.whnfNoDeltaForDefEq ub).run (methodsN depth) s₃ with + | error err s₄ => + rw [EStateM.run_bind_error runRB] + rw [runRB] at reducedB + exact reducedB + | ok rb s₄ => + rw [EStateM.run_bind_ok runRB] + rw [runRB] at reducedB + obtain ⟨valid₄, RB, rbReads, claimRB, typesRB⟩ := reducedB + try dsimp only + exact (finishDefEqLazyDeltaStep_sound recursive annotations binders resources + valid₄ raReads (leftTyped.imp fun _ typed => typesRA _ (typesUA _ typed)) + rbReads (rightTyped.imp fun _ typed => typesRB _ (typesUB _ typed))).lift + (claimXUA.trans claimRA) (claimYUB.trans claimRB) + +/-- Equal rank: the guarded same-head attempt, then two-sided unfolding. -/ +theorem defEqLazyDeltaStepWithEqualRank_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + {before : TcState .anon} {left right : KExpr .anon} {x y : AExpr β} + (aHead bHead : Option (KId .anon)) + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftReads : readScopedExpr? resolve locals left = some x.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context x type) + (rightReads : readScopedExpr? resolve locals right = some y.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context y type) : + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.defEqLazyDeltaStepWithEqualRank left right aHead bHead).run (methodsN depth) before) := by + have miss : ∀ state, + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.defEqLazyDeltaStepAfterSameHeadMiss left right).run (methodsN depth) state) := + fun _ valid' => defEqLazyDeltaStepAfterSameHeadMiss_sound recursive seams annotations binders resources + valid' leftReads leftTyped rightReads rightTyped + unfold RecM.defEqLazyDeltaStepWithEqualRank + try dsimp only + cases aHead with + | none => exact miss before valid + | some ah => + cases bHead with + | none => exact miss before valid + | some bh => + try dsimp only + refine RecM.ite_post (fun _ => ?_) fun _ => miss before valid + try simp only [ReaderT.run_bind] + have regular := isRegular_keeps (methods := methodsN depth) resources valid ah + cases runR : (RecM.isRegular ah).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runR] + rw [runR] at regular + exact regular + | ok isRegular s₁ => + rw [EStateM.run_bind_ok runR] + rw [runR] at regular + try dsimp only + try simp only [ReaderT.run_bind] + have cached := trySameHeadSpineCached_sound recursive seams resources (!isRegular) left right locals + context bounds s₁ x y regular leftReads leftTyped rightReads rightTyped + cases runC : (RecM.trySameHeadSpineCached (!isRegular) left right).run (methodsN depth) s₁ with + | error err s₂ => + rw [EStateM.run_bind_error runC] + rw [runC] at cached + exact cached + | ok answer? s₂ => + rw [EStateM.run_bind_ok runC] + rw [runC] at cached + cases answer? with + | none => + try dsimp only + exact miss s₂ cached + | some answer => + try dsimp only + rw [pure_run] + cases answer with + | false => exact cached + | true => exact cached + +/-- Rank dispatch after the projection probe. -/ +theorem defEqLazyDeltaStepAfterProjectionMiss_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + {before : TcState .anon} {left right : KExpr .anon} {x y : AExpr β} + (aHead bHead : Option (KId .anon)) (aDelta bDelta : Bool) + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftReads : readScopedExpr? resolve locals left = some x.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context x type) + (rightReads : readScopedExpr? resolve locals right = some y.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context y type) : + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.defEqLazyDeltaStepAfterProjectionMiss left right aHead bHead aDelta bDelta).run + (methodsN depth) before) := by + have leftDelta : ∀ state, + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.defEqLazyDeltaStepWithLeftDelta left right).run (methodsN depth) state) := + fun _ valid' => defEqLazyDeltaStepWithLeftDelta_sound recursive seams annotations binders resources + valid' leftReads leftTyped rightReads rightTyped + have rightDelta : ∀ state, + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.defEqLazyDeltaStepWithRightDelta left right).run (methodsN depth) state) := + fun _ valid' => defEqLazyDeltaStepWithRightDelta_sound recursive seams annotations binders resources + valid' leftReads leftTyped rightReads rightTyped + unfold RecM.defEqLazyDeltaStepAfterProjectionMiss + try dsimp only + refine RecM.ite_post (fun _ => ?_) fun _ => RecM.ite_post (fun _ => leftDelta before valid) + fun _ => rightDelta before valid + try simp only [ReaderT.run_bind] + have rankA := rankDeltaHead_keeps (methods := methodsN depth) resources valid aHead + cases runA : (RecM.rankDeltaHead aHead).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runA] + rw [runA] at rankA + exact rankA + | ok waW s₁ => + rw [EStateM.run_bind_ok runA] + rw [runA] at rankA + try dsimp only + try simp only [ReaderT.run_bind] + have rankB := rankDeltaHead_keeps (methods := methodsN depth) resources rankA bHead + cases runB : (RecM.rankDeltaHead bHead).run (methodsN depth) s₁ with + | error err s₂ => + rw [EStateM.run_bind_error runB] + rw [runB] at rankB + exact rankB + | ok wbW s₂ => + rw [EStateM.run_bind_ok runB] + rw [runB] at rankB + try dsimp only + refine RecM.ite_post (fun _ => ?_) fun _ => RecM.ite_post (fun _ => leftDelta s₂ rankB) + fun _ => rightDelta s₂ rankB + exact defEqLazyDeltaStepWithEqualRank_sound recursive seams annotations binders resources aHead + bHead rankB leftReads leftTyped rightReads rightTyped + +/-- The asymmetric projection-app probe before unfolding. -/ +theorem defEqLazyDeltaStepAfterDeltaClassification_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + {before : TcState .anon} {left right : KExpr .anon} {x y : AExpr β} + (aHead bHead : Option (KId .anon)) (aDelta bDelta : Bool) + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftReads : readScopedExpr? resolve locals left = some x.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context x type) + (rightReads : readScopedExpr? resolve locals right = some y.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context y type) : + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.defEqLazyDeltaStepAfterDeltaClassification left right aHead bHead aDelta bDelta).run + (methodsN depth) before) := by + have miss : ∀ state, + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.defEqLazyDeltaStepAfterProjectionMiss left right aHead bHead aDelta bDelta).run + (methodsN depth) state) := + fun _ valid' => defEqLazyDeltaStepAfterProjectionMiss_sound recursive seams annotations binders + resources aHead bHead aDelta bDelta valid' leftReads leftTyped rightReads rightTyped + unfold RecM.defEqLazyDeltaStepAfterDeltaClassification + try dsimp only + refine RecM.ite_post (fun _ => ?_) fun _ => RecM.ite_post (fun _ => ?_) fun _ => miss before valid + · simp only [ReaderT.run_bind] + have probe := seams.unfoldProjApp right locals context bounds before y valid rightReads rightTyped + cases runP : (RecM.tryUnfoldProjApp right).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runP] + rw [runP] at probe + exact probe + | ok reduced? s₁ => + rw [EStateM.run_bind_ok runP] + rw [runP] at probe + cases reduced? with + | none => + try dsimp only + try simp only [ReaderT.run_bind] + try rw [EStateM.run_bind_ok (pure_run _ (methodsN depth) s₁)] + try dsimp only + exact miss s₁ probe + | some reduced => + obtain ⟨valid₁, R, rReads, claimYR, typesR⟩ := probe + try dsimp only + rw [pure_run] + exact ⟨valid₁, x, R, leftReads, leftTyped, rReads, rightTyped.imp fun _ => typesR _, + ConversionClaim.refl x, claimYR⟩ + · simp only [ReaderT.run_bind] + have probe := seams.unfoldProjApp left locals context bounds before x valid leftReads leftTyped + cases runP : (RecM.tryUnfoldProjApp left).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runP] + rw [runP] at probe + exact probe + | ok reduced? s₁ => + rw [EStateM.run_bind_ok runP] + rw [runP] at probe + cases reduced? with + | none => + try dsimp only + try simp only [ReaderT.run_bind] + try rw [EStateM.run_bind_ok (pure_run _ (methodsN depth) s₁)] + try dsimp only + exact miss s₁ probe + | some reduced => + obtain ⟨valid₁, R, rReads, claimXR, typesR⟩ := probe + try dsimp only + rw [pure_run] + exact ⟨valid₁, R, y, rReads, leftTyped.imp fun _ => typesR _, rightReads, rightTyped, + claimXR, ConversionClaim.refl y⟩ + +/-- Delta classification of both heads: neither reducible stops the loop. -/ +theorem defEqLazyDeltaStepAfterAcceleratorMiss_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + {before : TcState .anon} {left right : KExpr .anon} {x y : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftReads : readScopedExpr? resolve locals left = some x.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context x type) + (rightReads : readScopedExpr? resolve locals right = some y.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context y type) : + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.defEqLazyDeltaStepAfterAcceleratorMiss left right).run (methodsN depth) before) := by + unfold RecM.defEqLazyDeltaStepAfterAcceleratorMiss + try dsimp only + try simp only [ReaderT.run_bind] + have classA := classifyDeltaHead_keeps (methods := methodsN depth) resources valid left + cases runA : (RecM.classifyDeltaHead left).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runA] + rw [runA] at classA + exact classA + | ok aDelta s₁ => + rw [EStateM.run_bind_ok runA] + rw [runA] at classA + try dsimp only + try simp only [ReaderT.run_bind] + have classB := classifyDeltaHead_keeps (methods := methodsN depth) resources classA right + cases runB : (RecM.classifyDeltaHead right).run (methodsN depth) s₁ with + | error err s₂ => + rw [EStateM.run_bind_error runB] + rw [runB] at classB + exact classB + | ok bDelta s₂ => + rw [EStateM.run_bind_ok runB] + rw [runB] at classB + try dsimp only + refine RecM.ite_post (fun _ => ?_) fun _ => ?_ + · rw [pure_run] + exact LazyPairInvariant.refl classB leftReads leftTyped rightReads rightTyped + · exact defEqLazyDeltaStepAfterDeltaClassification_sound recursive seams annotations binders + resources _ _ aDelta bDelta classB leftReads leftTyped rightReads rightTyped + +/-- An optional reduction of the left operand answered through the callback, +otherwise the continuation. -/ +theorem LazyStepPost.optionalLeft {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + {probe : RecM .anon (Option (KExpr .anon))} + {next : Option (KExpr .anon) → RecM .anon + (RecM.BoundedStep (KExpr .anon × KExpr .anon) (LazyDeltaLoopResult .anon))} + {before : TcState .anon} {right : KExpr .anon} {x y : AExpr β} + (probeSound : OptionalReductionPost.{u,v} resolve anchor entries source catalog locals context bounds x + (probe.run (methodsN depth) before)) + (answered : ∀ reduced, next (some reduced) = RecM.isDefEqCall reduced right >>= fun answer => + pure (RecM.BoundedStep.done (LazyDeltaLoopResult.answer answer))) + (rightReads : readScopedExpr? resolve locals right = some y.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context y type) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context x type) + (restSound : ∀ state, + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((next none).run (methodsN depth) state)) : + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((probe >>= next).run (methodsN depth) before) := by + try simp only [ReaderT.run_bind] + cases run : probe.run (methodsN depth) before with + | error err after => + rw [EStateM.run_bind_error run] + rw [run] at probeSound + exact probeSound + | ok reduced? after => + rw [EStateM.run_bind_ok run] + rw [run] at probeSound + cases reduced? with + | none => exact restSound after probeSound + | some reduced => + obtain ⟨valid, R, rReads, claim, types⟩ := probeSound + rw [answered] + exact LazyStepPost.ofCallLeft recursive.toDefEqContract valid claim rReads + (leftTyped.imp fun _ => types _) rightReads rightTyped + +/-- An optional reduction of the right operand answered through the callback, +otherwise the continuation. -/ +theorem LazyStepPost.optionalRight {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + {probe : RecM .anon (Option (KExpr .anon))} + {next : Option (KExpr .anon) → RecM .anon + (RecM.BoundedStep (KExpr .anon × KExpr .anon) (LazyDeltaLoopResult .anon))} + {before : TcState .anon} {left : KExpr .anon} {x y : AExpr β} + (probeSound : OptionalReductionPost.{u,v} resolve anchor entries source catalog locals context bounds y + (probe.run (methodsN depth) before)) + (answered : ∀ reduced, next (some reduced) = RecM.isDefEqCall left reduced >>= fun answer => + pure (RecM.BoundedStep.done (LazyDeltaLoopResult.answer answer))) + (leftReads : readScopedExpr? resolve locals left = some x.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context x type) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context y type) + (restSound : ∀ state, + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((next none).run (methodsN depth) state)) : + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((probe >>= next).run (methodsN depth) before) := by + try simp only [ReaderT.run_bind] + cases run : probe.run (methodsN depth) before with + | error err after => + rw [EStateM.run_bind_error run] + rw [run] at probeSound + exact probeSound + | ok reduced? after => + rw [EStateM.run_bind_ok run] + rw [run] at probeSound + cases reduced? with + | none => exact restSound after probeSound + | some reduced => + obtain ⟨valid, R, rReads, claim, types⟩ := probeSound + rw [answered] + exact LazyStepPost.ofCallRight recursive.toDefEqContract valid claim leftReads leftTyped rReads + (rightTyped.imp fun _ => types _) + +/-- The accelerators: native and Decidable reduction on either side. -/ +theorem defEqLazyDeltaStepAfterNatMiss_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + {before : TcState .anon} {left right : KExpr .anon} {x y : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftReads : readScopedExpr? resolve locals left = some x.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context x type) + (rightReads : readScopedExpr? resolve locals right = some y.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context y type) : + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.defEqLazyDeltaStepAfterNatMiss left right).run (methodsN depth) before) := by + unfold RecM.defEqLazyDeltaStepAfterNatMiss + try dsimp only + refine LazyStepPost.optionalLeft recursive + (seams.reduceNative left locals context bounds before x valid leftReads leftTyped) (fun _ => rfl) + rightReads rightTyped leftTyped fun s₁ valid₁ => ?_ + try dsimp only + refine LazyStepPost.optionalRight recursive + (seams.reduceNative right locals context bounds s₁ y valid₁ rightReads rightTyped) (fun _ => rfl) + leftReads leftTyped rightTyped fun s₂ valid₂ => ?_ + try dsimp only + refine LazyStepPost.optionalLeft recursive + (seams.reduceDecidable left locals context bounds s₂ x valid₂ leftReads leftTyped) (fun _ => rfl) + rightReads rightTyped leftTyped fun s₃ valid₃ => ?_ + try dsimp only + refine LazyStepPost.optionalRight recursive + (seams.reduceDecidable right locals context bounds s₃ y valid₃ rightReads rightTyped) (fun _ => rfl) + leftReads leftTyped rightTyped fun s₄ valid₄ => ?_ + try dsimp only + exact defEqLazyDeltaStepAfterAcceleratorMiss_sound recursive seams annotations binders resources valid₄ + leftReads leftTyped rightReads rightTyped + +/-- The gated Nat reducers. -/ +theorem defEqLazyDeltaStepAfterOffsetMiss_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + {before : TcState .anon} {left right : KExpr .anon} {x y : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftReads : readScopedExpr? resolve locals left = some x.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context x type) + (rightReads : readScopedExpr? resolve locals right = some y.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context y type) : + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.defEqLazyDeltaStepAfterOffsetMiss (left, right)).run (methodsN depth) before) := by + have miss : ∀ state, + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.defEqLazyDeltaStepAfterNatMiss left right).run (methodsN depth) state) := + fun _ valid' => defEqLazyDeltaStepAfterNatMiss_sound recursive seams annotations binders resources + valid' leftReads leftTyped rightReads rightTyped + unfold RecM.defEqLazyDeltaStepAfterOffsetMiss + try dsimp only + try simp only [ReaderT.run_bind] + rw [EStateM.run_bind_ok (get_run (methodsN depth) before)] + try dsimp only + refine RecM.ite_post (fun _ => ?_) fun _ => miss before valid + refine LazyStepPost.optionalLeft recursive + (seams.reduceNat left locals context bounds before x valid leftReads leftTyped) (fun _ => rfl) + rightReads rightTyped leftTyped fun s₁ valid₁ => ?_ + try dsimp only + refine LazyStepPost.optionalRight recursive + (seams.reduceNat right locals context bounds s₁ y valid₁ rightReads rightTyped) (fun _ => rfl) + leftReads leftTyped rightTyped fun s₂ valid₂ => ?_ + try dsimp only + exact miss s₂ valid₂ + +/-- One iteration: the Nat-offset probe, then the remaining branches. -/ +theorem defEqLazyDeltaStep_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + {before : TcState .anon} {left right : KExpr .anon} {x y : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftReads : readScopedExpr? resolve locals left = some x.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context x type) + (rightReads : readScopedExpr? resolve locals right = some y.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context y type) : + LazyStepPost.{u,v} resolve anchor entries source catalog locals context bounds x y + ((RecM.defEqLazyDeltaStep (left, right)).run (methodsN depth) before) := by + unfold RecM.defEqLazyDeltaStep + try dsimp only + try simp only [ReaderT.run_bind] + have offset := seams.offset left right locals context bounds before x y valid leftReads leftTyped + rightReads rightTyped + cases runO : (RecM.tryDefEqOffset left right).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runO] + rw [runO] at offset + exact offset + | ok answer? s₁ => + rw [EStateM.run_bind_ok runO] + rw [runO] at offset + cases answer? with + | none => + try dsimp only + exact defEqLazyDeltaStepAfterOffsetMiss_sound recursive seams annotations binders resources offset + leftReads leftTyped rightReads rightTyped + | some answer => + try dsimp only + rw [pure_run] + cases answer with + | false => exact offset + | true => exact offset + +end Iteration + +/-! ### The loop and the lazy-delta pass -/ + +section Loop + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} + +/-- The bounded lazy-delta loop from any loop state. -/ +theorem runDefEqLazyDelta_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {before : TcState .anon} + {left right : KExpr .anon} {a b : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (leftReads : readScopedExpr? resolve locals left = some a.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context a type) + (rightReads : readScopedExpr? resolve locals right = some b.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context b type) : + LazyDeltaPost.{u,v} resolve anchor entries source catalog locals context bounds a b + ((RecM.runDefEqLazyDelta left right).run (methodsN depth) before) := by + unfold RecM.runDefEqLazyDelta + refine runBoundedInvariant_sound + (Inv := LazyPairInvariant.{u,v} resolve anchor entries source catalog locals context bounds a b) + (Post := LazyDeltaPost.{u,v} resolve anchor entries source catalog locals context bounds a b) + ?_ ?_ _ _ before (LazyPairInvariant.refl valid leftReads leftTyped rightReads rightTyped) + · rintro ⟨x, y⟩ state ⟨valid', X, Y, xReads, xTyped, yReads, yTyped, claimX, claimY⟩ + have post := (defEqLazyDeltaStep_sound recursive seams annotations binders resources valid' xReads + xTyped yReads yTyped).lift claimX claimY + cases run : (RecM.defEqLazyDeltaStep (x, y)).run (methodsN depth) state with + | error err after => + rw [run] at post + exact post + | ok next after => + rw [run] at post + cases next with + | next pair => exact post + | done result => exact post + · intro state before invariant + exact invariant.1 + +/-- The lazy-delta pass: an answer, or the stopped continuation on the +final pair. -/ +theorem isDefEqInnerAfterProofIrrelevance_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqInnerAfterProofIrrelevance left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.isDefEqInnerAfterProofIrrelevance + try simp only [ReaderT.run_bind] + have loop := runDefEqLazyDelta_sound recursive seams annotations binders resources valid leftReads + leftTyped rightReads rightTyped + cases run : (RecM.runDefEqLazyDelta left right).run (methodsN depth) before with + | error err after => + rw [EStateM.run_bind_error run] + rw [run] at loop + exact loop + | ok result after => + rw [EStateM.run_bind_ok run] + rw [run] at loop + cases result with + | answer answer => + try dsimp only + rw [pure_run] + cases answer with + | false => exact loop + | true => exact loop + | stopped x y => + obtain ⟨valid', X, Y, xReads, xTyped, yReads, yTyped, claimX, claimY⟩ := loop + try dsimp only + have stopped := isDefEqAfterLazyDeltaStopped_sound recursive seams annotations binders resources x y + locals context bounds after X Y valid' xReads xTyped yReads yTyped + cases runS : (RecM.isDefEqAfterLazyDeltaStopped x y).run (methodsN depth) after with + | error err final => + rw [runS] at stopped + exact stopped + | ok answer final => + rw [runS] at stopped + cases answer with + | false => exact stopped + | true => exact ⟨stopped.1, claimX.trans (stopped.2.trans claimY.symm)⟩ + +end Loop + +/-! ### The assembled reducing tiers -/ + +section Assembly + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} + +/-- The remaining recursive tiers after the quick structural probe: the eager +`Bool.true` shortcut, the vacuous string expansion, the structural-core and +no-delta passes, proof irrelevance, the lazy-delta loop, and the stopped +continuation through the final tier, under the smaller table's contracts, +the reducer seams, the annotation discipline, the binder premises, and the +finite resources. -/ +theorem isDefEqInnerAfterQuick_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqInnerAfterQuick left right).run (methodsN depth)) := + isDefEqInnerAfterQuick_sound_of_tail seams + (isDefEqInnerAfterBoolTrue_sound_of_tail + (isDefEqInnerAfterStringExpansion_sound_of_tail recursive seams annotations binders resources + (isDefEqInnerAfterCorePass_sound_of_tail recursive seams annotations binders resources + (isDefEqInnerAfterNoDeltaPass_sound_of_tail recursive seams resources + (isDefEqInnerAfterProofIrrelevance_sound recursive seams annotations binders resources))))) + left right + +/-- The direct tiers' seam record from the reducing seams: `inner` is +discharged, the other three fields are the model boundaries they were. -/ +theorem DefEqSeamAssumptions.ofReducing {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (transport : DefEqMemoTransport.{u,v} resolve anchor entries source catalog) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) : + DefEqSeamAssumptions.{u,v} resolve anchor entries source catalog depth where + inner := isDefEqInnerAfterQuick_sound recursive seams annotations binders resources + transport := transport + sameRawAnnotations := annotations + binders := binders + +/-- The `StepContracts.isDefEq` field modulo the reducer seams, the memo +transport, the annotation discipline, the binder premises, and the finite +resources. The seams may depend on the smaller table's contracts, as the +reduction package's `StepContracts` fields do. -/ +theorem StepContracts.isDefEq_of_reducing_tiers {depth : Nat} + (seams : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth) → + DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (transport : DefEqMemoTransport.{u,v} resolve anchor entries source catalog) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) : + MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth) → + ∀ left right, SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEq left right).run (methodsN depth)) := + fun recursive => StepContracts.isDefEq_of_direct_tiers + (DefEqSeamAssumptions.ofReducing recursive (seams recursive) transport annotations binders resources) + resources.tiers recursive + +end Assembly + +end Ix.Kernel.Consistency diff --git a/Ix/Kernel/Verify/Consistency/DefEqReducing.lean b/Ix/Kernel/Verify/Consistency/DefEqReducing.lean new file mode 100644 index 000000000..cb3aea2c3 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/DefEqReducing.lean @@ -0,0 +1,1325 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.DefEqTiers + +/-! +# The reducing DefEq tiers under the contracts: seams and shared tools + +After the quick structural probe misses, production conversion reduces: the +eager `Bool.true` shortcut, the cheap structural-core and no-delta passes, +proof irrelevance, the lazy-delta loop, and the final WHNF tiers. This module +states what those tiers consume and proves the pieces shared by all of them. + +Inside the conversion body the reducers `whnf`, `whnfCore`, +`whnfCoreWithFlags`, and `whnfNoDeltaImpl` are the production bodies run at +the conversion's own table, not entries of the `Methods` record; their +soundness at that table is the reduction package's `StepContracts` obligation +and enters here as fields of `DefEqReducingSeams`, together with the optional +reducers of the lazy-delta loop, the Nat-offset and projection-delta probes, +the structure-eta, unit-like, Nat, let, and eta-expansion tails, one frame +fact no invariant field records (full reduction never writes the primitive +table), and three boundaries of the set model: hereditary typing of +applications, formation of the type of a typed term, and transport of a +recorded proposition classification to the caller's registration. The table +edges `isDefEqCall` and `inferOnlyCall` are covered by the smaller table's +`MethodContracts`. + +Proved here: the run equations of the cheap-depth scope, the caught probe, +and the inference-only edge; invariant preservation through lookups, the +is-prop memo write, and the rejection-cache write; soundness of the cheap +reducers from the flagged bodies; the bounded-loop driver; the hash path of +a reduced pair; annotated spine readings with their typing; the proof +irrelevance tier through the memoized proposition classifier; and the upper +chain from the eager `Bool.true` shortcut through the no-delta pass, each +with its continuation abstracted by its contract. +-/ + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model + +universe u v + +/-! ### Run equations of the reducing tiers' primitives -/ + +theorem EStateM.run_bind_ok {ε σ α γ : Type} {action : EStateM ε σ α} {next : α → EStateM ε σ γ} + {state after : σ} {value : α} (run : action state = .ok value after) : + (action >>= next) state = next value after := by + change EStateM.bind action next state = _ + unfold EStateM.bind + rw [run] + +theorem EStateM.run_bind_error {ε σ α γ : Type} {action : EStateM ε σ α} {next : α → EStateM ε σ γ} + {state after : σ} {err : ε} (run : action state = .error err after) : + (action >>= next) state = .error err after := by + change EStateM.bind action next state = _ + unfold EStateM.bind + rw [run] + +/-- The reflexive lift of a checker action into itself. -/ +theorem monadLift_self {α : Type} (action : TcM .anon α) : + (monadLift action : TcM .anon α) = action := rfl + +theorem prims_run (methods : Methods .anon) (state : TcState .anon) : + (RecM.prims (m := .anon)).run methods state = .ok state.prims state := rfl + +theorem inferOnlyCall_run (term : KExpr .anon) (methods : Methods .anon) : + (RecM.inferOnlyCall term).run methods = TcM.withInferOnly (methods.infer term) := rfl + +/-- The caught probe never fails: it maps an error to `none` and keeps the +error-side state. -/ +theorem try?_run {α : Type} (action : RecM .anon α) (methods : Methods .anon) + (state : TcState .anon) : + (RecM.try? action).run methods state = + match action.run methods state with + | .ok value after => .ok (some value) after + | .error _ after => .ok none after := by + unfold RecM.try? + change EStateM.tryCatch (EStateM.bind (action.run methods) _) _ state = _ + unfold EStateM.tryCatch EStateM.bind + cases action.run methods state <;> rfl + +theorem tryFinally_run {α γ : Type} (action : RecM .anon α) (cleanup : RecM .anon γ) + (methods : Methods .anon) (state : TcState .anon) : + (tryFinally action cleanup).run methods state = + match action.run methods state with + | .ok value middle => + match cleanup.run methods middle with + | .ok _ after => .ok value after + | .error err after => .error err after + | .error err middle => + match cleanup.run methods middle with + | .ok _ after => .error err after + | .error err' after => .error err' after := by + change tryFinally (action.run methods) (cleanup.run methods) state = _ + unfold tryFinally + change EStateM.map (fun value : α × γ => value.1) + (tryFinally' (action.run methods) (fun _ => cleanup.run methods)) state = _ + unfold EStateM.map MonadFinally.tryFinally' EStateM.instMonadFinally + cases run : action.run methods state <;> simp only [run] <;> + cases finished : cleanup.run methods _ <;> rfl + +/-- The cheap recursion scope bumps the depth around its body and restores it +on both outcomes. -/ +theorem withCheapRecursionDepth_run {α : Type} (action : RecM .anon α) (methods : Methods .anon) + (state : TcState .anon) : + (RecM.withCheapRecursionDepth action).run methods state = + match action.run methods {state with cheapRecursionDepth := state.cheapRecursionDepth + 1} with + | .ok value after => + .ok value {after with cheapRecursionDepth := after.cheapRecursionDepth - 1} + | .error err after => + .error err {after with cheapRecursionDepth := after.cheapRecursionDepth - 1} := by + unfold RecM.withCheapRecursionDepth + simp only [ReaderT.run_bind] + rw [EStateM.run_bind_ok (modify_run _ methods state), tryFinally_run] + cases action.run methods _ <;> simp only [modify_run] + +/-- The caught inference-only edge: the policy is raised around the callback +and restored on both outcomes. -/ +theorem tryInferOnly_run (term : KExpr .anon) (methods : Methods .anon) (before : TcState .anon) : + (RecM.try? (RecM.inferOnlyCall term)).run methods before = + match methods.infer term {before with inferOnly := true} with + | .ok type after => .ok (some type) {after with inferOnly := before.inferOnly} + | .error _ after => .ok none {after with inferOnly := before.inferOnly} := by + rw [try?_run, inferOnlyCall_run, withInferOnly_eq] + cases methods.infer term {before with inferOnly := true} <;> rfl + +theorem whnfCoreForDefEq_def (term : KExpr .anon) : + RecM.whnfCoreForDefEq term = + RecM.withCheapRecursionDepth (RecM.whnfCoreWithFlags term .DEF_EQ_CORE) := rfl + +theorem whnfNoDeltaForDefEq_def (term : KExpr .anon) : + RecM.whnfNoDeltaForDefEq term = + RecM.withCheapRecursionDepth (RecM.whnfNoDeltaImpl term .DEF_EQ_CORE .collapse) := rfl + +/-- The answer of the `Bool.true` recognizer at a primitive table. -/ +def isBoolTrueAnswer (prims : Primitives .anon) : KExpr .anon → Bool + | .const id us _ => us.isEmpty && id.addr == prims.boolTrue.addr + | _ => false + +theorem isBoolTrue_run (term : KExpr .anon) (methods : Methods .anon) (state : TcState .anon) : + (RecM.isBoolTrue term).run methods state = .ok (isBoolTrueAnswer state.prims term) state := by + unfold RecM.isBoolTrue isBoolTrueAnswer + cases term <;> rfl + +/-- The eager-reduction policy answer: closed syntax, or the caller's marker. -/ +def boolTrueAllowed (state : TcState .anon) (term : KExpr .anon) : Bool := + if !term.hasFVars then true else state.eagerReduce + +theorem boolTrueReductionAllowed_run (term : KExpr .anon) (methods : Methods .anon) + (state : TcState .anon) : + (RecM.boolTrueReductionAllowed term).run methods state = .ok (boolTrueAllowed state term) state := by + unfold RecM.boolTrueReductionAllowed boolTrueAllowed + split <;> rfl + +/-! ### Outcomes of optional probes -/ + +section Posts + +variable {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (source : Ixon.Env) + (catalog : List (SourceCacheRequest source)) (locals : List FVarId) + (context : Model.Context β) (bounds : List VLevel) + +/-- Both outcomes of a run keep the invariant. -/ +def KeepsInvariant {α : Type} : EStateM.Result (TcError .anon) (TcState .anon) α → Prop + | .ok _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + +/-- One optional reduction outcome: `none` and errors keep the invariant; a +result has an annotated reading convertible to the source that retains every +type of the source. -/ +def OptionalReductionPost (term : AExpr β) : + EStateM.Result (TcError .anon) (TcState .anon) (Option (KExpr .anon)) → Prop + | .ok (some result) after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + ∃ target : AExpr β, readScopedExpr? resolve locals result = some target.erase ∧ + ConversionClaim.{u,v} entries context term target ∧ + ∀ type, TypingClaim.{u,v} entries context term type → + TypingClaim.{u,v} entries context target type + | .ok none after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + +/-- One optional conversion outcome: only `some true` carries a claim. -/ +def OptionalConversionPost (left right : AExpr β) : + EStateM.Result (TcError .anon) (TcState .anon) (Option Bool) → Prop + | .ok (some true) after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + ConversionClaim.{u,v} entries context left right + | .ok (some false) after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + | .ok none after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + +/-- One proposition-classification outcome: `true` classifies the annotated +reading as a proposition. -/ +def PropositionPost (term : AExpr β) : + EStateM.Result (TcError .anon) (TcState .anon) Bool → Prop + | .ok true after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + TypingClaim.{u,v} entries context term (.sort .zero) + | .ok false after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + +end Posts + +section SoundOptional + +variable {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (source : Ixon.Env) + (catalog : List (SourceCacheRequest source)) + +/-- An optional reduction probe is sound in every scope. -/ +def SoundOptionalReduction (input : KExpr .anon) (action : TcM .anon (Option (KExpr .anon))) : Prop := + ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (before : TcState .anon) (term : AExpr β), + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before → + readScopedExpr? resolve locals input = some term.erase → + (∃ type, TypingClaim.{u,v} entries context term type) → + OptionalReductionPost.{u,v} resolve anchor entries source catalog locals context bounds term + (action before) + +/-- An optional conversion probe is sound in every scope. -/ +def SoundOptionalConversion (left right : KExpr .anon) (action : TcM .anon (Option Bool)) : Prop := + ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (before : TcState .anon) (a b : AExpr β), + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before → + readScopedExpr? resolve locals left = some a.erase → + (∃ type, TypingClaim.{u,v} entries context a type) → + readScopedExpr? resolve locals right = some b.erase → + (∃ type, TypingClaim.{u,v} entries context b type) → + OptionalConversionPost.{u,v} resolve anchor entries source catalog locals context bounds a b + (action before) + +/-- The annotation discipline of the hash path, as the direct tiers state it: +two typed readings of one raw term carry equal binder annotations. -/ +def SameRawAnnotations : Prop := + ∀ (context : Model.Context β) (a b : AExpr β), a.erase = b.erase → + (∃ type, TypingClaim.{u,v} entries context a type) → + (∃ type, TypingClaim.{u,v} entries context b type) → a.annotations = b.annotations + +/-- Where a proposition-classification key was computed: an invariant state +at the caller's registration whose actual digest at the type's radius is the +key's context component. -/ +def IsPropKeyOrigin (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (type : KExpr .anon) (ctxAddr : Address) : Prop := + ∃ (state after : TcState .anon), + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state ∧ + TcM.ctxAddrForLbr type.lbr state = .ok ctxAddr after + +end SoundOptional + +/-! ### The seams of the reducing tiers -/ + +section Seams + +variable {β : Type u} (resolve : Address → Option (ConstRef β)) + (anchor entries : Model.Environment β) (source : Ixon.Env) + (catalog : List (SourceCacheRequest source)) + +/-- What the reducing tiers consume beyond the smaller table's contracts. + +The first group is soundness of the reduction bodies the conversion tiers +run at their own table: `whnf`, `whnfCore`, and `whnfCoreWithFlags` are +exactly the `StepContracts` reduction fields at this depth, and +`whnfNoDeltaImpl` is the no-delta body those fields are proved through; the +cheap def-eq variants are derived below. The one frame fact, that full +reduction retains the primitive table, is a state field the invariant does +not record. The second group is the optional +reducers and probes of the lazy-delta loop: each returns `none` with the +invariant preserved or a result convertible to its source, the Nat-offset +probe answers as a conversion, and the projection-delta loop compares two +projections of one field. The third group is the final-tier tails that need +inductive structure or literal facts: Nat bridging, structure eta, unit-like +types, let congruence (reached only when structural WHNF left a `let`), and +the constructed eta-expansion, whose reading and typing need the intern +resources and the typing of the expansion. The last group is boundaries of +the set model: the contracts supply whole-term typing only, so hereditary +typing of an application's function and argument, formation of the type of a +typed term, and the transport of a recorded proposition classification to the +caller's registration (the is-prop instance of the context-digest boundary) +are stated here rather than derived. -/ +structure DefEqReducingSeams (depth : Nat) : Prop where + /-- The full reduction body at this table (`StepContracts.whnf`). -/ + whnf : ∀ term, SoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnf term).run (methodsN depth)) + /-- Full reduction never writes the primitive table; the invariant records + no frame for that field, and the eager `Bool.true` tier compares the + recognitions made before and after one reduction. -/ + whnfPrims : ∀ (term : KExpr .anon) (before : TcState .anon), + match (RecM.whnf term).run (methodsN depth) before with + | .ok _ after | .error _ after => after.prims = before.prims + /-- The structural reduction body at this table (`StepContracts.whnfCore`). -/ + whnfCore : ∀ term, SoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfCore term).run (methodsN depth)) + /-- The flagged structural body at this table (`StepContracts.whnfCoreFlags`). -/ + whnfCoreWithFlags : ∀ term flags, SoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfCoreWithFlags term flags).run (methodsN depth)) + /-- The no-delta body at this table under any flags and succ mode. -/ + whnfNoDeltaImpl : ∀ term flags mode, SoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfNoDeltaImpl term flags mode).run (methodsN depth)) + /-- Nat literal and primitive reduction. -/ + reduceNat : ∀ term, SoundOptionalReduction.{u,v} resolve anchor entries source catalog term + ((RecM.tryReduceNat term).run (methodsN depth)) + /-- Native reduction; `none` without state change under `noAccel`. -/ + reduceNative : ∀ term, SoundOptionalReduction.{u,v} resolve anchor entries source catalog term + ((RecM.tryReduceNative term).run (methodsN depth)) + /-- Decidable-instance reduction; `none` without state change under `noAccel`. -/ + reduceDecidable : ∀ term, SoundOptionalReduction.{u,v} resolve anchor entries source catalog term + ((RecM.tryReduceDecidable term).run (methodsN depth)) + /-- The projection-headed no-delta probe. -/ + unfoldProjApp : ∀ term, SoundOptionalReduction.{u,v} resolve anchor entries source catalog term + ((RecM.tryUnfoldProjApp term).run (methodsN depth)) + /-- One delta unfolding of a defined head, justified by the reduction + package's `DefinitionBinding`. -/ + deltaUnfoldOne : ∀ term, SoundOptionalReduction.{u,v} resolve anchor entries source catalog term + ((RecM.deltaUnfoldOne term).run (methodsN depth)) + /-- Nat-offset comparison by shared-offset injectivity. -/ + offset : ∀ left right, SoundOptionalConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.tryDefEqOffset left right).run (methodsN depth)) + /-- The projection-directed delta loop on two projections of one field. -/ + projectionDelta : ∀ (id : KId .anon) (field : UInt64) (left right : KExpr .anon) + (leftInfo rightInfo : ExprInfo .anon), + SoundConversion.{u,v} resolve anchor entries source catalog (.prj id field left leftInfo) + (.prj id field right rightInfo) ((RecM.lazyDeltaProjReduction id field left right).run (methodsN depth)) + /-- Nat-like bridging in the final tier. -/ + whnfNat : ∀ left right, SoundOptionalConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.tryDefEqWhnfNat left right).run (methodsN depth)) + /-- Structure eta in both directions. -/ + structEta : ∀ left right, SoundOptionalConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.tryDefEqWhnfStructEta left right).run (methodsN depth)) + /-- Unit-like types. -/ + unit : ∀ left right, SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.tryDefEqUnit left right).run (methodsN depth)) + /-- Let congruence through the local declaration. -/ + whnfLet : ∀ (name : Mode.anon.F Name) (ty1 v1 body1 ty2 v2 body2 : KExpr .anon) + (nonDep1 nonDep2 : Bool) (info1 info2 : ExprInfo .anon), + SoundOptionalConversion.{u,v} resolve anchor entries source catalog + (.letE name ty1 v1 body1 nonDep1 info1) (.letE name ty2 v2 body2 nonDep2 info2) + ((RecM.tryDefEqWhnfLet name ty1 v1 body1 ty2 v2 body2).run (methodsN depth)) + /-- The constructed eta-expansion of a function typed by the exposed + dependent function type, compared against the lambda operand. -/ + compareEta : ∀ (t s : KExpr .anon) (name : Mode.anon.F Name) (bi : Mode.anon.F Lean.BinderInfo) + (ty : KExpr .anon) (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (before : TcState .anon) (T S A B : AExpr β) (condition : Certified.PropWhen), + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before → + readScopedExpr? resolve locals t = some T.erase → + (∃ type, TypingClaim.{u,v} entries context T type) → + readScopedExpr? resolve locals s = some S.erase → + readScopedExpr? resolve locals ty = some A.erase → + TypingClaim.{u,v} entries context S (.forallE condition A B) → + ConversionPost.{u,v} resolve anchor entries source catalog locals context bounds T S + ((RecM.compareEtaExpansion t s name bi ty).run (methodsN depth) before) + /-- Hereditary typing of an application. -/ + appHereditary : ∀ (context : Model.Context β) (fn arg type : AExpr β), + TypingClaim.{u,v} entries context (.app fn arg) type → + (∃ type, TypingClaim.{u,v} entries context fn type) ∧ + (∃ type, TypingClaim.{u,v} entries context arg type) + /-- The type of a typed term is a type. -/ + typeFormation : ∀ (context : Model.Context β) (term type : AExpr β), + TypingClaim.{u,v} entries context term type → + ∃ level, TypingClaim.{u,v} entries context type (.sort level) + /-- A positive is-prop entry at a key computed from the caller's registration + classifies the caller's reading of the keyed type. -/ + isPropTransport : ∀ (locals : List FVarId) (context : Model.Context β) (bounds : List VLevel) + (type : KExpr .anon) (ctxAddr : Address) (A : AExpr β), + IsPropKeyOrigin.{u,v} resolve anchor entries source catalog locals context bounds type ctxAddr → + readScopedExpr? resolve locals type = some A.erase → + (∃ type, TypingClaim.{u,v} entries context A type) → + (∃ (locals' : List FVarId) (context' : Model.Context β) (source' : KExpr .anon) (term : AExpr β), + source'.addr = type.addr ∧ readScopedExpr? resolve locals' source' = some term.erase ∧ + TypingClaim.{u,v} entries context' term (.sort .zero)) → + TypingClaim.{u,v} entries context A (.sort .zero) + +/-- Finite resources of the reducing tiers: the direct tiers' resources for +every operand, and per-lookup conversion data at every invariant state, each +an instance of `RunAssumptions`. -/ +structure DefEqReducingResources : Prop where + tiers : DefEqTierResources.{u,v} resolve anchor entries source catalog fun _ => True + lookup : ∀ (state : TcState .anon) (locals : List FVarId) (context : Model.Context β) + (bounds : List VLevel), + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state → + ∀ addr, StandaloneConversionData source addr state.env + +end Seams + +/-! ### Invariant preservation through the reducing tiers' bookkeeping -/ + +namespace CheckerInvariant + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +/-- The rejection-only same-head cache carries no claim. -/ +theorem ofDefEqFailure {state : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) + (key : Address × Address × Address) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {state with env := {state.env with defEqFailure := state.env.defEqFailure.insert key}} := + valid.ofMaps ⟨Nat.le_refl _, .refl _, rfl⟩ rfl rfl valid.coherent rfl rfl + (fun partition => by cases partition <;> rfl) (fun partition => by cases partition <;> rfl) + rfl rfl rfl + +/-- Recording a proposition classification preserves the invariant once a +positive answer records a proposition with the key's address. -/ +theorem ofIsPropInsert {state : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds state) + (key : Address × Address) (answer : Bool) + (justified : answer = true → + ∃ (locals' : List FVarId) (context' : Model.Context β) (source' : KExpr .anon) (term : AExpr β), + source'.addr = key.1 ∧ readScopedExpr? resolve locals' source' = some term.erase ∧ + TypingClaim.{u,v} entries context' term (.sort .zero)) : + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds + {state with env := {state.env with isPropCache := state.env.isPropCache.insert key answer}} where + sourceCache := ⟨valid.sourceState.ofMaps rfl rfl rfl valid.coherent, valid.cache.ofMaps rfl rfl rfl⟩ + synthesis := valid.synthesis.elim fun history => ⟨history.ofMaps rfl rfl⟩ + whnf := valid.whnf.elim fun history => ⟨history.ofMaps fun partition => by cases partition <;> rfl⟩ + structural := ⟨valid.structural.coherent, valid.structural.allocated, valid.structural.loader⟩ + reading := valid.reading + origin := valid.origin + semantics := ⟨valid.semantics.whnf.ofMaps (fun partition => by cases partition <;> rfl), + valid.semantics.defEq.ofMaps (fun partition => by cases partition <;> rfl), + valid.semantics.equivalence, valid.semantics.unfold.ofMap rfl, by + intro stored hit + change (state.env.isPropCache.insert key answer)[stored]? = some true at hit + rw [Std.HashMap.getElem?_insert] at hit + split at hit + · next same => + rw [← eq_of_beq same] + exact justified (Option.some.inj hit) + · exact valid.semantics.isProp stored hit⟩ + +/-- Lookup retains the invariant on both outcomes of the optional variant. -/ +theorem tryGetConst {before : TcState .anon} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (id : KId .anon) (data : StandaloneConversionData source id.addr before.env) : + KeepsInvariant.{u,v} resolve anchor entries source catalog locals context bounds + (TcM.tryGetConst id before) := by + have preserved := valid.getConst id data + unfold TcM.getConst at preserved + change (match (EStateM.bind (TcM.tryGetConst id) _ : TcM .anon (KConst .anon)) before with + | .ok _ after | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after) + at preserved + unfold EStateM.bind at preserved + cases run : TcM.tryGetConst id before <;> rw [run] at preserved + · rename_i optional after + cases optional <;> exact preserved + · exact preserved + +end CheckerInvariant + +/-! ### Reductions and probes -/ + +section Reductions + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} + +/-- The type of a typed term is typed. -/ +theorem DefEqReducingSeams.formed {depth : Nat} + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + {context : Model.Context β} {term type : AExpr β} + (typed : TypingClaim.{u,v} entries context term type) : + ∃ type', TypingClaim.{u,v} entries context type type' := + (seams.typeFormation context term type typed).elim fun _ formed => ⟨_, formed⟩ + +/-- The cheap recursion scope preserves reduction soundness: the depth is a +scalar outside every invariant map. -/ +theorem SoundReduction.withCheapRecursionDepth {input : KExpr .anon} + {action : RecM .anon (KExpr .anon)} {methods : Methods .anon} + (sound : SoundReduction.{u,v} resolve anchor entries source catalog input (action.run methods)) : + SoundReduction.{u,v} resolve anchor entries source catalog input + ((RecM.withCheapRecursionDepth action).run methods) := by + intro locals context bounds before term valid reads typed + rw [withCheapRecursionDepth_run] + have inner := sound locals context bounds {before with cheapRecursionDepth := before.cheapRecursionDepth + 1} + term (valid.ofDefEqScalars rfl rfl rfl rfl) reads typed + cases run : action.run methods {before with cheapRecursionDepth := before.cheapRecursionDepth + 1} <;> + rw [run] at inner + · exact ⟨inner.1.ofDefEqScalars rfl rfl rfl rfl, inner.2⟩ + · exact inner.ofDefEqScalars rfl rfl rfl rfl + +theorem whnfCoreForDefEq_sound {depth : Nat} + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) (term : KExpr .anon) : + SoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfCoreForDefEq term).run (methodsN depth)) := by + rw [whnfCoreForDefEq_def] + exact (seams.whnfCoreWithFlags term .DEF_EQ_CORE).withCheapRecursionDepth + +theorem whnfNoDeltaForDefEq_sound {depth : Nat} + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) (term : KExpr .anon) : + SoundReduction.{u,v} resolve anchor entries source catalog term + ((RecM.whnfNoDeltaForDefEq term).run (methodsN depth)) := by + rw [whnfNoDeltaForDefEq_def] + exact (seams.whnfNoDeltaImpl term .DEF_EQ_CORE .collapse).withCheapRecursionDepth + +/-- The caught inference-only edge under the smaller table's contract, at an +already typed annotated reading of the source. -/ +theorem tryInferOnly_sound {methods : Methods .anon} + (recursive : InferContract.{u,v} resolve anchor entries source catalog methods) + {locals : List FVarId} {context : Model.Context β} {bounds : List VLevel} {before : TcState .anon} + {term : KExpr .anon} {a T : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (reads : readScopedExpr? resolve locals term = some a.erase) + (typed : TypingClaim.{u,v} entries context a T) : + match (RecM.try? (RecM.inferOnlyCall term)).run methods before with + | .ok (some type) after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after ∧ + ∃ A : AExpr β, readScopedExpr? resolve locals type = some A.erase ∧ + TypingClaim.{u,v} entries context a A + | .ok none after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after + | .error _ after => + CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds after := by + rw [tryInferOnly_run] + have post := (recursive.infer term).only locals context bounds {before with inferOnly := true} a T + (valid.policy true) rfl reads typed + cases run : methods.infer term {before with inferOnly := true} <;> rw [run] at post + · exact ⟨post.1.policy _, post.2⟩ + · exact post.policy _ + +/-- A lookup-only classifier keeps the invariant. -/ +theorem KeepsInvariant.lookup {α : Type} {locals : List FVarId} {context : Model.Context β} + {bounds : List VLevel} {before : TcState .anon} {id : KId .anon} + {next : Option (KConst .anon) → TcM .anon α} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (data : StandaloneConversionData source id.addr before.env) + (pureNext : ∀ value state, ∃ result, next value state = .ok result state) : + KeepsInvariant.{u,v} resolve anchor entries source catalog locals context bounds + ((TcM.tryGetConst id >>= next) before) := by + have kept := valid.tryGetConst id data + cases run : TcM.tryGetConst id before <;> rw [run] at kept + · rw [EStateM.run_bind_ok run] + obtain ⟨result, next⟩ := pureNext _ _ + rw [next] + exact kept + · rw [EStateM.run_bind_error run] + exact kept + +/-- The bounded loop driver: an invariant on loop states that every step +respects yields the loop's outcome property, with exhaustion an error at an +invariant state. -/ +theorem runBoundedInvariant_sound {σ α : Type} {methods : Methods .anon} + {step : σ → RecM .anon (RecM.BoundedStep σ α)} + {Inv : σ → TcState .anon → Prop} + {Post : EStateM.Result (TcError .anon) (TcState .anon) α → Prop} + (stepSound : ∀ state before, Inv state before → + match (step state).run methods before with + | .ok (.next state') after => Inv state' after + | .ok (.done result) after => Post (.ok result after) + | .error err after => Post (.error err after)) + (exhausted : ∀ state before, Inv state before → Post (.error .maxRecDepth before)) : + ∀ (fuel : Nat) (state : σ) (before : TcState .anon), Inv state before → + Post ((RecM.runBounded step fuel state).run methods before) + | 0, state, before, invariant => exhausted state before invariant + | fuel + 1, state, before, invariant => by + rw [RecM.runBounded, ReaderT.run_bind] + have post := stepSound state before invariant + cases run : (step state).run methods before with + | error err after => + rw [EStateM.run_bind_error run] + rw [run] at post + exact post + | ok next after => + rw [EStateM.run_bind_ok run] + rw [run] at post + cases next with + | next state' => exact runBoundedInvariant_sound stepSound exhausted fuel state' after post + | done result => exact post + +end Reductions + +/-! ### Readings of the reduced shapes -/ + +section Readings + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {locals : List FVarId} + +private theorem bind_some {α γ : Type _} {action : Option α} {next : α → Option γ} {result : γ} + (run : action.bind next = some result) : + ∃ value, action = some value ∧ next value = some result := by + cases action with + | none => contradiction + | some value => exact ⟨value, rfl, run⟩ + +theorem readScopedExpr?_app_annotated {fn arg : KExpr .anon} {info : ExprInfo .anon} {a : AExpr β} + (reading : readScopedExpr? resolve locals (.app fn arg info) = some a.erase) : + ∃ f x : AExpr β, a = .app f x ∧ readScopedExpr? resolve locals fn = some f.erase ∧ + readScopedExpr? resolve locals arg = some x.erase := by + rw [readScopedExpr?] at reading + obtain ⟨f', fnReads, reading⟩ := bind_some reading + obtain ⟨x', argReads, reading⟩ := bind_some reading + have erased : a.erase = .app f' x' := (Option.some.inj reading).symm + cases a <;> simp only [AExpr.erase] at erased <;> cases erased + exact ⟨_, _, rfl, fnReads, argReads⟩ + +theorem readScopedExpr?_prj_annotated {id : KId .anon} {index : UInt64} {value : KExpr .anon} + {info : ExprInfo .anon} {a : AExpr β} + (reading : readScopedExpr? resolve locals (.prj id index value info) = some a.erase) : + ∃ (ref : ConstRef β) (v : AExpr β), resolve id.addr = some ref ∧ a = .proj ref index.toNat v ∧ + readScopedExpr? resolve locals value = some v.erase := by + rw [readScopedExpr?] at reading + obtain ⟨ref, resolved, reading⟩ := bind_some reading + obtain ⟨v', valueReads, reading⟩ := bind_some reading + have erased : a.erase = .proj ref index.toNat v' := (Option.some.inj reading).symm + cases a <;> simp only [AExpr.erase] at erased <;> cases erased + exact ⟨_, _, resolved, rfl, valueReads⟩ + +theorem readScopedExpr?_nat_annotated {value : Nat} {blob : Address} + {info : ExprInfo .anon} {a : AExpr β} + (reading : readScopedExpr? resolve locals (.nat value blob info) = some a.erase) : + a = .natLit value := by + have erased : a.erase = .natLit value := (Option.some.inj reading).symm + cases a <;> simp only [AExpr.erase] at erased <;> cases erased + rfl + +theorem readScopedExpr?_var_none (index : UInt64) (name : Mode.anon.F Name) (info : ExprInfo .anon) : + readScopedExpr? resolve locals (.var index name info) = none := by + simp [readScopedExpr?] + +theorem readScopedExpr?_str_none (value : String) (blob : Address) + (info : ExprInfo .anon) : + readScopedExpr? resolve locals (.str value blob info) = none := rfl + +/-- Neither operand of a readable pair is a compact string literal. -/ +theorem hasStringLiteralPair_eq_false {left right : KExpr .anon} {a b : VExpr β} + (leftReads : readScopedExpr? resolve locals left = some a) + (rightReads : readScopedExpr? resolve locals right = some b) : + RecM.hasStringLiteralPair left right = false := by + unfold RecM.hasStringLiteralPair + cases left <;> first + | (simp [readScopedExpr?] at leftReads; done) + | (cases right <;> first | rfl | (simp [readScopedExpr?] at rightReads; done)) + +end Readings + +/-! ### The hash path of a reduced pair -/ + +section HashPath + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} + +/-- Address equality of two readable, typed operands is conversion under +address faithfulness and the annotation discipline. -/ +theorem ConversionClaim.ofAddrEq {left right : KExpr .anon} {a b : AExpr β} + (faithful : left.AddrFaithful right) (annotations : SameRawAnnotations.{u,v} entries) + (equal : (left.addr == right.addr) = true) + (leftReads : readScopedExpr? resolve locals left = some a.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context a type) + (rightReads : readScopedExpr? resolve locals right = some b.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context b type) : + ConversionClaim.{u,v} entries context a b := by + have same := beq_readScopedExpr? (resolve := resolve) (locals := locals) (depth := 0) faithful + (by rw [KExpr.beq_def]; exact equal) + have erased : a.erase = b.erase := Option.some.inj (leftReads.symm.trans (same.trans rightReads)) + cases AExpr.eq_of_erase_annotations erased (annotations context a b erased leftTyped rightTyped) + exact ConversionClaim.refl a + +end HashPath + +/-! ### Annotated spines -/ + +section Spines + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {locals : List FVarId} {context : Model.Context β} + +/-- Application congruence along a spine of pairwise convertible arguments. -/ +theorem ConversionClaim.appN {f f' : AExpr β} : + ∀ {args args' : List (AExpr β)}, ConversionClaim.{u,v} entries context f f' → + args.length = args'.length → + (∀ pair ∈ args.zip args', ConversionClaim.{u,v} entries context pair.1 pair.2) → + ConversionClaim.{u,v} entries context (f.appN args) (f'.appN args') + | [], [], head, _, _ => head + | [], _ :: _, _, lengths, _ => absurd lengths (by simp) + | _ :: _, [], _, lengths, _ => absurd lengths (by simp) + | a :: as, a' :: as', head, lengths, pairs => + ConversionClaim.appN (f := .app f a) (f' := .app f' a') + (ConversionClaim.app head (pairs (a, a') (List.mem_cons_self ..))) + (by simpa using lengths) (fun pair member => pairs pair (List.mem_cons_of_mem _ member)) + +/-- Hereditary typing of a typed spine: the head and every argument are typed. -/ +theorem AExpr.appN_typed + (appHereditary : ∀ (context : Model.Context β) (fn arg type : AExpr β), + TypingClaim.{u,v} entries context (.app fn arg) type → + (∃ type, TypingClaim.{u,v} entries context fn type) ∧ + (∃ type, TypingClaim.{u,v} entries context arg type)) : + ∀ {f : AExpr β} {args : List (AExpr β)}, (∃ type, TypingClaim.{u,v} entries context (f.appN args) type) → + (∃ type, TypingClaim.{u,v} entries context f type) ∧ + ∀ a ∈ args, ∃ type, TypingClaim.{u,v} entries context a type + | _, [], typed => ⟨typed, fun _ member => nomatch member⟩ + | f, a :: as, typed => by + obtain ⟨⟨type, headTyped⟩, argsTyped⟩ := AExpr.appN_typed appHereditary (f := .app f a) (args := as) typed + obtain ⟨fTyped, aTyped⟩ := appHereditary context f a type headTyped + refine ⟨fTyped, fun x member => ?_⟩ + rcases List.mem_cons.mp member with rfl | member + · exact aTyped + · exact argsTyped x member + +/-- Typed spine readings of two argument lists of equal length. -/ +theorem SpineReadings.ofLists : + ∀ {rawLeft rawRight : List (KExpr .anon)} {left right : List (AExpr β)}, + rawLeft.map (readScopedExpr? resolve locals ·) = left.map (some ·.erase) → + rawRight.map (readScopedExpr? resolve locals ·) = right.map (some ·.erase) → + (∀ a ∈ left, ∃ type, TypingClaim.{u,v} entries context a type) → + (∀ b ∈ right, ∃ type, TypingClaim.{u,v} entries context b type) → + rawLeft.length = rawRight.length → + SpineReadings.{u,v} resolve entries locals context (rawLeft.zip rawRight) (left.zip right) + | [], [], left, right, leftReads, rightReads, _, _, _ => by + cases left <;> cases right <;> simp at leftReads rightReads + exact .nil + | [], _ :: _, _, _, _, _, _, _, lengths => absurd lengths (by simp) + | _ :: _, [], _, _, _, _, _, _, lengths => absurd lengths (by simp) + | l :: ls, r :: rs, left, right, leftReads, rightReads, leftTyped, rightTyped, lengths => by + cases left with + | nil => simp at leftReads + | cons a as => + cases right with + | nil => simp at rightReads + | cons b bs => + simp only [List.map_cons, List.cons.injEq] at leftReads rightReads + exact .cons leftReads.1 (leftTyped a (List.mem_cons_self ..)) rightReads.1 + (rightTyped b (List.mem_cons_self ..)) + (SpineReadings.ofLists leftReads.2 rightReads.2 + (fun x member => leftTyped x (List.mem_cons_of_mem _ member)) + (fun x member => rightTyped x (List.mem_cons_of_mem _ member)) (by simpa using lengths)) + +/-- The typed annotated spine parts of two readable operands whose raw spines +have equal size: both decompose as heads applied to argument lists, the raw +heads read the annotated heads, the heads are typed, and the zipped arguments +are typed spine readings. -/ +theorem spine_parts + (appHereditary : ∀ (context : Model.Context β) (fn arg type : AExpr β), + TypingClaim.{u,v} entries context (.app fn arg) type → + (∃ type, TypingClaim.{u,v} entries context fn type) ∧ + (∃ type, TypingClaim.{u,v} entries context arg type)) + {left right : KExpr .anon} {a b : AExpr β} + (leftReads : readScopedExpr? resolve locals left = some a.erase) + (leftTyped : ∃ type, TypingClaim.{u,v} entries context a type) + (rightReads : readScopedExpr? resolve locals right = some b.erase) + (rightTyped : ∃ type, TypingClaim.{u,v} entries context b type) + (sizes : left.collectSpine.2.size = right.collectSpine.2.size) : + a = (AppSpineSource.parts left a).1.appN (AppSpineSource.parts left a).2 ∧ + b = (AppSpineSource.parts right b).1.appN (AppSpineSource.parts right b).2 ∧ + readScopedExpr? resolve locals left.collectSpine.1 = some (AppSpineSource.parts left a).1.erase ∧ + readScopedExpr? resolve locals right.collectSpine.1 = some (AppSpineSource.parts right b).1.erase ∧ + (∃ type, TypingClaim.{u,v} entries context (AppSpineSource.parts left a).1 type) ∧ + (∃ type, TypingClaim.{u,v} entries context (AppSpineSource.parts right b).1 type) ∧ + (AppSpineSource.parts left a).2.length = (AppSpineSource.parts right b).2.length ∧ + SpineReadings.{u,v} resolve entries locals context + (left.collectSpine.2.toList.zip right.collectSpine.2.toList) + ((AppSpineSource.parts left a).2.zip (AppSpineSource.parts right b).2) := by + obtain ⟨aEq, aHeadReads, aArgsRead⟩ := AppSpineSource.reading leftReads + obtain ⟨bEq, bHeadReads, bArgsRead⟩ := AppSpineSource.reading rightReads + have aParts := AExpr.appN_typed appHereditary (aEq ▸ leftTyped) + have bParts := AExpr.appN_typed appHereditary (bEq ▸ rightTyped) + have aLength : left.collectSpine.2.toList.length = (AppSpineSource.parts left a).2.length := by + simpa using congrArg List.length aArgsRead + have bLength : right.collectSpine.2.toList.length = (AppSpineSource.parts right b).2.length := by + simpa using congrArg List.length bArgsRead + refine ⟨aEq, bEq, aHeadReads, bHeadReads, aParts.1, bParts.1, ?_, ?_⟩ + · rw [← aLength, ← bLength] + simpa using sizes + · exact SpineReadings.ofLists aArgsRead bArgsRead aParts.2 bParts.2 (by simpa using sizes) + +end Spines + +/-! ### Proof irrelevance -/ + +section ProofIrrelevance + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} {locals : List FVarId} + {context : Model.Context β} {bounds : List VLevel} + +/-- The uncached classifier: infer the type of the type, normalize it, and +accept a semantically zero sort. -/ +theorem classifyPropTypeUncached_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + {before : TcState .anon} {type : KExpr .anon} {A T : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (typeReads : readScopedExpr? resolve locals type = some A.erase) + (typed : TypingClaim.{u,v} entries context A T) : + PropositionPost.{u,v} resolve anchor entries source catalog locals context bounds A + ((RecM.classifyPropTypeUncached type).run (methodsN depth) before) := by + unfold RecM.classifyPropTypeUncached + simp only [ReaderT.run_bind] + have probe := tryInferOnly_sound (methods := methodsN depth) recursive.toInferContract valid typeReads typed + cases runI : (RecM.try? (RecM.inferOnlyCall type)).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runI] + rw [runI] at probe + exact probe + | ok sort? s₁ => + rw [EStateM.run_bind_ok runI] + rw [runI] at probe + cases sort? with + | none => + try dsimp only + rw [pure_run] + exact probe + | some sort => + obtain ⟨valid₁, S, sortReads, typedS⟩ := probe + obtain ⟨level, formedS⟩ := seams.typeFormation context A S typedS + try dsimp only + simp only [ReaderT.run_bind] + have reduced := seams.whnf sort locals context bounds s₁ S valid₁ sortReads ⟨_, formedS⟩ + cases runW : (RecM.whnf sort).run (methodsN depth) s₁ with + | error err s₂ => + have caught : (RecM.try? (RecM.whnf sort)).run (methodsN depth) s₁ = .ok none s₂ := by + rw [try?_run, runW] + rw [EStateM.run_bind_ok caught] + rw [runW] at reduced + try dsimp only + rw [pure_run] + exact reduced + | ok w s₂ => + have caught : (RecM.try? (RecM.whnf sort)).run (methodsN depth) s₁ = .ok (some w) s₂ := by + rw [try?_run, runW] + rw [EStateM.run_bind_ok caught] + rw [runW] at reduced + obtain ⟨valid₂, W, wReads, claimSW, _⟩ := reduced + cases w + case sort u info => + try dsimp only + rw [pure_run] + cases zero : u.isSemanticZero with + | false => exact valid₂ + | true => + have wEq := readScopedExpr?_sort_annotated wReads + subst wEq + obtain ⟨_, bound, _⟩ := resources.tiers.sorts u u info info trivial trivial + have zeroEq : ∀ values, (readLevel u).eval values = VLevel.zero.eval values := + fun values => by + rw [readLevel_eval] + exact Theory.VLevel.equiv_def.mp + (KUniv.toVLevel_equiv_zero_of_isSemanticZero bound zero) values + exact ⟨valid₂, TypingClaim.conv typedS (TypingClaim.sort .zero) + (claimSW.trans (ConversionClaim.sort zeroEq))⟩ + all_goals + try dsimp only + rw [pure_run] + exact valid₂ + +/-- The memoized classifier: a positive memo hit is transported to the +caller's registration, a miss classifies and records its answer. -/ +theorem isPropType_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + {before : TcState .anon} {type : KExpr .anon} {A T : AExpr β} + (valid : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds before) + (typeReads : readScopedExpr? resolve locals type = some A.erase) + (typed : TypingClaim.{u,v} entries context A T) : + PropositionPost.{u,v} resolve anchor entries source catalog locals context bounds A + ((RecM.isPropType type).run (methodsN depth) before) := by + unfold RecM.isPropType + simp only [ReaderT.run_bind, ReaderT.run_monadLift, monadLift_self] + obtain ⟨ctxAddr, s₁, keyRun⟩ := ctxAddrForLbr_ok type.lbr before + rw [EStateM.run_bind_ok keyRun] + have valid₁ : CheckerInvariant.{u,v} resolve anchor entries source catalog locals context bounds s₁ := by + rw [ctxAddrForLbr_state keyRun] + exact valid.ofCtxAddrCache _ + have origin : IsPropKeyOrigin.{u,v} resolve anchor entries source catalog locals context bounds + type ctxAddr := ⟨before, s₁, valid, keyRun⟩ + rw [EStateM.run_bind_ok (get_run (methodsN depth) s₁)] + try dsimp only + cases hit : s₁.env.isPropCache[(type.addr, ctxAddr)]? with + | some cached => + try dsimp only + rw [pure_run] + cases cached with + | false => exact valid₁ + | true => + exact ⟨valid₁, seams.isPropTransport locals context bounds type ctxAddr A origin typeReads + ⟨_, typed⟩ (valid₁.semantics.isProp _ hit)⟩ + | none => + try dsimp only + simp only [ReaderT.run_bind] + have classified := classifyPropTypeUncached_sound recursive seams resources valid₁ typeReads typed + cases runC : (RecM.classifyPropTypeUncached type).run (methodsN depth) s₁ with + | error err s₂ => + rw [EStateM.run_bind_error runC] + rw [runC] at classified + exact classified + | ok answer s₂ => + rw [EStateM.run_bind_ok runC] + rw [runC] at classified + try dsimp only + rw [EStateM.run_bind_ok (modify_run _ (methodsN depth) s₂)] + try dsimp only + rw [pure_run] + cases answer with + | false => exact classified.ofIsPropInsert (type.addr, ctxAddr) false (fun h => nomatch h) + | true => + exact ⟨classified.1.ofIsPropInsert (type.addr, ctxAddr) true + (fun _ => ⟨locals, context, type, A, rfl, typeReads, classified.2⟩), classified.2⟩ + +/-- Proof irrelevance: both operands typed by one proposition are convertible. -/ +theorem tryProofIrrel_sound {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.tryProofIrrel left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + obtain ⟨Ta, aTyped⟩ := leftTyped + obtain ⟨Tb, bTyped⟩ := rightTyped + unfold RecM.tryProofIrrel + simp only [ReaderT.run_bind] + have probeA := tryInferOnly_sound (methods := methodsN depth) recursive.toInferContract valid leftReads aTyped + cases runA : (RecM.try? (RecM.inferOnlyCall left)).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runA] + rw [runA] at probeA + exact probeA + | ok aType? s₁ => + rw [EStateM.run_bind_ok runA] + rw [runA] at probeA + cases aType? with + | none => + try dsimp only + rw [pure_run] + exact probeA + | some aType => + obtain ⟨valid₁, A, aTypeReads, typedA⟩ := probeA + obtain ⟨levelA, formedA⟩ := seams.typeFormation context a A typedA + try dsimp only + simp only [ReaderT.run_bind] + have prop := isPropType_sound recursive seams resources valid₁ aTypeReads formedA + cases runP : (RecM.isPropType aType).run (methodsN depth) s₁ with + | error err s₂ => + rw [EStateM.run_bind_error runP] + rw [runP] at prop + exact prop + | ok isProp s₂ => + rw [EStateM.run_bind_ok runP] + rw [runP] at prop + cases isProp with + | false => + simp only [Bool.not_false, ↓reduceIte] + rw [pure_run] + exact prop + | true => + obtain ⟨valid₂, propA⟩ := prop + simp only [Bool.not_true, Bool.false_eq_true, ↓reduceIte] + simp only [ReaderT.run_bind] + have probeB := tryInferOnly_sound (methods := methodsN depth) recursive.toInferContract + valid₂ rightReads bTyped + cases runB : (RecM.try? (RecM.inferOnlyCall right)).run (methodsN depth) s₂ with + | error err s₃ => + rw [EStateM.run_bind_error runB] + rw [runB] at probeB + exact probeB + | ok bType? s₃ => + rw [EStateM.run_bind_ok runB] + rw [runB] at probeB + cases bType? with + | none => + try dsimp only + rw [pure_run] + exact probeB + | some bType => + obtain ⟨valid₃, B, bTypeReads, typedB⟩ := probeB + try dsimp only + rw [isDefEqCall_run] + have conv := recursive.isDefEq aType bType locals context bounds s₃ A B valid₃ + aTypeReads ⟨_, propA⟩ bTypeReads (seams.formed typedB) + cases runD : (methodsN depth).isDefEq aType bType s₃ with + | error err s₄ => + rw [runD] at conv + exact conv + | ok answer s₄ => + rw [runD] at conv + cases answer with + | false => exact conv + | true => + obtain ⟨valid₄, claimAB⟩ := conv + exact ⟨valid₄, ConversionClaim.proofIrrel propA typedA + (TypingClaim.conv typedB propA claimAB.symm)⟩ + +end ProofIrrelevance + +/-! ### The upper chain: eager `Bool.true`, string expansion, and the cheap passes -/ + +section UpperChain + +variable {β : Type u} {resolve : Address → Option (ConstRef β)} + {anchor entries : Model.Environment β} {source : Ixon.Env} + {catalog : List (SourceCacheRequest source)} + +/-- A recognized `Bool.true` operand reads as the bound `Bool.true` reference +at no universes. -/ +theorem isBoolTrue_reading {locals : List FVarId} {prims : Primitives .anon} {term : KExpr .anon} + {a : AExpr β} + (recognized : isBoolTrueAnswer prims term = true) + (reads : readScopedExpr? resolve locals term = some a.erase) : + ∃ ref, resolve prims.boolTrue.addr = some ref ∧ a = .const ref [] := by + unfold isBoolTrueAnswer at recognized + cases term + case const id us info => + simp only [Bool.and_eq_true, beq_iff_eq] at recognized + obtain ⟨empty, addr⟩ := recognized + obtain ⟨ref, resolved, aEq⟩ := readScopedExpr?_const_annotated reads + have usNil : us = #[] := by simpa using empty + subst usNil + rw [← addr] + exact ⟨ref, resolved, aEq⟩ + all_goals exact absurd recognized Bool.false_ne_true + +/-- The eager `Bool.true` shortcut with the right operand recognized: the +left operand reduces to the same constant, or the remaining tiers decide. -/ +theorem isDefEqInnerAfterQuick_sound_of_tail {depth : Nat} + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (tail : ∀ left right, SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqInnerAfterBoolTrue left right).run (methodsN depth))) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqInnerAfterQuick left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + have symmetric : SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqInnerAfterFirstBoolGuardMiss left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.isDefEqInnerAfterFirstBoolGuardMiss + simp only [ReaderT.run_bind] + rw [EStateM.run_bind_ok (isBoolTrue_run left (methodsN depth) before)] + try dsimp only + rw [EStateM.run_bind_ok (boolTrueReductionAllowed_run right (methodsN depth) before)] + try dsimp only + by_cases guard : (isBoolTrueAnswer before.prims left && boolTrueAllowed before right) = true + · rw [if_pos guard] + simp only [Bool.and_eq_true] at guard + obtain ⟨leftTrue, _⟩ := guard + obtain ⟨ref, resolved, aEq⟩ := isBoolTrue_reading leftTrue leftReads + unfold RecM.whnfIsBoolTrue + simp only [ReaderT.run_bind, bind_assoc] + have reduced := seams.whnf right locals context bounds before b valid rightReads rightTyped + have prims := seams.whnfPrims right before + cases runW : (RecM.whnf right).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runW] + rw [runW] at reduced + exact reduced + | ok w s₁ => + rw [EStateM.run_bind_ok runW] + rw [runW] at reduced prims + obtain ⟨valid₁, W, wReads, claimBW, _⟩ := reduced + rw [EStateM.run_bind_ok (isBoolTrue_run w (methodsN depth) s₁)] + try dsimp only + by_cases wTrue : isBoolTrueAnswer s₁.prims w = true + · rw [if_pos wTrue, pure_run] + rw [prims] at wTrue + obtain ⟨ref', resolved', wEq⟩ := isBoolTrue_reading wTrue wReads + cases Option.some.inj (resolved.symm.trans resolved') + subst aEq wEq + exact ⟨valid₁, claimBW.symm⟩ + · rw [if_neg wTrue] + try simp only [ReaderT.run_bind] + try rw [EStateM.run_bind_ok (pure_run _ (methodsN depth) s₁)] + try dsimp only + exact tail left right locals context bounds s₁ a b valid₁ leftReads leftTyped rightReads + rightTyped + · rw [if_neg guard] + try simp only [ReaderT.run_bind] + try rw [EStateM.run_bind_ok (pure_run _ (methodsN depth) before)] + try dsimp only + exact tail left right locals context bounds before a b valid leftReads leftTyped rightReads + rightTyped + unfold RecM.isDefEqInnerAfterQuick + simp only [ReaderT.run_bind] + rw [EStateM.run_bind_ok (isBoolTrue_run right (methodsN depth) before)] + try dsimp only + rw [EStateM.run_bind_ok (boolTrueReductionAllowed_run left (methodsN depth) before)] + try dsimp only + by_cases guard : (isBoolTrueAnswer before.prims right && boolTrueAllowed before left) = true + · rw [if_pos guard] + simp only [Bool.and_eq_true] at guard + obtain ⟨rightTrue, _⟩ := guard + obtain ⟨ref, resolved, bEq⟩ := isBoolTrue_reading rightTrue rightReads + unfold RecM.whnfIsBoolTrue + simp only [ReaderT.run_bind, bind_assoc] + have reduced := seams.whnf left locals context bounds before a valid leftReads leftTyped + have prims := seams.whnfPrims left before + cases runW : (RecM.whnf left).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runW] + rw [runW] at reduced + exact reduced + | ok w s₁ => + rw [EStateM.run_bind_ok runW] + rw [runW] at reduced prims + obtain ⟨valid₁, W, wReads, claimAW, _⟩ := reduced + rw [EStateM.run_bind_ok (isBoolTrue_run w (methodsN depth) s₁)] + try dsimp only + by_cases wTrue : isBoolTrueAnswer s₁.prims w = true + · rw [if_pos wTrue, pure_run] + rw [prims] at wTrue + obtain ⟨ref', resolved', wEq⟩ := isBoolTrue_reading wTrue wReads + cases Option.some.inj (resolved.symm.trans resolved') + subst bEq wEq + exact ⟨valid₁, claimAW⟩ + · rw [if_neg wTrue] + try simp only [ReaderT.run_bind] + try rw [EStateM.run_bind_ok (pure_run _ (methodsN depth) s₁)] + try dsimp only + exact tail left right locals context bounds s₁ a b valid₁ leftReads leftTyped rightReads + rightTyped + · rw [if_neg guard] + exact symmetric locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + +/-- String-literal expansion is never reached on readable operands. -/ +theorem isDefEqInnerAfterBoolTrue_sound_of_tail {depth : Nat} + (tail : ∀ left right, SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqInnerAfterStringExpansion left right).run (methodsN depth))) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqInnerAfterBoolTrue left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.isDefEqInnerAfterBoolTrue + rw [hasStringLiteralPair_eq_false leftReads rightReads] + simp only [Bool.false_eq_true, ↓reduceIte] + try simp only [ReaderT.run_bind, pure_run] + try rw [EStateM.run_bind_ok (pure_run _ (methodsN depth) before)] + exact tail left right locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + +/-- The cheap structural-core pass: both operands are reduced, an address +match or the quick tier decides, otherwise the no-delta pass runs on the +original operands. -/ +theorem isDefEqInnerAfterStringExpansion_sound_of_tail {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (tail : ∀ left right, SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqInnerAfterCorePass left right).run (methodsN depth))) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqInnerAfterStringExpansion left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.isDefEqInnerAfterStringExpansion + simp only [ReaderT.run_bind] + have reducedA := whnfCoreForDefEq_sound seams left locals context bounds before a valid leftReads leftTyped + cases runA : (RecM.whnfCoreForDefEq left).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runA] + rw [runA] at reducedA + exact reducedA + | ok ca s₁ => + rw [EStateM.run_bind_ok runA] + rw [runA] at reducedA + obtain ⟨valid₁, Ca, caReads, claimA, typesA⟩ := reducedA + have caTyped : ∃ type, TypingClaim.{u,v} entries context Ca type := leftTyped.imp fun _ => typesA _ + have reducedB := whnfCoreForDefEq_sound seams right locals context bounds s₁ b valid₁ rightReads + rightTyped + cases runB : (RecM.whnfCoreForDefEq right).run (methodsN depth) s₁ with + | error err s₂ => + rw [EStateM.run_bind_error runB] + rw [runB] at reducedB + exact reducedB + | ok cb s₂ => + rw [EStateM.run_bind_ok runB] + rw [runB] at reducedB + obtain ⟨valid₂, Cb, cbReads, claimB, typesB⟩ := reducedB + have cbTyped : ∃ type, TypingClaim.{u,v} entries context Cb type := + rightTyped.imp fun _ => typesB _ + try dsimp only + split + · rename_i same + rw [pure_run] + exact ⟨valid₂, claimA.trans ((ConversionClaim.ofAddrEq (resources.tiers.faithful ca cb trivial + trivial) annotations same caReads caTyped cbReads cbTyped).trans claimB.symm)⟩ + · simp only [ReaderT.run_bind] + have quick := quickDefEq_sound recursive.toDefEqContract (MethodsLocalState.methodsN depth) + binders resources.tiers valid₂ trivial trivial caReads caTyped cbReads cbTyped + cases runQ : (RecM.quickDefEq ca cb).run (methodsN depth) s₂ with + | error err s₃ => + rw [EStateM.run_bind_error runQ] + rw [runQ] at quick + exact quick + | ok answer s₃ => + rw [EStateM.run_bind_ok runQ] + rw [runQ] at quick + cases answer with + | true => + simp only [↓reduceIte] + rw [pure_run] + exact ⟨quick.1, claimA.trans (quick.2.trans claimB.symm)⟩ + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + exact tail left right locals context bounds s₃ a b quick leftReads leftTyped + rightReads rightTyped + +/-- The cheap no-delta pass: both operands are reduced, an address match or +the quick tier decides, otherwise the remaining tiers run on the reduced +pair, whose readings are convertible to the originals. -/ +theorem isDefEqInnerAfterCorePass_sound_of_tail {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (annotations : SameRawAnnotations.{u,v} entries) + (binders : DefEqBinderAssumptions.{u,v} resolve anchor entries) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (tail : ∀ left right, SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqInnerAfterNoDeltaPass left right).run (methodsN depth))) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqInnerAfterCorePass left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.isDefEqInnerAfterCorePass + simp only [ReaderT.run_bind] + have reducedA := whnfNoDeltaForDefEq_sound seams left locals context bounds before a valid leftReads + leftTyped + cases runA : (RecM.whnfNoDeltaForDefEq left).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runA] + rw [runA] at reducedA + exact reducedA + | ok wa s₁ => + rw [EStateM.run_bind_ok runA] + rw [runA] at reducedA + obtain ⟨valid₁, Wa, waReads, claimA, typesA⟩ := reducedA + have waTyped : ∃ type, TypingClaim.{u,v} entries context Wa type := leftTyped.imp fun _ => typesA _ + have reducedB := whnfNoDeltaForDefEq_sound seams right locals context bounds s₁ b valid₁ rightReads + rightTyped + cases runB : (RecM.whnfNoDeltaForDefEq right).run (methodsN depth) s₁ with + | error err s₂ => + rw [EStateM.run_bind_error runB] + rw [runB] at reducedB + exact reducedB + | ok wb s₂ => + rw [EStateM.run_bind_ok runB] + rw [runB] at reducedB + obtain ⟨valid₂, Wb, wbReads, claimB, typesB⟩ := reducedB + have wbTyped : ∃ type, TypingClaim.{u,v} entries context Wb type := + rightTyped.imp fun _ => typesB _ + try dsimp only + split + · rename_i same + rw [pure_run] + exact ⟨valid₂, claimA.trans ((ConversionClaim.ofAddrEq (resources.tiers.faithful wa wb trivial + trivial) annotations same waReads waTyped wbReads wbTyped).trans claimB.symm)⟩ + · simp only [ReaderT.run_bind] + have quick := quickDefEq_sound recursive.toDefEqContract (MethodsLocalState.methodsN depth) + binders resources.tiers valid₂ trivial trivial waReads waTyped wbReads wbTyped + cases runQ : (RecM.quickDefEq wa wb).run (methodsN depth) s₂ with + | error err s₃ => + rw [EStateM.run_bind_error runQ] + rw [runQ] at quick + exact quick + | ok answer s₃ => + rw [EStateM.run_bind_ok runQ] + rw [runQ] at quick + cases answer with + | true => + simp only [↓reduceIte] + rw [pure_run] + exact ⟨quick.1, claimA.trans (quick.2.trans claimB.symm)⟩ + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + have rest := tail wa wb locals context bounds s₃ Wa Wb quick waReads waTyped wbReads + wbTyped + cases runT : (RecM.isDefEqInnerAfterNoDeltaPass wa wb).run (methodsN depth) s₃ with + | error err s₄ => + rw [runT] at rest + exact rest + | ok answer s₄ => + rw [runT] at rest + cases answer with + | false => exact rest + | true => exact ⟨rest.1, claimA.trans (rest.2.trans claimB.symm)⟩ + +/-- Proof irrelevance before delta, then the lazy-delta tail. -/ +theorem isDefEqInnerAfterNoDeltaPass_sound_of_tail {depth : Nat} + (recursive : MethodContracts.{u,v} resolve anchor entries source catalog (methodsN depth)) + (seams : DefEqReducingSeams.{u,v} resolve anchor entries source catalog depth) + (resources : DefEqReducingResources.{u,v} resolve anchor entries source catalog) + (tail : ∀ left right, SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqInnerAfterProofIrrelevance left right).run (methodsN depth))) + (left right : KExpr .anon) : + SoundConversion.{u,v} resolve anchor entries source catalog left right + ((RecM.isDefEqInnerAfterNoDeltaPass left right).run (methodsN depth)) := by + intro locals context bounds before a b valid leftReads leftTyped rightReads rightTyped + unfold RecM.isDefEqInnerAfterNoDeltaPass + simp only [ReaderT.run_bind] + have irrel := tryProofIrrel_sound recursive seams resources left right locals context bounds before a b + valid leftReads leftTyped rightReads rightTyped + cases runI : (RecM.tryProofIrrel left right).run (methodsN depth) before with + | error err s₁ => + rw [EStateM.run_bind_error runI] + rw [runI] at irrel + exact irrel + | ok answer s₁ => + rw [EStateM.run_bind_ok runI] + rw [runI] at irrel + cases answer with + | true => + simp only [↓reduceIte] + rw [pure_run] + exact irrel + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + exact tail left right locals context bounds s₁ a b irrel leftReads leftTyped rightReads rightTyped + +end UpperChain + +end Ix.Kernel.Consistency From ffbaa795143cda2a402e943ce7c141781566923c Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 15 Sep 2026 17:21:52 -0400 Subject: [PATCH 62/63] Admit singleton inductive blocks through the certified witness Consistency/Inductive/: the first WP5 slice, for one-family non-indexed non-mutual non-nested blocks with a separately stored canonical recursor. Witness construction (Shape.lean): the erased Ordinary shape is read from the stored family and constructor types (universe/parameter arities from the family header, parameter domains and result sort from the family type, ordinary and recursive field telescopes from each constructor type, the j-th recursive domain lowered past its earlier recursive binders); the elimination mode is the recursor/family universe-arity difference; binder conditions are annotations of the readings. The stored family block and recursor block are read to certified Blocks and compared by a decidable check with Shape.source and Shape.recursorSource (singletonWitnessCheck), including SupportsK for a K flag. Proved conditions (Block/Recursor/Formation/Admission/Run.lean): checkInductiveBlockImpl and checkRecursorBlockImpl/checkRecursorMemberImpl success are inverted into execution traces retaining every stored-type inference, validation, header agreement, A1-A4 run, K agreement, candidate selection and the exhaustive type and per-rule DefEq comparison (singleton blocks take the strict path). From the retained closed type checks: parameter/field/recursive-domain telescope formation (forallView iteration), transfer from the family-extended interface through a trivial realization of the fresh family, constructor and recursor formation, scope of every generated type and rule from validation of the stored declarations, rule left-hand-side scope and references from the stored right-hand side, and the syntactic large-elimination cases. The result is Ordinary.CheckedBlock, hence the published well-formed environment and the model extension (singleton_checkedBlock, singleton_published, singleton_admission). Seams (InductiveSeamAssumptions): field and recursive-domain universe bounds (A4 runs through the WHNF field loop), later recursive domains with nonempty telescopes (binder strengthening), large elimination from a singleton proposition with fields (inference-only field sorts), and rule typing (never checked by production; consumed only by small elimination). The generated-recursor reading of buildRecType/buildRuleRhs is not needed by the certified side, which decides canonicity on the stored recursor. Regression: Tests/Ix/Kernel/Inductive.lean (Nat-like block plus canonical recursor block) under tc-unit. --- Ix/Kernel/Verify/Consistency.lean | 13 + Ix/Kernel/Verify/Consistency/Audit.lean | 69 ++ .../Consistency/Inductive/Admission.lean | 346 +++++++++ .../Verify/Consistency/Inductive/Block.lean | 507 ++++++++++++++ .../Consistency/Inductive/Formation.lean | 467 +++++++++++++ .../Consistency/Inductive/Recursor.lean | 415 +++++++++++ .../Verify/Consistency/Inductive/Run.lean | 311 +++++++++ .../Verify/Consistency/Inductive/Shape.lean | 654 ++++++++++++++++++ Tests/Ix/Kernel/Inductive.lean | 114 +++ Tests/Main.lean | 2 + 10 files changed, 2898 insertions(+) create mode 100644 Ix/Kernel/Verify/Consistency/Inductive/Admission.lean create mode 100644 Ix/Kernel/Verify/Consistency/Inductive/Block.lean create mode 100644 Ix/Kernel/Verify/Consistency/Inductive/Formation.lean create mode 100644 Ix/Kernel/Verify/Consistency/Inductive/Recursor.lean create mode 100644 Ix/Kernel/Verify/Consistency/Inductive/Run.lean create mode 100644 Ix/Kernel/Verify/Consistency/Inductive/Shape.lean create mode 100644 Tests/Ix/Kernel/Inductive.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 8b25f0e9f..9a1fad88e 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -97,6 +97,12 @@ import Ix.Kernel.Verify.Consistency.WhnfSteps import Ix.Kernel.Verify.Consistency.DefEqReducing import Ix.Kernel.Verify.Consistency.DefEqFinal import Ix.Kernel.Verify.Consistency.DefEqLazyDelta +import Ix.Kernel.Verify.Consistency.Inductive.Shape +import Ix.Kernel.Verify.Consistency.Inductive.Block +import Ix.Kernel.Verify.Consistency.Inductive.Recursor +import Ix.Kernel.Verify.Consistency.Inductive.Formation +import Ix.Kernel.Verify.Consistency.Inductive.Admission +import Ix.Kernel.Verify.Consistency.Inductive.Run import Ix.Kernel.Verify.Consistency.Audit /-! @@ -395,4 +401,11 @@ the quick probe, from the eager `Bool.true` shortcut through the cheap passes, proof irrelevance, the lazy-delta loop, and the final WHNF tier, are proved modulo the reducer seams, which discharges that reducing-tail obligation. General checker soundness remains outside this fragment. +Singleton non-indexed inductive blocks are admitted through the certified +`Ordinary` witness: the stored family, constructors, and one-member recursor +block read to a certified shape by a decidable check, the retained closed type +checks of the block and recursor passes supply every formation fact, and the +remaining certified conditions (field universe bounds, later recursive +domains, singleton-proposition large elimination, and rule typing) are +collected in one seam record. -/ diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 03dc81374..0797d026d 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -24,6 +24,7 @@ import Ix.Kernel.Verify.Consistency.Contracts import Ix.Kernel.Verify.Consistency.DefEqTiers import Ix.Kernel.Verify.Consistency.WhnfSteps import Ix.Kernel.Verify.Consistency.DefEqLazyDelta +import Ix.Kernel.Verify.Consistency.Inductive.Run import Ix.Kernel.Verify.Audit.Basic /-! Exact full-dependency boundaries for the direct model-refinement roots. @@ -1684,6 +1685,61 @@ private def wp4ExpressionRoots : Array Lean.Name := #[ ``consumeBetaLamsFuel_size, ``consumeBetaLams_lam_nonempty, ``finishAppResult_run ] + +/-- Singleton inductive admission (WP5, first slice): pure syntax of the +erased shape, the witness readers, and the generated-rule syntax. -/ +private def inductiveAxiomFreeRoots : Array Lean.Name := #[``Inductive.context_append] + +private def inductivePropextRoots : Array Lean.Name := #[ + ``Inductive.erase_familyApp, ``Inductive.erase_shapeType, ``Inductive.peelForalls?_forallN, + ``Inductive.erase_annotateNever, ``Inductive.conditionsScopedCheck_sound, ``Inductive.modeOf_recUvars, + ``Inductive.recursorBlockOf?_recursor, ``Inductive.references_liftN, ``Inductive.referencesIn_insert_inv, + ``Inductive.motiveLevel_wf, ``Inductive.recursorBlock?_some +] + +private def inductiveQuotRoots : Array Lean.Name := #[ + ``Inductive.erase_recursiveFieldType, ``Inductive.erase_recursiveTypesFrom, ``Inductive.erase_constructorType, + ``Inductive.constructorShape?_type, ``Inductive.erasedShape?_sound, ``Inductive.eraseConstructor_annotate, + ``Inductive.eraseShape_annotate, ``Inductive.annotate_singleton, ``Inductive.recursorSourceCheck_sound, + ``Inductive.singletonWitnessCheck_sound, ``Inductive.readCtor?_source, ``Inductive.familyBlockOf?_family, + ``Inductive.familyBlock?_family, ``Inductive.mapM_some, ``Inductive.ctorMetadata_success, + ``Inductive.snapshot_success, ``Inductive.ContextRefs.push, ``Inductive.ContextRefs.context, + ``Inductive.scope_lamN_iff, ``Inductive.scope_forallN_iff, ``Inductive.scope_appN_iff, + ``Inductive.parameterVars_scope, ``Inductive.referencesIn_lamN_iff, ``Inductive.referencesIn_appN_iff, + ``Inductive.parameterVars_referencesIn, ``Inductive.length_ruleBinders, ``Inductive.ruleLhs_scope, + ``Inductive.ruleLhs_referencesIn, ``Inductive.referencesIn_forallN_iff, ``Inductive.forIn_yield_all, + ``Inductive.recursorSourceCheck_block +] + +/-- Semantic transfer through the trivial family realization, formation +splitting, the syntactic large-elimination cases, and validation scope. -/ +private def inductiveStandardRoots : Array Lean.Name := #[ + ``Inductive.formed_append_inv, ``Inductive.typing_of_insert, ``Inductive.formed_of_insert, + ``Inductive.family_realizable, ``Inductive.largeEvidence_of_syntax, + ``Inductive.validateConst_type_scoped, ``Inductive.validateConst_rules_scoped +] + +/-- Retained-check telescopes and the certified block theorem. The seam +record is a hypothesis of these roots; no seam is an axiom. -/ +private def inductiveExpressionRoots : Array Lean.Name := #[ + ``Inductive.classify_trace, ``Inductive.constructor_check_run, ``Inductive.constructors_trace, + ``Inductive.constructor_members_trace, ``Inductive.kTarget_success, ``Inductive.rules_trace, + ``Inductive.candidate_check_run, ``Inductive.CheckedTelescope.formed, ``Inductive.CheckedTelescope.get, + ``Inductive.telescopeChecks, ``Inductive.retypeCheck_context, ``Inductive.SingletonChecks.parametersFormed, + ``Inductive.SingletonChecks.constructorTelescopes, ``Inductive.singleton_checkedShape, + ``Inductive.singleton_checkedBlock, ``Inductive.singleton_published, + ``Inductive.MemberTypeRun.storedTypeCheck, ``Inductive.SingletonRunSupport.scope, + ``Inductive.SingletonRunSupport.checks, ``Inductive.singleton_admission +] + +/-- Traces through the whole inductive and recursor block checkers reach the +recursor generator's remaining generated proofs. -/ +private def inductiveProductionRoots : Array Lean.Name := #[ + ``Inductive.resolved_member_run, ``Inductive.member_impl_run, ``Inductive.members_trace, + ``Inductive.inductive_block_trace, ``Inductive.prepared_run, ``Inductive.recursor_member_trace, + ``Inductive.RecursorMemberTrace.strict, ``Inductive.recursor_block_trace, + ``Inductive.RecursorBlockTrace.rulesScoped +] private def wp4ProductionRoots : Array Lean.Name := #[ ``ReductionInvariant, ``ReductionInvariant.ofChecker, ``ReductionInvariant.sourceState, ``ReductionInvariant.inference, ``ReductionInvariant.coherent, ``ReductionInvariant.installed, @@ -2072,6 +2128,19 @@ def roots : Array RootAllowance := #[ ++ wp3ReducingRecognizerRoots.map (fun root => { root, standardAxioms := #[``propext], forbiddenDependencies := forbiddenProduction }) ++ wp3ReducingAxiomFreeRoots.map (fun root => { root, forbiddenDependencies := forbiddenProduction }) + ++ inductiveAxiomFreeRoots.map (fun root => { root, forbiddenDependencies := forbiddenProduction }) + ++ inductivePropextRoots.map (fun root => { + root, standardAxioms := #[``propext], forbiddenDependencies := forbiddenProduction }) + ++ inductiveQuotRoots.map (fun root => { + root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction }) + ++ inductiveStandardRoots.map (fun root => { + root, standardAxioms := standard, forbiddenDependencies := forbiddenProduction }) + ++ inductiveExpressionRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], + forbiddenDependencies := forbiddenProduction }) + ++ inductiveProductionRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := productionNative, + forbiddenDependencies := forbiddenProduction }) run_cmd Kernel.Verify.Audit.check roots diff --git a/Ix/Kernel/Verify/Consistency/Inductive/Admission.lean b/Ix/Kernel/Verify/Consistency/Inductive/Admission.lean new file mode 100644 index 000000000..1a25b8009 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Inductive/Admission.lean @@ -0,0 +1,346 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Inductive.Formation +import Ix.Theory.Certified.Ordinary.Checked + +/-! +# Admission of singleton inductive blocks through the certified witness + +The certified `Ordinary.CheckedBlock` proposition is exactly what +`Ordinary.checkBlock` establishes and what `checkOrdinaryExtension?` consumes. +Here it is derived for a singleton shape from: the decidable witness check on +the stored declarations, retained closed type checks of the stored family, +constructor, and recursor types (the inference calls production makes on +every stored member), validation-derived scope, interface freshness and +reference facts, and an explicit record of the remaining seams. The published +model extension then follows from the certified realization theorems. +-/ + +namespace Ix.Kernel.Consistency.Inductive + +open Theory Theory.Model Theory.Model.SetTheory Theory.Certified Theory.Certified.Ordinary + Theory.Inductive + +universe u v + +theorem referencesIn_forallN_iff {β : Type u} {entries : Model.Environment β} {p : PropWhen} : + ∀ {domains : List (AExpr β)} {body : AExpr β}, + (AExpr.forallN p domains body).ReferencesIn entries ↔ + (∀ A ∈ domains, A.ReferencesIn entries) ∧ body.ReferencesIn entries + | [], body => by simp [AExpr.forallN] + | A :: rest, body => by + simp only [AExpr.forallN, List.mem_cons, forall_eq_or_imp] + constructor + · intro h + have hA : A.ReferencesIn entries := fun r hr => h r (List.mem_append_left _ hr) + have ht : (AExpr.forallN p rest body).ReferencesIn entries := + fun r hr => h r (List.mem_append_right _ hr) + obtain ⟨hrest, hbody⟩ := referencesIn_forallN_iff.mp ht + exact ⟨⟨hA, hrest⟩, hbody⟩ + · rintro ⟨⟨hA, hrest⟩, hbody⟩ + intro r hr + rcases List.mem_append.mp hr with hr | hr + · exact hA r hr + · exact (referencesIn_forallN_iff.mpr ⟨hrest, hbody⟩) r hr + +/-! ### Seams -/ + +/-- Certified conditions of a singleton block not yet derived from the +production run. Each field records the sole reason it remains open. -/ +structure InductiveSeamAssumptions (entries : Model.Environment Address) (source recursor : Address) + (shape : Shape Address) (mode : ElimMode) : Prop where + /-- Production's A4 check (`checkFieldUniverses`) bounds every field sort + by the family level through a `whnf`-driven loop over the opened telescope; + identifying its iterations with the shape's fields needs the WHNF Pi-leaf + contract (WP4). -/ + fieldBounds : ∀ ctor ∈ shape.constructors, + TelescopeBound.{0,v} entries shape.parameterContext ctor.fields (some shape.level) + /-- The same A4 bound for the domains of a recursive field, whose sort is + only checked by production as part of the whole field type. -/ + recursiveBounds : ∀ ctor ∈ shape.constructors, ∀ field ∈ ctor.recursive, + TelescopeBound.{0,v} entries (ctor.context shape) field.domains (some shape.level) + /-- A recursive field after an earlier recursive field: the retained check + forms its domains under the earlier recursive binders, and removing those + binders semantically needs their inhabitation. -/ + laterRecursiveDomains : ∀ ctor ∈ shape.constructors, ∀ (j : Nat) (field : RecursiveField Address), + ctor.recursive[j]? = some field → 0 < j → field.domains ≠ [] → + Telescope.Formed.{0,v} entries (ctor.context shape) field.domains + /-- Large elimination from a proposition with one constructor and ordinary + fields: production decides it with inference-only field sorts + (`isLargeEliminator`), whose contract is WP2. -/ + largeElimination : mode = .large → zeroCondition shape.level ≠ .never → shape.constructors ≠ [] → + ¬ (∃ ctor, shape.constructors = [ctor] ∧ ctor.fields = []) → LargeEvidence.{0,v} entries shape + /-- Production never types recursor rules: it compares the stored right-hand + sides with the regenerated ones by definitional equality. The certified rule + typing is only consumed by small-elimination proof irrelevance. -/ + ruleTyping : ∀ (i : Nat) (ctor : Constructor Address), shape.constructors[i]? = some ctor → + (∃ l, TypingClaim.{0,v} (shape.recursorEnvironment entries source recursor mode) [] + (shape.ruleType source mode i ctor) (.sort l) ∧ (mode = .small → ∀ levels, l.eval levels = 0)) ∧ + TypingClaim.{0,v} (shape.recursorEnvironment entries source recursor mode) [] + (shape.ruleLhs source recursor mode i ctor) (shape.ruleType source mode i ctor) ∧ + TypingClaim.{0,v} (shape.recursorEnvironment entries source recursor mode) [] + (shape.ruleRhs source recursor mode i ctor) (shape.ruleType source mode i ctor) + +/-! ### Interface, scope, and retained checks -/ + +/-- Interface facts about the preceding model environment and the store view. -/ +structure SingletonInterface (resolve : Address → Option (ConstRef Address)) + (entries : Model.Environment Address) (store : Store Address) (source recursor : Address) + (family : KConst .anon) (ctors : List (KConst .anon)) (recr : KConst .anon) + (shape : Shape Address) (mode : ElimMode) : Prop where + wf : entries.WF + familyStored : store.blocks source = familyBlock? resolve family ctors + recursorStored : store.blocks recursor = recursorBlock? resolve recr + fresh : ∀ r ∈ shape.references source, entries r = none + recursorFresh : entries (.member recursor 0) = none + distinct : recursor ≠ source + familyRefs : shape.type.ReferencesIn entries + constructorRefs : ∀ ctor ∈ shape.constructors, + (ctor.type shape source).ReferencesIn (shape.familyEnvironment entries source) + recursorRefs : (shape.recursorType source mode).ReferencesIn (shape.constructorEnvironment entries source) + ruleRefs : ∀ (i : Nat) (ctor : Constructor Address), shape.constructors[i]? = some ctor → + (shape.ruleRhs source recursor mode i ctor).ReferencesIn + (shape.recursorEnvironment entries source recursor mode) + +/-- Scope of every stored type and rule, as established by production +validation of the stored declarations. -/ +structure SingletonScope (source recursor : Address) (shape : Shape Address) (mode : ElimMode) : Prop where + familyScope : shape.type.Scope shape.universes 0 + constructorScope : ∀ ctor ∈ shape.constructors, (ctor.type shape source).Scope shape.universes 0 + recursorScope : (shape.recursorType source mode).Scope (mode.recUvars shape.universes) 0 + ruleScope : ∀ (i : Nat) (ctor : Constructor Address), shape.constructors[i]? = some ctor → + (shape.ruleRhs source recursor mode i ctor).Scope (mode.recUvars shape.universes) 0 + +/-- The retained closed type checks of the stored family, constructor, and +recursor types, each over the interface production checks it against. -/ +structure SingletonChecks (resolve : Address → Option (ConstRef Address)) + (entries : Model.Environment Address) (source recursor : Address) (shape : Shape Address) + (mode : ElimMode) where + familyLevel : VLevel + familyCheck : SynthesisTypeCheck resolve entries shape.type familyLevel + constructorLevel : Nat → VLevel + constructorCheck : ∀ (i : Nat) (ctor : Constructor Address), shape.constructors[i]? = some ctor → + SynthesisTypeCheck resolve (shape.familyEnvironment entries source) (ctor.type shape source) + (constructorLevel i) + recursorLevel : VLevel + recursorCheck : SynthesisTypeCheck resolve (shape.constructorEnvironment entries source) + (shape.recursorType source mode) recursorLevel + +/-! ### Derived formation facts -/ + +section Derived + +variable {resolve : Address → Option (ConstRef Address)} {entries : Model.Environment Address} + {source recursor : Address} {shape : Shape Address} {mode : ElimMode} + +theorem SingletonChecks.parametersFormed (C : SingletonChecks resolve entries source recursor shape mode) : + Telescope.Formed.{0,v} entries [] shape.parameters := + (formed_append_inv ((telescopeChecks (p := .never) + (shape.parameters ++ shape.indices) (B := .sort shape.level) (closedScoped C.familyCheck)).1.formed + (ContextFormation.empty entries))).1 + +/-- The parameter, field, and recursive telescopes of one constructor, as +checked by its retained closed type check. -/ +theorem SingletonChecks.constructorTelescopes + (C : SingletonChecks resolve entries source recursor shape mode) {i : Nat} + {ctor : Constructor Address} (hc : shape.constructors[i]? = some ctor) : + Telescope.Formed.{0,v} (shape.familyEnvironment entries source) shape.parameterContext ctor.fields ∧ + ∀ (j : Nat) (field : RecursiveField Address), ctor.recursive[j]? = some field → j = 0 → + Telescope.Formed.{0,v} (shape.familyEnvironment entries source) (ctor.context shape) + field.domains := by + let check := closedScoped (C.constructorCheck i ctor hc) + let params := telescopeChecks (p := zeroCondition shape.level) shape.parameters + (B := AExpr.forallN (zeroCondition shape.level) ctor.fields + (AExpr.forallN (zeroCondition shape.level) (ctor.recursiveTypes shape source) + (shape.familyApp source (ctor.fields.length + ctor.recursive.length) + (ctor.indices.map (AExpr.liftN ctor.recursive.length ·))))) check + let fields := telescopeChecks (p := zeroCondition shape.level) ctor.fields params.2.val + have hfields := fields.1.formed (ContextFormation.empty _) + have hparamsCtx : params.2.val.context = shape.parameterContext := params.2.property + refine ⟨hparamsCtx ▸ hfields, ?_⟩ + intro j field hf hj + subst hj + let recursive := telescopeChecks (p := zeroCondition shape.level) + (ctor.recursiveTypes shape source) fields.2.val + have hfirst : (ctor.recursiveTypes shape source)[0]? = some ((field.type shape source ctor.fields.length).liftN 0) := by + simp [Constructor.recursiveTypes, Ordinary.recursiveTypesFrom, hf] + obtain ⟨found, hfound⟩ := recursive.1.get 0 _ hfirst + have hlift : (field.type shape source ctor.fields.length).liftN 0 = field.type shape source ctor.fields.length := + AExpr.liftN_zero _ _ + let found' := retypeCheck hlift found + have hfound' : found'.context = fields.2.val.context := by + rw [retypeCheck_context, hfound] + rfl + let domains := telescopeChecks (p := zeroCondition shape.level) field.domains + (B := shape.familyApp source (ctor.fields.length + field.domains.length) field.indices) found' + have hdomains := domains.1.formed (ContextFormation.empty _) + have hctx : found'.context = ctor.context shape := by + rw [hfound', fields.2.property, hparamsCtx] + rfl + exact hctx ▸ hdomains + +end Derived + +/-! ### The certified block -/ + +section Block + +variable {resolve : Address → Option (ConstRef Address)} {entries : Model.Environment Address} + {store : Store Address} {source recursor : Address} {family : KConst .anon} + {ctors : List (KConst .anon)} {recr : KConst .anon} {shape : Shape Address} {mode : ElimMode} + +theorem largeEvidence_of_syntax + (seams : InductiveSeamAssumptions.{v} entries source recursor shape mode) (hmode : mode = .large) : + LargeEvidence.{0,v} entries shape := by + by_cases hz : zeroCondition shape.level = .never + · left + intro levels he + have hc := zeroCondition_correct shape.level levels + simp only [hz, PropWhen.holds_never, he, beq_self_eq_true, Bool.false_eq_true] at hc + by_cases hempty : shape.constructors = [] + · exact Or.inr (Or.inl hempty) + by_cases hone : ∃ ctor, shape.constructors = [ctor] ∧ ctor.fields = [] + · obtain ⟨ctor, hsingle, hfields⟩ := hone + refine Or.inr (Or.inr ⟨ctor, hsingle, ?_⟩) + rw [hfields] + exact TelescopeProp.nil _ _ _ + exact seams.largeElimination hmode hz hempty hone + +theorem singleton_checkedShape + (check : singletonWitnessCheck resolve source recursor family ctors recr shape mode = true) + (I : SingletonInterface resolve entries store source recursor family ctors recr shape mode) + (S : SingletonScope source recursor shape mode) + (C : SingletonChecks resolve entries source recursor shape mode) + (seams : InductiveSeamAssumptions.{v} entries source recursor shape mode) : + CheckedShape.{0,v} entries store source shape := by + obtain ⟨hfam, _, hsingle, _, _, _, hmention, _, _⟩ := singletonWitnessCheck_sound check + have hparams : Telescope.Formed.{0,v} entries [] shape.parameters := C.parametersFormed + have hparamRefs : ∀ A ∈ shape.parameters, A.ReferencesIn entries := by + have := (referencesIn_forallN_iff.mp (show (AExpr.forallN .never (shape.parameters ++ shape.indices) + (.sort shape.level)).ReferencesIn entries from I.familyRefs)).1 + exact fun A hA => this A (List.mem_append_left _ hA) + have hfamilyFresh : entries (.member source 0) = none := I.fresh _ (List.mem_cons_self ..) + have realizable := family_realizable.{0,v} hsingle.1 S.familyScope I.familyRefs hfamilyFresh hparams + have hparamCtx : ContextRefs entries shape.parameterContext := + ContextRefs.context shape.parameters (ContextRefs.nil entries) hparamRefs + refine ⟨by rw [I.familyStored, hfam], I.fresh, ?_, S.familyScope, S.constructorScope, hparams, + by rw [hsingle.1]; exact .nil, ?_⟩ + · intro A hA + rcases List.mem_append.mp hA with hA | hA + · exact hparamRefs A hA + · rw [hsingle.1] at hA + simp at hA + · intro ctor hmem + obtain ⟨i, hc⟩ := List.mem_iff_getElem?.mp hmem + obtain ⟨hci, hrec⟩ := hsingle.2 ctor hmem + obtain ⟨hfieldsNo, hdomainsNo⟩ := hmention ctor hmem + have hrefs := I.constructorRefs ctor hmem + obtain ⟨_, hrefs⟩ := referencesIn_forallN_iff.mp + (show (AExpr.forallN (zeroCondition shape.level) shape.parameters _).ReferencesIn + (shape.familyEnvironment entries source) from hrefs) + obtain ⟨hfieldRefs, hrefs⟩ := referencesIn_forallN_iff.mp hrefs + obtain ⟨hrecRefs, _⟩ := referencesIn_forallN_iff.mp hrefs + have hfieldRefs' : ∀ A ∈ ctor.fields, A.ReferencesIn entries := fun A hA => + referencesIn_insert_inv (hfieldRefs A hA) (hfieldsNo A hA) + obtain ⟨hfieldsFormed, hrecursiveFormed⟩ := C.constructorTelescopes hc + refine ⟨?_, formed_of_insert I.wf hfamilyFresh realizable hfieldsFormed hparamCtx hfieldRefs', + seams.fieldBounds ctor hmem, by rw [hsingle.1, hci]; exact ArgumentsFit.nil _ _, ?_⟩ + · intro A hA + rcases List.mem_append.mp hA with hA | hA + · exact hfieldRefs' A hA + · rw [hci] at hA + simp at hA + · intro field hfield + obtain ⟨j, hj⟩ := List.mem_iff_getElem?.mp hfield + have hfi := hrec field hfield + have hdomainRefs : ∀ A ∈ field.domains, A.ReferencesIn entries := by + intro A hA + have hin : (field.type shape source ctor.fields.length).liftN j ∈ ctor.recursiveTypes shape source := by + apply List.mem_of_getElem? (i := j) + simp [Constructor.recursiveTypes, Ordinary.recursiveTypesFrom, hj] + have htype := hrecRefs _ hin + have htype' : (field.type shape source ctor.fields.length).ReferencesIn + (shape.familyEnvironment entries source) := by + intro r hr + exact htype r (by rw [references_liftN]; exact hr) + obtain ⟨hdomains, _⟩ := referencesIn_forallN_iff.mp + (show (AExpr.forallN (zeroCondition shape.level) field.domains _).ReferencesIn + (shape.familyEnvironment entries source) from htype') + exact referencesIn_insert_inv (hdomains A hA) (hdomainsNo field hfield A hA) + have hctorCtx : ContextRefs entries (ctor.context shape) := + ContextRefs.context ctor.fields hparamCtx hfieldRefs' + refine ⟨?_, ?_, seams.recursiveBounds ctor hmem field hfield, + by rw [hsingle.1, hfi]; exact ArgumentsFit.nil _ _⟩ + · intro A hA + rcases List.mem_append.mp hA with hA | hA + · exact hdomainRefs A hA + · rw [hfi] at hA + simp at hA + · by_cases hempty : field.domains = [] + · rw [hempty] + exact .nil + · cases j with + | zero => + exact formed_of_insert I.wf hfamilyFresh realizable + (hrecursiveFormed 0 field hj rfl) hctorCtx hdomainRefs + | succ j => + exact seams.laterRecursiveDomains ctor hmem (j + 1) field hj (Nat.succ_pos j) hempty + +/-- The certified block condition for a singleton family, its constructors, +and its stored canonical recursor. -/ +theorem singleton_checkedBlock + (check : singletonWitnessCheck resolve source recursor family ctors recr shape mode = true) + (I : SingletonInterface resolve entries store source recursor family ctors recr shape mode) + (S : SingletonScope source recursor shape mode) + (C : SingletonChecks resolve entries source recursor shape mode) + (seams : InductiveSeamAssumptions.{v} entries source recursor shape mode) : + CheckedBlock.{0,v} entries store source recursor shape mode := by + obtain ⟨_, hrec, hsingle, _, _, _, _, _, _⟩ := singletonWitnessCheck_sound check + refine ⟨singleton_checkedShape check I S C seams, ?_, ?_, ?_, ?_⟩ + · intro ctor hmem + obtain ⟨i, hc⟩ := List.mem_iff_getElem?.mp hmem + refine ⟨⟨S.constructorScope ctor hmem, by simp [Shape.constructorEntry], I.constructorRefs ctor hmem, + by simp [Shape.constructorEntry], by simp [Shape.constructorEntry], + by simp [Shape.constructorEntry], by simp [Shape.constructorEntry], + by simp [Shape.constructorEntry]⟩, C.constructorLevel i, (C.constructorCheck i ctor hc).sound⟩ + · refine ⟨recursorSourceCheck_sound hrec I.recursorStored, ?_, + ⟨S.recursorScope, by simp [Shape.recursorEntry], I.recursorRefs, by simp [Shape.recursorEntry], + by simp [Shape.recursorEntry], by simp [Shape.recursorEntry], by simp [Shape.recursorEntry], + by simp [Shape.recursorEntry]⟩, C.recursorLevel, C.recursorCheck.sound⟩ + simp [Shape.constructorEnvironment, Environment.overlay, Shape.constructorEntries, + Shape.familyEnvironment, Environment.insert, I.distinct, I.recursorFresh] + · cases mode with + | small => trivial + | large => exact largeEvidence_of_syntax seams rfl + · intro i ctor hc + have hci : ctor.indices = [] := (hsingle.2 ctor (List.mem_of_getElem? hc)).1 + obtain ⟨htype, hlhs, hrhs⟩ := seams.ruleTyping i ctor hc + exact ⟨⟨ruleLhs_scope hci (S.ruleScope i ctor hc), S.ruleScope i ctor hc⟩, + ⟨ruleLhs_referencesIn hci hc (I.ruleRefs i ctor hc), I.ruleRefs i ctor hc⟩, htype, hlhs, hrhs⟩ + +/-- The published environment is well formed, and every model of the +preceding interface extends to a model of the published one that keeps all +earlier interpretations. -/ +theorem singleton_published + (check : singletonWitnessCheck resolve source recursor family ctors recr shape mode = true) + (I : SingletonInterface resolve entries store source recursor family ctors recr shape mode) + (S : SingletonScope source recursor shape mode) + (C : SingletonChecks resolve entries source recursor shape mode) + (seams : InductiveSeamAssumptions.{v} entries source recursor shape mode) : + (shape.publishedEnvironment entries source recursor mode).WF ∧ + ∀ (V : Type v) [SetTheory V] (constants : Assignment Address V), Realizes constants entries → + Realizes (shape.recursorAssignment constants source recursor mode) + (shape.publishedEnvironment entries source recursor mode) ∧ + Assignment.AgreesOn entries constants (shape.recursorAssignment constants source recursor mode) := by + have checked := singleton_checkedBlock check I S C seams + refine ⟨Shape.publishedEnvironment_wf checked I.wf, ?_⟩ + intro V _ constants hM + exact ⟨Shape.publishedAssignment_realizes checked I.wf constants hM, + Shape.publishedAssignment_agrees checked constants⟩ + +end Block + +end Ix.Kernel.Consistency.Inductive diff --git a/Ix/Kernel/Verify/Consistency/Inductive/Block.lean b/Ix/Kernel/Verify/Consistency/Inductive/Block.lean new file mode 100644 index 000000000..1181c7bfc --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Inductive/Block.lean @@ -0,0 +1,507 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Inductive.Shape +import Ix.Kernel.Check + +/-! +# Production inductive block checks as execution traces + +`checkInductiveBlockImpl` first classifies every stored member, inferring the +untouched stored type of each family and constructor, then validates every +family header with its constructors, then re-validates each constructor +against its parent. The traces below retain exactly the production calls made +on the success path, in order, with the concrete declarations returned by +lookup. Later modules consume the retained type-inference runs; the +inductive-specific checks are recorded so their consequences can be extracted +without re-executing the checker. +-/ + +namespace Ix.Kernel.Consistency.Inductive + +open Theory + +private theorem bind_success {α β : Type} {x : TcM .anon α} {k : α → TcM .anon β} + {before after : TcState .anon} {value : β} + (accepted : EStateM.bind x k before = .ok value after) : + ∃ intermediate state, x before = .ok intermediate state ∧ + k intermediate state = .ok value after := by + rw [EStateM.bind] at accepted + cases run : x before with + | error err state => rw [run] at accepted; contradiction + | ok intermediate state => + rw [run] at accepted + exact ⟨intermediate, state, rfl, accepted⟩ + +/-! ### Member classification -/ + +/-- One member of the classification pass: reset, lookup, validation, type +inference of the untouched stored type, and its sort check. -/ +structure MemberTypeRun (member : KId .anon) (methods : Methods .anon) (before : TcState .anon) where + concrete : KConst .anon + reset : TcState .anon + loaded : TcState .anon + validated : TcState .anon + inferred : KExpr .anon + typeState : TcState .anon + level : KUniv .anon + after : TcState .anon + resetRun : TcM.reset before = .ok () reset + getRun : TcM.getConst member reset = .ok concrete loaded + validationRun : (RecM.validateConstWellScoped concrete).run methods loaded = .ok () validated + typeRun : (RecM.infer concrete.ty).run methods validated = .ok inferred typeState + sortRun : (RecM.ensureSortDirect inferred).run methods typeState = .ok level after + +/-- Whether a loaded declaration is a family. -/ +def isInductiveDecl : KConst .anon → Bool + | .indc .. => true + | _ => false + +/-- Whether a loaded declaration is a constructor. -/ +def isConstructorDecl : KConst .anon → Bool + | .ctor .. => true + | _ => false + +/-- The classification pass over the remaining members with the accumulated +family and constructor identifiers. -/ +inductive ClassifyTrace (block : KId .anon) (methods : Methods .anon) : + List (KId .anon) → Array (KId .anon) → Array (KId .anon) → TcState .anon → + Array (KId .anon) × Array (KId .anon) → TcState .anon → Type + | nil (inds ctors : Array (KId .anon)) (state : TcState .anon) : + ClassifyTrace block methods [] inds ctors state (inds, ctors) state + | indc {member : KId .anon} {members : List (KId .anon)} {inds ctors : Array (KId .anon)} + {before : TcState .anon} {result : Array (KId .anon) × Array (KId .anon)} {after : TcState .anon} + (run : MemberTypeRun member methods before) (kind : isInductiveDecl run.concrete = true) + (tail : ClassifyTrace block methods members (inds.push member) ctors run.after result after) : + ClassifyTrace block methods (member :: members) inds ctors before result after + | ctor {member : KId .anon} {members : List (KId .anon)} {inds ctors : Array (KId .anon)} + {before : TcState .anon} {result : Array (KId .anon) × Array (KId .anon)} {after : TcState .anon} + (run : MemberTypeRun member methods before) (kind : isConstructorDecl run.concrete = true) + (tail : ClassifyTrace block methods members inds (ctors.push member) run.after result after) : + ClassifyTrace block methods (member :: members) inds ctors before result after + +theorem classify_trace {block : KId .anon} {methods : Methods .anon} : + ∀ {members : List (KId .anon)} {inds ctors : Array (KId .anon)} {before : TcState .anon} + {result : Array (KId .anon) × Array (KId .anon)} {after : TcState .anon}, + (RecM.classifyInductiveBlockMembers block members inds ctors).run methods before = + .ok result after → + Nonempty (ClassifyTrace block methods members inds ctors before result after) + | [], inds, ctors, before, result, after, h => by + simp only [RecM.classifyInductiveBlockMembers, ReaderT.run_pure] at h + change EStateM.Result.ok (inds, ctors) before = _ at h + cases h + exact ⟨.nil inds ctors before⟩ + | member :: members, inds, ctors, before, result, after, h => by + unfold RecM.classifyInductiveBlockMembers at h + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at h + change EStateM.bind TcM.reset _ before = _ at h + obtain ⟨⟨⟩, reset, resetRun, h⟩ := bind_success h + change EStateM.bind (TcM.getConst member) _ reset = _ at h + obtain ⟨concrete, loaded, getRun, h⟩ := bind_success h + change EStateM.bind ((RecM.validateConstWellScoped concrete).run methods) _ loaded = _ at h + obtain ⟨⟨⟩, validated, validationRun, h⟩ := bind_success h + revert h + cases concrete + case indc name levelParams lvls params indices isUnsafe indBlock memberIdx ty ctorIds leanAll => + intro h + simp only [ReaderT.run_bind] at h + change EStateM.bind ((RecM.infer ty).run methods) _ validated = _ at h + obtain ⟨inferred, typeState, typeRun, h⟩ := bind_success h + change EStateM.bind ((RecM.ensureSortDirect inferred).run methods) _ typeState = _ at h + obtain ⟨level, sortState, sortRun, h⟩ := bind_success h + obtain ⟨tail⟩ := classify_trace h + exact ⟨.indc ⟨_, reset, loaded, validated, inferred, typeState, level, sortState, resetRun, + getRun, validationRun, typeRun, sortRun⟩ rfl tail⟩ + case ctor name levelParams isUnsafe lvls induct cidx params fields ty => + intro h + simp only [ReaderT.run_bind] at h + change EStateM.bind ((RecM.infer ty).run methods) _ validated = _ at h + obtain ⟨inferred, typeState, typeRun, h⟩ := bind_success h + change EStateM.bind ((RecM.ensureSortDirect inferred).run methods) _ typeState = _ at h + obtain ⟨level, sortState, sortRun, h⟩ := bind_success h + obtain ⟨tail⟩ := classify_trace h + exact ⟨.ctor ⟨_, reset, loaded, validated, inferred, typeState, level, sortState, resetRun, + getRun, validationRun, typeRun, sortRun⟩ rfl tail⟩ + all_goals + intro h + change EStateM.Result.error _ validated = _ at h + cases h + +/-! ### Constructor validation -/ + +/-- The successful header agreement of a stored constructor with its parent, +as returned by production lookup. -/ +theorem ctorMetadata_success {ctorId inductId : KId .anon} {expectedCidx indParams : Nat} + {indLvls : UInt64} {indIsUnsafe : Bool} {methods : Methods .anon} {before after : TcState .anon} + {ctorTy : KExpr .anon} {ctorFields : Nat} + (h : (RecM.checkCtorMetadataAgainstParent ctorId inductId expectedCidx indParams indLvls + indIsUnsafe).run methods before = .ok (ctorTy, ctorFields) after) : + ∃ name levelParams isUnsafe lvls induct cidx params fields, + TcM.getConst ctorId before = + .ok (KConst.ctor name levelParams isUnsafe lvls induct cidx params fields ctorTy) after ∧ + (induct != inductId) = false ∧ lvls = indLvls ∧ isUnsafe = indIsUnsafe ∧ + params.toNat = indParams ∧ cidx.toNat = expectedCidx ∧ ctorFields = fields.toNat := by + unfold RecM.checkCtorMetadataAgainstParent at h + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at h + change EStateM.bind (TcM.getConst ctorId) _ before = _ at h + obtain ⟨concrete, loaded, getRun, h⟩ := bind_success h + revert h + cases concrete + case ctor name levelParams isUnsafe lvls induct cidx params fields ty => + intro h + simp only [ReaderT.run_bind] at h + change EStateM.bind (pure _) _ loaded = _ at h + obtain ⟨_, _, hpure, h⟩ := bind_success h + change EStateM.Result.ok _ loaded = _ at hpure + cases hpure + by_cases hinduct : (induct != inductId) = true + · simp only [hinduct, if_true] at h + change EStateM.Result.error _ loaded = _ at h + cases h + · simp only [hinduct, Bool.false_eq_true, if_false] at h + by_cases hlvls : (lvls != indLvls) = true + · simp only [hlvls, if_true] at h + change EStateM.Result.error _ loaded = _ at h + cases h + · simp only [hlvls, Bool.false_eq_true, if_false] at h + by_cases hunsafe : (isUnsafe != indIsUnsafe) = true + · simp only [hunsafe, if_true] at h + change EStateM.Result.error _ loaded = _ at h + cases h + · simp only [hunsafe, Bool.false_eq_true, if_false] at h + by_cases hparams : (params.toNat != indParams) = true + · simp only [hparams, if_true] at h + change EStateM.Result.error _ loaded = _ at h + cases h + · simp only [hparams, Bool.false_eq_true, if_false] at h + by_cases hcidx : (cidx.toNat != expectedCidx) = true + · simp only [hcidx, if_true] at h + change EStateM.Result.error _ loaded = _ at h + cases h + · simp only [hcidx, Bool.false_eq_true, if_false] at h + change EStateM.Result.ok (ty, fields.toNat) loaded = _ at h + cases h + refine ⟨name, levelParams, isUnsafe, lvls, induct, cidx, params, fields, getRun, + Bool.eq_false_iff.mpr hinduct, ?_, ?_, ?_, ?_, rfl⟩ + · simpa using Bool.eq_false_iff.mpr hlvls + · simpa using Bool.eq_false_iff.mpr hunsafe + · simpa using Bool.eq_false_iff.mpr hparams + · simpa using Bool.eq_false_iff.mpr hcidx + all_goals + intro h + simp only [ReaderT.run_bind] at h + change EStateM.Result.error _ loaded = _ at h + cases h + +/-- The A1–A4 runs of one constructor against its resolved parent. -/ +structure ConstructorCheckRun (ctorId inductId : KId .anon) (expectedCidx indParams indIndices : Nat) + (indLvls : UInt64) (indIsUnsafe : Bool) (indTy : KExpr .anon) (indLevel : KUniv .anon) + (blockAddrs : Array Address) (methods : Methods .anon) (before : TcState .anon) where + ctorTy : KExpr .anon + ctorFields : Nat + headerState : TcState .anon + headerRun : (RecM.checkCtorMetadataAgainstParent ctorId inductId expectedCidx indParams indLvls + indIsUnsafe).run methods before = .ok (ctorTy, ctorFields) headerState + agreementState : TcState .anon + agreementRun : (RecM.checkParamAgreement indTy ctorTy indParams).run methods headerState = + .ok () agreementState + positivityState : TcState .anon + positivityRun : indIsUnsafe = false → + (RecM.checkPositivity ctorTy indParams blockAddrs).run methods agreementState = .ok () positivityState + positivitySkipped : indIsUnsafe = true → positivityState = agreementState + universesState : TcState .anon + universesRun : (RecM.checkFieldUniverses ctorTy indParams indLevel).run methods positivityState = + .ok () universesState + after : TcState .anon + returnRun : (RecM.checkCtorReturnType ctorTy indParams indIndices ctorFields inductId.addr indLvls + blockAddrs).run methods universesState = .ok () after + +theorem constructor_check_run {ctorId inductId : KId .anon} {expectedCidx indParams indIndices : Nat} + {indLvls : UInt64} {indIsUnsafe : Bool} {indTy : KExpr .anon} {indLevel : KUniv .anon} + {blockAddrs : Array Address} {methods : Methods .anon} {before after : TcState .anon} + (h : (RecM.checkInductiveConstructor ctorId inductId expectedCidx indParams indIndices indLvls + indIsUnsafe indTy indLevel blockAddrs).run methods before = .ok () after) : + ∃ run : ConstructorCheckRun ctorId inductId expectedCidx indParams indIndices indLvls indIsUnsafe + indTy indLevel blockAddrs methods before, run.after = after := by + unfold RecM.checkInductiveConstructor at h + simp only [ReaderT.run_bind] at h + change EStateM.bind ((RecM.checkCtorMetadataAgainstParent ctorId inductId expectedCidx indParams + indLvls indIsUnsafe).run methods) _ before = _ at h + obtain ⟨⟨ctorTy, ctorFields⟩, headerState, headerRun, h⟩ := bind_success h + change EStateM.bind ((RecM.checkParamAgreement indTy ctorTy indParams).run methods) _ headerState = + _ at h + obtain ⟨⟨⟩, agreementState, agreementRun, h⟩ := bind_success h + cases indIsUnsafe with + | false => + simp only [Bool.not_false, if_true] at h + change EStateM.bind ((RecM.checkPositivity ctorTy indParams blockAddrs).run methods) _ + agreementState = _ at h + obtain ⟨⟨⟩, positivityState, positivityRun, h⟩ := bind_success h + change EStateM.bind ((RecM.checkFieldUniverses ctorTy indParams indLevel).run methods) _ + positivityState = _ at h + obtain ⟨⟨⟩, universesState, universesRun, h⟩ := bind_success h + exact ⟨⟨ctorTy, ctorFields, headerState, headerRun, agreementState, agreementRun, + positivityState, (fun _ => positivityRun), (fun contra => by cases contra), universesState, + universesRun, after, h⟩, rfl⟩ + | true => + simp only [Bool.not_true, Bool.false_eq_true, if_false, ReaderT.run_bind] at h + change EStateM.bind ((RecM.checkFieldUniverses ctorTy indParams indLevel).run methods) _ + agreementState = _ at h + obtain ⟨⟨⟩, universesState, universesRun, h⟩ := bind_success h + exact ⟨⟨ctorTy, ctorFields, headerState, headerRun, agreementState, agreementRun, + agreementState, (fun contra => by cases contra), (fun _ => rfl), universesState, + universesRun, after, h⟩, rfl⟩ + +/-- The source-ordered constructor traversal of one resolved family header. -/ +inductive ConstructorsTrace (inductId : KId .anon) (indParams indIndices : Nat) (indLvls : UInt64) + (indIsUnsafe : Bool) (indTy : KExpr .anon) (indLevel : KUniv .anon) (blockAddrs : Array Address) + (methods : Methods .anon) : List (KId .anon) → Nat → TcState .anon → TcState .anon → Type + | nil (expectedCidx : Nat) (state : TcState .anon) : + ConstructorsTrace inductId indParams indIndices indLvls indIsUnsafe indTy indLevel blockAddrs + methods [] expectedCidx state state + | cons {ctorId : KId .anon} {ctorIds : List (KId .anon)} {expectedCidx : Nat} + {before after : TcState .anon} + (run : ConstructorCheckRun ctorId inductId expectedCidx indParams indIndices indLvls indIsUnsafe + indTy indLevel blockAddrs methods before) + (tail : ConstructorsTrace inductId indParams indIndices indLvls indIsUnsafe indTy indLevel + blockAddrs methods ctorIds (expectedCidx + 1) run.after after) : + ConstructorsTrace inductId indParams indIndices indLvls indIsUnsafe indTy indLevel blockAddrs + methods (ctorId :: ctorIds) expectedCidx before after + +theorem constructors_trace {inductId : KId .anon} {indParams indIndices : Nat} {indLvls : UInt64} + {indIsUnsafe : Bool} {indTy : KExpr .anon} {indLevel : KUniv .anon} {blockAddrs : Array Address} + {methods : Methods .anon} : + ∀ {ctorIds : List (KId .anon)} {expectedCidx : Nat} {before after : TcState .anon}, + (RecM.checkInductiveConstructors inductId indParams indIndices indLvls indIsUnsafe indTy indLevel + blockAddrs ctorIds expectedCidx).run methods before = .ok () after → + Nonempty (ConstructorsTrace inductId indParams indIndices indLvls indIsUnsafe indTy indLevel + blockAddrs methods ctorIds expectedCidx before after) + | [], expectedCidx, before, after, h => by + simp only [RecM.checkInductiveConstructors, ReaderT.run_pure] at h + change EStateM.Result.ok () before = _ at h + cases h + exact ⟨.nil expectedCidx before⟩ + | ctorId :: ctorIds, expectedCidx, before, after, h => by + unfold RecM.checkInductiveConstructors at h + simp only [ReaderT.run_bind] at h + change EStateM.bind ((RecM.checkInductiveConstructor ctorId inductId expectedCidx indParams + indIndices indLvls indIsUnsafe indTy indLevel blockAddrs).run methods) _ before = _ at h + obtain ⟨⟨⟩, middle, headRun, h⟩ := bind_success h + obtain ⟨run, rfl⟩ := constructor_check_run headRun + obtain ⟨tail⟩ := constructors_trace h + exact ⟨.cons run tail⟩ + +/-! ### Family header validation -/ + +/-- The execution of one resolved family header check: block discovery, the +result sort, peer agreement, the constructor traversal, and recursor +generation. -/ +structure ResolvedMemberRun (id : KId .anon) (params indices lvls : UInt64) (ctors : Array (KId .anon)) + (block : KId .anon) (isUnsafe : Bool) (ty : KExpr .anon) (methods : Methods .anon) + (before : TcState .anon) where + blockInds : Array (KId .anon) + discovered : TcState .anon + discoverRun : (RecM.discoverBlockInductives block).run methods before = .ok blockInds discovered + arity : UInt64 + arityState : TcState .anon + arityRun : (RecM.checkedMetadataSum "inductive params + indices" #[params, indices]).run methods + discovered = .ok arity arityState + level : KUniv .anon + levelState : TcState .anon + levelRun : (RecM.getResultSortLevel ty arity.toNat).run methods arityState = .ok level levelState + peerState : TcState .anon + peerRun : (RecM.checkInductivePeerAgreement id block params lvls isUnsafe ty level blockInds).run + methods levelState = .ok () peerState + ctorState : TcState .anon + constructors : ConstructorsTrace id params.toNat indices.toNat lvls isUnsafe ty level + (blockInds.map (·.addr)) methods ctors.toList 0 peerState ctorState + after : TcState .anon + recursorRun : (RecM.ensureInductiveRecursors block).run methods ctorState = .ok () after + +theorem resolved_member_run {id : KId .anon} {params indices lvls : UInt64} {ctors : Array (KId .anon)} + {block : KId .anon} {isUnsafe : Bool} {ty : KExpr .anon} {methods : Methods .anon} + {before after : TcState .anon} + (h : (RecM.checkResolvedInductiveMember id params indices lvls ctors block isUnsafe ty).run + methods before = .ok () after) : + ∃ run : ResolvedMemberRun id params indices lvls ctors block isUnsafe ty methods before, + run.after = after := by + unfold RecM.checkResolvedInductiveMember at h + simp only [ReaderT.run_bind] at h + change EStateM.bind ((RecM.discoverBlockInductives block).run methods) _ before = _ at h + obtain ⟨blockInds, discovered, discoverRun, h⟩ := bind_success h + change EStateM.bind ((RecM.checkedMetadataSum "inductive params + indices" #[params, indices]).run + methods) _ discovered = _ at h + obtain ⟨arity, arityState, arityRun, h⟩ := bind_success h + change EStateM.bind ((RecM.getResultSortLevel ty arity.toNat).run methods) _ arityState = _ at h + obtain ⟨level, levelState, levelRun, h⟩ := bind_success h + change EStateM.bind ((RecM.checkInductivePeerAgreement id block params lvls isUnsafe ty level + blockInds).run methods) _ levelState = _ at h + obtain ⟨⟨⟩, peerState, peerRun, h⟩ := bind_success h + change EStateM.bind ((RecM.checkInductiveConstructors id params.toNat indices.toNat lvls isUnsafe ty + level (blockInds.map (·.addr)) ctors.toList 0).run methods) _ peerState = _ at h + obtain ⟨⟨⟩, ctorState, ctorRun, h⟩ := bind_success h + obtain ⟨constructors⟩ := constructors_trace ctorRun + exact ⟨⟨blockInds, discovered, discoverRun, arity, arityState, arityRun, level, levelState, levelRun, + peerState, peerRun, ctorState, constructors, after, h⟩, rfl⟩ + +/-- The member check of one family identifier: lookup of the stored family +declaration and its resolved header check. -/ +structure MemberImplRun (id : KId .anon) (methods : Methods .anon) (before : TcState .anon) where + name : Mode.anon.F Name + levelParams : Mode.anon.F (Array Name) + lvls : UInt64 + params : UInt64 + indices : UInt64 + isUnsafe : Bool + block : KId .anon + memberIdx : UInt64 + ty : KExpr .anon + ctors : Array (KId .anon) + leanAll : Mode.anon.F (Array (KId .anon)) + loaded : TcState .anon + getRun : TcM.getConst id before = + .ok (.indc name levelParams lvls params indices isUnsafe block memberIdx ty ctors leanAll) loaded + resolved : ResolvedMemberRun id params indices lvls ctors block isUnsafe ty methods loaded + +theorem member_impl_run {id : KId .anon} {methods : Methods .anon} {before after : TcState .anon} + (h : (RecM.checkInductiveMemberImpl id).run methods before = .ok () after) : + ∃ run : MemberImplRun id methods before, run.resolved.after = after := by + unfold RecM.checkInductiveMemberImpl at h + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at h + change EStateM.bind (TcM.getConst id) _ before = _ at h + obtain ⟨concrete, loaded, getRun, h⟩ := bind_success h + revert h + cases concrete + case indc name levelParams lvls params indices isUnsafe block memberIdx ty ctors leanAll => + intro h + simp only [ReaderT.run_bind] at h + change EStateM.bind (pure _) _ loaded = _ at h + obtain ⟨_, _, hpure, h⟩ := bind_success h + change EStateM.Result.ok _ loaded = _ at hpure + cases hpure + obtain ⟨resolved, hafter⟩ := resolved_member_run h + exact ⟨⟨name, levelParams, lvls, params, indices, isUnsafe, block, memberIdx, ty, ctors, leanAll, + loaded, getRun, resolved⟩, hafter⟩ + all_goals + intro h + simp only [ReaderT.run_bind] at h + change EStateM.Result.error _ loaded = _ at h + cases h + +/-- The source-ordered family member checks. -/ +inductive MembersTrace (methods : Methods .anon) : + List (KId .anon) → TcState .anon → TcState .anon → Type + | nil (state : TcState .anon) : MembersTrace methods [] state state + | cons {id : KId .anon} {ids : List (KId .anon)} {before reset after : TcState .anon} + (resetRun : TcM.reset before = .ok () reset) + (run : MemberImplRun id methods reset) + (tail : MembersTrace methods ids run.resolved.after after) : + MembersTrace methods (id :: ids) before after + +theorem members_trace {methods : Methods .anon} : + ∀ {ids : List (KId .anon)} {before after : TcState .anon}, + (RecM.checkInductiveMembers ids).run methods before = .ok () after → + Nonempty (MembersTrace methods ids before after) + | [], before, after, h => by + simp only [RecM.checkInductiveMembers, ReaderT.run_pure] at h + change EStateM.Result.ok () before = _ at h + cases h + exact ⟨.nil before⟩ + | id :: ids, before, after, h => by + unfold RecM.checkInductiveMembers at h + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at h + change EStateM.bind TcM.reset _ before = _ at h + obtain ⟨⟨⟩, reset, resetRun, h⟩ := bind_success h + change EStateM.bind ((RecM.checkInductiveMemberImpl id).run methods) _ reset = _ at h + obtain ⟨⟨⟩, middle, implRun, h⟩ := bind_success h + obtain ⟨run, rfl⟩ := member_impl_run implRun + obtain ⟨tail⟩ := members_trace h + exact ⟨.cons resetRun run tail⟩ + +/-! ### Constructor member re-validation -/ + +/-- The standalone re-validation of each constructor against its stored parent +identifier; a non-constructor member is skipped by production. -/ +inductive ConstructorMembersTrace (methods : Methods .anon) : + List (KId .anon) → TcState .anon → TcState .anon → Type + | nil (state : TcState .anon) : ConstructorMembersTrace methods [] state state + | ctor {id : KId .anon} {ids : List (KId .anon)} {before loaded reset checked after : TcState .anon} + {name : Mode.anon.F Name} {levelParams : Mode.anon.F (Array Name)} {isUnsafe : Bool} + {lvls : UInt64} {induct : KId .anon} {cidx params fields : UInt64} {ty : KExpr .anon} + (getRun : TcM.getConst id before = + .ok (.ctor name levelParams isUnsafe lvls induct cidx params fields ty) loaded) + (resetRun : TcM.reset loaded = .ok () reset) + (checkRun : (RecM.checkCtorAgainstInductiveMemberImpl id induct).run methods reset = .ok () checked) + (tail : ConstructorMembersTrace methods ids checked after) : + ConstructorMembersTrace methods (id :: ids) before after + | skip {id : KId .anon} {ids : List (KId .anon)} {before loaded after : TcState .anon} + {concrete : KConst .anon} + (getRun : TcM.getConst id before = .ok concrete loaded) (kind : isConstructorDecl concrete = false) + (tail : ConstructorMembersTrace methods ids loaded after) : + ConstructorMembersTrace methods (id :: ids) before after + +theorem constructor_members_trace {methods : Methods .anon} : + ∀ {ids : List (KId .anon)} {before after : TcState .anon}, + (RecM.checkInductiveConstructorMembers ids).run methods before = .ok () after → + Nonempty (ConstructorMembersTrace methods ids before after) + | [], before, after, h => by + simp only [RecM.checkInductiveConstructorMembers, ReaderT.run_pure] at h + change EStateM.Result.ok () before = _ at h + cases h + exact ⟨.nil before⟩ + | id :: ids, before, after, h => by + unfold RecM.checkInductiveConstructorMembers at h + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at h + change EStateM.bind (TcM.getConst id) _ before = _ at h + obtain ⟨concrete, loaded, getRun, h⟩ := bind_success h + revert h + cases concrete + case ctor name levelParams isUnsafe lvls induct cidx params fields ty => + intro h + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at h + change EStateM.bind TcM.reset _ loaded = _ at h + obtain ⟨⟨⟩, reset, resetRun, h⟩ := bind_success h + change EStateM.bind ((RecM.checkCtorAgainstInductiveMemberImpl id induct).run methods) _ + reset = _ at h + obtain ⟨⟨⟩, checked, checkRun, h⟩ := bind_success h + obtain ⟨tail⟩ := constructor_members_trace h + exact ⟨.ctor getRun resetRun checkRun tail⟩ + all_goals + intro h + obtain ⟨tail⟩ := constructor_members_trace h + exact ⟨.skip getRun rfl tail⟩ + +/-! ### The whole block check -/ + +/-- The three phases of a successful inductive block check. -/ +structure InductiveBlockTrace (block : KId .anon) (members : Array (KId .anon)) + (methods : Methods .anon) (before : TcState .anon) where + indIds : Array (KId .anon) + ctorIds : Array (KId .anon) + classified : TcState .anon + classify : ClassifyTrace block methods members.toList #[] #[] before (indIds, ctorIds) classified + membersChecked : TcState .anon + familyChecks : MembersTrace methods indIds.toList classified membersChecked + after : TcState .anon + constructorChecks : ConstructorMembersTrace methods ctorIds.toList membersChecked after + +theorem inductive_block_trace {block : KId .anon} {members : Array (KId .anon)} + {methods : Methods .anon} {before after : TcState .anon} + (h : (RecM.checkInductiveBlockImpl block members).run methods before = .ok () after) : + ∃ trace : InductiveBlockTrace block members methods before, trace.after = after := by + unfold RecM.checkInductiveBlockImpl at h + simp only [ReaderT.run_bind] at h + change EStateM.bind ((RecM.classifyInductiveBlockMembers block members.toList #[] #[]).run methods) + _ before = _ at h + obtain ⟨⟨indIds, ctorIds⟩, classified, classifyRun, h⟩ := bind_success h + change EStateM.bind ((RecM.checkInductiveMembers indIds.toList).run methods) _ classified = _ at h + obtain ⟨⟨⟩, membersChecked, membersRun, h⟩ := bind_success h + obtain ⟨classify⟩ := classify_trace classifyRun + obtain ⟨familyChecks⟩ := members_trace membersRun + obtain ⟨constructorChecks⟩ := constructor_members_trace h + exact ⟨⟨indIds, ctorIds, classified, classify, membersChecked, familyChecks, after, + constructorChecks⟩, rfl⟩ + +end Ix.Kernel.Consistency.Inductive diff --git a/Ix/Kernel/Verify/Consistency/Inductive/Formation.lean b/Ix/Kernel/Verify/Consistency/Inductive/Formation.lean new file mode 100644 index 000000000..39ef737b6 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Inductive/Formation.lean @@ -0,0 +1,467 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Inductive.Recursor +import Ix.Kernel.Verify.Consistency.SynthesisInference + +/-! +# Formation facts for singleton shapes + +The certified shape conditions are semantic typing facts about telescopes. +Every one of them is derived here from retained closed type checks of the +stored declarations: a retained product check splits into the retained checks +of its binder domains, each in the context of its predecessors. Facts stated +over the family-extended interface transfer to the preceding interface +through a trivial realization of the fresh family entry, because ordinary +fields never mention the family. The generated rule syntax shares its binders +with the stored rule, so its scope and references follow syntactically. +-/ + +namespace Ix.Kernel.Consistency.Inductive + +open Theory Theory.Model Theory.Model.SetTheory Theory.Certified Theory.Certified.Ordinary + Theory.Inductive + +universe u v +variable {β : Type u} + +/-! ### Telescope contexts -/ + +theorem context_append (Γ : Context β) (left right : List (AExpr β)) : + Telescope.context Γ (left ++ right) = Telescope.context (Telescope.context Γ left) right := by + induction left generalizing Γ with + | nil => rfl + | cons A rest ih => exact ih (Γ.push A) + +theorem formed_append_inv {entries : Model.Environment β} : + ∀ {Γ : Context β} {left right : List (AExpr β)}, + Telescope.Formed.{u,v} entries Γ (left ++ right) → + Telescope.Formed.{u,v} entries Γ left ∧ + Telescope.Formed.{u,v} entries (Telescope.context Γ left) right + | _, [], _, h => ⟨.nil, h⟩ + | _, A :: rest, right, h => by + cases h with + | cons l hA htail => + obtain ⟨hleft, hright⟩ := formed_append_inv htail + exact ⟨.cons l hA hleft, hright⟩ + +theorem references_liftN (e : AExpr β) (n k : Nat) : (e.liftN n k).references = e.references := by + induction e generalizing k <;> simp_all [AExpr.liftN, AExpr.references] + +/-- Every context entry refers to the interface. -/ +def ContextRefs (entries : Model.Environment β) (Γ : Context β) : Prop := + ∀ B ∈ Γ, B.ReferencesIn entries + +theorem ContextRefs.nil (entries : Model.Environment β) : ContextRefs entries [] := by + intro B hB + simp at hB + +theorem ContextRefs.push {entries : Model.Environment β} {Γ : Context β} {A : AExpr β} + (hΓ : ContextRefs entries Γ) (hA : A.ReferencesIn entries) : ContextRefs entries (Γ.push A) := by + intro B hB + simp only [Context.push, List.mem_cons, List.mem_map] at hB + rcases hB with rfl | ⟨C, hC, rfl⟩ + · intro r hr + rw [references_liftN] at hr + exact hA r hr + · intro r hr + rw [references_liftN] at hr + exact hΓ C hC r hr + +theorem ContextRefs.context {entries : Model.Environment β} : + ∀ (domains : List (AExpr β)) {Γ : Context β}, ContextRefs entries Γ → + (∀ A ∈ domains, A.ReferencesIn entries) → ContextRefs entries (Telescope.context Γ domains) + | [], _, hΓ, _ => hΓ + | A :: rest, Γ, hΓ, hd => + ContextRefs.context rest (Γ := Γ.push A) (hΓ.push (hd A (List.mem_cons_self ..))) + (fun B hB => hd B (List.mem_cons_of_mem A hB)) + +/-! ### Retained checks of product telescopes -/ + +/-- The retained domain checks of a product telescope, each in the context of +its predecessors. -/ +inductive CheckedTelescope (resolve : Address → Option (ConstRef β)) (incoming : Model.Environment β) + (incomingContext : Context β) (incomingBounds : List VLevel) (entries : Model.Environment β) : + Context β → List (AExpr β) → Type u + | nil {Γ : Context β} : CheckedTelescope resolve incoming incomingContext incomingBounds entries Γ [] + | cons {Γ : Context β} {A : AExpr β} {rest : List (AExpr β)} + (check : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries A) + (context : check.context = Γ) + (tail : CheckedTelescope resolve incoming incomingContext incomingBounds entries (Γ.push A) rest) : + CheckedTelescope resolve incoming incomingContext incomingBounds entries Γ (A :: rest) + +theorem CheckedTelescope.formed {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} + {incomingContext : Context β} {incomingBounds : List VLevel} + (formed : ContextFormation.{u,v} incoming incomingContext incomingBounds) : + ∀ {Γ : Context β} {domains : List (AExpr β)}, + CheckedTelescope resolve incoming incomingContext incomingBounds entries Γ domains → + Telescope.Formed.{u,v} entries Γ domains + | _, _, .nil => .nil + | _, _, .cons check hctx tail => + .cons check.level (hctx ▸ check.sound formed) (CheckedTelescope.formed formed tail) + +/-- The retained check at one telescope position. -/ +theorem CheckedTelescope.get {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} + {incomingContext : Context β} {incomingBounds : List VLevel} : + ∀ {Γ : Context β} {domains : List (AExpr β)}, + CheckedTelescope resolve incoming incomingContext incomingBounds entries Γ domains → + ∀ (j : Nat) (A : AExpr β), domains[j]? = some A → + ∃ check : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries A, + check.context = Telescope.context Γ (domains.take j) + | _, _, .nil, j, A, h => by simp at h + | _, _, .cons check hctx tail, 0, A, h => by + simp only [List.getElem?_cons_zero, Option.some.injEq] at h + subst h + exact ⟨check, hctx⟩ + | _, _, .cons check hctx tail, j + 1, A, h => by + simp only [List.getElem?_cons_succ] at h + obtain ⟨found, hfound⟩ := tail.get j A h + exact ⟨found, hfound⟩ + +/-- Split a retained check of a product into the retained checks of its +domains and the retained check of its body. -/ +def telescopeChecks {resolve : Address → Option (ConstRef β)} + {incoming entries : Model.Environment β} {incomingContext : Context β} {incomingBounds : List VLevel} + {p : PropWhen} : + ∀ (domains : List (AExpr β)) {B : AExpr β} + (check : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries + (AExpr.forallN p domains B)), + CheckedTelescope resolve incoming incomingContext incomingBounds entries check.context domains × + { body : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries B // + body.context = Telescope.context check.context domains } + | [], _, check => (.nil, ⟨check, rfl⟩) + | A :: rest, B, check => + let view := check.check.forallView (condition := p) (domain := A) + (body := AExpr.forallN p rest B) rfl + let domainCheck : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries A := + ⟨check.context, view.domainLevel, view.domainCheck⟩ + let bodyCheck : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries + (AExpr.forallN p rest B) := + ⟨check.context.push A, view.bodyLevel, view.bodyCheck⟩ + let inner := telescopeChecks rest bodyCheck + (.cons domainCheck rfl inner.1, ⟨inner.2.val, inner.2.property⟩) + +/-- Transport a retained check along an equation between its terms. -/ +def retypeCheck {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} + {incomingContext : Context β} {incomingBounds : List VLevel} {t t' : AExpr β} (h : t = t') + (check : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries t) : + SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries t' := h ▸ check + +theorem retypeCheck_context {resolve : Address → Option (ConstRef β)} {incoming entries : Model.Environment β} + {incomingContext : Context β} {incomingBounds : List VLevel} {t t' : AExpr β} (h : t = t') + (check : SynthesisScopedTypeCheck resolve incoming incomingContext incomingBounds entries t) : + (retypeCheck h check).context = check.context := by + subst h + rfl + +/-- A retained closed check viewed in the empty context. -/ +def closedScoped {resolve : Address → Option (ConstRef β)} {entries : Model.Environment β} + {type : AExpr β} {level : VLevel} (check : SynthesisTypeCheck resolve entries type level) : + SynthesisScopedTypeCheck resolve entries [] [] entries type := + check.scoped + +/-! ### Transfer from the family-extended interface -/ + +/-- A fresh entry realizable in every model of the interface. -/ +def FreshRealizable [DecidableEq β] (entries : Model.Environment β) (r : ConstRef β) (entry : ConstantEntry β) : Prop := + ∀ (V : Type v) [SetTheory V] (constants : Assignment β V), Realizes constants entries → + ∃ value : List Nat → V, EntryRealization (constants.insert r value) r entry + +theorem typing_of_insert [DecidableEq β] {entries : Model.Environment β} {r : ConstRef β} + {entry : ConstantEntry β} {Γ : Context β} {e A : AExpr β} + (hE : entries.WF) (fresh : entries r = none) (realizable : FreshRealizable.{u,v} entries r entry) + (claim : TypingClaim.{u,v} (entries.insert r entry) Γ e A) + (hΓ : ContextRefs entries Γ) (he : e.ReferencesIn entries) (hA : A.ReferencesIn entries) : + TypingClaim.{u,v} entries Γ e A := by + intro V _ constants hM levels env hvalid + obtain ⟨value, realization⟩ := realizable V constants hM + have agrees := Assignment.insert_agrees fresh constants value + have hM' : Realizes (constants.insert r value) (entries.insert r entry) := + (hM.of_agrees hE agrees).insert realization + have hvalid' : Γ.Valid (constants.insert r value) levels env := by + intro i B hB + have hBr := hΓ B (List.mem_of_getElem? hB) + obtain ⟨hw, hm⟩ := hvalid i B hB + refine ⟨(agrees.wellDenoted hBr levels env).mpr hw, ?_⟩ + rw [agrees.interp hBr] + exact hm + obtain ⟨hew, hAw, hem⟩ := claim V (constants.insert r value) hM' levels env hvalid' + refine ⟨(agrees.wellDenoted he levels env).mp hew, (agrees.wellDenoted hA levels env).mp hAw, ?_⟩ + rwa [agrees.interp he levels env, agrees.interp hA levels env] at hem + +theorem formed_of_insert [DecidableEq β] {entries : Model.Environment β} {r : ConstRef β} + {entry : ConstantEntry β} (hE : entries.WF) (fresh : entries r = none) + (realizable : FreshRealizable.{u,v} entries r entry) : + ∀ {Γ : Context β} {domains : List (AExpr β)}, + Telescope.Formed.{u,v} (entries.insert r entry) Γ domains → ContextRefs entries Γ → + (∀ A ∈ domains, A.ReferencesIn entries) → Telescope.Formed.{u,v} entries Γ domains + | _, [], _, _, _ => .nil + | _, A :: rest, h, hΓ, hd => by + cases h with + | cons l hA htail => + have hAr := hd A (List.mem_cons_self ..) + refine .cons l (typing_of_insert hE fresh realizable hA hΓ hAr ?_) + (formed_of_insert hE fresh realizable htail (hΓ.push hAr) + (fun B hB => hd B (List.mem_cons_of_mem A hB))) + intro q hq + simp [AExpr.references] at hq + +/-- The fresh family entry is realized by the constantly empty family. -/ +theorem family_realizable [DecidableEq β] {entries : Model.Environment β} {shape : Shape β} {source : β} + (hs : shape.indices = []) (hscope : shape.type.Scope shape.universes 0) + (hrefs : shape.type.ReferencesIn entries) (fresh : entries (.member source 0) = none) + (formed : Telescope.Formed.{u,v} entries [] shape.parameters) : + FreshRealizable.{u,v} entries (.member source 0) shape.familyEntry := by + intro V _ constants hM + have formedAll : Telescope.Formed.{u,v} entries [] (shape.parameters ++ shape.indices) := + formed.append (by rw [hs]; exact .nil) + obtain ⟨l, _, htype⟩ := formedAll.forallN (TypingClaim.sort shape.level) + have htype' : TypingClaim.{u,v} entries [] shape.type (.sort l) := htype + let value : List Nat → V := fun levels => + Telescope.curry 1 (Telescope.interpret constants levels (fun _ => empty) + (shape.parameters ++ shape.indices)) (fun _ => empty) + have agrees := Assignment.insert_agrees fresh constants value + refine ⟨value, ?_⟩ + constructor + · intro levels _ env + exact (agrees.wellDenoted hrefs levels env).mpr + (htype' V constants hM levels env (Context.valid_nil constants levels env)).1 + · intro levels _ env + rw [Assignment.insert_same] + change value levels ∈ˢ interp (constants.insert (.member source 0) value) levels env shape.type + rw [agrees.interp hrefs levels env, interp_closed shape.type constants levels hscope env (fun _ => empty)] + simp only [Shape.type, AExpr.interp_forallN, regime_never, interp, value] + apply Telescope.curry_mem + intro xs _ + exact empty_mem_univ _ + · intro body hb + cases hb + · intro body hb + cases hb + · intro law hl + simp [Shape.familyEntry] at hl + · intro fact hf + simp [Shape.familyEntry] at hf + +/-! ### Scope and references of generated syntax -/ + +/-- Binder domains scoped in sequence, each one level deeper. -/ +def BindersScope (p : PropWhen) (universes : Nat) : Nat → List (AExpr β) → Prop + | _, [] => True + | depth, A :: rest => p.WF universes ∧ A.Scope universes depth ∧ BindersScope p universes (depth + 1) rest + +theorem scope_lamN_iff {p : PropWhen} {universes : Nat} : + ∀ {domains : List (AExpr β)} {body : AExpr β} {depth : Nat}, + (AExpr.lamN p domains body).Scope universes depth ↔ + BindersScope p universes depth domains ∧ body.Scope universes (depth + domains.length) + | [], body, depth => by simp [AExpr.lamN, BindersScope] + | A :: rest, body, depth => by + simp only [AExpr.lamN, AExpr.Scope, BindersScope, List.length_cons] + rw [scope_lamN_iff (domains := rest)] + have : depth + 1 + rest.length = depth + (rest.length + 1) := by omega + rw [this] + constructor + · rintro ⟨hp, hA, hrest, hbody⟩ + exact ⟨⟨hp, hA, hrest⟩, hbody⟩ + · rintro ⟨⟨hp, hA, hrest⟩, hbody⟩ + exact ⟨hp, hA, hrest, hbody⟩ + +theorem scope_forallN_iff {p : PropWhen} {universes : Nat} : + ∀ {domains : List (AExpr β)} {body : AExpr β} {depth : Nat}, + (AExpr.forallN p domains body).Scope universes depth ↔ + BindersScope p universes depth domains ∧ body.Scope universes (depth + domains.length) + | [], body, depth => by simp [AExpr.forallN, BindersScope] + | A :: rest, body, depth => by + simp only [AExpr.forallN, AExpr.Scope, BindersScope, List.length_cons] + rw [scope_forallN_iff (domains := rest)] + have : depth + 1 + rest.length = depth + (rest.length + 1) := by omega + rw [this] + constructor + · rintro ⟨hp, hA, hrest, hbody⟩ + exact ⟨⟨hp, hA, hrest⟩, hbody⟩ + · rintro ⟨⟨hp, hA, hrest⟩, hbody⟩ + exact ⟨hp, hA, hrest, hbody⟩ + +theorem scope_appN_iff {universes depth : Nat} : + ∀ {args : List (AExpr β)} {f : AExpr β}, + (AExpr.appN f args).Scope universes depth ↔ + f.Scope universes depth ∧ ∀ a ∈ args, a.Scope universes depth + | [], f => by simp [AExpr.appN] + | a :: args, f => by + simp only [AExpr.appN, List.mem_cons, forall_eq_or_imp] + rw [scope_appN_iff (args := args)] + simp only [AExpr.Scope] + constructor + · rintro ⟨⟨hf, ha⟩, hargs⟩ + exact ⟨hf, ha, hargs⟩ + · rintro ⟨hf, ha, hargs⟩ + exact ⟨⟨hf, ha⟩, hargs⟩ + +theorem parameterVars_scope {universes depth offset : Nat} : + ∀ {count : Nat}, offset + count ≤ depth → + ∀ v ∈ (parameterVars offset count : List (AExpr β)), v.Scope universes depth + | 0, _ => by simp [parameterVars] + | count + 1, h => by + intro v hv + simp only [parameterVars, List.mem_cons] at hv + rcases hv with rfl | hv + · show offset + count < depth + omega + · exact parameterVars_scope (by omega) v hv + +theorem referencesIn_lamN_iff {entries : Model.Environment β} {p : PropWhen} : + ∀ {domains : List (AExpr β)} {body : AExpr β}, + (AExpr.lamN p domains body).ReferencesIn entries ↔ + (∀ A ∈ domains, A.ReferencesIn entries) ∧ body.ReferencesIn entries + | [], body => by simp [AExpr.lamN] + | A :: rest, body => by + simp only [AExpr.lamN, List.mem_cons, forall_eq_or_imp] + constructor + · intro h + have hA : A.ReferencesIn entries := fun r hr => h r (List.mem_append_left _ hr) + have ht : (AExpr.lamN p rest body).ReferencesIn entries := + fun r hr => h r (List.mem_append_right _ hr) + obtain ⟨hrest, hbody⟩ := referencesIn_lamN_iff.mp ht + exact ⟨⟨hA, hrest⟩, hbody⟩ + · rintro ⟨⟨hA, hrest⟩, hbody⟩ + intro r hr + rcases List.mem_append.mp hr with hr | hr + · exact hA r hr + · exact (referencesIn_lamN_iff.mpr ⟨hrest, hbody⟩) r hr + +theorem referencesIn_appN_iff {entries : Model.Environment β} : + ∀ {args : List (AExpr β)} {f : AExpr β}, + (AExpr.appN f args).ReferencesIn entries ↔ + f.ReferencesIn entries ∧ ∀ a ∈ args, a.ReferencesIn entries + | [], f => by simp [AExpr.appN] + | a :: args, f => by + simp only [AExpr.appN, List.mem_cons, forall_eq_or_imp] + rw [referencesIn_appN_iff (args := args)] + constructor + · rintro ⟨hfa, hargs⟩ + exact ⟨fun r hr => hfa r (List.mem_append_left _ hr), + fun r hr => hfa r (List.mem_append_right _ hr), hargs⟩ + · rintro ⟨hf, ha, hargs⟩ + refine ⟨?_, hargs⟩ + intro r hr + rcases List.mem_append.mp hr with hr | hr + · exact hf r hr + · exact ha r hr + +theorem parameterVars_referencesIn {entries : Model.Environment β} {offset : Nat} : + ∀ {count : Nat}, ∀ v ∈ (parameterVars offset count : List (AExpr β)), v.ReferencesIn entries + | 0 => by simp [parameterVars] + | count + 1 => by + intro v hv + simp only [parameterVars, List.mem_cons] at hv + rcases hv with rfl | hv + · intro r hr + simp [AExpr.references] at hr + · exact parameterVars_referencesIn v hv + +theorem referencesIn_insert_inv [DecidableEq β] {entries : Model.Environment β} {r : ConstRef β} + {entry : ConstantEntry β} {e : AExpr β} (h : e.ReferencesIn (entries.insert r entry)) + (absent : r ∉ e.references) : e.ReferencesIn entries := by + intro q hq + have hne : q ≠ r := fun heq => absent (heq ▸ hq) + have := h q hq + simpa [Environment.insert, hne] using this + +theorem length_independent : ∀ (l : List (AExpr β)) (offset : Nat), + (Telescope.independent l offset).length = l.length + | [], _ => rfl + | _ :: rest, offset => by simp [Telescope.independent, length_independent rest] + +theorem length_lift : ∀ (l : List (AExpr β)) (count cutoff : Nat), + (Telescope.lift count l cutoff).length = l.length + | [], _, _ => rfl + | _ :: rest, count, cutoff => by simp [Telescope.lift, length_lift rest] + +theorem length_recursiveTypes (ctor : Constructor β) (shape : Shape β) (source : β) : + (ctor.recursiveTypes shape source).length = ctor.recursive.length := by + simp [Constructor.recursiveTypes, Ordinary.recursiveTypesFrom] + +theorem length_minorTypesSyntax (shape : Shape β) (source : β) (mode : ElimMode) : + (shape.minorTypesSyntax source mode).length = shape.constructors.length := by + simp [Shape.minorTypesSyntax, length_independent] + +theorem length_ruleBinders (shape : Shape β) (source : β) (mode : ElimMode) (ctor : Constructor β) : + (shape.ruleBinders source mode ctor).length = + shape.parameters.length + 1 + shape.constructors.length + + (ctor.fields.length + ctor.recursive.length) := by + simp only [Shape.ruleBinders, List.length_append, List.length_map, List.length_singleton, + length_minorTypesSyntax, length_lift, length_recursiveTypes] + +theorem motiveLevel_wf (mode : ElimMode) (universes : Nat) : + mode.motiveLevel.WF (mode.recUvars universes) := by + cases mode <;> simp [ElimMode.motiveLevel, ElimMode.recUvars, ElimMode.offset, VLevel.WF] + +/-- The generated left-hand side of a rule is scoped whenever the stored +right-hand side is: both bind the same telescope, and the left body applies +the recursor and the constructor to bound variables only. -/ +theorem ruleLhs_scope {shape : Shape β} {source recursor : β} {mode : ElimMode} {i : Nat} + {ctor : Constructor β} (hc : ctor.indices = []) + (hrhs : (shape.ruleRhs source recursor mode i ctor).Scope (mode.recUvars shape.universes) 0) : + (shape.ruleLhs source recursor mode i ctor).Scope (mode.recUvars shape.universes) 0 := by + unfold Shape.ruleRhs at hrhs + unfold Shape.ruleLhs + obtain ⟨hbinders, _⟩ := scope_lamN_iff.mp hrhs + refine scope_lamN_iff.mpr ⟨hbinders, ?_⟩ + rw [length_ruleBinders] + unfold Shape.ruleLhsBody + refine scope_appN_iff.mpr ⟨?_, ?_⟩ + · intro l hl + exact VLevel.params_wf hl + · intro a ha + simp only [List.mem_append, List.mem_singleton] at ha + rcases ha with (ha | ha) | rfl + · exact parameterVars_scope (by omega) a ha + · simp [Shape.ruleIndices, hc] at ha + · unfold Shape.ruleConstructor + refine scope_appN_iff.mpr ⟨?_, ?_⟩ + · intro l hl + exact mode.sourceLevels_wf shape.universes l hl + · intro v hv + rcases List.mem_append.mp hv with hv | hv + · exact parameterVars_scope (by omega) v hv + · exact parameterVars_scope (by omega) v hv + +/-- The generated left-hand side refers to the recursor, the constructor, +and the binders of the stored right-hand side. -/ +theorem ruleLhs_referencesIn [DecidableEq β] {entries : Model.Environment β} {shape : Shape β} + {source recursor : β} {mode : ElimMode} {i : Nat} {ctor : Constructor β} + (hc : ctor.indices = []) (hi : shape.constructors[i]? = some ctor) + (hrhs : (shape.ruleRhs source recursor mode i ctor).ReferencesIn + (shape.recursorEnvironment entries source recursor mode)) : + (shape.ruleLhs source recursor mode i ctor).ReferencesIn + (shape.recursorEnvironment entries source recursor mode) := by + unfold Shape.ruleRhs at hrhs + unfold Shape.ruleLhs + obtain ⟨hbinders, _⟩ := referencesIn_lamN_iff.mp hrhs + refine referencesIn_lamN_iff.mpr ⟨hbinders, ?_⟩ + unfold Shape.ruleLhsBody + refine referencesIn_appN_iff.mpr ⟨?_, ?_⟩ + · intro r hr + simp only [AExpr.references, List.mem_singleton] at hr + subst hr + simp [Shape.recursorEnvironment, Environment.insert] + · intro a ha + simp only [List.mem_append, List.mem_singleton] at ha + rcases ha with (ha | ha) | rfl + · exact parameterVars_referencesIn a ha + · simp [Shape.ruleIndices, hc] at ha + · unfold Shape.ruleConstructor + refine referencesIn_appN_iff.mpr ⟨?_, ?_⟩ + · intro r hr + simp only [AExpr.references, List.mem_singleton] at hr + subst hr + simp [Shape.recursorEnvironment, Environment.insert, Shape.constructorEnvironment, + Environment.overlay, Shape.constructorEntries, hi] + · intro v hv + rcases List.mem_append.mp hv with hv | hv + · exact parameterVars_referencesIn v hv + · exact parameterVars_referencesIn v hv + +end Ix.Kernel.Consistency.Inductive diff --git a/Ix/Kernel/Verify/Consistency/Inductive/Recursor.lean b/Ix/Kernel/Verify/Consistency/Inductive/Recursor.lean new file mode 100644 index 000000000..30c26c5b1 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Inductive/Recursor.lean @@ -0,0 +1,415 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Inductive.Block + +/-! +# Production recursor checks as execution traces + +A stored recursor is checked in two passes over its block: its untouched +stored type is inferred and sort-checked, then `checkRecursorMemberImpl` +freezes the complete stored declaration, validates the major family and the +constructive K flag, populates canonical rules, selects the generated +candidate, and compares every header field, the complete closed type, and +every rule (field count and right-hand side) with the stored declaration by +definitional equality. For a singleton family the recursor block has one +member and one motive, so the exhaustive comparison path is the only success +path: there is no coherence escape. +-/ + +namespace Ix.Kernel.Consistency.Inductive + +open Theory + +private theorem bind_success {α β : Type} {x : TcM .anon α} {k : α → TcM .anon β} + {before after : TcState .anon} {value : β} + (accepted : EStateM.bind x k before = .ok value after) : + ∃ intermediate state, x before = .ok intermediate state ∧ + k intermediate state = .ok value after := by + rw [EStateM.bind] at accepted + cases run : x before with + | error err state => rw [run] at accepted; contradiction + | ok intermediate state => + rw [run] at accepted + exact ⟨intermediate, state, rfl, accepted⟩ + +/-! ### The stored declaration snapshot -/ + +/-- The frozen stored declaration comes from production lookup, with its +major-position sum computed from the stored arities. -/ +theorem snapshot_success {id : KId .anon} {methods : Methods .anon} {before after : TcState .anon} + {snapshot : RecM.RecursorMemberDeclarationSnapshot .anon} + (h : (RecM.snapshotRecursorMemberDeclaration id).run methods before = .ok snapshot after) : + ∃ name levelParams k isUnsafe lvls params indices motives minors block memberIdx ty rules leanAll + loaded, + TcM.getConst id before = .ok (KConst.recr name levelParams k isUnsafe lvls params indices motives + minors block memberIdx ty rules leanAll) loaded ∧ + (RecM.checkedMetadataSum "recursor major index" #[params, motives, minors, indices]).run methods + loaded = .ok snapshot.majorSkip after ∧ + snapshot.recBlock = block ∧ snapshot.ty = ty ∧ snapshot.declaredK = k ∧ + snapshot.declaredLvls = lvls ∧ snapshot.declaredIsUnsafe = isUnsafe ∧ + snapshot.params = params ∧ snapshot.motives = motives ∧ snapshot.minors = minors ∧ + snapshot.indices = indices ∧ snapshot.storedRules = rules := by + unfold RecM.snapshotRecursorMemberDeclaration at h + simp only [ReaderT.run_bind, ReaderT.run_monadLift] at h + change EStateM.bind (TcM.getConst id) _ before = _ at h + obtain ⟨concrete, loaded, getRun, h⟩ := bind_success h + revert h + cases concrete + case recr name levelParams k isUnsafe lvls params indices motives minors block memberIdx ty rules + leanAll => + intro h + simp only [ReaderT.run_bind] at h + change EStateM.bind (pure _) _ loaded = _ at h + obtain ⟨_, _, hpure, h⟩ := bind_success h + change EStateM.Result.ok _ loaded = _ at hpure + cases hpure + change EStateM.bind ((RecM.checkedMetadataSum "recursor major index" + #[params, motives, minors, indices]).run methods) _ loaded = _ at h + obtain ⟨majorSkip, skipState, skipRun, h⟩ := bind_success h + change EStateM.Result.ok _ skipState = _ at h + cases h + exact ⟨name, levelParams, k, isUnsafe, lvls, params, indices, motives, minors, block, memberIdx, ty, + rules, leanAll, loaded, getRun, skipRun, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl⟩ + all_goals + intro h + change EStateM.Result.error _ loaded = _ at h + cases h + +/-- The constructive K flag is recomputed from the major family and compared +with the stored flag. -/ +theorem kTarget_success {snapshot : RecM.RecursorMemberDeclarationSnapshot .anon} {indId : KId .anon} + {methods : Methods .anon} {before after : TcState .anon} {computedK : Bool} + (h : (RecM.validateRecursorMemberKTarget snapshot indId).run methods before = .ok computedK after) : + (RecM.computeKTarget indId).run methods before = .ok computedK after ∧ + snapshot.declaredK = computedK := by + unfold RecM.validateRecursorMemberKTarget at h + simp only [ReaderT.run_bind] at h + change EStateM.bind ((RecM.computeKTarget indId).run methods) _ before = _ at h + obtain ⟨computed, state, kRun, h⟩ := bind_success h + by_cases hk : (snapshot.declaredK != computed) = true + · simp only [hk, if_true] at h + change EStateM.Result.error _ state = _ at h + cases h + · simp only [hk, Bool.false_eq_true, if_false] at h + change EStateM.Result.ok computed state = _ at h + cases h + exact ⟨kRun, by simpa using Bool.eq_false_iff.mpr hk⟩ + +/-! ### The exhaustive candidate comparison -/ + +/-- Every stored rule is compared with the generated rule at the same index: +equal field counts and definitionally equal right-hand sides. -/ +inductive RulesTrace (generatedRules storedRules : Array (Kernel.RecRule .anon)) (methods : Methods .anon) : + Nat → Nat → TcState .anon → TcState .anon → Type + | nil (index : Nat) (state : TcState .anon) : RulesTrace generatedRules storedRules methods index 0 state state + | cons {index fuel : Nat} {before middle after : TcState .anon} + (fields : generatedRules[index]!.fields = storedRules[index]!.fields) + (defEqRun : (RecM.isDefEq generatedRules[index]!.rhs storedRules[index]!.rhs).run methods before = + .ok true middle) + (tail : RulesTrace generatedRules storedRules methods (index + 1) fuel middle after) : + RulesTrace generatedRules storedRules methods index (fuel + 1) before after + +theorem rules_trace {generatedRules storedRules : Array (Kernel.RecRule .anon)} {methods : Methods .anon} : + ∀ {index fuel : Nat} {before after : TcState .anon}, + (RecM.checkGeneratedRecursorRules generatedRules storedRules index fuel).run methods before = + .ok () after → + Nonempty (RulesTrace generatedRules storedRules methods index fuel before after) + | index, 0, before, after, h => by + simp only [RecM.checkGeneratedRecursorRules, ReaderT.run_pure] at h + change EStateM.Result.ok () before = _ at h + cases h + exact ⟨.nil index before⟩ + | index, fuel + 1, before, after, h => by + unfold RecM.checkGeneratedRecursorRules at h + by_cases hfields : (generatedRules[index]!.fields != storedRules[index]!.fields) = true + · simp only [hfields, if_true, ReaderT.run_bind] at h + change EStateM.Result.error _ before = _ at h + cases h + · simp only [hfields, Bool.false_eq_true, if_false, ReaderT.run_bind] at h + change EStateM.bind ((RecM.isDefEq generatedRules[index]!.rhs storedRules[index]!.rhs).run + methods) _ before = _ at h + obtain ⟨answer, middle, defEqRun, h⟩ := bind_success h + cases answer with + | false => + simp only [Bool.not_false, if_true] at h + change EStateM.Result.error _ middle = _ at h + cases h + | true => + simp only [Bool.not_true, Bool.false_eq_true, if_false] at h + obtain ⟨tail⟩ := rules_trace h + exact ⟨.cons (by simpa using Bool.eq_false_iff.mpr hfields) defEqRun tail⟩ + +/-- The successful exhaustive comparison of the stored declaration with the +selected generated candidate. -/ +structure CandidateCheckRun (ty : KExpr .anon) (declaredLvls : UInt64) (declaredIsUnsafe : Bool) + (params motives minors indices : UInt64) (storedRules : Array (Kernel.RecRule .anon)) + (generated : GeneratedRecursor .anon) (methods : Methods .anon) (before : TcState .anon) where + lvls : declaredLvls = generated.lvls + isUnsafe : declaredIsUnsafe = generated.isUnsafe + params : params = generated.params + motives : motives = generated.motives + minors : minors = generated.minors + indices : indices = generated.indices + typeState : TcState .anon + typeRun : (RecM.isDefEq generated.ty ty).run methods before = .ok true typeState + ruleCount : generated.rules.size = storedRules.size + after : TcState .anon + rules : RulesTrace generated.rules storedRules methods 0 generated.rules.size typeState after + +theorem candidate_check_run {ty : KExpr .anon} {declaredLvls : UInt64} {declaredIsUnsafe : Bool} + {params motives minors indices : UInt64} {storedRules : Array (Kernel.RecRule .anon)} + {generated : GeneratedRecursor .anon} {methods : Methods .anon} {before after : TcState .anon} + (h : (RecM.checkGeneratedRecursorCandidate ty declaredLvls declaredIsUnsafe params motives minors + indices storedRules generated).run methods before = .ok () after) : + ∃ run : CandidateCheckRun ty declaredLvls declaredIsUnsafe params motives minors indices storedRules + generated methods before, run.after = after := by + unfold RecM.checkGeneratedRecursorCandidate at h + by_cases hlvls : (declaredLvls != generated.lvls) = true + · simp only [hlvls, if_true, ReaderT.run_bind] at h + change EStateM.Result.error _ before = _ at h + cases h + simp only [hlvls, Bool.false_eq_true, if_false] at h + by_cases hunsafe : (declaredIsUnsafe != generated.isUnsafe) = true + · simp only [hunsafe, if_true, ReaderT.run_bind] at h + change EStateM.Result.error _ before = _ at h + cases h + simp only [hunsafe, Bool.false_eq_true, if_false] at h + by_cases harity : (params != generated.params || motives != generated.motives || + minors != generated.minors || indices != generated.indices) = true + · simp only [harity, if_true, ReaderT.run_bind] at h + change EStateM.Result.error _ before = _ at h + cases h + simp only [harity, Bool.false_eq_true, if_false, ReaderT.run_bind] at h + change EStateM.bind ((RecM.isDefEq generated.ty ty).run methods) _ before = _ at h + obtain ⟨answer, typeState, typeRun, h⟩ := bind_success h + cases answer with + | false => + simp only [Bool.not_false, if_true] at h + change EStateM.Result.error _ typeState = _ at h + cases h + | true => + simp only [Bool.not_true, Bool.false_eq_true, if_false] at h + by_cases hempty : (generated.rules.isEmpty && !storedRules.isEmpty) = true + · simp only [hempty, if_true] at h + change EStateM.Result.error _ typeState = _ at h + cases h + simp only [hempty, Bool.false_eq_true, if_false] at h + by_cases hmissing : (!generated.rules.isEmpty && storedRules.isEmpty) = true + · simp only [hmissing, if_true] at h + change EStateM.Result.error _ typeState = _ at h + cases h + simp only [hmissing, Bool.false_eq_true, if_false] at h + by_cases hsize : (generated.rules.size != storedRules.size) = true + · simp only [hsize, if_true] at h + change EStateM.Result.error _ typeState = _ at h + cases h + simp only [hsize, Bool.false_eq_true, if_false] at h + obtain ⟨rules⟩ := rules_trace h + have harity' := Bool.eq_false_iff.mpr harity + simp only [Bool.or_eq_false_iff, bne_eq_false_iff_eq] at harity' + exact ⟨⟨by simpa using Bool.eq_false_iff.mpr hlvls, by simpa using Bool.eq_false_iff.mpr hunsafe, + harity'.1.1.1, harity'.1.1.2, harity'.1.2, harity'.2, typeState, typeRun, + by simpa using Bool.eq_false_iff.mpr hsize, after, rules⟩, rfl⟩ + +/-! ### The member check -/ + +/-- The stateful prelude and the frozen inputs it hands to the comparison. -/ +structure PreparedRun (id : KId .anon) (methods : Methods .anon) (before : TcState .anon) where + snapshot : RecM.RecursorMemberDeclarationSnapshot .anon + snapshotState : TcState .anon + snapshotRun : (RecM.snapshotRecursorMemberDeclaration id).run methods before = .ok snapshot snapshotState + indId : KId .anon + majorState : TcState .anon + majorRun : (RecM.validateRecursorMemberMajor snapshot).run methods snapshotState = .ok indId majorState + resolvedBlock : KId .anon + resolvedState : TcState .anon + resolveRun : (RecM.resolveRecursorMemberBlock snapshot indId).run methods majorState = + .ok resolvedBlock resolvedState + computedK : Bool + kState : TcState .anon + kRun : (RecM.validateRecursorMemberKTarget snapshot indId).run methods resolvedState = + .ok computedK kState + populated : TcState .anon + populateRun : (RecM.populateRecursorRulesFromBlock resolvedBlock snapshot.recBlock).run methods kState = + .ok () populated + generated : Array (GeneratedRecursor .anon) + after : TcState .anon + generatedRun : (RecM.snapshotGeneratedRecursors resolvedBlock).run methods populated = + .ok generated after + +/-- The prepared inputs handed to the comparison tail. -/ +def PreparedRun.prepared {id : KId .anon} {methods : Methods .anon} {before : TcState .anon} + (run : PreparedRun id methods before) : RecM.PreparedRecursorMemberCheck .anon := + { recBlock := run.snapshot.recBlock + ty := run.snapshot.ty + declaredK := run.snapshot.declaredK + declaredLvls := run.snapshot.declaredLvls + declaredIsUnsafe := run.snapshot.declaredIsUnsafe + params := run.snapshot.params + motives := run.snapshot.motives + minors := run.snapshot.minors + indices := run.snapshot.indices + storedRules := run.snapshot.storedRules + indId := run.indId + resolvedBlock := run.resolvedBlock + computedK := run.computedK + generated := run.generated } + +theorem prepared_run {id : KId .anon} {methods : Methods .anon} {before after : TcState .anon} + {prepared : RecM.PreparedRecursorMemberCheck .anon} + (h : (RecM.prepareRecursorMemberCheck id).run methods before = .ok prepared after) : + ∃ run : PreparedRun id methods before, run.after = after ∧ run.prepared = prepared := by + unfold RecM.prepareRecursorMemberCheck at h + simp only [ReaderT.run_bind] at h + change EStateM.bind ((RecM.snapshotRecursorMemberDeclaration id).run methods) _ before = _ at h + obtain ⟨snapshot, snapshotState, snapshotRun, h⟩ := bind_success h + change EStateM.bind ((RecM.validateRecursorMemberMajor snapshot).run methods) _ snapshotState = _ at h + obtain ⟨indId, majorState, majorRun, h⟩ := bind_success h + change EStateM.bind ((RecM.resolveRecursorMemberBlock snapshot indId).run methods) _ majorState = + _ at h + obtain ⟨resolvedBlock, resolvedState, resolveRun, h⟩ := bind_success h + change EStateM.bind ((RecM.validateRecursorMemberKTarget snapshot indId).run methods) _ + resolvedState = _ at h + obtain ⟨computedK, kState, kRun, h⟩ := bind_success h + change EStateM.bind ((RecM.populateRecursorRulesFromBlock resolvedBlock snapshot.recBlock).run + methods) _ kState = _ at h + obtain ⟨⟨⟩, populated, populateRun, h⟩ := bind_success h + change EStateM.bind ((RecM.snapshotGeneratedRecursors resolvedBlock).run methods) _ populated = + _ at h + obtain ⟨generated, generatedState, generatedRun, h⟩ := bind_success h + change EStateM.Result.ok _ generatedState = _ at h + cases h + exact ⟨⟨snapshot, snapshotState, snapshotRun, indId, majorState, majorRun, resolvedBlock, + resolvedState, resolveRun, computedK, kState, kRun, populated, populateRun, generated, + _, generatedRun⟩, rfl, rfl⟩ + +/-- The complete member check: the prelude, candidate selection, and the +exhaustive comparison of the selected candidate. -/ +structure RecursorMemberTrace (id : KId .anon) (methods : Methods .anon) (before : TcState .anon) where + prepared : PreparedRun id methods before + selectedIdx : Option Nat + selectedState : TcState .anon + selectRun : (RecM.selectGeneratedRecursorIndex prepared.snapshot.recBlock id prepared.snapshot.ty + prepared.snapshot.params prepared.snapshot.motives prepared.snapshot.minors prepared.indId + prepared.generated).run methods prepared.after = .ok selectedIdx selectedState + selected : GeneratedRecursor .anon + selection : selectedIdx.bind (prepared.generated[·]?) = some selected + candidate : CandidateCheckRun prepared.snapshot.ty prepared.snapshot.declaredLvls + prepared.snapshot.declaredIsUnsafe prepared.snapshot.params prepared.snapshot.motives + prepared.snapshot.minors prepared.snapshot.indices prepared.snapshot.storedRules selected methods + selectedState + +theorem recursor_member_trace {id : KId .anon} {methods : Methods .anon} {before after : TcState .anon} + (h : (RecM.checkRecursorMemberImpl id).run methods before = .ok () after) : + ∃ trace : RecursorMemberTrace id methods before, trace.candidate.after = after := by + unfold RecM.checkRecursorMemberImpl at h + simp only [ReaderT.run_bind] at h + change EStateM.bind ((RecM.prepareRecursorMemberCheck id).run methods) _ before = _ at h + obtain ⟨prepared, preparedState, preparedRun, h⟩ := bind_success h + obtain ⟨run, rfl, rfl⟩ := prepared_run preparedRun + change (RecM.checkGeneratedRecursorFromCache run.snapshot.recBlock id run.snapshot.ty + run.snapshot.declaredLvls run.snapshot.declaredIsUnsafe run.snapshot.params run.snapshot.motives + run.snapshot.minors run.snapshot.indices run.indId run.snapshot.storedRules run.generated).run + methods run.after = _ at h + unfold RecM.checkGeneratedRecursorFromCache at h + simp only [ReaderT.run_bind] at h + change EStateM.bind ((RecM.selectGeneratedRecursorIndex run.snapshot.recBlock id run.snapshot.ty + run.snapshot.params run.snapshot.motives run.snapshot.minors run.indId run.generated).run + methods) _ run.after = _ at h + obtain ⟨selectedIdx, selectedState, selectRun, h⟩ := bind_success h + revert h + cases hsel : selectedIdx.bind (run.generated[·]?) with + | none => + intro h + change EStateM.Result.error _ selectedState = _ at h + cases h + | some selected => + intro h + obtain ⟨candidate, rfl⟩ := candidate_check_run h + exact ⟨⟨run, selectedIdx, selectedState, selectRun, selected, hsel, candidate⟩, rfl⟩ + +/-- Singleton families have one motive, so the selected candidate is compared +in full with the stored declaration; no coherence escape exists. -/ +theorem RecursorMemberTrace.strict {id : KId .anon} {methods : Methods .anon} {before : TcState .anon} + (trace : RecursorMemberTrace id methods before) : + trace.prepared.snapshot.declaredLvls = trace.selected.lvls ∧ + trace.prepared.snapshot.declaredIsUnsafe = trace.selected.isUnsafe ∧ + trace.prepared.snapshot.motives = trace.selected.motives ∧ + (RecM.isDefEq trace.selected.ty trace.prepared.snapshot.ty).run methods trace.selectedState = + .ok true trace.candidate.typeState ∧ + trace.selected.rules.size = trace.prepared.snapshot.storedRules.size ∧ + trace.prepared.snapshot.declaredK = trace.prepared.computedK := + ⟨trace.candidate.lvls, trace.candidate.isUnsafe, trace.candidate.motives, trace.candidate.typeRun, + trace.candidate.ruleCount, (kTarget_success trace.prepared.kRun).2⟩ + +/-! ### The singleton recursor block -/ + +/-- Whether a loaded declaration is a recursor. -/ +def isRecursorDecl : KConst .anon → Bool + | .recr .. => true + | _ => false + +/-- The two passes of `checkRecursorBlockImpl` over a one-member block. -/ +structure RecursorBlockTrace (member : KId .anon) (methods : Methods .anon) (before : TcState .anon) where + typeRun : MemberTypeRun member methods before + recursor : isRecursorDecl typeRun.concrete = true + reset : TcState .anon + resetRun : TcM.reset typeRun.after = .ok () reset + memberCheck : RecursorMemberTrace member methods reset + +theorem recursor_block_trace {block member : KId .anon} {methods : Methods .anon} + {before after : TcState .anon} + (h : (RecM.checkRecursorBlockImpl block #[member]).run methods before = .ok () after) : + ∃ trace : RecursorBlockTrace member methods before, trace.memberCheck.candidate.after = after := by + unfold RecM.checkRecursorBlockImpl at h + simp only [← Array.forIn_toList, List.forIn_cons, List.forIn_nil, ReaderT.run_bind, + ReaderT.run_monadLift] at h + change EStateM.bind (EStateM.bind (EStateM.bind TcM.reset _) _) _ before = _ at h + obtain ⟨_, firstState, first, h⟩ := bind_success h + obtain ⟨step, stepState, inner, firstMatch⟩ := bind_success first + obtain ⟨⟨⟩, reset₁, resetRun₁, inner⟩ := bind_success inner + change EStateM.bind (TcM.getConst member) _ reset₁ = _ at inner + obtain ⟨concrete, loaded, getRun, inner⟩ := bind_success inner + change EStateM.bind ((RecM.validateConstWellScoped concrete).run methods) _ loaded = _ at inner + obtain ⟨⟨⟩, validated, validationRun, inner⟩ := bind_success inner + revert inner + cases concrete + case recr name levelParams k isUnsafe lvls params indices motives minors recBlock memberIdx ty rules + leanAll => + intro inner + simp only [ReaderT.run_bind] at inner + change EStateM.bind ((RecM.infer ty).run methods) _ validated = _ at inner + obtain ⟨inferred, typeState, typeRun, inner⟩ := bind_success inner + change EStateM.bind ((RecM.ensureSortDirect inferred).run methods) _ typeState = _ at inner + obtain ⟨level, sortState, sortRun, inner⟩ := bind_success inner + change EStateM.Result.ok (ForInStep.yield ()) sortState = _ at inner + cases inner + change EStateM.Result.ok () stepState = _ at firstMatch + cases firstMatch + change EStateM.bind (EStateM.bind (EStateM.bind TcM.reset _) _) _ _ = _ at h + obtain ⟨_, secondState, second, h⟩ := bind_success h + obtain ⟨step₂, stepState₂, inner₂, secondMatch⟩ := bind_success second + obtain ⟨⟨⟩, reset₂, resetRun₂, inner₂⟩ := bind_success inner₂ + change EStateM.bind ((RecM.checkRecursorMemberImpl member).run methods) _ reset₂ = _ at inner₂ + obtain ⟨⟨⟩, checked, checkRun, inner₂⟩ := bind_success inner₂ + obtain ⟨memberCheck, rfl⟩ := recursor_member_trace checkRun + change EStateM.Result.ok (ForInStep.yield ()) memberCheck.candidate.after = _ at inner₂ + cases inner₂ + change EStateM.Result.ok () memberCheck.candidate.after = _ at secondMatch + cases secondMatch + change EStateM.Result.ok () memberCheck.candidate.after = _ at h + cases h + exact ⟨⟨⟨_, reset₁, loaded, validated, inferred, typeState, level, _, resetRun₁, getRun, + validationRun, typeRun, sortRun⟩, rfl, reset₂, resetRun₂, memberCheck⟩, rfl⟩ + all_goals + intro inner + simp only [ReaderT.run_bind] at inner + change EStateM.bind (throw _) _ validated = _ at inner + obtain ⟨_, _, hthrow, _⟩ := bind_success inner + change EStateM.Result.error _ validated = _ at hthrow + cases hthrow + +end Ix.Kernel.Consistency.Inductive diff --git a/Ix/Kernel/Verify/Consistency/Inductive/Run.lean b/Ix/Kernel/Verify/Consistency/Inductive/Run.lean new file mode 100644 index 000000000..7ee74267d --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Inductive/Run.lean @@ -0,0 +1,311 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Inductive.Admission + +/-! +# Singleton admission from the production runs + +The retained closed type checks required by the admission theorem are built +from the exact inference calls of the member classification pass and of the +recursor block's first pass, together with a finite inference tree for each +call, as the axiom and definition admissions do. Scope of every stored type +and rule comes from the validation calls of the same passes. +-/ + +namespace Ix.Kernel.Consistency.Inductive + +open Theory Theory.Model Theory.Certified Theory.Certified.Ordinary Theory.Inductive + +universe v + +private theorem bind_success {α β : Type} {x : TcM .anon α} {k : α → TcM .anon β} + {before after : TcState .anon} {value : β} + (accepted : EStateM.bind x k before = .ok value after) : + ∃ intermediate state, x before = .ok intermediate state ∧ + k intermediate state = .ok value after := by + rw [EStateM.bind] at accepted + cases run : x before with + | error err state => rw [run] at accepted; contradiction + | ok intermediate state => + rw [run] at accepted + exact ⟨intermediate, state, rfl, accepted⟩ + +/-! ### Scope from validation -/ + +/-- Validation of any stored declaration begins by validating its type. -/ +theorem validateConst_type_scoped {c : KConst .anon} {methods : Methods .anon} + {before after : TcState .anon} + (h : (RecM.validateConstWellScoped c).run methods before = .ok () after) + {support : RunSupport} (coverage : c.ty.ValidationCoverage support) + (collision : support.CollisionFree) : c.ty.Scoped 0 c.lvls.toNat := by + unfold RecM.validateConstWellScoped at h + simp only [ReaderT.run_bind] at h + change EStateM.bind ((RecM.validateExprWellScoped c.ty 0 c.lvls.toNat).run methods) _ before = _ at h + obtain ⟨⟨⟩, _, typeRun, _⟩ := bind_success h + obtain ⟨_, _, _, hscoped⟩ := RecM.validateExprWellScoped_sound coverage collision typeRun + exact hscoped + +/-- A yielding loop body runs on every element of the list. -/ +theorem forIn_yield_all {α : Type} {methods : Methods .anon} + {f : α → PUnit → RecM .anon (ForInStep PUnit)} (P : α → Prop) + (hf : ∀ a s x s', (f a ()).run methods s = .ok x s' → x = .yield () ∧ P a) : + ∀ {l : List α} {s s' : TcState .anon}, (forIn l () f).run methods s = .ok () s' → ∀ a ∈ l, P a + | [], _, _, _ => by simp + | a :: l, s, s', h => by + simp only [List.forIn_cons, ReaderT.run_bind] at h + change EStateM.bind ((f a ()).run methods) _ s = _ at h + obtain ⟨x, s1, hfa, h⟩ := bind_success h + obtain ⟨rfl, hP⟩ := hf a s x s1 hfa + intro b hb + rcases List.mem_cons.mp hb with rfl | hb + · exact hP + · change (forIn l () f).run methods s1 = _ at h + exact forIn_yield_all P hf h b hb + +/-- Validation of a recursor validates every stored rule right-hand side. -/ +theorem validateConst_rules_scoped {name : Mode.anon.F Name} {levelParams : Mode.anon.F (Array Name)} + {k isUnsafe : Bool} {lvls params indices motives minors : UInt64} {block : KId .anon} + {memberIdx : UInt64} {ty : KExpr .anon} {rules : Array (Kernel.RecRule .anon)} + {leanAll : Mode.anon.F (Array (KId .anon))} {methods : Methods .anon} {before after : TcState .anon} + (h : (RecM.validateConstWellScoped (KConst.recr name levelParams k isUnsafe lvls params indices + motives minors block memberIdx ty rules leanAll)).run methods before = .ok () after) + {support : RunSupport} (coverage : ∀ rule ∈ rules.toList, rule.rhs.ValidationCoverage support) + (collision : support.CollisionFree) : + ∀ rule ∈ rules.toList, rule.rhs.Scoped 0 lvls.toNat := by + unfold RecM.validateConstWellScoped at h + simp only [ReaderT.run_bind] at h + change EStateM.bind ((RecM.validateExprWellScoped ty 0 lvls.toNat).run methods) _ before = _ at h + obtain ⟨⟨⟩, validated, _, h⟩ := bind_success h + simp only [← Array.forIn_toList] at h + change EStateM.bind ((forIn rules.toList () _ : RecM .anon PUnit).run methods) _ validated = _ at h + obtain ⟨⟨⟩, looped, hloop, _⟩ := bind_success h + have hf : ∀ (rule : Kernel.RecRule .anon) (s : TcState .anon) (x : ForInStep PUnit) (s' : TcState .anon), + ((fun rule (_ : PUnit) => (do + RecM.validateExprWellScoped rule.rhs 0 lvls.toNat + pure (ForInStep.yield PUnit.unit) : RecM .anon (ForInStep PUnit))) rule ()).run methods s = + .ok x s' → + x = .yield () ∧ (rule ∈ rules.toList → rule.rhs.Scoped 0 lvls.toNat) := by + intro rule s x s' hb + simp only [ReaderT.run_bind] at hb + change EStateM.bind ((RecM.validateExprWellScoped rule.rhs 0 lvls.toNat).run methods) _ s = _ at hb + obtain ⟨⟨⟩, s2, run, hb⟩ := bind_success hb + change EStateM.Result.ok (ForInStep.yield ()) s2 = _ at hb + cases hb + refine ⟨rfl, fun hmem => ?_⟩ + obtain ⟨_, _, _, hscoped⟩ := RecM.validateExprWellScoped_sound (coverage rule hmem) collision run + exact hscoped + intro rule hmem + exact forIn_yield_all _ hf hloop rule hmem hmem + +/-! ### Retained checks from member runs -/ + +/-- A retained closed type check whose executed source is a stored +declaration's type, with the validation-derived scope of that type. -/ +structure StoredTypeCheck (resolve : Address → Option (ConstRef Address)) + (entries : Model.Environment Address) (decl : KConst .anon) (type : AExpr Address) (level : VLevel) where + check : SynthesisTypeCheck resolve entries type level + source : check.source = decl.ty + typeScoped : decl.ty.Scoped 0 decl.lvls.toNat + +/-- The retained check of a classification member's actual inference call. -/ +def MemberTypeRun.storedTypeCheck {member : KId .anon} {fuel : Nat} {before : TcState .anon} + (run : MemberTypeRun member (methodsN fuel) before) + {resolve : Address → Option (ConstRef Address)} {entries : Model.Environment Address} + {type : AExpr Address} {level bound : VLevel} + (tree : SynthesisInference resolve entries [] [] [] fuel run.validated run.concrete.ty type + (.sort level) bound) + (reading : readScopedExpr? resolve [] run.concrete.ty = some type.erase) + {support : RunSupport} (coverage : run.concrete.ty.ValidationCoverage support) + (collision : support.CollisionFree) : + StoredTypeCheck resolve entries run.concrete type level where + check := + { fuel, before := run.validated, after := run.typeState, source := run.concrete.ty, + result := run.inferred, bound, inference := tree, reading, run := run.typeRun } + source := rfl + typeScoped := validateConst_type_scoped run.validationRun coverage collision + +/-- The stored rules of a recursor block member are scoped by its validation. -/ +theorem RecursorBlockTrace.rulesScoped {member : KId .anon} {methods : Methods .anon} + {before : TcState .anon} (trace : RecursorBlockTrace member methods before) + {name : Mode.anon.F Name} {levelParams : Mode.anon.F (Array Name)} {k isUnsafe : Bool} + {lvls params indices motives minors : UInt64} {block : KId .anon} {memberIdx : UInt64} + {ty : KExpr .anon} {rules : Array (Kernel.RecRule .anon)} {leanAll : Mode.anon.F (Array (KId .anon))} + (concrete : trace.typeRun.concrete = KConst.recr name levelParams k isUnsafe lvls params indices + motives minors block memberIdx ty rules leanAll) + {support : RunSupport} (coverage : ∀ rule ∈ rules.toList, rule.rhs.ValidationCoverage support) + (collision : support.CollisionFree) : + ∀ rule ∈ rules.toList, rule.rhs.Scoped 0 lvls.toNat := by + have h := trace.typeRun.validationRun + rw [concrete] at h + exact validateConst_rules_scoped h coverage collision + +/-- The stored rules of a recursor declaration. -/ +def storedRules : KConst .anon → List (Kernel.RecRule .anon) + | .recr (rules := rules) .. => rules.toList + | _ => [] + +/-! ### The run support -/ + +/-- Everything the admission theorem takes from the production runs: the +retained closed type checks of the stored family, every constructor, and the +recursor, and validation of the stored rules. -/ +structure SingletonRunSupport (resolve : Address → Option (ConstRef Address)) + (entries : Model.Environment Address) (source recursor : Address) (family : KConst .anon) + (ctors : List (KConst .anon)) (recr : KConst .anon) (shape : Shape Address) (mode : ElimMode) where + familyLevel : VLevel + familyCheck : StoredTypeCheck resolve entries family shape.type familyLevel + constructorLevel : Nat → VLevel + constructorCheck : ∀ (i : Nat) (ctor : Constructor Address) (decl : KConst .anon), + shape.constructors[i]? = some ctor → ctors[i]? = some decl → + StoredTypeCheck resolve (shape.familyEnvironment entries source) decl (ctor.type shape source) + (constructorLevel i) + recursorLevel : VLevel + recursorCheck : StoredTypeCheck resolve (shape.constructorEnvironment entries source) recr + (shape.recursorType source mode) recursorLevel + rulesScoped : ∀ rule ∈ storedRules recr, rule.rhs.Scoped 0 recr.lvls.toNat + +section + +variable {resolve : Address → Option (ConstRef Address)} {entries : Model.Environment Address} + {source recursor : Address} {family : KConst .anon} {ctors : List (KConst .anon)} + {recr : KConst .anon} {shape : Shape Address} {mode : ElimMode} + +def SingletonRunSupport.checks + (R : SingletonRunSupport resolve entries source recursor family ctors recr shape mode) + (hlen : ctors.length = shape.constructors.length) : + SingletonChecks resolve entries source recursor shape mode where + familyLevel := R.familyLevel + familyCheck := R.familyCheck.check + constructorLevel := R.constructorLevel + constructorCheck := fun i ctor hc => + match hd : ctors[i]? with + | some decl => (R.constructorCheck i ctor decl hc hd).check + | none => by + exfalso + have hlt : i < ctors.length := by + rw [hlen] + exact (List.getElem?_eq_some_iff.mp hc).1 + rw [List.getElem?_eq_getElem hlt] at hd + cases hd + recursorLevel := R.recursorLevel + recursorCheck := R.recursorCheck.check + +end + +/-! ### Scope from the witness readings -/ + +theorem recursorSourceCheck_block {resolve : Address → Option (ConstRef Address)} + {shape : Shape Address} {source recursor : Address} {mode : ElimMode} {recr : KConst .anon} + (check : recursorSourceCheck resolve shape source recursor mode recr = true) : + ∃ k, recursorBlock? resolve recr = some ⟨[shape.recursorSource source recursor mode k]⟩ := by + cases recr + case recr name levelParams k isUnsafe lvls params indices motives minors block memberIdx ty rules leanAll => + simp only [recursorSourceCheck, Bool.and_eq_true, decide_eq_true_eq] at check + exact ⟨k, check.1⟩ + all_goals simp [recursorSourceCheck] at check + +theorem recursorBlock?_some {resolve : Address → Option (ConstRef Address)} {recr : KConst .anon} + {block : Block Address} (h : recursorBlock? resolve recr = some block) : + ∃ name levelParams k lvls params indices motives minors rblock memberIdx ty rules leanAll, + recr = KConst.recr name levelParams k false lvls params indices motives minors rblock memberIdx ty + rules leanAll ∧ + recursorBlockOf? resolve k lvls params indices motives minors ty rules.toList = some block := by + unfold recursorBlock? at h + split at h + next name levelParams k lvls params indices motives minors rblock memberIdx ty rules leanAll => + exact ⟨name, levelParams, k, lvls, params, indices, motives, minors, rblock, memberIdx, ty, rules, + leanAll, rfl, h⟩ + · contradiction + +section + +variable {resolve : Address → Option (ConstRef Address)} {entries : Model.Environment Address} + {source recursor : Address} {family : KConst .anon} {ctors : List (KConst .anon)} + {recr : KConst .anon} {shape : Shape Address} {mode : ElimMode} + +/-- Scope of every stored type and rule follows from validation of the stored +declarations and their readings to the certified syntax. -/ +theorem SingletonRunSupport.scope + (R : SingletonRunSupport resolve entries source recursor family ctors recr shape mode) + (check : singletonWitnessCheck resolve source recursor family ctors recr shape mode = true) : + SingletonScope source recursor shape mode := by + obtain ⟨hfam, hrec, _, hmode, hcondFamily, hcondCtors, _, hcondRec, hcondRules⟩ := + singletonWitnessCheck_sound check + obtain ⟨lvls, params, ty, ctorIds, block, memberIdx, leanAll, name, levelParams, hfamily, hlvls, + _, _, hreading, hlen, hctors⟩ := familyBlock?_family hfam + have familyScoped := R.familyCheck.typeScoped + rw [hfamily] at familyScoped + change ty.Scoped 0 lvls.toNat at familyScoped + rw [hlvls] at familyScoped + obtain ⟨k, hblock⟩ := recursorSourceCheck_block hrec + obtain ⟨rname, rlevelParams, k', rlvls, rparams, rindices, rmotives, rminors, rblock, rmemberIdx, rty, + rules, rleanAll, hrecr, hof⟩ := recursorBlock?_some hblock + obtain ⟨type, ruleDecls, htype, hrules, hblockEq⟩ := recursorBlockOf?_recursor hof + simp only [Shape.recursorSource, Block.mk.injEq, List.cons.injEq, and_true, + Const.recursor.injEq] at hblockEq + obtain ⟨hrlvls, _, _, _, _, htypeEq, hrulesEq, _⟩ := hblockEq + have hrecUvars : rlvls.toNat = mode.recUvars shape.universes := by + have := modeOf_recUvars hmode + rw [hfamily, hrecr] at this + change rlvls.toNat = mode.recUvars lvls.toNat at this + rw [this, hlvls] + have recursorScoped := R.recursorCheck.typeScoped + rw [hrecr] at recursorScoped + change rty.Scoped 0 rlvls.toNat at recursorScoped + rw [hrecUvars] at recursorScoped + refine ⟨readScopedExpr?_annotated_scope hreading familyScoped hcondFamily, ?_, + readScopedExpr?_annotated_scope (by rw [htype, htypeEq]) recursorScoped hcondRec, ?_⟩ + · intro ctor hmem + obtain ⟨i, hc⟩ := List.mem_iff_getElem?.mp hmem + obtain ⟨decl, hd, hread⟩ := hctors i ctor hc + obtain ⟨cty, fields, induct, cidx, cname, clevelParams, hdecl, _, hcreading⟩ := readCtor?_source hread + have ctorScoped := (R.constructorCheck i ctor decl hc hd).typeScoped + rw [hdecl] at ctorScoped + change cty.Scoped 0 lvls.toNat at ctorScoped + rw [hlvls] at ctorScoped + exact readScopedExpr?_annotated_scope hcreading ctorScoped (hcondCtors ctor hmem) + · intro i ctor hc + have hdecl : ruleDecls[i]? = some ⟨ctor.fields.length + ctor.recursive.length, + (shape.ruleRhs source recursor mode i ctor).erase⟩ := by + rw [← hrulesEq] + simp only [List.getElem?_map, List.getElem?_zipIdx, hc, Option.map_some, Nat.zero_add] + obtain ⟨hlenRules, hforward⟩ := mapM_some hrules + have hlt : i < rules.toList.length := by + rw [← hlenRules] + exact (List.getElem?_eq_some_iff.mp hdecl).1 + obtain ⟨read, hread, hrule⟩ := hforward i rules.toList[i] (List.getElem?_eq_getElem hlt) + rw [hdecl] at hread + cases hread + unfold readRule? at hrule + obtain ⟨rhs, hrhs, hrhsEq⟩ := Option.map_eq_some_iff.mp hrule + simp only [Theory.RecRule.mk.injEq] at hrhsEq + have ruleScoped := R.rulesScoped rules.toList[i] (by rw [hrecr]; exact List.getElem_mem hlt) + rw [hrecr] at ruleScoped + change (rules.toList[i]).rhs.Scoped 0 rlvls.toNat at ruleScoped + rw [hrecUvars] at ruleScoped + exact readScopedExpr?_annotated_scope (by rw [hrhs, hrhsEq.2]) ruleScoped (hcondRules i ctor hc) + +/-- Admission of a singleton block from its production runs: the certified +block condition and the published model extension. -/ +theorem singleton_admission {store : Store Address} + (check : singletonWitnessCheck resolve source recursor family ctors recr shape mode = true) + (I : SingletonInterface resolve entries store source recursor family ctors recr shape mode) + (R : SingletonRunSupport resolve entries source recursor family ctors recr shape mode) + (seams : InductiveSeamAssumptions.{v} entries source recursor shape mode) : + CheckedBlock.{0,v} entries store source recursor shape mode ∧ + (shape.publishedEnvironment entries source recursor mode).WF ∧ + ∀ (V : Type v) [SetTheory V] (constants : Assignment Address V), Realizes constants entries → + Realizes (shape.recursorAssignment constants source recursor mode) + (shape.publishedEnvironment entries source recursor mode) ∧ + Assignment.AgreesOn entries constants (shape.recursorAssignment constants source recursor mode) := by + obtain ⟨hfam, _⟩ := singletonWitnessCheck_sound check + obtain ⟨_, _, _, _, _, _, _, _, _, _, _, _, _, _, hlen, _⟩ := familyBlock?_family hfam + exact ⟨singleton_checkedBlock check I (R.scope check) (R.checks hlen) seams, + singleton_published check I (R.scope check) (R.checks hlen) seams⟩ + +end + +end Ix.Kernel.Consistency.Inductive diff --git a/Ix/Kernel/Verify/Consistency/Inductive/Shape.lean b/Ix/Kernel/Verify/Consistency/Inductive/Shape.lean new file mode 100644 index 000000000..8aedd2b09 --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Inductive/Shape.lean @@ -0,0 +1,654 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Expr +import Ix.Kernel.Verify.Consistency.Validation +import Ix.Theory.Certified.Ordinary.RecursorStage +import Ix.Certified.Ixon + +/-! +# Singleton inductive shapes read from kernel declarations + +A singleton block is one non-indexed family with its constructors, checked +by production as one inductive block, and its canonical recursor stored as a +separate one-member block. The certified `Ordinary.Shape` witness is +determined by the stored declarations as follows. + +* `Shape.universes`, `Shape.parameters.length`: the family's `lvls` and + `params` metadata. +* `Shape.parameters`, `Shape.level`: the outermost `params` binder domains + and the final sort of the stored family type, read by `readExpr?`. +* `Shape.indices`, every `Constructor.indices`, every + `RecursiveField.indices`: empty. +* `Constructor.fields`, `Constructor.recursive`: the binder domains of the + stored constructor type after its parameter prefix, split into the ordinary + prefix and the family-valued suffix; the `j`th recursive domain is the stored + domain with the `j` earlier recursive binders removed. +* `ElimMode`: the difference between the recursor's and the family's universe + arities. +* binder conditions: annotations of the raw readings; the retained inference + fixes them, and `annotateNever` is exact for binder-free domains. + +Every reading is validated by a decidable comparison of the stored block with +the certified `Shape.source` and `Shape.recursorSource` syntax. +-/ + +namespace Ix.Kernel.Consistency.Inductive + +open Theory Theory.Model Theory.Certified Theory.Certified.Ordinary Theory.Inductive + +universe u +variable {β : Type u} + +/-! ### Erased singleton shapes -/ + +/-- Constructor field telescopes with every binder condition erased. -/ +structure ErasedConstructor (β : Type u) where + fields : List (VExpr β) + recursive : List (List (VExpr β)) +deriving DecidableEq + +structure ErasedShape (β : Type u) where + universes : Nat + parameters : List (VExpr β) + level : VLevel + constructors : List (ErasedConstructor β) +deriving DecidableEq + +def familyHead (source : β) (universes : Nat) : VExpr β := + .const (.member source 0) (VLevel.params universes) + +def familyApp (source : β) (universes nparams offset : Nat) : VExpr β := + (familyHead source universes).appN (VExpr.bvarRevRange offset nparams) + +def recursiveType (source : β) (universes nparams ordinary : Nat) + (domains : List (VExpr β)) : VExpr β := + VExpr.forallN domains (familyApp source universes nparams (ordinary + domains.length)) + +def recursiveTelescope (source : β) (universes nparams ordinary : Nat) : + List (List (VExpr β)) → Nat → List (VExpr β) + | [], _ => [] + | domains :: rest, offset => + (recursiveType source universes nparams ordinary domains).liftN offset :: + recursiveTelescope source universes nparams ordinary rest (offset + 1) + +def ErasedConstructor.type (ctor : ErasedConstructor β) (source : β) (universes : Nat) + (parameters : List (VExpr β)) : VExpr β := + VExpr.forallN parameters <| + VExpr.forallN ctor.fields <| + VExpr.forallN (recursiveTelescope source universes parameters.length ctor.fields.length + ctor.recursive 0) <| + familyApp source universes parameters.length (ctor.fields.length + ctor.recursive.length) + +def ErasedShape.type (shape : ErasedShape β) : VExpr β := + VExpr.forallN shape.parameters (.sort shape.level) + +/-! ### Erasure of the certified shape -/ + +def eraseConstructor (ctor : Constructor β) : ErasedConstructor β := + ⟨ctor.fields.map AExpr.erase, ctor.recursive.map fun field => field.domains.map AExpr.erase⟩ + +def eraseShape (shape : Shape β) : ErasedShape β := + ⟨shape.universes, shape.parameters.map AExpr.erase, shape.level, + shape.constructors.map eraseConstructor⟩ + +/-- No indices anywhere: the family, every constructor result, and every +recursive field target the bare parameter instance. -/ +def SingletonShape (shape : Shape β) : Prop := + shape.indices = [] ∧ ∀ ctor ∈ shape.constructors, + ctor.indices = [] ∧ ∀ field ∈ ctor.recursive, field.indices = [] + +instance (shape : Shape β) : Decidable (SingletonShape shape) := by + unfold SingletonShape + infer_instance + +theorem erase_familyApp (shape : Shape β) (source : β) (offset : Nat) : + (shape.familyApp source offset []).erase = + familyApp source shape.universes shape.parameters.length offset := by + simp [Shape.familyApp, familyApp, familyHead, AExpr.erase_appN, AExpr.erase] + +theorem erase_recursiveFieldType {shape : Shape β} {field : RecursiveField β} (source : β) + (ordinary : Nat) (hf : field.indices = []) : + (field.type shape source ordinary).erase = + recursiveType source shape.universes shape.parameters.length ordinary + (field.domains.map AExpr.erase) := by + simp [RecursiveField.type, recursiveType, hf, erase_familyApp] + +theorem erase_recursiveTypesFrom {shape : Shape β} (source : β) (ordinary : Nat) + (fields : List (RecursiveField β)) (offset : Nat) + (hf : ∀ field ∈ fields, field.indices = []) : + (Ordinary.recursiveTypesFrom shape source ordinary fields offset).map AExpr.erase = + recursiveTelescope source shape.universes shape.parameters.length ordinary + (fields.map fun field => field.domains.map AExpr.erase) offset := by + induction fields generalizing offset with + | nil => rfl + | cons field fields ih => + have head := erase_recursiveFieldType (shape := shape) (field := field) source ordinary + (hf field (List.mem_cons_self ..)) + have tail := ih (offset + 1) (fun f hm => hf f (List.mem_cons_of_mem field hm)) + simp only [Ordinary.recursiveTypesFrom, List.zipIdx_cons, List.map_cons, List.map_map, + Function.comp_def, AExpr.erase_liftN, head, recursiveTelescope] at tail ⊢ + rw [tail] + +theorem erase_constructorType {shape : Shape β} {ctor : Constructor β} (source : β) + (hc : ctor.indices = []) (hr : ∀ field ∈ ctor.recursive, field.indices = []) : + (ctor.type shape source).erase = + (eraseConstructor ctor).type source shape.universes (eraseShape shape).parameters := by + simp only [Constructor.type, ErasedConstructor.type, eraseConstructor, + eraseShape, Constructor.recursiveTypes, AExpr.erase_forallN, List.length_map] + rw [erase_recursiveTypesFrom source ctor.fields.length ctor.recursive 0 hr] + simp [hc, erase_familyApp] + +theorem erase_shapeType {shape : Shape β} (hs : shape.indices = []) : + shape.type.erase = (eraseShape shape).type := by + simp [Shape.type, ErasedShape.type, eraseShape, hs, AExpr.erase] + +/-! ### Reading singleton shapes from erased declaration types -/ + +/-- Peel exactly `n` outermost dependent function binders. -/ +def peelForalls? : Nat → VExpr β → Option (List (VExpr β) × VExpr β) + | 0, e => some ([], e) + | n + 1, .forallE A B => + match peelForalls? n B with + | some (domains, body) => some (A :: domains, body) + | none => none + | _ + 1, _ => none + +theorem peelForalls?_forallN {n : Nat} : ∀ {e : VExpr β} {domains : List (VExpr β)} {body : VExpr β}, + peelForalls? n e = some (domains, body) → e = VExpr.forallN domains body ∧ domains.length = n := by + induction n with + | zero => + intro e domains body h + simp only [peelForalls?, Option.some.injEq, Prod.mk.injEq] at h + obtain ⟨rfl, rfl⟩ := h + exact ⟨rfl, rfl⟩ + | succ n ih => + intro e domains body h + cases e with + | forallE A B => + simp only [peelForalls?] at h + split at h + next rest body' hrest => + cases h + obtain ⟨rfl, hlen⟩ := ih hrest + exact ⟨rfl, by simp [hlen]⟩ + next => contradiction + | _ => simp [peelForalls?] at h + +/-- Peel every outermost binder. -/ +def peelAllForalls : VExpr β → List (VExpr β) × VExpr β + | .forallE A B => + match peelAllForalls B with + | (domains, body) => (A :: domains, body) + | e => ([], e) + +/-- Remove `n` free variables at cutoff `k` from a term with no free +occurrence in that range. -/ +def lowerN? (n : Nat) : VExpr β → (k : Nat := 0) → Option (VExpr β) + | .bvar i, k => if i < k then some (.bvar i) else if n + k ≤ i then some (.bvar (i - n)) else none + | .sort u, _ => some (.sort u) + | .const r us, _ => some (.const r us) + | .app f a, k => do return .app (← lowerN? n f k) (← lowerN? n a k) + | .lam A b, k => do return .lam (← lowerN? n A k) (← lowerN? n b (k + 1)) + | .forallE A b, k => do return .forallE (← lowerN? n A k) (← lowerN? n b (k + 1)) + | .proj r i e, k => do return .proj r i (← lowerN? n e k) + | .natLit v, _ => some (.natLit v) + +/-- The domains of a recursive field whose lowered type targets the family at +the parameter instance, after `ordinary` earlier fields. -/ +def recursiveDomains? [DecidableEq β] (source : β) (universes nparams ordinary : Nat) + (domain : VExpr β) : Option (List (VExpr β)) := + match peelAllForalls domain with + | (domains, body) => + if body = familyApp source universes nparams (ordinary + domains.length) then some domains + else none + +def collectRecursive [DecidableEq β] (source : β) (universes nparams ordinary : Nat) : + List (VExpr β) → Nat → Option (List (List (VExpr β))) + | [], _ => some [] + | domain :: rest, previous => do + let lowered ← lowerN? previous domain + let domains ← recursiveDomains? source universes nparams ordinary lowered + let tail ← collectRecursive source universes nparams ordinary rest (previous + 1) + return domains :: tail + +/-- Split field domains into the ordinary prefix and the recursive suffix. -/ +def splitFields [DecidableEq β] (source : β) (universes nparams : Nat) : + List (VExpr β) → List (VExpr β) → Option (ErasedConstructor β) + | [], fields => some ⟨fields, []⟩ + | domain :: rest, fields => + match recursiveDomains? source universes nparams fields.length domain with + | some domains => do + let tail ← collectRecursive source universes nparams fields.length rest 1 + return ⟨fields, domains :: tail⟩ + | none => splitFields source universes nparams rest (fields ++ [domain]) + +/-- Whether an erased term mentions the family being declared. -/ +def mentionsFamily [DecidableEq β] (source : β) (e : VExpr β) : Bool := + e.mentions (.member source 0) + +/-- Read one constructor. The reconstruction is compared with the stored +type, so the split heuristic is not trusted. -/ +def constructorShape? [DecidableEq β] (source : β) (universes : Nat) + (parameters : List (VExpr β)) (type : VExpr β) : Option (ErasedConstructor β) := + match peelForalls? parameters.length type with + | none => none + | some (ps, rest) => + if ps = parameters then + match splitFields source universes parameters.length (peelAllForalls rest).1 [] with + | none => none + | some ctor => + if ctor.type source universes parameters = type then + if ctor.fields.all fun domain => !mentionsFamily source domain then some ctor else none + else none + else none + +theorem constructorShape?_type [DecidableEq β] {source : β} {universes : Nat} + {parameters : List (VExpr β)} {type : VExpr β} {ctor : ErasedConstructor β} + (h : constructorShape? source universes parameters type = some ctor) : + ctor.type source universes parameters = type ∧ + ∀ domain ∈ ctor.fields, mentionsFamily source domain = false := by + unfold constructorShape? at h + split at h + · contradiction + · split at h + · split at h + · contradiction + · split at h + next htype => + split at h + next hfields => + cases h + refine ⟨htype, ?_⟩ + intro domain hd + have := List.all_eq_true.mp hfields domain hd + simpa using this + · contradiction + · contradiction + · contradiction + +theorem mapM_some {α γ : Type _} {f : α → Option γ} {xs : List α} {ys : List γ} + (h : xs.mapM f = some ys) : + ys.length = xs.length ∧ ∀ (i : Nat) (x : α), xs[i]? = some x → ∃ y, ys[i]? = some y ∧ f x = some y := by + induction xs generalizing ys with + | nil => + simp only [List.mapM_nil, pure, Option.some.injEq] at h + subst h + simp + | cons x xs ih => + simp only [List.mapM_cons, bind, Option.bind_eq_some_iff, pure, Option.some.injEq] at h + obtain ⟨y, hy, tail, htail, rfl⟩ := h + obtain ⟨hlen, ih⟩ := ih htail + refine ⟨by simp [hlen], ?_⟩ + intro i x' hx + cases i with + | zero => + simp only [List.getElem?_cons_zero, Option.some.injEq] at hx + subst hx + exact ⟨y, rfl, hy⟩ + | succ i => + simp only [List.getElem?_cons_succ] at hx ⊢ + exact ih i x' hx + +/-- Read the erased singleton shape from the erased family and constructor +types. -/ +def erasedShape? [DecidableEq β] (source : β) (universes nparams : Nat) + (familyType : VExpr β) (constructorTypes : List (VExpr β)) : Option (ErasedShape β) := + match peelForalls? nparams familyType with + | none => none + | some (parameters, .sort level) => + match constructorTypes.mapM (constructorShape? source universes parameters) with + | none => none + | some constructors => some ⟨universes, parameters, level, constructors⟩ + | some (_, _) => none + +theorem erasedShape?_sound [DecidableEq β] {source : β} {universes nparams : Nat} + {familyType : VExpr β} {constructorTypes : List (VExpr β)} {shape : ErasedShape β} + (h : erasedShape? source universes nparams familyType constructorTypes = some shape) : + shape.universes = universes ∧ shape.parameters.length = nparams ∧ + shape.type = familyType ∧ shape.constructors.length = constructorTypes.length ∧ + ∀ (i : Nat) (type : VExpr β), constructorTypes[i]? = some type → + ∃ ctor : ErasedConstructor β, shape.constructors[i]? = some ctor ∧ + ctor.type source universes shape.parameters = type ∧ + ∀ domain ∈ ctor.fields, mentionsFamily source domain = false := by + unfold erasedShape? at h + split at h + · contradiction + next parameters level hpeel => + split at h + · contradiction + next constructors hmap => + cases h + obtain ⟨hfam, hlen⟩ := peelForalls?_forallN hpeel + obtain ⟨hclen, hctors⟩ := mapM_some hmap + refine ⟨rfl, hlen, ?_, hclen, ?_⟩ + · simp [ErasedShape.type, hfam] + · intro i type ht + obtain ⟨ctor, hc, hread⟩ := hctors i type ht + exact ⟨ctor, hc, constructorShape?_type hread⟩ + · contradiction + +/-! ### Default annotation -/ + +/-- The structural annotation whose every binder is never a proposition. It is +exact for binder-free domains; general domains take their conditions from the +retained inference. -/ +def annotateNever : VExpr β → AExpr β + | .bvar i => .bvar i + | .sort l => .sort l + | .const r ls => .const r ls + | .app f a => .app (annotateNever f) (annotateNever a) + | .lam a b => .lam .never (annotateNever a) (annotateNever b) + | .forallE a b => .forallE .never (annotateNever a) (annotateNever b) + | .proj r i e => .proj r i (annotateNever e) + | .natLit v => .natLit v + +@[simp] theorem erase_annotateNever (e : VExpr β) : (annotateNever e).erase = e := by + induction e <;> simp_all [annotateNever, AExpr.erase] + +def ErasedConstructor.annotate (ctor : ErasedConstructor β) : Constructor β := + ⟨ctor.fields.map annotateNever, ctor.recursive.map fun domains => ⟨domains.map annotateNever, []⟩, []⟩ + +def ErasedShape.annotate (shape : ErasedShape β) : Shape β := + ⟨shape.universes, shape.parameters.map annotateNever, [], shape.level, + shape.constructors.map ErasedConstructor.annotate⟩ + +@[simp] theorem eraseConstructor_annotate (ctor : ErasedConstructor β) : + eraseConstructor ctor.annotate = ctor := by + cases ctor + simp [ErasedConstructor.annotate, eraseConstructor, List.map_map, Function.comp_def] + +@[simp] theorem eraseShape_annotate (shape : ErasedShape β) : eraseShape shape.annotate = shape := by + cases shape + simp [ErasedShape.annotate, eraseShape, List.map_map, Function.comp_def] + +theorem annotate_singleton (shape : ErasedShape β) : SingletonShape shape.annotate := by + refine ⟨rfl, ?_⟩ + intro ctor hc + obtain ⟨erased, _, rfl⟩ := List.mem_map.mp hc + refine ⟨rfl, ?_⟩ + intro field hf + obtain ⟨domains, _, rfl⟩ := List.mem_map.mp hf + rfl + +/-! ### Decidable binder-condition bounds -/ + +/-- Executable form of `ConditionsScoped`. -/ +def conditionsScopedCheck (universes : Nat) : AExpr β → Bool + | .app fn arg => conditionsScopedCheck universes fn && conditionsScopedCheck universes arg + | .lam condition domain body | .forallE condition domain body => + decide (condition.WF universes) && conditionsScopedCheck universes domain && + conditionsScopedCheck universes body + | .proj _ _ major => conditionsScopedCheck universes major + | _ => true + +theorem conditionsScopedCheck_sound {universes : Nat} : + ∀ {e : AExpr β}, conditionsScopedCheck universes e = true → ConditionsScoped universes e + | .bvar _, _ | .sort _, _ | .const _ _, _ | .natLit _, _ => trivial + | .app fn arg, h => by + simp only [conditionsScopedCheck, Bool.and_eq_true] at h + exact ⟨conditionsScopedCheck_sound h.1, conditionsScopedCheck_sound h.2⟩ + | .lam condition domain body, h | .forallE condition domain body, h => by + simp only [conditionsScopedCheck, Bool.and_eq_true, decide_eq_true_eq] at h + exact ⟨h.1.1, conditionsScopedCheck_sound h.1.2, conditionsScopedCheck_sound h.2⟩ + | .proj _ _ major, h => conditionsScopedCheck_sound (e := major) h + +/-! ### Certified blocks read from stored kernel declarations -/ + +/-- The certified constructor read from a stored constructor declaration whose +header agrees with its family. -/ +def readCtor? (resolve : Address → Option (ConstRef Address)) (lvls params : UInt64) : + KConst .anon → Option (Ctor Address) + | .ctor (isUnsafe := false) (lvls := clvls) (params := cparams) (fields := fields) (ty := ty) .. => + if clvls = lvls ∧ cparams = params then + match readScopedExpr? resolve [] ty with + | some type => some ⟨lvls.toNat, params.toNat, fields.toNat, type, .safe⟩ + | none => none + else none + | _ => none + +/-- The certified one-member block of a non-indexed safe family with the given +header, type, and constructor declarations. -/ +def familyBlockOf? (resolve : Address → Option (ConstRef Address)) (lvls params : UInt64) + (ty : KExpr .anon) (ctors : List (KConst .anon)) : Option (Block Address) := + match readScopedExpr? resolve [] ty with + | none => none + | some type => + match ctors.mapM (readCtor? resolve lvls params) with + | none => none + | some ctorDecls => some ⟨[.induct lvls.toNat params.toNat 0 type ctorDecls .safe]⟩ + +/-- The certified one-member block read from a stored non-indexed safe family +and its constructor declarations, in constructor order. -/ +def familyBlock? (resolve : Address → Option (ConstRef Address)) : + KConst .anon → List (KConst .anon) → Option (Block Address) + | .indc (lvls := lvls) (params := params) (indices := 0) (isUnsafe := false) (ty := ty) .., ctors => + familyBlockOf? resolve lvls params ty ctors + | _, _ => none + +/-- The certified rule read from a stored recursor rule. -/ +def readRule? (resolve : Address → Option (ConstRef Address)) (rule : Kernel.RecRule .anon) : + Option (Theory.RecRule Address) := + (readScopedExpr? resolve [] rule.rhs).map fun rhs => ⟨rule.fields.toNat, rhs⟩ + +/-- The certified one-member block of a safe recursor with the given header, +type, and rules. -/ +def recursorBlockOf? (resolve : Address → Option (ConstRef Address)) (k : Bool) + (lvls params indices motives minors : UInt64) (ty : KExpr .anon) + (rules : List (Kernel.RecRule .anon)) : Option (Block Address) := + match readScopedExpr? resolve [] ty with + | none => none + | some type => + match rules.mapM (readRule? resolve) with + | none => none + | some ruleDecls => + some ⟨[.recursor lvls.toNat params.toNat indices.toNat motives.toNat minors.toNat + type ruleDecls k .safe]⟩ + +/-- The certified one-member block read from a stored safe recursor. -/ +def recursorBlock? (resolve : Address → Option (ConstRef Address)) : + KConst .anon → Option (Block Address) + | .recr (k := k) (isUnsafe := false) (lvls := lvls) (params := params) (indices := indices) + (motives := motives) (minors := minors) (ty := ty) (rules := rules) .. => + recursorBlockOf? resolve k lvls params indices motives minors ty rules.toList + | _ => none + +/-- The elimination mode determined by the two stored universe arities. -/ +def modeOf (familyUniverses recursorUniverses : UInt64) : Option ElimMode := + if recursorUniverses = familyUniverses then some .small + else if recursorUniverses.toNat = familyUniverses.toNat + 1 then some .large + else none + +theorem modeOf_recUvars {familyUniverses recursorUniverses : UInt64} {mode : ElimMode} + (h : modeOf familyUniverses recursorUniverses = some mode) : + recursorUniverses.toNat = mode.recUvars familyUniverses.toNat := by + unfold modeOf at h + split at h + · cases h; simp_all [ElimMode.recUvars] + · split at h + · cases h; simp_all [ElimMode.recUvars] + · contradiction + +/-- The stored recursor is the certified canonical recursor of the shape. A +`k` flag is accepted only for a supported singleton proposition. -/ +def recursorSourceCheck (resolve : Address → Option (ConstRef Address)) (shape : Shape Address) + (source recursor : Address) (mode : ElimMode) (recr : KConst .anon) : Bool := + match recr with + | .recr (k := k) .. => + decide (recursorBlock? resolve recr = some ⟨[shape.recursorSource source recursor mode k]⟩) && + (!k || decide shape.SupportsK) + | _ => false + +theorem recursorSourceCheck_sound {resolve : Address → Option (ConstRef Address)} + {shape : Shape Address} {source recursor : Address} {mode : ElimMode} {recr : KConst .anon} + {store : Store Address} (check : recursorSourceCheck resolve shape source recursor mode recr = true) + (stored : store.blocks recursor = recursorBlock? resolve recr) : + shape.RecursorSourceMatches store source recursor mode := by + cases recr + case recr name levelParams k isUnsafe lvls params indices motives minors block memberIdx ty rules leanAll => + simp only [recursorSourceCheck, Bool.and_eq_true, decide_eq_true_eq, Bool.or_eq_true] at check + obtain ⟨hblock, hk⟩ := check + unfold Shape.RecursorSourceMatches + rw [stored, hblock] + cases k with + | false => exact Or.inl rfl + | true => + rcases hk with hk | hk + · simp at hk + · exact Or.inr ⟨hk, rfl⟩ + all_goals simp [recursorSourceCheck] at check + +/-- The complete decidable witness check for one singleton block: the family +block reads to the certified source of `shape`, the recursor block reads to the +certified canonical recursor, the shape has no indices, and every binder +condition is bounded by the family's universe arity. -/ +def singletonWitnessCheck (resolve : Address → Option (ConstRef Address)) (source recursor : Address) + (family : KConst .anon) (ctors : List (KConst .anon)) (recr : KConst .anon) + (shape : Shape Address) (mode : ElimMode) : Bool := + decide (familyBlock? resolve family ctors = some ⟨[shape.source source]⟩) && + recursorSourceCheck resolve shape source recursor mode recr && + decide (SingletonShape shape) && + decide (modeOf family.lvls recr.lvls = some mode) && + conditionsScopedCheck shape.universes shape.type && + shape.constructors.all (fun ctor => conditionsScopedCheck shape.universes (ctor.type shape source)) && + shape.constructors.all (fun ctor => + ctor.fields.all (fun A => decide (ConstRef.member source 0 ∉ A.references)) && + ctor.recursive.all fun field => + field.domains.all fun A => decide (ConstRef.member source 0 ∉ A.references)) && + conditionsScopedCheck (mode.recUvars shape.universes) (shape.recursorType source mode) && + shape.constructors.zipIdx.all fun (ctor, i) => + conditionsScopedCheck (mode.recUvars shape.universes) (shape.ruleRhs source recursor mode i ctor) + +theorem singletonWitnessCheck_sound {resolve : Address → Option (ConstRef Address)} + {source recursor : Address} {family : KConst .anon} {ctors : List (KConst .anon)} + {recr : KConst .anon} {shape : Shape Address} {mode : ElimMode} + (check : singletonWitnessCheck resolve source recursor family ctors recr shape mode = true) : + familyBlock? resolve family ctors = some ⟨[shape.source source]⟩ ∧ + recursorSourceCheck resolve shape source recursor mode recr = true ∧ + SingletonShape shape ∧ modeOf family.lvls recr.lvls = some mode ∧ + ConditionsScoped shape.universes shape.type ∧ + (∀ ctor ∈ shape.constructors, ConditionsScoped shape.universes (ctor.type shape source)) ∧ + (∀ ctor ∈ shape.constructors, + (∀ A ∈ ctor.fields, ConstRef.member source 0 ∉ A.references) ∧ + ∀ field ∈ ctor.recursive, ∀ A ∈ field.domains, ConstRef.member source 0 ∉ A.references) ∧ + ConditionsScoped (mode.recUvars shape.universes) (shape.recursorType source mode) ∧ + ∀ (i : Nat) (ctor : Constructor Address), shape.constructors[i]? = some ctor → + ConditionsScoped (mode.recUvars shape.universes) (shape.ruleRhs source recursor mode i ctor) := by + simp only [singletonWitnessCheck, Bool.and_eq_true, decide_eq_true_eq, List.all_eq_true] at check + obtain ⟨⟨⟨⟨⟨⟨⟨⟨hfam, hrec⟩, hsingle⟩, hmode⟩, htype⟩, hctors⟩, hmention⟩, hrecType⟩, hrules⟩ := check + refine ⟨hfam, hrec, hsingle, hmode, conditionsScopedCheck_sound htype, + fun ctor hc => conditionsScopedCheck_sound (hctors ctor hc), ?_, conditionsScopedCheck_sound hrecType, ?_⟩ + · intro ctor hc + obtain ⟨hfields, hrecursive⟩ := hmention ctor hc + exact ⟨hfields, hrecursive⟩ + · intro i ctor hc + exact conditionsScopedCheck_sound (hrules (ctor, i) (List.mk_mem_zipIdx_iff_getElem?.mpr hc)) + +/-- Reading agreement between a stored constructor declaration and the +certified constructor it reads to. -/ +theorem readCtor?_source {resolve : Address → Option (ConstRef Address)} {lvls params : UInt64} + {decl : KConst .anon} {shape : Shape Address} {source : Address} {ctor : Constructor Address} + (h : readCtor? resolve lvls params decl = some (ctor.source shape source)) : + ∃ (cty : KExpr .anon) (fields : UInt64) (induct : KId .anon) (cidx : UInt64) + (cname : Mode.anon.F Name) (clevelParams : Mode.anon.F (Array Name)), + decl = KConst.ctor cname clevelParams false lvls induct cidx params fields cty ∧ + fields.toNat = ctor.fields.length + ctor.recursive.length ∧ + readScopedExpr? resolve [] cty = some (ctor.type shape source).erase := by + unfold readCtor? at h + split at h + next cname clevelParams clvls induct cidx cparams fields cty => + split at h + next hheader => + split at h + next ctype hctype => + simp only [Constructor.source, Option.some.injEq, Ctor.mk.injEq] at h + obtain ⟨_, _, hfields, hctype', _⟩ := h + exact ⟨cty, fields, induct, cidx, cname, clevelParams, by rw [hheader.1, hheader.2], + hfields, by rw [hctype, hctype']⟩ + · contradiction + · contradiction + · contradiction + +theorem familyBlockOf?_family {resolve : Address → Option (ConstRef Address)} {lvls params : UInt64} + {ty : KExpr .anon} {ctors : List (KConst .anon)} {shape : Shape Address} {source : Address} + (h : familyBlockOf? resolve lvls params ty ctors = some ⟨[shape.source source]⟩) : + lvls.toNat = shape.universes ∧ params.toNat = shape.parameters.length ∧ + shape.indices.length = 0 ∧ + readScopedExpr? resolve [] ty = some shape.type.erase ∧ + ctors.length = shape.constructors.length ∧ + ∀ (i : Nat) (ctor : Constructor Address), shape.constructors[i]? = some ctor → + ∃ decl : KConst .anon, ctors[i]? = some decl ∧ + readCtor? resolve lvls params decl = some (ctor.source shape source) := by + unfold familyBlockOf? at h + cases htype : readScopedExpr? resolve [] ty with + | none => simp [htype] at h + | some type => + cases hctors : ctors.mapM (readCtor? resolve lvls params) with + | none => simp [htype, hctors] at h + | some ctorDecls => + simp only [htype, hctors, Option.some.injEq, Block.mk.injEq, List.cons.injEq, and_true, + Shape.source, Const.induct.injEq] at h + obtain ⟨hlvls, hparams, hindices, htyeq, hlist⟩ := h + obtain ⟨hclen, hread⟩ := mapM_some hctors + refine ⟨hlvls, hparams, hindices.symm, by rw [htyeq], ?_, ?_⟩ + · rw [← hclen, hlist, List.length_map] + · intro i ctor hc + have hdecl : ctorDecls[i]? = some (ctor.source shape source) := by + rw [hlist, List.getElem?_map, hc] + rfl + cases hi : ctors[i]? with + | none => + have hlt : i < ctorDecls.length := (List.getElem?_eq_some_iff.mp hdecl).1 + rw [hclen] at hlt + rw [List.getElem?_eq_getElem hlt] at hi + cases hi + | some decl => + obtain ⟨read, hread', hdecl'⟩ := hread i decl hi + rw [hdecl] at hread' + cases hread' + exact ⟨decl, rfl, hdecl'⟩ + +/-- Reading agreement between the stored family declaration and the shape. -/ +theorem familyBlock?_family {resolve : Address → Option (ConstRef Address)} + {family : KConst .anon} {ctors : List (KConst .anon)} {shape : Shape Address} {source : Address} + (h : familyBlock? resolve family ctors = some ⟨[shape.source source]⟩) : + ∃ (lvls params : UInt64) (ty : KExpr .anon) (ctorIds : Array (KId .anon)) (block : KId .anon) + (memberIdx : UInt64) (leanAll : Mode.anon.F (Array (KId .anon))) (name : Mode.anon.F Name) + (levelParams : Mode.anon.F (Array Name)), + family = KConst.indc name levelParams lvls params 0 false block memberIdx ty ctorIds leanAll ∧ + lvls.toNat = shape.universes ∧ params.toNat = shape.parameters.length ∧ + shape.indices.length = 0 ∧ + readScopedExpr? resolve [] ty = some shape.type.erase ∧ + ctors.length = shape.constructors.length ∧ + ∀ (i : Nat) (ctor : Constructor Address), shape.constructors[i]? = some ctor → + ∃ decl : KConst .anon, ctors[i]? = some decl ∧ + readCtor? resolve lvls params decl = some (ctor.source shape source) := by + unfold familyBlock? at h + split at h + next name levelParams lvls params block memberIdx ty ctorIds leanAll => + obtain ⟨hlvls, hparams, hindices, htype, hlen, hctors⟩ := familyBlockOf?_family h + exact ⟨lvls, params, ty, ctorIds, block, memberIdx, leanAll, name, levelParams, rfl, hlvls, + hparams, hindices, htype, hlen, hctors⟩ + · contradiction + +theorem recursorBlockOf?_recursor {resolve : Address → Option (ConstRef Address)} {k : Bool} + {lvls params indices motives minors : UInt64} {ty : KExpr .anon} + {rules : List (Kernel.RecRule .anon)} {block : Block Address} + (h : recursorBlockOf? resolve k lvls params indices motives minors ty rules = some block) : + ∃ type ruleDecls, readScopedExpr? resolve [] ty = some type ∧ + rules.mapM (readRule? resolve) = some ruleDecls ∧ + block = ⟨[.recursor lvls.toNat params.toNat indices.toNat motives.toNat minors.toNat + type ruleDecls k .safe]⟩ := by + unfold recursorBlockOf? at h + cases htype : readScopedExpr? resolve [] ty with + | none => simp [htype] at h + | some type => + cases hrules : rules.mapM (readRule? resolve) with + | none => simp [htype, hrules] at h + | some ruleDecls => + simp only [htype, hrules, Option.some.injEq] at h + exact ⟨type, ruleDecls, rfl, rfl, h.symm⟩ + +end Ix.Kernel.Consistency.Inductive diff --git a/Tests/Ix/Kernel/Inductive.lean b/Tests/Ix/Kernel/Inductive.lean new file mode 100644 index 000000000..93cab1125 --- /dev/null +++ b/Tests/Ix/Kernel/Inductive.lean @@ -0,0 +1,114 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import LSpec +import Ix.Kernel.Verify.Consistency.Inductive.Run +import Ix.Kernel.Verify.Consistency.Resolution +import Tests.Ix.Kernel.IxonFixtures + +/-! +Regressions for the singleton inductive witness: a `Nat`-like family with its +constructors in one block and its canonical recursor in a separate block. The +production checker accepts every member, the erased shape is read from the +stored declarations, and the decidable witness check accepts the annotated +shape against the stored recursor. This file is not a `module` because the +proof library it exercises is not one. +-/ + +namespace Tests.Kernel.Inductive + +open LSpec Ix.Kernel Ix.Kernel.Consistency.Inductive Tests.Kernel.Fixtures +open Ix.Theory Ix.Theory.Model Ix.Theory.Certified.Ordinary Ix.Theory.Inductive + +/-- `N : Sort 1`, `zero : N`, `succ : N → N` in one block; `N.rec` in its own +block, in the canonical form +`∀ (motive : N → Sort u), motive zero → (∀ (n : N), motive n → motive (succ n)) → ∀ (t : N), motive t` +with the canonical rules `λ motive z s, z` and `λ motive z s n, s n (N.rec motive z s n)`. -/ +def natLikeEnv : Ixon.Env × Address × Address := Id.run do + let ind : Ixon.Inductive := + ⟨false, 0, 0, 0, .sort 0, + #[⟨false, 0, 0, 0, 0, .recur 0 #[]⟩, + ⟨false, 0, 1, 0, 1, .leanAll (.recur 0 #[]) (.recur 0 #[])⟩]⟩ + let (env, familyBlock) := storeMutsWithProjs {} ⟨.muts #[.indc ind], #[], #[], #[.succ .zero]⟩ + let nRef : Ixon.Expr := .ref 0 #[] + let zeroRef : Ixon.Expr := .ref 1 #[] + let succRef : Ixon.Expr := .ref 2 #[] + let motiveTy : Ixon.Expr := .leanAll nRef (.sort 0) + let minorZero : Ixon.Expr := .app (.var 0) zeroRef + let minorSucc : Ixon.Expr := + .leanAll nRef (.leanAll (.app (.var 2) (.var 0)) (.app (.var 3) (.app succRef (.var 1)))) + let recTy : Ixon.Expr := + .leanAll motiveTy (.leanAll minorZero (.leanAll minorSucc (.leanAll nRef (.app (.var 3) (.var 0))))) + let ruleZero : Ixon.Expr := .leanLam motiveTy (.leanLam minorZero (.leanLam minorSucc (.var 1))) + let recCall : Ixon.Expr := + .app (.app (.app (.app (.recur 0 #[0]) (.var 3)) (.var 2)) (.var 1)) (.var 0) + let ruleSucc : Ixon.Expr := + .leanLam motiveTy (.leanLam minorZero (.leanLam minorSucc + (.leanLam nRef (.app (.app (.var 1) (.var 0)) recCall)))) + let recr : Ixon.Recursor := + ⟨false, false, 1, 0, 0, 1, 2, recTy, #[⟨0, ruleZero⟩, ⟨1, ruleSucc⟩]⟩ + let (env, recursorBlock) := storeMutsWithProjs env + ⟨.muts #[.recr recr], #[], + #[indcProjAddr familyBlock 0, ctorProjAddr familyBlock 0 0, ctorProjAddr familyBlock 0 1], + #[.var 0]⟩ + return (env, familyBlock, recursorBlock) + +def ingressEnvOf (env : Ixon.Env) : AnonEnv := + match (ingressAll env).run {} with + | .ok _ kenv => kenv + | .error _ _ => {} + +def accepts (env : Ixon.Env) (addr : Address) : Bool := + match (TcM.checkConst (⟨addr, ()⟩ : KId .anon)).run (.ofEnvAnon (ingressEnvOf env)) with + | .ok () _ => true + | .error _ _ => false + +/-- The erased shape read from the stored family and constructor types. -/ +def readShape (env : Ixon.Env) (familyBlock : Address) : Option (ErasedShape Address) := do + let kenv := ingressEnvOf env + let resolve := env.resolve + let family ← kenv.get? ⟨indcProjAddr familyBlock 0, ()⟩ + let zero ← kenv.get? ⟨ctorProjAddr familyBlock 0 0, ()⟩ + let succ ← kenv.get? ⟨ctorProjAddr familyBlock 0 1, ()⟩ + let familyType ← Ix.Kernel.Consistency.readScopedExpr? resolve [] family.ty + let zeroType ← Ix.Kernel.Consistency.readScopedExpr? resolve [] zero.ty + let succType ← Ix.Kernel.Consistency.readScopedExpr? resolve [] succ.ty + erasedShape? familyBlock family.lvls.toNat 0 familyType [zeroType, succType] + +/-- The expected erased shape: no parameters, level `1`, a constructor with no +fields and a constructor with one recursive field. -/ +def expectedShape : ErasedShape Address := + ⟨0, [], .succ .zero, [⟨[], []⟩, ⟨[], [[]]⟩]⟩ + +/-- The decidable witness check on the annotated shape and the stored +recursor, in the given elimination mode. -/ +def witnessAcceptedWith (env : Ixon.Env) (familyBlock recursorBlock : Address) (mode : ElimMode) : Bool := + let kenv := ingressEnvOf env + match kenv.get? ⟨indcProjAddr familyBlock 0, ()⟩, kenv.get? ⟨ctorProjAddr familyBlock 0 0, ()⟩, + kenv.get? ⟨ctorProjAddr familyBlock 0 1, ()⟩, kenv.get? ⟨recrProjAddr recursorBlock 0, ()⟩, + readShape env familyBlock with + | some family, some zero, some succ, some recr, some erased => + singletonWitnessCheck env.resolve familyBlock recursorBlock family [zero, succ] recr + erased.annotate mode + | _, _, _, _, _ => false + +def witnessAccepted (env : Ixon.Env) (familyBlock recursorBlock : Address) : Bool := + witnessAcceptedWith env familyBlock recursorBlock .large + +private def cases : TestSeq := + let (env, familyBlock, recursorBlock) := natLikeEnv + test "singleton inductive: production accepts the family, constructors, and recursor" + (accepts env (indcProjAddr familyBlock 0) && accepts env (ctorProjAddr familyBlock 0 0) && + accepts env (ctorProjAddr familyBlock 0 1) && accepts env (recrProjAddr recursorBlock 0)) + ++ test "singleton inductive: the erased shape reads from the stored declarations" + (decide (readShape env familyBlock = some expectedShape)) + ++ test "singleton inductive: the witness check accepts the annotated shape and stored recursor" + (witnessAccepted env familyBlock recursorBlock) + ++ test "singleton inductive: the witness check rejects the wrong elimination mode" + (!witnessAcceptedWith env familyBlock recursorBlock .small) + +def suite : List TestSeq := [cases] + +end Tests.Kernel.Inductive diff --git a/Tests/Main.lean b/Tests/Main.lean index bad78914c..1d65c582c 100644 --- a/Tests/Main.lean +++ b/Tests/Main.lean @@ -38,6 +38,7 @@ import Tests.Ix.Kernel.Unit import Tests.Ix.Kernel.Substrate import Tests.Ix.Kernel.IxonFixtures import Tests.Ix.Kernel.Resolution +import Tests.Ix.Kernel.Inductive import Tests.Ix.Kernel.WhnfTests import Tests.Ix.Kernel.InferDefEq import Tests.Ix.Kernel.CheckTests @@ -113,6 +114,7 @@ def primarySuites : Std.HashMap String (List LSpec.TestSeq) := .ofList [ ++ Tests.Kernel.Fixtures.suite ++ Tests.Kernel.WhnfTests.suite ++ Tests.Kernel.InferDefEq.suite ++ Tests.Kernel.CheckTests.suite ++ Tests.Kernel.Consistency.suite ++ Tests.Kernel.Resolution.suite + ++ Tests.Kernel.Inductive.suite ++ Tests.Kernel.DefinitionDependencies.suite ++ Tests.Kernel.SafeRecursion.suite ++ Tests.Kernel.Roundtrip.unitTests ++ Tests.Kernel.IngressMeta.unitTests), From 026b02b6e230aa3519b3b92819e67a2718208cf1 Mon Sep 17 00:00:00 2001 From: "John C. Burnham" Date: Tue, 15 Sep 2026 17:21:52 -0400 Subject: [PATCH 63/63] Admit the canonical quotient constants into the model Consistency/Quotient.lean inverts the guard sequence of checkQuot into an executed trace (validation, checkQuot, type inference, sort check): a successful run declares the reserved primitive address of its kind, the fixed universe count, and a type hashing to the canonical kernel type, and Quot.lift ran the Eq/Eq.refl prerequisite, whose facts about the inspected environment are extracted (EqualityPrerequisite). Under address faithfulness of the compared pair, the four canonical kernel types read to the certified quotient description (canonicalQuotType_reads): the production declarations correspond to one certified reference package, whose Quot.sound slot is stood in by the induction reference since production declares Quot.sound as an ordinary axiom. extend_quotients admits the four entries with the certified types and the Quot.lift/Quot.ind computation equations into every model of a closed interface that realizes the equality family, from the model's rule producers (formation of the canonical types) and the certified values. QuotientEnvironmentFragment layers the four accepted standalones on the resolved fragment: checkEnvAnon_preserves_model_quotient, checkEnvAnon_quotient_published, checkEnvAnon_represents_source_quotient, and checkEnvAnon_no_false_quotient, with the Eq entries as the static EqualityBinding premise pending the inductive admission of WP5. DeclarationReading gains the quotient case; tc-unit gains the canonical bundle regressions; the audit gains the quotient root groups. --- Ix/Kernel/Verify/Consistency.lean | 5 + Ix/Kernel/Verify/Consistency/Audit.lean | 98 + Ix/Kernel/Verify/Consistency/Environment.lean | 4 +- Ix/Kernel/Verify/Consistency/Quotient.lean | 1584 +++++++++++++++++ Tests/Ix/Kernel/Quotient.lean | 117 ++ Tests/Main.lean | 2 + 6 files changed, 1808 insertions(+), 2 deletions(-) create mode 100644 Ix/Kernel/Verify/Consistency/Quotient.lean create mode 100644 Tests/Ix/Kernel/Quotient.lean diff --git a/Ix/Kernel/Verify/Consistency.lean b/Ix/Kernel/Verify/Consistency.lean index 9a1fad88e..34349545b 100644 --- a/Ix/Kernel/Verify/Consistency.lean +++ b/Ix/Kernel/Verify/Consistency.lean @@ -17,6 +17,7 @@ import Ix.Kernel.Verify.Consistency.InferenceCache import Ix.Kernel.Verify.Consistency.SortCache import Ix.Kernel.Verify.Consistency.Literals import Ix.Kernel.Verify.Consistency.Resolution +import Ix.Kernel.Verify.Consistency.Quotient import Ix.Kernel.Verify.Consistency.ConstantCache import Ix.Kernel.Verify.Consistency.LazyCache import Ix.Kernel.Verify.Consistency.BlockCache @@ -400,6 +401,10 @@ discipline, and hereditary typing of binder operands. The reducing tiers after the quick probe, from the eager `Bool.true` shortcut through the cheap passes, proof irrelevance, the lazy-delta loop, and the final WHNF tier, are proved modulo the reducer seams, which discharges that reducing-tail obligation. +The four canonical quotient constants are admitted through the guard sequence of +`checkQuot`: the canonical kernel types read as the certified quotient description, and +a fragment containing all four extends every model of an initial interface that +realizes the equality family, publishing the certified quotient entries and equations. General checker soundness remains outside this fragment. Singleton non-indexed inductive blocks are admitted through the certified `Ordinary` witness: the stored family, constructors, and one-member recursor diff --git a/Ix/Kernel/Verify/Consistency/Audit.lean b/Ix/Kernel/Verify/Consistency/Audit.lean index 0797d026d..32863e4cd 100644 --- a/Ix/Kernel/Verify/Consistency/Audit.lean +++ b/Ix/Kernel/Verify/Consistency/Audit.lean @@ -10,6 +10,7 @@ import Ix.Kernel.Verify.Consistency.RecursiveCache import Ix.Kernel.Verify.Consistency.RecursiveState import Ix.Kernel.Verify.Consistency.SourceAgreement import Ix.Kernel.Verify.Consistency.Resolution +import Ix.Kernel.Verify.Consistency.Quotient import Ix.Kernel.Verify.Consistency.SourceCache import Ix.Kernel.Verify.Consistency.Dependencies import Ix.Kernel.Verify.Consistency.CheapBeta @@ -1844,6 +1845,81 @@ private def wp3ReducingAxiomFreeRoots : Array Lean.Name := #[ ``readScopedExpr?_str_none ] +/-- Quotient kinds, the reserved primitive table, the certified reference +package, and the declared production data are pure. -/ +private def quotientPureRoots : Array Lean.Name := #[ + ``Ix.QuotKind.certified, ``Ix.QuotKind.universes, ``Ix.QuotKind.certified_injective, + ``Ix.QuotKind.eq_of_bne_eq_false, ``Ix.QuotKind.eq_lift_of_beq, ``Ix.QuotKind.beq_lift_eq_false, + ``Ix.QuotKind.all, ``Primitives.quot, ``QuotientRefs.certified, ``QuotientRefs.ref, + ``QuotientRefs.Injective, ``QuotientRefs.EqualityApart, ``typeType_scope, ``indType_scope, + ``indRuleLhs_scope, ``indRuleRhs_scope, + ``QuotientSpec.constant, ``QuotientBinding, ``QuotientBinding.quot, ``QuotientPackage.addresses +] + +private def quotientKindRoots : Array Lean.Name := #[ + ``Ix.QuotKind.certified_universes, ``Ix.QuotKind.mem_all +] + +/-- The published quotient interface, its lookups, its syntactic closure, and +the static equality binding reach only the set model's syntax. -/ +private def quotientInterfaceRoots : Array Lean.Name := #[ + ``QuotientRefs.entryType, ``QuotientRefs.entry, ``quotientEnvironment, + ``quotientEnvironment_same, ``quotientEnvironment_old, ``quotientEnvironment_cases, + ``quotientEnvironment_extends, ``Theory.Model.AExpr.ReferencesIn.quotient, + ``Theory.Model.ConstantFact.ReferencesIn.quotient, ``ctorType_scope, ``liftType_scope, + ``liftRuleLhs_scope, ``liftRuleRhs_scope, ``QuotientRefs.entryType_scope, + ``QuotientRefs.equation_scope, + ``QuotientRefs.entryType_references, ``QuotientRefs.equation_references, + ``quotientEnvironment_wf, ``EqualityInterface, ``EqualityBinding, ``QuotientPackage.mem_addresses +] + +/-- Formation of the four canonical quotient types from the model's rule +producers, the certified values installed at the quotient references, and the +model extension by the four constants. -/ +private def quotientModelRoots : Array Lean.Name := #[ + ``typeType_formed, ``ctorType_formed, ``liftType_formed, ``indType_formed, + ``quotientAssignment, ``quotientAssignment_agrees, ``quotientAssignment_reading, + ``extend_quotients, ``PreservesModels.trans +] + +/-- The guard sequence of `checkQuot`, the executed prefix of a quotient member +check, the `Eq`/`Eq.refl` prerequisite facts, and the reading of the canonical +kernel types reach expression and universe construction. -/ +private def quotientExprRoots : Array Lean.Name := #[ + ``checkQuotBody_success, ``checkQuot_success, ``QuotTypeTrace, ``QuotTypeTrace.guards, + ``QuotTypeTrace.equality, ``QuotTypeTrace.synthesisTypeCheck, ``EqualityPrerequisite, + ``checkEqType_success, ``QuotTypeTrace.equalityPrerequisite, ``canonicalQuotType_reads, + ``QuotTypeTrace.reads +] + +/-- The production smart-constructor readers reach expression or universe construction. -/ +private def quotientExprReaderRoots : Array Lean.Name := #[ + ``readExpr?_mkAll, ``readExpr?_mkApp, ``readExpr?_mkVar +] + +private def quotientLevelReaderRoots : Array Lean.Name := #[ + ``readLevel_mkParam +] + +private def quotientPlainReaderRoots : Array Lean.Name := #[ + ``readLevel_mkZero +] + +/-- Quotient constants at work positions and the environment fragment with the +four constants reach the production driver like the resolved fragment. -/ +private def quotientProductionRoots : Array Lean.Name := #[ + ``quot_type_trace, ``QuotientObservation, ``QuotientObservation.guards, + ``QuotientEnvironmentFragment, ``QuotientEnvironmentFragment.published, + ``QuotientEnvironmentFragment.serial_success, ``QuotientEnvironmentFragment.address, + ``QuotientEnvironmentFragment.universes, ``QuotientEnvironmentFragment.injective, + ``QuotientEnvironmentFragment.fresh, ``QuotientEnvironmentFragment.apart_of_present, + ``QuotientEnvironmentFragment.apart, ``QuotientEnvironmentFragment.binding, + ``QuotientEnvironmentFragment.published_wf, ``QuotientEnvironmentFragment.published_canonical, + ``QuotientEnvironmentFragment.atomicPlan, ``checkEnvAnon_preserves_model_quotient, + ``checkEnvAnon_quotient_published, ``checkEnvAnon_represents_source_quotient, + ``checkEnvAnon_no_false_quotient +] + def roots : Array RootAllowance := #[ { root := ``InterfaceExtends.refl, forbiddenDependencies := forbiddenProduction }, { root := ``InterfaceExtends.trans, forbiddenDependencies := forbiddenProduction }, @@ -2141,6 +2217,28 @@ def roots : Array RootAllowance := #[ ++ inductiveProductionRoots.map (fun root => { root, standardAxioms := standard, nativeAxioms := productionNative, forbiddenDependencies := forbiddenProduction }) + ++ quotientPureRoots.map (fun root => { root, forbiddenDependencies := forbiddenProduction }) + ++ quotientKindRoots.map (fun root => { + root, standardAxioms := #[``propext], forbiddenDependencies := forbiddenProduction }) + ++ quotientInterfaceRoots.map (fun root => { + root, standardAxioms := #[``propext, ``Quot.sound], forbiddenDependencies := forbiddenProduction }) + ++ quotientModelRoots.map (fun root => { + root, standardAxioms := standard, forbiddenDependencies := forbiddenProduction }) + ++ quotientExprRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := #[expressionNative, levelNative], + forbiddenDependencies := forbiddenProduction }) + ++ quotientExprReaderRoots.map (fun root => { + root, standardAxioms := #[``propext, ``Classical.choice], nativeAxioms := #[expressionNative], + forbiddenDependencies := forbiddenProduction }) + ++ quotientLevelReaderRoots.map (fun root => { + root, standardAxioms := #[``propext, ``Classical.choice], nativeAxioms := #[levelNative], + forbiddenDependencies := forbiddenProduction }) + ++ quotientPlainReaderRoots.map (fun root => { + root, standardAxioms := #[``propext, ``Classical.choice], + forbiddenDependencies := forbiddenProduction }) + ++ quotientProductionRoots.map (fun root => { + root, standardAxioms := standard, nativeAxioms := productionNative, + forbiddenDependencies := forbiddenProduction }) run_cmd Kernel.Verify.Audit.check roots diff --git a/Ix/Kernel/Verify/Consistency/Environment.lean b/Ix/Kernel/Verify/Consistency/Environment.lean index 7b5d2790a..01f90e533 100644 --- a/Ix/Kernel/Verify/Consistency/Environment.lean +++ b/Ix/Kernel/Verify/Consistency/Environment.lean @@ -446,13 +446,13 @@ theorem checkEnvAnon_atomic_preserves_model {β : Type u} [DecidableEq β] fragment.plan.sound wellFormed (fragment.serial_success accepted succeeded) /-- Exact type, universe arity, and value agreement with the declaration -returned by production lookup. Axiom entries contain no body. -/ +returned by production lookup. Axiom and quotient entries contain no body. -/ def DeclarationReading {β : Type u} (resolve : Address → Option (ConstRef β)) (concrete : KConst .anon) (entry : ConstantEntry β) : Prop := readExpr? resolve concrete.ty = some entry.type.erase ∧ entry.universes = concrete.lvls.toNat ∧ match concrete with - | .axio .. => entry.body = none + | .axio .. | .quot .. => entry.body = none | .defn (val := value) .. => ∃ body, entry.body = some body ∧ readExpr? resolve value = some body.erase | _ => False diff --git a/Ix/Kernel/Verify/Consistency/Quotient.lean b/Ix/Kernel/Verify/Consistency/Quotient.lean new file mode 100644 index 000000000..9ce827d3c --- /dev/null +++ b/Ix/Kernel/Verify/Consistency/Quotient.lean @@ -0,0 +1,1584 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import Ix.Kernel.Verify.Consistency.Resolution +import Ix.Theory.Certified.Quotient.Publish + +/-! +# Admission of the canonical quotient constants + +Production accepts a `.quot` declaration only through `checkQuot`: the +declared address must be the reserved primitive address of its kind, the +universe count must be the fixed one, the declared type must hash to the +canonical type installed by Lean's `Environment.addQuot`, and `Quot.lift` +additionally requires the exact `Eq`/`Eq.refl` prerequisites. This module +inverts that guard sequence (`quot_type_trace`, `checkQuot_success`, +`checkEqType_success`), reads the four canonical kernel types to the certified +quotient description (`canonicalQuotType_reads`), and admits the four constants +into the set model with the certified quotient semantics (`extend_quotients`): +the published entries carry the certified types and the `Quot.lift`/`Quot.ind` +computation equations, and every model of the preceding interface extends to +them while keeping its old interpretations. + +The certified package (`Ix.Theory.Certified.Quotient`) also names the +soundness axiom `Quot.sound`; production declares it as an ordinary axiom, not +as a quotient primitive, so it is outside this admission, and the certified +reading is instantiated with the induction reference standing in for it (both +denote the point). The `Eq` family is an inductive block, whose admission is +not yet refined in this library: its three model entries with the canonical +equality types are an explicit static binding premise (`EqualityBinding`), to +be discharged by the inductive admission of WP5. The environment fragment +`QuotientEnvironmentFragment` restates the environment theorems for runs whose +initial interface already contains that binding and whose work contains the +four accepted quotient standalones. +-/ + +namespace Ix.QuotKind + +/-- The certified kind of a production quotient primitive. -/ +def certified : Ix.QuotKind → Ix.Theory.Certified.Quotient.Kind + | .type => .type + | .ctor => .ctor + | .lift => .lift + | .ind => .ind + +/-- The universe count required by `checkQuot`. -/ +def universes : Ix.QuotKind → UInt64 + | .lift => 2 + | .type | .ctor | .ind => 1 + +theorem certified_universes (kind : Ix.QuotKind) : + kind.certified.universes = kind.universes.toNat := by + cases kind <;> rfl + +theorem certified_injective {left right : Ix.QuotKind} (same : left.certified = right.certified) : + left = right := by + cases left <;> cases right <;> first | rfl | cases same + +theorem eq_of_bne_eq_false {left right : Ix.QuotKind} (h : (left != right) = false) : + left = right := by + revert h + cases left <;> cases right <;> decide + +theorem eq_lift_of_beq {kind : Ix.QuotKind} (h : (kind == .lift) = true) : kind = .lift := by + revert h + cases kind <;> decide + +theorem beq_lift_eq_false {kind : Ix.QuotKind} (h : kind ≠ .lift) : (kind == .lift) = false := by + revert h + cases kind <;> decide + +end Ix.QuotKind + +namespace Ix.Kernel + +/-- The reserved primitive identifier of each quotient kind. -/ +def Primitives.quot {m : Mode} (p : Primitives m) : Ix.QuotKind → KId m + | .type => p.quotType + | .ctor => p.quotCtor + | .lift => p.quotLift + | .ind => p.quotInd + +end Ix.Kernel + +namespace Ix.Kernel.Consistency + +open Theory Theory.Model Theory.Model.SetTheory Theory.Certified + +universe u v + +/-! ### Certified quotient references -/ + +/-- The model references of the four quotient primitives together with the +equality family, its reflexivity constructor, and its recursor, which the +certified quotient semantics reads. -/ +structure QuotientRefs (β : Type u) where + type : ConstRef β + ctor : ConstRef β + lift : ConstRef β + ind : ConstRef β + eq : ConstRef β + eqRefl : ConstRef β + eqRec : ConstRef β + +namespace QuotientRefs + +variable {β : Type u} + +/-- The certified reference package. Production declares `Quot.sound` as an +ordinary axiom rather than a quotient primitive, so no soundness entry is +published here; the induction reference stands in for it in the certified +reading, since both denote the point. -/ +def certified (refs : QuotientRefs β) : Quotient.Refs β := + { eq := refs.eq, type := refs.type, ctor := refs.ctor, lift := refs.lift, ind := refs.ind, + eqRefl := refs.eqRefl, eqRec := refs.eqRec, sound := refs.ind } + +/-- The reference of a production quotient kind. -/ +def ref (refs : QuotientRefs β) (kind : Ix.QuotKind) : ConstRef β := + refs.certified.ref kind.certified + +/-- The certified type of a production quotient kind. -/ +def entryType (refs : QuotientRefs β) (kind : Ix.QuotKind) : AExpr β := + refs.certified.entryType kind.certified + +/-- The published entry of a production quotient kind: the certified type, +universe count, no value, the `Quot.lift`/`Quot.ind` computation equation, and +no facts. -/ +def entry (refs : QuotientRefs β) (kind : Ix.QuotKind) : ConstantEntry β := + refs.certified.entry kind.certified + +@[simp] theorem ref_type (refs : QuotientRefs β) : refs.ref .type = refs.type := rfl +@[simp] theorem ref_ctor (refs : QuotientRefs β) : refs.ref .ctor = refs.ctor := rfl +@[simp] theorem ref_lift (refs : QuotientRefs β) : refs.ref .lift = refs.lift := rfl +@[simp] theorem ref_ind (refs : QuotientRefs β) : refs.ref .ind = refs.ind := rfl + +@[simp] theorem entry_universes (refs : QuotientRefs β) (kind : Ix.QuotKind) : + (refs.entry kind).universes = kind.universes.toNat := + kind.certified_universes + +@[simp] theorem entry_type (refs : QuotientRefs β) (kind : Ix.QuotKind) : + (refs.entry kind).type = refs.entryType kind := rfl + +@[simp] theorem entry_body (refs : QuotientRefs β) (kind : Ix.QuotKind) : + (refs.entry kind).body = none := rfl + +@[simp] theorem entry_facts (refs : QuotientRefs β) (kind : Ix.QuotKind) : + (refs.entry kind).facts = [] := rfl + +theorem entryType_type (refs : QuotientRefs β) : refs.entryType .type = Quotient.typeType := rfl +theorem entryType_ctor (refs : QuotientRefs β) : + refs.entryType .ctor = Quotient.ctorType refs.certified := rfl +theorem entryType_lift (refs : QuotientRefs β) : + refs.entryType .lift = Quotient.liftType refs.certified := rfl +theorem entryType_ind (refs : QuotientRefs β) : + refs.entryType .ind = Quotient.indType refs.certified := rfl + +/-- The four quotient references are pairwise distinct. -/ +def Injective (refs : QuotientRefs β) : Prop := Function.Injective refs.ref + +/-- The equality references are outside the four quotient references. -/ +structure EqualityApart (refs : QuotientRefs β) : Prop where + eq : ∀ kind, refs.ref kind ≠ refs.eq + eqRefl : ∀ kind, refs.ref kind ≠ refs.eqRefl + eqRec : ∀ kind, refs.ref kind ≠ refs.eqRec + +end QuotientRefs + +/-! ### The published environment -/ + +/-- The preceding interface extended by the four quotient entries. -/ +def quotientEnvironment {β : Type u} [DecidableEq β] (refs : QuotientRefs β) + (entries : Model.Environment β) : Model.Environment β := + (((entries.insert refs.type (refs.entry .type)).insert refs.ctor (refs.entry .ctor)).insert + refs.lift (refs.entry .lift)).insert refs.ind (refs.entry .ind) + +section Published + +variable {β : Type u} [DecidableEq β] {refs : QuotientRefs β} {entries : Model.Environment β} + +theorem quotientEnvironment_same (injective : refs.Injective) (kind : Ix.QuotKind) : + quotientEnvironment refs entries (refs.ref kind) = some (refs.entry kind) := by + have ne (left right : Ix.QuotKind) (different : left ≠ right) : refs.ref left ≠ refs.ref right := + fun same => different (injective same) + have typeInd : refs.type ≠ refs.ind := ne .type .ind (by decide) + have typeLift : refs.type ≠ refs.lift := ne .type .lift (by decide) + have typeCtor : refs.type ≠ refs.ctor := ne .type .ctor (by decide) + have ctorInd : refs.ctor ≠ refs.ind := ne .ctor .ind (by decide) + have ctorLift : refs.ctor ≠ refs.lift := ne .ctor .lift (by decide) + have liftInd : refs.lift ≠ refs.ind := ne .lift .ind (by decide) + cases kind with + | type => simp [quotientEnvironment, Model.Environment.insert, typeInd, typeLift, typeCtor] + | ctor => simp [quotientEnvironment, Model.Environment.insert, ctorInd, ctorLift] + | lift => simp [quotientEnvironment, Model.Environment.insert, liftInd] + | ind => simp [quotientEnvironment, Model.Environment.insert] + +theorem quotientEnvironment_old (fresh : ∀ kind, entries (refs.ref kind) = none) + {r : ConstRef β} {entry : ConstantEntry β} (present : entries r = some entry) : + quotientEnvironment refs entries r = some entry := by + have notInd : r ≠ refs.ind := fresh_ne (fresh .ind) present + have notLift : r ≠ refs.lift := fresh_ne (fresh .lift) present + have notCtor : r ≠ refs.ctor := fresh_ne (fresh .ctor) present + have notType : r ≠ refs.type := fresh_ne (fresh .type) present + simp only [quotientEnvironment, Model.Environment.insert, if_neg notInd, if_neg notLift, + if_neg notCtor, if_neg notType, present] + +theorem quotientEnvironment_cases {r : ConstRef β} {entry : ConstantEntry β} + (present : quotientEnvironment refs entries r = some entry) : + entries r = some entry ∨ ∃ kind, r = refs.ref kind ∧ entry = refs.entry kind := by + simp only [quotientEnvironment, Model.Environment.insert] at present + split at present + · exact .inr ⟨.ind, ‹_›, (Option.some.inj present).symm⟩ + split at present + · exact .inr ⟨.lift, ‹_›, (Option.some.inj present).symm⟩ + split at present + · exact .inr ⟨.ctor, ‹_›, (Option.some.inj present).symm⟩ + split at present + · exact .inr ⟨.type, ‹_›, (Option.some.inj present).symm⟩ + · exact .inl present + +theorem quotientEnvironment_extends (fresh : ∀ kind, entries (refs.ref kind) = none) : + ∀ r entry, entries r = some entry → quotientEnvironment refs entries r = some entry := + fun _ _ present => quotientEnvironment_old fresh present + +theorem _root_.Ix.Theory.Model.AExpr.ReferencesIn.quotient {e : AExpr β} + (references : e.ReferencesIn entries) : + e.ReferencesIn (quotientEnvironment refs entries) := + references.insert.insert.insert.insert + +theorem _root_.Ix.Theory.Model.ConstantFact.ReferencesIn.quotient {fact : ConstantFact β} + (references : fact.ReferencesIn entries) : + fact.ReferencesIn (quotientEnvironment refs entries) := + references.insert.insert.insert.insert + +end Published + +/-! ### Syntactic closure of the certified quotient syntax -/ + +section Closure + +variable {β : Type u} + +theorem typeType_scope : (Quotient.typeType : AExpr β).Scope 1 0 := of_decide_eq_true rfl +theorem ctorType_scope (refs : Quotient.Refs β) : (Quotient.ctorType refs).Scope 1 0 := + of_decide_eq_true rfl +theorem liftType_scope (refs : Quotient.Refs β) : (Quotient.liftType refs).Scope 2 0 := + of_decide_eq_true rfl +theorem indType_scope (refs : Quotient.Refs β) : (Quotient.indType refs).Scope 1 0 := + of_decide_eq_true rfl +theorem liftRuleLhs_scope (refs : Quotient.Refs β) : (Quotient.liftRuleLhs refs).Scope 2 0 := + of_decide_eq_true rfl +theorem liftRuleRhs_scope (refs : Quotient.Refs β) : (Quotient.liftRuleRhs refs).Scope 2 0 := + of_decide_eq_true rfl +theorem indRuleLhs_scope (refs : Quotient.Refs β) : (Quotient.indRuleLhs refs).Scope 1 0 := + of_decide_eq_true rfl +theorem indRuleRhs_scope (refs : Quotient.Refs β) : (Quotient.indRuleRhs refs).Scope 1 0 := + of_decide_eq_true rfl + +theorem QuotientRefs.entryType_scope (refs : QuotientRefs β) (kind : Ix.QuotKind) : + (refs.entryType kind).Scope kind.universes.toNat 0 := by + cases kind with + | type => exact typeType_scope + | ctor => exact ctorType_scope _ + | lift => exact liftType_scope _ + | ind => exact indType_scope _ + +theorem QuotientRefs.equation_scope (refs : QuotientRefs β) (kind : Ix.QuotKind) + {law : ConstantEquation β} (listed : law ∈ (refs.entry kind).equations) : + law.lhs.Scope kind.universes.toNat 0 ∧ law.rhs.Scope kind.universes.toNat 0 := by + cases kind with + | type | ctor => cases listed + | lift => + cases List.mem_singleton.mp listed + exact ⟨liftRuleLhs_scope _, liftRuleRhs_scope _⟩ + | ind => + cases List.mem_singleton.mp listed + exact ⟨indRuleLhs_scope _, indRuleRhs_scope _⟩ + +/-- The references of the published types and equations: only the four +quotient references and the equality family. -/ +theorem QuotientRefs.entryType_references (refs : QuotientRefs β) (kind : Ix.QuotKind) + {entries : Model.Environment β} (type : (entries refs.type).isSome = true) + (ctor : (entries refs.ctor).isSome = true) (eq : (entries refs.eq).isSome = true) : + (refs.entryType kind).ReferencesIn entries := by + intro r member + cases kind <;> simp only [QuotientRefs.entryType, Quotient.Refs.entryType, Ix.QuotKind.certified, + Quotient.typeType, Quotient.ctorType, Quotient.liftType, Quotient.indType, + Quotient.liftPrefix, Quotient.indPrefix, Quotient.invariantType, Quotient.relationType, + Quotient.applied, Quotient.constructed, Basis.Equality.applied, AExpr.forallN, AExpr.appN, + AExpr.references, List.mem_append, List.mem_singleton, List.not_mem_nil, or_false, false_or] + at member + · rcases member with rfl + exact type + · rcases member with rfl | rfl + · exact eq + · exact type + · rcases member with rfl | rfl | rfl + · exact type + · exact ctor + · exact type + +theorem QuotientRefs.equation_references (refs : QuotientRefs β) (kind : Ix.QuotKind) + {entries : Model.Environment β} (type : (entries refs.type).isSome = true) + (ctor : (entries refs.ctor).isSome = true) (lift : (entries refs.lift).isSome = true) + (ind : (entries refs.ind).isSome = true) (eq : (entries refs.eq).isSome = true) + {law : ConstantEquation β} (listed : law ∈ (refs.entry kind).equations) : + law.lhs.ReferencesIn entries ∧ law.rhs.ReferencesIn entries := by + cases kind with + | type | ctor => cases listed + | lift => + cases List.mem_singleton.mp listed + constructor + · intro r member + simp only [Quotient.liftRuleLhs, Quotient.liftRuleBinders, Quotient.liftPrefix, + Quotient.invariantType, Quotient.relationType, Quotient.constructed, + Basis.Equality.applied, AExpr.lamN, AExpr.appN, AExpr.references, List.cons_append, + List.nil_append, List.mem_cons, List.not_mem_nil, or_false] at member + rcases member with rfl | rfl | rfl + · exact eq + · exact lift + · exact ctor + · intro r member + simp only [Quotient.liftRuleRhs, Quotient.liftRuleBinders, Quotient.liftPrefix, + Quotient.invariantType, Quotient.relationType, Basis.Equality.applied, AExpr.lamN, + AExpr.appN, AExpr.references, List.cons_append, List.nil_append, List.mem_cons, + List.not_mem_nil, or_false] at member + rcases member with rfl + exact eq + | ind => + cases List.mem_singleton.mp listed + constructor + · intro r member + simp only [Quotient.indRuleLhs, Quotient.indRuleBinders, Quotient.indPrefix, + Quotient.relationType, Quotient.applied, Quotient.constructed, AExpr.lamN, AExpr.appN, + AExpr.references, List.cons_append, List.nil_append, List.mem_cons, List.not_mem_nil, + or_false] at member + rcases member with rfl | rfl | rfl | rfl + · exact type + · exact ctor + · exact ind + · exact ctor + · intro r member + simp only [Quotient.indRuleRhs, Quotient.indRuleBinders, Quotient.indPrefix, + Quotient.relationType, Quotient.applied, Quotient.constructed, AExpr.lamN, AExpr.appN, + AExpr.references, List.cons_append, List.nil_append, List.mem_cons, List.not_mem_nil, + or_false] at member + rcases member with rfl | rfl + · exact type + · exact ctor + +end Closure + +section WellFormed + +variable {β : Type u} [DecidableEq β] {refs : QuotientRefs β} {entries : Model.Environment β} + +private theorem published_isSome (injective : refs.Injective) (kind : Ix.QuotKind) : + (quotientEnvironment refs entries (refs.ref kind)).isSome = true := by + rw [quotientEnvironment_same injective kind] + rfl + +/-- The published interface is syntactically closed whenever the preceding +one is: the certified syntax is scoped, and it references only the four +quotient references and the admitted equality family. -/ +theorem quotientEnvironment_wf (wellFormed : entries.WF) (injective : refs.Injective) + (fresh : ∀ kind, entries (refs.ref kind) = none) + (equality : (entries refs.eq).isSome = true) : + (quotientEnvironment refs entries).WF := by + have eqPresent : (quotientEnvironment refs entries refs.eq).isSome = true := by + cases present : entries refs.eq with + | none => rw [present] at equality; cases equality + | some entry => rw [quotientEnvironment_old fresh present]; rfl + have closed : ∀ r entry, quotientEnvironment refs entries r = some entry → + EntryClosed (quotientEnvironment refs entries) entry := by + intro r entry present + rcases quotientEnvironment_cases present with old | ⟨kind, _, rfl⟩ + · exact ⟨wellFormed.typeScope _ _ old, wellFormed.bodyScope _ _ old, + (wellFormed.typeReferences _ _ old).quotient, + fun body hb => (wellFormed.bodyReferences _ _ old body hb).quotient, + wellFormed.equationScope _ _ old, + fun law hl => ⟨(wellFormed.equationReferences _ _ old law hl).1.quotient, + (wellFormed.equationReferences _ _ old law hl).2.quotient⟩, + wellFormed.factScope _ _ old, + fun fact hf => (wellFormed.factReferences _ _ old fact hf).quotient⟩ + · refine ⟨?_, (fun body hb => by cases hb), ?_, (fun body hb => by cases hb), ?_, ?_, + (fun fact hf => by cases hf), (fun fact hf => by cases hf)⟩ + · simpa only [QuotientRefs.entry_universes, QuotientRefs.entry_type] using + refs.entryType_scope kind + · simpa only [QuotientRefs.entry_type] using refs.entryType_references kind + (published_isSome injective .type) (published_isSome injective .ctor) eqPresent + · intro law listed + simpa only [QuotientRefs.entry_universes] using refs.equation_scope kind listed + · intro law listed + exact refs.equation_references kind (published_isSome injective .type) + (published_isSome injective .ctor) (published_isSome injective .lift) + (published_isSome injective .ind) eqPresent listed + exact ⟨fun r e h => (closed r e h).typeScope, fun r e h => (closed r e h).bodyScope, + fun r e h => (closed r e h).typeReferences, fun r e h => (closed r e h).bodyReferences, + fun r e h => (closed r e h).equationScope, fun r e h => (closed r e h).equationReferences, + fun r e h => (closed r e h).factScope, fun r e h => (closed r e h).factReferences⟩ + +end WellFormed + +/-! ### Formation of the canonical quotient types + +The certified validator establishes formation of these fixed types by running +its checker on a witness. Here the same typing claims are derived once, from +the model's rule producers, so no witness or validator run is needed. -/ + +section Formation + +open Quotient + +variable {β : Type u} {E : Model.Environment β} {refs : Refs β} + +/-- `α → α → Prop` in a context whose most recent binder is a sort. -/ +private theorem relationType_typing (Γ : Context β) (l : VLevel) : + TypingClaim.{u,v} E (.sort l :: Γ) relationType + (.sort (.imax l (.imax l (.succ .zero)))) := by + apply TypingClaim.forallE (b := .imax l (.succ .zero)) (TypingClaim.bvar rfl) ?_ rfl + exact TypingClaim.forallE (b := .succ .zero) (TypingClaim.bvar rfl) (TypingClaim.sort _) rfl + +/-- Application with the result type supplied separately, so that expected-type +propagation is not blocked by an unevaluated substitution. -/ +private theorem app_typing {Γ : Context β} {f a A B C : AExpr β} {p : PropWhen} + (hf : TypingClaim.{u,v} E Γ f (.forallE p A B)) (ha : TypingClaim.{u,v} E Γ a A) + (result : B.inst a = C) : TypingClaim.{u,v} E Γ (.app f a) C := + result ▸ TypingClaim.app hf ha + +theorem typeType_formed : + ∃ level, TypingClaim.{u,v} E [] (typeType : AExpr β) (.sort level) := by + constructor + apply TypingClaim.forallE (TypingClaim.sort _) + · exact TypingClaim.forallE (b := .succ (.param 0)) (relationType_typing [] (.param 0)) + (TypingClaim.sort _) rfl + · rfl + +/-- The quotient former applied to a sort and a relation. The relation's +context entry is stated in the form produced by the application rule, so the +lookups are closed computations at each use. -/ +private theorem applied_typing (hT : E.HasType refs.type 1 typeType) {Γ : Context β} + {carrier relation : Nat} {sortLevel : VLevel} (hc : Γ[carrier]? = some (.sort sortLevel)) + (hr : Γ[relation]? = some ((relationType.instL [sortLevel]).inst (.bvar carrier))) : + TypingClaim.{u,v} E Γ (applied refs sortLevel (.bvar carrier) (.bvar relation)) + (.sort sortLevel) := by + obtain ⟨entry, he, hn, ht⟩ := hT + have c := TypingClaim.const (Γ := Γ) (ls := [sortLevel]) he (by simp [hn]) + rw [ht] at c + have a1 := TypingClaim.app c (TypingClaim.bvar hc) + have a2 := TypingClaim.app a1 (TypingClaim.bvar hr) + exact a2 + +theorem ctorType_formed (hT : E.HasType refs.type 1 typeType) : + ∃ level, TypingClaim.{u,v} E [] (ctorType refs) (.sort level) := by + constructor + apply TypingClaim.forallE (TypingClaim.sort _) + · apply TypingClaim.forallE (relationType_typing [] (.param 0)) + · apply TypingClaim.forallE (TypingClaim.bvar rfl) + · exact applied_typing (carrier := 2) (relation := 1) hT rfl rfl + · rfl + · rfl + · rfl + +/-- The equality family applied to a sort and two of its elements, with the +element types in the form produced by the application rule. -/ +private theorem equality_typing (hE : E.HasType refs.eq 1 Basis.Equality.type) {Γ : Context β} + {carrier : Nat} {sortLevel : VLevel} {left right : AExpr β} + (hc : Γ[carrier]? = some (.sort sortLevel)) + (hl : TypingClaim.{u,v} E Γ left (((AExpr.bvar 0).instL [sortLevel]).inst (.bvar carrier))) + (hr : TypingClaim.{u,v} E Γ right + ((((AExpr.bvar 1).instL [sortLevel]).inst (.bvar carrier) 1).inst left)) : + TypingClaim.{u,v} E Γ (Basis.Equality.applied refs.eq sortLevel (.bvar carrier) left right) + (.sort .zero) := by + obtain ⟨entry, he, hn, ht⟩ := hE + have c := TypingClaim.const (Γ := Γ) (ls := [sortLevel]) he (by simp [hn]) + rw [ht] at c + have a1 := TypingClaim.app c (TypingClaim.bvar hc) + have a2 := TypingClaim.app a1 hl + have a3 := TypingClaim.app a2 hr + exact a3 + +theorem liftType_formed (hT : E.HasType refs.type 1 typeType) + (hE : E.HasType refs.eq 1 Basis.Equality.type) : + ∃ level, TypingClaim.{u,v} E [] (liftType refs) (.sort level) := by + constructor + apply TypingClaim.forallE (TypingClaim.sort _) + · apply TypingClaim.forallE (relationType_typing [] (.param 0)) + · apply TypingClaim.forallE (TypingClaim.sort _) + · apply TypingClaim.forallE + · exact TypingClaim.forallE (b := .param 1) (TypingClaim.bvar rfl) (TypingClaim.bvar rfl) + rfl + · apply TypingClaim.forallE + · -- Invariance of `f` under `R`, in the context `[f, B, R, A]`. + apply TypingClaim.forallE (TypingClaim.bvar rfl) + · apply TypingClaim.forallE (TypingClaim.bvar rfl) + · apply TypingClaim.forallE + · exact app_typing (app_typing (TypingClaim.bvar rfl) + (TypingClaim.bvar rfl) rfl) (TypingClaim.bvar rfl) rfl + · exact equality_typing (carrier := 4) hE rfl + (app_typing (TypingClaim.bvar rfl) (TypingClaim.bvar rfl) rfl) + (app_typing (TypingClaim.bvar rfl) (TypingClaim.bvar rfl) rfl) + · rfl + · rfl + · rfl + · -- The result `Quot A R → B`, in the context `[h, f, B, R, A]`. + apply TypingClaim.forallE + · exact applied_typing (carrier := 4) (relation := 3) hT rfl rfl + · exact TypingClaim.bvar rfl + · rfl + · rfl + · rfl + · rfl + · rfl + · rfl + +theorem indType_formed (hT : E.HasType refs.type 1 typeType) + (hC : E.HasType refs.ctor 1 (ctorType refs)) : + ∃ level, TypingClaim.{u,v} E [] (indType refs) (.sort level) := by + obtain ⟨entry, he, hn, ht⟩ := hC + constructor + apply TypingClaim.forallE (TypingClaim.sort _) + · apply TypingClaim.forallE (relationType_typing [] (.param 0)) + · -- The motive domain `Quot A R → Prop`, in the context `[R, A]`. + apply TypingClaim.forallE + · exact TypingClaim.forallE (b := .succ .zero) + (applied_typing (carrier := 1) (relation := 0) hT rfl rfl) (TypingClaim.sort _) rfl + · -- The minor premise `∀ a, β (Quot.mk r a)`, in the context `[β, R, A]`. + apply TypingClaim.forallE + · apply TypingClaim.forallE (TypingClaim.bvar rfl) + · have c := TypingClaim.const (Γ := Context.push (.bvar 2) (Context.push + (.forallE .never (applied refs (.param 0) (.bvar 1) (.bvar 0)) (.sort .zero)) + (Context.push relationType (Context.push (.sort (.param 0)) [])))) + (ls := [.param 0]) he (by simp [hn]) + rw [ht] at c + have mk := TypingClaim.app (TypingClaim.app (TypingClaim.app c + (TypingClaim.bvar (i := 3) rfl)) (TypingClaim.bvar (i := 2) rfl)) + (TypingClaim.bvar (i := 0) rfl) + exact app_typing (TypingClaim.bvar rfl) mk rfl + · rfl + · -- The conclusion `∀ q, β q`, in the context `[mk, β, R, A]`. + apply TypingClaim.forallE + · exact applied_typing (carrier := 3) (relation := 2) hT rfl rfl + · exact app_typing (TypingClaim.bvar rfl) (TypingClaim.bvar rfl) rfl + · rfl + · rfl + · rfl + · rfl + · rfl + +end Formation + +/-! ### Realization -/ + +section Realization + +open Quotient + +variable {β : Type u} [DecidableEq β] {V : Type v} [SetTheory V] + +/-- The certified quotient values installed at the four references; every +other reference keeps its value. -/ +noncomputable def quotientAssignment (refs : QuotientRefs β) (constants : Assignment β V) : + Assignment β V := + (((constants.insert refs.type (fun levels => formerValue (levels.getD 0 0))).insert refs.ctor + (fun levels => constructorValue (levels.getD 0 0))).insert refs.lift + (fun levels => liftValue constants refs.eq (levels.getD 0 0) (levels.getD 1 0))).insert + refs.ind (fun _ => pt) + +variable {refs : QuotientRefs β} {entries : Model.Environment β} + +theorem quotientAssignment_agrees (fresh : ∀ kind, entries (refs.ref kind) = none) + (constants : Assignment β V) : + Assignment.AgreesOn entries constants (quotientAssignment refs constants) := by + intro r entry present levels + have notInd : r ≠ refs.ind := fresh_ne (fresh .ind) present + have notLift : r ≠ refs.lift := fresh_ne (fresh .lift) present + have notCtor : r ≠ refs.ctor := fresh_ne (fresh .ctor) present + have notType : r ≠ refs.type := fresh_ne (fresh .type) present + simp only [quotientAssignment, Assignment.insert, if_neg notInd, if_neg notLift, if_neg notCtor, + if_neg notType] + +/-- The installed values are the certified reading of the package. -/ +theorem quotientAssignment_reading (injective : refs.Injective) (apart : refs.EqualityApart) + (constants : Assignment β V) : + Reading (quotientAssignment refs constants) refs.certified := by + have ne (left right : Ix.QuotKind) (different : left ≠ right) : refs.ref left ≠ refs.ref right := + fun same => different (injective same) + have typeInd : refs.type ≠ refs.ind := ne .type .ind (by decide) + have typeLift : refs.type ≠ refs.lift := ne .type .lift (by decide) + have typeCtor : refs.type ≠ refs.ctor := ne .type .ctor (by decide) + have ctorInd : refs.ctor ≠ refs.ind := ne .ctor .ind (by decide) + have ctorLift : refs.ctor ≠ refs.lift := ne .ctor .lift (by decide) + have liftInd : refs.lift ≠ refs.ind := ne .lift .ind (by decide) + have eqInd : refs.eq ≠ refs.ind := (apart.eq .ind).symm + have eqLift : refs.eq ≠ refs.lift := (apart.eq .lift).symm + have eqCtor : refs.eq ≠ refs.ctor := (apart.eq .ctor).symm + have eqType : refs.eq ≠ refs.type := (apart.eq .type).symm + have eqValue (levels : List Nat) : quotientAssignment refs constants refs.eq levels = + constants refs.eq levels := by + simp only [quotientAssignment, Assignment.insert, if_neg eqInd, if_neg eqLift, if_neg eqCtor, + if_neg eqType] + constructor + · intro u + simp only [QuotientRefs.certified, quotientAssignment, Assignment.insert, if_neg typeInd, + if_neg typeLift, if_neg typeCtor, if_true, List.getD_cons_zero] + · intro u + simp only [QuotientRefs.certified, quotientAssignment, Assignment.insert, if_neg ctorInd, + if_neg ctorLift, if_true, List.getD_cons_zero] + · intro u w + have lhs : quotientAssignment refs constants refs.lift [u, w] = + liftValue constants refs.eq u w := by + simp only [quotientAssignment, Assignment.insert, if_neg liftInd, if_true, + List.getD_cons_zero, + List.getD_cons_succ] + simp only [QuotientRefs.certified] + rw [lhs] + simp only [liftValue, invariantSet, Basis.Equality.value, eqValue] + · intro u + simp only [QuotientRefs.certified, quotientAssignment, Assignment.insert, if_true] + · intro u + simp only [QuotientRefs.certified, quotientAssignment, Assignment.insert, if_true] + +/-- The certified equality interface, restated for the quotient references. -/ +def EqualityInterface (entries : Model.Environment β) (refs : QuotientRefs β) : Prop := + Basis.Equality.Interface entries refs.eq refs.eqRefl refs.eqRec + +/-- Admitting the four quotient constants extends every model of the +preceding interface, which must already realize the equality family, and +keeps every old interpretation. The published entries carry the certified +types and the `Quot.lift`/`Quot.ind` computation equations. -/ +theorem extend_quotients (wellFormed : entries.WF) (injective : refs.Injective) + (apart : refs.EqualityApart) (fresh : ∀ kind, entries (refs.ref kind) = none) + (equality : EqualityInterface entries refs) + (constants : Assignment β V) (realizes : Realizes constants entries) : + ∃ next : Assignment β V, + Realizes next (quotientEnvironment refs entries) ∧ + Assignment.AgreesOn entries constants next := by + let next := quotientAssignment refs constants + have agrees : Assignment.AgreesOn entries constants next := + quotientAssignment_agrees fresh constants + have reading : Reading next refs.certified := quotientAssignment_reading injective apart constants + have old : Realizes next entries := realizes.of_agrees wellFormed agrees + have ne (left right : Ix.QuotKind) (different : left ≠ right) : refs.ref left ≠ refs.ref right := + fun same => different (injective same) + have eqType : entries.HasType refs.eq 1 Basis.Equality.type := equality.former + -- Membership of each installed value in its published type. + have member (kind : Ix.QuotKind) : ∀ levels, levels.length = (refs.entry kind).universes → + ∀ env, next (refs.ref kind) levels ∈ˢ interp next levels env (refs.entry kind).type := by + intro levels length env + exact value_mem reading equality old kind.certified levels + (by simpa only [QuotientRefs.entry_universes, ← Ix.QuotKind.certified_universes] using length) + env + -- The interface after each insertion, innermost first. + let E₁ := entries.insert refs.type (refs.entry .type) + let E₂ := E₁.insert refs.ctor (refs.entry .ctor) + let E₃ := E₂.insert refs.lift (refs.entry .lift) + have ctorType' : refs.ctor ≠ refs.type := ne .ctor .type (by decide) + have liftCtor : refs.lift ≠ refs.ctor := ne .lift .ctor (by decide) + have liftType' : refs.lift ≠ refs.type := ne .lift .type (by decide) + have freshCtor₁ : E₁ refs.ctor = none := by + simp only [E₁, Model.Environment.insert, if_neg ctorType'] + exact fresh .ctor + have freshLift₁ : E₁ refs.lift = none := by + simp only [E₁, Model.Environment.insert, if_neg liftType'] + exact fresh .lift + have freshLift₂ : E₂ refs.lift = none := by + simp only [E₂, Model.Environment.insert, if_neg liftCtor] + exact freshLift₁ + have typeIn₁ : E₁.HasType refs.type 1 typeType := + ⟨_, Model.Environment.insert_same .., rfl, rfl⟩ + have typeIn₂ : E₂.HasType refs.type 1 typeType := + ⟨_, Model.Environment.insert_old freshCtor₁ (Model.Environment.insert_same ..), rfl, rfl⟩ + have ctorIn₂ : E₂.HasType refs.ctor 1 (ctorType refs.certified) := + ⟨_, Model.Environment.insert_same .., rfl, rfl⟩ + have typeIn₃ : E₃.HasType refs.type 1 typeType := + ⟨_, Model.Environment.insert_old freshLift₂ + (Model.Environment.insert_old freshCtor₁ (Model.Environment.insert_same ..)), rfl, rfl⟩ + have ctorIn₃ : E₃.HasType refs.ctor 1 (ctorType refs.certified) := + ⟨_, Model.Environment.insert_old freshLift₂ (Model.Environment.insert_same ..), rfl, rfl⟩ + have eqIn₂ : E₂.HasType refs.eq 1 Basis.Equality.type := by + obtain ⟨entry, present, arity, type⟩ := eqType + exact ⟨entry, Model.Environment.insert_old freshCtor₁ + (Model.Environment.insert_old (fresh .type) present), arity, type⟩ + have realizes₁ : Realizes next E₁ := by + refine old.insert ⟨?_, member .type, (fun body hb => by cases hb), (fun body hb => by cases hb), + (fun law hl => by cases hl), (fun fact hf => by cases hf)⟩ + intro levels _ env + obtain ⟨_, formed⟩ := typeType_formed.{u,v} (E := entries) + exact (formed V next old levels env + (Context.valid_nil next levels env)).1 + have realizes₂ : Realizes next E₂ := by + refine realizes₁.insert ⟨?_, member .ctor, (fun body hb => by cases hb), + (fun body hb => by cases hb), + (fun law hl => by cases hl), (fun fact hf => by cases hf)⟩ + intro levels _ env + obtain ⟨_, formed⟩ := ctorType_formed.{u,v} (refs := refs.certified) typeIn₁ + exact (formed V next realizes₁ levels env + (Context.valid_nil next levels env)).1 + have realizes₃ : Realizes next E₃ := by + obtain ⟨_, formed⟩ := liftType_formed.{u,v} (refs := refs.certified) typeIn₂ eqIn₂ + refine realizes₂.insert ⟨?_, member .lift, (fun body hb => by cases hb), + (fun body hb => by cases hb), + ?_, (fun fact hf => by cases hf)⟩ + · intro levels _ env + exact (formed V next realizes₂ levels env (Context.valid_nil next levels env)).1 + · intro law listed levels length env + cases List.mem_singleton.mp listed + match levels, length with + | [u, w], _ => exact liftRule_eq reading equality old u w env + obtain ⟨_, formed⟩ := indType_formed.{u,v} (refs := refs.certified) typeIn₃ ctorIn₃ + refine ⟨next, realizes₃.insert ⟨?_, member .ind, (fun body hb => by cases hb), + (fun body hb => by cases hb), ?_, (fun fact hf => by cases hf)⟩, agrees⟩ + · intro levels _ env + exact (formed V next realizes₃ levels env (Context.valid_nil next levels env)).1 + · intro law listed levels _ env + cases List.mem_singleton.mp listed + exact indRule_eq _ _ _ _ + +end Realization + +/-! ### Production data of a quotient declaration -/ + +/-- The production data of one `.quot` declaration of the given kind: its address, +universe count, and declared type. -/ +structure QuotientSpec (kind : Ix.QuotKind) where + id : KId .anon + lvls : UInt64 + sourceType : KExpr .anon + +def QuotientSpec.constant {kind : Ix.QuotKind} (spec : QuotientSpec kind) : KConst .anon := + .quot () () kind spec.lvls spec.sourceType + +private instance : LawfulBEq ByteArray where + eq_of_beq {left right} h := by + cases left + cases right + exact congrArg ByteArray.mk (eq_of_beq h) + rfl {bytes} := beq_self_eq_true bytes.data + +private instance : LawfulBEq Address where + eq_of_beq {left right} h := by + cases left + cases right + exact congrArg Address.mk (eq_of_beq h) + rfl {addr} := by + cases addr + exact beq_self_eq_true (α := ByteArray) _ + +private theorem bind_success {α γ : Type} {action : TcM .anon α} {next : α → TcM .anon γ} + {before after : TcState .anon} {result : γ} + (accepted : EStateM.bind action next before = .ok result after) : + ∃ value state, action before = .ok value state ∧ next value state = .ok result after := by + cases run : action before with + | error err failed => rw [EStateM.bind, run] at accepted; contradiction + | ok value state => + rw [EStateM.bind, run] at accepted + exact ⟨value, state, rfl, accepted⟩ + +private theorem anon_id (id : KId .anon) : (⟨id.addr, ()⟩ : KId .anon) = id := by + cases id with + | mk addr name => cases name; rfl + +/-! ### The executed guard sequence -/ + +/-- A successful `checkQuotBody` passed exactly its four guards: the declared +kind is the one selected by the address, the universe count is the fixed one, +the declared type hashes to the canonical type, and `Quot.lift` ran the +`Eq`/`Eq.refl` prerequisite check from the same state. -/ +theorem checkQuotBody_success {p : Primitives .anon} {expectedKind kind : Ix.QuotKind} + {lvls : UInt64} {ty : KExpr .anon} {methods : Methods .anon} {before after : TcState .anon} + (run : (RecM.checkQuotBody p expectedKind kind lvls ty).run methods before = .ok () after) : + kind = expectedKind ∧ lvls = kind.universes ∧ + (ty.addr == (RecM.canonicalQuotType p kind).addr) = true ∧ + (kind = .lift → (RecM.checkEqType (m := .anon)).run methods before = .ok () after) ∧ + (kind ≠ .lift → after = before) := by + unfold RecM.checkQuotBody at run + by_cases mismatch : (kind != expectedKind) = true + · simp only [mismatch, ↓reduceIte] at run + cases run + have same : kind = expectedKind := Ix.QuotKind.eq_of_bne_eq_false (Bool.eq_false_iff.mpr mismatch) + simp only [mismatch, Bool.false_eq_true, ↓reduceIte] at run + subst same + refine ⟨rfl, ?_⟩ + cases kind with + | lift => + dsimp only at run + split at run + · cases run + rename_i arity + split at run + · cases run + rename_i hash + refine ⟨by simpa [Ix.QuotKind.universes] using Bool.eq_false_iff.mpr arity, + by simpa [bne] using Bool.eq_false_iff.mpr hash, fun _ => ?_, fun h => absurd rfl h⟩ + simp only [show (Ix.QuotKind.lift == Ix.QuotKind.lift) = true from rfl, ↓reduceIte] at run + exact run + | type => + dsimp only at run + split at run + · cases run + rename_i arity + split at run + · cases run + rename_i hash + refine ⟨by simpa [Ix.QuotKind.universes] using Bool.eq_false_iff.mpr arity, + by simpa [bne] using Bool.eq_false_iff.mpr hash, (fun h => nomatch h), fun _ => ?_⟩ + simp only [show (Ix.QuotKind.type == Ix.QuotKind.lift) = false from rfl, Bool.false_eq_true, + ↓reduceIte] at run + cases run + rfl + | ctor => + dsimp only at run + split at run + · cases run + rename_i arity + split at run + · cases run + rename_i hash + refine ⟨by simpa [Ix.QuotKind.universes] using Bool.eq_false_iff.mpr arity, + by simpa [bne] using Bool.eq_false_iff.mpr hash, (fun h => nomatch h), fun _ => ?_⟩ + simp only [show (Ix.QuotKind.ctor == Ix.QuotKind.lift) = false from rfl, Bool.false_eq_true, + ↓reduceIte] at run + cases run + rfl + | ind => + dsimp only at run + split at run + · cases run + rename_i arity + split at run + · cases run + rename_i hash + refine ⟨by simpa [Ix.QuotKind.universes] using Bool.eq_false_iff.mpr arity, + by simpa [bne] using Bool.eq_false_iff.mpr hash, (fun h => nomatch h), fun _ => ?_⟩ + simp only [show (Ix.QuotKind.ind == Ix.QuotKind.lift) = false from rfl, Bool.false_eq_true, + ↓reduceIte] at run + cases run + rfl + +/-- A successful `checkQuot` selected the kind by the declared address, which +is therefore the reserved primitive address of that kind, and then passed the +body guards. -/ +theorem checkQuot_success {id : KId .anon} {kind : Ix.QuotKind} {lvls : UInt64} + {ty : KExpr .anon} {methods : Methods .anon} {before after : TcState .anon} + (run : (RecM.checkQuot id kind lvls ty).run methods before = .ok () after) : + id.addr = (before.prims.quot kind).addr ∧ lvls = kind.universes ∧ + (ty.addr == (RecM.canonicalQuotType before.prims kind).addr) = true ∧ + (kind = .lift → (RecM.checkEqType (m := .anon)).run methods before = .ok () after) ∧ + (kind ≠ .lift → after = before) := by + unfold RecM.checkQuot at run + simp only [ReaderT.run_bind] at run + change EStateM.bind ((RecM.prims (m := .anon)).run methods) _ before = _ at run + obtain ⟨p, primed, primsRun, run⟩ := bind_success run + change EStateM.Result.ok before.prims before = _ at primsRun + cases primsRun + have dispatch (expectedKind : Ix.QuotKind) + (selected : (id.addr == (before.prims.quot expectedKind).addr) = true) + (rest : (RecM.checkQuotBody before.prims expectedKind kind lvls ty).run methods before = + .ok () after) : + id.addr = (before.prims.quot kind).addr ∧ lvls = kind.universes ∧ + (ty.addr == (RecM.canonicalQuotType before.prims kind).addr) = true ∧ + (kind = .lift → (RecM.checkEqType (m := .anon)).run methods before = .ok () after) ∧ + (kind ≠ .lift → after = before) := by + obtain ⟨same, count, canonical, equality, unchanged⟩ := checkQuotBody_success rest + subst same + exact ⟨eq_of_beq selected, count, canonical, equality, unchanged⟩ + by_cases isType : (id.addr == before.prims.quotType.addr) = true + · simp only [isType, ↓reduceIte] at run + exact dispatch .type isType run + simp only [isType, Bool.false_eq_true, ↓reduceIte] at run + by_cases isCtor : (id.addr == before.prims.quotCtor.addr) = true + · simp only [isCtor, ↓reduceIte] at run + exact dispatch .ctor isCtor run + simp only [isCtor, Bool.false_eq_true, ↓reduceIte] at run + by_cases isLift : (id.addr == before.prims.quotLift.addr) = true + · simp only [isLift, ↓reduceIte] at run + exact dispatch .lift isLift run + simp only [isLift, Bool.false_eq_true, ↓reduceIte] at run + by_cases isInd : (id.addr == before.prims.quotInd.addr) = true + · simp only [isInd, ↓reduceIte] at run + exact dispatch .ind isInd run + simp only [isInd, Bool.false_eq_true, ↓reduceIte] at run + cases run + +/-- The execution prefix of a successful quotient member check: validation, +the guard sequence, type inference, and the sort check. -/ +structure QuotTypeTrace {kind : Ix.QuotKind} (spec : QuotientSpec kind) (methods : Methods .anon) + (before : TcState .anon) where + validated : TcState .anon + guarded : TcState .anon + inferred : KExpr .anon + typeState : TcState .anon + level : KUniv .anon + afterSort : TcState .anon + validationRun : (RecM.validateConstWellScoped spec.constant).run methods before = .ok () validated + guardRun : (RecM.checkQuot spec.id kind spec.lvls spec.sourceType).run methods validated = + .ok () guarded + typeRun : (RecM.infer spec.sourceType).run methods guarded = .ok inferred typeState + sortRun : (RecM.ensureSortDirect inferred).run methods typeState = .ok level afterSort + +/-- Extract the executed prefix from a successful production member check. -/ +theorem quot_type_trace {kind : Ix.QuotKind} {spec : QuotientSpec kind} {methods : Methods .anon} + {before after : TcState .anon} + (accepted : (RecM.checkConstMember spec.id spec.constant).run methods before = .ok () after) : + Nonempty (QuotTypeTrace spec methods before) := by + unfold RecM.checkConstMember at accepted + simp only [QuotientSpec.constant, Mode.F.hasDups, Bool.false_eq_true, if_false, + ReaderT.run_bind] at accepted + change EStateM.bind ((RecM.validateConstWellScoped spec.constant).run methods) _ before = _ + at accepted + obtain ⟨⟨⟩, validated, validationRun, accepted⟩ := bind_success accepted + change EStateM.bind ((RecM.checkQuot spec.id kind spec.lvls spec.sourceType).run methods) _ + validated = _ at accepted + obtain ⟨⟨⟩, guarded, guardRun, accepted⟩ := bind_success accepted + change EStateM.bind ((RecM.infer spec.sourceType).run methods) _ guarded = _ at accepted + obtain ⟨inferred, typeState, typeRun, accepted⟩ := bind_success accepted + change EStateM.bind ((RecM.ensureSortDirect inferred).run methods) _ typeState = _ at accepted + obtain ⟨level, afterSort, sortRun, _⟩ := bind_success accepted + exact ⟨⟨validated, guarded, inferred, typeState, level, afterSort, validationRun, guardRun, + typeRun, sortRun⟩⟩ + +/-- The guard facts of a trace, about the primitive table installed at the +validated state. -/ +theorem QuotTypeTrace.guards {kind : Ix.QuotKind} {spec : QuotientSpec kind} + {methods : Methods .anon} {before : TcState .anon} + (trace : QuotTypeTrace spec methods before) : + spec.id.addr = (trace.validated.prims.quot kind).addr ∧ spec.lvls = kind.universes ∧ + (spec.sourceType.addr == (RecM.canonicalQuotType trace.validated.prims kind).addr) = true := + let ⟨address, count, canonical, _, _⟩ := checkQuot_success trace.guardRun + ⟨address, count, canonical⟩ + +/-- `Quot.lift` ran the `Eq`/`Eq.refl` prerequisite check from the validated state. -/ +theorem QuotTypeTrace.equality {kind : Ix.QuotKind} {spec : QuotientSpec kind} + {methods : Methods .anon} {before : TcState .anon} + (trace : QuotTypeTrace spec methods before) (isLift : kind = .lift) : + (RecM.checkEqType (m := .anon)).run methods trace.validated = .ok () trace.guarded := + (checkQuot_success trace.guardRun).2.2.2.1 isLift + +/-! ### The `Eq`/`Eq.refl` prerequisite of `Quot.lift` -/ + +private theorem foldl_find_some {α γ : Type _} {pred : α → Bool} {f : α → γ} : + ∀ {l : List α} {init : Option γ} {x : γ}, + l.foldl (fun acc a => if pred a then some (f a) else acc) init = some x → + init = some x ∨ ∃ a ∈ l, pred a = true ∧ x = f a + | [], _, _, h => .inl h + | a :: l, init, x, h => by + rw [List.foldl_cons] at h + rcases foldl_find_some h with found | ⟨b, mem, pb, rfl⟩ + · by_cases pa : pred a = true + · rw [if_pos pa] at found + exact .inr ⟨a, List.mem_cons_self .., pa, (Option.some.inj found).symm⟩ + · rw [if_neg pa] at found + exact .inl found + · exact .inr ⟨b, List.mem_cons_of_mem _ mem, pb, rfl⟩ + +/-- The facts established by a successful `Eq`/`Eq.refl` prerequisite check +about the checker environment it inspected: a safe inductive record at the +reserved `Eq` address with one universe parameter, two parameters, one index, +a single constructor at the reserved `Eq.refl` address, and a type hashing to +the canonical equality type; and a safe constructor record at the reserved +`Eq.refl` address, of that family, with two parameters, no fields, and a type +hashing to the canonical reflexivity type. -/ +structure EqualityPrerequisite (p : Primitives .anon) (env : KEnv .anon) : Prop where + eq : ∃ (id : KId .anon) (name : Mode.anon.F Ix.Name) (levelParams : Mode.anon.F (Array Ix.Name)) + (block : KId .anon) (memberIdx : UInt64) (ty : KExpr .anon) (ctors : Array (KId .anon)) + (leanAll : Mode.anon.F (Array (KId .anon))), + (id, KConst.indc name levelParams 1 2 1 false block memberIdx ty ctors leanAll) ∈ + env.consts.toList ∧ + id.addr = p.eq.addr ∧ ctors.size = 1 ∧ ctors[0]!.addr = p.eqRefl.addr ∧ + (ty.addr == (RecM.canonicalEqType (m := .anon)).addr) = true + refl : ∃ (id : KId .anon) (name : Mode.anon.F Ix.Name) (levelParams : Mode.anon.F (Array Ix.Name)) + (induct : KId .anon) (ty : KExpr .anon), + (id, KConst.ctor name levelParams false 1 induct 0 2 0 ty) ∈ env.consts.toList ∧ + id.addr = p.eqRefl.addr ∧ induct.addr = p.eq.addr ∧ + (ty.addr == (RecM.canonicalEqReflType p).addr) = true + +/-- One guard of a `do` block: a successful run passed it and continued. -/ +private theorem guard_success {c : Prop} [Decidable c] {α : Type} {e : TcError .anon} + {rest : RecM .anon α} {methods : Methods .anon} {before after : TcState .anon} {a : α} + (run : (if c then (do let _ ← (throw e : RecM .anon Unit); rest) else rest).run methods before = + .ok a after) : + ¬ c ∧ rest.run methods before = .ok a after := by + by_cases h : c + · rw [if_pos h] at run + cases run + · rw [if_neg h] at run + exact ⟨h, run⟩ + +/-- The final guard of a `do` block: a successful run passed it and returned. -/ +private theorem final_guard_success {c : Prop} [Decidable c] {e : TcError .anon} + {methods : Methods .anon} {before after : TcState .anon} + (run : (if c then (throw e : RecM .anon Unit) else pure ()).run methods before = .ok () after) : + ¬ c ∧ after = before := by + by_cases h : c + · rw [if_pos h] at run + cases run + · rw [if_neg h] at run + cases run + exact ⟨h, rfl⟩ + +/-- A successful prerequisite check changes no state and establishes the +`Eq`/`Eq.refl` facts about the environment it inspected. -/ +theorem checkEqType_success {methods : Methods .anon} {before after : TcState .anon} + (run : (RecM.checkEqType (m := .anon)).run methods before = .ok () after) : + after = before ∧ EqualityPrerequisite before.prims before.env := by + unfold RecM.checkEqType at run + rw [ReaderT.run_bind] at run + change EStateM.bind ((RecM.prims (m := .anon)).run methods) _ before = _ at run + obtain ⟨p, primed, primsRun, run⟩ := bind_success run + change EStateM.Result.ok before.prims before = _ at primsRun + cases primsRun + rw [ReaderT.run_bind] at run + change EStateM.bind ((get : RecM .anon (TcState .anon)).run methods) _ before = _ at run + obtain ⟨state, got, getRun, run⟩ := bind_success run + change EStateM.Result.ok before before = _ at getRun + cases getRun + dsimp only at run + split at run + case h_2 => cases run + rename_i eqId eqC found + split at run + case h_2 => cases run + rename_i name levelParams lvls params indices isUnsafe block memberIdx ty ctors leanAll + obtain ⟨arity, run⟩ := guard_success run + obtain ⟨parameters, run⟩ := guard_success run + obtain ⟨indexed, run⟩ := guard_success run + obtain ⟨safe, run⟩ := guard_success run + obtain ⟨single, run⟩ := guard_success run + obtain ⟨constructor, run⟩ := guard_success run + obtain ⟨canonical, run⟩ := guard_success run + rw [ReaderT.run_bind] at run + change EStateM.bind ((get : RecM .anon (TcState .anon)).run methods) _ before = _ at run + obtain ⟨state', got', getRun', run⟩ := bind_success run + change EStateM.Result.ok before before = _ at getRun' + cases getRun' + split at run + case h_2 => cases run + rename_i reflC foundRefl + split at run + case h_2 => cases run + rename_i reflName reflLevelParams reflUnsafe reflLvls induct cidx reflParams fields reflTy + obtain ⟨metadata, run⟩ := guard_success run + obtain ⟨reflCanonical, unchanged⟩ := final_guard_success run + have eqMember : (eqId, KConst.indc name levelParams lvls params indices isUnsafe block memberIdx + ty + ctors leanAll) ∈ before.env.consts.toList ∧ (eqId.addr == before.prims.eq.addr) = true := by + simp only [Std.HashMap.fold_eq_foldl_toList] at found + rcases foldl_find_some (pred := fun (entry : KId .anon × KConst .anon) => + entry.1.addr == before.prims.eq.addr) + (f := fun (entry : KId .anon × KConst .anon) => (entry.1, entry.2)) found with + impossible | ⟨⟨id, constant⟩, member, selected, same⟩ + · cases impossible + · cases same + exact ⟨member, selected⟩ + have reflMember : (⟨before.prims.eqRefl.addr, ()⟩, KConst.ctor reflName reflLevelParams reflUnsafe + reflLvls induct cidx reflParams fields reflTy) ∈ before.env.consts.toList := by + simp only [Std.HashMap.fold_eq_foldl_toList] at foundRefl + rcases foldl_find_some (pred := fun (entry : KId .anon × KConst .anon) => + entry.1.addr == before.prims.eqRefl.addr) + (f := fun (entry : KId .anon × KConst .anon) => entry.2) foundRefl with + impossible | ⟨⟨id, constant⟩, member, selected, same⟩ + · cases impossible + · cases same + have address : id.addr = before.prims.eqRefl.addr := eq_of_beq selected + rw [← address, anon_id id] + exact member + simp only [Bool.or_eq_true, not_or] at metadata + obtain ⟨⟨⟨⟨⟨unsafe', reflArity⟩, family⟩, index⟩, reflParameters⟩, reflFields⟩ := metadata + refine ⟨unchanged, ?_, ?_⟩ + · have lvlsOne : lvls = 1 := by simpa using Bool.eq_false_iff.mpr arity + have paramsTwo : params = 2 := by simpa using Bool.eq_false_iff.mpr parameters + have indicesOne : indices = 1 := by simpa using Bool.eq_false_iff.mpr indexed + have safeFalse : isUnsafe = false := Bool.eq_false_iff.mpr safe + subst lvlsOne paramsTwo indicesOne safeFalse + exact ⟨eqId, name, levelParams, block, memberIdx, ty, ctors, leanAll, eqMember.1, + eq_of_beq eqMember.2, by simpa using Bool.eq_false_iff.mpr single, + eq_of_beq (by simpa [bne] using Bool.eq_false_iff.mpr constructor), + by simpa [bne] using Bool.eq_false_iff.mpr canonical⟩ + · have lvlsOne : reflLvls = 1 := by simpa using Bool.eq_false_iff.mpr reflArity + have cidxZero : cidx = 0 := by simpa using Bool.eq_false_iff.mpr index + have paramsTwo : reflParams = 2 := by simpa using Bool.eq_false_iff.mpr reflParameters + have fieldsZero : fields = 0 := by simpa using Bool.eq_false_iff.mpr reflFields + have safeFalse : reflUnsafe = false := Bool.eq_false_iff.mpr unsafe' + subst lvlsOne cidxZero paramsTwo fieldsZero safeFalse + exact ⟨_, reflName, reflLevelParams, induct, reflTy, reflMember, rfl, + eq_of_beq (by simpa [bne] using Bool.eq_false_iff.mpr family), + by simpa [bne] using Bool.eq_false_iff.mpr reflCanonical⟩ + +/-- The `Eq`/`Eq.refl` facts established while admitting `Quot.lift`, about +the environment at its validated state. -/ +theorem QuotTypeTrace.equalityPrerequisite {kind : Ix.QuotKind} {spec : QuotientSpec kind} + {methods : Methods .anon} {before : TcState .anon} (trace : QuotTypeTrace spec methods before) + (isLift : kind = .lift) : EqualityPrerequisite trace.validated.prims trace.validated.env := + (checkEqType_success (trace.equality isLift)).2 + +/-- Store formation from the exact inference call made while admitting this +quotient constant, as for axioms. -/ +def QuotTypeTrace.synthesisTypeCheck {β : Type u} {resolve : Address → Option (ConstRef β)} + {entries : Model.Environment β} {kind : Ix.QuotKind} {spec : QuotientSpec kind} {fuel : Nat} + {before : TcState .anon} + (trace : QuotTypeTrace spec (methodsN fuel) before) {type : AExpr β} {level bound : VLevel} + (inference : SynthesisInference resolve entries [] [] [] fuel trace.guarded spec.sourceType + type (.sort level) bound) + (reading : readScopedExpr? resolve [] spec.sourceType = some type.erase) : + SynthesisTypeCheck resolve entries type level := + { fuel, before := trace.guarded, after := trace.typeState, source := spec.sourceType, + result := trace.inferred, bound, inference, reading, run := trace.typeRun } + +/-! ### Reading the canonical kernel types -/ + +@[simp] theorem readExpr?_mkAll {β : Type u} (resolve : Address → Option (ConstRef β)) + (name : Mode.anon.F Ix.Name) (bi : Mode.anon.F Lean.BinderInfo) (domain body : KExpr .anon) : + readExpr? resolve (KExpr.mkAll name bi domain body) = do + return .forallE (← readExpr? resolve domain) (← readExpr? resolve body) := rfl + +@[simp] theorem readExpr?_mkApp {β : Type u} (resolve : Address → Option (ConstRef β)) + (fn arg : KExpr .anon) : + readExpr? resolve (KExpr.mkApp fn arg) = do + return .app (← readExpr? resolve fn) (← readExpr? resolve arg) := rfl + +@[simp] theorem readExpr?_mkVar {β : Type u} (resolve : Address → Option (ConstRef β)) + (index : UInt64) (name : Mode.anon.F Ix.Name) : + readExpr? resolve (KExpr.mkVar index name) = some (.bvar index.toNat) := rfl + +@[simp] theorem readLevel_mkParam (index : UInt64) (name : Mode.anon.F Ix.Name) : + readLevel (KUniv.mkParam index name) = .param index.toNat := rfl + +@[simp] theorem readLevel_mkZero : readLevel (KUniv.mkZero (m := .anon)) = .zero := rfl + +/-- The reserved quotient addresses resolve to the package references. -/ +structure QuotientBinding {β : Type u} (resolve : Address → Option (ConstRef β)) + (prims : Primitives .anon) (refs : QuotientRefs β) : Prop where + type : resolve prims.quotType.addr = some refs.type + ctor : resolve prims.quotCtor.addr = some refs.ctor + lift : resolve prims.quotLift.addr = some refs.lift + ind : resolve prims.quotInd.addr = some refs.ind + +theorem QuotientBinding.quot {β : Type u} {resolve : Address → Option (ConstRef β)} + {prims : Primitives .anon} {refs : QuotientRefs β} + (binding : QuotientBinding resolve prims refs) + (kind : Ix.QuotKind) : resolve (prims.quot kind).addr = some (refs.ref kind) := by + cases kind with + | type => exact binding.type + | ctor => exact binding.ctor + | lift => exact binding.lift + | ind => exact binding.ind + +/-- The equality family admitted in the preceding interface: the reserved +`Eq`/`Eq.refl` addresses resolve to its references, whose entries carry the +canonical equality types together with the recursor. This is the static +binding discharged by the inductive admission of `Eq` (plan item WP5); until +then it is a premise of quotient admission, like `PrimitiveNatBinding`. -/ +structure EqualityBinding {β : Type u} (resolve : Address → Option (ConstRef β)) + (entries : Model.Environment β) (prims : Primitives .anon) (refs : QuotientRefs β) : Prop where + eq : resolve prims.eq.addr = some refs.eq + eqRefl : resolve prims.eqRefl.addr = some refs.eqRefl + interface : EqualityInterface entries refs + +/-- The canonical kernel type of each quotient kind reads to the certified +quotient syntax of that kind. -/ +theorem canonicalQuotType_reads {β : Type u} {resolve : Address → Option (ConstRef β)} + {prims : Primitives .anon} {refs : QuotientRefs β} + (binding : QuotientBinding resolve prims refs) + (equality : resolve prims.eq.addr = some refs.eq) (kind : Ix.QuotKind) : + readExpr? resolve (RecM.canonicalQuotType prims kind) = some (refs.entryType kind).erase := by + cases kind with + | type => rfl + | ctor => + simp [RecM.canonicalQuotType, RecM.canonicalAll, RecM.canonicalArrow, RecM.canonicalVar, + RecM.canonicalQuotRelation, KExpr.mkAppN, binding.type, QuotientRefs.entryType, + Quotient.Refs.entryType, Ix.QuotKind.certified, QuotientRefs.certified, Quotient.ctorType, + Quotient.relationType, Quotient.applied, AExpr.erase, AExpr.appN] + | lift => + simp [RecM.canonicalQuotType, RecM.canonicalAll, RecM.canonicalArrow, RecM.canonicalVar, + RecM.canonicalQuotRelation, KExpr.mkAppN, binding.type, equality, QuotientRefs.entryType, + Quotient.Refs.entryType, Ix.QuotKind.certified, QuotientRefs.certified, Quotient.liftType, + Quotient.liftPrefix, Quotient.invariantType, Basis.Equality.applied, Quotient.relationType, + Quotient.applied, AExpr.erase, AExpr.appN, AExpr.forallN] + | ind => + simp [RecM.canonicalQuotType, RecM.canonicalAll, RecM.canonicalArrow, RecM.canonicalVar, + RecM.canonicalQuotRelation, KExpr.mkAppN, binding.type, binding.ctor, QuotientRefs.entryType, + Quotient.Refs.entryType, Ix.QuotKind.certified, QuotientRefs.certified, Quotient.indType, + Quotient.indPrefix, Quotient.constructed, Quotient.relationType, Quotient.applied, + AExpr.erase, AExpr.appN, AExpr.forallN] + +/-- The reading of a declaration accepted by `checkQuot`, under address +faithfulness of the compared pair: the source type reads to the certified +syntax of its kind. -/ +theorem QuotTypeTrace.reads {β : Type u} {resolve : Address → Option (ConstRef β)} + {refs : QuotientRefs β} {kind : Ix.QuotKind} {spec : QuotientSpec kind} + {methods : Methods .anon} {before : TcState .anon} + (trace : QuotTypeTrace spec methods before) + (binding : QuotientBinding resolve trace.validated.prims refs) + (equality : resolve trace.validated.prims.eq.addr = some refs.eq) + (faithful : spec.sourceType.AddrFaithful (RecM.canonicalQuotType trace.validated.prims kind)) : + readExpr? resolve spec.sourceType = some (refs.entryType kind).erase := by + rw [beq_readExpr? faithful trace.guards.2.2] + exact canonicalQuotType_reads binding equality kind + +/-! ### Quotient constants at work positions -/ + +/-- The four production quotient kinds. -/ +def _root_.Ix.QuotKind.all : List Ix.QuotKind := [.type, .ctor, .lift, .ind] + +theorem _root_.Ix.QuotKind.mem_all (kind : Ix.QuotKind) : kind ∈ Ix.QuotKind.all := by + cases kind <;> simp [Ix.QuotKind.all] + + +/-- Syntactic provenance of one quotient constant at a real work position, +with the resources its guard sequence consumes: the primitive table installed +at the validated state, and address faithfulness of the compared pair. -/ +structure QuotientObservation (env : Ixon.Env) (cfg : CheckCfg) (work : Array AnonWorkItem) + (prims : Primitives .anon) {kind : Ix.QuotKind} (spec : QuotientSpec kind) where + position : WorkPosition work (.standalone spec.id.addr) + path : StandalonePrefix spec.id (position.state env cfg).checker spec.constant + primitives : ∀ trace : QuotTypeTrace spec + (methodsN (position.state env cfg).checker.recFuel.toNat) path.ready, + trace.validated.prims = prims + faithful : spec.sourceType.AddrFaithful (RecM.canonicalQuotType prims kind) + +/-- Successful rows supply the guard facts of an observed quotient constant: +its address is the reserved one, its universe count is the fixed one, and its +type reads as the canonical kernel type under any reference map. -/ +theorem QuotientObservation.guards {env : Ixon.Env} {cfg : CheckCfg} {work : Array AnonWorkItem} + {prims : Primitives .anon} {kind : Ix.QuotKind} {spec : QuotientSpec kind} + (observation : QuotientObservation env cfg work prims spec) + (succeeded : ∀ result ∈ (runAnonCheckList cfg work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none) : + spec.id.addr = (prims.quot kind).addr ∧ spec.lvls = kind.universes ∧ + ∀ {β : Type u} (resolve : Address → Option (ConstRef β)), + readExpr? resolve spec.sourceType = + readExpr? resolve (RecM.canonicalQuotType prims kind) := by + obtain ⟨after, run⟩ := observation.position.check_success succeeded + rw [anon_id] at run + obtain ⟨trace⟩ := quot_type_trace (observation.path.member_success run) + have primitives := observation.primitives trace + obtain ⟨address, count, canonical⟩ := trace.guards + rw [primitives] at address canonical + exact ⟨address, count, fun _ => beq_readExpr? observation.faithful canonical⟩ + +/-- The four quotient constants of a run: the primitive table, the model +references, and one production declaration per kind. -/ +structure QuotientPackage (β : Type u) where + prims : Primitives .anon + refs : QuotientRefs β + spec : ∀ kind, QuotientSpec kind + +/-- The declared addresses, in kind order. -/ +def QuotientPackage.addresses {β : Type u} (package : QuotientPackage β) : List Address := + Ix.QuotKind.all.map fun kind => (package.spec kind).id.addr + +theorem QuotientPackage.mem_addresses {β : Type u} (package : QuotientPackage β) + (kind : Ix.QuotKind) : + (package.spec kind).id.addr ∈ package.addresses := + List.mem_map.mpr ⟨kind, kind.mem_all, rfl⟩ + +/-- Model preservation composes along an interface extension. -/ +theorem PreservesModels.trans {β : Type u} {before middle after : Model.Environment β} + (extension : ∀ r entry, before r = some entry → middle r = some entry) + (first : PreservesModels.{u,v} before middle) (second : PreservesModels.{u,v} middle after) : + PreservesModels.{u,v} before after := by + intro V _ constants realizes + obtain ⟨next, nextModel, agrees⟩ := first V constants realizes + obtain ⟨final, finalModel, finalAgrees⟩ := second V next nextModel + refine ⟨final, finalModel, ?_⟩ + intro ref entry present levels + exact (finalAgrees ref entry (extension ref entry present) levels).trans + (agrees ref entry present levels) + +/-! ### The environment fragment with the four quotient constants -/ + +/-- The production environment fragment of `ResolvedEnvironmentFragment` +extended by the four quotient constants. The initial interface is any closed +interface that contains the listed axioms and realizes the equality family; +the quotient entries are published right after it, at their canonical +standalone coordinates, and the definitions follow. All four quotient +constants must be present as accepted standalone work items. -/ +structure QuotientEnvironmentFragment (env : Ixon.Env) (cfg : CheckCfg) where + work : Array AnonWorkItem + enumerated : buildAnonWork env = .ok work + materializes : SourceMaterializes env + initial : Model.Environment Address + axioms : List (AxiomSpec Address) + package : QuotientPackage Address + definitions : List (DefinitionSpec Address) + entries : Model.Environment Address + canonical : ∀ spec ∈ axioms, spec.Canonical + quotientCanonical : ∀ kind, package.refs.ref kind = .member (package.prims.quot kind).addr 0 + distinct : (axioms.map (·.id.addr) ++ package.addresses ++ + definitions.map (·.input.id.addr)).Nodup + initialCanonical : CanonicalOutside initial + (package.addresses ++ definitions.map (·.input.id.addr)) + axiomsInstalled : ∀ spec ∈ axioms, initial spec.ref = some spec.entry + axiomRuns : ∀ spec ∈ axioms, Nonempty (ResolvedAxiomObservation env cfg work spec) + quotientRuns : ∀ kind, + Nonempty (QuotientObservation env cfg work package.prims (package.spec kind)) + equality : EqualityBinding env.resolve initial package.prims package.refs + plan : ResolvedDefinitionPlan env cfg work (quotientEnvironment package.refs initial) definitions + entries + workCovered : ∀ item ∈ work, + (∃ spec ∈ axioms, item = .standalone spec.id.addr) ∨ + (∃ kind, item = .standalone (package.spec kind).id.addr) ∨ + (∃ spec ∈ definitions, item = .standalone spec.input.id.addr) ∨ + (∃ block primary targets, item = .block block primary targets) + +namespace QuotientEnvironmentFragment + +variable {env : Ixon.Env} {cfg : CheckCfg} (fragment : QuotientEnvironmentFragment env cfg) + +/-- The published quotient interface. -/ +def published : Model.Environment Address := + quotientEnvironment fragment.package.refs fragment.initial + +theorem serial_success {results : Array CheckResult} + (accepted : checkEnvAnon env cfg = .ok results) + (succeeded : ∀ result ∈ results, result.err? = none) : + ∀ result ∈ (runAnonCheckList cfg fragment.work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none := by + unfold checkEnvAnon at accepted + rw [fragment.enumerated] at accepted + cases accepted + exact succeeded + +/-- Every quotient constant of a successful run is declared at the reserved +address of its kind. -/ +theorem address (serial : ∀ result ∈ (runAnonCheckList cfg fragment.work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none) (kind : Ix.QuotKind) : + (fragment.package.spec kind).id.addr = (fragment.package.prims.quot kind).addr := + let ⟨observation⟩ := fragment.quotientRuns kind + (observation.guards.{0} serial).1 + +/-- Every quotient constant of a successful run declares the fixed universe count. -/ +theorem universes (serial : ∀ result ∈ (runAnonCheckList cfg fragment.work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none) (kind : Ix.QuotKind) : + (fragment.package.spec kind).lvls = kind.universes := + let ⟨observation⟩ := fragment.quotientRuns kind + (observation.guards.{0} serial).2.1 + +/-- The quotient references are pairwise distinct: their addresses are the +declared ones, which are listed without repetition. -/ +theorem injective (serial : ∀ result ∈ (runAnonCheckList cfg fragment.work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none) : + fragment.package.refs.Injective := by + intro left right same + rw [fragment.quotientCanonical left, fragment.quotientCanonical right] at same + have addresses := (ConstRef.member.inj same).1 + rw [← fragment.address serial left, ← fragment.address serial right] at addresses + have nodup : fragment.package.addresses.Nodup := + (List.nodup_append.mp (List.nodup_append.mp fragment.distinct).1).2.1 + simp only [QuotientPackage.addresses, Ix.QuotKind.all, List.map_cons, List.map_nil, + List.nodup_cons, List.mem_cons, List.not_mem_nil, or_false, not_or, List.nodup_nil, + not_false_eq_true, + and_true] at nodup + obtain ⟨⟨typeCtor, typeLift, typeInd⟩, ⟨ctorLift, ctorInd⟩, liftInd⟩ := nodup + cases left <;> cases right <;> first + | rfl + | exact absurd addresses typeCtor + | exact absurd addresses typeLift + | exact absurd addresses typeInd + | exact absurd addresses ctorLift + | exact absurd addresses ctorInd + | exact absurd addresses liftInd + | exact absurd addresses.symm typeCtor + | exact absurd addresses.symm typeLift + | exact absurd addresses.symm typeInd + | exact absurd addresses.symm ctorLift + | exact absurd addresses.symm ctorInd + | exact absurd addresses.symm liftInd + +/-- Every quotient reference is fresh in the initial interface, whose entries +sit at canonical coordinates outside the declared addresses. -/ +theorem fresh (serial : ∀ result ∈ (runAnonCheckList cfg fragment.work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none) : + ∀ kind, fragment.initial (fragment.package.refs.ref kind) = none := by + intro kind + cases present : fragment.initial (fragment.package.refs.ref kind) with + | none => rfl + | some entry => + obtain ⟨addr, same, absent⟩ := fragment.initialCanonical _ entry present + rw [fragment.quotientCanonical kind] at same + obtain ⟨same, _⟩ := ConstRef.member.inj same + subst same + exact (absent (List.mem_append_left _ (fragment.address serial kind ▸ + fragment.package.mem_addresses kind))).elim + +/-- A reference present in the initial interface is not a quotient reference. -/ +theorem apart_of_present (serial : ∀ result ∈ (runAnonCheckList cfg fragment.work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none) + {ref : ConstRef Address} {entry : ConstantEntry Address} + (present : fragment.initial ref = some entry) + (kind : Ix.QuotKind) : fragment.package.refs.ref kind ≠ ref := by + intro same + rw [← same, fragment.fresh serial kind] at present + cases present + +theorem apart (serial : ∀ result ∈ (runAnonCheckList cfg fragment.work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none) : + fragment.package.refs.EqualityApart := by + obtain ⟨eqEntry, eqPresent, _, _⟩ := fragment.equality.interface.former + obtain ⟨reflEntry, reflPresent, _, _⟩ := fragment.equality.interface.reflexivity + obtain ⟨recEntry, recPresent, _, _⟩ := fragment.equality.interface.elimination + exact ⟨fragment.apart_of_present serial eqPresent, fragment.apart_of_present serial reflPresent, + fragment.apart_of_present serial recPresent⟩ + +/-- The reserved quotient addresses resolve to the package references: each is +the declared address of an enumerated standalone item. -/ +theorem binding (serial : ∀ result ∈ (runAnonCheckList cfg fragment.work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none) : + QuotientBinding env.resolve fragment.package.prims fragment.package.refs := by + have resolved (kind : Ix.QuotKind) : + env.resolve (fragment.package.prims.quot kind).addr = + some (fragment.package.refs.ref kind) := by + obtain ⟨observation⟩ := fragment.quotientRuns kind + rw [fragment.quotientCanonical kind, ← fragment.address serial kind] + exact resolve_of_work_standalone fragment.materializes fragment.enumerated + observation.position.mem + exact ⟨resolved .type, resolved .ctor, resolved .lift, resolved .ind⟩ + +/-- The published quotient interface is closed whenever the initial one is. -/ +theorem published_wf (wellFormed : fragment.initial.WF) + (serial : ∀ result ∈ (runAnonCheckList cfg fragment.work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none) : + fragment.published.WF := by + obtain ⟨eqEntry, eqPresent, _, _⟩ := fragment.equality.interface.former + exact quotientEnvironment_wf wellFormed (fragment.injective serial) (fragment.fresh serial) + (by rw [eqPresent]; rfl) + +/-- The published interface keeps every canonical coordinate outside the +pending definitions. -/ +theorem published_canonical (serial : ∀ result ∈ (runAnonCheckList cfg fragment.work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none) : + CanonicalOutside fragment.published (fragment.definitions.map (·.input.id.addr)) := by + intro ref entry present + rcases quotientEnvironment_cases present with old | ⟨kind, rfl, _⟩ + · obtain ⟨addr, same, absent⟩ := fragment.initialCanonical ref entry old + exact ⟨addr, same, fun listed => absent (List.mem_append_right _ listed)⟩ + · refine ⟨(fragment.package.prims.quot kind).addr, fragment.quotientCanonical kind, + fun listed => ?_⟩ + have disjoint := (List.nodup_append.mp fragment.distinct).2.2 + exact disjoint _ (List.mem_append_right _ (fragment.address serial kind ▸ + fragment.package.mem_addresses kind)) _ listed rfl + +/-- The definition plan over the published interface is an atomic plan. -/ +theorem atomicPlan (serial : ∀ result ∈ (runAnonCheckList cfg fragment.work.toList + (initialAnonCheckLoopState env cfg)).results, result.err? = none) : + AtomicDefinitionPlan env cfg fragment.work env.resolve fragment.published fragment.definitions + fragment.entries := + fragment.plan.atomic fragment.materializes fragment.enumerated + (fragment.published_canonical serial) + (List.nodup_append.mp fragment.distinct).2.1 + +private theorem locations {work : Array AnonWorkItem} {before after : Model.Environment Address} + {definitions : List (DefinitionSpec Address)} + (plan : ResolvedDefinitionPlan env cfg work before definitions after) : + ∀ spec ∈ definitions, ∃ position : WorkPosition work (.standalone spec.input.id.addr), + Nonempty (StandalonePrefix spec.input.id (position.state env cfg).checker + spec.input.constant) := by + induction plan with + | nil => simp + | cons spec canonical position run tail ih => + intro candidate present + rcases List.mem_cons.mp present with same | later + · subst candidate + exact ⟨position, ⟨run.path⟩⟩ + · exact ih candidate later + +end QuotientEnvironmentFragment + +/-- A successful `checkEnvAnon` run in the quotient fragment extends every +model of its initial interface, which must realize the equality family, while +preserving all its interpretations: the four quotient constants are admitted +with the certified quotient semantics, and the definitions follow. -/ +theorem checkEnvAnon_preserves_model_quotient {env : Ixon.Env} {cfg : CheckCfg} + (fragment : QuotientEnvironmentFragment env cfg) (wellFormed : fragment.initial.WF) + {results : Array CheckResult} (accepted : checkEnvAnon env cfg = .ok results) + (succeeded : ∀ result ∈ results, result.err? = none) : + fragment.entries.WF ∧ PreservesModels.{0,v} fragment.initial fragment.entries := by + have serial := fragment.serial_success accepted succeeded + have publishedFormed := fragment.published_wf wellFormed serial + obtain ⟨finalFormed, preserve⟩ := (fragment.atomicPlan serial).sound publishedFormed serial + refine ⟨finalFormed, PreservesModels.trans + (quotientEnvironment_extends (fragment.fresh serial)) ?_ preserve⟩ + intro V _ constants realizes + exact extend_quotients wellFormed (fragment.injective serial) (fragment.apart serial) + (fragment.fresh serial) fragment.equality.interface constants realizes + +/-- The four quotient constants receive exactly the certified quotient entries +at their canonical coordinates. -/ +theorem checkEnvAnon_quotient_published {env : Ixon.Env} {cfg : CheckCfg} + (fragment : QuotientEnvironmentFragment env cfg) + {results : Array CheckResult} (accepted : checkEnvAnon env cfg = .ok results) + (succeeded : ∀ result ∈ results, result.err? = none) (kind : Ix.QuotKind) : + fragment.entries (.member (fragment.package.spec kind).id.addr 0) = + some (fragment.package.refs.entry kind) := by + have serial := fragment.serial_success accepted succeeded + have published := quotientEnvironment_same (entries := fragment.initial) + (fragment.injective serial) kind + rw [fragment.quotientCanonical kind, ← fragment.address serial kind] at published + exact (fragment.atomicPlan serial).extends _ _ published + +/-- Every standalone source record receives an interface entry whose type +reads the declaration reached by production lookup: axioms and quotient +constants with no body, definitions with the exact checked body. -/ +theorem checkEnvAnon_represents_source_quotient {env : Ixon.Env} {cfg : CheckCfg} + (fragment : QuotientEnvironmentFragment env cfg) (wellFormed : fragment.initial.WF) + {results : Array CheckResult} (accepted : checkEnvAnon env cfg = .ok results) + (succeeded : ∀ result ∈ results, result.err? = none) : + ∀ addr, env.resolve addr = some (.member addr 0) → + ∃ entry, fragment.entries (.member addr 0) = some entry ∧ + ∃ concrete : KConst .anon, + (∃ before after, TcM.checkConst (⟨addr, ()⟩ : KId .anon) before = .ok () after ∧ + Nonempty (StandalonePrefix (⟨addr, ()⟩ : KId .anon) before concrete)) ∧ + DeclarationReading env.resolve concrete entry := by + have serial := fragment.serial_success accepted succeeded + have atomic := fragment.atomicPlan serial + have publishedFormed := fragment.published_wf wellFormed serial + have represented := atomic.represents publishedFormed serial + intro addr resolved + have listed := work_standalone_of_resolve fragment.materializes fragment.enumerated resolved + rcases fragment.workCovered _ listed with ⟨spec, member, same⟩ | ⟨kind, same⟩ | + ⟨spec, member, same⟩ | ⟨block, primary, targets, same⟩ + · cases AnonWorkItem.standalone.inj same + obtain ⟨observation⟩ := fragment.axiomRuns spec member + obtain ⟨after, run⟩ := observation.position.check_success serial + refine ⟨spec.entry, ?_, spec.constant, + ⟨(observation.position.state env cfg).checker, after, run, ?_⟩, + observation.reads, rfl, rfl⟩ + · rw [← fragment.canonical spec member] + exact atomic.extends _ _ (quotientEnvironment_old (fragment.fresh serial) + (fragment.axiomsInstalled spec member)) + · simpa only [anon_id] using Nonempty.intro observation.path + · cases AnonWorkItem.standalone.inj same + obtain ⟨observation⟩ := fragment.quotientRuns kind + obtain ⟨after, run⟩ := observation.position.check_success serial + obtain ⟨_, count, reads⟩ := observation.guards.{0} serial + refine ⟨fragment.package.refs.entry kind, + checkEnvAnon_quotient_published fragment accepted succeeded kind, + (fragment.package.spec kind).constant, + ⟨(observation.position.state env cfg).checker, after, run, ?_⟩, ?_, ?_, rfl⟩ + · simpa only [anon_id] using Nonempty.intro observation.path + · exact (reads env.resolve).trans + (canonicalQuotType_reads (fragment.binding serial) fragment.equality.eq kind) + · simp only [QuotientRefs.entry_universes, QuotientSpec.constant, KConst.lvls, count] + · cases AnonWorkItem.standalone.inj same + obtain ⟨resolvedSpec, installed, valueReads, typeReads⟩ := represented spec member + have same : spec.ref = .member spec.input.id.addr 0 := + Option.some.inj (resolvedSpec.symm.trans resolved) + obtain ⟨position, path⟩ := QuotientEnvironmentFragment.locations fragment.plan spec member + obtain ⟨after, run⟩ := position.check_success serial + refine ⟨spec.entry, ?_, spec.input.constant, + ⟨(position.state env cfg).checker, after, run, ?_⟩, typeReads, rfl, spec.body, rfl, + valueReads⟩ + · rw [← same] + exact installed + · simpa only [anon_id] using path + · cases same + +/-- No declaration can inhabit a type interpreted as empty in the initial +interface, such as `False`. -/ +theorem checkEnvAnon_no_false_quotient {env : Ixon.Env} {cfg : CheckCfg} + (fragment : QuotientEnvironmentFragment env cfg) (wellFormed : fragment.initial.WF) + {results : Array CheckResult} (accepted : checkEnvAnon env cfg = .ok results) + (succeeded : ∀ result ∈ results, result.err? = none) + {V : Type v} [SetTheory V] (initialValues : Assignment Address V) + (initialModel : Realizes initialValues fragment.initial) + {falseAddr : Address} {falseEntry : ConstantEntry Address} + (hasFalse : fragment.initial (.member falseAddr 0) = some falseEntry) + (falseEmpty : initialValues (.member falseAddr 0) [] = SetTheory.empty) + {ref : ConstRef Address} {entry : ConstantEntry Address} + (present : fragment.entries ref = some entry) + (isFalse : entry.type.erase = .const (.member falseAddr 0) []) : False := by + obtain ⟨_, preserve⟩ := + checkEnvAnon_preserves_model_quotient fragment wellFormed accepted succeeded + obtain ⟨values, model, agrees⟩ := preserve V initialValues initialModel + have emptyValue : values (.member falseAddr 0) [] = SetTheory.empty := + (agrees _ _ hasFalse []).trans falseEmpty + have typeEq := AExpr.eq_const_of_erase_eq isFalse + have member := model.member ref entry present (List.replicate entry.universes 0) + (by simp) (fun _ => SetTheory.empty) + rw [typeEq] at member + simp only [interp, List.map_nil, emptyValue] at member + exact SetTheory.not_mem_empty _ member + +end Ix.Kernel.Consistency diff --git a/Tests/Ix/Kernel/Quotient.lean b/Tests/Ix/Kernel/Quotient.lean new file mode 100644 index 000000000..b286d1bb2 --- /dev/null +++ b/Tests/Ix/Kernel/Quotient.lean @@ -0,0 +1,117 @@ +/- +Copyright (c) 2026 Argument Computer Corporation. +SPDX-License-Identifier: MIT OR Apache-2.0 +-/ + +import LSpec +import Ix.Kernel.Verify.Consistency.Quotient + +/-! +Regressions for the canonical quotient bundle: the four constants installed +by Lean's `Environment.addQuot`, built directly in a kernel environment at the +reserved primitive addresses as in `Tests.Kernel.CheckTests`, pass +`TcM.checkConst`; a non-canonical type or a wrong universe count is rejected; +and the canonical kernel types read as the certified quotient description +under the reference map that the admission theorems derive. This file is not +a `module` because the proof library it exercises is not one. +-/ + +namespace Tests.Kernel.Quotient + +open LSpec Ix.Kernel Ix.Theory Ix.Kernel.Consistency + +private def prims : Primitives .anon := .ofAnonAddrs + +/-- The canonical `Eq`/`Eq.refl`/quotient bundle at the reserved addresses. -/ +private def canonicalQuotEnv : KEnv .anon := Id.run do + let p := prims + let mut env : KEnv .anon := {} + env := env.insert p.eq + (.indc () () 1 2 1 false p.eq 0 (RecM.canonicalEqType (m := .anon)) #[p.eqRefl] ()) + env := env.insert p.eqRefl (.ctor () () false 1 p.eq 0 2 0 (RecM.canonicalEqReflType p)) + env := env.insert p.quotType (.quot () () .type 1 (RecM.canonicalQuotType p .type)) + env := env.insert p.quotCtor (.quot () () .ctor 1 (RecM.canonicalQuotType p .ctor)) + env := env.insert p.quotLift (.quot () () .lift 2 (RecM.canonicalQuotType p .lift)) + env := env.insert p.quotInd (.quot () () .ind 1 (RecM.canonicalQuotType p .ind)) + return env + +private def checkOn (env : KEnv .anon) (id : KId .anon) : Except (TcError .anon) Unit := + match (TcM.checkConst id).run (.ofEnvAnon env) with + | .ok () _ => .ok () + | .error e _ => .error e + +private def failsContaining (env : KEnv .anon) (id : KId .anon) (fragment : String) : Bool := + match checkOn env id with + | .error e => ((toString e).splitOn fragment).length > 1 + | .ok () => false + +private def replaceQuot (env : KEnv .anon) (id : KId .anon) (kind : Ix.QuotKind) (lvls : UInt64) + (ty : KExpr .anon) : KEnv .anon := + env.insert id (.quot () () kind lvls ty) + +/-- A well-typed type of the right binder depth carrying no quotient semantics. -/ +private def forgedForallType (n : Nat) : KExpr .anon := + (List.range n).foldl (fun body _ => KExpr.mkAll () () (.mkSort .mkZero) body) (.mkSort .mkZero) + +private instance : LawfulBEq ByteArray where + eq_of_beq {left right} h := by + cases left + cases right + exact congrArg ByteArray.mk (eq_of_beq h) + rfl {bytes} := beq_self_eq_true bytes.data + +private instance : LawfulBEq Address where + eq_of_beq {left right} h := by + cases left + cases right + exact congrArg Address.mk (eq_of_beq h) + rfl {addr} := by + cases addr + exact beq_self_eq_true (α := ByteArray) _ + +private instance : DecidableEq Address := fun left right => + decidable_of_iff ((left == right) = true) ⟨eq_of_beq, fun same => same ▸ beq_self_eq_true left⟩ + +/-- The canonical standalone coordinates of the reserved addresses. -/ +private def refs : QuotientRefs Address := + { type := .member prims.quotType.addr 0, ctor := .member prims.quotCtor.addr 0, + lift := .member prims.quotLift.addr 0, ind := .member prims.quotInd.addr 0, + eq := .member prims.eq.addr 0, eqRefl := .member prims.eqRefl.addr 0, + eqRec := .member prims.eq.addr 1 } + +/-- The reference map on the reserved addresses. -/ +private def resolve (addr : Address) : Option (ConstRef Address) := + if addr == prims.quotType.addr || addr == prims.quotCtor.addr || addr == prims.quotLift.addr || + addr == prims.quotInd.addr || addr == prims.eq.addr || addr == prims.eqRefl.addr then + some (.member addr 0) + else none + +private def readsCanonically (kind : Ix.QuotKind) : Bool := + decide (readExpr? resolve (RecM.canonicalQuotType prims kind) = some (refs.entryType kind).erase) + +private def cases : TestSeq := + test "quotient: the canonical bundle passes checkConst" + ((checkOn canonicalQuotEnv prims.quotType).isOk && + (checkOn canonicalQuotEnv prims.quotCtor).isOk && + (checkOn canonicalQuotEnv prims.quotLift).isOk && + (checkOn canonicalQuotEnv prims.quotInd).isOk) + ++ test "quotient: a non-canonical type is rejected" + (failsContaining (replaceQuot canonicalQuotEnv prims.quotType .type 1 (forgedForallType 2)) + prims.quotType "type is not canonical" && + failsContaining (replaceQuot canonicalQuotEnv prims.quotLift .lift 2 (forgedForallType 6)) + prims.quotLift "type is not canonical") + ++ test "quotient: a wrong universe count is rejected" + (failsContaining (replaceQuot canonicalQuotEnv prims.quotLift .lift 3 + (RecM.canonicalQuotType prims .lift)) prims.quotLift "expects 2 universe params" && + failsContaining (replaceQuot canonicalQuotEnv prims.quotInd .ind 2 + (RecM.canonicalQuotType prims .ind)) prims.quotInd "expects 1 universe params") + ++ test "quotient: a kind at another reserved address is rejected" + (failsContaining (replaceQuot canonicalQuotEnv prims.quotType .ctor 1 + (RecM.canonicalQuotType prims .ctor)) prims.quotType "kind mismatch") + ++ test "quotient: the canonical kernel types read as the certified quotient description" + (readsCanonically .type && readsCanonically .ctor && readsCanonically .lift && + readsCanonically .ind) + +def suite : List TestSeq := [cases] + +end Tests.Kernel.Quotient diff --git a/Tests/Main.lean b/Tests/Main.lean index 1d65c582c..ca549550c 100644 --- a/Tests/Main.lean +++ b/Tests/Main.lean @@ -39,6 +39,7 @@ import Tests.Ix.Kernel.Substrate import Tests.Ix.Kernel.IxonFixtures import Tests.Ix.Kernel.Resolution import Tests.Ix.Kernel.Inductive +import Tests.Ix.Kernel.Quotient import Tests.Ix.Kernel.WhnfTests import Tests.Ix.Kernel.InferDefEq import Tests.Ix.Kernel.CheckTests @@ -115,6 +116,7 @@ def primarySuites : Std.HashMap String (List LSpec.TestSeq) := .ofList [ ++ Tests.Kernel.InferDefEq.suite ++ Tests.Kernel.CheckTests.suite ++ Tests.Kernel.Consistency.suite ++ Tests.Kernel.Resolution.suite ++ Tests.Kernel.Inductive.suite + ++ Tests.Kernel.Quotient.suite ++ Tests.Kernel.DefinitionDependencies.suite ++ Tests.Kernel.SafeRecursion.suite ++ Tests.Kernel.Roundtrip.unitTests ++ Tests.Kernel.IngressMeta.unitTests),

pC2Jw)DOa%0s8f#w8N zyH7kc&Xev!xoL=3`_wBNQw0^x^od^?W5=BV6jAeO>M`CGl$uLE@5MaQpMCt(E!Lg> zmF+&s*fi>0yQ-~;``X(>SJ(^kVDEV!w#2>Y5d5?{Y^}$kJXDlmz1%K*hx+;P@N)RX z0(b3;8<55*w1tgPayXexM91i^j?W#X((%=g-z(e3qJW;@-Qgmey8GwdM|_#gOCaGnS( zf3Q9(z3sWQt}&i-$_@9g?m>UiW?MYv)zC*!&GSE~yH3%hhBydQXUpi}jVdv~Mo((X;cM-da~How*qw zt~Q=81a&rhhK|`#z7d}HgT2cAz+PIB@3a7WJ>K?TIvJY{Z|?H@r*QXuS6AP+bn|^@ z7vI6o--%21$Eq5Ekhx$hdePpR((FPH<7bZ|dLFve<1iX-l8<%~VL$xY*!TRcZw0-} zRp<9d-^T||gBOWyJO`8aS0~+U54p#>=fh!---#K$&zlYh-2ek;uub0V2Ha172ks}n z1-F=#RhEHxSE+vKQ@{b9YDZrjp-d>n2mQf`<+Y3=#u`K zwoewlfX&aa)dA#YZooNj`qTX%^&_v?5G(1E<0*3K4gz94w<6q9rRX{^p*z8K~h+DZ0G4#k9e(--ZwgrNW~-Pjmw!~ zY27L!wkGwpa$~V+uDtCuqGwazvSWMRxsUhZ`-sl#bD8Pbyt{fTJ2~R{zDyX1T%PE0 zuF2){WMNm|mFf$OnF3CPOuQc?E3{-pTW>uMbF2pD2mPH2{OpcVTw|tIJDdr|aN<4I zB;+U26#F(n?}` zQO(rgtgC`|UPfH|wKjvAc7-zCD+dPf?2XCIVyvpYXyFF&DiT?K@S%ls3T7G%Z_u$0 z_hSQ5RkEc)L0r6EG^_2UI&#`9stwva9fS;5973IE9m0_gALO=&hBx1Ff0;faMrg?#r^zpBzVKOUQarM^zyThZR6I;>{Mn_s!oY?MDJP zGcAs!&moM)9w!_*>zbnf+F6cq*L>Xb=Y20M$oLSB`MwP) z*afiY*pwM8sGlim4ha}NSIj$ zGk=;o=BcJhrSa-d$3E-FFjMi5cB?9@v$nW;$>@%zEg)`_D4?VU(#zyP@v>>p{H}Kf z*14{~jpf3u^p4TZ4s6D5F)_4Gs*gr@Q)i#Y*Dqc$4bQB%k6++j+X?l^e1um z893~aU1&F(?T%eK& zk-L>*1BIZyzNobu3T1MwvnF|7C~l{!*EUs?#^*!;wjbgJHpo&L_Ox1cy5dR4%hUDN z)%B&^e0QdWeNXk$lK*`ZG+sVfsas3t>6OKaW|3(>`Q=t8SyaV4720;| zAs>$v=!0^ov*1*Ya~I0u>}>s#H{n$R933of!BIdG_|ox91qgkcogdy4vNuv`%AEaP zXn);t44Z%BNa2vC~>pZ;~cxzU_vc4Af{LbGlW_i`+0;4T~S{`BN=(A#Xr2s!{bXY~-iUY&A#Eon@a!7S`s=^R_Iy{_&M-aGRu*OVIx>&^rYsqeqy8yKi1M6_(+DSzQ}U5r_TFMU;pOrTjvy#&HOhth z)6X{FTYCHA(EQEAq@jA(5z$zQUWC z2AyiQBjqnA=GY?{-gI#tI_Xd5$+&+fs%Oh?Qq)i1E!nfQZuJZqVDt8eaF&ERfHdSY z{Uh-{H63+HAk7NSr?Aw+cbMzVkB%feH&0u^&C|lW%G7GPiryi|yRFdxK~|}T&`Z`n z-1(EVGne7m=U<)1exrS`V>b49+|6u|0qMH+@=_H~kg>tMlSJLovwG>!?V;CFp46QV zsZ}#iOPzUnBMo(~4o^ER|73wOpVYqAb%09RQ`nx=PE#AIZ~glw-B(ZQFULpyom%%3 zo#!0s6vaj{`&8RT4PQbJ7w0!QZc}kY{Ce&xCqHEbH)!5VqD1x za(%@k%UiXZK_d;e=JJ*~{zO?s9qVrPPWqGsAKO@eXMdSOomp#Q?VfM- z8N*Lyuz#cH=0d*G6W?1+XCX5uyzP}Tdp@Zjt5rFt$?z=<^uD3=Xo`LhT+w634z;_tmt1sYW%(_QO-T_el8K zXlx@aPtmO&h8J|127KGWN7}b~{T<^+-~YXzF3F^VJk?$XUh+(LX}CKg@ZV?|~}c5(EizOUHClIIb}3%6c+6Wq|FV1}uokuR5Tw-r=n%T>oZ75(U^ z+D^FfU8yw7)Bk?%F+u%rkB{~DkvaLY6*|UdoL+23)=hV8)MIp`#e8nOBYrYIH{fCY zRpe2S8b6tvx7vM9H5$ZW=bC*s)t~P^Puj;b##>^FWi;KcwU2a^EBnhOL_Hd8Xw@eb z6aMsg8M$OTi$IqRC-gzkYUCaJN(`wwyup5R;dnl*wJj-nS zeG^ZK`i#D3dAX)F#!Hel;q&9Jlh^v7Ol4-(i6_kbul`mmo{Qg#4S1#C)F1pnA5kfJ-)@u5t0S8Ubwtl3Z&Zyu zi?sgmo_&gprRzz(&l!W!fv_RxoiY3Z@8XQntH%$%5@vt;;48kn$ecyYx@u@oj9<$O zxnYzJJ*g z_nhk5uqVz1sdt=Z#Gg4lQlp&im8s>H+NHPLK~8M~H2tTAO+WN&d39e&7wg%?db*SJ zRolJTszh?E;1^a9E4PfR)6M$j;uaR!Nf_nQE_i-|xUXiJn`1<;Y;y{+9XTmbd;M-%w|}HfqY@mD*2s)ao4p z?yS?Bg?I?OEF)D`-IJY)NxAci2+zDx^;CwnJ*LJpBMl zutj@R;BcyG*RlECJI}ITxA$}m<(=QK$SqsmlQQZvY(-VYT`ZlZ*jx54p1Di-8+H0? zWv1<~Pu^_Wrf#y{SGH0sN+)aW}Eh*45=rg+Ua0cjz`xY&#%5{J%e7!yMJ1&jGy_m9((tq(d+3jR7hHK52+ZpG=RP zD-4J^#JOVPX7lDj4Rp=U)exI%AJ*xP(|UI*6$P0h=m64XHj`&yB>XMVA?tDdX4 zCjFblN7|{U?^v?)g5YTf6}@}8go6?qyfGH^~ z^s-x-Awl?)QW>RMRj$|f2TPY7rgu~39rPM^Z+iHwpq<<@hyV)CVb+RCGH|u4jc=XT z)FSyiW^f)&Ou1@ooSEO5AoHhg5^nXbOk7{wz#c!g*U)-CSQOthj_OB4?M<>VeIFh7 zOfoKD(R-RdbFX}G)8h0cdU!B7%o-#xMsS^%Bdf&l&Up3uxoKCZ1ElvjGTneXu<=7a zoeF0L$NOH7`O@~fPZ5V(^?oXfIM7HpIa$tFH)CEKe)O|2vvrs4x}|(+b47cbc5pM# znZ2HyFx(YW9-HqR%}rTwPhTp&GP0lbYOy?AMXx#%n-}g@MW-h|c(`EBpE&CC;ir21 zUyG~L6F0s3mstS*U~cM#s`m$nbRIlh%1hLKbq3^my%jIyozB8?TaDtjcC>l?@S8`U z-e0a>$;JJzzP{izEA;fy{eS(<{l`z1gfX|w>5=B9Q70zYyHA|fh(T#&TL3ciIxSdI4 zw$g9(xU1q)g?VrqZX>ayzm*#`t6zw}eeF+)nKSO);KxOl1cCm1se_)HY3K<~Gr@z+ zYX)sk75KTCv~!%O9Z1xtTcs4%l_^^yrq|0nm@s+p9I- z>tg}ODUO^{(l2#%DqlTp&9zv=O&G}i`1l#4nVetRF}m>wQ;v~+Gv6K)@|V2%HETb1 zZ{Nju+*RUPj*Fj(-Pp5+I@W{TFk5iR5_=<}UpK2=0ny#-cZ!GJ)#6*^gFoH<$it?Y z2KXnmexGp5U#-sd^+XW&`HGq;_WQ*ZLwK5V%+4t9E>)WoP3P|8g|}fn7IN=S zrHWK2Hm|Ln++9u7`|$pms1{yVcbU=E^gFFX-HNm+=2Tr@@$?jw^k+}GWB;aCffqr| z3ny{1qtx}q3Y_j_TvZqS==tx~tBYYBgAiZy_4Jg-VJ$9-)5M2H|NGPgq3FVN8pG4D zuG#X={d<2K{ivS%S}@T#_t$T6-ym-1O#Qa>bg<9;tz4|1t2l}iB`4Ei47)KEZf2sc z=~x`Y%4WhBOw>1VgMUmeqnq-sBA~=Q-Z6gBd7r(L9YrQ8nnk7)BkZwz5B}^e+5zXu zweriE)}L7hVz}Q8c*ro)>1ajGX8uyVm|RD2|NAcTOaIji*}WQnIAkg3-|b>tcss7b zzc}a>9F+1doK08r^@d?#aTDk^=0ET~0pE2d7 z?Lsr#WJ_xE3==Jl_mwiE;jOx)Osf9|jQmqq8*rCaCTy%ct4ds*p0iSOyb5JP{h`x^ zj;b-=FhS~#KU!Z3aGo1~rVsL`K2z%pb;E@aFgafS>}hXSseP@0p499@yeXqQl`%85 z_qz+Q$BQF-fxe4?pl8_n%551gnr5DJ`B*Al^UaUPy$7A%HolB+ZsJRQ=lzGpg<8Au z4SSa8EXb;qGHlxZv-|2BTa__$y13BG7W4M8{_^ES`r=VVYG9b`Eyz`Uc5FXU7CIY- z+5hgy%U+;@wU}heq_vGQIZsvk!G{zX^?BnNJXjmGYt5_qluj(_`ZY8djYoZzzU$JR zyWVKk2gVAmea>%vni)&4Ai}+~fLz*gNmNl4p6AC~UA_9OPsa^hpYaZ$4SR-yx)?hq z*eg@EJ7c=OIHP*~XHWk|rJ#C6A%?Av_81wiK3%VP@#$}vQDUjW$Uo~=SoCt=O%A~8 zG0hk%3)%IBxU`|m*`383;fAwnub#-wIkkWfcbrreh*_dGD_uJpA%8s#Sy4b#n$3U9B!UNq@lVsBaEi9jiXQ;Wu+5awl@# zz`lNT|H}uD@0;&-j_sEHeATK>4|y88?h^X>O?-C$^9Nr&c=F)kR}S{$7(=`#3R-dwn~e15IcT0h^&+MM${V!Uhr951mGk<-kzW2odr zDwzZu`N)C?UNgVr^CL4NOI>$K^@f505w|+8rQCc@YkWCZp>DCB%Z|z`}N|5Y%ppw$lkzvWQ47!T=ARr?lyX~ z*wybpHhi;VxULdPt;bqyZB;hVAAhxYbpPI`zq$9x7xx#R-T(B9dynpsF|&|mHzotx zsMbf{eD&nPm-iR<|N6=ONBqK$@!woLy8r1nj~=6*#cv-z`m6ho@X}CKzm{9~zn7dQ z)wy$(=+K${wsf-vZq=#utn>Co!SYAC} zYp0rX@d}FhXYvv(GLG`B7{2|D1ShMcz{ZQCK>cIQH7`%sOrYLBv$ks3@$f-U!_nU? zhdL!?M5nF2@iI2MDESs2>auSZJPSIsbEceCqgy%R?`rT zry-u62GtS!8{6;Co^~^7C$K*y{KDDpfUvLs#A6M|V-4n(oDATa3--yndCeiZ2Pz<4 zFh5eTOu6}laRU{O{pm*QDSBquWP|t47zTAqb`u=)FFo#Ke=b<=3voVCEb^zrx>C)DAwX*!1g$A)UUJ|}^o7u(xwt;x>F6yPt5mKG4tF3;d{yeeDCZr2Js#WJy zBt#qe$~-}71}N>mBn(%<2#$n`3x#<*0zHajPNmsg>+Q-9`)CHdc&d$eoY-4t@op=Q zblWnbTWSqXCdz5n}#Mu=CDEg#5k{11%83r_<5oO%v6CH+7m2><} z1z6P~(3as$FX(D3u$fWKzV-(Klu`l`VySEBiJn>MaXdG-+P-(&Ue)W*FQ1yR&3xZ( zq`ZwLR#feFEY5fcs$YA0<#_y!YUXNzX4z>aG+owp-gQ^mFxO|+2ak7X{m9lXinY7m z?!`T%yXh5!D;NrLUQ~+*SJW_->0^6cov*I0T;_G_D$>tGWHB!`Ez@AQTuHZ$FE++- zy`_~yWp`KlrO@{?l}p_3bTB+GX}i*0Kh=YKWF4oEY9qt_$N>-35%JK;+=cbLyU-#0 zOa#A=tuR%WER|PStJ5>u(5Ysg@S&-rCw3K7(xMl?R_#J8I$^J8Of9Uk1nI)PetAJl z?+#-!)-D*M>&0{@RZW-XIM-J6^=7?pRUojBwNqdh(ZF_ZM{S-{{#5MtOIGguvQ_on zNL)|G^_$;A@9O0nsN+A*)?+*K=YOub$1jH88`%B6v7F)l@3}>>%ftoq);h_+W0VCqpvGtI zYjYu+CL5^|z`k6mqXX<1a3tmwQ%uHWht>Yo>ORo+YIW9U0vf(~`E!i7sn1vJ?rtE` zR~Plwebdmc?cA<5`fi(Hs_OSGqcOOPS8JU}$ueH`paY+ZC%3w)rOR)pOZE2d?EVm} z-4LkR0J=82?7h16k6nC5-wGaAYWn1&vi7=Ye$CZOZluEq@p}!nKy$qII`Me-c*mD? zOg6>I4WPz*ylY0rTS0$g&wyt;;&jtNlX@;x+CrMd(8kAu*_y7@7=_ij`phso*rF?+ zXkC}Ijhpl5-hg$#fApB(UM^<4_7>CV-|I5WVz|e8SH2o-^I93{TOVx~^}Shj`P+rQ`Ll2) z0oB!gcLR(w%q;~x?seA$jPL(IxjjBXQAJT`pj(7ZXnYXy3MM;jb76bmpBJ_^CcVEG>%;NSHyR>-v@S7)JfAi$| zXAd6T|MbbjNB_@f6L+rWdasa}>h1fM?i4dv6F{yKGysashUaVCJmd*P5#p&X2Mkx;eW*n7iI6YnOZ;~vl ze**eboqcwMRy#g)Uh7H?ZSQI+%#O? zBW(xKjy-HzH=;V>zB(Rt#iTespfAIiPB-g~X=kcCU2rX91?_@s^$w=^z@1EeYIM?x zlpuUO4hP1kU5KxKMi4tGru}7Q@;;-Wd35aG^)?<--bLxwU25o8WUpxstLoNucsaXW znn)PEYAj+Ob*al3J&{&6FxSn83YGZ-N~G&xYsc&&X|&6iUH~*&I=U=@+v;Mq zJ^grmirHB(kA}(l=Ca0ETDOGQyU9I4YAo=FzyGh}8!vTtP-!AraZ{L(1l_%Bem}5p z*#eeU_XRZR1v0T8|M8RB)IW=CHrBw`OK4*QRT%^AuC|=PxKhpmF30>Q{Q0plola)0 zu`S!Xdq#=qI-)&(6|F-!@V`rIbk&l=)p+cBVcgI`6#Ye6TJtz2oOnt35)W>GPi3o` zn_=_qvGU1fW$GZx*0fmsg1;S4|MNdr@0BG~&6DL3-;ncLxA)!{bn3^u11Y12=OI{T z7FQ?z^~vH*N6G4>#|PwA6cs(SJi*#$4UafI0IGo&OAn( zz*)5hyhJ&3=r|0{{%r$8JKm&i2lU{%rDHbSI$pTj!W;pnVr;=aSv>7R;jvD~MxOb< z2k-2#yku~Pj;+fJdRKFACpzql`gs35+~<7~2SMzo$Gd06>+Q|wW{5Lfo8E*uhnn4G zXC_l0oNXQB?@Qw8%w#?J+y!SGp$F*!mtVY%96y?h@M6;~&xv%7>U7P+h2dy+9{5d* zE!&sQ^vnHV5_-H;mmcQFVduI!(YJS|&d%dOKbaZ0mv`I2eN4j_U z+P(0=+`@-YZ}ea4Gyg!>HT|P!xI4TDUJC8I{##Gh_*3pVI}Eq^ojX%sTkQ5WbNeoR zo9&1Nkw^dd4?M3NjN*8{pX!8oy)h?xo>H$(9>a=u53Qd>+@uCDwcmK2dYJQ_YC4=A zJD%dmvhxM5J3!P6Kdx;u)AMyad~(v&=i@SEUD-uU2eFy1hYYYWA+q|vO`|(En8&Po z;N7GRp&oHi*G9;HZ{y~-4hd-89>n?jq9Nk_xI?@Hr{pK@UY=*JHSy~08JgmYM~o;Y z&gl)elpEh2;>x6=nzH#WP2OD)4FD^77_Y)D?k>W_8;o`~4S9KAMK?e%S_0vVNbiT8#b@=)}e0I z{}bgGIczuWw|la9;(=wgxpW+JL%F@&H7*hH*w*L;HD@o-2cufqdX9Q(pi4PxQF~wE zw6FV0QLkfNp&Rt3ui$8-rc)!1XZvXUGbe4L-RB1k!mQ=TY)M{x-VMOwcC|B93eqcq z?KFO~+tO7lONhUPoOW+4ImX5Sjn{Lg?^C5ydJg*}_C9G2`y}x`k%xVfdY^1BWv%$j zs$KNvQ8Cu1M^7t|^;7YTr@T)a?{CS!tCL0Z?D&~pQ?=Xhh2R@q=+KWnix!0!`_y?s zb~%c>~6azTqb8GY1{=#zN+i(2I`tkM~)72ku9c2Bm@%4x7#CFD^hvAeAKXCJ* z#R8&mz%txYE~oN@jH$k)-2bZtXRiy7tRdbOpOfB^ZN!MiEt*Nok%nD{>{7p9-vokfXl8yX- zy=&v1udmTEjdQP3twy@%kE3Z9)z)B?1E$@%`X02o^yKQv&?B36{#HG>*$j`rdFL6{ z8xC)@=c(IoW`brfiKs6|eY{K4=l?^~zB=?96f#}9dv{R8rg&Rj0C7LOID6gq>iF$S z`(V94w~k)WO6%O=QigS$+h_5%7k2HL1i?PrveC##mGwI4i(SI50}BE*T$t#lRoZmceHdg4B|O^Wc1P<8JTwRUo!}b=ZD{MaWPyol|#Q6bz@~H zCf)Mt@wH2Rq-%Hai3bEu7T%8Ia8J|{`NwSHr3yWJ?`$Ph)Wk-igT4Z@H|xD$I=ae^Hg{1sK}5bYK1J^Xj^hRF;hM>njGh z>(6+2GFSqdcGUcMcT`8`2D*&r-6G~1ZNA?8j(-H^&iyh}$-+zvqk5`+^@RKDL8)8% z0`SR17y+>idynPWb)!zCpQ?IM04*r3-Y8U0oiQN_NJ^-HdcF6MeZoV2bQE<1J$~j} z$k-w7oULb_)EU^kP2RW3Fgk%<>zmnQ*wY)@V>k}?$(d|*qy=X~5SSmQ&ujnQ-usGr zzkN^htx?geQt!E4EziH3zPqT-~<#C*!_KA9pE6eWstuwJn+j*Fu;J^dkt;Ks7 zuW(%&4A$0cclmqt=9}OQI=|qjR{)Afyhzv!)TBeDYp85On0wD3=s;Em7pbY@V_^Sp zE;LH+o)Itmzw1@=%Kxp-{(2=;X5($|oC~T1u`4FP`n;=>0N}?x?>al8tsYVrjCN{d z7B87dw^}=3nA^D_GpX8Lt!T1SHrTj|(fhZn%LmP??#zWRPc;13!6($8D6N}0@uy#P z=(r18?vF;rmR}rg)PH)Tc|-+cyWX(GcsOnhgJNF)Rh@JF=S#2uPRxylaZR?QS8Op} zCiQxA%4EJX1B4bfQpn7unn$*Pddd}GH*BP}MqQUte`=`HEUCI3yI)#$>wc3GT?E^s zIl^ibRUPjtpreG?bf0LmHl0y_+UD$L7<9n>(uCjrHp<`)dg#-8qhsIOPC@(Jm`Au(;_C?cu(H!=Lva&Qv1Jr*XHM5+%IY8`?={94LQ?ECh>&q**=_a<*zV)E{*YSXP8_U$xrIDRRDxHnM zChqJq2OQZ8rYGiZf}`}+g$ucyO=jYQVdPXfbDZNbsdG5+5pBE>#8!pxGyBL~G6Q)~?7jeGQBkHNV3vYtW zU$j8jhFw9J@{tmgizaZwyqHyP&T=$2XsFGPu#*!9TSa<+1# zZy;*N4HH!L=$5rZ;(I$jeNnNyLUnY@8fb@we8cUrr^~9-C*7GYgZ9(`+Hi=@1x@KN z9J9@^t5ff~D}nYL*Qw{~4Vb0dHAlAyT)JtwWz~E?HSXFC-c8>0Eln%X1>5j5=bD_k zUY*u=VZKu5-Ef0iwHopq;I%=z?NAIl#+ z!;MK{{ufMaJXkqf=9a447^WtuUCSY_a>E6>^z{>!>oPs6Fd|{H4eP67d7UmhUaw2> z`fM$l!zwX_ZT@A}GTiG&ejewEDhJ7_O;zdwi`q+$-Kd~dzg4Hcei*Y+{Ib#M^eCK} za_gOrlJ(Kz<1i%3-hQ$& z!P8z^H;MTGZyJEuM9elojtf-RTX9Qeo6d7%F7@hSX7cxuoXJ$A>tMQif{y9lm@&SK zm&XntkA6a-xt_9zC~G^sd-@KIjMs6O?#0XZ5j)YUBK7)OyNLz<(=N(F?{SwKxO?Z~ z44Mh9g`Xc!X*Oq~l4mD28j4bAus`%2{}UTpK*=KihWknHcK-1*KLwthx|ZEA^`aMh zXvu|R;oV3^yLSN#KYFA3=mHlyit-$>_wAdFjiLCa?MFd$mD#K0?cTbEp%v%rRBpNZ zD^$*Ni;WFkX`C|0wOZcOQB|uBtL=M7F8UAX*!Il>)evIaX{kon z-m$<2nar=S9U=+xrOwc&8^O$t2mR8jkv)3%e|~5y@LXzHckgxw30>(yT48+F+ZOi5 z!EB)+ox3Ydy*6w$vr}U}o0^;6?TU}O+Z971>s?~lMQpaMx1ujMTIFo8b%Cbzx@Ok4KegQkkg2Tjs9xdkUm#A{HJH8*^6>)j^4Ojgk3t z4xrkFc>HO@rz+w7F0oV1E$g?5ocb^R`JbQ5Kg*9UX||KwkLv8U=8-O^l~P3CLmNB; z!G`@8I$mhd|8E#2Q4-o``gxcHQTi_y!FyFT__UhAUP;rrEaT3L%jiaJUWKmS5tXkQK zwn*AA4%-;j2SuAyMJ#O_?T1nIvr{ znD1k`IOhjQOt1=JsG;4B{j?tMSM6b#~6Xd8JtHiiws*uZEwp9~=^ui!- z!l-T=Q8i&*w^<|8RDgilcoDWDE80}XO^f+%vp5&cbTeOX*53oDUDVPwl!UUuvJ_#N ziX_c3iDlcwp~&!G)Kp+h=yz13$u<`PCCV%cs}fy{YiyM1h92pB&MTKRm`ycVmSiGr z(l{zHaaEm0H5RBzs|?iN$fOFxtid2a#dTbT4PUn@XiP*!l;y*wyo^d8eR91z>lUdg zMO|T$a#WFly|k^Axs0ngBav08KFz{9%taKpVU?t^ZbaG&nbbvCNLlS_$qR_CNL*sM z!#b@KFrow$Swa>GYWokt>U(lkt)u*pRb%3V!8+%&5T#s_!A zR~KoB3c*_{*(PlXk}h(rPo}m@(_jVbNES(0QpU9f`W1sx+Omo9CO)JwV+ONLMrD`> z2{tB)JdHumqR!(Y&ss3gAO)S5b(zUT1R$6slVuVWO`RoxX$?3tid` z82T#`P+VNsL5XcDz&)B+rZE_3(8#cAD=E_^OX{pfLi1BsL8rx@bV_w*_w_W?Ai+R zC@GpkV#+`vZQJCsl~I`$U=c}F%Sb{Hfx$!G#6gQqDT6dhn>vVE%tcVnwB>WbxCt;D zO_M|PR&}0;xB{_*D`Et#tb?XVOOQ;QhDBC3MIB_Zg#5}ylBaDxM-|Yev?lHvs%2c} zsSuecqBe*E(PVWQi8Q0&OPjc=Fe(vaAD0+p4z?qUN|X(_jDT>R+g@y_QF#Q{7ac1M zz>V4j#Fr&O8HG)ea8JeK@mj~MIgrr0u!tw3c-E%HTa-y zU0aaTtq5z%$5Md*rbV8EFO*<_SymQ+!cCC~3GSZPGRY$#s3ZlHZ@zQ3;`q_YC zZfJQ9R*)4HxJJ~JHLzQjN7$&f5Mh+ZIbdVnmJlm7gnyb=*kWPS#u-pTkrrY{OXdK` z<`wfq8YT%GI4ptcFl9iZGN#-FA_T99tF{F0tdcka=M7T)nWdl-?9#Ao^ElkoWGB!v z0IRG-bri{%s;sTsP=-Y#F>5tIcNXOU%!w!gby`kvo&wx~>DPG}h&dk2Ofy4}BZ3CJ zA}Ml=OLF^y$V*5PK_J`$^wdO~u}CluO1UU3fO6UjG9eG~pZTV~pz*1r{TwQS2vQ*C zwhkb51*Wnnz+J(bAkcE)vor)1$PBU#kSoYS&><+M0mqK^wR8XX-H5^}n=fe6D0>w?I+ZENu3s-17oL}Z^}lMsH`%0&={K@!Uh zT2WnrwP5|hnR5Iq$-pdI0odIFafhW0pg|D*)(~;Yj%{Y!)Z49-HWB2EXiC7GI!gf{ zA#R{bL2JPONgzcLWhEe2)^ek_PjVH53n(B8 znjp$DppJ^0S!7@?O(^S#lMf{`;?x2dfsY2D;T9h0TgIN zUgZUtnW#xiP-`-3Gzq{8@Ra~tEvmQ>K~jnek}lja!iSgA1W|R3m30n+gN6m#4gt5x zvnoisylE;?2LZMj1a;6xfFaY`;s(LLlC(g$Sa+ZUid$Q_O-(fbg6q%)gqllsz1R;vfZ4z^ zxC*W_)lmDnTkAR+zNjJgD1&SqKt$AT(g5X9yd*WjfEVaFmxdYk1n8$u)|y=~-kScKqKS{6&6CeaHb2#%Jq z=-t&9@3qut()n(P{pOR2LISP^zLbq6ef0%{-wDL+-m&c?j;3O&lvCnqbOql;Q`IkPZW(%6@z{%eJ{|cT$~>KanRs#ZVOGP4&-82!UXZ? z-T~gHw8JMZj8QSs1R!XH;3gCeN%^7`pfqWcAi426@u~kAb>y3Sq>YCO=S~c#dl7m6 z&Ko8HlFCJ~T~#_W_3K3#4eLm|c`6BCCmj2%DjkTU^SNXsQ1z_8pU{bz>g~QoQ!jpN zAMXaY<^3u9CoLw;d=YT?M1V`GoaOQ<7@KKD^%|>|j~#!3mIh4m?$F zSHs409LK4FCXx!6aN~+u6o3i@c}Q}*uA#X;Xy%^l5*)uE2=0p8pHe^$Qeh=hJQ;KV z+JV%Zd-7xsU09+zMO|#*G@s6z-!`vLga7xRW|F>}!QwtA@)_DM6q)U;q+{KfHuoiO zkK?E?KP^EcLo?ha$z)LqHb^Q?FqhB3$tO?U#dTFwn-!qHV@#82nUqcBRi*Vye%9&h zhj8(EnPKD+V0ZM9>lb&O4y2H}E^##i+pn9N+49QV6yet(;~7IC=lE%p8ej_K$anNJ z>Y@^Uru#^SIEs3i&}ITMO7`6386$(iGsFkjr3Q@YD)48R1uCI)^da9 z9J>C#c;qy3xMh&n=%YU`S9NOz>d|-sj&4RNLDb3Z?{dM2=yRGlJH~mocrWk?y=k^K z0I>c^(5#m@=97f}71{dczvJ2?lrHT(`c*CyU4Xt?+|N9A;}v1ne8zI-P7}Hf-C7+F zbGp3lGBDFh>q>DK2SKUDoKwS-`uj>g?m(Nyoaf-tf`LBtH-Dsc4z3^P2THa-eb4m0 z%fwg5PaLCHJi+Pt+aRf+0d8XEs7qpgz8U5Vs?Kz#n*imh<3zKy8Pi&fupgSELM42H zp>`>#7uOYUFYhpn>;8zHgP1f362fxwtyptWB5fuBSysVez48-{RaP z!BHZjN}C(7!fN^5I^{~P*}CDl#yJCv>lBKd10eVL*ZOQhInP9eC_G zI~>phxQ<{4>=;V6K$m2B8sy^^v9z!s3yyj*`_y^C_r)}G7iq|@aXv9IFV#Ca{YLYa zKs}L|7Yuc#FY;P}Vm1)a8rpgAT?H%A3gOW-?s%p%&^9~O^RE1n1n@D2Sjt$Ay5aLahULt=pm9!dWS zF2H8(7ZVEAK>gzF;PF`%(rbDRtT zcx%YmVZ^#Tt}I!=lQ<~cn#A5?mqeZ7SdqE|&>5~|tiiP=1Pl22EVKo(Ava%6IozKAO{qE}wzpzQ3L-;ih_8H)VmL%FoV9Wr0@SMs}3lIg`P{aXf(a}Du z3ylV#9%D@6O+&CWO^vdx!`@z*|aI3QJ42F0Ex_$ldHG3~lZkc#-^~NawIp6=Da;g8bsR zW4gSvKal%H62v9!rI9c67=@JhGj)y-C-IgMKNa-1fW?E->YzXCgdJ`a=)xDnR_1~R z5>;jws@b{usn}_M2Lnz7in$6sl9c2@1Ie{IIyy#$x+>5c%%Qn)JV4e8nmFL^%28Oz zT3GRXrLtL~18kjvRD(01Nw!V_96U-Bz_;eMk#I-%aLSQedV~TFUtqI-m%oA_-Ewj_ z6L0%%ZP?5cZ4hX}KycV%@^QXY9^MxhL%c_3tMMk{nXdPMRV_{#6}RsOm^Wu=1pFAd zpavkvNKlRRN+ZfN=#oFvfk7VN4~0*yT1yYK((^941h zNZkYxr)NsJ7vf$(Vw51Ml#g+lCAka=49_jGh&=@w&SFX5=60@84+*l1oL1Yy@yv>- zYpACkgzU`3^NN@W3S|pt!!}h(fih~X2)maMSPRRH7Pw7DH{DS*4=^}bkvIF|{mGZ% z5E-R{4#B=Utc7)m5;ZV9Ra`_6by5cLEQ>D!D7#i#8jzW`iyU>ecRkxQs;lhn&&#tq ziN+!T`&e@0%|54l!`7cEEfQFOVF^U=rX*hcns5E!UBDwpy?KW2y%lXm4je2f`%Arh80Cc`CBJhw1>jRJ4fgWx?pgwicu0 z3E23IPS~Dhe?6i~`Kgz{6(bdH*T%y_%F4}2BpZ(80FNij(cEh5D6v56OpX%yDKLyj zSRUNYWi}Nc2#`QN0e1Sq6$?>*j9xEy`ZW$^;+jSLS2i*EEE>;-(Ss52kq_}egT+0i zwhWpOS2inNQ=G|W1p}g}|e=sm2twYx4m}%7j9}z_PKMFE5%JpR)Hji;n5E$tHI(t9x^}w~M-+`dg$~@B$p&5`RPR_C zWPf=Q+Z~_Jfl3>z7ru5cc^Wl=j9gyo9}nbfPPlJo1$%KzJh~*&*C|D`{K7l!4uMHP z`En*5#F@(UU5;j-KHtvm^#i`A6vh*ncujt6 za4%1Ncb#Xk0iBcD z4zY~!C5=_6=UI-dF$hxd`d%rA$3%!eYjBOMr%YhOXjU%!O5uW;cP9j59Y3;hvmcmb zz8li2GX$88cYBsZlgEiwwPvlfZh;*-nNLjFb zfA$I#WamR=h|`nJIys`?!!Q0v3a_pDHbZ*O)42*zrCm=VU;y#$?iZh#ImC)r8YK4- zaUyX5@pGnDYG=j+31cCjobcgPHFo3)7wK~OQZ*k#P2aIbLWUWnMu zUgEzJX%m0~{5nXXu)h!K7dFB??r zP~^l((4~L#YWO|-rSc)~06k#kl1_PCGl*BjmilTPdwWc$ZXQ=xxm*mpa0COCa1L85 z_HK=n7K-@4^N(mwYo&?3`YV|Xybi@x-NKf-6S@H%8e+Z?k~2CNu^UUc=M;tp9&5*D zX$w2*^{O(}5QKDmLM`+twj$6A9?YYCN?8uM4xMg#O8R)lPxNPaP5>@yd^mjh0}~d) zJ-g%#faVLJ(Aw6L@{$Bb3}0LK&fn>cx6;PKhF4SiHd?zHj&1-^6hjI*AK_(?sJB(XYZ6F@6soT)=L! z)U62hH|TiBcNMx!?_M{YRvVj7h5>!7*U^BVWi{Bu6#SGrhBO@Z`eCq|HR1Lustms5 zCbJ5osn2a#ZvRYzUlK8}paVjHVzoEMF&uGLq^HmPc(QyS1Nna|h(=&zTy8rZ7R}m(;@% zk%zNB>#BY`T-8!6K4cvg4GxNqzMTw;)R3jj*%K;7 zPNC8gyS>Q(TY_BOR;)#auk98&0BZJE!-eVoFRNT#6Ws~iP7&^}oZ&EtaYg`8s5|L| z!`Omq2 zMJ5f=n@WO+EZwIcX;W*wqj)9lR*A2nOwxg(O(?k|r0dF9$of#OM)9&y2^}8Y5ff*o zaf%b&`zV$E0LL#rHwKD-aOAt`knBw{q9IPUDZ@8FXTtsM0P?zWQ$29`4B>iE#W=wR9|NY9t zc+D85&C||!r^XWRbIUtAFF+$a2C&Qg`rb?n^5&VX%^d9xV%fa{+h`X>dVX9(ompJz zGXfG`R816?7j$4xFC=AbWh-JcYRl5)C>S)q^C|a82~IF#k(B6;=j>}PY&wEHB7zCH z*_EOK@^dmPSt;i;bAg*PO87YsBW)+n0rd0nz!__d$QhaLc=rVP#+U`8++b(mp6+Sw zBg8c#I5c`o8Urvk{n_fL?n~ zeWx#wQDEa6*g2CzK(Xhs5yuf_{yyWD*DvipD@m$cAZaq=0-blmG4#$k^OeLd-(M0 zV%qI9$jOr;(c*7juL5#(adXl7LDaN}co z*Y$MwH5D)BvU=J9FF=zIz@(Ii5^5*T_eefnHXpNy@1^S$ugs+piR-hCJTbdHH!OB` zK4;;nk}GD$jlAIvED@GmWhGvZ1~XSe1rtLl0tm$(BS7i!e+2z<#~|zW=dZg;4c_i; zMA){d%m3j@6Vu_u-deJ}siT6?Apm6RTN+G8PVi;sqZA=(L+7(<4oAcE{6}XB4C8uR z2K2L(O!+IUK(g}|KRsPy3FJ3*RT&HV-(<2$Lseiu{L&pnuixFScgwsHx?xMtzoUv5tVQM5f@RA#;W@BRnaGF7tT2vEDs$<_>u1ihzx_gPSv zqwLCqsO%&FhYxBlxZ&67nVaAR*i@fED!SlbpI$rj zPk$^3HtM)TL;=qj z4Qyuh<#Nade{V&dQ%b3V@)OSM(DJ$hgU^LzSCl!>^!@TEoruDxM6-T`1U;?pG%P(- zoU35Yk*Qt;&*g(&xmDJj`N`}%PZ$L7S%7m^2k-neTi`XJu7v@BU`#fN;7~3qEdcz8 zj{t4;4?#6*JbVRn+bED;on3+dY4MbNg`mlJ{ExXo?U!oeQA^n~mNKX>jXB5)9N2uO z(T9sdf$tXuTN)fSpePqUSk4S&d)tv@)O8qqTOxuENJVK;8RTH4j0%%V@f`(MK^#?D zQZp9vQV?3b#QW6Ulnij@V+P-llJ?b$@r<6;DGd?6&D2pRgC`oXv|bs1lVh+dX~ZIc zz(=ehY-o*|?3UQ=b`Q74N3Th$@%;ENn6E3?Hi45(uP}ZOFxBZiqzU6Xk`T@V-J%c! zyh0%;?yAAH8kFw-_{|IAb-f8HkB2F>ZaBr?OU!c>rM)E(dn5(4+QH^Y0eAfH<}}rw z2y>(Z*!!V0r%>O4zFSR zNZ++n5_=G;wSJGl^TSW}{bLnSrK=R2^Hv<623#|II9$P3`vh3bj>oqFyF=z;Wttyo zf#PMs+MzvWjFc5KJU8Rrdio^K2>8ooDTC2Os8fL(ZRHJ;=1w=XhFX{UBG6lX#t0|2b}>tQq&>z8~78ikU%x^t0JGXVweY=4(yCHd~cjetJ)= z0{QJq`w}S@_7Na37<*p90x+6Y7#`!g%dMFqJ!x$`%%zS&QKq<34&xqa&?<1D1m692 z$#>ULJbLszjS8=8M_cg=;Oqq7=M@$=myd?Rltg^BDo{WY|AUjV+_A@4pd_VYeyN!0e2LHo&{6-;fsC9NI5 zk9)2mWij)Iq8d`@L(q&_FTWxMQp*BZEE&V&(Gr`-b#;CmXkGbFS?UnjsB` z4K_w6i4t~_5cZ)RA63$)NAkV~89N-|CV6#yY3uz zXo(waIh*1HC~ncJ!mF9GAWVNeM4vr@mcw4XDr1xp_~a)6^g^g;tW4(`HbySja+E}1 z?3RRam~WOAMZTr1d;4WL6KpDYcAh+khd6^A`x_hElhSpv>Kc#n@tP~OJOWRHHAZq+ zSr}n;b=;-7+OOsn@-I@#K618tmE~4n~vH z{J4!fW2tTud~+Zse!yD-QkHrd>8H0Sq7aRlmv9$nP+zq2Y|q+7pa4|NcB>$kR%;fC z=XE@%C}%cpipwVqMe;%~tntmUVcpB&31X)7U&p|mHyd3wo^BEZN%`zI_i>!{#6rDw zru9tg14dJZ4ZS1%M-=~*r)S8A*1hecud+H+!-9l{NJrs382B3^ zA)iHa0}&s|)ZapjAY(fK=6h+upAnx%E=mDwS%uoxY!eTXq$?=PW!=#o*%l5tkt9$8F2xm8F|NolI_T=wwsKDr3f zteIGA5gGeFn6IOg;?OR^1LI=8%5LXP4YdW}om4vMo6X`IU&adfhLO(FyT&i9G#UT30QXT$JoOTt)( zygi_#9S+)5b2&_#xIh``j_mXZ1S#!ovPv=1w}JU*HfxM|0}W;ypK|c0tZ4*J@t9s1 zi=Nr_awX$h>Wc6{G}DsazjdXh?p*-L=wprM@f&;}T@3G4ODD!!viPLeDm7%<^#%;} zu}&1y5cGjQKqKk$iv3FK1$4@+!}UU%JNz%78kYzpERtm@baZExZG|Yd|8l+`7W6`3 z+{o=*lFXpK?dM0yAX!3gqurO6H!kQR*^DnkCzm1YuI(3OkKG;7|)U;U4PL7fz z*|@=3^3YmT9HQ|GTXID9yj_)Qz9|V~NJMi9KC@t2dAu@NaFDve1kf41gdGa>*F=f{ z_!@d4eDAZ8vGKWBXM;)gYZ~qf<@co1sixv`IvRqi9aQ_&CRe03b?BLh_d9(D@Rr{E zmDQ79N0=KrM_6=1aJ)lToIcms+81g3`T|?o%myCJG-QgUSgrxe%yb%!M#g*o8wzxN z=r`>1Lj9BTF4A_$8i|V4XEfJ8{+#38S0IBKg>)8iJ&zBUpb+}4$?@=C_xt$m<*hG! z&psZed}EwTCJ*Ax5LoKu81LY4sTw^h#0^@Txz>+4O#U}J1ABmy7%vt7L)ni} zVS9INyddjVlx)ojKnFR9D*RxFUw)uh0>qL|*;qPc(JqvqV_uzT z=d4rTwW?qnR~Di6JN0>Kd1d*o3TYGDj+~(;a+v+o2z&a_wf$X+>dyU^Hu?3W%m1rE zfR$9fJ_W{G-V0bidC|saIX91KN*lB97^{li-%%b2rrOtH@-K?}t<4heRC(fH);$*V zzTQzeUkq%1sRS0k8>+7Ww|?vSrqhb1bYAZc>$DpPOG~}6XJ+`Et@75IfaYC(xAAH< zmC-~dBwMvuNH-uAuOVsHyYh@FUBY^jAVf|EyHVnx-uEsauoFw-6nKB}-+B)-LfQ!nIS(NF~!Uu7@?Uea<8Wm62c&y-I?~LuMsASZ8EqT>n z>H(C+ecLITDdhVs*;b)@zjf2YJ5BbhG5_+XNzYu8=$AgwA1Oy-5nxk0{KpP#0xrbA zR7eWW1~55=5ZU{AfemgZDw_)X(KOZ=hH{uzwzbBdQgC+KmnR*bQNch;()hW!jA9<{E>gKOro@;WHt)bmw@`yp$qaIuQU`?#;w5rTWY zmX-8;kJActuOk#NUm-rWt&bSntxdo5`l>Me+4^xT{i6#=3ZeQA9upxIhitkDs(08@ zFc&UW00SF2sX(k-82uN@Cy64ts>Ns3hf%vY7rV~g~!Dg2qU+tO}h^Vp?t=C&o67=Gol5Lzo(?lT$x$rhcm zc+MSn>yV@VNDR{RSLPZOvopL}b}Qyy(ICP)gvyOE*Grg?h5edC_n(t8!2fHt_R$sTbPB0L*otYxwvJv}KHj;&Yd_@C~h1vdU zEu0Z~Fkb@eJY*WE;aU~7Vw~~~Zc-Gxi(XfS6g%?SNh=U1*IXP&y5VAM#K4p?<%4_4 zAOAaDdK2`|IKYPk!fBwbBPrlJ&iM_4(r)*3IJvS-VZCG1UvK4l2jqV&p%=)reVr96iVn0)H?4K6R!ADn zMe0J72W7(1T`o;c*Zv##{S1~AFgJpqLAv51z*eIDZR;oktZXg!@}c}MNU|lM320Jw zm{>NF6t(*PPEaq~e6jTKrYNMnLRF!Fy_c*^8HYxO(`@%?SLDmG*~MJ|*f@Y_QI=-g zA=$5a_QAQ;W@L5^s)3N=iFZ3@lz0?{*w9>V_kR%KG0@M6%4-;g0$Vzc_m2SaInQ;&z^rO~n;x}+z0*I1};C`x|#Y25{F5mDxnqN=Znjo`_D&p1D_(t+^{ zqs0sVa^$N>#g`{qJyWC!unV&i!6gg_Au$1y!vCoMvEO*}*Bx&+E6*tYi-fh)C*AI9 zQbze?4jPr6kzuO~ggG*CqQwN6Ef_ET*rF}I5~`^^@k!N%Q-qqfUPN}~v)`kz?By-R zDt{~u*!2M9ttriS2@qo5MuHY>QgYMd@`H*vWE%Q&v2wsq(xWrR0UP(PJ5tZhPWmEh zvG?swewpY}Hxps=<^twu;!T6zFHkkBcTZ+vd#r34-HxhiYAi&txQv+qq87%!;r4#H zWC7+WDvvRzsQs5G;88!KR$`U-+c#DBJb~d!0I~78$RiQ)XDZQp`9X zaF%m?;kM)MOz$Sru0y(gJL9_2KV*+alfxb?G3zDb1Gn8yiq-s&$CZ%qP4jC-)VD#e zY4cTA8t8_aGr|78KfD>c9`w9l-*}hS57yxqt(2{=Ai27piSsE7r?cI5xS&Z+HSA=N zlm1tMBM6VPVxG214CbgUghj)y8-F6cbqGmQvx5YM*M$FO?2(v=!--0p9@X>(lpWOR z==!v#nrNE^(KUUtNQzR(y9CMqA;Su4%2EA!w%s8HXALQo+YAv4T}U^YT)z4O;3w51 z15wO?~6$T*=-lm<&O>vWB$jygZgec8 z+-5dbaOL%umlj{8E}GH5sX<85+p%SUoZhT?6mSL4AT#YcJ{Y!|+ad3kDi#wuJ3jThp*{%~)dhMvB{bXP=f5@nY^X zSnXfaQM45E-JtW$Fe0^(z*de*F`+%cZs<%+pPa0BAd=s^7K!V zIQ1$+tkR!5l~Sya;{&|OZ{0IfTcp2DA21j!Cy89GEL)&O(YGqnijO{*;qJM-gg(gY zu8sR@O9XQ^(dLQQr#=|pt#iuRJu_+D4=Mio{b}@#7&axFjwA`4mJ5s%zwc8b<;Ea| z{&>0fT(x|%0ax<%5o_}(7CY6Qhud=RMJ@uC4mlzfU2Z6voMQkNg8&#~0K_MI{3mz6 z$pC>8=>R(E!fpj*$k~~7@9OI})OkKgW87H&F=b8dBQbf8S7XhMcn>@u{uwtNKAcgp z{(;OBgw8Cw{r)Coae~lQWSn65?N>q%go(qx%bP`7Pj}F<(or7$I(0ya)x&wk*Lh(j z8fA~sz^#_PlS%>VF6^4xeedDDDN!UAT?Bd)@4Hn}S6L}Sp(M+V*XPPbcJ?}?zErTH z0iFUrqX^))`-IMyCoy*nYF5Jg>+U8rq`cfWsErjOw=}#;S-ckOn@3MPBva{abTDq(RQ$Ks%$(58A81KKT^UR}St8Kt&WkIP$J6OcqiqWyNXLSsm5)v8|neDbI zoQ~#OiVd1(FHX%tGOAF3X$x2U(`vm7)7iR}|6bkd2Ws>?rE*o@!uFD?=Q2ivi?G-1 z?j{T=5QF=1glJ~oLr;=?8Y}5H`^cC2R}Rmuu5?>k9Xs*1RC2ep%K%oh0s*GAF`(l! z_mTy)HdUY6N=x?x!`{jeuUu;3rLfbbuBN5sd38p0xwKwx7s+}zNE``iVUsYj{V-Wl zDtyge5;QGji*FXDiZL-@o7&n;G1r3hJ(qYdMn5UoJ6y?tZySYK`hX zE=;|3Q!~s62qu*V&Y&zaLXODl65F1*l_+XOC&UK$FHhEK)Rb*sa27PP0AObOG39{! zPtSB^R{Z6VT+yGhKXkn1tch=g_a62-fvk0IrIgWD_1f+Ft@Baye!gDE38Xk&pb$0` zpveO8efc((wS6h zrD~XR(`5hsE~=+_OmX+?M2$BhzzfFOl-XwdPdXA`@m$g9UYEqYpYiYG?A+u#I2k{b zTY|(vkBp=f;I~Av1`K?N`-M%TdSaFzF%E`b?lnSXnL>5zE6aPnuwiE`T+#D~KlZe_ zRRnhX4 zXg>+S9iBv;wrRSdjs~P~HiqjT8i@AN(#2Pl&zUkJyEMPZCo0iuE^EjoMN=+WNxS-m zA4>3+)9PvI$Lvb#npl&hd@avDE zi*#gJR(e&qM<(|G-7>Q9smc)Qox)g&{cX^0B>m1nO%&!MoSDY*HVB=T?VEph)NqlO z*K#!4|G-I3>=cs|2^fXjX`{NWM-6fm!-9B4QA{Sr?ZnXDWW2 zEP(xSqy6R6ubBR~o{V9(5q818JW^OnZe}~mft6D(--1bv=KHbsoKi#k5rqf%n<{Bj zm)nWWrVzq8!2^@HvQcNzKL|8WPL-3;>6>vA5#rmfDjH1SE{J|-eVT5l<;TH45I$do zx!yw|G}}YnTAt_yc|C}b12h86v6Y~LvJ&C}!r=ffCL#@+z*@V!=FPelU&WCybn5P; zt_ir!Q-5wPhPrVHu*#0BquC@5F+F8hb1 z-QU}x5b;NydPE6_MNTHm-JA$BT$AbMe{~^BX_|lE1G^u^M5u*A9XT=k84o;^YL_NN za9f{1ZRt%Bb)4E^WXJ9Upt$&xKO-U_hkJn%Zac5sqB&g5b_FIChQ9cW9?Ij6>!SS$ z^$zfohxZb5B1ilVwae)w;rVcCupGOq^a{W*)167C34&x4vgOpZ#t(<<0}nrlVV;-A zqw$(W{wony_U4}1ck@q!fp|e4FY+SD=`>_#d)=Jla|t z<=!f5D0mepnJ8;}3D~bfKi@Wxop!M^}&)_ewtTf)Xu~!5;UhFv;xVqWkOl&IWC-mv5Ih5Yn6O_pRe3;${xJJqpT6(GzH#iCr%{nD($Xttwg$HqAWU`m7#h{ke;1rtn9gZZ7&&Jx$J+nvw=}TMvpf`IjQj-M! zoO5Ws?aFvs;$>uEPZsGm&Sek7!Q+-gvV<|c_$S)SGaRfQuQbDBpG;9gJxq}F9JhNj zVon%-a@9w19^yPS+#4d)%+Q$+s`^VMP!t5N)>ns{xBYoG2E1=*MRgdCaC#)xl>?*XNbCFq2p zHy@I2$=(X!7cK=Fm{++t2=)&TG@J|HFZ zFQ{LN>mO}Ip*%2kTX;IId0=yY02xrTJme=ud!%dDc-M>`pGcJ%Ob@x{LH;kI$D>~m zq&Gz>eDV>-&oxN5t&6i&KBKz6Qrk_ViInU?m&Zm>4c?>^*Yqzdnj-~@On$Dz{@%#> z{3l2Y_q#X+TU3{g{)xv+j3b7QE?Mu;5)7icNWgQ}VeMyUnP=s-eG>y2{J&(OsGU?ZL#tlx_;q;s>sYAv;@br4L-sgkIfclub88=~8b zu^`-K^P#wjdhbYqsAOVx#IT?7g?6ERQdC@*p*5O`sqA*hRtU~CSA79`(Vi#akP1|` z+tCc$M4>!Zwgj~MWftf6qn<7n;v}nr(6%C3B-{*`f!CSe%E)f2KaxqGT)BwYszKZ) zV=WB(da7ogG_o(n`oR6F)%`X+gh@go6e4bXmMF#=u-TC9L_|j?Xf6mb69EJsrl0pw zOg}#AH&wmyrs9F-nS5$X?+{v%JEC7CEhkSJM8ugL;AdO0636bG6>bs~z&NYTgkN2S z)pgok;I-tOOf^!}Jc<#;6$PuVp_-9W1jSUM5*eT~pbg~icuNP=a>dP-#9=i~l69!8 z3r4+}MHTlh`vRz>{*DDf zFK+GPgr-^vPRwh|3C2@7GN2r@|t)ZsC+HmAb_!=9o>W?)KsDb92ZQ z+0!7cXo|sSb}RDR28j{>MwXx3{rtLj^>O4AePCrBiXM@TUv#9JA1Qk2L$wr3YA9*s ztr#-T{}hT%2W|^^y&I@P{&08@rPhr+uPYk%ojSb>h{67%5T94@B&g7MAgXfAGCw)% z0K5RftMYHgWfwa+x_%k*hjmfZE+-662oAVSdBn@(%<~IAh;Xey%9UT6ij=f5h^@l1 zO~*mKsr{fzbM%_C8Q(SP2?1e-qBP{)meko63Z?i709qQ5=s9AAOb_kJ zLcCl5a)hox$CC-8KZ`bsJ8Q$G2+Qed+q-4tKH3v;TMbPlk-s+_Y&2ipnAYz|xqjA} zkrhtE{LOn*sA}N)l&SYZ_*w4uHtL%xQmz!sa4pCdA`hC$gu1Hu!#%b}wbu&u`Pulj5W^`np#9ur~@c zS{luP>8Wh7Zaxal9W{G;yahlEbi68@y||2_YuNrZ%Ui2vf>D2)9S2`S{IjOJ=d9>z zd@#)!aK^5gP|7?+3UGmYO{vdAOqY0-p<*S+Jaiq5!z7Ocb|;O#7U*M{`!aW{k?wgO zJ_m%R=Mzd!g>fUvjP@J-f%CFA-k1dU(;GYy>i^Sh^CoyA8u|>r=|Xg}E0Jg06T7J& z(aJCNSNE>NU{r|IP*CS!^zW*`m;3{YXAkXZPqt^-Qp?EGEp&9NPiN9CyOMEy$nQs0X$<@d7~J)+)LT1M0%Q|Moj zJ`6s8a^+X@@#^ZoZ)=BIKlxRgL)r>7(`-~-UQAUwLo6!9^a=r!Skk|eBH5PkSGFu_ z+*%-VHPX>_;9~7?JT2YWEF@FS+VVE8k@c~z^%N)giyP<3$XGfNMl0U;U7?H=HK&I+ z)3lq|me=SxeyrX)6t78FcIfsS*MJEru@7h_3I#3HVutul^(P&|w`sb7(uom`*0M6^ z9JGH2miU`od;ZS3^dxTyrAMcAJPP<#?7Z-2InrV)+3#|AV@V1dI{eJKU^0>2T`b&%u@4?!8(d zM;>}k*oTGV;Rd0J^g+j`MD%J<=f=g)V}b?%ZD%qe0Ep(Im%4uTQ!R1(dEXM_CTEPz zuhvMuimo`1DUi+Km&1LV5II`I7v}PK&a4$V4!xq=%1TvqJ52TMV)+B13r}?@p~}tg z+ZJd@`3;OwH|<})zu37XBLlA=!0uAJpy-i*6_57I8_vI)Sz-Wx$P{MBHMzPz2Tku91aeBeG-hV%1^u)_?7-ptzO2gBIol89RZ^VvB$Nxw#^ zd=J+4Vj(n)VqEhacUb2Oxfuj>%cM4vfLKS+?>t);LPICusy5CBLtLyXdUl{-0!HncxiM#>O{tN40&r{+6a~= za;!z;Y~poq-Y9UXwh-N#F>(x&D5X!(r*RUW$tv%D>}==Kx5yf>yd@93U|2B2u@WMP zDdp5^SR}9v%7Pi)4Acru30-1g^YEw<66qdtyM~IM9;dr9rAmO+fv2hjLzo3m>zWr} zWLIi(3?uX!m=5Gv)eUg1E_f$(fL*)HrY*ruOANq9OM>^SS>U=@0KH(V>9k>W53P9h zm!S_sX`c)7DoE8L!hl4_OfJ2L5@v#}Vb~ng3qh|eJ|S5(+R!Tx!&F+W>RfW4cN0fw z9R`fMidXCKdSN$O-a8iyzV*>M#Ch!t*0BJ&)wWj7vjR8E&X$VDtu=5O&wU*0)zzBi zJDEMp(zvY9-juKj9_nbedZi9flMQW}idD=&=e)H_8>m?ph;gPVB%KSqO(e1s6!W`< zts!I~_4J|6cBl@52Wk^?Qqrexjp5aPjg7>A(p9`AtL9}*j1We%guf71!k4_3$d5p3 zxtrOH3a}loYk{f4dzBfs4}p*vQ>U=w5I-7ov{lhCYp8B`vsoEfqMF<`N}mB$4Rer| z`7YQ>1O98IEdmqD1n_I({bj5H4H9c0mSBBCqU(wq>A=)~!C}=yE`Ct5* z|0|d?uKUm0&jrjx@i(jD7aPGnf9|%ocC%XjHM;uO_$RDWT0MRGVtu=^Z{4rZR_=iZ z9zA{f^ubmhJ$drv;gxdwTU*o8v<)C^{t}M@8c0| z(o8=8clh!&3oX1eZ4}=owz!Gt@M`s^fBtXy|FvVr>h^3H`Jevzzn^s-p5-3->gPAD z1LHUGRqlNCczf5r2wD7;!uar$m+{hpX&>L$TIw#Lx%VNPbDkb!fRCR1b-O$pJIpsV z|7Lx4dp><5KhA-z9<4%5xwU8U4|mXotCQT++tbZ?d-dTfLfDQ7R{f)Izq1P0{ifTN z28PjfG;xRmx~3j?uRT6}^Z@Tn!R;c&PainQJF|xl@y_fa@=kSDubx!kA9Bv4)%ubN zCV7R*k6)cnO9Zs=VR&TxHiq+wG4SZg(gVY0ZQ09PPk*)g?bR*oQpK?PVSURJ{*~`z z{CMoP`toI5+2JiR0KuE9dL&_{8#jdtT-uF~CAes{ZqbXa*BbXz{L zy18=~Yrl$})!Kf2eR+eujU5lhkXJ+2Tn?$b^;oSs`URb|1h%4=<=xH3zUH?*<rV1eVmRx!J>r#)S2@)2hTUI!Tv<4t zbW)bP(f))NUgiZS(I>9`>9|$==yx4?wj$xl@vWT9_x5DUOnIZpIDKgEIR3oz_l}_x zOTFM9zLDlRVD;612(CPuriT%*M^kN*>FY zdz-Fpe(5uObD+b%A#CFhR}3!fr!{$!3v%5$iGVpC7-xSr-?_8>#W>3YJRf%; z{~E@t-nJ8cxNW!Nlj|3l#>F0wWhW(m{rDAR@P}95JU`*le) zR@XB$cCx*)H`|xjw*!4-g6%%+m$2=o=`_{AnQgVc~`b`cHm6NcVHthcrc3 zeUDrTEJ*l#PWtUG4Gr<@%>$|S+L2>oJcTCy;x}Eaa zU%ZpylM$Wp&mS?(&GipV6z`CYQ#-*?`DZlJxnaoT=>Da1oV|9;3p$6F*md_y~`Yc@|IzdzaYtc{M{^C`Bte~N=_ z`Wc)4OZ0O5u210Q&)a(r_YDc)o^jt#0vVQyGf@JUO8(PogHg*~=|F&mpLT8fB7G)?)x9`{Vyp2vLb!* zm+WCqZEc4g!DSZ@z~^>-d9(T&LLG4G2LJu=&C1@chR0^i@Xp`&8!#@K#gP9BqNm-q zzI&`#d}aOZ>U@25k+J2?*+^3_ZB|#qqkB{2BjYsj#5kSpga%rgZN<;>9{nOBp(lgC zXeS3L;SeoscWL1sYB+`-{xagIBa6eZe-=rzi`!>kE&>1f0}IH1{y>KQlGZu?Hk}Z( zFJ5fo3)`Xj-+p{`TbGnRf?tNXS$|LP^Fj2`k?taX_y+*3rvzGGY_4yw{q@DUzuWMh zsqB_(KjQl^+n>;P*+qJ;zK=QoWhGbZE61#0G;`0U&ysj|w`}(1(x%TJSlIOW1Kf0>1b3bPG=uIV z!<_R1(qK1X!ZrIJPM-fmmjyU`e)q#!`~9NLJJ|WO6=L~~4))KU-~H^TRvsPu{N(A& z*6%uddiS%R+VTIYlM&Cs15Eym;rz$qrelFnbm0OC4KE91e*BDL&%{pc$Gu*yTrNV0r8>m%TY&CPYor00*G zJpbZ@|M2vawU1ZZ__UordfdFd`t;*3SD!yyy?bN9X;0;O@#qP8bNTcrhqex1EgepE z{&Y6L!==GEyczw@BW~~HS}O@5 zS?y)qVo~C3!TAm3+f~2R{C(uvyw+cE8riMW3PG?Kr^uM8i5n^V1ky!(l?yS?zldEr zIb{9p^VTSve6-j1v%8C!sdem|KK7+{#?zPXG-W>c(}mXOlNy`_vnh2M*t@Q=C%q?a z>6`g@b|%vL#-6mwY3|$o_40Ak@+sHgm>;G>w|tg=-2%M9F;sip)t(deGalT}+;zx&=|4R#)!46AKucU&z+V_f-%h0pu(w(SjkbpN2+#`=e+ z4NO_?m^LQQ&&Dw_=usmX9^Vt9-}&T|Ao?(!u2 zMb9_PnizL7J4IJrf#Vwp^2<}0V&Ik~o){(EaD@D1I=D;)XWj0f&fTBwnL!pEXCw3U z9?xz6R(6{%@%;8@F#W#AeUJ6u0Fr%L+Gbxe}=37$#Jg! zCsPygJZ?6i*T?bIO{Y*-Nd1E|jPr9|A6f0^yxlsgPA8O}BeJrj zK6VjjYI0OcqhvpFEcu1UgF4AH)XMEc=PD;-3`RG?({4&F)$EA>w#ANL+E>S8m`n;g za$+V!cGl?V<(ZRL>mtse}hzXKYX$y_}SI}$F1Z?iro=p*jxz5>U0M1e0)COe8o-`#9}eR_U-o%PDFshN=Hd-_IV ze|C~}#D<^jN%(%>4=h3JP!;uTdU*Q%H1fPS;yL~tarSgp9P{wY z`Q>*Q>*{L`;batZXYCWW*IlFW)#oW&fi3dupF4kPw=Yjyv2-#MbPqHzd34^!MA|(x zhT+M13$4U^;N=$dwC@eLKcmO}RUE|Mv8&k1xd%p&A6gjscJIYG)Se2qaovp->#LG1gfUmyCkghkc`%v4?sZCc+SDAB_gN+_N>HI8by)O1`C#@H!^)`L! zKb=N6yS5Jf>)tgpIAB@|`tZWK)nus4o}2UO?hJC}`Z`+&Iq0};jpcyIgnyg4?mzCh z)W&ws^E`h2?UsW(JLcIBJ4e@%@7OIvyhsz@LZQ&jx_yIJ>-Y&O0fi|Xv&uNPZb4vE=qX#UhfGxIau-PdOa zeG6~*;boIli`^!MNA9k^-QI!bx7)+R=wnm&8kr{lOgmc`((F-mdfBF3504%^5{+K-%1SM-uiDd>-SxKGh(AGJnm<}+30rJR6?5k1NJ1 z)WlsaKw{cWrsFp@u7!Xc77Jq0iK^c+QW#RR~~r$(*FMFXNd)l zeDwYPtn?h@v3Y&;Xz41%eHx{z%jbB^mI?zk6wUyCtuZP5K+H>$Uyj+w+8V`*r)~)D_S> z4fh(#6T@O}L9qF?)Bct0B+OK^^WMqHNUZ$6x0Ok2n;cKN)aaupR8IafOq@4tjkY&P z0K36Y84f`VhlV>WH$C7OhlKSXXPaN>0{1>_oucpSRe#oPi4WgiK{0K)Df!_a`S(wc z$Clfi%RCrpBc4#Um)+&Z+s8OG%W-wjjwcy+R~Pg<{q5KP5isZ`z9J@oT1J1+?O^=t zbyl|dZt#p0zZv(~PTE-=n!;qv^hHq4qf>j6>jQ*#K&v0^Frjt2{W5MYiK9PdjL60H zi_A!uzk2-a^UuHRn)*Mle$27Pyj%6=hNX^1L4q&e{SP01{^@fxo2T2h%{9ohwZ0ma z{SiBq-#K=NdK2VFzIA9$!@Ssp*sTk=G^rmH+n!JO)>+%p>AgcZnUCT9=dkF{Sr{0jd45Y3he2e2@s!K{obiCN zc#G^r@6CsHGV;k7^>Mnu>hnQ#@^O6Sd}~v@eZ6|;omKvECIfSqY-ONxAiuB$a(=mb zeE#j-72kjb?c132kr?0Uf85{VK~WwOu%l3=EW#&kH*fVIf*DwEPbE?}r=GsvA>DU0 zeE#eXoJ<#4+ zp~-jdpLQ-2&yQ~_@nPQGG-p$vkrLSN@&-E^t{>`UMv-F=(~mppIf~s$Gt^(tTLrc~ z-(qEd`nRiRAHMtkZ{B_HlMh!PeE9w+?>>8%0-6u?V(0uKUcI<|x!3Nq-+uPx$De+< z`tUz|`QbCZ(a-b$yn6QG`@enm9PO-r_xZDb`tTXP?5Jzr%Ht2ek65aHc+;pU`oDg7 zdCmOH)-g5&My#K{`W7O0TsN3h*p0A$wF^t)R7GJ?^ z8~T6mlXw5=L$&(oqmS{*C7X+{ZqU>EcGwU%UJCiR_sbskvmN%zeqk}8Wq<7CtEDi{ z>DLQh*y-0f#Iq`Lh$sFTLp-Mep4l4c(l~|>JJiMi=5-fIhNZA9#Ju|I(>~7e@zB~3 z={aq!|L_gk8AGn-!|FagqNS%@OMR&4w4MI5o4!iG!|k1}ewbxU4@ZK(Q2RrBJx@&A z>p#n}dp~2WXcid@*r-^Xdaj zg$H1K0rOzFb0lD&g5G`Fz22{1yJlRd2ag2$7u&|q$6WW}qs*Fvk?}LK|K}~8kHrA8 zg!`pNIFztxx#D@p0{}ZinVl&Hc)@d%Hp?SspwK1IOd_7!)S7*AZj+6E9yK3GXn!-J zZo`5;7-zFfd4B;4Kf2Gq2Dwl3y@pa7eOZ|URG-Q2c$#XdbojM9a{oox;0OlzMFus3-R(=Ze4edhjPdc){49V?e|+8+vis-%)@9_4 zMC#90U*q$xG7j5p|NhfI|MzdEzwdn4g)RO<4dRt=CKU(b=rccd!A4Q^POj6nI_U@2 zf8kFbUfd7qcjoKQ=nCy$J-vUaC!?J#`0BXzX&oNyvW$}O%w4>;M%M4?(rDewIDeJWAPm8%s zZ6SWy$ zm3V^=A6S}M0&ITen@*SJo&D7=T_v=X&IfX8d+hh(r{(>6gLYcj^3LqbA@z8uZ6nG$ zoBR2n-rUd5yd}{bhNj!oQz*ktJUiUVdt)o_vXxBK(`NDqLpzzpRFtR~+S35%c!$aR%sxEvZIW=d@89pM1Yd7XTU6ZMb~5?=t5%&F2?qH4Q;{ zCzC?mvuAn*IVkIsX}Rv%qf@z?n;ckY_SjGl@w6wZU$jjaIYYBhwv)P-!*N_zx?g|O z>in-y`V^U+RqbBls*b^yzi%V;PltnyfuCE$0Xv8Wzgu60vAKbVALkj6@27o@8=jkB z9zfrC%L3C{75k01&pe*(cl40F%YAfhofFyd5V?JJptO5k6L}eiY8Z!H6bnxspJ;GQ zW7BK{q5hX-8{}b5juD0tK6uK| zzJ3*_?vMSd61yuk-_YGU6TAMnIQN{bW-sQf>AXZ)-qY2Z2A^=3ZcbkeSK=9-#TS6j z|99_{nbZsK{m1#ywt+5j`gfc4ZEW?lRwNXixw0A=`Dz=UW}O_ryU@_{huvWTq%qfBG-~Wi>gEJP&OZq^s6; z%&=@4KFi=rN3qb8XfG!EsyWjKdvZVK*@^RHxT9ez+#r_bw=(RFiQyXktM|M?weGmv zyR)7bewn20Pao|1aTBN3ujYbo25raizARZb_J=b@v2AQCSGodGl<2)1X8SGjw^Pb<1Guk%@Q$oQw%;or_H_e|Rx z5!Y%|3R8*OesZ1#WGiWhX+6;1!XAv@?!LRQA>+f}>^^Q_GdN#$r@+F!6A;o0AjYsmm_# zJ|~Qve8))fZ%;|*_;K~lY8XM6`gz(`27`zW=Ud?4F;Qh&24{S0zxdtKk<4P{yRnhc z!}M?qlUg@p1G{zRc1n`nR|#+N1nF>HyFas{U00Y5#C8d3gUy@NeCzV?`o|}h&OIZY z*SUb~!lT4n8cbb-rDpk67jFeRRLQy|w;3`m=IG?5u-HS&SE?^ubtRm=(qw<^pAy z=slLm7hQx_``t`{%;MdKsLr~C#2Zfe#z3YlVIV#IU|J86Y4+wOr^~j7Y&lD9Pa-V~ zB)21gn9lUU(C2VY+RZbo{h{6d0D9|t?=kENoDIRv8}5e+r`hL&iv=7)+*&;L`|I^pM*{iDu6?Y=x7}deYd|LO;L85nt}H$~>K^!}Es#Cr zaAv4H-Nbh9zMT{{?M5+Z`+?ev(}{rYB-4*0$K-R+bMH&aRlHj*SJMd>cl4+=<72N$rP@I&yWH>4C@e z<@23R81LSvBArevN1e1sfNh!Uc>8S|+XT`13TWX2sMwb)C3W5Ge*2qfH@6NHwasdf ze|k9IhTgxEKc836ehwnGoAvcrJbUI-%nrase<8nlBb%Tf%eL!raKa5kp!1E*GKQih zh^Kc~883Df+4iWcl~{+q9EO*pGv*E7WI~SNP|FiSzqga=bBq1iyI1RrwcWganls{$ z>aczC<0HFmpMHO-^Y#vor#T)1$Zm6}^Op5H`Jc_!`yX7KY1dhcdqLGtzgQpDA8%Xv z8Qo1(6ZW&uNZlUlau7)OZs+*aw9jwmJky7UkM-sU3Lc0#e>|K~LcZq{mMpd2-mkQr z+7p2;p6jGI*-((jolX~Ua(_IZ{9ze z&dW3#!X(CNcMQYT)(>`meYlMwZ0q{i#ug&L_FJ0;f!*8OUXarK#;oNyr9n?-vV6CX zI(|4(c@PJ*7QfBWhWS9NBYA=-D3w>t1M6rWR>CZfxwk*AzgrEo{?2NldfzaUJgwC8 z@aaN>y(eW-*e|D9^fz#3XwnvOYTGzpZ$JNT^>=@_@Je^#-|{O%>Av;F_3a1kb%w_T zcu$vYFp);mS;o0@Yg|m#u`Rj3QP66tl89uzeYd!Cs1FSzsdouBab)Oxgh^4zD z$$Sf5w&j5T2Plxz=3CY(3vFSkv36&?856SJq{o3`xWqZi%3e%Lf_7MCwJ;v0p3o-C;d;AJ|>8gRA0fXxF4Cx4bN~AV=0h>cU@VHwpGmxhY$} z^Y`6R^v*rE<23?((TO0KH{5Ku<1Ig_@)y?dkPD1q1Eepi#$Ncr4=ImqBS`XeNI~#-PKAdDj z{s=`lHG6;Rveic=deY^(@8USR&AYRZrF@x*xtQZh4z}?I$utXg=OjM+4cB9eDJF?? z-E+%%>;3qL0~p`utoQlrN93*_xewD+_Hj450{q9V`kUmnccS^tGSn9~Wc!kk^ylxz z=vC<&A1m;uZ_LGbZ`z2Ab(o0PzRmM=VQ)X} zAW!#UPAwW%zx(jKjYH~x>o=hPeMmqYlLGs%=-oTZzS!Q$+9u+W9wu%7^c3**X@^`N z@AcGg%8z?p?&RFJ$?`kToDW~`;$y*M$D#EDgP$ZmcGehE51x1OK49B?t_Jz`>iKJ^ zwM%g7Mf}3vzC7LA_m4kc{T7D9e1|73yfgXbtc znA?0ggDeLYJY@B?mFG^-^xZaYy26y+UeV;uQyq#o^3<%o7pxse{`S4~6(>AhiMRSo z#M&3yY_^@(7T)RISoB!ot&;=ZAXXo`y?vcanEantd*fMXxW56gYq{O;laoDn>Ud#O z=UF4&=1jfTMKu5Xo6~c~w&VzbaV-7yO;>Z$=s13dLbx{>87$ET16K#XJtLTh6Ry32 z(i!#--u~gG<-OhJdZB$HKnwg^O#1ff$GM2^*Q%>%y5l=cLp9Uo9$EsM#P1x}%iPoU zKqq%s{pL=KfW9wRUw!&eKf6wq2YPCqGr9bF|HA##8Fr^V9?tHPiB3rOZcnRrhbXQi z_9(|W=eO2ZeV3R}r(HmhR4@~wY|Y7k`5$DE_VLHC5+}-cTG;r2jB4u(FN?#s#+w9J z-EFRz=gkH(_2O#vfBbQ^#D5-ZGqTxiqAefxH-gO=bchyuoHwc8(FH!9(bmHE-9ijs z_`X0EU0G9(zc*Ipp~7B$!Pr16-I5OwHFWzI4V`FRylte~!PF_n299}m7q|IZ^C7e@CtOa`DtF%Rtk_B9+fKmSkE`9t ztVyKX8^pTpM!UJ)Xg9YI?bd}Pa)w!ow9{+9&e4L?Xt(8fH@6(`)&`P|i~MPqr1@sc zJR|_;WuB%3FE8_SL)(tn>TJruz4_-^C}Zw#YBuC{p7jZhC%5*+YI;TTfpKvN(?rO- z)y-2!$tI`%%f~M+uARMj%1eiy=9I(7*@=0ev0RH_gB?PnLla#p z;?qe}_cU_C`)TZVaV1Y@t>h83MKvCHyQim3>jrHTgjQGWyEzl)y#FOv2wUi9^YZ6) zg7>nQbxWaj{+}W!ajE%RNRh3F5czXD=`zsH`RhCd3Y&dl8A9)_!|U7we?6Iu>a_jp zkHZuB-<_d;ckfnb#W;DoE&BhofBOD+zgwNR&kwH;0Ym!>e{#cpI^Ch!;m!3`fJRx` zuakBA;^J$U*Eu9*ahxZY?TbsVt*@>Z#@Np8f*+- z3O%@}C%3#Y==PJ<*KH-5m4Ln7{#1N(y3_8r4{S|$iLc;F~iREslJ9~6Dci-Dh*Gc>1 zy3-Sks$<-?mc)Uz-ecG9rO7bgaBSTzCKQ%!TeCrZdtexQq&W#I zZEy1HIOU$;(n%J`G<5Dz%;~g(NSmfU3m^SUM2@yyw!w`H-TgH}Ec;lrF0k^8_2F^+ zg`+Qro5RP@%Z0K06+<)U(NaCZjj_dZI&2Z($=3@&>TDSroz1b%*#wfD?cs+G1V5%h zyQhNBW@`0pRF!9=`a2s`+S#C{z8QFy!g)MxZk@9owf~6ke!@#5XPoVT>hYUy58?|W#+ z`1H07ntZc<28r8hSwDk_yE^&XK_|~Y?;s=ox%0mNrv;Go2l1U=7|&ez{RDbS95!-) z4y{$RL@I;1NZV+zi#nx`?&dw-U9}hbwMRy6J;pa%v~sd&ZK=3HJ^Os~U7N)=@3sra z>Ut%L{`s9A*r}YFbp>3;dIs$#O+Grw*nU~3^r_}2U1DvQ@-K^3!Gx`^zd0RL?&`os z`t`=&UD!=`68G1h+icvRYSE4RAmMd7X&*VCb^3I7)K^o@RPGC?1rUC^*yUVi|IE&& zjqG$YRkMrpn<(qSH8Ssh^2DDr;n_%b_0cajjGcy053qZ$>IaUWA%O!Um_5^tLH``~ zzq{JR3#KBEuOU7impyR}5R`zXt^S`bhxLas1t9vK^ zWK1I;&P4lh4DQn|bb@c+8#p}+%Q$c^$@T6%B#Uv?eM@}^Bb)lAuI8D9&-V|U4Ix^6 zDD=By@yTtxv`ojJW$qIu#0bmzx-AJg?A^SWB#bM5%kzq%uE1IA@#T5e@Ol>py_5P+ z-{^1?P%cN%)t;ONElx+Z&Vd>&;ARzqd_z_~6q_7F@@~Xw8G*bX{Q=e+P~-zq*ZXUm zjr5-O)3{{&ad6!cWq*~#aNO}@an{G*9UbD%Q^12U`ae51d~(sn5FgmSKZ_82Fu`Fz zUT_E-8zwN;EI4KqWTc~pa(}=Y{~R&6S^V(fpvnVbREsf5{X1_`f_^`HZBl!?D>BvI z0bo;`RyK&tw#mIx-7Lp_wm8dab6v#D+kNr)d879QZ{>ph%T>VlM*YxLETcsG)h;=- zi#;QR-RoD{V#{48num@m^?ajjLf0%^U@3zMJtAnlcM&nv`b9VW+bilrhkewR_-SZr>?{Gwr zyu{$?b{*oM)AJY&*A0L$y7K_%9}k=WWJLZ`#pPJ!WHFVW?JRG04LJpX!nM4HKJUsnIc0L0(@T|b5WK7T>D@aBM_ zr}V3Q>(uwLjnUdoS)T_8PvSb>6jiio?$fBMvx!RnS-_&RdhfjrWHHYE=j1Vr!ML+n z+|Rnz`~Q<@+^$&LUppGNhb8~ZqjED1EQRH^U)&d$yKe7@{*3Wt!(mPf--xXqci#BL zGl^V7l^=Ew{jjk&H*xc{y@i9r-~EQAy7PHX57y?dEPT2|v8#_0r6EW__c(n_Oe3tsmGIB_9eT!y5n8 zUhpnKYJ;}U(pXuxZJ{kYk8iiP+1h7YSAEUDR)e&6WRKQ(w?U(CYNr)ke1>(G{@>+F!7l6y3$yKmkZUu{#1x<~)+?^;*iJF7Xn%qou0?qJxfp552Yp4^}C z!wBe|%5&6?EH0mspf8kN*2L@ZI+a6;~w%Cpu z_8^xg17B7_Z#MMJLqiXL?t&jR@}IHj4~~5EXD|9xh#!zGf5rs-Llf|`3HYBggI#6Q z*$$J*9Dp6I*KTGW*w`MAqBaeeURS30`|{od+_nTJO}* zS=@SibFddq_;S4PWAxT8&v)-I^zz;b_m3Y+pS!$o!rc*5nw*?=CpGs@KUd9u+Sl10 zsjuyNT#d^E>1RhD@f|ch*X~PG+4;bzbBXfY{Hnid zd4J?@m?U>HbJwJC`p4(XEx9ayR>s_%%tu@Ja!i`*NT<)+Je)n(w)HGi>;7|ndfb8U zXUTCbA7;UpuR5Ao-OGHqYxm_>?<2|Xg##&)OI&#J3zuFaEq=yZs>TZr?xDq9Te;)9 zHk@E@$iF1>_A52iJU*~Y|LWLxcgYs``2QEU_)U3ul771LMW2V-v21^CGbqwYNytIZ z_odnX&Hf2P*V@=zM=1*fnR4*nD8?M@ynWMQ6kT538>G%oQ#p^xqW#pauO3LB9Ul3! z^Jd4EW>?StYs#11Nri2%^@9*TfmGY^c^>Bk&oAba&0n{9oICgvbj!+=Zdb-Y(d;24R4z0 zP0_xayZ(YC`23}nRh?U~E)<0=MDT^yu54;yVqJ(PYA@@$QnePs3ybze9h{IRIZ>9% z>q@$&3H_+vmpctNb*)ONY*MmuWo#N}qDwK@)RajmkyP}yXj17KZ=0gj%Iah*?@KKl z7Gh{v?KB)^X`GX)##EcerVvb3mZmII9hGm;PLho>m6I}+R^Z37^g*jsVpUS%hK9?X zhAZFLC`6?yCBP;g|7uy6RWQaHCmZ7v7uOpMIXPJ-Wo%VerZS~hbu(D-~)fwJu^KN>vsy;s+(v(6HWJiZXeriU|4&T9#55WrC6sk&EBv+J5Nhh4P##90ihY7t=-EGk(i65VJVOwv#dl%NXBT&fTfISOc z7rHcF_{s<;D;<0)75>&u1#$~m7OPZpTB%x;Nk`-Mr2f#d*18kqYZ31po5_s zCrCe_^3o7%V$< zREij|-;FSpEis!C3>1{GHU33QO<5Ev8tKtx($?Y$Q%m6thNgTG{c=aoBEXGxYhK+p zbQS8ltZfx4C5;Wj`AT~1e5hOvwhFOy0xXP4fIYl&BJk^k%~V!4vevs@P2#5Cdhc$1 zF>X?$R4i+mY&1rJ4}xwAldwfjdu>F}F+>QKQW>zLEVN0ojutFf;ZtAkO={ZH`+ez{ zwkvX>bODmdgrcr=s-zL%Wzc+6RZU^7#LQ|PeUNxa;QLxxQCBvq5}SKpSJV55*q3Yq zC<-!^G-bf}N`TYrBp_RxI@DfbBTY1Q1(B%h3Iq>Et&9`eLTuUl`ucnm*03zb-O|3P zL|G*WNEcI?u#iQgK?`-}`4B)YUdE;rhKf3&BdkSblL-pnsAD?oE#H2AM(Co1bV*5q zN`fxH(5lgON}?*N2&(g@j-@R?oMjzNi52m62?!e??#lbR{P%a)V6dy(kw!cS9@MS8 zFAQXjg$N8PRtj362vNA?Vyr@4daP>+7SokV5Z`rWtPRb*^Lu^^d)98oovX^asDiXo zLTPD1CX00y4Tu6_QR@JSXNuqzwo@8oM1s&w(0QWLF52e)&bl<}p*LH^%1d3wP+?QR z-%z;NC#c1usg;2=g2pT=S2P7EN&2#ej`P$%3JNL6dwXkx=|-m3XcAhuf<&-I1nm4apCU0{%`7)D-vvx=%Gl3L*ki9O!F8^#S;!g3@=D!fc>iLD*6^+IT0h z5dvyjK)F=lI$vu{qXE#6PywY(KAJ;o2jj2xoQ8wQXc7WMRQkM%t>T_6D;# zS2xir3AwBx3?brT(be#0-h2A3bnyU z246PN(H@uq6w#DYf*d8ZK7^$dkn|6A_;E@@UjQ4)3Qr|(EM$GvXy|2#;w0+YG{Ezr zDQ$!%(LR=10tP~I6%eFYYVg>DeOb6*}ySxsOxR8ls?XfNG@-#sh%XT7pr%wZJk;C(uMP&@Uc1 zq^cYQqmW6}q@Aic%wVl1qxYgqV;ecnV38n!g#k3hlGPBpiaHvw7P3OQWB{I_J|!e? zQQ=1@J_s^MEt9mmhd6hXXNs~B4RmshL^fsPp`kGe1^%llL)?j7)edZ?L7Nf^pok%4 z45GBUhfwwal`TSwAPkLpMY1o5+(469qIqag2o6k6*%I@hmXo3Zq5^gdDnrP_U46ow z%aQgq>>LO}!2aZEw_xPZ$`DH?G*D<7h!^VFxL8BC(YC1t=nxbGd;n>$l&~CQbY-eMbbkNeV?u9oZw}M3L6NBJ@9rF&5r!^fed+D6a&p3syVbKrvZQ zxd1!6riopGx`2i(RAl2+V~Y}72-G6(nc?R*aWkAJw4E6b4liNyLrw{pY1BWCmTw5o z`l5iygppA|OiMs&m^~N-%sUw?;3==}(RsZ+us}5qY*-ZZLjV<72u)uXKtcEj^TyJG zWGks+L;qBjDxw(=L)BqVjjZ<#;e}3j$)+e%0`>_q)D9@A&@gU*^c+xfRbVrr%3;R> zEaI_(;yg*H_oSgM9l-WIU}EetpBc;rkHk_|9*hS&K~vGe^Z@dL73&~I!5IOp2eyG6 zuz(!V!p@{^i*Z0pn?tC5-t7>z1QX@ONJo*12-IrnvZ@*d%cDZa5;zLhp8^I3fkFZo z4u2|2c?uw4a{Q?a_lA9sBo!;dXz-=aW+bLt1H>0F{>y|d#IzdN%Ye$NHZBQJTjL?* zbyI=VMRSjQpZ3ce%coEPbW#^|^#Km}#;6J!+lmO~2q6z3C!xC09YK8|OUh;#&=UI- ztOqS0(J3#`!%qyp9K>Ig0;ol)7$CP5t;-0l2|W!+C(8n&w+RWe0;U9vgLp0@1imyC zKzoSC^x77t8Cj=>y&^Sml6SFD3MQz5^a};7-4q?r2*iL8GJ?`zhQQ(m95XaHpopsa z9&|PI_z92H^KF4to#k7TZ`gpB^ytCp?g67iz|oB~0|hs_OHfK3gVEg~NJ|MLmD15I zLlh8yAc7!L5-KXj>-*_F-oN1aav#TYU(a)1=Q+AH5)SN^?&w~7?da6my-d(oUVos% z$$=2XSil`=GF09Re)KT$%Amu3OR;)59{2pSK?eqWYg*i*MS=D^nCj7)7gT)>A2MtY zyj>1_#*P-Pde?~$EkHT*QI78Fg&uao9RE8i6AfsQ>}apAlNheP#86W=VUdESvt)EU5oJ`U-0Zm(dgoN8xGA#->-x+)rN4$@GB?W$ABZ#c6HaMS{8ij}l? zfdl#sffI@|T!0=m=cB&1+UJb@Y+H=Y*H@U+7|BlxI+(4|l((vMeDrfRDVtgpY1NkZ zG;q(!fe5x>sh;Rme6L%~CivjrmF-ob*+gqu+)EgxO;oaZbt+ z${vZvmYhRbJ2O<1NQ!`Uq8wb5!1?kFTO+OYDw}C|G^T^YrP>zj7~1jZxwANR1${G> zsCMVJYg$o{Sz&x+;t;Vdx#2Ok(iDRQ_ySD-^Y^_%owq{xju7XbupR5fCxuS#z5OVE zG;i>g-NgW0_A(OZPN@lRq9&7Xw}&EV6D~ z{GN7rO|yy~vT{S2^0Rq*a_qNV_Vk&P`|3$Gf1$J0)`1PDOF@q$e^oyED;C zSNzSZj#s4USyommdpq=y-Y)lM2=yrY2{CQF`7B4|{@N!waeCr#4V1u#p;@#SOVoVI zTVlAxAKui^WNTh_jki**$PI*r)A|ohMj`tD8$atv((J4H$7Y&y2@luE!>~oQL-b(B0|=K`&A*vnSjUF}Rh!*-?XMDN07`(a|P> zZ~OT=si<6w%-#!=8&7X|3D2G;Pi4Zi>f*lwqFMdZBY_%A&3!9Y zj;EA_!n^RYz%C~(&Czyj)SfiL*XXq`cTv3wwD*ZMpr=L}s5X08sTt*>cob1gKeT>7D}LAY{0p!D+II-OGH7n4+Qf=YY&hq6``ZKBA!V^s)c5wf-`sv7 zg+ldo-lmWtPEWl?Vw&T_cHGB-ek7}`lFhPZbja0puK7G7eOyv$bV)wUo;fGLV^$E7 zSeAxY6lcfAbFbPKHl&KX<42?=xiAVl!wHSf?E^fij`boMcuI#aL^>x>Po+plC`PIy zFgF}vBIiJ7Kp!oqZwHzl71wHBfby(3S=SRSunH`~9~#H6N(c)T_2SUV!qdBY$121`# z7;69UN}iTjqyB0hi)K?V-S>jT0Ax-(Q%Tw=w;r8|l*Om!wNncrDkno!+p`M;_rq|x znFHUbsVcTjOWG)3cRDv7{&%1r(_DN>Jyh&EsfH*`JX5XSl8jH=(w;h+eHO6^fj!!b z*U@&_loMYe8!b<$kbw?6LJI(qgMvkQ4(1xEe^)S~hpT0m#LNIbqEf8Gd;jO<_w3U& z{&qjlL?n`(IPUM<>WafNbhyl@V_{_5%4{|3f}ByxmtRyz^)#!>KS}A_s95WH|qA1Ls6)pJF!1A*HJVYN<-@w z)uC(%L8>UG9rhZkWTGTcVJ|4jmQ}%jbt1=S*&G%Qu`f^&2mfrE(Wb0lC_xT;vrKL1 zSCjCDFr+)0-G{#9FRfjor@|K!{!kfR{shiv#;O!l)w_2G7CWX8!5VU8aug{9`04M9 z#yk6|s5=y=UUAU%MaNZzkvU4dt3Y5YHNd2p8^H_tZAj{_#6yXDiZw52u=XBIQ6!5( zMiy8rr`ej(3(#L+w>otbtr$b?b6RH4hBRyl27md{_y@!0<2%ki1XHDLptyc5U`X(H z8YH#{l$M->&vfN(%v4f7B2S##6jZ8zqPrNi_ZxjT`>;Ylv^ zfJ!4r0|-L974KqDLd$$^3H5*o+eEv-V43+tN1tM1B6-?(8|jPe{SZ6QHhE&!2MqUb zauY?E+-6+>xM3;OQ{Db=1Mrt=?)!UH#GQLoxp+83*j9LmNt;+=w!iPkQGyccD^7eQ zw1Ck&h2D!0B$w96z6{sorV7Th0xp_GV%|pnTOX4*Pn_}m&z5lP$($=A?a~z~OE!)I z2tVYwwbME}$dF5mLa@(PVpoqkE{-yqS-q`bmk4qimiinZD(}9|j#tC$B}v%7Fs_y<6wPt79A@~-KEJ!}prS}FLcMHAi+z``h{ zj$$Qev%h~044k-6cr7cdit+o7QjOo!y<+&KI0DHnDUx!exQsI8(>DZSh9VI_Px=x) zr%RknklM|_5p3qr6>PIR?c ztq<{iFur?nS71U4-CO3$2i5xQf!uq~r%3dOu?TJj2p_ywp447a!V5`Pm)=#32 zn4zqERNF4nb@4q+!pDaw6|@`FKY-=M&(iWbv1CvgNz4k<1ppijhfyOlw6}a`liCEe z|AcvjG+1$8^UbuG?PpPlKLL3dbyR3wda|pJnFBQmytmi-8l?lJzJma)(!240p8eB6 zjGg=n)_P}63!%b(p27FZj&=~nH{)yHsIm8g1Cdy-CAEh7c6Xb%qE!{BoD*#F9W~C= z*#iOb0qVD0-tpKm6&l6-iVH6Y+LpGPX4b@`XZS3m?6sDxd|us4paS*OLW1_aABt`t z!JSfWUDiKpx-+Dph#aAC{zuowGAz{0JSWX(&#;LeTc z=%LCabV1=sNeKtJ%;wVvF0*Dcxxgdt2?yM4RRDJosUP(g?ZUi7v9Uh-w(55E0Ws9i znY9QXj0M=`64-HrzD9d14KI5ht0dLROnJq{Q`?bWqQR82cURA~D1Hqqrc?RJ3hnrK zgRICP-&i$Zfb6HD)0`0WPsaq#Dr?;uhCKY!$8=CUkT=w`{b8L=FW2TnaXdFHw$n@J zJi!KHXzi}>N%{Soo9=B!xmfR4B@%Ot>yrJN*%gxgzoxP>2)hbSuKuv)Ya;~wFdaks zonAs>XpR@Q4AQc=!k!4rCh#yvKZZV2?_61s-%e+#yWSE&5dEoQn;1jLWV#1!N}{6D z?nUs-=7>Sr1U8Fts&rSb1EC=N@tbvO&EF$!YHVg+uquE^y9p$~(XA1*TUYQ( z9&G2s2}%sT-FmUt0at;GslQ1n`ggNj5N8i@$%NAb6o=RoBMhlBjkFtXpfQA|Ut|A5 zKZ4Qeo&B0Vn@iBL^QN;L03m8PeOulGN&Cc#P)w#d*o-u8R1W22|&YAk>}X{kth;> z=5l7OQ5#N7Kb{I1F{-3aR=3zt2RJ6{^6uSPV7lp5A2`E}VNU1p5BB6}QIVYd0lI$U zdU>EpJ?sBQ?G61)%>PC0Tgm^T_FW~tdWv{{eMjyJ(TeU?Y?-b;{)o%c1r$Q|wd2pA zv4(Qha#!-bE^&l2&UvE*D(}2LGqz!rf+>LW0ziLOAd*sopr8Nr_>HCX%q`7{QqjKj zE#tarEk;Hmg3x{N>b+x+TQ9*a;6XLU34hUlFkFU85{`p?4uL9$$T`IOKQ&n{Ak|@C z|9i)r;X{A84nF-Z=;nHj4`fZ0HlqGYeMCR23tFaO5~cYnxc36Q39P+-qOliW>Yyp5 zKcj}S*{*c>@~anC`K@+=wn0N2K&;}DX1LCj!>0OFI)ZkGsRYKB^Tu4;g6KQFrzX2fVwLdY>-w%& zuK+jUq?_Px{o zH^e?mA4CBv_qBA*r_S9ab(c&8FXv?rho3U9;x;n)(h|~f@3su4otl{Jdl}-@$=teE}^hU!w<}8w}>`}@1&$>0U+IN5a^uGIb%QxX^y16De5kxJ4o?+Pp-{qDi0fOg+0 zt|U0bXkD^0Q)xo62=khdi(-Cat5~kK%YHHher_u8aBSB6%3?463d(5sR|(WQvC)^7 zaD2j&VU*Gf`X))a5uSA_N%R)iO=RresPktEh}6!s`aCo`=Nai4s>5+DkyY67HGrLY z1*13HZ9%QrXg2kO5EepvwtR&u=H)HdL+WCI!!YAy1Exi4)}mE7Fw*9|025=97sD6? z-pGz-IdsS+P1HkPK&|`*hy6nb-mH8}{#mcX)ih3~QARofEg<@aN@j`g3Cqgt!e*+L zcEg<(ZEfZxT5O|tS#L(!Fki^4gU>qLD&mlS)-T%9@(da8uu(Te9JcAXpowivnt&?( zQr&+PUd?J+KSj$T>$0*a;kVV#S5&?5Io%W{hN~;k&>Sn2q3#$J$PE-bLHDv*e2Wkh z1Y)MYvy6lCH`McC9JqF4hg06ad6sTCT1$d#7r&m>W9$XQgpU~*z7|axq)Vf|3ba~V z6WGe@cs^uz+F$*IX$|lVkB{EMCIEm2<-RDBl4w>Eqbk2eD(eHL6pAQ#H%Rai@Ot)f zSn@w1_25iVM*2lj=~f9xXhIv8_gDh3k#Fr&WGmVc8Gyd8ls}qA^CqcJQV-CO8aqd z(`E0HX@_HnJ@SdxJxe zWWkbQ0D=Fpt0!_X{Gf(Hq|=rY^#A*VyT3BorQX1iAH*ZcjBw#^nWOBjokpcX1?Z?6 zik6M_5`+fECqCjO{WU|FCBLhKs$@M&YZsD1FAt(a*X_{0Zeqs=5=$TmOS_Phwe0~zl;C=+&z;9RajM~;@9-lO(rhD ztgAD_d6vo<#|NItd8Ijq4LAs2Rb-9)tS{Epm$yZ%!$r6;-iS!#c8k^SR75Yhsx8f_* zo-AF+;LjCVp3v#@nnxA`)E_V#If1e&>kicTS%i{ipcte$fMscH6K?(?F-Zl-n0tF= z`-Sb<2O4(92UAM`!#DUN8`h;fS~L3YU{5$t6YjtG?+_(c>{E6?E~=R+*J#DLMf>7h zMVky)`kf&cttH@VUo@t}?V{Nzj!scuWg$!>K%ph4IVHTCy_5P(Na@ljpE5e{3Vig# z47rS2Hn8fWu?QP3ffn)c}*+vx}G$lt= z|CcL($H0zKS^WZq&s>2`&)P;f7MaiV|?ytmW5kjB~T8&4%dASTZpSrvP!w4+#K zPO^qmUJ^FI>hch%-_nWTn|RrdJ3635IfJRlAqy62eRjT4D0XN>`ZR~2g~PFGw;-p4 zN4ZVzV`HbHVb+``l89t{sq#Vm{YYqB<;@V)_KUo|c(U#t?li^*&AIE#)>F~9qzbLj ziFLy6`J(zvVBe)WDrAkhl;QgwtA4-ZqlD9!b*c3b7U)wMD8TlM1=*t}-0~fY-qhix zhViXsO_N`m>fQ;PekF?ly6NMKPIgos?(Y>>`&Kqv6^7rCh%m9LJ2_R#bT`N}MKv8& zy7OJpG{e@N+A{v&ah+=&bakiSVt+zk9X!2nW`>(b{(jn ztE*-u`{ir$z}Jh1YciMao!aPfKzorJ(h$q^qfQUJM)luoz!H9K(|)du>o{j}xG{;C zOUDm?Q*pT6TkfP5*0q~)IDUP>XW2G4AHr5Et6c@HZENNoeg~OY<=J!ro;=2Gs%kJT zzVd=4sKy4o%-TLc`+UB+dUX5vmIH~grsg;2r;rwvsme!e)(xa!dO5t$x2sIQr5jhQ z9ULm|8fq22JKf;j`z8W-)Vnc1D4irL{M1%c9&1eg)v9Ip0oFQP$7NfMjO@0df0{il1gf2tqngdpsOkOC`z+Yz5AxZvXd$+K^%_HKu&$&sT9*SKEd-i zKEO6WqXb?~^Dh1MSt*|tI{Pa!@T}yg6`(tDCChN7>MP#E)0sUdm@2Tqe4ZwTP47hj ztZEVjDsXm`^EUov)BO!PM%nq6;Lg6$13|b8X>5v?m}yO!e$V!+bmLAD-rPzu>P~#n zQV%JJ|L>%-r!VWq*ETx^&2v&KQfEY&MN(p(ca!ec7i%;C??Bk|!}<*q9 zNzuVr_DdJdncHXfx^rESQ6lT$PSu_APItlFJHCbLXV|GL(y+)T+q9s7R7&{H>EL>p%ULjhpuYPD@Pp6bWZzX** zYw~Pb*E*^w#1Ay5UlORQxA3#m1m!21EiBV;PvJpDH5;le?*l2%+4jh8KH2q@1d;*8 zw#ry2m)u1^=hgy>FgL#f(7<~#h;(AD+>V*_{Zh`6CUUe%*==e9E`Rfe;#AD;V*d7I zrT(x()w<&2yac7dW{tx<4WoXTJ**K^3gp~U@6CXsMC=}P|JRD^;9aFt&`ecM`rk<`nN=_b)LW!>_va~y!Z4r}go zZs~{I6N%urnhlL`__OuTUcn;6iRAnVPJ%DpF0q@a*6K?e`!aUXgd>)&x#m`3n*qmq z!VD40`28V=YHA_Hc%1{Ir7ukbr#kkaB7HzeXILs=x+R6GK|yj07&a8r)ojfPDU2-g z-OLXNk-yExryg(wct*6+EL$arjGmT^@973Zn4a>bIVLB&P z?a?d~V-BDZyXDiG%uu^u2#6w4aYlt+O(H4;fu7F#aR9ei%2^6MxO_tS3%b38^yiy} zU-%i#0y$%H02ND?A{>c|mr>7$roFl?hdbuv4XIagfe+)qxzh&Eqm+la11hWgnCI3` zMOplL1Pi$K;k40x#qU;+X8q=3EAr36u?n>}5w;`yj~Z&eMVKm;Q#g?4U{}jbJH5ez zct1gYhUe(6!=0{!&^zbXPQQ*_()bx{y6cKFlb~Zdc5#DndtqqK!z8Is&RMf7ip8dx zBWfh1uz_b7c3$VMq`sjG3`fMM;NDA&0y{=z*Q}xX3 z2am^g0Tp-0XZim#j9YlY8{FJlb!C5ZFIuju%|e$+txFX_x{0G>xMu;C>3H2rJ%7ei z#T%H1*=rsOamQL5;9VkVA0PWKXz^3o?f$1#8IzWmR`iZcNO(O$(Zai)Jkf*i{~`e|3yJ?e1dENl-fJTO|E4ocZ(r`#1?R;Ip8 zkfiD3{@7I#wean!Q%_*vA|>}QfW~@rKh2x9?%uRJm65`KHf%i(TFJC{mDZYyC2WOD zPH<527xLXi+44k0hqyj1f_{lj(+yy#%MWkBc01Beus3<9_*(@!LAkjzw; ze`AZy?>*<%1E>)u4tPcerSH7IJp!iasA`$L4zCFEkm}yX_gqhvG;Wcv};T260xR6>2H!4yWWEBIn%l?>R$lh82 z)@|11GL(ig^kO<+%9}?Rul6PtuA7f;FAB0+xZtG%`5S#Di~`ndVzF<;U!L>tvlL>B zDXj#rqk2#PRxRsTCfG$}0CoM_kC=^K4g^e?f$f0Ki2sgruP zoy9ZW65ybhkE63%MKJ)$VNSdogEf?tB(;uTs5)0Lnb&_2Gi=K>LfN?f?hmPQ?B->S zGSxRVyH`EF(%>|RdNyHEHr{4wBz7F!`BHb2+A+G2L5Xv7UzlGsv!mss+G6$vmsQpr z$pEW>k!cN)qendA$M#qYno|57PXAC_kbr`COp;KU@3&#Rtg~p0?pG%5`zYE#YK%cT z@WFM!2pUSy$r@3Nk4pA}@UsB~7)W6RcoXiU%SqHUHuu#SR6lNSSKxP@dd}ChXxSG$ z{KvyFOIj@FSjm^v+0%w|)qFWzNLomD^PA@>E=^(=o83G#e@}khcI2u8k|OB7LuSNC z!Eu{7Va;9#Fk9(zEikx^iw*>Tk8%H3A6EkG2Gdr;e#c82+v_{=W_lUNWW0$WS_1i% zHgXJa1xn&9*7`IZw#l9w$W&W8GW1d>Ro)`KlIqWR>wXRUqs1PpiY+5|-o4%3(g875!JQKS>fY zvryFIJ>O(K!Vsg$ zp+Tjr4d#auA8zNM`iPUGaMX|7^<#__al}zkRlxB%#b|MyjVnO{_Y=gg<}lohRhf6y zszSY|`^lnwU85n9e?Rh_8>G#K@U=yEXESX1TQ`-atHSm5n8R>z%axOTUWbI&MB5@3 zmANxDQ-05M78r|#p5y1A4yC$PJj>vh3@q1>)1x=On>ABX>gd+ESW&DHSDcyH0du1G zsi@Rmg!MS%OneXXBhBTJ{+vyvvY0&G4enk=Yd2T#hZ<-(yY>`U@>fIJl;J3Qz7o&l zz+0gb$&xRaLXIxKd0*(UA#%uARQdxG%CgsV%>k>pL_sx6-!t~sUpM|U_M7mQs{Ewz z2b$95rm1(RCws0S4;K6^x4rvgeQ^0MF6t(2007D_05H!C-B#CW2Nx}lhez%?sL9bhM?eQj*v%kvPn&S=lHKIzLV2_Go5c#27TO`L(ME3>2 zfdJZzvv)X5$bBtz3M*)6eIXddK1GTvHHV?%=|Qju2y?r$X6YQskQr<#k?^|48I&*# z`^u`;{pd6qnTWQ#v69oUde34?Z%0uT66^d!uS43TR{9s&Lv*TW)wfl$wZ=#x6`Lw4 zk=qd@wtmPBG5tn#GlD}6A4mOVW(X|$Z6k&!PKKZ`f%Dyp-X4StzM^#(pAJ=uGAJF{ z!<6hZ(5{MUEid%1J|)wvy~=q>u9{0XN9779B9{`o6YHq7b>}{+yK$A$+cb&Et$y;q ze>=DMj!Jz1mH~2eALOl#kDz>mmjifQ1+tt&3{ zFBj+i1e>t8oXjrG^yxd+7}yepEbXhV84Vv(Y1q6eUZn9RaA*y>sB37jS4kvfQUa@V zFiyzZpK7E_=qk{KEO}+bf5guHQNbpuwW{BKY5HeG=HkGrwnt+3#Uvj({V-S-^1)kg z2UMaARshC}T(h2$E;}kqa(fu_`rCs7tyIsLlRv7=ejPqJ<@gXg8g7}}^G>^t2#wnO zYEwq<1L}hnsjs#&PIOGs&_6zvP8l8gQ0~FRMsv3$ryp7IuY@pO8?~B)a(UpTt0gGS z|4DC*q(lEfH?P$T7xc1$-F(;j?WxeAXhp&wZ$nk@ak#Z7GEg2eBsG_S7e|^OHjV^V z?>Fn|`*w|4=~{#^wtuurj#VoB0#f`#rYUKxL5~&^q1#-^jWYZh`X6jQk0dHs#~{O~ zA8eBraypXh=LiOD=$BbE1)mB4=A;~eLjd2j9~wX2 zw|$A^bjYo`VTNvKmJ);<*g6zaD|?-=?An!}9|gDZL+w{FYgSvRT%GhEBOgLdbfbpf z+96oL{IuePic)E+rM^xBc*nQFECxzR>IYV^(!?&=a>)c>g@a(FI1Zgv6si1{V>Y zxlKf#VN&i%O=8kRK8(p<4iZUUdyPO_={AIOHZBdHH!`GRHCgnxF$(nuDFNX zl;}?pga@9|w$JuqPt~GBuTY3A@Ds@hJCKQ8w+Bc3u@?K6o%M<5akq!ot@NtQm}$q^ zH+5gB#CQ3O4@~91adE&{Ztmsv4W~YlAD)pd<-(k9b~Kk0mztv=3Ao+8y4Yd(INF0F z9eYw!EJ?q{9M6efHF1&_pElRUl&5P*&z#B8ET5zLb&wsC&u-T!R?2j2cRrDFNZ$iv>Tn4owwcnI4ZKb`0s=96-=rh1DXDzCm%bW>bz9d zWy-c4TEoE_Z(YiSP1=x}dVCWm3CWe2V|ojrSTr^p3u?#F?7|ArAAZhlh~A?_Vt+_s z>>b|2WO(_O-%?YdQtL9wn($WI3dE*3z{tS)yqh(2ZUp!W?FazpW{Rtf_ zT1eg73H=?G4itVn{Gpj6%46NA$-dQGmTn=z%dzWP#-ETBM%VlU{maH1zXko~Ne{89 zHXG#YO5HLLAlQNg5cwSS&BF0waDtQFxYK)?p*@psFqk;;CNDiq$o>!W<=OGrRKRs~ zvt>|psYt}2L(da{VHMwOizTTY?8GL| zLuner*#L3ZSOLC?M`_f>wfJnn^p&)mT{W^i?>E@Gt$3q_fDSH zvh@{Y&P-fqI+uMZ@2D`;nrTZE^Ne0~D{Pe+p=*E#$h^c3j}?K4MfuVw%?%L|5iz0< zxuaKFLBB=nK~hh06a1NJ6?77SHFZn~T`KUsC*=Y!7r4_BjQ1%qw;7F~N1iU*_bX^v zu^o<|J)=ZXDF-8%^V%PHda*6P>;U!t1--coS36Lixo2{_=%o|MOvqbCl3h?*Gcic2 z=USEaMt?z>w27`d_4T4+MX8d4wv6!ju{Fkxn2XmxV_e?oOL!|pp%16b=UHj!x0M}I zw2qS-J5fxf@7bdGn^-gWK1FqihH6?|B>avrwz!n=g|>T`RxbXKj>@%9jeIo8%3)XA zIvw^}_neBQ=vPuDK#FYZX@V#D=~BIj{ZE_AXG2jrH5}0a*ldq@Z?-|!GkIq-PlLcb z;uqAQ!$m<(LZ9Ec*WDnE)tl0SlHcId6RYidX<1jD*pa_CogX&a<`iUFqMETHGLC_a zFiB&9y2*~NXce)_w+^eV1&9ZBDyC<8$SNy3&3|Udr`;-JJ0u^(n$7T5k{jC#3iQJI z{!O>+4mpG}*4VwmepL~uL^uJInmg?`8%bDiVVsSR!`rCjSR?gY>S~6LIj|I~oAyfw zKX!F_6dZEi4-9sdlnKz)f=1>_4JaZxbEuC|8h8C5El$J#0dhOSZ}>5Fjgy%MSc%18 zL*4VWH=YO%ELi^gdl+q=lPw(Wo+RV8*q>_}ngD*^*!aa3G8ddZkb$$*Ur@O=C-g2p zl0D!pw_AusvM)IIn$69=nVdX!`SL_vsL&)2z6DCt`*vIIwF#$)NtbHT4orCL*$MJ9 zY=F7*k#s;K0v+QvX^)}7>lJHNH0nS8FFfkT%C@a&@{?3M-?;dHGf}CyoAp0GbHv#M z>p#5rGdddGe&97}?~>Fh+j+pUC+`|((u!20Bvz^;Dk$**)|7hs(iq^x z7m?wt=V#0Soc-H0wB9;N&u`|q=TzteFP`YoiXkcBBGDPJ&*{l;=Gd-qM)~x)XH*t9 z$$gLL+OxPCj2N&7NHJ+7d8K45ZT0wAMALmwQIOIbq2RU%lDwdOh`r8MN`up=Ne4n9Pk)$jj zZxvwsxYV4Sz7OusXpe5@(!D+Skp{#hDOvny%8<`lG*k-r;VCW-;byTTG_N#q>^nVR zk3VXTt1oDA7JqmzJSi_`%Kt#OhEbZfh?X9rzU=+M>gqrfqThZITvcpak;O9}!C)Ofrz@ZEbyBN8j0P9< zzs&K8q%A~}t>kzx6-kk8FSgqJ_lR@zSOxA!D(w#5U*M`@>c00k)MoPeBHZGGqNYtx z<}RH2UpW69>}tTJq1kuxZh88#$3C&m5rA2w(+y5vd7q~|_-_%GQJIY$KKh@AINeTAQ2b_b;1)6X>NC1i2Zo!5R|Scw;hkD<=L~I;ODkNiKsW4;Wvd#IYId65 zg8$vlTTfouNH@qhJ@#D$z}jvg%mIea-GVoY<=YDW;WE2^!tFcX5^kH;$=FL#InAe0 zaERa-j!R$uSuaSYHYVMvPzZq#XhXO$vWw|?i#MbjAgK&J9}vkHU$_08aEllr$ht>hH{r6Pp;7_S?iZ?hP2dNIKLYIuVpIm0v?;LQA7-!WEUBekW%GdEt?j zKd*mr^xqvy87_XL4iRS8;qN*;a%y@OZp9W=B_w0&QQYA^XuAA1Y4U|&shK%nb{G3? ztuC>aVD<4!-&UNsJ+>gBC#!f^{n}gByj;`gggaM@GuSupz9pLvG9EtlyUHFjd_eJ# zyE-#4iPA1!(w@;CQ<2coXC7dYPK4#sx2 zM8^+M+kWhknSiv?)OAOa+cURxA12;@)uzK*YnjE*DiYu$LJq{SQ+TD7zL7d`cL656 z!2H%=jJBBqgCg9XCTlS^P0SOS1HQfcl*IxZV+=eDGNqXd4@5&qrWruVSA!juT(0my z$Rr^!HS#ZLREB%`;L$&mecHC@LPE#b=H3`8uyJL=Uj}xlZ_yTN!9+-+%L-$Hwl9!> zMPK-g3WE$@kV=cUXau;&8MpQE4yvd3^jDkkafVp?m?neK0UaD70c^J5(%7KfdfoI^ zzcePS&$It;ITQA(k8nwY_l`@b{s--n!K+DrT^JgKAM%bBz3f<8F>6N+h*>zmus-QR zetFW_RG%DnzkaDZoS$Bor4D=xA;P06UzhyHsUvIdB>9N3Nyb-O9YF;6f^v{6{cJzg z4W2roI2h*3wf64Mc>T0UY$HmBD-=otBv)uD4}0ikPjoGpt3n+0q(1pyxINMzI+ni; zelQ%Bn4)nWZoS*Ts=B>Y%(6>;UKa^D%PqR|NY#NZahjQd>^B}p@fpP5Q_~#P>vin( z6r0dfd@K<^FLLW2a)M7)xDRN5sXJQyMw(6~Nh}kIj&S5(`E6ye$T)p{L!Jj&#}L>G zq4nU&H?V{E;83CLB)#ZGIf&j#cr}Y3ovn|7?6)y+ zq|qzIR&$Off8LxBWeZmD-$goi!p%gSFUYP^kP5`mZvT(yfihZC{<;Vh^(C{uF7Qs# zoCW^7e6%nWw&%B+=VN?$A9mS0LV@)8=FQyRq#4fPJ!`VEw${)>Lp_RT792kj`#4}P zBmkXhr3#$_rxiF^7J_a+!0pIjp5nRdJ)P(F3R`s%7vEkMc2#UpA+RueScq!;m&82K z1JdSyR7M%Q^~@IRU}9RFENUOz_fN4{6+!kjz{+qjDb+>cVqQ>uw~74**^ zuzzVvA65gS&!Q8b(k=LJg|9LEjzaVx5|x~T_RXsQakp05`xgD~f~M^ih*BkC4q%T2 zBzaERpfVzdvIXC)A0OJ++9#58rhk@gJm_zV%FbWONZ+3CB#*%4Ne)`ts49WsD2T~Y zLtFZ&3-$Ubp&&bof$hS3TBM$x;4KKIdhs0%aI2t~nNE7*8#_-Q?|d_RQ76y&oI`5^ z6(G-hyN7w{uA2fNXe<<~if6lFg(1c}p+ZI9y}3(z z_}f5%PUt{LL`0u6-qnuqo@Ps_3Q3=4ciWt<=U9r&XWSFNVu73WPv2A5&gSC&u1dXA_tr^RfP~6h z%e7dY_Or~|*=&qY&HQD);dq+2llIR5r*QLr)7v9z1B;-Nb*UlgV!O>E2|* zvP;R3RvlmyJeZZcQn5l(Wy`Kdh=>*I(Q<08H+4W@qx3aGQu?0rKQZE{0gu9EOk&)9 za~3!Qj7kOsHH~1X71y%_^&`b1kzalcyo*zvNOZq6bGw&c@&`6P!;K_-O4Le-DB{#n z2t!|ivj#<(sI98P<-JAnH{NIS>n1i`c)?K9>a)i@IakOZq4N?1t4U2X zGnG~_pcvDxSBU=HXEKaadCHsYa_T)u^-WIX42(w9NyvX_^I9Zlh2}hH^}Prw|6E#ry){-62>^*~~N4 zMm%E;UH*4G{iSPMj9>e*RB*_`{pUl+wSVK;%#G9Ebg2^)47x1V95$6>^a^Mx3{|80*r!es$QtdO%cr zLE<61mwc4oc`2N|cycZ9_dmhTrTo$BbcD{x)Dz#!$=-$Ro0%(~Jx&&<-zjdE?OX|c zhjd9}igKKr`W>Ol3qdUgel&0UUT_K(vzk>e_w24`_IPmLV4+?(xp!^dK{DT?$*7|e z&i+i4LrMe>LNR{{-8t7L0zRrPve-Zb<(y6-@!|BKK191)X9RLy6wlg| zkVu>WRzNv|A#YRkK3q@7#I=F2NwO*5K8Yvf+&F}IhYXf%VE6)sa3O@s==&Pm+;k56 zF8;fA{^IvLkVNPnKwJT(|^+ejocVr1a;ki_Jf)HMZgbrZHMmiKl&fNYi$>=k3o>L?-C(Rvh>cnMduItDYA;M z=H67RGbi1-B%9@4{I8-!#A+dK4E$dq@QauZmLw2iMk4HPOpljHrKKt+b{%rQ=n!t5j}$ zkQJGu$QXj9l+o!G<%hRJ;@LKzh{JQ#BWZO@%fwF`LXYVR!0nvg$4@pZ_>mGYn|Y9K z+WYXzH>BkL>GE@4#WI~)TIZ#E9|~I;%r3OFtTKG4G2z|Wbsu!Oa(!#Ib!f(8$Cdfy z%f_?Uht@44n!LF!skR4YoMjqX*t5RM`0w@uWM@av-%qVn5lr!jHBO6xREo7n(pmhy z4cMS#zG1ugM=~g2jO}8r0U#^N*6LCn#WOH{u}~1{tXI45TN~u>He;=n+34Aqj@fvg zrx2%Uxg^h}X5q?l3w9mD!vUZ*0KK&W9iPgDRK1`XtHT+0s>g~%TT@8;2YZ*?%8L|K zB6Ud6N1~j9t`s}R2^CdKSK5nMjPu~P*Zw?v7qiuOmpS8!_9}uajq=|9Df90F+d6zh zPAAWfKF{8&e^KprFXoHd-^+!+{Z~JRtgl}Gs~G&w<$28h>*zm6oX6lZj%Ab5zyAYX zK%u|IpPhe+!m9qW_H!Z1Qvc1W_=U%@_~&kWYd5RaU!$vkjeo*ArPb4?FV?p!`_^Hs zuyXf&`Do49fR*H}UqyMs1d2)Xb|L_m1r$Uxz&FTsN z5yc9hgqT*AnGeq*^Xy_+y}f!5QfKw{JCtshB<0l)>sub({Cc(SB3$3QxOUs&x6d74 zJr{iLCExnv>gxI`uHO0kRk9b`*nYIVzKnfK`0CM93eSspyGqxa7wq?))sMsXY>8uA zU$yb){I>h?sFGA zAN`co-Rf61Uv0S^a7j4-vG{4Fied%rL`AsVU;FlIb#l|zcUpm7zg~Fqo6~N|AFUo8 z=B$rjbuRr~njgOCII3poe(B=*x#oSLrb+j3)cEoOH;rGClk}NTt0(xoBpa=Wj_x_ue3oj37{i}Dnt zlaDP|zHgyl%pD({r<3-Lm8;8VPG3w`Y91tGBSa{^1Jv?B)9A zt?%$Pz8mO)(|WXWT;R>>63_}?!n#|z*7dvEz67efda;VHZfqNHxBB>Yl|w%^-^XoB zPj9uv`RM7*b)RH{hHuf-MOWjcZIq(q^5*t6$JoZsU=Evz&u{S&CW|>`;b=*upu;;X1;eab-ePN9o0~g#vGyxZ5T9qkv9if+ydfXPRoYx%K6;v)-gdRD>_xwF z=Z~HYL(XmbF{W*G8E^5}tv$n-Jjk3)=ar80=f+7tdcs-fhWd#Q^NUy9ov?ZtH<(i& zG=_m(uD4uQu1QZT7q1^Z!3y7Q*8X-oE*3ZA1}fl1g)o1qhbair_#Q{X?suc`deAY^$ zw^#3mzzS-o?6hxZh^_j04Q1K1y}sLE^lxX@I?ut1SaLj{?B)96b^pcC)hQdj$!mF% zpB@^d{0-MsMeDR`-#%}n9@pCsGd*)BMlE>z`X;Xa^q>E)JQaEwf4$0+=m*~pxEo*B z9^Ppi*hK8d)#|+6_5SON?=QCJx7S#N{*#-0o5=jLj`iCQd)1jo^Yu5YlX39t?QgHP z7J!7~UY*~yzeMQw)j5AW<-7S;ax68=pEIrQ^$E*tb2GD*yuHpf>;KdG=Hu{+s`k^Y zYft#?S>}ybr_^NFhpgx@8Gd!{*Lb{LgkQbWKG)7F!8^Aifo(3r@DaN|>zf;=@z9R^>7V~Q$n$-Qn}N)q{F21-WFVEv@xkP)4trSb7_klPVR!+YbKxHB zF@1djeN4ZmJT=8xy?TPbbuUY?2Xv@#KG^l)m)~`mwVPa?_J^04*>9mVTWEd`wRxRB z-T;`b?COWx)erdFk#nxF_08(v{5@4Ua3p`a&1VyQYVynM8~3OOuNSwrZ^d*UZi$P! z&qP5I!iT*4CU3;~IlsGoxw-yf^?1M={nuNN$sg|4n@mnEx***6>eC+NJbna~8G4!` zf!$JOKW~4S&%9MiTtDa}ac25ujC|r`yMBQY!g#{#!^!XS;ln}7pS19c4{-_eFH?HV z)8r2wRi03aTwGs&cX!hvx1p!=Y%K9O>-0@tU^Hizy%&&$tCNW-kRLx?{V=tdvS7^* z?pr1Inj>>#WVGW}yB+r54X+K``0di(W+r}kW7}zrhE>0|xl72EHK{#r$AzPm?!L{` zedwDWh|PI^*MIocr#;Ib_@?9deD$Y{^88#D!BmAcbB;gscFXXGz9p%GQfYATp8-Af zmek3b!mZtHP+}`721@Uc2p0ElQ?2dJKt6m$ zLvi)|HX8#cpJcM@e*2&Qb@lFMV_#?SJ^@SAw%JoKAuD{jK3}r_UqWg88YI@y)Gd~; zC#3C)s+>UimQIZc={)YSMegQg*~C8G?Q~+&=U#j2KBnuwQTlbep7RE9{acpXUH9{C zd*yMvD`N}iH0*mX%ivVWYFV%pss z2j*rEPv{GbZv84xi$e5w;PGuod8>g|*Db{C=q|JH;@)1ws~5fOIq>jgPr9d9lc%{< z^OfsId%an|fbhB48R^NKJ=%Xx`-FkcEEoNI?1<{hH80IHKT(0 zg_vdLKpLjWZG89Ysw<_voe|6Vg}uZUhwDoV!#Qs_-8}p>6V2p{{*`OU;|-r`fB30q zQD?*0OOG(Ji)`4SMIYYRKd?yAWPsny+I)6*5sz=JrKGR--Cvx+q?s0T{Pa!h?|Q@G z&wYGf&y?W8T%NqaTxLzTl>YVoe!asJ>!`G)t+wty!@n1SNk5z&S)Y5DXvEG13`E@) zEK1if`7b_%$jsg1pF8hY=l>~gu0fP1lkeU4cC#u@`=7)qzRyGKOpmWy4_Zym(E2aK z87GhP_B^%2{yaWgO*GyQndg`E+y2Tdk<01Xyq^~QKHXr~|E!KCWHWZrN$;mqHTH8m z8NK5Vuqw#vbg()f=8X37ZouL0<{}rQ{HqRmQIpWmy!^$ROmA85?6y7mX0jL^Q$FqW z&eLtro=3099$yc=W(D=luCIJ{CJkgc`+dNwA_irERlN#DG{uf*eEv?0Q0JeKi={F& zMEOtVV}9{%d=*#!^8X%u&`)pH7uUD{^8Z==Ku6}IzZFYYpJy?=dq!xU-!u(`SJZkd1d z4f@->z54XyFIS>fKW@{mb}Np2Mnt`WDvm$#^OixU9`8`Rnu|}8@OP`5ms&6uIJmU529ZO zo~eBJhbbDG4c@k&Pae%<>34tiB|qFw#7a!`OSU*<(&*oQ{SCjSZ({sDyC7kee?L5d zopo*#zhC`sv%bxiMV9hk_WiUjNpfw|sT&x-Oz%uZ))Jv4Twsk`-s-J0D%+jEGQm*c>Gnz+3CC$@~H z+Z|B1?W+?zaVh`IF|{PYA@QenM0RH=`J2U&#O`_^I;OYrA`MP(e0qKH{a|iB!?X0w zPaN6fxLahCk^AsaJC#f@{JbX)%GyjE{mDR!@3Z4E@6tdh8P2b#kM3@6ek`8`tTwB= z?ez48OLo_;+1<81t;x-|-?ohk+MU?8HthH@rh3-Ru%n1UAgzA-rd!=j7n#wm?BCB`CPdB04-s{K`Q`Q@!p=?bxu5#8;-P#5zB!%m z9oA+u?%8)|_io$Le)X@u(Y9S)w*gT2_;SCg%k@>ap>B7p+hy+7X|mPbPIspP-?sak zUBh|g(+%ino}V3O&I1JcB5z+_o()H?$4vYPQIi87R^Qu;yZF$fdCc8(zCENBAUuA) zc-nRLw<@5WP510fOwGX}G)KBVYs2l(SQaZ&sX3cW*VB(v#Q3Lw{%>s)up0VyGKd_0 z$>8=+|NP(Q=F0na%OllWn2p<2Yki;R&jtf#m{&K|w$qOFXP;&t1zofKJzhKor2W{h z@}>Qb;a9XqdG4N_v|KsHpz(wJZ@cmb+v=5X-qD#qdi8D(XZije%_MN>bVKsSjlYq5 zLf>Bd!bh_}u3@hROKQ4%cz2BN%4;^;(mo~(yf|(8WV*NX=(K0;nJ9VA(yvE<(1a&W zPsc;a{(LE3(RWW*g9s|*;z4+dP0re=JxMAA2vC^K&l^i zd*rfVGy9M3TNfBU_;<1t4Cc<(PhO0^iF46gH=TYPg7UKWX+9f?0S>ciWqd)Q9|D1aYIfZcXmH^kzDh}#~HOB?4w}{iL=S$?N8^TQxGoa7*H)v;>BB1nuaNV!_Ql4 z<^%oQ`-Z>iS{wPQr^d6{>W^!7)=Ien+qV9=7&8B#WNCw+PA7R1PkQUMbKL)$i#bNa z`}1y&{=A)2PtN~e_WrFojwDMA2IrZ-A_{HRohf8y0VM0v>_PJ*S>QC(#bOP?sva!{ zh7M!~m|4slBQgn~Q8b&?n8|wHWZKVm9wn1$O@^Us%#C6pBgj=#iH$vD28W!m$*=acDQ7; zDKY- z1$$I!{^Zp%%?FlHcpEW1>yz>~fQCC{9}iJLrQV`*WCyoMkO&9@$^8IJQ7fvpwJ5#4 zt%O6wZ;IRZt_&NsV(&JU(@&P?hW(LUUcSt2q|ii#INC&)AW`eSv5V8VLzx#&9^R}6a%RR+U0I4P zrR(q_-;iJ55}`A7h+k0@D5Sx7v7r=ZZ#BXY-hIJQVCI?4iegl$+O0h|RaGi@ud+M0 z3}zGD(9u@TJixo@Y_OcWxA+L}<<1X9Uiv1loNzlE4_LfP4XY9qQLrN1P@FQQkk4e3 zGr#~ro?j=$Xfihf3WDZur*uQvO{{Lvlzt2OA;dom_y%1Wz%8J?9&}fkc1q0b;-wq1z{(+4gU$BPV@7z;n>33g`}Z^)^%yL3FKG%DA=Z&5M> zbJH0F7RupzV_BqQx_hKKN1J@Z`-YhQLheUnJUTfV^m4F0cK0YApCyT}3G@D51WL;< zEwntnRDhyZl`RpU`*IN5YuAC~GweZ>_1kT7kQM!WgyImPPaG;@MVq-Goel-SJB|YY z81eXcODxt$15lqM5;&NhM9(F*Wmm)e1PNFl&e+Hu%*J9yW4y=@9Fp(6p}TY@Yn$(z z)LwaT_?hKdca3|q*Kq=E1vEkIr$41(s_<=HDMOIGyWJ#D)Rxx?Hjs`m?o-@Cf$iYg z(K!aUMEpZ9*se^=+VV52J*tFg+bo_7s+O>Dbu@DOTHvhmu~AACCz3Jm0VS4_yL=@$ zX**7V3*X3@4M_}Zp-ZzW?yR?16@x{sYQyst6CRIB0R)e~s0QDuSm&^0H`xa-@KaBt zNcgCEmYE_8K&3aaKCK%~TJ@Bfjl-J=nmUof?1VSrp6%YK_zHC#GQp!oMrYiCS;Ei@ zbe*{_7TKf+@v6Q~7sIyC(vO~N4+CEEs``O?_}H`zM}8=F^t|`+G*Kc4lQ}w;Or|)PE^ta2T^}KngWs<`;U|2qn(>!cJ=(?F&YCg#NIG1hVObK zL#aD$f-bm=`>wF69@wJvCa<5Le@BN}u;-(J+%elPnyMr%)r1T)w$T;?!`6=V?TJ?t zzhzr2S|2o|4eCQNu7B!A8%L=;MBE#S&jxB$9s#gITo}1%c#` zONEo{sy|*94&O3tJ;e66;cE0%kF))4zm6`H8%}&Nqu}XShWpUh)0x|tB5FEYr1!JC z56tR`Gm>QkYPEy5JBI#g6Lp(mxUD)Vxuj#$tGJx zkCSu^*(lZ`$0}*Nj&xi*ysHXP7}D_wN6(r4yaN)Xw5@E;{6#8=8+-Uj=cIa}^);?d^B2djTRUu(z4&HK_UCj{`j%J|%?g_xt zcCT+}BWGzB48ZGbXz7~MFs+uE&_Yz;a@9$@-qCb%@2=bTK%rpDiWc;jM!3Y_4FVDU>dU5#VCW?tl3BZ~ON?yLbO?DgN6hdw-7q_96bQ?k`)N!{&5OZqCMC`J~L*>#iDhCCWy2TD|@m4)q_23Ch}DsT_j7@uUT z(VNNvt?HvUVT^&#!IkU&&oQP5$Lp{b-(u6xv0oC?0{JaGm(VN+d>br^`hWVzKs{t# z9eCv9bzoDLzyd~mw(($^gn)4dx@3H!?05X&FV<5u@W*H6uou{eyj4GgmG+s8jl`@$ z*M;I?P2?zR$;TcbCB(?1)jC0-)2)lMgXTHbDAL)nur>{CzD~sA#tfYpwOHiLP<91d z3SG2*WSts?pIiVm2R^lvZ|PvoCh^Q=-59(;KIHJAf^fMgmWY@OQHjPwo?#Zn)w$of zyO-?#{L{|8JzfQJVnlC+!p4D;sb@UttCSaGNpgi~`JN1Fy4qen-w%KyVo2*3&BJKv zYH8zYsWmQM)ta5bpxNDq&Bp7e;n?v|c;O4s4ncew`~iB@xTlYFwe!Z+&UFWCTZ?z_ zk*;6cY`p$?I6&;y;b><(<^k%#4sEs`_Q%-DKs|0;?eR7Ts9XQXw zBMFF%$l1!+GdM>=wqdjWz1(1c(d`LV6_%?j;D_VN9VK1h%c7lnk>I}PW#Bch_&Ki;)jG?y0Rmk?U^V3vNQd$oqs&zqbGIp}0C~p}(Cp}{ z_k|5)prV`6Pd*uZ+WGvhIUC&jyz}`zC(n&X7YhpeVt(*DFyTj+PK=#qr3Kf-M$!0o zH-pnmJXjU4Q-_bDj}D6_K|9xu`lw>?a9B>{@?gWh0SLQMVKXl zJBTWv0!g-b_J0iori#FEv+Z0>crF!%tFmNp)Yf!)tAMe)n?TtPO)8%X&%5Zo(4#eI zR+GOOA$gEw6LMZ#@>Lv*`~!>f;tqqh>U4#W?Bv{h4# zBBz@9L8C=Izn*(B%%$zWqI>(!3!!S?`ax6~BmTw?USSpT?Bch|CeIdl&BO<;0k)k6 z!B_2~x?S|--HpI(XIb4MH1ifB6mPbs6r5zsk&bb+){Atkg#24YI^KLdEF@oWzje&y zME=|+X0lrLw~v{O*xWj1vR$^fj+wl>DWt6|-Zf$}cb%iE67DeAfG=V;5wI8Qs##sgI^MKHiMMy!s5I-~P7 zhHgugD|8x~t1Fdb$>gz;hzB#}=X)ajb6v7Vo81c1j<>K5v5Q6UR(i0C@Kj^B>qJXoa>L7eeW`Pb$b9zksdI)K{8V5n5b(b|}F_l54aYY2llQvvVr z(<<+&m7~&TTHglT=uow7k4k!_QSr6%DK7_F!7UkFH$+!Nx=L7<_SeAv>eDarCv#15 zBCueiD;lut2LXp~QYX&h`Qk|KE48dqx`gzZdYgK3a%vsUPfqETvr^`fM(;9+8R$#n z-Z1NJ0S$qaQu3^!T30D0Wmr5q035U)GFr$E6PCuZzyQ4K5gZ6vo4m(*Ktzkn=JBd7 zj)PaV4$Ee(g}PpAmA_82k$$@~hqF6}s&+y(@uEXo6WX-|7WluhY1q2(VsuTxcUQJW zWGFzrxCiatSLik8CnRF`xY}{#e;p%lQpxr~YReiKnm&*iR)e2ES%y(YDs+-zNA3pb zvpk-Siep&5^p?4zPFv(ZxEXg*w3|$m1U2wtTWtvxn>N-_0`oa0he&3UAm0Mgi_96* zqMkBqxVx&Lom&?*zu9FK$B5usS_s428SS@cK{82enFb@KC4c&d|3X+H!Bkskq3cA{ zJj;EF*=MP*5d$st74Km_1Dl%t&)9Cn#pU^0tb--kvGx5>F?ZxOxODiTc+5z1SwBt@ z23z9&ovm$TJAW(@HOjl4!5OVnK(_!`-}`yzle?I0I-800yR5lR_Yfc_OQZL8NF^EsDTS2LET8yD)h_# z_+#0hvg$%2Q zkS)XB-A3ycPgY0~e7 z5|Q6W+bl-$F7P;3rX*QM5k$iHsKw0~c|e0~R|>MFT$4=c#Aiy(!f+`K*QPSEXIXzg zHpS0%IY$XrdUG5^#!;lbu6FvD@>**3L_QbZYwb#&xaih`MYcoWkoF;OayNO2u_`d! zzjG|=QlqcPMfC0LHSH9@D~6f*3#9=JyOF}ZPI|EbtMrI5HIASW7kbzZaOIC}st&Pk zccsB;2wcGKB-vHgU*da?mj)sQ`UqJA?AiHv^oo;JVY1wT4K1dk4QxW-w1lcwSj)y< zptT)jgVeE;x~14jh>Re?4f}DEHg9;X^;aFQ+K~nsr;rx`4imptJ;Dt>O8p#vDW&Wy z2xSQwvq;wB&Eru5$uG?KTrBnWkql=Ar&Dov^1Op!Y7_`@XI^kRmphz0GiMj9J8w ze_2bOI|CLa`?u}n%Tsc_TWdmB9Uvc1C#7N;um`adZ)WH-04J9&BV13&m&z+xl z_CeKl_b&VkN-L5W&1w@@$YCa{Scc8XcRF|)kL}M2VmL;^o6HwpFlusm%yI~-P#7L& zS24!^XHP%rUXi7Ff9T}CQSlU{50}kt>tf$~lj?qq@*2$P2+|-2q0ltTJ%~^vYZ|WR zd0WeA)0yVL7@X|P{2!<62VlcnWS%RyoN#=Ij$b0tI_^b0bc-ToLW526%kD;4McMrO z5(+(hO%t>81)TmoIb}FE|D|YOcu;p1K)N5MZ?j~WIl*duns(SEo@^A3Az?;K=q!74 z23<1$S^?^!<_1ObyhSfjypOSa6u&^^D#b5=9?GAB7cTw{44&AjRhW_P!wa4-szj^B z3KlG;m0@cL$rdgts@*r2lUi)a$_d!fA4xry zb0I^Htp_d$J{mwB@fgUcCws{|F-U-*#uDFv%nYE3;MF_1Vrf!Zm4%TWKuACpsJZk~ zmAAH3x_tQ3gD=gE0;X(r74y|6S49*p;sfAvew zhG)vT-{4o6)J+#^38ITqeUQv_5eZo+h;-rJB+#!$KRUnj??7QP@eg_FPMMo zj|StNYE(1*fL{PymSaGS%f4_k^%8j`KLjET9=r}A`Y{9=q*K5i%0bp#7N`(Zhw!brIff1FP^=bP+?hAY!fvM zjc3P{A~x&6x138pH-VR-KPYY;_df6JgE#5@z0SVgdovjPRcc(#Y;XdmU3>0?7;7Z?97Uc5f<0HvE~>uBiGx_dr}xOBY0t7) zzPcmzjc^8xj@Oton8kesLI?-aOIn;xd9=)Xf7SkA>m9=}utaG@YD^?hUf?!hq0+=P=MF67lfDf>j!Mhr$;4G$0bUs2Go$tR-ve?T}@~40Jepzru`jkWw2s+;6?Qo}pSN_*i`s#~_kxK=O@yBDMW%_prLC$RZW%a7^NbE3OeD zI>X~>=k&Rz#Loq7}F3b=;J~5_T%YtJZ6en0S5?Yud`?9 zrgQf`Mz`4gxO->!$!va|k6sPUM%r&DclYjoYG~xu5>-VU|24-Ph%+`Ay|a7#^%sBB z{c_aLrbX7JbBk#(e~>&odYL?bkvuZAILx|tdq$W83&A_PqY1BC zv16NK^kdMe7@lCA0LZqr2;%1%xfz0uauH0i2~r_?xOPzHLZd~L4;KdIE*mTvhNc9` z%RHUW4a>*aeRp;-XM{D;wIU{2){W41VU#2!w-H3_mY%?oqkI8 zdj32~ugurC_#J-RPWbI?p1z?VdiYbC zd?OCHu377o%k9dufQM5S!scZ0vX0>${E&s?MKiOZ1#C34Ao({swBD9y`|0)+&(?0U z4i|_QLizM4>Ia7VC^240hQ2Bjx-0FX#87ycCUIf>FnTSUz&-IY*JF^B%JlX46`icg z>D&{WMk&ny>@wVA{izL-A_XXNiz{Fwxs;k{kTYIF$zFMj>v_nO3I)pFc;pnhZ9UA%tNFm zSclM;0vL1NhiM{SL&#D4)^s;uGEpIl=IBM?%zNL_&r4r)N zEgsITHQ)nD1jL~HjGP(+@KA7jMjRVFq%Hn+cHK$NaCbL9%^53K&PIikeazGtnT%8# z)#ies>chD?g5@};dQSWOB%3bsYtE9};)^Z&zPI1@l=1ih4sh@TWMnLL+GfQH*k^L2 zWKu0r?gYr*H3oIi2=2ltK|=o;vQwC&Vse;5EO*YxggSCVHE%})Ln{#fT3>+t!rq8^ znVZZOk|9e+1)Vkur5gyKeZ%nYrxmC3~R7 zk6j|@V*6C==X-m}pML-QMCZR9%qGfN*K?B9;shjcI~PS5TX^_i^;w8?X*%_rTMOoE zOXKeuG#QfVxcK;hcf@+CeOkf`mnJ~omkP>cih%CN46y_hC}Yoo!h)eOOAK-)6T#z& z{2T_>;QiB|^3VNt5XV|M#n&YoK2P@Ax>_qwFFR&mS>&nDmQ6{w=+#iRBf(C{(ruuO z-V9VJVCT;|C!TQmQwu{)qCUK12s-S5<}1uA3cN%I3wbSN7 z9NpZ9c>NX>t%O=SySO(|a7|Q9{?|${xTu0W*-#zA?~=Z!1KFGD+>Afzzz;toFyS>F zM-6Lo9c-l0h?T0x+v+b!eZhF=4#ymn3qf^6>~&Iz82SjWQuEo^nM)0g#5sC3nx?)` znJh6Y8idy=?xre0--4R~Sno346y-v=r=P7&&Jyk7HR)AqJcNT%AXM&;hhW%mI5P#XVFZvGV}B5>>qK1i&*)oNjR2TNy;^LfzM@RZl9iL0;COx# zutphIDtt;&tsna_4cm5%#sik|wWkRpW3N&%@)sO|RA@Q!EI=&aOabX@#siR{!Px=g zT<{xw;LXBud^}L%&_fx1pSJDXSgZS%95dZqnquG0MpK@1EXnZAn4K{)=hv_RbF)-_ z!Oq3d5EtMp8nyqI0*%=TuynkqQ={IvMS#p=B&*~m7)kn!pgW!$u-lqhUHz^q|EyOzE4~}ccFI&AKQg^6$&HA(Ydsd6|f&D30sp-$`aiQXf zYKr~LUb*UCoooA@c$cV%pAWt&vR&AzJi8QAAjia*X-F?p=1r-qhT&e}7D0ywH+#DZ z53Wwia0;B02nRn6Qz1S=Y!St5!eZVlyDsIfcD{b?fvY`Ozjoi%?yp@NhXRy~e1+;w zjnJB1EGgW}+i?^&42o)N;NwOa?Rh9OCN=-gHBeTfXhlm6k(Q3OD>N<}k&oSQ`ie^Q z8BoSgMUZr(Y~?YhQ0(ZSl9eSf1erT{E(EYECid6fsB#r|{B+>yU5CcT9W^_0*43xT zeov{rt-O|7c`ff)!1g5Sgo`>!?_Ae(hf7oPOJOdNEV6#O(a=T<3_Q-n$Br(*<&f#@ zhR9$@w?oZb49NGzPTN;4@Q|G-`l(UgP0-L$bd0JWpaj$e*=t`h5aTn zBeihM?lu*l|C;@0Dr@r%V8wT~niuowC-+qxS9P)_+x8Sh$UY+5OTc_h9gXP*=~ZTk z5ah@^yO{Y}?sv%8nsS-L0+=lqeRI4kl6;Bw&pIP_{6t+RehY0>O5e{glFtfRTuq<` z(hFJxuUm0#X{(}1Ycc!R#cWC!ui4^kMndBxy?T-Lzc9l$Ozz$N?9_lTLG4;z`gScTA`mhWm;{G<9WvVK8Td_rLDaJHPMqW zdiyI{F?fwl56DD)uiX$gsD%=Ws<{F$DDH-=2~KF7kDcBCEnT?*X8RkU<~On-iUE)+ zR1e>De{E5$Xn?{()qNFZNL4GqHo8U_pAJl0z*zPxh}-fPvfGCmhCZ1T6oET-ZEIj* z9Dy2;mE(XIj@HC))Ev9fZE*wBSiJ@-06~m^T1Ml5F|UE{mZ;N%N7GM~R6&ekd8-|x zTC0)aRooPrMvJ%-sb;Gfkway#9(SgWik5)Ye`)$7SgvA!v5E@Kl{`@4*l0R^{F+T^ z47K`ceLTDBS_!P{RD_W&QUrwa*;Oss0!TB9Zi57YqXt-v;mYS`BpsGtqwj#Iw_UD6 zHnN$PkZrP;R$$zKkiJewxj$3&Si@~pWXI5-5p6-WruPl@4;WNDjBaKA5z<-|AT`#Y z0YcXbC)+`{q3lwLb~DE3(BBpjdtGQw#u5CR5prWr!DL*C0enAPlU=wv4avo#+<3SV z?V9oS&|YW6Be*vhb8~*`LJxO3_GlMB*P++!UbqX$S5`=dkXgZ~fVblHr^^Z6Ufgdd z_t$I#mc{A@Sg>Ztwa?q_NC$&(Dk?f-O1i$7 z*3|Fz+H+)pl{R-fYUyM?&UklU3+iOqVAf3`hx=XCk9(#cpRV6x!{PIq{bc>&yR(bN z(?5B&gy=V-Xb>C2149F%gkbAu??&^1$^fIyJO1e(tHLgMb|1Gj__#y>feH*1cVry# z8^IVM&voQrQSCUlgn1{qw08Is_QLP03PjtoqtS2oAORF!3!DKA&_Tyz;H~Q5rPoDP z{fx%5#TQwBJTlF#iOyWmp-VyzU3WRl2Ssc&O#7Zcc9!R}T;<=C_p{eJ3ulCQKpASC zU0Y=KUi45;R;XqRYL*a|5#J6pZ`6DR-m~R`<6c!N@G8xj;G>vnZBxHD0i3b{c`sOh z4~r?y@kxc^1E=8;m>O1U{>Z79%Fr}c6uiu5(^rr3(c=14xq*_Y5LqI0l2Kck3qgXF zk9N+}L02MZb#F%R8tNbgNRr$%DX&alYP84n9dQN2RZmiAC%e0mp|nR#>^^vKRq8d| z07VDVmcvOo231fcD4G;1Qws~*gp=>ub0TrbqH_|_)l^SNxp3`lIBnSav*Dyb?D$g< zzMq&;D(%`qL2>ikfN~j51?iS_19fy87G6QLQk)9Z9c$JaL)qtv#>`WdAV8s1!80~W zgHntw3CuRW_UN+w5*^!^XjVPAkpIxJn|jLu%#VyJTyA*Yho+cl+(gfPgsx zo*CPfqvBK??~mxnK-3IN%EiSWsjoEEH zv%Z02#j#OMp*t-$IBbMMx_$Vx?YdvLDjdyN2gH}kkeh7U;076OuE*DCA!uYlun$-E z*Q0{1wIS8T42{9>>{=NF5`!68H}lQLQ%o&U*c61&5ZWu|#X_=^8E}+D?9Q%djt*=#E0wS~`*FHREJU|RuyxeNjhK0j-7Q;?O;)58C7C8igC5+4s~}2X{@f?KHIU;b zB*#sZ95?9+F5io6&Vr@_F(*?Xu|@BjT6Vx{s$FU(vHo!(0-B#=iIzm*k4xG={V7pa zMY!yBj|wa_`+}r9#qEDuRRpAKZ#Ds%mX~={%3^b2FQG;JEeX=ZbK@a+(ef$wL77l} zp-H|44)E$t3|{qTF{iDI2pmO|*v<_fj><9YBhl-f8Q7&icviFz2UXqUn@z<=qwP|L zVY6Fg7;n@tx2!OH7Iv9SiX_wwtvo}8Uh+;U8b0s1K(*b;r~r=8U67(U9D58TtilKT z2SpnL_l+NU{>HpO>;NjF^#(8ei{8Mn>SSMxCPU+`DkM?N>w<^Q_ECh}KM;cmOLGLk zhm8V!rTk-j%cgpsE1>XDjIx4(y7huhni!O$4UmJJFEeb!gL4iT! zi=N7Gum(;znsfsILF#UgmwUD$xF}XV4l5%o3##bnqj?~CB(7Lbmmp9!j7_9SkAvEX z3F!qb-wUQbjJ#)Rl4|-L{cqg?b9bGvM!M+d@IJ8np1R_>s!@axgk zSGP*DVma?SPP-xST&E!d5V$hoYDS3wm|7KVk~G!>@BE;&k$Z4ZZHqscB7aFa)DkHT zkwEumU%^}_#_K7$J=J0m1Lq$>i1igc*AgLBbfn17LXiAuNt~Y}BzfXUpkpWOR1>BR zD8x0?Fj)0F`aD-E=Fdbg;OXWu%)LG^;B1$5@liFrg~p~@5L8YPtlPcO}pZ8*`;VojT*ELOTzxWraHBgY}+$!3mVa;{qfXZci9_G z7T<&=QZ+XnqXBzRm3*Q{=yK!sHjGB)Io_3=?p$Pt0Y`i-hp?ePfpeJ)A)USP3yQ}d z$MEw6BKL=5%xPa44cQApoebc-5JIrQ`kKO^3h$ifoek^a2N**4bb6)dU8zFZb(bao z*JKB)C%Dg%Q*h(2H1E@SI##Jl9c=P7YC=DE8`6&u4nb}}HJ+=cM>B5rbB^dU?~Mjm z9dUaEpM2%{SQ>qQ?8~w+vviV$1O6W7U=;;ju+wdDj}P(PzQUL`b|($@9H+zjth$Wa znHhJhfC0}ululZMQJ%qR8N(xA7mIAtdus6eI$aFgzBG39Jje?L76;7Mj`cCEs#bDFjY0t5(?$@WUSz|JpiBZhX}#|Xq_37tG^CeVEUsa9-}zhf#WE9UQm-=>Doe{P zvc@o<36QFFcJlq{*El>=_M?{R+Oo*>-?bHffJ(?4n1;}|GO<|s+Kvn^a_?~thFKFe{peOiIFs14|FUX8hI{}If$H}Ags zA%=;~-Bz>u>*w}HG$j4DC|@S5?6fmpIw{A;PNb~58D~)MeZ{O^7=p<*7Kl9|PC2z2 zuhnuv~Q;B{85EIGWkOthwR0a6Qh8&jE;D zohm^B4Q05NKy$TATFkIMFNbo?)?JS4?aK1Nm&;ZN$Ep>zsWcg!NB`EhsoqnI#^}9POAkesglXch_zGBg*e6qW&+FeMPv2I5MG* zon?`}${vXIL^kMNP|Mu~MshlN{>{s;zj=A~#nFqyCoiAB_&29Ja!Nz>;azyDLLPOT zh;(n5rGtkkL%^~fw+7BE~LeTe<+b5 zd^yXmvgD8d-F04WJ*fA8{J&Vw2mPJRP_+7O*xBb?PWJC|U}|KQsJQ z%xYAQ3J(6J>f5_8Rz?PK?LPjP*g6AKd1^6D?Vo=CU!zgt%vWjN6yfa!02|K#SJ~q5 z3KHJq3v=kTjY(Cwa3OR6J{Buf_%IG!#Kqrm|R=GK{tfW_kE3hgh(bOdv@MC~vcBN*o|K{}foG50QO9gE?l#Ycsf;oqjy(k5obe zi?rHb5ZQHhuY1_8#Y1_8Vv)}K;z2`*SANS9WT@|@9m#QKwGoNR@ z?Q{aIe6B#oFj%^j-|pt`;{^x~M?AuOVfL-5`LF5QjDN1)9`h zv)$s|gCdA5x?uU1b!AVTp{Ey63-^LngE(Li-7kn?in98qh**_?+FJz(2@o4yl7a7P zC4tndW5#PhNMUAU2IWP@v;%6cm~q#^JVSY%%_|+d!i5G@jT(|U8b^1T9;xj%lX+M!Dw#3?ycRHyJgeq3R~Y;`IG8Ck85MH=Ln}` z$Y_P>KSZ5qP3_WAIRvz8$xYEh+JU&ec$leE7cTY4(XfLeSh}AZTD0-i2w=``6oX;D zKvR$~Heo(vEGq8EmdTvLC1N>L>N1<`(F;9Or?pIU>i;HoA{XW080y>V!Rr+G%6VXz zr$qO>I8yIcaxizLR0}B7R2vDEcot<4!(@@|9H`;LJaE2~tx+ipp9rJ!WN0Tyt>3N7 zMZ~iB)@nfMvrfP~GiDu;)u2nc3E6(8Oao!mj@^`}QvG!Oehq7Zh?=?!UJ%GBN9&-V zHmLc#9JZ&C`wk(tza@vf@fTLGySGw09vS`(f$%hxD)E3;HfCgH5~|N(Ac}l-+A*<8 zd4NMAy(ORzpGbVtv02>y&WtRszn=3i6-*0XDU&>;D=mSR6Tj`;vJhhnFRxH6$O4U! z+-w34vmusYju!>m`;@Cjtw!jgsSYlv{W-h!j2@aWT^*)0AvcO^4saf^kcVRnBMtgT zmT`;+-iPcju}X>wxIT&M)Moj4B2|R&D8spHpAZkozyOB@o3kOYHw;gY)BY3~=0XD% z``@2`Cd9_^Mt_gY#p+}TI^TkrkV=u>Zkvi-ml;E^aPC2snWC1*$j67g8)Fcn56oNj z*&rv%jca8)VvuKf3k}=)i}QxnwB8g+$&clIc3jfnS$h zYlU$4u+7eOz1i?^)ieLTzbY@{`CPd9*^YHE`(42aRkB?X4ArwFod+@cj4-b612Y>@ z4VU<)==f(ec=P9g%1HXyb24?lc1BfmtBmM7#m37I6)}Xb*HOK{OqQJ{9yI zmt(W!#6^=cU=a^WyIEDL!O#3)f!^_v1BQk(xERSxC}ignZ8{)RDM7$4jjxwMuMYbu=?dtXa!Dkq&5<)H;8EROZz zm=oJk8c>Ke?q2a*s-x}!@v&x)RrwwhU~u~4E|EV^8R-nCg{6jHHiJ&}kiubJJ3ohE z0yrCm3?s6wRTkOb*El8%B>kr+r*!fsl8ZR!N141~nv@Mpx>S{5riTEorph4Qds-dN zC@TKv{O!!_RXww}#i=W|$$pwkHlG1JzIgA^_z|p`qn=7`D|9Klc3L7k){6lu+0K}C zglDT>Mp57hzc4}I1H(}2IaV`tZW`7!j~p&G^<=XLt}H~nYRHbu113ClNtAPHA!8V`^WqG)h^3sFZ=1I6j7^<+Z@>E z1iEcoYR8KnL zqQ`dKH@sto`{YVmbd5Cd^>fGd;Zr&-{0utqT<-B%>#M>14pAkq!W9%tx?@wTAVR-S z&{9}FT;iR40=R`%P3BF0C@ zL*n&Bkv>L}QUkXzPb*)TlM-Esa$haZ3f62StP)xLE{qmYf`$g_(;$If3XuG}du^B~ z2X9l|klO}Uuv_Kl%JnB?D`f~vTpMvnozadAhJT}lzRY6=ozi`dB6z0` zNn?sTk=hD~A^c#|OWUj4qq_=TS zj1BG;j@uAK>`;_`H?v(eA`Y9U&UA;;B(LW$Eh9 zi6c;DohvqmDPAD2WtJcgPr(IoahR4_iaf_9(BrIs^Mfn1vr3j`>w&8O5Ui{##4T+R z_heA-m_1nmB=KSkMmvZ4XJt^=8~$rLyR32mFRux6iG&V_%(I?qN~=l3 z?6Ssf_cewi8o$#?D(+45{W5$CMys`eS(9^mtjdyjE47chO;ykD(@fCNVXZZDB$=#H zjKoR#cf`cuZ%}3t)zQW5F*pT!LAW8oCv2J&#ph0_S2*pYd~RdbtGG1KTX`JZ9{FM2 zqsF6b6`4e(4#AB`WHAdkr>BgodIfI3xmpSGNuwyG6zB4xE5#xesecOcMqaabtbZ{F zHN7dSEEy*TjHXA`^BJn%-JxK2%lJd`UWkTP$kP$*o^FYThlB4l6jTea>ilkvb7C>9 zXbJDpW?Dx9uqzg02(5M7@L1;Aj^RA}NhE*bA#wVGto(-*?T%KQbR^3yh16zm580P{F|0_z6d*}2==43jh zyS89NvTO*6#*DEIe}TF%k3t2)dI8(T6~*=5(5-QAikiFoYw;X{XRqTdI2^+14+?_s zsT4ichAq=%9i&XSzK=~J8db0guus$5Ba_B(mY>(SgCB%NpGRRL5pandZrHHRjz7ay zh!HD^s*XJ(kd)xMF6BCMIz;+(yZX+u6usWUptsa7rQ4+vYsps;edoA;~Ynu(|p|Dc#!@DpH< zS$E3Ha3q7~3C%gNrXN3D!}qb59}!xvn|tpo_k_k&6PSa9Qj?vN3L=!%(E z(}}wW@#Pe!*XuyY@!;G&1t8ekwb7@+xn-u$4C2cXf>DS}=9A#WGqe2uhyu5W1G5fm zs^r%^<24-f>MumLW>^Pl-W&mg4{!dZgikDkQHv~!2UMA;h_rc)oHjEwL;UZ3&AbPQ z2W-$XW+)^AZi+Usx5!Bk^jD~Fa;>r?u{^!0aIc#y@ptN96NH&;LolSKf_mNru~^M- zSof|GX8n7?56Tf9n3Aj$O@YKYzjBR&UEIq)wX zeWrfpzv61xt=>4o=OH_jo43g%3F1E9ZcJo)17JpK0CV!@2 zXv12ec4O3&%l0$gv;{E6{hRXZW%}{4`Od1lw2>>{(qhv2QG?IIMOg(~E8guRl=UAn z2j(vs+{s`O+oq|ZNZKY*C>2|U|D!Z~pPj7?FG+EdMX+da74;PfS=&>!+MvA-QBhd- zu!f;3Aw6cb&#YGbQLgr$?99C?%Q8H371pTL`}-o*o`+3=?zCi`00f1Tq8qbY;Zw2- zfoAscYKEHTmm@x8pLVX^s+_^nTd^w&>1g^@hnE-v`^~}ujpJg(eOJlJUsc%Fwfa(E zCeeR_k-9X)Y*BlVTwqHm3u-gM+9k0iVWewYS=q5w$;ey;JLO6kWELHAJmp|DlYJ- zEP2&iubMq!l4~{C;?a8s=xr%#iaGa%s6la>zM(@L(s+%>X_5(^8A@2^3%z4eP zb0`CS<}r%U(S$sW^WrfqA+^rD3T zQGTG>5sF+gD(8tRqWCPd@R&|RXoPJE1# zp&>h-%{~k1^0#c38tYUd5gWh6Vcv0wT=vjf@E9n~YL?bYhNn?Y+1unJCrYZz$8TCx@U$5v zWOot<8R^JA+;iV2kZH-G=J@T@Cem;rguA|l5}Z^n^EC-C!|m$Sd*3DQEfP|jG3%pb z3_^jHwJ{+Pff3HI14|yLY$)l6eDol%SA_8oBy(Vmjs?3y%xv|r>&4s@hyS;ZRv*TZwbMA9@b{7o@e^0Hol@vrXy=; zB-bgpmc1|7D${NdrN&?t)J3aiy!wF`c8Pj$EztBU%2}SyzKZvx08G~ty2!3jFoZK< ztXSA|;E`->sYFVXlO(v}npTZ~1sMl+`h+S1NdjhSXHdW14F{x!$~kLc*U+yutkf4$ zPf+l3ti0rcB_$jjdahdGbm+$BY4NtRpQriDYg2(-1o^Ga z5*T@uf*SjfBrx$REgY_X076lI*a;r+XlT<<0zL_~C>B zdvQnJE-&{Vvt$*gKv(8TtV4QS-1qV6TcZ&uMyFI={*9zeJcE*J3It1_i`q04Xfm(O zUC$U+Ila%<{_Wb_9q~%#)lzITP(Ge1F(iv5>3!Rn3aTC+Ki~WL>CxOnEy*tWLjlBy zIjKnrbYTE?I-OkxF!lpVLh*c-@o~5doLMji`6KUWeBLpOS@G-S;U>RE=jym)KmhQ) z#nHBJ#iSz?i@rmR@Z=JJi$x`xUirr{wKvpBN=G|WbdzXoD8qqc^57JvRpxf$?aBy_ z+{E`*^BryBqXn>h0|57P|DzXI|Hlw+7Cc8$_oU-7jR_p1A%Cl|CGnL{EanN2#(n?Yv*9QXb2YKp_xbIRVWs-%BW}nBsPKct^fux zu;?sgCaQy8a1Db0c804LLn?SN(#(BnAyrN~t-(c{gelr(jKuH@12dGxe-PqQ{V=%7GvR=wg`N>m@|~p4sj`o+rNtj32u}L-FwN@e6Qpa zqKlO&a(pkgB?CR~l9&kLncfev`~BOwBt1UQ&m)8T;k_gV!ftmkI4l@w?Y)l@lrGF2 z@2C6IH3P?24hBR$LHM(k9eF_9-;Rgt1ClF6a)CLnuu4)^~HI038dgx<;AyI;+ zCwsNylQ+t8KUPLVZ8hwC%+gp}Q96_6H5&rIjFiR#nS!3gnj_WnAR-w0TM2229}{<8 z-6#u%hl~6@&t93kA^6_ic{|CtA8m!NFD*U2q16;n6$**|3;26|jsq3h-#hN>G!y*&1ym2S~nE*fi z_GvfAx~}_0Oh5M%71{3Y)6)%q0=izGl>6)D+I0@zFR!E9b&jY=tck8#+3oyB}3m5$(cqCg*R!Zey`{nVM4T z(NX4B0yWQ-^rop5=-=w#GmmjLP-<1`JnjS92R|7sdkoJ!g|4has<%mHW7Zx_qGK)`vuELaOJnD5}-zR&r2?16f8s1{4G%^mCki z+9z0^kJg`(LBek}Ze7zRuE^V&h-5Jhd`y~h^(-qCL)lMjp>1m|&AaplY3tJYDghml znek4`=9>uEw64Gxm3IXL3&oOz-L!jEYyHbK~)te8;)2MeU$e#QAA zaeT?tC2kYg#hwpQoi-4I#MXJAp-5@~&$teo8aVj?UGyXNHCUh(d$5QaGq2#vjbagR zoX=utw9VvJ1z~M6hoa9o0?`$O@Hhie51W3 zCCgwvyppAqlv67FcBY1XlfOql$JPo>y>yts4~@;C*uj&-e7Sy?2up(YfG~LJWshzp z)d>#!&58={zN0O+F!!KqI@7)|?4Ef^8}+FA+*kiapTlHf_Lf5%6`Sk#us?2K=WG%i z5$5+by>~ZB5MCjx(RHwe?oBxaIo4S6w{(;bNtrONL}BiBoa7AcCj5Nz(h_;mqr&uG zEwO9m`!5mqZal)v065*%1r5aCtQX=N2t#;^d})Dz0=BbsDTWA#DyIUt#x&NYhs2IGIxM2Nc7oN0AsC_ ztE9XZk6}gKLjDYc>Fr-E4+@KXG-U4}^{WL}#3{Q&Q3EqKhKg;JZsBIni;Vn}GTTYt zRmJ+FbDHYqxV~WZa;-~G_97g%Z~wUBdapINZWU5>&+g^66swCS;TPXJ2bQj{HS0{Z z^i54Cb2G=$%~sgfrZOWZVYa{r_^7yBLpwokfiyeGnwJ4lvGhTxMs(yIk$UVlgM_}z zvRkGyGYG|pHTT8d&{U0Vga(+H)z^$C*FGVB(+z>Fo85^=u=(5@tr$H=CV4%fwkeLu z1TMneO8!k*r}ILh6J*Ud1v4pE2Vi8KJvcEB^hbTXq(`56yeRbim}sMExDV4d!NgYf zAExg8$)0BRXcM}1U4GIPYHI0}hXT+q(#Xa60xG{eVTlb?NGxl4cGyjjl`aC&$N-() zrGnUiE3vvc5+$sy8nV~6+bN|Oi%N;Kzvo~yzY^eZw!X{iHLv>$$benlO-U_LN2Dvy zyI>5w4k+l!gmPdcnoimE?ac2l2g)B*RZJJ|?pB8_&&kH3xhPh-aCoBA>hcuP?bthP zM=okf^0Azu(hB`@VzLz3IVVQuok9;OJ-8~z=O#z7NhN;!o4QyAXH$?5G-U`h!X7%8 zfdOe|*;Y}wANRr=6}+aZFhikZZe*?ajm2621aGmGPQT`;>hosCRL^s2>8@Vg{WOgzofn`29+C$qeW zU%04o^KzIy7EoS6FP4c1DMnoFae=G!iqw^CFoM9=V#lPhR%0h{KhDmlw!I>vaFC|~ zJbjUNaenPxElM@lqXP3Z)F#s<}qd0|q z*?w*TJGPi=BA+o!SHV<<5nOD;ef%Yyy}+L951E7>+IS!B#J8dMl~=K`{Zixyo2b+E zzK}5^_3^=DM-+rO9%u*XI0fuQH!}(@oh|kB9HD3CKsS#DP>}LQw(_=gg}hIkQcn&c zx>^tYm<&It=rA1}>-&$E;Ou+@BHtL}@pW%$d69YmbKQAlj+`*WHo-C0*0BSJ!cQx0 z?owJ9iZ6cr*u{k)bZ;z`ir#S7y_I)NfYNz|cT7L7_tcC!u`7U@xdm@!g%KR2%QTG! zaD4im?YpA4fV4!=f5}=5uuNBbH+*X2^FM#wo7%vv3fHy%P?lwMYbwFwWp&{ks9r?} zvG)W&7}}syA}?kf(Ny^BS6h8fSK#=eE^`vZ(qrhEV$xt_&|#E+fjpsARvK}s{&=(F z_8u~VpsN;3YD9f~URWMY)?vH@RhNnT3fB;f9@W%{f=dJD$-0^+YgVe0lB#c!_3(=WpN@1L{xFktvqF*&Y@_RzhVBo)|~?n!lH2i`5l{uvQKP zp6^0OYsFpQQe?nf`k>9kiYe0J29_(11PNZox4Rf6uGi-k1()0#v+2}kQ_-L;`7{^r zQK?WC-yKyZ0Vlu|FUnE{9HCW5FHP#NTN^2WF!F|hcebuFF z{Vxm?Xg>B=II|ZC$l62|%@n`LpZ_#y zdTnW;qgJfAb5!dcQ!Q8CQiT&vpr={?^$`Dh}G zI6WMd%@o>zi33PABXKmL#K0L2`M=cPOG(W0`qK=a`d1DQHR2gkyt5@=%PSyg*Mhi^ z|I$v3E|kG7FV#J`12$5#Ntb?M=QzJfqd7~SQi!M*EOn>~e+#WDZSd>vUf44Tlze#g z#OU*R&nMELWJkHKmQE+t4cXmjAC>W3Y_SoI<&Xk*09NsY_0fNso1Hi0eyh1Xebdow z-D*TOr@c7CLo&Ue!KT5Xe2a;EeTLI7?Z23PPf5_v#S4E%v2M*Z-^6_4Hg!}ss=DcH zYeng}l;aiNSER@FZ;oSQF8uvnc^*iO-SUPLIr;FE(QOp?n7!6uzfz9}lm;T!a0hdQ zmVCM2=g`#_*P^SaiklrtQmViQ^&(pBVj+3!Zef@ide7@3!2OMA)HQ(HPVioF>AF`8YW zZPjg~7>VC^pT+|h2D^m#+WNjQGd@^#6R4_q+%#w_x*N!6sHa0BQWw?Q{6qBCL zTsM4ig4md;yH04GHpW?6r)8bA$Jts_tet3sRIJeg^0nl3>slE%E1NK?8ooi#5*yZ}xZAX!g znLdrmtF8zmWYX1%(Je!5PsUA62U=YybkDd-{oJLM*j_Y?uH3gli+9#m5vRqLi> zCoX6}ccWEZ%`9r5tBu-FyukR&5QuDH``W#=^laojq^PzR5Sde0{idL<- z3F2+Dif*kP_CuM(8BJ>z8l$Lk*&KLtU-q-%*KqMm++GV21;OhFyAJRFLaGSH zKKZqFh!?a7|Hq|~T(#nA&^rAG&o!q_xcAqrDrn7?N-g2as$IiGA+^`OgVn*e+4NV~ z!e(t2{F}&$h%WQ1GiTnah};loA%=>ywM+(4Cm7ezSKEZK-$( z23efQu$kl@E3=0BvMK0Rde*P>nHhbb`n4 zXwlFScr-+7L7~yrXF4`?vq`ZX+9Z%5+c1MzmCjm09u=5HbOgQy3FKelHBd80DjM5C znSu-aK-;*+|2V_Z%|gyGH-~zc3y#DHe!~GjdA${(Bj8vd;YJzDwDGxak7v zhdEiuobp1@`0M99Ct{eyRD;N<>SD1fc=k@-ua!?b zELK!I@cw&G7DFC*^s%t26_jIl7q{~hqd5iJ3gj#}BU`iL7=pLVKdrW`>4SZsTqU*& zM5V+*xqO5$KbwGO+}HtP>rH@Hg7a6&_k1n+u!=Gi1FdxsTJlM;daY3Nw{d1-%d>#R zn)C&C(Ed&|!vrTsWqlkk%7fahx6%fMJ%C#Dufl^IvTyq3TX?)0V;hA_2DfNc2Qur? zB58#k13J{oec3x%< zt-&`Wk3_3p->V@Oax6_yW^c{kFC!TPMx2F^sMd^#|Kih`^pi$Az`{H*gC1Z3FH7G_|b)X;wz(K6i4;v z2w%icya{|MbDLvTKwlAF03Wf1`3K5^4kjMQY8EKpDkSMDZ$r_wT(lGW!0FU3D!iMX zmEr*Edm5^psZ^i2W(cT2->J6_&R-f18woB!v|NMx3|oz zw(R;2HVigxzMkIpR<{*6H%G^H`RnR6L2j(z-%4}XzvrKZn@W6JS8_-}tD4$3lw|fm zqTODa^W;oKh#jzA{zPsLUQC#9=4F#Y&{rB^#-LAtUps(N$lbEmCLh3sY3 z+GaqYEKx}$JC-{T>E6bwkPO}Q4d-U8IYMY?lBsG{|APkN@t$9PrZ;=b3y%r4&^xg@ zr{ZBtGYhNPza!6V91Vv|&Np|1EY?bTY|hv$2=iI4eUyd?*zPUZy*L`Q@L)8MP{7{(oRWb4%vz<%^$6&ol}ZT) zMhi5>PV>njd{Fp|l&Jb|KH{T+=El7ygNuKmfb*CK3PP{GhT`Q-R*>L&#$P77Q}^(c zX?rU1wo)9560HV%=%O^k<$_!FrqxWzJ~`%aprpgFf2O7oSl#RK%#?7F4e_RStPiZ> z=qDNG>SH2Imx6Gqba*;ix)g#c$w+vL)DU{CT5L$Aw?bt9+}q}EU$v5g&NCMVn2op| zm2skvevon}2k2{1$7w<&)zSd~LzCD}jUTv7g_-{!ywG~{bwIOzHByMn-mkD2Mafpg z+!mde3$>8KjNkZGQeo;inmIBo_XwnZc!{Nn3B z@=BQ2oK+z@1_GIZC;dGO)m@T`Uzcf_mMK-`_b1%{_+-|8H2eb}`Sl?2S;0>_`DxBI z_WynI>(XXPzEi<0_S^5;pv+^?KX#Sxrh8|}c9^?p%UdQz z-PugDtW`WTQH4k&f7SoR<;Q!gM9Q}JdWW|K!5r`0ouUmg6Z^1NT;l6lQ*o`Ll{0-Y zZ5``tb2U!aR=bQ3St7>^%PdGSOD?(ktIX#N)UT*sS0)<=vPUm1axNU{Zr{?iNIwfP`7a1i!7SI6AYkQeF8p9z^L zu^;*53=IOFPEsX_+##wDVG!SwcjVQ-2zi{Zn543BbLw}2kluL_%r?)~SO<*Hj@3A4 zouJIImu5sybJzWm(XWmG)=J8y6eaHyn5DMg7b#EP4OJ>}N4`5&S&Wr!uB7}I3p%uQ za3RbEB|Oom!bQUcNMrPLK*0a8QJTIz#NI($sCf7uj|d-KbAV1+hK!er*^c)l@5g!2 z{OS8i`28F;EQQCM<`W0S%gUva)yoLt4+6zuP~eTV$m?_-bvf&76^=TKG+5tag?>^| zJ%Rh+GZ(Z?ubCfazfiLrl>6boSQc+zOftbp7_=!D!>*jfjo)<1W~!jcr z*sy4PBZ2H73TD3V*EnMXtF2ZPi$65D8nExwro|NmqIe-;BW3I&~&=aX_71qniR zakF@C>*8Ti2F@UBurjZ*(DMjGxQ%~!xV{~4z}OcPB+xqU ziQa^U)fiG>Piwl56Dy#WrT})I2XQbE7fGXwz9KkHt@6p5R2nPHBXf|u+N1QpPUR5Y z<1=sDWp=6W+HnLm1$)AiG>&iS2ma!QKjqXeb$6@F;l9`GMTEq7ft&u8aMRH}Grcm$ z> zSf*-n^mu=5F}rud?>vdChn{bMwN?;MdfKg}?hSQNVc|&r;*mU)g}MN+wuy9)U!0r( zKKSCvs_{uc8gs@ADJ`g0g-KRLJGT_-BLTCeD- z9NM`wO&<`ZjL85o#TVwYmjunh3!FEgBfATF1QIjJl-4MXHBEq-AOp~|ebflPTaJqc zwO$)Kz;}Mk>|`=fD~9&YXq3^?wT|I{W`BLq(_%;sQgY> zj#KR?&}o~i(AQ{#=W03Se>B1P#RwZ96*&b?3c#tzo{xz0_IWtD{{NztP#LaN55n*W zb9kJKL`wYT8%CKF6Q`6CUCp%HX10lyCVf&5Y(JVntdwwP_quSL*1k#8?d-OFoKYbi+|P>e2ze43&P}t0x?j*6Jq@6KZD`gBmp>WYTu+)YET2XP*cR0vR+n1#LVI-5Ge|WZ;J+@^q;H zgJw4xP`$(1TLVg@>e3%78+J<3nNB%y>a0WYJ(Za0Y>RNCT?HQ z!6wSrMkOMCj{zlfz5pc~64(HOR&6F16#nc4lRVu%v*i#JcIyO2f%An2Axl@@fQp~B zAt1`fg^t&+D}#@p)xjs~_tzi=G>QR&-g*Kdb6$as2hyp9iJyf}nc12E+=PcKlUt|+ z^xuk1l;1fG1g*19)rv;cFLBiZjDoYbOeq)`w?YR>Ru!dI9cF;tXagp>3N@?>jM70! z2IzMZ7{!>f1(Qg~$Q_^?+o}y3k|w>*mC3mg0nuoz$nY&fNy{geP9&!J^=M|?+OrfWt}Yn5wYmo7o_AW&1k-g65Y7aGZk@fUMu?x?()ACvl||6y5d^u}ZH0~R6Xdt{558Bl#UdhRj>iS` z7_`JGC~P6Wj1aHLMGVkZIJ$1f&OaFHs0NoPKh)MQ90>Y|X_Hb=I2d9DFrgj4$!%+fAD;nH4>45O@WskK+je{_;svIgMgs#j1E5F;}sjJo&CYOthNDKT&w*T zVt{#_N`Q+izy$|v{H%}Pe`2ul{>jaN1d#`;g^pKLD#9bmznGl716ZQ$ADm9~*A@`d z;C(ts$-cd9fF^EVK+xYA?V5mo!T^Z`XavTEv`r!i5)|I1A`!nyBq(fzRtZ98?Z4{@ zNFk#(!0@W>cI1MAyW5O{109?;fB|4?rvYK#y8s2m)v3=GoyZ`DsS+-}4rYfAOcIHe z24Ll0U*rtHzo&rV^M#)+0D0Y6Qt2T@-)`a`ya7dx4vdmLHcuu9@^8ZnA)d%_^aG#_ zAmQ^rbV12p4|}EjgHfnf0NLtqNdriH0ZD%_iA2Dl&US#Os*SAU~j;9VSsg3j7AZzhCzM#hzJEm~kHP8>mrfi$?@p>H)A`qnce1M8@X=OtLP} zP|hDrI~1u5A$}LV3Mzg#WewmWpgjy=6qS?aDE?jqz=(iA*P(s_5>jK;3Q|%-O5i^= z=%zQR1%+E39|7?q*KD%@xbp)#e)oC10|+|BgajBx6xitl5CcbD?%YX7V;F3tD4S?=# z#r(l0vjvR(!K@#8nI$ME~nIE>|5*gqxn#6qogOJ%zR*uqR0J79m z`@cK^q!)>X9h2a|)rpT4FpA}I3qW@_G5_GPRU10N0SvV%FQ|A$p78(P3xHSAZBDDM zKUlf*BoH)-q%{svzy$|Pe4hyiR6J2ND|~$4(F%OL{`1>A5Hz|2SQ`+Mm;*(7((D(# zxscKUx$SgWD#}?O9ss*$^5$QLKQZC4n>Nm`*TOY}s5f&80^1yl zzuS*cgw>mOY}=PtCw3IQABze@Bw=p- z_|g4k;ZW(@k}Ke%o5IvQtxtzYcOd$j-_y-?C49FIR+A<(=CcNN@wVXx=In*~dGLOT z1~gjP&pVly`NVWkeDv(5MvhB_c23f0fM4I_wD1ScxF5u}Vh`!C z#^w4FfTTYRF|TFLMu+7k74D9M)d(QOTua9$oNxknT%*WUbdBZdTtY_u6BuUR6&wi# z&51!RKVVDt3M`si4eMJ6;GF>uL%JB`h3c-X8>53sn`@+5TB%hMMBV%AynlI$8(7-Z ziSEd{heYXp@4K6<{x>m}esRJDrT8I_0}KuwzQgn|eQuovM3TpYBEs3a3}u5U=&!{U z_6qH3V6ASE`W>pnXSV$Aw+^<31lzX~!8zfE9mLCjymcjW9Q#vwZc6D6(6I{lzz0&w z&$!VDBov&im&_7h4$PfxlzrF+toT#(Z;{U2psH~^fQP3OjD;5cqbyTwi6^@!CbWpX z6P30tZxxC?ObXAwZqZrwE}AG@mqpdB;?Zt?HE>mmVX(?g`K2CLBX8H>XQCgfnK%kmK*I; z<}rs8`^*!eW%I~73R3b5;Mq5y=xXK3imko5)Qw(HAG3V7$QkqnA9%lCimzg~Pzb%k zc$v}Ne`W*C#h5G0)8PcqTqKS##gsCg_4gZ3O_o!kP0RczZu&6^p#YSOtkjj^yCx9& zD~aGwP&t)^sQ!Grzvg17HeaAH$4$|!J5<(gEn@9)JGfw6Xr=csg0(VdElqS+P`alS zw)sq}eUfS0B2_T~(dHjXL%hoq(`;ap%}N}uUp^^E3=RDax$SAkXb;X(5ZtDISu^@Q zchFh;SI87^n%v+(l8>uiUl>zyhd?i?;0t%|^7`FXg_?Gj13bC1bwz>JS1$UPjlEufflPJB z2}U6}XC3PT9Y_y{+>+3w8m%wmea@R%Kv4Y_Wgr>F__Nizt~(z&$(qZXiJ&_*Vzl~M zAlpofF1kvyofyy>ow}f*JMUKiIU{2fa6Fe~%Y*(#KPH4P~$SDr8ws%fZ{Wvfj+EGAm9Q8VeQ8*{C-;T{YNpu3VI5Z z`2>Hgy32t0=!z}i3Pm*8evTD1nZ zJvySfr`q&ZUM(7JSzvtKP$`zfFEe_4$}AKm=r)#;E1oT>XZDi#mm=3WZK3rvj`>u+ zD3%R-1Nb2?O+lcQ)fP}53rcy2wIqhOJ34|x?J(ppb{nZCGqedK5N!iK! zUW%gqWBh9YP)EgW!a5QK#~C)iJhbj;%AE|3%8$J z;ELfQkg{X8t%$Van)BZVc+LB{$}`$ty5haV%tB@xp~xz=J8a{LM)gTyG*@B$s({O4 z^+N2=JW#UiGs&s`Bz!!LD?K!H^yYZc$F^Gwr1jsc#v=3yvTggeqhDX{;2~?$Y;8= zHS%c#9QdQi!#5qHoiE;M8^>owXPVVrtIMm4ptZoI@9-}n4H(?UIxajQ1_PlwBB;)EWYj#-SfUx&Nsna`uc_DUPBqP!!NFX87AVW-q+sBOr;rd>+$M0{aW1)y`BCJ9>_p-i$o zBBh8mK_}E-ls^3nM@wPwC9rATvS#){czD;nXAx(Jf`;2J!7GAm+Lkj%vAe0yGMn9I zoC<-c7f+8$w65!B#9V67E@Z@9Zpda-gl-TZU|+HDNjX zB;X{2hqbZ*O)mesPu=KZP*BtDD%;bq4m6Wucg4k@Bmw%{iiEb>8rUGaK0AnZKD2p4 z&XEvd44tBl7`w*iz@AjH{7g2=kc%n-vghvxkrd;|c}jfb)j_5aosIYij@O1Li3IXm zFQN_3x&;j+sUI^~E_|@{^aEdK{Z%?=^4bT)v9VPat-rG}OQegd3GV6Oc34J#wU?ra z-R!QV??a1%yx*bvbuK){R{d`C(kfnx<>1Tbn(G|vkrzU< zdhP!Ol0a?0`3jG*emaHQ6zivD*|urzL2}dAsdSt8)bFnFso!1g#l9o47Opbuwr+RZ zH%qf~$RRqKlXoqzb*>{zTGi6ayn(kk1GjOvUED==DY!bAFyw&gdkxJb+qY-4+rk%yLR+V$?&osf#^%x zE;fy^|Kc~degmwo<&D5p^Ypg}9lNRf;caXx>$`Q$g@LSfzbAKP=kMxqvD>=b?>9fsGhQJw z+=8cIGNTa@Tq@cUzub@Qb^bNn<&(fc-Wj1IHnrR|sXMOCvb(bOV{?RCtSTr_)f%jU z32PW*8cV+5VijDdK?2LF8ob`EYhB|eN=v;-Q{F;v+!T&I)v?4`cB-yY42RGPC&+C9 zTDWLxAXR&P`@0~LzYLCp0|#>)#PX^L3kkq=-NMa|7jp~ed$k50+RxI_wBAeGmbk5_ zwgaTRwfFD+D4ezZi^z2xV2ZXJfD4rk-%zvY&NsuB+XxA#D#+Qu%oM&eKhmF zzVjDb{L}k*4i{Nj@$u-Y+yAtOPupz3`~g3Dv)2EDJnHxSfA8P>Y=3WG-qHL0{rmSn z{flI8%XmcpEI~1rBp;%wALAcy5g%PCu1hv?yqqh(@WQV8N=v-zD=hZ?&ta{bzdys3 zo@lEan^!_!lPQjVZNn+c8BLkKPw^fUJPY&DTewR3BI}O^>G?Q2d;<$BJwMO0w~5#b zJ{AQNGVGQB`lo+PtVMa-RI45@Vg~5l8XL6WbMxJ>7K#_R<$)BB_EX5D7;frBI#4 za5Vo#QVdhrZ2`6^Q6%7xE#B96cESBx<>;Zw|5;&cVI#fdgu_?2hVy>5-bm*5W#ehp-^048{^o{ zQwtX6MLHRcuR9g==W3Cgx_&VlTqP|?chhGe?Y3K!Nr2M|K(uNZ-@FcAxnbWNV;o9* z@4qFS2X#2Rl-ZkOO##6zpl#vZUrigBCulnaqH(5IJpf*us9JrCqGRGvDAr-jVAQC) zNwftX1l(3|Aw8uV(ts`0Zj~{<&}ikmZLzc+3e56`7_zFchK-qep|?EqX*PbF*~-;B zU#U0dUA#ATs|$fYtm+29fvv7Iu3>p>&~9^;dzBP-4AbAMN{DqzO~ZC@N_As98Nxx~ z?Ic#S!!myl>Ka1-?(og2xA~SZD1ika=a@B^LBYcr!1Eym^UfnrW20len{5#`k&XzH z`U1LRfQ`^9r?C4YL4YdsoQ}t5uVxq!%VHUR0aFF+7mON?boS<~KbuTW!;-3_7<^w# zUZuhYNh8Ldjnq;Ni!d#~5iBBLt>Q&BQjMVgy(gE20u8@bV$7tIaNo_3D|2is&Wyl+ zHqI^NZe+ES=bC^iS$)0`+ZK*W)mwY;0&7z z>?ZrNC8bx18hng&cqIa7tU3;E)n^(`5eG}K?QLXj{ltc1*`u5gTf^vWn#;N9ms|im zH@`^Ki7wgmH?(wsK+oQ)Dblmjj!&Bv&AC1`*Y0I+)3M-ouws_%)@0D*h{p6m7ULP1 zMV;6-I?1>AFE(Nyrs?A7S$fqeHHIsgLBECMW6<;q0}MrwiX(^gk~6CSN#Zj#CJ9sL zP6=#S;0OlH2hY$?PLmVf7U7gB$6xdJ2vj3uH*z2b$ms=kmx@#PSg;U|7j4#qU#u)H zI*A8Tv0a*2_(;&&QSvEZkj!?woDPzk_|VefAMx_W7`dY+FdAyh6cdXUTxC3?luee( zTh(JmRz4QRwl{`_rQs$c7@<@t@hY2wo!aPkP@9V@L25Mig&=oOFP7(~vs8oL(80ZD z>L`-&DP}aZ;L5sz7~NFw)iBmLqG!-fs7Cl+(_2X)vDn_E=tz01Q8fQ8d~L_~5{hP| z@l4i$5St1$9XJbSm|JG-k=4e?e+|;7Kthda-?i~DB+ihYYf0Hm0JdN$nl1n%>vyQT z=z+9i&ju@8$v?54Rm4j(X>BaVnK2o6CV-`Ps#_LE~)70s95xpZ>Gg^4z~o^01sM5euwK0Qr(MZ48)o- zsHbMTotlG?(yS(U1)vo1q;hxLGhBq&%{itYaPG|sXAX@k*ef@7zJ zSC9_f+ucE?q74#gN@FQVTEqw!*;Ji0W{~Qg+b80LZ8Zcu({xXkB zaySwn0$D(y0$uC*rEWZ!;{0x;^LcUux$!RYB+cKgMcWrpX(Q}Y)7GZ_28=HtvOWI< zyty;ubduaWO3uyZnw+oMnMY7*Bb3ffTbp(UFgAk8^xCw4-sTsGz08l{%%)3^;pO8G z)}_a$t&Qi$U`$==!5(7v34=a#&Fl*1+adLYyEo6jOKvDBBZj=@l$Wyf@e2oPQd;#K zoxu?ut2RwM=7SY$n9m9gv2IrlgL-Z6P{Z3G0N#|Ptv|dpJhH!UI72Y#r7>^ghuL?C z8@^{n0XN1IW?%}_lg?oEb-BMxfWWLV*;1mMY;S z*B;cb2fnlw9&+H@!vvA79|6^ny$~Tv6_(I^G!-n1X8ELmNO@<4NIiFO5Y46Bo^apX zsnZZ5C^Hdk=>(xb>yw1m?ODcG)qp$HTKe$Lv=*2wHPN`@r3A+EKu>wks^KePHx2 z%v=pkwKV4mtn32dhezlOK1!z+b&$rEuo^+A3{UR(!Cn}>m+ttNghsSq)q~)Q!8<~fP@kT)KIm6ovBWMrP`nI6( z5XaJQtkg*sr!GLALe89uL`CuxwfM;VcV6Ej6gK6sSZ#|e&OCzc8(Tb=2_bpLwfJ~N zA0ZH_0rPlOi=xW%T74{H0|^bL-_Y=I(%^mw%b!zuLw|3#PJehYnk7FFK`EM^A&ayz)Yv1a{Es|@C?2j;TvX?Ggk%W=`IT+HE48CAf*UUsI_ z{h==APyhD+p3uMlc5Sr`Z#!kbiubk=`KlSfeO>KLX;%tC{@e8xJ~d`-tw8?;wrfn| zrB8Hs3Z9ytzt1YqFm%taaSe0dZ{-`Nz$oXid;6dN@Xle*dCh(=<#<-E)ZjyQh>OKa zhAK8n0*=byiKO#t${h>Ge}Dq(d1;jq@|>5zCgfGNIHTda`QQH|`QQK35ES?zzJrUg z2Z?zF`f7GZ{NMl6yuPxQ4gdRpn8$1P_&;T7ih~oaYdSPOz}l$kykdiy3nArexV#%e zY?^CUo;zJL>*raksqNgaN@bO8yUlIvsnjC^+*M${bS>saB_jxHyYvgzD_$4Ba4)Z^ zEE+pp0aV3))d?mv#<~3&%$;toN$CS-Kw#k$IeLkOw(eg^TZ_GsKH|9j!ueBOAyjC{ zntih-H$1kfQziugo=ov`+XJf%1a!8F*q{uqx$eQgbWDVkf|u#hFNI66nBVM{>K>uuXW(SdHu zK+%mzUVP6kN;0LSkuwE!WMzK_ZNO5R>hBeBEUetAFa=*Q-K7fHJ{0ua2@sJ}c1Mxa zeDKNZC%(|ckGq;Bf05=eE{t|^YC`><bbWx$9Thz^eaf8 z-EKZFwBg_(2mXg6Xa0r3jG|RN_NxY+ah37TO4gd}HbVl(2fG@r@^QY=NL0}72g{Ui{@Ig0!3+-P2pC!)6jFfx9__Ru5n*m3G!|PIh?l8X^p$$q!N$c zmgJ1maFOEq3+wc`75%T<;}@9c9E6VZ+29)etxM8}-Qr1tSg-_vs+@f7uDoEc+xOOy zd^yIzqwDm<69Ss{9v&lmw`!hHyRk_)6;JS5j?|{Mepf`D~yVCdjLbEySoByV~J`w$(7%brQvSXvl zU>Z-jbsWyQMsVndyMcZ%27a%9H<}*}%(0KM(Aw8z#@m4fgl>vw>0sdZKd_F8_A>X; zH@N?dPJYOS)HGqiVm=0Sl&Lp|$3Om|e0yu&HMzX4pr3ijLfvKU0uajtow=u1%|ae( zyFrtc{OSMs{{;vUri$|J8LgRLeOQUi6k3iKq0C+FqtEa*yl!pJdNMv@nZh~MuO<7@c)w?%&oI?G z(XUbg1%s~j4lLV}rAeqqitDVD)=FM%!p9Y>QVg8_-~y226g^N^n=x?ZTgMb&h*)M{ zu{T%D@{cp&e}Srgp^67uH?~L6-S*JN&S3kPd3|D^AtW$6(c+3bepbh|Vs~*y)dd z=#fzp15?~av)|wP%n$oN+d#{P9k151xs#n6mvkHIqtj%G zNiv6hpR2=>(&y=s^IwFwyifhVUM=Na?JRx`;8&_*p#7h@jz-QJ%{scJW5>#FMlT80 z@29?r)g3J>0O_A;nVrSe^N5RXOEWjo(MGwMz;_jCN#(Gl+{P9vm4hIixK?nQ z&}g1$z^h(L+$eA}HpD>xjOZVXlIj`a(_ZQ<@kP%@-nM6kC!&p$HE_zNi+tn)wdf$} z|NDQmE27w0Yo`C9Rb1@QD-dJ;yl|P_vQ85G>QBG_y{!~6PLfYm0az~%^wUV~O_ zp)HRb{k+c1avY?Klt~85Qy~ru$$e1KnXI~%o*`he#2g&Hw%Y>Q3311K#uZwT2A&pV z3zbcXGZxwSBI)&PQ?W9$3!KF>GSY2FCYejTmwATkr4|E2vjG%yE{a9F_tk8$oVSS< z>L%H$BpVRS4@VHR(5I3%2pz#ZkvON1b5u|_Mlk_5JA{K=ylH~lO0#*pRa8u0le&k}>PKsO8RDLf^za?u_E(hkkJtmYnCxl9M~_R)Q*A!NM(! zwb5VOHPOiXvjO`34QkC}Yk1lu!qjL-q#EkP3x%3#FybS`8zEq;s+p!5%)`PHuQr$p zpKGD@J>O0etJv&vl9?8!t~2iutl~x+b12TX9%l$*6b@y8FP%el6LcrwWgcw0%&8*W zsc}U0?Jy~|?74$N*8ZCJMtsSbL%$nvHR6ZUJ8rbv`%6W8B@jJT3e{@0@oij1xN%4OGd* zw;VQN1**Pw=@y7{#u}^k^GDoQ{Qb)t_quSqA9?~*KYRmpM;V-ZTYdEi=Z7L47MNrM zVvF_=Z8UTAOxEq?iO1KJ0o2(%9p%-+LRAKl8xpl2Sf96NM&=t^gQr9M8j&N8we6MV zV?2!&m1fYtuOX(BX|oev2@!8RLL)slc57o+xF6Zfp+NS z!f{o!ma4$JD#6KcsK78q^3{kCqg>F@TM=;s^|+rcizBcM%Xf%X()*H8uzgv2Pm-+! zLL5-64f3gEfr?0)9|W@`%}0xC4APq>>;_x7Y<@O87)A_Em47BdgG}R@0c0QyCZh!e z|MlGq%y*@7^&@VCQuM8C{8ot3+g=%KOV|yTQz%@MU0xtyTcM~dV%vjSttDS+QLbgr z=bBeGec`eWRy9N-I*72bVQ66Nv*`vhI+~gWB09ymvNpY8nMzT+e4c~&SAJ!_X`{d0 zU0a2*>M-ZlOR_mZC_-2pIh#w$nQC`)UE)>_=N=%PkM#a0KNgpBoJC#WW#4!323PY( zOK>|}jQXi#SX~K$&54@3Jds z%->g~b(4gy*qr5bq22<4gZhC#^oD6M>=mQmfr8=QUB~wrA;a9#*V#R;c;Gbc1_WYb z=KQa+1#PVO!c3oFXlwOmuO(2ez{j#hmfNy?b4zt(1bI8#;t+BcB{gvD_G<7pJnQ`7 zdmS?HfVjwK6YS8Jhg7BqH!G%KFhUml-sH8o{?4SL8YdASc)?GNTsWL9dbCxYo%7J~ zYrE@8Ijcmkz1i~p0m_$|0=~!U)8}#Zm%ZGCO`TbTUL*n067InTm)m`Cns9?{A$PZYZOuwOOA}RZ`i*DSCMY2=4>~ zE|k1fsNS(ikdh$XrCF*t8a11x(n&@NJe2XVc*`k6O27n{h$yV2un0VTq@>U}&e9_p z$;?cbePD*+`TFp?@=3jPuJ*fC>6N(N9e|}wv(le}11Xc3c|6?0g@ALCs|soQv{TA; zYQGr_7WwGyC>>Ym_*0NHQNHS z2!Y#n35%ngrAl@R!1Npwu_9uqb*TbXDF>9=KqZWYFJ0`i#puL4cKRL`IyH6F4~Vlm6x2kbOYYmAU>(MYV`D z9mZOSkK6}YP}F@-7>%r?qv?FPs9QiJm8oTW= zn2oO~*1nk7bc>{m3jy5t_M&n@NQ$F%(Xma$4&I)hjLYGk2`dz8ky0AZ{uu{LFoJ0bkrBcG^(SafNNJ2)p-VkV0gH9MTevIBI{?P`J%_7 z&>8E;EtkTtLd;V5#;gsA_Y;*G4YE@SNtaYpzJvt6K&w}7tQU${-t8HyW27ua!{}wx zx1)SE#lZh!Dy}#W(U`ghTSdtNzKWu&zDFoqwfd*u|6S7hmhJNsvuT`IoqO`_Df+VE z`bVu;0ecu;S%{7Ew{7KsS{f{;>Ud&_r^zt6Mpa5O!c~$1aW?sJHhpDikS%q4DpbJUu4r6P`+!16JVOcie>9zf^V5qKMWB%a7sNz zb*cfj{LvgF6p^U3TLK4>fQlvHa~LVOL2}t;Ta*gB2Qxk?La=Ss7U+99xK+@aPwvWy zR?m!;$Je%#RZ_K_8C7PqvNx+2U@R{ER~!^QQ5;+Fq36_ssL!lk5y zfjEwlS=nwA(k;s^Qg2^8L`9@(n(QfUXsEYI{=>C=h8MVn5eGSmcCZp=T1Tn*R$BXuK-uo2uJs@9F)2*+m8ZNRI5J>$Z#f_PPeHVn!iuIxHl z33I-Itg{qaT!K&Z!scH2nL1*<9GZjEs6eY4d*CUue8fj7#WQ|Z&?yM*aMeKy`*UYk zl1iQ?o+QA49qje|FRe<2c z3dE4?-3lrT2?$RExeape`rZb{h?yr;9$xFtt@{vKgJts|7pR<|bGaE_sbf166&qR7 zvivm50 zlF69KJDkiH5E~s0B-nA*V0_?KbV~|TK&&1Gp_VIa%-6w1IyNY}=MyFAKcovB0sa9a z9b{8*f4=v#qCg@LL?Fl1sydoFT8omqBgc>O+xp`l+97DPg*rL4A-C|E<$%a>Ih+RG z&v~$58D5-vPE_GFJ_w-|CR8ES(7j9z)w~m-C}J#7K`#jKPyg=!4Cy%1FJ(WV{0%F} zvFhkj#;g-GZJh_d_b57WaN00Zvlpvbx#(%S8oDQnpz@ zgHh2Rg4o^UW?9hO($(_HNDr&7m4Su1!|vbP;ecex1mZ#`ImxQ}HgZsWv>c2UHSUS-`>mW46&U53`189zh-V_W&DFUn zJ`jZj|T;y2>M<%OJbZL%1F8eB)1#8+v(~s$t*Y{$E3Q5BUmZrKh z1<~5fUh-s|jwYyml=~Beb)NQL!&GNiS$|2db4Jaq_Va51Ju-4Vk(6Nzvl*;BNax5X z$ck4Uhxk|TaGajQh)=MgDp$pxnpT?>6dQR!k%0-=SEe#7jh~zVDgV}k5Em1}GU)&> zQqFL9>BOMu*q>p2rKaOF(sdxhjNcVy%g)3xw96(<5pXqLav{t*hph@k14U4*{Td6R zI07{)?56N#A2}7mF)L>>qP#o}X@^3%K8qu2De5Ke6P2HzRicd~R|w%uLGG(7fO2OU ziYC6dX^BYm=fZHJy6p)2EwJXi$sbQvT zSEIvWtQu*i7Bd_RDve5}pPYE33Nq6O6VRK{9nT85m}-H#l!x}vT{;*6(}gC*qq7}h zv79ZkY>I|dlk7M{)A*ZrZSMsXWYO)`W*0O0_xQ%OkB8HOE77(-Ngixs{% zBbCoC5A)oB*a?KN2Z`A=$jAW7-oW4Qb{t?xxicv!_3&&wHtX~ey@7&M3@}RpEiNl~ zffd#mZoz(X@>{$axB~WUgfY{e5z>17;cNt_yx;!ya)t&2)4{2m;#9#&Dte$$hC6B_ zs1~M8!=&>WgrPuulAF}_XLPZ6ndK9Z&Yl_Y$$0h(m(k>-ofprazhqE|_uI!9 zdBXhQgZ*hQonAwl54;7-E=E_+FNl%gtf&dZ)}fo4eDB_Xc^AbTd^-ixJK$Pj2H%b&t&fAqV|(zpJ2WCp|Daa4T&A~T{)_G^fq zT~We(gOJaWuiwnUsOi)WZbjdh)5Qo(-)Q-arf&^xfYpatvC~)Cz;v5?>Gj+rB)RMu z%vLnwk)vxszh>6yc&>v2Th}q|U34w^?ib06!$(hk_2}`JhshU*PriKg;t?=sC?qV0 z{TNDMUwre`%cEz9$>HC;JbVE)q?dm?eR25Yn-|9hPV$@QFMfUa!hB@P@}caI;}05e z*uM zf;R$yy;bJT7O?tOkky-kdYcuf{p7ZRD zwgslU91vsV@(UMz(n={56@`y7<=tajOp4tV8(r9>`r%w#ve>L(62@xsTBQo}f>E^` znnOwo1{XwSjBBw?%h+mk&fy9%8(+XxBKG7=bXPzqFJ|aq>JuDZ>^^eIWz6Z$?J>Nso(olTzq(JdL#xtv>5c9J)G5F9dJH4M0q2k>g}rX;$Z|mS z5(m!=0%IXKyi8x+hbu}SK=0|7kN&C?K<$0|=*7{auU_)4tj2D^%oLC_Vyl`Ch$V05 z;IA?>2O$fF(b~f)2bm(-aX}lgZ-Z=ZB*HPoMcSdlc!Ri`?CT*VwFn&5B*LE=T&)(t z7V>PdG{O(fean`1lWgi)t;oKv!E-I&?&@zi(=FC+*scSX?TXg|=1g_lNMs;9BtiE9 z_)ajiaA*{dz_z%yN5<9<*}q6%c>@G9k1E9r_yh(Muab`r!Z`;*NWT9bi(o*(auFsA zbsDJek^zM?84e5dQUV!g34D4!&+w|u>u)P(&RQ2t(Qa4K^?~G@<^`z%OF<%AGR`!I zIz;4xYrCtt2WmB9V>a$#f@lB7Xm0z!?Fn2+^i6h0$Jlm%r(!YwUUNM7*{O){Xz$vx zbO3li++e`?dR>f;^ynijgn{+io(2>gvl@<|Qb}icazOG2eWmD+U-RHNH7N#Mj}LID zA3(43*eD0bR4AH)15_}&ZMe)}eT>@{A|nn%ZlxC=c0vbeNU4bPHH0KveX>Lj5(lCl z2+Y!0dO%~A)X%H~M_C!4$P4wTR}Rf#I$pVgc(TiM!5?DlX)Mw}xbk&YK#GE_pD`VF(dMs@;}0=D1+8rLZ1>ieJUyPYbvcL8Gnd z_!>>G)Cmtxc4T;eXZO41;1#`t@X~P8!Vy8H{qeG}XE`y$QbZ%UKycI=t&DbMSOByf zjSrcg+&05kJ@=-ICdbY{6NEh34biYf_ttKKK{N!1!r6B-Gb$}5PM1aASe#{EcNH_- z3WfAG8o*YR#$tc^{eP?a&UoPf1crgSQ+oP1d&3PJoNK4;o%x&!2Ro-9)Y9Qq-w4QN z>m=jGw8wIdwsAa{m+dvJ!49Wyv$2__sQ$?NxSPWC65v@H#7B(++_G8Sg%26kbAKS+dQAW%2P9Fp!J-CN(?iOyw0u* zGIkqT2L&#gyrqF%t-x{;kJ%R%v;J)C#1u0NX6H1k=9{DG;?w(Z62gNyX!!e)*EBcf z4?MJ`_vx!wxfv%M#nQ(CX!Btef06Y^1JF(%zLC97Q0*C3pTnR&aM+`%f!{M(Ag*U~ zTiRCS*M9s%>=qgdK2Wrq2+{DZcPPaOnnIq>Qd2tAChQ#jP4~&+isPm8Dfk_^6)E zD@Q@!Ys*QfcPhm+Wxg{_*?!~ThK1y-&-`jYxkC;F!p6zz(AalmXgfS|oy1?Iogj9$ zikr=Sz(Tyidl)gxEVF;>Gutqih`X&RFCL|II)?MigUU0GDd(l#v5i*b4_TpB(@Rh5 z)G~#b)~oq^!1hGx*NWvFWHUsq#5_&GaI@71d_g5V+&3KI;6xuzl$VA=&hjG;k3nPn zOA_*tSKt{wLluNmP*o>$S(V4dDW&R^bmjT*9G)XOI?>X(xv*}bqN}T)w1_IMXgGH? zP@?3J8s;3CqHlW&p6yoMR;-~%^R#Hr4@(I7Vsu4#yAYjP;K%EH*%gyx$^QgCf6n{E zrGv7UwMIf8fCQXpX!%ZVAKq$XXAgb!52!3SKi1M9ognpmxQ7pLl0+VhVYH%W$>8&b zr^yhCwo3Rh|JuAjw{Lk{vW4? z29U1U;TVOk86Hz`Yan&`xwCNn;~(0|Pk(A?1V5zS2hXwJVPJirYp}TSuyBt1svl@S zN&0y5VE()0s*_xyh9<>V^!wad0BARM2dRTS?4{%%3;;VNMLoZf-a!|@xewLX)-@Jx z5r)YXYOVj{e;fAp0ij{<&U-nT6L7;$0`V5Y&RVvC&uvcSM|J1I2wGD8X|QoE;@ zet@7RR~QJU`U1nE=G`KfE-=)fha%2eqaFx4dM*(t0gK#Ouz*{-`wNCYa)BY^U5W=< z9{P?O=QVFMOqIs>88{<|OK`1GeWwAQ+5_j4+dLW9>ToxNcExv|0))~tTPW;hz)m>X z75rn4tFg>ohIPlk!r7%wp~Sve&b@?wpyCdD+BBin$`CEo&&n|4F2s0eh7|ba*0D7K{TWOD^mNg_`=~AHa>3?%kxM588SI1EB%gguB@q zgcj^&9`$;0%3bJ8Z9%2UQ??avn{7y?d?+t&Np$7FQO=eoB*$=f^hi_s;@dpjf;4Rx zFK$I@F}FsoiYO6cppO6ssgDV*S{iHzpknqtBBH2i>*!&o5HGsN9SOnTl3hLhyp z=)J0>Duq5!GJ}=u!R|NFF{0bFQkt;JG15Y}MPhqQ%JoY(9&jd$FDzw`SH>~cQZK*k zSRm@43A6La9)(sCUCXx)tHQkl*^tmP@^bwqd{J(datN_rE2DTv1Fg&lVjjVcqxqXs@syAu@KL3Y zi?k+%S1Ew38i;U-iEnoCll&4gqNquBM5h#$zT)|3f}5f`^^;R$ZKQ;XimR0-nCthy zQngh(8g*)~$k?pla-EQAbxM3H7(U@rk>QJTDOXi7s7UQ7gYr%;pgp-An`v}kaJIIfw%W}hf?73QsmWJcgIj$*Gww@Shn8P51dWcbf`|zT4cp|HI#Hy1UoyU0M21J)5Zl;7BTtVyl_X4#D#>?hrtyZ+3LOdedue^zPA!sWF(%5SNm;J;rfzF)FMwG zXT3R4hZC|_I2Jrwwt2{Oa*iR)?3?ju0a2nK!v~5>|8+ha^e!NZ&R4U+a!#7}Cdvt~ zq*zCA-d&feJG60Jm9pDHMDVff!1hDX=C*SEmMJ%s@v8FPyP6I)*}lA8FHmaK0u|ic zxzL?5N;0KyC|XjJ+84gC87#*}3V!LI>`x8VtgCvd6}oB#H&ycc{ok&ZUwUlSoK3W@ ztNI%%{LfKNGBb=}~Hnz)QSn zJL5K4Mf_4Fk=S-M1RFQfwqt8IA`5LbiBhAEKuED#nbv^-U2>jDp10 zHb8E?r^)vC@CV#ZLj|$F&r_^*Y1W%beXRvB;7G8%^*&s-HZ5+kxi&gAxwYa>PTcll z8=I_7q!UBKA%g5i=jvfJ##`A?{e(VoOe`+qN&U+p%v$Uq?Wv(|xgQ5TmpJ^}+i=6Q zMpfew-g`#I!oU1nO&SqZz z!}MWghHdQIu~}|1*WQdfxg$2~ZU)lm3}^{JL8_ln`Xl-;$XRHqf^PpMpZY@XDa`M2 z$EmWMZd`a}r@9>{)|U+l><8=(c|OE+VYb1#W?~hM$V4;Ual_LQGKjb32Qcyfl(cXS zKURBdl$0HuRlrbUD4eyorNdYy;^DcM&O{E(8p0VJUf1_)LeCXSOAPwDWC*b&Gs$^e zczH#{kp(xiUh*8w;63LPBltoLyb%Nd1y7YCN0 z1w;?X^^6ZRDzHtZt^&zhd=QAZ<(g9km*@&o~%}a2jeU<4fA|mQwK9{NpO|J688ny2FcO9l%AUId^aH zOq8P~8UXO~z{wswpxgiqMAHBY4vE=)>1CgMl(;lWav?ZY0el<~x6C{yU_! zl7trJB80eU6>T zN&m80VpSR}5oXucwnq47g!0&{Uz4%khLU0Kwllw;Zdh8X~MXVWerO%>)UDeTeLar}Cc_5|M&( z1fXH6b-t&AxH-gkk`V-y#CDejPBEngzO#$5-&mWM9Gb(6wa(;xoEWSPvsjqn8pF9B zSA9I8g+EqxgL{*p zyiE=fmi7-=^yC`IBMrPLW1~|!my4$^COpU;!?8{nuY)}gF^CFYfzRh={U9^S-keQG zW0al@LxBhr(-|aMoxl{M?g?{L0R0fNa;Vg^L@tgft zT`lwv!X9c}c!KO;i>|3|niC9;jXt_or_K#PAt1tU&y=ClydfnXZD~CM?u=U$>}u z?9aaaSp|mnn6m}!Dr8QDgOW8IGQCHyARz1W=@vR)(HL8bcnRi1*}9|l3wL%{B@(U5 zD_`|JA|eI8Jb!04Zpb^_;eR<1R&qead$NuS^n`=*hJd}RpxuSu)tLrE+^N}C!3=iJZnT~X>BMZ~(0z&}f*i*RUJG+CKp+G}p92ecNal`4M zl%%6asj@%I2QIKN=e#m|2=aEwJy*-vdB=ZG{Cz#qr)E2d@!cN*+x;^mj55MC`QU0c zxQYvsly?#1CxKia3bmMa6GLadR~bV3-KuUSb+il%;lPMEZ7o!TXxgxOT=r)c2{SUM zTI>L+Kvuu6#-VR}dgjon*k*|HpE=`C8J7ZVU zAA*-vov<00Y;F%$vJ7dS2) zwx$QOLNP+Eo)p{EfkNS#jQ3fQm!T?el@QXxgE8cZMLU!d=ib9mb$GOHlT9Hy?IDk7 z+cR&U&b+1pyzg|89*Grm0LMu>hx0c~APvR0a3J-nvi6@&5o! zHF!{;HZ*n65IiG?A3?}dZH7^A<^a|~D-*0Ep;2AUZtmb=SIYy0H@ihSErA(F)c1=xA=AP5Z;#Jg^J@%|-^0#sFvv~75p zCr)AY3cJ$Eu!T-AJSdn;!PgI!uTkvWa6 zAY2|tjL4$`LmLW+GwfaE-%ioYD!Pb z>GS?BnWv+iu0zzv-eLwjVnCE25=_etLxIRtA=taRU?st9 zGQ3SjwvM8BmRGXmQWeXa*?uMD&iIn90R9gYLI%?QV^ESqbY+xkaGEeKaV4v_)cImL z&Q_A89V~NFSmwnnhgwww<(0V|4d#HH5Fb3y`2gP5P#t{l!Cyy%t2Jql`vbPD0EF4t zL4>VQBSZLD$;!;6MYs+Zvg8o#dRUn!8Gm~l9Yf{s{N$D32BSN+Y6cOi z-zj~#B!5=d!0&Ay27`A$7y;hqMI^)akv(;|c-4UTU5-mD@lLz%^%93_%u0;h8b5l# znhsr=ZPSJ)0uns%c$uScp1iIfvB?Y!9?id_DL^4*1T3)**RP}~OHOsPw9demhOKIR z*Be;rA(xTISc5zWQj(rFnkB{2XiE?V3lB6srBuB2@?&GpqWG%fRxA0cE9>sgZ6)A{ z^orY!AH_&`G{@qpIX*}B=xUk9>s`ezQo%kwG0%zYwApS!x?{bK`KE@G1v>gxIeA;_ zZ$x4r$H32Ne*K_`B+=0m?Rl?AjMShmf)|tRD_sQg4Jw&WJZIxc z>rl=OfomUrl9(z6Pz6pVA zU2H4juU#~qVHM;-{Jfh@u@@Zzs-8ES>vAX$j1o-KZNqoq?{IxGsvBMe!6AG<^18R>cj24RCtXS4Y_qX7Dh`iZe;;Fh zHfC}K<-y*xg2t?TR!2}0-JX1zWw?oiCKL5qUbL?HY@Y*f7E*%Q+kWt_6+ z`XvnfNvo)2cR6QD84!EN+3r}z@7^i4?p`9#W_7f@<<0JnCb%*UAtK>icaXFlaE^HsRABLC9jF zv>zf|F)$~#-1_xs%3`5QR|4Ec;j-|Km?=lPMtt(zzB-o+O}D-6o;OmYi5GMj}^ z^&+qy3rww|mR1}gPFd=*lZs9y&G0}l1zVmoNH2S$8a@@P2Voks3vJdw-SG0<74}eY zjU}~(vmr0Ao=B(D*#e#R*?|u%J11bRwHQ(6W>ayLD_aOSv&kL084a|JP}UFP%6%kG zE7`3kV5P;|YNwO3+Hif7-yg!<_qL|K8=Cpv+Qc_l`L%M%>R44JA*3qU3QgL^lqM4$ z#?LNA^lUoSGf0!`dBA;mAGiXZYhRIytB%BHX)-;`p0c|5-(u$89D@4k1BIPo&d7L9#R0g3J*AIO+Q>+ z$&VD^)%e!b(XM>Ddf7=smsCL5`{7#>m2K-$?=f3gxx_u6QhbFRN~q&m^&u#WtJ|T)ow32gu5)|+YC0i2L93vo zX8k3U`W5WR~lb* zmSR(fc?BCBkQy!#2Ur4VU~Dz=z2MwWu}Hcq*&qaNd*&uu)wjUeAUjlRGy>m5L1G(% z0H8bo>Rw`CNX$scl(Ds>%Ku*cFKa?n}0jm$gQLu|so(q0**JsZ!fHJ#bi2DQ}E)=>plM-xmi zw%9p_D(8*_$q&6GblwEt#Pq19_jP(yFFvG47EOjIzJfLyqzx68K~h*itoC-xBVJL` zYo&9ER1`VqHfM&=x*S9V64pjD7D;oZmQ#tI8D7t^J$3J+DsCe>ujVrG*t~U^W^Nx* zwl5I{k{%rSOM2mOr&ezw;rwlRh( z-H9-Uj(SZbq?2f-s}_KFl|zFY%T(#dN~PT;8dTWYi3#t~k=Z2)VP$Gd*A>}uw4n#nRcVsk2<0P2*rBjGruBBa!EI9I7TvKQ92KlP*y{c-N;h)tTF-ynd6@`? z(V~G+R1@o*m@26IGLUP(CN(Ixo1*F-4Hyj1&N(*=YgN5y;VkhYiS+L5ia3X2Cd=?F z9aFd;2}E_CU4TL%on9w{Q8C9Wr#v~&O`F3YWH@pNyw7UFk(zPW(rt9Ws3JV7N<8qG zu+OPrF$r6I7%3g`q44I4eV~ci2P}tKy`>z@xJ&#dWcg9PgI7^>wqxij8$3}&;pXDQ zO~vDxp(h&FR!9(~o|eWJf!@4pbHGx4CikW_aw1=A+QlHLe0GX#zyz{1E0L?su&hlF zprXdmy@`}$!f>P|+ll-uTnq1J@G{7>%OCM_NPV{1-CYSUw2Hd2sp?gOD;!Uprs7tH z6TWUukC|p6)+?0sB0`t}lZx&VULCyYCl8yIV1pLvEe6GJ+?Ldl(NP+M#!0NQ8T3;hfY zKf+AK?_{Z>)|oV}Dj8iXt^u)&1O+ARh?mI$wwL^3tWkFg)+*FD(OmFM*YvKkV-KAEAwp4_Lb zNy-Yvu2lRishUVW2MIv%NMD%+(nG}_9jk^TxaqhY&I$|bS(d*Z(`N4-y=dp(mf=&7 zip#9AUl~-K@=COLV&sI}r#`9r%CzZ`n41M+6@r9b+-3a3EV+k{1paON(R{+ zqS;RS5RDgJjL>U|-!4Q>xeaA89?vep zW69tKV&mIUF=D?2Q0-Ty1#Y`xXjKEv?jVNG8LMYc}=?%yYV2sT|raDY(KnnScegz;vLFZ`u?t?Z_HgA#X{Rc~vF2ay#J<)#s2PnF7Izg3Bp_WoUn*5`gp66{jS{t^ zXZ&cT+oo~)(N!=hB}@8{09q^`LeH1T@8+;-hu69-=5UM=wP*0? zLQbs}nY*)s?ifCl!v2QFW}02mzZi^)NK2ls?>>Q_nJPN^2gifNvn89c}c`r#=xywc|}pKhyV zrfzG-L&7S{+@T2$dQto{Hxu$ZADl-pCGTd7tok^TM~)*jlHgHQvMi$W!YoReR2R&NqKhPJEF|fWX}LHtu^m!rz-E z+{;=VO8gyOGB#vx!Sa+@V>TrW#dn2kGDsu?oAcon@D+X$B!(fWMwldqO`>81rc!ga z<0e13cee|)VFbLZ6gIp`YFYb^VktJR8l7NM>5}Wiud=Jv_}ek|dVs|F`~n~}$9xK@ zlBZn`exlLG{uJ1t?t-`nVF!3UIP~Ra7`yRZftE91=hOSw1*k7t7~H0&XRXMcwp=H* z;NAmZ_FiR+=jY#jn~ooWD+gB*kk~|ANG4D;D#vSTLM z?l5G+qkL+1{Hq&QF^3oW&>bgRBb614aAUwSRB}h?^3ap&U?OHZpzw(6r+O`VA7y#19uQ}ET~4NXhxCb7N}pt6-lp}Ku`4dw$|@=m*|J=&Rf zV*Z2_?{M7_r&>F04M{>pt_g~(Iaq2aSfZfSL2Y)V9YcEN|GLA>9I?|ABYGw0nTRzV zC%w#a7E}U0$wWjq(`Dld7GjzhS42FsTe*Y|s;y}`m~WKJhLED@LkPBU#q zlng9%T=-(VhLfaMOmH6!GUaF>ab~{bAoD{z2s@t3#B#cZKEAB40quD(DOxpdz?Zt} z4Kk~~TWwDzV*?g#Y5dr)Jn*y_UK$J=lS8FK!t~K_oy(F|BJ>$Qzdqx3Q@lXhV$Y}n zdtswPJ`Gc=1~=b5jNzkT?NdZ^t=@;Ch=xkK=wMl8U4?ortn{N$vpHuwH+0SJRGA}wbJ~R^{Q~^#G~g4#3#js#|5+We=Q&rHj4jlA+D<^KV`} zIqZ$DvgGiqZx6qG{`Fz+++h0R@UOo)Jbu{&F=mHvkBFPzb=w4B_t>5qO`}hbaef|; zrccpY(c&+4?5xEr=uSFm0K57!yIKUbo>Ev%I})x2+FIDL^x#;j!iw(j!piNCM5f4e zqiqLbj$t0`33rg075#0N)`5AbARqBuJ*V`zM z9T25C{`^OxH&)tE%X-Xes4Uon5-TI3F9)N#favz~t>U3OC|V=G_rv)|HXCM8z`q3T zcL$dIVl*N5L_^$@5u8lTdY>*JgeTu)I;FgIs^%L_Ywn}M+W@H4CQL#Nx$>#RNQG&B zEqZe6nn>&LaGahe3e`F_Iu5@*b)Z+Iq?kikU%~CEp`@Rjz>58?2S7zoQ{f~!HKmr* zk>PZmGAI}Q=GBKHMxJ1gKz>3Fcu)vZitXBbB@QZkwjBSa$>ZYBaN9*ZLcRuR4+0x#mh zUnZB5r@RGZq{J20F}i5A&0eRbNCZ%&WE$zA$L^K*v$CiYoTGE4%W>c26J{E3BT0F7Y%Bj zNp_s!S@Y&y+k4^8ZDnVC_QcP~=l%O+igQ=?u!j;(gN&t6u_^ci`dCLmv6|Dyh3;LL zaeGKLFC*!TOMtLrX0QuGu2`)tRZv4GVVHHjmdaj$!CE+(GSFI%D(4u`JG-!D^d|$Q z@BnCVx+Y!?Z^4KK-LK6FhU8Iwq<1JiI8eRC3ao`DoAaF~pR%Sm1qt^-V)ms1rUZ*( zc%FAV4ldpGQQW}U7<>RF?3pP@#Q?Dsu;Q}rj%hg^!}0njC%?rMR9CQxnWb*pgUE3G z;cNtsPrrqX5W`uJD@NIH3nuOTTautgf0hklAF|aXLPZ6ndK8W1w4a$ zHzQ`^BAR@(^WypQmkjD}r#H~sXW1g{&F6<#^V~5J^YPK&96f(_Y`|h?2{w!X($#3{ zcKQahgLRtCjvSwEpiWhf?2Vl3*w-%(pB)_^a=qHV?MU^}1tJ~tG=sWL=%<_b;_&Iw zS4S_8o_`equh`i;d*zCgg*i&Wkk(sxK-j!!Wbqi0G=G&H!@(0XYp2OMth>~F=6jpJ zOwR3ty$7Bw%__Pwo9?ty>N?eBjj%B`M?o@dwF$^w4Tz>-Szo07^W^Rx`TMNC$ZiA1ZEJ*63c03=omRlzSh{HJo z*|!~8=spt68(kGijec;D;7^bLVUqyG(s>U|27d#d3p>5DC8o7L%dUjG9tm|F0M#=E8xgUfyp~!16tn@DO)>KE$c%;6 zqgH(XA~Rx4_Ur7r5`y`LQg-?4H}ip^kyAUi@vgJlih80nR%X3UUu6T+Ywo1iagU75 z(nC}H!K}Uwj}{$#{hIlvW4IOw>{`dPZ55ae^xZF#7l)6Y{OZx;FAtM14xfDa=*1&o z%uq@?exXrlW$%e8#u{tp1=6@;S2MTsmh14 zbNDucVM634nCQ@|{R%L%HF7JZ(i7wD=|G3Ba(B-?o1co*cQ$PaR(bs8qhBB1OP)SG zN;nrvb~PW5`lE#%SJniMvZA+Btwbj!pQr78X~eWb>0+ObOgBc0Yf<;ses8Yttp}qO zzW06@g)btgk7}G@I;H}qZ_gysuNOri03-cu5I8P;c-c>m&C*Bap;fyrx?Uj@<7{y@ zBcqy0dSwpgUu1K`BJbV(?9dm%Cwekq1vc)r(h5*feRPu^L!32!(E^G{Al z{xbDDS`<4?-l@}}BoC*)pQi;vrZIRrx&n)B!_bg+XyTyQG%F-cebrC^q?H4K&22Oc zcgw?YFFp*2h&-F+_mdMhk}`n)kU$NqyE78@@(1qgez~uERZ|WOU#aKIakqgDJ{m(9)B@PeUK@Txn#8Y_!;p?${ zY{%-?R$p1$M*r;2XXERMsWlw+EtD36aWO*oEvdIF;6avwfrbY0;Y$$V`!{A5X_=#G zL_0ZAACCs~%^ZnQE%}pIOAz#g6kLkl&&%H+9RP_N_Rgoa5Jty()THF-s#&?qG@m@0 z4qmW(tlJM?rA-NWwp?^)7hRN54H+p`889m1WIzA;&lM9Vu71kQEr48P% zT!AUbT2UoO3pin->Z4emPezM{&AcwAB7Pnsk$KIcg)|s8S5lX4u}vSA1!y@iyE~pQ zQ}WM5leq6F7@kVnj%U{=)VUX8INj>=91~dLASc!jB{PJ(SH9oh;ER z?1IuWO6XJ`CsUDOc-tWDYv>Q*zwkB??f6rt!>c@K&rNF z+4y4Hw*3bCXKaNCXE=NVOVq5gbP9QE?alz#Cy&U1yfmtzz zBSB6vWHOu`mh7w1AE;Q2#y%4u@lEB=VQW*(S7db$%$7cy_7{iTP~CFA80F;K#!MBT zclg9$9j|7TNJ$tkzHq`P^5hEw)wB8SsFb&ME7wN~(3wDa0T48qb`Lf^)bSbe3N8a> zoNQE94@G{ef(zV;!U#cq61EJ@f!k~3b$x%!PCA@TF}eV-zwNpaDK7=8BX_`+h}g4f z&?JoorY!`Om}%o-Fj{e-tQ5w3^@?sdv4wy~TBnlIINv{e9VWcrDJHm*iwSBQ45Rkz zVrCJpG0){|V3v6wmGCyQ(ebretCJp>zJtlTcn^3!TyWvm$7@+RXNu;ZmG;3|!W91L zPorlC?~6Jsc<+O=f%)bxame4Qb@aSv4ox}T2NWOe*!dy7sJ3RM$e$0An|_aOS=#oV z&{CTkmjm^HYekUC&B+~)Bkp_O8ytIf2?i%B)Aa3~#_G-(UPgjjR`k>P`|OKcr-gjF z0@odDYn;K(1GrwMMY4qa?J0TwBvvJWsIKx2U}Kma6g(a|HvzW&4-U8Ojwy=tRWv9o zuoceFr6+fpXQb1Y*3LO0Wu?Acb#h zk6k@|F9{CjS#`b8=uSsFN31M!`@5Z}qs0EbAI0^KzL(j+M+0VpLW&%b&3^*1lizBqbu_~hmD7ysro;&WAnJxXGTb#G`+7!UCz6zmgb!Rpxj zd-bH^2O6*Wx;c4Ngc`s1+5H~}Wn_5R0j67QbURoTzp2WFB*@6cO?d&HzM}1lD5m5` z;^LtwhY-;BQ`##~O`R`CV}s0WxhM?jkmi9SL}EB5yvH_Q`>-#)nqdf*e71ZwOnzHR zaroPERHkrv9QNxZ%kpnPKh)i)^V)^w4RW4*;m}XW4IbHO9sdE)d{h+62||Bqef!C> zvo|+~DNX=u-lg;s-59Ne`29w75W<<4vLMq@Xcl>+%<1{wHA_-A#&(?ii-j z5sTo9#MZH98aD3{=|RY_2b!iv5D8nWT|$LZ95=uvjOnxq30_89`)Fxc%Na=Y2K>`Bhu{db5R;a+JQXP7OXFanlSz zxp@tpvoCra3Bv;;5_<_HmoYk$R(6lw&BFr~|1gJ0i{ORy*+$a1=v}G+XrSqESptE3 ziBU|Biq9p-dh*im;$&lRPsgPUm&65P80P2#9Ar_o%0cba2?An%>O64cYj7TymPQMe z_iZ#C6~l++TTG|HY=z1BsUg@zQq}l^n?m76peSaDLZN zKhSSU0io4Z0ZqI>CQ|wRC$;JSD6-jzg3n86BY~<|L0i|BRT!V6ae&P+{}>)0vg$ON zwMJSN-#tD=I7GScq+l1)HH8qVOlBC+cxC;27S%ck4PFyHFfN9ytseTT=Nw-uo zxTbw`c|i||d)uu;KJ?qG$KeqCNvsGWRonHWqOe_mY(hE1wy6fp8r0O6tr$$YI4dmU z_jlrWW-@PocEK4-(nF>Mmyb5t$6K)oFOqJ#YD=F2;{-Zai8l%AwsGLQP-2%26vgn{t&??b`BDxcDy&Bq#E7i!wCtDk-Na95H&+AFa5hjSgL| zUZfGGIxgvQ^>z}Mwzsk%f_YbiJuT@QM<={bbCPK=q`OUR`#}kFi58)5=oePg7rM6T zcS>>B90Qd?`#^tHdyO7)W$X|x^9K)NwMBN@WNdGPTO}eQM7I9;2e_{cdeN@N6K{xT zIp651Lp`ZHLO}Ih^OJ~kY5<15_BeSm=dB<$r$@(AELl2R;Jy=xyzt|slZlU4JABh| z@U&cp+LbzDYQ$z7h7e#QvSsU(@(#dQ( z0EzdZlXwH8GW(%$ah%+%+~4 zaaq@>f|>&s1_wS`37nR9474d{MQZmCobuT}6nTibLTg~-A8<&iX^#=@N*vifGpZAH z)f;qz=kg)6lue(y4v5^2D+5!IE>g3c%3ta=U47Jq=p|%N_pHfb9fLXXDr0(|D(#`C zS(SUL%Ai@5`>IOTtjZ^x)B!koT zDa|RaHd7;oFI1>$Wu?)A%?ogHR*)`7u}iM3B1_2hl5aFqcA2n=7uYo^1y1%(KT5Rz z3a6ez(>MhHVZ=$%L4F?eF4&j|1p-TaRpXeR^6)sHa|+bt+Tb0Q1e0fh+@}-+u$~^0 zE)DmuP#1Af8kC+qx#h~Ox791|bmhublN@6JO{$B|qxeWTarZ_K*1PTvqxx>Ak@iF4 z>xb+_kg?K3I3!^OJ094-IyPETif~VGCuycbXmW!8QRo@gmk%Mf7(^Z2rp!6WXLIn7 ztd{lGh6&oX(z~^B*2^9bUq-kKa~^EbXbvwCvt<;o7Dm(jr(L zZC?piK;pp%2T4^pxV0!#EAI$A#DP77*fw|`gT@d~YAv3~u)_G$2>BHKh@gd<7VhPI zHY&hMfhA5oXZyaH19}S|9x`TXke*UJf*|i!Sa)0o)*Tj%&`H#agYI^4iu5&GdWoTDAB{&TMok=KSEdax+Whps4PWuh zP$cRjoJ&581E$b{SBoLs2~tp^2ON~hNX&1^1$?h#ef5=hrgO~Cv;HzqsA=?G6a$!J za8X59ariodlO-ru%o-g*)He)=1ee*8<7|ALVC)|8*-Ow5^%t0PBFDfo(^-;&{TZ14 z&BkL)LksCu`lGzR9H%+f?+V|tS#>eYjM%>*P?~^HzeTue_jzCH zonQ(;wS(iMd{j&K22@5_x1?2#4!+L6gCBeBbKl#?WMSMw3C{T{IQy&Nm{L3B0Nja$ z5lDOc-s^0<9N>-g3Bol6Obd9b=L6M=RVLis15z>o;9Rd>0#4>7RBGLI9c`a#4H+N= zE2mJ}Ny@;c7P+sJf#?J>*X!wH0Mj+~G0+cNWt?45qy?)(NX_`z^E}!2df(6C+%Mlj zealBQIn*n&tKQ^w+!3d}c%( z*Wb64jj~CPxpb@2M4_!lL;B8Jy0~$`ntYABB#lt%ysXI+EA7d{-j_PP{{y!+_!4qE#}c7zs=+X*1kAy}X=LmmTby8__j zN2>rLs1A0r7U1l1N*}&U!bA(*Nx8L#j zgf>kQo^;wY~csmzc=x=IK3OXy0WtDHynlHi`D8Jy7 zg%K26;7I5NYNAl-Br0XHja8|#_KOC0HzP6rU?wyMjJRPBHcnt+r9 z8^;*EzZ=bu23PLR1wM92{HNd(_|uf8W;%NK5uvsVTdsS9vE`Fij{kVQc>xEEVwOXR za(8SWf?^7Pgjt0@d{p?`ZLZXfC9ox$v7|gr_&Fa!GT(|0np0TmK*mlrFQfpzg&6=e zB+}Xd-DU76i8_NG9NpUXiH>gW87R?4ux(Wl)&QxhT~~mjgh;wi(`KKM!JpEcEQSC9 z`$UBAp7}7i28Np62Ne5WkTR#woRPwxc7)nn#&6J@%<#J36*R)#EH7$D(CYw^IH(-P znKLS-Z^UW*=y3SXBN6@O$4DaooM!_&09z+_0Dm^ay1Fq2`(`;?Fj)KW?bxdo^-bBX3q?;A#w-)j%!F08fDQyEUlf+=B+&4lvz8 zdCTd~@_ad8*hLpvUd!e7Q$$|payH~B&65@V*k?bYA5TEkA0 z&mHMm6#IUib4_X^uAzvnHHw$db@_aC`GGD!SY19!WE2j5A3bC{Je9uo!2Q(@m^W*t zVooEqN-D}mAc;F&=75$sn0DAV!BYBYYC~?)a`3WG(ET4-BdO4053To;x|vYUWZ|Iv=bD(Mo??hTIiyV4QOWvb_9@xbAoh$~@AEJO`kY9uvB(s(~x znuB2i8Zhs#KrqnZRAET7Rnd8es%qrMQU%*z0|3-)+Ea6QBvW^4Fd5dfCcB~-Ad)FM z^rCJ{$|5WVI?I`=fSZC#x@wmvXl6JgEBmjqiDC&{G|WqD2j@YBC{c-BTUM7-WI1^t zlHmpiRc-Bv9@5m8{*j}ibD?VOh=MjOq!qVqb(Y23Cql+$(2iZ8gGgP)4w?bs!jgX( zpoD9^rSmvPKnBUF85BCO;99cm27^}p25)_J7_(CRveNBzDV!Oa^~R%QeVknOB)bdc z1vjIVF0wC1IfOp31+~$GGr_{m8=_;u6-~u$x6E-x>@SX#T1c3Z5jdU52{A$$@F1bcQg1c%|z9OKICj%T%&vK-%d+? zi6O-I{tTJ#f>vY`XZmth13KE3YTSmzE7Ma|SiIF_#lh1q#Z00)z#9o5l89LXoStXnjzmMj1C3c9 z;ahHJ1`K}$9q3;0Gtw0j${@Z%fA(FQKG6r~KY zSFM{|Vkq8R_)!ooFmWZnSP3f(tym2c&2m=*MB}+6m(Z1Llv#!FhJdLY8m<-_8_&DqW9MBF5}D`3 zP)BUi)<*PYdyB>fg*E2k*^hUSEECF|qbBWaGsx=6_8WguMS<3ktQMOpYK$TEVkI5c zQlW+skRr9=nMgSNtD9>Y3zarda^oxqE=B%;JRKA>5F`hA0o*q$iJMq}i!WMukx3)* zO&wS!wMd!PeTgY*;R2T=yXQKPI6AQsNIa!rs4}!M6e@?U+5&@y6*_GZ>AXSc7 z*dR7$T@clv#BF0_{+I)(x)6^aHhd~>-nWUJ%3jvDj+_c=UNq8STJWB6l7lil)&4t} z*t?(XcRo#a;h+6aU~)mzGCj+#vgD8dUG*HK!$1CCY!DB<1WOe&ra@+VGZl9d447Aj z;Rm0!z;+d|s#voKceetrnw3UC9eu9=>34d!s?!LM!6mT1A7|74aFXV)3o{m=l116^ z=;~$vTXSH)xW4ljfBy5&`A_fTIjC*3;^Wa(xBpoWpSIiv+<_T}{TCE3cF+HJ|C9Tl z-rtvZ^nQQ;{wMeUBH7zA9??Hb@QzE857E?*@sG!fkFLV4QxqP|o9hcOvtCh%xc7=e zz|QY~*7+>iHGhAOyqpASQOM)1BpOPCwhS(2jRg6vITpgjskd=DEq|LrRD%A9FBt`@ z3Tjyk(@LxcEUWnyP`C}clj!)0aC0Ux1OyGd!V>>+iOl(&UA}*nU18&ix(~S1#Y?uz zlI~KVx{SIGs4m1;0rsl%!!_A)4PpdqwA0r$us9%QHX*vWQaV5c^*~R~gXHiEjtxu% ziE;unf)h`=++quMVi;3F0SwCO_S<(>tQ`(rUcGj7fy%Yx6{=c0ULjaJpMW6riCI6e za_%HSXmp+MisjFwGQK(;z~TYe?PQ^J01?(*m}0rg@%^S=Vq!HLXxMBwLvFetgNT_~ zLX<6yGpb!Ir<5}dBdn{GZydJR-gM(@c2u_a$iupt{#Sj7k8ph^h_3%2ixs_u67_YGa$hks}! znb)-tm2A+FX@^^LUaNpA6Q~)dZR3T^v1Nm=B@$aWdPmPw1+RYN@;-@HtQnM+KesKt zm~f(D;TT$kjPwfs2a=k%5{D%4q_R<)>BYCB?2_XtJL>@dj#xloKw%19&6naXRy@a& zc@_;>Jd?o845;OTk8mVwd7&pX8lqXoGKfo|3>(T9(lD90$)ls8X-$tUR{F|(sc6s! z#c0hg&BAxxQYB$v>>bnlZG~BYFAuohcl?>_ln0}O2=;XN7xSoNFRJ}8j8Pt}F)yR^ z;q+})%-ncfc3k_P-Un&y(@#BV>~@jPh2ZNVotGkTRs}p4VA~6L-Yv<7fg#5;jGc6` zaw)6|f6fdATEL%{d(;FGC&*;|L4>y30NpSs`lhH85Ng;li!&W4Yb|C`GpR|)L4vq8 z5iU|*a+d6zX#p_sF0#T*HZnyBNB zbJX<$?%^jF@HD~j{m1g6kw_zQxs{{Ysb8rueW_gKR{@y}&Bw(06B1W&F#?08WYEid zpLF&OC%X43{Nq@Ww{12%7}_IN^}%qqHqhBhm|H%=c7p?_^%qrvhw=0Y@5sWG0-Pp} z`YX<67ubkKE3N*nO3I^ z)%%^;3CYGK&%7w*vCwPmy*FatIO#-N%IsLRaf02qSnyfQ-ZtKmi}{!RURAcwx9@I; zsCn3gc*PP6?4oTC^lb^+_FImg|o>6~lXqP~lkH*GNK+i0cv ztm~xd8_m5=BHfSxYVfWn0TfI3JNx&ITzVJ&q1_;1M&ai3Prv`K=D)ueaGf*HkvY?FZdj7fbAa^z_=kkQgB9L3Ie^$?zrPlIYNH03;84Y5gwJ9$ znI|WPsT*->ZvMMGP0~eTi+PLT;FmH1ZqQPL@&{l&9K?#7HzP&~zk8!8IxGO{mZpX& zZdbNwsf$p=b<>n^-6<3e5X}<%iORh~{7^6X zi#k9{?t6QCHu(#l?T5{(J9uAlGmI-IMYnpqVzREaJFGf^L7PG|c)xd0UM;*vn z-k|^0473fy%>0EOXdb!Qi{z+*L(+~Bfn zqLOYBuY%O@OORj}*eM&2kW^oAYn6gYPCA&37V!BzyMPY>U}olJ1RbVWg6G%#c3@sY z274>FOAydfMqxeRVIE;WP$-TyCV&6`wfAVQ)G_b(n_S<4GK<`^G7PC9En5TYf?9SWm#|8RF z_?PVYiii`DkyTj~W&7@mw#cf?h!YVfPMkP#;(U-=#~KRRME6dOL^^@NFuQRrKB3WVKiy6 zH-3MDfruNn*!Fq4-7n(TP4QbUtC+V#LA5;bKjRo){~F49X}A$vb+LV9(|Hz?;aO(y zIn*7I05PbsAml-90D?hVpTBc;N{>mAHGrjizUf36tmm(2fGf7Cq^r0X=M*VkF31qg z?ZsINI0y==ZZV*+Urf=^FZvL9?pxd2b+o+G49pbP0Q8cp14EVf`r#I6t)AH8L0@Xs zrr*{=Urz3Dax&v<=~1;*-?WD~XTQNF8>AO)&oHL6n@LiNCf|LHX3ZS%1*2P`A9G|) zI1Xp8khQ5RUF*$SGW$(m{mFL*5?I%+6r=GhOf3~!t8|~U_M$Iq+)GP;oqK5F00dXo zXULUI|ENz*e>1UNFvCl)pz6O-Pt|`z9}y+?xOI_C(&X08CVJPQ+j0u~cRqq$k~e8% zpu-^O@aWR@l4oJ+_8@a zA)fh!5AO>*Q+{*_lbrzfV-)(1Tx+(fwaUUGz2SLMu&^#R8_H zgn7xjqZg{r8cT+GJrA9ZAv)Nv$SkZ35jSy z|5)4fqW4%^ICm!t)Vf|8)r?iZFa;e9({7@=8rR&k_3T)_f)1}Xwk<*XhElscNPa`B z?q#4-b8tAW{uiABJJ+gH_COR}HS44Wns^~ibYMe4HEXd(mGoqmRac{0 zD_Yt+42dFmNlBj6uV$0QbV(^%=iht4y#56zs@BriQ3!F9m=KU(Dgws8(+#V87k!c1 zk#H*^2o(;cQ!Ieyi=w(4#x}JCq$0LUtV=-*md*&K2jnMPwDL;0Rr%W85SF22{m#7C zp`~w{I(D)VF^}a7(Cd9E0O7KUAH9lBw-p+&Kt+8kp4l?XR)wX+TBTRQRtx%jlNE4f zNxn?%*C@#|^_X22R~6>v{45((ta(+`h!JM3tSCN8f2ds+Wr3PS_sv~t8^~jJS-W_B z{7Fe$bB)-yv4}H86IE0 zEpRLb8VEIfTq3CIXf!_^QDjS2FTp8@K5($<5*Vq25nI=;G?}KryVkCjkNb<>JXX!b z%D0Y1Z}^1wn;>l>Kf*9^eV3_^8iMi`e zQ9j&FFjXK<3Kf2hzu#^+$Pye0JA-M@vffR`X z%-fCMXz{5gzC3tz_|4&q!zbTFz$>Qq)~ZZty3m@4a)Wxy56N4Wj#D>faOQrF=e7n% zR;7sBDZCb~*yl;tesRt+3s_4a_0PtYqSvu5JIkg+I8Gt!a}^q{#=rifVt|qQ=abp1 zY__~DO)3rvX5s+vuv`u=NT5R(l$Q@Fv^9K?E0E)MI)+aD>0jD;@pP6A@{1<}<|Qhe z3jw0m^WuOIK&W+$(_-a;`N&boO>|F114OzLOoV7vHYgJ0!)%6o)FC?2BF3`*!^hSj|Wnd2*uJlNPhj|dklZQ(e5lLNd%mrdm9q7 z(GcSU^RYa|cB8Cfu#TxBFM=oB%uORipa<5j=14LoLthV&?uCZi&lUxT)Xip(&{73X z`j)`;%R|71z0<9D|0S9wX1~uaD|p=fXU_pn^8J%%zdv{e zFNL~%C>sZ78KBB^Pg_l){_Fg7g8rGT7_){^xibhA!zT9Ir?;Pp#g>Q&$z(jdbXHT= zP}lg_wv*?^hLJRA*##WiZ2J$t-uwN*t>n?8L#RT5w6cpSw3N^7gorXO2T&VEFI%l= ztJcasO<_QBeXM#FGxOB1BU)JfnoK-D-69iD_~T4GwSgy{2E>iSJ`|}94VZJWBm-2y zOF-&(kByz{rPYuUBo0=#AtKne`UTt1%uLQ3N zzJ+JjgobCHk?r0U`kG*>Zaf`K7xT6?b|FViS{Ned;afJdsdzJ;6toj9q330xN>)WF zSrwq9W^58~86J*L*kf{uZRo>AKCDpv`(a5cj3E=Tp>AzMceZc0wt0b}aeFz4iHSvm zmYvS3(*#vOxo=K^P1xR4yoT7gEZMWcNF2C%J{)3tX|z(s;Mr8&nRDVD0;zzK=>(N+ zHpEBZrnA}u(%3YU1v`F%XQ5lL38GQ$|En|5JXPxNh+W|dI2ATcrSKR_n@QG>J`f8$ z0EaJ-c>vvH320o<<*8IR_>&Z>n@N-wy({?SMXLdHa8Z}oqw690hY!>vT z8%;=g?}5T=xA}9ByY~08p*BodnF^{Wva3&1Or`d#T)AJ%GN`ctuBEeTMJ&I=kH?{1 z9`07}u**s;i*)OD**kaHG4`%;jHUI)0r8`$#l*izqA{p6(>BXA=QKuP-eqK>Jm{z~ zlyv0{4vJ2hCphRR2sY;WoCDciUv^$GTf8QKP~})f9MCBOY9h?Bq6ZYG%cX`eFM7?@ z$X%B1z7o|sy@5k`QV&0s8SfR+j4#)Uve6k@t5z@#?7LrB=e!0wkqGLm!Wiu)4=3dD zXOJyvz#iD(G29FDjTX#(I%(Z^(Ni$EvfLIUny8A)`XP+A5u~d#Nx7T$6J~Um%vS?r zgTQ({V_isbG-zgF>$EZ>BY<`;$k*grJO1#PjPv~IYY*KcXRyCA|4$2-vA)?k`=z)bp zOY9c$PyQqP6Q5bb`o|;>QPTTnKhFzhc-Xw^x7oTkv|4z z)Il?SlT2OqBzlIq&-HiAT&}-!T6q10b1yO(-P$Hu5dY}F3fG)4)IuGulQ7iQx}Jog zn+DuCeGL`q#fD^CoV_BQyHq~@_bTzShNJ_cW9;|y<*=4`em zwP(J{R3hN;tx!wC=8(0Pab@}F23}{f4poAj^MbwEbKDRQ6+Z~mNib8ZiBREgkT|Y{ zjTRv%R^#9+cNPVz5d8QQm`hcwKgv-hLQMG+8pfFj^>LZ6IZM2XKp7C?io!mA9B# zta1#IYe48j0;CaYRN8JU2ehGSmH7-+x?%K*)M(JvbZ8r3ms4Ea!c5eDZR~l(4++=R z^Tma1onsF>{Llbbc%?aIhe>{ET;?5l>Q~5lhH3qQ!toz{&e<3LZgD_04p4StYvOYX&^LK^Ggo*Oy!oq zExg&);n7;^c+mP+hDLDs^u=M&Fr5W0Q?%HQ0+TpWgFs(**c=U-xdB`9voe(WgytHi>XI6`t#@&@!%>H6R;}BT_bP)hO4$Y@bYl#8ZYToy}z$ft+>i@>fj zOz^|33&z#Cv9So%xUegR=|tQfqNU)jZBCud>odZ z^1Y@N7PrH3yO06V-sU~z7!2{@3xSR{6}6d)GxoUBuAs{ z3q+s-0kM;eey|Y;mTo)7bi1^~6{qE#W>`%*LA!l;pEW=9RWB z9Ms1k^|k9`VOL)w+q2PxEhtZIjI&Ctxd0+;d%i{l{q}YXV_f~X$at%3I3nT#$4z_f z=9Me)-)5_F-QuioxfYv1%9=C7YyDD%I6-ZScQ4-P*%^klmf z!+UZVX}m&9@lF%z96lxw659iD{cLh%FksRBb^-FQb5zk`a9pi&@VnTROf>SH1L8DH zt0AaZHx57;XD$dywk#4}^aiOquH8FEw3EPWKx`x6HZXZA=9~E8nd0iCv!thf?7P;o zL6t(>seo}cs_{H8Z2^bHVdP4|C4&)#;hxG7UesZ4olrt}!g!WhrH`nDY*0|qB$*?^ zob)*(%y3SornAghsJ1`4@d6_zV+vv@3?+t_1fKB&>0q2)o5&>@Aa@#m9d(k0{k z)atMR*OXnsSgc}oW8I(5=V|X1>a)lq)@wk8#W+Dqw}%xpvx5HieTHHOy5uDCR4lak zoyR~tdpB#ZFel-8!8~JBi^D7#Mmxv-|3B7KQw#wF_W^> z_9!P@Ti!p*@NkJc3;T8=T*Fd+Td7l!MdHqAH4TF8rf%FCL|Vo<8TOgslc{t*kR zNG2HEBRr_VH-Lm^HcGFyn3^Z_MOcbEv)KEb29}ZMY12*>AHs+{hLg!F0OzI_iv2Lj z$3g;BQs4tNI?|c$C5TM029xwN35$;nNr7VykZpZ9NmSO8$aB|GflZ|scDJ|GzSUF= zS~$~dw_@lXG&f7?b%1AnO)a!^2ZVbiZ?U87T(d$^y!v7y>o2{Lkk{{c6_NGJcM!Ml z{wnSu9%UqOB1bGL3x(?Uiei%Lb$PU;z~h2p2$sGfYwqP`p2D_?TM(UFmlZVeA?)qR z{#|tFn)S%hT3jhWDw>SxYFaisR7={slVSRYOTMibPSQRHg7LEt=s(NX=N7flW;3*Mu)~|xB2z_ZI zx?Hrqba9an^K^E(NgnaF8sw6PHJjzq`?1D32gfGaLx5yAt7@E=zVLrKwk|Ii9jP?N zaxYNz=F_}ZdzfwI3eBml3Fgyyq!v|8T86ZAI$bH<=CQwKjA^)wH*YD}5;Hxvca*^2 z>4t^4)^0h{@_-?NfE)o8HA;^krGu+?z9ldq*`8eaKiU^%kEIwi)(IFF6jRsYDL4hm zdlO5q#c-91LB`THS^Pp%OcM#rWp|gEADBRr)|SscG06!-!~B7b>s>N{a<7gCe7e_q z5a}G7WVn2TLEq|z$+WtmxSdCkv&iYY{E(Set+WOdEbc=Qjg*2BR#aNBI-xh0_eQi# zUKf;&Q`iTa+InT^%Si}OX49znFTMDjZq~vGJirXpKeUiv>&sZI=wp6z4 z08fh@vNog(Eg^XnqF__GKtC{{c{n9_cFgAdF?p3(T7Qs4qW9U6(69z(4L2hIjV46} z_9Hh7lYx^&6At3UFxq+X;iBDvG2c$0-p zC*(QqJ1ZLOSF5g#Zk?>MxG2%X~^>=Y+HabJYZy!kDkIMMeo>NaYb+OqalV#;gs zDy=4jj~fyN;e0ad7c~GHry4uSs5g5$$qSO`huKNmy97y;qMn#6Vz2UPlI0YBCM};8 z5{!Q}4%7RzlSgSjbeYZYq*1ULwRZoev;@>#vKUL$52xxnPiLdOasSz5IP9iqkMr}b zT5fsqEO#U>zKE7BZt%Z?0+HKvFjiTg153Hso!Qhm zWUbSpr5HppSy`n+6%tfrl_b(1bUi_x3{l=bPI4P#c>!g8{6KK|=1r-{X+Ea{TM^d~ ztFD5xnIx&hk%nGVGK{H^waLHNQ%WI16VlgvK@)c{iVQ6jV*cu75;RIVy{sSnR<5FhJhv{5fC<{cOI+uXdo9nX2lg;wS#sMxayn4C zYSGu&I^w!_wes3!Ts}3)vL4VCi%GFaw;jpbX453fdlQaLe|2PG5(5mFd0i82z|1gR z5Wz*sf*XtxMwDQHPRcbLrBu*RCF$64XBVd_NArjeee3FLLQonVy=`_K1Vk&n0}hBK zz?<3Sd*KNVwUxU$HC`I(1N6xJD_Q*@$(2gU$Q}rXFrphVMABn^QQA3;Z5nIvly^3C zMjk3}$k(tD>SOO)@m6=6XhVG6LpcuB=5iMj^OsS_MY1cYgt3M*P2}9E?6cRzhn6Xn z`>mV)+Y6YU@mV+i>ot7WYtDmqWp}e70r=}-eI37cVa@A#s7EHGI7N~4>T}_= zDgxXlrGQ^wr^Wn>RkL)NgIg^>l4hOT(Zmn97;u zXfA@P?{mJf2@KvvhsU-TN1BxlT9Ne(4?UV19J576-dF6^z-$kO>HM*XJo1oSi!Ao# zVVmuc%r`Ru=DT%3bWVZh6>9fdq>D95R(8ZgdQh<6Zi4u_DU$0(sU^iW}Fv{`$2CX=3^YN#>7pNM2su2H(O9GQpc#VP-6wP8Wq|g zj6O7JM6-r;OP+eHBJ!UfH`{32LKXt9FD8A{ z*e4BH@d1V4+>m4tqYX4%wR(HBV7510rGU~T_FHYgtrVZzg5rf5M*tT5Sti|0Ui&QC z;7THz2D-z6jb<8efeB36@6>C>*R-u@WHC0AtAl{ui8qYggS0uLC_i8%9Dqgl!t8|@0+d$`cH=I*ib8A{h4wu@kGN>=7 zSY`PZ7`(ksLdz^=>9&TgTWIap4Xxd}5okr z?FhL@rb(WR65wqKjQv$oI6WqU)cVVfli{SB4mUCB&?dPYZjchQ0vN?2n86GIPzyp_ z5jPd6B|sXOpC)~kB|A+I3iW`4YOJVj=xN4kP<9YX#$1;?VcO;kMF@+uGdsOnBT$dD ztOYWy_8&5!#2ENFaFL=1Oype}5f8LBe)V0TV6ulPn$UMA{Yz>Azcy5_n*8dGEkS>S zuD)72)wD6H(j4@E)0AFb_f^v7^0vA)7%C&aF~xam1~hAH=Hou>D6#o!$mX9ZqKJw@qW3E@KHN;f%VXel3ONXn`uO)V4w1rk8wK2zxLG!PZBaTFq zY{Pk*KW!a11Gt?PQzKcVdgKD{^PWc(a9~}|Bi?x2&^+lCOF52;NufDT35ex2MYO7B z6+zub&n>MUFRfmQN%TS{5pPD%Z0%+iXX#8D;g3t32$x3v(Qs5E8I61?H6;s|V@Xut zy0l!SlIrG~eUmIE$SfI_*QriLH%g?b%t|?vpRAN6#-+*&M4Qf4*_`SKBJ8H_nUB5? z%TaEUjT~5P`IV;ru z8s5EyN^WJe0-@Tyk%_;x4Gh7TVF3tw<}@W97vQPP{##MT-iq?!Rwe5+nDW_OE6U2= z8v?cr0%Rjq!&jPh(*pN#bIwMS>$PJ!F7L8EPecs&f{qk}Y)D zIy!FJEjOu{$X-6{Er#h#ZsLCKv3dOls@|4#^RMZkHKULqY>$S+U&BR&Q6lCka&az$+tt zxOFewb^b>*z;p)vUE{#7*<|mR&a*j9oy-O3bBEJhI*)m{PzOY2q0R z@KVZDdYz2gR?6m+h~&2%$!)Q91r_TfrI%x$@$g=}mhX1Mci#Tje@s^77Akpl2mU9{ zjojXentoXIshCb+?WKm(o>@jkUXn?eha_rOZ6FyFMg(+c@ z2qgBTHd@rZ%E?fJ1#XN&KpC>=QcPV#Vs1noMu6tSTwtw1kt?jO%ebXOx}p7nLs{7l zPOd0TDhYet@p`Le?C)xuxZ^EgrH%fTZNpWf3u_<9^*sy0l@1PNdqHb9W(VM67S!nl zE~HvrS#Gd~KQ%h%*$-D6RaTf$MQxHyopnOcm%Z0i>}?4$#W?`d0lSr1mdPMXp}H{~ zcPlzd4Nfd(c1`ejALugUEehBlj{;um^{P}Xj-!1Xk^>{#BZM&h3I|)3MqJ_*`D<-D z1QjJ^KtnQ5!&cNN-8EV{J=ma7hssn|y9%fF5}&`)sSD+abJo6{%N2qRScKN@Do&Bn zslGK1WytB%F$y--&}Hw-lb({4JREyFBG_OVT^-VV*=6a}JX4A$Mt~!y$+AfgPFzp{B*H77TwrN0$|@E^>2DUz;`DOp-FM zes2QAC!d%hl-s;UabbOd!BF~`vQC|4Y@-#rtosJS%C2Kwse;|ywN+J1ZOK;>EacR? zG#g0N&i*QX49*zWv&H>Ju=oEHYg`G|_H$d~8nNUTx5_0Nh?(W`i)D7XIp>J}2JOl0 zWe$sPC|hlaHa>bMK`~VEp%glwrPFCP+vF@9*!}PghU%vK93!mxRU$7;4c)H)j@mKu zkcrf`NdeyZo`>Jqn1u4`SOp(1GOGh3H3ZNZtepXTIK}kR=<$s4jTTdivBU8LO;B7} z5yD9FK8K`}b4UMxlTTRH_aO0?ix+X+%fV2Jo^9f0tNaPCb$*N_ zmsAd3F?Y~#a3n~-3sg?FgX@i=yvX0*P#W6y59dJ$!pdGt<6WCO;KuPK|OVvNYQ*VJob$=BR#Dqzn_GClY2-=`B>t!`Vsq&gnog4XN$Y*p9$ z?~3qRJ--6dSN44N&Pdlxyn?s9!vK1#2GH{c(7VbA!p%m`4r(wJVh3wI{f<%Y8SKJu zj*bnlc$X$ac_!8-CclX_>7V+|HC6HPh6o#*ui25tyS-T%s!UylR|G42tu4yj6&YnK z*8Mh5!%$|wekU?>zBaz2^&GrPYMm9HSFD|~jb%kIomRGj10K1AA5L2vA8+Y0bhLE9 zrg)Y6+-TW=<%(&bZcgQ4BYVR`^&{t7FY6^^U2(ug-6b5 zPDsc2ZB~2FF|21qt>tpYJ+9)t=N!lDkQf`KI#Q?Z_&N;FeN=TBNlJwZ7s=QvY+Q5b zHKfIFFiVw7aIl0H!?UuiIyNULb@@6Mufdd!d0c^}U)}NzQ5IRpe-5j7-8HP-PlFf6 z4^@_|xwo+!sR{{c_jwbV?XSu=3kLpP(~)ye@1!V?`*a1BscKSeM;DkZ#o$;$v^!! z8}n+D_ytEM`O|+Fj}xGJv>`&|yz~R&V_~F`kCKmWM8U~z*(PTDEiAwr3Sb3#2i{5u2mJTjI`iuAb)4n;y18Y{?%r7>{rM^uMuConv zmWIXo7wk5@<^SEedwXl=Q}c|UZ}05f-TsSY>pDA8^|QdZhDq{sH1%uz@fM^bM#V0l z5>dz!T~Q*=lsRLancjB$_ML3!{!af+lv}TAK}R>(zWs}E>mYH)Y`UmOs=;w=7b~hMJ#t@(dU>ioO`HBbjEDSiDD?08BETrLU4_&z~EEO%h^m(iJA3BGY~f@*8Hq zLZyK@#)o_WtG zn4AN5G)5pcJYY$tfg2dxxSlevd|*l*78_LQXB7bNxh-Co&WCJ#2~b3LGk zPfW-qYT_t*Tg^@dXZJRgx6mSn7Du^wIZLB#*Je->H4@o)c(hN^7A?nU*aNX@A9 zhV$xZGM;>sjwi)8lXG|5T`Y!kosYg@ubnx}V^wYAY`_p&rt~Ja>0+j3KQ3~##X_|Q zaM1#oQ0(R(Mqo#U;NS3J9TA$@^tJSj-%=)VcplR!3$yqlnYP1BxLfv_#B%1psupTZ2rF#Hwl+|k>+M{Bt8U;N{fsEU zxwb=e&#bXPW2&83OyW3K5bYJ3?R3m>Z~{w%BxWs*L;KR!|H5g>EJ$Qys_Xg{dS*sO zWivR?62$l&yT*ecA#>r3r@Sl@y$vw~aMmx#>m^7gHX0qKx`?ehlY-=UbH6iW-j7IWix>q%5ep6X0C-CdZZ@Np#{H8BLFW2t*`Z9GVBi}H?l5j<~_)#jv#;Jc|=-1GR4feWMz{OdL#L< z%>u(S3z#0xVTlfZhs^*jZ?(jJNvfI-mXKr0m}}%RhCJ58$5dcwzJXV!f1Ob zGECP0U4|!Ghg3(m{HYDcx>J{1oDnR%(P7)gL8b=mA;Gk$jtDKi_K?EGamU7T$5t)Z zB)RSSn57&iNE|}#bkjg|Ma^DpInPNpe+0972(&5YJ8T{71| zAW|A5dGo#xSOsi-fq+PFzqH$8T#o3oY8<1&+0a?YMW!~=lsk1d&gDezNh0`;A22mj zAx))B5ab=g2J+0BK*pPVdpMroYw~xHz-}fxpWbQO=MHo`8%^hzp1};|0w?8FTT+5A zbqXNT5+%iMIsf59<9TQ@9jX2k>54x$9Wdzo4SCYuq4m<_b1QAd&LcQbL^O$v`2dHI z$y!UFhc=A8N!AxHUu}4@VZ?EB!F_9M%NR%(m08OzNj2nVuvMuBS=~Qloa0@`_c_q6 z?O!18M;zI)<0~DTUB_o~NNdH^Gto}d;jj@o=IJ!n;s~$occf5-oz8~RZAw`)Gp?^j zN7RaHiz+)SsH^Yxx<1Ob_Tz zQ?>vD*#f1PWtg^dK6$tp3^)^jRI!h!Ls)QgGZ7l0U;=U=MU*X0PLX#^(JCD`c#3l% zhYH?AXS@jv^TAw{od9iqn!_>~P71b6GP*yEX*=Pxa*JVR@{7TOpBaYGtN%?%2)8>lV;|yk^SCo&(Zoov&hCuaNwr#g3i#e{62Y5!^KCWEbhDuf7j3ukQ zpe+;eO`(kUNKNSkX+bgA4UG=_XS8D4&! z(aeGrI7%a*iOm-Acxb5z5V?NZ8)mauIhXqftI5h1`?D_mkPHhoYMRI6tDO z9eK60<`K7SMHd91+iu>PYJ<|XnDW6Y>Qp0hs3On9IoE~K=gK5>wwoe_No zTTbu4ind^z;kGGq(lXkiraG&3w2N&1vHi?v+wCTX zEOIIASYK;%u-WapDK987S%oHd1KA^k;vr>{4l$K@OdvQdxXeue^8Vy930a99-;41#eR182CWWz{?2<`54x+6gnB7tq%cf z*~xa7Gp*oKrWLu2A!U(w)?H2ctNdYC`l%|8goko;JJsh5+LY^gyz)FU3_HPfYo5$f zoM)(d_R47b8sfEJ&(fG>Lrg=>CbtSUW<3%xZ`=6=`PFtcG0o&0q7Ebyj^B7K^h7VF zPA9GKwlqx3Ds0lbJbfU?FeK_P^&}N{sUCDyp24rTh^10+ zX

v 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%0D{5&j(jw4)F%zpdbnJnXlUtFRH0!^-}AH? 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